u-isdn/config/subst.c

592 lines
12 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* This is from INN. */
/* $Revision: 1.2 $
**
** A C version of Henry Spencer's "subst" script.
*/
#include <stdio.h>
#include <signal.h>
#include <errno.h>
#define LINESIZE 1024
#define FNAMESIZE 1024
#define PARAMSIZE 128
#define WHITE(c) ((c) == ' ' || (c) == '\t')
/*
** AFS doesn't support hard links, so enable this #define.
#define USE_RENAME
*/
/*
** If you don't have getopt in your C library, enable this #define.
#define NEED_GETOPT
*/
typedef struct _PAIR {
char *Name;
int Length;
char *Value;
} PAIR;
static char *argv0;
extern char *optarg;
extern int optind;
extern void exit();
extern char *malloc();
extern char *strcpy();
/*
** Local implementations of common C library functions.
*/
/*
** Return string represtation of errno.
*/
static char *
xstrerror()
{
extern int errno;
static char buff[30];
if (errno >= 0 && errno < sys_nerr)
return sys_errlist[errno];
(void)sprintf(buff, "Error code %d\n", errno);
return buff;
}
/*
** Return the first occurrence of 'c' in 'p' or NULL if not there.
*/
static char *
xstrchr(p, c)
register char *p;
register char c;
{
for ( ; *p; p++)
if (*p == c)
return p;
return NULL;
}
/*
** Return the last occurrence of 'c' in 'p' or NULL if not there.
*/
static char *
xstrrchr(p, c)
register char *p;
register char c;
{
register char *ret;
for (ret = NULL; *p; p++)
if (*p == c)
ret = p;
return ret;
}
/*
** Copy a string to malloc'd memory or exit.
*/
static char *
xstrdup(p)
char *p;
{
char *new;
if ((new = malloc(strlen(p) + 1)) == NULL) {
(void)fprintf(stderr, "%s: Can't copy \"%s\", %s\n",
argv0, p, xstrerror());
exit(1);
}
return strcpy(new, p);
}
#if defined(NEED_GETOPT)
#define TYPE int
#define ERR(s, c) \
if (opterr) { \
char buff[2]; \
buff[0] = c; buff[1] = '\n'; \
(void)write(2, av[0], (TYPE)strlen(av[0])); \
(void)write(2, s, (TYPE)strlen(s)); \
(void)write(2, buff, 2); \
}
int opterr = 1;
int optind = 1;
int optopt;
char *optarg;
/*
** Return options and their values from the command line.
** This comes from the AT&T public-domain getopt published in mod.sources
** (i.e., comp.sources.unix before the great Usenet renaming).
*/
int
getopt(ac, av, opts)
int ac;
char *av[];
char *opts;
{
static int i = 1;
char *p;
/* Move to next value from argv? */
if (i == 1) {
if (optind >= ac || av[optind][0] != '-' || av[optind][1] == '\0')
return EOF;
if (strcmp(av[optind], "--") == 0) {
optind++;
return EOF;
}
}
/* Get next option character. */
if ((optopt = av[optind][i]) == ':' || (p = IDX(opts, optopt)) == NULL) {
ERR(": illegal option -- ", optopt);
if (av[optind][++i] == '\0') {
optind++;
i = 1;
}
return '?';
}
/* Snarf argument? */
if (*++p == ':') {
if (av[optind][i + 1] != '\0')
optarg = &av[optind++][i + 1];
else {
if (++optind >= ac) {
ERR(": option requires an argument -- ", optopt);
i = 1;
return '?';
}
optarg = av[optind++];
}
i = 1;
}
else {
if (av[optind][++i] == '\0') {
i = 1;
optind++;
}
optarg = NULL;
}
return optopt;
}
#endif /* defined(NEED_GETOPT) */
/*
** Simulate "mv $from $to" -- return no useful status. We know that
** the $from and $to are on the same filesystem.
*/
static void
mv(from, to)
char *from;
char *to;
{
if (unlink(to) < 0 && errno != ENOENT) {
(void)fprintf(stderr, "%s: Can't unlink %s, %s\n",
argv0, to, xstrerror());
return;
}
#if defined(USE_RENAME)
if (rename(from, to) < 0) {
(void)fprintf(stderr, "%s: Can't rename %s to %s, %s\n",
argv0, from, to, xstrerror());
return;
}
#else
if (link(from, to) < 0) {
(void)fprintf(stderr, "%s: Can't link %s to %s, %s\n",
argv0, from, to, xstrerror());
return;
}
if (unlink(from) < 0)
(void)fprintf(stderr, "%s: Can't unlink %s, %s\n",
argv0, from, xstrerror());
#endif /* defined(USE_RENAME) */
}
/*
** Simulate "cmp -s $n1 $n2" -- return 0 if files are the same.
*/
static int
cmp(n1, n2)
char *n1;
char *n2;
{
FILE *f1;
FILE *f2;
int c;
if ((f1 = fopen(n1, "r")) == NULL)
return 1;
if ((f2 = fopen(n2, "r")) == NULL) {
(void)fclose(f1);
return 1;
}
while ((c = getc(f1)) != EOF)
if (getc(f2) != c) {
(void)fclose(f1);
(void)fclose(f2);
return 1;
}
if (getc(f2) != EOF) {
(void)fclose(f1);
(void)fclose(f2);
return 1;
}
(void)fclose(f1);
(void)fclose(f2);
return 0;
}
/*
** If line does not look like a template, return NULL, otherwise modify
** it to delete the trailing gunk and return the start of the template.
*/
static char *
istemplate(line)
char *line;
{
char *p;
char *start;
/* Find "=()<" and remember where it starts. */
for (p = line; (p = xstrchr(p, '=')) != NULL; p++)
if (p[1] == '(' && p[2] == ')' && p[3] == '<')
break;
if (p == NULL)
return NULL;
start = &p[4];
/* Now find ">()=" and nip it off. */
for (p = start; (p = xstrchr(p, '>')) != NULL; p++)
if (p[1] == '(' && p[2] == ')' && p[3] == '=') {
*p++ = '\n';
*p = '\0';
return start;
}
return NULL;
}
/*
** Splice three strings together, returning an allocated copy.
*/
static char *
splice(s1, s2, s3)
char *s1;
char *s2;
char *s3;
{
int i;
char *new;
i = strlen(s1) + strlen(s2) + strlen(s3) + 1;
if ((new = malloc(i)) == NULL) {
(void)fprintf(stderr, "%s: Can't splice %s+%s+%s, %s\n",
argv0, s1, s2, s3, xstrerror());
exit(1);
}
(void)sprintf(new, "%s%s%s", s1, s2, s3);
return new;
}
/*
** Substitute all found patterns in the line and print it. Using the goto
** makes the code more clear than using do/while.
*/
static int
doline(f, out, line, tp, end)
char *f;
FILE *out;
char *line;
PAIR *tp;
PAIR *end;
{
char *p;
char *new;
char save;
int count;
count = 0;
for (line = xstrdup(line); tp < end; tp++) {
Again:
for (p = line; (p = xstrchr(p, tp->Name[0])) != NULL; p++)
if (strncmp(p, tp->Name, tp->Length) == 0) {
save = *p;
*p = '\0';
count ++;
new = splice(line, tp->Value, p + tp->Length);
*p = save;
if (strcmp(new, line) == 0) {
(void)fprintf(stderr, "%s: subst loop in %s:\n\t%s\n",
argv0, f, line);
free(new);
break;
}
free(line);
line = new;
goto Again;
}
}
if (count > 0 && fputs(line, out) == EOF) {
(void)fprintf(stderr, "%s: can't write %s, %s\n",
argv0, f, xstrerror());
free(line);
return -1;
}
free(line);
return count;
}
/*
** Process one file, carefully substituting it in place.
*/
static void
Process(f, Table, end)
char *f;
PAIR *Table;
PAIR *end;
{
char new[FNAMESIZE];
char old[FNAMESIZE];
char line[LINESIZE];
int bad;
int i;
int count;
FILE *in;
FILE *out;
FILE *temp;
char *p;
/* First, figure out temporary names. */
if ((p = xstrrchr(f, '/')) == NULL) {
(void)strcpy(new, "substtmp.new");
(void)strcpy(old, "substtmp.old");
}
else {
*p = '\0';
(void)sprintf(new, "%s/substtmp.new", f);
(void)sprintf(old, "%s/substtmp.old", f);
*p = '/';
}
/* Test existences. */
if ((in = fopen(f, "r")) == NULL) {
(void)fprintf(stderr, "%s: can't open %s, %s\n",
argv0, f, xstrerror());
return;
}
if ((temp = fopen(new, "r")) != NULL) {
(void)fclose(in);
(void)fprintf(stderr, "%s: %s exists, cannot proceed\n",
argv0, new);
exit(1);
}
if ((temp = fopen(old, "r")) != NULL) {
(void)fprintf(stderr, "%s: %s exists, cannot proceed\n",
argv0, old);
exit(1);
}
temp = fopen(old, "w");
out = fopen(new, "w");
if (out == NULL || temp == NULL) {
if (temp != NULL)
(void)fclose(temp);
(void)unlink(old);
if (out != NULL)
(void)fclose(out);
(void)unlink(new);
(void)fprintf(stderr, "%s: cannot create temporaries %s and %s\n",
argv0, old, new);
exit(1);
}
(void)fclose(temp);
/* Generate the new version. */
for (i = 1, bad = 0; fgets(line, sizeof line, in) != NULL; i++) {
if ((p = xstrchr(line, '\n')) == NULL) {
(void)fprintf(stderr, "%s: Line %d of %s is too long\n",
argv0, i, f);
bad++;
break;
}
(void)fputs(line, out);
if ((p = istemplate(line)) != NULL) {
count = doline(f, out, p, Table, end);
if (count < 0) {
bad++;
break;
}
if(count > 0) {
(void)fgets(line, sizeof line, in);
i++;
}
else
fprintf(stderr,"%s: %s:%d: unknown variable:\n\t%s",
argv0,f,i,p);
}
}
(void)fclose(in);
if (fflush(out) == EOF || fclose(out) == EOF) {
(void)fprintf(stderr, "%s: can't close %s, %s\n",
argv0, f, xstrerror());
bad++;
}
if (bad || cmp(new, f) == 0) {
(void)unlink(old);
(void)unlink(new);
(void)printf("%s: unchanged\n", f);
return;
}
/* Substitute new for old the only safe way -- ignore signals. */
(void)signal(SIGHUP, SIG_IGN);
(void)signal(SIGINT, SIG_IGN);
(void)signal(SIGTERM, SIG_IGN);
mv(f, old);
mv(new, f);
(void)signal(SIGHUP, SIG_DFL);
(void)signal(SIGINT, SIG_DFL);
(void)signal(SIGTERM, SIG_DFL);
(void)printf("%s: updated\n", f);
(void)unlink(old);
}
/*
** Print usage message and exit.
*/
static void
Usage()
{
(void)fprintf(stderr, "Usage: %s -f file victims...\n", argv0);
exit(1);
}
int
main(ac, av)
int ac;
char *av[];
{
static char NIL[] = "";
char *ctlfile;
char *p;
char *dest;
FILE *F;
int i;
char buff[LINESIZE];
char name[PARAMSIZE];
PAIR *Table;
PAIR *tp;
/* Set defaults. */
ctlfile = NULL;
argv0 = av[0];
/* Parse JCL. */
while ((i = getopt(ac, av, "f:")) != EOF)
switch (i) {
default:
Usage();
/* NOTREACHED */
case 'f':
if (ctlfile != NULL)
Usage();
ctlfile = optarg;
break;
}
ac -= optind;
av += optind;
/* Open control file, count lines, allocate table. */
if ((F = fopen(ctlfile, "r")) == NULL) {
(void)fprintf(stderr, "%s: Can't open %s to read it, %s\n",
argv0, ctlfile, xstrerror());
exit(1);
}
for (i = 0; fgets(buff, sizeof buff, F) != NULL; i++)
continue;
if ((Table = (PAIR *)malloc(i * sizeof *Table)) == NULL) {
(void)fprintf(stderr, "%s: Can't allocate %d table elements, %s\n",
argv0, i, xstrerror());
exit(1);
}
/* Now parse the table. */
(void)fseek(F, 0L, 0);
for (i = 1, tp = Table; fgets(buff, sizeof buff, F) != NULL; i++) {
if ((p = xstrchr(buff, '\n')) == NULL) {
(void)fprintf(stderr, "%s: Line %d of %s is too long\n",
argv0, i, ctlfile);
exit(1);
}
*p = '\0';
/* Skip empty lines, comment lines, and all-blank lines. */
if (buff[0] == '\0' || buff[0] == '#')
continue;
for (p = buff; WHITE(*p); p++)
continue;
if (*p == '\0')
continue;
/* Find end of first word, copy second word (or empty string) */
for (p = buff; *p && !WHITE(*p); p++)
continue;
if (*p == '\0')
tp->Value = NIL;
else {
for (*p++ = '\0'; *p && WHITE(*p); p++)
continue;
tp->Value = xstrdup(p);
/* Turn things like \& into &. */
for (p = dest = tp->Value; *p; p++)
*dest++ = (*p == '\\' && p[1] != '\0') ? *++p : *p;
*dest = '\0';
}
/* Turn first word into something directly searchable. */
if (strlen(buff) > sizeof name - 4) {
(void)fprintf(stderr, "%s: Parameter %s is too long\n",
argv0, buff);
exit(1);
}
(void)sprintf(name, "@<%s>@", buff);
tp->Name = xstrdup(name);
tp->Length = strlen(tp->Name);
tp++;
}
(void)fclose(F);
while (*av != NULL)
Process(*av++, Table, tp);
exit(0);
/* NOTREACHED */
}