1 |
|
|
/* $OpenBSD: entry.c,v 1.48 2015/11/14 13:09:14 millert Exp $ */ |
2 |
|
|
|
3 |
|
|
/* |
4 |
|
|
* Copyright 1988,1990,1993,1994 by Paul Vixie |
5 |
|
|
* Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC") |
6 |
|
|
* Copyright (c) 1997,2000 by Internet Software Consortium, Inc. |
7 |
|
|
* |
8 |
|
|
* Permission to use, copy, modify, and distribute this software for any |
9 |
|
|
* purpose with or without fee is hereby granted, provided that the above |
10 |
|
|
* copyright notice and this permission notice appear in all copies. |
11 |
|
|
* |
12 |
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES |
13 |
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
14 |
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR |
15 |
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
16 |
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
17 |
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT |
18 |
|
|
* OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
19 |
|
|
*/ |
20 |
|
|
|
21 |
|
|
#include <sys/types.h> |
22 |
|
|
|
23 |
|
|
#include <bitstring.h> /* for structs.h */ |
24 |
|
|
#include <ctype.h> |
25 |
|
|
#include <pwd.h> |
26 |
|
|
#include <stdio.h> |
27 |
|
|
#include <stdlib.h> |
28 |
|
|
#include <string.h> |
29 |
|
|
#include <syslog.h> |
30 |
|
|
#include <time.h> /* for structs.h */ |
31 |
|
|
#include <unistd.h> |
32 |
|
|
|
33 |
|
|
#include "pathnames.h" |
34 |
|
|
#include "macros.h" |
35 |
|
|
#include "structs.h" |
36 |
|
|
#include "funcs.h" |
37 |
|
|
|
38 |
|
|
typedef enum ecode { |
39 |
|
|
e_none, e_minute, e_hour, e_dom, e_month, e_dow, |
40 |
|
|
e_cmd, e_timespec, e_username, e_option, e_memory |
41 |
|
|
} ecode_e; |
42 |
|
|
|
43 |
|
|
static const char *ecodes[] = { |
44 |
|
|
"no error", |
45 |
|
|
"bad minute", |
46 |
|
|
"bad hour", |
47 |
|
|
"bad day-of-month", |
48 |
|
|
"bad month", |
49 |
|
|
"bad day-of-week", |
50 |
|
|
"bad command", |
51 |
|
|
"bad time specifier", |
52 |
|
|
"bad username", |
53 |
|
|
"bad option", |
54 |
|
|
"out of memory" |
55 |
|
|
}; |
56 |
|
|
|
57 |
|
|
static const char *MonthNames[] = { |
58 |
|
|
"Jan", "Feb", "Mar", "Apr", "May", "Jun", |
59 |
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec", |
60 |
|
|
NULL |
61 |
|
|
}; |
62 |
|
|
|
63 |
|
|
static const char *DowNames[] = { |
64 |
|
|
"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun", |
65 |
|
|
NULL |
66 |
|
|
}; |
67 |
|
|
|
68 |
|
|
static int get_list(bitstr_t *, int, int, const char *[], int, FILE *), |
69 |
|
|
get_range(bitstr_t *, int, int, const char *[], int, FILE *), |
70 |
|
|
get_number(int *, int, const char *[], int, FILE *, const char *), |
71 |
|
|
set_element(bitstr_t *, int, int, int); |
72 |
|
|
|
73 |
|
|
void |
74 |
|
|
free_entry(entry *e) |
75 |
|
|
{ |
76 |
|
|
free(e->cmd); |
77 |
|
|
free(e->pwd); |
78 |
|
|
if (e->envp) |
79 |
|
|
env_free(e->envp); |
80 |
|
|
free(e); |
81 |
|
|
} |
82 |
|
|
|
83 |
|
|
/* return NULL if eof or syntax error occurs; |
84 |
|
|
* otherwise return a pointer to a new entry. |
85 |
|
|
*/ |
86 |
|
|
entry * |
87 |
|
|
load_entry(FILE *file, void (*error_func)(const char *), struct passwd *pw, |
88 |
|
|
char **envp) |
89 |
|
|
{ |
90 |
|
|
/* this function reads one crontab entry -- the next -- from a file. |
91 |
|
|
* it skips any leading blank lines, ignores comments, and returns |
92 |
|
|
* NULL if for any reason the entry can't be read and parsed. |
93 |
|
|
* |
94 |
|
|
* the entry is also parsed here. |
95 |
|
|
* |
96 |
|
|
* syntax: |
97 |
|
|
* user crontab: |
98 |
|
|
* minutes hours doms months dows cmd\n |
99 |
|
|
* system crontab (/etc/crontab): |
100 |
|
|
* minutes hours doms months dows USERNAME cmd\n |
101 |
|
|
*/ |
102 |
|
|
|
103 |
|
|
ecode_e ecode = e_none; |
104 |
|
|
entry *e; |
105 |
|
|
int ch; |
106 |
|
|
char cmd[MAX_COMMAND]; |
107 |
|
|
char envstr[MAX_ENVSTR]; |
108 |
|
|
char **tenvp; |
109 |
|
|
|
110 |
|
|
skip_comments(file); |
111 |
|
|
|
112 |
|
|
ch = get_char(file); |
113 |
|
|
if (ch == EOF) |
114 |
|
|
return (NULL); |
115 |
|
|
|
116 |
|
|
/* ch is now the first useful character of a useful line. |
117 |
|
|
* it may be an @special or it may be the first character |
118 |
|
|
* of a list of minutes. |
119 |
|
|
*/ |
120 |
|
|
|
121 |
|
|
e = calloc(sizeof(entry), 1); |
122 |
|
|
if (e == NULL) { |
123 |
|
|
ecode = e_memory; |
124 |
|
|
goto eof; |
125 |
|
|
} |
126 |
|
|
|
127 |
|
|
if (ch == '@') { |
128 |
|
|
/* all of these should be flagged and load-limited; i.e., |
129 |
|
|
* instead of @hourly meaning "0 * * * *" it should mean |
130 |
|
|
* "close to the front of every hour but not 'til the |
131 |
|
|
* system load is low". Problems are: how do you know |
132 |
|
|
* what "low" means? (save me from /etc/cron.conf!) and: |
133 |
|
|
* how to guarantee low variance (how low is low?), which |
134 |
|
|
* means how to we run roughly every hour -- seems like |
135 |
|
|
* we need to keep a history or let the first hour set |
136 |
|
|
* the schedule, which means we aren't load-limited |
137 |
|
|
* anymore. too much for my overloaded brain. (vix, jan90) |
138 |
|
|
* HINT |
139 |
|
|
*/ |
140 |
|
|
ch = get_string(cmd, MAX_COMMAND, file, " \t\n"); |
141 |
|
|
if (!strcmp("reboot", cmd)) { |
142 |
|
|
e->flags |= WHEN_REBOOT; |
143 |
|
|
} else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ |
144 |
|
|
bit_set(e->minute, 0); |
145 |
|
|
bit_set(e->hour, 0); |
146 |
|
|
bit_set(e->dom, 0); |
147 |
|
|
bit_set(e->month, 0); |
148 |
|
|
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); |
149 |
|
|
e->flags |= DOW_STAR; |
150 |
|
|
} else if (!strcmp("monthly", cmd)) { |
151 |
|
|
bit_set(e->minute, 0); |
152 |
|
|
bit_set(e->hour, 0); |
153 |
|
|
bit_set(e->dom, 0); |
154 |
|
|
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); |
155 |
|
|
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); |
156 |
|
|
e->flags |= DOW_STAR; |
157 |
|
|
} else if (!strcmp("weekly", cmd)) { |
158 |
|
|
bit_set(e->minute, 0); |
159 |
|
|
bit_set(e->hour, 0); |
160 |
|
|
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); |
161 |
|
|
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); |
162 |
|
|
bit_set(e->dow, 0); |
163 |
|
|
e->flags |= DOW_STAR; |
164 |
|
|
} else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { |
165 |
|
|
bit_set(e->minute, 0); |
166 |
|
|
bit_set(e->hour, 0); |
167 |
|
|
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); |
168 |
|
|
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); |
169 |
|
|
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); |
170 |
|
|
} else if (!strcmp("hourly", cmd)) { |
171 |
|
|
bit_set(e->minute, 0); |
172 |
|
|
bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); |
173 |
|
|
bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); |
174 |
|
|
bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); |
175 |
|
|
bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); |
176 |
|
|
e->flags |= HR_STAR; |
177 |
|
|
} else { |
178 |
|
|
ecode = e_timespec; |
179 |
|
|
goto eof; |
180 |
|
|
} |
181 |
|
|
/* Advance past whitespace between shortcut and |
182 |
|
|
* username/command. |
183 |
|
|
*/ |
184 |
|
|
Skip_Blanks(ch, file); |
185 |
|
|
if (ch == EOF || ch == '\n') { |
186 |
|
|
ecode = e_cmd; |
187 |
|
|
goto eof; |
188 |
|
|
} |
189 |
|
|
} else { |
190 |
|
|
if (ch == '*') |
191 |
|
|
e->flags |= MIN_STAR; |
192 |
|
|
ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, |
193 |
|
|
NULL, ch, file); |
194 |
|
|
if (ch == EOF) { |
195 |
|
|
ecode = e_minute; |
196 |
|
|
goto eof; |
197 |
|
|
} |
198 |
|
|
|
199 |
|
|
/* hours |
200 |
|
|
*/ |
201 |
|
|
|
202 |
|
|
if (ch == '*') |
203 |
|
|
e->flags |= HR_STAR; |
204 |
|
|
ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, |
205 |
|
|
NULL, ch, file); |
206 |
|
|
if (ch == EOF) { |
207 |
|
|
ecode = e_hour; |
208 |
|
|
goto eof; |
209 |
|
|
} |
210 |
|
|
|
211 |
|
|
/* DOM (days of month) |
212 |
|
|
*/ |
213 |
|
|
|
214 |
|
|
if (ch == '*') |
215 |
|
|
e->flags |= DOM_STAR; |
216 |
|
|
ch = get_list(e->dom, FIRST_DOM, LAST_DOM, |
217 |
|
|
NULL, ch, file); |
218 |
|
|
if (ch == EOF) { |
219 |
|
|
ecode = e_dom; |
220 |
|
|
goto eof; |
221 |
|
|
} |
222 |
|
|
|
223 |
|
|
/* month |
224 |
|
|
*/ |
225 |
|
|
|
226 |
|
|
ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, |
227 |
|
|
MonthNames, ch, file); |
228 |
|
|
if (ch == EOF) { |
229 |
|
|
ecode = e_month; |
230 |
|
|
goto eof; |
231 |
|
|
} |
232 |
|
|
|
233 |
|
|
/* DOW (days of week) |
234 |
|
|
*/ |
235 |
|
|
|
236 |
|
|
if (ch == '*') |
237 |
|
|
e->flags |= DOW_STAR; |
238 |
|
|
ch = get_list(e->dow, FIRST_DOW, LAST_DOW, |
239 |
|
|
DowNames, ch, file); |
240 |
|
|
if (ch == EOF) { |
241 |
|
|
ecode = e_dow; |
242 |
|
|
goto eof; |
243 |
|
|
} |
244 |
|
|
} |
245 |
|
|
|
246 |
|
|
/* make sundays equivalent */ |
247 |
|
|
if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) { |
248 |
|
|
bit_set(e->dow, 0); |
249 |
|
|
bit_set(e->dow, 7); |
250 |
|
|
} |
251 |
|
|
|
252 |
|
|
/* check for permature EOL and catch a common typo */ |
253 |
|
|
if (ch == '\n' || ch == '*') { |
254 |
|
|
ecode = e_cmd; |
255 |
|
|
goto eof; |
256 |
|
|
} |
257 |
|
|
|
258 |
|
|
/* ch is the first character of a command, or a username */ |
259 |
|
|
unget_char(ch, file); |
260 |
|
|
|
261 |
|
|
if (!pw) { |
262 |
|
|
char *username = cmd; /* temp buffer */ |
263 |
|
|
|
264 |
|
|
ch = get_string(username, MAX_COMMAND, file, " \t\n"); |
265 |
|
|
|
266 |
|
|
if (ch == EOF || ch == '\n' || ch == '*') { |
267 |
|
|
ecode = e_cmd; |
268 |
|
|
goto eof; |
269 |
|
|
} |
270 |
|
|
|
271 |
|
|
pw = getpwnam(username); |
272 |
|
|
if (pw == NULL) { |
273 |
|
|
ecode = e_username; |
274 |
|
|
goto eof; |
275 |
|
|
} |
276 |
|
|
} |
277 |
|
|
|
278 |
|
|
if ((e->pwd = pw_dup(pw)) == NULL) { |
279 |
|
|
ecode = e_memory; |
280 |
|
|
goto eof; |
281 |
|
|
} |
282 |
|
|
explicit_bzero(e->pwd->pw_passwd, strlen(e->pwd->pw_passwd)); |
283 |
|
|
|
284 |
|
|
/* copy and fix up environment. some variables are just defaults and |
285 |
|
|
* others are overrides. |
286 |
|
|
*/ |
287 |
|
|
if ((e->envp = env_copy(envp)) == NULL) { |
288 |
|
|
ecode = e_memory; |
289 |
|
|
goto eof; |
290 |
|
|
} |
291 |
|
|
if (!env_get("SHELL", e->envp)) { |
292 |
|
|
if (snprintf(envstr, sizeof envstr, "SHELL=%s", _PATH_BSHELL) >= |
293 |
|
|
sizeof(envstr)) |
294 |
|
|
syslog(LOG_ERR, "(CRON) ERROR (can't set SHELL)"); |
295 |
|
|
else { |
296 |
|
|
if ((tenvp = env_set(e->envp, envstr)) == NULL) { |
297 |
|
|
ecode = e_memory; |
298 |
|
|
goto eof; |
299 |
|
|
} |
300 |
|
|
e->envp = tenvp; |
301 |
|
|
} |
302 |
|
|
} |
303 |
|
|
if (!env_get("HOME", e->envp)) { |
304 |
|
|
if (snprintf(envstr, sizeof envstr, "HOME=%s", pw->pw_dir) >= |
305 |
|
|
sizeof(envstr)) |
306 |
|
|
syslog(LOG_ERR, "(CRON) ERROR (can't set HOME)"); |
307 |
|
|
else { |
308 |
|
|
if ((tenvp = env_set(e->envp, envstr)) == NULL) { |
309 |
|
|
ecode = e_memory; |
310 |
|
|
goto eof; |
311 |
|
|
} |
312 |
|
|
e->envp = tenvp; |
313 |
|
|
} |
314 |
|
|
} |
315 |
|
|
if (snprintf(envstr, sizeof envstr, "LOGNAME=%s", pw->pw_name) >= |
316 |
|
|
sizeof(envstr)) |
317 |
|
|
syslog(LOG_ERR, "(CRON) ERROR (can't set LOGNAME)"); |
318 |
|
|
else { |
319 |
|
|
if ((tenvp = env_set(e->envp, envstr)) == NULL) { |
320 |
|
|
ecode = e_memory; |
321 |
|
|
goto eof; |
322 |
|
|
} |
323 |
|
|
e->envp = tenvp; |
324 |
|
|
} |
325 |
|
|
if (snprintf(envstr, sizeof envstr, "USER=%s", pw->pw_name) >= |
326 |
|
|
sizeof(envstr)) |
327 |
|
|
syslog(LOG_ERR, "(CRON) ERROR (can't set USER)"); |
328 |
|
|
else { |
329 |
|
|
if ((tenvp = env_set(e->envp, envstr)) == NULL) { |
330 |
|
|
ecode = e_memory; |
331 |
|
|
goto eof; |
332 |
|
|
} |
333 |
|
|
e->envp = tenvp; |
334 |
|
|
} |
335 |
|
|
|
336 |
|
|
/* If the first character of the command is '-' it is a cron option. |
337 |
|
|
*/ |
338 |
|
|
ch = get_char(file); |
339 |
|
|
while (ch == '-') { |
340 |
|
|
switch (ch = get_char(file)) { |
341 |
|
|
case 'q': |
342 |
|
|
e->flags |= DONT_LOG; |
343 |
|
|
Skip_Nonblanks(ch, file) |
344 |
|
|
break; |
345 |
|
|
default: |
346 |
|
|
ecode = e_option; |
347 |
|
|
goto eof; |
348 |
|
|
} |
349 |
|
|
Skip_Blanks(ch, file) |
350 |
|
|
if (ch == EOF || ch == '\n') { |
351 |
|
|
ecode = e_cmd; |
352 |
|
|
goto eof; |
353 |
|
|
} |
354 |
|
|
} |
355 |
|
|
unget_char(ch, file); |
356 |
|
|
|
357 |
|
|
/* Everything up to the next \n or EOF is part of the command... |
358 |
|
|
* too bad we don't know in advance how long it will be, since we |
359 |
|
|
* need to malloc a string for it... so, we limit it to MAX_COMMAND. |
360 |
|
|
*/ |
361 |
|
|
ch = get_string(cmd, MAX_COMMAND, file, "\n"); |
362 |
|
|
|
363 |
|
|
/* a file without a \n before the EOF is rude, so we'll complain... |
364 |
|
|
*/ |
365 |
|
|
if (ch == EOF) { |
366 |
|
|
ecode = e_cmd; |
367 |
|
|
goto eof; |
368 |
|
|
} |
369 |
|
|
|
370 |
|
|
/* got the command in the 'cmd' string; save it in *e. |
371 |
|
|
*/ |
372 |
|
|
if ((e->cmd = strdup(cmd)) == NULL) { |
373 |
|
|
ecode = e_memory; |
374 |
|
|
goto eof; |
375 |
|
|
} |
376 |
|
|
|
377 |
|
|
/* success, fini, return pointer to the entry we just created... |
378 |
|
|
*/ |
379 |
|
|
return (e); |
380 |
|
|
|
381 |
|
|
eof: |
382 |
|
|
if (e) |
383 |
|
|
free_entry(e); |
384 |
|
|
while (ch != '\n' && !feof(file)) |
385 |
|
|
ch = get_char(file); |
386 |
|
|
if (ecode != e_none && error_func) |
387 |
|
|
(*error_func)(ecodes[(int)ecode]); |
388 |
|
|
return (NULL); |
389 |
|
|
} |
390 |
|
|
|
391 |
|
|
static int |
392 |
|
|
get_list(bitstr_t *bits, int low, int high, const char *names[], |
393 |
|
|
int ch, FILE *file) |
394 |
|
|
{ |
395 |
|
|
int done; |
396 |
|
|
|
397 |
|
|
/* we know that we point to a non-blank character here; |
398 |
|
|
* must do a Skip_Blanks before we exit, so that the |
399 |
|
|
* next call (or the code that picks up the cmd) can |
400 |
|
|
* assume the same thing. |
401 |
|
|
*/ |
402 |
|
|
|
403 |
|
|
/* list = range {"," range} |
404 |
|
|
*/ |
405 |
|
|
|
406 |
|
|
/* clear the bit string, since the default is 'off'. |
407 |
|
|
*/ |
408 |
|
|
bit_nclear(bits, 0, (high-low+1)); |
409 |
|
|
|
410 |
|
|
/* process all ranges |
411 |
|
|
*/ |
412 |
|
|
done = FALSE; |
413 |
|
|
while (!done) { |
414 |
|
|
if (EOF == (ch = get_range(bits, low, high, names, ch, file))) |
415 |
|
|
return (EOF); |
416 |
|
|
if (ch == ',') |
417 |
|
|
ch = get_char(file); |
418 |
|
|
else |
419 |
|
|
done = TRUE; |
420 |
|
|
} |
421 |
|
|
|
422 |
|
|
/* exiting. skip to some blanks, then skip over the blanks. |
423 |
|
|
*/ |
424 |
|
|
Skip_Nonblanks(ch, file) |
425 |
|
|
Skip_Blanks(ch, file) |
426 |
|
|
|
427 |
|
|
return (ch); |
428 |
|
|
} |
429 |
|
|
|
430 |
|
|
|
431 |
|
|
static int |
432 |
|
|
get_range(bitstr_t *bits, int low, int high, const char *names[], |
433 |
|
|
int ch, FILE *file) |
434 |
|
|
{ |
435 |
|
|
/* range = number | number "-" number [ "/" number ] |
436 |
|
|
*/ |
437 |
|
|
|
438 |
|
|
int i, num1, num2, num3; |
439 |
|
|
|
440 |
|
|
if (ch == '*') { |
441 |
|
|
/* '*' means "first-last" but can still be modified by /step |
442 |
|
|
*/ |
443 |
|
|
num1 = low; |
444 |
|
|
num2 = high; |
445 |
|
|
ch = get_char(file); |
446 |
|
|
if (ch == EOF) |
447 |
|
|
return (EOF); |
448 |
|
|
} else { |
449 |
|
|
ch = get_number(&num1, low, names, ch, file, ",- \t\n"); |
450 |
|
|
if (ch == EOF) |
451 |
|
|
return (EOF); |
452 |
|
|
|
453 |
|
|
if (ch != '-') { |
454 |
|
|
/* not a range, it's a single number. |
455 |
|
|
*/ |
456 |
|
|
if (EOF == set_element(bits, low, high, num1)) { |
457 |
|
|
unget_char(ch, file); |
458 |
|
|
return (EOF); |
459 |
|
|
} |
460 |
|
|
return (ch); |
461 |
|
|
} else { |
462 |
|
|
/* eat the dash |
463 |
|
|
*/ |
464 |
|
|
ch = get_char(file); |
465 |
|
|
if (ch == EOF) |
466 |
|
|
return (EOF); |
467 |
|
|
|
468 |
|
|
/* get the number following the dash |
469 |
|
|
*/ |
470 |
|
|
ch = get_number(&num2, low, names, ch, file, "/, \t\n"); |
471 |
|
|
if (ch == EOF || num1 > num2) |
472 |
|
|
return (EOF); |
473 |
|
|
} |
474 |
|
|
} |
475 |
|
|
|
476 |
|
|
/* check for step size |
477 |
|
|
*/ |
478 |
|
|
if (ch == '/') { |
479 |
|
|
/* eat the slash |
480 |
|
|
*/ |
481 |
|
|
ch = get_char(file); |
482 |
|
|
if (ch == EOF) |
483 |
|
|
return (EOF); |
484 |
|
|
|
485 |
|
|
/* get the step size -- note: we don't pass the |
486 |
|
|
* names here, because the number is not an |
487 |
|
|
* element id, it's a step size. 'low' is |
488 |
|
|
* sent as a 0 since there is no offset either. |
489 |
|
|
*/ |
490 |
|
|
ch = get_number(&num3, 0, NULL, ch, file, ", \t\n"); |
491 |
|
|
if (ch == EOF || num3 == 0) |
492 |
|
|
return (EOF); |
493 |
|
|
} else { |
494 |
|
|
/* no step. default==1. |
495 |
|
|
*/ |
496 |
|
|
num3 = 1; |
497 |
|
|
} |
498 |
|
|
|
499 |
|
|
/* range. set all elements from num1 to num2, stepping |
500 |
|
|
* by num3. (the step is a downward-compatible extension |
501 |
|
|
* proposed conceptually by bob@acornrc, syntactically |
502 |
|
|
* designed then implemented by paul vixie). |
503 |
|
|
*/ |
504 |
|
|
for (i = num1; i <= num2; i += num3) |
505 |
|
|
if (EOF == set_element(bits, low, high, i)) { |
506 |
|
|
unget_char(ch, file); |
507 |
|
|
return (EOF); |
508 |
|
|
} |
509 |
|
|
|
510 |
|
|
return (ch); |
511 |
|
|
} |
512 |
|
|
|
513 |
|
|
static int |
514 |
|
|
get_number(int *numptr, int low, const char *names[], int ch, FILE *file, |
515 |
|
|
const char *terms) |
516 |
|
|
{ |
517 |
|
|
char temp[MAX_TEMPSTR], *pc; |
518 |
|
|
int len, i; |
519 |
|
|
|
520 |
|
|
pc = temp; |
521 |
|
|
len = 0; |
522 |
|
|
|
523 |
|
|
/* first look for a number */ |
524 |
|
|
while (isdigit((unsigned char)ch)) { |
525 |
|
|
if (++len >= MAX_TEMPSTR) |
526 |
|
|
goto bad; |
527 |
|
|
*pc++ = ch; |
528 |
|
|
ch = get_char(file); |
529 |
|
|
} |
530 |
|
|
*pc = '\0'; |
531 |
|
|
if (len != 0) { |
532 |
|
|
/* got a number, check for valid terminator */ |
533 |
|
|
if (!strchr(terms, ch)) |
534 |
|
|
goto bad; |
535 |
|
|
*numptr = atoi(temp); |
536 |
|
|
return (ch); |
537 |
|
|
} |
538 |
|
|
|
539 |
|
|
/* no numbers, look for a string if we have any */ |
540 |
|
|
if (names) { |
541 |
|
|
while (isalpha((unsigned char)ch)) { |
542 |
|
|
if (++len >= MAX_TEMPSTR) |
543 |
|
|
goto bad; |
544 |
|
|
*pc++ = ch; |
545 |
|
|
ch = get_char(file); |
546 |
|
|
} |
547 |
|
|
*pc = '\0'; |
548 |
|
|
if (len != 0 && strchr(terms, ch)) { |
549 |
|
|
for (i = 0; names[i] != NULL; i++) { |
550 |
|
|
if (!strcasecmp(names[i], temp)) { |
551 |
|
|
*numptr = i+low; |
552 |
|
|
return (ch); |
553 |
|
|
} |
554 |
|
|
} |
555 |
|
|
} |
556 |
|
|
} |
557 |
|
|
|
558 |
|
|
bad: |
559 |
|
|
unget_char(ch, file); |
560 |
|
|
return (EOF); |
561 |
|
|
} |
562 |
|
|
|
563 |
|
|
static int |
564 |
|
|
set_element(bitstr_t *bits, int low, int high, int number) |
565 |
|
|
{ |
566 |
|
|
|
567 |
|
|
if (number < low || number > high) |
568 |
|
|
return (EOF); |
569 |
|
|
|
570 |
|
|
bit_set(bits, (number-low)); |
571 |
|
|
return (0); |
572 |
|
|
} |