Skip to content

Instantly share code, notes, and snippets.

@rplantiko
Last active August 29, 2015 14:25
Show Gist options
  • Save rplantiko/a1635dc2a087046e29de to your computer and use it in GitHub Desktop.
Save rplantiko/a1635dc2a087046e29de to your computer and use it in GitHub Desktop.
/*
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