/* @(#)osd.c 17.1.1.1 (ESO-DMD) 01/25/02 17:35:24 */ /*=========================================================================== Copyright (C) 1995 European Southern Observatory (ESO) 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 2 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 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 Massachusetts Ave, Cambridge, MA 02139, USA. Correspondence concerning ESO-MIDAS should be addressed as follows: Internet e-mail: midas@eso.org Postal address: European Southern Observatory Data Management Division Karl-Schwarzschild-Strasse 2 D 85748 Garching bei Muenchen GERMANY ===========================================================================*/ /*+++++++++++ .TYPE Module .NAME osd.c .LANGUAGE C .AUTHOR IPG-ESO Garching .CATEGORY Host operating system interfaces. i/o on disk files. .ENVIRONMENT VAX / VMS .COMMENTS Handling of disk io's. All files are referenced by the physical name in the open function. A file identifier (number) is returned by open functions to be used in later i/o operations. \begin{TeX} This module includes {\em osd} routines which perform access to disk files. Possible seeking at byte address. The buffering is done within this routine, and a large blocking {\em LARGE\_BUFFER} option is also available. Note that only {\em STMLF} format files are unix-compatible! For other format, use preferably the {\em osa} routines... The routines return always a non-negative integer number on successful return. Otherwise, a value of -1 is set to indicate an error condition or the End of File; the variable ``oserror'' contains the numeric error code ($0$ for EOF), which can be converted to a character pointer via {\em osmsg routine}. \end{TeX} .VERSION 0.0 25-Aug-1986 Definition J. D. Ponz .VERSION 2.0 13-Oct-1988 VMS version (F. Ochsenbein) .VERSION 2.1 10-Jan-1989 Coorected bug in osdopen (propagate NOATTR option) .VERSION 2.2 24-Aug-1990 Corrected bug in osdwrite (very large blocks). Allow non-sequential organisation. .VERSION 2.3 19-Nov-1990 Force multiple of 8 bytes for buffers. .VERSION 2.4 23-Apr-1991 Be sure to free buffer memory at osdclose... ------------------------------------------------------------*/ #define IO_MAX (90*512) /* Set to 16 FITS blocks */ #define DEBUG 0 #include #include #include #include #include /* System */ #include /* System */ # define fab$l_fna name # define fab$b_fns lname # define rab$l_ctx buffer /* Used as address of Buffer */ # define fab$l_ctx file_no #include /* System */ #include /* System */ #include /* System */ MID_EXTERN int vmserror; /* VMS-specific error code */ MID_EXTERN int oserror; MID_EXTERN char *oserrmsg; #define FINISH goto FIN static struct FAB *fap; static struct XABFHC *fhp; static struct RAB *fp; static int TheFile = 0; static unsigned short chan; /* Set by getfile, tells QIO's to be used */ static short move_len; struct RAB *vmsrab(); long vmsize(); struct Buffer { int size, offset, bytes, block; unsigned long file_size; unsigned char buffer_modified; unsigned char flush; unsigned char spare[2]; char *text; }; static struct Buffer *b; static unsigned long full_size; static char gbuf = 1; /* In getfile(), ask to allocate buffer */ /*==========================================================================*/ static int vmsread(pbuf, nochar) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Interface to SYS$READ .RETURNS Number of characters actually read / -1 if an error is encountered / 0 if the end of file is reached. In case of error, oserror is set to the relevant error number. .REMARKS Works only on Disk Files ! The virtual sector to read is b->block. Also, nochar is assumed to be POSITIVE ... ------------------------------------------------------------*/ char *pbuf; /* OUT : pointer to i/o buffer */ int nochar; /* IN : size of i/o buffer */ { register int len; if (nochar <= 0) { oserror = EINVAL; FINISH; } fp->rab$l_bkt = b->block + 1; fp->rab$l_ubf = pbuf; fp->rab$w_usz = nochar; vmserror = SYS$READ(fp); len = fp->rab$w_rsz; /* Length of read block */ switch(vmserror) { case RMS$_NORMAL: /* Compute Next Block */ break; case RMS$_EOF: len = 0; break; default: oserror = EIO; break; } FIN: #if DEBUG printf("vmsread file %d rab$l_bkt #%d: bytes=%d on %d\n", TheFile, fp->rab$l_bkt-1, len, nochar); #endif return(oserror ? -1 : len); } /*==========================================================================*/ static int vmswrite(pbuf, nochar) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Interface to SYS$PUT. .RETURNS Number of characters actually written / -1 if an error is encountered. In case of error, oserror is set to the relevant error number. .REMARKS Works only on Disk Files ! The virtual sector to read is b->block. Also, nochar is assumed to be POSITIVE ... ------------------------------------------------------------*/ char *pbuf; /* IN : pointer to i/o buffer */ int nochar; /* IN : size of i/o buffer */ { register int len; fp->rab$l_bkt = b->block + 1; fp->rab$l_rbf = pbuf; fp->rab$w_rsz = nochar; vmserror = SYS$WRITE(fp); if (!(vmserror&1)) oserror = EIO; else len = nochar; FIN: #if DEBUG printf("vmswritten file %d: bytes=%d on %d - Block is now %d\n", TheFile, len, nochar, fp->rab$l_bkt); #endif return(oserror ? -1 : len); } /**************************** * General Utility Routines * ****************************/ /*==========================================================================*/ static int osbuffer(size) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Open a buffer for file fid. .RETURNS size of buffer (success) / -1 (failure) ------------------------------------------------------------*/ int size; /* IN : size of buffer (bytes) */ { b = (struct Buffer *)osmmget(size + sizeof(struct Buffer)); if (oserror) FINISH; fp->buffer = (unsigned int) b; /* casted by tm 05.09.89 */ b->text = (char *) ( b + 1); b->size = size; b->offset = 0; b->bytes = 0; b->block = 0; /* Nothing was read */ b->buffer_modified = 0; b->flush = 0; b->file_size = full_size; FIN: return (oserror ? -1 : size); } /*==========================================================================*/ static int getfile(fid) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Retrieves the file pointer. .RETURNS File number (fid); the static fp (file pointer) is also set up. ------------------------------------------------------------*/ int fid; /* IN : file identifier */ { if (!(fp = vmsrab(fid))) FINISH; b = NULL_PTR(struct Buffer); fap = fp->rab$l_fab; fhp = (struct XABFHC *)(fap->fab$l_xab); TheFile = fap->file_no; if (fp->rab$l_ctx == 0xffffffff) { /* Indicator for QIO usage */ chan = fp->rab$w_isi; } else { chan = 0, b = (struct Buffer *)fp->buffer; if(gbuf && (!b)) /* Allocate Buffer if Necessary */ { full_size = (fhp->xab$l_ebk > 0 ? (fhp->xab$l_ebk-1)*fap->fab$w_bls + fhp->xab$w_ffb : 0); osbuffer(fap->fab$w_bls); b->block = 0; } } FIN: return (oserror ? -1 : fid); } /*==========================================================================*/ static int oswb() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Write the Buffer to disk (called by osd routines) .RETURNS Bytes written / -1 for error (oserror provides an explanation) ------------------------------------------------------------*/ { register int len; unsigned long fsize; #if DEBUG printf("....Entering oswb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified); #endif len = 0; if (b->buffer_modified) /* Write Block */ { fsize = b->block*fap->fab$w_bls + b->bytes; if (b->file_size < fsize) b->file_size = fsize; if ((len = vmswrite(b->text, b->bytes)) < 0) FINISH; b->buffer_modified = 0; #if DEBUG printf("....Exiting oswb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified); #endif } FIN: return (oserror ? -1 : len); } /*==========================================================================*/ static int osrb() /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Read the Next Buffer from disk (called by osd routines) .RETURNS Length of read block, 0 for EOF, -1 for errors. .REMARKS The current is rewritten on disk, if it was modified. ------------------------------------------------------------*/ { register int i; /* Read now one Buffer */ #if DEBUG printf("....Entering osrb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified); #endif if (b->buffer_modified) if (oswb() < 0) FINISH; if ((b->block * fap->fab$w_bls + b->bytes) >= b->file_size) { i= 0; FINISH; } /* EOF */ b->block += (b->bytes / fap->fab$w_bls); b->bytes = 0; b->offset = 0; i = vmsread(b->text, b->size); if (i < 0) FINISH; b->bytes = i; #if DEBUG printf("....Exiting osrb: block=%d, size=%d, bytes=%d, offset=%d, file_size=%d, flag=%d\n", b->block, b->size, b->bytes, b->offset, b->file_size, b->buffer_modified); #endif FIN: return (oserror ? -1 : i); } /************************ * OSD Routines * ************************/ /*==========================================================================*/ int osdopen(phname, mode) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Opens disk file for read or write, and positions at file beginning (at end for APPEND mode). The variable `mode' defines the way of opening the file. .RETURNS Upon successful completion a file descriptor is returned. -1 is returned on error. .REMARKS The mode is a combination of the following options \begin{TeX} (defined in {\tt osfile.h}): \begin{itemize} \item {\em READ WRITE READ\_WRITE APPEND} \item {\em NOATTR} on VMS only (record without attributes, i.e. without linefeed) \item {\em LARGE\_BUFFER} for better efficiency when randomly accessing large sections files. \end{itemize} The routine {\em osfop}, if called before {\em osdopen}, allows to use non STMLF record formats. Note that e.g. variable-length record are not interpreted: the file is seen as a stream !! \end{TeX} ------------------------------------------------------------*/ char *phname; /* IN : physical filename */ int mode; /* IN : open mode (see remarks) */ { register int i, bs, bsize; if ((i = vmsopen(phname, mode)) < 0) FINISH; /* Check Special File Organisation */ fp = vmsrab(i); if (fp->rab$l_ctx == 0xffffffff) FINISH; /* Not a Disk */ fap = fp->rab$l_fab; /* Take care of non-sequential files. Close / Reopen to use QIO's only. */ if (fap->fab$b_org != FAB$C_SEQ) { bs = vmsopen ((char *)-1, 0); if (bs < 0) FINISH; fp->rab$l_bkt = 1; /* Virtual Block */ fp->rab$l_ctx = 0xffffffff; /* Use Qio */ fp->rab$w_isi = bs; fap->fab$l_fop = FAB$M_UFO; FINISH; } bs = gbuf; gbuf = 0; /* Don't allocate buffers */ getfile(i); gbuf = bs; /* Determine the size of buffers */ bs = (mode&LARGE_BUFFER ? IO_MAX/fap->fab$w_bls : 1); full_size = vmsize(TheFile); bsize = fap->fab$w_bls * bs; if ((mode&3) == 0) { bsize = MIN(bsize, full_size); bsize = ((bsize+7)>>3)<<3; /* Take multiple of 8 bytes */ } if (osbuffer(bsize) < 0) FINISH; switch(mode&3) { case APPEND: b->block = b->file_size/fap->fab$w_bls; if (osrb() < 0) FINISH; b->offset = b->bytes; break; case READ_WRITE: if (osrb() < 0) FINISH; break; case WRITE: b->block = 0; break; } FIN: return (oserror ? -1 : i); } /*==========================================================================*/ int osdclose(fid) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Closes a disk file (previously opened by osdopen) .RETURNS 0 (success) / -1 (failure) ------------------------------------------------------------*/ int fid; /* IN : file identifier */ { if (getfile(fid) < 0) FINISH; if (chan == 0) if (oswb() < 0) FINISH; /* Write last buffer */ osmmfree(fp->buffer); /* Free Buffer */ vmsclose(TheFile); FIN: return (oserror ? -1 : 0); } /*==========================================================================*/ int osdwait(fid) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Force the physical writing on disk of modified buffers. The argument fid is the file identification obtained from osdopen. .RETURNS 0 (success) / -1 (failure) .REMARKS ------------------------------------------------------------*/ int fid; /* IN : file identifier */ { if (getfile(fid) < 0) FINISH; if (chan) FINISH; if (oswb() < 0) FINISH; FIN: return (oserror ? -1 : 0); } /*==========================================================================*/ int osdread(fid, pbuf, nochar) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Synchronous read from a file. The argument fid is the file identification obtained from osdopen. The routine reads nochar bytes starting at at the current byte position. .RETURNS --- Number of characters actually read. --- -1 if an error is encountered or if the end of file is reached. In case of error, oserror is set to the relevant error number; oserror is set to 0 in case of EOF. .REMARKS It's a straight read, i.e. reading variable-length record files provide stange results... ------------------------------------------------------------*/ int fid; /* IN : file identifier */ char *pbuf; /* IN : pointer to i/o buffer */ unsigned int nochar; /* IN : buffer size */ { register int i, len; register char *p, *pe; if (getfile(fid) < 0) FINISH; p = pbuf, pe = p + nochar; if (nochar == 0) FINISH; /* Read 0 bytes ... */ if (chan) { if (fap->fab$l_fop & FAB$M_UFO) { len = vmsqio (chan, IO$_READVBLK, pbuf, nochar, fp->rab$l_bkt); if (len <= 0) FINISH; fp->rab$l_bkt += (len + 511)>>9; } else len = vmsrlb(chan, pbuf, nochar); p += len; FINISH; } /* Get first Data existing in Buffer */ if ((b->block*fap->fab$w_bls + b->offset) >= b->file_size) FINISH; /* Beyond EOF */ if (b->bytes > b->offset) { len = MIN(nochar, b->bytes - b->offset); move_len = len, LIB$MOVC3(&move_len, b->text + b->offset, p); b->offset += len, p += len; if (len == nochar) FINISH; } /* Read Now Directly into user's buffer */ if (b->buffer_modified) if (oswb() < 0) FINISH; if ((b->block*fap->fab$w_bls + b->offset) >= b->file_size) FINISH; /* Beyond EOF */ b->block += (b->offset / fap->fab$w_bls); b->bytes = 0, b->offset = 0; while( (pe - p) >= b->size) { len = MIN((pe-p),IO_MAX) / b->size; len *= b->size; i = vmsread(p, len); if (i <= 0) FINISH; b->block += (i / fap->fab$w_bls); p += i; if (i < len) /* EOF Encountered */ { b->offset = i % fap->fab$w_bls; FINISH; } } if (p == pe) FINISH; /* All is read */ /* Read now one Buffer */ if (osrb() <= 0) FINISH; /* Get remaining bytes from new buffer */ len = MIN(pe-p, b->bytes); /* b->offset is zero... */ move_len = len, LIB$MOVC3(&move_len, b->text, p); b->offset += len, p += len; FIN: if (oserror) len = -1; else len = p - pbuf; return(len == 0 ? -1 : len); } /*==========================================================================*/ int osdputs(fid, pbuf) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Synchronous write to a file a string of characters followed by a newline (end of record). The argument fid is the file identification obtained from osdopen. .RETURNS Number of characters actually written / -1 for error (oserror provides an explanation) .REMARKS ------------------------------------------------------------*/ int fid; /* IN : file identifier */ char *pbuf; /* IN : EOS-terminated string to write */ { register char *p; register int i; i = (int) strlen(pbuf); p = pbuf + i++; *p = '\n'; i = osdwrite(fid, pbuf, i); *p = EOS; FIN: return (oserror ? -1 : i); } /*==========================================================================*/ int osdwrite(fid, pbuf, nochar) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Synchronous write to a disk file. The argument fid is the file identification obtained from osdopen. The routine writes nochar bytes from the buffer pointed by pbuf to file, at the current byte position. .RETURNS Number of characters actually written. -1 for error (oserror provides an explanation) ------------------------------------------------------------*/ int fid; /* IN : file identifier */ char *pbuf; /* IN : pointer to i/o buffer */ unsigned int nochar; /* IN : number of i/o characters */ { register int len; char *p, *pe, *p1; unsigned long fsize; if (nochar == 0) FINISH; if (getfile(fid) < 0) FINISH; p = pbuf, pe = pbuf+nochar, len = 0; /* Take care first which system service to use */ if (chan) { /* Output via QIO */ if (fap->fab$l_fop & FAB$M_UFO) { len = vmsqio (chan, IO$_WRITEVBLK, pbuf, nochar, fp->rab$l_bkt); if (len <= 0) FINISH; fp->rab$l_bkt += (len + 511)>>9; } else if (fid > 2) len = vmsqio(chan, IO$_WRITELBLK, pbuf, len); else while (p < pe) { /* Map \n to \r\n */ p1 = p + 1; if (p1 < pe) p1 += oscloc(p1, pe-p1, '\n'); if (p1 < pe) *p1 = '\r', len += vmsqio(chan, IO$_WRITELBLK, p, (p1-p)+1), *p1 = '\n'; else p1 = pe, len += vmsqio(chan, IO$_WRITELBLK, p, (p1-p)); p = p1; } FINISH; } /* Buffered File: move data first into Buffer */ len = MIN((pe-p), b->size - b->offset); if (len) { move_len = len, LIB$MOVC3(&move_len, p, b->text + b->offset); p += len, b->offset += len; } if (b->bytes < b->offset) b->bytes = b->offset; if (p > pbuf) b->buffer_modified = 1; if (pe == p) goto FLUSH; /* All is written */ /* ... write the filled buffer ... */ if (b->offset == b->size) if (oswb() < 0) FINISH; b->block += (b->bytes / fap->fab$w_bls); b->bytes = 0, b->offset = 0; /* ... Write Directly to File ... */ for (; (pe-p) >= b->size; p += len) { len = MIN((pe-p), IO_MAX) / b->size; len *= b->size; len = vmswrite(p, len); if (len < 0) FINISH; b->block += (len / fap->fab$w_bls); } if (pe == p) goto FLUSH; /* All is written */ /* ... copy rest to empty Buffer ... */ if (osrb() < 0) FINISH; b->offset = pe - p; move_len = b->offset, LIB$MOVC3(&move_len, p, b->text); p += b->offset; if (b->bytes < b->offset) b->bytes = b->offset; if (b->offset) b->buffer_modified = 1; FLUSH: /* Rewrite buffer to file */ fsize = b->block*fap->fab$w_bls + b->bytes; if (b->file_size < fsize) b->file_size = fsize; len = p - pbuf; if (b->flush) osdwait(fid); FIN: return (oserror ? -1 : len); } /*==========================================================================*/ long osdseek(fid, address, mode) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Moves read/write pointer to a specified byte position. Bytes are numbered from 0 at the file beginning. The address is a displacement in BYTES from start of the file, current position or end of the file as defined by the variable mode (FILE_START, FILE_CURRENT, FILE_END). The argument fid is the file identification obtained from osdopen. .RETURNS Upon successful completion, the new byte position; -1 on error (oserror explains the error). ------------------------------------------------------------*/ int fid; /* IN : file identifier */ long address; /* IN : Move in Bytes */ int mode; /* IN : mode of addressing */ { long pos, sector, posf; if (getfile(fid) < 0) FINISH; if (chan) { oserror = ESPIPE; FINISH;} /* Compute present position for Buffered File */ switch (mode) { case FILE_CURRENT : /* seek to file_current + address */ pos = b->block*fap->fab$w_bls + b->offset; if (address == 0) FINISH; pos += address; /* Absolute address, continue */ break; case FILE_START : /* seek to file_start + address */ pos = address; break; case FILE_END : /* seek to file_end + address */ pos = b->file_size + address; break; default: oserror = EINVAL; break; } if (oserror) FINISH; if (pos < 0) { oserror = ESPIPE; FINISH;} sector = pos / fap->fab$w_bls; posf = pos - b->block * fap->fab$w_bls; /* Offset in Buffer */ /* Test block is loaded */ if (( posf >= 0) && (posf <= b->bytes)) { b->offset = posf; FINISH; } /* ... Must set position to a new sector ... */ if (b->buffer_modified) if (oswb() < 0) FINISH; /* Rewrite first the Sector... */ b->offset = 0, b->bytes = 0; b->block = sector; /* Position to sector ... */ posf = pos - sector*fap->fab$w_bls; /* Fraction of sector */ if (posf == 0) FINISH; /* ... if at boundary, it's OK */ if (osrb() < 0) FINISH; /* ... read the correct sector */ b->offset = posf; FIN: return (oserror ? -1 : pos); } /*==========================================================================*/ long osdsize(fid) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Tells the size of an opened file without moving the file pointer. .RETURNS Upon successful completion, the file size; -1 on error (oserror explains the error). .REMARKS ------------------------------------------------------------*/ int fid; /* IN : file identifier */ { if (getfile(fid) < 0) FINISH; if (chan) { oserror = ESPIPE; FINISH;} FIN: return (oserror ? -1 : b->file_size); } /*==========================================================================*/ int osdunix(fid) /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ .PURPOSE Tells if the opened file is Unix-compatible .RETURNS 0 if non-unix, 1 if Unix, -1 on error (oserror explains the error). .REMARKS VMS only ------------------------------------------------------------*/ int fid; /* IN : file identifier */ { register int isunix; if (getfile(fid) < 0) return(-1); return ((fap->fab$b_rfm == FAB$C_STMLF) && (fap->fab$b_org = FAB$C_SEQ)); }