mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1162 lines
26 KiB
1162 lines
26 KiB
/*** doclib.c - standard DH library routines
|
|
* cl /c /Zep /D LINT_ARGS doclib.c
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <fcntl.h>
|
|
#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;
|
|
}
|