]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/cron/lib/entry.c
zfs: merge openzfs/zfs@a382e2119
[FreeBSD/FreeBSD.git] / usr.sbin / cron / lib / entry.c
1 /*
2  * Copyright 1988,1990,1993,1994 by Paul Vixie
3  * All rights reserved
4  */
5
6 /*
7  * Copyright (c) 1997 by Internet Software Consortium
8  *
9  * Permission to use, copy, modify, and distribute this software for any
10  * purpose with or without fee is hereby granted, provided that the above
11  * copyright notice and this permission notice appear in all copies.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
14  * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
15  * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
16  * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
17  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
18  * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
19  * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
20  * SOFTWARE.
21  */
22
23 #if !defined(lint) && !defined(LINT)
24 static const char rcsid[] =
25     "$Id: entry.c,v 1.3 1998/08/14 00:32:39 vixie Exp $";
26 #endif
27
28 /* vix 26jan87 [RCS'd; rest of log is in RCS file]
29  * vix 01jan87 [added line-level error recovery]
30  * vix 31dec86 [added /step to the from-to range, per bob@acornrc]
31  * vix 30dec86 [written]
32  */
33
34
35 #include "cron.h"
36 #include <grp.h>
37 #ifdef LOGIN_CAP
38 #include <login_cap.h>
39 #endif
40
41 typedef enum ecode {
42         e_none, e_minute, e_hour, e_dom, e_month, e_dow,
43         e_cmd, e_timespec, e_username, e_group, e_option,
44         e_mem
45 #ifdef LOGIN_CAP
46         , e_class
47 #endif
48 } ecode_e;
49
50 static const char *ecodes[] =
51         {
52                 "no error",
53                 "bad minute",
54                 "bad hour",
55                 "bad day-of-month",
56                 "bad month",
57                 "bad day-of-week",
58                 "bad command",
59                 "bad time specifier",
60                 "bad username",
61                 "bad group name",
62                 "bad option",
63                 "out of memory",
64 #ifdef LOGIN_CAP
65                 "bad class name",
66 #endif
67         };
68
69 static char     get_list(bitstr_t *, int, int, const char *[], int, FILE *),
70                 get_range(bitstr_t *, int, int, const char *[], int, FILE *),
71                 get_number(int *, int, const char *[], int, FILE *);
72 static int      set_element(bitstr_t *, int, int, int);
73
74 void
75 free_entry(entry *e)
76 {
77 #ifdef LOGIN_CAP
78         if (e->class != NULL)
79                 free(e->class);
80 #endif
81         if (e->cmd != NULL)
82                 free(e->cmd);
83         if (e->envp != NULL)
84                 env_free(e->envp);
85         free(e);
86 }
87
88
89 /* return NULL if eof or syntax error occurs;
90  * otherwise return a pointer to a new entry.
91  */
92 entry *
93 load_entry(FILE *file, void (*error_func)(const char *), struct passwd *pw,
94     char **envp)
95 {
96         /* this function reads one crontab entry -- the next -- from a file.
97          * it skips any leading blank lines, ignores comments, and returns
98          * EOF if for any reason the entry can't be read and parsed.
99          *
100          * the entry is also parsed here.
101          *
102          * syntax:
103          *   user crontab:
104          *      minutes hours doms months dows cmd\n
105          *   system crontab (/etc/crontab):
106          *      minutes hours doms months dows USERNAME cmd\n
107          */
108
109         ecode_e ecode = e_none;
110         entry   *e;
111         int     ch;
112         int     len;
113         char    cmd[MAX_COMMAND];
114         char    envstr[MAX_ENVSTR];
115         char    **prev_env;
116
117         Debug(DPARS, ("load_entry()...about to eat comments\n"))
118
119         skip_comments(file);
120
121         ch = get_char(file);
122         if (ch == EOF)
123                 return NULL;
124
125         /* ch is now the first useful character of a useful line.
126          * it may be an @special or it may be the first character
127          * of a list of minutes.
128          */
129
130         e = (entry *) calloc(sizeof(entry), sizeof(char));
131
132         if (e == NULL) {
133                 warn("load_entry: calloc failed");
134                 return NULL;
135         }
136
137         if (ch == '@') {
138                 long interval;
139                 char *endptr;
140
141                 /* all of these should be flagged and load-limited; i.e.,
142                  * instead of @hourly meaning "0 * * * *" it should mean
143                  * "close to the front of every hour but not 'til the
144                  * system load is low".  Problems are: how do you know
145                  * what "low" means? (save me from /etc/cron.conf!) and:
146                  * how to guarantee low variance (how low is low?), which
147                  * means how to we run roughly every hour -- seems like
148                  * we need to keep a history or let the first hour set
149                  * the schedule, which means we aren't load-limited
150                  * anymore.  too much for my overloaded brain. (vix, jan90)
151                  * HINT
152                  */
153                 Debug(DPARS, ("load_entry()...about to test shortcuts\n"))
154                 ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
155                 if (!strcmp("reboot", cmd)) {
156                         Debug(DPARS, ("load_entry()...reboot shortcut\n"))
157                         e->flags |= WHEN_REBOOT;
158                 } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
159                         Debug(DPARS, ("load_entry()...yearly shortcut\n"))
160                         bit_set(e->second, 0);
161                         bit_set(e->minute, 0);
162                         bit_set(e->hour, 0);
163                         bit_set(e->dom, 0);
164                         bit_set(e->month, 0);
165                         bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
166                         e->flags |= DOW_STAR;
167                 } else if (!strcmp("monthly", cmd)) {
168                         Debug(DPARS, ("load_entry()...monthly shortcut\n"))
169                         bit_set(e->second, 0);
170                         bit_set(e->minute, 0);
171                         bit_set(e->hour, 0);
172                         bit_set(e->dom, 0);
173                         bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
174                         bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
175                         e->flags |= DOW_STAR;
176                 } else if (!strcmp("weekly", cmd)) {
177                         Debug(DPARS, ("load_entry()...weekly shortcut\n"))
178                         bit_set(e->second, 0);
179                         bit_set(e->minute, 0);
180                         bit_set(e->hour, 0);
181                         bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
182                         e->flags |= DOM_STAR;
183                         bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
184                         bit_set(e->dow, 0);
185                 } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
186                         Debug(DPARS, ("load_entry()...daily shortcut\n"))
187                         bit_set(e->second, 0);
188                         bit_set(e->minute, 0);
189                         bit_set(e->hour, 0);
190                         bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
191                         bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
192                         bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
193                 } else if (!strcmp("hourly", cmd)) {
194                         Debug(DPARS, ("load_entry()...hourly shortcut\n"))
195                         bit_set(e->second, 0);
196                         bit_set(e->minute, 0);
197                         bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
198                         bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
199                         bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
200                         bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
201                 } else if (!strcmp("every_minute", cmd)) {
202                         Debug(DPARS, ("load_entry()...every_minute shortcut\n"))
203                         bit_set(e->second, 0);
204                         bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1));
205                         bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
206                         bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
207                         bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
208                         bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
209                 } else if (!strcmp("every_second", cmd)) {
210                         Debug(DPARS, ("load_entry()...every_second shortcut\n"))
211                         e->flags |= SEC_RES;
212                         bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1));
213                         bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1));
214                         bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1));
215                         bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
216                         bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
217                         bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
218                 } else if (*cmd != '\0' &&
219                     (interval = strtol(cmd, &endptr, 10)) > 0 &&
220                     *endptr == '\0') {
221                         Debug(DPARS, ("load_entry()... %ld seconds "
222                             "since last run\n", interval))
223                         e->interval = interval;
224                         e->flags = INTERVAL;
225                 } else {
226                         ecode = e_timespec;
227                         goto eof;
228                 }
229                 /* Advance past whitespace between shortcut and
230                  * username/command.
231                  */
232                 Skip_Blanks(ch, file);
233                 if (ch == EOF) {
234                         ecode = e_cmd;
235                         goto eof;
236                 }
237         } else {
238                 Debug(DPARS, ("load_entry()...about to parse numerics\n"))
239                 bit_set(e->second, 0);
240
241                 ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
242                               PPC_NULL, ch, file);
243                 if (ch == EOF) {
244                         ecode = e_minute;
245                         goto eof;
246                 }
247
248                 /* hours
249                  */
250
251                 ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
252                               PPC_NULL, ch, file);
253                 if (ch == EOF) {
254                         ecode = e_hour;
255                         goto eof;
256                 }
257
258                 /* DOM (days of month)
259                  */
260
261                 if (ch == '*')
262                         e->flags |= DOM_STAR;
263                 ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
264                               PPC_NULL, ch, file);
265                 if (ch == EOF) {
266                         ecode = e_dom;
267                         goto eof;
268                 }
269
270                 /* month
271                  */
272
273                 ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
274                               MonthNames, ch, file);
275                 if (ch == EOF) {
276                         ecode = e_month;
277                         goto eof;
278                 }
279
280                 /* DOW (days of week)
281                  */
282
283                 if (ch == '*')
284                         e->flags |= DOW_STAR;
285                 ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
286                               DowNames, ch, file);
287                 if (ch == EOF) {
288                         ecode = e_dow;
289                         goto eof;
290                 }
291         }
292
293         /* make sundays equivalent */
294         if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
295                 bit_set(e->dow, 0);
296                 bit_set(e->dow, 7);
297         }
298
299         /* ch is the first character of a command, or a username */
300         unget_char(ch, file);
301
302         if (!pw) {
303                 char            *username = cmd;        /* temp buffer */
304                 char            *s;
305                 struct group    *grp;
306 #ifdef LOGIN_CAP
307                 login_cap_t *lc;
308 #endif
309
310                 Debug(DPARS, ("load_entry()...about to parse username\n"))
311                 ch = get_string(username, MAX_COMMAND, file, " \t");
312
313                 Debug(DPARS, ("load_entry()...got %s\n",username))
314                 if (ch == EOF) {
315                         ecode = e_cmd;
316                         goto eof;
317                 }
318
319                 /* need to have consumed blanks when checking options below */
320                 Skip_Blanks(ch, file)
321                 unget_char(ch, file);
322 #ifdef LOGIN_CAP
323                 if ((s = strrchr(username, '/')) != NULL) {
324                         *s = '\0';
325                         e->class = strdup(s + 1);
326                         if (e->class == NULL)
327                                 warn("strdup(\"%s\")", s + 1);
328                 } else {
329                         e->class = strdup(RESOURCE_RC);
330                         if (e->class == NULL)
331                                 warn("strdup(\"%s\")", RESOURCE_RC);
332                 }
333                 if (e->class == NULL) {
334                         ecode = e_mem;
335                         goto eof;
336                 }
337                 if ((lc = login_getclass(e->class)) == NULL) {
338                         ecode = e_class;
339                         goto eof;
340                 }
341                 login_close(lc);
342 #endif
343                 grp = NULL;
344                 if ((s = strrchr(username, ':')) != NULL) {
345                         *s = '\0';
346                         if ((grp = getgrnam(s + 1)) == NULL) {
347                                 ecode = e_group;
348                                 goto eof;
349                         }
350                 }
351
352                 pw = getpwnam(username);
353                 if (pw == NULL) {
354                         ecode = e_username;
355                         goto eof;
356                 }
357                 if (grp != NULL)
358                         pw->pw_gid = grp->gr_gid;
359                 Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid))
360 #ifdef LOGIN_CAP
361                 Debug(DPARS, ("load_entry()...class %s\n",e->class))
362 #endif
363         }
364
365 #ifndef PAM     /* PAM takes care of account expiration by itself */
366         if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
367                 ecode = e_username;
368                 goto eof;
369         }
370 #endif /* !PAM */
371
372         e->uid = pw->pw_uid;
373         e->gid = pw->pw_gid;
374
375         /* copy and fix up environment.  some variables are just defaults and
376          * others are overrides; we process only the overrides here, defaults
377          * are handled in do_command after login.conf is processed.
378          */
379         e->envp = env_copy(envp);
380         if (e->envp == NULL) {
381                 warn("env_copy");
382                 ecode = e_mem;
383                 goto eof;
384         }
385         if (!env_get("SHELL", e->envp)) {
386                 prev_env = e->envp;
387                 e->envp = env_set(e->envp, "SHELL=" _PATH_BSHELL);
388                 if (e->envp == NULL) {
389                         warn("env_set(%s)", "SHELL=" _PATH_BSHELL);
390                         env_free(prev_env);
391                         ecode = e_mem;
392                         goto eof;
393                 }
394         }
395         /* If LOGIN_CAP, this is deferred to do_command where the login class
396          * is processed. If !LOGIN_CAP, do it here.
397          */
398 #ifndef LOGIN_CAP
399         if (!env_get("HOME", e->envp)) {
400                 prev_env = e->envp;
401                 len = snprintf(envstr, sizeof(envstr), "HOME=%s", pw->pw_dir);
402                 if (len < (int)sizeof(envstr))
403                         e->envp = env_set(e->envp, envstr);
404                 if (len >= (int)sizeof(envstr) || e->envp == NULL) {
405                         warn("env_set(%s)", envstr);
406                         env_free(prev_env);
407                         ecode = e_mem;
408                         goto eof;
409                 }
410         }
411 #endif
412         prev_env = e->envp;
413         len = snprintf(envstr, sizeof(envstr), "LOGNAME=%s", pw->pw_name);
414         if (len < (int)sizeof(envstr))
415                 e->envp = env_set(e->envp, envstr);
416         if (len >= (int)sizeof(envstr) || e->envp == NULL) {
417                 warn("env_set(%s)", envstr);
418                 env_free(prev_env);
419                 ecode = e_mem;
420                 goto eof;
421         }
422 #if defined(BSD)
423         prev_env = e->envp;
424         len = snprintf(envstr, sizeof(envstr), "USER=%s", pw->pw_name);
425         if (len < (int)sizeof(envstr))
426                 e->envp = env_set(e->envp, envstr);
427         if (len >= (int)sizeof(envstr) || e->envp == NULL) {
428                 warn("env_set(%s)", envstr);
429                 env_free(prev_env);
430                 ecode = e_mem;
431                 goto eof;
432         }
433 #endif
434
435         Debug(DPARS, ("load_entry()...checking for command options\n"))
436
437         ch = get_char(file);
438
439         while (ch == '-') {
440                 Debug(DPARS|DEXT, ("load_entry()...expecting option\n"))
441                 switch (ch = get_char(file)) {
442                 case 'n':
443                         Debug(DPARS|DEXT, ("load_entry()...got MAIL_WHEN_ERR ('n') option\n"))
444                         /* only allow the user to set the option once */
445                         if ((e->flags & MAIL_WHEN_ERR) == MAIL_WHEN_ERR) {
446                                 Debug(DPARS|DEXT, ("load_entry()...duplicate MAIL_WHEN_ERR ('n') option\n"))
447                                 ecode = e_option;
448                                 goto eof;
449                         }
450                         e->flags |= MAIL_WHEN_ERR;
451                         break;
452                 case 'q':
453                         Debug(DPARS|DEXT, ("load_entry()...got DONT_LOG ('q') option\n"))
454                         /* only allow the user to set the option once */
455                         if ((e->flags & DONT_LOG) == DONT_LOG) {
456                                 Debug(DPARS|DEXT, ("load_entry()...duplicate DONT_LOG ('q') option\n"))
457                                 ecode = e_option;
458                                 goto eof;
459                         }
460                         e->flags |= DONT_LOG;
461                         break;
462                 default:
463                         Debug(DPARS|DEXT, ("load_entry()...invalid option '%c'\n", ch))
464                         ecode = e_option;
465                         goto eof;
466                 }
467                 ch = get_char(file);
468                 if (ch!='\t' && ch!=' ') {
469                         ecode = e_option;
470                         goto eof;
471                 }
472
473                 Skip_Blanks(ch, file)
474                 if (ch == EOF || ch == '\n') {
475                         ecode = e_cmd;
476                         goto eof;
477                 }
478         }
479
480         unget_char(ch, file);
481
482         Debug(DPARS, ("load_entry()...about to parse command\n"))
483
484         /* Everything up to the next \n or EOF is part of the command...
485          * too bad we don't know in advance how long it will be, since we
486          * need to malloc a string for it... so, we limit it to MAX_COMMAND.
487          */
488         ch = get_string(cmd, MAX_COMMAND, file, "\n");
489
490         /* a file without a \n before the EOF is rude, so we'll complain...
491          */
492         if (ch == EOF) {
493                 ecode = e_cmd;
494                 goto eof;
495         }
496
497         /* got the command in the 'cmd' string; save it in *e.
498          */
499         e->cmd = strdup(cmd);
500         if (e->cmd == NULL) {
501                 warn("strdup(\"%s\")", cmd);
502                 ecode = e_mem;
503                 goto eof;
504         }
505         Debug(DPARS, ("load_entry()...returning successfully\n"))
506
507         /* success, fini, return pointer to the entry we just created...
508          */
509         return e;
510
511  eof:
512         free_entry(e);
513         if (ecode != e_none && error_func)
514                 (*error_func)(ecodes[(int)ecode]);
515         while (ch != EOF && ch != '\n')
516                 ch = get_char(file);
517         return NULL;
518 }
519
520
521 /*
522  * bits         one bit per flag, default=FALSE
523  * low, high    bounds, impl. offset for bitstr
524  * names        NULL or names for these elements
525  * ch           current character being processed
526  * file         file being read
527  */
528 static char
529 get_list(bitstr_t *bits, int low, int high, const char *names[], int ch,
530     FILE *file)
531 {
532         int done;
533
534         /* we know that we point to a non-blank character here;
535          * must do a Skip_Blanks before we exit, so that the
536          * next call (or the code that picks up the cmd) can
537          * assume the same thing.
538          */
539
540         Debug(DPARS|DEXT, ("get_list()...entered\n"))
541
542         /* list = range {"," range}
543          */
544
545         /* clear the bit string, since the default is 'off'.
546          */
547         bit_nclear(bits, 0, (high-low+1));
548
549         /* process all ranges
550          */
551         done = FALSE;
552         while (!done) {
553                 ch = get_range(bits, low, high, names, ch, file);
554                 if (ch == ',')
555                         ch = get_char(file);
556                 else
557                         done = TRUE;
558         }
559
560         /* exiting.  skip to some blanks, then skip over the blanks.
561          */
562         Skip_Nonblanks(ch, file)
563         Skip_Blanks(ch, file)
564
565         Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
566
567         return ch;
568 }
569
570
571 /*
572  * bits         one bit per flag, default=FALSE
573  * low, high    bounds, impl. offset for bitstr
574  * names        NULL or names for these elements
575  * ch           current character being processed
576  * file         file being read
577  */
578 static char
579 get_range(bitstr_t *bits, int low, int high, const char *names[], int ch,
580     FILE *file)
581 {
582         /* range = number | number "-" number [ "/" number ]
583          */
584
585         int i, num1, num2, num3;
586
587         Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
588
589         if (ch == '*') {
590                 /* '*' means "first-last" but can still be modified by /step
591                  */
592                 num1 = low;
593                 num2 = high;
594                 ch = get_char(file);
595                 if (ch == EOF)
596                         return EOF;
597         } else {
598                 if (EOF == (ch = get_number(&num1, low, names, ch, file)))
599                         return EOF;
600
601                 if (ch == '/')
602                         num2 = high;
603                 else if (ch != '-') {
604                         /* not a range, it's a single number.
605                          */
606                         if (EOF == set_element(bits, low, high, num1))
607                                 return EOF;
608                         return ch;
609                 } else {
610                         /* eat the dash
611                          */
612                         ch = get_char(file);
613                         if (ch == EOF)
614                                 return EOF;
615
616                         /* get the number following the dash
617                          */
618                         ch = get_number(&num2, low, names, ch, file);
619                         if (ch == EOF)
620                                 return EOF;
621                 }
622         }
623
624         /* check for step size
625          */
626         if (ch == '/') {
627                 /* eat the slash
628                  */
629                 ch = get_char(file);
630                 if (ch == EOF)
631                         return EOF;
632
633                 /* get the step size -- note: we don't pass the
634                  * names here, because the number is not an
635                  * element id, it's a step size.  'low' is
636                  * sent as a 0 since there is no offset either.
637                  */
638                 ch = get_number(&num3, 0, PPC_NULL, ch, file);
639                 if (ch == EOF || num3 == 0)
640                         return EOF;
641         } else {
642                 /* no step.  default==1.
643                  */
644                 num3 = 1;
645         }
646
647         /* range. set all elements from num1 to num2, stepping
648          * by num3.  (the step is a downward-compatible extension
649          * proposed conceptually by bob@acornrc, syntactically
650          * designed then implemented by paul vixie).
651          */
652         for (i = num1;  i <= num2;  i += num3)
653                 if (EOF == set_element(bits, low, high, i))
654                         return EOF;
655
656         return ch;
657 }
658
659
660 /*
661  * numptr       where does the result go?
662  * low          offset applied to enum result
663  * names        symbolic names, if any, for enums
664  * ch           current character
665  * file         source
666  */
667 static char
668 get_number(int *numptr, int low, const char *names[], int ch, FILE *file)
669 {
670         char    temp[MAX_TEMPSTR], *pc;
671         int     len, i, all_digits;
672
673         /* collect alphanumerics into our fixed-size temp array
674          */
675         pc = temp;
676         len = 0;
677         all_digits = TRUE;
678         while (isalnum(ch)) {
679                 if (++len >= MAX_TEMPSTR)
680                         return EOF;
681
682                 *pc++ = ch;
683
684                 if (!isdigit(ch))
685                         all_digits = FALSE;
686
687                 ch = get_char(file);
688         }
689         *pc = '\0';
690         if (len == 0)
691             return (EOF);
692
693         /* try to find the name in the name list
694          */
695         if (names) {
696                 for (i = 0;  names[i] != NULL;  i++) {
697                         Debug(DPARS|DEXT,
698                                 ("get_num, compare(%s,%s)\n", names[i], temp))
699                         if (!strcasecmp(names[i], temp)) {
700                                 *numptr = i+low;
701                                 return ch;
702                         }
703                 }
704         }
705
706         /* no name list specified, or there is one and our string isn't
707          * in it.  either way: if it's all digits, use its magnitude.
708          * otherwise, it's an error.
709          */
710         if (all_digits) {
711                 *numptr = atoi(temp);
712                 return ch;
713         }
714
715         return EOF;
716 }
717
718
719 static int
720 set_element(bitstr_t *bits, int low, int high, int number)
721 {
722         Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
723
724         if (number < low || number > high)
725                 return EOF;
726
727         bit_set(bits, (number-low));
728         return OK;
729 }