1 /*
2  *      cook - file construction tool
3  *      Copyright (C) 1993-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  * The options may be set at various levels.  The level with the highest
21  * precedence which has actually been set is used to determine the option
22  * value at any given time.
23  *
24  * Each level of an option is represented by 2 bits in the flag word.  One bit
25  * is used to indicate that the option has been set for that level, the other
26  * bit indicates the state.  Determining the least set bit in an expression is
27  * cheap (x&-x) so highest priority is the lowest numbered level.
28  *
29  * The COOK enviroment variable is basically a replacement for the defaults,
30  * so that users can change the default behaviour.  The command line overrides
31  * almost everything.  The error level is the only level with higher
32  * precedence than the command line, and it is used to prevent disasters
33  * after parse errors or interrupts have happened.
34  *
35  * -------------------------------------------------------------------------
36  *
37  *
38  * If you are going to add a new recipe flag (set by the "set" statement,
39  * or the "set" clause of a recipe) you need to change all of the
40  * following places:
41  *
42  * cook/option.h
43  *     to define the OPTION_ value
44  * cook/option.c
45  *     option_tidyup()
46  *         if the option defaults to true
47  *     option_set_errors()
48  *         if the option should be turned off once cookbook errors
49  *         are encountered.
50  *     option_number_name()
51  *         for the name of the option
52  * cook/flag.h
53  *     to define the RF_ values (RF stands for Recipe Flag)
54  * cook/flag.c
55  *     to define the RF_ names
56  * langu.flags.so
57  *     to document the recipe flag
58  *
59  * If you choose to make it a command line option,
60  * you must also update these files:
61  *
62  * cook/main.c
63  *     to define the new command line option and process it
64  *     (only if it should also be a command line option)
65  * cook/builtin/options.c
66  *     to access the option from within the cookbook (typically used
67  *     for recursive cook invokations)
68  * lib/en/man1/cook.1
69  *     to document it, if you added a new command line option
70  */
71 
72 #include <common/ac/ctype.h>
73 #include <common/ac/limits.h>
74 #include <common/ac/stddef.h>
75 #include <common/ac/stdio.h>
76 #include <common/ac/stdlib.h>
77 #include <common/ac/string.h>
78 #include <common/ac/time.h>
79 
80 #include <common/libdir.h>
81 #include <common/mem.h>
82 #include <common/progname.h>
83 #include <common/trace.h>
84 #include <cook/option.h>
85 #include <cook/os_interface.h>
86 
87 
88 typedef struct flag_ty flag_ty;
89 struct flag_ty
90 {
91     unsigned long   o_flag[OPTION_max];
92 };
93 
94 option_ty       option;
95 static flag_ty  flag;
96 
97 
98 /*
99  * NAME
100  *      option_set - set an option
101  *
102  * SYNOPSIS
103  *      void option_set(option_number_ty num, option_level_ty lvl, int state);
104  *
105  * DESCRIPTION
106  *      The option_set function is used to set the given option at the given
107  *      level to the given state.
108  *
109  * RETURNS
110  *      void
111  */
112 
113 void
option_set(option_number_ty o,option_level_ty level,int state)114 option_set(option_number_ty o, option_level_ty level, int state)
115 {
116     trace(("option_set(o = %s, level = %s, state = %d)\n{\n",
117         option_number_name(o), option_level_name(level), state));
118     assert((int)o >= 0 && (int)o < (int)OPTION_max);
119     flag.o_flag[(size_t) o] &= ~(3L << (2 * (int)level));
120     if (state)
121         flag.o_flag[(size_t) o] |= 3L << (2 * (int)level);
122     else
123         flag.o_flag[(size_t) o] |= 1L << (2 * (int)level);
124     trace(("}\n"));
125 }
126 
127 
128 /*
129  * NAME
130  *      option_already - see if an option is already set
131  *
132  * SYNOPSIS
133  *      int option_already(option_number_ty num, option_level_ty lvl);
134  *
135  * DESCRIPTION
136  *      The option_already function is used to test if a given option at a
137  *      given level has been set.
138  *
139  * RETURNS
140  *      int: zero if the option has not been set, nonzero if it has.
141  */
142 
143 int
option_already(option_number_ty o,option_level_ty level)144 option_already(option_number_ty o, option_level_ty level)
145 {
146     int             result;
147 
148     trace(("option_already(o = %s, level = %s)\n{\n",
149             option_number_name(o), option_level_name(level)));
150     assert((int)o >= 0 && (int)o < (int)OPTION_max);
151     result = (((flag.o_flag[(size_t) o] >> (2 * (int)level)) & 1) != 0);
152     trace(("return %d;\n", result));
153     trace(("}\n"));
154     return result;
155 }
156 
157 
158 /*
159  * NAME
160  *      option_undo - remove option setting
161  *
162  * SYNOPSIS
163  *      void option_undo(option_number_ty num, option_level_ty lvl);
164  *
165  * DESCRIPTION
166  *      The option_undo function is used to is used to remove the option
167  *      setting for the given option at the given level.
168  *
169  * RETURNS
170  *      void
171  */
172 
173 void
option_undo(option_number_ty o,option_level_ty level)174 option_undo(option_number_ty o, option_level_ty level)
175 {
176     trace(("option_undo(o = %s, level = %s)\n{\n", option_number_name(o),
177         option_level_name(level)));
178     assert((int)o >= 0 && (int)o < (int)OPTION_max);
179     flag.o_flag[(size_t) o] &= ~(3L << (2 * (int)level));
180     trace(("}\n"));
181 }
182 
183 
184 /*
185  * NAME
186  *      option_undo_level - remove options settings
187  *
188  * SYNOPSIS
189  *      void option_undo_level(option_level_ty lvl);
190  *
191  * DESCRIPTION
192  *      The option_undo_level function is used to remove the settings for all
193  *      options at a given level.
194  *
195  * RETURNS
196  *      void
197  */
198 
199 void
option_undo_level(option_level_ty level)200 option_undo_level(option_level_ty level)
201 {
202     int             o;
203 
204     trace(("option_undo_level(level = %s)\n{\n", option_level_name(level)));
205     for (o = 0; o < (int)OPTION_max; ++o)
206         option_undo((option_number_ty) o, level);
207     trace(("}\n"));
208 }
209 
210 
211 /*
212  * NAME
213  *      option_test - test an option
214  *
215  * SYNOPSIS
216  *      int option_test(option_number_ty num);
217  *
218  * DESCRIPTION
219  *      The option_test function is used to test the setting of an option.
220  *      The level of highest precedence which hash been set is used.
221  *
222  * RETURNS
223  *      int: zero if the option is off, nonzero if the option is on.
224  */
225 
226 int
option_test(option_number_ty o)227 option_test(option_number_ty o)
228 {
229     unsigned long   *op;
230     unsigned long   mask;
231     int             result;
232 
233     trace(("option_test(o = %s)\n{\n", option_number_name(o)));
234     assert((int)o >= 0 && (int)o < (int)OPTION_max);
235     op = &flag.o_flag[(size_t) o];
236     mask = *op & 0x55555555;
237     mask &= -mask; /* get LSB */
238     result = ((*op & (mask << 1)) != 0);
239     trace(("return %d;\n", result));
240     trace(("}\n"));
241     return result;
242 }
243 
244 
245 static string_ty *
Capitalize(string_ty * s)246 Capitalize(string_ty *s)
247 {
248     if (s->str_length < 1 || !islower((unsigned char)s->str_text[0]))
249         return str_copy(s);
250     return
251         str_format
252         (
253             "%c%s",
254             toupper((unsigned char)s->str_text[0]),
255             s->str_text + 1
256         );
257 }
258 
259 
260 /*
261  * NAME
262  *      option_tidy_up - mother hen
263  *
264  * SYNOPSIS
265  *      void option_tidy_up(void);
266  *
267  * DESCRIPTION
268  *      The option_tidy_up function is used to set a few defaults, and tidy up
269  *      after the command line.
270  *
271  * RETURNS
272  *      void
273  *
274  * CAVEAT
275  *      Must be called after the command line has been parsed.
276  */
277 
278 void
option_tidy_up(void)279 option_tidy_up(void)
280 {
281     string_ty       *s;
282     string_ty       *s1;
283 
284     /*
285      * Set the defaults before we do anything else,
286      * the rest of tidy_up depends on them.
287      */
288     trace(("option_tidy_up()\n{\n"));
289     option_set(OPTION_ACTION, OPTION_LEVEL_DEFAULT, 1);
290     option_set(OPTION_CASCADE, OPTION_LEVEL_DEFAULT, 1);
291     option_set(OPTION_FINGERPRINT_WRITE, OPTION_LEVEL_DEFAULT, 1);
292     option_set(OPTION_INCLUDE_COOKED, OPTION_LEVEL_DEFAULT, 1);
293     option_set(OPTION_INCLUDE_COOKED_WARNING, OPTION_LEVEL_DEFAULT, 1);
294     option_set(OPTION_LOGGING, OPTION_LEVEL_DEFAULT, 1);
295     option_set(OPTION_STRIP_DOT, OPTION_LEVEL_DEFAULT, 1);
296     option_set(OPTION_TERMINAL, OPTION_LEVEL_DEFAULT, 1);
297 
298     /*
299      * user's library
300      */
301     s = os_accdir();
302     assert(s);
303     s1 = str_format("%s/.%s", s->str_text, progname_get());
304     str_free(s);
305     string_list_append_unique(&option.o_search_path, s1);
306     str_free(s1);
307 
308     /*
309      * cook's library
310      *      architecture-specific, then architecture-neutral
311      */
312     s = str_from_c(library_directory_get());
313     string_list_append_unique(&option.o_search_path, s);
314     str_free(s);
315     s = str_from_c(data_directory_get());
316     string_list_append_unique(&option.o_search_path, s);
317     str_free(s);
318 
319     if (!option.o_book)
320     {
321         static char *name[] =
322         {
323             ".how.to.%s",
324             ".howto.%s",
325             "how.to.%s",
326             "howto.%s",
327             "%s.file",
328             "%sfile",
329             "%s.book",
330             "%sbook",
331             ".%s.rc",
332             ".%src",
333         };
334         size_t          j;
335 
336         /*
337          * A huge range of alternative default names is given.
338          * The first found will be used.
339          */
340         for (j = 0; j < SIZEOF(name); j++)
341         {
342             s = str_format(name[j], progname_get());
343             switch (os_exists(s))
344             {
345             case -1:
346                 exit(1);
347 
348             case 0:
349                 s1 = Capitalize(s);
350                 str_free(s);
351                 s = s1;
352                 switch (os_exists(s))
353                 {
354                 case -1:
355                     exit(1);
356 
357                 case 0:
358                     str_free(s);
359                     continue;
360 
361                 case 1:
362                     option.o_book = s;
363                     break;
364                 }
365                 break;
366 
367             case 1:
368                 option.o_book = s;
369                 break;
370             }
371             break;
372         }
373     }
374 
375     if (!option.o_logfile && option.o_book)
376     {
377         char            *sp;
378         char            *cp;
379 
380         sp = option.o_book->str_text;
381         /* skip first char in case it's a '.' */
382         cp = strrchr(sp + 1, '.');
383         if (cp)
384             s = str_n_from_c(sp, cp - sp);
385         else
386             s = str_copy(option.o_book);
387         sp = (option_test(OPTION_CMDFILE) ? "sh" : "list");
388         option.o_logfile = str_format("%s.%s", s->str_text, sp);
389         str_free(s);
390     }
391     trace(("}\n"));
392 }
393 
394 
395 /*
396  * NAME
397  *      option_set_errors - set error flags
398  *
399  * SYNOPSIS
400  *      void option_set_errors(void);
401  *
402  * DESCRIPTION
403  *      The option_set_errors function is used to set the appropriate options
404  *      to prevent undesirable side effects when errors occur.
405  *
406  * RETURNS
407  *      void
408  */
409 
410 void
option_set_errors(void)411 option_set_errors(void)
412 {
413     trace(("option_set_errors()\n{\n"));
414     option_set(OPTION_SILENT, OPTION_LEVEL_ERROR, 0);
415     option_set(OPTION_ACTION, OPTION_LEVEL_ERROR, 0);
416     option_set(OPTION_ERROK, OPTION_LEVEL_ERROR, 0);
417     option_set(OPTION_METER, OPTION_LEVEL_ERROR, 0);
418     option_set(OPTION_PERSEVERE, OPTION_LEVEL_ERROR, 0);
419     trace(("}\n"));
420 }
421 
422 
423 void *
option_flag_state_get(void)424 option_flag_state_get(void)
425 {
426     flag_ty         *fp;
427 
428     fp = mem_alloc(sizeof(flag_ty));
429     *fp = flag;
430     return fp;
431 }
432 
433 
434 void
option_flag_state_set(void * p)435 option_flag_state_set(void *p)
436 {
437     flag_ty         *fp;
438 
439     fp = p;
440     flag = *fp;
441     mem_free(p);
442 }
443 
444 
445 const char *
option_level_name(option_level_ty lvl)446 option_level_name(option_level_ty lvl)
447 {
448     switch (lvl)
449     {
450     case OPTION_LEVEL_ERROR:
451         return "OPTION_LEVEL_ERROR";
452 
453     case OPTION_LEVEL_AUTO:
454         return "OPTION_LEVEL_AUTO";
455 
456     case OPTION_LEVEL_COMMAND_LINE:
457         return "OPTION_LEVEL_COMMAND_LINE";
458 
459     case OPTION_LEVEL_EXECUTE:
460         return "OPTION_LEVEL_EXECUTE";
461 
462     case OPTION_LEVEL_RECIPE:
463         return "OPTION_LEVEL_RECIPE";
464 
465     case OPTION_LEVEL_COOKBOOK:
466         return "OPTION_LEVEL_COOKBOOK";
467 
468     case OPTION_LEVEL_ENVIRONMENT:
469         return "OPTION_LEVEL_ENVIRONMENT";
470 
471     case OPTION_LEVEL_DEFAULT:
472         return "OPTION_LEVEL_DEFAULT";
473     }
474     return "option level unknown";
475 }
476 
477 
478 const char *
option_number_name(option_number_ty o)479 option_number_name(option_number_ty o)
480 {
481     switch (o)
482     {
483     case OPTION_ACTION:
484         return "OPTION_ACTION";
485 
486     case OPTION_BOOK:
487         return "OPTION_BOOK";
488 
489     case OPTION_CASCADE:
490         return "OPTION_CASCADE";
491 
492     case OPTION_CMDFILE:
493         return "OPTION_CMDFILE";
494 
495     case OPTION_DISASSEMBLE:
496         return "OPTION_DISASSEMBLE";
497 
498     case OPTION_ERROK:
499         return "OPTION_ERROK";
500 
501     case OPTION_FINGERPRINT:
502         return "OPTION_FINGERPRINT";
503 
504     case OPTION_FINGERPRINT_WRITE:
505         return "OPTION_FINGERPRINT_WRITE";
506 
507     case OPTION_FORCE:
508         return "OPTION_FORCE";
509 
510     case OPTION_GATEFIRST:
511         return "OPTION_GATEFIRST";
512 
513     case OPTION_IMPLICIT_ALLOWED:
514         return "OPTION_IMPLICIT_ALLOWED";
515 
516     case OPTION_INCLUDE_COOKED:
517         return "OPTION_INCLUDE_COOKED";
518 
519     case OPTION_INCLUDE_COOKED_WARNING:
520         return "OPTION_INCLUDE_COOKED_WARNING";
521 
522     case OPTION_INGREDIENTS_FINGERPRINT:
523         return "OPTION_INGREDIENTS_FINGERPRINT";
524 
525     case OPTION_INVALIDATE_STAT_CACHE:
526         return "OPTION_INVALIDATE_STAT_CACHE";
527 
528     case OPTION_LOGGING:
529         return "OPTION_LOGGING";
530 
531     case OPTION_METER:
532         return "OPTION_METER";
533 
534     case OPTION_MKDIR:
535         return "OPTION_MKDIR";
536 
537     case OPTION_PERSEVERE:
538         return "OPTION_PERSEVERE";
539 
540     case OPTION_PRECIOUS:
541         return "OPTION_PRECIOUS";
542 
543     case OPTION_REASON:
544         return "OPTION_REASON";
545 
546     case OPTION_RECURSE:
547         return "OPTION_RECURSE";
548 
549     case OPTION_SHALLOW:
550         return "OPTION_SHALLOW";
551 
552     case OPTION_SILENT:
553         return "OPTION_SILENT";
554 
555     case OPTION_STAR:
556         return "OPTION_STAR";
557 
558     case OPTION_STRIP_DOT:
559         return "OPTION_STRIP_DOT";
560 
561     case OPTION_SYMLINK_INGREDIENTS:
562         return "OPTION_SYMLINK_INGREDIENTS";
563 
564     case OPTION_TERMINAL:
565         return "OPTION_TERMINAL";
566 
567     case OPTION_TOUCH:
568         return "OPTION_TOUCH";
569 
570     case OPTION_UNLINK:
571         return "OPTION_UNLINK";
572 
573     case OPTION_UPDATE:
574         return "OPTION_UPDATE";
575 
576     case OPTION_UPDATE_MAX:
577         return "OPTION_UPDATE_MAX";
578 
579     case OPTION_MATCH_MODE_REGEX:
580         return "OPTION_MATCH_MODE_REGEX";
581 
582     case OPTION_TELL_POSITION:
583         return "OPTION_TELL_POSITION";
584 
585     case OPTION_max:
586         break;
587     }
588     return "option number unknown";
589 }
590