483 lines
8.4 KiB
C
483 lines
8.4 KiB
C
/*
|
||
* Copyright (c) 1999-2000 Sendmail, Inc. and its suppliers.
|
||
* All rights reserved.
|
||
*
|
||
* By using this file, you agree to the terms and conditions set
|
||
* forth in the LICENSE file which can be found at the top level of
|
||
* the sendmail distribution.
|
||
*
|
||
* Contributed by Exactis.com, Inc.
|
||
*
|
||
*/
|
||
|
||
#ifndef lint
|
||
static char id[] = "@(#)$Id: bf_portable.c,v 8.25.4.3 2000/06/29 21:21:58 gshapiro Exp $";
|
||
#endif /* ! lint */
|
||
|
||
#if SFIO
|
||
# include <sfio/stdio.h>
|
||
#endif /* SFIO */
|
||
|
||
#include <unistd.h>
|
||
#include <fcntl.h>
|
||
#include <sys/types.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <sys/uio.h>
|
||
#include <errno.h>
|
||
#if !SFIO
|
||
# include <stdio.h>
|
||
#endif /* !SFIO */
|
||
#ifndef BF_STANDALONE
|
||
# include "sendmail.h"
|
||
#endif /* ! BF_STANDALONE */
|
||
#include "bf_portable.h"
|
||
#include "bf.h"
|
||
|
||
/*
|
||
** BFOPEN -- create a new buffered file
|
||
**
|
||
** Parameters:
|
||
** filename -- the file's name
|
||
** fmode -- what mode the file should be created as
|
||
** bsize -- amount of buffer space to allocate (may be 0)
|
||
** flags -- if running under sendmail, passed directly to safeopen
|
||
**
|
||
** Returns:
|
||
** a FILE * which may then be used with stdio functions, or NULL
|
||
** on failure. FILE * is opened for writing (mode "w+").
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
**
|
||
** Sets errno:
|
||
** ENOMEM -- out of memory
|
||
** ENOENT -- illegal empty filename specified
|
||
** any value of errno specified by open()
|
||
** any value of errno specified by fdopen()
|
||
** any value of errno specified by funopen()
|
||
*/
|
||
|
||
#ifdef BF_STANDALONE
|
||
# define OPEN(fn, omode, cmode, sff) open(fn, omode, cmode)
|
||
#else /* BF_STANDALONE */
|
||
# define OPEN(fn, omode, cmode, sff) safeopen(fn, omode, cmode, sff)
|
||
#endif /* BF_STANDALONE */
|
||
|
||
/* List of currently-open buffered files */
|
||
struct bf *bflist = NULL;
|
||
|
||
FILE *
|
||
bfopen(filename, fmode, bsize, flags)
|
||
char *filename;
|
||
int fmode;
|
||
size_t bsize;
|
||
long flags;
|
||
{
|
||
struct bf *bfp;
|
||
FILE *retval;
|
||
int fd, l;
|
||
|
||
fd = OPEN(filename, O_RDWR | O_CREAT | O_TRUNC, fmode, flags);
|
||
if (fd == -1)
|
||
{
|
||
/* errno is set implicitly by open */
|
||
return NULL;
|
||
}
|
||
|
||
retval = fdopen(fd, "w+");
|
||
|
||
/* If failure, return immediately */
|
||
if (retval == NULL)
|
||
{
|
||
/* errno is set implicitly by fdopen */
|
||
return NULL;
|
||
}
|
||
|
||
/* Allocate memory */
|
||
bfp = (struct bf *)malloc(sizeof(struct bf));
|
||
if (bfp == NULL)
|
||
{
|
||
(void) fclose(retval);
|
||
|
||
/* don't care about errors */
|
||
(void) unlink(filename);
|
||
errno = ENOMEM;
|
||
return NULL;
|
||
}
|
||
if (tTd(58, 8))
|
||
dprintf("bfopen(%s): malloced %ld\n",
|
||
filename, (long) sizeof(struct bf));
|
||
|
||
l = strlen(filename) + 1;
|
||
bfp->bf_filename = (char *)malloc(l);
|
||
if (bfp->bf_filename == NULL)
|
||
{
|
||
free(bfp);
|
||
(void) fclose(retval);
|
||
|
||
/* don't care about errors */
|
||
(void) unlink(filename);
|
||
errno = ENOMEM;
|
||
return NULL;
|
||
}
|
||
(void) strlcpy(bfp->bf_filename, filename, l);
|
||
|
||
/* Fill in the other fields, then add it to the list */
|
||
bfp->bf_key = retval;
|
||
bfp->bf_committed = FALSE;
|
||
bfp->bf_refcount = 1;
|
||
|
||
bfinsert(bfp);
|
||
|
||
/* Whew. Nothing bad happened. We're okay. */
|
||
return retval;
|
||
}
|
||
/*
|
||
** BFDUP -- increase refcount on buffered file
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to "duplicate"
|
||
**
|
||
** Returns:
|
||
** fp with increased refcount
|
||
*/
|
||
|
||
FILE *
|
||
bfdup(fp)
|
||
FILE *fp;
|
||
{
|
||
struct bf *bfp;
|
||
|
||
/* Get associated bf structure */
|
||
bfp = bflookup(fp);
|
||
|
||
if (bfp == NULL)
|
||
return NULL;
|
||
|
||
/* Increase the refcount */
|
||
bfp->bf_refcount++;
|
||
|
||
return fp;
|
||
}
|
||
|
||
/*
|
||
** BFCOMMIT -- "commits" the buffered file
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to commit to disk
|
||
**
|
||
** Returns:
|
||
** 0 on success, -1 on error
|
||
**
|
||
** Side Effects:
|
||
** Forces the given FILE * to be written to disk if it is not
|
||
** already, and ensures that it will be kept after closing. If
|
||
** fp is not a buffered file, this is a no-op.
|
||
**
|
||
** Sets errno:
|
||
** any value of errno specified by open()
|
||
** any value of errno specified by write()
|
||
** any value of errno specified by lseek()
|
||
*/
|
||
|
||
int
|
||
bfcommit(fp)
|
||
FILE *fp;
|
||
{
|
||
struct bf *bfp;
|
||
|
||
/* Get associated bf structure */
|
||
bfp = bflookup(fp);
|
||
|
||
/* If called on a normal FILE *, noop */
|
||
if (bfp != NULL)
|
||
bfp->bf_committed = TRUE;
|
||
|
||
return 0;
|
||
}
|
||
|
||
/*
|
||
** BFREWIND -- rewinds the FILE *
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to rewind
|
||
**
|
||
** Returns:
|
||
** 0 on success, -1 on error
|
||
**
|
||
** Side Effects:
|
||
** rewinds the FILE * and puts it into read mode. Normally one
|
||
** would bfopen() a file, write to it, then bfrewind() and
|
||
** fread(). If fp is not a buffered file, this is equivalent to
|
||
** rewind().
|
||
**
|
||
** Sets errno:
|
||
** any value of errno specified by fseek()
|
||
*/
|
||
|
||
int
|
||
bfrewind(fp)
|
||
FILE *fp;
|
||
{
|
||
int err;
|
||
|
||
/* check to see if there is an error on the stream */
|
||
err = ferror(fp);
|
||
|
||
(void) fflush(fp);
|
||
|
||
/*
|
||
** Clear error if tried to fflush()
|
||
** a read-only file pointer and
|
||
** there wasn't a previous error.
|
||
*/
|
||
|
||
if (err == 0)
|
||
clearerr(fp);
|
||
|
||
/* errno is set implicitly by fseek() before return */
|
||
return fseek(fp, 0, SEEK_SET);
|
||
}
|
||
|
||
/*
|
||
** BFTRUNCATE -- rewinds and truncates the FILE *
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to truncate
|
||
**
|
||
** Returns:
|
||
** 0 on success, -1 on error
|
||
**
|
||
** Side Effects:
|
||
** rewinds the FILE *, truncates it to zero length, and puts it
|
||
** into write mode. If fp is not a buffered file, this is
|
||
** equivalent to a rewind() and then an ftruncate(fileno(fp), 0).
|
||
**
|
||
** Sets errno:
|
||
** any value of errno specified by fseek()
|
||
** any value of errno specified by ftruncate()
|
||
*/
|
||
|
||
int
|
||
bftruncate(fp)
|
||
FILE *fp;
|
||
{
|
||
int ret;
|
||
|
||
if (bfrewind(fp) == -1)
|
||
{
|
||
/* errno is set implicitly by bfrewind() */
|
||
return -1;
|
||
}
|
||
|
||
#if NOFTRUNCATE
|
||
/* XXX */
|
||
errno = EINVAL;
|
||
ret = -1;
|
||
#else /* NOFTRUNCATE */
|
||
/* errno is set implicitly by ftruncate() before return */
|
||
ret = ftruncate(fileno(fp), 0);
|
||
#endif /* NOFTRUNCATE */
|
||
return ret;
|
||
}
|
||
|
||
/*
|
||
** BFCLOSE -- close a buffered file
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to close
|
||
**
|
||
** Returns:
|
||
** 0 on success, EOF on failure
|
||
**
|
||
** Side Effects:
|
||
** Closes fp. If fp is a buffered file, unlink it if it has not
|
||
** already been committed. If fp is not a buffered file, this is
|
||
** equivalent to fclose().
|
||
**
|
||
** Sets errno:
|
||
** any value of errno specified by fclose()
|
||
*/
|
||
|
||
int
|
||
bfclose(fp)
|
||
FILE *fp;
|
||
{
|
||
int retval;
|
||
struct bf *bfp = NULL;
|
||
|
||
/* Get associated bf structure */
|
||
bfp = bflookup(fp);
|
||
|
||
/* Decrement and check refcount */
|
||
if (bfp != NULL && --bfp->bf_refcount > 0)
|
||
return 0;
|
||
|
||
/* If bf, get bf structure and remove from list */
|
||
if (bfp != NULL)
|
||
bfp = bfdelete(fp);
|
||
|
||
if (fclose(fp) == EOF)
|
||
{
|
||
if (tTd(58, 8))
|
||
dprintf("bfclose: fclose failed\n");
|
||
/* errno is set implicitly by fclose() */
|
||
return -1;
|
||
}
|
||
|
||
if (bfp == NULL)
|
||
return 0;
|
||
|
||
/* Success unless we determine otherwise in next block */
|
||
retval = 0;
|
||
|
||
if (bfp != NULL)
|
||
{
|
||
/* Might have to unlink; certainly will have to deallocate */
|
||
if (!bfp->bf_committed)
|
||
retval = unlink(bfp->bf_filename);
|
||
|
||
free(bfp->bf_filename);
|
||
free(bfp);
|
||
if (tTd(58, 8))
|
||
dprintf("bfclose: freed %ld\n",
|
||
(long) sizeof(struct bf));
|
||
}
|
||
else
|
||
{
|
||
if (tTd(58, 8))
|
||
dprintf("bfclose: bfp was NULL\n");
|
||
}
|
||
|
||
return retval;
|
||
}
|
||
|
||
/*
|
||
** BFTEST -- test if a FILE * is a buffered file
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to test
|
||
**
|
||
** Returns:
|
||
** TRUE if fp is a buffered file, FALSE otherwise.
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
**
|
||
** Sets errno:
|
||
** never.
|
||
*/
|
||
|
||
bool
|
||
bftest(fp)
|
||
FILE *fp;
|
||
{
|
||
return (bflookup(fp) != NULL);
|
||
}
|
||
|
||
/*
|
||
** BFINSERT -- insert item in linking list
|
||
**
|
||
** Parameters:
|
||
** datum -- item to insert
|
||
**
|
||
** Returns:
|
||
** none.
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
**
|
||
** Sets errno:
|
||
** never.
|
||
*/
|
||
|
||
void
|
||
bfinsert(datum)
|
||
struct bf *datum;
|
||
{
|
||
datum->bf_cdr = bflist;
|
||
bflist = datum;
|
||
}
|
||
|
||
/*
|
||
** BFLOOKUP -- lookup FILE * in list
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to lookup
|
||
**
|
||
** Returns:
|
||
** bf struct for the FILE *, NULL if not found
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
**
|
||
** Sets errno:
|
||
** never.
|
||
*/
|
||
|
||
struct bf *
|
||
bflookup(key)
|
||
FILE *key;
|
||
{
|
||
struct bf *t;
|
||
|
||
for (t = bflist; t != NULL; t = t->bf_cdr)
|
||
{
|
||
if (t->bf_key == key)
|
||
{
|
||
return t;
|
||
}
|
||
}
|
||
|
||
/* If we got this far, we didn't find it */
|
||
return NULL;
|
||
}
|
||
|
||
/*
|
||
** BFDELETE -- delete a FILE * in list
|
||
**
|
||
** Parameters:
|
||
** fp -- FILE * to delete
|
||
**
|
||
** Returns:
|
||
** bf struct for deleted FILE *, NULL if not found,
|
||
**
|
||
** Side Effects:
|
||
** none.
|
||
**
|
||
** Sets errno:
|
||
** never.
|
||
*/
|
||
|
||
struct bf *
|
||
bfdelete(key)
|
||
FILE *key;
|
||
{
|
||
struct bf *t, *u;
|
||
|
||
if (bflist == NULL)
|
||
return NULL;
|
||
|
||
/* if first element, special case */
|
||
if (bflist->bf_key == key)
|
||
{
|
||
u = bflist;
|
||
bflist = bflist->bf_cdr;
|
||
return u;
|
||
}
|
||
|
||
for (t = bflist; t->bf_cdr != NULL; t = t->bf_cdr)
|
||
{
|
||
if (t->bf_cdr->bf_key == key)
|
||
{
|
||
u = t->bf_cdr;
|
||
t->bf_cdr = u->bf_cdr;
|
||
return u;
|
||
}
|
||
}
|
||
|
||
/* If we got this far, we didn't find it */
|
||
return NULL;
|
||
}
|