"Fossies" - the Fresh Open Source Software Archive

Member "recode-3.7.12/src/main.c" (15 Feb 2022, 25116 Bytes) of package /linux/misc/recode-3.7.12.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "main.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.7.11_vs_3.7.12.

    1 /* Conversion of files between different charsets and surfaces.
    2    Copyright © 1990,92,93,94,96,97,98,99,00 Free Software Foundation, Inc.
    3    François Pinard <pinard@iro.umontreal.ca>, 1990.
    4 
    5    This program is free software; you can redistribute it and/or modify it
    6    under the terms of the GNU General Public License as published by the
    7    Free Software Foundation; either version 3, or (at your option) any later
    8    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 General
   13    Public License for more details.
   14 
   15    You should have received a copy of the GNU General Public License along
   16    along with this program; if not, see <https://www.gnu.org/licenses/>.
   17 */
   18 
   19 #include "config.h"
   20 #include "common.h"
   21 
   22 #include <ctype.h>
   23 #include <sys/types.h>
   24 #include <sys/stat.h>
   25 #include <unistd.h>
   26 #include <libgen.h>
   27 #include <utime.h>
   28 #include <setjmp.h>
   29 #include <locale.h>
   30 
   31 #include "getopt.h"
   32 #include "xbinary-io.h"
   33 
   34 /* Variables.  */
   35 
   36 /* The name this program was run with. */
   37 const char *program_name;
   38 
   39 /* If nonzero, display usage information and exit.  */
   40 static int show_help = 0;
   41 
   42 /* If nonzero, print the version on standard output and exit.  */
   43 static int show_version = 0;
   44 
   45 /* If true, show a list of one or all known charsets, then exit.  */
   46 static bool show_symbols = false;
   47 
   48 /* If true, check all charsets for subsets, then exit.  */
   49 static bool find_subsets = false;
   50 
   51 /* User provided list of charset restrictions (option -k).  */
   52 static const char *charset_restrictions = NULL;
   53 
   54 /* Indicates the format for showing only one charset.  */
   55 enum recode_list_format list_format = RECODE_NO_FORMAT;
   56 
   57 /* Table name in generated language code.  */
   58 static const char *header_name;
   59 
   60 /* Language to use for generated code.  */
   61 static enum recode_programming_language header_language;
   62 
   63 /* This option asks recode to report the progress while recoding many files
   64    in a single call.  */
   65 static bool verbose_flag = false;
   66 
   67 /* This option merely inhibits messages about non-reversible recodings, but
   68    it does not prevent recodings to be aborted or exit status to be set.  */
   69 static bool quiet_flag = false;
   70 
   71 /* If the recoding yields some problems in reversibility in some file, this
   72    file replacement is denied and it is left unrecoded or, if recode is used
   73    as a mere filter, the recoding is aborted.  The following flag forces
   74    the recoding to run to completion and the replacement to be done even if
   75    the recoding is not reversible.  */
   76 static bool force_flag = false;
   77 
   78 /* When a file is recoded over itself, precautions are taken to move the
   79    timestamps of the original file into the recoded file, so to make the
   80    recoding the most transparent possible to make, and other tools.
   81    However, selecting the following option inhibit the timestamps handling,
   82    thus effectively `touching' the file.  */
   83 static bool touch_option = false;
   84 
   85 /* With strict mapping, all reversibility fallbacks get defeated.  */
   86 static bool strict_mapping = false;
   87 
   88 /* Use iconv if possible. */
   89 static bool prefer_iconv = false;
   90 
   91 /* The following charset name will be ignored, if given.  */
   92 static const char *ignored_name = NULL;
   93 
   94 /* Ordinals of list, BEFORE and AFTER charset.  */
   95 static RECODE_SYMBOL list_charset;
   96 
   97 /* Flag telling usage that we are decoding charsets.  */
   98 bool decoding_charset_flag = false;
   99 
  100 /* Error handling.  */
  101 
  102 /*-------------------------------------------------.
  103 | Produce a message explaining the error in TASK.  |
  104 `-------------------------------------------------*/
  105 
  106 static const char *
  107 task_perror (RECODE_CONST_TASK task)
  108 {
  109   switch (task->error_so_far)
  110     {
  111     case RECODE_NO_ERROR:
  112       return _("No error");
  113 
  114     case RECODE_NOT_CANONICAL:
  115       return _("Non canonical input");
  116 
  117     case RECODE_AMBIGUOUS_OUTPUT:
  118       return _("Ambiguous output");
  119 
  120     case RECODE_UNTRANSLATABLE:
  121       return _("Untranslatable input");
  122 
  123     case RECODE_INVALID_INPUT:
  124       return _("Invalid input");
  125 
  126     case RECODE_SYSTEM_ERROR:
  127       return _("System detected problem");
  128 
  129     case RECODE_USER_ERROR:
  130       return _("Misuse of recoding library");
  131 
  132     default:
  133       return _("Internal recoding bug");
  134     }
  135 }
  136 
  137 /*-----------------.
  138 | Signal handler.  |
  139 `-----------------*/
  140 
  141 #ifdef HAVE_PIPE
  142 static void
  143 sig_catch(int sig, void (*handler) (int))
  144 {
  145   struct sigaction sa;
  146   sa.sa_handler = handler;
  147   sa.sa_flags = 0;
  148   sigemptyset (&sa.sa_mask);
  149   sigaction (sig, &sa, NULL);  /* ignore error: none possible */
  150 }
  151 
  152 static void
  153 signal_handler (int number)
  154 {
  155   recode_interrupted = 1;
  156   sig_catch (number, signal_handler);
  157 }
  158 
  159 static void
  160 setup_signals (void)
  161 {
  162  sig_catch (SIGPIPE, signal_handler);
  163 }
  164 #endif
  165 
  166 /* Main control.  */
  167 
  168 /*-----------------------------------.
  169 | Prints a more detailed Copyright.  |
  170 `-----------------------------------*/
  171 
  172 static void
  173 print_copyright (void)
  174 {
  175   fputs (_("\
  176 This program is free software; you can redistribute it and/or modify\n\
  177 it under the terms of the GNU General Public License as published by\n\
  178 the Free Software Foundation; either version 3, or (at your option)\n\
  179 any later version.\n\
  180 \n\
  181 This program is distributed in the hope that it will be useful,\n\
  182 but WITHOUT ANY WARRANTY; without even the implied warranty of\n\
  183 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\n\
  184 GNU General Public License for more details.\n\
  185 \n\
  186 You should have received a copy of the GNU General Public License\n\
  187 along with this program; if not, see <https://www.gnu.org/licenses/>.\n"),
  188      stdout);
  189 }
  190 
  191 /*-----------------------------------------------.
  192 | Explain how to use the program, then get out.  |
  193 `-----------------------------------------------*/
  194 
  195 static void
  196 usage (int status, bool list)
  197 {
  198   if (status != EXIT_SUCCESS)
  199     fprintf (stderr, _("Try `%s %s' for more information.\n"), program_name,
  200          list ? "--list" : "--help");
  201   else
  202     {
  203       fputs (_("\
  204 Recode converts files between various character sets and surfaces.\n\
  205 "),
  206          stdout);
  207       printf (_("\
  208 \n\
  209 Usage: %s [OPTION]... [ [CHARSET] | REQUEST [FILE]... ]\n"), program_name);
  210       fputs (_("\
  211 \n\
  212 If a long option shows an argument as mandatory, then it is mandatory\n\
  213 for the equivalent short option also.  Similarly for optional arguments.\n\
  214 "),
  215          stdout);
  216       fputs (_("\
  217 \n\
  218 Listings:\n\
  219   -l, --list[=FORMAT]        list one or all known charsets and aliases\n\
  220   -k, --known=PAIRS          restrict charsets according to known PAIRS list\n\
  221   -h, --header[=[LN/]NAME]   write table NAME on stdout using LN, then exit\n\
  222   -T, --find-subsets         report all charsets being subset of others\n\
  223   -C, --copyright            display Copyright and copying conditions\n\
  224       --help                 display this help and exit\n\
  225       --version              output version information and exit\n\
  226 "),
  227          stdout);
  228       fputs (_("\
  229 \n\
  230 Operation modes:\n\
  231   -v, --verbose           explain sequence of steps and report progress\n\
  232   -q, --quiet, --silent   inhibit messages about irreversible recodings\n\
  233   -f, --force             force recodings even when not reversible\n\
  234   -t, --touch             touch the recoded files after replacement\n\
  235   -i, -p, --sequence=STRATEGY  ignored for backwards compatibility\n\
  236 "),
  237          stdout);
  238       fputs (_("\
  239 \n\
  240 Fine tuning:\n\
  241   -s, --strict           use strict mappings; discard untranslatable characters\n\
  242   -d, --diacritics       convert only diacritics and special characters for\n\
  243                          HTML/LaTeX/BibTeX\n\
  244   -S, --source[=LN]      limit recoding to strings and comments as for LN\n\
  245   -c, --colons           use colons instead of double quotes for diaeresis\n\
  246   -g, --graphics         approximate IBMPC rulers by ASCII graphics\n\
  247   -x, --ignore=CHARSET   ignore CHARSET while choosing a recoding path\n\
  248   -I, --prefer-iconv     use iconv if possible\n\
  249 "),
  250          stdout);
  251       fputs (_("\
  252 \n\
  253 Option -l with no FORMAT nor CHARSET list available charsets and surfaces.\n\
  254 FORMAT is `decimal', `octal', `hexadecimal' or `full' (or one of `dohf').\n\
  255 "),
  256          stdout);
  257       fputs (_("\
  258 Unless DEFAULT_CHARSET is set in environment, CHARSET defaults to the locale\n\
  259 dependent encoding, determined by LC_ALL, LC_CTYPE, LANG.\n\
  260 "),
  261            stdout);
  262       fputs (_("\
  263 With -k, possible before charsets are listed for the given after CHARSET,\n\
  264 both being tabular charsets, with PAIRS of the form `BEF1:AFT1,BEF2:AFT2,...'\n\
  265 and BEFs and AFTs being codes are given as decimal numbers.\n"),
  266           stdout);
  267       fputs (_("\
  268 LN is some language, it may be `c', `perl' or `po'; `c' is the default.\n"),
  269          stdout);
  270       fputs (_("\
  271 \n\
  272 REQUEST is SUBREQUEST[,SUBREQUEST]...; SUBREQUEST is ENCODING[..ENCODING]...\n\
  273 ENCODING is [CHARSET][/[SURFACE]]...; REQUEST often looks like BEFORE..AFTER,\n\
  274 with BEFORE and AFTER being charsets.  An omitted CHARSET implies the usual\n\
  275 charset; an omitted [/SURFACE]... means the implied surfaces for CHARSET; a /\n\
  276 with an empty surface name means no surfaces at all.  See the manual.\n"),
  277          stdout);
  278       fputs (_("\
  279 \n\
  280 Each FILE is recoded over itself, destroying the original.  If no\n\
  281 FILE is specified, then act as a filter and recode stdin to stdout.\n"),
  282          stdout);
  283       fputs (_("\
  284 \n\
  285 Report bugs at https://github.com/rrthomas/recode\n"),
  286            stdout);
  287     }
  288   exit (status);
  289 }
  290 
  291 /*----------------------------------------------------------------------.
  292 | Main program.  Decode ARGC arguments passed through the ARGV array of |
  293 | strings, then launch execution.                       |
  294 `----------------------------------------------------------------------*/
  295 
  296 /* Long options equivalences.  */
  297 static const struct option long_options[] =
  298 {
  299   {"colons", no_argument, NULL, 'c'},
  300   {"copyright", no_argument, NULL, 'C'},
  301   {"diacritics", no_argument, NULL, 'd'},
  302   {"find-subsets", no_argument, NULL, 'T'},
  303   {"force", no_argument, NULL, 'f'},
  304   {"header", optional_argument, NULL, 'h'},
  305   {"help", no_argument, &show_help, 1},
  306   {"ignore", required_argument, NULL, 'x'},
  307   {"known", required_argument, NULL, 'k'},
  308   {"list", optional_argument, NULL, 'l'},
  309   {"prefer-iconv", no_argument, NULL, 'I'},
  310   {"quiet", no_argument, NULL, 'q'},
  311   {"sequence", required_argument, NULL, '\n'},
  312   {"source", optional_argument, NULL, 'S'},
  313   {"silent", no_argument, NULL, 'q'},
  314   {"strict", no_argument, NULL, 's'},
  315   {"touch", no_argument, NULL, 't'},
  316   {"verbose", no_argument, NULL, 'v'},
  317   {"version", no_argument, &show_version, 1},
  318   {0, 0, 0, 0},
  319 };
  320 
  321 static const char *const format_strings[]
  322   = { "decimal", "octal", "hexadecimal", "full", NULL };
  323 
  324 static const char *const language_strings[]
  325   = { "c", "perl", "po", NULL };
  326 
  327 static const char *const sequence_strings[]
  328   = { "memory", "files", "pipe", NULL };
  329 
  330 
  331 static RECODE_OUTER
  332 new_outer(unsigned flags)
  333 {
  334   /* Register all modules and build internal tables.  */
  335 
  336   RECODE_OUTER outer =
  337     recode_new_outer (flags | RECODE_AUTO_ABORT_FLAG);
  338   if (!outer)
  339     abort ();
  340 
  341   /* If using strict mapping, remove fallbacks.  */
  342 
  343   if (outer->strict_mapping)
  344     for (RECODE_SINGLE single = outer->single_list;
  345          single;
  346          single = single->next)
  347       single->fallback_routine = NULL;
  348 
  349   /* Set the ignored charset.  */
  350 
  351   if (ignored_name)
  352     {
  353       RECODE_ALIAS alias
  354     = find_alias (outer, ignored_name, ALIAS_FIND_AS_CHARSET);
  355 
  356       if (!alias)
  357     {
  358       error (0, 0, _("Symbol `%s' is unknown"), ignored_name);
  359       usage (EXIT_FAILURE, 1);
  360     }
  361 
  362       alias->symbol->ignore = true;
  363     }
  364 
  365   return outer;
  366 }
  367 
  368 static RECODE_REQUEST
  369 new_request(RECODE_OUTER outer, struct recode_request *request_option)
  370 {
  371   RECODE_REQUEST request = recode_new_request (outer);
  372   request->ascii_graphics = request_option->ascii_graphics;
  373   request->diacritics_only = request_option->diacritics_only;
  374   request->diaeresis_char = request_option->diaeresis_char;
  375   request->make_header_flag = request_option->make_header_flag;
  376   request->verbose_flag = request_option->verbose_flag;
  377   return request;
  378 }
  379 
  380 int
  381 main (int argc, char *const *argv)
  382 {
  383   int option_char;      /* option character */
  384   bool success = true;      /* reversibility of all recodings */
  385 
  386   static bool (*processor) (RECODE_TASK);
  387   struct recode_outer outer_option;
  388   struct recode_request request_option;
  389   struct recode_task task_option;
  390 
  391   program_name = argv[0];
  392   /* libtool creates a temporary executable whose names is prefixed with
  393      "lt-".  Remove this prefix here.  */
  394   if (strncmp (program_name, "lt-", 3) == 0)
  395     program_name += 3;
  396 
  397   setlocale (LC_ALL, "");
  398   textdomain (PACKAGE);
  399 
  400   /* Decode command options.  */
  401 
  402   if (argc == 1)
  403     usage (EXIT_SUCCESS, 0);
  404 
  405   processor = recode_perform_task;
  406   memset (&outer_option, 0, sizeof (struct recode_outer));
  407   memset (&request_option, 0, sizeof (struct recode_request));
  408   memset (&task_option, 0, sizeof (struct recode_task));
  409 
  410   request_option.diaeresis_char = '"';
  411   task_option.fail_level = RECODE_AMBIGUOUS_OUTPUT;
  412   task_option.abort_level = RECODE_AMBIGUOUS_OUTPUT;
  413 
  414   while (option_char = getopt_long (argc, argv, "CIFS::Tcdfgh::ik:l::pqstvx:",
  415                     long_options, NULL),
  416      option_char != -1)
  417     switch (option_char)
  418       {
  419       default:
  420     usage (EXIT_FAILURE, 0);
  421 
  422       case NUL:
  423     break;
  424 
  425       case '\n':
  426     switch (argmatch (optarg, sequence_strings, NULL, 0))
  427       {
  428       case -2:
  429         error (0, 0, _("Sequence `%s' is ambiguous"), optarg);
  430         usage (EXIT_FAILURE, 0);
  431             break;
  432 
  433       case -1:
  434         error (0, 0, _("Sequence `%s' is unknown"), optarg);
  435         usage (EXIT_FAILURE, 0);
  436             break;
  437 
  438             /* Ignore for backwards compatibility with version 3.6.  */
  439       case 0:
  440       case 1:
  441       case 2:
  442         break;
  443 
  444           default:
  445             break;
  446       }
  447     break;
  448 
  449       case 'C':
  450     print_copyright ();
  451     exit (EXIT_SUCCESS);
  452 
  453       case 'I':
  454         prefer_iconv = true;
  455         break;
  456 
  457       case 'S':
  458     if (optarg)
  459       switch (argmatch (optarg, language_strings, NULL, 0))
  460         {
  461         case -2:
  462           error (0, 0, _("Language `%s' is ambiguous"), optarg);
  463           usage (EXIT_FAILURE, 0);
  464               break;
  465 
  466         default:        /* -1 */
  467           error (0, 0, _("Language `%s' is unknown"), optarg);
  468           usage (EXIT_FAILURE, 0);
  469               break;
  470 
  471         case 0:
  472           processor = transform_c_source;
  473           break;
  474 
  475         case 2:
  476           processor = transform_po_source;
  477           break;
  478         }
  479     else
  480       processor = transform_c_source;
  481     break;
  482 
  483       case 'T':
  484     find_subsets = true;
  485     break;
  486 
  487       case 'c':
  488     request_option.diaeresis_char = ':';
  489     break;
  490 
  491       case 'd':
  492     request_option.diacritics_only = true;
  493     break;
  494 
  495       case 'f':
  496     task_option.fail_level = RECODE_SYSTEM_ERROR;
  497     task_option.abort_level = RECODE_USER_ERROR;
  498         force_flag = true;
  499     break;
  500 
  501       case 'g':
  502     request_option.ascii_graphics = true;
  503     break;
  504 
  505       case 'h':
  506     request_option.make_header_flag = true;
  507     header_name = optarg ? strrchr (optarg, '/') : NULL;
  508     if (header_name)
  509       {
  510         char *buffer;
  511         unsigned counter;
  512 
  513         header_name++;
  514         buffer = (char *) xmalloc ((size_t) (header_name - optarg));
  515         if (*header_name == NUL)
  516           header_name = NULL;
  517         for (counter = 0; optarg[counter] != '/'; counter++)
  518           buffer[counter] = tolower (optarg[counter]);
  519         buffer[counter] = NUL;
  520         switch (argmatch (buffer, language_strings, NULL, 0))
  521           {
  522           case -2:
  523         error (0, 0, _("Language `%s' is ambiguous"), buffer);
  524         usage (EXIT_FAILURE, 0);
  525                 break;
  526 
  527           default:      /* -1 */
  528         error (0, 0, _("Language `%s' is unknown"), buffer);
  529         usage (EXIT_FAILURE, 0);
  530                 break;
  531 
  532           case 0:
  533         header_language = RECODE_LANGUAGE_C;
  534         break;
  535 
  536           case 1:
  537         header_language = RECODE_LANGUAGE_PERL;
  538         break;
  539           }
  540         free (buffer);
  541       }
  542     else
  543       {
  544         header_name = optarg;
  545         header_language = RECODE_LANGUAGE_C;
  546       }
  547     break;
  548 
  549       case 'i':
  550         /* Ignore for backwards compatibility with version 3.6.  */
  551     break;
  552 
  553       case 'k':
  554     charset_restrictions = optarg;
  555     break;
  556 
  557       case 'l':
  558     show_symbols = true;
  559     if (optarg)
  560       switch (argmatch (optarg, format_strings, NULL, 0))
  561         {
  562         case -2:
  563           error (0, 0, _("Format `%s' is ambiguous"), optarg);
  564           usage (EXIT_FAILURE, 0);
  565               break;
  566 
  567         case -1:
  568           error (0, 0, _("Format `%s' is unknown"), optarg);
  569           usage (EXIT_FAILURE, 0);
  570               break;
  571 
  572         case 0:
  573           list_format = RECODE_DECIMAL_FORMAT;
  574           break;
  575 
  576         case 1:
  577           list_format = RECODE_OCTAL_FORMAT;
  578           break;
  579 
  580         case 2:
  581           list_format = RECODE_HEXADECIMAL_FORMAT;
  582           break;
  583 
  584         case 3:
  585           list_format = RECODE_FULL_FORMAT;
  586           break;
  587 
  588             default:
  589               break;
  590         }
  591     break;
  592 
  593       case 'p':
  594         /* Ignore for backwards compatibility with version 3.6.  */
  595     break;
  596 
  597       case 'q':
  598     quiet_flag = true;
  599     break;
  600 
  601       case 's':
  602     strict_mapping = true;
  603     task_option.fail_level = RECODE_NOT_CANONICAL;
  604     task_option.abort_level = RECODE_NOT_CANONICAL;
  605     break;
  606 
  607       case 't':
  608     touch_option = true;
  609     break;
  610 
  611       case 'v':
  612     verbose_flag = true;
  613     request_option.verbose_flag = true;
  614     break;
  615 
  616       case 'x':
  617     ignored_name = optarg;
  618     break;
  619       }
  620 
  621   if (request_option.ascii_graphics)
  622     force_flag = true;
  623 
  624   /* Process trivial options.  */
  625 
  626   if (show_version)
  627     {
  628       printf ("%s %s\n", PACKAGE, VERSION);
  629       fputs (_("\
  630 Written by François Pinard <pinard@iro.umontreal.ca>.\n"),
  631          stdout);
  632       fputs (_("\
  633 \n\
  634 Copyright (C) 1990-2022 Free Software Foundation, Inc.\n"),
  635          stdout);
  636       fputs (_("\
  637 This is free software; see the source for copying conditions.  There is NO\n\
  638 warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"),
  639          stdout);
  640       exit (EXIT_SUCCESS);
  641     }
  642 
  643   if (show_help)
  644     usage (EXIT_SUCCESS, 0);
  645 
  646   /* Set up the outer level.  */
  647 
  648   unsigned flags = 0;
  649   if ((ignored_name && *ignored_name == ':')
  650       || request_option.make_header_flag)
  651     flags |= RECODE_NO_ICONV_FLAG;
  652   if (strict_mapping)
  653     flags |= RECODE_STRICT_MAPPING_FLAG;
  654   if (force_flag)
  655     flags |= RECODE_FORCE_FLAG;
  656   RECODE_OUTER outer = new_outer (flags);
  657 
  658   /* Process charset listing options.  */
  659 
  660   if (find_subsets)
  661     {
  662       find_and_report_subsets (outer);
  663       exit (EXIT_SUCCESS);
  664     }
  665 
  666   if (show_symbols || charset_restrictions)
  667     {
  668       if (charset_restrictions)
  669     if (!decode_known_pairs (outer, charset_restrictions))
  670       {
  671         error (0, 0, "Could not understand `%s'", charset_restrictions);
  672         usage (EXIT_FAILURE, 0);
  673 
  674       }
  675       if (optind + 1 < argc)
  676     {
  677       error (0, 0, "Argument `%s' is extraneous", argv[optind]);
  678       usage (EXIT_FAILURE, 0);
  679     }
  680 
  681       /* Select a possible charset and a default format.  */
  682 
  683       if (optind < argc)
  684     {
  685       RECODE_ALIAS alias
  686         = find_alias (outer, argv[optind], ALIAS_FIND_AS_CHARSET);
  687 
  688       if (!alias)
  689         {
  690           error (0, 0, _("Charset `%s' is unknown or ambiguous"),
  691              argv[optind]);
  692           usage (EXIT_FAILURE, 1);
  693         }
  694 
  695       list_charset = alias->symbol;
  696     }
  697       else if (list_format != RECODE_NO_FORMAT || charset_restrictions)
  698     {
  699       RECODE_ALIAS alias
  700         = find_alias (outer, NULL, ALIAS_FIND_AS_CHARSET);
  701 
  702       if (!alias)
  703         {
  704           error (0, 0, _("Charset `%s' is unknown or ambiguous"),
  705              argv[optind]);
  706           usage (EXIT_FAILURE, 1);
  707         }
  708 
  709       list_charset = alias->symbol;
  710     }
  711       else
  712     list_charset = NULL;
  713 
  714       /* List the charset(s) appropriately.  */
  715 
  716       if (charset_restrictions)
  717     list_all_symbols (outer, list_charset);
  718       else if (list_charset)
  719     if (list_format == RECODE_FULL_FORMAT)
  720       list_full_charset (outer, list_charset);
  721     else
  722       list_concise_charset (outer, list_charset, list_format);
  723       else
  724     list_all_symbols (outer, NULL);
  725 
  726       /* Then get out.  */
  727 
  728       exit (EXIT_SUCCESS);
  729     }
  730 
  731   /* Decode the REQUEST argument.  */
  732 
  733   if (optind + 1 > argc)
  734     {
  735       error (0, 0, _("Required argument is missing"));
  736       usage (EXIT_FAILURE, 0);
  737     }
  738 
  739   RECODE_REQUEST request = new_request (outer, &request_option);
  740 
  741   const char *user_request = argv[optind];
  742 
  743   if (!recode_scan_request (request, user_request))
  744     error (EXIT_FAILURE, 0, _("Request `%s' is erroneous"), user_request);
  745 
  746   /* If we merely want source code, do it and get out.  */
  747 
  748   if (request_option.make_header_flag)
  749     {
  750       recode_format_table (request, header_language, header_name);
  751       exit (EXIT_SUCCESS);
  752     }
  753 
  754   /* If we are recoding, and we are allowed to use iconv, and --prefer-iconv
  755      was not used, see if we can do it without iconv.  */
  756 
  757   if ((flags & RECODE_NO_ICONV_FLAG) == 0 && !prefer_iconv)
  758     {
  759       RECODE_OUTER no_iconv_outer = new_outer (RECODE_NO_ICONV_FLAG);
  760       RECODE_REQUEST no_iconv_request =
  761         new_request (no_iconv_outer, &request_option);
  762 
  763       if (recode_scan_request (no_iconv_request, user_request))
  764         {
  765           recode_delete_request (request);
  766           recode_delete_outer (outer);
  767           outer = no_iconv_outer;
  768           request = no_iconv_request;
  769         }
  770     }
  771 
  772   /* Discard the request argument.  */
  773   optind++;
  774 
  775 #if HAVE_PIPE
  776   setup_signals ();
  777 #endif
  778 
  779   {
  780     RECODE_TASK task;
  781 
  782     task = recode_new_task (request);
  783     task->fail_level = task_option.fail_level;
  784     task->abort_level = task_option.fail_level;
  785 
  786     /* If there is no input file, act as a filter.  Else, recode all files
  787        over themselves.  */
  788 
  789     if (optind < argc)
  790       {
  791     /* In case files are recoded over themselves and there is no
  792        recoding step at all, do not even try to touch the files.  */
  793 
  794     if (request->sequence_length > 0)
  795 
  796       /* Process files, one at a time.  */
  797 
  798       for (; optind < argc; optind++)
  799         {
  800           const char *input_name;
  801           FILE *file;
  802           struct stat file_stat;
  803           struct utimbuf file_utime;
  804 
  805           input_name = realpath (argv[optind], NULL);
  806           if (input_name == NULL)
  807         error (EXIT_FAILURE, errno, "realpath (%s)", argv[optind]);
  808 
  809           /* Check if the file can be read and rewritten.  */
  810 
  811           if (file = fopen (input_name, "r+"), file == NULL)
  812         error (EXIT_FAILURE, errno, "fopen (%s)", input_name);
  813 
  814           /* Save the input file attributes.  */
  815 
  816           fstat (fileno (file), &file_stat);
  817           fclose (file);
  818 
  819           /* Choose an output file in the same directory.  */
  820 
  821           char *input_name_copy = xstrdup (input_name);
  822           char *output_dir = dirname (input_name_copy);
  823           char *output_name;
  824           if (asprintf (&output_name, "%s/recode-XXXXXX.tmp", output_dir) == -1)
  825         error (EXIT_FAILURE, errno, "asprintf");
  826           int fd = mkstemps (output_name, 4);
  827           if (fd == -1)
  828         error (EXIT_FAILURE, errno, "mkstemps (%s)", output_name);
  829           xset_binary_mode (fd, O_BINARY);
  830 
  831           /* Recode the file.  */
  832 
  833           task->input.name = input_name;
  834           task->output.name = NULL;
  835           task->output.file = fdopen (fd, "w+");
  836           if (task->output.file == NULL)
  837             error (EXIT_FAILURE, errno, "fdopen ()");
  838 
  839           if (verbose_flag)
  840         {
  841           fprintf (stderr, _("Recoding %s..."), task->input.name);
  842           fflush (stderr);
  843         }
  844 
  845           if ((*processor) (task))
  846         {
  847           /* Recoding was successful.  */
  848 
  849           if (verbose_flag)
  850             {
  851               fprintf (stderr, _(" done\n"));
  852               fflush (stderr);
  853             }
  854 
  855                   /* Close the file. */
  856 
  857                   if (fclose(task->output.file) == EOF)
  858                     error (EXIT_FAILURE, errno, "close ()");
  859 
  860           /* Move the new file over the original.  */
  861 
  862           if (unlink (input_name) < 0)
  863             error (EXIT_FAILURE, errno, "unlink (%s)", input_name);
  864 
  865           /* Preserve the file permissions if possible.  */
  866 
  867           chmod (output_name, file_stat.st_mode & 07777);
  868 
  869           if (rename (output_name, input_name) < 0)
  870             error (EXIT_FAILURE, errno, "rename (%s, %s)",
  871                output_name, input_name);
  872 
  873           /* Adjust the time stamp for the new file.  */
  874 
  875           if (!touch_option)
  876             {
  877               file_utime.actime = file_stat.st_atime;
  878               file_utime.modtime = file_stat.st_mtime;
  879               utime (input_name, &file_utime);
  880             }
  881         }
  882           else
  883         {
  884           /* Recoding failed, discard output.  */
  885 
  886           success = false;
  887           if (verbose_flag)
  888             {
  889               fprintf (stderr, _(" failed: %s in step `%s..%s'\n"),
  890                    task_perror (task),
  891                    task->error_at_step->before->name,
  892                    task->error_at_step->after->name);
  893               fflush (stderr);
  894             }
  895           else if (!quiet_flag)
  896             error (0, 0, _("%s failed: %s in step `%s..%s'"),
  897                input_name, task_perror (task),
  898                task->error_at_step->before->name,
  899                task->error_at_step->after->name);
  900 
  901           unlink (output_name);
  902         }
  903           free (output_name);
  904           free (input_name_copy);
  905         }
  906       }
  907     else
  908       {
  909     task->input.name = "";
  910     task->output.name = "";
  911     if (!(*processor) (task))
  912       {
  913         success = false;
  914         if (!quiet_flag)
  915           error (0, 0, _("%s in step `%s..%s'"),
  916              task_perror (task),
  917              task->error_at_step->before->name,
  918              task->error_at_step->after->name);
  919       }
  920       }
  921   }
  922 
  923   /* Exit with an appropriate status.  */
  924 
  925   exit (success ? EXIT_SUCCESS : EXIT_FAILURE);
  926 }