summaryrefslogtreecommitdiff
path: root/release/src/btools
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/btools')
-rw-r--r--release/src/btools/Makefile17
-rw-r--r--release/src/btools/fpkg.c330
-rwxr-xr-xrelease/src/btools/libfoo.pl408
-rwxr-xr-xrelease/src/btools/sizehistory.pl50
-rwxr-xr-xrelease/src/btools/uversion.pl75
5 files changed, 880 insertions, 0 deletions
diff --git a/release/src/btools/Makefile b/release/src/btools/Makefile
new file mode 100644
index 00000000..06ec47e3
--- /dev/null
+++ b/release/src/btools/Makefile
@@ -0,0 +1,17 @@
+
+CC = gcc
+CFLAGS = -O3 -Wall
+
+all: fpkg
+
+fpkg: fpkg.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+dcfg: dcfg.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+clean:
+ rm -f *.o fpkg dcfg
+
+%.o: %.c
+ $(CC) $(CFLAGS) -c $<
diff --git a/release/src/btools/fpkg.c b/release/src/btools/fpkg.c
new file mode 100644
index 00000000..8e0244e1
--- /dev/null
+++ b/release/src/btools/fpkg.c
@@ -0,0 +1,330 @@
+/*
+
+ fpkg - Package a firmware
+ Copyright (C) 2007 Jonathan Zarate
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ as published by the Free Software Foundation; either version 2
+ of the License, or (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <time.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <arpa/inet.h>
+
+
+#define ROUNDUP(n, a) ((n + (a - 1)) & ~(a - 1))
+
+#define TRX_MAGIC 0x30524448
+#define TRX_VERSION 1
+#define TRX_MAX_OFFSET 3
+#define TRX_MAX_LEN ((8 * 1024 * 1024) - ((256 + 128) * 1024)) // 8MB - (256K cfe + 128K cfg)
+
+typedef struct {
+ uint32_t magic;
+ uint32_t length;
+ uint32_t crc32;
+ uint32_t flag_version;
+ uint32_t offsets[TRX_MAX_OFFSET];
+} trx_t;
+
+typedef struct {
+ uint32_t crc32;
+ uint32_t magic;
+} moto_t;
+
+typedef struct {
+ uint8_t magic[4];
+ uint8_t extra1[4];
+ uint8_t date[3];
+ uint8_t version[3];
+ uint8_t u2nd[4];
+ uint8_t hardware;
+ uint8_t serial;
+ uint16_t flags;
+ uint8_t extra2[10];
+} cytan_t;
+
+uint32_t *crc_table = NULL;
+trx_t *trx = NULL;
+int trx_count = 0;
+int trx_final = 0;
+int trx_padding;
+time_t max_time = 0;
+
+int crc_init(void)
+{
+ uint32_t c;
+ int i, j;
+
+ if (crc_table == NULL) {
+ if ((crc_table = malloc(sizeof(uint32_t) * 256)) == NULL) return 0;
+ for (i = 255; i >= 0; --i) {
+ c = i;
+ for (j = 8; j > 0; --j) {
+ if (c & 1) c = (c >> 1) ^ 0xEDB88320L;
+ else c >>= 1;
+ }
+ crc_table[i] = c;
+ }
+ }
+ return 1;
+}
+
+void crc_done(void)
+{
+ free(crc_table);
+ crc_table = NULL;
+}
+
+uint32_t crc_calc(uint32_t crc, uint8_t *buf, int len)
+{
+ while (len-- > 0) {
+ crc = crc_table[(crc ^ *buf) & 0xFF] ^ (crc >> 8);
+ ++buf;
+ }
+ return crc;
+}
+
+void help(void)
+{
+ fprintf(stderr,
+ "fpkg - Package a firmware\n"
+ "Copyright (C) 2007 Jonathan Zarate\n"
+ "\n"
+ "Usage: -i <input> [-i <input>] {output}\n"
+ "Output:\n"
+ " TRX: -t <output file>\n"
+ " Linksys: -l <id>,<output file>\n"
+ " W54G WRT54G / WRT54GL\n"
+ " W54U WRTSL54GS\n"
+ " W54S WRTS54GS\n"
+ " W54s WRT54GS v4\n"
+ " Motorola: -m <id>,<output file>\n"
+ " 0x10577000 WE800\n"
+ " 0x10577040 WA840\n"
+ " 0x10577050 WR850\n"
+ "\n"
+ );
+ exit(1);
+}
+
+void load_image(const char *fname)
+{
+ struct stat st;
+ FILE *f;
+ long rsize;
+ char *p;
+
+ if (trx_final) {
+ fprintf(stderr, "Cannot load another image if an output has already been written.\n");
+ exit(1);
+ }
+ if (trx_count >= TRX_MAX_OFFSET) {
+ fprintf(stderr, "Too many input files.\n");
+ exit(1);
+ }
+
+ if (stat(fname, &st) != 0) {
+ perror(fname);
+ exit(1);
+ }
+ if (st.st_ctime > max_time) max_time = st.st_ctime;
+
+ rsize = ROUNDUP(st.st_size, 4);
+ if ((trx->length + rsize) > TRX_MAX_LEN) {
+ fprintf(stderr, "Total size is too big.\n");
+ exit(1);
+ }
+
+ p = (char *)trx + trx->length;
+ if ((f = fopen(fname, "r")) == NULL) {
+ perror(fname);
+ exit(1);
+ }
+ if (fread((char *)trx + trx->length, st.st_size, 1, f) != 1) {
+ perror(fname);
+ exit(1);
+ }
+ fclose(f);
+
+ trx->offsets[trx_count++] = trx->length;
+ trx->length += rsize;
+}
+
+void finalize_trx(void)
+{
+ uint32_t len;
+
+ if (trx_count == 0) {
+ fprintf(stderr, "No image was loaded.\n");
+ exit(1);
+ }
+
+ if (trx_final) return;
+ trx_final = 1;
+
+ len = trx->length;
+
+ trx->length = ROUNDUP(len, 4096);
+ trx->magic = TRX_MAGIC;
+ trx->flag_version = TRX_VERSION << 16;
+ trx->crc32 = crc_calc(0xFFFFFFFF, (void *)&trx->flag_version,
+ trx->length - (sizeof(*trx) - (sizeof(trx->flag_version) + sizeof(trx->offsets))));
+
+ trx_padding = trx->length - len;
+}
+
+void create_trx(const char *fname)
+{
+ FILE *f;
+
+ finalize_trx();
+
+ printf("Creating TRX: %s\n", fname);
+
+ if (((f = fopen(fname, "w")) == NULL) ||
+ (fwrite(trx, trx->length, 1, f) != 1)) {
+ perror(fname);
+ exit(1);
+ }
+ fclose(f);
+}
+
+void create_cytan(const char *fname, const char *pattern)
+{
+ FILE *f;
+ cytan_t h;
+ char padding[1024 - sizeof(h)];
+ struct tm *tm;
+
+ if (strlen(pattern) != 4) {
+ fprintf(stderr, "Linksys signature must be 4 characters. \"%s\" is invalid.\n", pattern);
+ exit(1);
+ }
+
+ finalize_trx();
+
+ printf("Creating Linksys %s: %s\n", pattern, fname);
+
+ memset(&h, 0, sizeof(h));
+ memcpy(h.magic, pattern, 4);
+ memcpy(h.u2nd, "U2ND", 4);
+ h.version[0] = 4; // 4.0.0 should be >= *_VERSION_FROM defined in code_pattern.h
+ h.flags = 0xFF;
+ tm = localtime(&max_time);
+ h.date[0] = tm->tm_year - 100;
+ h.date[1] = tm->tm_mon + 1;
+ h.date[2] = tm->tm_mday;
+
+ memset(padding, 0, sizeof(padding));
+
+ if (((f = fopen(fname, "w")) == NULL) ||
+ (fwrite(&h, sizeof(h), 1, f) != 1) ||
+ (fwrite(trx, trx->length, 1, f) != 1) ||
+ (fwrite(padding, sizeof(padding), 1, f) != 1)) {
+ perror(fname);
+ exit(1);
+ }
+ fclose(f);
+}
+
+void create_moto(const char *fname, const char *signature)
+{
+ FILE *f;
+ moto_t h;
+ char *p;
+
+ h.magic = strtoul(signature, &p, 0);
+ if (*p != 0) help();
+
+ finalize_trx();
+
+ printf("Creating Motorola 0x%08X: %s\n", h.magic, fname);
+
+ h.magic = htonl(h.magic);
+ h.crc32 = crc_calc(0xFFFFFFFF, (void *)&h.magic, sizeof(h.magic));
+ h.crc32 = htonl(crc_calc(h.crc32, (void *)trx, trx->length));
+
+ if (((f = fopen(fname, "w")) == NULL) ||
+ (fwrite(&h, sizeof(h), 1, f) != 1) ||
+ (fwrite(trx, trx->length, 1, f) != 1)) {
+ perror(fname);
+ exit(1);
+ }
+ fclose(f);
+}
+
+int main(int argc, char **argv)
+{
+ char s[256];
+ char *p;
+ int o;
+
+ printf("\n");
+
+ if ((!crc_init()) || ((trx = calloc(1, TRX_MAX_LEN)) == NULL)) {
+ fprintf(stderr, "Not enough memory\n");
+ exit(1);
+ }
+ trx->length = sizeof(*trx);
+
+ while ((o = getopt(argc, argv, "i:t:l:m:")) != -1) {
+ switch (o) {
+ case 'i':
+ load_image(optarg);
+ break;
+ case 't':
+ create_trx(optarg);
+ break;
+ case 'l':
+ case 'm':
+ if (strlen(optarg) >= sizeof(s)) help();
+ strcpy(s, optarg);
+ if ((p = strchr(s, ',')) == NULL) help();
+ *p = 0;
+ ++p;
+ if (o == 'l') create_cytan(p, s);
+ else create_moto(p, s);
+ break;
+ default:
+ help();
+ return 1;
+ }
+ }
+
+ if (trx_count == 0) {
+ help();
+ }
+ else {
+ finalize_trx();
+ printf("\nTRX Image:\n");
+ printf(" Total Size .... : %u (%.2fK)\n", trx->length, trx->length / 1024.0);
+ printf(" Images ...... : %u\n", trx->length - trx_padding);
+ printf(" Padding ..... : %d\n", trx_padding);
+ printf(" CRC-32 ........ : %8X\n", trx->crc32);
+ printf(" 128K Blocks ... : %u\n", (ROUNDUP(trx->length, (128 * 1024)) / (128 * 1024)));
+ printf(" Offsets:\n");
+ for (o = 0; o < TRX_MAX_OFFSET; ++o) {
+ printf(" %d: 0x%08X\n", o, trx->offsets[o]);
+ }
+ }
+ printf("\n");
+ return 0;
+}
diff --git a/release/src/btools/libfoo.pl b/release/src/btools/libfoo.pl
new file mode 100755
index 00000000..f7025acf
--- /dev/null
+++ b/release/src/btools/libfoo.pl
@@ -0,0 +1,408 @@
+#!/usr/bin/perl
+#
+# libfoo.pl
+# Copyright (C) 2006-2008 Jonathan Zarate
+#
+# - strip un-needed objects
+# - create xref of symbols used
+#
+
+sub error
+{
+ print STDERR "\n*** ERROR: " . (shift) . "\n\n";
+ exit 1;
+}
+
+sub basename
+{
+ my $fn = shift;
+ if ($fn =~ /([^\/]+)$/) {
+ return $1;
+ }
+ return $fn;
+}
+
+sub load
+{
+ my $fname = shift;
+
+ if ((-l $fname) ||
+ ($fname =~ /\/lib\/modules\/\d+\.\d+\.\d+/) ||
+ ($fname =~ /\.(asp|gif|png|svg|js|jsx|css|txt|pat|sh)$/)) {
+ return;
+ }
+
+ if (-d $fname) {
+ my $d;
+ if (opendir($d, $fname)) {
+ foreach (readdir($d)) {
+ if ($_ !~ /^\./) {
+ load($fname . "/" . $_);
+ }
+ }
+ closedir($d);
+ }
+ return;
+ }
+
+
+ my $f;
+ my $base;
+ my $ok;
+ my $s;
+
+ $base = basename($fname);
+ print LOG "\n\nreadelf $base:\n";
+
+ open($f, "mipsel-linux-readelf -WhsdD ${fname} 2>&1 |") || error("readelf - $!\n");
+
+ while (<$f>) {
+ print LOG;
+
+ if (/\s+Type:\s+(\w+)/) {
+ $elf_type{$base} = $1;
+ $ok = 1;
+ last;
+ }
+ }
+
+ if (!$ok) {
+ close($f);
+ return;
+ }
+
+ print "$elf_type{$base} $base", " " x 30, "\r";
+
+ push(@elfs, $base);
+
+ while (<$f>) {
+ print LOG;
+
+ if (/\(NEEDED\)\s+Shared library: \[(.+)\]/) {
+ push(@{$elf_lib{$base}}, $1);
+ }
+ elsif (/Symbol table for image:/) {
+ last;
+ }
+ }
+
+ while (<$f>) {
+ print LOG;
+
+ if (/\s+(WEAK|GLOBAL)\s+(?:DEFAULT|VISIBLE)\s+(\w+)\s+(\w+)/) {
+ $s = $3;
+ if ($2 eq 'UND') {
+ if ($1 eq 'GLOBAL') {
+ $elf_ext{$base}{$s} = 1;
+ }
+ else {
+ print LOG "*** not GLOBAL\n";
+ }
+ }
+ elsif ($2 eq 'ABS') {
+ }
+ elsif ($2 =~ /^\d+$/) {
+ $elf_exp{$base}{$s} = 1;
+ }
+ else {
+ print LOG "*** unknown type\n";
+ }
+ }
+ elsif (!/Num Buc:/) {
+ print LOG "*** strange line\n";
+ }
+ }
+
+ close($f);
+}
+
+sub fixDynDep
+{
+ my ($user, $dep) = @_;
+
+ if (!defined $elf_dyn{$user}{$dep}) {
+ push(@{$elf_lib{$user}}, $dep);
+ $elf_dyn{$user}{$dep} = 1;
+
+ print LOG "FixDynDep: $user = $dep\n";
+ }
+}
+
+sub fixDyn
+{
+ my $s;
+
+ foreach (@elfs) {
+ if (/^libipt_.+\.so$/) {
+ fixDynDep("iptables", $_);
+ }
+ elsif (/^CP\d+\.so$/) {
+ fixDynDep("smbd", $_);
+ }
+ }
+
+ fixDynDep("l2tpd", "cmd.so");
+ fixDynDep("l2tpd", "sync-pppd.so");
+
+# fixDynDep("libbcm.so", "libshared.so");
+# fixDynDep("libbcm.so", "libc.so.0");
+}
+
+sub usersOf
+{
+ my $name = shift;
+ my $sym = shift;
+ my @x;
+ my $e;
+ my $l;
+
+ @x = ();
+ foreach $e (@elfs) {
+ foreach $l (@{$elf_lib{$e}}) {
+ if ($l eq $name) {
+ if ((!defined $sym) || (defined $elf_ext{$e}{$sym})) {
+ push(@x, $e);
+ }
+ last;
+ }
+ }
+ }
+ return @x;
+}
+
+sub resolve
+{
+ my $name = shift;
+ my $sym = shift;
+ my $l;
+
+ foreach $l (@{$elf_lib{$name}}) {
+# print "\n$l $sym ", $elf_exp{$l}{$sym}, "\n";
+ return $l if (defined $elf_exp{$l}{$sym});
+ }
+ return "*** unresolved ***";
+}
+
+sub fillGaps
+{
+ my $name;
+ my $sym;
+ my @users;
+ my $u;
+ my $t;
+ my $found;
+
+# print "Resolving implicit links...\n";
+
+ foreach $name (@elfs) {
+ foreach $sym (keys %{$elf_ext{$name}}) {
+ $found = 0;
+ if (resolve($name, $sym) eq "*** unresolved ***") {
+ @users = usersOf($name);
+ foreach $u (@users) {
+ # if exported by $u
+ if (defined $elf_exp{$u}{$sym}) {
+ fixDynDep($name, $u);
+ $found = 1;
+ }
+ # if exported by shared libs of $u
+ if (($t = resolve($u, $sym)) ne "*** unresolved ***") {
+ fixDynDep($name, $t);
+ $found = 1;
+ }
+ }
+
+ if ($found == 0) {
+ print "Unable to resolve $sym used by $name\n", @users;
+ exit 1;
+ }
+ }
+ }
+ }
+}
+
+sub tab
+{
+ my $current = shift;
+ my $target = shift;
+ my $s = "";
+ my $n;
+
+ while (1) {
+ $n = $current + (4 - ($current % 4));
+ last if ($n > $target);
+ $s = $s . "\t";
+ $current = $n;
+ }
+ while ($current < $target) {
+ $s = $s . " ";
+ $current++;
+ }
+ return $s;
+}
+
+sub genXref
+{
+ my $f;
+ my $fname;
+ my $s;
+ my @u;
+
+# print "Generating Xref Report...\n";
+
+ open($f, ">libfoo_xref.txt");
+ foreach $fname (sort keys %elf_type) {
+ print $f "$fname:\n";
+
+ if (scalar(@{$elf_lib{$fname}}) > 0) {
+ print $f "Dependency:\n";
+ foreach $s (sort @{$elf_lib{$fname}}) {
+ print $f "\t$s", defined $elf_dyn{$fname}{$s} ? " (dyn)\n" : "\n";
+ }
+ }
+
+ if (scalar(keys %{$elf_exp{$fname}}) > 0) {
+ print $f "Export:\n";
+ foreach $s (sort keys %{$elf_exp{$fname}}) {
+ @u = usersOf($fname, $s);
+ if (scalar(@u) > 0) {
+ print $f "\t$s", tab(length($s) + 4, 40), " > ", join(",", @u), "\n";
+ }
+ else {
+ print $f "\t$s\n";
+ }
+ }
+ }
+
+ if (scalar(keys %{$elf_ext{$fname}}) > 0) {
+ print $f "External:\n";
+ foreach $s (sort keys %{$elf_ext{$fname}}) {
+ print $f "\t$s", tab(length($s) + 4, 40), " < ", resolve($fname, $s), "\n";
+ }
+ }
+
+ print $f "\n";
+ }
+ close($f);
+}
+
+
+sub genSO
+{
+ my ($so, $arc, $opt) = @_;
+ my $name = basename($so);
+ my $sym;
+ my $fn;
+ my $inuse;
+ my @used;
+ my @unused;
+ my $cmd;
+ my $before, $after;
+
+ if (!-f $so) {
+ print "$name: not found, skipping...\n";
+ return 0;
+ }
+
+ foreach $sym (sort keys %{$elf_exp{$name}}) {
+ if (scalar(usersOf($name, $sym)) > 0) {
+ push(@used, $sym);
+ }
+ else {
+ push(@unused, $sym);
+ }
+ }
+
+# print "\n$name: Attempting to link ", scalar(@used), " and remove ", scalar(@unused), " objects...\n";
+
+ print LOG "\n\n${base}\n";
+
+ $cmd = "mipsel-uclibc-ld -shared -s -z combreloc --warn-common --fatal-warnings ${opt} -soname ${name} -o ${so}";
+ foreach (@{$elf_lib{$name}}) {
+ if ((!$elf_dyn{$name}{$_}) && (/^lib(.+)\.so/)) {
+ $cmd .= " -l$1";
+ }
+ else {
+# print LOG "Not marking for linkage: $_\n";
+ }
+ }
+# print "$cmd -u... ${arc}\n";
+ if (scalar(@used) == 0) {
+ print "\n\n\n$name: WARNING: Library is not used by anything.\n\n\n";
+ <>;
+# return 0;
+ }
+ $cmd .= " -u " . join(" -u ", @used) . " ". $arc;
+
+ print LOG "Command: $cmd\n";
+ print LOG "Used: ", join(",", @used), "\n";
+ print LOG "Unused: ", join(",", @unused), "\n";
+
+ $before = -s $so;
+
+ system($cmd);
+ if ($? != 0) {
+ error("ld returned $?");
+ }
+
+ $after = -s $so;
+
+ print "$name: Attempted to remove ", scalar(@unused), "/", scalar(@unused) + scalar(@used), " symbols. ";
+ printf "%.2fK - %.2fK = %.2fK\n", $before / 1024, $after / 1024, ($before - $after) / 1024;
+
+# print "\n$name: Attempting to link ", scalar(@used), " and remove ", scalar(@unused), " objects...\n";
+# printf "Before: %.2fK / After: %.2fK / Removed: %.2fK\n\n", $before / 1024, $after / 1024, ($before - $after) / 1024;
+
+ return ($before > $after)
+}
+
+
+##
+##
+##
+
+# print "\nlibfoo.pl - fooify shared libraries\n";
+# print "Copyright (C) 2006-2007 Jonathan Zarate\n\n";
+
+$root = $ENV{"TARGETDIR"};
+$uclibc = $ENV{"TOOLCHAIN"};
+$router = $ENV{"SRCBASE"} . "/router";
+
+if ((!-d $root) || (!-d $uclibc) || (!-d $router)) {
+ print "Missing or invalid environment variables\n";
+ exit(1);
+}
+
+#open(LOG, ">libfoo.debug");
+open(LOG, ">/dev/null");
+
+print "Loading...\r";
+load($root);
+print "Finished loading files.", " " x 30, "\r";
+
+fixDyn();
+fillGaps();
+
+genXref();
+
+
+genSO("${root}/lib/libc.so.0", "${uclibc}/lib/libc.a", "-init __uClibc_init ${uclibc}/lib/optinfo/interp.o");
+genSO("${root}/lib/libresolv.so.0", "${uclibc}/lib/libresolv.a");
+genSO("${root}/lib/libcrypt.so.0", "${uclibc}/lib/libcrypt.a");
+genSO("${root}/lib/libm.so.0", "${uclibc}/lib/libm.a");
+genSO("${root}/lib/libpthread.so.0", "${uclibc}/lib/libpthread.a");
+genSO("${root}/lib/libutil.so.0", "${uclibc}/lib/libutil.a");
+# genSO("${root}/lib/libdl.so.0", "${uclibc}/lib/libdl.a");
+# genSO("${root}/lib/libnsl.so.0", "${uclibc}/lib/libnsl.a");
+
+genSO("${root}/usr/lib/libcrypto.so", "${router}/openssl/libcrypto.a");
+genSO("${root}/usr/lib/libzebra.so", "${router}/zebra/lib/libzebra.a");
+# genSO("${root}/usr/lib/libtamba.so", "${router}/samba3/source/bin/libtamba.a");
+# genSO("${root}/usr/lib/libiptc.so", "${router}/iptables/libiptc/libiptc.a");
+# genSO("${root}/usr/lib/libshared.so", "${router}/shared/libshared.a");
+# genSO("${root}/usr/lib/libnvram.so", "${router}/nvram/libnvram.a");
+
+print "\n";
+
+close(LOG);
+exit(0);
diff --git a/release/src/btools/sizehistory.pl b/release/src/btools/sizehistory.pl
new file mode 100755
index 00000000..fe9e9525
--- /dev/null
+++ b/release/src/btools/sizehistory.pl
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+#
+# sizehistory
+# Copyright (C) 2006 Jonathan Zarate
+#
+
+if (($#ARGV < 0) || ($#ARGV > 1)) {
+ print "Usage: sizehistory <filename> [datafile]\n";
+ exit 1;
+}
+
+$fname = $ARGV[0];
+$dname = ($ARGV[1] || $fname) . ".size";
+
+print "\nSize history for $fname\n\n";
+@size = `mipsel-linux-size $fname`;
+foreach (@size) {
+ if (($text, $data, $bss, $total) = $_ =~ /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+/) {
+ $line = "$text\t$data\t$bss\t$total\t" . scalar localtime((stat($fname))[10]);
+ print "text\tdata\tbss\ttotal\ttime\n";
+
+ if (open(F, "<$dname")) {
+ @hist = <F>;
+ close(F);
+ if ($#hist >= 0) {
+ if ($#hist > 20) {
+ splice(@hist, 0, $#hist - 19);
+ }
+ print @hist;
+ if ($hist[$#hist] =~ /^(\d+)\t(\d+)\t(\d+)\t(\d+)\t/) {
+ if (($1 == $text) && ($2 == $data) && ($3 == $bss)) {
+ print "--- same size as last ---\n$line <= current\n";
+ exit 0;
+ }
+ printf "%d\t%d\t%d\t%d\t--- changes since above ---\n", ($text - $1), ($data - $2), ($bss - $3), ($total - $4);
+ }
+ }
+ }
+
+ print $line, "\n";
+
+ if ((localtime((stat($dname))[9]))[7] != (localtime())[7]) {
+ open(F, ">>$dname") || die "$dname: $!";
+ print F $line, "\n";
+ close(F);
+ }
+ exit 0;
+ }
+}
+exit 1;
diff --git a/release/src/btools/uversion.pl b/release/src/btools/uversion.pl
new file mode 100755
index 00000000..1e22b5d3
--- /dev/null
+++ b/release/src/btools/uversion.pl
@@ -0,0 +1,75 @@
+#!/usr/bin/perl
+#
+# uversion.pl
+# Copyright (C) 2006 Jonathan Zarate
+#
+# - update the build number for Tomato
+#
+
+use POSIX qw(strftime);
+
+sub error
+{
+ print "\nuversion error: $@\n";
+ exit(1);
+}
+
+sub help
+{
+ print "Usage: uversion --bump|--gen\n";
+ exit(1);
+}
+
+#
+#
+
+if ($#ARGV != 0) {
+ help();
+}
+
+$path = "router/shared";
+$major = 0;
+$minor = 0;
+$build = 0;
+
+open(F, "$path/tomato_version") || error("opening tomato_version: $!");
+$_ = <F>;
+if (!(($major, $minor, $build) = /^(\d+)\.(\d+)\.(\d+)$/)) {
+ error("Invalid version: '$_'");
+}
+close(F);
+
+
+if ($ARGV[0] eq "--bump") {
+ ++$build;
+ open(F, ">$path/tomato_version.~") || error("creating temp file: $!");
+ printf F "%d.%02d.%04d", $major, $minor, $build;
+ close(F);
+ rename("$path/tomato_version.~", "$path/tomato_version") || error("renaming: $!");
+ exit(0);
+}
+
+if ($ARGV[0] ne "--gen") {
+ help();
+}
+
+$time = strftime("%a, %d %b %Y %H:%M:%S %z", localtime());
+$minor = sprintf("%02d", $minor);
+$build = sprintf("%04d", $build);
+
+open(F, ">$path/tomato_version.h~") || error("creating temp file: $!");
+print F <<"END";
+#ifndef __TOMATO_VERSION_H__
+#define __TOMATO_VERSION_H__
+#define TOMATO_MAJOR "$major"
+#define TOMATO_MINOR "$minor"
+#define TOMATO_BUILD "$build"
+#define TOMATO_BUILDTIME "$time"
+#define TOMATO_VERSION "$major.$minor.$build"
+#endif
+END
+close(F);
+rename("$path/tomato_version.h~", "$path/tomato_version.h") || error("renaming: $!");
+
+print "Version: $major.$minor.$build ($time)\n";
+exit(0);