home *** CD-ROM | disk | FTP | other *** search
- /******************************************************************************/
- /* SGMLIO: Ideas for non-DOS environments. */
- /* Includes both stream I/O and low-level I/O (stream tested for MSC only). */
- /******************************************************************************/
- #include "vmincl.h" /* Include files for VM. */
- #include "vmxtrn.h" /* Declarations for VM public variables. */
- /******************************************************************************/
- #ifdef IBMC /* Stream I/O for IBMC (mainframe) only. */
- #define STREAMIO
- #else
- #define LOWLEVIO
- #endif /* def IBMC */
- #ifdef STREAMIO
- #define OKIO(rc) ((UNIV)(rc)>NULL) /* OPEN return: 1=good; 0=error. */
- #define OPENREAD "rb" /* Stream I/O: binary read (for MSC stream). */
- #define close fclose /* Stream I/O: equivalent function. */
- #define lseek fseek /* Stream I/O: equivalent function. */
- #define open fopen /* Stream I/O: equivalent function. */
- #define tell ftell /* Stream I/O: equivalent function. */
- #define eof feof /* Stream I/O: equivalent function. */
- #else /* If low-level I/O: */
- #define OKIO(rc) ((rc)>0) /* OPEN return: 1=good; 0=error. */
- #define OPENREAD (int)(O_RDONLY|O_BINARY) /* Low-level I/O: binary read. */
- #endif /* def STREAMIO */
- /******************************************************************************/
- /* Functions used in this module only.
- */
- struct iofcb *fcbgen(char *);
- void fcbnext(struct iofcb *);
- void readeof(struct iofcb *, char *, int *);
- int readcat(struct iofcb *);
- int readfrst(struct iofcb *, char *);
- /******************************************************************************/
- /* SGMLIO: Text processor I/O services for SGML.
- SGML must see a file in which RE and RS (CR/LF) are present between records,
- and EOFCHAR (Ctl-Z) is present at the end. SGMLIO must supply these
- characters if they are not naturally present in the file.
- SGML will open two files at a time: when an entity is nested, the
- new file is opened before closing the old in order to make sure the
- open is successful. If it is, the original open file is closed temporarily
- (FILEPEND); when the stack is popped, the new file is closed and the original
- file is re-opened (FILECONT). SGML will check error returns
- for the initial open of a file and all reads, and for re-openings when the
- stack is popped, but not for closes. Setting io.ipbrc<0 indicates
- an error; 0 or more is a successful operation,
- except for READ where io.ipbrc is the number of characters read, and must
- exceed 0 to be successful. The first READ must always be successful, and
- normally consists of just priming the buffer with EOBCHAR (or RS EOBCHAR).
- SGMLIO must assure that there is an EOBCHAR at the end of each block read,
- except for the last block of the entity, which must have an EOFCHAR.
-
- SGML views an entity as a contiguous whole, without regard to its
- actual form of storage. SGMLIO supports entities that are equivalent
- to a single file of one or more records, or to a concatenation of
- files (e.g., "profile.gml;main.gml"). It also recognizes
- an initial code that indicates whether prefixing of an RS and/or trimming
- of a trailing RE/RS are wanted (e.g., "3=profile.gml;main.gml").
- If the code is 1 or 3 (the default), RS is prefixed to the first
- record read for a file. If 2 or 3 (the default), an RE/RS sequence
- occurring at the end of a file (before the EOF) will be trimmed. Many
- editors insert such a sequence to allow file concatenation, but it can
- cause an extraneous space to occur when formatting.
- A default setting is contained in boundsw, which can be gotten from the
- command line or a processing instruction. A code of 4 in a system identifier
- means to use the default value for that file, just as if no code were
- specified. A code of 0 means that no special treatment is wanted.
- */
- VOID sgmlio(io)
- struct ipbfile *io; /* IPB: file I/O services. */
- {
- struct iofcb *f; /* Active file control block. */
- int fnum; /* READ: Return code from DOS. */
- char *flast; /* READ: Last char read into buffer. */
-
- switch (io->ipbtype) {
- case FILENM: /* Generate fileid from name & external ID. */
- io->ipbn = (UNIV)savestr(xidgen((struct fpi *)io->ipbn));
- return;
- case FILEOPEN: /* Open new file for binary read-only. */
- f = fcbgen((UNCH *)io->ipbn); /* Generate FCB. */
- io->ipbn = (UNIV)f; /* Return its pointer. */
- fcbnext(f); /* Make it next file. */
- f->fcbfd = open(f->fcbfile+1, OPENREAD); /* Open file. */
- io->ipbrc = OKIO(f->fcbfd) ? 1 : -1; /* Normalize return code. */
- f->fcbfirst = 1; /* Next read is first. */
- return;
- case FILEREAD: /* Read file at current location. */
- f = (struct iofcb *)io->ipbn; /* Get FCB pointer. */
- if (f->fcbfirst) { /* Fake first READ of an entity. */
- fnum = readfrst(f, io->ipbbuf);
- goto readrtrn;
- }
- if (f->fcbcatsw && readcat(f)) { /* Open catenated file, if due. */
- fnum = -1; goto readrtrn; /* Return if can't open it. */
- }
- f->fcboff = tell(f->fcbfd); /* Location of START of block.*/
- #ifdef LOWLEVIO
- if (eof(f->fcbfd)) { /* File ended on previous READ. */
- *io->ipbbuf = EOFCHAR; /* Fake read of Ctl-Z. */
- fnum = 1;
- }
- else fnum = read(f->fcbfd, io->ipbbuf, readcnt); /* Read the file. */
- if (fnum<0) goto readrtrn; /* Return if read error. */
- flast = io->ipbbuf + fnum-1; /* Last character read. */
- if (*flast==EOFCHAR || fnum<readcnt) readeof(f, flast, &fnum);
- else *(io->ipbbuf + fnum++) = EOBCHAR; /* Add EOB char. */
- #else /* def STREAMIO */
- if (eof(f->fcbfd)) { /* File ended on previous READ. */
- *io->ipbbuf = EOFCHAR; /* Fake read of Ctl-Z. */
- *(io->ipbbuf+1) = EOS; /* End the string. */
- }
- else
- fgets(io->ipbbuf, (int)readcnt, f->fcbfd); /* Read the next block. */
- if (ferror(f->fcbfd)) {
- fnum = -1; goto readrtrn; /* Return if read error. */
- }
- fnum = (int)strlen(io->ipbbuf); /* Length of record read. */
- /* TO DO: Insert RE,RS in front of record if needed. */
- /* If appended, fix READEOF to avoid it on EOF. */
- flast = io->ipbbuf + fnum; /* Point after last char read. */
- if (*(--flast)==EOFCHAR) { /* File ended with this READ. */
- readeof(f, flast, &fnum);
- }
- else {
- ++fnum; ++flast; /* Include EOB char in count. */
- *flast = EOBCHAR; /* Add EOB char. */
- /* TO DO: Insert RE,RS in front of record if needed. */
- /* If appended, fix READEOF avoid it on EOF. */
- }
- #endif /* def STREAMIO */
- readrtrn:
- io->ipbrc = fnum; /* Do return code.*/
- return;
- case FILEPEND: /* Close file temporarily. */
- (f = IPBFCB)->fcbcatsw = 0; /*Resume in same file.*/
- lseek(f->fcbfd, f->fcboff, SEEK_SET); /* Start of block. */
- lseek(f->fcbfd, (long)io->ipboff, SEEK_CUR); /* Current char. */
- f->fcboff = tell(f->fcbfd); /* Save location. */
- close(f->fcbfd);
- return;
- case FILECONT: /* Reopen file; position to saved location. */
- f->fcbfd = open((f = IPBFCB)->fcbfile+1, OPENREAD);
- io->ipbrc = OKIO(f->fcbfd) ? 1 : -1;
- if (io->ipbrc>0)
- io->ipbrc = (int)lseek(f->fcbfd, f->fcboff, SEEK_SET);
- return;
- case FILECLOS: /* Close file permanently. */
- close(IPBFCB->fcbfd);
- free((UNIV)IPBFCB);
- return;
- }
- }
- /******************************************************************************/
- /* READEOF: Process end of file. There are three possibilities:
- 1. Data and Ctl-Z read.
- 2. Data only read.
- 3. Ctl-Z only read.
- */
- void readeof(f, flast, pfnum)
- struct iofcb *f; /* Pointer to file control block. */
- char *flast; /* Last char read into buffer. */
- int *pfnum; /* Number of chars read. */
- {
- int eofsw = 0; /* 1=EOFCHAR present; 0=supply one. */
-
- if (*flast==EOFCHAR) eofsw = 1; /* Set switch if EOF present. */
- else {++flast; ++*pfnum;} /* Prepare for EOF or EOB. */
- if (f->fcbnext) { /* Another file in this entity? */
- f->fcbcatsw = 1; /* For next READ. */
- *flast = EOBCHAR; /* Not entity end. */
- }
- else { /* Entity ended: */
- if (!eofsw) /* If no EOF char? */
- *flast = EOFCHAR; /* Add EOF char. */
- else if ( f->fcbRE /* EOF was there: is RE trim wanted? */
- && *pfnum>=3 /* At least 2 chars before EOF? */
- && *(--flast)==0x0A /* Is RS there? */
- && *(--flast)==0x0D ) /* Is RE there? */
- {*flast = EOFCHAR; *pfnum -= 2;} /* Cut them off. */
- }
- }
- /******************************************************************************/
- /* READCAT: Open concatenated file in current entity.
- Returns 1 if open was successful, 0 if not.
- */
- int readcat(f)
- struct iofcb *f; /* Pointer to file control block. */
- {
- f->fcbcatsw = 0; /* Next read will not be new concatenated file. */
- close(f->fcbfd); /* Close old file.*/
- fcbnext(f); /* Find new file. */
- f->fcbfd = open(f->fcbfile+1, OPENREAD);
- return (!OKIO(f->fcbfd));
- }
- /******************************************************************************/
- /* READFRST: Simulate first READ of entity in order to handle RS insertion
- and guarantee an error-free return.
- */
- int readfrst(f, ipbbuf)
- struct iofcb *f; /* Pointer to file control block. */
- char *ipbbuf; /* IPBFILE: Ptr to SGML read buffer. */
- {
- f->fcbfirst = 0; /* Next read will no longer be first. */
- if (f->fcbRS) { /* RS prefix wanted? */
- *((STRING)ipbbuf) = RSCHAR; /* Put RS in buffer. */
- *((STRING)ipbbuf+1) = EOBCHAR; /* And end block. */
- return(2); /* Successful "read" */
- }
- /* else */
- *((STRING)ipbbuf) = EOBCHAR; /* Put EOB in buffer. */
- return(1); /* Successful "read" */
- }
- /******************************************************************************/
- /* FCBGEN: Generates a file control block from an external identifier.
- */
- struct iofcb *fcbgen(x)
- UNCH *x; /* Ptr to external ID (len+EOS). */
- {
- struct iofcb *f; /* Ptr to new fcb. */
-
- f = (struct iofcb *)vmalloc((UNS)sizeof(struct iofcb));
- f->fcbxid = x;
- f->fcbRS = (int)*(x+1)-'0' & 1;
- f->fcbRE = (int)*(x+1)-'0' & 2;
- f->fcbnext = x+3;
- return(f);
- }
- /******************************************************************************/
- /* FCBNEXT: Generates next full system fileid for a file control block.
- */
- VOID fcbnext(f)
- struct iofcb *f; /* Pointer to file control block. */
- {
- UNS i; /* Work variable. */
- UNCH *p, *n; /* Work variable. */
-
- if ((p = f->fcbnext)==0) return; /* Return if no more files for entity.*/
- f->fcbnext = 0; /* Assume this file is the last. */
- if ((n = strchr(p, ';'))!=0) { /* If ; found, there is still another.*/
- *n = EOS; /* Temporary EOS for strlen to use. */
- f->fcbnext = n+1; /* Location of next file after this. */
- }
- i = strlen(p); /* Get length of this file name. */
- memcpy(pd+1, p, ++i); /* Move to buffer to work on it. */
- *pd = (char)++i; /* Prefix length to it. */
- p = xidpath(pd); /* Complete the fileid (if necessary).*/
- memcpy(f->fcbfile, p, *p); /* Save full fileid in fcb. */
- if (f->fcbnext) *n = ';'; /* Put the ; back in the xid. */
- }
- /******************************************************************************/
- /* XIDGEN: Generates a system identifier (fileid) from a name,
- and possibly a public or system identifier as well.
- It returns a ptr to the fileid in the fpi or in the pd buffer.
- Note 1: This routine assumes that an installation assigns
- reserved entity names that correspond to public identifiers, so it
- ignores the actual public identifier; in a more general approach,
- a table would be checked for the corresponding system ID.
- For version-dependent public entities, a real application
- would use different tables, depending on the display device
- (or different suffixes, in a scheme like that used here).
- If this routine is modified so that, under some conditions,
- it cannot generate a fileid, it should return 0 in those cases.
- FILEOPEN should check for the 0 and return an error code to SGML
- at that time.
- Note 2: This routine allows boundary treatment codes for data
- content notation files (f->fpistore==6) even though such files
- are not parsed. VM strips the code before displaying the file
- identifier (which is analagous to a text processor stripping it
- before passing it to an application). Alternatively, this
- routine could be modified so that the code is never inserted
- in the first place.
- Note 3: This is the place to check that system identifiers are
- valid for your environment, and that the boundary code (if
- specified) is valid. SGML does not check such things because
- it knows nothing about the environment.
- */
- UNCH *xidgen(f)
- struct fpi *f; /* Pointer to fpi control block. */
- {
- UNS i, j; /* Work variables. */
- UNCH *p; /* Work pointer. */
-
- *pd = 4; /* Starting length of constructed ID. */
- pd[1] = (char)boundsw; /* Use default boundary treatment. */
- pd[2] = '='; /* Boundary treatment delimiter. */
- p = pd+3; /* Pt after boundary treatment delimiter. */
- /* If a complete system ID was specified, use it all.
- If only the boundary treatment was specified (n=), use it
- when constructing the system ID.
- If the boundary treatment was omitted, append the system ID to
- the default boundary treatment code.
- */
- if (f->fpisysl) {
- if (*(p = f->fpisysis+2)=='=') {
- if (*(--p)>'3' || *p<'0') *p = (char)boundsw;
- if (*(p+2)==EOS) {
- memcpy(pd, f->fpisysis, 3);
- p = pd+3;
- }
- else return(f->fpisysis);
- }
- else {
- memcpy(pd+3, f->fpisysis+1, f->fpisysl-1);
- *pd = f->fpisysl+2;
- return(pd);
- }
- }
- /* If not, add a suffix to the entity name to produce a system ID. */
- memcpy(p, f->fpinm+1, (i = f->fpinml-2));
- j = (f->fpipubl ? 1 : 0)*(f->fpiversw>0 ? 2 : 1)*6+f->fpistore;
- memcpy(p+i, genext[j], 5); /* Add extension.*/
- *pd += (char)(i + 4); /* Add entity name and extension lengths. */
- return(pd);
- }
- /******************************************************************************/
- /******************************************************************************/
- #ifdef IBMC
- /******************************************************************************/
- /* XIDPATH: The path buffer is used to store the completed fileid.
- TO DO: If mode is omitted, default it to the current drive,
- or to * if cdirsw says path searching is wanted.
- */
- UNCH *xidpath(pt)
- UNCH *pt; /* Ptr to fileid in temporary storage (len+EOS).*/
- {
- /* If path is to be searched, get the actual fileid. */
- if (filefind(pt+1, path+1)) {
- *path = (char)(strlen(path+1)+2); /* Get len of actual fileid. */
- pt = path; /* Return ptr to actual fileid. */
- }
- return(pt);
- }
- /******************************************************************************/
- /* FILEFIND: Returns 1 if a file exists, 0 if not.
- If it exists, the complete fileid is returned in caller's buffer.
- TO DO: Return the full fileid found by STATE.
- */
- /******************************************************************************/
- char statebuf[27] = "STATE ";
- /******************************************************************************/
- int filefind(filename, buffer)
- char *filename; /* File to be searched for (with EOS). */
- char *buffer; /* Buffer for found filename (with EOS). */
- {
- if (strlen(filename)>20) return (0); /* Return if invalid fileid.*/
- strcat(statebuf, filename);
- if (system(statebuf)) return (0); /* Return if no file found. */
- strcpy(buffer, filename);
- return (1);
- }
- /******************************************************************************/
- /******************************************************************************/
- #else /* if ndef IBMC */
- /******************************************************************************/
- /******************************************************************************/
- /* XIDPATH: Prefixes a path to a system identifier (DOS fileid) if
- path searching is wanted.
- The path buffer is used to build the prefixed fileid.
- */
- UNCH *xidpath(pt)
- UNCH *pt; /* Ptr to fileid in temporary storage (len+EOS).*/
- {
- UNS i; /* Work variable. */
-
- /* If path is to be searched, get the full path name and fileid. */
- if (!cdirsw && ((i = filefind(pt+1, path+1))>1) ) {
- *path = *pt += (char)i; /* Add len of path to fileid len. */
- pt = path; /* Return offset of path+fileid. */
- }
- return(pt);
- }
- /******************************************************************************/
- /* FILEFIND: This is the routine to locate a file, using SRCHPATH.
- If the file is found in the current directory, 1 is returned.
- If found in another directory on the environment path list,
- the length of the path name (no EOS) is returned; otherwise 0.
- */
- /******************************************************************************/
- #ifdef TURBOC
- #define srchpath(s) searchpath(s)
- #else
- static char *srchpath(char *);
- static int testopen(char *);
- #endif /* def TURBOC */
- /******************************************************************************/
- int filefind(filename, buffer)
- char *filename; /* File to be searched for (with EOS). */
- char *buffer; /* Buffer for found filename (with EOS). */
- {
- char *fptr; /* Pointer to found filename. */
- unsigned fnlen; /* Length of filename (+EOS). */
-
- _fmode = (int)O_BINARY; /* Default file I/O is binary mode. */
- if ((fptr = srchpath(filename))==0) return (0);/*Return if no file found.*/
- memcpy( buffer , fptr, (fnlen = (unsigned)strlen(fptr)) );
- if (strlen(filename)==fnlen) return (1); /* In current directory. */
- buffer[fnlen] = '\0'; /* Somewhere on path. */
- return ((int)(fnlen+1));
- }
- #ifndef TURBOC
- /******************************************************************************/
- /* SRCHPATH: This file contains the routine to locate a file, utilizing the PATH
- environment variable for the directories to search.
- If the file is found in the current directory, a pointer to
- the filename argument is returned. If the filename
- is found in another directory on the environment path list,
- the path name (with EOS) is stored in a static buffer, and a
- pointer to it is returned; if not found, NULL is returned.
- NOTE: DOS 2.0 or greater only; stack requirements: ~300 bytes.
- */
- /******************************************************************************/
- char tar_et[80]; /* Buffer for path (if any) + filename (+EOS). */
- /******************************************************************************/
- char *srchpath(filename)
- char *filename; /* File to be searched for (with EOS). */
- {
- char paths[256]; /* Buffer for PATHS environment variable. */
- char *pptr; /* Pointer into paths. */
- char *tptr; /* Pointer into tar_et. */
- unsigned fnlen; /* Length of filename (+EOS). */
-
- /* First check in the local directory */
- if (testopen(filename)>0) return(filename);
- /* No luck; search the path list from the environment. */
- if ((pptr = getenv("PATH"))==0) return (0);
- /* else */ strcpy(paths, pptr);
- fnlen = (unsigned)strlen(filename)+1;
- pptr = paths;
- while (*pptr != 0) {
- /* copy the directory name */
- tptr = tar_et;
- while (*pptr != ';' && *pptr != 0) {
- *tptr++ = *pptr++;
- }
- if (*pptr) pptr++; /* beyond the ';' (ready for next try) */
- if (*(tptr-1) != '/' && *(tptr-1) != '\\')
- *tptr++ = '\\';
- /* Concatenate the filename to the path. */
- memcpy(tptr, filename, fnlen);
- if (testopen(tar_et)>0) return (tar_et);
- }
- return (0); /* can't find one */
- }
- /******************************************************************************/
- /* TESTOPEN: Test whether a file can be opened.
- */
- int testopen(pathname)
- char *pathname; /* File to be tested (with EOS). */
- {
- int fid; /* File handle returned by open; work variable. */
-
- if ((fid = open(pathname, O_RDONLY))>0) {
- close(fid);
- return (1);
- }
- /* else */ return(0);
- }
- /******************************************************************************/
- #endif /* ndef TURBOC */
- /******************************************************************************/
- #endif /* def IBMC */
- /******************************************************************************/
- /******************************************************************************/