/***************************************
Functions to handle files.
Part of the Routino routing software.
******************/ /******************
This file Copyright 2008-2015 Andrew M. Bishop
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
***************************************/
#if defined(_MSC_VER)
#include
#include
#define read(fd,address,length) _read(fd,address,(unsigned int)(length))
#define write(fd,address,length) _write(fd,address,(unsigned int)(length))
#define open _open
#define close _close
#define unlink _unlink
#define ssize_t SSIZE_T
#else
#include
#endif
#include
#include
#include
#include
#include
#include
#if defined(_MSC_VER) || defined(__MINGW32__)
#undef lseek
#undef stat
#undef fstat
#define lseek _lseeki64
#define stat _stati64
#define fstat _fstati64
#endif
#if defined(_MSC_VER) || defined(__MINGW32__)
#include "mman-win32.h"
#else
#include
#endif
#include
#include "files.h"
/*+ A structure to contain the list of memory mapped files. +*/
struct mmapinfo
{
const char *filename; /*+ The name of the file (the index of the list). +*/
int fd; /*+ The file descriptor used when it was opened. +*/
char *address; /*+ The address the file was mapped to. +*/
size_t length; /*+ The length of the file. +*/
};
/*+ The list of memory mapped files. +*/
static struct mmapinfo *mappedfiles;
/*+ The number of mapped files. +*/
static int nmappedfiles=0;
#define BUFFLEN 4096
/*+ A structure to contain the list of file buffers. +*/
struct filebuffer
{
char buffer[BUFFLEN]; /*+ The data buffer. +*/
size_t pointer; /*+ The read/write pointer for the file buffer. +*/
size_t length; /*+ The read pointer for the file buffer. +*/
int reading; /*+ A flag to indicate if the file is for reading. +*/
};
/*+ The list of file buffers. +*/
static struct filebuffer **filebuffers=NULL;
/*+ The number of allocated file buffer pointers. +*/
static int nfilebuffers=0;
#if defined(_MSC_VER) || defined(__MINGW32__)
/*+ A structure to contain the list of opened files to record which are to be deleted when closed. +*/
struct openedfile
{
const char *filename; /*+ The name of the file. +*/
int delete; /*+ Set to non-zero value if the file is to be deleted when closed. +*/
};
/*+ The list of opened files. +*/
static struct openedfile **openedfiles=NULL;
/*+ The number of allocated opened file buffer pointers. +*/
static int nopenedfiles=0;
#endif
/* Local functions */
static void CreateFileBuffer(int fd,int read_write);
#if defined(_MSC_VER) || defined(__MINGW32__)
static void CreateOpenedFile(int fd,const char *filename);
#endif
/*++++++++++++++++++++++++++++++++++++++
Return a filename composed of the dirname, prefix and name.
char *FileName Returns a pointer to memory allocated to the filename.
const char *dirname The directory name.
const char *prefix The file prefix.
const char *name The main part of the name.
++++++++++++++++++++++++++++++++++++++*/
char *FileName(const char *dirname,const char *prefix, const char *name)
{
char *filename=(char*)malloc((dirname?strlen(dirname):0)+1+(prefix?strlen(prefix):0)+1+strlen(name)+1);
sprintf(filename,"%s%s%s%s%s",dirname?dirname:"",dirname?"/":"",prefix?prefix:"",prefix?"-":"",name);
return(filename);
}
/*++++++++++++++++++++++++++++++++++++++
Open a file read-only and map it into memory.
void *MapFile Returns the address of the file or exits in case of an error.
const char *filename The name of the file to open.
++++++++++++++++++++++++++++++++++++++*/
void *MapFile(const char *filename)
{
int fd;
struct stat buf;
offset_t size;
void *address;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_RDONLY|O_BINARY|O_RANDOM);
#else
fd=open(filename,O_RDONLY);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(NULL);
#else
fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
/* Get its size */
if(stat(filename,&buf))
{
#ifdef LIBROUTINO
return(NULL);
#else
fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
size=buf.st_size;
/* Map the file */
address=mmap(NULL,size,PROT_READ,MAP_SHARED,fd,0);
if(address==MAP_FAILED)
{
close(fd);
#ifdef LIBROUTINO
return(NULL);
#else
fprintf(stderr,"Cannot mmap file '%s' for reading [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
#ifndef LIBROUTINO
log_mmap(size);
#endif
/* Store the information about the mapped file */
mappedfiles=(struct mmapinfo*)realloc((void*)mappedfiles,(nmappedfiles+1)*sizeof(struct mmapinfo));
mappedfiles[nmappedfiles].filename=filename;
mappedfiles[nmappedfiles].fd=fd;
mappedfiles[nmappedfiles].address=address;
mappedfiles[nmappedfiles].length=size;
nmappedfiles++;
return(address);
}
/*++++++++++++++++++++++++++++++++++++++
Open a file read-write and map it into memory.
void *MapFileWriteable Returns the address of the file or exits in case of an error.
const char *filename The name of the file to open.
++++++++++++++++++++++++++++++++++++++*/
void *MapFileWriteable(const char *filename)
{
int fd;
struct stat buf;
offset_t size;
void *address;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_RDWR|O_BINARY|O_RANDOM);
#else
fd=open(filename,O_RDWR);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(NULL);
#else
fprintf(stderr,"Cannot open file '%s' for reading and writing [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
/* Get its size */
if(stat(filename,&buf))
{
#ifdef LIBROUTINO
return(NULL);
#else
fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
size=buf.st_size;
/* Map the file */
address=mmap(NULL,size,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(address==MAP_FAILED)
{
close(fd);
#ifdef LIBROUTINO
return(NULL);
#else
fprintf(stderr,"Cannot mmap file '%s' for reading and writing [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
#ifndef LIBROUTINO
log_mmap(size);
#endif
/* Store the information about the mapped file */
mappedfiles=(struct mmapinfo*)realloc((void*)mappedfiles,(nmappedfiles+1)*sizeof(struct mmapinfo));
mappedfiles[nmappedfiles].filename=filename;
mappedfiles[nmappedfiles].fd=fd;
mappedfiles[nmappedfiles].address=address;
mappedfiles[nmappedfiles].length=size;
nmappedfiles++;
return(address);
}
/*++++++++++++++++++++++++++++++++++++++
Unmap a file and close it.
void *UnmapFile Returns NULL (for similarity to the MapFile function).
const void *address The address of the mapped file in memory.
++++++++++++++++++++++++++++++++++++++*/
void *UnmapFile(const void *address)
{
int i;
for(i=0;ii)
memmove(&mappedfiles[i],&mappedfiles[i+1],(nmappedfiles-i)*sizeof(struct mmapinfo));
return(NULL);
}
/*++++++++++++++++++++++++++++++++++++++
Open an existing file on disk for reading.
int SlimMapFile Returns the file descriptor if OK or exits in case of an error.
const char *filename The name of the file to open.
++++++++++++++++++++++++++++++++++++++*/
int SlimMapFile(const char *filename)
{
int fd;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_RDONLY|O_BINARY|O_RANDOM);
#else
fd=open(filename,O_RDONLY);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
CreateFileBuffer(fd,0);
return(fd);
}
/*++++++++++++++++++++++++++++++++++++++
Open an existing file on disk for reading or writing.
int SlimMapFileWriteable Returns the file descriptor if OK or exits in case of an error.
const char *filename The name of the file to open.
++++++++++++++++++++++++++++++++++++++*/
int SlimMapFileWriteable(const char *filename)
{
int fd;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_RDWR|O_BINARY|O_RANDOM);
#else
fd=open(filename,O_RDWR);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot open file '%s' for reading and writing [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
CreateFileBuffer(fd,0);
return(fd);
}
/*++++++++++++++++++++++++++++++++++++++
Close a file on disk.
int SlimUnmapFile returns -1 (for similarity to the UnmapFile function).
int fd The file descriptor to close.
++++++++++++++++++++++++++++++++++++++*/
int SlimUnmapFile(int fd)
{
close(fd);
return(-1);
}
/*++++++++++++++++++++++++++++++++++++++
Open a new file on disk for writing (with buffering).
int OpenFileBufferedNew Returns the file descriptor if OK or exits in case of an error.
const char *filename The name of the file to create.
++++++++++++++++++++++++++++++++++++++*/
int OpenFileBufferedNew(const char *filename)
{
int fd;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC|O_BINARY|O_RANDOM,S_IREAD|S_IWRITE);
#else
fd=open(filename,O_WRONLY|O_CREAT|O_TRUNC ,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot open file '%s' for writing [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
CreateFileBuffer(fd,-1);
#if defined(_MSC_VER) || defined(__MINGW32__)
CreateOpenedFile(fd,filename);
#endif
return(fd);
}
/*++++++++++++++++++++++++++++++++++++++
Open a new or existing file on disk for appending (with buffering).
int OpenFileBufferedAppend Returns the file descriptor if OK or exits in case of an error.
const char *filename The name of the file to create or open.
++++++++++++++++++++++++++++++++++++++*/
int OpenFileBufferedAppend(const char *filename)
{
int fd;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_WRONLY|O_CREAT|O_APPEND|O_BINARY|O_RANDOM,S_IREAD|S_IWRITE);
#else
fd=open(filename,O_WRONLY|O_CREAT|O_APPEND ,S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot open file '%s' for appending [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
CreateFileBuffer(fd,-1);
#if defined(_MSC_VER) || defined(__MINGW32__)
CreateOpenedFile(fd,filename);
#endif
return(fd);
}
/*++++++++++++++++++++++++++++++++++++++
Open an existing file on disk for reading (with buffering).
int ReOpenFileBuffered Returns the file descriptor if OK or exits in case of an error.
const char *filename The name of the file to open.
++++++++++++++++++++++++++++++++++++++*/
int ReOpenFileBuffered(const char *filename)
{
int fd;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_RDONLY|O_BINARY|O_RANDOM);
#else
fd=open(filename,O_RDONLY);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
CreateFileBuffer(fd,1);
#if defined(_MSC_VER) || defined(__MINGW32__)
CreateOpenedFile(fd,filename);
#endif
return(fd);
}
/*++++++++++++++++++++++++++++++++++++++
Open an existing file on disk for reading (with buffering), delete
it and open a new file on disk for writing (with buffering).
int ReplaceFileBuffered Returns the file descriptor of the new writable file.
const char *filename The name of the file to open, delete and replace.
int *oldfd Returns the file descriptor of the old, readable file.
++++++++++++++++++++++++++++++++++++++*/
int ReplaceFileBuffered(const char *filename,int *oldfd)
{
int newfd;
#if defined(_MSC_VER) || defined(__MINGW32__)
char *filename2;
filename2=strcpy(malloc(strlen(filename)+2),filename);
strcat(filename2,"2");
RenameFile(filename,filename2);
*oldfd=ReOpenFileBuffered(filename2);
DeleteFile(filename2);
#else
*oldfd=ReOpenFileBuffered(filename);
DeleteFile(filename);
#endif
newfd=OpenFileBufferedNew(filename);
return(newfd);
}
/*++++++++++++++++++++++++++++++++++++++
Write data to a file descriptor (via a buffer).
int WriteFileBuffered Returns 0 if OK or something else in case of an error.
int fd The file descriptor to write to.
const void *address The address of the data to be written.
size_t length The length of data to write.
++++++++++++++++++++++++++++++++++++++*/
int WriteFileBuffered(int fd,const void *address,size_t length)
{
#ifndef LIBROUTINO
logassert(fd!=-1,"File descriptor is in error - report a bug");
logassert(fdreading,"File descriptor was not opened for writing - report a bug");
#endif
/* Write the data */
if((filebuffers[fd]->pointer+length)>BUFFLEN)
{
if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
return(-1);
filebuffers[fd]->pointer=0;
}
if(length>=BUFFLEN)
{
if(write(fd,address,length)!=(ssize_t)length)
return(-1);
return(0);
}
memcpy(filebuffers[fd]->buffer+filebuffers[fd]->pointer,address,length);
filebuffers[fd]->pointer+=length;
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Read data from a file descriptor (via a buffer).
int ReadFileBuffered Returns 0 if OK or something else in case of an error.
int fd The file descriptor to read from.
void *address The address the data is to be read into.
size_t length The length of data to read.
++++++++++++++++++++++++++++++++++++++*/
int ReadFileBuffered(int fd,void *address,size_t length)
{
#ifndef LIBROUTINO
logassert(fd!=-1,"File descriptor is in error - report a bug");
logassert(fdreading,"File descriptor was not opened for reading - report a bug");
#endif
/* Read the data */
if((filebuffers[fd]->pointer+length)>filebuffers[fd]->length)
if(filebuffers[fd]->pointerlength)
{
memcpy(address,filebuffers[fd]->buffer+filebuffers[fd]->pointer,filebuffers[fd]->length-filebuffers[fd]->pointer);
address=(char*)address+filebuffers[fd]->length-filebuffers[fd]->pointer;
length-=filebuffers[fd]->length-filebuffers[fd]->pointer;
filebuffers[fd]->pointer=0;
filebuffers[fd]->length=0;
}
if(length>=BUFFLEN)
{
if(read(fd,address,length)!=(ssize_t)length)
return(-1);
return(0);
}
if(filebuffers[fd]->pointer==filebuffers[fd]->length)
{
ssize_t len=read(fd,filebuffers[fd]->buffer,BUFFLEN);
if(len<=0)
return(-1);
filebuffers[fd]->length=len;
filebuffers[fd]->pointer=0;
}
if(filebuffers[fd]->length==0)
return(-1);
memcpy(address,filebuffers[fd]->buffer+filebuffers[fd]->pointer,length);
filebuffers[fd]->pointer+=length;
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Seek to a position in a file descriptor that uses a buffer.
int SeekFileBuffered Returns 0 if OK or something else in case of an error.
int fd The file descriptor to seek within.
offset_t position The position to seek to.
++++++++++++++++++++++++++++++++++++++*/
int SeekFileBuffered(int fd,offset_t position)
{
#ifndef LIBROUTINO
logassert(fd!=-1,"File descriptor is in error - report a bug");
logassert(fdreading)
if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
return(-1);
filebuffers[fd]->pointer=0;
filebuffers[fd]->length=0;
if(lseek(fd,position,SEEK_SET)!=position)
return(-1);
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Skip forward by an offset in a file descriptor that uses a buffer.
int SkipFileBuffered Returns 0 if OK or something else in case of an error.
int fd The file descriptor to skip within.
offset_t skip The amount to skip forward.
++++++++++++++++++++++++++++++++++++++*/
int SkipFileBuffered(int fd,offset_t skip)
{
#ifndef LIBROUTINO
logassert(fd!=-1,"File descriptor is in error - report a bug");
logassert(fdreading,"File descriptor was not opened for reading - report a bug");
#endif
/* Skip the data - needs to be optimised */
if((filebuffers[fd]->pointer+skip)>filebuffers[fd]->length)
{
skip-=(offset_t)(filebuffers[fd]->length-filebuffers[fd]->pointer);
filebuffers[fd]->pointer=0;
filebuffers[fd]->length=0;
if(lseek(fd,skip,SEEK_CUR)==-1)
return(-1);
}
else
filebuffers[fd]->pointer+=skip;
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Get the size of a file.
offset_t SizeFile Returns the file size if OK or exits in case of an error.
const char *filename The name of the file to check.
++++++++++++++++++++++++++++++++++++++*/
offset_t SizeFile(const char *filename)
{
struct stat buf;
if(stat(filename,&buf))
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot stat file '%s' [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
return(buf.st_size);
}
/*++++++++++++++++++++++++++++++++++++++
Get the size of a file from a file descriptor.
offset_t SizeFileFD Returns the file size if OK or exits in case of an error.
int fd The file descriptor to check.
++++++++++++++++++++++++++++++++++++++*/
offset_t SizeFileFD(int fd)
{
struct stat buf;
if(fstat(fd,&buf))
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot stat file descriptor '%d' [%s].\n",fd,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
return(buf.st_size);
}
/*++++++++++++++++++++++++++++++++++++++
Check if a file exists.
int ExistsFile Returns 1 if the file exists and 0 if not.
const char *filename The name of the file to check.
++++++++++++++++++++++++++++++++++++++*/
int ExistsFile(const char *filename)
{
struct stat buf;
if(stat(filename,&buf))
return(0);
else
return(1);
}
/*++++++++++++++++++++++++++++++++++++++
Close a file on disk (and flush the buffer).
int CloseFileBuffered returns -1 (for similarity to the *OpenFileBuffered* functions).
int fd The file descriptor to close.
++++++++++++++++++++++++++++++++++++++*/
int CloseFileBuffered(int fd)
{
#ifndef LIBROUTINO
logassert(fdreading)
if(write(fd,filebuffers[fd]->buffer,filebuffers[fd]->pointer)!=(ssize_t)filebuffers[fd]->pointer)
return(-1);
close(fd);
free(filebuffers[fd]);
filebuffers[fd]=NULL;
#if defined(_MSC_VER) || defined(__MINGW32__)
#ifndef LIBROUTINO
logassert(fddelete)
unlink(openedfiles[fd]->filename);
free(openedfiles[fd]);
openedfiles[fd]=NULL;
#endif
return(-1);
}
/*++++++++++++++++++++++++++++++++++++++
Open an existing file on disk for reading (in a simple mode).
int OpenFile Returns the file descriptor if OK or exits in case of an error.
const char *filename The name of the file to open.
++++++++++++++++++++++++++++++++++++++*/
int OpenFile(const char *filename)
{
int fd;
/* Open the file */
#if defined(_MSC_VER) || defined(__MINGW32__)
fd=open(filename,O_RDONLY|O_BINARY|O_RANDOM);
#else
fd=open(filename,O_RDONLY);
#endif
if(fd<0)
{
#ifdef LIBROUTINO
return(-1);
#else
fprintf(stderr,"Cannot open file '%s' for reading [%s].\n",filename,strerror(errno));
exit(EXIT_FAILURE);
#endif
}
#if defined(_MSC_VER) || defined(__MINGW32__)
CreateOpenedFile(fd,filename);
#endif
return(fd);
}
/*++++++++++++++++++++++++++++++++++++++
Close a file on disk (that was opened in simple mode).
int fd The file descriptor to close.
++++++++++++++++++++++++++++++++++++++*/
void CloseFile(int fd)
{
close(fd);
#if defined(_MSC_VER) || defined(__MINGW32__)
#ifndef LIBROUTINO
logassert(fddelete)
unlink(openedfiles[fd]->filename);
free(openedfiles[fd]);
openedfiles[fd]=NULL;
#endif
}
/*++++++++++++++++++++++++++++++++++++++
Delete a file from disk.
int DeleteFile Returns 0 if OK.
const char *filename The name of the file to delete.
++++++++++++++++++++++++++++++++++++++*/
int DeleteFile(const char *filename)
{
#if defined(_MSC_VER) || defined(__MINGW32__)
int fd;
for(fd=0;fdfilename,filename))
{
openedfiles[fd]->delete=1;
return(0);
}
#endif
unlink(filename);
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Rename a file on disk.
int RenameFile Returns 0 if OK.
const char *oldfilename The old name of the file before renaming.
const char *newfilename The new name of the file after renaming.
++++++++++++++++++++++++++++++++++++++*/
int RenameFile(const char *oldfilename,const char *newfilename)
{
rename(oldfilename,newfilename);
return(0);
}
/*++++++++++++++++++++++++++++++++++++++
Create a file buffer.
int fd The file descriptor.
int read_write A flag set to 1 for reading, -1 for writing and 0 for unbuffered.
++++++++++++++++++++++++++++++++++++++*/
static void CreateFileBuffer(int fd,int read_write)
{
if(nfilebuffers<=fd)
{
int i;
filebuffers=(struct filebuffer**)realloc((void*)filebuffers,(fd+1)*sizeof(struct filebuffer*));
for(i=nfilebuffers;i<=fd;i++)
filebuffers[i]=NULL;
nfilebuffers=fd+1;
}
if(read_write)
{
filebuffers[fd]=(struct filebuffer*)calloc(sizeof(struct filebuffer),1);
filebuffers[fd]->reading=(read_write==1);
}
}
#if defined(_MSC_VER) || defined(__MINGW32__)
/*++++++++++++++++++++++++++++++++++++++
Create an opened file record.
int fd The file descriptor.
const char *filename The name of the file.
++++++++++++++++++++++++++++++++++++++*/
static void CreateOpenedFile(int fd,const char *filename)
{
if(nopenedfiles<=fd)
{
int i;
openedfiles=(struct openedfile**)realloc((void*)openedfiles,(fd+1)*sizeof(struct openedfile*));
for(i=nopenedfiles;i<=fd;i++)
openedfiles[i]=NULL;
nopenedfiles=fd+1;
}
openedfiles[fd]=(struct openedfile*)calloc(sizeof(struct openedfile),1);
openedfiles[fd]->filename=strcpy(malloc(strlen(filename)+1),filename);
openedfiles[fd]->delete=0;
}
#endif