--- /dev/null
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+\f
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+\f
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+\f
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+\f
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+\f
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) 19yy <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) 19yy name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
--- /dev/null
+# Makefile for efax
+
+# Change the following to the name of your ANSI C compiler
+# (normally gcc).
+
+CC=gcc
+
+# Compile/load options. Add -DNO_STRERROR to CFLAGS if _strerror
+# is undefined
+
+CFLAGS=
+LDFLAGS=
+
+# Change the following to the destination directories for
+# binaries and man pages. Probably /usr/bin and /usr/man on
+# Linux, /usr/local/{bin,man} on other systems.
+
+BINDIR=/usr/bin
+MANDIR=/usr/man
+
+.c.o:
+ $(CC) $(CFLAGS) -c $<
+
+all: efax efix
+
+efax: efax.o efaxlib.o efaxio.o efaxos.o efaxmsg.o
+ $(CC) -o efax $(LDFLAGS) efax.o efaxlib.o efaxio.o efaxos.o efaxmsg.o
+ strip efax
+
+efix: efix.o efaxlib.o efaxmsg.o
+ $(CC) -o efix $(LDFLAGS) efix.o efaxlib.o efaxmsg.o
+ strip efix
+
+install:
+ cp fax efax efix $(BINDIR)
+ cp fax.1 efax.1 efix.1 $(MANDIR)/man1
+
+clean:
+ rm -f efax efix efax.o efix.o efaxlib.o efaxio.o efaxos.o efaxmsg.o
+
+efax.o: efax.c efaxmsg.h efaxlib.h efaxio.h efaxos.h
+efaxio.o: efaxio.c efaxmsg.h efaxio.h efaxos.h
+efaxos.o: efaxos.c efaxmsg.h efaxlib.h efaxos.h
+efix.o: efix.c efaxmsg.h efaxlib.h
+efaxlib.o: efaxlib.c efaxmsg.h efaxlib.h
+efaxmsg.o: efaxmsg.c efaxmsg.h
--- /dev/null
+
+ Introduction
+
+This is the README file for version 0.9 of efax, a small ANSI
+C/POSIX program that sends and receives faxes using any fax modem
+(Class 1, 2 or 2.0).
+
+efax is smaller and easier to install than HylaFAX or
+mgetty+sendfax. As one user put it ``EFAX is a nice simple
+program for single user systems.''
+
+The ``fax'' command, a shell script, lets you send, receive, view
+and print faxes. In larger systems, faxes can be sent by
+printing to a ``fax'' printer and received faxes can be e-mailed
+as MIME attachments to an administrator for distribution. efax
+can also pass incoming data calls to a getty program.
+
+The efax package includes ``efix,'' a program to convert between
+various image file formats. To fax Postscript files you will
+need Ghostscript. To view faxes you can use any program that
+displays PGM files (e.g. xloadimage or xv). efix can convert
+received files to Postscript or HP Laserjet formats for printing.
+
+This version of the program was written & tested under Linux 2.0.
+Previous versions have been compiled and used under most versions
+of Unix and should work with minor changes on any Unix with an
+ANSI C compiler and libraries that include select(2) and
+termios(4).
+
+efax is distributed as a gzip'ed tar file, efax-0.9.tar.gz. It
+may be obtained by anonymous FTP from metalab.unc.edu in
+/pub/Linux/apps/serialcomm/fax.
+
+ Changes from version 0.8a to version 0.9
+
+- fixed bad (0x0 pixel) file output on new glibc systems
+
+- fixed bad file output on 64-bit systems
+
+- automatic selection of baud rate and class
+
+- hardware flow control made optional
+
+- modernized directory and file names
+
+- many bugs removed, others added
+
+ Manifest
+
+The efax distribution should contain the following files:
+
+README - this file
+COPYING - the GNU General Public License
+Makefile - makefile to make all/install/clean
+efax.c - program to send and receive faxes
+efix.c - program to convert between file formats
+efaxmsg.{h,c} - functions to print errors, warnings, etc
+efaxlib.{h,c} - functions common to efax and efix
+efaxio.{h,c} - low-level modem i/o functions
+efaxos.{h,c} - OS-dependent functions
+fax.1 - man page for fax(1)
+efax.1 - man page for efax(1)
+efix.1 - man page for efix(1)
+fax - a (Bourne) shell script to create, send,
+ receive, view and print faxes.
+
+ Installation
+
+Edit the makefile and change the compile command and destination
+directories if required.
+
+Type "make" to compile the efax and efix binaries.
+
+Edit the configuration information at the beginning of the
+``fax'' script according to the comments.
+
+Type "make install" to install the fax script, the binaries and
+the man pages.
+
+Read the fax(1) man page first for information on using efax.
+
+The efax(1) man page has details on resolving problems, setting
+up a network fax server and using efax to handle both fax and
+data calls.
--- /dev/null
+.TH EFAX 1 "February 1999" "" ""
+.UC 1
+.SH NAME
+efax \- send/receive faxes with Class 1, 2 or 2.0 fax modem
+
+.ce 1
+(Please read the \fBfax\fP man page first.)
+.SH SYNOPSIS
+
+.B efax
+[
+\fIoptions\fP
+]
+[
+\fB-t\fP \fInum\fP [ \fIfile\fP... ]
+]
+
+.SH OPTIONS
+Where \fIoptions\fP are:
+
+.TP 9
+.B -a \fIcmd\fP
+use the command \fBATcmd\fP when answering the phone. The
+default is "A".
+
+.TP 9
+.B -c \fIcaps\fP
+set the local modem capabilities. See the section on
+capabilities below for the format and meaning of \fIcaps\fP. For
+Class 1 the default is 1,n,0,2,0,0,0,0 where n is the highest
+speed supported by the modem. For Class 2 the default is
+determined by the modem.
+
+.TP 9
+.B -d \fIdev\fP
+use the fax modem connected to device \fIdev\fP. The default is
+\fB/dev/modem\fP.
+
+.TP 9
+.B -f \fIfnt\fP
+use font file \fIfnt\fP for generating the header. The default
+is a built-in 8x16 font. See the efix(1) -f option for the font
+file format.
+
+.TP 9
+.B -g \fIcmd\fP
+if a \fBCONNECT\fP (or \fBDATA\fP) response indicates a data
+call, the shell \fB/bin/sh\fP is exec(2)'ed with \fIcmd\fP as its
+command. \fIcmd\fP is a printf(3) format that may contain up to
+6 %d escapes which are replaced by the baud rate following the
+most recent \fBCONNECT\fP message. \fIcmd\fP typically exec's
+getty(8).
+
+.TP 9
+.B -h \fIhdr\fP
+put string `hdr' at the top of each page. The first %d in `hdr'
+is replaced by the page number and the second, if any, is
+replaced by the number of pages being sent.
+
+.TP 9
+.B -i \fIstr\fP
+.TP 9
+.B -j \fIstr\fP
+.TP 9
+.B -k \fIstr\fP
+send the command \fBAT\fP\fIstr\fP to the modem to initialize it.
+-i commands are sent before the modem is put into fax mode, -j
+commands after the modem is in fax mode, and -k commands just
+before efax exits. The only default is a hang-up (ATH) command
+that is sent before exiting only if no other -k options are
+given. Multiple options may be used.
+
+.TP 9
+.B -l \fIid\fP
+set the local identification string to \fIid\fP. \fIid\fP should
+be the local telephone number in international format (for
+example "+1 800 555 1212"). This is passed to the remote fax
+machine. Some fax machines may not accept characters other than
+numbers, space, and '+'.
+
+.TP 9
+.B -o \fIopt\fP
+use option \fIopt\fP to accommodate a non-standard fax modem
+protocol. See the MODEM REQUIREMENTS section below for more
+details. The \fIopt\fPions are:
+
+.TP 9
+.B
+ 0
+Force use of Class 2.0 fax modem commands. The modem must
+support Class 2.0.
+
+.TP 9
+.B
+ 2
+Force use of Class 2 fax modem commands. The modem must support
+Class 2.
+
+.TP 9
+.B
+ 1
+Force use of Class 1 fax modem commands. The modem must support
+Class 1. By default efax queries the modem and uses the first of
+the three above classes which is supported by the modem.
+
+.TP 9
+.B
+ a
+use software adaptive answer method. If the first attempt to
+answer the call does not result in a data connection within 8
+seconds the phone is hung up temporarily and answered again in
+fax mode (see "Accepting both fax and data calls" below).
+
+.TP 9
+.B
+ e
+ignore errors in modem initialization commands.
+
+.TP 9
+.B
+ f
+use "virtual flow control". efax tries to estimate the number of
+bytes in the modem's transmit buffer and pauses as necessary to
+avoid filling it. The modem's buffer is assumed to hold at least
+96 bytes. This feature does not work properly with Class 2
+modems that add redundant padding to scan lines. Use this option
+only if you have problems configuring flow control.
+
+.TP 9
+.B
+ h
+use hardware (RTS/CTS) in addition to software (XON/XOFF) flow
+control. Many modems will stop responding if this option is
+used. See the section `Resolving Problems' before using this
+option.
+
+.TP 9
+.B
+ l
+halve the time between testing lock files when waiting for other
+programs to complete. By default this is 8 seconds. For example
+-olll sets the interval to 1 second.
+
+.TP 9
+.B
+ n
+ignore requests for pages to be retransmitted. Use this option if
+you don't care about the quality of the received fax or if the
+receiving machine is too fussy. Otherwise each page may be
+retransmitted up to 3 times.
+
+.TP 9
+.B
+ r
+do not reverse bit order during data reception for Class 2
+modems. Only Multitech modems require this option. Not normally
+required since efax detects these modems.
+
+.TP 9
+.B
+ x
+send XON (DC1) instead of DC2 to start data reception. Applies
+to a very few Class 2 modems only.
+
+.TP 9
+.B
+ z
+delay an additional 100 milliseconds before each modem
+initialization or reset command. The initial delay is 100
+ms. For example, -ozzz produces a 400 ms delay. Use with modems
+that get confused when commands arrive too quickly.
+
+
+.TP 9
+.B -q \fIn\fP
+ask for retransmission of pages received with more than \fIn\fP
+errors. Default is 10.
+
+.TP 9
+.B -r \fIpat\fP
+each received fax page is stored in a separate file. The file
+name is created using \fIpat\fP as a strftime(3) format string.
+A page number of the form .001, .002, ... is appended to the
+file name. If \fIpat\fP is blank ("") or no -r option is given a
+default string of "%m%d%H%M%S" is used.
+
+.\" If a file already exists, efax terminates with an error.
+
+.TP 9
+.B -s
+remove lock file(s) after initializing the modem. This allows
+outgoing calls to proceed when efax is waiting for an incoming
+call. If efax detects modem activity it will attempt to re-lock
+the device. If the modem has been locked by the other program
+efax will exit and return 1 (``busy''). Normally a new efax
+process is then started by init(8). The new efax process will
+then check periodically until the lock file disappears and
+then re-initialize the modem.
+
+.TP 9
+.B -t \fInum [file\fP...]
+dial telephone number \fInum\fP and send the fax image files
+\fIfile\fP.... If used, this must be the last argument on the
+command line. The telephone number \fInum\fP is a string that
+may contain any dial modifiers that the modem supports such as a
+T prefix for tone dialing or commas for delays. If no file names
+are given the remote fax machine will be polled. If no -t
+argument is given efax will answer the phone and attempt to
+receive a fax.
+
+.TP 9
+.B -v \fIstrng\fP
+select types of messages to be printed. Each \fIlower-case\fP
+letter in \fIstrng\fP enables one type of message:
+
+.RS 12
+.B
+e -
+errors
+.br
+.B
+w -
+warnings
+.br
+.B
+i -
+session progress information
+.br
+.B
+n -
+capability negotiation information
+.br
+.B
+c -
+modem (AT) commands and responses
+.br
+.B
+h -
+HDLC frame data (Class 1 only)
+.br
+.B
+m -
+modem output
+.br
+.B
+a -
+program arguments
+.br
+.B
+r -
+reception error details
+.br
+.B
+t -
+transmission details
+.br
+.B
+f -
+image file details
+.br
+.B
+x -
+lock file processing
+
+.RE
+.RS 9
+Up to two -v options may be used. The first is for messages
+printed to the standard error and the second is for messages to
+the standard output. The default is "ewin" to the standard error
+only.
+.RE
+
+.TP 9
+.B -w
+wait for an OK or CONNECT prompt instead of issuing an answer
+(\fBATA\fP) command to receive a fax. Use this option when the
+modem is set to auto-answer (using S0=\fIn\fP) or if another
+program has already answered the call.
+
+.TP 9
+.B -x \fIlkf\fP
+use a UUCP-style lock file \fIlkf\fP to lock the modem device
+before opening it. If the device is locked, efax checks every 15
+seconds until it is free. Up to 16 -x options may be used if
+there are several names for the same device. A `#' prefix on the
+file name creates an binary rather than text (HDB-style) lock
+file. This is the reverse of what was used by previous efax
+versions.
+
+.SH FAX FILE FORMATS
+
+efax can read the same types of files as \fBefix(1)\fP including
+text, T.4 (Group 3), PBM, single- and multi-page TIFF (G3 and
+uncompressed). efax automatically determines the type of file
+from its contents. TIFF files are recommended as they contain
+information about the image size and resolution.
+
+Each page to be sent should be converted to a separate TIFF
+format file with Group 3 (G3) compression. Received files are
+also stored in this format. The EXAMPLES section below shows how
+efix and other programs can be used to create, view and print
+these files.
+
+.SH OPERATING SYSTEM REQUIREMENTS
+
+The operating system must provide short response times to avoid
+protocol timeouts. For Class 2 and 2.0 modems the delay should
+not exceed 1 or 2 seconds.
+
+When using Class 1 modems the program must respond to certain
+events within 55 milliseconds. Longer delays may cause the fax
+protocol to fail in certain places (between DCS and TCF or
+between RTC and MPS). Class 1 modems should therefore not be
+used on systems that cannot guarantee that the program will
+respond to incoming data in less than 55 milliseconds. In
+particular, some intelligent serial cards and terminal servers
+may introduce enough delay to cause problems with Class 1
+operation.
+
+The operating system must also provide sufficient low-level
+buffering to allow uninterrupted transfer of data between the
+modem and a disk file at the selected baud rate, typically 9600
+bps. Since the fax protocol does not provide end-to-end flow
+control the effectiveness of flow control while receiving is
+limited by the size of the modem's buffer. This can be less than
+100 bytes. Efax does not use flow control during reception.
+
+.SH MODEM REQUIREMENTS
+
+The "Group" is the protocol used to send faxes between fax
+machines. Efax supports the Group 3 protocol used over the
+public telephone network.
+
+The "Class" is the protocol used by computers to control fax
+modems. Efax supports Class 1, 2 and 2.0 fax modems.
+
+Most fax modems use XON/XOFF flow control when in fax mode. This
+type of flow control adds very little overhead for fax use. Many
+modems have unreliable hardware (RTS/CTS) flow control in fax
+mode. By default efax enables only XON/XOFF flow control and the
+-oh option must be used to add hardware flow control.
+
+While some modems have serial buffers of about 1k bytes, many
+inexpensive modems have buffers of about one hundred bytes and
+are thus more likely to suffer overruns when sending faxes.
+
+A few older modems may need a delay between commands of more than
+the default value used by efax (100 milliseconds). If the delay
+is too short, commands may not echo properly, may time out, or
+may give inconsistent responses. Use one or more \fB-oz\fP
+options to increase the delay between modem initialization
+commands and use the E0 modem initialization command to disable
+echoing of modem commands.
+
+By default efax sends DC2 to start the data flow from the modem
+when receiving faxes from Class 2 modems. A few older modems
+require XON instead. Use of DC2 would cause the modem to give an
+error message and/or the program to time out. The \fB-ox\fP
+option should be used in this case.
+
+A few older Class 2 modems (e.g. some Intel models) don't send
+DC2 or XON to start the data flow to the modem when sending
+faxes. After waiting 2 seconds efax will print a warning and
+start sending anyways.
+
+A very few Class 2 modems do not reverse the bit order (MSB to
+LSB) by default on receive. This might cause errors when trying
+to display or print the received files. The \fB-or\fP option can
+be used in this case.
+
+Some inexpensive "9600 bps" fax modems only \fItransmit\fP at
+9600 bps and reception is limited to 4800 bps.
+
+The following Class 1 modems have been reported to work with efax:
+AT&T DataPort,
+.\" Andrea Gozzi <work@forum.sublink.org>, v0.6, SCO 3.2.0, (Class 1)
+Cardinal Digital Fax Modem (14400),
+.\" awk0%navajo@gte.com, v0.6, linux 1.0, downloading fax144c.car
+Digicom Scout+,
+.\" umlin000@CC.UManitoba.CA, v 0.6, Linux 1.1.12
+Motorola Lifestyle 28.8,
+.\" mortbay@ozemail.com.au
+Motorola Power 28.8,
+.\" danz@wv.mentorg.com, Linux 1.2.10
+QuickComm Spirit II,
+.\" umlin000@CC.UManitoba.CA, v 0.6, Linux 1.1.12
+.\" gsmith@softsmiths.oz.au, v 0.7a, add "*F1" for Xon/Xoff
+Smartlink 9614AV-Modem,
+.\" gt@sky.gun.de, v0.6, Linux 1.1.15
+Supra Faxmodem 144LC,
+.\" john@johncon.johncon.com, v0.6, Consensys (ie., Unixware) 4.2
+USR Courier V.32bis Terbo,
+.\" meyer@geomatic.no, v0.6, SunOS 4.1.3
+USR Sportster (V.32 and V.34),
+.\" satyr!kayvan@apple.com (Kayvan Sylvan), v0.6, Linux (?)
+Zoom AFC 2.400,
+.\" hausutzu@pse.panic.bln.sub.org (Utz-Uwe Haus), v0.6, Linux
+Zoom VFX14.4V.
+.\" edc@ee.ubc.ca (me!), v0.6, Linux
+
+The following Class 2 modems have been reported to work with efax:
+14k4 Amigo Communion fax/modem,
+.\" bekker@tn.utwente.nl, efax0.5
+Adtech Micro Systems 14.4 Fax/modem,
+.\" gmaughan@grape.fcit.monash.edu.au, Linux 1.2.10, efax 07a
+askey modem type 1414VQE,
+.\" thowi@chiba.escape.de, efax06?, Linux?
+AT&T DataPort,
+.\" ingber@alumni.caltech.edu (Class 2)
+ATT/Paradyne,
+.\" john@johncon.johncon.com
+AT&T Paradyne PCMCIA,
+.\" jh@datanet.tele.fi (Juha Heinanen)
+Boca modem,
+.\" ?
+BOCA M1440E,
+.\" v0.6a, SunOS 4.1.1, Linux 1.0.9
+.\" hsw1@papa.attmail.com
+Crosslink 9614FH faxmodem,
+.\" ?
+FuryCard DNE 5005,
+.\" a PCMCIA Class 3 faxmodem
+.\" ron@draconia.hacktic.nl
+GVC 14.4k internal,
+.\" jchen@ee.mcgill.ca, 0.6a w/ stty fax patch, Linux kernel 1.1.59
+Intel 14.4 fax modem,
+.\" (matloff@cs.ucdavis.edu)
+Megahertz 14.4,
+,\" norman@bellcore.com
+Microcom DeskPorte FAST ES 28.8,
+.\" Skip Montanaro (skip@automatrix.com), 0.6a, Linux
+Motorola UDS FasTalk II,
+.\" Raj Mathur (root@darbari.ncst.ernet.in), 0.6a, Linux 1.1.48
+MultiTech 1432MU,
+.\"reb@pdsf.ssc.gov
+Practical Peripherals PM14400FXMT,
+.\" (DEC Alpha AXP 3000/500 running OSF/1 V1.3)
+Supra V32bis,
+.\" john@johncon.johncon.com, v0.5b, SysV R4.2
+.\" tbucks!timothy@csn.org
+.\" (ROCKWELL)
+.\" Alec.Muffett@UK.Sun.COM (Alec Muffett), Linux 1.1.51,
+.\" Supra FAXModem v.32bis
+Telebit Worldblazer,
+.\" blurfl!jhood@Dartmouth.EDU
+.\" Telebit Worldblazer with ROM version LA7.02. (requires -or)
+.\" (my configuration required hardware flow control)
+.\" Dario_Ballabio@milano.europe.dg.com, v 0.6, Version LA7.05C.
+TKR DM-24VF+,
+.\" rainer.dorsch@student.uni-ulm.de
+Twincom 144/DFi,
+.\" (ROCKWELL, V.32AC, V1.270 TR14-Jxxx-001)
+ViVa 14.4/Fax modem,
+.\" Robert.Sprockeels@csc.be, v0.6a, Linux
+Vobis Fax-Modem (BZT-approved),
+.\" klein@pc-klein.zxa.basf-ag.de (Peter Klein), Linux, kernel 0.99.14
+.\" beck@irs.inf.tu-dresden.de (Andre Beck), v 0.6, Ultrix 4.3, gcc V2.5.8:
+.\" gcc -ansi -D_XOPEN_SOURCE -O2 efax.c -o efax -lcP
+Zoom VFX14.4V,
+.\" edc@ee.ubc.ca (me!), v0.6, Linux
+ZyXEL U-1496E[+],
+.\" plph@umcc.umich.edu, v0.3 & faxmodem ROM version 5.05M)
+.\" requires -or
+.\" Marc@Synergytics.Com, v0.5a & ZyXEL 1496E Plus, ROM Version 6.11a)
+.\" -or -i '+FCLASS=2;+FCR=1' -c '+FDCC=1,5,2,2,0,0,0,0'
+ZyXEL Elite 2864I.
+.\" schlatt@dial.eunet.ch, v0.7a, using -Xn (n<4)
+
+.SH MODEM INITIALIZATION OPTIONS
+
+The required modem initialization commands are generated by efax.
+Additional commands may be supplied as command-line arguments.
+The modem must be set up to issue verbose(text) result codes.
+The following command does this and is sent by efax before trying
+to initialize the modem.
+
+.TP 9
+.BR Q0V1
+respond to commands with verbose result codes
+
+.PP
+The following commands may be useful for special purposes:
+
+.TP 9
+.BR X3
+don't wait for dial tone before dialing. This may be used to
+send a fax when the call has already been dialed manually. In
+this case use an empty string ("") as the first argument to the
+\fB-t\fP command. Use \fBX4\fP (usual default) to enable all
+result codes.
+
+.TP 9
+.BR M2
+leave the monitor speaker turned on for the duration of the call
+(use \fBM0\fP to leave it off).
+
+.TP 9
+.BR L0
+turn monitor speaker volume to minimum (use \fBL3\fP for maximum).
+
+.TP 9
+.BR E0
+disable echoing of modem commands. See the Resolving Problems
+section below.
+
+.TP 9
+.BR &D2
+returns the modem to command mode when DTR is dropped. The
+program drops DTR at the start and end of the call if it can't
+get a response to a modem command. You can use \fB&D3\fP to
+reset the modem when DTR is dropped.
+
+.TP 9
+.BR S7=120
+wait up to two minutes (120 seconds) for carrier. This may be
+useful if the answering fax machine takes a long time to start
+the handshaking operation (e.g. a combined fax/answering machine
+with a long announcement).
+
+.SH CAPABILITIES
+
+The capabilities of the local hardware and software can be set
+using a string of 8 digits separated by commas:
+
+.BR \fIvr\fP,\fIbr\fP,\fIwd\fP,\fIln\fP,\fIdf\fP,\fIec\fP,\fIbf\fP,\fIst\fP
+
+where:
+
+.TP 9
+.I vr \fP (vertical resolution) =
+0 for 98 lines per inch
+.br
+1 for 196 lpi
+
+.TP 9
+.I br \fP (bit rate) =
+0 for 2400 bps
+.br
+1 for 4800
+.br
+2 for 7200
+.br
+3 for 9600
+.br
+4 for 12000 (V.17)
+.br
+5 for 14400 (V.17)
+
+.TP 9
+.I wd \fP (width) =
+0 for 8.5" (21.5 cm) page width
+.br
+1 for 10" (25.5 cm)
+.br
+2 for 12" (30.3 cm)
+
+.TP 9
+.I ln \fP (length) =
+0 for 11" (A4: 29.7 cm) page length
+.br
+1 for 14" (B4: 36.4 cm)
+.br
+2 for unlimited page length
+
+.TP 9
+.I df \fP (data format) =
+0 for 1-D coding
+.br
+1 for 2-D coding (not supported)
+
+.TP 9
+.I ec \fP (error correction) =
+0 for no error correction
+.\" .br
+.\" 1 for EC mode with 64 byte frames (not supported)
+.\" .br
+.\" 2 for EC mode with 256 byte frames (not supported)
+
+.TP 9
+.I bf \fP (binary file) =
+0 for no binary file transfer
+
+.TP 9
+.I st \fP (minimum scan time) =
+0 for zero delay per line
+.br
+1 for 5 ms per line
+.br
+3 for 10 ms per line
+.br
+5 for 20 ms per line
+.br
+7 for 40 ms per line
+
+.PP
+
+When \fIreceiving\fP a fax the \fIvr\fP, \fIwd\fP, and \fIln\fP
+fields of the capability string should be set to the maximum
+values that your display software supports. The default is 196
+lpi, standard (8.5"/21.5cm) width and unlimited length.
+
+When \fIsending\fP a fax efax will determine \fIvr\fP and
+\fIln\fP from the image file and set \fIwd\fP to the default.
+
+If the receiving fax machine does not support high resolution
+(\fIvr\fP=1) mode, efax will reduce the resolution by combining
+pairs of scan lines. If the receiving fax machine does not
+support the image's width then efax will truncate or pad as
+required. Most fax machines can receive \fIln\fP up to 2. Few
+machines support values of \fIwd\fP other than 0.
+
+
+.SH HEADERS
+
+efax adds blank scan lines at the top of each image when it is
+sent. This allows room for the page header but increases the
+length of the image (by default about 0.1" or 2.5mm of blank
+space is added).
+
+The header placed in this area typically includes the date and
+time, identifies the, and shows the page number and total pages.
+Headers cannot be disabled but the header string can be set to a
+blank line.
+
+The default font for generating the headers is the built-in 8x16
+pixel font scaled to 12x24 pixels (about 9 point size).
+
+Note that both efax and efix have -f options to specify the font.
+efIx uses the font to generate text when doing text-to-fax
+conversions (during "fax make") while efAx uses the font to
+generate the header (during "fax send").
+
+.SH SESSION LOG
+
+A session log is written to the standard error stream. This log
+gives status and error messages from the program as selected by
+the \fB-v\fP option. A time stamp showing the full time or just
+minutes and seconds is printed before each message. Times
+printed along with modem responses also show milliseconds.
+
+.SH RETURN VALUES
+
+The program returns an error code as follows:
+
+.TP 9
+0
+The fax was successfully sent or received.
+
+.TP 9
+1
+The dialed number was busy or the modem device was in use. Try
+again later.
+
+.TP 9
+2
+Something failed (e.g. file not found or disk full). Don't retry.
+Check the session log for more details.
+
+.TP 9
+3
+Modem protocol error. The program did not receive the expected
+response from the modem. The modem may not have been properly
+initialized, the correct \fB-o\fP options were not used, or a bug
+report may be in order. Check the session log for more details.
+
+.TP 9
+4
+The modem is not responding. Operator attention is required.
+Check that the modem is turned on and connected to the correct
+port.
+
+.TP 9
+5
+The program was terminated by a signal.
+
+.SH EXAMPLES
+
+.B Creating fax (G3) files
+
+The efix program can be used to convert text files to TIFF-G3
+format. For example, the following command will convert the text
+file \fBletter\fP to the files \fBletter.001\fP,
+\fBletter.002\fP, etc,:
+
+.IP
+.nf
+.ft CW
+efix -nletter.%03d letter
+.ft P
+.fi
+.LP
+
+Ghostscript's \fBtiffg3\fP driver can generate fax files in
+TIFF-G3 format from postscript files. For example, the command:
+
+.IP
+.nf
+\f(CW gs -q -sDEVICE=tiffg3 -dNOPAUSE \\
+ -sOutputFile=letter.%03d letter.ps </dev/null\fP
+.fi
+.LP
+
+will convert the Postscript file
+.BR letter.ps
+into high-resolution
+(\fIvr\fP=1) G3 fax image files \fBletter.001, letter.002,\fP ...
+
+The images should have margins of at least 1/2 inch (1 cm) since
+the fax standard only requires that fax machines print a central
+portion of the image 196.6mm (7.7 inches) wide by 281.5mm (11.1
+inches) high.
+
+The efix program can also insert bitmaps in images to create
+letterhead, signatures, etc.
+
+.B Printing fax files
+
+You can use the efix program to print faxes on Postscript or
+HP-PCL (LaserJet) printers. For example, to print the received
+fax file \fBreply.001\fP on a Postscript printer use the command:
+
+.IP
+.nf
+.ft CW
+efix -ops reply.001 | lpr
+.ft P
+.fi
+.LP
+
+.B Sending fax files
+
+The following command will dial the number 222-2222 using tone
+dialing and send a two-page fax from the TIFF-G3 files letter.001
+and letter.002 using the fax modem connected to device /dev/cua1.
+
+.IP
+.nf
+.ft CW
+efax -d /dev/cua1 \\
+ -t T222-2222 letter.001 letter.002
+.ft P
+.fi
+.LP
+
+.B Manual answer
+
+You can use efax to answer the phone immediately and start fax
+reception. Use this mode if you need to answer calls manually to
+see if they are fax or voice.
+
+For example, the following command will make the fax modem on
+device \fB/dev/ttyS1\fP answer the phone and attempt to receive a
+fax. The received fax will be stored in the files
+\fBreply.001\fP, \fBreply.002\fP, and so on. The modem will
+identify itself as "555 1212" and receive faxes at high or low
+resolution (\fIvr\fP=1), at up to 14.4 kbps (\fIbr\fP=5).
+
+.IP
+.nf
+.ft CW
+efax -d /dev/ttyS1 -l "555 1212" \\
+ -c 1,5 -r reply
+.ft P
+.fi
+.LP
+
+.B Automatic answer
+
+The \fB-w\fP option makes efax wait for characters to become
+available from the modem (indicating an incoming call) before
+starting fax reception. Use the \fB-w\fP option and a
+\fB-i\fPS0=\fIn\fP option to answer the phone after \fIn\fP
+rings. The example below will make the modem answer incoming
+calls in fax mode on the fourth ring and save the received faxes
+using files names corresponding to the reception date and time.
+
+.IP
+.nf
+.ft CW
+efax -d /dev/ttyb -w -iS0=4 2>&1 >> fax.log
+.ft P
+.fi
+.LP
+
+.B Sharing the modem with outgoing calls
+
+The modem device can be shared by programs that use the UUCP
+device locking protocol. This includes pppd, chat, minicom,
+kermit, uucico, efax, cu, and many others others. However,
+locking will only work if all programs use the same lock file.
+
+efax will lock the modem device before opening it if one or more
+UUCP lock file names are given with \fB-x\fP options. Most
+programs place their lock files in the \fR/usr/spool/uucp\fP or
+\fR/var/lock\fP directories and use the name \fRLCK..\fP\fIdev\fP
+where \fIdev\fP is the name of the device file in the /dev
+directory that is to be locked.
+
+If the \fB-s\fP (share) option is used, the lock file is removed
+while waiting for incoming calls so other programs can use the
+same device.
+
+If efax detects another program using the modem while it is
+waiting to receive a fax, efax exits with a termination code of
+1. A subsequent efax process using this device will wait until
+the other program is finished before re-initializing the modem
+and starting to wait for incoming calls again.
+
+Programs that try to lock the modem device by using device
+locking facilities other than UUCP lock files not be able to use
+this arbitration mechanism because the device will still be open
+to the efax process. In this case you will need to kill the efax
+process (e.g. "fax stop") before starting the other program.
+
+When efax is waiting for a fax it leaves the modem ready to
+receive in fax mode but removes the lock file. When a slip or
+PPP program takes over the modem port by setting up its own lock
+file efax cannot send any more commands to the modem -- not even
+to reset it. Therefore the other program has to set the modem
+back to data mode when it starts up. To do this add a modem
+reset command (send ATZ expect OK) to the beginning of your slip
+or PPP chat script.
+
+.B Accepting both fax and data calls
+
+Many modems have an adaptive data/fax answer mode that can be
+enabled using the \fB-j+FAE=1\fP (for Class 1) or \fB-jFAA=1\fP
+(for Class 2[.0]) initialization string. The type of call (data
+or fax) can then be deduced from the modem's responses.
+
+Some modems have limited adaptive answer features (e.g. only
+working properly at certain baud rates or only in Class 2) or
+none at all. In this case use the initialization string
+\fB-i+FCLASS=0\fP to answer in data mode first and the \fB-oa\fP
+option to then hang up and try again in fax mode if the first
+answer attempt was not successful. This method only works if
+your telephone system waits a few seconds after you hang up
+before disconnecting incoming calls.
+
+If the \fB-g\fP option is used then the option's argument will be
+run as a shell command when an incoming data call is detected.
+Typically this command will exec \fBgetty\fP(8). This program
+should expect to find the modem already off-hook and a lock file
+present so it should not try to hang up the line or create a lock
+file. Note that the modem should be set up to report the DCE-DTE
+(modem-computer, e.g. CONNECT 38400) speed, not the DCE-DCE
+(modem-modem, e.g. CONNECT 14400) speed. For many modems the
+initialization option -iW0 will set this.
+
+The following command will make efax answer incoming calls on
+\fB/dev/cua1\fP on the second ring. This device will be locked
+using two different lock files but these lock files will be
+removed while waiting for incoming calls (\fB-s\fP). If a data
+call is detected, the \fBgetty\fP program will be run to
+initialize the terminal driver and start a \fBlogin\fP(1)
+process. Received fax files will be stored using names like
+\fBDec02-12.32.33.001\fP, in the \fB/usr/spool/fax/incoming\fP
+directory and the log file will be appended to
+\fB/usr/spool/fax/faxlog.cua1\fP.
+
+.IP
+.nf
+.ft CW
+efax -d /dev/cua1 -j '+FAA=1' \\
+ -x /usr/spool/uucp/LCK..cua1 \\
+ -x /usr/spool/uucp/LCK..ttyS1 \\
+ -g "exec /sbin/getty -h /dev/cua1 %d" \\
+ -iS0=2 -w -s \\
+ -r "/usr/spool/fax/incoming/%b%d-%H.%I.%S" \\
+ >> /usr/spool/fax/faxlog.cua1 2>&1
+.ft P
+.fi
+.LP
+
+Note that adaptive answer of either type will not work for all
+callers. For some data calls the duration of the initial
+data-mode answer may be too short for data handshaking to
+complete. In other cases this duration may be so long that
+incoming fax calls will time out before efax switches to fax
+mode. In addition, some calling fax modems mistake data-mode
+answering tones for fax signaling tones and initiate fax
+negotiation too soon. If you use software adaptive answer you
+can reduce the value of the initial data-mode answer (set by
+TO_DATAF in efax.c) to get more reliable fax handshaking or
+increase it for more reliable data handshaking. However, if you
+need to provide reliable fax and data service to all callers you
+should use separate phone numbers for the two types of calls.
+
+When a call is answered the modem goes on-line with the
+computer-to-modem baud rate fixed at the speed used for the most
+recent AT command. When efax is waiting for a fax or data call
+it sets the interface speed to 19200 bps since this is the speed
+required for fax operation. This prevents full use of 28.8kbps
+modem capabilities.
+
+
+.SH USING INIT TO RUN EFAX
+
+efax can answer all incoming calls if you place an entry for efax
+in \fB/etc/inittab\fP (for SysV-like systems) or
+\fB/etc/ttytab\fP (for BSD-like systems). The \fBinit\fP(8)
+process will run a new copy of efax when the system boots up and
+whenever the previous efax process terminates. The inittab or
+ttytab entry should invoke efax by running the \fBfax\fP script
+with an \fBanswer\fP argument.
+
+For example, placing the following line in \fB/etc/inittab\fP
+(and running "kill -1 1") will make init run the \fBfax\fP script
+with the argument \fBanswer\fP every time previous process
+terminates and \fBinit\fP is in runlevel 4 or 5.
+
+.IP
+.nf
+.ft CW
+s1:45:respawn:/bin/sh /usr/bin/fax answer
+.ft P
+.fi
+.LP
+
+For BSD-like systems (e.g. SunOS), a line such as the following
+in \fB/etc/ttytab\fP will have the same effect:
+
+.IP
+.nf
+.ft CW
+ttya "/usr/local/bin/fax answer" unknown on
+.ft P
+.fi
+.LP
+
+You should protect the fax script and configuration files against
+tampering since init will execute them as a privileged (root)
+process. If you will be allowing data calls via getty and login
+you should ensure that your system is reasonably secure
+(e.g. that all user id's have secure passwords).
+
+If efax exec()'s getty properly but you get a garbled login
+prompt then there is probably a baud rate mismatch between the
+modem and the computer. First, check the efax log file to make
+sure the modem's CONNECT response reported the serial port speed
+(e.g. 19200), \fBnot\fP the modem-modem speed (e.g. 14400).
+Next, check the getty options and/or configuration files
+(e.g. /etc/gettydefs) for that particular baud rate. Then run
+getty manually with the same arguments and verify the port
+settings using ``stty </dev/XXX''. Note that you'll probably
+want to enable hardware flow control for data connections (-h for
+agetty, CRTSCTS for getty_ps).
+
+A few programs won't work properly when efax is set up to answer
+calls because they don't create lock files. You can put the
+shell script ``wrapper'' below around such programs to make them
+work properly. Change BIN and LOCKF to suit.
+
+.IP
+.nf
+.ft CW
+#!/bin/sh
+BIN=/bin/badprogram
+LOCKF=/var/spool/uucp/LCK..cua1
+if [ -f $LOCKF ]
+then
+ echo lock file $LOCKF exists
+ exit 1
+else
+ printf "%10d\n" $$ >$LOCKF
+ $BIN $*
+ rm $LOCKF
+fi
+.ft P
+.fi
+.LP
+
+
+.SH DELIVERING RECEIVED FAXES BY E-MAIL
+
+The "fax answer" script described above can be configured to
+e-mail the fax files received by the previous fax answer process
+to a "fax manager" who can then forward the fax to the correct
+recipient. The received fax files are send as MIME attachments,
+one file per page, using the ``base64'' text encoding and the
+``image/tiff'' file format.
+
+To view the fax images directly from your e-mail reader you will
+have to configure it with an application that can display files
+of type image/tiff. Typically this is specified in a ``mailcap''
+file. For example, placing the following line in /etc/mailcap
+will cause the fax file attachments to be displayed using the
+``fax view'' command.
+
+.ft CW
+image/tiff; fax view %s
+.ft P
+
+.SH SENDING FAXES USING THE PRINT SPOOLER
+
+You can configure a "fax" printer into the lpr print spooler that
+will fax a document out using efax instead of printing it. This
+allows a network server running efax to send faxes on behalf of
+other machines, including non-Unix clients. In the following
+steps use the directories specified in the fax script if they are
+different than /usr/bin and /var/spool/fax (FAXDIR). To set up a
+fax printer do the following as root:
+
+(1) Create a link to the fax script called ``faxlpr'' so the fax
+script can determine when it is being invoked from the print
+spooler:
+
+.ft CW
+ln /usr/bin/fax /usr/bin/faxlpr
+.ft P
+
+
+(2) Edit /etc/printcap and add an entry such as:
+
+.IP
+.nf
+.ft CW
+fax:lp=/dev/null:sd=/var/spool/fax:if=/usr/bin/faxlpr:
+.ft P
+.fi
+.LP
+
+to define a printer called "fax". Print files will be spooled to
+the /var/spool/fax (sd=) directory and then piped to the
+/usr/bin/faxlpr filter (if=).
+
+(3) Create and/or set the permissions to allow anyone to read and
+write in the fax spool directory. For example:
+
+.IP
+.nf
+.ft CW
+mkdir /var/spool/fax
+chmod 777 /var/spool/fax
+.ft P
+.fi
+.LP
+
+(4) Create a printer daemon lock file that is readable by anyone:
+
+.IP
+.nf
+.ft CW
+touch /var/spool/fax/lock
+chmod 644 /var/spool/fax/lock
+.ft P
+.fi
+.LP
+
+You should now be able to send a fax using the lpr interface by
+using a command such as:
+
+.IP
+.nf
+.ft CW
+lpr -P fax -J "555 1212" file.ps
+.ft P
+.fi
+.LP
+
+where the -J option is used to specify the phone number or alias
+to be dialed.
+
+Note that if more than one file is given on the command line they
+will be concatenated before being passed to "fax send". TIFF-G3,
+Postscript or PBM files must therefore be sent one file at a time
+although TIFF and Postscript files may contain multiple pages.
+Only multiple \fItext\fP files can be sent in one command. Page
+breaks in text files can be marked with form-feed characters.
+Files will be converted and sent at the default (high)
+resolution.
+
+You can use lpq(1) to check the fax queue, lprm(1) to remove fax
+jobs and lpc(8) to control the spooler. In each case use the
+-Pfax option to specify the fax ``printer.'' A log file will be
+mailed to the user when the fax is sent.
+
+You should also be able to send a fax from any networked computer
+that has lpr-compatible remote printing software and that allows
+you to set the job name (-J option) to an arbitrary string. Such
+software is available for most computers.
+
+See the lpd(8) and printcap(5) man pages for information on the
+print spooler and for restricting access by host name
+(/etc/host.lpd) or by user group (the `rg' printcap entry).
+
+.SH RESOLVING PROBLEMS
+
+Double check the configuration setup in the first part of the fax
+script, particularly the modem device name and the lock file
+names.
+
+If efax hangs when trying to open the modem device (typically
+/dev/ttyX), the device is either already in use by another
+process (e.g. pppd) or it requires the carrier detect line to be
+true before it can be opened. Many systems define an alternate
+device name for the same physical device (typically cuaX) that
+can be opened even if carrier is not present or other programs
+are already using it.
+
+If responses to modem initialization commands are being lost or
+generated at random, another processes (e.g. getty or an efax
+auto-answer process) may be trying to use the modem at the same
+time. Try running efax while this other program is running. If
+efax does not report "/dev/ttyX locked or busy. waiting." then
+the lock files names are not specified correctly.
+
+Attempt to send a fax. Check that the modem starts making the
+calling signal (CNG, a 0.5 second beep every 3 seconds) as soon
+as it's finished dialing. This shows the modem is in fax mode.
+You may need to set the SPKR variable to -iM2L3 to monitor the
+phone line to do this.
+
+Listen for the answering fax machine and check that it sends the
+answer signal (CED, a 3 second beep) followed by "warbling"
+sounds (DIS frames) every 3 seconds. If you hear a continuous
+sound (tones or noise) instead, then you've connected to a data
+modem instead.
+
+Your modem should send back its own warble (DCS frame) in
+response to DIS immediately followed by 1.5 seconds of noise (a
+channel check). If everything is OK, the receiving end will send
+another warble (CFR frame) and your modem will start to send
+data. If you have an external modem, check its LEDs. If flow
+control is working properly the modem's send data (SD) LED will
+turn off periodically while the fax data is sent.
+
+Check the message showing the line count and the average bit rate
+when the page transmission is done. Low line counts (under 1000
+for a letter size image) or the warning "fax output buffer
+overflow" while sending indicate that the image data format is
+incorrect. Check the file being sent using the "fax view"
+command.
+
+If you get the error message ``flow control did not work'' then
+flow control was not active. This usually results in a garbled
+transmission and the receiving machine may reject the page, abort
+the call, print a distorted or blank image and/or hang up.
+
+The warning "characters received while sending" or an <XOFF>
+character appearing after the transmission means that the
+operating system ignored the modem's XOFF flow control character.
+Ensure that you are not running other programs such as getty or
+pppd at the same time as efax since they will turn off xon/xoff
+flow control.
+
+If you cannot get flow control to work properly then enable
+``virtual flow control'' with the \fB-of\fP option or hardware
+flow control with the \fB-oh\fP option.
+
+Check that the remote machine confirms reception with a +FPTS:1
+response (Class 2) or an MCF frame (Class 1).
+
+For Class 2 modems, the error message "abnormal call termination
+(code \fInn\fP)" indicates that the modem detected an error and
+hung up.
+
+Many companies advertise services that will fax back information
+on their products. These can be useful for testing fax
+reception.
+
+The message "run length buffer overflow" when receiving indicates
+an error with the image data format. You may need to use the
+\fB-or\fP option with certain Class 2 modems.
+
+If efax displays the message "can't happen (<details>)" please
+send a bug report to the author.
+
+Finally, don't play "option bingo," if you can't resolve the
+problem send a verbose log of the failed session (the output from
+\fBfax -v ...\fP) to the address below.
+
+.SH WEB PAGE
+
+A Web Page with pointers to the latest version, known bugs and
+patches is available at:
+.IP
+.ft CW
+http://casas.ee.ubc.ca/efax/
+.ft P
+.LP
+
+.SH RELATED SOFTWARE
+
+For Linux Systems
+
+Independent packages provide more user-friendly interfaces to
+efax (xfax, tefax) and provide an e-mail-to-fax (Qfax) gateway
+using efax. All are available by anonymous FTP from
+metalab.unc.edu in /pub/Linux/apps/serialcomm/fax/.
+
+For Amiga Systems
+
+A port of an early version of efax for the Amiga is available as
+a component of a shareware voice mail package, AVM, distributed
+by Al Villarica (rvillari@cat.syr.edu).
+
+Other Ports
+
+efax is relatively easy to port. All system-dependent code is in
+\fBefaxos.c\fP. An early version of efax was ported to VMS.
+Version 0.8a was ported to Win32 by Luigi Capriotti. Contact the
+author if you would like to integrate the Win32 code into the
+current version.
+
+.SH AUTHOR
+
+Efax was written by Ed Casas. Please send comments or bug
+reports to edc@cce.com.
+
+.SH BUG REPORTS
+
+Bug reports should include the operating system, the type of the
+modem and a copy of a verbose session log that demonstrates the
+problem. It's usually impossible to help without a verbose log.
+Please do \fBnot\fP send fax image files.
+
+.SH COPYRIGHT
+
+efax is copyright 1993 -- 1999 Ed Casas. It may be used, copied
+and modified under the terms of the GNU Public License.
+
+.SH DISCLAIMER
+
+Although \fBefax\fP has been tested it may have errors that will
+prevent it from working correctly on your system. Some of these
+errors may cause serious problems including loss of data and
+interruptions to telephone service.
+
+.SH REFERENCES
+
+CCITT Recommendation T.30, "Procedures for Document Facsimile
+Transmission in the General Switched Telephone Network". 1988
+
+CCITT Recommendation T.4, "Standardization of Group 3 Facsimile
+Apparatus for Document Transmission". 1988.
+
+For documentation on Class 1 and Class 2 fax commands as
+implemented by Connexant (formerly Rockwell) modems see
+http://www.conexant.com/techinfo.
+
+For the TIFF specification see
+http://partners.adobe.com/supportservice/devrelations/PDFS/TN/TIFF6.pdf
+or RFC 2301 (ftp://ftp.isi.edu/in-notes/rfc2301.txt).
+
+For information on Ghostscript see
+http://www.cs.wisc.edu/~ghost/.
+
+The pbm utilities can be obtained by ftp from wuarchive.wustl.edu
+in /graphics/graphics/packages/NetPBM/netpbm-1mar1994.tar.gz.
+
+PCX and many other file formats are described in: Gunter Born,
+The File Formats Handbook, International Thomson Computer Press,
+1995.
+
+The "Fax Modem Source Book" by Andrew Margolis, published by John
+Wiley & Sons in 1994 (ISBN 0471950726), is a book on writing fax
+applications which includes source code.
+
+Dennis Bodson et. al., "FAX: Digital Facsimile Technology and
+Applications", Second Edition. Artech House, Boston. 1992.
+
+.SH SEE ALSO
+
+.BR fax(1),
+.BR efix(1),
+.BR gs(1),
+.BR init(8),
+.BR inittab(5),
+.BR ttytab(5),
+.BR printcap(5),
+.BR lpd(8),
+.BR printf(3),
+.BR strftime(3).
+
+.SH BUGS
+
+Can't read TIFF files with more than 1 strip
+
+Class 1 operation may fail if the program can't respond to
+certain data received from the modem within 55 milliseconds.
+
+May fail if multitasking delays cause the received data to
+overflow the computer's serial device buffer or if an under-run
+of transmit data exceeds 5 seconds.
+
+Polling does not work.
+
+Does not support 2-D coding, ECM, or BFT.
--- /dev/null
+#define Copyright "Copyright 1999 Ed Casas"
+
+#define Version "efax v 0.9"
+
+/*
+ Copyright (C) 1999 Ed Casas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Please contact the author if you wish to use efax or efix in
+ ways not covered by the GNU GPL.
+
+ You may contact the author by e-mail at: edc@cce.com, by mail
+ at: 2629 West 3rd Ave, Vancouver, BC, Canada, V6K 1M4, or by
+ fax at: +1 604 734 5291.
+
+*/
+
+const char *Usage =
+ "Usage:\n"
+ " %s [ option ]... [ -t num [ file... ] ]\n"
+ "Options:\n"
+ " -a str use command ATstr to answer\n"
+ " -c cap set modem and receive capabilites to cap\n"
+ " -d dev use modem on device dev\n"
+ " -e cmd exec \"/bin/sh -c cmd\" for voice calls\n"
+ " -f fnt use (PBM) font file fnt for headers\n"
+ " -g cmd exec \"/bin/sh -c cmd\" for data calls\n"
+ " -h hdr use page header hdr (use %%d's for current page/total pages)\n"
+ " -i str send modem command ATstr at start\n"
+ " -j str send modem command ATstr after set fax mode\n"
+ " -k str send modem command ATstr when done\n"
+ " -l id set local identification to id\n"
+ " -o opt use protocol option opt:\n"
+ " 0 use class 2.0 instead of class 2 modem commands\n"
+ " 1 use class 1 modem commands\n"
+ " 2 use class 2 modem commands\n"
+ " a if first [data mode] answer attempt fails retry as fax\n"
+ " e ignore errors in modem initialization commands\n"
+ " f use virtual flow control\n"
+ " h use hardware flow control\n"
+ " l halve lock file polling interval\n"
+ " n ignore page retransmission requests\n"
+ " r do not reverse received bit order for Class 2 modems\n"
+ " x use XON instead of DC2 to trigger reception\n"
+ " z add 100 ms to pause before each modem comand (cumulative)\n"
+ " -q ne ask for retransmission if more than ne errors per page\n"
+ " -r pat save received pages into files pat.001, pat.002, ... \n"
+ " -s share (unlock) modem device while waiting for call\n"
+ " -v lvl print messages of type in string lvl (ewinchamr)\n"
+ " -w don't answer phone, wait for OK or CONNECT instead\n"
+ " -x fil use uucp-style lock file fil\n"
+ "Commands:\n"
+ " -t dial num and send fax image files file... \n"
+ ;
+
+#include <ctype.h> /* ANSI C */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include "efaxio.h" /* EFAX */
+#include "efaxlib.h"
+#include "efaxmsg.h"
+#include "efaxos.h"
+
+/* constants... */
+
+ /* delays and timeouts (t/o), in deciseconds */
+#define T1 350 /* T.30 T1 - waiting for DIS/DCS before Phase B */
+#define T2 60 /* T.30 T2 - waiting for frame in Phase B */
+#define T3S 30 /* T.30 response timeout (not T3) */
+#define T4 30 /* T.30 T4 - between [re]transmissions of DIS */
+
+#define TMOD 55 /* T.30 pause between v.21&v.29, 75-20 ms */
+
+#define TO_A 1200 /* dial/answer (Phase A) - modem may t/o first */
+#define TO_ABRT 20 /* max delay after sending abort sequence */
+#define TO_CHAR 102 /* per data character (2x max FILL length) */
+#define TO_DATAF 80 /* software adaptive answer data connect t/o */
+#define TO_DRAIN_H 136 /* minimum HDLC buffer drain time (4k/300cps) */
+#define TO_DRAIN_D 300 /* minimum data buffer drain time */
+#define TO_FT 31 /* max delay after +F[TR][MH] command */
+#define TO_RTCMD 20 /* return to command mode after DLE-ETX (rx) */
+
+#define TO_C2B 450 /* Class 2 DIS to CONNECT:(DCS+TCF+CFR)xretries */
+#define TO_C2X 20 /* Class 2 wait for XON: 2/5 of 5s timeout */
+#define TO_C2PP 200 /* Class 2 wait for ppr: (ppm+ppr)x3retries + 2 */
+#define TO_C2R 600 /* Class 2 receive: (TCF+FTT)x11 retrains + 5 */
+#define TO_C2EOR 120 /* Class 2 end of data rx (4 retrans x 3 s) */
+
+#define ANSCMD "A" /* default modem command to answer calls */
+#define DCSLEN 3 /* length of FIF for DCS commands sent */
+#define DEFDISLEN 3 /* length of DIS initially transmitted */
+#define DEFCAP 1,3,0,2,0,0,0,0 /* default local capabilities */
+#define DEFID " " /* default local ID */
+#define DEFPAT "%m%d%H%M%S" /* default received file name pattern */
+#define HDRSHFT 54 /* shift header right 6.7mm into image area */
+#define HDRSPCE 20 /* number of scan lines inserted before image */
+#define HDRSTRT 4 /* scan line where header is placed on image */
+#define HDRCHRH 24 /* header character height (pels, at 196lpi) */
+#define HDRCHRW 12 /* header character width (pels) */
+#define IDLEN 20 /* length of T.30 ID strings, must be 20 */
+#define MAXDIS 8 /* maximum DIS frames sent without response (T1) */
+#define MAXERRPRT 32 /* maximum number of reception errors to report */
+#define MAXFIFLEN 125 /* max FIF len = MAXFRLEN - (adx+ctl+FCF) - FCS */
+#define MAXFRLEN 130 /* max frame length = 3.45s x 300 bps / 8 */
+#define MAXGETTY 512 /* maximum length of exec'ed (-g, -e) commands */
+#define MAXICMD 100 /* maximum # of modem setup/reset commands */
+#define MAXLKFILE 16 /* maximum number of lock files */
+#define MAXNULLS 2 /* maximum consecutive received nulls saved */
+#define MAXPGERR 10 /* maximum received errors allowed per page */
+#define MAXTRAIN 2 /* maximum training retries at lowest speed */
+#define MAXRETRY 3 /* maximum retries of unacknowledged commands */
+#define NCAP 8 /* number of fields in a capability string */
+#define NTXRETRY 3 /* maximum re-sends per page */
+
+typedef int cap [ NCAP ] ; /* remote/local capabilities */
+
+ /* capability fields... */
+enum captype { VR, BR, WD, LN, DF, EC, BF, ST } ;
+int capmax [ NCAP ] = { 1, 7, 2, 2, 3, 2, 1, 7 } ;
+ /* & maximum values */
+
+ /* vertical resolution, dpi */
+int vresolution [ 2 ] = { 98, 196 } ;
+
+ /* characters per second for br */
+int cps [ 8 ] = { 300, 600, 900, 1200, 1500, 1800, 900, 1200 } ;
+
+ /* next br = fallback [ br ] */
+ /* 0, 1, 2, 3, 4, 5, 6, 7 */
+int fallback [ 8 ] = {-1, 0, 1, 2, 7, 4, 3, 6 } ;
+
+ /* negotiation speed index */
+ /* 0, 1, 2, 3, 4, 5, 6, 7 */
+int brindex [ 8 ] = { 0, 1, 2, 3, 4, 7, 5, 6 } ;
+
+ /* minimum scan time in ms */
+int mst [ 8 ] = { 0 , 5, 10, 10, 20, 20, 40, 40 } ;
+
+ /* page width in pixels */
+int pagewidth [ 5 ] = { 1728, 2048, 2432, 1216, 864 } ;
+
+/* Table to convert between T.30 DIS/DCS/DTC FIF and Class 2-like
+ capability codes. Uses br=6, 7 for V.17 at 7200, 9600. */
+
+typedef struct t30tabstruct
+{
+ char *name ;
+ uchar byte, shift, mask ;
+ uchar safeval ;
+ uchar captodis[8], distocap[16], captodcs[8], dcstocap[16] ;
+} t30tabst ;
+
+#define X 0xff /* invalid values */
+
+t30tabst t30tab [ NCAP ] = {
+ { "vr", 1, 1, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
+ { "br", 1, 2, 0x0f, 0,
+ { 0, 4, 12, 12, 13, 13 } ,
+ { 0, X, X, X, 1, X, X, X, 3, X, X, X, 3, 5, 3, X } ,
+ { 0, 4, 12, 8, 5, 1 } ,
+ { 0, 5, 5, X, 1, 4, 4, X, 3, 7, X, X, 2, 6, X, X } } ,
+ { "wd", 2, 6, 0x03, 0, { 0, 2, 1 } , { 0, 2, 1, 2 } ,
+ { 0, 2, 1 } , { 0, 2, 1, 2 } },
+ { "ln", 2, 4, 0x03, 0, { 0, 2, 1 } , { 0, 2, 1, X } ,
+ { 0, 2, 1 } , { 0, 2, 1, X } },
+ { "df", 1, 0, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
+ { "ec", 3, 4, 0x03, 0, { 0, 2, 2 } , { 0, X, 2, X } ,
+ { 0, 3, 2 } , { 0, 0, 2, 1 } },
+ { "bf", 5, 5, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
+ { "st", 2, 1, 0x07, 7,
+ { 7, 4, 3, 2, 6, 0, 5, 1 } , { 5, 7, 3, 2, 1, 6, 4, 0 } ,
+ { 7, 4, X, 2, X, 0, X, 1 } , { 5, 7, 3, 1, X, X, X, 0 } }
+} ;
+
+ /* values of capability fields */
+char *capvaluestr [ NCAP ] [8] = {
+ { " 98lpi", "196lpi" } ,
+ { " 2400bps", " 4800bps", " 7200bps", " 9600bps", " 12kbps", "14.4kbps",
+ "7200V.17", "9600V.17" } ,
+ { "8.5\"/215mm", " 10\"/255mm", " 12\"/303mm",
+ " 6\"/151mm", "4.2\"/107mm" } ,
+ { "11\"/A4", "14\"/B4", " any " } ,
+ { "1D" , "2D" }, { " - ", "ECM-256", "ECM-64 " }, { " - ", "BFT" },
+ { "0ms", "5ms", "10/5ms", "10ms", "20/10ms", "20ms", "40/20ms", "40ms" }
+} ;
+
+/* T.30 control frames */
+
+enum frametype {
+ DIS=0x01, CSI, NSF=0x04,
+ CFR=0x21, FTT,
+ MCF=0x31, RTN, RTP, PIN, PIP,
+ DCS=0x41, TSI, NSS=0x44,
+ CRP=0x58, DCN=0x5f,
+ EOM=0x71, MPS, EOP=0x74, PRI_EOM=0x79, PRI_MPS, PRI_EOP=0x7c,
+ DTC=0x81, CIG, NSC=0x84
+ } ;
+
+enum commanddtype { RCV=0, SND=1, DTA=0, TRN=1 } ;
+
+/* Class 1 commands to [receive=0/transmit=1] [data=0/training=1] for
+ [baud rate=BR]. */
+
+char *c1cmd [ 2 ] [ 2 ] [ 8 ] = {
+{ { "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=122", "+FRM=146" ,
+ "+FRM=74", "+FRM=98" } ,
+ { "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=121", "+FRM=145" ,
+ "+FRM=73", "+FRM=97" } } ,
+{ { "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=122", "+FTM=146" ,
+ "+FTM=74", "+FTM=98", } ,
+ { "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=121", "+FTM=145" ,
+ "+FTM=73", "+FTM=97" } }
+} ;
+
+struct c2msgstruct
+{
+ int min, max ;
+ char *msg ;
+} c2msg [] = {
+ { 0, 9, "Call Placement and Termination:" },
+ { 0, 0, " Normal and proper end of connection" },
+ { 1, 1, " Ring Detect without successful handshake" },
+ { 2, 2, " Call aborted, from +FK[S] or CAN" },
+ { 3, 3, " No Loop Current" },
+ { 4, 4, " Ringback Detected, no answer" },
+ { 5, 5, " Ringback Detected, answer without CED" },
+
+ { 10, 19, "Transmit Phase A & Miscellaneous Errors:" },
+ { 10, 10, " Unspecified Phase A error" },
+ { 11, 11, " No Answer (T.30 T1 timeout)" },
+ { 20, 39, "Transmit Phase B Hangup Codes:" },
+ { 20, 20, " Unspecified Transmit Phase B error" },
+ { 21, 21, " Remote cannot receive or send" },
+ { 22, 22, " COMREC error in transmit Phase B" },
+ { 23, 23, " COMREC invalid command received" },
+ { 24, 24, " RSPREC error" },
+ { 25, 25, " DCS sent three times without response" },
+ { 26, 26, " DIS/DTC received 3 times; DCS not recognized" },
+ { 27, 27, " Failure to train at 2400 bps or +FMINSP value" },
+ { 28, 28, " RSPREC invalid response received" },
+ { 40, 49, "Transmit Phase C Hangup Codes:" },
+ { 40, 40, " Unspecified Transmit Phase C error" },
+ { 41, 41, " Unspecified image format error" },
+ { 42, 42, " Image conversion error" },
+ { 43, 43, " DTE to DCE data underflow" },
+ { 44, 44, " Unrecognized transparent data command" },
+ { 45, 45, " Image error, line length wrong" },
+ { 46, 46, " Image error, page length wrong" },
+ { 47, 47, " Image error, wrong compression code" },
+ { 50, 69, "Transmit Phase D Hangup Codes:" },
+ { 50, 50, " Unspecified Transmit Phase D error" },
+ { 51, 51, " RSPREC error" },
+ { 52, 52, " No response to MPS repeated 3 times" },
+ { 53, 53, " Invalid response to MPS" },
+ { 54, 54, " No response to EOP repeated 3 times" },
+ { 55, 55, " Invalid response to EOP" },
+ { 56, 56, " No response to EOM repeated 3 times" },
+ { 57, 57, " Invalid response to EOM" },
+ { 58, 58, " Unable to continue after PIN or PIP" },
+
+ { 70, 89, "Receive Phase B Hangup Codes:" },
+ { 70, 70, " Unspecified Receive Phase B error" },
+ { 71, 71, " RSPREC error" },
+ { 72, 72, " COMREC error" },
+ { 73, 73, " T.30 T2 timeout, expected page not received" },
+ { 74, 74, " T.30 T1 timeout, after EOM received" },
+ { 90, 99, "Receive Phase C Hangup Codes:" },
+ { 90, 90, " Unspecified Receive Phase C error" },
+ { 91, 91, " Missing EOL after 5 seconds" },
+ { 92, 92, " Unused code" },
+ { 93, 93, " DCE to DTE buffer overflow" },
+ { 94, 94, " Bad CRC or frame (ECM or BFT modes)" },
+ { 100, 119, "Receive Phase D Hangup Codes:" },
+ { 100, 100, " Unspecified Receive Phase D errors" },
+ { 101, 101, " RSPREC invalid response received" },
+ { 102, 102, " COMREC invalid response received" },
+ { 103, 103, " Unable to continue after PIN or PIP" },
+ { 120, 255, "Reserved Codes" },
+ { -1, -1, "" }
+} ;
+
+/* meaning of efax return codes */
+
+char *errormsg [] = {
+ "success",
+ "number busy or modem in use",
+ "unrecoverable error",
+ "invalid modem response",
+ "no response from modem",
+ "terminated by signal",
+ "internal error" } ;
+
+/* Functions... */
+
+/* Return name of frame of type 'fr'. */
+
+char *frname ( int fr )
+{
+ static struct framenamestruct { int code ; char *name ; }
+ framenames [] = {
+
+ {NSC,"NSC - poller features"}, /* these 3 frames must be first */
+ {CIG,"CIG - poller ID"},
+ {DTC,"DTC - poller capabilities"},
+ {NSF,"NSF - answering features"},
+ {CSI,"CSI - answering ID"},
+ {DIS,"DIS - answering capabilities"},
+ {NSS,"NSS - caller features"},
+ {TSI,"TSI - caller ID"},
+ {DCS,"DCS - session format"},
+
+ {CFR,"CFR - channel OK"},
+ {FTT,"FTT - channel not OK"},
+
+ {MPS,"MPS - not done"},
+ {EOM,"EOM - not done, new format"},
+ {EOP,"EOP - done"},
+
+ {PRI_MPS,"PRI-MPS - not done, call operator"},
+ {PRI_EOM,"PRI-EOM - not done, new format, call operator"},
+ {PRI_EOP,"PRI-EOP - done, call operator"},
+
+ {MCF,"MCF - page OK"},
+ {RTP,"RTP - page OK, check channel"},
+ {PIP,"PIP - page OK, call operator"},
+ {RTN,"RTN - page not OK, check channel"},
+ {PIN,"PIN - page not OK, call operator"},
+
+ {CRP,"CRP - repeat command"},
+ {DCN,"DCN - disconnect"},
+ {0,0} },
+ *p ;
+
+ for ( p=framenames ; p->code ; p++ )
+ if ( fr == p->code || ( fr & 0x7f ) == p->code) break ;
+ return p->code ? p->name : "UNKNOWN" ;
+}
+
+/* Range-check capability. */
+
+int checkcap ( cap c )
+{
+ int err=0, i ;
+
+ for ( i=0 ; i<NCAP ; i++ )
+ if ( c[i] > capmax[i] || c[i] < 0 ) {
+ err = msg ( "E3%s = %d out of range, set to 0", t30tab[i].name, c[i] ) ;
+ c[i]=0 ;
+ }
+ return err ;
+}
+
+
+/* Print cap[ability] c using text values and prefix s. */
+
+void printcap ( char *s , cap c )
+{
+ int i ;
+ msg ( "N-+ %s" , s ) ;
+ checkcap ( c ) ;
+ for ( i=0 ; i<NCAP ; i++ )
+ msg ( "N-+ %s" , capvaluestr [ i ] [ c[i] ] ) ;
+ msg ( "N-" ) ;
+}
+
+
+/* Convert capability string to cap struct. Returns 0 or 2 on errors. */
+
+int str2cap ( char *s, cap c )
+{
+ int err=0, n ;
+
+ n = sscanf ( s, "%d,%d,%d,%d,%d,%d,%d,%d",
+ c+0, c+1, c+2, c+3, c+4, c+5, c+6, c+7 ) ;
+
+ if ( n < NCAP ) msg ( "Wmissing value(s) in \"%s\"", s ) ;
+
+ checkcap ( c ) ;
+
+ return err ;
+}
+
+
+/* Convert a cap[ability] 'c' to a DIS/DCS/DTC FIF 'fif' of 'len'
+ bytes. Converts into DIS format if 'isdis' is true, else into
+ DCS/DTC format. */
+
+void mkdis ( cap c, uchar *fif, int len, int isdis, int t4tx )
+{
+ int i, k ;
+ t30tabst *p ;
+
+ len = len > DCSLEN ? DCSLEN : len ;
+
+ fif[0] = 0 ;
+ fif[1] = ( isdis && t4tx ? 0x80 : 0 ) | 0x40 ;
+ for ( i=2 ; i<len-1 ; i++ ) fif[i] = 0x01 ; /* add extension bits */
+ fif[i] = 0 ;
+
+ checkcap ( c ) ;
+
+ for ( i=0 ; i<NCAP ; i++ ) {
+ p = t30tab + i ;
+ if ( ( k = ( isdis ? p->captodis : p->captodcs ) [ c [ i ] ] ) == X )
+ msg ( "E3mkdis: can't happen (invalid %s)", p->name ), k=0 ;
+ if ( p->byte < len ) fif [ p->byte ] |= k << p->shift ;
+ }
+}
+
+
+/* Return length of DIS/DTC FIF (counts extension bits). */
+
+int dislen ( uchar *fif )
+{
+ int n ;
+ for ( n=3 ; fif [ n-1 ] & 0x01 && n < MAXFIFLEN ; n++ ) ;
+ return n ;
+}
+
+
+/* Convert received DIS/DCS/DTC FIF to cap. Returns 0 or 3 if bad DIS/DCS
+ field. */
+
+int mkcap ( uchar *fif, cap c, int dis )
+{
+ int err=0, i, j, k, len ;
+ t30tabst *p ;
+
+ len = dislen ( fif ) ;
+
+ for ( i=0 ; i<NCAP ; i++ ) {
+ p=t30tab+i ;
+ if ( p->byte >= len ) {
+ c [ i ] = 0 ;
+ } else {
+ j = ( fif [ p->byte ] >> p->shift ) & p->mask ;
+ k = ( dis ? p->distocap : p->dcstocap ) [ j ] ;
+ if ( k == X ) {
+ c [ i ] = p->safeval ;
+ err = msg("E3mkcap: bad %s field (%d) set to %d",
+ p->name, j, c [ i ] ) ;
+ } else {
+ c [ i ] = k ;
+ }
+ }
+ }
+ return err ;
+}
+
+
+/* Compute compatible local/remote capabilities. Used by the
+ sending station only and only for Class 1. Returns 0 if OK or
+ 3 if no compatible settings possible. */
+
+int mincap ( cap local, cap remote, cap session )
+{
+ int err=0, i ;
+ int msttab[2][8] = { { 0,1,3,3,5,5,7,7 } , { 0,1,1,3,3,5,5,7 } } ;
+
+ printcap ( "local ", local ) ;
+ printcap ( "remote ", remote ) ;
+
+ for ( i=0 ; i<NCAP && i!=ST && i !=BR ; i++ )
+ session[i] = remote[i] < local[i] ? remote[i] : local[i] ;
+
+ session[BR] = brindex[ remote[BR] ] < brindex[ local[BR] ] ?
+ remote[BR] : local[BR] ;
+
+ session[ST] = msttab [ session[VR] ] [ remote[ST] ] ;
+
+ printcap ( "session", session ) ;
+
+ if ( local[WD] != session[WD] || local[LN] > session[LN] ||
+ local[DF] != session[DF] )
+ err = msg ("W3incompatible local and remote capabilities" ) ;
+
+ return err ;
+}
+
+
+/* Skip to start of first/next page (or to start of previous page
+ if dp is 0). If ppm in not null, it is then set to EOP if
+ there are no pages following this one, MPS if the next page
+ has the same format as `local' (assumed to be the format of
+ the previous page), EOM if the page has a different format.
+ If local is non-NULL its format fields are set according to
+ the format of the new page. Currently only considers the
+ file's y-resolution.
+
+ This function is called before send_data() and obtains the ppm
+ for that page. It can be called again with dp=0 if a PIN or
+ RTN is received to restart the page. Returns 0 or 2 on
+ errors. */
+
+int rdpage ( IFILE *f, int dp, int *ppm, cap local, int *changed )
+{
+ int err=0, m=EOP, yres, fVR, nVR ;
+
+ if ( nextipage ( f, dp ) )
+ err = msg ( "E2 can't happen (rdpage: can't go to %s page)",
+ dp ? "next" : "same" ) ;
+
+ if ( ! err ) {
+
+ yres = f->page->yres ;
+ fVR = ( yres > (196+98)/2 ) ? 1 : 0 ;
+
+ if ( local && yres ) {
+ if ( local [ VR ] != fVR ) {
+ local [ VR ] = fVR ;
+ if ( changed ) *changed = 1 ;
+ } else {
+ if ( changed ) *changed = 0 ;
+ }
+ }
+
+ if ( lastpage ( f ) ) {
+ m = EOP ;
+ } else {
+ PAGE *p = f->page + 1 ;
+ nVR = ( p->yres > (196+98)/2 ) ? 1 : 0 ;
+ m = ( nVR != fVR ) ? EOM : MPS ;
+ }
+
+ }
+
+ if ( ppm ) {
+ *ppm = err ? EOP : m ;
+ }
+
+ return err ;
+}
+
+
+/* Terminate previous page if page number is non-zero and start
+ next output page if page number is non-negative. If page is -1
+ removes the most recently opened file. Returns 0 if OK, 2 on
+ errors. */
+
+int wrpage ( OFILE *f, int page )
+{
+ int err=0 ;
+
+ err = nextopage ( f, page ) ;
+
+ if ( ! err && page == -1 ) {
+ if ( remove ( f->cfname ) ) {
+ err = msg ( "ES2can't delete file %s:", f->cfname ) ;
+ } else {
+ msg ( "Fremoved %s", f->cfname ) ;
+ }
+ }
+
+ return err ;
+}
+
+
+/* Send data for one page. Figures out required padding and 196->98 lpi
+ decimation based on local and session capabilitites, substitutes page
+ numbers in header string and enables serial port flow control. Inserts
+ the page header before the input file data. Converts each scan line to
+ T.4 codes and adds padding (FILL) and EOL codes before writing out.
+ Sends RTC when done. Sends DLE-ETX and returns serial port to command
+ mode when done. Returns 0 if OK, non-0 on errors. */
+
+int send_data ( TFILE *mf, IFILE *f, int page, int pages,
+ cap local, cap session, char *header, faxfont *font )
+{
+ int done=0, err=0, noise=0, nr=0, lastnr=0, line, pixels ;
+ int i, decimate, pwidth, minlen, dcecps, inheader, skip=0 ;
+ uchar buf [ MAXCODES + 2*EOLBITS/8 + 1 ], *p ;
+ short runs [ MAXRUNS ], lastruns [ MAXRUNS ] ;
+ char headerbuf [ MAXLINELEN ] ;
+ ENCODER e ;
+
+ newENCODER ( &e ) ;
+
+ dcecps = cps[session[BR]] ;
+ minlen = ( (long)dcecps * mst[session[ST]] - 1500 + 500 ) / 1000 ;
+ pwidth = pagewidth [ session [ WD ] ] ;
+ decimate = local[VR] > session[VR] ;
+
+ msg ( "T padding to %d bytes/scan line.%s", minlen+1,
+ decimate ? " reducing 196->98 lpi." : "" ) ;
+
+ if ( vfc )
+ msg ( "T limiting output to %d bps for %d byte modem buffer",
+ dcecps*8, MAXDCEBUF + MINWRITE ) ;
+
+ if ( ckfmt ( header, 6 ) )
+ msg ( "W too many %%d escapes in header format string \"%s\"", header ) ;
+ else
+ sprintf ( headerbuf, header, page, pages, page, pages, page, pages ) ;
+ msg ("I header:[%s]", headerbuf ) ;
+
+ done = err = ttymode ( mf, SEND ) ;
+
+ mf->start = time(0) ;
+ mf->mstart = proc_ms() ;
+ mf->bytes = mf->pad = mf->lines = 0 ;
+
+ /* start T.4 data with some FILL and an EOL */
+
+ for ( i=0 ; i<32 ; i++ )
+ p = putcode ( &e, 0, 8, buf ) ;
+ p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
+
+ if ( ! f || ! f->f )
+ err = msg ( "E2can't happen(send_data)" ) ;
+
+ mf->lines=0 ;
+ for ( line=0 ; ! done && ! err ; line++ ) {
+
+ if ( line < HDRSPCE ) { /* insert blank lines at the top */
+ runs[0] = pwidth ;
+ pixels = pwidth ;
+ nr = 1 ;
+ } else {
+ if ( ( nr = readline ( f, runs, &pixels ) ) < 0 ) {
+ done = 1 ;
+ continue ;
+ }
+ }
+ /* generate and OR in header pixels */
+ if ( line >= HDRSTRT && line < HDRSTRT + HDRCHRH ) {
+ int hnr ;
+ short hruns [ MAXRUNS ] ;
+ hnr = texttorun ( (uchar*) headerbuf, font, line-HDRSTRT,
+ HDRCHRW, HDRCHRH, HDRSHFT,
+ hruns, 0 ) ;
+ nr = runor ( runs, nr, hruns, hnr, 0, &pixels ) ;
+ }
+
+ inheader = line < HDRSTRT + HDRCHRH ;
+
+ if ( decimate || ( inheader && local[VR] == 0 ) ) {
+ if ( ++skip & 1 ) { /* save the first of every 2 lines */
+ memcpy ( lastruns, runs, nr * sizeof(short) ) ;
+ lastnr = nr ;
+ continue ; /* get next line */
+ } else { /* OR previous line into current line */
+ nr = runor ( runs, nr, lastruns, lastnr, 0, &pixels ) ;
+ }
+ }
+
+ if ( nr > 0 ) {
+ if ( pixels ) {
+ /* make line the right width */
+ if ( pixels != pwidth ) nr = xpad ( runs, nr, pwidth - pixels ) ;
+ /* convert to MH coding */
+ p = runtocode ( &e, runs, nr, p ) ;
+ /* zero pad to minimum scan time */
+ while ( p - buf < minlen ) {
+ p = putcode ( &e, 0, 8, p ) ;
+ mf->pad ++ ;
+ }
+ /* add EOL */
+ p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
+ sendbuf ( mf, buf, p - buf, dcecps ) ;
+ mf->bytes += p - buf ;
+ mf->lines++ ;
+ } else {
+ /* probably read an EOL as part of RTC */
+ }
+ if ( tdata ( mf, 0 ) ) noise = 1 ;
+ p = buf ;
+ }
+ }
+
+ for ( i=0 ; i < RTCEOL ; i++ )
+ p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
+ p = putcode ( &e, 0, 0, p ) ;
+ sendbuf ( mf, buf, p - buf, dcecps ) ;
+ mf->bytes += p - buf ;
+
+ if ( noise ) msg ("W- characters received while sending" ) ;
+
+ return err ;
+}
+
+
+int end_data ( TFILE *mf, cap session, int ppm, int *good )
+{
+ int err=0, c ;
+ uchar *p ;
+ long dt, draintime ;
+
+ if ( ! ppm ) p = DLE_ETX ;
+ else if ( ppm == MPS ) p = "\020," ;
+ else if ( ppm == EOM ) p = "\020;" ;
+ else if ( ppm == EOP ) p = "\020." ;
+ else {
+ p = "" ;
+ err = msg ( "E2 can't happen (end_data)" ) ;
+ }
+
+ tput ( mf, p, 2 ) ;
+
+ dt = time(0) - mf->start ;
+ /* time to drain buffers + 100% + 4s */
+ draintime = ( 2 * ( mf->bytes / cps[ session[BR] ] + 1 - dt ) + 4 ) * 10 ;
+ draintime = draintime < TO_DRAIN_D ? TO_DRAIN_D : draintime ;
+
+ c = ckcmd ( mf, 0, 0, (int) draintime, OK ) ;
+
+ if ( good ) *good = ( c == OK ) ? 1 : 0 ;
+
+ dt = time(0) - mf->start ;
+
+ msg ( "Isent %d+%d lines, %d+%d bytes, %d s %d bps" ,
+ HDRSPCE, mf->lines-HDRSPCE,
+ mf->bytes-mf->pad, mf->pad, (int) dt, (mf->bytes*8)/dt ) ;
+
+ if ( mf->bytes / (dt+1) > cps[session[BR]] )
+ msg ( "E flow control did not work" ) ;
+
+ if ( ! err ) err = ttymode ( mf, COMMAND ) ;
+
+ return err ;
+}
+
+
+/* Read one scan line from fax device. If pointer pels is not
+ null it is used to save pixel count. Returns number of runs
+ stored, EOF on RTC, or -2 on EOF, DLE-ETX or other error. */
+
+int readfaxruns ( TFILE *f, DECODER *d, short *runs, int *pels )
+{
+ int err=0, c=EOF, x, n ;
+ dtab *tab, *t ;
+ short shift ;
+ short *p, *maxp, *q, len=0 ;
+ uchar rd_state ;
+
+ maxp = ( p = runs ) + MAXRUNS ;
+
+ x = d->x ; shift = d->shift ; tab = d->tab ; /* restore decoder state */
+ rd_state = f->rd_state ;
+
+ do {
+ do {
+ while ( shift < 0 ) {
+ c = tgetd ( f, TO_CHAR ) ;
+
+ rd_state = ( rd_state & rd_allowed[c] ) ?
+ ( ( rd_state & rd_nexts[c] ) ? rd_state <<= 1 : rd_state ) :
+ RD_BEGIN ;
+
+ if ( rd_state == RD_END )
+ msg ( "W+ modem response in data" ) ;
+
+ if ( c < 0 ) {
+ x = ( x << 15 ) | 1 ; shift += 15 ; /* EOL pad at EOF */
+ } else {
+ x = ( x << 8 ) | c ; shift += 8 ;
+ }
+ }
+ t = tab + ( ( x >> shift ) & 0x1ff ) ;
+ tab = t->next ;
+ shift -= t->bits ;
+ } while ( ! t->code ) ;
+ if ( p < maxp ) *p++ = t->code ;
+ } while ( t->code != -1 ) ;
+
+ d->x = x ; d->shift = shift ; d->tab = tab ; /* save state */
+ f->rd_state = rd_state ;
+
+ if ( p >= maxp ) msg ( "Wrun length buffer overflow" ) ;
+
+ /* combine make-up and terminating codes and remove +1 offset
+ in run lengths */
+
+ n = p - runs - 1 ;
+ for ( p = q = runs ; n-- > 0 ; )
+ if ( *p > 64 && n-- > 0 ) {
+ len += *q++ = p[0] + p[1] - 2 ;
+ p+=2 ;
+ } else {
+ len += *q++ = *p++ - 1 ;
+ }
+ n = q - runs ;
+
+ /* check for RTC and errors */
+
+ if ( len )
+ d->eolcnt = 0 ;
+ else
+ if ( ++(d->eolcnt) >= RTCEOL ) err = EOF ;
+
+ if ( c < 0 ) err = - 2 ;
+
+ if ( pels ) *pels = len ;
+
+ return err ? err : n ;
+}
+
+
+/* Receive data. Reads scan lines from modem and writes to output
+ file. Checks for errors by comparing received line width and
+ session line width. Check that the output file is still OK
+ and if not, send one CANcel character and wait for protocol to
+ complete. Returns 0 if OK or 2 if there was a file write error. */
+
+int receive_data ( TFILE *mf, OFILE *f, cap session, int *nerr )
+{
+ int err=0, line, lines, nr, len ;
+ int pwidth = pagewidth [ session [ WD ] ] ;
+ short runs [ MAXRUNS ] ;
+ DECODER d ;
+
+ if ( ! f || ! f->f ) {
+ msg ( "E2 can't happen (writeline)" ) ;
+ }
+
+ newDECODER ( &d ) ;
+ *nerr = 0 ;
+
+ lines=0 ;
+ for ( line=0 ; ( nr = readfaxruns ( mf, &d, runs, &len ) ) >= 0 ; line++ ) {
+ if ( nr > 0 && len > 0 && line ) { /* skip first line+EOL and RTC */
+ if ( len != pwidth ) {
+ (*nerr)++ ;
+ if ( *nerr <= MAXERRPRT ) msg ("R-+ (%d:%d)", line, len ) ;
+ nr = xpad ( runs, nr, pwidth - len ) ;
+ }
+ writeline ( f, runs, nr, 1 ) ;
+ lines++ ;
+ }
+ if ( ferror ( f->f ) ) {
+ err = msg ("ES2file write:") ;
+ tput ( mf, CAN_STR, 1 ) ;
+ msg ("Wdata reception CANcelled") ;
+ }
+ }
+
+ if ( *nerr ) {
+ if ( *nerr > MAXERRPRT ) msg ("R-+ ....." ) ;
+ msg ("R- : reception errors" ) ;
+ msg ("W- %d reception errors", *nerr ) ;
+ }
+
+ if ( nr == EOF )
+ while ( tgetd ( mf, TO_CHAR ) >= 0 ) ; /* got RTC, wait for DLE-ETX */
+
+ msg ( "I- received %d lines, %d errors", lines, *nerr ) ;
+
+ return err ;
+}
+
+
+/* Send training check sequence of n zeroes. Returns 0 or 2 on error. */
+
+int puttrain ( TFILE *f, char *s, int n )
+{
+ int i, m, err=0 ;
+ uchar buf [ MINWRITE ] = { 0 } ;
+
+ ckcmd ( f, &err, s, TO_FT, CONNECT ) ;
+
+ if ( ! err ) {
+ ttymode ( f, SEND ) ;
+
+ for ( i=0 ; i < n ; i += m ) {
+ m = n-i < MINWRITE ? n-i : MINWRITE ;
+ sendbuf ( f, buf, m, 0 ) ;
+ }
+
+ buf[0] = 1 ; /* make sure last byte is non-zero */
+ buf[1] = DLE ;
+ buf[2] = ETX ;
+ sendbuf ( f, buf, 3, 0 ) ;
+
+ ttymode ( f, COMMAND ) ;
+
+ ckcmd ( f, &err, 0, TO_DRAIN_D, OK ) ;
+ msg ( "I- sent TCF - channel check of %d bytes", n ) ;
+ }
+
+ return err ;
+}
+
+
+/* Checks for an error-free run of at least n bytes in the
+ received training check sequence. Sets good if it's not null,
+ the run was long enough and there were no errors. Returns 0 or
+ 3 on other errors. */
+
+int gettrain ( TFILE *f, char *s, int n, int *good )
+{
+ int err=0, c, i=0, maxrunl=0, runl=0 ;
+
+ ckcmd ( f, &err, s, T2, CONNECT ) ;
+
+ if ( ! err ) {
+
+ for ( i=0 ; ( c = tgetd ( f, T3S ) ) >= 0 ; i++ )
+ if ( c ) {
+ if ( runl > maxrunl ) maxrunl = runl ;
+ runl = 0 ;
+ } else {
+ runl ++ ;
+ }
+
+ if ( c == EOF )
+ err = msg ( "E3timed out during training check data" ) ;
+ else
+ ckcmd ( f, &err, 0, TO_RTCMD, NO ) ;
+
+ }
+
+ if ( runl > maxrunl ) maxrunl = runl ;
+
+ if ( good ) *good = !err && maxrunl > n ;
+
+ if ( !err ) {
+ msg ( "I- received TCF - channel check (%sOK: run of %d in %d)",
+ maxrunl > n ? "" : "not ", maxrunl, i ) ;
+ }
+
+ return err ;
+}
+
+
+/* Log a sent/received HDLC frame. Display of these messages is delayed to
+ avoid possible timing problems. */
+
+void logfr ( char *s , char *nm , uchar *p , int n )
+{
+ int i=0 ;
+ msg ( n > 10 ? "H- %s %d bytes:" : "H-+ %s %d bytes:" , s, n ) ;
+ for ( i=0 ; i<n ; i++ ) {
+ msg ( "H-+ %02x" , p[i] & 0xff ) ;
+ if ( ( i&0xf ) == 0xf && i != n-1 ) msg ( "H-" ) ;
+ }
+ msg ( "H-" ) ;
+ msg ( "I- %s %s", s, nm ) ;
+}
+
+
+/* Send HDLC control frame of type type. Extra bits can be OR'ed
+ into the frame type (FCF) to indicate that this frame follows
+ a previous one (no +FTH required) and/or that more frames will
+ follow. Sets up flag, address, and fax control field bytes in
+ `buf'. Sends these plus `len` additional bytes. Terminates
+ with DLE-ETX and checks response. Returns 0 if OK, 2 or 3 on
+ error. */
+
+#define MORE_FR 0x100
+#define SUB_FR 0x200
+
+int nframes = 0 ; /* counts frames sent/received */
+
+int putframe ( int type, uchar *buf, int len, TFILE *f, int t )
+{
+ int err=0 ;
+
+ buf [ 0 ] = 0xff ;
+ buf [ 1 ] = type & MORE_FR ? 0xc0 : 0xc8 ;
+ buf [ 2 ] = type & 0xff ;
+
+ if ( nframes++ && ! ( type & SUB_FR ) )
+ ckcmd ( f, &err, "+FTH=3" , TO_FT, CONNECT ) ;
+
+ if ( ! err ) {
+
+ if ( ! buf[len+2] ) {
+ msg ( "Wlast byte of frame is NULL" ) ;
+ }
+
+ /* ttymode ( f, SEND ) ; */
+ sendbuf ( f, buf, len+3, 0 ) ;
+ tput ( f, DLE_ETX, 2 ) ;
+ /* ttymode ( f, COMMAND ) ; */
+
+ logfr ( "sent", frname ( buf [ 2 ] ), buf, len+3 ) ;
+
+ ckcmd ( f, &err, 0, TO_DRAIN_H, ( type & MORE_FR ) ? CONNECT : OK ) ;
+ }
+
+ return err ;
+}
+
+
+/* Reverse bit and byte order of ID strings as per T.30 5.3.6.2.4-6 */
+
+void revcpy ( uchar *from , uchar *to )
+{
+ int i, j ;
+ for ( i=0, j=IDLEN-1 ; i<IDLEN ; i++, j-- )
+ to [ i ] = normalbits [ from [ j ] & 0xff ] ;
+}
+
+
+/* Check for missing initial 0xFF in HDLC frame and insert it if
+ missing. Ugly fix for a still-hidden bug. Also do bit
+ inversion if required. */
+
+int fixframe ( uchar *buf, int n, TFILE *f )
+{
+ int i;
+
+ if ( *buf == 0xc0 || *buf == 0xc8 ) {
+ for ( i=n; i >= 1 ; i-- )
+ buf[i]=buf[i-1] ;
+ buf[i] = 0xff ;
+ msg ("W HDLC frame missing initial 0xff" ) ;
+ n++ ;
+ }
+
+ if ( buf[1] == 0x03 || buf[1] == 0x13 ) {
+ for ( i=0 ; i < n ; i++ )
+ buf[i]=normalbits[buf[i]] ;
+ msg ("W bit-reversed HDLC frame, reversing bit order" ) ;
+ f->ibitorder = f->ibitorder == normalbits ? reversebits : normalbits ;
+ }
+
+ return n ;
+}
+
+
+/* Read HDLC frame data. Returns 0 if OK, 1 on frame error, 3 on
+ timeout, invalid response or too-long frame. */
+
+int receive_frame_data ( TFILE *f, uchar *buf, int n, int *len )
+{
+ int err=0, c, i ;
+
+ for ( i=0 ; ( c = tgetd ( f, T3S ) ) >= 0 ; i++ )
+ if ( i < n ) buf[ i ] = c ;
+
+ if ( c == EOF ) {
+
+ err = msg ( "E3timed out reading frame data" ) ;
+
+ } else {
+
+ switch ( cmd ( f, 0, TO_RTCMD ) ) {
+ case OK:
+ case CONNECT:
+ break ;
+ case ERROR:
+ case NO:
+ err = msg ( "W1frame error" ) ;
+ break ;
+ case EOF:
+ err = msg ( "E3no response after frame data" ) ;
+ break ;
+ default:
+ err = msg ( "E3wrong response after frame data" ) ;
+ break ;
+ }
+
+ }
+
+ if ( i >= n )
+ err = msg ( "E3frame too long (%d, > %d max bytes)", i, n ) ;
+
+ if ( len ) *len = i ;
+
+ return err ;
+}
+
+
+/* Get a Class 1 command or response frame. An attempt to match
+ and combine T.30 "Response Received?" and "Command Received?"
+ protocol flowcharts.
+
+ When receiving commands returns after first correct
+ non-optional frame or after the time given by getcmd has
+ elapsed. This is instead of looping through main flowchart.
+
+ When receiving responses returns on the first detected
+ non-optional frame, after timeout T4, or on errors.
+
+ Returns immediately if gets a +FCERROR response so can retry
+ as data carrier. Returns DCN as a valid frame instead of
+ hanging up.
+
+ Returns the command/response received, or EOF on timeout or
+ error.
+
+*/
+
+int getfr ( TFILE *mf, uchar *buf, int getcmd )
+{
+ int err=0, frame=0, frlen, c, t ;
+ char remoteid [ IDLEN + 1 ] ;
+ time_t start ;
+ uchar *fif=buf+3 ;
+
+ start = 10*time(0) ;
+
+ t = getcmd ? ( getcmd > 1 ? getcmd : T2 ) : T4 ;
+
+ Enter:
+
+ err = 0 ;
+
+ if ( nframes++ ) {
+ c = cmd ( mf, "+FRH=3", t ) ;
+ } else {
+ c = CONNECT ; /* implied by ATA or ATD */
+ }
+
+ switch ( c ) {
+ case EOF: /* time out */
+ tput ( mf, CAN_STR, 1 ) ;
+ ckcmd ( mf, 0, 0, TO_ABRT, OK ) ;
+ err = 1 ;
+ break ;
+ case NO: /* S7 time out */
+ err = 1 ;
+ break ;
+ case MODULATION: /* data carrier (or DHS) */
+ return -msg ( "W-2 wrong carrier" ) ;
+ break ;
+ case CONNECT: /* frame */
+ break ;
+ default: /* shouldn't happen */
+ err = msg ( "E3wrong response to receive-frame command" ) ;
+ break ;
+ }
+
+ if ( ! err )
+ err = receive_frame_data ( mf, buf, MAXFRLEN, &frlen ) ;
+
+ if ( ! err && frlen < 3 )
+ err = msg ( "E3received short frame (%d bytes)", frlen ) ;
+
+ if ( ! err ) {
+
+ frlen = fixframe ( buf, frlen, mf ) ;
+ logfr ( "received", frname ( buf [ 2 ] ), buf, frlen ) ;
+ frame = buf [ 2 ] & 0x7f ;
+
+ switch ( frame ) {
+ case CRP:
+ err = 1 ;
+ case NSF:
+ case NSC:
+ case NSS:
+ goto Enter ;
+ case CIG:
+ case CSI:
+ case TSI:
+ revcpy ( fif , (uchar*) remoteid ) ;
+ msg ( "I- remote ID -> %*.*s", IDLEN, IDLEN, remoteid ) ;
+ goto Enter ;
+ }
+
+ }
+
+ if ( err && getcmd && ( t -= 10*time(0) - start ) > 0 )
+ goto Enter ;
+
+ return err ? EOF : frame ;
+}
+
+
+/* Class 1 send/receive.
+
+ The logic in this function is a mess because it's meant to
+ mirror the flowchart in ITU-T recommendation T.30 which is the
+ protocol specification.
+
+ */
+
+int c1sndrcv (
+ TFILE *mf, cap local, char *localid,
+ OFILE *outf, IFILE *inf,
+ int pages, char *header, faxfont *font,
+ int maxpgerr, int noretry, int calling )
+{
+ int err=0, rxpage=0, page=1, t, disbit, good, frame, last, nerr ;
+ int rxdislen, ppm, try=0, pagetry=0, retry=0, remtx=0, remrx=0 ;
+ int writepending=0, dp=0 ;
+ cap remote = { DEFCAP }, session = { DEFCAP } ;
+ char *fname=0 ;
+ uchar buf [ MAXFRLEN ], *fif=buf+3 ;
+
+ if ( ! calling ) goto RX ;
+
+ /* Class 1 Transmitter: */
+
+ T: /* Transmitter Phase B - wait for DIS or DTC */
+
+ pagetry = 0 ;
+
+ frame = getfr ( mf, buf, T1 ) ;
+
+ if ( frame <= 0 ) {
+ err = msg ( "E3no answer from remote fax" ) ;
+ goto B ;
+ }
+
+ if ( frame != DIS && frame != DTC ) {
+ msg ( "W2 can't open page" ) ;
+ goto C ;
+ }
+
+ disbit = ( frame == DIS ) ? 0x80 : 0x00 ;
+ try = 0 ;
+
+ A: /* decide to send or receive after DIS/DTC */
+
+ if ( frame == DIS || frame == DTC ) {
+ rxdislen = dislen ( fif ) ;
+ mkcap ( fif, remote, 1 ) ;
+ remtx = fif[1] & 0x80 ;
+ remrx = fif[1] & 0x40 ;
+ }
+
+ msg ( "N remote has %sdocument(s) to send, and can %sreceive",
+ remtx ? "" : "no ", remrx ? "" : "not " ) ;
+
+ if ( pages > 0 ) {
+ if ( ! remrx ) msg ( "W remote cannot receive, trying anyways" ) ;
+ goto D ;
+ } else {
+ if ( ! remtx ) msg ( "W remote has nothing to send, trying anyways" ) ;
+ goto R ;
+ }
+
+ D: /* send DCS */
+
+ if ( rdpage ( inf, dp, &ppm, local, 0 ) ) {
+ err = msg ( "E2can't open page" ) ;
+ goto B ;
+ }
+
+ D_2:
+
+ mincap ( local, remote, session ) ;
+
+ revcpy ( (uchar*) localid, fif ) ;
+ if ( ! err )
+ err = putframe ( TSI | MORE_FR | disbit, buf, IDLEN, mf, -1 ) ;
+
+ mkdis ( session, fif, DCSLEN, 0, pages ) ;
+ if ( ! err )
+ err = putframe ( DCS | SUB_FR | disbit, buf, DCSLEN, mf, -1 ) ;
+
+ if ( cmd ( mf, "+FTS=8", T3S ) != OK )
+ msleep ( TMOD ) ; /* if +FTS not supported */
+
+ if ( ! err )
+ err = puttrain ( mf, c1cmd[SND][TRN][session[BR]],
+ 1.5*cps [ session[BR] ] ) ;
+ try++ ;
+
+ if ( ! err ) {
+ cmd ( mf, "+FRS=1", T3S ) ; /* wait for TCF carrier to drop */
+ frame = getfr ( mf, buf, 0 ) ;
+ }
+
+ if ( err || frame < 0 ) {
+ if ( try >= 3 ) {
+ goto C_timeout ;
+ } else {
+ goto D_2 ;
+ }
+ }
+
+ switch ( frame ) {
+
+ case DIS:
+ case DTC:
+ if ( try >= 3 ) goto C_timeout ;
+ else goto A ;
+
+ case FTT:
+ msg ( "I channel not usable at %d bps", 8*cps[session[BR]] ) ;
+ remote[BR] = fallback[session[BR]] ;
+ if ( remote[BR] >= 0 ) goto D_2 ;
+ else { err = msg ( "E2 channel not usable at lowest speed" ) ; goto C ; }
+
+ case CFR:
+ goto I_2 ;
+
+ default:
+ err = msg ( "E3 invalid response to DCS (0x%02x)", frame ) ;
+ goto C ;
+ }
+
+ I: /* send a page */
+
+ if ( rdpage ( inf, dp, &ppm, local, 0 ) ) {
+ err = msg ( "E2can't open page" ) ;
+ goto B ;
+ }
+
+ I_2:
+
+ ckcmd ( mf, &err, c1cmd [SND][DTA][session[BR]], TO_FT, CONNECT ) ;
+ if ( !err )
+ err = send_data ( mf, inf, page, pages, local, session, header, font ) ;
+
+ pagetry++ ;
+
+ if ( !err )
+ err = end_data ( mf, session, 0, 0 ) ;
+
+ if ( cmd ( mf, "+FTS=8", T3S ) != OK )
+ msleep ( TMOD ) ; /* if +FTS not supported */
+
+ /* fix ppm if on last page of stdin */
+ if ( lastpage ( inf ) ) ppm = EOP ;
+
+ try = 0 ;
+ sendppm:
+ if ( !err ) err = putframe ( ppm | disbit, buf, 0, mf, -1 ) ;
+ try++ ;
+
+ frame = getfr ( mf, buf, 0 ) ;
+ if ( frame < 0 ) {
+ if ( try >= 3 ) {
+ goto C_timeout ;
+ } else {
+ goto sendppm ;
+ }
+ }
+
+ fname = inf->page->fname ;
+
+ switch ( noretry ? MCF : frame ) { /* common retry logic */
+ case MCF:
+ case RTP:
+ case PIP:
+ fname = inf->page->fname ;
+ if ( fname ) msg ( "Isent -> %s", fname ) ;
+ pagetry=0 ;
+ page++ ;
+ dp = 1 ;
+ break ;
+ case PIN:
+ case RTN:
+ dp = 0 ;
+ retry = pagetry < NTXRETRY ;
+ break ;
+ default:
+ err = msg ( "E3invalid post-page response (0x%02x)", frame ) ;
+ goto C ;
+ }
+
+ switch ( ppm ) {
+
+ case MPS:
+ switch ( frame ) {
+ case PIN: goto E ;
+ case PIP: goto E ;
+ case MCF: goto I ;
+ case RTP: goto D ;
+ case RTN: goto D ;
+ }
+
+ case EOP:
+ switch ( frame ) {
+ case PIN: goto E ;
+ case PIP: goto E ;
+ case MCF:
+ case RTP:
+ nextipage ( inf, 1 ) ; /* skip ahead to mark all files done */
+ if ( remtx ) goto R ; /* poll after sending */
+ else goto C ;
+ case RTN:
+ if ( retry ) goto D ;
+ else goto C ;
+ }
+
+ case EOM:
+ switch ( frame ) {
+ case PIN: goto E ;
+ case PIP: goto E ;
+ case MCF:
+ case RTP:
+ case RTN:
+ cmd ( mf, "+FRS=20", T3S ) ; /* wait for ppr carrier to drop */
+ if ( retry ) goto T ;
+ else goto T ;
+ }
+
+ }
+
+ E: /* ignore PIN and PIP */
+ msg ( "W interrupt request ignored" ) ;
+ try=0 ;
+ goto A ;
+
+ /* Class 1 Receiver */
+
+ RX:
+
+ R: /* Receiver Phase B */
+
+ if ( ! err ) err = wrpage ( outf, rxpage ) ;
+
+ disbit=0x00 ;
+
+ for ( t=0 ; !err && t<T1 ; t+=T2+10 ) {
+
+ revcpy ( (uchar*) localid, fif ) ;
+ if ( !err )
+ err = putframe ( CSI | disbit | MORE_FR, buf, IDLEN, mf, -1 ) ;
+
+ mkdis ( local, fif, DEFDISLEN, 1, pages ) ;
+ if ( !err )
+ err = putframe ( DIS | disbit | SUB_FR, buf, DEFDISLEN, mf, -1 ) ;
+
+ frame = getfr ( mf, buf, 0 ) ;
+
+ if ( frame > 0 ) {
+ disbit = ( frame == DIS ) ? 0x80 : 0x00 ;
+ goto F_2 ;
+ }
+ }
+ if ( err ) goto C ;
+ else goto C_timeout ;
+
+
+ F: /* get a command */
+
+ last = frame ;
+ frame = getfr ( mf, buf, 1 ) ;
+
+ if ( writepending ) { /* do postponed file close/open */
+ writepending=0 ;
+ err = wrpage ( outf, rxpage ) ;
+ if ( err ) goto C ;
+ }
+
+ if ( frame < 0 ) {
+ if ( frame == -2 ) goto getdata ; /* data carrier detected */
+ if ( last == EOM ) goto R ;
+ else { err = msg ("E3 timed out waiting for command" ) ; goto B ; }
+ }
+
+ F_2:
+
+ switch ( frame ) {
+
+ case DTC:
+ goto D ;
+
+ case DIS:
+ try=0 ;
+ goto A ;
+
+ case DCS:
+ mkcap ( fif, session, 0 ) ;
+ printcap ( "session", session ) ;
+
+ cmd ( mf, "+FTS=1", T3S ) ; /* make sure DCS is over */
+
+ gettrain ( mf, c1cmd [RCV][TRN][session[BR]], cps[session[BR]], &good ) ;
+
+ if ( putframe ( ( good ? CFR : FTT ) | disbit, buf, 0, mf, -1 ) ||
+ ! good ) goto F ;
+
+ getdata:
+
+ outf->w=pagewidth[session[WD]];
+ outf->h=0;
+ outf->xres=204.0;
+ outf->yres=vresolution[session[VR]];
+
+ if ( cmd ( mf, c1cmd [RCV][DTA][session[BR]], TO_FT ) != CONNECT )
+ goto F ; /* +FCERROR -> DCS resent */
+
+ if ( receive_data ( mf, outf, session, &nerr ) == 0 ) {
+ good = nerr < maxpgerr ;
+ msg ( "I-received -> %s", outf->cfname ) ;
+ writepending=1 ; /* ppm follows immediately, don't write yet */
+ rxpage++ ;
+ } else {
+ good = 0 ;
+ }
+ ckcmd ( mf, 0, 0, TO_RTCMD, NO ) ;
+ goto F ;
+
+ /* III: */
+
+ case PRI_EOM:
+ case PRI_MPS:
+ case PRI_EOP:
+ frame &=0xf7 ; /* ignore PRocedure Interrupt bit */
+ case MPS:
+ case EOP:
+ case EOM:
+ putframe ( ( good ? MCF : RTN ) | disbit, buf, 0, mf, -1 ) ;
+ if ( good && frame == MPS ) goto getdata ;
+ else goto F ;
+
+ case DCN:
+ goto B ;
+
+ default:
+ err = msg ( "E3 unrecognized command" ) ;
+ goto B ;
+
+ }
+
+ C_timeout:
+ err = msg ( "E3 no command/response from remote" ) ;
+
+ C:
+ putframe ( DCN, buf, 0, mf, -1 ) ;
+
+ B:
+ ckcmd ( mf, 0, "H", TO_RESET, OK ) ; /* hang up */
+
+ if ( rxpage > 0 )
+ wrpage ( outf, -1 ) ; /* remove last file */
+
+ return err ;
+}
+
+
+/* Check for hangup message. Assumes hsc is initialized to a
+ negative value. Returns 0 if no hangup message, 1 if there
+ was one. If perr is not null, sets it to 2 if the hsc was
+ non-zero (error). */
+
+int gethsc ( int *hsc, int *perr )
+{
+ int err=0, i ;
+ if ( sresponse ( "+FHNG:", hsc ) || sresponse ( "+FHS:", hsc ) ) {
+ if ( hsc && *hsc > 0 ) {
+ err = msg ( "E2abnormal termination (code %d)", *hsc ) ;
+ for ( i=0 ; c2msg[i].min >= 0 ; i++ ) {
+ if ( *hsc >= c2msg[i].min && *hsc <= c2msg[i].max ) {
+ msg ( "E %s", c2msg[i].msg ) ;
+ }
+ }
+ if ( perr && ! *perr ) {
+ *perr = 2 ;
+ }
+ } else {
+ err = 1 ;
+ }
+ }
+ return err ;
+}
+
+
+/* Print remote ID and store DCS values in session as per
+ responses since last command. */
+
+void getc2dcs ( cap session )
+{
+ char *p ;
+ if ( ( p = sresponse ( "+FTI:", 0 ) ) != 0 ||
+ ( p = sresponse ( "+FTSI:", 0 ) ) != 0 ) {
+ msg ( "I- remote ID -> %s", p ) ;
+ }
+ if ( ( p = sresponse ( "+FCS:", 0 ) ) != 0 ||
+ ( p = sresponse ( "+FDCS:", 0 ) ) != 0 ) {
+ str2cap ( p, session ) ;
+ printcap ( "session", session ) ;
+ }
+}
+
+/* Wait for a starting character XON or DC2. Display & ignore
+ any other characters received. */
+
+void getstartc ( TFILE *mf )
+{
+ int c, noise ;
+
+ for ( noise=0 ; ( c = tgetc ( mf, TO_C2X ) ) != XON && c != DC2 ; noise++ ) {
+ if ( c == EOF ) {
+ msg ( "Wno XON/DC2 received after CONNECT") ;
+ break ;
+ } else {
+ msg ( "W-+%s", cname ( c ) ) ;
+ noise++ ;
+ }
+ }
+
+ if ( noise )
+ msg ( "W : %d characters received while waiting to send", noise ) ;
+}
+
+
+/* Class 2 send and receive.
+
+ If calling, polls if no files to send, otherwise sends. If
+ not calling sends documents if files to send, else receives.
+
+ When sending, issues +FDIS to change session parameters if
+ file format changes, then sends +FDT followed by data and a
+ post-page message determined by format of next page, if any.
+ Retransmits each page up to NTXRETRY times.
+
+ When receiving extracts file format from responses to +FDR or
+ ATA and saves them in the file. Receives data to a file and
+ sets page transfer status if too many errors.
+
+ Returns 0 if OK or 2 on errors. */
+
+
+int c2sndrcv (
+ TFILE *mf, cap local, char *localid,
+ OFILE *outf, IFILE *inf,
+ int pages, char *header, faxfont *font,
+ int maxpgerr, int noretry, int calling )
+{
+ int err=0, done=0, page, pagetry, nerr, c, dp=0 ;
+ int ppm=0, good, hsc, changed ;
+ int remtx=0 ;
+ char *fname=0 ;
+ cap session = { 0,0,0,0, 0,0,0,0 } ;
+ char buf [ CMDBUFSIZE ] ;
+
+ hsc=-1 ; /* will be set >= 0 on hangup */
+
+ if ( sresponse ( "+FPO", 0 ) ) {
+ remtx = 1 ;
+ msg ( "N remote has document(s) to send." ) ;
+ }
+
+ if ( calling ) {
+ if ( pages ) goto send ;
+ else goto poll ;
+ } else {
+ if ( pages ) goto pollserver ;
+ else goto receive ;
+ }
+
+ /* Class 2 Send */
+
+ pollserver:
+
+ /* with +FLP[L]=1 the modem should accept +FDT. */
+
+ send:
+
+ page=1 ;
+
+ pagetry=0 ;
+ while ( ! err && ! done ) {
+
+ err = rdpage ( inf, dp, &ppm, local, &changed ) ;
+
+ if ( ! err && changed ) {
+ sprintf ( buf, c20 ? "+FIS=%d,%d,%d,%d" : "+FDIS=%d,%d,%d,%d",
+ local[0], local[1], local[2], local[3] ) ;
+ ckcmd ( mf, 0, buf, TO_FT, OK ) ;
+ if ( gethsc ( &hsc, &err ) ) {
+ continue ;
+ }
+ }
+
+ ckcmd ( mf, &err, "+FDT", -TO_C2B, CONNECT ) ;
+ if ( err || gethsc ( &hsc, &err ) ) {
+ done=1 ;
+ continue ;
+ }
+
+ getc2dcs ( session ) ;
+
+ if ( ! c20 ) getstartc ( mf ) ;
+
+ send_data ( mf, inf, page, pages, local, session, header, font ) ;
+ pagetry++ ;
+
+ if ( c20 ) {
+ end_data ( mf, session, ppm, &good ) ;
+ } else {
+ end_data ( mf, session, 0, 0 ) ;
+
+ gethsc ( &hsc, &err ) ;
+
+ if ( ! err && hsc < 0 ) {
+ ckcmd ( mf, &err, ppm == EOP ? "+FET=2" :
+ ppm == EOM ? "+FET=1" : "+FET=0" , TO_C2PP, OK ) ;
+ }
+
+ gethsc ( &hsc, &err ) ;
+
+ if ( ! err && hsc < 0 ) {
+ if ( sresponse ( "+FPTS:", &good ) ) {
+ good &= 1 ; /* odd values mean received OK */
+ } else { /* no +FPTS and +FHNG probably NG */
+ good = gethsc ( 0, 0 ) ? 0 :
+ msg ( "W1no +FPTS response, assumed received" ) ;
+ }
+ }
+
+ }
+
+ if ( noretry ) good = 1;
+
+ if ( good ) {
+ fname = inf->page->fname ;
+ if ( fname ) msg ( "Isent -> %s", fname ) ;
+ pagetry=0 ;
+ page++ ;
+ dp = 1 ;
+ if ( ppm == EOP ) {
+ nextipage ( inf, 1 ) ; /* skip ahead to mark all files done */
+ done = 1 ;
+ }
+ } else {
+ dp = 0 ;
+ if ( pagetry >= NTXRETRY )
+ err = msg ( "E2too many page send retries" ) ;
+ }
+
+ if ( gethsc ( &hsc, &err ) ) done=1 ;
+
+ if ( good && lastpage ( inf ) ) {
+ done = 1 ;
+ }
+ }
+
+ goto done ;
+
+ /* Class 2 Receive */
+
+ poll:
+
+ /* with +FSP[L]=1 and +FPO[LL]: the modem should now accept +FDR. */
+
+ receive:
+
+ getc2dcs ( session ) ; /* get ATA responses */
+
+ done=0 ;
+ for ( page=0 ; ! err && ! done ; page++ ) {
+
+ if ( ! ( err = wrpage ( outf, page ) ) ) {
+ c = cmd ( mf, "+FDR", -TO_C2R ) ;
+
+ switch ( c ) {
+
+ case CONNECT:
+ getc2dcs ( session ) ;
+
+ outf->w=pagewidth[session[WD]];
+ outf->h=0;
+ outf->xres=204.0;
+ outf->yres=vresolution[session[VR]];
+
+ tput ( mf, &startchar, 1 ) ;
+
+ if ( receive_data ( mf, outf, session, &nerr ) == 0 ) {
+ good = nerr < maxpgerr ;
+ msg ( "I-received -> %s", outf->cfname ) ;
+ } else {
+ good = 0 ;
+ }
+
+ ckcmd ( mf, &err, 0, TO_C2EOR, OK ) ;
+
+ if ( err || gethsc ( &hsc, &err ) ) {
+ wrpage ( outf, +1 ) ;
+ wrpage ( outf, -1 ) ;
+ done=1 ;
+ continue ;
+ }
+
+ if ( ! good ) {
+ msg ( "Wreception errors" ) ;
+ ckcmd ( mf, 0, c20 ? "+FPS=2" : "+FPTS=2", T3S, OK ) ;
+ if ( gethsc ( &hsc, &err ) ) continue ;
+ }
+ break ;
+
+ case OK:
+ wrpage ( outf, -1 ) ; /* no more pages */
+ done=1 ;
+ if ( gethsc ( &hsc, &err ) ) continue ;
+ break ;
+
+ default:
+ wrpage ( outf, -1 ) ; /* oops */
+ err = msg ( "E3receive (+FDR) command failed") ;
+ break ;
+ }
+ }
+ }
+
+
+ done:
+ if ( hsc < 0 ) ckcmd ( mf, 0, c20 ? "+FKS" : "+FK", TO_RESET, OK ) ;
+
+ return err ;
+}
+
+
+/* Dial the phone number given by string s. If nowait is true
+ adds a ';' to the dial string to avoid waiting for a
+ CONNECTion (might allow ersatz polling). Also resets the
+ global "nframes" if appropriate so getfr() and putframe() know
+ not to issue +FRH/+FTH. Returns 0 if dialed OK, 1 if busy, 2
+ on errors. */
+
+int dial ( TFILE *f, char *s, int nowait )
+{
+ int err=0, hsc=-1 ;
+ char c, dsbuf [ 128 ], *p ;
+
+ sprintf ( dsbuf, nowait ? "D%.126s;" : "D%.127s" , s ) ;
+ msg ( "Idialing %s", dsbuf+1 ) ;
+
+ c = cmd ( f, dsbuf, TO_A ) ;
+
+ if ( ( p = sresponse ( "+FCSI:", 0 ) ) != 0 ||
+ ( p = sresponse ( "+FCI:", 0 ) ) != 0 ) {
+ msg ( "I- remote ID -> %s", p ) ;
+ }
+
+ if ( nowait && c == OK ) {
+ msg ( "Icalled" ) ;
+ nframes = 1 ;
+ } else if ( c1 && c == CONNECT ) {
+ msg ( "Iconnected" ) ;
+ nframes = 0 ;
+ } else if ( !c1 && c == OK ) {
+ msg ( "Iconnected" ) ;
+ } else if ( c == BUSY ) {
+ err = msg ( "W1number is busy" ) ;
+ } else {
+ err = msg ( "E2dial command failed" ) ;
+ }
+
+ gethsc ( &hsc, err ? 0 : &err ) ;
+
+ return err ;
+}
+
+
+/* Figure out which mode the modem answered in (fax, data, voice
+ or none) based on modem class and responses to the previous
+ command. Sets crate (connect rate) for DATAMODE and hsc
+ (hangup status code) if detects a class 2 hangup message. */
+
+enum connectmode { NONE, DATAMODE, FAXMODE, VOICEMODE } ;
+
+enum connectmode ansmode ( int *crate, int *hsc )
+{
+ enum connectmode mode = NONE ;
+ int x=0 ;
+
+ if ( c1 && sresponse ( "CONNECT", &x ) ) {
+ mode = x ? DATAMODE : FAXMODE ;
+ }
+
+ if ( !c1 && sresponse ( "OK", 0 ) ) {
+ mode = FAXMODE ;
+ }
+
+ if ( !c1 && ( sresponse ( "CONNECT", &x ) || sresponse ( "+FDM", 0 ) ) ) {
+ mode = DATAMODE ;
+ }
+
+ if ( sresponse ( "DATA", 0 ) || sresponse ( "CONNECT DATA", 0 ) ) {
+ mode = DATAMODE ;
+ sresponse ( "CONNECT", &x ) ;
+ }
+
+ if ( sresponse ( "FAX", 0 ) || sresponse ( "+FCO", 0 ) ) {
+ mode = FAXMODE ;
+ }
+
+ if ( sresponse ( "VCON", 0 ) ) {
+ mode = VOICEMODE ;
+ }
+
+ if ( gethsc ( hsc, 0 ) ) {
+ mode = NONE ;
+ }
+
+ if ( DATAMODE && x ) *crate = x ;
+
+ return mode ;
+}
+
+
+/* Answer the phone. Remove our lock if sharing device with
+ outgoing calls. If waiting for call, wait for modem activity,
+ else answer phone. Figure out what mode we answered in and
+ handle call appropriately. Re-lock if necessary. Exec *getty
+ or *vcmd for data or voice calls. */
+
+int answer ( TFILE *f, char **lkfile,
+ int wait, int share, int softaa,
+ char *getty, char *vcmd, char *acmd )
+{
+ int err=0, c ;
+ int crate=19200, hsc=-1, i ;
+ enum connectmode mode=NONE ;
+
+ if ( ! err && share ) {
+ err = ttymode ( f, COMMAND ) ;
+ if ( ! err )
+ err = unlockall ( lkfile ) ;
+ }
+
+ if ( ! err && wait ) {
+ msg ( "Iwaiting for activity") ;
+ tdata ( f, -1 ) ;
+ msg ( "Iactivity detected") ;
+ }
+
+ if ( ! err && share ) {
+ msleep ( 500 ) ; /* let other programs lock port */
+ err = lockall ( lkfile, 1 ) ;
+ if ( err )
+ err = msg ( "W1can't answer: can't lock device" ) ;
+ else
+ err = ttymode ( f, COMMAND ) ; /* in case it was changed silently */
+ }
+
+ for ( i=0 ; ! err && mode == NONE && ( i==0 || ( i==1 && softaa ) ) ; i++ ) {
+
+ c = cmd ( f, wait ? 0 : acmd, ( i==0 && softaa ) ? TO_DATAF : TO_A ) ;
+
+ if ( c == DATA ) cmd ( f, c1 ? "O" : 0, TO_A ) ; /* +FAE=1 weirdness */
+
+ mode = ansmode ( &crate, &hsc ) ;
+
+ switch ( mode ) {
+ case DATAMODE :
+ msg ( "Idata call answered") ;
+ if ( getty && *getty ) {
+ char buf [ MAXGETTY ] ;
+ if ( ckfmt ( getty, 6 ) ) {
+ err = msg ( "E3 too many %%d escapes in command (%s)", getty ) ;
+ } else {
+ sprintf ( buf, getty, crate, crate, crate, crate, crate, crate ) ;
+ msg ( "Iexec'ing /bin/sh -c \"%s\"" , buf ) ;
+ execl ( "/bin/sh" , "sh" , "-c" , buf , (void*) 0 ) ;
+ err = msg ( "ES2exec failed:" ) ;
+ }
+ } else {
+ err = msg ( "E2no getty command defined for data call" ) ;
+ }
+ break ;
+ case FAXMODE :
+ nframes = 0 ;
+ msg ( "Ifax call answered") ;
+ break ;
+ case VOICEMODE :
+ msg ( "Ivoice call answered") ;
+ if ( vcmd && *vcmd ) {
+ char buf [ MAXGETTY ] ;
+ if ( ckfmt ( vcmd, 6 ) ) {
+ } else {
+ sprintf ( buf, vcmd, f->fd, f->fd, f->fd, f->fd, f->fd, f->fd ) ;
+ msg ( "Iexec'ing /bin/sh -c \"%s\"" , buf ) ;
+ execl ( "/bin/sh" , "sh" , "-c" , buf , (void*) 0 ) ;
+ err = msg ( "ES2exec failed:" ) ;
+ }
+ } else {
+ err = msg ( "E2no voice command defined for voice call" ) ;
+ }
+ break ;
+ case NONE:
+ if ( i==0 && softaa && hsc < 0 && getty && *getty ) {
+ int j ; /* switch to fax for 2nd try */
+ for ( j=0 ; j<3 ; j++ )
+ if ( cmd ( f, c1 ? "+FCLASS=1" :
+ ( c20 ? "+FCLASS=2.0" : "+FCLASS=2" ), -TO_RESET )
+ == OK ) break ;
+ wait = 0 ;
+ acmd = ANSCMD ;
+ } else {
+ err = msg ( "E3unable to answer call") ;
+ }
+ break ;
+ default:
+ err = msg ( "E3can't happen(answer)" ) ;
+ break ;
+ }
+
+ }
+
+ return err ;
+}
+
+
+/* Initialize modem. Determine class to use and issue
+ class-specific fax initialization commands. If poll is true,
+ issues commands to enable polling also. Returns 0 or 3 if a
+ mandatory setup command fails. */
+
+int modem_init ( TFILE *mf, cap c, char *id,
+ int calling, int poll, int capsset, int *preverse )
+{
+ int err=0, t=-TO_RESET ;
+ char buf [ CMDBUFSIZE ], model [ CMDBUFSIZE ] = "" ;
+ char **p, *q, *modelq [2][4] = { { "+FMFR?", "+FMDL?", 0 },
+ { "+FMI?", "+FMM?", "+FMR?", 0 } } ;
+
+
+ /* diasable command echo and get firmware revision */
+
+ cmd ( mf, "E0", t ) ;
+
+ if ( cmd ( mf, "I3", t ) == OK ) {
+ getresp ( "", model, CMDBUFSIZE-1 ) ;
+ strcat ( model, " " ) ;
+ }
+
+ /* if not already chosen, pick safest class; set it */
+
+ if ( ! err && ! c1 && !c2 && ! c20 ) {
+ if ( cmd ( mf, "+FCLASS=?", t ) != OK ) {
+ err = msg ("E3 modem does not support fax" ) ;
+ } else {
+ if ( strinresp ( "2.0" ) ) c20 = 1 ;
+ else if ( strinresp ( "2" ) ) ;
+ else if ( strinresp ( "1" ) ) c1 = 1 ;
+ else err = msg ("E3 can't determine fax modem class support" ) ;
+ if ( strstr ( model, "Sportster" ) ) { /* USR Sporsters are buggy */
+ c1 = 1 ;
+ c2 = c20 = 0 ;
+ }
+ }
+ }
+
+ ckcmd ( mf, &err, c1 ? "+FCLASS=1" :
+ ( c20 ? "+FCLASS=2.0" : "+FCLASS=2" ), t, OK ) ;
+
+ /* get make & model if using Class 2 or 2.0 */
+
+ if ( ! c1 ) {
+ for ( p = modelq [ c20 ] ; ! err && *p ; p++ ) {
+ if ( cmd ( mf, *p, t ) == OK ) {
+ getresp ( "", model, CMDBUFSIZE-1 ) ;
+ strcat ( model, " " ) ;
+ }
+ }
+
+ if ( ! c1 && strstr ( model, "Multi-Tech" ) ) {
+ *preverse = 0 ;
+ msg ("I Multi-Tech bit order set" ) ;
+ }
+ }
+
+ if ( ! err )
+ msg ( "I using %sin class %s", model, c1 ? "1" : c20 ? "2.0" : "2" ) ;
+
+ /* get maximum modem speed if not already set (Class 1 only) */
+
+ if ( ! err && c1 && ! capsset ) {
+ int i ;
+ char *c1spstr [6] = { "24", "48", "72", "96", "121", "145" } ;
+ ckcmd ( mf, &err, calling ? "+FTM=?" : "+FRM=?", t, OK ) ;
+ for ( i=0 ; i < 6 && strinresp ( c1spstr[i] ) ; i++ ) ;
+ c[1]=i?i-1:0;
+ }
+
+ /* issue essential commands and set/get modem capabilities (Class 2 only) */
+
+ if ( ! err && ! c1 ) {
+
+ if ( c20 ) {
+ ckcmd ( mf, 0, "+FIP", t, OK ) ;
+ ckcmd ( mf, 0, "+FNR=1,1,1,1", t, OK ) ;
+ }
+
+ ckcmd ( mf, &err, "+FCR=1", t, OK ) ;
+
+ if ( ! capsset ) {
+ if ( cmd ( mf, c20 ? "+FIS?" : "+FDIS?", -t ) == OK &&
+ ( q = strinresp ( "," ) ) ) {
+ str2cap ( q-1, c ) ;
+ } else {
+ msg ( "W can't get modem capabilities, set to default" ) ;
+ capsset = 1 ;
+ }
+ }
+
+ if ( capsset ) {
+ sprintf ( buf, c20 ? "+FCC=%d,%d,%d,%d,%d,%d,%d,%d" :
+ "+FDCC=%d,%d,%d,%d,%d,%d,%d,%d",
+ c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7] ) ;
+ ckcmd ( mf, 0, buf, -t, OK ) ;
+ }
+
+ sprintf ( buf, c20 ? "+FLI=\"%.*s\"" : "+FLID=\"%.*s\"" ,
+ CMDBUFSIZE-9, id ) ;
+ ckcmd ( mf, 0, buf, -t, OK ) ;
+
+ if ( ! err && poll ) {
+
+ ckcmd ( mf, 0, c20 ? "+FSP=1" : "+FSPL=1", -t, OK ) ;
+
+ sprintf ( buf, c20 ? "+FPI=\"%.*s\"" : "+FCIG=\"%.*s\"" ,
+ CMDBUFSIZE-9, id ) ;
+ ckcmd ( mf, 0, buf, -t, OK ) ;
+
+ }
+ }
+
+ return err ;
+}
+
+
+/* the following are global so can terminate properly on signal */
+
+char *icmd[3][ MAXICMD+1 ] = {{0},{0},{0}} ; /* initialization commands */
+TFILE faxdev = { -1, 0,0, {0}, 0, 0 } ; /* modem */
+char *lkfile [ MAXLKFILE+1 ] = {0} ; /* lock file names */
+IFILE ifile = { 0 } ; /* files being sent */
+int locked = 0 ; /* modem locked */
+
+
+/* print names of files not sent and reset modem before
+ exiting. */
+
+int cleanup ( int err )
+{
+ /* log names of files not sent */
+ logifnames ( &ifile, "I failed -> %s" ) ;
+
+ if ( ! locked && faxdev.fd >= 0 )
+ end_session ( &faxdev, icmd[2], lkfile, err != 4 ) ;
+
+ msg ( "Idone, returning %d (%s)", err,
+ errormsg [ err >= 0 && err <= 5 ? err : 6 ] ) ;
+
+ return err ;
+}
+
+
+/* signal handler */
+
+void onsig ( int sig )
+{
+ msg ( "E terminating on signal %d", sig ) ;
+ exit ( cleanup ( 5 ) ) ;
+}
+
+
+/* Fax send/receive program for Class 1, 2 and 2.0 fax
+ modems. Returns 0 on success, 1 if number busy or device
+ locked, 2 for errors, 3 for protocol errors, 4 if no modem
+ response, 5 on signal. */
+
+int main( int argc, char **argv)
+{
+ int err=0, doneargs=0, c=0, i ;
+ int testing=0, calling=0 ;
+
+ int nicmd[3]={0,0,0}, nlkfile=0, nverb=0 ;
+
+ char *faxfile = FAXFILE ;
+
+ int softaa=0, share=0, wait=0, reverse=1, ignerr=0, noretry=0, hwfc=0 ;
+ int capsset=0 ;
+ char *getty = "", *vcmd = "", *acmd=ANSCMD ;
+
+ cap local = { DEFCAP } ;
+ char localid [ IDLEN + 1 ] = DEFID ;
+
+ int maxpgerr = MAXPGERR ;
+
+ time_t now ;
+ char *header = 0, headerbuf [ MAXLINELEN ] ;
+ char *fontname = 0 ;
+ faxfont font ;
+
+ OFILE ofile ;
+ int pages = 0 ;
+ char *phnum="", *ansfname = DEFPAT ;
+ char fnamepat [ EFAX_PATH_MAX ] ;
+
+ /* print initial message to both stderr & stdout */
+ argv0 = argv[0] ;
+ verb[1] = "ewia" ;
+ msg ( "I " Version " " Copyright ) ;
+ argv0 = efaxbasename ( argv0 ) ;
+ msg ( "A compiled "__DATE__ " " __TIME__ ) ;
+ verb[1] = "" ;
+
+ while ( ! err && ! doneargs &&
+ ( c = nextopt ( argc,argv,
+ "a:c:d:e:f:g:h:i:j:k:l:o:p:q:r:st:v:wx:T" ) ) != -1 ) {
+
+ switch (c) {
+ case 'a':
+ acmd = nxtoptarg ;
+ break ;
+ case 'c':
+ err = str2cap ( nxtoptarg, local ) ;
+ capsset = 1 ;
+ break ;
+ case 'l':
+ if ( strlen ( nxtoptarg ) > IDLEN )
+ msg("Wlocal ID (%s) truncated to %d characters", nxtoptarg, IDLEN ) ;
+ if ( strspn ( nxtoptarg, " +0123456789" ) != strlen ( nxtoptarg ) )
+ msg("Wlocal ID (%s) has non-standard characters", nxtoptarg ) ;
+ sprintf ( localid, "%*.*s", IDLEN, IDLEN, nxtoptarg ) ;
+ break ;
+ case 'i':
+ if ( nicmd[0] < MAXICMD ) icmd[0][ nicmd[0]++ ] = nxtoptarg ;
+ else err = msg ( "E2too many '-i' options");
+ break ;
+ case 'j':
+ if ( nicmd[1] < MAXICMD ) icmd[1][ nicmd[1]++ ] = nxtoptarg ;
+ else err = msg ( "E2too many '-j' options");
+ break ;
+ case 'k':
+ if ( nicmd[2] < MAXICMD ) icmd[2][ nicmd[2]++ ] = nxtoptarg ;
+ else err = msg ( "E2too many '-k' options");
+ break ;
+ case 'h':
+ header = nxtoptarg ;
+ break ;
+ case 'f':
+ fontname = nxtoptarg ;
+ break ;
+ case 'd':
+ faxfile = nxtoptarg ;
+ break ;
+ case 'e':
+ vcmd = nxtoptarg ;
+ break ;
+ case 'g':
+ getty = nxtoptarg ;
+ break ;
+ case 'o': /* most protocol options are globals */
+ for ( ; *nxtoptarg ; nxtoptarg++ )
+ switch ( *nxtoptarg ) {
+ case '0' : c20 = 1 ; break ;
+ case '1' : c1 = 1 ; break ;
+ case '2' : c2 = 1 ; break ;
+ case 'a' : softaa = 1 ; break ;
+ case 'e' : ignerr = 1 ; break ;
+ case 'f' : vfc = 1 ; break ;
+ case 'h' : hwfc = 1 ; break ;
+ case 'l' : lockpolldelay /= 2 ; break ;
+ case 'n' : noretry = 1 ; break ;
+ case 'r' : reverse = 0 ; break ;
+ case 'x' : startchar = XON ; break ;
+ case 'z' : cmdpause += T_CMD ; break ;
+ default : msg ( "Wunrecognized protocol option (%c)", *nxtoptarg ) ;
+ }
+ break ;
+ case 'q':
+ if ( sscanf ( nxtoptarg , "%d", &maxpgerr ) != 1 || maxpgerr < 0 )
+ err=msg ("E2bad quality (-q) argument (%s)", nxtoptarg ) ;
+ break;
+ case 'r':
+ ansfname = nxtoptarg ;
+ break;
+ case 's':
+ share = 1 ;
+ break;
+ case 't':
+ calling=1;
+ /* fall through */
+ case 'p':
+ if ( argv [ argc ] ) err = msg ("E2can't happen(unterminated argv)") ;
+ if ( ! err ) err = newIFILE ( &ifile, argv + nxtoptind ) ;
+ pages = argc - nxtoptind - ( c == 'p' ? 1 : 0 ) ;
+ pages = pages < 0 ? 0 : pages ;
+ phnum = nxtoptarg ;
+ doneargs=1 ;
+ break;
+ case 'v':
+ verb[nverb] = nxtoptarg ;
+ nverb=1;
+ break ;
+ case 'w':
+ wait = 1 ;
+ break ;
+ case 'x':
+ if ( nlkfile < MAXLKFILE ) lkfile[ nlkfile++ ] = nxtoptarg ;
+ else err = msg ( "E2too many lock files" ) ;
+ break ;
+ case 'T': /* test: begin+end session */
+ testing=1;
+ doneargs=1 ;
+ break ;
+ default : fprintf ( stderr, Usage, argv0 ) ; err = 2 ; break ;
+ }
+ }
+
+ for ( i=0 ; i<argc ; i++ )
+ msg ( "Aargv[%d]=%s", i, argv[i]) ;
+
+ if ( ! nicmd[2] ) icmd[2][nicmd[2]++] = "H" ; /* default -k command */
+
+ readfont ( fontname, &font ) ;
+
+ if ( ! header ) {
+ char tmp [ MAXLINELEN ] ;
+ now = time ( 0 ) ;
+ strftime ( tmp, MAXLINELEN, "%c %%s P. %%%%d", localtime ( &now ) ) ;
+ sprintf ( header = headerbuf, tmp, localid ) ;
+ }
+
+ if ( ! err ) {
+ err = begin_session ( &faxdev, faxfile,
+ !c1 && !c20 && reverse, /* Class 2 rx bit reversal */
+ hwfc, lkfile, COMMAND, onsig ) ;
+ if ( ! err ) err = setup ( &faxdev, icmd[0], ignerr ) ;
+ if ( ! err ) err = modem_init ( &faxdev, local, localid,
+ calling, calling && !pages, capsset,
+ &reverse ) ;
+ if ( ! err ) err = setup ( &faxdev, icmd[1], ignerr ) ;
+ if ( err == 1 ) locked = 1 ;
+ }
+
+ if ( ! err && ! testing ) {
+
+ if ( calling ) {
+ err = dial ( &faxdev, phnum, 0 ) ;
+ } else {
+ err = answer ( &faxdev, lkfile, wait, share, softaa,
+ getty, vcmd, acmd ) ;
+ if ( err == 1 ) locked = 1 ;
+ }
+
+ now = time(0) ; /* do it here so use reception time */
+ strftime ( fnamepat, EFAX_PATH_MAX, ansfname, localtime ( &now ) ) ;
+ strncat ( fnamepat, ".%03d", EFAX_PATH_MAX - strlen ( fnamepat ) ) ;
+ newOFILE ( &ofile, O_TIFF_FAX, fnamepat, 0, 0, 0, 0 ) ;
+
+ if ( ! err ) {
+ if ( c1 ) {
+ err = c1sndrcv ( &faxdev, local, localid,
+ &ofile, &ifile, pages, header, &font,
+ maxpgerr, noretry, calling ) ;
+ } else {
+ err = c2sndrcv ( &faxdev, local, localid,
+ &ofile, &ifile, pages, header, &font,
+ maxpgerr, noretry, calling ) ;
+ }
+ }
+ }
+
+ return cleanup ( err ) ;
+}
--- /dev/null
+#include <ctype.h> /* ANSI C */
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "efaxio.h" /* EFAX */
+#include "efaxmsg.h"
+#include "efaxos.h"
+
+#define MAXRESPB 1024 /* maximum bytes of modem responses saved */
+
+char *prompts[] = { /* modem responses that are prompts */
+ "OOK", "-CONNECT FAX", "CCONNECT", "NNO CARRIER", "EERROR",
+ "NNO DIALTONE", "BBUSY", "NNO ANSWER", "M+FCERROR", "VVCON",
+ "DDATA", 0 } ;
+
+int lockpolldelay = 8000 ; /* milliseconds between checks of lock files */
+
+ /* signals to be caught so can hang up phone */
+int catch [] = { CATCHSIGS, 0 } ;
+
+
+/* Modem features */
+
+int c1=0, c20=0 ; /* use class 1/class 2.0 */
+int c2=0 ; /* force class 2 */
+int cmdpause = T_CMD ; /* delay before each init command */
+int vfc = 0 ; /* virtual flow control */
+uchar startchar = DC2 ; /* character to start reception */
+
+ /* response detector lookup tables */
+uchar rd_nexts [ 256 ] = { 0 }, rd_allowed [ 256 ] = { 0 } ;
+
+/* Initialize two lookup tables used by a state machine to detect
+ modem responses embedded in data. The first shows which
+ characters are allowed in each state. The second shows which
+ characters in each state increment the state. Each table is
+ indexed by character. The detector sequences through 6 states
+ corresponding to sequences of the form: <CR><LF>AX...<CR><LF>
+ where A is an upper-case letter and X is an u.c. letter or
+ space. The state values are 01, 02, 04, 08, 10 and 20 (hex)
+ and are used to mask in a bit from the tables. When the state
+ reaches 0x20 a modem response has been detected. With random
+ data there is a small O(10^-10) chance of spurious
+ detection. */
+
+void rd_init ( void )
+{
+ int c ;
+
+ rd_nexts[CR] = rd_allowed[CR] = 0x01 | 0x08 ;
+ rd_nexts[LF] = rd_allowed[LF] = 0x02 | 0x10 ;
+ for ( c='A' ; c<'Z' ; c++ ) {
+ rd_allowed[c] = 0x04 | 0x08 ;
+ rd_nexts[c] = 0x04 ;
+ }
+ rd_allowed[' '] = 0x08 ;
+}
+
+
+/* Get a modem response into buffer s, storing up to n bytes.
+ The response includes characters from the most recent control
+ character until the first LF following some text. Returns s
+ or null if times-out in t deciseconds or on i/o error. Trace
+ messages are buffered to reduce possible timing problems. */
+
+char *tgets( TFILE *f, char *s, int len, int t )
+{
+ int i, n, c ;
+
+ for ( i=n=0 ; 1 ; i++ ) {
+ if ( ( c = tgetc ( f, t ) ) == EOF ) break ;
+ if ( i == 0 ) msg ( "M-+ .%03d [", time_ms ( ) ) ;
+ msg ( "M-+ %s", cname ( c ) ) ;
+ if ( n > 0 && c == LF ) break ;
+ if ( ! iscntrl ( c ) && n < len ) s[n++] = c ;
+ }
+
+ if ( n >= len ) msg ( "W- modem response overflow" ) ;
+ s[ n < len ? n : len-1 ] = '\0' ;
+ if ( i > 0 ) {
+ if ( c == EOF ) msg ( "M- <...%.1f s>]", (float)t/10 ) ;
+ else msg ( "M- ]" ) ;
+ }
+
+ return c == EOF ? 0 : s ;
+}
+
+
+/* Send bytes to the modem, doing bit-reversal and escaping DLEs.
+ Returns 0 or 2 on errors. */
+
+int sendbuf ( TFILE *f, uchar *p, int n, int dcecps )
+{
+ int err=0, c, over ;
+ uchar *order = f->obitorder ;
+ uchar buf [ MINWRITE+1 ] ;
+ int i ;
+
+ for ( i=0 ; ! err && n > 0 ; n-- ) {
+ c = order [ *p++ ] ;
+ if ( c == DLE ) buf[i++] = DLE ;
+ buf[i++] = c ;
+ if ( i >= MINWRITE || n == 1 ) {
+
+ /* ``virtual'' flow control */
+
+ if ( vfc && dcecps > 0 ) {
+ over = f->bytes - ( proc_ms ( ) - f->mstart ) * dcecps / 1000
+ - MAXDCEBUF ;
+ if ( over > 0 ) msleep ( over * 1000 / dcecps ) ;
+ }
+
+ if ( tput ( f, buf, i ) < 0 )
+ err = msg ( "ES2fax device write error:" ) ;
+
+ i = 0 ;
+ }
+ }
+
+ return err ;
+}
+
+
+/* Scan responses since giving previous command (by cmd()) for a
+ match to string 's' at start of response. If a matching
+ response is found it finds the start of the data field which
+ is defined as the next non-space character in the current or
+ any subsequent responses. If ip is not null, reads one integer
+ (decimal format) into ip. [problem: Class 2.0 status responses
+ are in hex.] Returns pointer to start of data field of
+ response string or NULL if not found. */
+
+char responses [ MAXRESPB ], *lresponse = responses ;
+
+char *sresponse ( char *s, int *ip )
+{
+ char *p, *r = 0 ;
+ int lens, lenr ;
+
+ lens = strlen ( s ) ;
+ for ( p=responses ; p<lresponse ; p += strlen(p) + 1 ) {
+
+ if ( ! strncmp ( p, s, lens ) ) {
+ r = p + lens ;
+
+ lenr = strlen ( r ) ;
+ if ( strspn ( r, " \r\n" ) == lenr && r+lenr < lresponse ) r += lenr ;
+
+ if ( ip && sscanf ( r, "%d", ip ) > 0 )
+ msg ( "R read value %d from \"%s\"", *ip, r ) ;
+ }
+
+ }
+
+ return r ;
+}
+
+
+/* Search for the string s in responses since last command.
+ Skips lines beginning with "AT" (command echo) and removes
+ trailing spaces. Returns pointer to the string or NULL if not
+ found. */
+
+char *strinresp ( char *s )
+{
+ char *p, *r = 0 ;
+
+ for ( p=responses ; p<lresponse && !r ; p += strlen(p) + 1 )
+ if ( strncmp ( p, "AT", 2 ) )
+ r = strstr ( p, s ) ;
+
+ return r ;
+}
+
+
+/* Appends the value of the first response that includes the
+ string s to buffer buf of length len. Skips characters before
+ and including "=" in the response and doesn't copy trailing
+ spaces. */
+
+int getresp ( char *s, char *buf, int len )
+{
+ int err=0, n ;
+ char *p, *q ;
+
+ if ( ( p = strinresp ( s ) ) ) {
+ if ( ( q = strchr ( p, '=' ) ) ) p = q+1 ;
+ for ( q = p+strlen(p)-1 ; q>=p && isspace(*q) ; q-- ) ;
+ n = q - p + 1 ;
+ if ( n + strlen(buf) < len )
+ strncat ( buf, p, n ) ;
+ else
+ strncat ( buf, p, len - strlen(buf) - 1 ) ;
+ } else {
+ err = 1 ;
+ }
+ return err ;
+}
+
+
+/* Search for a match to the string s in a null-terminated array of
+ possible prefix strings pointed to by p. The first character of each
+ prefix string is skipped. Returns pointer to the table entry or NULL if
+ not found. */
+
+char *strtabmatch ( char **p, char *s )
+{
+ while ( *p && strncmp ( *p+1, s, strlen ( *p+1 ) ) ) p++ ;
+ return ( ! *p || **p == '-' ) ? NULL : *p ;
+}
+
+
+/* Send command to modem and check responses. All modem commands
+ go through this function. Collects pending (unexpected)
+ responses and then pauses for inter-command delay (cmdpause)
+ if t is negative. Writes command s to modem if s is not null.
+ Reads responses and terminates when a response is one of the
+ prompts in prompts[] or if times out in t deciseconds.
+ Repeats command if detects a RING response (probable
+ collision). Returns the first character of the matching prefix
+ string (e.g. 'O' for OK, 'C' for CONNECT, etc.) or EOF if no
+ such response was received within timeout t. */
+
+int cmd ( TFILE *f, char *s, int t )
+{
+ char buf [ CMDBUFSIZE ], *p = "" ;
+ int resplen=0, pause=0 ;
+
+ if ( t < 0 ) {
+ pause = cmdpause ;
+ t = -t ;
+ }
+
+ lresponse = responses ;
+
+ retry:
+
+ if ( s ) {
+
+ while ( tgets ( f, buf, CMDBUFSIZE, pause ) )
+ msg ( "W- unexpected response \"%s\"", buf ) ;
+
+ msg ( "C- command \"%s\"", s ) ;
+
+ if ( strlen(s) >= CMDBUFSIZE-4 ) {
+ msg ( "E modem command \"%s\" too long", s ) ;
+ } else {
+ sprintf ( buf, "AT%s\r", s ) ;
+ tput ( f, buf, strlen(buf) ) ;
+ }
+ }
+
+ if ( t ) {
+
+ msg ( "C- waiting %.1f s", ((float) t)/10 ) ;
+
+ while ( ( p = tgets ( f, buf, CMDBUFSIZE, t ) ) ) {
+
+ if ( ( resplen += strlen ( buf ) + 1 ) <= MAXRESPB ) {
+ strcpy ( lresponse, buf ) ;
+ lresponse += strlen ( buf ) + 1 ;
+ }
+
+ if ( ( p = strtabmatch ( (char**) prompts, buf ) ) ) {
+ msg ( "C- response \"%s\"", buf ) ;
+ break ;
+ }
+
+ if ( ! strcmp ( buf, "RING" ) ) { msleep ( 100 ) ; goto retry ; }
+ }
+ }
+
+ return p ? *p : EOF ;
+}
+
+
+/* Send command to modem and wait for reply after testing (and
+ possibly setting) current error status via err
+ pointer. Returns 0 if err is already set, command response, or
+ EOF on timeout. */
+
+int ckcmd ( TFILE *f, int *err, char *s, int t, int r )
+{
+ int c=0 ;
+ if ( ( ! err || ! *err ) && ( c = cmd ( f, s, t ) ) != r && r ) {
+ msg ( err ? "E %s %s %s" : "W %s %s %s",
+ c == EOF ? "timed out" : "wrong response",
+ s ? "after command: " : "after waiting",
+ s ? s : "" ) ;
+ if ( err ) *err = 3 ;
+ }
+ return c ;
+}
+
+
+/* Resynchronize modem from an unknown state. If no immediate
+ response, try pulsing DTR low (needs &D{2,3,4}), and then
+ cancelling data or fax data modes. In each case, discards any
+ responses for about 2 seconds and then tries command ATQ0V1 to
+ enable text responses. Returns 0 if OK or 4 if no response.
+ */
+
+int modemsync ( TFILE *f )
+{
+ int err=0, method=0 ;
+
+ for ( method=0 ; ! err ; method++ ) {
+ switch ( method ) {
+ case 0 :
+ break ;
+ case 1 :
+ break ;
+ ttymode ( f, VOICECOMMAND ) ;
+ case 2 :
+ msg ("Isync: dropping DTR") ;
+ ttymode ( f, COMMAND ) ; msleep ( 200 ) ;
+ ttymode ( f, DROPDTR ) ; msleep ( 200 ) ;
+ ttymode ( f, COMMAND ) ;
+ break ;
+ case 3 :
+ msg ("Isync: sending escapes") ;
+ ttymode ( f, VOICECOMMAND ) ;
+ tput ( f, CAN_STR, 1 ) ;
+ tput ( f, DLE_ETX, 2 ) ;
+ msleep ( 100 ) ;
+ ttymode ( f, COMMAND ) ;
+ tput ( f, CAN_STR, 1 ) ;
+ tput ( f, DLE_ETX, 2 ) ;
+ msleep ( 1500 ) ;
+ tput ( f, "+++", 3 ) ;
+ break ;
+ case 4 :
+ err = msg ("E4sync: modem not responding") ;
+ continue ;
+ }
+ while ( method && cmd ( f, 0, 20 ) != EOF ) ;
+ if ( cmd ( f, "Q0V1", -20 ) == OK ) break ;
+ }
+ return err ;
+}
+
+
+/* Set up modem by sending initialization/reset commands.
+ Accepts either OK or CONNECT responses. Optionally changes
+ baud rate if a command begins with a number. Returns 0 if OK,
+ 3 on errors. */
+
+int setup ( TFILE *f, char **cmds, int ignerr )
+{
+ int err=0 ;
+ char c ;
+
+ for ( ; ! err && *cmds ; cmds++ ) {
+#if 0
+ if ( *cmds && isdigit( **cmds ) ) {
+
+ }
+#endif
+ if ( ( c = cmd ( f, *cmds, -TO_RESET ) ) != OK && c != VCONNECT &&
+ ! ignerr ) {
+ err = msg ( "E3modem command (%s) failed", *cmds ? *cmds : "none" ) ;
+ }
+ }
+
+ return err ;
+}
+
+
+/* Terminate session. Makes sure modem is responding, sends
+ modem reset commands or hang-up command if none, removes lock
+ files. Returns 0 if OK, 3 on error.*/
+
+int end_session ( TFILE *f, char **zcmd, char **lkfile, int sync )
+{
+ int err = 0 ;
+
+ if ( f && sync )
+ err = modemsync ( f ) ;
+ if ( f && zcmd && ! err && sync )
+ err = setup ( f, zcmd, 0 ) ;
+ if ( f )
+ ttymode ( f, ORIGINAL ) ;
+ if ( lkfile )
+ unlockall ( lkfile ) ;
+ return err ;
+}
+
+
+/* Initialize session. Try locking and opening fax device until opened or
+ get error. Then set tty modes, register signal handler, set up
+ modem. Returns 0 if OK, 2 on errors, 3 if initialization failed, 4 if no
+ modem response. */
+
+int begin_session ( TFILE *f, char *fname, int reverse, int hwfc,
+ char **lkfile, ttymodes mode, void (*onsig) (int) )
+{
+ int i, err=0, busy=0, minbusy=0 ;
+
+ do {
+ err = lockall ( lkfile, busy >= minbusy ) ;
+ if ( ! err ) err = ttyopen ( f, fname, reverse, hwfc ) ;
+ if ( err == 1 ) {
+ if ( busy++ >= minbusy ) {
+ msg ( "W %s locked or busy. waiting.", fname ) ;
+ minbusy = minbusy ? minbusy*2 : 1 ;
+ }
+ msleep ( lockpolldelay ) ;
+ }
+ } while ( err == 1 ) ;
+
+ if ( ! err ) msg ( "Iopened %s", fname ) ;
+
+ if ( ! err ) err = ttymode ( f, mode ) ;
+
+ if ( ! err ) {
+ rd_init ( ) ;
+ f->rd_state = RD_BEGIN ;
+ }
+
+ for ( i=0 ; ! err && catch [ i ] ; i++ )
+ if ( signal ( catch [ i ], onsig ) == SIG_ERR )
+ err = msg ( "ES2can't set signal %d handler:", catch [ i ] ) ;
+
+ if ( !err ) err = modemsync ( f ) ;
+
+ return err ;
+}
--- /dev/null
+#ifndef _EFAXIO_H
+#define _EFAXIO_H
+
+#include "efaxos.h" /* TFILE definition */
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#define CMDBUFSIZE 256 /* longest possible command or response */
+#define DLE_ETX "\020\003" /* DLE-ETX (end of data) string */
+#define CAN_STR "\030" /* CAN (cancel reception) string */
+#define TO_RESET 50 /* t/o for modem reset commands (>>2.6s) */
+#define T_CMD 1 /* pause before each modem command */
+#define MAXDCEBUF 32 /* max bytes allowed in buffer when write */
+#define MINWRITE 64 /* minimum bytes to write() to modem */
+
+enum promptcodes { /* codes for modem prompts */
+ BUSY = 'B', CONNECT = 'C', DATA='D', ERROR = 'E',
+ MODULATION='M', NO = 'N', OK = 'O', RING = 'R', VCONNECT = 'V' } ;
+
+ /* Modem features */
+
+extern int c1, c20 ; /* use class 1/class 2.0 */
+extern int c2 ; /* force class 2 */
+extern int cmdpause ; /* delay before each init command */
+extern int vfc ; /* virtual flow control */
+extern uchar startchar ; /* character to start reception */
+extern int lockpolldelay ; /* milliseconds between checks of lock files */
+
+ /* response detector lookup tables */
+extern uchar rd_allowed[], rd_nexts[] ;
+#define RD_BEGIN 0x01
+#define RD_END 0x20
+
+ /* Modem interface routines */
+
+int cmd ( TFILE *f, char *s , int t ) ;
+int ckcmd ( TFILE *f, int *err, char *s, int t, int r ) ;
+int modemsync ( TFILE *f ) ;
+char *sresponse ( char *s, int *ip ) ;
+char *strinresp ( char *s ) ;
+int getresp ( char *s, char *buf, int len ) ;
+
+int setup ( TFILE *f, char **cmds, int ignerr ) ;
+
+int sendbuf ( TFILE *f, uchar *p, int n, int dcecps ) ;
+
+int begin_session ( TFILE *f, char *fname, int reverse, int hwfc, char **lkfile,
+ ttymodes mode, void (*onsig) (int) ) ;
+
+int end_session ( TFILE *f, char **zcmd, char **lkfile, int sync ) ;
+
+#endif
--- /dev/null
+/*
+ efaxlib.c - utility routines for efax
+ Copyright 1995 Ed Casas
+*/
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "efaxmsg.h"
+#include "efaxlib.h"
+
+#ifndef SEEK_SET
+#define SEEK_SET 0
+#endif
+
+#define DEFXRES 204.145 /* fax x and y resolution in dpi */
+#define DEFYRES 195.58
+
+#define DEFWIDTH 1728 /* 215x297 mm image at fax resolution */
+#define DEFHEIGHT 2287
+
+extern t4tab wtab [ ( 64 + 27 + 13 ) + 1 ] ; /* T.4 coding tables */
+extern t4tab btab [ ( 64 + 27 + 13 ) + 1 ] ;
+
+short short256 = 256 ; /* for endian-ness detection */
+
+/* Make sure printf strings have only %d escapes and n or fewer
+ of them. Returns 0 if OK, 1 on error. */
+
+int ckfmt ( char *p, int n )
+{
+ for ( ; *p ; p++ ) {
+ if ( p[0] == '%' ) {
+ if ( p[1] == 'd' ) n-- ;
+ else if ( p[1] == '%' ) p++ ;
+ else n=-1 ;
+ }
+ }
+
+ return n < 0 ;
+}
+
+
+/* Initialize state of variable-length code word encoder. */
+
+void newENCODER ( ENCODER *e )
+{
+ e->x = 0 ;
+ e->shift = -8 ;
+}
+
+
+/* Store a code word `code' of length `bits' (<=24) in buffer pointed to by
+ `buf'. Bits that don't fit in complete bytes are saved between calls.
+ To flush the remaining bits call the function with code=0 and bits=0.
+ Returns pointer to next free element in output buffer. Calling function
+ must ensure at least bits/8 bytes are available in buffer. */
+
+uchar *putcode ( ENCODER *e, short code, short bits, uchar *buf )
+{
+ e->x = ( e->x << bits ) | code ;
+ e->shift += bits ? bits : -e->shift ;
+
+ while ( e->shift >= 0 ) {
+ *buf++ = e->x >> e->shift ;
+ e->shift -= 8 ;
+ }
+
+ return buf ;
+}
+
+
+/* Convert run lengths to 1-D T.4-codes. First run is white. Silently
+ truncates run lengths that are too long. After using this function EOLs
+ may need to be added and/or the putcode() buffer flushed. Returns
+ pointer to next free element in output buffer. */
+
+uchar *runtocode ( ENCODER *e, short *runs, int nr, uchar *codes )
+{
+ uchar col = 0, *maxcodes = codes + MAXCODES ;
+ t4tab *ctab = wtab, *p ;
+ short rlen ;
+ long x ;
+ short shift ;
+
+#define PUTCODE(p) { x = ( x << p->bits ) | p->code ; shift += p->bits ; \
+ while ( shift >= 0 ) { *codes++ = x >> shift ; shift -= 8 ; } }
+
+ x = e->x ; shift = e->shift ;
+
+ while ( nr-- > 0 ) {
+ rlen = *runs++ ;
+ if ( rlen > 63 ) { /* make-up code */
+ if ( rlen > MAXRUNLEN ) rlen = MAXRUNLEN ;
+ p = ctab + 63 + ( rlen >> 6 ) ;
+ if ( codes < maxcodes ) PUTCODE(p) ;
+ }
+ p = ctab + ( rlen & 0x3f ) ; /* terminating code */
+ if ( codes < maxcodes ) PUTCODE(p) ;
+ ctab = ( col ^= 1 ) ? btab : wtab ;
+ }
+
+ e->x = x ; e->shift = shift ;
+
+ return codes ;
+}
+
+
+/* Pad/truncate run-length coded scan line 'runs' of 'nr' runs by 'pad'
+ pixels (truncate if negative). Returns the new number of runs. */
+
+int xpad ( short *runs, int nr, int pad )
+{
+ if ( pad < 0 ) { /* truncate */
+ while ( pad < 0 ) pad += ( nr <= 0 ? -pad : runs [ --nr ] ) ;
+ runs [ nr++ ] = pad ;
+ } else { /* pad with white */
+ if ( nr & 1 ) runs [ nr - 1 ] += pad ;
+ else runs [ nr++ ] = pad ;
+ }
+ return nr ;
+}
+
+
+/* Shift a run-length coded scan line right by s pixels (left if negative).
+ If necessary, zero-length runs are created to avoid copying. Returns
+ the pixel width change (+/-). */
+
+int xshift ( short *runs, int nr, int s )
+{
+ int i=0, n=0 ;
+ if ( s < 0 ) {
+ for ( i = 0 ; s < 0 && i < nr ; i++ ) {
+ s += runs [ i ] ;
+ n -= runs [ i ] ;
+ runs [ i ] = 0 ;
+ }
+ i-- ;
+ }
+ if ( i < nr ) {
+ runs [ i ] += s ;
+ n += s ;
+ }
+ return n ;
+}
+
+
+/* Scale nr run lengths in buffer pointed to by p to scale image
+ horizontally. The scaling factor is xs/256. Returns new line width in
+ pixels. */
+
+int xscale ( short *p, int nr, int xs )
+{
+ int inlen=0, outlen=0 ;
+ for ( ; nr-- > 0 ; p++ ) {
+ inlen += *p ;
+ *p = ( ( inlen * xs + 128 ) >> 8 ) - outlen ;
+ outlen += *p ;
+ }
+ return outlen ;
+}
+
+
+/* Zero-terminated lists of run lengths for each byte. */
+
+uchar byteruns [ 1408 + 1 ] =
+ "8071061106205120511105210530413041210411110411204220421104310440"
+ "3140313103121103122031112031111103112103113032303221032111032120"
+ "3320331103410350215021410213110213202121202121110212210212302111"
+ "3021112102111111021111202112202112110211310211402240223102221102"
+ "2220221120221111022121022130233023210231110231202420241102510260"
+ "1160115101141101142011312011311101132101133011213011212101121111"
+ "0112112011222011221101123101124011114011113101111211011112201111"
+ "1120111111110111112101111130111230111221011121110111212011132011"
+ "1311011141011150125012410123110123201221201221110122210122301211"
+ "3012112101211111012111201212201212110121310121401340133101321101"
+ "3220131120131111013121013130143014210141110141201520151101610170"
+ "1701610151101520141201411101421014301313013121013111101311201322"
+ "0132110133101340121401213101212110121220121112012111110121121012"
+ "1130122301222101221110122120123201231101241012501115011141011131"
+ "1011132011121201112111011122101112301111130111112101111111101111"
+ "1120111122011112110111131011114011240112310112211011222011211201"
+ "1211110112121011213011330113210113111011312011420114110115101160"
+ "2602510241102420231202311102321023302213022121022111102211202222"
+ "0222110223102240211402113102112110211220211112021111110211121021"
+ "1130212302122102121110212120213202131102141021503503410331103320"
+ "3212032111032210323031130311210311111031112031220312110313103140"
+ "4404310421104220411204111104121041305305210511105120620611071080" ;
+
+/* Convert byte-aligned bit-mapped n-byte scan line into array of run
+ lengths. Run length array must have *more* than 8*n elements. First
+ run is white. Returns number of runs coded. */
+
+int bittorun ( uchar *bits, int n, short *runs )
+{
+ static uchar init=0, *rltab [ 256 ] ;
+ register uchar *p, c, lastc = 0x00 ;
+ short *runs0 = runs ;
+
+ if ( ! init ) { /* initialize pointer and run tables */
+ int i = 0 ;
+ for ( rltab[ 0 ] = p = byteruns ; *p ; p++ )
+ if ( ! ( *p -= '0' ) && i < 255 )
+ rltab [ ++i ] = p+1 ;
+ init = 1 ;
+ }
+
+ *runs = 0 ;
+ for ( ; n > 0 ; n-- ) {
+ p = rltab [ c = *bits++ ] ;
+ if ( ( lastc & 0x01 ) ? ! ( c & 0x80 ) : ( c & 0x80 ) )
+ *(++runs) = *p++ ; /* new run */
+ else
+ *runs += *p++ ; /* continue run */
+ while ( *p )
+ *(++runs) = *p++ ;
+ lastc = c ;
+ }
+
+ return runs - runs0 + 1 ;
+}
+
+
+/* Bitwise-OR two run-length coded scan lines. The run length
+ vectors a and b are OR-ed into c. If c is null, the result is
+ placed in a. The new image width is stored in pels if it is
+ not null. Returns the number of runs in the result. */
+
+int runor ( short *a, int na, short *b, int nb, short *c, int *pels )
+{
+ register short la, lb ;
+ int ia, ib, ic, np=0 ;
+ short tmp [ MAXRUNS ] ;
+
+ if ( ! c ) c = tmp ;
+
+ la = a [ ia = 0 ] ;
+ lb = b [ ib = 0 ] ;
+ c [ ic = 0 ] = 0 ;
+
+ while ( 1 ) {
+ if ( la <= lb ) { /* select shorter sub-run */
+ if ( ( ( ia | ib ) ^ ic ) & 1 ) /* OR of subruns same colour as c? */
+ c [ ++ic ] = la ; /* no, new output run */
+ else
+ c [ ic ] += la ; /* yes, add it */
+ lb -= la ; /* align subruns */
+ if ( ++ia >= na ) break ; /* done */
+ la = a [ ia ] ; /* get new subrun */
+ } else { /* same for line b ... */
+ if ( ( ( ia | ib ) ^ ic ) & 1 )
+ c [ ++ic ] = lb ;
+ else
+ c [ ic ] += lb ;
+ la -= lb ;
+ if ( ++ib >= nb ) break ;
+ lb = b [ ib ] ;
+ }
+ }
+
+ if ( ia < na )
+ while ( 1 ) {
+ if ( ( ia ^ ic ) & 1 )
+ c [ ++ic ] = la ;
+ else
+ c [ ic ] += la ;
+ if ( ++ia >= na ) break ;
+ la = a [ ia ] ;
+ }
+ else
+ while ( 1 ) {
+ if ( ( ib ^ ic ) & 1 )
+ c [ ++ic ] = lb ;
+ else
+ c [ ic ] += lb ;
+ if ( ++ib >= nb ) break ;
+ lb = b [ ib ] ;
+ }
+
+ if ( c == tmp ) for ( ia=0 ; ia <= ic ; ia++ ) np += a[ia] = c[ia] ;
+
+ if ( pels ) *pels = np ;
+
+ return ic + 1 ;
+}
+
+
+/* Get a number from a PBM file header while skipping whitespace
+ and comments. Returns the number or 0 on EOF. Reads one more
+ byte than used by the number. */
+
+int pbmdim ( IFILE *f )
+{
+ int c, n=0 ;
+
+ /* scan for first digit and skip comments */
+ while ( ! isdigit ( c = fgetc ( f->f ) ) && c >= 0 )
+ if ( c == '#' )
+ while ( ( c = fgetc ( f->f ) ) != '\n' && c >= 0 ) ;
+
+ /* get the number */
+ if ( c >= 0 && isdigit( c ) ) {
+ n = c - '0' ;
+ while ( isdigit ( c = fgetc ( f->f ) ) && c >= 0 )
+ n = n * 10 + c - '0' ;
+ }
+
+ return n ;
+}
+
+
+/* Append nb bits from in[from] to bit-mapped scan line buffer
+ where `from' is a bit (not byte) index. Bits in bytes are
+ ordered from MS to LS bit. Initialize before each scan line by
+ calling with nb=0 and in pointing to output buffer. Flush
+ after each scan line by calling with nb=0 and in=NULL. */
+
+#define putbits( c, b ) { x = ( x << (b) ) | (c) ; shift += (b) ; \
+ if ( shift >= 0 ) { *out++ = x >> shift ; shift -= 8 ; } }
+
+void copybits ( uchar *in, int from, short nb )
+{
+ uchar *f ;
+ short bits ;
+ static uchar *out ;
+ static short x, shift ;
+ static unsigned char right [ 9 ] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff } ;
+
+ if ( ! nb ) { /* reset for new scan line */
+ if ( in ) out = in ;
+ else putbits ( 0, -shift ) ; /* or flush bit buffer */
+ x = 0 ;
+ shift = -8 ;
+ } else {
+ f = in + ( from >> 3 ) ;
+ bits = 8 - ( from & 7 ) ;
+
+ if ( nb >= bits ) {
+ putbits ( *f++ & right [ bits ], bits ) ;
+ nb -= bits ;
+ } else {
+ putbits ( ( *f >> ( bits - nb ) ) & right [ bits ], nb ) ;
+ nb = 0 ;
+ }
+
+ while ( nb >= 8 ) { putbits ( *f++, 8 ) ; nb -= 8 ; }
+
+ if ( nb > 0 ) putbits ( *f >> ( 8 - nb ), nb );
+ }
+}
+
+
+/* Generate scan line 'line' of string 'txt' using font `font'
+ and store the runs in 'runs'. The font is scaled so it
+ appears to have cells of width w and height h. lmargin pixels
+ of white space are added at the left margin. Sets 'pels' to
+ line width if not null. Returns number of runs coded. */
+
+int texttorun ( uchar *txt, faxfont *font, short line,
+ int w, int h, int lmargin,
+ short *runs, int *ppels )
+{
+ uchar *in, out [ MAXLINELEN * MAXFONTW / 8 + 1 ] ;
+ int i, nc = 0, cw, nr, pels ;
+
+ line = ( line * font->h + h/2 ) / h ;
+
+ cw = font->w ;
+ if ( line >= font->h ) line = font->h - 1 ;
+ in = font->buf + 256/8 * cw * line ;
+
+ copybits ( out, 0, 0 ) ;
+ for ( i=0 ; txt[i] && i < MAXLINELEN ; i++ ) {
+ copybits ( in, font->offset [ txt[i] ], cw ) ;
+ nc++ ;
+ while ( ( txt[i] == HT ) && ( nc & 7 ) ) { /* tab */
+ copybits ( in, font->offset [ ' ' ], cw ) ;
+ nc++ ;
+ }
+ }
+ copybits ( 0, 0, 0 ) ;
+
+ nr = bittorun ( out, ( nc*cw + 7 )/8, runs ) ;
+
+ if ( font->w == w )
+ pels = nc*cw ;
+ else
+ pels = xscale ( runs, nr, ( w * 256 ) / font->w ) ;
+
+ pels += xshift ( runs, nr, lmargin ) ;
+
+ if ( ppels ) *ppels = pels ;
+
+ return nr ;
+}
+
+ /* Image File Input Functions */
+
+
+/* Names of file formats */
+
+char *iformatname [ NIFORMATS ] = IFORMATS ;
+char *oformatname [ NOFORMATS ] = OFORMATS ;
+char *pformatname [ NPFORMATS ] = PFORMATS ;
+
+/* Log the names of files still to be sent using the "msg()"
+ format string s. */
+
+void logifnames ( IFILE *f, char *s )
+{
+ PAGE *p ;
+ char *fn ;
+
+ if ( f && f->page )
+ for ( p = f->page ; p <= f->lastpage ; p++ ) {
+ fn = p->fname ;
+ if ( fn ) msg ( s, fn ) ;
+ }
+}
+
+
+/* Read run lengths for one scan line from T.4-coded IFILE f into buffer
+ runs. If pointer pels is not null it is used to save pixel count.
+ Returns number of runs stored, EOF on RTC, or -2 on EOF or other
+ error. */
+
+int readruns ( IFILE *f, short *runs, int *pels )
+{
+ int err=0, c=EOF, n ;
+ register int x ;
+ dtab *tab, *t ;
+ short shift ;
+ short *p, *maxp, *q, len=0, npad=0 ;
+ DECODER *d ;
+ uchar reverse=f->page->revbits ;
+
+ maxp = ( p = runs ) + MAXRUNS ;
+ d = &f->d ;
+
+ x = d->x ; shift = d->shift ; tab = d->tab ; /* restore decoder state */
+
+ do {
+ do {
+ while ( shift < 0 ) {
+ if ( ( c = fgetc ( f->f ) ) == EOF ) {
+ x = ( x << 15 ) | 1 ; shift += 15 ; /* EOL pad at EOF */
+ npad++ ;
+ } else {
+ if ( reverse ) c = normalbits [ c & 0xff ] ;
+ x = ( x << 8 ) | c ; shift += 8 ;
+ }
+ }
+ t = tab + ( ( x >> shift ) & 0x1ff ) ;
+ tab = t->next ;
+ shift -= t->bits ;
+ } while ( ! t->code ) ;
+ if ( p < maxp ) *p++ = t->code ;
+ } while ( t->code != -1 ) ;
+
+ d->x = x ; d->shift = shift ; d->tab = tab ; /* save state */
+
+ if ( npad > 1 ) msg ("W EOF before RTC" ) ;
+
+ if ( p >= maxp ) msg ( "W run length buffer overflow" ) ;
+
+ /* combine make-up and terminating codes and remove +1 offset
+ in run lengths */
+
+ n = p - runs - 1 ;
+ for ( p = q = runs ; n-- > 0 ; )
+ if ( *p > 64 && n-- > 0 ) {
+ len += *q++ = p[0] + p[1] - 2 ;
+ p+=2 ;
+ } else {
+ len += *q++ = *p++ - 1 ;
+ }
+ n = q - runs ;
+
+ /* check for RTC and errors */
+
+ if ( len )
+ d->eolcnt = 0 ;
+ else
+ if ( ++(d->eolcnt) >= RTCEOL ) err = EOF ;
+
+ if ( c == EOF ) {
+ if ( ferror ( f->f ) ) {
+ err = -msg ("ES2error reading fax file:") ;
+ } else {
+ err = -2 ;
+ }
+ }
+
+ if ( pels ) *pels = len ;
+
+ return err ? err : n ;
+}
+
+
+/* Read a PCX compressed bit-map */
+
+int readpcx ( char *p, int len, IFILE *f )
+{
+ int err=0, n, c ;
+
+ while ( !err && len > 0 ) {
+ if ( ( c = fgetc ( f->f ) ) < 0 ) {
+ err = msg ( "ES2 PCX read failed" ) ;
+ } else {
+ if ( ( c & 0xc0 ) == 0xc0 ) { /* a run count */
+ n = c & 0x3f ;
+ c = fgetc ( f->f ) ;
+ if ( ( c = fgetc ( f->f ) ) < 0 ) {
+ err = msg ( "ES2 PCX read failed" ) ;
+ } else {
+ memset ( p, c, n <= len ? n : len ) ;
+ p += n ;
+ len -= n ;
+ }
+ } else { /* bits 0 to 7 are image data */
+ *p++ = c ;
+ len-- ;
+ }
+ }
+ }
+
+ if ( len != 0 )
+ msg ( "W PCX run-length coding error" ) ;
+
+ return err ;
+}
+
+/* Read a scan line from the current page of IFILE f. Stores
+ number of runs in runs and line width in pels if not null.
+ Pages ends at EOF. Text pages also end if a complete text line
+ would not fit or if the line contains a formfeed character.
+ PBM pages also end when all lines in the bitmap have been
+ read. Fax pages also end at RTC. Returns number of runs stored
+ or EOF at end of page. */
+
+int readline ( IFILE *f, short *runs, int *pels )
+{
+ int nr = 0, nb ;
+ uchar bits [ MAXBITS ] ;
+
+ if ( f->lines != 0 ) { /* -1 allowed as well */
+ switch ( f->page->format ) {
+
+ case P_TEXT :
+ if ( f->txtlines <= 0 ) { /* need another text line */
+ if ( fgets ( f->text, MAXLINELEN, f->f ) ) {
+ f->txtlines = f->charh ;
+ if ( strchr ( f->text, FF ) ) {
+ f->lines = 0 ; /* no more lines in this page */
+ nr = EOF ; /* don't return any */
+ }
+ } else {
+ nr = EOF ;
+ }
+ }
+ if ( nr != EOF ) {
+ nr = texttorun ( (uchar*) f->text, f->font, f->charh - f->txtlines,
+ f->charw, f->charh, f->lmargin,
+ runs, pels ) ;
+ f->txtlines-- ;
+ }
+ break ;
+
+ case P_RAW:
+ case P_PBM:
+ if ( fread ( bits, 1, f->page->w/8, f->f ) != f->page->w/8 ) {
+ nr = EOF ;
+ } else {
+ nr = bittorun ( bits, f->page->w/8, runs ) ;
+ if ( pels ) *pels = f->page->w ;
+ }
+ break ;
+
+ case P_FAX:
+ nr = readruns ( f, runs, pels ) ;
+ break ;
+
+ case P_PCX:
+ nb = ( ( f->page->w + 15 ) / 16 ) * 2 ; /* round up */
+ if ( readpcx ( bits, nb, f ) != 0 ) {
+ nr = EOF ;
+ } else {
+ nr = bittorun ( bits, nb, runs ) ;
+ if ( pels ) *pels = f->page->w ;
+ }
+ break ;
+
+ }
+ } else {
+ nr = EOF ;
+ }
+
+ if ( nr >= 0 && f->lines > 0 ) f->lines-- ;
+
+ return nr ;
+}
+
+
+/* Deduce the file type by scanning buffer p of n bytes. */
+
+int getformat ( uchar *p, int n )
+{
+ int format = 0 ;
+
+ /* figure out file type if not already set */
+
+ if ( ! format && n < 2 ) {
+ format = I_TEXT ;
+ msg ( "W only read %d byte(s) from input file, assuming text", n ) ;
+ }
+
+ if ( ! format && ! p[0] && ! ( p[1] & 0xe0 ) ) {
+ format = I_FAX ;
+ }
+
+ if ( ! format && ! strncmp ( p, "P4", 2 ) ) {
+ format = I_PBM ;
+ }
+
+ if ( ! format && n >= 128 && p[0] == 0x0a &&
+ strchr ("\02\03\05", p[1] ) && p[2] <= 1 ) {
+ if ( p[65] != 1 ) {
+ msg ( "E can't read colour PCX" ) ;
+ } else {
+ format = p[2] ? I_PCX : I_PCX ;
+ }
+ }
+
+ if ( ! format && ! strncmp ( p, "%!", 2 ) ) {
+ msg ( "W Postscript input file will be treated as text" ) ;
+ }
+
+ if ( ! format && ( p[0] == 'M' || p[1] == 'I' ) && ( p[1] == p[0] ) ) {
+ format = I_TIFF ;
+ }
+
+ if ( ! format &&
+ ( p[0] == 0x3a && p[1] == 0xde &&
+ p[2] == 0x68 && p[3] == 0xb1) ) {
+ format = I_DCX ;
+ }
+
+ if ( ! format && n ) { /* "90% printable" heuristic */
+ int i, nprint = 0 ;
+ for ( i=0 ; i<n ; i++ ) {
+ if ( isspace ( p[i] ) || isprint ( p[i] ) ) {
+ nprint++ ;
+ }
+ }
+ if ( ( nprint / (float) n ) > 0.90 ) {
+ format = I_TEXT ;
+ }
+ }
+
+ return format ;
+}
+
+
+/* initialize page descriptor */
+
+void page_init ( PAGE *p, char *fn )
+{
+ p->fname = fn ;
+ p->offset = 0 ;
+ p->w = DEFWIDTH ;
+ p->h = DEFHEIGHT ;
+ p->xres = DEFXRES ;
+ p->yres = DEFYRES ;
+ p->format = P_FAX ;
+ p->revbits = 0 ;
+}
+
+
+void page_report ( PAGE *p, int fmt, int n )
+{
+ msg ( "F page %d : %s + %ld : %dx%d @ %.fx%.f dpi %s/%s",
+ n,
+ p->fname, p->offset, p->w, p->h,
+ p->xres, p->yres,
+ iformatname [fmt], pformatname [p->format] ) ;
+}
+
+/* File handling for undefined file types */
+
+#define auto_first 0
+#define auto_next 0
+
+/* File handling for TIFF files */
+
+#define tiff_reset 0
+
+/* Name of TIFF tag 'tag'. */
+
+char *tagname ( int tag )
+{
+ static struct tagnamestruct { int code ; char *name ; }
+ tagnames [] = {
+ { 256, "width" },
+ { 257, "length" },
+ { 258, "bits/sample" },
+ { 259, "compresssion(g3=3)" },
+ { 262, "photometric(0-min=white)" },
+ { 266, "fill order(msb2lsb=1)" },
+ { 273, "strip offsets" },
+ { 274, "orientation(1=normal)" },
+ { 277, "samples/pixel" },
+ { 278, "rows/strip" },
+ { 279, "strip byte counts" },
+ { 282, "xresolution" },
+ { 283, "yresolution" },
+ { 284, "storage(1=single plane)" },
+ { 292, "g3options" },
+ { 296, "resolution units(2=in,3=cm)" },
+ { 297, "page number" },
+ { 327, "clean fax(0=clean/1=regen/2=errors)" },
+ {0,0} },
+ *p ;
+
+ for ( p=tagnames ; p->code ; p++ )
+ if ( tag == p->code ) break ;
+ return p->code ? p->name : "unknown tag" ;
+}
+
+/* Read 2- or 4-byte integers from a file and correct for file
+ endianness. Returns 0 if OK, 1 on error. */
+
+int fread2 ( unsigned short *p, IFILE *f )
+{
+ int err=0 ;
+ uchar c[2] ;
+
+ err = fread ( c, 1, 2, f->f ) == 2 ? 0 : 1 ;
+
+ *p = f->bigend ?
+ c[0] << 8 | c[1] << 0 :
+ c[1] << 8 | c[0] << 0 ;
+
+ return err ;
+}
+
+int fread4 ( unsigned long *p, IFILE *f )
+{
+ int err=0 ;
+ uchar c[4] ;
+
+ err = fread ( c, 1, 4, f->f ) == 4 ? 0 : 1 ;
+
+ *p = f->bigend ?
+ c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3] << 0 :
+ c[3] << 24 | c[2] << 16 | c[1] << 8 | c[0] << 0 ;
+
+ return err ;
+}
+
+
+/* Read a TIFF directory at current file offset, save image
+ format information and seek to next directory if any. Returns
+ 0 if OK, 2 on errors. */
+
+int tiff_next ( IFILE *f )
+{
+ int err=0 ;
+ unsigned short ntag, tag, type ;
+ unsigned long count, tv ;
+ double ftv ;
+
+ msg ( "F+ TIFF directory at %ld", ftell ( f->f ) ) ;
+
+ if ( fread2 ( &ntag, f ) ) {
+ err = msg ( "E2can't read TIFF tag count" ) ;
+ } else {
+ msg ( "F+ with %d tags", (int) ntag ) ;
+ }
+
+ for ( ; ! err && ntag > 0 ; ntag-- ) {
+
+ err = err || fread2 ( &tag, f ) ;
+ err = err || fread2 ( &type, f ) ;
+ err = err || fread4 ( &count, f ) ;
+
+ if ( type == 3 ) { /* left-aligned short */
+ unsigned short a, b ;
+ err = err || fread2 ( &a, f ) ;
+ err = err || fread2 ( &b, f ) ;
+ tv = a ;
+ } else { /* long or offset to data */
+ err = err || fread4 ( &tv, f ) ;
+ }
+
+ if ( type == 5 ) { /* float as ratio in directory data */
+ long a, b, where=0 ;
+ err = err || ( ( where = ftell ( f->f ) ) < 0 ) ;
+ err = err || fseek ( f->f, tv, SEEK_SET ) ;
+ err = err || fread4 ( &a, f ) ;
+ err = err || fread4 ( &b, f ) ;
+ err = err || fseek ( f->f, where, SEEK_SET ) ;
+ ftv = (float) a / ( b ? b : 1 ) ;
+ } else {
+ ftv = 0.0 ;
+ }
+
+ if ( err ) {
+ err = msg ( "ES2can't read TIFF tag" ) ;
+ continue ;
+ }
+
+#ifdef TIFF_DEBUG
+ {
+ char *tagtype[] = { "none", "byte", "ascii", "short", "long", "ratio" } ;
+ msg ( "F %3d %-5s tag %s %5ld (%3d:%s)",
+ count,
+ type <= 5 ? tagtype[type] : "other",
+ count > 1 ? "@" : "=",
+ type == 5 ? (long) ftv : tv, tag, tagname(tag) ) ;
+ }
+#endif
+
+ switch ( tag ) {
+ case 256 : /* width */
+ f->page->w = tv ;
+ break ;
+ case 257 : /* height */
+ f->page->h = tv ;
+ break ;
+ case 259 : /* compression: 1=none, 3=G3 */
+ if ( tv == 1 ) {
+ f->page->format = P_RAW ;
+ } else if ( tv == 3 ) {
+ f->page->format = P_FAX ;
+ } else {
+ err = msg ( "E2can only read TIFF/G3 or TIFF/uncompressed" ) ;
+ }
+ break ;
+ case 266 : /* fill order */
+ f->page->revbits = ( tv == 2 ? 1 : 0 ) ;
+ break ;
+ case 273 : /* data offset */
+ if ( count != 1 )
+ err = msg ( "E2can't read multi-strip TIFF files" ) ;
+ else
+ f->page->offset = tv ;
+ break ;
+ case 282 : /* x resolution */
+ f->page->xres = ftv ;
+ break ;
+ case 283 : /* y resolution */
+ f->page->yres = ftv ;
+ break ;
+ case 292 : /* T4 options: 1=2D, 2=uncompressed */
+ if ( tv & 0x1 )
+ err = msg ( "E2can't read 2D compressed TIFF-F file" ) ;
+ if ( tv & 0x2 )
+ err = msg ( "E2can't read uncompressed TIFF-F file" ) ;
+ break ;
+ case 296 : /* units: 2=in, 3=cm */
+ if ( tv == 3 ) {
+ f->page->xres *= 2.54 ;
+ f->page->yres *= 2.54 ;
+ }
+ break ;
+ }
+
+ } /* end of tag reading loop */
+
+
+ if ( f->page->format == I_AUTO ) {
+ msg ( "W missing TIFF compression format, set to raw" ) ;
+ f->page->format = P_RAW ;
+ }
+
+ if ( ! err ) {
+
+ if ( fread4 ( &(f->next), f ) ) {
+ err = msg ( "E2can't read offset to next TIFF directory" ) ;
+ } else {
+ if ( f->next ) {
+ msg ( "F , next directory at %ld.", f->next ) ;
+ if ( fseek ( f->f, f->next, SEEK_SET ) )
+ err = msg ( "ES2 seek to next TIFF directory failed" ) ;
+ } else {
+ msg ( "F , last image." ) ;
+ }
+ }
+
+ }
+
+ if ( ! f->page->offset )
+ err = msg ( "E2 missing offset to TIFF data" ) ;
+
+ return err ;
+}
+
+
+int tiff_first ( IFILE *f )
+{
+ short magic, version ;
+
+ fread ( (uchar*) &magic, 1, 2, f->f ) ;
+ f->bigend = ( *(uchar*) &magic == 'M' ) ? 1 : 0 ;
+ fread2 ( &version, f ) ;
+ fread4 ( &(f->next), f ) ;
+
+ msg ( "F TIFF version %d.%d file (%s-endian)",
+ version/10, version%10, f->bigend ? "big" : "little" ) ;
+
+ fseek ( f->f, f->next, SEEK_SET ) ;
+
+ return tiff_next ( f ) ;
+}
+
+
+/* File handling for text files. */
+
+int text_next ( IFILE *f )
+{
+ int err = 0, i, nc ;
+ char buf [ MAXLINELEN ] ;
+
+ f->page->offset = ftell ( f->f ) ;
+ f->page->format = P_TEXT ;
+
+ nc = ( f->page->w - f->lmargin ) / f->charw ;
+
+ if ( nc > MAXLINELEN )
+ nc = MAXLINELEN ;
+
+ for ( i = 0 ; i < f->pglines && fgets ( buf, nc, f->f ) ; i++ ) ;
+
+ f->next = ! feof(f->f) ? ftell ( f->f ) : 0 ;
+
+ err = ferror(f->f) ? 2 : 0 ;
+
+ return err ;
+}
+
+
+int text_first ( IFILE *f )
+{
+ static faxfont defaultfont ;
+
+ if ( ! f->font ) {
+ /* use default font scaled 2X, 1 inch margin, 66 lines/page */
+ f->font = &defaultfont ;
+ readfont ( 0, f->font ) ;
+ if ( ! f->charw ) f->charw = 2 * f->font->w ;
+ if ( ! f->charh ) f->charh = 2 * f->font->h ;
+ if ( ! f->lmargin ) f->lmargin = 204 ;
+ if ( ! f->pglines ) f->pglines = DEFPGLINES ;
+ }
+ /* if not set, take default values from font */
+ if ( ! f->charw ) f->charw = f->font->w ;
+ if ( ! f->charh ) f->charh = f->font->h ;
+ if ( ! f->lmargin ) f->lmargin = 0 ;
+ if ( ! f->pglines ) f->pglines = f->page->h / f->charh - 6 ;
+
+ return text_next ( f ) ;
+}
+
+
+/* File handling for PBM files */
+
+int pbm_first ( IFILE *f )
+{
+ int err=0 ;
+
+ fseek ( f->f, 2, SEEK_SET ) ;
+
+ if ( ! ( f->page->w = pbmdim ( f ) ) || ! ( f->page->h = pbmdim ( f ) ) ) {
+ err = msg ( "E2 EOF or 0 dimension in PBM header" ) ;
+ } else if ( f->page->w % 8 ) {
+ err = msg ( "E2 PBM width must be multiple of 8" ) ;
+ } else {
+ msg ( "F read %dx%d PBM header", f->page->w, f->page->h ) ;
+ }
+
+ f->page->offset = ftell ( f->f ) ;
+ f->page->format = P_PBM ;
+ f->next = 0 ;
+
+ return err ;
+}
+
+#define pbm_next 0
+
+
+/* File handling for FAX files */
+
+#define fax_first 0
+
+#define fax_next 0
+
+/* File handling for PCX files */
+
+/* get a 16-bit word in Intel byte order. */
+
+int fgeti ( IFILE *f )
+{
+ return fgetc ( f->f ) + fgetc ( f->f ) * 256 ;
+}
+
+int pcx_first ( IFILE *f )
+{
+ int err=0, xmin, xmax, ymin, ymax, nc, nb ;
+ long start ;
+
+ start = ftell ( f->f ) ;
+
+ fseek ( f->f, start+4, SEEK_SET ) ;
+ xmin = fgeti ( f ) ;
+ ymin = fgeti ( f ) ;
+ xmax = fgeti ( f ) ;
+ ymax = fgeti ( f ) ;
+
+ f->page->w = xmax - xmin + 1 ;
+ f->page->h = ymax - ymin + 1 ;
+
+ f->page->xres = fgeti ( f ) ;
+ f->page->yres = fgeti ( f ) ;
+
+ fseek ( f->f, start+0x41, SEEK_SET ) ;
+ nc = fgetc ( f->f ) ;
+ nb = fgeti ( f ) ;
+
+ if ( nc != 1 )
+ msg ( "W mono PCX file has %d colour planes", nc ) ;
+
+ if ( nb != ( f->page->w + 15 ) / 16 * 2 )
+ msg ( "W PCX file has %d bytes per scan line for %d pels",
+ nb, f->page->w ) ;
+
+ f->page->offset = start + 128 ;
+ f->page->format = P_PCX ;
+
+ return err ;
+}
+
+#define pcx_next 0
+
+/* File handling for DCX files */
+
+int dcx_next ( IFILE *f )
+{
+ int err=0 ;
+ long thisp, nextp ;
+
+ /* get this and next pages' offsets */
+
+ fseek ( f->f, f->next, SEEK_SET ) ;
+ fread4 ( &thisp, f ) ;
+ fread4 ( &nextp, f ) ;
+
+ /* save address of next directory entry, if any */
+
+ f->next = nextp ? f->next + 4 : 0 ;
+
+ if ( ! thisp )
+ err = msg ( "E2 can't happen (dcx_next)" ) ;
+ else
+ fseek ( f->f, thisp, SEEK_SET ) ;
+
+ return err ? err : pcx_first ( f ) ;
+}
+
+int dcx_first ( IFILE *f )
+{
+ f->bigend = 0 ;
+ f->next = 4 ;
+ return dcx_next ( f ) ;
+}
+
+
+#define raw_reset 0
+#define raw_first 0
+#define raw_next 0
+
+
+/* input file state reset for different compression methods */
+
+#define pcx_reset 0
+#define pbm_reset 0
+
+int text_reset ( IFILE *f )
+{
+ int err=0 ;
+
+ f->lines = ( ( f->pglines * f->charh ) / f->charh ) * f->charh ;
+ f->txtlines = 0 ;
+ f->page->yres = f->lines > 1078 ? 196 : 98 ; /* BOGUS */
+
+ return err ;
+}
+
+int fax_reset ( IFILE *f )
+{
+ int pels ;
+ short runs [ MAXRUNS ] ;
+
+ newDECODER ( &f->d ) ;
+ if ( readruns ( f, runs, &pels ) < 0 || pels ) /* skip first EOL */
+ msg ( "W first line has %d pixels: probably not fax data", pels ) ;
+ f->lines = -1 ;
+
+ return 0 ;
+}
+
+
+/* Skip to start of same (dp=0) or next (dp=1) page image.
+ Returns 0 if OK, 1 if no more pages, 2 on errors. */
+
+int nextipage ( IFILE *f, int dp )
+{
+ int err=0 ;
+
+ int ( *reset [NPFORMATS] ) ( IFILE * ) = {
+ raw_reset, fax_reset, pbm_reset, text_reset, pcx_reset
+ }, (*pf)(IFILE*) ;
+
+ /* close current file if any and set to NULL */
+
+ if ( f->f ) {
+ fclose ( f->f ) ;
+ f->f = 0 ;
+ }
+
+ /* if requested, point to next page and check if done */
+
+ if ( dp ) {
+ f->page++ ;
+ }
+
+ if ( f->page > f->lastpage ) {
+ err = 1 ;
+ }
+
+ /* open the file and seek to start of image data */
+
+ if ( ! err ) {
+ f->f = fopen ( f->page->fname, (f->page->format == P_TEXT) ? "r" : "rb" ) ;
+ if ( ! f->f )
+ err = msg ( "ES2can't open %s:", f->page->fname ) ;
+ }
+
+ if ( ! err && fseek ( f->f, f->page->offset, SEEK_SET ) )
+ err = msg ( "ES2 seek failed" ) ;
+
+ /* default initializations */
+
+ f->lines = f->page->h ;
+
+ /* coding-specific initializations for this page */
+
+ if ( ! err ) {
+ pf = reset[f->page->format] ;
+ if ( pf )
+ err = (*pf)(f) ;
+ }
+
+ return err ;
+}
+
+
+/* Returns true if on last file. */
+
+int lastpage ( IFILE *f )
+{
+ return f->page >= f->lastpage ;
+}
+
+#define dfax_first 0
+#define dfax_next 0
+
+/* Initialize an input (IFILE) structure. This structure
+ collects the data about images to be processed to allow a
+ simple interface for functions that need to read image files.
+
+ The IFILE is initialized by building an array of information
+ for each page (image) in all of the files.
+
+ The page pointer index is initialized so that the first call
+ to nextipage with dp=1 actually opens the first file.
+
+*/
+
+
+int newIFILE ( IFILE *f, char **fnames )
+{
+ int err=0, i, n, fformat=0 ;
+ char **p ;
+ uchar buf[128] ;
+ int ( *fun ) ( IFILE * ) ;
+
+ int ( *first [NIFORMATS] ) ( IFILE * ) = {
+ auto_first, pbm_first, fax_first, text_first, tiff_first,
+ dfax_first, pcx_first, raw_first, dcx_first
+ } ;
+
+ int ( *next [NIFORMATS] ) ( IFILE * ) = {
+ auto_next, pbm_next, fax_next, text_next, tiff_next,
+ dfax_next, pcx_next, raw_next, dcx_next
+ } ;
+
+ f->page = f->pages ;
+
+ /* get info for all pages in all files */
+
+ for ( p=fnames ; ! err && *p ; p++ ) {
+
+ if ( ! ( f->f = fopen ( *p, "rb" ) ) )
+ err = msg ( "ES2 can't open %s:", *p ) ;
+
+ if ( ! err ) {
+ n = fread ( buf, 1, 128, f->f ) ;
+ if ( ferror ( f->f ) )
+ err = msg ( "ES2 can't open %s:", *p ) ;
+ }
+
+ if ( ! err ) {
+ fformat = getformat ( buf, n ) ;
+ if ( ! fformat )
+ err = msg ( "E2 can't get format of %s", *p ) ;
+ }
+
+ if ( ! err && fseek ( f->f, 0, SEEK_SET ) )
+ err = msg ( "ES2 can't rewind %s:", *p ) ;
+
+ /* get format information for all pages in this file */
+
+ for ( i=0 ; ! err ; i++ ) {
+
+ page_init ( f->page, *p ) ;
+
+ if ( ( fun = i ? next[fformat] : first[fformat] ) )
+ err = (*fun)(f) ;
+
+ if ( ! err ) {
+
+ page_report ( f->page, fformat, f->page - f->pages + 1 ) ;
+
+ f->page++ ;
+
+ if ( f->page >= f->pages + MAXPAGE )
+ err = msg ( "E2 too many pages (max is %d)", MAXPAGE ) ;
+ }
+
+ if ( ! f->next ) break ;
+ }
+
+ if ( f->f ) {
+ fclose ( f->f ) ;
+ f->f = 0 ;
+ }
+
+ }
+
+ f->lastpage = f->page - 1 ;
+
+ f->page = f->pages ;
+
+ if ( ! normalbits[1] ) initbittab() ; /* bit-reverse table initialization */
+
+ return err ;
+}
+
+ /* Image File Output Functions */
+
+/* Strings and function to write a bit map in HP-PCL format. The only
+ compression is removal of trailing zeroes. Margins and resolution are
+ set before first write. */
+
+char *PCLBEGIN =
+ "\033E" /* Printer reset. */
+ "\033&l0E" /* top margin = 0 */
+ "\033&a0L" /* left margin = 0 */
+ "\033*t%dR" /* Set raster graphics resolution */
+ "\033*r1A" ; /* Start raster graphics, rel. adressing */
+
+char *PCLEND =
+ "\033*rB" /* end raster graphics */
+ "\014" /* form feed */
+ "\033E" ; /* Printer reset. */
+
+void pclwrite ( OFILE *f, unsigned char *buf, int n )
+{
+ while ( n > 0 && buf [ n-1 ] == 0 ) n-- ;
+ fprintf( f->f, "\033*b%dW", n ) ;
+ fwrite ( buf, n, 1, f->f ) ;
+}
+
+
+/* Write a bit map as (raw) Portable Gray Map (PGM) format after
+ decimating by a factor of 4. Sums bits in each 4x4-pel square
+ to compute sample value. This function reduces each dimension
+ of a bit map by 4 (it writes n*8/4 pixels per scan line and
+ one scan line for every 4 in). The 17 possible sample values
+ are spread linearly over the range 0-255. */
+
+void pgmwrite ( OFILE *f, uchar *buf, int n )
+{
+ static uchar gval [ MAXBITS * 8 / 4 ] ;
+ static int init=0, lines=0 ;
+ static uchar hbits [ 256 ], lbits [ 256 ] ;
+ static int nybblecnt [ 16 ] = { 0,1,1,2, 1,2,2,3, 1,2,2,3, 2,3,3,4 } ;
+ static uchar corr [ 17 ] = { 255, 239, 223, 207, 191, 175, 159, 143, 127,
+ 111, 95, 79, 63, 47, 31, 15, 0 } ;
+ int m ;
+ uchar *p, *q ;
+
+ if ( ! init ) { /* build table of bit counts in each nybble */
+ short i ;
+ for ( i=0 ; i<256 ; i++ ) {
+ hbits [ i ] = nybblecnt [ i >> 4 & 0x0f ] ;
+ lbits [ i ] = nybblecnt [ i & 0x0f ] ;
+ }
+ init = 1 ;
+ }
+
+ for ( m=n, p=gval, q=buf ; m-- > 0 ; q++ ) {
+ *p++ += hbits [ *q ] ;
+ *p++ += lbits [ *q ] ;
+ }
+
+ if ( ( lines++ & 0x03 ) == 0x03 ) {
+ for ( p=gval, m=2*n ; m-- > 0 ; p++ ) *p = corr [ *p ] ;
+ fwrite ( gval, 1, 2*n, f->f ) ;
+ memset ( gval, 0, 2*n ) ;
+ }
+}
+
+
+/* Postscript image data is differentially coded vertically and
+ run-length coded horizontally. A leading byte (n) defines the type
+ of coding for subsequent data:
+
+ 0 repeat previous line
+ 1-127 n data bytes follow
+ 128-254 copy n-127 bytes from previous line
+ 255 n repeat the next character 'n' times
+
+ The overhead for coding a copy is 2 bytes (copy count, data count),
+ so copies > 2 bytes should be so coded. The overhead for coding a
+ run is 4 bytes (255, count, byte, data count), so runs > 4 bytes
+ should be so coded. Copies decode/execute faster and code more
+ compactly so are preferred over runs.
+
+*/
+
+const char PSBEGIN [] = /* start of file */
+ "%%!PS-Adobe-2.0 EPSF-2.0 \n"
+ "%%%%Creator: efax (Copyright 1995 Ed Casas) \n"
+ "%%%%Title: efix output\n"
+ "%%%%Pages: (atend) \n"
+ "%%%%BoundingBox: 0 0 %d %d \n"
+ "%%%%BeginComments \n"
+ "%%%%EndComments \n"
+ "/val 1 string def \n"
+ "/buf %d string def \n"
+ "/getval { \n"
+ " currentfile val readhexstring pop 0 get \n"
+ "} bind def \n"
+ "/readbuf { \n"
+ " 0 %% => index \n"
+ " { \n"
+ " dup buf length ge { exit } if \n"
+ " getval %% => index run_length \n"
+ " dup 127 le { \n"
+ " dup 0 eq { \n"
+ " pop buf length \n"
+ " } { \n"
+ " currentfile buf 3 index 3 index getinterval readhexstring pop pop\n"
+ " } ifelse \n"
+ " } { \n"
+ " dup 255 eq { \n"
+ " pop getval getval %% => index run_length value \n"
+ " 2 index 1 3 index 2 index add 1 sub %% => ... start 1 end \n"
+ " { buf exch 2 index put } for \n"
+ " pop \n"
+ " } { \n"
+ " 127 sub \n"
+ " } ifelse \n"
+ " } ifelse \n"
+ " add %% => index \n"
+ " } loop \n"
+ " pop \n"
+ " buf \n"
+ "} bind def \n"
+ "%%%%EndProlog \n" ;
+
+const char PSPAGE [] = /* start of page */
+ "%%%%Page: %d %d \n"
+ "gsave \n"
+ "%f %f translate \n"
+ "%f %f scale \n"
+ "%d %d %d [ %d %d %d %d %d %d ] { readbuf } image \n" ;
+
+const char PSPAGEEND [] = /* end of page */
+ "\n"
+ "grestore \n"
+ "showpage \n" ;
+
+const char PSEND [] = /* end of file */
+ "%%Trailer \n"
+ "%%%%Pages: %d \n" ;
+
+
+void psinit ( OFILE *f, int newfile, int page, int w, int h, int n )
+{
+ float ptw, pth ;
+
+ if ( ! f ) {
+ msg ( "E2 can't happen (psinit)" ) ;
+ return ;
+ }
+
+ ptw = w/f->xres * 72.0 ; /* convert to points */
+ pth = h/f->yres * 72.0 ;
+
+ if ( newfile )
+ fprintf ( f->f, PSBEGIN,
+ (int) ptw, (int) pth, /* Bounding Box */
+ n ) ; /* buffer string length */
+
+ fprintf ( f->f, PSPAGE,
+ page, page, /* page number */
+ 0.0, 0.0, /* shift */
+ ptw, pth, /* scaling */
+ w, h, 1, /* image size */
+ w, 0, 0, -h, 0, h ) ; /* CTM */
+ f->pslines = 0 ;
+ f->lastpageno = page ;
+}
+
+
+char nhexout = 0, hexchars [ 16 ] = "0123456789abcdef" ;
+
+#define hexputc( f, c ) ( \
+ putc ( hexchars [ (c) >> 4 ], f ), \
+ putc ( hexchars [ (c) & 0x0f ], f ), \
+ ( ( ( nhexout++ & 31 ) == 31 ) ? putc ( '\n', f ) : 0 ) )
+
+void hexputs ( FILE *f, uchar *p, int n )
+{
+ uchar c ;
+ if ( n > 0 ) {
+ hexputc ( f, n ) ;
+ while ( n-- ) { c = *p++ ^ 0xff ; hexputc ( f, c ) ; }
+ }
+}
+
+/* Encode into postscript. If not a repeated line, test (using
+ index j) from current position (i) for possible encodings as:
+ copy of > 2 bytes, runs of > 4 or data >=127. Otherwise the
+ byte is skipped. Uncoded bytes are output from the last
+ uncoded byte (l) before output of runs/copies. */
+
+void pswrite ( OFILE *f, unsigned char *buf, int n )
+{
+ int i, j, l ;
+ static unsigned char last [ MAXBITS ] ;
+
+ l=i=0 ;
+
+ if ( ! f || ! buf || n<0 ) {
+ msg ( "E2 can't happen (pswrite)" ) ;
+ return ;
+ }
+
+ for ( j=0 ; j<n && buf[j]==last[j] && f->pslines ; j++ ) ;
+ if ( j == n ) { /* repeat line */
+ hexputc ( f->f, 0 ) ;
+ l=i=n ;
+ }
+
+ while ( i<n ) {
+
+ for ( j=i ; j<n && buf[j]==last[j] && j-i<127 && f->pslines ; j++ ) ;
+ if ( j-i > 2 ) { /* skip */
+ hexputs ( f->f, buf+l, i-l ) ;
+ hexputc ( f->f, j-i + 127 ) ;
+ l=i=j ;
+ } else {
+ for ( j=i ; j<n && buf[j]==buf[i] && j-i<255 ; j++ ) ;
+ if ( j-i > 4 ) { /* run */
+ hexputs ( f->f, buf+l, i-l ) ;
+ hexputc ( f->f, 255 ) ;
+ hexputc ( f->f, j-i ) ;
+ hexputc ( f->f, buf[i] ^ 0xff ) ;
+ l=i=j ;
+ } else {
+ if ( i-l >= 127 ) { /* maximum data length */
+ hexputs ( f->f, buf+l, i-l ) ;
+ l=i ;
+ } else { /* data */
+ i++ ;
+ }
+ }
+ }
+
+ }
+ hexputs ( f->f, buf+l, i-l ) ;
+
+ if ( n >= 0 )
+ memcpy ( last, buf, n ) ;
+
+ f->pslines++ ;
+}
+
+
+/* Write 2- and 4-byte integers to an image output file. Return
+ as for fwrite. */
+
+int fwrite2 ( short s, OFILE *f )
+{
+ uchar *p = (void*) &s ;
+ return fwrite ( bigendian ? p + sizeof(short) - 2 : p, 2, 1, f->f ) ;
+}
+
+int fwrite4 ( long l, OFILE *f )
+{
+ uchar *p = (void*) &l ;
+ return fwrite ( bigendian ? p + sizeof(long ) - 4 : p, 4, 1, f->f ) ;
+}
+
+
+/* Write a TIFF directory tag. Returns 0 if OK, 1 on errors. */
+
+int wtag ( OFILE *f, int lng, short tag, short type, long count, long offset )
+{
+ int err=0 ;
+
+ err = err || ! fwrite2 ( tag, f ) ;
+ err = err || ! fwrite2 ( type, f ) ;
+ err = err || ! fwrite4 ( count, f ) ;
+ if ( lng ) {
+ err = err || ! fwrite4 ( offset, f ) ;
+ } else {
+ err = err || ! fwrite2 ( offset, f ) ;
+ err = err || ! fwrite2 ( 0, f ) ;
+ }
+
+ if ( err ) msg ( "ES2 can't write TIFF tag" ) ;
+ return err ;
+}
+
+
+/* Write TIFF header and directory. File format based on Sam
+ Leffler's tiff.h. Can only be used for single-image TIFFs
+ because always seeks to start of file to re-write the
+ header. */
+
+#define NTAGS 17 /* number of tags in directory */
+#define NRATIO 2 /* number of floats (as ratios) */
+
+int tiffinit ( OFILE *f )
+{
+ int err=0, compr=1 ;
+ long tdoff, doff ;
+
+ fseek ( f->f, 0, SEEK_SET ) ;
+
+ /* 0 ==> (start of TIFF file) */
+
+ /* write magic, TIFF version and offset to directory */
+
+ fwrite2 ( bigendian ? 0x4d4d : 0x4949, f ) ;
+ fwrite2 ( 42, f ) ;
+ fwrite4 ( 8, f ) ;
+
+ /* 8 ==> directory */
+
+ fwrite2 ( NTAGS, f ) ;
+
+ /* figure out offsets within file and compression code */
+
+ tdoff = 8 + 2 + NTAGS*12 + 4 ; /* offset to directory data */
+ doff = tdoff + NRATIO*8 ; /* offset to image data */
+
+ switch ( f->format ) {
+ case O_TIFF_RAW: compr = 1 ; break ;
+ case O_TIFF_FAX: compr = 3 ; break ;
+ default: err = msg ( "E2can't happen(tiffinit)" ) ; break ;
+ }
+
+ /* write directory tags, 12 bytes each */
+
+ wtag( f, 1, 256, 4, 1, f->w ) ; /* width long */
+ wtag( f, 1, 257, 4, 1, f->h ) ; /* length long */
+ wtag( f, 0, 258, 3, 1, 1 ) ; /* bits/sample short */
+
+ wtag( f, 0, 259, 3, 1, compr ) ; /* compresssion(g3=3) short */
+ wtag( f, 0, 262, 3, 1, 0 ) ; /* photometric(0-min=white) short */
+ wtag( f, 0, 266, 3, 1, 1 ) ; /* fill order(msb2lsb=1) short */
+ wtag( f, 1, 273, 4, 1, doff ) ; /* strip offsets long */
+
+ wtag( f, 0, 274, 3, 1, 1 ) ; /* orientation(1=normal) short */
+ wtag( f, 0, 277, 3, 1, 1 ) ; /* samples/pixel short */
+ wtag( f, 1, 278, 4, 1, f->h ) ; /* rows/strip long */
+ wtag( f, 1, 279, 4, 1, f->bytes ) ; /* strip byte counts long */
+
+ wtag( f, 1, 282, 5, 1, tdoff+0 ) ; /* xresolution ratio */
+ wtag( f, 1, 283, 5, 1, tdoff+8 ) ; /* yresolution ratio */
+ wtag( f, 0, 284, 3, 1, 1 ) ; /* storage(1=single plane) short */
+ wtag( f, 1, 292, 4, 1, 0 ) ; /* g3options long */
+
+ wtag( f, 0, 296, 3, 1, 2 ) ; /* resolution units(2=in,3=cm) short */
+ wtag( f, 0, 327, 3, 1, 0 ) ; /* clean fax(0=clean) short */
+
+ fwrite4 ( 0, f ) ; /* offset to next dir (no more) */
+
+ /* ==> tdoff (tag data offset), write ratios for floats here */
+
+ fwrite4 ( f->xres+0.5, f ) ;
+ fwrite4 ( 1, f ) ;
+ fwrite4 ( f->yres+0.5, f ) ;
+ fwrite4 ( 1, f ) ;
+
+ /* ==> doff (strip data offset), image data goes here */
+
+ return err ;
+}
+
+
+/* Convert array 'runs' of 'nr' run lengths into a bit map 'buf'. Returns
+ the number of bytes filled. */
+
+int runtobit ( short *runs, int nr, uchar *buf )
+{
+ static uchar zerofill [ 9 ] = {
+ 0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 } ;
+ static uchar onefill [ 9 ] = {
+ 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff } ;
+
+ uchar col=0, *buf0 = buf ;
+ register short len, b=8, bytes ;
+
+ while ( nr-- > 0 ) {
+ len = *runs++ ;
+ if ( col ) *buf |= onefill [ b ] ; /* right bits of cur. byte */
+ else *buf &= zerofill [ b ] ;
+ if ( b > len ) { /* done fill */
+ b -= len ;
+ } else { /* continue to next byte */
+ len -= b ;
+ buf++ ;
+ b = 8 ;
+ if ( ( bytes = len>>3 ) > 0 ) { /* fill >1 byte */
+ memset ( buf, col, bytes ) ;
+ len -= bytes*8;
+ buf += bytes ;
+ }
+ *buf = col ; /* flood the rest */
+ b -= len ;
+ }
+ col ^= 0xff ;
+ }
+
+ return buf - buf0 + ( b < 8 ) ;
+}
+
+
+/* Write a PCX file header. */
+
+int fputi ( int i, OFILE *f )
+{
+ putc ( i & 0xff, f->f ) ;
+ putc ( ( i >> 8 ) & 0xff, f->f ) ;
+ return 0 ;
+}
+
+void pcxinit ( OFILE *f )
+{
+ uchar buf [ 60 ] = { 0x0a, 3, 1, 1 } ; /* magic, version, compr, BPP */
+
+ fwrite ( buf, 1, 4, f->f ) ; /* 4 */
+ fputi ( 0, f ) ; /* 8 xmin, ymin, xmax, ymax */
+ fputi ( 0, f ) ;
+ fputi ( f->w-1, f ) ;
+ fputi ( f->h-1, f ) ;
+ fputi ( f->xres, f ) ; /* 4 x and y dpi */
+ fputi ( f->yres, f ) ;
+ memset ( buf, 0, 48 ) ; /* 48 palette */
+ fwrite ( buf, 1, 48, f->f ) ;
+ putc ( 0, f->f ) ; /* 1 reserved */
+ putc ( 1, f->f ) ; /* 1 planes per pixel */
+ fputi ( (f->w+15)/16*2, f ) ; /* 2 bytes per line */
+ memset ( buf, 0, 60 ) ; /* 60 zero */
+ fwrite ( buf, 1, 60, f->f ) ;
+}
+
+/* Write a PCX-compressed scan line. */
+
+void pcxwrite ( OFILE *of, uchar *p, int nb )
+{
+ int c, n, runc ;
+ FILE *f = of->f ;
+
+ runc = *p++ ;
+ n = 1 ;
+
+ for ( nb-- ; nb > 0 ; nb-- ) {
+ c = *p++ ;
+ if ( c == runc && n < 63 ) { /* continue run */
+ n++ ;
+ } else { /* terminate run */
+ if ( n > 1 || ( ( runc & 0xc0 ) == 0xc0 ) ) /* output as run */
+ putc ( n | 0xc0, f ) ;
+ putc ( runc, f ) ;
+ runc = c ; /* start new run */
+ n = 1 ;
+ }
+ }
+
+ /* last run */
+
+ if ( n > 1 || ( ( runc & 0xc0 ) == 0xc0 ) ) /* output as run */
+ putc ( n | 0xc0, f ) ;
+ putc ( runc, f ) ;
+
+}
+
+
+/* Begin/end output pages. If not starting first page (0), terminate
+ previous page. If output filename pattern is defined, [re-]opens that
+ file. If not terminating last page (page==EOF), writes file header.
+ Returns 0 or 2 on errors. */
+
+int nextopage ( OFILE *f, int page )
+{
+ int err = 0 ;
+ int i, nb=0 ;
+ uchar *p, codes [ ( RTCEOL * EOLBITS ) / 8 + 3 ] ;
+
+ if ( f->f ) { /* terminate previous page */
+
+ switch ( f->format ) {
+ case O_PBM:
+ break ;
+ case O_PGM:
+ break ;
+ case O_FAX:
+ case O_TIFF_FAX:
+ for ( p = codes, i=0 ; i<RTCEOL ; i++ )
+ p = putcode ( &f->e, EOLCODE, EOLBITS, p ) ;
+ nb = putcode ( &f->e, 0, 0, p ) - codes ;
+ fwrite ( codes, 1, nb, f->f ) ;
+ f->bytes += nb ;
+ if ( f->format == O_TIFF_FAX ) tiffinit ( f ) ;
+ break ;
+ case O_TIFF_RAW:
+ tiffinit(f) ; /* rewind & update TIFF header */
+ break ;
+ case O_PCL:
+ fprintf ( f->f, PCLEND ) ;
+ break ;
+ case O_PS:
+ fprintf ( f->f, PSPAGEEND ) ;
+ if ( f->fname || page<0 ) fprintf ( f->f, PSEND, f->lastpageno ) ;
+ break ;
+ case O_PCX:
+ case O_PCX_RAW:
+ fseek ( f->f, 0, SEEK_SET ) ;
+ pcxinit ( f ) ;
+ break ;
+ }
+
+ if ( ferror ( f->f ) ) {
+ err = msg ("ES2output error:" ) ;
+ } else {
+ msg ( "F+ wrote %s as %dx%d pixel %.fx%.f dpi %s page",
+ f->cfname, f->w, f->h, f->xres, f->yres,
+ oformatname [f->format] ) ;
+
+ switch ( f->format ) {
+ case O_PS:
+ msg ( "F (%d lines)", f->pslines ) ;
+ break ;
+ case O_TIFF_RAW:
+ case O_TIFF_FAX:
+ msg ( "F (%d bytes)", f->bytes ) ;
+ break ;
+ default:
+ msg ( "F " ) ;
+ break ;
+ }
+
+ }
+
+ }
+
+ if ( ! err && page >= 0 ) { /* open new file */
+ if ( f->fname ) {
+ sprintf ( f->cfname, f->fname, page+1, page+1, page+1 ) ;
+
+ if ( ! f->f )
+ f->f = fopen ( f->cfname, ( f->format == O_PS ) ? "w" : "wb+" ) ;
+ else
+ f->f = freopen ( f->cfname, ( f->format == O_PS ) ? "w" : "wb+", f->f ) ;
+
+ if ( ! f->f ) {
+ err = msg ("ES2can't open output file %s:", f->cfname ) ;
+ }
+ } else {
+ f->f = stdout ;
+ strcpy ( f->cfname, "standard output" ) ;
+ }
+ }
+
+ /* start new page */
+
+ if ( ! err && page >= 0 ) {
+ switch ( f->format ) {
+ case O_PBM:
+ fprintf ( f->f, "P4 %d %d\n", f->w, f->h ) ;
+ break ;
+ case O_PGM:
+ fprintf ( f->f, "P5 %d %d %d\n", f->w/4, f->h/4, 255 ) ;
+ break ;
+ case O_FAX:
+ case O_TIFF_FAX:
+ if ( f->format == O_TIFF_FAX ) tiffinit ( f ) ;
+ p = putcode ( &f->e, EOLCODE, EOLBITS, codes ) ;
+ nb = p - codes ;
+ fwrite ( codes, 1, nb, f->f ) ;
+ break ;
+ case O_TIFF_RAW:
+ tiffinit ( f ) ;
+ break ;
+ case O_PCL:
+ fprintf ( f->f, PCLBEGIN, (int) f->xres ) ;
+ break ;
+ case O_PS:
+ psinit ( f, ( f->fname || page==0 ), page+1, f->w, f->h, f->w/8 ) ;
+ break ;
+ case O_PCX:
+ case O_PCX_RAW:
+ fseek ( f->f, 0, SEEK_SET ) ;
+ pcxinit ( f ) ;
+ break ;
+ }
+
+ if ( ferror ( f->f ) ) err = msg ("ES2output error:" ) ;
+ }
+
+ /* only count lines/bytes for those formats that don't have
+ headers or where we will update the headers on closing */
+
+ switch ( f->format ) {
+ case O_FAX:
+ case O_TIFF_FAX:
+ case O_PCX:
+ case O_PCX_RAW:
+ f->h = 0 ;
+ f->bytes = nb ;
+ break ;
+ }
+
+ return err ;
+}
+
+
+/* Output scan line of nr runs no times to output file f. */
+
+void writeline ( OFILE *f, short *runs, int nr, int no )
+{
+ int nb = 0 ;
+ uchar *p, buf [ MAXCODES ] ;
+
+ /* if line to be output, convert to right format */
+
+ if ( no > 0 )
+ switch ( f->format ) {
+ case O_PBM:
+ case O_PGM:
+ case O_PCL:
+ case O_PS:
+ case O_TIFF_RAW:
+ case O_PCX:
+ case O_PCX_RAW:
+ nb = runtobit ( runs, nr, buf ) ;
+ break ;
+ case O_FAX:
+ case O_TIFF_FAX:
+ break ;
+ }
+
+ /* output `no' times. */
+
+ while ( no-- > 0 ) {
+ switch ( f->format ) {
+ case O_PCX_RAW:
+ case O_TIFF_RAW:
+ case O_PBM:
+ fwrite ( buf, 1, nb, f->f ) ;
+ break ;
+ case O_PGM:
+ pgmwrite ( f, buf, nb ) ;
+ break ;
+ case O_TIFF_FAX:
+ case O_FAX:
+ p = runtocode ( &f->e, runs, nr, buf ) ;
+ p = putcode ( &f->e, EOLCODE, EOLBITS, p ) ;
+ nb = p - buf ;
+ fwrite ( buf, 1, nb, f->f ) ;
+ break ;
+ case O_PCL:
+ pclwrite ( f, buf, nb ) ;
+ break ;
+ case O_PS:
+ pswrite ( f, buf, nb ) ;
+ break ;
+ case O_PCX:
+ pcxwrite ( f, buf, nb ) ;
+ break ;
+ }
+
+ /* only count lines/bytes for those formats that don't have
+ headers or where we will update the headers on closing */
+
+ switch ( f->format ) {
+ case O_FAX:
+ case O_TIFF_FAX:
+ case O_TIFF_RAW:
+ case O_PCX:
+ case O_PCX_RAW:
+ f->h++ ;
+ f->bytes += nb ;
+ break ;
+ }
+
+ }
+}
+
+
+/* Initialize new output file. If fname is NULL, stdout will be used for
+ all images. */
+
+void newOFILE ( OFILE *f, int format, char *fname,
+ float xres, float yres, int w, int h )
+{
+ f->f = 0 ;
+ f->format = format ;
+ f->fname = fname ;
+ f->xres = xres ;
+ f->yres = yres ;
+ f->w = w ;
+ f->h = h ;
+ f->bytes = 0 ;
+ newENCODER ( &f->e ) ;
+}
+
+/* Read a bitmap to use as a font and fill in the font data. If
+ the file name is null, empty, or there are errors, the font is
+ initialized to the built-in font. Returns 0 if OK, 2 on
+ errors. */
+
+int readfont ( char *fname, faxfont *font )
+{
+ int err=0, i, j, n=0, nr, nb, fontok=0, pels ;
+ char *fnames [2] = { 0, 0 } ;
+ short runs [ MAXRUNS ] ;
+ IFILE f;
+
+ if ( fname && *fname ) {
+
+ fnames[0] = fname ;
+
+ newIFILE ( &f, fnames ) ;
+
+ if ( nextipage ( &f, 0 ) ) {
+ err = msg ( "E2 can't open font file %s", fnames[0] ) ;
+ }
+
+ nb = 0 ;
+ while ( ! err && ( nr = readline ( &f, runs, &pels ) ) >= 0 ) {
+ if ( nb+pels/8 < MAXFONTBUF ) {
+ nb += runtobit ( runs, nr, font->buf+nb ) ;
+ } else {
+ err = msg ("E2font file %s too large (max %d bytes)",
+ fnames[0], MAXFONTBUF ) ;
+ }
+ }
+
+ if ( ! err && nb != f.page->w * f.page->h / 8 )
+ err = msg ( "E2 read %d bytes of font data for %dx%d bitmap",
+ nb, f.page->w, f.page->h ) ;
+
+ if ( ! err && ( f.page->w / 256 > MAXFONTW || f.page->h > MAXFONTH ) ) {
+ err = msg ( "E2font size (%dx%d) too large", f.page->w, f.page->h ) ;
+ }
+
+ if ( err ) {
+ font->w = font->h = 0 ;
+ } else {
+ font->w = f.page->w / 256 ;
+ font->h = f.page->h ;
+ for ( i=0 ; i<256 ; i++ ) font->offset[i] = i*font->w ;
+ msg ("Iread %dx%d font %s (%d bytes)", font->w, font->h, fname, nb ) ;
+ fontok = 1 ;
+ }
+
+ if ( f.f ) {
+ fclose ( f.f ) ;
+ f.f = 0 ;
+ }
+ }
+
+ if ( ! fontok ) { /* use built-in font */
+
+ font->w = STDFONTW ;
+ font->h = STDFONTH ;
+
+ for ( i=j=0 ; j<STDFONTBUF ; i++ ) /* expand bit map */
+ if ( stdfont [ i ] == 0 )
+ for ( n = stdfont [ ++i ] ; n > 0 ; n-- )
+ font->buf [ j++ ] = 0 ;
+ else
+ font->buf [ j++ ] = stdfont [ i ] ;
+
+ if ( i != 1980 ) err = msg ( "E2can't happen(readfont)" ) ;
+
+ for ( i=0 ; i<256 ; i++ ) font->offset[i] = i*font->w ;
+ }
+
+ return err ;
+}
+
+
+/* Initialize bit reversal lookup tables (note that the
+ `normalbits' array is the one actually used for the bit
+ reversal. */
+
+void initbittab ( void )
+{
+ int i ;
+ for ( i=0 ; i<256 ; i++ )
+ normalbits [ reversebits [ i ] = i ] =
+ ( i& 1 ? 128:0 ) | ( i& 2 ? 64:0 ) | ( i& 4 ? 32:0 ) | ( i& 8 ? 16:0 ) |
+ ( i&16 ? 8:0 ) | ( i&32 ? 4:0 ) | ( i&64 ? 2:0 ) | ( i&128 ? 1:0 ) ;
+}
+
+
+ /* T.4 Encoding/Decoding */
+
+/* Table-lookup decoder for variable-bit-length codewords. The table index
+ is the N most recently undecoded bits with the first (oldest) undecoded
+ bit as the MS bit. If the N bits uniquely identify a codeword then the
+ indexed 'code' member identifies the code, otherwise it is zero. The
+ 'bits' member gives the number of bits to be considered decoded (to be
+ removed from the bit stream) and the 'next' element is a pointer to the
+ table to use for decoding the next part of the bit sequence.
+
+ For T.4 decoding the longest T.4 codeword is 13 bits. The implementation
+ below uses two tables of 512 elements (N=9 bits) for each colour.
+ Codewords longer than 9 bits require a second lookup. Since all
+ codewords longer than than 9 bits have a 4-bit zero prefix it is
+ possible to use only one secondary 9-bit lookup table by dropping only
+ the first 4 bits after the first lookup. The code indentifier is the run
+ length + 1. A separate table is used for decoding the variable-length
+ FILL patterns.
+
+ For undefined codewords, one bit is skipped and decoding continues at
+ the white code table. */
+
+/* the lookup tables for each colour and the fill lookup table */
+
+dtab tw1 [ 512 ], tw2 [ 512 ], tb1 [ 512 ], tb2 [ 512 ], fill [ 512 ] ;
+char tabinit=0 ;
+
+/* Add code cword shifted left by shift to decoding table tab. */
+
+void addcode ( dtab *tab, int cword, int shift,
+ short code, short bits, dtab *next )
+{
+ int i, n = 1 << shift ;
+
+ for ( i = cword << shift ; n-- > 0 ; i++ ) {
+ tab[i].code = code ;
+ tab[i].bits = bits ;
+ tab[i].next = next ;
+ }
+}
+
+/* Initialize the decoding table for one colour using the codes in the T.4
+ table p0. t1 and t2 are the two decoding tables and ot is the first
+ table of the other colour. */
+
+void init1dtab ( t4tab *p0, dtab *t1, dtab *t2, dtab *ot )
+{
+ t4tab *p ;
+ for ( p = p0 ; p->code ; p++ )
+ if ( p->bits <= 9 ) {
+ addcode ( t1, p->code, 9 - p->bits, p->rlen + 1, p->bits,
+ ( p - p0 ) > 63 ? t1 : ot ) ;
+ } else {
+ addcode ( t1, p->code >> ( p->bits - 9 ), 0, 0, 4, t2 ) ;
+ addcode ( t2, p->code, 13 - p->bits, p->rlen + 1, p->bits - 4,
+ ( p - p0 ) > 63 ? t1 : ot ) ;
+ }
+}
+
+
+/* Initialize a T.4 decoder. */
+
+void newDECODER ( DECODER *d )
+{
+ int i ;
+
+ if ( ! tabinit ) {
+
+ /* undefined codes */
+
+ addcode ( tw1, 0, 9, 0, 1, tw1 ) ;
+ addcode ( tw2, 0, 9, 0, 1, tw1 ) ;
+ addcode ( tb1, 0, 9, 0, 1, tw1 ) ;
+ addcode ( tb2, 0, 9, 0, 1, tw1 ) ;
+ addcode ( fill, 0, 9, 0, 1, tw1 ) ;
+
+ /* fill and EOL */
+
+ addcode ( tw1, 0, 0, 0, 4, tw2 ) ;
+ addcode ( tw2, 0, 2, 0, 7, fill ) ;
+ addcode ( tb1, 0, 0, 0, 4, tb2 ) ;
+ addcode ( tb2, 0, 2, 0, 7, fill ) ;
+
+ addcode ( fill, 0, 0, 0, 9, fill ) ;
+ for ( i=0 ; i<=8 ; i++ )
+ addcode ( fill, 1, i, -1, 9-i, tw1 ) ;
+
+ /* white and black runs */
+
+ init1dtab ( wtab, tw1, tw2, tb1 ) ;
+ init1dtab ( btab, tb1, tb2, tw1 ) ;
+
+ tabinit=1 ;
+ }
+
+ /* initialize decoder to starting state */
+
+ d->x = 0 ;
+ d->shift = -9 ;
+ d->tab = tw1 ;
+ d->eolcnt = 0 ;
+}
+
+ /* T.4 coding table and default font for efax/efix */
+
+/* T.4 1-D run-length coding tables. codes must be in run length
+ order for runtocode(). */
+
+t4tab wtab [ ( 64 + 27 + 13 ) + 1 ] = { /* runs of white */
+
+/* Terminating White Codes */
+
+{53,8,0}, {7,6,1}, {7,4,2}, {8,4,3}, {11,4,4}, {12,4,5},
+{14,4,6}, {15,4,7}, {19,5,8}, {20,5,9}, {7,5,10}, {8,5,11},
+{8,6,12}, {3,6,13}, {52,6,14}, {53,6,15}, {42,6,16}, {43,6,17},
+{39,7,18}, {12,7,19}, {8,7,20}, {23,7,21}, {3,7,22}, {4,7,23},
+{40,7,24}, {43,7,25}, {19,7,26}, {36,7,27}, {24,7,28}, {2,8,29},
+{3,8,30}, {26,8,31}, {27,8,32}, {18,8,33}, {19,8,34}, {20,8,35},
+{21,8,36}, {22,8,37}, {23,8,38}, {40,8,39}, {41,8,40}, {42,8,41},
+{43,8,42}, {44,8,43}, {45,8,44}, {4,8,45}, {5,8,46}, {10,8,47},
+{11,8,48}, {82,8,49}, {83,8,50}, {84,8,51}, {85,8,52}, {36,8,53},
+{37,8,54}, {88,8,55}, {89,8,56}, {90,8,57}, {91,8,58}, {74,8,59},
+{75,8,60}, {50,8,61}, {51,8,62}, {52,8,63},
+
+/* Make Up White Codes */
+
+{27,5,64}, {18,5,128}, {23,6,192}, {55,7,256}, {54,8,320}, {55,8,384},
+{100,8,448}, {101,8,512}, {104,8,576}, {103,8,640}, {204,9,704}, {205,9,768},
+{210,9,832}, {211,9,896}, {212,9,960}, {213,9,1024},{214,9,1088},{215,9,1152},
+{216,9,1216},{217,9,1280},{218,9,1344},{219,9,1408},{152,9,1472},{153,9,1536},
+{154,9,1600},{24,6,1664}, {155,9,1728},
+
+/* Extended Make Up Codes (Black and White) */
+
+{8,11,1792}, {12,11,1856},{13,11,1920},{18,12,1984},{19,12,2048},{20,12,2112},
+{21,12,2176},{22,12,2240},{23,12,2304},{28,12,2368},{29,12,2432},{30,12,2496},
+{31,12,2560},
+
+{0,0,0} } ;
+
+t4tab btab [ ( 64 + 27 + 13 ) + 1 ] = { /* runs of black */
+
+/* Terminating Black Codes */
+
+{55,10,0}, {2,3,1}, {3,2,2}, {2,2,3}, {3,3,4}, {3,4,5},
+{2,4,6}, {3,5,7}, {5,6,8}, {4,6,9}, {4,7,10}, {5,7,11},
+{7,7,12}, {4,8,13}, {7,8,14}, {24,9,15}, {23,10,16}, {24,10,17},
+{8,10,18}, {103,11,19}, {104,11,20}, {108,11,21}, {55,11,22}, {40,11,23},
+{23,11,24}, {24,11,25}, {202,12,26}, {203,12,27}, {204,12,28}, {205,12,29},
+{104,12,30}, {105,12,31}, {106,12,32}, {107,12,33}, {210,12,34}, {211,12,35},
+{212,12,36}, {213,12,37}, {214,12,38}, {215,12,39}, {108,12,40}, {109,12,41},
+{218,12,42}, {219,12,43}, {84,12,44}, {85,12,45}, {86,12,46}, {87,12,47},
+{100,12,48}, {101,12,49}, {82,12,50}, {83,12,51}, {36,12,52}, {55,12,53},
+{56,12,54}, {39,12,55}, {40,12,56}, {88,12,57}, {89,12,58}, {43,12,59},
+{44,12,60}, {90,12,61}, {102,12,62}, {103,12,63},
+
+/* Make Up Black Codes */
+
+{15,10,64}, {200,12,128},{201,12,192},{91,12,256}, {51,12,320}, {52,12,384},
+{53,12,448}, {108,13,512},{109,13,576},{74,13,640}, {75,13,704}, {76,13,768},
+{77,13,832}, {114,13,896},{115,13,960},{116,13,1024},{117,13,1088},
+{118,13,1152},
+{119,13,1216},{82,13,1280},{83,13,1344},{84,13,1408},{85,13,1472},{90,13,1536},
+{91,13,1600},{100,13,1664},{101,13,1728},
+
+/* Extended Make Up Codes (Black and White) */
+
+{8,11,1792}, {12,11,1856},{13,11,1920},{18,12,1984},{19,12,2048},{20,12,2112},
+{21,12,2176},{22,12,2240},{23,12,2304},{28,12,2368},{29,12,2432},{30,12,2496},
+{31,12,2560},
+
+{0,0,0} } ;
+
+
+/* The built-in 8x16 font. Runs of zeroes are coded as 0
+ followed by the repetition count. */
+
+uchar stdfont [ 1980 ] = {
+0,255,0,255,0,194,8,4,12,10,18,0,3,16,4,8,20,8,4,8,20,0,1,10,8,4,
+4,10,18,0,2,16,4,8,20,4,0,68,20,0,1,8,0,2,12,6,48,0,5,2,0,43,14,32,
+56,0,2,12,0,1,32,0,1,2,0,1,14,0,1,32,8,4,32,56,0,14,6,8,48,0,40,8,
+0,1,18,0,6,30,0,4,4,0,11,4,8,18,20,18,12,0,2,8,8,20,20,4,8,20,20,
+0,1,20,4,8,10,20,18,0,2,8,8,20,20,8,0,1,24,8,4,8,10,20,12,0,2,8,4,
+8,20,16,8,8,20,54,10,8,4,8,10,20,0,2,16,4,8,20,4,0,1,20,0,33,12,20,
+18,28,48,12,12,8,8,8,0,4,2,28,8,28,28,4,62,28,62,28,28,0,5,60,28,
+12,60,14,56,62,30,14,34,62,62,33,16,33,34,12,60,12,60,30,127,34,33,
+65,34,34,62,8,32,8,8,0,1,24,0,1,32,0,1,2,0,1,16,0,1,32,8,4,32,8,0,
+7,16,0,6,8,8,8,0,36,4,14,0,1,34,8,12,18,28,24,0,3,28,0,1,24,0,1,28,
+28,8,0,1,30,0,2,8,28,0,1,100,100,98,0,6,18,31,14,0,8,56,0,7,13,0,
+5,32,36,4,8,20,20,20,18,0,2,4,8,20,20,8,16,20,20,8,20,4,8,20,20,20,
+0,2,8,8,20,20,8,32,20,0,33,12,20,18,42,73,18,24,8,8,42,8,0,3,4,34,
+24,34,34,12,32,34,2,34,34,0,2,2,0,1,16,2,34,12,34,18,36,32,16,18,
+34,8,8,34,16,51,50,18,34,18,34,32,8,34,33,73,34,34,2,8,16,8,8,0,1,
+24,0,1,32,0,1,2,0,1,16,0,1,32,0,2,32,8,0,7,16,0,6,8,8,8,0,36,15,16,
+65,34,8,18,0,1,34,4,0,3,34,0,1,36,8,2,2,0,2,58,0,2,56,34,0,1,36,36,
+18,0,1,12,12,12,12,12,12,24,18,62,62,62,62,62,62,62,62,36,34,12,12,
+12,12,12,0,1,18,34,34,34,34,34,32,36,0,5,12,0,10,52,0,6,8,0,6,32,
+0,34,12,0,1,63,40,74,18,0,1,16,4,20,8,0,3,4,34,40,2,2,20,32,32,2,
+34,34,24,24,4,0,1,8,2,78,18,34,32,34,32,16,32,34,8,8,36,16,51,50,
+33,34,33,34,32,8,34,33,73,20,34,4,8,16,8,20,0,2,28,44,14,30,28,62,
+30,44,56,60,34,8,82,44,28,44,30,22,30,62,34,34,65,34,34,62,8,8,8,
+0,35,12,20,16,62,34,8,16,0,1,77,4,0,3,93,0,1,24,8,2,12,0,1,34,58,
+0,2,8,34,0,1,40,40,100,4,12,12,12,12,12,12,40,32,32,32,32,32,8,8,
+8,8,34,50,18,18,18,18,18,34,35,34,34,34,34,34,60,40,28,28,28,28,28,
+28,54,14,28,28,28,28,56,56,56,56,2,44,28,28,28,28,28,8,29,34,34,34,
+34,34,44,34,0,33,12,0,1,18,24,52,12,0,1,16,4,42,8,0,3,8,34,8,2,2,
+36,60,32,4,34,34,24,24,8,127,4,2,82,18,34,32,34,32,16,32,34,8,8,40,
+16,45,42,33,34,33,34,48,8,34,33,73,20,20,4,8,8,8,20,0,2,34,50,16,
+34,34,16,34,50,8,4,36,8,109,50,34,50,34,24,32,16,34,34,73,34,34,2,
+4,8,16,57,0,34,12,36,16,34,20,0,1,40,0,1,81,28,18,127,0,1,89,0,2,
+127,12,2,0,1,34,58,28,0,1,8,34,36,40,40,24,4,18,18,18,18,18,18,40,
+32,32,32,32,32,8,8,8,8,34,50,33,33,33,33,33,20,37,34,34,34,34,20,
+34,40,34,34,34,34,34,34,9,16,34,34,34,34,8,8,8,8,30,50,34,34,34,34,
+34,0,1,34,34,34,34,34,34,50,34,0,33,12,0,1,18,12,8,25,0,1,16,4,8,
+127,0,1,127,0,1,8,34,8,4,12,68,2,60,8,28,30,0,2,16,0,1,2,28,82,18,
+60,32,34,60,30,32,62,8,8,56,16,45,42,33,34,33,60,28,8,34,18,85,8,
+20,8,8,8,8,34,0,2,2,34,32,34,34,16,34,34,8,4,40,8,73,34,34,34,34,
+16,32,16,34,34,73,20,34,4,24,8,12,78,0,35,36,60,34,62,0,1,36,0,1,
+81,36,36,1,28,85,0,2,8,16,2,0,1,34,26,28,0,1,8,34,18,18,22,106,0,
+1,18,18,18,18,18,18,47,32,60,60,60,60,8,8,8,8,122,42,33,33,33,33,
+33,8,45,34,34,34,34,20,34,36,2,2,2,2,2,2,9,32,34,34,34,34,8,8,8,8,
+34,34,34,34,34,34,34,127,38,34,34,34,34,34,34,34,0,33,8,0,1,63,10,
+22,37,0,1,16,4,0,1,8,0,3,8,34,8,8,2,126,2,34,8,34,2,0,2,8,127,4,16,
+86,63,34,32,34,32,16,34,34,8,8,36,16,45,38,33,60,33,36,6,8,34,18,
+54,20,8,16,8,8,8,34,0,2,30,34,32,34,62,16,34,34,8,4,56,8,73,34,34,
+34,34,16,28,16,34,20,85,8,20,8,4,8,16,0,35,8,36,16,34,8,8,18,0,1,
+81,26,72,1,0,1,34,0,2,8,30,28,0,1,34,10,28,0,1,8,28,9,22,17,22,4,
+63,63,63,63,63,63,120,32,32,32,32,32,8,8,8,8,34,42,33,33,33,33,33,
+20,41,34,34,34,34,8,34,34,30,30,30,30,30,30,63,32,62,62,62,62,8,8,
+8,8,34,34,34,34,34,34,34,0,1,42,34,34,34,34,20,34,20,0,35,18,10,41,
+34,0,1,16,4,0,1,8,0,3,16,34,8,16,2,4,2,34,16,34,2,0,2,4,0,1,8,0,1,
+73,33,34,32,34,32,16,34,34,8,8,34,16,33,38,33,32,33,34,2,8,34,18,
+34,20,8,16,8,4,8,0,3,34,34,32,34,32,16,38,34,8,4,36,8,73,34,34,34,
+34,16,2,16,34,20,34,20,20,16,8,8,8,0,35,12,20,16,62,62,8,10,0,1,77,
+0,1,36,1,0,1,28,0,6,34,10,0,4,18,42,34,42,28,33,33,33,33,33,33,72,
+32,32,32,32,32,8,8,8,8,34,38,33,33,33,33,33,34,49,34,34,34,34,8,60,
+34,34,34,34,34,34,34,72,32,32,32,32,32,8,8,8,8,34,34,34,34,34,34,
+34,8,50,34,34,34,34,20,34,20,0,33,12,0,1,18,42,73,34,0,1,8,8,0,1,
+8,12,0,1,24,16,34,8,32,34,4,34,34,16,34,34,24,24,2,0,1,16,16,32,33,
+34,16,36,32,16,18,34,8,8,33,16,33,34,18,32,18,34,2,8,34,12,34,34,
+8,32,8,4,8,0,3,34,34,16,38,34,16,26,34,8,4,34,8,73,34,34,34,38,16,
+2,16,38,8,34,34,8,32,8,8,8,0,35,12,15,16,65,8,8,4,0,1,34,0,1,18,0,
+5,127,0,3,54,10,0,4,36,79,68,79,32,33,33,33,33,33,33,72,16,32,32,
+32,32,8,8,8,8,36,38,18,18,18,18,18,0,1,18,34,34,34,34,8,32,34,34,
+34,34,34,34,34,72,16,34,34,34,34,8,8,8,8,34,34,34,34,34,34,34,8,34,
+38,38,38,38,8,34,8,0,33,12,0,1,18,28,6,29,0,1,8,8,0,2,12,0,1,24,32,
+28,8,62,28,4,28,28,16,28,28,24,24,0,3,16,28,33,60,14,56,62,16,14,
+34,62,112,33,30,33,34,12,32,12,34,60,8,28,12,34,34,8,62,8,2,8,0,3,
+29,60,14,26,28,16,2,34,8,4,33,8,73,34,28,60,26,16,60,14,26,8,34,34,
+8,62,8,8,8,0,35,12,4,62,0,1,8,8,36,0,1,28,0,11,42,10,0,5,66,71,66,
+32,33,33,33,33,33,33,79,14,62,62,62,62,62,62,62,62,56,34,12,12,12,
+12,12,0,1,44,28,28,28,28,8,32,36,29,29,29,29,29,29,55,14,28,28,28,
+28,8,8,8,8,28,34,28,28,28,28,28,0,1,92,26,26,26,26,8,60,8,0,36,8,
+0,3,6,48,0,2,24,0,2,32,0,11,48,0,21,6,0,9,14,2,56,0,1,127,0,7,2,0,
+2,4,0,5,32,2,0,7,16,0,1,6,8,48,0,35,12,0,4,8,24,0,13,32,10,0,1,4,
+0,6,32,0,7,4,0,31,4,0,21,16,32,16,0,81,3,0,21,28,0,2,56,0,5,32,2,
+0,7,48,0,39,12,0,19,32,0,2,24,0,6,30,0,7,24,0,31,24,0,21,48,32,48,
+0,255,0,1
+} ;
--- /dev/null
+#ifndef _EFAXLIB_H
+#define _EFAXLIB_H
+
+#include <stdio.h>
+
+#define EFAX_PATH_MAX 1024
+
+ /* T.4 fax encoding/decoding */
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+#define DEFPGLINES 66 /* default lines per page */
+
+ /* Buffer sizes. */
+
+/* The maximum scan line width, MAXRUNS, is conservatively
+ set at 8k pels. This is enough for the longest standard T.4 coding line
+ width (2432 pels), the longest encodeable run (2623 pels) and with
+ 32-pel-wide characters allows up to 256 characters per line. Converted
+ to T.4 codes, each pair of runs takes up to 25 bits to code. MAXCODES
+ must also be at least the maximum minimum line length (1200 cps*40 ms ~=
+ 48 bytes). */
+
+#define MAXRUNS 8192
+#define MAXBITS (MAXRUNS/8+1)
+#define MAXCODES (MAXRUNS*25/8/2+1)
+
+/* Line/font size limits */
+
+#define MAXLINELEN 256 /* maximum length of string */
+#define MAXFONTW 32 /* maximum char width */
+#define MAXFONTH 48 /* maximum char height */
+#define MAXFONTBUF (MAXFONTW*MAXFONTH/8*256) /* PBM font buffer size */
+
+/* Longest run encodeable by the T.4 encoding tables used. */
+
+#define MAXRUNLEN (2560+63)
+
+ /* Codes for EOL and number of EOLs required for RTC */
+
+#define EOLCODE 1
+#define EOLBITS 12
+#define RTCEOL 5
+ /* Fonts */
+
+#define STDFONTW 8 /* the built-in font width, height & size */
+#define STDFONTH 16
+#define STDFONTBUF 4096
+
+typedef struct fontstruct {
+ int h, w ;
+ uchar buf [ MAXFONTBUF ] ;
+ short offset [ 256 ] ;
+} faxfont ;
+
+extern uchar stdfont [ ] ; /* compressed bit map for built-in font */
+
+int readfont ( char *fname, faxfont *font ) ;
+
+ /* T.4 Encoding/Decoding */
+
+typedef struct t4tabstruct {
+ short code, bits, rlen ; /* code, bits, run length */
+} t4tab ;
+
+extern t4tab wtab [ ( 64 + 27 + 13 ) + 1 ] ; /* white runs */
+extern t4tab btab [ ( 64 + 27 + 13 ) + 1 ] ; /* black runs */
+
+typedef struct dtabstruct { /* decoder table entry */
+ struct dtabstruct *next ;
+ short bits, code ;
+} dtab ;
+
+ /* Image Input */
+
+#define bigendian ( * (uchar*) &short256 )
+extern short short256 ;
+
+/* input, output and page file formats */
+
+#define NIFORMATS 9
+#define NOFORMATS 14
+#define NPFORMATS 5
+
+enum iformats { I_AUTO=0, I_PBM=1, I_FAX=2, I_TEXT=3, I_TIFF=4,
+ I_DFAX=5, I_PCX=6, I_RAW=7, I_DCX=8 } ;
+
+#define IFORMATS { "AUTO", "PBM", "FAX", "TEXT", "TIFF", \
+ "DFAX", "PCX", "RAW", "DCX" } ;
+
+enum oformats { O_AUTO=0, O_PBM=1, O_FAX=2, O_PCL=3, O_PS=4,
+ O_PGM=5, O_TEXT=6, O_TIFF_FAX=7, O_TIFF_RAW=8, O_DFAX=9,
+ O_TIFF=10, O_PCX=11, O_PCX_RAW=12, O_DCX=13 } ;
+
+#define OFORMATS { "AUTO", "PBM", "FAX", "PCL", "PS", \
+ "PGM", "TEXT", "TIFF", "TIFF", "DFAX", \
+ "TIFF", "PCX", "PCX", "DCX" }
+
+enum pformats { P_RAW=0, P_FAX=1, P_PBM=2, P_TEXT=3, P_PCX=4 } ;
+
+#define PFORMATS { "RAW", "FAX", "PBM", "TEXT", "PCX" }
+
+
+extern char *iformatname [ NIFORMATS ] ;
+extern char *oformatname [ NOFORMATS ] ;
+extern char *pformatname [ NPFORMATS ] ;
+
+typedef struct decoderstruct {
+ long x ; /* undecoded bits */
+ short shift ; /* number of unused bits - 9 */
+ dtab *tab ; /* current decoding table */
+ int eolcnt ; /* EOL count for detecting RTC */
+} DECODER ;
+
+void newDECODER ( DECODER *d ) ;
+
+#define IFILEBUFSIZE 512
+
+#define MAXPAGE 360 /* number of A4 pages in a 100m roll */
+
+typedef struct PAGEstruct { /* page data */
+ char *fname ; /* file name */
+ long offset ; /* location of data within file */
+ int w, h ; /* pel and line counts */
+ float xres, yres ; /* x and y resolution, dpi */
+ uchar format ; /* image coding */
+ uchar revbits ; /* fill order is LS to MS bit */
+} PAGE ;
+
+typedef struct ifilestruct { /* input image file */
+
+ /* data for each pages */
+
+ PAGE *page, *lastpage ; /* pointers to current and last page */
+ PAGE pages [ MAXPAGE ] ; /* page data */
+
+ long next ; /* offset to next page (while scanning only) */
+
+ /* data for current input page */
+
+ FILE *f ; /* current file pointer */
+ int lines ; /* scan lines remaining in page */
+
+ uchar bigend ; /* TIFF: big-endian byte order */
+
+ DECODER d ; /* FAX: T.4 decoder state */
+
+ faxfont *font ; /* TEXT: font to use */
+ int pglines ; /* TEXT: text lines per page */
+ char text [ MAXLINELEN ] ; /* TEXT: current string */
+ int txtlines ; /* TEXT: scan lines left in text l. */
+ int charw, charh, lmargin ; /* TEXT: desired char w, h & margin */
+
+} IFILE ;
+
+int newIFILE ( IFILE *f, char **fname ) ;
+void logifnames ( IFILE *f, char *s ) ;
+int nextipage ( IFILE *f, int dp ) ;
+int lastpage ( IFILE *f ) ;
+int readline ( IFILE *f, short *runs, int *pels ) ;
+
+ /* Image Output */
+
+typedef struct encoderstruct {
+ long x ; /* unused bits */
+ short shift ; /* number of unused bits - 8 */
+} ENCODER ;
+
+void newENCODER ( ENCODER *e ) ;
+
+typedef struct ofilestruct { /* input image file state */
+ FILE *f ; /* file pointer */
+ int format ; /* file format */
+ char *fname ; /* file name pattern */
+ float xres, yres ; /* x and y resolution, dpi */
+ int w, h ; /* width & height, pixels */
+ int lastpageno ; /* PS: last page number this file */
+ int pslines ; /* PS: scan lines written to file */
+ int bytes ; /* TIFF: data bytes written */
+ ENCODER e ; /* T.4 encoder state */
+ char cfname [ EFAX_PATH_MAX + 1 ] ; /* current file name */
+} OFILE ;
+
+void newOFILE ( OFILE *f, int format, char *fname,
+ float xres, float yres, int w, int h ) ;
+int nextopage ( OFILE *f, int page ) ;
+void writeline ( OFILE *f, short *runs, int nr, int no ) ;
+
+ /* Scan Line Processing */
+
+uchar *putcode ( ENCODER *e, short code , short bits , uchar *buf ) ;
+uchar *runtocode ( ENCODER *e, short *runs, int nr, uchar *buf ) ;
+
+/* int bittorun ( uchar *buf, int n, short *runs ) ; */
+int texttorun ( uchar *txt, faxfont *font, short line,
+ int w, int h, int lmargin,
+ short *runs, int *pels ) ;
+
+int xpad ( short *runs, int nr, int pad ) ;
+int xscale ( short *runs, int nr, int xs ) ;
+int xshift ( short *runs, int nr, int s ) ;
+
+int runor ( short *a, int na, short *b, int nb, short *c, int *pels ) ;
+
+/* Bit reversal lookup tables (note that the `normalbits' array
+ is the one actually used for the bit reversal. */
+
+uchar reversebits [ 256 ], normalbits [ 256 ] ;
+
+void initbittab(void) ;
+
+/* Other Stuff */
+
+int ckfmt ( char *p, int n ) ;
+
+#endif
--- /dev/null
+#include <ctype.h> /* ANSI C */
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+
+#include "efaxmsg.h"
+
+#define MAXTSTAMP 80 /* maximum length of a time stamp */
+#define MAXMSGBUF 4096 /* maximum status/error message bytes held */
+
+#define NLOG 2
+
+char *verb[NLOG] = { "ewin", "" } ;
+char *argv0 = "" ;
+
+int nxtoptind = 1 ; /* for communication with nextopt() */
+char *nxtoptarg ;
+
+
+/* For systems without strerror(3) */
+
+#ifdef NO_STRERROR
+extern int sys_nerr;
+extern char *sys_errlist[];
+
+extern char *strerror( int i )
+{
+ return ( i >= 0 && i < sys_nerr ) ? sys_errlist[i] : "Unknown Error" ;
+}
+#endif
+
+
+/* Print time stamp. */
+
+time_t tstamp ( time_t last, FILE *f )
+{
+ time_t now ;
+ char tbuf [ MAXTSTAMP ] ;
+
+ now = time ( 0 ) ;
+
+ strftime ( tbuf, MAXTSTAMP, ( now - last > 600 ) ? "%c" : "%M:%S",
+ localtime( &now ) ) ;
+ fputs ( tbuf, f ) ;
+
+ return now ;
+}
+
+
+/* Return string corresponding to character c. */
+
+char *cname ( uchar c )
+{
+#define CNAMEFMT "<0x%02x>"
+#define CNAMELEN 6+1
+ static char *cnametab [ 256 ] = { /* character names */
+ "<NUL>","<SOH>","<STX>","<ETX>", "<EOT>","<ENQ>","<ACK>","<BEL>",
+ "<BS>", "<HT>", "<LF>", "<VT>", "<FF>", "<CR>", "<SO>", "<SI>",
+ "<DLE>","<XON>","<DC2>","<XOFF>","<DC4>","<NAK>","<SYN>","<ETB>",
+ "<CAN>","<EM>", "<SUB>","<ESC>", "<FS>", "<GS>", "<RS>", "<US>" } ;
+ static char names[ (127-32)*2 + 129*(CNAMELEN) ] ;
+ char *p=names ;
+ static int i=0 ;
+
+ if ( ! i ) {
+ for ( i=32 ; i<256 ; i++ ) {
+ cnametab [ i ] = p ;
+ sprintf ( p, i<127 ? "%c" : CNAMEFMT, i ) ;
+ p += strlen ( p ) + 1 ;
+ }
+ }
+
+ return cnametab [ c ] ;
+}
+
+/* Print a message with a variable number of printf()-type
+ arguments if the first character appears in the global
+ verb[ose] string. Other leading characters and digits do
+ additional actions: + allows the message to be continued on
+ the same line, '-' buffers the message instead of printing it,
+ E, and W expand into strings, S prints the error message for
+ the most recent system error, a digit sets the return value, a
+ space ends prefix but isn't printed. Returns 0 if no prefix
+ digit. */
+
+enum msgflags { E=0x01, W=0x02, S=0x04, NOFLSH=0x08, NOLF=0x10 } ;
+
+int msg ( char *fmt, ... )
+{
+ static int init=0 ;
+ static FILE *logfile [ NLOG ] ;
+ static char msgbuf [ NLOG ] [ MAXMSGBUF ] ;
+ static time_t logtime [ NLOG ] = { 0, 0 } ;
+ static int atcol1 [ NLOG ] = { 1, 1 } ;
+
+ int err=0, i, flags=0 ;
+ char *p ;
+
+ va_list ap ;
+ va_start ( ap, fmt ) ;
+
+ if ( ! init ) {
+ logfile[0] = stderr ;
+ logfile[1] = stdout ;
+ for ( i=0 ; i<NLOG ; i++ )
+ setvbuf ( logfile[i], msgbuf[i], _IOFBF, MAXMSGBUF ) ;
+ cname ( 0 ) ;
+ init = 1 ;
+ }
+
+ for ( i=0 ; i<NLOG ; i++ ) {
+
+ for ( p=fmt ; *p ; p++ ) {
+ switch ( *p ) {
+ case ' ': p++ ; goto print ;
+ case 'E': flags |= E ; break ;
+ case 'W': flags |= W ; break ;
+ case 'S': flags |= S ; break ;
+ case '+': flags |= NOLF ; break ;
+ case '-': flags |= NOFLSH ; break ;
+ default:
+ if ( isdigit ( *p ) ) {
+ err = *p - '0' ;
+ } else if ( ! isupper ( *p ) ) {
+ goto print ;
+ }
+ }
+ }
+
+ print:
+
+ if ( strchr ( verb[i], tolower ( *fmt ) ) ) {
+
+ if ( atcol1[i] ) {
+ fprintf ( logfile[i], "%s: ", argv0 ) ;
+ logtime[i] = tstamp ( logtime[i], logfile[i] ) ;
+ fputs ( ( flags & E ) ? " Error: " :
+ ( flags & W ) ? " Warning: " :
+ " ",
+ logfile[i] ) ;
+ }
+ vfprintf( logfile[i], p, ap ) ;
+ if ( flags & S ) fprintf ( logfile[i], " %s", strerror ( errno ) ) ;
+ if ( ! ( flags & NOLF ) ) fputs ( "\n", logfile[i] ) ;
+ atcol1[i] = flags & NOLF ? 0 : 1 ;
+ if ( ! ( flags & NOFLSH ) ) fflush ( logfile[i] ) ;
+
+ }
+
+ }
+
+ va_end ( ap ) ;
+
+ return err ;
+}
+
+
+/* Simple (one option per argument) version of getopt(3). */
+
+int nextopt( int argc, char **argv, char *args )
+{
+ char *a, *p ;
+
+ if ( nxtoptind >= argc || *(a = argv[nxtoptind]) != '-' ) return -1 ;
+ nxtoptind++ ;
+
+ if ( ! *(a+1) || ( ( p = strchr ( args, *(a+1) ) ) == 0 ) )
+ return msg ( "Eunknown option (%s)", a ), '?' ;
+
+ if ( *(p+1) != ':' ) nxtoptarg = 0 ;
+ else
+ if ( *(a+2) ) nxtoptarg = a+2 ;
+ else
+ if ( nxtoptind >= argc ) return msg ( "Eno argument for (%s)", a ), '?' ;
+ else nxtoptarg = argv [ nxtoptind++ ] ;
+ return *(a+1) ;
+}
+
--- /dev/null
+#ifndef _EFAXMSG_H
+#define _EFAXMSG_H
+
+#include <time.h>
+
+#ifndef uchar
+#define uchar unsigned char
+#endif
+
+ /* Messages & Program Arguments */
+
+enum cchar { /* control characters */
+ NUL, SOH, STX, ETX, EOT, ENQ, ACK, BEL, BS, HT, LF,
+ VT, FF, CR, SO, SI, DLE, XON, DC2, XOFF,DC4, NAK,
+ SYN, ETB, CAN, EM, SUB, ESC, FS, GS, RS, US } ;
+
+extern char *verb[] ; /* types of messages to print */
+extern char *argv0 ; /* program name */
+
+char *cname ( unsigned char c ) ;
+time_t tstamp ( time_t last, FILE *f ) ;
+int msg ( char *fmt, ... ) ;
+
+extern int nxtoptind ;
+extern char *nxtoptarg ;
+
+int nextopt( int argc, char **argv, char *args ) ;
+
+#endif
+
+
--- /dev/null
+/*
+ efaxos.c - O/S-dependent routines
+ Copyright 1995, Ed Casas
+*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <unistd.h>
+
+#ifndef FD_SET
+#include <sys/select.h> /* for AIX */
+#endif
+
+#include "efaxlib.h"
+#include "efaxmsg.h"
+#include "efaxos.h"
+
+#ifdef USE_TERMIO
+#include <termio.h>
+#include <sys/ioctl.h>
+#define termios termio
+#define tcgetattr(fd, pt) ioctl(fd, TCGETA, pt)
+#define tcsetattr(fd, x, pt) ioctl(fd, TCSETAW, pt)
+#define cfsetospeed(pt, b) ((pt)->c_cflag = ((pt)->c_cflag & ~CBAUD) | b)
+#define cfsetispeed(pt, b)
+#define tcdrain(fd)
+#else
+#include <termios.h>
+#endif
+
+#ifdef TIOCSSOFTCAR
+#include <sys/ioctl.h>
+#endif
+
+#ifndef CRTSCTS
+#define CRTSCTS 0
+#endif
+
+
+/* The milliseconds portion of the current time. If your system
+ does not provide gettimeofday(3) you can safely substitute a
+ dummy function that returns 0. This will cause the
+ milliseconds portion of time stamps to be printed as 0. */
+
+int time_ms ( void )
+{
+ struct timeval tv ;
+ gettimeofday ( &tv, NULL ) ;
+ return (int) ( tv.tv_usec / 1000 ) ;
+}
+
+
+/* Process elapsed time in milliseconds. This is used for
+ ``virtual flow control'' only. */
+
+long proc_ms ( void )
+{
+ struct timeval t ;
+ static int init=0 ;
+ static struct timeval s ;
+ if ( ! init ) {
+ gettimeofday ( &s, 0 ) ;
+ init = 1 ;
+ }
+ gettimeofday ( &t, 0 ) ;
+ return ( t.tv_sec - s.tv_sec ) * 1000 + ( t.tv_usec - s.tv_usec ) / 1000 ;
+}
+
+
+/* Wait for t millisecond (for systems without usleep). */
+
+void msleep ( int t )
+{
+ struct timeval timeout ;
+ timeout.tv_sec = t / 1000 ;
+ timeout.tv_usec = ( t % 1000 ) * 1000 ;
+ if ( select ( 1 , 0 , 0 , 0 , &timeout ) < 0 )
+ msg ("ES2select failed in msleep:") ;
+}
+
+
+/* Return number of characters ready to read or < 0 on error. t
+ is tenths of a second of idle time before timing out. If t is
+ negative, waits forever. */
+
+int tdata ( TFILE *f, int t )
+{
+ int n, err=0 ;
+ fd_set fds ;
+
+ struct timeval timeout ;
+
+ if ( f->fd < 0 ) msg ( "Ecan't happen (faxdata)" ) ;
+
+ timeout.tv_sec = t / 10 ;
+ timeout.tv_usec = ( t % 10 ) * 100000 ;
+
+ FD_ZERO ( &fds ) ;
+ FD_SET ( f->fd, &fds ) ;
+
+ do {
+ n = select ( f->fd + 1, &fds, 0, 0, t<0 ? 0 : &timeout ) ;
+ if ( n < 0 ) {
+ if ( errno == EINTR ) {
+ msg ( "W0 select() interrupted in tdata()" ) ;
+ } else {
+ err = msg ( "ES2 select() failed in tdata():" ) ;
+ }
+ }
+ } while ( n < 0 && ! err ) ;
+
+ return n ;
+}
+
+
+/* tundrflw is called only by the tgetc() macro when the buffer
+ is empty. t is maximum idle time before giving up. Returns
+ number of characters read or EOF on timeout or errors. */
+
+int tundrflw ( TFILE *f, int t )
+{
+ int n ;
+
+ n = tdata ( f, t ) ;
+
+ if ( n > 0 )
+ if ( ( n = read( f->fd, f->ibuf, IBUFSIZE ) ) < 0 )
+ msg ( "ES2fax device read:" ) ;
+
+ f->iq = ( f->ip = f->ibuf ) + ( n > 0 ? n : 0 ) ;
+
+ return n > 0 ? n : EOF ;
+}
+
+
+/* tgetd returns the next data character after removing DLE
+ escapes, DLE-ETX terminators and fixing bit order. Evaluates
+ to the next character, EOF on error/timeout, or -2 on DLE-ETX. */
+
+int tgetd ( TFILE *f, int t )
+{
+ int c ;
+
+ if ( ( c = tgetc(f,t) ) < 0 )
+ c = EOF ;
+ else
+ if ( c != DLE )
+ c = f->ibitorder[c] ;
+ else { /* escape sequence */
+ c = tgetc(f,t) ;
+ if ( c == ETX )
+ c = -2 ;
+ else
+ if ( c == DLE || c == SUB )
+ c = f->ibitorder [ DLE ] ;
+ else
+ c = msg ( "W0invalid escape sequence (DLE-%s) in data", cname(c) ) ;
+ }
+
+ return c ;
+}
+
+/* Write buffer to modem. Returns 0 or EOF on error. */
+
+int tput ( TFILE *f, uchar *p, int n )
+{
+ int m=0 ;
+
+ while ( n > 0 && ( m = write( f->fd, p, n ) ) > 0 ) {
+ if ( m != n )
+ msg ( "Wonly wrote %d of %d bytes", m, n ) ;
+ n -= m ;
+ p += m ;
+ }
+
+ if ( m < 0 )
+ msg ( "ES2fax device write:" ) ;
+
+ return m ;
+}
+
+
+/* Compare current termios state with termios struct t. Returns 0 if equal,
+ 1 otherwise. */
+
+int ckfld ( char *field, int set, int get )
+{
+ return set == get ?
+ 0 : msg ( "W1 termios.%s is 0%08o, not 0%08o", field, get, set ) ;
+}
+
+int checktermio ( struct termios *t, TFILE *f )
+{
+ struct termios s ;
+ int err=0 ;
+ s.c_iflag=s.c_oflag=s.c_lflag=s.c_cflag=s.c_cc[VMIN]=s.c_cc[VTIME]=0 ;
+ if ( tcgetattr ( f->fd , &s ) )
+ err = msg ("ES2tcgetattr failed:") ;
+
+ if ( ! err ) return
+ ckfld ( "iflag" , t->c_iflag, s.c_iflag ) ||
+ ckfld ( "oflag" , t->c_oflag , s.c_oflag ) ||
+ ckfld ( "lflag" , t->c_lflag , s.c_lflag ) ||
+ ckfld ( "cflag" , t->c_cflag , s.c_cflag ) ||
+ ckfld ( "START" , t->c_cc[VSTART] , s.c_cc[VSTART] ) ||
+ ckfld ( "STOP" , t->c_cc[VSTOP] , s.c_cc[VSTOP] ) ||
+ ckfld ( "MIN" , t->c_cc[VMIN] , s.c_cc[VMIN] ) ||
+ ckfld ( "TIME" , t->c_cc[VTIME] , s.c_cc[VTIME] ) ;
+ return err ;
+}
+
+
+/* Set serial port mode. Sets raw, 8-bit, 19.2 kbps mode with no
+ flow control or as required. Break and parity errors are
+ ignored. CLOCAL means DCD is ignored since some modems
+ apparently drop it during the fax session. Flow control is
+ only used when sending. Returns 0 or 2 on error. */
+
+int ttymode ( TFILE *f, enum ttymodes mode )
+{
+ int err=0, i ;
+ static struct termios t, oldt, *pt ;
+ static int saved=0 ;
+
+ if ( ! saved ) {
+ if ( tcgetattr ( f->fd, &oldt ) )
+ err = msg ( "ES2tcgetattr on fd=%d failed:", f->fd ) ;
+ else
+ saved=1 ;
+ }
+
+ t.c_iflag = IGNBRK | IGNPAR ;
+ t.c_oflag = 0 ;
+ t.c_cflag = CS8 | CREAD | CLOCAL ;
+ t.c_lflag = 0 ;
+
+ for ( i=0 ; i<NCCS ; i++ ) t.c_cc[i] = 0 ;
+
+ t.c_cc[VMIN] = 1 ;
+ t.c_cc[VTIME] = 0 ;
+ t.c_cc[VSTOP] = XOFF;
+ t.c_cc[VSTART] = XON;
+
+ pt = &t ;
+
+ switch ( mode ) {
+ case VOICESEND :
+ t.c_iflag |= IXON ;
+ t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
+ case VOICECOMMAND :
+ cfsetospeed ( pt, B38400 ) ;
+ cfsetispeed ( pt, B38400 ) ;
+ break ;
+ case SEND :
+ t.c_iflag |= IXON ;
+ t.c_cflag |= f->hwfc ? CRTSCTS : 0 ;
+ case COMMAND :
+ cfsetospeed ( pt, B19200 ) ;
+ cfsetispeed ( pt, B19200 ) ;
+ break ;
+ case DROPDTR :
+ cfsetospeed ( pt, B0 ) ;
+ break ;
+ case ORIGINAL :
+ if ( saved ) pt = &oldt ;
+ break ;
+ default :
+ err = msg ("E2can't happen(ttymode)") ;
+ break ;
+ }
+
+ if ( ! err && tcsetattr ( f->fd, TCSADRAIN, pt ) )
+ err = msg ( "ES2tcsetattr on fd=%d failed:", f->fd ) ;
+
+ if ( ! err && checktermio ( pt, f ) )
+ msg ( "Wterminal mode not set properly" ) ;
+
+ tcflow ( f->fd, TCOON ) ; /* in case XON got lost */
+
+ return err ;
+}
+
+
+/* Initialize TFILE data structure. Bit ordering: serial devices
+ transmit LS bit first. T.4/T.30 says MS bit is sent
+ first. `Normal' order therefore reverses bit order. */
+
+void tinit ( TFILE *f, int fd, int reverse, int hwfc )
+{
+ f->ip = f->iq = f->ibuf ;
+ f->obitorder = normalbits ;
+ f->ibitorder = reverse ? reversebits : normalbits ;
+ f->fd = fd ;
+ f->hwfc = hwfc ;
+ if ( ! normalbits[1] ) initbittab () ;
+}
+
+
+/* Open a serial fax device as a TFILE. Returns 0 if OK, 1 if
+ busy, 2 on error. */
+
+int ttyopen ( TFILE *f, char *fname, int reverse, int hwfc )
+{
+ int flags, err=0 ;
+
+ tinit ( f, open ( fname, O_RDWR | O_NDELAY | O_NOCTTY ), reverse, hwfc ) ;
+
+ if ( f->fd < 0 ) {
+ if ( errno == EBUSY ) {
+ err = 1 ;
+ } else {
+ err = msg ( "ES2can't open serial port %s:", fname ) ;
+ }
+ }
+
+ if ( ! err ) {
+ if ( ( flags = fcntl( f->fd, F_GETFL, 0 ) ) < 0 ||
+ fcntl( f->fd, F_SETFL, ( flags & ~O_NDELAY ) ) < 0 )
+ err = msg ( "ES2fax device fcntl failed %s:", fname ) ;
+ }
+
+#ifdef TIOCSSOFTCAR
+ {
+ int arg = 1 ;
+ if ( ! err )
+ if ( ioctl ( f->fd, TIOCSSOFTCAR, &arg ) )
+ msg ("WS unable to set software carrier:" ) ;
+ }
+#endif
+
+ return err ;
+}
+
+ /* UUCP-style device locking using lock files */
+
+/* Test for UUCP lock file & remove stale locks. Returns 0 on null file
+ name or if no longer locked, 1 if locked by another pid, 2 on error, 3
+ if locked by us. */
+
+int ttlocked ( char *fname, int log )
+{
+ int err=0, ipid ;
+ FILE *f ;
+ pid_t pid = 0 ;
+ char buf [ EFAX_PATH_MAX ] = "" ;
+
+ if ( fname && *fname == BINLKFLAG ) fname++ ;
+
+ if ( fname && ( f = fopen ( fname , "r" ) ) ) {
+
+ if ( fread ( buf, sizeof(char), EFAX_PATH_MAX-1, f ) == sizeof(pid_t) ||
+ sscanf ( buf, "%d" , &ipid ) != 1 ) {
+ pid = * (pid_t *) buf ;
+ if ( log ) msg ("X+ read binary pid %d from %s", (int) pid, fname ) ;
+ } else {
+ char *p ;
+ pid = (pid_t) ipid ;
+ if ( log ) {
+ msg ( "X+ read HDB pid %d [", (int) pid ) ;
+ for ( p=buf ; *p ; p++ ) msg ( "X+ %s", cname ( *p ) ) ;
+ msg ( "X+ ] from %s", fname ) ;
+ }
+ }
+
+ if ( kill ( pid, 0 ) && errno == ESRCH ) {
+ if ( log ) msg ("X - stale" ) ;
+ if ( remove ( fname ) )
+ err = msg ( "ES2can't remove stale lock %s from pid %d:",
+ fname, pid ) ;
+ else
+ err = msg ( "I0removed stale lock %s from pid %d", fname, pid ) ;
+ } else {
+ if ( pid != getpid() ) {
+ err = 1 ;
+ if ( log ) msg ( "X1 (not our pid)" ) ;
+ } else {
+ err = 3 ;
+ if ( log ) msg ( "X3 (our pid)" ) ;
+ }
+ }
+ fclose ( f ) ;
+ }
+ return err ;
+}
+
+
+/* Create UUCP (text or binary) lock file. Returns 0 on null
+ file name or if created, 1 if locked by another pid, 2 on
+ error, 3 if locked by us. */
+
+int ttlock ( char *fname, int log )
+{
+ int err=0, dirlen, bin=0 ;
+ FILE *f=0 ;
+ pid_t pid = getpid ( ) ;
+ char *p , buf [ EFAX_PATH_MAX ] = "" ;
+
+ if ( fname && *fname == BINLKFLAG ) {
+ fname++ ;
+ bin = 1 ;
+ }
+
+ err = ttlocked ( fname, log ) ;
+
+ if ( ! err ) {
+ dirlen = ( p = strrchr( fname , '/' ) ) ? p-fname+1 : strlen ( fname ) ;
+ sprintf ( buf , "%.*sTMP..%05d" , dirlen , fname , (int) pid ) ;
+ if ( ! ( f = fopen( buf, "w" ) ) )
+ err = msg ( "ES2can't open pre-lock file %s:", buf ) ;
+ }
+
+ if ( ! err && f ) {
+ if ( bin ) {
+ if ( fwrite ( &pid, sizeof(pid_t), 1, f ) < 1 )
+ err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
+ } else {
+ if ( fprintf ( f, "%10d\n", (int) pid ) < 0 )
+ err = msg ( "ES2can't write pre-lock file %s:", buf ) ;
+ }
+ }
+
+ if ( ! err && f ) {
+ if ( rename ( buf , fname ) == 0 ) {
+ chmod ( fname , 0444 ) ;
+ msg ( "Xcreated %s lock file %s", bin ? "binary" : "text", fname ) ;
+ } else {
+ err = ttlocked ( fname, log ) ;
+ if ( ! err )
+ err = msg ( "ES2can't rename lock file %s to %s:", buf, fname ) ;
+ }
+ }
+
+ if ( f ) {
+ fclose ( f ) ;
+ if ( err ) remove ( buf ) ;
+ }
+
+ return err ;
+}
+
+
+/* Remove lock file. Returns 0 on null file name, doesn't exist, or was
+ removed, 1 if the lock is to another pid, 2 on errors. */
+
+int ttunlock ( char *fname )
+{
+ int err = 0 ;
+
+ if ( fname && *fname == BINLKFLAG ) fname++ ;
+
+ switch ( ttlocked ( fname, 1 ) ) {
+ case 0: break ;
+ case 1: err = msg ( "E1won't remove lock %s (not ours)" , fname ) ; break ;
+ case 2: err = 2 ; break ;
+ case 3:
+ if ( remove ( fname ) ) {
+ err = msg ( "ES2can't remove lock %s:", fname ) ;
+ } else {
+ err = msg ( "X0removed lock file %s", fname ) ;
+ }
+ break ;
+ default:
+ err = msg ( "E2can't happen (ttunlock)" ) ;
+ break ;
+ }
+ return err ;
+}
+
+
+/* Lock all lock files and possibly log attempt if log=1.
+ Returns 0 if all locks [already] applied, 1 if any are locked
+ to other pids, 2 on any errors. */
+
+int lockall ( char **lkfiles, int log )
+{
+ int err = 0 ;
+ char **p = lkfiles ;
+ while ( *p && ! err )
+ if ( ( err = ttlock ( *p++, log ) ) == 3 ) err = 0 ;
+ return err ;
+}
+
+
+/* Remove all lock files. Returns 0 if all locks removed, 2 on
+ errors. */
+
+int unlockall ( char **lkfiles )
+{
+ int err = 0, i ;
+ char **p = lkfiles ;
+ while ( *p )
+ if ( ( i = ttunlock ( *p++ ) ) != 0 ) err = i ;
+ return err ;
+}
+
+/* Return basename of the argument or the whole thing if can't
+ find it. */
+
+char *efaxbasename ( char *p )
+{
+ return strrchr ( p , '/' ) ? strrchr ( p , '/' ) + 1 : p ;
+}
+
--- /dev/null
+#ifndef _EFAXOS_H
+#define _EFAXOS_H
+
+#include <time.h>
+
+#include "efaxlib.h"
+
+/* signals to be caught */
+
+#define ANSISIGS SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM
+#define UNIXSIGS SIGHUP, SIGQUIT, SIGIOT, SIGALRM
+#define CATCHSIGS ANSISIGS, UNIXSIGS
+
+/* Bit order reversal table. */
+
+extern unsigned char normalbits [ ] ;
+
+typedef enum ttymodes /* serial port modes: */
+{
+ COMMAND, /* 19200 8N1, no f/c, DTR high */
+ SEND, /* 19200 send-only XON/XOFF f/c */
+ VOICECOMMAND, /* 38400 8N1, no f/c, DTR high */
+ VOICESEND, /* 38400 send-only XON/XOFF f/c*/
+ DROPDTR, /* ", DTR low */
+ ORIGINAL /* restore original settings */
+} ttymodes ;
+
+/* OS-specific i/o & delay functions */
+
+/* We define new stream i/o macros because it's not possible to
+ do non-blocking reads/writes with C stream i/o [UNIX select()
+ gives the status of the file, not the stream buffer].*/
+
+#define IBUFSIZE 1024 /* read up to this many bytes at a time from fax */
+#define OBUFSIZE 1024 /* maximum bytes to write at a time to fax */
+
+typedef struct tfilestruct {
+ int fd ;
+ unsigned char *ip, *iq ;
+ unsigned char ibuf [ IBUFSIZE ] ;
+ unsigned char *ibitorder, *obitorder ;
+ int bytes, pad, lines ;
+ int hwfc ;
+ time_t start ;
+ long mstart ;
+ int rd_state ;
+} TFILE ;
+
+/* tgetc() is a macro like getc(). It evaluates to the next
+ character from the fax device or EOF after idle time t. */
+
+#define tgetc(f,t) ( (f)->ip >= (f)->iq && tundrflw(f,t) == EOF ? EOF : \
+ *(unsigned char*)(f)->ip++ )
+
+int tundrflw ( TFILE *f, int t ) ;
+int tgetd ( TFILE *f, int t ) ;
+int tput ( TFILE *f, unsigned char *p, int n ) ;
+int tdata ( TFILE *f, int t ) ;
+void tinit ( TFILE *f, int fd, int reverse, int hwfc ) ;
+int ttyopen ( TFILE *f, char *fname, int reverse, int hwfc ) ;
+int ttymode ( TFILE *f, ttymodes mode ) ;
+void msleep ( int t ) ;
+long proc_ms ( void ) ;
+int time_ms ( void ) ;
+
+/* POSIX execl */
+
+extern int execl ( const char *path, const char *arg , ... ) ;
+
+/* UUCP-style device locks */
+
+#define BINLKFLAG '#' /* prefix to force binary lock files */
+
+ /* [un]lock serial port using named files */
+int lockall ( char **lkfiles, int log ) ;
+int unlockall ( char **lkfiles ) ;
+
+/* extract program name to be used in messages from argv0 */
+
+char *efaxbasename ( char *p ) ;
+
+/* default fax modem device */
+
+#define FAXFILE "/dev/modem"
+
+#endif
--- /dev/null
+.TH EFIX 1 "February 1999" "" ""
+.UC 1
+.SH NAME
+efix \- convert between fax, text, bit-map and gray-scale formats
+.SH SYNOPSIS
+
+.B efix
+[
+.I options
+]
+.I file...
+
+.SH OPTIONS
+
+Where \fIoptions\fP are:
+
+.TP 9
+.B -i \fIf\fP
+the input image is in format \fIf\fP. Default is to automatically
+determine the input type from its contents.
+
+.TP 9
+.B
+ fax
+fax ("Group3") 1-D coded image
+
+.TP 9
+.B
+ text
+text. Line feeds separate lines, form feeds cause page breaks
+and tabs are expanded assuming tabs every 8 columns.
+
+.TP 9
+.B
+ pbm
+raw PBM (portable bit map)
+
+.TP 9
+.B
+ tiffg3
+TIFF format with Group 3 (fax) compression.
+
+.TP 9
+.B
+ tiffraw
+TIFF format with no compression.
+
+.TP 9
+.B -o \fIf\fP
+write the output in format \fIf\fP. Default is tiffg3.
+
+.TP 9
+.B
+ fax
+fax ("Group3") 1-D coded image
+
+.TP 9
+.B
+ pbm
+raw PBM
+
+.TP 9
+.B
+ pgm
+raw PGM (Portable Gray Map). Gray-scale values are produced by
+summing pixels in 4x4 pixel blocks. The output file is 1/4 of
+the size given by -p. The resulting image has 17 discrete values
+between 0 and 255.
+
+.TP 9
+.B
+ pcl
+HP-PCL (e.g. HP LaserJet).
+
+.TP 9
+.B
+ ps
+encapsulated Postscript (e.g. Apple Laserwriter). The file is
+compressed using differential coding vertically and run-length
+coding horizontally. There is no provision for positioning the
+image within the page and so the image will appear at the lower
+left corner of the page when printed.
+
+.TP 9
+.B
+ tiffg3
+TIFF format with Group 3 (fax) compression.
+
+.TP 9
+.B
+ tiffraw
+TIFF format with no compression.
+
+.TP 9
+.B -n \fIpat\fP
+use the printf(3) pattern \fIpath\fP to generate the output file
+name. Up to three %d escapes will be replaced by the page number
+starting with 1 (e.g. -n order.%03d will create file names
+order.001, order.002, etc.)
+
+.TP 9
+.B -v \fIlvl\fP
+print messages of type in string \fIlvl\fP. Each
+\fIlower-case\fP letter in \fIlvl\fP enables one type of message:
+
+.RS 12
+.B
+e -
+errors
+.br
+.B
+w -
+warnings
+.br
+.B
+i -
+information messages
+.br
+.B
+a -
+program arguments
+.br
+.B
+f -
+file format details
+.RE
+
+.RS 9
+The default is "ewi".
+.RE
+
+.TP 9
+.B -f \fIfnt\fP
+use font file \fIfnt\fP for text. The font file for an WxH font
+should be a bit map of an image of H rows and 256*W columns.
+Each successive WxH cell contains the bit map for characters with
+codes from 0 to 255. The default is to use a built-in 8x16 font.
+
+.TP 9
+.B -s \fIX\fP\fRx\fP\fIY\fP
+scale the input by a factor of X horizontally and Y vertically.
+Scaling does not change the size of the output (use -p). If Y is
+not specified it is assumed to be the same as X. Any floating
+point value may be used for X and Y. The default is 1,1.
+
+.TP 9
+.B -d \fIR\fP\fR,\fP\fID\fP
+displace the output right by R and down by D (opposite if
+negative). See below for units. Default is 0,0.
+
+.TP 9
+.B -p \fIW\fP\fRx\fP\fIH\fP
+truncate or pad the output to generate an image of width W and
+height H. This does not scale the input. See below for units.
+The default is the size of the input image if it can be
+determined or A4 (215x297mm) if it can't.
+
+.TP 9
+.B -r \fIX\fP\fRx\fP\fIY\fP
+assume an output device resolution of X by Y dots per inch. If Y
+is not specified it is assumed to be the same as X. The default
+is the input resolution if it can be determined or the fax
+resolution of 204.1x195.6 dpi if it can't.
+
+.TP 9
+.B -R \fIX\fP\fRx\fP\fIY\fP
+assume an input device resolution of X by Y dots per inch. If Y
+is not specified it is assumed to be the same as X. The default
+is the input resolution if it can be determined or the fax
+resolution of 204.1x195.6 dpi if it can't.
+
+.TP 9
+.B -l \fIn\fP
+place n lines per page during text input. Default is 66.
+
+.TP 9
+.B -O \fIf\fP
+overlay (logical OR) the image from file f into the output. Use
+'-' for standard input (-O-). Default is no overlay file.
+
+.TP 9
+.B -M
+ignore all other options and copy the standard input to the
+standard output while applying base64 (MIME) encoding as
+specified by RFC 1521.
+
+
+.SH FILES
+
+If no -n options are given, output is written to the standard
+output.
+
+.SH UNITS
+
+The units of the W, H, R, and D values above are in inches by
+default. Any floating point value may be used. Units of inches,
+centimetres, millimetres or points (72 per inch) can be used
+instead by appending one of the strings `in', `cm', `mm', or `pt'
+to the argument (e.g. -d2,4cm).
+
+.SH CUT AND PASTE
+
+The -d and -p options allow efix to cut out images from received
+faxes for use in other faxes or documents. The -d option specifies
+the top left portion of the desired image and the -p option gives
+the size of the cut image. For example, the command
+.RS
+.nf
+.ft CW
+ efix -d-5,-8 -p2,1 sample.001 >sig.001
+.ft P
+.fi
+.RE
+would cut out part of the input with its top left corner 5 inches
+from the left edge and 8 inches from top of the input image. The
+output image would be 2 inches wide and 1 inch high.
+
+The -O option allows efix to superimpose two or more images. The
+overlay image must be in fax format and cannot be scaled,
+truncated or shifted. However, multiple efix commands may be
+pipelined to transform images before combining them. For
+example, the command
+.RS
+.nf
+.ft CW
+ efix -d4,8 signature | \\
+ efix -O- letterhead | \\
+ efix -O- letter.002 >letter.002.new
+.ft P
+.fi
+.RE
+will shift the image in the file signature down 8 inches and
+right 4 inches and combine (overlay) it with the images in the
+files letterhead and letter.002.
+
+.SH REFERENCES
+
+Gunter Born, "The File Formats Handbook", International Thompson
+Computer Press, 1995.
+
+.SH COPYRIGHT
+
+efix is copyright 1994 -- 1999 by Ed Casas. It may be used,
+copied and modified under the terms of the GNU Public License.
+
+.SH DISCLAIMER
+
+Although \fBefix\fP has been tested it may have errors that will
+prevent it from working correctly on your system. Some of these
+errors may cause serious problems including loss of data.
+
+.SH SEE ALSO
+
+.BR efax(1),
+.BR ghostscript(1),
+.BR pbm(5),
+.BR pgm(5).
+
+.SH BUGS
+
+Only reads two types of TIFF compression formats.
+
+Does not write multi-page TIFF files (a feature).
+
--- /dev/null
+#define Copyright "Copyright 1999 Ed Casas"
+
+#define Version "efix v 0.3"
+
+/*
+ Copyright (C) 1999 Ed Casas
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ Please contact the author if you wish to use efax or efix in
+ ways not covered by the GNU GPL.
+
+ You may contact the author by e-mail at: edc@cce.com, by mail
+ at: 2629 West 3rd Ave, Vancouver, BC, Canada, V6K 1M4, or by
+ fax at: +1 604 734 5291.
+
+*/
+
+const char *Usage =
+ "Usage:\n"
+ " %s [ option ]... file... \n"
+"Options (defaults):\n"
+ " -i f input format (auto):\n"
+ " fax fax (\"Group3\") 1-D coded image\n"
+ " text text\n"
+ " pbm raw PBM (portable bit map)\n"
+ " tiffg3 TIFF, Group 3 fax compression\n"
+ " tiffraw TIFF, no compression\n"
+ " pcx mono PCX\n"
+ " dcx mono DCX\n"
+ " -o f output format (tiffg3):\n"
+ " fax fax (\"Group3\") 1-D coded image\n"
+ " pbm Portable Bit Map\n"
+ " pgm Portable Gray Map (decimated by 4)\n"
+ " pcl HP-PCL (e.g. HP LaserJet)\n"
+ " ps Postscript (e.g. Apple Laserwriter)\n"
+ " tiffg3 TIFF, Group 3 fax compression\n"
+ " tiffraw TIFF, no compression\n"
+ " pcx mono PCX\n"
+ " dcx mono DCX\n"
+ " -n pat printf() pattern for output file name (ofile)\n"
+ " -f fnt use PBM font file fnt for text (built-in)\n"
+ " -l n lines per text page (66)\n"
+ " -v lvl print messages of type in string lvl (ewi)\n"
+ " -s XxY scale input by X and Y (Y optional) (1x1)\n"
+ " -r XxY resolution of output is X by Y (dpi, Y optional) (204x196)\n"
+ " -R XxY resolution of input is X by Y (dpi, Y optional) (204x196)\n"
+ " -p WxH pad/truncate output to width W by height H (215x297mm)\n"
+ " -d R,D displace output right R, down D (opposite if -ve) (0,0)\n"
+ " -O f overlay file f (none)\n"
+ " -M ignore other options and base64 (MIME) encode stdin to stdout\n"
+ "\n"
+ "Add 'in', 'cm', 'mm', or 'pt' to -p and -d arguments (default in[ches]).\n"
+ "Default output size and resolution is same as input (if known).\n"
+ ;
+
+#include <ctype.h> /* ANSI C */
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "efaxlib.h"
+#include "efaxmsg.h"
+
+#ifndef INT_MAX
+#define INT_MAX 32767
+#endif
+
+/* Allowed input and output formats. *** MUST match enum *** */
+
+char *iformatstr[] = { " 3text", " 1pbm", " 2fax", " 4tiffg3", " 4tiffraw",
+ " 6pcx", " 6pcxraw", " 8dcx", 0 } ;
+
+char *oformatstr[] = { " 1pbm" , " 2fax", " 3pcl", " 4ps", " 5pgm",
+ " 7tiffg3", " 8tiffraw",
+ "11pcx", "12pcxraw", "13dcx", 0 } ;
+
+/* Look up a string in a NULL-delimited table where the first
+ character of each string is the digit to return if the rest of
+ the string matches. Returns the value of the digit for the
+ matching string or -1 if no matches. */
+
+int lookup ( char **tab, char *s )
+{
+ char **p ;
+ for ( p=tab ; p && *p && strcmp ( *p+2, s ) ; p++ ) ;
+ return p && *p ? atoi ( *p ) : -1 ;
+}
+
+
+/* Extract pair of values from string. If it's a `dim'ension,
+ two values are required and they are converted to inches, else
+ the y value is optional. Returns 0 or 2 on error. */
+
+int getxy ( char *arg, float *x, float *y, int dim )
+{
+ int i, n, nc=0, err=0 ;
+ char c ;
+ static char *unitstr[] = { " 0in", " 1cm", " 2mm", " 3pt", 0 } ;
+ static float unitval[] = { 1.0, 2.54, 25.4, 72.0, 1.0 } ;
+
+ if ( ! arg )
+ err = msg ( "E2 missing argument" ) ;
+
+ if ( !x || !y )
+ err = msg ( "E2 can't happen (getxy)" ) ;
+
+ if ( ! err ) {
+ n = sscanf ( arg , "%f%c%f%n", x, &c, y, &nc ) ;
+ switch ( n ) {
+ case 0 : err = msg ( "E2bad X value in (%s)", arg ) ; break ;
+ case 2 : err = msg ( "E2bad Y value in (%s)", arg ) ; break ;
+ }
+ }
+
+ if ( ! err ) {
+ if ( dim ) {
+ if ( n != 3 ) {
+ err = msg ( "Emissing Y dimension in (%s)", arg ) ;
+ } else {
+ while ( arg [ nc ] && isspace ( arg [ nc ] ) ) nc++ ;
+ if ( arg [ nc ] ) {
+ if ( ( i = lookup ( unitstr, arg+nc ) ) >= 0 ) {
+ *x /= unitval [ i ] ;
+ *y /= unitval [ i ] ;
+ } else {
+ err = msg ( "E2bad units: `%s'", arg+nc ) ;
+ }
+ }
+ }
+ } else {
+ if ( n == 1 ) *y = *x ;
+ }
+ }
+
+ if ( ! err )
+ msg ( "Aconverted (%s) into %f x %f", arg, *x, *y ) ;
+
+ return err ;
+}
+
+
+/* Copy stdin to stdout while applying base64 (RFC 1521)
+ encoding. This encoding must be applied after the file is
+ complete since some output formats (e.g. TIFF) require seeking
+ backwards within the (binary) file. */
+
+int base64encode ( void )
+{
+ int err=0, c ;
+ uchar n=0, m=0, bits=0 ;
+
+ static uchar chartab[65] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/" ;
+
+ while ( ( c = fgetc ( stdin ) ) >= 0 ) {
+ switch ( n ) {
+ case 0:
+ putc ( chartab [ c >> 2 ], stdout ) ;
+ bits = c & 0x3 ;
+ n = 1 ;
+ break ;
+ case 1:
+ putc ( chartab [ (bits << 4) | ( c >> 4 ) ], stdout ) ;
+ bits = c & 0xf ;
+ n = 2 ;
+ break ;
+ case 2:
+ putc ( chartab [ (bits << 2) | ( c >> 6 ) ], stdout ) ;
+ putc ( chartab [ c & 0x3f ], stdout ) ;
+ n = 0 ;
+ if ( ++m >= 18 ) {
+ putc ( '\n', stdout ) ;
+ m = 0 ;
+ }
+ break ;
+ }
+
+ }
+
+ switch ( n ) {
+ case 0:
+ break ;
+ case 1:
+ putc ( chartab [ (bits << 4) | ( 0 >> 4 ) ], stdout ) ;
+ putc ( '=', stdout ) ;
+ putc ( '=', stdout ) ;
+ break ;
+ case 2 :
+ putc ( chartab [ (bits << 2) | ( 0 >> 6 ) ], stdout ) ;
+ putc ( '=', stdout ) ;
+ break ;
+ }
+
+ putc ( '\n', stdout ) ;
+
+ return err ;
+}
+
+
+int main( int argc, char **argv)
+{
+ int err=0, done=0, i, c ;
+ int nr, pels, ovnr, ovpels, no ; /* run/pixel/repeat counts */
+ int linesout ;
+ int page, ilines, olines ; /* page & line counts */
+ int xs, ys, w, h, ixsh, iysh ; /* integer scale, size & shift */
+ short runs [ MAXRUNS ] , ovruns [ MAXRUNS ] ;
+
+ float /* defaults: */
+ xsc=1.0, ysc=1.0, /* scale */
+ xsh=0.0, ysh=0.0, /* shift */
+ dxres = 204.145, /* o/p res'n: 1728/215mm * 25.4 x */
+ dyres = 195.58, /* 7.7 * 25.4 */
+ dxsz = 215 / 25.4, /* o/p size: 8.5" x A4 */
+ dysz = 297 / 25.4 ;
+
+ float /* arguments: */
+ axres = 0, ayres = 0, axsz = 0, aysz = 0, ainxres=0, ainyres=0 ;
+
+ float /* values used: */
+ xres = 0, yres = 0, xsz = 0, ysz = 0 ;
+
+ IFILE ifile, ovfile ;
+ OFILE ofile ;
+
+ char **ifnames, *ovfnames [ 2 ] = { 0, 0 } ;
+
+ int iformat=I_AUTO, oformat=O_TIFF_FAX, pglines=0 ;
+ char *ofname=0 ;
+
+ faxfont font, *pfont=0 ; /* text font */
+
+ /* initialize */
+
+ argv0 = argv[0] ;
+
+ /* process arguments */
+
+ while ( !err && (c=nextopt(argc,argv,"n:i:o:O:v:l:f:r:s:p:d:R:M") ) != -1) {
+ switch ( c ) {
+ case 'n':
+ ofname = nxtoptarg ;
+ break ;
+ case 'i':
+ if ( ( iformat = lookup ( iformatstr, nxtoptarg ) ) < 0 )
+ err = msg ( "E2invalid input type (%s)", nxtoptarg ) ;
+ break ;
+ case 'o':
+ if ( ( oformat = lookup ( oformatstr, nxtoptarg ) ) < 0 )
+ err = msg ( "E2invalid output type (%s)", nxtoptarg ) ;
+ break ;
+ case 'O':
+ ovfnames[0] = nxtoptarg ;
+ break ;
+ case 'v':
+ verb[0] = nxtoptarg ;
+ msg ( "A " Version ) ;
+ for ( i=0 ; i<argc ; i++ ) msg ( "Aargv[%d]=%s", i, argv[i]) ;
+ break ;
+ case 'l':
+ if ( sscanf ( nxtoptarg , "%d", &pglines ) != 1 || pglines <= 0 ) {
+ err = msg ( "E2bad page length (%s)", nxtoptarg ) ;
+ pglines = 0 ;
+ }
+ break ;
+ case 'f' :
+ if ( ! ( err = readfont ( nxtoptarg, &font ) ) )
+ pfont = &font ;
+ break ;
+ case 's' : err = getxy ( nxtoptarg, &xsc , &ysc , 0 ) ; break ;
+ case 'r' : err = getxy ( nxtoptarg, &axres, &ayres, 0 ) ; break ;
+ case 'R' : err = getxy ( nxtoptarg, &ainxres, &ainyres, 0 ) ; break ;
+ case 'p' : err = getxy ( nxtoptarg, &axsz , &aysz , 1 ) ; break ;
+ case 'd' : err = getxy ( nxtoptarg, &xsh , &ysh , 1 ) ; break ;
+ case 'M' : err = base64encode() ; done=1 ; break ;
+ default : fprintf ( stderr, Usage, argv0 ) ; err = 2 ; break ;
+ }
+ }
+
+ msg ( "I " Version " " Copyright ) ;
+
+ if ( ! err && ! done ) {
+
+ if ( nxtoptind < argc ) {
+ ifnames = argv + nxtoptind ;
+ if ( argv [ argc ] ) {
+ err = msg ("E2can't happen(unterminated argv)") ;
+ } else {
+ newIFILE ( &ifile, ifnames ) ;
+ }
+ } else {
+ err = msg ( "E3 missing input file name" ) ;
+ }
+
+ if ( pfont ) ifile.font = pfont ;
+ if ( pglines ) ifile.pglines = pglines ;
+
+ newIFILE ( &ovfile, ovfnames ) ;
+
+ newOFILE ( &ofile, oformat, ofname, 0, 0, 0, 0 ) ;
+
+ }
+
+ for ( page = 0 ; ! err && ! done ; page++ ) {
+
+ if ( nextipage ( &ifile, page != 0 ) ) {
+ done=1 ;
+ continue ;
+ }
+
+ /* set output size and resolution equal to input if none specified */
+
+ if ( ainxres > 0 ) ifile.page->xres = ainxres ;
+ if ( ainyres > 0 ) ifile.page->yres = ainyres ;
+
+ if ( ifile.page->xres <= 0 ) ifile.page->xres = dxres ;
+ if ( ifile.page->yres <= 0 ) ifile.page->yres = dyres ;
+
+ xres = axres > 0 ? axres : ifile.page->xres ;
+ yres = ayres > 0 ? ayres : ifile.page->yres ;
+
+ xsz = axsz > 0 ? axsz : ( ifile.page->w > 0 ?
+ ifile.page->w / ifile.page->xres : dxsz ) ;
+ ysz = aysz > 0 ? aysz : ( ifile.page->h > 0 ?
+ ifile.page->h / ifile.page->yres : dysz ) ;
+
+
+ w = xsz * xres + 0.5 ; /* output dimensions in pixels */
+ h = ysz * yres + 0.5 ;
+
+ ixsh = xsh * xres ; /* x/y shifts in pixels/lines */
+ iysh = ysh * yres ;
+
+ if ( ( w & 7 ) != 0 ) /* just about everything requires... */
+ msg ("Iimage width rounded to %d pixels",
+ w = ( w + 7 ) & ~7 ) ;
+
+ if ( ofile.format == O_PGM && h & 3 ) /* PGM x4 decimation requires... */
+ msg ("I PGM image height rounded up to %d lines",
+ h = ( h + 3 ) & ~3 ) ;
+
+ if ( w <= 0 || h <= 0 || xres < 0 || yres < 0 )
+ err = msg ( "E2negative/zero scaling/size/resolution" ) ;
+
+ if ( ofile.format == O_PCL && /* check for strange PCL resolutions */
+ ( xres != yres || ( xres != 300 && xres != 150 && xres != 75 ) ) )
+ msg ( "Wstrange PCL resolution (%.0fx%.0f)", xres, yres ) ;
+
+ if ( w > MAXBITS*8 ) /* make sure output will fit... */
+ err = msg( "E2requested output width too large (%d pixels)", w ) ;
+
+ ofile.w = w ;
+ ofile.h = h ;
+ ofile.xres = xres ;
+ ofile.yres = yres ;
+
+ /* scale according to input file resolution */
+
+ xs = 256 * xsc * xres / ifile.page->xres + 0.5 ;
+ ys = 256 * ysc * yres / ifile.page->yres + 0.5 ;
+
+ if ( xs <= 0 || ys <= 0 )
+ err = msg ( "E2negative/zero scaling" ) ;
+
+ if ( *ovfnames ) /* [re-]open overlay file */
+ if ( nextipage ( &ovfile , 0 ) ) {
+ err=2 ;
+ continue ;
+ }
+
+ if ( nextopage ( &ofile, page ) ) {
+ err=2 ;
+ continue ;
+ }
+ linesout=0 ;
+
+ /* y-shift */
+
+ if ( iysh > 0 ) {
+ writeline ( &ofile, ( ( *runs = w ), runs ), 1, iysh ) ;
+ linesout += iysh ;
+ } else {
+ for ( i=0 ; i < -iysh ; i++ )
+ readline ( &ifile, runs, 0 ) ;
+ }
+
+ /* copy input to output */
+
+ olines = ilines = 0 ;
+
+ while ( linesout < h ) {
+
+ if ( ( nr = readline ( &ifile, runs, &pels ) ) < 0 )
+ break ;
+ else
+ ilines++ ;
+
+ if ( *ovfnames ) {
+ if ( ( ovnr = readline ( &ovfile, ovruns, &ovpels ) ) >= 0 )
+ nr = runor ( runs, nr, ovruns, ovnr, 0, &pels ) ;
+ }
+
+ /* x-scale, x-shift & x-pad input line */
+
+ pels = ( xs == 256 ) ? pels : xscale ( runs, nr, xs ) ;
+ pels += ( ixsh == 0 ) ? 0 : xshift ( runs, nr, ixsh ) ;
+ nr = ( pels == w ) ? nr : xpad ( runs, nr, w - pels ) ;
+
+ /* y-scale by deleting/duplicating lines. */
+
+ no = ( ( ilines * ys ) >> 8 ) - olines ;
+
+ if ( linesout + no > h ) no = h - linesout ;
+ olines += no ;
+
+ writeline ( &ofile, runs, nr, no ) ;
+ linesout += no ;
+ }
+
+ /* y-pad */
+
+ if ( linesout < h )
+ writeline ( &ofile, ( ( *runs = w ), runs ), 1, h - linesout ) ;
+
+ if ( ferror ( ifile.f ) ) err = msg ( "ES2input error:" ) ;
+ }
+
+ nextopage ( &ofile, EOF ) ;
+
+ return err ;
+}
--- /dev/null
+#!/bin/sh
+#
+# fax - script to make, send, receive, view or print a fax
+# Copyright 1993-1999 by Ed Casas
+#
+# --- Start of user configuration section ---
+#
+# Notes:
+#
+# - do not put spaces before or after the equal (=) signs.
+#
+# - variables can also be set on the command line, for example:
+# fax DEV=cua0 send file.ps
+# or in a configuration file (see CONFIGFILES below)
+#
+
+# The names of the fax script, efax and efix, including full path
+# if necessary.
+
+FAX=fax
+EFAX=efax
+EFIX=efix
+
+# The device to which the fax modem is connected (e.g. ttya for
+# /dev/ttya). Use a dial-out (cua) device if available. If
+# there are links to this device then all programs must use same
+# name or the UUCP locking mechanism will fail. For example, if
+# /dev/modem is a link to /dev/cua1, then getty, uucp, kermit,
+# pppd, dip, etc. must *all* use either /dev/modem or /dev/cua1.
+
+DEV=cua1
+
+# Your fax number in international format, 20 characters maximum.
+# Use only digits, spaces, and the "+" character.
+
+FROM="+1 800 555 5555"
+
+# Your name as it should appear on the page header.
+
+NAME="Put Your Name Here"
+
+# The preferred page size for creating and printing faxes.
+# Allowed values are "letter", "legal", and "a4".
+
+PAGE=letter
+# PAGE=legal
+# PAGE=a4
+
+# The type of printer. Use 'pcl' for HP-PCL or 'ps' for
+# Postscript. See definition of PRINT (below) for more options.
+
+PRTYPE=ps # Postscript (e.g. Apple LaserWriter)
+# PRTYPE=pcl # HP-PCL (e.g. HP LaserJet)
+
+# The command to print image files from standard input. Typically
+# this is "lpr" or "lp".
+
+PRCMD="lpr"
+
+# The command to view a Portable Gray Map (PGM) image from the
+# standard input. Typically "xv -" or "xloadimage stdin".
+
+VIEWCMD="xloadimage stdin" # best
+# VIEWCMD="pnmtoxwd | xwud" # slower alternative
+# VIEWCMD="xv -" # much slower alternative
+
+# The name of the Ghostscript executable including full path if
+# necessary. Only required if faxing Postscript files.
+
+GS=gs
+
+# Dial string prefix and suffix such as T for tone dialing, P for
+# pulse dialing, 9 to get an external line, commas for delays or
+# W to wait for dial tone. See definition of TELCVT below if you
+# have more complex requirements.
+
+DIALPREFIX="T"
+DIALSUFFIX=""
+
+# The name(s) of lock file(s) according to your system's
+# conventions. Protect with single quotes for delayed evaluation.
+# Add a leading '#' to the file name to use binary format.
+
+LOCK='-x /var/lock/LCK..$DEV' # modern systems
+# LOCK='-x /usr/spool/uucp/LCK..$DEV' # older systems
+# LOCK='-x /var/lock/LCK..$DEV -x /var/spool/uucp/LCK..$DEV' # both
+# LOCK='-x #/usr/spool/uucp/LCK..$DEV' # binary format
+# LOCK='-x /usr/spool/locks/LK.047.040.011' # SysV style names
+# LOCK='' # no lock file
+
+# Uncomment one of the following lines to force xon/xoff flow
+# control only if you have one of the types of modems listed.
+
+# FCINIT='-j\Q4' # AT&T (Dataport, Paradyne)
+# FCINIT='-j\Q1' # Motorola (Power Modem, 3400 Pro,...)
+# FCINIT='-j*F1' # QuickComm (Spirit II)
+# FCINIT='-j&H2' # USR (Courier, Sportster)
+# FCINIT='-or' # Multi-Tech (for bit reversal)
+
+# ****************************************************************
+# The remaining options probably won't need to be changed.
+# ****************************************************************
+
+# Configuration files that are sourced if they exist. Comment
+# out if you don't need to use config files. Warning: any type of
+# shell command in these files will be executed.
+
+CONFIGFILES="/etc/efax.rc ${HOME:-~}/.efaxrc ./.efaxrc"
+
+# A command that will generate unique names for logs and received
+# files. 'date +%m%d%H%M%S' works on most systems. Protect with
+# single quotes.
+
+TSTAMP='date +%m%d%H%M%S'
+# TSTAMP='echo $$' # alternative - use process number
+
+# Shell command to convert aliases to phone numbers when sending
+# faxes. When executed $1 will be the alias and $f the file name
+# to search. The example below uses a directory file where alias
+# lines start with the keyword "fax" followed by the alias in
+# parentheses and a colon. The remainder of the line is taken to
+# be the phone number. Other lines are ignored. For example, if
+# one of the files in DIRFILES (defined below) contained the line
+# "fax(kpmg): 691-3031", you could use the command "fax send kpmg
+# invoice.24". Protect with single quotes.
+
+LOOKUP='eval sed -n -e "/^fax($1):/{" -e "s/^[^:]*://p" -eq -e"}" $f'
+
+# List of telephone directory file(s) to be searched. The
+# default is the file .faxdir in the user's home directory.
+
+DIRFILES="${HOME:-.}/.faxdir"
+
+# Shell command to convert phone numbers to dial strings. This
+# lets you to store numbers without the long distance or
+# alternate carrier access codes, passwords, accounting digits,
+# etc. In the examples below this is used to convert numbers
+# beginning with '+'; the first substitution handles same-country
+# calls and the second handles international calls.
+
+TELCVT='sed -e s/+1/1/ -e s/+/011/' # North America
+# TELCVT='sed -e s/+61/0/ -e s/+/0011/' # Australia
+# TELCVT='sed -e s/+44/0/ -e s/+/00/' # UK
+# TELCVT='sed -e s/+49/0/ -e s/+/00/' # Germany
+# TELCVT='sed -e s/+852// -e s/+/001/' # Hong Kong
+# TELCVT='sed -e s/+33// -e s/+/19W/' # France (?)
+# TELCVT='sed -e s/+34/0/ -e s/+/07W/' # Spain
+# TELCVT='sed -e s/+1/10288/' # use AT&T
+# TELCVT='sed -e s/+/T82W1682W9W/' # get out of PBX
+
+# efix options to use a bitmap font for text-to-fax conversion.
+# The option -l66 puts 66 lines of text per page, -d1,1 sets 1
+# inch top & left margin. Comment these out to use the built-in
+# font. Use "fax makefont" to make bitmap fonts from Postscript
+# fonts.
+
+# TEXTFONT="-l66 -d1,1 -f /usr/bin/efaxfont"
+
+# efax options to specify a different font for headers. Generate
+# using "fax makefont."
+
+# HDRFONT="-f /usr/bin/efaxfont"
+
+# Dimensions of page sizes.
+
+PAGE_letter="8.465x11in" # fax width x letter length
+PAGE_legal="8.465x14in" # fax width x legal length
+PAGE_a4="21x29.7cm" # ISO A4
+
+# Default resolution for converting to fax format. Can only be
+# 204x196 or 204x98.
+
+RES=204x196 # default "Fine" resolution (196 lpi)
+# RES=204x98 # standard resolution (98 lpi)
+
+# When the print and view commands below are executed, $f will be
+# the input file name and $PAGEDIM will be one of the above page
+# dimensions. Protect with single quotes.
+
+# PRINT: A command to convert fax files to a printable format.
+# For printers other than Postscript or PCL you can use efix's
+# PBM output and an appropriate pbm filter (such as pbmtoepson)
+# or efix's Postsript output and Ghostscript as a filter. Change
+# the scaling (-s) and displacement (-d) options as required to
+# fit the image onto the area your printer can print.
+
+PRINT='$EFIX -ve -p$PAGEDIM -r300 -s0.98 -d0,0.125 -o$PRTYPE $f'
+
+# example using pbm utilities:
+# PRINT='$EFIX -ve -p$PAGEDIM -r60x72 -opbm $f | pbmtoepson'
+
+# example using Ghostscript:
+# PRINT='$EFIX -ve -p$PAGEDIM -r120x144 -ops $f | \
+# $GS -q -sPAPERSIZE=$PAGE -sDEVICE=epson -r120x144 \
+# -dNOPAUSE -dSAFER -sOutputFile=- - '
+
+# VIEW: A command to convert fax files to PGM format for
+# previewing. efix's pgm output reduces image dimensions by 4X.
+
+# VIEW='$EFIX -ve -p$PAGEDIM -r200 -opgm $f' # 50dpi: fast, whole-page view
+VIEW='$EFIX -ve -p$PAGEDIM -r300 -opgm $f' # 75dpi: slower, readable size
+
+# Commands to set up modem. "-iZ -i&FE&D2S7=120 -i&C0"
+# works with almost all modems. See the efax(1) man page for
+# details.
+
+INIT="-iZ -i&FE&D2S7=120 -i&C0"
+
+# Command(s) to reset modem when efax finishes. "-kZ" works in
+# almost all cases.
+
+RESET="-kZ"
+# RESET="-kZ -k&F+FCLASS=0" # for modems that stay in fax mode after reset
+
+# Speaker mode(M) and loudness(L). Mn sets speaker mode where n
+# means: 0:never, 1:until carrier, 2:always, 3:on receive only.
+
+SPKR="-iM1L0"
+
+# Options to use a particular command sets. Normally efax
+# selects the command set based on the modem's capabilities. Use
+# -o1 to force Class 1, -o2 for Class 2 and -o0 for Class 2.0.
+
+# CLASSINIT="-o1" # Class 1
+# CLASSINIT="" # Class 2
+# CLASSINIT="-o0" # Class 2.0
+
+# The modem's capabilities for sending faxes. Normally efax
+# chooses these by querying the modem. "-c 1,3,0,0,0,0,0,0"
+# forces 9600 bps maximum speed. See the efax(1) man page for a
+# description of the fields.
+
+# TXCAP="-c 1,3,0,2,0,0,0,0"
+
+# Capabilities for receiving faxes. Usually the same as TXCAP.
+# If your modem only receives at 4800 bps use "-c 1,1,0,0,0,0,0,0".
+
+# RXCAP="$TXCAP"
+
+# Additional options required only for transmit or only for
+# receive. None normally required.
+
+RXINIT=""
+TXINIT=""
+
+# Command to make a date for the page header. Protect with single
+# quotes. 'date "+%Y/%m/%d %H:%M"' works on most systems.
+
+DATECMD='date "+%Y/%m/%d %H:%M"' # YYYY/MM/DD HH:MM (24hour)
+# DATECMD='date' # longer, more readable
+
+# Page header format. You may use $DATE, $NAME, $FROM, $TO, and
+# "%d/%d" (for page number and count). Protect with single
+# quotes. Example: '$DATE $FROM $NAME p. %d/%d'.
+
+HDR='$DATE $FROM $NAME p. %d/%d'
+
+# BUSYRETRIES is a list of delays in seconds between attempts to
+# redial busy numbers. Comment out if you don't want to retry
+# busy numbers.
+
+BUSYRETRIES="30 60 120 300 60 600 60 60 1200 60 60"
+
+# FAILRETRIES is a list of delays in seconds between attempts to
+# retry failed transmissions. Retries are only attempted if at
+# least one page was sent in the previous attempt. Retries
+# include only pages not already sent. Comment out if you don't
+# want to retry failed transmissions.
+
+FAILRETRIES="300 300" # try two more times at 5 minute intervals
+
+# Command to run another program (efax) at a higher-than-normal
+# scheduling priority. This command isn't used if it fails
+# (e.g. because the current user isn't privileged). Comment this
+# out if it causes problems.
+
+NICE="nice -n -10"
+
+# Standard versions of commands that are often aliased.
+
+RM="/bin/rm -f"
+LS="/bin/ls"
+
+# Messages to display. VERB sets the messages displayed (stderr)
+# and VERBLOG the messages written to log files (stdout).
+
+VERB="ewin" # show errors, warnings, progress & negotiation
+VERBLOG="chewmainrxtf" # log everything
+
+# ****************************************************************
+# The remaining configuration options apply only to the `fax
+# answer' command. You can ignore these if you will only be
+# running efax manually. See "USING INIT TO RUN EFAX" in the
+# efax man page for more information.
+# ****************************************************************
+
+# device or file where fatal error messages should be written
+
+CONSOLE=/dev/console
+
+# The directory to store incoming faxes and log files. This directory
+# should already exist and be writable by the user(s) of this script.
+
+FAXDIR=/var/spool/fax
+LOGDIR=/var/log/fax
+
+# The strftime(3) pattern that generates the file name for
+# received files. For example, at 10:45:36 on February 25,
+# "%m%d%H%M%S" would produce 0225104536, "%j-%H%M" would produce
+# 056-1045, and %d%b%H%M 25Feb1045.
+
+ANSFNAME="%m%d%H%M%S"
+
+# umask for received files. Use 022 to allow anyone to retrieve faxes.
+
+UMASK=022
+
+# The user to be sent mail when a fax is received.
+
+FAXMGR=root
+
+# The sendmail executable including full path if necessary. Only
+# required if forwarding received faxes by e-mail in $NOTIFY.
+
+SENDMAIL=/usr/sbin/sendmail
+
+# The command to execute when a fax is received. Normally this
+# sends FAXMGR e-mail or prints the received fax. The variable
+# $f will be the name of the log file, $FILES will contain the
+# names of the received files, and $REMID will have the remote ID
+# string or '?' if none. The faxmail function will e-mail the fax
+# as MIME image/tiff attachments. Comment this out to do
+# nothing. Protect with single quotes.
+
+NOTIFY='faxmail "$REMID" "$f" $FILES | $SENDMAIL $FAXMGR'
+# NOTIFY='mail -s "fax/message from $REMID: $FILES" $FAXMGR <$f'
+# NOTIFY='lpr $f ; $FAX print $OPT $FILES'
+
+# The number of rings to wait before answering.
+
+ANSRINGS=1
+
+# If you want to enable fax/data adaptive answer (AA) read the
+# efax man page and define DATAINIT to be the options that enable
+# AA. Note: AA does not work properly on some (2400/9600) modems
+# unless the modem initialization is done at 2400 bps (not
+# possible with efax). USR modems do not support modem adaptive
+# answer (+FAE=) in Class 1. &C1 enables most modems' DCD line
+# so a signal can be sent to shells when a call is dropped. You
+# must also define DCMD (see below).
+
+DATAOPT="-j&C1 -j+FCLASS=0 -jS7=30"
+# DATAINIT="$DATAOPT -j+FAE=1" # Class 1 modem adaptive answer
+# DATAINIT="$DATAOPT -j+FAA=1" # Class 2[.0] modem adaptive answer
+# DATAINIT="$DATAOPT -oa" # software adaptive answer
+# DATAINIT="$DATAOPT" # data-only answer
+
+# If you have a voice modem and want to answer in voice mode
+# define VOICEINIT to be the options that enable voice mode. You
+# must also set VCMD below. Voice support is not yet available.
+
+# VOICEINIT="-j#CLS=8" # Rockwell voice modems
+# VOICEINIT="-jM2L2#CLS=8#VLS=4" # with speaker on
+
+# Argument to exec(2) of "/bin/sh -c" for incoming data calls.
+# This command will usually exec getty(8) but can include other
+# commands to set up the serial port, etc. Up to 6 %d arguments
+# are replaced by the baud rate following the CONNECT response
+# from the modem or 19200 if none. If using getty_ps ensure
+# /etc/gettydefs has entries for all possible %d values
+# (e.g. 19200). Use 'nice' if required to reduce any special
+# priority set by NICE.
+
+DCMD="exec /sbin/getty -h $DEV %d vt100" # for getty_ps (Linux)
+# DCMD="exec /sbin/agetty -h $DEV %d vt100" # for agetty (Linux)
+# DCMD="exec pppd $DEV %d" # start PPP server
+
+# Argument to exec(2) of "/bin/sh -c" for incoming voice calls.
+# This command will usually be a shell script that interacts with
+# the caller by using efone to play/record audio and detect DTMF
+# tones. Up to 6 %d arguments are replaced by the modem file
+# descriptor. VCMD can "exec fax reanswer" to switch to fax or
+# data mode if required.
+
+FONE=/usr/bin/fone # minimal voice mail
+VCMD="exec $FONE %d"
+
+# The owner.group and mode to which "fax answer" sets the serial
+# device. This allows non-root processes to grab the device from
+# efax even if a previous process (e.g. login) has changed it.
+# Comment out if you don't need to reset device ownership.
+
+OWNER=root.tty # typical
+MODE=666 # anybody
+# MODE=660 # only owner & group
+
+# Regular expression for efax exit codes in log files that will
+# *not* be saved. For example, use [145] to ignore exits due to
+# `locked' (1), `no modem' (4), and `signal' (5) conditions
+
+NOLOG='[145]'
+
+# ****************************************************************
+# --- End of user configuration section ---
+# ****************************************************************
+
+# --- source configuration files
+
+for f in $CONFIGFILES
+do
+ if [ -r $f ]
+ then
+ eval "`cat $f`"
+ fi
+done
+
+# --- set any variables given on command line
+
+while : ; do
+ case $# in 0) break ;; esac
+ case "$1" in [A-Z]*=*) eval $1 ; shift ;; *) break ;; esac
+done
+
+# -------- initialize
+
+ERR=0
+
+$NICE true 2>/dev/null ; case $? in 0) ;; *) NICE="" ;; esac
+
+# -------- resolve dependencies on command-line arguments
+
+eval LOCK=\"$LOCK\" # depends on DEV
+
+# make device name w/o directories
+
+case $DEV in
+ */*) DEVN=`echo $DEV|sed -e s./._.g` ;;
+ *) DEVN=$DEV ;;
+esac
+
+case $PAGE in
+ letter) PAGEDIM="$PAGE_letter" ;;
+ legal) PAGEDIM="$PAGE_legal" ;;
+ a4) PAGEDIM="$PAGE_a4" ;;
+ *) echo "Error: PAGE=\"${PAGE}\" not valid." ; exit 2 ;;
+esac
+
+# --- check for a command or alias and optional flags
+
+cmd=""
+case $0 in
+*/faxlpr|faxlpr) cmd=faxlpr ;;
+*)
+ while : ; do
+ case $# in 0) case $cmd in '') cmd=receive ;; esac ; break ;; esac
+ case $1 in
+ -l) OPT="$OPT -l" ; RES=204x98 ; shift ;;
+ -h) OPT="$OPT -h" ; RES=204x196 ; shift ;;
+ -v) OPT="$OPT -v" ; VERB=$VERBLOG ; shift ;;
+ *)
+ case $cmd in '') cmd=$1 ; shift ;; *) break ;; esac
+ ;;
+ esac
+ done
+;;
+esac
+
+# -------- functions
+
+faxmail () {
+ echo "Subject: fax/message from $1"
+ shift
+ echo "Content-Type: multipart/mixed; boundary=EFAX_MAIL"
+ echo ""
+ echo "--EFAX_MAIL"
+ echo "Content-Type: text/plain; charset=\"us-ascii\""
+ echo "Content-Transfer-Encoding: 7bit"
+ echo ""
+ cat $1
+ shift
+ for f in $*
+ do
+ echo "--EFAX_MAIL"
+ echo "Content-Type: image/tiff"
+ echo "Content-Transfer-Encoding: base64"
+ echo ""
+ $EFIX -M <$f
+ done
+}
+
+# -------- export variables for fone script
+
+export DEV TSTAMP
+
+# -------- do the appropriate command
+
+while : ; do # so we can use `break' to get to the end of the script
+
+case $cmd in
+
+# fax answer : clean up logs and exec efax. normally run by init(8).
+
+ answer)
+
+ if cd $FAXDIR ; then :
+ else
+ echo "Error: $FAX cannot cd to $FAXDIR" >>$CONSOLE
+ sleep 30
+ break
+ fi
+
+ while [ -f ${DEVN}.stop ] ; do sleep 15 ; done
+
+ umask $UMASK
+ case $OWNER in '') ;; *) chown $OWNER /dev/$DEV ;; esac
+ case $MODE in '') ;; *) chmod $MODE /dev/$DEV ;; esac
+
+ for f in ${DEVN}.[0-9]* # clean up old log files
+ do
+ egrep "done, returning $NOLOG|exec'ing" $f >/dev/null 2>/dev/null
+ case $? in
+ 0)
+ $RM $f
+ ;;
+ 1) FILES=`sed -n -e '/received ->/s/^.*-> \(.*\)$/\1/p' $f`
+ FILES=`echo $FILES`
+ REMID=`sed -n -e '/remote ID ->/s/^.*-> \(.*\)$/\1/p' \
+ -e tok -e b -e ':ok' -e q $f`
+ case $REMID in '') REMID='?' ;; esac
+ eval $NOTIFY
+ echo >>${LOGDIR}/${DEVN}.log
+ cat $f >>${LOGDIR}/${DEVN}.log
+ $RM $f
+ ;;
+ esac
+ done
+
+ exec $NICE $EFAX -v "" -v "$VERBLOG" -d/dev/$DEV $INIT $SPKR \
+ $CLASSINIT $FCINIT $RXINIT $LOCK \
+ $RXCAP -l "$FROM" $RESET \
+ $DATAINIT -g "$DCMD" $VOICEINIT -e "$VCMD" \
+ -jS0=$ANSRINGS -w -s -r "$ANSFNAME" 2>$CONSOLE >${DEVN}.$$
+
+ echo ERROR: $FAX answer exec failed >>$CONSOLE ; sleep 30
+
+ break
+ ;;
+
+
+# fax reanswer : switch from voice mode to fax[/data] mode
+
+ reanswer)
+
+ # we should already be in the fax spool directory, the
+ # device locked, the modem answered and initialized in
+ # voice mode and stdout/stderr redirected appropriately
+
+ umask $UMASK
+
+ exec $NICE $EFAX -v "" -v "$VERBLOG" -d/dev/$DEV '-i#CLS=0' \
+ $CLASSINIT $FCINIT $RXINIT \
+ $RXCAP -l "$FROM" $RESET \
+ $DATAINIT -g "$DCMD" \
+ -r "$ANSFNAME"
+
+ echo ERROR: $FAX reanswer exec failed >>$CONSOLE ; sleep 30
+
+ break
+ ;;
+
+# fax queue : list received fax files
+
+ q*)
+
+ cd $FAXDIR
+ case $? in 0) ;; *) echo "cannot cd to $FAXDIR" ; break ;; esac
+
+ for f in [0-9]*.[0-9][0-9][0-9] [0-9]*.v
+ do
+ if [ -r $f ]
+ then
+ echo
+ echo Fax files in `pwd` :
+ echo
+ $LS -l [0-9]*.[0-9][0-9][0-9] [0-9]*.v
+ echo
+ break
+ fi
+ done
+
+ break
+ ;;
+
+# faxlpr : get phone number and user from current cf* file and run fax send
+
+ faxlpr)
+
+ cd $FAXDIR # the lpr spool directory for printer 'fax'
+ case $? in 0) ;; *) echo "$0: cannot cd to $FAXDIR" ; break ;; esac
+
+ test -r lock
+ case $? in 0) ;; *) echo "$0: can't read lock file" ; break ;; esac
+
+ cfile=`tail -1 lock`
+
+ test -r $cfile
+ case $? in 0) ;; *) echo "$0: can't read control file" ; break ;; esac
+
+ cfile=`cat $cfile`
+
+ num=` echo "$cfile" | sed -e /^[^J]/d -e s/.//`
+ host=`echo "$cfile" | sed -e /^[^H]/d -e s/.//`
+ user=`echo "$cfile" | sed -e /^[^P]/d -e s/.//`
+
+ test "$num"
+ case $? in 0) ;; *) echo "$0: can't read phone number" ; break ;; esac
+
+ cat - >> fax$$ # save in a file
+
+ l=`$FAX send "$num" fax$$`
+
+ case $? in
+ 0) echo "$l" | mail -s "fax to $num succeeded" $user@$host ;;
+ *) echo "$l" | mail -s "fax to $num failed " $user@$host ;;
+ esac
+
+ $RM fax$$ fax$$.???
+
+ break
+ ;;
+
+# fax start/stop/status : manage fax receive daemon
+
+ start|stop|st*) # common section
+
+ cd $FAXDIR ;
+ case $? in 0) ;; *) echo "cannot cd to $FAXDIR" ; break ;; esac
+
+ n= ; for f in ${DEVN}.[0-9]* ; do logfile="$f" ; n=x$n ; done
+
+ case $n in
+ xx*) echo Warning: multiple logs for $DEV : ; ls ${DEVN}.[0-9]* ;;
+ esac
+
+ case $logfile in
+ *\*) echo no fax answer process for device $DEV ; break ;;
+ esac
+
+ efaxpid=`echo $logfile | sed -e "s/${DEVN}\.//g"`
+
+ case $cmd in
+
+# fax start - remove stop file so fax answer will continue
+
+ start)
+
+ if [ ! -w . ] ; then echo "can't write `pwd`" ; break ; fi
+ $RM ${DEVN}.stop
+ break
+ ;;
+
+# fax stop - make a stop file and kill current fax answer daemon
+
+ stop)
+
+ if [ ! -w . ] ; then echo "can't write `pwd`" ; break ; fi
+ touch ${DEVN}.stop
+ echo stopping fax daemon for ${DEV}, pid=$efaxpid
+ kill -HUP $efaxpid
+ break
+ ;;
+
+
+# fax status - display pid and log file for current daemon
+
+ st*)
+
+ if [ -f ${DEVN}.stop ] ; then stat="(set to stop)" ; fi
+
+ if ps -u $efaxpid 2>/dev/null ; then :
+ else
+ echo "NOT ACTIVE (last daemon was $efaxpid)"
+ fi
+
+ echo
+ echo from: $FAXDIR/$logfile
+ echo
+
+ egrep "Warning|Error|starts|activity|opened|received -|done" $logfile
+
+ case $# in
+ 0) ;; *) echo "---------------" ; sleep $1 ; exec $FAX status $1 ;;
+ esac
+
+ break
+ ;;
+
+ esac # common section
+ ;;
+
+# fax makefont : rasterize a PS font into a 256-character-wide bitmap
+
+ makefont)
+
+ if [ $# -lt 5 ]
+ then
+ echo Usage: fax makefont fontname fontsize \
+ cellwidth cellheight filename
+ echo "(cellwidth and cellheight in pixels, fontsize in points)"
+ echo "Example: fax makefont Courier-Bold 8 16 24 efaxfont"
+ echo "will make an 8pt font (there are about 3 pixels per pt)"
+ exit 1
+ fi
+
+ FNTFMT=pbmraw # format for font files
+ # FNTFMT=tiffg3 # smaller, available with Ghostscript 3.x or later
+
+ pelwidth=`expr 256 \* $3`
+ gs -q -sDEVICE=$FNTFMT -r204x196 -g${pelwidth}x$4 \
+ -sOutputFile=$5 - <<EOF
+ /$1 findfont $2 scalefont setfont
+ /buf 1 string def
+ /dx $3 204 div 72 mul def % x offset per character
+ 0 1 255 {
+ dup dx mul 0.7 add 3 moveto
+ buf exch 0 exch put
+ buf show
+ } for
+ showpage
+EOF
+ break
+ ;;
+
+# fax make : convert a text or Postscript file to fax format
+
+ m*)
+
+ case $# in 0) echo "No files specified" ; ERR=2 ; break ;; esac
+
+ if [ ! -r $1 ] ; then echo "Can't read $1" ; ERR=2 ; break ; fi
+
+
+ read x <$1
+ case $x in
+ %!*|?%!*)
+ echo "$1 is postscript..."
+ # GS can't deal with long paths so we 'cd'
+ DIRNAME=`dirname $1` ; BASENAME=`basename $1`
+ ( cd $DIRNAME ; \
+ $GS -q -sDEVICE=tiffg3 -r$RES -dNOPAUSE -dSAFER \
+ -sOutputFile=$BASENAME.%03d \
+ -sPAPERSIZE=$PAGE \
+ $BASENAME </dev/null >/dev/null )
+ ;;
+ II*|MM*|P4*)
+ echo "$1 is an image file..."
+ $EFIX -ve -otiffg3 -p$PAGEDIM -r$RES -n $1.%03d $1
+ ;;
+ *) echo "$1 is text..."
+ $EFIX -ve -otiffg3 -p$PAGEDIM -r$RES -n $1.%03d $TEXTFONT $1
+ ;;
+ esac
+
+ break
+ ;;
+
+# fax send : fax files to given number, converting first if necessary
+
+ s*)
+
+ case $# in
+ 0) echo "missing phone number to call" ; ERR=2 ; break ;;
+ esac
+
+ # look up names
+
+ case $1 in
+ [A-Za-z]*)
+ for f in $DIRFILES ; do
+ if [ -r $f ] ; then TELNO=`$LOOKUP` ; fi
+ case "$TELNO" in '') continue ;; *) break ;; esac
+ done
+ case "$TELNO" in
+ '') echo "Name lookup for $1 failed" ; ERR=2 ; break ;;
+ *) echo "Lookup: $1 = $TELNO" ;;
+ esac
+ ;;
+ *) TELNO="$1" ;;
+ esac
+
+ shift
+
+ case "$TO" in '') TO="$TELNO" ;; *) ;; esac
+
+ TELNO=`echo $TELNO|sed "s/[ ()][ ()]*//g"`
+
+ # handle manual dialing and number->dial string conversions
+
+ case "$TELNO" in
+ -m*) MANINIT="-jX3" ; TELNO="" ;;
+ +*) TELNO=`echo $TELNO | $TELCVT` ;;
+ esac
+
+ case $TELNO in
+ '') ;;
+ *) TELNO="${DIALPREFIX}${TELNO}${DIALSUFFIX}" ;;
+ esac
+
+ # use `fax make' to convert files if they need to be updated
+
+ FILES=""
+ for f in $* ; do
+ case $f in -) FILES="$FILES -" ; continue ;; esac
+ if [ ! -r $f ] ; then
+ echo "can't read file $f" ; ERR=2 ; break 2
+ fi
+ case $f in
+ *.[0-9][0-9][0-9]) FILES="$FILES $f" ;; # skip image files
+ *) if echo ${f}.001: $f \; x | make -r -q -f - ; then
+ echo ${f}.nnn is up-to-date
+ else
+ $RM ${f}.[0-9][0-9][0-9]
+ $FAX make $OPT $f
+ fi
+ if [ -r $f.001 ] ; then
+ FILES="$FILES $f.[0-9][0-9][0-9]"
+ else # something's wrong, catch it later
+ FILES="$FILES $f.001"
+ fi
+ ;;
+ esac
+ done
+
+ # check that all files are OK
+
+ for f in $FILES ; do
+ case $f in -) continue ;; esac
+ if [ ! -r $f ] ; then
+ echo "can't read file $f" ; ERR=2 ; break 2
+ fi
+ done
+
+ # send it
+
+ for s in 0 $FAILRETRIES ; do
+
+ case $s in
+ 0) ;; *) echo "Will try again in $s seconds" ; sleep $s ;;
+ esac
+
+# logfile=`$TSTAMP`.log
+ logfile="$TELNO".log
+
+ for t in 0 $BUSYRETRIES ; do
+
+ case $t in
+ 0) ;; *) echo "Will try again in $t seconds" ; sleep $t ;;
+ esac
+
+ DATE=`eval "$DATECMD"`
+ eval HDR=\"$HDR\"
+
+ $NICE $EFAX -v "$VERB" -v "$VERBLOG" \
+ -d/dev/$DEV $LOCK $INIT $SPKR \
+ $CLASSINIT $FCINIT $TXINIT \
+ $TXCAP -l "$FROM" $RESET $HDRFONT -h "$HDR" \
+ $MANINIT -t "$TELNO" $FILES >$logfile
+
+ ERR=$?
+
+ case $ERR in
+ 0) $RM $logfile ; break 2 ;;
+ 1) echo Busy... ;;
+ *) echo "There were errors (see ${logfile})." ; break ;;
+ esac
+
+ done
+
+ SENT=` sed -n -e '/sent ->/s/^.*-> \([^ ]*\).*/\1/p' $logfile`
+ FILES=`sed -n -e '/failed ->/s/^.*-> \([^ ]*\).*/\1/p' $logfile`
+ case $SENT in '') break ;; esac
+ case $FILES in '') break ;; esac
+ echo Failed...
+
+ done
+
+ break
+ ;;
+
+# fax hangup : hang up the phone
+
+ hangup*)
+ $NICE exec $EFAX -v $VERB -d/dev/$DEV $LOCK -iZ -T
+ break
+ ;;
+
+# fax fone : open modem device and exec fone script
+
+ fone)
+
+ cd $FAXDIR
+ case $? in 0) ;; *) echo "cannot cd to $FAXDIR" ; break ;; esac
+
+ $NICE exec $EFAX -v $VERB -d/dev/$DEV $LOCK $INIT \
+ -j#CLS=8 -a#VLS=1 -e "$VCMD $*"
+
+ break
+ ;;
+
+# fax (rm|print|view) commands possibly on files in spool directory
+
+ rm|p*|v*) # common code
+
+ # switch to spool directory if first file is found there
+
+ for f in $FAXDIR/$1 ; do
+ if test -r $f ; then cd $FAXDIR ; break ; fi
+ done
+
+ for f in $* ; do
+ case $cmd in
+
+ rm) # fax rm : delete files
+ if $RM $f ; then echo deleted $f ; fi
+ ;;
+
+ p*) # fax print : print files
+ echo "$f ... "
+ eval "$PRINT | $PRCMD"
+ ;;
+
+ v*) # fax view : display images
+ echo "$f ... "
+ eval "$VIEW | $VIEWCMD"
+ ;;
+
+ esac
+ done
+
+ break
+ ;;
+
+# fax [receive] : answer phone now and receive fax files
+
+ r*)
+
+ case $1 in '') file=`$TSTAMP` ;; *) file=$1 ;; esac
+ logfile=${file}.log
+
+ $NICE $EFAX -v "$VERB" -v "$VERBLOG" -d/dev/$DEV $LOCK $INIT $SPKR \
+ $CLASSINIT $FCINIT $RXINIT \
+ $RXCAP -l "$FROM" $RESET \
+ -r $file >$logfile
+
+ ERR=$?
+
+ case $ERR in
+ 0) $RM $logfile ; break ;;
+ 1) echo Busy... ;;
+ *) echo "There were errors (see ${logfile})." ; break ;;
+ esac
+
+ break
+ ;;
+
+# fax new : create a cover page for a fax (needs work)
+
+ new)
+
+ fname=${1-new.fax}
+ DATE=`date "+%B %d %Y"`
+ cat >$fname 2>/dev/null << EOF
+
+ ________________________________________________
+
+ FAX COVER PAGE
+ ________________________________________________
+
+ To: x
+ fax:
+
+ ________________________________________________
+
+ From: $NAME
+ fax: $FROM
+
+ Date: $DATE
+
+ Pages: 1 (including this page)
+
+ ________________________________________________
+
+EOF
+ ${VISUAL-${EDITOR-vi}} $fname
+
+ break
+ ;;
+
+# fax help : show command arguments
+
+ -\?|\?|-h*|h*)
+
+cat 1>&2 <<EOF
+ Usage:
+
+ fax [ r[eceive] [-v] [filename-prefix] ]
+ fax m[ake] [-l] { postscript-file-name | text-file-name }
+ fax s[end] [-l] [-v] { -m | telephone-number } filename...
+ fax { p[rint] | v[iew] | rm } [-l] filename...
+
+ fax [ stop | start | st[atus] | q[ueue] ]
+ fax answer
+
+ use -l to create low resolution (98 lpi) faxes
+ use -m if the number has been dialed manually
+ use -v for verbose output
+
+ if given no arguments, answers the phone and receives a fax.
+
+ use VAR=value to set variables (e.g. "fax DEV=cua2 receive")
+
+ session logs are written to a file with the date/time as the
+ file name and extension of .log (except for automatic reception).
+
+ fax device is /dev/$DEV, incoming spool directory is $FAXDIR
+
+EOF
+
+ ERR=1
+ break
+ ;;
+
+ *)
+ echo "Error. Invalid command ($cmd)" ; ERR=2 ; break
+ ;;
+
+esac
+done
+
+exit $ERR
--- /dev/null
+.TH FAX 1 "May 1996"
+.UC 1
+.SH NAME
+fax \- make, send, receive, view or print a fax
+.SH SYNOPSIS
+
+.de ML
+\fR[\fB-l\fR]
+..
+
+.de MV
+\fR[\fB-v\fR]
+..
+
+.de MU
+\fR[\fIunits\fR]
+..
+
+.de MF
+\fIfilename\fR...
+..
+
+.B fax
+.B help
+
+.B fax
+.B make
+.ML
+.I file
+
+.B fax
+.B send
+.ML
+.MV
+{
+.B -m
+|
+.I number
+}
+.MF
+
+.B fax
+[
+\fBreceive\fR
+.MV
+[
+.I filename-prefix
+]
+]
+
+.B fax
+{
+.B print
+|
+.B view
+|
+.B rm
+}
+.MF
+
+.B fax
+\fR{\fB queue \fR|\fB status \fR[\fIt\fR] | \fB start \fR|\fB stop \fR}\fR
+
+.B fax
+.B answer
+
+.SH OPTIONS
+
+.TP 9
+.B -l
+use low (96 line per inch) resolution
+.TP 9
+.B -v
+display verbose messages for debugging
+.TP 9
+.B -m
+the phone call has already been dialed manually
+
+.PP
+
+The commands make, send, receive, view and queue may be
+abbreviated to their first characters (e.g. ``fax q'').
+
+Assignments of the form \fIVARIABLE\fB=\fIvalue\fR may appear
+before the command name to temporarily change the values of most
+fax script variables (e.g. ``fax PAGE=A4 print letter.001'')
+
+
+.SH DESCRIPTION
+
+\fBfax\fP provides a simple user interface to the efax(1) and
+efix(1) programs. It allows you to send text or Postscript files
+as faxes and receive, print or preview received faxes. The
+\fBfax help\fP command prints a summary of the possible commands.
+
+To send a fax, the original files need to be converted from ASCII
+or Postscript into a particular bit-map format (TIFF with Group 3
+encoding). This can be done automatically by the \fBfax send\fP
+command or you can use the \fBfax make\fP command to do the
+conversion before sending the fax. The conversion will create
+one file per page. These files will have the name of the
+original file with the page number as an additional suffix. For
+example, running \fBfax make doc.ps\fP on the two-page postscript
+file doc.ps would generate the files doc.ps.001 and doc.ps.002.
+
+When sending a fax with the \fBfax send\fP command you may dial
+the number manually and use the \fB-m\fP option or you may give
+the phone number on the command line. The names of the files to
+be sent are given on the command line, usually by using
+wildcards. For example, to send a multi-page fax consisting of
+the files doc.ps.001, doc.ps.002, and so on, you could use the
+command \fBfax send 555-1212 doc.ps.0*\fP (if you had already run
+the \fBfax make\fP command) or simply \fBfax send 555-1212
+doc.ps\fP. If the number is busy the script will wait and try
+again.
+
+Use the \fBfax receive\fP command to answer the phone and receive
+a fax. If a file name is specified the received fax will be
+stored in files with the given file name plus an extension equal
+to the page number. If no options are given, the received fax
+will be stored in files having a name given by the date and time
+and an extension equal to the page number. For example, a fax
+received beginning on July 4 at 3:05:20 pm will generate files
+0704150520.001, 0704150520.002, and so on.
+
+The \fBfax print\fP, \fBfax view\fP, and \fBfax rm\fP commands
+are used to print, preview or remove received fax files. As with
+the send command the file names are usually given using
+wildcards.
+
+If efax has been installed for automatic fax reception you can
+use the \fBfax queue\fP command to check for files in the
+incoming spool directory. The fax script can also be configured
+to print received faxes or e-mail them as MIME attachments with
+type image/tiff-f. For convenience the \fBfax print\fP,
+\fBview\fP and \fBrm\fP commands will first check for the named
+files in this spool directory. The \fBfax status\fP command
+shows the status of the automatic receive process once, or every
+\fIt\fP seconds. Privileged users can use the \fBfax stop\fP and
+\fBfax start\fP commands to stop and restart the fax reception
+daemon.
+
+The \fBfax answer\fP command is used for unattended reception of
+faxes. It is normally placed in the inittab(5) or ttytab(5) file
+and is run automatically by init(8).
+
+The \fB-v\fP option displays verbose messages.
+
+Other features of the fax script are documented within the
+script:
+
+.TP 3
+.B -
+a directory that lets you specify recipients by name instead of
+number
+
+.TP 3
+.B -
+the \fBfax new\fP command to create a simple cover page and start
+up a text editor
+
+.TP 3
+.B -
+the \fBfax makefont\fP command converts a Postscript font to a
+bit-mapped font for use in headers or text
+
+.SH RESOLUTION
+
+Faxes can be created at low (98 lines per inch) or high (196 lpi)
+resolution. Almost all fax machines will operate at either
+resolution. By default files are created at high resolution but
+you can use the optional \fB-l\fP argument to create files at low
+resolution.
+
+.SH SESSION LOGS
+
+The modem commands and responses together with status and error
+messages are written to file. If the fax is successfully sent or
+received the log file is removed. Otherwise a message is printed
+showing the log file name. Please send a copy of this file when
+reporting problems with efax.
+
+.SH FILES
+
+The fax script will `source' the optional shell scripts
+\fB/etc/efax.rc\fP, \fB~/.efaxrc\fP and/or \fB./.efaxrc\fP before
+processing command-line arguments. These files can be used to
+set script variables to custom values for a particular system,
+user and/or directory.
+
+The following files are created in the FAXDIR spool directory
+when automatic fax reception is enabled (see the fax script).
+DEV represents the name of the fax modem device file in /dev
+(e.g. cua1 for /dev/cua1).
+
+.TP 10
+DEV.\fIn\fP
+the log file created by the fax answer daemon with process id
+\fIn\fP
+.TP 10
+DEV.log
+contains collected log files for device DEV. Log files showing a
+termination status of 1 (device busy) or 4 (no response from
+modem) are not added to this file.
+.TP 10
+DEV.stop
+created by the fax stop command to prevent the fax daemon from
+starting up.
+
+.SH AUTHOR
+
+Fax was written by Ed Casas. Please send comments or bug reports
+to edc@cce.com. Please describe the type of modem used and
+include a copy of the log file.
+
+.SH COPYRIGHT
+
+Fax is copyright 1993 -- 1999 by Ed Casas. It may be used,
+copied and modified under the terms of the GNU Public License.
+
+.SH DISCLAIMER
+
+Although \fBfax\fP has been tested, it may have errors that will
+prevent it from working correctly on your system. Some of these
+errors may cause serious problems including loss of data and
+interruptions to telephone service.
+
+.SH SEE ALSO
+
+.BR efax(1),
+.BR efix(1),
+.BR ghostscript(1).
+
+.SH BUGS
+
+See efax(1).