|
|
DataMuseum.dkPresents historical artifacts from the history of: Commodore CBM-900 |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about Commodore CBM-900 Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - download
Length: 8536 (0x2158)
Types: TextFile
Notes: UNIX file
Names: »pr.c«
└─⟦f27320a65⟧ Bits:30001972 Commodore 900 hard disk image with partial source code
└─⟦f4b8d8c84⟧ UNIX Filesystem
└─⟦this⟧ »cmd/pr.c«
/*
* pr -- print files
*
* All references to page length exclude possible margin lines.
*/
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#define LSIZE 256 /* line size */
#define LENGTH (66-MARGIN) /* default page length */
#define WIDTH 80 /* default page width */
#define TMAR 5 /* five lines of top margin */
#define BMAR 5 /* five lines of bottom margin */
#define HEADER 2 /* line # where header appears */
#define MARGIN (TMAR+BMAR)
/*
* info on input file streams
* Unless -m is in effect, only f[0] is used.
*/
struct f {
int f_ff; /* '\f' received */
FILE *f_stream; /* input stream */
};
int ncol = 1, /* # of columns */
nskip, /* # pages to skip of each file */
length = LENGTH, /* page length */
width = WIDTH, /* page width */
lno, /* current input line # */
fwidth; /* field width */
char schar, /* separator char */
tflag, /* -t: no header or margins */
mflag, /* -m: multiple file output */
nflag, /* -n: line number output */
*date, /* date string */
*header, /* -h: header text */
**lines; /* buffer addr for multicolumn */
struct f f[20]; /* input file info */
char **init( ),
*malloc( ),
*ctime( );
int page1( ), page2( ),
putl( ), nop( );
FILE *openf( );
int (*page)( ) = page1;
/*
* paginate files to standard output
* If no files are given, use standard input. The file name "-" also
* means standard input.
*/
main( argc, argv)
register char **argv;
{
argv = init( argc, argv);
if (*argv)
while (*argv) {
f[0].f_stream = openf( *argv);
print( *argv++);
fclose( f[0].f_stream);
}
else
print( "");
return (0);
}
/*
* initialize & get options
* Flags are recognized up to the first file name. If multi-column (-N),
* allocate line array. If printing multiple files (-m), open all files.
* There are two paging algorithms: one file per column (page1), and
* many columns per file (page2). The latter requires page buffering.
* init( ) makes this selection.
*/
char **
init( ac, av)
register char **av;
{
register mar = MARGIN;
static char obuf[BUFSIZ];
time_t tvec;
setbuf( stdout, obuf);
while (++av, --ac) {
if (av[0][0] == '+')
if ((nskip=atoi( &av[0][1])) <= 0)
fatal( "bad skip");
else
continue;
if (av[0][0] != '-')
break;
switch (av[0][1]) {
case '\0':
break;
case 'l':
length = atoi( &av[0][2]) - mar;
continue;
case 'w':
width = atoi( &av[0][2]);
continue;
case 'h':
if (av[0][2])
header = &av[0][2];
else {
if (--ac <= 0)
fatal( "missing header arg");
header = (++av)[0];
}
continue;
case 's':
if ((schar=av[0][2]) == '\0')
schar = '\t';
continue;
case 'm':
++mflag;
continue;
case 't':
++tflag;
length += mar;
mar = 0;
continue;
case 'n':
++nflag;
continue;
default:
if ('0'<=av[0][1] && av[0][1]<='9')
ncol = atoi( &av[0][1]);
else
fatal( "no such switch %s", av[0]);
continue;
}
break;
}
f[0].f_stream = stdin;
if (mflag && av[0]) {
ncol = 0;
do {
f[ncol++].f_stream = openf( av++[0]);
} while (av[0]);
}
/*
* check that all options jive
*/
if (length <= 0)
if (length == -mar) { /* gunja artifice */
length = 1;
++tflag;
}
else
fatal( "length too small");
fwidth = width / ncol;
if (schar)
--fwidth;
if (fwidth <= 0 || (nflag && fwidth < 10))
fatal( "width too small");
if (fwidth >= LSIZE-1)
fatal( "too wide");
if (ncol>1 && mflag==0) {
if ((lines=malloc( (ncol-1)*length*sizeof( char *))) == NULL)
fatal( "insufficient core");
page = page2;
}
time( &tvec);
date = ctime( &tvec);
return (av);
}
/*
* open input file
* Failure to open is fatal. openf( "-") returns stdin.
*/
FILE *
openf( file)
register char *file;
{
register FILE *stream;
extern errno;
lno = 0;
if (file[0]=='-' && file[1]=='\0')
return (stdin);
if ((stream=fopen( file, "r")) == NULL)
if (errno == EMFILE)
fatal( "too many files for -m");
else
fatal( "can't open %s", file);
return (stream);
}
/*
* print from open input streams
* Control output of pages, perhaps provide header/footer margins and title.
* The paging routine is expected to give an eof warning on the last page.
*/
print( file)
char *file;
{
register i,
pg,
eof;
for (pg=1; pg<=nskip; ++pg)
if ((*page)( nop))
return;
do {
if (tflag == 0)
for (i=0; i<TMAR; ++i) {
if (i != HEADER) {
putchar( '\n');
continue;
}
printf( "%.12s%.5s %s Page %d, line %D\n",
date+4, date+19,
header? header: file,
pg,
(long)(pg-1)*(mflag?1:ncol)*length+1);
}
eof = (*page)( putl);
if (tflag == 0)
for (i=0; i<BMAR; ++i)
putchar( '\n');
} while (++pg, eof==0);
}
/*
* page one stream per column
* This handles all cases of one column per page.
* Formfeed advances output of that stream to the next page.
* Eof is only indicated when all streams give this condition.
*/
page1( putline)
int (*putline)( );
{
register i,
j;
char lbuf[LSIZE];
for (i=0; i<length; ++i) {
for (j=0; j<ncol; ++j) {
getline( &f[j], lbuf);
(*putline)( lbuf, j<ncol-1? schar: 0);
}
(*putline)( (char *)0);
}
i = 0;
for (j=0; j<ncol; ++j) {
f[j].f_ff = 0;
if (feof( f[j].f_stream))
++i;
else if (ungetc( getc( f[j].f_stream), f[j].f_stream) == EOF)
++i;
}
return (i == j);
}
/*
* page one stream across multiple columns
* The first `ncol'-1 columns of text are buffered; the last is simply
* read as needed. Formfeeds advance output to the next column.
*/
page2( putline)
int (*putline)( );
{
register i,
j,
k;
char lbuf[LSIZE];
for (i=0; i<(ncol-1)*length; ++i) {
if ((i%length) == 0)
f[0].f_ff = 0;
if (k = getline( &f[0], lbuf)) {
if ((lines[i]=malloc( k+1)) == NULL)
fatal( "out of core");
strcpy( lines[i], lbuf);
}
else
lines[i] = NULL;
}
f[0].f_ff = 0;
for (i=0; i<length; ++i) {
for (j=0; j<ncol-1; ++j) {
k = j*length + i;
if (lines[k]) {
(*putline)( lines[k], schar);
free( lines[k]);
}
else
(*putline)( "", schar);
}
getline( &f[0], lbuf);
(*putline)( lbuf, 0);
(*putline)( (char *)0);
}
if (feof( f[0].f_stream))
return (1);
if (ungetc( getc( f[0].f_stream), f[0].f_stream) == EOF)
return (1);
return (0);
}
/*
* read a line
* Simple char processing is done, including tab expansion. getline( )
* will return an empty line if f_ff is set. Lines are truncated to
* fit the field width.
*/
getline( fp, lbuf)
register struct f *fp;
char *lbuf;
{
register col;
register char *p;
char c;
col = 0;
p = lbuf;
if (feof( fp->f_stream)==0 && fp->f_ff==0) {
if (nflag) {
sprintf(p, "%4d: ", ++lno);
p += 6;
}
for (; ; ) {
switch (c = getc(fp->f_stream)) {
case '\f':
++fp->f_ff;
case EOF:
if (nflag && (p == &lbuf[6]))
p = lbuf;
break;
case '\n':
break;
case '\r':
continue;
case '\t':
do {
if (p<&lbuf[LSIZE-1] && col<fwidth)
*p++ = ' ';
} while (++col & 7);
continue;
case '\b':
if (col) {
--col;
if (p<&lbuf[LSIZE-1] && col<fwidth)
*p++ = c;
}
continue;
default:
if (p<&lbuf[LSIZE-1] && col<fwidth)
*p++ = c;
++col;
continue;
}
break;
}
}
*p = '\0';
return (p - lbuf);
}
/*
* write a line, incrementally
* A line of output can be built by successive calls to putl( ). A line
* address of 0 puts the newline. Simple char processing is done,
* including tab optimization. Each line segment is padded to the
* field width, unless a there is a field separator char.
*/
putl( lbuf, schr)
char *lbuf;
{
register char *p;
register c,
nextxcol;
static col,
xcol;
if ((p=lbuf) == NULL) {
col = 0;
xcol = 0;
putchar( '\n');
return;
}
nextxcol = xcol + fwidth;
for (; ; ) {
if ((c= *p++) == '\0') {
if ((c=schr) == '\0')
break;
schr = 0;
--p;
nextxcol = xcol + 1;
}
if (c == ' ') {
++xcol;
continue;
}
if (c == '\b') {
--xcol;
continue;
}
while ((col|7)+1 <= xcol) {
col = (col|7) + 1;
putchar( '\t');
}
while (col < xcol) {
++col;
putchar( ' ');
}
while (col > xcol) {
--col;
putchar( '\b');
}
putchar( c);
xcol = ++col;
}
xcol = nextxcol;
}
/*
* throw away output line
* Used when skipping the first pages of input.
*/
nop( )
{
}
/*
* print error message, exit
*/
fatal( arg0)
{
fflush( stdout);
fprintf( stderr, "pr: %r\n", &arg0);
exit (1);
}