Last active
August 29, 2015 14:25
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
ephemeris via fastcgi | |
See http://swepar.blogspot.ch/2015/07/ein-exkurs-fast-cgi.html | |
*/ | |
#include "ephemeris.h" | |
int main (int argc, char** argv) { | |
query q = {}; | |
result r = {}; | |
char ephepath[MAXLEN_EPHE_PATH]; | |
initializations( ephepath ); | |
swe_set_ephe_path( ephepath ); | |
while (FCGI_Accept() >= 0) { | |
init_request(&q,&r); | |
TRY { | |
const char *qs = getenv("QUERY_STRING"); | |
if (qs) { | |
parse(qs, &q); | |
compute(&q,&r); | |
} | |
output(&q,&r); | |
} | |
CATCH { | |
do_header( ); | |
printf("error: %s \n",r.error); | |
} | |
} /* while */ | |
return 0; | |
} | |
// The core part: Do the computations | |
void compute( query* q, result* r) { | |
if (q->nplanets) compute_planets(q,r); | |
if (q->house_system) compute_houses(q,r); | |
} | |
// Calls swe_calc_ut | |
void compute_planets(query* q, result* r) { | |
if (q->nplanets == 0) return; | |
if (!q->jd_given) THROW( r->error, "Specify Julian Date in UT (jd) for planet computation!" ); | |
r->ppos = malloc( q->nplanets*sizeof(double) ); | |
for (int i = 0; i < q->nplanets; i++) { | |
double xx[6] = {}; | |
char serr[255] = ""; | |
int ipl = q->planets[i]; | |
int iret = swe_calc_ut( q->jd, ipl, q->iflag, xx, serr); | |
if (iret < 0) { | |
THROW( r->error, "Error from swe_calc: %s",serr); | |
} | |
strcpy(r->warning,serr); | |
r->ppos[i] = xx[0]; | |
} | |
} | |
// Calls swe_houses | |
void compute_houses(query* q, result* r) { | |
double cusps[13],armc[10]; | |
if (!q->lon_given) THROW( r->error, "Specify longitude (lon) for houses!" ); | |
if (!q->lat_given) THROW( r->error, "Specify latitude (lat) for houses!" ); | |
if (!q->jd_given) THROW( r->error, "Specify Julian Date in UT (jd) for houses!" ); | |
int rc = swe_houses( q->jd, q->lat, q->lon, (int) q->house_system, cusps, armc ); | |
for (int i=0;i<12;i++) r->hpos[i] = cusps[i+1]; | |
if (rc != 0) THROW( r->error,"Error code %d when computing house cusps", rc ); | |
} | |
// Write result to output | |
void output(query* q,result* r) { | |
do_header( ); | |
if (q->nplanets) { | |
printf("planets:"); | |
print_numbers( r->ppos, q->nplanets); | |
printf("\r\n"); | |
} | |
if (q->house_system) { | |
printf("houses:"); | |
print_numbers( r->hpos, 12); | |
printf("\r\n"); | |
} | |
if (q->debug) { | |
printf("pid: %d\r\n", getpid()); | |
} | |
if (r->warning[0]) { | |
for (char* x=r->warning;*x!='\0';x++) { | |
if (*x=='\r') *x = ';'; | |
if (*x=='\n') *x = ' '; | |
} | |
printf("warning: %s",r->warning); | |
} | |
} | |
// Output a list of numbers | |
// Using sprintf on an auxiliary string | |
// There was a problem with printf("...%.lf...") in fastcgi | |
// See http://stackoverflow.com/questions/31525028/printf-swallowing-bytes-in-fcgi-mode | |
void print_numbers( double* numbers, int n) { | |
if (n<=0) return; | |
char s[15],t[n*15]; | |
*t = '\0'; | |
for (int i=0;i<n;i++) { | |
sprintf(s,"%s%.4lf",i>0?",":"",numbers[i]); | |
strcat(t,s); | |
} | |
printf(t); | |
} | |
// Generates the HTTP header part of the response | |
void do_header() { | |
printf("Content-type: text/plain\r\n\r\n"); | |
} | |
// Parse the query string | |
void parse( const char *query_string, query *q) { | |
char *from, *to, *end; | |
// Copy query string, to preserve the original | |
int len = strlen(query_string); | |
char qs[len+1]; | |
strcpy(qs,query_string); | |
end = qs + len; | |
// Split up qs along '&', parse single parameters | |
for( from = qs; from < end; from=to+1) { | |
if ((to = strchr(from,'&'))) *to = '\0'; | |
if (strlen(from)==0) continue; | |
// Read the single parameter | |
analyze_parameter(from,q); | |
if (to==NULL) break; | |
} | |
} | |
// Parse a single parameter | |
void analyze_parameter( char *par, query *q ){ | |
char *name = par, *value; | |
// May be "name" or "name=value" | |
value = strchr(par,'='); | |
if (value) { | |
*value = '\0'; | |
value++; | |
} | |
else value = ""; | |
if (*name == '\0') return; | |
// Specific parsing for specific parameters: | |
if (strcmp(name,"jd")==0) { | |
if (sscanf(value,"%lf",&q->jd)==0) { | |
THROW( q->error, "Invalid Julian Date value '%s' (must be a number)", value); | |
} | |
if (q->jd < SWIEPH_START || q->jd > SWIEPH_END) { | |
THROW( q->error, | |
"Julian Date %lf out of bounds" | |
"(must be between %lf and %lf )", | |
q->jd,SWIEPH_START,SWIEPH_END); | |
} | |
q->jd_given = true; | |
} | |
else if (strcmp(name,"lon")==0) { | |
if (sscanf(value,"%lf",&q->lon)==0) { | |
THROW( q->error, | |
"Invalid longitude '%s' (must be a decimal number)", value); | |
} | |
q->lon_given = true; | |
} | |
else if (strcmp(name,"lat")==0) { | |
if (sscanf(value,"%lf",&q->lat)==0) { | |
THROW( q->error, | |
"Invalid latitude '%s' (must be a decimal number)", value); | |
} | |
q->lat_given = true; | |
} | |
else if (strcmp(name,"iflag")==0) { | |
if (strlen(value) == 0) q->iflag = 0; | |
else if (sscanf(value,"%d",&q->iflag)==0) { | |
THROW( q->error, | |
"Could not read iflag '%s' - must be a number", value); | |
} | |
} | |
else if (strcmp(name,"planets")==0) { | |
parse_planets(value, q ); | |
} | |
else if (strcmp(name,"houses")==0) { | |
parse_house_system( value, q); | |
} | |
else if (strcmp(name,"debug")==0) { | |
q->debug = true; | |
} | |
} | |
// "planets" = list of Swiss Ephemeris object numbers | |
void parse_planets(char* input, query* q) { | |
if (strlen(input)==0) { | |
// Default set of Planets: Sun, Moon, Mercury .. Pluto | |
q->nplanets = 10; | |
q->planets = malloc( 10*sizeof(int)); | |
memcpy( q->planets, (int[]) { 0,1,2,3,4,5,6,7,8,9}, 10*sizeof(int) ); | |
} | |
else { | |
// planet numbers from the parameter | |
q->nplanets = parse_csv_integers(input,& q->planets, q->error ); | |
} | |
} | |
// "houses" = a letter, specifying the system to be used | |
void parse_house_system(char* input, query* q) { | |
switch (strlen(input)) { | |
case 0: | |
q->house_system = 'P'; // Placidus is default | |
return; | |
case 1: | |
if (strchr("KORCAEVWXHTBMUGY",*input)==NULL) { | |
THROW( q->error, | |
"Illegal house system identifier '%c'" | |
" - see programmer's manual for values",*input); | |
} | |
q->house_system = *input; | |
return; | |
default: | |
THROW( q->error, | |
"Specify exactly one house system (one letter)," | |
" or none to use default"); | |
} | |
} | |
// Read a list of integers, comma-separated | |
int parse_csv_integers(char* input, int** numbers, char* error) { | |
int total; | |
char *s = input,*t; | |
// Precompute total, for getting effective malloc size | |
for (total = 1; *s; total += (*s == ','), s++); | |
// Temporarily allocate the array on stack | |
int n[total]; | |
for (s=input,total=0;(t=strtok(s,","));s=NULL) { | |
if (sscanf(t,"%d",n+total++)==0) { | |
THROW( error, "Can't read number '%s'", t); | |
} | |
} | |
*numbers = malloc( total*sizeof(int)); | |
memcpy( *numbers, n, total*sizeof(int) ); | |
return total; | |
} | |
// Clear out all results from last computation | |
void init_request(query *q, result *r) { | |
if (q->planets) { free(q->planets); } | |
if (r->ppos) { free(r->ppos); } | |
*q = EMPTY_QUERY; | |
*r = EMPTY_RESULT; | |
q->error = &(r->error[0]); | |
r->error[0] = r->warning[0] = '\0'; | |
} | |
// Read config file ".ephemeris" for initial settings | |
// Thus, 'ephepath' can be set at run time | |
void initializations( char* ephepath ) { | |
strcpy(ephepath,EPHE_PATH); // Default | |
FILE *config = fopen(".ephemeris","r"); | |
if (config) { | |
const int LINE_SIZE = 255; | |
char line[LINE_SIZE+1],*name,*value,*p; | |
while (fgets(line, LINE_SIZE,config) != NULL) { | |
int i = strlen(line); | |
if (i==0) continue; | |
while (line[--i]=='\n') line[i] = '\0'; | |
for (name = line;*name !='\0' && isspace(*name);name++); | |
value = strchr(line,'='); | |
if (value) { | |
*value ='\0'; | |
p = value; | |
for (p=value-1;isspace(*p);p--) *p ='\0'; | |
for (value++;isspace(*value);value++); | |
if (strcmp(name,"ephepath")==0) { | |
strcpy(ephepath,value); | |
} | |
} | |
} | |
fclose(config); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment