1#! /bin/sh
2#
3# Copyright (C) 2009-2010, 2014, 2018-2020 Free Software Foundation, Inc.
4#
5# This program is free software: you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 3 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13# GNU General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program.  If not, see <https://www.gnu.org/licenses/>.
17#
18
19# Usage: convert-archive FROM TO [FROMFILE [TOFILE]]
20# where FROM is dir or dirgz or dirbz2 or dirxz or cvs or git
21# and   TO   is dir or dirgz or dirbz2 or dirxz or cvs or git
22# This will read FROMFILE (default: archive.$FROM.tar.gz)
23# and produce TOFILE (default: archive.$TO.tar.gz).
24
25progname=$0
26package=@PACKAGE@
27version=@VERSION@
28
29# func_usage
30# outputs to stdout the --help usage message.
31func_usage ()
32{
33  echo "\
34Usage: convert-archive FROM TO [FROMFILE [TOFILE]]
35
36Converts the archive of gettext infrastructure from the FROM format
37to the TO format.
38FROMFILE is the original file, defaulting to archive.\$FROM.tar.gz.
39TOFILE is the destination file, defaulting to archive.\$TO.tar.gz.
40
41Report bugs in the bug tracker at <https://savannah.gnu.org/projects/gettext>
42or by email to <bug-gettext@gnu.org>."
43}
44
45# func_version
46# outputs to stdout the --version message.
47func_version ()
48{
49  echo "$progname (GNU $package) $version"
50  echo "Copyright (C) 2009-2020 Free Software Foundation, Inc.
51License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>
52This is free software: you are free to change and redistribute it.
53There is NO WARRANTY, to the extent permitted by law."
54  echo "Written by" "Bruno Haible"
55}
56
57# func_fatal_error message
58# outputs to stderr a fatal error message, and terminates the program.
59func_fatal_error ()
60{
61  echo "convert-archive: *** $1" 1>&2
62  echo "convert-archive: *** Stop." 1>&2
63  exit 1
64}
65
66# Command-line option processing.
67
68if { { test $# = 2 || test $# = 3 || test $# = 4; } \
69       && case "$1" in dir | cvs | git ) true;; *) false;; esac \
70       && case "$2" in dir | cvs | git ) true;; *) false;; esac; }; then
71  :
72else
73  echo "Usage: convert-archive {dir|cvs|git} {dir|cvs|git} [fromfile [tofile]]"
74  exit 1
75fi
76
77from="$1"
78to="$2"
79fromfile="$3"
80test -n "$fromfile" || fromfile=`pwd`/archive.$from.tar.gz
81tofile="$4"
82test -n "$tofile" || tofile=`pwd`/archive.$to.tar.gz
83
84unpacked=`pwd`/unpacked-files
85mkdir "$unpacked" || {
86  if test -d "$unpacked"; then
87    func_fatal_error "directory $unpacked already exists"
88  else
89    func_fatal_error "cannot create directory $unpacked"
90  fi
91}
92
93# Unpack the original archive.
94case "$from" in
95  dir*)
96    { case "$from" in
97        dir) cat < "$fromfile" ;;
98        dirgz) gzip -d -c < "$fromfile" ;;
99        dirbz2) bzip2 -d -c < "$fromfile" ;;
100        dirxz) xz -d -c < "$fromfile" ;;
101      esac
102    } \
103      | (cd "$unpacked" && tar xf -) \
104      || func_fatal_error "file copy failed"
105    ;;
106
107  cvs)
108    # Set variables
109    # - work_dir        directory containing the temporary checkout
110    work_dir=tmpwrk$$
111    mkdir "$work_dir" || {
112      if test -d "$work_dir"; then
113        func_fatal_error "directory $work_dir already exists"
114      else
115        func_fatal_error "cannot create directory $work_dir"
116      fi
117    }
118
119    # Set variables
120    # - cvs_dir         directory containing the temporary repository
121    cvs_dir=tmpcvs$$
122    # Use an umask of 077, to avoid attacks that work by overwriting files in
123    # the "$CVSROOT"/CVSROOT directory.
124    (umask 077 && mkdir "$cvs_dir") || {
125      if test -d "$cvs_dir"; then
126        func_fatal_error "directory $cvs_dir already exists"
127      else
128        func_fatal_error "cannot create directory $cvs_dir"
129      fi
130    }
131    CVSROOT=`pwd`/"$cvs_dir"
132    unset CVS_CLIENT_LOG
133    unset CVS_CLIENT_PORT
134    unset CVS_IGNORE_REMOTE_ROOT
135    unset CVS_LOCAL_BRANCH_NUM
136    unset CVS_NOBASES
137    unset CVS_PASSFILE
138    unset CVS_PASSWORD
139    unset CVS_PROXY_PORT
140    unset CVS_RCMD_PORT
141    unset CVS_RSH
142    unset CVS_SERVER
143    unset CVS_SERVER_SLEEP
144    CVS_SIGN_COMMITS=
145    export CVS_SIGN_COMMITS
146    unset CVS_SSH
147    unset CVS_VERIFY_CHECKOUTS
148    unset CVS_VERIFY_TEMPLATE
149    unset CVSIGNORE
150    unset CVSREAD
151    unset CVSREADONLYFS
152    unset CVSUMASK
153    unset CVSWRAPPERS
154
155    # Need to pass -d "$CVSROOT", because there may be a CVS directory in the
156    # current directory.
157    cvs -d "$CVSROOT" init || func_fatal_error "cvs init failed"
158    gzip -d -c < "$fromfile" | (cd "$cvs_dir" && tar xf -)
159
160    # A witness that contains all versions.
161    # Can be e.g. ABOUT-NLS, intl/ChangeLog, m4/gettext.m4.
162    witness='ABOUT-NLS'
163
164    # Get list of tags or versions.
165    sed_extract_tags_from_log1='/^symbolic names:/{
166:a
167/^	/p
168n
169ba
170}
171/^keyword substitution:/q'
172    sed_extract_tags_from_log2='s/^	\([^:]*\):.*/\1/'
173    rlog_harmless_warning_regex="warning: Unknown phrases like \`commitid \.\.\.;' are present\."
174    tags=`
175      ( LC_ALL=C rlog "$cvs_dir"/archive/$witness,v 2>&1 1>&3 \
176        | grep -v "$rlog_harmless_warning_regex" 1>&2 ) 3>&1 \
177      | sed -n -e "$sed_extract_tags_from_log1" \
178      | sed -e "$sed_extract_tags_from_log2"`
179    test -n "$tags" || func_fatal_error "no release tags found"
180    sed_tag_to_version='s/_/./g'
181    for tag in $tags; do
182      if test $tag != release; then
183        version=`echo "$tag" | sed -e "$sed_tag_to_version"`
184        (cd "$work_dir"
185         cvs -d "$CVSROOT" checkout -r"$tag" archive > /dev/null 2> cvs.err \
186           || { cat cvs.err 1>&2; exit 1; }
187         cat cvs.err | grep -v '^cvs checkout: Updating'
188         find archive -name CVS -type d -print | xargs rm -rf
189         rm -rf CVS
190        ) || func_fatal_error "cvs checkout failed"
191        mv "$work_dir"/archive "$unpacked/$version"
192      fi
193    done
194    rm -rf "$cvs_dir"
195    rm -rf "$work_dir"
196    ;;
197
198  git)
199    # Set variables
200    # - work_dir        directory containing the temporary checkout
201    work_dir=tmpwrk$$
202    mkdir "$work_dir" || {
203      if test -d "$work_dir"; then
204        func_fatal_error "directory $work_dir already exists"
205      else
206        func_fatal_error "cannot create directory $work_dir"
207      fi
208    }
209
210    mkdir "$work_dir/master" || func_fatal_error "mkdir failed"
211    gzip -d -c < "$fromfile" | (cd "$work_dir/master" && tar xf -)
212    (unset GIT_CONFIG
213     unset XDG_CONFIG_HOME
214     unset HOME
215     GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
216     cd "$work_dir"
217     tags=`cd master && git tag`
218     test -n "$tags" || func_fatal_error "no release tags found"
219     for tag in $tags; do
220       if test $tag != empty; then
221         version=$tag
222         (cd master && git checkout -q $tag) \
223           || func_fatal_error "git checkout failed"
224         rm -f master/.gitignore
225         mv master/.git .git
226         mkdir "$unpacked/$version" || func_fatal_error "mkdir failed"
227         (cd master && tar cf - .) | (cd "$unpacked/$version" && tar xf -) \
228           || func_fatal_error "file copy failed"
229         mv .git master/.git
230       fi
231     done
232    )
233    rm -rf "$work_dir"
234    ;;
235esac
236
237# Find a good 'tar' program.
238existing_file=gettext-0.10.35/ABOUT-NLS
239TAR=tar
240TAR_OPTIONS=
241for prog in tar gnutar gtar; do
242  if (cd "$unpacked" && $prog cf - --owner=root --group=root "$existing_file") >/dev/null 2>&1; then
243    TAR=$prog
244    TAR_OPTIONS='--owner=root --group=root'
245    break
246  fi
247done
248
249# Create the target archive.
250case "$to" in
251  dir*)
252    (cd "$unpacked" && $TAR cf - $TAR_OPTIONS *) \
253      | { case "$to" in
254            dir) cat > "$tofile" ;;
255            dirgz) gzip -c -9 > "$tofile" ;;
256            dirbz2) bzip2 -c -9 > "$tofile" ;;
257            dirxz) xz -c -5 > "$tofile" ;;
258          esac
259        } \
260      || func_fatal_error "archive creation failed"
261    ;;
262
263  cvs)
264    # Set variables
265    # - cvs_dir         directory containing the temporary repository
266    cvs_dir=autopoint-files
267    # Use an umask of 077, to avoid attacks that work by overwriting files in
268    # the "$CVSROOT"/CVSROOT directory.
269    (umask 077 && mkdir "$cvs_dir") || {
270      if test -d "$cvs_dir"; then
271        func_fatal_error "directory $cvs_dir already exists"
272      else
273        func_fatal_error "cannot create directory $cvs_dir"
274      fi
275    }
276    CVSROOT=`pwd`/"$cvs_dir"
277    unset CVS_CLIENT_LOG
278    unset CVS_CLIENT_PORT
279    unset CVS_IGNORE_REMOTE_ROOT
280    unset CVS_PASSFILE
281    unset CVS_PASSWORD
282    unset CVS_RCMD_PORT
283    unset CVS_RSH
284    unset CVS_SERVER
285    unset CVS_SERVER_SLEEP
286    unset CVSIGNORE
287    unset CVSREAD
288    unset CVSUMASK
289    unset CVSWRAPPERS
290
291    # Set a nonstandard variable, for a good-looking cvs history.
292    cvsuser=bruno
293    gcc -shared -fPIC -O cvsuser.c -o cvsuser.so
294    cvsuser_hack=`pwd`/cvsuser.so
295
296    # Need to pass -d "$CVSROOT", because there may be a CVS directory in the
297    # current directory.
298    cvs -d "$CVSROOT" init || func_fatal_error "cvs init failed"
299
300    # Add the contents of the unpacked directory to the repository.
301    (cd "$unpacked"
302     for version in *; do
303       cvsver=`echo "$version" | sed -e 's/\./_/g'`
304       (cd $version
305        CVSUSER=$cvsuser LD_PRELOAD=$cvsuser_hack \
306        cvs -d "$CVSROOT" import -m "Import $version" archive release "$cvsver" > /dev/null
307       ) || func_fatal_error "cvs import failed"
308       # In order to avoid keyword substitution, we have to use "cvs admin"
309       # in a temporary checkout.
310       mkdir tmpcheckout || func_fatal_error "mkdir failed"
311       (cd tmpcheckout \
312        && cvs -d "$CVSROOT" checkout -r"$cvsver" archive > /dev/null \
313        && cvs -d "$CVSROOT" admin -ko `find . -type f -print | sed -e 's,^\./,,' | grep -v '^CVS/' | grep -v '/CVS/'` > /dev/null
314       ) 2> cvs.err || { cat cvs.err 1>&2; func_fatal_error "cvs checkout or admin failed"; }
315       rm -rf tmpcheckout
316     done
317    )
318    (cd "$cvs_dir" && $TAR cf - $TAR_OPTIONS archive) \
319      | gzip -c -9 > "$tofile" \
320      || func_fatal_error "archive creation failed"
321    rm -rf "$cvs_dir" cvsuser.so
322    ;;
323
324  git)
325    git_dir=`pwd`/tmpgit$$
326    mkdir "$git_dir" || func_fatal_error "mkdir failed"
327    unset GIT_CONFIG
328    unset XDG_CONFIG_HOME
329    unset HOME
330    GIT_CONFIG_NOSYSTEM=1; export GIT_CONFIG_NOSYSTEM
331    (cd "$git_dir" && {
332      git init -q
333      git config user.name 'GNU Gettext Build'
334      git config user.email 'bug-gnu-gettext@gnu.org'
335      touch .gitignore
336      git add .
337      git commit --author="Bruno Haible <bruno@clisp.org>" \
338                 --message="Empty directory" 2>&1 >/dev/null
339      git tag empty
340    }) || func_fatal_error "git init failed"
341    sed_remove_leading_dot='s,^\./,,'
342    sed_remove_git_infrastructure='/^\.git/d'
343    (cd "$unpacked"
344     for version in *; do
345       (cd $version && tar cf - .) | \
346       (cd "$git_dir" && {
347         prev_files=`find . -type f | sed -e "$sed_remove_leading_dot" -e "$sed_remove_git_infrastructure"`
348         if test -n "$prev_files"; then
349           git rm -q $prev_files
350         fi
351         tar xf -
352         git add .
353         git commit --author="Bruno Haible <bruno@clisp.org>" \
354                    --message="Import $version" 2>&1 >/dev/null
355         git tag "$version"
356       }) || func_fatal_error "file copy into git repository failed"
357     done
358    )
359    (cd "$git_dir" && git reset -q --hard empty && git repack -a -d -q) \
360      || func_fatal_error "git reset or repack failed"
361    (cd "$git_dir" && $TAR cf - $TAR_OPTIONS .git) \
362      | gzip -c -9 > "$tofile" \
363      || func_fatal_error "archive creation failed"
364    rm -rf "$git_dir"
365    ;;
366esac
367
368rm -rf "$unpacked"
369