12311Sjkh/* Copyright 1988,1990,1993,1994 by Paul Vixie 22311Sjkh * All rights reserved 32311Sjkh * 42311Sjkh * Distribute freely, except: don't remove my name from the source or 52311Sjkh * documentation (don't take credit for my work), mark your changes (don't 62311Sjkh * get me blamed for your possible bugs), don't alter or remove this 72311Sjkh * notice. May be sold if buildable source is provided to buyer. No 82311Sjkh * warrantee of any kind, express or implied, is included with this 92311Sjkh * software; use at your own risk, responsibility for damages (if any) to 102311Sjkh * anyone resulting from the use of this software rests entirely with the 112311Sjkh * user. 122311Sjkh * 132311Sjkh * Send bug reports, bug fixes, enhancements, requests, flames, etc., and 142311Sjkh * I'll try to keep a version up to date. I can be reached as follows: 152311Sjkh * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul 162311Sjkh */ 172311Sjkh 182311Sjkh#if !defined(lint) && !defined(LINT) 1929452Scharnierstatic const char rcsid[] = 2050479Speter "$FreeBSD$"; 212311Sjkh#endif 222311Sjkh 232311Sjkh/* vix 26jan87 [RCS'd; rest of log is in RCS file] 242311Sjkh * vix 01jan87 [added line-level error recovery] 252311Sjkh * vix 31dec86 [added /step to the from-to range, per bob@acornrc] 262311Sjkh * vix 30dec86 [written] 272311Sjkh */ 282311Sjkh 292311Sjkh 302311Sjkh#include "cron.h" 3130895Sache#include <grp.h> 3230895Sache#ifdef LOGIN_CAP 3330895Sache#include <login_cap.h> 3430895Sache#endif 352311Sjkh 362311Sjkhtypedef enum ecode { 372311Sjkh e_none, e_minute, e_hour, e_dom, e_month, e_dow, 3860825Sghelmer e_cmd, e_timespec, e_username, e_group, e_mem 3930895Sache#ifdef LOGIN_CAP 4030895Sache , e_class 4130895Sache#endif 422311Sjkh} ecode_e; 432311Sjkh 44173412Skevlostatic char get_list(bitstr_t *, int, int, char *[], int, FILE *), 45173412Skevlo get_range(bitstr_t *, int, int, char *[], int, FILE *), 46173412Skevlo get_number(int *, int, char *[], int, FILE *); 47173412Skevlostatic int set_element(bitstr_t *, int, int, int); 482311Sjkh 492311Sjkhstatic char *ecodes[] = 502311Sjkh { 512311Sjkh "no error", 522311Sjkh "bad minute", 532311Sjkh "bad hour", 542311Sjkh "bad day-of-month", 552311Sjkh "bad month", 562311Sjkh "bad day-of-week", 572311Sjkh "bad command", 582311Sjkh "bad time specifier", 592311Sjkh "bad username", 6030895Sache "bad group name", 6160825Sghelmer "out of memory", 6230895Sache#ifdef LOGIN_CAP 6330895Sache "bad class name", 6430895Sache#endif 652311Sjkh }; 662311Sjkh 672311Sjkh 682311Sjkhvoid 692311Sjkhfree_entry(e) 702311Sjkh entry *e; 712311Sjkh{ 7230895Sache#ifdef LOGIN_CAP 7330895Sache if (e->class != NULL) 7430895Sache free(e->class); 7530895Sache#endif 7679860Sdd if (e->cmd != NULL) 7779860Sdd free(e->cmd); 7879860Sdd if (e->envp != NULL) 7979860Sdd env_free(e->envp); 802311Sjkh free(e); 812311Sjkh} 822311Sjkh 832311Sjkh 842311Sjkh/* return NULL if eof or syntax error occurs; 852311Sjkh * otherwise return a pointer to a new entry. 862311Sjkh */ 872311Sjkhentry * 882311Sjkhload_entry(file, error_func, pw, envp) 892311Sjkh FILE *file; 90184809Smatteo void (*error_func)(char *); 912311Sjkh struct passwd *pw; 922311Sjkh char **envp; 932311Sjkh{ 942311Sjkh /* this function reads one crontab entry -- the next -- from a file. 952311Sjkh * it skips any leading blank lines, ignores comments, and returns 962311Sjkh * EOF if for any reason the entry can't be read and parsed. 972311Sjkh * 982311Sjkh * the entry is also parsed here. 992311Sjkh * 1002311Sjkh * syntax: 1012311Sjkh * user crontab: 1022311Sjkh * minutes hours doms months dows cmd\n 1032311Sjkh * system crontab (/etc/crontab): 1042311Sjkh * minutes hours doms months dows USERNAME cmd\n 1052311Sjkh */ 1062311Sjkh 1072311Sjkh ecode_e ecode = e_none; 1082311Sjkh entry *e; 1092311Sjkh int ch; 1102311Sjkh char cmd[MAX_COMMAND]; 1112311Sjkh char envstr[MAX_ENVSTR]; 11260825Sghelmer char **prev_env; 1132311Sjkh 1142311Sjkh Debug(DPARS, ("load_entry()...about to eat comments\n")) 1152311Sjkh 1162311Sjkh skip_comments(file); 1172311Sjkh 1182311Sjkh ch = get_char(file); 1192311Sjkh if (ch == EOF) 1202311Sjkh return NULL; 1212311Sjkh 1222311Sjkh /* ch is now the first useful character of a useful line. 1232311Sjkh * it may be an @special or it may be the first character 1242311Sjkh * of a list of minutes. 1252311Sjkh */ 1262311Sjkh 1272311Sjkh e = (entry *) calloc(sizeof(entry), sizeof(char)); 1282311Sjkh 12960825Sghelmer if (e == NULL) { 13060825Sghelmer warn("load_entry: calloc failed"); 13160825Sghelmer return NULL; 13260825Sghelmer } 13360825Sghelmer 1342311Sjkh if (ch == '@') { 1352311Sjkh /* all of these should be flagged and load-limited; i.e., 1362311Sjkh * instead of @hourly meaning "0 * * * *" it should mean 1372311Sjkh * "close to the front of every hour but not 'til the 1382311Sjkh * system load is low". Problems are: how do you know 1392311Sjkh * what "low" means? (save me from /etc/cron.conf!) and: 1402311Sjkh * how to guarantee low variance (how low is low?), which 1412311Sjkh * means how to we run roughly every hour -- seems like 1422311Sjkh * we need to keep a history or let the first hour set 1432311Sjkh * the schedule, which means we aren't load-limited 1442311Sjkh * anymore. too much for my overloaded brain. (vix, jan90) 1452311Sjkh * HINT 1462311Sjkh */ 14758017Sghelmer Debug(DPARS, ("load_entry()...about to test shortcuts\n")) 1482311Sjkh ch = get_string(cmd, MAX_COMMAND, file, " \t\n"); 1492311Sjkh if (!strcmp("reboot", cmd)) { 15058017Sghelmer Debug(DPARS, ("load_entry()...reboot shortcut\n")) 1512311Sjkh e->flags |= WHEN_REBOOT; 1522311Sjkh } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){ 15358017Sghelmer Debug(DPARS, ("load_entry()...yearly shortcut\n")) 154242101Ssobomax bit_set(e->second, 0); 1552311Sjkh bit_set(e->minute, 0); 1562311Sjkh bit_set(e->hour, 0); 1572311Sjkh bit_set(e->dom, 0); 1582311Sjkh bit_set(e->month, 0); 1592311Sjkh bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 16081778Smikeh e->flags |= DOW_STAR; 1612311Sjkh } else if (!strcmp("monthly", cmd)) { 16258017Sghelmer Debug(DPARS, ("load_entry()...monthly shortcut\n")) 163242101Ssobomax bit_set(e->second, 0); 1642311Sjkh bit_set(e->minute, 0); 1652311Sjkh bit_set(e->hour, 0); 1662311Sjkh bit_set(e->dom, 0); 1672311Sjkh bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 1682311Sjkh bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 16981778Smikeh e->flags |= DOW_STAR; 1702311Sjkh } else if (!strcmp("weekly", cmd)) { 17158017Sghelmer Debug(DPARS, ("load_entry()...weekly shortcut\n")) 172242101Ssobomax bit_set(e->second, 0); 1732311Sjkh bit_set(e->minute, 0); 1742311Sjkh bit_set(e->hour, 0); 1752311Sjkh bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 17681778Smikeh e->flags |= DOM_STAR; 1772311Sjkh bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 1782311Sjkh bit_set(e->dow, 0); 1792311Sjkh } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) { 18058017Sghelmer Debug(DPARS, ("load_entry()...daily shortcut\n")) 181242101Ssobomax bit_set(e->second, 0); 1822311Sjkh bit_set(e->minute, 0); 1832311Sjkh bit_set(e->hour, 0); 1842311Sjkh bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 1852311Sjkh bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 1862311Sjkh bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 1872311Sjkh } else if (!strcmp("hourly", cmd)) { 18858017Sghelmer Debug(DPARS, ("load_entry()...hourly shortcut\n")) 189242101Ssobomax bit_set(e->second, 0); 1902311Sjkh bit_set(e->minute, 0); 19158017Sghelmer bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); 1922311Sjkh bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 1932311Sjkh bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 1942311Sjkh bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 195242101Ssobomax } else if (!strcmp("every_minute", cmd)) { 196242101Ssobomax Debug(DPARS, ("load_entry()...every_minute shortcut\n")) 197242101Ssobomax bit_set(e->second, 0); 198242101Ssobomax bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); 199242101Ssobomax bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); 200242101Ssobomax bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 201242101Ssobomax bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 202242101Ssobomax bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 203242101Ssobomax } else if (!strcmp("every_second", cmd)) { 204242101Ssobomax Debug(DPARS, ("load_entry()...every_second shortcut\n")) 205242101Ssobomax e->flags |= SEC_RES; 206242101Ssobomax bit_nset(e->second, 0, (LAST_SECOND-FIRST_SECOND+1)); 207242101Ssobomax bit_nset(e->minute, 0, (LAST_MINUTE-FIRST_MINUTE+1)); 208242101Ssobomax bit_nset(e->hour, 0, (LAST_HOUR-FIRST_HOUR+1)); 209242101Ssobomax bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1)); 210242101Ssobomax bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1)); 211242101Ssobomax bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1)); 2122311Sjkh } else { 2132311Sjkh ecode = e_timespec; 2142311Sjkh goto eof; 2152311Sjkh } 21658017Sghelmer /* Advance past whitespace between shortcut and 21758017Sghelmer * username/command. 21858017Sghelmer */ 21958017Sghelmer Skip_Blanks(ch, file); 22058017Sghelmer if (ch == EOF) { 22158017Sghelmer ecode = e_cmd; 22258017Sghelmer goto eof; 22358017Sghelmer } 2242311Sjkh } else { 2252311Sjkh Debug(DPARS, ("load_entry()...about to parse numerics\n")) 226242101Ssobomax bit_set(e->second, 0); 2272311Sjkh 2282311Sjkh ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE, 2292311Sjkh PPC_NULL, ch, file); 2302311Sjkh if (ch == EOF) { 2312311Sjkh ecode = e_minute; 2322311Sjkh goto eof; 2332311Sjkh } 2342311Sjkh 2352311Sjkh /* hours 2362311Sjkh */ 2372311Sjkh 2382311Sjkh ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR, 2392311Sjkh PPC_NULL, ch, file); 2402311Sjkh if (ch == EOF) { 2412311Sjkh ecode = e_hour; 2422311Sjkh goto eof; 2432311Sjkh } 2442311Sjkh 2452311Sjkh /* DOM (days of month) 2462311Sjkh */ 2472311Sjkh 2482311Sjkh if (ch == '*') 2492311Sjkh e->flags |= DOM_STAR; 2502311Sjkh ch = get_list(e->dom, FIRST_DOM, LAST_DOM, 2512311Sjkh PPC_NULL, ch, file); 2522311Sjkh if (ch == EOF) { 2532311Sjkh ecode = e_dom; 2542311Sjkh goto eof; 2552311Sjkh } 2562311Sjkh 2572311Sjkh /* month 2582311Sjkh */ 2592311Sjkh 2602311Sjkh ch = get_list(e->month, FIRST_MONTH, LAST_MONTH, 2612311Sjkh MonthNames, ch, file); 2622311Sjkh if (ch == EOF) { 2632311Sjkh ecode = e_month; 2642311Sjkh goto eof; 2652311Sjkh } 2662311Sjkh 2672311Sjkh /* DOW (days of week) 2682311Sjkh */ 2692311Sjkh 2702311Sjkh if (ch == '*') 2712311Sjkh e->flags |= DOW_STAR; 2722311Sjkh ch = get_list(e->dow, FIRST_DOW, LAST_DOW, 2732311Sjkh DowNames, ch, file); 2742311Sjkh if (ch == EOF) { 2752311Sjkh ecode = e_dow; 2762311Sjkh goto eof; 2772311Sjkh } 2782311Sjkh } 2792311Sjkh 280228990Suqs /* make sundays equivalent */ 2812311Sjkh if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) { 2822311Sjkh bit_set(e->dow, 0); 2832311Sjkh bit_set(e->dow, 7); 2842311Sjkh } 2852311Sjkh 2862311Sjkh /* ch is the first character of a command, or a username */ 2872311Sjkh unget_char(ch, file); 2882311Sjkh 2892311Sjkh if (!pw) { 2902311Sjkh char *username = cmd; /* temp buffer */ 29179861Sdd char *s; 29230895Sache struct group *grp; 29378156Sdd#ifdef LOGIN_CAP 29478156Sdd login_cap_t *lc; 29578156Sdd#endif 2962311Sjkh 2972311Sjkh Debug(DPARS, ("load_entry()...about to parse username\n")) 2982311Sjkh ch = get_string(username, MAX_COMMAND, file, " \t"); 2992311Sjkh 3002311Sjkh Debug(DPARS, ("load_entry()...got %s\n",username)) 3012311Sjkh if (ch == EOF) { 3022311Sjkh ecode = e_cmd; 3032311Sjkh goto eof; 3042311Sjkh } 3052311Sjkh 30630895Sache#ifdef LOGIN_CAP 30730895Sache if ((s = strrchr(username, '/')) != NULL) { 30830895Sache *s = '\0'; 30930895Sache e->class = strdup(s + 1); 31060825Sghelmer if (e->class == NULL) 31160825Sghelmer warn("strdup(\"%s\")", s + 1); 31260825Sghelmer } else { 31330895Sache e->class = strdup(RESOURCE_RC); 31460825Sghelmer if (e->class == NULL) 31560825Sghelmer warn("strdup(\"%s\")", RESOURCE_RC); 31660825Sghelmer } 31760825Sghelmer if (e->class == NULL) { 31860825Sghelmer ecode = e_mem; 31960825Sghelmer goto eof; 32060825Sghelmer } 32178156Sdd if ((lc = login_getclass(e->class)) == NULL) { 32230895Sache ecode = e_class; 32330895Sache goto eof; 32430895Sache } 32578156Sdd login_close(lc); 32630895Sache#endif 32730895Sache grp = NULL; 32830895Sache if ((s = strrchr(username, ':')) != NULL) { 32930895Sache *s = '\0'; 33030895Sache if ((grp = getgrnam(s + 1)) == NULL) { 33130895Sache ecode = e_group; 33230895Sache goto eof; 33330895Sache } 33430895Sache } 33530895Sache 3362311Sjkh pw = getpwnam(username); 3372311Sjkh if (pw == NULL) { 3382311Sjkh ecode = e_username; 3392311Sjkh goto eof; 3402311Sjkh } 34130895Sache if (grp != NULL) 34230895Sache pw->pw_gid = grp->gr_gid; 34330895Sache Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid)) 34430895Sache#ifdef LOGIN_CAP 34530895Sache Debug(DPARS, ("load_entry()...class %s\n",e->class)) 34630895Sache#endif 3472311Sjkh } 3482311Sjkh 349170890Syar#ifndef PAM /* PAM takes care of account expiration by itself */ 35010401Smpp if (pw->pw_expire && time(NULL) >= pw->pw_expire) { 35110401Smpp ecode = e_username; 35210401Smpp goto eof; 35310401Smpp } 354170890Syar#endif /* !PAM */ 35510401Smpp 3562311Sjkh e->uid = pw->pw_uid; 3572311Sjkh e->gid = pw->pw_gid; 3582311Sjkh 3592311Sjkh /* copy and fix up environment. some variables are just defaults and 3602311Sjkh * others are overrides. 3612311Sjkh */ 3622311Sjkh e->envp = env_copy(envp); 36360825Sghelmer if (e->envp == NULL) { 36460825Sghelmer warn("env_copy"); 36560825Sghelmer ecode = e_mem; 36660825Sghelmer goto eof; 36760825Sghelmer } 3682311Sjkh if (!env_get("SHELL", e->envp)) { 36960825Sghelmer prev_env = e->envp; 3702311Sjkh sprintf(envstr, "SHELL=%s", _PATH_BSHELL); 3712311Sjkh e->envp = env_set(e->envp, envstr); 37260825Sghelmer if (e->envp == NULL) { 37360825Sghelmer warn("env_set(%s)", envstr); 37460825Sghelmer env_free(prev_env); 37560825Sghelmer ecode = e_mem; 37660825Sghelmer goto eof; 37760825Sghelmer } 3782311Sjkh } 379167328Swill if (!env_get("HOME", e->envp)) { 380167328Swill prev_env = e->envp; 381167328Swill sprintf(envstr, "HOME=%s", pw->pw_dir); 382167328Swill e->envp = env_set(e->envp, envstr); 383167328Swill if (e->envp == NULL) { 384167328Swill warn("env_set(%s)", envstr); 385167328Swill env_free(prev_env); 386167328Swill ecode = e_mem; 387167328Swill goto eof; 388167328Swill } 38960825Sghelmer } 3902311Sjkh if (!env_get("PATH", e->envp)) { 39160825Sghelmer prev_env = e->envp; 3922311Sjkh sprintf(envstr, "PATH=%s", _PATH_DEFPATH); 3932311Sjkh e->envp = env_set(e->envp, envstr); 39460825Sghelmer if (e->envp == NULL) { 39560825Sghelmer warn("env_set(%s)", envstr); 39660825Sghelmer env_free(prev_env); 39760825Sghelmer ecode = e_mem; 39860825Sghelmer goto eof; 39960825Sghelmer } 4002311Sjkh } 40160825Sghelmer prev_env = e->envp; 4022311Sjkh sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name); 4032311Sjkh e->envp = env_set(e->envp, envstr); 40460825Sghelmer if (e->envp == NULL) { 40560825Sghelmer warn("env_set(%s)", envstr); 40660825Sghelmer env_free(prev_env); 40760825Sghelmer ecode = e_mem; 40860825Sghelmer goto eof; 40960825Sghelmer } 4102311Sjkh#if defined(BSD) 41160825Sghelmer prev_env = e->envp; 4122311Sjkh sprintf(envstr, "%s=%s", "USER", pw->pw_name); 4132311Sjkh e->envp = env_set(e->envp, envstr); 41460825Sghelmer if (e->envp == NULL) { 41560825Sghelmer warn("env_set(%s)", envstr); 41660825Sghelmer env_free(prev_env); 41760825Sghelmer ecode = e_mem; 41860825Sghelmer goto eof; 41960825Sghelmer } 4202311Sjkh#endif 4212311Sjkh 4222311Sjkh Debug(DPARS, ("load_entry()...about to parse command\n")) 4232311Sjkh 4242311Sjkh /* Everything up to the next \n or EOF is part of the command... 4252311Sjkh * too bad we don't know in advance how long it will be, since we 4262311Sjkh * need to malloc a string for it... so, we limit it to MAX_COMMAND. 4272311Sjkh * XXX - should use realloc(). 4288857Srgrimes */ 4292311Sjkh ch = get_string(cmd, MAX_COMMAND, file, "\n"); 4302311Sjkh 4312311Sjkh /* a file without a \n before the EOF is rude, so we'll complain... 4322311Sjkh */ 4332311Sjkh if (ch == EOF) { 4342311Sjkh ecode = e_cmd; 4352311Sjkh goto eof; 4362311Sjkh } 4372311Sjkh 4382311Sjkh /* got the command in the 'cmd' string; save it in *e. 4392311Sjkh */ 4402311Sjkh e->cmd = strdup(cmd); 44160825Sghelmer if (e->cmd == NULL) { 44279861Sdd warn("strdup(\"%s\")", cmd); 44360825Sghelmer ecode = e_mem; 44460825Sghelmer goto eof; 44560825Sghelmer } 4462311Sjkh Debug(DPARS, ("load_entry()...returning successfully\n")) 4472311Sjkh 4482311Sjkh /* success, fini, return pointer to the entry we just created... 4492311Sjkh */ 4502311Sjkh return e; 4512311Sjkh 4522311Sjkh eof: 45378156Sdd free_entry(e); 4542311Sjkh if (ecode != e_none && error_func) 4552311Sjkh (*error_func)(ecodes[(int)ecode]); 4562311Sjkh while (ch != EOF && ch != '\n') 4572311Sjkh ch = get_char(file); 4582311Sjkh return NULL; 4592311Sjkh} 4602311Sjkh 4612311Sjkh 4622311Sjkhstatic char 4632311Sjkhget_list(bits, low, high, names, ch, file) 4642311Sjkh bitstr_t *bits; /* one bit per flag, default=FALSE */ 4652311Sjkh int low, high; /* bounds, impl. offset for bitstr */ 4662311Sjkh char *names[]; /* NULL or *[] of names for these elements */ 4672311Sjkh int ch; /* current character being processed */ 4682311Sjkh FILE *file; /* file being read */ 4692311Sjkh{ 4702311Sjkh register int done; 4712311Sjkh 4722311Sjkh /* we know that we point to a non-blank character here; 4732311Sjkh * must do a Skip_Blanks before we exit, so that the 4742311Sjkh * next call (or the code that picks up the cmd) can 4752311Sjkh * assume the same thing. 4762311Sjkh */ 4772311Sjkh 4782311Sjkh Debug(DPARS|DEXT, ("get_list()...entered\n")) 4792311Sjkh 4802311Sjkh /* list = range {"," range} 4812311Sjkh */ 4828857Srgrimes 4832311Sjkh /* clear the bit string, since the default is 'off'. 4842311Sjkh */ 4852311Sjkh bit_nclear(bits, 0, (high-low+1)); 4862311Sjkh 4872311Sjkh /* process all ranges 4882311Sjkh */ 4892311Sjkh done = FALSE; 4902311Sjkh while (!done) { 4912311Sjkh ch = get_range(bits, low, high, names, ch, file); 4922311Sjkh if (ch == ',') 4932311Sjkh ch = get_char(file); 4942311Sjkh else 4952311Sjkh done = TRUE; 4962311Sjkh } 4972311Sjkh 4982311Sjkh /* exiting. skip to some blanks, then skip over the blanks. 4992311Sjkh */ 5002311Sjkh Skip_Nonblanks(ch, file) 5012311Sjkh Skip_Blanks(ch, file) 5022311Sjkh 5032311Sjkh Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch)) 5042311Sjkh 5052311Sjkh return ch; 5062311Sjkh} 5072311Sjkh 5082311Sjkh 5092311Sjkhstatic char 5102311Sjkhget_range(bits, low, high, names, ch, file) 5112311Sjkh bitstr_t *bits; /* one bit per flag, default=FALSE */ 5122311Sjkh int low, high; /* bounds, impl. offset for bitstr */ 5132311Sjkh char *names[]; /* NULL or names of elements */ 5142311Sjkh int ch; /* current character being processed */ 5152311Sjkh FILE *file; /* file being read */ 5162311Sjkh{ 5172311Sjkh /* range = number | number "-" number [ "/" number ] 5182311Sjkh */ 5192311Sjkh 5202311Sjkh register int i; 5212311Sjkh auto int num1, num2, num3; 5222311Sjkh 5232311Sjkh Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n")) 5242311Sjkh 5252311Sjkh if (ch == '*') { 5262311Sjkh /* '*' means "first-last" but can still be modified by /step 5272311Sjkh */ 5282311Sjkh num1 = low; 5292311Sjkh num2 = high; 5302311Sjkh ch = get_char(file); 5312311Sjkh if (ch == EOF) 5322311Sjkh return EOF; 5332311Sjkh } else { 5342311Sjkh if (EOF == (ch = get_number(&num1, low, names, ch, file))) 5352311Sjkh return EOF; 5362311Sjkh 537162666Sbrian if (ch == '/') 538162666Sbrian num2 = high; 539162666Sbrian else if (ch != '-') { 5402311Sjkh /* not a range, it's a single number. 5412311Sjkh */ 5422311Sjkh if (EOF == set_element(bits, low, high, num1)) 5432311Sjkh return EOF; 5442311Sjkh return ch; 5452311Sjkh } else { 5462311Sjkh /* eat the dash 5472311Sjkh */ 5482311Sjkh ch = get_char(file); 5492311Sjkh if (ch == EOF) 5502311Sjkh return EOF; 5512311Sjkh 5522311Sjkh /* get the number following the dash 5532311Sjkh */ 5542311Sjkh ch = get_number(&num2, low, names, ch, file); 5552311Sjkh if (ch == EOF) 5562311Sjkh return EOF; 5572311Sjkh } 5582311Sjkh } 5592311Sjkh 5602311Sjkh /* check for step size 5612311Sjkh */ 5622311Sjkh if (ch == '/') { 5632311Sjkh /* eat the slash 5642311Sjkh */ 5652311Sjkh ch = get_char(file); 5662311Sjkh if (ch == EOF) 5672311Sjkh return EOF; 5682311Sjkh 5692311Sjkh /* get the step size -- note: we don't pass the 5702311Sjkh * names here, because the number is not an 5712311Sjkh * element id, it's a step size. 'low' is 5722311Sjkh * sent as a 0 since there is no offset either. 5732311Sjkh */ 5742311Sjkh ch = get_number(&num3, 0, PPC_NULL, ch, file); 575141915Sdelphij if (ch == EOF || num3 == 0) 5762311Sjkh return EOF; 5772311Sjkh } else { 5782311Sjkh /* no step. default==1. 5792311Sjkh */ 5802311Sjkh num3 = 1; 5812311Sjkh } 5822311Sjkh 5832311Sjkh /* range. set all elements from num1 to num2, stepping 5842311Sjkh * by num3. (the step is a downward-compatible extension 5852311Sjkh * proposed conceptually by bob@acornrc, syntactically 5862311Sjkh * designed then implmented by paul vixie). 5872311Sjkh */ 5882311Sjkh for (i = num1; i <= num2; i += num3) 5892311Sjkh if (EOF == set_element(bits, low, high, i)) 5902311Sjkh return EOF; 5912311Sjkh 5922311Sjkh return ch; 5932311Sjkh} 5942311Sjkh 5952311Sjkh 5962311Sjkhstatic char 5972311Sjkhget_number(numptr, low, names, ch, file) 5982311Sjkh int *numptr; /* where does the result go? */ 5992311Sjkh int low; /* offset applied to result if symbolic enum used */ 6002311Sjkh char *names[]; /* symbolic names, if any, for enums */ 6012311Sjkh int ch; /* current character */ 6022311Sjkh FILE *file; /* source */ 6032311Sjkh{ 6042311Sjkh char temp[MAX_TEMPSTR], *pc; 6052311Sjkh int len, i, all_digits; 6062311Sjkh 6072311Sjkh /* collect alphanumerics into our fixed-size temp array 6082311Sjkh */ 6092311Sjkh pc = temp; 6102311Sjkh len = 0; 6112311Sjkh all_digits = TRUE; 6122311Sjkh while (isalnum(ch)) { 6132311Sjkh if (++len >= MAX_TEMPSTR) 6142311Sjkh return EOF; 6152311Sjkh 6162311Sjkh *pc++ = ch; 6172311Sjkh 6182311Sjkh if (!isdigit(ch)) 6192311Sjkh all_digits = FALSE; 6202311Sjkh 6212311Sjkh ch = get_char(file); 6222311Sjkh } 6232311Sjkh *pc = '\0'; 624141915Sdelphij if (len == 0) 625141915Sdelphij return (EOF); 6262311Sjkh 6272311Sjkh /* try to find the name in the name list 6282311Sjkh */ 6292311Sjkh if (names) { 6302311Sjkh for (i = 0; names[i] != NULL; i++) { 6312311Sjkh Debug(DPARS|DEXT, 6322311Sjkh ("get_num, compare(%s,%s)\n", names[i], temp)) 6332311Sjkh if (!strcasecmp(names[i], temp)) { 6342311Sjkh *numptr = i+low; 6352311Sjkh return ch; 6362311Sjkh } 6372311Sjkh } 6382311Sjkh } 6392311Sjkh 6402311Sjkh /* no name list specified, or there is one and our string isn't 6412311Sjkh * in it. either way: if it's all digits, use its magnitude. 6422311Sjkh * otherwise, it's an error. 6432311Sjkh */ 6442311Sjkh if (all_digits) { 6452311Sjkh *numptr = atoi(temp); 6462311Sjkh return ch; 6472311Sjkh } 6482311Sjkh 6492311Sjkh return EOF; 6502311Sjkh} 6512311Sjkh 6522311Sjkh 6532311Sjkhstatic int 6542311Sjkhset_element(bits, low, high, number) 6552311Sjkh bitstr_t *bits; /* one bit per flag, default=FALSE */ 6562311Sjkh int low; 6572311Sjkh int high; 6582311Sjkh int number; 6592311Sjkh{ 6602311Sjkh Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number)) 6612311Sjkh 6622311Sjkh if (number < low || number > high) 6632311Sjkh return EOF; 6642311Sjkh 6652311Sjkh bit_set(bits, (number-low)); 6662311Sjkh return OK; 6672311Sjkh} 668