LIBUCW=$(o)/ucw/libucw.pc
ifdef CONFIG_UCW_UTILS
-UCW_UTILS=basecode b224 rotate-log urltool daemon-helper hex
-
-PROGS+=$(addprefix $(o)/ucw/,$(UCW_UTILS))
+include $(s)/ucw/utils/Makefile
endif
LIBUCW_MODS= \
$(o)/ucw/kmp-test: $(o)/ucw/kmp-test.o $(LIBUCW) $(LIBCHARSET)
$(o)/ucw/ipaccess-test: $(o)/ucw/ipaccess-test.o $(LIBUCW)
$(o)/ucw/trie-test: $(o)/ucw/trie-test.o $(LIBUCW)
-$(o)/ucw/basecode: $(o)/ucw/basecode.o $(LIBUCW)
TESTS+=$(addprefix $(o)/ucw/,regex.test unicode.test hash-test.test mempool.test stkstring.test \
slists.test kmp-test.test bbuf.test getopt.test ff-unicode.test eltpool.test \
ifdef CONFIG_UCW_SHELL_UTILS
include $(s)/ucw/shell/Makefile
endif
-
-ifdef CONFIG_UCW_UTILS
-$(o)/ucw/b224: $(o)/ucw/b224.o $(LIBUCW)
-$(o)/ucw/daemon-helper: $(o)/ucw/daemon-helper.o $(LIBUCW)
-$(o)/ucw/urltool: $(o)/ucw/urltool.o $(LIBUCW)
-$(o)/ucw/hex: $(o)/ucw/hex.o $(LIBUCW)
-
-TESTS+=$(o)/ucw/basecode.test
-$(o)/ucw/basecode.test: $(o)/ucw/basecode
-endif
+++ /dev/null
-/*
- * A Program For Manipulation With Base224 Encoded Files
- *
- * (c) 2002 Martin Mares <mj@ucw.cz>
- */
-
-#include "ucw/lib.h"
-#include "ucw/fastbuf.h"
-#include "ucw/base224.h"
-
-#include <stdio.h>
-
-int main(int argc, char **argv)
-{
- struct fastbuf *in = bfdopen_shared(0, 4096);
- struct fastbuf *out = bfdopen_shared(1, 4096);
- byte ib[BASE224_IN_CHUNK*10], ob[BASE224_OUT_CHUNK*10], *b;
- uns il, ol;
-
- if (argc != 2 || argv[1][0] != '-')
- goto usage;
-
- switch (argv[1][1])
- {
- case 'e': /* Plain encoding */
- while (il = bread(in, ib, sizeof(ib)))
- {
- ol = base224_encode(ob, ib, il);
- bwrite(out, ob, ol);
- }
- break;
- case 'E': /* Line block encoding */
- while (il = bread(in, ib, BASE224_IN_CHUNK*6))
- {
- ol = base224_encode(ob, ib, il);
- bputc(out, 'N');
- bwrite(out, ob, ol);
- bputc(out, '\n');
- }
- break;
- case 'd': /* Plain decoding */
- while (ol = bread(in, ob, sizeof(ob)))
- {
- il = base224_decode(ib, ob, ol);
- bwrite(out, ib, il);
- }
- break;
- case 'D': /* Line block decoding */
- while (b = bgets(in, ob, sizeof(ob)))
- {
- if (!ob[0])
- die("Invalid line syntax");
- il = base224_decode(ib, ob+1, b-ob-1);
- bwrite(out, ib, il);
- }
- break;
- default:
- usage:
- fputs("Usage: b224 (-e|-E|-d|-D)\n", stderr);
- return 1;
- }
-
- bclose(in);
- bclose(out);
- return 0;
-}
+++ /dev/null
-/*
- * UCW Library -- Line utility for encoding and decoding base64 & base224
- *
- * (c) 2008, Michal Vaner <vorner@ucw.cz>
- *
- * This software may be freely distributed and used according to the terms
- * of the GNU Lesser General Public License.
- */
-
-#include <string.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#include "ucw/lib.h"
-#include "ucw/base64.h"
-#include "ucw/base224.h"
-#include "ucw/fastbuf.h"
-#include "ucw/getopt.h"
-
-static struct option opts[] = {
- { "encode64", 0, 0, 'e' },
- { "decode64", 0, 0, 'd' },
- { "encode224", 0, 0, 'E' },
- { "decode224", 0, 0, 'D' },
- { "prefix", 1, 0, 'p' },
- { "blocks", 1, 0, 'b' },
- { 0, 0, 0, 0 }
-};
-
-static const struct {
- uns (*function)(byte *, const byte *, uns);
- uns in_block, out_block, num_blocks;
- uns add_prefix;
-} actions[] = {
- {
- base64_encode,
- BASE64_IN_CHUNK, BASE64_OUT_CHUNK, 20,
- 1
- },
- {
- base64_decode,
- BASE64_OUT_CHUNK, BASE64_IN_CHUNK, 20,
- 0
- },
- {
- base224_encode,
- BASE224_IN_CHUNK, BASE64_OUT_CHUNK, 6,
- 1
- },
- {
- base224_decode,
- BASE224_OUT_CHUNK, BASE224_IN_CHUNK, 6,
- 0
- }
-};
-
-int main(int argc, char **argv)
-{
- // Choose mode
- int mode = -1;
- char *prefix = NULL;
- uns blocks = 0;
- int opt;
- while ((opt = getopt_long(argc, argv, "edEDp:b:", opts, NULL)) >= 0)
- switch (opt)
- {
- case 'e': mode = 0; break;
- case 'd': mode = 1; break;
- case 'E': mode = 2; break;
- case 'D': mode = 3; break;
- case 'p': prefix = optarg; break;
- case 'b':
- {
- char *end;
- blocks = strtol(optarg, &end, 0);
- if ((blocks > 0) && !*end)
- break;
- }
- default: goto usage;
- }
-
- if (mode == -1)
- {
- usage:
- fprintf(stderr, "basecode mode [--prefix=prefix] [--blocks=number_of_blocks]\nMode is one of:\n\t--encode64 (-e)\n\t--decode64 (-d)\n\t--encode224 (-E)\n\t--decode224 (-D)\n");
- return 1;
- }
- if (!blocks)
- blocks = actions[mode].num_blocks;
-
- // Prepare buffers
- struct fastbuf *in = bfdopen_shared(0, 4096);
- struct fastbuf *out = bfdopen_shared(1, 4096);
- int has_offset = !actions[mode].add_prefix && prefix;
- uns offset = has_offset ? strlen(prefix) : 0;
- uns read_size = actions[mode].in_block * blocks + offset + has_offset;
- uns write_size = actions[mode].out_block * blocks;
- byte in_buff[read_size], out_buff[write_size];
- uns isize;
-
- // Recode it
- while (isize = bread(in, in_buff, read_size))
- {
- if (prefix)
- {
- if (actions[mode].add_prefix)
- bputs(out, prefix);
- else
- if ((isize < offset) || (in_buff[isize-1] != '\n')
- || (strncmp(prefix, in_buff, offset)))
- die("Invalid line syntax");
- }
- uns osize = actions[mode].function(out_buff, in_buff + offset, isize - offset - has_offset);
- bwrite(out, out_buff, osize);
- if (actions[mode].add_prefix && prefix)
- bputc(out, '\n');
- }
-
- bclose(in);
- bclose(out);
- return 0;
-}
+++ /dev/null
-/*
- * A Simple Wrapper for Starting and Stopping of Daemons
- *
- * (c) 2003 Martin Mares <mj@ucw.cz>
- *
- * It would seem that we are reinventing the wheel and the
- * start-stop-daemon command present in most Linux distributions
- * is just what we need, but the usual "does the process already
- * exist?" strategies fail in presence of multiple running daemons.
- *
- * Return codes:
- * 101 already running
- * 102 not running
- */
-
-#include "ucw/lib.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <getopt.h>
-#include <errno.h>
-#include <alloca.h>
-
-enum action {
- ACTION_NONE,
- ACTION_START,
- ACTION_STOP,
- ACTION_FORCE_STOP,
- ACTION_CHECK,
- ACTION_RELOAD
-};
-
-static int action;
-
-static struct option options[] = {
- { "pid-file", required_argument, NULL, 'p' },
- { "status-file", required_argument, NULL, 's' },
- { "start", no_argument, &action, ACTION_START },
- { "stop", no_argument, &action, ACTION_STOP },
- { "force-stop", no_argument, &action, ACTION_FORCE_STOP },
- { "check", no_argument, &action, ACTION_CHECK },
- { "reload", no_argument, &action, ACTION_RELOAD },
- { NULL, no_argument, NULL, 0 }
-};
-
-static void NONRET
-usage(void)
-{
- fputs("\n\
-Usage: daemon-helper --start <options> -- <daemon> <args>\n\
- or: daemon-helper --stop <options>\n\
- or: daemon-helper --force-stop <options>\n\
- or: daemon-helper --reload <options>\n\
- or: daemon-helper --check <options>\n\
-\n\
-Options:\n\
---pid-file <name> Name of PID file for this daemon (mandatory)\n\
---status-file <name> Status file used by the daemon (deleted just before starting)\n\
-", stderr);
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- int c, fd;
- char *pidfile = NULL;
- char *statfile = NULL;
- struct flock fl;
- char buf[64];
-
- while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
- switch (c)
- {
- case 0:
- break;
- case 'p':
- pidfile = optarg;
- break;
- case 's':
- statfile = optarg;
- break;
- default:
- usage();
- }
- if (!pidfile)
- usage();
-
- bzero(&fl, sizeof(fl));
- fl.l_type = F_WRLCK;
- fl.l_whence = SEEK_SET;
-
- switch (action)
- {
- case ACTION_START:
- if (optind >= argc)
- usage();
- fd = open(pidfile, O_RDWR | O_CREAT, 0666);
- if (fd < 0)
- die("Unable to create %s: %m", pidfile);
- if ((c = fcntl(fd, F_SETLK, &fl)) < 0)
- {
- if (errno == EAGAIN || errno == EACCES)
- return 101;
- else
- die("fcntl lock on %s failed: %m", pidfile);
- }
- c = sprintf(buf, "%d\n", getpid());
- if (write(fd, buf, c) != c)
- die("write on %s failed: %m", pidfile);
- if (ftruncate(fd, c) < 0)
- die("truncate on %s failed: %m", pidfile);
- if (statfile && unlink(statfile) < 0 && errno != ENOENT)
- die("unlink(%s) failed: %m", statfile);
- setsid();
- /* Disconnect from stdin and stdout, leave stderr to the daemon. */
- close(0);
- open("/dev/null", O_RDWR, 0);
- dup2(0, 1);
- argv += optind;
- argc -= optind;
- char **a = alloca(sizeof(char *) * (argc+1));
- memcpy(a, argv, sizeof(char *) * argc);
- a[argc] = NULL;
- execv(a[0], a);
- die("Cannot execute %s: %m", a[0]);
- case ACTION_STOP:
- case ACTION_FORCE_STOP:
- case ACTION_CHECK:
- case ACTION_RELOAD:
- if (optind < argc)
- usage();
- fd = open(pidfile, O_RDWR);
- if (fd < 0)
- {
- if (errno == ENOENT)
- return 102;
- else
- die("Unable to open %s: %m", pidfile);
- }
- if ((c = fcntl(fd, F_SETLK, &fl)) >= 0)
- {
- nopid:
- unlink(pidfile);
- return 102;
- }
- if (errno != EAGAIN && errno != EACCES)
- die("fcntl lock on %s failed: %m", pidfile);
- if ((c = read(fd, buf, sizeof(buf))) < 0)
- die("read on %s failed: %m", pidfile);
- if (!c)
- goto nopid;
- if (c >= (int) sizeof(buf) || sscanf(buf, "%d", &c) != 1)
- die("PID file syntax error");
- int sig = 0;
- if (action == ACTION_CHECK || action == ACTION_RELOAD)
- {
- if (action == ACTION_RELOAD)
- sig = SIGHUP;
- if (kill(c, sig) < 0 && errno == ESRCH)
- goto nopid;
- return 0;
- }
- sig = (action == ACTION_STOP) ? SIGTERM : SIGQUIT;
- if (kill(c, sig) < 0)
- {
- if (errno == ESRCH)
- goto nopid;
- die("Cannot kill process %d: %m", c);
- }
- if ((c = fcntl(fd, F_SETLKW, &fl)) < 0)
- die("Cannot lock %s: %m", pidfile);
- if (statfile)
- unlink(statfile);
- if (unlink(pidfile) < 0)
- die("Cannot unlink %s: %m", pidfile);
- return 0;
- default:
- usage();
- }
-}
+++ /dev/null
-/*
- * Hexadecimal dumper (CP/M style format)
- *
- * Original version (c) Eric S. Raymond <esr@snark.thyrsus.com>
- * Heavily modified by Martin Mares <mj@ucw.cz>
- *
- * Can be freely distributed and used under the terms of the GNU GPL.
- */
-
-#include "ucw/lib.h"
-#include "ucw/lfs.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <ctype.h>
-#include <string.h>
-#include <fcntl.h>
-#include <unistd.h>
-
-#define DEFWIDTH 16 /* Default # chars to show per line */
-#define MAXWIDTH 32 /* Maximum # of bytes per line */
-
-typedef int bool;
-#define TRUE 1
-#define FALSE 0
-
-static long linesize = DEFWIDTH; /* # of bytes to print per line */
-static bool cflag = FALSE; /* show printables as ASCII if true */
-static bool gflag = FALSE; /* suppress mid-page gutter if true */
-static ucw_off_t start = 0; /* file offset to start dumping at */
-static ucw_off_t length = 0; /* if nz, how many chars to dump */
-
-static void dumpfile(FILE *f)
- /* dump a single, specified file -- stdin if filename is NULL */
-{
- int ch = '\0'; /* current character */
- char ascii[MAXWIDTH+3]; /* printable ascii data */
- int i = 0; /* counter: # bytes processed */
- int ai = 0; /* index into ascii[] */
- ucw_off_t offset = start; /* byte offset of line in file */
- int hpos = 0; /* horizontal position counter */
- ucw_off_t fstart = start;
- ucw_off_t flength = length;
- char *specials = "\b\f\n\r\t";
- char *escapes = "bfnrt";
- char *cp;
-
- if (fstart && ucw_seek(fileno(f), fstart, SEEK_SET) >= 0)
- fstart = 0;
-
- do {
- ch = getc(f);
-
- if (ch != EOF)
- {
- if (length && flength-- <= 0)
- ch = EOF;
- }
-
- if (ch != EOF)
- {
- if (i++ % linesize == 0)
- {
- (void) printf("%04Lx ", (long long) offset);
- offset += linesize;
- hpos = 5;
- }
-
- /* output one space for the mid-page gutter */
- if (!gflag)
- if ((i - 1) % (linesize / 2) == 0)
- {
- (void) putchar(' ');
- hpos++;
- ascii[ai++] = ' ';
- }
-
- /* dump the indicated representation of a character */
- ascii[ai] = (isprint (ch) || ch == ' ') ? ch : '.';
-
- if (cflag && (isprint(ch) || ch == ' '))
- (void) printf("%c ", ch);
- else if (cflag && ch && (cp = strchr(specials, ch)))
- (void) printf("\\%c ", escapes[cp - specials]);
- else
- (void) printf("%02x ", ch);
-
- /* update counters and things */
- ai++;
- hpos += 3;
- }
-
- /* At end-of-line or EOF, show ASCII version of data. */
- if (i && (ch == EOF || (i % linesize == 0)))
- {
- if (!cflag)
- {
- while (hpos < linesize * 3 + 7)
- {
- hpos++;
- (void) putchar(' ');
- }
-
- ascii[ai] = '\0';
- (void) printf("%s", ascii);
- }
-
- if (ch != EOF || (i % linesize != 0))
- (void) putchar('\n');
- ai = 0; /* reset counters */
- }
- } while
- (ch != EOF);
-}
-
-static ucw_off_t getoffs(char *cp)
- /* fetch decimal or hex integer to be used as file start or offset */
-{
- ucw_off_t value = 0;
- char *hexdigits = "0123456789abcdefABCDEF";
-
-#if 0
- bool foundzero = FALSE;
- int base = 0;
-
- for (; *cp; cp++)
- if (*cp == '0')
- foundzero = TRUE;
- else if (isdigit(*cp))
- {
- base = 10;
- break;
- }
- else if (*cp = 'x' || *cp == 'X' || *cp == 'h' || *cp == 'H')
- {
- base = 16;
- cp++;
- break;
- }
- else
- return(-1L);
-
- if (base == 0)
- if (foundzero)
- base = 10;
- else
- return(-1L);
-
- if (base == 10)
- {
- for (; *cp; cp++)
- if (isdigit(*cp))
- value = value * 10 + (*cp - '0');
- else
- return(-1L);
- }
- else
-#endif
- {
- for (; *cp; cp++)
- if (strchr(hexdigits, *cp))
- value = value*16 + (strchr(hexdigits, tolower(*cp))-hexdigits);
- else
- return -1;
- }
-
- return(value);
-}
-
-int main(int argc, char **argv)
-{
- FILE *infile; /* file pointer input file */
- int dumpcount = 0; /* count of files dumped so far */
- char *cp;
- int fd;
-
- for (argv++, argc--; argc > 0; argv++, argc--)
- {
- char s = **argv;
-
- if (s == '-' || s == '+')
- {
- int c = *++*argv;
-
- switch (c)
- {
- case 'c': cflag = (s == '-'); continue;
- case 'g': gflag = (s == '-'); continue;
-
- case 's':
- if ((*argv)[1])
- (*argv)++;
- else
- argc--, argv++;
- if (s == '-' && argc >= 0)
- {
- if (cp = strchr(*argv, ','))
- *cp++ = '\0';
- if ((start = getoffs(*argv)) < 0)
- {
- (void) fputs("hex: start offset no good\n", stderr);
- exit(1);
- }
-
- if (cp)
- if ((length = getoffs(cp)) < 0)
- {
- (void) fputs("hex: length no good\n", stderr);
- exit(1);
- }
- }
- else
- start = length = 0L;
- continue;
-
- case '\0':
- infile = stdin;
- break;
-
- case 'w':
- if ((*argv)[1])
- (*argv)++;
- else
- argc--, argv++;
- if ((linesize = getoffs(*argv)) == -1L || linesize > MAXWIDTH)
- {
- (void) fputs("hex: line width no good\n", stderr);
- exit(1);
- }
- if (linesize % 2)
- gflag = TRUE;
- continue;
-
- default:
- (void) fprintf(stderr, "hex: no such option as %s\n", *argv);
- exit(1);
- }
- }
- else
- {
- fd = ucw_open(*argv, O_RDONLY, 0);
- if (fd < 0 || !(infile = fdopen(fd, "r")))
- {
- (void) fprintf(stderr, "hex: cannot open %s: %m\n", *argv);
- exit(1);
- }
- }
-
- if (dumpcount > 0 || argc > 1)
- if (infile == stdin)
- (void) printf("---- <Standard input> ----\n");
- else
- (void) printf("---- %s ----\n", *argv);
- dumpfile(infile);
- dumpcount++;
- if (infile != stdin)
- (void) fclose(infile);
- }
-
- if (dumpcount == 0)
- dumpfile(stdin);
- return(0);
-}
+++ /dev/null
-#!/usr/bin/perl
-
-# Rotate Sherlock logs
-# (c) 2001--2002 Martin Mares <mj@ucw.cz>
-
-use File::stat;
-
-@ARGV >= 3 or die "Usage: rotate-log <days-to-compress> <date-to-delete> <logs...>";
-
-$now = time;
-$cps = shift @ARGV;
-$del = shift @ARGV;
-
-$compress_thr = $now - 86400 * $cps;
-$delete_thr = $now - 86400 * $del;
-foreach $f (@ARGV) {
- -f $f or next;
- $st = stat $f or next;
- if ($del > 0 && $st->mtime < $delete_thr) {
- print "Deleting $f\n";
- unlink $f || die "Delete FAILED: $!";
- } elsif ($cps > 0 && $st->mtime < $compress_thr && $f !~ /\.(gz|bz2)$/) {
- print "Compressing $f\n";
- `gzip -f $f`;
- $? && die "Compression FAILED: $!";
- }
-}
+++ /dev/null
-/*
- * Sherlock Utilities -- URL Handling Tool
- *
- * (c) 2004 Martin Mares <mj@ucw.cz>
- */
-
-#include "ucw/lib.h"
-#include "ucw/getopt.h"
-#include "ucw/url.h"
-#include "ucw/fastbuf.h"
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-static byte *base_url;
-static struct url base;
-static uns opt_split = 0, opt_normalize = 0, opt_forgive = 0;
-static struct fastbuf *fout;
-static uns err_count;
-
-static void
-process_url(byte *url)
-{
- byte buf1[MAX_URL_SIZE], buf2[MAX_URL_SIZE], buf3[MAX_URL_SIZE], buf4[MAX_URL_SIZE];
- int e;
- struct url ur;
-
- if ((e = url_deescape(url, buf1)) || (e = url_split(buf1, &ur, buf2)))
- goto error;
- if ((base_url || opt_normalize) && (e = url_normalize(&ur, &base)))
- goto error;
- if (opt_normalize && (e = url_canonicalize(&ur)))
- goto error;
- if (opt_split)
- {
- if (ur.protocol)
- bprintf(fout, "protocol=%s\n", ur.protocol);
- if (ur.user)
- bprintf(fout, "user=%s\n", ur.user);
- if (ur.pass)
- bprintf(fout, "pass=%s\n", ur.pass);
- if (ur.host)
- bprintf(fout, "host=%s\n", ur.host);
- if (ur.port != ~0U)
- bprintf(fout, "port=%d\n", ur.port);
- if (ur.rest)
- bprintf(fout, "rest=%s\n", ur.rest);
- bputc(fout, '\n');
- }
- else
- {
- if ((e = url_pack(&ur, buf3)) || (e = url_enescape(buf3, buf4)))
- goto error;
- bprintf(fout, "%s\n", buf4);
- }
- return;
-
- error:
- msg(L_ERROR, "%s: %s", url, url_error(e));
- err_count++;
-}
-
-static char *shortopts = CF_SHORT_OPTS "b:fns";
-static struct option longopts[] =
-{
- CF_LONG_OPTS
- { "base", 1, 0, 'b' },
- { "forgive", 0, 0, 'f' },
- { "normalize", 0, 0, 'n' },
- { "split", 0, 0, 's' },
- { NULL, 0, 0, 0 }
-};
-
-static char *help = "\
-Usage: urltool [<options>] <operations> [<URL's>]\n\
-\n\
-Options:\n"
-CF_USAGE "\
--b, --base <URL>\tInput URL's are relative to this base\n\
--f, --forgive\t\tReturn exit status 0 even if there were errors\n\
-\n\
-Operations:\n\
--s, --split\t\tSplit a given URL to components\n\
--n, --normalize\t\tNormalize given URL\n\
-";
-
-static void NONRET
-usage(byte *msg)
-{
- if (msg)
- {
- fputs(msg, stderr);
- fputc('\n', stderr);
- }
- fputs(help, stderr);
- exit(1);
-}
-
-int
-main(int argc, char **argv)
-{
- int opt, err;
- byte *base_url = NULL;
- byte basebuf1[MAX_URL_SIZE], basebuf2[MAX_URL_SIZE];
-
- log_init(argv[0]);
- while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0)
- switch (opt)
- {
- case 'b':
- base_url = optarg;
- err = url_canon_split(base_url, basebuf1, basebuf2, &base);
- if (err)
- die("Invalid base URL: %s", url_error(err));
- break;
- case 's':
- opt_split = 1;
- break;
- case 'n':
- opt_normalize = 1;
- break;
- case 'f':
- opt_forgive = 1;
- break;
- default:
- usage("Invalid option");
- }
-
- fout = bfdopen_shared(1, 4096);
- if (optind >= argc)
- {
- struct fastbuf *fin = bfdopen_shared(0, 4096);
- byte url[MAX_URL_SIZE];
- while (bgets(fin, url, sizeof(url)))
- process_url(url);
- bclose(fin);
- }
- else
- while (optind < argc)
- process_url(argv[optind++]);
- bclose(fout);
-
- return (err_count && !opt_forgive);
-}
--- /dev/null
+# Makefile for the UCW utilities (c) 2008 Michal Vaner <vorner@ucw.cz>
+
+PROGS+=$(addprefix $(o)/ucw/utils/,b224 basecode daemon-helper rotate-log urltool)
+DIRS+=ucw/utils
+
+ifdef CONFIG_DEBUG
+PROGS+=$(o)/ucw/utils/hex
+endif
+
+$(o)/ucw/utils/b224: $(o)/ucw/utils/b224.o $(LIBUCW)
+$(o)/ucw/utils/basecode: $(o)/ucw/utils/basecode.o $(LIBUCW)
+$(o)/ucw/utils/daemon-helper: $(o)/ucw/utils/daemon-helper.o $(LIBUCW)
+$(o)/ucw/utils/hex: $(o)/ucw/utils/hex.o $(LIBUCW)
+$(o)/ucw/utils/urltool: $(o)/ucw/utils/urltool.o $(LIBUCW)
+
+TESTS+=$(o)/ucw/utils/basecode.test
+$(o)/ucw/utils/basecode.test: $(o)/ucw/utils/basecode
--- /dev/null
+/*
+ * A Program For Manipulation With Base224 Encoded Files
+ *
+ * (c) 2002 Martin Mares <mj@ucw.cz>
+ */
+
+#include "ucw/lib.h"
+#include "ucw/fastbuf.h"
+#include "ucw/base224.h"
+
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+ struct fastbuf *in = bfdopen_shared(0, 4096);
+ struct fastbuf *out = bfdopen_shared(1, 4096);
+ byte ib[BASE224_IN_CHUNK*10], ob[BASE224_OUT_CHUNK*10], *b;
+ uns il, ol;
+
+ if (argc != 2 || argv[1][0] != '-')
+ goto usage;
+
+ switch (argv[1][1])
+ {
+ case 'e': /* Plain encoding */
+ while (il = bread(in, ib, sizeof(ib)))
+ {
+ ol = base224_encode(ob, ib, il);
+ bwrite(out, ob, ol);
+ }
+ break;
+ case 'E': /* Line block encoding */
+ while (il = bread(in, ib, BASE224_IN_CHUNK*6))
+ {
+ ol = base224_encode(ob, ib, il);
+ bputc(out, 'N');
+ bwrite(out, ob, ol);
+ bputc(out, '\n');
+ }
+ break;
+ case 'd': /* Plain decoding */
+ while (ol = bread(in, ob, sizeof(ob)))
+ {
+ il = base224_decode(ib, ob, ol);
+ bwrite(out, ib, il);
+ }
+ break;
+ case 'D': /* Line block decoding */
+ while (b = bgets(in, ob, sizeof(ob)))
+ {
+ if (!ob[0])
+ die("Invalid line syntax");
+ il = base224_decode(ib, ob+1, b-ob-1);
+ bwrite(out, ib, il);
+ }
+ break;
+ default:
+ usage:
+ fputs("Usage: b224 (-e|-E|-d|-D)\n", stderr);
+ return 1;
+ }
+
+ bclose(in);
+ bclose(out);
+ return 0;
+}
--- /dev/null
+/*
+ * UCW Library -- Line utility for encoding and decoding base64 & base224
+ *
+ * (c) 2008, Michal Vaner <vorner@ucw.cz>
+ *
+ * This software may be freely distributed and used according to the terms
+ * of the GNU Lesser General Public License.
+ */
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "ucw/lib.h"
+#include "ucw/base64.h"
+#include "ucw/base224.h"
+#include "ucw/fastbuf.h"
+#include "ucw/getopt.h"
+
+static struct option opts[] = {
+ { "encode64", 0, 0, 'e' },
+ { "decode64", 0, 0, 'd' },
+ { "encode224", 0, 0, 'E' },
+ { "decode224", 0, 0, 'D' },
+ { "prefix", 1, 0, 'p' },
+ { "blocks", 1, 0, 'b' },
+ { 0, 0, 0, 0 }
+};
+
+static const struct {
+ uns (*function)(byte *, const byte *, uns);
+ uns in_block, out_block, num_blocks;
+ uns add_prefix;
+} actions[] = {
+ {
+ base64_encode,
+ BASE64_IN_CHUNK, BASE64_OUT_CHUNK, 20,
+ 1
+ },
+ {
+ base64_decode,
+ BASE64_OUT_CHUNK, BASE64_IN_CHUNK, 20,
+ 0
+ },
+ {
+ base224_encode,
+ BASE224_IN_CHUNK, BASE64_OUT_CHUNK, 6,
+ 1
+ },
+ {
+ base224_decode,
+ BASE224_OUT_CHUNK, BASE224_IN_CHUNK, 6,
+ 0
+ }
+};
+
+int main(int argc, char **argv)
+{
+ // Choose mode
+ int mode = -1;
+ char *prefix = NULL;
+ uns blocks = 0;
+ int opt;
+ while ((opt = getopt_long(argc, argv, "edEDp:b:", opts, NULL)) >= 0)
+ switch (opt)
+ {
+ case 'e': mode = 0; break;
+ case 'd': mode = 1; break;
+ case 'E': mode = 2; break;
+ case 'D': mode = 3; break;
+ case 'p': prefix = optarg; break;
+ case 'b':
+ {
+ char *end;
+ blocks = strtol(optarg, &end, 0);
+ if ((blocks > 0) && !*end)
+ break;
+ }
+ default: goto usage;
+ }
+
+ if (mode == -1)
+ {
+ usage:
+ fprintf(stderr, "basecode mode [--prefix=prefix] [--blocks=number_of_blocks]\nMode is one of:\n\t--encode64 (-e)\n\t--decode64 (-d)\n\t--encode224 (-E)\n\t--decode224 (-D)\n");
+ return 1;
+ }
+ if (!blocks)
+ blocks = actions[mode].num_blocks;
+
+ // Prepare buffers
+ struct fastbuf *in = bfdopen_shared(0, 4096);
+ struct fastbuf *out = bfdopen_shared(1, 4096);
+ int has_offset = !actions[mode].add_prefix && prefix;
+ uns offset = has_offset ? strlen(prefix) : 0;
+ uns read_size = actions[mode].in_block * blocks + offset + has_offset;
+ uns write_size = actions[mode].out_block * blocks;
+ byte in_buff[read_size], out_buff[write_size];
+ uns isize;
+
+ // Recode it
+ while (isize = bread(in, in_buff, read_size))
+ {
+ if (prefix)
+ {
+ if (actions[mode].add_prefix)
+ bputs(out, prefix);
+ else
+ if ((isize < offset) || (in_buff[isize-1] != '\n')
+ || (strncmp(prefix, in_buff, offset)))
+ die("Invalid line syntax");
+ }
+ uns osize = actions[mode].function(out_buff, in_buff + offset, isize - offset - has_offset);
+ bwrite(out, out_buff, osize);
+ if (actions[mode].add_prefix && prefix)
+ bputc(out, '\n');
+ }
+
+ bclose(in);
+ bclose(out);
+ return 0;
+}
--- /dev/null
+/*
+ * A Simple Wrapper for Starting and Stopping of Daemons
+ *
+ * (c) 2003 Martin Mares <mj@ucw.cz>
+ *
+ * It would seem that we are reinventing the wheel and the
+ * start-stop-daemon command present in most Linux distributions
+ * is just what we need, but the usual "does the process already
+ * exist?" strategies fail in presence of multiple running daemons.
+ *
+ * Return codes:
+ * 101 already running
+ * 102 not running
+ */
+
+#include "ucw/lib.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <errno.h>
+#include <alloca.h>
+
+enum action {
+ ACTION_NONE,
+ ACTION_START,
+ ACTION_STOP,
+ ACTION_FORCE_STOP,
+ ACTION_CHECK,
+ ACTION_RELOAD
+};
+
+static int action;
+
+static struct option options[] = {
+ { "pid-file", required_argument, NULL, 'p' },
+ { "status-file", required_argument, NULL, 's' },
+ { "start", no_argument, &action, ACTION_START },
+ { "stop", no_argument, &action, ACTION_STOP },
+ { "force-stop", no_argument, &action, ACTION_FORCE_STOP },
+ { "check", no_argument, &action, ACTION_CHECK },
+ { "reload", no_argument, &action, ACTION_RELOAD },
+ { NULL, no_argument, NULL, 0 }
+};
+
+static void NONRET
+usage(void)
+{
+ fputs("\n\
+Usage: daemon-helper --start <options> -- <daemon> <args>\n\
+ or: daemon-helper --stop <options>\n\
+ or: daemon-helper --force-stop <options>\n\
+ or: daemon-helper --reload <options>\n\
+ or: daemon-helper --check <options>\n\
+\n\
+Options:\n\
+--pid-file <name> Name of PID file for this daemon (mandatory)\n\
+--status-file <name> Status file used by the daemon (deleted just before starting)\n\
+", stderr);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c, fd;
+ char *pidfile = NULL;
+ char *statfile = NULL;
+ struct flock fl;
+ char buf[64];
+
+ while ((c = getopt_long(argc, argv, "", options, NULL)) >= 0)
+ switch (c)
+ {
+ case 0:
+ break;
+ case 'p':
+ pidfile = optarg;
+ break;
+ case 's':
+ statfile = optarg;
+ break;
+ default:
+ usage();
+ }
+ if (!pidfile)
+ usage();
+
+ bzero(&fl, sizeof(fl));
+ fl.l_type = F_WRLCK;
+ fl.l_whence = SEEK_SET;
+
+ switch (action)
+ {
+ case ACTION_START:
+ if (optind >= argc)
+ usage();
+ fd = open(pidfile, O_RDWR | O_CREAT, 0666);
+ if (fd < 0)
+ die("Unable to create %s: %m", pidfile);
+ if ((c = fcntl(fd, F_SETLK, &fl)) < 0)
+ {
+ if (errno == EAGAIN || errno == EACCES)
+ return 101;
+ else
+ die("fcntl lock on %s failed: %m", pidfile);
+ }
+ c = sprintf(buf, "%d\n", getpid());
+ if (write(fd, buf, c) != c)
+ die("write on %s failed: %m", pidfile);
+ if (ftruncate(fd, c) < 0)
+ die("truncate on %s failed: %m", pidfile);
+ if (statfile && unlink(statfile) < 0 && errno != ENOENT)
+ die("unlink(%s) failed: %m", statfile);
+ setsid();
+ /* Disconnect from stdin and stdout, leave stderr to the daemon. */
+ close(0);
+ open("/dev/null", O_RDWR, 0);
+ dup2(0, 1);
+ argv += optind;
+ argc -= optind;
+ char **a = alloca(sizeof(char *) * (argc+1));
+ memcpy(a, argv, sizeof(char *) * argc);
+ a[argc] = NULL;
+ execv(a[0], a);
+ die("Cannot execute %s: %m", a[0]);
+ case ACTION_STOP:
+ case ACTION_FORCE_STOP:
+ case ACTION_CHECK:
+ case ACTION_RELOAD:
+ if (optind < argc)
+ usage();
+ fd = open(pidfile, O_RDWR);
+ if (fd < 0)
+ {
+ if (errno == ENOENT)
+ return 102;
+ else
+ die("Unable to open %s: %m", pidfile);
+ }
+ if ((c = fcntl(fd, F_SETLK, &fl)) >= 0)
+ {
+ nopid:
+ unlink(pidfile);
+ return 102;
+ }
+ if (errno != EAGAIN && errno != EACCES)
+ die("fcntl lock on %s failed: %m", pidfile);
+ if ((c = read(fd, buf, sizeof(buf))) < 0)
+ die("read on %s failed: %m", pidfile);
+ if (!c)
+ goto nopid;
+ if (c >= (int) sizeof(buf) || sscanf(buf, "%d", &c) != 1)
+ die("PID file syntax error");
+ int sig = 0;
+ if (action == ACTION_CHECK || action == ACTION_RELOAD)
+ {
+ if (action == ACTION_RELOAD)
+ sig = SIGHUP;
+ if (kill(c, sig) < 0 && errno == ESRCH)
+ goto nopid;
+ return 0;
+ }
+ sig = (action == ACTION_STOP) ? SIGTERM : SIGQUIT;
+ if (kill(c, sig) < 0)
+ {
+ if (errno == ESRCH)
+ goto nopid;
+ die("Cannot kill process %d: %m", c);
+ }
+ if ((c = fcntl(fd, F_SETLKW, &fl)) < 0)
+ die("Cannot lock %s: %m", pidfile);
+ if (statfile)
+ unlink(statfile);
+ if (unlink(pidfile) < 0)
+ die("Cannot unlink %s: %m", pidfile);
+ return 0;
+ default:
+ usage();
+ }
+}
--- /dev/null
+/*
+ * Hexadecimal dumper (CP/M style format)
+ *
+ * Original version (c) Eric S. Raymond <esr@snark.thyrsus.com>
+ * Heavily modified by Martin Mares <mj@ucw.cz>
+ *
+ * Can be freely distributed and used under the terms of the GNU GPL.
+ */
+
+#include "ucw/lib.h"
+#include "ucw/lfs.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#define DEFWIDTH 16 /* Default # chars to show per line */
+#define MAXWIDTH 32 /* Maximum # of bytes per line */
+
+typedef int bool;
+#define TRUE 1
+#define FALSE 0
+
+static long linesize = DEFWIDTH; /* # of bytes to print per line */
+static bool cflag = FALSE; /* show printables as ASCII if true */
+static bool gflag = FALSE; /* suppress mid-page gutter if true */
+static ucw_off_t start = 0; /* file offset to start dumping at */
+static ucw_off_t length = 0; /* if nz, how many chars to dump */
+
+static void dumpfile(FILE *f)
+ /* dump a single, specified file -- stdin if filename is NULL */
+{
+ int ch = '\0'; /* current character */
+ char ascii[MAXWIDTH+3]; /* printable ascii data */
+ int i = 0; /* counter: # bytes processed */
+ int ai = 0; /* index into ascii[] */
+ ucw_off_t offset = start; /* byte offset of line in file */
+ int hpos = 0; /* horizontal position counter */
+ ucw_off_t fstart = start;
+ ucw_off_t flength = length;
+ char *specials = "\b\f\n\r\t";
+ char *escapes = "bfnrt";
+ char *cp;
+
+ if (fstart && ucw_seek(fileno(f), fstart, SEEK_SET) >= 0)
+ fstart = 0;
+
+ do {
+ ch = getc(f);
+
+ if (ch != EOF)
+ {
+ if (length && flength-- <= 0)
+ ch = EOF;
+ }
+
+ if (ch != EOF)
+ {
+ if (i++ % linesize == 0)
+ {
+ (void) printf("%04Lx ", (long long) offset);
+ offset += linesize;
+ hpos = 5;
+ }
+
+ /* output one space for the mid-page gutter */
+ if (!gflag)
+ if ((i - 1) % (linesize / 2) == 0)
+ {
+ (void) putchar(' ');
+ hpos++;
+ ascii[ai++] = ' ';
+ }
+
+ /* dump the indicated representation of a character */
+ ascii[ai] = (isprint (ch) || ch == ' ') ? ch : '.';
+
+ if (cflag && (isprint(ch) || ch == ' '))
+ (void) printf("%c ", ch);
+ else if (cflag && ch && (cp = strchr(specials, ch)))
+ (void) printf("\\%c ", escapes[cp - specials]);
+ else
+ (void) printf("%02x ", ch);
+
+ /* update counters and things */
+ ai++;
+ hpos += 3;
+ }
+
+ /* At end-of-line or EOF, show ASCII version of data. */
+ if (i && (ch == EOF || (i % linesize == 0)))
+ {
+ if (!cflag)
+ {
+ while (hpos < linesize * 3 + 7)
+ {
+ hpos++;
+ (void) putchar(' ');
+ }
+
+ ascii[ai] = '\0';
+ (void) printf("%s", ascii);
+ }
+
+ if (ch != EOF || (i % linesize != 0))
+ (void) putchar('\n');
+ ai = 0; /* reset counters */
+ }
+ } while
+ (ch != EOF);
+}
+
+static ucw_off_t getoffs(char *cp)
+ /* fetch decimal or hex integer to be used as file start or offset */
+{
+ ucw_off_t value = 0;
+ char *hexdigits = "0123456789abcdefABCDEF";
+
+#if 0
+ bool foundzero = FALSE;
+ int base = 0;
+
+ for (; *cp; cp++)
+ if (*cp == '0')
+ foundzero = TRUE;
+ else if (isdigit(*cp))
+ {
+ base = 10;
+ break;
+ }
+ else if (*cp = 'x' || *cp == 'X' || *cp == 'h' || *cp == 'H')
+ {
+ base = 16;
+ cp++;
+ break;
+ }
+ else
+ return(-1L);
+
+ if (base == 0)
+ if (foundzero)
+ base = 10;
+ else
+ return(-1L);
+
+ if (base == 10)
+ {
+ for (; *cp; cp++)
+ if (isdigit(*cp))
+ value = value * 10 + (*cp - '0');
+ else
+ return(-1L);
+ }
+ else
+#endif
+ {
+ for (; *cp; cp++)
+ if (strchr(hexdigits, *cp))
+ value = value*16 + (strchr(hexdigits, tolower(*cp))-hexdigits);
+ else
+ return -1;
+ }
+
+ return(value);
+}
+
+int main(int argc, char **argv)
+{
+ FILE *infile; /* file pointer input file */
+ int dumpcount = 0; /* count of files dumped so far */
+ char *cp;
+ int fd;
+
+ for (argv++, argc--; argc > 0; argv++, argc--)
+ {
+ char s = **argv;
+
+ if (s == '-' || s == '+')
+ {
+ int c = *++*argv;
+
+ switch (c)
+ {
+ case 'c': cflag = (s == '-'); continue;
+ case 'g': gflag = (s == '-'); continue;
+
+ case 's':
+ if ((*argv)[1])
+ (*argv)++;
+ else
+ argc--, argv++;
+ if (s == '-' && argc >= 0)
+ {
+ if (cp = strchr(*argv, ','))
+ *cp++ = '\0';
+ if ((start = getoffs(*argv)) < 0)
+ {
+ (void) fputs("hex: start offset no good\n", stderr);
+ exit(1);
+ }
+
+ if (cp)
+ if ((length = getoffs(cp)) < 0)
+ {
+ (void) fputs("hex: length no good\n", stderr);
+ exit(1);
+ }
+ }
+ else
+ start = length = 0L;
+ continue;
+
+ case '\0':
+ infile = stdin;
+ break;
+
+ case 'w':
+ if ((*argv)[1])
+ (*argv)++;
+ else
+ argc--, argv++;
+ if ((linesize = getoffs(*argv)) == -1L || linesize > MAXWIDTH)
+ {
+ (void) fputs("hex: line width no good\n", stderr);
+ exit(1);
+ }
+ if (linesize % 2)
+ gflag = TRUE;
+ continue;
+
+ default:
+ (void) fprintf(stderr, "hex: no such option as %s\n", *argv);
+ exit(1);
+ }
+ }
+ else
+ {
+ fd = ucw_open(*argv, O_RDONLY, 0);
+ if (fd < 0 || !(infile = fdopen(fd, "r")))
+ {
+ (void) fprintf(stderr, "hex: cannot open %s: %m\n", *argv);
+ exit(1);
+ }
+ }
+
+ if (dumpcount > 0 || argc > 1)
+ if (infile == stdin)
+ (void) printf("---- <Standard input> ----\n");
+ else
+ (void) printf("---- %s ----\n", *argv);
+ dumpfile(infile);
+ dumpcount++;
+ if (infile != stdin)
+ (void) fclose(infile);
+ }
+
+ if (dumpcount == 0)
+ dumpfile(stdin);
+ return(0);
+}
--- /dev/null
+#!/usr/bin/perl
+
+# Rotate Sherlock logs
+# (c) 2001--2002 Martin Mares <mj@ucw.cz>
+
+use File::stat;
+
+@ARGV >= 3 or die "Usage: rotate-log <days-to-compress> <date-to-delete> <logs...>";
+
+$now = time;
+$cps = shift @ARGV;
+$del = shift @ARGV;
+
+$compress_thr = $now - 86400 * $cps;
+$delete_thr = $now - 86400 * $del;
+foreach $f (@ARGV) {
+ -f $f or next;
+ $st = stat $f or next;
+ if ($del > 0 && $st->mtime < $delete_thr) {
+ print "Deleting $f\n";
+ unlink $f || die "Delete FAILED: $!";
+ } elsif ($cps > 0 && $st->mtime < $compress_thr && $f !~ /\.(gz|bz2)$/) {
+ print "Compressing $f\n";
+ `gzip -f $f`;
+ $? && die "Compression FAILED: $!";
+ }
+}
--- /dev/null
+/*
+ * Sherlock Utilities -- URL Handling Tool
+ *
+ * (c) 2004 Martin Mares <mj@ucw.cz>
+ */
+
+#include "ucw/lib.h"
+#include "ucw/getopt.h"
+#include "ucw/url.h"
+#include "ucw/fastbuf.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static byte *base_url;
+static struct url base;
+static uns opt_split = 0, opt_normalize = 0, opt_forgive = 0;
+static struct fastbuf *fout;
+static uns err_count;
+
+static void
+process_url(byte *url)
+{
+ byte buf1[MAX_URL_SIZE], buf2[MAX_URL_SIZE], buf3[MAX_URL_SIZE], buf4[MAX_URL_SIZE];
+ int e;
+ struct url ur;
+
+ if ((e = url_deescape(url, buf1)) || (e = url_split(buf1, &ur, buf2)))
+ goto error;
+ if ((base_url || opt_normalize) && (e = url_normalize(&ur, &base)))
+ goto error;
+ if (opt_normalize && (e = url_canonicalize(&ur)))
+ goto error;
+ if (opt_split)
+ {
+ if (ur.protocol)
+ bprintf(fout, "protocol=%s\n", ur.protocol);
+ if (ur.user)
+ bprintf(fout, "user=%s\n", ur.user);
+ if (ur.pass)
+ bprintf(fout, "pass=%s\n", ur.pass);
+ if (ur.host)
+ bprintf(fout, "host=%s\n", ur.host);
+ if (ur.port != ~0U)
+ bprintf(fout, "port=%d\n", ur.port);
+ if (ur.rest)
+ bprintf(fout, "rest=%s\n", ur.rest);
+ bputc(fout, '\n');
+ }
+ else
+ {
+ if ((e = url_pack(&ur, buf3)) || (e = url_enescape(buf3, buf4)))
+ goto error;
+ bprintf(fout, "%s\n", buf4);
+ }
+ return;
+
+ error:
+ msg(L_ERROR, "%s: %s", url, url_error(e));
+ err_count++;
+}
+
+static char *shortopts = CF_SHORT_OPTS "b:fns";
+static struct option longopts[] =
+{
+ CF_LONG_OPTS
+ { "base", 1, 0, 'b' },
+ { "forgive", 0, 0, 'f' },
+ { "normalize", 0, 0, 'n' },
+ { "split", 0, 0, 's' },
+ { NULL, 0, 0, 0 }
+};
+
+static char *help = "\
+Usage: urltool [<options>] <operations> [<URL's>]\n\
+\n\
+Options:\n"
+CF_USAGE "\
+-b, --base <URL>\tInput URL's are relative to this base\n\
+-f, --forgive\t\tReturn exit status 0 even if there were errors\n\
+\n\
+Operations:\n\
+-s, --split\t\tSplit a given URL to components\n\
+-n, --normalize\t\tNormalize given URL\n\
+";
+
+static void NONRET
+usage(byte *msg)
+{
+ if (msg)
+ {
+ fputs(msg, stderr);
+ fputc('\n', stderr);
+ }
+ fputs(help, stderr);
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int opt, err;
+ byte *base_url = NULL;
+ byte basebuf1[MAX_URL_SIZE], basebuf2[MAX_URL_SIZE];
+
+ log_init(argv[0]);
+ while ((opt = cf_getopt(argc, argv, shortopts, longopts, NULL)) >= 0)
+ switch (opt)
+ {
+ case 'b':
+ base_url = optarg;
+ err = url_canon_split(base_url, basebuf1, basebuf2, &base);
+ if (err)
+ die("Invalid base URL: %s", url_error(err));
+ break;
+ case 's':
+ opt_split = 1;
+ break;
+ case 'n':
+ opt_normalize = 1;
+ break;
+ case 'f':
+ opt_forgive = 1;
+ break;
+ default:
+ usage("Invalid option");
+ }
+
+ fout = bfdopen_shared(1, 4096);
+ if (optind >= argc)
+ {
+ struct fastbuf *fin = bfdopen_shared(0, 4096);
+ byte url[MAX_URL_SIZE];
+ while (bgets(fin, url, sizeof(url)))
+ process_url(url);
+ bclose(fin);
+ }
+ else
+ while (optind < argc)
+ process_url(argv[optind++]);
+ bclose(fout);
+
+ return (err_count && !opt_forgive);
+}