update from latest MirBSD CVS
[shellsnippets/shellsnippets.git] / mksh / debian-dev / mkdebidx.sh
1 #!/bin/mksh
2 rcsid='$MirOS: contrib/hosted/tg/deb/mkdebidx.sh,v 1.77 2019/05/18 18:39:07 tg Exp $'
3 #-
4 # Copyright © 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
5 #             2016, 2017, 2019
6 #       mirabilos <m@mirbsd.org>
7 #
8 # Provided that these terms and disclaimer and all copyright notices
9 # are retained or reproduced in an accompanying document, permission
10 # is granted to deal in this work without restriction, including un-
11 # limited rights to use, publicly perform, distribute, sell, modify,
12 # merge, give away, or sublicence.
13 #
14 # This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15 # the utmost extent permitted by applicable law, neither express nor
16 # implied; without malicious intent or gross negligence. In no event
17 # may a licensor, author or contributor be held liable for indirect,
18 # direct, other damage, loss, or other issues arising in any way out
19 # of dealing in the work, even if advised of the possibility of such
20 # damage or existence of a defect, except proven that it results out
21 # of said person's immediate fault when using the work as intended.
22
23 unset normarchs repo_keyid gpg_remote gpg_bin repo_origin repo_label repo_title
24 unset -f repo_intro repo_description
25 me=$(dirname "$0"); [[ -s $me/mkdebidx.inc ]] && . "$me/mkdebidx.inc"
26 unset me
27
28 [[ -n ${normarchs+x} ]] || set -A normarchs -- i386
29 # either '' (locally) or 'remsign user@host.domain.com' (remote ssh)
30 [[ -n ${repo_keyid+x} ]] || repo_keyid=0xAA917C6F
31 [[ -n ${gpg_remote+x} ]] || gpg_remote=
32 case ${gpg_bin:-x} {
33 (gpg|gpg1|gpg2|gnupg|gnupg1|gnupg2) ;;
34 (*)
35         if [[ -n $gpg_remote ]]; then
36                 gpg_bin=gpg
37         elif ! gpg_bin=$(whence -p gpg1); then
38                 gpg_bin=gpg
39         fi
40         ;;
41 }
42 [[ -n ${repo_origin+x} ]] || repo_origin='The MirOS Project'
43 [[ -n ${repo_label+x} ]] || repo_label=wtf
44 [[ -n ${repo_title+x} ]] || repo_title='MirDebian “WTF” Repository'
45 typeset -f repo_intro >/dev/null || function repo_intro {
46         cat <<-'EOF'
47         <p>This APT repository contains packages by mirabilos (<i>wtf</i>)
48          for use with the Debian operating system and its derivates. It is
49          not affiliated with Debian. Some of the content is merely hosted
50          for people close to MirBSD or Debian; some is affiliated with The
51          MirOS Project.<br /><i>Debian</i> is a registered trademark owned
52          by Software in the Public Interest, Inc.<br />“The MirOS Project”
53          and “MirBSD” are unregistered trademarks owned by mirabilos.</p>
54 EOF
55 }
56 typeset -f repo_description >/dev/null || function repo_description {
57         typeset suite_nick=$1
58
59         print -nr -- "WTF ${suite_nick} Repository"
60 }
61 set -A dpkgarchs -- alpha amd64 arm arm64 armel armhf hppa hurd-i386 i386 \
62     ia64 kfreebsd-amd64 kfreebsd-i386 m68k mips mips64el mipsel powerpc \
63     powerpcspe ppc64 ppc64el s390 s390x sh4 sparc sparc64 x32
64 [[ -n "${normarchs[*]}" ]] || set -A normarchs -- "${dpkgarchs[@]}"
65
66 set +U
67 export LC_ALL=C
68 unset LANGUAGE
69 typeset -Z11 -Uui16 hv
70
71 function remsign {
72         target=$1; shift
73         master=remsign.ctl$$
74         tmpfnm=remsign.tmp$$
75         ssh -fNM -o ControlPath=$tmpfnm "$target"
76         ssh -o ControlPath=$tmpfnm "$target" cat \>$tmpfnm
77         ssh -o ControlPath=$tmpfnm -t "$target" "$* $tmpfnm" 0<&2 1>&2
78         rv=$?
79         ssh -o ControlPath=$tmpfnm "$target" "cat $tmpfnm.asc; rm -f $tmpfnm $tmpfnm.asc"
80         ssh -o ControlPath=$tmpfnm "$target" -O exit
81         return $rv
82 }
83
84 function die {
85         local rv=1
86
87         if [[ $1 = +([0-9]) ]]; then
88                 rv=$1
89                 shift
90         fi
91         print -ru2 -- "E: $*"
92         exit $rv
93 }
94
95 function checkedhash {
96         if [[ $1 = size ]]; then
97                 REPLY=$(stat -c '%s' "$2")
98                 [[ $REPLY = +([0-9]) ]] || die "Error getting size of '$2'"
99         else
100                 set -o noglob
101                 set -A REPLY -- $($1 "$2")
102                 set +o noglob
103                 [[ $REPLY = +([0-9a-f]) ]] || die "Error getting $1 of '$2'"
104         fi
105 }
106
107 function putfile {
108         tee $1 | gzip -n9 >$1.gz
109 }
110
111 function sortlist {
112         typeset x u=$1
113
114         if [[ $u = -u ]]; then
115                 shift
116         else
117                 u=
118         fi
119
120         for x in "$@"; do
121                 print -r -- "$x"
122         done | sort $u
123 }
124
125 # escape XHTML characters (three mandatory XML ones plus double quotes,
126 # the latter in an XML safe fashion numerically though)
127 function xhtml_escape {
128         if (( $# )); then
129                 print -nr -- "$@"
130         else
131                 cat
132         fi | sed \
133             -e 's\ 1&\ 1\&amp;\ 1g' \
134             -e 's\ 1<\ 1\&lt;\ 1g' \
135             -e 's\ 1>\ 1\&gt;\ 1g' \
136             -e 's\ 1"\ 1\&#34;\ 1g'
137 }
138
139 cd "$(dirname "$0")"
140 rm -f dpkg_578162_workaround
141
142 IFS=:; set -o noglob
143 dpkgarchl=:all:"${dpkgarchs[*]}":
144 IFS=$' \t\n'; set +o noglob
145
146 suites=:
147 for suite in "$@"; do
148         suites=:dists/$suite$suites
149 done
150
151 allsuites=
152 for suite in dists/*; do
153         allsuites="$allsuites${allsuites:+ }${suite##*/}"
154         [[ -h $suite ]] && continue
155         [[ $suites = : || $suites = *:"$suite":* ]] || continue
156         archs=
157         distribution=
158         dcodename=
159         debootstrap_compat=0
160         . $suite/distinfo.sh
161         suitearchs=${archs:-${normarchs[*]}}
162         components=Components:
163         for dist in $suite/*; do
164                 [[ -d $dist/. ]] || continue
165                 rm -rf $dist/binary-* $dist/source
166                 ovf= oef= osf= om=-m
167                 (( debootstrap_compat )) && om=
168                 [[ -s $dist/override.file ]] && ovf=$dist/override.file
169                 [[ -s $dist/override.extra ]] && oef="-e $dist/override.extra"
170                 [[ -s $dist/override.src ]] && osf="-s $dist/override.src"
171                 components="$components ${dist##*/}"
172                 archs=
173                 [[ -s $dist/distinfo.sh ]] && . $dist/distinfo.sh
174                 set -A distarchs -- $(sortlist -u all ${archs:-$suitearchs})
175                 IFS=:; set -o noglob
176                 distarchl=:"${distarchs[*]}":
177                 IFS=$' \t\n'; set +o noglob
178                 nmds=0
179                 for arch in $(sortlist -u ${distarchs[*]} ${dpkgarchs[*]}) /; do
180                         # put "all" last
181                         [[ $arch = all ]] && continue
182                         [[ $arch = / ]] && arch=all
183                         # create index
184                         if [[ $dpkgarchl != *:"$arch":* ]]; then
185                                 die "Invalid arch '$arch' in $dist"
186                         elif [[ $distarchl != *:"$arch":* ]]; then
187                                 print "\n===> Linking all =>" \
188                                     "${dist#dists/}/$arch/Packages"
189                                 ln -s binary-all $dist/binary-$arch
190                         elif [[ $arch = all ]] && (( nmds == 1 )); then
191                                 print "\n===> Linking $firstmd =>" \
192                                     "${dist#dists/}/all/Packages"
193                                 ln -s binary-$firstmd $dist/binary-all
194                         else
195                                 print "\n===> Creating" \
196                                     "${dist#dists/}/$arch/Packages\n"
197                                 mkdir -p $dist/binary-$arch
198                                 (dpkg-scanpackages $oef $om -a $arch \
199                                     $dist $ovf || \
200                                     echo $? >$dist/binary-$arch/failed) | \
201                                     putfile $dist/binary-$arch/Packages
202                                 [[ -e $dist/binary-$arch/failed ]] && \
203                                     exit $(<$dist/binary-$arch/failed)
204                                 (( nmds++ )) || firstmd=$arch
205                         fi
206                 done
207                 print "\n===> Creating ${dist#dists/}/Sources"
208                 mkdir -p $dist/source
209                 [[ -e dpkg_578162_workaround ]] || (dpkg-scansources $oef $osf \
210                     $dist $ovf || touch dpkg_578162_workaround) | \
211                     putfile $dist/source/Sources
212                 [[ -e dpkg_578162_workaround ]] && (dpkg-scansources $osf \
213                     $dist $ovf || echo $? >$dist/source/failed) | \
214                     putfile $dist/source/Sources
215                 [[ -e $dist/source/failed ]] && exit $(<$dist/source/failed)
216                 print done.
217                 print "\n===> Creating ${dist#dists/}/i18n/Index"
218                 [[ -d $dist/i18n/. ]] || mkdir -p $dist/i18n
219                 [[ -d $dist/i18n/. ]] || die "Cannot create $dist/i18n"
220                 rm -f $dist/i18n/.done
221                 (cd $dist/i18n
222                 tfiles=/
223                 [[ -h .hashcache || ! -f .hashcache ]] && rm -rf .hashcache
224                 for ent in .* *; do
225                         [[ $ent = . || $ent = .. || $ent = .hashcache ]] && continue
226                         [[ -h $ent || -e $ent ]] || continue
227                         if [[ ! -f $ent || $ent != Translation-* ]]; then
228                                 rm -rf "$ent"
229                                 continue
230                         fi
231                         ent=${ent#Translation-}
232                         ent=${ent%.bz2}
233                         [[ $tfiles = */"$ent"/* ]] || tfiles+=$ent/
234                 done
235                 [[ -e .hashcache ]] || :>.hashcache
236                 if [[ $tfiles = / ]]; then
237                         :>Translation-tlh_DE
238                         tfiles=/tlh_DE/
239                 fi
240                 print SHA1: >Index
241                 IFS=/; set -o noglob
242                 set -A tflist -- ${tfiles#/}
243                 IFS=$' \t\n'; set +o noglob
244                 for ent in "${tflist[@]}"; do
245                         ent=Translation-$ent
246                         if [[ $ent -nt $ent.bz2 ]]; then
247                                 if ! bzip2 -9 <"$ent" >"$ent.bz2"; then
248                                         rm -f "$ent.bz2"
249                                         die "bzip2 '$ent' died"
250                                 fi
251                         elif [[ -e $ent ]]; then
252                                 rm -f "$ent"
253                         fi
254                         hash=${|checkedhash sha1sum "$ent.bz2";} || exit 1
255                         hnum=0
256                         grep "^$hash " .hashcache |&
257                         while read -p hsha1 hsize hmd5 hsha2 usha1 usize umd5 usha2; do
258                                 [[ $hsha1 = "$hash" ]] || continue
259                                 hnum=1
260                                 while read -p hsha1 x; do
261                                         # flush coprocess, look for dupes
262                                         [[ $hsha1 = "$hash" ]] && hnum=2
263                                 done
264                                 break
265                         done
266                         hsha1=$hash
267                         if (( hnum != 1 )); then
268                                 [[ -e $ent ]] || \
269                                     if ! bzip2 -d <"$ent.bz2" >"$ent"; then
270                                         rm -f "$ent"
271                                         die "bzip2 '$ent.bz2' died"
272                                 fi
273                                 umd5=${|checkedhash md5sum "$ent";} || exit 1
274                                 hmd5=${|checkedhash md5sum "$ent.bz2";} || exit 1
275                                 usha1=${|checkedhash sha1sum "$ent";} || exit 1
276                                 usha2=${|checkedhash sha256sum "$ent";} || exit 1
277                                 hsha2=${|checkedhash sha256sum "$ent.bz2";} || exit 1
278                                 usize=${|checkedhash size "$ent";} || exit 1
279                                 hsize=${|checkedhash size "$ent.bz2";} || exit 1
280                                 (( hnum )) || print $hsha1 $hsize $hmd5 $hsha2 $usha1 $usize $umd5 $usha2 >>.hashcache
281                         fi
282                         [[ -e $ent ]] && rm -f "$ent"
283                         print -u4 $hsha1 $hsize $hmd5 $hsha2 $usha1 $usize $umd5 $usha2
284                         print -ru5 " $hsha1 $hsize $ent.bz2"
285                         print -ru6 " $umd5 $usize ${dist##*/}/i18n/$ent"
286                         print -ru6 " $hmd5 $hsize ${dist##*/}/i18n/$ent.bz2"
287                         print -ru7 " $usha1 $usize ${dist##*/}/i18n/$ent"
288                         print -ru7 " $hsha1 $hsize ${dist##*/}/i18n/$ent.bz2"
289                         print -ru8 " $usha2 $usize ${dist##*/}/i18n/$ent"
290                         print -ru8 " $hsha2 $hsize ${dist##*/}/i18n/$ent.bz2"
291                 done 4>.hashcache.new 5>>Index 6>.hashcache.md5 7>.hashcache.sha1 8>.hashcache.sha2
292                 rm -f .hashcache
293                 mv -f .hashcache.new .hashcache
294                 :>.done)
295                 [[ -e $dist/i18n/.done ]] || die i18n generation unsuccessful
296                 rm -f $dist/i18n/.done
297                 print done.
298         done
299         print "\n===> Creating ${suite#dists/}/Release"
300         rm -f $suite/Release-*
301         xdone=$(realpath $suite/Release-done)
302         (cat <<-EOF
303                 Origin: ${repo_origin}
304                 Label: ${repo_label}
305                 Suite: ${distribution:-${suite##*/}}
306                 Codename: ${dcodename:-${suite##*/}}
307                 Date: $(date -Ru)
308                 Architectures: all ${dpkgarchs[*]} source
309                 $components
310                 Description: $(repo_description "$nick")
311                 MD5Sum:
312         EOF
313         exec 4>$suite/Release-sha1
314         exec 5>$suite/Release-sha2
315         print -u4 SHA1:
316         print -u5 SHA256:
317         cd $suite
318         set -A cache_fn
319         set -A cache_md5
320         set -A cache_sha1
321         set -A cache_sha2
322         set -A cache_size
323         for n in Contents-* */{binary-*,i18n,source}/{Index,{Packag,Sourc}es*}; do
324                 [[ -f $n ]] || continue
325                 # realpath-ise $n and cache the checksum
326                 nn=$(realpath "$n")
327                 #XXX once mksh can, use associative arrays instead
328                 hv=16#${nn@#}
329                 # simple hash collision solver by increment
330                 nc=${cache_fn[hv]}
331                 while [[ -n $nc && $nc != "$nn" ]]; do
332                         nc=${cache_fn[++hv]}
333                 done
334                 if [[ $nc = "$nn" ]]; then
335                         nm=${cache_md5[hv]}
336                         ns=${cache_size[hv]}
337                         nsha1=${cache_sha1[hv]}
338                         nsha2=${cache_sha2[hv]}
339                 else
340                         # GNU *sum tools are horridly inefficient
341                         nm=${|checkedhash md5sum "$nn";} || exit 1
342                         nsha1=${|checkedhash sha1sum "$nn";} || exit 1
343                         nsha2=${|checkedhash sha256sum "$nn";} || exit 1
344                         ns=${|checkedhash size "$nn";} || exit 1
345                         cache_md5[hv]=$nm
346                         cache_size[hv]=$ns
347                         cache_fn[hv]=$nn
348                         cache_sha1[hv]=$nsha1
349                         cache_sha2[hv]=$nsha2
350                 fi
351                 print " $nm $ns $n"
352                 print -u4 " $nsha1 $ns $n"
353                 print -u5 " $nsha2 $ns $n"
354                 if [[ $n = */i18n/Index ]]; then
355                         n=${n%Index}
356                         cat "${n}.hashcache.md5"
357                         cat >&4 "${n}.hashcache.sha1"
358                         cat >&5 "${n}.hashcache.sha2"
359                         rm -f "${n}.hashcache."*
360                 fi
361         done
362         :>"$xdone") >$suite/Release-tmp
363         [[ -e $xdone ]] || die Release generation died
364         cat $suite/Release-sha1 $suite/Release-sha2 >>$suite/Release-tmp
365
366         # note: InRelease files can only be safely used by jessie and up.
367         unset use_inrelease
368         . $suite/distinfo.sh
369         rm -f $suite/InRelease $suite/Release $suite/Release.gpg
370         if [[ $use_inrelease = 1 ]]; then
371                 $gpg_remote $gpg_bin -u $repo_keyid --no-comment --clearsign \
372                     <$suite/Release-tmp >$suite/Release-inl
373                 mv -f $suite/Release-inl $suite/InRelease
374         else
375                 $gpg_remote $gpg_bin -u $repo_keyid --no-comment -sab \
376                     <$suite/Release-tmp >$suite/Release-sig
377                 mv -f $suite/Release-tmp $suite/Release
378                 mv -f $suite/Release-sig $suite/Release.gpg
379         fi
380         rm -f $suite/Release-*
381 done
382
383 print "\n===> Creating debidx.htm\n"
384
385 set -A preplsrc
386 set -A prepldst
387 integer nsrc=0 nbin=0 nrpl=0
388 br='<br />'
389
390 # syntax:       ${suitename}/${distname}/${pN}/${pp} <suite>
391 # example:      sid/wtf/openntpd/i386 lenny
392 # not here:     squeeze/wtf/xz-utils/% backport-source
393 # binary-only?  sid/wtf/pbuilder/= something
394 if [[ -s mkdebidx.lnk ]]; then
395         while read pn pd; do
396                 [[ $pn = '#'* ]] && continue
397                 if [[ $pn != +([a-z0-9_])/+([a-z0-9_-])/+([!/])/@(%|=|+([a-z0-9])) || \
398                     $pd != +([a-z0-9_]) ]]; then
399                         print -u2 "W: Invalid lnk line '$pn' '$pd'"
400                         continue
401                 fi
402                 preplsrc[nrpl]=$pn
403                 prepldst[nrpl++]=$pd
404         done <mkdebidx.lnk
405 fi
406
407 for suite in dists/*; do
408         [[ -h $suite ]] && continue
409         for dist in $suite/*; do
410                 [[ -d $dist/. ]] || continue
411                 suitename=${suite##*/}
412                 if [[ $suitename != +([a-z0-9_]) ]]; then
413                         print -u2 "W: Invalid suite name '$suitename'"
414                         continue 2
415                 fi
416                 distname=${dist##*/}
417                 if [[ $distname != +([a-z0-9_-]) ]]; then
418                         print -u2 "W: Invalid dist name '$distname'"
419                         continue
420                 fi
421
422                 gzip -dc $dist/source/Sources.gz |&
423                 pn=; pv=; pd=; pp=; Lf=
424                 while IFS= read -pr line; do
425                         case $line {
426                         (" "*)
427                                 if [[ -n $Lf ]]; then
428                                         eval x=\$$Lf
429                                         x=$x$line
430                                         eval $Lf=\$x
431                                 fi
432                                 ;;
433                         ("Package: "*)
434                                 pn=${line##Package:*([   ])}
435                                 Lf=pn
436                                 ;;
437                         ("Version: "*)
438                                 pv=${line##Version:*([   ])}
439                                 Lf=pv
440                                 ;;
441                         ("Binary: "*)
442                                 pd=${line##Binary:*([    ])}
443                                 Lf=pd
444                                 ;;
445                         ("Directory: "*)
446                                 pp=${line##Directory:*([         ])}
447                                 Lf=pp
448                                 ;;
449                         (?*)    # anything else
450                                 Lf=
451                                 ;;
452                         (*)     # empty line
453                                 if [[ -n $pn && -n $pv && -n $pd && -n $pp ]]; then
454                                         i=0
455                                         while (( i < nsrc )); do
456                                                 [[ ${sp_name[i]} = "$pn" && \
457                                                     ${sp_dist[i]} = "$distname" ]] && break
458                                                 let i++
459                                         done
460                                         if (( i == nsrc )); then
461                                                 let nsrc++
462                                                 pvo=
463                                                 ppo=
464                                         else
465                                                 eval pvo=\$\{sp_ver_${suitename}[i]\}
466                                                 eval ppo=\$\{sp_dir_${suitename}[i]\}
467                                         fi
468                                         sp_name[i]=$pn
469                                         sp_dist[i]=$distname
470                                         #sp_suites[i]="${sp_suites[i]} $suitename"
471                                         if (( nrpl )); then
472                                                 x=${suitename}/${distname}/${pn}/source
473                                                 j=0
474                                                 while (( j < nrpl )); do
475                                                         [[ ${preplsrc[j]} = "$x" ]] && break
476                                                         let j++
477                                                 done
478                                                 (( j < nrpl )) && pv="${pv}\1afrom ${prepldst[j]}"
479                                         fi
480                                         eval sp_ver_${suitename}[i]='${pvo:+$pvo,}$pv'
481                                         eval sp_dir_${suitename}[i]='${ppo:+$ppo,}$pp/'
482                                         sp_desc[i]=${sp_desc[i]},$pd
483                                 fi
484                                 pn=; pv=; pd=; pp=; Lf=
485                                 ;;
486                         }
487                 done
488
489                 gzip -dc $(for f in $dist/binary-*/Packages.gz; do
490                         [[ -e $f ]] || continue
491                         realpath "$f"
492                 done | sort -u) |&
493                 pn=; pv=; pd=; pp=; pN=; pf=; pABP=; Lf=
494                 while IFS= read -pr line; do
495                         case $line {
496                         (" "*)
497                                 if [[ -n $Lf ]]; then
498                                         eval x=\$$Lf
499                                         x=$x$line
500                                         eval $Lf=\$x
501                                 fi
502                                 ;;
503                         ("Package: "*)
504                                 pN=${line##Package:*([   ])}
505                                 Lf=pN
506                                 ;;
507                         ("Source: "*)
508                                 pn=${line##Source:*([    ])}
509                                 pn=${pn%% *}
510                                 Lf=pn
511                                 ;;
512                         ("Version: "*)
513                                 pv=${line##Version:*([   ])}
514                                 Lf=pv
515                                 ;;
516                         ("Description: "*)
517                                 pd=${line##Description:*([       ])}
518                                 ;;
519                         ("Architecture: "*)
520                                 pp=${line##Architecture:*([      ])}
521                                 Lf=pp
522                                 ;;
523                         ("Filename: "*)
524                                 pf=${line##Filename:*([  ])}
525                                 Lf=pf
526                                 ;;
527                         ("Auto-Built-Package: "*)
528                                 pABP=${line##Auto-Built-Package:*([      ])}
529                                 Lf=pABP
530                                 ;;
531                         (?*)    # anything else
532                                 Lf=
533                                 ;;
534                         (*)     # empty line
535                                 [[ $pf = *:* || $pf = *'%'* ]] && \
536                                     die Illegal character in $dist \
537                                     packages $pp "'Filename: $pf'"
538                                 [[ -n $pn ]] || pn=$pN
539                                 if [[ $pN = *-dbgsym && $pABP = debug-symbols ]]; then
540                                         : skip
541                                 elif [[ -n $pn && -n $pv && -n $pd && -n $pp ]]; then
542                                         i=0
543                                         while (( i < nbin )); do
544                                                 [[ ${bp_disp[i]} = "$pN" && ${bp_desc[i]} = "$pd" && \
545                                                     ${bp_dist[i]} = "$distname" ]] && break
546                                                 let i++
547                                         done
548                                         (( i == nbin )) && let nbin++
549                                         bp_name[i]=$pn
550                                         bp_disp[i]=$pN
551                                         bp_dist[i]=$distname
552                                         #bp_suites[i]="${bp_suites[i]} $suitename"
553                                         if (( nrpl )); then
554                                                 x=${suitename}/${distname}/${pN}/${pp}
555                                                 j=0
556                                                 while (( j < nrpl )); do
557                                                         [[ ${preplsrc[j]} = "$x" ]] && break
558                                                         let j++
559                                                 done
560                                                 (( j < nrpl )) && pv="from ${prepldst[j]}"
561                                         fi
562                                         [[ -n $pf ]] && pv="<a href=\"$pf\">$pv</a>"
563                                         pv="$pp: $pv"
564                                         eval x=\${bp_ver_${suitename}[i]}
565                                         [[ $br$x$br = *"$br$pv$br"* ]] || x=$x${x:+$br}$pv
566                                         eval bp_ver_${suitename}[i]=\$x
567                                         bp_desc[i]=$pd
568                                 fi
569                                 pn=; pv=; pd=; pp=; pN=; pf=; pABP=; Lf=
570                                 ;;
571                         }
572                 done
573         done
574 done
575
576 (cat <<'EOF'
577 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
578  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
579 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
580  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
581  <meta name="MSSmartTagsPreventParsing" content="TRUE" />
582 EOF
583 print -r -- " <title>${repo_title} Index</title>"
584 [[ -s NEWS.rss ]] && print '<link rel="alternate" type="application/rss+xml" title="RSS" href="NEWS.rss" />'
585 cat <<'EOF'
586  <style type="text/css"><!--/*--><![CDATA[/*><!--*/
587   table {
588    border: 1px solid black;
589    border-collapse: collapse;
590    text-align: left;
591    vertical-align: top;
592   }
593   tr {
594    border: 1px solid black;
595    text-align: left;
596    vertical-align: top;
597   }
598   td {
599    border: 1px solid black;
600    text-align: left;
601    vertical-align: top;
602   }
603   th {
604    background-color: #000000;
605    color: #FFFFFF;
606   }
607   .tableheadcell {
608    border: 1px solid #999999;
609    padding: 3px;
610    white-space: nowrap;
611   }
612   .srcpkgline {
613    background-color: #CCCCCC;
614   }
615   .srcpkgdist {
616    background-color: #666666;
617    color: #FFFFFF;
618    font-weight: bold;
619   }
620   .binpkgdist {
621    background-color: #999999;
622    color: #FFFFFF;
623    font-weight: bold;
624   }
625  /*]]>*/--></style>
626 </head><body>
627 EOF
628 print -r -- "<h1>${repo_title}</h1>"
629 repo_intro
630 cat <<'EOF'
631 <p><a href="dists/">Browse</a> the repository or read about how to amend <a
632  href="sources.txt">/etc/apt/sources.list</a> in order to use it.
633 EOF
634 [[ -s 0-NOTE.txt ]] && print ' Also read my <a href="0-NOTE.txt">notes</a>.'
635 [[ -s NEWS.rss ]] && print ' There is an <a href="NEWS.rss">RSS newsfeed</a>.'
636 cat <<EOF
637  This repository uses <a
638   href="http://pgp.uni-mainz.de:11371/pks/lookup?search=${repo_keyid}&amp;op=vindex">${repo_keyid}</a>
639  as signing key.
640 </p>
641 <h2>Suites</h2>
642 <ul>
643 EOF
644
645 allsuites=$(for suitename in $allsuites; do
646         print $suitename
647 done | sort -u)
648
649 for suitename in $allsuites; do
650         suite=dists/$suitename
651         if [[ -h $suite ]]; then
652                 ent=$(realpath "$suite")
653                 n=$(realpath dists)
654                 [[ $ent = "$n"/+([!/]) ]] || continue
655                 ent=${ent#"$n"/}
656                 for n in $allsuites; do
657                         [[ $n = "$ent" ]] && break
658                 done
659                 [[ $n = "$ent" ]] && print -r \
660                     " <li>$suitename: symbolic link to $ent</li>"
661                 continue
662         fi
663         . $suite/distinfo.sh
664         print -n " <li>${suite##*/}: <a href=\"$suite/\">$desc</a> (dists:"
665         for dist in $suite/*; do
666                 [[ -d $dist/. ]] || continue
667                 distname=${dist##*/}
668                 print -n " <a href=\"$suite/$distname/\">$distname</a>"
669         done
670         print ")</li>"
671 done
672 print "</ul>"
673 print "<h2>Packages</h2>"
674 print "<table width=\"100%\"><thead>"
675 print "<tr class=\"tablehead\">"
676 print " <th class=\"tableheadcell\">dist</th>"
677 print " <th class=\"tableheadcell\" rowspan=\"2\">Binary / Description</th>"
678 for suitename in $allsuites; do
679         [[ -h dists/$suitename ]] && continue
680         print " <th class=\"tableheadcell\" rowspan=\"2\">$suitename</th>"
681 done
682 print "</tr><tr class=\"tablehead\">"
683 print " <th class=\"tableheadcell\">package name</th>"
684 print "</tr></thead><tbody>"
685
686 set -A bp_sort
687 i=0
688 while (( i < nbin )); do
689         print $i ${bp_disp[i++]} #${bp_suites[i]}
690 done | sort -k2 |&
691 while read -p num rest; do
692         bp_sort[${#bp_sort[*]}]=$num
693 done
694
695 i=0
696 while (( i < nsrc )); do
697         print $i ${sp_name[i++]}
698 done | sort -k2 |&
699 while read -p num rest; do
700         print "\n<!-- sp #$num = ${sp_name[num]} -->"
701         print "<tr class=\"srcpkgline\">"
702         print " <td class=\"srcpkgdist\">${sp_dist[num]}</td>"
703         pd=
704         for x in $(tr ', ' '\n' <<<"${sp_desc[num]}" | sort -u); do
705                 [[ -n $x ]] && pd="$pd, $x"
706         done
707         print " <td rowspan=\"2\" class=\"srcpkgdesc\">${pd#, }</td>"
708         for suitename in $allsuites; do
709                 [[ -h dists/$suitename ]] && continue
710                 eval pvo=\${sp_ver_${suitename}[num]}
711                 eval ppo=\${sp_dir_${suitename}[num]}
712                 IFS=,; set -o noglob
713                 set -A pva -- $pvo
714                 set -A ppa -- $ppo
715                 IFS=$' \t\n'; set +o noglob
716                 (( ${#pva[*]} )) || pva[0]=
717                 y=
718                 i=0
719                 while (( i < ${#pva[*]} )); do
720                         pv=${pva[i]}
721                         pp=${ppa[i]}
722                         if [[ $pv = *"\1a"* ]]; then
723                                 pvdsc=${pv%%"\1a"*}
724                                 pv=${pv##*"\1a"}
725                         else
726                                 pvdsc=$pv
727                         fi
728                         if [[ -z $pv ]]; then
729                                 pv=-
730                                 if (( nrpl )); then
731                                         x=${suitename}/${sp_dist[num]}/${sp_name[num]}/%
732                                         j=0
733                                         while (( j < nrpl )); do
734                                                 [[ ${preplsrc[j]} = "$x" ]] && break
735                                                 let j++
736                                         done
737                                         (( j < nrpl )) && pv=${prepldst[j]}
738                                 fi
739                         elif [[ $pp != ?(/) ]]; then
740                                 pv="<a href=\"$pp${sp_name[num]}_${pvdsc##+([0-9]):}.dsc\">$pv</a>"
741                         fi
742                         [[ $pp != ?(/) ]] && pv="<a href=\"$pp\">[dir]</a> $pv"
743                         y=${y:+"$y<br />"}$pv
744                         let i++
745                 done
746                 print " <td rowspan=\"2\" class=\"srcpkgitem\">$y</td>"
747         done
748         print "</tr><tr class=\"srcpkgline\">"
749         print " <td class=\"srcpkgname\">${sp_name[num]}</td>"
750         print "</tr>"
751         k=0
752         while (( k < nbin )); do
753                 (( (i = bp_sort[k++]) < 0 )) && continue
754                 [[ ${bp_name[i]} = "${sp_name[num]}" && \
755                     ${bp_dist[i]} = "${sp_dist[num]}" ]] || continue
756                 bp_sort[k - 1]=-1
757                 #print "<!-- bp #$i for${bp_suites[i]} -->"
758                 print "<!-- bp #$i -->"
759                 print "<tr class=\"binpkgline\">"
760                 print " <td class=\"binpkgname\">${bp_disp[i]}</td>"
761                 print " <td class=\"binpkgdesc\">$(xhtml_escape "${bp_desc[i]}")</td>"
762                 for suitename in $allsuites; do
763                         [[ -h dists/$suitename ]] && continue
764                         eval pv=\${bp_ver_${suitename}[i]}
765                         if [[ -z $pv ]]; then
766                                 pv=-
767                                 if (( nrpl )); then
768                                         x=${suitename}/${sp_dist[num]}/${sp_name[num]}/%
769                                         j=0
770                                         while (( j < nrpl )); do
771                                                 [[ ${preplsrc[j]} = "$x" ]] && break
772                                                 let j++
773                                         done
774                                         (( j < nrpl )) && pv=${prepldst[j]}
775                                 fi
776                         fi
777                         print " <td class=\"binpkgitem\">$pv</td>"
778                 done
779                 print "</tr>"
780         done
781 done
782
783 num=0
784 for i in ${bp_sort[*]}; do
785         (( i < 0 )) && continue
786         if (( !num )); then
787                 print "\n<!-- sp ENOENT -->"
788                 print "<tr class=\"srcpkgline\">"
789                 print " <td class=\"srcpkgname\">~ENOENT~</td>"
790                 print " <td class=\"srcpkgdesc\">binary" \
791                     "packages without a matching source package</td>"
792                 for suitename in $allsuites; do
793                         [[ -h dists/$suitename ]] && continue
794                         print " <td class=\"srcpkgitem\">-</td>"
795                 done
796                 print "</tr>"
797                 num=1
798         fi
799         #print "<!-- bp #$i for${bp_suites[i]} -->"
800         print "<!-- bp #$i -->"
801         print "<tr class=\"binpkgline\">"
802         print " <td class=\"binpkgdist\">${bp_dist[i]}</td>"
803         print " <td rowspan=\"2\" class=\"binpkgdesc\">$(xhtml_escape "${bp_desc[i]}")</td>"
804         for suitename in $allsuites; do
805                 [[ -h dists/$suitename ]] && continue
806                 eval pv=\${bp_ver_${suitename}[i]}
807                 if [[ -z $pv ]]; then
808                         pv=-
809                         if (( nrpl )); then
810                                 x=${suitename}/${bp_dist[num]}/${bp_disp[num]}/=
811                                 j=0
812                                 while (( j < nrpl )); do
813                                         [[ ${preplsrc[j]} = "$x" ]] && break
814                                         let j++
815                                 done
816                                 (( j < nrpl )) && pv=${prepldst[j]}
817                         fi
818                 fi
819                 print " <td rowspan=\"2\" class=\"binpkgitem\">$pv</td>"
820         done
821         print "</tr><tr class=\"binpkgline\">"
822         print " <td class=\"binpkgname\">${bp_disp[i]}</td>"
823         print "</tr>"
824 done
825
826 cat <<EOF
827
828 </tbody></table>
829
830 <p>• <a href="http://validator.w3.org/check/referer">Valid XHTML/1.1!</a>
831  • <small>Generated on $(date -u +'%F %T') by <tt
832  style="white-space:pre;">$rcsid</tt></small> •</p>
833 </body></html>
834 EOF
835
836 :) >debidx.htm
837 print done.