XEphem/GUI/xephem/db.c

946 lines
23 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 <fcntl.h>
#include <Xm/Xm.h>
#include "xephem.h"
static void db_init (void);
static Obj *db_objadd (DBCat *dbcp, Obj *newop);
static DBCat *db_catadd (char *filename);
static DBCat *db_nmfindcat (char *name);
static void dbfifo_cb (XtPointer client, int *fdp, XtInputId *idp);
static int db_catcmpf (const void *d1, const void *d2);
static Obj *DBCatObj (DBCat *dbcp, int t, int i);
static void dupaddnm (char nm[][MAXNM], int nnm, Obj *op);
static void dupdelcat (DBCat *);
static void dupsort (void);
static int dup_qsort (const void *p1, const void *p2);
static int dupchknm (char nm[][MAXNM], int nnm);
#define MAXDBLINE 256 /* longest allowed db line */
#define DBFIFO_MSG '!' /* introduces a message line from the DBFIFO */
/* Category for ours and dbmenu's resources in the Save system */
char dbcategory[] = "Data files";
/* 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 */
/* return true if the database has been initialized */
#define DBINITED (dbcat) /* anything set by db_init() */
/* all data is kept in their respective catalogs. the first is a fake for the
* builtin objects. the others are kept sorted by name.
*/
static DBCat *dbcat; /* malloced array, one for each loaded catalog*/
static int ndbcat; /* number of entries in dbcat[] */
static char dbifres[] = "DBinitialFiles"; /* init files resource name */
/* db fifo name */
static char dbfifo[] = "fifos/xephem_db_fifo";
/* names for builtin and fifo catalogs */
static char bi_catname[] = "BuiltIn";
static char fifo_catname[] = "Remote";
/* list of duplicate names and their objects */
#define MAXDUPS 20 /* max duplicate names we will catch */
static DupName *dupnames; /* malloced list, sorted by name */
static int ndupnames; /* n entries used in dupnames[] */
static int mdupnames; /* total entries malloced in dupnames[] */
static int ndupsorted; /* n entries in dupnames[] that are sorted */
/* db fifo fd and XtAddInput id */
static int db_fifofd = -1;
static XtInputId db_fifoid;
/* return number of objects in the database.
*/
int
db_n()
{
int i, t, n;
if (!DBINITED)
db_init();
for (n = i = 0; i < ndbcat; i++)
for (t = 0; t < NOBJTYPES; t++)
n += dbcat[i].tmem[t].nuse;
return (n + fav_get_loaded (NULL));
}
/* given one of the basic ids in astro.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 >= dbcat[0].tmem[PLANET].nuse) {
printf ("db_basic(): bad id: %d\n", id);
abort();
}
op = DBCatObj (&dbcat[0], PLANET, id);
db_update(op);
return (op);
}
/* load each file of objects listed in the DBinitialFiles resource
* and inform all modules of the update.
* support leading ~ and / else assume in ShareDir.
*/
void
db_loadinitial()
{
char *fns; /* value of DBinitialFiles */
char *dbicpy; /* local copy of dir */
char *fnf[128]; /* ptrs into dbicpy[] at each ' ' */
int nfn;
int i;
/* get the initial list of files, if any */
fns = getXRes (dbifres, NULL);
if (!fns)
return;
/* work on a copy since we are about to break into fields */
splashMsg ("Parsing data file names\n");
dbicpy = XtNewString (fns);
nfn = get_fields (dbicpy, ' ', fnf);
if (nfn > XtNumber(fnf)) {
/* we exit because we've clobbered our stack by now!
* TODO: pass the size of fnf to get_fields().
*/
printf ("Too many entries in %s. Max is %d\n", dbifres,
XtNumber(fnf));
abort();
}
/* read in each catalog.
* N.B. get_fields() will return 1 even if there are no fields.
*/
for (i = 0; i < nfn && fnf[i][0] != '\0'; i++) {
splashMsg ("Reading %s\n", fnf[i]);
db_read (fnf[i]);
}
/* all new */
splashMsg ("Inform all systems of new data\n");
all_newdb(0);
XtFree (dbicpy);
}
/* make the current set of databases (except first for builtin objects) the
* new default
*/
static void
db_setinitial()
{
char buf[2048];
int i, l;
buf[0] = '\0';
for (l = 0, i = 1; i < ndbcat; i++)
l += sprintf (buf+l, " %s", dbcat[i].name);
setXRes (dbifres, buf);
}
/* return pointer to our duplicate names list.
* N.B. caller must not modify
*/
int
db_dups (DupName **dnpp)
{
*dnpp = dupnames;
return (ndupnames);
}
/* delete the given catalog.
* N.B. dbcp not valid on return
*/
void
db_catdel (dbcp)
DBCat *dbcp;
{
int t, c;
/* sanity check */
if (dbcp < &dbcat[1] || dbcp >= &dbcat[ndbcat]) {
printf ("Bug! attempt to remove bogus catalog\n");
abort();
}
/* remove each dupname referring to this catalog's entries */
dupdelcat (dbcp);
/* reclaim mem used by dbcp then remove from dbcat */
for (t = 0; t < NOBJTYPES; t++) {
DBTMem *dbtp = &dbcp->tmem[t];
if (dbtp->mem) {
for (c = 0; c < dbtp->nmem/NDBCHNKO; c++)
free (dbtp->mem[c]);
free ((char *)dbtp->mem);
}
}
memmove (dbcp, dbcp+1, (&dbcat[--ndbcat] - dbcp)*sizeof(DBCat));
/* update GUI -- don't include builtin */
db_newcatmenu (dbcat+1, ndbcat-1);
db_setinitial();
}
/* given an object, return which catalog it is in, else NULL */
DBCat *
db_opfindcat (Obj *op)
{
int t = op->o_type;
int i;
for (i = 0; i < ndbcat; i++) {
DBCat *dbcp = &dbcat[i];
int j, n = dbcp->tmem[t].nuse;
/* scan each chunk looking for op */
for (j = 0; j < n; j += NDBCHNKO) {
Obj *oplo= DBCatObj(dbcp, t, j);
Obj *ophi= DBCatObj(dbcp, t, j+NDBCHNKO>n ? n-1 : j+NDBCHNKO-1);
if (oplo <= op && op <= ophi)
return (dbcp);
}
}
return (NULL);
}
/* search for a loaded catalog with the given name.
* if find it return pointer to DBCat, else return NULL.
*/
static DBCat *
db_nmfindcat (name)
char *name;
{
char *base;
int i;
/* find just the basename */
while (*name == ' ')
name++;
for (base = name+strlen(name);
base > name && base[-1] != '/' && base[-1] != '\\'; --base)
continue;
for (i = 1; i < ndbcat; i++)
if (!strcmp (dbcat[i].name, base))
return (&dbcat[i]);
return (NULL);
}
/* allocate a new DBCat in dbcat[] and init with name, sorted by name but
* leaving first for builtins.
* name is already just the basename of a full path.
* return pointer if ok, else NULL if no more memory.
*/
static DBCat *
db_catadd (name)
char *name;
{
DBCat *dbcp;
int i;
/* make room for another */
dbcp = (DBCat *) realloc (dbcat, (ndbcat+1)*sizeof(DBCat));
if (!dbcp)
return (NULL);
dbcat = dbcp;
dbcp = &dbcat[ndbcat++];
/* init */
memset (dbcp, 0, sizeof(*dbcp));
(void) sprintf (dbcp->name, "%.*s", (int)sizeof(dbcp->name)-1, name);
dbcp->tmem[FIXED].siz = sizeof(ObjF);
dbcp->tmem[BINARYSTAR].siz = sizeof(ObjB);
dbcp->tmem[ELLIPTICAL].siz = sizeof(ObjE);
dbcp->tmem[HYPERBOLIC].siz = sizeof(ObjH);
dbcp->tmem[PARABOLIC].siz = sizeof(ObjP);
dbcp->tmem[EARTHSAT].siz = sizeof(ObjES);
dbcp->tmem[PLANET].siz = sizeof(ObjPl);
/* sort by name, but leave built in at head */
qsort (dbcat+1, ndbcat-1, sizeof(DBCat), db_catcmpf);
/* find name again */
for (i = 0; i < ndbcat; i++)
if (strcmp (dbcat[i].name, name) == 0)
return (&dbcat[i]);
/* eh?? */
printf ("Bug! catalog disappeared after sorting: %s\n", name);
abort();
return (0); /* for lint */
}
/* 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. tmask 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 in 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, tmask, op, nop)
DBScan *sp;
int tmask;
ObjF *op;
int nop;
{
if (!DBINITED)
db_init();
sp->m = tmask;
sp->t = UNDEFOBJ;
sp->n = 0;
sp->c = 0;
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 op list with FIXED */
while (sp->c < ndbcat) {
while (sp->t < NOBJTYPES) {
if (OBJTYPE2MASK(sp->t) & sp->m) {
if (sp->t == FIXED && sp->op) {
/* return next in op list (all are FIXED) */
if (sp->n < sp->nop)
return ((Obj*)&sp->op[sp->n++]);
sp->op = NULL; /* flag to turn off op list */
sp->n = 0;
}
if (sp->n < dbcat[sp->c].tmem[sp->t].nuse) {
/* return next in this catalog for this type */
return (DBCatObj (&dbcat[sp->c], sp->t, sp->n++));
}
}
/* go on to next type */
sp->t++;
sp->n = 0;
}
/* go on to next catalog */
sp->c++;
sp->t = UNDEFOBJ;
sp->n = 0;
}
/* then any Favorites not also in db */
return (fav_scan (&sp->n, sp->m));
}
/* see to it that all the s_* fields in the given db object are up to date.
* 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);
abort();
}
if ((int)op->o_type >= NOBJTYPES) {
printf ("%s: called with bad pointer: %d\n", me, (int)op->o_type);
abort();
}
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;
}
}
/* reload all loaded catalogs
*/
void
db_rel_all()
{
char **curn = NULL;
int i, n;
if (!DBINITED)
db_init();
/* gather all currently loaded names so they can be reloaded */
n = ndbcat - 1; /* sans first for builtins */
curn = (char **) XtMalloc (n*sizeof(char *));
for (i = 1; i < ndbcat; i++)
curn[i-1] = XtNewString(dbcat[i].name);
/* reload all, free names as we go, then list itself */
for (i = 0; i < n; i++) {
db_read (curn[i]);
XtFree (curn[i]);
}
XtFree ((char *)curn);
/* spread the word */
all_newdb(0);
}
/* delete all catalogs except the basic objects.
*/
void
db_del_all()
{
int i, n;
if (!DBINITED)
db_init();
/* free each catalog */
n = ndbcat; /* save, decrements after each */
for (i = 1; i < n; i++)
db_catdel (&dbcat[1]);
}
/* read the given .edb or .tle file into memory.
* add (or replace) 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, add to Favrorites
* N.B. caller is responsible for calling all_newdb().
*/
void
db_read (fn)
char *fn;
{
char bufs[3][MAXDBLINE];
char *brot, *b0 = bufs[0], *b1 = bufs[1], *b2 = bufs[2];
int alts = db_chkAltNames();
char fullfn[1024];
DBCat *dbcp;
Obj *newop = NULL;
int nobjs, nnewobjs;
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;
sprintf (fullfn, "%s/%s", getPrivateDir(), fn);
fp = fopenh (fullfn, "r");
if (fp)
goto ok;
sprintf (fullfn, "%s/catalogs/%s", getShareDir(), fn);
fp = fopenh (fullfn, "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);
pm_set (0);
/* get a /fresh/ catalog entry */
dbcp = db_nmfindcat(base);
if (dbcp)
db_catdel(dbcp);
dbcp = db_catadd(base);
if (!dbcp) {
xe_msg (1, "No memory for new catalog");
fclose(fp);
return;
}
/* read each line from the file and add good ones to the db and dups */
nobjs = nnewobjs = 0;
memset (bufs, 0, sizeof(bufs));
while (fgets (b2, MAXDBLINE, fp)) {
char nm[MAXDUPS][MAXNM];
char wn[1024];
int nnm;
pm_set ((int)(ftell(fp)*100/len)); /* update progress meter */
wn[0] = '\0';
if ((nnm=db_crack_line (b2,&o,nm,MAXDUPS,wn)) > 0
|| !db_tle (b0,b1,b2,&o)) {
nobjs++;
if (nnm <= 0) {
/* must have found a tle entry */
nnm = 1;
strcpy (nm[0], o.o_name);
}
if (!is_type(&o,PLANETM) && (!alts || !dupchknm (nm, nnm))) {
if (!(newop = db_objadd (dbcp, &o))) {
xe_msg (1, "No more memory");
fclose(fp);
db_catdel (dbcp);
return;
}
dupaddnm (nm, alts ? nnm : 1, newop);
nnewobjs++;
}
} else if (wn[0])
xe_msg (0, "%s: %s", fn, wn);
/* 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());
db_catdel (dbcp);
return;
}
/* reject catalog if found nothing at all */
if (nobjs == 0) {
db_catdel (dbcp);
xe_msg (1, "%s contains no data", base);
return;
}
xe_msg (0, "%s: contained %d new objects", base, nnewobjs);
db_newcatmenu (dbcat+1, ndbcat-1); /* sans builtin */
db_setinitial();
dupsort();
/* auto add to Favorites if exactly 1 in catalog */
if (nnewobjs == 1 && newop && db_load1())
fav_add (newop);
}
/* assuming we can open it ok, connect the db fifo to a callback.
* we close and reopen each time we are called.
*/
void
db_connect_fifo()
{
char fn[1024];
/* close if currently open */
if (db_fifofd >= 0) {
XtRemoveInput (db_fifoid);
(void) close (db_fifofd);
db_fifofd = -1;
}
/* open for read/write. this assures open will never block, that
* reads (and hence select()) WILL block if it's empty, and let's
* processes using it come and go as they please.
*/
(void) sprintf (fn,"%s/%s", getPrivateDir(), dbfifo);
db_fifofd = openh (fn, O_RDWR);
if (db_fifofd < 0) {
(void) sprintf (fn,"%s/%s", getShareDir(), dbfifo);
db_fifofd = openh (fn, O_RDWR);
if (db_fifofd < 0) {
xe_msg (0, "%s: %s\n", fn, syserrstr());
return;
}
}
/* wait for messages */
db_fifoid = XtAppAddInput(xe_app, db_fifofd, (XtPointer)XtInputReadMask,
dbfifo_cb, (XtPointer)fn);
}
/* allocate *newop to the given catalog list, growing if necessary.
* return new ptr if ok, NULL if no more memory.
*/
static Obj *
db_objadd (DBCat *dbcp, Obj *newop)
{
DBTMem *dbtp = &dbcp->tmem[newop->o_type];
int siz = dbtp->siz;
/* add another chunk if no more room in current chunk */
if (dbtp->nuse >= dbtp->nmem) {
int nchk = dbtp->nmem/NDBCHNKO; /* n chunks now */
int ncl = (nchk+1)*sizeof(char*); /* bytes in new chunk list */
char *newchk, *newcl;
/* get new chunk and add one to chunk list */
newchk = malloc (NDBCHNKO*siz);
if (!newchk)
return (NULL);
newcl = dbtp->mem ? realloc ((char*)dbtp->mem, ncl)
: malloc (ncl);
if (!newcl) {
free (newchk);
return (NULL);
}
/* put chunk on list and record more room */
dbtp->mem = (char**) newcl;
dbtp->mem[nchk] = newchk;
dbtp->nmem += NDBCHNKO;
}
/* copy newop to list in next available chunk */
return(memcpy(DBCatObj(dbcp, newop->o_type, dbtp->nuse++), newop, siz));
}
/* set up the basic database.
*/
static void
db_init()
{
char buf[256];
char nm[1][MAXNM];
DBCat *dbcp;
Obj *biop;
int n;
int i;
/* first catalog is for the builtin objects */
dbcp = db_catadd (bi_catname);
n = getBuiltInObjs (&biop);
for (i = 0; i < n; i++) {
Obj *op = db_objadd (dbcp, &biop[i]);
strcpy (nm[0], op->o_name);
dupaddnm (nm, 1, op);
}
dupsort();
/* inform subsystem where to find moon tables */
sprintf (buf, "%s/auxil", getShareDir());
setMoonDir (XtNewString(buf)); /* must provide perm storage */
/* register the initial files list */
sr_reg (0, dbifres, dbcategory, 1);
}
/* given a catalog, type and index, return pointer to object */
static Obj *
DBCatObj (DBCat *dbcp, int t, int i)
{
DBTMem *dbtp = &dbcp->tmem[t];
return ((Obj*)&dbtp->mem[(i)/NDBCHNKO][((i)%NDBCHNKO)*dbtp->siz]);
}
/* called whenever there is input readable from the db fifo.
* read and crack what we can.
* be prepared for partial lines split across reads.
* N.B. do EXACTLY ONE read -- don't know that more won't block.
* set the watch cursor while we work and call all_newdb() when we're done.
* we guess we are "done" when we end up without a partial line.
*/
/* ARGSUSED */
static void
dbfifo_cb (client, fdp, idp)
XtPointer client; /* file name */
int *fdp; /* pointer to file descriptor */
XtInputId *idp; /* pointer to input id */
{
static char partial[MAXDBLINE]; /* partial line from before */
static int npartial; /* length of stuff in partial[] */
int alts = db_chkAltNames(); /* whether to check dup names */
char buf[16*1024]; /* nice big read gulps */
char *name = (char *)client; /* fifo filename */
int nr; /* number of bytes read from fifo */
/* turn on the watch cursor if there's no prior line */
if (!npartial)
watch_cursor (1);
/* catch up where we left off from last time */
if (npartial)
(void) strcpy (buf, partial);
/* read what's available up to the room we have left.
* if we have no room left, it will look like an EOF.
*/
nr = read (db_fifofd, buf+npartial, sizeof(buf)-npartial);
if (nr > 0) {
char c, *lp, *bp, *ep; /* last line, current, end */
/* process each whole line */
ep = buf + npartial + nr;
for (lp = bp = buf; bp < ep; ) {
c = *bp++;
if (c == '\n') {
bp[-1] = '\0'; /* replace nl with EOS */
if (*lp == DBFIFO_MSG) {
xe_msg (0, "DBFIFO message: %s", lp+1);
} else {
Obj o;
char nm[MAXDUPS][MAXNM];
int nnm;
if ((nnm=db_crack_line(lp,&o,nm,MAXDUPS,NULL)) < 0) {
xe_msg (0, "Bad DBFIFO line: %s", lp);
} else if (!alts || !dupchknm (nm, nnm)) {
Obj *op;
if (is_type(&o, PLANETM)) {
xe_msg (0,
"Planet %s ignored from DBFIFO",o.o_name);
} else {
DBCat *dbcp = db_nmfindcat (fifo_catname);
if (!dbcp)
dbcp = db_catadd (fifo_catname);
if (!dbcp || !(op = db_objadd (dbcp, &o)))
xe_msg (0, "No more memory for DBFIFO");
else {
dupaddnm (nm, alts ? nnm : 1, op);
dupsort();
}
}
}
}
lp = bp;
}
}
/* save any partial line for next time */
npartial = ep - lp;
if (npartial > 0) {
if (npartial > sizeof(partial)) {
xe_msg (0,"Discarding long line in %.100s.\n",name);
npartial = 0;
} else {
*ep = '\0';
(void) strcpy (partial, lp);
}
}
} else {
if (nr < 0)
xe_msg (1, "Error reading %.150s: %.50s.\n",
name, syserrstr());
else
xe_msg (1, "Unexpected EOF on %.200s.\n", name);
XtRemoveInput (db_fifoid);
(void) close (db_fifofd);
db_fifofd = -1;
npartial = 0;
}
/* if there is not likely to be more coming inform everyone about all
* the new stuff and turn off the watch cursor.
*/
if (!npartial) {
all_newdb (1);
watch_cursor (0);
}
}
/* compare 2 pointers to DBCat's by name in qsort fashion */
static int
db_catcmpf (const void *d1, const void *d2)
{
return (strcmp (((DBCat *)d1)->name, ((DBCat *)d2)->name));
}
/* append nm's to the dup list, each pointing to op.
* we increment ndupnames but not ndupsorted.
* N.B. result is not sorted .. use dupsort() when finished
*/
static void
dupaddnm (char nm[][MAXNM], int nnm, Obj *op)
{
int i;
if (mdupnames < ndupnames + nnm)
dupnames = (DupName *) XtRealloc ((char *)dupnames,
(mdupnames = ndupnames+10*nnm)*sizeof(DupName));
for (i = 0; i < nnm; i++) {
strcpy (dupnames[ndupnames+i].nm, nm[i]);
dupnames[ndupnames+i].op = op;
}
ndupnames += nnm;
}
/* delete each dupnames[] IN PLACE that points to catalog dbcp.
* N.B. we assume this will never be called to remove a builtin object
*/
static void
dupdelcat (DBCat *dbcp)
{
int from, to;
/* scan the dupnames list, copy over any entries pointing to dbcp */
for (to = from = 0; from < ndupnames; from++) {
Obj *op = dupnames[from].op;
int t = op->o_type;
int n = dbcp->tmem[t].nuse;
int i;
/* see if op is within any chunk of type t in catalog dbcp */
for (i = 0; i < n; i += NDBCHNKO) {
Obj *oplo= DBCatObj(dbcp, t, i);
Obj *ophi= DBCatObj(dbcp, t, i+NDBCHNKO>n ? n-1 : i+NDBCHNKO-1);
if (oplo <= op && op <= ophi)
break; /* yes, this dupname refers to catalog dbcp */
}
/* keep if not in dbcp */
if (i >= n) {
if (from > to)
memcpy (&dupnames[to], &dupnames[from], sizeof(DupName));
to++;
}
}
/* new count */
ndupnames = to;
/* unsorted, if any, are at end of list */
if (ndupnames < ndupsorted)
ndupsorted = ndupnames;
/* cut back to ndupnames memory */
dupnames = (DupName *) XtRealloc ((char *)dupnames,
(mdupnames = ndupnames)*sizeof(DupName));
}
/* check whether any of the nm[] names are already in the sorted portion of
* dup list. if at least one is, add all the ones that aren't and return -1
* else don't add anything and return 0 (meaning "no dups")
*/
static int
dupchknm (char nm[][MAXNM], int nnm)
{
Obj *op = NULL;
int i;
for (i = 0; i < nnm; i++) {
/* binary search to find a matching dupname */
int l = 0;
int u = ndupsorted - 1;
int m = -1, diff = -1;
while (l <= u) {
m = (l+u)/2;
diff = strnncmp (nm[i], dupnames[m].nm);
if (diff == 0)
break; /* found dup */
if (diff < 0)
u = m-1;
else
l = m+1;
}
if (diff == 0) {
/* found a matching dup, add all prev if first */
if (!op) {
op = dupnames[m].op;
dupaddnm (nm, i, op);
}
} else {
/* no match, add if found a dup already */
if (op)
dupaddnm (&nm[i], 1, op);
}
}
/* return 0 if found no matches */
return (op ? -1 : 0);
}
/* compare pointers to two DupNames by nm, qsort-style */
static int
dup_qsort (const void *p1, const void *p2)
{
return (strnncmp (((DupName*)p1)->nm, ((DupName*)p2)->nm));
}
/* sort dupnames[] by name */
static void
dupsort (void)
{
qsort (dupnames, ndupnames, sizeof(dupnames[0]), dup_qsort);
ndupsorted = ndupnames;
}
/* For RCS Only -- Do Not Edit */
static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: db.c,v $ $Date: 2010/01/18 01:47:02 $ $Revision: 1.46 $ $Name: $"};