1 /*===========================================================================
2                             g3topbm
3 =============================================================================
4 
5   This program reads a Group 3 FAX file and produces a PBM image.
6 
7   Bryan Henderson wrote this on August 5, 2004 and contributed it to
8   the public domain.
9 
10   This program is designed to be a drop-in replacement for the program
11   of the same name that was distributed with Pbmplus and Netpbm since
12   1989, written by Paul Haeberli <paul@manray.sgi.com>.
13 
14   Bryan used ideas on processing G3 data from Haeberli's code, but did
15   not use any of the code.
16 
17   Others have modified the program since Bryan's initial work, each
18   contributing their work to the public domain.
19 ===========================================================================*/
20 
21 #define _DEFAULT_SOURCE /* New name for SVID & BSD source defines */
22 #define _BSD_SOURCE   /* Make nstring.h define strcaseeq() */
23 
24 #include "pm_c_util.h"
25 #include "pbm.h"
26 #include "shhopt.h"
27 #include "nstring.h"
28 #include "mallocvar.h"
29 #include "g3.h"
30 #include "g3ttable.h"
31 #include "bitreverse.h"
32 #include "bitarith.h"
33 
34 #define LEFTBITS pm_byteLeftBits
35 #define RIGHTBITS pm_byteRightBits
36 
37 #define MAXCOLS 10800
38 #define MAXROWS 14400   /* this allows up to two pages of image */
39 
40 #define WHASHA 3510
41 #define WHASHB 1178
42 
43 #define BHASHA 293
44 #define BHASHB 2695
45 
46 #define HASHSIZE 1021
47 
48 #define MAXFILLBITS (5 * 9600)
49 
50 /*
51 Fill bits are for flow control.  This was important when DRAM was
52 expensive and fax machines came with small buffers.
53 
54 If data arrives too quickly it may overflow the buffer of the
55 receiving device.  On sending devices transmission time of compressed
56 data representing a single row can be shorter than the time required
57 to scan and encode.  The CCITT standard allows sending devices to
58 insert fill bits to put communication on hold in these cases.
59 
60 By the CCITT standard, the maximum transmission time for one row is:
61 
62 100 - 400 pixels/inch 13 seconds
63 (standard mode: 200 pixels/inch)
64 600 pixels/inch 19 seconds
65 1200 pixels/inch 37 seconds
66 
67 If one row is not received within the above limits, the receiving
68 machine must disconnect the line.
69 
70 The receiver may be less patient.  It may opt to disconnect if one row
71 is not received within 5 seconds.
72 */
73 
74 static G3TableEntry * whash[HASHSIZE];
75 static G3TableEntry * bhash[HASHSIZE];
76 
77 
78 struct CmdlineInfo {
79     /* All the information the user supplied in the command line,
80        in a form easy for the program to use.
81     */
82     const char * inputFilespec;  /* Filespec of input file */
83     unsigned int reversebits;
84     unsigned int kludge;
85     unsigned int stretch;
86     unsigned int stop_error;
87     unsigned int expectedLineSize;
88 };
89 
90 
91 
92 static void
parseCommandLine(int argc,const char ** const argv,struct CmdlineInfo * const cmdlineP)93 parseCommandLine(int argc, const char ** const argv,
94                  struct CmdlineInfo * const cmdlineP) {
95 /*----------------------------------------------------------------------------
96    Note that the file spec array we return is stored in the storage that
97    was passed to us as the argv array.
98 -----------------------------------------------------------------------------*/
99     optEntry * option_def;  /* malloc'ed */
100         /* Instructions to OptParseOptions3 on how to parse our options.  */
101     optStruct3 opt;
102 
103     unsigned int option_def_index;
104 
105     unsigned int widthSpec, paper_sizeSpec;
106     const char * paperSize;
107 
108     MALLOCARRAY_NOFAIL(option_def, 100);
109 
110     option_def_index = 0;   /* incremented by OPTENTRY */
111     OPTENT3(0, "reversebits",      OPT_FLAG,  NULL, &cmdlineP->reversebits,
112             0);
113     OPTENT3(0, "kludge",           OPT_FLAG,  NULL, &cmdlineP->kludge,
114             0);
115     OPTENT3(0, "stretch",          OPT_FLAG,  NULL, &cmdlineP->stretch,
116             0);
117     OPTENT3(0, "stop_error",       OPT_FLAG,  NULL, &cmdlineP->stop_error,
118             0);
119     OPTENT3(0, "width",            OPT_UINT,  &cmdlineP->expectedLineSize,
120             &widthSpec,                0);
121     OPTENT3(0, "paper_size",       OPT_STRING, &paperSize,
122             &paper_sizeSpec,           0);
123 
124     opt.opt_table = option_def;
125     opt.short_allowed = FALSE;  /* We have no short (old-fashioned) options */
126     opt.allowNegNum = FALSE;  /* We may have parms that are negative numbers */
127 
128     pm_optParseOptions3(&argc, (char**)argv, opt, sizeof(opt), 0);
129         /* Uses and sets argc, argv, and some of *cmdlineP and others. */
130 
131     if (widthSpec && paper_sizeSpec)
132         pm_error("You can't specify both -width and -paper_size");
133 
134     if (widthSpec) {
135         if (cmdlineP->expectedLineSize < 1)
136             pm_error("-width must be at least 1");
137     } else if (paper_sizeSpec) {
138         if (strcaseeq(paperSize, "A6"))
139             cmdlineP->expectedLineSize = 864;
140         else if (strcaseeq(paperSize, "A5"))
141             cmdlineP->expectedLineSize = 1216;
142         else if (strcaseeq(paperSize, "A4"))
143             cmdlineP->expectedLineSize = 1728;
144         else if (strcaseeq(paperSize, "B4"))
145             cmdlineP->expectedLineSize = 2048;
146         else if (strcaseeq(paperSize, "A3"))
147             cmdlineP->expectedLineSize = 2432;
148         else
149             pm_error("Unrecognized value for -paper_size '%s'.  "
150                      "We recognize only A3, A4, A5, A6, and B4.",
151                      paperSize);
152     } else
153         cmdlineP->expectedLineSize = 0;
154 
155     if (argc-1 == 0)
156         cmdlineP->inputFilespec = "-";
157     else if (argc-1 != 1)
158         pm_error("Program takes zero or one argument (filename).  You "
159                  "specified %d", argc-1);
160     else
161         cmdlineP->inputFilespec = argv[1];
162 }
163 
164 
165 
166 struct BitStream {
167 
168     FILE * fileP;
169     bool reversebits;
170     int shdata;
171         /* 8-bit buffer for rawgetbit(). */
172     unsigned int shbit;
173         /* single bit mask for the bit of 'shdata' that is next in the stream.
174            zero when 'shdata' is empty.
175         */
176     unsigned int zeroBitCount;
177         /* Number of consecutive zero bits the stream has seen.  Note that
178            because an EOL mark ends in a one bit, this starts over for each
179            line.
180         */
181 };
182 
183 
184 
185 static void
readBit(struct BitStream * const bitStreamP,unsigned int * const bitP,const char ** const errorP)186 readBit(struct BitStream * const bitStreamP,
187         unsigned int *     const bitP,
188         const char **      const errorP) {
189 /*----------------------------------------------------------------------------
190    Return the next raw bit from the G3 input stream.
191 
192    Do not call this outside of the bit stream object; Caller is responsible
193    for maintaining object state.
194 -----------------------------------------------------------------------------*/
195     *errorP = NULL;  /* initial assumption */
196 
197     if ((bitStreamP->shbit & 0xff) == 0) {
198         bitStreamP->shdata = getc(bitStreamP->fileP);
199         if (bitStreamP->shdata == EOF)
200             pm_asprintf(errorP, "EOF or error reading file");
201         else {
202             bitStreamP->shbit = 0x80;
203             if ( bitStreamP->reversebits )
204                 bitStreamP->shdata = bitreverse[ bitStreamP->shdata ];
205             }
206     }
207 
208     if (bitStreamP->shdata & bitStreamP->shbit)
209         *bitP = 1;
210     else
211         *bitP = 0;
212 
213     bitStreamP->shbit >>= 1;
214 }
215 
216 
217 
218 static void
readBitAndDetectEol(struct BitStream * const bitStreamP,unsigned int * const bitP,bool * const eolP,const char ** const errorP)219 readBitAndDetectEol(struct BitStream * const bitStreamP,
220                     unsigned int *     const bitP,
221                     bool *             const eolP,
222                     const char **      const errorP) {
223 /*----------------------------------------------------------------------------
224    Same as readBit(), but iff the bit read is the final bit of an EOL
225    mark, return *eolP == TRUE.
226 
227    An EOL mark is 11 zero bits followed by a one.
228 -----------------------------------------------------------------------------*/
229     readBit(bitStreamP, bitP, errorP);
230     if (!*errorP) {
231         bool eol;
232 
233         eol = FALSE;  /* initial assumption */
234         if (*bitP == 0)
235             ++bitStreamP->zeroBitCount;
236         else {
237             if (bitStreamP->zeroBitCount >= 11)
238                 eol = TRUE;
239             bitStreamP->zeroBitCount = 0;
240         }
241         *eolP = eol;
242     }
243 }
244 
245 
246 static void
initBitStream(struct BitStream * const bitStreamP,FILE * const fileP,bool const reversebits)247 initBitStream(struct BitStream * const bitStreamP,
248               FILE *             const fileP,
249               bool               const reversebits) {
250 
251     bitStreamP->fileP        = fileP;
252     bitStreamP->reversebits  = reversebits;
253     bitStreamP->shbit        = 0x00;
254     bitStreamP->zeroBitCount = 0;
255 }
256 
257 
258 
259 static void
skipToNextLine(struct BitStream * const bitStreamP)260 skipToNextLine(struct BitStream * const bitStreamP) {
261 
262     bool eol;
263     const char * error;
264 
265     eol = FALSE;
266     error = NULL;
267 
268     while (!eol && !error) {
269         unsigned int bit;
270 
271         readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
272     }
273 }
274 
275 
276 
277 static void
addtohash(G3TableEntry * hash[],G3TableEntry table[],unsigned int const n,int const a,int const b)278 addtohash(G3TableEntry *     hash[],
279           G3TableEntry       table[],
280           unsigned int const n,
281           int          const a,
282           int          const b) {
283 
284     unsigned int i;
285 
286     for (i = 0; i < n; ++i) {
287         G3TableEntry * const teP = &table[i*2];
288         unsigned int const pos =
289             ((teP->length + a) * (teP->code + b)) % HASHSIZE;
290         if (hash[pos])
291             pm_error("internal error: addtohash fatal hash collision");
292         hash[pos] = teP;
293     }
294 }
295 
296 
297 
298 static G3TableEntry *
hashfind(G3TableEntry * hash[],int const length,int const code,int const a,int const b)299 hashfind(G3TableEntry *       hash[],
300          int            const length,
301          int            const code,
302          int            const a,
303          int            const b) {
304 
305     unsigned int pos;
306     G3TableEntry * te;
307 
308     pos = ((length + a) * (code + b)) % HASHSIZE;
309     te = hash[pos];
310     return ((te && te->length == length && te->code == code) ? te : NULL);
311 }
312 
313 
314 
315 static void
buildHashes(G3TableEntry * (* whashP)[HASHSIZE],G3TableEntry * (* bhashP)[HASHSIZE])316 buildHashes(G3TableEntry * (*whashP)[HASHSIZE],
317             G3TableEntry * (*bhashP)[HASHSIZE]) {
318 
319     unsigned int i;
320 
321     for (i = 0; i < HASHSIZE; ++i)
322         (*whashP)[i] = (*bhashP)[i] = NULL;
323 
324     addtohash(*whashP, &g3ttable_table[0], 64, WHASHA, WHASHB);
325     addtohash(*whashP, &g3ttable_mtable[2], 40, WHASHA, WHASHB);
326 
327     addtohash(*bhashP, &g3ttable_table[1], 64, BHASHA, BHASHB);
328     addtohash(*bhashP, &g3ttable_mtable[3], 40, BHASHA, BHASHB);
329 
330 }
331 
332 
333 
334 static void
makeRowWhite(unsigned char * const packedBitrow,unsigned int const cols)335 makeRowWhite(unsigned char * const packedBitrow,
336              unsigned int    const cols) {
337 
338     unsigned int colByte;
339     for (colByte = 0; colByte < pbm_packed_bytes(cols); ++colByte)
340         packedBitrow[colByte] = PBM_WHITE * 0xff;
341 }
342 
343 
344 
345 static G3TableEntry *
g3code(unsigned int const curcode,unsigned int const curlen,bit const color)346 g3code(unsigned int const curcode,
347        unsigned int const curlen,
348        bit          const color) {
349 /*----------------------------------------------------------------------------
350    Return the position in the code tables mtable and ttable of the
351    G3 code which is the 'curlen' bits long with value 'curcode'.
352 
353    Note that it is the _position_ in the table that determines the meaning
354    of the code.  The contents of the table entry do not.
355 -----------------------------------------------------------------------------*/
356     G3TableEntry * retval;
357 
358     switch (color) {
359     case PBM_WHITE:
360         if (curlen < 4)
361             retval = NULL;
362         else
363             retval = hashfind(whash, curlen, curcode, WHASHA, WHASHB);
364         break;
365     case PBM_BLACK:
366         if (curlen < 2)
367             retval = NULL;
368         else
369             retval = hashfind(bhash, curlen, curcode, BHASHA, BHASHB);
370         break;
371     default:
372         pm_error("INTERNAL ERROR: color is not black or white");
373     }
374     return retval;
375 }
376 
377 
378 
379 static void
writeBlackBitSpan(unsigned char * const packedBitrow,int const cols,int const offset)380 writeBlackBitSpan(unsigned char * const packedBitrow,
381                   int             const cols,
382                   int             const offset) {
383 /*----------------------------------------------------------------------------
384    Write black (="1") bits into packedBitrow[], starting at 'offset',
385    length 'cols'.
386 -----------------------------------------------------------------------------*/
387     unsigned char * const dest = & packedBitrow[offset/8];
388     unsigned int const rs  = offset % 8;
389     unsigned int const trs = (cols + rs) % 8;
390     unsigned int const colBytes = pbm_packed_bytes(cols + rs);
391     unsigned int const last = colBytes - 1;
392 
393     unsigned char const origHead = dest[0];
394     unsigned char const origEnd =  0x00;
395 
396     unsigned int i;
397 
398     for( i = 0; i < colBytes; ++i)
399         dest[i] = PBM_BLACK * 0xff;
400 
401     if (rs > 0)
402         dest[0] = LEFTBITS(origHead, rs) | RIGHTBITS(dest[0], 8-rs);
403 
404     if (trs > 0)
405         dest[last] = LEFTBITS(dest[last], trs) | RIGHTBITS(origEnd, 8-trs);
406 }
407 
408 
409 
410 enum g3tableId {TERMWHITE, TERMBLACK, MKUPWHITE, MKUPBLACK};
411 
412 static void
processG3Code(const G3TableEntry * const teP,unsigned char * const packedBitrow,unsigned int * const colP,bit * const colorP,unsigned int * const countP)413 processG3Code(const G3TableEntry * const teP,
414               unsigned char *      const packedBitrow,
415               unsigned int *       const colP,
416               bit *                const colorP,
417               unsigned int *       const countP) {
418 /*----------------------------------------------------------------------------
419    'teP' is a pointer into the mtable/ttable.  Note that the thing it points
420    to is irrelevant to us; it is only the position in the table that
421    matters.
422 -----------------------------------------------------------------------------*/
423     enum g3tableId const teId =
424         (teP > g3ttable_mtable ? 2 : 0) + (teP - g3ttable_table) % 2;
425 
426     unsigned int teCount;
427 
428     switch(teId) {
429     case TERMWHITE: teCount = (teP - g3ttable_table    ) / 2;      break;
430     case TERMBLACK: teCount = (teP - g3ttable_table - 1) / 2;      break;
431     case MKUPWHITE: teCount = (teP - g3ttable_mtable    ) / 2 * 64; break;
432     case MKUPBLACK: teCount = (teP - g3ttable_mtable - 1) / 2 * 64; break;
433     }
434 
435     switch (teId) {
436     case TERMWHITE:
437     case TERMBLACK: {
438         unsigned int totalRunLength;
439         unsigned int col;
440 
441         col = *colP;
442         totalRunLength = MIN(*countP + teCount, MAXCOLS - col);
443 
444         if (totalRunLength > 0) {
445             if (*colorP == PBM_BLACK)
446                 writeBlackBitSpan(packedBitrow, totalRunLength, col);
447             /* else : Row was initialized to white, so we just skip */
448             col += totalRunLength;
449         }
450         *colorP = !*colorP;
451         *countP = 0;
452         *colP   = col;
453     } break;
454     case MKUPWHITE:
455     case MKUPBLACK:
456         *countP += teCount;
457         break;
458     default:
459         pm_error("Can't happen");
460     }
461 }
462 
463 
464 
465 static void
formatBadCodeException(const char ** const exceptionP,unsigned int const col,unsigned int const curlen,unsigned int const curcode)466 formatBadCodeException(const char ** const exceptionP,
467                        unsigned int  const col,
468                        unsigned int  const curlen,
469                        unsigned int  const curcode) {
470 
471     pm_asprintf(exceptionP,
472                 "bad code word at Column %u.  "
473                 "No prefix of the %u bits 0x%x matches any recognized "
474                 "code word and no code words longer than 13 bits are "
475                 "defined.  ",
476                 col, curlen, curcode);
477 }
478 
479 
480 
481 static void
readFaxRow(struct BitStream * const bitStreamP,unsigned char * const packedBitrow,unsigned int * const lineLengthP,const char ** const exceptionP,const char ** const errorP)482 readFaxRow(struct BitStream * const bitStreamP,
483            unsigned char *    const packedBitrow,
484            unsigned int *     const lineLengthP,
485            const char **      const exceptionP,
486            const char **      const errorP) {
487 /*----------------------------------------------------------------------------
488   Read one line of G3 fax from the bit stream *bitStreamP into
489   packedBitrow[].  Return the length of the line, in pixels, as *lineLengthP.
490 
491   If there's a problem with the line, return as much of it as we can,
492   advance the input stream past the next EOL mark, and put a text
493   description of the problem in newly malloc'ed storage at
494   *exceptionP.  If there's no problem, return *exceptionP = NULL.
495 
496   We guarantee that we make progress through the input stream.
497 
498   Iff there is an error, return a text description of it in newly
499   malloc'ed storage at *errorP and all other specified behavior
500   (including return values) is unspecified.
501 -----------------------------------------------------------------------------*/
502     unsigned int col;
503     unsigned int curlen;
504         /* Number of bits we've read so far for the code we're currently
505            reading
506         */
507     unsigned int fillbits;
508         /* Number of consecutive 0 bits.  Can precede EOL codes */
509     unsigned int curcode;
510         /* What we've assembled so far of the code we're currently reading */
511     unsigned int count;
512         /* Number of consecutive pixels of the same color */
513     bit currentColor;
514         /* The color of the current run of pixels */
515     bool done;
516 
517     makeRowWhite(packedBitrow, MAXCOLS);  /* initialize row */
518 
519     col = 0;
520     curlen = 0;
521     curcode = 0;
522     fillbits = 0;
523     currentColor = PBM_WHITE;
524     count = 0;
525     *exceptionP = NULL;
526     *errorP = NULL;
527     done = FALSE;
528 
529     while (!done) {
530         if (col >= MAXCOLS) {
531             pm_asprintf(exceptionP, "Line is too long for this program to "
532                         "handle -- longer than %u columns", MAXCOLS);
533             done = TRUE;
534         } else {
535             unsigned int bit;
536             bool eol;
537             const char * error;
538 
539             readBitAndDetectEol(bitStreamP, &bit, &eol, &error);
540             if (error) {
541                 if (col > 0)
542                     /* We got at least some of the row, so it's only an
543                        exception, not a fatal error.
544                     */
545                     *exceptionP = error;
546                 else
547                     *errorP = error;
548                 done = TRUE;
549             } else if (eol)
550                 done = TRUE;
551             else {
552                 curcode = (curcode << 1) | bit;
553                 ++curlen;
554 
555 		if (curlen > 11 && curcode == 0x00) {
556 		    if (++fillbits > MAXFILLBITS)
557                 pm_error("Encountered %u consecutive fill bits.  "
558                        "Aborting", fillbits);
559 		}
560 		else if (curlen - fillbits > 13) {
561                     formatBadCodeException(exceptionP, col, curlen, curcode);
562                     done = TRUE;
563                 } else if (curcode != 0) {
564                     const G3TableEntry * const teP =
565                         g3code(curcode, curlen, currentColor);
566                         /* Address of structure that describes the
567                            current G3 code.  Null means 'curcode' isn't
568                            a G3 code yet (probably just the beginning of one)
569                         */
570                     if (teP) {
571                         processG3Code(teP, packedBitrow,
572                                       &col, &currentColor, &count);
573 
574                         curcode = 0;
575                         curlen = 0;
576                     }
577                 }
578             }
579         }
580     }
581     if (*exceptionP)
582         skipToNextLine(bitStreamP);
583 
584     *lineLengthP = col;
585 }
586 
587 
588 
589 static void
freeBits(unsigned char ** const packedBits,unsigned int const rows,bool const stretched)590 freeBits(unsigned char ** const packedBits,
591          unsigned int     const rows,
592          bool             const stretched) {
593 
594     unsigned int row;
595 
596     for (row = 0; row < rows; ++row) {
597         if (stretched && row % 2 == 1) {
598             /* This is just a pointer to the previous row; don't want to
599                free it twice.
600             */
601         } else
602             pbm_freerow_packed(packedBits[row]);
603     }
604     free(packedBits);
605 }
606 
607 
608 
609 static void
handleRowException(const char * const exception,const char * const error,unsigned int const row,bool const tolerateErrors)610 handleRowException(const char * const exception,
611                    const char * const error,
612                    unsigned int const row,
613                    bool         const tolerateErrors) {
614 
615 
616     if (exception) {
617         if (tolerateErrors)
618             pm_message("Problem reading Row %u.  Skipping rest of row.  %s",
619                        row, exception);
620         else
621             pm_error("Problem reading Row %u.  Aborting.  %s", row, exception);
622         pm_strfree(exception);
623     }
624 
625     if (error) {
626         if (tolerateErrors)
627             pm_message("Unable to read Row %u.  Skipping rest of image.  %s",
628                        row, error);
629         else
630             pm_error("Unable to read Row %u.  Aborting.  %s", row, error);
631         pm_strfree(error);
632     }
633 }
634 
635 
636 
637 typedef struct {
638     unsigned int expectedLineSize;
639         /* The size that lines are supposed to be.  Zero means we're happy
640            with any size.
641         */
642     unsigned int maxLineSize;
643         /* The maximum line size we have seen so far, or zero if we have
644            not seen any lines yet.
645         */
646     bool warned;
647         /* We have warned the user that he has a line length problem */
648     bool tolerateErrors;
649         /* Try to continue when we detect a line size error, as opposed to
650            aborting the program.
651         */
652 } lineSizeAnalyzer;
653 
654 
655 
656 static void
initializeLineSizeAnalyzer(lineSizeAnalyzer * const analyzerP,unsigned int const expectedLineSize,bool const tolerateErrors)657 initializeLineSizeAnalyzer(lineSizeAnalyzer * const analyzerP,
658                            unsigned int       const expectedLineSize,
659                            bool               const tolerateErrors) {
660 
661     analyzerP->expectedLineSize = expectedLineSize;
662     analyzerP->tolerateErrors   = tolerateErrors;
663 
664     analyzerP->maxLineSize = 0;
665     analyzerP->warned      = FALSE;
666 }
667 
668 
669 
670 static void
analyzeLineSize(lineSizeAnalyzer * const analyzerP,unsigned int const thisLineSize)671 analyzeLineSize(lineSizeAnalyzer * const analyzerP,
672                 unsigned int       const thisLineSize) {
673 
674     const char * error;
675 
676     if (analyzerP->expectedLineSize &&
677         thisLineSize != analyzerP->expectedLineSize)
678         pm_asprintf(&error, "Image contains a line of %u pixels.  "
679                     "You specified lines should be %u pixels.",
680                     thisLineSize, analyzerP->expectedLineSize);
681     else {
682         if (analyzerP->maxLineSize && thisLineSize != analyzerP->maxLineSize)
683             pm_asprintf(&error, "There are at least two different "
684                         "line lengths in this image, "
685                         "%u pixels and %u pixels.  "
686                         "This is a violation of the G3 standard.  ",
687                         thisLineSize, analyzerP->maxLineSize);
688         else
689             error = NULL;
690     }
691 
692     if (error) {
693         if (analyzerP->tolerateErrors) {
694             if (!analyzerP->warned) {
695                 pm_message("Warning: %s.", error);
696                 analyzerP->warned = TRUE;
697             }
698         } else
699             pm_error("%s", error);
700 
701         pm_strfree(error);
702     }
703     analyzerP->maxLineSize = MAX(thisLineSize, analyzerP->maxLineSize);
704 }
705 
706 
707 
708 /* An empty line means EOF.  An ancient comment in the code said there
709    is supposed to be 6 EOL marks in a row to indicate EOF, but the code
710    checked for 3 and considered 2 in a row just to mean a zero length
711    line.  Starting in Netpbm 10.24 (August 2004), we assume there is
712    no valid reason to have an empty line and recognize EOF as any
713    empty line.  Alternatively, we could read off and ignore two empty
714    lines without a 3rd.
715 */
716 
717 static void
readFax(struct BitStream * const bitStreamP,bool const stretch,unsigned int const expectedLineSize,bool const tolerateErrors,unsigned char *** const packedBitsP,unsigned int * const colsP,unsigned int * const rowsP)718 readFax(struct BitStream * const bitStreamP,
719         bool               const stretch,
720         unsigned int       const expectedLineSize,
721         bool               const tolerateErrors,
722         unsigned char ***  const packedBitsP,
723         unsigned int *     const colsP,
724         unsigned int *     const rowsP) {
725 
726     lineSizeAnalyzer lineSizeAnalyzer;
727     unsigned char ** packedBits;
728     const char * error;
729     bool eof;
730     unsigned int row;
731 
732     MALLOCARRAY_NOFAIL(packedBits, MAXROWS);
733 
734     initializeLineSizeAnalyzer(&lineSizeAnalyzer,
735                                expectedLineSize, tolerateErrors);
736 
737     eof = FALSE;
738     error = NULL;
739     row = 0;
740 
741     while (!eof && !error) {
742         unsigned int lineSize;
743 
744         if (row >= MAXROWS)
745             pm_asprintf(&error, "Image is too tall.  This program can "
746                         "handle at most %u rows", MAXROWS);
747         else {
748             const char * exception;
749 
750             packedBits[row] = pbm_allocrow_packed(MAXCOLS);
751             readFaxRow(bitStreamP, packedBits[row],
752                        &lineSize, &exception, &error);
753 
754             handleRowException(exception, error, row, tolerateErrors);
755 
756             if (!error) {
757                 if (lineSize == 0) {
758                     /* EOF.  See explanation above */
759                     eof = TRUE;
760                 } else {
761                     analyzeLineSize(&lineSizeAnalyzer, lineSize);
762 
763                     if (stretch) {
764                         ++row;
765                         if (row >= MAXROWS)
766                             pm_asprintf(&error, "Image is too tall.  This "
767                                         "program can handle at most %u rows "
768                                         "after stretching", MAXROWS);
769                         else
770                             packedBits[row] = packedBits[row-1];
771                     }
772                     ++row;
773                 }
774             }
775         }
776     }
777     *rowsP        = row;
778     *colsP        = lineSizeAnalyzer.maxLineSize;
779     *packedBitsP  = packedBits;
780 }
781 
782 
783 
784 int
main(int argc,const char * argv[])785 main(int argc, const char * argv[]) {
786 
787     struct CmdlineInfo cmdline;
788     FILE * ifP;
789     struct BitStream bitStream;
790     unsigned int rows, cols;
791     unsigned char ** packedBits;
792 
793     pm_proginit(&argc, argv);
794 
795     parseCommandLine(argc, argv, &cmdline);
796 
797     ifP = pm_openr(cmdline.inputFilespec);
798 
799     initBitStream(&bitStream, ifP, cmdline.reversebits);
800 
801     if (cmdline.kludge) {
802         /* Skip extra lines to get in sync. */
803         skipToNextLine(&bitStream);
804         skipToNextLine(&bitStream);
805         skipToNextLine(&bitStream);
806     }
807     skipToNextLine(&bitStream);
808 
809     buildHashes(&whash, &bhash);
810 
811     readFax(&bitStream, cmdline.stretch, cmdline.expectedLineSize,
812             !cmdline.stop_error,
813             &packedBits, &cols, &rows);
814 
815     pm_close(ifP);
816 
817     if (cols > 0 && rows > 0) {
818         unsigned int row;
819         pbm_writepbminit(stdout, cols, rows, 0);
820         for (row = 0; row < rows; ++row)
821             pbm_writepbmrow_packed(stdout, packedBits[row], cols, 0);
822     } else
823         pm_error("No image data in input");
824 
825     pm_close(stdout);
826 
827     freeBits(packedBits, rows, cmdline.stretch);
828 
829     return 0;
830 }
831