/* Implement Input/Output runtime actions for CHILL. Copyright (C) 1992,1993 Free Software Foundation, Inc. Author: Wilfried Moser, et al This file is part of GNU CC. GNU CC 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 2, or (at your option) any later version. GNU CC 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 GNU CC; see the file COPYING. If not, write to the Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* As a special exception, if you link this library with other files, some of which are compiled with GCC, to produce an executable, this library does not by itself cause the resulting executable to be covered by the GNU General Public License. This exception does not however invalidate any other reasons why the executable file might be covered by the GNU General Public License. */ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <limits.h> #include <errno.h> #include <string.h> #include <stdlib.h> #include "fileio.h" #ifndef PATH_MAX # ifdef _POSIX_PATH_MAX # define PATH_MAX _POSIX_PATH_MAX # else # ifdef MAXPATHLEN # define PATH_MAX MAXPATHLEN # else # define PATH_MAX 1024 # endif # endif #endif static void GetSetAttributes( Association_Mode* the_assoc ) { struct stat statbuf; int retco; if( (retco = stat( the_assoc->pathname, &statbuf )) ) return; if( S_ISREG(statbuf.st_mode) ) { SET_FLAG( the_assoc, IO_EXISTING ); if( !TEST_FLAG( the_assoc, IO_VARIABLE ) ) SET_FLAG( the_assoc, IO_INDEXABLE ); } else if( S_ISCHR(statbuf.st_mode) || S_ISFIFO(statbuf.st_mode) ) { SET_FLAG( the_assoc, IO_EXISTING ); CLR_FLAG( the_assoc, IO_INDEXABLE ); } SET_FLAG( the_assoc, IO_SEQUENCIBLE ); /* FIXME: File size and computation of number of records for outoffile ? */ if( !access( the_assoc->pathname, R_OK ) ) SET_FLAG( the_assoc, IO_READABLE ); if( !access( the_assoc->pathname, W_OK ) ) SET_FLAG( the_assoc, IO_WRITEABLE ); } static void makeName( Association_Mode* the_assoc, char* the_path, int the_path_len, char* file, int line) { int namlen; if( ! the_assoc->pathname && ! (the_assoc->pathname = (char*)malloc( PATH_MAX )) ) CHILLEXCEPTION( file, line, SPACEFAIL, PATHNAME_ALLOC ); if( the_path[0] != DIRSEP ) { if( !getcwd( the_assoc->pathname, PATH_MAX ) ) { the_assoc->syserrno = errno; CHILLEXCEPTION( file, line, ASSOCIATEFAIL, GETCWD_FAILS ); } namlen = strlen( the_assoc->pathname ); the_assoc->pathname[namlen++] = DIRSEP; } else namlen = 0; strncpy( the_assoc->pathname + namlen, the_path, the_path_len ); the_assoc->pathname[namlen+the_path_len] = '\0'; } /* * ASSOCIATE */ /* Caution: returns an Association mode location (!) */ Association_Mode* __associate( Association_Mode* the_assoc, char* the_path, int the_path_len, char* the_mode, int the_mode_len, char* file, int line ) { if( !the_assoc ) CHILLEXCEPTION( file, line, EMPTY, NULL_ASSOCIATION ); if( TEST_FLAG(the_assoc, IO_ISASSOCIATED) ) CHILLEXCEPTION( file, line, ASSOCIATEFAIL, IS_ASSOCIATED ); /* clear all flags */ the_assoc->flags = 0; if( ! the_path_len ) CHILLEXCEPTION( file, line, ASSOCIATEFAIL, NO_PATH_NAME ); makeName( the_assoc, the_path, the_path_len, file, line ); GetSetAttributes( the_assoc ); CLR_FLAG( the_assoc, IO_VARIABLE ); if ( the_mode ) { if( !strncmp( the_mode, "VARIABLE", 8 ) ) { SET_FLAG( the_assoc, IO_VARIABLE ); CLR_FLAG( the_assoc, IO_INDEXABLE ); } else if( strlen( the_mode ) ) CHILLEXCEPTION( file, line, ASSOCIATEFAIL, INVALID_ASSOCIATION_MODE ); } SET_FLAG( the_assoc, IO_ISASSOCIATED ); return the_assoc; } /* * DISSOCIATE */ void __dissociate( Association_Mode* the_assoc, char* file, int line ) { if( !the_assoc ) CHILLEXCEPTION( file, line, EMPTY, NULL_ASSOCIATION ); if( !TEST_FLAG( the_assoc, IO_ISASSOCIATED ) ) CHILLEXCEPTION( file, line, NOTASSOCIATED, IS_NOT_ASSOCIATED ); if( the_assoc->access ) __disconnect( the_assoc->access, file, line ); the_assoc->access = NULL; CLR_FLAG( the_assoc, IO_ISASSOCIATED ); /* free allocated memory */ if (the_assoc->pathname) { free (the_assoc->pathname); the_assoc->pathname = 0; } if (the_assoc->bufptr) { free (the_assoc->bufptr); the_assoc->bufptr = 0; } } /* * CREATE */ void __create( Association_Mode* the_assoc, char* file, int line ) { if( !the_assoc ) CHILLEXCEPTION( file, line, EMPTY, NULL_ASSOCIATION ); if( !TEST_FLAG( the_assoc, IO_ISASSOCIATED ) ) CHILLEXCEPTION( file, line, NOTASSOCIATED, IS_NOT_ASSOCIATED ); if( TEST_FLAG( the_assoc, IO_EXISTING ) ) CHILLEXCEPTION( file, line, CREATEFAIL, FILE_EXISTING ); if( (the_assoc->handle = open( the_assoc->pathname, O_CREAT+O_TRUNC+O_WRONLY, 0666 )) == -1 ) CHILLEXCEPTION( file, line, CREATEFAIL, CREATE_FAILS ); the_assoc->usage = ReadWrite; GetSetAttributes( the_assoc ); close( the_assoc->handle ); } /* * MODIFY */ void __modify( Association_Mode* the_assoc, char* the_path, int the_path_len, char* the_mode, int the_mode_len, char* file, int line ) { if( !the_assoc ) CHILLEXCEPTION( file, line, EMPTY, NULL_ASSOCIATION ); if( !TEST_FLAG( the_assoc, IO_ISASSOCIATED ) ) CHILLEXCEPTION( file, line, NOTASSOCIATED, IS_NOT_ASSOCIATED ); if( the_path_len ) { char* oldname; if( ! (oldname = (char*)malloc( PATH_MAX )) ) CHILLEXCEPTION( file, line, SPACEFAIL, PATHNAME_ALLOC ); strcpy( oldname, the_assoc->pathname ); makeName( the_assoc, the_path, the_path_len, file, line ); if( rename( oldname, the_assoc->pathname ) ) { free( oldname ); CHILLEXCEPTION( file, line, MODIFYFAIL, RENAME_FAILS ); } free( oldname ); } else { /* FIXME: other options? */ } } static /*** char* DirMode[] = { "rb", "r+b", "r+b" }; ***/ int DirMode[] = { O_RDONLY, O_RDWR, O_RDWR }; static /*** char* SeqMode [] = { "rb", "r+b", "r+b" }; ***/ int SeqMode[] = { O_RDONLY, O_RDWR, O_RDWR }; /* * CONNECT */ void __connect( void* the_transfer, Association_Mode* the_assoc, Usage_Mode the_usage, Where_Mode the_where, Boolean with_index, signed long the_index, char* file, int line ) { Access_Mode* the_access; off_t filepos; off_t savepos; char dummy; unsigned long nbytes; int oflag; if( !the_transfer ) CHILLEXCEPTION( file, line, EMPTY, NULL_ACCESS ); if( !the_assoc ) CHILLEXCEPTION( file, line, EMPTY, NULL_ASSOCIATION ); if( TEST_FLAG((Text_Mode*)the_transfer, IO_TEXTLOCATION )) { if( ! ((Text_Mode*)the_transfer)->access_sub ) CHILLEXCEPTION( file, line, EMPTY, NO_ACCESS_SUBLOCATION ); the_access = ((Text_Mode*)the_transfer)->access_sub; SET_FLAG( the_access, IO_TEXTIO ); } else { the_access = (Access_Mode*)the_transfer; CLR_FLAG( the_access, IO_TEXTIO ); } /* FIXME: This should be an (implementation-dependent) static check if( with_index && the_access->rectype > Fixed ) CHILLEXCEPTION( file, line, CONNECTFAIL, IMPL_RESTRICTION ); */ if( ! TEST_FLAG(the_assoc, IO_ISASSOCIATED) ) CHILLEXCEPTION( file, line, NOTASSOCIATED, IS_NOT_ASSOCIATED ); if( ! TEST_FLAG( the_assoc, IO_EXISTING ) ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_EXISTING ); if( ! TEST_FLAG( the_assoc, IO_READABLE ) && ( the_usage = ReadOnly || the_usage == ReadWrite ) ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_READABLE ); if( ! TEST_FLAG( the_assoc, IO_WRITEABLE ) && ( the_usage = WriteOnly || the_usage == ReadWrite ) ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_WRITEABLE ); if( ! TEST_FLAG( the_assoc, IO_INDEXABLE ) && TEST_FLAG( the_access, IO_INDEXED ) ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_INDEXABLE ); if( ! TEST_FLAG( the_assoc, IO_SEQUENCIBLE ) && ! TEST_FLAG( the_access, IO_INDEXED ) ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_SEQUENCIBLE ); if( the_where == Same && the_assoc->access == NULL ) CHILLEXCEPTION( file, line, CONNECTFAIL, NO_CURRENT_POS ); /* This dynamic condition is not checked for text connections. */ if( ! TEST_FLAG( the_access, IO_TEXTIO ) ) if( ! TEST_FLAG( the_assoc, IO_VARIABLE ) && the_access->rectype > Fixed && ( the_usage == WriteOnly || the_usage == ReadWrite ) ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_VARIABLE ); if( TEST_FLAG( the_assoc, IO_VARIABLE ) && the_access->rectype == Fixed && ( the_usage == ReadOnly || the_usage == ReadWrite ) ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_FIXED ); if( ! TEST_FLAG( the_access, IO_INDEXED ) && the_usage == ReadWrite ) CHILLEXCEPTION( file, line, CONNECTFAIL, NOT_INDEXED ); /* Access location may be connected to a different association. */ if( the_access->association && the_access->association != the_assoc ) __disconnect( the_access, file, line ); /* Is the association location already connected? */ if( the_assoc->access ) { /* save position just in case we need it for the_where == Same */ if( (savepos = lseek( the_assoc->handle, 0L, SEEK_CUR )) == -1L ) CHILLEXCEPTION( file, line, CONNECTFAIL, LSEEK_FAILS ); /* text: read correction, flush buffer */ if( the_assoc->bufptr ){ savepos -= the_assoc->bufptr->len - the_assoc->bufptr->cur; the_assoc->bufptr->len = the_assoc->bufptr->cur = 0; } /* implicit disconnect */ __disconnect( the_assoc->access, file, line ); } the_assoc->usage = the_usage; CLR_FLAG( the_access, IO_OUTOFFILE ); if( TEST_FLAG( the_access, IO_INDEXED ) ) { if( (the_assoc->handle = open( the_assoc->pathname, DirMode[the_usage] )) == -1 ) CHILLEXCEPTION( file, line, CONNECTFAIL, OPEN_FAILS ); /* Set base index. */ switch( the_where ) { case First: filepos = 0; break; case Same: filepos = savepos; break; case Last: if( lseek( the_assoc->handle, 0L, SEEK_END ) == -1L ) CHILLEXCEPTION( file, line, CONNECTFAIL, LSEEK_FAILS ); filepos = lseek( the_assoc->handle, 0L, SEEK_CUR ); break; } /* Set current index */ if( with_index ) { if( the_index < the_access->lowindex || the_access->highindex < the_index ) CHILLEXCEPTION( file, line, RANGEFAIL, BAD_INDEX ); filepos += (the_index - the_access->lowindex) * the_access->reclength; } if( lseek( the_assoc->handle, filepos, SEEK_SET ) == -1L ) CHILLEXCEPTION( file, line, CONNECTFAIL, LSEEK_FAILS ); the_access->base = filepos; } else { /* for association to text for reading: allocate buffer */ if( TEST_FLAG((Text_Mode*)the_transfer, IO_TEXTLOCATION ) && the_usage == ReadOnly && !the_assoc->bufptr ) { if( ! (the_assoc->bufptr = (readbuf_t*)malloc( sizeof(readbuf_t) )) ) CHILLEXCEPTION( file, line, CONNECTFAIL, BUFFER_ALLOC ); memset (the_assoc->bufptr, 0, sizeof (readbuf_t)); } if( (the_assoc->handle = open( the_assoc->pathname, SeqMode[the_usage] )) == -1 ) CHILLEXCEPTION( file, line, CONNECTFAIL, OPEN_FAILS ); /* Set base index. */ switch( the_where ) { case First: filepos = 0; break; case Same: filepos = savepos; break; case Last: if( lseek( the_assoc->handle, 0L, SEEK_END ) == -1L ) CHILLEXCEPTION( file, line, CONNECTFAIL, LSEEK_FAILS ); filepos = lseek( the_assoc->handle, 0L, SEEK_CUR ); break; } /* file truncation for sequential, Write Only */ /***************************** FIXME: cannot truncate at Same if( the_usage == WriteOnly ) { if( fseek( the_assoc->file_ptr, filepos, SEEK_SET ) == -1L ) CHILLEXCEPTION( file, line, CONNECTFAIL, FSEEK_FAILS ); fclose( the_assoc->file_ptr ); if( !(the_assoc->file_ptr = fopen( the_assoc->pathname, "ab" )) ) CHILLEXCEPTION( file, line, CONNECTFAIL, OPEN_FAILS ); } else ***************************/ if( (filepos = lseek( the_assoc->handle, filepos, SEEK_SET )) == -1L ) CHILLEXCEPTION( file, line, CONNECTFAIL, LSEEK_FAILS ); } the_access->association = the_assoc; the_assoc->access = the_access; /* for text: set carriage control default */ if( TEST_FLAG((Text_Mode*)the_transfer, IO_TEXTLOCATION ) ){ the_assoc->ctl_pre = '\0'; the_assoc->ctl_post = '\n'; } } void __disconnect( void* the_transfer, char* file, int line ) { Access_Mode* the_access; if( !the_transfer ) CHILLEXCEPTION( file, line, EMPTY, NULL_ACCESS ); if( TEST_FLAG((Text_Mode*)the_transfer, IO_TEXTLOCATION )) { the_access = ((Text_Mode*)the_transfer)->access_sub; CLR_FLAG( the_access, IO_TEXTIO ); } else the_access = (Access_Mode*)the_transfer; if( !the_access->association ) CHILLEXCEPTION( file, line, NOTCONNECTED, IS_NOT_CONNECTED ); close( the_access->association->handle ); /* FIXME: check result */ if( the_access->store_loc ) free( the_access->store_loc ); the_access->store_loc = NULL; the_access->association->access = NULL; the_access->association = NULL; }