#!/bin/sh # /usr/local/bin/lpf_vsl by Julian Stacey # Installed from ~jhs/public_html/src/bsd/jhs/bin/local/lpf_vsl/lpf_vsl # See also: /usr/share/doc/handbook/printing.{html,latin1} # Alternative I haven't looked at yet JJLATER: /usr/ports/print/apsfilter & # Bug: # An ascii file with 2 blank lines, then all indented 3 spaces, gets: # "unrecognised format" mail # Also a calendar file beinning with /* British calendar .. */ does too. # # Bug: date|lpr in /var/log/lpd-errs produces 2 x "[: Tue: unexpected operator" # I presume it''s the input format detector going wrong. # I never wrote it to work on one line files. # I don''t care. # This filter also blows on some long lines eg .tiff # & some blows on files with just 1 char (#) in 1st line eg /etc/ttys. log=/var/log/lpf_vsl dbg=echo $dbg " " >> $log $dbg "`date`: lpf_vsl start" >> $log tmp=/var/spool/output/lpd.lpf_vsl/$$.tmp # $$ is PID # Destroy security for now. JJLATER fix to 775 or 770. xs chmod 777 /var/spool/output/lpd.lpf_vsl $dbg "tmp = $tmp" >> $log # Keep $tmp same as in /etc/printcap # chmod 755 `dirname $tmp` ; chown daemon:daemon `dirname $tmp` # man printcap: # The if filter is invoked with arguments: # if [-c] -wwidth -llength -iindent -n login -h host acct-file # I just ignore arguments delivered here for now. # JJLATER If the "-h host" component has a bjc in it, generate bjc not pcl. gs="/usr/local/bin/gs -sPAPERSIZE=a4 -dFIXEDMEDIA -dNOPAUSE -q -sDEVICE=ljet3 -sOutputFile=- -dBATCH -dSAFER -" # PDF Processing: gs now accepts PDF as input pdfin="$gs" $dbg "pdfin = $pdfin" >> $log # Previously, I used to try to convert PDF to PS with this: # pdf2ps="/usr/local/bin/pdf2ps -sPAPERSIZE=a4" # Avoid pdf2ps error "exec: gs: not found" in /var/log/lpd-errs # PATH=:/bin:/usr/bin:/usr/local/bin ; export PATH # Although man pdf2ps says # pdf2ps [ options ] input.pdf [output.ps] # The second parameter is not actually optional, defaulting to pipe if # absent, it's just: `basename "$1" \.pdf`.ps # From /usr/local/bin/pdf2ps: -c save pop -f "$1" # Suppress Inter Field Seperator so it will not swallow the leading # white space on the first line of plain text input, between left # margin & first printable character. # IFS= # Read first characters of first line, to determine file type. # This syntax was not good enough: # read first_line # because a PCL file contains nulls \0 (a .tiff too). # None of the shell variables & other unix commands (EG echo printf etc) # tools I've tried cope with it, so to avoid writing a C program, I swallow # the first line whole & unchanged with head. # I lost some PCL data with this: # head -1 > $tmp # first_line=`cat $tmp` # So now I swallow whole file (tough if its real big, this proc. wont # work as a pipe. cat > $tmp yyy=`file -b --dereference $tmp` $dbg "`date`: Format (from file command): $yyy" >> $log ok="file command says OK it is" if [ "$yyy" = "JPEG image data" ]; then # { $dbg "Format (from file command): JPG" >> $log # .jpg data as examined from cameras: # Frank: # 377 330 377 341 u 344 E x i f \0 \0 I I * \0 # 377 330 377 341 w 327 E x i f \0 \0 I I * \0 # 377 330 377 341 p 371 E x i f \0 \0 I I * \0 # Julian: # 377 330 377 341 023 p E x i f \0 \0 I I * \0 # 377 330 377 341 023 . E x i f \0 \0 I I * \0 # 377 330 377 341 023 D E x i f \0 \0 I I * \0 # Notice that "I I * \0" as at start of tiff. /usr/local/bin/jpegtopnm < $tmp | /usr/local/bin/pnmtops \ | $gs && rm $tmp && exit 0 # Alternate filter not tried: # /usr/local/bin/djpeg -pnm < $tmp | \ # /usr/local/bin/pnmtotiff > $tmp.tmp # /usr/local/bin/tiff2ps -a $tmp.tmp | \ # $gs && rm $tmp $tmp.tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ "$yyy" = "TIFF image data, little-endian" ]; then # { # JJLATER add code to allow for big endian. $dbg "Format (from file command): TIFF" >> $log # From eg http://www.berklix.com/scanjet/ /usr/local/bin/tiff2ps -a $tmp | $gs && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } else #{ $dbg "Format testing (with file command) for ASCII" >> $log # Hope its not a binary that will consume masses of paper. # Plain text: print a form at the end to eject the last page. # EscE: Initialise printer to default state with EscE, # in case last use has left it in a mangled state # EG host computer might have died in mid print. # EscE: Initialise again for good measure: # maybe the printer might not recognise first Reset if it # gets swallowed into an embedded sequence, or perhaps the # printer is on a flakey dynamic multiplexer, & the first # couple of characters get lost, while the multiplexer is # switching the printer to a different host computer. # Unlike with a hayes escape, a pcl scape does not seem to # require a quiet period afterward, according to the big # HP PCL manual. # Esc&k2G: Tell printer to treat LF as CR+LF. # EscE Reset to default for next user (that on a dynamic # multiplexer may not even be Unix+lpd ) # A printer reset also has the side effect of causing # the printer to flush the last partial page (if any), whereas # If we sent a FF instead, we could end up printing an # extra blank page, if either { (A) the input stream of data had # terminated with a page aligning FF, or (B) the last line feed # happened to align the printer to end of page. } # File reports various sorts of Ascii, eg: # cd /etc ; file -b * | grep -i ascii | sort | uniq # ASCII C++ program text # ASCII English text # ASCII make commands text # ASCII text # See also: man file & /usr/share/misc/magic if [ "$yyy" = "ASCII text" ]; then # { $dbg "$ok $yyy" >> $log ; printf "\033E\033&k2G" && cat $tmp && \ printf "\033E" && rm $tmp && exit 0 # } elif [ "$yyy" = "ASCII English text" ]; then #{ $dbg "$ok $yyy" >> $log ; printf "\033E\033&k2G" && cat $tmp && \ printf "\033E" && rm $tmp && exit 0 # } elif [ "$yyy" = "ASCII make commands text" ]; then #{ $dbg "$ok $yyy" >> $log ; printf "\033E\033&k2G" && cat $tmp && \ printf "\033E" && rm $tmp && exit 0 # } elif [ "$yyy" = "ASCII C++ program text" ]; then #{ $dbg "$ok $yyy" >> $log ; printf "\033E\033&k2G" && cat $tmp && \ printf "\033E" && rm $tmp && exit 0 # } # In a mail Removing Received: lines changes file report # From "ASCII mail text" # To: "ASCII HTML document text" OR "ASCII text" if a non MIME # so JJLATER trap "ASCII mail text". # I could also add Many other strings such as # "POSIX shell script text executable" # "troff or preprocessor input text" # But troff should be formatted first anyway. # I could extend to use a swith as below, # but I suspect below broke cos of spaces in strings # switch ( "$yyy") #{ # case "ASCII text": # case "ASCII English text": # case "ASCII make commands text": # case "ASCII C++ program text": # $dbg "Format OK to print" >> $log # breaksw # case default: # $dbg "Format not ASCII so skipped" >> $log # breaksw # endsw #} else # { $dbg "No more file command Ascii tests, end of test with file command." >> $log fi # } fi # } # Some .tif, the the 1st line from head -1 can be 360K long. # So something below here breaks, but hopefully .tiff will be caugh above. $dbg "Start of own ($0) analysis of first line." >> $log first_line=`head -1 $tmp` # Restore Inter Field Seperator to space tab nl # IFS="\040\011\012" first_two_chars=`expr "$first_line" : '\(..\)'` first_four_chars=`expr "$first_line" : '\(....\)'` first_six_chars=`expr "$first_line" : '\(......\)'` # If the input text is # /* British calendar # The output is: # + expr '/* British calendar ' : '\(...\)' # + [ /Distfile /FIRE /ad4s1 /ad4s2 /ad4s3 /ad4s4 /ad4s4dcrypt \ # /ad6s1 /ad6s2 /ad6s3 /ad6s4 /av /bin /boot /cdrom /cdrw \ # /chroot /compat /dev /devfs /devusb /dvd /dvdw /entropy \ # /etc /home /host /lib /libexec /media /mnt /opt /pri \ # /proc /pub /rescue /root if [ "$first_four_chars" = "%!PS" ]; then # { $dbg "Format (from own ($0) analysis): POSTSCRIPT from GROFF" >> $log # PostScript: use Ghostscript to convert and print it # groff generated .ps files start: "%!PS-Adobe-3.0" # I do not need to emit $first_line for gs, as I tested ghostview # without first line, & it displayed OK, & ghostview uses gs internaly. # Example: # %!PS-Adobe-3.0 # %%Creator: Applixware $gs $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ "$first_four_chars" = "%!ps" ]; then # { $dbg "Format (from own ($0) analysis): POSTSCRIPT not from GROFF" >> $log $gs $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(...\)'` = '.\"' ]; then # { $dbg "Format (from own ($0) analysis): GROFF" >> $log # Note the backslash while needed in the .rof file, used to be # discarded by # read first_line # before I received it here, however now using: # cat > $tmp ; first_line=`head -1 $tmp` # it is there. cat $tmp | groff -U -b -s -t -Tps -dformat=ps -dumlauts=p | \ $gs && rm $tmp && exit 0 # I could instead generate pcl direct from groff. $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.2' ]; then # { $dbg "Format (from own ($0) analysis): PDF-1.2" >> $log # from ps2pdf, groff, external $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.3' ]; then # { $dbg "Format (from own ($0) analysis): PDF-1.3" >> $log # From external # From Phil Walters I received %PDF-1.3 %âãÏÓ $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.4' ]; then # { $dbg "Format (from own ($0) analysis): PDF-1.4" >> $log # From external # From Companies House change of address form # %PDF-1.4?%?????? # 2544232302EECD00 # 5046D1E4D523F3DA # 0D=Carriage Return # From GEA Prog 2008_04.pdf # %PDF-1.4? # 254423230 # 5046D1E4A # 0A=New Line $pdfin $tmp && rm $tmp && exit 0 # GNU Ghostscript 7.07: Unrecoverable error, exit code 1 # Segmentation fault # However xpdf can display it, & can export it to a .ps file # which this can print. So I discovered this works OK generating .ps # pdftops $tmp - > t.ps # So merge the 2: # /usr/local/bin/pdftops $tmp - | $pdfin && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.5' ]; then # { $dbg "Format (from own ($0) analysis): PDF-1.5" >> $log # Never seen a 1.5, but presumably they exist. /usr/local/bin/pdftops $tmp - | $pdfin && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(........\)'` = '%PDF-1.6' ]; then # { $dbg "Format (from own ($0) analysis): PDF-1.6" >> $log # from mk@, scanner@msd. /usr/local/bin/pdftops $tmp - | $pdfin && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(....\)'` = '%PDF' ]; then # { $dbg "Format (from own ($0) analysis): PDF Unknown Version" >> $log # Not from ps2pdf, upper case $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(....\)'` = '%pdf' ]; then # { $dbg "Format (from own ($0) analysis): pdf unknown version" >> $log # not from ps2pdf, lower case $pdfin $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ $first_two_chars = '\033E' ]; then # { ( echo "PCL detected by /usr/local/bin/lpf_vsl" | mail -s "PCL from lpf_vsl" jhs ) $dbg "Format (from own ($0) analysis): PCL" >> $log # Escape E # HP/PCL: The big HP PCL 5 book recomends all PCL files should # start with this, & ghostscript generates it, so it's a safe bet. # Example: "^[E^[&l26A" from gs # I hope data in first line is not longer than $first_line can read ! # Some PCL files go berserk, & the whole damn file has no \n, # or just one at end. cat $tmp && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ `expr "$first_line" : '\(...\)'` = 'II*' ]; then # { $dbg "Format (from own ($0) analysis): TIFF" >> $log # From eg http://www.berklix.com/scanjet/ /usr/local/bin/tiff2ps -a $tmp | $gs && rm $tmp && exit 0 # The match pattern should have a null (shows in vi as ^@), # ie 'II*\0' # but I haven't got the elif to match on that. $dbg "Format has failed" >> $log exit 1 # } elif [ $first_six_chars = 'GIF87a' ]; then # { # from XV as frame grabber on FreeBSD-7.1-RELEASE $dbg "Format (from own ($0) analysis): GIF" >> $log /usr/local/bin/gif2ps < $tmp | $gs && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ $first_six_chars = 'GIF89a' ]; then # { $dbg "Format (from own ($0) analysis): GIF" >> $log /usr/local/bin/gif2ps < $tmp | $gs && rm $tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } elif [ "`echo \"$first_line\" | file -b -`" = "JPEG image data" ]; then # { # .jpg data as examined from cameras: # Frank: # 377 330 377 341 u 344 E x i f \0 \0 I I * \0 # 377 330 377 341 w 327 E x i f \0 \0 I I * \0 # 377 330 377 341 p 371 E x i f \0 \0 I I * \0 # Julian: # 377 330 377 341 023 p E x i f \0 \0 I I * \0 # 377 330 377 341 023 . E x i f \0 \0 I I * \0 # 377 330 377 341 023 D E x i f \0 \0 I I * \0 # Notice that "I I * \0" as at start of tiff. $dbg "Format (from own ($0) analysis): JPG" >> $log /usr/local/bin/jpegtopnm < $tmp | /usr/local/bin/pnmtops \ | $gs && rm $tmp && exit 0 # Alternate filter not tried: # /usr/local/bin/djpeg -pnm < $tmp | \ # /usr/local/bin/pnmtotiff > $tmp.tmp # /usr/local/bin/tiff2ps -a $tmp.tmp | \ # $gs && rm $tmp $tmp.tmp && exit 0 $dbg "Format has failed" >> $log exit 1 # } fi # } # About Trailing Form Feed: JJLATER # - The trailing Reset (\033E) Form Feed = New Page =Control L = (\014) # should be selective, sampling data at end of file, but it's not (yet), # - Printers can be set up to also require FF or not, # generatin their own instead. If both do FF you get a wasted page. # - If neither do it, the job doesn't print till next job, or until you manually # send an empty page. # - My HP is set up to generate its own trailing NP=FF # - My Brother is set up to want an FF from computer. # - Applixware.pcl does not have a terminating FF. # - Groff.pcl produced by groff -U -b -s -t -Tlj4 -dformat=lj4 # thing.rof > thing.pcl ) Have a terminating ^L^[E # - .pcl made by groff -U -b -s -t -Tlj4 -dformat=lj4 thing.rof > thing.pcl # Ends with ^L^[E (& has bits of words in, not pure bitmap), for CV ~55K. # - .pcl made by gs -sDEVICE=ljet3 -sOutputFile=thing.pcl -- thing.ps # also ends in ^L^[E (but seems to have no strings, just bit map, & is # much larger, for my CV ~650K) # JJLATER add a trap to rm $tmp # Should never get this far. echo "`date`: /usr/local/bin/lpf_vsl failure unrecognised format $tmp" | mail -s "lpf_vsl failure" root echo "`date`: /usr/local/bin/lpf_vsl failure unrecognised format $tmp" >> $log exit 2 # only on error. # /usr/share/doc/handbook/handbook93.html: # The text filter, confusingly called the input filter in LPD documentation, # handles regular text printing. Think of it as the default filter. LPD expects # every printer to be able to print plain text by default, and it is the text # filter's job to make sure backspaces, tabs, or other special characters do not # confuse the printer. If you are in an environment where you have to # account for printer usage, the text filter must also account for pages # printed, usually by counting the number of lines printed and comparing that # to the number of lines per page the printer supports. The text filter is # started with the following argument list: # [-c] -wwidth -llength -iindent -n login -h host acct-file