nexmon – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 office 1 /* Substitution of environment variables in shell format strings.
2 Copyright (C) 2003-2007, 2012, 2015-2016 Free Software Foundation, Inc.
3 Written by Bruno Haible <bruno@clisp.org>, 2003.
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 3 of the License, or
8 (at your option) any later version.
9  
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU 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, see <http://www.gnu.org/licenses/>. */
17  
18 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21  
22 #include <errno.h>
23 #include <getopt.h>
24 #include <stdbool.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <locale.h>
29  
30 #include "closeout.h"
31 #include "error.h"
32 #include "progname.h"
33 #include "relocatable.h"
34 #include "basename.h"
35 #include "xalloc.h"
36 #include "propername.h"
37 #include "gettext.h"
38  
39 #define _(str) gettext (str)
40  
41 /* If true, substitution shall be performed on all variables. */
42 static bool all_variables;
43  
44 /* Long options. */
45 static const struct option long_options[] =
46 {
47 { "help", no_argument, NULL, 'h' },
48 { "variables", no_argument, NULL, 'v' },
49 { "version", no_argument, NULL, 'V' },
50 { NULL, 0, NULL, 0 }
51 };
52  
53 /* Forward declaration of local functions. */
54 static void usage (int status)
55 #if defined __GNUC__ && ((__GNUC__ == 2 && __GNUC_MINOR__ >= 5) || __GNUC__ > 2)
56 __attribute__ ((noreturn))
57 #endif
58 ;
59 static void print_variables (const char *string);
60 static void note_variables (const char *string);
61 static void subst_from_stdin (void);
62  
63 int
64 main (int argc, char *argv[])
65 {
66 /* Default values for command line options. */
67 bool show_variables = false;
68 bool do_help = false;
69 bool do_version = false;
70  
71 int opt;
72  
73 /* Set program name for message texts. */
74 set_program_name (argv[0]);
75  
76 #ifdef HAVE_SETLOCALE
77 /* Set locale via LC_ALL. */
78 setlocale (LC_ALL, "");
79 #endif
80  
81 /* Set the text message domain. */
82 bindtextdomain (PACKAGE, relocate (LOCALEDIR));
83 textdomain (PACKAGE);
84  
85 /* Ensure that write errors on stdout are detected. */
86 atexit (close_stdout);
87  
88 /* Parse command line options. */
89 while ((opt = getopt_long (argc, argv, "hvV", long_options, NULL)) != EOF)
90 switch (opt)
91 {
92 case '\0': /* Long option. */
93 break;
94 case 'h':
95 do_help = true;
96 break;
97 case 'v':
98 show_variables = true;
99 break;
100 case 'V':
101 do_version = true;
102 break;
103 default:
104 usage (EXIT_FAILURE);
105 }
106  
107 /* Version information is requested. */
108 if (do_version)
109 {
110 printf ("%s (GNU %s) %s\n", basename (program_name), PACKAGE, VERSION);
111 /* xgettext: no-wrap */
112 printf (_("Copyright (C) %s Free Software Foundation, Inc.\n\
113 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\n\
114 This is free software: you are free to change and redistribute it.\n\
115 There is NO WARRANTY, to the extent permitted by law.\n\
116 "),
117 "2003-2007");
118 printf (_("Written by %s.\n"), proper_name ("Bruno Haible"));
119 exit (EXIT_SUCCESS);
120 }
121  
122 /* Help is requested. */
123 if (do_help)
124 usage (EXIT_SUCCESS);
125  
126 if (argc - optind > 1)
127 error (EXIT_FAILURE, 0, _("too many arguments"));
128  
129 /* Distinguish the two main operation modes. */
130 if (show_variables)
131 {
132 /* Output only the variables. */
133 switch (argc - optind)
134 {
135 case 1:
136 break;
137 case 0:
138 error (EXIT_FAILURE, 0, _("missing arguments"));
139 default:
140 abort ();
141 }
142 print_variables (argv[optind++]);
143 }
144 else
145 {
146 /* Actually perform the substitutions. */
147 switch (argc - optind)
148 {
149 case 1:
150 all_variables = false;
151 note_variables (argv[optind++]);
152 break;
153 case 0:
154 all_variables = true;
155 break;
156 default:
157 abort ();
158 }
159 subst_from_stdin ();
160 }
161  
162 exit (EXIT_SUCCESS);
163 }
164  
165  
166 /* Display usage information and exit. */
167 static void
168 usage (int status)
169 {
170 if (status != EXIT_SUCCESS)
171 fprintf (stderr, _("Try '%s --help' for more information.\n"),
172 program_name);
173 else
174 {
175 /* xgettext: no-wrap */
176 printf (_("\
177 Usage: %s [OPTION] [SHELL-FORMAT]\n\
178 "), program_name);
179 printf ("\n");
180 /* xgettext: no-wrap */
181 printf (_("\
182 Substitutes the values of environment variables.\n"));
183 printf ("\n");
184 /* xgettext: no-wrap */
185 printf (_("\
186 Operation mode:\n"));
187 /* xgettext: no-wrap */
188 printf (_("\
189 -v, --variables output the variables occurring in SHELL-FORMAT\n"));
190 printf ("\n");
191 /* xgettext: no-wrap */
192 printf (_("\
193 Informative output:\n"));
194 /* xgettext: no-wrap */
195 printf (_("\
196 -h, --help display this help and exit\n"));
197 /* xgettext: no-wrap */
198 printf (_("\
199 -V, --version output version information and exit\n"));
200 printf ("\n");
201 /* xgettext: no-wrap */
202 printf (_("\
203 In normal operation mode, standard input is copied to standard output,\n\
204 with references to environment variables of the form $VARIABLE or ${VARIABLE}\n\
205 being replaced with the corresponding values. If a SHELL-FORMAT is given,\n\
206 only those environment variables that are referenced in SHELL-FORMAT are\n\
207 substituted; otherwise all environment variables references occurring in\n\
208 standard input are substituted.\n"));
209 printf ("\n");
210 /* xgettext: no-wrap */
211 printf (_("\
212 When --variables is used, standard input is ignored, and the output consists\n\
213 of the environment variables that are referenced in SHELL-FORMAT, one per line.\n"));
214 printf ("\n");
215 /* TRANSLATORS: The placeholder indicates the bug-reporting address
216 for this package. Please add _another line_ saying
217 "Report translation bugs to <...>\n" with the address for translation
218 bugs (typically your translation team's web or email address). */
219 fputs (_("Report bugs to <bug-gnu-gettext@gnu.org>.\n"), stdout);
220 }
221  
222 exit (status);
223 }
224  
225  
226 /* Parse the string and invoke the callback each time a $VARIABLE or
227 ${VARIABLE} construct is seen, where VARIABLE is a nonempty sequence
228 of ASCII alphanumeric/underscore characters, starting with an ASCII
229 alphabetic/underscore character.
230 We allow only ASCII characters, to avoid dependencies w.r.t. the current
231 encoding: While "${\xe0}" looks like a variable access in ISO-8859-1
232 encoding, it doesn't look like one in the BIG5, BIG5-HKSCS, GBK, GB18030,
233 SHIFT_JIS, JOHAB encodings, because \xe0\x7d is a single character in these
234 encodings. */
235 static void
236 find_variables (const char *string,
237 void (*callback) (const char *var_ptr, size_t var_len))
238 {
239 for (; *string != '\0';)
240 if (*string++ == '$')
241 {
242 const char *variable_start;
243 const char *variable_end;
244 bool valid;
245 char c;
246  
247 if (*string == '{')
248 string++;
249  
250 variable_start = string;
251 c = *string;
252 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
253 {
254 do
255 c = *++string;
256 while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
257 || (c >= '0' && c <= '9') || c == '_');
258 variable_end = string;
259  
260 if (variable_start[-1] == '{')
261 {
262 if (*string == '}')
263 {
264 string++;
265 valid = true;
266 }
267 else
268 valid = false;
269 }
270 else
271 valid = true;
272  
273 if (valid)
274 callback (variable_start, variable_end - variable_start);
275 }
276 }
277 }
278  
279  
280 /* Print a variable to stdout, followed by a newline. */
281 static void
282 print_variable (const char *var_ptr, size_t var_len)
283 {
284 fwrite (var_ptr, var_len, 1, stdout);
285 putchar ('\n');
286 }
287  
288 /* Print the variables contained in STRING to stdout, each one followed by a
289 newline. */
290 static void
291 print_variables (const char *string)
292 {
293 find_variables (string, &print_variable);
294 }
295  
296  
297 /* Type describing list of immutable strings,
298 implemented using a dynamic array. */
299 typedef struct string_list_ty string_list_ty;
300 struct string_list_ty
301 {
302 const char **item;
303 size_t nitems;
304 size_t nitems_max;
305 };
306  
307 /* Initialize an empty list of strings. */
308 static inline void
309 string_list_init (string_list_ty *slp)
310 {
311 slp->item = NULL;
312 slp->nitems = 0;
313 slp->nitems_max = 0;
314 }
315  
316 /* Append a single string to the end of a list of strings. */
317 static inline void
318 string_list_append (string_list_ty *slp, const char *s)
319 {
320 /* Grow the list. */
321 if (slp->nitems >= slp->nitems_max)
322 {
323 size_t nbytes;
324  
325 slp->nitems_max = slp->nitems_max * 2 + 4;
326 nbytes = slp->nitems_max * sizeof (slp->item[0]);
327 slp->item = (const char **) xrealloc (slp->item, nbytes);
328 }
329  
330 /* Add the string to the end of the list. */
331 slp->item[slp->nitems++] = s;
332 }
333  
334 /* Compare two strings given by reference. */
335 static int
336 cmp_string (const void *pstr1, const void *pstr2)
337 {
338 const char *str1 = *(const char **)pstr1;
339 const char *str2 = *(const char **)pstr2;
340  
341 return strcmp (str1, str2);
342 }
343  
344 /* Sort a list of strings. */
345 static inline void
346 string_list_sort (string_list_ty *slp)
347 {
348 if (slp->nitems > 0)
349 qsort (slp->item, slp->nitems, sizeof (slp->item[0]), cmp_string);
350 }
351  
352 /* Test whether a string list contains a given string. */
353 static inline int
354 string_list_member (const string_list_ty *slp, const char *s)
355 {
356 size_t j;
357  
358 for (j = 0; j < slp->nitems; ++j)
359 if (strcmp (slp->item[j], s) == 0)
360 return 1;
361 return 0;
362 }
363  
364 /* Test whether a sorted string list contains a given string. */
365 static int
366 sorted_string_list_member (const string_list_ty *slp, const char *s)
367 {
368 size_t j1, j2;
369  
370 j1 = 0;
371 j2 = slp->nitems;
372 if (j2 > 0)
373 {
374 /* Binary search. */
375 while (j2 - j1 > 1)
376 {
377 /* Here we know that if s is in the list, it is at an index j
378 with j1 <= j < j2. */
379 size_t j = (j1 + j2) >> 1;
380 int result = strcmp (slp->item[j], s);
381  
382 if (result > 0)
383 j2 = j;
384 else if (result == 0)
385 return 1;
386 else
387 j1 = j + 1;
388 }
389 if (j2 > j1)
390 if (strcmp (slp->item[j1], s) == 0)
391 return 1;
392 }
393 return 0;
394 }
395  
396 /* Destroy a list of strings. */
397 static inline void
398 string_list_destroy (string_list_ty *slp)
399 {
400 size_t j;
401  
402 for (j = 0; j < slp->nitems; ++j)
403 free ((char *) slp->item[j]);
404 if (slp->item != NULL)
405 free (slp->item);
406 }
407  
408  
409 /* Set of variables on which to perform substitution.
410 Used only if !all_variables. */
411 static string_list_ty variables_set;
412  
413 /* Adds a variable to variables_set. */
414 static void
415 note_variable (const char *var_ptr, size_t var_len)
416 {
417 char *string = XNMALLOC (var_len + 1, char);
418 memcpy (string, var_ptr, var_len);
419 string[var_len] = '\0';
420  
421 string_list_append (&variables_set, string);
422 }
423  
424 /* Stores the variables occurring in the string in variables_set. */
425 static void
426 note_variables (const char *string)
427 {
428 string_list_init (&variables_set);
429 find_variables (string, &note_variable);
430 string_list_sort (&variables_set);
431 }
432  
433  
434 static int
435 do_getc ()
436 {
437 int c = getc (stdin);
438  
439 if (c == EOF)
440 {
441 if (ferror (stdin))
442 error (EXIT_FAILURE, errno, _("\
443 error while reading \"%s\""), _("standard input"));
444 }
445  
446 return c;
447 }
448  
449 static inline void
450 do_ungetc (int c)
451 {
452 if (c != EOF)
453 ungetc (c, stdin);
454 }
455  
456 /* Copies stdin to stdout, performing substitutions. */
457 static void
458 subst_from_stdin ()
459 {
460 static char *buffer;
461 static size_t bufmax;
462 static size_t buflen;
463 int c;
464  
465 for (;;)
466 {
467 c = do_getc ();
468 if (c == EOF)
469 break;
470 /* Look for $VARIABLE or ${VARIABLE}. */
471 if (c == '$')
472 {
473 bool opening_brace = false;
474 bool closing_brace = false;
475  
476 c = do_getc ();
477 if (c == '{')
478 {
479 opening_brace = true;
480 c = do_getc ();
481 }
482 if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') || c == '_')
483 {
484 bool valid;
485  
486 /* Accumulate the VARIABLE in buffer. */
487 buflen = 0;
488 do
489 {
490 if (buflen >= bufmax)
491 {
492 bufmax = 2 * bufmax + 10;
493 buffer = xrealloc (buffer, bufmax);
494 }
495 buffer[buflen++] = c;
496  
497 c = do_getc ();
498 }
499 while ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')
500 || (c >= '0' && c <= '9') || c == '_');
501  
502 if (opening_brace)
503 {
504 if (c == '}')
505 {
506 closing_brace = true;
507 valid = true;
508 }
509 else
510 {
511 valid = false;
512 do_ungetc (c);
513 }
514 }
515 else
516 {
517 valid = true;
518 do_ungetc (c);
519 }
520  
521 if (valid)
522 {
523 /* Terminate the variable in the buffer. */
524 if (buflen >= bufmax)
525 {
526 bufmax = 2 * bufmax + 10;
527 buffer = xrealloc (buffer, bufmax);
528 }
529 buffer[buflen] = '\0';
530  
531 /* Test whether the variable shall be substituted. */
532 if (!all_variables
533 && !sorted_string_list_member (&variables_set, buffer))
534 valid = false;
535 }
536  
537 if (valid)
538 {
539 /* Substitute the variable's value from the environment. */
540 const char *env_value = getenv (buffer);
541  
542 if (env_value != NULL)
543 fputs (env_value, stdout);
544 }
545 else
546 {
547 /* Perform no substitution at all. Since the buffered input
548 contains no other '$' than at the start, we can just
549 output all the buffered contents. */
550 putchar ('$');
551 if (opening_brace)
552 putchar ('{');
553 fwrite (buffer, buflen, 1, stdout);
554 if (closing_brace)
555 putchar ('}');
556 }
557 }
558 else
559 {
560 do_ungetc (c);
561 putchar ('$');
562 if (opening_brace)
563 putchar ('{');
564 }
565 }
566 else
567 putchar (c);
568 }
569 }