diff --git a/distrib/sets/lists/minix/mi b/distrib/sets/lists/minix/mi index 4f6433ceb..2577d1579 100644 --- a/distrib/sets/lists/minix/mi +++ b/distrib/sets/lists/minix/mi @@ -554,6 +554,7 @@ ./usr/bin/write minix-sys ./usr/bin/writeisofs minix-sys ./usr/bin/xargs minix-sys +./usr/bin/xstr minix-sys ./usr/bin/xz minix-sys ./usr/bin/xzcat minix-sys ./usr/bin/yacc minix-sys @@ -2119,6 +2120,7 @@ ./usr/man/man1/worldstone.1 minix-sys ./usr/man/man1/write.1 minix-sys ./usr/man/man1/xargs.1 minix-sys +./usr/man/man1/xstr.1 minix-sys ./usr/man/man1/xz.1 minix-sys ./usr/man/man1/xzcat.1 minix-sys ./usr/man/man1/yacc.1 minix-sys diff --git a/releasetools/nbsd_ports b/releasetools/nbsd_ports index 0efcbd603..a0e5f0923 100644 --- a/releasetools/nbsd_ports +++ b/releasetools/nbsd_ports @@ -230,6 +230,7 @@ 2012/10/17 12:00:00,usr.bin/whois 2012/10/17 12:00:00,usr.bin/write 2012/10/17 12:00:00,usr.bin/xinstall +2012/10/17 12:00:00,usr.bin/xstr 2013/03/15 12:00:00,usr.bin/yes 2012/02/10 16:16:12,usr.sbin/chroot 2013/07/10 15:18:55,usr.sbin/i2cscan diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 7f5048319..9f2de1f6c 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -32,7 +32,7 @@ SUBDIR= asa \ uuidgen vis \ \ wc whatis who whois \ - write xargs xinstall yes + write xargs xinstall xstr yes .if !defined(__MINIX) SUBDIR+= ../external/zlib/pigz/bin/pigz diff --git a/usr.bin/xstr/Makefile b/usr.bin/xstr/Makefile new file mode 100644 index 000000000..fe2acea95 --- /dev/null +++ b/usr.bin/xstr/Makefile @@ -0,0 +1,6 @@ +# $NetBSD: Makefile,v 1.3 1994/11/14 04:56:21 jtc Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/9/93 + +PROG= xstr + +.include diff --git a/usr.bin/xstr/pathnames.h b/usr.bin/xstr/pathnames.h new file mode 100644 index 000000000..d58de9565 --- /dev/null +++ b/usr.bin/xstr/pathnames.h @@ -0,0 +1,35 @@ +/* $NetBSD: pathnames.h,v 1.4 2003/08/07 11:17:51 agc Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/9/93 + * $NetBSD: pathnames.h,v 1.4 2003/08/07 11:17:51 agc Exp $ + */ + +#define _PATH_TMP "/tmp/xstrXXXXXX" diff --git a/usr.bin/xstr/xstr.1 b/usr.bin/xstr/xstr.1 new file mode 100644 index 000000000..dd0e9813c --- /dev/null +++ b/usr.bin/xstr/xstr.1 @@ -0,0 +1,211 @@ +.\" $NetBSD: xstr.1,v 1.18 2005/09/11 23:29:44 wiz Exp $ +.\" +.\" Copyright (c) 1980, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)xstr.1 8.2 (Berkeley) 12/30/93 +.\" +.Dd July 23, 2004 +.Dt XSTR 1 +.Os +.Sh NAME +.Nm xstr +.Nd "extract strings from C programs to implement shared strings" +.Sh SYNOPSIS +.Nm +.Op Fl cv +.Op Fl l Ar array +.Op Fl +.Op Ar +.Sh DESCRIPTION +.Nm +maintains a file +.Pa strings +into which strings in component parts of a large program are hashed. +These strings are replaced with references to this common area. +This serves to implement shared constant strings, most useful if they +are also read-only. +.Pp +Available options: +.Bl -tag -width XXlXarrayXX +.It Fl +.Nm +reads from the standard input. +.It Fl c +.Nm +will extract the strings from the C source +.Ar file +or the standard input +.Pq Fl , +replacing +string references by expressions of the form (\*[Am]xstr[number]) +for some number. +An appropriate declaration of +.Nm +is prepended to the file. +The resulting C text is placed in the file +.Pa x.c , +to then be compiled. +The strings from this file are placed in the +.Pa strings +data base if they are not there already. +Repeated strings and strings which are suffixes of existing strings +do not cause changes to the data base. +.It Fl l Ar array +Specify the named array in program references to abstracted +strings. +The default array name is xstr. +.It Fl v +Be verbose. +.El +.Pp +After all components of a large program have been compiled, a file +.Pa xs.c +declaring the common +.Nm +space can be created by a command of the form: +.Pp +.Dl $ xstr +.Pp +The file +.Pa xs.c +should then be compiled and loaded with the rest +of the program. +If possible, the array can be made read-only (shared) saving +space and swap overhead. +.Pp +.Nm +can also be used on a single file. +The following command creates files +.Pa x.c +and +.Pa xs.c +as before, without using or affecting any +.Pa strings +file in the same directory: +.Pp +.Dl $ xstr name +.Pp +It may be useful to run +.Nm +after the C preprocessor if any macro definitions yield strings +or if there is conditional code which contains strings +which may not, in fact, be needed. +An appropriate command sequence for running +.Nm +after the C preprocessor is: +.Pp +.Bd -literal -offset indent +$ cc \-E name.c | xstr \-c \- +$ cc \-c x.c +$ mv x.o name.o +.Ed +.Pp +.Nm +does not touch the file +.Pa strings +unless new items are added, thus +.Xr make 1 +can avoid remaking +.Pa xs.o +unless truly necessary. +.Sh FILES +.Bl -tag -width /tmp/xsxx* -compact +.It Pa strings +Data base of strings +.It Pa x.c +Massaged C source +.It Pa xs.c +C source for definition of array `xstr' +.It Pa /tmp/xs* +Temp file when `xstr name' doesn't touch +.Pa strings +.El +.Sh SEE ALSO +.Xr mkstr 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . +.Sh BUGS +If a string is a suffix of another string in the data base, +but the shorter string is seen first by +.Nm +both strings will be placed in the data base, when just +placing the longer one there will do. +.Pp +.Nm +does not parse the file properly so it does not know not to process: +.Bd -literal + char var[] = "const"; +.Ed +into: +.Bd -literal + char var[] = (\*[Am]xstr[N]); +.Ed +.Pp +These must be changed manually into an appropriate initialization for +the string, or use the following ugly hack. +.Pp +Also, +.Nm +cannot initialize structures and unions that contain strings. +Those can be fixed by changing from: +.Bd -literal + struct foo { + int i; + char buf[10]; + } = { + 1, "foo" + }; +.Ed +to: +.Bd -literal + struct foo { + int i; + char buf[10]; + } = { + 1, { 'f', 'o', 'o', '\e0' } + }; +.Ed +.Pp +The real problem in both cases above is that the compiler knows the size +of the literal constant so that it can perform the initialization required, +but when +.Nm +changes the literal string to a pointer reference, the size information is +lost. +It would require a real parser to do this right, so the obvious solution is +to fix the program manually to compile, or even better rely on the compiler +and the linker to merge strings appropriately. +.Pp +Finally, +.Nm +is not very useful these days because most of the string merging is done +automatically by the compiler and the linker, provided that the strings +are identical and read-only. diff --git a/usr.bin/xstr/xstr.c b/usr.bin/xstr/xstr.c new file mode 100644 index 000000000..4b3ec764c --- /dev/null +++ b/usr.bin/xstr/xstr.c @@ -0,0 +1,569 @@ +/* $NetBSD: xstr.c,v 1.25 2011/09/16 15:39:31 joerg Exp $ */ + +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1980, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)xstr.c 8.1 (Berkeley) 6/9/93"; +#else +__RCSID("$NetBSD: xstr.c,v 1.25 2011/09/16 15:39:31 joerg Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "pathnames.h" + +/* + * xstr - extract and hash strings in a C program + * + * Bill Joy UCB + * November, 1978 + */ + +static off_t hashit(const char *, int); +__dead static void onintr(int); +static off_t yankstr(char **); +static int octdigit(char); +static void inithash(void); +static int fgetNUL(char *, int, FILE *); +static int xgetc(FILE *); +static void flushsh(void); +static void found(int, off_t, const char *); +static void prstr(const char *); +static void xsdotc(void); +static char lastchr(const char *); +static int istail(const char *, const char *); +static void process(const char *); +__dead static void usage(void); + +static off_t tellpt; +static off_t mesgpt; +static char stringtmpfile[MAXPATHLEN]; +static const char *strings = "strings"; +static const char *array = 0; +static int cflg; +static int vflg; +static int readstd; +static char linebuf[8192]; + +#define BUCKETS 128 + +static struct hash { + off_t hpt; + char *hstr; + struct hash *hnext; + short hnew; +} bucket[BUCKETS]; + +int +main(int argc, char *argv[]) +{ + int c; + + while ((c = getopt(argc, argv, "-cvl:")) != -1) + switch (c) { + case '-': + readstd++; + break; + case 'c': + cflg++; + break; + case 'v': + vflg++; + break; + case 'l': + array = optarg; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (array == 0) + array = "xstr"; + + if (signal(SIGINT, SIG_IGN) == SIG_DFL) + (void)signal(SIGINT, onintr); + if (cflg || (argc == 0 && !readstd)) + inithash(); + else { + int fd; + + snprintf(stringtmpfile, sizeof(stringtmpfile), + "%s%s.XXXXXX", _PATH_TMP, "xstr"); + strings = stringtmpfile; + fd = mkstemp(stringtmpfile); + if (fd == -1) + err(1, "mkstemp failed"); + close(fd); + } + while (readstd || argc > 0) { + if (freopen("x.c", "w", stdout) == NULL) + err(1, "Cannot open `%s'", "x.c"); + if (!readstd && freopen(argv[0], "r", stdin) == NULL) + err(1, "Cannot open `%s'", argv[0]); + process("x.c"); + if (readstd == 0) + argc--, argv++; + else + readstd = 0; + }; + flushsh(); + if (cflg == 0) + xsdotc(); + if (strings[0] == '/') + (void)unlink(strings); + exit(0); +} + +static void +process(const char *name) +{ + char *cp; + int c; + int incomm = 0; + int inasm = 0; + int asmparnest = 0; + int ret; + + printf("extern char\t%s[];\n", array); + for (;;) { + if (fgets(linebuf, sizeof linebuf, stdin) == NULL) { + if (ferror(stdin)) + err(1, "Error reading `%s'", name); + break; + } + if (linebuf[0] == '#') { + printf("%s", linebuf); + continue; + } + for (cp = linebuf; (c = *cp++);) + switch (c) { + + case '"': + if (incomm || inasm) + goto def; + if ((ret = (int) yankstr(&cp)) == -1) + goto out; + printf("(&%s[%d])", array, ret); + break; + + case '\'': + if (incomm || inasm) + goto def; + putchar(c); + if (*cp) + putchar(*cp++); + break; + + case '/': + if (incomm || *cp != '*') + goto def; + incomm = 1; + cp++; + printf("/*"); + continue; + + case '*': + if (incomm && *cp == '/') { + incomm = 0; + cp++; + printf("*/"); + continue; + } + goto def; + + case '(': + if (!incomm && inasm) + asmparnest++; + goto def; + + case ')': + if (!incomm && inasm && !--asmparnest) + inasm = 0; + goto def; + + case '_': + if (incomm || inasm) + goto def; + if (!strncmp(cp, "_asm", 4)) { + cp += 4; + printf("__asm"); + if (!strncmp(cp, "__", 2)) { + cp += 2; + printf("__"); + } + if (isalnum((unsigned char)*cp) || + *cp == '_') + goto def; + asmparnest = 0; + inasm = 1; + } else + goto def; + break; +def: + default: + putchar(c); + break; + } + } +out: + if (ferror(stdout)) { + warn("Error reading `%s'", "x.c"); + onintr(1); + } +} + +static off_t +yankstr(char **cpp) +{ + char *cp = *cpp; + int c, ch; + char *dbuf, *dp, *edp; + const char *tp; + off_t hash; + size_t bsiz = BUFSIZ; + + if ((dp = dbuf = malloc(bsiz)) == NULL) + err(1, "malloc"); + edp = dbuf + bsiz; + + while ((c = *cp++) != '\0') { + switch (c) { + + case '"': + /* Look for a concatenated string */ + for (;;) { + while (isspace((unsigned char)*cp)) + cp++; + if (*cp == '\0') { + if (fgets(linebuf, + sizeof linebuf, stdin) == NULL) { + if (ferror(stdin)) + err(1, + "Error reading `x.c'"); + goto out; + } + cp = linebuf; + } else { + if (*cp == '"') { + cp++; + if (*cp == '"') { + cp++; + continue; + } else { + c = *cp++; + goto gotc; + } + } else { + cp++; + goto out; + } + } + } + /*NOTREACHED*/ + case '\\': + c = *cp++; + if (c == 0) + break; + if (c == '\n') { + if (fgets(linebuf, sizeof linebuf, stdin) + == NULL) { + if (ferror(stdin)) + err(1, "Error reading `x.c'"); + return(-1); + } + cp = linebuf; + continue; + } + for (tp = "b\bt\tr\rn\nf\f\\\\\"\""; (ch = *tp++); tp++) + if (c == ch) { + c = *tp; + goto gotc; + } + if (!octdigit(c)) { + *dp++ = '\\'; + break; + } + c -= '0'; + if (!octdigit(*cp)) + break; + c <<= 3, c += *cp++ - '0'; + if (!octdigit(*cp)) + break; + c <<= 3, c += *cp++ - '0'; + break; + } +gotc: + if (dp >= edp - 1) { + char *nbuf; + bsiz += BUFSIZ; + if ((nbuf = realloc(dbuf, bsiz)) == NULL) { + free(dbuf); + err(1, "realloc"); + } + dp = nbuf + (dp - dbuf); + edp = nbuf + bsiz; + dbuf = nbuf; + } + *dp++ = c; + } +out: + *cpp = --cp; + *dp = '\0'; + hash = hashit(dbuf, 1); + free(dbuf); + return hash; +} + +static int +octdigit(char c) +{ + + return (isdigit((unsigned char)c) && c != '8' && c != '9'); +} + +static void +inithash(void) +{ + char buf[BUFSIZ]; + FILE *mesgread = fopen(strings, "r"); + + if (mesgread == NULL) + return; + for (;;) { + mesgpt = tellpt; + if (fgetNUL(buf, sizeof buf, mesgread) == 0) + break; + (void)hashit(buf, 0); + } + (void)fclose(mesgread); +} + +static int +fgetNUL(char *obuf, int rmdr, FILE *file) +{ + int c; + char *buf = obuf; + + c = 0; /* XXXGCC -Wuninitialized */ + + while (--rmdr > 0 && (c = xgetc(file) != 0 && c != EOF)) + *buf++ = c; + *buf++ = 0; + return (feof(file) || ferror(file)) ? 0 : 1; +} + +static int +xgetc(FILE *file) +{ + + tellpt++; + return getc(file); +} + + +static off_t +hashit(const char *str, int new) +{ + int i; + struct hash *hp, *hp0; + + hp = hp0 = &bucket[lastchr(str) & 0177]; + while (hp->hnext) { + hp = hp->hnext; + i = istail(str, hp->hstr); + if (i >= 0) + return (hp->hpt + i); + } + if ((hp = calloc(1, sizeof (*hp))) == NULL) + err(1, NULL); + hp->hpt = mesgpt; + if ((hp->hstr = strdup(str)) == NULL) + err(1, NULL); + mesgpt += strlen(hp->hstr) + 1; + hp->hnext = hp0->hnext; + hp->hnew = new; + hp0->hnext = hp; + return (hp->hpt); +} + +static void +flushsh(void) +{ + int i; + struct hash *hp; + FILE *mesgwrit; + int old = 0, new = 0; + + for (i = 0; i < BUCKETS; i++) + for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext) + if (hp->hnew) + new++; + else + old++; + if (new == 0 && old != 0) + return; + mesgwrit = fopen(strings, old ? "r+" : "w"); + if (mesgwrit == NULL) + err(1, "Cannot open `%s'", strings); + for (i = 0; i < BUCKETS; i++) + for (hp = bucket[i].hnext; hp != NULL; hp = hp->hnext) { + found(hp->hnew, hp->hpt, hp->hstr); + if (hp->hnew) { + (void)fseek(mesgwrit, hp->hpt, 0); + (void)fwrite(hp->hstr, strlen(hp->hstr) + 1, 1, + mesgwrit); + if (ferror(mesgwrit)) + err(1, "Error writing `%s'", strings); + } + } + if (fclose(mesgwrit) == EOF) + err(1, "Error closing `%s'", strings); +} + +static void +found(int new, off_t off, const char *str) +{ + if (vflg == 0) + return; + if (!new) + (void)fprintf(stderr, "found at %d:", (int) off); + else + (void)fprintf(stderr, "new at %d:", (int) off); + prstr(str); + (void)fprintf(stderr, "\n"); +} + +static void +prstr(const char *cp) +{ + int c; + + while ((c = (*cp++ & 0377)) != '\0') + if (c < ' ') + (void)fprintf(stderr, "^%c", c + '`'); + else if (c == 0177) + (void)fprintf(stderr, "^?"); + else if (c > 0200) + (void)fprintf(stderr, "\\%03o", c); + else + (void)fprintf(stderr, "%c", c); +} + +static void +xsdotc(void) +{ + FILE *strf = fopen(strings, "r"); + FILE *xdotcf; + + if (strf == NULL) + err(1, "Cannot open `%s'", strings); + xdotcf = fopen("xs.c", "w"); + if (xdotcf == NULL) + err(1, "Cannot open `%s'", "xs.c"); + (void)fprintf(xdotcf, "char\t%s[] = {\n", array); + for (;;) { + int i, c; + + for (i = 0; i < 8; i++) { + c = getc(strf); + if (ferror(strf)) { + warn("Error reading `%s'", strings); + onintr(1); + } + if (feof(strf)) { + (void)fprintf(xdotcf, "\n"); + goto out; + } + (void)fprintf(xdotcf, "0x%02x,", c); + } + (void)fprintf(xdotcf, "\n"); + } +out: + (void)fprintf(xdotcf, "};\n"); + (void)fclose(xdotcf); + (void)fclose(strf); +} + +static char +lastchr(const char *cp) +{ + + while (cp[0] && cp[1]) + cp++; + return (*cp); +} + +static int +istail(const char *str, const char *of) +{ + int d = strlen(of) - strlen(str); + + if (d < 0 || strcmp(&of[d], str) != 0) + return (-1); + return (d); +} + +static void +onintr(int dummy) +{ + + (void)signal(SIGINT, SIG_IGN); + if (strings[0] == '/') + (void)unlink(strings); + (void)unlink("x.c"); + (void)unlink("xs.c"); + exit(dummy); +} + +static void +usage(void) +{ + + (void)fprintf(stderr, "usage: %s [-cv] [-l array] [-] [ ...]\n", + getprogname()); + exit(1); +}