Date:2010-09-07 22:03:45 (13 years 6 months ago)
Author:Werner Almesberger
Commit:002941d986bb900581d401d97d3a50a96854ab48
Message:Moved schhist2web and friends over to project eda-tools.

Files: scripts/Makefile (1 diff)
scripts/gitenealogy (1 diff)
scripts/gitsch2ps (1 diff)
scripts/normalizeschps (1 diff)
scripts/ppmdiff/Makefile (1 diff)
scripts/ppmdiff/ppmdiff.c (1 diff)
scripts/sanitize-profile (1 diff)
scripts/schhist2web (1 diff)
scripts/schps2pdf (1 diff)
scripts/schps2ppm (1 diff)

Change Details

scripts/Makefile
1SHELL = /bin/bash
2
3CACHE_DIRS = {ppm0,ppm1,ppm2,ps}
4DEST = werner@host:/home/httpd/almesberger/misc/ben/
5RSYNC = eval rsync -a --progress "--exclude "$(CACHE_DIRS)/
6
7.PHONY: all ben-wpan-schhist ben-wpan-schhist-upload
8.PHONY: xue-schhist xue-schhist-upload
9.PHONY: cntr-schhist cntr-schhist-upload
10
11# All the targets are for demo purposes pnly !
12
13all:
14        @echo "possible targets:" 2>&1
15        @echo " ben-wpan-schhist ben-wpan-schhist-upload" 2>&1
16        @echo " xue-schhist xue-schhist-upload" 2>&1
17        @echo " cntr-schhist cntr-schhist-upload" 2>&1
18        @exit 1
19
20ben-wpan-schhist:
21    SCHHIST_TITLE=ben-wpan/atrf \
22      SCHHIST_HOME_URL=http://projects.qi-hardware.com/index.php/p/ben-wpan/ \
23      SCHHIST_COMMIT_TEMPLATE='http://projects.qi-hardware.com/index.php/p/ben-wpan/source/commit/{}/' \
24      ./schhist2web atrf/wpan-atrf.sch
25
26ben-wpan-schhist-upload:
27     $(RSYNC) _out/* $(DEST)/demo/
28
29xue-schhist:
30    SCHHIST_TITLE=Xue \
31      SCHHIST_HOME_URL=http://projects.qi-hardware.com/index.php/p/xue/ \
32      SCHHIST_COMMIT_TEMPLATE='http://projects.qi-hardware.com/index.php/p/xue/source/commit/{}/' \
33      ./schhist2web -S ../../xue kicad/xue-rnc/xue-rnc.sch _xue
34
35xue-schhist-upload:
36     $(RSYNC) _xue/* $(DEST)/demo2/
37
38cntr-schhist:
39    SCHHIST_TITLE=ben-wpan/cntr \
40      SCHHIST_HOME_URL=http://projects.qi-hardware.com/index.php/p/ben-wpan/ \
41      SCHHIST_COMMIT_TEMPLATE='http://projects.qi-hardware.com/index.php/p/ben-wpan/source/commit/{}/' \
42      ./schhist2web cntr/cntr.sch _cntr
43
44cntr-schhist-upload:
45    $(RSYNC) _cntr/* $(DEST)/demo3/
scripts/gitenealogy
1#!/bin/sh
2#
3# gitenealogy - Trace the ancestry of a file in git across renames
4#
5# Written 2010 by Werner Almesberger
6# Copyright 2010 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
15usage()
16{
17    cat <<EOF 2>&1
18usage: $0 repo-dir path
19
20  The file to trace must be at repo-dir/path
21EOF
22    exit 1
23}
24
25
26if [ -z "$2" -o ! -z "$3" ]; then
27    usage
28fi
29
30if [ ! -d "$1" -o ! -d "$1/.git" ]; then
31    echo "no git repository at $1" 1>&2
32    exit 1
33fi
34if [ ! -f "$1/$2" ]; then
35    echo "cannot find $2" 2>&1
36    exit 1
37fi
38
39cd "$1" || exit
40git log --follow --name-status -- "$2" |
41    awk '
42/^commit /{ if (c) print c, n; c = $2 }
43{ if (NF) n = $(NF) }
44END { if (c) print c, n; }'
scripts/gitsch2ps
1#!/bin/sh
2#
3# gitsch2ps - Generate PS files for KiCad schematics in git
4#
5# Written 2010 by Werner Almesberger
6# Copyright 2010 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
15usage()
16{
17    cat <<EOF 1>&2
18usage: $0 [options] top-dir top-schem [commit] outdir
19
20  -S sanitize the KiCad profile
21EOF
22    exit 1
23}
24
25
26sanitize=true
27while true; do
28    case "$1" in
29    -S) sanitize=`PATH="$PATH":\`dirname "$0"\` which sanitize-profile`
30    [ "$sanitize" = "${sanitize#/}" ] && sanitize=`pwd`/"$sanitize"
31    shift;;
32    -*)
33    usage;;
34    *)
35    break;;
36    esac
37done
38
39[ ! -z "$3" -a -z "$5" ] || usage
40dir="$1"
41schem="$2"
42sdir=`dirname "$schem"`
43if [ -z "$4" ]; then
44    commit=HEAD
45    outdir="$3"
46else
47    commit="$3"
48    outdir="$4"
49fi
50
51[ "$dir" != "${dir#/}" ] || dir=`pwd`/$dir
52
53[ "$commit" != HEAD -o -f "$dir/$schem" ] || usage
54[ -d "$dir/.git" ] || usage
55
56tmp="$dir/../_gitsch2ps"
57sch="$tmp/$sdir"
58
59rm -rf "$tmp"
60rm -rf "$outdir"
61
62git clone -s -n "$dir/.git" "$tmp" || exit
63( cd "$tmp" && git checkout -q "$commit"; ) || exit
64
65if [ ! -f "$tmp/$schem" ]; then
66    echo "$schem not found (checked out into $tmp)" 1>&2
67    exit 1
68fi
69
70(
71    cd "$sch" || exit
72    rm -f *.ps
73    $sanitize "$tmp/${schem%.sch}.pro" ||
74      exit
75    eeschema --plot "$tmp/$schem"
76) || exit
77
78mkdir -p "$outdir"
79mv "$sch"/*.ps "$outdir"
80
81rm -rf "$tmp"
scripts/normalizeschps
1#!/bin/sh
2#
3# normalizeschps - Normalize eeschema Postscript
4#
5# Written 2010 by Werner Almesberger
6# Copyright 2010 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
15usage()
16{
17    cat <<EOF 1>&2
18usage: $0 [options] [in.ps [out.ps]]
19
20  -w points Postscript line width (default: use the original)
21EOF
22    exit 1
23}
24
25
26width=
27while true; do
28    case "$1" in
29    -w) [ -z "$2" ] && usage
30    width="$2"
31    shift 2;;
32    -*)
33    usage;;
34    *)
35    break;;
36    esac
37done
38
39[ -z "$3" ] || usage
40in=${1:--}
41out=${2:-/dev/stdout}
42
43sed '
44  1c%!PS-Adobe-3.0\
45  currentdict /DidNormalize known not { \
46      '"`[ -z \"$width\" ] ||
47        echo \"/setlinewidth { $width 2 copy lt { exch } if pop \
48        setlinewidth } bind def \"`"' \
49      /rectfill { rectstroke } bind def \
50      /DidNormalize true def \
51  } if \
52  gsave
53  /%%DocumentMedia: A4.*/a-20 -10 translate
54  /%%DocumentMedia: A3.*/{s/A3/A4/;a-20 -10 translate 0.70 dup scale
55  }
56  /%%DocumentMedia: A2.*/{s/A2/A4/;a-18 -12 translate 0.49 dup scale
57  }
58  $agrestore' "$in" >"$out"
59
60# /%%Orientation: Landscape/d' "$n"
scripts/ppmdiff/Makefile
1CFLAGS=-Wall -g
2
3ppmdiff:
scripts/ppmdiff/ppmdiff.c
1/*
2 * ppmdiff.c - Mark differences in two PPM files
3 *
4 * Written 2010 by Werner Almesberger
5 * Copyright 2010 Werner Almesberger
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 */
12
13
14#include <stdint.h>
15#include <stdlib.h>
16#include <stdio.h>
17#include <unistd.h>
18#include <string.h>
19
20
21static uint8_t a_only[3] = { 255, 0, 0 };
22static uint8_t b_only[3] = { 0, 255, 0 };
23static uint8_t both[3] = { 220, 220, 220 };
24static uint8_t frame[3] = { 0, 0, 255 };
25static uint8_t frame_fill[3] = { 255, 255, 200 };
26static int frame_dist = 40;
27static int frame_width = 2;
28
29
30static uint8_t *load_ppm(const char *name, int *x, int *y)
31{
32    FILE *file;
33    char line[100];
34    int this_x, this_y, depth;
35    int n;
36    uint8_t *img;
37
38    file = fopen(name, "r");
39    if (!file) {
40        perror(name);
41        exit(1);
42    }
43    if (!fgets(line, sizeof(line), file)) {
44        fprintf(stderr, "can't read file type\n");
45        exit(1);
46    }
47    if (strcmp(line, "P6\n")) {
48        fprintf(stderr, "file type must be P6, not %s", line);
49        exit(1);
50    }
51    if (!fgets(line, sizeof(line), file)) {
52        fprintf(stderr, "can't read resolution\n");
53        exit(1);
54    }
55    if (sscanf(line, "%d %d", &this_x, &this_y) != 2) {
56        fprintf(stderr, "can't parse resolution: %s", line);
57        exit(1);
58    }
59    if (*x || *y) {
60        if (*x != this_x || *y != this_y) {
61            fprintf(stderr,
62                "resolution changed from %dx%d to %dx%d\n",
63                *x, *y, this_x, this_y);
64            exit(1);
65        }
66    } else {
67        *x = this_x;
68        *y = this_y;
69    }
70    if (!fgets(line, sizeof(line), file)) {
71        fprintf(stderr, "can't read depth\n");
72        exit(1);
73    }
74    if (sscanf(line, "%d", &depth) != 1) {
75        fprintf(stderr, "can't parse depth: %s", line);
76        exit(1);
77    }
78    if (depth != 255) {
79        fprintf(stderr, "depth must be 255, not %d\n", depth);
80        exit(1);
81    }
82    n = *x**y*3;
83    img = malloc(n);
84    if (!img) {
85        perror("malloc");
86        exit(1);
87    }
88    if (fread(img, 1, n, file) != n) {
89        fprintf(stderr, "can't read %d bytes\n", n);
90        exit(1);
91    }
92    fclose(file);
93    return img;
94}
95
96
97static struct area {
98    int x0, y0, x1, y1;
99    struct area *next;
100} *areas = NULL;
101
102
103static void add_area(struct area **root, int x0, int y0, int x1, int y1)
104{
105    while (*root) {
106        struct area *area = *root;
107
108        if (area->x0 > x1 || area->y0 > y1 ||
109            area->x1 < x0 || area->y1 < y0) {
110            root = &(*root)->next;
111            continue;
112        }
113        x0 = x0 < area->x0 ? x0 : area->x0;
114        y0 = y0 < area->y0 ? y0 : area->y0;
115        x1 = x1 > area->x1 ? x1 : area->x1;
116        y1 = y1 > area->y1 ? y1 : area->y1;
117        *root = area->next;
118        free(area);
119        add_area(&areas, x0, y0, x1, y1);
120        return;
121    }
122    *root = malloc(sizeof(**root));
123    if (!*root) {
124        perror("malloc");
125        exit(1);
126    }
127    (*root)->x0 = x0;
128    (*root)->y0 = y0;
129    (*root)->x1 = x1;
130    (*root)->y1 = y1;
131    (*root)->next = NULL;
132}
133
134
135static void change(int x, int y)
136{
137    add_area(&areas,
138        x-frame_dist, y-frame_dist, x+frame_dist, y+frame_dist);
139}
140
141
142static void set_pixel(uint8_t *p, const uint8_t *color, const uint8_t *value)
143{
144    double f;
145    int i;
146
147    f = (255-(value[0] | value[1] | value[2]))/255.0;
148    for (i = 0; i != 3; i++)
149        p[i] = 255-(255-color[i])*f;
150}
151
152
153static uint8_t *diff(const uint8_t *a, const uint8_t *b, int xres, int yres,
154    int mark_areas)
155{
156    uint8_t *res, *p;
157    int x, y;
158    unsigned val_a, val_b;
159
160    res = p = malloc(xres*yres*3);
161    if (!res) {
162        perror("malloc");
163        exit(1);
164    }
165    for (y = 0; y != yres; y++)
166        for (x = 0; x != xres; x++) {
167            val_a = a[0]+a[1]+a[2];
168            val_b = b[0]+b[1]+b[2];
169            if (val_a == val_b) {
170                set_pixel(p, both, b);
171            } else if (val_a < val_b) {
172                set_pixel(p, a_only, a);
173                if (mark_areas)
174                    change(x, y);
175            } else if (val_a > val_b) {
176                set_pixel(p, b_only, b);
177                if (mark_areas)
178                    change(x, y);
179            } else {
180                abort(); /* no longer used */
181                memset(p, 255, 3);
182// memcpy(p, "\0\0\xff", 3);
183            }
184            a += 3;
185            b += 3;
186            p += 3;
187        }
188    return res;
189}
190
191
192static void shadow_diff(const uint8_t *a, const uint8_t *b, int xres, int yres)
193{
194    int x, y;
195
196    for (y = 0; y != yres; y++)
197        for (x = 0; x != xres; x++) {
198            if (memcmp(a, b, 3))
199                change(x, y);
200            a += 3;
201            b += 3;
202        }
203}
204
205
206static void point(uint8_t *img, int x, int y, int xres, int yres)
207{
208    uint8_t *p;
209
210    if (x < 0 || y < 0 || x >= xres || y >= yres)
211        return;
212    p = img+(y*xres+x)*3;
213    if ((p[0] & p[1] & p[2]) != 255)
214        return;
215    memcpy(p, frame, 3);
216}
217
218
219static void hline(uint8_t *img, int x0, int x1, int y, int xres, int yres)
220{
221    int x;
222
223    for (x = x0; x <= x1; x++)
224        point(img, x, y, xres, yres);
225}
226
227
228static void vline(uint8_t *img, int y0, int y1, int x, int xres, int yres)
229{
230    int y;
231
232    for (y = y0; y <= y1; y++)
233        point(img, x, y, xres, yres);
234}
235
236
237static void fill(uint8_t *img, int x0, int y0, int x1, int y1,
238   int xres, int yres)
239{
240    int x, y;
241    uint8_t *p;
242
243    for (y = y0; y <= y1; y++) {
244        if (y < 0 || y >= yres)
245            continue;
246        p = img+(xres*y+x0)*3;
247        for (x = x0; x <= x1; x++) {
248            if (x >= 0 && x < xres && (p[0] & p[1] & p[2]) == 255)
249                memcpy(p, frame_fill, 3);
250            p += 3;
251        }
252    }
253}
254
255
256static void mark_areas(uint8_t *img, int x, int y)
257{
258    const struct area *area;
259    int r1 = 0, r2 = 0, i;
260
261    if (frame_width) {
262        r1 = (frame_width-1)/2;
263        r2 = (frame_width-1)-r1;
264    }
265    for (area = areas; area; area = area->next) {
266        if (frame_width)
267            for (i = -r1; i <= r2; i++) {
268                hline(img, area->x0-r1, area->x1+r2, area->y0+i,
269                    x, y);
270                hline(img, area->x0-r1, area->x1+r2, area->y1+i,
271                    x, y);
272                vline(img, area->y0+r1, area->y1-r2, area->x0+i,
273                    x, y);
274                vline(img, area->y0+r1, area->y1-r2, area->x1+i,
275                    x, y);
276            }
277        fill(img,
278            area->x0+r1, area->y0+r1, area->x1-r2, area->y1-r2, x, y);
279    }
280}
281
282
283static void usage(const char *name)
284{
285    fprintf(stderr,
286"usage: %s [-f] [-a color] [-b color] [-c color] [-d pixels]\n"
287"%6s %*s [-m color] [-n color] [-w pixels] file_a.ppm file_b.ppm\n"
288"%6s %*s [file_a'.ppm file_b'.ppm] [out.ppm]\n\n"
289" file_a.ppm and file_b.ppm are two input images\n"
290" file_a'.ppm and file_b'.ppm if present, are searched for changes as well\n"
291" out.ppm output file (default: standard output)\n\n"
292" -f generate output (and return success) even if there is no change\n"
293" -a color color of items only in image A\n"
294" -b color color of items only in image B\n"
295" -c color color of items in both images\n"
296" -d pixels distance between change and marker box. 0 disables markers.\n"
297" -m color color of the frame of the marker box.\n"
298" -n color color of the background of the marker box\n"
299" -w pixels width of the frame of the marker box. 0 disables frames.\n\n"
300" color is specified as R,B,G with each component as a floating-point\n"
301" value from 0 to 1. E.g., 1,1,1 is white.\n\n"
302" The images are expected to have dark colors on a perfectly white\n"
303" background.\n"
304        , name, "", (int) strlen(name), "", "", (int) strlen(name), "");
305    exit(1);
306}
307
308
309static void parse_color(uint8_t *c, const char *s, const char *name)
310{
311    float r, g, b;
312
313    if (sscanf(s, "%f,%f,%f", &r, &g, &b) != 3)
314        usage(name);
315    c[0] = 255*r;
316    c[1] = 255*g;
317    c[2] = 255*b;
318}
319
320
321int main(int argc, char *const *argv)
322{
323    int force = 0;
324    int x = 0, y = 0;
325    uint8_t *old, *new, *d, *a, *b;
326    char *shadow_old = NULL, *shadow_new = NULL, *out_name = NULL;
327    FILE *out;
328    char *end;
329    int c;
330
331    while ((c = getopt(argc, argv, "a:b:c:d:fm:n:w:")) != EOF)
332        switch (c) {
333        case 'a':
334            parse_color(a_only, optarg, *argv);
335            break;
336        case 'b':
337            parse_color(b_only, optarg, *argv);
338            break;
339        case 'c':
340            parse_color(both, optarg, *argv);
341            break;
342        case 'd':
343            frame_dist = strtoul(optarg, &end, 0);
344            if (*end)
345                usage(*argv);
346            break;
347        case 'f':
348            force = 1;
349            break;
350        case 'm':
351            parse_color(frame, optarg, *argv);
352            break;
353        case 'n':
354            parse_color(frame_fill, optarg, *argv);
355            break;
356        case 'w':
357            frame_width = strtoul(optarg, &end, 0);
358            if (*end)
359                usage(*argv);
360            break;
361        default:
362            usage(*argv);
363        }
364    switch (argc-optind) {
365    case 2:
366        break;
367    case 3:
368        out_name = argv[optind+2];
369        break;
370    case 5:
371        out_name = argv[optind+4];
372        /* fall through */
373    case 4:
374        shadow_old = argv[optind+2];
375        shadow_new = argv[optind+3];
376        break;
377    default:
378        usage(*argv);
379    }
380
381    old = load_ppm(argv[optind], &x, &y);
382    new = load_ppm(argv[optind+1], &x, &y);
383    if (shadow_old) {
384        a = load_ppm(shadow_old, &x, &y);
385        b = load_ppm(shadow_new, &x, &y);
386        if (!force && !memcmp(a, b, x*y*3))
387            return 1;
388        shadow_diff(a, b, x, y);
389    }
390    if (!force && !areas && !memcmp(old, new, x*y*3))
391        return 1;
392    d = diff(old, new, x, y, !shadow_old);
393    if (frame_dist)
394        mark_areas(d, x, y);
395
396    if (out_name) {
397        out = fopen(out_name, "w");
398        if (!out) {
399            perror(out_name);
400            exit(1);
401        }
402    } else {
403        out = stdout;
404    }
405    fprintf(out, "P6\n%d %d\n255\n", x, y);
406    fwrite(d, 1, x*y*3, out);
407    if (fclose(out) == EOF) {
408        perror("fclose");
409        exit(1);
410    }
411    return 0;
412}
scripts/sanitize-profile
1#!/usr/bin/perl
2#
3# sanitize-profile - Remove items from a KiCad profile that may cause an upset
4#
5# Written 2010 by Werner Almesberger
6# Copyright 2010 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
15@LIBS = ("/usr/share/kicad/library", "/usr/local/share/kicad/library");
16
17
18sub rewrite
19{
20    local ($s) = @_;
21
22    return $s if $section ne "eeschema/libraries";
23    return $s unless /^LibName(\d+)=(.*)\s*$/;
24    my $lib = $2;
25    if ($1 == $in_lib) {
26    $in_lib++;
27    } else {
28    print STDERR "LibName$1 when expecting $next_lib. Renumbering.";
29    $in_lib = $1+1;
30    }
31    $out_lib++;
32    my $var = "LibName$out_lib";
33    if ($lib =~ /\//) {
34    return "$var=$lib\n" if -r "$lib.lib";
35    }
36    for (".", $libdir, @LIBS) {
37    return "$var=$lib\n" if -r "$_/$lib.lib";
38    }
39    print STDERR "cannot find $lib\n";
40    $out_lib--;
41    return undef;
42}
43
44
45sub usage
46{
47    print STDERR "usage: $0 file.pro [outfile]\n";
48    exit(1);
49}
50
51
52($file, $out) = @ARGV;
53&usage if $#ARGV > 1;
54
55($dir = $file) =~ s|.*/||;
56$dir = "." if $dir eq "";
57
58open(FILE, $file) || die "$file: $!";
59while (<FILE>) {
60    if (/^\[(\S+)\]/) {
61    $section = $1;
62    if ($section eq "eeschema/libraries") {
63        $in_lib = 1;
64        $out_lib = 0;
65    }
66    }
67    if ($section eq "eeschema") {
68    $libdir = $2 if /^LibDir=(.*)\s*$/;
69    }
70    $s = &rewrite($_);
71    push(@f, $s) if defined $s;
72}
73close FILE;
74
75if (!defined $out) {
76    rename($file, "$file.bak");
77    $out= $file;
78}
79
80open(FILE, ">$out") || die "$out: $!";
81print FILE join("", @f) || die "$out: $!";
82close FILE || die "$out: $!";
scripts/schhist2web
1#!/bin/bash
2#
3# schhist2web - Web-browseable graphical revision history of schematics
4#
5# Written 2010 by Werner Almesberger
6# Copyright 2010 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"
20
21
22shrink()
23{
24    pnmscale -width 120 "$@" || exit
25}
26
27
28pngdiff()
29{
30    # pngdiff preproc outfile arg ...
31    pp="$1"
32    of="$2"
33    shift 2
34    if ! PATH=$PATH:`dirname $0`/ppmdiff ppmdiff "$@" "$out/_tmp"; then
35    rm -f "$out/_tmp"
36    return 1
37    fi
38    $pp "$out/_tmp" | pnmtopng >"$of"
39    rm "$out/_tmp"
40}
41
42
43symlink()
44{
45    local old=$1 new=$2
46    local src=`dirname "$new"`/$old
47
48    if [ -L "$src" ]; then
49    ln -sf "`readlink \"$src\"`" "$new"
50    else
51    ln -sf "$old" "$new"
52    fi
53}
54
55
56commit_entry()
57{
58    # usage: commit_entry <base-dir> <commit>
59    # note: the repository's base in $dir must be provided by the caller
60
61    local dir=$1 next=$2
62
63    cat <<EOF
64<TABLE bgcolor="$SEP_COLOR" cellspacing=0 width="100%"><TR><TD></TABLE>
65EOF
66    echo "<PRE>"
67    ( cd "$dir" && git show \
68        --pretty=format:"%aN <%aE>%n %ad, %ar%n%n %s" \
69    --quiet $next; ) |
70      sed 's/&/&amp;/g;s/</\&lt;/g;s/>/\&gt;/g' |
71      if [ -z "$SCHHIST_COMMIT_TEMPLATE" ]; then
72    cat
73      else
74    url=`echo "$SCHHIST_COMMIT_TEMPLATE" | sed "s/{}/$next/g"`
75    sed "1s|^|<A href=\"$url\"><B>\&gt;\&gt;\&gt;</B></a> |"
76      fi
77    echo "</PRE>"
78}
79
80
81wrapped_png()
82{
83    local dir=$1 commit=$2 file=$3
84
85    mkdir -p "$dir/$commit/html"
86    echo "<A href=\"$commit/html/$file.html\"><IMG src=\"$commit/thumb/$file.png\"></A>"
87    cat <<EOF >"$dir/$commit/html/$file.html"
88<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
89<HTML>
90<TITLE>$file</TITLE>
91<BODY>
92<A href="../pdf/$file.pdf"><IMG src="../diff/$file.png"></A>
93</BODY>
94EOF
95}
96
97
98usage()
99{
100    cat <<EOF 2>&1
101usage: $0 [-c cache-dir] [-n] [-S] [top-dir] [top-schem] [out-dir]
102
103  top-dir top-level directory of the git archive (default: locate it)
104  top-schem root sheet of the schematics (default: locate it in top-dir)
105  out-dir output directory (default: $OUTDIR)
106  -c cache-dir cache directory (default: same as out-dir)
107  -n don't use previous cache content (rebuild the cache)
108  -S sanitize KiCad profile
109EOF
110    exit 1
111}
112
113
114# --- Parse command-line options ----------------------------------------------
115
116
117no_cache=false
118sanitize=
119
120while true; do
121    case "$1" in
122    -n) no_cache=true
123    shift;;
124    -c) [ -z "$1" ] && usage
125    cache="$1"
126    shift 2;;
127    -S) sanitize=-S
128    shift;;
129    -*) usage;;
130    *) break;;
131    esac
132done
133
134
135# --- Interpret the command-line arguments ------------------------------------
136
137
138if [ ! -z "$1" -a -d "$1/.git" ]; then
139    dir="$1"
140    shift
141else
142    dir=.
143    while [ ! -d $dir/.git ]; do
144    if [ $dir -ef $dir/.. ]; then
145        echo "no .git/ directory found in hierarchy" 1>&2
146        exit 1
147    fi
148    dir=$dir/..
149    done
150    echo "found top-dir: $dir" 1>&2
151fi
152
153if [ ! -z "$1" -a -f "$dir/$1" -a \
154  -f "$dir/${1%.sch}.pro" ]; then
155    sch="$1"
156    shift
157else
158    for n in "$dir"/*.sch; do
159    [ -f "${n%.sch}.pro" ] || continue
160    if [ ! -z "$sch" ]; then
161        echo "multiple choices for top-level .sch file" 1>&2
162        exit 1
163    fi
164    sch="$n"
165    done
166    if [ -z "$sch" -o "$sch" = "$dir/*.sch" ]; then
167    echo "no candidate for top-level .sch file found" 1>&2
168    exit 1
169    fi
170    echo "found root sheet: $sch" 1>&2
171fi
172
173if [ ! -z "$1" ] && [ ! -e "$1" ] || [ -d "$1" -a ! -d "$1"/.git ]; then
174    out="$1"
175    shift
176else
177    out=$OUTDIR
178fi
179[ -z "$cache" ] && cache="$out"
180
181[ -z "$1" ] || usage
182
183
184# --- Set up some variables and the directories for cache and output ----------
185
186
187PATH=`dirname "$0"`:"$PATH"
188first=`gitenealogy "$dir" "$sch" | sed '$s/ .*//p;d'`
189schname=`gitenealogy "$dir" "$sch" | sed '$s/^.* //p;d'`
190
191rm -rf "$out/*/"{diff,thumb,html,pdf} "$out/names"
192$no_cache && rm -rf "$cache"
193mkdir -p "$out/names"
194mkdir -p "$cache"
195
196ppmmake '#e0e0e0' 5 30 | pnmtopng >"$out"/unchanged.png
197
198
199# --- Generate/update the cache -----------------------------------------------
200
201
202head=
203for n in $first `cd "$dir" && git rev-list --reverse $first..HEAD`; do
204    ( cd "$dir" && git show --pretty=format:'' --name-only $n; ) |
205      egrep -q '\.sch$|\.pro$|\.lib$' || continue
206    echo Processing $n
207    new=`gitenealogy "$dir" "$sch" | sed "/^$n /s///p;d"`
208    if [ ! -z "$new" ]; then
209    echo Name change $schname to $new 1>&2
210    schname="$new"
211    fi
212    tmp=`pwd`/_schhist2web
213    trap "rm -rf \"$cache/$n/ps\" \"$cache/$n/ppm0\" \"$cache/$n/ppm1\" \
214      \"$cache/$n/ppm2\" \"$tmp\"" 0
215    if [ ! -d "$cache/$n/ppm2" ]; then
216    rm -rf "$cache/$n/"{ps,ppm0,ppm1,ppm2}
217    mkdir -p "$cache/$n/"{ps,ppm0,ppm1,ppm2}
218    #
219    # potential optimization here: remember Postscript files from previous
220    # run (or their md5sum) and check if they have changed. If not, skip
221    # the ghostscript run and just put a symlink, replacing the less
222    # efficient optimization below.
223    #
224    gitsch2ps $sanitize "$dir" "$schname" $n "$tmp" || exit
225    for m in "$tmp"/*.ps; do
226        # Postscript, for making PDFs later
227        ps="$cache/$n/ps/`basename "$m"`"
228        normalizeschps "$m" "$ps" || exit
229
230        # Unadorned pixmap, for comparison
231        ppm="$cache/$n/ppm0/`basename "$m" .ps`.ppm"
232        schps2ppm -n "$ps" "$ppm" || exit
233
234        # Pixmap with thin lines, for the detail views
235        ppm="$cache/$n/ppm1/`basename "$m" .ps`.ppm"
236        normalizeschps -w 120 "$m" | schps2ppm - "$ppm" || exit
237
238        # Pixmap with thick lines, for the thumbnails
239        ppm="$cache/$n/ppm2/`basename "$m" .ps`.ppm"
240        normalizeschps -w 500 "$m" | schps2ppm - "$ppm" || exit
241    done
242    rm -rf "$tmp"
243    fi
244    for m in "$cache/$n/ppm0/"*; do
245    [ "$m" = "$cache/$n/ppm0/*" ] && break
246    if [ ! -z "$head" ]; then
247        prev="$cache/$head/ppm0/${m##*/}"
248        if [ -r "$prev" ] && cmp -s "$prev" "$m"; then
249        for d in ppm0 ppm1 ppm2; do
250            symlink "../../$head/$d/${m##*/}" "$cache/$n/$d/${m##*/}"
251        done
252        m_ps=${m%.ppm}.ps
253        symlink "../../$head/ps/${m_ps##*/}" "$cache/$n/ps/${m_ps##*/}"
254        fi
255    fi
256    touch "$out/names/`basename \"$m\" .ppm`"
257    done
258    trap 0
259    head=$n
260done
261
262if [ -z "$head" ]; then
263    echo "no usable head found" 2>&1
264    exit 1
265fi
266
267
268# --- Title of the Web page and table header ----------------------------------
269
270
271index="$out/index.html"
272all=
273{
274    cat <<EOF
275<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
276<HTML>
277EOF
278    if [ ! -z "$SCHHIST_TITLE" ]; then
279    echo "<TITLE>$SCHHIST_TITLE</TITLE>"
280    fi
281    echo "<BODY>"
282    if [ ! -z "$SCHHIST_TITLE" ]; then
283    echo "<H1>"
284    [ -z "$SCHHIST_HOME_URL" ] || echo "<A href=\"$SCHHIST_HOME_URL\">"
285    echo "$SCHHIST_TITLE"
286    [ -z "$SCHHIST_HOME_URL" ] || echo "</A>"
287    echo "</H1>"
288    fi
289    cat <<EOF
290<TABLE bgcolor="$BG_COLOR" callpadding=1>
291<TR bgcolor="$FNAME_COLOR">
292EOF
293    while read m; do
294    ps="$cache/$head/ps/$m.ps"
295    if [ -r "$ps" ]; then
296        #
297        # Note: we read from variable ps_$head but we write to constant
298        # pdf_head. We can't use pdf_$head here, because that may just be a
299        # commit with a change and we thus generate a delta PDF below.
300        #
301        mkdir -p "$out/pdf_head"
302        schps2pdf -o "$out/pdf_head/$m.pdf" "$ps" || exit
303        all="$all \"$ps\""
304        echo "<TD><A href=\"pdf_head/$m.pdf\"><B>$m</B></A>"
305    else
306        echo "<TD><B>$m</B>"
307    fi
308    done < <(ls -1 "$out/names")
309    proj=`basename "$sch" .sch`
310    eval schps2pdf -t \""$proj-"\" -o \""$out/pdf_$proj.pdf"\" $all
311    echo "<TD><A href=\"pdf_$proj.pdf\">All sheets</A>"
312} >"$index"
313
314
315# --- Diff all the revisions, newest to oldest --------------------------------
316
317
318next="$head"
319for n in `cd "$dir" && git rev-list $first..HEAD~1` $first; do
320    [ -d "$cache/$n/ppm0" ] || continue
321    empty=true
322    s="<TR>"
323    mkdir -p "$out/$next/"{diff,thumb,html,pdf}
324    while read m; do
325    a0="$cache/$n/ppm0/$m.ppm"
326    a1="$cache/$n/ppm1/$m.ppm"
327    a2="$cache/$n/ppm2/$m.ppm"
328    aps="$cache/$n/ps/$m.ps"
329
330    b0="$cache/$next/ppm0/$m.ppm"
331    b1="$cache/$next/ppm1/$m.ppm"
332    b2="$cache/$next/ppm2/$m.ppm"
333    bps="$cache/$next/ps/$m.ps"
334
335    diff="$out/$next/diff/$m.png"
336    thumb="$out/$next/thumb/$m.png"
337    pdf="$out/$next/pdf/$m.pdf"
338
339    if [ -f "$a0" -a -f "$b0" ]; then
340        s="$s<TD align=\"center\" valign=\"middle\">"
341        if ! pngdiff cat "$diff" "$a1" "$b1" "$a0" "$b0"; then
342        s="$s<IMG src=\"unchanged.png\""
343        continue
344        fi
345        pngdiff shrink "$thumb" -f $THUMB_OPTS "$a2" "$b2" "$a0" "$b0" ||
346          exit
347        schps2pdf -T BEFORE -T AFTER -o "$pdf" "$aps" "$bps" || exit
348    elif [ -f "$a0" ]; then
349        s="$s<TD>"
350        pngdiff cat "$diff" -f -c 1,0,0 "$a1" "$a1" || exit
351        pngdiff shrink "$thumb" -f $THUMB_OPTS -c 1,0,0 "$a2" "$a2" ||
352          exit
353        schps2pdf -T DELETED -o "$pdf" "$aps" || exit
354    elif [ -f "$b0" ]; then
355        s="$s<TD>"
356        pngdiff cat "$diff" -f -c 0,1,0 "$b1" "$b1" || exit
357        pngdiff shrink "$thumb" -f $THUMB_OPTS -c 0,1,0 "$b2" "$b2" ||
358          exit
359        schps2pdf -T NEW -o "$pdf" "$bps" || exit
360    else
361        s="$s<TD>"
362        continue
363    fi
364    echo "$s"
365    s=
366        wrapped_png "$out" "$next" "$m"
367    empty=false
368    done < <(ls -1 "$out/names")
369    if ! $empty; then
370     echo "$s<TD valign=\"middle\">"
371    commit_entry "$dir" $next
372    fi
373    next=$n
374done >>"$index"
375
376
377# --- Add creation entries for all files in the first commit ------------------
378
379
380if [ -d "$cache/$next/ppm0" ]; then # could this ever be false ?
381    empty=true
382    echo "<TR>"
383    mkdir -p "$out/$next/"{diff,thumb,html,pdf}
384    while read m; do
385    p1="$cache/$next/ppm1/$m.ppm"
386    p2="$cache/$next/ppm2/$m.ppm"
387    ps="$cache/$next/ps/$m.ps"
388    diff="$out/$next/diff/$m.png"
389    thumb="$out/$next/thumb/$m.png"
390    pdf="$out/$next/pdf/$m.pdf"
391
392    echo "<TD>"
393    [ -f "$p1" ] || continue
394    pngdiff cat "$diff" -f -c 0,1,0 "$p1" "$p1" || exit
395    pngdiff shrink "$thumb" -f $THUMB_OPTS -c 0,1,0 "$p2" "$p2" ||
396      exit
397    schps2pdf -T NEW -o "$pdf" "$ps" || exit
398    wrapped_png "$out" "$next" "$m"
399    empty=false
400    done < <(ls -1 "$out/names")
401    if ! $empty; then
402     echo "<TD valign=\"middle\">"
403    commit_entry "$dir" $next
404    fi
405fi >>"$index"
406
407
408# --- Finish ------------------------------------------------------------------
409
410
411cat <<EOF >>"$index"
412</TABLE>
413<HR>
414`date -u '+%F %X'` UTC
415</BODY>
416</HTML>
417EOF
scripts/schps2pdf
1#!/bin/bash
2#
3# schps2pdf - Generate PDF files from Eeschema Postscript
4#
5# Written 2010 by Werner Almesberger
6# Copyright 2010 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
15usage()
16{
17    cat <<EOF 1>&2
18usage: $0 [options] [-o file.pdf] [file.ps ...]
19
20  -t prefix make a table of content. Remove prefix from file names.
21  -T title make a table of content using the specified title. Each
22             sheet needs one -T option.
23EOF
24    exit 1
25}
26
27
28toc=false
29out=-
30while true; do
31    case "$1" in
32    -o) [ -z "$2" ] && usage
33    out=$2
34    shift 2;;
35    -t) [ -z "$2" ] && usage
36    toc=true
37    prefix="$2"
38    shift 2;;
39    -T) [ -z "$2" ] && usage
40    toc=true
41    titles="$titles \"$2\""
42    shift 2;;
43    -*)
44    usage;;
45    *)
46    break;;
47    esac
48done
49
50in=${1:--}
51shift
52
53eval gs -q -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=\"$out\" -f \
54  `i=1; for n in "$in" "$@"; do \
55    i=\`expr $i + 1\`;
56    echo "<(sheet=\"\`basename \"$n\" .ps\`\";
57    set x $titles; eval t='\\${$i}';
58      $toc && echo '[ /Title ('\"\\${t:-\\${sheet#$prefix}}\"') /OUT pdfmark';
59    cat \"$n\";)"; done`
scripts/schps2ppm
1#!/bin/sh
2#
3# schps2ppm - Generate PPM files from normalized Eeschema Postscript
4#
5# Written 2010 by Werner Almesberger
6# Copyright 2010 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
15RES=1280x850
16
17
18usage()
19{
20    cat <<EOF 1>&2
21usage: $0 [options] [file.ps [file.ppm]]
22
23  -n disable alpha blending
24  -r XxY image resolution (default: $RES)
25EOF
26    exit 1
27}
28
29
30alpha=true
31while true; do
32    case "$1" in
33    -n) alpha=false
34    shift;;
35    -r) [ -z "$2" ] && usage
36    RES="$2"
37    shift 2;;
38    -) break;;
39    -*) usage;;
40    *) break;;
41    esac
42done
43
44[ ! -z "$3" ] && usage
45in=${1:--}
46out=${2:-/dev/stdout}
47
48X=`echo $RES | sed 's/x.*//'`
49Y=`echo $RES | sed 's/.*x//'`
50IRES=${Y}x$X
51res=`expr 72 \* $X / 800`
52
53cat "$in" |
54  gs -sDEVICE=ppmraw -sOutputFile=- -g$IRES -r$res \
55   `$alpha && echo '' -dTextAlphaBits=4 -dGraphicsAlphaBits=4` \
56   -q - |
57  pnmflip -r270 >"$out"

Archive Download the corresponding diff file



interactive