home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
vol_300
/
355_02
/
slk2.exe
/
SPP
/
SYS.C
< prev
next >
Wrap
C/C++ Source or Header
|
1991-06-09
|
19KB
|
940 lines
/*
New Sherlock Preprocessor -- system module.
Source: sys.c
Started: October 7, 1985
Version:
July 20, 1988;
November 1, 1988 bug fix: extra '\r' in sysnlput().
February 10, 1989 bug fix: kludge in sysopen().
February 16, 1989
periods removed from error messages.
t_line bug fixed in sysopen().
June 22, 1989
backslash newlines now filtered by sysnext().
August 3, 1989
signal code added to syscsts().
errors written to stderr.
PUBLIC DOMAIN SOFTWARE
Sherlock, including the SPP, SDEL and SDIF programs, was placed in
the public domain on June 15, 1991, by its author,
Edward K. Ream
166 North Prospect Ave.
Madison, WI 53705.
(608) 257-0802
Sherlock may be used for any commercial or non-commercial purpose.
DISCLAIMER OF WARRANTIES
Edward K. Ream (Ream) specifically disclaims all warranties,
expressed or implied, with respect to this computer software,
including but not limited to implied warranties of merchantability
and fitness for a particular purpose. In no event shall Ream be
liable for any loss of profit or any commercial damage, including
but not limited to special, incidental consequential or other damages.
*/
#include "spp.h"
#ifdef MICRO_SOFT
#include <signal.h>
#include <fcntl.h>
#include <sys\types.h>
#include <sys\stat.h>
#include <io.h>
#include <time.h>
#endif
#ifdef TURBOC
#include <signal.h>
#include <fcntl.h>
#include <sys\stat.h>
#include <io.h>
#include <conio.h>
#include <time.h>
#endif
/*
Define the format of a file node.
The node contains all information pertaining to the file.
The fil_name and f_line are used to update preprocessor globals.
*/
typedef enum { NULL_STAT,
CLOSED_STAT, INPUT_STAT, OUTPUT_STAT, LOCAL_STAT, EOF_STAT
} en_stat;
static struct FN {
struct FN * f_next; /* pointer to next node */
char * fil_name; /* file name */
int f_line; /* line number */
char * f_buffer; /* file buffer */
int f_bufsize; /* size of file buffer: bytes */
int f_handle; /* handle to file. */
char * f_bufp; /* pointer into file buffer */
int f_bufc; /* characters in buffer */
en_stat f_type; /* status of file */
int f_lastc; /* restart character */
};
#define FN_SIZE (sizeof(struct FN))
static struct FN * in_list = NULL; /* List of input nodes. */
static struct FN * outn_list = NULL; /* List of output nodes. */
/*
The following global variables are copies of the fields in FN.
Global variables are used as an efficiency measure.
*/
static char * s_ip = NULL; /* Copy of f_bufp for input file. */
static int s_ic = 0; /* Copy of f_bufc for input file. */
static char * s_op = NULL; /* Copy of f_bufp for output file. */
static int s_oc = 0; /* Copy of f_bufc for output file. */
#define MAX_INLEVEL 20 /* Max depth of nested #includes. */
#define IBUF_SIZE 2048 /* Size of each input buffer. */
#define OBUF_SIZE 2048 /* Size of the output buffer. */
/*
Define "pushed back" characters used by sysn1().
*/
static int push_back = -1; /* Used by macro logic. */
static int file_put_back = -1; /* Used by non-macro logic. */
/*
Define variables used by time and date routines.
*/
static long ltime;
static struct tm *newtime;
static char time_buf [30];
static char date_buf [30];
/*
Declare static functions in this module.
*/
static void raw_close (int);
static int raw_creat (char *);
static int raw_open (char *);
static int raw_read (int, char *, int);
static int raw_write (int, char *, int);
static int syscstat (void);
static void sysn1 (void);
static struct FN *sys_new (int);
static void sys_release (void);
/*
Close a file opened with raw_open() or raw_creat().
*/
static void
raw_close(handle)
int handle;
{
TRACEP("raw_close", printf("(handle: %d)\n", handle));
close (handle);
}
/*
Open the file for writing only.
Return a handle (int) or ERROR.
*/
static int
raw_creat(name)
char *name;
{
TRACEP("raw_creat", printf("(%s)\n", name));
chmod(name, S_IREAD | S_IWRITE);
return creat(name, S_IREAD | S_IWRITE);
}
/*
Open the file for reading only.
Return a handle (int) or ERROR.
*/
static int
raw_open(name)
char *name;
{
TRACEP("raw_open", printf("(%s)\n", name));
return open(name, O_RDONLY | O_BINARY);
}
/*
Read n bytes from the file described by handle into buffer[].
Return the number of bytes read.
*/
static int
raw_read(handle, buffer, n)
int handle;
char *buffer;
int n;
{
int result;
TRACEP("raw_read", printf("(handle: %d, buffer: %lx, n: %d)\n",
handle, buffer, n));
result = read (handle, buffer, n);
TRACEP("raw_read", printf("returns %d\n", result));
return result;
}
/*
Write n bytes from buffer[] to the file described by handle.
Return the number of bytes written.
*/
static int
raw_write(handle, buffer, n)
int handle;
char *buffer;
int n;
{
TRACEP("raw_write", printf("(handle: %dx, buffer: %lx, n: %d)\n",
handle, buffer, n));
return write (handle, buffer, n);
}
/*
Close all files and exit.
Do not call fatal() or sysabort() from inside
sysiclose(), sysoclose(), or sys_release().
*/
void
sysabort()
{
/* Close the output file. */
if (outn_list != NULL) {
sysoclose();
}
/* Close all input files. */
while(in_list != NULL) {
sysiclose();
}
sysend();
exit(BAD_EXIT);
}
/*
Put a non-newline to the output file.
This is the second most called routine after sysnext().
*/
void
syscput(c)
char c;
{
struct FN * fnp;
*s_op++ = c;
s_oc++;
if (s_oc == OBUF_SIZE) {
fnp = outn_list;
if (raw_write(fnp->f_handle, fnp->f_buffer, OBUF_SIZE) !=
OBUF_SIZE) {
error("Disk full");
return;
}
s_oc = 0;
s_op = fnp -> f_buffer;
}
}
/*
Open a file for output. Only one such file may be open at a time.
Return TRUE if all went well.
*/
bool
syscreat(name)
register char * name;
{
register struct FN * fnp;
int handle;
TRACEP("syscreat", printf("(%s)\n", name));
fnp = outn_list;
if (fnp == NULL) {
/* Allocate node for output file. */
fnp = outn_list = sys_new(OBUF_SIZE);
}
else if (fnp -> f_type != CLOSED_STAT) {
syserr("syscreat: Can't happen");
}
/* Actually open the file. */
handle = raw_creat(name);
if (handle == ERROR) {
return FALSE;
}
fnp -> f_handle = handle;
fnp -> f_type = OUTPUT_STAT;
/* The output buffer is empty. */
s_oc = 0;
s_op = fnp -> f_buffer;
return TRUE;
}
/*
Return 0 if no character is ready from the keyboard.
Otherwise, return the character itself.
*/
static int sys_inited = 0;
/*
Interrupt handler for control-c.
*/
static void
ctrl_handler(int sig)
{
fprintf(stderr, "\nSPP terminated by operator\n");
exit(0);
}
void
syscsts()
{
if (sys_inited == 0) {
sys_inited = 1;
if (signal(SIGINT, ctrl_handler) == SIG_ERR) {
fprintf(stderr, "\nsignal failed.\n");
exit(-1);
sysabort();
}
}
}
#if 0 /* Machine independent version. Old code. */
static int
syscstat()
{
if (kbhit()) {
return fgetchar() & 0x7f;
}
else {
return 0;
}
}
/*
Get console status and pause if the user has hit control S.
Abort if user has hit control C.
*/
#define CONTROL_C 3
void
syscsts()
{
int c;
c = syscstat();
if (c == CONTROL_C) {
fprintf(stderr, "\nSPP terminated by operator\n");
sysabort();
}
}
#endif /* old code */
/*
Return the print string of the current date.
*/
char *
sysdate()
{
char *p;
time(<ime);
newtime = localtime(<ime);
p = asctime(newtime);
if (strlen(p) != 25) {
return "";
}
else {
strcpy(date_buf, "\"");
p[11] = '\0';
strcat(date_buf, p);
p[24] = '\0';
strcat(date_buf, p + 20);
strcat(date_buf, "\"");
TRACEPN("sysdate", printf("returns: %s\n", date_buf));
return date_buf;
}
}
/*
Shut down the system. This routine is called just before doing an
exit(). Do any last minute things here.
*/
void
sysend()
{
TICK("sysend");
}
/*
Push c back into the file input stream, not the macro input stream.
*/
void
sys_fpb(c)
int c;
{
file_put_back = c;
}
/*
Close the current input file and pop back one level.
*/
void
sysiclose()
{
register struct FN * fnp;
TICK("sysiclose");
#ifdef DEBUG
if (t_inlevel < 0) {
syserr("sysiclose: Bad t_inlevel");
}
#endif
/* Close the current input file. */
fnp = in_list;
raw_close(fnp -> f_handle);
/* Release the current node. */
sys_release();
if (t_inlevel > 0 && in_list == NULL) {
syserr("sysiclose: NULL in_list");
}
/* Pop back one level. */
if (in_list != NULL) {
if (t_inlevel < 0) {
syserr("sysiclose: Unexpected in_list");
}
fnp = in_list;
s_ic = fnp -> f_bufc;
s_ip = fnp -> f_bufp;
t_file = fnp -> fil_name;
t_line = fnp -> f_line;
}
t_inlevel--;
if (t_inlevel == -1) {
ch = END_FILE;
TRACEPN("sysiclose", printf("restarts. ch=END_FILE\n"));
}
else {
ch = (unsigned char) fnp -> f_lastc;
TRACEPN("sysiclose", printf("retarts. ch=lastc: %s\n",
pr_ch(ch)));
}
TRACEP("sysiclose", printf("exit: t_inlevel=%d\n", t_inlevel));
}
void
syshdump(void)
{
int i;
int c;
/* Output to file. */
TRACEPN("file_dump",
syssput("\nDump of hold buffer<");
for (i = 0; i < hold_count; i++) {
c = hold_buf[i];
syssput(pr_ch(c));
}
syscput('>');
sysnlput());
printf("\n\nDump of hold buffer: hold_count = %d\n<", hold_count);
for (i = 0; i < hold_count; i++) {
c = hold_buf[i];
printf("%s", pr_ch(c));
}
printf(">\n\n");
}
/* Output ALL held output. */
void
syshflush(void)
{
int i;
TRACEP("syshflush", syshdump());
for (i = 0; hold_count > 0; i++, hold_count--) {
syscput(hold_buf [i]);
}
}
/* Discard all held output. */
void
syshkill(void)
{
TRACEP("syshkill", syshdump());
hold_count = 0;
}
/*
Delete all leading newlines and white space from the hold buffer,
but leave the last leading white space.
Return a pointer at the first non-blank character or NULL.
*/
char *
syshnldel(void)
{
int head, tail, end, i;
char c;
TICK("syshnldel");
end = hold_count;
hold_count = 0;
head = tail = 0;
/* Scan through the buffer, deleting leading white space. */
while (head < end) {
c = hold_buf[head++];
if (c == '\n') {
/* Delete any tentatively held white space. */
tail = 0;
hold_count = 0;
}
else if (c == ' ' || c == '\t') {
/* Tentatively hold the white space. */
hold_buf[tail++] = c;
hold_count++;
}
else {
hold_buf[tail++] = c;
hold_count++;
break;
}
}
/* Copy the remainder of the hold buffer. */
while (head < end) {
hold_buf[tail++] = hold_buf[head++];
hold_count++;
}
hold_buf[tail] = '\0';
TRACEPN("syshnldel", syshdump());
/* Return the first non-white space character of the buffer. */
for (i = 0; i < hold_count; i++) {
c = hold_buf[i];
if (c != ' ' && c != '\t' && c != '\n') {
return &hold_buf[i];
}
}
return NULL;
}
/*
Allocate space for a new file node and file buffer.
Fill in all other fields to neutral values.
*/
static struct FN *
sys_new(buf_size)
int buf_size;
{
register struct FN * fnp;
TRACEP("sys_new", printf("(buf_size: %d)\n", buf_size));
fnp = (struct FN *) m_alloc(sizeof(struct FN));
fnp -> f_buffer = (char *) m_alloc(buf_size);
fnp -> f_bufsize = buf_size;
fnp -> f_next = NULL;
fnp -> fil_name = NULL;
fnp -> f_bufp = fnp -> f_buffer;
fnp -> f_bufc = 0;
fnp -> f_type = CLOSED_STAT;
TRACEPN("sys_new", printf("returns %lx\n", fnp));
return fnp;
}
/*
Set the global variable ch to the next character from the input stream.
This is the most often called routine in the whole program.
This routine eliminates all '\r' and backslash-newlines.
Thus, '\r' and backslash-newline may appear ANYWHERE in the program.
*/
void
sysnext()
{
for(;;) {
/* sysn1() filters out all '\r' characters. */
sysn1();
/* Intercept backslash newline combinations here. */
if (ch == '\\') {
sysn1();
if (ch == '\n') {
t_line++;
continue;
}
else {
push_back = ch;
ch = '\\';
return;
}
}
else {
return;
}
}
}
void
sysn1(void)
{
register int n;
register struct FN * fnp;
/* Priority 1: A pushed back character. */
if (push_back != -1) {
ch = push_back;
TRACEP("sysnext", printf("pushback: %s\n", pr_ch(ch)));
push_back = -1;
goto done;
}
if (!m_flag && !hold_flag && ch_hold) {
TRACEPN("sysnext", printf("immediate out %s\n", pr_ch(ch)));
syscput(ch);
}
else if (!m_flag && ch_hold) {
if (hold_count >= MAX_HOLD) {
warning("Hold buffer overflow");
syshflush();
}
hold_buf [hold_count++] = ch;
TRACEPN("sysnext", printf("buffers %s\n", pr_ch(ch)));
}
loop:
/* Priority 2: The rescan buffer. */
if (m_flag) {
ch = *p_rescan++;
if (ch) {
TRACEP("sysnext", printf("rescan ch: %s\n",
pr_ch(ch)));
goto done;
}
m_flag = FALSE;
/*
Priority 2A: The trailing character.
This is NOT part of the rescan buffer.
*/
if (file_put_back != -1) {
ch = file_put_back;
TRACEP("sysnext", printf("fpb ch: %s\n",
pr_ch(ch)));
file_put_back = -1;
goto done;
}
/* FALL THROUGH. */
}
/* Priority 3: The current file. */
if (s_ic > 0) {
s_ic--;
ch = *s_ip++;
TRACEP("sysnext_v", printf("ch: %s\n", pr_ch(ch)));
goto done;
}
/* A file or local buffer is empty. */
fnp = in_list;
switch(fnp -> f_type) {
case EOF_STAT:
/* Continue returning END_FILE until the file is closed. */
ch = END_FILE;
return;
case INPUT_STAT:
n = raw_read(fnp->f_handle, fnp->f_buffer, IBUF_SIZE);
if (n > 0) {
s_ic = n;
s_ip = fnp -> f_buffer;
s_ic--;
ch = *s_ip++;
goto done;
}
else {
fnp -> f_type = EOF_STAT;
ch = END_FILE;
return;
}
default:
TRACEP("sysn1",
printf("in_list = %lx, in_list -> f_type = %d\n",
in_list, in_list -> f_type));
syserr("sysn1: Bad f_type field");
}
done:
/* Never pass '\r' to the rest of the program. */
if (ch == '\r') {
goto loop;
}
/*
Only buffer non-macro characters in the current file.
Do not buffer any character if hold_flag is TRUE.
*/
ch_hold = !m_flag && t_inlevel == 0;
TRACEP("sysnext", printf("returns %s\n", pr_ch(ch)));
}
/*
Put a newline to the output file.
*/
void
sysnlput()
{
TICK("sysnlput");
/* November 1, 1988: We are in text mode, so this is not needed.
syscput('\r');
----- */
syscput('\n');
/* Give user a chance to stop. */
syscsts();
}
/*
Close the output file(s).
*/
void
sysoclose()
{
register struct FN * fnp;
TICK("sysoclose");
fnp = outn_list;
if (fnp != NULL && fnp -> f_type != CLOSED_STAT) {
syscput(END_FILE);
raw_write(fnp -> f_handle, fnp -> f_buffer, s_oc);
raw_close(fnp -> f_handle);
fnp -> f_type = CLOSED_STAT;
}
}
/*
Open a file for input.
Return TRUE if all went well.
*/
bool
sysopen(name)
char *name;
{
struct FN * fnp;
int handle;
TRACEP("sysopen", printf("(%s); old t_inlevel = %d\n",
name, t_inlevel));
if (m_flag == TRUE) {
m_flag = FALSE;
error("Macro expansion truncated following #include");
}
/* Save information about the current level on the stack. */
if (t_inlevel != -1) {
if (in_list == NULL) {
syserr("sysopen: Can't happen");
}
fnp = in_list;
fnp -> f_line = t_line;
fnp -> f_bufc = s_ic;
fnp -> f_bufp = s_ip;
fnp -> f_lastc = (int) ch;
}
/* Enter a new level. */
if (t_inlevel >= MAX_INLEVEL) {
fatal("include files nested too deeply");
}
else {
t_inlevel++;
}
/* Create a new file node and link to the front of the list. */
TRACEP("sysopen", printf("set up new file: call sys_new\n"));
fnp = sys_new(IBUF_SIZE);
fnp -> f_next = in_list;
fnp -> f_type = INPUT_STAT;
in_list = fnp;
/* Actually open the file. */
handle = raw_open(name);
if (handle == ERROR) {
/* Deallocate the node. */
TRACEP("sysopen", printf("file open %s fails\n", name));
sys_release();
t_inlevel--;
return FALSE;
}
else {
fnp -> f_handle = handle;
}
/* Set the global variables. */
t_file = str_alloc(name);
fnp -> fil_name = t_file;
/* Put the first character of the file into ch. */
/* 2/10/88
Force a '\n' as the first character and decrease
the line number by one to compensate.
This little kludge will make preprocessor directives
legal in the first line of any file.
Since sysnext() is not called to get this character,
it won't be buffered or output.
*/
s_ic = 0;
s_ip = fnp -> f_buffer;
/* 2/16/89: Since we add a newline, start t_line at 0. */
ch = '\n';
t_line = 0;
/* -----
sysnext();
----- */
TRACEP("sysopen", printf("exit: t_inlevel=%d, in_list=%lx\n",
t_inlevel, (long)in_list));
return TRUE;
}
/*
Push back c so that sysnext() will return it next.
*/
void
syspushback(c)
int c;
{
push_back = c;
}
/*
Remove one node from the input list.
*/
static void
sys_release()
{
register struct FN * fnp;
TICK("sys_release");
#ifdef DEBUG
if (in_list == NULL) {
syserr("sys_release: Can't happen");
}
#endif
/* Remove the node from the input list. */
fnp = in_list;
in_list = fnp -> f_next;
fnp -> f_type = CLOSED_STAT;
/* Deallocate the node. */
if (fnp -> f_buffer != NULL) {
m_free(fnp -> f_buffer);
}
m_free(fnp);
/* Reset the global variables from the next node. */
if (in_list == NULL) {
s_ic = 0;
}
else {
fnp = in_list;
s_ic = fnp -> f_bufc;
s_ip = fnp -> f_bufp;
}
}
/*
Put one string to the output file.
*/
void
syssput(s)
register char * s;
{
TRACEP("syssput", printf("(%s)\n", s));
while (*s) {
syscput(*s++);
}
}
/*
Return a print string of the current time.
*/
char *
systime()
{
char *p;
time(<ime);
newtime = localtime(<ime);
p = asctime(newtime);
if (strlen(p) != 25) {
return "\"\"";
}
else {
strcpy(time_buf, "\"");
*(p+19) = '\0';
strcat(time_buf, p+11);
strcat(time_buf, "\"");
TRACEPN("systime", printf("returns: %s\n", time_buf));
return time_buf;
}
}