Root/schhist/schhist2web

1#!/bin/bash
2#
3# schhist2web - Web-browseable graphical revision history of schematics
4#
5# Written 2010, 2012 by Werner Almesberger
6# Copyright 2010, 2012 Werner Almesberger
7#
8# This program is free software; you can redistribute it and/or modify
9# it under the terms of the GNU General Public License as published by
10# the Free Software Foundation; either version 2 of the License, or
11# (at your option) any later version.
12#
13
14
15OUTDIR=_out
16THUMB_OPTS="-w 3 -d 60 -c 0.5,0.5,0.5 -n 1,1,0"
17BG_COLOR="f0f0ff"
18FNAME_COLOR="#b0f0ff"
19SEP_COLOR="#000000"
20CUTOFF=300
21
22
23shrink()
24{
25    pnmscale -width 120 "$@" || exit
26}
27
28
29pngdiff()
30{
31    # pngdiff preproc outfile arg ...
32    pp="$1"
33    of="$2"
34    shift 2
35    if ! PATH=$PATH:`dirname $0`/ppmdiff ppmdiff "$@" "$out/_tmp"; then
36    rm -f "$out/_tmp"
37    return 1
38    fi
39    $pp "$out/_tmp" | pnmtopng >"$of"
40    rm "$out/_tmp"
41}
42
43
44symlink()
45{
46    local old=$1 new=$2
47    local src=`dirname "$new"`/$old
48
49    if [ -L "$src" ]; then
50    ln -sf "`readlink \"$src\"`" "$new"
51    else
52    ln -sf "$old" "$new"
53    fi
54}
55
56
57commit_entry()
58{
59    # usage: commit_entry <base-dir> <commit>
60    # note: the repository's base in $dir must be provided by the caller
61
62    local dir=$1 next=$2
63
64    cat <<EOF
65<TABLE bgcolor="$SEP_COLOR" cellspacing=0 width="100%"><TR><TD></TABLE>
66EOF
67    echo "<PRE>"
68    ( cd "$dir" && git show \
69        --pretty=format:"%aN <%aE>%n %ad%n%n %s" \
70    --quiet $next; ) | sed '/^diff /Q' |
71    sed '/\(.\{'$CUTOFF'\}\).*/s//\1.../' |
72      sed 's/&/&amp;/g;s/</\&lt;/g;s/>/\&gt;/g' |
73      if [ -z "$SCHHIST_COMMIT_TEMPLATE" ]; then
74    cat
75      else
76    url=`echo "$SCHHIST_COMMIT_TEMPLATE" | sed "s/{}/$next/g"`
77    sed "1s|^|<A href=\"$url\"><B>\&gt;\&gt;\&gt;</B></a> |"
78      fi
79    echo "</PRE>"
80}
81
82
83wrapped_png()
84{
85    local dir=$1 commit=$2 file=$3
86
87    mkdir -p "$dir/$commit/html"
88    echo "<A href=\"$commit/html/$file.html\"><IMG src=\"$commit/thumb/$file.png\"></A>"
89    cat <<EOF >"$dir/$commit/html/$file.html"
90<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
91<HTML>
92<TITLE>$file</TITLE>
93<BODY>
94<A href="../pdf/$file.pdf"><IMG src="../diff/$file.png"></A>
95</BODY>
96EOF
97}
98
99
100#
101# Add extra : for :name: and :name= because they consume the leading : while
102# =name: and =name= don't and would therefore be placed one position later.
103#
104ordered_names()
105{
106    ls -1 "$out/names" | (
107    while read n; do
108        if [ "${order%%:$n=*}" != "$order" ]; then
109        echo "${order%%:$n=*}":
110        elif [ "${order%%=$n:*}" != "$order" ]; then
111        echo "${order%%=$n:*}"
112        elif [ "${order%%=$n=*}" != "$order" ]; then
113        echo "${order%%=$n=*}"
114        else
115        echo "${order%%:$n:*}":
116        fi | tr -cd : | wc -c | tr '\n' ' '
117            echo "$n"
118    done; ) |
119      sort -n -s | sed 's/^[^ ]* //'
120}
121
122
123usage()
124{
125    cat <<EOF 2>&1
126usage: $0 [options] [top-dir] [top-schem] [out-dir]
127
128  top-dir top-level directory of the git archive (default: locate it)
129  top-schem root sheet of the schematics (default: locate it in top-dir)
130  out-dir output directory (default: $OUTDIR)
131  -c cache-dir cache directory (default: same as out-dir)
132  -f identify sheets by their file name, not the sheet name
133  -n don't use previous cache content (rebuild the cache)
134  -D debug mode: make temporary work trees unique and keep them
135  -S sanitize KiCad profile and top-level sheet
136  -T tmp-dir use the specified temporary directory instead of _schhist
137EOF
138    exit 1
139}
140
141
142# --- Parse command-line options ----------------------------------------------
143
144
145no_cache=false
146sanitize=
147use_sch_name=false
148debug=false
149debug_opt=
150tmp=`pwd`/_schhist2web
151
152while true; do
153    case "$1" in
154    -n) no_cache=true
155    shift;;
156    -c) [ -z "$2" ] && usage
157    cache=$2
158    shift 2;;
159    -f) use_sch_name=true
160    shift;;
161    -D) debug=true
162    debug_opt=-D
163    shift;;
164    -S) sanitize=-S
165    shift;;
166    -T) [ -z "$2" ] && usage
167    tmp=$2
168    shift 2;;
169    -*) usage;;
170    *) break;;
171    esac
172done
173
174
175# --- Interpret the command-line arguments ------------------------------------
176
177
178if [ ! -z "$1" -a -d "$1/.git" ]; then
179    dir="$1"
180    shift
181else
182    dir=.
183    while [ ! -d $dir/.git ]; do
184    if [ $dir -ef $dir/.. ]; then
185        echo "no .git/ directory found in hierarchy" 1>&2
186        exit 1
187    fi
188    dir=$dir/..
189    done
190    echo "found top-dir: $dir" 1>&2
191fi
192
193if [ ! -z "$1" -a -f "$dir/$1" -a \
194  -f "$dir/${1%.sch}.pro" ]; then
195    sch="$1"
196    shift
197else
198    for n in "$dir"/*.sch; do
199    [ -f "${n%.sch}.pro" ] || continue
200    if [ ! -z "$sch" ]; then
201        echo "multiple choices for top-level .sch file" 1>&2
202        exit 1
203    fi
204    sch="$n"
205    done
206    if [ -z "$sch" -o "$sch" = "$dir/*.sch" ]; then
207    echo "no candidate for top-level .sch file found" 1>&2
208    exit 1
209    fi
210    echo "found root sheet: $sch" 1>&2
211fi
212
213if [ ! -z "$1" ] && [ ! -e "$1" ] || [ -d "$1" -a ! -d "$1"/.git ]; then
214    out="$1"
215    shift
216else
217    out=$OUTDIR
218fi
219[ -z "$cache" ] && cache="$out"
220
221[ -z "$1" ] || usage
222
223
224# --- Set up some variables and the directories for cache and output ----------
225
226
227PATH=`dirname "$0"`:"$PATH"
228first=`gitenealogy "$dir" "$sch" | sed '$s/ .*//p;d'`
229schname=`gitenealogy "$dir" "$sch" | sed '$s/^.* //p;d'`
230order=:$SCHHIST_ORDER:
231
232rm -rf "$out/*/"{diff,thumb,html,pdf} "$out/names"
233$no_cache && rm -rf "$cache"
234mkdir -p "$out/names"
235mkdir -p "$cache"
236
237ppmmake '#e0e0e0' 5 30 | pnmtopng >"$out"/unchanged.png
238
239
240# --- Generate/update the cache -----------------------------------------------
241
242
243head=
244for n in $first `cd "$dir" && git rev-list --reverse $first..HEAD`; do
245    ( cd "$dir" && git show --pretty=format:'' --name-only $n; ) |
246      egrep -q '\.sch$|\.pro$|\.lib$' || continue
247    echo Processing $n
248    new=`gitenealogy "$dir" "$sch" | sed "/^$n /s///p;d"`
249    if [ ! -z "$new" ]; then
250    echo Name change $schname to $new 1>&2
251    schname="$new"
252    fi
253    trap "rm -rf \"$cache/$n/ps\" \"$cache/$n/ppm0\" \"$cache/$n/ppm1\" \
254      \"$cache/$n/ppm2\" \"$tmp\"" 0
255    if [ ! -d "$cache/$n/ppm2" ]; then
256    rm -rf "$cache/$n/"{ps,ppm0,ppm1,ppm2}
257    mkdir -p "$cache/$n/"{ps,ppm0,ppm1,ppm2}
258    #
259    # potential optimization here: remember Postscript files from previous
260    # run (or their md5sum) and check if they have changed. If not, skip
261    # the ghostscript run and just put a symlink, replacing the less
262    # efficient optimization below.
263    #
264    tmp2=`gitsch2ps -k $sanitize $debug_opt "$dir" "$schname" $n "$tmp"` ||
265      exit
266    for m in "$tmp"/*.ps; do
267        [ "$m" = "$tmp/*.ps" ] && break
268        # call sub-sheets by their own name, without prepending the
269        # top-level sheet's name
270        name=`basename "$m" .ps`
271        name=${name#`basename "$schname" .sch`-}
272
273        #
274        # short-cut for $schname: since it's derived from the present $sch,
275        # we can just update it without further ado.
276        #
277        if [ "$name" = "`basename \"$schname\" .sch`" ]; then
278        name=`basename "$sch" .sch`
279        elif $use_sch_name &&
280          path=`subschname2file "$tmp2/$schname" "$name"`; then
281        name=`basename "$path" .sch`
282        #
283        # if the file is easily located, also track any future name
284        # changes
285        #
286        path=`dirname "$schname"`/$path
287        if [ -f "$tmp2/$path" ]; then
288            name=`gitwhoareyounow "$tmp2" "$path"`
289            name=`basename "$name" .sch`
290        fi
291        fi
292
293        # Postscript, for making PDFs later
294        ps="$cache/$n/ps/$name.ps"
295        normalizeschps "$m" "$ps" || exit
296
297        # Unadorned pixmap, for comparison
298        ppm="$cache/$n/ppm0/$name.ppm"
299        schps2ppm -n "$ps" "$ppm" || exit
300
301        # Pixmap with thin lines, for the detail views
302        ppm="$cache/$n/ppm1/$name.ppm"
303        normalizeschps -w 120 "$m" | schps2ppm - "$ppm" || exit
304
305        # Pixmap with thick lines, for the thumbnails
306        ppm="$cache/$n/ppm2/$name.ppm"
307        normalizeschps -w 500 "$m" | schps2ppm - "$ppm" || exit
308    done
309    rm -rf "$tmp"
310    $debug || rm -rf "$tmp2"
311    fi
312    for m in "$cache/$n/ppm0/"*; do
313    [ "$m" = "$cache/$n/ppm0/*" ] && break
314    if [ ! -z "$head" ]; then
315        prev="$cache/$head/ppm0/${m##*/}"
316        if [ -r "$prev" ] && cmp -s "$prev" "$m"; then
317        for d in ppm0 ppm1 ppm2; do
318            symlink "../../$head/$d/${m##*/}" "$cache/$n/$d/${m##*/}"
319        done
320        m_ps=${m%.ppm}.ps
321        symlink "../../$head/ps/${m_ps##*/}" "$cache/$n/ps/${m_ps##*/}"
322        fi
323    fi
324    touch "$out/names/`basename \"$m\" .ppm`"
325    done
326    trap 0
327    head=$n
328done
329
330if [ -z "$head" ]; then
331    echo "no usable head found" 2>&1
332    exit 1
333fi
334
335
336# --- Title of the Web page and table header ----------------------------------
337
338
339index="$out/_index.html"
340index_final="$out/index.html"
341all=
342{
343    cat <<EOF
344<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
345<HTML>
346EOF
347    if [ ! -z "$SCHHIST_TITLE" ]; then
348    echo "<TITLE>$SCHHIST_TITLE</TITLE>"
349    fi
350    echo "<BODY>"
351    if [ ! -z "$SCHHIST_TITLE" ]; then
352    echo "<H1>"
353    [ -z "$SCHHIST_HOME_URL" ] || echo "<A href=\"$SCHHIST_HOME_URL\">"
354    echo "$SCHHIST_TITLE"
355    [ -z "$SCHHIST_HOME_URL" ] || echo "</A>"
356    echo "</H1>"
357    fi
358    cat <<EOF
359<TABLE bgcolor="$BG_COLOR" callpadding=1>
360<TR bgcolor="$FNAME_COLOR">
361EOF
362    while read m; do
363    [ "${order%%=$m[:=]*}" = "$order" ] || continue
364    ps="$cache/$head/ps/$m.ps"
365    if [ -r "$ps" ]; then
366        #
367        # Note: we read from variable ps_$head but we write to constant
368        # pdf_head. We can't use pdf_$head here, because that may just be a
369        # commit with a change and we thus generate a delta PDF below.
370        #
371        mkdir -p "$out/pdf_head"
372        schps2pdf -o "$out/pdf_head/$m.pdf" "$ps" || exit
373        all="$all \"$ps\""
374        echo "<TD><A href=\"pdf_head/$m.pdf\"><B>$m</B></A>"
375    else
376        echo "<TD><B>$m</B>"
377    fi
378    done < <(ordered_names)
379    proj=`basename "$sch" .sch`
380    eval schps2pdf -t \""$proj-"\" -o \""$out/pdf_$proj.pdf"\" $all
381    echo "<TD><A href=\"pdf_$proj.pdf\">All sheets</A>"
382} >"$index"
383
384
385# --- Diff all the revisions, newest to oldest --------------------------------
386
387
388next="$head"
389for n in `cd "$dir" && git rev-list $first..HEAD~1` $first; do
390    [ -d "$cache/$n/ppm0" ] || continue
391    empty=true
392    s="<TR>"
393    td=false
394    mkdir -p "$out/$next/"{diff,thumb,html,pdf}
395    while read m; do
396    a0="$cache/$n/ppm0/$m.ppm"
397    a1="$cache/$n/ppm1/$m.ppm"
398    a2="$cache/$n/ppm2/$m.ppm"
399    aps="$cache/$n/ps/$m.ps"
400
401    b0="$cache/$next/ppm0/$m.ppm"
402    b1="$cache/$next/ppm1/$m.ppm"
403    b2="$cache/$next/ppm2/$m.ppm"
404    bps="$cache/$next/ps/$m.ps"
405
406    diff="$out/$next/diff/$m.png"
407    thumb="$out/$next/thumb/$m.png"
408    pdf="$out/$next/pdf/$m.pdf"
409
410    if [ "${order%%=$m[:=]*}" = "$order" ]; then
411        $td && s="$s<TD>"
412        td=true
413    fi
414
415    if [ -f "$a0" -a -f "$b0" ]; then
416        if ! pngdiff cat "$diff" "$a1" "$b1" "$a0" "$b0"; then
417            $td && s="$s<TD align=\"center\" valign=\"middle\">"
418            td=false
419        s="$s<IMG src=\"unchanged.png\">"
420        continue
421        fi
422        pngdiff shrink "$thumb" -f $THUMB_OPTS "$a2" "$b2" "$a0" "$b0" ||
423          exit
424        schps2pdf -T BEFORE -T AFTER -o "$pdf" "$aps" "$bps" || exit
425    elif [ -f "$a0" ]; then
426        pngdiff cat "$diff" -f -c 1,0,0 "$a1" "$a1" || exit
427        pngdiff shrink "$thumb" -f $THUMB_OPTS -c 1,0,0 "$a2" "$a2" ||
428          exit
429        schps2pdf -T DELETED -o "$pdf" "$aps" || exit
430    elif [ -f "$b0" ]; then
431        pngdiff cat "$diff" -f -c 0,1,0 "$b1" "$b1" || exit
432        pngdiff shrink "$thumb" -f $THUMB_OPTS -c 0,1,0 "$b2" "$b2" ||
433          exit
434        schps2pdf -T NEW -o "$pdf" "$bps" || exit
435    else
436        continue
437    fi
438    $td && s="$s<TD>"
439    td=false
440    echo "$s"
441    s=
442        wrapped_png "$out" "$next" "$m"
443    empty=false
444    done < <(ordered_names)
445    if ! $empty; then
446    $td && s="$s<TD>"
447     echo "$s<TD valign=\"middle\">"
448    commit_entry "$dir" $next
449    fi
450    next=$n
451done >>"$index"
452
453
454# --- Add creation entries for all files in the first commit ------------------
455
456
457if [ -d "$cache/$next/ppm0" ]; then # could this ever be false ?
458    empty=true
459    echo "<TR>"
460    mkdir -p "$out/$next/"{diff,thumb,html,pdf}
461    while read m; do
462    p1="$cache/$next/ppm1/$m.ppm"
463    p2="$cache/$next/ppm2/$m.ppm"
464    ps="$cache/$next/ps/$m.ps"
465    diff="$out/$next/diff/$m.png"
466    thumb="$out/$next/thumb/$m.png"
467    pdf="$out/$next/pdf/$m.pdf"
468
469    [ "${order%%=$m[:=]*}" = "$order" ] && echo "<TD>"
470    [ -f "$p1" ] || continue
471    pngdiff cat "$diff" -f -c 0,1,0 "$p1" "$p1" || exit
472    pngdiff shrink "$thumb" -f $THUMB_OPTS -c 0,1,0 "$p2" "$p2" ||
473      exit
474    schps2pdf -T NEW -o "$pdf" "$ps" || exit
475    wrapped_png "$out" "$next" "$m"
476    empty=false
477    done < <(ordered_names)
478    if ! $empty; then
479     echo "<TD valign=\"middle\">"
480    commit_entry "$dir" $next
481    fi
482fi >>"$index"
483
484
485# --- Finish ------------------------------------------------------------------
486
487
488cat <<EOF >>"$index"
489</TABLE>
490<HR>
491`date -u '+%F %X'` UTC
492</BODY>
493</HTML>
494EOF
495
496mv "$index" "$index_final"
497

Archive Download this file

Branches:
master



interactive