]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - usr.sbin/bsdconfig/timezone/share/zones.subr
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.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/strings.subr
36 f_include $BSDCFG_SHARE/timezone/continents.subr
37
38 BSDCFG_LIBE="/usr/libexec/bsdconfig" APP_DIR="090.timezone"
39 f_include_lang $BSDCFG_LIBE/$APP_DIR/include/messages.subr
40
41 ############################################################ CONFIGURATION
42
43 #
44 # Standard pathnames
45 #
46 _PATH_ZONETAB="/usr/share/zoneinfo/zone.tab"
47 _PATH_ZONEINFO="/usr/share/zoneinfo"
48 _PATH_LOCALTIME="/etc/localtime"
49 _PATH_DB="/var/db/zoneinfo"
50
51 #
52 # Export required i18n messages for awk(1) ENVIRON visibility
53 #
54 export msg_conflicting_zone_definition
55 export msg_country_code_invalid
56 export msg_country_code_unknown
57 export msg_invalid_country_code
58 export msg_invalid_format
59 export msg_invalid_region
60 export msg_invalid_zone_name
61 export msg_zone_multiply_defined
62 export msg_zone_must_have_description
63
64 ############################################################ FUNCTIONS
65
66 # f_read_zones
67 #
68 # Read the zone descriptions database in _PATH_ZONETAB:
69 #       /usr/share/zoneinfo/zone.tab on all OSes
70 #
71 # The format of this file (on all OSes) is:
72 #       code    coordinates     TZ      comments
73 #
74 # With each of the following elements (described below) being separated by a
75 # single tab character:
76 #
77 #       code
78 #               The ISO 3166 2-character country code.
79 #       coordinates
80 #               Latitude and logitude of the zone's principal location in ISO
81 #               6709 sign-degrees-minutes-seconds format, either +-DDMM+-DDDMM
82 #               or +-DDMMSS+-DDDMMSS, first latitude (+ is north), then long-
83 #               itude (+ is east).
84 #       TZ
85 #               Zone name used in value of TZ environment variable.
86 #       comments
87 #               Comments; present if and only if the country has multiple rows.
88 #
89 # Required variables [from continents.subr]:
90 #
91 #       CONTINENTS
92 #               Space-separated list of continents.
93 #       continent_*_name
94 #               Directory element in _PATH_ZONEINFO for the continent
95 #               represented by *.
96 #
97 # Required variables [created by f_read_iso3166_table from iso3166.subr]:
98 #
99 #       country_CODE_name
100 #               Country name of the country represented by CODE, the 2-
101 #               character country code.
102 #
103 # Variables created by this function:
104 #
105 #       country_CODE_nzones
106 #               Either set to `-1' to indicate that the 2-character country
107 #               code has only a single zone associated with it (and therefore
108 #               you should query the `country_CODE_*' environment variables),
109 #               or set to `0' or higher to indicate how many zones are assoc-
110 #               iated with the given country code. When multiple zones are
111 #               configured for a single code, you should instead query the
112 #               `country_CODE_*_N' environment variables (e.g., `echo
113 #               $country_AQ_descr_1' prints the description of the first
114 #               timezone in Antarctica).
115 #       country_CODE_filename
116 #               The ``filename'' portion of the TZ value that appears after the
117 #               `/' (e.g., `Hong_Kong' from `Asia/Hong_Kong' or `Isle_of_Man'
118 #               from `Europe/Isle_of_Man').
119 #       country_CODE_cont
120 #               The ``continent'' portion of the TZ value that appears before
121 #               the `/' (e.g., `Asia' from `Asia/Hong_Kong' or `Europe' from
122 #               `Europe/Isle_of_Man').
123 #       country_CODE_descr
124 #               The comments associated with the ISO 3166 code entry (if any).
125 #
126 #       NOTE: CODE is the 2-character country code.
127 #       
128 # This function is a two-parter. Below is the awk(1) portion of the function,
129 # afterward is the sh(1) function which utilizes the below awk script.
130 #
131 f_read_zones_awk='
132 # Variables that should be defined on the invocation line:
133 #       -v progname="progname"
134 #
135 BEGIN {
136         lineno = 0
137         failed = 0
138
139         #
140         # Initialize continents array/map (name => id)
141         #
142         split(ENVIRON["CONTINENTS"], array, /[[:space:]]+/)
143         for (item in array)
144         {
145                 cont = array[item]
146                 if (!cont) continue
147                 name = ENVIRON["continent_" cont "_name"]
148                 continents[name] = cont
149         }
150 }
151 function die(fmt, argc, argv)
152 {
153         printf "f_die 1 \"%%s: %s\" \"%s\"", fmt, progname
154         for (n = 1; n <= argc; n++)
155                 printf " \"%s\"", argv[n]
156         print ""
157         failed++
158         exit 1
159 }
160 function find_continent(name)
161 {
162         return continents[name]
163 }
164 function add_zone_to_country(lineno, tlc, descr, file, cont)
165 {
166         #
167         # Validate the two-character country code
168         #
169         if (!match(tlc, /^[A-Z][A-Z]$/))
170         {
171                 argv[1] = FILENAME
172                 argv[2] = lineno
173                 argv[3] = tlc
174                 die(ENVRION["msg_country_code_invalid"], 3, argv)
175         }
176         if (!ENVIRON["country_" tlc "_name"])
177         {
178                 argv[1] = FILENAME
179                 argv[2] = lineno
180                 argv[3] = tlc
181                 die(ENVIRON["msg_country_code_unknown"], 3, argv)
182         }
183
184         #
185         # Add Zone to an array that we will parse at the end
186         #
187         if (length(descr) > 0)
188         {
189                 if (country_nzones[tlc] < 0)
190                 {
191                         argv[1] = FILENAME
192                         argv[2] = lineno
193                         die(ENVIRON["msg_conflicting_zone_definition"], 2, argv)
194                 }
195
196                 n = ++country_nzones[tlc]
197                 country_cont[tlc,n] = cont
198                 country_filename[tlc,n] = file
199                 country_descr[tlc,n] = descr
200         }
201         else
202         {
203                 if (country_nzones[tlc] > 0)
204                 {
205                         argv[1] = FILENAME
206                         argv[2] = lineno
207                         die(ENVIRON["msg_zone_must_have_description"], 2, argv)
208                 }
209                 if (country_nzones[tlc] < 0)
210                 {
211                         argv[1] = FILENAME
212                         argv[2] = lineno
213                         die(ENVIRON["msg_zone_multiply_defined"], 2, argv)
214                 }
215
216                 country_nzones[tlc] = -1
217                 country_cont[tlc] = cont
218                 country_filename[tlc] = file
219         }
220 }
221 function print_country_code(tlc)
222 {
223         nz = country_nzones[tlc]
224
225         printf "country_%s_nzones=%d\n", tlc, nz
226         printf "export country_%s_nzones\n", tlc
227
228         if (nz < 0)
229         {
230                 printf "country_%s_cont=\"%s\"\n", tlc, country_cont[tlc]
231                 printf "export country_%s_cont\n", tlc
232                 printf "country_%s_filename=\"%s\"\n",
233                        tlc, country_filename[tlc]
234         }
235         else
236         {
237                 n = 0
238                 while ( ++n <= nz )
239                 {
240                         printf "country_%s_cont_%d=\"%s\"\n",
241                                tlc, n, country_cont[tlc,n]
242                         printf "export country_%s_cont_%d\n", tlc, n
243                         printf "country_%s_filename_%d=\"%s\"\n",
244                                tlc, n, country_filename[tlc,n]
245                         printf "country_%s_descr_%d=\"%s\"\n",
246                                tlc, n, country_descr[tlc,n]
247                 }
248         }
249 }
250 /^#/ {
251         lineno++
252         next
253 }
254 !/^#/ {
255         lineno++
256
257         #
258         # Split the current record (on TAB) into an array
259         #
260         if (split($0, line, /\t/) < 2)
261         {
262                 argv[1] = FILENAME
263                 argv[2] = lineno
264                 die(ENVIRON["msg_invalid_format"], 2, argv)
265         }
266
267         # Get the ISO3166-1 (Alpha 1) 2-letter country code
268         tlc = line[1]
269
270         #
271         # Validate the two-character country code
272         #
273         if (length(tlc) != 2)
274         {
275                 argv[1] = FILENAME
276                 argv[2] = lineno
277                 argv[3] = tlc
278                 die(ENVIRON["msg_invalid_country_code"], 3, argv)
279         }
280
281         # Get the TZ field
282         tz = line[3]
283
284         #
285         # Validate the TZ field
286         #
287         if (!match(tz, "/"))
288         {
289                 argv[1] = FILENAME
290                 argv[2] = lineno
291                 argv[3] = tz
292                 die(ENVIRON["msg_invalid_zone_name"], 3, argv)
293         }
294
295         #
296         # Get the continent portion of the TZ field
297         #
298         contbuf = tz
299         sub("/.*$", "", contbuf)
300
301         #
302         # Validate the continent
303         #
304         cont = find_continent(contbuf)
305         if (!cont)
306         {
307                 argv[1] = FILENAME
308                 argv[2] = lineno
309                 argv[3] = contbuf
310                 die(ENVIRON["msg_invalid_region"], 3, argv)
311         }
312
313         #
314         # Get the filename portion of the TZ field
315         #
316         filename = tz
317         sub("^[^/]*/", "", filename)
318
319         #
320         # Calculate the substr start-position of the comment
321         #
322         descr_start = 0
323         n = 4
324         while (--n)
325                 descr_start += length(line[n]) + 1
326
327         # Get the comment field
328         descr = substr($0, descr_start + 1)
329
330         add_zone_to_country(lineno, tlc, descr, filename, cont)
331 }
332 END {
333         if (failed) exit failed
334         for (tlc in country_nzones)
335                 print_country_code(tlc)
336 }
337 '
338 f_read_zones()
339 {
340         eval $( awk -v progname="$pgm"   \
341                     "$f_read_zones_awk"  \
342                     "$_PATH_ZONETAB"     )
343 }
344
345 # f_install_zoneinfo_file $filename
346 #
347 # Installs a zone file to _PATH_LOCALTIME.
348 #
349 f_install_zoneinfo_file()
350 {
351         local funcname=f_install_zoneinfo_file
352         local zoneinfo_file="$1"
353         local copymode title msg height width
354
355         if [ -L "$_PATH_LOCALTIME" ]; then
356                 copymode=
357         elif [ ! -e "$_PATH_LOCALTIME" ]; then
358                 # Nothing there yet...
359                 copymode=1
360         else
361                 copymode=1
362         fi
363
364         if [ "$VERBOSE" ]; then
365                 if [ ! "$zoneinfo_file" ]; then
366                         f_sprintf msg "$msg_removing_file" "$_PATH_LOCALTIME"
367                 elif [ "$copymode" ]; then
368                         f_sprintf msg "$msg_copying_file" \
369                                       "$zoneinfo_file" "$_PATH_LOCALTIME"
370                 else
371                         f_sprintf msg "$msg_creating_symlink" \
372                                       "$_PATH_LOCALTIME" "$zoneinfo_file"
373                 fi
374                 if [ "$USEDIALOG" ]; then
375                         f_dialog_title "$msg_info"
376                         f_dialog_msgbox "$msg"
377                         f_dialog_title_restore
378                 else
379                         printf "%s\n" "$msg"
380                 fi
381         fi
382
383         [ "$REALLYDOIT" ] || return $SUCCESS
384
385         local catch_args="-de"
386         [ "$USEDIALOG" ] && catch_args=
387
388         if [ ! "$zoneinfo_file" ]; then
389                 f_eval_catch $catch_args $funcname rm \
390                         'rm -f "%s"' "$_PATH_LOCALTIME" || return $FAILURE
391                 f_eval_catch $catch_args $funcname rm \
392                         'rm -f "%s"' "$_PATH_DB" || return $FAILURE
393
394                 if [ "$VERBOSE" ]; then
395                         f_sprintf msg "$msg_removed_file" "$_PATH_LOCALTIME"
396                         if [ "$USEDIALOG" ]; then
397                                 f_dialog_title "$msg_done"
398                                 f_dialog_msgbox "$msg"
399                                 f_dialog_title_restore
400                         else
401                                 printf "%s\n" "$msg"
402                         fi
403                 fi
404                 return $SUCCESS
405         fi # ! zoneinfo_file
406
407         if [ "$copymode" ]; then
408                 f_eval_catch $catch_args $funcname rm \
409                         'rm -f "%s"' "$_PATH_LOCALTIME" || return $FAILURE
410                 f_eval_catch $catch_args $funcname sh \
411                         'umask 222 && :> "%s"' "$_PATH_LOCALTIME" ||
412                         return $FAILURE
413                 f_eval_catch $catch_args $funcname sh \
414                         'cat "%s" > "%s"' \
415                         "$zoneinfo_file" "$_PATH_LOCALTIME" || return $FAILURE
416         else
417                 f_eval_catch $catch_args $funcname sh \
418                         '( :< "%s" )' "$zoneinfo_file" || return $FAILURE
419                 f_eval_catch $catch_args $funcname rm \
420                         'rm -f "%s"' "$_PATH_LOCALTIME" || return $FAILURE
421                 f_eval_catch $catch_args $funcname ln \
422                         'ln -s "%s" "%s"' \
423                         "$zoneinfo_file" "$_PATH_LOCALTIME" || return $FAILURE
424         fi # copymode
425
426         if [ "$VERBOSE" ]; then
427                 if [ "$copymode" ]; then
428                         f_sprintf msg "$msg_copied_timezone_file" \
429                                       "$zoneinfo_file" "$_PATH_LOCALTIME"
430                 else
431                         f_sprintf msg "$msg_created_symlink" \
432                                       "$_PATH_LOCALTIME" "$zoneinfo_file"
433                 fi
434                 if [ "$USEDIALOG" ]; then
435                         f_dialog_title "$msg_done"
436                         f_dialog_msgbox "$msg"
437                         f_dialog_title_restore
438                 else
439                         printf "%s\n" "$msg"
440                 fi
441         fi
442
443         return $SUCCESS
444 }
445
446 # f_install_zoneinfo $zoneinfo
447 #
448 # Install a zoneinfo file relative to _PATH_ZONEINFO. The given $zoneinfo
449 # will be written to _PATH_DB (usable later with the `-r' flag).
450 #
451 f_install_zoneinfo()
452 {
453         local zoneinfo="$1"
454         local rv
455
456         f_install_zoneinfo_file "$_PATH_ZONEINFO/$zoneinfo"
457         rv=$?
458
459         # Save knowledge for later
460         if [ "$REALLYDOIT" -a $rv -eq $SUCCESS ]; then
461                 if true 2> /dev/null > "$_PATH_DB"; then
462                         cat <<-EOF > "$_PATH_DB"
463                         $zoneinfo
464                         EOF
465                 fi
466         fi
467
468         return $rv
469 }
470
471 # f_confirm_zone $filename
472 #
473 # Prompt the user to confirm the new timezone data. The first (and only)
474 # argument should be the pathname to the zoneinfo file, either absolute or
475 # relative to `/usr/share/zoneinfo' (e.g., "America/Los_Angeles").
476 #
477 # The return status is 0 if "Yes" is chosen, 1 if "No", and 255 if Esc is
478 # pressed (see dialog(1) for additional details).
479
480 f_confirm_zone()
481 {
482         local filename="$1"
483         f_dialog_title "$msg_confirmation"
484         local title="$DIALOG_TITLE" btitle="$DIALOG_BACKTITLE"
485         f_dialog_title_restore
486         local tm_zone="$( TZ="$filename" date +%Z )"
487         local prompt # Calculated below
488         local height=5 width=72
489
490         f_sprintf prompt "$msg_look_reasonable" "$tm_zone"
491         if [ "$USE_XDIALOG" ]; then
492                 height=$(( $height + 4 ))
493                 $DIALOG \
494                         --title "$title"         \
495                         --backtitle "$btitle"    \
496                         --ok-label "$msg_yes"    \
497                         --cancel-label "$msg_no" \
498                         --yesno "$prompt" $height $width
499         else
500                 $DIALOG \
501                         --title "$title"       \
502                         --backtitle "$btitle"  \
503                         --yes-label "$msg_yes" \
504                         --no-label "$msg_no"   \
505                         --yesno "$prompt" $height $width
506         fi
507 }
508
509 # f_set_zone_utc
510 #
511 # Resets to the UTC timezone.
512 #
513 f_set_zone_utc()
514 {
515         f_confirm_zone "" || return $FAILURE
516         f_install_zoneinfo_file ""
517 }
518
519 ############################################################ MAIN
520
521 f_dprintf "%s: Successfully loaded." timezone/zones.subr
522
523 fi # ! $_TIMEZONE_ZONES_SUBR