]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/bsdconfig/timezone/share/zones.subr
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / usr.sbin / bsdconfig / timezone / share / zones.subr
1 if [ ! "$_TIMEZONE_ZONES_SUBR" ]; then _TIMEZONE_ZONES_SUBR=1
2 #
3 # Copyright (c) 2011-2012 Devin Teske
4 # All rights reserved.
5 #
6 # Redistribution and use in source and binary forms, with or without
7 # modification, are permitted provided that the following conditions
8 # are met:
9 # 1. Redistributions of source code must retain the above copyright
10 #    notice, this list of conditions and the following disclaimer.
11 # 2. Redistributions in binary form must reproduce the above copyright
12 #    notice, this list of conditions and the following disclaimer in the
13 #    documentation and/or other materials provided with the distribution.
14 #
15 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 # ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25 # SUCH DAMAGE.
26 #
27 # $FreeBSD$
28 #
29 ############################################################ INCLUDES
30
31 BSDCFG_SHARE="/usr/share/bsdconfig"
32 . $BSDCFG_SHARE/common.subr || exit 1
33 f_dprintf "%s: loading includes..." timezone/zones.subr
34 f_include $BSDCFG_SHARE/dialog.subr
35 f_include $BSDCFG_SHARE/timezone/continents.subr
36
37 BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone"
38 f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
39
40 ############################################################ CONFIGURATION
41
42 #
43 # Standard pathnames
44 #
45 _PATH_ZONETAB="/usr/share/zoneinfo/zone.tab"
46 _PATH_ZONEINFO="/usr/share/zoneinfo"
47 _PATH_LOCALTIME="/etc/localtime"
48 _PATH_DB="/var/db/zoneinfo"
49
50 #
51 # Export required i18n messages for awk(1) ENVIRON visibility
52 #
53 export msg_conflicting_zone_definition
54 export msg_country_code_invalid
55 export msg_country_code_unknown
56 export msg_invalid_country_code
57 export msg_invalid_format
58 export msg_invalid_region
59 export msg_invalid_zone_name
60 export msg_zone_multiply_defined
61 export msg_zone_must_have_description
62
63 ############################################################ FUNCTIONS
64
65 # f_read_zones
66 #
67 # Read the zone descriptions database in _PATH_ZONETAB:
68 #       /usr/share/zoneinfo/zone.tab on all OSes
69 #
70 # The format of this file (on all OSes) is:
71 #       code    coordinates     TZ      comments
72 #
73 # With each of the following elements (described below) being separated by a
74 # single tab character:
75 #
76 #       code
77 #               The ISO 3166 2-character country code.
78 #       coordinates
79 #               Latitude and logitude of the zone's principal location in ISO
80 #               6709 sign-degrees-minutes-seconds format, either +-DDMM+-DDDMM
81 #               or +-DDMMSS+-DDDMMSS, first latitude (+ is north), then long-
82 #               itude (+ is east).
83 #       TZ
84 #               Zone name used in value of TZ environment variable.
85 #       comments
86 #               Comments; present if and only if the country has multiple rows.
87 #
88 # Required variables [from continents.subr]:
89 #
90 #       CONTINENTS
91 #               Space-separated list of continents.
92 #       continent_*_name
93 #               Directory element in _PATH_ZONEINFO for the continent
94 #               represented by *.
95 #
96 # Required variables [created by f_read_iso3166_table from iso3166.subr]:
97 #
98 #       country_CODE_name
99 #               Country name of the country represented by CODE, the 2-
100 #               character country code.
101 #
102 # Variables created by this function:
103 #
104 #       country_CODE_nzones
105 #               Either set to `-1' to indicate that the 2-character country
106 #               code has only a single zone associated with it (and therefore
107 #               you should query the `country_CODE_*' environment variables),
108 #               or set to `0' or higher to indicate how many zones are assoc-
109 #               iated with the given country code. When multiple zones are
110 #               configured for a single code, you should instead query the
111 #               `country_CODE_*_N' environment variables (e.g., `echo
112 #               $country_AQ_descr_1' prints the description of the first
113 #               timezone in Antarctica).
114 #       country_CODE_filename
115 #               The ``filename'' portion of the TZ value that appears after the
116 #               `/' (e.g., `Hong_Kong' from `Asia/Hong_Kong' or `Isle_of_Man'
117 #               from `Europe/Isle_of_Man').
118 #       country_CODE_cont
119 #               The ``continent'' portion of the TZ value that appears before
120 #               the `/' (e.g., `Asia' from `Asia/Hong_Kong' or `Europe' from
121 #               `Europe/Isle_of_Man').
122 #       country_CODE_descr
123 #               The comments associated with the ISO 3166 code entry (if any).
124 #
125 #       NOTE: CODE is the 2-character country code.
126 #       
127 # This function is a two-parter. Below is the awk(1) portion of the function,
128 # afterward is the sh(1) function which utilizes the below awk script.
129 #
130 f_read_zones_awk='
131 # Variables that should be defined on the invocation line:
132 #       -v progname="progname"
133 #
134 BEGIN {
135         lineno = 0
136         failed = 0
137
138         #
139         # Initialize continents array/map (name => id)
140         #
141         split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/)
142         for (item in array)
143         {
144                 cont = array[item]
145                 if (!cont) continue
146                 name = ENVIRON["continent_" cont "_name"]
147                 continents[name] = cont
148         }
149 }
150 function die(fmt, argc, argv)
151 {
152         printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname
153         for (n = 1; n <= argc; n++)
154                 printf " \"%s\"", argv[n]
155         print ""
156         failed++
157         exit 1
158 }
159 function find_continent(name)
160 {
161         return continents[name]
162 }
163 function add_zone_to_country(lineno, tlc, descr, file, cont)
164 {
165         #
166         # Validate the two-character country code
167         #
168         if (!match(tlc, /^[A-Z][A-Z]$/))
169         {
170                 argv[1] = FILENAME
171                 argv[2] = lineno
172                 argv[3] = tlc
173                 die(ENVRION["msg_country_code_invalid"], 3, argv)
174         }
175         if (!ENVIRON["country_" tlc "_name"])
176         {
177                 argv[1] = FILENAME
178                 argv[2] = lineno
179                 argv[3] = tlc
180                 die(ENVIRON["msg_country_code_unknown"], 3, argv)
181         }
182
183         #
184         # Add Zone to an array that we will parse at the end
185         #
186         if (length(descr) > 0)
187         {
188                 if (country_nzones[tlc] < 0)
189                 {
190                         argv[1] = FILENAME
191                         argv[2] = lineno
192                         die(ENVIRON["msg_conflicting_zone_definition"], 2, argv)
193                 }
194
195                 n = ++country_nzones[tlc]
196                 country_cont[tlc,n] = cont
197                 country_filename[tlc,n] = file
198                 country_descr[tlc,n] = descr
199         }
200         else
201         {
202                 if (country_nzones[tlc] > 0)
203                 {
204                         argv[1] = FILENAME
205                         argv[2] = lineno
206                         die(ENVIRON["msg_zone_must_have_description"], 2, argv)
207                 }
208                 if (country_nzones[tlc] < 0)
209                 {
210                         argv[1] = FILENAME
211                         argv[2] = lineno
212                         die(ENVIRON["msg_zone_multiply_defined"], 2, argv)
213                 }
214
215                 country_nzones[tlc] = -1
216                 country_cont[tlc] = cont
217                 country_filename[tlc] = file
218         }
219 }
220 function print_country_code(tlc)
221 {
222         nz = country_nzones[tlc]
223
224         printf "country_%s_nzones=%d\n", tlc, nz
225         printf "export country_%s_nzones\n", tlc
226
227         if (nz < 0)
228         {
229                 printf "country_%s_cont=\"%s\"\n", tlc, country_cont[tlc]
230                 printf "export country_%s_cont\n", tlc
231                 printf "country_%s_filename=\"%s\"\n",
232                        tlc, country_filename[tlc]
233         }
234         else
235         {
236                 n = 0
237                 while ( ++n <= nz )
238                 {
239                         printf "country_%s_cont_%d=\"%s\"\n",
240                                tlc, n, country_cont[tlc,n]
241                         printf "export country_%s_cont_%d\n", tlc, n
242                         printf "country_%s_filename_%d=\"%s\"\n",
243                                tlc, n, country_filename[tlc,n]
244                         printf "country_%s_descr_%d=\"%s\"\n",
245                                tlc, n, country_descr[tlc,n]
246                 }
247         }
248 }
249 /^#/ {
250         lineno++
251         next
252 }
253 !/^#/ {
254         lineno++
255
256         #
257         # Split the current record (on TAB) into an array
258         #
259         if (split($0, line, /\t/) < 2)
260         {
261                 argv[1] = FILENAME
262                 argv[2] = lineno
263                 die(ENVIRON["msg_invalid_format"], 2, argv)
264         }
265
266         # Get the ISO3166-1 (Alpha 1) 2-letter country code
267         tlc = line[1]
268
269         #
270         # Validate the two-character country code
271         #
272         if (length(tlc) != 2)
273         {
274                 argv[1] = FILENAME
275                 argv[2] = lineno
276                 argv[3] = tlc
277                 die(ENVIRON["msg_invalid_country_code"], 3, argv)
278         }
279
280         # Get the TZ field
281         tz = line[3]
282
283         #
284         # Validate the TZ field
285         #
286         if (!match(tz, "/"))
287         {
288                 argv[1] = FILENAME
289                 argv[2] = lineno
290                 argv[3] = tz
291                 die(ENVIRON["msg_invalid_zone_name"], 3, argv)
292         }
293
294         #
295         # Get the continent portion of the TZ field
296         #
297         contbuf = tz
298         sub("/.*$", "", contbuf)
299
300         #
301         # Validate the continent
302         #
303         cont = find_continent(contbuf)
304         if (!cont)
305         {
306                 argv[1] = FILENAME
307                 argv[2] = lineno
308                 argv[3] = contbuf
309                 die(ENVIRON["msg_invalid_region"], 3, argv)
310         }
311
312         #
313         # Get the filename portion of the TZ field
314         #
315         filename = tz
316         sub("^[^/]*/", "", filename)
317
318         #
319         # Calculate the substr start-position of the comment
320         #
321         descr_start = 0
322         n = 4
323         while (--n)
324                 descr_start += length(line[n]) + 1
325
326         # Get the comment field
327         descr = substr($0, descr_start + 1)
328
329         add_zone_to_country(lineno, tlc, descr, filename, cont)
330 }
331 END {
332         if (failed) exit failed
333         for (tlc in country_nzones)
334                 print_country_code(tlc)
335 }
336 '
337 f_read_zones()
338 {
339         eval $( awk -v progname="$pgm"   \
340                     "$f_read_zones_awk"  \
341                     "$_PATH_ZONETAB"     )
342 }
343
344 # f_install_zoneinfo_file $filename
345 #
346 # Installs a zone file to _PATH_LOCALTIME.
347 #
348 f_install_zoneinfo_file()
349 {
350         local zoneinfo_file="$1"
351         local copymode title msg err height width
352
353         if [ -L "$_PATH_LOCALTIME" ]; then
354                 copymode=
355         elif [ ! -e "$_PATH_LOCALTIME" ]; then
356                 # Nothing there yet...
357                 copymode=1
358         else
359                 copymode=1
360         fi
361
362         if [ "$VERBOSE" ]; then
363                 if [ ! "$zoneinfo_file" ]; then
364                         msg=$( printf "$msg_removing_file" "$_PATH_LOCALTIME" )
365                 elif [ "$copymode" ]; then
366                         msg=$( printf "$msg_copying_file" \
367                                       "$zoneinfo_file" "$_PATH_LOCALTIME" )
368                 else
369                         msg=$( printf "$msg_creating_symlink" \
370                                       "$_PATH_LOCALTIME" "$zoneinfo_file" )
371                 fi
372                 if [ "$USEDIALOG" ]; then
373                         f_dialog_title "$msg_info"
374                         f_dialog_msgbox "$msg"
375                         f_dialog_title_restore
376                 else
377                         printf "%s\n" "$msg"
378                 fi
379         fi
380
381         [ "$REALLYDOIT" ] || return $SUCCESS
382
383         if [ ! "$zoneinfo_file" ]; then
384                 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 )
385                 if [ "$err" ]; then
386                         if [ "$USEDIALOG" ]; then
387                                 f_dialog_title "$msg_error"
388                                 f_dialog_msgbox "$err"
389                                 f_dialog_title_restore
390                         else
391                                 f_err "%s\n" "$err"
392                         fi
393                         return $FAILURE
394                 fi
395
396                 err=$( rm -f "$_PATH_DB" 2>&1 )
397                 if [ "$err" ]; then
398                         if [ "$USEDIALOG" ]; then
399                                 f_dialog_title "$msg_error"
400                                 f_dialog_msgbox "$err"
401                                 f_dialog_title_restore
402                         else
403                                 f_err "%s\n" "$err"
404                         fi
405                         return $FAILURE
406                 fi
407
408                 if [ "$VERBOSE" ]; then
409                         msg=$( printf "$msg_removed_file" "$_PATH_LOCALTIME" )
410                         if [ "$USEDIALOG" ]; then
411                                 f_dialog_title "$msg_done"
412                                 f_dialog_msgbox "$msg"
413                                 f_dialog_title_restore
414                         else
415                                 printf "%s\n" "$msg"
416                         fi
417                 fi
418
419                 return $SUCCESS
420
421         fi # ! zoneinfo_file
422
423         if [ "$copymode" ]; then
424
425                 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 )
426                 if [ "$err" ]; then
427                         if [ "$USEDIALOG" ]; then
428                                 f_dialog_title "$msg_error"
429                                 f_dialog_msgbox "$err"
430                                 f_dialog_title_restore
431                         else
432                                 f_err "%s\n" "$err"
433                         fi
434                         return $FAILURE
435                 fi
436
437                 err=$( umask 222 && : 2>&1 > "$_PATH_LOCALTIME" )
438                 if [ "$err" ]; then
439                         if [ "$USEDIALOG" ]; then
440                                 f_dialog_title "$msg_error"
441                                 f_dialog_msgbox "$err"
442                                 f_dialog_title_restore
443                         else
444                                 f_err "%s\n" "$err"
445                         fi
446                         return $FAILURE
447                 fi
448
449                 err=$( cat "$zoneinfo_file" 2>&1 > "$_PATH_LOCALTIME" )
450                 if [ "$err" ]; then
451                         if [ "$USEDIALOG" ]; then
452                                 f_dialog_title "$msg_error"
453                                 f_dialog_msgbox "$err"
454                                 f_dialog_title_restore
455                         else
456                                 f_err "%s\n" "$err"
457                         fi
458                         return $FAILURE
459                 fi
460
461         else # ! copymode
462
463                 err=$( ( :< "$zoneinfo_file" ) 2>&1 )
464                 if [ "$err" ]; then
465                         if [ "$USEDIALOG" ]; then
466                                 f_dialog_title "$msg_error"
467                                 f_dialog_msgbox "$err"
468                                 f_dialog_title_restore
469                         else
470                                 f_err "%s\n" "$err"
471                         fi
472                         return $FAILURE
473                 fi
474
475                 err=$( rm -f "$_PATH_LOCALTIME" 2>&1 )
476                 if [ "$err" ]; then
477                         if [ "$USEDIALOG" ]; then
478                                 f_dialog_title "$msg_error"
479                                 f_dialog_msgbox "$err"
480                                 f_dialog_title_restore
481                         else
482                                 f_err "%s\n" "$err"
483                         fi
484                         return $FAILURE
485                 fi
486
487                 err=$( ln -s "$zoneinfo_file" "$_PATH_LOCALTIME" 2>&1 )
488                 if [ "$err" ]; then
489                         if [ "$USEDIALOG" ]; then
490                                 f_dialog_title "$msg_error"
491                                 f_dialog_msgbox "$err"
492                                 f_dialog_title_restore
493                         else
494                                 f_err "%s\n" "$err"
495                         fi
496                         return $FAILURE
497                 fi
498
499         fi # copymode
500
501         if [ "$VERBOSE" ]; then
502                 if [ "$copymode" ]; then
503                         msg=$( printf "$msg_copied_timezone_file" \
504                                       "$zoneinfo_file" "$_PATH_LOCALTIME" )
505                 else
506                         msg=$( printf "$msg_created_symlink" \
507                                       "$_PATH_LOCALTIME" "$zoneinfo_file" )
508                 fi
509                 if [ "$USEDIALOG" ]; then
510                         f_dialog_title "$msg_done"
511                         f_dialog_msgbox "$msg"
512                         f_dialog_title_restore
513                 else
514                         printf "%s\n" "$msg"
515                 fi
516         fi
517
518         return $SUCCESS
519 }
520
521 # f_install_zoneinfo $zoneinfo
522 #
523 # Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo
524 # will be written to _PATH_DB (usable later with the `-r' flag).
525 #
526 f_install_zoneinfo()
527 {
528         local zoneinfo="$1"
529         local rv
530
531         f_install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo"
532         rv=$?
533
534         # Save knowledge for later
535         if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then
536                 if true 2> /dev/null > "$_PATH_DB"; then
537                         cat <<-EOF > "$_PATH_DB"
538                         $zoneinfo
539                         EOF
540                 fi
541         fi
542
543         return $rv
544 }
545
546 # f_confirm_zone $filename
547 #
548 # Prompt the user to confirm the new timezone data. The first (and only)
549 # argument should be the pathname to the zoneinfo file, either absolute or
550 # relative to `/usr/share/zoneinfo' (e.g., "America/Los_Angeles").
551 #
552 # The return status is 0 if "Yes" is chosen, 1 if "No", and 255 if Esc is
553 # pressed (see dialog(1) for additional details).
554
555 f_confirm_zone()
556 {
557         local filename="$1"
558         f_dialog_title "$msg_confirmation"
559         local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
560         f_dialog_title_restore
561         local tm_zone="$( TZ="$filename" date +%Z )"
562         local prompt="$( printf "$msg_look_reasonable" "$tm_zone" )"
563         local height=5 width=72
564
565         if [ "$USE_XDIALOG" ]; then
566                 height=$(( $height + 4 ))
567                 $DIALOG \
568                         --title "$title"       \
569                         --backtitle "$btitle"  \
570                         --ok-label "$msg_yes" \
571                         --cancel-label "$msg_no"   \
572                         --yesno "$prompt" $height $width
573         else
574                 $DIALOG \
575                         --title "$title"       \
576                         --backtitle "$btitle"  \
577                         --yes-label "$msg_yes" \
578                         --no-label "$msg_no"   \
579                         --yesno "$prompt" $height $width
580         fi
581 }
582
583 # f_set_zone_utc
584 #
585 # Resets to the UTC timezone.
586 #
587 f_set_zone_utc()
588 {
589         f_confirm_zone "" || return $FAILURE
590         f_install_zoneinfo_file ""
591 }
592
593 ############################################################ MAIN
594
595 f_dprintf "%s: Successfully loaded." timezone/zones.subr
596
597 fi # ! $_TIMEZONE_ZONES_SUBR