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