1 /*
2  *  libzvbi - Program Delivery Control
3  *
4  *  Copyright (C) 2000-2004 Michael H. Schimek
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License version 2 as
8  *  published by the Free Software Foundation.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with this program; if not, write to the Free Software
17  *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  */
19 
20 /* $Id: pdc.c,v 1.4 2009/03/23 01:30:33 mschimek Exp $ */
21 
22 #include "../site_def.h"
23 
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27 
28 #include <ctype.h>
29 #include <float.h>		/* FLT_MAX, DBL_MAX */
30 #include <errno.h>
31 
32 #include "misc.h"
33 #include "hamm.h"		/* vbi_unpar8() */
34 #include "bcd.h"		/* vbi_is_bcd() */
35 #include "pdc.h"
36 #include "conv.h"
37 
38 /**
39  * @addtogroup ProgramID VPS/PDC Program ID
40  * @ingroup LowDec
41  * @brief Functions to decode VPS/PDC Program IDs and helper functions.
42  *
43  * Program IDs are transmitted by networks to remotely control video
44  * recorders. They can be used to
45  * - start and stop recording exactly when a program starts and ends,
46  *   even when the program is early or late or overruns,
47  * - pause recording during a planned or unplanned interruption
48  *   (you can safely assume that no station transmits a pause code
49  *   during commercial breaks),
50  * - keep recording a program which continues on a different channel,
51  * - record a program after it has been postponed to a later date
52  *   and possibly a different channel,
53  * - record all episodes of a series without prior knowledge of the
54  *   broadcast times,
55  * - alert the user about emergency messages.
56  *
57  * The basic principle is to transmit a label along with the program
58  * containing the originally announced start date and time. When
59  * the label is no longer transmitted the program has ended. When two
60  * programs on different channels are scheduled for recording the
61  * recorder may have to scan the channels alternately. Better accuracy
62  * than a few seconds within the actual start should not be expected.
63  *
64  * Libzvbi supports Program IDs transmitted in Teletext packet 8/30
65  * format 2 and in VPS packets as defined in EN 300 231 "Television
66  * systems; Specification of the domestic video Programme Delivery
67  * Control system (PDC)", and DVB PDC descriptors as defined in EN 300
68  * 468 "Specification for Service Information (SI) in DVB systems".
69  * Support for XDS Current/Future Program ID packets as defined in EIA
70  * 608-B "Recommended Practice for Line 21 Data Service" is planned
71  * but not implemented yet.
72  *
73  * Program IDs are available through the low level functions
74  * vbi_decode_teletext_8302_pdc(), vbi_decode_vps_pdc(),
75  * vbi_decode_dvb_pdc_descriptor() and the @a vbi_decoder event @c
76  * VBI_EVENT_PROG_ID.
77  *
78  * @example examples/pdc1.c
79  * @example examples/pdc2.c
80  */
81 
82 /* XXX Preliminary, will not be returned to clients. */
83 enum {
84 	VBI_ERR_NO_TIME		= 0x7081900,
85 	VBI_ERR_INVALID_PIL,
86 };
87 
88 /**
89  * @internal
90  * @param pil vbi_pil to print.
91  * @param fp Destination stream.
92  *
93  * Prints a vbi_pil as service code or date and time string without
94  * trailing newline. This is intended for debugging.
95  */
96 void
_vbi_pil_dump(vbi_pil pil,FILE * fp)97 _vbi_pil_dump			(vbi_pil		pil,
98 				 FILE *			fp)
99 {
100 	switch (pil) {
101 	case VBI_PIL_TIMER_CONTROL:
102 		fputs ("TC", fp);
103 		break;
104 
105 	case VBI_PIL_INHIBIT_TERMINATE:
106 		fputs ("RI/T", fp);
107 		break;
108 
109 	case VBI_PIL_INTERRUPTION:
110 		fputs ("INT", fp);
111 		break;
112 
113 	case VBI_PIL_CONTINUE:
114 		fputs ("CONT", fp);
115 		break;
116 
117 	case VBI_PIL_NSPV:
118 		/* VBI_PIL_NSPV (PDC) == VBI_PIL_END (XDS) */
119 		fputs ("NSPV/END", fp);
120 		break;
121 
122 	default:
123 		fprintf (fp, "%05x (%02u-%02u %02u:%02u)",
124 			 pil,
125 			 VBI_PIL_MONTH (pil),
126 			 VBI_PIL_DAY (pil),
127 			 VBI_PIL_HOUR (pil),
128 			 VBI_PIL_MINUTE (pil));
129 		break;
130 	}
131 }
132 
133 #if 3 != VBI_VERSION_MINOR
134 #  define vbi_cni_type_name(type) ""
135 #endif
136 
137 /**
138  * @internal
139  * @param pid vbi_program_id structure to print.
140  * @param fp Destination stream.
141  *
142  * Prints the contents of a vbi_program_id structure as a string
143  * without trailing newline. This is intended for debugging.
144  */
145 void
_vbi_program_id_dump(const vbi_program_id * pid,FILE * fp)146 _vbi_program_id_dump		(const vbi_program_id *	pid,
147 				 FILE *			fp)
148 {
149 	static const char *pcs_audio [] = {
150 		"UNKNOWN",
151 		"MONO",
152 		"STEREO",
153 		"BILINGUAL"
154 	};
155 
156 	fprintf (fp, "ch=%u cni=%04x (%s) pil=",
157 		 pid->channel,
158 		 pid->cni,
159 		 vbi_cni_type_name (pid->cni_type));
160 
161 	_vbi_pil_dump (pid->pil, fp);
162 
163 	fprintf (fp,
164 		 " luf=%u mi=%u prf=%u "
165 		 "pcs=%s pty=%02x tape_delayed=%u",
166 		 pid->luf, pid->mi, pid->prf,
167 		 pcs_audio[pid->pcs_audio],
168 		 pid->pty, pid->tape_delayed);
169 }
170 
171 /**
172  * @internal
173  * @param pil The PIL will be stored here.
174  * @param inout_s Pointer to input buffer pointer, which will be
175  *   incremented by the number of bytes read. The buffer must contain
176  *   a NUL-terminated ASCII or UTF-8 string.
177  *
178  * Converts a date of the format MM-DDThh:mm to a PIL. MM must be in
179  * range 00 ... 15, DD and hh in range 00 ... 31 and mm in range 00
180  * ... 63. The MM-DDT part can be omitted. In this case day and month
181  * zero will be stored in @a pil. The separators -T: can be omitted.
182  * Additionally the symbols "cont[inue]", "end", "inhibit",
183  * "int[erruption]", "nspv", "rit", "terminate", "tc" and "timer" are
184  * recognized and converted to the respective PIL service
185  * code. Leading white space and upper/lower case is ignored.
186  *
187  * You can call the vbi_pil_is_valid_date() function to determine if
188  * a valid date and time or service code has been entered.
189  *
190  * @returns
191  * @c FALSE on syntax errors. In this case @a *pil and @a *inout_s
192  * remain unmodified.
193  */
194 vbi_bool
_vbi_pil_from_string(vbi_pil * pil,const char ** inout_s)195 _vbi_pil_from_string		(vbi_pil *		pil,
196 				 const char **		inout_s)
197 {
198 	const char *s;
199 	unsigned int value[4];
200 	unsigned int i;
201 	unsigned int n_fields;
202 	unsigned int sep_mask;
203 
204 	assert (NULL != pil);
205 	assert (NULL != inout_s);
206 	assert (NULL != *inout_s);
207 
208 	s = *inout_s;
209 
210 	while (isspace (*s))
211 		++s;
212 
213 	if (!isdigit (*s)) {
214 		static const _vbi_key_value_pair symbols [] = {
215 			{ "cont",		VBI_PIL_CONTINUE },
216 			{ "continue",		VBI_PIL_CONTINUE },
217 			{ "end",		VBI_PIL_END },
218 			{ "inhibit",		VBI_PIL_INHIBIT_TERMINATE },
219 			{ "int",		VBI_PIL_INTERRUPTION },
220 			{ "interruption",	VBI_PIL_INTERRUPTION },
221 			{ "nspv",		VBI_PIL_NSPV },
222 			{ "rit",		VBI_PIL_INHIBIT_TERMINATE },
223 			{ "terminate",		VBI_PIL_INHIBIT_TERMINATE },
224 			{ "tc",			VBI_PIL_TIMER_CONTROL },
225 			{ "timer",		VBI_PIL_TIMER_CONTROL }
226 		};
227 		int n;
228 
229 		if (_vbi_keyword_lookup (&n, inout_s,
230 					  symbols, N_ELEMENTS (symbols))) {
231 			*pil = n;
232 			return TRUE;
233 		} else {
234 			return FALSE;
235 		}
236 	}
237 
238 	n_fields = 4;
239 	sep_mask = 0;
240 
241 	for (i = 0; i < n_fields; ++i) {
242 		int c;
243 
244 		if (!isdigit (s[0])) {
245 			if (2 == i && 0 == sep_mask) {
246 				n_fields = 2;
247 				break;
248 			}
249 			return FALSE;
250 		} else if (!isdigit (s[1])) {
251 			return FALSE;
252 		}
253 
254 		value[i] = (s[0] - '0') * 10 + s[1] - '0';
255 
256 		s += 2;
257 		c = *s;
258 
259 		if (i < n_fields - 1) {
260 			if (0 == i && ':' == c) {
261 				n_fields = 2;
262 				sep_mask |= 1 << 2;
263 				++s;
264 			} else if ("-T:"[i] == c) {
265 				sep_mask |= 1 << i;
266 				++s;
267 			}
268 		}
269 	}
270 
271 	if (n_fields < 4) {
272 		value[3] = value[1];
273 		value[2] = value[0];
274 		value[1] = 0;
275 		value[0] = 0;
276 	}
277 
278 	if (unlikely (value[0] > 15
279 		      || (value[1] | value[2]) > 31
280 		      || value[3] > 63))
281 		return FALSE;
282 
283 	*inout_s = s;
284 
285 	*pil = VBI_PIL (value[0], value[1], value[2], value[3]);
286 
287 	return TRUE;
288 }
289 
290 static const uint8_t
291 month_days[12] = {
292 	31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
293 };
294 
295 /**
296  * @param pil Program Identification Label.
297  *
298  * Determines if @a pil represents a valid date and time.
299  *
300  * Since PILs have no year field February 29th is considered valid.
301  * You can find out if this date is valid in a given year with the
302  * vbi_pil_to_time() function.
303  *
304  * 24:00 is not valid (an unreal hour) as defined in EN 300 231
305  * Annex F and EIA 608-B Section 9.5.1.1.
306  *
307  * @returns
308  * @c TRUE if @a pil represents a valid date and time, @c FALSE
309  * if @a pil contains an unreal date or time (e.g. Jan 0 27:61),
310  * a service code or unallocated code.
311  *
312  * @since 0.2.34
313  */
314 vbi_bool
vbi_pil_is_valid_date(vbi_pil pil)315 vbi_pil_is_valid_date		(vbi_pil		pil)
316 {
317 	unsigned int month;
318 	unsigned int day;
319 
320 	month = VBI_PIL_MONTH (pil);
321 	day = VBI_PIL_DAY (pil);
322 
323 	/* Note this also checks for zero month and day. */
324 	return (month - 1 < 12
325 		&& day - 1 < month_days[month - 1]
326 		&& VBI_PIL_HOUR (pil) < 24
327 		&& VBI_PIL_MINUTE (pil) < 60);
328 }
329 
330 static vbi_bool
tm_mon_mday_from_pil(struct tm * tm,vbi_pil pil)331 tm_mon_mday_from_pil		(struct tm *		tm,
332 				 vbi_pil		pil)
333 {
334 	unsigned int month0;
335 
336 	month0 = VBI_PIL_MONTH (pil) - 1;
337 
338 	if (month0 >= (unsigned int) tm->tm_mon + 6) {
339 		/* POSIX defines tm_year as int. */
340 		if (unlikely (tm->tm_year <= INT_MIN))
341 			return FALSE;
342 		--tm->tm_year;
343 	} else if (month0 + 6 < (unsigned int) tm->tm_mon) {
344 		if (unlikely (tm->tm_year >= INT_MAX))
345 			return FALSE;
346 		++tm->tm_year;
347 	}
348 
349 	tm->tm_mon = month0;
350 	tm->tm_mday = VBI_PIL_DAY (pil);
351 
352 	return TRUE;
353 }
354 
355 static vbi_bool
is_leap_year(unsigned int year)356 is_leap_year			(unsigned int		year)
357 {
358 	if (0 != year % 4)
359 		return FALSE;
360 	else if (0 == year % 400)
361 		return TRUE;
362 	else
363 		return (0 != year % 100);
364 }
365 
366 static vbi_bool
tm_leap_day_check(const struct tm * tm)367 tm_leap_day_check		(const struct tm *	tm)
368 {
369 	return (1 != tm->tm_mon
370 		|| tm->tm_mday <= 28
371 		|| is_leap_year (tm->tm_year + 1900));
372 }
373 
374 static vbi_bool
restore_tz(char ** old_tz,const char * tz)375 restore_tz			(char **		old_tz,
376 				 const char *		tz)
377 {
378 	int saved_errno;
379 
380 	if (NULL == tz)
381 		return TRUE;
382 
383 	if (NULL == *old_tz) {
384 		/* Errors ignored, should never fail. */
385 		unsetenv ("TZ");
386 	} else {
387 		if (unlikely (-1 == setenv ("TZ", *old_tz,
388 					    /* overwrite */ TRUE))) {
389 			/* ENOMEM: Out of memory. */
390 			saved_errno = errno;
391 
392 			free (*old_tz);
393 			*old_tz = NULL;
394 
395 			errno = saved_errno;
396 			return FALSE;
397 		}
398 
399 		free (*old_tz);
400 		*old_tz = NULL;
401 	}
402 
403 	tzset ();
404 
405 	return TRUE;
406 }
407 
408 static vbi_bool
change_tz(char ** old_tz,const char * tz)409 change_tz			(char **		old_tz,
410 				 const char *		tz)
411 {
412 	const char *s;
413 	int saved_errno;
414 
415 	*old_tz = NULL;
416 
417 	s = getenv ("TZ");
418 	if (NULL != s) {
419 		*old_tz = strdup (s);
420 		if (unlikely (NULL == *old_tz)) {
421 			errno = ENOMEM;
422 			return FALSE;
423 		}
424 	}
425 
426 	if (unlikely (-1 == setenv ("TZ", tz, /* overwrite */ TRUE))) {
427 		/* EINVAL: tz is "" or contains '='.
428 		   ENOMEM: Out of memory. */
429 		saved_errno = errno;
430 
431 		free (*old_tz);
432 		*old_tz = NULL;
433 
434 		errno = saved_errno;
435 		return FALSE;
436 	}
437 
438 	tzset ();
439 
440 	return TRUE;
441 }
442 
443 static vbi_bool
localtime_tz(struct tm * tm,char ** old_tz,time_t t,const char * tz)444 localtime_tz			(struct tm *		tm,
445 				 char **		old_tz,
446 				 time_t			t,
447 				 const char *		tz)
448 {
449 	int saved_errno;
450 
451 	*old_tz = NULL;
452 
453 	/* Some system calls below may not set errno on failure. */
454 	errno = 0;
455 
456 	if (NULL != tz) {
457 		if (unlikely (!change_tz (old_tz, tz)))
458 			return FALSE;
459 	}
460 
461 	CLEAR (*tm);
462 
463 	if ((time_t) -1 == t) {
464 		if (unlikely ((time_t) -1 == time (&t))) {
465 			saved_errno = errno;
466 
467 			/* We do not ignore errors here because the
468 			   information that TZ wasn't restored seems
469 			   more important to me. */
470 			if (unlikely (!restore_tz (old_tz, tz)))
471 				return FALSE;
472 
473 			/* time() can fail but POSIX defines no
474 			   error code. On Linux EFAULT is possible. */
475 			if (0 == saved_errno)
476 				errno = VBI_ERR_NO_TIME;
477 			else
478 				errno = saved_errno;
479 			return FALSE;
480 		}
481 	}
482 
483 	if (unlikely (NULL == localtime_r (&t, tm))) {
484 		saved_errno = errno;
485 
486 		if (unlikely (!restore_tz (old_tz, tz)))
487 			return FALSE;
488 
489 		errno = saved_errno;
490 		return FALSE;
491 	}
492 
493 	return TRUE;
494 }
495 
496 #undef mktime
497 #undef timegm
498 
499 time_t
500 _vbi_mktime			(struct tm *		tm);
501 
502 /**
503  * @internal
504  * GNU libc mktime() appears to clamp its result, but it will be used
505  * in video recording apps where malfunction is not an option. I want
506  * it to fail with EOVERFLOW as POSIX suggests, so the caller can
507  * detect and address the problem. Don't call this function directly,
508  * we #define mktime to override the libc version.
509  */
510 time_t
_vbi_mktime(struct tm * tm)511 _vbi_mktime			(struct tm *		tm)
512 {
513 	time_t result = mktime (tm);
514 
515 	if (unlikely (result <= TIME_MIN || result >= TIME_MAX)) {
516 		errno = EOVERFLOW;
517 		result = (time_t) -1;
518 	}
519 
520 	return result;
521 }
522 
523 #ifdef HAVE_TIMEGM
524 
525 /**
526  * @internal
527  * timegm() is a GNU extension. It works like mktime(), but interprets
528  * @a tm as a time in the UTC zone instead of the time zone defined by
529  * the TZ environment variable. GNU libc timegm() appears to clamp the
530  * result, but we want an EOVERFLOW error instead. In fact no error
531  * codes are defined for timegm(), so we check for 0 == errno
532  * too. Note this function is NOT THREAD SAFE because its replacement
533  * is not. Don't call this function directly, we #define timegm to
534  * override the libc version.
535  */
536 time_t
_vbi_timegm(struct tm * tm)537 _vbi_timegm			(struct tm *		tm)
538 {
539 	time_t result;
540 
541 	errno = 0;
542 	result = timegm (tm);
543 
544 	if (unlikely ((time_t) -1 == result)) {
545 		if (0 == errno)
546 			errno = EOVERFLOW;
547 	} else if (unlikely (result <= TIME_MIN || result >= TIME_MAX)) {
548 		errno = EOVERFLOW;
549 		result = (time_t) -1;
550 	}
551 
552 	return result;
553 }
554 
555 #else
556 
557 time_t
558 _vbi_timegm			(struct tm *		tm);
559 
560 /**
561  * @internal
562  *
563  * Replacement for timegm() on non-GNU systems. Note this function is
564  * NOT THREAD SAFE because the C library permits the conversion of a
565  * broken-down time in an arbitrary time zone only by setting the TZ
566  * environment variable. The function may also fail to restore the
567  * value of TZ if insufficient memory is available. Don't call this
568  * function directly, we #define timegm.
569  */
570 time_t
_vbi_timegm(struct tm * tm)571 _vbi_timegm			(struct tm *		tm)
572 {
573 	char *old_tz;
574 	int saved_errno;
575 	time_t result;
576 
577 	if (unlikely (!change_tz (&old_tz, "UTC")))
578 		return (time_t) -1;
579 
580 	result = mktime (tm);
581 	if (unlikely (result <= TIME_MIN
582 		      || result >= TIME_MAX)) {
583 		saved_errno = EOVERFLOW;
584 		result = (time_t) -1;
585 	} else {
586 		saved_errno = errno;
587 	}
588 
589 	if (unlikely (!restore_tz (&old_tz, "UTC")))
590 		return (time_t) -1;
591 
592 	errno = saved_errno;
593 	return result;
594 }
595 
596 #endif /* !HAVE_TIMEGM */
597 
598 #define mktime _vbi_mktime
599 #define timegm _vbi_timegm
600 
601 static time_t
valid_pil_lto_to_time(vbi_pil pil,time_t start,int seconds_east)602 valid_pil_lto_to_time		(vbi_pil		pil,
603 				 time_t			start,
604 				 int			seconds_east)
605 {
606 	struct tm tm;
607 
608 	/* Some system calls below may not set errno on failure, but
609 	   we must distinguish those errors from VBI_ERR_INVALID_PIL
610 	   for valid_pil_lto_validity_window(). */
611 	errno = 0;
612 
613 	CLEAR (tm);
614 
615 	if ((time_t) -1 == start) {
616 		if (unlikely ((time_t) -1 == time (&start))) {
617 			/* time() can fail but POSIX defines no
618 			   error code. On Linux EFAULT is possible. */
619 			if (0 == errno)
620 				errno = VBI_ERR_NO_TIME;
621 			return (time_t) -1;
622 		}
623 	}
624 
625 	if (seconds_east < 0) {
626 		/* Note start can be negative. */
627 		if (unlikely (start < -seconds_east)) {
628 			errno = EOVERFLOW;
629 			return (time_t) -1;
630 		}
631 	} else {
632 		if (unlikely (start > TIME_MAX - seconds_east)) {
633 			errno = EOVERFLOW;
634 			return (time_t) -1;
635 		}
636 	}
637 
638 	start += seconds_east;
639 
640 	if (unlikely (NULL == gmtime_r (&start, &tm)))
641 		return (time_t) -1;
642 
643 	if (unlikely (!tm_mon_mday_from_pil (&tm, pil))) {
644 		errno = EOVERFLOW;
645 		return (time_t) -1;
646 	}
647 
648 	if (unlikely (!tm_leap_day_check (&tm))) {
649 		errno = VBI_ERR_INVALID_PIL;
650 		return (time_t) -1;
651 	}
652 
653 	tm.tm_hour = VBI_PIL_HOUR (pil);
654 	tm.tm_min = VBI_PIL_MINUTE (pil);
655 	tm.tm_sec = 0;
656 
657 	start = timegm (&tm);
658 	if (unlikely ((time_t) -1 == start))
659 		return (time_t) -1;
660 
661 	if (seconds_east > 0) {
662 		/* Note start can be negative. */
663 		if (unlikely (start < seconds_east)) {
664 			errno = EOVERFLOW;
665 			return (time_t) -1;
666 		}
667 	} else {
668 		if (unlikely (start > TIME_MAX + seconds_east)) {
669 			errno = EOVERFLOW;
670 			return (time_t) -1;
671 		}
672 	}
673 
674 	return start - seconds_east;
675 }
676 
677 /**
678  * @param pil Program Identification Label (PIL) to convert.
679  * @param start The most recently announced start time of the
680  *   program. If zero the current system time will be used.
681  * @param seconds_east A time zone specified as an offset in seconds
682  *   east of UTC, for example +1 * 60 * 60 for CET. @a seconds_east
683  *   may include a daylight-saving time (DST) offset.
684  *
685  * This function converts a PIL to a time_t in the same manner
686  * localtime() converts a broken-down time to time_t.
687  *
688  * Since PILs do not contain a year field, the year is determined from
689  * the @a start parameter, that is the most recently announced start
690  * time of the program or "AT-1" in EN 300 231 parlance. If @a pil
691  * contains a month more than five months after @a start, @a pil is
692  * assumed to refer to an earlier date than @a start.
693  *
694  * @a pil is assumed to be a time in the time zone specified by @a
695  * seconds_east. @a start will be converted to a local time in the
696  * same time zone to determine the correct year.
697  *
698  * Teletext packet 8/30 format 2, VPS and DVB PDC descriptors give a
699  * PIL relative to the time zone of the intended audience of the
700  * program. An offset from UTC including the DST offset in effect at
701  * the specified date may be available on Teletext program
702  * announcement pages (see struct vbi_preselection). Another offset
703  * from UTC including the @em current DST offset is available as @c
704  * VBI_EVENT_LOCAL_TIME, but of course that information is
705  * insufficient to determine if DST is in effect at other dates.
706  *
707  * XDS Current/Future Program ID packets give a PIL relative to UTC,
708  * so @a seconds_east should be zero.
709  *
710  * @returns
711  * The PIL as a time_t, that is the number of seconds since
712  * 1970-01-01 00:00 UTC. On error the function
713  * returns (time_t) -1:
714  * - @a pil does not contain a valid date or time. February 29th is
715  *   a valid date only if the estimated year is a leap year.
716  * - @a start is zero and the current system time could not be
717  *   determined.
718  * - The time specified by @a pil, @a start and @a seconds_east
719  *   cannot be represented as a time_t value (2038 is closer than
720  *   you think!).
721  *
722  * @since 0.2.34
723  *
724  * @bug
725  * This function is not thread safe. That is a limitation of the C
726  * library which permits the conversion of a broken-down time in an
727  * arbitrary time zone only by setting the TZ environment variable.
728  * The function may also fail to restore the value of TZ if
729  * insufficient memory is available.
730  */
731 time_t
vbi_pil_lto_to_time(vbi_pil pil,time_t start,int seconds_east)732 vbi_pil_lto_to_time		(vbi_pil		pil,
733 				 time_t			start,
734 				 int			seconds_east)
735 {
736 	time_t t;
737 
738 	if (unlikely (!vbi_pil_is_valid_date (pil))) {
739 #if 3 == VBI_VERSION_MINOR
740 		errno = VBI_ERR_INVALID_PIL;
741 #else
742 		errno = 0;
743 #endif
744 		return (time_t) -1;
745 	}
746 
747 	t = valid_pil_lto_to_time (pil, start, seconds_east);
748 #if 2 == VBI_VERSION_MINOR
749 	errno = 0;
750 #endif
751 	return t;
752 }
753 
754 /**
755  * @param pil Program Identification Label (PIL) to convert.
756  * @param start The most recently announced start time of the
757  *   program. If zero the current system time will be used.
758  * @param tz A time zone name in the same format as the TZ environment
759  *   variable. If @c NULL the current value of TZ will be used.
760  *
761  * This function converts a PIL to a time_t in the same manner
762  * localtime() converts a broken-down time to time_t.
763  *
764  * Since PILs do not contain a year field, the year is determined from
765  * the @a start parameter, that is the most recently announced start
766  * time of the program or "AT-1" in EN 300 231 parlance. If @a pil
767  * contains a month more than five months after @a start, @a pil is
768  * assumed to refer to an earlier date than @a start.
769  *
770  * @a pil is assumed to be a time in the time zone @a tz. @a start
771  * will be converted to a local time in the same time zone to
772  * determine the correct year.
773  *
774  * Teletext packet 8/30 format 2, VPS and DVB PDC descriptors give a
775  * PIL relative to the time zone of the intended audience of the
776  * program. Ideally the time zone would be specified as a geographic
777  * area like "Europe/London", such that the function can determine
778  * the correct offset from UTC and if daylight-saving time is in
779  * effect at the specified date. See the documentation of the
780  * localtime() function and the TZ environment variable for details.
781  *
782  * XDS Current/Future Program ID packets give a PIL relative to UTC.
783  * Just specify time zone "UTC" in this case.
784  *
785  * @returns
786  * The PIL as a time_t, that is the number of seconds since
787  * 1970-01-01 00:00 UTC. On error the function
788  * returns (time_t) -1:
789  * - @a pil does not contain a valid date or time. February 29th is
790  *   a valid date only if the estimated year is a leap year.
791  * - @a tz is empty or contains an equal sign '='.
792  * - @a start is zero and the current system time could not be
793  *   determined.
794  * - The time specified by @a pil, @a start and @a tz cannot be
795  *   represented as a time_t value.
796  * - Insufficient memory was available.
797  *
798  * @since 0.2.34
799  *
800  * @bug
801  * This function is not thread safe unless @a tz is @c NULL. That is
802  * a limitation of the C library which permits the conversion of a
803  * broken-down time in an arbitrary time zone only by setting the TZ
804  * environment variable. The function may also fail to restore the
805  * value of TZ if insufficient memory is available.
806  */
807 time_t
vbi_pil_to_time(vbi_pil pil,time_t start,const char * tz)808 vbi_pil_to_time			(vbi_pil		pil,
809 				 time_t			start,
810 				 const char *		tz)
811 {
812 	struct tm tm;
813 	char *old_tz;
814 	time_t result;
815 	int saved_errno;
816 
817 	if (unlikely (!vbi_pil_is_valid_date (pil))) {
818 #if 3 == VBI_VERSION_MINOR
819 		errno = VBI_ERR_INVALID_PIL;
820 #else
821 		errno = 0;
822 #endif
823 		return (time_t) -1;
824 	}
825 
826 	if (NULL != tz && 0 == strcmp (tz, "UTC")) {
827 		time_t t;
828 
829 		t = valid_pil_lto_to_time (pil, start,
830 					   /* seconds_east */ 0);
831 #if 2 == VBI_VERSION_MINOR
832 		errno = 0;
833 #endif
834 		return t;
835 	}
836 
837 	if (unlikely (!localtime_tz (&tm, &old_tz, start, tz))) {
838 #if 2 == VBI_VERSION_MINOR
839 		errno = 0;
840 #endif
841 		return (time_t) -1;
842 	}
843 
844 	if (unlikely (!tm_mon_mday_from_pil (&tm, pil))) {
845 		saved_errno = EOVERFLOW;
846 		goto failed;
847 	}
848 
849 	if (unlikely (!tm_leap_day_check (&tm))) {
850 		saved_errno = VBI_ERR_INVALID_PIL;
851 		goto failed;
852 	}
853 
854 	tm.tm_hour = VBI_PIL_HOUR (pil);
855 	tm.tm_min = VBI_PIL_MINUTE (pil);
856 	tm.tm_sec = 0;
857 
858 	tm.tm_isdst = -1; /* unknown */
859 
860 	result = mktime (&tm);
861 	if (unlikely ((time_t) -1 == result))
862 		goto failed;
863 
864 	if (unlikely (!restore_tz (&old_tz, tz))) {
865 #if 2 == VBI_VERSION_MINOR
866 		errno = 0;
867 #endif
868 		return (time_t) -1;
869 	}
870 
871 	return result;
872 
873  failed:
874 	if (unlikely (!restore_tz (&old_tz, tz))) {
875 #if 2 == VBI_VERSION_MINOR
876 		errno = 0;
877 #endif
878 		return (time_t) -1;
879 	}
880 
881 #if 3 == VBI_VERSION_MINOR
882 	errno = saved_errno;
883 #else
884 	errno = 0;
885 #endif
886 	return (time_t) -1;
887 }
888 
889 #if 0 /* just an idea */
890 time_t
891 vbi_pil_year_to_time		(vbi_pil		pil,
892 				 int			year,
893 				 const char *		tz)
894 {
895 }
896 time_t
897 vbi_pil_year_lto_to_time	(vbi_pil		pil,
898 				 int			year,
899 				 int			seconds_east)
900 {
901 }
902 #endif
903 
904 static vbi_bool
pty_utc_validity_window(time_t * begin,time_t * end,time_t time)905 pty_utc_validity_window		(time_t *		begin,
906 				 time_t *		end,
907 				 time_t			time)
908 {
909 	struct tm tm;
910 	unsigned int seconds_since_midnight;
911 	unsigned int duration;
912 
913 	memset (&tm, 0, sizeof (tm));
914 
915 	errno = 0;
916 
917 	if (unlikely (NULL == gmtime_r (&time, &tm)))
918 		return FALSE;
919 
920 	seconds_since_midnight =
921 		+ tm.tm_hour * 3600
922 		+ tm.tm_min * 60
923 		+ tm.tm_sec;
924 
925 	/* This is safe because UTC does not observe DST and POSIX
926 	   time_t does not count leap seconds. (When a leap second is
927 	   inserted in UTC, POSIX time "freezes" for one second. When
928 	   a leap second is removed POSIX time jumps forward one
929 	   second.) The result is unambiguous because leap seconds are
930 	   inserted or removed just before midnight and we only return
931 	   full hours. */
932 	duration =
933 		+ 4 * 7 * 24 * 60 * 60
934 		+ (24 + 4) * 60 * 60
935 		- seconds_since_midnight;
936 
937 	if (unlikely (time > (time_t)(TIME_MAX - duration))) {
938 		errno = EOVERFLOW;
939 		return FALSE;
940 	}
941 
942 	*begin = time;
943 	*end = time + duration;
944 
945 	return TRUE;
946 }
947 
948 /**
949  * @param begin The begin of the validity of the PTY will be stored
950  *   here.
951  * @param end The end of the validity of the PTY will be stored here.
952  * @param last_transm The last time when a program ID with the PTY
953  *   in question was broadcast by the network.
954  * @param tz A time zone name in the same format as the TZ environment
955  *   variable. If @c NULL the current value of TZ will be used.
956  *
957  * This function calculates the validity time window of a Program Type
958  * (PTY) code according to EN 300 231. That is the time window where a
959  * network can be expected to broadcast another program with the same
960  * PTY, approximately up to four weeks after its last
961  * transmission. When the PTY is a series code (>= 0x80) and not
962  * transmitted again before @a end, the network may assign the code to
963  * another series.
964  *
965  * @a tz is the time zone of the intended audience of the program.
966  * Ideally the time zone would be specified as a geographic area like
967  * "Europe/London", such that the function can determine if
968  * daylight-saving time is in effect at @a time or at the end of the
969  * validity time window. See the documentation of the localtime()
970  * function and the TZ environment variable for details. If no time
971  * zone name is available "UTC" should be specified, the returned
972  * @a end time may be off by one hour in this case.
973  *
974  * @returns
975  * On error the function returns @c FALSE and @a *begin and @a *end
976  * remain unchanged:
977  * - @a tz is empty or contains an equal sign '='.
978  * - The @a end time cannot be represented as a time_t value
979  *   (December 2037 is closer than you think!).
980  * - Insufficient memory was available.
981  *
982  * @since 0.2.34
983  *
984  * @bug
985  * This function is not thread safe unless @a tz is @c NULL.
986  * That is a limitation of the C library which permits the conversion
987  * of a broken-down time in an arbitrary time zone only by setting
988  * the TZ environment variable. The function may also fail to restore
989  * the value of TZ if insufficient memory is available.
990  */
991 vbi_bool
vbi_pty_validity_window(time_t * begin,time_t * end,time_t last_transm,const char * tz)992 vbi_pty_validity_window		(time_t *		begin,
993 				 time_t *		end,
994 				 time_t			last_transm,
995 				 const char *		tz)
996 {
997 	char *old_tz;
998 	struct tm tm;
999 	time_t stop;
1000 	int saved_errno;
1001 
1002 	assert (NULL != begin);
1003 	assert (NULL != end);
1004 
1005 	if (NULL != tz && 0 == strcmp (tz, "UTC")) {
1006 		vbi_bool success;
1007 
1008 		success = pty_utc_validity_window (begin, end,
1009 						   last_transm);
1010 #if 2 == VBI_VERSION_MINOR
1011 		errno = 0;
1012 #endif
1013 		return success;
1014 	}
1015 
1016 	if (unlikely (!localtime_tz (&tm, &old_tz, last_transm, tz)))
1017 		goto failed;
1018 
1019 	tm.tm_mday += 4 * 7 + 1;
1020 	tm.tm_hour = 4;
1021 	tm.tm_min = 0;
1022 	tm.tm_sec = 0;
1023 	tm.tm_isdst = -1; /* unknown */
1024 
1025 	stop = mktime (&tm);
1026 	if (unlikely ((time_t) -1 == stop)) {
1027 		saved_errno = errno;
1028 
1029 		if (unlikely (!restore_tz (&old_tz, tz)))
1030 			return FALSE;
1031 
1032 #if 3 == VBI_VERSION_MINOR
1033 		errno = saved_errno;
1034 #else
1035 		errno = 0;
1036 #endif
1037 		return FALSE;
1038 	}
1039 
1040 	if (unlikely (!restore_tz (&old_tz, tz)))
1041 		goto failed;
1042 
1043 	*begin = last_transm;
1044 	*end = stop;
1045 
1046 	return TRUE;
1047 
1048  failed:
1049 #if 2 == VBI_VERSION_MINOR
1050 	errno = 0;
1051 #endif
1052 	return FALSE;
1053 }
1054 
1055 static vbi_bool
valid_pil_lto_validity_window(time_t * begin,time_t * end,vbi_pil pil,time_t start,int seconds_east)1056 valid_pil_lto_validity_window	(time_t *		begin,
1057 				 time_t *		end,
1058 				 vbi_pil		pil,
1059 				 time_t			start,
1060 				 int			seconds_east)
1061 {
1062 	time_t t;
1063 
1064 	t = valid_pil_lto_to_time (pil & VBI_PIL (/* month */ 15,
1065 						   /* day */ 31,
1066 						   /* hour */ 0,
1067 						   /* minute */ 0),
1068 				   start, seconds_east);
1069 	if (unlikely ((time_t) -1 == t)) {
1070 		if (VBI_ERR_INVALID_PIL == errno) {
1071 			/* Annex F: "Invalid days -
1072 			   indefinite time window". */
1073 			*begin = TIME_MIN;
1074 			*end = TIME_MAX;
1075 			return TRUE;
1076 		} else {
1077 			return FALSE;
1078 		}
1079 	}
1080 
1081 	/* EN 300 231 Section 9.3. */
1082 
1083 	/* Just adding a number of seconds is safe because UTC does
1084 	   not observe DST and POSIX time_t does not count leap
1085 	   seconds. The results are unambiguous because leap seconds
1086 	   are inserted or removed just before midnight and we only
1087 	   return full hours. */
1088 
1089 	if (unlikely (t > TIME_MAX - 28 * 60 * 60)) {
1090 		errno = EOVERFLOW;
1091 		return FALSE;
1092 	}
1093 
1094 	if (VBI_PIL_HOUR (pil) < 4) {
1095 		if (unlikely (t < 4 * 60 * 60)) {
1096 			errno = EOVERFLOW;
1097 			return FALSE;
1098 		}
1099 
1100 		*begin = t - 4 * 60 * 60;
1101 	} else {
1102 		*begin = t;
1103 	}
1104 
1105 	*end = t + 28 * 60 * 60;
1106 
1107 	return TRUE;
1108 }
1109 
1110 /**
1111  * @param begin The start of the validity of the PIL will be stored
1112  *   here.
1113  * @param end The end of the validity of the PIL will be stored here.
1114  * @param pil Program Identification Label (PIL).
1115  * @param start The most recently announced start time of the
1116  *   program. If zero the current system time will be used.
1117  * @param seconds_east A time zone specified as an offset in seconds
1118  *   east of UTC, for example +1 * 60 * 60 for CET. @a seconds_east
1119  *   may include a daylight-saving time (DST) offset.
1120  *
1121  * This function calculates the validity time window of a PIL
1122  * according to EN 300 231. That is the time window where a network
1123  * can be expected to broadcast this PIL, usually from 00:00 on the
1124  * same day until exclusive 04:00 on the next day.
1125  *
1126  * Since PILs do not contain a year field, the year is determined from
1127  * the @a start parameter, that is the most recently announced start
1128  * time of the program or "AT-1" in EN 300 231 parlance. If @a pil
1129  * contains a month more than five months after @a start, @a pil is
1130  * assumed to refer to an earlier date than @a start.
1131  *
1132  * @a pil is assumed to be a time in the time zone specified by @a
1133  * seconds_east. @a start will be converted to a local time in the
1134  * same time zone to determine the correct year.
1135  *
1136  * Teletext packet 8/30 format 2, VPS and DVB PDC descriptors give a
1137  * PIL relative to the time zone of the intended audience of the
1138  * program. An offset from UTC including the DST offset in effect at
1139  * the specified date may be available on Teletext program
1140  * announcement pages (see struct vbi_preselection). Another offset
1141  * from UTC including the current DST offset is available as @c
1142  * VBI_EVENT_LOCAL_TIME. But of course these offsets are insufficient
1143  * to determine if DST is in effect at any given date, so the returned
1144  * @a begin or @a end may be off by one hour if the validity window
1145  * straddles a DST discontinuity.
1146  *
1147  * If @a pil is @c VBI_PIL_NSPV this function ignores @a seconds_east
1148  * and returns the same values as vbi_pty_validity_window().
1149  *
1150  * @returns
1151  * On error the function returns @c FALSE:
1152  * - @a pil is not @c VBI_PIL_NSPV and does not contain a
1153  *   valid date or time. February 29th is a valid date only if the
1154  *   estimated year is a leap year.
1155  * - @a start is zero and the current system time could not be
1156  *   determined.
1157  * - The time specified by @a pil, @a start and @a seconds_east cannot
1158  *   be represented as a time_t value.
1159  *
1160  * @since 0.2.34
1161  *
1162  * @bug
1163  * This function is not thread safe. That is a limitation of the C
1164  * library which permits the conversion of a broken-down time in an
1165  * arbitrary time zone only by setting the TZ environment variable.
1166  * The function may also fail to restore the value of TZ if
1167  * insufficient memory is available.
1168  */
1169 vbi_bool
vbi_pil_lto_validity_window(time_t * begin,time_t * end,vbi_pil pil,time_t start,int seconds_east)1170 vbi_pil_lto_validity_window	(time_t *		begin,
1171 				 time_t *		end,
1172 				 vbi_pil		pil,
1173 				 time_t			start,
1174 				 int			seconds_east)
1175 {
1176 	unsigned int month;
1177 
1178 	assert (NULL != begin);
1179 	assert (NULL != end);
1180 
1181 	month = VBI_PIL_MONTH (pil);
1182 	if (0 == month) {
1183 		/* EN 300 231 Annex F: "Unallocated". */
1184 #if 3 == VBI_VERSION_MINOR
1185 		errno = VBI_ERR_INVALID_PIL;
1186 #else
1187 		errno = 0;
1188 #endif
1189 		return FALSE;
1190 	} else if (month <= 12) {
1191 		unsigned int day;
1192 		vbi_bool success;
1193 
1194 		day = VBI_PIL_DAY (pil);
1195 		if (day - 1 >= month_days[month - 1]) {
1196 			/* Annex F: "Invalid days
1197 			   - indefinite time window". */
1198 			*begin = TIME_MIN;
1199 			*end = TIME_MAX;
1200 			return TRUE;
1201 		}
1202 
1203 		success = valid_pil_lto_validity_window
1204 			(begin, end, pil, start, seconds_east);
1205 #if 2 == VBI_VERSION_MINOR
1206 		errno = 0;
1207 #endif
1208 		return success;
1209 	} else if (month <= 14) {
1210 		/* Annex F: "Indefinite time window". */
1211 		*begin = TIME_MIN;
1212 		*end = TIME_MAX;
1213 		return TRUE;
1214 	} else {
1215 		vbi_bool success;
1216 
1217 		/* Annex F: "Unallocated except for the following
1218 		 * service codes (for which there is no restriction to
1219 		 * the time window of validity)". */
1220 		switch (pil) {
1221 		case VBI_PIL_TIMER_CONTROL:
1222 		case VBI_PIL_INHIBIT_TERMINATE:
1223 		case VBI_PIL_INTERRUPTION:
1224 		case VBI_PIL_CONTINUE:
1225 			*begin = TIME_MIN;
1226 			*end = TIME_MAX;
1227 			return TRUE;
1228 
1229 		/* EN 300 231 Section 9.3, Annex E.3. */
1230 		case VBI_PIL_NSPV:
1231 			success = pty_utc_validity_window (begin, end,
1232 							   start);
1233 #if 2 == VBI_VERSION_MINOR
1234 			errno = 0;
1235 #endif
1236 			return success;
1237 
1238 		default:
1239 #if 3 == VBI_VERSION_MINOR
1240 			errno = VBI_ERR_INVALID_PIL;
1241 #else
1242 			errno = 0;
1243 #endif
1244 			return FALSE;
1245 		}
1246 	}
1247 }
1248 
1249 static vbi_bool
valid_pil_validity_window(time_t * begin,time_t * end,vbi_pil pil,time_t start,const char * tz)1250 valid_pil_validity_window	(time_t *		begin,
1251 				 time_t *		end,
1252 				 vbi_pil		pil,
1253 				 time_t			start,
1254 				 const char *		tz)
1255 {
1256 	char *old_tz;
1257 	struct tm tm;
1258 	struct tm tm2;
1259 	time_t stop;
1260 	int saved_errno;
1261 
1262 	/* EN 300 231 Section 9.3 and Annex F. */
1263 
1264 	old_tz = NULL;
1265 
1266 	if (NULL != tz && 0 == strcmp (tz, "UTC")) {
1267 		return valid_pil_lto_validity_window
1268 			(begin, end, pil, start, /* seconds_east */ 0);
1269 	}
1270 
1271 	if (unlikely (!localtime_tz (&tm, &old_tz, start, tz)))
1272 		return FALSE;
1273 
1274 	if (unlikely (!tm_mon_mday_from_pil (&tm, pil))) {
1275 		saved_errno = EOVERFLOW;
1276 		goto failed;
1277 	}
1278 
1279 	if (unlikely (!tm_leap_day_check (&tm))) {
1280 		/* Annex F: "Invalid days - indefinite time window". */
1281 		if (!restore_tz (&old_tz, tz))
1282 			return FALSE;
1283 		*begin = TIME_MIN;
1284 		*end = TIME_MAX;
1285 		return TRUE;
1286 	}
1287 
1288 	tm.tm_hour = 0;
1289 	tm.tm_min = 0;
1290 	tm.tm_sec = 0;
1291 	tm.tm_isdst = -1; /* unknown */
1292 
1293 	tm2 = tm;
1294 
1295 	if (VBI_PIL_HOUR (pil) < 4) {
1296 		--tm.tm_mday;
1297 		tm.tm_hour = 20;
1298 	}
1299 
1300 	start = mktime (&tm);
1301 	if (unlikely ((time_t) -1 == start))
1302 		goto failed;
1303 
1304 	tm2.tm_mday += 1;
1305 	tm2.tm_hour = 4;
1306 
1307 	stop = mktime (&tm2);
1308 	if (unlikely ((time_t) -1 == stop))
1309 		goto failed;
1310 
1311 	if (unlikely (!restore_tz (&old_tz, tz)))
1312 		return FALSE;
1313 
1314 	*begin = start;
1315 	*end = stop;
1316 
1317 	return TRUE;
1318 
1319  failed:
1320 	if (unlikely (!restore_tz (&old_tz, tz)))
1321 		return FALSE;
1322 
1323 	errno = saved_errno;
1324 	return FALSE;
1325 }
1326 
1327 /**
1328  * @param begin The start of the validity of the PIL will be stored
1329  *   here.
1330  * @param end The end of the validity of the PIL will be stored here.
1331  * @param pil Program Identification Label (PIL).
1332  * @param start The most recently announced start time of the program.
1333  *   If zero the current system time will be used.
1334  * @param tz A time zone name in the same format as the TZ environment
1335  *   variable. If @c NULL the current value of TZ will be used.
1336  *
1337  * This function calculates the validity time window of a PIL
1338  * according to EN 300 231. That is the time window where a network
1339  * can be expected to broadcast this PIL, usually from 00:00 on the
1340  * same day until 04:00 on the next day.
1341  *
1342  * Since PILs do not contain a year field, the year is determined from
1343  * the @a start parameter, that is the most recently announced start
1344  * time of the program or "AT-1" in EN 300 231 parlance. If @a pil
1345  * contains a month more than five months after @a start, @a pil is
1346  * assumed to refer to an earlier date than @a start.
1347  *
1348  * @a pil is assumed to be a time in the time zone specified by @a
1349  * seconds_east. @a start will be converted to a local time in the
1350  * same time zone to determine the correct year.
1351  *
1352  * Teletext packet 8/30 format 2, VPS and DVB PDC descriptors give a
1353  * PIL relative to the time zone of the intended audience of the
1354  * program. Ideally the time zone would be specified as a geographic
1355  * area like "Europe/London", such that the function can determine the
1356  * correct offset from UTC and if daylight-saving time is in effect at
1357  * any time within the validity window. See the documentation of the
1358  * localtime() function and the TZ environment variable for details.
1359  *
1360  * If @a pil is @c VBI_PIL_NSPV this function returns the same values
1361  * as vbi_pty_validity_window().
1362  *
1363  * @returns
1364  * On error the function returns @c FALSE:
1365  * - @a pil is not @c VBI_PIL_NSPV and does not contain a
1366  *   valid date or time. February 29th is a valid date only if the
1367  *   estimated year is a leap year.
1368  * - @a tz is empty or contains an equal sign '='.
1369  * - @a start is zero and the current system time could not be
1370  *   determined.
1371  * - The time specified by @a pil, @a start and @a tz cannot be
1372  *   represented as a time_t value.
1373  * - Insufficient memory was available.
1374  *
1375  * @since 0.2.34
1376  *
1377  * @bug
1378  * This function is not thread safe unless @a tz is @c NULL.
1379  * That is a limitation of the C library which permits the conversion
1380  * of a broken-down time in an arbitrary time zone only by setting
1381  * the TZ environment variable. The function may also fail to restore
1382  * the value of TZ if insufficient memory is available.
1383  */
1384 vbi_bool
vbi_pil_validity_window(time_t * begin,time_t * end,vbi_pil pil,time_t start,const char * tz)1385 vbi_pil_validity_window		(time_t *		begin,
1386 				 time_t *		end,
1387 				 vbi_pil		pil,
1388 				 time_t			start,
1389 				 const char *		tz)
1390 {
1391 	unsigned int month;
1392 
1393 	assert (NULL != begin);
1394 	assert (NULL != end);
1395 
1396 	month = VBI_PIL_MONTH (pil);
1397 	if (0 == month) {
1398 		/* EN 300 231 Annex F: "Unallocated". */
1399 #if 3 == VBI_VERSION_MINOR
1400 		errno = VBI_ERR_INVALID_PIL;
1401 #else
1402 		errno = 0;
1403 #endif
1404 		return FALSE;
1405 	} else if (month <= 12) {
1406 		unsigned int day;
1407 		vbi_bool success;
1408 
1409 		day = VBI_PIL_DAY (pil);
1410 		if (day - 1 >= month_days[month - 1]) {
1411 			/* Annex F: "Invalid days
1412 			   - indefinite time window". */
1413 			*begin = TIME_MIN;
1414 			*end = TIME_MAX;
1415 			return TRUE;
1416 		}
1417 
1418 		success = valid_pil_validity_window
1419 			(begin, end, pil, start, tz);
1420 #if 2 == VBI_VERSION_MINOR
1421 		errno = 0;
1422 #endif
1423 		return success;
1424 	} else if (month <= 14) {
1425 		/* Annex F: "Indefinite time window". */
1426 		*begin = TIME_MIN;
1427 		*end = TIME_MAX;
1428 		return TRUE;
1429 	} else {
1430 		vbi_bool success;
1431 
1432 		/* Annex F: "Unallocated except for the following
1433 		 * service codes (for which there is no restriction to
1434 		 * the time window of validity)". */
1435 		switch (pil) {
1436 		case VBI_PIL_TIMER_CONTROL:
1437 		case VBI_PIL_INHIBIT_TERMINATE:
1438 		case VBI_PIL_INTERRUPTION:
1439 		case VBI_PIL_CONTINUE:
1440 			*begin = TIME_MIN;
1441 			*end = TIME_MAX;
1442 			return TRUE;
1443 
1444 		/* EN 300 231 Section 9.3, Annex E.3. */
1445 		case VBI_PIL_NSPV:
1446 			success = vbi_pty_validity_window
1447 				(begin, end, start, tz);
1448 #if 2 == VBI_VERSION_MINOR
1449 			errno = 0;
1450 #endif
1451 			return success;
1452 
1453 		default:
1454 #if 3 == VBI_VERSION_MINOR
1455 			errno = VBI_ERR_INVALID_PIL;
1456 #else
1457 			errno = 0;
1458 #endif
1459 			return FALSE;
1460 		}
1461 	}
1462 }
1463 
1464 /*
1465 Local variables:
1466 c-set-style: K&R
1467 c-basic-offset: 8
1468 End:
1469 */
1470