home *** CD-ROM | disk | FTP | other *** search
- /* code to manage what the outside world sees as the db_ interface.
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <malloc.h>
- #include <math.h>
- #if defined(__STDC__)
- #include <stdlib.h>
- #include <string.h>
- #endif
- #include "astro.h"
- #include "circum.h"
- #include "preferences.h"
-
-
- #if defined(__STDC__) || defined(__cplusplus)
- #define P_(s) s
- #else
- #define P_(s) ()
- #endif
-
- extern Now *mm_get_now P_((void));
- extern int obj_cir P_((Now *np, Obj *op));
- extern void cal_mjd P_((int mn, double dy, int yr, double *Mjd));
- extern void f_dec_sexsign P_((double x, int *h, int *m, int *s));
- extern void f_sscandate P_((char *bp, int pref, int *m, double *d, int *y));
- extern void f_sscansex P_((char *bp, int *d, int *m, int *s));
- extern void mjd_cal P_((double Mjd, int *mn, double *dy, int *yr));
- extern void sex_dec P_((int hd, int m, int s, double *d));
- extern void xe_msg P_((char *msg, int app_modal));
- extern void zero_mem P_((char *loc, unsigned len));
-
- int db_n P_((void));
- Obj *db_basic P_((int id));
- void db_setuserobj P_((int id, Obj *op));
- void db_invalidate P_((void));
- Obj *db_next P_((Obj *op, HowNext how));
- void db_update P_((Obj *op));
- int db_read P_((FILE *fp, int append));
- int db_set_field P_((char bp[], int id, PrefDateFormat pref, Obj *op));
- static Obj *db_new P_((int t));
- static db_crack_line P_((char *s));
- static void db_init P_((void));
- static int nxt_db P_((char buf[], int blen, FILE *fp));
- static void crack_year P_((char *bp, PrefDateFormat pref, double *p));
- /* don't know why but my compiler complains with this one in here
- static get_fields P_((char *s, char delim, char *fields[]));
- */
- static get_fields ();
-
- #undef P_
-
- #define MAXDBLINE 256 /* longest allowable database file line */
- #define FLDSEP ',' /* major field separator */
- #define SUBFLD '|' /* subfield separator */
-
- #define ASIZ(a) (sizeof(a)/sizeof(a[0]))
-
- /* This counter is incremented when we want to mark all the Obj derived entries
- * as being out-of-date. This works because then each of the age's will be !=
- * db_age. This is to eliminate ever calling obj_cir() under the same
- * circumstances for a given db object.
- * N.B. For this to work, call db_update() before using a Obj *.
- */
- static Objage_t db_age;
-
- /* the "database".
- * there is a malloced array of each each type of object.
- * N.B. first NOBJ (see astro.h) objects are kept as a special case.
- * each array has nmem entries, nobj of which are valid; we realloc in groups
- * of DBMEMCHUNKS as a malloc cache.
- */
- #define DBMEMCHUNKS 200 /* number of things we realloc more at once */
- static char *db[NOBJTYPES]; /* actually arrays of each subtype */
- static int nobj[NOBJTYPES]; /* number of entries in db[type] valid */
- static int nmem[NOBJTYPES]; /* total number of entries in db[type] */
- static Obj basic[NOBJ]; /* special storage for the basic objects */
- static int totnobj; /* grand total number of objects */
- static int objsize[NOBJTYPES]; /* sizeof each type */
-
- /* return number of objects in the database.
- * N.B. this is expected to be very inexpensive to call.
- */
- int
- db_n()
- {
- if (!totnobj)
- db_init();
- return (totnobj);
- }
-
- /* given one of the basic ids in astro.h return pointer to its Obj in the
- * database.
- */
- Obj *
- db_basic(id)
- int id;
- {
- Obj *op;
-
- if (!totnobj)
- db_init();
-
- if (id < 0 || id >= NOBJ) {
- printf ("db_basic(): bad id: %d\n", id);
- exit (1);
- }
-
- op = &basic[id];
- if (op->type != UNDEFOBJ)
- db_update(op);
- return (op);
- }
-
- /* the user defined object named by id to op.
- */
- void
- db_setuserobj(id, op)
- int id; /* OBJX or OBJY */
- Obj *op;
- {
- if (id == OBJX || id == OBJY)
- basic[id] = *op;
- else {
- printf ("db_setuserobj(): bad id: %d\n", id);
- exit (1);
- }
- }
-
- /* mark all db objects as old
- */
- void
- db_invalidate()
- {
- if (!totnobj)
- db_init();
-
- db_age++;
- }
-
- /* given an Obj * into the the db storage, return the "next" one.
- * the idea is to call this function repeatedly to scan for all objects.
- * if op is NULL, return the "first" one.
- * return NULL if there are no more.
- * the series can be limited as to how the basic (NOBJ) set should be included
- * by adjusting the how parameter.
- * N.B. nothing should be assumed as to the order these are returned.
- * N.B. the s_ fields are *not* updated -- all db_update() when you need that.
- */
- Obj *
- db_next (op, how)
- Obj *op;
- HowNext how;
- {
- static char me[] = "db_next()";
- int i;
-
- if (!totnobj)
- db_init();
-
- switch (how) {
- case OBJS_JUST_BASIC:
- if (op == NULL)
- return (&basic[0]);
- if (op >= &basic[0] && op < &basic[NOBJ-1])
- return (&basic[op-basic+1]);
- if (op == &basic[NOBJ-1])
- return (NULL);
- break;
- case OBJS_ALL:
- if (op == NULL)
- return (&basic[0]);
- if (op >= &basic[0] && op < &basic[NOBJ-1])
- return (&basic[op-basic+1]);
- /* continue on to remaining stuff ... */
- case OBJS_ALLBUT_BASIC:
- if (op == NULL || op == &basic[NOBJ-1]) {
- for (i = 0; i < NOBJTYPES; i++)
- if (nobj[i] > 0)
- return ((Obj *)db[i]);
- return (NULL);
- }
- for (i = 0; i < NOBJTYPES; i++) {
- char *cp, *lastp;
- if (nobj[i] == 0)
- continue;
- lastp = db[i] + objsize[i]*(nobj[i]-1);
- cp = (char *)op;
- if (cp >= db[i] && cp < lastp)
- return ((Obj *)(cp + objsize[i]));
- if (cp == lastp) {
- while (++i < NOBJTYPES)
- if (nobj[i] > 0)
- return ((Obj *)db[i]);
- return (NULL);
- }
- }
- break;
- default:
- printf ("%s: bad how: %d\n", me, how);
- exit (1);
- }
-
- printf ("%s: bad op = 0x%x\n", me, (unsigned) op);
- exit(1);
- return (NULL); /* just for lint */
- }
-
-
- /* see that all the s_* fields in the given object are up to date.
- * always recompute the user defined objects because we don't know when
- * they might have been changed.
- * N.B. it is ok to call this even if op is not actually in the database
- * although we guarante an actual update occurs if it't not.
- */
- void
- db_update(op)
- Obj *op;
- {
- static char me[] = "db_update()";
-
- if (!totnobj)
- db_init();
-
- if (op->type == UNDEFOBJ) {
- printf ("%s: called with UNDEFOBJ pointer\n", me);
- exit (1);
- }
- if (op->type <= 0 || op->type >= NOBJTYPES) {
- printf ("%s: called with bad pointer\n", me);
- exit (1);
- }
-
- if (op->o_age != db_age || op == &basic[OBJX] || op == &basic[OBJY]) {
- if (obj_cir (mm_get_now(), op) < 0) {
- printf ("%s: bad object\n", me);
- exit(1);
- }
- op->o_age = db_age;
- }
- }
-
- /* read the given database file into memory.
- * if append is set, add to the existing list, else discard all but the
- * basic NOBJ set first.
- * return 0 if all ok, else -1.
- */
- db_read (fp, append)
- FILE *fp;
- int append;
- {
- char buf[MAXDBLINE];
- int nobjsave[NOBJTYPES];
- int nnew;
- int i;
-
- if (!totnobj)
- db_init();
-
- if (!append) {
- /* not appending so discard all existing objects */
- for (i = 0; i < NOBJTYPES; i++) {
- if (db[i]) {
- free (db[i]);
- db[i] = NULL;
- }
- nmem[i] = 0;
- nobj[i] = 0;
- }
- totnobj = NOBJ;
- }
-
- /* save the current number of objects in case we have trouble so
- * we can effectively reset the db back to what it is before.
- */
- for (i = 0; i < NOBJTYPES; i++)
- nobjsave[i] = nobj[i];
-
- /* read each line from the file and add to the db */
- for (nnew = 0;
- nxt_db (buf, sizeof(buf), fp) == 0 && db_crack_line (buf) == 0;
- nnew++)
- continue;
-
- if (!feof(fp)) {
- /* couldn't read entire file -- discard everything we've added.
- * ok to leave the raw memory allocated.
- */
- for (i = 0; i < NOBJTYPES; i++)
- nobj[i] = nobjsave[i];
- return (-1);
- }
-
- totnobj += nnew;
- return (0);
- }
-
- /* given a text buffer and a field id, and a PREF_DATE_FORMAT,
- * set the corresponding member in *op.
- * return 0 if ok, else -1.
- */
- db_set_field (bp, id, pref, op)
- char bp[];
- PrefDateFormat pref;
- Obj *op;
- {
- switch (id) {
- case O_NAME: {
- (void) strncpy (op->o_name, bp, sizeof(op->o_name)-1);
- op->o_name[sizeof(op->o_name)-1] = '\0';
- break;
- }
- case F_RA: {
- int h, m, s;
- double f_ra;
- f_dec_sexsign (radhr(op->f_RA), &h, &m, &s);
- f_sscansex (bp, &h, &m, &s);
- f_ra = op->f_RA;
- sex_dec (h, m, s, &f_ra);
- op->f_RA = hrrad(f_ra);
- break;
- }
- case F_DEC: {
- int dg, m, s;
- double tdec;
- f_dec_sexsign (raddeg(op->f_dec), &dg, &m, &s);
- f_sscansex (bp, &dg, &m, &s);
- tdec = op->f_dec;
- sex_dec (dg, m, s, &tdec);
- op->f_dec = degrad(tdec);
- break;
- }
- case F_MAG:
- op->f_mag = atof (bp) * MAGSCALE;
- break;
- case F_SIZE:
- op->f_size = atof (bp);
- break;
- case F_EPOCH: {
- double fepoch;
- fepoch = op->f_epoch;
- crack_year (bp, pref, &fepoch);
- op->f_epoch = fepoch;
- break;
- }
- case F_CLASS:
- switch (bp[0]) {
- case 'C': case 'U': case 'O': case 'G': case 'H': case 'A':
- case 'N': case 'F': case 'K': case 'P': case 'Q': case 'T':
- case 'B': case 'D': case 'M': case 'S': case 'V':
- op->f_class = bp[0];
- break;
- default:
- return (-1);
- }
- break;
- case F_SPECT: {
- int i, j;
- /* fill f_spect all the way */
- for (i = j = 0; i < sizeof(op->f_spect); i++)
- if ((op->f_spect[i] = bp[j]) != 0)
- j++;
- break;
- }
-
- case E_INC:
- op->e_inc = atof (bp);
- break;
- case E_LAN:
- op->e_Om = atof (bp);
- break;
- case E_AOP:
- op->e_om = atof (bp);
- break;
- case E_A:
- op->e_a = atof (bp);
- break;
- case E_N:
- op->e_n = atof (bp);
- break;
- case E_E:
- op->e_e = atof (bp);
- break;
- case E_M:
- op->e_M = atof (bp);
- break;
- case E_CEPOCH:
- crack_year (bp, pref, &op->e_cepoch);
- break;
- case E_EPOCH:
- crack_year (bp, pref, &op->e_epoch);
- break;
- case E_M1:
- switch (bp[0]) {
- case 'g':
- op->e_mag.whichm = MAG_gk;
- bp++;
- break;
- case 'H':
- op->e_mag.whichm = MAG_HG;
- bp++;
- break;
- default:
- /* leave type unchanged if no or unrecognized prefix */
- break;
- }
- op->e_mag.m1 = atof(bp);
- break;
- case E_M2:
- switch (bp[0]) {
- case 'k':
- op->e_mag.whichm = MAG_gk;
- bp++;
- break;
- case 'G':
- op->e_mag.whichm = MAG_HG;
- bp++;
- break;
- default:
- /* leave type unchanged if no or unrecognized prefix */
- break;
- }
- op->e_mag.m2 = atof(bp);
- break;
- case E_SIZE:
- op->e_size = atof (bp);
- break;
-
- case H_EP:
- crack_year (bp, pref, &op->h_ep);
- break;
- case H_INC:
- op->h_inc = atof (bp);
- break;
- case H_LAN:
- op->h_Om = atof (bp);
- break;
- case H_AOP:
- op->h_om = atof (bp);
- break;
- case H_E:
- op->h_e = atof (bp);
- break;
- case H_QP:
- op->h_qp = atof (bp);
- break;
- case H_EPOCH:
- crack_year (bp, pref, &op->h_epoch);
- break;
- case H_G:
- op->h_g = atof (bp);
- break;
- case H_K:
- op->h_k = atof (bp);
- break;
- case H_SIZE:
- op->h_size = atof (bp);
- break;
-
- case P_EP:
- crack_year (bp, pref, &op->p_ep);
- break;
- case P_INC:
- op->p_inc = atof (bp);
- break;
- case P_AOP:
- op->p_om = atof (bp);
- break;
- case P_QP:
- op->p_qp = atof (bp);
- break;
- case P_LAN:
- op->p_Om = atof (bp);
- break;
- case P_EPOCH:
- crack_year (bp, pref, &op->p_epoch);
- break;
- case P_G:
- op->p_g = atof (bp);
- break;
- case P_K:
- op->p_k = atof (bp);
- break;
- case P_SIZE:
- op->p_size = atof (bp);
- break;
- default:
- printf ("BUG! db_set_field: bad id: %d\n", id);
- exit (1);
- }
-
- return (0);
- }
-
- /* allocate and return zero'd room for a new object with the given type.
- * N.B we do *not* validate t.
- * reurn NULL if can't get more room.
- */
- static Obj *
- db_new (t)
- int t;
- {
- int size = objsize[t];
- int newidx;
-
- /* allocate more room if this type can't hold another one */
- if (nobj[t] >= nmem[t]) {
- int oldn = nmem[t];
- int newn = oldn + DBMEMCHUNKS;
- char *newp = db[t] ? realloc (db[t], size*newn)
- : malloc (size*newn);
- if (!newp)
- return (NULL);
- zero_mem (newp + size*oldn, size*DBMEMCHUNKS);
- nmem[t] = newn;
- db[t] = newp;
- }
-
- /* the next index to use is the current number of entries */
- newidx = nobj[t]++;
-
- /* find the address of the new entry */
- return ((Obj *)(db[t] + size*newidx));
- }
-
- /* crack the given database line and add to corresponding db list.
- * return 0 if ok else put up a message and return -1.
- */
- static
- db_crack_line (s)
- char *s;
- {
- static char nomem[] = "Insufficient memory to load this database\n";
- #define MAXFLDS 20 /* must be more than on any expected line */
- char *flds[MAXFLDS]; /* point to each field for easy reference */
- char *sflds[MAXFLDS]; /* point to each sub field for easy reference */
- char copy[MAXDBLINE]; /* work copy; leave s untouched */
- char msg[512]; /* misc message buffer */
- int nf, nsf; /* number of fields and subfields */
- Obj *op;
- int i;
-
- /* do all the parsing on a copy */
- (void) strcpy (copy, s);
-
- /* parse into main fields */
- nf = get_fields (copy, FLDSEP, flds);
-
- /* need at least 2: name and type */
- if (nf < 2) {
- (void)sprintf(msg, "Too few fields in Database line: `%.480s'\n",s);
- xe_msg (msg, 0);
- return (-1);
- }
-
- /* switch out on type of object - the second field */
- switch (flds[1][0]) {
- case 'f': {
- static int ids[] = {F_RA, F_DEC, F_MAG, F_EPOCH};
- if (nf != 6 && nf != 7) {
- (void)sprintf(msg,
- "Need ra,dec,mag,[epoch][,siz] for fixed object `%.460s'\n",
- flds[0]);
- xe_msg (msg, 0);
- return (-1);
- }
- op = db_new(FIXED);
- if (!op) {
- xe_msg (nomem, 1);
- return (-1);
- }
- op->type = FIXED;
- nsf = get_fields(flds[1], SUBFLD, sflds);
- if (nsf > 1 && db_set_field (sflds[1], F_CLASS, PREF_MDY, op) < 0) {
- (void) sprintf(msg,"Bad class `%c' for fixed object `%.450s'\n",
- *sflds[1], flds[0]);
- xe_msg (msg, 0);
- return (-1);
- }
- if (nsf > 2)
- (void) db_set_field (sflds[2], F_SPECT, PREF_MDY, op);
- for (i = 2; i < ASIZ(ids)+2; i++)
- (void) db_set_field (flds[i], ids[i-2], PREF_MDY, op);
- if (nf == 7)
- (void) db_set_field (flds[6], F_SIZE, PREF_MDY, op);
- break;
- }
-
- case 'e': {
- static int ids[] = {E_INC, E_LAN, E_AOP, E_A, E_N, E_E, E_M,
- E_CEPOCH, E_EPOCH, E_M1, E_M2
- };
- if (nf != 13 && nf != 14) {
- (void)sprintf (msg,
- "Need i,O,o,a,n,e,M,E,D,H/g,G/k[,siz] for elliptical object `%.450s'\n",
- flds[0]);
- xe_msg(msg, 0);
- return (-1);
- }
- op = db_new(ELLIPTICAL);
- if (!op) {
- xe_msg (nomem, 1);
- return (-1);
- }
- op->type = ELLIPTICAL;
- for (i = 2; i < ASIZ(ids)+2; i++)
- (void) db_set_field (flds[i], ids[i-2], PREF_MDY, op);
- if (nf == 14)
- (void) db_set_field (flds[13], E_SIZE, PREF_MDY, op);
- break;
- }
-
- case 'h': {
- static int ids[]= {H_EP,H_INC,H_LAN,H_AOP,H_E,H_QP,H_EPOCH,H_G,H_K};
- if (nf != 11 && nf != 12) {
- (void)sprintf (msg,
- "Need T,i,O,o,e,q,D,g,k[,siz] for hyperbolic object `%.450s'\n",
- flds[0]);
- xe_msg(msg, 0);
- return (-1);
- }
- op = db_new(HYPERBOLIC);
- if (!op) {
- xe_msg (nomem, 1);
- return (-1);
- }
- op->type = HYPERBOLIC;
- for (i = 2; i < ASIZ(ids)+2; i++)
- (void) db_set_field (flds[i], ids[i-2], PREF_MDY, op);
- if (nf == 12)
- (void) db_set_field (flds[11], H_SIZE, PREF_MDY, op);
- break;
- }
-
- case 'p': {
- static int ids[] = {P_EP,P_INC,P_AOP,P_QP,P_LAN,P_EPOCH,P_G,P_K};
- if (nf != 10 && nf != 11) {
- (void)sprintf (msg,
- "Need T,i,o,q,O,D,g,k[,siz] for parabolic object `%.450s'\n",
- flds[0]);
- xe_msg(msg, 0);
- return (-1);
- }
- op = db_new(PARABOLIC);
- if (!op) {
- xe_msg (nomem, 1);
- return (-1);
- }
- op->type = PARABOLIC;
- for (i = 2; i < ASIZ(ids)+2; i++)
- (void) db_set_field (flds[i], ids[i-2], PREF_MDY, op);
- if (nf == 11)
- (void) db_set_field (flds[10], P_SIZE, PREF_MDY, op);
- break;
- }
-
- default:
- (void)sprintf (msg,
- "Unknown type for Object %s: `%.480s'\n", flds[0], flds[1]);
- xe_msg(msg, 0);
- return (-1);
- }
-
- /* load up name */
- (void) db_set_field (flds[0], O_NAME, PREF_MDY, op);
-
- return (0);
- }
-
- /* set up the basic database.
- */
- static void
- db_init()
- {
- /* these must match the order in astro.h */
- static char *planet_names[] = {
- "Mercury", "Venus", "Mars", "Jupiter", "Saturn",
- "Uranus", "Neptune", "Pluto", "Sun", "Moon",
- };
-
- int i;
-
- /* init the planets */
- for (i = MERCURY; i <= MOON; i++) {
- Obj *op = &basic[i];
- op->type = PLANET;
- (void) strncpy (op->o_name, planet_names[i], sizeof(op->o_name)-1);
- op->pl.code = i;
- }
-
- /* the total includes the planets and the 2 undefined user objs too */
- totnobj = NOBJ;
-
- /* init the object size array */
- objsize[UNDEFOBJ] = 0;
- objsize[FIXED] = sizeof(ObjF);
- objsize[ELLIPTICAL] = sizeof(ObjE);
- objsize[HYPERBOLIC] = sizeof(ObjH);
- objsize[PARABOLIC] = sizeof(ObjP);
- objsize[PLANET] = sizeof(ObjPl);
- }
-
- /* read database file fp and put next valid entry (sans trailing \n) into buf.
- * we only count those lines that begin with alpha or numeric chars.
- * return 0 if ok.
- * if eof: return -1; caller will find that feof(fp) is true;
- * other errors: print a message and return -1.
- */
- static int
- nxt_db (buf, blen, fp)
- char buf[];
- int blen;
- FILE *fp;
- {
- char c;
- int l;
-
- for (;;) {
- if (fgets (buf, blen, fp) == 0)
- return (-1);
- l = strlen(buf);
- if (buf[l-1] != '\n') {
- xe_msg ("Databse file line length is too long\n", 1);
- return (-1);
- }
- c = buf[0];
- if (isalpha(c) || isdigit(c)) {
- buf[l-1] = '\0';
- return (0);
- }
- }
- }
-
- /* given either a decimal year (xxxx[.xxx]) or a calendar (x/x/x)
- * and a DateFormat preference convert it to an mjd and store it at *p.
- */
- static void
- crack_year (bp, pref, p)
- char *bp;
- PrefDateFormat pref;
- double *p;
- {
- int m, y;
- double d;
-
- mjd_cal (*p, &m, &d, &y); /* init with current */
- f_sscandate (bp, pref, &m, &d, &y);
- cal_mjd (m, d, y, p);
- }
-
- /* given a null-terminated string, fill in fields[] with the starting addresses
- * of each field delimited by delim or '\0'.
- * N.B. each character matching delim is REPLACED BY '\0' IN PLACE.
- * N.B. 0-length fields count, so even if *s=='\0' we return 1.
- * return the number of fields.
- */
- static
- get_fields (s, delim, fields)
- char *s;
- char delim;
- char *fields[];
- {
- int n;
- char c;
-
- *fields = s;
- n = 0;
- do {
- c = *s++;
- if (c == delim || c == '\0') {
- s[-1] = '\0';
- *++fields = s;
- n++;
- }
- } while (c);
-
- return (n);
- }
-