XEphem/GUI/xephem/tools/xephemdbd/db.c

390 lines
9.1 KiB
C

/* code to manage what the outside world sees as the db_ interface.
*/
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <X11/Intrinsic.h>
#include "xephem.h"
static int db_objadd (Obj *newop);
static void db_init (void);
#define MAXDBLINE 256 /* longest allowed db line */
/* 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() not obj_cir().
*/
static ObjAge_t db_age = 1; /* insure all objects are initially o-o-d */
/* the "database".
* one such struct per object type. the space for objects is malloced in
* seperate chunks of DBCHUNK to avoid needing big contiguous memory blocks.
* the space is never freed, the total is just reduced and can be reused.
* N.B. deleting by catalog depends on all objects from a given catalog being
* appended contiguously within their respective ObjType list.
* N.B. because the number is fixed and known, we use static storage for the
* NOBJ Objects for the PLANET type, not malloced storage; see db_init().
*/
#define DBCHUNK 256 /* number we malloc more of at once; a power of two
* might help the compiler optimize the divides and
* modulo arithmetic.
*/
typedef struct {
char **dblist; /* malloced list of malloced DBCHUNKS arrays */
int nobj; /* number of objects actually in use */
int nmem; /* total number of objects for which we have room */
int size; /* bytes per object */
} DBMem;
static DBMem db[NOBJTYPES]; /* this is the head for each object */
/* return true if the database has been initialized */
#define DBINITED (db[PLANET].dblist) /* anything setup by db_init()*/
/* macro that returns the address of an object given its type and index */
#define OBJP(t,n) \
((Obj *)(db[t].dblist[(n)/DBCHUNK] + ((n)%DBCHUNK)*db[t].size))
#define DB_SIZE_ROUND(s) \
(((s) + sizeof(double) - 1) & ~(sizeof(double) - 1))
static void db_free (DBMem *dmp);
/* return number of objects in the database.
* this includes the NOBJ basic objects.
* N.B. this is expected to be inexpensive to call.
*/
int
db_n()
{
DBMem *dmp;
int n;
if (!DBINITED)
db_init();
for (n = 0, dmp = db; dmp < &db[NOBJTYPES]; dmp++)
n += dmp->nobj;
return (n);
}
/* given one of the basic ids in astro.h or circum.h return pointer to its
* updated Obj in the database.
*/
Obj *
db_basic(id)
int id;
{
Obj *op;
if (!DBINITED)
db_init();
if (id < 0 || id >= db[PLANET].nobj) {
printf ("db_basic(): bad id: %d\n", id);
exit (1);
}
op = OBJP(PLANET,id);
if (op->o_type != UNDEFOBJ)
db_update(op);
return (op);
}
/* mark all db objects as out-of-date
*/
void
db_invalidate()
{
if (!DBINITED)
db_init();
db_age++; /* ok if wraps */
}
/* initialize the given DBScan for a database scan. mask is a collection of
* *M masks for the desired types of objects. op/nop describe a list of
* ObjF which will also be scanned in addition to what is in the database.
* the idea is to call this once, then repeatedly call db_scan() to get all
* objects in the db of those types, then those is op (if any).
* return NULL when there are no more.
* N.B. nothing should be assumed as to the order these are returned.
*/
void
db_scaninit (sp, mask, op, nop)
DBScan *sp;
int mask;
ObjF *op;
int nop;
{
if (!DBINITED)
db_init();
sp->t = UNDEFOBJ;
sp->n = 0;
sp->m = mask;
sp->op = op;
sp->nop = nop;
}
/* fetch the next object.
* N.B. the s_ fields are *not* updated -- call db_update() when you need that.
*/
Obj *
db_scan (sp)
DBScan *sp;
{
if (!DBINITED)
db_init();
/* find next object, splicing in ObjF list with FIXED */
while (sp->t < NOBJTYPES) {
if ((1<<sp->t) & sp->m) {
if (sp->t == FIXED && sp->op) {
if (sp->n < sp->nop)
return ((Obj*)&sp->op[sp->n++]);
sp->op = NULL; /* flag to turn off op list */
sp->n = 0;
}
/* return next for this type, skipping any UNDEF user objects */
while (sp->n < db[sp->t].nobj) {
Obj *op = OBJP(sp->t, sp->n); /* MACRO 2nd arg twice*/
sp->n++;
if (op->o_type != UNDEFOBJ)
return (op);
}
}
sp->t++;
sp->n = 0;
}
return (NULL);
}
/* see to it that all the s_* fields in the given db 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 guarantee an actual update occurs if it's not.
*/
void
db_update(op)
Obj *op;
{
static char me[] = "db_update()";
if (!DBINITED)
db_init();
if (op->o_type == UNDEFOBJ) {
printf ("%s: called with UNDEFOBJ pointer\n", me);
exit (1);
}
if ((int)op->o_type >= NOBJTYPES) {
printf ("%s: called with bad pointer\n", me);
exit (1);
}
if (op->o_age != db_age) {
if (obj_cir (mm_get_now(), op) < 0)
xe_msg (0, "%s: no longer valid", op->o_name);
op->o_age = db_age;
}
}
/* delete all but the basic objects.
*/
void
db_del_all()
{
DBMem *dmp;
if (!DBINITED)
db_init();
/* free memory for each type */
for (dmp = db; dmp < &db[NOBJTYPES]; dmp++) {
/* N.B. PLANET entries are fixed -- not malloced */
if (dmp == &db[PLANET])
continue; /* N.B. except planets! */
db_free (dmp);
}
}
/* read the given .edb or .tle file into memory.
* add a new catalog entry, sorted by catalog name, update GUI.
* stop gracefully if we run out of memory.
* keep operator informed.
* look in several places for file.
* if enabled and only one object in whole file, preload into an ObjXYZ.
*/
void
db_read (fn)
char *fn;
{
char bufs[3][MAXDBLINE];
char *brot, *b0 = bufs[0], *b1 = bufs[1], *b2 = bufs[2];
int nobjs;
Obj o;
char *base;
FILE *fp;
long len;
int fok;
if (!DBINITED)
db_init();
/* skip any leading blanks */
while (*fn == ' ')
fn++;
/* open the file.
* try looking for fn in several places.
*/
fp = fopenh (fn, "r");
if (fp)
goto ok;
xe_msg (1, "%s:\n%s", fn, syserrstr());
return;
/* need pure base of fn */
ok:
for (base = fn+strlen(fn);
base > fn && base[-1] != '/' && base[-1] != '\\'; --base)
continue;
/* set up to run the progress meter based on file position */
(void) fseek (fp, 0L, SEEK_END);
len = ftell (fp);
(void) fseek (fp, 0L, SEEK_SET);
/* read each line from the file and add good ones to the db */
nobjs = 0;
while (fgets (b2, MAXDBLINE, fp)) {
if (db_crack_line (b2,&o,0,0,0) > 0 || !db_tle (b0,b1,b2,&o)) {
if (db_objadd (&o) < 0) {
xe_msg (1, "No more memory");
fclose(fp);
return;
}
nobjs++;
}
/* rotate for possible TLE */
brot = b0;
b0 = b1;
b1 = b2;
b2 = brot;
}
/* clean up */
fok = !ferror(fp);
fclose(fp);
/* check for trouble */
if (!fok) {
xe_msg (1, "%s:\n%s", base, syserrstr());
return;
}
/* record result */
if (nobjs == 0)
xe_msg (1, "%s contains no data", base);
else
xe_msg (0, "%s: contained %d objects", base, nobjs);
}
/* allocate *newop to the appropriate list, growing if necessary.
* return 0 if ok, -1 if no more memory.
* N.B we do *not* validate newop in any way.
*/
static int
db_objadd (newop)
Obj *newop;
{
int t = newop->o_type;
DBMem *dmp = &db[t];
Obj *op;
/* allocate another chunk if this type can't hold another one */
if (dmp->nmem <= dmp->nobj) {
int ndbl = dmp->nmem/DBCHUNK;
int newdblsz = (ndbl + 1) * sizeof(char *);
char **newdbl;
char *newchk;
/* grow list of chunks */
newdbl = dmp->dblist ? (char **) realloc (dmp->dblist, newdblsz)
: (char **) malloc (newdblsz);
if (!newdbl)
return (-1);
/* add 1 chunk */
newchk = malloc (dmp->size * DBCHUNK);
if (!newchk) {
free ((char *)newdbl);
return (-1);
}
newdbl[ndbl] = newchk;
dmp->dblist = newdbl;
dmp->nmem += DBCHUNK;
}
op = OBJP (t, dmp->nobj);
dmp->nobj++;
memcpy ((void *)op, (void *)newop, dmp->size);
return (0);
}
/* set up the basic database.
*/
static void
db_init()
{
static Obj *plan_dblist[1];
/* init the object sizes.
* N.B. must do this before using the OBJP macro
*/
db[UNDEFOBJ].size = 0;
db[FIXED].size = DB_SIZE_ROUND(sizeof(ObjF));
db[BINARYSTAR].size = DB_SIZE_ROUND(sizeof(ObjB));
db[ELLIPTICAL].size = DB_SIZE_ROUND(sizeof(ObjE));
db[HYPERBOLIC].size = DB_SIZE_ROUND(sizeof(ObjH));
db[PARABOLIC].size = DB_SIZE_ROUND(sizeof(ObjP));
db[EARTHSAT].size = DB_SIZE_ROUND(sizeof(ObjES));
/* init access to the built-in objects
*/
db[PLANET].nmem = db[PLANET].nobj = getBuiltInObjs (&plan_dblist[0]);
db[PLANET].dblist = (char **) plan_dblist;
db[PLANET].size = sizeof(Obj); /* *NOT* ObjPl */
}
/* free all the memory associated with the given DBMem */
static void
db_free (dmp)
DBMem *dmp;
{
if (dmp->dblist) {
int i;
for (i = 0; i < dmp->nmem/DBCHUNK; i++)
free (dmp->dblist[i]);
free ((char *)dmp->dblist);
dmp->dblist = NULL;
}
dmp->nobj = 0;
dmp->nmem = 0;
}