239 lines
5.6 KiB
C
239 lines
5.6 KiB
C
/*
|
|
* IRC - Internet Relay Chat, ircd/fileio.c
|
|
* Copyright (C) 1998 Thomas Helvey <tomh@inxpress.net>
|
|
* Copyright (C) 1990 Jarkko Oikarinen and
|
|
* University of Oulu, Co Center
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 1, or (at your option)
|
|
* any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
/** @file
|
|
* @brief ANSI FILE* clone API implementation.
|
|
* @version $Id: fileio.c 1334 2005-03-20 16:06:30Z entrope $
|
|
*/
|
|
#include "config.h"
|
|
|
|
#include "fileio.h"
|
|
#include "ircd_alloc.h" /* MyMalloc, MyFree */
|
|
#include "ircd_log.h" /* assert */
|
|
|
|
/* #include <assert.h> -- Now using assert in ircd_log.h */ /* assert */
|
|
#include <fcntl.h> /* O_RDONLY, O_WRONLY, ... */
|
|
#include <stdio.h> /* BUFSIZ, EOF */
|
|
#include <sys/stat.h> /* struct stat */
|
|
#include <unistd.h> /* read, write, open, close */
|
|
#include <string.h>
|
|
|
|
#define FB_EOF 0x01 /**< File has reached EOF. */
|
|
#define FB_FAIL 0x02 /**< File operation failed. */
|
|
|
|
/** Tracks status and buffer for a file on disk. */
|
|
struct FileBuf {
|
|
int fd; /**< file descriptor */
|
|
char *endp; /**< one past the end */
|
|
char *ptr; /**< current read pos */
|
|
int flags; /**< file state */
|
|
char buf[BUFSIZ]; /**< buffer */
|
|
};
|
|
|
|
/** Open a new FBFILE.
|
|
* @param[in] filename Name of file to open.
|
|
* @param[in] mode fopen()-style mode string.
|
|
* @return Pointer to newly allocated FBFILE.
|
|
*/
|
|
FBFILE* fbopen(const char *filename, const char *mode)
|
|
{
|
|
int openmode = 0;
|
|
int pmode = 0;
|
|
FBFILE *fb = NULL;
|
|
int fd;
|
|
assert(filename);
|
|
assert(mode);
|
|
|
|
while (*mode) {
|
|
switch (*mode) {
|
|
case 'r':
|
|
openmode = O_RDONLY;
|
|
break;
|
|
case 'w':
|
|
openmode = O_WRONLY | O_CREAT | O_TRUNC;
|
|
pmode = S_IRUSR | S_IWUSR;
|
|
break;
|
|
case 'a':
|
|
openmode = O_WRONLY | O_CREAT | O_APPEND;
|
|
pmode = S_IRUSR | S_IWUSR;
|
|
break;
|
|
case '+':
|
|
openmode &= ~(O_RDONLY | O_WRONLY);
|
|
openmode |= O_RDWR;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
++mode;
|
|
}
|
|
/*
|
|
* stop NFS hangs...most systems should be able to open a file in
|
|
* 3 seconds. -avalon (courtesy of wumpus)
|
|
*/
|
|
alarm(3);
|
|
if ((fd = open(filename, openmode, pmode)) == -1) {
|
|
alarm(0);
|
|
return fb;
|
|
}
|
|
alarm(0);
|
|
|
|
if (NULL == (fb = fdbopen(fd, NULL)))
|
|
close(fd);
|
|
return fb;
|
|
}
|
|
|
|
/** Open a FBFILE from a file descriptor.
|
|
* @param[in] fd File descriptor to use.
|
|
* @param[in] mode fopen()-style mode string (ignored).
|
|
*/
|
|
FBFILE* fdbopen(int fd, const char *mode)
|
|
{
|
|
/*
|
|
* ignore mode, if file descriptor hasn't been opened with the
|
|
* correct mode, the first use will fail
|
|
*/
|
|
FBFILE *fb = (FBFILE *) MyMalloc(sizeof(FBFILE));
|
|
assert(0 != fb);
|
|
fb->ptr = fb->endp = fb->buf;
|
|
fb->fd = fd;
|
|
fb->flags = 0;
|
|
|
|
return fb;
|
|
}
|
|
|
|
/** Close a FBFILE.
|
|
* @param[in] fb File buffer to close.
|
|
*/
|
|
void fbclose(FBFILE* fb)
|
|
{
|
|
assert(fb);
|
|
close(fb->fd);
|
|
MyFree(fb);
|
|
}
|
|
|
|
/** Attempt to fill a file's buffer.
|
|
* @param[in] fb File to operate on.
|
|
* @return Number of bytes read into buffer, or a negative number on error.
|
|
*/
|
|
static int fbfill(FBFILE * fb)
|
|
{
|
|
int n;
|
|
assert(fb);
|
|
if (fb->flags)
|
|
return -1;
|
|
n = read(fb->fd, fb->buf, BUFSIZ);
|
|
if (0 < n)
|
|
{
|
|
fb->ptr = fb->buf;
|
|
fb->endp = fb->buf + n;
|
|
}
|
|
else if (n < 0)
|
|
fb->flags |= FB_FAIL;
|
|
else
|
|
fb->flags |= FB_EOF;
|
|
return n;
|
|
}
|
|
|
|
/** Get a single character from a file.
|
|
* @param[in] fb File to fetch from.
|
|
* @return Character value read, or EOF on error or end-of-file.
|
|
*/
|
|
int fbgetc(FBFILE * fb)
|
|
{
|
|
assert(fb);
|
|
if (fb->ptr < fb->endp || fbfill(fb) > 0)
|
|
return *fb->ptr++;
|
|
return EOF;
|
|
}
|
|
|
|
/** Get a line of input from a file.
|
|
* @param[out] buf Output buffer to read to.
|
|
* @param[in] len Maximum number of bytes to write to buffer
|
|
* (including terminating NUL).
|
|
* @param[in] fb File to read from.
|
|
*/
|
|
char *fbgets(char *buf, size_t len, FBFILE * fb)
|
|
{
|
|
char *p = buf;
|
|
assert(buf);
|
|
assert(fb);
|
|
assert(0 < len);
|
|
|
|
if (fb->ptr == fb->endp && fbfill(fb) < 1)
|
|
return 0;
|
|
--len;
|
|
while (len--) {
|
|
*p = *fb->ptr++;
|
|
if ('\n' == *p)
|
|
{
|
|
++p;
|
|
break;
|
|
}
|
|
/*
|
|
* deal with CR's
|
|
*/
|
|
else if ('\r' == *p) {
|
|
if (fb->ptr < fb->endp || fbfill(fb) > 0) {
|
|
if ('\n' == *fb->ptr)
|
|
++fb->ptr;
|
|
}
|
|
*p++ = '\n';
|
|
break;
|
|
}
|
|
++p;
|
|
if (fb->ptr == fb->endp && fbfill(fb) < 1)
|
|
break;
|
|
}
|
|
*p = '\0';
|
|
return buf;
|
|
}
|
|
|
|
/** Write a string to a file.
|
|
* @param[in] str String to write to file.
|
|
* @param[in] fb File to write to.
|
|
* @return Number of bytes written, or -1 on error.
|
|
*/
|
|
int fbputs(const char *str, FBFILE * fb)
|
|
{
|
|
int n = -1;
|
|
assert(str);
|
|
assert(fb);
|
|
|
|
if (0 == fb->flags) {
|
|
n = write(fb->fd, str, strlen(str));
|
|
if (-1 == n)
|
|
fb->flags |= FB_FAIL;
|
|
}
|
|
return n;
|
|
}
|
|
|
|
/** Get file status.
|
|
* @param[out] sb Receives file status.
|
|
* @param[in] fb File to get status for.
|
|
* @return Zero on success, -1 on error.
|
|
*/
|
|
int fbstat(struct stat *sb, FBFILE * fb)
|
|
{
|
|
assert(sb);
|
|
assert(fb);
|
|
return fstat(fb->fd, sb);
|
|
}
|
|
|