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@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 19VERS_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 47DIST_HOST=ftp.iana.org 48DIST_PATH=tz 49DIST_FILES=releases 50 51GTZURL=https://github.com/JodaOrg/global-tz/releases/download 52 53EDITOR=${EDITOR:-vi} 54 55TZBASE=$(pwd) || fail 'Cannot find myself ($PWD)' 56cd -P "$TZBASE" || fail "Cannot return home: ${TZBASE}" 57 58WORK_PFX=${TZBASE}/update-work 59UPDATE_FROM=${WORK_PFX}/updating.from.version 60 61usage() 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 76fail() 77{ 78 local IFS=' ' 79 80 printf >&2 '%s\n' "Error detected:" " $*" "Aborting." 81 exit 1 82} 83 84valid_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 102get_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 139argparse() 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 283setup_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 334DOIT() 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# 370shell_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 407validate_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 442findcvsroot() 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 451mkworkpfx() 452{ 453 mkdir -p "${WORK_PFX}" || fail "Unable to make missing ${WORK_PFX}" 454} 455mkworkdir() 456{ 457 mkdir -p "${WORKDIR}" || fail "Unable to make missing ${WORKDIR}" 458} 459 460cvsupdate() 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 467fetch() 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 482checksig() 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' 496gpg: Signature made .* using RSA key ID (62AA7E34|44AD418C) 497gpg: Good signature from "Paul Eggert <eggert@cs.ucla.edu>" 498gpg exit status 0 499EOF 500} 501 502extract() 503{ 504 [ -f "${EXTRACTDIR}/zone.tab" ] && return 505 mkdir -p "${EXTRACTDIR}" 506 tar -z -xf "${DISTFILE}" -C "${EXTRACTDIR}" 507} 508 509rflist() 510( 511 test "${1}" && cd "${1}" && find * -print | sort 512) 513 514zonelists() 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 582updatedzones() 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 624addnews() 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# 633trimnews() 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# 668mkimportmsg() 669{ 670 [ -s "${IMPORTMSGFILE}" ] && return 671 { cat <<EOF 672EDIT ME: Edit this file and then delete the lines marked "EDIT ME". 673EDIT ME: This file will be used as a log message for the "cvs commit" that 674EDIT ME: imports tzdata${NEWVER}. The initial contents of this file were 675EDIT ME: generated from ${NEWSFILE}. 676EDIT ME: 677EOF 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 739editimportmsg() 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 750cvsimport() 751{ 752 if [ -e "${IMPORTDONEFILE}" ]; then 753 cat >&2 <<EOF 754The CVS import has already been performed. 755EOF 756 return 0 757 fi 758 if ! [ -s "${IMPORTMSGFILE}" ] || grep -q '^EDIT' "${IMPORTMSGFILE}" 759 then 760 cat >&2 <<EOF 761The message file ${IMPORTMSGFILE} 762has not been properly edited. 763Not performing cvs import. 764EOF 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 773cvsmerge() 774{ 775 776 cd "${TZDISTDIR}" || exit 1 777 if [ -e "${MERGEDONEFILE}" ] 778 then 779 cat >&2 <<EOF 780The CVS merge has already been performed. 781EOF 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 793resolveconflicts() 794{ 795 cd "${TZDISTDIR}" || exit 1 796 if grep -l '^[<=>][<=>][<=>]' * 797 then 798 cat <<EOF 799There appear to be conflicts in the files listed above. 800Resolve conflicts, then re-run this script. 801EOF 802 return 1 803 fi 804} 805 806cvscommitmerge() 807{ 808 cd "${TZDISTDIR}" || exit 1 809 if grep -l '^[<=>][<=>][<=>]' * 810 then 811 cat >&2 <<EOF 812There still appear to be conflicts in the files listed above. 813Not performing cvs commit. 814EOF 815 return 1 816 fi 817 if [ -e "${COMMITMERGEDONEFILE}" ]; then 818 cat >&2 <<EOF 819The CVS commmit (of the merge result) has already been performed. 820EOF 821 return 0 822 fi 823 DOIT cvs -d "${CVSROOT}" commit -m "Merge tzdata${NEWVER}" && 824 touch "${COMMITMERGEDONEFILE}" 825} 826 827setlistupdate() 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 947extra() 948{ 949 cat <<EOF 950Also 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. 960EOF 961} 962 963main() 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 1001main "$@" 1002