home *** CD-ROM | disk | FTP | other *** search
/ Mega Top 1 / os2_top1.zip / os2_top1 / APPS / TEKST / GRECODE / RECODE.C < prev    next >
C/C++ Source or Header  |  1994-01-10  |  66KB  |  2,389 lines

  1. /* Conversion of files between different charsets and usages.
  2.    Copyright (C) 1990, 1992, 1993 Free Software Foundation, Inc.
  3.    Francois Pinard <pinard@iro.umontreal.ca>, 1990.
  4.  
  5.    This program is free software; you can redistribute it and/or modify
  6.    it under the terms of the GNU General Public License as published by
  7.    the Free Software Foundation; either version 2, or (at your option)
  8.    any later version.
  9.  
  10.    This program is distributed in the hope that it will be useful, but
  11.    WITHOUT ANY WARRANTY; without even the implied warranty of
  12.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  13.    General Public License for more details.
  14.  
  15.    You should have received a copy of the GNU General Public License
  16.    along with this program; if not, write to the Free Software
  17.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  18. */
  19.  
  20. #include "recode.h"
  21.  
  22. /* Maximum number of single step methods.  */
  23. #define MAX_SINGLE_STEPS 300
  24.  
  25. /* Maximum length of a conversion sequence.  */
  26. #define MAX_SEQUENCE 12
  27.  
  28. /* In the `BEFORE:AFTER' parameter, there is a default supplied whenever
  29.    `:AFTER' or `BEFORE:' are used.  */
  30.  
  31. #ifndef DEFAULT_CHARSET
  32. #if defined(MSDOS) || defined(OS2)
  33. #define DEFAULT_CHARSET "ibmpc"
  34. #else
  35. #ifdef atarist
  36. #define DEFAULT_CODE "atarist"
  37. #else
  38. #define DEFAULT_CHARSET "latin1"
  39. #endif
  40. #endif
  41. #endif
  42.  
  43. /* Global declarations and definitions.  */
  44.  
  45. #include <ctype.h>
  46.  
  47. #include <sys/types.h>
  48. #include <sys/stat.h>
  49.  
  50. #ifdef HAVE_STRING_H
  51. #include <string.h>
  52. #else
  53. #include <strings.h>
  54. #define strchr index
  55. #define strrchr rindex
  56. #endif
  57.  
  58. #ifdef OS2
  59. #include <io.h>
  60. #include <fcntl.h>
  61. #endif
  62.  
  63. #ifdef MSDOS
  64. #include <dir.h>
  65. #define unlink dummy1
  66. #include <io.h>
  67. #undef unlink
  68. #include <fcntl.h>
  69. #endif
  70.  
  71. #include <errno.h>
  72. #ifndef errno
  73. extern int errno;
  74. #endif
  75.  
  76. #include "getopt.h"
  77.  
  78. /* tmpnam/tmpname/mktemp/tmpfile and the associate logic has been the
  79.    main portability headache of GNU recode :-(.
  80.    
  81.    People reported that tmpname does not exist everywhere.  On OS/2,
  82.    recode aborts if the prefix has more than five characters.
  83.    
  84.    tmpnam seems to exist everywhere so far.  But NeXT's tmpnam() is such
  85.    that, if called many times in succession, it will always return the
  86.    same value.  One has to really open a file with the returned name
  87.    first, for the next call to tmpnam() to return a different value.  I
  88.    can manage it for a single invocation of recode, but using two recode
  89.    invocations connected with a shell pipe, on the NeXT, creates a race
  90.    by which both copies may call tmpnam() in parallel, then getting the
  91.    same value, and will consequently open the same temporary file.
  92.    
  93.    Noah Friedman <friedman@gnu.ai.mit.edu> suggests opening the file with
  94.    O_EXCL, and when the open presumably fails, call tmpnam again, or try
  95.    the mktemp routine in the GNU C library...maybe that will work better.
  96.    
  97.    Michael I Bushnell <mib@gnu.ai.mit.edu> suggests always using tmpfile,
  98.    which opens the file too, using the O_EXCL option to open.
  99.    
  100.    I'm trying this last suggestion, rewinding instead of closing.
  101.    Someone reported, a long while ago, that rewind did not work on his
  102.    system, so I reverted to opening and closing the temporary files all
  103.    the time.  I lost the precise references for this problem.  In any
  104.    case, I'm reusing rewind with tmpfile, now.  Hopefully, someone will
  105.    tell me if this creates a problem somewhere!  */
  106.  
  107. /* The previous round used tmpnam(3).  This one tries tmpfile(3).  */
  108. /* #define USE_TMPNAM 1 */
  109. #define USE_TMPFILE 1
  110.  
  111. #ifdef USE_TMPNAM
  112. /* Guarantee some value for L_tmpnam.  */
  113. #ifdef MSDOS
  114.  
  115. #define L_tmpnam 13
  116.  
  117. #else /* not MSDOS */
  118.  
  119. char *tmpnam ();
  120.  
  121. #ifndef L_tmpnam
  122. #include "pathmax.h"
  123. #define L_tmpnam PATH_MAX
  124. #endif
  125.  
  126. #endif /* not MSDOS */
  127. #endif /* USE_TMPNAM */
  128.  
  129. #ifdef USE_TMPFILE
  130.  
  131. FILE *tmpfile _((void));
  132.  
  133. #endif /* USE_TMPFILE */
  134.  
  135. /* Variables.  */
  136.  
  137. /* Program identification.  */
  138. const char *const version_string = "GNU recode version 3.3";
  139.  
  140. const char *const copyright_string = "\
  141. This program is free software; you can redistribute it and/or modify\n\
  142. it under the terms of the GNU General Public License as published by\n\
  143. the Free Software Foundation; either version 2, or (at your option)\n\
  144. any later version.\n\
  145. \n\
  146. This program is distributed in the hope that it will be useful,\n\
  147. but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
  148. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
  149. GNU General Public License for more details.\n\
  150. \n\
  151. You should have received a copy of the GNU General Public License\n\
  152. along with this program; if not, write to the Free Software\n\
  153. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.\n";
  154.  
  155. /* The name this program was run with. */
  156. const char *program_name;
  157.  
  158. /* If non-zero, display usage information and exit.  */
  159. static int show_help = 0;
  160.  
  161. /* If non-zero, print the version on standard output and exit.  */
  162. static int show_version = 0;
  163.  
  164. /* If non-zero, show a list of one or all known charsets, then exit.  */
  165. static int show_charsets = 0;
  166.  
  167. /* Indicates the format for showing only one charset.  */
  168. enum list_format list_format = NO_FORMAT;
  169.  
  170. /* If non-zero, merely explore all recoding paths, report and exit.  */
  171. static int auto_check_mode = 0;
  172.  
  173. /* If non-zero, produce C code for initializing the conversion and exit.  */
  174. static int make_header_mode = 0;
  175.  
  176. /* Table name in generated C code.  */
  177. static const char *header_name = NULL;
  178.  
  179. /* If the recoding yields some problems in reversability, the replacement is
  180.    normally not completed and the file is left unrecoded.  The following
  181.    option forces the replacement even if the case the recoding is not
  182.    reversible.  But if recode is used as a mere filter, there is no file
  183.    replacement and this option is then irrelevant.  */
  184. int force_option = 0;
  185.  
  186. /* This option prevents recode from automatically completing charsets.  */
  187. int strict_mapping = 0;
  188.  
  189. /* By selecting the following option, the program will echo to stderr the
  190.    sequence of elementary recoding steps which will be taken to effect
  191.    the requested recoding.  */
  192. int verbose_option = 0;
  193.  
  194. /* When a file is recoded over itself, precautions are taken to move the
  195.    timestamps of the original file into the recoded file, so to make the
  196.    recoding the most transparent possible to make, and other tools.
  197.    However, selecting the following option inhibit the timestamps handling,
  198.    thus effectively `touching' the file.  */
  199. int touch_option = 0;
  200.  
  201. /* In `texte' charset, some countries use double quotes to mark diaeresis,
  202.    while other countries prefer colons.  The following variable contains the
  203.    diaeresis character for `texte' charset.  Nominally set to a double
  204.    quote, it can be forced to a colon by an option on recode command.  */
  205. char diaeresis_char = '"';
  206.  
  207. /* For `latex' charset, it is often convenient to recode the diacritics
  208.    only, while letting other LaTeX code using backslashes unrecoded.
  209.    In the other charset, one can edit text as well as LaTeX directives.  */
  210. int diacritics_only = 0;
  211.  
  212. /* For `ibmpc' charset, characters 176 to 223 are use to draw boxes.
  213.    If this variable is set, while getting out of `ibmpc', ASCII
  214.    characters are selected so to approximate these boxes.  */
  215. int ascii_graphics = 0;
  216.  
  217. /* The following charset name will be ignored, if given.  */
  218. static const char *ignored_name = NULL;
  219.  
  220. /* Unabridged names of BEFORE and AFTER charsets, even if still aliases.
  221.    These are used for naming the array in produced C code.  */
  222. static const char *before_full_name;
  223. static const char *after_full_name;
  224.  
  225. /* Ordinals of list, BEFORE and AFTER charset.  */
  226. static CHARSET *list_charset;
  227. static CHARSET *before_charset;
  228. static CHARSET *after_charset;
  229.  
  230. /* Flag telling usage that we are decoding charsets.  */
  231. int decoding_charset_flag = 0;
  232.  
  233. /* Tells how various passes will be interconnected.  */
  234. enum sequence_strategy
  235.   {
  236.     STRATEGY_UNDECIDED,        /* sequencing strategy is undecided yet */
  237.     SEQUENCE_WITH_FILES,    /* do not fork, use intermediate files */
  238.     SEQUENCE_WITH_POPEN,    /* use `popen(3)' to fork processes */
  239.     SEQUENCE_WITH_PIPE        /* fork processes connected with `pipe(2)' */
  240.   };
  241. enum sequence_strategy sequence_strategy = STRATEGY_UNDECIDED;
  242.  
  243. /* Known single steps.  */
  244.  
  245. STEP single_step_array[MAX_SINGLE_STEPS];
  246. int number_of_single_steps;    /* number of announced single steps */
  247.  
  248. const unsigned char *one_to_same; /* identity recoding */
  249.  
  250. CHARSET *rfc1345;        /* special RFC 1345 charset value */
  251.  
  252. /* Array stating the sequence of conversions.  */
  253. const STEP *sequence[MAX_SEQUENCE];
  254. int length_of_sequence;
  255.  
  256. /* Quality handling.  */
  257.  
  258. /*---------------------------------------.
  259. | Return a string describing a quality.     |
  260. `---------------------------------------*/
  261.  
  262. const char *
  263. quality_to_string (QUALITY quality)
  264. {
  265.   switch (quality)
  266.     {
  267.     default:
  268.       abort ();
  269.  
  270.     case REVERSIBLE:
  271.       return "reversible";
  272.  
  273.     case ONE_TO_ONE:
  274.       return "one to one";
  275.  
  276.     case MANY_TO_ONE:
  277.       return "many to one";
  278.  
  279.     case ONE_TO_MANY:
  280.       return "one to many";
  281.  
  282.     case MANY_TO_MANY:
  283.       return "many to many";
  284.     }
  285. }
  286.  
  287. /*-------------------------------------------------------------------------.
  288. | Return the quality of a step obtained by merging two others steps, given |
  289. | their respective qualities FIRST and SECOND.                   |
  290. `-------------------------------------------------------------------------*/
  291.  
  292. QUALITY 
  293. merge_qualities (QUALITY first, QUALITY second)
  294. {
  295.   switch (first)
  296.     {
  297.     default:
  298.       abort ();
  299.       
  300.     case REVERSIBLE:
  301.       return second;
  302.  
  303.     case ONE_TO_ONE:
  304.       switch (second)
  305.     {
  306.     case REVERSIBLE:
  307.     case ONE_TO_ONE:
  308.       return ONE_TO_ONE;
  309.  
  310.     case MANY_TO_ONE:
  311.     case ONE_TO_MANY:
  312.     case MANY_TO_MANY:
  313.       return second;
  314.     }
  315.  
  316.     case MANY_TO_ONE:
  317.       switch (second)
  318.     {
  319.     case REVERSIBLE:
  320.     case ONE_TO_ONE:
  321.     case MANY_TO_ONE:
  322.       return MANY_TO_ONE;
  323.  
  324.     case ONE_TO_MANY:
  325.     case MANY_TO_MANY:
  326.       return MANY_TO_MANY;
  327.     }
  328.  
  329.     case ONE_TO_MANY:
  330.       switch (second)
  331.     {
  332.     case REVERSIBLE:
  333.     case ONE_TO_ONE:
  334.     case ONE_TO_MANY:
  335.       return ONE_TO_MANY;
  336.  
  337.     case MANY_TO_ONE:
  338.     case MANY_TO_MANY:
  339.       return MANY_TO_MANY;
  340.     }
  341.  
  342.     case MANY_TO_MANY:
  343.       return MANY_TO_MANY;
  344.     }
  345. }
  346.  
  347. /* Charset handling.  */
  348.  
  349. /*----------------------------------------------------.
  350. | Decode the BEFORE:AFTER argument, given in STRING.  |
  351. `----------------------------------------------------*/
  352.  
  353. void
  354. decode_before_after (const char *string)
  355. {
  356.   char *before;
  357.   char *after;
  358.   char *in;
  359.   char *out;
  360.  
  361.   /* Split the BEFORE:AFTER keyword at the colon.  A backslash can escape
  362.      a colon in both charsets.  */
  363.  
  364.   before = xstrdup (string);
  365.   after = NULL;
  366.   out = before;
  367.  
  368.   for (in = before; *in; in++)
  369.     if (*in == ':' && !after)
  370.       {
  371.     *out++ = '\0';
  372.     after = out;
  373.       }
  374.     else
  375.       {
  376.     if (*in == '\\' && *(in + 1))
  377.       in++;
  378.     *out++ = *in;
  379.       }
  380.   *out = '\0';
  381.  
  382.   if (!after)
  383.     usage (EXIT_FAILURE);
  384.  
  385.   /* Decode both charsets.  */
  386.  
  387.   before_full_name = clean_charset_name (before);
  388.   before_charset = find_charset (before_full_name);
  389.  
  390.   after_full_name = clean_charset_name (after);
  391.   after_charset = find_charset (after_full_name);
  392.  
  393.   /* Free the work area.  */
  394.  
  395.   free (before);
  396. }
  397.  
  398. /* Single step handling.  */
  399.  
  400. /*-----------------------------------------------------------------------.
  401. | Allocate and initialize a new single step, save for the before and after |
  402. | charsets and quality.                             |
  403. `-----------------------------------------------------------------------*/
  404.  
  405. static STEP *
  406. new_single_step (void)
  407. {
  408.   STEP *step;
  409.  
  410.   if (number_of_single_steps == MAX_SINGLE_STEPS)
  411.     error (EXIT_FAILURE, 0, "MAX_SINGLE_STEPS is too small");
  412.  
  413.   step = single_step_array + number_of_single_steps++;
  414.   step->init_recode = NULL;
  415.   step->file_recode = NULL;
  416.   step->one_to_one = NULL;
  417.   step->one_to_many = NULL;
  418.  
  419.   return step;
  420. }
  421.   
  422. /*------------------------------------------------------------------------.
  423. | Create and initialize a new single step for recoding between START_NAME |
  424. | and GOAL_NAME, which are given as strings, give it a recoding QUALITY,  |
  425. | also saving an INIT_RECODE and a FILE_RECODE functions.          |
  426. `------------------------------------------------------------------------*/
  427.  
  428. void
  429. declare_step (const char *before_name, const char *after_name, QUALITY quality,
  430.           void (*init_recode) (STEP *),
  431.           void (*file_recode) (const STEP *, FILE *, FILE *))
  432. {
  433.   STEP *step;
  434.  
  435.   step = new_single_step ();
  436.   step->before = find_charset (before_name);
  437.   step->after = find_charset (after_name);
  438.   step->quality = quality;
  439.   step->init_recode = init_recode;
  440.   step->file_recode = file_recode;
  441. }
  442.  
  443. /*------------------------------------------------------------------.
  444. | Create a one to one table which is the inverse of the given one.  |
  445. `------------------------------------------------------------------*/
  446.  
  447. unsigned char *
  448. invert_table (const unsigned char *table)
  449. {
  450.   unsigned char flag[256];
  451.   unsigned char *result;
  452.   int table_error;
  453.   int counter;
  454.  
  455.   result = (unsigned char *) xmalloc (256);
  456.   memset (flag, 0, 256);
  457.   table_error = 0;
  458.  
  459.   for (counter = 0; counter < 256; counter++)
  460.     {
  461.       if (flag[table[counter]])
  462.     {
  463.       error (0, 0, "Codes %3d and %3d both recode to %3d",
  464.          result[table[counter]], counter, table[counter]);
  465.       table_error = 1;
  466.     }
  467.       else
  468.     {
  469.       result[table[counter]] = counter;
  470.       flag[table[counter]] = 1;
  471.     }
  472.     }
  473.   if (table_error)
  474.     {
  475.       for (counter = 0; counter < 256; counter++)
  476.     if (!flag[counter])
  477.       error (0, 0, "No character recodes to %3d", counter);
  478.       error (EXIT_FAILURE, 0, "Cannot invert given one-to-one table");
  479.     }
  480.   return result;
  481. }
  482.  
  483. /*-------------------------------------------------------------------------.
  484. | Complete a STEP descriptor by a constructed recoding array for 256 chars |
  485. | and the adequate recoding routine.  If FIRST_HALF_IMPLIED is not zero,   |
  486. | default the unconstrained characters of the first 128 to the identity       |
  487. | mapping.  Use an KNOWN_PAIRS array of NUMBER_OF_PAIRS constraints.  If   |
  488. | REVERSE is not zero, use right_table instead of left_table.           |
  489. `-------------------------------------------------------------------------*/
  490.  
  491. void
  492. complete_pairs (STEP *step, int first_half_implied,
  493.         const KNOWN_PAIR *known_pairs, int number_of_pairs,
  494.         int reverse)
  495. {
  496.   unsigned char left_flag[256];
  497.   unsigned char right_flag[256];
  498.   unsigned char left_table[256];
  499.   unsigned char right_table[256];
  500.   int table_error;
  501.  
  502.   unsigned char *flag;
  503.   unsigned char *table;
  504.   const char **table2;
  505.   char *cursor;
  506.   unsigned char left;
  507.   unsigned char right;
  508.   unsigned char search;
  509.   int counter;
  510.   int used;
  511.  
  512.   /* Init tables with zeroes.  */
  513.  
  514.   memset (left_flag, 0, 256);
  515.   memset (right_flag, 0, 256);
  516.   table_error = 0;
  517.  
  518.   /* Establish known data.  */
  519.  
  520.   for (counter = 0; counter < number_of_pairs; counter++)
  521.     {
  522.       left = known_pairs[counter].left;
  523.       right = known_pairs[counter].right;
  524.  
  525.       /* Set one known correspondance.  */
  526.  
  527.       if (left_flag[left])
  528.     {
  529.       if (!table_error)
  530.         {
  531.           error (0, 0, "Following diagnostics for `%s' to `%s'",
  532.              step->before->name, step->after->name);
  533.           table_error = 1;
  534.         }
  535.       error (0, 0, "Pair no. %d: { %3d, %3d } conflicts with { %3d, %3d }",
  536.          counter, left, right, left, left_table[left]);
  537.     }
  538.       else if (right_flag[right])
  539.     {
  540.       if (!table_error)
  541.         {
  542.           error (0, 0, "Following diagnostics for `%s' to `%s'",
  543.              step->before->name, step->after->name);
  544.           table_error = 1;
  545.         }
  546.       error (0, 0, "Pair no. %d: { %3d, %3d } conflicts with { %3d, %3d }",
  547.          counter, left, right, right_table[right], right);
  548.     }
  549.       else
  550.     {
  551.       left_flag[left] = 1;
  552.       left_table[left] = right;
  553.       right_flag[right] = 1;
  554.       right_table[right] = left;
  555.     }
  556.     }
  557.  
  558.   /* Set all the implied correspondances.  */
  559.  
  560.   if (first_half_implied)
  561.     for (counter = 0; counter < 128; counter++)
  562.       if (!left_flag[counter] && !right_flag[counter])
  563.     {
  564.       left_flag[counter] = 1;
  565.       left_table[counter] = counter;
  566.       right_flag[counter] = 1;
  567.       right_table[counter] = counter;
  568.     }
  569.  
  570.   if (strict_mapping)
  571.     {
  572.  
  573.       /* If the recoding is strict, prepare a one to many table, each
  574.      entry being NULL or a string of a single character.  */
  575.  
  576.       /* Select the proper table.  */
  577.  
  578.       if (reverse)
  579.     {
  580.       flag = right_flag;
  581.       table = right_table;
  582.     }
  583.       else
  584.     {
  585.       flag = left_flag;
  586.       table = left_table;
  587.     }
  588.  
  589.       /* Allocate everything in one blow, so it will be freed likewise.  */
  590.  
  591.       used = 0;
  592.       for (counter = 0; counter < 256; counter++)
  593.     if (flag[counter])
  594.       used++;
  595.  
  596.       table2 = (const char **) xmalloc (256 * sizeof (char *) + 2 * used);
  597.       cursor = (char *) (table2 + 256);
  598.  
  599.       /* Construct the table and the strings in parallel.  */
  600.  
  601.       for (counter = 0; counter < 256; counter++)
  602.     if (flag[counter])
  603.       {
  604.         table2[counter] = cursor;
  605.         *cursor++ = table[counter];
  606.         *cursor++ = '\0';
  607.       }
  608.     else
  609.       table2[counter] = NULL;
  610.  
  611.       /* Save a one to many recoding table.  */
  612.  
  613.       step->file_recode = file_one_to_many;
  614.       step->one_to_many = table2;
  615.     }
  616.   else
  617.     {
  618.  
  619.       /* If the recoding is not strict, compute a reversible one to one
  620.      table.  */
  621.  
  622.       if (table_error)
  623.     error (EXIT_FAILURE, 0,
  624.            "Cannot complete table from set of known pairs");
  625.  
  626.       /* Close the table with small permutation cycles.  */
  627.  
  628.       for (counter = 0; counter < 256; counter++)
  629.     if (!right_flag[counter])
  630.       {
  631.         search = counter;
  632.         while (left_flag[search])
  633.           search = left_table[search];
  634.         left_flag[search] = 1;
  635.         left_table[search] = counter;
  636.         right_flag[counter] = 1;
  637.         right_table[counter] = search;
  638.       }
  639.  
  640.       /* Save a copy of the proper table.  */
  641.  
  642.       step->file_recode = file_one_to_one;
  643.       table = (unsigned char *) xmalloc (256);
  644.       memcpy (table, reverse ? right_table : left_table, 256);
  645.       step->one_to_one = table;
  646.     }
  647. }
  648.  
  649. /*----------------------------------------.
  650. | Initialize all collected single steps.  |
  651. `----------------------------------------*/
  652.  
  653. void
  654. register_all_modules (void)
  655. {
  656.   STEP *step;
  657.   int counter;
  658.   unsigned char *table;
  659.  
  660.   table = (unsigned char *) xmalloc (256);
  661.   for (counter = 0; counter < 256; counter++)
  662.     table[counter] = counter;
  663.   one_to_same = table;
  664.  
  665.   prepare_charset_initialization ();
  666.   number_of_single_steps = 0;
  667.  
  668.   rfc1345 = find_charset ("RFC 1345");
  669.   declare_alias (".", "RFC 1345");
  670.  
  671.   declare_alias ("latin1", "ISO_8859-1:1987");
  672.   declare_alias ("lat1", "latin1");
  673.  
  674. #include "initstep.h"
  675.  
  676.   for (step = single_step_array;
  677.        step < single_step_array + number_of_single_steps;
  678.        step++)
  679.  
  680.     if (step->file_recode == file_one_to_one
  681.     && step->one_to_one == one_to_same)
  682.  
  683.       step->conversion_cost = 0;
  684.  
  685.     else
  686.       switch (step->quality)
  687.     {
  688.     case REVERSIBLE:
  689.       step->conversion_cost = 2;
  690.       break;
  691.  
  692.     case ONE_TO_ONE:
  693.       step->conversion_cost = 30;
  694.       break;
  695.  
  696.     case MANY_TO_ONE:
  697.       step->conversion_cost = 10;
  698.       break;
  699.  
  700.     case ONE_TO_MANY:
  701.       step->conversion_cost = 40;
  702.       break;
  703.  
  704.     case MANY_TO_MANY:
  705.       step->conversion_cost = 50;
  706.       break;
  707.     }
  708.  
  709.   /* For all RFC 1345 participating steps, halve the cost since they
  710.      come in pair.  */
  711.  
  712.   for (counter = 0; counter < number_of_single_steps; counter++)
  713.     if (single_step_array[counter].before == rfc1345
  714.     || single_step_array[counter].after == rfc1345)
  715.       single_step_array[counter].conversion_cost /= 2;
  716. }
  717.  
  718. /*-------------------------------------------------------------------------.
  719. | Produce a C include file representing the recoding, on standard output.  |
  720. `-------------------------------------------------------------------------*/
  721.  
  722. static void
  723. output_header_file (void)
  724. {
  725.   const STEP *step;        /* step being analysed */
  726.   int column;            /* column counter */
  727.   char *name;            /* constructed name */
  728.   char *cursor;            /* cursor in constructed name */
  729.   const char *cursor2;        /* cursor to study strings */
  730.   int counter;            /* general purpose counter */
  731.  
  732.   /* This function is called only when the recoding sequence contains a
  733.      single step, so it is safe to use sequence[0] for the step.  */
  734.  
  735.   step = sequence[0];
  736.  
  737.   /* Print the header of the header file.  */
  738.  
  739.   printf ("/* Conversion table from `%s' charset to `%s' charset.\n",
  740.       before_full_name, after_full_name);
  741.   printf ("   Generated mechanically by %s.\n", version_string);
  742.   printf ("\n");
  743.   switch (sequence[0]->quality)
  744.     {
  745.     case REVERSIBLE:
  746.       printf ("   The recoding should be reversible.\n");
  747.       break;
  748.  
  749.     case ONE_TO_ONE:
  750.       printf ("   The recoding might not be reversible.\n");
  751.       break;
  752.  
  753.     case MANY_TO_ONE:
  754.       printf ("   Programming is needed to handle multichar input.\n");
  755.       break;
  756.  
  757.     case ONE_TO_MANY:
  758.       printf ("   Each input char transforms into an output string.\n");
  759.       break;
  760.  
  761.     case MANY_TO_MANY:
  762.       printf ("   Each input char transforms into an output string,\n");
  763.       printf ("   programming is needed to handle multichar input.\n");
  764.       break;
  765.     }
  766.   printf ("*/\n");
  767.   printf ("\n");
  768.  
  769.   /* Construct the name of the resulting table.  */
  770.  
  771.   if (header_name)
  772.     name = xstrdup (header_name);
  773.   else
  774.     {
  775.       name = (char *) xmalloc (strlen (before_full_name) + sizeof "_to_"
  776.                    + strlen (after_full_name));
  777.       strcpy (name, before_full_name);
  778.       strcat (name, "_to_");
  779.       strcat (name, after_full_name);
  780.     }
  781.  
  782.   /* Ensure the table name contains only valid characters for a C
  783.      identifier.  */
  784.  
  785.   for (cursor = name; *cursor; cursor++)
  786.     if (*cursor != '_'
  787.     && (*cursor < 'a' || *cursor > 'z')
  788.     && (*cursor < 'A' || *cursor > 'Z')
  789.     && (*cursor < '0' || *cursor > '9'))
  790.       *cursor = '_';
  791.  
  792.   /* Produce the recoding table in the correct format.  */
  793.  
  794.   if (step->one_to_one)
  795.     {
  796.  
  797.       /* Produce a one to one recoding table.  */
  798.  
  799.       printf ("unsigned char const %s[256] =\n", name);
  800.       printf ("  {\n");
  801.       for (counter = 0; counter < 256; counter++)
  802.     {
  803.       printf ("%s%3d,", counter % 8 == 0 ? "    " : " ",
  804.            step->one_to_one[counter]);
  805.       if (counter % 8 == 7)
  806.         printf ("     /* %3d - %3d */\n", counter - 7, counter);
  807.     }
  808.       printf ("  };\n");
  809.     }
  810.  
  811.   else if (step->one_to_many)
  812.     {
  813.  
  814.       /* Produce a one to many recoding table.  */
  815.  
  816.       printf ("const char *%s[256] =\n", name);
  817.       printf ("  {\n");
  818.       for (counter = 0; counter < 256; counter++)
  819.     {
  820.       printf ("    ");
  821.       column = 4;
  822.       if (step->one_to_many[counter])
  823.         {
  824.           printf ("\"");
  825.           column++;
  826.           for (cursor2 = step->one_to_many[counter]; *cursor2; cursor2++)
  827.         switch (*cursor2)
  828.           {
  829.           case ' ':
  830.             printf (" ");
  831.             column++;
  832.             break;
  833.  
  834.           case '\b':
  835.             printf ("\\b");
  836.             column += 2;
  837.             break;
  838.  
  839.           case '\t':
  840.             printf ("\\t");
  841.             column += 2;
  842.             break;
  843.  
  844.           case '\n':
  845.             printf ("\\n");
  846.             column += 2;
  847.             break;
  848.  
  849.           case '"':
  850.             printf ("\\\"");
  851.             column += 2;
  852.             break;
  853.  
  854.           case '\\':
  855.             printf ("\\\\");
  856.             column += 2;
  857.             break;
  858.  
  859.           default:
  860.             if (isprint (*cursor2))
  861.               {
  862.             printf ("%c", *cursor2);
  863.             column++;
  864.               }
  865.             else
  866.               {
  867.             printf ("\\%0.3o", *(const unsigned char *) cursor2);
  868.             column += 4;
  869.               }
  870.           }
  871.           printf ("\"");
  872.           column++;
  873.         }
  874.       else
  875.         {
  876.           printf ("0");
  877.           column++;
  878.         }
  879.       printf (", ");
  880.       column += 2;
  881.       while (column < 32)
  882.         {
  883.           printf (" ");
  884.           column++;
  885.         }
  886.       printf ("/* %3d */\n", counter);
  887.     }
  888.       printf ("  };\n");
  889.     }
  890.  
  891.   else
  892.     error (EXIT_FAILURE, 0, "No table to print");
  893.  
  894.   free (name);
  895. }
  896.  
  897. /* Double step handling (for RFC 1345).  */
  898.  
  899. /*-----------------------------------------------------------------------.
  900. | Associate a double TABLE with charset NAME, part of the RFC 1345 fully |
  901. | connected set.  Each entry in table uses SIZE characters.         |
  902. `-----------------------------------------------------------------------*/
  903.  
  904. void
  905. declare_double_step (DOUBLE_TABLE *table, const char *name, int size)
  906. {
  907.   CHARSET *charset;
  908.   STEP *step;
  909.  
  910.   charset = find_charset (name);
  911.   charset->table = table;
  912.   charset->size = size;
  913.  
  914.   step = new_single_step ();
  915.   step->before = charset;
  916.   step->after = rfc1345;
  917.   step->quality = strict_mapping ? ONE_TO_MANY : REVERSIBLE;
  918.   step->init_recode = NULL;
  919.   step->file_recode = NULL;
  920.  
  921.   step = new_single_step ();
  922.   step->before = rfc1345;
  923.   step->after = charset;
  924.   step->quality = strict_mapping ? ONE_TO_MANY : REVERSIBLE;
  925.   step->init_recode = NULL;
  926.   step->file_recode = NULL;
  927. }
  928.  
  929. void
  930. init_recode_rfc1345 (STEP *step)
  931. {
  932.   char pool[2 * 256 * 6];    /* character pool */
  933.   struct sort
  934.     {
  935.       const char *key;        /* ISO 10646 name, blank padded to size */
  936.       int code;            /* corresponding charset code (0..255) */
  937.     };
  938.   struct side
  939.     {
  940.       CHARSET *charset;        /* charset */
  941.       struct sort binding[256];    /* bindings */
  942.       int bindings;        /* number of bindings */
  943.     };
  944.  
  945.   DOUBLE_TABLE *table;        /* RFC 1345 table */
  946.   struct side side_array[2];    /* information for each side */
  947.   struct side *side;        /* cursor into side_array */
  948.   int reversed;            /* if both sides reversed */
  949.   KNOWN_PAIR pair[256];        /* obtained pairings */
  950.   int pairs;            /* number of pairings */
  951.   const char *in;        /* cursor in double table strings */
  952.   char *out;            /* cursor in character pool */
  953.   int code;            /* character code */
  954.   int row_counter;        /* double table row counter */
  955.   int position_counter;        /* double table column counter */
  956.   int counter;            /* counter for characters */
  957.   int left;            /* left bindings counter */
  958.   int right;            /* right bindings counter */
  959.  
  960.   /* For ensuring reversibility, known pairs should be computed the same
  961.      way regardless of the direction of recoding.  This canonalization is
  962.      ensured through the charset values, which are increasing along the
  963.      initialization order.  This should also reflect the charset order in
  964.      rfc1345.txt.  */
  965.  
  966.   if (step->before < step->after)
  967.     {
  968.       side_array[0].charset = step->before;
  969.       side_array[1].charset = step->after;
  970.       reversed = 0;
  971.     }
  972.   else
  973.     {
  974.       side_array[0].charset = step->after;
  975.       side_array[1].charset = step->before;
  976.       reversed = 1;
  977.     }
  978.  
  979.   out = pool;
  980.   for (side = side_array; side < side_array + 2; side++)
  981.     {
  982.  
  983.       /* Move out the values for sorting out of the double table.  */
  984.  
  985.       side->bindings = 0;
  986.       code = 0;
  987.       table = side->charset->table;
  988.  
  989.       for (row_counter = 0; row_counter < 8; row_counter++)
  990.     if (in = (*table)[row_counter], in)
  991.       for (position_counter = 0; position_counter < 32; position_counter++)
  992.         {
  993.           if (*in == ' ')
  994.         in += side->charset->size;
  995.           else
  996.         {
  997.  
  998.           /* Establish a new binding.  */
  999.  
  1000.           side->binding[side->bindings].code = code;
  1001.           side->binding[side->bindings].key = out;
  1002.           side->bindings++;
  1003.  
  1004.           /* Copy out the value to the character pool, and terminate it
  1005.              with a NULL.  */
  1006.  
  1007.           for (counter = 0; counter < side->charset->size; counter++)
  1008.             if (*in == ' ')
  1009.               in++;
  1010.             else
  1011.               *out++ = *in++;
  1012.           *out++ = '\0';
  1013.         }
  1014.           code++;
  1015.         }
  1016.     else
  1017.       code += 32;
  1018.     }
  1019.  
  1020.   /* This crude, quadratic pairing code will do for now.  */
  1021.  
  1022.   pairs = 0;
  1023.   for (left = 0; left < side_array[0].bindings; left++)
  1024.     {
  1025.       for (right = 0; right < side_array[1].bindings; right++)
  1026.     if (strcmp (side_array[0].binding[left].key,
  1027.             side_array[1].binding[right].key)
  1028.         == 0)
  1029.       {
  1030.         pair[pairs].left = side_array[0].binding[left].code;
  1031.         pair[pairs].right = side_array[1].binding[right].code;
  1032.         pairs++;
  1033.         break;
  1034.       }
  1035.     }
  1036.  
  1037.   /* Complete the recoding table out of this.  */
  1038.  
  1039.   complete_pairs (step, 0, pair, pairs, reversed);
  1040. }
  1041.  
  1042. /* Step sequence handling.  */
  1043.  
  1044. #define UNREACHABLE    30000        /* No way for this conversion */
  1045.  
  1046. /*-------------------------------------------------------.
  1047. | Explain what recoding step sequence has been planned.     |
  1048. `-------------------------------------------------------*/
  1049.  
  1050. static void
  1051. echo_sequence (void)
  1052. {
  1053.   const char *last;        /* last name printed */
  1054.   const char *name;        /* name being printed */
  1055.   QUALITY quality;        /* cumulative quality */
  1056.   int counter;            /* index into sequence */
  1057.  
  1058.   if (length_of_sequence < 0)
  1059.     error (0, 0, "UNACHIEVABLE recoding!");
  1060.   else if (length_of_sequence == 0)
  1061.     error (0, 0, "Mere copy, no recoding");
  1062.   else
  1063.     {
  1064.       quality = REVERSIBLE;
  1065.       last = NULL;
  1066.       for (counter = 0; counter < length_of_sequence; counter++)
  1067.     {
  1068.       name = sequence[counter]->before->name;
  1069.       if (counter == 0)
  1070.         fprintf (stderr, "%s", name);
  1071.       else if (name != last)
  1072.         fprintf (stderr, "/%s", name);
  1073.  
  1074.       name = sequence[counter]->after->name;
  1075.       fprintf (stderr, " -> %s", name);
  1076.  
  1077.       quality = merge_qualities (quality, sequence[counter]->quality);
  1078.       last = name;
  1079.     }
  1080.       fprintf (stderr, " (%s)\n", quality_to_string (quality));
  1081.     }
  1082. }
  1083.  
  1084. /*----------------------------------------------------------.
  1085. | Find a sequence of single steps to achieve a conversion.  |
  1086. `----------------------------------------------------------*/
  1087.  
  1088. static void
  1089. find_sequence (CHARSET *before, CHARSET *after)
  1090. {
  1091.   struct search
  1092.     {
  1093.       STEP *step;        /* step who will bring us nearer to after */
  1094.       int cost;            /* cost from here through after */
  1095.     };
  1096.   struct search *search_array;    /* critical path search tree */
  1097.   struct search *search;    /* item in search_array for charset */
  1098.   STEP *step;            /* cursor in possible single_steps */
  1099.   int cost;            /* cost under consideration */
  1100.   int modified;            /* != 0 if modified since last iteration */
  1101.   CHARSET *charset;        /* charset while reconstructing sequence */
  1102.  
  1103.   search_array
  1104.     = (struct search *) xmalloc (number_of_charsets * sizeof (struct search));
  1105.  
  1106.   /* Initialize the search for an economical route, looking our way
  1107.      backward from the after towards the before.  */
  1108.  
  1109.   for (search = search_array;
  1110.        search < search_array + number_of_charsets;
  1111.        search++)
  1112.     {
  1113.       search->step = NULL;
  1114.       search->cost = UNREACHABLE;
  1115.     }
  1116.   search_array[after - charset_array].cost = 0;
  1117.  
  1118.   modified = 1;
  1119.   while (modified)
  1120.     {
  1121.       modified = 0;
  1122.       for (step = single_step_array;
  1123.        step < single_step_array + number_of_single_steps;
  1124.        step++)
  1125.     if (!step->before->ignore)
  1126.       {
  1127.         cost = search_array[step->after - charset_array].cost;
  1128.         if (cost != UNREACHABLE)
  1129.           {
  1130.         cost += step->conversion_cost;
  1131.         search = search_array + (step->before - charset_array);
  1132.         if (cost < search->cost)
  1133.           {
  1134.             search->step = step;
  1135.             search->cost = cost;
  1136.             modified = 1;
  1137.           }
  1138.           }
  1139.       }
  1140.     }
  1141.  
  1142.   if (search_array[before - charset_array].cost == UNREACHABLE)
  1143.     {
  1144.       
  1145.       /* If no path has been found, return with a negative length.  */
  1146.  
  1147.       length_of_sequence = -1;
  1148.     }
  1149.   else
  1150.     {
  1151.  
  1152.       /* Save the retained best path in the sequence array.  While doing so,
  1153.      optimize out any single step which merely copies.  Also execute the
  1154.      delayed initialization for those steps which registered one.  */
  1155.  
  1156.       length_of_sequence = 0;
  1157.       for (charset = before; charset != after; charset = step->after)
  1158.     {
  1159.       step = search_array[charset - charset_array].step;
  1160.       if (step->file_recode != file_one_to_one
  1161.           || step->one_to_one != one_to_same)
  1162.         {
  1163.           if (length_of_sequence == MAX_SEQUENCE)
  1164.         error (EXIT_FAILURE, 0, "MAX_SEQUENCE is too small");
  1165.           sequence[length_of_sequence++] = step;
  1166.           if (step->init_recode)
  1167.         {
  1168.           (*step->init_recode) (step);
  1169.           step->init_recode = NULL;
  1170.         }
  1171.         }
  1172.     }
  1173.     }
  1174.  
  1175.   /* Tell what has been decided, for the user.  */
  1176.  
  1177.   if (verbose_option)
  1178.     echo_sequence ();
  1179.  
  1180.   free (search_array);
  1181. }
  1182.  
  1183. /*---------------------------------------------------------------------.
  1184. | Optimize a sequence of single steps by creating new single steps, if |
  1185. | this can be done by merging adjacent steps which are simple enough.  |
  1186. `---------------------------------------------------------------------*/
  1187.  
  1188. static void
  1189. optimize_sequence (void)
  1190. {
  1191.   int saved_steps;        /* number of saved steps */
  1192.   unsigned char *accum;        /* one_to_one accumulated recoding */
  1193.   unsigned char temp[256];    /* temporary value for accum array */
  1194.   const char **string;        /* one_to_many recoding */
  1195.   STEP *step;            /* new single step being constructed */
  1196.   int in;            /* ordinal of next studied sequence step */
  1197.   int out;            /* ordinal of next output sequence step */
  1198.   int counter;            /* all purpose counter */
  1199.  
  1200.   saved_steps = 0;
  1201.  
  1202.   /* See if there are some RFC 1345 double steps to merge.  */
  1203.  
  1204.   in = 0;
  1205.   out = 0;
  1206.  
  1207.   while (in < length_of_sequence)
  1208.     if (sequence[in]->after == rfc1345
  1209.     && in < length_of_sequence - 1
  1210.     && sequence[in+1]->before == rfc1345)
  1211.       {
  1212.  
  1213.     /* Produce a new single step for the double step.  */
  1214.  
  1215.     step = new_single_step ();
  1216.     step->before = sequence[in]->before;
  1217.     step->after = sequence[in+1]->after;
  1218.     step->quality = merge_qualities (sequence[in]->quality,
  1219.                      sequence[in+1]->quality);
  1220.     step->init_recode = init_recode_rfc1345;
  1221.     step->file_recode = file_one_to_one;
  1222.  
  1223.     in += 2;
  1224.     saved_steps++;
  1225.  
  1226.     /* Initialize the new single step.  If not, it will not be
  1227.        mergeable with others.  */
  1228.  
  1229.     (*step->init_recode) (step);
  1230.     step->init_recode = NULL;
  1231.  
  1232.     sequence[out++] = step;
  1233.       }
  1234.     else if (sequence[in]->before == rfc1345
  1235.          || sequence[in]->after == rfc1345)
  1236.       error (EXIT_FAILURE, 0, "You may not ask for RFC 1345 explicitely");
  1237.     else
  1238.       sequence[out++] = sequence[in++];
  1239.  
  1240.   length_of_sequence = out;
  1241.  
  1242.   /* Recopy the sequence array over itself, while merging subsequences of
  1243.      one or more consecutive one-to-one recodings, including an optional
  1244.      final one-to-many recoding.  */
  1245.  
  1246.   in = 0;
  1247.   out = 0;
  1248.   while (in < length_of_sequence)
  1249.     if (sequence[in]->one_to_one
  1250.     && (sequence[in]->file_recode == file_one_to_one || make_header_mode)
  1251.     && in < length_of_sequence - 1
  1252.     && (((sequence[in+1]->one_to_one)
  1253.          && (sequence[in+1]->file_recode == file_one_to_one
  1254.          || make_header_mode))
  1255.         || (sequence[in+1]->one_to_many
  1256.         && (sequence[in+1]->file_recode == file_one_to_many
  1257.             || make_header_mode))))
  1258.       {
  1259.  
  1260.     /* Construct a new single step, and initialize a cumulative
  1261.        one-to-one recoding with the identity permutation.  */
  1262.  
  1263.     accum = (unsigned char *) xmalloc (256);
  1264.     for (counter = 0; counter < 256; counter++)
  1265.       accum[counter] = counter;
  1266.  
  1267.     step = new_single_step ();
  1268.     step->before = sequence[in]->before;
  1269.     step->quality = REVERSIBLE;
  1270.  
  1271.     /* Merge in all consecutive one-to-one recodings.  */
  1272.  
  1273.     while (in < length_of_sequence
  1274.            && sequence[in]->one_to_one
  1275.            && (sequence[in]->file_recode == file_one_to_one
  1276.            || make_header_mode))
  1277.       {
  1278.         for (counter = 0; counter < 256; counter++)
  1279.           temp[counter] = sequence[in]->one_to_one[accum[counter]];
  1280.         for (counter = 0; counter < 256; counter++)
  1281.           accum[counter] = temp[counter];
  1282.         step->after = sequence[in]->after;
  1283.         step->quality
  1284.           = merge_qualities (step->quality, sequence[in]->quality);
  1285.         in++;
  1286.         saved_steps++;
  1287.       }
  1288.  
  1289.     /* Check for a possible one-to-many recoding.  */
  1290.  
  1291.     if (in < length_of_sequence
  1292.         && sequence[in]->one_to_many
  1293.         && (sequence[in]->file_recode == file_one_to_many
  1294.         || make_header_mode))
  1295.       {
  1296.  
  1297.         /* Merge in the one-to-many recoding, and make the new single
  1298.            step be a one-to-many recoding.  */
  1299.  
  1300.         string = (const char **) xmalloc (256 * sizeof (char *));
  1301.         for (counter = 0; counter < 256; counter++)
  1302.           string[counter] = sequence[in]->one_to_many[accum[counter]];
  1303.         free (accum);
  1304.         step->one_to_many = string;
  1305.         step->file_recode = file_one_to_many;
  1306.         step->after = sequence[in]->after;
  1307.         step->quality
  1308.           = merge_qualities (step->quality, sequence[in]->quality);
  1309.         in++;
  1310.         saved_steps++;
  1311.       }
  1312.     else
  1313.       {
  1314.  
  1315.         /* Make the new single step be a one-to-one recoding.  */
  1316.  
  1317.         step->one_to_one = accum;
  1318.         step->file_recode = file_one_to_one;
  1319.       }
  1320.  
  1321.     /* Save the newly created step.  */
  1322.  
  1323.     sequence[out++] = step;
  1324.       }
  1325.     else
  1326.       {
  1327.  
  1328.     /* This step is not being optimized.  Keep it verbatim.  */
  1329.  
  1330.     sequence[out++] = sequence[in++];
  1331.       }
  1332.  
  1333.   /* Save the resulting sequence length, and tell the user if something
  1334.      changed.  */
  1335.  
  1336.   length_of_sequence = out;
  1337.  
  1338.   if (saved_steps > 0 && verbose_option)
  1339.     echo_sequence ();
  1340. }
  1341.  
  1342. /* Recoding execution control.  */
  1343.  
  1344. /*--------------.
  1345. | Copy a file.  |
  1346. `--------------*/
  1347.  
  1348. static void
  1349. file_copy (FILE *input_file, FILE *output_file)
  1350. {
  1351.   int input_char;        /* current character */
  1352.  
  1353.   while (input_char = getc (input_file), input_char != EOF)
  1354.     putc (input_char, output_file);
  1355. }
  1356.  
  1357. /*--------------------------------------------------.
  1358. | Recode a file using a one-to-one recoding table.  |
  1359. `--------------------------------------------------*/
  1360.  
  1361. void
  1362. file_one_to_one (const STEP *step, FILE *input_file, FILE *output_file)
  1363. {
  1364.   const unsigned char *table;    /* conversion table */
  1365.   int input_char;        /* current character */
  1366.  
  1367.   table = step->one_to_one;
  1368.   if (table == one_to_same)
  1369.  
  1370.     /* Optimize a little an identity recoding.  */
  1371.  
  1372.     file_copy (input_file, output_file);
  1373.  
  1374.   else
  1375.  
  1376.     /* Copy the file through the one to one recoding table.  */
  1377.  
  1378.     while (input_char = getc (input_file), input_char != EOF)
  1379.       putc (table[input_char], output_file);
  1380. }
  1381.  
  1382. /*---------------------------------------------------.
  1383. | Recode a file using a one-to-many recoding table.  |
  1384. `---------------------------------------------------*/
  1385.  
  1386. void
  1387. file_one_to_many (const STEP *step, FILE *input_file, FILE *output_file)
  1388. {
  1389.   const char *const *table;    /* conversion table */
  1390.   int input_char;        /* current character */
  1391.   const char *output_string;    /* translated characters */
  1392.  
  1393.   /* Copy the file through the one to many recoding table.  */
  1394.  
  1395.   table = step->one_to_many;
  1396.   while (input_char = getc (input_file), input_char != EOF)
  1397.     if (output_string = table[input_char], output_string)
  1398.       while (*output_string)
  1399.     {
  1400.       putc (*output_string, output_file);
  1401.       output_string++;
  1402.     }
  1403. }
  1404.  
  1405. /*-------------------------------------------------------------------.
  1406. | Execute the conversion sequence, using several passes with two     |
  1407. | alternating intermediate files.  This routine assumes at least one |
  1408. | needed recoding step.                             |
  1409. `-------------------------------------------------------------------*/
  1410.  
  1411. static void
  1412. execute_pass_sequence (const char *input_name, const char *output_name)
  1413. {
  1414.   int sequence_index;        /* index into sequence */
  1415.   const STEP *step;        /* pointer to step */
  1416.   FILE *input_file;        /* input file to recoding step */
  1417.   FILE *output_file;        /* output file from recoding step */
  1418. #ifdef USE_TMPNAM
  1419.   char *temp_input_name;    /* step input file name */
  1420.   char *temp_output_name;    /* step output file name */
  1421.   char temp_name_1[L_tmpnam];    /* one temporary file name */
  1422.   char temp_name_2[L_tmpnam];    /* another temporary file name */
  1423.   char *exchange_temp;        /* for exchanging temporary names */
  1424. #endif
  1425.  
  1426. #ifdef USE_TMPNAM
  1427.  
  1428.   /* Choose names for intermediate files.  Use "" for delaying them.  */
  1429.  
  1430. #if defined(MSDOS) || defined(OS2)
  1431.   strcpy (temp_name_1, "recodex1.tmp");
  1432.   strcpy (temp_name_2, "recodex2.tmp");
  1433. #else
  1434.   temp_name_1[0] = '\0';
  1435.   temp_name_2[0] = '\0';
  1436. #endif
  1437.   temp_input_name = temp_name_1;
  1438.   temp_output_name = temp_name_2;
  1439.  
  1440. #endif /* USE_TMPNAM */
  1441.  
  1442.   /* Execute one pass for each step of the sequence.  */
  1443.  
  1444.   for (sequence_index = 0;
  1445.        sequence_index < length_of_sequence;
  1446.        sequence_index++)
  1447.     {
  1448.  
  1449.       /* Select the input file for this step.  */
  1450.  
  1451.       if (sequence_index == 0)
  1452.     if (input_name)
  1453.       {
  1454.         input_file = fopen (input_name, "r");
  1455.         if (input_file == NULL)
  1456.           error (EXIT_FAILURE, errno, input_name);
  1457.       }
  1458.     else
  1459.       input_file = stdin;
  1460.       else
  1461.     {
  1462. #ifdef USE_TMPNAM
  1463.       input_file = fopen (temp_input_name, "r");
  1464.       if (input_file == NULL)
  1465.         error (EXIT_FAILURE, errno, temp_input_name);
  1466. #endif
  1467.  
  1468. #ifdef USE_TMPFILE
  1469.       rewind (input_file);
  1470. #endif
  1471.     }
  1472.  
  1473.       /* Select the output file for this step.  */
  1474.  
  1475.       if (sequence_index == length_of_sequence - 1)
  1476.     if (output_name)
  1477.       {
  1478.         output_file = fopen (output_name, "w");
  1479.         if (output_file == NULL)
  1480.           error (EXIT_FAILURE, errno, output_name);
  1481.       }
  1482.     else
  1483.       output_file = stdout;
  1484.       else
  1485.     {
  1486. #ifdef USE_TMPNAM
  1487. #if defined(MSDOS) || defined(OS2)
  1488.       if (*temp_output_name == '\0')
  1489.         tmpnam (temp_output_name);
  1490. #endif
  1491.       output_file = fopen (temp_output_name, "w");
  1492.       if (output_file == NULL)
  1493.         error (EXIT_FAILURE, errno, temp_output_name);
  1494. #endif
  1495.  
  1496. #ifdef USE_TMPFILE
  1497.       output_file = tmpfile ();
  1498.       if (output_file == NULL)
  1499.         error (EXIT_FAILURE, errno, "tmpfile()");
  1500. #endif
  1501.     }
  1502.  
  1503.       /* Execute one recoding step.  */
  1504.  
  1505.       step = sequence[sequence_index];
  1506.       (*step->file_recode) (step, input_file, output_file);
  1507.  
  1508.       /* Close the input file, unlink it if it was temporary.  */
  1509.  
  1510.       if (sequence_index == 0)
  1511.     {
  1512.       if (input_name)
  1513.         fclose (input_file);
  1514.     }
  1515.       else
  1516.     {
  1517.       fclose (input_file);
  1518. #ifdef USE_TMPNAM
  1519.       unlink (temp_input_name);
  1520. #endif
  1521.     }
  1522.  
  1523.       /* Close the output file, exchange names for subsequent step.  */
  1524.  
  1525.       if (sequence_index == length_of_sequence - 1)
  1526.     {
  1527.       if (output_name)
  1528.         fclose (output_file);
  1529.     }
  1530.       else
  1531.     {
  1532. #ifdef USE_TMPNAM
  1533.       fclose (output_file);
  1534.  
  1535.       exchange_temp = temp_input_name;
  1536.       temp_input_name = temp_output_name;
  1537.       temp_output_name = exchange_temp;
  1538. #endif
  1539.  
  1540. #ifdef USE_TMPFILE
  1541.       input_file = output_file;
  1542. #endif
  1543.     }
  1544.     }
  1545. }
  1546.  
  1547. /*-------------------------------------------------------------------------.
  1548. | Execute the conversion sequence, using a chain of invocations of the       |
  1549. | program through popen.  This routine assumes that more than one recoding |
  1550. | step is needed.                               |
  1551. `-------------------------------------------------------------------------*/
  1552.  
  1553. #ifdef HAVE_POPEN
  1554.  
  1555. static void
  1556. execute_popen_sequence (const char *input_name, const char *output_name)
  1557. {
  1558.   const STEP *step;        /* current step */
  1559.   FILE *input_file;        /* input file to recoding step */
  1560.   FILE *output_file;        /* output file from recoding step */
  1561.   char popen_command[80];    /* to receive command string */
  1562.   int status;            /* status to be asserted */
  1563.  
  1564.   /* Construct a `recode' command for all recoding steps but the first.  */
  1565.  
  1566.   sprintf (popen_command, "%s -o %s %s:%s %s%s",
  1567.        program_name,
  1568.        diaeresis_char == ':' ? " -c" : "",
  1569.        clean_charset_name (sequence[1]->before->name),
  1570.        clean_charset_name (sequence[length_of_sequence-1]->after->name),
  1571.        output_name ? "> " : "",
  1572.        output_name ? output_name : "");
  1573.  
  1574.   /* Execute the first recoding step.  */
  1575.  
  1576.   if (!input_name)
  1577.     input_file = stdin;
  1578.   else if (input_file = fopen (input_name, "r"), input_file == NULL)
  1579.     error (EXIT_FAILURE, errno, input_name);
  1580.  
  1581.   if (output_file = popen (popen_command, "w"), output_file == NULL)
  1582.     error (EXIT_FAILURE, errno, popen_command);
  1583.  
  1584.   step = sequence[0];
  1585.   (*step->file_recode) (step, input_file, output_file);
  1586.  
  1587.   if (input_name)
  1588.     fclose (input_file);
  1589.   status = pclose (output_file);
  1590.   if (status != 0)
  1591.     error (EXIT_FAILURE, errno, popen_command);
  1592. }
  1593.  
  1594. #endif /* HAVE_POPEN */
  1595.  
  1596. /*-------------------------------------------------------------------------.
  1597. | Execute the conversion sequence, forking the program many times for all  |
  1598. | elementary steps, interconnecting them with pipes.  This routine assumes |
  1599. | at least one recoding step is needed.                       |
  1600. `-------------------------------------------------------------------------*/
  1601.  
  1602. #ifndef HAVE_DUP2
  1603. #undef HAVE_PIPE
  1604. #endif
  1605.  
  1606. #ifdef HAVE_PIPE
  1607.  
  1608. static void
  1609. execute_pipe_sequence (const char *input_name, const char *output_name)
  1610. {
  1611.   int sequence_index;        /* index into sequence */
  1612.   const STEP *step;        /* pointer into single_steps */
  1613.  
  1614.   FILE *input_file;        /* input file to recoding step */
  1615.   FILE *output_file;        /* output file from recoding step */
  1616.   int pipe_pair[2];        /* pair of file descriptors for a pipe */
  1617.   int child_process;        /* child process number, zero if child */
  1618.   int status;            /* status to be asserted */
  1619.  
  1620.   /* Prepare the final output file.  */
  1621.  
  1622.   if (output_name)
  1623.     {
  1624.       output_file = fopen (output_name, "w");
  1625.       if (output_file != NULL)
  1626.     error (EXIT_FAILURE, errno, output_name);
  1627.     }
  1628.   else
  1629.     output_file = stdout;
  1630.  
  1631.   /* Create all subprocesses and interconnect them.  */
  1632.  
  1633.   for (sequence_index = length_of_sequence - 1;
  1634.        sequence_index > 0;
  1635.        sequence_index--)
  1636.     {
  1637.       status = pipe (pipe_pair);
  1638.       if (status != 0)
  1639.     error (EXIT_FAILURE, errno, "Creating pipe");
  1640.       child_process = fork ();
  1641.       if (child_process < 0)
  1642.     error (EXIT_FAILURE, errno, "Forking process");
  1643.       if (child_process == 0)
  1644.     {
  1645.  
  1646.           /* The child executes its recoding step, reading from the pipe
  1647.              and writing to the current output file; then it exits.  */
  1648.  
  1649.       status = close (pipe_pair[1]);
  1650.       if (status != 0)
  1651.         error (EXIT_FAILURE, errno, "Closing pipe output side");
  1652.       input_file = fdopen (pipe_pair[0], "r");
  1653.       if (input_file == NULL)
  1654.         error (EXIT_FAILURE, errno, "Streaming pipe input side");
  1655.  
  1656.       step = sequence[sequence_index];
  1657.       (*step->file_recode) (step, input_file, output_file);
  1658.  
  1659.       fclose (input_file);
  1660.       if (sequence_index < length_of_sequence - 1 || output_name)
  1661.         fclose (output_file);
  1662.       exit (EXIT_SUCCESS);
  1663.     }
  1664.       else
  1665.     {
  1666.  
  1667.           /* The parent redirects the current output file to the pipe.  */
  1668.  
  1669.       status = dup2 (pipe_pair[1], fileno (output_file));
  1670.       if (status == -1)
  1671.         error (EXIT_FAILURE, errno, "Duplicating pipe output");
  1672.       status = close (pipe_pair[0]);
  1673.       if (status != 0)
  1674.         error (EXIT_FAILURE, error, "Closing pipe input side");
  1675.       status = close (pipe_pair[1]);
  1676.       if (status != 0)
  1677.         error (EXIT_FAILURE, error, "Closing pipe output side");
  1678.     }
  1679.     }
  1680.  
  1681.   /* All the children are created, blocked on read.  Now, feed the whole
  1682.      chain of processes with the output of the first recoding step.  */
  1683.  
  1684.   if (!input_name)
  1685.     input_file = stdin;
  1686.   else
  1687.     {
  1688.       input_file = fopen (input_name, "r");
  1689.       if (input_file == NULL)
  1690.     error (EXIT_FAILURE, errno, input_name);
  1691.     }
  1692.  
  1693.   (*sequence[0]->routine) (input_file, output_file);
  1694.  
  1695.   if (input_name)
  1696.     fclose (input_file);
  1697.   if (output_name)
  1698.     fclose (output_file);
  1699.  
  1700.   /* Wait on all children, mainly to avoid synchronisation problems on
  1701.      output file contents, but also to reduce the number of zombie
  1702.      processes in case the user recodes many files at once.  */
  1703.  
  1704.   while (wait (NULL) > 0)
  1705.     ;
  1706. }
  1707.  
  1708. #endif /* HAVE_PIPE */
  1709.  
  1710. /*-----------------------------------------------------------------------.
  1711. | Execute the conversion sequence, using the selected strategy whenever     |
  1712. | more than one conversion step is needed.  If no conversion are needed, |
  1713. | merely copy the input onto the output.                 |
  1714. `-----------------------------------------------------------------------*/
  1715.  
  1716. /* If some sequencing strategies are missing, this routine automatically
  1717.    uses fallback strategies.  */
  1718.  
  1719. static void
  1720. execute_sequence (const char *input_name, const char *output_name)
  1721. {
  1722.   FILE *input_file;        /* input file to recoding step */
  1723.   FILE *output_file;        /* output file from recoding step */
  1724.   const STEP *step;        /* current step */
  1725.  
  1726. #if defined(MSDOS) || defined(OS2)
  1727.   if (!input_name)
  1728.     setmode (fileno (stdin), O_BINARY);
  1729.   if (!output_name)
  1730.     setmode (fileno (stdout), O_BINARY);
  1731. #ifdef __EMX__
  1732.   {
  1733.     extern int _fmode_bin;
  1734.     _fmode_bin = 1;
  1735.   }
  1736. #else
  1737.   _fmode = O_BINARY;
  1738. #endif
  1739. #endif
  1740.  
  1741.   if (verbose_option && input_name)
  1742.     {
  1743.       fprintf (stderr, "Recoding %s...", input_name);
  1744.       fflush (stderr);
  1745.     }
  1746.  
  1747.   if (length_of_sequence > 1)
  1748.     switch (sequence_strategy)
  1749.       {
  1750.       case STRATEGY_UNDECIDED:
  1751.     error (EXIT_FAILURE, 0, "Internal error - strategy undecided");
  1752.  
  1753.       case SEQUENCE_WITH_PIPE:
  1754. #ifdef HAVE_PIPE
  1755.     execute_pipe_sequence (input_name, output_name);
  1756.     break;
  1757. #endif
  1758.  
  1759.       case SEQUENCE_WITH_POPEN:
  1760. #ifdef HAVE_POPEN
  1761.     execute_popen_sequence (input_name, output_name);
  1762.     break;
  1763. #endif
  1764.  
  1765.       case SEQUENCE_WITH_FILES:
  1766.     execute_pass_sequence (input_name, output_name);
  1767.     break;
  1768.       }
  1769.   else
  1770.     {
  1771.  
  1772.       /* This is a single-step recoding or a mere copy.  Do it.  */
  1773.  
  1774.       if (!input_name)
  1775.     input_file = stdin;
  1776.       else if (input_file = fopen (input_name, "r"), input_file == NULL)
  1777.     error (EXIT_FAILURE, errno, input_name);
  1778.  
  1779.       if (!output_name)
  1780.     output_file = stdout;
  1781.       else if (output_file = fopen (output_name, "w"), output_file == NULL)
  1782.     error (EXIT_FAILURE, errno, output_name);
  1783.  
  1784.       if (length_of_sequence == 1)
  1785.     {
  1786.       step = sequence[0];
  1787.       (*step->file_recode) (step, input_file, output_file);
  1788.     }
  1789.       else
  1790.     file_copy (input_file, output_file);
  1791.  
  1792.       if (input_name)
  1793.     fclose (input_file);
  1794.       if (output_name)
  1795.     fclose (output_file);
  1796.     }
  1797.  
  1798.   if (verbose_option && input_name)
  1799.     {
  1800.       fprintf (stderr, " done\n");
  1801.       fflush (stderr);
  1802.     }
  1803. }
  1804.  
  1805. /* Some special option handling.  */
  1806.  
  1807. /*-----------------------------------------------------------------.
  1808. | Print a truncated charset name, with a guaranteed space at end.  |
  1809. `-----------------------------------------------------------------*/
  1810.  
  1811. static void
  1812. print_truncated_charset_name (const char *name)
  1813. {
  1814.   char copy[15];
  1815.  
  1816.   if ((int) strlen (name) > 14)
  1817.     {
  1818.       memcpy (copy, name, 14);
  1819.       copy[14] = '\0';
  1820.       name = copy;
  1821.     }
  1822.   printf ("%-14s ", name);
  1823. }
  1824.  
  1825. /*----------------------------------------------------.
  1826. | Find all possible sequences and report about them.  |
  1827. `----------------------------------------------------*/
  1828.  
  1829. static void
  1830. report_about_all_sequences (void)
  1831. {
  1832.   CHARSET *before;
  1833.   CHARSET *after;
  1834.   int saved_length_of_sequence;
  1835.   int saved_number_of_single_steps;
  1836.   QUALITY quality;
  1837.   const char *quality_string;
  1838.   int counter;
  1839.   STEP *step;
  1840.  
  1841.   for (before = charset_array;
  1842.        before < charset_array + number_of_charsets;
  1843.        before++)
  1844.  
  1845.     if (!before->ignore && before != rfc1345)
  1846.  
  1847.       for (after = charset_array;
  1848.        after < charset_array + number_of_charsets;
  1849.        after++)
  1850.  
  1851.     if (!after->ignore && after != before && after != rfc1345)
  1852.       {
  1853.  
  1854.         /* Study what we can do.  */
  1855.  
  1856.         find_sequence (before, after);
  1857.         if (length_of_sequence < 0)
  1858.           {
  1859.         if (!ignored_name)
  1860.           {
  1861.             print_truncated_charset_name (before->name);
  1862.             print_truncated_charset_name (after->name);
  1863.             printf ("  UNACHIEVABLE\n");
  1864.           }
  1865.           }
  1866.         else
  1867.           {
  1868.  
  1869.         /* Compute the recoding quality.  */
  1870.  
  1871.         quality = REVERSIBLE;
  1872.         for (counter = 0; counter < length_of_sequence; counter++)
  1873.           quality = merge_qualities (quality,
  1874.                          sequence[counter]->quality);
  1875.         quality_string = quality_to_string (quality);
  1876.  
  1877.         /* Study what optimization can do.  */
  1878.  
  1879.         saved_length_of_sequence = length_of_sequence;
  1880.         saved_number_of_single_steps = number_of_single_steps;
  1881.  
  1882.         optimize_sequence ();
  1883.  
  1884.         /* Check and report codes which should be aliases.  */
  1885.  
  1886.         if (length_of_sequence == 1 && sequence[0]->one_to_one)
  1887.           {
  1888.             for (counter = 0; counter < 256; counter++)
  1889.               if (sequence[0]->one_to_one[counter] != counter)
  1890.             break;
  1891.             if (counter == 256)
  1892.               quality_string = "ONE to SAME";
  1893.           }
  1894.  
  1895.         /* Make the report.  */
  1896.  
  1897.         print_truncated_charset_name (before->name);
  1898.         print_truncated_charset_name (after->name);
  1899.         printf ("  %-16s", quality_string);
  1900.         printf ("steps: %d", saved_length_of_sequence);
  1901.  
  1902.         if (length_of_sequence != saved_length_of_sequence)
  1903.           printf (", %d saved by merging",
  1904.               saved_length_of_sequence - length_of_sequence);
  1905.         printf ("\n");
  1906.  
  1907.         /* Unregister and clean up the merged steps.  */
  1908.  
  1909.         while (number_of_single_steps > saved_number_of_single_steps)
  1910.           {
  1911.             number_of_single_steps--;
  1912.             step = single_step_array + number_of_single_steps;
  1913.             if (step->one_to_one)
  1914.               free ((void *) step->one_to_one);
  1915.             if (step->one_to_many)
  1916.               free ((void *) step->one_to_many);
  1917.           }
  1918.           }
  1919.       }
  1920. }
  1921.  
  1922. /* Main program.  */
  1923.  
  1924. /*-----------------------------------------------.
  1925. | Explain how to use the program, then get out.     |
  1926. `-----------------------------------------------*/
  1927.  
  1928. void
  1929. usage (int status)
  1930. {
  1931.   if (status != EXIT_SUCCESS)
  1932.     fprintf (stderr, "Try `%s %s' for more information.\n", program_name,
  1933.          decoding_charset_flag ? "--list" : "--help");
  1934.   else
  1935.     {
  1936.       printf ("\n%s\n\n", version_string);
  1937.       printf ("\
  1938. Usage: %s [OPTION]... [CHARSET]\n", program_name);
  1939.  
  1940.       printf ("\
  1941.   -C, --copyright        display Copyright and copying conditions\n\
  1942.   -l, --list[=FORMAT]    list one or all known charsets\n\
  1943.       --help             display this help\n\
  1944.       --version          output version information\n\
  1945. \n\
  1946. FORMAT it is one of decimal, octal, hexadecimal or full, it defaults to\n\
  1947. decimal if CHARSET given.  CHARSET defaults to %s if FORMAT given.\n",
  1948.           DEFAULT_CHARSET);
  1949.  
  1950.       printf ("\
  1951. Option -l with no FORMAT nor CHARSET list all charsets, also see the texinfo\n\
  1952. documentation.  My preferred charsets are (each user has preferences):\n\
  1953. \n\
  1954.   ascii-bs   ASCII (7-bit), using backspace to apply diacritics\n\
  1955.   ibmpc      IBM-PC 8-bit characters, with proper newlines\n\
  1956.   latex      LaTeX coding of foreign and diacriticized characters\n\
  1957.   latin1     ISO Latin-1 8-bit extension of ASCII\n\
  1958.   texte      Easy French convention for transmitting email messages\n");
  1959.  
  1960.       printf ("\
  1961. \n\
  1962. Usage: %s [OPTION]... [BEFORE]:[AFTER] [FILE]...\n", program_name);
  1963.  
  1964.       printf ("\
  1965. \n\
  1966.   -a, --auto-check       study all recoding paths, report, then exit\n\
  1967.   -c, --colons           use colons instead of double quotes for diaeresis\n\
  1968.   -d, --diacritics       limit conversion to diacritics or alike for LaTeX\n");
  1969.  
  1970. #if 0
  1971.       printf ("\
  1972.   -f, --force            force recoding even if it is not reversible\n");
  1973. #endif
  1974.  
  1975.       printf ("\
  1976.   -g, --graphics         approximate ibmpc rulers by ASCII graphics\n\
  1977.   -h, --header[=NAME]    write C code with table NAME on stdout, then exit\n\
  1978.   -i, --sequence=files   use intermediate files for sequencing passes\n");
  1979.  
  1980. #ifdef HAVE_POPEN
  1981.       printf ("\
  1982.   -o, --sequence=popen   use popen machinery for sequencing passes\n");
  1983. #else
  1984.       printf ("\
  1985.   -o, --sequence=popen   same as -i (on this system)\n");
  1986. #endif
  1987.  
  1988. #ifdef HAVE_PIPE
  1989.       printf ("\
  1990.   -p, --sequence=pipe    use pipe machinery for sequencing passes\n");
  1991. #else
  1992.       printf ("\
  1993.   -p, --sequence=pipe    same as -o (on this system)\n");
  1994. #endif
  1995.  
  1996.       printf ("\
  1997.   -s, --strict           use strict mappings, even loose characters\n\
  1998.   -t, --touch            touch the recoded files after replacement\n\
  1999.   -v, --verbose          explain sequence of steps and report progress\n\
  2000.   -x, --ignore=CHARSET   ignore CHARSET while choosing a recoding path\n\
  2001. \n\
  2002. If none of -i, -o and -p are given, presume -p if no FILE, else -i.\n\
  2003. Each FILE is recoded over itself, destroying the original.  If no\n\
  2004. FILE is specified, then act as a filter and recode stdin to stdout.\n");
  2005.     }
  2006.   exit (status);
  2007. }
  2008.  
  2009. /*----------------------------------------------------------------------.
  2010. | Main program.  Decode ARGC arguments passed through the ARGV array of |
  2011. | strings, then launch execution.                        |
  2012. `----------------------------------------------------------------------*/
  2013.  
  2014. /* Long options equivalences.  */
  2015. static const struct option long_options[] =
  2016. {
  2017.   {"auto-check", no_argument, NULL, 'a'},
  2018.   {"colons", no_argument, NULL, 'c'},
  2019.   {"copyright", no_argument, NULL, 'C'},
  2020.   {"diacritics", no_argument, NULL, 'd'},
  2021.   {"force", no_argument, NULL, 'f'},
  2022.   {"header", optional_argument, NULL, 'h'},
  2023.   {"help", no_argument, &show_help, 1},
  2024.   {"ignore", required_argument, NULL, 'x'},
  2025.   {"list", optional_argument, NULL, 'l'},
  2026.   {"sequence", required_argument, NULL, '\n'},
  2027.   {"strict", no_argument, NULL, 's'},
  2028.   {"touch", no_argument, NULL, 't'},
  2029.   {"verbose", no_argument, NULL, 'v'},
  2030.   {"version", no_argument, &show_version, 1},
  2031.   {0, 0, 0, 0},
  2032. };
  2033.  
  2034. static const char *const format_strings[] =
  2035.   {
  2036.     "decimal",
  2037.     "octal",
  2038.     "hexadecimal",
  2039.     "full",
  2040.     NULL,
  2041.   };
  2042.  
  2043. static const char *const sequence_strings[] =
  2044.   {
  2045.     "files",
  2046.     "popen",
  2047.     "pipe",
  2048.     NULL,
  2049.   };
  2050.  
  2051. int
  2052. main (int argc, char *const *argv)
  2053. {
  2054.   extern int optind;        /* index of argument */
  2055.   int option_char;        /* option character */
  2056.   const char *input_name;    /* input file name */
  2057.   char output_name[200];    /* output file name */
  2058.   FILE *file;            /* file to check or stat */
  2059. #ifdef MSDOS
  2060.   struct ftime stamp_stat;    /* input file time stamps */
  2061. #else
  2062.   struct stat stamp_stat;    /* input file time stamps */
  2063.   time_t stamp_utime[2];    /* recoded file time stamps */
  2064. #endif
  2065.   char *cursor;            /* all purpose cursor */
  2066.  
  2067.   /* Decode command options.  */
  2068.  
  2069.   program_name = argv[0];
  2070.  
  2071.   while (option_char = getopt_long (argc, argv, "aCcdfgh::il::opstvx:",
  2072.                     long_options, NULL),
  2073.      option_char != EOF)
  2074.     switch (option_char)
  2075.       {
  2076.       default:
  2077.     usage (EXIT_FAILURE);
  2078.  
  2079.       case '\0':
  2080.     break;
  2081.  
  2082.       case '\n':
  2083.     switch (argmatch (optarg, sequence_strings))
  2084.       {
  2085.       case -2:
  2086.         error (0, 0, "Ambiguous sequence `%s'", optarg);
  2087.         usage (EXIT_FAILURE);
  2088.  
  2089.       case -1:
  2090.         error (0, 0, "Unknown sequence `%s'", optarg);
  2091.         usage (EXIT_FAILURE);
  2092.  
  2093.       case 0:
  2094.         sequence_strategy = SEQUENCE_WITH_FILES;
  2095.         break;
  2096.  
  2097.       case 1:
  2098.         sequence_strategy = SEQUENCE_WITH_POPEN;
  2099.         break;
  2100.  
  2101.       case 2:
  2102.         sequence_strategy = SEQUENCE_WITH_PIPE;
  2103.         break;
  2104.       }
  2105.     break;
  2106.  
  2107.       case 'a':
  2108.     auto_check_mode = 1;
  2109.     break;
  2110.  
  2111.       case 'C':
  2112.     fprintf (stderr, "%s", copyright_string);
  2113.     exit (EXIT_SUCCESS);
  2114.  
  2115.       case 'c':
  2116.     diaeresis_char = ':';
  2117.     break;
  2118.  
  2119.       case 'd':
  2120.     diacritics_only = 1;
  2121.     break;
  2122.  
  2123.       case 'f':
  2124.     force_option = 1;
  2125.     break;
  2126.  
  2127.       case 'g':
  2128.     ascii_graphics = 1;
  2129.     break;
  2130.  
  2131.       case 'h':
  2132.     make_header_mode = 1;
  2133.     header_name = optarg;
  2134.     break;
  2135.  
  2136.       case 'i':
  2137.     sequence_strategy = SEQUENCE_WITH_FILES;
  2138.     break;
  2139.  
  2140.       case 'l':
  2141.     show_charsets = 1;
  2142.     if (optarg)
  2143.       switch (argmatch (optarg, format_strings))
  2144.         {
  2145.         case -2:
  2146.           error (0, 0, "Ambiguous format `%s'", optarg);
  2147.           usage (EXIT_FAILURE);
  2148.  
  2149.         case -1:
  2150.           error (0, 0, "Unknown format `%s'", optarg);
  2151.           usage (EXIT_FAILURE);
  2152.  
  2153.         case 0:
  2154.           list_format = DECIMAL_FORMAT;
  2155.           break;
  2156.  
  2157.         case 1:
  2158.           list_format = OCTAL_FORMAT;
  2159.           break;
  2160.  
  2161.         case 2:
  2162.           list_format = HEXADECIMAL_FORMAT;
  2163.           break;
  2164.  
  2165.         case 3:
  2166.           list_format = FULL_FORMAT;
  2167.           break;
  2168.         }
  2169.     break;
  2170.  
  2171.       case 'o':
  2172.     sequence_strategy = SEQUENCE_WITH_POPEN;
  2173.     break;
  2174.  
  2175.       case 'p':
  2176.     sequence_strategy = SEQUENCE_WITH_PIPE;
  2177.     break;
  2178.  
  2179.       case 's':
  2180.     strict_mapping = 1;
  2181.     break;
  2182.  
  2183.       case 't':
  2184.     touch_option = 1;
  2185.     break;
  2186.  
  2187.       case 'v':
  2188.     verbose_option = 1;
  2189.     break;
  2190.  
  2191.       case 'x':
  2192.     ignored_name = optarg;
  2193.     break;
  2194.       }
  2195.  
  2196.   if (strict_mapping)
  2197.     force_option = 1;
  2198.  
  2199.   /* Process trivial options.  */
  2200.  
  2201.   if (show_version)
  2202.     {
  2203.       printf ("%s\n", version_string);
  2204.       exit (EXIT_SUCCESS);
  2205.     }
  2206.  
  2207.   if (show_help)
  2208.     usage (EXIT_SUCCESS);
  2209.  
  2210.   /* Register all modules, then set the ignored charset.  */
  2211.  
  2212.   register_all_modules ();
  2213.   make_argmatch_array ();
  2214.  
  2215.   if (ignored_name)
  2216.     find_charset (clean_charset_name (ignored_name))->ignore = 1;
  2217.  
  2218.   /* Process charset listing options.  */
  2219.  
  2220.   if (show_charsets)
  2221.     {
  2222.  
  2223.       /* Select a possible charset and a default format.  */
  2224.  
  2225.       if (optind + 1 == argc)
  2226.     list_charset = find_charset (clean_charset_name (argv[optind]));
  2227.       else if (list_format != NO_FORMAT)
  2228.     list_charset = find_charset (clean_charset_name (NULL));
  2229.       else
  2230.     list_charset = NULL;
  2231.  
  2232.       /* List the charset(s) appropriately.  */
  2233.       
  2234.       if (list_charset)
  2235.     if (list_format == FULL_FORMAT)
  2236.       list_full_charset (list_charset);
  2237.     else
  2238.       list_concise_charset (list_charset);
  2239.       else
  2240.     list_all_charsets ();
  2241.  
  2242.       /* Then get out.  */
  2243.  
  2244.       exit (EXIT_SUCCESS);
  2245.     }
  2246.  
  2247.   /* Process other options not requiring BEFORE:AFTER.  */
  2248.  
  2249.   if (auto_check_mode)
  2250.     {
  2251.       report_about_all_sequences ();
  2252.       exit (EXIT_SUCCESS);
  2253.     }
  2254.  
  2255.   /* Prepare for decoding the BEFORE:AFTER argument.  Split it.  Then
  2256.      guarantee a NULL at end of charset_name_array.  */
  2257.  
  2258.   if (optind + 1 > argc)
  2259.     usage (EXIT_SUCCESS);
  2260.  
  2261.   decode_before_after (argv[optind++]);
  2262.  
  2263.   /* Establish the sequence of recoding steps.  */
  2264.  
  2265.   length_of_sequence = 0;
  2266.   find_sequence (before_charset, after_charset);
  2267.   if (length_of_sequence < 0)
  2268.     error (EXIT_FAILURE, 0, "No way to recode from %s to %s.",
  2269.        before_charset->name, after_charset->name);
  2270.  
  2271.   optimize_sequence ();
  2272.  
  2273.   /* If we merely want C code, do it and get out.  */
  2274.  
  2275.   if (make_header_mode)
  2276.     {
  2277.       if (length_of_sequence == 0)
  2278.     error (EXIT_FAILURE, 0, "No C code for the trivial recoding");
  2279.       if (length_of_sequence > 1
  2280.       || !(sequence[0]->one_to_one || sequence[0]->one_to_many))
  2281.     error (EXIT_FAILURE, 0, "Unable to deduce the recoding table");
  2282.  
  2283.       output_header_file ();
  2284.       exit (EXIT_SUCCESS);
  2285.     }
  2286.  
  2287.   /* If there is no input file, act as a filter.  Else, recode all files
  2288.      over themselves.  */
  2289.  
  2290.   if (optind < argc)
  2291.     {
  2292.  
  2293.       /* When reading and writing files, unless the user selected otherwise,
  2294.      avoid forking and use intermediate files.  */
  2295.  
  2296.       if (sequence_strategy == STRATEGY_UNDECIDED)
  2297.     sequence_strategy = SEQUENCE_WITH_FILES;
  2298.  
  2299.       /* In case files are recoded over themselves and there is no
  2300.          recoding step at all, do not even try to touch the files.  */
  2301.  
  2302.       if (length_of_sequence > 0)
  2303.  
  2304.     /* Process files, one at a time.  */
  2305.  
  2306.     for (; optind < argc; optind++)
  2307.       {
  2308.         input_name = argv[optind];
  2309.  
  2310.         /* Check if the file can be read and rewritten.  */
  2311.  
  2312.         if (file = fopen (input_name, "r+"), file == NULL)
  2313.           error (EXIT_FAILURE, errno, input_name);
  2314.  
  2315.         /* Save the input file time stamp.  */
  2316.  
  2317.         if (!touch_option)
  2318.           {
  2319. #ifdef MSDOS
  2320.         getftime (fileno (file), &stamp_stat);
  2321. #else
  2322.         fstat (fileno (file), &stamp_stat);
  2323. #endif
  2324.           }
  2325.  
  2326.         fclose (file);
  2327.  
  2328.         /* Choose an output file in the same directory.  */
  2329.  
  2330.         strcpy (output_name, input_name);
  2331.         for (cursor = output_name + strlen (output_name);
  2332.          cursor > output_name && cursor[-1] != '/'
  2333. #if defined(MSDOS) || defined(OS2)
  2334.          && cursor[-1] != '\\' && cursor[-1] != ':'
  2335. #endif
  2336.          ; cursor--)
  2337.           ;
  2338.         strcpy (cursor, "recodeXX.TMP");
  2339.  
  2340.         /* Recode the file.  */
  2341.  
  2342.         execute_sequence (input_name, output_name);
  2343.  
  2344.         /* Move the new file over the original.  */
  2345.  
  2346.         if (unlink (input_name) < 0)
  2347.           error (EXIT_FAILURE, errno, input_name);
  2348. #ifdef HAVE_RENAME
  2349.         if (rename (output_name, input_name) < 0)
  2350.           error (EXIT_FAILURE, errno, output_name);
  2351. #else
  2352.         if (link (output_name, input_name) < 0)
  2353.           error (EXIT_FAILURE, errno, output_name);
  2354.         if (unlink (output_name) < 0)
  2355.           error (EXIT_FAILURE, errno, output_name);
  2356. #endif
  2357.  
  2358.         /* Adjust the time stamp for the new file.  */
  2359.  
  2360.         if (!touch_option)
  2361.           {
  2362. #ifdef MSDOS
  2363.         file = fopen (input_name, "r");
  2364.         if (file == NULL)
  2365.           error (EXIT_FAILURE, errno, input_name);
  2366.         setftime (fileno (file), &stamp_stat);
  2367.         fclose (file);
  2368. #else
  2369.         stamp_utime[0] = stamp_stat.st_atime;
  2370.         stamp_utime[1] = stamp_stat.st_mtime;
  2371.         utime (input_name, stamp_utime);
  2372. #endif
  2373.           }
  2374.       }
  2375.     }
  2376.   else
  2377.     {
  2378.  
  2379.       /* When reading stdin and writing stdout, unless the user selected
  2380.          otherwise, fork processes interconnected with pipes.  */
  2381.  
  2382.       if (sequence_strategy == STRATEGY_UNDECIDED)
  2383.     sequence_strategy = SEQUENCE_WITH_PIPE;
  2384.  
  2385.       execute_sequence (NULL, NULL);
  2386.     }
  2387.   exit (EXIT_SUCCESS);
  2388. }
  2389.