~ubuntu-branches/ubuntu/precise/super/precise-updates

1 by Robert Luberda
Import upstream version 3.23.0
1
static const char rcsid[] = "$Id: time.c,v 1.28 2004/04/30 17:00:58 will Exp $";
2
#include "super.h"
3
4
#define ANYDAY 7
5
6
/* Maximum length of a time field in super.tab. */
7
#define MAX_T_FIELDLEN 80
8
9
struct weekDay {
10
    char name[20];
11
    char abbr[20];
12
};
13
14
#ifdef HAVE_LOCALE_H
15
    /* We will pick up weekday names dynamically; make the first name a
16
     * null string so we know that initialization has yet to be done.
17
     */
18
    static struct weekDay weekday[7] = { { "", ""}, };
19
#else
20
    static struct weekDay weekday[7] = {
21
		{ "sunday",	"sun", },
22
		{ "monday",	"mon", },
23
		{ "tuesday",	"tue", },
24
		{ "wednesday",	"wed", },
25
		{ "thursday",	"thu", },
26
		{ "friday",	"fri", },
27
		{ "saturday",	"sat"  }
28
    };
29
#endif
30
31
char *
32
dayname(n)
33
int n;		/* day number: 0..6 = sun..sat; 7 = any */
34
{
35
    /* Returns ptr to day of week */
36
    static char *anyday="*";
37
38
#ifdef HAVE_LOCALE_H
39
    if (*(weekday[0].name) == '\0')
40
	readtime_init();
41
#endif
42
43
    if (n < 0 || n > 6)
44
	return anyday;
45
    else
46
	return weekday[n].name;
47
}
48
49
int
50
daynum(t)
51
int t;		/* A Unix time */
52
{
53
    /* Returns day of week, with 0 = sunday.  This routine takes the input
54
     * time as given -- it doesn't assume GMT or any such thing -- and
55
     * returns the day number: you should use localtime() or gmtime() if
56
     * you have them available.
57
     */
58
    int jd = (int) (2440587.5 + ((double) t) / 86400.);
59
    int i = (jd+1) / 7;
60
    return jd - 7 * i + 1;
61
}
62
63
int
64
InsertTimeList(str, wdlist, tl, timetype, invert)
65
char *str;	/* Contains one of the time types accepted by readtimerange(),
66
		 * and extended to allow brace-expansion */
67
char **wdlist;	/* list-expanded form of str */
68
TimeList *tl;	/* Insert time list elements at tl->next */
69
char *timetype;	/* "per-cmd" or "global": a string to use in info msgs */
70
int invert;	/* Inverts the test */
71
{
72
    /* Checks if time is in the range specified by the time string str.
73
     * Returns
74
     *	-1 on syntax error, malloc error, or similar;
75
     *	0 otherwise.
76
     * Side effect: sets the matches.time field.
77
     */
78
    int match;
79
    int iwd;
80
    char *tok;
81
    TimeList *new;
82
83
    for (iwd=0, match=0; (tok=wdlist[iwd]); iwd++) {
84
	new = (TimeList *) malloc(sizeof(TimeList));
85
	if (!new)
86
	    return Error(0, 0, "%t\n\tFailed to malloc space for time entry\n");
87
	new->next = tl->next;
88
	new->te.invert = invert;
89
	/* interpret pat */
90
	if (readtimerange(tok, &new->te.begin, &new->te.end,
91
						&new->te.day) == -1)
92
	    return -1;
93
	if (debug)
94
		(void) fprintf(stderr,
95
	"\tInsert %s time pattern: %stime~%s (min=%d-%d day=%d [dayname=%s])\n",
96
		    timetype,
97
		    invert ? "!" : "", tok, new->te.begin, new->te.end,
98
		    new->te.day, dayname(new->te.day));
99
	tl->next = new;
100
    }
101
    return 0;
102
}
103
104
void
105
matchtime(our, tl)
106
OurTime *our;	/* A time entry to match.  Only the "min" and "day" fields
107
		 * will be used, so it's ok if the "start" field doesn't
108
		 * match them.
109
		 */
110
TimeList *tl;	/* A list of times to match against; 1st used is tl->next */
111
{
112
    /* The time list created by the InsertTimeList function
113
     * is in reverse order, so we only need to find the first
114
     * entry in the list that is a match (+ or -) and stop there.
115
116
     * But if there are no matches, we scan all entries.
117
118
     * Side effects:
119
     *	If an entry is matched, matches.time is set to 0 or 1,
120
     *	    according as the entry is/is not inverted.  Otherwise unmodified.
121
     *	If an entry is non-inverted, matches.alltime_invert is set to 0,
122
     *      otherwise unmodified.  Note that this will include all entries
123
     *      only if there was no match, because scanning stops with a match.
124
125
     */
126
127
    /* The caller should interpret the returned matches as
128
     * follows.
129
     * Case 1:  Our time 12:30
130
     *       time~8-17    !time~12-13   time~23-24
131
     * a. time~8-17 matches, user allowed.
132
     * b. time~12-13 matches, ! means user disallowed.
133
     * c. time~23-24 doesn't match, doesn't change allow/disallow.
134
     * Therefore user disallowed.
135
136
     * Case 2:  Our time 12:30
137
     *     time~8-17    !time~12-13   !time~23-24
138
     * a. time~8-17 matches, user allowed.
139
     * b. time~12-13 matches, ! means user disallowed.
140
     * c. time~23-24 doesn't match, doesn't change allow/disallow.
141
     * Therefore user disallowed.
142
143
     * Case 3:  Our time 12:30
144
     *     !time~23-24
145
     * a. time~23-24 doesn't match, doesn't change allow/disallow.
146
     * b. Thus fall back on default.  Default is
147
     *		i)  allow if _all_ statements are inverted;
148
     *		ii) else disallow.
149
     * Therefore user is allowed.
150
151
     * Case 4:  Our time 17:30
152
     *     time~8-17    !time~12-13   !time~23-24
153
     * a. time~8-17 doesn't match, doesn't change allow/disallow.
154
     * b. time~12-13 doesn't match, doesn't change allow/disallow.
155
     * c. time~23-24 doesn't match, doesn't change allow/disallow.
156
     * d. Thus fall back on default.  Default is
157
     *		i)  allow if _all_ statements are inverted;
158
     *		ii) else disallow.
159
     * Therefore user is disallowed.
160
     */
161
162
    int match;
163
164
    for (tl=tl->next, match=0; tl && !match; tl=tl->next) {
165
	if (!tl->te.invert)
166
	    matches.allinverted = 0;
167
168
	if (our->min >= tl->te.begin && our->min <= tl->te.end &&
169
			    (tl->te.day == ANYDAY || our->day == tl->te.day)) {
170
	    /* matches */
171
	    match = 1;
172
	    matches.time = tl->te.invert ? 0 : 1;
173
	    if (debug || it_came_from_cmdline) {
174
		(void) fprintf(stderr, "\t%s: %stime~%d:%.2d-%d:%.2d/%s\n",
175
		    tl->te.invert ? "Permission denied" : "Permission allowed",
176
		    tl->te.invert ? "!" : "", 
177
		    tl->te.begin/60, tl->te.begin%60,
178
		    tl->te.end/60, tl->te.end%60,
179
		    dayname(tl->te.day));
180
	    }
181
	} else {
182
	    /* Our time is not in the range of this time entry. */
183
	    if (debug || it_came_from_cmdline) {
184
		(void) fprintf(stderr,
185
		    "\tNot applicable: %stime~%d:%.2d-%d:%.2d/%s\n",
186
			tl->te.invert ? "!" : "", 
187
			tl->te.begin/60, tl->te.begin%60,
188
			tl->te.end/60, tl->te.end%60,
189
			dayname(tl->te.day));
190
	    }
191
	}
192
    }
193
}
194
195
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
196
/* Frees all elements in a TimeList, except the one it's given.
197
 * The "next" field of that element is set NULL.
198
 */
199
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
200
void
201
free_TimeList(tl)
202
TimeList *tl;
203
{
204
    TimeList *tlp;
205
206
    if (!tl || !tl->next)
207
	return;
208
    tlp = tl->next;
209
    tl->next = NULL;
210
    for (tl=tl->next ; tl; tl = tlp) {
211
	tlp = tl->next;
212
	free(tl);
213
    }
214
}
215
#ifdef HAVE_LOCALE_H
216
/* The following function should be invoked each time the locale
217
 * is changed (via lang=xxx).
218
 */
219
void
220
readtime_init()
221
{
222
    struct tm ti;
223
    int i;
224
225
    /* We know the following to be a Sunday */
226
    ti.tm_sec = 0;
227
    ti.tm_min = 0;
228
    ti.tm_hour = 12;
229
    ti.tm_mday = 4;
230
    ti.tm_wday = 0;
231
    ti.tm_mon = 0;
232
    ti.tm_year = 70;
233
234
    /* Figure out the days of the week in the current locale */
235
    if (debug)
236
	fprintf(stderr,
237
	"\tInitializing days of week.  Full names and official abbr's:\n\t");
238
239
    for (i = 0; i < 7; i++) {
240
	strftime(weekday[i].name, sizeof(weekday[0].name)-1, "%A", &ti);
241
	strtolower(weekday[i].name);
242
	strftime(weekday[i].abbr, sizeof(weekday[0].abbr)-1, "%a", &ti);
243
	strtolower(weekday[i].abbr);
244
	if (debug) {
245
	    fprintf(stderr, "%s (%s)  ", weekday[i].name, weekday[i].abbr);
246
	    if (i == 3)
247
		fputs("\n\t", stderr);
248
	}
249
	ti.tm_mday++;
250
	ti.tm_wday++;
251
    }
252
253
    if (debug)
254
	fputs("\n", stderr);
255
}
256
#endif
257
258
int
259
readtimerange(str, t1, t2, d)
260
char *str;
261
short *t1;	/* Returned with 1st time, units=minutes, range = 0..1439 */
262
short *t2;	/* Returned with 2nd time, units=minutes, range = 0..1439 */
263
char *d;	/* Returned with day number (0=Sunday) or ANYDAY */
264
{
265
    /* str is a string containing one of the following patterns:
266
     *
267
     * ""			(implies all days, all hours)
268
     * [/]dayname		(implies all hours)
269
     * hh[:mm]-hh[:mm]/dayname
270
     * hh[:mm]-hh[:mm]		(implies all days)
271
     * xhh[:mm]/dayname		(x is one of <, >, <=, >= )
272
     * xhh[:mm]			(implies all days; x is as above)
273
     *
274
     * The valid daynames are (case-insensitive) either an official abbreviated
275
     * day name in the current locale; a 3-or-more character abbreviation
276
     * of the full weekday; or "*", meaning any day.
277
278
     * For convenience, the upper time can be 24:00, but it is converted
279
     * to 11:59.
280
281
     * Returns: -1 on invalid time pattern; 0 otherwise.
282
283
     */
284
    char *s, *p;
285
    int hh1, mm1;
286
    int hh2, mm2;
287
    int i, l;
288
    int has_relop;
289
#ifdef HAVE_ENUM
290
    enum { LT, LE, GT, GE } relop;
291
#else
292
    /* No enums! */
293
#define LT 0
294
#define LE 1
295
#define GT 2
296
#define GE 3
297
    int relop;
298
#endif
299
300
    if (strlen(str) > MAX_T_FIELDLEN) {
301
	return Error(0, 0,
302
		"%t\n\tInvalid time range; first %d chars are: <%*.*s>\n",
303
		MAX_T_FIELDLEN, MAX_T_FIELDLEN, str);
304
    }
305
306
#ifdef HAVE_LOCALE_H
307
    if (*(weekday[0].name) == '\0')
308
	readtime_init();
309
#endif
310
311
    s = str;
312
    has_relop = 0;
313
    if (*s == '<' || *s == '>') {
314
	/* Must be xhh[:mm][/dayname] */
315
	has_relop = 1;
316
	switch (*s++) {
317
	case '<':
318
	    relop = (*s == '=') ? (s++, LE) : LT;
319
	    break;
320
	case '>':
321
	    relop = (*s == '=') ? (s++, GE) : GT;
322
	    break;
323
	default:
324
	    return Error(0, 0, "%t\n\tInvalid time range <%s>\n", str);
325
	}
326
	if (!isdigit(*s))
327
	    return Error(0, 0,
328
		"%t\n\tInvalid time range <%s> (invalid hour part)\n",
329
	    str);
330
331
	hh1 = strtol(s, &p, 10);
332
	if (p == s || hh1 > 24 || hh1 < 0)
333
	    return Error(0, 0,
334
		"%t\n\tInvalid time range <%s> (invalid hour part)\n", str);
335
336
	s = p;
337
	if (*s != ':') {
338
	    /* minutes not given */
339
	    mm1 = 0;
340
	} else {
341
	    mm1 = strtol(++s, &p, 10);
342
	    if (p == s || mm1 > 59 || mm1 < 0 || (mm1 > 0 && hh1 == 24))
343
		return Error(0, 0,
344
		"%t\n\tInvalid time range <%s> (invalid minute part)\n", str);
345
	}
346
347
	s = p;
348
349
	switch (relop) {
350
	case LT:
351
	    *t1 = 0;
352
	    *t2 = hh1*60 + mm1 - 1;
353
	    break;
354
	case LE:
355
	    *t1 = 0;
356
	    *t2 = hh1*60 + mm1;
357
	    break;
358
	case GT:
359
	    *t1 = hh1*60 + mm1 + 1;
360
	    *t2 = 24*60 - 1;
361
	    break;
362
	case GE:
363
	    *t1 = hh1*60 + mm1;
364
	    *t2 = 24*60 - 1;
365
	    break;
366
	default:
367
	    return Error(0, 0,
368
			"%t\n\tInternal time range error -- relop is %d\n",
369
			relop);
370
	}
371
372
    } else if (isdigit(*str)) {
373
	/* It must begin hh:mm-hh:mm */
374
	s = str;
375
376
	hh1 = strtol(s, &p, 10);
377
	if (p == s || hh1 > 24 || hh1 < 0)
378
	    return Error(0, 0,
379
		"%t\n\tInvalid time range <%s> (invalid first hr)\n", str);
380
381
	s = p;
382
	if (*s != ':') {
383
	    mm1 = 0;
384
	} else {
385
	    mm1 = strtol(++s, &p, 10);
386
	    if (p == s || mm1 > 59 || mm1 < 0 || (mm1 > 0 && hh1 == 24))
387
		return Error(0, 0,
388
		    "%t\n\tInvalid time range <%s> (invalid first min)\n", str);
389
	}
390
391
	*t1 = hh1*60 + mm1;
392
393
	s = p;
394
	if (*s++ != '-')
395
	    return Error(0, 0,
396
	    "%t\n\tInvalid time range <%s> (expected '-' in hh:mm-hh:mm)\n",
397
	    str);
398
399
	hh2 = strtol(s, &p, 10);
400
	if (p == s || hh2 > 24 || hh2 < 0)
401
	    return Error(0, 0,
402
		"%t\n\tInvalid time range <%s> (invalid second hr)\n", str);
403
404
	s = p;
405
	if (*s != ':') {
406
	    mm2 = 0;
407
	} else {
408
	    mm2 = strtol(++s, &p, 10);
409
	    if (p == s || mm2 > 59 || mm2 < 0 || (mm2 > 0 && hh2 == 24))
410
		return Error(0, 0,
411
		    "%t\n\tInvalid time range <%s> (invalid second min)\n",
412
		    str);
413
	}
414
415
	s = p;
416
	if (*s != '\0' && *s != '/')
417
	    return Error(0, 0,
418
	    "%t\n\tInvalid time range <%s> (invalid char after time range)\n",
419
	    str);
420
421
	*t2 = hh2*60 + mm2;
422
423
    } else {
424
	/* We've seen no time part; must be dayname only; time is all hrs */
425
	s = str;
426
	*t1 = 0;
427
	*t2 = 24 * 60;
428
    }
429
430
    if (*s == '/')	/* Skip field separator, if present */
431
	s++;
432
433
    if (*s == '\0' || strcmp(s, "*") == 0) {
434
	*d = ANYDAY;	/* all days */
435
    } else {
436
	/* It must be a weekday, expressed in the current locale.
437
	 * Allow just dayname or /dayname.
438
	 */
439
	char buf[MAX_T_FIELDLEN+1];
440
	strcpy(buf, s);
441
	strtolower(buf);
442
	for (i=0; i<7; i++) {
443
#if 0
444
	    fprintf(stderr, "HERE: s=<%s> buf=<%s> day=<%s> abbr=<%s>\n",
445
		s, buf, weekday[i].name, weekday[i].abbr);
446
#endif
447
	    if (strlen(buf) < 3) {
448
		;	/* not accepted -- must be at least three chars */
449
	    } else if (strncmp(weekday[i].name, buf, (l=strlen(buf))) == 0) {
450
		*d = i;
451
		break;
452
	    } else if (strcmp(weekday[i].abbr, buf) == 0) {
453
		*d = i;
454
		break;
455
	    }
456
	}
457
	if (i >= 7)
458
	    return Error(0, 0,
459
		"%t\n\tInvalid time range <%s> (invalid day field)\n",
460
		str);
461
    }
462
    /* Convert 24:00 to 23:59 */
463
    if (*t1 == 24*60)
464
	(*t1)--;
465
    if (*t2 == 24*60)
466
	(*t2)--;
467
    if (*t1 < 0 || *t2 < 0 || *t1 > 24*60-1 || *t2 > 24*60-1)
468
	return Error(0, 0, "%t\n\tInvalid time range <%s>\n", str);
469
    return 0;
470
}
471
472
int
473
readtime(str, t, d)
474
char *str;
475
short *t;	/* Returned with time, units=minutes, range = 0..1439 */
476
char *d;	/* Returned with day number (0=Sunday) */
477
{
478
    /* str must be like:
479
     *
480
     * hh[:mm]/dayname
481
     *
482
     * The valid daynames are (case-insensitive) either an official abbreviated
483
     * day name in the current locale; a 3-or-more character abbreviation
484
     * of the full weekday; or "*", meaning any day.
485
486
     * Returns: -1 on invalid time pattern; 0 otherwise.
487
488
     */
489
    char buf[MAX_T_FIELDLEN+1];
490
    int hh, mm;
491
    int i, l;
492
493
#ifdef HAVE_LOCALE_H
494
    if (*(weekday[0].name) == '\0')
495
	readtime_init();
496
#endif
497
498
    if (strlen(str) > sizeof(buf)-1)
499
	return Error(0, 0, "%t\n\tInvalid time <%s>\n", str);
500
    if (sscanf(str, "%d:%d/%s", &hh, &mm, buf) != 3) {
501
	mm = 0;
502
	if ( (sscanf(str, "%d:/%s", &hh, buf) != 2) &&
503
		(sscanf(str, "%d/%s", &hh, buf) != 2) ) {
504
	    return Error(0, 0,
505
	    "%t\n\tInvalid time <%s>; format must be hh[:mm]/dayname\n", str);
506
	}
507
    }
508
    if (hh > 23 || hh < 0)
509
	return Error(0, 0, "%t\n\tInvalid time <%s> (invalid hr)\n", str);
510
    if (mm > 59 || mm < 0)
511
	return Error(0, 0, "%t\n\tInvalid time <%s> (invalid min)\n", str);
512
513
    *t = hh*60 + mm;
514
515
    /* The dayname must be a weekday, expressed in the current locale. */
516
    strtolower(buf);
517
    for (i=0; i<7; i++) {
518
	if (strlen(buf) < 3) {
519
	    ;	/* not accepted -- must be at least three chars */
520
	} else if (strncmp(weekday[i].name, buf, (l=strlen(buf))) == 0) {
521
	    *d = i;
522
	    break;
523
	} else if (strcmp(weekday[i].abbr, buf) == 0) {
524
	    *d = i;
525
	    break;
526
	}
527
    }
528
    if (i >= 7)
529
	return Error(0, 0, "%t\n\tInvalid time <%s> (invalid dayname)\n", str);
530
531
    return 0;
532
}