ftp://ftp.metalab.unc.edu/pub/Linux/apps/serialcomm/fax/efax-0.9.tar.gz master bp_uc orig0_9
authorshort <>
Sun, 14 Apr 2002 21:32:16 +0000 (21:32 +0000)
committershort <>
Sun, 14 Apr 2002 21:32:16 +0000 (21:32 +0000)
md5sum: 23bd3767f87c455c58ccae7f88bce725
size: 96736

17 files changed:
COPYING [new file with mode: 0644]
Makefile [new file with mode: 0644]
README [new file with mode: 0644]
efax.1 [new file with mode: 0644]
efax.c [new file with mode: 0644]
efaxio.c [new file with mode: 0644]
efaxio.h [new file with mode: 0644]
efaxlib.c [new file with mode: 0644]
efaxlib.h [new file with mode: 0644]
efaxmsg.c [new file with mode: 0644]
efaxmsg.h [new file with mode: 0644]
efaxos.c [new file with mode: 0644]
efaxos.h [new file with mode: 0644]
efix.1 [new file with mode: 0644]
efix.c [new file with mode: 0644]
fax [new file with mode: 0755]
fax.1 [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..60549be
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+                   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.
diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..ffefde8
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,46 @@
+# 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
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..07c107d
--- /dev/null
+++ b/README
@@ -0,0 +1,84 @@
+
+                         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.
diff --git a/efax.1 b/efax.1
new file mode 100644 (file)
index 0000000..8b61c48
--- /dev/null
+++ b/efax.1
@@ -0,0 +1,1289 @@
+.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.
diff --git a/efax.c b/efax.c
new file mode 100644 (file)
index 0000000..42880d2
--- /dev/null
+++ b/efax.c
@@ -0,0 +1,2354 @@
+#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 ) ;
+}
diff --git a/efaxio.c b/efaxio.c
new file mode 100644 (file)
index 0000000..5b8dbde
--- /dev/null
+++ b/efaxio.c
@@ -0,0 +1,428 @@
+#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 ;
+}
diff --git a/efaxio.h b/efaxio.h
new file mode 100644 (file)
index 0000000..257b8df
--- /dev/null
+++ b/efaxio.h
@@ -0,0 +1,54 @@
+#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
diff --git a/efaxlib.c b/efaxlib.c
new file mode 100644 (file)
index 0000000..4887cd9
--- /dev/null
+++ b/efaxlib.c
@@ -0,0 +1,2306 @@
+/* 
+             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
+} ;
diff --git a/efaxlib.h b/efaxlib.h
new file mode 100644 (file)
index 0000000..9c725ae
--- /dev/null
+++ b/efaxlib.h
@@ -0,0 +1,218 @@
+#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
diff --git a/efaxmsg.c b/efaxmsg.c
new file mode 100644 (file)
index 0000000..068f5f1
--- /dev/null
+++ b/efaxmsg.c
@@ -0,0 +1,180 @@
+#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) ;
+}
+
diff --git a/efaxmsg.h b/efaxmsg.h
new file mode 100644 (file)
index 0000000..d148bbb
--- /dev/null
+++ b/efaxmsg.h
@@ -0,0 +1,31 @@
+#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
+
+
diff --git a/efaxos.c b/efaxos.c
new file mode 100644 (file)
index 0000000..6b346d6
--- /dev/null
+++ b/efaxos.c
@@ -0,0 +1,510 @@
+/* 
+               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 ;
+}
+
diff --git a/efaxos.h b/efaxos.h
new file mode 100644 (file)
index 0000000..8696993
--- /dev/null
+++ b/efaxos.h
@@ -0,0 +1,86 @@
+#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
diff --git a/efix.1 b/efix.1
new file mode 100644 (file)
index 0000000..6d0bffb
--- /dev/null
+++ b/efix.1
@@ -0,0 +1,264 @@
+.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).
+
diff --git a/efix.c b/efix.c
new file mode 100644 (file)
index 0000000..57f96ff
--- /dev/null
+++ b/efix.c
@@ -0,0 +1,447 @@
+#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 ;
+}
diff --git a/fax b/fax
new file mode 100755 (executable)
index 0000000..b8ea77a
--- /dev/null
+++ b/fax
@@ -0,0 +1,1040 @@
+#!/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
diff --git a/fax.1 b/fax.1
new file mode 100644 (file)
index 0000000..c4b7b3b
--- /dev/null
+++ b/fax.1
@@ -0,0 +1,236 @@
+.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).