efax-0.9a-001114/0040755000076400007640000000000007204272336011263 5ustar edcedcefax-0.9a-001114/README0100644000076400007640000000560606701171657012154 0ustar edcedc 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. efax-0.9a-001114/COPYING0100644000076400007640000004312706663446724012336 0ustar edcedc 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. 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.) 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. 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. 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 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. Copyright (C) 19yy 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. , 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. efax-0.9a-001114/Makefile0100644000076400007640000000240307126526762012727 0ustar edcedc# 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) chmod 755 $(BINDIR)/fax $(BINDIR)/efax $(BINDIR)/efix cp fax.1 efax.1 efix.1 $(MANDIR)/man1 chmod 644 $(MANDIR)/man1/fax.1 $(MANDIR)/man1/efax.1 \ $(MANDIR)/man1/efix.1 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 efax-0.9a-001114/efax.c0100644000076400007640000017571007204272301012352 0ustar edcedc#define Copyright "Copyright 1999 Ed Casas" #define Version "efax v 0.9a-001114" /* 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. */ 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 /* ANSI C */ #include #include #include #include #include #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 TCFSECS 1.5 /* TCF duration (seconds, nominally 1.5) */ #define TMOD 55 /* T.30 pause between v.21&v.29, 75-20 ms */ #define MODDLY "5" /* same as above, a string */ #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 51 /* per data character (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 160 /* Class 2 end of data rx (4 retrans x 4 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 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 DCSLEN ? DCSLEN : len ; fif[0] = 0 ; fif[1] = ( isdis && t4tx ? 0x80 : 0 ) | 0x40 ; for ( i=2 ; icaptodis : 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 ; ibyte >= 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 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 */ p = buf ; for ( i=0 ; i<32 ; i++ ) { p = putcode ( &e, 0, 8, p ) ; } 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, 1 on DLE-ETX without RTC, 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 ) ; 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 */ } else { err = 1 ; /* DLE-ETX without RTC - should try again */ } 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 } ; #ifdef ADDTCFRTC ENCODER e ; uchar *p ; #endif ckcmd ( f, &err, s, TO_FT, CONNECT ) ; if ( ! err ) { ttymode ( f, SEND ) ; /* send n bytes of zeros */ for ( i=0 ; i < n ; i += m ) { m = n-i < MINWRITE ? n-i : MINWRITE ; sendbuf ( f, buf, m, 0 ) ; } #ifdef ADDTCFRTC /* append RTC in case modem is looking for it */ newENCODER ( &e ) ; p = buf ; for ( i=0 ; i < RTCEOL ; i++ ) p = putcode ( &e, EOLCODE, EOLBITS, p ) ; p = putcode ( &e, 0, 0, p ) ; sendbuf ( f, buf, p - buf, 0 ) ; #endif tput ( f, DLE_ETX, 2 ) ; ckcmd ( f, &err, 0, TO_DRAIN_D, OK ) ; msg ( "I- sent TCF - channel check of %d bytes", n ) ; ttymode ( f, COMMAND ) ; } 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= 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 ) ; logfr ( "received", frname ( buf [ 2 ] ), buf, frlen ) ; if ( ! err ) { frlen = fixframe ( buf, frlen, mf ) ; 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 ) ; #ifdef USEFTS if ( cmd ( mf, "+FTS=" MODDLY, T3S ) != OK ) #endif msleep ( TMOD ) ; /* if +FTS not supported */ if ( ! err ) err = puttrain ( mf, c1cmd[SND][TRN][session[BR]], TCFSECS*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 ) { msleep ( 1000 ) ; err = send_data ( mf, inf, page, pages, local, session, header, font ) ; } pagetry++ ; if ( !err ) err = end_data ( mf, session, 0, 0 ) ; #ifdef USEFTS if ( cmd ( mf, "+FTS=" MODDLY, T3S ) != OK ) #endif 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 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]]; nerr = 0 ; re_getdata: if ( cmd ( mf, c1cmd [RCV][DTA][session[BR]], TO_FT ) != CONNECT ) goto F ; /* +FCERROR -> DCS resent */ switch ( receive_data ( mf, outf, session, &nerr ) ) { case 0: good = nerr < maxpgerr ; msg ( "I-received -> %s", outf->cfname ) ; writepending=1 ; /* ppm follows immediately, don't write yet */ rxpage++ ; break ; case 1: /* no RTC, re-issue +FRM command */ goto re_getdata ; default: good = 0 ; break ; } 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]]; nerr = 0 ; 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, page+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,0", t, OK ) ; ckcmd ( mf, 0, "+FLO=1", t, OK ) ; ckcmd ( mf, 0, "+FBO=0", 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] = "" ; setlocale ( LC_ALL, "" ) ; 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 /* ANSI C */ #include #include #include #include #include #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] ; setlocale ( LC_ALL, "" ) ; /* 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 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 ( ! ifile.lines || ( 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 ; } efax-0.9a-001114/efaxlib.h0100644000076400007640000001447307047350752013060 0ustar edcedc#ifndef _EFAXLIB_H #define _EFAXLIB_H #include #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 */ uchar black_is_zero ; /* black is encoded as zero */ } 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 efax-0.9a-001114/efaxlib.c0100644000076400007640000017255707047433760013064 0ustar edcedc/* efaxlib.c - utility routines for efax Copyright 1995 Ed Casas */ #include #include #include #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 ; } /* Invert a run-length buffer by prepending or removing a zero-length initial run. */ int xinvert ( short *p, int nr ) { int i ; if ( ! *p ) { for ( i=0 ; i0 ; i-- ) { /* insert */ p[i] = p[i-1] ; } p[0] = 0 ; nr++ ; } return nr ; } /* 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->page->black_is_zero ) { /* invert */ nr = xinvert ( runs, nr ) ; } 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 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 ; p->black_is_zero = 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 262 : /* photometric interpretation */ f->page->black_is_zero = tv ; 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 ; jpslines ; j++ ) ; if ( j == n ) { /* repeat line */ hexputc ( f->f, 0 ) ; l=i=n ; } while ( ipslines ; 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 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 ; ie, 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 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 } ; efax-0.9a-001114/efaxmsg.h0100644000076400007640000000124106663153522013064 0ustar edcedc#ifndef _EFAXMSG_H #define _EFAXMSG_H #include #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 efax-0.9a-001114/efaxmsg.c0100644000076400007640000001051706674623074013073 0ustar edcedc#include /* ANSI C */ #include #include #include #include #include #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 */ "","","","", "","","","", "", "", "", "", "", "", "", "", "","","","","","","","", "","", "","", "", "", "", "" } ; 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= 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) ; } efax-0.9a-001114/efaxio.h0100644000076400007640000000345606666701021012713 0ustar edcedc#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 efax-0.9a-001114/efaxio.c0100644000076400007640000002660206702155640012705 0ustar edcedc#include /* ANSI C */ #include #include #include #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: AX... 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 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= 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 : ttymode ( f, VOICECOMMAND ) ; break ; 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 ; } efax-0.9a-001114/efaxos.h0100644000076400007640000000454406663153522012730 0ustar edcedc#ifndef _EFAXOS_H #define _EFAXOS_H #include #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 efax-0.9a-001114/efaxos.c0100644000076400007640000002761206666701266012733 0ustar edcedc/* efaxos.c - O/S-dependent routines Copyright 1995, Ed Casas */ #include #include #include #include #include #include #include #include #include #include #ifndef FD_SET #include /* for AIX */ #endif #include "efaxlib.h" #include "efaxmsg.h" #include "efaxos.h" #ifdef USE_TERMIO #include #include #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 #endif #ifdef TIOCSSOFTCAR #include #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 ; ihwfc ? 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 ; } efax-0.9a-001114/fax.10100644000076400007640000001437106667527734012147 0ustar edcedc.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). efax-0.9a-001114/efax.10100644000076400007640000011734207055247702012300 0ustar edcedc.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 , 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 &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 $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 -s /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=). Error messages will appear on /dev/console. (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 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 (
)" 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. efax-0.9a-001114/efix.10100644000076400007640000001371407054671122012302 0ustar edcedc.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 used to transform images before combining them. For example, the commands .RS .nf .ft CW efix -d4,8 signature >sig.fax efix -O sig.fax letterhead >letterhead.fax efix -O letterhead.fax 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). efax-0.9a-001114/fax0100755000076400007640000006727707166113120011777 0ustar edcedc#!/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&I0&R1&D3I4' # USR (Courier, Sportster) # FCINIT='-or' # Multi-Tech (for bit reversal) # FCINIT="$FCINIT -j+FBO=0" # USR modems using Class 2.0 # **************************************************************** # 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 -iE0&D2S7=120 -i&C0" # works with almost all modems. See the efax(1) man page for # details. INIT="-iZ -i&FE0&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. # Class 2 is not the same as 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 | $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=4 # 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 . $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 $REMID : $FILES" echo "Mime-Version: 1.0" 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 $f for f in $FILES do echo "--EFAX_MAIL" echo "Content-Type: image/tiff; name=\"$f.tiff\"" echo "Content-Transfer-Encoding: base64" echo "Content-Disposition: attachment; filename=\"$f.tiff\"" echo "" $EFIX -M <$f echo "--EFAX_MAIL--" done echo "--EFAX_MAIL--" } # -------- 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 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 || \ echo "Error: $FAX cannot save log files" >>$CONSOLE ;; esac done while [ -f ${DEVN}.stop ] ; do sleep 15 ; 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 2>/dev/null echo break fi done break ;; # faxlpr : get phone number and user from current cf* file and run fax send faxlpr) # display permissions for debugging echo "$0: running as:" >&2 ; id -a >&2 ${LS} -ld $FAXDIR >&2 # the lpr spool directory for printer 'fax' if ! cd $FAXDIR then echo "$0: cannot cd to $FAXDIR" >&2 ; break fi if ! test -r lock then echo "$0: can't read lock file" >&2 ; break fi cfile=`tail -1 lock` if ! test -r $cfile then echo "$0: can't read control file" >&2 ; break fi 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/.//` if ! test "$num" then echo "$0: can't read phone number" >&2 ; break fi # save stdin in a file if ! cat - >> fax$$ then echo "$0: can't write `pwd`/fax$$" >&2 ; break fi 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 - </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 <