1 /*
2 command line option processing
3
4 Copyright (C) 2000-2003 David Necas (Yeti) <yeti@physics.muni.cz>
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of version 2 of the GNU General Public License as published
8 by the Free Software Foundation.
9
10 This program is distributed in the hope that it will be useful, but WITHOUT
11 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
18 */
19 #include "common.h"
20
21 #ifdef HAVE_GETOPT_H
22 # include <getopt.h>
23 #else /* HAVE_GETOPT_H */
24 # include "getopt.h"
25 #endif /* HAVE_GETOPT_H */
26
27 #ifdef HAVE_WORDEXP
28 # ifdef HAVE_WORDEXP_H
29 # include <wordexp.h>
30 # else /* HAVE_WORDEXP_H */
31 /* don't declare all the stuff from wordexp.h when not present, it would
32 probably not work anyway---just don't use it */
33 # undef HAVE_WORDEXP
34 # endif /* HAVE_WORDEXP_H */
35 #endif /* HAVE_WORDEXP */
36
37 typedef void (* ReportFunc)(void);
38
39 /* Program behaviour: enca or enconv. */
40 typedef enum {
41 BEHAVE_ENCA,
42 BEHAVE_ENCONV
43 } ProgramBehaviour;
44
45 /* Settings. */
46 char *program_name = NULL;
47 static ProgramBehaviour behaviour = BEHAVE_ENCA;
48 Options options;
49
50 /* Environment variable containing default options. */
51 static const char *ENCA_ENV_VAR = "ENCAOPT";
52
53 /* Environment variable containing default target charset (think recode). */
54 static const char *RECODE_CHARSET_VAR = "DEFAULT_CHARSET";
55
56 /* Default option values. */
57 static const Options DEFAULTS = {
58 0, /* verbosity_level */
59 NULL, /* language */
60 OTYPE_HUMAN, /* output_type */
61 { ENCA_CS_UNKNOWN, 0 }, /* target_enc */
62 NULL, /* target_enc_str */
63 -1, /* prefix_filename */
64 };
65
66 extern const char *const COPYING_text[];
67 extern const char *const HELP_text[];
68
69 /* Version/copyright text. */
70 static const char *VERSION_TEXT = /* {{{ */
71 "Features: "
72 #ifdef HAVE_LIBRECODE
73 "+"
74 #else /* HAVE_LIBRECODE */
75 "-"
76 #endif /* HAVE_LIBRECODE */
77 "librecode-interface "
78
79 #ifdef HAVE_GOOD_ICONV
80 "+"
81 #else /* HAVE_GOOD_ICONV */
82 "-"
83 #endif /* HAVE_GOOD_ICONV */
84 "iconv-interface "
85
86 #ifdef ENABLE_EXTERNAL
87 "+"
88 #else /* ENABLE_EXTERNAL */
89 "-"
90 #endif /* ENABLE_EXTERNAL */
91 "external-converter "
92
93 #ifdef HAVE_SETLOCALE
94 "+"
95 #else /* HAVE_SETLOCALE */
96 "-"
97 #endif /* HAVE_SETLOCALE */
98 "language-detection "
99
100 #ifdef HAVE_LOCALE_ALIAS
101 "+"
102 #else /* HAVE_LOCALE_ALIAS */
103 "-"
104 #endif /* HAVE_LOCALE_ALIAS */
105 "locale-alias "
106
107 #ifdef HAVE_NL_LANGINFO
108 "+"
109 #else /* HAVE_NL_LANGINFO */
110 "-"
111 #endif /* HAVE_NL_LANGINFO */
112 "target-charset-auto "
113
114 #ifdef HAVE_WORDEXP
115 "+"
116 #else /* HAVE_WORDEXP */
117 "-"
118 #endif /* HAVE_WORDEXP */
119 "ENCAOPT ";
120 /* }}} */
121
122 static const char *COPYRIGHT_TEXT = /* {{{ */
123 "Copyright (C) 2000-2005 David Necas (Yeti) (<yeti@physics.muni.cz>),\n"
124 " 2005 Zuxy Meng (<zuxy.meng@gmail.com>).\n"
125 "\n"
126 PACKAGE_NAME
127 " is free software; it can be copied and/or modified under the terms of\n"
128 "version 2 of GNU General Public License, run `enca --license' to see the full\n"
129 "license text. There is NO WARRANTY; not even for MERCHANTABILITY or FITNESS\n"
130 "FOR A PARTICULAR PURPOSE.";
131 /* }}} */
132
133 /* Local prototypes. */
134 static char** interpret_opt (int argc,
135 char *argv[],
136 int cmdl_argc);
137 static int prepend_env (int argc,
138 char *argv[],
139 int *newargc,
140 char *(*newargv[]));
141 static OutputType optchar_to_otype (const char c);
142 static void set_otype_from_name (const char *otname);
143 static void set_program_behaviour (void);
144 static int parse_arg_x (const char *s);
145 static int add_parsed_converters (const char *list);
146 static void print_some_list (const char *listname);
147 static char** make_filelist (const int n,
148 char *argvrest[]);
149 static int prefix_filename (int pfx);
150 #ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME
151 # define program_invocation_short_name strip_path(argv[0])
152 static char* strip_path (const char *fullpath);
153 #endif /* not HAVE_PROGRAM_INVOCATION_SHORT_NAME */
154 static void print_version (void);
155 static void print_all_charsets (void);
156 static void print_builtin_charsets (void);
157 static void print_surfaces (void);
158 static void print_languages (void);
159 static void print_lists (void);
160 static void print_names (void);
161 static void print_charsets (int only_builtin);
162 static void print_text_and_exit (const char *const *text,
163 int exitcode);
164
165 /* merge all sources of options (ENCAOPT and command line arguments) and
166 process them
167 returns list of file to process (or NULL for stdin) */
168 char**
process_opt(const int argc,char * argv[])169 process_opt(const int argc, char *argv[])
170 {
171 int newargc;
172 char **newargv;
173 char **flist;
174
175 /* Assign defaults. */
176 options = DEFAULTS;
177
178 program_name = program_invocation_short_name;
179 set_program_behaviour();
180
181 #ifdef ENABLE_EXTERNAL
182 set_external_converter(DEFAULT_EXTERNAL_CONVERTER);
183 #endif /* ENABLE_EXTERNAL */
184
185 /* Prepend options in $ENCAOPT. */
186 prepend_env(argc, argv, &newargc, &newargv);
187
188 /* Interpret them. */
189 flist = interpret_opt(newargc, newargv, argc);
190
191 /* prefix result with file name iff we are about to process stdin or the
192 file list contains only one file and we don't print result */
193 if (prefix_filename(-1) == -1) {
194 if ((flist == NULL || flist[1] == NULL)
195 && options.output_type != OTYPE_DETAILS)
196 prefix_filename(0);
197 else
198 prefix_filename(1);
199 }
200
201 return flist;
202 }
203
204 /* process options, return file list (i.e. all remaining arguments)
205
206 FIXME: this function is infinitely ugly */
207 static char**
interpret_opt(int argc,char * argv[],int cmdl_argc)208 interpret_opt(int argc, char *argv[], int cmdl_argc)
209 {
210 /* Short command line options. */
211 static const char *short_options =
212 "cC:deE:fgGhil:L:mn:pPrsvVx:";
213
214 /* Long `GNU style' command line options {{{. */
215 static const struct option long_options[] = {
216 { "auto-convert", no_argument, NULL, 'c' },
217 { "convert-to", required_argument, NULL, 'x' },
218 { "cstocs-name", no_argument, NULL, 's' },
219 { "details", no_argument, NULL, 'd' },
220 { "enca-name", no_argument, NULL, 'e' },
221 { "external-converter-program", required_argument, NULL, 'E' },
222 { "guess", no_argument, NULL, 'g' },
223 { "help", no_argument, NULL, 'h' },
224 { "human-readable", no_argument, NULL, 'f' },
225 { "iconv-name", no_argument, NULL, 'i' },
226 { "language", required_argument, NULL, 'L' },
227 { "license", no_argument, NULL, 'G' },
228 { "list", required_argument, NULL, 'l' },
229 { "mime-name", no_argument, NULL, 'm' },
230 { "name", required_argument, NULL, 'n' },
231 { "no-filename", no_argument, NULL, 'P' },
232 { "rfc1345-name", no_argument, NULL, 'r' },
233 { "try-converters", required_argument, NULL, 'C' },
234 { "verbose", no_argument, NULL, 'V' },
235 { "version", no_argument, NULL, 'v' },
236 { "with-filename", no_argument, NULL, 'p' },
237 { NULL, 0, NULL, 0 }
238 };
239 /* }}} */
240
241 int c;
242 char **filelist;
243 int otype_set = 0; /* Whether output type was explicitely set. */
244
245 /* Process options. */
246 opterr = 0; /* Getopt() shouldn't print errors, we do it ourself. */
247 while ((c = getopt_long(argc, argv, short_options,
248 long_options, NULL)) != -1) {
249 switch (c) {
250 case '?': /* Unknown option. */
251 fprintf(stderr, "%s: Unknown option -%c%s.\n"
252 "Run `%s --help' to get brief help.\n",
253 program_name, optopt,
254 optopt == '\0' ? " or misspelt/ambiguous long option"
255 : "",
256 program_name);
257 exit(EXIT_TROUBLE);
258 break;
259
260 case ':': /* Missing paramter. */
261 fprintf(stderr, "%s: Option -%c requires an argument.\n"
262 "Run `%s --help' to get brief help.\n",
263 program_name, optopt, program_name);
264 exit(EXIT_TROUBLE);
265 break;
266
267 case 'h': /* Help (and exit). */
268 print_text_and_exit(HELP_text, EXIT_SUCCESS);
269 break;
270
271 case 'v': /* Version (and exit). */
272 print_version();
273 exit(EXIT_SUCCESS);
274 break;
275
276 case 'G': /* License (and exit). */
277 print_text_and_exit(COPYING_text, EXIT_SUCCESS);
278 break;
279
280 case 'l': /* Print required list (and exit). */
281 print_some_list(optarg);
282 exit(EXIT_SUCCESS);
283 break;
284
285 case 'd': /* Detailed output. */
286 case 'e': /* Canonical name. */
287 case 'f': /* Full (descriptive) output. */
288 case 'i': /* Iconv name. */
289 case 'm': /* MIME name. */
290 case 'r': /* RFC 1345 name as output. */
291 case 's': /* Cstocs name as output. */
292 options.output_type = optchar_to_otype(c);
293 otype_set = 1;
294 break;
295
296 case 'n': /* Output type by name. */
297 set_otype_from_name(optarg);
298 otype_set = 1;
299 break;
300
301 case 'p': /* Prefix filename on. */
302 case 'P': /* Prefix filename off. */
303 prefix_filename(islower(c));
304 break;
305
306 case 'g': /* Behave enca. */
307 behaviour = BEHAVE_ENCA;
308 break;
309
310 case 'c': /* Behave enconv. */
311 behaviour = BEHAVE_ENCONV;
312 break;
313
314 case 'V': /* Increase verbosity level. */
315 options.verbosity_level++;
316 break;
317
318 case 'x': /* Convert to. */
319 options.output_type = OTYPE_CONVERT;
320 parse_arg_x(optarg);
321 otype_set = 1;
322 break;
323
324 case 'L': /* Language. */
325 options.language = optarg;
326 break;
327
328 case 'C': /* Add converters to converter list. */
329 add_parsed_converters(optarg);
330 break;
331
332 case 'E': /* Converter name. */
333 #ifdef ENABLE_EXTERNAL
334 set_external_converter(optarg);
335 #else /* ENABLE_EXTERNAL */
336 fprintf(stderr, "%s: Cannot set external converter.\n"
337 "Enca was built without support "
338 "for external converters.\n",
339 program_name);
340 #endif /* ENABLE_EXTERNAL */
341 break;
342
343 default:
344 abort();
345 break;
346 }
347 }
348
349 /* Set and initialize language. */
350 options.language = detect_lang(options.language);
351 if (options.language == NULL) {
352 fprintf(stderr, "%s: Cannot determine (or understand) "
353 "your language preferences.\n"
354 "Please use `-L language', or `-L none' if your language is not supported\n"
355 "(only a few multibyte encodings can be recognized then).\n"
356 "Run `%s --list languages' to get a list of supported languages.\n",
357 program_name, program_name);
358 exit(EXIT_TROUBLE);
359 }
360
361 /* Behaviour. */
362 /* With an explicit output type doesn't matter how we were called. */
363 if (otype_set) {
364 behaviour = BEHAVE_ENCA;
365 if (options.output_type == OTYPE_CONVERT
366 && options.verbosity_level > 2)
367 fprintf(stderr, "Explicitly specified target charset: %s\n",
368 options.target_enc_str);
369 }
370
371 switch (behaviour) {
372 case BEHAVE_ENCA:
373 /* Nothing special here. */
374 break;
375
376 case BEHAVE_ENCONV:
377 {
378 const char *charset;
379
380 /* Try recode's default target charset. */
381 charset = getenv(RECODE_CHARSET_VAR);
382 if (charset != NULL) {
383 if (options.verbosity_level > 2)
384 fprintf(stderr, "Inherited recode's %s target charset: %s\n",
385 RECODE_CHARSET_VAR, charset);
386 }
387 else {
388 /* Then locale native charset. */
389 charset = get_lang_codeset();
390 assert(charset != NULL);
391 }
392
393 parse_arg_x(charset);
394 }
395 if (options.target_enc_str[0] == '\0') {
396 fprintf(stderr, "%s: Cannot detect native charset for locale %s.\n"
397 "You have to use the `-x' option "
398 "or the %s environment variable "
399 "to set the target encoding manually.\n",
400 program_name,
401 options.language,
402 RECODE_CHARSET_VAR);
403 exit(EXIT_TROUBLE);
404 }
405 options.output_type = OTYPE_CONVERT;
406 break;
407
408 default:
409 abort();
410 break;
411 }
412
413 /* Set up default list of converters. */
414 if (add_parsed_converters(NULL) == 0)
415 add_parsed_converters(DEFAULT_CONVERTER_LIST);
416
417 /* Create file list from remaining options. */
418 filelist = make_filelist(argc-optind, argv+optind);
419 /* When run without any arguments and input is a tty, print help. */
420 if (filelist == NULL && enca_isatty(STDIN_FILENO) && cmdl_argc == 1)
421 print_text_and_exit(HELP_text, EXIT_SUCCESS);
422
423 #ifdef ENABLE_EXTERNAL
424 if (options.output_type == OTYPE_CONVERT
425 && external_converter_listed()
426 && !check_external_converter())
427 exit(EXIT_TROUBLE);
428 #endif
429
430 return filelist;
431 }
432
433 /* prepend parsed contents of environment variable containing default options
434 (ENCAOPT) before command line arguments (but after argv[0]) and return the
435 new list of arguments in newargv (its length is newargc) */
436 static int
prepend_env(int argc,char * argv[],int * newargc,char * (* newargv[]))437 prepend_env(int argc,
438 char *argv[],
439 int *newargc,
440 char *(*newargv[]))
441 #ifdef HAVE_WORDEXP
442 {
443 char *msg;
444 char *encaenv;
445 wordexp_t encaenv_parsed;
446 size_t i;
447
448 *newargc = argc;
449 *newargv = argv;
450 /* Fetch value of ENCA_ENV_VAR, if set. */
451 encaenv = getenv(ENCA_ENV_VAR);
452 if (encaenv == NULL)
453 return 0;
454
455 /* Parse encaenv. */
456 if ((i = wordexp(encaenv, &encaenv_parsed, WRDE_NOCMD)) != 0) {
457 switch (i) {
458 case WRDE_NOSPACE:
459 wordfree(&encaenv_parsed);
460 fprintf(stderr, "%s: Cannot allocate memory.\n",
461 program_name);
462 exit(EXIT_TROUBLE);
463 break;
464
465 case WRDE_BADCHAR:
466 msg = "invalid characters";
467 break;
468
469 case WRDE_CMDSUB:
470 msg = "command substitution is disabled";
471 break;
472
473 case WRDE_SYNTAX:
474 msg = "syntax error";
475 break;
476
477 default:
478 msg = NULL;
479 break;
480 }
481 fprintf(stderr, "%s: Cannot parse value of %s (",
482 program_name, ENCA_ENV_VAR);
483 if (msg == NULL)
484 fprintf(stderr, "error %zd", i);
485 else
486 fprintf(stderr, "%s", msg);
487
488 fputs("), ignoring it\n", stderr);
489
490 return 1;
491 }
492
493 /* create newargv starting from argv[0], then encaenv_parsed, and last rest
494 of argv; note we copy addresses, not strings themselves from argv */
495 *newargc = argc + encaenv_parsed.we_wordc;
496 *newargv = (char**)enca_malloc((*newargc)*sizeof(char*));
497 (*newargv)[0] = argv[0];
498
499 for (i = 0; i < encaenv_parsed.we_wordc; i++)
500 (*newargv)[i+1] = enca_strdup(encaenv_parsed.we_wordv[i]);
501
502 for (i = 1; i < (size_t)argc; i++)
503 (*newargv)[i + encaenv_parsed.we_wordc] = argv[i];
504
505 /* Free memory. */
506 wordfree(&encaenv_parsed);
507
508 return 0;
509 }
510 #else /* HAVE_WORDEXP */
511 {
512 char *encaenv;
513 size_t nitems;
514 size_t i, state;
515 const char *p;
516
517 *newargc = argc;
518 *newargv = argv;
519 /* Fetch value of ENCA_ENV_VAR, if set. */
520 encaenv = getenv(ENCA_ENV_VAR);
521 if (encaenv == NULL)
522 return 0;
523
524 /* Count the number of tokens in ENCA_ENV_VAR. */
525 encaenv = enca_strdup(encaenv);
526 nitems = 0;
527 state = 0;
528 for (i = 0; encaenv[i] != '\0'; i++) {
529 if (state == 0) {
530 if (!isspace(encaenv[i]))
531 nitems += ++state;
532 }
533 else {
534 if (isspace(encaenv[i])) {
535 encaenv[i] = '\0';
536 state = 0;
537 }
538 }
539 }
540
541 /* Extend argv[]. (see above) */
542 *newargc = argc + nitems;
543 *newargv = (char**)enca_malloc((*newargc)*sizeof(char*));
544 (*newargv)[0] = argv[0];
545
546 p = encaenv;
547 for (i = 0; i < nitems; i++) {
548 while (isspace(*p))
549 p++;
550
551 (*newargv)[i+1] = enca_strdup(p);
552
553 while (*p != '\0')
554 p++;
555 p++;
556 }
557 enca_free(encaenv);
558
559 for (i = 1; i < argc; i++)
560 (*newargv)[i + nitems] = argv[i];
561
562 return 0;
563 }
564 #endif /* HAVE_WORDEXP */
565
566 /* Return output type appropriate for given option character. */
567 static OutputType
optchar_to_otype(const char c)568 optchar_to_otype(const char c)
569 {
570 switch (c) {
571 case 'd': return OTYPE_DETAILS; /* Detailed output. */
572 case 'e': return OTYPE_CANON; /* Enca's name. */
573 case 'f': return OTYPE_HUMAN; /* Full (descriptive) output. */
574 case 'i': return OTYPE_ICONV; /* Iconv name. */
575 case 'r': return OTYPE_RFC1345; /* RFC 1345 name as output */
576 case 's': return OTYPE_CS2CS; /* Cstocs name as output. */
577 case 'm': return OTYPE_MIME; /* Preferred MIME name as output. */
578 }
579
580 abort();
581 return 0;
582 }
583
584 /* if otname represents a valid output type name, assign it to *otype,
585 otherwise do nothing
586 when gets NULL as the name, prints list of valid names instead */
587 static void
set_otype_from_name(const char * otname)588 set_otype_from_name(const char *otname)
589 {
590 /* Abbreviations table stores pointers, we need something to point to. */
591 static const OutputType OTS[] = {
592 OTYPE_DETAILS,
593 OTYPE_CANON,
594 OTYPE_HUMAN,
595 OTYPE_RFC1345,
596 OTYPE_ICONV,
597 OTYPE_CS2CS,
598 OTYPE_MIME,
599 OTYPE_ALIASES
600 };
601
602 /* Output type names. */
603 static const Abbreviation OTNAMES[] =
604 {
605 { "aliases", OTS+7 },
606 { "cstocs", OTS+4 },
607 { "details", OTS },
608 { "enca", OTS+1 },
609 { "human-readable", OTS+2 },
610 { "iconv", OTS+5 },
611 { "mime", OTS+6 },
612 { "rfc1345", OTS+3 },
613 };
614
615 const Abbreviation *p;
616
617 p = expand_abbreviation(otname, OTNAMES,
618 sizeof(OTNAMES)/sizeof(Abbreviation),
619 "output type");
620 if (p != NULL)
621 options.output_type = *(OutputType*)p->data;
622 }
623
624 /* parse -x argument, assign output encoding */
625 static int
parse_arg_x(const char * s)626 parse_arg_x(const char *s)
627 {
628 /* Encoding separator for -x argument. */
629 static const char XENC_SEPARATOR[] = "..";
630 static const size_t XENC_SEPARATOR_LEN = sizeof(XENC_SEPARATOR);
631
632 /* Strip leading `..' if present. */
633 if (strncmp(s, XENC_SEPARATOR, XENC_SEPARATOR_LEN) == 0)
634 s += XENC_SEPARATOR_LEN;
635
636 /* Assign target encoding. */
637 enca_free(options.target_enc_str);
638 options.target_enc_str = enca_strdup(s);
639
640 /* We have to check for `..CHARSET/SURFACE..CHARSET2/SURFACE2' which would
641 * enca_parse_encoding_name() split as
642 * charset = CHARSET
643 * surfaces = SURFACE..CHARSET2, SURFACE2
644 * which is aboviously not what we want. */
645 if (enca_strstr(s, XENC_SEPARATOR) == NULL)
646 options.target_enc = enca_parse_encoding_name(s);
647 else {
648 options.target_enc.charset = ENCA_CS_UNKNOWN;
649 options.target_enc.surface = 0;
650 }
651
652 return 0;
653 }
654
655 /* add comma separated list of converters to list of converters
656 returns zero on success, nonzero otherwise
657 when list is NULL return number of successfully added converters instead */
658 static int
add_parsed_converters(const char * list)659 add_parsed_converters(const char *list)
660 {
661 /* Converter list separator for -E argument. */
662 static const char CONVERTER_SEPARATOR = ',';
663
664 char *s;
665 char *p_c,*p_c1;
666 static int nc = 0;
667
668 if (list == NULL)
669 return nc;
670
671 s = enca_strdup(list);
672 /* Add converter names one by one. */
673 p_c = s;
674 while ((p_c1 = strchr(p_c, CONVERTER_SEPARATOR)) != NULL) {
675 *p_c1++ = '\0';
676 if (add_converter(p_c) == 0) nc++;
677 p_c = p_c1;
678 }
679 if (add_converter(p_c) == 0) nc++;
680 enca_free(s);
681
682 return 0;
683 }
684
685 /* create NULL-terminated file list from remaining fields in argv[]
686 and return it */
687 static char**
make_filelist(const int n,char * argvrest[])688 make_filelist(const int n, char *argvrest[])
689 {
690 int i;
691 char **flist = NULL;
692
693 /* Accept `-' as stdin. */
694 if (n == 0
695 || (n == 1 && strcmp(argvrest[0], "-") == 0))
696 return NULL;
697
698 flist = (char**)enca_malloc((n+1)*sizeof(char*));
699 for (i = 0; i < n; i++) flist[i] = enca_strdup(argvrest[i]);
700 flist[n] = NULL;
701
702 return flist;
703 }
704
705 static int
prefix_filename(int pfx)706 prefix_filename(int pfx) {
707 if (pfx != -1)
708 options.prefix_filename = pfx;
709 return options.prefix_filename;
710 }
711
712 /* prints some list user asked for (--list) -- just calls appropriate
713 * functions from appropriate module (with funny abbreviation expansion)
714 * when listname is NULL prints list of available lists instead */
715 static void
print_some_list(const char * listname)716 print_some_list(const char *listname)
717 {
718 /* ISO C forbids initialization between function pointers and void*
719 so we use one more level of indirection to comply (and hope gracious
720 complier will forgive us our sins, amen) */
721 static const ReportFunc printer_bics = print_builtin_charsets;
722 static const ReportFunc printer_conv = print_converter_list;
723 static const ReportFunc printer_char = print_all_charsets;
724 static const ReportFunc printer_lang = print_languages;
725 static const ReportFunc printer_list = print_lists;
726 static const ReportFunc printer_name = print_names;
727 static const ReportFunc printer_surf = print_surfaces;
728
729 /* List names and pointers to pointers to list-printers. */
730 static const Abbreviation LISTS[] = {
731 { "built-in-charsets", &printer_bics },
732 { "converters", &printer_conv },
733 { "charsets", &printer_char },
734 { "languages", &printer_lang },
735 { "lists", &printer_list },
736 { "names", &printer_name },
737 { "surfaces", &printer_surf },
738 };
739
740 const Abbreviation *p = NULL;
741 ReportFunc list_printer; /* Pointer to list printing functions. */
742
743 /* Get the abbreviation data. */
744 p = expand_abbreviation(listname, LISTS,
745 sizeof(LISTS)/sizeof(Abbreviation),
746 "list");
747
748 /* p can be NULL in weird situations, e.g. when print_some_list() was
749 * called recursively by itself through print_lists.
750 * In all cases, return. */
751 if (p == NULL)
752 return;
753
754 list_printer = *(ReportFunc*)p->data;
755 list_printer();
756 }
757
758 #ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME
759 /* Create and return string containing only last component of path fullpath. */
760 static char*
strip_path(const char * fullpath)761 strip_path(const char *fullpath)
762 {
763 char *p;
764
765 p = strrchr(fullpath, '/');
766 if (p == NULL)
767 p = (char*)fullpath;
768 else
769 p++;
770
771 return enca_strdup(p);
772 }
773 #endif
774
775 /* Print version information. */
776 static void
print_version(void)777 print_version(void)
778 {
779 printf("%s %s\n\n%s\n\n%s\n", PACKAGE_TARNAME, PACKAGE_VERSION, VERSION_TEXT, COPYRIGHT_TEXT);
780 }
781
782 /**
783 * Prints builtin charsets.
784 * Must be of type ReportFunc.
785 **/
786 static void
print_builtin_charsets(void)787 print_builtin_charsets(void)
788 {
789 print_charsets(1);
790 }
791
792 /**
793 * Prints all charsets.
794 * Must be of type ReportFunc.
795 **/
796 static void
print_all_charsets(void)797 print_all_charsets(void)
798 {
799 print_charsets(0);
800 }
801
802 /**
803 * Prints list of charsets using name style from options.output_type.
804 *
805 * It prints all charsets, except:
806 * - charsets without given name, and
807 * - charsets without UCS-2 map when only_builtin is set.
808 **/
809 static void
print_charsets(int only_builtin)810 print_charsets(int only_builtin)
811 {
812 size_t ncharsets, i;
813
814 ncharsets = enca_number_of_charsets();
815 for (i = 0; i < ncharsets; i++) {
816 if (only_builtin && !enca_charset_has_ucs2_map(i))
817 continue;
818
819 switch (options.output_type) {
820 case OTYPE_ALIASES:
821 print_aliases(i);
822 break;
823
824 case OTYPE_CANON:
825 case OTYPE_CONVERT:
826 puts(enca_charset_name(i, ENCA_NAME_STYLE_ENCA));
827 break;
828
829 case OTYPE_HUMAN:
830 case OTYPE_DETAILS:
831 puts(enca_charset_name(i, ENCA_NAME_STYLE_HUMAN));
832 break;
833
834 case OTYPE_RFC1345:
835 puts(enca_charset_name(i, ENCA_NAME_STYLE_RFC1345));
836 break;
837
838 case OTYPE_CS2CS:
839 if (enca_charset_name(i, ENCA_NAME_STYLE_CSTOCS) != NULL)
840 puts(enca_charset_name(i, ENCA_NAME_STYLE_CSTOCS));
841 break;
842
843 case OTYPE_ICONV:
844 if (enca_charset_name(i, ENCA_NAME_STYLE_ICONV) != NULL)
845 puts(enca_charset_name(i, ENCA_NAME_STYLE_ICONV));
846 break;
847
848 case OTYPE_MIME:
849 if (enca_charset_name(i, ENCA_NAME_STYLE_MIME) != NULL)
850 puts(enca_charset_name(i, ENCA_NAME_STYLE_MIME));
851 break;
852
853 default:
854 abort();
855 break;
856 }
857 }
858 }
859
860 /**
861 * Prints all aliases of given charset.
862 **/
863 void
print_aliases(size_t cs)864 print_aliases(size_t cs)
865 {
866 size_t i, na;
867 const char **aliases = enca_get_charset_aliases(cs, &na);
868
869 for (i = 0; i < na; i++)
870 printf("%s ", aliases[i]);
871
872 putchar('\n');
873 enca_free(aliases);
874 }
875
876 /**
877 * Prints all [public] surfaces.
878 * Must be of type ReportFunc.
879 **/
880 static void
print_surfaces(void)881 print_surfaces(void)
882 {
883 EncaNameStyle ns;
884 char *s;
885 unsigned int i;
886
887 /* Only these two know surfaces. */
888 if (options.output_type == OTYPE_HUMAN)
889 ns = ENCA_NAME_STYLE_HUMAN;
890 else
891 ns = ENCA_NAME_STYLE_ENCA;
892
893 for (i = 1; i != 0; i <<= 1) {
894 s = enca_get_surface_name(i, ns);
895 if (s != NULL && s[0] != '\0') {
896 fputs(s, stdout);
897 if (ns == ENCA_NAME_STYLE_ENCA)
898 putchar('\n');
899 enca_free(s);
900 }
901 }
902 }
903
904 /* Magically print the list of lists. */
905 static void
print_lists(void)906 print_lists(void)
907 {
908 print_some_list(NULL);
909 }
910
911 /**
912 * Prints all languages list of charsets of each.
913 * Must be of type ReportFunc.
914 * Quite illogically affected by options.output_type: it changes *language*
915 * name style, instead of charset name style.
916 **/
917 static void
print_languages(void)918 print_languages(void)
919 {
920 size_t nl, nc, i, j, maxlen;
921 const char **l;
922 int *c;
923 int english;
924
925 l = enca_get_languages(&nl);
926
927 english = options.output_type == OTYPE_HUMAN
928 || options.output_type == OTYPE_DETAILS;
929 /* Find max. language name length for English. */
930 maxlen = 0;
931 if (english) {
932 for (i = 0; i < nl; i++) {
933 j = strlen(enca_language_english_name(l[i]));
934 if (j > maxlen)
935 maxlen = j;
936 }
937 }
938
939 /* Print the names. */
940 for (i = 0; i < nl; i++) {
941 if (english)
942 printf("%*s:", (int)maxlen, enca_language_english_name(l[i]));
943 else
944 printf("%s:", l[i]);
945 c = enca_get_language_charsets(l[i], &nc);
946 for (j = 0; j < nc; j++)
947 printf(" %s", enca_charset_name(c[j], ENCA_NAME_STYLE_ENCA));
948
949 putchar('\n');
950 enca_free(c);
951 }
952 enca_free(l);
953 }
954
955 /**
956 * Prints list of all encoding name styles.
957 * Must be of type ReportFunc.
958 **/
959 static void
print_names(void)960 print_names(void)
961 {
962 set_otype_from_name(NULL);
963 }
964
965 /**
966 * Print some text (help, copying, ...) and exit with given code.
967 **/
968 static void
print_text_and_exit(const char * const * text,int exitcode)969 print_text_and_exit(const char *const *text, int exitcode)
970 {
971 assert(text);
972
973 for (; *text; text++)
974 puts(*text);
975
976 exit(exitcode);
977 }
978
979 /**
980 * set_program_behaviour:
981 *
982 * Sets behaviour according to name we were called.
983 **/
984 static void
set_program_behaviour(void)985 set_program_behaviour(void)
986 {
987 static const char enca_name[] = "enca";
988 static const char enconv_name[] = "enconv";
989 static const size_t nenca = sizeof(enca_name) - 1;
990 static const size_t nenconv = sizeof(enconv_name) - 1;
991
992 if (strncmp(program_name, enca_name, nenca) == 0
993 && !isalpha(program_name[nenca])) {
994 behaviour = BEHAVE_ENCA;
995 return;
996 }
997
998 if (strncmp(program_name, enconv_name, nenconv) == 0
999 && !isalpha(program_name[nenconv])) {
1000 behaviour = BEHAVE_ENCONV;
1001 return;
1002 }
1003 }
1004
1005 /* vim: ts=2
1006 */
1007