############################################################################ # # File: iprint.icn # # Subject: Program to print Icon program # # Author: Robert J. Alexander # # Date: March 26, 2002 # ############################################################################ # # This file is in the public domain. # ############################################################################ # # The defaults are set up for printing of Icon programs, but # through command line options it can be set up to print programs # in other languages, too (such as C). This program has several # features: # # If a program is written in a consistent style, this program # will attempt to keep whole procedures on the same page. The # default is to identify the end of a print group (i.e. a pro- # cedure) by looking for the string "end" at the beginning of a # line. Through the -g option, alternative strings can be used to # signal end of a group. Using "end" as the group delimiter # (inclusive), comments and declarations prior to the procedure are # grouped with the procedure. Specifying a null group delimiter # string (-g '') suppresses grouping. # # Page creases are skipped over, and form-feeds (^L) embedded in # the file are handled properly. (Form-feeds are treated as spaces # by many C compilers, and signal page ejects in a listing). Page # headings (file name, date, time, page number) are normally # printed unless suppressed by the -h option. # # Options: # # -n number lines. # # -pN page length: number of lines per page (default: 60 # lines). # # -tN tab stop spacing (default: 8). # # -h suppress page headings. # # -l add three lines at top of each page for laser printer. # # -gS end of group string (default: "end"). # # -cS start of comment string (default: "#"). # # -xS end of comment string (default: none). # # -i ignore FF at start of line. # # Any number of file names specified will be printed, each # starting on a new page. # # For example, to print C source files such as the Icon source # code, use the following options: # # iprint -g ' }' -c '/*' -x '*/' file ... # # Control lines: # # Control lines are special character strings that occur at the # beginnings of lines that signal special action. Control lines # begin with the start of comment string (see options). The control # lines currently recognized are: # # eject -- page eject (line containing "eject" # does not print). # # title -- define a title line to print at top # of each page. Title text is separated from the title control string by one space and is terminated by # or end of line, whichever comes first. # # subtitle -- define a sub-title line to print # at top of each page. Format is parallel to the "title" control # line, above. # # If a page eject is forced by maximum lines per page being # exceeded (rather than intentional eject via control line, ff, or # grouping), printing of blank lines at the top of the new page is # suppressed. Line numbers will still be printed correctly. # ############################################################################ global pagelines,tabsize,lines,page,datetime,title,subtitle,pagestatus,blanks, group,numbers,noheaders,hstuff,gpat,comment,comment_end,laser, ignore_ff procedure main(arg) local files,x &dateline ? {tab(find(",")) ; move(2) ; datetime := tab(0)} files := [] pagelines := 60 tabsize := 8 gpat := "end" comment := "#" while x := get(arg) do { if match("-",x) then { # Arg is an option case x[2] of { "n": numbers := "yes" "p": { pagelines := ("" ~== x[3:0]) | get(arg) if not (pagelines := integer(pagelines)) then stop("Invalid -p parameter: ",pagelines) } "t": { tabsize := ("" ~== x[3:0]) | get(arg) if not (tabsize := integer(tabsize)) then stop("Invalid -t parameter: ",tabsize) } "h": noheaders := "yes" "l": laser := "yes" "g": { gpat := ("" ~== x[3:0]) | get(arg) } "c": { comment := ("" ~== x[3:0]) | get(arg) } "x": { comment_end := ("" ~== x[3:0]) | get(arg) } "i": ignore_ff := "yes" default: stop("Invalid option ",x) } } else put(files,x) } if *files = 0 then stop("usage: iprint -options file ...\n_ options:\n_ \t-n\tnumber the lines\n_ \t-p N\tspecify lines per page (default 60)\n_ \t-t N\tspecify tab width (default 8)\n_ \t-h\tsuppress page headers\n_ \t-l\tadd 3 blank lines at top of each page\n_ \t-g S\tpattern for last line in group\n_ \t-c S\t'start of comment' string\n_ \t-x S\t'end of comment' string\n_ \t-i\tignore FF") every x := !files do expand(x) end procedure expand(fn) local f,line,cmd,linenbr,fname f := open(fn) | stop("Can't open ",fn) fn ? { while tab(find("/")) & move(1) fname := tab(0) } hstuff := fname || " " || datetime || " page " title := subtitle := &null lines := pagelines page := 0 ; linenbr := 0 group := [] while line := trim(read(f)) do { if \ignore_ff then while match("\f",line) do line[1] := "" linenbr +:= 1 if match("\f",line) then { dumpgroup() lines := pagelines repeat { line[1] := "" if not match("\f",line) then break } } line ? { if =comment & cmd := =("eject" | "title" | "subtitle") then { dumpgroup() case cmd of { # Command line "title": (move(1) & title := trim(tab(find(comment_end)))) | (title := &null) "subtitle": (move(1) & subtitle := trim(tab(find(comment_end)))) | (subtitle := &null) } lines := pagelines } else { # Ordinary (non-command) line if not (*group = 0 & *line = 0) then { put(group,line) if \numbers then put(group,linenbr) } if endgroup(line) then dumpgroup() } } } dumpgroup() close(f) lines := pagelines end procedure dumpgroup() local line,linenbr if *group > 0 then { if lines + *group / ((\numbers & 2) | 1) + 2 >= pagelines then lines := pagelines else {write("\n") ; lines +:= 2} while line := get(group) do { if \numbers then linenbr := get(group) if lines >= pagelines then { printhead() } if *line = 0 then { if pagestatus ~== "empty" then {blanks +:= 1 ; lines +:= 1} next } every 1 to blanks do write() blanks := 0 pagestatus := "not empty" if \numbers then writes(right(linenbr,5)," ") write(detab(line)) lines +:= 1 } } return end procedure endgroup(s) return match("" ~== gpat,s) end procedure printhead() static ff,pg writes(ff) ; ff := "\f" lines := 0 pg := string(page +:= 1) if /noheaders then { if \laser then write("\n\n") write(left(\title | "",79 - *hstuff - *pg),hstuff,pg) lines +:= 2 write(\subtitle) & lines +:= 1 write() } pagestatus := "empty" blanks := 0 return end procedure detab(s) local t t := "" s ? { while t ||:= tab(find("\t")) do { t ||:= repl(" ",tabsize - *t % tabsize) move(1) } t ||:= tab(0) } return t end