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