update
[shellsnippets/shellsnippets.git] / mksh / debian-dev / mkdebidx.sh
1 #!/bin/mksh
2 rcsid='$MirOS: contrib/hosted/tg/deb/mkdebidx.sh,v 1.76 2017/05/06 22:26:18 tg Exp $'
3 #-
4 # Copyright © 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015,
5 #             2016, 2017
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         debootstrap_compat=0
159         . $suite/distinfo.sh
160         suitearchs=${archs:-${normarchs[*]}}
161         components=Components:
162         for dist in $suite/*; do
163                 [[ -d $dist/. ]] || continue
164                 rm -rf $dist/binary-* $dist/source
165                 ovf= oef= osf= om=-m
166                 (( debootstrap_compat )) && om=
167                 [[ -s $dist/override.file ]] && ovf=$dist/override.file
168                 [[ -s $dist/override.extra ]] && oef="-e $dist/override.extra"
169                 [[ -s $dist/override.src ]] && osf="-s $dist/override.src"
170                 components="$components ${dist##*/}"
171                 archs=
172                 [[ -s $dist/distinfo.sh ]] && . $dist/distinfo.sh
173                 set -A distarchs -- $(sortlist -u all ${archs:-$suitearchs})
174                 IFS=:; set -o noglob
175                 distarchl=:"${distarchs[*]}":
176                 IFS=$' \t\n'; set +o noglob
177                 nmds=0
178                 for arch in $(sortlist -u ${distarchs[*]} ${dpkgarchs[*]}) /; do
179                         # put "all" last
180                         [[ $arch = all ]] && continue
181                         [[ $arch = / ]] && arch=all
182                         # create index
183                         if [[ $dpkgarchl != *:"$arch":* ]]; then
184                                 die "Invalid arch '$arch' in $dist"
185                         elif [[ $distarchl != *:"$arch":* ]]; then
186                                 print "\n===> Linking all =>" \
187                                     "${dist#dists/}/$arch/Packages"
188                                 ln -s binary-all $dist/binary-$arch
189                         elif [[ $arch = all ]] && (( nmds == 1 )); then
190                                 print "\n===> Linking $firstmd =>" \
191                                     "${dist#dists/}/all/Packages"
192                                 ln -s binary-$firstmd $dist/binary-all
193                         else
194                                 print "\n===> Creating" \
195                                     "${dist#dists/}/$arch/Packages\n"
196                                 mkdir -p $dist/binary-$arch
197                                 (dpkg-scanpackages $oef $om -a $arch \
198                                     $dist $ovf || \
199                                     echo $? >$dist/binary-$arch/failed) | \
200                                     putfile $dist/binary-$arch/Packages
201                                 [[ -e $dist/binary-$arch/failed ]] && \
202                                     exit $(<$dist/binary-$arch/failed)
203                                 (( nmds++ )) || firstmd=$arch
204                         fi
205                 done
206                 print "\n===> Creating ${dist#dists/}/Sources"
207                 mkdir -p $dist/source
208                 [[ -e dpkg_578162_workaround ]] || (dpkg-scansources $oef $osf \
209                     $dist $ovf || touch dpkg_578162_workaround) | \
210                     putfile $dist/source/Sources
211                 [[ -e dpkg_578162_workaround ]] && (dpkg-scansources $osf \
212                     $dist $ovf || echo $? >$dist/source/failed) | \
213                     putfile $dist/source/Sources
214                 [[ -e $dist/source/failed ]] && exit $(<$dist/source/failed)
215                 print done.
216                 print "\n===> Creating ${dist#dists/}/i18n/Index"
217                 [[ -d $dist/i18n/. ]] || mkdir -p $dist/i18n
218                 [[ -d $dist/i18n/. ]] || die "Cannot create $dist/i18n"
219                 rm -f $dist/i18n/.done
220                 (cd $dist/i18n
221                 tfiles=/
222                 [[ -h .hashcache || ! -f .hashcache ]] && rm -rf .hashcache
223                 for ent in .* *; do
224                         [[ $ent = . || $ent = .. || $ent = .hashcache ]] && continue
225                         [[ -h $ent || -e $ent ]] || continue
226                         if [[ ! -f $ent || $ent != Translation-* ]]; then
227                                 rm -rf "$ent"
228                                 continue
229                         fi
230                         ent=${ent#Translation-}
231                         ent=${ent%.bz2}
232                         [[ $tfiles = */"$ent"/* ]] || tfiles+=$ent/
233                 done
234                 [[ -e .hashcache ]] || :>.hashcache
235                 if [[ $tfiles = / ]]; then
236                         :>Translation-tlh_DE
237                         tfiles=/tlh_DE/
238                 fi
239                 print SHA1: >Index
240                 IFS=/; set -o noglob
241                 set -A tflist -- ${tfiles#/}
242                 IFS=$' \t\n'; set +o noglob
243                 for ent in "${tflist[@]}"; do
244                         ent=Translation-$ent
245                         if [[ $ent -nt $ent.bz2 ]]; then
246                                 if ! bzip2 -9 <"$ent" >"$ent.bz2"; then
247                                         rm -f "$ent.bz2"
248                                         die "bzip2 '$ent' died"
249                                 fi
250                         elif [[ -e $ent ]]; then
251                                 rm -f "$ent"
252                         fi
253                         hash=${|checkedhash sha1sum "$ent.bz2";} || exit 1
254                         hnum=0
255                         grep "^$hash " .hashcache |&
256                         while read -p hsha1 hsize hmd5 hsha2 usha1 usize umd5 usha2; do
257                                 [[ $hsha1 = "$hash" ]] || continue
258                                 hnum=1
259                                 while read -p hsha1 x; do
260                                         # flush coprocess, look for dupes
261                                         [[ $hsha1 = "$hash" ]] && hnum=2
262                                 done
263                                 break
264                         done
265                         hsha1=$hash
266                         if (( hnum != 1 )); then
267                                 [[ -e $ent ]] || \
268                                     if ! bzip2 -d <"$ent.bz2" >"$ent"; then
269                                         rm -f "$ent"
270                                         die "bzip2 '$ent.bz2' died"
271                                 fi
272                                 umd5=${|checkedhash md5sum "$ent";} || exit 1
273                                 hmd5=${|checkedhash md5sum "$ent.bz2";} || exit 1
274                                 usha1=${|checkedhash sha1sum "$ent";} || exit 1
275                                 usha2=${|checkedhash sha256sum "$ent";} || exit 1
276                                 hsha2=${|checkedhash sha256sum "$ent.bz2";} || exit 1
277                                 usize=${|checkedhash size "$ent";} || exit 1
278                                 hsize=${|checkedhash size "$ent.bz2";} || exit 1
279                                 (( hnum )) || print $hsha1 $hsize $hmd5 $hsha2 $usha1 $usize $umd5 $usha2 >>.hashcache
280                         fi
281                         [[ -e $ent ]] && rm -f "$ent"
282                         print -u4 $hsha1 $hsize $hmd5 $hsha2 $usha1 $usize $umd5 $usha2
283                         print -ru5 " $hsha1 $hsize $ent.bz2"
284                         print -ru6 " $umd5 $usize ${dist##*/}/i18n/$ent"
285                         print -ru6 " $hmd5 $hsize ${dist##*/}/i18n/$ent.bz2"
286                         print -ru7 " $usha1 $usize ${dist##*/}/i18n/$ent"
287                         print -ru7 " $hsha1 $hsize ${dist##*/}/i18n/$ent.bz2"
288                         print -ru8 " $usha2 $usize ${dist##*/}/i18n/$ent"
289                         print -ru8 " $hsha2 $hsize ${dist##*/}/i18n/$ent.bz2"
290                 done 4>.hashcache.new 5>>Index 6>.hashcache.md5 7>.hashcache.sha1 8>.hashcache.sha2
291                 rm -f .hashcache
292                 mv -f .hashcache.new .hashcache
293                 :>.done)
294                 [[ -e $dist/i18n/.done ]] || die i18n generation unsuccessful
295                 rm -f $dist/i18n/.done
296                 print done.
297         done
298         print "\n===> Creating ${suite#dists/}/Release"
299         rm -f $suite/Release-*
300         xdone=$(realpath $suite/Release-done)
301         (cat <<-EOF
302                 Origin: ${repo_origin}
303                 Label: ${repo_label}
304                 Suite: ${distribution:-${suite##*/}}
305                 Codename: ${suite##*/}
306                 Date: $(date -Ru)
307                 Architectures: all ${dpkgarchs[*]} source
308                 $components
309                 Description: $(repo_description "$nick")
310                 MD5Sum:
311         EOF
312         exec 4>$suite/Release-sha1
313         exec 5>$suite/Release-sha2
314         print -u4 SHA1:
315         print -u5 SHA256:
316         cd $suite
317         set -A cache_fn
318         set -A cache_md5
319         set -A cache_sha1
320         set -A cache_sha2
321         set -A cache_size
322         for n in Contents-* */{binary-*,i18n,source}/{Index,{Packag,Sourc}es*}; do
323                 [[ -f $n ]] || continue
324                 # realpath-ise $n and cache the checksum
325                 nn=$(realpath "$n")
326                 #XXX once mksh can, use associative arrays instead
327                 hv=16#${nn@#}
328                 # simple hash collision solver by increment
329                 nc=${cache_fn[hv]}
330                 while [[ -n $nc && $nc != "$nn" ]]; do
331                         nc=${cache_fn[++hv]}
332                 done
333                 if [[ $nc = "$nn" ]]; then
334                         nm=${cache_md5[hv]}
335                         ns=${cache_size[hv]}
336                         nsha1=${cache_sha1[hv]}
337                         nsha2=${cache_sha2[hv]}
338                 else
339                         # GNU *sum tools are horridly inefficient
340                         nm=${|checkedhash md5sum "$nn";} || exit 1
341                         nsha1=${|checkedhash sha1sum "$nn";} || exit 1
342                         nsha2=${|checkedhash sha256sum "$nn";} || exit 1
343                         ns=${|checkedhash size "$nn";} || exit 1
344                         cache_md5[hv]=$nm
345                         cache_size[hv]=$ns
346                         cache_fn[hv]=$nn
347                         cache_sha1[hv]=$nsha1
348                         cache_sha2[hv]=$nsha2
349                 fi
350                 print " $nm $ns $n"
351                 print -u4 " $nsha1 $ns $n"
352                 print -u5 " $nsha2 $ns $n"
353                 if [[ $n = */i18n/Index ]]; then
354                         n=${n%Index}
355                         cat "${n}.hashcache.md5"
356                         cat >&4 "${n}.hashcache.sha1"
357                         cat >&5 "${n}.hashcache.sha2"
358                         rm -f "${n}.hashcache."*
359                 fi
360         done
361         :>"$xdone") >$suite/Release-tmp
362         [[ -e $xdone ]] || die Release generation died
363         cat $suite/Release-sha1 $suite/Release-sha2 >>$suite/Release-tmp
364
365         # note: InRelease files can only be safely used by jessie and up.
366         unset use_inrelease
367         . $suite/distinfo.sh
368         rm -f $suite/InRelease $suite/Release $suite/Release.gpg
369         if [[ $use_inrelease = 1 ]]; then
370                 $gpg_remote $gpg_bin -u $repo_keyid --no-comment --clearsign \
371                     <$suite/Release-tmp >$suite/Release-inl
372                 mv -f $suite/Release-inl $suite/InRelease
373         else
374                 $gpg_remote $gpg_bin -u $repo_keyid --no-comment -sab \
375                     <$suite/Release-tmp >$suite/Release-sig
376                 mv -f $suite/Release-tmp $suite/Release
377                 mv -f $suite/Release-sig $suite/Release.gpg
378         fi
379         rm -f $suite/Release-*
380 done
381
382 print "\n===> Creating debidx.htm\n"
383
384 set -A preplsrc
385 set -A prepldst
386 integer nsrc=0 nbin=0 nrpl=0
387 br='<br />'
388
389 # syntax:       ${suitename}/${distname}/${pN}/${pp} <suite>
390 # example:      sid/wtf/openntpd/i386 lenny
391 if [[ -s mkdebidx.lnk ]]; then
392         while read pn pd; do
393                 [[ $pn = '#'* ]] && continue
394                 if [[ $pn != +([a-z0-9_])/+([a-z0-9_-])/+([!/])/@(%|=|+([a-z0-9])) || \
395                     $pd != +([a-z0-9_]) ]]; then
396                         print -u2 "W: Invalid lnk line '$pn' '$pd'"
397                         continue
398                 fi
399                 preplsrc[nrpl]=$pn
400                 prepldst[nrpl++]=$pd
401         done <mkdebidx.lnk
402 fi
403
404 for suite in dists/*; do
405         [[ -h $suite ]] && continue
406         for dist in $suite/*; do
407                 [[ -d $dist/. ]] || continue
408                 suitename=${suite##*/}
409                 if [[ $suitename != +([a-z0-9_]) ]]; then
410                         print -u2 "W: Invalid suite name '$suitename'"
411                         continue 2
412                 fi
413                 distname=${dist##*/}
414                 if [[ $distname != +([a-z0-9_-]) ]]; then
415                         print -u2 "W: Invalid dist name '$distname'"
416                         continue
417                 fi
418
419                 gzip -dc $dist/source/Sources.gz |&
420                 pn=; pv=; pd=; pp=; Lf=
421                 while IFS= read -pr line; do
422                         case $line {
423                         (" "*)
424                                 if [[ -n $Lf ]]; then
425                                         eval x=\$$Lf
426                                         x=$x$line
427                                         eval $Lf=\$x
428                                 fi
429                                 ;;
430                         ("Package: "*)
431                                 pn=${line##Package:*([   ])}
432                                 Lf=pn
433                                 ;;
434                         ("Version: "*)
435                                 pv=${line##Version:*([   ])}
436                                 Lf=pv
437                                 ;;
438                         ("Binary: "*)
439                                 pd=${line##Binary:*([    ])}
440                                 Lf=pd
441                                 ;;
442                         ("Directory: "*)
443                                 pp=${line##Directory:*([         ])}
444                                 Lf=pp
445                                 ;;
446                         (?*)    # anything else
447                                 Lf=
448                                 ;;
449                         (*)     # empty line
450                                 if [[ -n $pn && -n $pv && -n $pd && -n $pp ]]; then
451                                         i=0
452                                         while (( i < nsrc )); do
453                                                 [[ ${sp_name[i]} = "$pn" && \
454                                                     ${sp_dist[i]} = "$distname" ]] && break
455                                                 let i++
456                                         done
457                                         if (( i == nsrc )); then
458                                                 let nsrc++
459                                                 pvo=
460                                                 ppo=
461                                         else
462                                                 eval pvo=\$\{sp_ver_${suitename}[i]\}
463                                                 eval ppo=\$\{sp_dir_${suitename}[i]\}
464                                         fi
465                                         sp_name[i]=$pn
466                                         sp_dist[i]=$distname
467                                         #sp_suites[i]="${sp_suites[i]} $suitename"
468                                         if (( nrpl )); then
469                                                 x=${suitename}/${distname}/${pn}/source
470                                                 j=0
471                                                 while (( j < nrpl )); do
472                                                         [[ ${preplsrc[j]} = "$x" ]] && break
473                                                         let j++
474                                                 done
475                                                 (( j < nrpl )) && pv="${pv}\1afrom ${prepldst[j]}"
476                                         fi
477                                         eval sp_ver_${suitename}[i]='${pvo:+$pvo,}$pv'
478                                         eval sp_dir_${suitename}[i]='${ppo:+$ppo,}$pp/'
479                                         sp_desc[i]=${sp_desc[i]},$pd
480                                 fi
481                                 pn=; pv=; pd=; pp=; Lf=
482                                 ;;
483                         }
484                 done
485
486                 gzip -dc $(for f in $dist/binary-*/Packages.gz; do
487                         [[ -e $f ]] || continue
488                         realpath "$f"
489                 done | sort -u) |&
490                 pn=; pv=; pd=; pp=; pN=; pf=; pABP=; Lf=
491                 while IFS= read -pr line; do
492                         case $line {
493                         (" "*)
494                                 if [[ -n $Lf ]]; then
495                                         eval x=\$$Lf
496                                         x=$x$line
497                                         eval $Lf=\$x
498                                 fi
499                                 ;;
500                         ("Package: "*)
501                                 pN=${line##Package:*([   ])}
502                                 Lf=pN
503                                 ;;
504                         ("Source: "*)
505                                 pn=${line##Source:*([    ])}
506                                 pn=${pn%% *}
507                                 Lf=pn
508                                 ;;
509                         ("Version: "*)
510                                 pv=${line##Version:*([   ])}
511                                 Lf=pv
512                                 ;;
513                         ("Description: "*)
514                                 pd=${line##Description:*([       ])}
515                                 ;;
516                         ("Architecture: "*)
517                                 pp=${line##Architecture:*([      ])}
518                                 Lf=pp
519                                 ;;
520                         ("Filename: "*)
521                                 pf=${line##Filename:*([  ])}
522                                 Lf=pf
523                                 ;;
524                         ("Auto-Built-Package: "*)
525                                 pABP=${line##Auto-Built-Package:*([      ])}
526                                 Lf=pABP
527                                 ;;
528                         (?*)    # anything else
529                                 Lf=
530                                 ;;
531                         (*)     # empty line
532                                 [[ $pf = *:* || $pf = *'%'* ]] && \
533                                     die Illegal character in $dist \
534                                     packages $pp "'Filename: $pf'"
535                                 [[ -n $pn ]] || pn=$pN
536                                 if [[ $pN = *-dbgsym && $pABP = debug-symbols ]]; then
537                                         : skip
538                                 elif [[ -n $pn && -n $pv && -n $pd && -n $pp ]]; then
539                                         i=0
540                                         while (( i < nbin )); do
541                                                 [[ ${bp_disp[i]} = "$pN" && ${bp_desc[i]} = "$pd" && \
542                                                     ${bp_dist[i]} = "$distname" ]] && break
543                                                 let i++
544                                         done
545                                         (( i == nbin )) && let nbin++
546                                         bp_name[i]=$pn
547                                         bp_disp[i]=$pN
548                                         bp_dist[i]=$distname
549                                         #bp_suites[i]="${bp_suites[i]} $suitename"
550                                         if (( nrpl )); then
551                                                 x=${suitename}/${distname}/${pN}/${pp}
552                                                 j=0
553                                                 while (( j < nrpl )); do
554                                                         [[ ${preplsrc[j]} = "$x" ]] && break
555                                                         let j++
556                                                 done
557                                                 (( j < nrpl )) && pv="from ${prepldst[j]}"
558                                         fi
559                                         [[ -n $pf ]] && pv="<a href=\"$pf\">$pv</a>"
560                                         pv="$pp: $pv"
561                                         eval x=\${bp_ver_${suitename}[i]}
562                                         [[ $br$x$br = *"$br$pv$br"* ]] || x=$x${x:+$br}$pv
563                                         eval bp_ver_${suitename}[i]=\$x
564                                         bp_desc[i]=$pd
565                                 fi
566                                 pn=; pv=; pd=; pp=; pN=; pf=; pABP=; Lf=
567                                 ;;
568                         }
569                 done
570         done
571 done
572
573 (cat <<'EOF'
574 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
575  "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
576 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en"><head>
577  <meta http-equiv="content-type" content="text/html; charset=utf-8" />
578  <meta name="MSSmartTagsPreventParsing" content="TRUE" />
579 EOF
580 print -r -- " <title>${repo_title} Index</title>"
581 [[ -s NEWS.rss ]] && print '<link rel="alternate" type="application/rss+xml" title="RSS" href="NEWS.rss" />'
582 cat <<'EOF'
583  <style type="text/css"><!--/*--><![CDATA[/*><!--*/
584   table {
585    border: 1px solid black;
586    border-collapse: collapse;
587    text-align: left;
588    vertical-align: top;
589   }
590   tr {
591    border: 1px solid black;
592    text-align: left;
593    vertical-align: top;
594   }
595   td {
596    border: 1px solid black;
597    text-align: left;
598    vertical-align: top;
599   }
600   th {
601    background-color: #000000;
602    color: #FFFFFF;
603   }
604   .tableheadcell {
605    border: 1px solid #999999;
606    padding: 3px;
607    white-space: nowrap;
608   }
609   .srcpkgline {
610    background-color: #CCCCCC;
611   }
612   .srcpkgdist {
613    background-color: #666666;
614    color: #FFFFFF;
615    font-weight: bold;
616   }
617   .binpkgdist {
618    background-color: #999999;
619    color: #FFFFFF;
620    font-weight: bold;
621   }
622  /*]]>*/--></style>
623 </head><body>
624 EOF
625 print -r -- "<h1>${repo_title}</h1>"
626 repo_intro
627 cat <<'EOF'
628 <p><a href="dists/">Browse</a> the repository or read about how to amend <a
629  href="sources.txt">/etc/apt/sources.list</a> in order to use it.
630 EOF
631 [[ -s 0-NOTE.txt ]] && print ' Also read my <a href="0-NOTE.txt">notes</a>.'
632 [[ -s NEWS.rss ]] && print ' There is an <a href="NEWS.rss">RSS newsfeed</a>.'
633 cat <<EOF
634  This repository uses <a
635   href="http://pgp.uni-mainz.de:11371/pks/lookup?search=${repo_keyid}&amp;op=vindex">${repo_keyid}</a>
636  as signing key.
637 </p>
638 <h2>Suites</h2>
639 <ul>
640 EOF
641
642 allsuites=$(for suitename in $allsuites; do
643         print $suitename
644 done | sort -u)
645
646 for suitename in $allsuites; do
647         suite=dists/$suitename
648         if [[ -h $suite ]]; then
649                 ent=$(realpath "$suite")
650                 n=$(realpath dists)
651                 [[ $ent = "$n"/+([!/]) ]] || continue
652                 ent=${ent#"$n"/}
653                 for n in $allsuites; do
654                         [[ $n = "$ent" ]] && break
655                 done
656                 [[ $n = "$ent" ]] && print -r \
657                     " <li>$suitename: symbolic link to $ent</li>"
658                 continue
659         fi
660         . $suite/distinfo.sh
661         print -n " <li>${suite##*/}: <a href=\"$suite/\">$desc</a> (dists:"
662         for dist in $suite/*; do
663                 [[ -d $dist/. ]] || continue
664                 distname=${dist##*/}
665                 print -n " <a href=\"$suite/$distname/\">$distname</a>"
666         done
667         print ")</li>"
668 done
669 print "</ul>"
670 print "<h2>Packages</h2>"
671 print "<table width=\"100%\"><thead>"
672 print "<tr class=\"tablehead\">"
673 print " <th class=\"tableheadcell\">dist</th>"
674 print " <th class=\"tableheadcell\" rowspan=\"2\">Binary / Description</th>"
675 for suitename in $allsuites; do
676         [[ -h dists/$suitename ]] && continue
677         print " <th class=\"tableheadcell\" rowspan=\"2\">$suitename</th>"
678 done
679 print "</tr><tr class=\"tablehead\">"
680 print " <th class=\"tableheadcell\">package name</th>"
681 print "</tr></thead><tbody>"
682
683 set -A bp_sort
684 i=0
685 while (( i < nbin )); do
686         print $i ${bp_disp[i++]} #${bp_suites[i]}
687 done | sort -k2 |&
688 while read -p num rest; do
689         bp_sort[${#bp_sort[*]}]=$num
690 done
691
692 i=0
693 while (( i < nsrc )); do
694         print $i ${sp_name[i++]}
695 done | sort -k2 |&
696 while read -p num rest; do
697         print "\n<!-- sp #$num = ${sp_name[num]} -->"
698         print "<tr class=\"srcpkgline\">"
699         print " <td class=\"srcpkgdist\">${sp_dist[num]}</td>"
700         pd=
701         for x in $(tr ', ' '\n' <<<"${sp_desc[num]}" | sort -u); do
702                 [[ -n $x ]] && pd="$pd, $x"
703         done
704         print " <td rowspan=\"2\" class=\"srcpkgdesc\">${pd#, }</td>"
705         for suitename in $allsuites; do
706                 [[ -h dists/$suitename ]] && continue
707                 eval pvo=\${sp_ver_${suitename}[num]}
708                 eval ppo=\${sp_dir_${suitename}[num]}
709                 IFS=,; set -o noglob
710                 set -A pva -- $pvo
711                 set -A ppa -- $ppo
712                 IFS=$' \t\n'; set +o noglob
713                 (( ${#pva[*]} )) || pva[0]=
714                 y=
715                 i=0
716                 while (( i < ${#pva[*]} )); do
717                         pv=${pva[i]}
718                         pp=${ppa[i]}
719                         if [[ $pv = *"\1a"* ]]; then
720                                 pvdsc=${pv%%"\1a"*}
721                                 pv=${pv##*"\1a"}
722                         else
723                                 pvdsc=$pv
724                         fi
725                         if [[ -z $pv ]]; then
726                                 pv=-
727                                 if (( nrpl )); then
728                                         x=${suitename}/${sp_dist[num]}/${sp_name[num]}/%
729                                         j=0
730                                         while (( j < nrpl )); do
731                                                 [[ ${preplsrc[j]} = "$x" ]] && break
732                                                 let j++
733                                         done
734                                         (( j < nrpl )) && pv=${prepldst[j]}
735                                 fi
736                         elif [[ $pp != ?(/) ]]; then
737                                 pv="<a href=\"$pp${sp_name[num]}_${pvdsc##+([0-9]):}.dsc\">$pv</a>"
738                         fi
739                         [[ $pp != ?(/) ]] && pv="<a href=\"$pp\">[dir]</a> $pv"
740                         y=${y:+"$y<br />"}$pv
741                         let i++
742                 done
743                 print " <td rowspan=\"2\" class=\"srcpkgitem\">$y</td>"
744         done
745         print "</tr><tr class=\"srcpkgline\">"
746         print " <td class=\"srcpkgname\">${sp_name[num]}</td>"
747         print "</tr>"
748         k=0
749         while (( k < nbin )); do
750                 (( (i = bp_sort[k++]) < 0 )) && continue
751                 [[ ${bp_name[i]} = "${sp_name[num]}" && \
752                     ${bp_dist[i]} = "${sp_dist[num]}" ]] || continue
753                 bp_sort[k - 1]=-1
754                 #print "<!-- bp #$i for${bp_suites[i]} -->"
755                 print "<!-- bp #$i -->"
756                 print "<tr class=\"binpkgline\">"
757                 print " <td class=\"binpkgname\">${bp_disp[i]}</td>"
758                 print " <td class=\"binpkgdesc\">$(xhtml_escape "${bp_desc[i]}")</td>"
759                 for suitename in $allsuites; do
760                         [[ -h dists/$suitename ]] && continue
761                         eval pv=\${bp_ver_${suitename}[i]}
762                         if [[ -z $pv ]]; then
763                                 pv=-
764                                 if (( nrpl )); then
765                                         x=${suitename}/${sp_dist[num]}/${sp_name[num]}/%
766                                         j=0
767                                         while (( j < nrpl )); do
768                                                 [[ ${preplsrc[j]} = "$x" ]] && break
769                                                 let j++
770                                         done
771                                         (( j < nrpl )) && pv=${prepldst[j]}
772                                 fi
773                         fi
774                         print " <td class=\"binpkgitem\">$pv</td>"
775                 done
776                 print "</tr>"
777         done
778 done
779
780 num=0
781 for i in ${bp_sort[*]}; do
782         (( i < 0 )) && continue
783         if (( !num )); then
784                 print "\n<!-- sp ENOENT -->"
785                 print "<tr class=\"srcpkgline\">"
786                 print " <td class=\"srcpkgname\">~ENOENT~</td>"
787                 print " <td class=\"srcpkgdesc\">binary" \
788                     "packages without a matching source package</td>"
789                 for suitename in $allsuites; do
790                         [[ -h dists/$suitename ]] && continue
791                         print " <td class=\"srcpkgitem\">-</td>"
792                 done
793                 print "</tr>"
794                 num=1
795         fi
796         #print "<!-- bp #$i for${bp_suites[i]} -->"
797         print "<!-- bp #$i -->"
798         print "<tr class=\"binpkgline\">"
799         print " <td class=\"binpkgdist\">${bp_dist[i]}</td>"
800         print " <td rowspan=\"2\" class=\"binpkgdesc\">$(xhtml_escape "${bp_desc[i]}")</td>"
801         for suitename in $allsuites; do
802                 [[ -h dists/$suitename ]] && continue
803                 eval pv=\${bp_ver_${suitename}[i]}
804                 if [[ -z $pv ]]; then
805                         pv=-
806                         if (( nrpl )); then
807                                 x=${suitename}/${bp_dist[num]}/${bp_disp[num]}/=
808                                 j=0
809                                 while (( j < nrpl )); do
810                                         [[ ${preplsrc[j]} = "$x" ]] && break
811                                         let j++
812                                 done
813                                 (( j < nrpl )) && pv=${prepldst[j]}
814                         fi
815                 fi
816                 print " <td rowspan=\"2\" class=\"binpkgitem\">$pv</td>"
817         done
818         print "</tr><tr class=\"binpkgline\">"
819         print " <td class=\"binpkgname\">${bp_disp[i]}</td>"
820         print "</tr>"
821 done
822
823 cat <<EOF
824
825 </tbody></table>
826
827 <p>• <a href="http://validator.w3.org/check/referer">Valid XHTML/1.1!</a>
828  • <small>Generated on $(date -u +'%F %T') by <tt
829  style="white-space:pre;">$rcsid</tt></small> •</p>
830 </body></html>
831 EOF
832
833 :) >debidx.htm
834 print done.