minix/commands/simple/pr.c

505 lines
12 KiB
C
Raw Normal View History

2005-04-21 16:53:53 +02:00
/* pr - print files Author: Michiel Huisjes */
/* Pr - print files
*
* Author: Michiel Huisjes.
* Modified: Jacob P. Bunschoten. (30 nov 87)
* When "columns" is not given and numbering is on:
* line numbers are correlated with input lines.
* (try pr [-1] -n file )
* tabs are accounted for.
* When numbering is turned on, width know this.
* automatic line-folding. -f to get the original program.
* backspaces are accounted for. -b to disable this.
* multi-column mode changed.
* header can be given and used.
* format changed may occur between printing of several files:
* pr -l30 file1 -w75 file2
*
* Modified: Rick Thomas. (Sept 12, 1988)
* added "-M" option to cover functionality of old "-n" option,
* and made "-n" option behavior compatible with system V.
*
* Usage: pr [+page] [-columns] [-h header] [-wwidth] [-llength] [-ntm] [files]
* -t : Do not print the 5 line header and trailer at the page.
* -n : Turn on line numbering.
* -M : Use "Minix" style line numbering -- Each page begins at
* a line number that is an even multiple of the page length.
* Like the listings in Appendix E of the book.
* +page : Start printing at page n.
* -columns : Print files in n-columns.
* -l length: Take the length of the page to be n instead of 66
* -h header: Take next argument as page header.
* -w width : Take the width of the page to be n instead of default 79
* -f : do not fold lines.
*
* Modified: Lars Fredriksen (Jan 19, 1990)
* fixed the program so that
* pr -n *.c
* would work. The clobal variable 'width' was decremented
* by NUM_WIDTH, for each file, resulting in width finally
* being so small that nothing was printed. Used the local
* variable 'w' for the width adjustment (in print())
*
* Modified: Kenneth J. Hendrickson (10 April 1991)
* date in header should be last modification date for files,
* and the current time for stdin.
*
* Modified: Kees J. Bot (5 October 1992)
* Use localtime(3) to get the date, it knows TZ.
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <time.h>
#include <stdlib.h>
#include <stdio.h>
#define DEF_LENGTH 66
#define DEF_WIDTH 79
#define NUM_WIDTH 8
#define TAB_WIDTH 8 /* fixed tab_width */
/* Used to compute next (fixed) tabstop */
#define TO_TAB(x) (( (x) + TAB_WIDTH ) & ~07 )
typedef char BOOL;
#define FALSE 0
#define TRUE 1
/* EAT: eat rest of input line */
#define EAT(fp) while((c=getc(fp))!='\n' && c!=EOF)
/* L_BUF: calculate address of pointer to char (string) used in format */
#define L_BUF(i,j) * (char **) (line_buf + (i + j*length)*sizeof(char *))
char *header;
BOOL no_header;
BOOL number = FALSE;
BOOL minix_number = FALSE;
BOOL ext_header_set = FALSE; /* external header given */
BOOL back_space = TRUE; /* back space correction in line width */
BOOL dont_fold = FALSE; /* original. If the line does not fit eat it. */
short columns;
short cwidth;
short start_page = 1;
short width = DEF_WIDTH;
short length = DEF_LENGTH;
short linenr;
char *line_buf; /* used in format for multi-column output */
char output[1024];
_PROTOTYPE(int main, (int argc, char **argv));
_PROTOTYPE(static char *myalloc, (size_t size));
_PROTOTYPE(char skip_page, (int lines, int width, FILE * filep));
_PROTOTYPE(void format, (FILE * filep));
_PROTOTYPE(void print_page, (int pagenr, int maxcol));
_PROTOTYPE(void print, (FILE * filep));
_PROTOTYPE(void out_header, (int page));
_PROTOTYPE(void print_time, (time_t t));
int main(argc, argv)
int argc;
char *argv[];
{
FILE *file;
char *ptr;
int index = 1; /* index is one ahead of argc */
int line, col;
setbuf(stdout, output);
do {
if (argc == index) /* No arguments (left) */
goto pr_files;
ptr = argv[index++];
if (*ptr == '+') {
start_page = atoi(++ptr);
continue;
}
if (*ptr != '-') { /* no flags */
index--;
goto pr_files;
}
if (*++ptr >= '0' && *ptr <= '9') {
columns = atoi(ptr);
if (columns <= 0) columns = 1;
continue; /* Fetch next flag */
}
while (*ptr) switch (*ptr++) {
case 't': no_header = TRUE; break;
case 'n':
number = TRUE;
minix_number = FALSE;
break;
case 'M':
number = TRUE;
minix_number = TRUE;
break;
case 'h':
header = argv[index++];
ext_header_set = TRUE;
break;
case 'w':
if ((width = atoi(ptr)) <= 0) width = DEF_WIDTH;
*ptr = '\0';
break;
case 'l':
if ((length = atoi(ptr)) <= 0) length = DEF_LENGTH;
*ptr = '\0';
break;
case 'b': /* back_space correction off */
back_space = FALSE;
break;
case 'f': /* do not fold lines */
dont_fold = TRUE;
break;
default:
fprintf(stderr, "Usage: %s [+page] [-columns] [-h header] [-w<width>] [-l<length>] [-nMt] [files]\n", argv[0]);
exit(1);
}
continue; /* Scan for next flags */
/* ============== flags are read. Print the file(s) ========= */
pr_files:
if (!no_header) length -= 10;
if (columns) {
cwidth = width / columns + 1;
if (columns > width) {
fprintf(stderr, "Too many columns for page width.\n");
exit(1);
}
/* Allocate piece of mem to hold some pointers */
line_buf = myalloc(length * columns * sizeof(char *));
}
for (line = 0; line < length; line++)
for (col = 0; col < columns; col++)
L_BUF(line, col) = NULL;
if (length <= 0) {
fprintf(stderr, "Minimal length should be %d\n", no_header ?
1 : 11);
exit(1);
}
while (index <= argc) { /* print all files, including stdin */
if (index < argc && (*argv[index] == '-' || *argv[index] == '+'))
break; /* Format change */
if (argc == index) { /* no file specified, so stdin */
if (!ext_header_set) header = "";
file = stdin;
} else {
if ((file = fopen(argv[index], "r")) == (FILE *) 0) {
fprintf(stderr, "Cannot open %s\n", argv[index++]);
continue;
}
if (!ext_header_set) header = argv[index];
}
if (columns)
format(file);
else
print(file);
fclose(file);
if (++index >= argc)
break; /* all files (including stdin) done */
}
if (index >= argc) break;
/* When control comes here. format changes are to be done.
* reinitialize some variables */
if (!no_header) length += 10;
start_page = 1;
ext_header_set = FALSE;
if (columns) free(line_buf);
} while (index <= argc); /* "pr -l60" should work too */
(void) fflush(stdout);
return(0);
}
char skip_page(lines, width, filep)
int lines, width;
FILE *filep;
{
short c;
int char_cnt;
int w;
do {
w = width;
if (number) /* first lines are shorter */
if (!columns || /* called from print(file) */
!(lines % columns)) /* called from format(file) */
w -= NUM_WIDTH;
char_cnt = 0;
while ((c = getc(filep)) != '\n' && c != EOF && char_cnt < w) {
/* Calculate if this line is longer than "width (w)"
* characters */
if (c == '\b' && back_space) {
if (--char_cnt < 0) char_cnt = 0;
} else if (c == '\t')
char_cnt = TO_TAB(char_cnt);
else
char_cnt++;
}
if (dont_fold && c != '\n' && c != EOF) EAT(filep);
lines--;
if (c == '\n') linenr++;
} while (lines > 0 && c != EOF);
return c; /* last char read */
}
void format(filep)
FILE *filep;
{
char buf[512];
short c = '\0';
short index, lines, i;
short page_number = 0;
short maxcol = columns;
short wdth;
short line, col;
do {
/* Check printing of page */
page_number++;
if (page_number < start_page && c != EOF) {
c = (char) skip_page(columns * length, cwidth, filep);
continue;
}
if (c == EOF) return;
lines = columns * length;
for (line = 0; line < length; line++)
for (col = 0; col < columns; col++) {
if (L_BUF(line, col) != NULL)
free(L_BUF(line, col));
L_BUF(line, col) = (char *) NULL;
}
line = 0;
col = 0;
do {
index = 0;
wdth = cwidth - 1;
if (number && !col) /* need room for numbers */
wdth -= NUM_WIDTH;
/* Intermidiate colums are shortened by 1 char */
/* Last column not */
if (col + 1 == columns) wdth++;
for (i = 0; i < wdth - 1; i++) {
c = getc(filep);
if (c == '\n' || c == EOF) break;
if (c == '\b' && back_space) {
buf[index++] = '\b';
if (--i < 0) { /* just in case ... */
i = 0;
index = 0;
}
} else if (c == '\t') {
int cnt, max;
max = TO_TAB(i);
for (cnt = i; cnt < max; cnt++)
buf[index++] = ' ';
i = max - 1;
} else
buf[index++] = (char) c;
}
buf[index++] = '\0';
/* Collected enough chars (or eoln, or EOF) */
/* First char is EOF */
if (i == 0 && lines == columns * length && c == EOF) return;
/* Alloc mem to hold this (sub) string */
L_BUF(line, col) = myalloc(index * sizeof(char));
strcpy(L_BUF(line, col), buf);
line++;
line %= length;
if (line == 0) {
col++;
col %= columns;
}
if (dont_fold && c != '\n' && c != EOF) EAT(filep);
lines--; /* line ready for output */
if (c == EOF) {
maxcol = columns - lines / length;
}
} while (c != EOF && lines);
print_page(page_number, maxcol);
} while (c != EOF);
}
void print_page(pagenr, maxcol)
short pagenr, maxcol;
{
short pad, i, j;
short width;
char *p;
if (minix_number)
linenr = (pagenr - 1) * length + 1;
else
linenr = 1;
if (!no_header) out_header(pagenr);
for (i = 0; i < length; i++) {
for (j = 0; j < maxcol; j++) {
width = cwidth;
if (number && j == 0) { /* first columns */
printf("%7.7d ", linenr++); /* 7 == NUM_WIDTH-1 */
width -= NUM_WIDTH;
}
pad = 0;
if (p = (char *) L_BUF(i, j))
for (; pad < width - 1 && *p; pad++) putchar(*p++);
if (j < maxcol - 1) while (pad++ < width - 1)
putchar(' ');
}
putchar('\n');
}
if (!no_header) printf("\n\n\n\n\n");
}
void print(filep)
FILE *filep;
{
short c = '\0';
short page_number = 0;
short lines;
short cnt;
short w = width;
BOOL pr_number = TRUE; /* only real lines are numbered, not folded
* parts */
linenr = 1;
if (number) w -= NUM_WIDTH;
do {
/* Check printing of page */
page_number++;
if (page_number < start_page && c != EOF) {
pr_number = FALSE;
c = skip_page(length, w, filep);
if (c == '\n') pr_number = TRUE;
continue;
}
if (c == EOF) return;
if (minix_number) linenr = (page_number - 1) * length + 1;
if (page_number == start_page) c = getc(filep);
/* Print the page */
lines = length;
while (lines && c != EOF) {
if (lines == length && !no_header) out_header(page_number);
if (number)
if (pr_number)
printf("%7.7d ", linenr++); /* 7 == NUM_WIDTH-1 */
else
printf("%7c ", ' '); /* 7 == NUM_WIDTH-1 */
pr_number = FALSE;
cnt = 0;
while (c != '\n' && c != EOF && cnt < w) {
if (c == '\t') {
int i, max;
max = TO_TAB(cnt);
for (i = cnt; i < max; i++) putchar(' ');
cnt = max - 1;
} else if (c == '\b' && back_space) {
putchar('\b');
cnt--;
} else
putchar(c);
c = getc(filep);
cnt++;
}
putchar('\n');
if (dont_fold && c != '\n' && c != EOF) EAT(filep);
lines--;
if (c == '\n') {
c = getc(filep);
pr_number = TRUE;
}
}
if (lines == length) /* We never printed anything on this
* page -- */
return; /* even the header, so dont try to fill it up */
if (!no_header) /* print the trailer -- 5 blank lines */
printf("\n\n\n\n\n");
} while (c != EOF);
/* Fill last page */
if (page_number >= start_page) {
while (lines--) putchar('\n');
}
}
static char *myalloc(size)
size_t size; /* How many bytes */
{
void *ptr;
ptr = malloc(size);
if (ptr == NULL) {
fprintf(stderr, "malloc returned NULL\n");
exit(1);
}
return(char *) ptr;
}
void out_header(page)
short page;
{
time_t t;
struct stat buf;
if (strlen(header)) {
stat(header, &buf); /* use last modify time for file */
t = buf.st_mtime;
} else
(void) time(&t); /* use current time for stdin */
print_time(t);
printf(" %s Page %d\n\n\n", header, page);
}
char *moname[] = {
"Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
};
/* Print the date. */
void print_time(t)
time_t t;
{
struct tm *tm;
tm = localtime(&t);
printf("\n\n%s %2d %2d:%02d %d",
moname[tm->tm_mon],
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
1900 + tm->tm_year
);
}