1 /*
2 * cook - file construction tool
3 * Copyright (C) 1994-1999, 2001, 2003, 2004, 2006, 2007 Peter Miller;
4 * All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see
18 * <http://www.gnu.org/licenses/>.
19 *
20 *
21 * If you are going to add a new recipe flag (set by the "set" statement,
22 * or the "set" clause of a recipe) you need to change all of the
23 * following places:
24 *
25 * cook/option.h
26 * to define the OPTION_ value
27 * cook/option.c
28 * option_tidyup()
29 * if the option defaults to true
30 * option_set_errors()
31 * if the option should be turned off once cookbook errors
32 * are encountered.
33 * option_number_name()
34 * for the name of the option
35 * cook/flag.h
36 * to define the RF_ values (RF stands for Recipe Flag)
37 * cook/flag.c
38 * to define the RF_ names
39 * lib/en/user-guide/langu.flags.so
40 * to document the recipe flag
41 *
42 * If you choose to make it a command line option,
43 * you must also update these files:
44 *
45 * cook/main.c
46 * to define the new command line option and process it
47 * (only if it should also be a command line option)
48 * cook/builtin/options.c
49 * to access the option from within the cookbook (typically used
50 * for recursive cook invokations)
51 * lib/en/man1/cook.1
52 * to document it, if you added a new command line option
53 */
54
55 #include <common/ac/stddef.h>
56 #include <common/ac/string.h>
57 #include <common/ac/stdio.h>
58 #include <common/ac/stdlib.h>
59 #include <common/ac/signal.h>
60
61 #include <common/arglex.h>
62 #include <common/error_intl.h>
63 #include <common/fflush_slow.h>
64 #include <common/help.h>
65 #include <common/progname.h>
66 #include <common/quit.h>
67 #include <common/star.h>
68 #include <common/trace.h>
69 #include <common/version.h>
70 #include <cook/builtin.h>
71 #include <cook/cook.h>
72 #include <cook/fingerprint.h>
73 #include <cook/id.h>
74 #include <cook/id/variable.h>
75 #include <cook/lex.h>
76 #include <cook/listing.h>
77 #include <cook/opcode/context.h>
78 #include <cook/option.h>
79 #include <cook/parse.h>
80
81
82 enum
83 {
84 arglex_token_action,
85 arglex_token_action_not,
86 arglex_token_book,
87 arglex_token_book_not,
88 arglex_token_cascade,
89 arglex_token_cascade_not,
90 arglex_token_disassemble,
91 arglex_token_disassemble_not,
92 arglex_token_errok,
93 arglex_token_errok_not,
94 arglex_token_fingerprint,
95 arglex_token_fingerprint_not,
96 arglex_token_fingerprint_update,
97 arglex_token_force,
98 arglex_token_force_not,
99 arglex_token_include,
100 arglex_token_include_cooked,
101 arglex_token_include_cooked_not,
102 arglex_token_include_cooked_warning,
103 arglex_token_include_cooked_warning_not,
104 arglex_token_log,
105 arglex_token_log_not,
106 arglex_token_metering,
107 arglex_token_metering_not,
108 arglex_token_pairs,
109 arglex_token_parallel,
110 arglex_token_parallel_not,
111 arglex_token_pedantic,
112 arglex_token_pedantic_not,
113 arglex_token_persevere,
114 arglex_token_persevere_not,
115 arglex_token_precious,
116 arglex_token_precious_not,
117 arglex_token_reason,
118 arglex_token_reason_not,
119 arglex_token_script,
120 arglex_token_shallow,
121 arglex_token_shallow_not,
122 arglex_token_silent,
123 arglex_token_silent_not,
124 arglex_token_star,
125 arglex_token_star_not,
126 arglex_token_strip_dot,
127 arglex_token_strip_dot_not,
128 arglex_token_symlink_ingredients,
129 arglex_token_symlink_ingredients_not,
130 arglex_token_tell_position,
131 arglex_token_tell_position_not,
132 arglex_token_touch,
133 arglex_token_touch_not,
134 arglex_token_tty,
135 arglex_token_tty_not,
136 arglex_token_update,
137 arglex_token_update_not,
138 arglex_token_web
139 /*
140 * When you add an option to this list, you must
141 * also add it to the list in cook/builtin/options.c
142 */
143 };
144
145 static arglex_table_ty argtab[] =
146 {
147 { "-Action", (arglex_token_ty) arglex_token_action },
148 { "-No_Action", (arglex_token_ty) arglex_token_action_not },
149 { "-Book", (arglex_token_ty) arglex_token_book },
150 { "-No_Book", (arglex_token_ty) arglex_token_book_not },
151 { "-CAScade", (arglex_token_ty) arglex_token_cascade },
152 { "-No_CAScade", (arglex_token_ty) arglex_token_cascade_not },
153 { "-Continue", (arglex_token_ty) arglex_token_persevere },
154 { "-No_Continue", (arglex_token_ty) arglex_token_persevere_not },
155 { "-DISassemble", (arglex_token_ty) arglex_token_disassemble },
156 { "-No_DISassemble", (arglex_token_ty) arglex_token_disassemble_not },
157 { "-Errok", (arglex_token_ty) arglex_token_errok },
158 { "-No_Errok", (arglex_token_ty) arglex_token_errok_not },
159 { "-FingerPrint", (arglex_token_ty) arglex_token_fingerprint },
160 { "-No_FingerPrint", (arglex_token_ty) arglex_token_fingerprint_not },
161 { "-FingerPrint_Update",
162 (arglex_token_ty) arglex_token_fingerprint_update },
163 { "-Forced", (arglex_token_ty) arglex_token_force },
164 { "-No_Forced", (arglex_token_ty) arglex_token_force_not },
165 { "-HyperText_Markup_Language", (arglex_token_ty) arglex_token_web },
166 { "-Include", (arglex_token_ty) arglex_token_include },
167 { "-\\I*", (arglex_token_ty) arglex_token_include },
168 { "-Include_Cooked", (arglex_token_ty) arglex_token_include_cooked },
169 { "-Include_Cooked_Warning",
170 (arglex_token_ty) arglex_token_include_cooked_warning },
171 { "-Jobs", (arglex_token_ty) arglex_token_parallel },
172 { "-No_Include_Cooked", (arglex_token_ty) arglex_token_include_cooked_not },
173 { "-No_Include_Cooked_Warning",
174 (arglex_token_ty) arglex_token_include_cooked_warning_not },
175 { "-LOg", (arglex_token_ty) arglex_token_log },
176 { "-List", (arglex_token_ty) arglex_token_log },
177 { "-No_LOg", (arglex_token_ty) arglex_token_log_not },
178 { "-No_List", (arglex_token_ty) arglex_token_log_not },
179 { "-Meter", (arglex_token_ty) arglex_token_metering },
180 { "-No_Meter", (arglex_token_ty) arglex_token_metering_not },
181 { "-PAirs", (arglex_token_ty) arglex_token_pairs },
182 { "-PARallel", (arglex_token_ty) arglex_token_parallel },
183 { "-No_PARallel", (arglex_token_ty) arglex_token_parallel_not },
184 { "-Precious", (arglex_token_ty) arglex_token_precious },
185 { "-No_Precious", (arglex_token_ty) arglex_token_precious_not },
186 { "-Reason", (arglex_token_ty) arglex_token_reason },
187 { "-No_Reason", (arglex_token_ty) arglex_token_reason_not },
188 { "-SCript", (arglex_token_ty) arglex_token_script },
189 { "-Silent", (arglex_token_ty) arglex_token_silent },
190 { "-No_Silent", (arglex_token_ty) arglex_token_silent_not },
191 { "-SHallow", (arglex_token_ty) arglex_token_shallow },
192 { "-No_SHallow", (arglex_token_ty) arglex_token_shallow_not },
193 { "-STar", (arglex_token_ty) arglex_token_star },
194 { "-No_STar", (arglex_token_ty) arglex_token_star_not },
195 { "-Strip_Dot", (arglex_token_ty) arglex_token_strip_dot },
196 { "-No_Strip_Dot", (arglex_token_ty) arglex_token_strip_dot_not },
197 { "-Symbolic_Link_Ingredients",
198 (arglex_token_ty)arglex_token_symlink_ingredients },
199 { "-Not_Symbolic_Link_Ingredients",
200 (arglex_token_ty)arglex_token_symlink_ingredients_not },
201 { "-Tell_Position", (arglex_token_ty) arglex_token_tell_position },
202 { "-No_Tell_Position", (arglex_token_ty) arglex_token_tell_position_not},
203 { "-TErminal", (arglex_token_ty) arglex_token_tty },
204 { "-No_TErminal", (arglex_token_ty) arglex_token_tty_not },
205 { "-Touch", (arglex_token_ty) arglex_token_touch },
206 { "-No_Touch", (arglex_token_ty) arglex_token_touch_not },
207 { "-TRace", (arglex_token_ty) arglex_token_reason },
208 { "-No_TRace", (arglex_token_ty) arglex_token_reason_not },
209 { "-Update", (arglex_token_ty) arglex_token_update },
210 { "-Time_Adjust", (arglex_token_ty) arglex_token_update },
211 { "-No_Update", (arglex_token_ty) arglex_token_update_not },
212 { "-No_Time_Adjust", (arglex_token_ty) arglex_token_update_not },
213 { "-Web", (arglex_token_ty) arglex_token_web },
214 { 0, (arglex_token_ty)0 }, /* end marker */
215 };
216
217
218 /*
219 * NAME
220 * usage - options diagnostic
221 *
222 * SYNOPSIS
223 * void usage(void);
224 *
225 * DESCRIPTION
226 * Usage is called when the user has made a syntactic or semantic error
227 * on the command line.
228 *
229 * CAVEAT
230 * This function does NOT return.
231 */
232
233 static void
usage(void)234 usage(void)
235 {
236 char *progname;
237
238 progname = progname_get();
239 fprintf(stderr, "usage: %s [ <option>... ][ <filename>... ]\n", progname);
240 fprintf(stderr, " %s -Help\n", progname);
241 fprintf(stderr, " %s -VERSion\n", progname);
242 quit(1);
243 trace(("to silence warnings\n"));
244 }
245
246
247 /*
248 * NAME
249 * argparse - parse command line
250 *
251 * SYNOPSIS
252 * void argparse(option_level_ty);
253 *
254 * DESCRIPTION
255 * The argparse function is used to parse command lines.
256 *
257 * RETURNS
258 * void
259 */
260
261 static void
argparse(option_level_ty level)262 argparse(option_level_ty level)
263 {
264 option_number_ty type;
265 string_ty *s;
266 sub_context_ty *scp;
267 int fingerprint_update;
268
269 type = -1;
270 fingerprint_update = 0;
271 switch (arglex())
272 {
273 case arglex_token_help:
274 if (level != OPTION_LEVEL_COMMAND_LINE)
275 {
276 not_in_env:
277 scp = sub_context_new();
278 sub_var_set(scp, "Name", "%s", arglex_value.alv_string);
279 fatal_intl(scp, i18n("may not use $name in environment variable"));
280 /* NOTREACHED */
281 }
282 help((char *)0, usage);
283 quit(0);
284
285 case arglex_token_version:
286 if (level != OPTION_LEVEL_COMMAND_LINE)
287 goto not_in_env;
288 version();
289 quit(0);
290
291 default:
292 break;
293 }
294 while (arglex_token != arglex_token_eoln)
295 {
296 switch (arglex_token)
297 {
298 default:
299 generic_argument(usage);
300 continue;
301
302 case arglex_token_include:
303 if (arglex() != arglex_token_string)
304 {
305 arg_needs_string(arglex_token_include, usage);
306 /* NOTREACHED */
307 }
308 s = str_from_c(arglex_value.alv_string);
309 string_list_append_unique(&option.o_search_path, s);
310 str_free(s);
311 break;
312
313 case arglex_token_reason:
314 type = OPTION_REASON;
315 normal_on:
316 if (option_already(type, level))
317 {
318 too_many:
319 arg_duplicate_cur(usage);
320 /* NOTREACHED */
321 }
322 option_set(type, level, 1);
323 break;
324
325 case arglex_token_reason_not:
326 type = OPTION_REASON;
327 normal_off:
328 if (option_already(type, level))
329 goto too_many;
330 option_set(type, level, 0);
331 break;
332
333 case arglex_token_cascade:
334 type = OPTION_CASCADE;
335 goto normal_on;
336
337 case arglex_token_cascade_not:
338 type = OPTION_CASCADE;
339 goto normal_off;
340
341 case arglex_token_disassemble:
342 type = OPTION_DISASSEMBLE;
343 goto normal_on;
344
345 case arglex_token_disassemble_not:
346 type = OPTION_DISASSEMBLE;
347 goto normal_off;
348
349 case arglex_token_tty:
350 type = OPTION_TERMINAL;
351 goto normal_on;
352
353 case arglex_token_tty_not:
354 type = OPTION_TERMINAL;
355 goto normal_off;
356
357 case arglex_token_precious:
358 type = OPTION_PRECIOUS;
359 goto normal_on;
360
361 case arglex_token_precious_not:
362 type = OPTION_PRECIOUS;
363 goto normal_off;
364
365 case arglex_token_log:
366 if (option_already(OPTION_LOGGING, level))
367 goto too_many;
368 option_set(OPTION_LOGGING, level, 1);
369 if (arglex() != arglex_token_string)
370 continue;
371 if (option.o_logfile)
372 str_free(option.o_logfile);
373 option.o_logfile = str_from_c(arglex_value.alv_string);
374 break;
375
376 case arglex_token_log_not:
377 type = OPTION_LOGGING;
378 goto normal_off;
379
380 case arglex_token_book:
381 if (option_already(OPTION_BOOK, level))
382 goto too_many;
383 option_set(OPTION_BOOK, level, 1);
384 if (arglex() != arglex_token_string)
385 continue;
386 if (option.o_book)
387 str_free(option.o_book);
388 option.o_book = str_from_c(arglex_value.alv_string);
389 break;
390
391 case arglex_token_book_not:
392 type = OPTION_BOOK;
393 goto normal_off;
394
395 case arglex_token_include_cooked:
396 type = OPTION_INCLUDE_COOKED;
397 goto normal_on;
398
399 case arglex_token_include_cooked_not:
400 type = OPTION_INCLUDE_COOKED;
401 goto normal_off;
402
403 case arglex_token_include_cooked_warning:
404 type = OPTION_INCLUDE_COOKED_WARNING;
405 goto normal_on;
406
407 case arglex_token_include_cooked_warning_not:
408 type = OPTION_INCLUDE_COOKED_WARNING;
409 goto normal_off;
410
411 case arglex_token_silent:
412 type = OPTION_SILENT;
413 goto normal_on;
414
415 case arglex_token_silent_not:
416 type = OPTION_SILENT;
417 goto normal_off;
418
419 case arglex_token_tell_position:
420 type = OPTION_TELL_POSITION;
421 goto normal_on;
422
423 case arglex_token_tell_position_not:
424 type = OPTION_TELL_POSITION;
425 goto normal_off;
426
427 case arglex_token_metering:
428 type = OPTION_METER;
429 goto normal_on;
430
431 case arglex_token_metering_not:
432 type = OPTION_METER;
433 goto normal_off;
434
435 case arglex_token_touch:
436 type = OPTION_TOUCH;
437 goto normal_on;
438
439 case arglex_token_touch_not:
440 type = OPTION_TOUCH;
441 goto normal_off;
442
443 case arglex_token_action:
444 type = OPTION_ACTION;
445 goto normal_on;
446
447 case arglex_token_action_not:
448 type = OPTION_ACTION;
449 goto normal_off;
450
451 case arglex_token_persevere:
452 type = OPTION_PERSEVERE;
453 goto normal_on;
454
455 case arglex_token_persevere_not:
456 type = OPTION_PERSEVERE;
457 goto normal_off;
458
459 case arglex_token_errok:
460 type = OPTION_ERROK;
461 goto normal_on;
462
463 case arglex_token_errok_not:
464 type = OPTION_ERROK;
465 goto normal_off;
466
467 case arglex_token_force:
468 type = OPTION_FORCE;
469 goto normal_on;
470
471 case arglex_token_force_not:
472 type = OPTION_FORCE;
473 goto normal_off;
474
475 case arglex_token_fingerprint:
476 type = OPTION_FINGERPRINT;
477 goto normal_on;
478
479 case arglex_token_fingerprint_not:
480 type = OPTION_FINGERPRINT;
481 goto normal_off;
482
483 case arglex_token_fingerprint_update:
484 if (level != OPTION_LEVEL_COMMAND_LINE)
485 goto not_in_env;
486 if (option.fingerprint_update)
487 goto too_many;
488 option.fingerprint_update++;
489 break;
490
491 case arglex_token_pairs:
492 if (level != OPTION_LEVEL_COMMAND_LINE)
493 goto not_in_env;
494 if (option.pairs)
495 goto too_many;
496 option.pairs++;
497 break;
498
499 case arglex_token_script:
500 if (level != OPTION_LEVEL_COMMAND_LINE)
501 goto not_in_env;
502 if (option.script)
503 goto too_many;
504 option.script++;
505 break;
506
507 case arglex_token_web:
508 if (level != OPTION_LEVEL_COMMAND_LINE)
509 goto not_in_env;
510 if (option.web)
511 goto too_many;
512 option.web++;
513 break;
514
515 case arglex_token_string:
516 if (level != OPTION_LEVEL_COMMAND_LINE)
517 {
518 if (strchr(arglex_value.alv_string, '='))
519 {
520 fatal_intl
521 (
522 0,
523 i18n("may not assign variables in environment variable")
524 );
525 }
526 else
527 {
528 fatal_intl
529 (
530 0,
531 i18n("may not name targets in environment variable")
532 );
533 }
534 }
535 else
536 {
537 char *cp;
538
539 cp = strchr(arglex_value.alv_string, '=');
540 if (!cp)
541 {
542 s = str_from_c(arglex_value.alv_string);
543 string_list_append(&option.o_target, s);
544 str_free(s);
545 }
546 else
547 {
548 s = str_from_c(arglex_value.alv_string);
549 string_list_append(&option.o_vardef, s);
550 str_free(s);
551 }
552 }
553 break;
554
555 case arglex_token_star:
556 type = OPTION_STAR;
557 goto normal_on;
558
559 case arglex_token_star_not:
560 type = OPTION_STAR;
561 goto normal_off;
562
563 case arglex_token_strip_dot:
564 type = OPTION_STRIP_DOT;
565 goto normal_on;
566
567 case arglex_token_strip_dot_not:
568 type = OPTION_STRIP_DOT;
569 goto normal_off;
570
571 case arglex_token_symlink_ingredients:
572 type = OPTION_SYMLINK_INGREDIENTS;
573 goto normal_on;
574
575 case arglex_token_symlink_ingredients_not:
576 type = OPTION_SYMLINK_INGREDIENTS;
577 goto normal_off;
578
579 case arglex_token_update:
580 type = OPTION_UPDATE;
581 goto normal_on;
582
583 case arglex_token_update_not:
584 type = OPTION_UPDATE;
585 goto normal_off;
586
587 case arglex_token_parallel:
588 if (arglex() != arglex_token_number)
589 {
590 s = str_from_c("parallel_jobs=4");
591 string_list_append(&option.o_vardef, s);
592 str_free(s);
593 continue;
594 }
595 s = str_format("parallel_jobs=%d", (int)arglex_value.alv_number);
596 string_list_append(&option.o_vardef, s);
597 str_free(s);
598 break;
599
600 case arglex_token_parallel_not:
601 s = str_from_c("parallel_jobs=1");
602 string_list_append(&option.o_vardef, s);
603 str_free(s);
604 break;
605
606 case arglex_token_shallow:
607 type = OPTION_SHALLOW;
608 goto normal_on;
609
610 case arglex_token_shallow_not:
611 type = OPTION_SHALLOW;
612 goto normal_off;
613 }
614 arglex();
615 }
616 }
617
618
619 static void
set_command_line_goals(void)620 set_command_line_goals(void)
621 {
622 string_ty *name;
623 opcode_context_ty *ocp;
624
625 name = str_from_c("command-line-goals");
626 ocp = opcode_context_new(0, 0);
627 opcode_context_id_assign(ocp, name, id_variable_new(&option.o_target), 0);
628 opcode_context_delete(ocp);
629 str_free(name);
630 }
631
632
633 /*
634 * NAME
635 * main - initial entry point for cook
636 *
637 * SYNOPSIS
638 * void main(int argc, char **argv);
639 *
640 * DESCRIPTION
641 * Main is the initial entry point for cook.
642 *
643 * RETURNS
644 * Exit is always through exit().
645 * The exit code will be 0 for success, or 1 for some error.
646 */
647
648 int
main(int argc,char ** argv)649 main(int argc, char **argv)
650 {
651 int retval;
652
653 /*
654 * Some versions of cron(8) and at(1) set SIGCHLD to SIG_IGN.
655 * This is kinda dumb, because it breaks assumprions made in
656 * libc (like pclose, for instance). It also blows away most
657 * of Cook's process handling. We explicitly set the SIGCHLD
658 * signal handling to SIG_DFL to make sure this signal does what
659 * we expect no matter how we are invoked.
660 */
661 #ifdef SIGCHLD
662 signal(SIGCHLD, SIG_DFL);
663 #else
664 signal(SIGCLD, SIG_DFL);
665 #endif
666
667 /*
668 * initialize things
669 * (order is critical here)
670 */
671 progname_set(argv[0]);
672 str_initialize();
673 id_initialize();
674 lex_initialize();
675
676 /*
677 * parse the COOK environment variable
678 */
679 arglex_init_from_env(argv[0], argtab);
680 argparse(OPTION_LEVEL_ENVIRONMENT);
681
682 /*
683 * parse the command line
684 */
685 arglex_init(argc, argv, argtab);
686 argparse(OPTION_LEVEL_COMMAND_LINE);
687
688 option_tidy_up();
689
690 log_open();
691
692 /*
693 * turn on progress stars if they asked for them
694 */
695 if (option_test(OPTION_STAR))
696 star_enable();
697
698 /*
699 * If we were asked to update the fingerprints, do it here.
700 * We don't actually ant to read in the cookbook.
701 */
702 if (option.fingerprint_update)
703 {
704 fp_tweak();
705 quit(0);
706 }
707
708 /*
709 * read in the cook book
710 *
711 * If there are #include-cooked directives,
712 * we may need to do it more than once.
713 */
714 if (!option.o_book)
715 fatal_intl(0, i18n("no book found"));
716 for (;;)
717 {
718 int status;
719 size_t j;
720
721 builtin_initialize();
722
723 /*
724 * instanciate the command line variable assignments
725 */
726 for (j = 0; j < option.o_vardef.nstrings; ++j)
727 {
728 char *s;
729 char *cp;
730 string_ty *name;
731 string_ty *value;
732 string_list_ty wl;
733 opcode_context_ty *ocp;
734
735 s = option.o_vardef.string[j]->str_text;
736 cp = strchr(s, '=');
737 assert(cp);
738 if (!cp)
739 continue;
740 name = str_n_from_c(s, cp - s);
741 value = str_from_c(cp + 1);
742 str2wl(&wl, value, (char *)0, 0);
743 str_free(value);
744 ocp = opcode_context_new(0, 0);
745 opcode_context_id_assign(ocp, name, id_variable_new(&wl), 0);
746 opcode_context_delete(ocp);
747 str_free(name);
748 string_list_destructor(&wl);
749 }
750
751 set_command_line_goals();
752
753 parse(option.o_book);
754 status = cook_auto_required();
755 if (status < 0)
756 quit(1);
757 if (!status)
758 break;
759 id_reset();
760 cook_reset();
761 }
762
763 /*
764 * work out what to cook.
765 * If no targets have been given, use the first explicit recipe.
766 */
767 set_command_line_goals();
768 if (!option.o_target.nstrings)
769 cook_find_default(&option.o_target);
770 assert(option.o_target.nstrings);
771
772 /*
773 * cook the target
774 */
775 if (option.pairs)
776 retval = cook_pairs(&option.o_target);
777 else if (option.script)
778 retval = cook_script(&option.o_target);
779 else if (option.web)
780 retval = cook_web(&option.o_target);
781 else
782 retval = cook(&option.o_target);
783
784 #ifdef DEBUG
785 fflush_slowly_report();
786 #endif
787
788 quit(retval);
789 /*NOTREACHED*/
790 return 0;
791 }
792