tzdata2netbsd revision 1.16 1 # $NetBSD: tzdata2netbsd,v 1.16 2024/02/05 21:52:38 kre Exp $
2
3 # For use by NetBSD developers when updating to new versions of tzdata.
4 #
5 # 0. Be in an up-to-date checkout of src/external/public-domain/tz
6 # from NetBSD-current.
7 # 1. Make sure that you have Paul Eggert's 4K RSA public key in your
8 # keyring (62AA7E34, eggert (a] cs.ucla.edu) It is not required that it be trusted.
9 # 2. Run this script. You will be prompted for confirmation before
10 # anything major (such as a cvs operation). The tz versions can be
11 # specified as args (new version first, and the previous second) if
12 # needed to override the calculated values
13 # 3. If something fails, abort the script and fix it.
14 # 4. Re-run this script until you are happy. It's designed to
15 # be re-run over and over, and later runs will try not to
16 # redo non-trivial work done by earlier runs.
17 #
18
19 VERS_PATTERN='2[0-9][0-9][0-9][a-z]'
20 # This needs to be updated twice every millennium to allow for the
21 # new millenium's years.
22 # First in the late xx90's sometime, allow the new one by changing the leading
23 # digit from a specific value to the class containing the current and
24 # following values (eg: in 2098 or so, change '2' to be '[23]').
25 # Then in the following early xx00's sometime, delete the class, and
26 # leave only the current value as valid (eg: in 3001 or 3002,
27 # change '[23]' to be just '3'
28 # Doing it this way helps guard against invalid specifications.
29 # We could automate this, but it is (IMO) not worth the cost, to avoid a
30 # twice a millenium edit requirement.
31 # A more significant (and harder) change will be needed in the late 9990's
32 # If this script lasts until then, send me a postcard, I'll be waiting for it!
33 # Things get easier again after that until the late 99990's (etc.)
34
35 # Note the pattern is used on both the old and new version specifiers,
36 # so it must be able to cope with the shift from one form (eg 2999g)
37 # to the new one (eg: 3000a) without failing (or the code that uses it
38 # below needs to be updated).
39
40 # Also note that the option of having a second alpha (1997aa or something)
41 # to handle years with much activity is handled below, the pattern does not
42 # need to match those.
43 # If that convention changes (as of date of writing, it has never been
44 # exercised) then code changes below will be required.
45 # Note it doesn't matter (here) if nnnnz is followed by nnnnaa or nnnnza
46
47 DIST_HOST=ftp.iana.org
48 DIST_PATH=tz
49 DIST_FILES=releases
50
51 GTZURL=https://github.com/JodaOrg/global-tz/releases/download
52
53 EDITOR=${EDITOR:-vi}
54
55 TZBASE=$(pwd) || fail 'Cannot find myself ($PWD)'
56 cd -P "$TZBASE" || fail "Cannot return home: ${TZBASE}"
57
58 WORK_PFX=${TZBASE}/update-work
59 UPDATE_FROM=${WORK_PFX}/updating.from.version
60
61 usage()
62 {
63 printf >&2 '%s\n' \
64 "Usage: $0 [new-version-id [old-version-id]]" \
65 " where a version-id is of the form YYYYx (eg: 2018c)" \
66 " or '' for new-version-id (to specify only the old)" \
67 " and where new-version-id can have =fetch-version-id" \
68 " appended to specify fetching that version instead" \
69 " where the 'fetch-version-id' can be omitted if it" \
70 " is \${new-version-id}gtz - and simply using '=' means" \
71 " to work out the new-version-id but then use the gtz fork"
72
73 exit 2
74 }
75
76 fail()
77 {
78 local IFS=' '
79
80 printf >&2 '%s\n' "Error detected:" " $*" "Aborting."
81 exit 1
82 }
83
84 valid_vers()
85 {
86 case "$2" in
87 # The IANA (Eggert) standard version names
88 ${VERS_PATTERN} | ${VERS_PATTERN}[a-z] )
89 ;;
90 # The alternate (more rational) fork "global timezone" version
91 ${VERS_PATTERN}gtz | ${VERS_PATTERN}[a-z]gtz )
92 ;;
93 *) printf >&2 '%s: %s\n' \
94 "Bad form for $1 version specifier '$2'" \
95 "should (usually) be 'YYYYx'"
96 return 1
97 ;;
98 esac
99 return 0
100 }
101
102 get_curvers()
103 {
104 local LF=''
105 local LIST=iana-listing
106 local SED_SCRIPT='
107 /tzdata-latest.*-> /{
108 s/^.*-> //
109 s/\..*$//
110 s;^releases/tzdata;;p
111 q
112 }
113 d'
114
115 test -d "${WORK_PFX}" &&
116 test -s "${WORK_PFX}/${LIST}" &&
117 test "${WORK_PFX}/${LIST}" -nt dist/CVS &&
118 LF=$(find "${WORK_PFX}" -name "${LIST}" -mtime -1 -print) &&
119 test -n "${LF}" &&
120 NEWVER=$(sed -n < "${LF}" "${SED_SCRIPT}") &&
121 valid_vers new "${NEWVER}" ||
122
123 ftp >/dev/null 2>&1 -ia "${DIST_HOST}" <<- EOF &&
124 dir ${DIST_PATH} ${WORK_PFX}/${LIST}
125 quit
126 EOF
127 test -s "${WORK_PFX}/${LIST}" &&
128 NEWVER=$(sed -n < "${WORK_PFX}/${LIST}" "${SED_SCRIPT}") &&
129 valid_vers new "${NEWVER}" ||
130
131 {
132 rm -f "${WORK_PFX}/${LIST}"
133 fail "Cannot fetch current tzdata version from ${DIST_HOST}"
134 }
135
136 printf '%s\n' "Updating from ${OLDVER} to ${NEWVER}"
137 }
138
139 argparse()
140 {
141 local OVF OV NV OVy OVs NVy NVs
142
143 if OVF=$(find "${WORK_PFX}" -name "${UPDATE_FROM##*/}" -mtime +2 -print)
144 then
145 # delete anything old
146 test -n "${OVF}" && rm -f "${OVF}"
147 fi
148
149 case "$#" in
150 0|1)
151 # once we have obtained OLDVER once, never guess it again.
152 if [ -f "${UPDATE_FROM}" ]
153 then
154 OLDVER=$(cat "${UPDATE_FROM}")
155 elif [ -f dist/TZDATA_VERSION ]
156 then
157 OLDVER=$(cat dist/TZDATA_VERSION)
158 elif [ -f dist/version ]
159 then
160 OLDVER=$(cat dist/version)
161 fi
162 OLDVER=${OLDVER#tzdata} # TZDATA_VERS is tzdata-nnnnX
163 OLDVER=${OLDVER#-} # but the '-' is optional
164 OLDVERGTZ=${OLDVER} # This would have been the cvs tag
165 OLDVER=${OLDVER%gtz} # want the base version elsewhere
166
167 if [ -z "${OLDVER}" ]
168 then
169 printf >&2 '%s\n' \
170 'Cannot determine current installed version' \
171 'Specify it on the command line.' \
172 ''
173 usage
174 fi
175
176 valid_vers old "${OLDVER}" ||
177 fail "Calculated bad OLDVER, give as 2nd arg"
178 ;;
179
180 2) valid_vers old "$2" && OLDVER="$2" || usage
181 ;;
182
183 *) usage
184 ;;
185 esac
186
187 GLOBAL=false
188 case "$#:$1" in
189 0: | 1: | 2: )
190 ;;
191 1:=|2:=)
192 GLOBAL=true;;
193 1:=?*|2:=?*)
194 valid_vers fetch "${1#=}" && FETCHVER="${1#=}" || usage
195 ;;
196 1:*=?*|2:*=?*)
197 set -- "{$1%=*}" "${1#*=}"
198 valid_vers fetch "$2" && FETCHVER="$2" || usage
199 valid_vers new "$1" && NEWVER="$1" || usage
200 ;;
201 1:?*|2:?*)
202 valid_vers new "$1" && NEWVER="$1" || usage
203 ;;
204 *) usage
205 ;;
206 esac
207
208 test -z "${NEWVER}" && get_curvers
209
210 if [ -z "${FETCHVER}" ]
211 then
212 if $GLOBAL
213 then
214 FETCHVER=${NEWVER}gtz
215 else
216 FETCHVER=${NEWVER}
217 fi
218 fi
219
220 case "${FETCHVER}" in
221 *gtz) GLOBAL=true;;
222 *) GLOBAL=false;;
223 esac
224
225 if [ "${NEWVER}" = "${OLDVER}" ]
226 then
227 printf 'New and old versions both %s; nothing to do\n' \
228 "${NEWVER}"
229 exit 0
230 fi
231
232 printf '%s\n' "${OLDVER}" > "${UPDATE_FROM}" ||
233 fail "Unable to preserve old version ${OLDVER} in ${UPDATE_FROM}"
234
235 # Do version upgrade test using base version names, ignoring
236 # the "gtz" in the "global timezone" versions, so we can
237 # switch back and forth between use of those as circumstances change
238 OV=${OLDVER%gtz}
239 NV=${NEWVER%gtz}
240
241 OVy=${OV%%[!0-9]*}
242 OVs=${OV#${OVy}}
243 NVy=${NV%%[!0-9]*}
244 NVs=${NV#${NVy}}
245
246 # To get all the permutations correct, we need to separate
247 # the year and suffix parts of the version IDs (done just above)
248 # and then compare them separately. The suffix is only relevant
249 # to the result when the years are the same.
250
251 # We compare the length of the suffix separately to the suffix
252 # itself, a multi-char suffix has never happened (and is never
253 # likely to) - but in the event that prediction is wrong, we don't
254 # know (yet) what is to come after 'z' - it might be 'za' 'zb'
255 # ... to 'zz" then 'zza' ... or it might be 'aa' 'ab' ... 'az' 'ba'...
256 # we need to handle both possibilities. Two things stand out from
257 # those: 1. a longer suffix is always going to be for a newer version
258 # than a shorter one; 2. equal length suffixes can be compared as
259 # strings
260
261 if [ "${OVy}" -gt "${NVy}" ] || {
262 [ "${OVy}" -eq "${NVy}" ] && {
263 [ "${#OVs}" -gt "${#NVs}" ] ||
264 LC_COLLATE=C [ "${OVs}" '>' "${NVs}" ]
265 }
266 } then
267 local reply
268
269 printf '%s\n' "Update would revert ${OLDVER} to ${NEWVER}"
270 read -p "Is reversion intended? " reply
271 case "${reply}" in
272 [Yy]*) ;;
273 *) printf '%s\n' OK. Aborted.
274 rm -f "${UPDATE_FROM}"
275 exit 1
276 ;;
277 esac
278 fi
279
280 return 0
281 }
282
283 setup_versions()
284 {
285 # Uppercase variants of OLDVER and NEWVER
286 OLDVER_UC="$( echo "${OLDVERGTZ}" | tr '[a-z]' '[A-Z]' )"
287 NEWVER_UC="$( echo "${NEWVER}" | tr '[a-z]' '[A-Z]' )"
288
289 # Tags for use with version control systems
290 CVSOLDTAG="TZDATA${OLDVER_UC}"
291 CVSNEWTAG="TZDATA${NEWVER_UC}"
292 CVSBRANCHTAG="TZDATA"
293 GITHUBTAG="${NEWVER}"
294
295 if $GLOBAL && [ "${CVSNEWTAG%GTZ}" = "${CVSNEWTAG}" ]
296 then
297 CVSNEWTAG=${CVSNEWTAG}GTZ
298 fi
299
300 # URLs for fetching distribution files, etc.
301 if $GLOBAL
302 then
303 DISTURL=${GTZURL}/${FETCHVER}/tzdata${FETCHVER}.tar.gz
304 unset SIGURL
305 else
306 DISTURL="ftp://${DIST_HOST}/${DIST_PATH}/${DIST_FILES}"
307 DISTURL="${DISTURL}/tzdata${NEWVER}.tar.gz"
308 SIGURL="${DISTURL}.asc"
309 fi
310 NEWSURL="https://github.com/eggert/tz/raw/${GITHUBTAG}/NEWS"
311
312 # Directories
313 REPODIR="src/external/public-domain/tz/dist"
314 # relative to the NetBSD CVS repo
315 TZDISTDIR="$(pwd)/dist" # should be .../external/public-domain/tz/dist
316 WORKDIR="${WORK_PFX}/${NEWVER}"
317 EXTRACTDIR="${WORKDIR}/extract"
318
319 # Files in the work directory
320 DISTFILE="${WORKDIR}/${DISTURL##*/}"
321 SIGFILE="${DISTFILE}.asc"
322 PGPVERIFYLOG="${WORKDIR}/pgpverify.log"
323 NEWSFILE="${WORKDIR}/NEWS"
324 NEWSTRIMFILE="${WORKDIR}/NEWS.trimmed"
325 IMPORTMSGFILE="${WORKDIR}/import.msg"
326 IMPORTDONEFILE="${WORKDIR}/import.done"
327 MERGSMSGFILE="${WORKDIR}/merge.msg"
328 MERGEDONEFILE="${WORKDIR}/merge.done"
329 COMMITMERGEDONEFILE="${WORKDIR}/commitmerge.done"
330
331 printf '%s\n' "${CVSOLDTAG}" > "${WORK_PFX}/updating_from"
332 }
333
334 DOIT()
335 {
336 local really_do_it=false
337 local reply
338
339 echo "In directory $(pwd)"
340 echo "ABOUT TO DO:" "$(shell_quote "$@")"
341 read -p "Really do it? [yes/no/quit] " reply
342 case "${reply}" in
343 [yY]*) really_do_it=true ;;
344 [nN]*) really_do_it=false ;;
345 [qQ]*)
346 echo "Aborting"
347 return 1
348 ;;
349 *) echo "Huh?"
350 return 1
351 ;;
352 esac
353 if $really_do_it; then
354 echo "REALLY DOING IT NOW..."
355 "$@"
356 else
357 echo "NOT REALLY DOING THE ABOVE COMMAND"
358 fi
359 }
360
361 # Quote args to make them safe in the shell.
362 # Usage: quotedlist="$(shell_quote args...)"
363 #
364 # After building up a quoted list, use it by evaling it inside
365 # double quotes, like this:
366 # eval "set -- $quotedlist"
367 # or like this:
368 # eval "\$command $quotedlist \$filename"
369 #
370 shell_quote()
371 (
372 local result=''
373 local arg qarg
374 LC_COLLATE=C ; export LC_COLLATE # so [a-zA-Z0-9] works in ASCII
375 for arg in "$@" ; do
376 case "${arg}" in
377 '')
378 qarg="''"
379 ;;
380 *[!-./a-zA-Z0-9]*)
381 # Convert each embedded ' to '\'',
382 # then insert ' at the beginning of the first line,
383 # and append ' at the end of the last line.
384 # Finally, elide unnecessary '' pairs at the
385 # beginning and end of the result and as part of
386 # '\'''\'' sequences that result from multiple
387 # adjacent quotes in the input.
388 qarg="$(printf '%s\n' "$arg" | \
389 ${SED:-sed} -e "s/'/'\\\\''/g" \
390 -e "1s/^/'/" -e "\$s/\$/'/" \
391 -e "1s/^''//" -e "\$s/''\$//" \
392 -e "s/'''/'/g"
393 )"
394 ;;
395 *)
396 # Arg is not the empty string, and does not contain
397 # any unsafe characters. Leave it unchanged for
398 # readability.
399 qarg="${arg}"
400 ;;
401 esac
402 result="${result}${result:+ }${qarg}"
403 done
404 printf '%s\n' "$result"
405 )
406
407 validate_pwd()
408 {
409 local P="$(pwd)" || return 1
410
411 test -d "${P}" &&
412 test -d "${P}/CVS" &&
413 test -d "${P}/dist" &&
414 test -f "${P}/dist/zone.tab" &&
415 test -f "${P}/tzdata2netbsd" || {
416 printf >&2 '%s\n' "Please change to the correct directory"
417 return 1
418 }
419
420 test -f "${P}/CVS/Tag" && {
421
422 # Here (for local use only) if needed for private branch work
423 # insert tests for the value of $(cat "${P}/CVS/Tag") and
424 # allow your private branch tag to pass. Eg:
425
426 # case "$(cat "${P}/CVS/Tag")" in
427 # my-branch-name) return 0;;
428 # esac
429
430 # Do not commit a version of this script modified that way,
431 # (not even on the private branch) - keep it as a local
432 # modified file. (This script will not commit it.)
433
434 printf >&2 '%s\n' \
435 "This script should be run in a checkout of HEAD only"
436 return 1
437 }
438
439 return 0
440 }
441
442 findcvsroot()
443 {
444 [ -n "${CVSROOT}" ] && return 0
445 CVSROOT="$( cat ./CVS/Root )"
446 [ -n "${CVSROOT}" ] && return 0
447 echo >&2 "Failed to set CVSROOT value"
448 return 1
449 }
450
451 mkworkpfx()
452 {
453 mkdir -p "${WORK_PFX}" || fail "Unable to make missing ${WORK_PFX}"
454 }
455 mkworkdir()
456 {
457 mkdir -p "${WORKDIR}" || fail "Unable to make missing ${WORKDIR}"
458 }
459
460 cvsupdate()
461 (
462 # Make sure our working directory is up to date (and HEAD)
463 cd "${TZBASE}/dist" || exit 1
464 cvs -d "${CVSROOT}" -q update -AdP || exit 2
465 )
466
467 fetch()
468 {
469 [ -f "${DISTFILE}" ] || ftp -o "${DISTFILE}" "${DISTURL}" ||
470 fail "fetch of ${DISTFILE} failed"
471
472 if [ -n "${SIGURL}" ]
473 then
474 [ -f "${SIGFILE}" ] || ftp -o "${SIGFILE}" "${SIGURL}" ||
475 fail "fetch of ${SIGFILE} failed"
476 fi
477
478 [ -f "${NEWSFILE}" ] || ftp -o "${NEWSFILE}" "${NEWSURL}" ||
479 fail "fetch of ${NEWSFILE} failed"
480 }
481
482 checksig()
483 {
484 { gpg --verify "${SIGFILE}" "${DISTFILE}"
485 echo gpg exit status $?
486 } 2>&1 | tee "${PGPVERIFYLOG}"
487
488 # The output should contain lines that match all the following regexps
489 #
490 while read line; do
491 if ! grep -E -q -e "^${line}\$" "${PGPVERIFYLOG}"; then
492 echo >&2 "Failed to verify signature: ${line}"
493 return 1
494 fi
495 done <<'EOF'
496 gpg: Signature made .* using RSA key ID (62AA7E34|44AD418C)
497 gpg: Good signature from "Paul Eggert <eggert (a] cs.ucla.edu>"
498 gpg exit status 0
499 EOF
500 }
501
502 extract()
503 {
504 [ -f "${EXTRACTDIR}/zone.tab" ] && return
505 mkdir -p "${EXTRACTDIR}"
506 tar -z -xf "${DISTFILE}" -C "${EXTRACTDIR}"
507 }
508
509 rflist()
510 (
511 test "${1}" && cd "${1}" && find * -print | sort
512 )
513
514 zonelists()
515 {
516 [ -f "${WORKDIR}"/addedzones ] && return
517
518 rm -fr "${WORK_PFX}"/oldzones "${WORK_PFX}"/newzones
519
520 (
521 cd "${TZBASE}/share/zoneinfo" || exit 1
522
523 make TOOL_ZIC=/usr/sbin/zic \
524 DESTDIR= \
525 TZBUILDDIR="${WORK_PFX}"/oldzones \
526 TZDIR="${WORK_PFX}"/oldzones \
527 TZDISTDIR="${TZBASE}"/dist \
528 posix_only >/dev/null 2>&1
529
530 ) || fail 'Unable to compile old zone data files'
531
532 (
533 cd "${TZBASE}/share/zoneinfo" || exit 1
534
535 make TOOL_ZIC=/usr/sbin/zic \
536 DESTDIR= \
537 TZBUILDDIR="${WORK_PFX}"/newzones \
538 TZDIR="${WORK_PFX}"/newzones \
539 TZDISTDIR="${EXTRACTDIR}" \
540 posix_only >/dev/null 2>&1
541
542 ) || fail 'Unable to compile new zone data files'
543
544 rflist "${WORK_PFX}"/oldzones > "${WORKDIR}"/oldzones
545 rflist "${WORK_PFX}"/newzones > "${WORKDIR}"/newzones
546
547 if cmp -s "${WORKDIR}"/oldzones "${WORKDIR}"/newzones >/dev/null
548 then
549 printf "No zones added or deleted by this update"
550 > "${WORKDIR}"/removedzones
551 > "${WORKDIR}"/addedzones
552 return 0
553 fi
554
555 comm -23 "${WORKDIR}"/oldzones "${WORKDIR}"/newzones \
556 > "${WORKDIR}"/removedzones
557
558 test "${REMOVEOK:-no}" != yes && test -s "${WORKDIR}"/removedzones && {
559 printf '%s\n' 'This update wants to remove these zone files:' ''
560 sed 's/^/ /' < "${WORKDIR}"/removedzones
561 printf '%s\n' '' 'It probably should not' ''
562
563 printf 'If this is OK, rerun this script with REMOVEOK=yes\n'
564 printf 'Otherwise, fix the problem, and then rerun the script\n'
565 exit 1
566 }
567
568 comm -13 "${WORKDIR}"/oldzones "${WORKDIR}"/newzones \
569 > "${WORKDIR}"/addedzones
570
571 test -s "${WORKDIR}"/addedzones && {
572 printf '%s\n' '' '********************************* NOTE:' \
573 '********************************* New Zones Created' \
574 ''
575 sed 's/^/ /' < "${WORKDIR}"/addedzones
576 printf '%s\n' '' '*********************************' ''
577 }
578
579 return 0
580 }
581
582 updatedzones()
583 {
584 [ -f "${WORKDIR}"/.zonesOK ] && return
585
586 rm -fr "${WORK_PFX}"/updzones
587
588 (
589 cd "${TZBASE}/share/zoneinfo" || exit 1
590
591 make TOOL_ZIC=/usr/sbin/zic \
592 DESTDIR= \
593 TZBUILDDIR="${WORK_PFX}"/updzones \
594 TZDIR="${WORK_PFX}"/updzones \
595 TZDISTDIR="${TZBASE}"/dist \
596 posix_only >/dev/null 2>&1
597
598 ) || fail 'Unable to compile updated zone data. HELP'
599
600 rflist "${WORK_PFX}"/updzones > "${WORKDIR}"/updzones
601
602 cmp -s "${WORKDIR}"/newzones "${WORKDIR}"/updzones || {
603
604 printf '%s\n' '' '*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*#*' \
605 'After cvs work, zones created are not as intended' \
606 '-------------------------------------------------' \
607 'Zones not created but should have been:'
608 comm -23 "${WORKDIR}"/newzones "${WORKDIR}"/updzones |
609 sed 's/^/ /'
610 printf '%s\n' \
611 '-------------------------------------------------' \
612 'Zones created that should not have been:'
613 comm -13 "${WORKDIR}"/newzones "${WORKDIR}"/updzones |
614 sed 's/^/ /'
615 prinrf '%s\n' \
616 '-------------------------------------------------'
617
618 fail 'cvs import/merge/update/commit botch'
619 }
620
621 > "${WORKDIR}"/.zonesOK
622 }
623
624 addnews()
625 {
626 [ -f "${EXTRACTDIR}/NEWS" ] && return
627 cp -p "${NEWSFILE}" "${EXTRACTDIR}"/NEWS
628 }
629
630 # Find the relevant part of the NEWS file for all releases between
631 # OLDVER and NEWVER, and save them to NEWSTRIMFILE.
632 #
633 trimnews()
634 {
635 [ -s "${NEWSTRIMFILE}" ] && return
636 awk -v oldver="${OLDVER}" -v newver="${NEWVER}" \
637 '
638 BEGIN {inrange = 0}
639 /^Release [0-9]+[a-z]+ - .*/ {
640 # "Release <version> - <date>"
641 # Note: must handle transition from 2018z to 2018aa
642 # Assumptions: OLDVER and NEWVER have been sanitized,
643 # and format of NEWS file does not alter (and
644 # contains valid data)
645 inrange = ((length($2) > length(oldver) || \
646 $2 > oldver) && \
647 (length($2) < newver || $2 <= newver))
648 }
649 // { if (inrange) print; }
650 ' \
651 <"${NEWSFILE}" >"${NEWSTRIMFILE}"
652
653 if $GLOBAL
654 then
655 printf '%s\n' "tzdata-${NEWVER}gtz"
656 else
657 printf '%s\n' "tzdata-${NEWVER}"
658 fi > ${TZDISTDIR}/TZDATA_VERSION
659 }
660
661 # Create IMPORTMSGFILE from NEWSTRIMFILE, by ignoring some sections,
662 # keeping only the first sentence from paragraphs in other sections,
663 # and changing the format.
664 #
665 # The result should be edited by hand before performing a cvs commit.
666 # A message to that effect is inserted at the beginning of the file.
667 #
668 mkimportmsg()
669 {
670 [ -s "${IMPORTMSGFILE}" ] && return
671 { cat <<EOF
672 EDIT ME: Edit this file and then delete the lines marked "EDIT ME".
673 EDIT ME: This file will be used as a log message for the "cvs commit" that
674 EDIT ME: imports tzdata${NEWVER}. The initial contents of this file were
675 EDIT ME: generated from ${NEWSFILE}.
676 EDIT ME:
677 EOF
678 awk -v oldver="${OLDVER}" -v newver="${NEWVER}" \
679 -v disturl="${DISTURL}" -v newsurl="${NEWSURL}" \
680 '
681 BEGIN {
682 bullet = " * ";
683 indent = " ";
684 blankline = 0;
685 goodsection = 0;
686 havesentence = 0;
687 print "Import tzdata"newver" from "disturl;
688 #print "and NEWS file from "newsurl;
689 }
690 /^Release/ {
691 # "Release <version> - <date>"
692 ver = $2;
693 date = gensub(".* - ", "", 1, $0);
694 print "";
695 print "Summary of changes in tzdata"ver \
696 " ("date"):";
697 }
698 /^$/ { blankline = 1; havesentence = 0; }
699 /^ Changes / { goodsection = 0; }
700 /^ Changes to future timestamps/ { goodsection = 1; }
701 /^ Changes to past timestamps/ { goodsection = 1; }
702 /^ Changes to documentation/ || \
703 /^ Changes to commentary/ {
704 t = gensub("^ *", "", 1, $0);
705 t = gensub("\\.*$", ".", 1, t);
706 print bullet t;
707 goodsection = 0;
708 }
709 /^ .*/ && goodsection {
710 # In a paragraph in a "good" section.
711 # Ignore leading spaces, and ignore anything
712 # after the first sentence.
713 # First line of paragraph gets a bullet.
714 t = gensub("^ *", "", 1, $0);
715 t = gensub("\\. .*", ".", 1, t);
716 if (blankline) print bullet t;
717 else if (! havesentence) print indent t;
718 havesentence = (havesentence || (t ~ "\\.$"));
719 }
720 /./ { blankline = 0; }
721 ' \
722 <"${NEWSTRIMFILE}"
723 } >"${IMPORTMSGFILE}"
724
725 if [ -s "${WORKDIR}"/addedzones ]
726 then
727 printf '%s\n' '' 'Zones added by this update:'
728 sed 's/^/ /' < "${WORKDIR}"/addedzones
729 fi >> "${IMPORTMSGFILE}"
730
731 if [ -s "${WORKDIR}"/removedzones ]
732 then
733 printf '%s\n' '' 'Zones removed by this update:'
734 sed 's/^/ /' < "${WORKDIR}"/removedzones
735 fi >> "${IMPORTMSGFILE}"
736
737 }
738
739 editimportmsg()
740 {
741 if [ -s "${IMPORTMSGFILE}" ] && ! grep -q '^EDIT' "${IMPORTMSGFILE}"
742 then
743 return 0 # file has already been edited
744 fi
745 # Pass both IMPORTMSGFILE and NEWSFILE to the editor, so that the
746 # user can easily consult NEWSFILE while editing IMPORTMSGFILE.
747 ${EDITOR} "${IMPORTMSGFILE}" "${NEWSFILE}"
748 }
749
750 cvsimport()
751 {
752 if [ -e "${IMPORTDONEFILE}" ]; then
753 cat >&2 <<EOF
754 The CVS import has already been performed.
755 EOF
756 return 0
757 fi
758 if ! [ -s "${IMPORTMSGFILE}" ] || grep -q '^EDIT' "${IMPORTMSGFILE}"
759 then
760 cat >&2 <<EOF
761 The message file ${IMPORTMSGFILE}
762 has not been properly edited.
763 Not performing cvs import.
764 EOF
765 return 1
766 fi
767 ( cd "${EXTRACTDIR}" &&
768 DOIT cvs -d "${CVSROOT}" import -I ! -m "$(cat "${IMPORTMSGFILE}")" \
769 "${REPODIR}" "${CVSBRANCHTAG}" "${CVSNEWTAG}"
770 ) && touch "${IMPORTDONEFILE}"
771 }
772
773 cvsmerge()
774 {
775
776 cd "${TZDISTDIR}" || exit 1
777 if [ -e "${MERGEDONEFILE}" ]
778 then
779 cat >&2 <<EOF
780 The CVS merge has already been performed.
781 EOF
782 return 0
783 fi
784 DOIT cvs -d "${CVSROOT}" update -j"${CVSOLDTAG}" -j"${CVSNEWTAG}" &&
785 touch "${MERGEDONEFILE}"
786 printf '%s\n' ================================== \
787 'The following differences exist between the merge results' \
788 'and the imported files:' '================================='
789 diff -ur "${EXTRACTDIR}" .
790 printf '%s\n' ==================================
791 }
792
793 resolveconflicts()
794 {
795 cd "${TZDISTDIR}" || exit 1
796 if grep -l '^[<=>][<=>][<=>]' *
797 then
798 cat <<EOF
799 There appear to be conflicts in the files listed above.
800 Resolve conflicts, then re-run this script.
801 EOF
802 return 1
803 fi
804 }
805
806 cvscommitmerge()
807 {
808 cd "${TZDISTDIR}" || exit 1
809 if grep -l '^[<=>][<=>][<=>]' *
810 then
811 cat >&2 <<EOF
812 There still appear to be conflicts in the files listed above.
813 Not performing cvs commit.
814 EOF
815 return 1
816 fi
817 if [ -e "${COMMITMERGEDONEFILE}" ]; then
818 cat >&2 <<EOF
819 The CVS commmit (of the merge result) has already been performed.
820 EOF
821 return 0
822 fi
823 DOIT cvs -d "${CVSROOT}" commit -m "Merge tzdata${NEWVER}" &&
824 touch "${COMMITMERGEDONEFILE}"
825 }
826
827 setlistupdate()
828 {
829 if [ -s "${WORKDIR}"/addedzones ] ||
830 [ -s "${WORKDIR}"/removedzones ]
831 then (
832 # Do all the preparatory work first, so
833 # when we get to manipulating the sets list file
834 # it all happens quickly...
835
836 while read file
837 do
838 printf '\\!zoneinfo/%s!{ %s ; %s ; }\n' \
839 "${file}" \
840 's/sys-share /obsolete /' \
841 's/ share$/ obsolete/'
842 done < "${WORKDIR}"/removedzones > "${WORKDIR}/sedcmds"
843
844 while read file
845 do
846 P=./usr/share/zoneinfo/"${file}"
847 T2=' '
848 case "$(( 48 - ${#P} ))" in
849 -*|0) T2=' ' T=' ' ;;
850 [12345678]) T=' ' ;;
851 9|1[0123456]) T=' ';;
852 1[789]|2[01234]) T=' ';;
853 2[5-9]|3[012])
854 T=' ';;
855 # the following cases can't happen,
856 # but for completeness...
857 3[3-9])
858 T=' ';;
859 *) T=' '
860 T="${T} " ;;
861 esac
862
863 if [ -d "${WORKDIR}/newzones/${file}" ]
864 then
865 printf '%s%sbase-sys-share\n' \
866 "${P}" "${T}"
867 continue
868 fi
869
870 printf '%s%sbase-sys-share%sshare\n' \
871 "${P}" "${T}" "${T2}"
872
873 # Deal with possibility that a new file
874 # might have previously existed, and then
875 # been deleted - marked obsolete
876 printf '\\!^%s .*obsolete!d\n' "${P}" \
877 >> "${WORKDIR}/sedcmds"
878
879 done < "${WORKDIR}"/addedzones > "${WORKDIR}/setadded"
880
881 printf '$r %s\n' "${WORKDIR}/setadded" \
882 >> "${WORKDIR}/sedcmds"
883
884 if ! [ -s "${WORKDIR}/sedcmds" ] # impossible?
885 then
886 exit 0
887 fi
888
889 MSG=$(
890 printf 'tzdata update to %s\n' "$NEWVER"
891 if [ -s "${WORKDIR}"/addedzones ]
892 then
893 printf 'Added zoneinfo files:\n'
894 sed 's/^/ /' \
895 < "${WORKDIR}"/addedzones
896 fi
897 if [ -s "${WORKDIR}"/removedzones ]
898 then
899 printf 'Removed zoneinfo files:\n'
900 sed 's/^/ /' \
901 < "${WORKDIR}"/removedzones
902 fi
903 printf '\nX'
904 )
905 MSG=${MSG%X}
906
907 # Now is where the changes start happening...
908
909 cd ${TZBASE}/../../../distrib/sets || exit 1
910 cd lists/base || exit 2
911 DOIT cvs -d "${CVSROOT}" -q update mi || exit 3
912 cp mi mi.unsorted || exit 4
913 sh ../../sort-list mi || exit 5
914 cmp -s mi mi.unsorted ||
915 DOIT cvs -d "${CVSROOT}" -q commit \
916 -m 'Sort (NFCI)' mi || exit 6
917 rm -f mi.unsorted
918 sed -f "${WORKDIR}/sedcmds" < mi > mi.new || exit 7
919 test ! -s mi.new || cmp -s mi mi.new && {
920 printf 'Failed to make changes to set lists'
921 exit 8
922 }
923 mv mi.new mi || exit 9
924 sh ../../sort-list mi || exit 10
925 DOIT cvs -d "${CVSROOT}" commit -m "${MSG}" mi ||
926 exit 11
927 printf 'Sets list successfully updated'
928 exit 0
929 ) || {
930 printf '%s: %d\n%s %s\n' 'Sets list update failed' "$?"\
931 'Update the sets list' \
932 '(src/distrib/sets/lists/base/mi) manually'
933 if [ -s "${WORKDIR}"/removedzones ]
934 then
935 printf 'Removed Zones:'
936 sed 's/^/ /' < "${WORKDIR}"/removedzones
937 fi
938 if [ -s "${WORKDIR}"/addedzones ]
939 then
940 printf 'Added Zones:'
941 sed 's/^/ /' < "${WORKDIR}"/addedzones
942 fi
943 }
944 fi
945 }
946
947 extra()
948 {
949 cat <<EOF
950 Also do the following:
951 * Edit and commit src/doc/3RDPARTY
952 * Edit and commit src/doc/CHANGES
953 * Edit and commit src/distrib/sets/lists/base/mi
954 * if the set of installed files altered (if not done by the script).
955 * Submit pullup requests for all active release branches.
956 * rm -rf ${WORK_PFX} (optional)
957 * Verify that
958 ${UPDATE_FROM}
959 * no longer exists.
960 EOF
961 }
962
963 main()
964 {
965 set -e
966 validate_pwd
967 findcvsroot
968 mkworkpfx
969
970 argparse "$@"
971
972 cvsupdate || fail 'working directory (dist) cvs update failed:'" $?"
973
974 setup_versions
975 mkworkdir
976 fetch
977 $GLOBAL || checksig
978 extract
979
980 zonelists
981
982 addnews
983 trimnews
984 mkimportmsg
985 editimportmsg
986 cvsimport
987 cvsmerge
988 resolveconflicts
989 cvscommitmerge
990 updatedzones
991
992 setlistupdate
993
994 rm -f "${UPDATE_FROM}"
995 rm -fr "${WORK_PFX}"/oldzones \
996 "${WORK_PFX}"/newzones \
997 "${WORK_PFX}"/updzones
998 extra
999 }
1000
1001 main "$@"
1002