dtsr: Coverity fixes for string buffer issues
[oweals/cde.git] / cde / programs / dtsr / huffcode.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /*
24  *   COMPONENT_NAME: austext
25  *
26  *   FUNCTIONS: build_tree
27  *              char_label
28  *              huffman_code
29  *              init_treebase
30  *              main
31  *              next_sorted_node
32  *              print_usage
33  *              strrev
34  *              user_args_processor
35  *
36  *   ORIGINS: 27
37  *
38  *
39  *   (C) COPYRIGHT International Business Machines Corp. 1990,1996
40  *   All Rights Reserved
41  *   Licensed Materials - Property of IBM
42  *   US Government Users Restricted Rights - Use, duplication or
43  *   disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
44  */
45 /************************** HUFFCODE.C ******************************
46  * $XConsortium: huffcode.c /main/9 1996/11/14 15:31:05 rcs $
47  * 12/90.
48  * Counts frequency of occurrance of every possible byte value of input text.
49  * Creates Huffman Code Table based on byte frequencies and writes it
50  * in 2 formats to 2 different output files.
51  * The encode table (.huf) maintains the frequency counts and explicitly
52  * includes the huffman code strings.  Generally speaking, the .huf file
53  * is intended for humans to read.  The decode table (.c) is an array
54  * of integers meant to be compiled into an object module, then linked
55  * into the decode program.  The .c format closely resembles the original 
56  * huffman code tree in this program.
57  * By keeping the tree as an obscure array of integers,
58  * the huffman code can double as an encryption technique,
59  * and the decoding method kept somewhat proprietary.
60  * 
61  * For a good discussion of Huffman codes and related algorithms,
62  * see "Data Compression Techniques and Applications Hardware and
63  * Software Considerations" by Gilbert Held and Thomas R. Marshall.
64  * The tree itself is balanced to minimize longest bitstring length
65  * per Eugene Schwartz, Information and Control 7, 37-44 (1964).
66  * 
67  * At beginning of each new execution, the program tries to
68  * open the .huf table file and continue byte frequency counting
69  * from the last run.
70  * If the .huf file doesn't exist, the table's counts are
71  * initialized to zeroes.  The .c decode table is recomputed fresh
72  * each run, whether it existed before or not.
73  * 
74  * If the input file is not specified then the frequencies in the table
75  * are not changed, and the huffman codes are recomputed with the
76  * existing frequencies.
77  * 
78  * THIS PROGRAM DOES NOT CHECK .HUF FILE FORMAT!--it had better be correct.
79  * 
80  * HUFFMAN ENCODE TABLE (.huf) FILE FORMAT:
81  * Each line represents each possible byte value (0 - 255),
82  * the huffman 'literal' character (#256), or comments.
83  * There are exactly 257 lines sorted by decreasing count.
84  * There are four fields, each separated by one or more tabs (\t).
85  * 
86  * 1.  CHARACTER.  a number from 0 to 256.
87  * 
88  *      The 'character' represented by the number 256 is the literal.
89  *      it represents all characters whose frequency is so low that
90  *      there is no huffman code translation--this reduces the max
91  *      length of the coded bit string when there are lots of zero
92  *      or low frequency bytes in the input.  For example,
93  *      pure ascii text files only occasionally have byte values
94  *      less than 32 (control chars) and rarely greater than 127
95  *      (high order bit turned on).
96  * 
97  * 2.  HUFFMAN CODE.  a string of binary digits (0's and 1's).
98  *      Each string is unique to that character.
99  *      This field will consist of a single blank, when the character
100  *      will be coded by the huffman literal.  If the code of
101  *      the literal itself is blank, then literal coding is
102  *      not used in this table--all characters are represented
103  *      by complete huffman code strings.
104  * 
105  * 3.  COUNT.  The number of times this character appeared in the text.
106  *      The literal's count equals the sum of the counts of all the
107  *      real characters which are represented by the literal.
108  *      The literal's count may be 0 if all the characters it
109  *      represents have zero frequencies.
110  * 
111  * 4.  COMMENTS.  A label depicting the printable char or its description.
112  * 
113  * HUFFMAN DECODE TABLE (.c) FILE FORMAT:
114  * A sequence of integers formatted as a C array of integer pairs
115  * and intended to be compiled and linked into the decode program.
116  * Each huffman tree node contains two integers.
117  * The root of the tree is the LAST integer pair.
118  * The first (left) integer contains the array index down the '0' branch,
119  * the right integer points down the '1' branch.
120  * However if an integer is negative,  the decoding ceases and the
121  * resulting plaintext character is the negative integer + 257,
122  * and will always be in the range 0 - 255, or 256 for the literal code.
123  *
124  * $Log$
125  * Revision 2.3  1996/03/25  18:55:04  miker
126  * Changed FILENAME_MAX to _POSIX_PATH_MAX.
127  *
128  * Revision 2.2  1995/10/25  17:50:34  miker
129  * Added prolog.
130  *
131  * Revision 2.1  1995/09/22  20:46:28  miker
132  * Freeze DtSearch 0.1, AusText 2.1.8
133  *
134  * Revision 1.4  1995/09/19  22:04:11  miker
135  * Print out nonascii chars in .huf file comments.
136  *
137  * Revision 1.3  1995/09/05  18:08:00  miker
138  * Name changes for DtSearch.
139  */
140 #include "SearchP.h"
141 #include <limits.h>
142 #include <ctype.h>
143 #include <errno.h>
144 #include <locale.h>
145 #include <sys/stat.h>
146 #include <stdlib.h>
147
148 #define MS_huff         30      /* message catalog set number */
149 #define DELIMITERS      "\t\n"  /* betw fields in .huf file */
150 #define LAST_BIT        '-'
151 #define MAX_BITLEN      24
152 #define MAX_NODES       514
153  /*
154   * 256 chars + 'literal' char = max leaves, therefore max treesize
155   * = 2n - 1 = 513 
156   */
157
158 /*----------------------- HCTREE --------------------------*/
159 /* tree is also a table so tree ptrs are table indexes:
160  * 0 - 255 =    characters themselves (leaves at base of tree).
161  *     256 =    literal code (special char/leaf).
162  *   > 256 =    higher nodes.
163  *              Global 'last_node' = highest actual node alloc so far.
164  *              When tree completed, last_node = root of tree.
165  *      -1 =    null links.
166  */
167 typedef struct {
168     char            bit;        /* '0' or '1' (assoc with link to
169                                  * father) */
170     long            count;      /* freq of occurrance of char */
171     int             sort;       /* specifies output sort order */
172     int             father;     /* index points UP toward root of
173                                  * tree */
174     int             son0;       /* index of '0' (left) subnode */
175     int             son1;       /* index of '1' (right) subnode */
176 }               HCTREE;
177
178 /*------------------------ GLOBALS ---------------------------*/
179 static int      last_node = 256;
180 long            total_count;
181 long            literal_threshold = 0L;
182 int             debug_switch = FALSE;
183 int             literal_coding_on = TRUE;
184 int             input_file_specified;   /* TRUE if user enters
185                                          * filename */
186 int             no_huffcode_file;       /* TRUE if table file not
187                                          * found */
188 HCTREE          hctree1[MAX_NODES];
189 char            filename_input[_POSIX_PATH_MAX];
190 char            filename_huf[_POSIX_PATH_MAX];
191 char            filename_huc[_POSIX_PATH_MAX];
192
193 #ifndef TURBO_COMPILER
194 /****************************************/
195 /*                                      */
196 /*               strrev                 */
197 /*                                      */
198 /****************************************/
199 static char    *strrev (char *string)
200 {
201     int             i;
202     int             j;
203     char            temp;
204
205     for (i = 0, j = strlen (string) - 1; i < j; i++, j--) {
206         temp = string[i];
207         string[i] = string[j];
208         string[j] = temp;
209     }
210     return string;
211 }
212
213 #endif  /* !TURBO_COMPILER */
214
215 /****************************************/
216 /*                                      */
217 /*             Build Tree               */
218 /*                                      */
219 /****************************************/
220 /* Each call joins the two nodes with smallest count
221  * into a single higher level node.  If there are more than 2 nodes with
222  * similar 'smallest' counts, then within that group the 2 nodes with the
223  * shortest current bitstring length are joined.
224  * Returns TRUE for each successful lower level join.
225  * Returns FALSE when final join is made at highest level (root).
226  */
227 static int      build_tree (void)
228 {
229     int             i, j;
230     int             low0 = -1;
231     int             low1 = -1;
232     int             len0 = 0;
233     int             len1 = 0;
234     int             curr;
235
236     /* find 2 lowest counts */
237     for (i = 0; i < 257; i++) {
238         /* skip over real chars with counts <= 'literal' threshold */
239         if (literal_coding_on
240             && i != 256
241             && hctree1[i].count <= literal_threshold) {
242             hctree1[i].sort = MAX_BITLEN + 1;
243             continue;
244         }
245
246         /* skip over literal if literal coding turned off */
247         if (i == 256 && !literal_coding_on) {
248             hctree1[256].sort = MAX_BITLEN + 1;
249             continue;
250         }
251
252         /*
253          * Ascend to highest tree level for current table entry,
254          * putting length of bitstring into sort field. Save
255          * highest tree level in curr. 
256          */
257         hctree1[i].sort = 0;
258         for (j = i; j != -1; j = hctree1[j].father) {
259             hctree1[i].sort++;
260             curr = j;
261         }
262
263         /*
264          * sanity checks after ascending tree: 1. if bit strings
265          * have grown too large, quit. 2. if curr points to top
266          * tree level, quit. 
267          */
268         if (hctree1[i].sort > MAX_BITLEN) {
269             fprintf (stderr, "%s", catgets(dtsearch_catd, MS_huff, 30,
270                 "\n183 Bit strings have grown too large.  You probably "
271                 "have literals\n  turned off with grossly unbalanced "
272                 "character counts.\n\7"));
273             exit (2);
274         }
275         if (hctree1[curr].count >= total_count) {
276             fprintf (stderr, "%s", catgets(dtsearch_catd, MS_huff, 31,
277                 "\n191 Programming Error:  Still trying to build\n"
278                 "  Huffman Code Tree after root created.\n\7"));
279             exit (2);
280         }
281
282         /*
283          * if curr ptr already joins low0 or low1, try the next
284          * table entry 
285          */
286         if (curr == low0 || curr == low1)
287             continue;
288
289         /*
290          * If curr count is less than low0, or if curr count = low0
291          * but curr bitstring length is less, replace both low0 and
292          * low1. (that way, we keep low0 always <= low1) 
293          */
294         if (low0 == -1 || hctree1[curr].count < hctree1[low0].count ||
295             (hctree1[curr].count == hctree1[low0].count && hctree1[i].sort < len0)) {
296             low1 = low0;
297             len1 = len0;
298             low0 = curr;
299             len0 = hctree1[i].sort;
300             continue;
301         }
302
303         /*
304          * At this point curr count is 'greater' than low0. If curr
305          * count is less than low1, or if curr count = low1 but
306          * curr bitstring length is less, replace only low1 
307          */
308         if (low1 == -1 || hctree1[curr].count < hctree1[low1].count ||
309             (hctree1[curr].count == hctree1[low1].count && hctree1[i].sort < len1)) {
310             low1 = curr;
311             len1 = hctree1[i].sort;
312             continue;
313         }
314
315         /*
316          * default: curr count is greater than BOTH low0 and low1,
317          * try next table entry 
318          */
319     }   /* end loop to find two lowest counts */
320
321     /* low0 and low1 now point to two lowest count nodes.
322      * link in low0 and low1 to next available new node.
323      */
324     last_node++;
325     hctree1[low0].bit = '0';
326     hctree1[low0].father = last_node;
327     hctree1[low1].bit = '1';
328     hctree1[low1].father = last_node;
329     hctree1[last_node].bit = LAST_BIT;
330     hctree1[last_node].father = -1;
331     hctree1[last_node].count = hctree1[low0].count + hctree1[low1].count;
332     hctree1[last_node].son0 = low0;
333     hctree1[last_node].son1 = low1;
334
335     if (debug_switch)
336         printf ("%3d: low0=%6ld\tlow1=%6ld\tsum=%6ld\t(%ld)\n",
337             last_node, hctree1[low0].count, hctree1[low1].count,
338             hctree1[last_node].count, total_count);
339
340     if (hctree1[last_node].count < total_count)
341         return TRUE;
342     else
343         return FALSE;
344 }  /* end of function build_tree */
345
346 /****************************************/
347 /*                                      */
348 /*             Char Label               */
349 /*                                      */
350 /****************************************/
351 static char    *char_label (int x)
352 {
353     static char     buf[64];
354
355     switch (x) {
356         case 0:
357             return "NULL";
358         case 8:
359             return "\\b (backspace)";
360         case 9:
361             return "\\t (tab)";
362         case 10:
363             return "\\n (linefeed)";
364         case 11:
365             return "\\v (vert tab)";
366         case 12:
367             return "\\f (form feed)";
368         case 13:
369             return "\\r (carr retn)";
370         case 26:
371             return "CTRL-Z (EOF)";
372         case 27:
373             return "CTRL-[ (ESC)";
374         case 31:
375             return "CTRL-dash";
376         case 32:
377             return "SPACE (blank)";
378         case 45:
379             return "- (dash)";
380         case 95:
381             return "_ (underscore)";
382         case 127:
383             return "DEL";
384         case 256:
385             return "*** LITERAL CODE ***";
386         default:
387             if (x > 256)
388                 return "";
389             else if (x < 32) {
390                 snprintf(buf, sizeof(buf), "'CTRL-%c'", 0x40 | x);
391                 return buf;
392             }
393             else if (x >= 128) {
394                 snprintf(buf, sizeof(buf), "%s", catgets(dtsearch_catd, MS_huff, 32,
395                     "(nonascii char, high bit set)"));
396                 return buf;
397             }
398             else {
399                 sprintf (buf, "'%c'", x);
400                 return buf;
401             }
402     }
403 }  /* end of function char_label */
404
405 /****************************************/
406 /*                                      */
407 /*           Next Sorted Node           */
408 /*                                      */
409 /****************************************/
410 /* Called repeatedly, returns the next treebase node in sorted order.
411  * Sort order is by length of Huffman Code String.
412  * Caller must pass index of last node returned (neg at first call).
413  * Lasti should never be larger than treebase.
414  */
415 static int      next_sorted_node (int lasti)
416 {
417     int             i;
418     int             nexti = -1;
419     long            nextsortval = MAX_BITLEN + 2;
420
421     /* permanently mark last returned node as unavailable */
422     if (lasti >= 0)
423         hctree1[lasti].sort = MAX_BITLEN + 2;
424
425     /* find next shortest string length */
426     for (i = 0; i < 257; i++)
427         if (hctree1[i].sort < nextsortval) {
428             nextsortval = hctree1[i].sort;
429             nexti = i;
430         }
431     return nexti;
432 }  /* end of function next_sorted_node */
433
434 /****************************************/
435 /*                                      */
436 /*         Initialize Treebase          */
437 /*                                      */
438 /****************************************/
439 /* 'Treebase' is original 257 character nodes (including literal code).
440  * If huffcode table file exists, initializes treebase with its values,
441  * else initializes treebase with zero counts.
442  */
443 static void     init_treebase (void)
444 {
445     int             i;
446     FILE           *instream_huf;
447     char            filebuf[128];
448
449     total_count = 0L;
450
451     /* .huf table file does not exist--zero all counts */
452     if ((instream_huf = fopen (filename_huf, "r")) == NULL) {
453         no_huffcode_file = TRUE;
454         for (i = 0; i < 257; i++) {
455             hctree1[i].bit = LAST_BIT;
456             hctree1[i].count = 0L;
457             hctree1[i].father = -1;
458             hctree1[i].son0 = -1;
459             hctree1[i].son1 = -1;
460         }
461     }
462
463     /* Table file exists--init treebase with values from file.
464      * We are only interested in the character itself (i),
465      * and its current count.  All other fields will be recreated
466      * at output time.  FILE FORMAT IS NOT CHECKED--IT HAD BETTER BE CORRECT!
467      */
468     else {
469         no_huffcode_file = FALSE;
470         if(NULL == fgets (filebuf, sizeof (filebuf) - 1, instream_huf)) {
471                 fprintf (stderr, "No first line in file\n");
472                 exit(2);
473         }
474         /* discard this first line (don't need id stamp) */
475         while (fgets (filebuf, sizeof (filebuf) - 1, instream_huf)
476             != NULL) {
477             i = atoi (strtok (filebuf, DELIMITERS));    /* char */
478             if (i < 0 || i > 256) {
479                 fprintf (stderr, catgets(dtsearch_catd, MS_huff, 33,
480                     "366 Invalid file format for %s.\n"),
481                     filename_huf);
482                 exit (2);
483             }
484             strtok (NULL, DELIMITERS);  /* skip over current huff
485                                          * code */
486             hctree1[i].count = (i == 256) ?
487                 0L : atol (strtok (NULL, DELIMITERS));
488             hctree1[i].bit = LAST_BIT;
489             hctree1[i].father = -1;
490             hctree1[i].son0 = -1;
491             hctree1[i].son1 = -1;
492             if (i != 256)
493                 total_count += hctree1[i].count;
494         }       /* endwhile loop that reads each table line */
495         fclose (instream_huf);
496     }
497     return;
498 }  /* end of function init_treebase */
499
500 /****************************************/
501 /*                                      */
502 /*            Huffman Code              */
503 /*                                      */
504 /****************************************/
505 /* determines correct huffman code based on current counts in tree,
506  * writes out all to both files overlaying previous values if they existed.
507  */
508 static void     huffman_code (time_t idstamp)
509 {
510     int             i;  /* current char */
511     int             lasti;
512     int             j;  /* ascends tree from i to build bit_string */
513     char            bit_string[MAX_BITLEN + 4];
514     char            sprintbuf[128];
515     char           *bitptr;
516     FILE           *outstream_huc;
517     FILE           *outstream_huf;
518
519     /* establish the 'literal' node (char #256) count
520      * equal to sum of all chars whose counts are less than threshold.
521      */
522     if (literal_coding_on) {
523         hctree1[256].count = 0L;
524         for (i = 0; i < 256; i++)
525             if (hctree1[i].count <= literal_threshold)
526                 hctree1[256].count += hctree1[i].count;
527     }
528
529     /* build the Huffman Code tree, and determine root (last_node) */
530     while (build_tree ());
531
532     /* now that we know the total number of tree nodes (last_node),
533      * we are ready to write.  
534      * Open both output files and verify they are not write protected.
535      */
536     if ((outstream_huc = fopen (filename_huc, "w")) == NULL) {
537         fprintf (stderr, catgets(dtsearch_catd, MS_huff, 34,
538             "424 File '%s' failed to open for write.  Is it read-only?\n"),
539             filename_huc);
540         exit (2);
541     }
542     if ((outstream_huf = fopen (filename_huf, "w")) == NULL) {
543         fprintf (stderr, catgets(dtsearch_catd, MS_huff, 34,
544             "439 File '%s' failed to open for write.  Is it read-only?\n"),
545             filename_huf);
546         exit (2);
547     }
548
549     /* create the .c decode file (tree as integer array) */
550     fprintf (outstream_huc,
551         "#include <time.h>\n"
552         "char   *hctree_name =\t\"%s\";\n"
553         "time_t hctree_id =\t%ldL;\n"
554         "int    hctree_root =\t%d;\n"
555         "static int hctree_array[] = {\n",
556         filename_huc, idstamp, last_node - 257);
557     for (i = 257; i <= last_node; i++) {
558         fprintf (outstream_huc, "\t%4d,\t%4d%c\t/* %3d */\n",
559             hctree1[i].son0 - 257, hctree1[i].son1 - 257,
560             (i == last_node) ? ' ' : ',',       /* no comma after last
561                                                  * one */
562             i - 257);   /* comment contains node number */
563     }
564     fprintf (outstream_huc, "\t};\nint *hctree =\thctree_array;\n");
565     fclose (outstream_huc);
566
567     /* write out the tree base (0-256) in sorted order to .huf file */
568     fprintf (outstream_huf, "%ld\tHCTREE_ID\n", idstamp);
569     for (lasti = -1; (i = next_sorted_node (lasti)) >= 0; lasti = i) {
570         /*
571          * Create huffman code digit string. j ascends tree from i
572          * to build string in reverse order. 
573          */
574         bitptr = bit_string;
575         for (j = i; j != -1; j = hctree1[j].father)
576             *bitptr++ = hctree1[j].bit;
577         *bitptr = '\0'; /* terminate reversed string */
578         strrev (bit_string);    /* reverse the string order */
579         if (bit_string[1] == 0)
580             strcpy (bit_string, "  ");
581         if (strlen (bit_string) < 9)
582             strcat (bit_string, "\t");
583
584         /* write out the line for this char */
585         sprintf (sprintbuf, "%d\t%s\t%ld\t%s\n",
586             i,
587             bit_string + 1,     /* hop over LAST_BIT */
588             hctree1[i].count,
589             char_label (i));
590         fprintf (outstream_huf, "%s", sprintbuf);
591
592     }   /* end forloop printing out each tree base entry */
593
594     fclose (outstream_huf);
595     return;
596 }  /* end of function huffman_code */
597
598 /****************************************/
599 /*                                      */
600 /*             Print Usage              */
601 /*                                      */
602 /****************************************/
603 static void     print_usage (void)
604 {
605     fprintf (stderr, catgets(dtsearch_catd, MS_huff, 35,
606 "USAGE: huffcode [-lN | -l-] [-o] <huffname> [<infile>]\n"
607 "  -l<N> specifies the 'literal' threshold count.  Any character occurring\n"
608 "       <= <N> times will be coded with the Huffman literal.  Default is -l0,\n"
609 "       literal coding only for bytes with counts of zero.\n"
610 "  -l-  turns off literal coding.  Turning off literal coding in unbalanced\n"
611 "       trees leads to EXTREMELY LONG bit string codes--don't do it unless\n"
612 "       the input is known to be a well balanced binary file.\n"
613 "  -o   preauthorizes overwriting any currently existing decode file.\n"
614 "  <huffname> is the filename prefix for the Huffman Code files.\n"
615 "       If the encode file (%s) already exists, byte counts from infile will\n"
616 "       be added to it, otherwise it will be newly created.\n"
617 "       The decode file (%s) is always newly created each run.\n"
618 "  <infile>   is an input file containing bytes to be counted.\n"
619 "       It may be omitted if the encode file already exists.\n"),
620         EXT_HUFFCODE, EXT_HDECODE);
621     return;
622 }  /* end of function print_usage */
623
624 /********************************************************/
625 /*                                                      */
626 /*                 USER_ARGS_PROCESSOR                  */
627 /*                                                      */
628 /********************************************************/
629 /* handles command line arguments for 'main' */
630 static void     user_args_processor (int argc, char **argv)
631 {
632     char           *argptr;
633     int             OK_to_overwrite = FALSE;
634     FILE           *stream;
635
636     if (argc <= 1) {    /* user just wants to see usage msg */
637         print_usage ();
638         exit (1);
639     }
640
641     /* each pass grabs new parm of "-xxx" format */
642     while (--argc > 0 && (*++argv)[0] == '-') {
643         argptr = argv[0];
644         argptr[1] = tolower (argptr[1]);
645         switch (argptr[1]) {
646             case 'l':   /* literal threshold */
647                 if (argptr[2] == 0)
648                     goto BADARG;
649                 else if (argptr[2] == '-')
650                     literal_coding_on = FALSE;
651                 else
652                     literal_threshold = atoi (argptr + 2);
653                 break;
654
655             case 'o':   /* OK_to_overwrite .c file if it already
656                          * exists */
657                 OK_to_overwrite = TRUE;
658                 break;
659
660             case 'v':   /* verbose mode = debug switch */
661                 debug_switch = TRUE;
662                 break;
663
664         BADARG:
665             default:
666                 fprintf (stderr, catgets(dtsearch_catd, MS_huff, 36,
667                     "'%s' is invalid argument.\n"), argptr);
668                 print_usage ();
669                 exit (2);       /* ABORT program */
670
671         }       /* endswitch */
672     }   /* endwhile for cmd line '-'processing */
673
674     /* test for required tree file name */
675     if (argc <= 0) {
676         fprintf (stderr, "%s", catgets(dtsearch_catd, MS_huff, 37,
677             "576 Missing Huffman Code file names prefix.\n"));
678         print_usage ();
679         exit (2);
680     }
681     /* create 2 output file names from passed argument */
682     strncpy (filename_huf, argv[0], _POSIX_PATH_MAX);
683     filename_huf[_POSIX_PATH_MAX - 6] = 0;
684     strcat (filename_huf, EXT_HUFFCODE);
685     strncpy (filename_huc, argv[0], _POSIX_PATH_MAX);
686     filename_huc[_POSIX_PATH_MAX - 6] = 0;
687     strcat (filename_huc, EXT_HDECODE);
688
689     /* Since the decode file is a C source code file (.c extension),
690      * we want to be sure not to erase somebody's source program.
691      * So if the .c file already exists, and the user didn't specify
692      * overwrite in a command line argument, ask him now if it's OK to
693      * blow away the old file.
694      */
695     if (!OK_to_overwrite)
696         if ((stream = fopen (filename_huc, "r")) != NULL) {
697             fclose (stream);
698             printf (catgets(dtsearch_catd, MS_huff, 38,
699                 "Decode file '%s' already exists.  "
700                 "Is it OK to overwrite it? [y/n] "),
701                 filename_huc);
702             if (toupper (getchar ()) != 'Y')
703                 exit (2);
704         }
705
706     /* test for optional input file name */
707     if (--argc <= 0)
708         input_file_specified = FALSE;
709     else {
710         input_file_specified = TRUE;
711         strncpy (filename_input, argv[1], _POSIX_PATH_MAX);
712         filename_input[_POSIX_PATH_MAX - 1] = 0;
713     }
714
715     return;
716 }  /* end of function user_args_processor */
717
718 /****************************************/
719 /*                                      */
720 /*                 Main                 */
721 /*                                      */
722 /****************************************/
723 int             main (int argc, char *argv[])
724 {
725     FILE           *instream;
726     struct stat     fstat_input;
727     long            bytes_in = 0L;
728     int             mychar;
729     time_t          now, start_stamp;
730
731     setlocale (LC_ALL, "");
732     dtsearch_catd = catopen (FNAME_DTSRCAT, 0);
733     printf (catgets(dtsearch_catd, MS_huff, 40,
734         "HUFFCODE Version %s\n"), AUSAPI_VERSION);
735
736     /* validate user's command line arguments */
737     user_args_processor (argc, argv);
738
739     /* initialize tree table, using the table file if it exists */
740     init_treebase ();
741     if (total_count == 0L)
742         printf ("%s", catgets(dtsearch_catd, MS_huff, 41,
743             "Huffman Code Tables will be newly created.\n"));
744     else
745         printf (catgets(dtsearch_catd, MS_huff, 42,
746             "Table '%s' already contains %ld Kbytes from previous runs.\n"),
747             filename_huf, total_count / 1000L);
748
749     if (!input_file_specified && no_huffcode_file) {
750         fprintf (stderr, catgets(dtsearch_catd, MS_huff, 43,
751             "645 Input file not specified and '%s' table file\n"
752             "   doesn't exist--nothing to do!\n"),
753             filename_huf);
754         print_usage ();
755         exit (2);
756     }
757
758     /* read the input file and count its bytes */
759     if (input_file_specified) {
760         if ((instream = fopen (filename_input, "rb")) == NULL) {
761     BAD_INPUT_FILE:
762             fprintf (stderr, catgets(dtsearch_catd, MS_huff, 44,
763                 "Could not open input file '%s' or access status: %s\n"),
764                 filename_input, strerror (errno));
765             exit (2);
766         }
767         if (fstat (fileno (instream), &fstat_input) == -1)
768             goto BAD_INPUT_FILE;
769         printf (catgets(dtsearch_catd, MS_huff, 45,
770             "Input file '%s' contains about %ld Kbytes.\n"),
771             filename_input, fstat_input.st_size / 1000L);
772
773         time (&start_stamp);
774         while ((mychar = getc (instream)) != EOF) {
775             hctree1[mychar].count++;
776             total_count++;
777
778             /* echo progress to user every so often */
779             if (!(++bytes_in % 10000L))
780                 printf (catgets(dtsearch_catd, MS_huff, 46,
781                     "\r%ld%% done. %2ld Kbytes read.  "
782                     "Estimate %3ld seconds to completion.   "),
783                     (bytes_in * 100L) / fstat_input.st_size,
784                     bytes_in / 1000L,
785                     (fstat_input.st_size - bytes_in) *
786                     (time (NULL) - start_stamp) / bytes_in);
787         }       /* end read loop for each char in input file */
788
789         putchar ('\n');
790         fclose (instream);
791     }   /* endif that processes input file */
792
793     /* build huffman code tree, write out files */
794     time (&now);        /* this will be the official tree id time
795                          * stamp */
796     printf (catgets(dtsearch_catd, MS_huff, 47,
797         "Identifying timestamp will be '%ld'.\n"
798         "%s Huffman Code Tables in '%s' and '%s'..."),
799         now,
800         (no_huffcode_file) ?
801             catgets(dtsearch_catd, MS_huff, 48, "Creating") :
802             catgets(dtsearch_catd, MS_huff, 49, "Rebuilding"),
803         filename_huf,
804         filename_huc);
805     huffman_code (now);
806     putchar ('\n');
807     return 0;
808 }  /* end of function main */
809
810 /************************** HUFFCODE.C ******************************/