From 074f0169c64bc756065afa1fbfbd2bb10b8e67e3 Mon Sep 17 00:00:00 2001 From: "Janet B. Anderson" Date: Sun, 28 Jul 1991 16:58:30 +0000 Subject: [PATCH] Initial revision --- src/util/racPrint.c | 701 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 701 insertions(+) create mode 100644 src/util/racPrint.c diff --git a/src/util/racPrint.c b/src/util/racPrint.c new file mode 100644 index 000000000..90e597a6b --- /dev/null +++ b/src/util/racPrint.c @@ -0,0 +1,701 @@ +/* epics/share/src/util $Id$ + * Author: Roger A. Cole + * Date: mm-dd-yy + * + * Experimental Physics and Industrial Control System (EPICS) + * + * Copyright 1991, the Regents of the University of California, + * and the University of Chicago Board of Governors. + * + * This software was produced under U.S. Government contracts: + * (W-7405-ENG-36) at the Los Alamos National Laboratory, + * and (W-31-109-ENG-38) at Argonne National Laboratory. + * + * Initial development by: + * The Controls and Automation Group (AT-8) + * Ground Test Accelerator + * Accelerator Technology Division + * Los Alamos National Laboratory + * + * Co-developed with + * The Controls and Computing Group + * Accelerator Systems Division + * Advanced Photon Source + * Argonne National Laboratory + * + * Modification Log: + * ----------------- + * 1.1 07/12/89 rac initial version + * 1.2 09/15/89 rac allow multiple input files; change + * the calling sequence so -o specifies + * output file; print non-printing chars + * as . + * 1.3 11/02/89 rac change printing of <; allow default + * input from stdin. If first line on page + * is FF, don't print empty page. + * 1.4 11/03/89 rac fix bug in input from stdin. + * 1.5 12/04/89 rac handle long lines without truncation + * 1.6 02/11/90 rac handle module name and subr name in heading; + * put page number in footing. + * 1.7 skipped + * 1.8 08/31/90 rac handle /subhead text--- divider lines + * 1.9 09/17/90 rac fix long line bug; allow more pages + * 1.10 07-28-91 rac installed in EPICS + * + */ +/*+/mod*********************************************************************** +* TITLE racPrint - filter for printing +* +* SYNOPSIS +* racPrint [-l lm] [-o output] [-t tab] input ... +* +* lm is the number of columns for left margin; default 0. +* tab is the tab width, in columns; default 8. +* input is an ascii file; several may be specified; if none +* is specified, stdin is used. +* output is an ascii file containing the input file broken +* into pages, with pages in reverse order; +* default is stdout. +* +* DESCRIPTION +* The input file is broken into pages and written in reverse page +* order to the output file. The following processing is done: +* o a 3 line heading is written, containing time and date, file +* path, file name, modification date, and subroutien name is +* printed +* o a 3 line footing containing file number and page number is +* printed +* o a left margin of 8 columns is added, plus an additional 7 +* columns for line numbering. (If -l is specified, it +* controls the left margin.) +* o tabs are expanded +* o form-feed characters are honored +* o long lines are folded +* o non-printing characters are printed as , where +* code is the hexadecimal code for the character. Where +* a < occurs, it is printed as a <; hopefully, the context +* will avoid confusion with . +* EXAMPLE +* % racPrint source.c | lpr +* +* BUGS +* o should guard against printing binary files +* o files which exactly fill the last page result in an extra, blank +* page being printed. +* o Doesn't print info following ^L if doesn't follow ^L +* +*-*/ +/*----------------------------------------------------------------------------- +* APOLOGY: I offer my humble apologies to whomever comes across this code-- +* it was my first program in C, and it shows it. R. Cole +*----------------------------------------------------------------------------*/ + +/* WISH LIST: +* - generate a 'table of contents' from names from divider lines +* - generate table of contents for file names and extractable entities +* - add a -help switch +* - handle the backspace character +* - handle "pre-formatted" files, such as man pages output +* - handle extremely long path names, or at least check for overflow +* - allow a `-H heading' option to provide a meaningful heading when +* input is from stdin +*/ + +#include +#include +#include +#include +#include + +#define MAXCHAR 95 /* max characters per line */ +#define MAXLINE 66 /* max lines per page */ +#define MAXPAGE 500 /* max pages at a time */ +#define MAXTOC 10 /* max table of contents pages */ +#define lineBufDim 10000 /* dimension for input line and ovf buffers */ + +#define STRCAT_CHK(str,sub_str) strcat_chk(str,sizeof str,sub_str) + +typedef struct +{ int page_no; /* page number */ + int nlines; /* number of lines on this page */ + char lines[MAXLINE][MAXCHAR+1]; /* lines of the page */ +} PAGE; + + +struct FILE_INF +{ FILE *ptr; /* file pointer from 'fopen' */ + char path[80]; /* pathname to file */ + char date_mod[30]; /* time and date last modified */ + short line_no; /* line number in file */ + char name[20]; /* file name */ + char subr[30]; /* subroutine name from subr heading */ + int fd; /* file descriptor for use in 'stat' call */ +}; + +char date[40]; /* date and time */ +char header[80]; /* heading line */ +int page_no; /* number of current page within file */ +int file_no = 0; /* file number being processed */ +int npages = 0; /* number of pages */ +PAGE pages[MAXPAGE]; /* pages from file */ +int pages_sub = 0; /* subscript into pages structure */ +PAGE toc[MAXTOC]; /* table of contents */ +int toc_sub = 0; /* subscript into toc structure */ + +int left_margin = 0; /* width of left margin in output */ +int line_numbering = 1; /* flag to number lines in output */ +int tab_width = 8; /* columns per 'tab stop' */ +char overflow[lineBufDim]; /* overflow record buffer */ +int overflow_flag = 0; /* overflow record buffer in use */ + + +/*+***************************************************************** +* NAME main - racPrint main program +* +*-*/ +main(argc, argv) + int argc; /* number of command line args */ + char *argv[]; /* command line args */ +{ + void get_time_and_date(); /* get time and date string */ + int i; /* temp for loops */ + struct FILE_INF in_file; /* input file */ + struct FILE_INF out_file; /* output file */ + + char line[MAXCHAR]; /* line of file */ + int get_next_page(); /* get a page from the file */ + + get_time_and_date(date); + +/* ------------------------------------------------------------- +* get the option arguments from the command line +* -------------------------------------------------------------*/ + + get_option_args(argc, argv, &out_file); + +/* ------------------------------------------------------------- +* get the input file names from the command line, processing each +* in turn. +* -------------------------------------------------------------*/ + + while (get_input_file(argc, argv, &in_file) != NULL) { + +/* ------------------------------------------------------------- +* Read pages from input file. If 'buffer' becomes full, print +* the accumulated pages in reverse order. +* -------------------------------------------------------------*/ + + in_file.line_no = 0; + while (get_next_page(&in_file) != EOF) { + if (pages_sub == MAXPAGE) { /* dump and start fresh */ + for (i=pages_sub-1; i>=0; i--) + print_page(&out_file,i); + pages_sub = 0; + } + } + if (in_file.ptr != stdin) /* don't close stdin */ + fclose(in_file.ptr); + } + + for (i=pages_sub-1; i>=0; i--) { + print_page(&out_file,i); + } + + print_banner_page(&in_file,&out_file); + +/* flush and close files */ + fflush(out_file.ptr); + fclose(out_file.ptr); + + exit(0); +} + +/*+/subr************************************************************ +* NAME print_banner_page - write a banner page to the output file +* +*-*/ +print_banner_page(p_in_file,p_out_file) + struct FILE_INF *p_in_file; /* pointer to input file */ + struct FILE_INF *p_out_file; /* pointer to output file */ +{ + char banner[80]; /* temp for banner line */ + char *env; /* pointer to string from getenv */ + char *getenv(); /* get environment variables */ + int i; /* temp for loops */ + + for (i=0; i<79; i++) + banner[i] = '/'; + banner[79] = '\0'; + + env = getenv("LOGNAME"); + + for (i=0; iptr, + "%s\n",banner); + else if (i==15) + fprintf(p_out_file->ptr, + "////////// For: %s\n",env); + else if (i==17) + fprintf(p_out_file->ptr, + "////////// Date: %s\n",date); + else + fprintf(p_out_file->ptr, + "////////// \n"); + + } + + return; +} + +/*+/subr************************************************************ +* NAME print_page - write a page to the output file +* +*-*/ +print_page(p_out_file,page_number) + struct FILE_INF *p_out_file; /* pointer to file */ + int page_number; /* page number to print 0-n */ +{ + int i; /* temp for loops */ + + for (i=0; iptr); + + return; +} + +/*+/subr************************************************************ +* NAME line_store - store a line into a page +* +* DESCRIPTION +* The record is stored into the line buffer, expanding tabs. +* +* If the record isn't an overflow record, left margin and +* record number are added to the line buffer. If the +* record is too long to fit in the line buffer, the overflow +* flag is set and the excess characters are copied into the +* overflow record. +* +* If the record is an overflow record, the left margin and +* padding for the record number field are added to the line +* buffer. If the record is too long to fit in the line buffer, +* the overflow flag and overflow record will be handled as above. +* +* RETURNS +* 0 if no form-feed encountered +* 1 if form-feed encountered and line printed +* 2 if form-feed encountered and no line printed +* +*-*/ +line_store (line, rec, line_no) + char line[]; /* line to receive record, with tabs + expanded and left margin set */ + char rec[]; /* record to store */ + int line_no; /* line number to attach; if zero, + no line number will appear. */ +{ + char c; /* temp for character */ + int ff=0; /* form feed encountered flag */ + char hex_code[3]; /* temp for hex code for char */ + int l_col=0; /* column index in line */ + int lmar; /* left margin to store actual record */ + int r_col=0; /* column index in record */ + +/* ------------------------------------------------------------- +* If the first thing on a line is a ^L (i.e., ff), then don't +* do anything; just exit with `ff' indicated. +* -------------------------------------------------------------*/ + if (rec[0] == '\f') { + ff = 2; + return ff; + } + + while (l_col < left_margin) + line[l_col++] = ' '; /* blank fill left margin */ + + if (!overflow_flag) { + if (line_no > 0) { /* 'print' the line number */ + sprintf (&line[l_col], "%.5d", line_no); + l_col += 5; + line[l_col++] = ' '; + line[l_col++] = ' '; + } + } + else { + sprintf(&line[l_col], " "); + l_col += 5; + line[l_col++] = ' '; + line[l_col++] = ' '; + } + +/* ------------------------------------------------------------- +* Move the record into the line buffer, expanding tabs. If record is +* too long, the overflow is invoked. Line buffer will always be +* terminated with \n\0. Non-printing characters will be shown as +* , where hh is the hex code for the character. Where < occurs +* in the text, it will be shown as itself. +* -------------------------------------------------------------*/ +#define store_c(c) \ +if (l_col < MAXCHAR-1)\ + line[l_col++] = c;\ +else\ + line[lmar-2] = '*'; + + overflow_flag = 0; /* reset overflow flag */ + lmar = l_col; /* save this to make tab expansion 'sane' */ + while ((c=rec[r_col++]) != NULL && !overflow_flag) { + if (l_col < MAXCHAR-2) { + switch (c) { + case '\t': + line[l_col++] = ' '; + while (l_col < MAXCHAR-1 && + ((l_col-lmar) % tab_width) != 0) + line[l_col++] = ' '; + break; + case '\f': + ff = 1; + break; + case '\n': + break; + case '<': + store_c(c); + break; + default: + if (isprint(c)) + line[l_col++] = c; + else { + store_c('<'); + sprintf(hex_code,"%02.2X",c); + store_c(hex_code[0]); + store_c(hex_code[1]); + store_c('>'); + } + break; + } + } + else { + r_col--; + strcpy(overflow, &rec[r_col]); + overflow_flag = 1; + } + } + + line[l_col++] = '\n'; + line[l_col++] = '\0'; + + return ff; +} + +/*+/subr************************************************************ +* NAME get_next_page - format the next page for printing +* +* DESCRIPTION +* +* RETURNS +* +* SEE ALSO +* +* EXAMPLE +* +*-*/ +int get_next_page(p_in_file) + struct FILE_INF *p_in_file; /* pointer to input file */ +{ + char buf_in[lineBufDim];/* input buffer */ + int buf_in_len; /* length of input line */ + int col = 0; /* temp for column in line */ + int eof = 0; /* end-of-file flag */ + int ff; /* form feed flag */ + int line = 0; /* temp for line in page */ + + + line = 3; + +/* ------------------------------------------------------------- +* read the lines from the file for one page, leaving room for 'footing' +* If the overflow flag is set, input line is taken from overflow +* buffer instead of file. +* -------------------------------------------------------------*/ + while (line < MAXLINE-3 && eof == 0) { + if (overflow_flag) { + strcpy(buf_in, overflow); + } + else if (fgets(buf_in, lineBufDim, p_in_file->ptr) != NULL ) { + p_in_file->line_no++; + } + else + eof = EOF; /* no more to read */ + + if (!eof) { + if (strncmp(buf_in, "* NAME", 6) == 0) { + sscanf(&buf_in[6], "%29s", p_in_file->subr); + } + else if (strncmp(buf_in, "/*/subhead ", 11) == 0) { + int i; + int c; + i = 0; + while (i< 29 && (c=buf_in[11+i])!='-') + p_in_file->subr[i++] = c; + p_in_file->subr[i] = '\0'; + + } + ff = line_store(pages[pages_sub].lines[line++], buf_in, + line_numbering ? p_in_file->line_no : 0); + if (ff == 2 && line == 4) { /* FF on first line of page */ + line--; /* throw away the FF; already new page */ + } + else if (ff) { /* if FF encountered, pad rest of page */ + if (ff == 2) /* if no line printed, reset line_no */ + line--; + while (line < MAXLINE-3) + pages[pages_sub].lines[line++][0] = '\n'; + } + } + } + +/*---------------------------------------------------------------------------- +* set up the footing +*---------------------------------------------------------------------------*/ + for(; linedate_mod[0] != '\0') ? "last modified on " : " ", + p_in_file->date_mod); + for (col=strlen(pages[pages_sub].lines[line]); + colname); col++) + pages[pages_sub].lines[line][col] = ' '; + sprintf(&(pages[pages_sub].lines[line][MAXCHAR-2-strlen(p_in_file->name)]), + "%s\n", p_in_file->name); + + line = 1; /* line 1 has path and subroutine name */ + col = 0; + while (colpath); + for (col=strlen(pages[pages_sub].lines[line]); + colsubr); col++) + pages[pages_sub].lines[line][col] = ' '; + sprintf(&(pages[pages_sub].lines[line][MAXCHAR-2-strlen(p_in_file->subr)]), + "%s\n", p_in_file->subr); + + line++; /* line 2 is blank */ + strcpy(pages[pages_sub].lines[line],"\n"); + + pages[pages_sub].page_no = ++npages; + pages_sub++; + page_no++; + + return eof; + +} + +/*+/subr************************************************************ +* NAME get_option_args - get option args from command line +* +* DESCRIPTION +* Gets the command line options, except for input file. If +* an error is encountered, exit(1) is done. +* +*-*/ +get_option_args(argc, argv, p_out_file) + int argc; /* number of command line args */ + char *argv[]; /* command line args */ + struct FILE_INF *p_out_file; /* pointer to output file */ +{ + int c; /* option letter */ + int errflg = 0; /* error flag for options */ + int i; /* temp for string length */ + extern char *optarg; /* argument for an option */ + extern int optind; /* index in argv of next option */ + +/* ------------------------------------------------------------- +* Get any options from the command line and process them. If the +* output file is specified, open it; abort if can't open it. +* -------------------------------------------------------------*/ + + p_out_file->ptr = stdout; /* default for output is stdout */ + while ((c = getopt (argc, argv, "l:t:o:")) != -1) { + switch(c) { + case'l': + left_margin = atoi(optarg); /* -l left_margin */ + case't': + tab_width = atoi(optarg); /* -t tab_width */ + break; + case'o': + if ((p_out_file->ptr = fopen (optarg, "w")) == NULL) { + fprintf(stderr, "racPrint: can't open %s\n", optarg); + exit(1); + } + break; + case'?': + errflg++; + } + } + + if (errflg) + usage(); + + return; +} +usage() +{ + fprintf(stderr, + "Usage: racPrint [-l l_mar] [-t tabwid] [-o out_file]\ +input_file\n"); + exit(2); +} + +/*+/subr************************************************************ +* NAME get_input_file - get next input file from command line +* +* DESCRIPTION +* Gets the next input file argument from the command line. If +* an error is encountered, exit(1) is done. +* +* RETURNS +* 0 if no input file arg found +* 1 if there is an input file +* +*-*/ +get_input_file(argc, argv, p_in_file) + int argc; /* number of command line args */ + char *argv[]; /* command line args */ + struct FILE_INF *p_in_file; /* pointer to input file */ +{ + char *ctime(); /* convert 'time_t' to 'ascii' */ + char *getenv(); /* get environment variable value */ + int i; /* temp for string length */ + extern char *optarg; /* argument for an option */ + extern int optind; /* index in argv of next option */ + struct stat p_fstat; /* pointer to status buffer */ + char *p_ftime; /* pointer to date and time of last mod */ + +/* ------------------------------------------------------------- +* Set up pointers and do the opens for the next input file. +* The file descriptor for the file is also captured and stored. +* -------------------------------------------------------------*/ + + if (optind>=argc && file_no==0) { + p_in_file->ptr = stdin; + strcpy(p_in_file->name, "stdin"); + strcpy(p_in_file->path, " "); + p_in_file->date_mod[0] = '\0'; + + page_no=1; + file_no++; + strcpy(p_in_file->subr, " "); + return(1); + } + else if (optindptr = fopen(argv[optind], "r")) == NULL) { + fprintf(stderr, "racPrint: can't open %s\n", argv[optind]); + exit(1); + } + strcpy(p_in_file->name,argv[optind]); + strcpy(p_in_file->path,getenv("PWD")); /* get working directory */ + STRCAT_CHK(p_in_file->path,"/"); /* append /filename to it */ + STRCAT_CHK(p_in_file->path,p_in_file->name); + p_in_file->fd = fileno(p_in_file->ptr); /* get file descriptor */ + i = fstat(p_in_file->fd,&p_fstat); /* get file stat info */ + p_ftime = ctime(&p_fstat.st_mtime); /* convert to ascii \n\0 */ + p_ftime[strlen(p_ftime)-1] = '\0'; /* remove the \n */ + strcpy(p_in_file->date_mod,p_ftime); /* date last modified */ + + optind++; + + page_no=1; + file_no++; + strcpy(p_in_file->subr, " "); + return(1); + } + else + return(0); +} + +/*+/subr************************************************************ +* NAME strcat_chk - concatenate strings, checking bounds +* +* DESCRIPTION +* The sub_string is concatenated to the end of string. If +* string isn't large enough, only the part of sub_string which +* will fit is concatenated. Strings are \0 terminated. +* +*-*/ +strcat_chk (string, size_of_string, sub_string) + char string[]; /* 'master' string */ + int size_of_string; /* declared length of string */ + char sub_string[]; /* substring to concatenate */ +{ + int i; + + i = 0; + while (string[i] && i