/* filelist.c Written by Keith Fenske. Copyright (c) 1995, 1996 by Keith Fenske. All rights reserved. File listing utility. Options: -? print a help message only -c number of columns (default: 1) -d column depth in lines (default: 57) -e no eject: do not start each output page with a form feed character (default: eject) -f input form feeds start new columns in listing (default: form feeds are unprintable characters) -g gap string between columns (default: " | ") -h no header or title line (default: header) -l left margin (default: 0) -n no line numbers (default: line numbers) -o output file name -p starting global page number (default: each file has its own local page number starting at 1) -s tab stops (default: 8), but 0 means that tabs are unprintable characters -t top margin (default: 1) -w column width in characters (default: 71) The column depth does not include the header or top margin. The column width does not include the column gap string, left margin, or line numbers. */ #include /* standard character types */ #include /* file statistics */ #include /* standard I/O */ #include /* standard library */ #include /* standard strings */ #include /* time and date */ /* definitions */ #define FALSE 0 /* false == no == off */ #define LINENUMBER "%5d. " /* format string for line numbers */ #define MAXSTRING 256 /* maximum length of a string */ #define TRUE 1 /* true == yes == on */ #ifdef __ALPHA #define PRINTABLE(x) isprint(x) int fileno(); /* DEC Alpha stdio.h defines a macro that returns char */ int fstat(); /* missing from OpenVMS 6.2 stat.h */ #else #define EXIT_FAILURE 0x10000002 /* VAX/VMS failure code */ #define EXIT_SUCCESS 0x00000001 /* VAX/VMS success code */ #define PRINTABLE(x) ((x >= 32) && (x < 127)) #define time_t unsigned long int char * asctime(); int fstat(); /* missing from OpenVMS 6.2 stat.h */ #endif /* function prototypes */ void ClearPageBuffer(); void EndColumn(); void EndColumnLine(); void EndPage(); void ListFile(char *); void ParseOption(char *); void PrintUsage(); void PutInputChar(char); void StartColumn(); void StartPage(); /* global variables */ char * ByteAddress; /* pointer to where next text byte will go in current output line on current column */ int ColumnActive; /* if column has been started */ char * ColumnAddress; /* pointer to first byte in current output column (including line number, but not column gap) */ int ColumnDepth = 57; /* column depth in lines */ int ColumnWidth = 71; /* column width in spaces */ int CurrentColumn; /* current column number on this page: 1, 2, ... */ int DepthUsed; /* how many output lines have been used in current column */ int EjectFlag = TRUE; /* start output page with form feed? */ char FileDateTime[MAXSTRING]; /* file creation date and time */ int FormFeedFlag = FALSE; /* input form feeds start new column? */ int GapSize; /* size of gap string (in bytes) */ char GapString[MAXSTRING]; /* string printed between columns */ int GlobalPageFlag = FALSE; /* use global (common) page numbers? */ int HeaderFlag = TRUE; /* print headers (titles)? */ char * InputFileName; /* current input file name */ int InputLineNumber; /* current input line number */ int InputPosition; /* offset (0, 1, ...) into infinitely wide output line where current input character would print, after tabs are expanded, etc. */ int LeftMargin = 0; /* left margin in spaces */ char * LineAddress; /* pointer to first byte in current output line on current column (including line number, if any) */ int LineNumberFlag = TRUE; /* print line numbers? */ int LineNumberSize; /* width of formatted line number */ int PageActive; /* if page has been started */ char * PageAddress; /* pointer to first byte in the output page buffer (which does not include headers or margins) */ int PageNumber = 1; /* current page number */ int PageSize; /* size of page buffer in bytes */ int PageWidth; /* page width (all columns, but not including left margin) */ int TabStops = 8; /* tab stops every 8 spaces */ int TopMargin = 1; /* top margin in lines */ int TotalColumns = 1; /* number of columns per page */ int WidthUsed; /* how many text bytes have been used on current output line in current column */ /* main routine */ int main ( int argc, /* number of arguments */ char *argv[] /* argument string vector */ ) { int i; /* index variable */ /* initialize some global variables */ strcpy(GapString, " | "); GapSize = strlen(GapString); /* process each argument as an option or a file name */ if (argc <= 1) { PrintUsage(); exit(EXIT_FAILURE); } for (i = 1; i < argc; i ++) { switch (argv[i][0]) { case '\0': /* be nice and ignore null parameters */ break; case '-': ParseOption(&argv[i][1]); break; default: ListFile(argv[i]); break; } } exit(EXIT_SUCCESS); } /* ClearPageBuffer() Clear the page buffer to all blanks (spaces). */ void ClearPageBuffer() { char * cp; /* character pointer */ int i; /* index variable */ cp = PageAddress; /* start clearing here */ for (i = 0; i < PageSize; i ++) (*cp++) = ' '; } /* EndColumn() End the current column. */ void EndColumn() { if (ColumnActive) { ColumnActive = FALSE; CurrentColumn ++; } if (CurrentColumn > TotalColumns) EndPage(); } /* EndColumnLine() End the current output line in the current column. */ void EndColumnLine() { DepthUsed ++; WidthUsed = 0; LineAddress += PageWidth; ByteAddress = LineAddress + LineNumberSize; if (DepthUsed >= ColumnDepth) EndColumn(); } /* EndPage() End the current page. Flush anything still in our buffers. */ void EndPage() { char * cp; /* temporary character pointer */ int empty; /* number of empty lines */ int i, j, k; /* index variables */ char * start; /* start of this output line */ if (ColumnActive) EndColumn(); if (PageActive) { /* flush the old page */ empty = 0; start = PageAddress; for (i = 0; i < ColumnDepth; i ++) { /* trim trailing spaces (blanks) */ cp = start + PageWidth - 1; j = PageWidth; while (j > 0) { if ((*cp) != ' ') break; cp --; j --; } if (j <= 0) { /* empty line */ empty ++; } else { /* this line had something on it; put back accumulated empty lines */ for (k = 0; k < empty; k ++) putchar('\n'); empty = 0; /* put in left margin */ for (k = 0; k < LeftMargin; k ++) putchar(' '); /* put in the (trimmed) output line */ cp = start; for (k = 0; k < j; k ++) putchar(*cp++); putchar('\n'); } start += PageWidth; } ClearPageBuffer(); PageNumber ++; } ColumnActive = PageActive = FALSE; CurrentColumn = 1; } /* ListFile() List one file, given the file name. */ void ListFile( char * name /* file name */ ) { char buffer[MAXSTRING]; /* temporary character string */ unsigned char c; /* one character */ FILE * infp; /* input file pointer */ int status; /* status of called routine */ #ifdef __ALPHA stat_t fileinfo; /* file information buffer */ #else struct stat fileinfo; /* file information buffer */ #endif /* open the file for reading */ InputFileName = name; /* current input file name */ infp = fopen(InputFileName, "r"); if (infp == NULL) { fprintf(stderr, "filelist: can't open file '%s' for reading\n", InputFileName); return; } status = fstat(fileno(infp), &fileinfo); if (status != 0) { fprintf(stderr, "filelist: bad status 0x%x when sensing file '%s'\n", status, InputFileName); exit(EXIT_FAILURE); } strcpy(FileDateTime, asctime(localtime((time_t *)&fileinfo.st_ctime))); FileDateTime[24] = '\0'; /* remove \n */ /* initialize */ ColumnActive = FALSE; CurrentColumn = 1; InputLineNumber = 1; InputPosition = 0; PageActive = FALSE; if (!GlobalPageFlag) PageNumber = 1; /* our formatting changes if we print line numbers */ if (LineNumberFlag) { /* measure size of formatted line number */ sprintf(buffer, LINENUMBER, 1); LineNumberSize = strlen(buffer); } else LineNumberSize = 0; PageWidth = (TotalColumns * (ColumnWidth + LineNumberSize)) + ((TotalColumns - 1) * GapSize); /* allocate a page buffer */ PageSize = ColumnDepth * PageWidth; PageAddress = malloc(PageSize); if (PageAddress == NULL) { fprintf(stderr, "filelist: can't allocate %d-byte page buffer\n", PageSize); exit(EXIT_FAILURE); } ClearPageBuffer(); StartPage(); /* read the file */ do { status = getc(infp); if (status != EOF) { c = (unsigned char) status; switch (c) { case ('\f'): if (FormFeedFlag) { StartColumn(); InputPosition = 0; } else PutInputChar('.'); break; case ('\n'): if (InputPosition == 0) { /* convert empty input line to one space to force line number, etc. */ PutInputChar(' '); } EndColumnLine(); InputLineNumber ++; InputPosition = 0; break; case ('\t'): if (TabStops > 0) { int i, j; i = TabStops - (InputPosition % TabStops); for (j = 0; j < i; j ++) PutInputChar(' '); } else PutInputChar('.'); break; default: if (PRINTABLE(c)) PutInputChar(c); else PutInputChar('.'); break; } } } while (status != EOF); EndPage(); free(PageAddress); /* close the file */ status = fclose(infp); if (status != 0) { fprintf(stderr, "filelist: bad status 0x%x when closing file '%s'\n", status, InputFileName); exit(EXIT_FAILURE); } } /* ParseOption() Parse an option string. The leading "-" must have already been removed. */ void ParseOption( char * option /* option string */ ) { char * cp; /* pointer to next character */ int n; /* number from option */ FILE * outfp; /* file pointer for re-open */ cp = option; switch(*cp++) { case('?'): PrintUsage(); exit(EXIT_SUCCESS); break; case('c'): n = atoi(cp); if ((n >= 1) && (n <= 999)) TotalColumns = n; else { fprintf(stderr, "filelist: number of columns '%s' must be from 1 to 999.\n", option); exit(EXIT_FAILURE); } break; case('d'): n = atoi(cp); if ((n >= 1) && (n <= 9999)) ColumnDepth = n; else { fprintf(stderr, "filelist: column depth '%s' must be from 1 to 9999.\n", option); exit(EXIT_FAILURE); } break; case('e'): EjectFlag = FALSE; break; case('f'): FormFeedFlag = TRUE; break; case('g'): strcpy(GapString, cp); GapSize = strlen(GapString); break; case('h'): HeaderFlag = FALSE; break; case('l'): n = atoi(cp); if ((n >= 0) && (n <= 999)) LeftMargin = n; else { fprintf(stderr, "filelist: left margin '%s' must be from 0 to 999.\n", option); exit(EXIT_FAILURE); } break; case('n'): LineNumberFlag = FALSE; break; case('o'): if (strlen(cp) > 0) outfp = freopen(cp, "w", stdout); else outfp = NULL; if (outfp == NULL) { fprintf(stderr, "filelist: can't open file '%s' for output\n", cp); exit(EXIT_FAILURE); } break; case('p'): n = atoi(cp); if ((n >= 1) && (n <= 99999)) { GlobalPageFlag = TRUE; PageNumber = n; } else { fprintf(stderr, "filelist: global page number '%s' must be from 1 to 99999.\n", option); exit(EXIT_FAILURE); } break; case('s'): n = atoi(cp); if ((n >= 0) && (n <= 999)) TabStops = n; else { fprintf(stderr, "filelist: tab stops '%s' must be from 0 to 999.\n", option); exit(EXIT_FAILURE); } break; case('t'): n = atoi(cp); if ((n >= 0) && (n <= 999)) TopMargin = n; else { fprintf(stderr, "filelist: top margin '%s' must be from 0 to 999.\n", option); exit(EXIT_FAILURE); } break; case('w'): n = atoi(cp); if ((n >= 1) && (n <= 9999)) ColumnWidth = n; else { fprintf(stderr, "filelist: column width '%s' must be from 1 to 9999.\n", option); exit(EXIT_FAILURE); } break; default: fprintf(stderr, "filelist: invalid option '%s'; use -? for help\n", option); PrintUsage(); exit(EXIT_FAILURE); break; } } /* PrintUsage() Print a usage summary (help). */ void PrintUsage() { fprintf(stderr, "\nUsage is: filelist \n\n"); fprintf(stderr, " -? print this help message\n"); fprintf(stderr, " -c number of columns (default: 1)\n"); fprintf(stderr, " -d column depth in lines (default: 57)\n"); fprintf(stderr, " -e do not start each output page with a form feed character\n"); fprintf(stderr, " -f input form feeds start new columns\n"); fprintf(stderr, " -g gap string between columns (default: \" | \")\n"); fprintf(stderr, " -h no header or title line (default: header)\n"); fprintf(stderr, " -l left margin (default: 0)\n"); fprintf(stderr, " -n no line numbers (default: line numbers)\n"); fprintf(stderr, " -o output file name\n"); fprintf(stderr, " -p starting global page number (default: 1 and local)\n"); fprintf(stderr, " -s tab stops (default: 8)\n"); fprintf(stderr, " -t top margin (default: 1)\n"); fprintf(stderr, " -w column width in characters (default: 71)\n"); fprintf(stderr, "\nThe column depth does not include the header or top margin. The column width\n"); fprintf(stderr, "does not include the column gap string, left margin, or line numbers.\n\n"); } /* PutInputChar() Put one input character into the current output line. The character must be a printable character: no tabs, newlines, etc. The current position in the output line is updated. */ void PutInputChar( char c /* single printable character */ ) { char buffer[MAXSTRING]; /* for formatting line number */ if (!ColumnActive) StartColumn(); if (WidthUsed >= ColumnWidth) EndColumnLine(); if (InputPosition == 0) { /* first character from this input line */ if (LineNumberFlag) { sprintf(buffer, LINENUMBER, InputLineNumber); memcpy(LineAddress, buffer, LineNumberSize); } } (*ByteAddress++) = c; InputPosition ++; WidthUsed ++; } /* StartColumn() Force the start of a new column. Add the inter-column gap string between columns. */ void StartColumn() { char * cp; /* temporary character pointer */ int i; /* index variable */ if (ColumnActive) EndColumn(); /* end active column (if any) */ if (!PageActive) StartPage(); /* force new page to start */ ColumnActive = TRUE; ColumnAddress = LineAddress = PageAddress + ((CurrentColumn - 1) * (ColumnWidth + GapSize + LineNumberSize)); ByteAddress = LineAddress + LineNumberSize; DepthUsed = WidthUsed = 0; if ((CurrentColumn > 1) && (GapSize > 0)) { /* put gap string down entire left side of column */ cp = ColumnAddress - GapSize; for (i = 0; i < ColumnDepth; i ++) { memcpy(cp, GapString, GapSize); cp += PageWidth; } } } /* StartPage() Force the start of a new page. Print the header (title) at the top of the page. The current date and time are left-aligned; the page number is right-aligned; and the file name is centered in between. */ void StartPage() { int first; /* spaces between time and name */ int i; /* index variable */ char page[MAXSTRING]; /* to create page number string */ int second; /* spaces between name and page */ if (PageActive) EndPage(); PageActive = TRUE; /* do we print a header (title)? */ if (EjectFlag) putchar('\f'); for (i = 0; i < TopMargin; i ++) putchar('\n'); if (!HeaderFlag) return; /* skip over the left margin and first line number (if any) */ for (i = 0; i < LeftMargin; i ++) putchar(' '); /* find out how big the different header strings are */ printf("%s", FileDateTime); first = ((PageWidth - strlen(InputFileName)) / 2) - strlen(FileDateTime); if (first < 3) first = 3; for (i = 0; i < first; i ++) putchar(' '); printf("%s", InputFileName); sprintf(page, "page %d", PageNumber); second = PageWidth - strlen(FileDateTime) - first - strlen(InputFileName) - strlen(page); if (second < 3) second = 3; for (i = 0; i < second; i ++) putchar(' '); printf("%s\n\n", page); }