/*** doclib.c - standard DH library routines * cl /c /Zep /D LINT_ARGS doclib.c */ #include #include #include "dh.h" #include "dhint.h" #ifdef XENIX #define O_BINARY 0 /* null or in constant */ #endif /* external functions */ extern int fprintf(); extern char *getenv(); extern char *malloc(); extern int free(); extern int open(); extern int close(); extern int read(); extern int write(); extern long _lseek(); extern int exit(); extern char *strcat(); extern char *strchr(); extern int strcmp(); extern char *strcpy(); extern char *_strdup(); extern int strlen(); extern char *strncpy(); /* ** Globals */ static Folder foldertab[NFOLDERS]; static Document doctab[NDOCS]; static char *dhpath = NULL; /* ** Forward functions */ static int mkfold(char *,struct Folder *); static unsigned int setnipe(void); static int pathsrch(char *,struct Folder *); static char *pathcat(char * *,char *); static unsigned int setsize(unsigned int ); static struct Folder *fhtofp(int ); static int readindex(struct Folder *,int ,struct Index *); static int writeindex(struct Folder *,int ,struct Index *); static int getextent(struct Folder *,int ,int ); static int addextent(struct Folder *,int ); static void flushextent(struct Folder *); static void clearextent(struct Folder *); static struct Document *dhtodp(int ); static char *nlnl(char *); /*global*/ int getfolder(char *,int ); /*global*/ void putfolder(int ); /*global*/ char *getname(int ); /*global*/ int getdoc(int ,int ,int ); /*global*/ int scanfolder(int ,int ); /*global*/ int putdoc(int ); /*global*/ int deldoc(int ,int ); /*global*/ int gettext(int ,int ); /*global*/ char *gethdr(int ); /*global*/ int getbdy(int ,int ); /*global*/ int puttext(int ,int ); /*global*/ int putbody(int ,int ); /*global*/ int puthdr(int ,char *); /*global*/ int getid(int ); char *(*dh_alloc) (int) = malloc; void (*dh_free) (char *) = free; /*** getfolder - prepare a DH folder for manipulation * * folderhand = getfolder(foldername, function); * * get a folder handle for the folder foldername * function can take one of the following values: * FLD_CREATE create a new folder * FLD_SPEC open existing folder */ Fhandle getfolder(name, func) char *name; int func; { int rv; char *cname; int fd; Folder *fp; /* find first free slot */ for (fp = foldertab; fp < &foldertab[NFOLDERS]; fp += 1) { if (fp->f_flags == 0) break; } switch(func) { case FLD_SPEC: /* open a specific folder */ if ( pathsrch(name, fp) == ERROR ) return ERROR; break; case FLD_CREATE: /* create a new subfolder */ if ( mkfold(name, fp) == ERROR ) return ERROR; break; default: /* bad action */ return ERROR; } /* ok, set it up */ fp->f_flags |= F_BUSY; return (Fhandle)(fp - foldertab); } /*** mkfold - create a new folder * * mkfold creates a new, empty folder. The difficult part * is determining the path name of the new folder. The path * name is determined by the following rules: * 1. If the name passed to mkfold contains a PATHSEP then * the name passed is the path name used. * 2. Otherwise, the name is generated by concatenating the * first element of DHPATH with the passed name. * * Folders are created with mode 666 (modified by the creator's * umask). * * Entry: name = name of new folder (see above) * fp = pointer to folder slot to fill * * Return: OK or ERROR */ static int mkfold(name, fp) char *name; Folder *fp; { char *work; int len; if ( strchr(name, PATHSEP) != NULL ) { fp->f_name = _strdup(name); } else { if ( dhpath == NULL && (dhpath = getenv("DHPATH")) == NULL ) { fprintf(stderr, "DHPATH not set.\n"); return ERROR; } if ( (work = strchr(dhpath, PATHBRK)) == NULL ) len = strlen(dhpath); else len = work - dhpath; /* allocate space; one byte for null terminator, one for PATHSEP */ fp->f_name = (*dh_alloc)(len + strlen(name) + 2); /* build the canonical name */ work = fp->f_name + len; if ( len ) { strncpy(fp->f_name, dhpath, len); *work++ = PATHSEP; } strcpy(work, name); } if ( (fp->f_fd = open(fp->f_name, O_RDWR|O_CREAT|O_EXCL|O_BINARY, 0666)) < 0 ) { fprintf(stderr, "Can't create folder '%s'.\n", fp->f_name); (*dh_free) (fp->f_name); return ERROR; } fp->f_control.c_magic = MAGIC; fp->f_control.c_numdoc = 0; fp->f_control.c_nipe = setnipe(); fp->f_extsize = setsize(fp->f_control.c_nipe); write(fp->f_fd, &fp->f_control, sizeof(Control)); fp->f_extent = (Extent *)(*dh_alloc)(fp->f_extsize); clearextent(fp); fp->f_extpos = sizeof(Control); fp->f_extnum = 0; write(fp->f_fd, fp->f_extent, fp->f_extsize); fp->f_flags = 0; return OK; } /*** setnipe - compute how many index entries to use per extent * * setnipe decides how many index entries per extent should be used * when we create a new folder. Currently, it always simply returns * a fixed, per-system value. * */ static unsigned setnipe() { return DEFAULT_NIPE; } /*** pathsrch - search along DHPATH for a folder * * pathsrch tries to locate a folder, using DHPATH if necessary. * If the name that is passed contains a PATHSEP, that is the name * used. Otherwise, the folder is searched for in each directory * specified in DHPATH. * * Entry: name = name of folder (see above) * fp = pointer to folder slot to fill * * Return: ERROR or OK */ static int pathsrch(name, fp) char *name; Folder *fp; { char *path; if ( strchr(name, PATHSEP) != NULL ) { fp->f_name = _strdup(name); if ( (fp->f_fd = open(fp->f_name, O_RDWR|O_BINARY)) < 0 ) { (*dh_free) (fp->f_name); return ERROR; } } else { fp->f_fd = -1; if ( dhpath == NULL && (dhpath = getenv("DHPATH")) == NULL ) { fprintf(stderr, "DHPATH not set.\n"); return ERROR; } path = dhpath; while ( (fp->f_name = pathcat(&path, name)) != NULL ) { if ( (fp->f_fd = open(fp->f_name, O_RDWR|O_BINARY)) >= 0 ) break; (*dh_free) (fp->f_name); } if ( fp->f_fd < 0 ) return ERROR; } read(fp->f_fd, &fp->f_control, sizeof(Control)); if ( fp->f_control.c_magic != MAGIC ) { close(fp->f_fd); (*dh_free) (fp->f_name); return ERROR; } fp->f_extnum = -1; fp->f_extent = NULL; fp->f_extsize = setsize(fp->f_control.c_nipe); fp->f_extpos = 0; fp->f_flags = 0; return OK; } /*** pathcat - help contruct paths for pathsrc * WE NEED BETTER HEADER HERE! */ static char *pathcat(pathpp, name) char **pathpp; char *name; { int len; char *end, *work, *rv; if ( **pathpp == '\0' ) return NULL; if ( (end = strchr(*pathpp, PATHBRK)) != NULL ) len = end - *pathpp; else len = strlen(*pathpp); /* allocate space; one byte for null terminator, one for PATHSEP */ rv = (*dh_alloc)(len + strlen(name) + 2); /* build the canonical name */ work = &rv[len]; if ( len ) { strncpy(rv, *pathpp, len); *work++ = PATHSEP; } strcpy(work, name); /* adjust the path pointer for next time */ *pathpp += len; if ( **pathpp == PATHBRK ) *pathpp += 1; return rv; } /*** setsize - return size of extent, based on nipe, static sizes. * * setsize subtracts the size of one index entry from the size of Extent, * which yields the TRUE size of the static part of an extent. It then * adds the number of index entries per extent (nipe) for this folder * times the size of and index entry. This yields the true size of * an extent for a particular folder. * */ static unsigned setsize(nipe) unsigned nipe; { unsigned size; size = sizeof(Extent) - sizeof(Index); size += nipe * sizeof(Index); return size; } /*** putfolder - close a folder * * Put_folder indicates that this folder is no longer going * to be manipulated. Thus, we can now deallocate any resources * associated with that folder. Currently, those resources are * the alloc'ed string for the canonical name, and possibly * an open stream, if the FLD_FIRST or FLD_NEXT operations of * get_folder have been used. * * Entry: fh = Fhandle of folder to close * Return: none */ void putfolder(fh) Fhandle fh; { Folder *fp; if ((fp = fhtofp(fh)) == NULL) return; if ( fp->f_flags & F_CDIRTY ) { _lseek(fp->f_fd, 0L, 0); write(fp->f_fd, &fp->f_control, sizeof(Control)); fp->f_flags &= ~F_CDIRTY; } if ( fp->f_flags & F_EDIRTY ) flushextent(fp); close(fp->f_fd); (*dh_free) (fp->f_name); if ( fp->f_extent != NULL ) (*dh_free) ((char *) (fp->f_extent)); fp->f_flags = 0; return; } /*** getname - get a DH name for a folder * * Getname gets the name of an open folder. The name of a folder * is the canonical name (see mkcname, above) of the folder, with * the dhroot portion cut off. In other words, we could form * the dh name of a folder by concatenating the name of the parent * folder and the name of the folder itself. * * we deduce the DH name of a folder by examining the canonical * name in the foldertab entry. We pass back a pointer into the * canonical name, with the pointer pointing just past the * dhroot part. * */ char *getname(fh) Fhandle fh; { Folder *fp; if ( (fp = fhtofp(fh)) == NULL ) return NULL; return _strdup(fp->f_name); } /*** fhtofp - convert folder handle to folder pointer * * Outside this module, the only reference to an active folder is * its 'folder handle.' This routine converts this handle to * a pointer to the appropriate entry in the foldertab. * * Entry: fh = folder handle to convert * Return: NULL if fh is not a valid folder handle * pointer to Folder if fh is valid */ static Folder *fhtofp(fh) Fhandle fh; { Folder *rv; if ((fh < 0) || (fh >= NFOLDERS)) return NULL; rv = &foldertab[fh]; if ( (rv->f_flags & F_BUSY) == 0) return NULL; return rv; } /*** index - create/manage the DH folder index * * DH folders contain both the data for the header and body, * and an index that allows programs to locate that data easily. * The index contains one entry for each document. Index * entries are grouped together in 'extents'. Each extent contains * a number of index entries, and pointers to the previous and * subsequent extents. The ends of this doubly linked list marked * by null pointers. * * A header block at the beginning of the file contains relevant * information such as how many extents are allocated, how many * documents there are, etc. */ /*** readindex - read the index for a specific document * * The index entry for a specific document is read from the * appropriate extent into the passed data area. * We first get the appropriate extent into memory, and then * copy the index from the extent. * * Entry: fp = pointer to folder that document is in * docid = Id of document to read index for * ip = pointer to place to read index into * * Return: ERROR if unsuccessful * OK if successful */ static int readindex(fp, docid, ip) Folder *fp; Docid docid; Index *ip; { int fd; int extent, offset; long pos; /* quick check, does doc exist? */ if ( docid > fp->f_control.c_numdoc ) return ERROR; /* read down the extent chain forward pointers */ getextent(fp, (docid-1)/fp->f_control.c_nipe, 0); *ip = fp->f_extent->e_index[(docid - 1) % fp->f_control.c_nipe]; return OK; } /*** writeindex - write the index for a specific document * * The index entry for a specific document is written to the * appropriate extent from the passed data area. * We first get the appropriate extent into memory, and then * copy the index into the extent. The altered extent is then * marked dirty, so that it will eventually be flushed to disk. * * Entry: fp = pointer to folder that document is in * docid = Id of document to write index for * ip = pointer to place to read index from * * Return: ERROR if unsuccessful * OK if successful */ static int writeindex(fp, docid, ip) Folder *fp; Docid docid; Index *ip; { /* quick check, does doc exist? */ if ( docid > fp->f_control.c_numdoc ) return ERROR; getextent(fp, (docid-1)/fp->f_control.c_nipe, 1); fp->f_extent->e_index[(docid - 1) % fp->f_control.c_nipe] = *ip; fp->f_flags |= F_EDIRTY; return OK; } /*** getextent - get a specified extent into memory * * The specified extent is read into memory. * If the current extent is dirty, it must be flushed to disk. * Then, the correct extent is located and read into memory. * If the extent doesn't exist yet, but the flag is non-zero * then the extent is created. New extents are created only * if they would fall immediately after the last extent in the * existing chain of extents. * * Entry: fp = pointer to folder that extent is part of * number = extent to be read * flag = 0 means don't create a new extent * flag = 1 means do create a new extent if needed * * Return: OK if successful * FAILURE if unsuccessful */ static int getextent(fp, number, flag) Folder *fp; int number; int flag; { long pos; int n; if (fp->f_extent == NULL ) { fp->f_extent = (Extent *)(*dh_alloc)(fp->f_extsize); fp->f_extnum = -1; fp->f_extpos = 0; } if ( fp->f_extnum == number ) return OK; if ( fp->f_flags & F_EDIRTY ) flushextent(fp); /* read down the links the right number of times */ if ( (fp->f_extnum > number) || (fp->f_extnum == -1) ) { /* start at beginning */ pos = sizeof(Control); fp->f_extnum = -1; } else { /* start from where we are */ pos = fp->f_extent->e_link; } for (; fp->f_extnum != number; pos = fp->f_extent->e_link) { if ( pos == 0L ) break; _lseek(fp->f_fd, pos, 0); read(fp->f_fd, fp->f_extent, fp->f_extsize); fp->f_extnum += 1; fp->f_extpos = pos; } if ( fp->f_extnum == number ) return OK; if ( (number - fp->f_extnum) > 1 ) { fprintf(stderr, "extent chain too short.\n"); fprintf(stderr, "extnum %d number %d\n", fp->f_extnum, number); exit(1); } if ( (number - fp->f_extnum) == 1 && flag != 0 ) { return addextent(fp, number); } } /*** addextent - add a new extent to the chain * * A new, empty extent is added to the current chain. * First, we get the last extent into memory. Then, we seek to the * end of the, set the link of the last extent to that position, and * create the new extent in memory. We flush the new extent, and * return. * * Entry: fp = pointer to folder to create extent for * number = number of extent to create. * Return: OK if ok, ERROR otherwise */ static int addextent(fp, number) Folder *fp; int number; { long pos; if ( getextent(fp, number - 1, 0) != OK ) return ERROR; if ( fp->f_extent->e_link != 0 ) { fprintf(stderr, "Warning: trying to add extent that exists.\n"); } pos = _lseek(fp->f_fd, 0L, 2); /* end of file */ fp->f_extent->e_link = pos; flushextent(fp); fp->f_extpos = pos; fp->f_extnum = number; fp->f_flags |= F_EDIRTY; clearextent(fp); flushextent(fp); return OK; } static void flushextent(fp) Folder *fp; { _lseek(fp->f_fd, fp->f_extpos, 0); write(fp->f_fd, fp->f_extent, fp->f_extsize); fp->f_flags &= ~F_EDIRTY; } static void clearextent(fp) Folder *fp; { register int i; register Index *ip; for (ip = fp->f_extent->e_index; ip < &(fp->f_extent->e_index[fp->f_control.c_nipe]); ip += 1) { ip->i_hpos = ip->i_bpos = 0; ip->i_hlen = ip->i_blen = 0; ip->i_flags = 0; } fp->f_extent->e_link = 0; } /*** getdoc - get a document * * get a document handle for the document indicated by flags [and docid]. * flags can take one of the following values: * DOC_SPEC document specified by 'docid' in 'folder' * DOC_CREATE create new document in 'folder' * * Entry: fh = Fhandle to parent folder of document * func = desired action * docid = document id, if needed * Return: ERROR if failed, Dhandle to document if successful */ Dhandle getdoc(fh, func, docid) Fhandle fh; int func; Docid docid; { Folder *fp; Document *dp; FILE *file; struct extent *ep; int extent, offset; if ( (fp = fhtofp(fh)) == NULL ) return ERROR; /* find empty document slot */ for (dp = doctab; dp < &doctab[NDOCS]; dp += 1) { if (dp->d_flags == 0) break; } if (dp >= &doctab[NDOCS]) return ERROR; /* do function specific work */ switch (func) { case DOC_SPEC: /* find a specific document */ /* quick check, does doc exist? */ if ( docid > fp->f_control.c_numdoc ) return ERROR; if ( readindex(fp, docid, &dp->d_index) == ERROR ) return ERROR; /* make sure the document exists */ if ( (dp->d_index.i_flags & (I_EXISTS|I_DELETED)) != I_EXISTS ) return ERROR; /* ok, init the document struct and return */ dp->d_docid = docid; dp->d_flags = 0; break; case DOC_CREATE: /* make a new document */ /* get a new document id */ fp->f_control.c_numdoc += 1; dp->d_docid = fp->f_control.c_numdoc; fp->f_flags |= F_CDIRTY; /* set up an empty document */ dp->d_index.i_flags = I_EXISTS; dp->d_index.i_hlen = 0; dp->d_index.i_blen = 0; dp->d_index.i_hpos = 0; dp->d_index.i_bpos = 0; dp->d_flags |= D_IDIRTY; break; default: /* Bad function */ return ERROR; } /* do work common to all funcs */ dp->d_flags |= D_BUSY; dp->d_fp = fp; return (Dhandle)(dp - doctab); } /*** scanfolder - scan the existing documents * * return the document id for the specified document * flags can take one of the following values: * DOC_FIRST first document in 'folder' * DOC_NEXT next document in 'folder' * DOC_LAST upper bound on last docid * * Entry: fh = Fhandle to parent folder of document * func = desired action * docid = document id, if needed * Return: ERROR if failed, docid of document if successful */ Docid scanfolder(fh, func) Fhandle fh; int func; { Folder *fp; int i, flags; if ( (fp = fhtofp(fh)) == NULL ) return ERROR; /* do function specific work */ switch (func) { case DOC_FIRST: /* find the first document */ fp->f_sdocid = 1; /* fall thru */ case DOC_NEXT: /* find the next document */ while ( 1 ) { if ( fp->f_sdocid > fp->f_control.c_numdoc ) return ERROR; if ( getextent(fp, (fp->f_sdocid - 1)/fp->f_control.c_nipe,0) != OK) return ERROR; i = (int)(fp->f_sdocid - 1) % fp->f_control.c_nipe; flags = fp->f_extent->e_index[i].i_flags; if ( (flags & (I_EXISTS|I_DELETED)) == I_EXISTS ) return fp->f_sdocid; fp->f_sdocid += 1; } case DOC_LAST: /* upper bound on last doc */ return fp->f_control.c_numdoc; default: /* Bad function */ return ERROR; } } /*** putdoc - close a document * * Put_doc indicates that the document is no longer going to * be manipulated. Thus, we can free all the resources that * are allocated for that document. * * Entry: dh = Dhandle to document to close * Return: none unless there's an error, in which case ERROR */ putdoc(dh) Dhandle dh; { Document *dp; if ((dp = dhtodp(dh)) == NULL) return ERROR; if ( dp->d_flags & D_IDIRTY) writeindex(dp->d_fp, dp->d_docid, &(dp->d_index)); dp->d_fp->f_cnt -= 1; dp->d_flags = 0; return 0; } /*** deldoc - make a document go away * * Entry: fh = handle of folder to delete document from * docid = number of document to delete * * Return: ERROR if document never existed, or already deleted * * BUGBUG - deleted an open document will result in chaos */ deldoc(fh, docid) Fhandle fh; int docid; { Index index; Folder *fp; if ( (fp = fhtofp(fh)) == NULL ) return ERROR; readindex(fp, docid, &index); if ( index.i_flags & I_DELETED ) return ERROR; index.i_flags |= I_DELETED; writeindex(fp, docid, &index); return OK; } /*** gettext - write document to stream * * The entire document, header and all, is written to the * passed stream in presentation format. * * Entry: dh = handle of document to write to stream * stream = stream to write document onto * * Return: ERROR or none */ gettext(dh, file) Dhandle dh; int file; { Document *dp; char *cp; if ((dp = dhtodp(dh)) == NULL) return ERROR; /* write out header */ if ( (cp = gethdr(dh)) == NULL ) return ERROR; write(file, cp, strlen(cp)); (*dh_free) (cp); write(file, "\n", 1); /* write out body */ getbdy(dh, file); return 0; } /*** gethdr(dh) - get header block * * We return a pointer to on allocated piece of memory that * contains the header for the document. The memory is allocated * from the heap. Because the memory pointer is returned to the * user, we must assume that we transfer ownership to the caller. * Thus, he is responsible for freeing the memory when he no longer * needs it. * * Entry: dh = handle of relevant document * * Return: pointer to block of memory if successful * NULL if unsuccessful */ char *gethdr(dh) Dhandle dh; { Document *dp; char *cp; int size; if ( (dp = dhtodp(dh)) == NULL) return NULL; size = (int)dp->d_index.i_hlen; cp = (*dh_alloc)(size + 1); _lseek(dp->d_fp->f_fd, dp->d_index.i_hpos, 0); read(dp->d_fp->f_fd, cp, size); cp[size] = '\0'; return cp; } /*** getbdy - write the body of the document to the passed file handle * * The body of the relevant document is written onto the passed * file handle. * * Entry: dh = handle of document * file = file handle to write body onto */ getbdy(dh, file) Dhandle dh; int file; { Document *dp; long pos, length; int fd; char *bufp; int cnt; if ( (dp = dhtodp(dh)) == NULL ) return ERROR; if ( (bufp = (*dh_alloc)(BUFFERSIZE)) == NULL ) return ERROR; length = dp->d_index.i_blen; pos = dp->d_index.i_bpos; fd = dp->d_fp->f_fd; _lseek(fd, pos, 0L); while ( length > 0 ) { if ( length > BUFFERSIZE ) cnt = BUFFERSIZE; else cnt = (int)length; cnt = read(fd, bufp, cnt); write(file, bufp, cnt); length -= (long)cnt; } (*dh_free) (bufp); return OK; } /*** puttext - replace contents of a document * * The entire document is replaced by the document read from * the stream, assumed to be a document is presentation format * * Entry: dh = handle of document to replace * file = file handle to read new document content from * * Return: ERROR or none */ int puttext(dh, file) Dhandle dh; int file; { Document *dp; char *bufp; char *wp; char *hp; int cnt; int nllast = 0; int fd; if ( (dp = dhtodp(dh)) == NULL ) return ERROR; if ( (bufp = (*dh_alloc)(BUFFERSIZE+1)) == NULL ) return ERROR; fd = dp->d_fp->f_fd; dp->d_index.i_hpos = _lseek(fd, 0L, 2); dp->d_index.i_hlen = 0; /* read in header */ while ( 1 ) { if ( (cnt = read(file, bufp, BUFFERSIZE)) == 0 ) break; if ( nllast && *bufp == '\n' ) { wp = bufp + 1; break; } bufp[cnt] = '\0'; if ( (wp = nlnl(bufp)) != NULL ) { /* found end of header */ wp += 1; write(fd, bufp, wp - bufp); dp->d_index.i_hlen += (long)(wp - bufp); wp += 1; break; } if ( bufp[cnt - 1] == '\n' ) nllast = 1; write(fd, bufp, cnt); dp->d_index.i_hlen += (long)cnt; } /* * wp = NULL iff there is no body * wp = ptr to start of body in buffer iff there is a body * bufp = ptr to buffer * cnt = amount of data in buffer */ if ( wp == NULL ) { fprintf(stdout, "no body\n"); /* Should this be stderr??? */ dp->d_index.i_bpos = 0; dp->d_index.i_blen = 0; } else { dp->d_index.i_bpos = dp->d_index.i_hpos + dp->d_index.i_hlen; dp->d_index.i_blen = (long)(cnt - (wp - bufp)); write(fd, wp, cnt - (wp - bufp)); } /* * write remainder of file into body */ while ( (cnt = read(file, bufp, BUFFERSIZE)) > 0 ) { dp->d_index.i_blen += (long)cnt; write(fd, bufp, cnt); } dp->d_flags |= D_IDIRTY; (*dh_free) (bufp); } /*** putbody - replace the body of a document * * The body of the specified document is replaced with the * body read from the specified file descriptor. * * Entry: dh = handle of document to replace * ifd = file handle to read body from * * Return: ERROR if some difficulty arises */ putbody(dh, ifd) Dhandle dh; int ifd; { Document *dp; int fd; int cnt; char *bufp; if ( (dp = dhtodp(dh)) == NULL ) return ERROR; if ( (bufp = (*dh_alloc)(BUFFERSIZE)) == NULL ) return ERROR; fd = dp->d_fp->f_fd; dp->d_index.i_bpos = _lseek(fd, 0L, 2); dp->d_index.i_blen = 0; while ( (cnt = read(ifd, bufp, BUFFERSIZE)) > 0 ) { dp->d_index.i_blen += (long)cnt; write(fd, bufp, cnt); } dp->d_flags |= D_IDIRTY; (*dh_free) (bufp); return OK; } /*** puthdr - set new header block * * The document specified by dh gets a new header, * which is specified by the memory block that bp points to. * All storage, including the list nodes, must be allocated * from the heap, as it will be freed from the heap at * put_doc time. * * Note that we replace a header IN PLACE if we can. * * entry: * dh = handle of document which will recieve the new header * lp = pointer to new header field/value list * * return: ERROR if some problem */ puthdr(dh, bp) Dhandle dh; char *bp; { Document *dp; long l = (long) strlen (bp); if ( (dp = dhtodp(dh)) == NULL ) return ERROR; if (l <= dp->d_index.i_hlen) _lseek (dp->d_fp->f_fd, dp->d_index.i_hpos, 0); else dp->d_index.i_hpos = _lseek(dp->d_fp->f_fd, 0L, 2); dp->d_index.i_hlen = l; write(dp->d_fp->f_fd, bp, dp->d_index.i_hlen); dp->d_flags |= D_IDIRTY; return OK; } /*** getid - get the name of a document * * The name of the document is its 'document id', really * just a number. This number is computed based on information * in the doclist and returned. * * entry: * dh = handle of document in question * * Return: id of document */ Docid getid(dh) Dhandle dh; { Document *dp; if ((dp = dhtodp(dh)) == NULL) return ERROR; return dp->d_docid; } /*** dhtodp - convert document handle to pointer to document list structure * * Dhtodp checks to make sure that dh is a valid handle, and if it is, returns * the internal document structure that it corresponds to. If not, NULL will * be returned, indicating an error. * * entry: * dh - document handle to test and dereference * * return: * pointer to docstrc of interest, or null if error * * globals: doclist * */ static Document *dhtodp(dh) Dhandle dh; { /* handle ok? */ if ( ((doctab[dh].d_flags & D_BUSY) == 0) || (dh >= NDOCS) || (dh < 0)) return NULL; return &doctab[dh]; } static char *nlnl(cp) char *cp; { while ( (cp = strchr(cp, '\n')) != NULL ) { if ( cp[1] == '\n' ) return cp; cp += 1; } return NULL; } /*** getbodylen - return length of the body of a document * * The document specified by dh is examined and the length is returned. * * entry: * dh = handle of document to be sized. * * return: ERROR (cast to LONG) if bogus doc handle */ long getbodylen (dh) Dhandle dh; { Document *dp; if ( (dp = dhtodp(dh)) == NULL ) return (long) ERROR; return dp->d_index.i_blen; }