pkg://efax-0.9-27.2.1.src.rpm:108195/efax-0.9.tar.gz
info downloads
efax-0.9/ 40755 144 144 0 6677205405 10352 5 ustar edc users efax-0.9/README 100644 144 144 5606 6677205204 11333 0 ustar edc users
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.9/COPYING 100644 144 144 43127 6663446724 11537 0 ustar edc users 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.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) 19yy <name of author>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19yy name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, the commands you use may
be called something other than `show w' and `show c'; they could even be
mouse-clicks or menu items--whatever suits your program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice
This General Public License does not permit incorporating your program into
proprietary programs. If your program is a subroutine library, you may
consider it more useful to permit linking proprietary applications with the
library. If this is what you want to do, use the GNU Library General
Public License instead of this License.
efax-0.9/Makefile 100644 144 144 2173 6672063462 12112 0 ustar edc users # Makefile for efax
# Change the following to the name of your ANSI C compiler
# (normally gcc).
CC=gcc
# Compile/load options. Add -DNO_STRERROR to CFLAGS if _strerror
# is undefined
CFLAGS=
LDFLAGS=
# Change the following to the destination directories for
# binaries and man pages. Probably /usr/bin and /usr/man on
# Linux, /usr/local/{bin,man} on other systems.
BINDIR=/usr/bin
MANDIR=/usr/man
.c.o:
$(CC) $(CFLAGS) -c $<
all: efax efix
efax: efax.o efaxlib.o efaxio.o efaxos.o efaxmsg.o
$(CC) -o efax $(LDFLAGS) efax.o efaxlib.o efaxio.o efaxos.o efaxmsg.o
strip efax
efix: efix.o efaxlib.o efaxmsg.o
$(CC) -o efix $(LDFLAGS) efix.o efaxlib.o efaxmsg.o
strip efix
install:
cp fax efax efix $(BINDIR)
cp fax.1 efax.1 efix.1 $(MANDIR)/man1
clean:
rm -f efax efix efax.o efix.o efaxlib.o efaxio.o efaxos.o efaxmsg.o
efax.o: efax.c efaxmsg.h efaxlib.h efaxio.h efaxos.h
efaxio.o: efaxio.c efaxmsg.h efaxio.h efaxos.h
efaxos.o: efaxos.c efaxmsg.h efaxlib.h efaxos.h
efix.o: efix.c efaxmsg.h efaxlib.h
efaxlib.o: efaxlib.c efaxmsg.h efaxlib.h
efaxmsg.o: efaxmsg.c efaxmsg.h
efax-0.9/efax.c 100644 144 144 174275 6677165012 11615 0 ustar edc users #define Copyright "Copyright 1999 Ed Casas"
#define Version "efax v 0.9"
/*
Copyright (C) 1999 Ed Casas
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Please contact the author if you wish to use efax or efix in
ways not covered by the GNU GPL.
You may contact the author by e-mail at: edc@cce.com, by mail
at: 2629 West 3rd Ave, Vancouver, BC, Canada, V6K 1M4, or by
fax at: +1 604 734 5291.
*/
const char *Usage =
"Usage:\n"
" %s [ option ]... [ -t num [ file... ] ]\n"
"Options:\n"
" -a str use command ATstr to answer\n"
" -c cap set modem and receive capabilites to cap\n"
" -d dev use modem on device dev\n"
" -e cmd exec \"/bin/sh -c cmd\" for voice calls\n"
" -f fnt use (PBM) font file fnt for headers\n"
" -g cmd exec \"/bin/sh -c cmd\" for data calls\n"
" -h hdr use page header hdr (use %%d's for current page/total pages)\n"
" -i str send modem command ATstr at start\n"
" -j str send modem command ATstr after set fax mode\n"
" -k str send modem command ATstr when done\n"
" -l id set local identification to id\n"
" -o opt use protocol option opt:\n"
" 0 use class 2.0 instead of class 2 modem commands\n"
" 1 use class 1 modem commands\n"
" 2 use class 2 modem commands\n"
" a if first [data mode] answer attempt fails retry as fax\n"
" e ignore errors in modem initialization commands\n"
" f use virtual flow control\n"
" h use hardware flow control\n"
" l halve lock file polling interval\n"
" n ignore page retransmission requests\n"
" r do not reverse received bit order for Class 2 modems\n"
" x use XON instead of DC2 to trigger reception\n"
" z add 100 ms to pause before each modem comand (cumulative)\n"
" -q ne ask for retransmission if more than ne errors per page\n"
" -r pat save received pages into files pat.001, pat.002, ... \n"
" -s share (unlock) modem device while waiting for call\n"
" -v lvl print messages of type in string lvl (ewinchamr)\n"
" -w don't answer phone, wait for OK or CONNECT instead\n"
" -x fil use uucp-style lock file fil\n"
"Commands:\n"
" -t dial num and send fax image files file... \n"
;
#include <ctype.h> /* ANSI C */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "efaxio.h" /* EFAX */
#include "efaxlib.h"
#include "efaxmsg.h"
#include "efaxos.h"
/* constants... */
/* delays and timeouts (t/o), in deciseconds */
#define T1 350 /* T.30 T1 - waiting for DIS/DCS before Phase B */
#define T2 60 /* T.30 T2 - waiting for frame in Phase B */
#define T3S 30 /* T.30 response timeout (not T3) */
#define T4 30 /* T.30 T4 - between [re]transmissions of DIS */
#define TMOD 55 /* T.30 pause between v.21&v.29, 75-20 ms */
#define TO_A 1200 /* dial/answer (Phase A) - modem may t/o first */
#define TO_ABRT 20 /* max delay after sending abort sequence */
#define TO_CHAR 102 /* per data character (2x max FILL length) */
#define TO_DATAF 80 /* software adaptive answer data connect t/o */
#define TO_DRAIN_H 136 /* minimum HDLC buffer drain time (4k/300cps) */
#define TO_DRAIN_D 300 /* minimum data buffer drain time */
#define TO_FT 31 /* max delay after +F[TR][MH] command */
#define TO_RTCMD 20 /* return to command mode after DLE-ETX (rx) */
#define TO_C2B 450 /* Class 2 DIS to CONNECT:(DCS+TCF+CFR)xretries */
#define TO_C2X 20 /* Class 2 wait for XON: 2/5 of 5s timeout */
#define TO_C2PP 200 /* Class 2 wait for ppr: (ppm+ppr)x3retries + 2 */
#define TO_C2R 600 /* Class 2 receive: (TCF+FTT)x11 retrains + 5 */
#define TO_C2EOR 120 /* Class 2 end of data rx (4 retrans x 3 s) */
#define ANSCMD "A" /* default modem command to answer calls */
#define DCSLEN 3 /* length of FIF for DCS commands sent */
#define DEFDISLEN 3 /* length of DIS initially transmitted */
#define DEFCAP 1,3,0,2,0,0,0,0 /* default local capabilities */
#define DEFID " " /* default local ID */
#define DEFPAT "%m%d%H%M%S" /* default received file name pattern */
#define HDRSHFT 54 /* shift header right 6.7mm into image area */
#define HDRSPCE 20 /* number of scan lines inserted before image */
#define HDRSTRT 4 /* scan line where header is placed on image */
#define HDRCHRH 24 /* header character height (pels, at 196lpi) */
#define HDRCHRW 12 /* header character width (pels) */
#define IDLEN 20 /* length of T.30 ID strings, must be 20 */
#define MAXDIS 8 /* maximum DIS frames sent without response (T1) */
#define MAXERRPRT 32 /* maximum number of reception errors to report */
#define MAXFIFLEN 125 /* max FIF len = MAXFRLEN - (adx+ctl+FCF) - FCS */
#define MAXFRLEN 130 /* max frame length = 3.45s x 300 bps / 8 */
#define MAXGETTY 512 /* maximum length of exec'ed (-g, -e) commands */
#define MAXICMD 100 /* maximum # of modem setup/reset commands */
#define MAXLKFILE 16 /* maximum number of lock files */
#define MAXNULLS 2 /* maximum consecutive received nulls saved */
#define MAXPGERR 10 /* maximum received errors allowed per page */
#define MAXTRAIN 2 /* maximum training retries at lowest speed */
#define MAXRETRY 3 /* maximum retries of unacknowledged commands */
#define NCAP 8 /* number of fields in a capability string */
#define NTXRETRY 3 /* maximum re-sends per page */
typedef int cap [ NCAP ] ; /* remote/local capabilities */
/* capability fields... */
enum captype { VR, BR, WD, LN, DF, EC, BF, ST } ;
int capmax [ NCAP ] = { 1, 7, 2, 2, 3, 2, 1, 7 } ;
/* & maximum values */
/* vertical resolution, dpi */
int vresolution [ 2 ] = { 98, 196 } ;
/* characters per second for br */
int cps [ 8 ] = { 300, 600, 900, 1200, 1500, 1800, 900, 1200 } ;
/* next br = fallback [ br ] */
/* 0, 1, 2, 3, 4, 5, 6, 7 */
int fallback [ 8 ] = {-1, 0, 1, 2, 7, 4, 3, 6 } ;
/* negotiation speed index */
/* 0, 1, 2, 3, 4, 5, 6, 7 */
int brindex [ 8 ] = { 0, 1, 2, 3, 4, 7, 5, 6 } ;
/* minimum scan time in ms */
int mst [ 8 ] = { 0 , 5, 10, 10, 20, 20, 40, 40 } ;
/* page width in pixels */
int pagewidth [ 5 ] = { 1728, 2048, 2432, 1216, 864 } ;
/* Table to convert between T.30 DIS/DCS/DTC FIF and Class 2-like
capability codes. Uses br=6, 7 for V.17 at 7200, 9600. */
typedef struct t30tabstruct
{
char *name ;
uchar byte, shift, mask ;
uchar safeval ;
uchar captodis[8], distocap[16], captodcs[8], dcstocap[16] ;
} t30tabst ;
#define X 0xff /* invalid values */
t30tabst t30tab [ NCAP ] = {
{ "vr", 1, 1, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
{ "br", 1, 2, 0x0f, 0,
{ 0, 4, 12, 12, 13, 13 } ,
{ 0, X, X, X, 1, X, X, X, 3, X, X, X, 3, 5, 3, X } ,
{ 0, 4, 12, 8, 5, 1 } ,
{ 0, 5, 5, X, 1, 4, 4, X, 3, 7, X, X, 2, 6, X, X } } ,
{ "wd", 2, 6, 0x03, 0, { 0, 2, 1 } , { 0, 2, 1, 2 } ,
{ 0, 2, 1 } , { 0, 2, 1, 2 } },
{ "ln", 2, 4, 0x03, 0, { 0, 2, 1 } , { 0, 2, 1, X } ,
{ 0, 2, 1 } , { 0, 2, 1, X } },
{ "df", 1, 0, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
{ "ec", 3, 4, 0x03, 0, { 0, 2, 2 } , { 0, X, 2, X } ,
{ 0, 3, 2 } , { 0, 0, 2, 1 } },
{ "bf", 5, 5, 0x01, 0, { 0, 1 } , { 0, 1 } , { 0, 1 } , { 0, 1 } },
{ "st", 2, 1, 0x07, 7,
{ 7, 4, 3, 2, 6, 0, 5, 1 } , { 5, 7, 3, 2, 1, 6, 4, 0 } ,
{ 7, 4, X, 2, X, 0, X, 1 } , { 5, 7, 3, 1, X, X, X, 0 } }
} ;
/* values of capability fields */
char *capvaluestr [ NCAP ] [8] = {
{ " 98lpi", "196lpi" } ,
{ " 2400bps", " 4800bps", " 7200bps", " 9600bps", " 12kbps", "14.4kbps",
"7200V.17", "9600V.17" } ,
{ "8.5\"/215mm", " 10\"/255mm", " 12\"/303mm",
" 6\"/151mm", "4.2\"/107mm" } ,
{ "11\"/A4", "14\"/B4", " any " } ,
{ "1D" , "2D" }, { " - ", "ECM-256", "ECM-64 " }, { " - ", "BFT" },
{ "0ms", "5ms", "10/5ms", "10ms", "20/10ms", "20ms", "40/20ms", "40ms" }
} ;
/* T.30 control frames */
enum frametype {
DIS=0x01, CSI, NSF=0x04,
CFR=0x21, FTT,
MCF=0x31, RTN, RTP, PIN, PIP,
DCS=0x41, TSI, NSS=0x44,
CRP=0x58, DCN=0x5f,
EOM=0x71, MPS, EOP=0x74, PRI_EOM=0x79, PRI_MPS, PRI_EOP=0x7c,
DTC=0x81, CIG, NSC=0x84
} ;
enum commanddtype { RCV=0, SND=1, DTA=0, TRN=1 } ;
/* Class 1 commands to [receive=0/transmit=1] [data=0/training=1] for
[baud rate=BR]. */
char *c1cmd [ 2 ] [ 2 ] [ 8 ] = {
{ { "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=122", "+FRM=146" ,
"+FRM=74", "+FRM=98" } ,
{ "+FRM=24", "+FRM=48", "+FRM=72", "+FRM=96", "+FRM=121", "+FRM=145" ,
"+FRM=73", "+FRM=97" } } ,
{ { "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=122", "+FTM=146" ,
"+FTM=74", "+FTM=98", } ,
{ "+FTM=24", "+FTM=48", "+FTM=72", "+FTM=96", "+FTM=121", "+FTM=145" ,
"+FTM=73", "+FTM=97" } }
} ;
struct c2msgstruct
{
int min, max ;
char *msg ;
} c2msg [] = {
{ 0, 9, "Call Placement and Termination:" },
{ 0, 0, " Normal and proper end of connection" },
{ 1, 1, " Ring Detect without successful handshake" },
{ 2, 2, " Call aborted, from +FK[S] or CAN" },
{ 3, 3, " No Loop Current" },
{ 4, 4, " Ringback Detected, no answer" },
{ 5, 5, " Ringback Detected, answer without CED" },
{ 10, 19, "Transmit Phase A & Miscellaneous Errors:" },
{ 10, 10, " Unspecified Phase A error" },
{ 11, 11, " No Answer (T.30 T1 timeout)" },
{ 20, 39, "Transmit Phase B Hangup Codes:" },
{ 20, 20, " Unspecified Transmit Phase B error" },
{ 21, 21, " Remote cannot receive or send" },
{ 22, 22, " COMREC error in transmit Phase B" },
{ 23, 23, " COMREC invalid command received" },
{ 24, 24, " RSPREC error" },
{ 25, 25, " DCS sent three times without response" },
{ 26, 26, " DIS/DTC received 3 times; DCS not recognized" },
{ 27, 27, " Failure to train at 2400 bps or +FMINSP value" },
{ 28, 28, " RSPREC invalid response received" },
{ 40, 49, "Transmit Phase C Hangup Codes:" },
{ 40, 40, " Unspecified Transmit Phase C error" },
{ 41, 41, " Unspecified image format error" },
{ 42, 42, " Image conversion error" },
{ 43, 43, " DTE to DCE data underflow" },
{ 44, 44, " Unrecognized transparent data command" },
{ 45, 45, " Image error, line length wrong" },
{ 46, 46, " Image error, page length wrong" },
{ 47, 47, " Image error, wrong compression code" },
{ 50, 69, "Transmit Phase D Hangup Codes:" },
{ 50, 50, " Unspecified Transmit Phase D error" },
{ 51, 51, " RSPREC error" },
{ 52, 52, " No response to MPS repeated 3 times" },
{ 53, 53, " Invalid response to MPS" },
{ 54, 54, " No response to EOP repeated 3 times" },
{ 55, 55, " Invalid response to EOP" },
{ 56, 56, " No response to EOM repeated 3 times" },
{ 57, 57, " Invalid response to EOM" },
{ 58, 58, " Unable to continue after PIN or PIP" },
{ 70, 89, "Receive Phase B Hangup Codes:" },
{ 70, 70, " Unspecified Receive Phase B error" },
{ 71, 71, " RSPREC error" },
{ 72, 72, " COMREC error" },
{ 73, 73, " T.30 T2 timeout, expected page not received" },
{ 74, 74, " T.30 T1 timeout, after EOM received" },
{ 90, 99, "Receive Phase C Hangup Codes:" },
{ 90, 90, " Unspecified Receive Phase C error" },
{ 91, 91, " Missing EOL after 5 seconds" },
{ 92, 92, " Unused code" },
{ 93, 93, " DCE to DTE buffer overflow" },
{ 94, 94, " Bad CRC or frame (ECM or BFT modes)" },
{ 100, 119, "Receive Phase D Hangup Codes:" },
{ 100, 100, " Unspecified Receive Phase D errors" },
{ 101, 101, " RSPREC invalid response received" },
{ 102, 102, " COMREC invalid response received" },
{ 103, 103, " Unable to continue after PIN or PIP" },
{ 120, 255, "Reserved Codes" },
{ -1, -1, "" }
} ;
/* meaning of efax return codes */
char *errormsg [] = {
"success",
"number busy or modem in use",
"unrecoverable error",
"invalid modem response",
"no response from modem",
"terminated by signal",
"internal error" } ;
/* Functions... */
/* Return name of frame of type 'fr'. */
char *frname ( int fr )
{
static struct framenamestruct { int code ; char *name ; }
framenames [] = {
{NSC,"NSC - poller features"}, /* these 3 frames must be first */
{CIG,"CIG - poller ID"},
{DTC,"DTC - poller capabilities"},
{NSF,"NSF - answering features"},
{CSI,"CSI - answering ID"},
{DIS,"DIS - answering capabilities"},
{NSS,"NSS - caller features"},
{TSI,"TSI - caller ID"},
{DCS,"DCS - session format"},
{CFR,"CFR - channel OK"},
{FTT,"FTT - channel not OK"},
{MPS,"MPS - not done"},
{EOM,"EOM - not done, new format"},
{EOP,"EOP - done"},
{PRI_MPS,"PRI-MPS - not done, call operator"},
{PRI_EOM,"PRI-EOM - not done, new format, call operator"},
{PRI_EOP,"PRI-EOP - done, call operator"},
{MCF,"MCF - page OK"},
{RTP,"RTP - page OK, check channel"},
{PIP,"PIP - page OK, call operator"},
{RTN,"RTN - page not OK, check channel"},
{PIN,"PIN - page not OK, call operator"},
{CRP,"CRP - repeat command"},
{DCN,"DCN - disconnect"},
{0,0} },
*p ;
for ( p=framenames ; p->code ; p++ )
if ( fr == p->code || ( fr & 0x7f ) == p->code) break ;
return p->code ? p->name : "UNKNOWN" ;
}
/* Range-check capability. */
int checkcap ( cap c )
{
int err=0, i ;
for ( i=0 ; i<NCAP ; i++ )
if ( c[i] > capmax[i] || c[i] < 0 ) {
err = msg ( "E3%s = %d out of range, set to 0", t30tab[i].name, c[i] ) ;
c[i]=0 ;
}
return err ;
}
/* Print cap[ability] c using text values and prefix s. */
void printcap ( char *s , cap c )
{
int i ;
msg ( "N-+ %s" , s ) ;
checkcap ( c ) ;
for ( i=0 ; i<NCAP ; i++ )
msg ( "N-+ %s" , capvaluestr [ i ] [ c[i] ] ) ;
msg ( "N-" ) ;
}
/* Convert capability string to cap struct. Returns 0 or 2 on errors. */
int str2cap ( char *s, cap c )
{
int err=0, n ;
n = sscanf ( s, "%d,%d,%d,%d,%d,%d,%d,%d",
c+0, c+1, c+2, c+3, c+4, c+5, c+6, c+7 ) ;
if ( n < NCAP ) msg ( "Wmissing value(s) in \"%s\"", s ) ;
checkcap ( c ) ;
return err ;
}
/* Convert a cap[ability] 'c' to a DIS/DCS/DTC FIF 'fif' of 'len'
bytes. Converts into DIS format if 'isdis' is true, else into
DCS/DTC format. */
void mkdis ( cap c, uchar *fif, int len, int isdis, int t4tx )
{
int i, k ;
t30tabst *p ;
len = len > DCSLEN ? DCSLEN : len ;
fif[0] = 0 ;
fif[1] = ( isdis && t4tx ? 0x80 : 0 ) | 0x40 ;
for ( i=2 ; i<len-1 ; i++ ) fif[i] = 0x01 ; /* add extension bits */
fif[i] = 0 ;
checkcap ( c ) ;
for ( i=0 ; i<NCAP ; i++ ) {
p = t30tab + i ;
if ( ( k = ( isdis ? p->captodis : p->captodcs ) [ c [ i ] ] ) == X )
msg ( "E3mkdis: can't happen (invalid %s)", p->name ), k=0 ;
if ( p->byte < len ) fif [ p->byte ] |= k << p->shift ;
}
}
/* Return length of DIS/DTC FIF (counts extension bits). */
int dislen ( uchar *fif )
{
int n ;
for ( n=3 ; fif [ n-1 ] & 0x01 && n < MAXFIFLEN ; n++ ) ;
return n ;
}
/* Convert received DIS/DCS/DTC FIF to cap. Returns 0 or 3 if bad DIS/DCS
field. */
int mkcap ( uchar *fif, cap c, int dis )
{
int err=0, i, j, k, len ;
t30tabst *p ;
len = dislen ( fif ) ;
for ( i=0 ; i<NCAP ; i++ ) {
p=t30tab+i ;
if ( p->byte >= len ) {
c [ i ] = 0 ;
} else {
j = ( fif [ p->byte ] >> p->shift ) & p->mask ;
k = ( dis ? p->distocap : p->dcstocap ) [ j ] ;
if ( k == X ) {
c [ i ] = p->safeval ;
err = msg("E3mkcap: bad %s field (%d) set to %d",
p->name, j, c [ i ] ) ;
} else {
c [ i ] = k ;
}
}
}
return err ;
}
/* Compute compatible local/remote capabilities. Used by the
sending station only and only for Class 1. Returns 0 if OK or
3 if no compatible settings possible. */
int mincap ( cap local, cap remote, cap session )
{
int err=0, i ;
int msttab[2][8] = { { 0,1,3,3,5,5,7,7 } , { 0,1,1,3,3,5,5,7 } } ;
printcap ( "local ", local ) ;
printcap ( "remote ", remote ) ;
for ( i=0 ; i<NCAP && i!=ST && i !=BR ; i++ )
session[i] = remote[i] < local[i] ? remote[i] : local[i] ;
session[BR] = brindex[ remote[BR] ] < brindex[ local[BR] ] ?
remote[BR] : local[BR] ;
session[ST] = msttab [ session[VR] ] [ remote[ST] ] ;
printcap ( "session", session ) ;
if ( local[WD] != session[WD] || local[LN] > session[LN] ||
local[DF] != session[DF] )
err = msg ("W3incompatible local and remote capabilities" ) ;
return err ;
}
/* Skip to start of first/next page (or to start of previous page
if dp is 0). If ppm in not null, it is then set to EOP if
there are no pages following this one, MPS if the next page
has the same format as `local' (assumed to be the format of
the previous page), EOM if the page has a different format.
If local is non-NULL its format fields are set according to
the format of the new page. Currently only considers the
file's y-resolution.
This function is called before send_data() and obtains the ppm
for that page. It can be called again with dp=0 if a PIN or
RTN is received to restart the page. Returns 0 or 2 on
errors. */
int rdpage ( IFILE *f, int dp, int *ppm, cap local, int *changed )
{
int err=0, m=EOP, yres, fVR, nVR ;
if ( nextipage ( f, dp ) )
err = msg ( "E2 can't happen (rdpage: can't go to %s page)",
dp ? "next" : "same" ) ;
if ( ! err ) {
yres = f->page->yres ;
fVR = ( yres > (196+98)/2 ) ? 1 : 0 ;
if ( local && yres ) {
if ( local [ VR ] != fVR ) {
local [ VR ] = fVR ;
if ( changed ) *changed = 1 ;
} else {
if ( changed ) *changed = 0 ;
}
}
if ( lastpage ( f ) ) {
m = EOP ;
} else {
PAGE *p = f->page + 1 ;
nVR = ( p->yres > (196+98)/2 ) ? 1 : 0 ;
m = ( nVR != fVR ) ? EOM : MPS ;
}
}
if ( ppm ) {
*ppm = err ? EOP : m ;
}
return err ;
}
/* Terminate previous page if page number is non-zero and start
next output page if page number is non-negative. If page is -1
removes the most recently opened file. Returns 0 if OK, 2 on
errors. */
int wrpage ( OFILE *f, int page )
{
int err=0 ;
err = nextopage ( f, page ) ;
if ( ! err && page == -1 ) {
if ( remove ( f->cfname ) ) {
err = msg ( "ES2can't delete file %s:", f->cfname ) ;
} else {
msg ( "Fremoved %s", f->cfname ) ;
}
}
return err ;
}
/* Send data for one page. Figures out required padding and 196->98 lpi
decimation based on local and session capabilitites, substitutes page
numbers in header string and enables serial port flow control. Inserts
the page header before the input file data. Converts each scan line to
T.4 codes and adds padding (FILL) and EOL codes before writing out.
Sends RTC when done. Sends DLE-ETX and returns serial port to command
mode when done. Returns 0 if OK, non-0 on errors. */
int send_data ( TFILE *mf, IFILE *f, int page, int pages,
cap local, cap session, char *header, faxfont *font )
{
int done=0, err=0, noise=0, nr=0, lastnr=0, line, pixels ;
int i, decimate, pwidth, minlen, dcecps, inheader, skip=0 ;
uchar buf [ MAXCODES + 2*EOLBITS/8 + 1 ], *p ;
short runs [ MAXRUNS ], lastruns [ MAXRUNS ] ;
char headerbuf [ MAXLINELEN ] ;
ENCODER e ;
newENCODER ( &e ) ;
dcecps = cps[session[BR]] ;
minlen = ( (long)dcecps * mst[session[ST]] - 1500 + 500 ) / 1000 ;
pwidth = pagewidth [ session [ WD ] ] ;
decimate = local[VR] > session[VR] ;
msg ( "T padding to %d bytes/scan line.%s", minlen+1,
decimate ? " reducing 196->98 lpi." : "" ) ;
if ( vfc )
msg ( "T limiting output to %d bps for %d byte modem buffer",
dcecps*8, MAXDCEBUF + MINWRITE ) ;
if ( ckfmt ( header, 6 ) )
msg ( "W too many %%d escapes in header format string \"%s\"", header ) ;
else
sprintf ( headerbuf, header, page, pages, page, pages, page, pages ) ;
msg ("I header:[%s]", headerbuf ) ;
done = err = ttymode ( mf, SEND ) ;
mf->start = time(0) ;
mf->mstart = proc_ms() ;
mf->bytes = mf->pad = mf->lines = 0 ;
/* start T.4 data with some FILL and an EOL */
for ( i=0 ; i<32 ; i++ )
p = putcode ( &e, 0, 8, buf ) ;
p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
if ( ! f || ! f->f )
err = msg ( "E2can't happen(send_data)" ) ;
mf->lines=0 ;
for ( line=0 ; ! done && ! err ; line++ ) {
if ( line < HDRSPCE ) { /* insert blank lines at the top */
runs[0] = pwidth ;
pixels = pwidth ;
nr = 1 ;
} else {
if ( ( nr = readline ( f, runs, &pixels ) ) < 0 ) {
done = 1 ;
continue ;
}
}
/* generate and OR in header pixels */
if ( line >= HDRSTRT && line < HDRSTRT + HDRCHRH ) {
int hnr ;
short hruns [ MAXRUNS ] ;
hnr = texttorun ( (uchar*) headerbuf, font, line-HDRSTRT,
HDRCHRW, HDRCHRH, HDRSHFT,
hruns, 0 ) ;
nr = runor ( runs, nr, hruns, hnr, 0, &pixels ) ;
}
inheader = line < HDRSTRT + HDRCHRH ;
if ( decimate || ( inheader && local[VR] == 0 ) ) {
if ( ++skip & 1 ) { /* save the first of every 2 lines */
memcpy ( lastruns, runs, nr * sizeof(short) ) ;
lastnr = nr ;
continue ; /* get next line */
} else { /* OR previous line into current line */
nr = runor ( runs, nr, lastruns, lastnr, 0, &pixels ) ;
}
}
if ( nr > 0 ) {
if ( pixels ) {
/* make line the right width */
if ( pixels != pwidth ) nr = xpad ( runs, nr, pwidth - pixels ) ;
/* convert to MH coding */
p = runtocode ( &e, runs, nr, p ) ;
/* zero pad to minimum scan time */
while ( p - buf < minlen ) {
p = putcode ( &e, 0, 8, p ) ;
mf->pad ++ ;
}
/* add EOL */
p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
sendbuf ( mf, buf, p - buf, dcecps ) ;
mf->bytes += p - buf ;
mf->lines++ ;
} else {
/* probably read an EOL as part of RTC */
}
if ( tdata ( mf, 0 ) ) noise = 1 ;
p = buf ;
}
}
for ( i=0 ; i < RTCEOL ; i++ )
p = putcode ( &e, EOLCODE, EOLBITS, p ) ;
p = putcode ( &e, 0, 0, p ) ;
sendbuf ( mf, buf, p - buf, dcecps ) ;
mf->bytes += p - buf ;
if ( noise ) msg ("W- characters received while sending" ) ;
return err ;
}
int end_data ( TFILE *mf, cap session, int ppm, int *good )
{
int err=0, c ;
uchar *p ;
long dt, draintime ;
if ( ! ppm ) p = DLE_ETX ;
else if ( ppm == MPS ) p = "\020," ;
else if ( ppm == EOM ) p = "\020;" ;
else if ( ppm == EOP ) p = "\020." ;
else {
p = "" ;
err = msg ( "E2 can't happen (end_data)" ) ;
}
tput ( mf, p, 2 ) ;
dt = time(0) - mf->start ;
/* time to drain buffers + 100% + 4s */
draintime = ( 2 * ( mf->bytes / cps[ session[BR] ] + 1 - dt ) + 4 ) * 10 ;
draintime = draintime < TO_DRAIN_D ? TO_DRAIN_D : draintime ;
c = ckcmd ( mf, 0, 0, (int) draintime, OK ) ;
if ( good ) *good = ( c == OK ) ? 1 : 0 ;
dt = time(0) - mf->start ;
msg ( "Isent %d+%d lines, %d+%d bytes, %d s %d bps" ,
HDRSPCE, mf->lines-HDRSPCE,
mf->bytes-mf->pad, mf->pad, (int) dt, (mf->bytes*8)/dt ) ;
if ( mf->bytes / (dt+1) > cps[session[BR]] )
msg ( "E flow control did not work" ) ;
if ( ! err ) err = ttymode ( mf, COMMAND ) ;
return err ;
}
/* Read one scan line from fax device. If pointer pels is not
null it is used to save pixel count. Returns number of runs
stored, EOF on RTC, or -2 on EOF, DLE-ETX or other error. */
int readfaxruns ( TFILE *f, DECODER *d, short *runs, int *pels )
{
int err=0, c=EOF, x, n ;
dtab *tab, *t ;
short shift ;
short *p, *maxp, *q, len=0 ;
uchar rd_state ;
maxp = ( p = runs ) + MAXRUNS ;
x = d->x ; shift = d->shift ; tab = d->tab ; /* restore decoder state */
rd_state = f->rd_state ;
do {
do {
while ( shift < 0 ) {
c = tgetd ( f, TO_CHAR ) ;
rd_state = ( rd_state & rd_allowed[c] ) ?
( ( rd_state & rd_nexts[c] ) ? rd_state <<= 1 : rd_state ) :
RD_BEGIN ;
if ( rd_state == RD_END )
msg ( "W+ modem response in data" ) ;
if ( c < 0 ) {
x = ( x << 15 ) | 1 ; shift += 15 ; /* EOL pad at EOF */
} else {
x = ( x << 8 ) | c ; shift += 8 ;
}
}
t = tab + ( ( x >> shift ) & 0x1ff ) ;
tab = t->next ;
shift -= t->bits ;
} while ( ! t->code ) ;
if ( p < maxp ) *p++ = t->code ;
} while ( t->code != -1 ) ;
d->x = x ; d->shift = shift ; d->tab = tab ; /* save state */
f->rd_state = rd_state ;
if ( p >= maxp ) msg ( "Wrun length buffer overflow" ) ;
/* combine make-up and terminating codes and remove +1 offset
in run lengths */
n = p - runs - 1 ;
for ( p = q = runs ; n-- > 0 ; )
if ( *p > 64 && n-- > 0 ) {
len += *q++ = p[0] + p[1] - 2 ;
p+=2 ;
} else {
len += *q++ = *p++ - 1 ;
}
n = q - runs ;
/* check for RTC and errors */
if ( len )
d->eolcnt = 0 ;
else
if ( ++(d->eolcnt) >= RTCEOL ) err = EOF ;
if ( c < 0 ) err = - 2 ;
if ( pels ) *pels = len ;
return err ? err : n ;
}
/* Receive data. Reads scan lines from modem and writes to output
file. Checks for errors by comparing received line width and
session line width. Check that the output file is still OK
and if not, send one CANcel character and wait for protocol to
complete. Returns 0 if OK or 2 if there was a file write error. */
int receive_data ( TFILE *mf, OFILE *f, cap session, int *nerr )
{
int err=0, line, lines, nr, len ;
int pwidth = pagewidth [ session [ WD ] ] ;
short runs [ MAXRUNS ] ;
DECODER d ;
if ( ! f || ! f->f ) {
msg ( "E2 can't happen (writeline)" ) ;
}
newDECODER ( &d ) ;
*nerr = 0 ;
lines=0 ;
for ( line=0 ; ( nr = readfaxruns ( mf, &d, runs, &len ) ) >= 0 ; line++ ) {
if ( nr > 0 && len > 0 && line ) { /* skip first line+EOL and RTC */
if ( len != pwidth ) {
(*nerr)++ ;
if ( *nerr <= MAXERRPRT ) msg ("R-+ (%d:%d)", line, len ) ;
nr = xpad ( runs, nr, pwidth - len ) ;
}
writeline ( f, runs, nr, 1 ) ;
lines++ ;
}
if ( ferror ( f->f ) ) {
err = msg ("ES2file write:") ;
tput ( mf, CAN_STR, 1 ) ;
msg ("Wdata reception CANcelled") ;
}
}
if ( *nerr ) {
if ( *nerr > MAXERRPRT ) msg ("R-+ ....." ) ;
msg ("R- : reception errors" ) ;
msg ("W- %d reception errors", *nerr ) ;
}
if ( nr == EOF )
while ( tgetd ( mf, TO_CHAR ) >= 0 ) ; /* got RTC, wait for DLE-ETX */
msg ( "I- received %d lines, %d errors", lines, *nerr ) ;
return err ;
}
/* Send training check sequence of n zeroes. Returns 0 or 2 on error. */
int puttrain ( TFILE *f, char *s, int n )
{
int i, m, err=0 ;
uchar buf [ MINWRITE ] = { 0 } ;
ckcmd ( f, &err, s, TO_FT, CONNECT ) ;
if ( ! err ) {
ttymode ( f, SEND ) ;
for ( i=0 ; i < n ; i += m ) {
m = n-i < MINWRITE ? n-i : MINWRITE ;
sendbuf ( f, buf, m, 0 ) ;
}
buf[0] = 1 ; /* make sure last byte is non-zero */
buf[1] = DLE ;
buf[2] = ETX ;
sendbuf ( f, buf, 3, 0 ) ;
ttymode ( f, COMMAND ) ;
ckcmd ( f, &err, 0, TO_DRAIN_D, OK ) ;
msg ( "I- sent TCF - channel check of %d bytes", n ) ;
}
return err ;
}
/* Checks for an error-free run of at least n bytes in the
received training check sequence. Sets good if it's not null,
the run was long enough and there were no errors. Returns 0 or
3 on other errors. */
int gettrain ( TFILE *f, char *s, int n, int *good )
{
int err=0, c, i=0, maxrunl=0, runl=0 ;
ckcmd ( f, &err, s, T2, CONNECT ) ;
if ( ! err ) {
for ( i=0 ; ( c = tgetd ( f, T3S ) ) >= 0 ; i++ )
if ( c ) {
if ( runl > maxrunl ) maxrunl = runl ;
runl = 0 ;
} else {
runl ++ ;
}
if ( c == EOF )
err = msg ( "E3timed out during training check data" ) ;
else
ckcmd ( f, &err, 0, TO_RTCMD, NO ) ;
}
if ( runl > maxrunl ) maxrunl = runl ;
if ( good ) *good = !err && maxrunl > n ;
if ( !err ) {
msg ( "I- received TCF - channel check (%sOK: run of %d in %d)",
maxrunl > n ? "" : "not ", maxrunl, i ) ;
}
return err ;
}
/* Log a sent/received HDLC frame. Display of these messages is delayed to
avoid possible timing problems. */
void logfr ( char *s , char *nm , uchar *p , int n )
{
int i=0 ;
msg ( n > 10 ? "H- %s %d bytes:" : "H-+ %s %d bytes:" , s, n ) ;
for ( i=0 ; i<n ; i++ ) {
msg ( "H-+ %02x" , p[i] & 0xff ) ;
if ( ( i&0xf ) == 0xf && i != n-1 ) msg ( "H-" ) ;
}
msg ( "H-" ) ;
msg ( "I- %s %s", s, nm ) ;
}
/* Send HDLC control frame of type type. Extra bits can be OR'ed
into the frame type (FCF) to indicate that this frame follows
a previous one (no +FTH required) and/or that more frames will
follow. Sets up flag, address, and fax control field bytes in
`buf'. Sends these plus `len` additional bytes. Terminates
with DLE-ETX and checks response. Returns 0 if OK, 2 or 3 on
error. */
#define MORE_FR 0x100
#define SUB_FR 0x200
int nframes = 0 ; /* counts frames sent/received */
int putframe ( int type, uchar *buf, int len, TFILE *f, int t )
{
int err=0 ;
buf [ 0 ] = 0xff ;
buf [ 1 ] = type & MORE_FR ? 0xc0 : 0xc8 ;
buf [ 2 ] = type & 0xff ;
if ( nframes++ && ! ( type & SUB_FR ) )
ckcmd ( f, &err, "+FTH=3" , TO_FT, CONNECT ) ;
if ( ! err ) {
if ( ! buf[len+2] ) {
msg ( "Wlast byte of frame is NULL" ) ;
}
/* ttymode ( f, SEND ) ; */
sendbuf ( f, buf, len+3, 0 ) ;
tput ( f, DLE_ETX, 2 ) ;
/* ttymode ( f, COMMAND ) ; */
logfr ( "sent", frname ( buf [ 2 ] ), buf, len+3 ) ;
ckcmd ( f, &err, 0, TO_DRAIN_H, ( type & MORE_FR ) ? CONNECT : OK ) ;
}
return err ;
}
/* Reverse bit and byte order of ID strings as per T.30 5.3.6.2.4-6 */
void revcpy ( uchar *from , uchar *to )
{
int i, j ;
for ( i=0, j=IDLEN-1 ; i<IDLEN ; i++, j-- )
to [ i ] = normalbits [ from [ j ] & 0xff ] ;
}
/* Check for missing initial 0xFF in HDLC frame and insert it if
missing. Ugly fix for a still-hidden bug. Also do bit
inversion if required. */
int fixframe ( uchar *buf, int n, TFILE *f )
{
int i;
if ( *buf == 0xc0 || *buf == 0xc8 ) {
for ( i=n; i >= 1 ; i-- )
buf[i]=buf[i-1] ;
buf[i] = 0xff ;
msg ("W HDLC frame missing initial 0xff" ) ;
n++ ;
}
if ( buf[1] == 0x03 || buf[1] == 0x13 ) {
for ( i=0 ; i < n ; i++ )
buf[i]=normalbits[buf[i]] ;
msg ("W bit-reversed HDLC frame, reversing bit order" ) ;
f->ibitorder = f->ibitorder == normalbits ? reversebits : normalbits ;
}
return n ;
}
/* Read HDLC frame data. Returns 0 if OK, 1 on frame error, 3 on
timeout, invalid response or too-long frame. */
int receive_frame_data ( TFILE *f, uchar *buf, int n, int *len )
{
int err=0, c, i ;
for ( i=0 ; ( c = tgetd ( f, T3S ) ) >= 0 ; i++ )
if ( i < n ) buf[ i ] = c ;
if ( c == EOF ) {
err = msg ( "E3timed out reading frame data" ) ;
} else {
switch ( cmd ( f, 0, TO_RTCMD ) ) {
case OK:
case CONNECT:
break ;
case ERROR:
case NO:
err = msg ( "W1frame error" ) ;
break ;
case EOF:
err = msg ( "E3no response after frame data" ) ;
break ;
default:
err = msg ( "E3wrong response after frame data" ) ;
break ;
}
}
if ( i >= n )
err = msg ( "E3frame too long (%d, > %d max bytes)", i, n ) ;
if ( len ) *len = i ;
return err ;
}
/* Get a Class 1 command or response frame. An attempt to match
and combine T.30 "Response Received?" and "Command Received?"
protocol flowcharts.
When receiving commands returns after first correct
non-optional frame or after the time given by getcmd has
elapsed. This is instead of looping through main flowchart.
When receiving responses returns on the first detected
non-optional frame, after timeout T4, or on errors.
Returns immediately if gets a +FCERROR response so can retry
as data carrier. Returns DCN as a valid frame instead of
hanging up.
Returns the command/response received, or EOF on timeout or
error.
*/
int getfr ( TFILE *mf, uchar *buf, int getcmd )
{
int err=0, frame=0, frlen, c, t ;
char remoteid [ IDLEN + 1 ] ;
time_t start ;
uchar *fif=buf+3 ;
start = 10*time(0) ;
t = getcmd ? ( getcmd > 1 ? getcmd : T2 ) : T4 ;
Enter:
err = 0 ;
if ( nframes++ ) {
c = cmd ( mf, "+FRH=3", t ) ;
} else {
c = CONNECT ; /* implied by ATA or ATD */
}
switch ( c ) {
case EOF: /* time out */
tput ( mf, CAN_STR, 1 ) ;
ckcmd ( mf, 0, 0, TO_ABRT, OK ) ;
err = 1 ;
break ;
case NO: /* S7 time out */
err = 1 ;
break ;
case MODULATION: /* data carrier (or DHS) */
return -msg ( "W-2 wrong carrier" ) ;
break ;
case CONNECT: /* frame */
break ;
default: /* shouldn't happen */
err = msg ( "E3wrong response to receive-frame command" ) ;
break ;
}
if ( ! err )
err = receive_frame_data ( mf, buf, MAXFRLEN, &frlen ) ;
if ( ! err && frlen < 3 )
err = msg ( "E3received short frame (%d bytes)", frlen ) ;
if ( ! err ) {
frlen = fixframe ( buf, frlen, mf ) ;
logfr ( "received", frname ( buf [ 2 ] ), buf, frlen ) ;
frame = buf [ 2 ] & 0x7f ;
switch ( frame ) {
case CRP:
err = 1 ;
case NSF:
case NSC:
case NSS:
goto Enter ;
case CIG:
case CSI:
case TSI:
revcpy ( fif , (uchar*) remoteid ) ;
msg ( "I- remote ID -> %*.*s", IDLEN, IDLEN, remoteid ) ;
goto Enter ;
}
}
if ( err && getcmd && ( t -= 10*time(0) - start ) > 0 )
goto Enter ;
return err ? EOF : frame ;
}
/* Class 1 send/receive.
The logic in this function is a mess because it's meant to
mirror the flowchart in ITU-T recommendation T.30 which is the
protocol specification.
*/
int c1sndrcv (
TFILE *mf, cap local, char *localid,
OFILE *outf, IFILE *inf,
int pages, char *header, faxfont *font,
int maxpgerr, int noretry, int calling )
{
int err=0, rxpage=0, page=1, t, disbit, good, frame, last, nerr ;
int rxdislen, ppm, try=0, pagetry=0, retry=0, remtx=0, remrx=0 ;
int writepending=0, dp=0 ;
cap remote = { DEFCAP }, session = { DEFCAP } ;
char *fname=0 ;
uchar buf [ MAXFRLEN ], *fif=buf+3 ;
if ( ! calling ) goto RX ;
/* Class 1 Transmitter: */
T: /* Transmitter Phase B - wait for DIS or DTC */
pagetry = 0 ;
frame = getfr ( mf, buf, T1 ) ;
if ( frame <= 0 ) {
err = msg ( "E3no answer from remote fax" ) ;
goto B ;
}
if ( frame != DIS && frame != DTC ) {
msg ( "W2 can't open page" ) ;
goto C ;
}
disbit = ( frame == DIS ) ? 0x80 : 0x00 ;
try = 0 ;
A: /* decide to send or receive after DIS/DTC */
if ( frame == DIS || frame == DTC ) {
rxdislen = dislen ( fif ) ;
mkcap ( fif, remote, 1 ) ;
remtx = fif[1] & 0x80 ;
remrx = fif[1] & 0x40 ;
}
msg ( "N remote has %sdocument(s) to send, and can %sreceive",
remtx ? "" : "no ", remrx ? "" : "not " ) ;
if ( pages > 0 ) {
if ( ! remrx ) msg ( "W remote cannot receive, trying anyways" ) ;
goto D ;
} else {
if ( ! remtx ) msg ( "W remote has nothing to send, trying anyways" ) ;
goto R ;
}
D: /* send DCS */
if ( rdpage ( inf, dp, &ppm, local, 0 ) ) {
err = msg ( "E2can't open page" ) ;
goto B ;
}
D_2:
mincap ( local, remote, session ) ;
revcpy ( (uchar*) localid, fif ) ;
if ( ! err )
err = putframe ( TSI | MORE_FR | disbit, buf, IDLEN, mf, -1 ) ;
mkdis ( session, fif, DCSLEN, 0, pages ) ;
if ( ! err )
err = putframe ( DCS | SUB_FR | disbit, buf, DCSLEN, mf, -1 ) ;
if ( cmd ( mf, "+FTS=8", T3S ) != OK )
msleep ( TMOD ) ; /* if +FTS not supported */
if ( ! err )
err = puttrain ( mf, c1cmd[SND][TRN][session[BR]],
1.5*cps [ session[BR] ] ) ;
try++ ;
if ( ! err ) {
cmd ( mf, "+FRS=1", T3S ) ; /* wait for TCF carrier to drop */
frame = getfr ( mf, buf, 0 ) ;
}
if ( err || frame < 0 ) {
if ( try >= 3 ) {
goto C_timeout ;
} else {
goto D_2 ;
}
}
switch ( frame ) {
case DIS:
case DTC:
if ( try >= 3 ) goto C_timeout ;
else goto A ;
case FTT:
msg ( "I channel not usable at %d bps", 8*cps[session[BR]] ) ;
remote[BR] = fallback[session[BR]] ;
if ( remote[BR] >= 0 ) goto D_2 ;
else { err = msg ( "E2 channel not usable at lowest speed" ) ; goto C ; }
case CFR:
goto I_2 ;
default:
err = msg ( "E3 invalid response to DCS (0x%02x)", frame ) ;
goto C ;
}
I: /* send a page */
if ( rdpage ( inf, dp, &ppm, local, 0 ) ) {
err = msg ( "E2can't open page" ) ;
goto B ;
}
I_2:
ckcmd ( mf, &err, c1cmd [SND][DTA][session[BR]], TO_FT, CONNECT ) ;
if ( !err )
err = send_data ( mf, inf, page, pages, local, session, header, font ) ;
pagetry++ ;
if ( !err )
err = end_data ( mf, session, 0, 0 ) ;
if ( cmd ( mf, "+FTS=8", T3S ) != OK )
msleep ( TMOD ) ; /* if +FTS not supported */
/* fix ppm if on last page of stdin */
if ( lastpage ( inf ) ) ppm = EOP ;
try = 0 ;
sendppm:
if ( !err ) err = putframe ( ppm | disbit, buf, 0, mf, -1 ) ;
try++ ;
frame = getfr ( mf, buf, 0 ) ;
if ( frame < 0 ) {
if ( try >= 3 ) {
goto C_timeout ;
} else {
goto sendppm ;
}
}
fname = inf->page->fname ;
switch ( noretry ? MCF : frame ) { /* common retry logic */
case MCF:
case RTP:
case PIP:
fname = inf->page->fname ;
if ( fname ) msg ( "Isent -> %s", fname ) ;
pagetry=0 ;
page++ ;
dp = 1 ;
break ;
case PIN:
case RTN:
dp = 0 ;
retry = pagetry < NTXRETRY ;
break ;
default:
err = msg ( "E3invalid post-page response (0x%02x)", frame ) ;
goto C ;
}
switch ( ppm ) {
case MPS:
switch ( frame ) {
case PIN: goto E ;
case PIP: goto E ;
case MCF: goto I ;
case RTP: goto D ;
case RTN: goto D ;
}
case EOP:
switch ( frame ) {
case PIN: goto E ;
case PIP: goto E ;
case MCF:
case RTP:
nextipage ( inf, 1 ) ; /* skip ahead to mark all files done */
if ( remtx ) goto R ; /* poll after sending */
else goto C ;
case RTN:
if ( retry ) goto D ;
else goto C ;
}
case EOM:
switch ( frame ) {
case PIN: goto E ;
case PIP: goto E ;
case MCF:
case RTP:
case RTN:
cmd ( mf, "+FRS=20", T3S ) ; /* wait for ppr carrier to drop */
if ( retry ) goto T ;
else goto T ;
}
}
E: /* ignore PIN and PIP */
msg ( "W interrupt request ignored" ) ;
try=0 ;
goto A ;
/* Class 1 Receiver */
RX:
R: /* Receiver Phase B */
if ( ! err ) err = wrpage ( outf, rxpage ) ;
disbit=0x00 ;
for ( t=0 ; !err && t<T1 ; t+=T2+10 ) {
revcpy ( (uchar*) localid, fif ) ;
if ( !err )
err = putframe ( CSI | disbit | MORE_FR, buf, IDLEN, mf, -1 ) ;
mkdis ( local, fif, DEFDISLEN, 1, pages ) ;
if ( !err )
err = putframe ( DIS | disbit | SUB_FR, buf, DEFDISLEN, mf, -1 ) ;
frame = getfr ( mf, buf, 0 ) ;
if ( frame > 0 ) {
disbit = ( frame == DIS ) ? 0x80 : 0x00 ;
goto F_2 ;
}
}
if ( err ) goto C ;
else goto C_timeout ;
F: /* get a command */
last = frame ;
frame = getfr ( mf, buf, 1 ) ;
if ( writepending ) { /* do postponed file close/open */
writepending=0 ;
err = wrpage ( outf, rxpage ) ;
if ( err ) goto C ;
}
if ( frame < 0 ) {
if ( frame == -2 ) goto getdata ; /* data carrier detected */
if ( last == EOM ) goto R ;
else { err = msg ("E3 timed out waiting for command" ) ; goto B ; }
}
F_2:
switch ( frame ) {
case DTC:
goto D ;
case DIS:
try=0 ;
goto A ;
case DCS:
mkcap ( fif, session, 0 ) ;
printcap ( "session", session ) ;
cmd ( mf, "+FTS=1", T3S ) ; /* make sure DCS is over */
gettrain ( mf, c1cmd [RCV][TRN][session[BR]], cps[session[BR]], &good ) ;
if ( putframe ( ( good ? CFR : FTT ) | disbit, buf, 0, mf, -1 ) ||
! good ) goto F ;
getdata:
outf->w=pagewidth[session[WD]];
outf->h=0;
outf->xres=204.0;
outf->yres=vresolution[session[VR]];
if ( cmd ( mf, c1cmd [RCV][DTA][session[BR]], TO_FT ) != CONNECT )
goto F ; /* +FCERROR -> DCS resent */
if ( receive_data ( mf, outf, session, &nerr ) == 0 ) {
good = nerr < maxpgerr ;
msg ( "I-received -> %s", outf->cfname ) ;
writepending=1 ; /* ppm follows immediately, don't write yet */
rxpage++ ;
} else {
good = 0 ;
}
ckcmd ( mf, 0, 0, TO_RTCMD, NO ) ;
goto F ;
/* III: */
case PRI_EOM:
case PRI_MPS:
case PRI_EOP:
frame &=0xf7 ; /* ignore PRocedure Interrupt bit */
case MPS:
case EOP:
case EOM:
putframe ( ( good ? MCF : RTN ) | disbit, buf, 0, mf, -1 ) ;
if ( good && frame == MPS ) goto getdata ;
else goto F ;
case DCN:
goto B ;
default:
err = msg ( "E3 unrecognized command" ) ;
goto B ;
}
C_timeout:
err = msg ( "E3 no command/response from remote" ) ;
C:
putframe ( DCN, buf, 0, mf, -1 ) ;
B:
ckcmd ( mf, 0, "H", TO_RESET, OK ) ; /* hang up */
if ( rxpage > 0 )
wrpage ( outf, -1 ) ; /* remove last file */
return err ;
}
/* Check for hangup message. Assumes hsc is initialized to a
negative value. Returns 0 if no hangup message, 1 if there
was one. If perr is not null, sets it to 2 if the hsc was
non-zero (error). */
int gethsc ( int *hsc, int *perr )
{
int err=0, i ;
if ( sresponse ( "+FHNG:", hsc ) || sresponse ( "+FHS:", hsc ) ) {
if ( hsc && *hsc > 0 ) {
err = msg ( "E2abnormal termination (code %d)", *hsc ) ;
for ( i=0 ; c2msg[i].min >= 0 ; i++ ) {
if ( *hsc >= c2msg[i].min && *hsc <= c2msg[i].max ) {
msg ( "E %s", c2msg[i].msg ) ;
}
}
if ( perr && ! *perr ) {
*perr = 2 ;
}
} else {
err = 1 ;
}
}
return err ;
}
/* Print remote ID and store DCS values in session as per
responses since last command. */
void getc2dcs ( cap session )
{
char *p ;
if ( ( p = sresponse ( "+FTI:", 0 ) ) != 0 ||
( p = sresponse ( "+FTSI:", 0 ) ) != 0 ) {
msg ( "I- remote ID -> %s", p ) ;
}
if ( ( p = sresponse ( "+FCS:", 0 ) ) != 0 ||
( p = sresponse ( "+FDCS:", 0 ) ) != 0 ) {
str2cap ( p, session ) ;
printcap ( "session", session ) ;
}
}
/* Wait for a starting character XON or DC2. Display & ignore
any other characters received. */
void getstartc ( TFILE *mf )
{
int c, noise ;
for ( noise=0 ; ( c = tgetc ( mf, TO_C2X ) ) != XON && c != DC2 ; noise++ ) {
if ( c == EOF ) {
msg ( "Wno XON/DC2 received after CONNECT") ;
break ;
} else {
msg ( "W-+%s", cname ( c ) ) ;
noise++ ;
}
}
if ( noise )
msg ( "W : %d characters received while waiting to send", noise ) ;
}
/* Class 2 send and receive.
If calling, polls if no files to send, otherwise sends. If
not calling sends documents if files to send, else receives.
When sending, issues +FDIS to change session parameters if
file format changes, then sends +FDT followed by data and a
post-page message determined by format of next page, if any.
Retransmits each page up to NTXRETRY times.
When receiving extracts file format from responses to +FDR or
ATA and saves them in the file. Receives data to a file and
sets page transfer status if too many errors.
Returns 0 if OK or 2 on errors. */
int c2sndrcv (
TFILE *mf, cap local, char *localid,
OFILE *outf, IFILE *inf,
int pages, char *header, faxfont *font,
int maxpgerr, int noretry, int calling )
{
int err=0, done=0, page, pagetry, nerr, c, dp=0 ;
int ppm=0, good, hsc, changed ;
int remtx=0 ;
char *fname=0 ;
cap session = { 0,0,0,0, 0,0,0,0 } ;
char buf [ CMDBUFSIZE ] ;
hsc=-1 ; /* will be set >= 0 on hangup */
if ( sresponse ( "+FPO", 0 ) ) {
remtx = 1 ;
msg ( "N remote has document(s) to send." ) ;
}
if ( calling ) {
if ( pages ) goto send ;
else goto poll ;
} else {
if ( pages ) goto pollserver ;
else goto receive ;
}
/* Class 2 Send */
pollserver:
/* with +FLP[L]=1 the modem should accept +FDT. */
send:
page=1 ;
pagetry=0 ;
while ( ! err && ! done ) {
err = rdpage ( inf, dp, &ppm, local, &changed ) ;
if ( ! err && changed ) {
sprintf ( buf, c20 ? "+FIS=%d,%d,%d,%d" : "+FDIS=%d,%d,%d,%d",
local[0], local[1], local[2], local[3] ) ;
ckcmd ( mf, 0, buf, TO_FT, OK ) ;
if ( gethsc ( &hsc, &err ) ) {
continue ;
}
}
ckcmd ( mf, &err, "+FDT", -TO_C2B, CONNECT ) ;
if ( err || gethsc ( &hsc, &err ) ) {
done=1 ;
continue ;
}
getc2dcs ( session ) ;
if ( ! c20 ) getstartc ( mf ) ;
send_data ( mf, inf, page, pages, local, session, header, font ) ;
pagetry++ ;
if ( c20 ) {
end_data ( mf, session, ppm, &good ) ;
} else {
end_data ( mf, session, 0, 0 ) ;
gethsc ( &hsc, &err ) ;
if ( ! err && hsc < 0 ) {
ckcmd ( mf, &err, ppm == EOP ? "+FET=2" :
ppm == EOM ? "+FET=1" : "+FET=0" , TO_C2PP, OK ) ;
}
gethsc ( &hsc, &err ) ;
if ( ! err && hsc < 0 ) {
if ( sresponse ( "+FPTS:", &good ) ) {
good &= 1 ; /* odd values mean received OK */
} else { /* no +FPTS and +FHNG probably NG */
good = gethsc ( 0, 0 ) ? 0 :
msg ( "W1no +FPTS response, assumed received" ) ;
}
}
}
if ( noretry ) good = 1;
if ( good ) {
fname = inf->page->fname ;
if ( fname ) msg ( "Isent -> %s", fname ) ;
pagetry=0 ;
page++ ;
dp = 1 ;
if ( ppm == EOP ) {
nextipage ( inf, 1 ) ; /* skip ahead to mark all files done */
done = 1 ;
}
} else {
dp = 0 ;
if ( pagetry >= NTXRETRY )
err = msg ( "E2too many page send retries" ) ;
}
if ( gethsc ( &hsc, &err ) ) done=1 ;
if ( good && lastpage ( inf ) ) {
done = 1 ;
}
}
goto done ;
/* Class 2 Receive */
poll:
/* with +FSP[L]=1 and +FPO[LL]: the modem should now accept +FDR. */
receive:
getc2dcs ( session ) ; /* get ATA responses */
done=0 ;
for ( page=0 ; ! err && ! done ; page++ ) {
if ( ! ( err = wrpage ( outf, page ) ) ) {
c = cmd ( mf, "+FDR", -TO_C2R ) ;
switch ( c ) {
case CONNECT:
getc2dcs ( session ) ;
outf->w=pagewidth[session[WD]];
outf->h=0;
outf->xres=204.0;
outf->yres=vresolution[session[VR]];
tput ( mf, &startchar, 1 ) ;
if ( receive_data ( mf, outf, session, &nerr ) == 0 ) {
good = nerr < maxpgerr ;
msg ( "I-received -> %s", outf->cfname ) ;
} else {
good = 0 ;
}
ckcmd ( mf, &err, 0, TO_C2EOR, OK ) ;
if ( err || gethsc ( &hsc, &err ) ) {
wrpage ( outf, +1 ) ;
wrpage ( outf, -1 ) ;
done=1 ;
continue ;
}
if ( ! good ) {
msg ( "Wreception errors" ) ;
ckcmd ( mf, 0, c20 ? "+FPS=2" : "+FPTS=2", T3S, OK ) ;
if ( gethsc ( &hsc, &err ) ) continue ;
}
break ;
case OK:
wrpage ( outf, -1 ) ; /* no more pages */
done=1 ;
if ( gethsc ( &hsc, &err ) ) continue ;
break ;
default:
wrpage ( outf, -1 ) ; /* oops */
err = msg ( "E3receive (+FDR) command failed") ;
break ;
}
}
}
done:
if ( hsc < 0 ) ckcmd ( mf, 0, c20 ? "+FKS" : "+FK", TO_RESET, OK ) ;
return err ;
}
/* Dial the phone number given by string s. If nowait is true
adds a ';' to the dial string to avoid waiting for a
CONNECTion (might allow ersatz polling). Also resets the
global "nframes" if appropriate so getfr() and putframe() know
not to issue +FRH/+FTH. Returns 0 if dialed OK, 1 if busy, 2
on errors. */
int dial ( TFILE *f, char *s, int nowait )
{
int err=0, hsc=-1 ;
char c, dsbuf [ 128 ], *p ;
sprintf ( dsbuf, nowait ? "D%.126s;" : "D%.127s" , s ) ;
msg ( "Idialing %s", dsbuf+1 ) ;
c = cmd ( f, dsbuf, TO_A ) ;
if ( ( p = sresponse ( "+FCSI:", 0 ) ) != 0 ||
( p = sresponse ( "+FCI:", 0 ) ) != 0 ) {
msg ( "I- remote ID -> %s", p ) ;
}
if ( nowait && c == OK ) {
msg ( "Icalled" ) ;
nframes = 1 ;
} else if ( c1 && c == CONNECT ) {
msg ( "Iconnected" ) ;
nframes = 0 ;
} else if ( !c1 && c == OK ) {
msg ( "Iconnected" ) ;
} else if ( c == BUSY ) {
err = msg ( "W1number is busy" ) ;
} else {
err = msg ( "E2dial command failed" ) ;
}
gethsc ( &hsc, err ? 0 : &err ) ;
return err ;
}
/* Figure out which mode the modem answered in (fax, data, voice
or none) based on modem class and responses to the previous
command. Sets crate (connect rate) for DATAMODE and hsc
(hangup status code) if detects a class 2 hangup message. */
enum connectmode { NONE, DATAMODE, FAXMODE, VOICEMODE } ;
enum connectmode ansmode ( int *crate, int *hsc )
{
enum connectmode mode = NONE ;
int x=0 ;
if ( c1 && sresponse ( "CONNECT", &x ) ) {
mode = x ? DATAMODE : FAXMODE ;
}
if ( !c1 && sresponse ( "OK", 0 ) ) {
mode = FAXMODE ;
}
if ( !c1 && ( sresponse ( "CONNECT", &x ) || sresponse ( "+FDM", 0 ) ) ) {
mode = DATAMODE ;
}
if ( sresponse ( "DATA", 0 ) || sresponse ( "CONNECT DATA", 0 ) ) {
mode = DATAMODE ;
sresponse ( "CONNECT", &x ) ;
}
if ( sresponse ( "FAX", 0 ) || sresponse ( "+FCO", 0 ) ) {
mode = FAXMODE ;
}
if ( sresponse ( "VCON", 0 ) ) {
mode = VOICEMODE ;
}
if ( gethsc ( hsc, 0 ) ) {
mode = NONE ;
}
if ( DATAMODE && x ) *crate = x ;
return mode ;
}
/* Answer the phone. Remove our lock if sharing device with
outgoing calls. If waiting for call, wait for modem activity,
else answer phone. Figure out what mode we answered in and
handle call appropriately. Re-lock if necessary. Exec *getty
or *vcmd for data or voice calls. */
int answer ( TFILE *f, char **lkfile,
int wait, int share, int softaa,
char *getty, char *vcmd, char *acmd )
{
int err=0, c ;
int crate=19200, hsc=-1, i ;
enum connectmode mode=NONE ;
if ( ! err && share ) {
err = ttymode ( f, COMMAND ) ;
if ( ! err )
err = unlockall ( lkfile ) ;
}
if ( ! err && wait ) {
msg ( "Iwaiting for activity") ;
tdata ( f, -1 ) ;
msg ( "Iactivity detected") ;
}
if ( ! err && share ) {
msleep ( 500 ) ; /* let other programs lock port */
err = lockall ( lkfile, 1 ) ;
if ( err )
err = msg ( "W1can't answer: can't lock device" ) ;
else
err = ttymode ( f, COMMAND ) ; /* in case it was changed silently */
}
for ( i=0 ; ! err && mode == NONE && ( i==0 || ( i==1 && softaa ) ) ; i++ ) {
c = cmd ( f, wait ? 0 : acmd, ( i==0 && softaa ) ? TO_DATAF : TO_A ) ;
if ( c == DATA ) cmd ( f, c1 ? "O" : 0, TO_A ) ; /* +FAE=1 weirdness */
mode = ansmode ( &crate, &hsc ) ;
switch ( mode ) {
case DATAMODE :
msg ( "Idata call answered") ;
if ( getty && *getty ) {
char buf [ MAXGETTY ] ;
if ( ckfmt ( getty, 6 ) ) {
err = msg ( "E3 too many %%d escapes in command (%s)", getty ) ;
} else {
sprintf ( buf, getty, crate, crate, crate, crate, crate, crate ) ;
msg ( "Iexec'ing /bin/sh -c \"%s\"" , buf ) ;
execl ( "/bin/sh" , "sh" , "-c" , buf , (void*) 0 ) ;
err = msg ( "ES2exec failed:" ) ;
}
} else {
err = msg ( "E2no getty command defined for data call" ) ;
}
break ;
case FAXMODE :
nframes = 0 ;
msg ( "Ifax call answered") ;
break ;
case VOICEMODE :
msg ( "Ivoice call answered") ;
if ( vcmd && *vcmd ) {
char buf [ MAXGETTY ] ;
if ( ckfmt ( vcmd, 6 ) ) {
} else {
sprintf ( buf, vcmd, f->fd, f->fd, f->fd, f->fd, f->fd, f->fd ) ;
msg ( "Iexec'ing /bin/sh -c \"%s\"" , buf ) ;
execl ( "/bin/sh" , "sh" , "-c" , buf , (void*) 0 ) ;
err = msg ( "ES2exec failed:" ) ;
}
} else {
err = msg ( "E2no voice command defined for voice call" ) ;
}
break ;
case NONE:
if ( i==0 && softaa && hsc < 0 && getty && *getty ) {
int j ; /* switch to fax for 2nd try */
for ( j=0 ; j<3 ; j++ )
if ( cmd ( f, c1 ? "+FCLASS=1" :
( c20 ? "+FCLASS=2.0" : "+FCLASS=2" ), -TO_RESET )
== OK ) break ;
wait = 0 ;
acmd = ANSCMD ;
} else {
err = msg ( "E3unable to answer call") ;
}
break ;
default:
err = msg ( "E3can't happen(answer)" ) ;
break ;
}
}
return err ;
}
/* Initialize modem. Determine class to use and issue
class-specific fax initialization commands. If poll is true,
issues commands to enable polling also. Returns 0 or 3 if a
mandatory setup command fails. */
int modem_init ( TFILE *mf, cap c, char *id,
int calling, int poll, int capsset, int *preverse )
{
int err=0, t=-TO_RESET ;
char buf [ CMDBUFSIZE ], model [ CMDBUFSIZE ] = "" ;
char **p, *q, *modelq [2][4] = { { "+FMFR?", "+FMDL?", 0 },
{ "+FMI?", "+FMM?", "+FMR?", 0 } } ;
/* diasable command echo and get firmware revision */
cmd ( mf, "E0", t ) ;
if ( cmd ( mf, "I3", t ) == OK ) {
getresp ( "", model, CMDBUFSIZE-1 ) ;
strcat ( model, " " ) ;
}
/* if not already chosen, pick safest class; set it */
if ( ! err && ! c1 && !c2 && ! c20 ) {
if ( cmd ( mf, "+FCLASS=?", t ) != OK ) {
err = msg ("E3 modem does not support fax" ) ;
} else {
if ( strinresp ( "2.0" ) ) c20 = 1 ;
else if ( strinresp ( "2" ) ) ;
else if ( strinresp ( "1" ) ) c1 = 1 ;
else err = msg ("E3 can't determine fax modem class support" ) ;
if ( strstr ( model, "Sportster" ) ) { /* USR Sporsters are buggy */
c1 = 1 ;
c2 = c20 = 0 ;
}
}
}
ckcmd ( mf, &err, c1 ? "+FCLASS=1" :
( c20 ? "+FCLASS=2.0" : "+FCLASS=2" ), t, OK ) ;
/* get make & model if using Class 2 or 2.0 */
if ( ! c1 ) {
for ( p = modelq [ c20 ] ; ! err && *p ; p++ ) {
if ( cmd ( mf, *p, t ) == OK ) {
getresp ( "", model, CMDBUFSIZE-1 ) ;
strcat ( model, " " ) ;
}
}
if ( ! c1 && strstr ( model, "Multi-Tech" ) ) {
*preverse = 0 ;
msg ("I Multi-Tech bit order set" ) ;
}
}
if ( ! err )
msg ( "I using %sin class %s", model, c1 ? "1" : c20 ? "2.0" : "2" ) ;
/* get maximum modem speed if not already set (Class 1 only) */
if ( ! err && c1 && ! capsset ) {
int i ;
char *c1spstr [6] = { "24", "48", "72", "96", "121", "145" } ;
ckcmd ( mf, &err, calling ? "+FTM=?" : "+FRM=?", t, OK ) ;
for ( i=0 ; i < 6 && strinresp ( c1spstr[i] ) ; i++ ) ;
c[1]=i?i-1:0;
}
/* issue essential commands and set/get modem capabilities (Class 2 only) */
if ( ! err && ! c1 ) {
if ( c20 ) {
ckcmd ( mf, 0, "+FIP", t, OK ) ;
ckcmd ( mf, 0, "+FNR=1,1,1,1", t, OK ) ;
}
ckcmd ( mf, &err, "+FCR=1", t, OK ) ;
if ( ! capsset ) {
if ( cmd ( mf, c20 ? "+FIS?" : "+FDIS?", -t ) == OK &&
( q = strinresp ( "," ) ) ) {
str2cap ( q-1, c ) ;
} else {
msg ( "W can't get modem capabilities, set to default" ) ;
capsset = 1 ;
}
}
if ( capsset ) {
sprintf ( buf, c20 ? "+FCC=%d,%d,%d,%d,%d,%d,%d,%d" :
"+FDCC=%d,%d,%d,%d,%d,%d,%d,%d",
c[0], c[1], c[2], c[3], c[4], c[5], c[6], c[7] ) ;
ckcmd ( mf, 0, buf, -t, OK ) ;
}
sprintf ( buf, c20 ? "+FLI=\"%.*s\"" : "+FLID=\"%.*s\"" ,
CMDBUFSIZE-9, id ) ;
ckcmd ( mf, 0, buf, -t, OK ) ;
if ( ! err && poll ) {
ckcmd ( mf, 0, c20 ? "+FSP=1" : "+FSPL=1", -t, OK ) ;
sprintf ( buf, c20 ? "+FPI=\"%.*s\"" : "+FCIG=\"%.*s\"" ,
CMDBUFSIZE-9, id ) ;
ckcmd ( mf, 0, buf, -t, OK ) ;
}
}
return err ;
}
/* the following are global so can terminate properly on signal */
char *icmd[3][ MAXICMD+1 ] = {{0},{0},{0}} ; /* initialization commands */
TFILE faxdev = { -1, 0,0, {0}, 0, 0 } ; /* modem */
char *lkfile [ MAXLKFILE+1 ] = {0} ; /* lock file names */
IFILE ifile = { 0 } ; /* files being sent */
int locked = 0 ; /* modem locked */
/* print names of files not sent and reset modem before
exiting. */
int cleanup ( int err )
{
/* log names of files not sent */
logifnames ( &ifile, "I failed -> %s" ) ;
if ( ! locked && faxdev.fd >= 0 )
end_session ( &faxdev, icmd[2], lkfile, err != 4 ) ;
msg ( "Idone, returning %d (%s)", err,
errormsg [ err >= 0 && err <= 5 ? err : 6 ] ) ;
return err ;
}
/* signal handler */
void onsig ( int sig )
{
msg ( "E terminating on signal %d", sig ) ;
exit ( cleanup ( 5 ) ) ;
}
/* Fax send/receive program for Class 1, 2 and 2.0 fax
modems. Returns 0 on success, 1 if number busy or device
locked, 2 for errors, 3 for protocol errors, 4 if no modem
response, 5 on signal. */
int main( int argc, char **argv)
{
int err=0, doneargs=0, c=0, i ;
int testing=0, calling=0 ;
int nicmd[3]={0,0,0}, nlkfile=0, nverb=0 ;
char *faxfile = FAXFILE ;
int softaa=0, share=0, wait=0, reverse=1, ignerr=0, noretry=0, hwfc=0 ;
int capsset=0 ;
char *getty = "", *vcmd = "", *acmd=ANSCMD ;
cap local = { DEFCAP } ;
char localid [ IDLEN + 1 ] = DEFID ;
int maxpgerr = MAXPGERR ;
time_t now ;
char *header = 0, headerbuf [ MAXLINELEN ] ;
char *fontname = 0 ;
faxfont font ;
OFILE ofile ;
int pages = 0 ;
char *phnum="", *ansfname = DEFPAT ;
char fnamepat [ EFAX_PATH_MAX ] ;
/* print initial message to both stderr & stdout */
argv0 = argv[0] ;
verb[1] = "ewia" ;
msg ( "I " Version " " Copyright ) ;
argv0 = efaxbasename ( argv0 ) ;
msg ( "A compiled "__DATE__ " " __TIME__ ) ;
verb[1] = "" ;
while ( ! err && ! doneargs &&
( c = nextopt ( argc,argv,
"a:c:d:e:f:g:h:i:j:k:l:o:p:q:r:st:v:wx:T" ) ) != -1 ) {
switch (c) {
case 'a':
acmd = nxtoptarg ;
break ;
case 'c':
err = str2cap ( nxtoptarg, local ) ;
capsset = 1 ;
break ;
case 'l':
if ( strlen ( nxtoptarg ) > IDLEN )
msg("Wlocal ID (%s) truncated to %d characters", nxtoptarg, IDLEN ) ;
if ( strspn ( nxtoptarg, " +0123456789" ) != strlen ( nxtoptarg ) )
msg("Wlocal ID (%s) has non-standard characters", nxtoptarg ) ;
sprintf ( localid, "%*.*s", IDLEN, IDLEN, nxtoptarg ) ;
break ;
case 'i':
if ( nicmd[0] < MAXICMD ) icmd[0][ nicmd[0]++ ] = nxtoptarg ;
else err = msg ( "E2too many '-i' options");
break ;
case 'j':
if ( nicmd[1] < MAXICMD ) icmd[1][ nicmd[1]++ ] = nxtoptarg ;
else err = msg ( "E2too many '-j' options");
break ;
case 'k':
if ( nicmd[2] < MAXICMD ) icmd[2][ nicmd[2]++ ] = nxtoptarg ;
else err = msg ( "E2too many '-k' options");
break ;
case 'h':
header = nxtoptarg ;
break ;
case 'f':
fontname = nxtoptarg ;
break ;
case 'd':
faxfile = nxtoptarg ;
break ;
case 'e':
vcmd = nxtoptarg ;
break ;
case 'g':
getty = nxtoptarg ;
break ;
case 'o': /* most protocol options are globals */
for ( ; *nxtoptarg ; nxtoptarg++ )
switch ( *nxtoptarg ) {
case '0' : c20 = 1 ; break ;
case '1' : c1 = 1 ; break ;
case '2' : c2 = 1 ; break ;
case 'a' : softaa = 1 ; break ;
case 'e' : ignerr = 1 ; break ;
case 'f' : vfc = 1 ; break ;
case 'h' : hwfc = 1 ; break ;
case 'l' : lockpolldelay /= 2 ; break ;
case 'n' : noretry = 1 ; break ;
case 'r' : reverse = 0 ; break ;
case 'x' : startchar = XON ; break ;
case 'z' : cmdpause += T_CMD ; break ;
default : msg ( "Wunrecognized protocol option (%c)", *nxtoptarg ) ;
}
break ;
case 'q':
if ( sscanf ( nxtoptarg , "%d", &maxpgerr ) != 1 || maxpgerr < 0 )
err=msg ("E2bad quality (-q) argument (%s)", nxtoptarg ) ;
break;
case 'r':
ansfname = nxtoptarg ;
break;
case 's':
share = 1 ;
break;
case 't':
calling=1;
/* fall through */
case 'p':
if ( argv [ argc ] ) err = msg ("E2can't happen(unterminated argv)") ;
if ( ! err ) err = newIFILE ( &ifile, argv + nxtoptind ) ;
pages = argc - nxtoptind - ( c == 'p' ? 1 : 0 ) ;
pages = pages < 0 ? 0 : pages ;
phnum = nxtoptarg ;
doneargs=1 ;
break;
case 'v':
verb[nverb] = nxtoptarg ;
nverb=1;
break ;
case 'w':
wait = 1 ;
break ;
case 'x':
if ( nlkfile < MAXLKFILE ) lkfile[ nlkfile++ ] = nxtoptarg ;
else err = msg ( "E2too many lock files" ) ;
break ;
case 'T': /* test: begin+end session */
testing=1;
doneargs=1 ;
break ;
default : fprintf ( stderr, Usage, argv0 ) ; err = 2 ; break ;
}
}
for ( i=0 ; i<argc ; i++ )
msg ( "Aargv[%d]=%s", i, argv[i]) ;
if ( ! nicmd[2] ) icmd[2][nicmd[2]++] = "H" ; /* default -k command */
readfont ( fontname, &font ) ;
if ( ! header ) {
char tmp [ MAXLINELEN ] ;
now = time ( 0 ) ;
strftime ( tmp, MAXLINELEN, "%c %%s P. %%%%d", localtime ( &now ) ) ;
sprintf ( header = headerbuf, tmp, localid ) ;
}
if ( ! err ) {
err = begin_session ( &faxdev, faxfile,
!c1 && !c20 && reverse, /* Class 2 rx bit reversal */
hwfc, lkfile, COMMAND, onsig ) ;
if ( ! err ) err = setup ( &faxdev, icmd[0], ignerr ) ;
if ( ! err ) err = modem_init ( &faxdev, local, localid,
calling, calling && !pages, capsset,
&reverse ) ;
if ( ! err ) err = setup ( &faxdev, icmd[1], ignerr ) ;
if ( err == 1 ) locked = 1 ;
}
if ( ! err && ! testing ) {
if ( calling ) {
err = dial ( &faxdev, phnum, 0 ) ;
} else {
err = answer ( &faxdev, lkfile, wait, share, softaa,
getty, vcmd, acmd ) ;
if ( err == 1 ) locked = 1 ;
}
now = time(0) ; /* do it here so use reception time */
strftime ( fnamepat, EFAX_PATH_MAX, ansfname, localtime ( &now ) ) ;
strncat ( fnamepat, ".%03d", EFAX_PATH_MAX - strlen ( fnamepat ) ) ;
newOFILE ( &ofile, O_TIFF_FAX, fnamepat, 0, 0, 0, 0 ) ;
if ( ! err ) {
if ( c1 ) {
err = c1sndrcv ( &faxdev, local, localid,
&ofile, &ifile, pages, header, &font,
maxpgerr, noretry, calling ) ;
} else {
err = c2sndrcv ( &faxdev, local, localid,
&ofile, &ifile, pages, header, &font,
maxpgerr, noretry, calling ) ;
}
}
}
return cleanup ( err ) ;
}
efax-0.9/efix.c 100644 144 144 31105 6666677407 11603 0 ustar edc users #define Copyright "Copyright 1999 Ed Casas"
#define Version "efix v 0.3"
/*
Copyright (C) 1999 Ed Casas
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Please contact the author if you wish to use efax or efix in
ways not covered by the GNU GPL.
You may contact the author by e-mail at: edc@cce.com, by mail
at: 2629 West 3rd Ave, Vancouver, BC, Canada, V6K 1M4, or by
fax at: +1 604 734 5291.
*/
const char *Usage =
"Usage:\n"
" %s [ option ]... file... \n"
"Options (defaults):\n"
" -i f input format (auto):\n"
" fax fax (\"Group3\") 1-D coded image\n"
" text text\n"
" pbm raw PBM (portable bit map)\n"
" tiffg3 TIFF, Group 3 fax compression\n"
" tiffraw TIFF, no compression\n"
" pcx mono PCX\n"
" dcx mono DCX\n"
" -o f output format (tiffg3):\n"
" fax fax (\"Group3\") 1-D coded image\n"
" pbm Portable Bit Map\n"
" pgm Portable Gray Map (decimated by 4)\n"
" pcl HP-PCL (e.g. HP LaserJet)\n"
" ps Postscript (e.g. Apple Laserwriter)\n"
" tiffg3 TIFF, Group 3 fax compression\n"
" tiffraw TIFF, no compression\n"
" pcx mono PCX\n"
" dcx mono DCX\n"
" -n pat printf() pattern for output file name (ofile)\n"
" -f fnt use PBM font file fnt for text (built-in)\n"
" -l n lines per text page (66)\n"
" -v lvl print messages of type in string lvl (ewi)\n"
" -s XxY scale input by X and Y (Y optional) (1x1)\n"
" -r XxY resolution of output is X by Y (dpi, Y optional) (204x196)\n"
" -R XxY resolution of input is X by Y (dpi, Y optional) (204x196)\n"
" -p WxH pad/truncate output to width W by height H (215x297mm)\n"
" -d R,D displace output right R, down D (opposite if -ve) (0,0)\n"
" -O f overlay file f (none)\n"
" -M ignore other options and base64 (MIME) encode stdin to stdout\n"
"\n"
"Add 'in', 'cm', 'mm', or 'pt' to -p and -d arguments (default in[ches]).\n"
"Default output size and resolution is same as input (if known).\n"
;
#include <ctype.h> /* ANSI C */
#include <limits.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "efaxlib.h"
#include "efaxmsg.h"
#ifndef INT_MAX
#define INT_MAX 32767
#endif
/* Allowed input and output formats. *** MUST match enum *** */
char *iformatstr[] = { " 3text", " 1pbm", " 2fax", " 4tiffg3", " 4tiffraw",
" 6pcx", " 6pcxraw", " 8dcx", 0 } ;
char *oformatstr[] = { " 1pbm" , " 2fax", " 3pcl", " 4ps", " 5pgm",
" 7tiffg3", " 8tiffraw",
"11pcx", "12pcxraw", "13dcx", 0 } ;
/* Look up a string in a NULL-delimited table where the first
character of each string is the digit to return if the rest of
the string matches. Returns the value of the digit for the
matching string or -1 if no matches. */
int lookup ( char **tab, char *s )
{
char **p ;
for ( p=tab ; p && *p && strcmp ( *p+2, s ) ; p++ ) ;
return p && *p ? atoi ( *p ) : -1 ;
}
/* Extract pair of values from string. If it's a `dim'ension,
two values are required and they are converted to inches, else
the y value is optional. Returns 0 or 2 on error. */
int getxy ( char *arg, float *x, float *y, int dim )
{
int i, n, nc=0, err=0 ;
char c ;
static char *unitstr[] = { " 0in", " 1cm", " 2mm", " 3pt", 0 } ;
static float unitval[] = { 1.0, 2.54, 25.4, 72.0, 1.0 } ;
if ( ! arg )
err = msg ( "E2 missing argument" ) ;
if ( !x || !y )
err = msg ( "E2 can't happen (getxy)" ) ;
if ( ! err ) {
n = sscanf ( arg , "%f%c%f%n", x, &c, y, &nc ) ;
switch ( n ) {
case 0 : err = msg ( "E2bad X value in (%s)", arg ) ; break ;
case 2 : err = msg ( "E2bad Y value in (%s)", arg ) ; break ;
}
}
if ( ! err ) {
if ( dim ) {
if ( n != 3 ) {
err = msg ( "Emissing Y dimension in (%s)", arg ) ;
} else {
while ( arg [ nc ] && isspace ( arg [ nc ] ) ) nc++ ;
if ( arg [ nc ] ) {
if ( ( i = lookup ( unitstr, arg+nc ) ) >= 0 ) {
*x /= unitval [ i ] ;
*y /= unitval [ i ] ;
} else {
err = msg ( "E2bad units: `%s'", arg+nc ) ;
}
}
}
} else {
if ( n == 1 ) *y = *x ;
}
}
if ( ! err )
msg ( "Aconverted (%s) into %f x %f", arg, *x, *y ) ;
return err ;
}
/* Copy stdin to stdout while applying base64 (RFC 1521)
encoding. This encoding must be applied after the file is
complete since some output formats (e.g. TIFF) require seeking
backwards within the (binary) file. */
int base64encode ( void )
{
int err=0, c ;
uchar n=0, m=0, bits=0 ;
static uchar chartab[65] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/" ;
while ( ( c = fgetc ( stdin ) ) >= 0 ) {
switch ( n ) {
case 0:
putc ( chartab [ c >> 2 ], stdout ) ;
bits = c & 0x3 ;
n = 1 ;
break ;
case 1:
putc ( chartab [ (bits << 4) | ( c >> 4 ) ], stdout ) ;
bits = c & 0xf ;
n = 2 ;
break ;
case 2:
putc ( chartab [ (bits << 2) | ( c >> 6 ) ], stdout ) ;
putc ( chartab [ c & 0x3f ], stdout ) ;
n = 0 ;
if ( ++m >= 18 ) {
putc ( '\n', stdout ) ;
m = 0 ;
}
break ;
}
}
switch ( n ) {
case 0:
break ;
case 1:
putc ( chartab [ (bits << 4) | ( 0 >> 4 ) ], stdout ) ;
putc ( '=', stdout ) ;
putc ( '=', stdout ) ;
break ;
case 2 :
putc ( chartab [ (bits << 2) | ( 0 >> 6 ) ], stdout ) ;
putc ( '=', stdout ) ;
break ;
}
putc ( '\n', stdout ) ;
return err ;
}
int main( int argc, char **argv)
{
int err=0, done=0, i, c ;
int nr, pels, ovnr, ovpels, no ; /* run/pixel/repeat counts */
int linesout ;
int page, ilines, olines ; /* page & line counts */
int xs, ys, w, h, ixsh, iysh ; /* integer scale, size & shift */
short runs [ MAXRUNS ] , ovruns [ MAXRUNS ] ;
float /* defaults: */
xsc=1.0, ysc=1.0, /* scale */
xsh=0.0, ysh=0.0, /* shift */
dxres = 204.145, /* o/p res'n: 1728/215mm * 25.4 x */
dyres = 195.58, /* 7.7 * 25.4 */
dxsz = 215 / 25.4, /* o/p size: 8.5" x A4 */
dysz = 297 / 25.4 ;
float /* arguments: */
axres = 0, ayres = 0, axsz = 0, aysz = 0, ainxres=0, ainyres=0 ;
float /* values used: */
xres = 0, yres = 0, xsz = 0, ysz = 0 ;
IFILE ifile, ovfile ;
OFILE ofile ;
char **ifnames, *ovfnames [ 2 ] = { 0, 0 } ;
int iformat=I_AUTO, oformat=O_TIFF_FAX, pglines=0 ;
char *ofname=0 ;
faxfont font, *pfont=0 ; /* text font */
/* initialize */
argv0 = argv[0] ;
/* process arguments */
while ( !err && (c=nextopt(argc,argv,"n:i:o:O:v:l:f:r:s:p:d:R:M") ) != -1) {
switch ( c ) {
case 'n':
ofname = nxtoptarg ;
break ;
case 'i':
if ( ( iformat = lookup ( iformatstr, nxtoptarg ) ) < 0 )
err = msg ( "E2invalid input type (%s)", nxtoptarg ) ;
break ;
case 'o':
if ( ( oformat = lookup ( oformatstr, nxtoptarg ) ) < 0 )
err = msg ( "E2invalid output type (%s)", nxtoptarg ) ;
break ;
case 'O':
ovfnames[0] = nxtoptarg ;
break ;
case 'v':
verb[0] = nxtoptarg ;
msg ( "A " Version ) ;
for ( i=0 ; i<argc ; i++ ) msg ( "Aargv[%d]=%s", i, argv[i]) ;
break ;
case 'l':
if ( sscanf ( nxtoptarg , "%d", &pglines ) != 1 || pglines <= 0 ) {
err = msg ( "E2bad page length (%s)", nxtoptarg ) ;
pglines = 0 ;
}
break ;
case 'f' :
if ( ! ( err = readfont ( nxtoptarg, &font ) ) )
pfont = &font ;
break ;
case 's' : err = getxy ( nxtoptarg, &xsc , &ysc , 0 ) ; break ;
case 'r' : err = getxy ( nxtoptarg, &axres, &ayres, 0 ) ; break ;
case 'R' : err = getxy ( nxtoptarg, &ainxres, &ainyres, 0 ) ; break ;
case 'p' : err = getxy ( nxtoptarg, &axsz , &aysz , 1 ) ; break ;
case 'd' : err = getxy ( nxtoptarg, &xsh , &ysh , 1 ) ; break ;
case 'M' : err = base64encode() ; done=1 ; break ;
default : fprintf ( stderr, Usage, argv0 ) ; err = 2 ; break ;
}
}
msg ( "I " Version " " Copyright ) ;
if ( ! err && ! done ) {
if ( nxtoptind < argc ) {
ifnames = argv + nxtoptind ;
if ( argv [ argc ] ) {
err = msg ("E2can't happen(unterminated argv)") ;
} else {
newIFILE ( &ifile, ifnames ) ;
}
} else {
err = msg ( "E3 missing input file name" ) ;
}
if ( pfont ) ifile.font = pfont ;
if ( pglines ) ifile.pglines = pglines ;
newIFILE ( &ovfile, ovfnames ) ;
newOFILE ( &ofile, oformat, ofname, 0, 0, 0, 0 ) ;
}
for ( page = 0 ; ! err && ! done ; page++ ) {
if ( nextipage ( &ifile, page != 0 ) ) {
done=1 ;
continue ;
}
/* set output size and resolution equal to input if none specified */
if ( ainxres > 0 ) ifile.page->xres = ainxres ;
if ( ainyres > 0 ) ifile.page->yres = ainyres ;
if ( ifile.page->xres <= 0 ) ifile.page->xres = dxres ;
if ( ifile.page->yres <= 0 ) ifile.page->yres = dyres ;
xres = axres > 0 ? axres : ifile.page->xres ;
yres = ayres > 0 ? ayres : ifile.page->yres ;
xsz = axsz > 0 ? axsz : ( ifile.page->w > 0 ?
ifile.page->w / ifile.page->xres : dxsz ) ;
ysz = aysz > 0 ? aysz : ( ifile.page->h > 0 ?
ifile.page->h / ifile.page->yres : dysz ) ;
w = xsz * xres + 0.5 ; /* output dimensions in pixels */
h = ysz * yres + 0.5 ;
ixsh = xsh * xres ; /* x/y shifts in pixels/lines */
iysh = ysh * yres ;
if ( ( w & 7 ) != 0 ) /* just about everything requires... */
msg ("Iimage width rounded to %d pixels",
w = ( w + 7 ) & ~7 ) ;
if ( ofile.format == O_PGM && h & 3 ) /* PGM x4 decimation requires... */
msg ("I PGM image height rounded up to %d lines",
h = ( h + 3 ) & ~3 ) ;
if ( w <= 0 || h <= 0 || xres < 0 || yres < 0 )
err = msg ( "E2negative/zero scaling/size/resolution" ) ;
if ( ofile.format == O_PCL && /* check for strange PCL resolutions */
( xres != yres || ( xres != 300 && xres != 150 && xres != 75 ) ) )
msg ( "Wstrange PCL resolution (%.0fx%.0f)", xres, yres ) ;
if ( w > MAXBITS*8 ) /* make sure output will fit... */
err = msg( "E2requested output width too large (%d pixels)", w ) ;
ofile.w = w ;
ofile.h = h ;
ofile.xres = xres ;
ofile.yres = yres ;
/* scale according to input file resolution */
xs = 256 * xsc * xres / ifile.page->xres + 0.5 ;
ys = 256 * ysc * yres / ifile.page->yres + 0.5 ;
if ( xs <= 0 || ys <= 0 )
err = msg ( "E2negative/zero scaling" ) ;
if ( *ovfnames ) /* [re-]open overlay file */
if ( nextipage ( &ovfile , 0 ) ) {
err=2 ;
continue ;
}
if ( nextopage ( &ofile, page ) ) {
err=2 ;
continue ;
}
linesout=0 ;
/* y-shift */
if ( iysh > 0 ) {
writeline ( &ofile, ( ( *runs = w ), runs ), 1, iysh ) ;
linesout += iysh ;
} else {
for ( i=0 ; i < -iysh ; i++ )
readline ( &ifile, runs, 0 ) ;
}
/* copy input to output */
olines = ilines = 0 ;
while ( linesout < h ) {
if ( ( nr = readline ( &ifile, runs, &pels ) ) < 0 )
break ;
else
ilines++ ;
if ( *ovfnames ) {
if ( ( ovnr = readline ( &ovfile, ovruns, &ovpels ) ) >= 0 )
nr = runor ( runs, nr, ovruns, ovnr, 0, &pels ) ;
}
/* x-scale, x-shift & x-pad input line */
pels = ( xs == 256 ) ? pels : xscale ( runs, nr, xs ) ;
pels += ( ixsh == 0 ) ? 0 : xshift ( runs, nr, ixsh ) ;
nr = ( pels == w ) ? nr : xpad ( runs, nr, w - pels ) ;
/* y-scale by deleting/duplicating lines. */
no = ( ( ilines * ys ) >> 8 ) - olines ;
if ( linesout + no > h ) no = h - linesout ;
olines += no ;
writeline ( &ofile, runs, nr, no ) ;
linesout += no ;
}
/* y-pad */
if ( linesout < h )
writeline ( &ofile, ( ( *runs = w ), runs ), 1, h - linesout ) ;
if ( ferror ( ifile.f ) ) err = msg ( "ES2input error:" ) ;
}
nextopage ( &ofile, EOF ) ;
return err ;
}
efax-0.9/efaxlib.h 100644 144 144 14403 6665315510 12250 0 ustar edc users #ifndef _EFAXLIB_H
#define _EFAXLIB_H
#include <stdio.h>
#define EFAX_PATH_MAX 1024
/* T.4 fax encoding/decoding */
#ifndef uchar
#define uchar unsigned char
#endif
#define DEFPGLINES 66 /* default lines per page */
/* Buffer sizes. */
/* The maximum scan line width, MAXRUNS, is conservatively
set at 8k pels. This is enough for the longest standard T.4 coding line
width (2432 pels), the longest encodeable run (2623 pels) and with
32-pel-wide characters allows up to 256 characters per line. Converted
to T.4 codes, each pair of runs takes up to 25 bits to code. MAXCODES
must also be at least the maximum minimum line length (1200 cps*40 ms ~=
48 bytes). */
#define MAXRUNS 8192
#define MAXBITS (MAXRUNS/8+1)
#define MAXCODES (MAXRUNS*25/8/2+1)
/* Line/font size limits */
#define MAXLINELEN 256 /* maximum length of string */
#define MAXFONTW 32 /* maximum char width */
#define MAXFONTH 48 /* maximum char height */
#define MAXFONTBUF (MAXFONTW*MAXFONTH/8*256) /* PBM font buffer size */
/* Longest run encodeable by the T.4 encoding tables used. */
#define MAXRUNLEN (2560+63)
/* Codes for EOL and number of EOLs required for RTC */
#define EOLCODE 1
#define EOLBITS 12
#define RTCEOL 5
/* Fonts */
#define STDFONTW 8 /* the built-in font width, height & size */
#define STDFONTH 16
#define STDFONTBUF 4096
typedef struct fontstruct {
int h, w ;
uchar buf [ MAXFONTBUF ] ;
short offset [ 256 ] ;
} faxfont ;
extern uchar stdfont [ ] ; /* compressed bit map for built-in font */
int readfont ( char *fname, faxfont *font ) ;
/* T.4 Encoding/Decoding */
typedef struct t4tabstruct {
short code, bits, rlen ; /* code, bits, run length */
} t4tab ;
extern t4tab wtab [ ( 64 + 27 + 13 ) + 1 ] ; /* white runs */
extern t4tab btab [ ( 64 + 27 + 13 ) + 1 ] ; /* black runs */
typedef struct dtabstruct { /* decoder table entry */
struct dtabstruct *next ;
short bits, code ;
} dtab ;
/* Image Input */
#define bigendian ( * (uchar*) &short256 )
extern short short256 ;
/* input, output and page file formats */
#define NIFORMATS 9
#define NOFORMATS 14
#define NPFORMATS 5
enum iformats { I_AUTO=0, I_PBM=1, I_FAX=2, I_TEXT=3, I_TIFF=4,
I_DFAX=5, I_PCX=6, I_RAW=7, I_DCX=8 } ;
#define IFORMATS { "AUTO", "PBM", "FAX", "TEXT", "TIFF", \
"DFAX", "PCX", "RAW", "DCX" } ;
enum oformats { O_AUTO=0, O_PBM=1, O_FAX=2, O_PCL=3, O_PS=4,
O_PGM=5, O_TEXT=6, O_TIFF_FAX=7, O_TIFF_RAW=8, O_DFAX=9,
O_TIFF=10, O_PCX=11, O_PCX_RAW=12, O_DCX=13 } ;
#define OFORMATS { "AUTO", "PBM", "FAX", "PCL", "PS", \
"PGM", "TEXT", "TIFF", "TIFF", "DFAX", \
"TIFF", "PCX", "PCX", "DCX" }
enum pformats { P_RAW=0, P_FAX=1, P_PBM=2, P_TEXT=3, P_PCX=4 } ;
#define PFORMATS { "RAW", "FAX", "PBM", "TEXT", "PCX" }
extern char *iformatname [ NIFORMATS ] ;
extern char *oformatname [ NOFORMATS ] ;
extern char *pformatname [ NPFORMATS ] ;
typedef struct decoderstruct {
long x ; /* undecoded bits */
short shift ; /* number of unused bits - 9 */
dtab *tab ; /* current decoding table */
int eolcnt ; /* EOL count for detecting RTC */
} DECODER ;
void newDECODER ( DECODER *d ) ;
#define IFILEBUFSIZE 512
#define MAXPAGE 360 /* number of A4 pages in a 100m roll */
typedef struct PAGEstruct { /* page data */
char *fname ; /* file name */
long offset ; /* location of data within file */
int w, h ; /* pel and line counts */
float xres, yres ; /* x and y resolution, dpi */
uchar format ; /* image coding */
uchar revbits ; /* fill order is LS to MS bit */
} PAGE ;
typedef struct ifilestruct { /* input image file */
/* data for each pages */
PAGE *page, *lastpage ; /* pointers to current and last page */
PAGE pages [ MAXPAGE ] ; /* page data */
long next ; /* offset to next page (while scanning only) */
/* data for current input page */
FILE *f ; /* current file pointer */
int lines ; /* scan lines remaining in page */
uchar bigend ; /* TIFF: big-endian byte order */
DECODER d ; /* FAX: T.4 decoder state */
faxfont *font ; /* TEXT: font to use */
int pglines ; /* TEXT: text lines per page */
char text [ MAXLINELEN ] ; /* TEXT: current string */
int txtlines ; /* TEXT: scan lines left in text l. */
int charw, charh, lmargin ; /* TEXT: desired char w, h & margin */
} IFILE ;
int newIFILE ( IFILE *f, char **fname ) ;
void logifnames ( IFILE *f, char *s ) ;
int nextipage ( IFILE *f, int dp ) ;
int lastpage ( IFILE *f ) ;
int readline ( IFILE *f, short *runs, int *pels ) ;
/* Image Output */
typedef struct encoderstruct {
long x ; /* unused bits */
short shift ; /* number of unused bits - 8 */
} ENCODER ;
void newENCODER ( ENCODER *e ) ;
typedef struct ofilestruct { /* input image file state */
FILE *f ; /* file pointer */
int format ; /* file format */
char *fname ; /* file name pattern */
float xres, yres ; /* x and y resolution, dpi */
int w, h ; /* width & height, pixels */
int lastpageno ; /* PS: last page number this file */
int pslines ; /* PS: scan lines written to file */
int bytes ; /* TIFF: data bytes written */
ENCODER e ; /* T.4 encoder state */
char cfname [ EFAX_PATH_MAX + 1 ] ; /* current file name */
} OFILE ;
void newOFILE ( OFILE *f, int format, char *fname,
float xres, float yres, int w, int h ) ;
int nextopage ( OFILE *f, int page ) ;
void writeline ( OFILE *f, short *runs, int nr, int no ) ;
/* Scan Line Processing */
uchar *putcode ( ENCODER *e, short code , short bits , uchar *buf ) ;
uchar *runtocode ( ENCODER *e, short *runs, int nr, uchar *buf ) ;
/* int bittorun ( uchar *buf, int n, short *runs ) ; */
int texttorun ( uchar *txt, faxfont *font, short line,
int w, int h, int lmargin,
short *runs, int *pels ) ;
int xpad ( short *runs, int nr, int pad ) ;
int xscale ( short *runs, int nr, int xs ) ;
int xshift ( short *runs, int nr, int s ) ;
int runor ( short *a, int na, short *b, int nb, short *c, int *pels ) ;
/* Bit reversal lookup tables (note that the `normalbits' array
is the one actually used for the bit reversal. */
uchar reversebits [ 256 ], normalbits [ 256 ] ;
void initbittab(void) ;
/* Other Stuff */
int ckfmt ( char *p, int n ) ;
#endif
efax-0.9/efaxlib.c 100644 144 144 171427 6675741001 12274 0 ustar edc users /*
efaxlib.c - utility routines for efax
Copyright 1995 Ed Casas
*/
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include "efaxmsg.h"
#include "efaxlib.h"
#ifndef SEEK_SET
#define SEEK_SET 0
#endif
#define DEFXRES 204.145 /* fax x and y resolution in dpi */
#define DEFYRES 195.58
#define DEFWIDTH 1728 /* 215x297 mm image at fax resolution */
#define DEFHEIGHT 2287
extern t4tab wtab [ ( 64 + 27 + 13 ) + 1 ] ; /* T.4 coding tables */
extern t4tab btab [ ( 64 + 27 + 13 ) + 1 ] ;
short short256 = 256 ; /* for endian-ness detection */
/* Make sure printf strings have only %d escapes and n or fewer
of them. Returns 0 if OK, 1 on error. */
int ckfmt ( char *p, int n )
{
for ( ; *p ; p++ ) {
if ( p[0] == '%' ) {
if ( p[1] == 'd' ) n-- ;
else if ( p[1] == '%' ) p++ ;
else n=-1 ;
}
}
return n < 0 ;
}
/* Initialize state of variable-length code word encoder. */
void newENCODER ( ENCODER *e )
{
e->x = 0 ;
e->shift = -8 ;
}
/* Store a code word `code' of length `bits' (<=24) in buffer pointed to by
`buf'. Bits that don't fit in complete bytes are saved between calls.
To flush the remaining bits call the function with code=0 and bits=0.
Returns pointer to next free element in output buffer. Calling function
must ensure at least bits/8 bytes are available in buffer. */
uchar *putcode ( ENCODER *e, short code, short bits, uchar *buf )
{
e->x = ( e->x << bits ) | code ;
e->shift += bits ? bits : -e->shift ;
while ( e->shift >= 0 ) {
*buf++ = e->x >> e->shift ;
e->shift -= 8 ;
}
return buf ;
}
/* Convert run lengths to 1-D T.4-codes. First run is white. Silently
truncates run lengths that are too long. After using this function EOLs
may need to be added and/or the putcode() buffer flushed. Returns
pointer to next free element in output buffer. */
uchar *runtocode ( ENCODER *e, short *runs, int nr, uchar *codes )
{
uchar col = 0, *maxcodes = codes + MAXCODES ;
t4tab *ctab = wtab, *p ;
short rlen ;
long x ;
short shift ;
#define PUTCODE(p) { x = ( x << p->bits ) | p->code ; shift += p->bits ; \
while ( shift >= 0 ) { *codes++ = x >> shift ; shift -= 8 ; } }
x = e->x ; shift = e->shift ;
while ( nr-- > 0 ) {
rlen = *runs++ ;
if ( rlen > 63 ) { /* make-up code */
if ( rlen > MAXRUNLEN ) rlen = MAXRUNLEN ;
p = ctab + 63 + ( rlen >> 6 ) ;
if ( codes < maxcodes ) PUTCODE(p) ;
}
p = ctab + ( rlen & 0x3f ) ; /* terminating code */
if ( codes < maxcodes ) PUTCODE(p) ;
ctab = ( col ^= 1 ) ? btab : wtab ;
}
e->x = x ; e->shift = shift ;
return codes ;
}
/* Pad/truncate run-length coded scan line 'runs' of 'nr' runs by 'pad'
pixels (truncate if negative). Returns the new number of runs. */
int xpad ( short *runs, int nr, int pad )
{
if ( pad < 0 ) { /* truncate */
while ( pad < 0 ) pad += ( nr <= 0 ? -pad : runs [ --nr ] ) ;
runs [ nr++ ] = pad ;
} else { /* pad with white */
if ( nr & 1 ) runs [ nr - 1 ] += pad ;
else runs [ nr++ ] = pad ;
}
return nr ;
}
/* Shift a run-length coded scan line right by s pixels (left if negative).
If necessary, zero-length runs are created to avoid copying. Returns
the pixel width change (+/-). */
int xshift ( short *runs, int nr, int s )
{
int i=0, n=0 ;
if ( s < 0 ) {
for ( i = 0 ; s < 0 && i < nr ; i++ ) {
s += runs [ i ] ;
n -= runs [ i ] ;
runs [ i ] = 0 ;
}
i-- ;
}
if ( i < nr ) {
runs [ i ] += s ;
n += s ;
}
return n ;
}
/* Scale nr run lengths in buffer pointed to by p to scale image
horizontally. The scaling factor is xs/256. Returns new line width in
pixels. */
int xscale ( short *p, int nr, int xs )
{
int inlen=0, outlen=0 ;
for ( ; nr-- > 0 ; p++ ) {
inlen += *p ;
*p = ( ( inlen * xs + 128 ) >> 8 ) - outlen ;
outlen += *p ;
}
return outlen ;
}
/* Zero-terminated lists of run lengths for each byte. */
uchar byteruns [ 1408 + 1 ] =
"8071061106205120511105210530413041210411110411204220421104310440"
"3140313103121103122031112031111103112103113032303221032111032120"
"3320331103410350215021410213110213202121202121110212210212302111"
"3021112102111111021111202112202112110211310211402240223102221102"
"2220221120221111022121022130233023210231110231202420241102510260"
"1160115101141101142011312011311101132101133011213011212101121111"
"0112112011222011221101123101124011114011113101111211011112201111"
"1120111111110111112101111130111230111221011121110111212011132011"
"1311011141011150125012410123110123201221201221110122210122301211"
"3012112101211111012111201212201212110121310121401340133101321101"
"3220131120131111013121013130143014210141110141201520151101610170"
"1701610151101520141201411101421014301313013121013111101311201322"
"0132110133101340121401213101212110121220121112012111110121121012"
"1130122301222101221110122120123201231101241012501115011141011131"
"1011132011121201112111011122101112301111130111112101111111101111"
"1120111122011112110111131011114011240112310112211011222011211201"
"1211110112121011213011330113210113111011312011420114110115101160"
"2602510241102420231202311102321023302213022121022111102211202222"
"0222110223102240211402113102112110211220211112021111110211121021"
"1130212302122102121110212120213202131102141021503503410331103320"
"3212032111032210323031130311210311111031112031220312110313103140"
"4404310421104220411204111104121041305305210511105120620611071080" ;
/* Convert byte-aligned bit-mapped n-byte scan line into array of run
lengths. Run length array must have *more* than 8*n elements. First
run is white. Returns number of runs coded. */
int bittorun ( uchar *bits, int n, short *runs )
{
static uchar init=0, *rltab [ 256 ] ;
register uchar *p, c, lastc = 0x00 ;
short *runs0 = runs ;
if ( ! init ) { /* initialize pointer and run tables */
int i = 0 ;
for ( rltab[ 0 ] = p = byteruns ; *p ; p++ )
if ( ! ( *p -= '0' ) && i < 255 )
rltab [ ++i ] = p+1 ;
init = 1 ;
}
*runs = 0 ;
for ( ; n > 0 ; n-- ) {
p = rltab [ c = *bits++ ] ;
if ( ( lastc & 0x01 ) ? ! ( c & 0x80 ) : ( c & 0x80 ) )
*(++runs) = *p++ ; /* new run */
else
*runs += *p++ ; /* continue run */
while ( *p )
*(++runs) = *p++ ;
lastc = c ;
}
return runs - runs0 + 1 ;
}
/* Bitwise-OR two run-length coded scan lines. The run length
vectors a and b are OR-ed into c. If c is null, the result is
placed in a. The new image width is stored in pels if it is
not null. Returns the number of runs in the result. */
int runor ( short *a, int na, short *b, int nb, short *c, int *pels )
{
register short la, lb ;
int ia, ib, ic, np=0 ;
short tmp [ MAXRUNS ] ;
if ( ! c ) c = tmp ;
la = a [ ia = 0 ] ;
lb = b [ ib = 0 ] ;
c [ ic = 0 ] = 0 ;
while ( 1 ) {
if ( la <= lb ) { /* select shorter sub-run */
if ( ( ( ia | ib ) ^ ic ) & 1 ) /* OR of subruns same colour as c? */
c [ ++ic ] = la ; /* no, new output run */
else
c [ ic ] += la ; /* yes, add it */
lb -= la ; /* align subruns */
if ( ++ia >= na ) break ; /* done */
la = a [ ia ] ; /* get new subrun */
} else { /* same for line b ... */
if ( ( ( ia | ib ) ^ ic ) & 1 )
c [ ++ic ] = lb ;
else
c [ ic ] += lb ;
la -= lb ;
if ( ++ib >= nb ) break ;
lb = b [ ib ] ;
}
}
if ( ia < na )
while ( 1 ) {
if ( ( ia ^ ic ) & 1 )
c [ ++ic ] = la ;
else
c [ ic ] += la ;
if ( ++ia >= na ) break ;
la = a [ ia ] ;
}
else
while ( 1 ) {
if ( ( ib ^ ic ) & 1 )
c [ ++ic ] = lb ;
else
c [ ic ] += lb ;
if ( ++ib >= nb ) break ;
lb = b [ ib ] ;
}
if ( c == tmp ) for ( ia=0 ; ia <= ic ; ia++ ) np += a[ia] = c[ia] ;
if ( pels ) *pels = np ;
return ic + 1 ;
}
/* Get a number from a PBM file header while skipping whitespace
and comments. Returns the number or 0 on EOF. Reads one more
byte than used by the number. */
int pbmdim ( IFILE *f )
{
int c, n=0 ;
/* scan for first digit and skip comments */
while ( ! isdigit ( c = fgetc ( f->f ) ) && c >= 0 )
if ( c == '#' )
while ( ( c = fgetc ( f->f ) ) != '\n' && c >= 0 ) ;
/* get the number */
if ( c >= 0 && isdigit( c ) ) {
n = c - '0' ;
while ( isdigit ( c = fgetc ( f->f ) ) && c >= 0 )
n = n * 10 + c - '0' ;
}
return n ;
}
/* Append nb bits from in[from] to bit-mapped scan line buffer
where `from' is a bit (not byte) index. Bits in bytes are
ordered from MS to LS bit. Initialize before each scan line by
calling with nb=0 and in pointing to output buffer. Flush
after each scan line by calling with nb=0 and in=NULL. */
#define putbits( c, b ) { x = ( x << (b) ) | (c) ; shift += (b) ; \
if ( shift >= 0 ) { *out++ = x >> shift ; shift -= 8 ; } }
void copybits ( uchar *in, int from, short nb )
{
uchar *f ;
short bits ;
static uchar *out ;
static short x, shift ;
static unsigned char right [ 9 ] = {
0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff } ;
if ( ! nb ) { /* reset for new scan line */
if ( in ) out = in ;
else putbits ( 0, -shift ) ; /* or flush bit buffer */
x = 0 ;
shift = -8 ;
} else {
f = in + ( from >> 3 ) ;
bits = 8 - ( from & 7 ) ;
if ( nb >= bits ) {
putbits ( *f++ & right [ bits ], bits ) ;
nb -= bits ;
} else {
putbits ( ( *f >> ( bits - nb ) ) & right [ bits ], nb ) ;
nb = 0 ;
}
while ( nb >= 8 ) { putbits ( *f++, 8 ) ; nb -= 8 ; }
if ( nb > 0 ) putbits ( *f >> ( 8 - nb ), nb );
}
}
/* Generate scan line 'line' of string 'txt' using font `font'
and store the runs in 'runs'. The font is scaled so it
appears to have cells of width w and height h. lmargin pixels
of white space are added at the left margin. Sets 'pels' to
line width if not null. Returns number of runs coded. */
int texttorun ( uchar *txt, faxfont *font, short line,
int w, int h, int lmargin,
short *runs, int *ppels )
{
uchar *in, out [ MAXLINELEN * MAXFONTW / 8 + 1 ] ;
int i, nc = 0, cw, nr, pels ;
line = ( line * font->h + h/2 ) / h ;
cw = font->w ;
if ( line >= font->h ) line = font->h - 1 ;
in = font->buf + 256/8 * cw * line ;
copybits ( out, 0, 0 ) ;
for ( i=0 ; txt[i] && i < MAXLINELEN ; i++ ) {
copybits ( in, font->offset [ txt[i] ], cw ) ;
nc++ ;
while ( ( txt[i] == HT ) && ( nc & 7 ) ) { /* tab */
copybits ( in, font->offset [ ' ' ], cw ) ;
nc++ ;
}
}
copybits ( 0, 0, 0 ) ;
nr = bittorun ( out, ( nc*cw + 7 )/8, runs ) ;
if ( font->w == w )
pels = nc*cw ;
else
pels = xscale ( runs, nr, ( w * 256 ) / font->w ) ;
pels += xshift ( runs, nr, lmargin ) ;
if ( ppels ) *ppels = pels ;
return nr ;
}
/* Image File Input Functions */
/* Names of file formats */
char *iformatname [ NIFORMATS ] = IFORMATS ;
char *oformatname [ NOFORMATS ] = OFORMATS ;
char *pformatname [ NPFORMATS ] = PFORMATS ;
/* Log the names of files still to be sent using the "msg()"
format string s. */
void logifnames ( IFILE *f, char *s )
{
PAGE *p ;
char *fn ;
if ( f && f->page )
for ( p = f->page ; p <= f->lastpage ; p++ ) {
fn = p->fname ;
if ( fn ) msg ( s, fn ) ;
}
}
/* Read run lengths for one scan line from T.4-coded IFILE f into buffer
runs. If pointer pels is not null it is used to save pixel count.
Returns number of runs stored, EOF on RTC, or -2 on EOF or other
error. */
int readruns ( IFILE *f, short *runs, int *pels )
{
int err=0, c=EOF, n ;
register int x ;
dtab *tab, *t ;
short shift ;
short *p, *maxp, *q, len=0, npad=0 ;
DECODER *d ;
uchar reverse=f->page->revbits ;
maxp = ( p = runs ) + MAXRUNS ;
d = &f->d ;
x = d->x ; shift = d->shift ; tab = d->tab ; /* restore decoder state */
do {
do {
while ( shift < 0 ) {
if ( ( c = fgetc ( f->f ) ) == EOF ) {
x = ( x << 15 ) | 1 ; shift += 15 ; /* EOL pad at EOF */
npad++ ;
} else {
if ( reverse ) c = normalbits [ c & 0xff ] ;
x = ( x << 8 ) | c ; shift += 8 ;
}
}
t = tab + ( ( x >> shift ) & 0x1ff ) ;
tab = t->next ;
shift -= t->bits ;
} while ( ! t->code ) ;
if ( p < maxp ) *p++ = t->code ;
} while ( t->code != -1 ) ;
d->x = x ; d->shift = shift ; d->tab = tab ; /* save state */
if ( npad > 1 ) msg ("W EOF before RTC" ) ;
if ( p >= maxp ) msg ( "W run length buffer overflow" ) ;
/* combine make-up and terminating codes and remove +1 offset
in run lengths */
n = p - runs - 1 ;
for ( p = q = runs ; n-- > 0 ; )
if ( *p > 64 && n-- > 0 ) {
len += *q++ = p[0] + p[1] - 2 ;
p+=2 ;
} else {
len += *q++ = *p++ - 1 ;
}
n = q - runs ;
/* check for RTC and errors */
if ( len )
d->eolcnt = 0 ;
else
if ( ++(d->eolcnt) >= RTCEOL ) err = EOF ;
if ( c == EOF ) {
if ( ferror ( f->f ) ) {
err = -msg ("ES2error reading fax file:") ;
} else {
err = -2 ;
}
}
if ( pels ) *pels = len ;
return err ? err : n ;
}
/* Read a PCX compressed bit-map */
int readpcx ( char *p, int len, IFILE *f )
{
int err=0, n, c ;
while ( !err && len > 0 ) {
if ( ( c = fgetc ( f->f ) ) < 0 ) {
err = msg ( "ES2 PCX read failed" ) ;
} else {
if ( ( c & 0xc0 ) == 0xc0 ) { /* a run count */
n = c & 0x3f ;
c = fgetc ( f->f ) ;
if ( ( c = fgetc ( f->f ) ) < 0 ) {
err = msg ( "ES2 PCX read failed" ) ;
} else {
memset ( p, c, n <= len ? n : len ) ;
p += n ;
len -= n ;
}
} else { /* bits 0 to 7 are image data */
*p++ = c ;
len-- ;
}
}
}
if ( len != 0 )
msg ( "W PCX run-length coding error" ) ;
return err ;
}
/* Read a scan line from the current page of IFILE f. Stores
number of runs in runs and line width in pels if not null.
Pages ends at EOF. Text pages also end if a complete text line
would not fit or if the line contains a formfeed character.
PBM pages also end when all lines in the bitmap have been
read. Fax pages also end at RTC. Returns number of runs stored
or EOF at end of page. */
int readline ( IFILE *f, short *runs, int *pels )
{
int nr = 0, nb ;
uchar bits [ MAXBITS ] ;
if ( f->lines != 0 ) { /* -1 allowed as well */
switch ( f->page->format ) {
case P_TEXT :
if ( f->txtlines <= 0 ) { /* need another text line */
if ( fgets ( f->text, MAXLINELEN, f->f ) ) {
f->txtlines = f->charh ;
if ( strchr ( f->text, FF ) ) {
f->lines = 0 ; /* no more lines in this page */
nr = EOF ; /* don't return any */
}
} else {
nr = EOF ;
}
}
if ( nr != EOF ) {
nr = texttorun ( (uchar*) f->text, f->font, f->charh - f->txtlines,
f->charw, f->charh, f->lmargin,
runs, pels ) ;
f->txtlines-- ;
}
break ;
case P_RAW:
case P_PBM:
if ( fread ( bits, 1, f->page->w/8, f->f ) != f->page->w/8 ) {
nr = EOF ;
} else {
nr = bittorun ( bits, f->page->w/8, runs ) ;
if ( pels ) *pels = f->page->w ;
}
break ;
case P_FAX:
nr = readruns ( f, runs, pels ) ;
break ;
case P_PCX:
nb = ( ( f->page->w + 15 ) / 16 ) * 2 ; /* round up */
if ( readpcx ( bits, nb, f ) != 0 ) {
nr = EOF ;
} else {
nr = bittorun ( bits, nb, runs ) ;
if ( pels ) *pels = f->page->w ;
}
break ;
}
} else {
nr = EOF ;
}
if ( nr >= 0 && f->lines > 0 ) f->lines-- ;
return nr ;
}
/* Deduce the file type by scanning buffer p of n bytes. */
int getformat ( uchar *p, int n )
{
int format = 0 ;
/* figure out file type if not already set */
if ( ! format && n < 2 ) {
format = I_TEXT ;
msg ( "W only read %d byte(s) from input file, assuming text", n ) ;
}
if ( ! format && ! p[0] && ! ( p[1] & 0xe0 ) ) {
format = I_FAX ;
}
if ( ! format && ! strncmp ( p, "P4", 2 ) ) {
format = I_PBM ;
}
if ( ! format && n >= 128 && p[0] == 0x0a &&
strchr ("\02\03\05", p[1] ) && p[2] <= 1 ) {
if ( p[65] != 1 ) {
msg ( "E can't read colour PCX" ) ;
} else {
format = p[2] ? I_PCX : I_PCX ;
}
}
if ( ! format && ! strncmp ( p, "%!", 2 ) ) {
msg ( "W Postscript input file will be treated as text" ) ;
}
if ( ! format && ( p[0] == 'M' || p[1] == 'I' ) && ( p[1] == p[0] ) ) {
format = I_TIFF ;
}
if ( ! format &&
( p[0] == 0x3a && p[1] == 0xde &&
p[2] == 0x68 && p[3] == 0xb1) ) {
format = I_DCX ;
}
if ( ! format && n ) { /* "90% printable" heuristic */
int i, nprint = 0 ;
for ( i=0 ; i<n ; i++ ) {
if ( isspace ( p[i] ) || isprint ( p[i] ) ) {
nprint++ ;
}
}
if ( ( nprint / (float) n ) > 0.90 ) {
format = I_TEXT ;
}
}
return format ;
}
/* initialize page descriptor */
void page_init ( PAGE *p, char *fn )
{
p->fname = fn ;
p->offset = 0 ;
p->w = DEFWIDTH ;
p->h = DEFHEIGHT ;
p->xres = DEFXRES ;
p->yres = DEFYRES ;
p->format = P_FAX ;
p->revbits = 0 ;
}
void page_report ( PAGE *p, int fmt, int n )
{
msg ( "F page %d : %s + %ld : %dx%d @ %.fx%.f dpi %s/%s",
n,
p->fname, p->offset, p->w, p->h,
p->xres, p->yres,
iformatname [fmt], pformatname [p->format] ) ;
}
/* File handling for undefined file types */
#define auto_first 0
#define auto_next 0
/* File handling for TIFF files */
#define tiff_reset 0
/* Name of TIFF tag 'tag'. */
char *tagname ( int tag )
{
static struct tagnamestruct { int code ; char *name ; }
tagnames [] = {
{ 256, "width" },
{ 257, "length" },
{ 258, "bits/sample" },
{ 259, "compresssion(g3=3)" },
{ 262, "photometric(0-min=white)" },
{ 266, "fill order(msb2lsb=1)" },
{ 273, "strip offsets" },
{ 274, "orientation(1=normal)" },
{ 277, "samples/pixel" },
{ 278, "rows/strip" },
{ 279, "strip byte counts" },
{ 282, "xresolution" },
{ 283, "yresolution" },
{ 284, "storage(1=single plane)" },
{ 292, "g3options" },
{ 296, "resolution units(2=in,3=cm)" },
{ 297, "page number" },
{ 327, "clean fax(0=clean/1=regen/2=errors)" },
{0,0} },
*p ;
for ( p=tagnames ; p->code ; p++ )
if ( tag == p->code ) break ;
return p->code ? p->name : "unknown tag" ;
}
/* Read 2- or 4-byte integers from a file and correct for file
endianness. Returns 0 if OK, 1 on error. */
int fread2 ( unsigned short *p, IFILE *f )
{
int err=0 ;
uchar c[2] ;
err = fread ( c, 1, 2, f->f ) == 2 ? 0 : 1 ;
*p = f->bigend ?
c[0] << 8 | c[1] << 0 :
c[1] << 8 | c[0] << 0 ;
return err ;
}
int fread4 ( unsigned long *p, IFILE *f )
{
int err=0 ;
uchar c[4] ;
err = fread ( c, 1, 4, f->f ) == 4 ? 0 : 1 ;
*p = f->bigend ?
c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3] << 0 :
c[3] << 24 | c[2] << 16 | c[1] << 8 | c[0] << 0 ;
return err ;
}
/* Read a TIFF directory at current file offset, save image
format information and seek to next directory if any. Returns
0 if OK, 2 on errors. */
int tiff_next ( IFILE *f )
{
int err=0 ;
unsigned short ntag, tag, type ;
unsigned long count, tv ;
double ftv ;
msg ( "F+ TIFF directory at %ld", ftell ( f->f ) ) ;
if ( fread2 ( &ntag, f ) ) {
err = msg ( "E2can't read TIFF tag count" ) ;
} else {
msg ( "F+ with %d tags", (int) ntag ) ;
}
for ( ; ! err && ntag > 0 ; ntag-- ) {
err = err || fread2 ( &tag, f ) ;
err = err || fread2 ( &type, f ) ;
err = err || fread4 ( &count, f ) ;
if ( type == 3 ) { /* left-aligned short */
unsigned short a, b ;
err = err || fread2 ( &a, f ) ;
err = err || fread2 ( &b, f ) ;
tv = a ;
} else { /* long or offset to data */
err = err || fread4 ( &tv, f ) ;
}
if ( type == 5 ) { /* float as ratio in directory data */
long a, b, where=0 ;
err = err || ( ( where = ftell ( f->f ) ) < 0 ) ;
err = err || fseek ( f->f, tv, SEEK_SET ) ;
err = err || fread4 ( &a, f ) ;
err = err || fread4 ( &b, f ) ;
err = err || fseek ( f->f, where, SEEK_SET ) ;
ftv = (float) a / ( b ? b : 1 ) ;
} else {
ftv = 0.0 ;
}
if ( err ) {
err = msg ( "ES2can't read TIFF tag" ) ;
continue ;
}
#ifdef TIFF_DEBUG
{
char *tagtype[] = { "none", "byte", "ascii", "short", "long", "ratio" } ;
msg ( "F %3d %-5s tag %s %5ld (%3d:%s)",
count,
type <= 5 ? tagtype[type] : "other",
count > 1 ? "@" : "=",
type == 5 ? (long) ftv : tv, tag, tagname(tag) ) ;
}
#endif
switch ( tag ) {
case 256 : /* width */
f->page->w = tv ;
break ;
case 257 : /* height */
f->page->h = tv ;
break ;
case 259 : /* compression: 1=none, 3=G3 */
if ( tv == 1 ) {
f->page->format = P_RAW ;
} else if ( tv == 3 ) {
f->page->format = P_FAX ;
} else {
err = msg ( "E2can only read TIFF/G3 or TIFF/uncompressed" ) ;
}
break ;
case 266 : /* fill order */
f->page->revbits = ( tv == 2 ? 1 : 0 ) ;
break ;
case 273 : /* data offset */
if ( count != 1 )
err = msg ( "E2can't read multi-strip TIFF files" ) ;
else
f->page->offset = tv ;
break ;
case 282 : /* x resolution */
f->page->xres = ftv ;
break ;
case 283 : /* y resolution */
f->page->yres = ftv ;
break ;
case 292 : /* T4 options: 1=2D, 2=uncompressed */
if ( tv & 0x1 )
err = msg ( "E2can't read 2D compressed TIFF-F file" ) ;
if ( tv & 0x2 )
err = msg ( "E2can't read uncompressed TIFF-F file" ) ;
break ;
case 296 : /* units: 2=in, 3=cm */
if ( tv == 3 ) {
f->page->xres *= 2.54 ;
f->page->yres *= 2.54 ;
}
break ;
}
} /* end of tag reading loop */
if ( f->page->format == I_AUTO ) {
msg ( "W missing TIFF compression format, set to raw" ) ;
f->page->format = P_RAW ;
}
if ( ! err ) {
if ( fread4 ( &(f->next), f ) ) {
err = msg ( "E2can't read offset to next TIFF directory" ) ;
} else {
if ( f->next ) {
msg ( "F , next directory at %ld.", f->next ) ;
if ( fseek ( f->f, f->next, SEEK_SET ) )
err = msg ( "ES2 seek to next TIFF directory failed" ) ;
} else {
msg ( "F , last image." ) ;
}
}
}
if ( ! f->page->offset )
err = msg ( "E2 missing offset to TIFF data" ) ;
return err ;
}
int tiff_first ( IFILE *f )
{
short magic, version ;
fread ( (uchar*) &magic, 1, 2, f->f ) ;
f->bigend = ( *(uchar*) &magic == 'M' ) ? 1 : 0 ;
fread2 ( &version, f ) ;
fread4 ( &(f->next), f ) ;
msg ( "F TIFF version %d.%d file (%s-endian)",
version/10, version%10, f->bigend ? "big" : "little" ) ;
fseek ( f->f, f->next, SEEK_SET ) ;
return tiff_next ( f ) ;
}
/* File handling for text files. */
int text_next ( IFILE *f )
{
int err = 0, i, nc ;
char buf [ MAXLINELEN ] ;
f->page->offset = ftell ( f->f ) ;
f->page->format = P_TEXT ;
nc = ( f->page->w - f->lmargin ) / f->charw ;
if ( nc > MAXLINELEN )
nc = MAXLINELEN ;
for ( i = 0 ; i < f->pglines && fgets ( buf, nc, f->f ) ; i++ ) ;
f->next = ! feof(f->f) ? ftell ( f->f ) : 0 ;
err = ferror(f->f) ? 2 : 0 ;
return err ;
}
int text_first ( IFILE *f )
{
static faxfont defaultfont ;
if ( ! f->font ) {
/* use default font scaled 2X, 1 inch margin, 66 lines/page */
f->font = &defaultfont ;
readfont ( 0, f->font ) ;
if ( ! f->charw ) f->charw = 2 * f->font->w ;
if ( ! f->charh ) f->charh = 2 * f->font->h ;
if ( ! f->lmargin ) f->lmargin = 204 ;
if ( ! f->pglines ) f->pglines = DEFPGLINES ;
}
/* if not set, take default values from font */
if ( ! f->charw ) f->charw = f->font->w ;
if ( ! f->charh ) f->charh = f->font->h ;
if ( ! f->lmargin ) f->lmargin = 0 ;
if ( ! f->pglines ) f->pglines = f->page->h / f->charh - 6 ;
return text_next ( f ) ;
}
/* File handling for PBM files */
int pbm_first ( IFILE *f )
{
int err=0 ;
fseek ( f->f, 2, SEEK_SET ) ;
if ( ! ( f->page->w = pbmdim ( f ) ) || ! ( f->page->h = pbmdim ( f ) ) ) {
err = msg ( "E2 EOF or 0 dimension in PBM header" ) ;
} else if ( f->page->w % 8 ) {
err = msg ( "E2 PBM width must be multiple of 8" ) ;
} else {
msg ( "F read %dx%d PBM header", f->page->w, f->page->h ) ;
}
f->page->offset = ftell ( f->f ) ;
f->page->format = P_PBM ;
f->next = 0 ;
return err ;
}
#define pbm_next 0
/* File handling for FAX files */
#define fax_first 0
#define fax_next 0
/* File handling for PCX files */
/* get a 16-bit word in Intel byte order. */
int fgeti ( IFILE *f )
{
return fgetc ( f->f ) + fgetc ( f->f ) * 256 ;
}
int pcx_first ( IFILE *f )
{
int err=0, xmin, xmax, ymin, ymax, nc, nb ;
long start ;
start = ftell ( f->f ) ;
fseek ( f->f, start+4, SEEK_SET ) ;
xmin = fgeti ( f ) ;
ymin = fgeti ( f ) ;
xmax = fgeti ( f ) ;
ymax = fgeti ( f ) ;
f->page->w = xmax - xmin + 1 ;
f->page->h = ymax - ymin + 1 ;
f->page->xres = fgeti ( f ) ;
f->page->yres = fgeti ( f ) ;
fseek ( f->f, start+0x41, SEEK_SET ) ;
nc = fgetc ( f->f ) ;
nb = fgeti ( f ) ;
if ( nc != 1 )
msg ( "W mono PCX file has %d colour planes", nc ) ;
if ( nb != ( f->page->w + 15 ) / 16 * 2 )
msg ( "W PCX file has %d bytes per scan line for %d pels",
nb, f->page->w ) ;
f->page->offset = start + 128 ;
f->page->format = P_PCX ;
return err ;
}
#define pcx_next 0
/* File handling for DCX files */
int dcx_next ( IFILE *f )
{
int err=0 ;
long thisp, nextp ;
/* get this and next pages' offsets */
fseek ( f->f, f->next, SEEK_SET ) ;
fread4 ( &thisp, f ) ;
fread4 ( &nextp, f ) ;
/* save address of next directory entry, if any */
f->next = nextp ? f->next + 4 : 0 ;
if ( ! thisp )
err = msg ( "E2 can't happen (dcx_next)" ) ;
else
fseek ( f->f, thisp, SEEK_SET ) ;
return err ? err : pcx_first ( f ) ;
}
int dcx_first ( IFILE *f )
{
f->bigend = 0 ;
f->next = 4 ;
return dcx_next ( f ) ;
}
#define raw_reset 0
#define raw_first 0
#define raw_next 0
/* input file state reset for different compression methods */
#define pcx_reset 0
#define pbm_reset 0
int text_reset ( IFILE *f )
{
int err=0 ;
f->lines = ( ( f->pglines * f->charh ) / f->charh ) * f->charh ;
f->txtlines = 0 ;
f->page->yres = f->lines > 1078 ? 196 : 98 ; /* BOGUS */
return err ;
}
int fax_reset ( IFILE *f )
{
int pels ;
short runs [ MAXRUNS ] ;
newDECODER ( &f->d ) ;
if ( readruns ( f, runs, &pels ) < 0 || pels ) /* skip first EOL */
msg ( "W first line has %d pixels: probably not fax data", pels ) ;
f->lines = -1 ;
return 0 ;
}
/* Skip to start of same (dp=0) or next (dp=1) page image.
Returns 0 if OK, 1 if no more pages, 2 on errors. */
int nextipage ( IFILE *f, int dp )
{
int err=0 ;
int ( *reset [NPFORMATS] ) ( IFILE * ) = {
raw_reset, fax_reset, pbm_reset, text_reset, pcx_reset
}, (*pf)(IFILE*) ;
/* close current file if any and set to NULL */
if ( f->f ) {
fclose ( f->f ) ;
f->f = 0 ;
}
/* if requested, point to next page and check if done */
if ( dp ) {
f->page++ ;
}
if ( f->page > f->lastpage ) {
err = 1 ;
}
/* open the file and seek to start of image data */
if ( ! err ) {
f->f = fopen ( f->page->fname, (f->page->format == P_TEXT) ? "r" : "rb" ) ;
if ( ! f->f )
err = msg ( "ES2can't open %s:", f->page->fname ) ;
}
if ( ! err && fseek ( f->f, f->page->offset, SEEK_SET ) )
err = msg ( "ES2 seek failed" ) ;
/* default initializations */
f->lines = f->page->h ;
/* coding-specific initializations for this page */
if ( ! err ) {
pf = reset[f->page->format] ;
if ( pf )
err = (*pf)(f) ;
}
return err ;
}
/* Returns true if on last file. */
int lastpage ( IFILE *f )
{
return f->page >= f->lastpage ;
}
#define dfax_first 0
#define dfax_next 0
/* Initialize an input (IFILE) structure. This structure
collects the data about images to be processed to allow a
simple interface for functions that need to read image files.
The IFILE is initialized by building an array of information
for each page (image) in all of the files.
The page pointer index is initialized so that the first call
to nextipage with dp=1 actually opens the first file.
*/
int newIFILE ( IFILE *f, char **fnames )
{
int err=0, i, n, fformat=0 ;
char **p ;
uchar buf[128] ;
int ( *fun ) ( IFILE * ) ;
int ( *first [NIFORMATS] ) ( IFILE * ) = {
auto_first, pbm_first, fax_first, text_first, tiff_first,
dfax_first, pcx_first, raw_first, dcx_first
} ;
int ( *next [NIFORMATS] ) ( IFILE * ) = {
auto_next, pbm_next, fax_next, text_next, tiff_next,
dfax_next, pcx_next, raw_next, dcx_next
} ;
f->page = f->pages ;
/* get info for all pages in all files */
for ( p=fnames ; ! err && *p ; p++ ) {
if ( ! ( f->f = fopen ( *p, "rb" ) ) )
err = msg ( "ES2 can't open %s:", *p ) ;
if ( ! err ) {
n = fread ( buf, 1, 128, f->f ) ;
if ( ferror ( f->f ) )
err = msg ( "ES2 can't open %s:", *p ) ;
}
if ( ! err ) {
fformat = getformat ( buf, n ) ;
if ( ! fformat )
err = msg ( "E2 can't get format of %s", *p ) ;
}
if ( ! err && fseek ( f->f, 0, SEEK_SET ) )
err = msg ( "ES2 can't rewind %s:", *p ) ;
/* get format information for all pages in this file */
for ( i=0 ; ! err ; i++ ) {
page_init ( f->page, *p ) ;
if ( ( fun = i ? next[fformat] : first[fformat] ) )
err = (*fun)(f) ;
if ( ! err ) {
page_report ( f->page, fformat, f->page - f->pages + 1 ) ;
f->page++ ;
if ( f->page >= f->pages + MAXPAGE )
err = msg ( "E2 too many pages (max is %d)", MAXPAGE ) ;
}
if ( ! f->next ) break ;
}
if ( f->f ) {
fclose ( f->f ) ;
f->f = 0 ;
}
}
f->lastpage = f->page - 1 ;
f->page = f->pages ;
if ( ! normalbits[1] ) initbittab() ; /* bit-reverse table initialization */
return err ;
}
/* Image File Output Functions */
/* Strings and function to write a bit map in HP-PCL format. The only
compression is removal of trailing zeroes. Margins and resolution are
set before first write. */
char *PCLBEGIN =
"\033E" /* Printer reset. */
"\033&l0E" /* top margin = 0 */
"\033&a0L" /* left margin = 0 */
"\033*t%dR" /* Set raster graphics resolution */
"\033*r1A" ; /* Start raster graphics, rel. adressing */
char *PCLEND =
"\033*rB" /* end raster graphics */
"\014" /* form feed */
"\033E" ; /* Printer reset. */
void pclwrite ( OFILE *f, unsigned char *buf, int n )
{
while ( n > 0 && buf [ n-1 ] == 0 ) n-- ;
fprintf( f->f, "\033*b%dW", n ) ;
fwrite ( buf, n, 1, f->f ) ;
}
/* Write a bit map as (raw) Portable Gray Map (PGM) format after
decimating by a factor of 4. Sums bits in each 4x4-pel square
to compute sample value. This function reduces each dimension
of a bit map by 4 (it writes n*8/4 pixels per scan line and
one scan line for every 4 in). The 17 possible sample values
are spread linearly over the range 0-255. */
void pgmwrite ( OFILE *f, uchar *buf, int n )
{
static uchar gval [ MAXBITS * 8 / 4 ] ;
static int init=0, lines=0 ;
static uchar hbits [ 256 ], lbits [ 256 ] ;
static int nybblecnt [ 16 ] = { 0,1,1,2, 1,2,2,3, 1,2,2,3, 2,3,3,4 } ;
static uchar corr [ 17 ] = { 255, 239, 223, 207, 191, 175, 159, 143, 127,
111, 95, 79, 63, 47, 31, 15, 0 } ;
int m ;
uchar *p, *q ;
if ( ! init ) { /* build table of bit counts in each nybble */
short i ;
for ( i=0 ; i<256 ; i++ ) {
hbits [ i ] = nybblecnt [ i >> 4 & 0x0f ] ;
lbits [ i ] = nybblecnt [ i & 0x0f ] ;
}
init = 1 ;
}
for ( m=n, p=gval, q=buf ; m-- > 0 ; q++ ) {
*p++ += hbits [ *q ] ;
*p++ += lbits [ *q ] ;
}
if ( ( lines++ & 0x03 ) == 0x03 ) {
for ( p=gval, m=2*n ; m-- > 0 ; p++ ) *p = corr [ *p ] ;
fwrite ( gval, 1, 2*n, f->f ) ;
memset ( gval, 0, 2*n ) ;
}
}
/* Postscript image data is differentially coded vertically and
run-length coded horizontally. A leading byte (n) defines the type
of coding for subsequent data:
0 repeat previous line
1-127 n data bytes follow
128-254 copy n-127 bytes from previous line
255 n repeat the next character 'n' times
The overhead for coding a copy is 2 bytes (copy count, data count),
so copies > 2 bytes should be so coded. The overhead for coding a
run is 4 bytes (255, count, byte, data count), so runs > 4 bytes
should be so coded. Copies decode/execute faster and code more
compactly so are preferred over runs.
*/
const char PSBEGIN [] = /* start of file */
"%%!PS-Adobe-2.0 EPSF-2.0 \n"
"%%%%Creator: efax (Copyright 1995 Ed Casas) \n"
"%%%%Title: efix output\n"
"%%%%Pages: (atend) \n"
"%%%%BoundingBox: 0 0 %d %d \n"
"%%%%BeginComments \n"
"%%%%EndComments \n"
"/val 1 string def \n"
"/buf %d string def \n"
"/getval { \n"
" currentfile val readhexstring pop 0 get \n"
"} bind def \n"
"/readbuf { \n"
" 0 %% => index \n"
" { \n"
" dup buf length ge { exit } if \n"
" getval %% => index run_length \n"
" dup 127 le { \n"
" dup 0 eq { \n"
" pop buf length \n"
" } { \n"
" currentfile buf 3 index 3 index getinterval readhexstring pop pop\n"
" } ifelse \n"
" } { \n"
" dup 255 eq { \n"
" pop getval getval %% => index run_length value \n"
" 2 index 1 3 index 2 index add 1 sub %% => ... start 1 end \n"
" { buf exch 2 index put } for \n"
" pop \n"
" } { \n"
" 127 sub \n"
" } ifelse \n"
" } ifelse \n"
" add %% => index \n"
" } loop \n"
" pop \n"
" buf \n"
"} bind def \n"
"%%%%EndProlog \n" ;
const char PSPAGE [] = /* start of page */
"%%%%Page: %d %d \n"
"gsave \n"
"%f %f translate \n"
"%f %f scale \n"
"%d %d %d [ %d %d %d %d %d %d ] { readbuf } image \n" ;
const char PSPAGEEND [] = /* end of page */
"\n"
"grestore \n"
"showpage \n" ;
const char PSEND [] = /* end of file */
"%%Trailer \n"
"%%%%Pages: %d \n" ;
void psinit ( OFILE *f, int newfile, int page, int w, int h, int n )
{
float ptw, pth ;
if ( ! f ) {
msg ( "E2 can't happen (psinit)" ) ;
return ;
}
ptw = w/f->xres * 72.0 ; /* convert to points */
pth = h/f->yres * 72.0 ;
if ( newfile )
fprintf ( f->f, PSBEGIN,
(int) ptw, (int) pth, /* Bounding Box */
n ) ; /* buffer string length */
fprintf ( f->f, PSPAGE,
page, page, /* page number */
0.0, 0.0, /* shift */
ptw, pth, /* scaling */
w, h, 1, /* image size */
w, 0, 0, -h, 0, h ) ; /* CTM */
f->pslines = 0 ;
f->lastpageno = page ;
}
char nhexout = 0, hexchars [ 16 ] = "0123456789abcdef" ;
#define hexputc( f, c ) ( \
putc ( hexchars [ (c) >> 4 ], f ), \
putc ( hexchars [ (c) & 0x0f ], f ), \
( ( ( nhexout++ & 31 ) == 31 ) ? putc ( '\n', f ) : 0 ) )
void hexputs ( FILE *f, uchar *p, int n )
{
uchar c ;
if ( n > 0 ) {
hexputc ( f, n ) ;
while ( n-- ) { c = *p++ ^ 0xff ; hexputc ( f, c ) ; }
}
}
/* Encode into postscript. If not a repeated line, test (using
index j) from current position (i) for possible encodings as:
copy of > 2 bytes, runs of > 4 or data >=127. Otherwise the
byte is skipped. Uncoded bytes are output from the last
uncoded byte (l) before output of runs/copies. */
void pswrite ( OFILE *f, unsigned char *buf, int n )
{
int i, j, l ;
static unsigned char last [ MAXBITS ] ;
l=i=0 ;
if ( ! f || ! buf || n<0 ) {
msg ( "E2 can't happen (pswrite)" ) ;
return ;
}
for ( j=0 ; j<n && buf[j]==last[j] && f->pslines ; j++ ) ;
if ( j == n ) { /* repeat line */
hexputc ( f->f, 0 ) ;
l=i=n ;
}
while ( i<n ) {
for ( j=i ; j<n && buf[j]==last[j] && j-i<127 && f->pslines ; j++ ) ;
if ( j-i > 2 ) { /* skip */
hexputs ( f->f, buf+l, i-l ) ;
hexputc ( f->f, j-i + 127 ) ;
l=i=j ;
} else {
for ( j=i ; j<n && buf[j]==buf[i] && j-i<255 ; j++ ) ;
if ( j-i > 4 ) { /* run */
hexputs ( f->f, buf+l, i-l ) ;
hexputc ( f->f, 255 ) ;
hexputc ( f->f, j-i ) ;
hexputc ( f->f, buf[i] ^ 0xff ) ;
l=i=j ;
} else {
if ( i-l >= 127 ) { /* maximum data length */
hexputs ( f->f, buf+l, i-l ) ;
l=i ;
} else { /* data */
i++ ;
}
}
}
}
hexputs ( f->f, buf+l, i-l ) ;
if ( n >= 0 )
memcpy ( last, buf, n ) ;
f->pslines++ ;
}
/* Write 2- and 4-byte integers to an image output file. Return
as for fwrite. */
int fwrite2 ( short s, OFILE *f )
{
uchar *p = (void*) &s ;
return fwrite ( bigendian ? p + sizeof(short) - 2 : p, 2, 1, f->f ) ;
}
int fwrite4 ( long l, OFILE *f )
{
uchar *p = (void*) &l ;
return fwrite ( bigendian ? p + sizeof(long ) - 4 : p, 4, 1, f->f ) ;
}
/* Write a TIFF directory tag. Returns 0 if OK, 1 on errors. */
int wtag ( OFILE *f, int lng, short tag, short type, long count, long offset )
{
int err=0 ;
err = err || ! fwrite2 ( tag, f ) ;
err = err || ! fwrite2 ( type, f ) ;
err = err || ! fwrite4 ( count, f ) ;
if ( lng ) {
err = err || ! fwrite4 ( offset, f ) ;
} else {
err = err || ! fwrite2 ( offset, f ) ;
err = err || ! fwrite2 ( 0, f ) ;
}
if ( err ) msg ( "ES2 can't write TIFF tag" ) ;
return err ;
}
/* Write TIFF header and directory. File format based on Sam
Leffler's tiff.h. Can only be used for single-image TIFFs
because always seeks to start of file to re-write the
header. */
#define NTAGS 17 /* number of tags in directory */
#define NRATIO 2 /* number of floats (as ratios) */
int tiffinit ( OFILE *f )
{
int err=0, compr=1 ;
long tdoff, doff ;
fseek ( f->f, 0, SEEK_SET ) ;
/* 0 ==> (start of TIFF file) */
/* write magic, TIFF version and offset to directory */
fwrite2 ( bigendian ? 0x4d4d : 0x4949, f ) ;
fwrite2 ( 42, f ) ;
fwrite4 ( 8, f ) ;
/* 8 ==> directory */
fwrite2 ( NTAGS, f ) ;
/* figure out offsets within file and compression code */
tdoff = 8 + 2 + NTAGS*12 + 4 ; /* offset to directory data */
doff = tdoff + NRATIO*8 ; /* offset to image data */
switch ( f->format ) {
case O_TIFF_RAW: compr = 1 ; break ;
case O_TIFF_FAX: compr = 3 ; break ;
default: err = msg ( "E2can't happen(tiffinit)" ) ; break ;
}
/* write directory tags, 12 bytes each */
wtag( f, 1, 256, 4, 1, f->w ) ; /* width long */
wtag( f, 1, 257, 4, 1, f->h ) ; /* length long */
wtag( f, 0, 258, 3, 1, 1 ) ; /* bits/sample short */
wtag( f, 0, 259, 3, 1, compr ) ; /* compresssion(g3=3) short */
wtag( f, 0, 262, 3, 1, 0 ) ; /* photometric(0-min=white) short */
wtag( f, 0, 266, 3, 1, 1 ) ; /* fill order(msb2lsb=1) short */
wtag( f, 1, 273, 4, 1, doff ) ; /* strip offsets long */
wtag( f, 0, 274, 3, 1, 1 ) ; /* orientation(1=normal) short */
wtag( f, 0, 277, 3, 1, 1 ) ; /* samples/pixel short */
wtag( f, 1, 278, 4, 1, f->h ) ; /* rows/strip long */
wtag( f, 1, 279, 4, 1, f->bytes ) ; /* strip byte counts long */
wtag( f, 1, 282, 5, 1, tdoff+0 ) ; /* xresolution ratio */
wtag( f, 1, 283, 5, 1, tdoff+8 ) ; /* yresolution ratio */
wtag( f, 0, 284, 3, 1, 1 ) ; /* storage(1=single plane) short */
wtag( f, 1, 292, 4, 1, 0 ) ; /* g3options long */
wtag( f, 0, 296, 3, 1, 2 ) ; /* resolution units(2=in,3=cm) short */
wtag( f, 0, 327, 3, 1, 0 ) ; /* clean fax(0=clean) short */
fwrite4 ( 0, f ) ; /* offset to next dir (no more) */
/* ==> tdoff (tag data offset), write ratios for floats here */
fwrite4 ( f->xres+0.5, f ) ;
fwrite4 ( 1, f ) ;
fwrite4 ( f->yres+0.5, f ) ;
fwrite4 ( 1, f ) ;
/* ==> doff (strip data offset), image data goes here */
return err ;
}
/* Convert array 'runs' of 'nr' run lengths into a bit map 'buf'. Returns
the number of bytes filled. */
int runtobit ( short *runs, int nr, uchar *buf )
{
static uchar zerofill [ 9 ] = {
0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00 } ;
static uchar onefill [ 9 ] = {
0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff } ;
uchar col=0, *buf0 = buf ;
register short len, b=8, bytes ;
while ( nr-- > 0 ) {
len = *runs++ ;
if ( col ) *buf |= onefill [ b ] ; /* right bits of cur. byte */
else *buf &= zerofill [ b ] ;
if ( b > len ) { /* done fill */
b -= len ;
} else { /* continue to next byte */
len -= b ;
buf++ ;
b = 8 ;
if ( ( bytes = len>>3 ) > 0 ) { /* fill >1 byte */
memset ( buf, col, bytes ) ;
len -= bytes*8;
buf += bytes ;
}
*buf = col ; /* flood the rest */
b -= len ;
}
col ^= 0xff ;
}
return buf - buf0 + ( b < 8 ) ;
}
/* Write a PCX file header. */
int fputi ( int i, OFILE *f )
{
putc ( i & 0xff, f->f ) ;
putc ( ( i >> 8 ) & 0xff, f->f ) ;
return 0 ;
}
void pcxinit ( OFILE *f )
{
uchar buf [ 60 ] = { 0x0a, 3, 1, 1 } ; /* magic, version, compr, BPP */
fwrite ( buf, 1, 4, f->f ) ; /* 4 */
fputi ( 0, f ) ; /* 8 xmin, ymin, xmax, ymax */
fputi ( 0, f ) ;
fputi ( f->w-1, f ) ;
fputi ( f->h-1, f ) ;
fputi ( f->xres, f ) ; /* 4 x and y dpi */
fputi ( f->yres, f ) ;
memset ( buf, 0, 48 ) ; /* 48 palette */
fwrite ( buf, 1, 48, f->f ) ;
putc ( 0, f->f ) ; /* 1 reserved */
putc ( 1, f->f ) ; /* 1 planes per pixel */
fputi ( (f->w+15)/16*2, f ) ; /* 2 bytes per line */
memset ( buf, 0, 60 ) ; /* 60 zero */
fwrite ( buf, 1, 60, f->f ) ;
}
/* Write a PCX-compressed scan line. */
void pcxwrite ( OFILE *of, uchar *p, int nb )
{
int c, n, runc ;
FILE *f = of->f ;
runc = *p++ ;
n = 1 ;
for ( nb-- ; nb > 0 ; nb-- ) {
c = *p++ ;
if ( c == runc && n < 63 ) { /* continue run */
n++ ;
} else { /* terminate run */
if ( n > 1 || ( ( runc & 0xc0 ) == 0xc0 ) ) /* output as run */
putc ( n | 0xc0, f ) ;
putc ( runc, f ) ;
runc = c ; /* start new run */
n = 1 ;
}
}
/* last run */
if ( n > 1 || ( ( runc & 0xc0 ) == 0xc0 ) ) /* output as run */
putc ( n | 0xc0, f ) ;
putc ( runc, f ) ;
}
/* Begin/end output pages. If not starting first page (0), terminate
previous page. If output filename pattern is defined, [re-]opens that
file. If not terminating last page (page==EOF), writes file header.
Returns 0 or 2 on errors. */
int nextopage ( OFILE *f, int page )
{
int err = 0 ;
int i, nb=0 ;
uchar *p, codes [ ( RTCEOL * EOLBITS ) / 8 + 3 ] ;
if ( f->f ) { /* terminate previous page */
switch ( f->format ) {
case O_PBM:
break ;
case O_PGM:
break ;
case O_FAX:
case O_TIFF_FAX:
for ( p = codes, i=0 ; i<RTCEOL ; i++ )
p = putcode ( &f->e, EOLCODE, EOLBITS, p ) ;
nb = putcode ( &f->e, 0, 0, p ) - codes ;
fwrite ( codes, 1, nb, f->f ) ;
f->bytes += nb ;
if ( f->format == O_TIFF_FAX ) tiffinit ( f ) ;
break ;
case O_TIFF_RAW:
tiffinit(f) ; /* rewind & update TIFF header */
break ;
case O_PCL:
fprintf ( f->f, PCLEND ) ;
break ;
case O_PS:
fprintf ( f->f, PSPAGEEND ) ;
if ( f->fname || page<0 ) fprintf ( f->f, PSEND, f->lastpageno ) ;
break ;
case O_PCX:
case O_PCX_RAW:
fseek ( f->f, 0, SEEK_SET ) ;
pcxinit ( f ) ;
break ;
}
if ( ferror ( f->f ) ) {
err = msg ("ES2output error:" ) ;
} else {
msg ( "F+ wrote %s as %dx%d pixel %.fx%.f dpi %s page",
f->cfname, f->w, f->h, f->xres, f->yres,
oformatname [f->format] ) ;
switch ( f->format ) {
case O_PS:
msg ( "F (%d lines)", f->pslines ) ;
break ;
case O_TIFF_RAW:
case O_TIFF_FAX:
msg ( "F (%d bytes)", f->bytes ) ;
break ;
default:
msg ( "F " ) ;
break ;
}
}
}
if ( ! err && page >= 0 ) { /* open new file */
if ( f->fname ) {
sprintf ( f->cfname, f->fname, page+1, page+1, page+1 ) ;
if ( ! f->f )
f->f = fopen ( f->cfname, ( f->format == O_PS ) ? "w" : "wb+" ) ;
else
f->f = freopen ( f->cfname, ( f->format == O_PS ) ? "w" : "wb+", f->f ) ;
if ( ! f->f ) {
err = msg ("ES2can't open output file %s:", f->cfname ) ;
}
} else {
f->f = stdout ;
strcpy ( f->cfname, "standard output" ) ;
}
}
/* start new page */
if ( ! err && page >= 0 ) {
switch ( f->format ) {
case O_PBM:
fprintf ( f->f, "P4 %d %d\n", f->w, f->h ) ;
break ;
case O_PGM:
fprintf ( f->f, "P5 %d %d %d\n", f->w/4, f->h/4, 255 ) ;
break ;
case O_FAX:
case O_TIFF_FAX:
if ( f->format == O_TIFF_FAX ) tiffinit ( f ) ;
p = putcode ( &f->e, EOLCODE, EOLBITS, codes ) ;
nb = p - codes ;
fwrite ( codes, 1, nb, f->f ) ;
break ;
case O_TIFF_RAW:
tiffinit ( f ) ;
break ;
case O_PCL:
fprintf ( f->f, PCLBEGIN, (int) f->xres ) ;
break ;
case O_PS:
psinit ( f, ( f->fname || page==0 ), page+1, f->w, f->h, f->w/8 ) ;
break ;
case O_PCX:
case O_PCX_RAW:
fseek ( f->f, 0, SEEK_SET ) ;
pcxinit ( f ) ;
break ;
}
if ( ferror ( f->f ) ) err = msg ("ES2output error:" ) ;
}
/* only count lines/bytes for those formats that don't have
headers or where we will update the headers on closing */
switch ( f->format ) {
case O_FAX:
case O_TIFF_FAX:
case O_PCX:
case O_PCX_RAW:
f->h = 0 ;
f->bytes = nb ;
break ;
}
return err ;
}
/* Output scan line of nr runs no times to output file f. */
void writeline ( OFILE *f, short *runs, int nr, int no )
{
int nb = 0 ;
uchar *p, buf [ MAXCODES ] ;
/* if line to be output, convert to right format */
if ( no > 0 )
switch ( f->format ) {
case O_PBM:
case O_PGM:
case O_PCL:
case O_PS:
case O_TIFF_RAW:
case O_PCX:
case O_PCX_RAW:
nb =