#include #include #include "kwdb.h" /* * KWDB -- Keyword Database interface. * * The KWDB is a simple keyword/value interface which buffers a list of * keywords in memory within the KWDB. * * KWDB routines: * * kwdb = kwdb_Open (kwdbname) * name = kwdb_Name (kwdb) * nkw = kwdb_Len (kwdb) * kwdb_Close (kwdb) * * kwdb_AddEntry (kwdb, keyword, value, type, comment) * value = kwdb_GetValue (kwdb, keyword) * kwdb_SetValue (kwdb, keyword, value) * kwdb_SetComment (kwdb, keyword, comment) * str = kwdb_GetComment (kwdb, keyword) * kwdb_SetType (kwdb, keyword, type) * type = kwdb_GetType (kwdb, keyword) * * ep = kwdb_Lookup (kwdb, keyword, instance) * ep = kwdb_Head (kwdb) * ep = kwdb_Tail (kwdb) * ep = kwdb_Next (kwdb, ep) * kwdb_DeleteEntry (kwdb, ep) * kwdb_RenameEntry (kwdb, ep, newname) * kwdb_CopyEntry (kwdb, o_kwdb, o_ep, newname) * count = kwdb_GetEntry (kwdb, ep, keyword, value, type, comment) * name = kwdb_KWName (kwdb, ep) * * Associated utility routines: * * kwdb = kwdb_OpenFITS (filename, maxcards, nblank) * kwdb_UpdateFITS (kwdb, filename, update, extend, npad) * count = kwdb_ReadFITS (kwdb, fd, maxcards, nblank) * kwdb_WriteFITS (kwdb, fd) * kwdb_SetIO (kwdb, readfcn, writefcn) * * FITS card types: * * S=string L=logical N=numeric C=comment H=history T=text * * The kwdbname is a string identifying the database. The value of the * string is arbitrary and is not used internally by KWDB. kwdb_Name * returns the value of kwdbname entered in kwdb_Open. kwdb_Len returns * the number of keywords (or other entries including null entries) in the * database. kwdb_Close destroys a database and frees all resources used * by the KWDB. * * KWDB entries are maintained in a FIFO list and may be referenced either * by the entry reference EP or by the keyword name if the entry is a * keyword. Multiple entries with the same keyword name are permitted, in * which case the most recent entry takes precedence. * * kwdb_AddEntry adds an entry to the database. kwdb_GetValue returns the * value of a keyword (as a string) given its name. kwdb_SetValue sets the * value string of a keyword; kwdb_SetComment sets the comment field. * kwdb_Lookup searches the database for the most recent entry for a keyword * and returns the entry reference: zero is returned if the keyword is not * found. * * kwdb_CopyEntry copies an entry from one database to another. * kwdb_DeleteEntry deletes an entry. kwdb_RenameEntry renames an entry: * either the old or new name can be nil. Renaming an entry does not change * its position in the list but if there are redefinitions it becomes the * most recent instance. kwdb_GetEntry gets an entry given the symbol * reference. kwdb_Head returns the entry reference EP of the first entry in * the list. kwdb_Next returns the reference for the next entry after the * current one. * * All keyword values and other fields are stored and manipulated as strings. * There is an associated data type field however which can be used to guide * how the value field is interpreted or presented. The contents of the type * field are up to the application (e.g., "bool", "int", "float", "string"). * * The high level utility routines kwdb_OpenFITS and kwdb_UpdateFITS create * a new KWDB and load the header of a FITS file into it (OpenFITS), and * save a KWDB to a new FITS file or update the header of an existing file * (UpdateFITS). kwdb_ReadFITS and kwdb_WriteFITS read and write FITS cards * to a file stream. kwdb_SetIO can be used to enter private file read and * write functions to be used by ReadFITS/WriteFITS, allowing data other than * host files to be read and written. * * These are the only routines in KWDB which know anything about FITS, the * rest of the interface provides a general purpose symbol table with special * characteristics of FIFO ordering, non-keyword entries, and comment lines * on keywords. */ /* Data structure design. * ---------------------- * hash table * hash to thread table * thread points to head of linked-list of entries * search down list for first matching entry * link new entries at head of list * descriptor array (indexed) * include hash table thread links * sbuf * character storage * append new strings * strings referenced by index */ #define NTHREADS 797 #define DEF_SZSBUF 32768 #define DEF_NKEYWORDS 512 #define MAX_HASHCHARS 18 #define SZ_FILEBUF 102400 #define COM_EP(db,p) ((p)-db->itab) #define REF_EP(db,ep) &db->itab[ep] int primes[] = { 101,103,107,109,113,127,131,137,139, 149,151,157,163,167,173,179,181,191, }; /* KWDB item descriptor. */ struct item { int name; /* item name (optional) */ int value, vallen; /* item value (optional) */ int type, typelen; /* value type (optional) */ int comment, comlen; /* item comment (optional) */ int nexthash; /* next item in hash thread */ int nextglob; /* next item in global list */ }; typedef struct item Item; typedef int (*PFI)(); struct kwdb { int kwdbname; /* database name */ int maxitems; /* capacity of itab */ int nitems; /* number of valid items */ int itemsused; /* number of item slots used */ int sbuflen; /* capacity of sbuf */ int sbufused; /* sbuf characters used */ int head; /* itab index of first item */ int tail; /* itab index of last item */ Item *itab; /* pointer to itab array */ char *sbuf; /* pointer to string buffer */ int hashtbl[NTHREADS]; /* hash table */ int (*read)(); /* private file read function */ int (*write)(); /* private file write function */ }; typedef struct kwdb KWDB; extern char *mktemp(); extern int read(), write(); static int hash(); static int addstr(); static int streq(); /* * Public functions. * ----------------- */ /* KWDB_OPEN -- Open a new, empty keyword database. */ pointer kwdb_Open (kwdbname) char *kwdbname; { register KWDB *db = NULL; Item *itab = NULL; char *sbuf = NULL; if (!(db = (KWDB *) calloc (1, sizeof(KWDB)))) goto cleanup; if (!(itab = (Item *) calloc (DEF_NKEYWORDS, sizeof(Item)))) goto cleanup; if (!(sbuf = (char *) calloc (DEF_SZSBUF, sizeof(char)))) goto cleanup; sbuf[0] = '\0'; db->kwdbname = 1; strcpy (sbuf+1, kwdbname); db->sbufused = strlen(kwdbname) + 2; db->sbuflen = DEF_SZSBUF; db->sbuf = sbuf; db->maxitems = DEF_NKEYWORDS; db->nitems = 0; db->itemsused = 1; db->head = 0; db->tail = 0; db->itab = itab; db->read = read; db->write = write; return ((pointer) db); cleanup: if (sbuf) free (sbuf); if (itab) free ((char *) itab); if (db) free ((char *) db); return (NULL); } /* KWDB_CLOSE -- Destroy a KWDB database and free all resources. */ void kwdb_Close (kwdb) pointer kwdb; { register KWDB *db = (KWDB *) kwdb; if (db->sbuf) free (db->sbuf); if (db->itab) free ((char *) db->itab); if (db) free ((char *) db); } /* KWDB_NAME -- Return the name of a KWDB database. */ char * kwdb_Name (kwdb) pointer kwdb; { register KWDB *db = (KWDB *) kwdb; return (db->sbuf + db->kwdbname); } /* KWDB_LEN -- Return the number of items in a KWDB database. */ kwdb_Len (kwdb) pointer kwdb; { register KWDB *db = (KWDB *) kwdb; return (db->nitems); } /* KWDB_ADDENTRY -- Append an entry to the keyword database. Any of the * fields keyword, value, type, or comment can be NULL if these fields * have no value. */ kwdb_AddEntry (kwdb, keyword, value, type, comment) pointer kwdb; char *keyword, *value, *type, *comment; { register KWDB *db = (KWDB *) kwdb; register Item *itp, *otp; int index, i; /* Make sure there is space in the item buffer. */ if (db->itemsused >= db->maxitems) { db->maxitems += DEF_NKEYWORDS; itp = (Item *) realloc ((char *)db->itab, sizeof(Item) * db->maxitems); if (!itp) return (-1); db->itab = itp; } index = db->itemsused++; itp = &db->itab[index]; itp->name = addstr (db, keyword); itp->value = addstr (db, value); itp->vallen = value ? strlen(value) : 0; itp->type = addstr (db, type); itp->typelen = type ? strlen(type) : 0; itp->comment = addstr (db, comment); itp->comlen = comment ? strlen(comment) : 0; itp->nexthash = 0; itp->nextglob = 0; /* Link item at head of global list if list empty. */ if (!db->head) db->head = index; /* Link item at tail of global list. */ if (db->tail) { otp = REF_EP(db,db->tail); otp->nextglob = index; } db->tail = index; /* Enter item in hash table. */ if (keyword && *keyword) { int hashval = hash (keyword); if (i = db->hashtbl[hashval]) itp->nexthash = i; db->hashtbl[hashval] = index; } db->nitems++; return (COM_EP(db,itp)); } /* KWDB_LOOKUP -- Lookup a keyword in the database. If there are multiple * values for the same keyword in the database then the instance number * determines which is returned. An instance of zero returns the first * (most recently entered) instance. Successive instances return entries * earlier in the list. Note that this is opposite of an ordered traversal * of the list using kwdb_Head/kwdb_Next, which accesses the list with * oldest entries first. */ kwdb_Lookup (kwdb, keyword, instance) pointer kwdb; char *keyword; int instance; { register KWDB *db = (KWDB *) kwdb; register Item *itp; register int i, j; int hashval; hashval = hash (keyword); if (i = db->hashtbl[hashval]) for (itp = REF_EP(db,i), j=0; i; itp = REF_EP(db,i=itp->nexthash)) if (streq (db->sbuf + itp->name, keyword)) if (instance == j++) return (i); return (0); } /* KWDB_GETVALUE -- Lookup a keyword and returns its value. NULL is returned * if the keyword is not found. */ char * kwdb_GetValue (kwdb, keyword) pointer kwdb; char *keyword; { register KWDB *db = (KWDB *) kwdb; register Item *itp; register int ep; if (ep = kwdb_Lookup (kwdb, keyword, 0)) { itp = REF_EP(db,ep); return (db->sbuf + itp->value); } return (NULL); } /* KWDB_SETVALUE -- Modify the value of a keyword. Zero is returned for a * successul operation, otherwise -1. */ kwdb_SetValue (kwdb, keyword, value) pointer kwdb; char *keyword, *value; { register KWDB *db = (KWDB *) kwdb; register Item *itp = NULL; char *oldval; int ep; if (ep = kwdb_Lookup (kwdb, keyword, 0)) itp = REF_EP(db,ep); else return (-1); oldval = db->sbuf + itp->value; memset (oldval, 0, itp->vallen); if (strlen(value) > itp->vallen) { itp->value = addstr (db, value); itp->vallen = strlen (value); } else strcpy (oldval, value); return (0); } /* KWDB_SETCOMMENT -- Modify the comment field of a keyword. Zero is * returned for a successul operation, otherwise -1. */ kwdb_SetComment (kwdb, keyword, comment) pointer kwdb; char *keyword, *comment; { register KWDB *db = (KWDB *) kwdb; register Item *itp = NULL; char *oldcom; int ep; if (ep = kwdb_Lookup (kwdb, keyword, 0)) itp = REF_EP(db,ep); else return (-1); oldcom = db->sbuf + itp->comment; memset (oldcom, 0, itp->comlen); if (strlen(comment) > itp->comlen) { itp->comment = addstr (db, comment); itp->comlen = strlen (comment); } else strcpy (oldcom, comment); return (0); } /* KWDB_GETCOMMENT -- Lookup a keyword and returns its comment field. NULL * is returned if the keyword is not found. */ char * kwdb_GetComment (kwdb, keyword) pointer kwdb; char *keyword; { register KWDB *db = (KWDB *) kwdb; register Item *itp; register int ep; if (ep = kwdb_Lookup (kwdb, keyword, 0)) { itp = REF_EP(db,ep); return (db->sbuf + itp->comment); } return (NULL); } /* KWDB_SETTYPE -- Modify the data type field of a keyword. Zero is * returned for a successul operation, otherwise -1. */ kwdb_SetType (kwdb, keyword, type) pointer kwdb; char *keyword, *type; { register KWDB *db = (KWDB *) kwdb; register Item *itp = NULL; char *oldtype; int ep; if (ep = kwdb_Lookup (kwdb, keyword, 0)) itp = REF_EP(db,ep); else return (-1); oldtype = db->sbuf + itp->type; memset (oldtype, 0, itp->typelen); if (strlen(type) > itp->typelen) { itp->type = addstr (db, type); itp->typelen = strlen (type); } else strcpy (oldtype, type); return (0); } /* KWDB_GETTYPE -- Lookup a keyword and returns its type field. NULL * is returned if the keyword is not found. */ char * kwdb_GetType (kwdb, keyword) pointer kwdb; char *keyword; { register KWDB *db = (KWDB *) kwdb; register Item *itp; register int ep; if (ep = kwdb_Lookup (kwdb, keyword, 0)) { itp = REF_EP(db,ep); return (db->sbuf + itp->type); } return (NULL); } /* KWDB_HEAD -- Return the entry pointer for the first entry in the database. */ kwdb_Head (kwdb) pointer kwdb; { register KWDB *db = (KWDB *) kwdb; return (db->head); } /* KWDB_TAIL -- Return the entry pointer for the most recent entry in * the database. */ kwdb_Tail (kwdb) pointer kwdb; { register KWDB *db = (KWDB *) kwdb; return (db->tail); } /* KWDB_NEXT -- Return the entry pointer for the next entry in a database, * given a pointer to the preceding entry. Zero is returned at the end of * the database. */ kwdb_Next (kwdb, ep) pointer kwdb; int ep; { register KWDB *db = (KWDB *) kwdb; register Item *itp = NULL; itp = REF_EP(db,ep); return (itp->nextglob); } /* KWDB_DELETEENTRY -- Delete an entry from the database given its entry * pointer. */ kwdb_DeleteEntry (kwdb, ep) pointer kwdb; int ep; { register KWDB *db = (KWDB *) kwdb; register Item *itp = NULL, *otp; int hashval, i; char *name; if (ep <= 0 || ep > db->tail) return (-1); itp = REF_EP(db,ep); name = db->sbuf + itp->name; /* If the entry is a keyword remove it from the hash table. */ if (itp->name && *name) { hashval = hash (name); i = db->hashtbl[hashval]; if (ep == i) db->hashtbl[hashval] = itp->nexthash; else { otp = REF_EP(db,i); for (i=otp->nexthash; i; otp=REF_EP(db,i), i=otp->nexthash) if (ep == i) { otp->nexthash = itp->nexthash; break; } } } /* Remove the entry from the global list. */ if (db->head == ep) db->head = itp->nextglob; for (i=db->head; i; i = otp->nextglob) { otp = REF_EP(db,i); if (otp->nextglob == ep) { otp->nextglob = itp->nextglob; break; } } if (db->tail == ep) { db->tail = i; db->itemsused--; } db->nitems--; return (0); } /* KWDB_RENAMEENTRY -- Rename an entry given its entry pointer. * Either the old or new name can be nil. Renaming an entry does not change * its position in the list but if there are redefinitions it becomes the * most recent instance. */ kwdb_RenameEntry (kwdb, ep, newname) pointer kwdb; int ep; char *newname; { register KWDB *db = (KWDB *) kwdb; register Item *itp = NULL, *otp; int hashval, i; char *name; if (ep <= 0 || ep > db->tail) return (-1); itp = REF_EP(db,ep); name = db->sbuf + itp->name; /* If the entry is a keyword remove it from the hash table. */ if (itp->name && *name) { hashval = hash (name); i = db->hashtbl[hashval]; if (ep == i) db->hashtbl[hashval] = itp->nexthash; else { otp = REF_EP(db,i); for (i=otp->nexthash; i; otp=REF_EP(db,i), i=otp->nexthash) if (ep == i) { otp->nexthash = itp->nexthash; break; } } } /* Reenter the item in the hash table using the new name. */ if (newname && *newname) { int hashval = hash (newname); if (i = db->hashtbl[hashval]) itp->nexthash = i; db->hashtbl[hashval] = ep; itp->name = addstr (db, newname); } else itp->name = 0; return (0); } /* KWDB_COPYENTRY -- Copy an entry from one database to another. If newname * is not not null it will be the name of the new entry. The input and output * datbases can be the same. */ kwdb_CopyEntry (kwdb, o_kwdb, o_ep, newname) pointer kwdb; pointer o_kwdb; int o_ep; char *newname; { register KWDB *db = (KWDB *) kwdb; register KWDB *o_db = (KWDB *) o_kwdb; register Item *itp = REF_EP(o_db,o_ep); char *name; if (newname && *newname) name = newname; else name = itp->name + o_db->sbuf; return (kwdb_AddEntry (kwdb, name, itp->value + o_db->sbuf, itp->type + o_db->sbuf, itp->comment + o_db->sbuf)); } /* KWDB_GETENTRY -- Get an entry given its entry pointer. A count of the * number of nonempty strings is returned (i.e. zero is returned if the value * is a blank line, 3 for a full keyword with value and comment). */ kwdb_GetEntry (kwdb, ep, keyword, value, type, comment) pointer kwdb; int ep; char **keyword, **value, **type, **comment; { register KWDB *db = (KWDB *) kwdb; register Item *itp; if (ep <= 0 || ep > db->tail) return (-1); else itp = REF_EP(db,ep); if (keyword) *keyword = itp->name + db->sbuf; if (value) *value = itp->value + db->sbuf; if (type) *type = itp->type + db->sbuf; if (comment) *comment = itp->comment + db->sbuf; return (itp->name + itp->value + itp->type, itp->comment); } /* KWDB_KWNAME -- Return a pointer to the name of a keyword given its entry * pointer. */ char * kwdb_KWName (kwdb, ep) pointer kwdb; int ep; { register KWDB *db = (KWDB *) kwdb; register Item *itp; if (ep <= 0 || ep > db->tail) return (NULL); else itp = REF_EP(db,ep); return (itp->name + db->sbuf); } /* * Utility routines. * ------------------- */ /* KWDB_OPENFITS -- Open a FITS file and read the file header into a KWDB * keyword database. Each 80 character card image in the input file * corresponds to one entry in the output KWDB. Values of the type field * generated are L (logical), S (string), N (numeric), H (history), * C (comment), T (text other than history or comment), and B (blank). * Any trailing blank lines at the end of the header are omitted, but a * count of the number of blank trailers can be returned as an argument if * desired. The KWDB pointer is returned as the function value. */ pointer kwdb_OpenFITS (filename, maxcards, nblank) char *filename; /* file to be opened */ int maxcards; /* maximum FITS cards to be read */ int *nblank; /* if not NULL receives count of blank lines at end */ { register KWDB *kwdb; int fd; /* Open FITS file. */ if ((fd = open (filename, O_RDONLY)) < 0) return (NULL); /* Open new, empty KWDB. */ if (!(kwdb = (KWDB *) kwdb_Open (filename))) { close (fd); return (NULL); } /* Scan the file into the KWDB. */ if (kwdb_ReadFITS (kwdb, fd, maxcards, nblank) < 0) { close (fd); return (NULL); } close (fd); return ((pointer) kwdb); } /* KWDB_READFITS -- Scan a file stream and load successive FITS cards into * a keyword database. Each 80 character card image in the input file * corresponds to one entry in the output KWDB. Values of the type field * generated are L (logical), S (string), N (numeric), H (history), * C (comment), T (text other than history or comment), and B (blank). * Any trailing blank lines at the end of the header are omitted, but a * count of the number of blank trailers can be returned as an argument if * desired. A count of the number of cards read is returned as the function * value, or -1 if an error occurs. */ kwdb_ReadFITS (kwdb, fd, maxcards, nblank) pointer kwdb; int fd; int maxcards; int *nblank; { register KWDB *db = (KWDB *) kwdb; register char *ip, *op; char keyword[9], value[80], type[2], comment[80]; int ncards, istext, isstring, iscomment, ishistory, ep, nb, n; char card[80], *lop; for (ncards=0; (!maxcards || ncards < maxcards) && db->read(fd,card,80)==80; ncards++) { istext = isstring = 0; /* Get keyword field (if any). */ for (ip=card, n=8, op=keyword; --n >= 0 && *ip != ' '; ) *op++ = *ip++; *op = '\0'; /* Quit when we see the END card, or if we miss the END card and * run into some binary data. */ for (ip=card, n=8; --n >= 0; ip++) if (!isprint (*ip)) { strcpy (keyword, "END "); break; } if (streq (keyword, "END")) break; iscomment = (streq (keyword, "COMMENT")); ishistory = (streq (keyword, "HISTORY")); /* Get value field if any. */ if (!iscomment && !ishistory && card[8] == '=') { istext = 0; op = value; for (ip=card+9, n=21; --n >= 0 && isspace(*ip); ) ip++; if (*ip == '\'') { ip++; isstring = 1; while (*ip != '\'' && ip < card+80) *op++ = *ip++; ip++; } else { while (*ip != ' ' && *ip != '/' && ip < card+80) *op++ = *ip++; } *op = '\0'; /* Get comment field if any. Trailing whitespace is trimmed. */ while ((*ip == '/' || *ip == ' ') && ip < card+80) ip++; for (op=lop=comment; ip < card+80 && *ip != '\n'; ip++) { *op++ = *ip; if (*ip != ' ') lop = op; } *lop = '\0'; } else { istext = 1; /* Process history or comment (text) card. The text is placed * in the value field. Trailing whitespace is trimmed. */ op = lop = value; for (ip=card+8; ip < card+80 && *ip != '\n'; ip++) { *op++ = *ip; if (*ip != ' ') lop = op; } *lop = '\0'; } /* Determine the card type. */ if (istext) { if (iscomment) strcpy (type, "C"); else if (ishistory) strcpy (type, "H"); else strcpy (type, "T"); } else { if (isstring) strcpy (type, "S"); else if (streq(value,"T") || streq(value,"F")) strcpy (type, "L"); else strcpy (type, "N"); } /* Enter the card into the KWDB. */ if (kwdb_AddEntry(kwdb, keyword, value, type, comment) < 0) { return (-1); } } /* Trim any blank lines at the end of the header list. */ for (ep=kwdb_Tail(kwdb), nb=0; ep > 0; ep=kwdb_Tail(kwdb), nb++) { char *keyword, *value, *type; if (kwdb_GetEntry (kwdb, ep, &keyword, &value, &type, NULL) >= 0) if (*type == 'T' && *value == '\0') kwdb_DeleteEntry (kwdb, ep); else break; } if (nblank) *nblank = nb; return (ncards); } /* KWDB_UPDATEFITS -- Update the contents of a FITS file header from a KWDB * database to a FITS header. If update=0 a new FITS file is written * consisting of only a header (from the KWDB) and no data. If update=1 the * header of an existing file is updated in place, replacing the entire header * with the contents of the KWDB. If extend=1 in update mode, the file will * automatically be extended if the KWDB will not fit in the existing header * area. */ kwdb_UpdateFITS (kwdb, filename, update, extend, npad) register KWDB *kwdb; char *filename; int update, extend, npad; { register char *ip, *op, *cp; int nblocks, nbytes, lastone, maxcards, new, fd, i; char card[256], tmpfile[512]; char block[SZ_FILEBUF]; char *lop; /* Prepare the output file. If we are updating an existing file it * must exist and must have sufficient space to hold the contents of * the KWDB. If we are writing to a new file we must be able to * create the file. */ if (update) { /* Update an existing file. */ if ((fd = open (filename, O_RDWR)) < 0) return (-1); /* Determine the capacity of the file header. */ lastone = 0; maxcards = 0; while (!lastone && read (fd, block, 2880) == 2880) { for (i=0, cp=block; i < 36; i++, cp += 80) { cp[8] = '\0'; if (streq (cp, "END")) { lastone++; break; } } maxcards += (lastone ? 35 : 36); } /* Extend the file if there is insufficient space and extension * is requested. */ if (kwdb_Len(kwdb) > maxcards) { if (!extend) { close (fd); return (-1); } /* Get a scratch file. */ for (ip=filename, op=lop=tmpfile; *ip; ip++) if ((*op++ = *ip) == '/') lop = op; for (ip="kwdb.XXXXXX", op=lop; *op++ = *ip++; ) ; *op = '\0'; if (!mktemp(tmpfile)) { close (fd); return (-1); } if ((new = open (tmpfile, O_CREAT|O_TRUNC|O_RDWR, 0644)) < 0) { close (fd); return (-1); } /* Write the header area of the new file. */ nblocks = (kwdb_Len(kwdb) + npad + 1) + 35 / 36; memset (block, 0, 2880); while (--nblocks >= 0) if (write (new, block, 2880) != 2880) { abort: close (fd); close (new); unlink (tmpfile); return (-1); } /* Copy the data area of the target file. */ while ((nbytes = read (fd, block, SZ_FILEBUF)) > 0) if (write (new, block, nbytes) != nbytes) goto abort; /* Replace the original file with the extended version. */ if (close (new) < 0) goto abort; if (close (fd) < 0) goto abort; if (rename (tmpfile, filename) < 0) goto abort; /* Ready the newly extended file for header updating. */ if ((fd = open (filename, O_RDWR)) < 0) return (-1); } else lseek (fd, 0L, SEEK_SET); } else { /* Write a new file. */ if ((fd = open (filename, O_CREAT|O_WRONLY, 0644)) < 0) return (-1); } /* Output the KWDB as a FITS file header. */ kwdb_SetIO (kwdb, read, write); if (kwdb_WriteFITS (kwdb, fd) < 0) { close (fd); return (-1); } /* Write the END card to mark the end of the header. */ strcpy (card, "END"); memset (card+3, ' ', 80-3); if (write (fd, card, 80) != 80) { close (fd); return (-1); } return (close (fd)); } /* KWDB_WRITEFITS -- Write the contents of a KWDB database to a file stream. * The KWDB entries are written out one per FITS card without any checks. * No END card is written. WriteFITS may be called to write all or part of * a FITS header. */ kwdb_WriteFITS (kwdb, fd) KWDB *kwdb; int fd; { register KWDB *db = (KWDB *) kwdb; register char *ip, *op, *cp; int ncards=0, ep, n, ch; char card[256]; for (ep=kwdb_Head(kwdb); ep; ep=kwdb_Next(kwdb,ep), ncards++) { char *keyword, *value, *type, *comment; /* Get entry from database. */ if ((kwdb_GetEntry(kwdb,ep,&keyword,&value,&type,&comment)) < 0) return (-1); /* Format the FITS card. */ memset (card, ' ', 80); for (ip=keyword, op=card, n=8; *ip && --n >= 0; ) { ch = *ip++; if (isprint (ch)) *op++ = ch; } switch (*type) { case 'S': case 'N': case 'L': card[8] = '='; op = card + 10; /* Output value field. */ if (*type == 'S') { *op++ = '\''; for (ip=value; *ip && op < card+80; ) { ch = *ip++; if (isprint (ch)) *op++ = ch; } while (op < card+19) op++; *op++ = '\''; while (op < card+30) op++; } else { if ((n = 20 - strlen(value)) > 0) op += n; for (ip=value; *ip && op < card+80; ) { ch = *ip++; if (isprint (ch)) *op++ = ch; } } /* Output comment. */ if (*comment && op < card+80) { op++; *op++ = '/'; op++; for (ip=comment; *ip && op < card+80; ) { ch = *ip++; if (isprint (ch)) *op++ = ch; } } break; default: for (ip=value, op=card+8; *ip && op < card+80; ) *op++ = *ip++; break; } /* Write card to the output file. */ if (db->write (fd, card, 80) != 80) return (-1); } return (ncards); } /* KWDB_SETIO -- Set the read and write functions to be used by kwdb_ReadFITS * and kwdb_WriteFITS. */ kwdb_SetIO (kwdb, readfcn, writefcn) register KWDB *kwdb; int (*readfcn)(); int (*writefcn)(); { register KWDB *db = (KWDB *) kwdb; db->read = readfcn; db->write = writefcn; } /* * Internal functions. * ------------------- */ /* ADDSTR -- Add a string to the database string buffer. The sbuf index of * the string is returned. If the string is NULL or empty zero is returned. * Element zero of sbuf is always the null/empty string. */ static int addstr (db, text) register KWDB *db; char *text; { int offset, sbuflen, nchars; char *sbuf; if (!text || !*text) return (0); nchars = strlen(text) + 1; /* Get more space if the string buffer fills. */ if (db->sbufused + nchars >= db->sbuflen) { sbuflen = db->sbuflen + DEF_SZSBUF; if (!(sbuf = (char *) realloc (db->sbuf, sbuflen))) return (0); db->sbuf = sbuf; db->sbuflen = sbuflen; } strcpy (db->sbuf + db->sbufused, text); offset = db->sbufused; db->sbufused += nchars; return (offset); } /* HASH -- Compute the (case insensitive) hash value of a string. */ static int hash (key) char *key; { register char *ip; register int sum, i, ch; for (ip=key, sum=i=0; (ch = *ip) && i < MAX_HASHCHARS; i++) sum += (isupper(ch) ? tolower(ch) : ch) * primes[i]; return (sum % NTHREADS); } /* STREQ -- Case insensitive string compare. */ static int streq (s1, s2) register char *s1, *s2; { register int c1, c2; for (;;) { c1 = *s1++; c2 = *s2++; if (!c1 || !c2) break; if (isupper(c1)) c1 = tolower(c1); if (isupper(c2)) c2 = tolower(c2); if (c1 != c2) break; } return (!c1 && !c2); }