Add externs to avoid multiple definitions, and then add missing definitions.
[rrq/maintain_lilo.git] / src / cfg.c
1 /* cfg.c  -  Configuration file parser
2  * 
3  * Copyright 1992-1997 Werner Almesberger
4  * Copyright 1999-2007 John Coffman
5  * Copyright 2009-2011 Joachim Wiedorn
6  * All rights reserved.
7  * 
8  * Licensed under the terms contained in the file 'COPYING'
9  * in the source directory.
10  */
11
12 #define _GNU_SOURCE
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <fcntl.h>
17 #include <errno.h>
18 #include <stdarg.h>
19 #include <ctype.h>
20 #include <string.h>
21
22 #include "lilo.h"
23 #include "common.h"
24 #include "temp.h"
25 #include "cfg.h"
26
27 #define NEW_PARSE !__MSDOS__
28
29 #define MAX_VAR_NAME MAX_TOKEN
30
31 #if !__MSDOS__
32 extern void do_image(void);
33 extern void do_other(void);
34 extern void do_disk(void);
35 extern void do_partition(void);
36
37 extern void id_image(void);
38 extern void id_other(void);
39
40 extern void do_map_drive(void);
41 extern void do_cr(void);
42 extern void do_change(void);
43 extern void do_cr_type(void);
44 extern void do_cr_reset(void);
45 extern void do_cr_part(void);
46 extern void do_cr_auto(void);
47
48
49 CONFIG cf_top[] = {
50   { cft_strg, "image",          do_image,       NULL,NULL },
51   { cft_strg, "other",          do_other,       NULL,NULL },
52   { cft_end,  NULL,             NULL,           NULL,NULL }};
53
54 CONFIG cf_identify[] = {
55   { cft_strg, "image",          id_image,       NULL,NULL },
56   { cft_strg, "other",          id_other,       NULL,NULL },
57   { cft_end,  NULL,             NULL,           NULL,NULL }};
58 #endif /* !__MSDOS__ */
59
60 CONFIG cf_options[] = {
61 #if !__MSDOS__
62   { cft_strg, "append",         NULL,           NULL,NULL },
63   { cft_strg, "backup",         NULL,           NULL,NULL },
64   { cft_strg, "bios-passes-dl", NULL,           NULL,NULL },
65   { cft_strg, "bitmap",         NULL,           NULL,NULL },
66   { cft_strg, "bmp-colors",     NULL,           NULL,NULL },
67   { cft_flag, "bmp-retain",     NULL,           NULL,NULL },
68   { cft_strg, "bmp-table",      NULL,           NULL,NULL },
69   { cft_strg, "bmp-timer",      NULL,           NULL,NULL },
70   { cft_strg, "boot",           NULL,           NULL,NULL },
71   { cft_strg, "boot-as",        NULL,           NULL,NULL },
72   { cft_flag, "change-rules",   do_cr,          NULL,NULL },
73   { cft_flag, "compact",        NULL,           NULL,NULL },
74   { cft_strg, "default",        NULL,           NULL,NULL },
75   { cft_strg, "delay",          NULL,           NULL,NULL },
76   { cft_strg, "disk",           do_disk,        NULL,NULL },
77   { cft_strg, "disktab",        NULL,           NULL,NULL },
78   { cft_flag, "el-torito-bootable-cd",  NULL,           NULL,NULL },
79   { cft_strg, "fallback",       NULL,           NULL,NULL },
80   { cft_flag, "fix-table",      NULL,           NULL,NULL },
81   { cft_strg, "force-backup",   NULL,           NULL,NULL },
82   { cft_flag, "geometric",      NULL,           NULL,NULL }, 
83   { cft_flag, "ignore-table",   NULL,           NULL,NULL },
84   { cft_strg, "initrd",         NULL,           NULL,NULL },
85   { cft_strg, "install",        NULL,           NULL,NULL },
86   { cft_strg, "keytable",       NULL,           NULL,NULL },
87   { cft_flag, "large-memory",   NULL,           NULL,NULL },
88   { cft_flag, "lba32",          NULL,           NULL,NULL }, 
89   { cft_flag, "linear",         NULL,           NULL,NULL },
90   { cft_strg, "loader",         NULL,           NULL,NULL },
91   { cft_flag, "lock",           NULL,           NULL,NULL },
92   { cft_flag, "mandatory",      NULL,           NULL,NULL },
93 #endif /* !__MSDOS__ */
94   { cft_strg, "map",            NULL,           NULL,NULL },
95 #if !__MSDOS__
96   { cft_flag, "master-boot",    NULL,           NULL,NULL },
97   { cft_strg, "menu-scheme",    NULL,           NULL,NULL },
98   { cft_strg, "menu-title",     NULL,           NULL,NULL },
99   { cft_strg, "message",        NULL,           NULL,NULL },
100   { cft_flag, "nodevcache",     NULL,           NULL,NULL },
101 #ifdef LCF_NOKEYBOARD
102   { cft_strg, "nokbdefault",    NULL,           NULL,NULL },
103 #endif
104   { cft_flag, "noraid",         NULL,           NULL,NULL },
105   { cft_flag, "nowarn",         NULL,           NULL,NULL },
106   { cft_flag, "optional",       NULL,           NULL,NULL },
107   { cft_strg, "password",       NULL,           NULL,NULL },
108   { cft_flag, "prompt",         NULL,           NULL,NULL },
109   { cft_strg, RAID_EXTRA_BOOT,  NULL,           NULL,NULL },
110   { cft_strg, "ramdisk",        NULL,           NULL,NULL },
111   { cft_flag, "read-only",      NULL,           NULL,NULL },
112   { cft_flag, "read-write",     NULL,           NULL,NULL },
113   { cft_flag, "restricted",     NULL,           NULL,NULL },
114   { cft_strg, "root",           NULL,           NULL,NULL },
115   { cft_strg, "serial",         NULL,           NULL,NULL },
116   { cft_flag, "single-key",     NULL,           NULL,NULL },
117   { cft_flag, "small-memory",   NULL,           NULL,NULL },
118   { cft_flag, "static-bios-codes",      NULL,           NULL,NULL },
119   { cft_flag, "suppress-boot-time-BIOS-data",   NULL,   NULL,NULL },
120   { cft_strg, "timeout",        NULL,           NULL,NULL },
121   { cft_flag, "unattended",     NULL,           NULL,NULL },
122   { cft_strg, "verbose",        NULL,           NULL,NULL },
123   { cft_strg, "vga",            NULL,           NULL,NULL },
124 #ifdef LCF_VIRTUAL
125   { cft_strg, "vmdefault",      NULL,           NULL,NULL },
126 #endif
127 #endif /* !__MSDOS__ */
128   { cft_end,  NULL,             NULL,           NULL,NULL }};
129
130 #if !__MSDOS__
131 CONFIG cf_all[] = {
132   { cft_strg, "alias",          NULL,           NULL,NULL },
133   { cft_flag, "bmp-retain",     NULL,           NULL,NULL },
134   { cft_flag, "bypass",         NULL,           NULL,NULL },
135   { cft_strg, "fallback",       NULL,           NULL,NULL },
136   { cft_strg, "label",          NULL,           NULL,NULL },
137   { cft_strg, "literal",        NULL,           NULL,NULL },
138   { cft_flag, "lock",           NULL,           NULL,NULL },
139   { cft_flag, "mandatory",      NULL,           NULL,NULL },
140 #ifdef LCF_NOKEYBOARD
141   { cft_flag, "nokbdisable",    NULL,           NULL,NULL },
142 #endif
143   { cft_flag, "optional",       NULL,           NULL,NULL },
144   { cft_strg, "password",       NULL,           NULL,NULL },
145   { cft_flag, "restricted",     NULL,           NULL,NULL },
146   { cft_flag, "single-key",     NULL,           NULL,NULL },
147 #ifdef LCF_VIRTUAL
148   { cft_flag, "vmdisable",      NULL,           NULL,NULL },
149   { cft_flag, "vmwarn",         NULL,           NULL,NULL },
150 #endif
151   { cft_end,  NULL,             NULL,           NULL,NULL }};
152
153 CONFIG cf_kernel[] = {
154   { cft_strg, "addappend",      NULL,           NULL,NULL },
155   { cft_strg, "append",         NULL,           NULL,NULL },
156   { cft_strg, "initrd",         NULL,           NULL,NULL },
157   { cft_strg, "ramdisk",        NULL,           NULL,NULL },
158   { cft_flag, "read-only",      NULL,           NULL,NULL },
159   { cft_flag, "read-write",     NULL,           NULL,NULL },
160   { cft_strg, "root",           NULL,           NULL,NULL },
161   { cft_strg, "vga",            NULL,           NULL,NULL },
162   { cft_link, NULL,             &cf_all,        NULL,NULL }};
163
164 CONFIG cf_image[] = {
165   { cft_strg, "range",          NULL,           NULL,NULL },
166   { cft_link, NULL,             &cf_kernel,     NULL,NULL }};
167
168 CONFIG cf_other[] = {
169   { cft_strg, "boot-as",        NULL,           NULL,NULL },
170   { cft_flag, "change",         do_change,      NULL,NULL },
171   { cft_strg, "loader",         NULL,           NULL,NULL },
172   { cft_strg, "map-drive",      do_map_drive,   NULL,NULL },
173   { cft_flag, "master-boot",    NULL,           NULL,NULL },
174   { cft_strg, "table",          NULL,           NULL,NULL },
175   { cft_flag, "unsafe",         NULL,           NULL,NULL },
176   { cft_link, NULL,             &cf_all,        NULL,NULL }};
177
178 CONFIG cf_disk[] = {
179   { cft_strg, "bios",           NULL,           NULL,NULL },
180   { cft_strg, "cylinders",      NULL,           NULL,NULL },
181   { cft_strg, "heads",          NULL,           NULL,NULL },
182   { cft_flag, "inaccessible",   NULL,           NULL,NULL },
183   { cft_strg, "max-partitions", NULL,           NULL,NULL },
184   { cft_strg, "sectors",        NULL,           NULL,NULL },
185   { cft_end,  NULL,             NULL,           NULL,NULL }};
186
187 CONFIG cf_partitions[] = {
188   { cft_strg, "partition",      do_partition,   NULL,NULL },
189   { cft_end,  NULL,             NULL,           NULL,NULL }};
190
191 CONFIG cf_partition[] = {
192   { cft_strg, "start",          NULL,           NULL,NULL },
193   { cft_end,  NULL,             NULL,           NULL,NULL }};
194
195 CONFIG cf_map_drive[] = {
196   { cft_strg, "to",             NULL,           NULL,NULL },
197   { cft_end,  NULL,             NULL,           NULL,NULL }};
198
199 CONFIG cf_change_rules[] = {
200   { cft_flag, "reset",          do_cr_reset,    NULL,NULL },
201   { cft_strg, "type",           do_cr_type,     NULL,NULL },
202   { cft_end,  NULL,             NULL,           NULL,NULL }};
203
204 CONFIG cf_change_rule[] = {
205   { cft_strg, "hidden",         NULL,           NULL,NULL },
206   { cft_strg, "normal",         NULL,           NULL,NULL },
207   { cft_end,  NULL,             NULL,           NULL,NULL }};
208
209 CONFIG cf_change[] = {
210   { cft_flag, "automatic",      do_cr_auto,     NULL,NULL },
211   { cft_strg, "partition",      do_cr_part,     NULL,NULL },
212   { cft_end,  NULL,             NULL,           NULL,NULL }};
213
214 CONFIG cf_change_dsc[] = {
215   { cft_flag, "activate",       NULL,           NULL,NULL },
216   { cft_flag, "deactivate",     NULL,           NULL,NULL },
217   { cft_strg, "set",            NULL,           NULL,NULL },
218   { cft_end,  NULL,             NULL,           NULL,NULL }};
219
220 CONFIG cf_bitmap[] = {
221   { cft_strg, "bitmap",         NULL,           NULL,NULL },
222   { cft_strg, "bmp-colors",     NULL,           NULL,NULL },
223   { cft_strg, "bmp-table",      NULL,           NULL,NULL },
224   { cft_strg, "bmp-timer",      NULL,           NULL,NULL },
225   { cft_end,  NULL,             NULL,           NULL,NULL }};
226 #endif /* !__MSDOS__ */
227
228 #if NEW_PARSE
229
230 static CONFIG *keywords[] = {cf_top, cf_identify, cf_options, cf_all,
231         cf_kernel, cf_image, cf_other, cf_disk, cf_partitions, cf_partition,
232         cf_map_drive, cf_change_rules, cf_change_rule, cf_change, 
233         cf_change_dsc, cf_bitmap, NULL };
234
235 #endif
236         
237 static FILE *file;
238 static char flag_set;
239 static char *last_token = NULL,*last_item = NULL,*last_value = NULL;
240 static int line_num;
241 static char *file_name = NULL;
242 static int back = 0; /* can go back by one char */
243
244
245 int cfg_open(char *name)
246 {
247     line_num = 1;
248     if (!strcmp(name,"-")) file = stdin;
249
250 #if __MSDOS__
251     else if (!strcasecmp(name,"none")) return -1;
252 #endif /* __MSDOS__ */
253
254     else if (!(file = fopen(file_name = name,"r"))) {
255 #if !__MSDOS__
256                 die("Cannot open: %s", name);
257 #else  /* __MSDOS__ */
258                 warn("No configuration file: %s\n", name);
259                 return -1;
260 #endif /* __MSDOS__ */
261     }
262
263     return fileno(file);
264 }
265
266
267 void __attribute__ ((format (printf, 1, 2))) cfg_error(char *msg,...)
268 {
269     va_list ap;
270
271     fflush(stdout);
272     va_start(ap,msg);
273     vfprintf(errstd,msg,ap);
274     va_end(ap);
275     if (!file_name) fputc('\n',errstd);
276     else fprintf(errstd," at or above line %d in file '%s'\n",line_num,file_name);
277     exit(1);
278 }
279
280
281 static int next_raw(void)
282 {
283     int ch;
284
285     if (!back) return getc(file);
286     ch = back;
287     back = 0;
288     return ch;
289 }
290
291
292 static int next(void)
293 {
294     static char *var;
295     char buffer[MAX_VAR_NAME+1];
296     int ch,braced;
297     char *put;
298
299     if (back) {
300         ch = back;
301         back = 0;
302         return ch;
303     }
304     if (var && *var) return *var++;
305     ch = getc(file);
306     if (ch == '\r')             /* strip DOS <cr> */
307         ch = getc(file);
308     if (ch == '\\') {
309         ch = getc(file);
310         if (ch == '$') return ch;
311         ungetc(ch,file);
312         return '\\';
313     }
314     if (ch != '$') return ch;
315     ch = getc(file);
316     braced = ch == '{';
317     put = buffer;
318     if (!braced) *put++ = ch;
319     while (1) {
320         ch = getc(file);
321 #if 0
322         if (!braced && ch < ' ') {
323             ungetc(ch,file);
324             break;
325         }
326 #endif
327         if (ch == EOF) cfg_error("EOF in variable name");
328         if (ch < ' ') cfg_error("control character in variable name");
329         if (braced && ch == '}') break;
330         if (!braced && !isalpha(ch) && !isdigit(ch) && ch != '_') {
331             ungetc(ch,file);
332             break;
333         }
334         if (put-buffer == MAX_VAR_NAME) cfg_error("variable name too long");
335         *put++ = ch;
336     }
337     *put = 0;
338 #if !__MSDOS__
339     if (!(var = getenv(buffer))) cfg_error("unknown variable \"%s\"",buffer);
340 #endif /* !__MSDOS__ */
341     return next();
342 }
343
344
345 static void again(int ch)
346 {
347     if (back) die("internal error: again invoked twice");
348     back = ch;
349 }
350
351
352 static char *cfg_get_token(void)
353 {
354     char buf[MAX_TOKEN+1];
355     char *here;
356     int ch,escaped;
357
358     if (last_token) {
359         here = last_token;
360         last_token = NULL;
361         return here;
362     }
363     while (1) {
364         while ((ch = next()), ch == ' ' || ch == '\t' || ch == '\n')
365             if (ch == '\n') line_num++;
366         if (ch == EOF) return NULL;
367         if (ch != '#') break;
368         while ((ch = next_raw()), ch != '\n')
369             if (ch == EOF) return NULL;
370         line_num++;
371     }
372     if (ch == '=') return stralloc("=");
373     if (ch == '"') {
374         here = buf;
375         while (here-buf < MAX_TOKEN) {
376             if ((ch = next()) == EOF) cfg_error("EOF in quoted string");
377             if (ch == '"') {
378                 *here = 0;
379                 return stralloc(buf);
380             }
381             if (ch == '\\') {
382                 ch = next();
383                 if (ch != '"' && ch != '\\' && ch != '\n')
384                     cfg_error("Bad use of \\ in quoted string");
385                 if (ch == '\n') {
386                     while ((ch = next()), ch == ' ' || ch == '\t');
387                     if (!ch) continue;
388                     again(ch);
389                     ch = ' ';
390                 }
391             }
392             if (ch == '\n' || ch == '\t')
393                 cfg_error("\\n and \\t are not allowed in quoted strings");
394             *here++ = ch;
395         }
396         cfg_error("Quoted string is too long");
397         return 0; /* not reached */
398     }
399     here = buf;
400     escaped = 0;
401     while (here-buf < MAX_TOKEN) {
402         if (escaped) {
403             if (ch == EOF) cfg_error("\\ precedes EOF");
404             if (ch == '\n') line_num++;
405             else *here++ = ch == '\t' ? ' ' : ch;
406             escaped = 0;
407         }
408         else {
409             if (ch == ' ' || ch == '\t' || ch == '\n' || ch == '#' ||
410               ch == '=' || ch == EOF) {
411                 again(ch);
412                 *here = 0;
413                 return stralloc(buf);
414             }
415 #if !__MSDOS__
416             if (!(escaped = (ch == '\\')))
417 #endif /* !__MSDOS__ */
418                 *here++ = ch;
419         }
420         ch = next();
421     }
422     cfg_error("Token is too long");
423     return 0; /* not reached */
424 }
425
426
427 static void cfg_return_token(char *token)
428 {
429     last_token = token;
430 }
431
432
433 static int cfg_next(char **item,char **value)
434 {
435     char *this;
436
437     if (last_item) {
438         *item = last_item;
439         *value = last_value;
440         last_item = NULL;
441         return 1;
442     }
443     *value = NULL;
444     if (!(*item = cfg_get_token())) return 0;
445     if (!strcmp(*item,"=")) cfg_error("Syntax error");
446     if (!(this = cfg_get_token())) return 1;
447     if (strcmp(this,"=")) {
448         cfg_return_token(this);
449         return 1;
450     }
451     if (!(*value = cfg_get_token())) cfg_error("Value expected at EOF");
452     if (!strcmp(*value,"=")) cfg_error("Syntax error after %s",*item);
453     return 1;
454 }
455
456
457 static void cfg_return(char *item,char *value)
458 {
459     last_item = item;
460     last_value = value;
461 }
462
463
464 void cfg_init(CONFIG *table)
465 {
466     while (table->type != cft_end) {
467         switch (table->type) {
468             case cft_strg:
469                 if (table->data) free(table->data);
470             case cft_flag:
471                 table->data = NULL;
472                 break;
473             case cft_link:
474                 table = ((CONFIG *) table->action)-1;
475                 break;
476             default:
477                 die("Unknown syntax code %d",table->type);
478         }
479         table++;
480     }
481 }
482
483
484 static int cfg_do_set(CONFIG *table,char *item,char *value,int copy,
485     void *context)
486 {
487     CONFIG *walk;
488
489     for (walk = table; walk->type != cft_end; walk++) {
490         if (walk->name && !strcasecmp(walk->name,item)) {
491             if (value && walk->type != cft_strg)
492                 cfg_error("'%s' doesn't have a value",walk->name);
493             if (!value && walk->type == cft_strg)
494                 cfg_error("Value expected for '%s'",walk->name);
495             if (walk->data) {
496                 if (walk->context == context)
497                     cfg_error("Duplicate entry '%s'",walk->name);
498                 else {
499                     warn("Ignoring entry '%s'",walk->name);
500                     if (!copy) free(value);
501                     return 1;
502                 }
503             }
504             if (walk->type == cft_flag) walk->data = &flag_set;
505             else if (walk->type == cft_strg)
506                     walk->data = copy ? stralloc(value) : value; 
507             walk->context = context;
508             if (walk->action) ((void (*)(void)) walk->action)();
509             break;
510         }
511         if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1;
512     }
513     if (walk->type != cft_end) return 1;
514     cfg_return(item,value);
515     return 0;
516 }
517
518
519 void cfg_set(CONFIG *table,char *item,char *value,void *context)
520 {
521     if (cfg_do_set(table,item,value,1,context) != 1)
522         cfg_error("cfg_set: Can't set %s",item);
523 }
524
525
526 void cfg_unset(CONFIG *table,char *item)
527 {
528     CONFIG *walk;
529
530     for (walk = table; walk->type != cft_end; walk++)
531         if (walk->name && !strcasecmp(walk->name,item)) {
532             if (!walk->data) die("internal error (cfg_unset %s, unset)",item);
533             if (walk->type == cft_strg) free(walk->data);
534             walk->data = NULL;
535             return;
536         }
537     die("internal error (cfg_unset %s, unknown",item);
538 }
539
540 #if NEW_PARSE
541
542 static int cfg_end (char *item)
543 {
544     CONFIG **key;
545     CONFIG *c;
546
547 key = keywords;
548 while ((c = *key++)) {
549     while (c->name) {
550         if (!strcasecmp(c->name, item)) return 1;
551         c++;
552     }
553 }
554     return 0;
555 }
556 #endif
557
558
559 int cfg_parse(CONFIG *table)
560 {
561     char *item,*value;
562
563     while (1) {
564         if (!cfg_next(&item,&value)) return 0;
565 if(verbose>=6) printf("cfg_parse:  item=\"%s\" value=\"%s\"\n", item, value);
566         if (!cfg_do_set(table,item,value,0,table)) {
567 #if NEW_PARSE==0
568                 return 1;
569 #else
570             if (cfg_end(item)) return 1;
571             else cfg_error("Unrecognized token \"%s\"", item);
572 #endif
573         }
574         free(item);
575     }
576 }
577
578
579 int cfg_get_flag(CONFIG *table,char *item)
580 {
581     CONFIG *walk;
582
583     for (walk = table; walk->type != cft_end; walk++) {
584         if (walk->name && !strcasecmp(walk->name,item)) {
585             if (walk->type != cft_flag)
586                 die("cfg_get_flag: operating on non-flag %s",item);
587             return !!walk->data;
588         }
589         if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1;
590     }
591     die("cfg_get_flag: unknown item %s",item);
592     return 0; /* not reached */
593 }
594
595
596 char *cfg_get_strg(CONFIG *table,char *item)
597 {
598     CONFIG *walk;
599
600     for (walk = table; walk->type != cft_end; walk++) {
601         if (walk->name && !strcasecmp(walk->name,item)) {
602             if (walk->type != cft_strg)
603                 die("cfg_get_strg: operating on non-string %s",item);
604             return walk->data;
605         }
606         if (walk->type == cft_link) walk = ((CONFIG *) walk->action)-1;
607     }
608     die("cfg_get_strg: unknown item %s",item);
609     return 0; /* not reached */
610 }
611
612
613 #if !__MSDOS__
614 /* open the password file, passw=1 forces a new file to be created */
615
616 static char *pw_file_name;
617 FILE *pw_file = NULL;
618
619 FILE *cfg_pw_open(void)
620 {
621     char name[MAX_TOKEN+1];
622     struct stat buf;
623     time_t conf;
624     int fd;
625     
626     strcpy(name, file_name);    /* copy name of config file '/etc/lilo.conf' */
627     strcat(name, PW_FILE_SUFFIX);
628     pw_file_name = stralloc(name);
629 #if 1
630     if (stat(file_name, &buf)) die("Cannot stat '%s'", file_name);
631     conf = buf.st_mtime;
632     if (!stat(pw_file_name, &buf) && conf>buf.st_mtime && !passw) {
633         warn("'%s' more recent than '%s'\n"
634               "   Running 'lilo -p' is recommended.",
635               file_name, pw_file_name);
636     }
637 #endif    
638
639     if (passw & !test) {
640         if (unlink(pw_file_name) && errno != ENOENT) die("Could not delete '%s'", pw_file_name);
641         if ((fd = creat(pw_file_name, 0600)) < 0) die("Could not create '%s'", pw_file_name);
642         (void) close(fd);
643         pw_file = fopen(pw_file_name, "w+");
644     }
645     else if (passw) return (pw_file=NULL);
646     else {
647         if (!(pw_file = fopen(pw_file_name, "r"))) {
648             if (!test) {
649                 passw = 1;                      /* force writing */
650                 if ((fd = creat(pw_file_name, 0600)) < 0) die("Could not create '%s'", pw_file_name);
651                 (void) close(fd);
652                 pw_file = fopen(pw_file_name, "w+");
653             }
654             else return (pw_file=NULL);
655         }
656     }
657     if (!pw_file) die("Could not create '%s'", pw_file_name);
658 #if 1
659     if (!stat(pw_file_name, &buf) && (buf.st_mode&0044) ) {
660         warn("'%s' readable by other than 'root'", pw_file_name);
661     }
662 #endif    
663     return pw_file;
664 }
665
666
667 /* allow only the "bitmap" keywords */
668
669 void cfg_bitmap_only(void)
670 {
671     keywords[0] = cf_bitmap;
672     keywords[1] = NULL;
673 }
674
675 #if BETA_TEST
676 void cfg_alpha_check(void)
677 {
678     CONFIG **kw = keywords;
679     CONFIG *cfg;
680     
681     while ((cfg=*kw)) {
682         while (cfg[1].name) {
683             if (strcmp(cfg[0].name, cfg[1].name) >= 0) {
684                 die("cfg_alpha_check:  failure at '%s', '%s'", cfg[0].name, cfg[1].name);
685             }
686         cfg++;
687         }
688     kw++;
689     }
690 }
691 #endif
692
693 #endif /* !__MSDOS__ */