forked from briandfoy/PerlPowerTools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpr
executable file
·500 lines (397 loc) · 11.4 KB
/
pr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
#!/usr/bin/perl
=begin metadata
Name: pr
Description: convert text files for printing
Author: Clinton Pierce, clintp@geeksalad.org
License: perl
=end metadata
=cut
#
# pr -- print formatter
#
# Notes and ToDo's:
# There are probably bugs in the option processing.
# -e and -i are not implemented. I didn't understand the description,
# didn't see the point, and was tired of looking at it.
# Some implementations of pr(1) (notably AIX's) have a column-smart folding
# built in. Not implemented here, but would be nice.
#
# The version I tested against seemed to adjust page-length automagically
# according to screen-length if output was to a tty. Cute and usable, but
# not implemented.
# The BSD manual says that input formfeed characters cause a page-feed.
# I could not reliably get this to happen with the BSD code to test,
# so it's left unimplemented, but it shouldn't be hard--I just didn't
# know what it should look like.
#
use strict;
use File::Basename qw(basename);
use FileHandle;
use constant EX_SUCCESS => 0;
use constant EX_FAILURE => 1;
my $Program = basename($0);
my $length=66; # Total page length (Int)
my $trailer=1; # 5 lines of header; 5 lines of trailer (Bool)
my $multimerge=0; # side-by-side files (Bool)
my $columns=1; # number of columns (Int)
my $pagewidth=0; # Default page width; to start (Int)
my $offsetspaces=0; # chars at beginning of line (Char/Bool)
my $doublespace=0; # whether to double space
my $number=0; # number the lines; how high? (Int/Bool)
my $startpageno=1; # starting page no
my $header; # optional header text (String/Bool)
my $formfeed=0; # Use formfeeds instead of spaces. (Bool)
my $quietskip=0; # Ignore unopened files (Bool)
my $column_sep=""; # specified column separator (Bool/Char)
my $roundrobin=0; # across not down. STR (Bool)
# Constants that are useful
my $trailerlength=5;
my $numberchar="\t";
my $curfile="";
my(@FINFO, @COLINFO);
#
# Process the arguments by hand because of pre-getopt nonsense like "-2",
# "-s-" and "-n+5". Grr...
#
OPTION:
while (@ARGV && $ARGV[0] =~ /^-(.+)/ && (shift, ($_ = $1), 1)) {
next OPTION unless length;
last if ($_ eq '-');
# Lousy options
if (s/^[ei]//) {
shift; # Skipped, accepted for comptability
redo OPTION;
}
if (s/^s(.)//) {
$column_sep=$1;
redo OPTION;
}
if (s/^n(\D)?(\d+)//) {
$number=$2;
$numberchar=$1;
$numberchar="\t" if (! $numberchar);
redo OPTION;
}
# Simple flags
if (s/^m//) { warn "-m flag already set" if $multimerge++; redo OPTION; }
if (s/^s//) { warn "-a flag already set" if $roundrobin++; redo OPTION; }
if (s/^d//) { warn "-d flag already set" if $doublespace++; redo OPTION; }
if (s/(^F)//i) { warn "-$1 flag already set" if $formfeed++; redo OPTION; }
if (s/^r//) { warn "-r flag already set" if $quietskip++; redo OPTION; }
if (s/^t//) {
warn "-t flag already set" unless ( $trailer);
$trailer=0;
redo OPTION;
}
# normal "-opt value" arguments
if (s/^w//) {
$pagewidth=shift;
redo OPTION;
}
if (s/^o//) {
warn "-o option already used" if $offsetspaces;
$offsetspaces = checknum(shift);
redo OPTION;
}
if (s/^l//) {
$length=shift;
redo OPTION;
}
if (s/^h//) {
warn "-h option already used" if defined $header;
$header=shift;
redo OPTION;
}
# Accept -2, -3, etc...
if (s/\A([0-9]+)//) {
$columns = $1;
if ($columns == 0) {
warn "$Program: invalid number of columns: $columns\n";
exit EX_FAILURE;
}
redo OPTION;
}
usage("unexpected option: -$_");
}
# One more option
if (@ARGV and $ARGV[0]=~/^\+(\d+)/) {
$startpageno=$1;
shift @ARGV;
}
if (! $column_sep) {
$pagewidth=72;
} else {
$pagewidth=512;
}
#
# Initialize file and column structures
#
my $pageno=$startpageno;
foreach(1..$columns) {
push(@COLINFO, &create_col);
}
if (scalar(@ARGV) == 0) {
@ARGV = ('-');
}
foreach my $file (@ARGV) {
my $fh;
if ($file eq '-') {
$fh = *STDIN;
} else {
if (-d $file) {
warn "$Program: '$file' is a directory\n";
exit EX_FAILURE;
}
$fh = FileHandle->new($file, 'r');
if (! $fh) {
next if ($quietskip);
warn "$Program: Can't open '$file': $!\n";
exit EX_FAILURE;
}
}
push(@FINFO, { name => $file,
handle=> $fh,
lineno=> 0,
});
}
#
# MAIN
#
if ($roundrobin) { # Across the columns fill, pagebreak on EOF or end
foreach my $fstruct (@FINFO) {
NEXTREAD: while(! $$fstruct{handle}->eof) {
foreach my $col (@COLINFO) {
if (! &fill_column_1($col, $fstruct)) {
&printpage;
next NEXTREAD;
}
}
}
&printpage;
}
} elsif ($multimerge) { # Down the columns, one file per column
while (&stillhavefiles) {
my $i=0;
foreach my $col (@COLINFO) {
my $fstruct=$FINFO[$i];
if (! $$fstruct{handle}->eof) {
fill_column($col, $fstruct);
}
$i++;
}
&printpage;
}
} else { # Down the columns fill, pagebreak on EOF or end.
NEXTFILE: foreach my $fstruct (@FINFO) {
while(!$$fstruct{handle}->eof) {
foreach my $col (@COLINFO) {
if (! &fill_column($col, $fstruct)) {
&printpage;
next NEXTFILE;
}
}
&printpage;
}
}
}
exit EX_SUCCESS;
sub usage {
print STDERR @_, "\n";
print STDERR <<USAGE;
usage: $Program [-columns] [+page] [-adFfrts] [-n[char][count]] [-schar] [-ei] [-w width]
[-o count] [-l length] [-h text] files
$Program -m [+page] [-adFfrts] [-n[char][count]] [-schar] [-ei] [-w width]
[-o count] [-l length] [-h text] files
USAGE
exit EX_FAILURE;
}
sub checknum {
my $n = shift;
if (length($n) == 0 || $n !~ m/\A[0-9]+\Z/) {
warn "$Program: invalid number: '$n'\n";
exit EX_FAILURE;
}
return int($n);
}
sub create_col {
my $pagelen=$length-($trailerlength*2);
if ($pagelen <= 0) {
$trailer = 0;
$pagelen = 1;
}
if($doublespace) {
$pagelen=($pagelen%2 == 0)?$pagelen/2:int($pagelen/2)+1;
}
return({ height => $pagelen,
oheight=> $pagelen,
cfile =>"",
text => [],
});
}
sub stillhavefiles {
my $eof=0;
foreach my $fstruct (@FINFO) {
if (! $$fstruct{handle}->eof) {
$eof++;
}
}
return $eof;
}
sub fill_column {
my($col,$fstruct)=@_; # Column structure, and a filehandle
while( $$col{height} ){
if (! $$fstruct{handle}->eof) {
fill_column_1($col, $fstruct);
} else {
return;
}
}
return 1;
}
sub fill_column_1 {
my($col, $fstruct)=@_;
return if (! $$col{height});
my $foo=$$fstruct{handle}; # Couldn't read it directly. Syntax?
my $line=<$foo>;
chomp $line;
# BSD Manual LIES. Formfeeds are treated strangely, but experimentation
# shows they don't do much. At least in /usr/xpgs/bin/pr under Solaris,
# which claims to be POSIX, which should work the same as BSD, no?.
$line=~s/\f//g;
$$col{cfile}=$$fstruct{name}; # In multi-merge, this is useless.
push(@{$$col{text}}, { text => $line, lineno => ++$$fstruct{lineno} });
$$col{height}--;
return 1;
}
sub print_header {
my($col)=@_; # the current column.
return if (!$trailer);
print "\n\n";
print ' ' x $offsetspaces if $offsetspaces;
print scalar(localtime), " ";
if (defined $header) {
print "$header ";
} else {
if (! $multimerge) {
if ($curfile ne $$col{cfile}) {
$pageno=$startpageno;
$curfile=$$col{cfile};
}
print $$col{cfile}, " ";
}
}
print "Page ", $pageno++, "\n\n\n";
}
sub print_footer {
return if (!$trailer);
if ($formfeed) {print chr(12); } else { print "\n"x5;}
}
#
# Most of the horizontal output-options are exercised here.
#
sub printpage {
# option -o does not factor here.
my $colwidth=$pagewidth/scalar(@COLINFO);
if ($number) {
$colwidth-=(length($numberchar)+$number);
}
print_header($COLINFO[0]);
foreach my $line (1..$COLINFO[0]{oheight}) {
print ' ' x $offsetspaces if $offsetspaces;
foreach my $column (@COLINFO) {
my $pfmt;
my $numbering="";
next if (! exists $$column{text}[$line-1]{lineno});
if ($number) {
$pfmt="%${number}s";
$numbering=sprintf("$pfmt", $$column{text}[$line-1]{lineno});
# Truncate off left hand side
$numbering=substr($numbering, length($numbering)-$number, $number);
}
print $numbering;
print $numberchar if ($number);
if (!$column_sep && $trailer) {
$pfmt="%-${colwidth}s";
printf("$pfmt", $$column{text}[$line-1]{text});
} else {
print $$column{text}[$line-1]{text}, $column_sep;
}
}
print "\n"x($doublespace+1);
}
print_footer;
# Re-create blank page.
@COLINFO=();
foreach(1..$columns) { push(@COLINFO, &create_col); }
}
__END__
=pod
=head1 NAME
pr - convert text files for printing
=head1 SYNOPSIS
C<pr [-columns] [+page] [-adFfrts] [-n[char][count]] [-schar] [-ei] [-w width]>
C<[-o count] [-l length] [-h text] files>
C<pr -m [+page] [-adFfrts] [-n[char][count]] [-schar] [-ei] [-w width]>
C<[-o count] [-l length] [-h text] files>
=head1 DESCRIPTION
I<pr> formats text into fixed-length pages with headers, multiple columns and
other options suitable for hardcopy output.
=head2 OPTIONS
I<pr> accepts the following option:
=over 4
=item -columns
Causes I<pr> to print text in columns down the page. Note that I<pr> does not attempt
to fold lines which are too long to fit in their column and long lines and control characters
will affect output. Column width decreases as the number of columns goes up. I<-w>
should be used to make the page wider.
=item +page
Begin numbering pages at page number I<page>.
=item -a
Multiple columns are filled from left-to-right down the page.
=item -m
Each input file is given a column in the output, and printing continues until
all input files are exhausted.
=item -d
Output is double-spaced
=item -f
Formfeed characters are used instead of trailing blank lines to control
page length. I<-F> is a synonym for I<-f>.
=item -r
Do not print a message if files cannot be opened
=item -t
Suppress the 5-line header and 5-line trailer on each page
=item -sB<char>
The single character B<char> will be used to separate multi-column output.
Note that the columns are no-longer fixed-width with this option.
=item -n[B<char>[B<count>]]
Precede each column with a line number. B<count> is the desired width of
the line numbering (numbers too large are truncated). B<char> can be used to
separate numbers from the output line, tab is used if B<char> is not specified.
=item -e
Not implemented in this version, accepted for comptability.
=item -i
Not implemented in this version, accepted for comptability.
=item -w B<width>
Width of the page, in characters. Note that excessively long lines are not folded or
truncated--they simply keep printing. This may foul column alignment. Defaults to 72.
=item -l B<length>
Length of the page, in lines. Defaults to 66.
=item -o B<count>
Offset each line with a left margin B<count> characters wide. This is in addition to
the width specified with the I<-w> option.
=item -h B<text>
Use B<text> in the header for each file, instead of the file name
=back
=head1 BUGS
The I<-e> and I<-i> switches are not implemented in this version.
Input which contains carriage returns, or other ASCII control characters will
affect the alignment of columns, and may affect page-length counts. This includes
overprinting and backspaces.
Excessively long lines will cause I<pr> to mis-align columns in multi-column
output.
=head1 AUTHOR
The Perl implementation of I<pr> was written by Clinton Pierce, I<clintp@geeksalad.org>.
=head1 COPYRIGHT and LICENSE
This program is Copyright 1999, by Clinton Pierce.
Freely redistributable under the Perl Artistic License.
=cut