diff options
Diffstat (limited to 'buildrump.sh/src/usr.bin')
282 files changed, 80277 insertions, 0 deletions
diff --git a/buildrump.sh/src/usr.bin/Makefile.inc b/buildrump.sh/src/usr.bin/Makefile.inc new file mode 100644 index 00000000..1c512e0a --- /dev/null +++ b/buildrump.sh/src/usr.bin/Makefile.inc @@ -0,0 +1,5 @@ +# $NetBSD: Makefile.inc,v 1.10 2012/03/21 05:47:53 matt Exp $ +# from: @(#)Makefile.inc 8.1 (Berkeley) 6/6/93 + +WARNS?= 5 +BINDIR?=/usr/bin diff --git a/buildrump.sh/src/usr.bin/cksum/Makefile b/buildrump.sh/src/usr.bin/cksum/Makefile new file mode 100644 index 00000000..2f04928f --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/Makefile @@ -0,0 +1,20 @@ +# $NetBSD: Makefile,v 1.16 2009/04/14 22:15:18 lukem Exp $ +# @(#)Makefile 8.2 (Berkeley) 4/28/95 + +PROG= cksum +SRCS= cksum.c crc.c md2.c md4.c md5.c sha1.c rmd160.c print.c sum1.c sum2.c +SRCS+= sha256.c sha384.c sha512.c +LINKS= ${BINDIR}/cksum ${BINDIR}/sum +LINKS+= ${BINDIR}/cksum ${BINDIR}/md2 +LINKS+= ${BINDIR}/cksum ${BINDIR}/md4 +LINKS+= ${BINDIR}/cksum ${BINDIR}/md5 +LINKS+= ${BINDIR}/cksum ${BINDIR}/sha1 +LINKS+= ${BINDIR}/cksum ${BINDIR}/rmd160 +MLINKS= cksum.1 sum.1 +MLINKS+=cksum.1 md2.1 +MLINKS+=cksum.1 md4.1 +MLINKS+=cksum.1 md5.1 +MLINKS+=cksum.1 sha1.1 +MLINKS+=cksum.1 rmd160.1 + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/cksum/cksum.1 b/buildrump.sh/src/usr.bin/cksum/cksum.1 new file mode 100644 index 00000000..3de15fd3 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/cksum.1 @@ -0,0 +1,341 @@ +.\" $NetBSD: cksum.1,v 1.47 2014/08/31 07:23:53 wiz Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)cksum.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd August 31, 2014 +.Dt CKSUM 1 +.Os +.Sh NAME +.Nm cksum , +.Nm md2 , +.Nm md4 , +.Nm md5 , +.Nm rmd160 , +.Nm sha1 , +.Nm sum +.Nd display file checksums and block counts +.Sh SYNOPSIS +.Nm cksum +.Op Fl n +.Op Fl a Ar algorithm Oo Fl pqtx Oc Oo Fl s Ar string Oc +.Op Fl o Ar 1 Ns | Ns Ar 2 +.Op Ar Li \&| Fl c Oo Fl w Oc Oo Ar sumfile Oc +.Nm sum +.Op Fl n +.Op Fl a Ar algorithm Oo Fl pqtx Oc Oo Fl s Ar string Oc +.Op Fl o Ar 1 Ns | Ns Ar 2 +.Op Ar Li \&| Fl c Oo Fl w Oc Oo Ar sumfile Oc +.Nm md2 +.Op Fl npqtx +.Op Fl s Ar string +.Op Ar Li \&| Fl c Oo Fl w Oc Oo Ar sumfile Oc +.Nm md4 +.Op Fl npqtx +.Op Fl s Ar string +.Op Ar Li \&| Fl c Oo Fl w Oc Oo Ar sumfile Oc +.Nm md5 +.Op Fl npqtx +.Op Fl s Ar string +.Op Ar Li \&| Fl c Oo Fl w Oc Oo Ar sumfile Oc +.Nm rmd160 +.Op Fl npqtx +.Op Fl s Ar string +.Op Ar Li \&| Fl c Oo Fl w Oc Oo Ar sumfile Oc +.Nm sha1 +.Op Fl npqtx +.Op Fl s Ar string +.Op Ar Li \&| Fl c Oo Fl w Oc Oo Ar sumfile Oc +.Sh DESCRIPTION +The +.Nm +utility writes to the standard output three whitespace separated +fields for each input file. +These fields are a checksum +.Tn CRC , +the total number of octets in the file and the file name. +If no file name is specified, the standard input is used and no file name +is written. +.Pp +The +.Nm sum +utility is identical to the +.Nm +utility, except that it defaults to using historic algorithm 1, as +described below. +It is provided for compatibility only. +.Pp +The +.Nm md2 , +.Nm md4 , +.Nm md5 , +.Nm sha1 , +and +.Nm rmd160 +utilities compute cryptographic hash functions, and write to standard +output the hexadecimal representation of the hash of their input. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl a Ar algorithm +When invoked as +.Nm cksum , +use the specified +.Ar algorithm . +Valid algorithms are: +.Bl -column -offset indent ".Sy Algorithm" ".Sy Bits" ".Sy Description" +.It Sy Algorithm Ta Sy Bits Ta Sy Description +.It Li CRC Ta 32 Ta Default CRC algorithm +.It Li MD2 Ta 128 Ta MD2, per Li RFC1319 +.It Li MD4 Ta 128 Ta MD4, per Li RFC1320 +.It Li MD5 Ta 128 Ta MD5, per Li RFC1321 +.It Li RMD160 Ta 160 Ta RIPEMD-160 +.It Li SHA1 Ta 160 Ta SHA-1, per Li FIPS PUB 180-1 +.It Li SHA256 Ta 256 Ta SHA-2 +.It Li SHA384 Ta 384 Ta SHA-2 +.It Li SHA512 Ta 512 Ta SHA-2 +.It Li old1 Ta 16 Ta Algorithm 1, per Fl o Ar 1 +.It Li old2 Ta 16 Ta Algorithm 2, per Fl o Ar 2 +.El +.It Fl c Op Ar sumfile +Verify (check) files against a list of checksums. +The list is read from +.Ar sumfile , +or from stdin if no filename is given. +E.g. first run +.Dl Ic md5 *.tgz \*[Gt] MD5 +.Dl Ic sha1 *.tgz \*[Gt] SHA1 +to generate a list of MD5 checksums in +.Pa MD5 , +then use the following command to verify them: +.Dl Ic cat MD5 SHA1 | cksum -c +If an error is found during checksum verification, an error +message is printed, and the program returns an error code of 1. +.It Fl o +Use historic algorithms instead of the (superior) default one. +.Pp +Algorithm 1 is the algorithm used by historic +.Bx +systems as the +.Xr sum 1 +algorithm and by historic +.At V +systems as the +.Xr sum 1 +algorithm when using the +.Fl r +option. +This is a 16-bit checksum, with a right rotation before each addition; +overflow is discarded. +.Pp +Algorithm 2 is the algorithm used by historic +.At V +systems as the +default +.Xr sum 1 +algorithm. +This is a 32-bit checksum, and is defined as follows: +.Bd -unfilled -offset indent +s = sum of all bytes; +r = s % 2^16 + (s % 2^32) / 2^16; +cksum = (r % 2^16) + r / 2^16; +.Ed +.Pp +Both algorithm 1 and 2 write to the standard output the same fields as +the default algorithm except that the size of the file in bytes is +replaced with the size of the file in blocks. +For historic reasons, the block size is 1024 for algorithm 1 and 512 +for algorithm 2. +Partial blocks are rounded up. +.It Fl w +Print warnings about malformed checksum files when verifying +checksums with +.Fl c . +.El +.Pp +The following options apply only when using the one of the message +digest algorithms: +.Bl -tag -width indent +.It Fl n +Print the hash and the filename in the normal sum output form, with +the hash at the left and the filename following on the right. +.It Fl p +Echo input from standard input to standard output, and append the +selected message digest. +.It Fl q +Quiet mode \(em only the checksum is printed out. +Overrides the +.Fl n +option. +.It Fl s Ar string +Print the hash of the given string +.Ar string . +.It Fl t +Run a built-in message digest time trial. +.It Fl x +Run a built-in message digest test script. +The tests that are run +are supposed to encompass all the various tests in the suites that +accompany the algorithms' descriptions with the exception of the +last test for the SHA-1 algorithm and the RIPEMD-160 algorithm. +The +last test for these is one million copies of the lower letter a. +.El +.Pp +The default +.Tn CRC +used is based on the polynomial used for +.Tn CRC +error checking +in the networking standard +.St -iso8802-3 . +The +.Tn CRC +checksum encoding is defined by the generating polynomial: +.Pp +.Bd -unfilled -offset indent +G(x) = x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 1 +.Ed +.Pp +Mathematically, the +.Tn CRC +value corresponding to a given file is defined by +the following procedure: +.Bd -filled -offset indent +The +.Ar n +bits to be evaluated are considered to be the coefficients of a mod 2 +polynomial M(x) of degree +.Ar n Ns \-1 . +These +.Ar n +bits are the bits from the file, with the most significant bit being the most +significant bit of the first octet of the file and the last bit being the least +significant bit of the last octet, padded with zero bits (if necessary) to +achieve an integral number of octets, followed by one or more octets +representing the length of the file as a binary value, least significant octet +first. +The smallest number of octets capable of representing this integer are used. +.Pp +M(x) is multiplied by x^32 (i.e., shifted left 32 bits) and divided by +G(x) using mod 2 division, producing a remainder R(x) of degree \*[Le] 31. +.Pp +The coefficients of R(x) are considered to be a 32-bit sequence. +.Pp +The bit sequence is complemented and the result is the CRC. +.Ed +.Pp +The +.Nm +and +.Nm sum +utilities exit 0 on success, and \*[Gt]0 if an error occurs. +.Sh SEE ALSO +.Xr openssl 1 , +.Xr mtree 8 +.Pp +The default calculation is identical to that given in pseudo-code +in the following +.Tn ACM +article. +.Rs +.%T "Computation of Cyclic Redundancy Checks Via Table Lookup" +.%A Dilip V. Sarwate +.%J "Communications of the ACM" +.%D "August 1988" +.Re +.Rs +.%A R. Rivest +.%T The MD2 Message-Digest Algorithm +.%O RFC 1319 +.Re +.Rs +.%A R. Rivest +.%T The MD4 Message-Digest Algorithm +.%O RFC 1186 and RFC 1320 +.Re +.Rs +.%A R. Rivest +.%T The MD5 Message-Digest Algorithm +.%O RFC 1321 +.Re +.Rs +.%A U.S. DOC/NIST +.%T Secure Hash Standard +.%O FIPS PUB 180-1 +.Re +.Sh STANDARDS +The +.Nm +utility is expected to conform to +.St -p1003.1-2004 . +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.4 . +.Nm md5 +was added in +.Nx 1.3 . +The functionality for +.Nm md2 , +.Nm md4 , +.Nm sha1 , +and +.Nm rmd160 +was added in +.Nx 1.6 . +Support for the SHA-2 algorithms +.Po +.Li SHA256 , +.Li SHA384 , +and +.Li SHA512 +.Pc +was added in +.Nx 3.0 . +The functionality to verify checksum stored in a file +.Pq Fl c +first appeared in +.Nx 4.0 . +Quiet mode +.Pq Fl q +was added in +.Nx 7.0 . +.\" .Pp +.\" The +.\" .Nm sum +.\" utility appeared in +.\" .Bx ?.? +.\" and +.\" .At V . diff --git a/buildrump.sh/src/usr.bin/cksum/cksum.c b/buildrump.sh/src/usr.bin/cksum/cksum.c new file mode 100644 index 00000000..0aaa623b --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/cksum.c @@ -0,0 +1,551 @@ +/* $NetBSD: cksum.c,v 1.47 2014/08/31 07:05:33 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * 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. + */ + +/*- + * Copyright (c) 1997 Jason R. Thorpe. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__COPYRIGHT) && !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1991, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)cksum.c 8.2 (Berkeley) 4/28/95"; +#endif +__RCSID("$NetBSD: cksum.c,v 1.47 2014/08/31 07:05:33 christos Exp $"); +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <locale.h> +#include <md2.h> +#include <md4.h> +#include <md5.h> +#include <rmd160.h> +#include <sha1.h> +#include <sha2.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define PRINT_NORMAL 0x01 +#define PRINT_QUIET 0x02 + +typedef char *(*_filefunc)(const char *, char *); + +const struct hash { + const char *progname; + const char *hashname; + void (*stringfunc)(const char *); + void (*timetrialfunc)(void); + void (*testsuitefunc)(void); + void (*filterfunc)(int); + char *(*filefunc)(const char *, char *); +} hashes[] = { + { "md2", "MD2", + MD2String, MD2TimeTrial, MD2TestSuite, + MD2Filter, MD2File }, + { "md4", "MD4", + MD4String, MD4TimeTrial, MD4TestSuite, + MD4Filter, MD4File }, + { "md5", "MD5", + MD5String, MD5TimeTrial, MD5TestSuite, + MD5Filter, MD5File }, + { "rmd160", "RMD160", + RMD160String, RMD160TimeTrial, RMD160TestSuite, + RMD160Filter, (_filefunc) RMD160File }, + { "sha1", "SHA1", + SHA1String, SHA1TimeTrial, SHA1TestSuite, + SHA1Filter, (_filefunc) SHA1File }, + { "sha256", "SHA256", + SHA256_String, SHA256_TimeTrial, SHA256_TestSuite, + SHA256_Filter, (_filefunc) SHA256_File }, + { "sha384", "SHA384", + SHA384_String, SHA384_TimeTrial, SHA384_TestSuite, + SHA384_Filter, (_filefunc) SHA384_File }, + { "sha512", "SHA512", + SHA512_String, SHA512_TimeTrial, SHA512_TestSuite, + SHA512_Filter, (_filefunc) SHA512_File }, + { .progname = NULL, }, +}; + +static int hash_digest_file(char *, const struct hash *, int); +__dead static void requirehash(const char *); +__dead static void usage(void); + +int +main(int argc, char **argv) +{ + int ch, fd, rval, pflag, nohashstdin; + u_int32_t val; + off_t len; + char *fn; + const char *progname; + int (*cfncn) (int, u_int32_t *, off_t *); + void (*pfncn) (char *, u_int32_t, off_t); + const struct hash *hash; + int i, check_warn, do_check; + int print_flags; + + cfncn = NULL; + pfncn = NULL; + pflag = nohashstdin = 0; + check_warn = 0; + do_check = 0; + print_flags = 0; + + setlocale(LC_ALL, ""); + + progname = getprogname(); + + for (hash = hashes; hash->hashname != NULL; hash++) + if (strcmp(progname, hash->progname) == 0) + break; + + if (hash->hashname == NULL) { + hash = NULL; + + if (!strcmp(progname, "sum")) { + cfncn = csum1; + pfncn = psum1; + } else { + cfncn = crc; + pfncn = pcrc; + } + } + + while ((ch = getopt(argc, argv, "a:cno:pqs:twx")) != -1) + switch(ch) { + case 'a': + if (hash) { + warnx("illegal use of -a option\n"); + usage(); + } + i = 0; + while (hashes[i].hashname != NULL) { + if (!strcasecmp(hashes[i].hashname, optarg)) { + hash = &hashes[i]; + break; + } + i++; + } + if (hash == NULL) { + if (!strcasecmp(optarg, "old1")) { + cfncn = csum1; + pfncn = psum1; + } else if (!strcasecmp(optarg, "old2")) { + cfncn = csum2; + pfncn = psum2; + } else if (!strcasecmp(optarg, "crc")) { + cfncn = crc; + pfncn = pcrc; + } else { + warnx("illegal argument to -a option"); + usage(); + } + } + break; + case 'c': + do_check = 1; + break; + case 'n': + print_flags |= PRINT_NORMAL; + break; + case 'o': + if (hash) { + warnx("%s mutually exclusive with sum", + hash->hashname); + usage(); + } + if (!strcmp(optarg, "1")) { + cfncn = csum1; + pfncn = psum1; + } else if (!strcmp(optarg, "2")) { + cfncn = csum2; + pfncn = psum2; + } else { + warnx("illegal argument to -o option"); + usage(); + } + break; + case 'p': + if (hash == NULL) + requirehash("-p"); + pflag = 1; + break; + case 'q': + print_flags |= PRINT_QUIET; + break; + case 's': + if (hash == NULL) + requirehash("-s"); + nohashstdin = 1; + hash->stringfunc(optarg); + break; + case 't': + if (hash == NULL) + requirehash("-t"); + nohashstdin = 1; + hash->timetrialfunc(); + break; + case 'w': + check_warn = 1; + break; + case 'x': + if (hash == NULL) + requirehash("-x"); + nohashstdin = 1; + hash->testsuitefunc(); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (do_check) { + /* + * Verify checksums + */ + FILE *f; + char buf[BUFSIZ]; + char *s, *p_filename, *p_cksum; + int l_filename, l_cksum; + char filename[BUFSIZ]; + char cksum[BUFSIZ]; + int ok,cnt,badcnt; + + rval = 0; + cnt = badcnt = 0; + + if (argc == 0) { + f = fdopen(STDIN_FILENO, "r"); + } else { + f = fopen(argv[0], "r"); + } + if (f == NULL) + err(1, "Cannot read %s", + argc>0?argv[0]:"stdin"); + + while(fgets(buf, sizeof(buf), f) != NULL) { + s=strrchr(buf, '\n'); + if (s) + *s = '\0'; + + p_cksum = p_filename = NULL; + + p_filename = strchr(buf, '('); + if (p_filename) { + /* + * Assume 'normal' output if there's a '(' + */ + p_filename += 1; + print_flags &= ~(PRINT_NORMAL); + + p_cksum = strrchr(p_filename, ')'); + if (p_cksum == NULL) { + if (check_warn) + warnx("bogus format: %s. " + "Skipping...", + buf); + rval = 1; + continue; + } + p_cksum += 4; + + l_cksum = strlen(p_cksum); + l_filename = p_cksum - p_filename - 4; + + /* Sanity check, and find proper hash if + * it's not the same as the current program + */ + if (hash == NULL || + strncmp(buf, hash->hashname, + strlen(hash->hashname)) != 0) { + /* + * Search proper hash + */ + const struct hash *nhash; + + for (nhash = hashes ; + nhash->hashname != NULL; + nhash++) + if (strncmp(buf, + nhash->hashname, + strlen(nhash->hashname)) == 0) + break; + + + if (nhash->hashname == NULL) { + if (check_warn) + warnx("unknown hash: %s", + buf); + rval = 1; + continue; + } else { + hash = nhash; + } + } + + } else { + if (hash) { + int nspaces; + + /* + * 'normal' output, no (ck)sum + */ + print_flags |= PRINT_NORMAL; + nspaces = 1; + + p_cksum = buf; + p_filename = strchr(buf, ' '); + if (p_filename == NULL) { + if (check_warn) + warnx("no filename in %s? " + "Skipping...", buf); + rval = 1; + continue; + } + while (isspace((int)*++p_filename)) + nspaces++; + l_filename = strlen(p_filename); + l_cksum = p_filename - buf - nspaces; + } else { + /* + * sum/cksum output format + */ + p_cksum = buf; + s=strchr(p_cksum, ' '); + if (s == NULL) { + if (check_warn) + warnx("bogus format: %s." + " Skipping...", + buf); + rval = 1; + continue; + } + l_cksum = s - p_cksum; + + p_filename = strrchr(buf, ' '); + if (p_filename == NULL) { + if (check_warn) + warnx("no filename in %s?" + " Skipping...", + buf); + rval = 1; + continue; + } + p_filename++; + l_filename = strlen(p_filename); + } + } + + strlcpy(filename, p_filename, l_filename+1); + strlcpy(cksum, p_cksum, l_cksum+1); + + if (hash) { + if (access(filename, R_OK) == 0 + && strcmp(cksum, hash->filefunc(filename, NULL)) == 0) + ok = 1; + else + ok = 0; + } else { + if ((fd = open(filename, O_RDONLY, 0)) < 0) { + if (check_warn) + warn("%s", filename); + rval = 1; + ok = 0; + } else { + if (cfncn(fd, &val, &len)) + ok = 0; + else { + u_int32_t should_val; + + should_val = + strtoul(cksum, NULL, 10); + if (val == should_val) + ok = 1; + else + ok = 0; + } + close(fd); + } + } + + if (! ok) { + if (hash) + printf("(%s) ", hash->hashname); + printf("%s: FAILED\n", filename); + badcnt++; + } + cnt++; + + } + fclose(f); + + if (badcnt > 0) + rval = 1; + + } else { + /* + * Calculate checksums + */ + + fd = STDIN_FILENO; + fn = NULL; + rval = 0; + do { + if (*argv) { + fn = *argv++; + if (hash != NULL) { + if (hash_digest_file(fn, hash, print_flags)) { + warn("%s", fn); + rval = 1; + } + continue; + } + if ((fd = open(fn, O_RDONLY, 0)) < 0) { + warn("%s", fn); + rval = 1; + continue; + } + } else if (hash && !nohashstdin) { + hash->filterfunc(pflag); + } + + if (hash == NULL) { + if (cfncn(fd, &val, &len)) { + warn("%s", fn ? fn : "stdin"); + rval = 1; + } else + pfncn(fn, val, len); + (void)close(fd); + } + } while (*argv); + } + exit(rval); +} + +static int +hash_digest_file(char *fn, const struct hash *hash, int flags) +{ + char *cp; + + cp = hash->filefunc(fn, NULL); + if (cp == NULL) + return 1; + + if (flags & PRINT_QUIET) + printf("%s\n", cp); + else if (flags & PRINT_NORMAL) + printf("%s %s\n", cp, fn); + else + printf("%s (%s) = %s\n", hash->hashname, fn, cp); + + free(cp); + + return 0; +} + +static void +requirehash(const char *flg) +{ + warnx("%s flag requires `-a algorithm'", flg); + usage(); +} + +static void +usage(void) +{ + const char fileargs[] = "[file ... | -c [-w] [sumfile]]"; + const char sumargs[] = "[-n] [-a algorithm [-ptx] [-s string]] [-o 1|2]"; + const char hashargs[] = "[-nptx] [-s string]"; + + (void)fprintf(stderr, "usage: cksum %s\n %s\n", + sumargs, fileargs); + (void)fprintf(stderr, " sum %s\n %s\n", + sumargs, fileargs); + (void)fprintf(stderr, " md2 %s %s\n", hashargs, fileargs); + (void)fprintf(stderr, " md4 %s %s\n", hashargs, fileargs); + (void)fprintf(stderr, " md5 %s %s\n", hashargs, fileargs); + (void)fprintf(stderr, " rmd160 %s %s\n", hashargs, fileargs); + (void)fprintf(stderr, " sha1 %s %s\n", hashargs, fileargs); + exit(1); +} diff --git a/buildrump.sh/src/usr.bin/cksum/crc.c b/buildrump.sh/src/usr.bin/cksum/crc.c new file mode 100644 index 00000000..b3979db5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/crc.c @@ -0,0 +1,162 @@ +/* $NetBSD: crc.c,v 1.19 2014/10/29 18:09:35 uebayasi Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James W. Williams of NASA Goddard Space Flight Center. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)crc.c 8.1 (Berkeley) 6/17/93"; +#else +__RCSID("$NetBSD: crc.c,v 1.19 2014/10/29 18:09:35 uebayasi Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <unistd.h> + +#include "extern.h" + +static const u_int32_t crctab[] = { + 0x0, + 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b, + 0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, + 0x2b4bcb61, 0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, + 0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9, 0x5f15adac, + 0x5bd4b01b, 0x569796c2, 0x52568b75, 0x6a1936c8, 0x6ed82b7f, + 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3, 0x709f7b7a, + 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039, + 0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, + 0xbaea46ef, 0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, + 0xa4ad16ea, 0xa06c0b5d, 0xd4326d90, 0xd0f37027, 0xddb056fe, + 0xd9714b49, 0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95, + 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1, 0xe13ef6f4, + 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0, + 0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, + 0x2ac12072, 0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, + 0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca, 0x7897ab07, + 0x7c56b6b0, 0x71159069, 0x75d48dde, 0x6b93dddb, 0x6f52c06c, + 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, + 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba, + 0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, + 0xbb60adfc, 0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, + 0x832f1041, 0x87ee0df6, 0x99a95df3, 0x9d684044, 0x902b669d, + 0x94ea7b2a, 0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e, + 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2, 0xc6bcf05f, + 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34, + 0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, + 0x644fc637, 0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, + 0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f, 0x5c007b8a, + 0x58c1663d, 0x558240e4, 0x51435d53, 0x251d3b9e, 0x21dc2629, + 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5, 0x3f9b762c, + 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff, + 0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, + 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, + 0xeba91bbc, 0xef68060b, 0xd727bbb6, 0xd3e6a601, 0xdea580d8, + 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3, + 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7, 0xae3afba2, + 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71, + 0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, + 0x857130c3, 0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, + 0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c, 0x7b827d21, + 0x7f436096, 0x7200464f, 0x76c15bf8, 0x68860bfd, 0x6c47164a, + 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e, 0x18197087, + 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec, + 0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, + 0x2056cd3a, 0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, + 0xcc2b1d17, 0xc8ea00a0, 0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, + 0xdbee767c, 0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18, + 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4, 0x89b8fd09, + 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662, + 0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, + 0xa2f33668, 0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 +}; + +/* + * Compute a POSIX 1003.2 checksum. This routine has been broken out so that + * other programs can use it. It takes a file descriptor to read from and + * locations to store the crc and the number of bytes read. It returns 0 on + * success and 1 on failure. Errno is set on failure. + */ +int +crc(int fd, u_int32_t *cval, off_t *clen) +{ + u_char *p; + ssize_t nr; + u_int32_t thecrc; + off_t len; + u_char buf[16 * 1024]; + +#define COMPUTE(var, ch) (var) = (var) << 8 ^ crctab[(var) >> 24 ^ (ch)] + + thecrc = 0; + len = 0; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (len += nr, p = buf; nr--; ++p) { + COMPUTE(thecrc, *p); + } + if (nr < 0) + return 1; + + *clen = len; + + /* Include the length of the file. */ + for (; len != 0; len >>= 8) { + COMPUTE(thecrc, len & 0xff); + } + + *cval = ~thecrc; + return 0; +} + +/* These two are rather more useful to the outside world */ + +uint32_t +crc_buf(uint32_t thecrc, const void *buf, size_t len) +{ + const uint8_t *p = buf; + + for (p = buf; len; p++, len--) + COMPUTE(thecrc, *p); + return thecrc; +} + +uint32_t +crc_byte(uint32_t thecrc, unsigned int byte_val) +{ + COMPUTE(thecrc, byte_val & 0xff); + return thecrc; +} diff --git a/buildrump.sh/src/usr.bin/cksum/crc_extern.h b/buildrump.sh/src/usr.bin/cksum/crc_extern.h new file mode 100644 index 00000000..b0c1c33d --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/crc_extern.h @@ -0,0 +1,38 @@ +/* $NetBSD: crc_extern.h,v 1.1 2006/09/04 20:01:10 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +__BEGIN_DECLS +int crc(int, u_int32_t *, off_t *); +uint32_t crc_buf(uint32_t, const void *, size_t); +uint32_t crc_byte(uint32_t, unsigned int); +__END_DECLS diff --git a/buildrump.sh/src/usr.bin/cksum/extern.h b/buildrump.sh/src/usr.bin/cksum/extern.h new file mode 100644 index 00000000..8384110c --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/extern.h @@ -0,0 +1,88 @@ +/* $NetBSD: extern.h,v 1.19 2006/09/04 20:01:10 dsl Exp $ */ + +/*- + * Copyright (c) 1991, 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#include "crc_extern.h" + +__BEGIN_DECLS +void pcrc(char *, u_int32_t, off_t); +void psum1(char *, u_int32_t, off_t); +void psum2(char *, u_int32_t, off_t); +int csum1(int, u_int32_t *, off_t *); +int csum2(int, u_int32_t *, off_t *); +int md5(int, u_int32_t *, u_int32_t *); + +void MD2String(const char *); +void MD2TimeTrial(void); +void MD2TestSuite(void); +void MD2Filter(int); + +void MD4String(const char *); +void MD4TimeTrial(void); +void MD4TestSuite(void); +void MD4Filter(int); + +void MD5String(const char *); +void MD5TimeTrial(void); +void MD5TestSuite(void); +void MD5Filter(int); + +void SHA1String(const char *); +void SHA1TimeTrial(void); +void SHA1TestSuite(void); +void SHA1Filter(int); + +void RMD160String(const char *); +void RMD160TimeTrial(void); +void RMD160TestSuite(void); +void RMD160Filter(int); + +void SHA256_String(const char *); +void SHA256_TimeTrial(void); +void SHA256_TestSuite(void); +void SHA256_Filter(int); + +void SHA384_String(const char *); +void SHA384_TimeTrial(void); +void SHA384_TestSuite(void); +void SHA384_Filter(int); + +void SHA512_String(const char *); +void SHA512_TimeTrial(void); +void SHA512_TestSuite(void); +void SHA512_Filter(int); +__END_DECLS diff --git a/buildrump.sh/src/usr.bin/cksum/md2.c b/buildrump.sh/src/usr.bin/cksum/md2.c new file mode 100644 index 00000000..cc80b4a2 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/md2.c @@ -0,0 +1,21 @@ +/* $NetBSD: md2.c,v 1.1 2001/03/20 18:46:26 atatat Exp $ */ + +#include <md2.h> /* this hash type */ +#include <md5.h> /* the hash we're replacing */ + +#define HASHTYPE "MD2" +#define HASHLEN 32 + +#define MD5Filter MD2Filter +#define MD5String MD2String +#define MD5TestSuite MD2TestSuite +#define MD5TimeTrial MD2TimeTrial + +#define MD5Data MD2Data +#define MD5Init MD2Init +#define MD5Update MD2Update +#define MD5End MD2End + +#define MD5_CTX MD2_CTX + +#include "md5.c" diff --git a/buildrump.sh/src/usr.bin/cksum/md4.c b/buildrump.sh/src/usr.bin/cksum/md4.c new file mode 100644 index 00000000..e27b4161 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/md4.c @@ -0,0 +1,21 @@ +/* $NetBSD: md4.c,v 1.1 2001/03/20 18:46:26 atatat Exp $ */ + +#include <md4.h> /* this hash type */ +#include <md5.h> /* the hash we're replacing */ + +#define HASHTYPE "MD4" +#define HASHLEN 32 + +#define MD5Filter MD4Filter +#define MD5String MD4String +#define MD5TestSuite MD4TestSuite +#define MD5TimeTrial MD4TimeTrial + +#define MD5Data MD4Data +#define MD5Init MD4Init +#define MD5Update MD4Update +#define MD5End MD4End + +#define MD5_CTX MD4_CTX + +#include "md5.c" diff --git a/buildrump.sh/src/usr.bin/cksum/md5.c b/buildrump.sh/src/usr.bin/cksum/md5.c new file mode 100644 index 00000000..5547771c --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/md5.c @@ -0,0 +1,153 @@ +/* $NetBSD: md5.c,v 1.10 2008/12/29 00:51:29 christos Exp $ */ + +/* + * MDDRIVER.C - test driver for MD2, MD4 and MD5 + */ + +/* + * Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All + * rights reserved. + * + * RSA Data Security, Inc. makes no representations concerning either + * the merchantability of this software or the suitability of this + * software for any particular purpose. It is provided "as is" + * without express or implied warranty of any kind. + * + * These notices must be retained in any copies of any part of this + * documentation and/or software. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +__RCSID("$NetBSD: md5.c,v 1.10 2008/12/29 00:51:29 christos Exp $"); +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <md5.h> +#include <stdio.h> +#include <string.h> +#include <time.h> + +void MD5Filter(int); +void MD5String(const char *); +void MD5TestSuite(void); +void MD5TimeTrial(void); + +#ifndef HASHTYPE +#define HASHTYPE "MD5" +#endif + +#ifndef HASHLEN +#define HASHLEN 32 +#endif + +/* + * Length of test block, number of test blocks. + */ +#define TEST_BLOCK_LEN 1000 +#define TEST_BLOCK_COUNT 1000 + +/* + * Digests a string and prints the result. + */ +void +MD5String(const char *string) +{ + unsigned int len = strlen(string); + char buf[HASHLEN + 1]; + + printf("%s (\"%s\") = %s\n", HASHTYPE, string, + MD5Data((const unsigned char *)string, len, buf)); +} + +/* + * Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks. + */ +void +MD5TimeTrial(void) +{ + MD5_CTX context; + time_t endTime, startTime; + unsigned char block[TEST_BLOCK_LEN]; + unsigned int i; + char *p, buf[HASHLEN + 1]; + + printf("%s time trial. Digesting %d %d-byte blocks ...", HASHTYPE, + TEST_BLOCK_LEN, TEST_BLOCK_COUNT); + fflush(stdout); + + /* Initialize block */ + for (i = 0; i < TEST_BLOCK_LEN; i++) + block[i] = (unsigned char) (i & 0xff); + + /* Start timer */ + time(&startTime); + + /* Digest blocks */ + MD5Init(&context); + for (i = 0; i < TEST_BLOCK_COUNT; i++) + MD5Update(&context, block, TEST_BLOCK_LEN); + p = MD5End(&context,buf); + + /* Stop timer */ + time(&endTime); + + printf(" done\n"); + printf("Digest = %s\n", p); + printf("Time = %ld seconds\n", (long) (endTime - startTime)); + + /* + * Be careful that endTime-startTime is not zero. + * (Bug fix from Ric * Anderson, ric@Artisoft.COM.) + */ + printf("Speed = %lld bytes/second\n", + (long long) TEST_BLOCK_LEN * TEST_BLOCK_COUNT / + ((endTime - startTime) != 0 ? (endTime - startTime) : 1)); +} + +/* + * Digests a reference suite of strings and prints the results. + */ +void +MD5TestSuite(void) +{ + printf("%s test suite:\n", HASHTYPE); + + MD5String(""); + MD5String("a"); + MD5String("abc"); + MD5String("message digest"); + MD5String("abcdefghijklmnopqrstuvwxyz"); + MD5String("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"); + MD5String + ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"); + MD5String + ("1234567890123456789012345678901234567890\ +1234567890123456789012345678901234567890"); +} + +/* + * Digests the standard input and prints the result. + */ +void +MD5Filter(int pipe) +{ + MD5_CTX context; + size_t len; + unsigned char buffer[BUFSIZ]; + char buf[HASHLEN + 1]; + + MD5Init(&context); + while ((len = fread(buffer, (size_t)1, (size_t)BUFSIZ, stdin)) > 0) { + if (pipe && (len != fwrite(buffer, (size_t)1, len, stdout))) + err(1, "stdout"); + MD5Update(&context, buffer, (unsigned int)len); + } + printf("%s\n", MD5End(&context,buf)); +} diff --git a/buildrump.sh/src/usr.bin/cksum/print.c b/buildrump.sh/src/usr.bin/cksum/print.c new file mode 100644 index 00000000..03dbd5a9 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/print.c @@ -0,0 +1,76 @@ +/* $NetBSD: print.c,v 1.11 2005/01/12 17:04:35 xtraeme Exp $ */ + +/*- + * Copyright (c) 1991, 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)print.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: print.c,v 1.11 2005/01/12 17:04:35 xtraeme Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <stdio.h> +#include "extern.h" + +void +pcrc(char *fn, u_int32_t val, off_t len) +{ + (void)printf("%lu %lld", (unsigned long)val, (long long)len); + if (fn) + (void)printf(" %s", fn); + (void)printf("\n"); +} + +void +psum1(char *fn, u_int32_t val, off_t len) +{ + (void)printf("%lu %lld", (unsigned long)val, + (long long)(len + 1023) / 1024); + if (fn) + (void)printf(" %s", fn); + (void)printf("\n"); +} + +void +psum2(char *fn, u_int32_t val, off_t len) +{ + (void)printf("%lu %lld", (unsigned long)val, + (long long)(len + 511) / 512); + if (fn) + (void)printf(" %s", fn); + (void)printf("\n"); +} diff --git a/buildrump.sh/src/usr.bin/cksum/rmd160.c b/buildrump.sh/src/usr.bin/cksum/rmd160.c new file mode 100644 index 00000000..f04a58d5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/rmd160.c @@ -0,0 +1,25 @@ +/* $NetBSD: rmd160.c,v 1.3 2006/10/30 20:22:54 christos Exp $ */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <rmd160.h> /* this hash type */ +#include <md5.h> /* the hash we're replacing */ + +#define HASHTYPE "RMD160" +#define HASHLEN 40 + +#define MD5Filter RMD160Filter +#define MD5String RMD160String +#define MD5TestSuite RMD160TestSuite +#define MD5TimeTrial RMD160TimeTrial + +#define MD5Data RMD160Data +#define MD5Init RMD160Init +#define MD5Update RMD160Update +#define MD5End RMD160End + +#define MD5_CTX RMD160_CTX + +#include "md5.c" diff --git a/buildrump.sh/src/usr.bin/cksum/sha1.c b/buildrump.sh/src/usr.bin/cksum/sha1.c new file mode 100644 index 00000000..221a6e05 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/sha1.c @@ -0,0 +1,21 @@ +/* $NetBSD: sha1.c,v 1.1 2001/03/20 18:46:27 atatat Exp $ */ + +#include <sha1.h> /* this hash type */ +#include <md5.h> /* the hash we're replacing */ + +#define HASHTYPE "SHA1" +#define HASHLEN 40 + +#define MD5Filter SHA1Filter +#define MD5String SHA1String +#define MD5TestSuite SHA1TestSuite +#define MD5TimeTrial SHA1TimeTrial + +#define MD5Data SHA1Data +#define MD5Init SHA1Init +#define MD5Update SHA1Update +#define MD5End SHA1End + +#define MD5_CTX SHA1_CTX + +#include "md5.c" diff --git a/buildrump.sh/src/usr.bin/cksum/sha256.c b/buildrump.sh/src/usr.bin/cksum/sha256.c new file mode 100644 index 00000000..41f272ee --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/sha256.c @@ -0,0 +1,25 @@ +/* $NetBSD: sha256.c,v 1.3 2006/10/30 20:22:54 christos Exp $ */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sha2.h> /* this hash type */ +#include <md5.h> /* the hash we're replacing */ + +#define HASHTYPE "SHA256" +#define HASHLEN 64 + +#define MD5Filter SHA256_Filter +#define MD5String SHA256_String +#define MD5TestSuite SHA256_TestSuite +#define MD5TimeTrial SHA256_TimeTrial + +#define MD5Data SHA256_Data +#define MD5Init SHA256_Init +#define MD5Update SHA256_Update +#define MD5End SHA256_End + +#define MD5_CTX SHA256_CTX + +#include "md5.c" diff --git a/buildrump.sh/src/usr.bin/cksum/sha384.c b/buildrump.sh/src/usr.bin/cksum/sha384.c new file mode 100644 index 00000000..845988b2 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/sha384.c @@ -0,0 +1,25 @@ +/* $NetBSD: sha384.c,v 1.3 2006/10/30 20:22:54 christos Exp $ */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sha2.h> /* this hash type */ +#include <md5.h> /* the hash we're replacing */ + +#define HASHTYPE "SHA384" +#define HASHLEN 96 + +#define MD5Filter SHA384_Filter +#define MD5String SHA384_String +#define MD5TestSuite SHA384_TestSuite +#define MD5TimeTrial SHA384_TimeTrial + +#define MD5Data SHA384_Data +#define MD5Init SHA384_Init +#define MD5Update SHA384_Update +#define MD5End SHA384_End + +#define MD5_CTX SHA384_CTX + +#include "md5.c" diff --git a/buildrump.sh/src/usr.bin/cksum/sha512.c b/buildrump.sh/src/usr.bin/cksum/sha512.c new file mode 100644 index 00000000..69dae77a --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/sha512.c @@ -0,0 +1,25 @@ +/* $NetBSD: sha512.c,v 1.3 2006/10/30 20:22:54 christos Exp $ */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sha2.h> /* this hash type */ +#include <md5.h> /* the hash we're replacing */ + +#define HASHTYPE "SHA512" +#define HASHLEN 128 + +#define MD5Filter SHA512_Filter +#define MD5String SHA512_String +#define MD5TestSuite SHA512_TestSuite +#define MD5TimeTrial SHA512_TimeTrial + +#define MD5Data SHA512_Data +#define MD5Init SHA512_Init +#define MD5Update SHA512_Update +#define MD5End SHA512_End + +#define MD5_CTX SHA512_CTX + +#include "md5.c" diff --git a/buildrump.sh/src/usr.bin/cksum/sum1.c b/buildrump.sh/src/usr.bin/cksum/sum1.c new file mode 100644 index 00000000..22e466e5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/sum1.c @@ -0,0 +1,76 @@ +/* $NetBSD: sum1.c,v 1.13 2005/02/05 00:13:34 simonb Exp $ */ + +/*- + * Copyright (c) 1991, 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)sum1.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: sum1.c,v 1.13 2005/02/05 00:13:34 simonb Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <unistd.h> + +#include "extern.h" + +int +csum1(int fd, u_int32_t *cval, off_t *clen) +{ + off_t total; + int nr; + u_int thecrc; + u_char *p; + u_char buf[8192]; + + /* + * 16-bit checksum, rotating right before each addition; + * overflow is discarded. + */ + thecrc = total = 0; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (total += nr, p = buf; nr--; ++p) { + if (thecrc & 1) + thecrc |= 0x10000; + thecrc = ((thecrc >> 1) + *p) & 0xffff; + } + if (nr < 0) + return 1; + + *cval = thecrc; + *clen = total; + return 0; +} diff --git a/buildrump.sh/src/usr.bin/cksum/sum2.c b/buildrump.sh/src/usr.bin/cksum/sum2.c new file mode 100644 index 00000000..34b3e2ec --- /dev/null +++ b/buildrump.sh/src/usr.bin/cksum/sum2.c @@ -0,0 +1,80 @@ +/* $NetBSD: sum2.c,v 1.13 2005/02/05 00:13:34 simonb Exp $ */ + +/*- + * Copyright (c) 1991, 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)sum2.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: sum2.c,v 1.13 2005/02/05 00:13:34 simonb Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <unistd.h> + +#include "extern.h" + +int +csum2(int fd, u_int32_t *cval, off_t *clen) +{ + u_int32_t thecrc; + off_t total; + int nr; + u_char *p; + u_char buf[8192]; + + /* + * Draft 8 POSIX 1003.2: + * + * s = sum of all bytes + * r = s % 2^16 + (s % 2^32) / 2^16 + * thecrc = (r % 2^16) + r / 2^16 + */ + thecrc = 0; + total = 0; + while ((nr = read(fd, buf, sizeof(buf))) > 0) + for (total += nr, p = buf; nr--; ++p) + thecrc += *p; + if (nr < 0) + return 1; + + thecrc = (thecrc & 0xffff) + (thecrc >> 16); + thecrc = (thecrc & 0xffff) + (thecrc >> 16); + + *cval = thecrc; + *clen = total; + return 0; +} diff --git a/buildrump.sh/src/usr.bin/config/Makefile b/buildrump.sh/src/usr.bin/config/Makefile new file mode 100644 index 00000000..429921ee --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/Makefile @@ -0,0 +1,27 @@ +# $NetBSD: Makefile,v 1.10 2014/10/29 19:10:49 christos Exp $ +# from: @(#)Makefile 8.2 (Berkeley) 4/19/94 + +.include <bsd.own.mk> +WARNS=6 + +PROG= config +SRCS= files.c gram.y hash.c lint.c main.c mkdevsw.c mkheaders.c mkioconf.c \ + mkmakefile.c mkswap.c pack.c scan.l sem.c util.c + +.PATH: ${NETBSDSRCDIR}/usr.bin/cksum +SRCS+= crc.c + +MAN= config.1 +MAN+= config.5 config.samples.5 +YHEADER=1 +CPPFLAGS+=-I${.CURDIR} -I. +CPPFLAGS+= -I${NETBSDSRCDIR}/usr.bin/cksum + +.ifndef HOSTPROG +LDADD+=-lutil +DPADD+=${LIBUTIL} +.endif + +CWARNFLAGS+=-Wno-format-y2k + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/config/TODO b/buildrump.sh/src/usr.bin/config/TODO new file mode 100644 index 00000000..2335814e --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/TODO @@ -0,0 +1,287 @@ +o Call module as module. + + Until now, everything is called as attribute. Separate module from it: + + - Module is a collection of code (*.[cSo]), and provides a function. + Module can depend on other modules. + + - Attribute provides metadata for modules. One module can have + multiple attributes. Attribute doesn't generate a module (*.o, + *.ko). + +o Emit everything (ioconf.*, Makefile, ...) per-attribute. + + config(9) related metadata (cfdriver, cfattach, cfdata, ...) should be + collected using linker. Create ELF sections like + .{rodata,data}.config.{cfdriver,cfattach,cfdata}. Provide reference + symbols (e.g. cfdriverinit[]) using linker script. Sort entries by name + to lookup entries by binary search in kernel. + +o Generate modular(9) related information. Especially module dependency. + + At this moment modular(9) modules hardcode dependency in *.c using the + MODULE() macro: + + MODULE(MODULE_CLASS_DRIVER, hdaudio, "pci"); + + This information already exists in config(5) definitions (files.*). + Extend config(5) to be able to specify module's class. + + Ideally these module metadata are kept somewhere in ELF headers, so that + loaders (e.g. boot(8)) can easily read. One idea is to abuse DYNAMIC + sections to record dependency, as shared library does. (Feasibility + unknown.) + +o Rename "interface attribute" to "bus". + + Instead of + + define audiobus {} + attach audio at audiobus + + Do like this + + defbus audiobus {} + attach audio at audiobus + + Always provide xxxbusprint() (and xxxbussubmatch if multiple children). + Extend struct cfiattrdata like: + + struct cfiattrdata { + const char *ci_name; + cfprint_t ci_print; + cfsubmatch_t ci_submatch; + int ci_loclen; + const struct cflocdesc ci_locdesc[]; + }; + +o Simplify child configuration API + + With said struct cfiattrdata extension, config_found*() can omit + print/submatch args. If the found child is known (e.g., "pcibus" creating + "pci"): + + config_found(self, "pcibus"); + + If finding unknown children (e.g. "pci" finding pci devices): + + config_find(self, "pci", locs, aux); + +o Retire "attach foo at bar with foo_bar.c" + + Most of these should be rewritten by defining a common interface attribute + "foobus", instead of writing multiple attachments. com(4), ld(4), ehci(4) + are typical examples. For ehci(4), EHCI-capable controller drivers implement + "ehcibus" interface, like: + + defne ehcibus {} + device imxehci: ehcibus + + These drivers' attach functions call config_found() to attach ehci(4) via + the "ehcibus" interface attribute, instead of calling ehci_init() directly. + Same for com(4) (com_attach_subr()) and ld(4) (ldattach()). + +o Sort objects in more reasonable order. + + Put machdep.ko in the lowest address. uvm.ko and kern.ko follow. + + Kill alphabetical sort (${OBJS:O} in sys/conf/Makefile.inc.kern. + + Use ldscript. Do like this + + .text : + AT (ADDR(.text) & 0x0fffffff) + { + *(.text.machdep.locore.entry) + *(.text.machdep.locore) + *(.text.machdep) + *(.text) + *(.text.*) + : + + Kill linker definitions in sys/conf/Makefile.inc.kern. + +o Differentiate "options" and "flags"/"params". + + "options" enables features by adding *.c files (via attributes). + + "flags" and "params" are to change contents of *.c files. These don't add + *.c files to the result kernel, or don't build attributes (modules). + +o Make flags/params per attributes (modules). + + Basically flags and params are cpp(1) #define's generated in opt_*.h. Make + them local to one attributes (modules). Flags/params which affects files + across attributes (modules) are possible, but should be discouraged. + +o Generate things only by definitions. + + In the ideal dynamically modular world, "selection" will be done not at + compile time but at runtime. Users select their wanted modules, by + dynamically loading them. + + This means that the system provides all choices; that is, build all modules + in the source tree. Necessary information is defined in the "definition" + part. + +o Split cfdata. + + cfdata is a set of pattern matching rules to enable devices at runtime device + auto-configuration. It is pure data and can (should) be generated separately + from the code. + +o Allow easier adding and removing of options. + + It should be possible to add or remove options, flags, etc., + without regard to whether or not they are already defined. + For example, a configuration like this: + + include GENERIC + options FOO + no options BAR + + should work regardless of whether or not options FOO and/or + options BAR were defined in GENERIC. It should not give + errors like "options BAR was already defined" or "options FOO + was not defined". + +o Introduce "class". + + Every module should be classified as at least one class, as modular(9) + modules already do. For example, file systems are marked as "vfs", network + protocols are "netproto". + + Consider to merge "devclass" into "class". + + For syntax clarity, class names could be used as a keyword to select the + class's instance module: + + # Define net80211 module as netproto class + class netproto + define net80211: netproto + + # Select net80211 to be builtin + netproto net80211 + + Accordingly device/attach selection syntax should be revisited. + +o Support kernel constructor/destructor (.kctors/.kdtors) + + Initialization and finalization should be called via constructors and + destructors. Don't hardcode those sequences as sys/kern/init_main.c:main() + does. + + The order of .kctors/.kdtors is resolved by dependency. The difference from + userland is that in kernel depended ones are located in lower addresses; + "machdep" module is the lowest. Thus the lowest entry in .ctors must be + executed the first. + + The .kctors/.kdtors entries are executed by kernel's main() function, unlike + userland where start code executes .ctors/.dtors before main(). The hardcoded + sequence of various subsystem initializations in init_main.c:main() will be + replaced by an array of .kctors invocations, and #ifdef's there will be gone. + +o Hide link-set in the final kernel. + + Link-set is used to collect references (pointers) at link time. It relys on + the ld(1) behavior that it automatically generates `__start_X' and `__stop_X' + symbols for the section `X' to reduce coding. + + Don't allow kernel subsystems create random ELF sections. + + Pre-define all the available link-set names and pre-generate a linker script + to merge them into .rodata. + + (For modular(9) modules, `link_set_modules' is looked up by kernel loader. + Provide only it.) + + Provide a way for 3rd party modules to declare extra link-set. + +o Shared kernel objects. + + Since NetBSD has not established a clear kernel ABI, every single kernel + has to build all the objects by their own. As a result, similar kernels + (e.g. evbarm kernels) repeatedly compile similar objects, that is waste of + energy & space. + + Share them if possible. For evb* ports, ideally everything except machdep.ko + should be shared. + + While leaving optimizations as options (CPU specific optimizations, inlined + bus_space(9) operations, etc.) for users, the official binaries build + provided by TNF should be as portable as possible. + +o Control ELF sections using linker script. + + Now kernel is linked and built directly from object files (*.o). Each port + has an MD linker script, which does everything needed to be done at link + time. As a result, they do from MI alignment restriction (read_mostly, + cacheline_aligned) to load address specification for external boot loaders. + + Make this into multiple stages to make linkage more structural. Especially, + reserve the final link for purely MD purpose. Note that in modular build, + *.ko are shared between build of kernel and modular(9) modules (*.kmod). + + Monolithic build: + *.o ---> netbsd.ko Generic MI linkage + netbsd.ko ---> netbsd.ro Kernel MI linkage + netbsd.ro ---> netbsd Kernel MD linkage + + Modular build (kernel): + *.o ---> *.ko Generic + Per-module MI linkage + *.ko ---> netbsd.ro Kernel MI linkage + netbsd.ro ---> netbsd Kernel MD linkage + + Modular build (module): + *.o ---> *.ko Generic + Per-module MI linkage + *.ko ---> *.ro Modular MI linkage + *.ro ---> *.kmod Modular MD linkage + + Genric MI linkage is for processing MI linkage that can be applied generally. + Data section alignment (.data.read_mostly and .data.cacheline_aligned) is + processed here. + + Per-module MI linkage is for modules that want some ordering. For example, + machdep.ko wants to put entry code at the top of .text and .data. + + Kernel MI linkage is for collecting kernel global section data, that is what + link-set is used for now. Once they are collected and symbols to the ranges + are assigned, those sections are merged into the pre-existing sections + (.rodata) because link-set sections in "netbsd" will never be interpreted by + external loaders. + + Kernel MD linkage is used purely for MD purposes, that is, how kernels are + loaded by external loaders. It might be possible that one kernel relocatable + (netbsd.ro) is linked into multiple final kernel image (netbsd) for diferent + load addresses. + + Modular MI linkage is to prepare a module to be loadable as modular(9). This + may add some extra sections and/or symbols. + + Modular MD linkage is again for pure MD purposes like kernel MD linkage. + Adjustment and/or optimization may be done. + + Kernel and modular MI linkages may change behavior depending on existence + of debug information. In the future .symtab will be copied using linker + during this stage. + +o Redesign swapnetbsd.c (root/swap device specification) + + Don't build a whole kernel only to specify root/swap devices. + + Make these parameter re-configurable afterwards. + +o Namespace. + + Investigate namespace of attributes/modules/options. Figure out the hidden + design about these, document it, then re-design it. + + At this moment, all of them share the single "selecttab", which means their + namespaces are common, but they also have respective tables (attrtab, + opttab, etc.). + + Selecting an option (addoption()), that is also a module name, works only if + the module doesn't depend on anything, because addoption() doesn't select + module and its dependencies (selectattr()). In other words, an option is + only safely converted to a module (define), only if it doesn't depend on + anything. (One example is DDB.) diff --git a/buildrump.sh/src/usr.bin/config/config.1 b/buildrump.sh/src/usr.bin/config/config.1 new file mode 100644 index 00000000..d70fb83a --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/config.1 @@ -0,0 +1,349 @@ +.\" $NetBSD: config.1,v 1.17 2014/10/10 11:58:59 wiz Exp $ +.\" +.\" Copyright (c) 1980, 1991, 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. +.\" +.\" from: @(#)config.8 8.2 (Berkeley) 4/19/94 +.\" +.Dd October 10, 2014 +.Dt CONFIG 1 +.Os +.Sh NAME +.Nm config +.Nd build kernel compilation directories +.Sh SYNOPSIS +.Nm +.Op Fl MPpv +.Op Fl b Ar builddir +.Op Fl D Ar var=value +.Op Fl s Ar srcdir +.Op Fl U Ar value +.Op Ar config-file +.Nm +.Fl x +.Op Ar kernel-file +.Nm +.Fl L +.Op Fl v +.Op Fl s Ar srcdir +.Op Ar config-file +.Sh DESCRIPTION +In its first synopsis form, +.Nm +creates a kernel build directory from the machine description file +.Ar config-file , +which describes the system to configure. +Refer to section +.Sx KERNEL BUILD CONFIGURATION +for the details of that use +of +.Nm . +.Pp +In its second synopsis form, +.Nm +takes the binary kernel +.Ar kernel-file +as its single argument (aside from the mandatory +.Fl x +flag), then extracts the embedded configuration file (if any) and +writes it to standard output. +If +.Ar kernel-file +is not given, and the system is not running +.Nx +an error is printed. +On systems running +.Nx +the booted kernel is looked up using the +.Xr sysctl 3 +variable +.Dv machdep.booted_kernel +and if that is not found, +.Dv _PATH_UNIX +.Pq Pa /netbsd +is used. +Configuration data will be available if the given kernel was compiled +with either +.Va INCLUDE_CONFIG_FILE +or +.Va INCLUDE_JUST_CONFIG +options. +.Pp +In its third synopsis form, +.Nm +is a tool for the kernel developer and generates a +.Dq lint +configuration file to be used during regression testing. +Refer to section +.Sx LINT CONFIGURATION +for the details of that use of +.Nm . +.Pp +.Nm +accepts the following parameters: +.Bl -tag -width indent +.It Fl b Ar builddir +Use +.Ar builddir +as the kernel build directory, instead of computing and creating one +automatically. +.It Fl D Ar var=value +Define a makeoptions variable to the given value. +This is equivalent to appending a +.Li makeoptions var=value +line to the config file. +.It Fl L +Generate a lint configuration. +See section +.Sx LINT CONFIGURATION +for details. +.It Fl M +Do modular build (experimental). +Instead of linking all object files (*.o) at once, collect related object +files into an intermediate relocatable object (*.ko), then link those *.ko +files into the final kernel. +This changes the order of objects in the kernel binary. +.It Fl P +Pack locators to save space in the resulting kernel binary. +The amount of space saved that way is so small that this option should +be considered historical, and of no actual use. +.It Fl p +Generate a build directory suited for kernel profiling. +However, this options should be avoided in favor of the relevant options +inside the configuration file as described in section +.Sx KERNEL BUILD CONFIGURATION . +.It Fl s Ar srcdir +Point to the top of the kernel source tree. +It must be an absolute path when +.Nm +is used to prepare a kernel build directory, but can be relative +when it is used in combination with the +.Fl L +flag. +.It Fl U Ar var +Undefine the makeoption +.Ar var . +This is equivalent to appending the line +.Li no makeoptions var +to the config file. +.It Fl v +Increase verbosity by enabling some more warnings. +.It Fl x +Extract the configuration embedded in a kernel binary. +.El +.Ss KERNEL BUILD CONFIGURATION +There are several different ways to run the +.Nm +program. +The traditional way is to run +.Nm +from the +.Pa conf +subdirectory of the machine-specific directory of the system source +(usually +.Pa /sys/arch/MACHINE/conf , +where +.Pa MACHINE +is one of +.Pa vax , +.Pa hp300 , +and so forth), and to specify as the +.Ar config-file +the name of a machine description file located in that directory. +.Nm +will by default create files in the directory +.Pa ../compile/SYSTEMNAME , +where +.Pa SYSTEMNAME +is the last path component of +.Ar config-file . +.Nm +will assume that the top-level kernel source directory is located four +directories above the build directory. +.Pp +Another way is to create the build directory yourself, place the +machine description file in the build directory with the name +.Pa CONFIG , +and run +.Nm +from within the build directory without specifying a +.Ar config-file . +.Nm +will then by default create files in the current directory. +If you run +.Nm +this way, you must specify the location of the top-level kernel source +directory using the +.Fl s +option or by using the +.Dq Li source +directive at the beginning of the machine description file. +.Pp +Finally, you can specify the build directory for +.Nm +and run it from anywhere. +You can specify a build directory with the +.Fl b +option or by using the +.Dq Li build +directive at the beginning of the machine description file. +You must specify the location of the top-level kernel source directory if you +specify a build directory. +.Pp +If +.Ar config-file +is a binary kernel, +.Nm +will try to extract the configuration file embedded into it, which will +be present if that kernel was built either with +.Va INCLUDE_CONFIG_FILE +or +.Va INCLUDE_JUST_CONFIG +options. +This work mode requires you to manually specify a build directory with +the +.Fl b +option, which implies the need to provide a source tree too. +.Pp +If the +.Fl p +option is supplied, +.Pa .PROF +is appended to the default compilation directory name, and +.Nm +acts as if the lines +.Dq Li makeoptions PROF="-pg" +and +.Dq Li options GPROF +appeared in the machine description file. +This will build a system that includes profiling code; see +.Xr kgmon 8 +and +.Xr gprof 1 . +The +.Fl p +flag is expected to be used for +.Dq one-shot +profiles of existing systems; for regular profiling, it is probably +wiser to create a separate machine description file containing the +.Li makeoptions +line. +.Pp +The old undocumented +.Fl g +flag is no longer supported. +Instead, use +.Dq Li makeoptions DEBUG="-g" +and (typically) +.Dq Li options KGDB . +.Pp +The output of +.Nm +consists of a number of files, principally +.Pa ioconf.c , +a description of I/O devices that may be attached to the system; and a +.Pa Makefile , +used by +.Xr make 1 +in building the kernel. +.Pp +After running +.Nm , +it is wise to run +.Dq Li make depend +in the directory where the new makefile +was created. +.Nm +prints a reminder of this when it completes. +.Pp +If +.Nm +stops due to errors, the problems reported should be corrected and +.Nm +should be run again. +.Nm +attempts to avoid changing the compilation directory +if there are configuration errors, +but this code is not well-tested, +and some problems (such as running out of disk space) +are unrecoverable. +.Ss LINT CONFIGURATION +A so-called +.Dq lint +configuration should include everything from the kernel that can +possibly be selected. +The rationale is to provide a way to reach all the code a user might +select, in order to make sure all options and drivers compile without +error for a given source tree. +.Pp +When used with the +.Fl L +flag, +.Nm +takes the regular configuration file +.Ar config-file +and prints on the standard output a configuration file that includes +.Ar config-file , +selects all options and file-systems the user can possibly select, +and defines an instance of every possible attachment as described by +the kernel option definition files used by +.Ar config-file . +.Pp +The resulting configuration file is meant as a way to select all +possible features in order to test that each of them compiles. +It is not meant to result in a kernel binary that can run on any +hardware. +.Pp +Unlike the first synopsis form, the provided +.Ar srcdir +is relative to the current working directory. +In the first synopsis form, it is relative to the build directory. +.Sh SEE ALSO +The SYNOPSIS portion of each device in section 4. +.\".Rs +.\" .%T "Building 4.4 BSD Systems with Config" +.\" .%T "Device Support in 4.4BSD" +.\".Re +.Pp +.Xr options 4 , +.Xr config 5 , +.Xr config 9 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.1 . +It was completely revised in +.Bx 4.4 . +The +.Fl x +option appeared in +.Nx 2.0 . +The +.Fl L +option appeared in +.Nx 5.0 . diff --git a/buildrump.sh/src/usr.bin/config/config.5 b/buildrump.sh/src/usr.bin/config/config.5 new file mode 100644 index 00000000..90b4c094 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/config.5 @@ -0,0 +1,729 @@ +.\" $NetBSD: config.5,v 1.25 2014/10/31 07:38:36 uebayasi Exp $ +.\" +.\" Copyright (c) 2006, 2007 The NetBSD Foundation. +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd October 30, 2014 +.Dt CONFIG 5 +.Os +.Sh NAME +.Nm config +.Nd kernel configuration file syntax +.Sh DESCRIPTION +The kernel configuration file specifies the way the kernel should be compiled +by the rest of the toolchain. +It is processed by +.Xr config 1 +to produce a number of files that will allow the user to compile a possibly +customised kernel. +One compilation can issue several kernel binaries, with different root and +dump devices configurations, or with full debugging information. +.Pp +This manual page is intended to serve as a complete reference of all aspects +of the syntax used in the many files processed by +.Xr config 1 . +The novice user will prefer looking at the examples given in +.Xr config.samples 5 +in order to understand better how the default configuration can be changed, +and how all of its elements interact with each other. +.Pp +The kernel configuration file actually contains the description of all the +options, drivers and source files involved in the kernel compilation, and the +logic that binds them. +The +.Ic machine +statement, usually found in the +.Pa std.${MACHINE} +file, hides this from the user by automatically including all the descriptive +files spread all around the kernel source tree, the main one being +.Pa conf/files . +.Pp +Thus, the kernel configuration file contains two parts: +the description of the compilation options, and the selection of those options. +However, it begins with a small preamble that controls a couple of options of +.Xr config 1 , +and a few statements belong to any of the two sections. +.Pp +The user controls the options selection part, which is located in a file +commonly referenced as the +.Em main configuration file +or simply the +.Em kernel configuration file . +The developer is responsible for describing the options in the relevant files +from the kernel source tree. +.Pp +Statements are separated by new-line characters. +However, new-line characters can appear in the middle of a given statement, +with the value of a space character. +.Ss OBJECTS AND NAMES +.Xr config 1 +is a rather complicated piece of software that tries to comply with any +configuration the user might think of. +Quite a few different objects are manipulated through the kernel configuration +file, therefore some definitions are needed. +.Ss Options and attributes +The basic objects driving the kernel compilation are +.Em options , +and are called +.Ar attributes +in some contexts. +An +.Ar attribute +usually refers to a feature a given piece of hardware might have. +However, the scope of an attribute is rather wide and can just be a place +holder to group some source files together. +.Pp +There is a special class of attribute, named +.Em interface attribute , +which represents a hook that allows a device to attach to (i.e., be a child of) +another device. +An +.Em interface attribute +has a (possibly empty) list of +.Ar locators +to match the actual location of a device. +For example, on a PCI bus, devices are located by a +.Em device number +that is fixed by the wiring of the motherboard. +Additionally, each of those devices can appear through several interfaces named +.Em functions . +A single PCI device entity is a unique function number of a given device from +the considered PCI bus. +Therefore, the locators for a +.Xr pci 4 +device are +.Ar dev +(for device), and +.Ar function . +.Pp +A +.Ar locator +can either be a single integer value, or an array of integer values. +It can have a default value, in which case it can be wildcarded with a +.Dq \&? +in the options selection section of the configuration file. +A single +.Ar locator +definition can take one of the following forms: +.Bl -enum -offset indent -compact +.It +.Ar locator +.It +.Ar locator += +.Ar value +.It +.Ar locator Ns Oo Ar length Oc +.It +.Ar locator Ns Oo Ar length Oc = Brq Ar value , ... +.El +The variants that specify a default value can be enclosed into square brackets, +in which case the locator will not have to be specified later in the options +selection section of the configuration file. +.Pp +In the options selection section, the locators are specified when declaring an +instance as a space-separated list of +.Dq Ao Ar locator Ac Ao Ar value Ac +where value can be the +.Dq \&? +wildcard if the locator allows it. +.Ss Devices, instances and attachments +The main benefit of the kernel configuration file is to allow the user to avoid +compiling some drivers, and wire down the configuration of some others. +We have already seen that devices attach to each other through +.Em interface attributes , +but not everything can attach to anything. +Furthermore, the user has the ability to define precise instances for the +devices. +An +.Ar instance +is simply the reality of a device when it is probed and attached by the kernel. +.Pp +Each driver has a name for its devices. +It is called the base device name and is found as +.Ar base +in this documentation. +An +.Ar instance +is the concatenation of a device name and a number. +In the kernel configuration file, instances can sometimes be wildcarded +(i.e., the number is replaced by a +.Dq * +or a +.Dq \&? ) +in order to match all the possible instances of a device. +.Pp +The usual +.Dq * +becomes a +.Dq \&? +when the instance name is used as an +.Em attachment name . +In the options selection part of the kernel configuration files, an +.Em attachment +is an +.Em interface attribute +concatenated with a number or the wildcard +.Dq \&? . +.Ss Pseudo-devices +Some components of the kernel behave like a device although they don't have +any actual reality in the hardware. +For example, this is the case for special network devices, such as +.Xr tun 4 +and +.Xr tap 4 . +They are integrated in the kernel as pseudo-devices, and can have several +instances and even children, just like normal devices. +.Ss Dependencies +The options description part of the kernel configuration file contains all the +logic that ties the source files together, and it is done first through writing +down dependencies between +.Xr config 1 +objects. +.Pp +In this documentation, the syntax for +.Ar dependencies +is a comma-separated list of +.Ar options +and +.Ar attributes . +.Pp +For example, the use of an Ethernet network card requires the source files that +handle the specificities of that protocol. +Therefore, all Ethernet network card drivers depend on the +.Ar ether +attribute. +.Ss Conditions +Finally, source file selection is possible through the help of +conditionals, referred to as +.Ar condition +later in this documentation. +The syntax for those conditions uses well-known operators ( +.Dq \*[Am] , +.Dq | +and +.Dq \&! ) +to combine +.Ar options +and +.Ar attributes . +.Ss CONTEXT NEUTRAL STATEMENTS +.Bl -ohang +.It Ic version Ar yyyymmdd +Indicates the syntax version used by the rest of the file, or until the next +.Ic version +statement. +The argument is an ISO date. +A given +.Xr config 1 +binary might only be compatible with a limited range of version numbers. +.It Ic include Ar path +Includes a file. +The path is relative to the top of the kernel source tree, or the inner-most +defined +.Ic prefix . +.It Ic cinclude Ar path +Conditionally includes a file. +Contrary to +.Ic include , +it will not produce an error if the file does not exist. +The argument obeys the same rules as for +.Ic include . +.It Ic prefix Op Ar path +If +.Ar path +is given, it pushes a new prefix for +.Ic include +and +.Ic cinclude . +.Ic prefix +statements act like a stack, and an empty +.Ar path +argument has the latest prefix popped out. +The +.Ar path +argument is either absolute or relative to the current defined prefix, which +defaults to the top of the kernel source tree. +.It Ic ifdef Ar attribute +.It Ic ifndef Ar attribute +.It Ic elifdef Ar attribute +.It Ic elifndef Ar attribute +.It Ic else +.It Ic endif +Conditionally interprets portions of the current file. +Those statements depend on whether or not the given +.Ar attribute +has been previously defined, through +.Ic define +or any other statement that implicitely defines attributes such as +.Ic device . +.El +.Ss PREAMBLE +In addition to +.Ic include , cinclude , +and +.Ic prefix , +the preamble may contain the following optional statements: +.Bl -ohang +.It Ic build Ar path +Defines the build directory for the compilation of the kernel. +It replaces the default of +.Pa ../compile/\*[Lt]config-file\*[Gt] +and is superseded by the +.Fl b +parameter of +.Xr config 1 . +.It Ic source Ar path +Defines the directory in which the source of the kernel lives. +It replaces the default of +.Pa ../../../.. +and is superseded by the +.Fl s +parameter of +.Xr config 1 . +.El +.Ss OPTIONS DESCRIPTION +The user will not usually have to use descriptive statements, as they are meant +for the developer to tie a given piece of code to the rest of the kernel. +However, third parties may provide sources to add to the kernel compilation, +and the logic that binds them to the +.Nx +kernel will have to be added to the user-edited configuration file. +.Bl -ohang +.It Ic devclass Ar class +Defines a special attribute, named +.Em device class . +A given device cannot belong to more than one device class. +.Xr config 1 +translates that property by the rule that a device cannot depend on more than +one device class, and will properly fill the configuration information file it +generates according to that value. +.It Ic defflag Oo Ar file Oc Ar option Oo Ar option Oo Ar ... Oc Oc \ + Op : Ar dependencies +Defines a boolean option, that can either be selected or be un-selected by the +user with the +.Ic options +statement. +The optional +.Ar file +argument names a header file that will contain the C pre-processor definition +for the option. +If no file name is given, it will default to +.Ar opt_\*[Lt]option\*[Gt].h . +.Xr config 1 +will always create the header file, but if the user choose not to select the +option, it will be empty. +Several options can be combined in one header file, for convenience. +The header file is created in the compilation directory, making them directly +accessible by source files. +.It Ic defparam Oo Ar file Oc Ar option Oo = Ar value Oc \ + Oo := Ar lint-value Oc Oo Ar option Oo Ar ... Oc Oc Op : Ar dependencies +Behaves like +.Ic defflag , +except the defined option must have a value. +Such options are not typed: +they can have either a numeric or a string value. +If a +.Ar value +is specified, it is treated as a default, and the option is +always defined in the corresponding header file. +If a +.Ar lint-value +is specified, +.Xr config 1 +will use it as a value when generating a lint configuration with +.Fl L , +and ignore it in all other cases. +.It Ic deffs Ar name Op Ar name Op Ar ... +Defines a file-system name. +It is no more than a regular option, as defined by +.Ic defflag , +but it allows the user to select the +file-systems to be compiled in the kernel with the +.Ic file-system +statement instead of the +.Ic options +statement. +.It Ic obsolete defflag Oo Ar file Oc Ar option Op Ar option Op Ar ... +.It Ic obsolete defparam Oo Ar file Oc Ar option Op Ar option Op Ar ... +Those two statements are identical and mark the listed option names as +obsolete. +If the user selects one of the listed options in the kernel configuration +file, +.Xr config 1 +will emit a warning and ignore the option. +The optional +.Ar file +argument should match the original definition of the option. +.It Ic define Ar attribute Oo Bro Ar locators Brc Oc Oo : Ar dependencies Oc +Defines an +.Ar attribute . +The +.Ar locators +list is optional, and can be empty. +If the pair of brackets are present, the locator list is defined and the +declared attribute becomes an +.Em interface attribute , +on which devices can attach. +.It Ic maxpartitions Ar number +Defines the maximum number of partitions the disklabels for the considered +architecture can hold. +This statement cannot be repeated and should only appear in the +.Pa std\&.$\&{ARCH\&} +file. +.It Ic maxusers Ar min default max +Indicates the range of values that will later be accepted by +.Xr config 1 +for the +.Ic maxusers +statement in the options selection part of the configuration file. +In case the user doesn't include a +.Ic maxusers +statement in the configuration file, the value +.Ar default +is used instead. +.It Ic device Ar base Oo Bro Ar locators Brc Oc Oo : dependencies Oc +Declares a device of name +.Ar base . +The optional list of +.Ar locators , +which can also be empty, indicates the device can have children attached +directly to it. +Internally, that means +.Ar base +becomes an +.Ar interface attribute . +For every device the user selects, +.Xr config 1 +will add the matching +.Fn CFDRIVER_DECL +statement to +.Pa ioconf.c . +However, it is the responsibility of the developer to add the relevant +.Fn CFATTACH_DECL +line to the source of the device's driver. +.It Ic attach Ar base Ic at Ar attr Oo , Ar attr Oo , Ar ... Oc Oc Oo Ic with \ + Ar name Oc Oo : dependencies Oc +All devices must have at least one declared attachment. +Otherwise, they will never be found in the +.Xr autoconf 9 +process. +The attributes on which an instance of device +.Ar base +can attach must be +.Ar interface attributes , +or +.Ic root +in case the device is at the top-level, which is usually the case of e.g., +.Xr mainbus 4 . +The instances of device +.Ar base +will later attach to one interface attribute from the specified list. +.Pp +Different +.Ic attach +definitions must use different names using the +.Ic with +option. +It is then possible to use the associated +.Ar name +as a conditional element in a +.Ic file +statement. +.It Ic defpseudo Ar base Oo : dependencies Oc +Declares a pseudo-device. +Those devices don't need an attachment to be declared, they will always be +attached if they were selected by the user. +.It Ic defpseudodev Ar base Oo Bro Ar locators Brc Oc Oo : dependencies Oc +Declares a pseudo-device. +Those devices don't need an attachment to be declared, they will always be +attached if they were selected by the user. +This declaration should be used if the pseudodevice uses +.Xr autoconf 9 +functions to manage its instances or attach children. +As for normal devices, an optional list of +.Ar locators +can be defined, which implies an interface attribute named +.Ar base , +allowing the pseudo-device to have children. +Interface attributes can also be defined in the +.Ar dependencies +list. +.It Ic file Ar path Oo Ar condition Oc Oo Ic needs-count Oc \ + Oo Ic needs-flag Oc Op Ic compile with Ar rule +Adds a source file to the list of files to be compiled into the kernel, if the +.Ar conditions +are met. +The +.Ic needs-count +option indicates that the source file requires the number of all the countable +objects it depends on (through the +.Ar conditions ) +to be defined. +It is usually used for +.Ar pseudo-devices +whose number can be specified by the user in the +.Ic pseudo-device +statement. +Countable objects are devices and pseudo-devices. +For the former, the count is the number of declared instances. +For the latter, it is the number specified by the user, defaulting to 1. +The +.Ic needs-flag +options requires that a flag indicating the selection of an attribute to +be created, but the precise number isn't needed. +This is useful for source files that only partly depend on the attribute, +and thus need to add pre-processor statements for it. +.Pp +.Ic needs-count +and +.Ic needs-flag +both produce a header file for each of the considered attributes. +The name of that file is +.Pa \*[Lt]attribute\*[Gt].h . +It contains one pre-processor definition of +.Dv NATTRIBUTE +set to 0 if the attribute was not selected by the user, or to the number of +instances of the device in the +.Ic needs-count +case, or to 1 in all the other cases. +.Pp +The +.Ar rule +argument specifies the +.Xr make 1 +rule that will be used to compile the source file. +If it is not given, the default rule for the type of the file will be used. +For a given file, there can be more than one +.Ic file +statement, but not from the same configuration source file, and all later +statements can only specify a +.Ar rule +argument, and no +.Ar conditions +or flags. +This is useful when a file needs special consideration from one particular +architecture. +.It Ic object Ar path Op Ar condition +Adds an object file to the list of objects to be linked into the kernel, if the +.Ar conditions +are met. +This is most useful for third parties providing binary-only components. +.It Ic device-major Ar base Oo Ic char Ar number Oc Oo Ic block Ar number Oc \ + Op Ar condition +Associates a major device number with the device +.Ar base . +A device can be a character device, a block device, or both, and can have +different numbers for each. +The +.Ar condition +indicates when the relevant line should be added to +.Pa ioconf.c , +and works just like the +.Ic file +statement. +.It Ic makeoptions Ar condition name Ns += Ns Ar value Op , Ar condition \ + name Ns += Ns Ar value +Appends to a definition in the generated +.Pa Makefile . +.El +.Ss OPTIONS SELECTION +.Bl -ohang +.It Ic machine Ar machine Op Ar arch Op Ar subarch Op Ar ... +The +.Ic machine +statement should appear first in the kernel configuration file, with the +exception of context-neutral statements. +It makes +.Xr config 1 +include, in that order, the following files: +.Bl -enum -offset indent -compact +.It +.Pa conf/files +.It +.Pa arch/${ARCH}/conf/files.${ARCH} +if defined +.It +.Pa arch/${SUBARCH}/conf/files.${SUBARCH} +for each defined sub-architecture +.It +.Pa arch/${MACHINE}/conf/files.${MACHINE} +.El +It also defines an attribute for the +.Ar machine , +the +.Ar arch +and each of the +.Ar subarch . +.It Ic package Ar path +Simpler version of: +.Bd -literal -offset indent +prefix PATH +include FILE +prefix +.Ed +.It Ic ident Ar string +Defines the indentification string of the kernel. +This statement is optional, and the name of the main configuration file will be +used as a default value. +.It Ic no ident +Deletes any pre-existing indentification string of the kernel. +.It Ic maxusers Ar number +Despite its name, this statement does not limit the maximum number of users on +the system. +There is no such limit, actually. +However, some kernel structures need to be adjusted to accommodate with more +users, and the +.Ic maxusers +parameter is used for example to compute the maximum number of opened files, +and the maximum number of processes, which itself is used to adjust a few +other parameters. +.It Ic options Ar name Oo = Ar value Oc Op , Ar name Oo = Ar \ + value Oc , Ar ... +Selects the option +.Ar name , +affecting it a +.Ar value +if the options requires it (see the +.Ic defflag +and +.Ic defparam +statements). +.Pp +If the option has not been declared in the options description part of the +kernel configuration machinery, it will be added as a pre-processor definition +when source files are compiled. +.It Ic no options Ar name Op , Ar name Op , Ar ... +Un-selects the option +.Ar name . +If option +.Ar name +has not been previously selected, the statement produces an error. +.It Ic file-system Ar name Op , Ar name Op , Ar ... +Adds support for all the listed file-systems. +.It Ic no file-system Ar name Op , Ar name Op , Ar ... +Removes support for all the listed file-systems. +.It Ic config Ar name Ic root on Ar device Oo Ic type Ar fs Oc Op Ic dumps on \ + Ar device +Adds +.Ar name +to the list of kernel binaries to compile from the configuration file, using +the specified root and dump devices information. +.Pp +Any of the +.Ar device +and +.Ar fs +parameters can be wildcarded with +.Dq \&? +to let the kernel automatically discover those values. +.Pp +At least one +.Ic config +statement must appear in the configuration file. +.It Ic no config Ar name +Removes +.Ar name +from the list of kernel binaries to compile from the configuration file. +.It Ar instance Ic at Ar attachment Op Ar locator specification +Configures an instance of a device attaching at a specific location in the +device tree. +All parameters can be wildcarded, with a +.Dq * +for +.Ar instance , +and a +.Dq \&? +for +.Ar attachment +and the locators. +.It Ic no Ar instance Op Ic at Ar attachment +Removes the previously configured instances of a device that exactly match the +given specification. +If two instances differ only by their locators, both are removed. +If no +.Ar attachment +is specified, all matching instances are removed. +.Pp +If +.Ar instance +is a bare device name, all the previously defined instances of that device, +regardless of the numbers or wildcard, are removed. +.It Ic no device at Ar attachment +Removes all previously configured instances that attach to the specified +attachment. +If +.Ar attachment +ends with a +.Dq * , +all instances attaching to all the variants of +.Ar attachment +are removed. +.It Ic pseudo-device Ar device Op Ar number +Adds support for the specified pseudo-device. +The parameter +.Ar number +is passed to the initialisation function of the pseudo-device, usually to +indicate how many instances should be created. +It defaults to 1, and some pseudo-devices ignore that parameter. +.It Ic no pseudo-device Ar name +Removes support for the specified pseudo-device. +.It Ic makeoptions Ar name Ns = Ns value Op , Ar name Ns += Ns value \ + Op , Ar ... +Adds or appends to a definition in the generated +.Pa Makefile . +A definition cannot be overriden, it must be removed before it can be added +again. +.It Ic no makeoptions Ar name Op , Ar name Op , Ar ... +Removes one or more definitions from the generated +.Pa Makefile . +.It Ic select Ar name +Adds the specified attribute and its dependencies. +.It Ic no select Ar name +Removes the specified attribute and all the attributes which depend on it. +.El +.Sh FILES +The files are relative to the kernel source top directory (e.g., +.Pa /usr/src/sys ) . +.Pp +.Bl -tag -width arch/${MACHINE}/conf/std.${MACHINE} +.It Pa arch/${MACHINE}/conf/std.${MACHINE} +Standard configuration for the given architecture. +This file should always be included. +.It Pa arch/${MACHINE}/conf/GENERIC +Standard options selection file for the given architecture. +Users should always start changing their main kernel configuration file by +editing a copy of this file. +.It Pa conf/files +Main options description file. +.El +.Sh EXAMPLES +.Xr config.samples 5 +uses several examples to cover all the practical aspects of writing or +modifying a kernel configuration file. +.Sh SEE ALSO +.Xr config 1 , +.Xr options 4 , +.Xr config.samples 5 , +.Xr config 9 diff --git a/buildrump.sh/src/usr.bin/config/config.samples.5 b/buildrump.sh/src/usr.bin/config/config.samples.5 new file mode 100644 index 00000000..b670c9af --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/config.samples.5 @@ -0,0 +1,252 @@ +.\" $NetBSD: config.samples.5,v 1.6 2014/03/06 15:00:21 riastradh Exp $ +.\" +.\" Copyright (c) 2006 The NetBSD Foundation. +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd June 4, 2006 +.Dt CONFIG.SAMPLES 5 +.Os +.Sh NAME +.Nm config.samples +.Nd kernel configuration file syntax examples +.Sh DESCRIPTION +.Ss Devices, drivers and instances +For a given device, at most one driver will attach. +In order for a driver to attach, the kernel configuration file must include a +compatible instance of the driver for the location of the device. +The following lines from the +.Pa GENERIC +kernel configuration file of +.Nx Ns / Ns i386 +are examples of instances of drivers: +.Bd -literal +pchb* at pci? dev ? function ? # PCI-Host bridges +pcib* at pci? dev ? function ? # PCI-ISA bridges +ppb* at pci? dev ? function ? # PCI-PCI bridges + +siop* at pci? dev ? function ? # Symbios 53c8xx SCSI +esiop* at pci? dev ? function ? # Symbios 53c875 SCSI and newer + +ix0 at isa? port 0x300 irq 10 # EtherExpress/16 +.Ed +.Pp +The first three instances allow three different drivers to attach to all the +matching devices found on any PCI bus. +This is the most generic case. +.Pp +The next two lines allow two distinct drivers to attach to any matching device +found on any PCI bus, but those two drivers are special because they both +support some of the same devices. +Each of the driver has a matching function that returns their score for the +device that is being considered. +.Xr autoconf 9 +decides at run-time which driver will attach. +Of course, it is deterministic so if the user wants to change the driver that +attaches to the device, the instance of the other driver will have to be +removed, e.g. by commenting it out. +.Pp +The last line configures an instance of an ISA device. +Unlike the PCI bus, the ISA bus cannot discover the devices that are present on +the bus. +The driver has to try accessing the device in order to discover it. +That implies locators must be specified to some extent: a driver would +usually need the base address of the device, some need the IRQ line that the +device is configured to use, though some others would just try a set of known +values, at the risk of badly interacting with other devices on the bus. +.Ss Hard-wiring kernel configuration +This technique consists in specifying exactly the location of the devices on a +given system. +In the general case it has very little use, because it does not change the size +of the kernel, and it will prevent it from finding devices in case the hardware +changes, even slightly. +.Pp +Let's consider the following excerpt of +.Xr dmesg 8 +output: +.Bd -literal +auich0 at pci0 dev 31 function 5: i82801DB/DBM (ICH4/ICH4M) AC-97 Audio +.Ed +.Pp +The +.Xr auich 4 +driver (which controls Intel's AC-97 audio chips) attached there because of the +following instance of +.Pa GENERIC : +.Bd -literal +auich* at pci? dev ? function ? +.Ed +.Pp +Hard-wiring that instance means re-writing it to the following: +.Bd -literal +auich0 at pci0 dev 31 function 5 +.Ed +.Pp +and that way, +.Ar auich0 +will attach to that specific location, or will not attach. +.Ss Removing options and drivers +When two kernel configurations differ by a very small number of changes, it is +easier to manage them by having one include the other, and add or remove the +differences. +Removing options and drivers is also useful in the situation of a user who +wants to follow the development of +.Nx : +drivers and options get added to the configuration files found in the source +tree, such as +.Pa GENERIC , +so one can include it and remove all options and drivers that are not relevant +to the considered system. +Additions to +.Pa GENERIC +will then automatically be followed and used in case they are relevant. +.Pp +While negating an options (with +.Ic no options ) +is unambiguous, it is not as clear for devices instances. +.Pp +The +.Ic no Ar instance definition +statements of +.Xr config 1 +syntax only apply on the current state of the configuration file, not on the +resulting kernel binary. +.Xr autoconf 9 +has no knowledge of instance negation, thus it is currently impossible to +express the following in a kernel configuration file: +.Bd -ragged -offset indent +.Do I want support for +.Xr ath 4 +attaching at +.Xr pci 4 , +but I do not want any instance of +.Xr ath 4 +attaching at +.Ar pci3 . +.Dc +.Ed +.Pp +For a real-world use of +.Ic no device at Ar instance +consider the following, taken from +.Nx Ns / Ns i386 : +.Bd -literal -offset indent +include "arch/i386/conf/GENERIC" + +acpi0 at mainbus? + +com* at acpi? +[... more instances of legacy devices attaching at acpi? ...] + +no device at isa0 +.Ed +.Pp +One could actually live without the +.Ar isa0 +instance, as all the legacy devices are attached at +.Ar acpi0 . +But unfortunately, dependencies on the +.Ar isa +attribute are not well registered all through the source tree, so an instance +of the +.Xr isa 4 +driver is required to compile a kernel. +So while: +.Bd -literal -offset indent +no isa* +.Ed +.Pp +is what is intended, the +.Xr isa 4 +instance itself must be kept, and that is precisely the difference made by: +.Bd -literal -offset indent +no device at isa0 +.Ed +.Ss Interface attributes +.Em Interface attributes +are a subtlety of +.Xr config 1 +and +.Xr autoconf 9 , +which often confuses users and utilities that parse +.Xr dmesg 8 +output to manipulate kernel configuration files. +What they are is best shown by the following example. +.Pp +The +.Xr dmesg 8 +output look like this: +.Bd -literal -offset indent +auvia0 at pci0 dev 17 function 5: VIA Technologies VT8235 AC'97 Audio (rev 0x50) +audio0 at auvia0: full duplex, mmap, independent +.Ed +.Pp +while the kernel configuration look like this: +.Bd -literal +auvia* at pci? dev ? function ? +audio* at audiobus? +.Ed +.Pp +It is not obvious from the kernel configuration file that an +.Xr audio 4 +device can attach at an +.Xr auvia 4 +device. +.Ar audiobus +is an +.Em interface attribute , +exposed by +.Ar auvia . +.Pp +Of course, it is possible to specify +.Bd -literal -offset indent +audio* at auvia? +.Ed +in the kernel configuration file, but then one instance per audio controller +would be needed. +.Em Interface attributes +reflect the fact there is a standard way to attach a device to its parent, no +matter what the latter is precisely. +It also means lower maintainance of the kernel configuration files because +drivers for audio controllers are added more easily. +.Pp +Most attachments are done through +.Em interface attributes , +although only a few of them are specified that way in the configuration files +found in the tree. +Another example of such an attribute is +.Ar ata : +.Bd -literal -offset indent +viaide0 at pci0 dev 17 function 1 +atabus0 at viaide0 channel 0 + +viaide* at pci? dev ? function ? +atabus* at ata? +.Ed +.\" Suggested section, maybe for later: +.\" .Ss Using a third-party driver +.Sh SEE ALSO +.Xr config 1 , +.Xr options 4 , +.Xr config 5 , +.Xr dmesg 8 diff --git a/buildrump.sh/src/usr.bin/config/defs.h b/buildrump.sh/src/usr.bin/config/defs.h new file mode 100644 index 00000000..afaabbf7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/defs.h @@ -0,0 +1,695 @@ +/* $NetBSD: defs.h,v 1.64 2014/11/17 00:53:15 uebayasi Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)config.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * defs.h: Global definitions for "config" + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/queue.h> + +#if !defined(MAKE_BOOTSTRAP) && defined(BSD) +#include <sys/cdefs.h> +#include <paths.h> +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +/* These are really for MAKE_BOOTSTRAP but harmless. */ +#ifndef __dead +#define __dead +#endif +#ifndef __printflike +#define __printflike(a, b) +#endif +#ifndef _PATH_DEVNULL +#define _PATH_DEVNULL "/dev/null" +#endif + +#ifdef MAKE_BOOTSTRAP +#undef dev_t +#undef devmajor_t +#undef devminor_t +#undef NODEV +#undef NODEVMAJOR +#undef major +#undef minor +#undef makedev +#define dev_t unsigned int /* XXX: assumes int is 32 bits */ +#define NODEV ((dev_t)-1) +#define devmajor_t int +#define devminor_t int +#define NODEVMAJOR (-1) +#define major(x) ((devmajor_t)((((x) & 0x000fff00) >> 8))) +#define minor(x) ((devminor_t)((((x) & 0xfff00000) >> 12) | \ + (((x) & 0x000000ff) >> 0))) +#define makedev(x,y) ((dev_t)((((x) << 8) & 0x000fff00) | \ + (((y) << 12) & 0xfff00000) | \ + (((y) << 0) & 0x000000ff))) +#define __attribute__(x) +#endif /* MAKE_BOOTSTRAP */ + +#undef setprogname +#undef getprogname +extern const char *progname; +#define setprogname(s) ((void)(progname = (s))) +#define getprogname() (progname) + +#define ARRCHR '#' + +/* + * The next two lines define the current version of the config(1) binary, + * and the minimum version of the configuration files it supports. + */ +#define CONFIG_VERSION 20141030 +#define CONFIG_MINVERSION 0 + +/* + * Name/value lists. Values can be strings or pointers and/or can carry + * integers. The names can be NULL, resulting in simple value lists. + */ +struct nvlist { + struct nvlist *nv_next; + const char *nv_name; + const char *nv_str; + void *nv_ptr; + long long nv_num; + int nv_ifunit; /* XXX XXX XXX */ + int nv_flags; +#define NV_DEPENDED 1 +}; + +/* + * Kernel configurations. + */ +struct config { + TAILQ_ENTRY(config) cf_next; + const char *cf_name; /* "netbsd" */ + int cf_lineno; /* source line */ + const char *cf_fstype; /* file system type */ + struct nvlist *cf_root; /* "root on ra0a" */ + struct nvlist *cf_dump; /* "dumps on ra0b" */ +}; + +/* + * Option definition list + */ +struct defoptlist { + struct defoptlist *dl_next; + const char *dl_name; + const char *dl_value; + const char *dl_lintvalue; + int dl_obsolete; + struct nvlist *dl_depends; +}; + +struct module { + const char *m_name; +#if 1 + struct attrlist *m_deps; +#else + struct attrlist *m_attrs; + struct modulelist *m_deps; +#endif + int m_expanding; + TAILQ_HEAD(, files) m_files; + int m_weight; +}; + +/* + * Attributes. These come in three flavors: "plain", "device class," + * and "interface". Plain attributes (e.g., "ether") simply serve + * to pull in files. Device class attributes are like plain + * attributes, but additionally specify a device class (e.g., the + * "disk" device class attribute specifies that devices with the + * attribute belong to the "DV_DISK" class) and are mutually exclusive. + * Interface attributes (e.g., "scsi") carry three lists: locators, + * child devices, and references. The locators are those things + * that must be specified in order to configure a device instance + * using this attribute (e.g., "tg0 at scsi0"). The a_devs field + * lists child devices that can connect here (e.g., "tg"s), while + * the a_refs are parents that carry the attribute (e.g., actual + * SCSI host adapter drivers such as the SPARC "esp"). + */ +struct attr { + /* XXX */ + struct module a_m; +#define a_name a_m.m_name +#define a_deps a_m.m_deps +#define a_expanding a_m.m_expanding +#define a_files a_m.m_files +#define a_weight a_m.m_weight + + /* "interface attribute" */ + int a_iattr; /* true => allows children */ + struct loclist *a_locs; /* locators required */ + int a_loclen; /* length of above list */ + struct nvlist *a_devs; /* children */ + struct nvlist *a_refs; /* parents */ + + /* "device class" */ + const char *a_devclass; /* device class described */ +}; + +/* + * List of attributes. + */ +struct attrlist { + struct attrlist *al_next; + struct attr *al_this; +}; + +/* + * List of locators. (Either definitions or uses...) + * + * XXX it would be nice if someone could clarify wtf ll_string and ll_num + * are actually holding. (This stuff was previously stored in a very ad + * hoc fashion, and the code is far from clear.) + */ +struct loclist { + const char *ll_name; + const char *ll_string; + long long ll_num; + struct loclist *ll_next; +}; + +/* + * Parent specification. Multiple device instances may share a + * given parent spec. Parent specs are emitted only if there are + * device instances which actually reference it. + */ +struct pspec { + TAILQ_ENTRY(pspec) p_list; /* link on parent spec list */ + struct attr *p_iattr; /* interface attribute of parent */ + struct devbase *p_atdev; /* optional parent device base */ + int p_atunit; /* optional parent device unit */ + struct nvlist *p_devs; /* children using it */ + int p_inst; /* parent spec instance */ + int p_active; /* parent spec is actively used */ +}; + +/* + * The "base" part (struct devbase) of a device ("uba", "sd"; but not + * "uba2" or "sd0"). It may be found "at" one or more attributes, + * including "at root" (this is represented by a NULL attribute), as + * specified by the device attachments (struct deva). + * + * Each device may also export attributes. If any provide an output + * interface (e.g., "esp" provides "scsi"), other devices (e.g., + * "tg"s) can be found at instances of this one (e.g., "esp"s). + * Such a connection must provide locators as specified by that + * interface attribute (e.g., "target"). The base device can + * export both output (aka `interface') attributes, as well as + * import input (`plain') attributes. Device attachments may + * only import input attributes; it makes no sense to have a + * specific attachment export a new interface to other devices. + * + * Each base carries a list of instances (via d_ihead). Note that this + * list "skips over" aliases; those must be found through the instances + * themselves. Each base also carries a list of possible attachments, + * each of which specify a set of devices that the device can attach + * to, as well as the device instances that are actually using that + * attachment. + */ +struct devbase { + const char *d_name; /* e.g., "sd" */ + TAILQ_ENTRY(devbase) d_next; + int d_isdef; /* set once properly defined */ + int d_ispseudo; /* is a pseudo-device */ + devmajor_t d_major; /* used for "root on sd0", e.g. */ + struct attrlist *d_attrs; /* attributes, if any */ + int d_umax; /* highest unit number + 1 */ + struct devi *d_ihead; /* first instance, if any */ + struct devi **d_ipp; /* used for tacking on more instances */ + struct deva *d_ahead; /* first attachment, if any */ + struct deva **d_app; /* used for tacking on attachments */ + struct attr *d_classattr; /* device class attribute (if any) */ +}; + +struct deva { + const char *d_name; /* name of attachment, e.g. "com_isa" */ + TAILQ_ENTRY(deva) d_next; /* list of all instances */ + struct deva *d_bsame; /* list on same base */ + int d_isdef; /* set once properly defined */ + struct devbase *d_devbase; /* the base device */ + struct nvlist *d_atlist; /* e.g., "at tg" (attr list) */ + struct attrlist *d_attrs; /* attributes, if any */ + struct devi *d_ihead; /* first instance, if any */ + struct devi **d_ipp; /* used for tacking on more instances */ +}; + +/* + * An "instance" of a device. The same instance may be listed more + * than once, e.g., "xx0 at isa? port FOO" + "xx0 at isa? port BAR". + * + * After everything has been read in and verified, the devi's are + * "packed" to collect all the information needed to generate ioconf.c. + * In particular, we try to collapse multiple aliases into a single entry. + * We then assign each "primary" (non-collapsed) instance a cfdata index. + * Note that there may still be aliases among these. + */ +struct devi { + /* created while parsing config file */ + const char *i_name; /* e.g., "sd0" */ + int i_unit; /* unit from name, e.g., 0 */ + struct devbase *i_base;/* e.g., pointer to "sd" base */ + TAILQ_ENTRY(devi) i_next; /* list of all instances */ + struct devi *i_bsame; /* list on same base */ + struct devi *i_asame; /* list on same base attachment */ + struct devi *i_alias; /* other aliases of this instance */ + const char *i_at; /* where this is "at" (NULL if at root) */ + struct pspec *i_pspec; /* parent spec (NULL if at root) */ + struct deva *i_atdeva; + const char **i_locs; /* locators (as given by pspec's iattr) */ + int i_cfflags; /* flags from config line */ + int i_lineno; /* line # in config, for later errors */ + const char *i_srcfile; /* file it appears in */ + int i_level; /* position between negated instances */ + int i_active; +#define DEVI_ORPHAN 0 /* instance has no active parent */ +#define DEVI_ACTIVE 1 /* instance has an active parent */ +#define DEVI_IGNORED 2 /* instance's parent has been removed */ +#define DEVI_BROKEN 3 /* instance is broken (syntax error) */ + int i_pseudoroot; /* instance is pseudoroot */ + + /* created during packing or ioconf.c generation */ + short i_collapsed; /* set => this alias no longer needed */ + u_short i_cfindex; /* our index in cfdata */ + int i_locoff; /* offset in locators.vec */ + +}; +/* special units */ +#define STAR (-1) /* unit number for, e.g., "sd*" */ +#define WILD (-2) /* unit number for, e.g., "sd?" */ + +/* + * Files or objects. This structure defines the common fields + * between the two. + */ +struct filetype +{ + const char *fit_srcfile; /* the name of the "files" file that got us */ + u_short fit_srcline; /* and the line number */ + u_char fit_flags; /* as below */ + char fit_lastc; /* last char from path */ + const char *fit_path; /* full file path */ + const char *fit_prefix; /* any file prefix */ + size_t fit_len; /* path string length */ + int fit_suffix; /* single char suffix */ + struct attr *fit_attr; /* owner attr */ + TAILQ_ENTRY(files) fit_anext; /* next file in attr */ +}; +/* Anything less than 0x10 is sub-type specific */ + +/* + * Files. Each file is either standard (always included) or optional, + * depending on whether it has names on which to *be* optional. The + * options field (fi_optx) is an expression tree of type struct + * condexpr, with nodes for OR, AND, and NOT, as well as atoms (words) + * representing some particular option. + * + * For any file marked as needs-count or needs-flag, fixfiles() will + * build fi_optf, a `flat list' of the options with nv_num fields that + * contain counts or `need' flags; this is used in mkheaders(). + */ +struct files { + struct filetype fi_fit; + TAILQ_ENTRY(files) fi_next; + const char *fi_tail; /* name, i.e., strrchr(fi_path, '/') + 1 */ + const char *fi_base; /* tail minus ".c" (or whatever) */ + struct condexpr *fi_optx; /* options expression */ + struct nvlist *fi_optf; /* flattened version of above, if needed */ + const char *fi_mkrule; /* special make rule, if any */ +}; +#define fi_srcfile fi_fit.fit_srcfile +#define fi_srcline fi_fit.fit_srcline +#define fi_flags fi_fit.fit_flags +#define fi_lastc fi_fit.fit_lastc +#define fi_path fi_fit.fit_path +#define fi_prefix fi_fit.fit_prefix +#define fi_suffix fi_fit.fit_suffix +#define fi_len fi_fit.fit_len +#define fi_attr fi_fit.fit_attr +#define fi_anext fi_fit.fit_anext + +/* flags */ +#define FI_SEL 0x01 /* selected */ +#define FI_NEEDSCOUNT 0x02 /* needs-count */ +#define FI_NEEDSFLAG 0x04 /* needs-flag */ +#define FI_HIDDEN 0x08 /* obscured by other(s), base names overlap */ + +/* + * Objects and libraries. This allows precompiled object and library + * files (e.g. binary-only device drivers) to be linked in. + */ +struct objects { + struct filetype oi_fit; + TAILQ_ENTRY(objects) oi_next; + struct condexpr *oi_optx; /* condition expression */ + struct nvlist *oi_optf;/* flattened version of above, if needed */ +}; + +#define oi_srcfile oi_fit.fit_srcfile +#define oi_srcline oi_fit.fit_srcline +#define oi_flags oi_fit.fit_flags +#define oi_lastc oi_fit.fit_lastc +#define oi_path oi_fit.fit_path +#define oi_prefix oi_fit.fit_prefix + +/* flags */ +#define OI_SEL 0x01 /* selected */ +#define OI_NEEDSFLAG 0x02 /* needs-flag */ + +/* + * Condition expressions. + */ + +enum condexpr_types { + CX_ATOM, + CX_NOT, + CX_AND, + CX_OR, +}; +struct condexpr { + enum condexpr_types cx_type; + union { + const char *atom; + struct condexpr *not; + struct { + struct condexpr *left; + struct condexpr *right; + } and, or; + } cx_u; +}; +#define cx_atom cx_u.atom +#define cx_not cx_u.not +#define cx_and cx_u.and +#define cx_or cx_u.or + +/* + * File/object prefixes. These are arranged in a stack, and affect + * the behavior of the source path. + */ +struct prefix { + SLIST_ENTRY(prefix) pf_next; /* next prefix in stack */ + const char *pf_prefix; /* the actual prefix */ +}; + +/* + * Device major informations. + */ +struct devm { + TAILQ_ENTRY(devm) dm_next; + const char *dm_srcfile; /* the name of the "majors" file */ + u_short dm_srcline; /* the line number */ + const char *dm_name; /* [bc]devsw name */ + devmajor_t dm_cmajor; /* character major */ + devmajor_t dm_bmajor; /* block major */ + struct condexpr *dm_opts; /* options */ + struct nvlist *dm_devnodes; /* information on /dev nodes */ +}; + +/* + * Hash tables look up name=value pairs. The pointer value of the name + * is assumed to be constant forever; this can be arranged by interning + * the name. (This is fairly convenient since our lexer does this for + * all identifier-like strings---it has to save them anyway, lest yacc's + * look-ahead wipe out the current one.) + */ +struct hashtab; + +int lkmmode; +const char *conffile; /* source file, e.g., "GENERIC.sparc" */ +const char *machine; /* machine type, e.g., "sparc" or "sun3" */ +const char *machinearch; /* machine arch, e.g., "sparc" or "m68k" */ +struct nvlist *machinesubarches; + /* machine subarches, e.g., "sun68k" or "hpc" */ +const char *ioconfname; /* ioconf name, mutually exclusive to machine */ +const char *srcdir; /* path to source directory (rel. to build) */ +const char *builddir; /* path to build directory */ +const char *defbuilddir; /* default build directory */ +const char *ident; /* kernel "ident"ification string */ +int errors; /* counts calls to error() */ +int minmaxusers; /* minimum "maxusers" parameter */ +int defmaxusers; /* default "maxusers" parameter */ +int maxmaxusers; /* default "maxusers" parameter */ +int maxusers; /* configuration's "maxusers" parameter */ +int maxpartitions; /* configuration's "maxpartitions" parameter */ +int version; /* version of the configuration file */ +struct nvlist *options; /* options */ +struct nvlist *fsoptions; /* filesystems */ +struct nvlist *mkoptions; /* makeoptions */ +struct nvlist *appmkoptions; /* appending mkoptions */ +struct nvlist *condmkoptions; /* conditional makeoption table */ +struct hashtab *devbasetab; /* devbase lookup */ +struct hashtab *devroottab; /* attach at root lookup */ +struct hashtab *devatab; /* devbase attachment lookup */ +struct hashtab *devitab; /* device instance lookup */ +struct hashtab *deaddevitab; /* removed instances lookup */ +struct hashtab *selecttab; /* selects things that are "optional foo" */ +struct hashtab *needcnttab; /* retains names marked "needs-count" */ +struct hashtab *opttab; /* table of configured options */ +struct hashtab *fsopttab; /* table of configured file systems */ +struct dlhash *defopttab; /* options that have been "defopt"'d */ +struct dlhash *defflagtab; /* options that have been "defflag"'d */ +struct dlhash *defparamtab; /* options that have been "defparam"'d */ +struct dlhash *defoptlint; /* lint values for options */ +struct nvhash *deffstab; /* defined file systems */ +struct dlhash *optfiletab; /* "defopt"'d option .h files */ +struct hashtab *attrtab; /* attributes (locators, etc.) */ +struct hashtab *attrdeptab; /* attribute dependencies */ +struct hashtab *bdevmtab; /* block devm lookup */ +struct hashtab *cdevmtab; /* character devm lookup */ + +TAILQ_HEAD(, devbase) allbases; /* list of all devbase structures */ +TAILQ_HEAD(, deva) alldevas; /* list of all devbase attachments */ +TAILQ_HEAD(conftq, config) allcf; /* list of configured kernels */ +TAILQ_HEAD(, devi) alldevi, /* list of all instances */ + allpseudo; /* list of all pseudo-devices */ +TAILQ_HEAD(, devm) alldevms; /* list of all device-majors */ +TAILQ_HEAD(, pspec) allpspecs; /* list of all parent specs */ +int ndevi; /* number of devi's (before packing) */ +int npspecs; /* number of parent specs */ +devmajor_t maxbdevm; /* max number of block major */ +devmajor_t maxcdevm; /* max number of character major */ +int do_devsw; /* 0 if pre-devsw config */ +int oktopackage; /* 0 before setmachine() */ +int devilevel; /* used for devi->i_level */ + +TAILQ_HEAD(, files) allfiles; /* list of all kernel source files */ +TAILQ_HEAD(, objects) allobjects; /* list of all kernel object and + library files */ + +SLIST_HEAD(, prefix) prefixes, /* prefix stack */ + allprefixes; /* all prefixes used (after popped) */ +SLIST_HEAD(, prefix) curdirs; /* curdir stack */ + +extern struct attr allattr; +struct devi **packed; /* arrayified table for packed devi's */ +size_t npacked; /* size of packed table, <= ndevi */ + +struct { /* loc[] table for config */ + const char **vec; + int used; +} locators; + +struct numconst { + int64_t val; + int fmt; +}; + +/* files.c */ +void initfiles(void); +void checkfiles(void); +int fixfiles(void); /* finalize */ +int fixobjects(void); +int fixdevsw(void); +void addfile(const char *, struct condexpr *, u_char, const char *); +void addobject(const char *, struct condexpr *, u_char); +int expr_eval(struct condexpr *, int (*)(const char *, void *), void *); + +/* hash.c */ +struct hashtab *ht_new(void); +void ht_free(struct hashtab *); +int ht_insrep2(struct hashtab *, const char *, const char *, void *, int); +int ht_insrep(struct hashtab *, const char *, void *, int); +#define ht_insert2(ht, nam1, nam2, val) ht_insrep2(ht, nam1, nam2, val, 0) +#define ht_insert(ht, nam, val) ht_insrep(ht, nam, val, 0) +#define ht_replace(ht, nam, val) ht_insrep(ht, nam, val, 1) +int ht_remove2(struct hashtab *, const char *, const char *); +int ht_remove(struct hashtab *, const char *); +void *ht_lookup2(struct hashtab *, const char *, const char *); +void *ht_lookup(struct hashtab *, const char *); +void initintern(void); +const char *intern(const char *); +typedef int (*ht_callback2)(const char *, const char *, void *, void *); +typedef int (*ht_callback)(const char *, void *, void *); +int ht_enumerate2(struct hashtab *, ht_callback2, void *); +int ht_enumerate(struct hashtab *, ht_callback, void *); + +/* typed hash, named struct HT, whose type is string -> struct VT */ +#define DECLHASH(HT, VT) \ + struct HT; \ + struct HT *HT##_create(void); \ + int HT##_insert(struct HT *, const char *, struct VT *); \ + int HT##_replace(struct HT *, const char *, struct VT *); \ + int HT##_remove(struct HT *, const char *); \ + struct VT *HT##_lookup(struct HT *, const char *); \ + int HT##_enumerate(struct HT *, \ + int (*)(const char *, struct VT *, void *), \ + void *) +DECLHASH(nvhash, nvlist); +DECLHASH(dlhash, defoptlist); + +/* lint.c */ +void emit_instances(void); +void emit_options(void); +void emit_params(void); + +/* main.c */ +extern int Mflag; +void addoption(const char *, const char *); +void addfsoption(const char *); +void addmkoption(const char *, const char *); +void appendmkoption(const char *, const char *); +void appendcondmkoption(struct condexpr *, const char *, const char *); +void deffilesystem(struct nvlist *, struct nvlist *); +void defoption(const char *, struct defoptlist *, struct nvlist *); +void defflag(const char *, struct defoptlist *, struct nvlist *, int); +void defparam(const char *, struct defoptlist *, struct nvlist *, int); +void deloption(const char *); +void delfsoption(const char *); +void delmkoption(const char *); +int devbase_has_instances(struct devbase *, int); +int is_declared_option(const char *); +int deva_has_instances(struct deva *, int); +void setupdirs(void); +const char *strtolower(const char *); + +/* tests on option types */ +#define OPT_FSOPT(n) (nvhash_lookup(deffstab, (n)) != NULL) +#define OPT_DEFOPT(n) (dlhash_lookup(defopttab, (n)) != NULL) +#define OPT_DEFFLAG(n) (dlhash_lookup(defflagtab, (n)) != NULL) +#define OPT_DEFPARAM(n) (dlhash_lookup(defparamtab, (n)) != NULL) +#define OPT_OBSOLETE(n) (dlhash_lookup(obsopttab, (n)) != NULL) +#define DEFINED_OPTION(n) (is_declared_option((n))) + +/* main.c */ +void logconfig_include(FILE *, const char *); + +/* mkdevsw.c */ +int mkdevsw(void); + +/* mkheaders.c */ +int mkheaders(void); +int moveifchanged(const char *, const char *); +int emitlocs(void); +int emitioconfh(void); + +/* mkioconf.c */ +int mkioconf(void); + +/* mkmakefile.c */ +int mkmakefile(void); + +/* mkswap.c */ +int mkswap(void); + +/* pack.c */ +void pack(void); + +/* scan.l */ +u_short currentline(void); +int firstfile(const char *); +void package(const char *); +int include(const char *, int, int, int); + +/* sem.c, other than for yacc actions */ +void initsem(void); +int onlist(struct nvlist *, void *); + +/* util.c */ +void prefix_push(const char *); +void prefix_pop(void); +char *sourcepath(const char *); +extern int dflag; +#define CFGDBG(n, ...) \ + do { if ((dflag) >= (n)) cfgdbg(__VA_ARGS__); } while (0) +void cfgdbg(const char *, ...) /* debug info */ + __printflike(1, 2); +void cfgwarn(const char *, ...) /* immediate warns */ + __printflike(1, 2); +void cfgxwarn(const char *, int, const char *, ...) /* delayed warns */ + __printflike(3, 4); +void cfgerror(const char *, ...) /* immediate errs */ + __printflike(1, 2); +void cfgxerror(const char *, int, const char *, ...) /* delayed errs */ + __printflike(3, 4); +__dead void panic(const char *, ...) + __printflike(1, 2); +struct nvlist *newnv(const char *, const char *, void *, long long, struct nvlist *); +void nvfree(struct nvlist *); +void nvfreel(struct nvlist *); +struct nvlist *nvcat(struct nvlist *, struct nvlist *); +void autogen_comment(FILE *, const char *); +struct defoptlist *defoptlist_create(const char *, const char *, const char *); +void defoptlist_destroy(struct defoptlist *); +struct defoptlist *defoptlist_append(struct defoptlist *, struct defoptlist *); +struct attrlist *attrlist_create(void); +struct attrlist *attrlist_cons(struct attrlist *, struct attr *); +void attrlist_destroy(struct attrlist *); +void attrlist_destroyall(struct attrlist *); +struct loclist *loclist_create(const char *, const char *, long long); +void loclist_destroy(struct loclist *); +struct condexpr *condexpr_create(enum condexpr_types); +void condexpr_destroy(struct condexpr *); + +/* liby */ +void yyerror(const char *); +int yylex(void); diff --git a/buildrump.sh/src/usr.bin/config/files.c b/buildrump.sh/src/usr.bin/config/files.c new file mode 100644 index 00000000..3b9ddd77 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/files.c @@ -0,0 +1,594 @@ +/* $NetBSD: files.c,v 1.18 2014/11/17 00:53:15 uebayasi Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)files.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: files.c,v 1.18 2014/11/17 00:53:15 uebayasi Exp $"); + +#include <sys/param.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <util.h> +#include "defs.h" + +extern const char *yyfile; + +/* + * We check that each full path name is unique. File base names + * should generally also be unique, e.g., having both a net/xx.c and + * a kern/xx.c (or, worse, a net/xx.c and a new/xx.c++) is probably + * wrong, but is permitted under some conditions. + */ +static struct hashtab *basetab; /* file base names */ +static struct hashtab *pathtab; /* full path names */ + +static struct files **unchecked; + +static void addfiletoattr(const char *, struct files *); +static int checkaux(const char *, void *); +static int fixcount(const char *, void *); +static int fixfsel(const char *, void *); +static int fixsel(const char *, void *); + +void +initfiles(void) +{ + + basetab = ht_new(); + pathtab = ht_new(); + TAILQ_INIT(&allfiles); + unchecked = &TAILQ_FIRST(&allfiles); + TAILQ_INIT(&allobjects); +} + +void +addfile(const char *path, struct condexpr *optx, u_char flags, const char *rule) +{ + struct files *fi; + const char *dotp, *tail; + size_t baselen; + int needc, needf; + char base[200]; + + /* check various errors */ + needc = flags & FI_NEEDSCOUNT; + needf = flags & FI_NEEDSFLAG; + if (needc && needf) { + cfgerror("cannot mix needs-count and needs-flag"); + goto bad; + } + if (optx == NULL && (needc || needf)) { + cfgerror("nothing to %s for %s", needc ? "count" : "flag", + path); + goto bad; + } + + /* find last part of pathname, and same without trailing suffix */ + tail = strrchr(path, '/'); + if (tail == NULL) + tail = path; + else + tail++; + dotp = strrchr(tail, '.'); + if (dotp == NULL || dotp[1] == 0 || + (baselen = (size_t)(dotp - tail)) >= sizeof(base)) { + cfgerror("invalid pathname `%s'", path); + goto bad; + } + + /* + * Commit this file to memory. We will decide later whether it + * will be used after all. + */ + fi = ecalloc(1, sizeof *fi); + if (ht_insert(pathtab, path, fi)) { + free(fi); + if ((fi = ht_lookup(pathtab, path)) == NULL) + panic("addfile: ht_lookup(%s)", path); + + /* + * If it's a duplicate entry, it is must specify a make + * rule, and only a make rule, and must come from + * a different source file than the original entry. + * If it does otherwise, it is disallowed. This allows + * machine-dependent files to override the compilation + * options for specific files. + */ + if (rule != NULL && optx == NULL && flags == 0 && + yyfile != fi->fi_srcfile) { + fi->fi_mkrule = rule; + return; + } + cfgerror("duplicate file %s", path); + cfgxerror(fi->fi_srcfile, fi->fi_srcline, + "here is the original definition"); + goto bad; + } + memcpy(base, tail, baselen); + base[baselen] = 0; + fi->fi_srcfile = yyfile; + fi->fi_srcline = currentline(); + fi->fi_flags = flags; + fi->fi_path = path; + fi->fi_tail = tail; + fi->fi_base = intern(base); + fi->fi_prefix = SLIST_EMPTY(&prefixes) ? NULL : + SLIST_FIRST(&prefixes)->pf_prefix; + fi->fi_len = strlen(path); + fi->fi_suffix = path[fi->fi_len - 1]; + fi->fi_optx = optx; + fi->fi_optf = NULL; + fi->fi_mkrule = rule; + fi->fi_attr = NULL; + TAILQ_INSERT_TAIL(&allfiles, fi, fi_next); + return; + bad: + if (optx != NULL) { + condexpr_destroy(optx); + } +} + +void +addobject(const char *path, struct condexpr *optx, u_char flags) +{ + struct objects *oi; + + /* + * Commit this object to memory. We will decide later whether it + * will be used after all. + */ + oi = ecalloc(1, sizeof *oi); + if (ht_insert(pathtab, path, oi)) { + free(oi); + if ((oi = ht_lookup(pathtab, path)) == NULL) + panic("addfile: ht_lookup(%s)", path); + cfgerror("duplicate file %s", path); + cfgxerror(oi->oi_srcfile, oi->oi_srcline, + "here is the original definition"); + } + oi->oi_srcfile = yyfile; + oi->oi_srcline = currentline(); + oi->oi_flags = flags; + oi->oi_path = path; + oi->oi_prefix = SLIST_EMPTY(&prefixes) ? NULL : + SLIST_FIRST(&prefixes)->pf_prefix; + oi->oi_optx = optx; + oi->oi_optf = NULL; + TAILQ_INSERT_TAIL(&allobjects, oi, oi_next); + return; +} + +static void +addfiletoattr(const char *name, struct files *fi) +{ + struct attr *a; + + a = ht_lookup(attrtab, name); + if (a == NULL) { + CFGDBG(1, "attr `%s' not found", name); + } else { + fi->fi_attr = a; + TAILQ_INSERT_TAIL(&a->a_files, fi, fi_anext); + } +} + +/* + * We have finished reading some "files" file, either ../../conf/files + * or ./files.$machine. Make sure that everything that is flagged as + * needing a count is reasonable. (This prevents ../../conf/files from + * depending on some machine-specific device.) + */ +void +checkfiles(void) +{ + struct files *fi, *last; + + last = NULL; + for (fi = *unchecked; fi != NULL; + last = fi, fi = TAILQ_NEXT(fi, fi_next)) { + if ((fi->fi_flags & FI_NEEDSCOUNT) != 0) + (void)expr_eval(fi->fi_optx, checkaux, fi); + } + if (last != NULL) + unchecked = &TAILQ_NEXT(last, fi_next); +} + +/* + * Auxiliary function for checkfiles, called from expr_eval. + * We are not actually interested in the expression's value. + */ +static int +checkaux(const char *name, void *context) +{ + struct files *fi = context; + + if (ht_lookup(devbasetab, name) == NULL) { + cfgxerror(fi->fi_srcfile, fi->fi_srcline, + "`%s' is not a countable device", + name); + /* keep fixfiles() from complaining again */ + fi->fi_flags |= FI_HIDDEN; + } + return (0); +} + +/* + * We have finished reading everything. Tack the files down: calculate + * selection and counts as needed. Check that the object files built + * from the selected sources do not collide. + */ +int +fixfiles(void) +{ + struct files *fi, *ofi; + struct nvlist *flathead, **flatp; + int err, sel; + + err = 0; + TAILQ_FOREACH(fi, &allfiles, fi_next) { + + /* Skip files that generated counted-device complaints. */ + if (fi->fi_flags & FI_HIDDEN) + continue; + + if (fi->fi_optx != NULL) { + if (fi->fi_optx->cx_type == CX_ATOM) { + addfiletoattr(fi->fi_optx->cx_u.atom, fi); + } + flathead = NULL; + flatp = &flathead; + sel = expr_eval(fi->fi_optx, + fi->fi_flags & FI_NEEDSCOUNT ? fixcount : + fi->fi_flags & FI_NEEDSFLAG ? fixfsel : + fixsel, + &flatp); + fi->fi_optf = flathead; + if (!sel) + continue; + } + + /* We like this file. Make sure it generates a unique .o. */ + if (ht_insert(basetab, fi->fi_base, fi)) { + if ((ofi = ht_lookup(basetab, fi->fi_base)) == NULL) + panic("fixfiles ht_lookup(%s)", fi->fi_base); + /* + * If the new file comes from a different source, + * allow the new one to override the old one. + */ + if (fi->fi_path != ofi->fi_path) { + if (ht_replace(basetab, fi->fi_base, fi) != 1) + panic("fixfiles ht_replace(%s)", + fi->fi_base); + ofi->fi_flags &= (u_char)~FI_SEL; + ofi->fi_flags |= FI_HIDDEN; + } else { + cfgxerror(fi->fi_srcfile, fi->fi_srcline, + "object file collision on %s.o, from %s", + fi->fi_base, fi->fi_path); + cfgxerror(ofi->fi_srcfile, ofi->fi_srcline, + "here is the previous file: %s", + ofi->fi_path); + err = 1; + } + } + fi->fi_flags |= FI_SEL; + CFGDBG(3, "file selected `%s'", fi->fi_path); + + /* Add other files to the default "netbsd" attribute. */ + if (fi->fi_attr == NULL) { + addfiletoattr(allattr.a_name, fi); + } + CFGDBG(3, "file `%s' belongs to attr `%s'", fi->fi_path, + fi->fi_attr->a_name); + } + return (err); +} + +/* + * We have finished reading everything. Tack the objects down: calculate + * selection. + */ +int +fixobjects(void) +{ + struct objects *oi; + struct nvlist *flathead, **flatp; + int err, sel; + + err = 0; + TAILQ_FOREACH(oi, &allobjects, oi_next) { + /* Optional: see if it is to be included. */ + if (oi->oi_optx != NULL) { + flathead = NULL; + flatp = &flathead; + sel = expr_eval(oi->oi_optx, + oi->oi_flags & OI_NEEDSFLAG ? fixfsel : + fixsel, + &flatp); + oi->oi_optf = flathead; + if (!sel) + continue; + } + + oi->oi_flags |= OI_SEL; + } + return (err); +} + +/* + * We have finished reading everything. Tack the devsws down: calculate + * selection. + */ +int +fixdevsw(void) +{ + int error; + struct devm *dm, *res; + struct hashtab *fixdevmtab; + char mstr[16]; + + error = 0; + fixdevmtab = ht_new(); + + TAILQ_FOREACH(dm, &alldevms, dm_next) { + res = ht_lookup(fixdevmtab, intern(dm->dm_name)); + if (res != NULL) { + if (res->dm_cmajor != dm->dm_cmajor || + res->dm_bmajor != dm->dm_bmajor) { + cfgxerror(res->dm_srcfile, res->dm_srcline, + "device-major '%s' " + "block %d, char %d redefined" + " at %s:%d as block %d, char %d", + res->dm_name, + res->dm_bmajor, res->dm_cmajor, + dm->dm_srcfile, dm->dm_srcline, + dm->dm_bmajor, dm->dm_cmajor); + } else { + cfgxerror(res->dm_srcfile, res->dm_srcline, + "device-major '%s' " + "(block %d, char %d) duplicated" + " at %s:%d", + dm->dm_name, dm->dm_bmajor, + dm->dm_cmajor, + dm->dm_srcfile, dm->dm_srcline); + } + error = 1; + goto out; + } + if (ht_insert(fixdevmtab, intern(dm->dm_name), dm)) { + panic("fixdevsw: %s char %d block %d", + dm->dm_name, dm->dm_cmajor, dm->dm_bmajor); + } + + if (dm->dm_opts != NULL && + !expr_eval(dm->dm_opts, fixsel, NULL)) + continue; + + if (dm->dm_cmajor != NODEVMAJOR) { + if (ht_lookup(cdevmtab, intern(dm->dm_name)) != NULL) { + cfgxerror(dm->dm_srcfile, dm->dm_srcline, + "device-major of character device '%s' " + "is already defined", dm->dm_name); + error = 1; + goto out; + } + (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_cmajor); + if (ht_lookup(cdevmtab, intern(mstr)) != NULL) { + cfgxerror(dm->dm_srcfile, dm->dm_srcline, + "device-major of character major '%d' " + "is already defined", dm->dm_cmajor); + error = 1; + goto out; + } + if (ht_insert(cdevmtab, intern(dm->dm_name), dm) || + ht_insert(cdevmtab, intern(mstr), dm)) { + panic("fixdevsw: %s character major %d", + dm->dm_name, dm->dm_cmajor); + } + } + if (dm->dm_bmajor != NODEVMAJOR) { + if (ht_lookup(bdevmtab, intern(dm->dm_name)) != NULL) { + cfgxerror(dm->dm_srcfile, dm->dm_srcline, + "device-major of block device '%s' " + "is already defined", dm->dm_name); + error = 1; + goto out; + } + (void)snprintf(mstr, sizeof(mstr), "%d", dm->dm_bmajor); + if (ht_lookup(bdevmtab, intern(mstr)) != NULL) { + cfgxerror(dm->dm_srcfile, dm->dm_srcline, + "device-major of block major '%d' " + "is already defined", dm->dm_bmajor); + error = 1; + goto out; + } + if (ht_insert(bdevmtab, intern(dm->dm_name), dm) || + ht_insert(bdevmtab, intern(mstr), dm)) { + panic("fixdevsw: %s block major %d", + dm->dm_name, dm->dm_bmajor); + } + } + } + +out: + ht_free(fixdevmtab); + return (error); +} + +/* + * Called when evaluating a needs-count expression. Make sure the + * atom is a countable device. The expression succeeds iff there + * is at least one of them (note that while `xx*' will not always + * set xx's d_umax > 0, you cannot mix '*' and needs-count). The + * mkheaders() routine wants a flattened, in-order list of the + * atoms for `#define name value' lines, so we build that as we + * are called to eval each atom. + */ +static int +fixcount(const char *name, void *context) +{ + struct nvlist ***p = context; + struct devbase *dev; + struct nvlist *nv; + + dev = ht_lookup(devbasetab, name); + if (dev == NULL) /* cannot occur here; we checked earlier */ + panic("fixcount(%s)", name); + nv = newnv(name, NULL, NULL, dev->d_umax, NULL); + **p = nv; + *p = &nv->nv_next; + (void)ht_insert(needcnttab, name, nv); + return (dev->d_umax != 0); +} + +/* + * Called from fixfiles when eval'ing a selection expression for a + * file that will generate a .h with flags. We will need the flat list. + */ +static int +fixfsel(const char *name, void *context) +{ + struct nvlist ***p = context; + struct nvlist *nv; + int sel; + + sel = ht_lookup(selecttab, name) != NULL; + nv = newnv(name, NULL, NULL, sel, NULL); + **p = nv; + *p = &nv->nv_next; + return (sel); +} + +/* + * As for fixfsel above, but we do not need the flat list. + */ +static int +/*ARGSUSED*/ +fixsel(const char *name, void *context) +{ + + return (ht_lookup(selecttab, name) != NULL); +} + +/* + * Eval an expression tree. Calls the given function on each node, + * passing it the given context & the name; return value is &/|/! of + * results of evaluating atoms. + * + * No short circuiting ever occurs. fn must return 0 or 1 (otherwise + * our mixing of C's bitwise & boolean here may give surprises). + */ +int +expr_eval(struct condexpr *expr, int (*fn)(const char *, void *), void *ctx) +{ + int lhs, rhs; + + switch (expr->cx_type) { + + case CX_ATOM: + return ((*fn)(expr->cx_atom, ctx)); + + case CX_NOT: + return (!expr_eval(expr->cx_not, fn, ctx)); + + case CX_AND: + lhs = expr_eval(expr->cx_and.left, fn, ctx); + rhs = expr_eval(expr->cx_and.right, fn, ctx); + return (lhs & rhs); + + case CX_OR: + lhs = expr_eval(expr->cx_or.left, fn, ctx); + rhs = expr_eval(expr->cx_or.right, fn, ctx); + return (lhs | rhs); + } + panic("invalid condexpr type %d", (int)expr->cx_type); + /* NOTREACHED */ + return (0); +} + +#ifdef DEBUG +/* + * Print expression tree. + */ +void +prexpr(struct nvlist *expr) +{ + static void pr0(); + + printf("expr ="); + pr0(expr); + printf("\n"); + (void)fflush(stdout); +} + +static void +pr0(struct nvlist *e) +{ + + switch (e->nv_num) { + case FX_ATOM: + printf(" %s", e->nv_name); + return; + case FX_NOT: + printf(" (!"); + break; + case FX_AND: + printf(" (&"); + break; + case FX_OR: + printf(" (|"); + break; + default: + printf(" (?%lld?", e->nv_num); + break; + } + if (e->nv_ptr) + pr0(e->nv_ptr); + pr0(e->nv_next); + printf(")"); +} +#endif diff --git a/buildrump.sh/src/usr.bin/config/gram.y b/buildrump.sh/src/usr.bin/config/gram.y new file mode 100644 index 00000000..7e635981 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/gram.y @@ -0,0 +1,1443 @@ +%{ +/* $NetBSD: gram.y,v 1.46 2014/11/04 23:01:23 joerg Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)gram.y 8.1 (Berkeley) 6/6/93 + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: gram.y,v 1.46 2014/11/04 23:01:23 joerg Exp $"); + +#include <sys/types.h> +#include <sys/param.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include "defs.h" +#include "sem.h" + +#define FORMAT(n) (((n).fmt == 8 && (n).val != 0) ? "0%llo" : \ + ((n).fmt == 16) ? "0x%llx" : "%lld") + +#define stop(s) cfgerror(s), exit(1) + +static struct config conf; /* at most one active at a time */ + + +/* + * Allocation wrapper functions + */ +static void wrap_alloc(void *ptr, unsigned code); +static void wrap_continue(void); +static void wrap_cleanup(void); + +/* + * Allocation wrapper type codes + */ +#define WRAP_CODE_nvlist 1 +#define WRAP_CODE_defoptlist 2 +#define WRAP_CODE_loclist 3 +#define WRAP_CODE_attrlist 4 +#define WRAP_CODE_condexpr 5 + +/* + * The allocation wrappers themselves + */ +#define DECL_ALLOCWRAP(t) static struct t *wrap_mk_##t(struct t *arg) + +DECL_ALLOCWRAP(nvlist); +DECL_ALLOCWRAP(defoptlist); +DECL_ALLOCWRAP(loclist); +DECL_ALLOCWRAP(attrlist); +DECL_ALLOCWRAP(condexpr); + +/* allow shorter names */ +#define wrap_mk_loc(p) wrap_mk_loclist(p) +#define wrap_mk_cx(p) wrap_mk_condexpr(p) + +/* + * Macros for allocating new objects + */ + +/* old-style for struct nvlist */ +#define new0(n,s,p,i,x) wrap_mk_nvlist(newnv(n, s, p, i, x)) +#define new_n(n) new0(n, NULL, NULL, 0, NULL) +#define new_nx(n, x) new0(n, NULL, NULL, 0, x) +#define new_ns(n, s) new0(n, s, NULL, 0, NULL) +#define new_si(s, i) new0(NULL, s, NULL, i, NULL) +#define new_nsi(n,s,i) new0(n, s, NULL, i, NULL) +#define new_np(n, p) new0(n, NULL, p, 0, NULL) +#define new_s(s) new0(NULL, s, NULL, 0, NULL) +#define new_p(p) new0(NULL, NULL, p, 0, NULL) +#define new_px(p, x) new0(NULL, NULL, p, 0, x) +#define new_sx(s, x) new0(NULL, s, NULL, 0, x) +#define new_nsx(n,s,x) new0(n, s, NULL, 0, x) +#define new_i(i) new0(NULL, NULL, NULL, i, NULL) + +/* new style, type-polymorphic; ordinary and for types with multiple flavors */ +#define MK0(t) wrap_mk_##t(mk_##t()) +#define MK1(t, a0) wrap_mk_##t(mk_##t(a0)) +#define MK2(t, a0, a1) wrap_mk_##t(mk_##t(a0, a1)) +#define MK3(t, a0, a1, a2) wrap_mk_##t(mk_##t(a0, a1, a2)) + +#define MKF0(t, f) wrap_mk_##t(mk_##t##_##f()) +#define MKF1(t, f, a0) wrap_mk_##t(mk_##t##_##f(a0)) +#define MKF2(t, f, a0, a1) wrap_mk_##t(mk_##t##_##f(a0, a1)) + +/* + * Data constructors + */ + +static struct defoptlist *mk_defoptlist(const char *, const char *, + const char *); +static struct loclist *mk_loc(const char *, const char *, long long); +static struct loclist *mk_loc_val(const char *, struct loclist *); +static struct attrlist *mk_attrlist(struct attrlist *, struct attr *); +static struct condexpr *mk_cx_atom(const char *); +static struct condexpr *mk_cx_not(struct condexpr *); +static struct condexpr *mk_cx_and(struct condexpr *, struct condexpr *); +static struct condexpr *mk_cx_or(struct condexpr *, struct condexpr *); + +/* + * Other private functions + */ + +static void setmachine(const char *, const char *, struct nvlist *, int); +static void check_maxpart(void); + +static struct loclist *present_loclist(struct loclist *ll); +static void app(struct loclist *, struct loclist *); +static struct loclist *locarray(const char *, int, struct loclist *, int); +static struct loclist *namelocvals(const char *, struct loclist *); + +%} + +%union { + struct attr *attr; + struct devbase *devb; + struct deva *deva; + struct nvlist *list; + struct defoptlist *defoptlist; + struct loclist *loclist; + struct attrlist *attrlist; + struct condexpr *condexpr; + const char *str; + struct numconst num; + int64_t val; + u_char flag; + devmajor_t devmajor; + int32_t i32; +} + +%token AND AT ATTACH +%token BLOCK BUILD +%token CHAR COLONEQ COMPILE_WITH CONFIG +%token DEFFS DEFINE DEFOPT DEFPARAM DEFFLAG DEFPSEUDO DEFPSEUDODEV +%token DEVICE DEVCLASS DUMPS DEVICE_MAJOR +%token ENDFILE +%token XFILE FILE_SYSTEM FLAGS +%token IDENT IOCONF +%token LINKZERO +%token XMACHINE MAJOR MAKEOPTIONS MAXUSERS MAXPARTITIONS MINOR +%token NEEDS_COUNT NEEDS_FLAG NO +%token XOBJECT OBSOLETE ON OPTIONS +%token PACKAGE PLUSEQ PREFIX PSEUDO_DEVICE PSEUDO_ROOT +%token ROOT +%token SELECT SINGLE SOURCE +%token TYPE +%token VECTOR VERSION +%token WITH +%token <num> NUMBER +%token <str> PATHNAME QSTRING WORD EMPTYSTRING +%token ENDDEFS + +%type <condexpr> fopts condexpr condatom +%type <condexpr> cond_or_expr cond_and_expr cond_prefix_expr +%type <condexpr> cond_base_expr +%type <str> fs_spec +%type <flag> fflags fflag oflags oflag +%type <str> rule +%type <attr> depend +%type <devb> devbase +%type <deva> devattach_opt +%type <list> atlist +%type <loclist> interface_opt +%type <str> atname +%type <loclist> loclist locdef +%type <str> locdefault +%type <loclist> values locdefaults +%type <attrlist> depend_list depends +%type <loclist> locators locator +%type <list> dev_spec +%type <str> device_instance +%type <str> attachment +%type <str> value +%type <val> major_minor +%type <num> signed_number +%type <i32> int32 npseudo device_flags +%type <str> deffs +%type <list> deffses +%type <defoptlist> defopt +%type <defoptlist> defopts +%type <str> optdepend +%type <list> optdepends +%type <list> optdepend_list +%type <str> optfile_opt +%type <list> subarches +%type <str> filename stringvalue locname mkvarname +%type <devmajor> device_major_block device_major_char +%type <list> devnodes devnodetype devnodeflags devnode_dims + +%% + +/* + * A complete configuration consists of both the selection part (a + * kernel config such as GENERIC or SKYNET, plus also the various + * std.* files), which selects the material to be in the kernel, and + * also the definition part (files, files.*, etc.) that declares what + * material is available to be placed in kernels. + * + * The two parts have almost entirely separate syntaxes. This grammar + * covers both of them. When config is run on a kernel configuration + * file, the std.* file for the port is included explicitly. The + * files.* files are included implicitly when the std.* file declares + * the machine type. + * + * The machine spec, which brings in the definition part, must appear + * before all configuration material except for the "topthings"; these + * are the "source" and "build" declarations that tell config where + * things are. These are not used by default. + * + * A previous version of this comment contained the following text: + * + * Note that we do not have sufficient keywords to enforce any + * order between elements of "topthings" without introducing + * shift/reduce conflicts. Instead, check order requirements in + * the C code. + * + * As of March 2012 this comment makes no sense, as there are only two + * topthings and no reason for them to be forcibly ordered. + * Furthermore, the statement about conflicts is false. + */ + +/* Complete configuration. */ +configuration: + topthings machine_spec definition_part selection_part +; + +/* Sequence of zero or more topthings. */ +topthings: + /* empty */ + | topthings topthing +; + +/* Directory specification. */ +topthing: + '\n' + | SOURCE filename '\n' { if (!srcdir) srcdir = $2; } + | BUILD filename '\n' { if (!builddir) builddir = $2; } +; + +/* "machine foo" from std.whatever */ +machine_spec: + XMACHINE WORD '\n' { setmachine($2,NULL,NULL,0); } + | XMACHINE WORD WORD '\n' { setmachine($2,$3,NULL,0); } + | XMACHINE WORD WORD subarches '\n' { setmachine($2,$3,$4,0); } + | IOCONF WORD '\n' { setmachine($2,NULL,NULL,1); } + | error { stop("cannot proceed without machine or ioconf specifier"); } +; + +/* One or more sub-arches. */ +subarches: + WORD { $$ = new_n($1); } + | subarches WORD { $$ = new_nx($2, $1); } +; + +/************************************************************/ + +/* + * The machine definitions grammar. + */ + +/* Complete definition part: the contents of all files.* files. */ +definition_part: + definitions ENDDEFS { check_maxpart(); check_version(); } +; + +/* Zero or more definitions. Trap errors. */ +definitions: + /* empty */ + | definitions '\n' + | definitions definition '\n' { wrap_continue(); } + | definitions error '\n' { wrap_cleanup(); } + | definitions ENDFILE { enddefs(); checkfiles(); } +; + +/* A single definition. */ +definition: + define_file + | define_object + | define_device_major + | define_prefix + | define_devclass + | define_filesystems + | define_attribute + | define_option + | define_flag + | define_obsolete_flag + | define_param + | define_obsolete_param + | define_device + | define_device_attachment + | define_maxpartitions + | define_maxusers + | define_makeoptions + | define_pseudo + | define_pseudodev + | define_major + | define_version +; + +/* source file: file foo/bar.c bar|baz needs-flag compile-with blah */ +define_file: + XFILE filename fopts fflags rule { addfile($2, $3, $4, $5); } +; + +/* object file: object zot.o foo|zot needs-flag */ +define_object: + XOBJECT filename fopts oflags { addobject($2, $3, $4); } +; + +/* device major declaration */ +define_device_major: + DEVICE_MAJOR WORD device_major_char device_major_block fopts devnodes + { + adddevm($2, $3, $4, $5, $6); + do_devsw = 1; + } +; + +/* prefix delimiter */ +define_prefix: + PREFIX filename { prefix_push($2); } + | PREFIX { prefix_pop(); } +; + +define_devclass: + DEVCLASS WORD { (void)defdevclass($2, NULL, NULL, 1); } +; + +define_filesystems: + DEFFS deffses optdepend_list { deffilesystem($2, $3); } +; + +define_attribute: + DEFINE WORD interface_opt depend_list + { (void)defattr0($2, $3, $4, 0); } +; + +define_option: + DEFOPT optfile_opt defopts optdepend_list + { defoption($2, $3, $4); } +; + +define_flag: + DEFFLAG optfile_opt defopts optdepend_list + { defflag($2, $3, $4, 0); } +; + +define_obsolete_flag: + OBSOLETE DEFFLAG optfile_opt defopts + { defflag($3, $4, NULL, 1); } +; + +define_param: + DEFPARAM optfile_opt defopts optdepend_list + { defparam($2, $3, $4, 0); } +; + +define_obsolete_param: + OBSOLETE DEFPARAM optfile_opt defopts + { defparam($3, $4, NULL, 1); } +; + +define_device: + DEVICE devbase interface_opt depend_list + { defdev($2, $3, $4, 0); } +; + +define_device_attachment: + ATTACH devbase AT atlist devattach_opt depend_list + { defdevattach($5, $2, $4, $6); } +; + +define_maxpartitions: + MAXPARTITIONS int32 { maxpartitions = $2; } +; + +define_maxusers: + MAXUSERS int32 int32 int32 + { setdefmaxusers($2, $3, $4); } +; + +define_makeoptions: + MAKEOPTIONS condmkopt_list +; + +define_pseudo: + /* interface_opt in DEFPSEUDO is for backwards compatibility */ + DEFPSEUDO devbase interface_opt depend_list + { defdev($2, $3, $4, 1); } +; + +define_pseudodev: + DEFPSEUDODEV devbase interface_opt depend_list + { defdev($2, $3, $4, 2); } +; + +define_major: + MAJOR '{' majorlist '}' +; + +define_version: + VERSION int32 { setversion($2); } +; + +/* file options: optional expression of conditions */ +fopts: + /* empty */ { $$ = NULL; } + | condexpr { $$ = $1; } +; + +/* zero or more flags for a file */ +fflags: + /* empty */ { $$ = 0; } + | fflags fflag { $$ = $1 | $2; } +; + +/* one flag for a file */ +fflag: + NEEDS_COUNT { $$ = FI_NEEDSCOUNT; } + | NEEDS_FLAG { $$ = FI_NEEDSFLAG; } +; + +/* extra compile directive for a source file */ +rule: + /* empty */ { $$ = NULL; } + | COMPILE_WITH stringvalue { $$ = $2; } +; + +/* zero or more flags for an object file */ +oflags: + /* empty */ { $$ = 0; } + | oflags oflag { $$ = $1 | $2; } +; + +/* a single flag for an object file */ +oflag: + NEEDS_FLAG { $$ = OI_NEEDSFLAG; } +; + +/* char 55 */ +device_major_char: + /* empty */ { $$ = -1; } + | CHAR int32 { $$ = $2; } +; + +/* block 33 */ +device_major_block: + /* empty */ { $$ = -1; } + | BLOCK int32 { $$ = $2; } +; + +/* device node specification */ +devnodes: + /* empty */ { $$ = new_s("DEVNODE_DONTBOTHER"); } + | devnodetype ',' devnodeflags { $$ = nvcat($1, $3); } + | devnodetype { $$ = $1; } +; + +/* device nodes without flags */ +devnodetype: + SINGLE { $$ = new_s("DEVNODE_SINGLE"); } + | VECTOR '=' devnode_dims { $$ = nvcat(new_s("DEVNODE_VECTOR"), $3); } +; + +/* dimensions (?) */ +devnode_dims: + NUMBER { $$ = new_i($1.val); } + | NUMBER ':' NUMBER { + struct nvlist *__nv1, *__nv2; + + __nv1 = new_i($1.val); + __nv2 = new_i($3.val); + $$ = nvcat(__nv1, __nv2); + } +; + +/* flags for device nodes */ +devnodeflags: + LINKZERO { $$ = new_s("DEVNODE_FLAG_LINKZERO");} +; + +/* one or more file system names */ +deffses: + deffs { $$ = new_n($1); } + | deffses deffs { $$ = new_nx($2, $1); } +; + +/* a single file system name */ +deffs: + WORD { $$ = $1; } +; + +/* optional locator specification */ +interface_opt: + /* empty */ { $$ = NULL; } + | '{' '}' { $$ = present_loclist(NULL); } + | '{' loclist '}' { $$ = present_loclist($2); } +; + +/* + * loclist order matters, must use right recursion + * XXX wot? + */ + +/* list of locator definitions */ +loclist: + locdef { $$ = $1; } + | locdef ',' loclist { $$ = $1; app($1, $3); } +; + +/* + * "[ WORD locdefault ]" syntax may be unnecessary... + */ + +/* one locator definition */ +locdef: + locname locdefault { $$ = MK3(loc, $1, $2, 0); } + | locname { $$ = MK3(loc, $1, NULL, 0); } + | '[' locname locdefault ']' { $$ = MK3(loc, $2, $3, 1); } + | locname '[' int32 ']' { $$ = locarray($1, $3, NULL, 0); } + | locname '[' int32 ']' locdefaults + { $$ = locarray($1, $3, $5, 0); } + | '[' locname '[' int32 ']' locdefaults ']' + { $$ = locarray($2, $4, $6, 1); } +; + +/* locator name */ +locname: + WORD { $$ = $1; } + | QSTRING { $$ = $1; } +; + +/* locator default value */ +locdefault: + '=' value { $$ = $2; } +; + +/* multiple locator default values */ +locdefaults: + '=' '{' values '}' { $$ = $3; } +; + +/* list of depends, may be empty */ +depend_list: + /* empty */ { $$ = NULL; } + | ':' depends { $$ = $2; } +; + +/* one or more depend items */ +depends: + depend { $$ = MK2(attrlist, NULL, $1); } + | depends ',' depend { $$ = MK2(attrlist, $1, $3); } +; + +/* one depend item (which is an attribute) */ +depend: + WORD { $$ = refattr($1); } +; + +/* list of option depends, may be empty */ +optdepend_list: + /* empty */ { $$ = NULL; } + | ':' optdepends { $$ = $2; } +; + +/* a list of option dependencies */ +optdepends: + optdepend { $$ = new_n($1); } + | optdepends ',' optdepend { $$ = new_nx($3, $1); } +; + +/* one option depend, which is an option name */ +optdepend: + WORD { $$ = $1; } +; + + +/* list of places to attach: attach blah at ... */ +atlist: + atname { $$ = new_n($1); } + | atlist ',' atname { $$ = new_nx($3, $1); } +; + +/* a place to attach a device */ +atname: + WORD { $$ = $1; } + | ROOT { $$ = NULL; } +; + +/* one or more defined options */ +defopts: + defopt { $$ = $1; } + | defopts defopt { $$ = defoptlist_append($2, $1); } +; + +/* one defined option */ +defopt: + WORD { $$ = MK3(defoptlist, $1, NULL, NULL); } + | WORD '=' value { $$ = MK3(defoptlist, $1, $3, NULL); } + | WORD COLONEQ value { $$ = MK3(defoptlist, $1, NULL, $3); } + | WORD '=' value COLONEQ value { $$ = MK3(defoptlist, $1, $3, $5); } +; + +/* list of conditional makeoptions */ +condmkopt_list: + condmkoption + | condmkopt_list ',' condmkoption +; + +/* one conditional make option */ +condmkoption: + condexpr mkvarname PLUSEQ value { appendcondmkoption($1, $2, $4); } +; + +/* device name */ +devbase: + WORD { $$ = getdevbase($1); } +; + +/* optional attachment: with foo */ +devattach_opt: + /* empty */ { $$ = NULL; } + | WITH WORD { $$ = getdevattach($2); } +; + +/* list of major numbers */ +/* XXX why is this right-recursive? */ +majorlist: + majordef + | majorlist ',' majordef +; + +/* one major number */ +majordef: + devbase '=' int32 { setmajor($1, $3); } +; + +int32: + NUMBER { + if ($1.val > INT_MAX || $1.val < INT_MIN) + cfgerror("overflow %" PRId64, $1.val); + else + $$ = (int32_t)$1.val; + } +; + +/************************************************************/ + +/* + * The selection grammar. + */ + +/* Complete selection part: all std.* files plus selected config. */ +selection_part: + selections +; + +/* Zero or more config items. Trap errors. */ +selections: + /* empty */ + | selections '\n' + | selections selection '\n' { wrap_continue(); } + | selections error '\n' { wrap_cleanup(); } +; + +/* One config item. */ +selection: + definition + | select_attr + | select_no_attr + | select_no_filesystems + | select_filesystems + | select_no_makeoptions + | select_makeoptions + | select_no_options + | select_options + | select_maxusers + | select_ident + | select_no_ident + | select_config + | select_no_config + | select_no_pseudodev + | select_pseudodev + | select_pseudoroot + | select_no_device_instance_attachment + | select_no_device_attachment + | select_no_device_instance + | select_device_instance +; + +select_attr: + SELECT WORD { addattr($2); } +; + +select_no_attr: + NO SELECT WORD { delattr($3); } +; + +select_no_filesystems: + NO FILE_SYSTEM no_fs_list +; + +select_filesystems: + FILE_SYSTEM fs_list +; + +select_no_makeoptions: + NO MAKEOPTIONS no_mkopt_list +; + +select_makeoptions: + MAKEOPTIONS mkopt_list +; + +select_no_options: + NO OPTIONS no_opt_list +; + +select_options: + OPTIONS opt_list +; + +select_maxusers: + MAXUSERS int32 { setmaxusers($2); } +; + +select_ident: + IDENT stringvalue { setident($2); } +; + +select_no_ident: + NO IDENT { setident(NULL); } +; + +select_config: + CONFIG conf root_spec sysparam_list + { addconf(&conf); } +; + +select_no_config: + NO CONFIG WORD { delconf($3); } +; + +select_no_pseudodev: + NO PSEUDO_DEVICE WORD { delpseudo($3); } +; + +select_pseudodev: + PSEUDO_DEVICE WORD npseudo { addpseudo($2, $3); } +; + +select_pseudoroot: + PSEUDO_ROOT device_instance { addpseudoroot($2); } +; + +select_no_device_instance_attachment: + NO device_instance AT attachment + { deldevi($2, $4); } +; + +select_no_device_attachment: + NO DEVICE AT attachment { deldeva($4); } +; + +select_no_device_instance: + NO device_instance { deldev($2); } +; + +select_device_instance: + device_instance AT attachment locators device_flags + { adddev($1, $3, $4, $5); } +; + +/* list of filesystems */ +fs_list: + fsoption + | fs_list ',' fsoption +; + +/* one filesystem */ +fsoption: + WORD { addfsoption($1); } +; + +/* list of filesystems that had NO in front */ +no_fs_list: + no_fsoption + | no_fs_list ',' no_fsoption +; + +/* one filesystem that had NO in front */ +no_fsoption: + WORD { delfsoption($1); } +; + +/* list of make options */ +/* XXX why is this right-recursive? */ +mkopt_list: + mkoption + | mkopt_list ',' mkoption +; + +/* one make option */ +mkoption: + mkvarname '=' value { addmkoption($1, $3); } + | mkvarname PLUSEQ value { appendmkoption($1, $3); } +; + +/* list of make options that had NO in front */ +no_mkopt_list: + no_mkoption + | no_mkopt_list ',' no_mkoption +; + +/* one make option that had NO in front */ +/* XXX shouldn't this be mkvarname rather than WORD? */ +no_mkoption: + WORD { delmkoption($1); } +; + +/* list of options */ +opt_list: + option + | opt_list ',' option +; + +/* one option */ +option: + WORD { addoption($1, NULL); } + | WORD '=' value { addoption($1, $3); } +; + +/* list of options that had NO in front */ +no_opt_list: + no_option + | no_opt_list ',' no_option +; + +/* one option that had NO in front */ +no_option: + WORD { deloption($1); } +; + +/* the name in "config name root on ..." */ +conf: + WORD { + conf.cf_name = $1; + conf.cf_lineno = currentline(); + conf.cf_fstype = NULL; + conf.cf_root = NULL; + conf.cf_dump = NULL; + } +; + +/* root fs specification */ +root_spec: + ROOT on_opt dev_spec { setconf(&conf.cf_root, "root", $3); } + | ROOT on_opt dev_spec fs_spec { setconf(&conf.cf_root, "root", $3); } +; + +/* device for root fs or dump */ +dev_spec: + '?' { $$ = new_si(intern("?"), + (long long)NODEV); } + | WORD { $$ = new_si($1, + (long long)NODEV); } + | major_minor { $$ = new_si(NULL, $1); } +; + +/* major and minor device number */ +major_minor: + MAJOR NUMBER MINOR NUMBER { $$ = (int64_t)makedev($2.val, $4.val); } +; + +/* filesystem type for root fs specification */ +fs_spec: + TYPE '?' { setfstype(&conf.cf_fstype, intern("?")); } + | TYPE WORD { setfstype(&conf.cf_fstype, $2); } +; + +/* zero or more additional system parameters */ +sysparam_list: + /* empty */ + | sysparam_list sysparam +; + +/* one additional system parameter (there's only one: dumps) */ +sysparam: + DUMPS on_opt dev_spec { setconf(&conf.cf_dump, "dumps", $3); } +; + +/* number of pseudo devices to configure (which is optional) */ +npseudo: + /* empty */ { $$ = 1; } + | int32 { $$ = $1; } +; + +/* name of a device to configure */ +device_instance: + WORD { $$ = $1; } + | WORD '*' { $$ = starref($1); } +; + +/* name of a device to configure an attachment to */ +attachment: + ROOT { $$ = NULL; } + | WORD { $$ = $1; } + | WORD '?' { $$ = wildref($1); } +; + +/* zero or more locators */ +locators: + /* empty */ { $$ = NULL; } + | locators locator { $$ = $2; app($2, $1); } +; + +/* one locator */ +locator: + WORD '?' { $$ = MK3(loc, $1, NULL, 0); } + | WORD values { $$ = namelocvals($1, $2); } +; + +/* optional device flags */ +device_flags: + /* empty */ { $$ = 0; } + | FLAGS int32 { $$ = $2; } +; + +/************************************************************/ + +/* + * conditions + */ + + +/* + * order of options is important, must use right recursion + * + * dholland 20120310: wut? + */ + +/* expression of conditions */ +condexpr: + cond_or_expr +; + +cond_or_expr: + cond_and_expr + | cond_or_expr '|' cond_and_expr { $$ = MKF2(cx, or, $1, $3); } +; + +cond_and_expr: + cond_prefix_expr + | cond_and_expr '&' cond_prefix_expr { $$ = MKF2(cx, and, $1, $3); } +; + +cond_prefix_expr: + cond_base_expr +/* XXX notyet - need to strengthen downstream first */ +/* | '!' cond_prefix_expr { $$ = MKF1(cx, not, $2); } */ +; + +cond_base_expr: + condatom { $$ = $1; } + | '!' condatom { $$ = MKF1(cx, not, $2); } + | '(' condexpr ')' { $$ = $2; } +; + +/* basic element of config element expression: a config element */ +condatom: + WORD { $$ = MKF1(cx, atom, $1); } +; + +/************************************************************/ + +/* + * Various nonterminals shared between the grammars. + */ + +/* variable name for make option */ +mkvarname: + QSTRING { $$ = $1; } + | WORD { $$ = $1; } +; + +/* optional file for an option */ +optfile_opt: + /* empty */ { $$ = NULL; } + | filename { $$ = $1; } +; + +/* filename. */ +filename: + QSTRING { $$ = $1; } + | PATHNAME { $$ = $1; } +; + +/* constant value */ +value: + QSTRING { $$ = $1; } + | WORD { $$ = $1; } + | EMPTYSTRING { $$ = $1; } + | signed_number { + char bf[40]; + + (void)snprintf(bf, sizeof(bf), FORMAT($1), (long long)$1.val); + $$ = intern(bf); + } +; + +/* constant value that is a string */ +stringvalue: + QSTRING { $$ = $1; } + | WORD { $$ = $1; } +; + +/* comma-separated list of values */ +/* XXX why right-recursive? */ +values: + value { $$ = MKF2(loc, val, $1, NULL); } + | value ',' values { $$ = MKF2(loc, val, $1, $3); } +; + +/* possibly negative number */ +signed_number: + NUMBER { $$ = $1; } + | '-' NUMBER { $$.fmt = $2.fmt; $$.val = -$2.val; } +; + +/* optional ON keyword */ +on_opt: + /* empty */ + | ON +; + +%% + +void +yyerror(const char *s) +{ + + cfgerror("%s", s); +} + +/************************************************************/ + +/* + * Wrap allocations that live on the parser stack so that we can free + * them again on error instead of leaking. + */ + +#define MAX_WRAP 1000 + +struct wrap_entry { + void *ptr; + unsigned typecode; +}; + +static struct wrap_entry wrapstack[MAX_WRAP]; +static unsigned wrap_depth; + +/* + * Remember pointer PTR with type-code CODE. + */ +static void +wrap_alloc(void *ptr, unsigned code) +{ + unsigned pos; + + if (wrap_depth >= MAX_WRAP) { + panic("allocation wrapper stack overflow"); + } + pos = wrap_depth++; + wrapstack[pos].ptr = ptr; + wrapstack[pos].typecode = code; +} + +/* + * We succeeded; commit to keeping everything that's been allocated so + * far and clear the stack. + */ +static void +wrap_continue(void) +{ + wrap_depth = 0; +} + +/* + * We failed; destroy all the objects allocated. + */ +static void +wrap_cleanup(void) +{ + unsigned i; + + /* + * Destroy each item. Note that because everything allocated + * is entered on the list separately, lists and trees need to + * have their links blanked before being destroyed. Also note + * that strings are interned elsewhere and not handled by this + * mechanism. + */ + + for (i=0; i<wrap_depth; i++) { + switch (wrapstack[i].typecode) { + case WRAP_CODE_nvlist: + nvfree(wrapstack[i].ptr); + break; + case WRAP_CODE_defoptlist: + { + struct defoptlist *dl = wrapstack[i].ptr; + + dl->dl_next = NULL; + defoptlist_destroy(dl); + } + break; + case WRAP_CODE_loclist: + { + struct loclist *ll = wrapstack[i].ptr; + + ll->ll_next = NULL; + loclist_destroy(ll); + } + break; + case WRAP_CODE_attrlist: + { + struct attrlist *al = wrapstack[i].ptr; + + al->al_next = NULL; + al->al_this = NULL; + attrlist_destroy(al); + } + break; + case WRAP_CODE_condexpr: + { + struct condexpr *cx = wrapstack[i].ptr; + + cx->cx_type = CX_ATOM; + cx->cx_atom = NULL; + condexpr_destroy(cx); + } + break; + default: + panic("invalid code %u on allocation wrapper stack", + wrapstack[i].typecode); + } + } + + wrap_depth = 0; +} + +/* + * Instantiate the wrapper functions. + * + * Each one calls wrap_alloc to save the pointer and then returns the + * pointer again; these need to be generated with the preprocessor in + * order to be typesafe. + */ +#define DEF_ALLOCWRAP(t) \ + static struct t * \ + wrap_mk_##t(struct t *arg) \ + { \ + wrap_alloc(arg, WRAP_CODE_##t); \ + return arg; \ + } + +DEF_ALLOCWRAP(nvlist); +DEF_ALLOCWRAP(defoptlist); +DEF_ALLOCWRAP(loclist); +DEF_ALLOCWRAP(attrlist); +DEF_ALLOCWRAP(condexpr); + +/************************************************************/ + +/* + * Data constructors + * + * (These are *beneath* the allocation wrappers.) + */ + +static struct defoptlist * +mk_defoptlist(const char *name, const char *val, const char *lintval) +{ + return defoptlist_create(name, val, lintval); +} + +static struct loclist * +mk_loc(const char *name, const char *str, long long num) +{ + return loclist_create(name, str, num); +} + +static struct loclist * +mk_loc_val(const char *str, struct loclist *next) +{ + struct loclist *ll; + + ll = mk_loc(NULL, str, 0); + ll->ll_next = next; + return ll; +} + +static struct attrlist * +mk_attrlist(struct attrlist *next, struct attr *a) +{ + return attrlist_cons(next, a); +} + +static struct condexpr * +mk_cx_atom(const char *s) +{ + struct condexpr *cx; + + cx = condexpr_create(CX_ATOM); + cx->cx_atom = s; + return cx; +} + +static struct condexpr * +mk_cx_not(struct condexpr *sub) +{ + struct condexpr *cx; + + cx = condexpr_create(CX_NOT); + cx->cx_not = sub; + return cx; +} + +static struct condexpr * +mk_cx_and(struct condexpr *left, struct condexpr *right) +{ + struct condexpr *cx; + + cx = condexpr_create(CX_AND); + cx->cx_and.left = left; + cx->cx_and.right = right; + return cx; +} + +static struct condexpr * +mk_cx_or(struct condexpr *left, struct condexpr *right) +{ + struct condexpr *cx; + + cx = condexpr_create(CX_OR); + cx->cx_or.left = left; + cx->cx_or.right = right; + return cx; +} + +/************************************************************/ + +static void +setmachine(const char *mch, const char *mcharch, struct nvlist *mchsubarches, + int isioconf) +{ + char buf[MAXPATHLEN]; + struct nvlist *nv; + + if (isioconf) { + if (include(_PATH_DEVNULL, ENDDEFS, 0, 0) != 0) + exit(1); + ioconfname = mch; + return; + } + + machine = mch; + machinearch = mcharch; + machinesubarches = mchsubarches; + + /* + * Define attributes for all the given names + */ + if (defattr(machine, NULL, NULL, 0) != 0 || + (machinearch != NULL && + defattr(machinearch, NULL, NULL, 0) != 0)) + exit(1); + for (nv = machinesubarches; nv != NULL; nv = nv->nv_next) { + if (defattr(nv->nv_name, NULL, NULL, 0) != 0) + exit(1); + } + + /* + * Set up the file inclusion stack. This empty include tells + * the parser there are no more device definitions coming. + */ + if (include(_PATH_DEVNULL, ENDDEFS, 0, 0) != 0) + exit(1); + + /* Include arch/${MACHINE}/conf/files.${MACHINE} */ + (void)snprintf(buf, sizeof(buf), "arch/%s/conf/files.%s", + machine, machine); + if (include(buf, ENDFILE, 0, 0) != 0) + exit(1); + + /* Include any arch/${MACHINE_SUBARCH}/conf/files.${MACHINE_SUBARCH} */ + for (nv = machinesubarches; nv != NULL; nv = nv->nv_next) { + (void)snprintf(buf, sizeof(buf), "arch/%s/conf/files.%s", + nv->nv_name, nv->nv_name); + if (include(buf, ENDFILE, 0, 0) != 0) + exit(1); + } + + /* Include any arch/${MACHINE_ARCH}/conf/files.${MACHINE_ARCH} */ + if (machinearch != NULL) + (void)snprintf(buf, sizeof(buf), "arch/%s/conf/files.%s", + machinearch, machinearch); + else + strlcpy(buf, _PATH_DEVNULL, sizeof(buf)); + if (include(buf, ENDFILE, 0, 0) != 0) + exit(1); + + /* + * Include the global conf/files. As the last thing + * pushed on the stack, it will be processed first. + */ + if (include("conf/files", ENDFILE, 0, 0) != 0) + exit(1); + + oktopackage = 1; +} + +static void +check_maxpart(void) +{ + + if (maxpartitions <= 0 && ioconfname == NULL) { + stop("cannot proceed without maxpartitions specifier"); + } +} + +static void +check_version(void) +{ + /* + * In essence, version is 0 and is not supported anymore + */ + if (version < CONFIG_MINVERSION) + stop("your sources are out of date -- please update."); +} + +/* + * Prepend a blank entry to the locator definitions so the code in + * sem.c can distinguish "empty locator list" from "no locator list". + * XXX gross. + */ +static struct loclist * +present_loclist(struct loclist *ll) +{ + struct loclist *ret; + + ret = MK3(loc, "", NULL, 0); + ret->ll_next = ll; + return ret; +} + +static void +app(struct loclist *p, struct loclist *q) +{ + while (p->ll_next) + p = p->ll_next; + p->ll_next = q; +} + +static struct loclist * +locarray(const char *name, int count, struct loclist *adefs, int opt) +{ + struct loclist *defs = adefs; + struct loclist **p; + char buf[200]; + int i; + + if (count <= 0) { + fprintf(stderr, "config: array with <= 0 size: %s\n", name); + exit(1); + } + p = &defs; + for(i = 0; i < count; i++) { + if (*p == NULL) + *p = MK3(loc, NULL, "0", 0); + snprintf(buf, sizeof(buf), "%s%c%d", name, ARRCHR, i); + (*p)->ll_name = i == 0 ? name : intern(buf); + (*p)->ll_num = i > 0 || opt; + p = &(*p)->ll_next; + } + *p = 0; + return defs; +} + + +static struct loclist * +namelocvals(const char *name, struct loclist *vals) +{ + struct loclist *p; + char buf[200]; + int i; + + for (i = 0, p = vals; p; i++, p = p->ll_next) { + snprintf(buf, sizeof(buf), "%s%c%d", name, ARRCHR, i); + p->ll_name = i == 0 ? name : intern(buf); + } + return vals; +} + diff --git a/buildrump.sh/src/usr.bin/config/hash.c b/buildrump.sh/src/usr.bin/config/hash.c new file mode 100644 index 00000000..77382a2f --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/hash.c @@ -0,0 +1,456 @@ +/* $NetBSD: hash.c,v 1.11 2014/10/29 17:14:50 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)hash.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: hash.c,v 1.11 2014/10/29 17:14:50 christos Exp $"); + +#include <sys/param.h> +#include <assert.h> +#include <stdlib.h> +#include <string.h> +#include <util.h> +#include "defs.h" + +/* + * Interned strings are kept in a hash table. By making each string + * unique, the program can compare strings by comparing pointers. + */ +struct hashent { + // XXXLUKEM: a SIMPLEQ might be more appropriate + TAILQ_ENTRY(hashent) h_next; + const char *h_names[2]; /* the string */ +#define h_name1 h_names[0] +#define h_name2 h_names[1] +#define h_name h_name1 + u_int h_hash; /* its hash value */ + void *h_value; /* other values (for name=value) */ +}; +struct hashtab { + size_t ht_size; /* size (power of 2) */ + size_t ht_mask; /* == ht_size - 1 */ + size_t ht_used; /* number of entries used */ + size_t ht_lim; /* when to expand */ + TAILQ_HEAD(hashenthead, hashent) *ht_tab; +}; + +static struct hashtab strings; + +/* + * HASHFRACTION controls ht_lim, which in turn controls the average chain + * length. We allow a few entries, on average, as comparing them is usually + * cheap (the h_hash values prevent a strcmp). + */ +#define HASHFRACTION(sz) ((sz) * 3 / 2) + +static void ht_expand(struct hashtab *); +static void ht_init(struct hashtab *, size_t); +static inline u_int hash(u_int, const char *); +static inline u_int hash2(u_int, const char *, const char *); +static inline struct hashent *newhashent(const char *, u_int); + +/* + * Initialize a new hash table. The size must be a power of 2. + */ +static void +ht_init(struct hashtab *ht, size_t sz) +{ + u_int n; + + ht->ht_tab = emalloc(sz * sizeof (ht->ht_tab[0])); + ht->ht_size = sz; + ht->ht_mask = sz - 1; + for (n = 0; n < sz; n++) + TAILQ_INIT(&ht->ht_tab[n]); + ht->ht_used = 0; + ht->ht_lim = HASHFRACTION(sz); +} + +/* + * Expand an existing hash table. + */ +static void +ht_expand(struct hashtab *ht) +{ + struct hashenthead *h, *oldh; + struct hashent *p; + size_t n, i; + + n = ht->ht_size * 2; + h = emalloc(n * sizeof *h); + for (i = 0; i < n; i++) + TAILQ_INIT(&h[i]); + oldh = ht->ht_tab; + n--; + for (i = 0; i < ht->ht_size; i++) { + while ((p = TAILQ_FIRST(&oldh[i])) != NULL) { + TAILQ_REMOVE(&oldh[i], p, h_next); + // XXXLUKEM: really should be TAILQ_INSERT_TAIL + TAILQ_INSERT_HEAD(&h[p->h_hash & n], p, h_next); + } + } + free(ht->ht_tab); + ht->ht_tab = h; + ht->ht_mask = n; + ht->ht_size = ++n; + ht->ht_lim = HASHFRACTION(n); +} + +/* + * Make a new hash entry, setting its h_next to NULL. + * If the free list is not empty, use the first entry from there, + * otherwise allocate a new entry. + */ +static inline struct hashent * +newhashent2(const char *name1, const char *name2, u_int h) +{ + struct hashent *hp; + + hp = ecalloc(1, sizeof(*hp)); + + hp->h_name1 = name1; + hp->h_name2 = name2; + hp->h_hash = h; + return (hp); +} + +static inline struct hashent * +newhashent(const char *name, u_int h) +{ + return newhashent2(name, NULL, h); +} + +static inline u_int +hv(u_int h, char c) +{ + return (h << 5) + h + (unsigned char)c; +} + +/* + * Hash a string. + */ +static inline u_int +hash(u_int h, const char *str) +{ + + while (str && *str) + h = hv(h, *str++); + return (h); +} + +#define HASH2DELIM ' ' + +static inline u_int +hash2(u_int h, const char *str1, const char *str2) +{ + + h = hash(h, str1); + h = hv(h, HASH2DELIM); + h = hash(h, str2); + return (h); +} + +void +initintern(void) +{ + + ht_init(&strings, 128); +} + +/* + * Generate a single unique copy of the given string. We expect this + * function to be used frequently, so it should be fast. + */ +const char * +intern(const char *s) +{ + struct hashtab *ht; + struct hashent *hp; + struct hashenthead *hpp; + u_int h; + char *p; + + ht = &strings; + h = hash2(0, s, NULL); + hpp = &ht->ht_tab[h & ht->ht_mask]; + TAILQ_FOREACH(hp, hpp, h_next) { + if (hp->h_hash == h && strcmp(hp->h_name, s) == 0) + return (hp->h_name); + } + p = estrdup(s); + hp = newhashent(p, h); + TAILQ_INSERT_TAIL(hpp, hp, h_next); + if (++ht->ht_used > ht->ht_lim) + ht_expand(ht); + return (p); +} + +struct hashtab * +ht_new(void) +{ + struct hashtab *ht; + + ht = ecalloc(1, sizeof *ht); + ht_init(ht, 8); + return (ht); +} + +void +ht_free(struct hashtab *ht) +{ + size_t i; + struct hashent *hp; + struct hashenthead *hpp; + + for (i = 0; i < ht->ht_size; i++) { + hpp = &ht->ht_tab[i]; + while ((hp = TAILQ_FIRST(hpp)) != NULL) { + TAILQ_REMOVE(hpp, hp, h_next); + free(hp); + ht->ht_used--; + } + } + + assert(ht->ht_used == 0); + free(ht->ht_tab); + free(ht); +} + +/* + * Insert and/or replace. + */ +int +ht_insrep2(struct hashtab *ht, const char *nam1, const char *nam2, void *val, int replace) +{ + struct hashent *hp; + struct hashenthead *hpp; + u_int h; + + h = hash2(0, nam1, nam2); + hpp = &ht->ht_tab[h & ht->ht_mask]; + TAILQ_FOREACH(hp, hpp, h_next) { + if (hp->h_name1 == nam1 && + hp->h_name2 == nam2) { + if (replace) + hp->h_value = val; + return (1); + } + } + hp = newhashent2(nam1, nam2, h); + TAILQ_INSERT_TAIL(hpp, hp, h_next); + hp->h_value = val; + if (++ht->ht_used > ht->ht_lim) + ht_expand(ht); + return (0); +} + +int +ht_insrep(struct hashtab *ht, const char *nam, void *val, int replace) +{ + return ht_insrep2(ht, nam, NULL, val, replace); +} + +/* + * Remove. + */ +int +ht_remove2(struct hashtab *ht, const char *name1, const char *name2) +{ + struct hashent *hp; + struct hashenthead *hpp; + u_int h; + + h = hash2(0, name1, name2); + hpp = &ht->ht_tab[h & ht->ht_mask]; + + TAILQ_FOREACH(hp, hpp, h_next) { + if (hp->h_name1 != name1 || hp->h_name2 != name2) + continue; + TAILQ_REMOVE(hpp, hp, h_next); + + free(hp); + ht->ht_used--; + return (0); + } + return (1); +} + +int +ht_remove(struct hashtab *ht, const char *name) +{ + return ht_remove2(ht, name, NULL); +} + +void * +ht_lookup2(struct hashtab *ht, const char *nam1, const char *nam2) +{ + struct hashent *hp; + struct hashenthead *hpp; + u_int h; + + h = hash2(0, nam1, nam2); + hpp = &ht->ht_tab[h & ht->ht_mask]; + TAILQ_FOREACH(hp, hpp, h_next) + if (hp->h_name == nam1) + return (hp->h_value); + return (NULL); +} + +void * +ht_lookup(struct hashtab *ht, const char *nam) +{ + return ht_lookup2(ht, nam, NULL); +} + +/* + * first parameter to callback is the entry name from the hash table + * second parameter is the value from the hash table + * third argument is passed through from the "arg" parameter to ht_enumerate() + */ + +int +ht_enumerate2(struct hashtab *ht, ht_callback2 cbfunc2, void *arg) +{ + struct hashent *hp; + struct hashenthead *hpp; + size_t i; + int rval = 0; + + for (i = 0; i < ht->ht_size; i++) { + hpp = &ht->ht_tab[i]; + TAILQ_FOREACH(hp, hpp, h_next) + rval += (*cbfunc2)(hp->h_name1, hp->h_name2, hp->h_value, arg); + } + return rval; +} + +int +ht_enumerate(struct hashtab *ht, ht_callback cbfunc, void *arg) +{ + struct hashent *hp; + struct hashenthead *hpp; + size_t i; + int rval = 0; + + for (i = 0; i < ht->ht_size; i++) { + hpp = &ht->ht_tab[i]; + TAILQ_FOREACH(hp, hpp, h_next) + rval += (*cbfunc)(hp->h_name, hp->h_value, arg); + } + return rval; +} + +/************************************************************/ + +/* + * Type-safe wrappers. + */ + +#define DEFHASH(HT, VT) \ + struct HT { \ + struct hashtab imp; \ + }; \ + \ + struct HT * \ + HT##_create(void) \ + { \ + struct HT *tbl; \ + \ + tbl = ecalloc(1, sizeof(*tbl)); \ + ht_init(&tbl->imp, 8); \ + return tbl; \ + } \ + \ + int \ + HT##_insert(struct HT *tbl, const char *name, struct VT *val) \ + { \ + return ht_insert(&tbl->imp, name, val); \ + } \ + \ + int \ + HT##_replace(struct HT *tbl, const char *name, struct VT *val) \ + { \ + return ht_replace(&tbl->imp, name, val); \ + } \ + \ + int \ + HT##_remove(struct HT *tbl, const char *name) \ + { \ + return ht_remove(&tbl->imp, name); \ + } \ + \ + struct VT * \ + HT##_lookup(struct HT *tbl, const char *name) \ + { \ + return ht_lookup(&tbl->imp, name); \ + } \ + \ + struct HT##_enumcontext { \ + int (*func)(const char *, struct VT *, void *); \ + void *userctx; \ + }; \ + \ + static int \ + HT##_enumerate_thunk(const char *name, void *value, void *voidctx) \ + { \ + struct HT##_enumcontext *ctx = voidctx; \ + \ + return ctx->func(name, value, ctx->userctx); \ + } \ + \ + int \ + HT##_enumerate(struct HT *tbl, \ + int (*func)(const char *, struct VT *, void *), \ + void *userctx) \ + { \ + struct HT##_enumcontext ctx; \ + \ + ctx.func = func; \ + ctx.userctx = userctx; \ + return ht_enumerate(&tbl->imp, HT##_enumerate_thunk, &ctx); \ + } + +DEFHASH(nvhash, nvlist); +DEFHASH(dlhash, defoptlist); diff --git a/buildrump.sh/src/usr.bin/config/lint.c b/buildrump.sh/src/usr.bin/config/lint.c new file mode 100644 index 00000000..0066acd8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/lint.c @@ -0,0 +1,208 @@ +/* $NetBSD: lint.c,v 1.15 2014/10/29 17:14:50 christos Exp $ */ + +/* + * Copyright (c) 2007 The NetBSD Foundation. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: lint.c,v 1.15 2014/10/29 17:14:50 christos Exp $"); + +#include <assert.h> +#include <stdlib.h> + +#include "defs.h" + +void +emit_params(void) +{ + + printf("version\t%d\n", CONFIG_VERSION); + printf("ident\t\"LINT_%s\"\n", conffile); + printf("maxusers\t%d\n", defmaxusers); + printf("config netbsdlint root on ?\n"); + printf("\n"); +} + +enum opt_types { + OT_FLAG, + OT_PARAM, + OT_FS +}; + +static struct opt_type { + enum opt_types ot_type; + const char *ot_name; + struct hashtab **ot_ht; +} opt_types[] = { + { OT_FLAG, "options", &opttab }, + { OT_PARAM, "options", &opttab }, + { OT_FS, "file-system", &fsopttab }, +}; + +static int +do_emit_option(const char *name, struct defoptlist *dl, void *v) +{ + const struct opt_type *ot = v; + const char *value; + + if (dl->dl_obsolete) + return 0; + + if (ht_lookup(*(ot->ot_ht), name)) + return 0; + + printf("%s\t%s", ot->ot_name, dl->dl_name); + if (ot->ot_type == OT_PARAM) { + struct defoptlist *dl2 = dlhash_lookup(defoptlint, dl->dl_name); + if (dl2 != NULL) + value = dl2->dl_lintvalue; + else + value = dl->dl_value; + assert(dl2 == dl); + printf("=\"%s\"", value ? value : "1"); + } + printf("\n"); + + return 1; +} + +/* + * Same as do_emit_option but for filesystem definitions, which now + * have a different data type. XXX these should probably be unified + * again. + */ +static int +do_emit_fs(const char *name, struct nvlist *nv, void *v) +{ + const struct opt_type *ot = v; + + if (ht_lookup(*(ot->ot_ht), name)) + return 0; + + assert(ot->ot_type != OT_PARAM); + printf("%s\t%s\n", ot->ot_name, nv->nv_name); + + return 1; +} + + +void +emit_options(void) +{ + + (void)dlhash_enumerate(defflagtab, do_emit_option, &opt_types[0]); + printf("\n"); + (void)dlhash_enumerate(defparamtab, do_emit_option, &opt_types[1]); + printf("\n"); + (void)nvhash_enumerate(deffstab, do_emit_fs, &opt_types[2]); + printf("\n"); +} + +static void +do_emit_instances(struct devbase *d, struct attr *at) +{ + struct nvlist *nv1; + struct loclist *ll; + struct attrlist *al; + struct attr *a; + struct deva *da; + + /* + * d_isdef is used to check whether a deva has been seen or not, + * for there are devices that can be their own ancestor (e.g. + * uhub, pci). + */ + + if (at != NULL) { + for (da = d->d_ahead; da != NULL; da = da->d_bsame) + if (onlist(da->d_atlist, at)) + break; + if (da == NULL) + panic("do_emit_instances: no deva found for %s at %s", + d->d_name, at->a_name); + + if (da->d_isdef > 1) + return; + da->d_isdef = 2; + } + + if (at == NULL && !d->d_ispseudo && d->d_ihead == NULL) + printf("%s0\tat\troot\n", d->d_name); + else if (at != NULL && !d->d_ispseudo && da->d_ihead == NULL) { + printf("%s0\tat\t%s?", d->d_name, at->a_name); + + for (ll = at->a_locs; ll != NULL; ll = ll->ll_next) { + if (ll->ll_num == 0) + printf(" %s %c", ll->ll_name, + ll->ll_string ? '?' : '0'); + } + + printf("\n"); + } + + /* + * Children attachments are found the same way as in the orphan + * detection code in main.c. + */ + for (al = d->d_attrs; al != NULL; al = al->al_next) { + a = al->al_this; + for (nv1 = a->a_devs; nv1 != NULL; nv1 = nv1->nv_next) + do_emit_instances(nv1->nv_ptr, a); + } +} + +/* ARGSUSED */ +static int +emit_root_instance(const char *name, void *value, void *v) +{ + + do_emit_instances((struct devbase *)value, NULL); + + return 1; +} + +/* ARGSUSED */ +static int +emit_pseudo_instance(const char *name, void *value, void *v) +{ + struct devbase *d = value; + + if (d->d_ispseudo && d->d_ihead == NULL) + printf("pseudo-device\t%s\n", d->d_name); + return 0; +} + +void +emit_instances(void) +{ + + (void)ht_enumerate(devroottab, emit_root_instance, NULL); + printf("\n"); + (void)ht_enumerate(devbasetab, emit_pseudo_instance, NULL); +} diff --git a/buildrump.sh/src/usr.bin/config/main.c b/buildrump.sh/src/usr.bin/config/main.c new file mode 100644 index 00000000..909aa3ed --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/main.c @@ -0,0 +1,1902 @@ +/* $NetBSD: main.c,v 1.74 2015/01/22 20:01:22 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)main.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: main.c,v 1.74 2015/01/22 20:01:22 christos Exp $"); + +#ifndef MAKE_BOOTSTRAP +#include <sys/cdefs.h> +#define COPYRIGHT(x) __COPYRIGHT(x) +#else +#define COPYRIGHT(x) static const char copyright[] = x +#endif + +#ifndef lint +COPYRIGHT("@(#) Copyright (c) 1992, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <sys/mman.h> +#if !HAVE_NBTOOL_CONFIG_H +#include <sys/sysctl.h> +#endif +#include <paths.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <vis.h> +#include <util.h> + +#include "defs.h" +#include "sem.h" + +#ifndef LINE_MAX +#define LINE_MAX 1024 +#endif + +int vflag; /* verbose output */ +int Pflag; /* pack locators */ +int Lflag; /* lint config generation */ +int Mflag; /* modular build */ +int handling_cmdlineopts; /* currently processing -D/-U options */ + +int yyparse(void); + +#ifndef MAKE_BOOTSTRAP +extern int yydebug; +#endif +int dflag; + +static struct dlhash *obsopttab; +static struct hashtab *mkopttab; +static struct nvlist **nextopt; +static struct nvlist **nextmkopt; +static struct nvlist **nextappmkopt; +static struct nvlist **nextcndmkopt; +static struct nvlist **nextfsopt; +static struct nvlist *cmdlinedefs, *cmdlineundefs; + +static void usage(void) __dead; +static void dependopts(void); +static void dependopts_one(const char *); +static void do_depends(struct nvlist *); +static void do_depend(struct nvlist *); +static void stop(void); +static int do_option(struct hashtab *, struct nvlist ***, + const char *, const char *, const char *); +static int undo_option(struct hashtab *, struct nvlist **, + struct nvlist ***, const char *, const char *); +static int crosscheck(void); +static int badstar(void); + int main(int, char **); +static int mksymlinks(void); +static int mkident(void); +static int devbase_has_dead_instances(const char *, void *, void *); +static int devbase_has_any_instance(struct devbase *, int, int, int); +static int check_dead_devi(const char *, void *, void *); +static void add_makeopt(const char *); +static void remove_makeopt(const char *); +static void handle_cmdline_makeoptions(void); +static void kill_orphans(void); +static void do_kill_orphans(struct devbase *, struct attr *, + struct devbase *, int); +static int kill_orphans_cb(const char *, void *, void *); +static int cfcrosscheck(struct config *, const char *, struct nvlist *); +static void defopt(struct dlhash *ht, const char *fname, + struct defoptlist *opts, struct nvlist *deps, int obs); +static struct defoptlist *find_declared_option_option(const char *name); +static struct nvlist *find_declared_fs_option(const char *name); + +#define LOGCONFIG_LARGE "INCLUDE_CONFIG_FILE" +#define LOGCONFIG_SMALL "INCLUDE_JUST_CONFIG" + +static void logconfig_start(void); +static void logconfig_end(void); +static FILE *cfg; +static time_t cfgtime; + +static int is_elf(const char *); +static int extract_config(const char *, const char *, int); + +int badfilename(const char *fname); + +const char *progname; +extern const char *yyfile; + +int +main(int argc, char **argv) +{ + char *p, cname[PATH_MAX]; + const char *last_component; + int pflag, xflag, ch, removeit; + + setprogname(argv[0]); + + pflag = 0; + xflag = 0; + while ((ch = getopt(argc, argv, "D:LMPU:dgpvb:s:x")) != -1) { + switch (ch) { + + case 'd': +#ifndef MAKE_BOOTSTRAP + yydebug = 1; +#endif + dflag++; + break; + + case 'M': + Mflag = 1; + break; + + case 'L': + Lflag = 1; + break; + + case 'P': + Pflag = 1; + break; + + case 'g': + /* + * In addition to DEBUG, you probably wanted to + * set "options KGDB" and maybe others. We could + * do that for you, but you really should just + * put them in the config file. + */ + warnx("-g is obsolete (use -D DEBUG=\"-g\")"); + usage(); + /*NOTREACHED*/ + + case 'p': + /* + * Essentially the same as makeoptions PROF="-pg", + * but also changes the path from ../../compile/FOO + * to ../../compile/FOO.PROF; i.e., compile a + * profiling kernel based on a typical "regular" + * kernel. + * + * Note that if you always want profiling, you + * can (and should) use a "makeoptions" line. + */ + pflag = 1; + break; + + case 'v': + vflag = 1; + break; + + case 'b': + builddir = optarg; + break; + + case 's': + srcdir = optarg; + break; + + case 'x': + xflag = 1; + break; + + case 'D': + add_makeopt(optarg); + break; + + case 'U': + remove_makeopt(optarg); + break; + + case '?': + default: + usage(); + } + } + + if (xflag && optind != 2) { + errx(EXIT_FAILURE, "-x must be used alone"); + } + + argc -= optind; + argv += optind; + if (argc > 1) { + usage(); + } + + if (Lflag && (builddir != NULL || Pflag || pflag)) + errx(EXIT_FAILURE, "-L can only be used with -s and -v"); + + if (xflag) { + if (argc == 0) { +#if !HAVE_NBTOOL_CONFIG_H + char path_unix[MAXPATHLEN]; + size_t len = sizeof(path_unix) - 1; + path_unix[0] = '/'; + + conffile = sysctlbyname("machdep.booted_kernel", + &path_unix[1], &len, NULL, 0) == -1 ? _PATH_UNIX : + path_unix; +#else + errx(EXIT_FAILURE, "no kernel supplied"); +#endif + } else + conffile = argv[0]; + if (!is_elf(conffile)) + errx(EXIT_FAILURE, "%s: not a binary kernel", + conffile); + if (!extract_config(conffile, "stdout", STDOUT_FILENO)) + errx(EXIT_FAILURE, "%s does not contain embedded " + "configuration data", conffile); + exit(0); + } + + conffile = (argc == 1) ? argv[0] : "CONFIG"; + if (firstfile(conffile)) { + err(EXIT_FAILURE, "Cannot read `%s'", conffile); + exit(2); + } + + /* + * Init variables. + */ + minmaxusers = 1; + maxmaxusers = 10000; + initintern(); + ident = NULL; + devbasetab = ht_new(); + devroottab = ht_new(); + devatab = ht_new(); + devitab = ht_new(); + deaddevitab = ht_new(); + selecttab = ht_new(); + needcnttab = ht_new(); + opttab = ht_new(); + mkopttab = ht_new(); + fsopttab = ht_new(); + deffstab = nvhash_create(); + defopttab = dlhash_create(); + defparamtab = dlhash_create(); + defoptlint = dlhash_create(); + defflagtab = dlhash_create(); + optfiletab = dlhash_create(); + obsopttab = dlhash_create(); + bdevmtab = ht_new(); + maxbdevm = 0; + cdevmtab = ht_new(); + maxcdevm = 0; + nextopt = &options; + nextmkopt = &mkoptions; + nextappmkopt = &appmkoptions; + nextcndmkopt = &condmkoptions; + nextfsopt = &fsoptions; + initfiles(); + initsem(); + + /* + * Handle profiling (must do this before we try to create any + * files). + */ + last_component = strrchr(conffile, '/'); + last_component = (last_component) ? last_component + 1 : conffile; + if (pflag) { + p = emalloc(strlen(last_component) + 17); + (void)sprintf(p, "../compile/%s.PROF", last_component); + (void)addmkoption(intern("PROF"), "-pg"); + (void)addoption(intern("GPROF"), NULL); + } else { + p = emalloc(strlen(last_component) + 13); + (void)sprintf(p, "../compile/%s", last_component); + } + defbuilddir = (argc == 0) ? "." : p; + + if (Lflag) { + char resolvedname[MAXPATHLEN]; + + if (realpath(conffile, resolvedname) == NULL) + err(EXIT_FAILURE, "realpath(%s)", conffile); + + if (yyparse()) + stop(); + + printf("include \"%s\"\n", resolvedname); + + emit_params(); + emit_options(); + emit_instances(); + + exit(EXIT_SUCCESS); + } + + removeit = 0; + if (is_elf(conffile)) { + const char *tmpdir; + int cfd; + + if (builddir == NULL) + errx(EXIT_FAILURE, "Build directory must be specified " + "with binary kernels"); + + /* Open temporary configuration file */ + tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + tmpdir = _PATH_TMP; + snprintf(cname, sizeof(cname), "%s/config.tmp.XXXXXX", tmpdir); + cfd = mkstemp(cname); + if (cfd == -1) + err(EXIT_FAILURE, "Cannot create `%s'", cname); + + printf("Using configuration data embedded in kernel...\n"); + if (!extract_config(conffile, cname, cfd)) { + unlink(cname); + errx(EXIT_FAILURE, "%s does not contain embedded " + "configuration data", conffile); + } + + removeit = 1; + close(cfd); + firstfile(cname); + } + + /* + * Log config file. We don't know until yyparse() if we're + * going to need config_file.h (i.e. if we're doing ioconf-only + * or not). Just start creating the file, and when we know + * later, we'll just keep or discard our work here. + */ + logconfig_start(); + + /* + * Parse config file (including machine definitions). + */ + if (yyparse()) + stop(); + + if (ioconfname && cfg) + fclose(cfg); + else + logconfig_end(); + + if (removeit) + unlink(cname); + + /* + * Handle command line overrides + */ + yyfile = "handle_cmdline_makeoptions"; + handle_cmdline_makeoptions(); + + /* + * Detect and properly ignore orphaned devices + */ + yyfile = "kill_orphans"; + kill_orphans(); + + /* + * Select devices and pseudo devices and their attributes + */ + yyfile = "fixdevis"; + if (fixdevis()) + stop(); + + /* + * If working on an ioconf-only config, process here and exit + */ + if (ioconfname) { + yyfile = "pack"; + pack(); + yyfile = "mkioconf"; + mkioconf(); + yyfile = "emitlocs"; + emitlocs(); + yyfile = "emitioconfh"; + emitioconfh(); + return 0; + } + + yyfile = "dependattrs"; + dependattrs(); + + /* + * Deal with option dependencies. + */ + yyfile = "dependopts"; + dependopts(); + + /* + * Fix (as in `set firmly in place') files. + */ + yyfile = "fixfiles"; + if (fixfiles()) + stop(); + + /* + * Fix objects and libraries. + */ + yyfile = "fixobjects"; + if (fixobjects()) + stop(); + + /* + * Fix device-majors. + */ + yyfile = "fixdevsw"; + if (fixdevsw()) + stop(); + + /* + * Perform cross-checking. + */ + if (maxusers == 0) { + if (defmaxusers) { + (void)printf("maxusers not specified; %d assumed\n", + defmaxusers); + maxusers = defmaxusers; + } else { + warnx("need \"maxusers\" line"); + errors++; + } + } + if (crosscheck() || errors) + stop(); + + /* + * Squeeze things down and finish cross-checks (STAR checks must + * run after packing). + */ + yyfile = "pack"; + pack(); + yyfile = "badstar"; + if (badstar()) + stop(); + + yyfile = NULL; + /* + * Ready to go. Build all the various files. + */ + if (mksymlinks() || mkmakefile() || mkheaders() || mkswap() || + mkioconf() || (do_devsw ? mkdevsw() : 0) || mkident() || errors) + stop(); + (void)printf("Build directory is %s\n", builddir); + (void)printf("Don't forget to run \"make depend\"\n"); + return 0; +} + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s [-Ppv] [-b builddir] [-D var=value] " + "[-s srcdir] [-U var] " + "[config-file]\n\t%s -x [kernel-file]\n" + "\t%s -L [-v] [-s srcdir] [config-file]\n", + getprogname(), getprogname(), getprogname()); + exit(1); +} + +/* + * Set any options that are implied by other options. + */ +static void +dependopts(void) +{ + struct nvlist *nv; + + for (nv = options; nv != NULL; nv = nv->nv_next) { + dependopts_one(nv->nv_name); + } + + for (nv = fsoptions; nv != NULL; nv = nv->nv_next) { + dependopts_one(nv->nv_name); + } +} + +static void +dependopts_one(const char *name) +{ + struct defoptlist *dl; + struct nvlist *fs; + + dl = find_declared_option_option(name); + if (dl != NULL) { + do_depends(dl->dl_depends); + } + fs = find_declared_fs_option(name); + if (fs != NULL) { + do_depends(fs->nv_ptr); + } + + CFGDBG(3, "depend `%s' searched", name); +} + +static void +do_depends(struct nvlist *nv) +{ + struct nvlist *opt; + + for (opt = nv; opt != NULL; opt = opt->nv_next) { + do_depend(opt); + } +} + +static void +do_depend(struct nvlist *nv) +{ + struct attr *a; + + if (nv != NULL && (nv->nv_flags & NV_DEPENDED) == 0) { + nv->nv_flags |= NV_DEPENDED; + /* + * If the dependency is an attribute, then just add + * it to the selecttab. + */ + CFGDBG(3, "depend attr `%s'", nv->nv_name); + if ((a = ht_lookup(attrtab, nv->nv_name)) != NULL) { + if (a->a_iattr) + panic("do_depend(%s): dep `%s' is an iattr", + nv->nv_name, a->a_name); + expandattr(a, selectattr); + } else { + if (ht_lookup(opttab, nv->nv_name) == NULL) + addoption(nv->nv_name, NULL); + dependopts_one(nv->nv_name); + } + } +} + +static int +recreate(const char *p, const char *q) +{ + int ret; + + if ((ret = unlink(q)) == -1 && errno != ENOENT) + warn("unlink(%s)\n", q); + if ((ret = symlink(p, q)) == -1) + warn("symlink(%s -> %s)", q, p); + return ret; +} + +/* + * Make a symlink for "machine" so that "#include <machine/foo.h>" works, + * and for the machine's CPU architecture, so that works as well. + */ +static int +mksymlinks(void) +{ + int ret; + char *p, buf[MAXPATHLEN]; + const char *q; + struct nvlist *nv; + + snprintf(buf, sizeof(buf), "arch/%s/include", machine); + p = sourcepath(buf); + ret = recreate(p, "machine"); + ret = recreate(p, machine); + free(p); + + if (machinearch != NULL) { + snprintf(buf, sizeof(buf), "arch/%s/include", machinearch); + p = sourcepath(buf); + q = machinearch; + } else { + p = estrdup("machine"); + q = machine; + } + + ret = recreate(p, q); + free(p); + + for (nv = machinesubarches; nv != NULL; nv = nv->nv_next) { + q = nv->nv_name; + snprintf(buf, sizeof(buf), "arch/%s/include", q); + p = sourcepath(buf); + ret = recreate(p, q); + free(p); + } + + return (ret); +} + +static __dead void +stop(void) +{ + (void)fprintf(stderr, "*** Stop.\n"); + exit(1); +} + +static void +check_dependencies(const char *thing, struct nvlist *deps) +{ + struct nvlist *dep; + struct attr *a; + + for (dep = deps; dep != NULL; dep = dep->nv_next) { + /* + * If the dependency is an attribute, it must not + * be an interface attribute. Otherwise, it must + * be a previously declared option. + */ + if ((a = ht_lookup(attrtab, dep->nv_name)) != NULL) { + if (a->a_iattr) + cfgerror("option `%s' dependency `%s' " + "is an interface attribute", + thing, a->a_name); + } else if (OPT_OBSOLETE(dep->nv_name)) { + cfgerror("option `%s' dependency `%s' " + "is obsolete", thing, dep->nv_name); + } else if (!is_declared_option(dep->nv_name)) { + cfgerror("option `%s' dependency `%s' " + "is an unknown option", + thing, dep->nv_name); + } + } +} + +static void +add_fs_dependencies(struct nvlist *nv, struct nvlist *deps) +{ + /* Use nv_ptr to link any other options that are implied. */ + nv->nv_ptr = deps; + check_dependencies(nv->nv_name, deps); +} + +static void +add_opt_dependencies(struct defoptlist *dl, struct nvlist *deps) +{ + dl->dl_depends = deps; + check_dependencies(dl->dl_name, deps); +} + +/* + * Define one or more file systems. + */ +void +deffilesystem(struct nvlist *fses, struct nvlist *deps) +{ + struct nvlist *nv; + + /* + * Mark these options as ones to skip when creating the Makefile. + */ + for (nv = fses; nv != NULL; nv = nv->nv_next) { + if (DEFINED_OPTION(nv->nv_name)) { + cfgerror("file system or option `%s' already defined", + nv->nv_name); + return; + } + + /* + * Also mark it as a valid file system, which may be + * used in "file-system" directives in the config + * file. + */ + if (nvhash_insert(deffstab, nv->nv_name, nv)) + panic("file system `%s' already in table?!", + nv->nv_name); + + add_fs_dependencies(nv, deps); + + /* + * Implicit attribute definition for filesystem. + */ + const char *n; + n = strtolower(nv->nv_name); + refattr(n); + } +} + +/* + * Sanity check a file name. + */ +int +badfilename(const char *fname) +{ + const char *n; + + /* + * We're putting multiple options into one file. Sanity + * check the file name. + */ + if (strchr(fname, '/') != NULL) { + cfgerror("option file name contains a `/'"); + return 1; + } + if ((n = strrchr(fname, '.')) == NULL || strcmp(n, ".h") != 0) { + cfgerror("option file name does not end in `.h'"); + return 1; + } + return 0; +} + + +/* + * Search for a defined option (defopt, filesystem, etc), and if found, + * return the option's struct nvlist. + * + * This used to be one function (find_declared_option) before options + * and filesystems became different types. + */ +static struct defoptlist * +find_declared_option_option(const char *name) +{ + struct defoptlist *option; + + if ((option = dlhash_lookup(defopttab, name)) != NULL || + (option = dlhash_lookup(defparamtab, name)) != NULL || + (option = dlhash_lookup(defflagtab, name)) != NULL) { + return (option); + } + + return (NULL); +} + +static struct nvlist * +find_declared_fs_option(const char *name) +{ + struct nvlist *fs; + + if ((fs = nvhash_lookup(deffstab, name)) != NULL) { + return fs; + } + + return (NULL); +} + +/* + * Like find_declared_option but doesn't return what it finds, so it + * can search both the various kinds of options and also filesystems. + */ +int +is_declared_option(const char *name) +{ + struct defoptlist *option = NULL; + struct nvlist *fs; + + if ((option = dlhash_lookup(defopttab, name)) != NULL || + (option = dlhash_lookup(defparamtab, name)) != NULL || + (option = dlhash_lookup(defflagtab, name)) != NULL) { + return 1; + } + if ((fs = nvhash_lookup(deffstab, name)) != NULL) { + return 1; + } + + return 0; +} + +/* + * Define one or more standard options. If an option file name is specified, + * place all options in one file with the specified name. Otherwise, create + * an option file for each option. + * record the option information in the specified table. + */ +void +defopt(struct dlhash *ht, const char *fname, struct defoptlist *opts, + struct nvlist *deps, int obs) +{ + struct defoptlist *dl, *nextdl, *olddl; + const char *name; + char buf[500]; + + if (fname != NULL && badfilename(fname)) { + return; + } + + /* + * Mark these options as ones to skip when creating the Makefile. + */ + for (dl = opts; dl != NULL; dl = nextdl) { + nextdl = dl->dl_next; + + if (dl->dl_lintvalue != NULL) { + /* + * If an entry already exists, then we are about to + * complain, so no worry. + */ + (void) dlhash_insert(defoptlint, dl->dl_name, + dl); + } + + /* An option name can be declared at most once. */ + if (DEFINED_OPTION(dl->dl_name)) { + cfgerror("file system or option `%s' already defined", + dl->dl_name); + return; + } + + if (dlhash_insert(ht, dl->dl_name, dl)) { + cfgerror("file system or option `%s' already defined", + dl->dl_name); + return; + } + + if (fname == NULL) { + /* + * Each option will be going into its own file. + * Convert the option name to lower case. This + * lower case name will be used as the option + * file name. + */ + (void) snprintf(buf, sizeof(buf), "opt_%s.h", + strtolower(dl->dl_name)); + name = intern(buf); + } else { + name = fname; + } + + add_opt_dependencies(dl, deps); + + /* + * Remove this option from the parameter list before adding + * it to the list associated with this option file. + */ + dl->dl_next = NULL; + + /* + * Flag as obsolete, if requested. + */ + if (obs) { + dl->dl_obsolete = 1; + (void)dlhash_insert(obsopttab, dl->dl_name, dl); + } + + /* + * Add this option file if we haven't seen it yet. + * Otherwise, append to the list of options already + * associated with this file. + */ + if ((olddl = dlhash_lookup(optfiletab, name)) == NULL) { + (void)dlhash_insert(optfiletab, name, dl); + } else { + while (olddl->dl_next != NULL) + olddl = olddl->dl_next; + olddl->dl_next = dl; + } + } +} + +/* + * Define one or more standard options. If an option file name is specified, + * place all options in one file with the specified name. Otherwise, create + * an option file for each option. + */ +void +defoption(const char *fname, struct defoptlist *opts, struct nvlist *deps) +{ + + cfgwarn("The use of `defopt' is deprecated"); + defopt(defopttab, fname, opts, deps, 0); +} + + +/* + * Define an option for which a value is required. + */ +void +defparam(const char *fname, struct defoptlist *opts, struct nvlist *deps, int obs) +{ + + defopt(defparamtab, fname, opts, deps, obs); +} + +/* + * Define an option which must not have a value, and which + * emits a "needs-flag" style output. + */ +void +defflag(const char *fname, struct defoptlist *opts, struct nvlist *deps, int obs) +{ + + defopt(defflagtab, fname, opts, deps, obs); +} + + +/* + * Add an option from "options FOO". Note that this selects things that + * are "optional foo". + */ +void +addoption(const char *name, const char *value) +{ + const char *n; + int is_fs, is_param, is_flag, is_undecl, is_obs; + + /* + * Figure out how this option was declared (if at all.) + * XXX should use "params" and "flags" in config. + * XXX crying out for a type field in a unified hashtab. + */ + is_fs = OPT_FSOPT(name); + is_param = OPT_DEFPARAM(name); + is_flag = OPT_DEFFLAG(name); + is_obs = OPT_OBSOLETE(name); + is_undecl = !DEFINED_OPTION(name); + + /* Warn and pretend the user had not selected the option */ + if (is_obs) { + cfgwarn("obsolete option `%s' will be ignored", name); + return; + } + + /* Make sure this is not a defined file system. */ + if (is_fs) { + cfgerror("`%s' is a defined file system", name); + return; + } + /* A defparam must have a value */ + if (is_param && value == NULL) { + cfgerror("option `%s' must have a value", name); + return; + } + /* A defflag must not have a value */ + if (is_flag && value != NULL) { + cfgerror("option `%s' must not have a value", name); + return; + } + + if (is_undecl && vflag) { + cfgwarn("undeclared option `%s' added to IDENT", name); + } + + if (do_option(opttab, &nextopt, name, value, "options")) + return; + + /* make lowercase, then add to select table */ + n = strtolower(name); + (void)ht_insert(selecttab, n, (void *)__UNCONST(n)); + CFGDBG(3, "option selected `%s'", n); +} + +void +deloption(const char *name) +{ + + CFGDBG(4, "deselecting opt `%s'", name); + if (undo_option(opttab, &options, &nextopt, name, "options")) + return; + if (undo_option(selecttab, NULL, NULL, strtolower(name), "options")) + return; +} + +/* + * Add a file system option. This routine simply inserts the name into + * a list of valid file systems, which is used to validate the root + * file system type. The name is then treated like a standard option. + */ +void +addfsoption(const char *name) +{ + const char *n; + + /* Make sure this is a defined file system. */ + if (!OPT_FSOPT(name)) { + cfgerror("`%s' is not a defined file system", name); + return; + } + + /* + * Convert to lower case. This will be used in the select + * table, to verify root file systems. + */ + n = strtolower(name); + + if (do_option(fsopttab, &nextfsopt, name, n, "file-system")) + return; + + /* Add to select table. */ + (void)ht_insert(selecttab, n, __UNCONST(n)); + CFGDBG(3, "fs selected `%s'", name); + + /* + * Select attribute if one exists. + */ + struct attr *a; + if ((a = ht_lookup(attrtab, n)) != NULL) + selectattr(a); +} + +void +delfsoption(const char *name) +{ + const char *n; + + CFGDBG(4, "deselecting fs `%s'", name); + n = strtolower(name); + if (undo_option(fsopttab, &fsoptions, &nextfsopt, name, "file-system")) + return; + if (undo_option(selecttab, NULL, NULL, n, "file-system")) + return; +} + +/* + * Add a "make" option. + */ +void +addmkoption(const char *name, const char *value) +{ + + (void)do_option(mkopttab, &nextmkopt, name, value, "makeoptions"); +} + +void +delmkoption(const char *name) +{ + + CFGDBG(4, "deselecting mkopt `%s'", name); + (void)undo_option(mkopttab, &mkoptions, &nextmkopt, name, + "makeoptions"); +} + +/* + * Add an appending "make" option. + */ +void +appendmkoption(const char *name, const char *value) +{ + struct nvlist *nv; + + nv = newnv(name, value, NULL, 0, NULL); + *nextappmkopt = nv; + nextappmkopt = &nv->nv_next; +} + +/* + * Add a conditional appending "make" option. + */ +void +appendcondmkoption(struct condexpr *cond, const char *name, const char *value) +{ + struct nvlist *nv; + + nv = newnv(name, value, cond, 0, NULL); + *nextcndmkopt = nv; + nextcndmkopt = &nv->nv_next; +} + +/* + * Add a name=value pair to an option list. The value may be NULL. + */ +static int +do_option(struct hashtab *ht, struct nvlist ***nppp, const char *name, + const char *value, const char *type) +{ + struct nvlist *nv; + + /* assume it will work */ + nv = newnv(name, value, NULL, 0, NULL); + if (ht_insert(ht, name, nv) == 0) { + **nppp = nv; + *nppp = &nv->nv_next; + return (0); + } + + /* oops, already got that option */ + nvfree(nv); + if ((nv = ht_lookup(ht, name)) == NULL) + panic("do_option"); + if (nv->nv_str != NULL && !OPT_FSOPT(name)) + cfgerror("already have %s `%s=%s'", type, name, nv->nv_str); + else + cfgerror("already have %s `%s'", type, name); + return (1); +} + +/* + * Remove a name from a hash table, + * and optionally, a name=value pair from an option list. + */ +static int +undo_option(struct hashtab *ht, struct nvlist **npp, + struct nvlist ***next, const char *name, const char *type) +{ + struct nvlist *nv; + + if (ht_remove(ht, name)) { + /* + * -U command line option removals are always silent + */ + if (!handling_cmdlineopts) + cfgwarn("%s `%s' is not defined", type, name); + return (1); + } + if (npp == NULL) { + CFGDBG(2, "opt `%s' deselected", name); + return (0); + } + + for ( ; *npp != NULL; npp = &(*npp)->nv_next) { + if ((*npp)->nv_name != name) + continue; + if (next != NULL && *next == &(*npp)->nv_next) + *next = npp; + nv = (*npp)->nv_next; + CFGDBG(2, "opt `%s' deselected", (*npp)->nv_name); + nvfree(*npp); + *npp = nv; + return (0); + } + panic("%s `%s' is not defined in nvlist", type, name); + return (1); +} + +/* + * Return true if there is at least one instance of the given unit + * on the given device attachment (or any units, if unit == WILD). + */ +int +deva_has_instances(struct deva *deva, int unit) +{ + struct devi *i; + + /* + * EHAMMERTOOBIG: we shouldn't check i_pseudoroot here. + * What we want by this check is them to appear non-present + * except for purposes of other devices being able to attach + * to them. + */ + for (i = deva->d_ihead; i != NULL; i = i->i_asame) + if (i->i_active == DEVI_ACTIVE && i->i_pseudoroot == 0 && + (unit == WILD || unit == i->i_unit || i->i_unit == STAR)) + return (1); + return (0); +} + +/* + * Return true if there is at least one instance of the given unit + * on the given base (or any units, if unit == WILD). + */ +int +devbase_has_instances(struct devbase *dev, int unit) +{ + struct deva *da; + + /* + * Pseudo-devices are a little special. We consider them + * to have instances only if they are both: + * + * 1. Included in this kernel configuration. + * + * 2. Be declared "defpseudodev". + */ + if (dev->d_ispseudo) { + return ((ht_lookup(devitab, dev->d_name) != NULL) + && (dev->d_ispseudo > 1)); + } + + for (da = dev->d_ahead; da != NULL; da = da->d_bsame) + if (deva_has_instances(da, unit)) + return (1); + return (0); +} + +static int +cfcrosscheck(struct config *cf, const char *what, struct nvlist *nv) +{ + struct devbase *dev; + struct devi *pd; + int errs, devunit; + + if (maxpartitions <= 0) + panic("cfcrosscheck"); + + for (errs = 0; nv != NULL; nv = nv->nv_next) { + if (nv->nv_name == NULL) + continue; + dev = ht_lookup(devbasetab, nv->nv_name); + if (dev == NULL) + panic("cfcrosscheck(%s)", nv->nv_name); + if (has_attr(dev->d_attrs, s_ifnet)) + devunit = nv->nv_ifunit; /* XXX XXX XXX */ + else + devunit = (int)(minor(nv->nv_num) / maxpartitions); + if (devbase_has_instances(dev, devunit)) + continue; + if (devbase_has_instances(dev, STAR) && + devunit >= dev->d_umax) + continue; + TAILQ_FOREACH(pd, &allpseudo, i_next) { + if (pd->i_base == dev && devunit < dev->d_umax && + devunit >= 0) + goto loop; + } + (void)fprintf(stderr, + "%s:%d: %s says %s on %s, but there's no %s\n", + conffile, cf->cf_lineno, + cf->cf_name, what, nv->nv_str, nv->nv_str); + errs++; + loop: + ; + } + return (errs); +} + +/* + * Cross-check the configuration: make sure that each target device + * or attribute (`at foo[0*?]') names at least one real device. Also + * see that the root and dump devices for all configurations are there. + */ +int +crosscheck(void) +{ + struct config *cf; + int errs; + + errs = 0; + if (TAILQ_EMPTY(&allcf)) { + warnx("%s has no configurations!", conffile); + errs++; + } + TAILQ_FOREACH(cf, &allcf, cf_next) { + if (cf->cf_root != NULL) { /* i.e., not root on ? */ + errs += cfcrosscheck(cf, "root", cf->cf_root); + errs += cfcrosscheck(cf, "dumps", cf->cf_dump); + } + } + return (errs); +} + +/* + * Check to see if there is a *'d unit with a needs-count file. + */ +int +badstar(void) +{ + struct devbase *d; + struct deva *da; + struct devi *i; + int errs, n; + + errs = 0; + TAILQ_FOREACH(d, &allbases, d_next) { + for (da = d->d_ahead; da != NULL; da = da->d_bsame) + for (i = da->d_ihead; i != NULL; i = i->i_asame) { + if (i->i_unit == STAR) + goto aybabtu; + } + continue; + aybabtu: + if (ht_lookup(needcnttab, d->d_name)) { + warnx("%s's cannot be *'d until its driver is fixed", + d->d_name); + errs++; + continue; + } + for (n = 0; i != NULL; i = i->i_alias) + if (!i->i_collapsed) + n++; + if (n < 1) + panic("badstar() n<1"); + } + return (errs); +} + +/* + * Verify/create builddir if necessary, change to it, and verify srcdir. + * This will be called when we see the first include. + */ +void +setupdirs(void) +{ + struct stat st; + + /* srcdir must be specified if builddir is not specified or if + * no configuration filename was specified. */ + if ((builddir || strcmp(defbuilddir, ".") == 0) && !srcdir) { + cfgerror("source directory must be specified"); + exit(1); + } + + if (Lflag) { + if (srcdir == NULL) + srcdir = "../../.."; + return; + } + + if (srcdir == NULL) + srcdir = "../../../.."; + if (builddir == NULL) + builddir = defbuilddir; + + if (stat(builddir, &st) == -1) { + if (mkdir(builddir, 0777) == -1) + errx(EXIT_FAILURE, "cannot create %s", builddir); + } else if (!S_ISDIR(st.st_mode)) + errx(EXIT_FAILURE, "%s is not a directory", builddir); + if (chdir(builddir) == -1) + err(EXIT_FAILURE, "cannot change to %s", builddir); + if (stat(srcdir, &st) == -1) + err(EXIT_FAILURE, "cannot stat %s", srcdir); + if (!S_ISDIR(st.st_mode)) + errx(EXIT_FAILURE, "%s is not a directory", srcdir); +} + +/* + * Write identifier from "ident" directive into file, for + * newvers.sh to pick it up. + */ +int +mkident(void) +{ + FILE *fp; + int error = 0; + + (void)unlink("ident"); + + if (ident == NULL) + return (0); + + if ((fp = fopen("ident", "w")) == NULL) { + warn("cannot write ident"); + return (1); + } + if (vflag) + (void)printf("using ident '%s'\n", ident); + fprintf(fp, "%s\n", ident); + fflush(fp); + if (ferror(fp)) + error = 1; + (void)fclose(fp); + + return error; +} + +void +logconfig_start(void) +{ + extern FILE *yyin; + char line[1024]; + const char *tmpdir; + struct stat st; + int fd; + + if (yyin == NULL || fstat(fileno(yyin), &st) == -1) + return; + cfgtime = st.st_mtime; + + tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + tmpdir = _PATH_TMP; + (void)snprintf(line, sizeof(line), "%s/config.tmp.XXXXXX", tmpdir); + if ((fd = mkstemp(line)) == -1 || + (cfg = fdopen(fd, "r+")) == NULL) { + if (fd != -1) { + (void)unlink(line); + (void)close(fd); + } + cfg = NULL; + return; + } + (void)unlink(line); + + (void)fprintf(cfg, "#include <sys/cdefs.h>\n\n"); + (void)fprintf(cfg, "#include \"opt_config.h\"\n"); + (void)fprintf(cfg, "\n"); + (void)fprintf(cfg, "/*\n"); + (void)fprintf(cfg, " * Add either (or both) of\n"); + (void)fprintf(cfg, " *\n"); + (void)fprintf(cfg, " *\toptions %s\n", LOGCONFIG_LARGE); + (void)fprintf(cfg, " *\toptions %s\n", LOGCONFIG_SMALL); + (void)fprintf(cfg, " *\n"); + (void)fprintf(cfg, + " * to your kernel config file to embed it in the resulting\n"); + (void)fprintf(cfg, + " * kernel. The latter option does not include files that are\n"); + (void)fprintf(cfg, + " * included (recursively) by your config file. The embedded\n"); + (void)fprintf(cfg, + " * data be extracted by using the command:\n"); + (void)fprintf(cfg, " *\n"); + (void)fprintf(cfg, + " *\tstrings netbsd | sed -n 's/^_CFG_//p' | unvis\n"); + (void)fprintf(cfg, " */\n"); + (void)fprintf(cfg, "\n"); + (void)fprintf(cfg, "#ifdef CONFIG_FILE\n"); + (void)fprintf(cfg, "#if defined(%s) || defined(%s)\n\n", + LOGCONFIG_LARGE, LOGCONFIG_SMALL); + (void)fprintf(cfg, "static const char config[] __used =\n\n"); + + (void)fprintf(cfg, "#ifdef %s\n\n", LOGCONFIG_LARGE); + (void)fprintf(cfg, "\"_CFG_### START CONFIG FILE \\\"%s\\\"\\n\"\n\n", + conffile); + (void)fprintf(cfg, "#endif /* %s */\n\n", LOGCONFIG_LARGE); + + logconfig_include(yyin, NULL); + + (void)fprintf(cfg, "#ifdef %s\n\n", LOGCONFIG_LARGE); + (void)fprintf(cfg, "\"_CFG_### END CONFIG FILE \\\"%s\\\"\\n\"\n", + conffile); + + rewind(yyin); +} + +void +logconfig_include(FILE *cf, const char *filename) +{ + char line[1024], in[2048], *out; + struct stat st; + int missingeol; + + if (!cfg) + return; + + missingeol = 0; + if (fstat(fileno(cf), &st) == -1) + return; + if (cfgtime < st.st_mtime) + cfgtime = st.st_mtime; + + if (filename) + (void)fprintf(cfg, + "\"_CFG_### (included from \\\"%s\\\")\\n\"\n", + filename); + while (fgets(line, sizeof(line), cf) != NULL) { + missingeol = 1; + (void)fprintf(cfg, "\"_CFG_"); + if (filename) + (void)fprintf(cfg, "###> "); + strvis(in, line, VIS_TAB); + for (out = in; *out; out++) + switch (*out) { + case '\n': + (void)fprintf(cfg, "\\n\"\n"); + missingeol = 0; + break; + case '"': case '\\': + (void)fputc('\\', cfg); + /* FALLTHROUGH */ + default: + (void)fputc(*out, cfg); + break; + } + } + if (missingeol) { + (void)fprintf(cfg, "\\n\"\n"); + warnx("%s: newline missing at EOF", + filename != NULL ? filename : conffile); + } + if (filename) + (void)fprintf(cfg, "\"_CFG_### (end include \\\"%s\\\")\\n\"\n", + filename); + + rewind(cf); +} + +void +logconfig_end(void) +{ + char line[1024]; + FILE *fp; + struct stat st; + + if (!cfg) + return; + + (void)fprintf(cfg, "#endif /* %s */\n", LOGCONFIG_LARGE); + (void)fprintf(cfg, ";\n"); + (void)fprintf(cfg, "#endif /* %s || %s */\n", + LOGCONFIG_LARGE, LOGCONFIG_SMALL); + (void)fprintf(cfg, "#endif /* CONFIG_FILE */\n"); + fflush(cfg); + if (ferror(cfg)) + err(EXIT_FAILURE, "write to temporary file for config.h failed"); + rewind(cfg); + + if (stat("config_file.h", &st) != -1) { + if (cfgtime < st.st_mtime) { + fclose(cfg); + return; + } + } + + fp = fopen("config_file.h", "w"); + if (!fp) + err(EXIT_FAILURE, "cannot open \"config.h\""); + + while (fgets(line, sizeof(line), cfg) != NULL) + fputs(line, fp); + fflush(fp); + if (ferror(fp)) + err(EXIT_FAILURE, "write to \"config.h\" failed"); + fclose(fp); + fclose(cfg); +} + +const char * +strtolower(const char *name) +{ + const char *n; + char *p, low[500]; + char c; + + for (n = name, p = low; (c = *n) != '\0'; n++) + *p++ = (char)(isupper((u_char)c) ? tolower((u_char)c) : c); + *p = '\0'; + return (intern(low)); +} + +static int +is_elf(const char *file) +{ + int kernel; + char hdr[4]; + + kernel = open(file, O_RDONLY); + if (kernel == -1) + err(EXIT_FAILURE, "cannot open %s", file); + if (read(kernel, hdr, 4) != 4) + err(EXIT_FAILURE, "Cannot read from %s", file); + (void)close(kernel); + + return memcmp("\177ELF", hdr, 4) == 0 ? 1 : 0; +} + +static int +extract_config(const char *kname, const char *cname, int cfd) +{ + char *ptr; + int found, kfd; + struct stat st; + off_t i; + + found = 0; + + /* mmap(2) binary kernel */ + kfd = open(conffile, O_RDONLY); + if (kfd == -1) + err(EXIT_FAILURE, "cannot open %s", kname); + if (fstat(kfd, &st) == -1) + err(EXIT_FAILURE, "cannot stat %s", kname); + ptr = mmap(0, (size_t)st.st_size, PROT_READ, MAP_FILE | MAP_SHARED, + kfd, 0); + if (ptr == MAP_FAILED) + err(EXIT_FAILURE, "cannot mmap %s", kname); + + /* Scan mmap(2)'ed region, extracting kernel configuration */ + for (i = 0; i < st.st_size; i++) { + if ((*ptr == '_') && (st.st_size - i > 5) && memcmp(ptr, + "_CFG_", 5) == 0) { + /* Line found */ + char *oldptr, line[LINE_MAX + 1], uline[LINE_MAX + 1]; + int j; + + found = 1; + + oldptr = (ptr += 5); + while (*ptr != '\n' && *ptr != '\0') + ptr++; + if (ptr - oldptr > LINE_MAX) + errx(EXIT_FAILURE, "line too long"); + i += ptr - oldptr + 5; + (void)memcpy(line, oldptr, (size_t)(ptr - oldptr)); + line[ptr - oldptr] = '\0'; + j = strunvis(uline, line); + if (j == -1) + errx(EXIT_FAILURE, "unvis: invalid " + "encoded sequence"); + uline[j] = '\n'; + if (write(cfd, uline, (size_t)j + 1) == -1) + err(EXIT_FAILURE, "cannot write to %s", cname); + } else + ptr++; + } + + (void)close(kfd); + + return found; +} + +struct dhdi_params { + struct devbase *d; + int unit; + int level; +}; + +static int +devbase_has_dead_instances(const char *key, void *value, void *aux) +{ + struct devi *i; + struct dhdi_params *dhdi = aux; + + for (i = value; i != NULL; i = i->i_alias) + if (i->i_base == dhdi->d && + (dhdi->unit == WILD || dhdi->unit == i->i_unit || + i->i_unit == STAR) && + i->i_level >= dhdi->level) + return 1; + return 0; +} + +/* + * This is almost the same as devbase_has_instances, except it + * may have special considerations regarding ignored instances. + */ + +static int +devbase_has_any_instance(struct devbase *dev, int unit, int state, int level) +{ + struct deva *da; + struct devi *i; + + if (dev->d_ispseudo) { + if (dev->d_ihead != NULL) + return 1; + else if (state != DEVI_IGNORED) + return 0; + if ((i = ht_lookup(deaddevitab, dev->d_name)) == NULL) + return 0; + return (i->i_level >= level); + } + + for (da = dev->d_ahead; da != NULL; da = da->d_bsame) + for (i = da->d_ihead; i != NULL; i = i->i_asame) + if ((i->i_active == DEVI_ACTIVE || + i->i_active == state) && + (unit == WILD || unit == i->i_unit || + i->i_unit == STAR)) + return 1; + + if (state == DEVI_IGNORED) { + struct dhdi_params dhdi = { dev, unit, level }; + /* also check dead devices */ + return ht_enumerate(deaddevitab, devbase_has_dead_instances, + &dhdi); + } + + return 0; +} + +/* + * check_dead_devi(), used with ht_enumerate, checks if any of the removed + * device instances would have been a valid instance considering the devbase, + * the parent device and the interface attribute. + * + * In other words, for a non-active device, it checks if children would be + * actual orphans or the result of a negative statement in the config file. + */ + +struct cdd_params { + struct devbase *d; + struct attr *at; + struct devbase *parent; +}; + +static int +check_dead_devi(const char *key, void *value, void *aux) +{ + struct cdd_params *cdd = aux; + struct devi *i = value; + struct pspec *p; + + if (i->i_base != cdd->d) + return 0; + + for (; i != NULL; i = i->i_alias) { + p = i->i_pspec; + if ((p == NULL && cdd->at == NULL) || + (p != NULL && p->p_iattr == cdd->at && + (p->p_atdev == NULL || p->p_atdev == cdd->parent))) { + if (p != NULL && + !devbase_has_any_instance(cdd->parent, p->p_atunit, + DEVI_IGNORED, i->i_level)) + return 0; + else + return 1; + } + } + return 0; +} + +static void +do_kill_orphans(struct devbase *d, struct attr *at, struct devbase *parent, + int state) +{ + struct nvlist *nv1; + struct attrlist *al; + struct attr *a; + struct devi *i, *j = NULL; + struct pspec *p; + int active = 0; + + /* + * A pseudo-device will always attach at root, and if it has an + * instance (it cannot have more than one), it is enough to consider + * it active, as there is no real attachment. + * + * A pseudo device can never be marked DEVI_IGNORED. + */ + if (d->d_ispseudo) { + if (d->d_ihead != NULL) + d->d_ihead->i_active = active = DEVI_ACTIVE; + else { + if (ht_lookup(deaddevitab, d->d_name) != NULL) + active = DEVI_IGNORED; + else + return; + } + } else { + int seen = 0; + + for (i = d->d_ihead; i != NULL; i = i->i_bsame) { + for (j = i; j != NULL; j = j->i_alias) { + p = j->i_pspec; + if ((p == NULL && at == NULL) || + (p != NULL && p->p_iattr == at && + (p->p_atdev == NULL || + p->p_atdev == parent))) { + if (p != NULL && + !devbase_has_any_instance(parent, + p->p_atunit, state, j->i_level)) + continue; + /* + * There are Fry-like devices which can + * be their own grand-parent (or even + * parent, like uhub). We don't want + * to loop, so if we've already reached + * an instance for one reason or + * another, stop there. + */ + if (j->i_active == DEVI_ACTIVE || + j->i_active == state) { + /* + * Device has already been + * seen. However it might + * have siblings who still + * have to be activated or + * orphaned. + */ + seen = 1; + continue; + } + j->i_active = active = state; + if (p != NULL) + p->p_active = state; + } + } + } + /* + * If we've been there but have made no change, stop. + */ + if (seen && !active) + return; + if (!active) { + struct cdd_params cdd = { d, at, parent }; + /* Look for a matching dead devi */ + if (ht_enumerate(deaddevitab, check_dead_devi, &cdd) && + d != parent) + /* + * That device had its instances removed. + * Continue the loop marking descendants + * with DEVI_IGNORED instead of DEVI_ACTIVE. + * + * There is one special case for devices that + * are their own parent: if that instance is + * removed (e.g., no uhub* at uhub?), we don't + * have to continue looping. + */ + active = DEVI_IGNORED; + else + return; + } + } + + for (al = d->d_attrs; al != NULL; al = al->al_next) { + a = al->al_this; + for (nv1 = a->a_devs; nv1 != NULL; nv1 = nv1->nv_next) + do_kill_orphans(nv1->nv_ptr, a, d, active); + } +} + +static int +/*ARGSUSED*/ +kill_orphans_cb(const char *key, void *value, void *aux) +{ + do_kill_orphans((struct devbase *)value, NULL, NULL, DEVI_ACTIVE); + return 0; +} + +static void +kill_orphans(void) +{ + ht_enumerate(devroottab, kill_orphans_cb, NULL); +} + +static void +add_makeopt(const char *opt) +{ + struct nvlist *p; + char *buf = estrdup(opt); + char *eq = strchr(buf, '='); + + if (!eq) + errx(EXIT_FAILURE, "-D %s is not in var=value format", opt); + + *eq = 0; + p = newnv(estrdup(buf), estrdup(eq+1), NULL, 0, NULL); + free(buf); + p->nv_next = cmdlinedefs; + cmdlinedefs = p; +} + +static void +remove_makeopt(const char *opt) +{ + struct nvlist *p; + + p = newnv(estrdup(opt), NULL, NULL, 0, NULL); + p->nv_next = cmdlineundefs; + cmdlineundefs = p; +} + +static void +handle_cmdline_makeoptions(void) +{ + struct nvlist *p, *n; + + handling_cmdlineopts = 1; + for (p = cmdlineundefs; p; p = n) { + n = p->nv_next; + delmkoption(intern(p->nv_name)); + free(__UNCONST(p->nv_name)); + nvfree(p); + } + for (p = cmdlinedefs; p; p = n) { + const char *name = intern(p->nv_name); + + n = p->nv_next; + delmkoption(name); + addmkoption(name, intern(p->nv_str)); + free(__UNCONST(p->nv_name)); + free(__UNCONST(p->nv_str)); + + nvfree(p); + } + handling_cmdlineopts = 0; +} diff --git a/buildrump.sh/src/usr.bin/config/mkdevsw.c b/buildrump.sh/src/usr.bin/config/mkdevsw.c new file mode 100644 index 00000000..831fcb4e --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/mkdevsw.c @@ -0,0 +1,241 @@ +/* $NetBSD: mkdevsw.c,v 1.12 2014/11/10 21:13:04 christos Exp $ */ + +/* + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by MAEKAWA Masahide (gehenna@NetBSD.org). + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: mkdevsw.c,v 1.12 2014/11/10 21:13:04 christos Exp $"); + +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <err.h> + +#include "defs.h" + +static void emitconv(FILE *); +static void emitdev(FILE *); +static void emitdevm(FILE *); +static void emitheader(FILE *); + +int +mkdevsw(void) +{ + FILE *fp; + + if ((fp = fopen("devsw.c.tmp", "w")) == NULL) { + warn("cannot create devsw.c"); + return (1); + } + + emitheader(fp); + emitdevm(fp); + emitconv(fp); + emitdev(fp); + + fflush(fp); + if (ferror(fp)) { + warn("error writing devsw.c"); + fclose(fp); + return 1; + } + + (void)fclose(fp); + + if (moveifchanged("devsw.c.tmp", "devsw.c") != 0) { + warn("error renaming devsw.c"); + return (1); + } + + return (0); +} + +static void +emitheader(FILE *fp) +{ + autogen_comment(fp, "devsw.c"); + + fputs("#include <sys/param.h>\n" + "#include <sys/conf.h>\n", fp); +} + +static void +dentry(FILE *fp, struct hashtab *t, devmajor_t i, char p) +{ + const struct devm *dm; + char mstr[16]; + + (void)snprintf(mstr, sizeof(mstr), "%d", i); + if ((dm = ht_lookup(t, intern(mstr))) == NULL) + return; + + fprintf(fp, "extern const struct %cdevsw %s_%cdevsw;\n", + p, dm->dm_name, p); +} + +static void +pentry(FILE *fp, struct hashtab *t, devmajor_t i, char p) +{ + const struct devm *dm; + char mstr[16]; + + (void)snprintf(mstr, sizeof(mstr), "%d", i); + dm = ht_lookup(t, intern(mstr)); + + if (dm) + fprintf(fp, "\t&%s_%cdevsw", dm->dm_name, p); + else + fputs("\tNULL", fp); + + fprintf(fp, ",\t// %3d\n", i); +} + +/* + * Emit device switch table for character/block device. + */ +static void +emitdevm(FILE *fp) +{ + devmajor_t i; + + fputs("\n/* device switch table for block device */\n", fp); + + for (i = 0; i <= maxbdevm ; i++) + dentry(fp, cdevmtab, i, 'b'); + + fputs("\nconst struct bdevsw *bdevsw0[] = {\n", fp); + + for (i = 0; i <= maxbdevm; i++) + pentry(fp, bdevmtab, i, 'b'); + + fputs("};\n\nconst struct bdevsw **bdevsw = bdevsw0;\n", fp); + + fputs("const int sys_bdevsws = __arraycount(bdevsw0);\n" + "int max_bdevsws = __arraycount(bdevsw0);\n", fp); + + fputs("\n/* device switch table for character device */\n", fp); + + for (i = 0; i <= maxcdevm; i++) + dentry(fp, cdevmtab, i, 'c'); + + fputs("\nconst struct cdevsw *cdevsw0[] = {\n", fp); + + for (i = 0; i <= maxcdevm; i++) + pentry(fp, cdevmtab, i, 'c'); + + fputs("};\n\nconst struct cdevsw **cdevsw = cdevsw0;\n", fp); + + fputs("const int sys_cdevsws = __arraycount(cdevsw0);\n" + "int max_cdevsws = __arraycount(cdevsw0);\n", fp); +} + +/* + * Emit device major conversion table. + */ +static void +emitconv(FILE *fp) +{ + struct devm *dm; + + fputs("\n/* device conversion table */\n" + "struct devsw_conv devsw_conv0[] = {\n", fp); + TAILQ_FOREACH(dm, &alldevms, dm_next) { + if (version < 20100430) { + /* Emit compatible structure */ + fprintf(fp, "\t{ \"%s\", %d, %d },\n", dm->dm_name, + dm->dm_bmajor, dm->dm_cmajor); + continue; + } + struct nvlist *nv; + const char *d_class, *d_flags = "0"; + int d_vec[2] = { 0, 0 }; + int i = 0; + + /* + * "parse" info. currently the rules are simple: + * 1) first entry defines class + * 2) next ones without n_str are d_vectdim + * 3) next one with n_str is d_flags + * 4) EOL + */ + nv = dm->dm_devnodes; + d_class = nv->nv_str; + while ((nv = nv->nv_next) != NULL) { + if (i > 2) + panic("invalid devnode definition"); + if (nv->nv_str) { + d_flags = nv->nv_str; + break; + } + if (nv->nv_num > INT_MAX || nv->nv_num < INT_MIN) + panic("out of range devnode definition"); + d_vec[i++] = (int)nv->nv_num; + } + + fprintf(fp, "\t{ \"%s\", %d, %d, %s, %s, { %d, %d }},\n", + dm->dm_name, dm->dm_bmajor, dm->dm_cmajor, + d_class, d_flags, d_vec[0], d_vec[1]); + + } + fputs("};\n\n" + "struct devsw_conv *devsw_conv = devsw_conv0;\n" + "int max_devsw_convs = __arraycount(devsw_conv0);\n", + fp); +} + +/* + * Emit specific device major informations. + */ +static void +emitdev(FILE *fp) +{ + struct devm *dm; + char mstr[16]; + + fputs("\n", fp); + + (void)strlcpy(mstr, "swap", sizeof(mstr)); + if ((dm = ht_lookup(bdevmtab, intern(mstr))) != NULL) { + fprintf(fp, "const dev_t swapdev = makedev(%d, 0);\n", + dm->dm_bmajor); + } + + (void)strlcpy(mstr, "mem", sizeof(mstr)); + if ((dm = ht_lookup(cdevmtab, intern(mstr))) == NULL) + panic("memory device is not configured"); + fprintf(fp, "const dev_t zerodev = makedev(%d, DEV_ZERO);\n", + dm->dm_cmajor); + + fputs("\n/* mem_no is only used in iskmemdev() */\n", fp); + fprintf(fp, "const int mem_no = %d;\n", dm->dm_cmajor); +} diff --git a/buildrump.sh/src/usr.bin/config/mkheaders.c b/buildrump.sh/src/usr.bin/config/mkheaders.c new file mode 100644 index 00000000..1d7d02f2 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/mkheaders.c @@ -0,0 +1,539 @@ +/* $NetBSD: mkheaders.c,v 1.26 2015/01/22 20:01:22 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)mkheaders.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: mkheaders.c,v 1.26 2015/01/22 20:01:22 christos Exp $"); + +#include <sys/param.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <util.h> +#include <err.h> +#include "defs.h" + +#include <crc_extern.h> + +static int emitcnt(struct nvlist *); +static int emitopts(void); +static int emittime(void); +static int herr(const char *, const char *, FILE *); +static int defopts_print(const char *, struct defoptlist *, void *); +static char *cntname(const char *); + +/* + * We define a global symbol with the name of each option and its value. + * This should stop code compiled with different options being linked together. + */ + +/* Unlikely constant for undefined options */ +#define UNDEFINED ('n' << 24 | 0 << 20 | 't' << 12 | 0xdefU) +/* Value for defined options with value UNDEFINED */ +#define DEFINED (0xdef1U << 16 | 'n' << 8 | 0xed) + +/* + * Make the various config-generated header files. + */ +int +mkheaders(void) +{ + struct files *fi; + + /* + * Make headers containing counts, as needed. + */ + TAILQ_FOREACH(fi, &allfiles, fi_next) { + if (fi->fi_flags & FI_HIDDEN) + continue; + if (fi->fi_flags & (FI_NEEDSCOUNT | FI_NEEDSFLAG) && + emitcnt(fi->fi_optf)) + return (1); + } + + if (emitopts() || emitlocs() || emitioconfh()) + return (1); + + /* + * If the minimum required version is ever bumped beyond 20090513, + * emittime() can be removed. + */ + if (version <= 20090513 && emittime()) + return (1); + + return (0); +} + +static void +fprint_global(FILE *fp, const char *name, long long value) +{ + /* + * We have to doubt the founding fathers here. + * The gas syntax for hppa is 'var .equ value', for all? other + * instruction sets it is ' .equ var,value'. both have been used in + * various assemblers, but supporting a common syntax would be good. + * Fortunately we can use .equiv since it has a consistent syntax, + * but requires us to detect multiple assignments - event with the + * same value. + */ + fprintf(fp, "#ifdef _LOCORE\n" + " .ifndef _KERNEL_OPT_%s\n" + " .global _KERNEL_OPT_%s\n" + " .equiv _KERNEL_OPT_%s,0x%llx\n" + " .endif\n" + "#else\n" + "__asm(\" .ifndef _KERNEL_OPT_%s\\n" + " .global _KERNEL_OPT_%s\\n" + " .equiv _KERNEL_OPT_%s,0x%llx\\n" + " .endif\");\n" + "#endif\n", + name, name, name, value, + name, name, name, value); +} + +/* Convert the option argument to a 32bit numder */ +static unsigned int +global_hash(const char *str) +{ + unsigned long h; + char *ep; + + /* + * If the value is a valid numeric, just use it + * We don't care about negative values here, we + * just use the value as a hash. + */ + h = strtoul(str, &ep, 0); + if (*ep != 0) + /* Otherwise shove through a 32bit CRC function */ + h = crc_buf(0, str, strlen(str)); + + /* Avoid colliding with the value used for undefined options. */ + /* At least until I stop any options being set to zero */ + return (unsigned int)(h != UNDEFINED ? h : DEFINED); +} + +static void +fprintcnt(FILE *fp, struct nvlist *nv) +{ + const char *name = cntname(nv->nv_name); + + fprintf(fp, "#define\t%s\t%lld\n", name, nv->nv_num); + fprint_global(fp, name, nv->nv_num); +} + +static int +emitcnt(struct nvlist *head) +{ + char nfname[BUFSIZ], tfname[BUFSIZ]; + struct nvlist *nv; + FILE *fp; + + (void)snprintf(nfname, sizeof(nfname), "%s.h", head->nv_name); + (void)snprintf(tfname, sizeof(tfname), "tmp_%s", nfname); + + if ((fp = fopen(tfname, "w")) == NULL) + return (herr("open", tfname, NULL)); + + for (nv = head; nv != NULL; nv = nv->nv_next) + fprintcnt(fp, nv); + + fflush(fp); + if (ferror(fp)) + return herr("writ", tfname, fp); + + if (fclose(fp) == EOF) + return (herr("clos", tfname, NULL)); + + return (moveifchanged(tfname, nfname)); +} + +/* + * Output a string, preceded by a tab and possibly unescaping any quotes. + * The argument will be output as is if it doesn't start with \". + * Otherwise the first backslash in a \? sequence will be dropped. + */ +static void +fprintstr(FILE *fp, const char *str) +{ + + if (strncmp(str, "\\\"", 2) != 0) { + (void)fprintf(fp, "\t%s", str); + return; + } + + (void)fputc('\t', fp); + + for (; *str; str++) { + switch (*str) { + case '\\': + if (!*++str) /* XXX */ + str--; + /*FALLTHROUGH*/ + default: + (void)fputc(*str, fp); + break; + } + } +} + +/* + * Callback function for walking the option file hash table. We write out + * the options defined for this file. + */ +static int +/*ARGSUSED*/ +defopts_print(const char *name, struct defoptlist *value, void *arg) +{ + char tfname[BUFSIZ]; + struct nvlist *option; + struct defoptlist *dl; + const char *opt_value; + int isfsoption; + FILE *fp; + + (void)snprintf(tfname, sizeof(tfname), "tmp_%s", name); + if ((fp = fopen(tfname, "w")) == NULL) + return (herr("open", tfname, NULL)); + + for (dl = value; dl != NULL; dl = dl->dl_next) { + isfsoption = OPT_FSOPT(dl->dl_name); + + if (dl->dl_obsolete) { + fprintf(fp, "/* %s `%s' is obsolete */\n", + isfsoption ? "file system" : "option", + dl->dl_name); + fprint_global(fp, dl->dl_name, 0xdeadbeef); + continue; + } + + if (((option = ht_lookup(opttab, dl->dl_name)) == NULL && + (option = ht_lookup(fsopttab, dl->dl_name)) == NULL) && + (dl->dl_value == NULL)) { + fprintf(fp, "/* %s `%s' not defined */\n", + isfsoption ? "file system" : "option", + dl->dl_name); + fprint_global(fp, dl->dl_name, UNDEFINED); + continue; + } + + opt_value = option != NULL ? option->nv_str : dl->dl_value; + if (isfsoption == 1) + /* For filesysteme we'd output the lower case name */ + opt_value = NULL; + + fprintf(fp, "#define\t%s", dl->dl_name); + if (opt_value != NULL) + fprintstr(fp, opt_value); + else if (!isfsoption) + fprintstr(fp, "1"); + fputc('\n', fp); + fprint_global(fp, dl->dl_name, + opt_value == NULL ? 1 : global_hash(opt_value)); + } + + fflush(fp); + if (ferror(fp)) + return herr("writ", tfname, fp); + + if (fclose(fp) == EOF) + return (herr("clos", tfname, NULL)); + + return (moveifchanged(tfname, name)); +} + +/* + * Emit the option header files. + */ +static int +emitopts(void) +{ + + return (dlhash_enumerate(optfiletab, defopts_print, NULL)); +} + +/* + * A callback function for walking the attribute hash table. + * Emit CPP definitions of manifest constants for the locators on the + * "name" attribute node (passed as the "value" parameter). + */ +static int +locators_print(const char *name, void *value, void *arg) +{ + struct attr *a; + struct loclist *ll; + int i; + char *locdup, *namedup; + char *cp; + FILE *fp = arg; + + a = value; + if (a->a_locs) { + if (strchr(name, ' ') != NULL || strchr(name, '\t') != NULL) + /* + * name contains a space; we can't generate + * usable defines, so ignore it. + */ + return 0; + locdup = estrdup(name); + for (cp = locdup; *cp; cp++) + if (islower((unsigned char)*cp)) + *cp = (char)toupper((unsigned char)*cp); + for (i = 0, ll = a->a_locs; ll; ll = ll->ll_next, i++) { + if (strchr(ll->ll_name, ' ') != NULL || + strchr(ll->ll_name, '\t') != NULL) + /* + * name contains a space; we can't generate + * usable defines, so ignore it. + */ + continue; + namedup = estrdup(ll->ll_name); + for (cp = namedup; *cp; cp++) + if (islower((unsigned char)*cp)) + *cp = (char)toupper((unsigned char)*cp); + else if (*cp == ARRCHR) + *cp = '_'; + fprintf(fp, "#define %sCF_%s %d\n", locdup, namedup, i); + if (ll->ll_string != NULL) + fprintf(fp, "#define %sCF_%s_DEFAULT %s\n", + locdup, namedup, ll->ll_string); + free(namedup); + } + /* assert(i == a->a_loclen) */ + fprintf(fp, "#define %sCF_NLOCS %d\n", locdup, a->a_loclen); + free(locdup); + } + return 0; +} + +/* + * Build the "locators.h" file with manifest constants for all potential + * locators in the configuration. Do this by enumerating the attribute + * hash table and emitting all the locators for each attribute. + */ +int +emitlocs(void) +{ + const char *tfname; + int rval; + FILE *tfp; + + tfname = "tmp_locators.h"; + if ((tfp = fopen(tfname, "w")) == NULL) + return (herr("open", tfname, NULL)); + + rval = ht_enumerate(attrtab, locators_print, tfp); + + fflush(tfp); + if (ferror(tfp)) + return (herr("writ", tfname, NULL)); + if (fclose(tfp) == EOF) + return (herr("clos", tfname, NULL)); + if (rval) + return (rval); + return (moveifchanged(tfname, "locators.h")); +} + +/* + * Build the "ioconf.h" file with extern declarations for all configured + * cfdrivers. + */ +int +emitioconfh(void) +{ + const char *tfname; + FILE *tfp; + struct devbase *d; + + tfname = "tmp_ioconf.h"; + if ((tfp = fopen(tfname, "w")) == NULL) + return (herr("open", tfname, NULL)); + + TAILQ_FOREACH(d, &allbases, d_next) { + if (!devbase_has_instances(d, WILD)) + continue; + fprintf(tfp, "extern struct cfdriver %s_cd;\n", d->d_name); + } + + fflush(tfp); + if (ferror(tfp)) + return herr("writ", tfname, tfp); + + if (fclose(tfp) == EOF) + return (herr("clos", tfname, NULL)); + + return (moveifchanged(tfname, "ioconf.h")); +} + +/* + * Make a file that config_time.h can use as a source, if required. + */ +static int +emittime(void) +{ + FILE *fp; + time_t t; + struct tm *tm; + char buf[128]; + + t = time(NULL); + tm = gmtime(&t); + + if ((fp = fopen("config_time.src", "w")) == NULL) + return (herr("open", "config_time.src", NULL)); + + if (strftime(buf, sizeof(buf), "%c %Z", tm) == 0) + return (herr("strftime", "config_time.src", fp)); + + fprintf(fp, "/* %s */\n" + "#define CONFIG_TIME\t%2lld\n" + "#define CONFIG_YEAR\t%2d\n" + "#define CONFIG_MONTH\t%2d\n" + "#define CONFIG_DATE\t%2d\n" + "#define CONFIG_HOUR\t%2d\n" + "#define CONFIG_MINS\t%2d\n" + "#define CONFIG_SECS\t%2d\n", + buf, (long long)t, + tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + fflush(fp); + if (ferror(fp)) + return (herr("fprintf", "config_time.src", fp)); + + if (fclose(fp) != 0) + return (herr("clos", "config_time.src", NULL)); + + /* + * *Don't* moveifchanged this file. Makefile.kern.inc will + * handle that if it determines such a move is necessary. + */ + return (0); +} + +/* + * Compare two files. If nfname doesn't exist, or is different from + * tfname, move tfname to nfname. Otherwise, delete tfname. + */ +int +moveifchanged(const char *tfname, const char *nfname) +{ + char tbuf[BUFSIZ], nbuf[BUFSIZ]; + FILE *tfp, *nfp; + + if ((tfp = fopen(tfname, "r")) == NULL) + return (herr("open", tfname, NULL)); + + if ((nfp = fopen(nfname, "r")) == NULL) + goto moveit; + + while (fgets(tbuf, sizeof(tbuf), tfp) != NULL) { + if (fgets(nbuf, sizeof(nbuf), nfp) == NULL) { + /* + * Old file has fewer lines. + */ + goto moveit; + } + if (strcmp(tbuf, nbuf) != 0) + goto moveit; + } + + /* + * We've reached the end of the new file. Check to see if new file + * has fewer lines than old. + */ + if (fgets(nbuf, sizeof(nbuf), nfp) != NULL) { + /* + * New file has fewer lines. + */ + goto moveit; + } + + (void) fclose(nfp); + (void) fclose(tfp); + if (remove(tfname) == -1) + return(herr("remov", tfname, NULL)); + return (0); + + moveit: + /* + * They're different, or the file doesn't exist. + */ + if (nfp) + (void) fclose(nfp); + if (tfp) + (void) fclose(tfp); + if (rename(tfname, nfname) == -1) + return (herr("renam", tfname, NULL)); + return (0); +} + +static int +herr(const char *what, const char *fname, FILE *fp) +{ + + warn("error %sing %s", what, fname); + if (fp) + (void)fclose(fp); + return (1); +} + +static char * +cntname(const char *src) +{ + char *dst; + char c; + static char buf[100]; + + dst = buf; + *dst++ = 'N'; + while ((c = *src++) != 0) + *dst++ = (char)(islower((u_char)c) ? toupper((u_char)c) : c); + *dst = 0; + return (buf); +} diff --git a/buildrump.sh/src/usr.bin/config/mkioconf.c b/buildrump.sh/src/usr.bin/config/mkioconf.c new file mode 100644 index 00000000..a73a3b73 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/mkioconf.c @@ -0,0 +1,512 @@ +/* $NetBSD: mkioconf.c,v 1.28 2014/11/01 11:02:41 uebayasi Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)mkioconf.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: mkioconf.c,v 1.28 2014/11/01 11:02:41 uebayasi Exp $"); + +#include <sys/param.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "defs.h" + +/* + * Make ioconf.c. + */ +static int cf_locators_print(const char *, void *, void *); +static int cforder(const void *, const void *); +static void emitcfdata(FILE *); +static void emitcfdrivers(FILE *); +static void emitexterns(FILE *); +static void emitcfattachinit(FILE *); +static void emithdr(FILE *); +static void emitloc(FILE *); +static void emitpseudo(FILE *); +static void emitparents(FILE *); +static void emitroots(FILE *); +static void emitname2blk(FILE *); + +#define SEP(pos, max) (((u_int)(pos) % (max)) == 0 ? "\n\t" : " ") + +#define ARRNAME(n, l) (strchr((n), ARRCHR) && strncmp((n), (l), strlen((l))) == 0) + +/* + * NEWLINE can only be used in the emitXXX functions. + * In most cases it can be subsumed into an fprintf. + */ +#define NEWLINE putc('\n', fp) + +int +mkioconf(void) +{ + FILE *fp; + + qsort(packed, npacked, sizeof *packed, cforder); + if ((fp = fopen("ioconf.c.tmp", "w")) == NULL) { + warn("cannot write ioconf.c"); + return (1); + } + + emithdr(fp); + emitcfdrivers(fp); + emitexterns(fp); + emitloc(fp); + emitparents(fp); + emitcfdata(fp); + emitcfattachinit(fp); + + if (ioconfname == NULL) { + emitroots(fp); + emitpseudo(fp); + if (!do_devsw) + emitname2blk(fp); + } + + fflush(fp); + if (ferror(fp)) { + warn("error writing ioconf.c"); + (void)fclose(fp); +#if 0 + (void)unlink("ioconf.c.tmp"); +#endif + return (1); + } + + (void)fclose(fp); + if (moveifchanged("ioconf.c.tmp", "ioconf.c") != 0) { + warn("error renaming ioconf.c"); + return (1); + } + return (0); +} + +static int +cforder(const void *a, const void *b) +{ + int n1, n2; + + n1 = (*(const struct devi * const *)a)->i_cfindex; + n2 = (*(const struct devi * const *)b)->i_cfindex; + return (n1 - n2); +} + +static void +emithdr(FILE *ofp) +{ + FILE *ifp; + size_t n; + char ifnbuf[200], buf[BUFSIZ]; + char *ifn; + + autogen_comment(ofp, "ioconf.c"); + + (void)snprintf(ifnbuf, sizeof(ifnbuf), "arch/%s/conf/ioconf.incl.%s", + machine ? machine : "(null)", machine ? machine : "(null)"); + ifn = sourcepath(ifnbuf); + if ((ifp = fopen(ifn, "r")) != NULL) { + while ((n = fread(buf, 1, sizeof(buf), ifp)) > 0) + (void)fwrite(buf, 1, n, ofp); + if (ferror(ifp)) + err(EXIT_FAILURE, "error reading %s", ifn); + (void)fclose(ifp); + } else { + fputs("#include <sys/param.h>\n" + "#include <sys/conf.h>\n" + "#include <sys/device.h>\n" + "#include <sys/mount.h>\n", ofp); + } + free(ifn); +} + +/* + * Emit an initialized array of character strings describing this + * attribute's locators. + */ +static int +cf_locators_print(const char *name, void *value, void *arg) +{ + struct attr *a; + struct loclist *ll; + FILE *fp = arg; + + a = value; + if (!a->a_iattr) + return (0); + if (ht_lookup(selecttab, name) == NULL) + return (0); + + if (a->a_locs) { + fprintf(fp, + "static const struct cfiattrdata %scf_iattrdata = {\n", + name); + fprintf(fp, "\t\"%s\", %d, {\n", name, a->a_loclen); + for (ll = a->a_locs; ll; ll = ll->ll_next) + fprintf(fp, "\t\t{ \"%s\", \"%s\", %s },\n", + ll->ll_name, + (ll->ll_string ? ll->ll_string : "NULL"), + (ll->ll_string ? ll->ll_string : "0")); + fprintf(fp, "\t}\n};\n"); + } else { + fprintf(fp, + "static const struct cfiattrdata %scf_iattrdata = {\n" + "\t\"%s\", 0, {\n\t\t{ NULL, NULL, 0 },\n\t}\n};\n", + name, name); + } + + return 0; +} + +static void +emitcfdrivers(FILE *fp) +{ + struct devbase *d; + struct attrlist *al; + struct attr *a; + int has_iattrs; + + NEWLINE; + ht_enumerate(attrtab, cf_locators_print, fp); + + NEWLINE; + TAILQ_FOREACH(d, &allbases, d_next) { + if (!devbase_has_instances(d, WILD)) + continue; + has_iattrs = 0; + for (al = d->d_attrs; al != NULL; al = al->al_next) { + a = al->al_this; + if (a->a_iattr == 0) + continue; + if (has_iattrs == 0) + fprintf(fp, + "static const struct cfiattrdata * const %s_attrs[] = { ", + d->d_name); + has_iattrs = 1; + fprintf(fp, "&%scf_iattrdata, ", a->a_name); + } + if (has_iattrs) + fprintf(fp, "NULL };\n"); + fprintf(fp, "CFDRIVER_DECL(%s, %s, ", d->d_name, /* ) */ + d->d_classattr != NULL ? d->d_classattr->a_devclass + : "DV_DULL"); + if (has_iattrs) + fprintf(fp, "%s_attrs", d->d_name); + else + fprintf(fp, "NULL"); + fprintf(fp, /* ( */ ");\n\n"); + } + + NEWLINE; + + fprintf(fp, + "%sstruct cfdriver * const cfdriver_%s_%s[] = {\n", + ioconfname ? "static " : "", + ioconfname ? "ioconf" : "list", + ioconfname ? ioconfname : "initial"); + + TAILQ_FOREACH(d, &allbases, d_next) { + if (!devbase_has_instances(d, WILD)) + continue; + fprintf(fp, "\t&%s_cd,\n", d->d_name); + } + fprintf(fp, "\tNULL\n};\n"); +} + +static void +emitexterns(FILE *fp) +{ + struct deva *da; + + NEWLINE; + TAILQ_FOREACH(da, &alldevas, d_next) { + if (!deva_has_instances(da, WILD)) + continue; + fprintf(fp, "extern struct cfattach %s_ca;\n", + da->d_name); + } +} + +static void +emitcfattachinit(FILE *fp) +{ + struct devbase *d; + struct deva *da; + + NEWLINE; + TAILQ_FOREACH(d, &allbases, d_next) { + if (!devbase_has_instances(d, WILD)) + continue; + if (d->d_ahead == NULL) + continue; + + fprintf(fp, + "static struct cfattach * const %s_cfattachinit[] = {\n\t", + d->d_name); + for (da = d->d_ahead; da != NULL; da = da->d_bsame) { + if (!deva_has_instances(da, WILD)) + continue; + fprintf(fp, "&%s_ca, ", da->d_name); + } + fprintf(fp, "NULL\n};\n"); + } + + NEWLINE; + fprintf(fp, "%sconst struct cfattachinit cfattach%s%s[] = {\n", + ioconfname ? "static " : "", + ioconfname ? "_ioconf_" : "init", + ioconfname ? ioconfname : ""); + + TAILQ_FOREACH(d, &allbases, d_next) { + if (!devbase_has_instances(d, WILD)) + continue; + if (d->d_ahead == NULL) + continue; + + fprintf(fp, "\t{ \"%s\", %s_cfattachinit },\n", + d->d_name, d->d_name); + } + + fprintf(fp, "\t{ NULL, NULL }\n};\n"); +} + +static void +emitloc(FILE *fp) +{ + int i; + + if (locators.used != 0) { + fprintf(fp, "\n/* locators */\n" + "static int loc[%d] = {", locators.used); + for (i = 0; i < locators.used; i++) + fprintf(fp, "%s%s,", SEP(i, 8), locators.vec[i]); + fprintf(fp, "\n};\n"); + } +} + +/* + * Emit static parent data. + */ +static void +emitparents(FILE *fp) +{ + struct pspec *p; + + NEWLINE; + TAILQ_FOREACH(p, &allpspecs, p_list) { + if (p->p_devs == NULL || p->p_active != DEVI_ACTIVE) + continue; + fprintf(fp, + "static const struct cfparent pspec%d = {\n", p->p_inst); + fprintf(fp, "\t\"%s\", ", p->p_iattr->a_name); + if (p->p_atdev != NULL) { + fprintf(fp, "\"%s\", ", p->p_atdev->d_name); + if (p->p_atunit == WILD) + fprintf(fp, "DVUNIT_ANY"); + else + fprintf(fp, "%d", p->p_atunit); + } else + fprintf(fp, "NULL, 0"); + fprintf(fp, "\n};\n"); + } +} + +/* + * Emit the cfdata array. + */ +static void +emitcfdata(FILE *fp) +{ + struct devi **p, *i; + struct pspec *ps; + int unit, v; + const char *state, *basename, *attachment; + struct loclist *ll; + struct attr *a; + const char *loc; + char locbuf[20]; + const char *lastname = ""; + + fprintf(fp, "\n" + "#define NORM FSTATE_NOTFOUND\n" + "#define STAR FSTATE_STAR\n" + "\n" + "%sstruct cfdata cfdata%s%s[] = {\n" + " /* driver attachment unit state " + " loc flags pspec */\n", + ioconfname ? "static " : "", + ioconfname ? "_ioconf_" : "", + ioconfname ? ioconfname : ""); + for (p = packed; (i = *p) != NULL; p++) { + /* the description */ + fprintf(fp, "/*%3d: %s at ", i->i_cfindex, i->i_name); + if ((ps = i->i_pspec) != NULL) { + if (ps->p_atdev != NULL && + ps->p_atunit != WILD) { + fprintf(fp, "%s%d", ps->p_atdev->d_name, + ps->p_atunit); + } else if (ps->p_atdev != NULL) { + fprintf(fp, "%s?", ps->p_atdev->d_name); + } else { + fprintf(fp, "%s?", ps->p_iattr->a_name); + } + + a = ps->p_iattr; + for (ll = a->a_locs, v = 0; ll != NULL; + ll = ll->ll_next, v++) { + if (ARRNAME(ll->ll_name, lastname)) { + fprintf(fp, " %s %s", + ll->ll_name, i->i_locs[v]); + } else { + fprintf(fp, " %s %s", + ll->ll_name, + i->i_locs[v]); + lastname = ll->ll_name; + } + } + } else { + a = NULL; + fputs("root", fp); + } + + fputs(" */\n", fp); + + /* then the actual defining line */ + basename = i->i_base->d_name; + attachment = i->i_atdeva->d_name; + if (i->i_unit == STAR) { + unit = i->i_base->d_umax; + state = "STAR"; + } else { + unit = i->i_unit; + state = "NORM"; + } + if (i->i_locoff >= 0) { + (void)snprintf(locbuf, sizeof(locbuf), "loc+%3d", + i->i_locoff); + loc = locbuf; + } else + loc = "NULL"; + fprintf(fp, " { \"%s\",%s\"%s\",%s%2d, %s, %7s, %#6x, ", + basename, strlen(basename) < 7 ? "\t\t" + : "\t", + attachment, strlen(attachment) < 5 ? "\t\t" + : "\t", + unit, state, loc, i->i_cfflags); + if (ps != NULL) + fprintf(fp, "&pspec%d },\n", ps->p_inst); + else + fputs("NULL },\n", fp); + } + fprintf(fp, " { %s,%s%s,%s%2d, %s, %7s, %#6x, %s }\n};\n", + "NULL", "\t\t", "NULL", "\t\t", 0, " 0", "NULL", 0, "NULL"); +} + +/* + * Emit the table of potential roots. + */ +static void +emitroots(FILE *fp) +{ + struct devi **p, *i; + + fputs("\nconst short cfroots[] = {\n", fp); + for (p = packed; (i = *p) != NULL; p++) { + if (i->i_at != NULL) + continue; + if (i->i_unit != 0 && + (i->i_unit != STAR || i->i_base->d_umax != 0)) + warnx("warning: `%s at root' is not unit 0", i->i_name); + fprintf(fp, "\t%2d /* %s */,\n", + i->i_cfindex, i->i_name); + } + fputs("\t-1\n};\n", fp); +} + +/* + * Emit pseudo-device initialization. + */ +static void +emitpseudo(FILE *fp) +{ + struct devi *i; + struct devbase *d; + + fputs("\n/* pseudo-devices */\n", fp); + TAILQ_FOREACH(i, &allpseudo, i_next) { + fprintf(fp, "void %sattach(int);\n", + i->i_base->d_name); + } + fputs("\nconst struct pdevinit pdevinit[] = {\n", fp); + TAILQ_FOREACH(i, &allpseudo, i_next) { + d = i->i_base; + fprintf(fp, "\t{ %sattach, %d },\n", + d->d_name, d->d_umax); + } + fputs("\t{ 0, 0 }\n};\n", fp); +} + +/* + * Emit name to major block number table. + */ +void +emitname2blk(FILE *fp) +{ + struct devbase *dev; + + fputs("\n/* device name to major block number */\n", fp); + + fprintf(fp, "struct devnametobdevmaj dev_name2blk[] = {\n"); + + TAILQ_FOREACH(dev, &allbases, d_next) { + if (dev->d_major == NODEVMAJOR) + continue; + + fprintf(fp, "\t{ \"%s\", %d },\n", + dev->d_name, dev->d_major); + } + fprintf(fp, "\t{ NULL, 0 }\n};\n"); +} diff --git a/buildrump.sh/src/usr.bin/config/mkmakefile.c b/buildrump.sh/src/usr.bin/config/mkmakefile.c new file mode 100644 index 00000000..8202f73e --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/mkmakefile.c @@ -0,0 +1,671 @@ +/* $NetBSD: mkmakefile.c,v 1.37 2014/12/15 15:49:25 uebayasi Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)mkmakefile.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: mkmakefile.c,v 1.37 2014/12/15 15:49:25 uebayasi Exp $"); + +#include <sys/param.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <err.h> +#include <util.h> +#include "defs.h" +#include "sem.h" + +/* + * Make the Makefile. + */ + +static const char *srcpath(struct files *); + +static const char *prefix_prologue(const char *); +static const char *filetype_prologue(struct filetype *); + + +static void emitdefs(FILE *); +static void emitfiles(FILE *, int, int); + +static void emitobjs(FILE *); +static void emitallkobjs(FILE *); +static int emitallkobjscb(const char *, void *, void *); +static void emitattrkobjs(FILE *); +static int emitattrkobjscb(const char *, void *, void *); +static void emitkobjs(FILE *); +static void emitcfiles(FILE *); +static void emitsfiles(FILE *); +static void emitrules(FILE *); +static void emitload(FILE *); +static void emitincludes(FILE *); +static void emitappmkoptions(FILE *); +static void emitsubs(FILE *, const char *, const char *, int); +static int selectopt(const char *, void *); + +int has_build_kernel; + +int +mkmakefile(void) +{ + FILE *ifp, *ofp; + int lineno; + void (*fn)(FILE *); + char *ifname; + char line[BUFSIZ], buf[200]; + + /* + * Check if conf/Makefile.kern.inc defines "build_kernel". + * + * (This is usually done by checking "version" in sys/conf/files; + * unfortunately the "build_kernel" change done around 2014 Aug didn't + * bump that version. Thus this hack.) + */ + (void)snprintf(buf, sizeof(buf), "conf/Makefile.kern.inc"); + ifname = sourcepath(buf); + if ((ifp = fopen(ifname, "r")) == NULL) { + warn("cannot read %s", ifname); + goto bad2; + } + while (fgets(line, sizeof(line), ifp) != NULL) { + if (strncmp(line, "build_kernel:", 13) == 0) { + has_build_kernel = 1; + break; + } + } + (void)fclose(ifp); + + /* + * Try a makefile for the port first. + */ + (void)snprintf(buf, sizeof(buf), "arch/%s/conf/Makefile.%s", + machine, machine); + ifname = sourcepath(buf); + if ((ifp = fopen(ifname, "r")) == NULL) { + /* + * Try a makefile for the architecture second. + */ + (void)snprintf(buf, sizeof(buf), "arch/%s/conf/Makefile.%s", + machinearch, machinearch); + free(ifname); + ifname = sourcepath(buf); + ifp = fopen(ifname, "r"); + } + if (ifp == NULL) { + warn("cannot read %s", ifname); + goto bad2; + } + + if ((ofp = fopen("Makefile.tmp", "w")) == NULL) { + warn("cannot write Makefile"); + goto bad1; + } + + emitdefs(ofp); + + lineno = 0; + while (fgets(line, sizeof(line), ifp) != NULL) { + lineno++; + if ((version < 20090214 && line[0] != '%') || line[0] == '#') { + fputs(line, ofp); + continue; + } + if (strcmp(line, "%OBJS\n") == 0) + fn = Mflag ? emitkobjs : emitobjs; + else if (strcmp(line, "%CFILES\n") == 0) + fn = emitcfiles; + else if (strcmp(line, "%SFILES\n") == 0) + fn = emitsfiles; + else if (strcmp(line, "%RULES\n") == 0) + fn = emitrules; + else if (strcmp(line, "%LOAD\n") == 0) + fn = emitload; + else if (strcmp(line, "%INCLUDES\n") == 0) + fn = emitincludes; + else if (strcmp(line, "%MAKEOPTIONSAPPEND\n") == 0) + fn = emitappmkoptions; + else if (strncmp(line, "%VERSION ", sizeof("%VERSION ")-1) == 0) { + int newvers; + if (sscanf(line, "%%VERSION %d\n", &newvers) != 1) { + cfgxerror(ifname, lineno, "syntax error for " + "%%VERSION"); + } else + setversion(newvers); + continue; + } else { + if (version < 20090214) + cfgxerror(ifname, lineno, + "unknown %% construct ignored: %s", line); + else + emitsubs(ofp, line, ifname, lineno); + continue; + } + (*fn)(ofp); + } + + fflush(ofp); + if (ferror(ofp)) + goto wrerror; + + if (ferror(ifp)) { + warn("error reading %s (at line %d)", ifname, lineno); + goto bad; + } + + if (fclose(ofp)) { + ofp = NULL; + goto wrerror; + } + (void)fclose(ifp); + + if (moveifchanged("Makefile.tmp", "Makefile") != 0) { + warn("error renaming Makefile"); + goto bad2; + } + free(ifname); + return (0); + + wrerror: + warn("error writing Makefile"); + bad: + if (ofp != NULL) + (void)fclose(ofp); + bad1: + (void)fclose(ifp); + /* (void)unlink("Makefile.tmp"); */ + bad2: + free(ifname); + return (1); +} + +static void +emitsubs(FILE *fp, const char *line, const char *file, int lineno) +{ + char *nextpct; + const char *optname; + struct nvlist *option; + + while (*line != '\0') { + if (*line != '%') { + fputc(*line++, fp); + continue; + } + + line++; + nextpct = strchr(line, '%'); + if (nextpct == NULL) { + cfgxerror(file, lineno, "unbalanced %% or " + "unknown construct"); + return; + } + *nextpct = '\0'; + + if (*line == '\0') + fputc('%', fp); + else { + optname = intern(line); + if (!DEFINED_OPTION(optname)) { + cfgxerror(file, lineno, "unknown option %s", + optname); + return; + } + + if ((option = ht_lookup(opttab, optname)) == NULL) + option = ht_lookup(fsopttab, optname); + if (option != NULL) + fputs(option->nv_str ? option->nv_str : "1", + fp); + /* + * Otherwise it's not a selected option and we don't + * output anything. + */ + } + + line = nextpct + 1; + } +} + +/* + * Return (possibly in a static buffer) the name of the `source' for a + * file. If we have `options source', or if the file is marked `always + * source', this is always the path from the `file' line; otherwise we + * get the .o from the obj-directory. + */ +static const char * +srcpath(struct files *fi) +{ +#if 1 + /* Always have source, don't support object dirs for kernel builds. */ + return (fi->fi_path); +#else + static char buf[MAXPATHLEN]; + + if (have_source || (fi->fi_flags & FI_ALWAYSSRC) != 0) + return (fi->fi_path); + if (objpath == NULL) { + cfgerror("obj-directory not set"); + return (NULL); + } + (void)snprintf(buf, sizeof buf, "%s/%s.o", objpath, fi->fi_base); + return (buf); +#endif +} + +static const char * +filetype_prologue(struct filetype *fit) +{ + + return (*fit->fit_path == '/') ? "" : "$S/"; +} + +static const char * +prefix_prologue(const char *path) +{ + + return (*path == '/') ? "" : "$S/"; +} + +static void +emitdefs(FILE *fp) +{ + struct nvlist *nv; + + fprintf(fp, "KERNEL_BUILD=%s\n", conffile); + fputs("IDENT= \\\n", fp); + for (nv = options; nv != NULL; nv = nv->nv_next) { + + /* Skip any options output to a header file */ + if (DEFINED_OPTION(nv->nv_name)) + continue; + const char *s = nv->nv_str; + fprintf(fp, "\t-D%s%s%s%s \\\n", nv->nv_name, + s ? "=\"" : "", + s ? s : "", + s ? "\"" : ""); + } + putc('\n', fp); + fprintf(fp, "PARAM=-DMAXUSERS=%d\n", maxusers); + fprintf(fp, "MACHINE=%s\n", machine); + + const char *subdir = ""; + if (*srcdir != '/' && *srcdir != '.') { + /* + * libkern and libcompat "Makefile.inc"s want relative S + * specification to begin with '.'. + */ + subdir = "./"; + } + fprintf(fp, "S=\t%s%s\n", subdir, srcdir); + for (nv = mkoptions; nv != NULL; nv = nv->nv_next) + fprintf(fp, "%s=%s\n", nv->nv_name, nv->nv_str); +} + +static void +emitobjs(FILE *fp) +{ + struct files *fi; + struct objects *oi; + + fputs("OBJS= \\\n", fp); + TAILQ_FOREACH(fi, &allfiles, fi_next) { + if ((fi->fi_flags & FI_SEL) == 0) + continue; + fprintf(fp, "\t%s.o \\\n", fi->fi_base); + } + TAILQ_FOREACH(oi, &allobjects, oi_next) { + const char *prologue, *prefix, *sep; + + if ((oi->oi_flags & OI_SEL) == 0) + continue; + prologue = prefix = sep = ""; + if (*oi->oi_path != '/') { + if (oi->oi_prefix != NULL) { + prologue = prefix_prologue(oi->oi_path); + prefix = oi->oi_prefix; + sep = "/"; + } else { + prologue = filetype_prologue(&oi->oi_fit); + } + } + fprintf(fp, "\t%s%s%s%s \\\n", prologue, prefix, sep, + oi->oi_path); + } + putc('\n', fp); +} + +static void +emitkobjs(FILE *fp) +{ + emitallkobjs(fp); + emitattrkobjs(fp); +} + +static int emitallkobjsweighcb(const char *name, void *v, void *arg); +static void weighattr(struct attr *a); +static int attrcmp(const void *l, const void *r); + +struct attr **attrbuf; +size_t attridx; + +static void +emitallkobjs(FILE *fp) +{ + size_t i; + + attrbuf = emalloc(nattrs * sizeof(*attrbuf)); + + ht_enumerate(attrtab, emitallkobjsweighcb, NULL); + ht_enumerate(attrtab, emitallkobjscb, NULL); + qsort(attrbuf, attridx, sizeof(struct attr *), attrcmp); + + fputs("OBJS= \\\n", fp); + for (i = 0; i < attridx; i++) + fprintf(fp, "\t%s.ko \\\n", attrbuf[i]->a_name); + putc('\n', fp); + + free(attrbuf); +} + +static int +emitallkobjscb(const char *name, void *v, void *arg) +{ + struct attr *a = v; + + if (ht_lookup(selecttab, name) == NULL) + return 0; + if (TAILQ_EMPTY(&a->a_files)) + return 0; + attrbuf[attridx++] = a; + /* XXX nattrs tracking is not exact yet */ + if (attridx == nattrs) { + nattrs *= 2; + attrbuf = erealloc(attrbuf, nattrs * sizeof(*attrbuf)); + } + return 0; +} + +static int +emitallkobjsweighcb(const char *name, void *v, void *arg) +{ + struct attr *a = v; + + weighattr(a); + return 0; +} + +static void +weighattr(struct attr *a) +{ + struct attrlist *al; + + for (al = a->a_deps; al != NULL; al = al->al_next) { + weighattr(al->al_this); + } + a->a_weight++; +} + +static int +attrcmp(const void *l, const void *r) +{ + const struct attr * const *a = l, * const *b = r; + const int wa = (*a)->a_weight, wb = (*b)->a_weight; + return (wa > wb) ? -1 : (wa < wb) ? 1 : 0; +} + +static void +emitattrkobjs(FILE *fp) +{ + extern struct hashtab *attrtab; + + ht_enumerate(attrtab, emitattrkobjscb, fp); +} + +static int +emitattrkobjscb(const char *name, void *v, void *arg) +{ + struct attr *a = v; + struct files *fi; + FILE *fp = arg; + + if (ht_lookup(selecttab, name) == NULL) + return 0; + if (TAILQ_EMPTY(&a->a_files)) + return 0; + fputc('\n', fp); + fprintf(fp, "# %s (%d)\n", name, a->a_weight); + fprintf(fp, "OBJS.%s= \\\n", name); + TAILQ_FOREACH(fi, &a->a_files, fi_anext) { + fprintf(fp, "\t%s.o \\\n", fi->fi_base); + } + fputc('\n', fp); + fprintf(fp, "%s.ko: ${OBJS.%s}\n", name, name); + fprintf(fp, "\t${LINK_O}\n"); + return 0; +} + +static void +emitcfiles(FILE *fp) +{ + + emitfiles(fp, 'c', 0); +} + +static void +emitsfiles(FILE *fp) +{ + + emitfiles(fp, 's', 'S'); +} + +static void +emitfiles(FILE *fp, int suffix, int upper_suffix) +{ + struct files *fi; + const char *fpath; + struct config *cf; + char swapname[100]; + + fprintf(fp, "%cFILES= \\\n", toupper(suffix)); + TAILQ_FOREACH(fi, &allfiles, fi_next) { + const char *prologue, *prefix, *sep; + + if ((fi->fi_flags & FI_SEL) == 0) + continue; + fpath = srcpath(fi); + if (fi->fi_suffix != suffix && fi->fi_suffix != upper_suffix) + continue; + prologue = prefix = sep = ""; + if (*fi->fi_path != '/') { + if (fi->fi_prefix != NULL) { + prologue = prefix_prologue(fi->fi_prefix); + prefix = fi->fi_prefix; + sep = "/"; + } else { + prologue = filetype_prologue(&fi->fi_fit); + } + } + fprintf(fp, "\t%s%s%s%s \\\n", + prologue, prefix, sep, fpath); + } + /* + * The allfiles list does not include the configuration-specific + * C source files. These files should be eliminated someday, but + * for now, we have to add them to ${CFILES} (and only ${CFILES}). + */ + if (suffix == 'c') { + TAILQ_FOREACH(cf, &allcf, cf_next) { + (void)snprintf(swapname, sizeof(swapname), "swap%s.c", + cf->cf_name); + fprintf(fp, "\t%s \\\n", swapname); + } + } + putc('\n', fp); +} + +/* + * Emit the make-rules. + */ +static void +emitrules(FILE *fp) +{ + struct files *fi; + const char *fpath; + + TAILQ_FOREACH(fi, &allfiles, fi_next) { + const char *prologue, *prefix, *sep; + + if ((fi->fi_flags & FI_SEL) == 0) + continue; + fpath = srcpath(fi); + prologue = prefix = sep = ""; + if (*fpath != '/') { + if (fi->fi_prefix != NULL) { + prologue = prefix_prologue(fi->fi_prefix); + prefix = fi->fi_prefix; + sep = "/"; + } else { + prologue = filetype_prologue(&fi->fi_fit); + } + } + fprintf(fp, "%s.o: %s%s%s%s\n", fi->fi_base, + prologue, prefix, sep, fpath); + if (fi->fi_mkrule != NULL) { + fprintf(fp, "\t%s\n\n", fi->fi_mkrule); + } else { + fprintf(fp, "\t${NORMAL_%c}\n\n", toupper(fi->fi_suffix)); + } + } +} + +/* + * Emit the load commands. + * + * This function is not to be called `spurt'. + */ +static void +emitload(FILE *fp) +{ + struct config *cf; + + fputs(".MAIN: all\n", fp); + fputs("all:", fp); + TAILQ_FOREACH(cf, &allcf, cf_next) { + fprintf(fp, " %s", cf->cf_name); + /* + * If we generate multiple configs inside the same build directory + * with a parallel build, strange things may happen, so sequentialize + * them. + */ + if (cf != TAILQ_LAST(&allcf,conftq)) + fprintf(fp, " .WAIT"); + } + fputs("\n\n", fp); + /* + * Generate the backward-compatible "build_kernel" rule if + * sys/conf/Makefile.kern.inc doesn't define any (pre-2014 Aug). + */ + if (has_build_kernel == 0) { + fprintf(fp, "build_kernel: .USE\n" + "\t${SYSTEM_LD_HEAD}\n" + "\t${SYSTEM_LD} swap${.TARGET}.o\n" + "\t${SYSTEM_LD_TAIL}\n" + "\n"); + } + /* + * Generate per-kernel rules. + */ + TAILQ_FOREACH(cf, &allcf, cf_next) { + fprintf(fp, "KERNELS+=%s\n", cf->cf_name); + fprintf(fp, "%s: ${SYSTEM_DEP} swap%s.o vers.o build_kernel\n", + cf->cf_name, cf->cf_name); + fprintf(fp, "swap%s.o: swap%s.c\n" + "\t${NORMAL_C}\n\n", cf->cf_name, cf->cf_name); + } + fputs("\n", fp); +} + +/* + * Emit include headers (for any prefixes encountered) + */ +static void +emitincludes(FILE *fp) +{ + struct prefix *pf; + + SLIST_FOREACH(pf, &allprefixes, pf_next) { + fprintf(fp, "EXTRA_INCLUDES+=\t-I%s%s\n", + prefix_prologue(pf->pf_prefix), pf->pf_prefix); + } +} + +/* + * Emit appending makeoptions. + */ +static void +emitappmkoptions(FILE *fp) +{ + struct nvlist *nv; + struct condexpr *cond; + + for (nv = appmkoptions; nv != NULL; nv = nv->nv_next) + fprintf(fp, "%s+=%s\n", nv->nv_name, nv->nv_str); + + for (nv = condmkoptions; nv != NULL; nv = nv->nv_next) { + cond = nv->nv_ptr; + if (expr_eval(cond, selectopt, NULL)) + fprintf(fp, "%s+=%s\n", nv->nv_name, nv->nv_str); + condexpr_destroy(cond); + nv->nv_ptr = NULL; + } +} + +static int +/*ARGSUSED*/ +selectopt(const char *name, void *context) +{ + + return (ht_lookup(selecttab, strtolower(name)) != NULL); +} diff --git a/buildrump.sh/src/usr.bin/config/mkswap.c b/buildrump.sh/src/usr.bin/config/mkswap.c new file mode 100644 index 00000000..cbd6abfe --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/mkswap.c @@ -0,0 +1,164 @@ +/* $NetBSD: mkswap.c,v 1.8 2014/10/29 17:14:50 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)mkswap.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: mkswap.c,v 1.8 2014/10/29 17:14:50 christos Exp $"); + +#include <sys/param.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <err.h> +#include "defs.h" +#include "sem.h" + +static char *mkdevstr(dev_t); +static int mkoneswap(struct config *); + +/* + * Make the various swap*.c files. Nothing to do for generic swap. + */ +int +mkswap(void) +{ + struct config *cf; + + TAILQ_FOREACH(cf, &allcf, cf_next) { + if (mkoneswap(cf)) + return (1); + } + return (0); +} + +static char * +mkdevstr(dev_t d) +{ + static char buf[32]; + + if (d == NODEV) + (void)snprintf(buf, sizeof(buf), "NODEV"); + else + (void)snprintf(buf, sizeof(buf), "makedev(%d, %d)", + major(d), minor(d)); + return buf; +} + +static int +mkoneswap(struct config *cf) +{ + struct nvlist *nv; + FILE *fp; + char fname[200], tname[200]; + char specinfo[200]; + + (void)snprintf(fname, sizeof(fname), "swap%s.c", cf->cf_name); + (void)snprintf(tname, sizeof(tname), "swap%s.c.tmp", cf->cf_name); + if ((fp = fopen(tname, "w")) == NULL) { + warn("cannot open %s", fname); + return (1); + } + + autogen_comment(fp, fname); + + fputs("#include <sys/param.h>\n" + "#include <sys/conf.h>\n\n", fp); + /* + * Emit the root device. + */ + nv = cf->cf_root; + if (cf->cf_root->nv_str == s_qmark) + strlcpy(specinfo, "NULL", sizeof(specinfo)); + else + snprintf(specinfo, sizeof(specinfo), "\"%s\"", + cf->cf_root->nv_str); + fprintf(fp, "const char *rootspec = %s;\n", specinfo); + fprintf(fp, "dev_t\trootdev = %s;\t/* %s */\n\n", + mkdevstr((dev_t)nv->nv_num), + nv->nv_str == s_qmark ? "wildcarded" : nv->nv_str); + + /* + * Emit the dump device. + */ + nv = cf->cf_dump; + if (cf->cf_dump == NULL) + strlcpy(specinfo, "NULL", sizeof(specinfo)); + else + snprintf(specinfo, sizeof(specinfo), "\"%s\"", cf->cf_dump->nv_str); + fprintf(fp, "const char *dumpspec = %s;\n", specinfo); + fprintf(fp, "dev_t\tdumpdev = %s;\t/* %s */\n\n", + nv ? mkdevstr((dev_t)nv->nv_num) : "NODEV", + nv ? nv->nv_str : "unspecified"); + + /* + * Emit the root file system. + */ + fprintf(fp, "const char *rootfstype = \"%s\";\n", + cf->cf_fstype ? cf->cf_fstype : "?"); + + fflush(fp); + if (ferror(fp)) + goto wrerror; + + if (fclose(fp)) { + fp = NULL; + goto wrerror; + } + if (moveifchanged(tname, fname) != 0) { + warn("error renaming %s", fname); + return (1); + } + return (0); + + wrerror: + warn("error writing %s", fname); + if (fp != NULL) + (void)fclose(fp); +#if 0 + (void)unlink(fname); +#endif + return (1); +} diff --git a/buildrump.sh/src/usr.bin/config/pack.c b/buildrump.sh/src/usr.bin/config/pack.c new file mode 100644 index 00000000..9976488b --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/pack.c @@ -0,0 +1,352 @@ +/* $NetBSD: pack.c,v 1.9 2014/10/29 17:14:50 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)pack.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: pack.c,v 1.9 2014/10/29 17:14:50 christos Exp $"); + +#include <sys/param.h> +#include <stdlib.h> +#include <string.h> +#include <util.h> +#include "defs.h" + +/* + * Packing. We have three separate kinds of packing here. + * + * First, we pack device instances which have identical parent specs. + * + * Second, we pack locators. Given something like + * + * hp0 at mba0 drive 0 + * hp* at mba* drive ? + * ht0 at mba0 drive 0 + * tu0 at ht0 slave 0 + * ht* at mba* drive ? + * tu* at ht* slave ? + * + * (where the default drive and slave numbers are -1), we have three + * locators whose value is 0 and three whose value is -1. Rather than + * emitting six integers, we emit just two. + * + * When packing locators, we would like to find sequences such as + * {1 2 3} {2 3 4} {3} {4 5} + * and turn this into the flat sequence {1 2 3 4 5}, with each subsequence + * given by the appropriate offset (here 0, 1, 2, and 3 respectively). + * Non-overlapping packing is much easier, and so we use that here + * and miss out on the chance to squeeze the locator sequence optimally. + * (So it goes.) + */ + +typedef int (*vec_cmp_func)(const void *, int, int); + +#define TAILHSIZE 128 +#define PVHASH(i) ((i) & (TAILHSIZE - 1)) +#define LOCHASH(l) (((long)(l) >> 2) & (TAILHSIZE - 1)) +struct tails { + struct tails *t_next; + int t_ends_at; +}; + +static struct tails *tails[TAILHSIZE]; +static int locspace; + +static void packdevi(void); +static void packlocs(void); + +static int sameas(struct devi *, struct devi *); +static int findvec(const void *, int, int, vec_cmp_func, int); +static int samelocs(const void *, int, int); +static int addlocs(const char **, int); +static int loclencmp(const void *, const void *); +static void resettails(void); + +void +pack(void) +{ + struct pspec *p; + struct devi *i; + + /* Pack instances and make parent vectors. */ + packdevi(); + + /* + * Now that we know what we have, find upper limits on space + * needed for the loc[] table. The loc table size is bounded by + * what we would get if no packing occurred. + */ + locspace = 0; + TAILQ_FOREACH(i, &alldevi, i_next) { + if (!i->i_active == DEVI_ACTIVE || i->i_collapsed) + continue; + if ((p = i->i_pspec) == NULL) + continue; + locspace += p->p_iattr->a_loclen; + } + + /* Allocate and pack loc[]. */ + locators.vec = ecalloc((size_t)locspace, sizeof(*locators.vec)); + locators.used = 0; + packlocs(); +} + +/* + * Pack device instances together wherever possible. + */ +static void +packdevi(void) +{ + struct devi *firststar, *i, **ip, *l, *p; + struct devbase *d; + u_short j, m, n; + + /* + * Sort all the cloning units to after the non-cloning units, + * preserving order of cloning and non-cloning units with + * respect to other units of the same type. + * + * Algorithm: Walk down the list until the first cloning unit is + * seen for the second time (or until the end of the list, if there + * are no cloning units on the list), moving starred units to the + * end of the list. + */ + TAILQ_FOREACH(d, &allbases, d_next) { + ip = &d->d_ihead; + firststar = NULL; + + for (i = *ip; i != firststar; i = *ip) { + if (i->i_unit != STAR) { + /* try i->i_bsame next */ + ip = &i->i_bsame; + } else { + if (firststar == NULL) + firststar = i; + + *d->d_ipp = i; + d->d_ipp = &i->i_bsame; + + *ip = i->i_bsame; + i->i_bsame = NULL; + + /* leave ip alone; try (old) i->i_bsame next */ + } + } + } + + packed = ecalloc((size_t)ndevi + 1, sizeof *packed); + n = 0; + TAILQ_FOREACH(d, &allbases, d_next) { + /* + * For each instance of each device, add or collapse + * all its aliases. + * + * Pseudo-devices have a non-empty d_ihead for convenience. + * Ignore them. + */ + if (d->d_ispseudo) + continue; + for (i = d->d_ihead; i != NULL; i = i->i_bsame) { + m = n; + for (l = i; l != NULL; l = l->i_alias) { + if (l->i_active != DEVI_ACTIVE + || i->i_pseudoroot) + continue; + l->i_locoff = -1; + /* try to find an equivalent for l */ + for (j = m; j < n; j++) { + p = packed[j]; + if (sameas(l, p)) { + l->i_collapsed = 1; + l->i_cfindex = p->i_cfindex; + goto nextalias; + } + } + /* could not find a suitable alias */ + l->i_collapsed = 0; + l->i_cfindex = n; + packed[n++] = l; + nextalias:; + } + } + } + npacked = n; + packed[n] = NULL; +} + +/* + * Return true if two aliases are "the same". In this case, they need + * to have the same parent spec, have the same config flags, and have + * the same locators. + */ +static int +sameas(struct devi *i1, struct devi *i2) +{ + const char **p1, **p2; + + if (i1->i_pspec != i2->i_pspec) + return (0); + if (i1->i_cfflags != i2->i_cfflags) + return (0); + for (p1 = i1->i_locs, p2 = i2->i_locs; *p1 == *p2; p2++) + if (*p1++ == 0) + return (1); + return (0); +} + +static void +packlocs(void) +{ + struct pspec *ps; + struct devi **p, *i; + int l,o; + extern int Pflag; + + qsort(packed, npacked, sizeof *packed, loclencmp); + for (p = packed; (i = *p) != NULL; p++) { + if ((ps = i->i_pspec) != NULL && + (l = ps->p_iattr->a_loclen) > 0) { + if (Pflag) { + o = findvec(i->i_locs, + LOCHASH(i->i_locs[l - 1]), l, + samelocs, locators.used); + i->i_locoff = o < 0 ? + addlocs(i->i_locs, l) : o; + } else + i->i_locoff = addlocs(i->i_locs, l); + } else + i->i_locoff = -1; + } + resettails(); +} + +/* + * Return the index at which the given vector already exists, or -1 + * if it is not anywhere in the current set. If we return -1, we assume + * our caller will add it at the end of the current set, and we make + * sure that next time, we will find it there. + */ +static int +findvec(const void *ptr, int hash, int len, vec_cmp_func cmp, int nextplace) +{ + struct tails *t, **hp; + int off; + + hp = &tails[hash]; + for (t = *hp; t != NULL; t = t->t_next) { + off = t->t_ends_at - len; + if (off >= 0 && (*cmp)(ptr, off, len)) + return (off); + } + t = ecalloc(1, sizeof(*t)); + t->t_next = *hp; + *hp = t; + t->t_ends_at = nextplace + len; + return (-1); +} + +/* + * Comparison function for locators. + */ +static int +samelocs(const void *ptr, int off, int len) +{ + const char * const *p, * const *q; + + for (p = &locators.vec[off], q = (const char * const *)ptr; --len >= 0;) + if (*p++ != *q++) + return (0); /* different */ + return (1); /* same */ +} + +/* + * Add the given locators at the end of the global loc[] table. + */ +static int +addlocs(const char **locs, int len) +{ + const char **p; + int ret; + + ret = locators.used; + if ((locators.used = ret + len) > locspace) + panic("addlocs: overrun"); + for (p = &locators.vec[ret]; --len >= 0;) + *p++ = *locs++; + return (ret); +} + +/* + * Comparison function for qsort-by-locator-length, longest first. + * We rashly assume that subtraction of these lengths does not overflow. + */ +static int +loclencmp(const void *a, const void *b) +{ + const struct pspec *p1, *p2; + int l1, l2; + + p1 = (*(const struct devi * const *)a)->i_pspec; + l1 = p1 != NULL ? p1->p_iattr->a_loclen : 0; + + p2 = (*(const struct devi * const *)b)->i_pspec; + l2 = p2 != NULL ? p2->p_iattr->a_loclen : 0; + + return (l2 - l1); +} + +static void +resettails(void) +{ + struct tails **p, *t, *next; + int i; + + for (p = tails, i = TAILHSIZE; --i >= 0; p++) { + for (t = *p; t != NULL; t = next) { + next = t->t_next; + free(t); + } + *p = NULL; + } +} diff --git a/buildrump.sh/src/usr.bin/config/scan.l b/buildrump.sh/src/usr.bin/config/scan.l new file mode 100644 index 00000000..2461ce78 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/scan.l @@ -0,0 +1,632 @@ +%{ +/* $NetBSD: scan.l,v 1.22 2014/11/07 17:50:14 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)scan.l 8.1 (Berkeley) 6/6/93 + */ + +#include <sys/cdefs.h> +__RCSID("$NetBSD: scan.l,v 1.22 2014/11/07 17:50:14 christos Exp $"); + +#include <sys/param.h> +#include <errno.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stddef.h> +#include <ctype.h> +#include <util.h> +#undef ECHO +#include "defs.h" +#include "gram.h" + +int yyline; +const char *yyfile; +const char *lastfile; +char curinclpath[PATH_MAX]; +int ifdefstate = -1; +int st; +#define IDS_PARENT_DISABLED \ + ((ifdefstate > 6) && ((((ifdefstate/6)-1) & 1) == 1)) +#define IDS_MAX_DEPTH 362797056 /* 6^11 */ +/* States for ifdefstate: + + 0 -> matched ifdef + 1 -> unmatched ifdef + 2 -> matched elifdef + 3 -> unmatched elifdef + 4 -> matched else + 5 -> unmatched else + + Upon "ifdef", add one and multiply by 6. + Upon "endif", divide by 6, remove 1. + + ifdef -> MATCH => continue + MISMATCH => set to 1 + elifdef -> if (!1) -> MISMATCH + MATCH => set to 2 + MISMATCH => if (2 || 3) set to 3, else set to 1 + else -> if (1) -> MATCH + MATCH => set to 4 + MISMATCH => set to 5 + + in each case, if parent & 1 == 1, MISMATCH +*/ + +/* + * Data for returning to previous files from include files. + */ +struct incl { + struct incl *in_prev; /* previous includes in effect, if any */ + YY_BUFFER_STATE in_buf; /* previous lex state */ + const char *in_fname; /* previous file name */ + int in_lineno; /* previous line number */ + int in_ateof; /* token to insert at EOF */ + int in_interesting; /* previous value for "interesting" */ + int in_ifdefstate; /* conditional level */ +}; +static struct incl *incl; +static int endinclude(void); +static int getincludepath(void); +static int getcurifdef(void); + + +%} + +%option noyywrap nounput noinput + +PATH [A-Za-z_0-9]*[./][-A-Za-z_0-9./]* +QCHARS \"(\\.|[^\\"])*\" +WORD [A-Za-z_][-A-Za-z_0-9]* +FILENAME ({PATH}|{QCHARS}) +RESTOFLINE [ \t]*(#[^\n]*)?\n + +%x IGNORED + +%% + /* Local variables for yylex() */ + int tok; + +and return AND; +at return AT; +attach return ATTACH; +block return BLOCK; +build return BUILD; +char return CHAR; +compile-with return COMPILE_WITH; +config return CONFIG; +deffs return DEFFS; +define return DEFINE; +defflag return DEFFLAG; +defopt return DEFOPT; +defparam return DEFPARAM; +defpseudo return DEFPSEUDO; +defpseudodev return DEFPSEUDODEV; +devclass return DEVCLASS; +device return DEVICE; +device-major return DEVICE_MAJOR; +dumps return DUMPS; +file return XFILE; +file-system return FILE_SYSTEM; +flags return FLAGS; +ident return IDENT; +ioconf return IOCONF; +linkzero return LINKZERO; +machine return XMACHINE; +major return MAJOR; +makeoptions return MAKEOPTIONS; +maxpartitions return MAXPARTITIONS; +maxusers return MAXUSERS; +minor return MINOR; +needs-count return NEEDS_COUNT; +needs-flag return NEEDS_FLAG; +no return NO; +object return XOBJECT; +obsolete return OBSOLETE; +on return ON; +options return OPTIONS; +prefix return PREFIX; +pseudo-device return PSEUDO_DEVICE; +pseudo-root return PSEUDO_ROOT; +root return ROOT; +select return SELECT; +single return SINGLE; +source return SOURCE; +type return TYPE; +vector return VECTOR; +version return VERSION; +with return WITH; + +\+= return PLUSEQ; +:= return COLONEQ; + +<*>ifdef[ \t]+{WORD}{RESTOFLINE} { + ifdefstate = (ifdefstate + 1) * 6; + if (ifdefstate >= IDS_MAX_DEPTH) { + yyerror("too many levels of conditional"); + } + if (!IDS_PARENT_DISABLED && getcurifdef()) { + BEGIN(INITIAL); + } else { + ifdefstate++; + BEGIN(IGNORED); + } + yyline++; + } + +<*>ifndef[ \t]+{WORD}{RESTOFLINE} { + ifdefstate = (ifdefstate + 1) * 6; + if (ifdefstate >= IDS_MAX_DEPTH) { + yyerror("too many levels of conditional"); + } + if (!IDS_PARENT_DISABLED && !getcurifdef()) { + BEGIN(INITIAL); + } else { + ifdefstate++; + BEGIN(IGNORED); + } + yyline++; + } + + +<*>elifdef[ \t]+{WORD}{RESTOFLINE} { + st = ifdefstate % 6; + if (ifdefstate < 0 || st > 3) { + yyerror("mismatched elifdef"); + } + if (IDS_PARENT_DISABLED || + st != 1 || !getcurifdef()) { + if (st == 2 || st == 3) { + ifdefstate += 3 - st; + } else { + ifdefstate += 1 - st; + } + BEGIN(IGNORED); + } else { + ifdefstate++; + BEGIN(INITIAL); + } + yyline++; + } + +<*>elifndef[ \t]+{WORD}{RESTOFLINE} { + st = ifdefstate % 6; + if (ifdefstate < 0 || st > 3) { + yyerror("mismatched elifndef"); + } + if (IDS_PARENT_DISABLED || + st != 1 || getcurifdef()) { + if (st == 2 || st == 3) { + ifdefstate += 3 - st; + } else { + ifdefstate += 1 - st; + } + BEGIN(IGNORED); + } else { + ifdefstate++; + BEGIN(INITIAL); + } + yyline++; + } + +<*>else{RESTOFLINE} { + st = ifdefstate % 6; + if (ifdefstate < 0 || st > 3) { + yyerror("mismatched else"); + } + if (!IDS_PARENT_DISABLED && (st == 1)) { + ifdefstate += 3; + BEGIN(INITIAL); + } else { + ifdefstate += 5 - st; + BEGIN(IGNORED); + } + yyline++; + } + +<*>endif{RESTOFLINE} { + if (ifdefstate < 0) { + yyerror("mismatched endif"); + } + if (!IDS_PARENT_DISABLED) { + BEGIN(INITIAL); + } + ifdefstate = (ifdefstate/6) - 1; + yyline++; + } + +<IGNORED>\n { + yyline++; + } + +<IGNORED>. /* ignore */ + +include[ \t]+{FILENAME}{RESTOFLINE} { + yyline++; + if (getincludepath()) { + include(curinclpath, 0, 0, 1); + } else { + yyerror("bad include path-name"); + } + } + +cinclude[ \t]+{FILENAME}{RESTOFLINE} { + yyline++; + if (getincludepath()) { + include(curinclpath, 0, 1, 1); + } else { + yyerror("bad cinclude path-name"); + } + } + +package[ \t]+{FILENAME}{RESTOFLINE} { + yyline++; + if (!oktopackage) { + yyerror("package not allowed here"); + } else if (getincludepath()) { + package(curinclpath); + } else { + yyerror("bad package path-name"); + } + } + +{PATH} { + yylval.str = intern(yytext); + return PATHNAME; + } + +{WORD} { + yylval.str = intern(yytext); + return WORD; + } + +\"\" { + yylval.str = intern(""); + return EMPTYSTRING; + } + +{QCHARS} { + size_t l = strlen(yytext); + if (l > 1 && yytext[l - 1] == '"') + yytext[l - 1] = '\0'; + + yylval.str = intern(yytext + 1); + return QSTRING; + } +0[0-7]* { + yylval.num.fmt = 8; + yylval.num.val = strtoll(yytext, NULL, 8); + return NUMBER; + } +0[xX][0-9a-fA-F]+ { + yylval.num.fmt = 16; + yylval.num.val = (long long)strtoull(yytext + 2, NULL, 16); + return NUMBER; + } +[1-9][0-9]* { + yylval.num.fmt = 10; + yylval.num.val = strtoll(yytext, NULL, 10); + return NUMBER; + } +\n[ \t] { + /* + * Note: newline followed by whitespace is always a + * continuation of the previous line, so do NOT + * return a token in this case. + */ + yyline++; + } +\n { + yyline++; + return '\n'; + } +\00 { + /* Detect NUL characters in the config file and + * error out. + */ + cfgerror("NUL character detected at line %i\n", yyline); + } +#.* { /* ignored (comment) */; } +[ \t]+ { /* ignored (white space) */; } +. { return yytext[0]; } +<*><<EOF>> { + if (ifdefstate > (incl == NULL ? -1 : incl->in_ifdefstate)) { + yyerror("reached EOF while looking for endif"); + } + if (incl == NULL) + return YY_NULL; + tok = endinclude(); + if (tok) + return tok; + /* otherwise continue scanning */ + } + +%% + +int interesting = 1; + +static int +curdir_push(const char *fname) +{ + struct prefix *pf; + char *p, *d, *f; + + /* Set up the initial "current directory" for include directives. */ + d = dirname(f = estrdup(fname)); + if (*d == '/') + p = estrdup(d); + else { + char *cwd, buf[PATH_MAX]; + + if ((cwd = getcwd(buf, sizeof(buf))) == NULL) { + free(f); + return (-1); + } + easprintf(&p, "%s/%s", cwd, d); + } + free(f); + pf = ecalloc(1, sizeof(*pf)); + pf->pf_prefix = p; + SLIST_INSERT_HEAD(&curdirs, pf, pf_next); + + return (0); +} + +static void +curdir_pop(void) +{ + struct prefix *pf; + + pf = SLIST_FIRST(&curdirs); + SLIST_REMOVE_HEAD(&curdirs, pf_next); + if (SLIST_EMPTY(&curdirs)) + panic("curdirs is empty"); + /* LINTED cast away const (pf_prefix is malloc'd for curdirs) */ + free((void *)__UNCONST(pf->pf_prefix)); + free(pf); +} + +/* + * Open the "main" file (conffile). + */ +int +firstfile(const char *fname) +{ + +#if defined(__NetBSD__) + if ((yyin = fopen(fname, "rf")) == NULL) +#else + if ((yyin = fopen(fname, "r")) == NULL) +#endif + return (-1); + + if (curdir_push(fname) == -1) + return (-1); + + yyfile = conffile = fname; + yyline = 1; + return (0); +} + +/* + * Add a "package" to the configuration. This is essentially + * syntactic sugar around the sequence: + * + * prefix ../some/directory + * include "files.package" + * prefix + */ +void +package(const char *fname) +{ + char *fname1 = estrdup(fname); + char *fname2 = estrdup(fname); + char *dir = dirname(fname1); + char *file = basename(fname2); + + /* + * Push the prefix on to the prefix stack and process the include + * file. When we reach the end of the include file, inserting + * the PREFIX token into the input stream will pop the prefix off + * of the prefix stack. + */ + prefix_push(dir); + (void) include(file, PREFIX, 0, 1); + + free(fname1); + free(fname2); +} + +/* + * Open the named file for inclusion at the current point. Returns 0 on + * success (file opened and previous state pushed), nonzero on failure + * (fopen failed, complaint made). The `ateof' parameter controls the + * token to be inserted at the end of the include file (i.e. ENDFILE). + * If ateof == 0 then nothing is inserted. + */ +int +include(const char *fname, int ateof, int conditional, int direct) +{ + FILE *fp; + struct incl *in; + char *s; + static int havedirs; + extern int vflag; + + if (havedirs == 0) { + havedirs = 1; + setupdirs(); + } + + if (fname[0] == '/') + s = estrdup(fname); + else if (fname[0] == '.' && fname[1] == '/') { + struct prefix *pf = SLIST_FIRST(&curdirs); + easprintf(&s, "%s/%s", pf->pf_prefix, fname + 2); + } else + s = sourcepath(fname); + if ((fp = fopen(s, "r")) == NULL) { + if (conditional == 0) + cfgerror("cannot open %s for reading: %s\n", s, + strerror(errno)); + else if (vflag) + cfgwarn("cannot open conditional include file %s: %s", + s, strerror(errno)); + free(s); + return (-1); + } + if (curdir_push(s) == -1) { + cfgerror("cannot record current working directory for %s\n", s); + fclose(fp); + free(s); + return (-1); + } + in = ecalloc(1, sizeof *in); + in->in_prev = incl; + in->in_buf = YY_CURRENT_BUFFER; + in->in_fname = yyfile; + in->in_lineno = yyline; + in->in_ateof = ateof; + in->in_interesting = interesting; + in->in_ifdefstate = ifdefstate; + interesting = direct & interesting; + if (interesting) + logconfig_include(fp, fname); + incl = in; + CFGDBG(1, "include `%s' from `%s' line %d", fname, yyfile, yyline); + yy_switch_to_buffer(yy_create_buffer(fp, YY_BUF_SIZE)); + yyfile = intern(s); + yyline = 1; + free(s); + return (0); +} + +/* + * Extract the pathname from a include/cinclude/package into curinclpath + */ +static int +getincludepath(void) +{ + const char *p = yytext; + ptrdiff_t len; + const char *e; + + while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p)) + p++; + while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p)) + p++; + if (!*p) + return 0; + if (*p == '"') { + p++; + e = strchr(p, '"'); + if (!e) return 0; + } else { + e = p; + while (*e && isascii((unsigned int)*e) + && !isspace((unsigned int)*e)) + e++; + } + + len = e-p; + if (len > (ptrdiff_t)sizeof(curinclpath)-1) + len = sizeof(curinclpath)-1; + strncpy(curinclpath, p, sizeof(curinclpath)); + curinclpath[len] = '\0'; + + return 1; +} + +/* + * Terminate the most recent inclusion. + */ +static int +endinclude(void) +{ + struct incl *in; + int ateof; + + curdir_pop(); + if ((in = incl) == NULL) + panic("endinclude"); + incl = in->in_prev; + lastfile = yyfile; + yy_delete_buffer(YY_CURRENT_BUFFER); + (void)fclose(yyin); + yy_switch_to_buffer(in->in_buf); + yyfile = in->in_fname; + yyline = in->in_lineno; + ateof = in->in_ateof; + interesting = in->in_interesting; + free(in); + + return (ateof); +} + +/* + * Return the current line number. If yacc has looked ahead and caused + * us to consume a newline, we have to subtract one. yychar is yacc's + * token lookahead, so we can tell. + */ +u_short +currentline(void) +{ + extern int yychar; + + return (u_short)(yyline - (yychar == '\n')); +} + +static int +getcurifdef(void) +{ + char *p = yytext, *q; + + while (*p && isascii((unsigned int)*p) && !isspace((unsigned int)*p)) + p++; + while (*p && isascii((unsigned int)*p) && isspace((unsigned int)*p)) + p++; + q = p; + while (*q && isascii((unsigned int)*q) && !isspace((unsigned int)*q)) + q++; + *q = '\0'; + + return ht_lookup(attrtab, intern(p)) != NULL; +} diff --git a/buildrump.sh/src/usr.bin/config/sem.c b/buildrump.sh/src/usr.bin/config/sem.c new file mode 100644 index 00000000..618ac301 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/sem.c @@ -0,0 +1,2177 @@ +/* $NetBSD: sem.c,v 1.71 2014/11/21 20:46:56 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)sem.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: sem.c,v 1.71 2014/11/21 20:46:56 christos Exp $"); + +#include <sys/param.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <util.h> +#include "defs.h" +#include "sem.h" + +/* + * config semantics. + */ + +#define NAMESIZE 100 /* local name buffers */ + +const char *s_ifnet; /* magic attribute */ +const char *s_qmark; +const char *s_none; + +static struct hashtab *cfhashtab; /* for config lookup */ +struct hashtab *devitab; /* etc */ +struct attr allattr; +size_t nattrs; + +static struct attr errattr; +static struct devbase errdev; +static struct deva errdeva; + +static int has_errobj(struct attrlist *, struct attr *); +static struct nvlist *addtoattr(struct nvlist *, struct devbase *); +static int resolve(struct nvlist **, const char *, const char *, + struct nvlist *, int); +static struct pspec *getpspec(struct attr *, struct devbase *, int); +static struct devi *newdevi(const char *, int, struct devbase *d); +static struct devi *getdevi(const char *); +static void remove_devi(struct devi *); +static const char *concat(const char *, int); +static char *extend(char *, const char *); +static int split(const char *, size_t, char *, size_t, int *); +static void selectbase(struct devbase *, struct deva *); +static const char **fixloc(const char *, struct attr *, struct loclist *); +static const char *makedevstr(devmajor_t, devminor_t); +static const char *major2name(devmajor_t); +static devmajor_t dev2major(struct devbase *); + +extern const char *yyfile; +extern int vflag; + +void +initsem(void) +{ + + attrtab = ht_new(); + attrdeptab = ht_new(); + + allattr.a_name = "netbsd"; + TAILQ_INIT(&allattr.a_files); + (void)ht_insert(attrtab, allattr.a_name, &allattr); + selectattr(&allattr); + + errattr.a_name = "<internal>"; + + TAILQ_INIT(&allbases); + + TAILQ_INIT(&alldevas); + + TAILQ_INIT(&allpspecs); + + cfhashtab = ht_new(); + TAILQ_INIT(&allcf); + + TAILQ_INIT(&alldevi); + errdev.d_name = "<internal>"; + + TAILQ_INIT(&allpseudo); + + TAILQ_INIT(&alldevms); + + s_ifnet = intern("ifnet"); + s_qmark = intern("?"); + s_none = intern("none"); +} + +/* Name of include file just ended (set in scan.l) */ +extern const char *lastfile; + +static struct attr * +finddep(struct attr *a, const char *name) +{ + struct attrlist *al; + + for (al = a->a_deps; al != NULL; al = al->al_next) { + struct attr *this = al->al_this; + if (strcmp(this->a_name, name) == 0) + return this; + } + return NULL; +} + +static void +mergedeps(const char *dname, const char *name) +{ + struct attr *a, *newa; + + CFGDBG(4, "merging attr `%s' to devbase `%s'", name, dname); + a = refattr(dname); + if (finddep(a, name) == NULL) { + newa = refattr(name); + a->a_deps = attrlist_cons(a->a_deps, newa); + CFGDBG(3, "attr `%s' merged to attr `%s'", newa->a_name, + a->a_name); + } +} + +static void +fixdev(struct devbase *dev) +{ + struct attrlist *al; + struct attr *devattr, *a; + + devattr = refattr(dev->d_name); + if (devattr->a_devclass) + panic("%s: dev %s is devclass!", __func__, devattr->a_name); + + CFGDBG(4, "fixing devbase `%s'", dev->d_name); + for (al = dev->d_attrs; al != NULL; al = al->al_next) { + a = al->al_this; + CFGDBG(4, "fixing devbase `%s' attr `%s'", dev->d_name, a->a_name); + if (a->a_iattr) { + a->a_refs = addtoattr(a->a_refs, dev); + CFGDBG(3, "device `%s' has iattr `%s'", dev->d_name, + a->a_name); + } else if (a->a_devclass != NULL) { + if (dev->d_classattr != NULL && dev->d_classattr != a) { + cfgwarn("device `%s' has multiple classes " + "(`%s' and `%s')", + dev->d_name, dev->d_classattr->a_name, + a->a_name); + } + if (dev->d_classattr == NULL) { + dev->d_classattr = a; + CFGDBG(3, "device `%s' is devclass `%s'", dev->d_name, + a->a_name); + } + } else { + if (strcmp(dev->d_name, a->a_name) != 0) { + mergedeps(dev->d_name, a->a_name); + } + } + } +} + +void +enddefs(void) +{ + struct devbase *dev; + + yyfile = "enddefs"; + + TAILQ_FOREACH(dev, &allbases, d_next) { + if (!dev->d_isdef) { + (void)fprintf(stderr, + "%s: device `%s' used but not defined\n", + lastfile, dev->d_name); + errors++; + continue; + } + fixdev(dev); + } + if (errors) { + (void)fprintf(stderr, "*** Stop.\n"); + exit(1); + } +} + +void +setdefmaxusers(int min, int def, int max) +{ + + if (min < 1 || min > def || def > max) + cfgerror("maxusers must have 1 <= min (%d) <= default (%d) " + "<= max (%d)", min, def, max); + else { + minmaxusers = min; + defmaxusers = def; + maxmaxusers = max; + } +} + +void +setmaxusers(int n) +{ + + if (maxusers == n) { + cfgerror("duplicate maxusers parameter"); + return; + } + if (vflag && maxusers != 0) + cfgwarn("warning: maxusers already defined"); + maxusers = n; + if (n < minmaxusers) { + cfgerror("warning: minimum of %d maxusers assumed", + minmaxusers); + errors--; /* take it away */ + maxusers = minmaxusers; + } else if (n > maxmaxusers) { + cfgerror("warning: maxusers (%d) > %d", n, maxmaxusers); + errors--; + } +} + +void +setident(const char *i) +{ + + if (i) + ident = intern(i); + else + ident = NULL; +} + +/* + * Define an attribute, optionally with an interface (a locator list) + * and a set of attribute-dependencies. + * + * Attribute dependencies MAY NOT be interface attributes. + * + * Since an empty locator list is logically different from "no interface", + * all locator lists include a dummy head node, which we discard here. + */ +int +defattr0(const char *name, struct loclist *locs, struct attrlist *deps, + int devclass) +{ + + if (locs != NULL) + return defiattr(name, locs, deps, devclass); + else if (devclass) + return defdevclass(name, locs, deps, devclass); + else + return defattr(name, locs, deps, devclass); +} + +int +defattr(const char *name, struct loclist *locs, struct attrlist *deps, + int devclass) +{ + struct attr *a, *dep; + struct attrlist *al; + + /* + * If this attribute depends on any others, make sure none of + * the dependencies are interface attributes. + */ + for (al = deps; al != NULL; al = al->al_next) { + dep = al->al_this; + if (dep->a_iattr) { + cfgerror("`%s' dependency `%s' is an interface " + "attribute", name, dep->a_name); + return (1); + } + (void)ht_insert2(attrdeptab, name, dep->a_name, NULL); + CFGDBG(2, "attr `%s' depends on attr `%s'", name, dep->a_name); + } + + if (getrefattr(name, &a)) { + cfgerror("attribute `%s' already defined", name); + loclist_destroy(locs); + return (1); + } + if (a == NULL) + a = mkattr(name); + + a->a_deps = deps; + expandattr(a, NULL); + CFGDBG(3, "attr `%s' defined", a->a_name); + + return (0); +} + +struct attr * +mkattr(const char *name) +{ + struct attr *a; + + a = ecalloc(1, sizeof *a); + if (ht_insert(attrtab, name, a)) { + free(a); + return NULL; + } + a->a_name = name; + TAILQ_INIT(&a->a_files); + CFGDBG(3, "attr `%s' allocated", name); + + return a; +} + +/* "interface attribute" initialization */ +int +defiattr(const char *name, struct loclist *locs, struct attrlist *deps, + int devclass) +{ + struct attr *a; + int len; + struct loclist *ll; + + if (devclass) + panic("defattr(%s): locators and devclass", name); + + if (defattr(name, locs, deps, devclass) != 0) + return (1); + + a = getattr(name); + a->a_iattr = 1; + /* unwrap */ + a->a_locs = locs->ll_next; + locs->ll_next = NULL; + loclist_destroy(locs); + len = 0; + for (ll = a->a_locs; ll != NULL; ll = ll->ll_next) + len++; + a->a_loclen = len; + if (deps) + CFGDBG(2, "attr `%s' iface with deps", a->a_name); + return (0); +} + +/* "device class" initialization */ +int +defdevclass(const char *name, struct loclist *locs, struct attrlist *deps, + int devclass) +{ + struct attr *a; + char classenum[256], *cp; + int errored = 0; + + if (deps) + panic("defattr(%s): dependencies and devclass", name); + + if (defattr(name, locs, deps, devclass) != 0) + return (1); + + a = getattr(name); + (void)snprintf(classenum, sizeof(classenum), "DV_%s", name); + for (cp = classenum + 3; *cp; cp++) { + if (!errored && + (!isalnum((unsigned char)*cp) || + (isalpha((unsigned char)*cp) && !islower((unsigned char)*cp)))) { + cfgerror("device class names must be " + "lower-case alphanumeric characters"); + errored = 1; + } + *cp = (char)toupper((unsigned char)*cp); + } + a->a_devclass = intern(classenum); + + return (0); +} + +/* + * Return true if the given `error object' is embedded in the given + * pointer list. + */ +static int +has_errobj(struct attrlist *al, struct attr *obj) +{ + + for (; al != NULL; al = al->al_next) + if (al->al_this == obj) + return (1); + return (0); +} + +/* + * Return true if the given attribute is embedded in the given + * pointer list. + */ +int +has_attr(struct attrlist *al, const char *attr) +{ + struct attr *a; + + if ((a = getattr(attr)) == NULL) + return (0); + + for (; al != NULL; al = al->al_next) + if (al->al_this == a) + return (1); + return (0); +} + +/* + * Add a device base to a list in an attribute (actually, to any list). + * Note that this does not check for duplicates, and does reverse the + * list order, but no one cares anyway. + */ +static struct nvlist * +addtoattr(struct nvlist *l, struct devbase *dev) +{ + struct nvlist *n; + + n = newnv(NULL, NULL, dev, 0, l); + return (n); +} + +/* + * Define a device. This may (or may not) also define an interface + * attribute and/or refer to existing attributes. + */ +void +defdev(struct devbase *dev, struct loclist *loclist, struct attrlist *attrs, + int ispseudo) +{ + struct loclist *ll; + struct attrlist *al; + + if (dev == &errdev) + goto bad; + if (dev->d_isdef) { + cfgerror("redefinition of `%s'", dev->d_name); + goto bad; + } + + dev->d_isdef = 1; + if (has_errobj(attrs, &errattr)) + goto bad; + + /* + * Handle implicit attribute definition from locator list. Do + * this before scanning the `at' list so that we can have, e.g.: + * device foo at other, foo { slot = -1 } + * (where you can plug in a foo-bus extender to a foo-bus). + */ + if (loclist != NULL) { + ll = loclist; + loclist = NULL; /* defattr disposes of them for us */ + if (defiattr(dev->d_name, ll, NULL, 0)) + goto bad; + attrs = attrlist_cons(attrs, getattr(dev->d_name)); + /* This used to be stored but was never used */ + /* attrs->al_name = dev->d_name; */ + } + + /* + * Pseudo-devices can have children. Consider them as + * attaching at root. + */ + if (ispseudo) { + for (al = attrs; al != NULL; al = al->al_next) + if (al->al_this->a_iattr) + break; + if (al != NULL) { + if (ispseudo < 2) { + if (version >= 20080610) + cfgerror("interface attribute on " + "non-device pseudo `%s'", dev->d_name); + else { + ispseudo = 2; + } + } + ht_insert(devroottab, dev->d_name, dev); + } + } + + /* Committed! Set up fields. */ + dev->d_ispseudo = ispseudo; + dev->d_attrs = attrs; + dev->d_classattr = NULL; /* for now */ + CFGDBG(3, "dev `%s' defined", dev->d_name); + + /* + * Implicit attribute definition for device. + */ + refattr(dev->d_name); + + /* + * For each interface attribute this device refers to, add this + * device to its reference list. This makes, e.g., finding all + * "scsi"s easier. + * + * While looking through the attributes, set up the device + * class if any are devclass attributes (and error out if the + * device has two classes). + */ + for (al = attrs; al != NULL; al = al->al_next) { + /* + * Implicit attribute definition for device dependencies. + */ + refattr(al->al_this->a_name); + (void)ht_insert2(attrdeptab, dev->d_name, al->al_this->a_name, NULL); + CFGDBG(2, "device `%s' depends on attr `%s'", dev->d_name, + al->al_this->a_name); + } + return; + bad: + loclist_destroy(loclist); + attrlist_destroyall(attrs); +} + +/* + * Look up a devbase. Also makes sure it is a reasonable name, + * i.e., does not end in a digit or contain special characters. + */ +struct devbase * +getdevbase(const char *name) +{ + const u_char *p; + struct devbase *dev; + + p = (const u_char *)name; + if (!isalpha(*p)) + goto badname; + while (*++p) { + if (!isalnum(*p) && *p != '_') + goto badname; + } + if (isdigit(*--p)) { + badname: + cfgerror("bad device base name `%s'", name); + return (&errdev); + } + dev = ht_lookup(devbasetab, name); + if (dev == NULL) { + dev = ecalloc(1, sizeof *dev); + dev->d_name = name; + dev->d_isdef = 0; + dev->d_major = NODEVMAJOR; + dev->d_attrs = NULL; + dev->d_ihead = NULL; + dev->d_ipp = &dev->d_ihead; + dev->d_ahead = NULL; + dev->d_app = &dev->d_ahead; + dev->d_umax = 0; + TAILQ_INSERT_TAIL(&allbases, dev, d_next); + if (ht_insert(devbasetab, name, dev)) + panic("getdevbase(%s)", name); + CFGDBG(3, "devbase defined `%s'", dev->d_name); + } + return (dev); +} + +/* + * Define some of a device's allowable parent attachments. + * There may be a list of (plain) attributes. + */ +void +defdevattach(struct deva *deva, struct devbase *dev, struct nvlist *atlist, + struct attrlist *attrs) +{ + struct nvlist *nv; + struct attrlist *al; + struct attr *a; + struct deva *da; + + if (dev == &errdev) + goto bad; + if (deva == NULL) + deva = getdevattach(dev->d_name); + if (deva == &errdeva) + goto bad; + if (!dev->d_isdef) { + cfgerror("attaching undefined device `%s'", dev->d_name); + goto bad; + } + if (deva->d_isdef) { + cfgerror("redefinition of `%s'", deva->d_name); + goto bad; + } + if (dev->d_ispseudo) { + cfgerror("pseudo-devices can't attach"); + goto bad; + } + + deva->d_isdef = 1; + if (has_errobj(attrs, &errattr)) + goto bad; + for (al = attrs; al != NULL; al = al->al_next) { + a = al->al_this; + if (a == &errattr) + continue; /* already complained */ + if (a->a_iattr || a->a_devclass != NULL) + cfgerror("`%s' is not a plain attribute", a->a_name); + } + + /* Committed! Set up fields. */ + deva->d_attrs = attrs; + deva->d_atlist = atlist; + deva->d_devbase = dev; + CFGDBG(3, "deva `%s' defined", deva->d_name); + + /* + * Implicit attribute definition for device attachment. + */ + refattr(deva->d_name); + + /* + * Turn the `at' list into interface attributes (map each + * nv_name to an attribute, or to NULL for root), and add + * this device to those attributes, so that children can + * be listed at this particular device if they are supported + * by that attribute. + */ + for (nv = atlist; nv != NULL; nv = nv->nv_next) { + if (nv->nv_name == NULL) + nv->nv_ptr = a = NULL; /* at root */ + else + nv->nv_ptr = a = getattr(nv->nv_name); + if (a == &errattr) + continue; /* already complained */ + + /* + * Make sure that an attachment spec doesn't + * already say how to attach to this attribute. + */ + for (da = dev->d_ahead; da != NULL; da = da->d_bsame) + if (onlist(da->d_atlist, a)) + cfgerror("attach at `%s' already done by `%s'", + a ? a->a_name : "root", da->d_name); + + if (a == NULL) { + ht_insert(devroottab, dev->d_name, dev); + continue; /* at root; don't add */ + } + if (!a->a_iattr) + cfgerror("%s cannot be at plain attribute `%s'", + dev->d_name, a->a_name); + else + a->a_devs = addtoattr(a->a_devs, dev); + } + + /* attach to parent */ + *dev->d_app = deva; + dev->d_app = &deva->d_bsame; + return; + bad: + nvfreel(atlist); + attrlist_destroyall(attrs); +} + +/* + * Look up a device attachment. Also makes sure it is a reasonable + * name, i.e., does not contain digits or special characters. + */ +struct deva * +getdevattach(const char *name) +{ + const u_char *p; + struct deva *deva; + + p = (const u_char *)name; + if (!isalpha(*p)) + goto badname; + while (*++p) { + if (!isalnum(*p) && *p != '_') + goto badname; + } + if (isdigit(*--p)) { + badname: + cfgerror("bad device attachment name `%s'", name); + return (&errdeva); + } + deva = ht_lookup(devatab, name); + if (deva == NULL) { + deva = ecalloc(1, sizeof *deva); + deva->d_name = name; + deva->d_bsame = NULL; + deva->d_isdef = 0; + deva->d_devbase = NULL; + deva->d_atlist = NULL; + deva->d_attrs = NULL; + deva->d_ihead = NULL; + deva->d_ipp = &deva->d_ihead; + TAILQ_INSERT_TAIL(&alldevas, deva, d_next); + if (ht_insert(devatab, name, deva)) + panic("getdeva(%s)", name); + } + return (deva); +} + +/* + * Look up an attribute. + */ +struct attr * +getattr(const char *name) +{ + struct attr *a; + + if ((a = ht_lookup(attrtab, name)) == NULL) { + cfgerror("undefined attribute `%s'", name); + a = &errattr; + } + return (a); +} + +/* + * Implicit attribute definition. + */ +struct attr * +refattr(const char *name) +{ + struct attr *a; + + if ((a = ht_lookup(attrtab, name)) == NULL) + a = mkattr(name); + return a; +} + +int +getrefattr(const char *name, struct attr **ra) +{ + struct attr *a; + + a = ht_lookup(attrtab, name); + if (a == NULL) { + *ra = NULL; + return (0); + } + /* + * Check if the existing attr is only referenced, not really defined. + */ + if (a->a_deps == NULL && + a->a_iattr == 0 && + a->a_devclass == 0) { + *ra = a; + return (0); + } + return (1); +} + +/* + * Recursively expand an attribute and its dependencies, checking for + * cycles, and invoking a callback for each attribute found. + */ +void +expandattr(struct attr *a, void (*callback)(struct attr *)) +{ + struct attrlist *al; + struct attr *dep; + + if (a->a_expanding) { + cfgerror("circular dependency on attribute `%s'", a->a_name); + return; + } + + a->a_expanding = 1; + + /* First expand all of this attribute's dependencies. */ + for (al = a->a_deps; al != NULL; al = al->al_next) { + dep = al->al_this; + expandattr(dep, callback); + } + + /* ...and now invoke the callback for ourself. */ + if (callback != NULL) + (*callback)(a); + + a->a_expanding = 0; +} + +/* + * Set the major device number for a device, so that it can be used + * as a root/dumps "on" device in a configuration. + */ +void +setmajor(struct devbase *d, devmajor_t n) +{ + + if (d != &errdev && d->d_major != NODEVMAJOR) + cfgerror("device `%s' is already major %d", + d->d_name, d->d_major); + else + d->d_major = n; +} + +const char * +major2name(devmajor_t maj) +{ + struct devbase *dev; + struct devm *dm; + + if (!do_devsw) { + TAILQ_FOREACH(dev, &allbases, d_next) { + if (dev->d_major == maj) + return (dev->d_name); + } + } else { + TAILQ_FOREACH(dm, &alldevms, dm_next) { + if (dm->dm_bmajor == maj) + return (dm->dm_name); + } + } + return (NULL); +} + +devmajor_t +dev2major(struct devbase *dev) +{ + struct devm *dm; + + if (!do_devsw) + return (dev->d_major); + + TAILQ_FOREACH(dm, &alldevms, dm_next) { + if (strcmp(dm->dm_name, dev->d_name) == 0) + return (dm->dm_bmajor); + } + return (NODEVMAJOR); +} + +/* + * Make a string description of the device at maj/min. + */ +static const char * +makedevstr(devmajor_t maj, devminor_t min) +{ + const char *devicename; + char buf[32]; + + devicename = major2name(maj); + if (devicename == NULL) + (void)snprintf(buf, sizeof(buf), "<%d/%d>", maj, min); + else + (void)snprintf(buf, sizeof(buf), "%s%d%c", devicename, + min / maxpartitions, (min % maxpartitions) + 'a'); + + return (intern(buf)); +} + +/* + * Map things like "ra0b" => makedev(major("ra"), 0*maxpartitions + 'b'-'a'). + * Handle the case where the device number is given but there is no + * corresponding name, and map NULL to the default. + */ +static int +resolve(struct nvlist **nvp, const char *name, const char *what, + struct nvlist *dflt, int part) +{ + struct nvlist *nv; + struct devbase *dev; + const char *cp; + devmajor_t maj; + devminor_t min; + size_t i, l; + int unit; + char buf[NAMESIZE]; + + if ((part -= 'a') >= maxpartitions || part < 0) + panic("resolve"); + if ((nv = *nvp) == NULL) { + dev_t d = NODEV; + /* + * Apply default. Easiest to do this by number. + * Make sure to retain NODEVness, if this is dflt's disposition. + */ + if ((dev_t)dflt->nv_num != NODEV) { + maj = major(dflt->nv_num); + min = ((minor(dflt->nv_num) / maxpartitions) * + maxpartitions) + part; + d = makedev(maj, min); + cp = makedevstr(maj, min); + } else + cp = NULL; + *nvp = nv = newnv(NULL, cp, NULL, (long long)d, NULL); + } + if ((dev_t)nv->nv_num != NODEV) { + /* + * By the numbers. Find the appropriate major number + * to make a name. + */ + maj = major(nv->nv_num); + min = minor(nv->nv_num); + nv->nv_str = makedevstr(maj, min); + return (0); + } + + if (nv->nv_str == NULL || nv->nv_str == s_qmark) + /* + * Wildcarded or unspecified; leave it as NODEV. + */ + return (0); + + /* + * The normal case: things like "ra2b". Check for partition + * suffix, remove it if there, and split into name ("ra") and + * unit (2). + */ + l = i = strlen(nv->nv_str); + cp = &nv->nv_str[l]; + if (l > 1 && *--cp >= 'a' && *cp < 'a' + maxpartitions && + isdigit((unsigned char)cp[-1])) { + l--; + part = *cp - 'a'; + } + cp = nv->nv_str; + if (split(cp, l, buf, sizeof buf, &unit)) { + cfgerror("%s: invalid %s device name `%s'", name, what, cp); + return (1); + } + dev = ht_lookup(devbasetab, intern(buf)); + if (dev == NULL) { + cfgerror("%s: device `%s' does not exist", name, buf); + return (1); + } + + /* + * Check for the magic network interface attribute, and + * don't bother making a device number. + */ + if (has_attr(dev->d_attrs, s_ifnet)) { + nv->nv_num = (long long)NODEV; + nv->nv_ifunit = unit; /* XXX XXX XXX */ + } else { + maj = dev2major(dev); + if (maj == NODEVMAJOR) { + cfgerror("%s: can't make %s device from `%s'", + name, what, nv->nv_str); + return (1); + } + nv->nv_num = (long long)makedev(maj, unit * maxpartitions + part); + } + + nv->nv_name = dev->d_name; + return (0); +} + +/* + * Add a completed configuration to the list. + */ +void +addconf(struct config *cf0) +{ + struct config *cf; + const char *name; + + name = cf0->cf_name; + cf = ecalloc(1, sizeof *cf); + if (ht_insert(cfhashtab, name, cf)) { + cfgerror("configuration `%s' already defined", name); + free(cf); + goto bad; + } + *cf = *cf0; + + /* + * Resolve the root device. + */ + if (cf->cf_root == NULL) { + cfgerror("%s: no root device specified", name); + goto bad; + } + if (cf->cf_root && cf->cf_root->nv_str != s_qmark) { + struct nvlist *nv; + nv = cf->cf_root; + if (resolve(&cf->cf_root, name, "root", nv, 'a')) + goto bad; + } + + /* + * Resolve the dump device. + */ + if (cf->cf_dump == NULL || cf->cf_dump->nv_str == s_qmark) { + /* + * Wildcarded dump device is equivalent to unspecified. + */ + cf->cf_dump = NULL; + } else if (cf->cf_dump->nv_str == s_none) { + /* + * Operator has requested that no dump device should be + * configured; do nothing. + */ + } else { + if (resolve(&cf->cf_dump, name, "dumps", cf->cf_dump, 'b')) + goto bad; + } + + /* Wildcarded fstype is `unspecified'. */ + if (cf->cf_fstype == s_qmark) + cf->cf_fstype = NULL; + + TAILQ_INSERT_TAIL(&allcf, cf, cf_next); + return; + bad: + nvfreel(cf0->cf_root); + nvfreel(cf0->cf_dump); +} + +void +setconf(struct nvlist **npp, const char *what, struct nvlist *v) +{ + + if (*npp != NULL) { + cfgerror("duplicate %s specification", what); + nvfreel(v); + } else + *npp = v; +} + +void +delconf(const char *name) +{ + struct config *cf; + + CFGDBG(5, "deselecting config `%s'", name); + if (ht_lookup(cfhashtab, name) == NULL) { + cfgerror("configuration `%s' undefined", name); + return; + } + (void)ht_remove(cfhashtab, name); + + TAILQ_FOREACH(cf, &allcf, cf_next) + if (!strcmp(cf->cf_name, name)) + break; + if (cf == NULL) + panic("lost configuration `%s'", name); + + TAILQ_REMOVE(&allcf, cf, cf_next); +} + +void +setfstype(const char **fstp, const char *v) +{ + + if (*fstp != NULL) { + cfgerror("multiple fstype specifications"); + return; + } + + if (v != s_qmark && OPT_FSOPT(v)) { + cfgerror("\"%s\" is not a configured file system", v); + return; + } + + *fstp = v; +} + +static struct devi * +newdevi(const char *name, int unit, struct devbase *d) +{ + struct devi *i; + + i = ecalloc(1, sizeof *i); + i->i_name = name; + i->i_unit = unit; + i->i_base = d; + i->i_bsame = NULL; + i->i_asame = NULL; + i->i_alias = NULL; + i->i_at = NULL; + i->i_pspec = NULL; + i->i_atdeva = NULL; + i->i_locs = NULL; + i->i_cfflags = 0; + i->i_lineno = currentline(); + i->i_srcfile = yyfile; + i->i_active = DEVI_ORPHAN; /* Proper analysis comes later */ + i->i_level = devilevel; + i->i_pseudoroot = 0; + if (unit >= d->d_umax) + d->d_umax = unit + 1; + return (i); +} + +/* + * Add the named device as attaching to the named attribute (or perhaps + * another device instead) plus unit number. + */ +void +adddev(const char *name, const char *at, struct loclist *loclist, int flags) +{ + struct devi *i; /* the new instance */ + struct pspec *p; /* and its pspec */ + struct attr *attr; /* attribute that allows attach */ + struct devbase *ib; /* i->i_base */ + struct devbase *ab; /* not NULL => at another dev */ + struct attrlist *al; + struct deva *iba; /* devbase attachment used */ + const char *cp; + int atunit; + char atbuf[NAMESIZE]; + int hit; + + ab = NULL; + iba = NULL; + if (at == NULL) { + /* "at root" */ + p = NULL; + if ((i = getdevi(name)) == NULL) + goto bad; + /* + * Must warn about i_unit > 0 later, after taking care of + * the STAR cases (we could do non-star's here but why + * bother?). Make sure this device can be at root. + */ + ib = i->i_base; + hit = 0; + for (iba = ib->d_ahead; iba != NULL; iba = iba->d_bsame) + if (onlist(iba->d_atlist, NULL)) { + hit = 1; + break; + } + if (!hit) { + cfgerror("`%s' cannot attach to the root", ib->d_name); + i->i_active = DEVI_BROKEN; + goto bad; + } + attr = &errattr; /* a convenient "empty" attr */ + } else { + if (split(at, strlen(at), atbuf, sizeof atbuf, &atunit)) { + cfgerror("invalid attachment name `%s'", at); + /* (void)getdevi(name); -- ??? */ + goto bad; + } + if ((i = getdevi(name)) == NULL) + goto bad; + ib = i->i_base; + + /* + * Devices can attach to two types of things: Attributes, + * and other devices (which have the appropriate attributes + * to allow attachment). + * + * (1) If we're attached to an attribute, then we don't need + * look at the parent base device to see what attributes + * it has, and make sure that we can attach to them. + * + * (2) If we're attached to a real device (i.e. named in + * the config file), we want to remember that so that + * at cross-check time, if the device we're attached to + * is missing but other devices which also provide the + * attribute are present, we don't get a false "OK." + * + * (3) If the thing we're attached to is an attribute + * but is actually named in the config file, we still + * have to remember its devbase. + */ + cp = intern(atbuf); + + /* Figure out parent's devbase, to satisfy case (3). */ + ab = ht_lookup(devbasetab, cp); + + /* Find out if it's an attribute. */ + attr = ht_lookup(attrtab, cp); + + /* Make sure we're _really_ attached to the attr. Case (1). */ + if (attr != NULL && onlist(attr->a_devs, ib)) + goto findattachment; + + /* + * Else a real device, and not just an attribute. Case (2). + * + * Have to work a bit harder to see whether we have + * something like "tg0 at esp0" (where esp is merely + * not an attribute) or "tg0 at nonesuch0" (where + * nonesuch is not even a device). + */ + if (ab == NULL) { + cfgerror("%s at %s: `%s' unknown", + name, at, atbuf); + i->i_active = DEVI_BROKEN; + goto bad; + } + + /* + * See if the named parent carries an attribute + * that allows it to supervise device ib. + */ + for (al = ab->d_attrs; al != NULL; al = al->al_next) { + attr = al->al_this; + if (onlist(attr->a_devs, ib)) + goto findattachment; + } + cfgerror("`%s' cannot attach to `%s'", ib->d_name, atbuf); + i->i_active = DEVI_BROKEN; + goto bad; + + findattachment: + /* + * Find the parent spec. If a matching one has not yet been + * created, create one. + */ + p = getpspec(attr, ab, atunit); + p->p_devs = newnv(NULL, NULL, i, 0, p->p_devs); + + /* find out which attachment it uses */ + hit = 0; + for (iba = ib->d_ahead; iba != NULL; iba = iba->d_bsame) + if (onlist(iba->d_atlist, attr)) { + hit = 1; + break; + } + if (!hit) + panic("adddev: can't figure out attachment"); + } + if ((i->i_locs = fixloc(name, attr, loclist)) == NULL) { + i->i_active = DEVI_BROKEN; + goto bad; + } + i->i_at = at; + i->i_pspec = p; + i->i_atdeva = iba; + i->i_cfflags = flags; + CFGDBG(3, "devi `%s' added", i->i_name); + + *iba->d_ipp = i; + iba->d_ipp = &i->i_asame; + + /* all done, fall into ... */ + bad: + loclist_destroy(loclist); + return; +} + +void +deldevi(const char *name, const char *at) +{ + struct devi *firsti, *i; + struct devbase *d; + int unit; + char base[NAMESIZE]; + + CFGDBG(5, "deselecting devi `%s'", name); + if (split(name, strlen(name), base, sizeof base, &unit)) { + cfgerror("invalid device name `%s'", name); + return; + } + d = ht_lookup(devbasetab, intern(base)); + if (d == NULL) { + cfgerror("%s: unknown device `%s'", name, base); + return; + } + if (d->d_ispseudo) { + cfgerror("%s: %s is a pseudo-device", name, base); + return; + } + if ((firsti = ht_lookup(devitab, name)) == NULL) { + cfgerror("`%s' not defined", name); + return; + } + if (at == NULL && firsti->i_at == NULL) { + /* 'at root' */ + remove_devi(firsti); + return; + } else if (at != NULL) + for (i = firsti; i != NULL; i = i->i_alias) + if (i->i_active != DEVI_BROKEN && + strcmp(at, i->i_at) == 0) { + remove_devi(i); + return; + } + cfgerror("`%s' at `%s' not found", name, at ? at : "root"); +} + +static void +remove_devi(struct devi *i) +{ + struct devbase *d = i->i_base; + struct devi *f, *j, **ppi; + struct deva *iba; + + CFGDBG(5, "removing devi `%s'", i->i_name); + f = ht_lookup(devitab, i->i_name); + if (f == NULL) + panic("remove_devi(): instance %s disappeared from devitab", + i->i_name); + + if (i->i_active == DEVI_BROKEN) { + cfgerror("not removing broken instance `%s'", i->i_name); + return; + } + + /* + * We have the device instance, i. + * We have to: + * - delete the alias + * + * If the devi was an alias of an already listed devi, all is + * good we don't have to do more. + * If it was the first alias, we have to replace i's entry in + * d's list by its first alias. + * If it was the only entry, we must remove i's entry from d's + * list. + */ + if (i != f) { + for (j = f; j->i_alias != i; j = j->i_alias) + continue; + j->i_alias = i->i_alias; + } else { + if (i->i_alias == NULL) { + /* No alias, must unlink the entry from devitab */ + ht_remove(devitab, i->i_name); + j = i->i_bsame; + } else { + /* Or have the first alias replace i in d's list */ + i->i_alias->i_bsame = i->i_bsame; + j = i->i_alias; + if (i == f) + ht_replace(devitab, i->i_name, i->i_alias); + } + + /* + * - remove/replace the instance from the devbase's list + * + * A double-linked list would make this much easier. Oh, well, + * what is done is done. + */ + for (ppi = &d->d_ihead; + *ppi != NULL && *ppi != i && (*ppi)->i_bsame != i; + ppi = &(*ppi)->i_bsame) + continue; + if (*ppi == NULL) + panic("deldev: dev (%s) doesn't list the devi" + " (%s at %s)", d->d_name, i->i_name, i->i_at); + f = *ppi; + if (f == i) + /* That implies d->d_ihead == i */ + *ppi = j; + else + (*ppi)->i_bsame = j; + if (d->d_ipp == &i->i_bsame) { + if (i->i_alias == NULL) { + if (f == i) + d->d_ipp = &d->d_ihead; + else + d->d_ipp = &f->i_bsame; + } else + d->d_ipp = &i->i_alias->i_bsame; + } + } + /* + * - delete the attachment instance + */ + iba = i->i_atdeva; + for (ppi = &iba->d_ihead; + *ppi != NULL && *ppi != i && (*ppi)->i_asame != i; + ppi = &(*ppi)->i_asame) + continue; + if (*ppi == NULL) + panic("deldev: deva (%s) doesn't list the devi (%s)", + iba->d_name, i->i_name); + f = *ppi; + if (f == i) + /* That implies iba->d_ihead == i */ + *ppi = i->i_asame; + else + (*ppi)->i_asame = i->i_asame; + if (iba->d_ipp == &i->i_asame) { + if (f == i) + iba->d_ipp = &iba->d_ihead; + else + iba->d_ipp = &f->i_asame; + } + /* + * - delete the pspec + */ + if (i->i_pspec) { + struct pspec *p = i->i_pspec; + struct nvlist *nv, *onv; + + /* Double-linked nvlist anyone? */ + for (nv = p->p_devs; nv->nv_next != NULL; nv = nv->nv_next) { + if (nv->nv_next && nv->nv_next->nv_ptr == i) { + onv = nv->nv_next; + nv->nv_next = onv->nv_next; + nvfree(onv); + break; + } + if (nv->nv_ptr == i) { + /* nv is p->p_devs in that case */ + p->p_devs = nv->nv_next; + nvfree(nv); + break; + } + } + if (p->p_devs == NULL) + TAILQ_REMOVE(&allpspecs, p, p_list); + } + /* + * - delete the alldevi entry + */ + TAILQ_REMOVE(&alldevi, i, i_next); + ndevi--; + /* + * Put it in deaddevitab + * + * Each time a devi is removed, devilevel is increased so that later on + * it is possible to tell if an instance was added before or after the + * removal of its parent. + * + * For active instances, i_level contains the number of devi removed so + * far, and for dead devis, it contains its index. + */ + i->i_level = devilevel++; + i->i_alias = NULL; + f = ht_lookup(deaddevitab, i->i_name); + if (f == NULL) { + if (ht_insert(deaddevitab, i->i_name, i)) + panic("remove_devi(%s) - can't add to deaddevitab", + i->i_name); + } else { + for (j = f; j->i_alias != NULL; j = j->i_alias) + continue; + j->i_alias = i; + } + /* + * - reconstruct d->d_umax + */ + d->d_umax = 0; + for (i = d->d_ihead; i != NULL; i = i->i_bsame) + if (i->i_unit >= d->d_umax) + d->d_umax = i->i_unit + 1; +} + +void +deldeva(const char *at) +{ + int unit; + const char *cp; + struct devbase *d, *ad; + struct devi *i, *j; + struct attr *a; + struct pspec *p; + struct nvlist *nv, *stack = NULL; + + if (at == NULL) { + TAILQ_FOREACH(i, &alldevi, i_next) + if (i->i_at == NULL) + stack = newnv(NULL, NULL, i, 0, stack); + } else { + size_t l; + + CFGDBG(5, "deselecting deva `%s'", at); + if (at[0] == '\0') + goto out; + + l = strlen(at) - 1; + if (at[l] == '?' || isdigit((unsigned char)at[l])) { + char base[NAMESIZE]; + + if (split(at, l+1, base, sizeof base, &unit)) { +out: + cfgerror("invalid attachment name `%s'", at); + return; + } + cp = intern(base); + } else { + cp = intern(at); + unit = STAR; + } + + ad = ht_lookup(devbasetab, cp); + a = ht_lookup(attrtab, cp); + if (a == NULL) { + cfgerror("unknown attachment attribute or device `%s'", + cp); + return; + } + if (!a->a_iattr) { + cfgerror("plain attribute `%s' cannot have children", + a->a_name); + return; + } + + /* + * remove_devi() makes changes to the devbase's list and the + * alias list, * so the actual deletion of the instances must + * be delayed. + */ + for (nv = a->a_devs; nv != NULL; nv = nv->nv_next) { + d = nv->nv_ptr; + for (i = d->d_ihead; i != NULL; i = i->i_bsame) + for (j = i; j != NULL; j = j->i_alias) { + /* Ignore devices at root */ + if (j->i_at == NULL) + continue; + p = j->i_pspec; + /* + * There are three cases: + * + * 1. unit is not STAR. Consider 'at' + * to be explicit, even if it + * references an interface + * attribute. + * + * 2. unit is STAR and 'at' references + * a real device. Look for pspec + * that have a matching p_atdev + * field. + * + * 3. unit is STAR and 'at' references + * an interface attribute. Look + * for pspec that have a matching + * p_iattr field. + */ + if ((unit != STAR && /* Case */ + !strcmp(j->i_at, at)) || /* 1 */ + (unit == STAR && + ((ad != NULL && /* Case */ + p->p_atdev == ad) || /* 2 */ + (ad == NULL && /* Case */ + p->p_iattr == a)))) /* 3 */ + stack = newnv(NULL, NULL, j, 0, + stack); + } + } + } + + for (nv = stack; nv != NULL; nv = nv->nv_next) + remove_devi(nv->nv_ptr); + nvfreel(stack); +} + +void +deldev(const char *name) +{ + size_t l; + struct devi *firsti, *i; + struct nvlist *nv, *stack = NULL; + + CFGDBG(5, "deselecting dev `%s'", name); + if (name[0] == '\0') + goto out; + + l = strlen(name) - 1; + if (name[l] == '*' || isdigit((unsigned char)name[l])) { + /* `no mydev0' or `no mydev*' */ + firsti = ht_lookup(devitab, name); + if (firsti == NULL) { +out: + cfgerror("unknown instance %s", name); + return; + } + for (i = firsti; i != NULL; i = i->i_alias) + stack = newnv(NULL, NULL, i, 0, stack); + } else { + struct devbase *d = ht_lookup(devbasetab, name); + + if (d == NULL) { + cfgerror("unknown device %s", name); + return; + } + if (d->d_ispseudo) { + cfgerror("%s is a pseudo-device; " + "use \"no pseudo-device %s\" instead", name, + name); + return; + } + + for (firsti = d->d_ihead; firsti != NULL; + firsti = firsti->i_bsame) + for (i = firsti; i != NULL; i = i->i_alias) + stack = newnv(NULL, NULL, i, 0, stack); + } + + for (nv = stack; nv != NULL; nv = nv->nv_next) + remove_devi(nv->nv_ptr); + nvfreel(stack); +} + +/* + * Insert given device "name" into devroottab. In case "name" + * designates a pure interface attribute, create a fake device + * instance for the attribute and insert that into the roottab + * (this scheme avoids mucking around with the orphanage analysis). + */ +void +addpseudoroot(const char *name) +{ + char buf[NAMESIZE]; + int unit; + struct attr *attr; + struct devi *i; + struct deva *iba; + struct devbase *ib; + + if (split(name, strlen(name), buf, sizeof(buf), &unit)) { + cfgerror("invalid pseudo-root name `%s'", name); + return; + } + + /* + * Prefer device because devices with locators define an + * implicit interface attribute. However, if a device is + * not available, try to attach to the interface attribute. + * This makes sure adddev() doesn't get confused when we + * are really attaching to a device (alternatively we maybe + * could specify a non-NULL atlist to defdevattach() below). + */ + ib = ht_lookup(devbasetab, intern(buf)); + if (ib == NULL) { + struct devbase *fakedev; + char fakename[NAMESIZE]; + + attr = ht_lookup(attrtab, intern(buf)); + if (!(attr && attr->a_iattr)) { + cfgerror("pseudo-root `%s' not available", name); + return; + } + + /* + * here we cheat a bit: create a fake devbase with the + * interface attribute and instantiate it. quick, cheap, + * dirty & bad for you, much like the stuff in the fridge. + * and, it works, since the pseudoroot device is not included + * in ioconf, just used by config to make sure we start from + * the right place. + */ + snprintf(fakename, sizeof(fakename), "%s_devattrs", buf); + fakedev = getdevbase(intern(fakename)); + fakedev->d_isdef = 1; + fakedev->d_ispseudo = 0; + fakedev->d_attrs = attrlist_cons(NULL, attr); + defdevattach(NULL, fakedev, NULL, NULL); + + if (unit == STAR) + snprintf(buf, sizeof(buf), "%s*", fakename); + else + snprintf(buf, sizeof(buf), "%s%d", fakename, unit); + name = buf; + } + + /* ok, everything should be set up, so instantiate a fake device */ + i = getdevi(name); + if (i == NULL) + panic("device `%s' expected to be present", name); + ib = i->i_base; + iba = ib->d_ahead; + + i->i_atdeva = iba; + i->i_cfflags = 0; + i->i_locs = fixloc(name, &errattr, NULL); + i->i_pseudoroot = 1; + i->i_active = DEVI_ORPHAN; /* set active by kill_orphans() */ + + *iba->d_ipp = i; + iba->d_ipp = &i->i_asame; + + ht_insert(devroottab, ib->d_name, ib); +} + +void +addpseudo(const char *name, int number) +{ + struct devbase *d; + struct devi *i; + + d = ht_lookup(devbasetab, name); + if (d == NULL) { + cfgerror("undefined pseudo-device %s", name); + return; + } + if (!d->d_ispseudo) { + cfgerror("%s is a real device, not a pseudo-device", name); + return; + } + if (ht_lookup(devitab, name) != NULL) { + cfgerror("`%s' already defined", name); + return; + } + i = newdevi(name, number - 1, d); /* foo 16 => "foo0..foo15" */ + if (ht_insert(devitab, name, i)) + panic("addpseudo(%s)", name); + /* Useful to retrieve the instance from the devbase */ + d->d_ihead = i; + i->i_active = DEVI_ACTIVE; + TAILQ_INSERT_TAIL(&allpseudo, i, i_next); +} + +void +delpseudo(const char *name) +{ + struct devbase *d; + struct devi *i; + + CFGDBG(5, "deselecting pseudo `%s'", name); + d = ht_lookup(devbasetab, name); + if (d == NULL) { + cfgerror("undefined pseudo-device %s", name); + return; + } + if (!d->d_ispseudo) { + cfgerror("%s is a real device, not a pseudo-device", name); + return; + } + if ((i = ht_lookup(devitab, name)) == NULL) { + cfgerror("`%s' not defined", name); + return; + } + d->d_umax = 0; /* clear neads-count entries */ + d->d_ihead = NULL; /* make sure it won't be considered active */ + TAILQ_REMOVE(&allpseudo, i, i_next); + if (ht_remove(devitab, name)) + panic("delpseudo(%s) - can't remove from devitab", name); + if (ht_insert(deaddevitab, name, i)) + panic("delpseudo(%s) - can't add to deaddevitab", name); +} + +void +adddevm(const char *name, devmajor_t cmajor, devmajor_t bmajor, + struct condexpr *cond, struct nvlist *nv_nodes) +{ + struct devm *dm; + + if (cmajor != NODEVMAJOR && (cmajor < 0 || cmajor >= 4096)) { + cfgerror("character major %d is invalid", cmajor); + condexpr_destroy(cond); + nvfreel(nv_nodes); + return; + } + + if (bmajor != NODEVMAJOR && (bmajor < 0 || bmajor >= 4096)) { + cfgerror("block major %d is invalid", bmajor); + condexpr_destroy(cond); + nvfreel(nv_nodes); + return; + } + if (cmajor == NODEVMAJOR && bmajor == NODEVMAJOR) { + cfgerror("both character/block majors are not specified"); + condexpr_destroy(cond); + nvfreel(nv_nodes); + return; + } + + dm = ecalloc(1, sizeof(*dm)); + dm->dm_srcfile = yyfile; + dm->dm_srcline = currentline(); + dm->dm_name = name; + dm->dm_cmajor = cmajor; + dm->dm_bmajor = bmajor; + dm->dm_opts = cond; + dm->dm_devnodes = nv_nodes; + + TAILQ_INSERT_TAIL(&alldevms, dm, dm_next); + + maxcdevm = MAX(maxcdevm, dm->dm_cmajor); + maxbdevm = MAX(maxbdevm, dm->dm_bmajor); +} + +int +fixdevis(void) +{ + struct devi *i; + int error = 0; + + TAILQ_FOREACH(i, &alldevi, i_next) { + CFGDBG(3, "fixing devis `%s'", i->i_name); + if (i->i_active == DEVI_ACTIVE) + selectbase(i->i_base, i->i_atdeva); + else if (i->i_active == DEVI_ORPHAN) { + /* + * At this point, we can't have instances for which + * i_at or i_pspec are NULL. + */ + ++error; + cfgxerror(i->i_srcfile, i->i_lineno, + "`%s at %s' is orphaned (%s `%s' found)", + i->i_name, i->i_at, i->i_pspec->p_atunit == WILD ? + "nothing matching" : "no", i->i_at); + } else if (vflag && i->i_active == DEVI_IGNORED) + cfgxwarn(i->i_srcfile, i->i_lineno, "ignoring " + "explicitly orphaned instance `%s at %s'", + i->i_name, i->i_at); + } + + if (error) + return error; + + TAILQ_FOREACH(i, &allpseudo, i_next) + if (i->i_active == DEVI_ACTIVE) + selectbase(i->i_base, NULL); + return 0; +} + +/* + * Look up a parent spec, creating a new one if it does not exist. + */ +static struct pspec * +getpspec(struct attr *attr, struct devbase *ab, int atunit) +{ + struct pspec *p; + + TAILQ_FOREACH(p, &allpspecs, p_list) { + if (p->p_iattr == attr && + p->p_atdev == ab && + p->p_atunit == atunit) + return (p); + } + + p = ecalloc(1, sizeof(*p)); + + p->p_iattr = attr; + p->p_atdev = ab; + p->p_atunit = atunit; + p->p_inst = npspecs++; + p->p_active = 0; + + TAILQ_INSERT_TAIL(&allpspecs, p, p_list); + + return (p); +} + +/* + * Define a new instance of a specific device. + */ +static struct devi * +getdevi(const char *name) +{ + struct devi *i, *firsti; + struct devbase *d; + int unit; + char base[NAMESIZE]; + + if (split(name, strlen(name), base, sizeof base, &unit)) { + cfgerror("invalid device name `%s'", name); + return (NULL); + } + d = ht_lookup(devbasetab, intern(base)); + if (d == NULL) { + cfgerror("%s: unknown device `%s'", name, base); + return (NULL); + } + if (d->d_ispseudo) { + cfgerror("%s: %s is a pseudo-device", name, base); + return (NULL); + } + firsti = ht_lookup(devitab, name); + i = newdevi(name, unit, d); + if (firsti == NULL) { + if (ht_insert(devitab, name, i)) + panic("getdevi(%s)", name); + *d->d_ipp = i; + d->d_ipp = &i->i_bsame; + } else { + while (firsti->i_alias) + firsti = firsti->i_alias; + firsti->i_alias = i; + } + TAILQ_INSERT_TAIL(&alldevi, i, i_next); + ndevi++; + return (i); +} + +static const char * +concat(const char *name, int c) +{ + size_t len; + char buf[NAMESIZE]; + + len = strlen(name); + if (len + 2 > sizeof(buf)) { + cfgerror("device name `%s%c' too long", name, c); + len = sizeof(buf) - 2; + } + memmove(buf, name, len); + buf[len] = (char)c; + buf[len + 1] = '\0'; + return (intern(buf)); +} + +const char * +starref(const char *name) +{ + + return (concat(name, '*')); +} + +const char * +wildref(const char *name) +{ + + return (concat(name, '?')); +} + +/* + * Split a name like "foo0" into base name (foo) and unit number (0). + * Return 0 on success. To make this useful for names like "foo0a", + * the length of the "foo0" part is one of the arguments. + */ +static int +split(const char *name, size_t nlen, char *base, size_t bsize, int *aunit) +{ + const char *cp; + int c; + size_t l; + + l = nlen; + if (l < 2 || l >= bsize || isdigit((unsigned char)*name)) + return (1); + c = (u_char)name[--l]; + if (!isdigit(c)) { + if (c == '*') + *aunit = STAR; + else if (c == '?') + *aunit = WILD; + else + return (1); + } else { + cp = &name[l]; + while (isdigit((unsigned char)cp[-1])) + l--, cp--; + *aunit = atoi(cp); + } + memmove(base, name, l); + base[l] = 0; + return (0); +} + +void +addattr(const char *name) +{ + struct attr *a; + + a = refattr(name); + selectattr(a); +} + +void +delattr(const char *name) +{ + struct attr *a; + + a = refattr(name); + deselectattr(a); +} + +void +selectattr(struct attr *a) +{ + struct attrlist *al; + struct attr *dep; + + CFGDBG(5, "selecting attr `%s'", a->a_name); + for (al = a->a_deps; al != NULL; al = al->al_next) { + dep = al->al_this; + selectattr(dep); + } + if (ht_insert(selecttab, a->a_name, __UNCONST(a->a_name)) == 0) + nattrs++; + CFGDBG(3, "attr selected `%s'", a->a_name); +} + +static int +deselectattrcb2(const char *name1, const char *name2, void *v, void *arg) +{ + const char *name = arg; + + if (strcmp(name, name2) == 0) + delattr(name1); + return 0; +} + +void +deselectattr(struct attr *a) +{ + + CFGDBG(5, "deselecting attr `%s'", a->a_name); + ht_enumerate2(attrdeptab, deselectattrcb2, __UNCONST(a->a_name)); + if (ht_remove(selecttab, a->a_name) == 0) + nattrs--; + CFGDBG(3, "attr deselected `%s'", a->a_name); +} + +static int +dumpattrdepcb2(const char *name1, const char *name2, void *v, void *arg) +{ + + CFGDBG(3, "attr `%s' depends on attr `%s'", name1, name2); + return 0; +} + +void +dependattrs(void) +{ + + ht_enumerate2(attrdeptab, dumpattrdepcb2, NULL); +} + +/* + * We have an instance of the base foo, so select it and all its + * attributes for "optional foo". + */ +static void +selectbase(struct devbase *d, struct deva *da) +{ + struct attr *a; + struct attrlist *al; + + (void)ht_insert(selecttab, d->d_name, __UNCONST(d->d_name)); + CFGDBG(3, "devbase selected `%s'", d->d_name); + CFGDBG(5, "selecting dependencies of devbase `%s'", d->d_name); + for (al = d->d_attrs; al != NULL; al = al->al_next) { + a = al->al_this; + expandattr(a, selectattr); + } + + struct attr *devattr; + devattr = refattr(d->d_name); + expandattr(devattr, selectattr); + + if (da != NULL) { + (void)ht_insert(selecttab, da->d_name, __UNCONST(da->d_name)); + CFGDBG(3, "devattr selected `%s'", da->d_name); + for (al = da->d_attrs; al != NULL; al = al->al_next) { + a = al->al_this; + expandattr(a, selectattr); + } + } + + fixdev(d); +} + +/* + * Is the given pointer on the given list of pointers? + */ +int +onlist(struct nvlist *nv, void *ptr) +{ + for (; nv != NULL; nv = nv->nv_next) + if (nv->nv_ptr == ptr) + return (1); + return (0); +} + +static char * +extend(char *p, const char *name) +{ + size_t l; + + l = strlen(name); + memmove(p, name, l); + p += l; + *p++ = ','; + *p++ = ' '; + return (p); +} + +/* + * Check that we got all required locators, and default any that are + * given as "?" and have defaults. Return 0 on success. + */ +static const char ** +fixloc(const char *name, struct attr *attr, struct loclist *got) +{ + struct loclist *m, *n; + int ord; + const char **lp; + int nmissing, nextra, nnodefault; + char *mp, *ep, *ndp; + char missing[1000], extra[1000], nodefault[1000]; + static const char *nullvec[1]; + + /* + * Look for all required locators, and number the given ones + * according to the required order. While we are numbering, + * set default values for defaulted locators. + */ + if (attr->a_loclen == 0) /* e.g., "at root" */ + lp = nullvec; + else + lp = emalloc((size_t)(attr->a_loclen + 1) * sizeof(const char *)); + for (n = got; n != NULL; n = n->ll_next) + n->ll_num = -1; + nmissing = 0; + mp = missing; + /* yes, this is O(mn), but m and n should be small */ + for (ord = 0, m = attr->a_locs; m != NULL; m = m->ll_next, ord++) { + for (n = got; n != NULL; n = n->ll_next) { + if (n->ll_name == m->ll_name) { + n->ll_num = ord; + break; + } + } + if (n == NULL && m->ll_num == 0) { + nmissing++; + mp = extend(mp, m->ll_name); + } + lp[ord] = m->ll_string; + } + if (ord != attr->a_loclen) + panic("fixloc"); + lp[ord] = NULL; + nextra = 0; + ep = extra; + nnodefault = 0; + ndp = nodefault; + for (n = got; n != NULL; n = n->ll_next) { + if (n->ll_num >= 0) { + if (n->ll_string != NULL) + lp[n->ll_num] = n->ll_string; + else if (lp[n->ll_num] == NULL) { + nnodefault++; + ndp = extend(ndp, n->ll_name); + } + } else { + nextra++; + ep = extend(ep, n->ll_name); + } + } + if (nextra) { + ep[-2] = 0; /* kill ", " */ + cfgerror("%s: extraneous locator%s: %s", + name, nextra > 1 ? "s" : "", extra); + } + if (nmissing) { + mp[-2] = 0; + cfgerror("%s: must specify %s", name, missing); + } + if (nnodefault) { + ndp[-2] = 0; + cfgerror("%s: cannot wildcard %s", name, nodefault); + } + if (nmissing || nnodefault) { + free(lp); + lp = NULL; + } + return (lp); +} + +void +setversion(int newver) +{ + if (newver > CONFIG_VERSION) + cfgerror("your sources require a newer version of config(1) " + "-- please rebuild it."); + else if (newver < CONFIG_MINVERSION) + cfgerror("your sources are out of date -- please update."); + else + version = newver; +} diff --git a/buildrump.sh/src/usr.bin/config/sem.h b/buildrump.sh/src/usr.bin/config/sem.h new file mode 100644 index 00000000..43e3bc4e --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/sem.h @@ -0,0 +1,91 @@ +/* $NetBSD: sem.h,v 1.19 2014/11/21 20:46:56 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)sem.h 8.1 (Berkeley) 6/6/93 + */ + +void enddefs(void); + +void setversion(int); +void setdefmaxusers(int, int, int); +void setmaxusers(int); +void setident(const char *); +int defattr0(const char *, struct loclist *, struct attrlist *, int); +int defattr(const char *, struct loclist *, struct attrlist *, int); +int defiattr(const char *, struct loclist *, struct attrlist *, int); +int defdevclass(const char *, struct loclist *, struct attrlist *, int); +void defdev(struct devbase *, struct loclist *, struct attrlist *, int); +void defdevattach(struct deva *, struct devbase *, struct nvlist *, + struct attrlist *); +struct devbase *getdevbase(const char *); +struct deva *getdevattach(const char *); +struct attr *mkattr(const char *); +struct attr *getattr(const char *); +struct attr *refattr(const char *); +int getrefattr(const char *, struct attr **); +void expandattr(struct attr *, void (*)(struct attr *)); +void addattr(const char *); +void delattr(const char *); +void selectattr(struct attr *); +void deselectattr(struct attr *); +void dependattrs(void); +void setmajor(struct devbase *, int); +void addconf(struct config *); +void setconf(struct nvlist **, const char *, struct nvlist *); +void delconf(const char *); +void setfstype(const char **, const char *); +void adddev(const char *, const char *, struct loclist *, int); +void deldevi(const char *, const char *); +void deldeva(const char *); +void deldev(const char *); +void addpseudo(const char *, int); +void delpseudo(const char *); +void addpseudoroot(const char *); +void adddevm(const char *, devmajor_t, devmajor_t, + struct condexpr *, struct nvlist *); +int fixdevis(void); +const char *ref(const char *); +const char *starref(const char *); +const char *wildref(const char *); +int has_attr(struct attrlist *, const char *); + +extern const char *s_qmark; +extern const char *s_none; +extern const char *s_ifnet; +extern size_t nattrs; diff --git a/buildrump.sh/src/usr.bin/config/util.c b/buildrump.sh/src/usr.bin/config/util.c new file mode 100644 index 00000000..78dc9183 --- /dev/null +++ b/buildrump.sh/src/usr.bin/config/util.c @@ -0,0 +1,545 @@ +/* $NetBSD: util.c,v 1.19 2014/10/29 17:14:50 christos Exp $ */ + +/* + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This software was developed by the Computer Systems Engineering group + * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66 and + * contributed to Berkeley. + * + * All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Lawrence Berkeley Laboratories. + * + * 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. + * + * from: @(#)util.c 8.1 (Berkeley) 6/6/93 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: util.c,v 1.19 2014/10/29 17:14:50 christos Exp $"); + +#include <sys/types.h> +#include <assert.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> +#include <util.h> +#include <err.h> +#include "defs.h" + +static void cfgvxerror(const char *, int, const char *, va_list) + __printflike(3, 0); +static void cfgvxdbg(const char *, int, const char *, va_list) + __printflike(3, 0); +static void cfgvxwarn(const char *, int, const char *, va_list) + __printflike(3, 0); +static void cfgvxmsg(const char *, int, const char *, const char *, va_list) + __printflike(4, 0); + +/************************************************************/ + +/* + * Prefix stack + */ + +/* + * Push a prefix onto the prefix stack. + */ +void +prefix_push(const char *path) +{ + struct prefix *pf; + char *cp; + + pf = ecalloc(1, sizeof(struct prefix)); + + if (! SLIST_EMPTY(&prefixes) && *path != '/') { + cp = emalloc(strlen(SLIST_FIRST(&prefixes)->pf_prefix) + 1 + + strlen(path) + 1); + (void) sprintf(cp, "%s/%s", + SLIST_FIRST(&prefixes)->pf_prefix, path); + pf->pf_prefix = intern(cp); + free(cp); + } else + pf->pf_prefix = intern(path); + + SLIST_INSERT_HEAD(&prefixes, pf, pf_next); +} + +/* + * Pop a prefix off the prefix stack. + */ +void +prefix_pop(void) +{ + struct prefix *pf; + + if ((pf = SLIST_FIRST(&prefixes)) == NULL) { + cfgerror("no prefixes on the stack to pop"); + return; + } + + SLIST_REMOVE_HEAD(&prefixes, pf_next); + /* Remember this prefix for emitting -I... directives later. */ + SLIST_INSERT_HEAD(&allprefixes, pf, pf_next); +} + +/* + * Prepend the source path to a file name. + */ +char * +sourcepath(const char *file) +{ + size_t len; + char *cp; + struct prefix *pf; + + pf = SLIST_EMPTY(&prefixes) ? NULL : SLIST_FIRST(&prefixes); + if (pf != NULL && *pf->pf_prefix == '/') + len = strlen(pf->pf_prefix) + 1 + strlen(file) + 1; + else { + len = strlen(srcdir) + 1 + strlen(file) + 1; + if (pf != NULL) + len += strlen(pf->pf_prefix) + 1; + } + + cp = emalloc(len); + + if (pf != NULL) { + if (*pf->pf_prefix == '/') + (void) sprintf(cp, "%s/%s", pf->pf_prefix, file); + else + (void) sprintf(cp, "%s/%s/%s", srcdir, + pf->pf_prefix, file); + } else + (void) sprintf(cp, "%s/%s", srcdir, file); + return (cp); +} + +/************************************************************/ + +/* + * Data structures + */ + +/* + * nvlist + */ + +struct nvlist * +newnv(const char *name, const char *str, void *ptr, long long i, struct nvlist *next) +{ + struct nvlist *nv; + + nv = ecalloc(1, sizeof(*nv)); + nv->nv_next = next; + nv->nv_name = name; + nv->nv_str = str; + nv->nv_ptr = ptr; + nv->nv_num = i; + return nv; +} + +/* + * Free an nvlist structure (just one). + */ +void +nvfree(struct nvlist *nv) +{ + + free(nv); +} + +/* + * Free an nvlist (the whole list). + */ +void +nvfreel(struct nvlist *nv) +{ + struct nvlist *next; + + for (; nv != NULL; nv = next) { + next = nv->nv_next; + free(nv); + } +} + +struct nvlist * +nvcat(struct nvlist *nv1, struct nvlist *nv2) +{ + struct nvlist *nv; + + if (nv1 == NULL) + return nv2; + + for (nv = nv1; nv->nv_next != NULL; nv = nv->nv_next); + + nv->nv_next = nv2; + return nv1; +} + +/* + * Option definition lists + */ + +struct defoptlist * +defoptlist_create(const char *name, const char *val, const char *lintval) +{ + struct defoptlist *dl; + + dl = emalloc(sizeof(*dl)); + dl->dl_next = NULL; + dl->dl_name = name; + dl->dl_value = val; + dl->dl_lintvalue = lintval; + dl->dl_obsolete = 0; + dl->dl_depends = NULL; + return dl; +} + +void +defoptlist_destroy(struct defoptlist *dl) +{ + struct defoptlist *next; + + while (dl != NULL) { + next = dl->dl_next; + dl->dl_next = NULL; + + // XXX should we assert that dl->dl_deps is null to + // be sure the deps have already been destroyed? + free(dl); + + dl = next; + } +} + +struct defoptlist * +defoptlist_append(struct defoptlist *dla, struct defoptlist *dlb) +{ + struct defoptlist *dl; + + if (dla == NULL) + return dlb; + + for (dl = dla; dl->dl_next != NULL; dl = dl->dl_next) + ; + + dl->dl_next = dlb; + return dla; +} + +/* + * Locator lists + */ + +struct loclist * +loclist_create(const char *name, const char *string, long long num) +{ + struct loclist *ll; + + ll = emalloc(sizeof(*ll)); + ll->ll_name = name; + ll->ll_string = string; + ll->ll_num = num; + ll->ll_next = NULL; + return ll; +} + +void +loclist_destroy(struct loclist *ll) +{ + struct loclist *next; + + while (ll != NULL) { + next = ll->ll_next; + ll->ll_next = NULL; + free(ll); + ll = next; + } +} + +/* + * Attribute lists + */ + +struct attrlist * +attrlist_create(void) +{ + struct attrlist *al; + + al = emalloc(sizeof(*al)); + al->al_next = NULL; + al->al_this = NULL; + return al; +} + +struct attrlist * +attrlist_cons(struct attrlist *next, struct attr *a) +{ + struct attrlist *al; + + al = attrlist_create(); + al->al_next = next; + al->al_this = a; + return al; +} + +void +attrlist_destroy(struct attrlist *al) +{ + assert(al->al_next == NULL); + assert(al->al_this == NULL); + free(al); +} + +void +attrlist_destroyall(struct attrlist *al) +{ + struct attrlist *next; + + while (al != NULL) { + next = al->al_next; + al->al_next = NULL; + /* XXX should we make the caller guarantee this? */ + al->al_this = NULL; + attrlist_destroy(al); + al = next; + } +} + +/* + * Condition expressions + */ + +/* + * Create an expression node. + */ +struct condexpr * +condexpr_create(enum condexpr_types type) +{ + struct condexpr *cx; + + cx = emalloc(sizeof(*cx)); + cx->cx_type = type; + switch (type) { + + case CX_ATOM: + cx->cx_atom = NULL; + break; + + case CX_NOT: + cx->cx_not = NULL; + break; + + case CX_AND: + cx->cx_and.left = NULL; + cx->cx_and.right = NULL; + break; + + case CX_OR: + cx->cx_or.left = NULL; + cx->cx_or.right = NULL; + break; + + default: + panic("condexpr_create: invalid expr type %d", (int)type); + } + return cx; +} + +/* + * Free an expression tree. + */ +void +condexpr_destroy(struct condexpr *expr) +{ + switch (expr->cx_type) { + + case CX_ATOM: + /* nothing */ + break; + + case CX_NOT: + condexpr_destroy(expr->cx_not); + break; + + case CX_AND: + condexpr_destroy(expr->cx_and.left); + condexpr_destroy(expr->cx_and.right); + break; + + case CX_OR: + condexpr_destroy(expr->cx_or.left); + condexpr_destroy(expr->cx_or.right); + break; + + default: + panic("condexpr_destroy: invalid expr type %d", + (int)expr->cx_type); + } + free(expr); +} + +/************************************************************/ + +/* + * Diagnostic messages + */ + +void +cfgdbg(const char *fmt, ...) +{ + va_list ap; + extern const char *yyfile; + + va_start(ap, fmt); + cfgvxdbg(yyfile, currentline(), fmt, ap); + va_end(ap); +} + +void +cfgwarn(const char *fmt, ...) +{ + va_list ap; + extern const char *yyfile; + + va_start(ap, fmt); + cfgvxwarn(yyfile, currentline(), fmt, ap); + va_end(ap); +} + +void +cfgxwarn(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + cfgvxwarn(file, line, fmt, ap); + va_end(ap); +} + +static void +cfgvxdbg(const char *file, int line, const char *fmt, va_list ap) +{ + cfgvxmsg(file, line, "debug: ", fmt, ap); +} + +static void +cfgvxwarn(const char *file, int line, const char *fmt, va_list ap) +{ + cfgvxmsg(file, line, "warning: ", fmt, ap); +} + +/* + * External (config file) error. Complain, using current file + * and line number. + */ +void +cfgerror(const char *fmt, ...) +{ + va_list ap; + extern const char *yyfile; + + va_start(ap, fmt); + cfgvxerror(yyfile, currentline(), fmt, ap); + va_end(ap); +} + +/* + * Delayed config file error (i.e., something was wrong but we could not + * find out about it until later). + */ +void +cfgxerror(const char *file, int line, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + cfgvxerror(file, line, fmt, ap); + va_end(ap); +} + +/* + * Internal form of error() and xerror(). + */ +static void +cfgvxerror(const char *file, int line, const char *fmt, va_list ap) +{ + cfgvxmsg(file, line, "", fmt, ap); + errors++; +} + + +/* + * Internal error, abort. + */ +__dead void +panic(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)fprintf(stderr, "%s: panic: ", getprogname()); + (void)vfprintf(stderr, fmt, ap); + (void)putc('\n', stderr); + va_end(ap); + exit(2); +} + +/* + * Internal form of error() and xerror(). + */ +static void +cfgvxmsg(const char *file, int line, const char *msgclass, const char *fmt, + va_list ap) +{ + + (void)fprintf(stderr, "%s:%d: %s", file, line, msgclass); + (void)vfprintf(stderr, fmt, ap); + (void)putc('\n', stderr); +} + +void +autogen_comment(FILE *fp, const char *targetfile) +{ + + (void)fprintf(fp, + "/*\n" + " * MACHINE GENERATED: DO NOT EDIT\n" + " *\n" + " * %s, from \"%s\"\n" + " */\n\n", + targetfile, conffile); +} diff --git a/buildrump.sh/src/usr.bin/genassym/Makefile b/buildrump.sh/src/usr.bin/genassym/Makefile new file mode 100644 index 00000000..74de3c7e --- /dev/null +++ b/buildrump.sh/src/usr.bin/genassym/Makefile @@ -0,0 +1,6 @@ +# $NetBSD: Makefile,v 1.1 2005/06/05 18:19:54 thorpej Exp $ + +MAN= genassym.1 +SCRIPTS= genassym.sh + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/genassym/genassym.1 b/buildrump.sh/src/usr.bin/genassym/genassym.1 new file mode 100644 index 00000000..70128492 --- /dev/null +++ b/buildrump.sh/src/usr.bin/genassym/genassym.1 @@ -0,0 +1,92 @@ +.\" $NetBSD: genassym.1,v 1.5 2010/04/13 09:01:10 jruoho Exp $ +.\" +.\" Copyright (c) 1997 Matthias Pfaller. +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +.\" +.Dd April 13, 2010 +.Dt GENASSYM 1 +.Os +.Sh NAME +.Nm genassym +.Nd emit an assym.h file +.Sh SYNOPSIS +.Nm genassym +.Op Fl c +.Op Fl f +.Ar C compiler invocation +.Sh DESCRIPTION +.Nm +is a shell script normally used during the kernel build process to +create an assym.h file. +This file defines a number of cpp constants derived from the configuration +information +.Nm +reads from stdin. The generated file is used by kernel sources +written in assembler to gain access to information (e.g. structure +offsets and sizes) normally only known to the C compiler. +.Pp +Arguments to +.Nm +are usually of the form +.Ar ${CC} ${CFLAGS} ${CPPFLAGS} +where +.Ar ${CC} +is the C compiler used to compile the kernel, while +.Ar ${CFLAGS} +and +.Ar ${CPPFLAGS} +are flag arguments to the C compiler. The script creates a C source file +from its input. Then the C compiler is called according to the script's +arguments to compile this file. +.Pp +Normally +.Nm +instructs the C compiler to create an assembler source from the constructed +C source. The resulting file is then processed to extract the information +needed to create the assym.h file. The +.Fl c +flag instructs +.Nm +to create slightly different code, generate an executable from this code +and run it. In both cases the assym.h file is written to stdout. +The +.Fl f +flag instructs +.Nm +to create forth code. +.Sh DIAGNOSTICS +Either self-explanatory, or generated by one of the programs +called from the script. +.Sh SEE ALSO +.Xr genassym.cf 5 +.Sh HISTORY +The +.Nm +command appeared in +.Nx 1.3 +as +.Dq genassym.sh +in +.Pa /usr/src/sys/kern . +It became a userland utility in +.Nx 4.0 . diff --git a/buildrump.sh/src/usr.bin/genassym/genassym.sh b/buildrump.sh/src/usr.bin/genassym/genassym.sh new file mode 100644 index 00000000..945f6948 --- /dev/null +++ b/buildrump.sh/src/usr.bin/genassym/genassym.sh @@ -0,0 +1,214 @@ +#!/bin/sh - +# $NetBSD: genassym.sh,v 1.8 2014/01/06 22:43:15 christos Exp $ +# +# Copyright (c) 1997 Matthias Pfaller. +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +# + +progname="$(basename "${0}")" +: ${AWK:=awk} + +ccode=0 # generate temporary C file, compile it, execute result +fcode=0 # generate Forth code + +usage() +{ + + echo "usage: ${progname} [-c | -f] -- compiler command" >&2 +} + +set -e + +while getopts cf i +do + case "$i" in + c) + ccode=1 + ;; + f) + fcode=1 + ;; + esac +done +shift "$(($OPTIND - 1))" +if [ $# -eq 0 ]; then + usage + exit 1 +fi + +# Deal with any leading environment settings.. + +while [ -n "$1" ] +do + case "$1" in + *=*) + eval export "$1" + shift + ;; + *) + break + ;; + esac +done + +genassym_temp="$(mktemp -d "${TMPDIR-/tmp}/genassym.XXXXXX")" + + +if [ ! -d $genassym_temp ]; then + echo "${progname}: unable to create temporary directory" >&2 + exit 1 +fi +trap "rm -rf $genassym_temp" 0 1 2 3 15 + +$AWK ' +BEGIN { + printf("#if __GNUC__ >= 4\n"); + printf("#define offsetof(type, member) __builtin_offsetof(type, member)\n"); + printf("#else\n"); + printf("#define offsetof(type, member) ((size_t)(&((type *)0)->member))\n"); + printf("#endif\n"); + defining = 0; + type = "long"; + asmtype = "n"; + asmprint = ""; +} + +{ + doing_member = 0; +} + +$0 ~ /^[ \t]*#.*/ || $0 ~ /^[ \t]*$/ { + # Just ignore comments and empty lines + next; +} + +$0 ~ /^config[ \t]/ { + type = $2; + asmtype = $3; + asmprint = $4; + next; +} + +/^include[ \t]/ { + if (defining != 0) { + defining = 0; + printf("}\n"); + } + printf("#%s\n", $0); + next; +} + +$0 ~ /^if[ \t]/ || +$0 ~ /^ifdef[ \t]/ || +$0 ~ /^ifndef[ \t]/ || +$0 ~ /^else/ || +$0 ~ /^elif[ \t]/ || +$0 ~ /^endif/ { + printf("#%s\n", $0); + next; +} + +/^struct[ \t]/ { + structname = $2; + $0 = "define " structname "_SIZEOF sizeof(struct " structname ")"; + # fall through +} + +/^member[ \t]/ { + if (NF > 2) + $0 = "define " $2 " offsetof(struct " structname ", " $3 ")"; + else + $0 = "define " $2 " offsetof(struct " structname ", " $2 ")"; + doing_member = 1; + # fall through +} + +/^export[ \t]/ { + $0 = "define " $2 " " $2; + # fall through +} + +/^define[ \t]/ { + if (defining == 0) { + defining = 1; + printf("void f" FNR "(void);\n"); + printf("void f" FNR "(void) {\n"); + if (ccode) + call[FNR] = "f" FNR; + defining = 1; + } + value = $0 + gsub("^define[ \t]+[A-Za-z_][A-Za-z_0-9]*[ \t]+", "", value) + if (ccode) + printf("printf(\"#define " $2 " %%ld\\n\", (%s)" value ");\n", type); + else if (fcode) { + if (doing_member) + printf("__asm(\"XYZZY : %s d# %%%s0 + ;\" : : \"%s\" (%s));\n", $2, asmprint, asmtype, value); + else + printf("__asm(\"XYZZY d# %%%s0 constant %s\" : : \"%s\" (%s));\n", asmprint, $2, asmtype, value); + } else + printf("__asm(\"XYZZY %s %%%s0\" : : \"%s\" (%s));\n", $2, asmprint, asmtype, value); + next; +} + +/^quote[ \t]/ { + gsub("^quote[ \t]+", ""); + print; + next; +} + +{ + printf("syntax error in line %d\n", FNR) >"/dev/stderr"; + exit(1); +} + +END { + if (defining != 0) { + defining = 0; + printf("}\n"); + } + if (ccode) { + printf("int main(int argc, char **argv) {"); + for (i in call) + printf(call[i] "();"); + printf("return(0); }\n"); + } +} +' ccode="$ccode" fcode="$fcode" > "${genassym_temp}/assym.c" || exit 1 + +if [ "$ccode" = 1 ]; then + "$@" "${genassym_temp}/assym.c" -o "${genassym_temp}/genassym" && \ + "${genassym_temp}/genassym" +elif [ "$fcode" = 1 ]; then + # Kill all of the "#" and "$" modifiers; locore.s already + # prepends the correct "constant" modifier. + "$@" -S "${genassym_temp}/assym.c" -o - | sed -e 's/\$//g' | \ + sed -n 's/.*XYZZY//gp' +else + # Kill all of the "#" and "$" modifiers; locore.s already + # prepends the correct "constant" modifier. + "$@" -S "${genassym_temp}/assym.c" -o - > \ + "${genassym_temp}/genassym.out" && \ + sed -e 's/#//g' -e 's/\$//g' < "${genassym_temp}/genassym.out" | \ + sed -n 's/.*XYZZY/#define/gp' +fi diff --git a/buildrump.sh/src/usr.bin/join/Makefile b/buildrump.sh/src/usr.bin/join/Makefile new file mode 100644 index 00000000..bb68cbea --- /dev/null +++ b/buildrump.sh/src/usr.bin/join/Makefile @@ -0,0 +1,6 @@ +# $NetBSD: Makefile,v 1.4 1997/10/19 03:32:11 lukem Exp $ +# from: @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= join + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/join/join.1 b/buildrump.sh/src/usr.bin/join/join.1 new file mode 100644 index 00000000..db48ef30 --- /dev/null +++ b/buildrump.sh/src/usr.bin/join/join.1 @@ -0,0 +1,209 @@ +.\" $NetBSD: join.1,v 1.13 2012/04/08 22:00:39 wiz Exp $ +.\" +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" from: @(#)join.1 8.3 (Berkeley) 4/28/95 +.\" $NetBSD: join.1,v 1.13 2012/04/08 22:00:39 wiz Exp $ +.\" +.Dd April 28, 1995 +.Dt JOIN 1 +.Os +.Sh NAME +.Nm join +.Nd relational database operator +.Sh SYNOPSIS +.Nm +.Op Fl a Ar file_number | Fl v Ar file_number +.Op Fl e Ar string +.Op Fl j Ar file_number field +.Op Fl o Ar list +.Op Fl t Ar char +.Op Fl \&1 Ar field +.Op Fl \&2 Ar field +.Ar file1 file2 +.Sh DESCRIPTION +The join utility performs an ``equality join'' on the specified files +and writes the result to the standard output. +The ``join field'' is the field in each file by which the files are compared. +The first field in each line is used by default. +There is one line in the output for each pair of lines in +.Ar file1 +and +.Ar file2 +which have identical join fields. +Each output line consists of the join field, the remaining fields from +.Ar file1 +and then the remaining fields from +.Ar file2 . +.Pp +The default field separators are tab and space characters. +In this case, multiple tabs and spaces count as a single field separator, +and leading tabs and spaces are ignored. +The default output field separator is a single space character. +.Pp +Many of the options use file and field numbers. +Both file numbers and field numbers are 1 based, i.e. the first file on +the command line is file number 1 and the first field is field number 1. +The following options are available: +.Bl -tag -width Fl +.It Fl a Ar file_number +In addition to the default output, produce a line for each unpairable +line in file +.Ar file_number . +(The argument to +.Fl a +must not be preceded by a space; see the +.Sx COMPATIBILITY +section.) +.It Fl e Ar string +Replace empty output fields with +.Ar string . +.It Fl o Ar list +The +.Fl o +option specifies the fields that will be output from each file for +each line with matching join fields. +Each element of +.Ar list +has the form +.Ql file_number.field , +where +.Ar file_number +is a file number and +.Ar field +is a field number. +The elements of list must be either comma (``,'') or whitespace separated. +(The latter requires quoting to protect it from the shell, or, a simpler +approach is to use multiple +.Fl o +options.) +.It Fl t Ar char +Use character +.Ar char +as a field delimiter for both input and output. +Every occurrence of +.Ar char +in a line is significant. +.It Fl v Ar file_number +Do not display the default output, but display a line for each unpairable +line in file +.Ar file_number . +The options +.Fl v Ar 1 +and +.Fl v Ar 2 +may be specified at the same time. +.It Fl 1 Ar field +Join on the +.Ar field Ns 'th +field of file 1. +.It Fl 2 Ar field +Join on the +.Ar field Ns 'th +field of file 2. +.El +.Pp +When the default field delimiter characters are used, the files to be joined +should be ordered in the collating sequence of +.Xr sort 1 , +using the +.Fl b +option, on the fields on which they are to be joined, otherwise +.Nm +may not report all field matches. +When the field delimiter characters are specified by the +.Fl t +option, the collating sequence should be the same as +.Xr sort 1 +without the +.Fl b +option. +.Pp +If one of the arguments +.Ar file1 +or +.Ar file2 +is ``-'', the standard input is used. +.Pp +The +.Nm +utility exits 0 on success, and \*[Gt]0 if an error occurs. +.Sh COMPATIBILITY +For compatibility with historic versions of +.Nm , +the following options are available: +.Bl -tag -width Fl +.It Fl a +In addition to the default output, produce a line for each unpairable line +in both file 1 and file 2. +(To distinguish between this and +.Fl a Ar file_number , +.Nm +currently requires that the latter not include any white space.) +.It Fl j1 Ar field +Join on the +.Ar field Ns 'th +field of file 1. +.It Fl j2 Ar field +Join on the +.Ar field Ns 'th +field of file 2. +.It Fl j Ar field +Join on the +.Ar field Ns 'th +field of both file 1 and file 2. +.It Fl o Ar list ... +Historical implementations of +.Nm +permitted multiple arguments to the +.Fl o +option. +These arguments were of the form ``file_number.field_number'' as described +for the current +.Fl o +option. +This has obvious difficulties in the presence of files named ``1.2''. +.El +.Pp +These options are available only so historic shell scripts don't require +modification and should not be used. +.Sh SEE ALSO +.Xr awk 1 , +.Xr comm 1 , +.Xr paste 1 , +.Xr sort 1 , +.Xr uniq 1 +.Sh STANDARDS +The +.Nm +command is expected to be +.St -p1003.2 +compatible. diff --git a/buildrump.sh/src/usr.bin/join/join.c b/buildrump.sh/src/usr.bin/join/join.c new file mode 100644 index 00000000..b377ec27 --- /dev/null +++ b/buildrump.sh/src/usr.bin/join/join.c @@ -0,0 +1,637 @@ +/* $NetBSD: join.c,v 1.31 2011/09/04 20:27:52 joerg Exp $ */ + +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Steve Hayman of Indiana University, Michiro Hikida and David + * Goodenough. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1991\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "from: @(#)join.c 5.1 (Berkeley) 11/18/91"; +#else +__RCSID("$NetBSD: join.c,v 1.31 2011/09/04 20:27:52 joerg Exp $"); +#endif +#endif /* not lint */ + +#include <sys/types.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* + * There's a structure per input file which encapsulates the state of the + * file. We repeatedly read lines from each file until we've read in all + * the consecutive lines from the file with a common join field. Then we + * compare the set of lines with an equivalent set from the other file. + */ +typedef struct { + char *line; /* line */ + u_long linealloc; /* line allocated count */ + char **fields; /* line field(s) */ + u_long fieldcnt; /* line field(s) count */ + u_long fieldalloc; /* line field(s) allocated count */ +} LINE; + +static char nolineline[1] = { '\0' }; +static LINE noline = {nolineline, 0, 0, 0, 0}; /* arg for outfield if no line to output */ + +typedef struct { + FILE *fp; /* file descriptor */ + u_long joinf; /* join field (-1, -2, -j) */ + int unpair; /* output unpairable lines (-a) */ + int number; /* 1 for file 1, 2 for file 2 */ + + LINE *set; /* set of lines with same field */ + u_long pushback; /* line on the stack */ + u_long setcnt; /* set count */ + u_long setalloc; /* set allocated count */ +} INPUT; + +static INPUT input1 = { NULL, 0, 0, 1, NULL, -1, 0, 0, }, + input2 = { NULL, 0, 0, 2, NULL, -1, 0, 0, }; + +typedef struct { + u_long fileno; /* file number */ + u_long fieldno; /* field number */ +} OLIST; + +static OLIST *olist; /* output field list */ +static u_long olistcnt; /* output field list count */ +static u_long olistalloc; /* output field allocated count */ + +static int joinout = 1; /* show lines with matched join fields (-v) */ +static int needsep; /* need separator character */ +static int spans = 1; /* span multiple delimiters (-t) */ +static char *empty; /* empty field replacement string (-e) */ +static const char *tabchar = " \t"; /* delimiter characters (-t) */ + +static int cmp(LINE *, u_long, LINE *, u_long); +__dead static void enomem(void); +static void fieldarg(char *); +static void joinlines(INPUT *, INPUT *); +static void obsolete(char **); +static void outfield(LINE *, u_long); +static void outoneline(INPUT *, LINE *); +static void outtwoline(INPUT *, LINE *, INPUT *, LINE *); +static void slurp(INPUT *); +__dead static void usage(void); + +int +main(int argc, char *argv[]) +{ + INPUT *F1, *F2; + int aflag, ch, cval, vflag; + char *end; + + F1 = &input1; + F2 = &input2; + + aflag = vflag = 0; + obsolete(argv); + while ((ch = getopt(argc, argv, "\01a:e:j:1:2:o:t:v:")) != -1) { + switch (ch) { + case '\01': + aflag = 1; + F1->unpair = F2->unpair = 1; + break; + case '1': + if ((F1->joinf = strtol(optarg, &end, 10)) < 1) { + warnx("-1 option field number less than 1"); + usage(); + } + if (*end) { + warnx("illegal field number -- %s", optarg); + usage(); + } + --F1->joinf; + break; + case '2': + if ((F2->joinf = strtol(optarg, &end, 10)) < 1) { + warnx("-2 option field number less than 1"); + usage(); + } + if (*end) { + warnx("illegal field number -- %s", optarg); + usage(); + } + --F2->joinf; + break; + case 'a': + aflag = 1; + switch(strtol(optarg, &end, 10)) { + case 1: + F1->unpair = 1; + break; + case 2: + F2->unpair = 1; + break; + default: + warnx("-a option file number not 1 or 2"); + usage(); + break; + } + if (*end) { + warnx("illegal file number -- %s", optarg); + usage(); + } + break; + case 'e': + empty = optarg; + break; + case 'j': + if ((F1->joinf = F2->joinf = + strtol(optarg, &end, 10)) < 1) { + warnx("-j option field number less than 1"); + usage(); + } + if (*end) { + warnx("illegal field number -- %s", optarg); + usage(); + } + --F1->joinf; + --F2->joinf; + break; + case 'o': + fieldarg(optarg); + break; + case 't': + spans = 0; + if (strlen(tabchar = optarg) != 1) { + warnx("illegal tab character specification"); + usage(); + } + break; + case 'v': + vflag = 1; + joinout = 0; + switch(strtol(optarg, &end, 10)) { + case 1: + F1->unpair = 1; + break; + case 2: + F2->unpair = 1; + break; + default: + warnx("-v option file number not 1 or 2"); + usage(); + break; + } + if (*end) { + warnx("illegal file number -- %s", optarg); + usage(); + } + break; + case '?': + default: + usage(); + } + } + argc -= optind; + argv += optind; + + if (aflag && vflag) + errx(1, "-a and -v options mutually exclusive"); + + if (argc != 2) + usage(); + + /* Open the files; "-" means stdin. */ + if (!strcmp(*argv, "-")) + F1->fp = stdin; + else if ((F1->fp = fopen(*argv, "r")) == NULL) + err(1, "%s", *argv); + ++argv; + if (!strcmp(*argv, "-")) + F2->fp = stdin; + else if ((F2->fp = fopen(*argv, "r")) == NULL) + err(1, "%s", *argv); + if (F1->fp == stdin && F2->fp == stdin) + errx(1, "only one input file may be stdin"); + + slurp(F1); + slurp(F2); + while (F1->setcnt && F2->setcnt) { + cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf); + if (cval == 0) { + /* Oh joy, oh rapture, oh beauty divine! */ + if (joinout) + joinlines(F1, F2); + slurp(F1); + slurp(F2); + } else if (cval < 0) { + /* File 1 takes the lead... */ + if (F1->unpair) + joinlines(F1, NULL); + slurp(F1); + } else { + /* File 2 takes the lead... */ + if (F2->unpair) + joinlines(F2, NULL); + slurp(F2); + } + } + + /* + * Now that one of the files is used up, optionally output any + * remaining lines from the other file. + */ + if (F1->unpair) + while (F1->setcnt) { + joinlines(F1, NULL); + slurp(F1); + } + if (F1->fp != stdin) + fclose(F1->fp); + + if (F2->unpair) + while (F2->setcnt) { + joinlines(F2, NULL); + slurp(F2); + } + if (F2->fp != stdin) + fclose(F2->fp); + + return 0; +} + +static void +slurp(INPUT *F) +{ + LINE *lp; + LINE tmp; + LINE *nline; + size_t len; + u_long cnt; + char *bp, *fieldp; + u_long nsize; + + /* + * Read all of the lines from an input file that have the same + * join field. + */ + for (F->setcnt = 0;; ++F->setcnt) { + /* + * If we're out of space to hold line structures, allocate + * more. Initialize the structure so that we know that this + * is new space. + */ + if (F->setcnt == F->setalloc) { + cnt = F->setalloc; + if (F->setalloc == 0) + nsize = 64; + else + nsize = F->setalloc << 1; + if ((nline = realloc(F->set, + nsize * sizeof(LINE))) == NULL) + enomem(); + F->set = nline; + F->setalloc = nsize; + memset(F->set + cnt, 0, + (F->setalloc - cnt) * sizeof(LINE)); + } + + /* + * Get any pushed back line, else get the next line. Allocate + * space as necessary. If taking the line from the stack swap + * the two structures so that we don't lose the allocated space. + * This could be avoided by doing another level of indirection, + * but it's probably okay as is. + */ + lp = &F->set[F->setcnt]; + if (F->pushback != (u_long)-1) { + tmp = F->set[F->setcnt]; + F->set[F->setcnt] = F->set[F->pushback]; + F->set[F->pushback] = tmp; + F->pushback = (u_long)-1; + continue; + } + if ((bp = fgetln(F->fp, &len)) == NULL) + return; + if (lp->linealloc <= len + 1) { + char *n; + + if (lp->linealloc == 0) + nsize = 128; + else + nsize = lp->linealloc; + while (nsize <= len + 1) + nsize <<= 1; + if ((n = realloc(lp->line, + nsize * sizeof(char))) == NULL) + enomem(); + lp->line = n; + lp->linealloc = nsize; + } + memmove(lp->line, bp, len); + + /* Replace trailing newline, if it exists. */ + if (bp[len - 1] == '\n') + lp->line[len - 1] = '\0'; + else + lp->line[len] = '\0'; + bp = lp->line; + + /* Split the line into fields, allocate space as necessary. */ + lp->fieldcnt = 0; + while ((fieldp = strsep(&bp, tabchar)) != NULL) { + if (spans && *fieldp == '\0') + continue; + if (lp->fieldcnt == lp->fieldalloc) { + char **n; + + if (lp->fieldalloc == 0) + nsize = 16; + else + nsize = lp->fieldalloc << 1; + if ((n = realloc(lp->fields, + nsize * sizeof(char *))) == NULL) + enomem(); + lp->fields = n; + lp->fieldalloc = nsize; + } + lp->fields[lp->fieldcnt++] = fieldp; + } + + /* See if the join field value has changed. */ + if (F->setcnt && cmp(lp, F->joinf, lp - 1, F->joinf)) { + F->pushback = F->setcnt; + break; + } + } +} + +static int +cmp(LINE *lp1, u_long fieldno1, LINE *lp2, u_long fieldno2) +{ + + if (lp1->fieldcnt <= fieldno1) + return (lp2->fieldcnt <= fieldno2 ? 0 : 1); + if (lp2->fieldcnt <= fieldno2) + return (-1); + return (strcmp(lp1->fields[fieldno1], lp2->fields[fieldno2])); +} + +static void +joinlines(INPUT *F1, INPUT *F2) +{ + u_long cnt1, cnt2; + + /* + * Output the results of a join comparison. The output may be from + * either file 1 or file 2 (in which case the first argument is the + * file from which to output) or from both. + */ + if (F2 == NULL) { + for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1) + outoneline(F1, &F1->set[cnt1]); + return; + } + for (cnt1 = 0; cnt1 < F1->setcnt; ++cnt1) + for (cnt2 = 0; cnt2 < F2->setcnt; ++cnt2) + outtwoline(F1, &F1->set[cnt1], F2, &F2->set[cnt2]); +} + +static void +outoneline(INPUT *F, LINE *lp) +{ + u_long cnt; + + /* + * Output a single line from one of the files, according to the + * join rules. This happens when we are writing unmatched single + * lines. Output empty fields in the right places. + */ + if (olist) + for (cnt = 0; cnt < olistcnt; ++cnt) { + if (olist[cnt].fileno == (u_long)F->number) + outfield(lp, olist[cnt].fieldno); + else + outfield(&noline, 1); + } + else + for (cnt = 0; cnt < lp->fieldcnt; ++cnt) + outfield(lp, cnt); + (void)printf("\n"); + if (ferror(stdout)) + err(1, "stdout"); + needsep = 0; +} + +static void +outtwoline(INPUT *F1, LINE *lp1, INPUT *F2, LINE *lp2) +{ + u_long cnt; + + /* Output a pair of lines according to the join list (if any). */ + if (olist) { + for (cnt = 0; cnt < olistcnt; ++cnt) + if (olist[cnt].fileno == 1) + outfield(lp1, olist[cnt].fieldno); + else /* if (olist[cnt].fileno == 2) */ + outfield(lp2, olist[cnt].fieldno); + } else { + /* + * Output the join field, then the remaining fields from F1 + * and F2. + */ + outfield(lp1, F1->joinf); + for (cnt = 0; cnt < lp1->fieldcnt; ++cnt) + if (F1->joinf != cnt) + outfield(lp1, cnt); + for (cnt = 0; cnt < lp2->fieldcnt; ++cnt) + if (F2->joinf != cnt) + outfield(lp2, cnt); + } + (void)printf("\n"); + if (ferror(stdout)) + err(1, "stdout"); + needsep = 0; +} + +static void +outfield(LINE *lp, u_long fieldno) +{ + if (needsep++) + (void)printf("%c", *tabchar); + if (!ferror(stdout)) { + if (lp->fieldcnt <= fieldno) { + if (empty != NULL) + (void)printf("%s", empty); + } else { + if (*lp->fields[fieldno] == '\0') + return; + (void)printf("%s", lp->fields[fieldno]); + } + } + if (ferror(stdout)) + err(1, "stdout"); +} + +/* + * Convert an output list argument "2.1, 1.3, 2.4" into an array of output + * fields. + */ +static void +fieldarg(char *option) +{ + u_long fieldno; + char *end, *token; + OLIST *n; + + while ((token = strsep(&option, ", \t")) != NULL) { + if (*token == '\0') + continue; + if ((token[0] != '1' && token[0] != '2') || token[1] != '.') + errx(1, "malformed -o option field"); + fieldno = strtol(token + 2, &end, 10); + if (*end) + errx(1, "malformed -o option field"); + if (fieldno == 0) + errx(1, "field numbers are 1 based"); + if (olistcnt == olistalloc) { + if ((n = realloc(olist, + (olistalloc + 50) * sizeof(OLIST))) == NULL) + enomem(); + olist = n; + olistalloc += 50; + } + olist[olistcnt].fileno = token[0] - '0'; + olist[olistcnt].fieldno = fieldno - 1; + ++olistcnt; + } +} + +static void +obsolete(char **argv) +{ + size_t len; + char **p, *ap, *t; + + while ((ap = *++argv) != NULL) { + /* Return if "--". */ + if (ap[0] == '-' && ap[1] == '-') + return; + switch (ap[1]) { + case 'a': + /* + * The original join allowed "-a", which meant the + * same as -a1 plus -a2. POSIX 1003.2, Draft 11.2 + * only specifies this as "-a 1" and "a -2", so we + * have to use another option flag, one that is + * unlikely to ever be used or accidentally entered + * on the command line. (Well, we could reallocate + * the argv array, but that hardly seems worthwhile.) + */ + if (ap[2] == '\0') + ap[1] = '\01'; + break; + case 'j': + /* + * The original join allowed "-j[12] arg" and "-j arg". + * Convert the former to "-[12] arg". Don't convert + * the latter since getopt(3) can handle it. + */ + switch(ap[2]) { + case '1': + if (ap[3] != '\0') + goto jbad; + ap[1] = '1'; + ap[2] = '\0'; + break; + case '2': + if (ap[3] != '\0') + goto jbad; + ap[1] = '2'; + ap[2] = '\0'; + break; + case '\0': + break; + default: +jbad: errx(1, "illegal option -- %s", ap); + usage(); + } + break; + case 'o': + /* + * The original join allowed "-o arg arg". Convert to + * "-o arg -o arg". + */ + if (ap[2] != '\0') + break; + for (p = argv + 2; *p; ++p) { + if ((p[0][0] != '1' && p[0][0] != '2') || + p[0][1] != '.') + break; + len = strlen(*p); + if (len - 2 != strspn(*p + 2, "0123456789")) + break; + if ((t = malloc(len + 3)) == NULL) + enomem(); + t[0] = '-'; + t[1] = 'o'; + memmove(t + 2, *p, len + 1); + *p = t; + } + argv = p - 1; + break; + } + } +} + +static void +enomem(void) +{ + errx(1, "no memory"); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: %s [-a fileno | -v fileno] [-e string] [-j fileno field]\n" + " [-o list] [-t char] [-1 field] [-2 field] file1 file2\n", + getprogname()); + exit(1); +} diff --git a/buildrump.sh/src/usr.bin/lorder/Makefile b/buildrump.sh/src/usr.bin/lorder/Makefile new file mode 100644 index 00000000..761bec70 --- /dev/null +++ b/buildrump.sh/src/usr.bin/lorder/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.11 1997/03/24 21:59:43 christos Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +MAN= lorder.1 + +SCRIPTS=lorder.sh + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/lorder/lorder.1 b/buildrump.sh/src/usr.bin/lorder/lorder.1 new file mode 100644 index 00000000..19b47ad1 --- /dev/null +++ b/buildrump.sh/src/usr.bin/lorder/lorder.1 @@ -0,0 +1,71 @@ +.\" $NetBSD: lorder.1,v 1.8 2012/03/22 07:58:19 wiz Exp $ +.\" +.\" Copyright (c) 1990, 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. +.\" +.\" @(#)lorder.1 8.2 (Berkeley) 4/28/95 +.\" +.Dd April 28, 1995 +.Dt LORDER 1 +.Os +.Sh NAME +.Nm lorder +.Nd list dependencies for object files +.Sh SYNOPSIS +.Nm +.Ar +.Sh DESCRIPTION +The +.Nm +utility uses +.Xr nm 1 +to determine interdependencies in the list of object files +specified on the command line. +.Nm +outputs a list of file names where the first file contains a symbol +which is defined by the second file. +.Pp +The output is normally used with +.Xr tsort 1 +when a library is created to determine the optimum ordering of the +object modules so that all references may be resolved in a single +pass of the loader. +.Sh EXAMPLES +.Bd -literal -offset indent +ar cr library.a `lorder ${OBJS} | tsort` +.Ed +.Sh SEE ALSO +.Xr ar 1 , +.Xr ld 1 , +.Xr nm 1 , +.Xr ranlib 1 , +.Xr tsort 1 +.Sh HISTORY +An +.Nm +utility appeared in +.At v7 . diff --git a/buildrump.sh/src/usr.bin/lorder/lorder.sh b/buildrump.sh/src/usr.bin/lorder/lorder.sh new file mode 100644 index 00000000..5fe5a178 --- /dev/null +++ b/buildrump.sh/src/usr.bin/lorder/lorder.sh @@ -0,0 +1,102 @@ +#!/bin/sh - +# $NetBSD: lorder.sh,v 1.15 2007/01/14 16:29:35 apb Exp $ +# +# Copyright (c) 1990, 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)lorder.sh 8.1 (Berkeley) 6/6/93 +# + +# If the user has set ${NM} then we use it, otherwise we use 'nm'. +# Similarly for JOIN, MKTEMP, SED, and SORT. +# +# For each of these, we try to find the command in the user's path, and +# if that fails we try to find it in the default path. If we can't +# find it, we punt. Once we find it, we canonicalize its name and set +# the path to the default path so that other commands we use are picked +# properly. + +: ${JOIN:=join} +: ${MKTEMP:=mktemp} +: ${NM:=nm} +: ${SED:=sed} +: ${SORT:=sort} +for var in JOIN MKTEMP NM SED SORT ; do + if ! eval type "\$${var}" >/dev/null 2>&1 ; then + PATH=/bin:/usr/bin + export PATH + if ! eval type "\$${var}" > /dev/null 2>&1; then + eval echo "lorder: \$${var}: not found" >&2 + exit 1 + fi + fi + cmd='set $(eval type "\$${var}") ; eval echo \$$#' + eval "${var}=\$(eval \${cmd})" +done + +# only one argument is a special case, just output the name twice +case $# in + 0) + echo "usage: lorder file ..." >&2; + exit ;; + 1) + echo $1 $1; + exit ;; +esac + +# temporary files +N=$("${MKTEMP}" /tmp/_nm_.XXXXXX) || exit 1 +R=$("${MKTEMP}" /tmp/_reference_.XXXXXX) || exit 1 +S=$("${MKTEMP}" /tmp/_symbol_.XXXXXX) || exit 1 + +# remove temporary files on exit +trap "rm -f $N $R $S; exit 0" 0 +trap "rm -f $N $R $S; exit 1" HUP INT QUIT PIPE TERM 2>/dev/null || \ + trap "rm -f $N $R $S; exit 1" 1 2 3 13 15 + +# if the line ends in a colon, assume it's the first occurrence of a new +# object file. Echo it twice, just to make sure it gets into the output. +# +# if the line has " T " or " D " it's a globally defined symbol, put it +# into the symbol file. +# +# if the line has " U " it's a globally undefined symbol, put it into +# the reference file. +(for file in $* ; do echo $file":" ; done ; $NM -go $*) >$N +"${SED}" -ne '/:$/{s/://;s/.*/& &/;p;}' <$N +"${SED}" -ne 's/:.* [TDGR] / /p' <$N >$S +"${SED}" -ne 's/:.* U / /p' <$N >$R + +# sort symbols and references on the second field (the symbol) +# join on that field, and print out the file names. +"${SORT}" -k2 $R -o $R +"${SORT}" -k2 $S -o $S +"${JOIN}" -j 2 -o 1.1,2.1 $R $S diff --git a/buildrump.sh/src/usr.bin/m4/Makefile b/buildrump.sh/src/usr.bin/m4/Makefile new file mode 100644 index 00000000..26944ebc --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/Makefile @@ -0,0 +1,27 @@ +# $NetBSD: Makefile,v 1.19 2015/01/29 19:26:20 christos Exp $ +# +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +# -DEXTENDED +# if you want the paste & spaste macros. +.include <bsd.own.mk> + +PROG= m4 +CPPFLAGS+= -DEXTENDED -I${.CURDIR}/lib +SRCS= parser.y tokenizer.l eval.c expr.c look.c main.c misc.c gnum4.c trace.c +.PATH: ${.CURDIR}/lib +SRCS+= ohash_create_entry.c ohash_delete.c ohash_do.c ohash_entries.c \ + ohash_enum.c ohash_init.c ohash_int.h ohash_interval.c \ + ohash_lookup_interval.c ohash_lookup_memory.c ohash_qlookup.c \ + ohash_qlookupi.c +YHEADER=1 +.if (${HOSTPROG:U} == "") +DPADD+= ${LIBUTIL} ${LIBL} +LDADD+= -lutil -ll +.endif + +tokenizer.o: parser.h + +CLEANFILES+=parser.c parser.h tokenizer.o + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/m4/NOTES b/buildrump.sh/src/usr.bin/m4/NOTES new file mode 100644 index 00000000..d60f80eb --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/NOTES @@ -0,0 +1,64 @@ +m4 - macro processor + +PD m4 is based on the macro tool distributed with the software +tools (VOS) package, and described in the "SOFTWARE TOOLS" and +"SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include +most of the command set of SysV m4, the standard UN*X macro processor. + +Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro, +there may be certain implementation similarities between +the two. The PD m4 was produced without ANY references to m4 +sources. + +written by: Ozan S. Yigit + +References: + + Software Tools distribution: macro + + Kernighan, Brian W. and P. J. Plauger, SOFTWARE + TOOLS IN PASCAL, Addison-Wesley, Mass. 1981 + + Kernighan, Brian W. and P. J. Plauger, SOFTWARE + TOOLS, Addison-Wesley, Mass. 1976 + + Kernighan, Brian W. and Dennis M. Ritchie, + THE M4 MACRO PROCESSOR, Unix Programmer's Manual, + Seventh Edition, Vol. 2, Bell Telephone Labs, 1979 + + System V man page for M4 + + +Implementation Notes: + +[1] PD m4 uses a different (and simpler) stack mechanism than the one + described in Software Tools and Software Tools in Pascal books. + The triple stack thing is replaced with a single stack containing + the call frames and the arguments. Each frame is back-linked to a + previous stack frame, which enables us to rewind the stack after + each nested call is completed. Each argument is a character pointer + to the beginning of the argument string within the string space. + The only exceptions to this are (*) arg 0 and arg 1, which are + the macro definition and macro name strings, stored dynamically + for the hash table. + + . . + | . | <-- sp | . | + +-------+ +-----+ + | arg 3 ------------------------------->| str | + +-------+ | . | + | arg 2 --------------+ . + +-------+ | + * | | | + +-------+ | +-----+ + | plev | <-- fp +---------------->| str | + +-------+ | . | + | type | . + +-------+ + | prcf -----------+ plev: paren level + +-------+ | type: call type + | . | | prcf: prev. call frame + . | + +-------+ | + | <----------+ + +-------+ diff --git a/buildrump.sh/src/usr.bin/m4/PSD.doc/Makefile b/buildrump.sh/src/usr.bin/m4/PSD.doc/Makefile new file mode 100644 index 00000000..9de1f837 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/PSD.doc/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.4 2014/07/05 19:22:04 dholland Exp $ +# +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +SECTION=psd +ARTICLE=m4 +SRCS= m4.ms +MACROS= -msU + +.include <bsd.doc.mk> diff --git a/buildrump.sh/src/usr.bin/m4/TEST/ack.m4 b/buildrump.sh/src/usr.bin/m4/TEST/ack.m4 new file mode 100644 index 00000000..2784c45b --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/TEST/ack.m4 @@ -0,0 +1,41 @@ +# $NetBSD: ack.m4,v 1.4 1995/09/28 05:37:54 tls Exp $ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)ack.m4 8.1 (Berkeley) 6/6/93 +# + +define(ack, `ifelse($1,0,incr($2),$2,0,`ack(DECR($1),1)', +`ack(DECR($1), ack($1,DECR($2)))')') diff --git a/buildrump.sh/src/usr.bin/m4/TEST/hanoi.m4 b/buildrump.sh/src/usr.bin/m4/TEST/hanoi.m4 new file mode 100644 index 00000000..6f734310 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/TEST/hanoi.m4 @@ -0,0 +1,46 @@ +# $NetBSD: hanoi.m4,v 1.4 1995/09/28 05:37:56 tls Exp $ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)hanoi.m4 8.1 (Berkeley) 6/6/93 +# + +define(hanoi, `trans(A, B, C, $1)') + +define(moved,`move disk from $1 to $2 +') + +define(trans, `ifelse($4,1,`moved($1,$2)', + `trans($1,$3,$2,DECR($4))moved($1,$2)trans($3,$2,$1,DECR($4))')') diff --git a/buildrump.sh/src/usr.bin/m4/TEST/hash.m4 b/buildrump.sh/src/usr.bin/m4/TEST/hash.m4 new file mode 100644 index 00000000..5de2f79b --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/TEST/hash.m4 @@ -0,0 +1,56 @@ +# $NetBSD: hash.m4,v 1.4 1995/09/28 05:37:58 tls Exp $ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)hash.m4 8.1 (Berkeley) 6/6/93 +# + +dnl This probably will not run on any m4 that cannot +dnl handle char constants in eval. +dnl +changequote(<,>) define(HASHVAL,99) dnl +define(hash,<eval(str(substr($1,1),0)%HASHVAL)>) dnl +define(str, + <ifelse($1,",$2, + <str(substr(<$1>,1),<eval($2+'substr($1,0,1)')>)>) + >) dnl +define(KEYWORD,<$1,hash($1),>) dnl +define(TSTART, +<struct prehash { + char *keyword; + int hashval; +} keytab[] = {>) dnl +define(TEND,< "",0 +};>) dnl diff --git a/buildrump.sh/src/usr.bin/m4/TEST/math.m4 b/buildrump.sh/src/usr.bin/m4/TEST/math.m4 new file mode 100644 index 00000000..dff39375 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/TEST/math.m4 @@ -0,0 +1,182 @@ +dnl $NetBSD: math.m4,v 1.2 2005/10/06 17:38:09 drochner Exp $ +dnl FreeBSD: /repoman/r/ncvs/src/usr.bin/m4/TEST/math.m4,v 1.1 2004/05/01 03:27:05 smkelly Exp +dnl A regression test for m4 C operators (ksb,petef) +dnl If you think you have a short-circuiting m4, run us m4 -DSHORCIRCUIT=yes +dnl +dnl first level of precedence +ifelse(eval(-7),-7,,`failed - +')dnl +ifelse(eval(- -2),2,,`failed - +')dnl +ifelse(eval(!0),1,,`failed ! +')dnl +ifelse(eval(!7),0,,`failed ! +')dnl +ifelse(eval(~-1),0,,`failed ~ +')dnl +dnl next level of precedence +ifelse(eval(3*5),15,,`failed * +')dnl +ifelse(eval(3*0),0,,`failed * +')dnl +ifelse(eval(11/2),5,,`failed / +')dnl +ifelse(eval(1/700),0,,`failed / +')dnl +ifelse(eval(10%5),0,,`failed % +')dnl +ifelse(eval(2%5),2,,`failed % +')dnl +ifelse(eval(2%-1),0,,`failed % +')dnl +dnl next level of precedence +ifelse(eval(2+2),4,,`failed + +')dnl +ifelse(eval(2+-2),0,,`failed + +')dnl +ifelse(eval(2- -2),4,,`failed - +')dnl +ifelse(eval(2-2),0,,`failed - +')dnl +dnl next level of precedence +ifelse(eval(1<<4),16,,`failed << +')dnl +ifelse(eval(16>>4),1,,`failed >> +')dnl +dnl next level of precedence +ifelse(eval(4<4),0,,`failed < +')dnl +ifelse(eval(4<5),1,,`failed < +')dnl +ifelse(eval(4<3),0,,`failed < +')dnl +ifelse(eval(4>4),0,,`failed > +')dnl +ifelse(eval(4>5),0,,`failed > +')dnl +ifelse(eval(4>3),1,,`failed > +')dnl +ifelse(eval(4<=4),1,,`failed <= +')dnl +ifelse(eval(4<=5),1,,`failed <= +')dnl +ifelse(eval(4<=3),0,,`failed <= +')dnl +ifelse(eval(4>=4),1,,`failed >= +')dnl +ifelse(eval(4>=5),0,,`failed >= +')dnl +ifelse(eval(4>=3),1,,`failed >= +')dnl +dnl next level of precedence +ifelse(eval(1==1),1,,`failed == +')dnl +ifelse(eval(1==-1),0,,`failed == +')dnl +ifelse(eval(1!=1),0,,`failed != +')dnl +ifelse(eval(1!=2),1,,`failed != +')dnl +dnl next level of precedence +ifelse(eval(3&5),1,,`failed & +')dnl +ifelse(eval(8&7),0,,`failed & +')dnl +dnl next level of precedence +ifelse(eval(1^1),0,,`failed ^ +')dnl +ifelse(eval(21^5),16,,`failed ^ +')dnl +dnl next level of precedence +ifelse(eval(1|1),1,,`failed | +')dnl +ifelse(eval(21|5),21,,`failed | +')dnl +ifelse(eval(100|1),101,,`failed | +')dnl +dnl next level of precedence +ifelse(eval(1&&1),1,,`failed && +')dnl +ifelse(eval(0&&1),0,,`failed && +')dnl +ifelse(eval(1&&0),0,,`failed && +')dnl +ifelse(SHORTCIRCUIT,`yes',`ifelse(eval(0&&10/0),0,,`failed && shortcircuit +')')dnl +dnl next level of precedence +ifelse(eval(1||1),1,,`failed || +')dnl +ifelse(eval(1||0),1,,`failed || +')dnl +ifelse(eval(0||0),0,,`failed || +')dnl +ifelse(SHORTCIRCUIT,`yes',`ifelse(eval(1||10/0),1,,`failed || shortcircuit +')')dnl +dnl next level of precedence +ifelse(eval(0 ? 2 : 5),5,,`failed ?: +')dnl +ifelse(eval(1 ? 2 : 5),2,,`failed ?: +')dnl +ifelse(SHORTCIRCUIT,`yes',`ifelse(eval(0 ? 10/0 : 7),7,,`failed ?: shortcircuit +')')dnl +ifelse(SHORTCIRCUIT,`yes',`ifelse(eval(1 ? 7 : 10/0),7,,`failed ?: shortcircuit +')')dnl +dnl operator precedence +ifelse(eval(!0*-2),-2,,`precedence wrong, ! * +')dnl +ifelse(eval(~8/~2),3,,`precedence wrong ~ / +')dnl +ifelse(eval(~-20%7),5,,`precedence wrong ~ % +')dnl +ifelse(eval(3*2+100),106,,`precedence wrong * + +')dnl +ifelse(eval(3+2*100),203,,`precedence wrong + * +')dnl +ifelse(eval(2%5-6/3),0,,`precedence wrong % - +')dnl +ifelse(eval(2/5-5%3),-2,,`precedence wrong / - +')dnl +ifelse(eval(2+5%5+1),3,,`precedence wrong % + +')dnl +ifelse(eval(7+9<<1),32,,`precedence wrong + << +')dnl +ifelse(eval(35-3>>2),8,,`precedence wrong - >> +')dnl +ifelse(eval(9<10<<5),1,,`precedence wrong << < +')dnl +ifelse(eval(9>10<<5),0,,`precedence wrong << > +')dnl +ifelse(eval(32>>2<32),1,,`precedence wrong >> < +')dnl +ifelse(eval(9<=10<<5),1,,`precedence wrong << < +')dnl +ifelse(eval(5<<1<=20>>1),1,,`precedence wrong << <= +')dnl +ifelse(eval(5<<1>=20>>1),1,,`precedence wrong << >= +')dnl +ifelse(eval(0<7==5>=5),1,,`precedence wrong < == +')dnl +ifelse(eval(0<7!=5>=5),0,,`precedence wrong < != +')dnl +ifelse(eval(0>7==5>=5),0,,`precedence wrong > == +')dnl +ifelse(eval(0>7!=5>=5),1,,`precedence wrong > != +')dnl +ifelse(eval(1&7==7),1,,`precedence wrong & == +')dnl +ifelse(eval(0&7!=6),0,,`precedence wrong & != +')dnl +ifelse(eval(9&1|5),5,,`precedence wrong & | +')dnl +ifelse(eval(9&1^5),4,,`precedence wrong & ^ +')dnl +ifelse(eval(9^1|5),13,,`precedence wrong ^ | +')dnl +ifelse(eval(5|0&&1),1,,`precedence wrong | && +')dnl +ifelse(eval(5&&0||0&&5||5),1,,`precedence wrong && || +')dnl +ifelse(eval(0 || 1 ? 0 : 1),0,,`precedence wrong || ?: +')dnl +ifelse(eval(5&&(0||0)&&(5||5)),0,,`precedence wrong || parens +')dnl diff --git a/buildrump.sh/src/usr.bin/m4/TEST/sqroot.m4 b/buildrump.sh/src/usr.bin/m4/TEST/sqroot.m4 new file mode 100644 index 00000000..6fc8d2c7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/TEST/sqroot.m4 @@ -0,0 +1,46 @@ +# $NetBSD: sqroot.m4,v 1.4 1995/09/28 05:38:01 tls Exp $ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)sqroot.m4 8.1 (Berkeley) 6/6/93 +# + +define(square_root, + `ifelse(eval($1<0),1,negative-square-root, + `square_root_aux($1, 1, eval(($1+1)/2))')') +define(square_root_aux, + `ifelse($3, $2, $3, + $3, eval($1/$2), $3, + `square_root_aux($1, $3, eval(($3+($1/$3))/2))')') diff --git a/buildrump.sh/src/usr.bin/m4/TEST/string.m4 b/buildrump.sh/src/usr.bin/m4/TEST/string.m4 new file mode 100644 index 00000000..a14f14a2 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/TEST/string.m4 @@ -0,0 +1,46 @@ +# $NetBSD: string.m4,v 1.4 1995/09/28 05:38:03 tls Exp $ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)string.m4 8.1 (Berkeley) 6/6/93 +# + +define(string,`integer $1(len(substr($2,1))) +str($1,substr($2,1),0) +data $1(len(substr($2,1)))/EOS/ +') + +define(str,`ifelse($2,",,data $1(incr($3))/`LET'substr($2,0,1)/ +`str($1,substr($2,1),incr($3))')') diff --git a/buildrump.sh/src/usr.bin/m4/TEST/test.m4 b/buildrump.sh/src/usr.bin/m4/TEST/test.m4 new file mode 100644 index 00000000..5b198073 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/TEST/test.m4 @@ -0,0 +1,244 @@ +# $NetBSD: test.m4,v 1.4 1995/09/28 05:38:05 tls Exp $ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# @(#)test.m4 8.1 (Berkeley) 6/6/93 +# + +# test file for mp (not comprehensive) +# +# v7 m4 does not have `decr'. +# +define(DECR,`eval($1-1)') +# +# include string macros +# +include(string.m4) +# +# create some fortrash strings for an even uglier language +# +string(TEXT, "text") +string(DATA, "data") +string(BEGIN, "begin") +string(END, "end") +string(IF, "if") +string(THEN, "then") +string(ELSE, "else") +string(CASE, "case") +string(REPEAT, "repeat") +string(WHILE, "while") +string(DEFAULT, "default") +string(UNTIL, "until") +string(FUNCTION, "function") +string(PROCEDURE, "procedure") +string(EXTERNAL, "external") +string(FORWARD, "forward") +string(TYPE, "type") +string(VAR, "var") +string(CONST, "const") +string(PROGRAM, "program") +string(INPUT, "input") +string(OUTPUT, "output") +# +divert(2) +diversion #1 +divert(3) +diversion #2 +divert(4) +diversion #3 +divert(5) +diversion #4 +divert(0) +define(abc,xxx) +ifdef(`abc',defined,undefined) +# +# v7 m4 does this wrong. The right output is +# this is A vEry lon sEntEnCE +# see m4 documentation for translit. +# +translit(`this is a very long sentence', abcdefg, ABCDEF) +# +# include towers-of-hanoi +# +include(hanoi.m4) +# +# some reasonable set of disks +# +hanoi(6) +# +# include ackermann's function +# +include(ack.m4) +# +# something like (3,3) will blow away un*x m4. +# +ack(2,3) +# +# include a square_root function for fixed nums +# +include(sqroot.m4) +# +# some square roots. +# +square_root(15) +square_root(100) +square_root(-4) +square_root(21372) +# +# some textual material for enjoyment. +# +[taken from the 'Clemson University Computer Newsletter', + September 1981, pp. 6-7] + +I am a wizard in the magical Kingdom of Transformation and I +slay dragons for a living. Actually, I am a systems programmer. +One of the problems with systems programming is explaining to +non-computer enthusiasts what that is. All of the terms I use to +describe my job are totally meaningless to them. Usually my response +to questions about my work is to say as little as possible. For +instance, if someone asks what happened at work this week, I say +"Nothing much" and then I change the subject. + +With the assistance of my brother, a mechanical engineer, I have devised +an analogy that everyone can understand. The analogy describes the +"Kingdom of Transformation" where travelers wander and are magically +transformed. This kingdom is the computer and the travelers are information. +The purpose of the computer is to change information to a more meaningful +forma. The law of conservation applies here: The computer never creates +and never intentionally destroys data. With no further ado, let us travel +to the Kingdom of Transformation: + +In a land far, far away, there is a magical kingdom called the Kingdom of +Transformation. A king rules over this land and employs a Council of +Wizardry. The main purpose of this kingdom is to provide a way for +neighboring kingdoms to transform citizens into more useful citizens. This +is done by allowing the citizens to enter the kingdom at one of its ports +and to travel any of the many routes in the kingdom. They are magically +transformed along the way. The income of the Kingdom of Transformation +comes from the many toll roads within its boundaries. + +The Kingdom of Transformation was created when several kingdoms got +together and discovered a mutual need for new talents and abilities for +citizens. They employed CTK, Inc. (Creators of Transformation, Inc.) to +create this kingdom. CTK designed the country, its transportation routes, +and its laws of transformation, and created the major highway system. + +Hazards +======= + +Because magic is not truly controllable, CTK invariably, but unknowingly, +creates dragons. Dragons are huge fire-breathing beasts which sometimes +injure or kill travelers. Fortunately, they do not travel, but always +remain near their den. + +Other hazards also exist which are potentially harmful. As the roads +become older and more weatherbeaten, pot-holes will develop, trees will +fall on travelers, etc. CTK maintenance men are called to fix these +problems. + +Wizards +======= + +The wizards play a major role in creating and maintaining the kingdom but +get little credit for their work because it is performed secretly. The +wizards do not wan the workers or travelers to learn their incantations +because many laws would be broken and chaos would result. + +CTK's grand design is always general enough to be applicable in many +different situations. As a result, it is often difficult to use. The +first duty of the wizards is to tailor the transformation laws so as to be +more beneficial and easier to use in their particular environment. + +After creation of the kingdom, a major duty of the wizards is to search for +and kill dragons. If travelers do not return on time or if they return +injured, the ruler of the country contacts the wizards. If the wizards +determine that the injury or death occurred due to the traveler's +negligence, they provide the traveler's country with additional warnings. +If not, they must determine if the cause was a road hazard or a dragon. If +the suspect a road hazard, they call in a CTK maintenance man to locate the +hazard and to eliminate it, as in repairing the pothole in the road. If +they think that cause was a dragon, then they must find and slay it. + +The most difficult part of eliminating a dragon is finding it. Sometimes +the wizard magically knows where the dragon's lair it, but often the wizard +must send another traveler along the same route and watch to see where he +disappears. This sounds like a failsafe method for finding dragons (and a +suicide mission for thr traveler) but the second traveler does not always +disappear. Some dragons eat any traveler who comes too close; others are +very picky. + +The wizards may call in CTK who designed the highway system and +transformation laws to help devise a way to locate the dragon. CTK also +helps provide the right spell or incantation to slay the dragon. (There is +no general spell to slay dragons; each dragon must be eliminated with a +different spell.) + +Because neither CTK nor wizards are perfect, spells to not always work +correctly. At best, nothing happens when the wrong spell is uttered. At +worst, the dragon becomes a much larger dragon or multiplies into several +smaller ones. In either case, new spells must be found. + +If all existing dragons are quiet (i.e. have eaten sufficiently), wizards +have time to do other things. They hide in castles and practice spells and +incatations. They also devise shortcuts for travelers and new laws of +transformation. + +Changes in the Kingdom +====================== + +As new transformation kingdoms are created and old ones are maintained, +CTK, Inc. is constantly learning new things. It learns ways to avoid +creating some of the dragons that they have previously created. It also +discovers new and better laws of transformation. As a result, CTK will +periodically create a new grand design which is far better than the old. +The wizards determine when is a good time to implement this new design. +This is when the tourist season is slow or when no important travelers +(VIPs) are to arrive. The kingdom must be closed for the actual +implementation and is leter reopened as a new and better place to go. + +A final question you might ask is what happens when the number of tourists +becomes too great for the kingdom to handle in a reasonable period of time +(i.e., the tourist lines at the ports are too long). The Kingdom of +Transformation has three options: (1) shorten the paths that a tourist must +travel, or (2) convince CTK to develop a faster breed of horses so that the +travelers can finish sooner, or (3) annex more territories so that the +kingdom can handle more travelers. + +Thus ends the story of the Kingdom of Transformation. I hope this has +explained my job to you: I slay dragons for a living. + +# +#should do an automatic undivert.. +# diff --git a/buildrump.sh/src/usr.bin/m4/eval.c b/buildrump.sh/src/usr.bin/m4/eval.c new file mode 100644 index 00000000..8b364064 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/eval.c @@ -0,0 +1,1021 @@ +/* $OpenBSD: eval.c,v 1.66 2008/08/21 21:01:47 espie Exp $ */ +/* $NetBSD: eval.c,v 1.23 2015/01/29 19:26:20 christos Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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. + */ + +/* + * eval.c + * Facility: m4 macro processor + * by: oz + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: eval.c,v 1.23 2015/01/29 19:26:20 christos Exp $"); + +#include <sys/types.h> +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <stdint.h> +#include <string.h> +#include <inttypes.h> +#include <fcntl.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" +#include "pathnames.h" + +static void dodefn(const char *); +static void dopushdef(const char *, const char *); +static void dodump(const char *[], int); +static void dotrace(const char *[], int, int); +static void doifelse(const char *[], int); +static int doincl(const char *); +static int dopaste(const char *); +static void dochq(const char *[], int); +static void dochc(const char *[], int); +static void dom4wrap(const char *); +static void dodiv(int); +static void doundiv(const char *[], int); +static void dosub(const char *[], int); +static void map(char *, const char *, const char *, const char *); +static const char *handledash(char *, char *, const char *); +static void expand_builtin(const char *[], int, int); +static void expand_macro(const char *[], int); +static void dump_one_def(const char *, struct macro_definition *); + +unsigned long expansion_id; + +/* + * eval - eval all macros and builtins calls + * argc - number of elements in argv. + * argv - element vector : + * argv[0] = definition of a user + * macro or NULL if built-in. + * argv[1] = name of the macro or + * built-in. + * argv[2] = parameters to user-defined + * . macro or built-in. + * . + * + * A call in the form of macro-or-builtin() will result in: + * argv[0] = nullstr + * argv[1] = macro-or-builtin + * argv[2] = nullstr + * + * argc is 3 for macro-or-builtin() and 2 for macro-or-builtin + */ +void +eval(const char *argv[], int argc, int td, int is_traced) +{ + size_t mark = SIZE_MAX; + + expansion_id++; + if (td & RECDEF) + m4errx(1, "expanding recursive definition for %s.", argv[1]); + if (is_traced) + mark = trace(argv, argc, infile+ilevel); + if (td == MACRTYPE) + expand_macro(argv, argc); + else + expand_builtin(argv, argc, td); + if (mark != SIZE_MAX) + finish_trace(mark); +} + +/* + * expand_builtin - evaluate built-in macros. + */ +void +expand_builtin(const char *argv[], int argc, int td) +{ + int c, n; + int ac; + static int sysval = 0; + +#ifdef DEBUG + printf("argc = %d\n", argc); + for (n = 0; n < argc; n++) + printf("argv[%d] = %s\n", n, argv[n]); + fflush(stdout); +#endif + + /* + * if argc == 3 and argv[2] is null, then we + * have macro-or-builtin() type call. We adjust + * argc to avoid further checking.. + */ + /* we keep the initial value for those built-ins that differentiate + * between builtin() and builtin. + */ + ac = argc; + + if (argc == 3 && !*(argv[2]) && !mimic_gnu) + argc--; + + switch (td & TYPEMASK) { + + case DEFITYPE: + if (argc > 2) + dodefine(argv[2], (argc > 3) ? argv[3] : null); + break; + + case PUSDTYPE: + if (argc > 2) + dopushdef(argv[2], (argc > 3) ? argv[3] : null); + break; + + case DUMPTYPE: + dodump(argv, argc); + break; + + case TRACEONTYPE: + dotrace(argv, argc, 1); + break; + + case TRACEOFFTYPE: + dotrace(argv, argc, 0); + break; + + case EXPRTYPE: + /* + * doexpr - evaluate arithmetic + * expression + */ + { + int base = 10; + int maxdigits = 0; + int e; + + if (argc > 3) { + base = strtoi(argv[3], NULL, 0, 2, 36, &e); + if (e) { + m4errx(1, "expr: base %s invalid.", argv[3]); + } + } + if (argc > 4) { + maxdigits = strtoi(argv[4], NULL, 0, 0, INT_MAX, &e); + if (e) { + m4errx(1, "expr: maxdigits %s invalid.", argv[4]); + } + } + if (argc > 2) + pbnumbase(expr(argv[2]), base, maxdigits); + break; + } + + case IFELTYPE: + if (argc > 4) + doifelse(argv, argc); + break; + + case IFDFTYPE: + /* + * doifdef - select one of two + * alternatives based on the existence of + * another definition + */ + if (argc > 3) { + if (lookup_macro_definition(argv[2]) != NULL) + pbstr(argv[3]); + else if (argc > 4) + pbstr(argv[4]); + } + break; + + case LENGTYPE: + /* + * dolen - find the length of the + * argument + */ + pbnum((argc > 2) ? strlen(argv[2]) : 0); + break; + + case INCRTYPE: + /* + * doincr - increment the value of the + * argument + */ + if (argc > 2) + pbnum(atoi(argv[2]) + 1); + break; + + case DECRTYPE: + /* + * dodecr - decrement the value of the + * argument + */ + if (argc > 2) + pbnum(atoi(argv[2]) - 1); + break; + + case SYSCTYPE: + /* + * dosys - execute system command + */ + if (argc > 2) { + fflush(stdout); + sysval = system(argv[2]); + } + break; + + case SYSVTYPE: + /* + * dosysval - return value of the last + * system call. + * + */ + pbnum(sysval); + break; + + case ESYSCMDTYPE: + if (argc > 2) + doesyscmd(argv[2]); + break; + case INCLTYPE: + if (argc > 2) + if (!doincl(argv[2])) + err(1, "%s at line %lu: include(%s)", + CURRENT_NAME, CURRENT_LINE, argv[2]); + break; + + case SINCTYPE: + if (argc > 2) + (void) doincl(argv[2]); + break; +#ifdef EXTENDED + case PASTTYPE: + if (argc > 2) + if (!dopaste(argv[2])) + err(1, "%s at line %lu: paste(%s)", + CURRENT_NAME, CURRENT_LINE, argv[2]); + break; + + case SPASTYPE: + if (argc > 2) + (void) dopaste(argv[2]); + break; + case FORMATTYPE: + doformat(argv, argc); + break; +#endif + case CHNQTYPE: + dochq(argv, ac); + break; + + case CHNCTYPE: + dochc(argv, argc); + break; + + case SUBSTYPE: + /* + * dosub - select substring + * + */ + if (argc > 3) + dosub(argv, argc); + break; + + case SHIFTYPE: + /* + * doshift - push back all arguments + * except the first one (i.e. skip + * argv[2]) + */ + if (argc > 3) { + for (n = argc - 1; n > 3; n--) { + pbstr(rquote); + pbstr(argv[n]); + pbstr(lquote); + pushback(COMMA); + } + pbstr(rquote); + pbstr(argv[3]); + pbstr(lquote); + } + break; + + case DIVRTYPE: + if (argc > 2 && (n = atoi(argv[2])) != 0) + dodiv(n); + else { + active = stdout; + oindex = 0; + } + break; + + case UNDVTYPE: + doundiv(argv, argc); + break; + + case DIVNTYPE: + /* + * dodivnum - return the number of + * current output diversion + */ + pbnum(oindex); + break; + + case UNDFTYPE: + /* + * doundefine - undefine a previously + * defined macro(s) or m4 keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + macro_undefine(argv[n]); + break; + + case POPDTYPE: + /* + * dopopdef - remove the topmost + * definitions of macro(s) or m4 + * keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + macro_popdef(argv[n]); + break; + + case MKTMTYPE: + /* + * dotemp - create a temporary file + */ + if (argc > 2) { + int fd; + char *temp; + + temp = xstrdup(argv[2]); + + fd = mkstemp(temp); + if (fd == -1) + err(1, + "%s at line %lu: couldn't make temp file %s", + CURRENT_NAME, CURRENT_LINE, argv[2]); + close(fd); + pbstr(temp); + free(temp); + } + break; + + case TRNLTYPE: + /* + * dotranslit - replace all characters in + * the source string that appears in the + * "from" string with the corresponding + * characters in the "to" string. + */ + if (argc > 3) { + char *temp; + + temp = xalloc(strlen(argv[2])+1, NULL); + if (argc > 4) + map(temp, argv[2], argv[3], argv[4]); + else + map(temp, argv[2], argv[3], null); + pbstr(temp); + free(temp); + } else if (argc > 2) + pbstr(argv[2]); + break; + + case INDXTYPE: + /* + * doindex - find the index of the second + * argument string in the first argument + * string. -1 if not present. + */ + pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); + break; + + case ERRPTYPE: + /* + * doerrp - print the arguments to stderr + * file + */ + if (argc > 2) { + for (n = 2; n < argc; n++) + fprintf(stderr, "%s%s", + mimic_gnu && n == 2 ? "" : " ", + argv[n]); + if (!mimic_gnu) + fprintf(stderr, "\n"); + } + break; + + case DNLNTYPE: + /* + * dodnl - eat-up-to and including + * newline + */ + while ((c = gpbc()) != '\n' && c != EOF) + ; + break; + + case M4WRTYPE: + /* + * dom4wrap - set up for + * wrap-up/wind-down activity + */ + if (argc > 2) + dom4wrap(argv[2]); + break; + + case EXITTYPE: + /* + * doexit - immediate exit from m4. + */ + killdiv(); + exit((argc > 2) ? atoi(argv[2]) : 0); + break; + + case DEFNTYPE: + if (argc > 2) + for (n = 2; n < argc; n++) + dodefn(argv[n]); + break; + + case INDIRTYPE: /* Indirect call */ + if (argc > 2) + doindir(argv, argc); + break; + + case BUILTINTYPE: /* Builtins only */ + if (argc > 2) + dobuiltin(argv, argc); + break; + + case PATSTYPE: + if (argc > 2) + dopatsubst(argv, argc); + break; + case REGEXPTYPE: + if (argc > 2) + doregexp(argv, argc); + break; + case LINETYPE: + doprintlineno(infile+ilevel); + break; + case FILENAMETYPE: + doprintfilename(infile+ilevel); + break; + case SELFTYPE: + pbstr(rquote); + pbstr(argv[1]); + pbstr(lquote); + break; + default: + m4errx(1, "eval: major botch."); + break; + } +} + +/* + * expand_macro - user-defined macro expansion + */ +void +expand_macro(const char *argv[], int argc) +{ + const char *t; + const char *p; + int n; + int argno; + + t = argv[0]; /* defn string as a whole */ + p = t; + while (*p) + p++; + p--; /* last character of defn */ + while (p > t) { + if (*(p - 1) != ARGFLAG) + PUSHBACK(*p); + else { + switch (*p) { + + case '#': + pbnum(argc - 2); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if ((argno = *p - '0') < argc - 1) + pbstr(argv[argno + 1]); + break; + case '*': + if (argc > 2) { + for (n = argc - 1; n > 2; n--) { + pbstr(argv[n]); + pushback(COMMA); + } + pbstr(argv[2]); + } + break; + case '@': + if (argc > 2) { + for (n = argc - 1; n > 2; n--) { + pbstr(rquote); + pbstr(argv[n]); + pbstr(lquote); + pushback(COMMA); + } + pbstr(rquote); + pbstr(argv[2]); + pbstr(lquote); + } + break; + default: + PUSHBACK(*p); + PUSHBACK('$'); + break; + } + p--; + } + p--; + } + if (p == t) /* do last character */ + PUSHBACK(*p); +} + + +/* + * dodefine - install definition in the table + */ +void +dodefine(const char *name, const char *defn) +{ + if (!*name && !mimic_gnu) + m4errx(1, "null definition."); + else + macro_define(name, defn); +} + +/* + * dodefn - push back a quoted definition of + * the given name. + */ +static void +dodefn(const char *name) +{ + struct macro_definition *p; + + if ((p = lookup_macro_definition(name)) != NULL) { + if ((p->type & TYPEMASK) == MACRTYPE) { + pbstr(rquote); + pbstr(p->defn); + pbstr(lquote); + } else { + pbstr(p->defn); + pbstr(BUILTIN_MARKER); + } + } +} + +/* + * dopushdef - install a definition in the hash table + * without removing a previous definition. Since + * each new entry is entered in *front* of the + * hash bucket, it hides a previous definition from + * lookup. + */ +static void +dopushdef(const char *name, const char *defn) +{ + if (!*name && !mimic_gnu) + m4errx(1, "null definition."); + else + macro_pushdef(name, defn); +} + +/* + * dump_one_def - dump the specified definition. + */ +static void +dump_one_def(const char *name, struct macro_definition *p) +{ + if (!traceout) + traceout = stderr; + if (mimic_gnu) { + if ((p->type & TYPEMASK) == MACRTYPE) + fprintf(traceout, "%s:\t%s\n", name, p->defn); + else { + fprintf(traceout, "%s:\t<%s>\n", name, p->defn); + } + } else + fprintf(traceout, "`%s'\t`%s'\n", name, p->defn); +} + +/* + * dodumpdef - dump the specified definitions in the hash + * table to stderr. If nothing is specified, the entire + * hash table is dumped. + */ +static void +dodump(const char *argv[], int argc) +{ + int n; + struct macro_definition *p; + + if (argc > 2) { + for (n = 2; n < argc; n++) + if ((p = lookup_macro_definition(argv[n])) != NULL) + dump_one_def(argv[n], p); + } else + macro_for_all(dump_one_def); +} + +/* + * dotrace - mark some macros as traced/untraced depending upon on. + */ +static void +dotrace(const char *argv[], int argc, int on) +{ + int n; + + if (argc > 2) { + for (n = 2; n < argc; n++) + mark_traced(argv[n], on); + } else + mark_traced(NULL, on); +} + +/* + * doifelse - select one of two alternatives - loop. + */ +static void +doifelse(const char *argv[], int argc) +{ + cycle { + if (STREQ(argv[2], argv[3])) + pbstr(argv[4]); + else if (argc == 6) + pbstr(argv[5]); + else if (argc > 6) { + argv += 3; + argc -= 3; + continue; + } + break; + } +} + +/* + * doinclude - include a given file. + */ +static int +doincl(const char *ifile) +{ + if (ilevel + 1 == MAXINP) + m4errx(1, "too many include files."); + if (fopen_trypath(infile+ilevel+1, ifile) != NULL) { + ilevel++; + bbase[ilevel] = bufbase = bp; + return (1); + } else + return (0); +} + +#ifdef EXTENDED +/* + * dopaste - include a given file without any + * macro processing. + */ +static int +dopaste(const char *pfile) +{ + FILE *pf; + int c; + + if ((pf = fopen(pfile, "r")) != NULL) { + if (synch_lines) + fprintf(active, "#line 1 \"%s\"\n", pfile); + while ((c = getc(pf)) != EOF) + putc(c, active); + (void) fclose(pf); + emit_synchline(); + return (1); + } else + return (0); +} +#endif + +/* + * dochq - change quote characters + */ +static void +dochq(const char *argv[], int ac) +{ + if (ac == 2) { + lquote[0] = LQUOTE; lquote[1] = EOS; + rquote[0] = RQUOTE; rquote[1] = EOS; + } else { + strlcpy(lquote, argv[2], sizeof(lquote)); + if (ac > 3) { + strlcpy(rquote, argv[3], sizeof(rquote)); + } else { + rquote[0] = ECOMMT; rquote[1] = EOS; + } + } +} + +/* + * dochc - change comment characters + */ +static void +dochc(const char *argv[], int argc) +{ +/* XXX Note that there is no difference between no argument and a single + * empty argument. + */ + if (argc == 2) { + scommt[0] = EOS; + ecommt[0] = EOS; + } else { + strlcpy(scommt, argv[2], sizeof(scommt)); + if (argc == 3) { + ecommt[0] = ECOMMT; ecommt[1] = EOS; + } else { + strlcpy(ecommt, argv[3], sizeof(ecommt)); + } + } +} + +/* + * dom4wrap - expand text at EOF + */ +static void +dom4wrap(const char *text) +{ + if (wrapindex >= maxwraps) { + if (maxwraps == 0) + maxwraps = 16; + else + maxwraps *= 2; + m4wraps = xrealloc(m4wraps, maxwraps * sizeof(*m4wraps), + "too many m4wraps"); + } + m4wraps[wrapindex++] = xstrdup(text); +} + +/* + * dodivert - divert the output to a temporary file + */ +static void +dodiv(int n) +{ + int fd; + + oindex = n; + if (n >= maxout) { + if (mimic_gnu) + resizedivs(n + 10); + else + n = 0; /* bitbucket */ + } + + if (n < 0) + n = 0; /* bitbucket */ + if (outfile[n] == NULL) { + char fname[] = _PATH_DIVNAME; + + if ((fd = mkstemp(fname)) < 0 || + (outfile[n] = fdopen(fd, "w+")) == NULL) + err(1, "%s: cannot divert", fname); + if (unlink(fname) == -1) + err(1, "%s: cannot unlink", fname); + } + active = outfile[n]; +} + +/* + * doundivert - undivert a specified output, or all + * other outputs, in numerical order. + */ +static void +doundiv(const char *argv[], int argc) +{ + int ind; + int n; + + if (argc > 2) { + for (ind = 2; ind < argc; ind++) { + int e; + n = strtoi(argv[ind], NULL, 0, 1, INT_MAX, &e); + if (e) { + if (errno == EINVAL && mimic_gnu) + getdivfile(argv[ind]); + } else { + if (n < maxout && outfile[n] != NULL) + getdiv(n); + } + } + } + else + for (n = 1; n < maxout; n++) + if (outfile[n] != NULL) + getdiv(n); +} + +/* + * dosub - select substring + */ +static void +dosub(const char *argv[], int argc) +{ + const char *ap, *fc, *k; + int nc; + + ap = argv[2]; /* target string */ +#ifdef EXPR + fc = ap + expr(argv[3]); /* first char */ +#else + fc = ap + atoi(argv[3]); /* first char */ +#endif + nc = strlen(fc); + if (argc >= 5) +#ifdef EXPR + nc = min(nc, expr(argv[4])); +#else + nc = min(nc, atoi(argv[4])); +#endif + if (fc >= ap && fc < ap + strlen(ap)) + for (k = fc + nc - 1; k >= fc; k--) + pushback(*k); +} + +/* + * map: + * map every character of s1 that is specified in from + * into s3 and replace in s. (source s1 remains untouched) + * + * This is a standard implementation of map(s,from,to) function of ICON + * language. Within mapvec, we replace every character of "from" with + * the corresponding character in "to". If "to" is shorter than "from", + * than the corresponding entries are null, which means that those + * characters dissapear altogether. Furthermore, imagine + * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, + * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' + * ultimately maps to `*'. In order to achieve this effect in an efficient + * manner (i.e. without multiple passes over the destination string), we + * loop over mapvec, starting with the initial source character. if the + * character value (dch) in this location is different than the source + * character (sch), sch becomes dch, once again to index into mapvec, until + * the character value stabilizes (i.e. sch = dch, in other words + * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary + * character, it will stabilize, since mapvec[0] == 0 at all times. At the + * end, we restore mapvec* back to normal where mapvec[n] == n for + * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is + * about 5 times faster than any algorithm that makes multiple passes over + * destination string. + */ +static void +map(char *dest, const char *src, const char *from, const char *to) +{ + const char *tmp; + unsigned char sch, dch; + static char frombis[257]; + static char tobis[257]; + static unsigned char mapvec[256] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, + 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, + 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, + 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, + 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, + 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, + 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, + 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, + 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, + 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, + 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, + 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, + 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, + 246, 247, 248, 249, 250, 251, 252, 253, 254, 255 + }; + + if (*src) { + if (mimic_gnu) { + /* + * expand character ranges on the fly + */ + from = handledash(frombis, frombis + 256, from); + to = handledash(tobis, tobis + 256, to); + } + tmp = from; + /* + * create a mapping between "from" and + * "to" + */ + while (*from) + mapvec[(unsigned char)(*from++)] = (*to) ? + (unsigned char)(*to++) : 0; + + while (*src) { + sch = (unsigned char)(*src++); + dch = mapvec[sch]; + while (dch != sch) { + sch = dch; + dch = mapvec[sch]; + } + if ((*dest = (char)dch)) + dest++; + } + /* + * restore all the changed characters + */ + while (*tmp) { + mapvec[(unsigned char)(*tmp)] = (unsigned char)(*tmp); + tmp++; + } + } + *dest = '\0'; +} + + +/* + * handledash: + * use buffer to copy the src string, expanding character ranges + * on the way. + */ +static const char * +handledash(char *buffer, char *end, const char *src) +{ + char *p; + + p = buffer; + while(*src) { + if (src[1] == '-' && src[2]) { + unsigned char i; + if ((unsigned char)src[0] <= (unsigned char)src[2]) { + for (i = (unsigned char)src[0]; + i <= (unsigned char)src[2]; i++) { + *p++ = i; + if (p == end) { + *p = '\0'; + return buffer; + } + } + } else { + for (i = (unsigned char)src[0]; + i >= (unsigned char)src[2]; i--) { + *p++ = i; + if (p == end) { + *p = '\0'; + return buffer; + } + } + } + src += 3; + } else + *p++ = *src++; + if (p == end) + break; + } + *p = '\0'; + return buffer; +} diff --git a/buildrump.sh/src/usr.bin/m4/expr.c b/buildrump.sh/src/usr.bin/m4/expr.c new file mode 100644 index 00000000..a6c901f7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/expr.c @@ -0,0 +1,50 @@ +/* $NetBSD: expr.c,v 1.19 2009/10/26 21:11:28 christos Exp $ */ +/* $OpenBSD: expr.c,v 1.17 2006/01/20 23:10:19 espie Exp $ */ +/* + * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: expr.c,v 1.19 2009/10/26 21:11:28 christos Exp $"); +#include <stdint.h> +#include <stdio.h> +#include <stddef.h> +#include "mdef.h" +#include "extern.h" + +int32_t end_result; +const char *copy_toeval; + +extern void yy_scan_string(const char *); +extern int yyparse(void); +extern int yyerror(const char *); + +int +yyerror(const char *msg) +{ + fprintf(stderr, "m4: %s in expr %s\n", msg, copy_toeval); + return(0); +} + +int +expr(const char *toeval) +{ + copy_toeval = toeval; + yy_scan_string(toeval); + yyparse(); + return end_result; +} diff --git a/buildrump.sh/src/usr.bin/m4/extern.h b/buildrump.sh/src/usr.bin/m4/extern.h new file mode 100644 index 00000000..c6175512 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/extern.h @@ -0,0 +1,175 @@ +/* $OpenBSD: extern.h,v 1.49 2009/10/14 17:19:47 sthen Exp $ */ +/* $NetBSD: extern.h,v 1.17 2015/01/29 03:27:06 christos Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +/* eval.c */ +extern void eval(const char *[], int, int, int); +extern void dodefine(const char *, const char *); +extern unsigned long expansion_id; + +/* expr.c */ +extern int expr(const char *); + +/* gnum4.c */ +extern void addtoincludepath(const char *); +extern struct input_file *fopen_trypath(struct input_file *, const char *); +extern void doindir(const char *[], int); +extern void dobuiltin(const char *[], int); +extern void dopatsubst(const char *[], int); +extern void doregexp(const char *[], int); + +extern void doprintlineno(struct input_file *); +extern void doprintfilename(struct input_file *); + +extern void doesyscmd(const char *); +extern void getdivfile(const char *); +extern void doformat(const char *[], int); + + +/* look.c */ + +#define FLAG_UNTRACED 0 +#define FLAG_TRACED 1 +#define FLAG_NO_TRACE 2 + +extern void init_macros(void); +extern ndptr lookup(const char *); +extern void mark_traced(const char *, int); +extern struct ohash macros; + +extern struct macro_definition *lookup_macro_definition(const char *); +extern void macro_define(const char *, const char *); +extern void macro_pushdef(const char *, const char *); +extern void macro_popdef(const char *); +extern void macro_undefine(const char *); +extern void setup_builtin(const char *, unsigned int); +extern void macro_for_all(void (*)(const char *, struct macro_definition *)); +#define macro_getdef(p) ((p)->d) +#define macro_name(p) ((p)->name) +#define macro_builtin_type(p) ((p)->builtin_type) +#define is_traced(p) ((p)->trace_flags == FLAG_NO_TRACE ? (trace_flags & TRACE_ALL) : (p)->trace_flags) + +extern ndptr macro_getbuiltin(const char *); + +/* main.c */ +extern void outputstr(const char *); +extern void do_emit_synchline(void); +#define emit_synchline() do { if (synch_lines) do_emit_synchline(); } while(0) + +/* misc.c */ +extern void chrsave(int); +extern char *compute_prevep(void); +extern void getdiv(int); +extern ptrdiff_t indx(const char *, const char *); +extern void initspaces(void); +extern void killdiv(void); +extern void pbnum(int); +extern void pbnumbase(int, int, int); +extern void pbunsigned(unsigned long); +extern void pbstr(const char *); +extern void pushback(int); +extern void *xalloc(size_t, const char *fmt, ...) __printflike(2, 3); +extern void *xrealloc(void *, size_t, const char *fmt, ...) + __printflike(3, 4); +extern char *xstrdup(const char *); +extern void resizedivs(int); +extern size_t buffer_mark(void); +extern void dump_buffer(FILE *, size_t); +extern void __dead m4errx(int, const char *, ...) __printflike(2, 3); + +extern int obtain_char(struct input_file *); +extern void set_input(struct input_file *, FILE *, const char *); +extern void release_input(struct input_file *); + +/* speeded-up versions of chrsave/pushback */ +#define PUSHBACK(c) \ + do { \ + if (bp >= endpbb) \ + enlarge_bufspace(); \ + *bp++ = (c); \ + } while(0) + +#define CHRSAVE(c) \ + do { \ + if (ep >= endest) \ + enlarge_strspace(); \ + *ep++ = (c); \ + } while(0) + +/* and corresponding exposure for local symbols */ +extern void enlarge_bufspace(void); +extern void enlarge_strspace(void); +extern unsigned char *endpbb; +extern char *endest; + +/* trace.c */ +extern unsigned int trace_flags; +#define TRACE_ALL 512 +extern void trace_file(const char *); +extern size_t trace(const char **, int, struct input_file *); +extern void finish_trace(size_t); +extern void set_trace_flags(const char *); +extern FILE *traceout; + +extern ndptr hashtab[]; /* hash table for macros etc. */ +extern stae *mstack; /* stack of m4 machine */ +extern char *sstack; /* shadow stack, for string space extension */ +extern FILE *active; /* active output file pointer */ +extern struct input_file infile[];/* input file stack (0=stdin) */ +extern FILE **outfile; /* diversion array(0=bitbucket) */ +extern int maxout; /* maximum number of diversions */ +extern int fp; /* m4 call frame pointer */ +extern int ilevel; /* input file stack pointer */ +extern int oindex; /* diversion index. */ +extern int sp; /* current m4 stack pointer */ +extern unsigned char *bp; /* first available character */ +extern unsigned char *buf; /* push-back buffer */ +extern unsigned char *bufbase; /* buffer base for this ilevel */ +extern unsigned char *bbase[]; /* buffer base per ilevel */ +extern char ecommt[MAXCCHARS+1];/* end character for comment */ +extern char *ep; /* first free char in strspace */ +extern char lquote[MAXCCHARS+1];/* left quote character (`) */ +extern char **m4wraps; /* m4wrap string default. */ +extern int maxwraps; /* size of m4wraps array */ +extern int wrapindex; /* current index in m4wraps */ + +extern const char *null; /* as it says.. just a null. */ +extern char rquote[MAXCCHARS+1];/* right quote character (') */ +extern char scommt[MAXCCHARS+1];/* start character for comment */ +extern int synch_lines; /* line synchronisation directives */ + +extern int mimic_gnu; /* behaves like gnu-m4 */ +extern int prefix_builtins; /* prefix builtin macros with m4_ */ diff --git a/buildrump.sh/src/usr.bin/m4/gnum4.c b/buildrump.sh/src/usr.bin/m4/gnum4.c new file mode 100644 index 00000000..168c3329 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/gnum4.c @@ -0,0 +1,669 @@ +/* $NetBSD: gnum4.c,v 1.9 2012/03/20 20:34:58 matt Exp $ */ +/* $OpenBSD: gnum4.c,v 1.39 2008/08/21 21:01:04 espie Exp $ */ + +/* + * Copyright (c) 1999 Marc Espie + * + * 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. + * + * 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. + */ + +/* + * functions needed to support gnu-m4 extensions, including a fake freezing + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: gnum4.c,v 1.9 2012/03/20 20:34:58 matt Exp $"); + +#include <sys/param.h> +#include <sys/types.h> +#include <sys/wait.h> +#include <ctype.h> +#include <err.h> +#include <paths.h> +#include <regex.h> +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" + + +int mimic_gnu = 0; +#ifndef SIZE_T_MAX +#define SIZE_T_MAX (size_t)~0ull +#endif + +/* + * Support for include path search + * First search in the current directory. + * If not found, and the path is not absolute, include path kicks in. + * First, -I options, in the order found on the command line. + * Then M4PATH env variable + */ + +struct path_entry { + char *name; + struct path_entry *next; +} *first, *last; + +static struct path_entry *new_path_entry(const char *); +static void ensure_m4path(void); +static struct input_file *dopath(struct input_file *, const char *); + +static struct path_entry * +new_path_entry(const char *dirname) +{ + struct path_entry *n; + + n = malloc(sizeof(struct path_entry)); + if (!n) + errx(1, "out of memory"); + n->name = strdup(dirname); + if (!n->name) + errx(1, "out of memory"); + n->next = 0; + return n; +} + +void +addtoincludepath(const char *dirname) +{ + struct path_entry *n; + + n = new_path_entry(dirname); + + if (last) { + last->next = n; + last = n; + } + else + last = first = n; +} + +static void +ensure_m4path(void) +{ + static int envpathdone = 0; + char *envpath; + char *sweep; + char *path; + + if (envpathdone) + return; + envpathdone = TRUE; + envpath = getenv("M4PATH"); + if (!envpath) + return; + /* for portability: getenv result is read-only */ + envpath = strdup(envpath); + if (!envpath) + errx(1, "out of memory"); + for (sweep = envpath; + (path = strsep(&sweep, ":")) != NULL;) + addtoincludepath(path); + free(envpath); +} + +static +struct input_file * +dopath(struct input_file *i, const char *filename) +{ + char path[MAXPATHLEN]; + struct path_entry *pe; + FILE *f; + + for (pe = first; pe; pe = pe->next) { + snprintf(path, sizeof(path), "%s/%s", pe->name, filename); + if ((f = fopen(path, "r")) != 0) { + set_input(i, f, path); + return i; + } + } + return NULL; +} + +struct input_file * +fopen_trypath(struct input_file *i, const char *filename) +{ + FILE *f; + + f = fopen(filename, "r"); + if (f != NULL) { + set_input(i, f, filename); + return i; + } + if (filename[0] == '/') + return NULL; + + ensure_m4path(); + + return dopath(i, filename); +} + +void +doindir(const char *argv[], int argc) +{ + ndptr n; + struct macro_definition *p; + + n = lookup(argv[2]); + if (n == NULL || (p = macro_getdef(n)) == NULL) + m4errx(1, "indir: undefined macro %s.", argv[2]); + argv[1] = p->defn; + + eval(argv+1, argc-1, p->type, is_traced(n)); +} + +void +dobuiltin(const char *argv[], int argc) +{ + ndptr p; + + argv[1] = NULL; + p = macro_getbuiltin(argv[2]); + if (p != NULL) + eval(argv+1, argc-1, macro_builtin_type(p), is_traced(p)); + else + m4errx(1, "unknown builtin %s.", argv[2]); +} + + +/* We need some temporary buffer space, as pb pushes BACK and substitution + * proceeds forward... */ +static char *buffer; +static size_t bufsize = 0; +static size_t current = 0; + +static void addchars(const char *, size_t); +static void addchar(int); +static char *twiddle(const char *); +static char *getstring(void); +static void exit_regerror(int, regex_t *) __dead; +static void do_subst(const char *, regex_t *, const char *, regmatch_t *); +static void do_regexpindex(const char *, regex_t *, regmatch_t *); +static void do_regexp(const char *, regex_t *, const char *, regmatch_t *); +static void add_sub(size_t, const char *, regex_t *, regmatch_t *); +static void add_replace(const char *, regex_t *, const char *, regmatch_t *); +#define addconstantstring(s) addchars((s), sizeof(s)-1) + +static void +addchars(const char *c, size_t n) +{ + if (n == 0) + return; + while (current + n > bufsize) { + if (bufsize == 0) + bufsize = 1024; + else + bufsize *= 2; + buffer = xrealloc(buffer, bufsize, NULL); + } + memcpy(buffer+current, c, n); + current += n; +} + +static void +addchar(int c) +{ + if (current +1 > bufsize) { + if (bufsize == 0) + bufsize = 1024; + else + bufsize *= 2; + buffer = xrealloc(buffer, bufsize, NULL); + } + buffer[current++] = c; +} + +static char * +getstring(void) +{ + addchar('\0'); + current = 0; + return buffer; +} + + +static void +exit_regerror(int er, regex_t *re) +{ + size_t errlen; + char *errbuf; + + errlen = regerror(er, re, NULL, 0); + errbuf = xalloc(errlen, + "malloc in regerror: %lu", (unsigned long)errlen); + regerror(er, re, errbuf, errlen); + m4errx(1, "regular expression error: %s.", errbuf); +} + +static void +add_sub(size_t n, const char *string, regex_t *re, regmatch_t *pm) +{ + if (n > re->re_nsub) + warnx("No subexpression %zu", n); + /* Subexpressions that did not match are + * not an error. */ + else if (pm[n].rm_so != -1 && + pm[n].rm_eo != -1) { + addchars(string + pm[n].rm_so, + pm[n].rm_eo - pm[n].rm_so); + } +} + +/* Add replacement string to the output buffer, recognizing special + * constructs and replacing them with substrings of the original string. + */ +static void +add_replace(const char *string, regex_t *re, const char *replace, regmatch_t *pm) +{ + const char *p; + + for (p = replace; *p != '\0'; p++) { + if (*p == '&' && !mimic_gnu) { + add_sub(0, string, re, pm); + continue; + } + if (*p == '\\') { + if (p[1] == '\\') { + addchar(p[1]); + p++; + continue; + } + if (p[1] == '&') { + if (mimic_gnu) + add_sub(0, string, re, pm); + else + addchar(p[1]); + p++; + continue; + } + if (isdigit((unsigned char)p[1])) { + add_sub(*(++p) - '0', string, re, pm); + continue; + } + } + addchar(*p); + } +} + +static void +do_subst(const char *string, regex_t *re, const char *replace, regmatch_t *pm) +{ + int error; + int flags = 0; + const char *last_match = NULL; + + while ((error = regexec(re, string, re->re_nsub+1, pm, flags)) == 0) { + if (pm[0].rm_eo != 0) { + if (string[pm[0].rm_eo-1] == '\n') + flags = 0; + else + flags = REG_NOTBOL; + } + + /* NULL length matches are special... We use the `vi-mode' + * rule: don't allow a NULL-match at the last match + * position. + */ + if (pm[0].rm_so == pm[0].rm_eo && + string + pm[0].rm_so == last_match) { + if (*string == '\0') + return; + addchar(*string); + if (*string++ == '\n') + flags = 0; + else + flags = REG_NOTBOL; + continue; + } + last_match = string + pm[0].rm_so; + addchars(string, pm[0].rm_so); + add_replace(string, re, replace, pm); + string += pm[0].rm_eo; + } + if (error != REG_NOMATCH) + exit_regerror(error, re); + pbstr(string); +} + +static void +do_regexp(const char *string, regex_t *re, const char *replace, regmatch_t *pm) +{ + int error; + + switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { + case 0: + add_replace(string, re, replace, pm); + pbstr(getstring()); + break; + case REG_NOMATCH: + break; + default: + exit_regerror(error, re); + } +} + +static void +do_regexpindex(const char *string, regex_t *re, regmatch_t *pm) +{ + int error; + + switch(error = regexec(re, string, re->re_nsub+1, pm, 0)) { + case 0: + pbunsigned(pm[0].rm_so); + break; + case REG_NOMATCH: + pbnum(-1); + break; + default: + exit_regerror(error, re); + } +} + +/* In Gnu m4 mode, parentheses for backmatch don't work like POSIX 1003.2 + * says. So we twiddle with the regexp before passing it to regcomp. + */ +static char * +twiddle(const char *p) +{ + /* + at start of regexp is a normal character for Gnu m4 */ + if (*p == '^') { + addchar(*p); + p++; + } + if (*p == '+') { + addchar('\\'); + } + /* This could use strcspn for speed... */ + while (*p != '\0') { + if (*p == '\\') { + switch(p[1]) { + case '(': + case ')': + case '|': + addchar(p[1]); + break; + case 'w': + addconstantstring("[_a-zA-Z0-9]"); + break; + case 'W': + addconstantstring("[^_a-zA-Z0-9]"); + break; + case '<': + addconstantstring("[[:<:]]"); + break; + case '>': + addconstantstring("[[:>:]]"); + break; + default: + addchars(p, 2); + break; + } + p+=2; + continue; + } + if (*p == '(' || *p == ')' || *p == '|') + addchar('\\'); + + addchar(*p); + p++; + } + return getstring(); +} + +/* patsubst(string, regexp, opt replacement) */ +/* argv[2]: string + * argv[3]: regexp + * argv[4]: opt rep + */ +void +dopatsubst(const char *argv[], int argc) +{ + if (argc <= 3) { + warnx("Too few arguments to patsubst"); + return; + } + /* special case: empty regexp */ + if (argv[3][0] == '\0') { + const char *s; + size_t len; + if (argv[4] && argc > 4) + len = strlen(argv[4]); + else + len = 0; + for (s = argv[2]; *s != '\0'; s++) { + addchars(argv[4], len); + addchar(*s); + } + } else { + int error; + regex_t re; + regmatch_t *pmatch; + int mode = REG_EXTENDED; + size_t l = strlen(argv[3]); + + if (!mimic_gnu || + (argv[3][0] == '^') || + (l > 0 && argv[3][l-1] == '$')) + mode |= REG_NEWLINE; + + error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], + mode); + if (error != 0) + exit_regerror(error, &re); + + pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL); + do_subst(argv[2], &re, + argc > 4 && argv[4] != NULL ? argv[4] : "", pmatch); + free(pmatch); + regfree(&re); + } + pbstr(getstring()); +} + +void +doregexp(const char *argv[], int argc) +{ + int error; + regex_t re; + regmatch_t *pmatch; + + if (argc <= 3) { + warnx("Too few arguments to regexp"); + return; + } + error = regcomp(&re, mimic_gnu ? twiddle(argv[3]) : argv[3], + REG_EXTENDED); + if (error != 0) + exit_regerror(error, &re); + + pmatch = xalloc(sizeof(regmatch_t) * (re.re_nsub+1), NULL); + if (argv[4] == NULL || argc == 4) + do_regexpindex(argv[2], &re, pmatch); + else + do_regexp(argv[2], &re, argv[4], pmatch); + free(pmatch); + regfree(&re); +} + +void +doformat(const char *argv[], int argc) +{ + const char *format = argv[2]; + int pos = 3; + int left_padded; + long width; + size_t l; + const char *thisarg; + char temp[2]; + size_t extra; + + while (*format != 0) { + if (*format != '%') { + addchar(*format++); + continue; + } + + format++; + if (*format == '%') { + addchar(*format++); + continue; + } + if (*format == 0) { + addchar('%'); + break; + } + + if (*format == '*') { + format++; + if (pos >= argc) + m4errx(1, + "Format with too many format specifiers."); + width = strtol(argv[pos++], NULL, 10); + } else { + char *eformat; + width = strtol(format, &eformat, 10); + format = eformat; + } + if (width < 0) { + left_padded = 1; + width = -width; + } else { + left_padded = 0; + } + if (*format == '.') { + format++; + if (*format == '*') { + format++; + if (pos >= argc) + m4errx(1, + "Format with too many format specifiers."); + extra = strtol(argv[pos++], NULL, 10); + } else { + char *eformat; + extra = strtol(format, &eformat, 10); + format = eformat; + } + } else { + extra = SIZE_T_MAX; + } + if (pos >= argc) + m4errx(1, "Format with too many format specifiers."); + switch(*format) { + case 's': + thisarg = argv[pos++]; + break; + case 'c': + temp[0] = strtoul(argv[pos++], NULL, 10); + temp[1] = 0; + thisarg = temp; + break; + default: + m4errx(1, "Unsupported format specification: %s.", + argv[2]); + } + format++; + l = strlen(thisarg); + if (l > extra) + l = extra; + if (!left_padded) { + while (l < (size_t)width--) + addchar(' '); + } + addchars(thisarg, l); + if (left_padded) { + while (l < (size_t)width--) + addchar(' '); + } + } + pbstr(getstring()); +} + +void +doesyscmd(const char *cmd) +{ + int p[2]; + pid_t pid, cpid; + const char *argv[4]; + int cc; + int status; + + /* Follow gnu m4 documentation: first flush buffers. */ + fflush(NULL); + + argv[0] = "sh"; + argv[1] = "-c"; + argv[2] = cmd; + argv[3] = NULL; + + /* Just set up standard output, share stderr and stdin with m4 */ + if (pipe(p) == -1) + err(1, "bad pipe"); + switch(cpid = fork()) { + case -1: + err(1, "bad fork"); + /* NOTREACHED */ + case 0: + (void) close(p[0]); + (void) dup2(p[1], 1); + (void) close(p[1]); + execv(_PATH_BSHELL, __UNCONST(argv)); + exit(1); + default: + /* Read result in two stages, since m4's buffer is + * pushback-only. */ + (void) close(p[1]); + do { + char result[BUFSIZE]; + cc = read(p[0], result, sizeof result); + if (cc > 0) + addchars(result, cc); + } while (cc > 0 || (cc == -1 && errno == EINTR)); + + (void) close(p[0]); + while ((pid = wait(&status)) != cpid && pid >= 0) + continue; + pbstr(getstring()); + } +} + +void +getdivfile(const char *name) +{ + FILE *f; + int c; + + f = fopen(name, "r"); + if (!f) + return; + + while ((c = getc(f))!= EOF) + putc(c, active); + (void) fclose(f); +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash.h b/buildrump.sh/src/usr.bin/m4/lib/ohash.h new file mode 100644 index 00000000..8ccdaf9c --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash.h @@ -0,0 +1,73 @@ +#ifndef OHASH_H +#define OHASH_H +/* $OpenBSD: ohash.h,v 1.8 2005/12/29 18:54:47 jaredy Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Open hashing support. + * Open hashing was chosen because it is much lighter than other hash + * techniques, and more efficient in most cases. + */ + +struct ohash_info { + ptrdiff_t key_offset; + void *data; /* user data */ + void *(*halloc)(size_t, void *); + void (*hfree)(void *, size_t, void *); + void *(*alloc)(size_t, void *); +}; + +struct _ohash_record; + +struct ohash { + struct _ohash_record *t; + struct ohash_info info; + unsigned int size; + unsigned int total; + unsigned int deleted; +}; + +/* For this to be tweakable, we use small primitives, and leave part of the + * logic to the client application. e.g., hashing is left to the client + * application. We also provide a simple table entry lookup that yields + * a hashing table index (opaque) to be used in find/insert/remove. + * The keys are stored at a known position in the client data. + */ +__BEGIN_DECLS +void ohash_init(struct ohash *, unsigned, struct ohash_info *); +void ohash_delete(struct ohash *); + +unsigned int ohash_lookup_interval(struct ohash *, const char *, + const char *, u_int32_t); +unsigned int ohash_lookup_memory(struct ohash *, const char *, + size_t, u_int32_t); +void *ohash_find(struct ohash *, unsigned int); +void *ohash_remove(struct ohash *, unsigned int); +void *ohash_insert(struct ohash *, unsigned int, void *); +void *ohash_first(struct ohash *, unsigned int *); +void *ohash_next(struct ohash *, unsigned int *); +unsigned int ohash_entries(struct ohash *); + +void *ohash_create_entry(struct ohash_info *, const char *, const char **); +u_int32_t ohash_interval(const char *, const char **); + +unsigned int ohash_qlookupi(struct ohash *, const char *, const char **); +unsigned int ohash_qlookup(struct ohash *, const char *); +__END_DECLS +#endif + diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_create_entry.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_create_entry.c new file mode 100644 index 00000000..3bdc9aba --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_create_entry.c @@ -0,0 +1,38 @@ +/* $OpenBSD: ohash_create_entry.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +/* This handles the common case of variable length keys, where the + * key is stored at the end of the record. + */ +void * +ohash_create_entry(struct ohash_info *i, const char *start, const char **end) +{ + char *p; + + if (!*end) + *end = start + strlen(start); + p = (i->alloc)(i->key_offset + (*end - start) + 1, i->data); + if (p) { + memcpy(p+i->key_offset, start, *end-start); + p[i->key_offset + (*end - start)] = '\0'; + } + return (void *)p; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_delete.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_delete.c new file mode 100644 index 00000000..12a9c0c3 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_delete.c @@ -0,0 +1,31 @@ +/* $OpenBSD: ohash_delete.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" +/* hash_delete only frees the hash structure. Use hash_first/hash_next + * to free entries as well. */ +void +ohash_delete(struct ohash *h) +{ + (h->info.hfree)(h->t, sizeof(struct _ohash_record) * h->size, + h->info.data); +#ifndef NDEBUG + h->t = NULL; +#endif +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_do.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_do.c new file mode 100644 index 00000000..2b8467dd --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_do.c @@ -0,0 +1,111 @@ +/* $OpenBSD: ohash_do.c,v 1.4 2004/06/22 20:00:16 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +static void ohash_resize(struct ohash *); + +static void +ohash_resize(struct ohash *h) +{ + struct _ohash_record *n; + unsigned int ns, j; + unsigned int i, incr; + + if (4 * h->deleted < h->total) + ns = h->size << 1; + else if (3 * h->deleted > 2 * h->total) + ns = h->size >> 1; + else + ns = h->size; + if (ns < MINSIZE) + ns = MINSIZE; +#ifdef STATS_HASH + STAT_HASH_EXPAND++; + STAT_HASH_SIZE += ns - h->size; +#endif + n = (h->info.halloc)(sizeof(struct _ohash_record) * ns, h->info.data); + if (!n) + return; + + for (j = 0; j < h->size; j++) { + if (h->t[j].p != NULL && h->t[j].p != DELETED) { + i = h->t[j].hv % ns; + incr = ((h->t[j].hv % (ns - 2)) & ~1) + 1; + while (n[i].p != NULL) { + i += incr; + if (i >= ns) + i -= ns; + } + n[i].hv = h->t[j].hv; + n[i].p = h->t[j].p; + } + } + (h->info.hfree)(h->t, sizeof(struct _ohash_record) * h->size, + h->info.data); + h->t = n; + h->size = ns; + h->total -= h->deleted; + h->deleted = 0; +} + +void * +ohash_remove(struct ohash *h, unsigned int i) +{ + void *result = __UNCONST(h->t[i].p); + + if (result == NULL || result == DELETED) + return NULL; + +#ifdef STATS_HASH + STAT_HASH_ENTRIES--; +#endif + h->t[i].p = DELETED; + h->deleted++; + if (h->deleted >= MINDELETED && 4 * h->deleted > h->total) + ohash_resize(h); + return result; +} + +void * +ohash_find(struct ohash *h, unsigned int i) +{ + if (h->t[i].p == DELETED) + return NULL; + else + return __UNCONST(h->t[i].p); +} + +void * +ohash_insert(struct ohash *h, unsigned int i, void *p) +{ +#ifdef STATS_HASH + STAT_HASH_ENTRIES++; +#endif + if (h->t[i].p == DELETED) { + h->deleted--; + h->t[i].p = p; + } else { + h->t[i].p = p; + /* Arbitrary resize boundary. Tweak if not efficient enough. */ + if (++h->total * 4 > h->size * 3) + ohash_resize(h); + } + return p; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_entries.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_entries.c new file mode 100644 index 00000000..683e2045 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_entries.c @@ -0,0 +1,26 @@ +/* $OpenBSD: ohash_entries.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +unsigned int +ohash_entries(struct ohash *h) +{ + return h->total - h->deleted; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_enum.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_enum.c new file mode 100644 index 00000000..966daa17 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_enum.c @@ -0,0 +1,36 @@ +/* $OpenBSD: ohash_enum.c,v 1.3 2004/06/22 20:00:16 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +void * +ohash_first(struct ohash *h, unsigned int *pos) +{ + *pos = 0; + return ohash_next(h, pos); +} + +void * +ohash_next(struct ohash *h, unsigned int *pos) +{ + for (; *pos < h->size; (*pos)++) + if (h->t[*pos].p != DELETED && h->t[*pos].p != NULL) + return __UNCONST(h->t[(*pos)++].p); + return NULL; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_init.3 b/buildrump.sh/src/usr.bin/m4/lib/ohash_init.3 new file mode 100644 index 00000000..a0939a8e --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_init.3 @@ -0,0 +1,229 @@ +.\" $OpenBSD: ohash_init.3,v 1.14 2007/05/31 19:19:30 jmc Exp $ +.\" Copyright (c) 1999 Marc Espie <espie@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: May 31 2007 $ +.Dt OPEN_HASH 3 +.Os +.Sh NAME +.Nm ohash_init , +.Nm ohash_delete , +.Nm ohash_lookup_interval , +.Nm ohash_lookup_memory , +.Nm ohash_find , +.Nm ohash_remove , +.Nm ohash_insert , +.Nm ohash_first , +.Nm ohash_next , +.Nm ohash_entries +.Nd light-weight open hashing +.Sh SYNOPSIS +.Fd #include <stdint.h> +.Fd #include <stddef.h> +.Fd #include <ohash.h> +.Ft void +.Fn ohash_init "struct ohash *h" "unsigned int size" "struct ohash_info *info" +.Ft void +.Fn ohash_delete "struct ohash *h" +.Ft "unsigned int" +.Fn ohash_lookup_interval "struct ohash *h" "const char *start" "const char *end" "uint32_t hv" +.Ft "unsigned int" +.Fn ohash_lookup_memory "struct ohash *h" "const char *k" "size_t s" "uint32_t hv" +.Ft void * +.Fn ohash_find "struct ohash *h" "unsigned int i" +.Ft void * +.Fn ohash_remove "struct ohash *h" "unsigned int i" +.Ft void * +.Fn ohash_insert "struct ohash *h" "unsigned int i" "void *p" +.Ft void * +.Fn ohash_first "struct ohash *h" "unsigned int *i" +.Ft void * +.Fn ohash_next "struct ohash *h" "unsigned int *i" +.Ft "unsigned int" +.Fn ohash_entries "struct ohash *h" +.Sh DESCRIPTION +These functions have been designed as a fast, extensible alternative to +the usual hash table functions. +They provide storage and retrieval of records indexed by keys, +where a key is a contiguous sequence of bytes at a fixed position in +each record. +Keys can either be NUL-terminated strings or fixed-size memory areas. +All functions take a pointer to an ohash structure as the +.Fa h +function argument. +Storage for this structure should be provided by user code. +.Pp +.Fn ohash_init +initializes the table to store roughly 2 to the power +.Fa size +elements. +.Fa info +holds the position of the key in each record, and two pointers to +.Xr calloc 3 +and +.Xr free 3 Ns -like +functions, to use for managing the table internal storage. +.Pp +.Fn ohash_delete +frees storage internal to +.Fa h . +Elements themselves should be freed by the user first, using for instance +.Fn ohash_first +and +.Fn ohash_next . +.Pp +.Fn ohash_lookup_interval +and +.Fn ohash_lookup_memory +are the basic look-up element functions. +The hashing function result is provided by the user as +.Fa hv . +These return a +.Qq slot +in the ohash table +.Fa h , +to be used with +.Fn ohash_find , +.Fn ohash_insert , +or +.Fn ohash_remove . +This slot is only valid up to the next call to +.Fn ohash_insert +or +.Fn ohash_remove . +.Pp +.Fn ohash_lookup_interval +handles string-like keys. +.Fn ohash_lookup_interval +assumes the key is the interval between +.Fa start +and +.Fa end , +exclusive, +though the actual elements stored in the table should only contain +NUL-terminated keys. +.Pp +.Fn ohash_lookup_memory +assumes the key is the memory area starting at +.Fa k +of size +.Fa s . +All bytes are significant in key comparison. +.Pp +.Fn ohash_find +retrieves an element from a slot +.Fa i +returned by the +.Fn ohash_lookup* +functions. +It returns +.Dv NULL +if the slot is empty. +.Pp +.Fn ohash_insert +inserts a new element +.Fa p +at slot +.Fa i . +Slot +.Fa i +must be empty and element +.Fa p +must have a key corresponding to the +.Fn ohash_lookup* +call. +.Pp +.Fn ohash_remove +removes the element at slot +.Fa i . +It returns the removed element, for user code to dispose of, or +.Dv NULL +if the slot was empty. +.Pp +.Fn ohash_first +and +.Fn ohash_next +can be used to access all elements in an ohash table, like this: +.Bd -literal -offset indent +for (n = ohash_first(h, &i); n != NULL; n = ohash_next(h, &i)) + do_something_with(n); +.Ed +.Pp +.Fa i +points to an auxiliary unsigned integer used to record the current position +in the ohash table. +Those functions are safe to use even while entries are added to/removed +from the table, but in such a case they don't guarantee that new entries +will be returned. +As a special case, they can safely be used to free elements in the table. +.Pp +.Fn ohash_entries +returns the number of elements in the hash table. +.Sh STORAGE HANDLING +Only +.Fn ohash_init , +.Fn ohash_insert , +.Fn ohash_remove +and +.Fn ohash_delete +may call the user-supplied memory functions. +It is the responsibility of the user memory allocation code to verify +that those calls did not fail. +.Pp +If memory allocation fails, +.Fn ohash_init +returns a useless hash table. +.Fn ohash_insert +and +.Fn ohash_remove +still perform the requested operation, but the returned table should be +considered read-only. +It can still be accessed by +.Fn ohash_lookup* , +.Fn ohash_find , +.Fn ohash_first +and +.Fn ohash_next +to dump relevant information to disk before aborting. +.Sh THREAD SAFETY +The open hashing functions are not thread-safe by design. +In particular, in a threaded environment, there is no guarantee that a +.Qq slot +will not move between a +.Fn ohash_lookup* +and a +.Fn ohash_find , +.Fn ohash_insert +or +.Fn ohash_remove +call. +.Pp +Multi-threaded applications should explicitly protect ohash table access. +.Sh SEE ALSO +.Xr ohash_interval 3 +.Rs +.%A Donald E. Knuth +.%B The Art of Computer Programming +.%V Vol. 3 +.%P pp 506-550 +.%D 1973 +.Re +.Sh STANDARDS +Those functions are completely non-standard and should be avoided in +portable programs. +.Sh HISTORY +Those functions were designed and written for +.Ox +.Xr make 1 +by Marc Espie in 1999. diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_init.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_init.c new file mode 100644 index 00000000..4d24fa4b --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_init.c @@ -0,0 +1,41 @@ +/* $OpenBSD: ohash_init.c,v 1.2 2004/06/22 20:00:16 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +void +ohash_init(struct ohash *h, unsigned int size, struct ohash_info *info) +{ + h->size = 1UL << size; + if (h->size < MINSIZE) + h->size = MINSIZE; +#ifdef STATS_HASH + STAT_HASH_CREATION++; + STAT_HASH_SIZE += h->size; +#endif + /* Copy info so that caller may free it. */ + h->info.key_offset = info->key_offset; + h->info.halloc = info->halloc; + h->info.hfree = info->hfree; + h->info.alloc = info->alloc; + h->info.data = info->data; + h->t = (h->info.halloc)(sizeof(struct _ohash_record) * h->size, + h->info.data); + h->total = h->deleted = 0; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_int.h b/buildrump.sh/src/usr.bin/m4/lib/ohash_int.h new file mode 100644 index 00000000..88c818b8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_int.h @@ -0,0 +1,23 @@ +/* $OpenBSD: ohash_int.h,v 1.3 2006/01/16 15:52:25 espie Exp $ */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include "ohash.h" + +struct _ohash_record { + u_int32_t hv; + const char *p; +}; + +#define DELETED ((const char *)h) +#define NONE (h->size) + +/* Don't bother changing the hash table if the change is small enough. */ +#define MINSIZE (1UL << 4) +#define MINDELETED 4 diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_interval.3 b/buildrump.sh/src/usr.bin/m4/lib/ohash_interval.3 new file mode 100644 index 00000000..2e56ca5b --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_interval.3 @@ -0,0 +1,90 @@ +.\" $OpenBSD: ohash_interval.3,v 1.11 2007/05/31 19:19:30 jmc Exp $ +.\" Copyright (c) 2001 Marc Espie <espie@openbsd.org> +.\" +.\" Permission to use, copy, modify, and distribute this software for any +.\" purpose with or without fee is hereby granted, provided that the above +.\" copyright notice and this permission notice appear in all copies. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +.\" +.Dd $Mdocdate: May 31 2007 $ +.Dt OPEN_HASH_HELPER 3 +.Os +.Sh NAME +.Nm ohash_interval , +.Nm ohash_create_entry , +.Nm ohash_qlookup , +.Nm ohash_qlookupi +.Nd helper functions for open hashing +.Sh SYNOPSIS +.Fd #include <stdint.h> +.Fd #include <stddef.h> +.Fd #include <ohash.h> +.Ft u_int32_t +.Fn ohash_interval "const char *start" "const char **pend" +.Ft "void *" +.Fn ohash_create_entry "struct ohash_info *info" "const char *start" "const char **pend" +.Ft "unsigned int" +.Fn ohash_qlookupi "struct ohash *h" "const char *start" "const char **pend" +.Ft "unsigned int" +.Fn ohash_qlookup "struct ohash *h" "const char *start" +.Sh DESCRIPTION +These functions are commonly used to simplify open hashing usage, and use +similar conventions. +They operate indifferently on NUL-terminated strings +.Po +by setting +.Fa *pend += +.Dv NULL +.Pc +or memory ranges +.Po +delimited by +.Fa start +and +.Fa *pend +.Pc . +For NUL-terminated strings, as a side effect, those functions +set +.Fa *pend +to the terminating NUL byte. +.Pp +.Fn ohash_interval +is a simple hashing function that yields good results on common data sets. +.Pp +.Fn ohash_create_entry +can be used to create a new record with a given key. +In that case, +the alloc field of +.Fa info +should point to a +.Xr malloc 3 Ns -like +function to allocate the storage. +.Pp +.Fn ohash_qlookupi +is a wrapper function that simply calls +.Fn ohash_interval +and +.Fn ohash_lookup_interval . +.Pp +.Fn ohash_qlookup +is a variation on +.Fn ohash_qlookupi +designed for NUL-terminated strings. +.Sh SEE ALSO +.Xr ohash_init 3 +.Sh STANDARDS +Those functions are completely non-standard and should be avoided in +portable programs. +.Sh HISTORY +Those functions were designed and written for +.Ox +.Xr make 1 +by Marc Espie in 1999. diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_interval.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_interval.c new file mode 100644 index 00000000..2434d3b0 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_interval.c @@ -0,0 +1,36 @@ +/* $OpenBSD: ohash_interval.c,v 1.3 2006/01/16 15:52:25 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +uint32_t +ohash_interval(const char *s, const char **e) +{ + uint32_t k; + + if (!*e) + *e = s + strlen(s); + if (s == *e) + k = 0; + else + k = *s++; + while (s != *e) + k = ((k << 2) | (k >> 30)) ^ *s++; + return k; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_lookup_interval.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_lookup_interval.c new file mode 100644 index 00000000..e4978016 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_lookup_interval.c @@ -0,0 +1,68 @@ +/* $OpenBSD: ohash_lookup_interval.c,v 1.3 2006/01/16 15:52:25 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +unsigned int +ohash_lookup_interval(struct ohash *h, const char *start, const char *end, + uint32_t hv) +{ + unsigned int i, incr; + unsigned int empty; + +#ifdef STATS_HASH + STAT_HASH_LOOKUP++; +#endif + empty = NONE; + i = hv % h->size; + incr = ((hv % (h->size-2)) & ~1) + 1; + while (h->t[i].p != NULL) { +#ifdef STATS_HASH + STAT_HASH_LENGTH++; +#endif + if (h->t[i].p == DELETED) { + if (empty == NONE) + empty = i; + } else if (h->t[i].hv == hv && + strncmp(h->t[i].p+h->info.key_offset, start, + end - start) == 0 && + (h->t[i].p+h->info.key_offset)[end-start] == '\0') { + if (empty != NONE) { + h->t[empty].hv = hv; + h->t[empty].p = h->t[i].p; + h->t[i].p = DELETED; + return empty; + } else { +#ifdef STATS_HASH + STAT_HASH_POSITIVE++; +#endif + return i; + } + } + i += incr; + if (i >= h->size) + i -= h->size; + } + + /* Found an empty position. */ + if (empty != NONE) + i = empty; + h->t[i].hv = hv; + return i; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_lookup_memory.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_lookup_memory.c new file mode 100644 index 00000000..93ded5ae --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_lookup_memory.c @@ -0,0 +1,64 @@ +/* $OpenBSD: ohash_lookup_memory.c,v 1.3 2006/01/16 15:52:25 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +unsigned int +ohash_lookup_memory(struct ohash *h, const char *k, size_t size, uint32_t hv) +{ + unsigned int i, incr; + unsigned int empty; + +#ifdef STATS_HASH + STAT_HASH_LOOKUP++; +#endif + empty = NONE; + i = hv % h->size; + incr = ((hv % (h->size-2)) & ~1) + 1; + while (h->t[i].p != NULL) { +#ifdef STATS_HASH + STAT_HASH_LENGTH++; +#endif + if (h->t[i].p == DELETED) { + if (empty == NONE) + empty = i; + } else if (h->t[i].hv == hv && + memcmp(h->t[i].p+h->info.key_offset, k, size) == 0) { + if (empty != NONE) { + h->t[empty].hv = hv; + h->t[empty].p = h->t[i].p; + h->t[i].p = DELETED; + return empty; + } else { +#ifdef STATS_HASH + STAT_HASH_POSITIVE++; +#endif + } return i; + } + i += incr; + if (i >= h->size) + i -= h->size; + } + + /* Found an empty position. */ + if (empty != NONE) + i = empty; + h->t[i].hv = hv; + return i; +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_qlookup.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_qlookup.c new file mode 100644 index 00000000..58b3454d --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_qlookup.c @@ -0,0 +1,27 @@ +/* $OpenBSD: ohash_qlookup.c,v 1.2 2004/06/22 20:00:17 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +unsigned int +ohash_qlookup(struct ohash *h, const char *s) +{ + const char *e = NULL; + return ohash_qlookupi(h, s, &e); +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/ohash_qlookupi.c b/buildrump.sh/src/usr.bin/m4/lib/ohash_qlookupi.c new file mode 100644 index 00000000..2acec4e8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/ohash_qlookupi.c @@ -0,0 +1,29 @@ +/* $OpenBSD: ohash_qlookupi.c,v 1.2 2004/06/22 20:00:17 espie Exp $ */ +/* ex:ts=8 sw=4: + */ + +/* Copyright (c) 1999, 2004 Marc Espie <espie@openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "ohash_int.h" + +unsigned int +ohash_qlookupi(struct ohash *h, const char *s, const char **e) +{ + u_int32_t hv; + + hv = ohash_interval(s, e); + return ohash_lookup_interval(h, s, *e, hv); +} diff --git a/buildrump.sh/src/usr.bin/m4/lib/strtonum.c b/buildrump.sh/src/usr.bin/m4/lib/strtonum.c new file mode 100644 index 00000000..8d11712e --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/lib/strtonum.c @@ -0,0 +1,73 @@ +/* $OpenBSD: strtonum.c,v 1.6 2004/08/03 19:38:01 millert Exp $ */ + +/* + * Copyright (c) 2004 Ted Unangst and Todd Miller + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: strtonum.c,v 1.2 2009/10/26 21:14:18 christos Exp $"); +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +#define INVALID 1 +#define TOOSMALL 2 +#define TOOLARGE 3 + +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp); +long long +strtonum(const char *numstr, long long minval, long long maxval, + const char **errstrp) +{ + long long ll = 0; + char *ep; + int error = 0; + struct errval { + const char *errstr; + int err; + } ev[4] = { + { NULL, 0 }, + { "invalid", EINVAL }, + { "too small", ERANGE }, + { "too large", ERANGE }, + }; + + ev[0].err = errno; + errno = 0; + if (minval > maxval) + error = INVALID; + else { + ll = strtoll(numstr, &ep, 10); + if (numstr == ep || *ep != '\0') + error = INVALID; + else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) + error = TOOSMALL; + else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) + error = TOOLARGE; + } + if (errstrp != NULL) + *errstrp = ev[error].errstr; + errno = ev[error].err; + if (error) + ll = 0; + + return (ll); +} + diff --git a/buildrump.sh/src/usr.bin/m4/look.c b/buildrump.sh/src/usr.bin/m4/look.c new file mode 100644 index 00000000..d88dc9e0 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/look.c @@ -0,0 +1,280 @@ +/* $NetBSD: look.c,v 1.12 2012/03/20 20:34:58 matt Exp $ */ +/* $OpenBSD: look.c,v 1.21 2009/10/14 17:23:17 sthen Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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. + */ + +/* + * look.c + * Facility: m4 macro processor + * by: oz + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: look.c,v 1.12 2012/03/20 20:34:58 matt Exp $"); +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <stddef.h> +#include <string.h> +#include <ohash.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" + +static void *hash_alloc(size_t, void *); +static void hash_free(void *, size_t, void *); +static void *element_alloc(size_t, void *); +static void setup_definition(struct macro_definition *, const char *, + const char *); + +static struct ohash_info macro_info = { + offsetof(struct ndblock, name), + NULL, hash_alloc, hash_free, element_alloc }; + +struct ohash macros; + +/* Support routines for hash tables. */ +void * +hash_alloc(size_t s, void *u UNUSED) +{ + void *storage = xalloc(s, "hash alloc"); + if (storage) + memset(storage, 0, s); + return storage; +} + +void +hash_free(void *p, size_t s UNUSED, void *u UNUSED) +{ + free(p); +} + +void * +element_alloc(size_t s, void *u UNUSED) +{ + return xalloc(s, "element alloc"); +} + +void +init_macros(void) +{ + ohash_init(¯os, 10, ¯o_info); +} + +/* + * find name in the hash table + */ +ndptr +lookup(const char *name) +{ + return ohash_find(¯os, ohash_qlookup(¯os, name)); +} + +struct macro_definition * +lookup_macro_definition(const char *name) +{ + ndptr p; + + p = ohash_find(¯os, ohash_qlookup(¯os, name)); + if (p) + return p->d; + else + return NULL; +} + +static void +setup_definition(struct macro_definition *d, const char *defn, const char *name) +{ + ndptr p; + + if (strncmp(defn, BUILTIN_MARKER, sizeof(BUILTIN_MARKER)-1) == 0 && + (p = macro_getbuiltin(defn+sizeof(BUILTIN_MARKER)-1)) != NULL) { + d->type = macro_builtin_type(p); + d->defn = xstrdup(defn+sizeof(BUILTIN_MARKER)-1); + } else { + if (!*defn) + d->defn = xstrdup(null); + else + d->defn = xstrdup(defn); + d->type = MACRTYPE; + } + if (STREQ(name, defn)) + d->type |= RECDEF; +} + +static ndptr +create_entry(const char *name) +{ + const char *end = NULL; + unsigned int i; + ndptr n; + + i = ohash_qlookupi(¯os, name, &end); + n = ohash_find(¯os, i); + if (n == NULL) { + n = ohash_create_entry(¯o_info, name, &end); + ohash_insert(¯os, i, n); + n->trace_flags = FLAG_NO_TRACE; + n->builtin_type = MACRTYPE; + n->d = NULL; + } + return n; +} + +void +macro_define(const char *name, const char *defn) +{ + ndptr n = create_entry(name); + if (n->d != NULL) { + if (n->d->defn != null) + free(n->d->defn); + } else { + n->d = xalloc(sizeof(struct macro_definition), NULL); + n->d->next = NULL; + } + setup_definition(n->d, defn, name); +} + +void +macro_pushdef(const char *name, const char *defn) +{ + ndptr n; + struct macro_definition *d; + + n = create_entry(name); + d = xalloc(sizeof(struct macro_definition), NULL); + d->next = n->d; + n->d = d; + setup_definition(n->d, defn, name); +} + +void +macro_undefine(const char *name) +{ + ndptr n = lookup(name); + if (n != NULL) { + struct macro_definition *r, *r2; + + for (r = n->d; r != NULL; r = r2) { + r2 = r->next; + if (r->defn != null) + free(r->defn); + free(r); + } + n->d = NULL; + } +} + +void +macro_popdef(const char *name) +{ + ndptr n = lookup(name); + + if (n != NULL) { + struct macro_definition *r = n->d; + if (r != NULL) { + n->d = r->next; + if (r->defn != null) + free(r->defn); + free(r); + } + } +} + +void +macro_for_all(void (*f)(const char *, struct macro_definition *)) +{ + ndptr n; + unsigned int i; + + for (n = ohash_first(¯os, &i); n != NULL; + n = ohash_next(¯os, &i)) + if (n->d != NULL) + f(n->name, n->d); +} + +void +setup_builtin(const char *name, unsigned int type) +{ + ndptr n; + char *name2; + + if (prefix_builtins) { + name2 = xalloc(strlen(name)+3+1, NULL); + memcpy(name2, "m4_", 3); + memcpy(name2 + 3, name, strlen(name)+1); + } else + name2 = xstrdup(name); + + n = create_entry(name2); + n->builtin_type = type; + n->d = xalloc(sizeof(struct macro_definition), NULL); + n->d->defn = name2; + n->d->type = type; + n->d->next = NULL; +} + +void +mark_traced(const char *name, int on) +{ + ndptr p; + unsigned int i; + + if (name == NULL) { + if (on) + trace_flags |= TRACE_ALL; + else + trace_flags &= ~TRACE_ALL; + for (p = ohash_first(¯os, &i); p != NULL; + p = ohash_next(¯os, &i)) + p->trace_flags = FLAG_NO_TRACE; + } else { + p = create_entry(name); + p->trace_flags = on; + } +} + +ndptr +macro_getbuiltin(const char *name) +{ + ndptr p; + + p = lookup(name); + if (p == NULL || p->builtin_type == MACRTYPE) + return NULL; + else + return p; +} + diff --git a/buildrump.sh/src/usr.bin/m4/m4.1 b/buildrump.sh/src/usr.bin/m4/m4.1 new file mode 100644 index 00000000..dbe5e34b --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/m4.1 @@ -0,0 +1,493 @@ +.\" $NetBSD: m4.1,v 1.25 2014/01/07 13:32:21 wiz Exp $ +.\" @(#) $OpenBSD: m4.1,v 1.56 2009/10/14 17:19:47 sthen Exp $ +.\" +.\" Copyright (c) 1989, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Ozan Yigit at York University. +.\" +.\" 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. +.\" +.Dd January 7, 2014 +.Dt M4 1 +.Os +.Sh NAME +.Nm m4 +.Nd macro language processor +.Sh SYNOPSIS +.Nm m4 +.Op Fl gPs +.Oo +.Sm off +.Fl D Ar name Op No = Ar value +.Sm on +.Oc +.Op Fl d Ar flags +.Op Fl I Ar dirname +.Op Fl o Ar filename +.Op Fl t Ar macro +.Op Fl U Ns Ar name +.Op Ar +.Sh DESCRIPTION +The +.Nm m4 +utility is a macro processor that can be used as a front end to any +language (e.g., C, ratfor, fortran, lex, and yacc). +If no input files are given, +.Nm m4 +reads from the standard input, +otherwise files specified on the command line are +processed in the given order. +Input files can be regular files, files in the m4 include paths, or a +single dash +.Pq Sq - , +denoting standard input. +.Nm m4 +writes +the processed text to the standard output, unless told otherwise. +.Pp +Macro calls have the form name(argument1[, argument2, ..., argumentN]). +.Pp +There cannot be any space following the macro name and the open +parenthesis +.Sq \&( . +If the macro name is not followed by an open +parenthesis it is processed with no arguments. +.Pp +Macro names consist of a leading alphabetic or underscore +possibly followed by alphanumeric or underscore characters, e.g., +valid macro names match the pattern +.Dq [a-zA-Z_][a-zA-Z0-9_]* . +.Pp +In arguments to macros, leading unquoted space, tab, and newline +.Pq Sq \en +characters are ignored. +To quote strings, use left and right single quotes +.Po e.g.,\ \& +.Sq "\ this is a string with a leading space" +.Pc . +You can change the quote characters with the +.Ic changequote +built-in macro. +.Pp +Most built-ins don't make any sense without arguments, and hence are not +recognized as special when not followed by an open parenthesis. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl D Ns Ar name Ns Op Pf = Ns Ar value +Define the symbol +.Ar name +to have some value (or +.Dv NULL ) . +.It Fl d Ar "flags" +Set trace flags. +.Ar flags +may hold the following: +.Bl -tag -width Ds +.It Ar a +print macro arguments. +.It Ar c +print macro expansion over several lines. +.It Ar e +print result of macro expansion. +.It Ar f +print filename location. +.It Ar l +print line number. +.It Ar q +quote arguments and expansion with the current quotes. +.It Ar t +start with all macros traced. +.It Ar x +number macro expansions. +.It Ar V +turn on all options. +.El +.Pp +By default, trace is set to +.Qq eq . +.It Fl g +Activate GNU-m4 compatibility mode. +In this mode, translit handles simple character +ranges (e.g., a-z), regular expressions mimic emacs behavior, +multiple m4wrap calls are handled as a stack, +the number of diversions is unlimited, +empty names for macro definitions are allowed, +and eval understands +.Sq 0rbase:value +numbers. +.It Fl I Ar "dirname" +Add directory +.Ar dirname +to the include path. +.It Fl o Ar filename +Send trace output to +.Ar filename . +.It Fl P +Prefix all built-in macros with +.Sq m4_ . +For example, instead of writing +.Ic define , +use +.Ic m4_define . +.It Fl s +Output line synchronization directives, suitable for +.Xr cpp 1 . +.It Fl t Ar macro +Turn tracing on for +.Ar macro . +.It Fl "U" Ns Ar "name" +Undefine the symbol +.Ar name . +.El +.Sh SYNTAX +.Nm m4 +provides the following built-in macros. +They may be redefined, losing their original meaning. +Return values are null unless otherwise stated. +.Bl -tag -width changequote +.It Fn builtin name +Calls a built-in by its +.Fa name , +overriding possible redefinitions. +.It Fn changecom startcomment endcomment +Changes the start comment and end comment sequences. +Comment sequences may be up to five characters long. +The default values are the hash sign +and the newline character. +.Bd -literal -offset indent +# This is a comment +.Ed +.Pp +With no arguments, comments are turned off. +With one single argument, the end comment sequence is set +to the newline character. +.It Fn changequote beginquote endquote +Defines the open quote and close quote sequences. +Quote sequences may be up to five characters long. +The default values are the backquote character and the quote +character. +.Bd -literal -offset indent +`Here is a quoted string' +.Ed +.Pp +With no arguments, the default quotes are restored. +With one single argument, the close quote sequence is set +to the newline character. +.It Fn decr arg +Decrements the argument +.Fa arg +by 1. +The argument +.Fa arg +must be a valid numeric string. +.It Fn define name value +Define a new macro named by the first argument +.Fa name +to have the +value of the second argument +.Fa value . +Each occurrence of +.Sq $n +(where +.Ar n +is 0 through 9) is replaced by the +.Ar n Ns 'th +argument. +.Sq $0 +is the name of the calling macro. +Undefined arguments are replaced by a null string. +.Sq $# +is replaced by the number of arguments; +.Sq $* +is replaced by all arguments comma separated; +.Sq $@ +is the same as +.Sq $* +but all arguments are quoted against further expansion. +.It Fn defn name ... +Returns the quoted definition for each argument. +This can be used to rename +macro definitions (even for built-in macros). +.It Fn divert num +There are 10 output queues (numbered 0-9). +At the end of processing +.Nm m4 +concatenates all the queues in numerical order to produce the +final output. +Initially the output queue is 0. +The divert +macro allows you to select a new output queue (an invalid argument +passed to divert causes output to be discarded). +.It Ic divnum +Returns the current output queue number. +.It Ic dnl +Discard input characters up to and including the next newline. +.It Fn dumpdef name ... +Prints the names and definitions for the named items, or for everything +if no arguments are passed. +.It Fn errprint msg +Prints the first argument on the standard error output stream. +.It Fn esyscmd cmd +Passes its first argument to a shell and returns the shell's standard output. +Note that the shell shares its standard input and standard error with +.Nm m4 . +.It Fn eval expr[,radix[,minimum]] +Computes the first argument as an arithmetic expression using 32-bit +arithmetic. +Operators are the standard C ternary, arithmetic, logical, +shift, relational, bitwise, and parentheses operators. +You can specify +octal, decimal, and hexadecimal numbers as in C. +The optional second argument +.Fa radix +specifies the radix for the result and the optional third argument +.Fa minimum +specifies the minimum number of digits in the result. +.It Fn expr expr +This is an alias for +.Ic eval . +.It Fn format formatstring arg1 ... +Returns +.Fa formatstring +with escape sequences substituted with +.Fa arg1 +and following arguments, in a way similar to +.Xr printf 3 . +This built-in is only available in GNU-m4 compatibility mode, and the only +parameters implemented are there for autoconf compatibility: +left-padding flag, an optional field width, a maximum field width, +*-specified field widths, and the %s and %c data type. +.It Fn ifdef name yes no +If the macro named by the first argument is defined then return the second +argument, otherwise the third. +If there is no third argument, the value is +.Dv NULL . +The word +.Qq unix +is predefined. +.It Fn ifelse a b yes ... +If the first argument +.Fa a +matches the second argument +.Fa b +then +.Fn ifelse +returns +the third argument +.Fa yes . +If the match fails the three arguments are +discarded and the next three arguments are used until there is +zero or one arguments left, either this last argument or +.Dv NULL +is returned if no other matches were found. +.It Fn include name +Returns the contents of the file specified in the first argument. +If the file is not found as is, look through the include path: +first the directories specified with +.Fl I +on the command line, then the environment variable +.Ev M4PATH , +as a colon-separated list of directories. +Include aborts with an error message if the file cannot be included. +.It Fn incr arg +Increments the argument by 1. +The argument must be a valid numeric string. +.It Fn index string substring +Returns the index of the second argument in the first argument (e.g., +.Ic index(the quick brown fox jumped, fox) +returns 16). +If the second +argument is not found index returns \-1. +.It Fn indir macro arg1 ... +Indirectly calls the macro whose name is passed as the first argument, +with the remaining arguments passed as first, ... arguments. +.It Fn len arg +Returns the number of characters in the first argument. +Extra arguments +are ignored. +.It Fn m4exit code +Immediately exits with the return value specified by the first argument, +0 if none. +.It Fn m4wrap todo +Allows you to define what happens at the final +.Dv EOF , +usually for cleanup purposes (e.g., +.Ic m4wrap("cleanup(tempfile)") +causes the macro cleanup to be +invoked after all other processing is done). +.Pp +Multiple calls to +.Fn m4wrap +get inserted in sequence at the final +.Dv EOF . +.It Fn maketemp template +Invokes +.Xr mkstemp 3 +on the first argument, and returns the modified string. +This can be used to create unique +temporary file names. +.It Fn paste file +Includes the contents of the file specified by the first argument without +any macro processing. +Aborts with an error message if the file cannot be +included. +.It Fn patsubst string regexp replacement +Substitutes a regular expression in a string with a replacement string. +Usual substitution patterns apply: an ampersand +.Pq Sq \&& +is replaced by the string matching the regular expression. +The string +.Sq \e# , +where +.Sq # +is a digit, is replaced by the corresponding back-reference. +.It Fn popdef arg ... +Restores the +.Ic pushdef Ns ed +definition for each argument. +.It Fn pushdef macro def +Takes the same arguments as +.Ic define , +but it saves the definition on a +stack for later retrieval by +.Fn popdef . +.It Fn regexp string regexp replacement +Finds a regular expression in a string. +If no further arguments are given, +it returns the first match position or \-1 if no match. +If a third argument +is provided, it returns the replacement string, with sub-patterns replaced. +.It Fn shift arg1 ... +Returns all but the first argument, the remaining arguments are +quoted and pushed back with commas in between. +The quoting +nullifies the effect of the extra scan that will subsequently be +performed. +.It Fn sinclude file +Similar to +.Ic include , +except it ignores any errors. +.It Fn spaste file +Similar to +.Fn paste , +except it ignores any errors. +.It Fn substr string offset length +Returns a substring of the first argument starting at the offset specified +by the second argument and the length specified by the third argument. +If no third argument is present it returns the rest of the string. +.It Fn syscmd cmd +Passes the first argument to the shell. +Nothing is returned. +.It Ic sysval +Returns the return value from the last +.Ic syscmd . +.It Fn traceon arg ... +Enables tracing of macro expansions for the given arguments, or for all +macros if no argument is given. +.It Fn traceoff arg ... +Disables tracing of macro expansions for the given arguments, or for all +macros if no argument is given. +.It Fn translit string mapfrom mapto +Transliterate the characters in the first argument from the set +given by the second argument to the set given by the third. +You cannot use +.Xr tr 1 +style abbreviations. +.It Fn undefine name1 ... +Removes the definition for the macros specified by its arguments. +.It Fn undivert arg ... +Flushes the named output queues (or all queues if no arguments). +.It Ic unix +A pre-defined macro for testing the OS platform. +.It Ic __line__ +Returns the current file's line number. +.It Ic __file__ +Returns the current file's name. +.El +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl dgIot +and the macros +.Ic builtin , +.Ic esyscmd , +.Ic expr , +.Ic format , +.Ic indir , +.Ic paste , +.Ic patsubst , +.Ic regexp , +.Ic spaste , +.Ic unix , +.Ic __line__ , +and +.Ic __file__ +are extensions to that specification. +.Pp +The output format of tracing and of +.Ic dumpdef +are not specified in any standard, +are likely to change and should not be relied upon. +The current format of tracing is closely modelled on +.Nm gnu-m4 , +to allow +.Nm autoconf +to work. +.Pp +The built-ins +.Ic pushdef +and +.Ic popdef +handle macro definitions as a stack. +However, +.Ic define +interacts with the stack in an undefined way. +In this implementation, +.Ic define +replaces the top-most definition only. +Other implementations may erase all definitions on the stack instead. +.Pp +All built-ins do expand without arguments in many other +.Nm m4 . +.Pp +Many other +.Nm +have dire size limitations with respect to buffer sizes. +.Sh AUTHORS +.An -nosplit +.An Ozan Yigit Aq Mt oz@sis.yorku.ca +and +.An Richard A. O'Keefe Aq Mt ok@goanna.cs.rmit.OZ.AU . +.Pp +GNU-m4 compatibility extensions by +.An Marc Espie Aq Mt espie@cvs.openbsd.org . diff --git a/buildrump.sh/src/usr.bin/m4/main.c b/buildrump.sh/src/usr.bin/m4/main.c new file mode 100644 index 00000000..ba76b858 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/main.c @@ -0,0 +1,654 @@ +/* $OpenBSD: main.c,v 1.77 2009/10/14 17:19:47 sthen Exp $ */ +/* $NetBSD: main.c,v 1.42 2012/04/25 18:23:58 christos Exp $ */ + +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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. + */ + +/* + * main.c + * Facility: m4 macro processor + * by: oz + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: main.c,v 1.42 2012/04/25 18:23:58 christos Exp $"); +#include <assert.h> +#include <signal.h> +#include <err.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <ohash.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" +#include "pathnames.h" + +ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ +stae *mstack; /* stack of m4 machine */ +char *sstack; /* shadow stack, for string space extension */ +static size_t STACKMAX; /* current maximum size of stack */ +int sp; /* current m4 stack pointer */ +int fp; /* m4 call frame pointer */ +struct input_file infile[MAXINP];/* input file stack (0=stdin) */ +FILE **outfile; /* diversion array(0=bitbucket)*/ +int maxout; +FILE *active; /* active output file pointer */ +int ilevel = 0; /* input file stack pointer */ +int oindex = 0; /* diversion index.. */ +const char *null = ""; /* as it says.. just a null.. */ +char **m4wraps = NULL; /* m4wraps array. */ +int maxwraps = 0; /* size of m4wraps array */ +int wrapindex = 0; /* current offset in m4wraps */ +char lquote[MAXCCHARS+1] = {LQUOTE}; /* left quote character (`) */ +char rquote[MAXCCHARS+1] = {RQUOTE}; /* right quote character (') */ +char scommt[MAXCCHARS+1] = {SCOMMT}; /* start character for comment */ +char ecommt[MAXCCHARS+1] = {ECOMMT}; /* end character for comment */ +int synch_lines = 0; /* line synchronisation for C preprocessor */ +int prefix_builtins = 0; /* -P option to prefix builtin keywords */ + +struct keyblk { + const char *knam; /* keyword name */ + int ktyp; /* keyword type */ +}; + +struct keyblk keywrds[] = { /* m4 keywords to be installed */ + { "include", INCLTYPE }, + { "sinclude", SINCTYPE }, + { "define", DEFITYPE }, + { "defn", DEFNTYPE }, + { "divert", DIVRTYPE | NOARGS }, + { "expr", EXPRTYPE }, + { "eval", EXPRTYPE }, + { "substr", SUBSTYPE }, + { "ifelse", IFELTYPE }, + { "ifdef", IFDFTYPE }, + { "len", LENGTYPE }, + { "incr", INCRTYPE }, + { "decr", DECRTYPE }, + { "dnl", DNLNTYPE | NOARGS }, + { "changequote", CHNQTYPE | NOARGS }, + { "changecom", CHNCTYPE | NOARGS }, + { "index", INDXTYPE }, +#ifdef EXTENDED + { "paste", PASTTYPE }, + { "spaste", SPASTYPE }, + /* Newer extensions, needed to handle gnu-m4 scripts */ + { "indir", INDIRTYPE}, + { "builtin", BUILTINTYPE}, + { "patsubst", PATSTYPE}, + { "regexp", REGEXPTYPE}, + { "esyscmd", ESYSCMDTYPE}, + { "__file__", FILENAMETYPE | NOARGS}, + { "__line__", LINETYPE | NOARGS}, +#endif + { "popdef", POPDTYPE }, + { "pushdef", PUSDTYPE }, + { "dumpdef", DUMPTYPE | NOARGS }, + { "shift", SHIFTYPE | NOARGS }, + { "translit", TRNLTYPE }, + { "undefine", UNDFTYPE }, + { "undivert", UNDVTYPE | NOARGS }, + { "divnum", DIVNTYPE | NOARGS }, + { "maketemp", MKTMTYPE }, + { "errprint", ERRPTYPE | NOARGS }, + { "m4wrap", M4WRTYPE | NOARGS }, + { "m4exit", EXITTYPE | NOARGS }, + { "syscmd", SYSCTYPE }, + { "sysval", SYSVTYPE | NOARGS }, + { "traceon", TRACEONTYPE | NOARGS }, + { "traceoff", TRACEOFFTYPE | NOARGS }, + +#if defined(unix) || defined(__unix__) + { "unix", SELFTYPE | NOARGS }, +#else +#ifdef vms + { "vms", SELFTYPE | NOARGS }, +#endif +#endif +}; + +#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) + +extern int optind; +extern char *optarg; + +#define MAXRECORD 50 +static struct position { + char *name; + unsigned long line; +} quotes[MAXRECORD], paren[MAXRECORD]; + +static void record(struct position *, int); +static void dump_stack(struct position *, int); + +static void macro(void); +static void initkwds(void); +static ndptr inspect(int, char *); +static int do_look_ahead(int, const char *); +static void reallyoutputstr(const char *); +static void reallyputchar(int); + +static void enlarge_stack(void); + +__dead static void +usage(void) +{ + fprintf(stderr, "usage: %s [-gPs] [-Dname[=value]] [-d flags] " + "[-I dirname] [-o filename]\n" + "\t[-t macro] [-Uname] [file ...]\n", getprogname()); + exit(1); +} + +__dead static void +onintr(int signo) +{ + char intrmessage[] = "m4: interrupted.\n"; + write(STDERR_FILENO, intrmessage, sizeof(intrmessage)-1); + _exit(1); +} + +int +main(int argc, char *argv[]) +{ + int c; + int n; + char *p; + + setprogname(argv[0]); + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); + + init_macros(); + initspaces(); + STACKMAX = INITSTACKMAX; + + mstack = (stae *)xalloc(sizeof(stae) * STACKMAX, NULL); + sstack = (char *)xalloc(STACKMAX, NULL); + + maxout = 0; + outfile = NULL; + resizedivs(MAXOUT); + + while ((c = getopt(argc, argv, "gst:d:D:U:o:I:P")) != -1) + switch(c) { + + case 'D': /* define something..*/ + for (p = optarg; *p; p++) + if (*p == '=') + break; + if (*p) + *p++ = EOS; + dodefine(optarg, p); + break; + case 'I': + addtoincludepath(optarg); + break; + case 'P': + prefix_builtins = 1; + break; + case 'U': /* undefine... */ + macro_popdef(optarg); + break; + case 'g': + mimic_gnu = 1; + break; + case 'd': + set_trace_flags(optarg); + break; + case 's': + synch_lines = 1; + break; + case 't': + mark_traced(optarg, 1); + break; + case 'o': + trace_file(optarg); + break; + case '?': + usage(); + } + + argc -= optind; + argv += optind; + + initkwds(); + if (mimic_gnu) + setup_builtin("format", FORMATTYPE); + + active = stdout; /* default active output */ + bbase[0] = bufbase; + if (!argc) { + sp = -1; /* stack pointer initialized */ + fp = 0; /* frame pointer initialized */ + set_input(infile+0, stdin, "stdin"); + /* default input (naturally) */ + macro(); + } else + for (; argc--; ++argv) { + p = *argv; + if (p[0] == '-' && p[1] == EOS) + set_input(infile, stdin, "stdin"); + else if (fopen_trypath(infile, p) == NULL) + err(1, "%s", p); + sp = -1; + fp = 0; + macro(); + release_input(infile); + } + + if (wrapindex) { + int i; + + ilevel = 0; /* in case m4wrap includes.. */ + bufbase = bp = buf; /* use the entire buffer */ + if (mimic_gnu) { + while (wrapindex != 0) { + for (i = 0; i < wrapindex; i++) + pbstr(m4wraps[i]); + wrapindex =0; + macro(); + } + } else { + for (i = 0; i < wrapindex; i++) { + pbstr(m4wraps[i]); + macro(); + } + } + } + + if (active != stdout) + active = stdout; /* reset output just in case */ + for (n = 1; n < maxout; n++) /* default wrap-up: undivert */ + if (outfile[n] != NULL) + getdiv(n); + /* remove bitbucket if used */ + if (outfile[0] != NULL) { + (void) fclose(outfile[0]); + } + + return 0; +} + +/* + * Look ahead for `token'. + * (on input `t == token[0]') + * Used for comment and quoting delimiters. + * Returns 1 if `token' present; copied to output. + * 0 if `token' not found; all characters pushed back + */ +static int +do_look_ahead(int t, const char *token) +{ + int i; + + assert((unsigned char)t == (unsigned char)token[0]); + + for (i = 1; *++token; i++) { + t = gpbc(); + if (t == EOF || (unsigned char)t != (unsigned char)*token) { + pushback(t); + while (--i) + pushback(*--token); + return 0; + } + } + return 1; +} + +#define LOOK_AHEAD(t, token) (t != EOF && \ + (unsigned char)(t)==(unsigned char)(token)[0] && \ + do_look_ahead(t,token)) + +/* + * macro - the work horse.. + */ +static void +macro(void) +{ + char token[MAXTOK+1]; + int t, l; + ndptr p; + int nlpar; + + cycle { + t = gpbc(); + + if (LOOK_AHEAD(t,lquote)) { /* strip quotes */ + nlpar = 0; + record(quotes, nlpar++); + /* + * Opening quote: scan forward until matching + * closing quote has been found. + */ + do { + + l = gpbc(); + if (LOOK_AHEAD(l,rquote)) { + if (--nlpar > 0) + outputstr(rquote); + } else if (LOOK_AHEAD(l,lquote)) { + record(quotes, nlpar++); + outputstr(lquote); + } else if (l == EOF) { + if (nlpar == 1) + warnx("unclosed quote:"); + else + warnx("%d unclosed quotes:", nlpar); + dump_stack(quotes, nlpar); + exit(1); + } else { + if (nlpar > 0) { + if (sp < 0) + reallyputchar(l); + else + CHRSAVE(l); + } + } + } + while (nlpar != 0); + } else if (sp < 0 && LOOK_AHEAD(t, scommt)) { + reallyoutputstr(scommt); + + for(;;) { + t = gpbc(); + if (LOOK_AHEAD(t, ecommt)) { + reallyoutputstr(ecommt); + break; + } + if (t == EOF) + break; + reallyputchar(t); + } + } else if (t == '_' || isalpha(t)) { + p = inspect(t, token); + if (p != NULL) + pushback(l = gpbc()); + if (p == NULL || (l != LPAREN && + (macro_getdef(p)->type & NEEDARGS) != 0)) + outputstr(token); + else { + /* + * real thing.. First build a call frame: + */ + pushf(fp); /* previous call frm */ + pushf(macro_getdef(p)->type); /* type of the call */ + pushf(is_traced(p)); + pushf(0); /* parenthesis level */ + fp = sp; /* new frame pointer */ + /* + * now push the string arguments: + */ + pushs1(macro_getdef(p)->defn); /* defn string */ + pushs1((char *)macro_name(p)); /* macro name */ + pushs(ep); /* start next..*/ + + if (l != LPAREN && PARLEV == 0) { + /* no bracks */ + chrsave(EOS); + + if ((size_t)sp == STACKMAX) + errx(1, "internal stack overflow"); + eval((const char **) mstack+fp+1, 2, + CALTYP, TRACESTATUS); + + ep = PREVEP; /* flush strspace */ + sp = PREVSP; /* previous sp.. */ + fp = PREVFP; /* rewind stack...*/ + } + } + } else if (t == EOF) { + if (sp > -1 && ilevel <= 0) { + warnx( "unexpected end of input, unclosed parenthesis:"); + dump_stack(paren, PARLEV); + exit(1); + } + if (ilevel <= 0) + break; /* all done thanks.. */ + release_input(infile+ilevel--); + emit_synchline(); + bufbase = bbase[ilevel]; + continue; + } else if (sp < 0) { /* not in a macro at all */ + reallyputchar(t); /* output directly.. */ + } + + else switch(t) { + + case LPAREN: + if (PARLEV > 0) + chrsave(t); + while (isspace(l = gpbc())) /* skip blank, tab, nl.. */ + if (PARLEV > 0) + chrsave(l); + pushback(l); + record(paren, PARLEV++); + break; + + case RPAREN: + if (--PARLEV > 0) + chrsave(t); + else { /* end of argument list */ + chrsave(EOS); + + if ((size_t)sp == STACKMAX) + errx(1, "internal stack overflow"); + + eval((const char **) mstack+fp+1, sp-fp, + CALTYP, TRACESTATUS); + + ep = PREVEP; /* flush strspace */ + sp = PREVSP; /* previous sp.. */ + fp = PREVFP; /* rewind stack...*/ + } + break; + + case COMMA: + if (PARLEV == 1) { + chrsave(EOS); /* new argument */ + while (isspace(l = gpbc())) + ; + pushback(l); + pushs(ep); + } else + chrsave(t); + break; + + default: + if (LOOK_AHEAD(t, scommt)) { + char *q; + for (q = scommt; *q; q++) + chrsave(*q); + for(;;) { + t = gpbc(); + if (LOOK_AHEAD(t, ecommt)) { + for (q = ecommt; *q; q++) + chrsave(*q); + break; + } + if (t == EOF) + break; + CHRSAVE(t); + } + } else + CHRSAVE(t); /* stack the char */ + break; + } + } +} + +/* + * output string directly, without pushing it for reparses. + */ +void +outputstr(const char *s) +{ + if (sp < 0) + reallyoutputstr(s); + else + while (*s) + CHRSAVE(*s++); +} + +void +reallyoutputstr(const char *s) +{ + if (synch_lines) { + while (*s) { + fputc(*s, active); + if (*s++ == '\n') { + infile[ilevel].synch_lineno++; + if (infile[ilevel].synch_lineno != + infile[ilevel].lineno) + do_emit_synchline(); + } + } + } else + fputs(s, active); +} + +void +reallyputchar(int c) +{ + putc(c, active); + if (synch_lines && c == '\n') { + infile[ilevel].synch_lineno++; + if (infile[ilevel].synch_lineno != infile[ilevel].lineno) + do_emit_synchline(); + } +} + +/* + * build an input token.. + * consider only those starting with _ or A-Za-z. + */ +static ndptr +inspect(int c, char *tp) +{ + char *name = tp; + char *etp = tp+MAXTOK; + ndptr p; + + *tp++ = c; + + while ((isalnum(c = gpbc()) || c == '_') && tp < etp) + *tp++ = c; + if (c != EOF) + PUSHBACK(c); + *tp = EOS; + /* token is too long, it won't match anything, but it can still + * be output. */ + if (tp == ep) { + outputstr(name); + while (isalnum(c = gpbc()) || c == '_') { + if (sp < 0) + reallyputchar(c); + else + CHRSAVE(c); + } + *name = EOS; + return NULL; + } + + p = ohash_find(¯os, ohash_qlookupi(¯os, name, (void *)&tp)); + if (p == NULL) + return NULL; + if (macro_getdef(p) == NULL) + return NULL; + return p; +} + +/* + * initkwds - initialise m4 keywords as fast as possible. + * This very similar to install, but without certain overheads, + * such as calling lookup. Malloc is not used for storing the + * keyword strings, since we simply use the static pointers + * within keywrds block. + */ +static void +initkwds(void) +{ + unsigned int type; + size_t i; + + for (i = 0; i < MAXKEYS; i++) { + type = keywrds[i].ktyp & TYPEMASK; + if ((keywrds[i].ktyp & NOARGS) == 0) + type |= NEEDARGS; + setup_builtin(keywrds[i].knam, type); + } +} + +static void +record(struct position *t, int lev) +{ + if (lev < MAXRECORD) { + t[lev].name = CURRENT_NAME; + t[lev].line = CURRENT_LINE; + } +} + +static void +dump_stack(struct position *t, int lev) +{ + int i; + + for (i = 0; i < lev; i++) { + if (i == MAXRECORD) { + fprintf(stderr, " ...\n"); + break; + } + fprintf(stderr, " %s at line %lu\n", + t[i].name, t[i].line); + } +} + + +static void +enlarge_stack(void) +{ + STACKMAX += STACKMAX/2; + mstack = xrealloc(mstack, sizeof(stae) * STACKMAX, + "Evaluation stack overflow (%lu)", + (unsigned long)STACKMAX); + sstack = xrealloc(sstack, STACKMAX, + "Evaluation stack overflow (%lu)", + (unsigned long)STACKMAX); +} diff --git a/buildrump.sh/src/usr.bin/m4/mdef.h b/buildrump.sh/src/usr.bin/m4/mdef.h new file mode 100644 index 00000000..b05f2767 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/mdef.h @@ -0,0 +1,235 @@ +/* $OpenBSD: mdef.h,v 1.29 2006/03/20 20:27:45 espie Exp $ */ +/* $NetBSD: mdef.h,v 1.15 2013/10/18 20:19:36 christos Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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. + * + * @(#)mdef.h 8.1 (Berkeley) 6/6/93 + */ + +#ifdef __GNUC__ +# define UNUSED __attribute__((__unused__)) +#else +# define UNUSED +#endif + +#define MACRTYPE 1 +#define DEFITYPE 2 +#define EXPRTYPE 3 +#define SUBSTYPE 4 +#define IFELTYPE 5 +#define LENGTYPE 6 +#define CHNQTYPE 7 +#define SYSCTYPE 8 +#define UNDFTYPE 9 +#define INCLTYPE 10 +#define SINCTYPE 11 +#define PASTTYPE 12 +#define SPASTYPE 13 +#define INCRTYPE 14 +#define IFDFTYPE 15 +#define PUSDTYPE 16 +#define POPDTYPE 17 +#define SHIFTYPE 18 +#define DECRTYPE 19 +#define DIVRTYPE 20 +#define UNDVTYPE 21 +#define DIVNTYPE 22 +#define MKTMTYPE 23 +#define ERRPTYPE 24 +#define M4WRTYPE 25 +#define TRNLTYPE 26 +#define DNLNTYPE 27 +#define DUMPTYPE 28 +#define CHNCTYPE 29 +#define INDXTYPE 30 +#define SYSVTYPE 31 +#define EXITTYPE 32 +#define DEFNTYPE 33 +#define SELFTYPE 34 +#define INDIRTYPE 35 +#define BUILTINTYPE 36 +#define PATSTYPE 37 +#define FILENAMETYPE 38 +#define LINETYPE 39 +#define REGEXPTYPE 40 +#define ESYSCMDTYPE 41 +#define TRACEONTYPE 42 +#define TRACEOFFTYPE 43 +#define FORMATTYPE 44 + +#define BUILTIN_MARKER "__builtin_" + +#define TYPEMASK 63 /* Keep bits really corresponding to a type. */ +#define RECDEF 256 /* Pure recursive def, don't expand it */ +#define NOARGS 512 /* builtin needs no args */ +#define NEEDARGS 1024 /* mark builtin that need args with this */ + +/* + * m4 special characters + */ + +#define ARGFLAG '$' +#define LPAREN '(' +#define RPAREN ')' +#define LQUOTE '`' +#define RQUOTE '\'' +#define COMMA ',' +#define SCOMMT '#' +#define ECOMMT '\n' + +#ifdef msdos +#define system(str) (-1) +#endif + +/* + * other important constants + */ + +#define EOS '\0' +#define MAXINP 10 /* maximum include files */ +#define MAXOUT 10 /* maximum # of diversions */ +#define BUFSIZE 4096 /* starting size of pushback buffer */ +#define INITSTACKMAX 4096 /* starting size of call stack */ +#define STRSPMAX 4096 /* starting size of string space */ +#define MAXTOK 512 /* maximum chars in a tokn */ +#define HASHSIZE 199 /* maximum size of hashtab */ +#define MAXCCHARS 5 /* max size of comment/quote delim */ + +#define ALL 1 +#define TOP 0 + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#define cycle for(;;) + +/* + * m4 data structures + */ + +typedef struct ndblock *ndptr; + +struct macro_definition { + struct macro_definition *next; + char *defn; /* definition.. */ + unsigned int type; /* type of the entry.. */ +}; + + +struct ndblock { /* hashtable structure */ + unsigned int builtin_type; + unsigned int trace_flags; + struct macro_definition *d; + char name[1]; /* entry name.. */ +}; + +typedef union { /* stack structure */ + int sfra; /* frame entry */ + char *sstr; /* string entry */ +} stae; + +struct input_file { + FILE *file; + char *name; + unsigned long lineno; + unsigned long synch_lineno; /* used for -s */ + int c; +}; + +#define CURRENT_NAME (infile[ilevel].name) +#define CURRENT_LINE (infile[ilevel].lineno) +#define TOKEN_LINE(f) (f->lineno - (f->c == '\n' ? 1 : 0)) + +/* + * macros for readibility and/or speed + * + * gpbc() - get a possibly pushed-back character + * pushf() - push a call frame entry onto stack + * pushs() - push a string pointer onto stack + */ +#define gpbc() (bp > bufbase) ? *--bp : obtain_char(infile+ilevel) +#define pushf(x) \ + do { \ + if ((size_t)++sp == STACKMAX) \ + enlarge_stack();\ + mstack[sp].sfra = (x); \ + sstack[sp] = 0; \ + } while (0) + +#define pushs(x) \ + do { \ + if ((size_t)++sp == STACKMAX) \ + enlarge_stack();\ + mstack[sp].sstr = (x); \ + sstack[sp] = 1; \ + } while (0) + +#define pushs1(x) \ + do { \ + if ((size_t)++sp == STACKMAX) \ + enlarge_stack();\ + mstack[sp].sstr = (x); \ + sstack[sp] = 0; \ + } while (0) + +/* + * . . + * | . | <-- sp | . | + * +-------+ +-----+ + * | arg 3 ----------------------->| str | + * +-------+ | . | + * | arg 2 ---PREVEP-----+ . + * +-------+ | + * . | | | + * +-------+ | +-----+ + * | plev | PARLEV +-------->| str | + * +-------+ | . | + * | type | CALTYP . + * +-------+ + * | prcf ---PREVFP--+ + * +-------+ | + * | . | PREVSP | + * . | + * +-------+ | + * | <----------+ + * +-------+ + * + */ +#define PARLEV (mstack[fp].sfra) +#define CALTYP (mstack[fp-2].sfra) +#define TRACESTATUS (mstack[fp-1].sfra) +#define PREVEP (mstack[fp+3].sstr) +#define PREVSP (fp-4) +#define PREVFP (mstack[fp-3].sfra) diff --git a/buildrump.sh/src/usr.bin/m4/misc.c b/buildrump.sh/src/usr.bin/m4/misc.c new file mode 100644 index 00000000..38f76500 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/misc.c @@ -0,0 +1,410 @@ +/* $OpenBSD: misc.c,v 1.41 2009/10/14 17:19:47 sthen Exp $ */ +/* $NetBSD: misc.c,v 1.23 2012/03/20 20:34:58 matt Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: misc.c,v 1.23 2012/03/20 20:34:58 matt Exp $"); +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <stddef.h> +#include <string.h> +#include <err.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" +#include "pathnames.h" + + +char *ep; /* first free char in strspace */ +static char *strspace; /* string space for evaluation */ +char *endest; /* end of string space */ +static size_t strsize = STRSPMAX; +static size_t bufsize = BUFSIZE; + +unsigned char *buf; /* push-back buffer */ +unsigned char *bufbase; /* the base for current ilevel */ +unsigned char *bbase[MAXINP]; /* the base for each ilevel */ +unsigned char *bp; /* first available character */ +unsigned char *endpbb; /* end of push-back buffer */ + + +/* + * find the index of second str in the first str. + */ +ptrdiff_t +indx(const char *s1, const char *s2) +{ + char *t; + + t = strstr(s1, s2); + if (t == NULL) + return (-1); + else + return (t - s1); +} +/* + * pushback - push character back onto input + */ +void +pushback(int c) +{ + if (c == EOF) + return; + if (bp >= endpbb) + enlarge_bufspace(); + *bp++ = c; +} + +/* + * pbstr - push string back onto input + * pushback is replicated to improve + * performance. + */ +void +pbstr(const char *s) +{ + size_t n; + + n = strlen(s); + while ((size_t)(endpbb - bp) <= n) + enlarge_bufspace(); + while (n > 0) + *bp++ = s[--n]; +} + +/* + * pbnum - convert number to string, push back on input. + */ +void +pbnum(int n) +{ + pbnumbase(n, 10, 0); +} + +void +pbnumbase(int n, int base, int d) +{ + static char digits[36] = "0123456789abcdefghijklmnopqrstuvwxyz"; + int num; + int printed = 0; + + if (base > 36) + m4errx(1, "base %d > 36: not supported.", base); + + if (base < 2) + m4errx(1, "bad base %d for conversion.", base); + + num = (n < 0) ? -n : n; + do { + pushback(digits[num % base]); + printed++; + } + while ((num /= base) > 0); + + if (n < 0) + printed++; + while (printed++ < d) + pushback('0'); + + if (n < 0) + pushback('-'); +} + +/* + * pbunsigned - convert unsigned long to string, push back on input. + */ +void +pbunsigned(unsigned long n) +{ + do { + pushback(n % 10 + '0'); + } + while ((n /= 10) > 0); +} + +void +initspaces(void) +{ + int i; + + strspace = xalloc(strsize+1, NULL); + ep = strspace; + endest = strspace+strsize; + buf = (unsigned char *)xalloc(bufsize, NULL); + bufbase = buf; + bp = buf; + endpbb = buf + bufsize; + for (i = 0; i < MAXINP; i++) + bbase[i] = buf; +} + +void +enlarge_strspace(void) +{ + char *newstrspace; + int i; + + strsize *= 2; + newstrspace = malloc(strsize + 1); + if (!newstrspace) + errx(1, "string space overflow"); + memcpy(newstrspace, strspace, strsize/2); + for (i = 0; i <= sp; i++) + if (sstack[i]) + mstack[i].sstr = (mstack[i].sstr - strspace) + + newstrspace; + ep = (ep-strspace) + newstrspace; + free(strspace); + strspace = newstrspace; + endest = strspace + strsize; +} + +void +enlarge_bufspace(void) +{ + unsigned char *newbuf; + int i; + + bufsize += bufsize/2; + newbuf = xrealloc(buf, bufsize, "too many characters pushed back"); + for (i = 0; i < MAXINP; i++) + bbase[i] = (bbase[i]-buf)+newbuf; + bp = (bp-buf)+newbuf; + bufbase = (bufbase-buf)+newbuf; + buf = newbuf; + endpbb = buf+bufsize; +} + +/* + * chrsave - put single char on string space + */ +void +chrsave(int c) +{ + if (ep >= endest) + enlarge_strspace(); + *ep++ = c; +} + +/* + * read in a diversion file, and dispose it. + */ +void +getdiv(int n) +{ + int c; + + if (active == outfile[n]) + m4errx(1, "undivert: diversion still active."); + rewind(outfile[n]); + while ((c = getc(outfile[n])) != EOF) + putc(c, active); + (void) fclose(outfile[n]); + outfile[n] = NULL; +} + +/* + * killdiv - get rid of the diversion files + */ +void +killdiv(void) +{ + int n; + + for (n = 0; n < maxout; n++) + if (outfile[n] != NULL) { + (void) fclose(outfile[n]); + } +} + +void +m4errx(int exval, const char *fmt, ...) +{ + fprintf(stderr, "%s: ", getprogname()); + fprintf(stderr, "%s at line %lu: ", CURRENT_NAME, CURRENT_LINE); + if (fmt != NULL) { + va_list ap; + + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + } + fprintf(stderr, "\n"); + exit(exval); +} + +/* + * resizedivs: allocate more diversion files */ +void +resizedivs(int n) +{ + int i; + + outfile = (FILE **)xrealloc(outfile, sizeof(FILE *) * n, + "too many diverts %d", n); + for (i = maxout; i < n; i++) + outfile[i] = NULL; + maxout = n; +} + +void * +xalloc(size_t n, const char *fmt, ...) +{ + void *p = malloc(n); + + if (p == NULL) { + if (fmt == NULL) + err(1, "malloc"); + else { + va_list va; + + va_start(va, fmt); + verr(1, fmt, va); + va_end(va); + } + } + return p; +} + +void * +xrealloc(void *old, size_t n, const char *fmt, ...) +{ + char *p = realloc(old, n); + + if (p == NULL) { + free(old); + if (fmt == NULL) + err(1, "realloc"); + else { + va_list va; + + va_start(va, fmt); + verr(1, fmt, va); + va_end(va); + } + } + return p; +} + +char * +xstrdup(const char *s) +{ + char *p = strdup(s); + if (p == NULL) + err(1, "strdup"); + return p; +} + +int +obtain_char(struct input_file *f) +{ + if (f->c == EOF) + return EOF; + + f->c = fgetc(f->file); + if (f->c == '\n') + f->lineno++; + + return f->c; +} + +void +set_input(struct input_file *f, FILE *real, const char *name) +{ + f->file = real; + f->lineno = 1; + f->c = 0; + f->name = xstrdup(name); + emit_synchline(); +} + +void +do_emit_synchline(void) +{ + fprintf(active, "#line %lu \"%s\"\n", + infile[ilevel].lineno, infile[ilevel].name); + infile[ilevel].synch_lineno = infile[ilevel].lineno; +} + +void +release_input(struct input_file *f) +{ + if (f->file != stdin) + fclose(f->file); + f->c = EOF; + /* + * XXX can't free filename, as there might still be + * error information pointing to it. + */ +} + +void +doprintlineno(struct input_file *f) +{ + pbunsigned(TOKEN_LINE(f)); +} + +void +doprintfilename(struct input_file *f) +{ + pbstr(rquote); + pbstr(f->name); + pbstr(lquote); +} + +/* + * buffer_mark/dump_buffer: allows one to save a mark in a buffer, + * and later dump everything that was added since then to a file. + */ +size_t +buffer_mark(void) +{ + return bp - buf; +} + + +void +dump_buffer(FILE *f, size_t m) +{ + unsigned char *s; + + for (s = bp; (size_t)(s - buf) > m;) + fputc(*--s, f); +} diff --git a/buildrump.sh/src/usr.bin/m4/parser.y b/buildrump.sh/src/usr.bin/m4/parser.y new file mode 100644 index 00000000..f8256ef4 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/parser.y @@ -0,0 +1,86 @@ +%{ +/* $NetBSD: parser.y,v 1.3 2015/01/04 18:31:09 joerg Exp $ */ +/* $OpenBSD: parser.y,v 1.6 2008/08/21 21:00:14 espie Exp $ */ +/* + * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: parser.y,v 1.3 2015/01/04 18:31:09 joerg Exp $"); +#include <stdint.h> +#define YYSTYPE int32_t +extern int32_t end_result; +extern int yylex(void); +extern int yyerror(const char *); +%} +%token NUMBER +%token ERROR +%left LOR +%left LAND +%left '|' +%left '^' +%left '&' +%left EQ NE +%left '<' LE '>' GE +%left LSHIFT RSHIFT +%left '+' '-' +%left '*' '/' '%' +%right UMINUS UPLUS '!' '~' + +%% + +top : expr { end_result = $1; } + ; +expr : expr '+' expr { $$ = $1 + $3; } + | expr '-' expr { $$ = $1 - $3; } + | expr '*' expr { $$ = $1 * $3; } + | expr '/' expr { + if ($3 == 0) { + yyerror("division by zero"); + exit(1); + } + $$ = $1 / $3; + } + | expr '%' expr { + if ($3 == 0) { + yyerror("modulo zero"); + exit(1); + } + $$ = $1 % $3; + } + | expr LSHIFT expr { $$ = $1 << $3; } + | expr RSHIFT expr { $$ = $1 >> $3; } + | expr '<' expr { $$ = $1 < $3; } + | expr '>' expr { $$ = $1 > $3; } + | expr LE expr { $$ = $1 <= $3; } + | expr GE expr { $$ = $1 >= $3; } + | expr EQ expr { $$ = $1 == $3; } + | expr NE expr { $$ = $1 != $3; } + | expr '&' expr { $$ = $1 & $3; } + | expr '^' expr { $$ = $1 ^ $3; } + | expr '|' expr { $$ = $1 | $3; } + | expr LAND expr { $$ = $1 && $3; } + | expr LOR expr { $$ = $1 || $3; } + | '(' expr ')' { $$ = $2; } + | '-' expr %prec UMINUS { $$ = -$2; } + | '+' expr %prec UPLUS { $$ = $2; } + | '!' expr { $$ = !$2; } + | '~' expr { $$ = ~$2; } + | NUMBER + ; +%% + diff --git a/buildrump.sh/src/usr.bin/m4/pathnames.h b/buildrump.sh/src/usr.bin/m4/pathnames.h new file mode 100644 index 00000000..ef788e06 --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/pathnames.h @@ -0,0 +1,40 @@ +/* $NetBSD: pathnames.h,v 1.15 2011/03/08 23:55:19 riz Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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/6/93 + */ + +/* + * Definitions of diversion files. + */ +#define _PATH_DIVNAME "/tmp/m4.0XXXXXXXXXX" /* unix diversion files */ diff --git a/buildrump.sh/src/usr.bin/m4/stdd.h b/buildrump.sh/src/usr.bin/m4/stdd.h new file mode 100644 index 00000000..0cbd681a --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/stdd.h @@ -0,0 +1,54 @@ +/* $NetBSD: stdd.h,v 1.4 2003/08/07 11:14:34 agc Exp $ */ + +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * 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. + * + * @(#)stdd.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * standard defines + */ + +#define max(a,b) ((a) > (b)? (a): (b)) +#define min(a,b) ((a) < (b)? (a): (b)) + +#define iswhite(c) ((c) == ' ' || (c) == '\t') + +/* + * STREQ is an optimised strcmp(a,b)==0 + * STREQN is an optimised strncmp(a,b,n)==0; assumes n > 0 + */ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) +#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0) + +#define YES 1 +#define NO 0 diff --git a/buildrump.sh/src/usr.bin/m4/tokenizer.l b/buildrump.sh/src/usr.bin/m4/tokenizer.l new file mode 100644 index 00000000..fc4ebf1c --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/tokenizer.l @@ -0,0 +1,108 @@ +%{ +/* $NetBSD: tokenizer.l,v 1.6 2012/03/20 20:34:58 matt Exp $ */ +/* $OpenBSD: tokenizer.l,v 1.6 2008/08/21 21:00:14 espie Exp $ */ +/* + * Copyright (c) 2004 Marc Espie <espie@cvs.openbsd.org> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include "parser.h" +__RCSID("$NetBSD: tokenizer.l,v 1.6 2012/03/20 20:34:58 matt Exp $"); +#include <stdlib.h> +#include <errno.h> +#include <stdint.h> +#include <limits.h> + +extern int mimic_gnu; +extern int32_t yylval; +extern int yylex(void); +extern int yywrap(void); + +int32_t number(void); +int32_t parse_radix(void); + +%} + +%option nounput +%option noinput + +delim [ \t\n] +ws {delim}+ +hex 0[xX][0-9a-fA-F]+ +oct 0[0-7]* +dec [1-9][0-9]* +radix 0[rR][0-9]+:[0-9a-zA-Z]+ + +%% +{ws} {/* just skip it */} +{hex}|{oct}|{dec} { yylval = number(); return(NUMBER); } +{radix} { if (mimic_gnu) { + yylval = parse_radix(); return(NUMBER); + } else { + return(ERROR); + } + } +"<=" { return(LE); } +">=" { return(GE); } +"<<" { return(LSHIFT); } +">>" { return(RSHIFT); } +"==" { return(EQ); } +"!=" { return(NE); } +"&&" { return(LAND); } +"||" { return(LOR); } +. { return yytext[0]; } +%% + +int32_t +number(void) +{ + long l; + + errno = 0; + l = strtol(yytext, NULL, 0); + if (((l == LONG_MAX || l == LONG_MIN) && errno == ERANGE) || + l > INT32_MAX || l < INT32_MIN) { + fprintf(stderr, "m4: numeric overflow in expr: %s\n", yytext); + } + return l; +} + +int32_t +parse_radix(void) +{ + long base; + char *next; + long l; + + l = 0; + base = strtol(yytext+2, &next, 0); + if (base > 36 || next == NULL) { + fprintf(stderr, "m4: error in number %s\n", yytext); + } else { + next++; + while (*next != 0) { + if (*next >= '0' && *next <= '9') + l = base * l + *next - '0'; + else if (*next >= 'a' && *next <= 'z') + l = base * l + *next - 'a' + 10; + else if (*next >= 'A' && *next <= 'Z') + l = base * l + *next - 'A' + 10; + next++; + } + } + return l; +} + diff --git a/buildrump.sh/src/usr.bin/m4/trace.c b/buildrump.sh/src/usr.bin/m4/trace.c new file mode 100644 index 00000000..bb9397fe --- /dev/null +++ b/buildrump.sh/src/usr.bin/m4/trace.c @@ -0,0 +1,203 @@ +/* $NetBSD: trace.c,v 1.8 2012/03/20 20:34:58 matt Exp $ */ +/* $OpenBSD: trace.c,v 1.15 2006/03/24 08:03:44 espie Exp $ */ +/* + * Copyright (c) 2001 Marc Espie. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT 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 OPENBSD + * PROJECT 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. + */ +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#include <sys/cdefs.h> +__RCSID("$NetBSD: trace.c,v 1.8 2012/03/20 20:34:58 matt Exp $"); + +#include <sys/types.h> +#include <err.h> +#include <stddef.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" + +FILE *traceout; + +#define TRACE_ARGS 1 +#define TRACE_EXPANSION 2 +#define TRACE_QUOTE 4 +#define TRACE_FILENAME 8 +#define TRACE_LINENO 16 +#define TRACE_CONT 32 +#define TRACE_ID 64 +#define TRACE_NEWFILE 128 /* not implemented yet */ +#define TRACE_INPUT 256 /* not implemented yet */ + +static unsigned int letter_to_flag(int); +static void print_header(struct input_file *); +static int frame_level(void); + + +unsigned int trace_flags = TRACE_QUOTE | TRACE_EXPANSION; + +void +trace_file(const char *name) +{ + + if (traceout && traceout != stderr) + fclose(traceout); + traceout = fopen(name, "w"); + if (!traceout) + err(1, "can't open %s", name); +} + +static unsigned int +letter_to_flag(int c) +{ + switch(c) { + case 'a': + return TRACE_ARGS; + case 'e': + return TRACE_EXPANSION; + case 'q': + return TRACE_QUOTE; + case 'c': + return TRACE_CONT; + case 'x': + return TRACE_ID; + case 'f': + return TRACE_FILENAME; + case 'l': + return TRACE_LINENO; + case 'p': + return TRACE_NEWFILE; + case 'i': + return TRACE_INPUT; + case 't': + return TRACE_ALL; + case 'V': + return ~0; + default: + return 0; + } +} + +void +set_trace_flags(const char *s) +{ + char mode = 0; + unsigned int f = 0; + + if (*s == '+' || *s == '-') + mode = *s++; + while (*s) + f |= letter_to_flag(*s++); + switch(mode) { + case 0: + trace_flags = f; + break; + case '+': + trace_flags |= f; + break; + case '-': + trace_flags &= ~f; + break; + } +} + +static int +frame_level(void) +{ + int level; + int framep; + + for (framep = fp, level = 0; framep != 0; + level++,framep = mstack[framep-3].sfra) + ; + return level; +} + +static void +print_header(struct input_file *inp) +{ + fprintf(traceout, "m4trace:"); + if (trace_flags & TRACE_FILENAME) + fprintf(traceout, "%s:", inp->name); + if (trace_flags & TRACE_LINENO) + fprintf(traceout, "%lu:", TOKEN_LINE(inp)); + fprintf(traceout, " -%d- ", frame_level()); + if (trace_flags & TRACE_ID) + fprintf(traceout, "id %lu: ", expansion_id); +} + +size_t +trace(const char *argv[], int argc, struct input_file *inp) +{ + if (!traceout) + traceout = stderr; + print_header(inp); + if (trace_flags & TRACE_CONT) { + fprintf(traceout, "%s ...\n", argv[1]); + print_header(inp); + } + fprintf(traceout, "%s", argv[1]); + if ((trace_flags & TRACE_ARGS) && argc > 2) { + char delim[3]; + int i; + + delim[0] = LPAREN; + delim[1] = EOS; + for (i = 2; i < argc; i++) { + fprintf(traceout, "%s%s%s%s", delim, + (trace_flags & TRACE_QUOTE) ? lquote : "", + argv[i], + (trace_flags & TRACE_QUOTE) ? rquote : ""); + delim[0] = COMMA; + delim[1] = ' '; + delim[2] = EOS; + } + fprintf(traceout, "%c", RPAREN); + } + if (trace_flags & TRACE_CONT) { + fprintf(traceout, " -> ???\n"); + print_header(inp); + fprintf(traceout, argc > 2 ? "%s(...)" : "%s", argv[1]); + } + if (trace_flags & TRACE_EXPANSION) + return buffer_mark(); + else { + fprintf(traceout, "\n"); + return SIZE_MAX; + } +} + +void +finish_trace(size_t mark) +{ + fprintf(traceout, " -> "); + if (trace_flags & TRACE_QUOTE) + fprintf(traceout, "%s", lquote); + dump_buffer(traceout, mark); + if (trace_flags & TRACE_QUOTE) + fprintf(traceout, "%s", rquote); + fprintf(traceout, "\n"); +} diff --git a/buildrump.sh/src/usr.bin/make/Makefile b/buildrump.sh/src/usr.bin/make/Makefile new file mode 100644 index 00000000..1f5e1f3f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/Makefile @@ -0,0 +1,52 @@ +# $NetBSD: Makefile,v 1.57 2014/07/05 19:22:05 dholland Exp $ +# @(#)Makefile 5.2 (Berkeley) 12/28/90 + +PROG= make +SRCS= arch.c buf.c compat.c cond.c dir.c for.c hash.c job.c main.c \ + make.c parse.c str.c suff.c targ.c trace.c var.c util.c +SRCS+= strlist.c +SRCS+= make_malloc.c +SRCS+= lstAppend.c lstAtEnd.c lstAtFront.c lstClose.c lstConcat.c \ + lstDatum.c lstDeQueue.c lstDestroy.c lstDupl.c lstEnQueue.c \ + lstFind.c lstFindFrom.c lstFirst.c lstForEach.c lstForEachFrom.c \ + lstInit.c lstInsert.c lstIsAtEnd.c lstIsEmpty.c lstLast.c \ + lstMember.c lstNext.c lstOpen.c lstRemove.c lstReplace.c lstSucc.c +SRCS += lstPrev.c + +# let people experiment for a bit +USE_META ?= no +.if ${USE_META:tl} != "no" +SRCS+= meta.c +CPPFLAGS+= -DUSE_META +FILEMON_H ?= ${.CURDIR:H:H}/sys/dev/filemon/filemon.h +.if exists(${FILEMON_H}) && ${FILEMON_H:T} == "filemon.h" +COPTS.meta.c += -DHAVE_FILEMON_H -I${FILEMON_H:H} +.endif +.endif + +.PATH: ${.CURDIR}/lst.lib +SUBDIR= PSD.doc +.if make(obj) || make(clean) +SUBDIR+= unit-tests +.endif + +.include <bsd.prog.mk> +.include <bsd.subdir.mk> + +CPPFLAGS+= -DMAKE_NATIVE +COPTS.var.c += -Wno-cast-qual +COPTS.job.c += -Wno-format-nonliteral +COPTS.parse.c += -Wno-format-nonliteral +COPTS.var.c += -Wno-format-nonliteral + +.ifdef TOOLDIR +# this is a native netbsd build, +# use libutil rather than the local emalloc etc. +CPPFLAGS+= -DUSE_EMALLOC +LDADD+=-lutil +DPADD+=${LIBUTIL} +.endif + +# A simple unit-test driver to help catch regressions +accept test: + cd ${.CURDIR}/unit-tests && MAKEFLAGS= ${.MAKE} -r -m / TEST_MAKE=${TEST_MAKE:U${.OBJDIR}/${PROG:T}} ${.TARGET} diff --git a/buildrump.sh/src/usr.bin/make/Makefile.boot b/buildrump.sh/src/usr.bin/make/Makefile.boot new file mode 100644 index 00000000..f5be09c6 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/Makefile.boot @@ -0,0 +1,45 @@ +# $NetBSD: Makefile.boot,v 1.21 2014/02/24 07:23:44 skrll Exp $ +# +# a very simple makefile... +# +# You only want to use this if you aren't running NetBSD. +# +# modify MACHINE and MACHINE_ARCH as appropriate for your target architecture +# +CC=gcc -O -g + +.c.o: + ${CC} ${CFLAGS} -c $< -o $@ + +MACHINE=i386 +MACHINE_ARCH=i386 +# tested on HP-UX 10.20 +#MAKE_MACHINE=hppa +#MAKE_MACHINE_ARCH=hppa +CFLAGS= -DTARGET_MACHINE=\"${MACHINE}\" \ + -DTARGET_MACHINE_ARCH=\"${MACHINE_ARCH}\" \ + -DMAKE_MACHINE=\"${MACHINE}\" +LIBS= + +OBJ=arch.o buf.o compat.o cond.o dir.o for.o hash.o job.o main.o make.o \ + make_malloc.o parse.o str.o strlist.o suff.o targ.o trace.o var.o util.o + +LIBOBJ= lst.lib/lstAppend.o lst.lib/lstAtEnd.o lst.lib/lstAtFront.o \ + lst.lib/lstClose.o lst.lib/lstConcat.o lst.lib/lstDatum.o \ + lst.lib/lstDeQueue.o lst.lib/lstDestroy.o lst.lib/lstDupl.o \ + lst.lib/lstEnQueue.o lst.lib/lstFind.o lst.lib/lstFindFrom.o \ + lst.lib/lstFirst.o lst.lib/lstForEach.o lst.lib/lstForEachFrom.o \ + lst.lib/lstInit.o lst.lib/lstInsert.o lst.lib/lstIsAtEnd.o \ + lst.lib/lstIsEmpty.o lst.lib/lstLast.o lst.lib/lstMember.o \ + lst.lib/lstNext.o lst.lib/lstOpen.o lst.lib/lstRemove.o \ + lst.lib/lstReplace.o lst.lib/lstSucc.o lst.lib/lstPrev.o + +bmake: ${OBJ} ${LIBOBJ} +# @echo 'make of make and make.0 started.' + ${CC} ${CFLAGS} ${OBJ} ${LIBOBJ} -o bmake ${LIBS} + @ls -l $@ +# nroff -h -man make.1 > make.0 +# @echo 'make of make and make.0 completed.' + +clean: + rm -f ${OBJ} ${LIBOBJ} ${PORTOBJ} bmake diff --git a/buildrump.sh/src/usr.bin/make/PSD.doc/Makefile b/buildrump.sh/src/usr.bin/make/PSD.doc/Makefile new file mode 100644 index 00000000..67702b80 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/PSD.doc/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.4 2014/07/05 19:22:43 dholland Exp $ +# @(#)Makefile 8.1 (Berkeley) 8/14/93 + +SECTION=reference/ref1 +ARTICLE=make +SRCS= tutorial.ms +MACROS= -ms +EXTRAHTMLFILES=make1.png make2.png + +.include <bsd.doc.mk> diff --git a/buildrump.sh/src/usr.bin/make/PSD.doc/tutorial.ms b/buildrump.sh/src/usr.bin/make/PSD.doc/tutorial.ms new file mode 100644 index 00000000..d5c8a7d5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/PSD.doc/tutorial.ms @@ -0,0 +1,3783 @@ +.\" $NetBSD: tutorial.ms,v 1.12 2014/09/30 21:33:14 christos Exp $ +.\" Copyright (c) 1988, 1989, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Adam de Boor. +.\" +.\" 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. +.\" +.\" Copyright (c) 1988, 1989 by Adam de Boor +.\" Copyright (c) 1989 by Berkeley Softworks +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Adam de Boor. +.\" +.\" 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. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. 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. +.\" +.\" @(#)tutorial.ms 8.1 (Berkeley) 8/18/93 +.\" +.EH 'PSD:12-%''PMake \*- A Tutorial' +.OH 'PMake \*- A Tutorial''PSD:12-%' +.\" Ix is an indexing macro similar to .IX but I've disabled it for now +.\" Since that would require 2 passes and I am not in the mood for that. +.de Ix +.. +.\" Rd is section (region) define and Rm is region mention? Again disable for +.\" now. +.de Rd +.. +.de Rm +.. +.\" xH is a macro to provide numbered headers that are automatically stuffed +.\" into a table-of-contents, properly indented, etc. If the first argument +.\" is numeric, it is taken as the depth for numbering (as for .NH), else +.\" the default (1) is assumed. +.\" +.\" @P The initial paragraph distance. +.\" @Q The piece of section number to increment (or 0 if none given) +.\" @R Section header. +.\" @S Indent for toc entry +.\" @T Argument to NH (can't use @Q b/c giving 0 to NH resets the counter) +.de xH +.NH \\$1 +\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.nr PD .1v +.XS \\n% +.ta 0.6i +\\*(SN \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.XE +.nr PD .3v +.. +.\" CW is used to place a string in fixed-width or switch to a +.\" fixed-width font. +.\" C is a typewriter font for a laserwriter. Use something else if +.\" you don't have one... +.de CW +.ie !\\n(.$ .ft C +.el \&\\$3\fC\\$1\fP\\$2 +.. +.\" Anything I put in a display I want to be in fixed-width +.am DS +.CW +.. +.\" The stuff in .No produces a little stop sign in the left margin +.\" that says NOTE in it. Unfortunately, it does cause a break, but +.\" hey. Can't have everything. In case you're wondering how I came +.\" up with such weird commands, they came from running grn on a +.\" gremlin file... +.de No +.br +.ne 0.5i +.po -0.5i +.br +.mk +.nr g3 \\n(.f +.nr g4 \\n(.s +.sp -1 +.\" .st cf +\D't 5u' +.sp -1 +\h'50u' +.sp -1 +\D't 3u' +.sp -1 +.sp 7u +\h'53u' +\d\D'p -0.19i 0.0i 0.0i -0.13i 0.30i 0.0i 0.0i 0.13i' +.sp -1 +.ft R +.ps 6 +.nr g8 \\n(.d +.ds g9 "NOTE +.sp 74u +\h'85u'\v'0.85n'\h-\w\\*(g9u/2u\&\\*(g9 +.sp |\\n(g8u +.sp 166u +\D't 3u' +.br +.po +.rt +.ft \\n(g3 +.ps \\n(g4 +.. +.de Bp +.ie !\\n(.$ .IP \(bu 2 +.el .IP "\&" 2 +.. +.po +.3i +.TL +PMake \*- A Tutorial +.AU +Adam de Boor +.AI +Berkeley Softworks +2150 Shattuck Ave, Penthouse +Berkeley, CA 94704 +adam@bsw.uu.net +\&...!uunet!bsw!adam +.FS +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appears in all copies. +The University of California, Berkeley Softworks, and Adam de Boor make no +representations about the suitability of this software for any +purpose. It is provided "as is" without express or implied warranty. +.FE +.PP +.xH 1 Introduction +.LP +PMake is a program for creating other programs, or anything else you +can think of for it to do. The basic idea behind PMake is that, for +any given system, be it a program or a document or whatever, there +will be some files that depend on the state of other files (on when +they were last modified). PMake takes these dependencies, which you +must specify, and uses them to build whatever it is you want it to +build. +.LP +PMake is almost fully-compatible with Make, with which you may already +be familiar. PMake's most important feature is its ability to run +several different jobs at once, making the creation of systems +considerably faster. It also has a great deal more functionality than +Make. Throughout the text, whenever something is mentioned that is an +important difference between PMake and Make (i.e. something that will +cause a makefile to fail if you don't do something about it), or is +simply important, it will be flagged with a little sign in the left +margin, like this: +.No +.LP +This tutorial is divided into three main sections corresponding to basic, +intermediate and advanced PMake usage. If you already know Make well, +you will only need to skim chapter 2 (there are some aspects of +PMake that I consider basic to its use that didn't exist in Make). +Things in chapter 3 make life much easier, while those in chapter 4 +are strictly for those who know what they are doing. Chapter 5 has +definitions for the jargon I use and chapter 6 contains possible +solutions to the problems presented throughout the tutorial. +.xH 1 The Basics of PMake +.LP +PMake takes as input a file that tells a) which files depend on which +other files to be complete and b) what to do about files that are +``out-of-date.'' This file is known as a ``makefile'' and is usually +.Ix 0 def makefile +kept in the top-most directory of the system to be built. While you +can call the makefile anything you want, PMake will look for +.CW Makefile +and +.CW makefile +(in that order) in the current directory if you don't tell it +otherwise. +.Ix 0 def makefile default +To specify a different makefile, use the +.B \-f +flag (e.g. +.CW "pmake -f program.mk" ''). `` +.Ix 0 ref flags -f +.Ix 0 ref makefile other +.LP +A makefile has four different types of lines in it: +.RS +.IP \(bu 2 +File dependency specifications +.IP \(bu 2 +Creation commands +.IP \(bu 2 +Variable assignments +.IP \(bu 2 +Comments, include statements and conditional directives +.RE +.LP +Any line may be continued over multiple lines by ending it with a +backslash. +.Ix 0 def "continuation line" +The backslash, following newline and any initial whitespace +on the following line are compressed into a single space before the +input line is examined by PMake. +.xH 2 Dependency Lines +.LP +As mentioned in the introduction, in any system, there are +dependencies between the files that make up the system. For instance, +in a program made up of several C source files and one header file, +the C files will need to be re-compiled should the header file be +changed. For a document of several chapters and one macro file, the +chapters will need to be reprocessed if any of the macros changes. +.Ix 0 def "dependency" +These are dependencies and are specified by means of dependency lines in +the makefile. +.LP +.Ix 0 def "dependency line" +On a dependency line, there are targets and sources, separated by a +one- or two-character operator. +The targets ``depend'' on the sources and are usually created from +them. +.Ix 0 def target +.Ix 0 def source +.Ix 0 ref operator +Any number of targets and sources may be specified on a dependency line. +All the targets in the line are made to depend on all the sources. +Targets and sources need not be actual files, but every source must be +either an actual file or another target in the makefile. +If you run out of room, use a backslash at the end of the line to continue onto +the next one. +.LP +Any file may be a target and any file may be a source, but the +relationship between the two (or however many) is determined by the +``operator'' that separates them. +.Ix 0 def operator +Three types of operators exist: one specifies that the datedness of a +target is determined by the state of its sources, while another +specifies other files (the sources) that need to be dealt with before +the target can be re-created. The third operator is very similar to +the first, with the additional condition that the target is +out-of-date if it has no sources. These operations are represented by +the colon, the exclamation point and the double-colon, respectively, and are +mutually exclusive. Their exact semantics are as follows: +.IP ":" +.Ix 0 def operator colon +.Ix 0 def : +If a colon is used, a target on the line is considered to be +``out-of-date'' (and in need of creation) if +.RS +.IP \(bu 2 +any of the sources has been modified more recently than the target, or +.IP \(bu 2 +the target doesn't exist. +.RE +.Ix 0 def out-of-date +.IP "\&" +Under this operation, steps will be taken to re-create the target only +if it is found to be out-of-date by using these two rules. +.IP "!" +.Ix 0 def operator force +.Ix 0 def ! +If an exclamation point is used, the target will always be re-created, +but this will not happen until all of its sources have been examined +and re-created, if necessary. +.IP "::" +.Ix 0 def operator double-colon +.Ix 0 def :: +If a double-colon is used, a target is out-of-date if: +.RS +.IP \(bu 2 +any of the sources has been modified more recently than the target, or +.IP \(bu 2 +the target doesn't exist, or +.IP \(bu 2 +the target has no sources. +.RE +.IP "\&" +If the target is out-of-date according to these rules, it will be re-created. +This operator also does something else to the targets, but I'll go +into that in the next section (``Shell Commands''). +.LP +Enough words, now for an example. Take that C program I mentioned +earlier. Say there are three C files +.CW a.c , ( +.CW b.c +and +.CW c.c ) +each of which +includes the file +.CW defs.h . +The dependencies between the files could then be expressed as follows: +.DS +program : a.o b.o c.o +a.o b.o c.o : defs.h +a.o : a.c +b.o : b.c +c.o : c.c +.DE +.LP +You may be wondering at this point, where +.CW a.o , +.CW b.o +and +.CW c.o +came in and why +.I they +depend on +.CW defs.h +and the C files don't. The reason is quite simple: +.CW program +cannot be made by linking together .c files \*- it must be +made from .o files. Likewise, if you change +.CW defs.h , +it isn't the .c files that need to be re-created, it's the .o files. +If you think of dependencies in these terms \*- which files (targets) +need to be created from which files (sources) \*- you should have no problems. +.LP +An important thing to notice about the above example, is that all the +\&.o files appear as targets on more than one line. This is perfectly +all right: the target is made to depend on all the sources mentioned +on all the dependency lines. E.g. +.CW a.o +depends on both +.CW defs.h +and +.CW a.c . +.Ix 0 ref dependency +.No +.LP +The order of the dependency lines in the makefile is +important: the first target on the first dependency line in the +makefile will be the one that gets made if you don't say otherwise. +That's why +.CW program +comes first in the example makefile, above. +.LP +Both targets and sources may contain the standard C-Shell wildcard +characters +.CW { , ( +.CW } , +.CW * , +.CW ? , +.CW [ , +and +.CW ] ), +but the non-curly-brace ones may only appear in the final component +(the file portion) of the target or source. The characters mean the +following things: +.IP \fB{}\fP +These enclose a comma-separated list of options and cause the pattern +to be expanded once for each element of the list. Each expansion +contains a different element. For example, +.CW src/{whiffle,beep,fish}.c +expands to the three words +.CW src/whiffle.c , +.CW src/beep.c , +and +.CW src/fish.c . +These braces may be nested and, unlike the other wildcard characters, +the resulting words need not be actual files. All other wildcard +characters are expanded using the files that exist when PMake is +started. +.IP \fB*\fP +This matches zero or more characters of any sort. +.CW src/*.c +will expand to the same three words as above as long as +.CW src +contains those three files (and no other files that end in +.CW .c ). +.IP \fB?\fP +Matches any single character. +.IP \fB[]\fP +This is known as a character class and contains either a list of +single characters, or a series of character ranges +.CW a-z , ( +for example means all characters between a and z), or both. It matches +any single character contained in the list. E.g. +.CW [A-Za-z] +will match all letters, while +.CW [0123456789] +will match all numbers. +.xH 2 Shell Commands +.LP +``Isn't that nice,'' you say to yourself, ``but how are files +actually `re-created,' as he likes to spell it?'' +The re-creation is accomplished by commands you place in the makefile. +These commands are passed to the Bourne shell (better known as +``/bin/sh'') to be executed and are +.Ix 0 ref shell +.Ix 0 ref re-creation +.Ix 0 ref update +expected to do what's necessary to update the target file (PMake +doesn't actually check to see if the target was created. It just +assumes it's there). +.Ix 0 ref target +.LP +Shell commands in a makefile look a lot like shell commands you would +type at a terminal, with one important exception: each command in a +makefile +.I must +be preceded by at least one tab. +.LP +Each target has associated with it a shell script made up of +one or more of these shell commands. The creation script for a target +should immediately follow the dependency line for that target. While +any given target may appear on more than one dependency line, only one +of these dependency lines may be followed by a creation script, unless +the `::' operator was used on the dependency line. +.Ix 0 ref operator double-colon +.Ix 0 ref :: +.No +.LP +If the double-colon was used, each dependency line for the target +may be followed by a shell script. That script will only be executed +if the target on the associated dependency line is out-of-date with +respect to the sources on that line, according to the rules I gave +earlier. +I'll give you a good example of this later on. +.LP +To expand on the earlier makefile, you might add commands as follows: +.DS +program : a.o b.o c.o + cc a.o b.o c.o \-o program +a.o b.o c.o : defs.h +a.o : a.c + cc \-c a.c +b.o : b.c + cc \-c b.c +c.o : c.c + cc \-c c.c +.DE +.LP +Something you should remember when writing a makefile is, the +commands will be executed if the +.I target +on the dependency line is out-of-date, not the sources. +.Ix 0 ref target +.Ix 0 ref source +.Ix 0 ref out-of-date +In this example, the command +.CW "cc \-c a.c" '' `` +will be executed if +.CW a.o +is out-of-date. Because of the `:' operator, +.Ix 0 ref : +.Ix 0 ref operator colon +this means that should +.CW a.c +.I or +.CW defs.h +have been modified more recently than +.CW a.o , +the command will be executed +.CW a.o "\&" ( +will be considered out-of-date). +.Ix 0 ref out-of-date +.LP +Remember how I said the only difference between a makefile shell +command and a regular shell command was the leading tab? I lied. There +is another way in which makefile commands differ from regular ones. +The first two characters after the initial whitespace are treated +specially. +If they are any combination of `@' and `\-', they cause PMake to do +different things. +.LP +In most cases, shell commands are printed before they're +actually executed. This is to keep you informed of what's going on. If +an `@' appears, however, this echoing is suppressed. In the case of an +.CW echo +command, say +.CW "echo Linking index" ,'' `` +it would be +rather silly to see +.DS +echo Linking index +Linking index +.DE +.LP +so PMake allows you to place an `@' before the command +.CW "@echo Linking index" '') (`` +to prevent the command from being printed. +.LP +The other special character is the `\-'. In case you didn't know, +shell commands finish with a certain ``exit status.'' This status is +made available by the operating system to whatever program invoked the +command. Normally this status will be 0 if everything went ok and +non-zero if something went wrong. For this reason, PMake will consider +an error to have occurred if one of the shells it invokes returns a non-zero +status. When it detects an error, PMake's usual action is to abort +whatever it's doing and exit with a non-zero status itself (any other +targets that were being created will continue being made, but nothing +new will be started. PMake will exit after the last job finishes). +This behavior can be altered, however, by placing a `\-' at the front +of a command +.CW "\-mv index index.old" ''), (`` +certain command-line arguments, +or doing other things, to be detailed later. In such +a case, the non-zero status is simply ignored and PMake keeps chugging +along. +.No +.LP +Because all the commands are given to a single shell to execute, such +things as setting shell variables, changing directories, etc., last +beyond the command in which they are found. This also allows shell +compound commands (like +.CW for +loops) to be entered in a natural manner. +Since this could cause problems for some makefiles that depend on +each command being executed by a single shell, PMake has a +.B \-B +.Ix 0 ref compatibility +.Ix 0 ref flags -B +flag (it stands for backwards-compatible) that forces each command to +be given to a separate shell. It also does several other things, all +of which I discourage since they are now old-fashioned.\|.\|.\|. +.No +.LP +A target's shell script is fed to the shell on its (the shell's) input stream. +This means that any commands, such as +.CW ci +that need to get input from the terminal won't work right \*- they'll +get the shell's input, something they probably won't find to their +liking. A simple way around this is to give a command like this: +.DS +ci $(SRCS) < /dev/tty +.DE +This would force the program's input to come from the terminal. If you +can't do this for some reason, your only other alternative is to use +PMake in its fullest compatibility mode. See +.B Compatibility +in chapter 4. +.Ix 0 ref compatibility +.LP +.xH 2 Variables +.LP +PMake, like Make before it, has the ability to save text in variables +to be recalled later at your convenience. Variables in PMake are used +much like variables in the shell and, by tradition, consist of +all upper-case letters (you don't +.I have +to use all upper-case letters. +In fact there's nothing to stop you from calling a variable +.CW @^&$%$ . +Just tradition). Variables are assigned-to using lines of the form +.Ix 0 def variable assignment +.DS +VARIABLE = value +.DE +.Ix 0 def variable assignment +appended-to by +.DS +VARIABLE += value +.DE +.Ix 0 def variable appending +.Ix 0 def variable assignment appended +.Ix 0 def += +conditionally assigned-to (if the variable isn't already defined) by +.DS +VARIABLE ?= value +.DE +.Ix 0 def variable assignment conditional +.Ix 0 def ?= +and assigned-to with expansion (i.e. the value is expanded (see below) +before being assigned to the variable\*-useful for placing a value at +the beginning of a variable, or other things) by +.DS +VARIABLE := value +.DE +.Ix 0 def variable assignment expanded +.Ix 0 def := +.LP +Any whitespace before +.I value +is stripped off. When appending, a space is placed between the old +value and the stuff being appended. +.LP +The final way a variable may be assigned to is using +.DS +VARIABLE != shell-command +.DE +.Ix 0 def variable assignment shell-output +.Ix 0 def != +In this case, +.I shell-command +has all its variables expanded (see below) and is passed off to a +shell to execute. The output of the shell is then placed in the +variable. Any newlines (other than the final one) are replaced by +spaces before the assignment is made. This is typically used to find +the current directory via a line like: +.DS +CWD != pwd +.DE +.LP +.B Note: +this is intended to be used to execute commands that produce small amounts +of output (e.g. ``pwd''). The implementation is less than intelligent and will +likely freeze if you execute something that produces thousands of +bytes of output (8 Kb is the limit on many UNIX systems). +.LP +The value of a variable may be retrieved by enclosing the variable +name in parentheses or curly braces and preceding the whole thing +with a dollar sign. +.LP +For example, to set the variable CFLAGS to the string +.CW "\-I/sprite/src/lib/libc \-O" ,'' `` +you would place a line +.DS +CFLAGS = \-I/sprite/src/lib/libc \-O +.DE +in the makefile and use the word +.CW "$(CFLAGS)" +wherever you would like the string +.CW "\-I/sprite/src/lib/libc \-O" +to appear. This is called variable expansion. +.Ix 0 def variable expansion +.No +.LP +Unlike Make, PMake will not expand a variable unless it knows +the variable exists. E.g. if you have a +.CW "${i}" +in a shell command and you have not assigned a value to the variable +.CW i +(the empty string is considered a value, by the way), where Make would have +substituted the empty string, PMake will leave the +.CW "${i}" +alone. +To keep PMake from substituting for a variable it knows, precede the +dollar sign with another dollar sign. +(e.g. to pass +.CW "${HOME}" +to the shell, use +.CW "$${HOME}" ). +This causes PMake, in effect, to expand the +.CW $ +macro, which expands to a single +.CW $ . +For compatibility, Make's style of variable expansion will be used +if you invoke PMake with any of the compatibility flags (\c +.B \-V , +.B \-B +or +.B \-M . +The +.B \-V +flag alters just the variable expansion). +.Ix 0 ref flags -V +.Ix 0 ref flags -B +.Ix 0 ref flags -M +.Ix 0 ref compatibility +.LP +.Ix 0 ref variable expansion +There are two different times at which variable expansion occurs: +When parsing a dependency line, the expansion occurs immediately +upon reading the line. If any variable used on a dependency line is +undefined, PMake will print a message and exit. +Variables in shell commands are expanded when the command is +executed. +Variables used inside another variable are expanded whenever the outer +variable is expanded (the expansion of an inner variable has no effect +on the outer variable. I.e. if the outer variable is used on a dependency +line and in a shell command, and the inner variable changes value +between when the dependency line is read and the shell command is +executed, two different values will be substituted for the outer +variable). +.Ix 0 def variable types +.LP +Variables come in four flavors, though they are all expanded the same +and all look about the same. They are (in order of expanding scope): +.RS +.IP \(bu 2 +Local variables. +.Ix 0 ref variable local +.IP \(bu 2 +Command-line variables. +.Ix 0 ref variable command-line +.IP \(bu 2 +Global variables. +.Ix 0 ref variable global +.IP \(bu 2 +Environment variables. +.Ix 0 ref variable environment +.RE +.LP +The classification of variables doesn't matter much, except that the +classes are searched from the top (local) to the bottom (environment) +when looking up a variable. The first one found wins. +.xH 3 Local Variables +.LP +.Ix 0 def variable local +Each target can have as many as seven local variables. These are +variables that are only ``visible'' within that target's shell script +and contain such things as the target's name, all of its sources (from +all its dependency lines), those sources that were out-of-date, etc. +Four local variables are defined for all targets. They are: +.RS +.IP ".TARGET" +.Ix 0 def variable local .TARGET +.Ix 0 def .TARGET +The name of the target. +.IP ".OODATE" +.Ix 0 def variable local .OODATE +.Ix 0 def .OODATE +The list of the sources for the target that were considered out-of-date. +The order in the list is not guaranteed to be the same as the order in +which the dependencies were given. +.IP ".ALLSRC" +.Ix 0 def variable local .ALLSRC +.Ix 0 def .ALLSRC +The list of all sources for this target in the order in which they +were given. +.IP ".PREFIX" +.Ix 0 def variable local .PREFIX +.Ix 0 def .PREFIX +The target without its suffix and without any leading path. E.g. for +the target +.CW ../../lib/compat/fsRead.c , +this variable would contain +.CW fsRead . +.RE +.LP +Three other local variables are set only for certain targets under +special circumstances. These are the ``.IMPSRC,'' +.Ix 0 ref variable local .IMPSRC +.Ix 0 ref .IMPSRC +``.ARCHIVE,'' +.Ix 0 ref variable local .ARCHIVE +.Ix 0 ref .ARCHIVE +and ``.MEMBER'' +.Ix 0 ref variable local .MEMBER +.Ix 0 ref .MEMBER +variables. When they are set and how they are used is described later. +.LP +Four of these variables may be used in sources as well as in shell +scripts. +.Ix 0 def "dynamic source" +.Ix 0 def source dynamic +These are ``.TARGET'', ``.PREFIX'', ``.ARCHIVE'' and ``.MEMBER''. The +variables in the sources are expanded once for each target on the +dependency line, providing what is known as a ``dynamic source,'' +.Rd 0 +allowing you to specify several dependency lines at once. For example, +.DS +$(OBJS) : $(.PREFIX).c +.DE +will create a dependency between each object file and its +corresponding C source file. +.xH 3 Command-line Variables +.LP +.Ix 0 def variable command-line +Command-line variables are set when PMake is first invoked by giving a +variable assignment as one of the arguments. For example, +.DS +pmake "CFLAGS = -I/sprite/src/lib/libc -O" +.DE +would make +.CW CFLAGS +be a command-line variable with the given value. Any assignments to +.CW CFLAGS +in the makefile will have no effect, because once it +is set, there is (almost) nothing you can do to change a command-line +variable (the search order, you see). Command-line variables may be +set using any of the four assignment operators, though only +.CW = +and +.CW ?= +behave as you would expect them to, mostly because assignments to +command-line variables are performed before the makefile is read, thus +the values set in the makefile are unavailable at the time. +.CW += +.Ix 0 ref += +.Ix 0 ref variable assignment appended +is the same as +.CW = , +because the old value of the variable is sought only in the scope in +which the assignment is taking place (for reasons of efficiency that I +won't get into here). +.CW := +and +.CW ?= +.Ix 0 ref := +.Ix 0 ref ?= +.Ix 0 ref variable assignment expanded +.Ix 0 ref variable assignment conditional +will work if the only variables used are in the environment. +.CW != +is sort of pointless to use from the command line, since the same +effect can no doubt be accomplished using the shell's own command +substitution mechanisms (backquotes and all that). +.xH 3 Global Variables +.LP +.Ix 0 def variable global +Global variables are those set or appended-to in the makefile. +There are two classes of global variables: those you set and those PMake sets. +As I said before, the ones you set can have any name you want them to have, +except they may not contain a colon or an exclamation point. +The variables PMake sets (almost) always begin with a +period and always contain upper-case letters, only. The variables are +as follows: +.RS +.IP .PMAKE +.Ix 0 def variable global .PMAKE +.Ix 0 def .PMAKE +.Ix 0 def variable global MAKE +.Ix 0 def MAKE +The name by which PMake was invoked is stored in this variable. For +compatibility, the name is also stored in the MAKE variable. +.IP .MAKEFLAGS +.Ix 0 def variable global .MAKEFLAGS +.Ix 0 def .MAKEFLAGS variable +.Ix 0 def variable global MFLAGS +.Ix 0 def MFLAGS +All the relevant flags with which PMake was invoked. This does not +include such things as +.B \-f +or variable assignments. Again for compatibility, this value is stored +in the MFLAGS variable as well. +.RE +.LP +Two other variables, ``.INCLUDES'' and ``.LIBS,'' are covered in the +section on special targets in chapter 3. +.Ix 0 ref variable global .INCLUDES +.Ix 0 ref variable global .LIBS +.LP +Global variables may be deleted using lines of the form: +.Ix 0 def #undef +.Ix 0 def variable deletion +.DS +#undef \fIvariable\fP +.DE +The +.CW # ' ` +must be the first character on the line. Note that this may only be +done on global variables. +.xH 3 Environment Variables +.LP +.Ix 0 def variable environment +Environment variables are passed by the shell that invoked PMake and +are given by PMake to each shell it invokes. They are expanded like +any other variable, but they cannot be altered in any way. +.LP +One special environment variable, +.CW PMAKE , +.Ix 0 def variable environment PMAKE +is examined by PMake for command-line flags, variable assignments, +etc., it should always use. This variable is examined before the +actual arguments to PMake are. In addition, all flags given to PMake, +either through the +.CW PMAKE +variable or on the command line, are placed in this environment +variable and exported to each shell PMake executes. Thus recursive +invocations of PMake automatically receive the same flags as the +top-most one. +.LP +Using all these variables, you can compress the sample makefile even more: +.DS +OBJS = a.o b.o c.o +program : $(OBJS) + cc $(.ALLSRC) \-o $(.TARGET) +$(OBJS) : defs.h +a.o : a.c + cc \-c a.c +b.o : b.c + cc \-c b.c +c.o : c.c + cc \-c c.c +.DE +.Ix 0 ref variable local .ALLSRC +.Ix 0 ref .ALLSRC +.Ix 0 ref variable local .TARGET +.Ix 0 ref .TARGET +.Rd 3 +.xH 2 Comments +.LP +.Ix 0 def comments +Comments in a makefile start with a `#' character and extend to the +end of the line. They may appear +anywhere you want them, except in a shell command (though the shell +will treat it as a comment, too). If, for some reason, you need to use the `#' +in a variable or on a dependency line, put a backslash in front of it. +PMake will compress the two into a single `#' (Note: this isn't true +if PMake is operating in full-compatibility mode). +.Ix 0 ref flags -M +.Ix 0 ref compatibility +.xH 2 Parallelism +.No +.LP +PMake was specifically designed to re-create several targets at once, +when possible. You do not have to do anything special to cause this to +happen (unless PMake was configured to not act in parallel, in which +case you will have to make use of the +.B \-L +and +.B \-J +flags (see below)), +.Ix 0 ref flags -L +.Ix 0 ref flags -J +but you do have to be careful at times. +.LP +There are several problems you are likely to encounter. One is +that some makefiles (and programs) are written in such a way that it is +impossible for two targets to be made at once. The program +.CW xstr , +for example, +always modifies the files +.CW strings +and +.CW x.c . +There is no way to change it. Thus you cannot run two of them at once +without something being trashed. Similarly, if you have commands +in the makefile that always send output to the same file, you will not +be able to make more than one target at once unless you change the +file you use. You can, for instance, add a +.CW $$$$ +to the end of the file name to tack on the process ID of the shell +executing the command (each +.CW $$ +expands to a single +.CW $ , +thus giving you the shell variable +.CW $$ ). +Since only one shell is used for all the +commands, you'll get the same file name for each command in the +script. +.LP +The other problem comes from improperly-specified dependencies that +worked in Make because of its sequential, depth-first way of examining +them. While I don't want to go into depth on how PMake +works (look in chapter 4 if you're interested), I will warn you that +files in two different ``levels'' of the dependency tree may be +examined in a different order in PMake than they were in Make. For +example, given the makefile +.DS +a : b c +b : d +.DE +PMake will examine the targets in the order +.CW c , +.CW d , +.CW b , +.CW a . +If the makefile's author expected PMake to abort before making +.CW c +if an error occurred while making +.CW b , +or if +.CW b +needed to exist before +.CW c +was made, +s/he will be sorely disappointed. The dependencies are +incomplete, since in both these cases, +.CW c +would depend on +.CW b . +So watch out. +.LP +Another problem you may face is that, while PMake is set up to handle the +output from multiple jobs in a graceful fashion, the same is not so for input. +It has no way to regulate input to different jobs, +so if you use the redirection from +.CW /dev/tty +I mentioned earlier, you must be careful not to run two of the jobs at once. +.xH 2 Writing and Debugging a Makefile +.LP +Now you know most of what's in a makefile, what do you do next? There +are two choices: (1) use one of the uncommonly-available makefile +generators or (2) write your own makefile (I leave out the third choice of +ignoring PMake and doing everything by hand as being beyond the bounds +of common sense). +.LP +When faced with the writing of a makefile, it is usually best to start +from first principles: just what +.I are +you trying to do? What do you want the makefile finally to produce? +.LP +To begin with a somewhat traditional example, let's say you need to +write a makefile to create a program, +.CW expr , +that takes standard infix expressions and converts them to prefix form (for +no readily apparent reason). You've got three source files, in C, that +make up the program: +.CW main.c , +.CW parse.c , +and +.CW output.c . +Harking back to my pithy advice about dependency lines, you write the +first line of the file: +.DS +expr : main.o parse.o output.o +.DE +because you remember +.CW expr +is made from +.CW .o +files, not +.CW .c +files. Similarly for the +.CW .o +files you produce the lines: +.DS +main.o : main.c +parse.o : parse.c +output.o : output.c +main.o parse.o output.o : defs.h +.DE +.LP +Great. You've now got the dependencies specified. What you need now is +commands. These commands, remember, must produce the target on the +dependency line, usually by using the sources you've listed. +You remember about local variables? Good, so it should come +to you as no surprise when you write +.DS +expr : main.o parse.o output.o + cc -o $(.TARGET) $(.ALLSRC) +.DE +Why use the variables? If your program grows to produce postfix +expressions too (which, of course, requires a name change or two), it +is one fewer place you have to change the file. You cannot do this for +the object files, however, because they depend on their corresponding +source files +.I and +.CW defs.h , +thus if you said +.DS + cc -c $(.ALLSRC) +.DE +you'd get (for +.CW main.o ): +.DS + cc -c main.c defs.h +.DE +which is wrong. So you round out the makefile with these lines: +.DS +main.o : main.c + cc -c main.c +parse.o : parse.c + cc -c parse.c +output.o : output.c + cc -c output.c +.DE +.LP +The makefile is now complete and will, in fact, create the program you +want it to without unnecessary compilations or excessive typing on +your part. There are two things wrong with it, however (aside from it +being altogether too long, something I'll address in chapter 3): +.IP 1) +The string +.CW "main.o parse.o output.o" '' `` +is repeated twice, necessitating two changes when you add postfix +(you were planning on that, weren't you?). This is in direct violation +of de Boor's First Rule of writing makefiles: +.QP +.I +Anything that needs to be written more than once +should be placed in a variable. +.IP "\&" +I cannot emphasize this enough as being very important to the +maintenance of a makefile and its program. +.IP 2) +There is no way to alter the way compilations are performed short of +editing the makefile and making the change in all places. This is evil +and violates de Boor's Second Rule, which follows directly from the +first: +.QP +.I +Any flags or programs used inside a makefile should be placed in a variable so +they may be changed, temporarily or permanently, with the greatest ease. +.LP +The makefile should more properly read: +.DS +OBJS = main.o parse.o output.o +expr : $(OBJS) + $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) +main.o : main.c + $(CC) $(CFLAGS) -c main.c +parse.o : parse.c + $(CC) $(CFLAGS) -c parse.c +output.o : output.c + $(CC) $(CFLAGS) -c output.c +$(OBJS) : defs.h +.DE +Alternatively, if you like the idea of dynamic sources mentioned in +section 2.3.1, +.Rm 0 2.3.1 +.Rd 4 +.Ix 0 ref "dynamic source" +.Ix 0 ref source dynamic +you could write it like this: +.DS +OBJS = main.o parse.o output.o +expr : $(OBJS) + $(CC) $(CFLAGS) -o $(.TARGET) $(.ALLSRC) +$(OBJS) : $(.PREFIX).c defs.h + $(CC) $(CFLAGS) -c $(.PREFIX).c +.DE +These two rules and examples lead to de Boor's First Corollary: +.QP +.I +Variables are your friends. +.LP +Once you've written the makefile comes the sometimes-difficult task of +.Ix 0 ref debugging +making sure the darn thing works. Your most helpful tool to make sure +the makefile is at least syntactically correct is the +.B \-n +.Ix 0 ref flags -n +flag, which allows you to see if PMake will choke on the makefile. The +second thing the +.B \-n +flag lets you do is see what PMake would do without it actually doing +it, thus you can make sure the right commands would be executed were +you to give PMake its head. +.LP +When you find your makefile isn't behaving as you hoped, the first +question that comes to mind (after ``What time is it, anyway?'') is +``Why not?'' In answering this, two flags will serve you well: +.CW "-d m" '' `` +.Ix 0 ref flags -d +and +.CW "-p 2" .'' `` +.Ix 0 ref flags -p +The first causes PMake to tell you as it examines each target in the +makefile and indicate why it is deciding whatever it is deciding. You +can then use the information printed for other targets to see where +you went wrong. The +.CW "-p 2" '' `` +flag makes PMake print out its internal state when it is done, +allowing you to see that you forgot to make that one chapter depend on +that file of macros you just got a new version of. The output from +.CW "-p 2" '' `` +is intended to resemble closely a real makefile, but with additional +information provided and with variables expanded in those commands +PMake actually printed or executed. +.LP +Something to be especially careful about is circular dependencies. +.Ix 0 def dependency circular +E.g. +.DS +a : b +b : c d +d : a +.DE +In this case, because of how PMake works, +.CW c +is the only thing PMake will examine, because +.CW d +and +.CW a +will effectively fall off the edge of the universe, making it +impossible to examine +.CW b +(or them, for that matter). +PMake will tell you (if run in its normal mode) all the targets +involved in any cycle it looked at (i.e. if you have two cycles in the +graph (naughty, naughty), but only try to make a target in one of +them, PMake will only tell you about that one. You'll have to try to +make the other to find the second cycle). When run as Make, it will +only print the first target in the cycle. +.xH 2 Invoking PMake +.LP +.Ix 0 ref flags +.Ix 0 ref arguments +.Ix 0 ref usage +PMake comes with a wide variety of flags to choose from. +They may appear in any order, interspersed with command-line variable +assignments and targets to create. +The flags are as follows: +.IP "\fB\-d\fP \fIwhat\fP" +.Ix 0 def flags -d +.Ix 0 ref debugging +This causes PMake to spew out debugging information that +may prove useful to you. If you can't +figure out why PMake is doing what it's doing, you might try using +this flag. The +.I what +parameter is a string of single characters that tell PMake what +aspects you are interested in. Most of what I describe will make +little sense to you, unless you've dealt with Make before. Just +remember where this table is and come back to it as you read on. +The characters and the information they produce are as follows: +.RS +.IP a +Archive searching and caching. +.IP c +Conditional evaluation. +.IP d +The searching and caching of directories. +.IP j +Various snippets of information related to the running of the multiple +shells. Not particularly interesting. +.IP m +The making of each target: what target is being examined; when it was +last modified; whether it is out-of-date; etc. +.IP p +Makefile parsing. +.IP r +Remote execution. +.IP s +The application of suffix-transformation rules. (See chapter 3) +.IP t +The maintenance of the list of targets. +.IP v +Variable assignment. +.RE +.IP "\&" +Of these all, the +.CW m +and +.CW s +letters will be most useful to you. +If the +.B \-d +is the final argument or the argument from which it would get these +key letters (see below for a note about which argument would be used) +begins with a +.B \- , +all of these debugging flags will be set, resulting in massive amounts +of output. +.IP "\fB\-f\fP \fImakefile\fP" +.Ix 0 def flags -f +Specify a makefile to read different from the standard makefiles +.CW Makefile "\&" ( +or +.CW makefile ). +.Ix 0 ref makefile default +.Ix 0 ref makefile other +If +.I makefile +is ``\-'', PMake uses the standard input. This is useful for making +quick and dirty makefiles.\|.\|. +.Ix 0 ref makefile "quick and dirty" +.IP \fB\-h\fP +.Ix 0 def flags -h +Prints out a summary of the various flags PMake accepts. It can also +be used to find out what level of concurrency was compiled into the +version of PMake you are using (look at +.B \-J +and +.B \-L ) +and various other information on how PMake was configured. +.Ix 0 ref configuration +.Ix 0 ref makefile system +.IP \fB\-i\fP +.Ix 0 def flags -i +If you give this flag, PMake will ignore non-zero status returned +by any of its shells. It's like placing a `\-' before all the commands +in the makefile. +.IP \fB\-k\fP +.Ix 0 def flags -k +This is similar to +.B \-i +in that it allows PMake to continue when it sees an error, but unlike +.B \-i , +where PMake continues blithely as if nothing went wrong, +.B \-k +causes it to recognize the error and only continue work on those +things that don't depend on the target, either directly or indirectly (through +depending on something that depends on it), whose creation returned the error. +The `k' is for ``keep going''.\|.\|. +.Ix 0 ref target +.IP \fB\-l\fP +.Ix 0 def flags -l +PMake has the ability to lock a directory against other +people executing it in the same directory (by means of a file called +``LOCK.make'' that it creates and checks for in the directory). This +is a Good Thing because two people doing the same thing in the same place +can be disastrous for the final product (too many cooks and all that). +Whether this locking is the default is up to your system +administrator. If locking is on, +.B \-l +will turn it off, and vice versa. Note that this locking will not +prevent \fIyou\fP from invoking PMake twice in the same place \*- if +you own the lock file, PMake will warn you about it but continue to execute. +.IP "\fB\-m\fP \fIdirectory\fP" +.Ix 0 def flags -m +Tells PMake another place to search for included makefiles via the <...> +style. Several +.B \-m +options can be given to form a search path. If this construct is used the +default system makefile search path is completely overridden. +To be explained in chapter 3, section 3.2. +.Rm 2 3.2 +.IP \fB\-n\fP +.Ix 0 def flags -n +This flag tells PMake not to execute the commands needed to update the +out-of-date targets in the makefile. Rather, PMake will simply print +the commands it would have executed and exit. This is particularly +useful for checking the correctness of a makefile. If PMake doesn't do +what you expect it to, it's a good chance the makefile is wrong. +.IP "\fB\-p\fP \fInumber\fP" +.Ix 0 def flags -p +.Ix 0 ref debugging +This causes PMake to print its input in a reasonable form, though +not necessarily one that would make immediate sense to anyone but me. The +.I number +is a bitwise-or of 1 and 2 where 1 means it should print the input +before doing any processing and 2 says it should print it after +everything has been re-created. Thus +.CW "\-p 3" +would print it twice\*-once before processing and once after (you +might find the difference between the two interesting). This is mostly +useful to me, but you may find it informative in some bizarre circumstances. +.IP \fB\-q\fP +.Ix 0 def flags -q +If you give PMake this flag, it will not try to re-create anything. It +will just see if anything is out-of-date and exit non-zero if so. +.IP \fB\-r\fP +.Ix 0 def flags -r +When PMake starts up, it reads a default makefile that tells it what +sort of system it's on and gives it some idea of what to do if you +don't tell it anything. I'll tell you about it in chapter 3. If you +give this flag, PMake won't read the default makefile. +.IP \fB\-s\fP +.Ix 0 def flags -s +This causes PMake to not print commands before they're executed. It +is the equivalent of putting an `@' before every command in the +makefile. +.IP \fB\-t\fP +.Ix 0 def flags -t +Rather than try to re-create a target, PMake will simply ``touch'' it +so as to make it appear up-to-date. If the target didn't exist before, +it will when PMake finishes, but if the target did exist, it will +appear to have been updated. +.IP \fB\-v\fP +.Ix 0 def flags -v +This is a mixed-compatibility flag intended to mimic the System V +version of Make. It is the same as giving +.B \-B , +and +.B \-V +as well as turning off directory locking. Targets can still be created +in parallel, however. This is the mode PMake will enter if it is +invoked either as +.CW smake '' `` +or +.CW vmake ''. `` +.IP \fB\-x\fP +.Ix 0 def flags -x +This tells PMake it's ok to export jobs to other machines, if they're +available. It is used when running in Make mode, as exporting in this +mode tends to make things run slower than if the commands were just +executed locally. +.IP \fB\-B\fP +.Ix 0 ref compatibility +.Ix 0 def flags -B +Forces PMake to be as backwards-compatible with Make as possible while +still being itself. +This includes: +.RS +.IP \(bu 2 +Executing one shell per shell command +.IP \(bu 2 +Expanding anything that looks even vaguely like a variable, with the +empty string replacing any variable PMake doesn't know. +.IP \(bu 2 +Refusing to allow you to escape a `#' with a backslash. +.IP \(bu 2 +Permitting undefined variables on dependency lines and conditionals +(see below). Normally this causes PMake to abort. +.RE +.IP \fB\-C\fP +.Ix 0 def flags -C +This nullifies any and all compatibility mode flags you may have given +or implied up to the time the +.B \-C +is encountered. It is useful mostly in a makefile that you wrote for PMake +to avoid bad things happening when someone runs PMake as +.CW make '' `` +or has things set in the environment that tell it to be compatible. +.B \-C +is +.I not +placed in the +.CW PMAKE +environment variable or the +.CW .MAKEFLAGS +or +.CW MFLAGS +global variables. +.Ix 0 ref variable environment PMAKE +.Ix 0 ref variable global .MAKEFLAGS +.Ix 0 ref variable global MFLAGS +.Ix 0 ref .MAKEFLAGS variable +.Ix 0 ref MFLAGS +.IP "\fB\-D\fP \fIvariable\fP" +.Ix 0 def flags -D +Allows you to define a variable to have +.CW 1 '' `` +as its value. The variable is a global variable, not a command-line +variable. This is useful mostly for people who are used to the C +compiler arguments and those using conditionals, which I'll get into +in section 4.3 +.Rm 1 4.3 +.IP "\fB\-I\fP \fIdirectory\fP" +.Ix 0 def flags -I +Tells PMake another place to search for included makefiles. Yet +another thing to be explained in chapter 3 (section 3.2, to be +precise). +.Rm 2 3.2 +.IP "\fB\-J\fP \fInumber\fP" +.Ix 0 def flags -J +Gives the absolute maximum number of targets to create at once on both +local and remote machines. +.IP "\fB\-L\fP \fInumber\fP" +.Ix 0 def flags -L +This specifies the maximum number of targets to create on the local +machine at once. This may be 0, though you should be wary of doing +this, as PMake may hang until a remote machine becomes available, if +one is not available when it is started. +.IP \fB\-M\fP +.Ix 0 ref compatibility +.Ix 0 def flags -M +This is the flag that provides absolute, complete, full compatibility +with Make. It still allows you to use all but a few of the features of +PMake, but it is non-parallel. This is the mode PMake enters if you +call it +.CW make .'' `` +.IP \fB\-P\fP +.Ix 0 def flags -P +.Ix 0 ref "output control" +When creating targets in parallel, several shells are executing at +once, each wanting to write its own two cent's-worth to the screen. +This output must be captured by PMake in some way in order to prevent +the screen from being filled with garbage even more indecipherable +than you usually see. PMake has two ways of doing this, one of which +provides for much cleaner output and a clear separation between the +output of different jobs, the other of which provides a more immediate +response so one can tell what is really happening. The former is done +by notifying you when the creation of a target starts, capturing the +output and transferring it to the screen all at once when the job +finishes. The latter is done by catching the output of the shell (and +its children) and buffering it until an entire line is received, then +printing that line preceded by an indication of which job produced +the output. Since I prefer this second method, it is the one used by +default. The first method will be used if you give the +.B \-P +flag to PMake. +.IP \fB\-V\fP +.Ix 0 def flags -V +As mentioned before, the +.B \-V +flag tells PMake to use Make's style of expanding variables, +substituting the empty string for any variable it doesn't know. +.IP \fB\-W\fP +.Ix 0 def flags -W +There are several times when PMake will print a message at you that is +only a warning, i.e. it can continue to work in spite of your having +done something silly (such as forgotten a leading tab for a shell +command). Sometimes you are well aware of silly things you have done +and would like PMake to stop bothering you. This flag tells it to shut +up about anything non-fatal. +.IP \fB\-X\fP +.Ix 0 def flags -X +This flag causes PMake to not attempt to export any jobs to another +machine. +.LP +Several flags may follow a single `\-'. Those flags that require +arguments take them from successive parameters. E.g. +.DS +pmake -fDnI server.mk DEBUG /chip2/X/server/include +.DE +will cause PMake to read +.CW server.mk +as the input makefile, define the variable +.CW DEBUG +as a global variable and look for included makefiles in the directory +.CW /chip2/X/server/include . +.xH 2 Summary +.LP +A makefile is made of four types of lines: +.RS +.IP \(bu 2 +Dependency lines +.IP \(bu 2 +Creation commands +.IP \(bu 2 +Variable assignments +.IP \(bu 2 +Comments, include statements and conditional directives +.RE +.LP +A dependency line is a list of one or more targets, an operator +.CW : ', (` +.CW :: ', ` +or +.CW ! '), ` +and a list of zero or more sources. Sources may contain wildcards and +certain local variables. +.LP +A creation command is a regular shell command preceded by a tab. In +addition, if the first two characters after the tab (and other +whitespace) are a combination of +.CW @ ' ` +or +.CW - ', ` +PMake will cause the command to not be printed (if the character is +.CW @ ') ` +or errors from it to be ignored (if +.CW - '). ` +A blank line, dependency line or variable assignment terminates a +creation script. There may be only one creation script for each target +with a +.CW : ' ` +or +.CW ! ' ` +operator. +.LP +Variables are places to store text. They may be unconditionally +assigned-to using the +.CW = ' ` +.Ix 0 ref = +.Ix 0 ref variable assignment +operator, appended-to using the +.CW += ' ` +.Ix 0 ref += +.Ix 0 ref variable assignment appended +operator, conditionally (if the variable is undefined) assigned-to +with the +.CW ?= ' ` +.Ix 0 ref ?= +.Ix 0 ref variable assignment conditional +operator, and assigned-to with variable expansion with the +.CW := ' ` +.Ix 0 ref := +.Ix 0 ref variable assignment expanded +operator. The output of a shell command may be assigned to a variable +using the +.CW != ' ` +.Ix 0 ref != +.Ix 0 ref variable assignment shell-output +operator. Variables may be expanded (their value inserted) by enclosing +their name in parentheses or curly braces, preceded by a dollar sign. +A dollar sign may be escaped with another dollar sign. Variables are +not expanded if PMake doesn't know about them. There are seven local +variables: +.CW .TARGET , +.CW .ALLSRC , +.CW .OODATE , +.CW .PREFIX , +.CW .IMPSRC , +.CW .ARCHIVE , +and +.CW .MEMBER . +Four of them +.CW .TARGET , ( +.CW .PREFIX , +.CW .ARCHIVE , +and +.CW .MEMBER ) +may be used to specify ``dynamic sources.'' +.Ix 0 ref "dynamic source" +.Ix 0 ref source dynamic +Variables are good. Know them. Love them. Live them. +.LP +Debugging of makefiles is best accomplished using the +.B \-n , +.B "\-d m" , +and +.B "\-p 2" +flags. +.xH 2 Exercises +.ce +\s+4\fBTBA\fP\s0 +.xH 1 Short-cuts and Other Nice Things +.LP +Based on what I've told you so far, you may have gotten the impression +that PMake is just a way of storing away commands and making sure you +don't forget to compile something. Good. That's just what it is. +However, the ways I've described have been inelegant, at best, and +painful, at worst. +This chapter contains things that make the +writing of makefiles easier and the makefiles themselves shorter and +easier to modify (and, occasionally, simpler). In this chapter, I +assume you are somewhat more +familiar with Sprite (or UNIX, if that's what you're using) than I did +in chapter 2, just so you're on your toes. +So without further ado... +.xH 2 Transformation Rules +.LP +As you know, a file's name consists of two parts: a base name, which +gives some hint as to the contents of the file, and a suffix, which +usually indicates the format of the file. +Over the years, as +.UX +has developed, +naming conventions, with regard to suffixes, have also developed that have +become almost as incontrovertible as Law. E.g. a file ending in +.CW .c +is assumed to contain C source code; one with a +.CW .o +suffix is assumed to be a compiled, relocatable object file that may +be linked into any program; a file with a +.CW .ms +suffix is usually a text file to be processed by Troff with the \-ms +macro package, and so on. +One of the best aspects of both Make and PMake comes from their +understanding of how the suffix of a file pertains to its contents and +their ability to do things with a file based solely on its suffix. This +ability comes from something known as a transformation rule. A +transformation rule specifies how to change a file with one suffix +into a file with another suffix. +.LP +A transformation rule looks much like a dependency line, except the +target is made of two known suffixes stuck together. Suffixes are made +known to PMake by placing them as sources on a dependency line whose +target is the special target +.CW .SUFFIXES . +E.g. +.DS +\&.SUFFIXES : .o .c +\&.c.o : + $(CC) $(CFLAGS) -c $(.IMPSRC) +.DE +The creation script attached to the target is used to transform a file with +the first suffix (in this case, +.CW .c ) +into a file with the second suffix (here, +.CW .o ). +In addition, the target inherits whatever attributes have been applied +to the transformation rule. +The simple rule given above says that to transform a C source file +into an object file, you compile it using +.CW cc +with the +.CW \-c +flag. +This rule is taken straight from the system makefile. Many +transformation rules (and suffixes) are defined there, and I refer you +to it for more examples (type +.CW "pmake -h" '' `` +to find out where it is). +.LP +There are several things to note about the transformation rule given +above: +.RS +.IP 1) +The +.CW .IMPSRC +variable. +.Ix 0 def variable local .IMPSRC +.Ix 0 def .IMPSRC +This variable is set to the ``implied source'' (the file from which +the target is being created; the one with the first suffix), which, in this +case, is the .c file. +.IP 2) +The +.CW CFLAGS +variable. Almost all of the transformation rules in the system +makefile are set up using variables that you can alter in your +makefile to tailor the rule to your needs. In this case, if you want +all your C files to be compiled with the +.B \-g +flag, to provide information for +.CW dbx , +you would set the +.CW CFLAGS +variable to contain +.CW -g +.CW "CFLAGS = -g" '') (`` +and PMake would take care of the rest. +.RE +.LP +To give you a quick example, the makefile in 2.3.4 +.Rm 3 2.3.4 +could be changed to this: +.DS +OBJS = a.o b.o c.o +program : $(OBJS) + $(CC) -o $(.TARGET) $(.ALLSRC) +$(OBJS) : defs.h +.DE +The transformation rule I gave above takes the place of the 6 lines\** +.FS +This is also somewhat cleaner, I think, than the dynamic source +solution presented in 2.6 +.FE +.Rm 4 2.6 +.DS +a.o : a.c + cc -c a.c +b.o : b.c + cc -c b.c +c.o : c.c + cc -c c.c +.DE +.LP +Now you may be wondering about the dependency between the +.CW .o +and +.CW .c +files \*- it's not mentioned anywhere in the new makefile. This is +because it isn't needed: one of the effects of applying a +transformation rule is the target comes to depend on the implied +source. That's why it's called the implied +.I source . +.LP +For a more detailed example. Say you have a makefile like this: +.DS +a.out : a.o b.o + $(CC) $(.ALLSRC) +.DE +and a directory set up like this: +.DS +total 4 +-rw-rw-r-- 1 deboor 34 Sep 7 00:43 Makefile +-rw-rw-r-- 1 deboor 119 Oct 3 19:39 a.c +-rw-rw-r-- 1 deboor 201 Sep 7 00:43 a.o +-rw-rw-r-- 1 deboor 69 Sep 7 00:43 b.c +.DE +While just typing +.CW pmake '' `` +will do the right thing, it's much more informative to type +.CW "pmake -d s" ''. `` +This will show you what PMake is up to as it processes the files. In +this case, PMake prints the following: +.DS +Suff_FindDeps (a.out) + using existing source a.o + applying .o -> .out to "a.o" +Suff_FindDeps (a.o) + trying a.c...got it + applying .c -> .o to "a.c" +Suff_FindDeps (b.o) + trying b.c...got it + applying .c -> .o to "b.c" +Suff_FindDeps (a.c) + trying a.y...not there + trying a.l...not there + trying a.c,v...not there + trying a.y,v...not there + trying a.l,v...not there +Suff_FindDeps (b.c) + trying b.y...not there + trying b.l...not there + trying b.c,v...not there + trying b.y,v...not there + trying b.l,v...not there +--- a.o --- +cc -c a.c +--- b.o --- +cc -c b.c +--- a.out --- +cc a.o b.o +.DE +.LP +.CW Suff_FindDeps +is the name of a function in PMake that is called to check for implied +sources for a target using transformation rules. +The transformations it tries are, naturally +enough, limited to the ones that have been defined (a transformation +may be defined multiple times, by the way, but only the most recent +one will be used). You will notice, however, that there is a definite +order to the suffixes that are tried. This order is set by the +relative positions of the suffixes on the +.CW .SUFFIXES +line \*- the earlier a suffix appears, the earlier it is checked as +the source of a transformation. Once a suffix has been defined, the +only way to change its position in the pecking order is to remove all +the suffixes (by having a +.CW .SUFFIXES +dependency line with no sources) and redefine them in the order you +want. (Previously-defined transformation rules will be automatically +redefined as the suffixes they involve are re-entered.) +.LP +Another way to affect the search order is to make the dependency +explicit. In the above example, +.CW a.out +depends on +.CW a.o +and +.CW b.o . +Since a transformation exists from +.CW .o +to +.CW .out , +PMake uses that, as indicated by the +.CW "using existing source a.o" '' `` +message. +.LP +The search for a transformation starts from the suffix of the target +and continues through all the defined transformations, in the order +dictated by the suffix ranking, until an existing file with the same +base (the target name minus the suffix and any leading directories) is +found. At that point, one or more transformation rules will have been +found to change the one existing file into the target. +.LP +For example, ignoring what's in the system makefile for now, say you +have a makefile like this: +.DS +\&.SUFFIXES : .out .o .c .y .l +\&.l.c : + lex $(.IMPSRC) + mv lex.yy.c $(.TARGET) +\&.y.c : + yacc $(.IMPSRC) + mv y.tab.c $(.TARGET) +\&.c.o : + cc -c $(.IMPSRC) +\&.o.out : + cc -o $(.TARGET) $(.IMPSRC) +.DE +and the single file +.CW jive.l . +If you were to type +.CW "pmake -rd ms jive.out" ,'' `` +you would get the following output for +.CW jive.out : +.DS +Suff_FindDeps (jive.out) + trying jive.o...not there + trying jive.c...not there + trying jive.y...not there + trying jive.l...got it + applying .l -> .c to "jive.l" + applying .c -> .o to "jive.c" + applying .o -> .out to "jive.o" +.DE +and this is why: PMake starts with the target +.CW jive.out , +figures out its suffix +.CW .out ) ( +and looks for things it can transform to a +.CW .out +file. In this case, it only finds +.CW .o , +so it looks for the file +.CW jive.o . +It fails to find it, so it looks for transformations into a +.CW .o +file. Again it has only one choice: +.CW .c . +So it looks for +.CW jive.c +and, as you know, fails to find it. At this point it has two choices: +it can create the +.CW .c +file from either a +.CW .y +file or a +.CW .l +file. Since +.CW .y +came first on the +.CW .SUFFIXES +line, it checks for +.CW jive.y +first, but can't find it, so it looks for +.CW jive.l +and, lo and behold, there it is. +At this point, it has defined a transformation path as follows: +.CW .l +\(-> +.CW .c +\(-> +.CW .o +\(-> +.CW .out +and applies the transformation rules accordingly. For completeness, +and to give you a better idea of what PMake actually did with this +three-step transformation, this is what PMake printed for the rest of +the process: +.DS +Suff_FindDeps (jive.o) + using existing source jive.c + applying .c -> .o to "jive.c" +Suff_FindDeps (jive.c) + using existing source jive.l + applying .l -> .c to "jive.l" +Suff_FindDeps (jive.l) +Examining jive.l...modified 17:16:01 Oct 4, 1987...up-to-date +Examining jive.c...non-existent...out-of-date +--- jive.c --- +lex jive.l +\&.\|.\|. meaningless lex output deleted .\|.\|. +mv lex.yy.c jive.c +Examining jive.o...non-existent...out-of-date +--- jive.o --- +cc -c jive.c +Examining jive.out...non-existent...out-of-date +--- jive.out --- +cc -o jive.out jive.o +.DE +.LP +One final question remains: what does PMake do with targets that have +no known suffix? PMake simply pretends it actually has a known suffix +and searches for transformations accordingly. +The suffix it chooses is the source for the +.CW .NULL +.Ix 0 ref .NULL +target mentioned later. In the system makefile, +.CW .out +is chosen as the ``null suffix'' +.Ix 0 def suffix null +.Ix 0 def "null suffix" +because most people use PMake to create programs. You are, however, +free and welcome to change it to a suffix of your own choosing. +The null suffix is ignored, however, when PMake is in compatibility +mode (see chapter 4). +.xH 2 Including Other Makefiles +.Ix 0 def makefile inclusion +.Rd 2 +.LP +Just as for programs, it is often useful to extract certain parts of a +makefile into another file and just include it in other makefiles +somehow. Many compilers allow you say something like +.DS +#include "defs.h" +.DE +to include the contents of +.CW defs.h +in the source file. PMake allows you to do the same thing for +makefiles, with the added ability to use variables in the filenames. +An include directive in a makefile looks either like this: +.DS +#include <file> +.DE +or this +.DS +#include "file" +.DE +The difference between the two is where PMake searches for the file: +the first way, PMake will look for +the file only in the system makefile directory (or directories) +(to find out what that directory is, give PMake the +.B \-h +flag). +.Ix 0 ref flags -h +The system makefile directory search path can be overridden via the +.B \-m +option. +.Ix 0 ref flags -m +For files in double-quotes, the search is more complex: +.RS +.IP 1) +The directory of the makefile that's including the file. +.IP 2) +The current directory (the one in which you invoked PMake). +.IP 3) +The directories given by you using +.B \-I +flags, in the order in which you gave them. +.IP 4) +Directories given by +.CW .PATH +dependency lines (see chapter 4). +.IP 5) +The system makefile directory. +.RE +.LP +in that order. +.LP +You are free to use PMake variables in the filename\*-PMake will +expand them before searching for the file. You must specify the +searching method with either angle brackets or double-quotes +.I outside +of a variable expansion. I.e. the following +.DS +SYSTEM = <command.mk> + +#include $(SYSTEM) +.DE +won't work. +.xH 2 Saving Commands +.LP +.Ix 0 def ... +There may come a time when you will want to save certain commands to +be executed when everything else is done. For instance: you're +making several different libraries at one time and you want to create the +members in parallel. Problem is, +.CW ranlib +is another one of those programs that can't be run more than once in +the same directory at the same time (each one creates a file called +.CW __.SYMDEF +into which it stuffs information for the linker to use. Two of them +running at once will overwrite each other's file and the result will +be garbage for both parties). You might want a way to save the ranlib +commands til the end so they can be run one after the other, thus +keeping them from trashing each other's file. PMake allows you to do +this by inserting an ellipsis (``.\|.\|.'') as a command between +commands to be run at once and those to be run later. +.LP +So for the +.CW ranlib +case above, you might do this: +.Rd 5 +.DS +lib1.a : $(LIB1OBJS) + rm -f $(.TARGET) + ar cr $(.TARGET) $(.ALLSRC) + ... + ranlib $(.TARGET) + +lib2.a : $(LIB2OBJS) + rm -f $(.TARGET) + ar cr $(.TARGET) $(.ALLSRC) + ... + ranlib $(.TARGET) +.DE +.Ix 0 ref variable local .TARGET +.Ix 0 ref variable local .ALLSRC +This would save both +.DS +ranlib $(.TARGET) +.DE +commands until the end, when they would run one after the other +(using the correct value for the +.CW .TARGET +variable, of course). +.LP +Commands saved in this manner are only executed if PMake manages to +re-create everything without an error. +.xH 2 Target Attributes +.LP +PMake allows you to give attributes to targets by means of special +sources. Like everything else PMake uses, these sources begin with a +period and are made up of all upper-case letters. There are various +reasons for using them, and I will try to give examples for most of +them. Others you'll have to find uses for yourself. Think of it as ``an +exercise for the reader.'' By placing one (or more) of these as a source on a +dependency line, you are ``marking the target(s) with that +attribute.'' That's just the way I phrase it, so you know. +.LP +Any attributes given as sources for a transformation rule are applied +to the target of the transformation rule when the rule is applied. +.Ix 0 def attributes +.Ix 0 ref source +.Ix 0 ref target +.nr pw 12 +.IP .DONTCARE \n(pw +.Ix 0 def attributes .DONTCARE +.Ix 0 def .DONTCARE +If a target is marked with this attribute and PMake can't figure out +how to create it, it will ignore this fact and assume the file isn't +really needed or actually exists and PMake just can't find it. This may prove +wrong, but the error will be noted later on, not when PMake tries to create +the target so marked. This attribute also prevents PMake from +attempting to touch the target if it is given the +.B \-t +flag. +.Ix 0 ref flags -t +.IP .EXEC \n(pw +.Ix 0 def attributes .EXEC +.Ix 0 def .EXEC +This attribute causes its shell script to be executed while having no +effect on targets that depend on it. This makes the target into a sort +of subroutine. An example. Say you have some LISP files that need to +be compiled and loaded into a LISP process. To do this, you echo LISP +commands into a file and execute a LISP with this file as its input +when everything's done. Say also that you have to load other files +from another system before you can compile your files and further, +that you don't want to go through the loading and dumping unless one +of +.I your +files has changed. Your makefile might look a little bit +like this (remember, this is an educational example, and don't worry +about the +.CW COMPILE +rule, all will soon become clear, grasshopper): +.DS +system : init a.fasl b.fasl c.fasl + for i in $(.ALLSRC); + do + echo -n '(load "' >> input + echo -n ${i} >> input + echo '")' >> input + done + echo '(dump "$(.TARGET)")' >> input + lisp < input + +a.fasl : a.l init COMPILE +b.fasl : b.l init COMPILE +c.fasl : c.l init COMPILE +COMPILE : .USE + echo '(compile "$(.ALLSRC)")' >> input +init : .EXEC + echo '(load-system)' > input +.DE +.Ix 0 ref .USE +.Ix 0 ref attributes .USE +.Ix 0 ref variable local .ALLSRC +.IP "\&" +.CW .EXEC +sources, don't appear in the local variables of targets that depend on +them (nor are they touched if PMake is given the +.B \-t +flag). +.Ix 0 ref flags -t +Note that all the rules, not just that for +.CW system , +include +.CW init +as a source. This is because none of the other targets can be made +until +.CW init +has been made, thus they depend on it. +.IP .EXPORT \n(pw +.Ix 0 def attributes .EXPORT +.Ix 0 def .EXPORT +This is used to mark those targets whose creation should be sent to +another machine if at all possible. This may be used by some +exportation schemes if the exportation is expensive. You should ask +your system administrator if it is necessary. +.IP .EXPORTSAME \n(pw +.Ix 0 def attributes .EXPORTSAME +.Ix 0 def .EXPORTSAME +Tells the export system that the job should be exported to a machine +of the same architecture as the current one. Certain operations (e.g. +running text through +.CW nroff ) +can be performed the same on any architecture (CPU and +operating system type), while others (e.g. compiling a program with +.CW cc ) +must be performed on a machine with the same architecture. Not all +export systems will support this attribute. +.IP .IGNORE \n(pw +.Ix 0 def attributes .IGNORE +.Ix 0 def .IGNORE attribute +Giving a target the +.CW .IGNORE +attribute causes PMake to ignore errors from any of the target's commands, as +if they all had `\-' before them. +.IP .INVISIBLE \n(pw +.Ix 0 def attributes .INVISIBLE +.Ix 0 def .INVISIBLE +This allows you to specify one target as a source for another without +the one affecting the other's local variables. Useful if, say, you +have a makefile that creates two programs, one of which is used to +create the other, so it must exist before the other is created. You +could say +.DS +prog1 : $(PROG1OBJS) prog2 MAKEINSTALL +prog2 : $(PROG2OBJS) .INVISIBLE MAKEINSTALL +.DE +where +.CW MAKEINSTALL +is some complex .USE rule (see below) that depends on the +.Ix 0 ref .USE +.CW .ALLSRC +variable containing the right things. Without the +.CW .INVISIBLE +attribute for +.CW prog2 , +the +.CW MAKEINSTALL +rule couldn't be applied. This is not as useful as it should be, and +the semantics may change (or the whole thing go away) in the +not-too-distant future. +.IP .JOIN \n(pw +.Ix 0 def attributes .JOIN +.Ix 0 def .JOIN +This is another way to avoid performing some operations in parallel +while permitting everything else to be done so. Specifically it +forces the target's shell script to be executed only if one or more of the +sources was out-of-date. In addition, the target's name, +in both its +.CW .TARGET +variable and all the local variables of any target that depends on it, +is replaced by the value of its +.CW .ALLSRC +variable. +As an example, suppose you have a program that has four libraries that +compile in the same directory along with, and at the same time as, the +program. You again have the problem with +.CW ranlib +that I mentioned earlier, only this time it's more severe: you +can't just put the ranlib off to the end since the program +will need those libraries before it can be re-created. You can do +something like this: +.DS +program : $(OBJS) libraries + cc -o $(.TARGET) $(.ALLSRC) + +libraries : lib1.a lib2.a lib3.a lib4.a .JOIN + ranlib $(.OODATE) +.DE +.Ix 0 ref variable local .TARGET +.Ix 0 ref variable local .ALLSRC +.Ix 0 ref variable local .OODATE +.Ix 0 ref .TARGET +.Ix 0 ref .ALLSRC +.Ix 0 ref .OODATE +In this case, PMake will re-create the +.CW $(OBJS) +as necessary, along with +.CW lib1.a , +.CW lib2.a , +.CW lib3.a +and +.CW lib4.a . +It will then execute +.CW ranlib +on any library that was changed and set +.CW program 's +.CW .ALLSRC +variable to contain what's in +.CW $(OBJS) +followed by +.CW "lib1.a lib2.a lib3.a lib4.a" .'' `` +In case you're wondering, it's called +.CW .JOIN +because it joins together different threads of the ``input graph'' at +the target marked with the attribute. +Another aspect of the .JOIN attribute is it keeps the target from +being created if the +.B \-t +flag was given. +.Ix 0 ref flags -t +.IP .MAKE \n(pw +.Ix 0 def attributes .MAKE +.Ix 0 def .MAKE +The +.CW .MAKE +attribute marks its target as being a recursive invocation of PMake. +This forces PMake to execute the script associated with the target (if +it's out-of-date) even if you gave the +.B \-n +or +.B \-t +flag. By doing this, you can start at the top of a system and type +.DS +pmake -n +.DE +and have it descend the directory tree (if your makefiles are set up +correctly), printing what it would have executed if you hadn't +included the +.B \-n +flag. +.IP .NOEXPORT \n(pw +.Ix 0 def attributes .NOEXPORT +.Ix 0 def .NOEXPORT attribute +If possible, PMake will attempt to export the creation of all targets to +another machine (this depends on how PMake was configured). Sometimes, +the creation is so simple, it is pointless to send it to another +machine. If you give the target the +.CW .NOEXPORT +attribute, it will be run locally, even if you've given PMake the +.B "\-L 0" +flag. +.IP .NOTMAIN \n(pw +.Ix 0 def attributes .NOTMAIN +.Ix 0 def .NOTMAIN +Normally, if you do not specify a target to make in any other way, +PMake will take the first target on the first dependency line of a +makefile as the target to create. That target is known as the ``Main +Target'' and is labeled as such if you print the dependencies out +using the +.B \-p +flag. +.Ix 0 ref flags -p +Giving a target this attribute tells PMake that the target is +definitely +.I not +the Main Target. +This allows you to place targets in an included makefile and +have PMake create something else by default. +.IP .PRECIOUS \n(pw +.Ix 0 def attributes .PRECIOUS +.Ix 0 def .PRECIOUS attribute +When PMake is interrupted (you type control-C at the keyboard), it +will attempt to clean up after itself by removing any half-made +targets. If a target has the +.CW .PRECIOUS +attribute, however, PMake will leave it alone. An additional side +effect of the `::' operator is to mark the targets as +.CW .PRECIOUS . +.Ix 0 ref operator double-colon +.Ix 0 ref :: +.IP .SILENT \n(pw +.Ix 0 def attributes .SILENT +.Ix 0 def .SILENT attribute +Marking a target with this attribute keeps its commands from being +printed when they're executed, just as if they had an `@' in front of them. +.IP .USE \n(pw +.Ix 0 def attributes .USE +.Ix 0 def .USE +By giving a target this attribute, you turn it into PMake's equivalent +of a macro. When the target is used as a source for another target, +the other target acquires the commands, sources and attributes (except +.CW .USE ) +of the source. +If the target already has commands, the +.CW .USE +target's commands are added to the end. If more than one .USE-marked +source is given to a target, the rules are applied sequentially. +.IP "\&" \n(pw +The typical .USE rule (as I call them) will use the sources of the +target to which it is applied (as stored in the +.CW .ALLSRC +variable for the target) as its ``arguments,'' if you will. +For example, you probably noticed that the commands for creating +.CW lib1.a +and +.CW lib2.a +in the example in section 3.3 +.Rm 5 3.3 +were exactly the same. You can use the +.CW .USE +attribute to eliminate the repetition, like so: +.DS +lib1.a : $(LIB1OBJS) MAKELIB +lib2.a : $(LIB2OBJS) MAKELIB + +MAKELIB : .USE + rm -f $(.TARGET) + ar cr $(.TARGET) $(.ALLSRC) + ... + ranlib $(.TARGET) +.DE +.Ix 0 ref variable local .TARGET +.Ix 0 ref variable local .ALLSRC +.IP "\&" \n(pw +Several system makefiles (not to be confused with The System Makefile) +make use of these .USE rules to make your +life easier (they're in the default, system makefile directory...take a look). +Note that the .USE rule source itself +.CW MAKELIB ) ( +does not appear in any of the targets's local variables. +There is no limit to the number of times I could use the +.CW MAKELIB +rule. If there were more libraries, I could continue with +.CW "lib3.a : $(LIB3OBJS) MAKELIB" '' `` +and so on and so forth. +.xH 2 Special Targets +.LP +As there were in Make, so there are certain targets that have special +meaning to PMake. When you use one on a dependency line, it is the +only target that may appear on the left-hand-side of the operator. +.Ix 0 ref target +.Ix 0 ref operator +As for the attributes and variables, all the special targets +begin with a period and consist of upper-case letters only. +I won't describe them all in detail because some of them are rather +complex and I'll describe them in more detail than you'll want in +chapter 4. +The targets are as follows: +.nr pw 10 +.IP .BEGIN \n(pw +.Ix 0 def .BEGIN +Any commands attached to this target are executed before anything else +is done. You can use it for any initialization that needs doing. +.IP .DEFAULT \n(pw +.Ix 0 def .DEFAULT +This is sort of a .USE rule for any target (that was used only as a +source) that PMake can't figure out any other way to create. It's only +``sort of'' a .USE rule because only the shell script attached to the +.CW .DEFAULT +target is used. The +.CW .IMPSRC +variable of a target that inherits +.CW .DEFAULT 's +commands is set to the target's own name. +.Ix 0 ref .IMPSRC +.Ix 0 ref variable local .IMPSRC +.IP .END \n(pw +.Ix 0 def .END +This serves a function similar to +.CW .BEGIN , +in that commands attached to it are executed once everything has been +re-created (so long as no errors occurred). It also serves the extra +function of being a place on which PMake can hang commands you put off +to the end. Thus the script for this target will be executed before +any of the commands you save with the ``.\|.\|.''. +.Ix 0 ref ... +.IP .EXPORT \n(pw +The sources for this target are passed to the exportation system compiled +into PMake. Some systems will use these sources to configure +themselves. You should ask your system administrator about this. +.IP .IGNORE \n(pw +.Ix 0 def .IGNORE target +.Ix 0 ref .IGNORE attribute +.Ix 0 ref attributes .IGNORE +This target marks each of its sources with the +.CW .IGNORE +attribute. If you don't give it any sources, then it is like +giving the +.B \-i +flag when you invoke PMake \*- errors are ignored for all commands. +.Ix 0 ref flags -i +.IP .INCLUDES \n(pw +.Ix 0 def .INCLUDES target +.Ix 0 def variable global .INCLUDES +.Ix 0 def .INCLUDES variable +The sources for this target are taken to be suffixes that indicate a +file that can be included in a program source file. +The suffix must have already been declared with +.CW .SUFFIXES +(see below). +Any suffix so marked will have the directories on its search path +(see +.CW .PATH , +below) placed in the +.CW .INCLUDES +variable, each preceded by a +.B \-I +flag. This variable can then be used as an argument for the compiler +in the normal fashion. The +.CW .h +suffix is already marked in this way in the system makefile. +.Ix 0 ref makefile system +E.g. if you have +.DS +\&.SUFFIXES : .bitmap +\&.PATH.bitmap : /usr/local/X/lib/bitmaps +\&.INCLUDES : .bitmap +.DE +PMake will place +.CW "-I/usr/local/X/lib/bitmaps" '' `` +in the +.CW .INCLUDES +variable and you can then say +.DS +cc $(.INCLUDES) -c xprogram.c +.DE +(Note: the +.CW .INCLUDES +variable is not actually filled in until the entire makefile has been read.) +.IP .INTERRUPT \n(pw +.Ix 0 def .INTERRUPT +When PMake is interrupted, +it will execute the commands in the script for this target, if it +exists. +.IP .LIBS \n(pw +.Ix 0 def .LIBS target +.Ix 0 def .LIBS variable +.Ix 0 def variable global .LIBS +This does for libraries what +.CW .INCLUDES +does for include files, except the flag used is +.B \-L , +as required by those linkers that allow you to tell them where to find +libraries. The variable used is +.CW .LIBS . +Be forewarned that PMake may not have been compiled to do this if the +linker on your system doesn't accept the +.B \-L +flag, though the +.CW .LIBS +variable will always be defined once the makefile has been read. +.IP .MAIN \n(pw +.Ix 0 def .MAIN +If you didn't give a target (or targets) to create when you invoked +PMake, it will take the sources of this target as the targets to +create. +.IP .MAKEFLAGS \n(pw +.Ix 0 def .MAKEFLAGS target +This target provides a way for you to always specify flags for PMake +when the makefile is used. The flags are just as they would be typed +to the shell (except you can't use shell variables unless they're in +the environment), +though the +.B \-f +and +.B \-r +flags have no effect. +.IP .NULL \n(pw +.Ix 0 def .NULL +.Ix 0 ref suffix null +.Ix 0 ref "null suffix" +This allows you to specify what suffix PMake should pretend a file has +if, in fact, it has no known suffix. Only one suffix may be so +designated. The last source on the dependency line is the suffix that +is used (you should, however, only give one suffix.\|.\|.). +.IP .PATH \n(pw +.Ix 0 def .PATH +If you give sources for this target, PMake will take them as +directories in which to search for files it cannot find in the current +directory. If you give no sources, it will clear out any directories +added to the search path before. Since the effects of this all get +very complex, I'll leave it til chapter four to give you a complete +explanation. +.IP .PATH\fIsuffix\fP \n(pw +.Ix 0 ref .PATH +This does a similar thing to +.CW .PATH , +but it does it only for files with the given suffix. The suffix must +have been defined already. Look at +.B "Search Paths" +(section 4.1) +.Rm 6 4.1 +for more information. +.IP .PRECIOUS \n(pw +.Ix 0 def .PRECIOUS target +.Ix 0 ref .PRECIOUS attribute +.Ix 0 ref attributes .PRECIOUS +Similar to +.CW .IGNORE , +this gives the +.CW .PRECIOUS +attribute to each source on the dependency line, unless there are no +sources, in which case the +.CW .PRECIOUS +attribute is given to every target in the file. +.IP .RECURSIVE \n(pw +.Ix 0 def .RECURSIVE +.Ix 0 ref attributes .MAKE +.Ix 0 ref .MAKE +This target applies the +.CW .MAKE +attribute to all its sources. It does nothing if you don't give it any sources. +.IP .SHELL \n(pw +.Ix 0 def .SHELL +PMake is not constrained to only using the Bourne shell to execute +the commands you put in the makefile. You can tell it some other shell +to use with this target. Check out +.B "A Shell is a Shell is a Shell" +(section 4.4) +.Rm 7 4.4 +for more information. +.IP .SILENT \n(pw +.Ix 0 def .SILENT target +.Ix 0 ref .SILENT attribute +.Ix 0 ref attributes .SILENT +When you use +.CW .SILENT +as a target, it applies the +.CW .SILENT +attribute to each of its sources. If there are no sources on the +dependency line, then it is as if you gave PMake the +.B \-s +flag and no commands will be echoed. +.IP .SUFFIXES \n(pw +.Ix 0 def .SUFFIXES +This is used to give new file suffixes for PMake to handle. Each +source is a suffix PMake should recognize. If you give a +.CW .SUFFIXES +dependency line with no sources, PMake will forget about all the +suffixes it knew (this also nukes the null suffix). +For those targets that need to have suffixes defined, this is how you do it. +.LP +In addition to these targets, a line of the form +.DS +\fIattribute\fP : \fIsources\fP +.DE +applies the +.I attribute +to all the targets listed as +.I sources . +.xH 2 Modifying Variable Expansion +.LP +.Ix 0 def variable expansion modified +.Ix 0 ref variable expansion +.Ix 0 def variable modifiers +Variables need not always be expanded verbatim. PMake defines several +modifiers that may be applied to a variable's value before it is +expanded. You apply a modifier by placing it after the variable name +with a colon between the two, like so: +.DS +${\fIVARIABLE\fP:\fImodifier\fP} +.DE +Each modifier is a single character followed by something specific to +the modifier itself. +You may apply as many modifiers as you want \*- each one is applied to +the result of the previous and is separated from the previous by +another colon. +.LP +There are seven ways to modify a variable's expansion, most of which +come from the C shell variable modification characters: +.RS +.IP "M\fIpattern\fP" +.Ix 0 def :M +.Ix 0 def modifier match +This is used to select only those words (a word is a series of +characters that are neither spaces nor tabs) that match the given +.I pattern . +The pattern is a wildcard pattern like that used by the shell, where +.CW * +means 0 or more characters of any sort; +.CW ? +is any single character; +.CW [abcd] +matches any single character that is either `a', `b', `c' or `d' +(there may be any number of characters between the brackets); +.CW [0-9] +matches any single character that is between `0' and `9' (i.e. any +digit. This form may be freely mixed with the other bracket form), and +`\\' is used to escape any of the characters `*', `?', `[' or `:', +leaving them as regular characters to match themselves in a word. +For example, the system makefile +.CW <makedepend.mk> +uses +.CW "$(CFLAGS:M-[ID]*)" '' `` +to extract all the +.CW \-I +and +.CW \-D +flags that would be passed to the C compiler. This allows it to +properly locate include files and generate the correct dependencies. +.IP "N\fIpattern\fP" +.Ix 0 def :N +.Ix 0 def modifier nomatch +This is identical to +.CW :M +except it substitutes all words that don't match the given pattern. +.IP "S/\fIsearch-string\fP/\fIreplacement-string\fP/[g]" +.Ix 0 def :S +.Ix 0 def modifier substitute +Causes the first occurrence of +.I search-string +in the variable to be replaced by +.I replacement-string , +unless the +.CW g +flag is given at the end, in which case all occurrences of the string +are replaced. The substitution is performed on each word in the +variable in turn. If +.I search-string +begins with a +.CW ^ , +the string must match starting at the beginning of the word. If +.I search-string +ends with a +.CW $ , +the string must match to the end of the word (these two may be +combined to force an exact match). If a backslash precedes these two +characters, however, they lose their special meaning. Variable +expansion also occurs in the normal fashion inside both the +.I search-string +and the +.I replacement-string , +.B except +that a backslash is used to prevent the expansion of a +.CW $ , +not another dollar sign, as is usual. +Note that +.I search-string +is just a string, not a pattern, so none of the usual +regular-expression/wildcard characters have any special meaning save +.CW ^ +and +.CW $ . +In the replacement string, +the +.CW & +character is replaced by the +.I search-string +unless it is preceded by a backslash. +You are allowed to use any character except +colon or exclamation point to separate the two strings. This so-called +delimiter character may be placed in either string by preceding it +with a backslash. +.IP T +.Ix 0 def :T +.Ix 0 def modifier tail +Replaces each word in the variable expansion by its last +component (its ``tail''). For example, given +.DS +OBJS = ../lib/a.o b /usr/lib/libm.a +TAILS = $(OBJS:T) +.DE +the variable +.CW TAILS +would expand to +.CW "a.o b libm.a" .'' `` +.IP H +.Ix 0 def :H +.Ix 0 def modifier head +This is similar to +.CW :T , +except that every word is replaced by everything but the tail (the +``head''). Using the same definition of +.CW OBJS , +the string +.CW "$(OBJS:H)" '' `` +would expand to +.CW "../lib /usr/lib" .'' `` +Note that the final slash on the heads is removed and +anything without a head is replaced by the empty string. +.IP E +.Ix 0 def :E +.Ix 0 def modifier extension +.Ix 0 def modifier suffix +.Ix 0 ref suffix "variable modifier" +.CW :E +replaces each word by its suffix (``extension''). So +.CW "$(OBJS:E)" '' `` +would give you +.CW ".o .a" .'' `` +.IP R +.Ix 0 def :R +.Ix 0 def modifier root +.Ix 0 def modifier base +This replaces each word by everything but the suffix (the ``root'' of +the word). +.CW "$(OBJS:R)" '' `` +expands to `` +.CW "../lib/a b /usr/lib/libm" .'' +.RE +.LP +In addition, the System V style of substitution is also supported. +This looks like: +.DS +$(\fIVARIABLE\fP:\fIsearch-string\fP=\fIreplacement\fP) +.DE +It must be the last modifier in the chain. The search is anchored at +the end of each word, so only suffixes or whole words may be replaced. +.xH 2 More on Debugging +.xH 2 More Exercises +.IP (3.1) +You've got a set programs, each of which is created from its own +assembly-language source file (suffix +.CW .asm ). +Each program can be assembled into two versions, one with error-checking +code assembled in and one without. You could assemble them into files +with different suffixes +.CW .eobj \& ( +and +.CW .obj , +for instance), but your linker only understands files that end in +.CW .obj . +To top it all off, the final executables +.I must +have the suffix +.CW .exe . +How can you still use transformation rules to make your life easier +(Hint: assume the error-checking versions have +.CW ec +tacked onto their prefix)? +.IP (3.2) +Assume, for a moment or two, you want to perform a sort of +``indirection'' by placing the name of a variable into another one, +then you want to get the value of the first by expanding the second +somehow. Unfortunately, PMake doesn't allow constructs like +.DS I +$($(FOO)) +.DE +What do you do? Hint: no further variable expansion is performed after +modifiers are applied, thus if you cause a $ to occur in the +expansion, that's what will be in the result. +.xH 1 PMake for Gods +.LP +This chapter is devoted to those facilities in PMake that allow you to +do a great deal in a makefile with very little work, as well as do +some things you couldn't do in Make without a great deal of work (and +perhaps the use of other programs). The problem with these features, +is they must be handled with care, or you will end up with a mess. +.LP +Once more, I assume a greater familiarity with +.UX +or Sprite than I did in the previous two chapters. +.xH 2 Search Paths +.Rd 6 +.LP +PMake supports the dispersal of files into multiple directories by +allowing you to specify places to look for sources with +.CW .PATH +targets in the makefile. The directories you give as sources for these +targets make up a ``search path.'' Only those files used exclusively +as sources are actually sought on a search path, the assumption being +that anything listed as a target in the makefile can be created by the +makefile and thus should be in the current directory. +.LP +There are two types of search paths +in PMake: one is used for all types of files (including included +makefiles) and is specified with a plain +.CW .PATH +target (e.g. +.CW ".PATH : RCS" ''), `` +while the other is specific to a certain type of file, as indicated by +the file's suffix. A specific search path is indicated by immediately following +the +.CW .PATH +with the suffix of the file. For instance +.DS +\&.PATH.h : /sprite/lib/include /sprite/att/lib/include +.DE +would tell PMake to look in the directories +.CW /sprite/lib/include +and +.CW /sprite/att/lib/include +for any files whose suffix is +.CW .h . +.LP +The current directory is always consulted first to see if a file +exists. Only if it cannot be found there are the directories in the +specific search path, followed by those in the general search path, +consulted. +.LP +A search path is also used when expanding wildcard characters. If the +pattern has a recognizable suffix on it, the path for that suffix will +be used for the expansion. Otherwise the default search path is employed. +.LP +When a file is found in some directory other than the current one, all +local variables that would have contained the target's name +.CW .ALLSRC , ( +and +.CW .IMPSRC ) +will instead contain the path to the file, as found by PMake. +Thus if you have a file +.CW ../lib/mumble.c +and a makefile +.DS +\&.PATH.c : ../lib +mumble : mumble.c + $(CC) -o $(.TARGET) $(.ALLSRC) +.DE +the command executed to create +.CW mumble +would be +.CW "cc -o mumble ../lib/mumble.c" .'' `` +(As an aside, the command in this case isn't strictly necessary, since +it will be found using transformation rules if it isn't given. This is because +.CW .out +is the null suffix by default and a transformation exists from +.CW .c +to +.CW .out . +Just thought I'd throw that in.) +.LP +If a file exists in two directories on the same search path, the file +in the first directory on the path will be the one PMake uses. So if +you have a large system spread over many directories, it would behoove +you to follow a naming convention that avoids such conflicts. +.LP +Something you should know about the way search paths are implemented +is that each directory is read, and its contents cached, exactly once +\&\*- when it is first encountered \*- so any changes to the +directories while PMake is running will not be noted when searching +for implicit sources, nor will they be found when PMake attempts to +discover when the file was last modified, unless the file was created in the +current directory. While people have suggested that PMake should read +the directories each time, my experience suggests that the caching seldom +causes problems. In addition, not caching the directories slows things +down enormously because of PMake's attempts to apply transformation +rules through non-existent files \*- the number of extra file-system +searches is truly staggering, especially if many files without +suffixes are used and the null suffix isn't changed from +.CW .out . +.xH 2 Archives and Libraries +.LP +.UX +and Sprite allow you to merge files into an archive using the +.CW ar +command. Further, if the files are relocatable object files, you can +run +.CW ranlib +on the archive and get yourself a library that you can link into any +program you want. The main problem with archives is they double the +space you need to store the archived files, since there's one copy in +the archive and one copy out by itself. The problem with libraries is +you usually think of them as +.CW -lm +rather than +.CW /usr/lib/libm.a +and the linker thinks they're out-of-date if you so much as look at +them. +.LP +PMake solves the problem with archives by allowing you to tell it to +examine the files in the archives (so you can remove the individual +files without having to regenerate them later). To handle the problem +with libraries, PMake adds an additional way of deciding if a library +is out-of-date: +.IP \(bu 2 +If the table of contents is older than the library, or is missing, the +library is out-of-date. +.LP +A library is any target that looks like +.CW \-l name'' `` +or that ends in a suffix that was marked as a library using the +.CW .LIBS +target. +.CW .a +is so marked in the system makefile. +.LP +Members of an archive are specified as +``\fIarchive\fP(\fImember\fP[ \fImember\fP...])''. +Thus +.CW libdix.a(window.o) '' ``' +specifies the file +.CW window.o +in the archive +.CW libdix.a . +You may also use wildcards to specify the members of the archive. Just +remember that most the wildcard characters will only find +.I existing +files. +.LP +A file that is a member of an archive is treated specially. If the +file doesn't exist, but it is in the archive, the modification time +recorded in the archive is used for the file when determining if the +file is out-of-date. When figuring out how to make an archived member target +(not the file itself, but the file in the archive \*- the +\fIarchive\fP(\fImember\fP) target), special care is +taken with the transformation rules, as follows: +.IP \(bu 2 +\&\fIarchive\fP(\fImember\fP) is made to depend on \fImember\fP. +.IP \(bu 2 +The transformation from the \fImember\fP's suffix to the +\fIarchive\fP's suffix is applied to the \fIarchive\fP(\fImember\fP) target. +.IP \(bu 2 +The \fIarchive\fP(\fImember\fP)'s +.CW .TARGET +variable is set to the name of the \fImember\fP if \fImember\fP is +actually a target, or the path to the member file if \fImember\fP is +only a source. +.IP \(bu 2 +The +.CW .ARCHIVE +variable for the \fIarchive\fP(\fImember\fP) target is set to the name +of the \fIarchive\fP. +.Ix 0 def variable local .ARCHIVE +.Ix 0 def .ARCHIVE +.IP \(bu 2 +The +.CW .MEMBER +variable is set to the actual string inside the parentheses. In most +cases, this will be the same as the +.CW .TARGET +variable. +.Ix 0 def variable local .MEMBER +.Ix 0 def .MEMBER +.IP \(bu 2 +The \fIarchive\fP(\fImember\fP)'s place in the local variables of the +targets that depend on it is taken by the value of its +.CW .TARGET +variable. +.LP +Thus, a program library could be created with the following makefile: +.DS +\&.o.a : + ... + rm -f $(.TARGET:T) +OBJS = obj1.o obj2.o obj3.o +libprog.a : libprog.a($(OBJS)) + ar cru $(.TARGET) $(.OODATE) + ranlib $(.TARGET) +.DE +This will cause the three object files to be compiled (if the +corresponding source files were modified after the object file or, if +that doesn't exist, the archived object file), the out-of-date ones +archived in +.CW libprog.a , +a table of contents placed in the archive and the newly-archived +object files to be removed. +.LP +All this is used in the +.CW makelib.mk +system makefile to create a single library with ease. This makefile +looks like this: +.DS +.SM +# +# Rules for making libraries. The object files that make up the library +# are removed once they are archived. +# +# To make several libraries in parallel, you should define the variable +# "many_libraries". This will serialize the invocations of ranlib. +# +# To use, do something like this: +# +# OBJECTS = <files in the library> +# +# fish.a: fish.a($(OBJECTS)) MAKELIB +# +# + +#ifndef _MAKELIB_MK +_MAKELIB_MK = + +#include <po.mk> + +\&.po.a .o.a : + ... + rm -f $(.MEMBER) + +ARFLAGS ?= crl + +# +# Re-archive the out-of-date members and recreate the library's table of +# contents using ranlib. If many_libraries is defined, put the ranlib +# off til the end so many libraries can be made at once. +# +MAKELIB : .USE .PRECIOUS + ar $(ARFLAGS) $(.TARGET) $(.OODATE) +#ifndef no_ranlib +# ifdef many_libraries + ... +# endif /* many_libraries */ + ranlib $(.TARGET) +#endif /* no_ranlib */ + +#endif /* _MAKELIB_MK */ +.DE +.xH 2 On the Condition... +.Rd 1 +.LP +Like the C compiler before it, PMake allows you to configure the makefile, +based on the current environment, using conditional statements. A +conditional looks like this: +.DS +#if \fIboolean expression\fP +\fIlines\fP +#elif \fIanother boolean expression\fP +\fImore lines\fP +#else +\fIstill more lines\fP +#endif +.DE +They may be nested to a maximum depth of 30 and may occur anywhere +(except in a comment, of course). The +.CW # '' `` +must the very first character on the line. +.LP +Each +.I "boolean expression" +is made up of terms that look like function calls, the standard C +boolean operators +.CW && , +.CW || , +and +.CW ! , +and the standard relational operators +.CW == , +.CW != , +.CW > , +.CW >= , +.CW < , +and +.CW <= , +with +.CW == +and +.CW != +being overloaded to allow string comparisons as well. +.CW && +represents logical AND; +.CW || +is logical OR and +.CW ! +is logical NOT. The arithmetic and string operators take precedence +over all three of these operators, while NOT takes precedence over +AND, which takes precedence over OR. This precedence may be +overridden with parentheses, and an expression may be parenthesized to +your heart's content. Each term looks like a call on one of four +functions: +.nr pw 9 +.Ix 0 def make +.Ix 0 def conditional make +.Ix 0 def if make +.IP make \n(pw +The syntax is +.CW make( \fItarget\fP\c +.CW ) +where +.I target +is a target in the makefile. This is true if the given target was +specified on the command line, or as the source for a +.CW .MAIN +target (note that the sources for +.CW .MAIN +are only used if no targets were given on the command line). +.IP defined \n(pw +.Ix 0 def defined +.Ix 0 def conditional defined +.Ix 0 def if defined +The syntax is +.CW defined( \fIvariable\fP\c +.CW ) +and is true if +.I variable +is defined. Certain variables are defined in the system makefile that +identify the system on which PMake is being run. +.IP exists \n(pw +.Ix 0 def exists +.Ix 0 def conditional exists +.Ix 0 def if exists +The syntax is +.CW exists( \fIfile\fP\c +.CW ) +and is true if the file can be found on the global search path +(i.e. that defined by +.CW .PATH +targets, not by +.CW .PATH \fIsuffix\fP +targets). +.IP empty \n(pw +.Ix 0 def empty +.Ix 0 def conditional empty +.Ix 0 def if empty +This syntax is much like the others, except the string inside the +parentheses is of the same form as you would put between parentheses +when expanding a variable, complete with modifiers and everything. The +function returns true if the resulting string is empty (NOTE: an undefined +variable in this context will cause at the very least a warning +message about a malformed conditional, and at the worst will cause the +process to stop once it has read the makefile. If you want to check +for a variable being defined or empty, use the expression +.CW !defined( \fIvar\fP\c `` +.CW ") || empty(" \fIvar\fP\c +.CW ) '' +as the definition of +.CW || +will prevent the +.CW empty() +from being evaluated and causing an error, if the variable is +undefined). This can be used to see if a variable contains a given +word, for example: +.DS +#if !empty(\fIvar\fP:M\fIword\fP) +.DE +.LP +The arithmetic and string operators may only be used to test the value +of a variable. The lefthand side must contain the variable expansion, +while the righthand side contains either a string, enclosed in +double-quotes, or a number. The standard C numeric conventions (except +for specifying an octal number) apply to both sides. E.g. +.DS +#if $(OS) == 4.3 + +#if $(MACHINE) == "sun3" + +#if $(LOAD_ADDR) < 0xc000 +.DE +are all valid conditionals. In addition, the numeric value of a +variable can be tested as a boolean as follows: +.DS +#if $(LOAD) +.DE +would see if +.CW LOAD +contains a non-zero value and +.DS +#if !$(LOAD) +.DE +would test if +.CW LOAD +contains a zero value. +.LP +In addition to the bare +.CW #if ,'' `` +there are other forms that apply one of the first two functions to each +term. They are as follows: +.DS + ifdef \fRdefined\fP + ifndef \fR!defined\fP + ifmake \fRmake\fP + ifnmake \fR!make\fP +.DE +There are also the ``else if'' forms: +.CW elif , +.CW elifdef , +.CW elifndef , +.CW elifmake , +and +.CW elifnmake . +.LP +For instance, if you wish to create two versions of a program, one of which +is optimized (the production version) and the other of which is for debugging +(has symbols for dbx), you have two choices: you can create two +makefiles, one of which uses the +.CW \-g +flag for the compilation, while the other uses the +.CW \-O +flag, or you can use another target (call it +.CW debug ) +to create the debug version. The construct below will take care of +this for you. I have also made it so defining the variable +.CW DEBUG +(say with +.CW "pmake -D DEBUG" ) +will also cause the debug version to be made. +.DS +#if defined(DEBUG) || make(debug) +CFLAGS += -g +#else +CFLAGS += -O +#endif +.DE +There are, of course, problems with this approach. The most glaring +annoyance is that if you want to go from making a debug version to +making a production version, you have to remove all the object files, +or you will get some optimized and some debug versions in the same +program. Another annoyance is you have to be careful not to make two +targets that ``conflict'' because of some conditionals in the +makefile. For instance +.DS +#if make(print) +FORMATTER = ditroff -Plaser_printer +#endif +#if make(draft) +FORMATTER = nroff -Pdot_matrix_printer +#endif +.DE +would wreak havoc if you tried +.CW "pmake draft print" '' `` +since you would use the same formatter for each target. As I said, +this all gets somewhat complicated. +.xH 2 A Shell is a Shell is a Shell +.Rd 7 +.LP +In normal operation, the Bourne Shell (better known as +.CW sh '') `` +is used to execute the commands to re-create targets. PMake also allows you +to specify a different shell for it to use when executing these +commands. There are several things PMake must know about the shell you +wish to use. These things are specified as the sources for the +.CW .SHELL +.Ix 0 ref .SHELL +.Ix 0 ref target .SHELL +target by keyword, as follows: +.IP "\fBpath=\fP\fIpath\fP" +PMake needs to know where the shell actually resides, so it can +execute it. If you specify this and nothing else, PMake will use the +last component of the path and look in its table of the shells it +knows and use the specification it finds, if any. Use this if you just +want to use a different version of the Bourne or C Shell (yes, PMake knows +how to use the C Shell too). +.IP "\fBname=\fP\fIname\fP" +This is the name by which the shell is to be known. It is a single +word and, if no other keywords are specified (other than +.B path ), +it is the name by which PMake attempts to find a specification for +it (as mentioned above). You can use this if you would just rather use +the C Shell than the Bourne Shell +.CW ".SHELL: name=csh" '' (`` +will do it). +.IP "\fBquiet=\fP\fIecho-off command\fP" +As mentioned before, PMake actually controls whether commands are +printed by introducing commands into the shell's input stream. This +keyword, and the next two, control what those commands are. The +.B quiet +keyword is the command used to turn echoing off. Once it is turned +off, echoing is expected to remain off until the echo-on command is given. +.IP "\fBecho=\fP\fIecho-on command\fP" +The command PMake should give to turn echoing back on again. +.IP "\fBfilter=\fP\fIprinted echo-off command\fP" +Many shells will echo the echo-off command when it is given. This +keyword tells PMake in what format the shell actually prints the +echo-off command. Wherever PMake sees this string in the shell's +output, it will delete it and any following whitespace, up to and +including the next newline. See the example at the end of this section +for more details. +.IP "\fBechoFlag=\fP\fIflag to turn echoing on\fP" +Unless a target has been marked +.CW .SILENT , +PMake wants to start the shell running with echoing on. To do this, it +passes this flag to the shell as one of its arguments. If either this +or the next flag begins with a `\-', the flags will be passed to the +shell as separate arguments. Otherwise, the two will be concatenated +(if they are used at the same time, of course). +.IP "\fBerrFlag=\fP\fIflag to turn error checking on\fP" +Likewise, unless a target is marked +.CW .IGNORE , +PMake wishes error-checking to be on from the very start. To this end, +it will pass this flag to the shell as an argument. The same rules for +an initial `\-' apply as for the +.B echoFlag . +.IP "\fBcheck=\fP\fIcommand to turn error checking on\fP" +Just as for echo-control, error-control is achieved by inserting +commands into the shell's input stream. This is the command to make +the shell check for errors. It also serves another purpose if the +shell doesn't have error-control as commands, but I'll get into that +in a minute. Again, once error checking has been turned on, it is +expected to remain on until it is turned off again. +.IP "\fBignore=\fP\fIcommand to turn error checking off\fP" +This is the command PMake uses to turn error checking off. It has +another use if the shell doesn't do error-control, but I'll tell you +about that.\|.\|.\|now. +.IP "\fBhasErrCtl=\fP\fIyes or no\fP" +This takes a value that is either +.B yes +or +.B no . +Now you might think that the existence of the +.B check +and +.B ignore +keywords would be enough to tell PMake if the shell can do +error-control, but you'd be wrong. If +.B hasErrCtl +is +.B yes , +PMake uses the check and ignore commands in a straight-forward manner. +If this is +.B no , +however, their use is rather different. In this case, the check +command is used as a template, in which the string +.B %s +is replaced by the command that's about to be executed, to produce a +command for the shell that will echo the command to be executed. The +ignore command is also used as a template, again with +.B %s +replaced by the command to be executed, to produce a command that will +execute the command to be executed and ignore any error it returns. +When these strings are used as templates, you must provide newline(s) +.CW \en '') (`` +in the appropriate place(s). +.LP +The strings that follow these keywords may be enclosed in single or +double quotes (the quotes will be stripped off) and may contain the +usual C backslash-characters (\en is newline, \er is return, \eb is +backspace, \e' escapes a single-quote inside single-quotes, \e" +escapes a double-quote inside double-quotes). Now for an example. +.LP +This is actually the contents of the +.CW <shx.mk> +system makefile, and causes PMake to use the Bourne Shell in such a +way that each command is printed as it is executed. That is, if more +than one command is given on a line, each will be printed separately. +Similarly, each time the body of a loop is executed, the commands +within that loop will be printed, etc. The specification runs like +this: +.DS +# +# This is a shell specification to have the Bourne shell echo +# the commands just before executing them, rather than when it reads +# them. Useful if you want to see how variables are being expanded, etc. +# +\&.SHELL : path=/bin/sh \e + quiet="set -" \e + echo="set -x" \e + filter="+ set - " \e + echoFlag=x \e + errFlag=e \e + hasErrCtl=yes \e + check="set -e" \e + ignore="set +e" +.DE +.LP +It tells PMake the following: +.Bp +The shell is located in the file +.CW /bin/sh . +It need not tell PMake that the name of the shell is +.CW sh +as PMake can figure that out for itself (it's the last component of +the path). +.Bp +The command to stop echoing is +.CW "set -" . +.Bp +The command to start echoing is +.CW "set -x" . +.Bp +When the echo off command is executed, the shell will print +.CW "+ set - " +(The `+' comes from using the +.CW \-x +flag (rather than the +.CW \-v +flag PMake usually uses)). PMake will remove all occurrences of this +string from the output, so you don't notice extra commands you didn't +put there. +.Bp +The flag the Bourne Shell will take to start echoing in this way is +the +.CW \-x +flag. The Bourne Shell will only take its flag arguments concatenated +as its first argument, so neither this nor the +.B errFlag +specification begins with a \-. +.Bp +The flag to use to turn error-checking on from the start is +.CW \-e . +.Bp +The shell can turn error-checking on and off, and the commands to do +so are +.CW "set +e" +and +.CW "set -e" , +respectively. +.LP +I should note that this specification is for Bourne Shells that are +not part of Berkeley +.UX , +as shells from Berkeley don't do error control. You can get a similar +effect, however, by changing the last three lines to be: +.DS + hasErrCtl=no \e + check="echo \e"+ %s\e"\en" \e + ignore="sh -c '%s || exit 0\en" +.DE +.LP +This will cause PMake to execute the two commands +.DS +echo "+ \fIcmd\fP" +sh -c '\fIcmd\fP || true' +.DE +for each command for which errors are to be ignored. (In case you are +wondering, the thing for +.CW ignore +tells the shell to execute another shell without error checking on and +always exit 0, since the +.B || +causes the +.CW "exit 0" +to be executed only if the first command exited non-zero, and if the +first command exited zero, the shell will also exit zero, since that's +the last command it executed). +.xH 2 Compatibility +.Ix 0 ref compatibility +.LP +There are three (well, 3 \(12) levels of backwards-compatibility built +into PMake. Most makefiles will need none at all. Some may need a +little bit of work to operate correctly when run in parallel. Each +level encompasses the previous levels (e.g. +.B \-B +(one shell per command) implies +.B \-V ) +The three levels are described in the following three sections. +.xH 3 DEFCON 3 \*- Variable Expansion +.Ix 0 ref compatibility +.LP +As noted before, PMake will not expand a variable unless it knows of a +value for it. This can cause problems for makefiles that expect to +leave variables undefined except in special circumstances (e.g. if +more flags need to be passed to the C compiler or the output from a +text processor should be sent to a different printer). If the +variables are enclosed in curly braces +.CW ${PRINTER} ''), (`` +the shell will let them pass. If they are enclosed in parentheses, +however, the shell will declare a syntax error and the make will come +to a grinding halt. +.LP +You have two choices: change the makefile to define the variables +(their values can be overridden on the command line, since that's +where they would have been set if you used Make, anyway) or always give the +.B \-V +flag (this can be done with the +.CW .MAKEFLAGS +target, if you want). +.xH 3 DEFCON 2 \*- The Number of the Beast +.Ix 0 ref compatibility +.LP +Then there are the makefiles that expect certain commands, such as +changing to a different directory, to not affect other commands in a +target's creation script. You can solve this is either by going +back to executing one shell per command (which is what the +.B \-B +flag forces PMake to do), which slows the process down a good bit and +requires you to use semicolons and escaped newlines for shell constructs, or +by changing the makefile to execute the offending command(s) in a subshell +(by placing the line inside parentheses), like so: +.DS +install :: .MAKE + (cd src; $(.PMAKE) install) + (cd lib; $(.PMAKE) install) + (cd man; $(.PMAKE) install) +.DE +.Ix 0 ref operator double-colon +.Ix 0 ref variable global .PMAKE +.Ix 0 ref .PMAKE +.Ix 0 ref .MAKE +.Ix 0 ref attribute .MAKE +This will always execute the three makes (even if the +.B \-n +flag was given) because of the combination of the ``::'' operator and +the +.CW .MAKE +attribute. Each command will change to the proper directory to perform +the install, leaving the main shell in the directory in which it started. +.xH 3 "DEFCON 1 \*- Imitation is the Not the Highest Form of Flattery" +.Ix 0 ref compatibility +.LP +The final category of makefile is the one where every command requires +input, the dependencies are incompletely specified, or you simply +cannot create more than one target at a time, as mentioned earlier. In +addition, you may not have the time or desire to upgrade the makefile +to run smoothly with PMake. If you are the conservative sort, this is +the compatibility mode for you. It is entered either by giving PMake +the +.B \-M +flag (for Make), or by executing PMake as +.CW make .'' `` +In either case, PMake performs things exactly like Make (while still +supporting most of the nice new features PMake provides). This +includes: +.IP \(bu 2 +No parallel execution. +.IP \(bu 2 +Targets are made in the exact order specified by the makefile. The +sources for each target are made in strict left-to-right order, etc. +.IP \(bu 2 +A single Bourne shell is used to execute each command, thus the +shell's +.CW $$ +variable is useless, changing directories doesn't work across command +lines, etc. +.IP \(bu 2 +If no special characters exist in a command line, PMake will break the +command into words itself and execute the command directly, without +executing a shell first. The characters that cause PMake to execute a +shell are: +.CW # , +.CW = , +.CW | , +.CW ^ , +.CW ( , +.CW ) , +.CW { , +.CW } , +.CW ; , +.CW & , +.CW < , +.CW > , +.CW * , +.CW ? , +.CW [ , +.CW ] , +.CW : , +.CW $ , +.CW ` , +and +.CW \e . +You should notice that these are all the characters that are given +special meaning by the shell (except +.CW ' +and +.CW " , +which PMake deals with all by its lonesome). +.IP \(bu 2 +The use of the null suffix is turned off. +.Ix 0 ref "null suffix" +.Ix 0 ref suffix null +.xH 2 The Way Things Work +.LP +When PMake reads the makefile, it parses sources and targets into +nodes in a graph. The graph is directed only in the sense that PMake +knows which way is up. Each node contains not only links to all its +parents and children (the nodes that depend on it and those on which +it depends, respectively), but also a count of the number of its +children that have already been processed. +.LP +The most important thing to know about how PMake uses this graph is +that the traversal is breadth-first and occurs in two passes. +.LP +After PMake has parsed the makefile, it begins with the nodes the user +has told it to make (either on the command line, or via a +.CW .MAIN +target, or by the target being the first in the file not labeled with +the +.CW .NOTMAIN +attribute) placed in a queue. It continues to take the node off the +front of the queue, mark it as something that needs to be made, pass +the node to +.CW Suff_FindDeps +(mentioned earlier) to find any implicit sources for the node, and +place all the node's children that have yet to be marked at the end of +the queue. If any of the children is a +.CW .USE +rule, its attributes are applied to the parent, then its commands are +appended to the parent's list of commands and its children are linked +to its parent. The parent's unmade children counter is then decremented +(since the +.CW .USE +node has been processed). You will note that this allows a +.CW .USE +node to have children that are +.CW .USE +nodes and the rules will be applied in sequence. +If the node has no children, it is placed at the end of +another queue to be examined in the second pass. This process +continues until the first queue is empty. +.LP +At this point, all the leaves of the graph are in the examination +queue. PMake removes the node at the head of the queue and sees if it +is out-of-date. If it is, it is passed to a function that will execute +the commands for the node asynchronously. When the commands have +completed, all the node's parents have their unmade children counter +decremented and, if the counter is then 0, they are placed on the +examination queue. Likewise, if the node is up-to-date. Only those +parents that were marked on the downward pass are processed in this +way. Thus PMake traverses the graph back up to the nodes the user +instructed it to create. When the examination queue is empty and no +shells are running to create a target, PMake is finished. +.LP +Once all targets have been processed, PMake executes the commands +attached to the +.CW .END +target, either explicitly or through the use of an ellipsis in a shell +script. If there were no errors during the entire process but there +are still some targets unmade (PMake keeps a running count of how many +targets are left to be made), there is a cycle in the graph. PMake does +a depth-first traversal of the graph to find all the targets that +weren't made and prints them out one by one. +.xH 1 Answers to Exercises +.IP (3.1) +This is something of a trick question, for which I apologize. The +trick comes from the UNIX definition of a suffix, which PMake doesn't +necessarily share. You will have noticed that all the suffixes used in +this tutorial (and in UNIX in general) begin with a period +.CW .ms , ( +.CW .c , +etc.). Now, PMake's idea of a suffix is more like English's: it's the +characters at the end of a word. With this in mind, one possible +.Ix 0 def suffix +solution to this problem goes as follows: +.DS I +\&.SUFFIXES : ec.exe .exe ec.obj .obj .asm +ec.objec.exe .obj.exe : + link -o $(.TARGET) $(.IMPSRC) +\&.asmec.obj : + asm -o $(.TARGET) -DDO_ERROR_CHECKING $(.IMPSRC) +\&.asm.obj : + asm -o $(.TARGET) $(.IMPSRC) +.DE +.IP (3.2) +The trick to this one lies in the ``:='' variable-assignment operator +and the ``:S'' variable-expansion modifier. +.Ix 0 ref variable assignment expanded +.Ix 0 ref variable expansion modified +.Ix 0 ref modifier substitute +.Ix 0 ref :S +.Ix 0 ref := +Basically what you want is to take the pointer variable, so to speak, +and transform it into an invocation of the variable at which it +points. You might try something like +.DS I +$(PTR:S/^/\e$(/:S/$/)) +.DE +which places +.CW $( '' `` +at the front of the variable name and +.CW ) '' `` +at the end, thus transforming +.CW VAR ,'' `` +for example, into +.CW $(VAR) ,'' `` +which is just what we want. Unfortunately (as you know if you've tried +it), since, as it says in the hint, PMake does no further substitution +on the result of a modified expansion, that's \fIall\fP you get. The +solution is to make use of ``:='' to place that string into yet +another variable, then invoke the other variable directly: +.DS I +*PTR := $(PTR:S/^/\e$(/:S/$/)/) +.DE +You can then use +.CW $(*PTR) '' `` +to your heart's content. +.de Gp +.XP +\&\fB\\$1:\fP +.. +.xH 1 Glossary of Jargon +.Gp "attribute" +A property given to a target that causes PMake to treat it differently. +.Gp "command script" +The lines immediately following a dependency line that specify +commands to execute to create each of the targets on the dependency +line. Each line in the command script must begin with a tab. +.Gp "command-line variable" +A variable defined in an argument when PMake is first executed. +Overrides all assignments to the same variable name in the makefile. +.Gp "conditional" +A construct much like that used in C that allows a makefile to be +configured on the fly based on the local environment, or on what is being +made by that invocation of PMake. +.Gp "creation script" +Commands used to create a target. See ``command script.'' +.Gp "dependency" +The relationship between a source and a target. This comes in three +flavors, as indicated by the operator between the target and the +source. `:' gives a straight time-wise dependency (if the target is +older than the source, the target is out-of-date), while `!' provides +simply an ordering and always considers the target out-of-date. `::' +is much like `:', save it creates multiple instances of a target each +of which depends on its own list of sources. +.Gp "dynamic source" +This refers to a source that has a local variable invocation in it. It +allows a single dependency line to specify a different source for each +target on the line. +.Gp "global variable" +Any variable defined in a makefile. Takes precedence over variables +defined in the environment, but not over command-line or local variables. +.Gp "input graph" +What PMake constructs from a makefile. Consists of nodes made of the +targets in the makefile, and the links between them (the +dependencies). The links are directed (from source to target) and +there may not be any cycles (loops) in the graph. +.Gp "local variable" +A variable defined by PMake visible only in a target's shell script. +There are seven local variables, not all of which are defined for +every target: +.CW .TARGET , +.CW .ALLSRC , +.CW .OODATE , +.CW .PREFIX , +.CW .IMPSRC , +.CW .ARCHIVE , +and +.CW .MEMBER . +.CW .TARGET , +.CW .PREFIX , +.CW .ARCHIVE , +and +.CW .MEMBER +may be used on dependency lines to create ``dynamic sources.'' +.Gp "makefile" +A file that describes how a system is built. If you don't know what it +is after reading this tutorial.\|.\|.\|. +.Gp "modifier" +A letter, following a colon, used to alter how a variable is expanded. +It has no effect on the variable itself. +.Gp "operator" +What separates a source from a target (on a dependency line) and specifies +the relationship between the two. There are three: +.CW : ', ` +.CW :: ', ` +and +.CW ! '. ` +.Gp "search path" +A list of directories in which a file should be sought. PMake's view +of the contents of directories in a search path does not change once +the makefile has been read. A file is sought on a search path only if +it is exclusively a source. +.Gp "shell" +A program to which commands are passed in order to create targets. +.Gp "source" +Anything to the right of an operator on a dependency line. Targets on +the dependency line are usually created from the sources. +.Gp "special target" +A target that causes PMake to do special things when it's encountered. +.Gp "suffix" +The tail end of a file name. Usually begins with a period, +.CW .c +or +.CW .ms , +e.g. +.Gp "target" +A word to the left of the operator on a dependency line. More +generally, any file that PMake might create. A file may be (and often +is) both a target and a source (what it is depends on how PMake is +looking at it at the time \*- sort of like the wave/particle duality +of light, you know). +.Gp "transformation rule" +A special construct in a makefile that specifies how to create a file +of one type from a file of another, as indicated by their suffixes. +.Gp "variable expansion" +The process of substituting the value of a variable for a reference to +it. Expansion may be altered by means of modifiers. +.Gp "variable" +A place in which to store text that may be retrieved later. Also used +to define the local environment. Conditionals exist that test whether +a variable is defined or not. +.bp +.\" Output table of contents last, with an entry for the index, making +.\" sure to save and restore the last real page number for the index... +.nr @n \n(PN+1 +.\" We are not generating an index +.\" .XS \n(@n +.\" Index +.\" .XE +.nr %% \n% +.PX +.nr % \n(%% diff --git a/buildrump.sh/src/usr.bin/make/arch.c b/buildrump.sh/src/usr.bin/make/arch.c new file mode 100644 index 00000000..78f3601c --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/arch.c @@ -0,0 +1,1355 @@ +/* $NetBSD: arch.c,v 1.63 2012/06/12 19:21:50 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: arch.c,v 1.63 2012/06/12 19:21:50 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)arch.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: arch.c,v 1.63 2012/06/12 19:21:50 joerg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * arch.c -- + * Functions to manipulate libraries, archives and their members. + * + * Once again, cacheing/hashing comes into play in the manipulation + * of archives. The first time an archive is referenced, all of its members' + * headers are read and hashed and the archive closed again. All hashed + * archives are kept on a list which is searched each time an archive member + * is referenced. + * + * The interface to this module is: + * Arch_ParseArchive Given an archive specification, return a list + * of GNode's, one for each member in the spec. + * FAILURE is returned if the specification is + * invalid for some reason. + * + * Arch_Touch Alter the modification time of the archive + * member described by the given node to be + * the current time. + * + * Arch_TouchLib Update the modification time of the library + * described by the given node. This is special + * because it also updates the modification time + * of the library's table of contents. + * + * Arch_MTime Find the modification time of a member of + * an archive *in the archive*. The time is also + * placed in the member's GNode. Returns the + * modification time. + * + * Arch_MemTime Find the modification time of a member of + * an archive. Called when the member doesn't + * already exist. Looks in the archive for the + * modification time. Returns the modification + * time. + * + * Arch_FindLib Search for a library along a path. The + * library name in the GNode should be in + * -l<name> format. + * + * Arch_LibOODate Special function to decide if a library node + * is out-of-date. + * + * Arch_Init Initialize this module. + * + * Arch_End Cleanup this module. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/param.h> + +#include <ar.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <utime.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "config.h" + +#ifdef TARGET_MACHINE +#undef MAKE_MACHINE +#define MAKE_MACHINE TARGET_MACHINE +#endif +#ifdef TARGET_MACHINE_ARCH +#undef MAKE_MACHINE_ARCH +#define MAKE_MACHINE_ARCH TARGET_MACHINE_ARCH +#endif + +static Lst archives; /* Lst of archives we've already examined */ + +typedef struct Arch { + char *name; /* Name of archive */ + Hash_Table members; /* All the members of the archive described + * by <name, struct ar_hdr *> key/value pairs */ + char *fnametab; /* Extended name table strings */ + size_t fnamesize; /* Size of the string table */ +} Arch; + +static int ArchFindArchive(const void *, const void *); +#ifdef CLEANUP +static void ArchFree(void *); +#endif +static struct ar_hdr *ArchStatMember(char *, char *, Boolean); +static FILE *ArchFindMember(char *, char *, struct ar_hdr *, const char *); +#if defined(__svr4__) || defined(__SVR4) || defined(__ELF__) +#define SVR4ARCHIVES +static int ArchSVR4Entry(Arch *, char *, size_t, FILE *); +#endif + +#ifdef CLEANUP +/*- + *----------------------------------------------------------------------- + * ArchFree -- + * Free memory used by an archive + * + * Results: + * None. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static void +ArchFree(void *ap) +{ + Arch *a = (Arch *)ap; + Hash_Search search; + Hash_Entry *entry; + + /* Free memory from hash entries */ + for (entry = Hash_EnumFirst(&a->members, &search); + entry != NULL; + entry = Hash_EnumNext(&search)) + free(Hash_GetValue(entry)); + + free(a->name); + if (a->fnametab) + free(a->fnametab); + Hash_DeleteTable(&a->members); + free(a); +} +#endif + + + +/*- + *----------------------------------------------------------------------- + * Arch_ParseArchive -- + * Parse the archive specification in the given line and find/create + * the nodes for the specified archive members, placing their nodes + * on the given list. + * + * Input: + * linePtr Pointer to start of specification + * nodeLst Lst on which to place the nodes + * ctxt Context in which to expand variables + * + * Results: + * SUCCESS if it was a valid specification. The linePtr is updated + * to point to the first non-space after the archive spec. The + * nodes for the members are placed on the given list. + * + * Side Effects: + * Some nodes may be created. The given list is extended. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Arch_ParseArchive(char **linePtr, Lst nodeLst, GNode *ctxt) +{ + char *cp; /* Pointer into line */ + GNode *gn; /* New node */ + char *libName; /* Library-part of specification */ + char *memName; /* Member-part of specification */ + char *nameBuf; /* temporary place for node name */ + char saveChar; /* Ending delimiter of member-name */ + Boolean subLibName; /* TRUE if libName should have/had + * variable substitution performed on it */ + + libName = *linePtr; + + subLibName = FALSE; + + for (cp = libName; *cp != '(' && *cp != '\0'; cp++) { + if (*cp == '$') { + /* + * Variable spec, so call the Var module to parse the puppy + * so we can safely advance beyond it... + */ + int length; + void *freeIt; + char *result; + + result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); + if (result == var_Error) { + return(FAILURE); + } else { + subLibName = TRUE; + } + + cp += length-1; + } + } + + *cp++ = '\0'; + if (subLibName) { + libName = Var_Subst(NULL, libName, ctxt, TRUE); + } + + + for (;;) { + /* + * First skip to the start of the member's name, mark that + * place and skip to the end of it (either white-space or + * a close paren). + */ + Boolean doSubst = FALSE; /* TRUE if need to substitute in memName */ + + while (*cp != '\0' && *cp != ')' && isspace ((unsigned char)*cp)) { + cp++; + } + memName = cp; + while (*cp != '\0' && *cp != ')' && !isspace ((unsigned char)*cp)) { + if (*cp == '$') { + /* + * Variable spec, so call the Var module to parse the puppy + * so we can safely advance beyond it... + */ + int length; + void *freeIt; + char *result; + + result = Var_Parse(cp, ctxt, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); + if (result == var_Error) { + return(FAILURE); + } else { + doSubst = TRUE; + } + + cp += length; + } else { + cp++; + } + } + + /* + * If the specification ends without a closing parenthesis, + * chances are there's something wrong (like a missing backslash), + * so it's better to return failure than allow such things to happen + */ + if (*cp == '\0') { + printf("No closing parenthesis in archive specification\n"); + return (FAILURE); + } + + /* + * If we didn't move anywhere, we must be done + */ + if (cp == memName) { + break; + } + + saveChar = *cp; + *cp = '\0'; + + /* + * XXX: This should be taken care of intelligently by + * SuffExpandChildren, both for the archive and the member portions. + */ + /* + * If member contains variables, try and substitute for them. + * This will slow down archive specs with dynamic sources, of course, + * since we'll be (non-)substituting them three times, but them's + * the breaks -- we need to do this since SuffExpandChildren calls + * us, otherwise we could assume the thing would be taken care of + * later. + */ + if (doSubst) { + char *buf; + char *sacrifice; + char *oldMemName = memName; + size_t sz; + + memName = Var_Subst(NULL, memName, ctxt, TRUE); + + /* + * Now form an archive spec and recurse to deal with nested + * variables and multi-word variable values.... The results + * are just placed at the end of the nodeLst we're returning. + */ + sz = strlen(memName)+strlen(libName)+3; + buf = sacrifice = bmake_malloc(sz); + + snprintf(buf, sz, "%s(%s)", libName, memName); + + if (strchr(memName, '$') && strcmp(memName, oldMemName) == 0) { + /* + * Must contain dynamic sources, so we can't deal with it now. + * Just create an ARCHV node for the thing and let + * SuffExpandChildren handle it... + */ + gn = Targ_FindNode(buf, TARG_CREATE); + + if (gn == NULL) { + free(buf); + return(FAILURE); + } else { + gn->type |= OP_ARCHV; + (void)Lst_AtEnd(nodeLst, gn); + } + } else if (Arch_ParseArchive(&sacrifice, nodeLst, ctxt)!=SUCCESS) { + /* + * Error in nested call -- free buffer and return FAILURE + * ourselves. + */ + free(buf); + return(FAILURE); + } + /* + * Free buffer and continue with our work. + */ + free(buf); + } else if (Dir_HasWildcards(memName)) { + Lst members = Lst_Init(FALSE); + char *member; + size_t sz = MAXPATHLEN, nsz; + nameBuf = bmake_malloc(sz); + + Dir_Expand(memName, dirSearchPath, members); + while (!Lst_IsEmpty(members)) { + member = (char *)Lst_DeQueue(members); + nsz = strlen(libName) + strlen(member) + 3; + if (sz > nsz) + nameBuf = bmake_realloc(nameBuf, sz = nsz * 2); + + snprintf(nameBuf, sz, "%s(%s)", libName, member); + free(member); + gn = Targ_FindNode(nameBuf, TARG_CREATE); + if (gn == NULL) { + free(nameBuf); + return (FAILURE); + } else { + /* + * We've found the node, but have to make sure the rest of + * the world knows it's an archive member, without having + * to constantly check for parentheses, so we type the + * thing with the OP_ARCHV bit before we place it on the + * end of the provided list. + */ + gn->type |= OP_ARCHV; + (void)Lst_AtEnd(nodeLst, gn); + } + } + Lst_Destroy(members, NULL); + free(nameBuf); + } else { + size_t sz = strlen(libName) + strlen(memName) + 3; + nameBuf = bmake_malloc(sz); + snprintf(nameBuf, sz, "%s(%s)", libName, memName); + gn = Targ_FindNode(nameBuf, TARG_CREATE); + free(nameBuf); + if (gn == NULL) { + return (FAILURE); + } else { + /* + * We've found the node, but have to make sure the rest of the + * world knows it's an archive member, without having to + * constantly check for parentheses, so we type the thing with + * the OP_ARCHV bit before we place it on the end of the + * provided list. + */ + gn->type |= OP_ARCHV; + (void)Lst_AtEnd(nodeLst, gn); + } + } + if (doSubst) { + free(memName); + } + + *cp = saveChar; + } + + /* + * If substituted libName, free it now, since we need it no longer. + */ + if (subLibName) { + free(libName); + } + + /* + * We promised the pointer would be set up at the next non-space, so + * we must advance cp there before setting *linePtr... (note that on + * entrance to the loop, cp is guaranteed to point at a ')') + */ + do { + cp++; + } while (*cp != '\0' && isspace ((unsigned char)*cp)); + + *linePtr = cp; + return (SUCCESS); +} + +/*- + *----------------------------------------------------------------------- + * ArchFindArchive -- + * See if the given archive is the one we are looking for. Called + * From ArchStatMember and ArchFindMember via Lst_Find. + * + * Input: + * ar Current list element + * archName Name we want + * + * Results: + * 0 if it is, non-zero if it isn't. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static int +ArchFindArchive(const void *ar, const void *archName) +{ + return (strcmp(archName, ((const Arch *)ar)->name)); +} + +/*- + *----------------------------------------------------------------------- + * ArchStatMember -- + * Locate a member of an archive, given the path of the archive and + * the path of the desired member. + * + * Input: + * archive Path to the archive + * member Name of member. If it is a path, only the last + * component is used. + * hash TRUE if archive should be hashed if not already so. + * + * Results: + * A pointer to the current struct ar_hdr structure for the member. Note + * That no position is returned, so this is not useful for touching + * archive members. This is mostly because we have no assurances that + * The archive will remain constant after we read all the headers, so + * there's not much point in remembering the position... + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static struct ar_hdr * +ArchStatMember(char *archive, char *member, Boolean hash) +{ +#define AR_MAX_NAME_LEN (sizeof(arh.ar_name)-1) + FILE * arch; /* Stream to archive */ + int size; /* Size of archive member */ + char *cp; /* Useful character pointer */ + char magic[SARMAG]; + LstNode ln; /* Lst member containing archive descriptor */ + Arch *ar; /* Archive descriptor */ + Hash_Entry *he; /* Entry containing member's description */ + struct ar_hdr arh; /* archive-member header for reading archive */ + char memName[MAXPATHLEN+1]; + /* Current member name while hashing. */ + + /* + * Because of space constraints and similar things, files are archived + * using their final path components, not the entire thing, so we need + * to point 'member' to the final component, if there is one, to make + * the comparisons easier... + */ + cp = strrchr(member, '/'); + if (cp != NULL) { + member = cp + 1; + } + + ln = Lst_Find(archives, archive, ArchFindArchive); + if (ln != NULL) { + ar = (Arch *)Lst_Datum(ln); + + he = Hash_FindEntry(&ar->members, member); + + if (he != NULL) { + return ((struct ar_hdr *)Hash_GetValue(he)); + } else { + /* Try truncated name */ + char copy[AR_MAX_NAME_LEN+1]; + size_t len = strlen(member); + + if (len > AR_MAX_NAME_LEN) { + len = AR_MAX_NAME_LEN; + strncpy(copy, member, AR_MAX_NAME_LEN); + copy[AR_MAX_NAME_LEN] = '\0'; + } + if ((he = Hash_FindEntry(&ar->members, copy)) != NULL) + return ((struct ar_hdr *)Hash_GetValue(he)); + return NULL; + } + } + + if (!hash) { + /* + * Caller doesn't want the thing hashed, just use ArchFindMember + * to read the header for the member out and close down the stream + * again. Since the archive is not to be hashed, we assume there's + * no need to allocate extra room for the header we're returning, + * so just declare it static. + */ + static struct ar_hdr sarh; + + arch = ArchFindMember(archive, member, &sarh, "r"); + + if (arch == NULL) { + return NULL; + } else { + fclose(arch); + return (&sarh); + } + } + + /* + * We don't have this archive on the list yet, so we want to find out + * everything that's in it and cache it so we can get at it quickly. + */ + arch = fopen(archive, "r"); + if (arch == NULL) { + return NULL; + } + + /* + * We use the ARMAG string to make sure this is an archive we + * can handle... + */ + if ((fread(magic, SARMAG, 1, arch) != 1) || + (strncmp(magic, ARMAG, SARMAG) != 0)) { + fclose(arch); + return NULL; + } + + ar = bmake_malloc(sizeof(Arch)); + ar->name = bmake_strdup(archive); + ar->fnametab = NULL; + ar->fnamesize = 0; + Hash_InitTable(&ar->members, -1); + memName[AR_MAX_NAME_LEN] = '\0'; + + while (fread((char *)&arh, sizeof(struct ar_hdr), 1, arch) == 1) { + if (strncmp( arh.ar_fmag, ARFMAG, sizeof(arh.ar_fmag)) != 0) { + /* + * The header is bogus, so the archive is bad + * and there's no way we can recover... + */ + goto badarch; + } else { + /* + * We need to advance the stream's pointer to the start of the + * next header. Files are padded with newlines to an even-byte + * boundary, so we need to extract the size of the file from the + * 'size' field of the header and round it up during the seek. + */ + arh.ar_size[sizeof(arh.ar_size)-1] = '\0'; + size = (int)strtol(arh.ar_size, NULL, 10); + + (void)strncpy(memName, arh.ar_name, sizeof(arh.ar_name)); + for (cp = &memName[AR_MAX_NAME_LEN]; *cp == ' '; cp--) { + continue; + } + cp[1] = '\0'; + +#ifdef SVR4ARCHIVES + /* + * svr4 names are slash terminated. Also svr4 extended AR format. + */ + if (memName[0] == '/') { + /* + * svr4 magic mode; handle it + */ + switch (ArchSVR4Entry(ar, memName, size, arch)) { + case -1: /* Invalid data */ + goto badarch; + case 0: /* List of files entry */ + continue; + default: /* Got the entry */ + break; + } + } + else { + if (cp[0] == '/') + cp[0] = '\0'; + } +#endif + +#ifdef AR_EFMT1 + /* + * BSD 4.4 extended AR format: #1/<namelen>, with name as the + * first <namelen> bytes of the file + */ + if (strncmp(memName, AR_EFMT1, sizeof(AR_EFMT1) - 1) == 0 && + isdigit((unsigned char)memName[sizeof(AR_EFMT1) - 1])) { + + unsigned int elen = atoi(&memName[sizeof(AR_EFMT1)-1]); + + if (elen > MAXPATHLEN) + goto badarch; + if (fread(memName, elen, 1, arch) != 1) + goto badarch; + memName[elen] = '\0'; + fseek(arch, -elen, SEEK_CUR); + if (DEBUG(ARCH) || DEBUG(MAKE)) { + fprintf(debug_file, "ArchStat: Extended format entry for %s\n", memName); + } + } +#endif + + he = Hash_CreateEntry(&ar->members, memName, NULL); + Hash_SetValue(he, bmake_malloc(sizeof(struct ar_hdr))); + memcpy(Hash_GetValue(he), &arh, sizeof(struct ar_hdr)); + } + fseek(arch, (size + 1) & ~1, SEEK_CUR); + } + + fclose(arch); + + (void)Lst_AtEnd(archives, ar); + + /* + * Now that the archive has been read and cached, we can look into + * the hash table to find the desired member's header. + */ + he = Hash_FindEntry(&ar->members, member); + + if (he != NULL) { + return ((struct ar_hdr *)Hash_GetValue(he)); + } else { + return NULL; + } + +badarch: + fclose(arch); + Hash_DeleteTable(&ar->members); + if (ar->fnametab) + free(ar->fnametab); + free(ar); + return NULL; +} + +#ifdef SVR4ARCHIVES +/*- + *----------------------------------------------------------------------- + * ArchSVR4Entry -- + * Parse an SVR4 style entry that begins with a slash. + * If it is "//", then load the table of filenames + * If it is "/<offset>", then try to substitute the long file name + * from offset of a table previously read. + * + * Results: + * -1: Bad data in archive + * 0: A table was loaded from the file + * 1: Name was successfully substituted from table + * 2: Name was not successfully substituted from table + * + * Side Effects: + * If a table is read, the file pointer is moved to the next archive + * member + * + *----------------------------------------------------------------------- + */ +static int +ArchSVR4Entry(Arch *ar, char *name, size_t size, FILE *arch) +{ +#define ARLONGNAMES1 "//" +#define ARLONGNAMES2 "/ARFILENAMES" + size_t entry; + char *ptr, *eptr; + + if (strncmp(name, ARLONGNAMES1, sizeof(ARLONGNAMES1) - 1) == 0 || + strncmp(name, ARLONGNAMES2, sizeof(ARLONGNAMES2) - 1) == 0) { + + if (ar->fnametab != NULL) { + if (DEBUG(ARCH)) { + fprintf(debug_file, "Attempted to redefine an SVR4 name table\n"); + } + return -1; + } + + /* + * This is a table of archive names, so we build one for + * ourselves + */ + ar->fnametab = bmake_malloc(size); + ar->fnamesize = size; + + if (fread(ar->fnametab, size, 1, arch) != 1) { + if (DEBUG(ARCH)) { + fprintf(debug_file, "Reading an SVR4 name table failed\n"); + } + return -1; + } + eptr = ar->fnametab + size; + for (entry = 0, ptr = ar->fnametab; ptr < eptr; ptr++) + switch (*ptr) { + case '/': + entry++; + *ptr = '\0'; + break; + + case '\n': + break; + + default: + break; + } + if (DEBUG(ARCH)) { + fprintf(debug_file, "Found svr4 archive name table with %lu entries\n", + (u_long)entry); + } + return 0; + } + + if (name[1] == ' ' || name[1] == '\0') + return 2; + + entry = (size_t)strtol(&name[1], &eptr, 0); + if ((*eptr != ' ' && *eptr != '\0') || eptr == &name[1]) { + if (DEBUG(ARCH)) { + fprintf(debug_file, "Could not parse SVR4 name %s\n", name); + } + return 2; + } + if (entry >= ar->fnamesize) { + if (DEBUG(ARCH)) { + fprintf(debug_file, "SVR4 entry offset %s is greater than %lu\n", + name, (u_long)ar->fnamesize); + } + return 2; + } + + if (DEBUG(ARCH)) { + fprintf(debug_file, "Replaced %s with %s\n", name, &ar->fnametab[entry]); + } + + (void)strncpy(name, &ar->fnametab[entry], MAXPATHLEN); + name[MAXPATHLEN] = '\0'; + return 1; +} +#endif + + +/*- + *----------------------------------------------------------------------- + * ArchFindMember -- + * Locate a member of an archive, given the path of the archive and + * the path of the desired member. If the archive is to be modified, + * the mode should be "r+", if not, it should be "r". + * + * Input: + * archive Path to the archive + * member Name of member. If it is a path, only the last + * component is used. + * arhPtr Pointer to header structure to be filled in + * mode The mode for opening the stream + * + * Results: + * An FILE *, opened for reading and writing, positioned at the + * start of the member's struct ar_hdr, or NULL if the member was + * nonexistent. The current struct ar_hdr for member. + * + * Side Effects: + * The passed struct ar_hdr structure is filled in. + * + *----------------------------------------------------------------------- + */ +static FILE * +ArchFindMember(char *archive, char *member, struct ar_hdr *arhPtr, + const char *mode) +{ + FILE * arch; /* Stream to archive */ + int size; /* Size of archive member */ + char *cp; /* Useful character pointer */ + char magic[SARMAG]; + size_t len, tlen; + + arch = fopen(archive, mode); + if (arch == NULL) { + return NULL; + } + + /* + * We use the ARMAG string to make sure this is an archive we + * can handle... + */ + if ((fread(magic, SARMAG, 1, arch) != 1) || + (strncmp(magic, ARMAG, SARMAG) != 0)) { + fclose(arch); + return NULL; + } + + /* + * Because of space constraints and similar things, files are archived + * using their final path components, not the entire thing, so we need + * to point 'member' to the final component, if there is one, to make + * the comparisons easier... + */ + cp = strrchr(member, '/'); + if (cp != NULL) { + member = cp + 1; + } + len = tlen = strlen(member); + if (len > sizeof(arhPtr->ar_name)) { + tlen = sizeof(arhPtr->ar_name); + } + + while (fread((char *)arhPtr, sizeof(struct ar_hdr), 1, arch) == 1) { + if (strncmp(arhPtr->ar_fmag, ARFMAG, sizeof(arhPtr->ar_fmag) ) != 0) { + /* + * The header is bogus, so the archive is bad + * and there's no way we can recover... + */ + fclose(arch); + return NULL; + } else if (strncmp(member, arhPtr->ar_name, tlen) == 0) { + /* + * If the member's name doesn't take up the entire 'name' field, + * we have to be careful of matching prefixes. Names are space- + * padded to the right, so if the character in 'name' at the end + * of the matched string is anything but a space, this isn't the + * member we sought. + */ + if (tlen != sizeof(arhPtr->ar_name) && arhPtr->ar_name[tlen] != ' '){ + goto skip; + } else { + /* + * To make life easier, we reposition the file at the start + * of the header we just read before we return the stream. + * In a more general situation, it might be better to leave + * the file at the actual member, rather than its header, but + * not here... + */ + fseek(arch, -sizeof(struct ar_hdr), SEEK_CUR); + return (arch); + } + } else +#ifdef AR_EFMT1 + /* + * BSD 4.4 extended AR format: #1/<namelen>, with name as the + * first <namelen> bytes of the file + */ + if (strncmp(arhPtr->ar_name, AR_EFMT1, + sizeof(AR_EFMT1) - 1) == 0 && + isdigit((unsigned char)arhPtr->ar_name[sizeof(AR_EFMT1) - 1])) { + + unsigned int elen = atoi(&arhPtr->ar_name[sizeof(AR_EFMT1)-1]); + char ename[MAXPATHLEN + 1]; + + if (elen > MAXPATHLEN) { + fclose(arch); + return NULL; + } + if (fread(ename, elen, 1, arch) != 1) { + fclose(arch); + return NULL; + } + ename[elen] = '\0'; + if (DEBUG(ARCH) || DEBUG(MAKE)) { + fprintf(debug_file, "ArchFind: Extended format entry for %s\n", ename); + } + if (strncmp(ename, member, len) == 0) { + /* Found as extended name */ + fseek(arch, -sizeof(struct ar_hdr) - elen, SEEK_CUR); + return (arch); + } + fseek(arch, -elen, SEEK_CUR); + goto skip; + } else +#endif + { +skip: + /* + * This isn't the member we're after, so we need to advance the + * stream's pointer to the start of the next header. Files are + * padded with newlines to an even-byte boundary, so we need to + * extract the size of the file from the 'size' field of the + * header and round it up during the seek. + */ + arhPtr->ar_size[sizeof(arhPtr->ar_size)-1] = '\0'; + size = (int)strtol(arhPtr->ar_size, NULL, 10); + fseek(arch, (size + 1) & ~1, SEEK_CUR); + } + } + + /* + * We've looked everywhere, but the member is not to be found. Close the + * archive and return NULL -- an error. + */ + fclose(arch); + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * Arch_Touch -- + * Touch a member of an archive. + * + * Input: + * gn Node of member to touch + * + * Results: + * The 'time' field of the member's header is updated. + * + * Side Effects: + * The modification time of the entire archive is also changed. + * For a library, this could necessitate the re-ranlib'ing of the + * whole thing. + * + *----------------------------------------------------------------------- + */ +void +Arch_Touch(GNode *gn) +{ + FILE * arch; /* Stream open to archive, positioned properly */ + struct ar_hdr arh; /* Current header describing member */ + char *p1, *p2; + + arch = ArchFindMember(Var_Value(ARCHIVE, gn, &p1), + Var_Value(MEMBER, gn, &p2), + &arh, "r+"); + if (p1) + free(p1); + if (p2) + free(p2); + snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long) now); + + if (arch != NULL) { + (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); + fclose(arch); + } +} + +/*- + *----------------------------------------------------------------------- + * Arch_TouchLib -- + * Given a node which represents a library, touch the thing, making + * sure that the table of contents also is touched. + * + * Input: + * gn The node of the library to touch + * + * Results: + * None. + * + * Side Effects: + * Both the modification time of the library and of the RANLIBMAG + * member are set to 'now'. + * + *----------------------------------------------------------------------- + */ +void +#if !defined(RANLIBMAG) +Arch_TouchLib(GNode *gn MAKE_ATTR_UNUSED) +#else +Arch_TouchLib(GNode *gn) +#endif +{ +#ifdef RANLIBMAG + FILE * arch; /* Stream open to archive */ + struct ar_hdr arh; /* Header describing table of contents */ + struct utimbuf times; /* Times for utime() call */ + + arch = ArchFindMember(gn->path, UNCONST(RANLIBMAG), &arh, "r+"); + snprintf(arh.ar_date, sizeof(arh.ar_date), "%-12ld", (long) now); + + if (arch != NULL) { + (void)fwrite((char *)&arh, sizeof(struct ar_hdr), 1, arch); + fclose(arch); + + times.actime = times.modtime = now; + utime(gn->path, ×); + } +#endif +} + +/*- + *----------------------------------------------------------------------- + * Arch_MTime -- + * Return the modification time of a member of an archive. + * + * Input: + * gn Node describing archive member + * + * Results: + * The modification time(seconds). + * + * Side Effects: + * The mtime field of the given node is filled in with the value + * returned by the function. + * + *----------------------------------------------------------------------- + */ +time_t +Arch_MTime(GNode *gn) +{ + struct ar_hdr *arhPtr; /* Header of desired member */ + time_t modTime; /* Modification time as an integer */ + char *p1, *p2; + + arhPtr = ArchStatMember(Var_Value(ARCHIVE, gn, &p1), + Var_Value(MEMBER, gn, &p2), + TRUE); + if (p1) + free(p1); + if (p2) + free(p2); + + if (arhPtr != NULL) { + modTime = (time_t)strtol(arhPtr->ar_date, NULL, 10); + } else { + modTime = 0; + } + + gn->mtime = modTime; + return (modTime); +} + +/*- + *----------------------------------------------------------------------- + * Arch_MemMTime -- + * Given a non-existent archive member's node, get its modification + * time from its archived form, if it exists. + * + * Results: + * The modification time. + * + * Side Effects: + * The mtime field is filled in. + * + *----------------------------------------------------------------------- + */ +time_t +Arch_MemMTime(GNode *gn) +{ + LstNode ln; + GNode *pgn; + char *nameStart, + *nameEnd; + + if (Lst_Open(gn->parents) != SUCCESS) { + gn->mtime = 0; + return (0); + } + while ((ln = Lst_Next(gn->parents)) != NULL) { + pgn = (GNode *)Lst_Datum(ln); + + if (pgn->type & OP_ARCHV) { + /* + * If the parent is an archive specification and is being made + * and its member's name matches the name of the node we were + * given, record the modification time of the parent in the + * child. We keep searching its parents in case some other + * parent requires this child to exist... + */ + nameStart = strchr(pgn->name, '(') + 1; + nameEnd = strchr(nameStart, ')'); + + if ((pgn->flags & REMAKE) && + strncmp(nameStart, gn->name, nameEnd - nameStart) == 0) { + gn->mtime = Arch_MTime(pgn); + } + } else if (pgn->flags & REMAKE) { + /* + * Something which isn't a library depends on the existence of + * this target, so it needs to exist. + */ + gn->mtime = 0; + break; + } + } + + Lst_Close(gn->parents); + + return (gn->mtime); +} + +/*- + *----------------------------------------------------------------------- + * Arch_FindLib -- + * Search for a library along the given search path. + * + * Input: + * gn Node of library to find + * path Search path + * + * Results: + * None. + * + * Side Effects: + * The node's 'path' field is set to the found path (including the + * actual file name, not -l...). If the system can handle the -L + * flag when linking (or we cannot find the library), we assume that + * the user has placed the .LIBRARIES variable in the final linking + * command (or the linker will know where to find it) and set the + * TARGET variable for this node to be the node's name. Otherwise, + * we set the TARGET variable to be the full path of the library, + * as returned by Dir_FindFile. + * + *----------------------------------------------------------------------- + */ +void +Arch_FindLib(GNode *gn, Lst path) +{ + char *libName; /* file name for archive */ + size_t sz = strlen(gn->name) + 6 - 2; + + libName = bmake_malloc(sz); + snprintf(libName, sz, "lib%s.a", &gn->name[2]); + + gn->path = Dir_FindFile(libName, path); + + free(libName); + +#ifdef LIBRARIES + Var_Set(TARGET, gn->name, gn, 0); +#else + Var_Set(TARGET, gn->path == NULL ? gn->name : gn->path, gn, 0); +#endif /* LIBRARIES */ +} + +/*- + *----------------------------------------------------------------------- + * Arch_LibOODate -- + * Decide if a node with the OP_LIB attribute is out-of-date. Called + * from Make_OODate to make its life easier. + * + * There are several ways for a library to be out-of-date that are + * not available to ordinary files. In addition, there are ways + * that are open to regular files that are not available to + * libraries. A library that is only used as a source is never + * considered out-of-date by itself. This does not preclude the + * library's modification time from making its parent be out-of-date. + * A library will be considered out-of-date for any of these reasons, + * given that it is a target on a dependency line somewhere: + * Its modification time is less than that of one of its + * sources (gn->mtime < gn->cmgn->mtime). + * Its modification time is greater than the time at which the + * make began (i.e. it's been modified in the course + * of the make, probably by archiving). + * The modification time of one of its sources is greater than + * the one of its RANLIBMAG member (i.e. its table of contents + * is out-of-date). We don't compare of the archive time + * vs. TOC time because they can be too close. In my + * opinion we should not bother with the TOC at all since + * this is used by 'ar' rules that affect the data contents + * of the archive, not by ranlib rules, which affect the + * TOC. + * + * Input: + * gn The library's graph node + * + * Results: + * TRUE if the library is out-of-date. FALSE otherwise. + * + * Side Effects: + * The library will be hashed if it hasn't been already. + * + *----------------------------------------------------------------------- + */ +Boolean +Arch_LibOODate(GNode *gn) +{ + Boolean oodate; + + if (gn->type & OP_PHONY) { + oodate = TRUE; + } else if (OP_NOP(gn->type) && Lst_IsEmpty(gn->children)) { + oodate = FALSE; + } else if ((!Lst_IsEmpty(gn->children) && gn->cmgn == NULL) || + (gn->mtime > now) || + (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime)) { + oodate = TRUE; + } else { +#ifdef RANLIBMAG + struct ar_hdr *arhPtr; /* Header for __.SYMDEF */ + int modTimeTOC; /* The table-of-contents's mod time */ + + arhPtr = ArchStatMember(gn->path, UNCONST(RANLIBMAG), FALSE); + + if (arhPtr != NULL) { + modTimeTOC = (int)strtol(arhPtr->ar_date, NULL, 10); + + if (DEBUG(ARCH) || DEBUG(MAKE)) { + fprintf(debug_file, "%s modified %s...", RANLIBMAG, Targ_FmtTime(modTimeTOC)); + } + oodate = (gn->cmgn == NULL || gn->cmgn->mtime > modTimeTOC); + } else { + /* + * A library w/o a table of contents is out-of-date + */ + if (DEBUG(ARCH) || DEBUG(MAKE)) { + fprintf(debug_file, "No t.o.c...."); + } + oodate = TRUE; + } +#else + oodate = FALSE; +#endif + } + return (oodate); +} + +/*- + *----------------------------------------------------------------------- + * Arch_Init -- + * Initialize things for this module. + * + * Results: + * None. + * + * Side Effects: + * The 'archives' list is initialized. + * + *----------------------------------------------------------------------- + */ +void +Arch_Init(void) +{ + archives = Lst_Init(FALSE); +} + + + +/*- + *----------------------------------------------------------------------- + * Arch_End -- + * Cleanup things for this module. + * + * Results: + * None. + * + * Side Effects: + * The 'archives' list is freed + * + *----------------------------------------------------------------------- + */ +void +Arch_End(void) +{ +#ifdef CLEANUP + Lst_Destroy(archives, ArchFree); +#endif +} + +/*- + *----------------------------------------------------------------------- + * Arch_IsLib -- + * Check if the node is a library + * + * Results: + * True or False. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +Arch_IsLib(GNode *gn) +{ + static const char armag[] = "!<arch>\n"; + char buf[sizeof(armag)-1]; + int fd; + + if ((fd = open(gn->path, O_RDONLY)) == -1) + return FALSE; + + if (read(fd, buf, sizeof(buf)) != sizeof(buf)) { + (void)close(fd); + return FALSE; + } + + (void)close(fd); + + return memcmp(buf, armag, sizeof(buf)) == 0; +} diff --git a/buildrump.sh/src/usr.bin/make/buf.c b/buildrump.sh/src/usr.bin/make/buf.c new file mode 100644 index 00000000..ac95c16c --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/buf.c @@ -0,0 +1,291 @@ +/* $NetBSD: buf.c,v 1.25 2012/04/24 20:26:58 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: buf.c,v 1.25 2012/04/24 20:26:58 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)buf.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: buf.c,v 1.25 2012/04/24 20:26:58 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * buf.c -- + * Functions for automatically-expanded buffers. + */ + +#include "make.h" +#include "buf.h" + +#ifndef max +#define max(a,b) ((a) > (b) ? (a) : (b)) +#endif + +#define BUF_DEF_SIZE 256 /* Default buffer size */ + +/*- + *----------------------------------------------------------------------- + * Buf_Expand_1 -- + * Extend buffer for single byte add. + * + *----------------------------------------------------------------------- + */ +void +Buf_Expand_1(Buffer *bp) +{ + bp->size += max(bp->size, 16); + bp->buffer = bmake_realloc(bp->buffer, bp->size); +} + +/*- + *----------------------------------------------------------------------- + * Buf_AddBytes -- + * Add a number of bytes to the buffer. + * + * Results: + * None. + * + * Side Effects: + * Guess what? + * + *----------------------------------------------------------------------- + */ +void +Buf_AddBytes(Buffer *bp, int numBytes, const Byte *bytesPtr) +{ + int count = bp->count; + Byte *ptr; + + if (__predict_false(count + numBytes >= bp->size)) { + bp->size += max(bp->size, numBytes + 16); + bp->buffer = bmake_realloc(bp->buffer, bp->size); + } + + ptr = bp->buffer + count; + bp->count = count + numBytes; + ptr[numBytes] = 0; + memcpy(ptr, bytesPtr, numBytes); +} + +/*- + *----------------------------------------------------------------------- + * Buf_GetAll -- + * Get all the available data at once. + * + * Results: + * A pointer to the data and the number of bytes available. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Byte * +Buf_GetAll(Buffer *bp, int *numBytesPtr) +{ + + if (numBytesPtr != NULL) + *numBytesPtr = bp->count; + + return (bp->buffer); +} + +/*- + *----------------------------------------------------------------------- + * Buf_Empty -- + * Throw away bytes in a buffer. + * + * Results: + * None. + * + * Side Effects: + * The bytes are discarded. + * + *----------------------------------------------------------------------- + */ +void +Buf_Empty(Buffer *bp) +{ + + bp->count = 0; + *bp->buffer = 0; +} + +/*- + *----------------------------------------------------------------------- + * Buf_Init -- + * Initialize a buffer. If no initial size is given, a reasonable + * default is used. + * + * Input: + * size Initial size for the buffer + * + * Results: + * A buffer to be given to other functions in this library. + * + * Side Effects: + * The buffer is created, the space allocated and pointers + * initialized. + * + *----------------------------------------------------------------------- + */ +void +Buf_Init(Buffer *bp, int size) +{ + if (size <= 0) { + size = BUF_DEF_SIZE; + } + bp->size = size; + bp->count = 0; + bp->buffer = bmake_malloc(size); + *bp->buffer = 0; +} + +/*- + *----------------------------------------------------------------------- + * Buf_Destroy -- + * Nuke a buffer and all its resources. + * + * Input: + * buf Buffer to destroy + * freeData TRUE if the data should be destroyed + * + * Results: + * Data buffer, NULL if freed + * + * Side Effects: + * The buffer is freed. + * + *----------------------------------------------------------------------- + */ +Byte * +Buf_Destroy(Buffer *buf, Boolean freeData) +{ + Byte *data; + + data = buf->buffer; + if (freeData) { + free(data); + data = NULL; + } + + buf->size = 0; + buf->count = 0; + buf->buffer = NULL; + + return data; +} + + +/*- + *----------------------------------------------------------------------- + * Buf_DestroyCompact -- + * Nuke a buffer and return its data. + * + * Input: + * buf Buffer to destroy + * + * Results: + * Data buffer + * + * Side Effects: + * If the buffer size is much greater than its content, + * a new buffer will be allocated and the old one freed. + * + *----------------------------------------------------------------------- + */ +#ifndef BUF_COMPACT_LIMIT +# define BUF_COMPACT_LIMIT 128 /* worthwhile saving */ +#endif + +Byte * +Buf_DestroyCompact(Buffer *buf) +{ +#if BUF_COMPACT_LIMIT > 0 + Byte *data; + + if (buf->size - buf->count >= BUF_COMPACT_LIMIT) { + /* We trust realloc to be smart */ + data = bmake_realloc(buf->buffer, buf->count + 1); + if (data) { + data[buf->count] = 0; + Buf_Destroy(buf, FALSE); + return data; + } + } +#endif + return Buf_Destroy(buf, FALSE); +} diff --git a/buildrump.sh/src/usr.bin/make/buf.h b/buildrump.sh/src/usr.bin/make/buf.h new file mode 100644 index 00000000..25be67d7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/buf.h @@ -0,0 +1,119 @@ +/* $NetBSD: buf.h,v 1.17 2012/04/24 20:26:58 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)buf.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)buf.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * buf.h -- + * Header for users of the buf library. + */ + +#ifndef _BUF_H +#define _BUF_H + +typedef char Byte; + +typedef struct Buffer { + int size; /* Current size of the buffer */ + int count; /* Number of bytes in buffer */ + Byte *buffer; /* The buffer itself (zero terminated) */ +} Buffer; + +/* If we aren't on netbsd, __predict_false() might not be defined. */ +#ifndef __predict_false +#define __predict_false(x) (x) +#endif + +/* Buf_AddByte adds a single byte to a buffer. */ +#define Buf_AddByte(bp, byte) do { \ + int _count = ++(bp)->count; \ + char *_ptr; \ + if (__predict_false(_count >= (bp)->size)) \ + Buf_Expand_1(bp); \ + _ptr = (bp)->buffer + _count; \ + _ptr[-1] = (byte); \ + _ptr[0] = 0; \ + } while (0) + +#define BUF_ERROR 256 + +#define Buf_Size(bp) ((bp)->count) + +void Buf_Expand_1(Buffer *); +void Buf_AddBytes(Buffer *, int, const Byte *); +Byte *Buf_GetAll(Buffer *, int *); +void Buf_Empty(Buffer *); +void Buf_Init(Buffer *, int); +Byte *Buf_Destroy(Buffer *, Boolean); +Byte *Buf_DestroyCompact(Buffer *); + +#endif /* _BUF_H */ diff --git a/buildrump.sh/src/usr.bin/make/compat.c b/buildrump.sh/src/usr.bin/make/compat.c new file mode 100644 index 00000000..76a71c18 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/compat.c @@ -0,0 +1,764 @@ +/* $NetBSD: compat.c,v 1.96 2014/09/07 20:55:34 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: compat.c,v 1.96 2014/09/07 20:55:34 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)compat.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: compat.c,v 1.96 2014/09/07 20:55:34 joerg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * compat.c -- + * The routines in this file implement the full-compatibility + * mode of PMake. Most of the special functionality of PMake + * is available in this mode. Things not supported: + * - different shells. + * - friendly variable substitution. + * + * Interface: + * Compat_Run Initialize things for this module and recreate + * thems as need creatin' + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" + +/* + * The following array is used to make a fast determination of which + * characters are interpreted specially by the shell. If a command + * contains any of these characters, it is executed by the shell, not + * directly by us. + */ + +static char meta[256]; + +static GNode *curTarg = NULL; +static GNode *ENDNode; +static void CompatInterrupt(int); + +static void +Compat_Init(void) +{ + const char *cp; + + Shell_Init(); /* setup default shell */ + + for (cp = "~#=|^(){};&<>*?[]:$`\\\n"; *cp != '\0'; cp++) { + meta[(unsigned char) *cp] = 1; + } + /* + * The null character serves as a sentinel in the string. + */ + meta[0] = 1; +} + +/*- + *----------------------------------------------------------------------- + * CompatInterrupt -- + * Interrupt the creation of the current target and remove it if + * it ain't precious. + * + * Results: + * None. + * + * Side Effects: + * The target is removed and the process exits. If .INTERRUPT exists, + * its commands are run first WITH INTERRUPTS IGNORED.. + * + *----------------------------------------------------------------------- + */ +static void +CompatInterrupt(int signo) +{ + GNode *gn; + + if ((curTarg != NULL) && !Targ_Precious (curTarg)) { + char *p1; + char *file = Var_Value(TARGET, curTarg, &p1); + + if (!noExecute && eunlink(file) != -1) { + Error("*** %s removed", file); + } + if (p1) + free(p1); + + /* + * Run .INTERRUPT only if hit with interrupt signal + */ + if (signo == SIGINT) { + gn = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); + if (gn != NULL) { + Compat_Make(gn, gn); + } + } + + } + if (signo == SIGQUIT) + _exit(signo); + bmake_signal(signo, SIG_DFL); + kill(myPid, signo); +} + +/*- + *----------------------------------------------------------------------- + * CompatRunCommand -- + * Execute the next command for a target. If the command returns an + * error, the node's made field is set to ERROR and creation stops. + * + * Input: + * cmdp Command to execute + * gnp Node from which the command came + * + * Results: + * 0 if the command succeeded, 1 if an error occurred. + * + * Side Effects: + * The node's 'made' field may be set to ERROR. + * + *----------------------------------------------------------------------- + */ +int +CompatRunCommand(void *cmdp, void *gnp) +{ + char *cmdStart; /* Start of expanded command */ + char *cp, *bp; + Boolean silent, /* Don't print command */ + doIt; /* Execute even if -n */ + volatile Boolean errCheck; /* Check errors */ + int reason; /* Reason for child's death */ + int status; /* Description of child's death */ + pid_t cpid; /* Child actually found */ + pid_t retstat; /* Result of wait */ + LstNode cmdNode; /* Node where current command is located */ + const char ** volatile av; /* Argument vector for thing to exec */ + char ** volatile mav;/* Copy of the argument vector for freeing */ + int argc; /* Number of arguments in av or 0 if not + * dynamically allocated */ + Boolean local; /* TRUE if command should be executed + * locally */ + Boolean useShell; /* TRUE if command should be executed + * using a shell */ + char * volatile cmd = (char *)cmdp; + GNode *gn = (GNode *)gnp; + + silent = gn->type & OP_SILENT; + errCheck = !(gn->type & OP_IGNORE); + doIt = FALSE; + + cmdNode = Lst_Member(gn->commands, cmd); + cmdStart = Var_Subst(NULL, cmd, gn, FALSE); + + /* + * brk_string will return an argv with a NULL in av[0], thus causing + * execvp to choke and die horribly. Besides, how can we execute a null + * command? In any case, we warn the user that the command expanded to + * nothing (is this the right thing to do?). + */ + + if (*cmdStart == '\0') { + free(cmdStart); + return(0); + } + cmd = cmdStart; + Lst_Replace(cmdNode, cmdStart); + + if ((gn->type & OP_SAVE_CMDS) && (gn != ENDNode)) { + (void)Lst_AtEnd(ENDNode->commands, cmdStart); + return(0); + } + if (strcmp(cmdStart, "...") == 0) { + gn->type |= OP_SAVE_CMDS; + return(0); + } + + while ((*cmd == '@') || (*cmd == '-') || (*cmd == '+')) { + switch (*cmd) { + case '@': + silent = DEBUG(LOUD) ? FALSE : TRUE; + break; + case '-': + errCheck = FALSE; + break; + case '+': + doIt = TRUE; + if (!meta[0]) /* we came here from jobs */ + Compat_Init(); + break; + } + cmd++; + } + + while (isspace((unsigned char)*cmd)) + cmd++; + + /* + * If we did not end up with a command, just skip it. + */ + if (!*cmd) + return (0); + +#if !defined(MAKE_NATIVE) + /* + * In a non-native build, the host environment might be weird enough + * that it's necessary to go through a shell to get the correct + * behaviour. Or perhaps the shell has been replaced with something + * that does extra logging, and that should not be bypassed. + */ + useShell = TRUE; +#else + /* + * Search for meta characters in the command. If there are no meta + * characters, there's no need to execute a shell to execute the + * command. + */ + for (cp = cmd; !meta[(unsigned char)*cp]; cp++) { + continue; + } + useShell = (*cp != '\0'); +#endif + + /* + * Print the command before echoing if we're not supposed to be quiet for + * this one. We also print the command if -n given. + */ + if (!silent || NoExecute(gn)) { + printf("%s\n", cmd); + fflush(stdout); + } + + /* + * If we're not supposed to execute any commands, this is as far as + * we go... + */ + if (!doIt && NoExecute(gn)) { + return (0); + } + if (DEBUG(JOB)) + fprintf(debug_file, "Execute: '%s'\n", cmd); + +again: + if (useShell) { + /* + * We need to pass the command off to the shell, typically + * because the command contains a "meta" character. + */ + static const char *shargv[5]; + int shargc; + + shargc = 0; + shargv[shargc++] = shellPath; + /* + * The following work for any of the builtin shell specs. + */ + if (errCheck && shellErrFlag) { + shargv[shargc++] = shellErrFlag; + } + if (DEBUG(SHELL)) + shargv[shargc++] = "-xc"; + else + shargv[shargc++] = "-c"; + shargv[shargc++] = cmd; + shargv[shargc++] = NULL; + av = shargv; + argc = 0; + bp = NULL; + mav = NULL; + } else { + /* + * No meta-characters, so no need to exec a shell. Break the command + * into words to form an argument vector we can execute. + */ + mav = brk_string(cmd, &argc, TRUE, &bp); + if (mav == NULL) { + useShell = 1; + goto again; + } + av = (void *)mav; + } + + local = TRUE; + +#ifdef USE_META + if (useMeta) { + meta_compat_start(); + } +#endif + + /* + * Fork and execute the single command. If the fork fails, we abort. + */ + cpid = vFork(); + if (cpid < 0) { + Fatal("Could not fork"); + } + if (cpid == 0) { + Var_ExportVars(); +#ifdef USE_META + if (useMeta) { + meta_compat_child(); + } +#endif + if (local) + (void)execvp(av[0], (char *const *)UNCONST(av)); + else + (void)execv(av[0], (char *const *)UNCONST(av)); + execError("exec", av[0]); + _exit(1); + } + if (mav) + free(mav); + if (bp) + free(bp); + Lst_Replace(cmdNode, NULL); + +#ifdef USE_META + if (useMeta) { + meta_compat_parent(); + } +#endif + + /* + * The child is off and running. Now all we can do is wait... + */ + while (1) { + + while ((retstat = wait(&reason)) != cpid) { + if (retstat > 0) + JobReapChild(retstat, reason, FALSE); /* not ours? */ + if (retstat == -1 && errno != EINTR) { + break; + } + } + + if (retstat > -1) { + if (WIFSTOPPED(reason)) { + status = WSTOPSIG(reason); /* stopped */ + } else if (WIFEXITED(reason)) { + status = WEXITSTATUS(reason); /* exited */ +#if defined(USE_META) && defined(USE_FILEMON_ONCE) + if (useMeta) { + meta_cmd_finish(NULL); + } +#endif + if (status != 0) { + if (DEBUG(ERROR)) { + fprintf(debug_file, "\n*** Failed target: %s\n*** Failed command: ", + gn->name); + for (cp = cmd; *cp; ) { + if (isspace((unsigned char)*cp)) { + fprintf(debug_file, " "); + while (isspace((unsigned char)*cp)) + cp++; + } else { + fprintf(debug_file, "%c", *cp); + cp++; + } + } + fprintf(debug_file, "\n"); + } + printf("*** Error code %d", status); + } + } else { + status = WTERMSIG(reason); /* signaled */ + printf("*** Signal %d", status); + } + + + if (!WIFEXITED(reason) || (status != 0)) { + if (errCheck) { +#ifdef USE_META + if (useMeta) { + meta_job_error(NULL, gn, 0, status); + } +#endif + gn->made = ERROR; + if (keepgoing) { + /* + * Abort the current target, but let others + * continue. + */ + printf(" (continuing)\n"); + } + } else { + /* + * Continue executing commands for this target. + * If we return 0, this will happen... + */ + printf(" (ignored)\n"); + status = 0; + } + } + break; + } else { + Fatal("error in wait: %d: %s", retstat, strerror(errno)); + /*NOTREACHED*/ + } + } + free(cmdStart); + + return (status); +} + +/*- + *----------------------------------------------------------------------- + * Compat_Make -- + * Make a target. + * + * Input: + * gnp The node to make + * pgnp Parent to abort if necessary + * + * Results: + * 0 + * + * Side Effects: + * If an error is detected and not being ignored, the process exits. + * + *----------------------------------------------------------------------- + */ +int +Compat_Make(void *gnp, void *pgnp) +{ + GNode *gn = (GNode *)gnp; + GNode *pgn = (GNode *)pgnp; + + if (!meta[0]) /* we came here from jobs */ + Compat_Init(); + if (gn->made == UNMADE && (gn == pgn || (pgn->type & OP_MADE) == 0)) { + /* + * First mark ourselves to be made, then apply whatever transformations + * the suffix module thinks are necessary. Once that's done, we can + * descend and make all our children. If any of them has an error + * but the -k flag was given, our 'make' field will be set FALSE again. + * This is our signal to not attempt to do anything but abort our + * parent as well. + */ + gn->flags |= REMAKE; + gn->made = BEINGMADE; + if ((gn->type & OP_MADE) == 0) + Suff_FindDeps(gn); + Lst_ForEach(gn->children, Compat_Make, gn); + if ((gn->flags & REMAKE) == 0) { + gn->made = ABORTED; + pgn->flags &= ~REMAKE; + goto cohorts; + } + + if (Lst_Member(gn->iParents, pgn) != NULL) { + char *p1; + Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); + if (p1) + free(p1); + } + + /* + * All the children were made ok. Now cmgn->mtime contains the + * modification time of the newest child, we need to find out if we + * exist and when we were modified last. The criteria for datedness + * are defined by the Make_OODate function. + */ + if (DEBUG(MAKE)) { + fprintf(debug_file, "Examining %s...", gn->name); + } + if (! Make_OODate(gn)) { + gn->made = UPTODATE; + if (DEBUG(MAKE)) { + fprintf(debug_file, "up-to-date.\n"); + } + goto cohorts; + } else if (DEBUG(MAKE)) { + fprintf(debug_file, "out-of-date.\n"); + } + + /* + * If the user is just seeing if something is out-of-date, exit now + * to tell him/her "yes". + */ + if (queryFlag) { + exit(1); + } + + /* + * We need to be re-made. We also have to make sure we've got a $? + * variable. To be nice, we also define the $> variable using + * Make_DoAllVar(). + */ + Make_DoAllVar(gn); + + /* + * Alter our type to tell if errors should be ignored or things + * should not be printed so CompatRunCommand knows what to do. + */ + if (Targ_Ignore(gn)) { + gn->type |= OP_IGNORE; + } + if (Targ_Silent(gn)) { + gn->type |= OP_SILENT; + } + + if (Job_CheckCommands(gn, Fatal)) { + /* + * Our commands are ok, but we still have to worry about the -t + * flag... + */ + if (!touchFlag || (gn->type & OP_MAKE)) { + curTarg = gn; +#ifdef USE_META + if (useMeta && !NoExecute(gn)) { + meta_job_start(NULL, gn); + } +#endif + Lst_ForEach(gn->commands, CompatRunCommand, gn); + curTarg = NULL; + } else { + Job_Touch(gn, gn->type & OP_SILENT); + } + } else { + gn->made = ERROR; + } +#ifdef USE_META + if (useMeta && !NoExecute(gn)) { + meta_job_finish(NULL); + } +#endif + + if (gn->made != ERROR) { + /* + * If the node was made successfully, mark it so, update + * its modification time and timestamp all its parents. Note + * that for .ZEROTIME targets, the timestamping isn't done. + * This is to keep its state from affecting that of its parent. + */ + gn->made = MADE; + pgn->flags |= Make_Recheck(gn) == 0 ? FORCE : 0; + if (!(gn->type & OP_EXEC)) { + pgn->flags |= CHILDMADE; + Make_TimeStamp(pgn, gn); + } + } else if (keepgoing) { + pgn->flags &= ~REMAKE; + } else { + PrintOnError(gn, "\n\nStop."); + exit(1); + } + } else if (gn->made == ERROR) { + /* + * Already had an error when making this beastie. Tell the parent + * to abort. + */ + pgn->flags &= ~REMAKE; + } else { + if (Lst_Member(gn->iParents, pgn) != NULL) { + char *p1; + Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), pgn, 0); + if (p1) + free(p1); + } + switch(gn->made) { + case BEINGMADE: + Error("Graph cycles through %s", gn->name); + gn->made = ERROR; + pgn->flags &= ~REMAKE; + break; + case MADE: + if ((gn->type & OP_EXEC) == 0) { + pgn->flags |= CHILDMADE; + Make_TimeStamp(pgn, gn); + } + break; + case UPTODATE: + if ((gn->type & OP_EXEC) == 0) { + Make_TimeStamp(pgn, gn); + } + break; + default: + break; + } + } + +cohorts: + Lst_ForEach(gn->cohorts, Compat_Make, pgnp); + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Compat_Run -- + * Initialize this mode and start making. + * + * Input: + * targs List of target nodes to re-create + * + * Results: + * None. + * + * Side Effects: + * Guess what? + * + *----------------------------------------------------------------------- + */ +void +Compat_Run(Lst targs) +{ + GNode *gn = NULL;/* Current root target */ + int errors; /* Number of targets not remade due to errors */ + + Compat_Init(); + + if (bmake_signal(SIGINT, SIG_IGN) != SIG_IGN) { + bmake_signal(SIGINT, CompatInterrupt); + } + if (bmake_signal(SIGTERM, SIG_IGN) != SIG_IGN) { + bmake_signal(SIGTERM, CompatInterrupt); + } + if (bmake_signal(SIGHUP, SIG_IGN) != SIG_IGN) { + bmake_signal(SIGHUP, CompatInterrupt); + } + if (bmake_signal(SIGQUIT, SIG_IGN) != SIG_IGN) { + bmake_signal(SIGQUIT, CompatInterrupt); + } + + ENDNode = Targ_FindNode(".END", TARG_CREATE); + ENDNode->type = OP_SPECIAL; + /* + * If the user has defined a .BEGIN target, execute the commands attached + * to it. + */ + if (!queryFlag) { + gn = Targ_FindNode(".BEGIN", TARG_NOCREATE); + if (gn != NULL) { + Compat_Make(gn, gn); + if (gn->made == ERROR) { + PrintOnError(gn, "\n\nStop."); + exit(1); + } + } + } + + /* + * Expand .USE nodes right now, because they can modify the structure + * of the tree. + */ + Make_ExpandUse(targs); + + /* + * For each entry in the list of targets to create, call Compat_Make on + * it to create the thing. Compat_Make will leave the 'made' field of gn + * in one of several states: + * UPTODATE gn was already up-to-date + * MADE gn was recreated successfully + * ERROR An error occurred while gn was being created + * ABORTED gn was not remade because one of its inferiors + * could not be made due to errors. + */ + errors = 0; + while (!Lst_IsEmpty (targs)) { + gn = (GNode *)Lst_DeQueue(targs); + Compat_Make(gn, gn); + + if (gn->made == UPTODATE) { + printf("`%s' is up to date.\n", gn->name); + } else if (gn->made == ABORTED) { + printf("`%s' not remade because of errors.\n", gn->name); + errors += 1; + } + } + + /* + * If the user has defined a .END target, run its commands. + */ + if (errors == 0) { + Compat_Make(ENDNode, ENDNode); + if (gn->made == ERROR) { + PrintOnError(gn, "\n\nStop."); + exit(1); + } + } +} diff --git a/buildrump.sh/src/usr.bin/make/cond.c b/buildrump.sh/src/usr.bin/make/cond.c new file mode 100644 index 00000000..b05a56cb --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/cond.c @@ -0,0 +1,1434 @@ +/* $NetBSD: cond.c,v 1.68 2015/05/05 21:51:09 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: cond.c,v 1.68 2015/05/05 21:51:09 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cond.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: cond.c,v 1.68 2015/05/05 21:51:09 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * cond.c -- + * Functions to handle conditionals in a makefile. + * + * Interface: + * Cond_Eval Evaluate the conditional in the passed line. + * + */ + +#include <ctype.h> +#include <errno.h> /* For strtoul() error checking */ + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "buf.h" + +/* + * The parsing of conditional expressions is based on this grammar: + * E -> F || E + * E -> F + * F -> T && F + * F -> T + * T -> defined(variable) + * T -> make(target) + * T -> exists(file) + * T -> empty(varspec) + * T -> target(name) + * T -> commands(name) + * T -> symbol + * T -> $(varspec) op value + * T -> $(varspec) == "string" + * T -> $(varspec) != "string" + * T -> "string" + * T -> ( E ) + * T -> ! T + * op -> == | != | > | < | >= | <= + * + * 'symbol' is some other symbol to which the default function (condDefProc) + * is applied. + * + * Tokens are scanned from the 'condExpr' string. The scanner (CondToken) + * will return TOK_AND for '&' and '&&', TOK_OR for '|' and '||', + * TOK_NOT for '!', TOK_LPAREN for '(', TOK_RPAREN for ')' and will evaluate + * the other terminal symbols, using either the default function or the + * function given in the terminal, and return the result as either TOK_TRUE + * or TOK_FALSE. + * + * TOK_FALSE is 0 and TOK_TRUE 1 so we can directly assign C comparisons. + * + * All Non-Terminal functions (CondE, CondF and CondT) return TOK_ERROR on + * error. + */ +typedef enum { + TOK_FALSE = 0, TOK_TRUE = 1, TOK_AND, TOK_OR, TOK_NOT, + TOK_LPAREN, TOK_RPAREN, TOK_EOF, TOK_NONE, TOK_ERROR +} Token; + +/*- + * Structures to handle elegantly the different forms of #if's. The + * last two fields are stored in condInvert and condDefProc, respectively. + */ +static void CondPushBack(Token); +static int CondGetArg(char **, char **, const char *); +static Boolean CondDoDefined(int, const char *); +static int CondStrMatch(const void *, const void *); +static Boolean CondDoMake(int, const char *); +static Boolean CondDoExists(int, const char *); +static Boolean CondDoTarget(int, const char *); +static Boolean CondDoCommands(int, const char *); +static Boolean CondCvtArg(char *, double *); +static Token CondToken(Boolean); +static Token CondT(Boolean); +static Token CondF(Boolean); +static Token CondE(Boolean); +static int do_Cond_EvalExpression(Boolean *); + +static const struct If { + const char *form; /* Form of if */ + int formlen; /* Length of form */ + Boolean doNot; /* TRUE if default function should be negated */ + Boolean (*defProc)(int, const char *); /* Default function to apply */ +} ifs[] = { + { "def", 3, FALSE, CondDoDefined }, + { "ndef", 4, TRUE, CondDoDefined }, + { "make", 4, FALSE, CondDoMake }, + { "nmake", 5, TRUE, CondDoMake }, + { "", 0, FALSE, CondDoDefined }, + { NULL, 0, FALSE, NULL } +}; + +static const struct If *if_info; /* Info for current statement */ +static char *condExpr; /* The expression to parse */ +static Token condPushBack=TOK_NONE; /* Single push-back token used in + * parsing */ + +static unsigned int cond_depth = 0; /* current .if nesting level */ +static unsigned int cond_min_depth = 0; /* depth at makefile open */ + +/* + * Indicate when we should be strict about lhs of comparisons. + * TRUE when Cond_EvalExpression is called from Cond_Eval (.if etc) + * FALSE when Cond_EvalExpression is called from var.c:ApplyModifiers + * since lhs is already expanded and we cannot tell if + * it was a variable reference or not. + */ +static Boolean lhsStrict; + +static int +istoken(const char *str, const char *tok, size_t len) +{ + return strncmp(str, tok, len) == 0 && !isalpha((unsigned char)str[len]); +} + +/*- + *----------------------------------------------------------------------- + * CondPushBack -- + * Push back the most recent token read. We only need one level of + * this, so the thing is just stored in 'condPushback'. + * + * Input: + * t Token to push back into the "stream" + * + * Results: + * None. + * + * Side Effects: + * condPushback is overwritten. + * + *----------------------------------------------------------------------- + */ +static void +CondPushBack(Token t) +{ + condPushBack = t; +} + +/*- + *----------------------------------------------------------------------- + * CondGetArg -- + * Find the argument of a built-in function. + * + * Input: + * parens TRUE if arg should be bounded by parens + * + * Results: + * The length of the argument and the address of the argument. + * + * Side Effects: + * The pointer is set to point to the closing parenthesis of the + * function call. + * + *----------------------------------------------------------------------- + */ +static int +CondGetArg(char **linePtr, char **argPtr, const char *func) +{ + char *cp; + int argLen; + Buffer buf; + int paren_depth; + char ch; + + cp = *linePtr; + if (func != NULL) + /* Skip opening '(' - verfied by caller */ + cp++; + + if (*cp == '\0') { + /* + * No arguments whatsoever. Because 'make' and 'defined' aren't really + * "reserved words", we don't print a message. I think this is better + * than hitting the user with a warning message every time s/he uses + * the word 'make' or 'defined' at the beginning of a symbol... + */ + *argPtr = NULL; + return (0); + } + + while (*cp == ' ' || *cp == '\t') { + cp++; + } + + /* + * Create a buffer for the argument and start it out at 16 characters + * long. Why 16? Why not? + */ + Buf_Init(&buf, 16); + + paren_depth = 0; + for (;;) { + ch = *cp; + if (ch == 0 || ch == ' ' || ch == '\t') + break; + if ((ch == '&' || ch == '|') && paren_depth == 0) + break; + if (*cp == '$') { + /* + * Parse the variable spec and install it as part of the argument + * if it's valid. We tell Var_Parse to complain on an undefined + * variable, so we don't do it too. Nor do we return an error, + * though perhaps we should... + */ + char *cp2; + int len; + void *freeIt; + + cp2 = Var_Parse(cp, VAR_CMD, TRUE, &len, &freeIt); + Buf_AddBytes(&buf, strlen(cp2), cp2); + if (freeIt) + free(freeIt); + cp += len; + continue; + } + if (ch == '(') + paren_depth++; + else + if (ch == ')' && --paren_depth < 0) + break; + Buf_AddByte(&buf, *cp); + cp++; + } + + *argPtr = Buf_GetAll(&buf, &argLen); + Buf_Destroy(&buf, FALSE); + + while (*cp == ' ' || *cp == '\t') { + cp++; + } + + if (func != NULL && *cp++ != ')') { + Parse_Error(PARSE_WARNING, "Missing closing parenthesis for %s()", + func); + return (0); + } + + *linePtr = cp; + return (argLen); +} + +/*- + *----------------------------------------------------------------------- + * CondDoDefined -- + * Handle the 'defined' function for conditionals. + * + * Results: + * TRUE if the given variable is defined. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoDefined(int argLen MAKE_ATTR_UNUSED, const char *arg) +{ + char *p1; + Boolean result; + + if (Var_Value(arg, VAR_CMD, &p1) != NULL) { + result = TRUE; + } else { + result = FALSE; + } + if (p1) + free(p1); + return (result); +} + +/*- + *----------------------------------------------------------------------- + * CondStrMatch -- + * Front-end for Str_Match so it returns 0 on match and non-zero + * on mismatch. Callback function for CondDoMake via Lst_Find + * + * Results: + * 0 if string matches pattern + * + * Side Effects: + * None + * + *----------------------------------------------------------------------- + */ +static int +CondStrMatch(const void *string, const void *pattern) +{ + return(!Str_Match(string, pattern)); +} + +/*- + *----------------------------------------------------------------------- + * CondDoMake -- + * Handle the 'make' function for conditionals. + * + * Results: + * TRUE if the given target is being made. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoMake(int argLen MAKE_ATTR_UNUSED, const char *arg) +{ + return Lst_Find(create, arg, CondStrMatch) != NULL; +} + +/*- + *----------------------------------------------------------------------- + * CondDoExists -- + * See if the given file exists. + * + * Results: + * TRUE if the file exists and FALSE if it does not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoExists(int argLen MAKE_ATTR_UNUSED, const char *arg) +{ + Boolean result; + char *path; + + path = Dir_FindFile(arg, dirSearchPath); + if (DEBUG(COND)) { + fprintf(debug_file, "exists(%s) result is \"%s\"\n", + arg, path ? path : ""); + } + if (path != NULL) { + result = TRUE; + free(path); + } else { + result = FALSE; + } + return (result); +} + +/*- + *----------------------------------------------------------------------- + * CondDoTarget -- + * See if the given node exists and is an actual target. + * + * Results: + * TRUE if the node exists as a target and FALSE if it does not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoTarget(int argLen MAKE_ATTR_UNUSED, const char *arg) +{ + GNode *gn; + + gn = Targ_FindNode(arg, TARG_NOCREATE); + return (gn != NULL) && !OP_NOP(gn->type); +} + +/*- + *----------------------------------------------------------------------- + * CondDoCommands -- + * See if the given node exists and is an actual target with commands + * associated with it. + * + * Results: + * TRUE if the node exists as a target and has commands associated with + * it and FALSE if it does not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +CondDoCommands(int argLen MAKE_ATTR_UNUSED, const char *arg) +{ + GNode *gn; + + gn = Targ_FindNode(arg, TARG_NOCREATE); + return (gn != NULL) && !OP_NOP(gn->type) && !Lst_IsEmpty(gn->commands); +} + +/*- + *----------------------------------------------------------------------- + * CondCvtArg -- + * Convert the given number into a double. + * We try a base 10 or 16 integer conversion first, if that fails + * then we try a floating point conversion instead. + * + * Results: + * Sets 'value' to double value of string. + * Returns 'true' if the convertion suceeded + * + *----------------------------------------------------------------------- + */ +static Boolean +CondCvtArg(char *str, double *value) +{ + char *eptr, ech; + unsigned long l_val; + double d_val; + + errno = 0; + l_val = strtoul(str, &eptr, str[1] == 'x' ? 16 : 10); + ech = *eptr; + if (ech == 0 && errno != ERANGE) { + d_val = str[0] == '-' ? -(double)-l_val : (double)l_val; + } else { + if (ech != 0 && ech != '.' && ech != 'e' && ech != 'E') + return FALSE; + d_val = strtod(str, &eptr); + if (*eptr) + return FALSE; + } + + *value = d_val; + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * CondGetString -- + * Get a string from a variable reference or an optionally quoted + * string. This is called for the lhs and rhs of string compares. + * + * Results: + * Sets freeIt if needed, + * Sets quoted if string was quoted, + * Returns NULL on error, + * else returns string - absent any quotes. + * + * Side Effects: + * Moves condExpr to end of this token. + * + * + *----------------------------------------------------------------------- + */ +/* coverity:[+alloc : arg-*2] */ +static char * +CondGetString(Boolean doEval, Boolean *quoted, void **freeIt, Boolean strictLHS) +{ + Buffer buf; + char *cp; + char *str; + int len; + int qt; + char *start; + + Buf_Init(&buf, 0); + str = NULL; + *freeIt = NULL; + *quoted = qt = *condExpr == '"' ? 1 : 0; + if (qt) + condExpr++; + for (start = condExpr; *condExpr && str == NULL; condExpr++) { + switch (*condExpr) { + case '\\': + if (condExpr[1] != '\0') { + condExpr++; + Buf_AddByte(&buf, *condExpr); + } + break; + case '"': + if (qt) { + condExpr++; /* we don't want the quotes */ + goto got_str; + } else + Buf_AddByte(&buf, *condExpr); /* likely? */ + break; + case ')': + case '!': + case '=': + case '>': + case '<': + case ' ': + case '\t': + if (!qt) + goto got_str; + else + Buf_AddByte(&buf, *condExpr); + break; + case '$': + /* if we are in quotes, then an undefined variable is ok */ + str = Var_Parse(condExpr, VAR_CMD, (qt ? 0 : doEval), + &len, freeIt); + if (str == var_Error) { + if (*freeIt) { + free(*freeIt); + *freeIt = NULL; + } + /* + * Even if !doEval, we still report syntax errors, which + * is what getting var_Error back with !doEval means. + */ + str = NULL; + goto cleanup; + } + condExpr += len; + /* + * If the '$' was first char (no quotes), and we are + * followed by space, the operator or end of expression, + * we are done. + */ + if ((condExpr == start + len) && + (*condExpr == '\0' || + isspace((unsigned char) *condExpr) || + strchr("!=><)", *condExpr))) { + goto cleanup; + } + /* + * Nope, we better copy str to buf + */ + for (cp = str; *cp; cp++) { + Buf_AddByte(&buf, *cp); + } + if (*freeIt) { + free(*freeIt); + *freeIt = NULL; + } + str = NULL; /* not finished yet */ + condExpr--; /* don't skip over next char */ + break; + default: + if (strictLHS && !qt && *start != '$' && + !isdigit((unsigned char) *start)) { + /* lhs must be quoted, a variable reference or number */ + if (*freeIt) { + free(*freeIt); + *freeIt = NULL; + } + str = NULL; + goto cleanup; + } + Buf_AddByte(&buf, *condExpr); + break; + } + } + got_str: + str = Buf_GetAll(&buf, NULL); + *freeIt = str; + cleanup: + Buf_Destroy(&buf, FALSE); + return str; +} + +/*- + *----------------------------------------------------------------------- + * CondToken -- + * Return the next token from the input. + * + * Results: + * A Token for the next lexical token in the stream. + * + * Side Effects: + * condPushback will be set back to TOK_NONE if it is used. + * + *----------------------------------------------------------------------- + */ +static Token +compare_expression(Boolean doEval) +{ + Token t; + char *lhs; + char *rhs; + char *op; + void *lhsFree; + void *rhsFree; + Boolean lhsQuoted; + Boolean rhsQuoted; + double left, right; + + t = TOK_ERROR; + rhs = NULL; + lhsFree = rhsFree = FALSE; + lhsQuoted = rhsQuoted = FALSE; + + /* + * Parse the variable spec and skip over it, saving its + * value in lhs. + */ + lhs = CondGetString(doEval, &lhsQuoted, &lhsFree, lhsStrict); + if (!lhs) + goto done; + + /* + * Skip whitespace to get to the operator + */ + while (isspace((unsigned char) *condExpr)) + condExpr++; + + /* + * Make sure the operator is a valid one. If it isn't a + * known relational operator, pretend we got a + * != 0 comparison. + */ + op = condExpr; + switch (*condExpr) { + case '!': + case '=': + case '<': + case '>': + if (condExpr[1] == '=') { + condExpr += 2; + } else { + condExpr += 1; + } + break; + default: + if (!doEval) { + t = TOK_FALSE; + goto done; + } + /* For .ifxxx "..." check for non-empty string. */ + if (lhsQuoted) { + t = lhs[0] != 0; + goto done; + } + /* For .ifxxx <number> compare against zero */ + if (CondCvtArg(lhs, &left)) { + t = left != 0.0; + goto done; + } + /* For .if ${...} check for non-empty string (defProc is ifdef). */ + if (if_info->form[0] == 0) { + t = lhs[0] != 0; + goto done; + } + /* Otherwise action default test ... */ + t = if_info->defProc(strlen(lhs), lhs) != if_info->doNot; + goto done; + } + + while (isspace((unsigned char)*condExpr)) + condExpr++; + + if (*condExpr == '\0') { + Parse_Error(PARSE_WARNING, + "Missing right-hand-side of operator"); + goto done; + } + + rhs = CondGetString(doEval, &rhsQuoted, &rhsFree, FALSE); + if (!rhs) + goto done; + + if (rhsQuoted || lhsQuoted) { +do_string_compare: + if (((*op != '!') && (*op != '=')) || (op[1] != '=')) { + Parse_Error(PARSE_WARNING, + "String comparison operator should be either == or !="); + goto done; + } + + if (DEBUG(COND)) { + fprintf(debug_file, "lhs = \"%s\", rhs = \"%s\", op = %.2s\n", + lhs, rhs, op); + } + /* + * Null-terminate rhs and perform the comparison. + * t is set to the result. + */ + if (*op == '=') { + t = strcmp(lhs, rhs) == 0; + } else { + t = strcmp(lhs, rhs) != 0; + } + } else { + /* + * rhs is either a float or an integer. Convert both the + * lhs and the rhs to a double and compare the two. + */ + + if (!CondCvtArg(lhs, &left) || !CondCvtArg(rhs, &right)) + goto do_string_compare; + + if (DEBUG(COND)) { + fprintf(debug_file, "left = %f, right = %f, op = %.2s\n", left, + right, op); + } + switch(op[0]) { + case '!': + if (op[1] != '=') { + Parse_Error(PARSE_WARNING, + "Unknown operator"); + goto done; + } + t = (left != right); + break; + case '=': + if (op[1] != '=') { + Parse_Error(PARSE_WARNING, + "Unknown operator"); + goto done; + } + t = (left == right); + break; + case '<': + if (op[1] == '=') { + t = (left <= right); + } else { + t = (left < right); + } + break; + case '>': + if (op[1] == '=') { + t = (left >= right); + } else { + t = (left > right); + } + break; + } + } + +done: + if (lhsFree) + free(lhsFree); + if (rhsFree) + free(rhsFree); + return t; +} + +static int +get_mpt_arg(char **linePtr, char **argPtr, const char *func MAKE_ATTR_UNUSED) +{ + /* + * Use Var_Parse to parse the spec in parens and return + * TOK_TRUE if the resulting string is empty. + */ + int length; + void *freeIt; + char *val; + char *cp = *linePtr; + + /* We do all the work here and return the result as the length */ + *argPtr = NULL; + + val = Var_Parse(cp - 1, VAR_CMD, FALSE, &length, &freeIt); + /* + * Advance *linePtr to beyond the closing ). Note that + * we subtract one because 'length' is calculated from 'cp - 1'. + */ + *linePtr = cp - 1 + length; + + if (val == var_Error) { + free(freeIt); + return -1; + } + + /* A variable is empty when it just contains spaces... 4/15/92, christos */ + while (isspace(*(unsigned char *)val)) + val++; + + /* + * For consistency with the other functions we can't generate the + * true/false here. + */ + length = *val ? 2 : 1; + if (freeIt) + free(freeIt); + return length; +} + +static Boolean +CondDoEmpty(int arglen, const char *arg MAKE_ATTR_UNUSED) +{ + return arglen == 1; +} + +static Token +compare_function(Boolean doEval) +{ + static const struct fn_def { + const char *fn_name; + int fn_name_len; + int (*fn_getarg)(char **, char **, const char *); + Boolean (*fn_proc)(int, const char *); + } fn_defs[] = { + { "defined", 7, CondGetArg, CondDoDefined }, + { "make", 4, CondGetArg, CondDoMake }, + { "exists", 6, CondGetArg, CondDoExists }, + { "empty", 5, get_mpt_arg, CondDoEmpty }, + { "target", 6, CondGetArg, CondDoTarget }, + { "commands", 8, CondGetArg, CondDoCommands }, + { NULL, 0, NULL, NULL }, + }; + const struct fn_def *fn_def; + Token t; + char *arg = NULL; + int arglen; + char *cp = condExpr; + char *cp1; + + for (fn_def = fn_defs; fn_def->fn_name != NULL; fn_def++) { + if (!istoken(cp, fn_def->fn_name, fn_def->fn_name_len)) + continue; + cp += fn_def->fn_name_len; + /* There can only be whitespace before the '(' */ + while (isspace(*(unsigned char *)cp)) + cp++; + if (*cp != '(') + break; + + arglen = fn_def->fn_getarg(&cp, &arg, fn_def->fn_name); + if (arglen <= 0) { + condExpr = cp; + return arglen < 0 ? TOK_ERROR : TOK_FALSE; + } + /* Evaluate the argument using the required function. */ + t = !doEval || fn_def->fn_proc(arglen, arg); + if (arg) + free(arg); + condExpr = cp; + return t; + } + + /* Push anything numeric through the compare expression */ + cp = condExpr; + if (isdigit((unsigned char)cp[0]) || strchr("+-", cp[0])) + return compare_expression(doEval); + + /* + * Most likely we have a naked token to apply the default function to. + * However ".if a == b" gets here when the "a" is unquoted and doesn't + * start with a '$'. This surprises people. + * If what follows the function argument is a '=' or '!' then the syntax + * would be invalid if we did "defined(a)" - so instead treat as an + * expression. + */ + arglen = CondGetArg(&cp, &arg, NULL); + for (cp1 = cp; isspace(*(unsigned char *)cp1); cp1++) + continue; + if (*cp1 == '=' || *cp1 == '!') + return compare_expression(doEval); + condExpr = cp; + + /* + * Evaluate the argument using the default function. + * This path always treats .if as .ifdef. To get here the character + * after .if must have been taken literally, so the argument cannot + * be empty - even if it contained a variable expansion. + */ + t = !doEval || if_info->defProc(arglen, arg) != if_info->doNot; + if (arg) + free(arg); + return t; +} + +static Token +CondToken(Boolean doEval) +{ + Token t; + + t = condPushBack; + if (t != TOK_NONE) { + condPushBack = TOK_NONE; + return t; + } + + while (*condExpr == ' ' || *condExpr == '\t') { + condExpr++; + } + + switch (*condExpr) { + + case '(': + condExpr++; + return TOK_LPAREN; + + case ')': + condExpr++; + return TOK_RPAREN; + + case '|': + if (condExpr[1] == '|') { + condExpr++; + } + condExpr++; + return TOK_OR; + + case '&': + if (condExpr[1] == '&') { + condExpr++; + } + condExpr++; + return TOK_AND; + + case '!': + condExpr++; + return TOK_NOT; + + case '#': + case '\n': + case '\0': + return TOK_EOF; + + case '"': + case '$': + return compare_expression(doEval); + + default: + return compare_function(doEval); + } +} + +/*- + *----------------------------------------------------------------------- + * CondT -- + * Parse a single term in the expression. This consists of a terminal + * symbol or TOK_NOT and a terminal symbol (not including the binary + * operators): + * T -> defined(variable) | make(target) | exists(file) | symbol + * T -> ! T | ( E ) + * + * Results: + * TOK_TRUE, TOK_FALSE or TOK_ERROR. + * + * Side Effects: + * Tokens are consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondT(Boolean doEval) +{ + Token t; + + t = CondToken(doEval); + + if (t == TOK_EOF) { + /* + * If we reached the end of the expression, the expression + * is malformed... + */ + t = TOK_ERROR; + } else if (t == TOK_LPAREN) { + /* + * T -> ( E ) + */ + t = CondE(doEval); + if (t != TOK_ERROR) { + if (CondToken(doEval) != TOK_RPAREN) { + t = TOK_ERROR; + } + } + } else if (t == TOK_NOT) { + t = CondT(doEval); + if (t == TOK_TRUE) { + t = TOK_FALSE; + } else if (t == TOK_FALSE) { + t = TOK_TRUE; + } + } + return (t); +} + +/*- + *----------------------------------------------------------------------- + * CondF -- + * Parse a conjunctive factor (nice name, wot?) + * F -> T && F | T + * + * Results: + * TOK_TRUE, TOK_FALSE or TOK_ERROR + * + * Side Effects: + * Tokens are consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondF(Boolean doEval) +{ + Token l, o; + + l = CondT(doEval); + if (l != TOK_ERROR) { + o = CondToken(doEval); + + if (o == TOK_AND) { + /* + * F -> T && F + * + * If T is TOK_FALSE, the whole thing will be TOK_FALSE, but we have to + * parse the r.h.s. anyway (to throw it away). + * If T is TOK_TRUE, the result is the r.h.s., be it an TOK_ERROR or no. + */ + if (l == TOK_TRUE) { + l = CondF(doEval); + } else { + (void)CondF(FALSE); + } + } else { + /* + * F -> T + */ + CondPushBack(o); + } + } + return (l); +} + +/*- + *----------------------------------------------------------------------- + * CondE -- + * Main expression production. + * E -> F || E | F + * + * Results: + * TOK_TRUE, TOK_FALSE or TOK_ERROR. + * + * Side Effects: + * Tokens are, of course, consumed. + * + *----------------------------------------------------------------------- + */ +static Token +CondE(Boolean doEval) +{ + Token l, o; + + l = CondF(doEval); + if (l != TOK_ERROR) { + o = CondToken(doEval); + + if (o == TOK_OR) { + /* + * E -> F || E + * + * A similar thing occurs for ||, except that here we make sure + * the l.h.s. is TOK_FALSE before we bother to evaluate the r.h.s. + * Once again, if l is TOK_FALSE, the result is the r.h.s. and once + * again if l is TOK_TRUE, we parse the r.h.s. to throw it away. + */ + if (l == TOK_FALSE) { + l = CondE(doEval); + } else { + (void)CondE(FALSE); + } + } else { + /* + * E -> F + */ + CondPushBack(o); + } + } + return (l); +} + +/*- + *----------------------------------------------------------------------- + * Cond_EvalExpression -- + * Evaluate an expression in the passed line. The expression + * consists of &&, ||, !, make(target), defined(variable) + * and parenthetical groupings thereof. + * + * Results: + * COND_PARSE if the condition was valid grammatically + * COND_INVALID if not a valid conditional. + * + * (*value) is set to the boolean value of the condition + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +Cond_EvalExpression(const struct If *info, char *line, Boolean *value, int eprint, Boolean strictLHS) +{ + static const struct If *dflt_info; + const struct If *sv_if_info = if_info; + char *sv_condExpr = condExpr; + Token sv_condPushBack = condPushBack; + int rval; + + lhsStrict = strictLHS; + + while (*line == ' ' || *line == '\t') + line++; + + if (info == NULL && (info = dflt_info) == NULL) { + /* Scan for the entry for .if - it can't be first */ + for (info = ifs; ; info++) + if (info->form[0] == 0) + break; + dflt_info = info; + } + + if_info = info != NULL ? info : ifs + 4; + condExpr = line; + condPushBack = TOK_NONE; + + rval = do_Cond_EvalExpression(value); + + if (rval == COND_INVALID && eprint) + Parse_Error(PARSE_FATAL, "Malformed conditional (%s)", line); + + if_info = sv_if_info; + condExpr = sv_condExpr; + condPushBack = sv_condPushBack; + + return rval; +} + +static int +do_Cond_EvalExpression(Boolean *value) +{ + + switch (CondE(TRUE)) { + case TOK_TRUE: + if (CondToken(TRUE) == TOK_EOF) { + *value = TRUE; + return COND_PARSE; + } + break; + case TOK_FALSE: + if (CondToken(TRUE) == TOK_EOF) { + *value = FALSE; + return COND_PARSE; + } + break; + default: + case TOK_ERROR: + break; + } + + return COND_INVALID; +} + + +/*- + *----------------------------------------------------------------------- + * Cond_Eval -- + * Evaluate the conditional in the passed line. The line + * looks like this: + * .<cond-type> <expr> + * where <cond-type> is any of if, ifmake, ifnmake, ifdef, + * ifndef, elif, elifmake, elifnmake, elifdef, elifndef + * and <expr> consists of &&, ||, !, make(target), defined(variable) + * and parenthetical groupings thereof. + * + * Input: + * line Line to parse + * + * Results: + * COND_PARSE if should parse lines after the conditional + * COND_SKIP if should skip lines after the conditional + * COND_INVALID if not a valid conditional. + * + * Side Effects: + * None. + * + * Note that the states IF_ACTIVE and ELSE_ACTIVE are only different in order + * to detect splurious .else lines (as are SKIP_TO_ELSE and SKIP_TO_ENDIF) + * otherwise .else could be treated as '.elif 1'. + * + *----------------------------------------------------------------------- + */ +int +Cond_Eval(char *line) +{ +#define MAXIF 128 /* maximum depth of .if'ing */ +#define MAXIF_BUMP 32 /* how much to grow by */ + enum if_states { + IF_ACTIVE, /* .if or .elif part active */ + ELSE_ACTIVE, /* .else part active */ + SEARCH_FOR_ELIF, /* searching for .elif/else to execute */ + SKIP_TO_ELSE, /* has been true, but not seen '.else' */ + SKIP_TO_ENDIF /* nothing else to execute */ + }; + static enum if_states *cond_state = NULL; + static unsigned int max_if_depth = MAXIF; + + const struct If *ifp; + Boolean isElif; + Boolean value; + int level; /* Level at which to report errors. */ + enum if_states state; + + level = PARSE_FATAL; + if (!cond_state) { + cond_state = bmake_malloc(max_if_depth * sizeof(*cond_state)); + cond_state[0] = IF_ACTIVE; + } + /* skip leading character (the '.') and any whitespace */ + for (line++; *line == ' ' || *line == '\t'; line++) + continue; + + /* Find what type of if we're dealing with. */ + if (line[0] == 'e') { + if (line[1] != 'l') { + if (!istoken(line + 1, "ndif", 4)) + return COND_INVALID; + /* End of conditional section */ + if (cond_depth == cond_min_depth) { + Parse_Error(level, "if-less endif"); + return COND_PARSE; + } + /* Return state for previous conditional */ + cond_depth--; + return cond_state[cond_depth] <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; + } + + /* Quite likely this is 'else' or 'elif' */ + line += 2; + if (istoken(line, "se", 2)) { + /* It is else... */ + if (cond_depth == cond_min_depth) { + Parse_Error(level, "if-less else"); + return COND_PARSE; + } + + state = cond_state[cond_depth]; + switch (state) { + case SEARCH_FOR_ELIF: + state = ELSE_ACTIVE; + break; + case ELSE_ACTIVE: + case SKIP_TO_ENDIF: + Parse_Error(PARSE_WARNING, "extra else"); + /* FALLTHROUGH */ + default: + case IF_ACTIVE: + case SKIP_TO_ELSE: + state = SKIP_TO_ENDIF; + break; + } + cond_state[cond_depth] = state; + return state <= ELSE_ACTIVE ? COND_PARSE : COND_SKIP; + } + /* Assume for now it is an elif */ + isElif = TRUE; + } else + isElif = FALSE; + + if (line[0] != 'i' || line[1] != 'f') + /* Not an ifxxx or elifxxx line */ + return COND_INVALID; + + /* + * Figure out what sort of conditional it is -- what its default + * function is, etc. -- by looking in the table of valid "ifs" + */ + line += 2; + for (ifp = ifs; ; ifp++) { + if (ifp->form == NULL) + return COND_INVALID; + if (istoken(ifp->form, line, ifp->formlen)) { + line += ifp->formlen; + break; + } + } + + /* Now we know what sort of 'if' it is... */ + + if (isElif) { + if (cond_depth == cond_min_depth) { + Parse_Error(level, "if-less elif"); + return COND_PARSE; + } + state = cond_state[cond_depth]; + if (state == SKIP_TO_ENDIF || state == ELSE_ACTIVE) { + Parse_Error(PARSE_WARNING, "extra elif"); + cond_state[cond_depth] = SKIP_TO_ENDIF; + return COND_SKIP; + } + if (state != SEARCH_FOR_ELIF) { + /* Either just finished the 'true' block, or already SKIP_TO_ELSE */ + cond_state[cond_depth] = SKIP_TO_ELSE; + return COND_SKIP; + } + } else { + /* Normal .if */ + if (cond_depth + 1 >= max_if_depth) { + /* + * This is rare, but not impossible. + * In meta mode, dirdeps.mk (only runs at level 0) + * can need more than the default. + */ + max_if_depth += MAXIF_BUMP; + cond_state = bmake_realloc(cond_state, max_if_depth * + sizeof(*cond_state)); + } + state = cond_state[cond_depth]; + cond_depth++; + if (state > ELSE_ACTIVE) { + /* If we aren't parsing the data, treat as always false */ + cond_state[cond_depth] = SKIP_TO_ELSE; + return COND_SKIP; + } + } + + /* And evaluate the conditional expresssion */ + if (Cond_EvalExpression(ifp, line, &value, 1, TRUE) == COND_INVALID) { + /* Syntax error in conditional, error message already output. */ + /* Skip everything to matching .endif */ + cond_state[cond_depth] = SKIP_TO_ELSE; + return COND_SKIP; + } + + if (!value) { + cond_state[cond_depth] = SEARCH_FOR_ELIF; + return COND_SKIP; + } + cond_state[cond_depth] = IF_ACTIVE; + return COND_PARSE; +} + + + +/*- + *----------------------------------------------------------------------- + * Cond_End -- + * Make sure everything's clean at the end of a makefile. + * + * Results: + * None. + * + * Side Effects: + * Parse_Error will be called if open conditionals are around. + * + *----------------------------------------------------------------------- + */ +void +Cond_restore_depth(unsigned int saved_depth) +{ + int open_conds = cond_depth - cond_min_depth; + + if (open_conds != 0 || saved_depth > cond_depth) { + Parse_Error(PARSE_FATAL, "%d open conditional%s", open_conds, + open_conds == 1 ? "" : "s"); + cond_depth = cond_min_depth; + } + + cond_min_depth = saved_depth; +} + +unsigned int +Cond_save_depth(void) +{ + int depth = cond_min_depth; + + cond_min_depth = cond_depth; + return depth; +} diff --git a/buildrump.sh/src/usr.bin/make/config.h b/buildrump.sh/src/usr.bin/make/config.h new file mode 100644 index 00000000..06af09c2 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/config.h @@ -0,0 +1,160 @@ +/* $NetBSD: config.h,v 1.21 2012/03/31 00:12:24 christos Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)config.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)config.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * DEFMAXJOBS + * DEFMAXLOCAL + * These control the default concurrency. On no occasion will more + * than DEFMAXJOBS targets be created at once (locally or remotely) + * DEFMAXLOCAL is the highest number of targets which will be + * created on the local machine at once. Note that if you set this + * to 0, nothing will ever happen... + */ +#define DEFMAXJOBS 4 +#define DEFMAXLOCAL 1 + +/* + * INCLUDES + * LIBRARIES + * These control the handling of the .INCLUDES and .LIBS variables. + * If INCLUDES is defined, the .INCLUDES variable will be filled + * from the search paths of those suffixes which are marked by + * .INCLUDES dependency lines. Similarly for LIBRARIES and .LIBS + * See suff.c for more details. + */ +#define INCLUDES +#define LIBRARIES + +/* + * LIBSUFF + * Is the suffix used to denote libraries and is used by the Suff module + * to find the search path on which to seek any -l<xx> targets. + * + * RECHECK + * If defined, Make_Update will check a target for its current + * modification time after it has been re-made, setting it to the + * starting time of the make only if the target still doesn't exist. + * Unfortunately, under NFS the modification time often doesn't + * get updated in time, so a target will appear to not have been + * re-made, causing later targets to appear up-to-date. On systems + * that don't have this problem, you should defined this. Under + * NFS you probably should not, unless you aren't exporting jobs. + */ +#define LIBSUFF ".a" +#define RECHECK + +/* + * POSIX + * Adhere to the POSIX 1003.2 draft for the make(1) program. + * - Use MAKEFLAGS instead of MAKE to pick arguments from the + * environment. + * - Allow empty command lines if starting with tab. + */ +#define POSIX + +/* + * SYSVINCLUDE + * Recognize system V like include directives [include "filename"] + * SYSVVARSUB + * Recognize system V like ${VAR:x=y} variable substitutions + */ +#define SYSVINCLUDE +#define SYSVVARSUB + +/* + * GMAKEEXPORT + * Recognize gmake like variable export directives [export <VAR>=<VALUE>] + */ +#define GMAKEEXPORT + +/* + * SUNSHCMD + * Recognize SunOS and Solaris: + * VAR :sh= CMD # Assign VAR to the command substitution of CMD + * ${VAR:sh} # Return the command substitution of the value + * # of ${VAR} + */ +#define SUNSHCMD + +/* + * USE_IOVEC + * We have writev(2) + */ +#define USE_IOVEC + +#if defined(MAKE_NATIVE) && !defined(__ELF__) +# ifndef RANLIBMAG +# define RANLIBMAG "__.SYMDEF" +# endif +#endif diff --git a/buildrump.sh/src/usr.bin/make/dir.c b/buildrump.sh/src/usr.bin/make/dir.c new file mode 100644 index 00000000..7b12769c --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/dir.c @@ -0,0 +1,1805 @@ +/* $NetBSD: dir.c,v 1.67 2013/03/05 22:01:43 christos Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: dir.c,v 1.67 2013/03/05 22:01:43 christos Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; +#else +__RCSID("$NetBSD: dir.c,v 1.67 2013/03/05 22:01:43 christos Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * dir.c -- + * Directory searching using wildcards and/or normal names... + * Used both for source wildcarding in the Makefile and for finding + * implicit sources. + * + * The interface for this module is: + * Dir_Init Initialize the module. + * + * Dir_InitCur Set the cur Path. + * + * Dir_InitDot Set the dot Path. + * + * Dir_End Cleanup the module. + * + * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. + * + * Dir_HasWildcards Returns TRUE if the name given it needs to + * be wildcard-expanded. + * + * Dir_Expand Given a pattern and a path, return a Lst of names + * which match the pattern on the search path. + * + * Dir_FindFile Searches for a file on a given search path. + * If it exists, the entire path is returned. + * Otherwise NULL is returned. + * + * Dir_FindHereOrAbove Search for a path in the current directory and + * then all the directories above it in turn until + * the path is found or we reach the root ("/"). + * + * Dir_MTime Return the modification time of a node. The file + * is searched for along the default search path. + * The path and mtime fields of the node are filled + * in. + * + * Dir_AddDir Add a directory to a search path. + * + * Dir_MakeFlags Given a search path and a command flag, create + * a string with each of the directories in the path + * preceded by the command flag and all of them + * separated by a space. + * + * Dir_Destroy Destroy an element of a search path. Frees up all + * things that can be freed for the element as long + * as the element is no longer referenced by any other + * search path. + * Dir_ClearPath Resets a search path to the empty list. + * + * For debugging: + * Dir_PrintDirectories Print stats about the directory cache. + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <dirent.h> +#include <errno.h> +#include <stdio.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" + +/* + * A search path consists of a Lst of Path structures. A Path structure + * has in it the name of the directory and a hash table of all the files + * in the directory. This is used to cut down on the number of system + * calls necessary to find implicit dependents and their like. Since + * these searches are made before any actions are taken, we need not + * worry about the directory changing due to creation commands. If this + * hampers the style of some makefiles, they must be changed. + * + * A list of all previously-read directories is kept in the + * openDirectories Lst. This list is checked first before a directory + * is opened. + * + * The need for the caching of whole directories is brought about by + * the multi-level transformation code in suff.c, which tends to search + * for far more files than regular make does. In the initial + * implementation, the amount of time spent performing "stat" calls was + * truly astronomical. The problem with hashing at the start is, + * of course, that pmake doesn't then detect changes to these directories + * during the course of the make. Three possibilities suggest themselves: + * + * 1) just use stat to test for a file's existence. As mentioned + * above, this is very inefficient due to the number of checks + * engendered by the multi-level transformation code. + * 2) use readdir() and company to search the directories, keeping + * them open between checks. I have tried this and while it + * didn't slow down the process too much, it could severely + * affect the amount of parallelism available as each directory + * open would take another file descriptor out of play for + * handling I/O for another job. Given that it is only recently + * that UNIX OS's have taken to allowing more than 20 or 32 + * file descriptors for a process, this doesn't seem acceptable + * to me. + * 3) record the mtime of the directory in the Path structure and + * verify the directory hasn't changed since the contents were + * hashed. This will catch the creation or deletion of files, + * but not the updating of files. However, since it is the + * creation and deletion that is the problem, this could be + * a good thing to do. Unfortunately, if the directory (say ".") + * were fairly large and changed fairly frequently, the constant + * rehashing could seriously degrade performance. It might be + * good in such cases to keep track of the number of rehashes + * and if the number goes over a (small) limit, resort to using + * stat in its place. + * + * An additional thing to consider is that pmake is used primarily + * to create C programs and until recently pcc-based compilers refused + * to allow you to specify where the resulting object file should be + * placed. This forced all objects to be created in the current + * directory. This isn't meant as a full excuse, just an explanation of + * some of the reasons for the caching used here. + * + * One more note: the location of a target's file is only performed + * on the downward traversal of the graph and then only for terminal + * nodes in the graph. This could be construed as wrong in some cases, + * but prevents inadvertent modification of files when the "installed" + * directory for a file is provided in the search path. + * + * Another data structure maintained by this module is an mtime + * cache used when the searching of cached directories fails to find + * a file. In the past, Dir_FindFile would simply perform an access() + * call in such a case to determine if the file could be found using + * just the name given. When this hit, however, all that was gained + * was the knowledge that the file existed. Given that an access() is + * essentially a stat() without the copyout() call, and that the same + * filesystem overhead would have to be incurred in Dir_MTime, it made + * sense to replace the access() with a stat() and record the mtime + * in a cache for when Dir_MTime was actually called. + */ + +Lst dirSearchPath; /* main search path */ + +static Lst openDirectories; /* the list of all open directories */ + +/* + * Variables for gathering statistics on the efficiency of the hashing + * mechanism. + */ +static int hits, /* Found in directory cache */ + misses, /* Sad, but not evil misses */ + nearmisses, /* Found under search path */ + bigmisses; /* Sought by itself */ + +static Path *dot; /* contents of current directory */ +static Path *cur; /* contents of current directory, if not dot */ +static Path *dotLast; /* a fake path entry indicating we need to + * look for . last */ +static Hash_Table mtimes; /* Results of doing a last-resort stat in + * Dir_FindFile -- if we have to go to the + * system to find the file, we might as well + * have its mtime on record. XXX: If this is done + * way early, there's a chance other rules will + * have already updated the file, in which case + * we'll update it again. Generally, there won't + * be two rules to update a single file, so this + * should be ok, but... */ + + +static int DirFindName(const void *, const void *); +static int DirMatchFiles(const char *, Path *, Lst); +static void DirExpandCurly(const char *, const char *, Lst, Lst); +static void DirExpandInt(const char *, Lst, Lst); +static int DirPrintWord(void *, void *); +static int DirPrintDir(void *, void *); +static char *DirLookup(Path *, const char *, const char *, Boolean); +static char *DirLookupSubdir(Path *, const char *); +static char *DirFindDot(Boolean, const char *, const char *); +static char *DirLookupAbs(Path *, const char *, const char *); + +/*- + *----------------------------------------------------------------------- + * Dir_Init -- + * initialize things for this module + * + * Results: + * none + * + * Side Effects: + * some directories may be opened. + *----------------------------------------------------------------------- + */ +void +Dir_Init(const char *cdname) +{ + dirSearchPath = Lst_Init(FALSE); + openDirectories = Lst_Init(FALSE); + Hash_InitTable(&mtimes, 0); + + Dir_InitCur(cdname); + + dotLast = bmake_malloc(sizeof(Path)); + dotLast->refCount = 1; + dotLast->hits = 0; + dotLast->name = bmake_strdup(".DOTLAST"); + Hash_InitTable(&dotLast->files, -1); +} + +/* + * Called by Dir_Init() and whenever .CURDIR is assigned to. + */ +void +Dir_InitCur(const char *cdname) +{ + Path *p; + + if (cdname != NULL) { + /* + * Our build directory is not the same as our source directory. + * Keep this one around too. + */ + if ((p = Dir_AddDir(NULL, cdname))) { + p->refCount += 1; + if (cur && cur != p) { + /* + * We've been here before, cleanup. + */ + cur->refCount -= 1; + Dir_Destroy(cur); + } + cur = p; + } + } +} + +/*- + *----------------------------------------------------------------------- + * Dir_InitDot -- + * (re)initialize "dot" (current/object directory) path hash + * + * Results: + * none + * + * Side Effects: + * some directories may be opened. + *----------------------------------------------------------------------- + */ +void +Dir_InitDot(void) +{ + if (dot != NULL) { + LstNode ln; + + /* Remove old entry from openDirectories, but do not destroy. */ + ln = Lst_Member(openDirectories, dot); + (void)Lst_Remove(openDirectories, ln); + } + + dot = Dir_AddDir(NULL, "."); + + if (dot == NULL) { + Error("Cannot open `.' (%s)", strerror(errno)); + exit(1); + } + + /* + * We always need to have dot around, so we increment its reference count + * to make sure it's not destroyed. + */ + dot->refCount += 1; + Dir_SetPATH(); /* initialize */ +} + +/*- + *----------------------------------------------------------------------- + * Dir_End -- + * cleanup things for this module + * + * Results: + * none + * + * Side Effects: + * none + *----------------------------------------------------------------------- + */ +void +Dir_End(void) +{ +#ifdef CLEANUP + if (cur) { + cur->refCount -= 1; + Dir_Destroy(cur); + } + dot->refCount -= 1; + dotLast->refCount -= 1; + Dir_Destroy(dotLast); + Dir_Destroy(dot); + Dir_ClearPath(dirSearchPath); + Lst_Destroy(dirSearchPath, NULL); + Dir_ClearPath(openDirectories); + Lst_Destroy(openDirectories, NULL); + Hash_DeleteTable(&mtimes); +#endif +} + +/* + * We want ${.PATH} to indicate the order in which we will actually + * search, so we rebuild it after any .PATH: target. + * This is the simplest way to deal with the effect of .DOTLAST. + */ +void +Dir_SetPATH(void) +{ + LstNode ln; /* a list element */ + Path *p; + Boolean hasLastDot = FALSE; /* true we should search dot last */ + + Var_Delete(".PATH", VAR_GLOBAL); + + if (Lst_Open(dirSearchPath) == SUCCESS) { + if ((ln = Lst_First(dirSearchPath)) != NULL) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) { + hasLastDot = TRUE; + Var_Append(".PATH", dotLast->name, VAR_GLOBAL); + } + } + + if (!hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } + + while ((ln = Lst_Next(dirSearchPath)) != NULL) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) + continue; + if (p == dot && hasLastDot) + continue; + Var_Append(".PATH", p->name, VAR_GLOBAL); + } + + if (hasLastDot) { + if (dot) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } + Lst_Close(dirSearchPath); + } +} + +/*- + *----------------------------------------------------------------------- + * DirFindName -- + * See if the Path structure describes the same directory as the + * given one by comparing their names. Called from Dir_AddDir via + * Lst_Find when searching the list of open directories. + * + * Input: + * p Current name + * dname Desired name + * + * Results: + * 0 if it is the same. Non-zero otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +DirFindName(const void *p, const void *dname) +{ + return (strcmp(((const Path *)p)->name, dname)); +} + +/*- + *----------------------------------------------------------------------- + * Dir_HasWildcards -- + * see if the given name has any wildcard characters in it + * be careful not to expand unmatching brackets or braces. + * XXX: This code is not 100% correct. ([^]] fails etc.) + * I really don't think that make(1) should be expanding + * patterns, because then you have to set a mechanism for + * escaping the expansion! + * + * Input: + * name name to check + * + * Results: + * returns TRUE if the word should be expanded, FALSE otherwise + * + * Side Effects: + * none + *----------------------------------------------------------------------- + */ +Boolean +Dir_HasWildcards(char *name) +{ + char *cp; + int wild = 0, brace = 0, bracket = 0; + + for (cp = name; *cp; cp++) { + switch(*cp) { + case '{': + brace++; + wild = 1; + break; + case '}': + brace--; + break; + case '[': + bracket++; + wild = 1; + break; + case ']': + bracket--; + break; + case '?': + case '*': + wild = 1; + break; + default: + break; + } + } + return wild && bracket == 0 && brace == 0; +} + +/*- + *----------------------------------------------------------------------- + * DirMatchFiles -- + * Given a pattern and a Path structure, see if any files + * match the pattern and add their names to the 'expansions' list if + * any do. This is incomplete -- it doesn't take care of patterns like + * src / *src / *.c properly (just *.c on any of the directories), but it + * will do for now. + * + * Input: + * pattern Pattern to look for + * p Directory to search + * expansion Place to store the results + * + * Results: + * Always returns 0 + * + * Side Effects: + * File names are added to the expansions lst. The directory will be + * fully hashed when this is done. + *----------------------------------------------------------------------- + */ +static int +DirMatchFiles(const char *pattern, Path *p, Lst expansions) +{ + Hash_Search search; /* Index into the directory's table */ + Hash_Entry *entry; /* Current entry in the table */ + Boolean isDot; /* TRUE if the directory being searched is . */ + + isDot = (*p->name == '.' && p->name[1] == '\0'); + + for (entry = Hash_EnumFirst(&p->files, &search); + entry != NULL; + entry = Hash_EnumNext(&search)) + { + /* + * See if the file matches the given pattern. Note we follow the UNIX + * convention that dot files will only be found if the pattern + * begins with a dot (note also that as a side effect of the hashing + * scheme, .* won't match . or .. since they aren't hashed). + */ + if (Str_Match(entry->name, pattern) && + ((entry->name[0] != '.') || + (pattern[0] == '.'))) + { + (void)Lst_AtEnd(expansions, + (isDot ? bmake_strdup(entry->name) : + str_concat(p->name, entry->name, + STR_ADDSLASH))); + } + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * DirExpandCurly -- + * Expand curly braces like the C shell. Does this recursively. + * Note the special case: if after the piece of the curly brace is + * done there are no wildcard characters in the result, the result is + * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. + * + * Input: + * word Entire word to expand + * brace First curly brace in it + * path Search path to use + * expansions Place to store the expansions + * + * Results: + * None. + * + * Side Effects: + * The given list is filled with the expansions... + * + *----------------------------------------------------------------------- + */ +static void +DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) +{ + const char *end; /* Character after the closing brace */ + const char *cp; /* Current position in brace clause */ + const char *start; /* Start of current piece of brace clause */ + int bracelevel; /* Number of braces we've seen. If we see a + * right brace when this is 0, we've hit the + * end of the clause. */ + char *file; /* Current expansion */ + int otherLen; /* The length of the other pieces of the + * expansion (chars before and after the + * clause in 'word') */ + char *cp2; /* Pointer for checking for wildcards in + * expansion before calling Dir_Expand */ + + start = brace+1; + + /* + * Find the end of the brace clause first, being wary of nested brace + * clauses. + */ + for (end = start, bracelevel = 0; *end != '\0'; end++) { + if (*end == '{') { + bracelevel++; + } else if ((*end == '}') && (bracelevel-- == 0)) { + break; + } + } + if (*end == '\0') { + Error("Unterminated {} clause \"%s\"", start); + return; + } else { + end++; + } + otherLen = brace - word + strlen(end); + + for (cp = start; cp < end; cp++) { + /* + * Find the end of this piece of the clause. + */ + bracelevel = 0; + while (*cp != ',') { + if (*cp == '{') { + bracelevel++; + } else if ((*cp == '}') && (bracelevel-- <= 0)) { + break; + } + cp++; + } + /* + * Allocate room for the combination and install the three pieces. + */ + file = bmake_malloc(otherLen + cp - start + 1); + if (brace != word) { + strncpy(file, word, brace-word); + } + if (cp != start) { + strncpy(&file[brace-word], start, cp-start); + } + strcpy(&file[(brace-word)+(cp-start)], end); + + /* + * See if the result has any wildcards in it. If we find one, call + * Dir_Expand right away, telling it to place the result on our list + * of expansions. + */ + for (cp2 = file; *cp2 != '\0'; cp2++) { + switch(*cp2) { + case '*': + case '?': + case '{': + case '[': + Dir_Expand(file, path, expansions); + goto next; + } + } + if (*cp2 == '\0') { + /* + * Hit the end w/o finding any wildcards, so stick the expansion + * on the end of the list. + */ + (void)Lst_AtEnd(expansions, file); + } else { + next: + free(file); + } + start = cp+1; + } +} + + +/*- + *----------------------------------------------------------------------- + * DirExpandInt -- + * Internal expand routine. Passes through the directories in the + * path one by one, calling DirMatchFiles for each. NOTE: This still + * doesn't handle patterns in directories... + * + * Input: + * word Word to expand + * path Path on which to look + * expansions Place to store the result + * + * Results: + * None. + * + * Side Effects: + * Things are added to the expansions list. + * + *----------------------------------------------------------------------- + */ +static void +DirExpandInt(const char *word, Lst path, Lst expansions) +{ + LstNode ln; /* Current node */ + Path *p; /* Directory in the node */ + + if (Lst_Open(path) == SUCCESS) { + while ((ln = Lst_Next(path)) != NULL) { + p = (Path *)Lst_Datum(ln); + DirMatchFiles(word, p, expansions); + } + Lst_Close(path); + } +} + +/*- + *----------------------------------------------------------------------- + * DirPrintWord -- + * Print a word in the list of expansions. Callback for Dir_Expand + * when DEBUG(DIR), via Lst_ForEach. + * + * Results: + * === 0 + * + * Side Effects: + * The passed word is printed, followed by a space. + * + *----------------------------------------------------------------------- + */ +static int +DirPrintWord(void *word, void *dummy) +{ + fprintf(debug_file, "%s ", (char *)word); + + return(dummy ? 0 : 0); +} + +/*- + *----------------------------------------------------------------------- + * Dir_Expand -- + * Expand the given word into a list of words by globbing it looking + * in the directories on the given search path. + * + * Input: + * word the word to expand + * path the list of directories in which to find the + * resulting files + * expansions the list on which to place the results + * + * Results: + * A list of words consisting of the files which exist along the search + * path matching the given pattern. + * + * Side Effects: + * Directories may be opened. Who knows? + *----------------------------------------------------------------------- + */ +void +Dir_Expand(const char *word, Lst path, Lst expansions) +{ + const char *cp; + + if (DEBUG(DIR)) { + fprintf(debug_file, "Expanding \"%s\"... ", word); + } + + cp = strchr(word, '{'); + if (cp) { + DirExpandCurly(word, cp, path, expansions); + } else { + cp = strchr(word, '/'); + if (cp) { + /* + * The thing has a directory component -- find the first wildcard + * in the string. + */ + for (cp = word; *cp; cp++) { + if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { + break; + } + } + if (*cp == '{') { + /* + * This one will be fun. + */ + DirExpandCurly(word, cp, path, expansions); + return; + } else if (*cp != '\0') { + /* + * Back up to the start of the component + */ + char *dirpath; + + while (cp > word && *cp != '/') { + cp--; + } + if (cp != word) { + char sc; + /* + * If the glob isn't in the first component, try and find + * all the components up to the one with a wildcard. + */ + sc = cp[1]; + ((char *)UNCONST(cp))[1] = '\0'; + dirpath = Dir_FindFile(word, path); + ((char *)UNCONST(cp))[1] = sc; + /* + * dirpath is null if can't find the leading component + * XXX: Dir_FindFile won't find internal components. + * i.e. if the path contains ../Etc/Object and we're + * looking for Etc, it won't be found. Ah well. + * Probably not important. + */ + if (dirpath != NULL) { + char *dp = &dirpath[strlen(dirpath) - 1]; + if (*dp == '/') + *dp = '\0'; + path = Lst_Init(FALSE); + (void)Dir_AddDir(path, dirpath); + DirExpandInt(cp+1, path, expansions); + Lst_Destroy(path, NULL); + } + } else { + /* + * Start the search from the local directory + */ + DirExpandInt(word, path, expansions); + } + } else { + /* + * Return the file -- this should never happen. + */ + DirExpandInt(word, path, expansions); + } + } else { + /* + * First the files in dot + */ + DirMatchFiles(word, dot, expansions); + + /* + * Then the files in every other directory on the path. + */ + DirExpandInt(word, path, expansions); + } + } + if (DEBUG(DIR)) { + Lst_ForEach(expansions, DirPrintWord, NULL); + fprintf(debug_file, "\n"); + } +} + +/*- + *----------------------------------------------------------------------- + * DirLookup -- + * Find if the file with the given name exists in the given path. + * + * Results: + * The path to the file or NULL. This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static char * +DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, + Boolean hasSlash MAKE_ATTR_UNUSED) +{ + char *file; /* the current filename to check */ + + if (DEBUG(DIR)) { + fprintf(debug_file, " %s ...\n", p->name); + } + + if (Hash_FindEntry(&p->files, cp) == NULL) + return NULL; + + file = str_concat(p->name, cp, STR_ADDSLASH); + if (DEBUG(DIR)) { + fprintf(debug_file, " returning %s\n", file); + } + p->hits += 1; + hits += 1; + return file; +} + + +/*- + *----------------------------------------------------------------------- + * DirLookupSubdir -- + * Find if the file with the given name exists in the given path. + * + * Results: + * The path to the file or NULL. This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * If the file is found, it is added in the modification times hash + * table. + *----------------------------------------------------------------------- + */ +static char * +DirLookupSubdir(Path *p, const char *name) +{ + struct stat stb; /* Buffer for stat, if necessary */ + Hash_Entry *entry; /* Entry for mtimes table */ + char *file; /* the current filename to check */ + + if (p != dot) { + file = str_concat(p->name, name, STR_ADDSLASH); + } else { + /* + * Checking in dot -- DON'T put a leading ./ on the thing. + */ + file = bmake_strdup(name); + } + + if (DEBUG(DIR)) { + fprintf(debug_file, "checking %s ...\n", file); + } + + if (stat(file, &stb) == 0) { + if (stb.st_mtime == 0) + stb.st_mtime = 1; + /* + * Save the modification time so if it's needed, we don't have + * to fetch it again. + */ + if (DEBUG(DIR)) { + fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), + file); + } + entry = Hash_CreateEntry(&mtimes, file, NULL); + Hash_SetTimeValue(entry, stb.st_mtime); + nearmisses += 1; + return (file); + } + free(file); + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * DirLookupAbs -- + * Find if the file with the given name exists in the given path. + * + * Results: + * The path to the file, the empty string or NULL. If the file is + * the empty string, the search should be terminated. + * This path is guaranteed to be in a different part of memory + * than name and so may be safely free'd. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static char * +DirLookupAbs(Path *p, const char *name, const char *cp) +{ + char *p1; /* pointer into p->name */ + const char *p2; /* pointer into name */ + + if (DEBUG(DIR)) { + fprintf(debug_file, " %s ...\n", p->name); + } + + /* + * If the file has a leading path component and that component + * exactly matches the entire name of the current search + * directory, we can attempt another cache lookup. And if we don't + * have a hit, we can safely assume the file does not exist at all. + */ + for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { + continue; + } + if (*p1 != '\0' || p2 != cp - 1) { + return NULL; + } + + if (Hash_FindEntry(&p->files, cp) == NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, " must be here but isn't -- returning\n"); + } + /* Return empty string: terminates search */ + return bmake_strdup(""); + } + + p->hits += 1; + hits += 1; + if (DEBUG(DIR)) { + fprintf(debug_file, " returning %s\n", name); + } + return (bmake_strdup(name)); +} + +/*- + *----------------------------------------------------------------------- + * DirFindDot -- + * Find the file given on "." or curdir + * + * Results: + * The path to the file or NULL. This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * Hit counts change + *----------------------------------------------------------------------- + */ +static char * +DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) +{ + + if (Hash_FindEntry(&dot->files, cp) != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, " in '.'\n"); + } + hits += 1; + dot->hits += 1; + return (bmake_strdup(name)); + } + if (cur && + Hash_FindEntry(&cur->files, cp) != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, " in ${.CURDIR} = %s\n", cur->name); + } + hits += 1; + cur->hits += 1; + return str_concat(cur->name, cp, STR_ADDSLASH); + } + + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * Dir_FindFile -- + * Find the file with the given name along the given search path. + * + * Input: + * name the file to find + * path the Lst of directories to search + * + * Results: + * The path to the file or NULL. This path is guaranteed to be in a + * different part of memory than name and so may be safely free'd. + * + * Side Effects: + * If the file is found in a directory which is not on the path + * already (either 'name' is absolute or it is a relative path + * [ dir1/.../dirn/file ] which exists below one of the directories + * already on the search path), its directory is added to the end + * of the path on the assumption that there will be more files in + * that directory later on. Sometimes this is true. Sometimes not. + *----------------------------------------------------------------------- + */ +char * +Dir_FindFile(const char *name, Lst path) +{ + LstNode ln; /* a list element */ + char *file; /* the current filename to check */ + Path *p; /* current path member */ + const char *cp; /* Terminal name of file */ + Boolean hasLastDot = FALSE; /* true we should search dot last */ + Boolean hasSlash; /* true if 'name' contains a / */ + struct stat stb; /* Buffer for stat, if necessary */ + Hash_Entry *entry; /* Entry for mtimes table */ + const char *trailing_dot = "."; + + /* + * Find the final component of the name and note whether it has a + * slash in it (the name, I mean) + */ + cp = strrchr(name, '/'); + if (cp) { + hasSlash = TRUE; + cp += 1; + } else { + hasSlash = FALSE; + cp = name; + } + + if (DEBUG(DIR)) { + fprintf(debug_file, "Searching for %s ...", name); + } + + if (Lst_Open(path) == FAILURE) { + if (DEBUG(DIR)) { + fprintf(debug_file, "couldn't open path, file not found\n"); + } + misses += 1; + return NULL; + } + + if ((ln = Lst_First(path)) != NULL) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) { + hasLastDot = TRUE; + if (DEBUG(DIR)) + fprintf(debug_file, "[dot last]..."); + } + } + if (DEBUG(DIR)) { + fprintf(debug_file, "\n"); + } + + /* + * If there's no leading directory components or if the leading + * directory component is exactly `./', consult the cached contents + * of each of the directories on the search path. + */ + if (!hasSlash || (cp - name == 2 && *name == '.')) { + /* + * We look through all the directories on the path seeking one which + * contains the final component of the given name. If such a beast + * is found, we concatenate the directory name and the final + * component and return the resulting string. If we don't find any + * such thing, we go on to phase two... + * + * No matter what, we always look for the file in the current + * directory before anywhere else (unless we found the magic + * DOTLAST path, in which case we search it last) and we *do not* + * add the ./ to it if it exists. + * This is so there are no conflicts between what the user + * specifies (fish.c) and what pmake finds (./fish.c). + */ + if (!hasLastDot && + (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } + + while ((ln = Lst_Next(path)) != NULL) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) + continue; + if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { + Lst_Close(path); + return file; + } + } + + if (hasLastDot && + (file = DirFindDot(hasSlash, name, cp)) != NULL) { + Lst_Close(path); + return file; + } + } + Lst_Close(path); + + /* + * We didn't find the file on any directory in the search path. + * If the name doesn't contain a slash, that means it doesn't exist. + * If it *does* contain a slash, however, there is still hope: it + * could be in a subdirectory of one of the members of the search + * path. (eg. /usr/include and sys/types.h. The above search would + * fail to turn up types.h in /usr/include, but it *is* in + * /usr/include/sys/types.h). + * [ This no longer applies: If we find such a beast, we assume there + * will be more (what else can we assume?) and add all but the last + * component of the resulting name onto the search path (at the + * end).] + * This phase is only performed if the file is *not* absolute. + */ + if (!hasSlash) { + if (DEBUG(DIR)) { + fprintf(debug_file, " failed.\n"); + } + misses += 1; + return NULL; + } + + if (*cp == '\0') { + /* we were given a trailing "/" */ + cp = trailing_dot; + } + + if (name[0] != '/') { + Boolean checkedDot = FALSE; + + if (DEBUG(DIR)) { + fprintf(debug_file, " Trying subdirectories...\n"); + } + + if (!hasLastDot) { + if (dot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; + } + + (void)Lst_Open(path); + while ((ln = Lst_Next(path)) != NULL) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) + continue; + if (p == dot) { + if (checkedDot) + continue; + checkedDot = TRUE; + } + if ((file = DirLookupSubdir(p, name)) != NULL) { + Lst_Close(path); + return file; + } + } + Lst_Close(path); + + if (hasLastDot) { + if (dot && !checkedDot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur && (file = DirLookupSubdir(cur, name)) != NULL) + return file; + } + + if (checkedDot) { + /* + * Already checked by the given name, since . was in the path, + * so no point in proceeding... + */ + if (DEBUG(DIR)) { + fprintf(debug_file, " Checked . already, returning NULL\n"); + } + return NULL; + } + + } else { /* name[0] == '/' */ + + /* + * For absolute names, compare directory path prefix against the + * the directory path of each member on the search path for an exact + * match. If we have an exact match on any member of the search path, + * use the cached contents of that member to lookup the final file + * component. If that lookup fails we can safely assume that the + * file does not exist at all. This is signified by DirLookupAbs() + * returning an empty string. + */ + if (DEBUG(DIR)) { + fprintf(debug_file, " Trying exact path matches...\n"); + } + + if (!hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL) + return *file?file:NULL; + + (void)Lst_Open(path); + while ((ln = Lst_Next(path)) != NULL) { + p = (Path *)Lst_Datum(ln); + if (p == dotLast) + continue; + if ((file = DirLookupAbs(p, name, cp)) != NULL) { + Lst_Close(path); + return *file?file:NULL; + } + } + Lst_Close(path); + + if (hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL) + return *file?file:NULL; + } + + /* + * Didn't find it that way, either. Sigh. Phase 3. Add its directory + * onto the search path in any case, just in case, then look for the + * thing in the hash table. If we find it, grand. We return a new + * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. + * Note that if the directory holding the file doesn't exist, this will + * do an extra search of the final directory on the path. Unless something + * weird happens, this search won't succeed and life will be groovy. + * + * Sigh. We cannot add the directory onto the search path because + * of this amusing case: + * $(INSTALLDIR)/$(FILE): $(FILE) + * + * $(FILE) exists in $(INSTALLDIR) but not in the current one. + * When searching for $(FILE), we will find it in $(INSTALLDIR) + * b/c we added it here. This is not good... + */ +#ifdef notdef + if (cp == traling_dot) { + cp = strrchr(name, '/'); + cp += 1; + } + cp[-1] = '\0'; + (void)Dir_AddDir(path, name); + cp[-1] = '/'; + + bigmisses += 1; + ln = Lst_Last(path); + if (ln == NULL) { + return NULL; + } else { + p = (Path *)Lst_Datum(ln); + } + + if (Hash_FindEntry(&p->files, cp) != NULL) { + return (bmake_strdup(name)); + } else { + return NULL; + } +#else /* !notdef */ + if (DEBUG(DIR)) { + fprintf(debug_file, " Looking for \"%s\" ...\n", name); + } + + bigmisses += 1; + entry = Hash_FindEntry(&mtimes, name); + if (entry != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, " got it (in mtime cache)\n"); + } + return(bmake_strdup(name)); + } else if (stat(name, &stb) == 0) { + if (stb.st_mtime == 0) + stb.st_mtime = 1; + entry = Hash_CreateEntry(&mtimes, name, NULL); + if (DEBUG(DIR)) { + fprintf(debug_file, " Caching %s for %s\n", Targ_FmtTime(stb.st_mtime), + name); + } + Hash_SetTimeValue(entry, stb.st_mtime); + return (bmake_strdup(name)); + } else { + if (DEBUG(DIR)) { + fprintf(debug_file, " failed. Returning NULL\n"); + } + return NULL; + } +#endif /* notdef */ +} + + +/*- + *----------------------------------------------------------------------- + * Dir_FindHereOrAbove -- + * search for a path starting at a given directory and then working + * our way up towards the root. + * + * Input: + * here starting directory + * search_path the path we are looking for + * result the result of a successful search is placed here + * rlen the length of the result buffer + * (typically MAXPATHLEN + 1) + * + * Results: + * 0 on failure, 1 on success [in which case the found path is put + * in the result buffer]. + * + * Side Effects: + *----------------------------------------------------------------------- + */ +int +Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) { + + struct stat st; + char dirbase[MAXPATHLEN + 1], *db_end; + char try[MAXPATHLEN + 1], *try_end; + + /* copy out our starting point */ + snprintf(dirbase, sizeof(dirbase), "%s", here); + db_end = dirbase + strlen(dirbase); + + /* loop until we determine a result */ + while (1) { + + /* try and stat(2) it ... */ + snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); + if (stat(try, &st) != -1) { + /* + * success! if we found a file, chop off + * the filename so we return a directory. + */ + if ((st.st_mode & S_IFMT) != S_IFDIR) { + try_end = try + strlen(try); + while (try_end > try && *try_end != '/') + try_end--; + if (try_end > try) + *try_end = 0; /* chop! */ + } + + /* + * done! + */ + snprintf(result, rlen, "%s", try); + return(1); + } + + /* + * nope, we didn't find it. if we used up dirbase we've + * reached the root and failed. + */ + if (db_end == dirbase) + break; /* failed! */ + + /* + * truncate dirbase from the end to move up a dir + */ + while (db_end > dirbase && *db_end != '/') + db_end--; + *db_end = 0; /* chop! */ + + } /* while (1) */ + + /* + * we failed... + */ + return(0); +} + +/*- + *----------------------------------------------------------------------- + * Dir_MTime -- + * Find the modification time of the file described by gn along the + * search path dirSearchPath. + * + * Input: + * gn the file whose modification time is desired + * + * Results: + * The modification time or 0 if it doesn't exist + * + * Side Effects: + * The modification time is placed in the node's mtime slot. + * If the node didn't have a path entry before, and Dir_FindFile + * found one for it, the full name is placed in the path slot. + *----------------------------------------------------------------------- + */ +int +Dir_MTime(GNode *gn, Boolean recheck) +{ + char *fullName; /* the full pathname of name */ + struct stat stb; /* buffer for finding the mod time */ + Hash_Entry *entry; + + if (gn->type & OP_ARCHV) { + return Arch_MTime(gn); + } else if (gn->type & OP_PHONY) { + gn->mtime = 0; + return 0; + } else if (gn->path == NULL) { + if (gn->type & OP_NOPATH) + fullName = NULL; + else { + fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); + if (fullName == NULL && gn->flags & FROM_DEPEND && + !Lst_IsEmpty(gn->iParents)) { + char *cp; + + cp = strrchr(gn->name, '/'); + if (cp) { + /* + * This is an implied source, and it may have moved, + * see if we can find it via the current .PATH + */ + cp++; + + fullName = Dir_FindFile(cp, Suff_FindPath(gn)); + if (fullName) { + /* + * Put the found file in gn->path + * so that we give that to the compiler. + */ + gn->path = bmake_strdup(fullName); + if (!Job_RunTarget(".STALE", gn->fname)) + fprintf(stdout, + "%s: %s, %d: ignoring stale %s for %s, " + "found %s\n", progname, gn->fname, gn->lineno, + makeDependfile, gn->name, fullName); + } + } + } + if (DEBUG(DIR)) + fprintf(debug_file, "Found '%s' as '%s'\n", + gn->name, fullName ? fullName : "(not found)" ); + } + } else { + fullName = gn->path; + } + + if (fullName == NULL) { + fullName = bmake_strdup(gn->name); + } + + if (!recheck) + entry = Hash_FindEntry(&mtimes, fullName); + else + entry = NULL; + if (entry != NULL) { + if (DEBUG(DIR)) { + fprintf(debug_file, "Using cached time %s for %s\n", + Targ_FmtTime(Hash_GetTimeValue(entry)), fullName); + } + stb.st_mtime = Hash_GetTimeValue(entry); + } else if (stat(fullName, &stb) < 0) { + if (gn->type & OP_MEMBER) { + if (fullName != gn->path) + free(fullName); + return Arch_MemMTime(gn); + } else { + stb.st_mtime = 0; + } + } else { + if (stb.st_mtime == 0) { + /* + * 0 handled specially by the code, if the time is really 0, + * return something else instead + */ + stb.st_mtime = 1; + } + entry = Hash_CreateEntry(&mtimes, fullName, NULL); + Hash_SetTimeValue(entry, stb.st_mtime); + } + + if (fullName && gn->path == NULL) { + gn->path = fullName; + } + + gn->mtime = stb.st_mtime; + return (gn->mtime); +} + +/*- + *----------------------------------------------------------------------- + * Dir_AddDir -- + * Add the given name to the end of the given path. The order of + * the arguments is backwards so ParseDoDependency can do a + * Lst_ForEach of its list of paths... + * + * Input: + * path the path to which the directory should be + * added + * name the name of the directory to add + * + * Results: + * none + * + * Side Effects: + * A structure is added to the list and the directory is + * read and hashed. + *----------------------------------------------------------------------- + */ +Path * +Dir_AddDir(Lst path, const char *name) +{ + LstNode ln = NULL; /* node in case Path structure is found */ + Path *p = NULL; /* pointer to new Path structure */ + DIR *d; /* for reading directory */ + struct dirent *dp; /* entry in directory */ + + if (strcmp(name, ".DOTLAST") == 0) { + ln = Lst_Find(path, name, DirFindName); + if (ln != NULL) + return (Path *)Lst_Datum(ln); + else { + dotLast->refCount += 1; + (void)Lst_AtFront(path, dotLast); + } + } + + if (path) + ln = Lst_Find(openDirectories, name, DirFindName); + if (ln != NULL) { + p = (Path *)Lst_Datum(ln); + if (path && Lst_Member(path, p) == NULL) { + p->refCount += 1; + (void)Lst_AtEnd(path, p); + } + } else { + if (DEBUG(DIR)) { + fprintf(debug_file, "Caching %s ...", name); + } + + if ((d = opendir(name)) != NULL) { + p = bmake_malloc(sizeof(Path)); + p->name = bmake_strdup(name); + p->hits = 0; + p->refCount = 1; + Hash_InitTable(&p->files, -1); + + while ((dp = readdir(d)) != NULL) { +#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ + /* + * The sun directory library doesn't check for a 0 inode + * (0-inode slots just take up space), so we have to do + * it ourselves. + */ + if (dp->d_fileno == 0) { + continue; + } +#endif /* sun && d_ino */ + (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); + } + (void)closedir(d); + (void)Lst_AtEnd(openDirectories, p); + if (path != NULL) + (void)Lst_AtEnd(path, p); + } + if (DEBUG(DIR)) { + fprintf(debug_file, "done\n"); + } + } + return p; +} + +/*- + *----------------------------------------------------------------------- + * Dir_CopyDir -- + * Callback function for duplicating a search path via Lst_Duplicate. + * Ups the reference count for the directory. + * + * Results: + * Returns the Path it was given. + * + * Side Effects: + * The refCount of the path is incremented. + * + *----------------------------------------------------------------------- + */ +void * +Dir_CopyDir(void *p) +{ + ((Path *)p)->refCount += 1; + + return (p); +} + +/*- + *----------------------------------------------------------------------- + * Dir_MakeFlags -- + * Make a string by taking all the directories in the given search + * path and preceding them by the given flag. Used by the suffix + * module to create variables for compilers based on suffix search + * paths. + * + * Input: + * flag flag which should precede each directory + * path list of directories + * + * Results: + * The string mentioned above. Note that there is no space between + * the given flag and each directory. The empty string is returned if + * Things don't go well. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +char * +Dir_MakeFlags(const char *flag, Lst path) +{ + char *str; /* the string which will be returned */ + char *s1, *s2;/* the current directory preceded by 'flag' */ + LstNode ln; /* the node of the current directory */ + Path *p; /* the structure describing the current directory */ + + str = bmake_strdup(""); + + if (Lst_Open(path) == SUCCESS) { + while ((ln = Lst_Next(path)) != NULL) { + p = (Path *)Lst_Datum(ln); + s2 = str_concat(flag, p->name, 0); + str = str_concat(s1 = str, s2, STR_ADDSPACE); + free(s1); + free(s2); + } + Lst_Close(path); + } + + return (str); +} + +/*- + *----------------------------------------------------------------------- + * Dir_Destroy -- + * Nuke a directory descriptor, if possible. Callback procedure + * for the suffixes module when destroying a search path. + * + * Input: + * pp The directory descriptor to nuke + * + * Results: + * None. + * + * Side Effects: + * If no other path references this directory (refCount == 0), + * the Path and all its data are freed. + * + *----------------------------------------------------------------------- + */ +void +Dir_Destroy(void *pp) +{ + Path *p = (Path *)pp; + p->refCount -= 1; + + if (p->refCount == 0) { + LstNode ln; + + ln = Lst_Member(openDirectories, p); + (void)Lst_Remove(openDirectories, ln); + + Hash_DeleteTable(&p->files); + free(p->name); + free(p); + } +} + +/*- + *----------------------------------------------------------------------- + * Dir_ClearPath -- + * Clear out all elements of the given search path. This is different + * from destroying the list, notice. + * + * Input: + * path Path to clear + * + * Results: + * None. + * + * Side Effects: + * The path is set to the empty list. + * + *----------------------------------------------------------------------- + */ +void +Dir_ClearPath(Lst path) +{ + Path *p; + while (!Lst_IsEmpty(path)) { + p = (Path *)Lst_DeQueue(path); + Dir_Destroy(p); + } +} + + +/*- + *----------------------------------------------------------------------- + * Dir_Concat -- + * Concatenate two paths, adding the second to the end of the first. + * Makes sure to avoid duplicates. + * + * Input: + * path1 Dest + * path2 Source + * + * Results: + * None + * + * Side Effects: + * Reference counts for added dirs are upped. + * + *----------------------------------------------------------------------- + */ +void +Dir_Concat(Lst path1, Lst path2) +{ + LstNode ln; + Path *p; + + for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { + p = (Path *)Lst_Datum(ln); + if (Lst_Member(path1, p) == NULL) { + p->refCount += 1; + (void)Lst_AtEnd(path1, p); + } + } +} + +/********** DEBUG INFO **********/ +void +Dir_PrintDirectories(void) +{ + LstNode ln; + Path *p; + + fprintf(debug_file, "#*** Directory Cache:\n"); + fprintf(debug_file, "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", + hits, misses, nearmisses, bigmisses, + (hits+bigmisses+nearmisses ? + hits * 100 / (hits + bigmisses + nearmisses) : 0)); + fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); + if (Lst_Open(openDirectories) == SUCCESS) { + while ((ln = Lst_Next(openDirectories)) != NULL) { + p = (Path *)Lst_Datum(ln); + fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits); + } + Lst_Close(openDirectories); + } +} + +static int +DirPrintDir(void *p, void *dummy) +{ + fprintf(debug_file, "%s ", ((Path *)p)->name); + return (dummy ? 0 : 0); +} + +void +Dir_PrintPath(Lst path) +{ + Lst_ForEach(path, DirPrintDir, NULL); +} diff --git a/buildrump.sh/src/usr.bin/make/dir.h b/buildrump.sh/src/usr.bin/make/dir.h new file mode 100644 index 00000000..aa004504 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/dir.h @@ -0,0 +1,108 @@ +/* $NetBSD: dir.h,v 1.15 2012/04/07 18:29:08 christos Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)dir.h 8.1 (Berkeley) 6/6/93 + */ + +/* dir.h -- + */ + +#ifndef _DIR +#define _DIR + +typedef struct Path { + char *name; /* Name of directory */ + int refCount; /* Number of paths with this directory */ + int hits; /* the number of times a file in this + * directory has been found */ + Hash_Table files; /* Hash table of files in directory */ +} Path; + +void Dir_Init(const char *); +void Dir_InitCur(const char *); +void Dir_InitDot(void); +void Dir_End(void); +void Dir_SetPATH(void); +Boolean Dir_HasWildcards(char *); +void Dir_Expand(const char *, Lst, Lst); +char *Dir_FindFile(const char *, Lst); +int Dir_FindHereOrAbove(char *, char *, char *, int); +int Dir_MTime(GNode *, Boolean); +Path *Dir_AddDir(Lst, const char *); +char *Dir_MakeFlags(const char *, Lst); +void Dir_ClearPath(Lst); +void Dir_Concat(Lst, Lst); +void Dir_PrintDirectories(void); +void Dir_PrintPath(Lst); +void Dir_Destroy(void *); +void * Dir_CopyDir(void *); + +#endif /* _DIR */ diff --git a/buildrump.sh/src/usr.bin/make/for.c b/buildrump.sh/src/usr.bin/make/for.c new file mode 100644 index 00000000..33bcf139 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/for.c @@ -0,0 +1,496 @@ +/* $NetBSD: for.c,v 1.49 2012/06/03 04:29:40 sjg Exp $ */ + +/* + * Copyright (c) 1992, 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: for.c,v 1.49 2012/06/03 04:29:40 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)for.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: for.c,v 1.49 2012/06/03 04:29:40 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * for.c -- + * Functions to handle loops in a makefile. + * + * Interface: + * For_Eval Evaluate the loop in the passed line. + * For_Run Run accumulated loop + * + */ + +#include <assert.h> +#include <ctype.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "buf.h" +#include "strlist.h" + +#define FOR_SUB_ESCAPE_CHAR 1 +#define FOR_SUB_ESCAPE_BRACE 2 +#define FOR_SUB_ESCAPE_PAREN 4 + +/* + * For statements are of the form: + * + * .for <variable> in <varlist> + * ... + * .endfor + * + * The trick is to look for the matching end inside for for loop + * To do that, we count the current nesting level of the for loops. + * and the .endfor statements, accumulating all the statements between + * the initial .for loop and the matching .endfor; + * then we evaluate the for loop for each variable in the varlist. + * + * Note that any nested fors are just passed through; they get handled + * recursively in For_Eval when we're expanding the enclosing for in + * For_Run. + */ + +static int forLevel = 0; /* Nesting level */ + +/* + * State of a for loop. + */ +typedef struct _For { + Buffer buf; /* Body of loop */ + strlist_t vars; /* Iteration variables */ + strlist_t items; /* Substitution items */ + char *parse_buf; + int short_var; + int sub_next; +} For; + +static For *accumFor; /* Loop being accumulated */ + + + +static char * +make_str(const char *ptr, int len) +{ + char *new_ptr; + + new_ptr = bmake_malloc(len + 1); + memcpy(new_ptr, ptr, len); + new_ptr[len] = 0; + return new_ptr; +} + +static void +For_Free(For *arg) +{ + Buf_Destroy(&arg->buf, TRUE); + strlist_clean(&arg->vars); + strlist_clean(&arg->items); + free(arg->parse_buf); + + free(arg); +} + +/*- + *----------------------------------------------------------------------- + * For_Eval -- + * Evaluate the for loop in the passed line. The line + * looks like this: + * .for <variable> in <varlist> + * + * Input: + * line Line to parse + * + * Results: + * 0: Not a .for statement, parse the line + * 1: We found a for loop + * -1: A .for statement with a bad syntax error, discard. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +int +For_Eval(char *line) +{ + For *new_for; + char *ptr = line, *sub; + int len; + int escapes; + unsigned char ch; + char **words, *word_buf; + int n, nwords; + + /* Skip the '.' and any following whitespace */ + for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + continue; + + /* + * If we are not in a for loop quickly determine if the statement is + * a for. + */ + if (ptr[0] != 'f' || ptr[1] != 'o' || ptr[2] != 'r' || + !isspace((unsigned char) ptr[3])) { + if (ptr[0] == 'e' && strncmp(ptr+1, "ndfor", 5) == 0) { + Parse_Error(PARSE_FATAL, "for-less endfor"); + return -1; + } + return 0; + } + ptr += 3; + + /* + * we found a for loop, and now we are going to parse it. + */ + + new_for = bmake_malloc(sizeof *new_for); + memset(new_for, 0, sizeof *new_for); + + /* Grab the variables. Terminate on "in". */ + for (;; ptr += len) { + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + if (*ptr == '\0') { + Parse_Error(PARSE_FATAL, "missing `in' in for"); + For_Free(new_for); + return -1; + } + for (len = 1; ptr[len] && !isspace((unsigned char)ptr[len]); len++) + continue; + if (len == 2 && ptr[0] == 'i' && ptr[1] == 'n') { + ptr += 2; + break; + } + if (len == 1) + new_for->short_var = 1; + strlist_add_str(&new_for->vars, make_str(ptr, len), len); + } + + if (strlist_num(&new_for->vars) == 0) { + Parse_Error(PARSE_FATAL, "no iteration variables in for"); + For_Free(new_for); + return -1; + } + + while (*ptr && isspace((unsigned char) *ptr)) + ptr++; + + /* + * Make a list with the remaining words + * The values are substituted as ${:U<value>...} so we must \ escape + * characters that break that syntax. + * Variables are fully expanded - so it is safe for escape $. + * We can't do the escapes here - because we don't know whether + * we are substuting into ${...} or $(...). + */ + sub = Var_Subst(NULL, ptr, VAR_GLOBAL, FALSE); + + /* + * Split into words allowing for quoted strings. + */ + words = brk_string(sub, &nwords, FALSE, &word_buf); + + free(sub); + + if (words != NULL) { + for (n = 0; n < nwords; n++) { + ptr = words[n]; + if (!*ptr) + continue; + escapes = 0; + while ((ch = *ptr++)) { + switch(ch) { + case ':': + case '$': + case '\\': + escapes |= FOR_SUB_ESCAPE_CHAR; + break; + case ')': + escapes |= FOR_SUB_ESCAPE_PAREN; + break; + case /*{*/ '}': + escapes |= FOR_SUB_ESCAPE_BRACE; + break; + } + } + /* + * We have to dup words[n] to maintain the semantics of + * strlist. + */ + strlist_add_str(&new_for->items, bmake_strdup(words[n]), escapes); + } + + free(words); + free(word_buf); + + if ((len = strlist_num(&new_for->items)) > 0 && + len % (n = strlist_num(&new_for->vars))) { + Parse_Error(PARSE_FATAL, + "Wrong number of words (%d) in .for substitution list" + " with %d vars", len, n); + /* + * Return 'success' so that the body of the .for loop is + * accumulated. + * Remove all items so that the loop doesn't iterate. + */ + strlist_clean(&new_for->items); + } + } + + Buf_Init(&new_for->buf, 0); + accumFor = new_for; + forLevel = 1; + return 1; +} + +/* + * Add another line to a .for loop. + * Returns 0 when the matching .endfor is reached. + */ + +int +For_Accum(char *line) +{ + char *ptr = line; + + if (*ptr == '.') { + + for (ptr++; *ptr && isspace((unsigned char) *ptr); ptr++) + continue; + + if (strncmp(ptr, "endfor", 6) == 0 && + (isspace((unsigned char) ptr[6]) || !ptr[6])) { + if (DEBUG(FOR)) + (void)fprintf(debug_file, "For: end for %d\n", forLevel); + if (--forLevel <= 0) + return 0; + } else if (strncmp(ptr, "for", 3) == 0 && + isspace((unsigned char) ptr[3])) { + forLevel++; + if (DEBUG(FOR)) + (void)fprintf(debug_file, "For: new loop %d\n", forLevel); + } + } + + Buf_AddBytes(&accumFor->buf, strlen(line), line); + Buf_AddByte(&accumFor->buf, '\n'); + return 1; +} + + +/*- + *----------------------------------------------------------------------- + * For_Run -- + * Run the for loop, imitating the actions of an include file + * + * Results: + * None. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ + +static int +for_var_len(const char *var) +{ + char ch, var_start, var_end; + int depth; + int len; + + var_start = *var; + if (var_start == 0) + /* just escape the $ */ + return 0; + + if (var_start == '(') + var_end = ')'; + else if (var_start == '{') + var_end = '}'; + else + /* Single char variable */ + return 1; + + depth = 1; + for (len = 1; (ch = var[len++]) != 0;) { + if (ch == var_start) + depth++; + else if (ch == var_end && --depth == 0) + return len; + } + + /* Variable end not found, escape the $ */ + return 0; +} + +static void +for_substitute(Buffer *cmds, strlist_t *items, unsigned int item_no, char ech) +{ + const char *item = strlist_str(items, item_no); + int len; + char ch; + + /* If there were no escapes, or the only escape is the other variable + * terminator, then just substitute the full string */ + if (!(strlist_info(items, item_no) & + (ech == ')' ? ~FOR_SUB_ESCAPE_BRACE : ~FOR_SUB_ESCAPE_PAREN))) { + Buf_AddBytes(cmds, strlen(item), item); + return; + } + + /* Escape ':', '$', '\\' and 'ech' - removed by :U processing */ + while ((ch = *item++) != 0) { + if (ch == '$') { + len = for_var_len(item); + if (len != 0) { + Buf_AddBytes(cmds, len + 1, item - 1); + item += len; + continue; + } + Buf_AddByte(cmds, '\\'); + } else if (ch == ':' || ch == '\\' || ch == ech) + Buf_AddByte(cmds, '\\'); + Buf_AddByte(cmds, ch); + } +} + +static char * +For_Iterate(void *v_arg, size_t *ret_len) +{ + For *arg = v_arg; + int i, len; + char *var; + char *cp; + char *cmd_cp; + char *body_end; + char ch; + Buffer cmds; + + if (arg->sub_next + strlist_num(&arg->vars) > strlist_num(&arg->items)) { + /* No more iterations */ + For_Free(arg); + return NULL; + } + + free(arg->parse_buf); + arg->parse_buf = NULL; + + /* + * Scan the for loop body and replace references to the loop variables + * with variable references that expand to the required text. + * Using variable expansions ensures that the .for loop can't generate + * syntax, and that the later parsing will still see a variable. + * We assume that the null variable will never be defined. + * + * The detection of substitions of the loop control variable is naive. + * Many of the modifiers use \ to escape $ (not $) so it is possible + * to contrive a makefile where an unwanted substitution happens. + */ + + cmd_cp = Buf_GetAll(&arg->buf, &len); + body_end = cmd_cp + len; + Buf_Init(&cmds, len + 256); + for (cp = cmd_cp; (cp = strchr(cp, '$')) != NULL;) { + char ech; + ch = *++cp; + if ((ch == '(' && (ech = ')')) || (ch == '{' && (ech = '}'))) { + cp++; + /* Check variable name against the .for loop variables */ + STRLIST_FOREACH(var, &arg->vars, i) { + len = strlist_info(&arg->vars, i); + if (memcmp(cp, var, len) != 0) + continue; + if (cp[len] != ':' && cp[len] != ech && cp[len] != '\\') + continue; + /* Found a variable match. Replace with :U<value> */ + Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); + Buf_AddBytes(&cmds, 2, ":U"); + cp += len; + cmd_cp = cp; + for_substitute(&cmds, &arg->items, arg->sub_next + i, ech); + break; + } + continue; + } + if (ch == 0) + break; + /* Probably a single character name, ignore $$ and stupid ones. {*/ + if (!arg->short_var || strchr("}):$", ch) != NULL) { + cp++; + continue; + } + STRLIST_FOREACH(var, &arg->vars, i) { + if (var[0] != ch || var[1] != 0) + continue; + /* Found a variable match. Replace with ${:U<value>} */ + Buf_AddBytes(&cmds, cp - cmd_cp, cmd_cp); + Buf_AddBytes(&cmds, 3, "{:U"); + cmd_cp = ++cp; + for_substitute(&cmds, &arg->items, arg->sub_next + i, /*{*/ '}'); + Buf_AddBytes(&cmds, 1, "}"); + break; + } + } + Buf_AddBytes(&cmds, body_end - cmd_cp, cmd_cp); + + cp = Buf_Destroy(&cmds, FALSE); + if (DEBUG(FOR)) + (void)fprintf(debug_file, "For: loop body:\n%s", cp); + + arg->sub_next += strlist_num(&arg->vars); + + arg->parse_buf = cp; + *ret_len = strlen(cp); + return cp; +} + +void +For_Run(int lineno) +{ + For *arg; + + arg = accumFor; + accumFor = NULL; + + if (strlist_num(&arg->items) == 0) { + /* Nothing to expand - possibly due to an earlier syntax error. */ + For_Free(arg); + return; + } + + Parse_SetInput(NULL, lineno, -1, For_Iterate, arg); +} diff --git a/buildrump.sh/src/usr.bin/make/hash.c b/buildrump.sh/src/usr.bin/make/hash.c new file mode 100644 index 00000000..ed236444 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/hash.c @@ -0,0 +1,466 @@ +/* $NetBSD: hash.c,v 1.20 2013/11/14 00:27:05 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: hash.c,v 1.20 2013/11/14 00:27:05 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hash.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: hash.c,v 1.20 2013/11/14 00:27:05 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/* hash.c -- + * + * This module contains routines to manipulate a hash table. + * See hash.h for a definition of the structure of the hash + * table. Hash tables grow automatically as the amount of + * information increases. + */ +#include "sprite.h" +#include "make.h" +#include "hash.h" + +/* + * Forward references to local procedures that are used before they're + * defined: + */ + +static void RebuildTable(Hash_Table *); + +/* + * The following defines the ratio of # entries to # buckets + * at which we rebuild the table to make it larger. + */ + +#define rebuildLimit 3 + +/* + *--------------------------------------------------------- + * + * Hash_InitTable -- + * + * This routine just sets up the hash table. + * + * Input: + * t Structure to to hold table. + * numBuckets How many buckets to create for starters. This + * number is rounded up to a power of two. If + * <= 0, a reasonable default is chosen. The + * table will grow in size later as needed. + * + * Results: + * None. + * + * Side Effects: + * Memory is allocated for the initial bucket area. + * + *--------------------------------------------------------- + */ + +void +Hash_InitTable(Hash_Table *t, int numBuckets) +{ + int i; + struct Hash_Entry **hp; + + /* + * Round up the size to a power of two. + */ + if (numBuckets <= 0) + i = 16; + else { + for (i = 2; i < numBuckets; i <<= 1) + continue; + } + t->numEntries = 0; + t->size = i; + t->mask = i - 1; + t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); + while (--i >= 0) + *hp++ = NULL; +} + +/* + *--------------------------------------------------------- + * + * Hash_DeleteTable -- + * + * This routine removes everything from a hash table + * and frees up the memory space it occupied (except for + * the space in the Hash_Table structure). + * + * Results: + * None. + * + * Side Effects: + * Lots of memory is freed up. + * + *--------------------------------------------------------- + */ + +void +Hash_DeleteTable(Hash_Table *t) +{ + struct Hash_Entry **hp, *h, *nexth = NULL; + int i; + + for (hp = t->bucketPtr, i = t->size; --i >= 0;) { + for (h = *hp++; h != NULL; h = nexth) { + nexth = h->next; + free(h); + } + } + free(t->bucketPtr); + + /* + * Set up the hash table to cause memory faults on any future access + * attempts until re-initialization. + */ + t->bucketPtr = NULL; +} + +/* + *--------------------------------------------------------- + * + * Hash_FindEntry -- + * + * Searches a hash table for an entry corresponding to key. + * + * Input: + * t Hash table to search. + * key A hash key. + * + * Results: + * The return value is a pointer to the entry for key, + * if key was present in the table. If key was not + * present, NULL is returned. + * + * Side Effects: + * None. + * + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_FindEntry(Hash_Table *t, const char *key) +{ + Hash_Entry *e; + unsigned h; + const char *p; + + if (t == NULL || t->bucketPtr == NULL) { + return NULL; + } + for (h = 0, p = key; *p;) + h = (h << 5) - h + *p++; + p = key; + for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) + if (e->namehash == h && strcmp(e->name, p) == 0) + return (e); + return NULL; +} + +/* + *--------------------------------------------------------- + * + * Hash_CreateEntry -- + * + * Searches a hash table for an entry corresponding to + * key. If no entry is found, then one is created. + * + * Input: + * t Hash table to search. + * key A hash key. + * newPtr Filled in with TRUE if new entry created, + * FALSE otherwise. + * + * Results: + * The return value is a pointer to the entry. If *newPtr + * isn't NULL, then *newPtr is filled in with TRUE if a + * new entry was created, and FALSE if an entry already existed + * with the given key. + * + * Side Effects: + * Memory may be allocated, and the hash buckets may be modified. + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_CreateEntry(Hash_Table *t, const char *key, Boolean *newPtr) +{ + Hash_Entry *e; + unsigned h; + const char *p; + int keylen; + struct Hash_Entry **hp; + + /* + * Hash the key. As a side effect, save the length (strlen) of the + * key in case we need to create the entry. + */ + for (h = 0, p = key; *p;) + h = (h << 5) - h + *p++; + keylen = p - key; + p = key; + for (e = t->bucketPtr[h & t->mask]; e != NULL; e = e->next) { + if (e->namehash == h && strcmp(e->name, p) == 0) { + if (newPtr != NULL) + *newPtr = FALSE; + return (e); + } + } + + /* + * The desired entry isn't there. Before allocating a new entry, + * expand the table if necessary (and this changes the resulting + * bucket chain). + */ + if (t->numEntries >= rebuildLimit * t->size) + RebuildTable(t); + e = bmake_malloc(sizeof(*e) + keylen); + hp = &t->bucketPtr[h & t->mask]; + e->next = *hp; + *hp = e; + Hash_SetValue(e, NULL); + e->namehash = h; + (void)strcpy(e->name, p); + t->numEntries++; + + if (newPtr != NULL) + *newPtr = TRUE; + return (e); +} + +/* + *--------------------------------------------------------- + * + * Hash_DeleteEntry -- + * + * Delete the given hash table entry and free memory associated with + * it. + * + * Results: + * None. + * + * Side Effects: + * Hash chain that entry lives in is modified and memory is freed. + * + *--------------------------------------------------------- + */ + +void +Hash_DeleteEntry(Hash_Table *t, Hash_Entry *e) +{ + Hash_Entry **hp, *p; + + if (e == NULL) + return; + for (hp = &t->bucketPtr[e->namehash & t->mask]; + (p = *hp) != NULL; hp = &p->next) { + if (p == e) { + *hp = p->next; + free(p); + t->numEntries--; + return; + } + } + (void)write(2, "bad call to Hash_DeleteEntry\n", 29); + abort(); +} + +/* + *--------------------------------------------------------- + * + * Hash_EnumFirst -- + * This procedure sets things up for a complete search + * of all entries recorded in the hash table. + * + * Input: + * t Table to be searched. + * searchPtr Area in which to keep state about search. + * + * Results: + * The return value is the address of the first entry in + * the hash table, or NULL if the table is empty. + * + * Side Effects: + * The information in searchPtr is initialized so that successive + * calls to Hash_Next will return successive HashEntry's + * from the table. + * + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_EnumFirst(Hash_Table *t, Hash_Search *searchPtr) +{ + searchPtr->tablePtr = t; + searchPtr->nextIndex = 0; + searchPtr->hashEntryPtr = NULL; + return Hash_EnumNext(searchPtr); +} + +/* + *--------------------------------------------------------- + * + * Hash_EnumNext -- + * This procedure returns successive entries in the hash table. + * + * Input: + * searchPtr Area used to keep state about search. + * + * Results: + * The return value is a pointer to the next HashEntry + * in the table, or NULL when the end of the table is + * reached. + * + * Side Effects: + * The information in searchPtr is modified to advance to the + * next entry. + * + *--------------------------------------------------------- + */ + +Hash_Entry * +Hash_EnumNext(Hash_Search *searchPtr) +{ + Hash_Entry *e; + Hash_Table *t = searchPtr->tablePtr; + + /* + * The hashEntryPtr field points to the most recently returned + * entry, or is nil if we are starting up. If not nil, we have + * to start at the next one in the chain. + */ + e = searchPtr->hashEntryPtr; + if (e != NULL) + e = e->next; + /* + * If the chain ran out, or if we are starting up, we need to + * find the next nonempty chain. + */ + while (e == NULL) { + if (searchPtr->nextIndex >= t->size) + return NULL; + e = t->bucketPtr[searchPtr->nextIndex++]; + } + searchPtr->hashEntryPtr = e; + return (e); +} + +/* + *--------------------------------------------------------- + * + * RebuildTable -- + * This local routine makes a new hash table that + * is larger than the old one. + * + * Results: + * None. + * + * Side Effects: + * The entire hash table is moved, so any bucket numbers + * from the old table are invalid. + * + *--------------------------------------------------------- + */ + +static void +RebuildTable(Hash_Table *t) +{ + Hash_Entry *e, *next = NULL, **hp, **xp; + int i, mask; + Hash_Entry **oldhp; + int oldsize; + + oldhp = t->bucketPtr; + oldsize = i = t->size; + i <<= 1; + t->size = i; + t->mask = mask = i - 1; + t->bucketPtr = hp = bmake_malloc(sizeof(*hp) * i); + while (--i >= 0) + *hp++ = NULL; + for (hp = oldhp, i = oldsize; --i >= 0;) { + for (e = *hp++; e != NULL; e = next) { + next = e->next; + xp = &t->bucketPtr[e->namehash & mask]; + e->next = *xp; + *xp = e; + } + } + free(oldhp); +} diff --git a/buildrump.sh/src/usr.bin/make/hash.h b/buildrump.sh/src/usr.bin/make/hash.h new file mode 100644 index 00000000..31d2ff1e --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/hash.h @@ -0,0 +1,154 @@ +/* $NetBSD: hash.h,v 1.10 2009/01/24 10:59:09 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)hash.h 8.1 (Berkeley) 6/6/93 + */ + +/* hash.h -- + * + * This file contains definitions used by the hash module, + * which maintains hash tables. + */ + +#ifndef _HASH +#define _HASH + +/* + * The following defines one entry in the hash table. + */ + +typedef struct Hash_Entry { + struct Hash_Entry *next; /* Used to link together all the + * entries associated with the same + * bucket. */ + union { + void *clientPtr; /* Arbitrary pointer */ + time_t clientTime; /* Arbitrary Time */ + } clientInfo; + unsigned namehash; /* hash value of key */ + char name[1]; /* key string */ +} Hash_Entry; + +typedef struct Hash_Table { + struct Hash_Entry **bucketPtr;/* Pointers to Hash_Entry, one + * for each bucket in the table. */ + int size; /* Actual size of array. */ + int numEntries; /* Number of entries in the table. */ + int mask; /* Used to select bits for hashing. */ +} Hash_Table; + +/* + * The following structure is used by the searching routines + * to record where we are in the search. + */ + +typedef struct Hash_Search { + Hash_Table *tablePtr; /* Table being searched. */ + int nextIndex; /* Next bucket to check (after current). */ + Hash_Entry *hashEntryPtr; /* Next entry to check in current bucket. */ +} Hash_Search; + +/* + * Macros. + */ + +/* + * void * Hash_GetValue(h) + * Hash_Entry *h; + */ + +#define Hash_GetValue(h) ((h)->clientInfo.clientPtr) +#define Hash_GetTimeValue(h) ((h)->clientInfo.clientTime) + +/* + * Hash_SetValue(h, val); + * Hash_Entry *h; + * char *val; + */ + +#define Hash_SetValue(h, val) ((h)->clientInfo.clientPtr = (val)) +#define Hash_SetTimeValue(h, val) ((h)->clientInfo.clientTime = (val)) + +/* + * Hash_Size(n) returns the number of words in an object of n bytes + */ + +#define Hash_Size(n) (((n) + sizeof (int) - 1) / sizeof (int)) + +void Hash_InitTable(Hash_Table *, int); +void Hash_DeleteTable(Hash_Table *); +Hash_Entry *Hash_FindEntry(Hash_Table *, const char *); +Hash_Entry *Hash_CreateEntry(Hash_Table *, const char *, Boolean *); +void Hash_DeleteEntry(Hash_Table *, Hash_Entry *); +Hash_Entry *Hash_EnumFirst(Hash_Table *, Hash_Search *); +Hash_Entry *Hash_EnumNext(Hash_Search *); + +#endif /* _HASH */ diff --git a/buildrump.sh/src/usr.bin/make/job.c b/buildrump.sh/src/usr.bin/make/job.c new file mode 100644 index 00000000..cf1f0245 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/job.c @@ -0,0 +1,3038 @@ +/* $NetBSD: job.c,v 1.180 2015/04/16 13:31:03 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: job.c,v 1.180 2015/04/16 13:31:03 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)job.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: job.c,v 1.180 2015/04/16 13:31:03 joerg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * job.c -- + * handle the creation etc. of our child processes. + * + * Interface: + * Job_Make Start the creation of the given target. + * + * Job_CatchChildren Check for and handle the termination of any + * children. This must be called reasonably + * frequently to keep the whole make going at + * a decent clip, since job table entries aren't + * removed until their process is caught this way. + * + * Job_CatchOutput Print any output our children have produced. + * Should also be called fairly frequently to + * keep the user informed of what's going on. + * If no output is waiting, it will block for + * a time given by the SEL_* constants, below, + * or until output is ready. + * + * Job_Init Called to intialize this module. in addition, + * any commands attached to the .BEGIN target + * are executed before this function returns. + * Hence, the makefile must have been parsed + * before this function is called. + * + * Job_End Cleanup any memory used. + * + * Job_ParseShell Given the line following a .SHELL target, parse + * the line as a shell specification. Returns + * FAILURE if the spec was incorrect. + * + * Job_Finish Perform any final processing which needs doing. + * This includes the execution of any commands + * which have been/were attached to the .END + * target. It should only be called when the + * job table is empty. + * + * Job_AbortAll Abort all currently running jobs. It doesn't + * handle output or do anything for the jobs, + * just kills them. It should only be called in + * an emergency, as it were. + * + * Job_CheckCommands Verify that the commands for a target are + * ok. Provide them if necessary and possible. + * + * Job_Touch Update a target without really updating it. + * + * Job_Wait Wait for all currently-running jobs to finish. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <sys/time.h> +#include <sys/wait.h> + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#ifndef USE_SELECT +#include <poll.h> +#endif +#include <signal.h> +#include <stdio.h> +#include <string.h> +#include <utime.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" +# define STATIC static + +/* + * error handling variables + */ +static int errors = 0; /* number of errors reported */ +static int aborting = 0; /* why is the make aborting? */ +#define ABORT_ERROR 1 /* Because of an error */ +#define ABORT_INTERRUPT 2 /* Because it was interrupted */ +#define ABORT_WAIT 3 /* Waiting for jobs to finish */ +#define JOB_TOKENS "+EI+" /* Token to requeue for each abort state */ + +/* + * this tracks the number of tokens currently "out" to build jobs. + */ +int jobTokensRunning = 0; +int not_parallel = 0; /* set if .NOT_PARALLEL */ + +/* + * XXX: Avoid SunOS bug... FILENO() is fp->_file, and file + * is a char! So when we go above 127 we turn negative! + */ +#define FILENO(a) ((unsigned) fileno(a)) + +/* + * post-make command processing. The node postCommands is really just the + * .END target but we keep it around to avoid having to search for it + * all the time. + */ +static GNode *postCommands = NULL; + /* node containing commands to execute when + * everything else is done */ +static int numCommands; /* The number of commands actually printed + * for a target. Should this number be + * 0, no shell will be executed. */ + +/* + * Return values from JobStart. + */ +#define JOB_RUNNING 0 /* Job is running */ +#define JOB_ERROR 1 /* Error in starting the job */ +#define JOB_FINISHED 2 /* The job is already finished */ + +/* + * Descriptions for various shells. + * + * The build environment may set DEFSHELL_INDEX to one of + * DEFSHELL_INDEX_SH, DEFSHELL_INDEX_KSH, or DEFSHELL_INDEX_CSH, to + * select one of the prefedined shells as the default shell. + * + * Alternatively, the build environment may set DEFSHELL_CUSTOM to the + * name or the full path of a sh-compatible shell, which will be used as + * the default shell. + * + * ".SHELL" lines in Makefiles can choose the default shell from the + # set defined here, or add additional shells. + */ + +#ifdef DEFSHELL_CUSTOM +#define DEFSHELL_INDEX_CUSTOM 0 +#define DEFSHELL_INDEX_SH 1 +#define DEFSHELL_INDEX_KSH 2 +#define DEFSHELL_INDEX_CSH 3 +#else /* !DEFSHELL_CUSTOM */ +#define DEFSHELL_INDEX_SH 0 +#define DEFSHELL_INDEX_KSH 1 +#define DEFSHELL_INDEX_CSH 2 +#endif /* !DEFSHELL_CUSTOM */ + +#ifndef DEFSHELL_INDEX +#define DEFSHELL_INDEX 0 /* DEFSHELL_INDEX_CUSTOM or DEFSHELL_INDEX_SH */ +#endif /* !DEFSHELL_INDEX */ + +static Shell shells[] = { +#ifdef DEFSHELL_CUSTOM + /* + * An sh-compatible shell with a non-standard name. + * + * Keep this in sync with the "sh" description below, but avoid + * non-portable features that might not be supplied by all + * sh-compatible shells. + */ +{ + DEFSHELL_CUSTOM, + FALSE, "", "", "", 0, + FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#', + "", + "", +}, +#endif /* DEFSHELL_CUSTOM */ + /* + * SH description. Echo control is also possible and, under + * sun UNIX anyway, one can even control error checking. + */ +{ + "sh", + FALSE, "", "", "", 0, + FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#', +#if defined(MAKE_NATIVE) && defined(__NetBSD__) + "q", +#else + "", +#endif + "", +}, + /* + * KSH description. + */ +{ + "ksh", + TRUE, "set +v", "set -v", "set +v", 6, + FALSE, "echo \"%s\"\n", "%s\n", "{ %s \n} || exit $?\n", "'\n'", '#', + "v", + "", +}, + /* + * CSH description. The csh can do echo control by playing + * with the setting of the 'echo' shell variable. Sadly, + * however, it is unable to do error control nicely. + */ +{ + "csh", + TRUE, "unset verbose", "set verbose", "unset verbose", 10, + FALSE, "echo \"%s\"\n", "csh -c \"%s || exit 0\"\n", "", "'\\\n'", '#', + "v", "e", +}, + /* + * UNKNOWN. + */ +{ + NULL, + FALSE, NULL, NULL, NULL, 0, + FALSE, NULL, NULL, NULL, NULL, 0, + NULL, NULL, +} +}; +static Shell *commandShell = &shells[DEFSHELL_INDEX]; /* this is the shell to + * which we pass all + * commands in the Makefile. + * It is set by the + * Job_ParseShell function */ +const char *shellPath = NULL, /* full pathname of + * executable image */ + *shellName = NULL; /* last component of shell */ +char *shellErrFlag = NULL; +static const char *shellArgv = NULL; /* Custom shell args */ + + +STATIC Job *job_table; /* The structures that describe them */ +STATIC Job *job_table_end; /* job_table + maxJobs */ +static int wantToken; /* we want a token */ +static int lurking_children = 0; +static int make_suspended = 0; /* non-zero if we've seen a SIGTSTP (etc) */ + +/* + * Set of descriptors of pipes connected to + * the output channels of children + */ +static struct pollfd *fds = NULL; +static Job **jobfds = NULL; +static int nfds = 0; +static void watchfd(Job *); +static void clearfd(Job *); +static int readyfd(Job *); + +STATIC GNode *lastNode; /* The node for which output was most recently + * produced. */ +static char *targPrefix = NULL; /* What we print at the start of TARG_FMT */ +static Job tokenWaitJob; /* token wait pseudo-job */ + +static Job childExitJob; /* child exit pseudo-job */ +#define CHILD_EXIT "." +#define DO_JOB_RESUME "R" + +#define TARG_FMT "%s %s ---\n" /* Default format */ +#define MESSAGE(fp, gn) \ + if (maxJobs != 1 && targPrefix && *targPrefix) \ + (void)fprintf(fp, TARG_FMT, targPrefix, gn->name) + +static sigset_t caught_signals; /* Set of signals we handle */ +#if defined(SYSV) +#define KILLPG(pid, sig) kill(-(pid), (sig)) +#else +#define KILLPG(pid, sig) killpg((pid), (sig)) +#endif + +static void JobChildSig(int); +static void JobContinueSig(int); +static Job *JobFindPid(int, int, Boolean); +static int JobPrintCommand(void *, void *); +static int JobSaveCommand(void *, void *); +static void JobClose(Job *); +static void JobExec(Job *, char **); +static void JobMakeArgv(Job *, char **); +static int JobStart(GNode *, int); +static char *JobOutput(Job *, char *, char *, int); +static void JobDoOutput(Job *, Boolean); +static Shell *JobMatchShell(const char *); +static void JobInterrupt(int, int) MAKE_ATTR_DEAD; +static void JobRestartJobs(void); +static void JobTokenAdd(void); +static void JobSigLock(sigset_t *); +static void JobSigUnlock(sigset_t *); +static void JobSigReset(void); + +const char *malloc_options="A"; + +static void +job_table_dump(const char *where) +{ + Job *job; + + fprintf(debug_file, "job table @ %s\n", where); + for (job = job_table; job < job_table_end; job++) { + fprintf(debug_file, "job %d, status %d, flags %d, pid %d\n", + (int)(job - job_table), job->job_state, job->flags, job->pid); + } +} + +/* + * JobSigLock/JobSigUnlock + * + * Signal lock routines to get exclusive access. Currently used to + * protect `jobs' and `stoppedJobs' list manipulations. + */ +static void JobSigLock(sigset_t *omaskp) +{ + if (sigprocmask(SIG_BLOCK, &caught_signals, omaskp) != 0) { + Punt("JobSigLock: sigprocmask: %s", strerror(errno)); + sigemptyset(omaskp); + } +} + +static void JobSigUnlock(sigset_t *omaskp) +{ + (void)sigprocmask(SIG_SETMASK, omaskp, NULL); +} + +static void +JobCreatePipe(Job *job, int minfd) +{ + int i, fd; + + if (pipe(job->jobPipe) == -1) + Punt("Cannot create pipe: %s", strerror(errno)); + + for (i = 0; i < 2; i++) { + /* Avoid using low numbered fds */ + fd = fcntl(job->jobPipe[i], F_DUPFD, minfd); + if (fd != -1) { + close(job->jobPipe[i]); + job->jobPipe[i] = fd; + } + } + + /* Set close-on-exec flag for both */ + (void)fcntl(job->jobPipe[0], F_SETFD, 1); + (void)fcntl(job->jobPipe[1], F_SETFD, 1); + + /* + * We mark the input side of the pipe non-blocking; we poll(2) the + * pipe when we're waiting for a job token, but we might lose the + * race for the token when a new one becomes available, so the read + * from the pipe should not block. + */ + fcntl(job->jobPipe[0], F_SETFL, + fcntl(job->jobPipe[0], F_GETFL, 0) | O_NONBLOCK); +} + +/*- + *----------------------------------------------------------------------- + * JobCondPassSig -- + * Pass a signal to a job + * + * Input: + * signop Signal to send it + * + * Side Effects: + * None, except the job may bite it. + * + *----------------------------------------------------------------------- + */ +static void +JobCondPassSig(int signo) +{ + Job *job; + + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "JobCondPassSig(%d) called.\n", signo); + } + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state != JOB_ST_RUNNING) + continue; + if (DEBUG(JOB)) { + (void)fprintf(debug_file, + "JobCondPassSig passing signal %d to child %d.\n", + signo, job->pid); + } + KILLPG(job->pid, signo); + } +} + +/*- + *----------------------------------------------------------------------- + * JobChldSig -- + * SIGCHLD handler. + * + * Input: + * signo The signal number we've received + * + * Results: + * None. + * + * Side Effects: + * Sends a token on the child exit pipe to wake us up from + * select()/poll(). + * + *----------------------------------------------------------------------- + */ +static void +JobChildSig(int signo MAKE_ATTR_UNUSED) +{ + while (write(childExitJob.outPipe, CHILD_EXIT, 1) == -1 && errno == EAGAIN) + continue; +} + + +/*- + *----------------------------------------------------------------------- + * JobContinueSig -- + * Resume all stopped jobs. + * + * Input: + * signo The signal number we've received + * + * Results: + * None. + * + * Side Effects: + * Jobs start running again. + * + *----------------------------------------------------------------------- + */ +static void +JobContinueSig(int signo MAKE_ATTR_UNUSED) +{ + /* + * Defer sending to SIGCONT to our stopped children until we return + * from the signal handler. + */ + while (write(childExitJob.outPipe, DO_JOB_RESUME, 1) == -1 && + errno == EAGAIN) + continue; +} + +/*- + *----------------------------------------------------------------------- + * JobPassSig -- + * Pass a signal on to all jobs, then resend to ourselves. + * + * Input: + * signo The signal number we've received + * + * Results: + * None. + * + * Side Effects: + * We die by the same signal. + * + *----------------------------------------------------------------------- + */ +MAKE_ATTR_DEAD static void +JobPassSig_int(int signo) +{ + /* Run .INTERRUPT target then exit */ + JobInterrupt(TRUE, signo); +} + +MAKE_ATTR_DEAD static void +JobPassSig_term(int signo) +{ + /* Dont run .INTERRUPT target then exit */ + JobInterrupt(FALSE, signo); +} + +static void +JobPassSig_suspend(int signo) +{ + sigset_t nmask, omask; + struct sigaction act; + + /* Suppress job started/continued messages */ + make_suspended = 1; + + /* Pass the signal onto every job */ + JobCondPassSig(signo); + + /* + * Send ourselves the signal now we've given the message to everyone else. + * Note we block everything else possible while we're getting the signal. + * This ensures that all our jobs get continued when we wake up before + * we take any other signal. + */ + sigfillset(&nmask); + sigdelset(&nmask, signo); + (void)sigprocmask(SIG_SETMASK, &nmask, &omask); + + act.sa_handler = SIG_DFL; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + (void)sigaction(signo, &act, NULL); + + if (DEBUG(JOB)) { + (void)fprintf(debug_file, + "JobPassSig passing signal %d to self.\n", signo); + } + + (void)kill(getpid(), signo); + + /* + * We've been continued. + * + * A whole host of signals continue to happen! + * SIGCHLD for any processes that actually suspended themselves. + * SIGCHLD for any processes that exited while we were alseep. + * The SIGCONT that actually caused us to wakeup. + * + * Since we defer passing the SIGCONT on to our children until + * the main processing loop, we can be sure that all the SIGCHLD + * events will have happened by then - and that the waitpid() will + * collect the child 'suspended' events. + * For correct sequencing we just need to ensure we process the + * waitpid() before passign on the SIGCONT. + * + * In any case nothing else is needed here. + */ + + /* Restore handler and signal mask */ + act.sa_handler = JobPassSig_suspend; + (void)sigaction(signo, &act, NULL); + (void)sigprocmask(SIG_SETMASK, &omask, NULL); +} + +/*- + *----------------------------------------------------------------------- + * JobFindPid -- + * Compare the pid of the job with the given pid and return 0 if they + * are equal. This function is called from Job_CatchChildren + * to find the job descriptor of the finished job. + * + * Input: + * job job to examine + * pid process id desired + * + * Results: + * Job with matching pid + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static Job * +JobFindPid(int pid, int status, Boolean isJobs) +{ + Job *job; + + for (job = job_table; job < job_table_end; job++) { + if ((job->job_state == status) && job->pid == pid) + return job; + } + if (DEBUG(JOB) && isJobs) + job_table_dump("no pid"); + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * JobPrintCommand -- + * Put out another command for the given job. If the command starts + * with an @ or a - we process it specially. In the former case, + * so long as the -s and -n flags weren't given to make, we stick + * a shell-specific echoOff command in the script. In the latter, + * we ignore errors for the entire job, unless the shell has error + * control. + * If the command is just "..." we take all future commands for this + * job to be commands to be executed once the entire graph has been + * made and return non-zero to signal that the end of the commands + * was reached. These commands are later attached to the postCommands + * node and executed by Job_End when all things are done. + * This function is called from JobStart via Lst_ForEach. + * + * Input: + * cmdp command string to print + * jobp job for which to print it + * + * Results: + * Always 0, unless the command was "..." + * + * Side Effects: + * If the command begins with a '-' and the shell has no error control, + * the JOB_IGNERR flag is set in the job descriptor. + * If the command is "..." and we're not ignoring such things, + * tailCmds is set to the successor node of the cmd. + * numCommands is incremented if the command is actually printed. + *----------------------------------------------------------------------- + */ +static int +JobPrintCommand(void *cmdp, void *jobp) +{ + Boolean noSpecials; /* true if we shouldn't worry about + * inserting special commands into + * the input stream. */ + Boolean shutUp = FALSE; /* true if we put a no echo command + * into the command file */ + Boolean errOff = FALSE; /* true if we turned error checking + * off before printing the command + * and need to turn it back on */ + const char *cmdTemplate; /* Template to use when printing the + * command */ + char *cmdStart; /* Start of expanded command */ + char *escCmd = NULL; /* Command with quotes/backticks escaped */ + char *cmd = (char *)cmdp; + Job *job = (Job *)jobp; + int i, j; + + noSpecials = NoExecute(job->node); + + if (strcmp(cmd, "...") == 0) { + job->node->type |= OP_SAVE_CMDS; + if ((job->flags & JOB_IGNDOTS) == 0) { + job->tailCmds = Lst_Succ(Lst_Member(job->node->commands, + cmd)); + return 1; + } + return 0; + } + +#define DBPRINTF(fmt, arg) if (DEBUG(JOB)) { \ + (void)fprintf(debug_file, fmt, arg); \ + } \ + (void)fprintf(job->cmdFILE, fmt, arg); \ + (void)fflush(job->cmdFILE); + + numCommands += 1; + + cmdStart = cmd = Var_Subst(NULL, cmd, job->node, FALSE); + + cmdTemplate = "%s\n"; + + /* + * Check for leading @' and -'s to control echoing and error checking. + */ + while (*cmd == '@' || *cmd == '-' || (*cmd == '+')) { + switch (*cmd) { + case '@': + shutUp = DEBUG(LOUD) ? FALSE : TRUE; + break; + case '-': + errOff = TRUE; + break; + case '+': + if (noSpecials) { + /* + * We're not actually executing anything... + * but this one needs to be - use compat mode just for it. + */ + CompatRunCommand(cmdp, job->node); + return 0; + } + break; + } + cmd++; + } + + while (isspace((unsigned char) *cmd)) + cmd++; + + /* + * If the shell doesn't have error control the alternate echo'ing will + * be done (to avoid showing additional error checking code) + * and this will need the characters '$ ` \ "' escaped + */ + + if (!commandShell->hasErrCtl) { + /* Worst that could happen is every char needs escaping. */ + escCmd = bmake_malloc((strlen(cmd) * 2) + 1); + for (i = 0, j= 0; cmd[i] != '\0'; i++, j++) { + if (cmd[i] == '$' || cmd[i] == '`' || cmd[i] == '\\' || + cmd[i] == '"') + escCmd[j++] = '\\'; + escCmd[j] = cmd[i]; + } + escCmd[j] = 0; + } + + if (shutUp) { + if (!(job->flags & JOB_SILENT) && !noSpecials && + commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + } else { + if (commandShell->hasErrCtl) + shutUp = FALSE; + } + } + + if (errOff) { + if (!noSpecials) { + if (commandShell->hasErrCtl) { + /* + * we don't want the error-control commands showing + * up either, so we turn off echoing while executing + * them. We could put another field in the shell + * structure to tell JobDoOutput to look for this + * string too, but why make it any more complex than + * it already is? + */ + if (!(job->flags & JOB_SILENT) && !shutUp && + commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + DBPRINTF("%s\n", commandShell->ignErr); + DBPRINTF("%s\n", commandShell->echoOn); + } else { + DBPRINTF("%s\n", commandShell->ignErr); + } + } else if (commandShell->ignErr && + (*commandShell->ignErr != '\0')) + { + /* + * The shell has no error control, so we need to be + * weird to get it to ignore any errors from the command. + * If echoing is turned on, we turn it off and use the + * errCheck template to echo the command. Leave echoing + * off so the user doesn't see the weirdness we go through + * to ignore errors. Set cmdTemplate to use the weirdness + * instead of the simple "%s\n" template. + */ + job->flags |= JOB_IGNERR; + if (!(job->flags & JOB_SILENT) && !shutUp) { + if (commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + } + DBPRINTF(commandShell->errCheck, escCmd); + shutUp = TRUE; + } else { + if (!shutUp) { + DBPRINTF(commandShell->errCheck, escCmd); + } + } + cmdTemplate = commandShell->ignErr; + /* + * The error ignoration (hee hee) is already taken care + * of by the ignErr template, so pretend error checking + * is still on. + */ + errOff = FALSE; + } else { + errOff = FALSE; + } + } else { + errOff = FALSE; + } + } else { + + /* + * If errors are being checked and the shell doesn't have error control + * but does supply an errOut template, then setup commands to run + * through it. + */ + + if (!commandShell->hasErrCtl && commandShell->errOut && + (*commandShell->errOut != '\0')) { + if (!(job->flags & JOB_SILENT) && !shutUp) { + if (commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOff); + } + DBPRINTF(commandShell->errCheck, escCmd); + shutUp = TRUE; + } + /* If it's a comment line or blank, treat as an ignored error */ + if ((escCmd[0] == commandShell->commentChar) || + (escCmd[0] == 0)) + cmdTemplate = commandShell->ignErr; + else + cmdTemplate = commandShell->errOut; + errOff = FALSE; + } + } + + if (DEBUG(SHELL) && strcmp(shellName, "sh") == 0 && + (job->flags & JOB_TRACED) == 0) { + DBPRINTF("set -%s\n", "x"); + job->flags |= JOB_TRACED; + } + + DBPRINTF(cmdTemplate, cmd); + free(cmdStart); + if (escCmd) + free(escCmd); + if (errOff) { + /* + * If echoing is already off, there's no point in issuing the + * echoOff command. Otherwise we issue it and pretend it was on + * for the whole command... + */ + if (!shutUp && !(job->flags & JOB_SILENT) && commandShell->hasEchoCtl){ + DBPRINTF("%s\n", commandShell->echoOff); + shutUp = TRUE; + } + DBPRINTF("%s\n", commandShell->errCheck); + } + if (shutUp && commandShell->hasEchoCtl) { + DBPRINTF("%s\n", commandShell->echoOn); + } + return 0; +} + +/*- + *----------------------------------------------------------------------- + * JobSaveCommand -- + * Save a command to be executed when everything else is done. + * Callback function for JobFinish... + * + * Results: + * Always returns 0 + * + * Side Effects: + * The command is tacked onto the end of postCommands's commands list. + * + *----------------------------------------------------------------------- + */ +static int +JobSaveCommand(void *cmd, void *gn) +{ + cmd = Var_Subst(NULL, (char *)cmd, (GNode *)gn, FALSE); + (void)Lst_AtEnd(postCommands->commands, cmd); + return(0); +} + + +/*- + *----------------------------------------------------------------------- + * JobClose -- + * Called to close both input and output pipes when a job is finished. + * + * Results: + * Nada + * + * Side Effects: + * The file descriptors associated with the job are closed. + * + *----------------------------------------------------------------------- + */ +static void +JobClose(Job *job) +{ + clearfd(job); + (void)close(job->outPipe); + job->outPipe = -1; + + JobDoOutput(job, TRUE); + (void)close(job->inPipe); + job->inPipe = -1; +} + +/*- + *----------------------------------------------------------------------- + * JobFinish -- + * Do final processing for the given job including updating + * parents and starting new jobs as available/necessary. Note + * that we pay no attention to the JOB_IGNERR flag here. + * This is because when we're called because of a noexecute flag + * or something, jstat.w_status is 0 and when called from + * Job_CatchChildren, the status is zeroed if it s/b ignored. + * + * Input: + * job job to finish + * status sub-why job went away + * + * Results: + * None + * + * Side Effects: + * Final commands for the job are placed on postCommands. + * + * If we got an error and are aborting (aborting == ABORT_ERROR) and + * the job list is now empty, we are done for the day. + * If we recognized an error (errors !=0), we set the aborting flag + * to ABORT_ERROR so no more jobs will be started. + *----------------------------------------------------------------------- + */ +/*ARGSUSED*/ +static void +JobFinish(Job *job, int status) +{ + Boolean done, return_job_token; + + if (DEBUG(JOB)) { + fprintf(debug_file, "Jobfinish: %d [%s], status %d\n", + job->pid, job->node->name, status); + } + + if ((WIFEXITED(status) && + (((WEXITSTATUS(status) != 0) && !(job->flags & JOB_IGNERR)))) || + WIFSIGNALED(status)) + { + /* + * If it exited non-zero and either we're doing things our + * way or we're not ignoring errors, the job is finished. + * Similarly, if the shell died because of a signal + * the job is also finished. In these + * cases, finish out the job's output before printing the exit + * status... + */ + JobClose(job); + if (job->cmdFILE != NULL && job->cmdFILE != stdout) { + (void)fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + done = TRUE; + } else if (WIFEXITED(status)) { + /* + * Deal with ignored errors in -B mode. We need to print a message + * telling of the ignored error as well as setting status.w_status + * to 0 so the next command gets run. To do this, we set done to be + * TRUE if in -B mode and the job exited non-zero. + */ + done = WEXITSTATUS(status) != 0; + /* + * Old comment said: "Note we don't + * want to close down any of the streams until we know we're at the + * end." + * But we do. Otherwise when are we going to print the rest of the + * stuff? + */ + JobClose(job); + } else { + /* + * No need to close things down or anything. + */ + done = FALSE; + } + + if (done) { + if (WIFEXITED(status)) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Process %d [%s] exited.\n", + job->pid, job->node->name); + } + if (WEXITSTATUS(status) != 0) { + if (job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } +#ifdef USE_META + if (useMeta) { + meta_job_error(job, job->node, job->flags, WEXITSTATUS(status)); + } +#endif + (void)printf("*** [%s] Error code %d%s\n", + job->node->name, + WEXITSTATUS(status), + (job->flags & JOB_IGNERR) ? " (ignored)" : ""); + if (job->flags & JOB_IGNERR) { + status = 0; + } else { + PrintOnError(job->node, NULL); + } + } else if (DEBUG(JOB)) { + if (job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + (void)printf("*** [%s] Completed successfully\n", + job->node->name); + } + } else { + if (job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + (void)printf("*** [%s] Signal %d\n", + job->node->name, WTERMSIG(status)); + } + (void)fflush(stdout); + } + +#ifdef USE_META + if (useMeta) { + meta_job_finish(job); + } +#endif + + return_job_token = FALSE; + + Trace_Log(JOBEND, job); + if (!(job->flags & JOB_SPECIAL)) { + if ((status != 0) || + (aborting == ABORT_ERROR) || + (aborting == ABORT_INTERRUPT)) + return_job_token = TRUE; + } + + if ((aborting != ABORT_ERROR) && (aborting != ABORT_INTERRUPT) && (status == 0)) { + /* + * As long as we aren't aborting and the job didn't return a non-zero + * status that we shouldn't ignore, we call Make_Update to update + * the parents. In addition, any saved commands for the node are placed + * on the .END target. + */ + if (job->tailCmds != NULL) { + Lst_ForEachFrom(job->node->commands, job->tailCmds, + JobSaveCommand, + job->node); + } + job->node->made = MADE; + if (!(job->flags & JOB_SPECIAL)) + return_job_token = TRUE; + Make_Update(job->node); + job->job_state = JOB_ST_FREE; + } else if (status != 0) { + errors += 1; + job->job_state = JOB_ST_FREE; + } + + /* + * Set aborting if any error. + */ + if (errors && !keepgoing && (aborting != ABORT_INTERRUPT)) { + /* + * If we found any errors in this batch of children and the -k flag + * wasn't given, we set the aborting flag so no more jobs get + * started. + */ + aborting = ABORT_ERROR; + } + + if (return_job_token) + Job_TokenReturn(); + + if (aborting == ABORT_ERROR && jobTokensRunning == 0) { + /* + * If we are aborting and the job table is now empty, we finish. + */ + Finish(errors); + } +} + +/*- + *----------------------------------------------------------------------- + * Job_Touch -- + * Touch the given target. Called by JobStart when the -t flag was + * given + * + * Input: + * gn the node of the file to touch + * silent TRUE if should not print message + * + * Results: + * None + * + * Side Effects: + * The data modification of the file is changed. In addition, if the + * file did not exist, it is created. + *----------------------------------------------------------------------- + */ +void +Job_Touch(GNode *gn, Boolean silent) +{ + int streamID; /* ID of stream opened to do the touch */ + struct utimbuf times; /* Times for utime() call */ + + if (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC|OP_OPTIONAL| + OP_SPECIAL|OP_PHONY)) { + /* + * .JOIN, .USE, .ZEROTIME and .OPTIONAL targets are "virtual" targets + * and, as such, shouldn't really be created. + */ + return; + } + + if (!silent || NoExecute(gn)) { + (void)fprintf(stdout, "touch %s\n", gn->name); + (void)fflush(stdout); + } + + if (NoExecute(gn)) { + return; + } + + if (gn->type & OP_ARCHV) { + Arch_Touch(gn); + } else if (gn->type & OP_LIB) { + Arch_TouchLib(gn); + } else { + char *file = gn->path ? gn->path : gn->name; + + times.actime = times.modtime = now; + if (utime(file, ×) < 0){ + streamID = open(file, O_RDWR | O_CREAT, 0666); + + if (streamID >= 0) { + char c; + + /* + * Read and write a byte to the file to change the + * modification time, then close the file. + */ + if (read(streamID, &c, 1) == 1) { + (void)lseek(streamID, (off_t)0, SEEK_SET); + while (write(streamID, &c, 1) == -1 && errno == EAGAIN) + continue; + } + + (void)close(streamID); + } else { + (void)fprintf(stdout, "*** couldn't touch %s: %s", + file, strerror(errno)); + (void)fflush(stdout); + } + } + } +} + +/*- + *----------------------------------------------------------------------- + * Job_CheckCommands -- + * Make sure the given node has all the commands it needs. + * + * Input: + * gn The target whose commands need verifying + * abortProc Function to abort with message + * + * Results: + * TRUE if the commands list is/was ok. + * + * Side Effects: + * The node will have commands from the .DEFAULT rule added to it + * if it needs them. + *----------------------------------------------------------------------- + */ +Boolean +Job_CheckCommands(GNode *gn, void (*abortProc)(const char *, ...)) +{ + if (OP_NOP(gn->type) && Lst_IsEmpty(gn->commands) && + ((gn->type & OP_LIB) == 0 || Lst_IsEmpty(gn->children))) { + /* + * No commands. Look for .DEFAULT rule from which we might infer + * commands + */ + if ((DEFAULT != NULL) && !Lst_IsEmpty(DEFAULT->commands) && + (gn->type & OP_SPECIAL) == 0) { + char *p1; + /* + * Make only looks for a .DEFAULT if the node was never the + * target of an operator, so that's what we do too. If + * a .DEFAULT was given, we substitute its commands for gn's + * commands and set the IMPSRC variable to be the target's name + * The DEFAULT node acts like a transformation rule, in that + * gn also inherits any attributes or sources attached to + * .DEFAULT itself. + */ + Make_HandleUse(DEFAULT, gn); + Var_Set(IMPSRC, Var_Value(TARGET, gn, &p1), gn, 0); + if (p1) + free(p1); + } else if (Dir_MTime(gn, 0) == 0 && (gn->type & OP_SPECIAL) == 0) { + /* + * The node wasn't the target of an operator we have no .DEFAULT + * rule to go on and the target doesn't already exist. There's + * nothing more we can do for this branch. If the -k flag wasn't + * given, we stop in our tracks, otherwise we just don't update + * this node's parents so they never get examined. + */ + static const char msg[] = ": don't know how to make"; + + if (gn->flags & FROM_DEPEND) { + if (!Job_RunTarget(".STALE", gn->fname)) + fprintf(stdout, "%s: %s, %d: ignoring stale %s for %s\n", + progname, gn->fname, gn->lineno, makeDependfile, + gn->name); + return TRUE; + } + + if (gn->type & OP_OPTIONAL) { + (void)fprintf(stdout, "%s%s %s (ignored)\n", progname, + msg, gn->name); + (void)fflush(stdout); + } else if (keepgoing) { + (void)fprintf(stdout, "%s%s %s (continuing)\n", progname, + msg, gn->name); + (void)fflush(stdout); + return FALSE; + } else { + (*abortProc)("%s%s %s. Stop", progname, msg, gn->name); + return FALSE; + } + } + } + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * JobExec -- + * Execute the shell for the given job. Called from JobStart + * + * Input: + * job Job to execute + * + * Results: + * None. + * + * Side Effects: + * A shell is executed, outputs is altered and the Job structure added + * to the job table. + * + *----------------------------------------------------------------------- + */ +static void +JobExec(Job *job, char **argv) +{ + int cpid; /* ID of new child */ + sigset_t mask; + + job->flags &= ~JOB_TRACED; + + if (DEBUG(JOB)) { + int i; + + (void)fprintf(debug_file, "Running %s %sly\n", job->node->name, "local"); + (void)fprintf(debug_file, "\tCommand: "); + for (i = 0; argv[i] != NULL; i++) { + (void)fprintf(debug_file, "%s ", argv[i]); + } + (void)fprintf(debug_file, "\n"); + } + + /* + * Some jobs produce no output and it's disconcerting to have + * no feedback of their running (since they produce no output, the + * banner with their name in it never appears). This is an attempt to + * provide that feedback, even if nothing follows it. + */ + if ((lastNode != job->node) && !(job->flags & JOB_SILENT)) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + + /* No interruptions until this job is on the `jobs' list */ + JobSigLock(&mask); + + /* Pre-emptively mark job running, pid still zero though */ + job->job_state = JOB_ST_RUNNING; + + cpid = vFork(); + if (cpid == -1) + Punt("Cannot vfork: %s", strerror(errno)); + + if (cpid == 0) { + /* Child */ + sigset_t tmask; + +#ifdef USE_META + if (useMeta) { + meta_job_child(job); + } +#endif + /* + * Reset all signal handlers; this is necessary because we also + * need to unblock signals before we exec(2). + */ + JobSigReset(); + + /* Now unblock signals */ + sigemptyset(&tmask); + JobSigUnlock(&tmask); + + /* + * Must duplicate the input stream down to the child's input and + * reset it to the beginning (again). Since the stream was marked + * close-on-exec, we must clear that bit in the new input. + */ + if (dup2(FILENO(job->cmdFILE), 0) == -1) { + execError("dup2", "job->cmdFILE"); + _exit(1); + } + (void)fcntl(0, F_SETFD, 0); + (void)lseek(0, (off_t)0, SEEK_SET); + + if (job->node->type & (OP_MAKE | OP_SUBMAKE)) { + /* + * Pass job token pipe to submakes. + */ + fcntl(tokenWaitJob.inPipe, F_SETFD, 0); + fcntl(tokenWaitJob.outPipe, F_SETFD, 0); + } + + /* + * Set up the child's output to be routed through the pipe + * we've created for it. + */ + if (dup2(job->outPipe, 1) == -1) { + execError("dup2", "job->outPipe"); + _exit(1); + } + /* + * The output channels are marked close on exec. This bit was + * duplicated by the dup2(on some systems), so we have to clear + * it before routing the shell's error output to the same place as + * its standard output. + */ + (void)fcntl(1, F_SETFD, 0); + if (dup2(1, 2) == -1) { + execError("dup2", "1, 2"); + _exit(1); + } + + /* + * We want to switch the child into a different process family so + * we can kill it and all its descendants in one fell swoop, + * by killing its process family, but not commit suicide. + */ +#if defined(MAKE_NATIVE) || defined(HAVE_SETPGID) +#if defined(SYSV) + /* XXX: dsl - I'm sure this should be setpgrp()... */ + (void)setsid(); +#else + (void)setpgid(0, getpid()); +#endif +#endif + + Var_ExportVars(); + + (void)execv(shellPath, argv); + execError("exec", shellPath); + _exit(1); + } + + /* Parent, continuing after the child exec */ + job->pid = cpid; + + Trace_Log(JOBSTART, job); + + /* + * Set the current position in the buffer to the beginning + * and mark another stream to watch in the outputs mask + */ + job->curPos = 0; + + watchfd(job); + + if (job->cmdFILE != NULL && job->cmdFILE != stdout) { + (void)fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + + /* + * Now the job is actually running, add it to the table. + */ + if (DEBUG(JOB)) { + fprintf(debug_file, "JobExec(%s): pid %d added to jobs table\n", + job->node->name, job->pid); + job_table_dump("job started"); + } + JobSigUnlock(&mask); +} + +/*- + *----------------------------------------------------------------------- + * JobMakeArgv -- + * Create the argv needed to execute the shell for a given job. + * + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +static void +JobMakeArgv(Job *job, char **argv) +{ + int argc; + static char args[10]; /* For merged arguments */ + + argv[0] = UNCONST(shellName); + argc = 1; + + if ((commandShell->exit && (*commandShell->exit != '-')) || + (commandShell->echo && (*commandShell->echo != '-'))) + { + /* + * At least one of the flags doesn't have a minus before it, so + * merge them together. Have to do this because the *(&(@*#*&#$# + * Bourne shell thinks its second argument is a file to source. + * Grrrr. Note the ten-character limitation on the combined arguments. + */ + (void)snprintf(args, sizeof(args), "-%s%s", + ((job->flags & JOB_IGNERR) ? "" : + (commandShell->exit ? commandShell->exit : "")), + ((job->flags & JOB_SILENT) ? "" : + (commandShell->echo ? commandShell->echo : ""))); + + if (args[1]) { + argv[argc] = args; + argc++; + } + } else { + if (!(job->flags & JOB_IGNERR) && commandShell->exit) { + argv[argc] = UNCONST(commandShell->exit); + argc++; + } + if (!(job->flags & JOB_SILENT) && commandShell->echo) { + argv[argc] = UNCONST(commandShell->echo); + argc++; + } + } + argv[argc] = NULL; +} + +/*- + *----------------------------------------------------------------------- + * JobStart -- + * Start a target-creation process going for the target described + * by the graph node gn. + * + * Input: + * gn target to create + * flags flags for the job to override normal ones. + * e.g. JOB_SPECIAL or JOB_IGNDOTS + * previous The previous Job structure for this node, if any. + * + * Results: + * JOB_ERROR if there was an error in the commands, JOB_FINISHED + * if there isn't actually anything left to do for the job and + * JOB_RUNNING if the job has been started. + * + * Side Effects: + * A new Job node is created and added to the list of running + * jobs. PMake is forked and a child shell created. + * + * NB: I'm fairly sure that this code is never called with JOB_SPECIAL set + * JOB_IGNDOTS is never set (dsl) + * Also the return value is ignored by everyone. + *----------------------------------------------------------------------- + */ +static int +JobStart(GNode *gn, int flags) +{ + Job *job; /* new job descriptor */ + char *argv[10]; /* Argument vector to shell */ + Boolean cmdsOK; /* true if the nodes commands were all right */ + Boolean noExec; /* Set true if we decide not to run the job */ + int tfd; /* File descriptor to the temp file */ + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state == JOB_ST_FREE) + break; + } + if (job >= job_table_end) + Punt("JobStart no job slots vacant"); + + memset(job, 0, sizeof *job); + job->job_state = JOB_ST_SETUP; + if (gn->type & OP_SPECIAL) + flags |= JOB_SPECIAL; + + job->node = gn; + job->tailCmds = NULL; + + /* + * Set the initial value of the flags for this job based on the global + * ones and the node's attributes... Any flags supplied by the caller + * are also added to the field. + */ + job->flags = 0; + if (Targ_Ignore(gn)) { + job->flags |= JOB_IGNERR; + } + if (Targ_Silent(gn)) { + job->flags |= JOB_SILENT; + } + job->flags |= flags; + + /* + * Check the commands now so any attributes from .DEFAULT have a chance + * to migrate to the node + */ + cmdsOK = Job_CheckCommands(gn, Error); + + job->inPollfd = NULL; + /* + * If the -n flag wasn't given, we open up OUR (not the child's) + * temporary file to stuff commands in it. The thing is rd/wr so we don't + * need to reopen it to feed it to the shell. If the -n flag *was* given, + * we just set the file to be stdout. Cute, huh? + */ + if (((gn->type & OP_MAKE) && !(noRecursiveExecute)) || + (!noExecute && !touchFlag)) { + /* + * tfile is the name of a file into which all shell commands are + * put. It is removed before the child shell is executed, unless + * DEBUG(SCRIPT) is set. + */ + char *tfile; + sigset_t mask; + /* + * We're serious here, but if the commands were bogus, we're + * also dead... + */ + if (!cmdsOK) { + PrintOnError(gn, NULL); /* provide some clue */ + DieHorribly(); + } + + JobSigLock(&mask); + tfd = mkTempFile(TMPPAT, &tfile); + if (!DEBUG(SCRIPT)) + (void)eunlink(tfile); + JobSigUnlock(&mask); + + job->cmdFILE = fdopen(tfd, "w+"); + if (job->cmdFILE == NULL) { + Punt("Could not fdopen %s", tfile); + } + (void)fcntl(FILENO(job->cmdFILE), F_SETFD, 1); + /* + * Send the commands to the command file, flush all its buffers then + * rewind and remove the thing. + */ + noExec = FALSE; + +#ifdef USE_META + if (useMeta) { + meta_job_start(job, gn); + if (Targ_Silent(gn)) { /* might have changed */ + job->flags |= JOB_SILENT; + } + } +#endif + /* + * We can do all the commands at once. hooray for sanity + */ + numCommands = 0; + Lst_ForEach(gn->commands, JobPrintCommand, job); + + /* + * If we didn't print out any commands to the shell script, + * there's not much point in executing the shell, is there? + */ + if (numCommands == 0) { + noExec = TRUE; + } + + free(tfile); + } else if (NoExecute(gn)) { + /* + * Not executing anything -- just print all the commands to stdout + * in one fell swoop. This will still set up job->tailCmds correctly. + */ + if (lastNode != gn) { + MESSAGE(stdout, gn); + lastNode = gn; + } + job->cmdFILE = stdout; + /* + * Only print the commands if they're ok, but don't die if they're + * not -- just let the user know they're bad and keep going. It + * doesn't do any harm in this case and may do some good. + */ + if (cmdsOK) { + Lst_ForEach(gn->commands, JobPrintCommand, job); + } + /* + * Don't execute the shell, thank you. + */ + noExec = TRUE; + } else { + /* + * Just touch the target and note that no shell should be executed. + * Set cmdFILE to stdout to make life easier. Check the commands, too, + * but don't die if they're no good -- it does no harm to keep working + * up the graph. + */ + job->cmdFILE = stdout; + Job_Touch(gn, job->flags&JOB_SILENT); + noExec = TRUE; + } + /* Just in case it isn't already... */ + (void)fflush(job->cmdFILE); + + /* + * If we're not supposed to execute a shell, don't. + */ + if (noExec) { + if (!(job->flags & JOB_SPECIAL)) + Job_TokenReturn(); + /* + * Unlink and close the command file if we opened one + */ + if (job->cmdFILE != stdout) { + if (job->cmdFILE != NULL) { + (void)fclose(job->cmdFILE); + job->cmdFILE = NULL; + } + } + + /* + * We only want to work our way up the graph if we aren't here because + * the commands for the job were no good. + */ + if (cmdsOK && aborting == 0) { + if (job->tailCmds != NULL) { + Lst_ForEachFrom(job->node->commands, job->tailCmds, + JobSaveCommand, + job->node); + } + job->node->made = MADE; + Make_Update(job->node); + } + job->job_state = JOB_ST_FREE; + return cmdsOK ? JOB_FINISHED : JOB_ERROR; + } + + /* + * Set up the control arguments to the shell. This is based on the flags + * set earlier for this job. + */ + JobMakeArgv(job, argv); + + /* Create the pipe by which we'll get the shell's output. */ + JobCreatePipe(job, 3); + + JobExec(job, argv); + return(JOB_RUNNING); +} + +static char * +JobOutput(Job *job, char *cp, char *endp, int msg) +{ + char *ecp; + + if (commandShell->noPrint) { + ecp = Str_FindSubstring(cp, commandShell->noPrint); + while (ecp != NULL) { + if (cp != ecp) { + *ecp = '\0'; + if (!beSilent && msg && job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } + /* + * The only way there wouldn't be a newline after + * this line is if it were the last in the buffer. + * however, since the non-printable comes after it, + * there must be a newline, so we don't print one. + */ + (void)fprintf(stdout, "%s", cp); + (void)fflush(stdout); + } + cp = ecp + commandShell->noPLen; + if (cp != endp) { + /* + * Still more to print, look again after skipping + * the whitespace following the non-printable + * command.... + */ + cp++; + while (*cp == ' ' || *cp == '\t' || *cp == '\n') { + cp++; + } + ecp = Str_FindSubstring(cp, commandShell->noPrint); + } else { + return cp; + } + } + } + return cp; +} + +/*- + *----------------------------------------------------------------------- + * JobDoOutput -- + * This function is called at different times depending on + * whether the user has specified that output is to be collected + * via pipes or temporary files. In the former case, we are called + * whenever there is something to read on the pipe. We collect more + * output from the given job and store it in the job's outBuf. If + * this makes up a line, we print it tagged by the job's identifier, + * as necessary. + * If output has been collected in a temporary file, we open the + * file and read it line by line, transfering it to our own + * output channel until the file is empty. At which point we + * remove the temporary file. + * In both cases, however, we keep our figurative eye out for the + * 'noPrint' line for the shell from which the output came. If + * we recognize a line, we don't print it. If the command is not + * alone on the line (the character after it is not \0 or \n), we + * do print whatever follows it. + * + * Input: + * job the job whose output needs printing + * finish TRUE if this is the last time we'll be called + * for this job + * + * Results: + * None + * + * Side Effects: + * curPos may be shifted as may the contents of outBuf. + *----------------------------------------------------------------------- + */ +STATIC void +JobDoOutput(Job *job, Boolean finish) +{ + Boolean gotNL = FALSE; /* true if got a newline */ + Boolean fbuf; /* true if our buffer filled up */ + int nr; /* number of bytes read */ + int i; /* auxiliary index into outBuf */ + int max; /* limit for i (end of current data) */ + int nRead; /* (Temporary) number of bytes read */ + + /* + * Read as many bytes as will fit in the buffer. + */ +end_loop: + gotNL = FALSE; + fbuf = FALSE; + + nRead = read(job->inPipe, &job->outBuf[job->curPos], + JOB_BUFSIZE - job->curPos); + if (nRead < 0) { + if (errno == EAGAIN) + return; + if (DEBUG(JOB)) { + perror("JobDoOutput(piperead)"); + } + nr = 0; + } else { + nr = nRead; + } + + /* + * If we hit the end-of-file (the job is dead), we must flush its + * remaining output, so pretend we read a newline if there's any + * output remaining in the buffer. + * Also clear the 'finish' flag so we stop looping. + */ + if ((nr == 0) && (job->curPos != 0)) { + job->outBuf[job->curPos] = '\n'; + nr = 1; + finish = FALSE; + } else if (nr == 0) { + finish = FALSE; + } + + /* + * Look for the last newline in the bytes we just got. If there is + * one, break out of the loop with 'i' as its index and gotNL set + * TRUE. + */ + max = job->curPos + nr; + for (i = job->curPos + nr - 1; i >= job->curPos; i--) { + if (job->outBuf[i] == '\n') { + gotNL = TRUE; + break; + } else if (job->outBuf[i] == '\0') { + /* + * Why? + */ + job->outBuf[i] = ' '; + } + } + + if (!gotNL) { + job->curPos += nr; + if (job->curPos == JOB_BUFSIZE) { + /* + * If we've run out of buffer space, we have no choice + * but to print the stuff. sigh. + */ + fbuf = TRUE; + i = job->curPos; + } + } + if (gotNL || fbuf) { + /* + * Need to send the output to the screen. Null terminate it + * first, overwriting the newline character if there was one. + * So long as the line isn't one we should filter (according + * to the shell description), we print the line, preceded + * by a target banner if this target isn't the same as the + * one for which we last printed something. + * The rest of the data in the buffer are then shifted down + * to the start of the buffer and curPos is set accordingly. + */ + job->outBuf[i] = '\0'; + if (i >= job->curPos) { + char *cp; + + cp = JobOutput(job, job->outBuf, &job->outBuf[i], FALSE); + + /* + * There's still more in that thar buffer. This time, though, + * we know there's no newline at the end, so we add one of + * our own free will. + */ + if (*cp != '\0') { + if (!beSilent && job->node != lastNode) { + MESSAGE(stdout, job->node); + lastNode = job->node; + } +#ifdef USE_META + if (useMeta) { + meta_job_output(job, cp, gotNL ? "\n" : ""); + } +#endif + (void)fprintf(stdout, "%s%s", cp, gotNL ? "\n" : ""); + (void)fflush(stdout); + } + } + /* + * max is the last offset still in the buffer. Move any remaining + * characters to the start of the buffer and update the end marker + * curPos. + */ + if (i < max) { + (void)memmove(job->outBuf, &job->outBuf[i + 1], max - (i + 1)); + job->curPos = max - (i + 1); + } else { + assert(i == max); + job->curPos = 0; + } + } + if (finish) { + /* + * If the finish flag is true, we must loop until we hit + * end-of-file on the pipe. This is guaranteed to happen + * eventually since the other end of the pipe is now closed + * (we closed it explicitly and the child has exited). When + * we do get an EOF, finish will be set FALSE and we'll fall + * through and out. + */ + goto end_loop; + } +} + +static void +JobRun(GNode *targ) +{ +#ifdef notyet + /* + * Unfortunately it is too complicated to run .BEGIN, .END, + * and .INTERRUPT job in the parallel job module. This has + * the nice side effect that it avoids a lot of other problems. + */ + Lst lst = Lst_Init(FALSE); + Lst_AtEnd(lst, targ); + (void)Make_Run(lst); + Lst_Destroy(lst, NULL); + JobStart(targ, JOB_SPECIAL); + while (jobTokensRunning) { + Job_CatchOutput(); + } +#else + Compat_Make(targ, targ); + if (targ->made == ERROR) { + PrintOnError(targ, "\n\nStop."); + exit(1); + } +#endif +} + +/*- + *----------------------------------------------------------------------- + * Job_CatchChildren -- + * Handle the exit of a child. Called from Make_Make. + * + * Input: + * block TRUE if should block on the wait + * + * Results: + * none. + * + * Side Effects: + * The job descriptor is removed from the list of children. + * + * Notes: + * We do waits, blocking or not, according to the wisdom of our + * caller, until there are no more children to report. For each + * job, call JobFinish to finish things off. + * + *----------------------------------------------------------------------- + */ + +void +Job_CatchChildren(void) +{ + int pid; /* pid of dead child */ + int status; /* Exit/termination status */ + + /* + * Don't even bother if we know there's no one around. + */ + if (jobTokensRunning == 0) + return; + + while ((pid = waitpid((pid_t) -1, &status, WNOHANG | WUNTRACED)) > 0) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Process %d exited/stopped status %x.\n", pid, + status); + } + JobReapChild(pid, status, TRUE); + } +} + +/* + * It is possible that wait[pid]() was called from elsewhere, + * this lets us reap jobs regardless. + */ +void +JobReapChild(pid_t pid, int status, Boolean isJobs) +{ + Job *job; /* job descriptor for dead child */ + + /* + * Don't even bother if we know there's no one around. + */ + if (jobTokensRunning == 0) + return; + + job = JobFindPid(pid, JOB_ST_RUNNING, isJobs); + if (job == NULL) { + if (isJobs) { + if (!lurking_children) + Error("Child (%d) status %x not in table?", pid, status); + } + return; /* not ours */ + } + if (WIFSTOPPED(status)) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Process %d (%s) stopped.\n", + job->pid, job->node->name); + } + if (!make_suspended) { + switch (WSTOPSIG(status)) { + case SIGTSTP: + (void)printf("*** [%s] Suspended\n", job->node->name); + break; + case SIGSTOP: + (void)printf("*** [%s] Stopped\n", job->node->name); + break; + default: + (void)printf("*** [%s] Stopped -- signal %d\n", + job->node->name, WSTOPSIG(status)); + } + job->job_suspended = 1; + } + (void)fflush(stdout); + return; + } + + job->job_state = JOB_ST_FINISHED; + job->exit_status = status; + + JobFinish(job, status); +} + +/*- + *----------------------------------------------------------------------- + * Job_CatchOutput -- + * Catch the output from our children, if we're using + * pipes do so. Otherwise just block time until we get a + * signal(most likely a SIGCHLD) since there's no point in + * just spinning when there's nothing to do and the reaping + * of a child can wait for a while. + * + * Results: + * None + * + * Side Effects: + * Output is read from pipes if we're piping. + * ----------------------------------------------------------------------- + */ +void +Job_CatchOutput(void) +{ + int nready; + Job *job; + int i; + + (void)fflush(stdout); + + /* The first fd in the list is the job token pipe */ + do { + nready = poll(fds + 1 - wantToken, nfds - 1 + wantToken, POLL_MSEC); + } while (nready < 0 && errno == EINTR); + + if (nready < 0) + Punt("poll: %s", strerror(errno)); + + if (nready > 0 && readyfd(&childExitJob)) { + char token = 0; + ssize_t count; + count = read(childExitJob.inPipe, &token, 1); + switch (count) { + case 0: + Punt("unexpected eof on token pipe"); + case -1: + Punt("token pipe read: %s", strerror(errno)); + case 1: + if (token == DO_JOB_RESUME[0]) + /* Complete relay requested from our SIGCONT handler */ + JobRestartJobs(); + break; + default: + abort(); + } + --nready; + } + + Job_CatchChildren(); + if (nready == 0) + return; + + for (i = 2; i < nfds; i++) { + if (!fds[i].revents) + continue; + job = jobfds[i]; + if (job->job_state == JOB_ST_RUNNING) + JobDoOutput(job, FALSE); + if (--nready == 0) + return; + } +} + +/*- + *----------------------------------------------------------------------- + * Job_Make -- + * Start the creation of a target. Basically a front-end for + * JobStart used by the Make module. + * + * Results: + * None. + * + * Side Effects: + * Another job is started. + * + *----------------------------------------------------------------------- + */ +void +Job_Make(GNode *gn) +{ + (void)JobStart(gn, 0); +} + +void +Shell_Init(void) +{ + if (shellPath == NULL) { + /* + * We are using the default shell, which may be an absolute + * path if DEFSHELL_CUSTOM is defined. + */ + shellName = commandShell->name; +#ifdef DEFSHELL_CUSTOM + if (*shellName == '/') { + shellPath = shellName; + shellName = strrchr(shellPath, '/'); + shellName++; + } else +#endif + shellPath = str_concat(_PATH_DEFSHELLDIR, shellName, STR_ADDSLASH); + } + if (commandShell->exit == NULL) { + commandShell->exit = ""; + } + if (commandShell->echo == NULL) { + commandShell->echo = ""; + } + if (commandShell->hasErrCtl && *commandShell->exit) { + if (shellErrFlag && + strcmp(commandShell->exit, &shellErrFlag[1]) != 0) { + free(shellErrFlag); + shellErrFlag = NULL; + } + if (!shellErrFlag) { + int n = strlen(commandShell->exit) + 2; + + shellErrFlag = bmake_malloc(n); + if (shellErrFlag) { + snprintf(shellErrFlag, n, "-%s", commandShell->exit); + } + } + } else if (shellErrFlag) { + free(shellErrFlag); + shellErrFlag = NULL; + } +} + +/*- + * Returns the string literal that is used in the current command shell + * to produce a newline character. + */ +const char * +Shell_GetNewline(void) +{ + + return commandShell->newline; +} + +void +Job_SetPrefix(void) +{ + + if (targPrefix) { + free(targPrefix); + } else if (!Var_Exists(MAKE_JOB_PREFIX, VAR_GLOBAL)) { + Var_Set(MAKE_JOB_PREFIX, "---", VAR_GLOBAL, 0); + } + + targPrefix = Var_Subst(NULL, "${" MAKE_JOB_PREFIX "}", VAR_GLOBAL, 0); +} + +/*- + *----------------------------------------------------------------------- + * Job_Init -- + * Initialize the process module + * + * Input: + * + * Results: + * none + * + * Side Effects: + * lists and counters are initialized + *----------------------------------------------------------------------- + */ +void +Job_Init(void) +{ + Job_SetPrefix(); + /* Allocate space for all the job info */ + job_table = bmake_malloc(maxJobs * sizeof *job_table); + memset(job_table, 0, maxJobs * sizeof *job_table); + job_table_end = job_table + maxJobs; + wantToken = 0; + + aborting = 0; + errors = 0; + + lastNode = NULL; + + /* + * There is a non-zero chance that we already have children. + * eg after 'make -f- <<EOF' + * Since their termination causes a 'Child (pid) not in table' message, + * Collect the status of any that are already dead, and suppress the + * error message if there are any undead ones. + */ + for (;;) { + int rval, status; + rval = waitpid((pid_t) -1, &status, WNOHANG); + if (rval > 0) + continue; + if (rval == 0) + lurking_children = 1; + break; + } + + Shell_Init(); + + JobCreatePipe(&childExitJob, 3); + + /* We can only need to wait for tokens, children and output from each job */ + fds = bmake_malloc(sizeof (*fds) * (2 + maxJobs)); + jobfds = bmake_malloc(sizeof (*jobfds) * (2 + maxJobs)); + + /* These are permanent entries and take slots 0 and 1 */ + watchfd(&tokenWaitJob); + watchfd(&childExitJob); + + sigemptyset(&caught_signals); + /* + * Install a SIGCHLD handler. + */ + (void)bmake_signal(SIGCHLD, JobChildSig); + sigaddset(&caught_signals, SIGCHLD); + +#define ADDSIG(s,h) \ + if (bmake_signal(s, SIG_IGN) != SIG_IGN) { \ + sigaddset(&caught_signals, s); \ + (void)bmake_signal(s, h); \ + } + + /* + * Catch the four signals that POSIX specifies if they aren't ignored. + * JobPassSig will take care of calling JobInterrupt if appropriate. + */ + ADDSIG(SIGINT, JobPassSig_int) + ADDSIG(SIGHUP, JobPassSig_term) + ADDSIG(SIGTERM, JobPassSig_term) + ADDSIG(SIGQUIT, JobPassSig_term) + + /* + * There are additional signals that need to be caught and passed if + * either the export system wants to be told directly of signals or if + * we're giving each job its own process group (since then it won't get + * signals from the terminal driver as we own the terminal) + */ + ADDSIG(SIGTSTP, JobPassSig_suspend) + ADDSIG(SIGTTOU, JobPassSig_suspend) + ADDSIG(SIGTTIN, JobPassSig_suspend) + ADDSIG(SIGWINCH, JobCondPassSig) + ADDSIG(SIGCONT, JobContinueSig) +#undef ADDSIG + + (void)Job_RunTarget(".BEGIN", NULL); + postCommands = Targ_FindNode(".END", TARG_CREATE); +} + +static void JobSigReset(void) +{ +#define DELSIG(s) \ + if (sigismember(&caught_signals, s)) { \ + (void)bmake_signal(s, SIG_DFL); \ + } + + DELSIG(SIGINT) + DELSIG(SIGHUP) + DELSIG(SIGQUIT) + DELSIG(SIGTERM) + DELSIG(SIGTSTP) + DELSIG(SIGTTOU) + DELSIG(SIGTTIN) + DELSIG(SIGWINCH) + DELSIG(SIGCONT) +#undef DELSIG + (void)bmake_signal(SIGCHLD, SIG_DFL); +} + +/*- + *----------------------------------------------------------------------- + * JobMatchShell -- + * Find a shell in 'shells' given its name. + * + * Results: + * A pointer to the Shell structure. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Shell * +JobMatchShell(const char *name) +{ + Shell *sh; + + for (sh = shells; sh->name != NULL; sh++) { + if (strcmp(name, sh->name) == 0) + return (sh); + } + return NULL; +} + +/*- + *----------------------------------------------------------------------- + * Job_ParseShell -- + * Parse a shell specification and set up commandShell, shellPath + * and shellName appropriately. + * + * Input: + * line The shell spec + * + * Results: + * FAILURE if the specification was incorrect. + * + * Side Effects: + * commandShell points to a Shell structure (either predefined or + * created from the shell spec), shellPath is the full path of the + * shell described by commandShell, while shellName is just the + * final component of shellPath. + * + * Notes: + * A shell specification consists of a .SHELL target, with dependency + * operator, followed by a series of blank-separated words. Double + * quotes can be used to use blanks in words. A backslash escapes + * anything (most notably a double-quote and a space) and + * provides the functionality it does in C. Each word consists of + * keyword and value separated by an equal sign. There should be no + * unnecessary spaces in the word. The keywords are as follows: + * name Name of shell. + * path Location of shell. + * quiet Command to turn off echoing. + * echo Command to turn echoing on + * filter Result of turning off echoing that shouldn't be + * printed. + * echoFlag Flag to turn echoing on at the start + * errFlag Flag to turn error checking on at the start + * hasErrCtl True if shell has error checking control + * newline String literal to represent a newline char + * check Command to turn on error checking if hasErrCtl + * is TRUE or template of command to echo a command + * for which error checking is off if hasErrCtl is + * FALSE. + * ignore Command to turn off error checking if hasErrCtl + * is TRUE or template of command to execute a + * command so as to ignore any errors it returns if + * hasErrCtl is FALSE. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Job_ParseShell(char *line) +{ + char **words; + char **argv; + int argc; + char *path; + Shell newShell; + Boolean fullSpec = FALSE; + Shell *sh; + + while (isspace((unsigned char)*line)) { + line++; + } + + if (shellArgv) + free(UNCONST(shellArgv)); + + memset(&newShell, 0, sizeof(newShell)); + + /* + * Parse the specification by keyword + */ + words = brk_string(line, &argc, TRUE, &path); + if (words == NULL) { + Error("Unterminated quoted string [%s]", line); + return FAILURE; + } + shellArgv = path; + + for (path = NULL, argv = words; argc != 0; argc--, argv++) { + if (strncmp(*argv, "path=", 5) == 0) { + path = &argv[0][5]; + } else if (strncmp(*argv, "name=", 5) == 0) { + newShell.name = &argv[0][5]; + } else { + if (strncmp(*argv, "quiet=", 6) == 0) { + newShell.echoOff = &argv[0][6]; + } else if (strncmp(*argv, "echo=", 5) == 0) { + newShell.echoOn = &argv[0][5]; + } else if (strncmp(*argv, "filter=", 7) == 0) { + newShell.noPrint = &argv[0][7]; + newShell.noPLen = strlen(newShell.noPrint); + } else if (strncmp(*argv, "echoFlag=", 9) == 0) { + newShell.echo = &argv[0][9]; + } else if (strncmp(*argv, "errFlag=", 8) == 0) { + newShell.exit = &argv[0][8]; + } else if (strncmp(*argv, "hasErrCtl=", 10) == 0) { + char c = argv[0][10]; + newShell.hasErrCtl = !((c != 'Y') && (c != 'y') && + (c != 'T') && (c != 't')); + } else if (strncmp(*argv, "newline=", 8) == 0) { + newShell.newline = &argv[0][8]; + } else if (strncmp(*argv, "check=", 6) == 0) { + newShell.errCheck = &argv[0][6]; + } else if (strncmp(*argv, "ignore=", 7) == 0) { + newShell.ignErr = &argv[0][7]; + } else if (strncmp(*argv, "errout=", 7) == 0) { + newShell.errOut = &argv[0][7]; + } else if (strncmp(*argv, "comment=", 8) == 0) { + newShell.commentChar = argv[0][8]; + } else { + Parse_Error(PARSE_FATAL, "Unknown keyword \"%s\"", + *argv); + free(words); + return(FAILURE); + } + fullSpec = TRUE; + } + } + + if (path == NULL) { + /* + * If no path was given, the user wants one of the pre-defined shells, + * yes? So we find the one s/he wants with the help of JobMatchShell + * and set things up the right way. shellPath will be set up by + * Shell_Init. + */ + if (newShell.name == NULL) { + Parse_Error(PARSE_FATAL, "Neither path nor name specified"); + free(words); + return(FAILURE); + } else { + if ((sh = JobMatchShell(newShell.name)) == NULL) { + Parse_Error(PARSE_WARNING, "%s: No matching shell", + newShell.name); + free(words); + return(FAILURE); + } + commandShell = sh; + shellName = newShell.name; + if (shellPath) { + /* Shell_Init has already been called! Do it again. */ + free(UNCONST(shellPath)); + shellPath = NULL; + Shell_Init(); + } + } + } else { + /* + * The user provided a path. If s/he gave nothing else (fullSpec is + * FALSE), try and find a matching shell in the ones we know of. + * Else we just take the specification at its word and copy it + * to a new location. In either case, we need to record the + * path the user gave for the shell. + */ + shellPath = path; + path = strrchr(path, '/'); + if (path == NULL) { + path = UNCONST(shellPath); + } else { + path += 1; + } + if (newShell.name != NULL) { + shellName = newShell.name; + } else { + shellName = path; + } + if (!fullSpec) { + if ((sh = JobMatchShell(shellName)) == NULL) { + Parse_Error(PARSE_WARNING, "%s: No matching shell", + shellName); + free(words); + return(FAILURE); + } + commandShell = sh; + } else { + commandShell = bmake_malloc(sizeof(Shell)); + *commandShell = newShell; + } + /* this will take care of shellErrFlag */ + Shell_Init(); + } + + if (commandShell->echoOn && commandShell->echoOff) { + commandShell->hasEchoCtl = TRUE; + } + + if (!commandShell->hasErrCtl) { + if (commandShell->errCheck == NULL) { + commandShell->errCheck = ""; + } + if (commandShell->ignErr == NULL) { + commandShell->ignErr = "%s\n"; + } + } + + /* + * Do not free up the words themselves, since they might be in use by the + * shell specification. + */ + free(words); + return SUCCESS; +} + +/*- + *----------------------------------------------------------------------- + * JobInterrupt -- + * Handle the receipt of an interrupt. + * + * Input: + * runINTERRUPT Non-zero if commands for the .INTERRUPT target + * should be executed + * signo signal received + * + * Results: + * None + * + * Side Effects: + * All children are killed. Another job will be started if the + * .INTERRUPT target was given. + *----------------------------------------------------------------------- + */ +static void +JobInterrupt(int runINTERRUPT, int signo) +{ + Job *job; /* job descriptor in that element */ + GNode *interrupt; /* the node describing the .INTERRUPT target */ + sigset_t mask; + GNode *gn; + + aborting = ABORT_INTERRUPT; + + JobSigLock(&mask); + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state != JOB_ST_RUNNING) + continue; + + gn = job->node; + + if ((gn->type & (OP_JOIN|OP_PHONY)) == 0 && !Targ_Precious(gn)) { + char *file = (gn->path == NULL ? gn->name : gn->path); + if (!noExecute && eunlink(file) != -1) { + Error("*** %s removed", file); + } + } + if (job->pid) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, + "JobInterrupt passing signal %d to child %d.\n", + signo, job->pid); + } + KILLPG(job->pid, signo); + } + } + + JobSigUnlock(&mask); + + if (runINTERRUPT && !touchFlag) { + interrupt = Targ_FindNode(".INTERRUPT", TARG_NOCREATE); + if (interrupt != NULL) { + ignoreErrors = FALSE; + JobRun(interrupt); + } + } + Trace_Log(MAKEINTR, 0); + exit(signo); +} + +/* + *----------------------------------------------------------------------- + * Job_Finish -- + * Do final processing such as the running of the commands + * attached to the .END target. + * + * Results: + * Number of errors reported. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +int +Job_Finish(void) +{ + if (postCommands != NULL && + (!Lst_IsEmpty(postCommands->commands) || + !Lst_IsEmpty(postCommands->children))) { + if (errors) { + Error("Errors reported so .END ignored"); + } else { + JobRun(postCommands); + } + } + return(errors); +} + +/*- + *----------------------------------------------------------------------- + * Job_End -- + * Cleanup any memory used by the jobs module + * + * Results: + * None. + * + * Side Effects: + * Memory is freed + *----------------------------------------------------------------------- + */ +void +Job_End(void) +{ +#ifdef CLEANUP + if (shellArgv) + free(shellArgv); +#endif +} + +/*- + *----------------------------------------------------------------------- + * Job_Wait -- + * Waits for all running jobs to finish and returns. Sets 'aborting' + * to ABORT_WAIT to prevent other jobs from starting. + * + * Results: + * None. + * + * Side Effects: + * Currently running jobs finish. + * + *----------------------------------------------------------------------- + */ +void +Job_Wait(void) +{ + aborting = ABORT_WAIT; + while (jobTokensRunning != 0) { + Job_CatchOutput(); + } + aborting = 0; +} + +/*- + *----------------------------------------------------------------------- + * Job_AbortAll -- + * Abort all currently running jobs without handling output or anything. + * This function is to be called only in the event of a major + * error. Most definitely NOT to be called from JobInterrupt. + * + * Results: + * None + * + * Side Effects: + * All children are killed, not just the firstborn + *----------------------------------------------------------------------- + */ +void +Job_AbortAll(void) +{ + Job *job; /* the job descriptor in that element */ + int foo; + + aborting = ABORT_ERROR; + + if (jobTokensRunning) { + for (job = job_table; job < job_table_end; job++) { + if (job->job_state != JOB_ST_RUNNING) + continue; + /* + * kill the child process with increasingly drastic signals to make + * darn sure it's dead. + */ + KILLPG(job->pid, SIGINT); + KILLPG(job->pid, SIGKILL); + } + } + + /* + * Catch as many children as want to report in at first, then give up + */ + while (waitpid((pid_t) -1, &foo, WNOHANG) > 0) + continue; +} + + +/*- + *----------------------------------------------------------------------- + * JobRestartJobs -- + * Tries to restart stopped jobs if there are slots available. + * Called in process context in response to a SIGCONT. + * + * Results: + * None. + * + * Side Effects: + * Resumes jobs. + * + *----------------------------------------------------------------------- + */ +static void +JobRestartJobs(void) +{ + Job *job; + + for (job = job_table; job < job_table_end; job++) { + if (job->job_state == JOB_ST_RUNNING && + (make_suspended || job->job_suspended)) { + if (DEBUG(JOB)) { + (void)fprintf(debug_file, "Restarting stopped job pid %d.\n", + job->pid); + } + if (job->job_suspended) { + (void)printf("*** [%s] Continued\n", job->node->name); + (void)fflush(stdout); + } + job->job_suspended = 0; + if (KILLPG(job->pid, SIGCONT) != 0 && DEBUG(JOB)) { + fprintf(debug_file, "Failed to send SIGCONT to %d\n", job->pid); + } + } + if (job->job_state == JOB_ST_FINISHED) + /* Job exit deferred after calling waitpid() in a signal handler */ + JobFinish(job, job->exit_status); + } + make_suspended = 0; +} + +static void +watchfd(Job *job) +{ + if (job->inPollfd != NULL) + Punt("Watching watched job"); + + fds[nfds].fd = job->inPipe; + fds[nfds].events = POLLIN; + jobfds[nfds] = job; + job->inPollfd = &fds[nfds]; + nfds++; +} + +static void +clearfd(Job *job) +{ + int i; + if (job->inPollfd == NULL) + Punt("Unwatching unwatched job"); + i = job->inPollfd - fds; + nfds--; + /* + * Move last job in table into hole made by dead job. + */ + if (nfds != i) { + fds[i] = fds[nfds]; + jobfds[i] = jobfds[nfds]; + jobfds[i]->inPollfd = &fds[i]; + } + job->inPollfd = NULL; +} + +static int +readyfd(Job *job) +{ + if (job->inPollfd == NULL) + Punt("Polling unwatched job"); + return (job->inPollfd->revents & POLLIN) != 0; +} + +/*- + *----------------------------------------------------------------------- + * JobTokenAdd -- + * Put a token into the job pipe so that some make process can start + * another job. + * + * Side Effects: + * Allows more build jobs to be spawned somewhere. + * + *----------------------------------------------------------------------- + */ + +static void +JobTokenAdd(void) +{ + char tok = JOB_TOKENS[aborting], tok1; + + /* If we are depositing an error token flush everything else */ + while (tok != '+' && read(tokenWaitJob.inPipe, &tok1, 1) == 1) + continue; + + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) aborting %d, deposit token %c\n", + getpid(), aborting, JOB_TOKENS[aborting]); + while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) + continue; +} + +/*- + *----------------------------------------------------------------------- + * Job_ServerStartTokenAdd -- + * Prep the job token pipe in the root make process. + * + *----------------------------------------------------------------------- + */ + +void +Job_ServerStart(int max_tokens, int jp_0, int jp_1) +{ + int i; + char jobarg[64]; + + if (jp_0 >= 0 && jp_1 >= 0) { + /* Pipe passed in from parent */ + tokenWaitJob.inPipe = jp_0; + tokenWaitJob.outPipe = jp_1; + (void)fcntl(jp_0, F_SETFD, 1); + (void)fcntl(jp_1, F_SETFD, 1); + return; + } + + JobCreatePipe(&tokenWaitJob, 15); + + snprintf(jobarg, sizeof(jobarg), "%d,%d", + tokenWaitJob.inPipe, tokenWaitJob.outPipe); + + Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); + Var_Append(MAKEFLAGS, jobarg, VAR_GLOBAL); + + /* + * Preload the job pipe with one token per job, save the one + * "extra" token for the primary job. + * + * XXX should clip maxJobs against PIPE_BUF -- if max_tokens is + * larger than the write buffer size of the pipe, we will + * deadlock here. + */ + for (i = 1; i < max_tokens; i++) + JobTokenAdd(); +} + +/*- + *----------------------------------------------------------------------- + * Job_TokenReturn -- + * Return a withdrawn token to the pool. + * + *----------------------------------------------------------------------- + */ + +void +Job_TokenReturn(void) +{ + jobTokensRunning--; + if (jobTokensRunning < 0) + Punt("token botch"); + if (jobTokensRunning || JOB_TOKENS[aborting] != '+') + JobTokenAdd(); +} + +/*- + *----------------------------------------------------------------------- + * Job_TokenWithdraw -- + * Attempt to withdraw a token from the pool. + * + * Results: + * Returns TRUE if a token was withdrawn, and FALSE if the pool + * is currently empty. + * + * Side Effects: + * If pool is empty, set wantToken so that we wake up + * when a token is released. + * + *----------------------------------------------------------------------- + */ + + +Boolean +Job_TokenWithdraw(void) +{ + char tok, tok1; + int count; + + wantToken = 0; + if (DEBUG(JOB)) + fprintf(debug_file, "Job_TokenWithdraw(%d): aborting %d, running %d\n", + getpid(), aborting, jobTokensRunning); + + if (aborting || (jobTokensRunning >= maxJobs)) + return FALSE; + + count = read(tokenWaitJob.inPipe, &tok, 1); + if (count == 0) + Fatal("eof on job pipe!"); + if (count < 0 && jobTokensRunning != 0) { + if (errno != EAGAIN) { + Fatal("job pipe read: %s", strerror(errno)); + } + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) blocked for token\n", getpid()); + wantToken = 1; + return FALSE; + } + + if (count == 1 && tok != '+') { + /* make being abvorted - remove any other job tokens */ + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) aborted by token %c\n", getpid(), tok); + while (read(tokenWaitJob.inPipe, &tok1, 1) == 1) + continue; + /* And put the stopper back */ + while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) + continue; + Fatal("A failure has been detected in another branch of the parallel make"); + } + + if (count == 1 && jobTokensRunning == 0) + /* We didn't want the token really */ + while (write(tokenWaitJob.outPipe, &tok, 1) == -1 && errno == EAGAIN) + continue; + + jobTokensRunning++; + if (DEBUG(JOB)) + fprintf(debug_file, "(%d) withdrew token\n", getpid()); + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * Job_RunTarget -- + * Run the named target if found. If a filename is specified, then + * set that to the sources. + * + * Results: + * None + * + * Side Effects: + * exits if the target fails. + * + *----------------------------------------------------------------------- + */ +Boolean +Job_RunTarget(const char *target, const char *fname) { + GNode *gn = Targ_FindNode(target, TARG_NOCREATE); + + if (gn == NULL) + return FALSE; + + if (fname) + Var_Set(ALLSRC, fname, gn, 0); + + JobRun(gn); + if (gn->made == ERROR) { + PrintOnError(gn, "\n\nStop."); + exit(1); + } + return TRUE; +} + +#ifdef USE_SELECT +int +emul_poll(struct pollfd *fd, int nfd, int timeout) +{ + fd_set rfds, wfds; + int i, maxfd, nselect, npoll; + struct timeval tv, *tvp; + long usecs; + + FD_ZERO(&rfds); + FD_ZERO(&wfds); + + maxfd = -1; + for (i = 0; i < nfd; i++) { + fd[i].revents = 0; + + if (fd[i].events & POLLIN) + FD_SET(fd[i].fd, &rfds); + + if (fd[i].events & POLLOUT) + FD_SET(fd[i].fd, &wfds); + + if (fd[i].fd > maxfd) + maxfd = fd[i].fd; + } + + if (maxfd >= FD_SETSIZE) { + Punt("Ran out of fd_set slots; " + "recompile with a larger FD_SETSIZE."); + } + + if (timeout < 0) { + tvp = NULL; + } else { + usecs = timeout * 1000; + tv.tv_sec = usecs / 1000000; + tv.tv_usec = usecs % 1000000; + tvp = &tv; + } + + nselect = select(maxfd + 1, &rfds, &wfds, 0, tvp); + + if (nselect <= 0) + return nselect; + + npoll = 0; + for (i = 0; i < nfd; i++) { + if (FD_ISSET(fd[i].fd, &rfds)) + fd[i].revents |= POLLIN; + + if (FD_ISSET(fd[i].fd, &wfds)) + fd[i].revents |= POLLOUT; + + if (fd[i].revents) + npoll++; + } + + return npoll; +} +#endif /* USE_SELECT */ diff --git a/buildrump.sh/src/usr.bin/make/job.h b/buildrump.sh/src/usr.bin/make/job.h new file mode 100644 index 00000000..91e2c878 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/job.h @@ -0,0 +1,274 @@ +/* $NetBSD: job.h,v 1.42 2013/07/05 22:14:56 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)job.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)job.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * job.h -- + * Definitions pertaining to the running of jobs in parallel mode. + */ +#ifndef _JOB_H_ +#define _JOB_H_ + +#define TMPPAT "makeXXXXXX" /* relative to tmpdir */ + +#ifdef USE_SELECT +/* + * Emulate poll() in terms of select(). This is not a complete + * emulation but it is sufficient for make's purposes. + */ + +#define poll emul_poll +#define pollfd emul_pollfd + +struct emul_pollfd { + int fd; + short events; + short revents; +}; + +#define POLLIN 0x0001 +#define POLLOUT 0x0004 + +int +emul_poll(struct pollfd *fd, int nfd, int timeout); +#endif + +/* + * The POLL_MSEC constant determines the maximum number of milliseconds spent + * in poll before coming out to see if a child has finished. + */ +#define POLL_MSEC 5000 + + +/*- + * Job Table definitions. + * + * Each job has several things associated with it: + * 1) The process id of the child shell + * 2) The graph node describing the target being made by this job + * 3) A LstNode for the first command to be saved after the job + * completes. This is NULL if there was no "..." in the job's + * commands. + * 4) An FILE* for writing out the commands. This is only + * used before the job is actually started. + * 5) The output is being caught via a pipe and + * the descriptors of our pipe, an array in which output is line + * buffered and the current position in that buffer are all + * maintained for each job. + * 6) A word of flags which determine how the module handles errors, + * echoing, etc. for the job + * + * When a job is finished, the Make_Update function is called on each of the + * parents of the node which was just remade. This takes care of the upward + * traversal of the dependency graph. + */ +struct pollfd; + + +#ifdef USE_META +# include "meta.h" +#endif + +#define JOB_BUFSIZE 1024 +typedef struct Job { + int pid; /* The child's process ID */ + GNode *node; /* The target the child is making */ + LstNode tailCmds; /* The node of the first command to be + * saved when the job has been run */ + FILE *cmdFILE; /* When creating the shell script, this is + * where the commands go */ + int exit_status; /* from wait4() in signal handler */ + char job_state; /* status of the job entry */ +#define JOB_ST_FREE 0 /* Job is available */ +#define JOB_ST_SETUP 1 /* Job is allocated but otherwise invalid */ +#define JOB_ST_RUNNING 3 /* Job is running, pid valid */ +#define JOB_ST_FINISHED 4 /* Job is done (ie after SIGCHILD) */ + char job_suspended; + short flags; /* Flags to control treatment of job */ +#define JOB_IGNERR 0x001 /* Ignore non-zero exits */ +#define JOB_SILENT 0x002 /* no output */ +#define JOB_SPECIAL 0x004 /* Target is a special one. i.e. run it locally + * if we can't export it and maxLocal is 0 */ +#define JOB_IGNDOTS 0x008 /* Ignore "..." lines when processing + * commands */ +#define JOB_TRACED 0x400 /* we've sent 'set -x' */ + + int jobPipe[2]; /* Pipe for readind output from job */ + struct pollfd *inPollfd; /* pollfd associated with inPipe */ + char outBuf[JOB_BUFSIZE + 1]; + /* Buffer for storing the output of the + * job, line by line */ + int curPos; /* Current position in op_outBuf */ + +#ifdef USE_META + struct BuildMon bm; +#endif +} Job; + +#define inPipe jobPipe[0] +#define outPipe jobPipe[1] + + +/*- + * Shell Specifications: + * Each shell type has associated with it the following information: + * 1) The string which must match the last character of the shell name + * for the shell to be considered of this type. The longest match + * wins. + * 2) A command to issue to turn off echoing of command lines + * 3) A command to issue to turn echoing back on again + * 4) What the shell prints, and its length, when given the echo-off + * command. This line will not be printed when received from the shell + * 5) A boolean to tell if the shell has the ability to control + * error checking for individual commands. + * 6) The string to turn this checking on. + * 7) The string to turn it off. + * 8) The command-flag to give to cause the shell to start echoing + * commands right away. + * 9) The command-flag to cause the shell to Lib_Exit when an error is + * detected in one of the commands. + * + * Some special stuff goes on if a shell doesn't have error control. In such + * a case, errCheck becomes a printf template for echoing the command, + * should echoing be on and ignErr becomes another printf template for + * executing the command while ignoring the return status. Finally errOut + * is a printf template for running the command and causing the shell to + * exit on error. If any of these strings are empty when hasErrCtl is FALSE, + * the command will be executed anyway as is and if it causes an error, so be + * it. Any templates setup to echo the command will escape any '$ ` \ "'i + * characters in the command string to avoid common problems with + * echo "%s\n" as a template. + */ +typedef struct Shell { + const char *name; /* the name of the shell. For Bourne and C + * shells, this is used only to find the + * shell description when used as the single + * source of a .SHELL target. For user-defined + * shells, this is the full path of the shell. + */ + Boolean hasEchoCtl; /* True if both echoOff and echoOn defined */ + const char *echoOff; /* command to turn off echo */ + const char *echoOn; /* command to turn it back on again */ + const char *noPrint; /* command to skip when printing output from + * shell. This is usually the command which + * was executed to turn off echoing */ + int noPLen; /* length of noPrint command */ + Boolean hasErrCtl; /* set if can control error checking for + * individual commands */ + const char *errCheck; /* string to turn error checking on */ + const char *ignErr; /* string to turn off error checking */ + const char *errOut; /* string to use for testing exit code */ + const char *newline; /* string literal that results in a newline + * character when it appears outside of any + * 'quote' or "quote" characters */ + char commentChar; /* character used by shell for comment lines */ + + /* + * command-line flags + */ + const char *echo; /* echo commands */ + const char *exit; /* exit on error */ +} Shell; + +extern const char *shellPath; +extern const char *shellName; +extern char *shellErrFlag; + +extern int jobTokensRunning; /* tokens currently "out" */ +extern int maxJobs; /* Max jobs we can run */ + +void Shell_Init(void); +const char *Shell_GetNewline(void); +void Job_Touch(GNode *, Boolean); +Boolean Job_CheckCommands(GNode *, void (*abortProc )(const char *, ...)); +#define CATCH_BLOCK 1 +void Job_CatchChildren(void); +void Job_CatchOutput(void); +void Job_Make(GNode *); +void Job_Init(void); +Boolean Job_Full(void); +Boolean Job_Empty(void); +ReturnStatus Job_ParseShell(char *); +int Job_Finish(void); +void Job_End(void); +void Job_Wait(void); +void Job_AbortAll(void); +void JobFlagForMigration(int); +void Job_TokenReturn(void); +Boolean Job_TokenWithdraw(void); +void Job_ServerStart(int, int, int); +void Job_SetPrefix(void); +Boolean Job_RunTarget(const char *, const char *); + +#endif /* _JOB_H_ */ diff --git a/buildrump.sh/src/usr.bin/make/lst.h b/buildrump.sh/src/usr.bin/make/lst.h new file mode 100644 index 00000000..e207bc80 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.h @@ -0,0 +1,189 @@ +/* $NetBSD: lst.h,v 1.20 2014/09/07 20:55:34 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)lst.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1988, 1989 by Adam de Boor + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)lst.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * lst.h -- + * Header for using the list library + */ +#ifndef _LST_H_ +#define _LST_H_ + +#include <sys/param.h> +#include <stdlib.h> + +#include "sprite.h" + +/* + * basic typedef. This is what the Lst_ functions handle + */ + +typedef struct List *Lst; +typedef struct ListNode *LstNode; + +typedef void *DuplicateProc(void *); +typedef void FreeProc(void *); + +#define LST_CONCNEW 0 /* create new LstNode's when using Lst_Concat */ +#define LST_CONCLINK 1 /* relink LstNode's when using Lst_Concat */ + +/* + * Creation/destruction functions + */ +/* Create a new list */ +Lst Lst_Init(Boolean); +/* Duplicate an existing list */ +Lst Lst_Duplicate(Lst, DuplicateProc *); +/* Destroy an old one */ +void Lst_Destroy(Lst, FreeProc *); +/* True if list is empty */ +Boolean Lst_IsEmpty(Lst); + +/* + * Functions to modify a list + */ +/* Insert an element before another */ +ReturnStatus Lst_InsertBefore(Lst, LstNode, void *); +/* Insert an element after another */ +ReturnStatus Lst_InsertAfter(Lst, LstNode, void *); +/* Place an element at the front of a lst. */ +ReturnStatus Lst_AtFront(Lst, void *); +/* Place an element at the end of a lst. */ +ReturnStatus Lst_AtEnd(Lst, void *); +/* Remove an element */ +ReturnStatus Lst_Remove(Lst, LstNode); +/* Replace a node with a new value */ +ReturnStatus Lst_Replace(LstNode, void *); +/* Concatenate two lists */ +ReturnStatus Lst_Concat(Lst, Lst, int); + +/* + * Node-specific functions + */ +/* Return first element in list */ +LstNode Lst_First(Lst); +/* Return last element in list */ +LstNode Lst_Last(Lst); +/* Return successor to given element */ +LstNode Lst_Succ(LstNode); +/* Return predecessor to given element */ +LstNode Lst_Prev(LstNode); +/* Get datum from LstNode */ +void *Lst_Datum(LstNode); + +/* + * Functions for entire lists + */ +/* Find an element in a list */ +LstNode Lst_Find(Lst, const void *, int (*)(const void *, const void *)); +/* Find an element starting from somewhere */ +LstNode Lst_FindFrom(Lst, LstNode, const void *, + int (*cProc)(const void *, const void *)); +/* + * See if the given datum is on the list. Returns the LstNode containing + * the datum + */ +LstNode Lst_Member(Lst, void *); +/* Apply a function to all elements of a lst */ +int Lst_ForEach(Lst, int (*)(void *, void *), void *); +/* + * Apply a function to all elements of a lst starting from a certain point. + * If the list is circular, the application will wrap around to the + * beginning of the list again. + */ +int Lst_ForEachFrom(Lst, LstNode, int (*)(void *, void *), + void *); +/* + * these functions are for dealing with a list as a table, of sorts. + * An idea of the "current element" is kept and used by all the functions + * between Lst_Open() and Lst_Close(). + */ +/* Open the list */ +ReturnStatus Lst_Open(Lst); +/* Next element please */ +LstNode Lst_Next(Lst); +/* Done yet? */ +Boolean Lst_IsAtEnd(Lst); +/* Finish table access */ +void Lst_Close(Lst); + +/* + * for using the list as a queue + */ +/* Place an element at tail of queue */ +ReturnStatus Lst_EnQueue(Lst, void *); +/* Remove an element from head of queue */ +void *Lst_DeQueue(Lst); + +#endif /* _LST_H_ */ diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/Makefile b/buildrump.sh/src/usr.bin/make/lst.lib/Makefile new file mode 100644 index 00000000..5b33a506 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.6 2006/11/11 21:23:36 dsl Exp $ + +OBJ=lstAppend.o lstDupl.o lstInit.o lstOpen.o lstAtEnd.o lstEnQueue.o \ + lstInsert.o lstAtFront.o lstIsAtEnd.o lstClose.o lstFind.o lstIsEmpty.o \ + lstRemove.o lstConcat.o lstFindFrom.o lstLast.o lstReplace.o lstFirst.o \ + lstDatum.o lstForEach.o lstMember.o lstSucc.o lstDeQueue.o \ + lstForEachFrom.o lstDestroy.o lstNext.o lstPrev.o + +CPPFLAGS=-I${.CURDIR}/.. +all: ${OBJ} diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstAppend.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstAppend.c new file mode 100644 index 00000000..4dafe831 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstAppend.c @@ -0,0 +1,122 @@ +/* $NetBSD: lstAppend.c,v 1.14 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAppend.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAppend.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAppend.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstAppend.c -- + * Add a new node with a new datum after an existing node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_InsertAfter -- + * Create a new node and add it to the given list after the given node. + * + * Input: + * l affected list + * ln node after which to append the datum + * d said datum + * + * Results: + * SUCCESS if all went well. + * + * Side Effects: + * A new ListNode is created and linked in to the List. The lastPtr + * field of the List will be altered if ln is the last node in the + * list. lastPtr and firstPtr will alter if the list was empty and + * ln was NULL. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_InsertAfter(Lst l, LstNode ln, void *d) +{ + List list; + ListNode lNode; + ListNode nLNode; + + if (LstValid (l) && (ln == NULL && LstIsEmpty (l))) { + goto ok; + } + + if (!LstValid (l) || LstIsEmpty (l) || ! LstNodeValid (ln, l)) { + return (FAILURE); + } + ok: + + list = l; + lNode = ln; + + PAlloc (nLNode, ListNode); + nLNode->datum = d; + nLNode->useCount = nLNode->flags = 0; + + if (lNode == NULL) { + if (list->isCirc) { + nLNode->nextPtr = nLNode->prevPtr = nLNode; + } else { + nLNode->nextPtr = nLNode->prevPtr = NULL; + } + list->firstPtr = list->lastPtr = nLNode; + } else { + nLNode->prevPtr = lNode; + nLNode->nextPtr = lNode->nextPtr; + + lNode->nextPtr = nLNode; + if (nLNode->nextPtr != NULL) { + nLNode->nextPtr->prevPtr = nLNode; + } + + if (lNode == list->lastPtr) { + list->lastPtr = nLNode; + } + } + + return (SUCCESS); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstAtEnd.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstAtEnd.c new file mode 100644 index 00000000..10f191a2 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstAtEnd.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstAtEnd.c,v 1.13 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAtEnd.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAtEnd.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAtEnd.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstAtEnd.c -- + * Add a node at the end of the list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_AtEnd -- + * Add a node to the end of the given list + * + * Input: + * l List to which to add the datum + * d Datum to add + * + * Results: + * SUCCESS if life is good. + * + * Side Effects: + * A new ListNode is created and added to the list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_AtEnd(Lst l, void *d) +{ + LstNode end; + + end = Lst_Last(l); + return (Lst_InsertAfter(l, end, d)); +} diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstAtFront.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstAtFront.c new file mode 100644 index 00000000..d8be1664 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstAtFront.c @@ -0,0 +1,76 @@ +/* $NetBSD: lstAtFront.c,v 1.13 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstAtFront.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstAtFront.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstAtFront.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstAtFront.c -- + * Add a node at the front of the list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_AtFront -- + * Place a piece of data at the front of a list + * + * Results: + * SUCCESS or FAILURE + * + * Side Effects: + * A new ListNode is created and stuck at the front of the list. + * hence, firstPtr (and possible lastPtr) in the list are altered. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_AtFront(Lst l, void *d) +{ + LstNode front; + + front = Lst_First(l); + return (Lst_InsertBefore(l, front, d)); +} diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstClose.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstClose.c new file mode 100644 index 00000000..06b68c5c --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstClose.c @@ -0,0 +1,86 @@ +/* $NetBSD: lstClose.c,v 1.11 2006/10/27 21:37:25 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstClose.c,v 1.11 2006/10/27 21:37:25 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstClose.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstClose.c,v 1.11 2006/10/27 21:37:25 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstClose.c -- + * Close a list for sequential access. + * The sequential functions access the list in a slightly different way. + * CurPtr points to their idea of the current node in the list and they + * access the list based on it. Because the list is circular, Lst_Next + * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be + * used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Close -- + * Close a list which was opened for sequential access. + * + * Input: + * l The list to close + * + * Results: + * None. + * + * Side Effects: + * The list is closed. + * + *----------------------------------------------------------------------- + */ +void +Lst_Close(Lst l) +{ + List list = l; + + if (LstValid(l) == TRUE) { + list->isOpen = FALSE; + list->atEnd = Unknown; + } +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstConcat.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstConcat.c new file mode 100644 index 00000000..534d34e4 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstConcat.c @@ -0,0 +1,185 @@ +/* $NetBSD: lstConcat.c,v 1.16 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstConcat.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstConcat.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstConcat.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * listConcat.c -- + * Function to concatentate two lists. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Concat -- + * Concatenate two lists. New elements are created to hold the data + * elements, if specified, but the elements themselves are not copied. + * If the elements should be duplicated to avoid confusion with another + * list, the Lst_Duplicate function should be called first. + * If LST_CONCLINK is specified, the second list is destroyed since + * its pointers have been corrupted and the list is no longer useable. + * + * Input: + * l1 The list to which l2 is to be appended + * l2 The list to append to l1 + * flags LST_CONCNEW if LstNode's should be duplicated + * LST_CONCLINK if should just be relinked + * + * Results: + * SUCCESS if all went well. FAILURE otherwise. + * + * Side Effects: + * New elements are created and appended the first list. + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Concat(Lst l1, Lst l2, int flags) +{ + ListNode ln; /* original LstNode */ + ListNode nln; /* new LstNode */ + ListNode last; /* the last element in the list. Keeps + * bookkeeping until the end */ + List list1 = l1; + List list2 = l2; + + if (!LstValid (l1) || !LstValid (l2)) { + return (FAILURE); + } + + if (flags == LST_CONCLINK) { + if (list2->firstPtr != NULL) { + /* + * We set the nextPtr of the + * last element of list two to be NIL to make the loop easier and + * so we don't need an extra case should the first list turn + * out to be non-circular -- the final element will already point + * to NIL space and the first element will be untouched if it + * existed before and will also point to NIL space if it didn't. + */ + list2->lastPtr->nextPtr = NULL; + /* + * So long as the second list isn't empty, we just link the + * first element of the second list to the last element of the + * first list. If the first list isn't empty, we then link the + * last element of the list to the first element of the second list + * The last element of the second list, if it exists, then becomes + * the last element of the first list. + */ + list2->firstPtr->prevPtr = list1->lastPtr; + if (list1->lastPtr != NULL) { + list1->lastPtr->nextPtr = list2->firstPtr; + } else { + list1->firstPtr = list2->firstPtr; + } + list1->lastPtr = list2->lastPtr; + } + if (list1->isCirc && list1->firstPtr != NULL) { + /* + * If the first list is supposed to be circular and it is (now) + * non-empty, we must make sure it's circular by linking the + * first element to the last and vice versa + */ + list1->firstPtr->prevPtr = list1->lastPtr; + list1->lastPtr->nextPtr = list1->firstPtr; + } + free(l2); + } else if (list2->firstPtr != NULL) { + /* + * We set the nextPtr of the last element of list 2 to be nil to make + * the loop less difficult. The loop simply goes through the entire + * second list creating new LstNodes and filling in the nextPtr, and + * prevPtr to fit into l1 and its datum field from the + * datum field of the corresponding element in l2. The 'last' node + * follows the last of the new nodes along until the entire l2 has + * been appended. Only then does the bookkeeping catch up with the + * changes. During the first iteration of the loop, if 'last' is nil, + * the first list must have been empty so the newly-created node is + * made the first node of the list. + */ + list2->lastPtr->nextPtr = NULL; + for (last = list1->lastPtr, ln = list2->firstPtr; + ln != NULL; + ln = ln->nextPtr) + { + PAlloc (nln, ListNode); + nln->datum = ln->datum; + if (last != NULL) { + last->nextPtr = nln; + } else { + list1->firstPtr = nln; + } + nln->prevPtr = last; + nln->flags = nln->useCount = 0; + last = nln; + } + + /* + * Finish bookkeeping. The last new element becomes the last element + * of list one. + */ + list1->lastPtr = last; + + /* + * The circularity of both list one and list two must be corrected + * for -- list one because of the new nodes added to it; list two + * because of the alteration of list2->lastPtr's nextPtr to ease the + * above for loop. + */ + if (list1->isCirc) { + list1->lastPtr->nextPtr = list1->firstPtr; + list1->firstPtr->prevPtr = list1->lastPtr; + } else { + last->nextPtr = NULL; + } + + if (list2->isCirc) { + list2->lastPtr->nextPtr = list2->firstPtr; + } + } + + return (SUCCESS); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstDatum.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstDatum.c new file mode 100644 index 00000000..6e2d9ad0 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstDatum.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstDatum.c,v 1.13 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDatum.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDatum.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDatum.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstDatum.c -- + * Return the datum associated with a list node. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Datum -- + * Return the datum stored in the given node. + * + * Results: + * The datum or NULL if the node is invalid. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +void * +Lst_Datum(LstNode ln) +{ + if (ln != NULL) { + return ((ln)->datum); + } else { + return NULL; + } +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstDeQueue.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstDeQueue.c new file mode 100644 index 00000000..bdb05cc1 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstDeQueue.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstDeQueue.c,v 1.14 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDeQueue.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDeQueue.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDeQueue.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstDeQueue.c -- + * Remove the node and return its datum from the head of the list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_DeQueue -- + * Remove and return the datum at the head of the given list. + * + * Results: + * The datum in the node at the head or NULL if the list + * is empty. + * + * Side Effects: + * The head node is removed from the list. + * + *----------------------------------------------------------------------- + */ +void * +Lst_DeQueue(Lst l) +{ + void *rd; + ListNode tln; + + tln = Lst_First(l); + if (tln == NULL) { + return NULL; + } + + rd = tln->datum; + if (Lst_Remove(l, tln) == FAILURE) { + return NULL; + } else { + return (rd); + } +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstDestroy.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstDestroy.c new file mode 100644 index 00000000..92c5b2b2 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstDestroy.c @@ -0,0 +1,101 @@ +/* $NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDestroy.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDestroy.c,v 1.16 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstDestroy.c -- + * Nuke a list and all its resources + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Destroy -- + * Destroy a list and free all its resources. If the freeProc is + * given, it is called with the datum from each node in turn before + * the node is freed. + * + * Results: + * None. + * + * Side Effects: + * The given list is freed in its entirety. + * + *----------------------------------------------------------------------- + */ +void +Lst_Destroy(Lst list, FreeProc *freeProc) +{ + ListNode ln; + ListNode tln = NULL; + + if (list == NULL) + return; + + /* To ease scanning */ + if (list->lastPtr != NULL) + list->lastPtr->nextPtr = NULL; + else { + free(list); + return; + } + + if (freeProc) { + for (ln = list->firstPtr; ln != NULL; ln = tln) { + tln = ln->nextPtr; + freeProc(ln->datum); + free(ln); + } + } else { + for (ln = list->firstPtr; ln != NULL; ln = tln) { + tln = ln->nextPtr; + free(ln); + } + } + + free(list); +} diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstDupl.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstDupl.c new file mode 100644 index 00000000..2174ff78 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstDupl.c @@ -0,0 +1,107 @@ +/* $NetBSD: lstDupl.c,v 1.16 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstDupl.c,v 1.16 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstDupl.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstDupl.c,v 1.16 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * listDupl.c -- + * Duplicate a list. This includes duplicating the individual + * elements. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Duplicate -- + * Duplicate an entire list. If a function to copy a void *is + * given, the individual client elements will be duplicated as well. + * + * Input: + * l the list to duplicate + * copyProc A function to duplicate each void * + * + * Results: + * The new Lst structure or NULL if failure. + * + * Side Effects: + * A new list is created. + *----------------------------------------------------------------------- + */ +Lst +Lst_Duplicate(Lst l, DuplicateProc *copyProc) +{ + Lst nl; + ListNode ln; + List list = l; + + if (!LstValid (l)) { + return NULL; + } + + nl = Lst_Init(list->isCirc); + if (nl == NULL) { + return NULL; + } + + ln = list->firstPtr; + while (ln != NULL) { + if (copyProc != NULL) { + if (Lst_AtEnd(nl, copyProc(ln->datum)) == FAILURE) { + return NULL; + } + } else if (Lst_AtEnd(nl, ln->datum) == FAILURE) { + return NULL; + } + + if (list->isCirc && ln == list->lastPtr) { + ln = NULL; + } else { + ln = ln->nextPtr; + } + } + + return (nl); +} diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstEnQueue.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstEnQueue.c new file mode 100644 index 00000000..be386c91 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstEnQueue.c @@ -0,0 +1,78 @@ +/* $NetBSD: lstEnQueue.c,v 1.13 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstEnQueue.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstEnQueue.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstEnQueue.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstEnQueue.c-- + * Treat the list as a queue and place a datum at its end + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_EnQueue -- + * Add the datum to the tail of the given list. + * + * Results: + * SUCCESS or FAILURE as returned by Lst_InsertAfter. + * + * Side Effects: + * the lastPtr field is altered all the time and the firstPtr field + * will be altered if the list used to be empty. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_EnQueue(Lst l, void *d) +{ + if (LstValid (l) == FALSE) { + return (FAILURE); + } + + return (Lst_InsertAfter(l, Lst_Last(l), d)); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstFind.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstFind.c new file mode 100644 index 00000000..d07dbe7f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstFind.c @@ -0,0 +1,74 @@ +/* $NetBSD: lstFind.c,v 1.15 2009/01/23 21:58:28 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFind.c,v 1.15 2009/01/23 21:58:28 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFind.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFind.c,v 1.15 2009/01/23 21:58:28 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstFind.c -- + * Find a node on a list. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Find -- + * Find a node on the given list using the given comparison function + * and the given datum. + * + * Results: + * The found node or NULL if none matches. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Find(Lst l, const void *d, int (*cProc)(const void *, const void *)) +{ + return (Lst_FindFrom(l, Lst_First(l), d, cProc)); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstFindFrom.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstFindFrom.c new file mode 100644 index 00000000..e2beab63 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstFindFrom.c @@ -0,0 +1,90 @@ +/* $NetBSD: lstFindFrom.c,v 1.15 2009/01/23 21:58:28 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFindFrom.c,v 1.15 2009/01/23 21:58:28 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFindFrom.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFindFrom.c,v 1.15 2009/01/23 21:58:28 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstFindFrom.c -- + * Find a node on a list from a given starting point. Used by Lst_Find. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_FindFrom -- + * Search for a node starting and ending with the given one on the + * given list using the passed datum and comparison function to + * determine when it has been found. + * + * Results: + * The found node or NULL + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_FindFrom(Lst l, LstNode ln, const void *d, + int (*cProc)(const void *, const void *)) +{ + ListNode tln; + + if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { + return NULL; + } + + tln = ln; + + do { + if ((*cProc)(tln->datum, d) == 0) + return (tln); + tln = tln->nextPtr; + } while (tln != ln && tln != NULL); + + return NULL; +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstFirst.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstFirst.c new file mode 100644 index 00000000..4e8334f8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstFirst.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstFirst.c,v 1.12 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstFirst.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstFirst.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstFirst.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstFirst.c -- + * Return the first node of a list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_First -- + * Return the first node on the given list. + * + * Results: + * The first node or NULL if the list is empty. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_First(Lst l) +{ + if (!LstValid (l) || LstIsEmpty (l)) { + return NULL; + } else { + return (l->firstPtr); + } +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstForEach.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstForEach.c new file mode 100644 index 00000000..917e4ea8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstForEach.c @@ -0,0 +1,76 @@ +/* $NetBSD: lstForEach.c,v 1.13 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstForEach.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstForEach.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstForEach.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstForeach.c -- + * Perform a given function on all elements of a list. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_ForEach -- + * Apply the given function to each element of the given list. The + * function should return 0 if Lst_ForEach should continue and non- + * zero if it should abort. + * + * Results: + * None. + * + * Side Effects: + * Only those created by the passed-in function. + * + *----------------------------------------------------------------------- + */ +/*VARARGS2*/ +int +Lst_ForEach(Lst l, int (*proc)(void *, void *), void *d) +{ + return Lst_ForEachFrom(l, Lst_First(l), proc, d); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstForEachFrom.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstForEachFrom.c new file mode 100644 index 00000000..c7f44adc --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstForEachFrom.c @@ -0,0 +1,125 @@ +/* $NetBSD: lstForEachFrom.c,v 1.17 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstForEachFrom.c,v 1.17 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstForEachFrom.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstForEachFrom.c,v 1.17 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * lstForEachFrom.c -- + * Perform a given function on all elements of a list starting from + * a given point. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_ForEachFrom -- + * Apply the given function to each element of the given list. The + * function should return 0 if traversal should continue and non- + * zero if it should abort. + * + * Results: + * None. + * + * Side Effects: + * Only those created by the passed-in function. + * + *----------------------------------------------------------------------- + */ +/*VARARGS2*/ +int +Lst_ForEachFrom(Lst l, LstNode ln, int (*proc)(void *, void *), + void *d) +{ + ListNode tln = ln; + List list = l; + ListNode next; + Boolean done; + int result; + + if (!LstValid (list) || LstIsEmpty (list)) { + return 0; + } + + do { + /* + * Take care of having the current element deleted out from under + * us. + */ + + next = tln->nextPtr; + + /* + * We're done with the traversal if + * - the next node to examine is the first in the queue or + * doesn't exist and + * - nothing's been added after the current node (check this + * after proc() has been called). + */ + done = (next == NULL || next == list->firstPtr); + + (void) tln->useCount++; + result = (*proc) (tln->datum, d); + (void) tln->useCount--; + + /* + * Now check whether a node has been added. + * Note: this doesn't work if this node was deleted before + * the new node was added. + */ + if (next != tln->nextPtr) { + next = tln->nextPtr; + done = 0; + } + + if (tln->flags & LN_DELETED) { + free((char *)tln); + } + tln = next; + } while (!result && !LstIsEmpty(list) && !done); + + return result; +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstInit.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstInit.c new file mode 100644 index 00000000..f98ac42b --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstInit.c @@ -0,0 +1,85 @@ +/* $NetBSD: lstInit.c,v 1.12 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstInit.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstInit.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstInit.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * init.c -- + * Initialize a new linked list. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Init -- + * Create and initialize a new list. + * + * Input: + * circ TRUE if the list should be made circular + * + * Results: + * The created list. + * + * Side Effects: + * A list is created, what else? + * + *----------------------------------------------------------------------- + */ +Lst +Lst_Init(Boolean circ) +{ + List nList; + + PAlloc (nList, List); + + nList->firstPtr = NULL; + nList->lastPtr = NULL; + nList->isOpen = FALSE; + nList->isCirc = circ; + nList->atEnd = Unknown; + + return (nList); +} diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstInsert.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstInsert.c new file mode 100644 index 00000000..77187bb3 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstInsert.c @@ -0,0 +1,122 @@ +/* $NetBSD: lstInsert.c,v 1.14 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstInsert.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstInsert.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstInsert.c,v 1.14 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstInsert.c -- + * Insert a new datum before an old one + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_InsertBefore -- + * Insert a new node with the given piece of data before the given + * node in the given list. + * + * Input: + * l list to manipulate + * ln node before which to insert d + * d datum to be inserted + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * the firstPtr field will be changed if ln is the first node in the + * list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_InsertBefore(Lst l, LstNode ln, void *d) +{ + ListNode nLNode; /* new lnode for d */ + ListNode lNode = ln; + List list = l; + + + /* + * check validity of arguments + */ + if (LstValid (l) && (LstIsEmpty (l) && ln == NULL)) + goto ok; + + if (!LstValid (l) || LstIsEmpty (l) || !LstNodeValid (ln, l)) { + return (FAILURE); + } + + ok: + PAlloc (nLNode, ListNode); + + nLNode->datum = d; + nLNode->useCount = nLNode->flags = 0; + + if (ln == NULL) { + if (list->isCirc) { + nLNode->prevPtr = nLNode->nextPtr = nLNode; + } else { + nLNode->prevPtr = nLNode->nextPtr = NULL; + } + list->firstPtr = list->lastPtr = nLNode; + } else { + nLNode->prevPtr = lNode->prevPtr; + nLNode->nextPtr = lNode; + + if (nLNode->prevPtr != NULL) { + nLNode->prevPtr->nextPtr = nLNode; + } + lNode->prevPtr = nLNode; + + if (lNode == list->firstPtr) { + list->firstPtr = nLNode; + } + } + + return (SUCCESS); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstInt.h b/buildrump.sh/src/usr.bin/make/lst.lib/lstInt.h new file mode 100644 index 00000000..ac53dcb6 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstInt.h @@ -0,0 +1,105 @@ +/* $NetBSD: lstInt.h,v 1.22 2014/09/07 20:55:34 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)lstInt.h 8.1 (Berkeley) 6/6/93 + */ + +/*- + * lstInt.h -- + * Internals for the list library + */ +#ifndef _LSTINT_H_ +#define _LSTINT_H_ + +#include "../lst.h" +#include "../make_malloc.h" + +typedef struct ListNode { + struct ListNode *prevPtr; /* previous element in list */ + struct ListNode *nextPtr; /* next in list */ + unsigned int useCount:8, /* Count of functions using the node. + * node may not be deleted until count + * goes to 0 */ + flags:8; /* Node status flags */ + void *datum; /* datum associated with this element */ +} *ListNode; +/* + * Flags required for synchronization + */ +#define LN_DELETED 0x0001 /* List node should be removed when done */ + +typedef enum { + Head, Middle, Tail, Unknown +} Where; + +typedef struct List { + ListNode firstPtr; /* first node in list */ + ListNode lastPtr; /* last node in list */ + Boolean isCirc; /* true if the list should be considered + * circular */ +/* + * fields for sequential access + */ + Where atEnd; /* Where in the list the last access was */ + Boolean isOpen; /* true if list has been Lst_Open'ed */ + ListNode curPtr; /* current node, if open. NULL if + * *just* opened */ + ListNode prevPtr; /* Previous node, if open. Used by + * Lst_Remove */ +} *List; + +/* + * PAlloc (var, ptype) -- + * Allocate a pointer-typedef structure 'ptype' into the variable 'var' + */ +#define PAlloc(var,ptype) var = (ptype) bmake_malloc(sizeof *(var)) + +/* + * LstValid (l) -- + * Return TRUE if the list l is valid + */ +#define LstValid(l) ((Lst)(l) != NULL) + +/* + * LstNodeValid (ln, l) -- + * Return TRUE if the LstNode ln is valid with respect to l + */ +#define LstNodeValid(ln, l) ((ln) != NULL) + +/* + * LstIsEmpty (l) -- + * TRUE if the list l is empty. + */ +#define LstIsEmpty(l) (((List)(l))->firstPtr == NULL) + +#endif /* _LSTINT_H_ */ diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstIsAtEnd.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstIsAtEnd.c new file mode 100644 index 00000000..70270d29 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstIsAtEnd.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstIsAtEnd.c,v 1.13 2008/02/15 21:29:50 christos Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstIsAtEnd.c,v 1.13 2008/02/15 21:29:50 christos Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstIsAtEnd.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstIsAtEnd.c,v 1.13 2008/02/15 21:29:50 christos Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstIsAtEnd.c -- + * Tell if the current node is at the end of the list. + * The sequential functions access the list in a slightly different way. + * CurPtr points to their idea of the current node in the list and they + * access the list based on it. Because the list is circular, Lst_Next + * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be + * used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_IsAtEnd -- + * Return true if have reached the end of the given list. + * + * Results: + * TRUE if at the end of the list (this includes the list not being + * open or being invalid) or FALSE if not. We return TRUE if the list + * is invalid or unopend so as to cause the caller to exit its loop + * asap, the assumption being that the loop is of the form + * while (!Lst_IsAtEnd (l)) { + * ... + * } + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Boolean +Lst_IsAtEnd(Lst l) +{ + List list = l; + + return (!LstValid (l) || !list->isOpen || + (list->atEnd == Head) || (list->atEnd == Tail)); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstIsEmpty.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstIsEmpty.c new file mode 100644 index 00000000..8b1d6ed0 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstIsEmpty.c @@ -0,0 +1,75 @@ +/* $NetBSD: lstIsEmpty.c,v 1.11 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstIsEmpty.c,v 1.11 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstIsEmpty.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstIsEmpty.c,v 1.11 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstIsEmpty.c -- + * A single function to decide if a list is empty + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_IsEmpty -- + * Return TRUE if the given list is empty. + * + * Results: + * TRUE if the list is empty, FALSE otherwise. + * + * Side Effects: + * None. + * + * A list is considered empty if its firstPtr == NULL (or if + * the list itself is NULL). + *----------------------------------------------------------------------- + */ +Boolean +Lst_IsEmpty(Lst l) +{ + return ( ! LstValid (l) || LstIsEmpty(l)); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstLast.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstLast.c new file mode 100644 index 00000000..096ca24d --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstLast.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstLast.c,v 1.12 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstLast.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstLast.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstLast.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstLast.c -- + * Return the last element of a list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Last -- + * Return the last node on the list l. + * + * Results: + * The requested node or NULL if the list is empty. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Last(Lst l) +{ + if (!LstValid(l) || LstIsEmpty (l)) { + return NULL; + } else { + return (l->lastPtr); + } +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstMember.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstMember.c new file mode 100644 index 00000000..e9046aca --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstMember.c @@ -0,0 +1,77 @@ +/* $NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstMember.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstMember.c,v 1.14 2013/11/14 00:01:28 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * lstMember.c -- + * See if a given datum is on a given list. + */ + +#include "lstInt.h" + +LstNode +Lst_Member(Lst l, void *d) +{ + List list = l; + ListNode lNode; + + if (list == NULL) { + return NULL; + } + lNode = list->firstPtr; + if (lNode == NULL) { + return NULL; + } + + do { + if (lNode->datum == d) { + return lNode; + } + lNode = lNode->nextPtr; + } while (lNode != NULL && lNode != list->firstPtr); + + return NULL; +} diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstNext.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstNext.c new file mode 100644 index 00000000..5c2e0eec --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstNext.c @@ -0,0 +1,120 @@ +/* $NetBSD: lstNext.c,v 1.12 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstNext.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstNext.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstNext.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstNext.c -- + * Return the next node for a list. + * The sequential functions access the list in a slightly different way. + * CurPtr points to their idea of the current node in the list and they + * access the list based on it. Because the list is circular, Lst_Next + * and Lst_Prev will go around the list forever. Lst_IsAtEnd must be + * used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Next -- + * Return the next node for the given list. + * + * Results: + * The next node or NULL if the list has yet to be opened. Also + * if the list is non-circular and the end has been reached, NULL + * is returned. + * + * Side Effects: + * the curPtr field is updated. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Next(Lst l) +{ + ListNode tln; + List list = l; + + if ((LstValid (l) == FALSE) || + (list->isOpen == FALSE)) { + return NULL; + } + + list->prevPtr = list->curPtr; + + if (list->curPtr == NULL) { + if (list->atEnd == Unknown) { + /* + * If we're just starting out, atEnd will be Unknown. + * Then we want to start this thing off in the right + * direction -- at the start with atEnd being Middle. + */ + list->curPtr = tln = list->firstPtr; + list->atEnd = Middle; + } else { + tln = NULL; + list->atEnd = Tail; + } + } else { + tln = list->curPtr->nextPtr; + list->curPtr = tln; + + if (tln == list->firstPtr || tln == NULL) { + /* + * If back at the front, then we've hit the end... + */ + list->atEnd = Tail; + } else { + /* + * Reset to Middle if gone past first. + */ + list->atEnd = Middle; + } + } + + return (tln); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstOpen.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstOpen.c new file mode 100644 index 00000000..941293e7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstOpen.c @@ -0,0 +1,87 @@ +/* $NetBSD: lstOpen.c,v 1.12 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstOpen.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstOpen.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstOpen.c,v 1.12 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstOpen.c -- + * Open a list for sequential access. The sequential functions access the + * list in a slightly different way. CurPtr points to their idea of the + * current node in the list and they access the list based on it. + * If the list is circular, Lst_Next and Lst_Prev will go around + * the list forever. Lst_IsAtEnd must be used to determine when to stop. + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Open -- + * Open a list for sequential access. A list can still be searched, + * etc., without confusing these functions. + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * isOpen is set TRUE and curPtr is set to NULL so the + * other sequential functions no it was just opened and can choose + * the first element accessed based on this. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Open(Lst l) +{ + if (LstValid (l) == FALSE) { + return (FAILURE); + } + (l)->isOpen = TRUE; + (l)->atEnd = LstIsEmpty (l) ? Head : Unknown; + (l)->curPtr = NULL; + + return (SUCCESS); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstPrev.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstPrev.c new file mode 100644 index 00000000..0ec865d5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstPrev.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstPrev.c,v 1.3 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstPrev.c,v 1.3 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstPrev.c,v 1.3 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstPrev.c -- + * return the predecessor to a given node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Prev -- + * Return the predecessor to the given node on its list. + * + * Results: + * The predecessor of the node, if it exists (note that on a circular + * list, if the node is the only one in the list, it is its own + * predecessor). + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Prev(LstNode ln) +{ + if (ln == NULL) { + return NULL; + } else { + return (ln->prevPtr); + } +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstRemove.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstRemove.c new file mode 100644 index 00000000..7480d30b --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstRemove.c @@ -0,0 +1,136 @@ +/* $NetBSD: lstRemove.c,v 1.16 2014/09/07 20:55:34 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstRemove.c,v 1.16 2014/09/07 20:55:34 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstRemove.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstRemove.c,v 1.16 2014/09/07 20:55:34 joerg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstRemove.c -- + * Remove an element from a list + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Remove -- + * Remove the given node from the given list. + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * The list's firstPtr will be set to NULL if ln is the last + * node on the list. firsPtr and lastPtr will be altered if ln is + * either the first or last node, respectively, on the list. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Remove(Lst l, LstNode ln) +{ + List list = l; + ListNode lNode = ln; + + if (!LstValid (l) || + !LstNodeValid (ln, l)) { + return (FAILURE); + } + + /* + * unlink it from the list + */ + if (lNode->nextPtr != NULL) { + lNode->nextPtr->prevPtr = lNode->prevPtr; + } + if (lNode->prevPtr != NULL) { + lNode->prevPtr->nextPtr = lNode->nextPtr; + } + + /* + * if either the firstPtr or lastPtr of the list point to this node, + * adjust them accordingly + */ + if (list->firstPtr == lNode) { + list->firstPtr = lNode->nextPtr; + } + if (list->lastPtr == lNode) { + list->lastPtr = lNode->prevPtr; + } + + /* + * Sequential access stuff. If the node we're removing is the current + * node in the list, reset the current node to the previous one. If the + * previous one was non-existent (prevPtr == NULL), we set the + * end to be Unknown, since it is. + */ + if (list->isOpen && (list->curPtr == lNode)) { + list->curPtr = list->prevPtr; + if (list->curPtr == NULL) { + list->atEnd = Unknown; + } + } + + /* + * the only way firstPtr can still point to ln is if ln is the last + * node on the list (the list is circular, so lNode->nextptr == lNode in + * this case). The list is, therefore, empty and is marked as such + */ + if (list->firstPtr == lNode) { + list->firstPtr = NULL; + } + + /* + * note that the datum is unmolested. The caller must free it as + * necessary and as expected. + */ + if (lNode->useCount == 0) { + free(ln); + } else { + lNode->flags |= LN_DELETED; + } + + return (SUCCESS); +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstReplace.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstReplace.c new file mode 100644 index 00000000..090e91a7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstReplace.c @@ -0,0 +1,78 @@ +/* $NetBSD: lstReplace.c,v 1.13 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstReplace.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstReplace.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstReplace.c,v 1.13 2009/01/23 21:26:30 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstReplace.c -- + * Replace the datum in a node with a new datum + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Replace -- + * Replace the datum in the given node with the new datum + * + * Results: + * SUCCESS or FAILURE. + * + * Side Effects: + * The datum field fo the node is altered. + * + *----------------------------------------------------------------------- + */ +ReturnStatus +Lst_Replace(LstNode ln, void *d) +{ + if (ln == NULL) { + return (FAILURE); + } else { + (ln)->datum = d; + return (SUCCESS); + } +} + diff --git a/buildrump.sh/src/usr.bin/make/lst.lib/lstSucc.c b/buildrump.sh/src/usr.bin/make/lst.lib/lstSucc.c new file mode 100644 index 00000000..3f13aa5e --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/lst.lib/lstSucc.c @@ -0,0 +1,79 @@ +/* $NetBSD: lstSucc.c,v 1.13 2008/12/13 15:19:29 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: lstSucc.c,v 1.13 2008/12/13 15:19:29 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lstSucc.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: lstSucc.c,v 1.13 2008/12/13 15:19:29 dsl Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * LstSucc.c -- + * return the successor to a given node + */ + +#include "lstInt.h" + +/*- + *----------------------------------------------------------------------- + * Lst_Succ -- + * Return the successor to the given node on its list. + * + * Results: + * The successor of the node, if it exists (note that on a circular + * list, if the node is the only one in the list, it is its own + * successor). + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +LstNode +Lst_Succ(LstNode ln) +{ + if (ln == NULL) { + return NULL; + } else { + return (ln->nextPtr); + } +} + diff --git a/buildrump.sh/src/usr.bin/make/main.c b/buildrump.sh/src/usr.bin/make/main.c new file mode 100644 index 00000000..91b05e9f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/main.c @@ -0,0 +1,1989 @@ +/* $NetBSD: main.c,v 1.232 2015/03/26 22:20:42 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: main.c,v 1.232 2015/03/26 22:20:42 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1989, 1990, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: main.c,v 1.232 2015/03/26 22:20:42 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * main.c -- + * The main file for this entire program. Exit routines etc + * reside here. + * + * Utility functions defined in this file: + * Main_ParseArgLine Takes a line of arguments, breaks them and + * treats them as if they were given when first + * invoked. Used by the parse module to implement + * the .MFLAGS target. + * + * Error Print a tagged error message. The global + * MAKE variable must have been defined. This + * takes a format string and optional arguments + * for it. + * + * Fatal Print an error message and exit. Also takes + * a format string and arguments for it. + * + * Punt Aborts all jobs and exits with a message. Also + * takes a format string and arguments for it. + * + * Finish Finish things up by printing the number of + * errors which occurred, as passed to it, and + * exiting. + */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/resource.h> +#include <sys/stat.h> +#ifdef MAKE_NATIVE +#include <sys/sysctl.h> +#endif +#include <sys/utsname.h> +#include <sys/wait.h> + +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <ctype.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "pathnames.h" +#include "trace.h" + +#ifdef USE_IOVEC +#include <sys/uio.h> +#endif + +#ifndef DEFMAXLOCAL +#define DEFMAXLOCAL DEFMAXJOBS +#endif /* DEFMAXLOCAL */ + +Lst create; /* Targets to be made */ +time_t now; /* Time at start of make */ +GNode *DEFAULT; /* .DEFAULT node */ +Boolean allPrecious; /* .PRECIOUS given on line by itself */ + +static Boolean noBuiltins; /* -r flag */ +static Lst makefiles; /* ordered list of makefiles to read */ +static Boolean printVars; /* print value of one or more vars */ +static Lst variables; /* list of variables to print */ +int maxJobs; /* -j argument */ +static int maxJobTokens; /* -j argument */ +Boolean compatMake; /* -B argument */ +int debug; /* -d argument */ +Boolean debugVflag; /* -dV */ +Boolean noExecute; /* -n flag */ +Boolean noRecursiveExecute; /* -N flag */ +Boolean keepgoing; /* -k flag */ +Boolean queryFlag; /* -q flag */ +Boolean touchFlag; /* -t flag */ +Boolean enterFlag; /* -w flag */ +Boolean ignoreErrors; /* -i flag */ +Boolean beSilent; /* -s flag */ +Boolean oldVars; /* variable substitution style */ +Boolean checkEnvFirst; /* -e flag */ +Boolean parseWarnFatal; /* -W flag */ +Boolean jobServer; /* -J flag */ +static int jp_0 = -1, jp_1 = -1; /* ends of parent job pipe */ +Boolean varNoExportEnv; /* -X flag */ +Boolean doing_depend; /* Set while reading .depend */ +static Boolean jobsRunning; /* TRUE if the jobs might be running */ +static const char * tracefile; +static void MainParseArgs(int, char **); +static int ReadMakefile(const void *, const void *); +static void usage(void) MAKE_ATTR_DEAD; + +static Boolean ignorePWD; /* if we use -C, PWD is meaningless */ +static char objdir[MAXPATHLEN + 1]; /* where we chdir'ed to */ +char curdir[MAXPATHLEN + 1]; /* Startup directory */ +char *progname; /* the program name */ +char *makeDependfile; +pid_t myPid; +int makelevel; + +Boolean forceJobs = FALSE; + +extern Lst parseIncPath; + +/* + * For compatibility with the POSIX version of MAKEFLAGS that includes + * all the options with out -, convert flags to -f -l -a -g -s. + */ +static char * +explode(const char *flags) +{ + size_t len; + char *nf, *st; + const char *f; + + if (flags == NULL) + return NULL; + + for (f = flags; *f; f++) + if (!isalpha((unsigned char)*f)) + break; + + if (*f) + return bmake_strdup(flags); + + len = strlen(flags); + st = nf = bmake_malloc(len * 3 + 1); + while (*flags) { + *nf++ = '-'; + *nf++ = *flags++; + *nf++ = ' '; + } + *nf = '\0'; + return st; +} + +static void +parse_debug_options(const char *argvalue) +{ + const char *modules; + const char *mode; + char *fname; + int len; + + for (modules = argvalue; *modules; ++modules) { + switch (*modules) { + case 'A': + debug = ~0; + break; + case 'a': + debug |= DEBUG_ARCH; + break; + case 'C': + debug |= DEBUG_CWD; + break; + case 'c': + debug |= DEBUG_COND; + break; + case 'd': + debug |= DEBUG_DIR; + break; + case 'e': + debug |= DEBUG_ERROR; + break; + case 'f': + debug |= DEBUG_FOR; + break; + case 'g': + if (modules[1] == '1') { + debug |= DEBUG_GRAPH1; + ++modules; + } + else if (modules[1] == '2') { + debug |= DEBUG_GRAPH2; + ++modules; + } + else if (modules[1] == '3') { + debug |= DEBUG_GRAPH3; + ++modules; + } + break; + case 'j': + debug |= DEBUG_JOB; + break; + case 'l': + debug |= DEBUG_LOUD; + break; + case 'M': + debug |= DEBUG_META; + break; + case 'm': + debug |= DEBUG_MAKE; + break; + case 'n': + debug |= DEBUG_SCRIPT; + break; + case 'p': + debug |= DEBUG_PARSE; + break; + case 's': + debug |= DEBUG_SUFF; + break; + case 't': + debug |= DEBUG_TARG; + break; + case 'V': + debugVflag = TRUE; + break; + case 'v': + debug |= DEBUG_VAR; + break; + case 'x': + debug |= DEBUG_SHELL; + break; + case 'F': + if (debug_file != stdout && debug_file != stderr) + fclose(debug_file); + if (*++modules == '+') { + modules++; + mode = "a"; + } else + mode = "w"; + if (strcmp(modules, "stdout") == 0) { + debug_file = stdout; + goto debug_setbuf; + } + if (strcmp(modules, "stderr") == 0) { + debug_file = stderr; + goto debug_setbuf; + } + len = strlen(modules); + fname = malloc(len + 20); + memcpy(fname, modules, len + 1); + /* Let the filename be modified by the pid */ + if (strcmp(fname + len - 3, ".%d") == 0) + snprintf(fname + len - 2, 20, "%d", getpid()); + debug_file = fopen(fname, mode); + if (!debug_file) { + fprintf(stderr, "Cannot open debug file %s\n", + fname); + usage(); + } + free(fname); + goto debug_setbuf; + default: + (void)fprintf(stderr, + "%s: illegal argument to d option -- %c\n", + progname, *modules); + usage(); + } + } +debug_setbuf: + /* + * Make the debug_file unbuffered, and make + * stdout line buffered (unless debugfile == stdout). + */ + setvbuf(debug_file, NULL, _IONBF, 0); + if (debug_file != stdout) { + setvbuf(stdout, NULL, _IOLBF, 0); + } +} + +/*- + * MainParseArgs -- + * Parse a given argument vector. Called from main() and from + * Main_ParseArgLine() when the .MAKEFLAGS target is used. + * + * XXX: Deal with command line overriding .MAKEFLAGS in makefile + * + * Results: + * None + * + * Side Effects: + * Various global and local flags will be set depending on the flags + * given + */ +static void +MainParseArgs(int argc, char **argv) +{ + char *p; + int c = '?'; + int arginc; + char *argvalue; + const char *getopt_def; + char *optscan; + Boolean inOption, dashDash = FALSE; + char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ + +#define OPTFLAGS "BC:D:I:J:NST:V:WXd:ef:ij:km:nqrstw" +/* Can't actually use getopt(3) because rescanning is not portable */ + + getopt_def = OPTFLAGS; +rearg: + inOption = FALSE; + optscan = NULL; + while(argc > 1) { + char *getopt_spec; + if(!inOption) + optscan = argv[1]; + c = *optscan++; + arginc = 0; + if(inOption) { + if(c == '\0') { + ++argv; + --argc; + inOption = FALSE; + continue; + } + } else { + if (c != '-' || dashDash) + break; + inOption = TRUE; + c = *optscan++; + } + /* '-' found at some earlier point */ + getopt_spec = strchr(getopt_def, c); + if(c != '\0' && getopt_spec != NULL && getopt_spec[1] == ':') { + /* -<something> found, and <something> should have an arg */ + inOption = FALSE; + arginc = 1; + argvalue = optscan; + if(*argvalue == '\0') { + if (argc < 3) + goto noarg; + argvalue = argv[2]; + arginc = 2; + } + } else { + argvalue = NULL; + } + switch(c) { + case '\0': + arginc = 1; + inOption = FALSE; + break; + case 'B': + compatMake = TRUE; + Var_Append(MAKEFLAGS, "-B", VAR_GLOBAL); + Var_Set(MAKE_MODE, "compat", VAR_GLOBAL, 0); + break; + case 'C': + if (chdir(argvalue) == -1) { + (void)fprintf(stderr, + "%s: chdir %s: %s\n", + progname, argvalue, + strerror(errno)); + exit(1); + } + if (getcwd(curdir, MAXPATHLEN) == NULL) { + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(errno)); + exit(2); + } + ignorePWD = TRUE; + break; + case 'D': + if (argvalue == NULL || argvalue[0] == 0) goto noarg; + Var_Set(argvalue, "1", VAR_GLOBAL, 0); + Var_Append(MAKEFLAGS, "-D", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'I': + if (argvalue == NULL) goto noarg; + Parse_AddIncludeDir(argvalue); + Var_Append(MAKEFLAGS, "-I", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'J': + if (argvalue == NULL) goto noarg; + if (sscanf(argvalue, "%d,%d", &jp_0, &jp_1) != 2) { + (void)fprintf(stderr, + "%s: internal error -- J option malformed (%s)\n", + progname, argvalue); + usage(); + } + if ((fcntl(jp_0, F_GETFD, 0) < 0) || + (fcntl(jp_1, F_GETFD, 0) < 0)) { +#if 0 + (void)fprintf(stderr, + "%s: ###### warning -- J descriptors were closed!\n", + progname); + exit(2); +#endif + jp_0 = -1; + jp_1 = -1; + compatMake = TRUE; + } else { + Var_Append(MAKEFLAGS, "-J", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + jobServer = TRUE; + } + break; + case 'N': + noExecute = TRUE; + noRecursiveExecute = TRUE; + Var_Append(MAKEFLAGS, "-N", VAR_GLOBAL); + break; + case 'S': + keepgoing = FALSE; + Var_Append(MAKEFLAGS, "-S", VAR_GLOBAL); + break; + case 'T': + if (argvalue == NULL) goto noarg; + tracefile = bmake_strdup(argvalue); + Var_Append(MAKEFLAGS, "-T", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'V': + if (argvalue == NULL) goto noarg; + printVars = TRUE; + (void)Lst_AtEnd(variables, argvalue); + Var_Append(MAKEFLAGS, "-V", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'W': + parseWarnFatal = TRUE; + break; + case 'X': + varNoExportEnv = TRUE; + Var_Append(MAKEFLAGS, "-X", VAR_GLOBAL); + break; + case 'd': + if (argvalue == NULL) goto noarg; + /* If '-d-opts' don't pass to children */ + if (argvalue[0] == '-') + argvalue++; + else { + Var_Append(MAKEFLAGS, "-d", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + } + parse_debug_options(argvalue); + break; + case 'e': + checkEnvFirst = TRUE; + Var_Append(MAKEFLAGS, "-e", VAR_GLOBAL); + break; + case 'f': + if (argvalue == NULL) goto noarg; + (void)Lst_AtEnd(makefiles, argvalue); + break; + case 'i': + ignoreErrors = TRUE; + Var_Append(MAKEFLAGS, "-i", VAR_GLOBAL); + break; + case 'j': + if (argvalue == NULL) goto noarg; + forceJobs = TRUE; + maxJobs = strtol(argvalue, &p, 0); + if (*p != '\0' || maxJobs < 1) { + (void)fprintf(stderr, "%s: illegal argument to -j -- must be positive integer!\n", + progname); + exit(1); + } + Var_Append(MAKEFLAGS, "-j", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + Var_Set(".MAKE.JOBS", argvalue, VAR_GLOBAL, 0); + maxJobTokens = maxJobs; + break; + case 'k': + keepgoing = TRUE; + Var_Append(MAKEFLAGS, "-k", VAR_GLOBAL); + break; + case 'm': + if (argvalue == NULL) goto noarg; + /* look for magic parent directory search string */ + if (strncmp(".../", argvalue, 4) == 0) { + if (!Dir_FindHereOrAbove(curdir, argvalue+4, + found_path, sizeof(found_path))) + break; /* nothing doing */ + (void)Dir_AddDir(sysIncPath, found_path); + } else { + (void)Dir_AddDir(sysIncPath, argvalue); + } + Var_Append(MAKEFLAGS, "-m", VAR_GLOBAL); + Var_Append(MAKEFLAGS, argvalue, VAR_GLOBAL); + break; + case 'n': + noExecute = TRUE; + Var_Append(MAKEFLAGS, "-n", VAR_GLOBAL); + break; + case 'q': + queryFlag = TRUE; + /* Kind of nonsensical, wot? */ + Var_Append(MAKEFLAGS, "-q", VAR_GLOBAL); + break; + case 'r': + noBuiltins = TRUE; + Var_Append(MAKEFLAGS, "-r", VAR_GLOBAL); + break; + case 's': + beSilent = TRUE; + Var_Append(MAKEFLAGS, "-s", VAR_GLOBAL); + break; + case 't': + touchFlag = TRUE; + Var_Append(MAKEFLAGS, "-t", VAR_GLOBAL); + break; + case 'w': + enterFlag = TRUE; + Var_Append(MAKEFLAGS, "-w", VAR_GLOBAL); + break; + case '-': + dashDash = TRUE; + break; + default: + case '?': + usage(); + } + argv += arginc; + argc -= arginc; + } + + oldVars = TRUE; + + /* + * See if the rest of the arguments are variable assignments and + * perform them if so. Else take them to be targets and stuff them + * on the end of the "create" list. + */ + for (; argc > 1; ++argv, --argc) + if (Parse_IsVar(argv[1])) { + Parse_DoVar(argv[1], VAR_CMD); + } else { + if (!*argv[1]) + Punt("illegal (null) argument."); + if (*argv[1] == '-' && !dashDash) + goto rearg; + (void)Lst_AtEnd(create, bmake_strdup(argv[1])); + } + + return; +noarg: + (void)fprintf(stderr, "%s: option requires an argument -- %c\n", + progname, c); + usage(); +} + +/*- + * Main_ParseArgLine -- + * Used by the parse module when a .MFLAGS or .MAKEFLAGS target + * is encountered and by main() when reading the .MAKEFLAGS envariable. + * Takes a line of arguments and breaks it into its + * component words and passes those words and the number of them to the + * MainParseArgs function. + * The line should have all its leading whitespace removed. + * + * Input: + * line Line to fracture + * + * Results: + * None + * + * Side Effects: + * Only those that come from the various arguments. + */ +void +Main_ParseArgLine(const char *line) +{ + char **argv; /* Manufactured argument vector */ + int argc; /* Number of arguments in argv */ + char *args; /* Space used by the args */ + char *buf, *p1; + char *argv0 = Var_Value(".MAKE", VAR_GLOBAL, &p1); + size_t len; + + if (line == NULL) + return; + for (; *line == ' '; ++line) + continue; + if (!*line) + return; + + buf = bmake_malloc(len = strlen(line) + strlen(argv0) + 2); + (void)snprintf(buf, len, "%s %s", argv0, line); + if (p1) + free(p1); + + argv = brk_string(buf, &argc, TRUE, &args); + if (argv == NULL) { + Error("Unterminated quoted string [%s]", buf); + free(buf); + return; + } + free(buf); + MainParseArgs(argc, argv); + + free(args); + free(argv); +} + +Boolean +Main_SetObjdir(const char *path) +{ + struct stat sb; + char *p = NULL; + char buf[MAXPATHLEN + 1]; + Boolean rc = FALSE; + + /* expand variable substitutions */ + if (strchr(path, '$') != 0) { + snprintf(buf, MAXPATHLEN, "%s", path); + path = p = Var_Subst(NULL, buf, VAR_GLOBAL, 0); + } + + if (path[0] != '/') { + snprintf(buf, MAXPATHLEN, "%s/%s", curdir, path); + path = buf; + } + + /* look for the directory and try to chdir there */ + if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) { + if (chdir(path)) { + (void)fprintf(stderr, "make warning: %s: %s.\n", + path, strerror(errno)); + } else { + strncpy(objdir, path, MAXPATHLEN); + Var_Set(".OBJDIR", objdir, VAR_GLOBAL, 0); + setenv("PWD", objdir, 1); + Dir_InitDot(); + rc = TRUE; + } + } + + if (p) + free(p); + return rc; +} + +/*- + * ReadAllMakefiles -- + * wrapper around ReadMakefile() to read all. + * + * Results: + * TRUE if ok, FALSE on error + */ +static int +ReadAllMakefiles(const void *p, const void *q) +{ + return (ReadMakefile(p, q) == 0); +} + +int +str2Lst_Append(Lst lp, char *str, const char *sep) +{ + char *cp; + int n; + + if (!sep) + sep = " \t"; + + for (n = 0, cp = strtok(str, sep); cp; cp = strtok(NULL, sep)) { + (void)Lst_AtEnd(lp, cp); + n++; + } + return (n); +} + +#ifdef SIGINFO +/*ARGSUSED*/ +static void +siginfo(int signo MAKE_ATTR_UNUSED) +{ + char dir[MAXPATHLEN]; + char str[2 * MAXPATHLEN]; + int len; + if (getcwd(dir, sizeof(dir)) == NULL) + return; + len = snprintf(str, sizeof(str), "%s: Working in: %s\n", progname, dir); + if (len > 0) + (void)write(STDERR_FILENO, str, (size_t)len); +} +#endif + +/* + * Allow makefiles some control over the mode we run in. + */ +void +MakeMode(const char *mode) +{ + char *mp = NULL; + + if (!mode) + mode = mp = Var_Subst(NULL, "${" MAKE_MODE ":tl}", VAR_GLOBAL, 0); + + if (mode && *mode) { + if (strstr(mode, "compat")) { + compatMake = TRUE; + forceJobs = FALSE; + } +#if USE_META + if (strstr(mode, "meta")) + meta_mode_init(mode); +#endif + } + if (mp) + free(mp); +} + +/*- + * main -- + * The main function, for obvious reasons. Initializes variables + * and a few modules, then parses the arguments give it in the + * environment and on the command line. Reads the system makefile + * followed by either Makefile, makefile or the file given by the + * -f argument. Sets the .MAKEFLAGS PMake variable based on all the + * flags it has received by then uses either the Make or the Compat + * module to create the initial list of targets. + * + * Results: + * If -q was given, exits -1 if anything was out-of-date. Else it exits + * 0. + * + * Side Effects: + * The program exits when done. Targets are created. etc. etc. etc. + */ +int +main(int argc, char **argv) +{ + Lst targs; /* target nodes to create -- passed to Make_Init */ + Boolean outOfDate = FALSE; /* FALSE if all targets up to date */ + struct stat sb, sa; + char *p1, *path; + char mdpath[MAXPATHLEN]; + const char *machine = getenv("MACHINE"); + const char *machine_arch = getenv("MACHINE_ARCH"); + char *syspath = getenv("MAKESYSPATH"); + Lst sysMkPath; /* Path of sys.mk */ + char *cp = NULL, *start; + /* avoid faults on read-only strings */ + static char defsyspath[] = _PATH_DEFSYSPATH; + char found_path[MAXPATHLEN + 1]; /* for searching for sys.mk */ + struct timeval rightnow; /* to initialize random seed */ + struct utsname utsname; + + /* default to writing debug to stderr */ + debug_file = stderr; + +#ifdef SIGINFO + (void)bmake_signal(SIGINFO, siginfo); +#endif + /* + * Set the seed to produce a different random sequence + * on each program execution. + */ + gettimeofday(&rightnow, NULL); + srandom(rightnow.tv_sec + rightnow.tv_usec); + + if ((progname = strrchr(argv[0], '/')) != NULL) + progname++; + else + progname = argv[0]; +#if defined(MAKE_NATIVE) || (defined(HAVE_SETRLIMIT) && defined(RLIMIT_NOFILE)) + /* + * get rid of resource limit on file descriptors + */ + { + struct rlimit rl; + if (getrlimit(RLIMIT_NOFILE, &rl) != -1 && + rl.rlim_cur != rl.rlim_max) { + rl.rlim_cur = rl.rlim_max; + (void)setrlimit(RLIMIT_NOFILE, &rl); + } + } +#endif + + if (uname(&utsname) == -1) { + (void)fprintf(stderr, "%s: uname failed (%s).\n", progname, + strerror(errno)); + exit(2); + } + + /* + * Get the name of this type of MACHINE from utsname + * so we can share an executable for similar machines. + * (i.e. m68k: amiga hp300, mac68k, sun3, ...) + * + * Note that both MACHINE and MACHINE_ARCH are decided at + * run-time. + */ + if (!machine) { +#ifdef MAKE_NATIVE + machine = utsname.machine; +#else +#ifdef MAKE_MACHINE + machine = MAKE_MACHINE; +#else + machine = "unknown"; +#endif +#endif + } + + if (!machine_arch) { +#ifdef MAKE_NATIVE + static char machine_arch_buf[sizeof(utsname.machine)]; + const int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; + size_t len = sizeof(machine_arch_buf); + + if (sysctl(mib, __arraycount(mib), machine_arch_buf, + &len, NULL, 0) < 0) { + (void)fprintf(stderr, "%s: sysctl failed (%s).\n", progname, + strerror(errno)); + exit(2); + } + + machine_arch = machine_arch_buf; +#else +#ifndef MACHINE_ARCH +#ifdef MAKE_MACHINE_ARCH + machine_arch = MAKE_MACHINE_ARCH; +#else + machine_arch = "unknown"; +#endif +#else + machine_arch = MACHINE_ARCH; +#endif +#endif + } + + myPid = getpid(); /* remember this for vFork() */ + + /* + * Just in case MAKEOBJDIR wants us to do something tricky. + */ + Var_Init(); /* Initialize the lists of variables for + * parsing arguments */ + Var_Set(".MAKE.OS", utsname.sysname, VAR_GLOBAL, 0); + Var_Set("MACHINE", machine, VAR_GLOBAL, 0); + Var_Set("MACHINE_ARCH", machine_arch, VAR_GLOBAL, 0); +#ifdef MAKE_VERSION + Var_Set("MAKE_VERSION", MAKE_VERSION, VAR_GLOBAL, 0); +#endif + Var_Set(".newline", "\n", VAR_GLOBAL, 0); /* handy for :@ loops */ + /* + * This is the traditional preference for makefiles. + */ +#ifndef MAKEFILE_PREFERENCE_LIST +# define MAKEFILE_PREFERENCE_LIST "makefile Makefile" +#endif + Var_Set(MAKEFILE_PREFERENCE, MAKEFILE_PREFERENCE_LIST, + VAR_GLOBAL, 0); + Var_Set(MAKE_DEPENDFILE, ".depend", VAR_GLOBAL, 0); + + create = Lst_Init(FALSE); + makefiles = Lst_Init(FALSE); + printVars = FALSE; + debugVflag = FALSE; + variables = Lst_Init(FALSE); + beSilent = FALSE; /* Print commands as executed */ + ignoreErrors = FALSE; /* Pay attention to non-zero returns */ + noExecute = FALSE; /* Execute all commands */ + noRecursiveExecute = FALSE; /* Execute all .MAKE targets */ + keepgoing = FALSE; /* Stop on error */ + allPrecious = FALSE; /* Remove targets when interrupted */ + queryFlag = FALSE; /* This is not just a check-run */ + noBuiltins = FALSE; /* Read the built-in rules */ + touchFlag = FALSE; /* Actually update targets */ + debug = 0; /* No debug verbosity, please. */ + jobsRunning = FALSE; + + maxJobs = DEFMAXLOCAL; /* Set default local max concurrency */ + maxJobTokens = maxJobs; + compatMake = FALSE; /* No compat mode */ + ignorePWD = FALSE; + + /* + * Initialize the parsing, directory and variable modules to prepare + * for the reading of inclusion paths and variable settings on the + * command line + */ + + /* + * Initialize various variables. + * MAKE also gets this name, for compatibility + * .MAKEFLAGS gets set to the empty string just in case. + * MFLAGS also gets initialized empty, for compatibility. + */ + Parse_Init(); + if (argv[0][0] == '/' || strchr(argv[0], '/') == NULL) { + /* + * Leave alone if it is an absolute path, or if it does + * not contain a '/' in which case we need to find it in + * the path, like execvp(3) and the shells do. + */ + p1 = argv[0]; + } else { + /* + * A relative path, canonicalize it. + */ + p1 = realpath(argv[0], mdpath); + if (!p1 || *p1 != '/' || stat(p1, &sb) < 0) { + p1 = argv[0]; /* realpath failed */ + } + } + Var_Set("MAKE", p1, VAR_GLOBAL, 0); + Var_Set(".MAKE", p1, VAR_GLOBAL, 0); + Var_Set(MAKEFLAGS, "", VAR_GLOBAL, 0); + Var_Set(MAKEOVERRIDES, "", VAR_GLOBAL, 0); + Var_Set("MFLAGS", "", VAR_GLOBAL, 0); + Var_Set(".ALLTARGETS", "", VAR_GLOBAL, 0); + /* some makefiles need to know this */ + Var_Set(MAKE_LEVEL ".ENV", MAKE_LEVEL_ENV, VAR_CMD, 0); + + /* + * Set some other useful macros + */ + { + char tmp[64], *ep; + + makelevel = ((ep = getenv(MAKE_LEVEL_ENV)) && *ep) ? atoi(ep) : 0; + if (makelevel < 0) + makelevel = 0; + snprintf(tmp, sizeof(tmp), "%d", makelevel); + Var_Set(MAKE_LEVEL, tmp, VAR_GLOBAL, 0); + snprintf(tmp, sizeof(tmp), "%u", myPid); + Var_Set(".MAKE.PID", tmp, VAR_GLOBAL, 0); + snprintf(tmp, sizeof(tmp), "%u", getppid()); + Var_Set(".MAKE.PPID", tmp, VAR_GLOBAL, 0); + } + if (makelevel > 0) { + char pn[1024]; + snprintf(pn, sizeof(pn), "%s[%d]", progname, makelevel); + progname = bmake_strdup(pn); + } + +#ifdef USE_META + meta_init(); +#endif + /* + * First snag any flags out of the MAKE environment variable. + * (Note this is *not* MAKEFLAGS since /bin/make uses that and it's + * in a different format). + */ +#ifdef POSIX + p1 = explode(getenv("MAKEFLAGS")); + Main_ParseArgLine(p1); + free(p1); +#else + Main_ParseArgLine(getenv("MAKE")); +#endif + + /* + * Find where we are (now). + * We take care of PWD for the automounter below... + */ + if (getcwd(curdir, MAXPATHLEN) == NULL) { + (void)fprintf(stderr, "%s: getcwd: %s.\n", + progname, strerror(errno)); + exit(2); + } + + MainParseArgs(argc, argv); + + if (enterFlag) + printf("%s: Entering directory `%s'\n", progname, curdir); + + /* + * Verify that cwd is sane. + */ + if (stat(curdir, &sa) == -1) { + (void)fprintf(stderr, "%s: %s: %s.\n", + progname, curdir, strerror(errno)); + exit(2); + } + + /* + * All this code is so that we know where we are when we start up + * on a different machine with pmake. + * Overriding getcwd() with $PWD totally breaks MAKEOBJDIRPREFIX + * since the value of curdir can vary depending on how we got + * here. Ie sitting at a shell prompt (shell that provides $PWD) + * or via subdir.mk in which case its likely a shell which does + * not provide it. + * So, to stop it breaking this case only, we ignore PWD if + * MAKEOBJDIRPREFIX is set or MAKEOBJDIR contains a transform. + */ +#ifndef NO_PWD_OVERRIDE + if (!ignorePWD) { + char *pwd, *ptmp1 = NULL, *ptmp2 = NULL; + + if ((pwd = getenv("PWD")) != NULL && + Var_Value("MAKEOBJDIRPREFIX", VAR_CMD, &ptmp1) == NULL) { + const char *makeobjdir = Var_Value("MAKEOBJDIR", + VAR_CMD, &ptmp2); + + if (makeobjdir == NULL || !strchr(makeobjdir, '$')) { + if (stat(pwd, &sb) == 0 && + sa.st_ino == sb.st_ino && + sa.st_dev == sb.st_dev) + (void)strncpy(curdir, pwd, MAXPATHLEN); + } + } + free(ptmp1); + free(ptmp2); + } +#endif + Var_Set(".CURDIR", curdir, VAR_GLOBAL, 0); + + /* + * Find the .OBJDIR. If MAKEOBJDIRPREFIX, or failing that, + * MAKEOBJDIR is set in the environment, try only that value + * and fall back to .CURDIR if it does not exist. + * + * Otherwise, try _PATH_OBJDIR.MACHINE, _PATH_OBJDIR, and + * finally _PATH_OBJDIRPREFIX`pwd`, in that order. If none + * of these paths exist, just use .CURDIR. + */ + Dir_Init(curdir); + (void)Main_SetObjdir(curdir); + + if ((path = Var_Value("MAKEOBJDIRPREFIX", VAR_CMD, &p1)) != NULL) { + (void)snprintf(mdpath, MAXPATHLEN, "%s%s", path, curdir); + (void)Main_SetObjdir(mdpath); + free(p1); + } else if ((path = Var_Value("MAKEOBJDIR", VAR_CMD, &p1)) != NULL) { + (void)Main_SetObjdir(path); + free(p1); + } else { + (void)snprintf(mdpath, MAXPATHLEN, "%s.%s", _PATH_OBJDIR, machine); + if (!Main_SetObjdir(mdpath) && !Main_SetObjdir(_PATH_OBJDIR)) { + (void)snprintf(mdpath, MAXPATHLEN, "%s%s", + _PATH_OBJDIRPREFIX, curdir); + (void)Main_SetObjdir(mdpath); + } + } + + /* + * Be compatible if user did not specify -j and did not explicitly + * turned compatibility on + */ + if (!compatMake && !forceJobs) { + compatMake = TRUE; + } + + /* + * Initialize archive, target and suffix modules in preparation for + * parsing the makefile(s) + */ + Arch_Init(); + Targ_Init(); + Suff_Init(); + Trace_Init(tracefile); + + DEFAULT = NULL; + (void)time(&now); + + Trace_Log(MAKESTART, NULL); + + /* + * Set up the .TARGETS variable to contain the list of targets to be + * created. If none specified, make the variable empty -- the parser + * will fill the thing in with the default or .MAIN target. + */ + if (!Lst_IsEmpty(create)) { + LstNode ln; + + for (ln = Lst_First(create); ln != NULL; + ln = Lst_Succ(ln)) { + char *name = (char *)Lst_Datum(ln); + + Var_Append(".TARGETS", name, VAR_GLOBAL); + } + } else + Var_Set(".TARGETS", "", VAR_GLOBAL, 0); + + + /* + * If no user-supplied system path was given (through the -m option) + * add the directories from the DEFSYSPATH (more than one may be given + * as dir1:...:dirn) to the system include path. + */ + if (syspath == NULL || *syspath == '\0') + syspath = defsyspath; + else + syspath = bmake_strdup(syspath); + + for (start = syspath; *start != '\0'; start = cp) { + for (cp = start; *cp != '\0' && *cp != ':'; cp++) + continue; + if (*cp == ':') { + *cp++ = '\0'; + } + /* look for magic parent directory search string */ + if (strncmp(".../", start, 4) != 0) { + (void)Dir_AddDir(defIncPath, start); + } else { + if (Dir_FindHereOrAbove(curdir, start+4, + found_path, sizeof(found_path))) { + (void)Dir_AddDir(defIncPath, found_path); + } + } + } + if (syspath != defsyspath) + free(syspath); + + /* + * Read in the built-in rules first, followed by the specified + * makefile, if it was (makefile != NULL), or the default + * makefile and Makefile, in that order, if it wasn't. + */ + if (!noBuiltins) { + LstNode ln; + + sysMkPath = Lst_Init(FALSE); + Dir_Expand(_PATH_DEFSYSMK, + Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath, + sysMkPath); + if (Lst_IsEmpty(sysMkPath)) + Fatal("%s: no system rules (%s).", progname, + _PATH_DEFSYSMK); + ln = Lst_Find(sysMkPath, NULL, ReadMakefile); + if (ln == NULL) + Fatal("%s: cannot open %s.", progname, + (char *)Lst_Datum(ln)); + } + + if (!Lst_IsEmpty(makefiles)) { + LstNode ln; + + ln = Lst_Find(makefiles, NULL, ReadAllMakefiles); + if (ln != NULL) + Fatal("%s: cannot open %s.", progname, + (char *)Lst_Datum(ln)); + } else { + p1 = Var_Subst(NULL, "${" MAKEFILE_PREFERENCE "}", + VAR_CMD, 0); + if (p1) { + (void)str2Lst_Append(makefiles, p1, NULL); + (void)Lst_Find(makefiles, NULL, ReadMakefile); + free(p1); + } + } + + /* In particular suppress .depend for '-r -V .OBJDIR -f /dev/null' */ + if (!noBuiltins || !printVars) { + makeDependfile = Var_Subst(NULL, "${.MAKE.DEPENDFILE:T}", + VAR_CMD, 0); + doing_depend = TRUE; + (void)ReadMakefile(makeDependfile, NULL); + doing_depend = FALSE; + } + + MakeMode(NULL); + + Var_Append("MFLAGS", Var_Value(MAKEFLAGS, VAR_GLOBAL, &p1), VAR_GLOBAL); + if (p1) + free(p1); + + if (!compatMake) + Job_ServerStart(maxJobTokens, jp_0, jp_1); + if (DEBUG(JOB)) + fprintf(debug_file, "job_pipe %d %d, maxjobs %d, tokens %d, compat %d\n", + jp_0, jp_1, maxJobs, maxJobTokens, compatMake); + + Main_ExportMAKEFLAGS(TRUE); /* initial export */ + + + /* + * For compatibility, look at the directories in the VPATH variable + * and add them to the search path, if the variable is defined. The + * variable's value is in the same format as the PATH envariable, i.e. + * <directory>:<directory>:<directory>... + */ + if (Var_Exists("VPATH", VAR_CMD)) { + char *vpath, savec; + /* + * GCC stores string constants in read-only memory, but + * Var_Subst will want to write this thing, so store it + * in an array + */ + static char VPATH[] = "${VPATH}"; + + vpath = Var_Subst(NULL, VPATH, VAR_CMD, FALSE); + path = vpath; + do { + /* skip to end of directory */ + for (cp = path; *cp != ':' && *cp != '\0'; cp++) + continue; + /* Save terminator character so know when to stop */ + savec = *cp; + *cp = '\0'; + /* Add directory to search path */ + (void)Dir_AddDir(dirSearchPath, path); + *cp = savec; + path = cp + 1; + } while (savec == ':'); + free(vpath); + } + + /* + * Now that all search paths have been read for suffixes et al, it's + * time to add the default search path to their lists... + */ + Suff_DoPaths(); + + /* + * Propagate attributes through :: dependency lists. + */ + Targ_Propagate(); + + /* print the initial graph, if the user requested it */ + if (DEBUG(GRAPH1)) + Targ_PrintGraph(1); + + /* print the values of any variables requested by the user */ + if (printVars) { + LstNode ln; + Boolean expandVars; + + if (debugVflag) + expandVars = FALSE; + else + expandVars = getBoolean(".MAKE.EXPAND_VARIABLES", FALSE); + for (ln = Lst_First(variables); ln != NULL; + ln = Lst_Succ(ln)) { + char *var = (char *)Lst_Datum(ln); + char *value; + + if (strchr(var, '$')) { + value = p1 = Var_Subst(NULL, var, VAR_GLOBAL, 0); + } else if (expandVars) { + char tmp[128]; + + if (snprintf(tmp, sizeof(tmp), "${%s}", var) >= (int)(sizeof(tmp))) + Fatal("%s: variable name too big: %s", + progname, var); + value = p1 = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + } else { + value = Var_Value(var, VAR_GLOBAL, &p1); + } + printf("%s\n", value ? value : ""); + if (p1) + free(p1); + } + } else { + /* + * Have now read the entire graph and need to make a list of + * targets to create. If none was given on the command line, + * we consult the parsing module to find the main target(s) + * to create. + */ + if (Lst_IsEmpty(create)) + targs = Parse_MainName(); + else + targs = Targ_FindList(create, TARG_CREATE); + + if (!compatMake) { + /* + * Initialize job module before traversing the graph + * now that any .BEGIN and .END targets have been read. + * This is done only if the -q flag wasn't given + * (to prevent the .BEGIN from being executed should + * it exist). + */ + if (!queryFlag) { + Job_Init(); + jobsRunning = TRUE; + } + + /* Traverse the graph, checking on all the targets */ + outOfDate = Make_Run(targs); + } else { + /* + * Compat_Init will take care of creating all the + * targets as well as initializing the module. + */ + Compat_Run(targs); + } + } + +#ifdef CLEANUP + Lst_Destroy(targs, NULL); + Lst_Destroy(variables, NULL); + Lst_Destroy(makefiles, NULL); + Lst_Destroy(create, (FreeProc *)free); +#endif + + /* print the graph now it's been processed if the user requested it */ + if (DEBUG(GRAPH2)) + Targ_PrintGraph(2); + + Trace_Log(MAKEEND, 0); + + if (enterFlag) + printf("%s: Leaving directory `%s'\n", progname, curdir); + + Suff_End(); + Targ_End(); + Arch_End(); + Var_End(); + Parse_End(); + Dir_End(); + Job_End(); + Trace_End(); + + return outOfDate ? 1 : 0; +} + +/*- + * ReadMakefile -- + * Open and parse the given makefile. + * + * Results: + * 0 if ok. -1 if couldn't open file. + * + * Side Effects: + * lots + */ +static int +ReadMakefile(const void *p, const void *q MAKE_ATTR_UNUSED) +{ + const char *fname = p; /* makefile to read */ + int fd; + size_t len = MAXPATHLEN; + char *name, *path = bmake_malloc(len); + + if (!strcmp(fname, "-")) { + Parse_File(NULL /*stdin*/, -1); + Var_Set("MAKEFILE", "", VAR_INTERNAL, 0); + } else { + /* if we've chdir'd, rebuild the path name */ + if (strcmp(curdir, objdir) && *fname != '/') { + size_t plen = strlen(curdir) + strlen(fname) + 2; + if (len < plen) + path = bmake_realloc(path, len = 2 * plen); + + (void)snprintf(path, len, "%s/%s", curdir, fname); + fd = open(path, O_RDONLY); + if (fd != -1) { + fname = path; + goto found; + } + + /* If curdir failed, try objdir (ala .depend) */ + plen = strlen(objdir) + strlen(fname) + 2; + if (len < plen) + path = bmake_realloc(path, len = 2 * plen); + (void)snprintf(path, len, "%s/%s", objdir, fname); + fd = open(path, O_RDONLY); + if (fd != -1) { + fname = path; + goto found; + } + } else { + fd = open(fname, O_RDONLY); + if (fd != -1) + goto found; + } + /* look in -I and system include directories. */ + name = Dir_FindFile(fname, parseIncPath); + if (!name) + name = Dir_FindFile(fname, + Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); + if (!name || (fd = open(name, O_RDONLY)) == -1) { + if (name) + free(name); + free(path); + return(-1); + } + fname = name; + /* + * set the MAKEFILE variable desired by System V fans -- the + * placement of the setting here means it gets set to the last + * makefile specified, as it is set by SysV make. + */ +found: + if (!doing_depend) + Var_Set("MAKEFILE", fname, VAR_INTERNAL, 0); + Parse_File(fname, fd); + } + free(path); + return(0); +} + + + +/*- + * Cmd_Exec -- + * Execute the command in cmd, and return the output of that command + * in a string. + * + * Results: + * A string containing the output of the command, or the empty string + * If errnum is not NULL, it contains the reason for the command failure + * + * Side Effects: + * The string must be freed by the caller. + */ +char * +Cmd_Exec(const char *cmd, const char **errnum) +{ + const char *args[4]; /* Args for invoking the shell */ + int fds[2]; /* Pipe streams */ + int cpid; /* Child PID */ + int pid; /* PID from wait() */ + char *res; /* result */ + int status; /* command exit status */ + Buffer buf; /* buffer to store the result */ + char *cp; + int cc; /* bytes read, or -1 */ + int savederr; /* saved errno */ + + + *errnum = NULL; + + if (!shellName) + Shell_Init(); + /* + * Set up arguments for shell + */ + args[0] = shellName; + args[1] = "-c"; + args[2] = cmd; + args[3] = NULL; + + /* + * Open a pipe for fetching its output + */ + if (pipe(fds) == -1) { + *errnum = "Couldn't create pipe for \"%s\""; + goto bad; + } + + /* + * Fork + */ + switch (cpid = vFork()) { + case 0: + /* + * Close input side of pipe + */ + (void)close(fds[0]); + + /* + * Duplicate the output stream to the shell's output, then + * shut the extra thing down. Note we don't fetch the error + * stream...why not? Why? + */ + (void)dup2(fds[1], 1); + (void)close(fds[1]); + + Var_ExportVars(); + + (void)execv(shellPath, UNCONST(args)); + _exit(1); + /*NOTREACHED*/ + + case -1: + *errnum = "Couldn't exec \"%s\""; + goto bad; + + default: + /* + * No need for the writing half + */ + (void)close(fds[1]); + + savederr = 0; + Buf_Init(&buf, 0); + + do { + char result[BUFSIZ]; + cc = read(fds[0], result, sizeof(result)); + if (cc > 0) + Buf_AddBytes(&buf, cc, result); + } + while (cc > 0 || (cc == -1 && errno == EINTR)); + if (cc == -1) + savederr = errno; + + /* + * Close the input side of the pipe. + */ + (void)close(fds[0]); + + /* + * Wait for the process to exit. + */ + while(((pid = waitpid(cpid, &status, 0)) != cpid) && (pid >= 0)) { + JobReapChild(pid, status, FALSE); + continue; + } + cc = Buf_Size(&buf); + res = Buf_Destroy(&buf, FALSE); + + if (savederr != 0) + *errnum = "Couldn't read shell's output for \"%s\""; + + if (WIFSIGNALED(status)) + *errnum = "\"%s\" exited on a signal"; + else if (WEXITSTATUS(status) != 0) + *errnum = "\"%s\" returned non-zero status"; + + /* + * Null-terminate the result, convert newlines to spaces and + * install it in the variable. + */ + res[cc] = '\0'; + cp = &res[cc]; + + if (cc > 0 && *--cp == '\n') { + /* + * A final newline is just stripped + */ + *cp-- = '\0'; + } + while (cp >= res) { + if (*cp == '\n') { + *cp = ' '; + } + cp--; + } + break; + } + return res; +bad: + res = bmake_malloc(1); + *res = '\0'; + return res; +} + +/*- + * Error -- + * Print an error message given its format. + * + * Results: + * None. + * + * Side Effects: + * The message is printed. + */ +/* VARARGS */ +void +Error(const char *fmt, ...) +{ + va_list ap; + FILE *err_file; + + err_file = debug_file; + if (err_file == stdout) + err_file = stderr; + (void)fflush(stdout); + for (;;) { + va_start(ap, fmt); + fprintf(err_file, "%s: ", progname); + (void)vfprintf(err_file, fmt, ap); + va_end(ap); + (void)fprintf(err_file, "\n"); + (void)fflush(err_file); + if (err_file == stderr) + break; + err_file = stderr; + } +} + +/*- + * Fatal -- + * Produce a Fatal error message. If jobs are running, waits for them + * to finish. + * + * Results: + * None + * + * Side Effects: + * The program exits + */ +/* VARARGS */ +void +Fatal(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (jobsRunning) + Job_Wait(); + + (void)fflush(stdout); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + + PrintOnError(NULL, NULL); + + if (DEBUG(GRAPH2) || DEBUG(GRAPH3)) + Targ_PrintGraph(2); + Trace_Log(MAKEERROR, 0); + exit(2); /* Not 1 so -q can distinguish error */ +} + +/* + * Punt -- + * Major exception once jobs are being created. Kills all jobs, prints + * a message and exits. + * + * Results: + * None + * + * Side Effects: + * All children are killed indiscriminately and the program Lib_Exits + */ +/* VARARGS */ +void +Punt(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)fflush(stdout); + (void)fprintf(stderr, "%s: ", progname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + + PrintOnError(NULL, NULL); + + DieHorribly(); +} + +/*- + * DieHorribly -- + * Exit without giving a message. + * + * Results: + * None + * + * Side Effects: + * A big one... + */ +void +DieHorribly(void) +{ + if (jobsRunning) + Job_AbortAll(); + if (DEBUG(GRAPH2)) + Targ_PrintGraph(2); + Trace_Log(MAKEERROR, 0); + exit(2); /* Not 1, so -q can distinguish error */ +} + +/* + * Finish -- + * Called when aborting due to errors in child shell to signal + * abnormal exit. + * + * Results: + * None + * + * Side Effects: + * The program exits + */ +void +Finish(int errors) + /* number of errors encountered in Make_Make */ +{ + Fatal("%d error%s", errors, errors == 1 ? "" : "s"); +} + +/* + * eunlink -- + * Remove a file carefully, avoiding directories. + */ +int +eunlink(const char *file) +{ + struct stat st; + + if (lstat(file, &st) == -1) + return -1; + + if (S_ISDIR(st.st_mode)) { + errno = EISDIR; + return -1; + } + return unlink(file); +} + +/* + * execError -- + * Print why exec failed, avoiding stdio. + */ +void +execError(const char *af, const char *av) +{ +#ifdef USE_IOVEC + int i = 0; + struct iovec iov[8]; +#define IOADD(s) \ + (void)(iov[i].iov_base = UNCONST(s), \ + iov[i].iov_len = strlen(iov[i].iov_base), \ + i++) +#else +#define IOADD(void)write(2, s, strlen(s)) +#endif + + IOADD(progname); + IOADD(": "); + IOADD(af); + IOADD("("); + IOADD(av); + IOADD(") failed ("); + IOADD(strerror(errno)); + IOADD(")\n"); + +#ifdef USE_IOVEC + while (writev(2, iov, 8) == -1 && errno == EAGAIN) + continue; +#endif +} + +/* + * usage -- + * exit with usage message + */ +static void +usage(void) +{ + char *p; + if ((p = strchr(progname, '[')) != NULL) + *p = '\0'; + + (void)fprintf(stderr, +"usage: %s [-BeikNnqrstWwX] \n\ + [-C directory] [-D variable] [-d flags] [-f makefile]\n\ + [-I directory] [-J private] [-j max_jobs] [-m directory] [-T file]\n\ + [-V variable] [variable=value] [target ...]\n", progname); + exit(2); +} + + +int +PrintAddr(void *a, void *b) +{ + printf("%lx ", (unsigned long) a); + return b ? 0 : 0; +} + + + +void +PrintOnError(GNode *gn, const char *s) +{ + static GNode *en = NULL; + char tmp[64]; + char *cp; + + if (s) + printf("%s", s); + + printf("\n%s: stopped in %s\n", progname, curdir); + + if (en) + return; /* we've been here! */ + if (gn) { + /* + * We can print this even if there is no .ERROR target. + */ + Var_Set(".ERROR_TARGET", gn->name, VAR_GLOBAL, 0); + } + strncpy(tmp, "${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'\n@}", + sizeof(tmp) - 1); + cp = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + if (cp) { + if (*cp) + printf("%s", cp); + free(cp); + } + /* + * Finally, see if there is a .ERROR target, and run it if so. + */ + en = Targ_FindNode(".ERROR", TARG_NOCREATE); + if (en) { + en->type |= OP_SPECIAL; + Compat_Make(en, en); + } +} + +void +Main_ExportMAKEFLAGS(Boolean first) +{ + static int once = 1; + char tmp[64]; + char *s; + + if (once != first) + return; + once = 0; + + strncpy(tmp, "${.MAKEFLAGS} ${.MAKEOVERRIDES:O:u:@v@$v=${$v:Q}@}", + sizeof(tmp)); + s = Var_Subst(NULL, tmp, VAR_CMD, 0); + if (s && *s) { +#ifdef POSIX + setenv("MAKEFLAGS", s, 1); +#else + setenv("MAKE", s, 1); +#endif + } +} + +char * +getTmpdir(void) +{ + static char *tmpdir = NULL; + + if (!tmpdir) { + struct stat st; + + /* + * Honor $TMPDIR but only if it is valid. + * Ensure it ends with /. + */ + tmpdir = Var_Subst(NULL, "${TMPDIR:tA:U" _PATH_TMP "}/", VAR_GLOBAL, 0); + if (stat(tmpdir, &st) < 0 || !S_ISDIR(st.st_mode)) { + free(tmpdir); + tmpdir = bmake_strdup(_PATH_TMP); + } + } + return tmpdir; +} + +/* + * Create and open a temp file using "pattern". + * If "fnamep" is provided set it to a copy of the filename created. + * Otherwise unlink the file once open. + */ +int +mkTempFile(const char *pattern, char **fnamep) +{ + static char *tmpdir = NULL; + char tfile[MAXPATHLEN]; + int fd; + + if (!pattern) + pattern = TMPPAT; + if (!tmpdir) + tmpdir = getTmpdir(); + if (pattern[0] == '/') { + snprintf(tfile, sizeof(tfile), "%s", pattern); + } else { + snprintf(tfile, sizeof(tfile), "%s%s", tmpdir, pattern); + } + if ((fd = mkstemp(tfile)) < 0) + Punt("Could not create temporary file %s: %s", tfile, strerror(errno)); + if (fnamep) { + *fnamep = bmake_strdup(tfile); + } else { + unlink(tfile); /* we just want the descriptor */ + } + return fd; +} + + +/* + * Return a Boolean based on setting of a knob. + * + * If the knob is not set, the supplied default is the return value. + * If set, anything that looks or smells like "No", "False", "Off", "0" etc, + * is FALSE, otherwise TRUE. + */ +Boolean +getBoolean(const char *name, Boolean bf) +{ + char tmp[64]; + char *cp; + + if (snprintf(tmp, sizeof(tmp), "${%s:tl}", name) < (int)(sizeof(tmp))) { + cp = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + + if (cp) { + switch(*cp) { + case '\0': /* not set - the default wins */ + break; + case '0': + case 'f': + case 'n': + bf = FALSE; + break; + case 'o': + switch (cp[1]) { + case 'f': + bf = FALSE; + break; + default: + bf = TRUE; + break; + } + break; + default: + bf = TRUE; + break; + } + free(cp); + } + } + return (bf); +} diff --git a/buildrump.sh/src/usr.bin/make/make.1 b/buildrump.sh/src/usr.bin/make/make.1 new file mode 100644 index 00000000..fae034b9 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/make.1 @@ -0,0 +1,2274 @@ +.\" $NetBSD: make.1,v 1.247 2015/04/10 08:43:32 wiz Exp $ +.\" +.\" Copyright (c) 1990, 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. +.\" +.\" from: @(#)make.1 8.4 (Berkeley) 3/19/94 +.\" +.Dd April 9, 2015 +.Dt MAKE 1 +.Os +.Sh NAME +.Nm make +.Nd maintain program dependencies +.Sh SYNOPSIS +.Nm +.Op Fl BeikNnqrstWwX +.Op Fl C Ar directory +.Op Fl D Ar variable +.Op Fl d Ar flags +.Op Fl f Ar makefile +.Op Fl I Ar directory +.Op Fl J Ar private +.Op Fl j Ar max_jobs +.Op Fl m Ar directory +.Op Fl T Ar file +.Op Fl V Ar variable +.Op Ar variable=value +.Op Ar target ... +.Sh DESCRIPTION +.Nm +is a program designed to simplify the maintenance of other programs. +Its input is a list of specifications as to the files upon which programs +and other files depend. +If no +.Fl f Ar makefile +makefile option is given, +.Nm +will try to open +.Ql Pa makefile +then +.Ql Pa Makefile +in order to find the specifications. +If the file +.Ql Pa .depend +exists, it is read (see +.Xr mkdep 1 ) . +.Pp +This manual page is intended as a reference document only. +For a more thorough description of +.Nm +and makefiles, please refer to +.%T "PMake \- A Tutorial" . +.Pp +.Nm +will prepend the contents of the +.Va MAKEFLAGS +environment variable to the command line arguments before parsing them. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl B +Try to be backwards compatible by executing a single shell per command and +by executing the commands to make the sources of a dependency line in sequence. +.It Fl C Ar directory +Change to +.Ar directory +before reading the makefiles or doing anything else. +If multiple +.Fl C +options are specified, each is interpreted relative to the previous one: +.Fl C Pa / Fl C Pa etc +is equivalent to +.Fl C Pa /etc . +.It Fl D Ar variable +Define +.Ar variable +to be 1, in the global context. +.It Fl d Ar [-]flags +Turn on debugging, and specify which portions of +.Nm +are to print debugging information. +Unless the flags are preceded by +.Ql \- +they are added to the +.Va MAKEFLAGS +environment variable and will be processed by any child make processes. +By default, debugging information is printed to standard error, +but this can be changed using the +.Ar F +debugging flag. +The debugging output is always unbuffered; in addition, if debugging +is enabled but debugging output is not directed to standard output, +then the standard output is line buffered. +.Ar Flags +is one or more of the following: +.Bl -tag -width Ds +.It Ar A +Print all possible debugging information; +equivalent to specifying all of the debugging flags. +.It Ar a +Print debugging information about archive searching and caching. +.It Ar C +Print debugging information about current working directory. +.It Ar c +Print debugging information about conditional evaluation. +.It Ar d +Print debugging information about directory searching and caching. +.It Ar e +Print debugging information about failed commands and targets. +.It Ar F Ns Oo Sy \&+ Oc Ns Ar filename +Specify where debugging output is written. +This must be the last flag, because it consumes the remainder of +the argument. +If the character immediately after the +.Ql F +flag is +.Ql \&+ , +then the file will be opened in append mode; +otherwise the file will be overwritten. +If the file name is +.Ql stdout +or +.Ql stderr +then debugging output will be written to the +standard output or standard error output file descriptors respectively +(and the +.Ql \&+ +option has no effect). +Otherwise, the output will be written to the named file. +If the file name ends +.Ql .%d +then the +.Ql %d +is replaced by the pid. +.It Ar f +Print debugging information about loop evaluation. +.It Ar "g1" +Print the input graph before making anything. +.It Ar "g2" +Print the input graph after making everything, or before exiting +on error. +.It Ar "g3" +Print the input graph before exiting on error. +.It Ar j +Print debugging information about running multiple shells. +.It Ar l +Print commands in Makefiles regardless of whether or not they are prefixed by +.Ql @ +or other "quiet" flags. +Also known as "loud" behavior. +.It Ar M +Print debugging information about "meta" mode decisions about targets. +.It Ar m +Print debugging information about making targets, including modification +dates. +.It Ar n +Don't delete the temporary command scripts created when running commands. +These temporary scripts are created in the directory +referred to by the +.Ev TMPDIR +environment variable, or in +.Pa /tmp +if +.Ev TMPDIR +is unset or set to the empty string. +The temporary scripts are created by +.Xr mkstemp 3 , +and have names of the form +.Pa makeXXXXXX . +.Em NOTE : +This can create many files in +.Ev TMPDIR +or +.Pa /tmp , +so use with care. +.It Ar p +Print debugging information about makefile parsing. +.It Ar s +Print debugging information about suffix-transformation rules. +.It Ar t +Print debugging information about target list maintenance. +.It Ar V +Force the +.Fl V +option to print raw values of variables. +.It Ar v +Print debugging information about variable assignment. +.It Ar x +Run shell commands with +.Fl x +so the actual commands are printed as they are executed. +.El +.It Fl e +Specify that environment variables override macro assignments within +makefiles. +.It Fl f Ar makefile +Specify a makefile to read instead of the default +.Ql Pa makefile . +If +.Ar makefile +is +.Ql Fl , +standard input is read. +Multiple makefiles may be specified, and are read in the order specified. +.It Fl I Ar directory +Specify a directory in which to search for makefiles and included makefiles. +The system makefile directory (or directories, see the +.Fl m +option) is automatically included as part of this list. +.It Fl i +Ignore non-zero exit of shell commands in the makefile. +Equivalent to specifying +.Ql Fl +before each command line in the makefile. +.It Fl J Ar private +This option should +.Em not +be specified by the user. +.Pp +When the +.Ar j +option is in use in a recursive build, this option is passed by a make +to child makes to allow all the make processes in the build to +cooperate to avoid overloading the system. +.It Fl j Ar max_jobs +Specify the maximum number of jobs that +.Nm +may have running at any one time. +The value is saved in +.Va .MAKE.JOBS . +Turns compatibility mode off, unless the +.Ar B +flag is also specified. +When compatibility mode is off, all commands associated with a +target are executed in a single shell invocation as opposed to the +traditional one shell invocation per line. +This can break traditional scripts which change directories on each +command invocation and then expect to start with a fresh environment +on the next line. +It is more efficient to correct the scripts rather than turn backwards +compatibility on. +.It Fl k +Continue processing after errors are encountered, but only on those targets +that do not depend on the target whose creation caused the error. +.It Fl m Ar directory +Specify a directory in which to search for sys.mk and makefiles included +via the +.Ao Ar file Ac Ns -style +include statement. +The +.Fl m +option can be used multiple times to form a search path. +This path will override the default system include path: /usr/share/mk. +Furthermore the system include path will be appended to the search path used +for +.Qo Ar file Qc Ns -style +include statements (see the +.Fl I +option). +.Pp +If a file or directory name in the +.Fl m +argument (or the +.Ev MAKESYSPATH +environment variable) starts with the string +.Qq \&.../ +then +.Nm +will search for the specified file or directory named in the remaining part +of the argument string. +The search starts with the current directory of +the Makefile and then works upward towards the root of the filesystem. +If the search is successful, then the resulting directory replaces the +.Qq \&.../ +specification in the +.Fl m +argument. +If used, this feature allows +.Nm +to easily search in the current source tree for customized sys.mk files +(e.g., by using +.Qq \&.../mk/sys.mk +as an argument). +.It Fl n +Display the commands that would have been executed, but do not +actually execute them unless the target depends on the .MAKE special +source (see below). +.It Fl N +Display the commands which would have been executed, but do not +actually execute any of them; useful for debugging top-level makefiles +without descending into subdirectories. +.It Fl q +Do not execute any commands, but exit 0 if the specified targets are +up-to-date and 1, otherwise. +.It Fl r +Do not use the built-in rules specified in the system makefile. +.It Fl s +Do not echo any commands as they are executed. +Equivalent to specifying +.Ql Ic @ +before each command line in the makefile. +.It Fl T Ar tracefile +When used with the +.Fl j +flag, +append a trace record to +.Ar tracefile +for each job started and completed. +.It Fl t +Rather than re-building a target as specified in the makefile, create it +or update its modification time to make it appear up-to-date. +.It Fl V Ar variable +Print +.Nm Ns 's +idea of the value of +.Ar variable , +in the global context. +Do not build any targets. +Multiple instances of this option may be specified; +the variables will be printed one per line, +with a blank line for each null or undefined variable. +If +.Ar variable +contains a +.Ql \&$ +then the value will be expanded before printing. +.It Fl W +Treat any warnings during makefile parsing as errors. +.It Fl w +Print entering and leaving directory messages, pre and post processing. +.It Fl X +Don't export variables passed on the command line to the environment +individually. +Variables passed on the command line are still exported +via the +.Va MAKEFLAGS +environment variable. +This option may be useful on systems which have a small limit on the +size of command arguments. +.It Ar variable=value +Set the value of the variable +.Ar variable +to +.Ar value . +Normally, all values passed on the command line are also exported to +sub-makes in the environment. +The +.Fl X +flag disables this behavior. +Variable assignments should follow options for POSIX compatibility +but no ordering is enforced. +.El +.Pp +There are seven different types of lines in a makefile: file dependency +specifications, shell commands, variable assignments, include statements, +conditional directives, for loops, and comments. +.Pp +In general, lines may be continued from one line to the next by ending +them with a backslash +.Pq Ql \e . +The trailing newline character and initial whitespace on the following +line are compressed into a single space. +.Sh FILE DEPENDENCY SPECIFICATIONS +Dependency lines consist of one or more targets, an operator, and zero +or more sources. +This creates a relationship where the targets +.Dq depend +on the sources +and are usually created from them. +The exact relationship between the target and the source is determined +by the operator that separates them. +The three operators are as follows: +.Bl -tag -width flag +.It Ic \&: +A target is considered out-of-date if its modification time is less than +those of any of its sources. +Sources for a target accumulate over dependency lines when this operator +is used. +The target is removed if +.Nm +is interrupted. +.It Ic \&! +Targets are always re-created, but not until all sources have been +examined and re-created as necessary. +Sources for a target accumulate over dependency lines when this operator +is used. +The target is removed if +.Nm +is interrupted. +.It Ic \&:: +If no sources are specified, the target is always re-created. +Otherwise, a target is considered out-of-date if any of its sources has +been modified more recently than the target. +Sources for a target do not accumulate over dependency lines when this +operator is used. +The target will not be removed if +.Nm +is interrupted. +.El +.Pp +Targets and sources may contain the shell wildcard values +.Ql \&? , +.Ql * , +.Ql [] , +and +.Ql {} . +The values +.Ql \&? , +.Ql * , +and +.Ql [] +may only be used as part of the final +component of the target or source, and must be used to describe existing +files. +The value +.Ql {} +need not necessarily be used to describe existing files. +Expansion is in directory order, not alphabetically as done in the shell. +.Sh SHELL COMMANDS +Each target may have associated with it one or more lines of shell +commands, normally +used to create the target. +Each of the lines in this script +.Em must +be preceded by a tab. +(For historical reasons, spaces are not accepted.) +While targets can appear in many dependency lines if desired, by +default only one of these rules may be followed by a creation +script. +If the +.Ql Ic \&:: +operator is used, however, all rules may include scripts and the +scripts are executed in the order found. +.Pp +Each line is treated as a separate shell command, unless the end of +line is escaped with a backslash +.Pq Ql \e +in which case that line and the next are combined. +.\" The escaped newline is retained and passed to the shell, which +.\" normally ignores it. +.\" However, the tab at the beginning of the following line is removed. +If the first characters of the command are any combination of +.Ql Ic @ , +.Ql Ic + , +or +.Ql Ic \- , +the command is treated specially. +A +.Ql Ic @ +causes the command not to be echoed before it is executed. +A +.Ql Ic + +causes the command to be executed even when +.Fl n +is given. +This is similar to the effect of the .MAKE special source, +except that the effect can be limited to a single line of a script. +A +.Ql Ic \- +in compatibility mode +causes any non-zero exit status of the command line to be ignored. +.Pp +When +.Nm +is run in jobs mode with +.Fl j Ar max_jobs , +the entire script for the target is fed to a +single instance of the shell. +In compatibility (non-jobs) mode, each command is run in a separate process. +If the command contains any shell meta characters +.Pq Ql #=|^(){};&<>*?[]:$`\e\en +it will be passed to the shell; otherwise +.Nm +will attempt direct execution. +If a line starts with +.Ql Ic \- +and the shell has ErrCtl enabled then failure of the command line +will be ignored as in compatibility mode. +Otherwise +.Ql Ic \- +affects the entire job; +the script will stop at the first command line that fails, +but the target will not be deemed to have failed. +.Pp +Makefiles should be written so that the mode of +.Nm +operation does not change their behavior. +For example, any command which needs to use +.Dq cd +or +.Dq chdir +without potentially changing the directory for subsequent commands +should be put in parentheses so it executes in a subshell. +To force the use of one shell, escape the line breaks so as to make +the whole script one command. +For example: +.Bd -literal -offset indent +avoid-chdir-side-effects: + @echo Building $@ in `pwd` + @(cd ${.CURDIR} && ${MAKE} $@) + @echo Back in `pwd` + +ensure-one-shell-regardless-of-mode: + @echo Building $@ in `pwd`; \e + (cd ${.CURDIR} && ${MAKE} $@); \e + echo Back in `pwd` +.Ed +.Pp +Since +.Nm +will +.Xr chdir 2 +to +.Ql Va .OBJDIR +before executing any targets, each child process +starts with that as its current working directory. +.Sh VARIABLE ASSIGNMENTS +Variables in make are much like variables in the shell, and, by tradition, +consist of all upper-case letters. +.Ss Variable assignment modifiers +The five operators that can be used to assign values to variables are as +follows: +.Bl -tag -width Ds +.It Ic \&= +Assign the value to the variable. +Any previous value is overridden. +.It Ic \&+= +Append the value to the current value of the variable. +.It Ic \&?= +Assign the value to the variable if it is not already defined. +.It Ic \&:= +Assign with expansion, i.e. expand the value before assigning it +to the variable. +Normally, expansion is not done until the variable is referenced. +.Em NOTE : +References to undefined variables are +.Em not +expanded. +This can cause problems when variable modifiers are used. +.It Ic \&!= +Expand the value and pass it to the shell for execution and assign +the result to the variable. +Any newlines in the result are replaced with spaces. +.El +.Pp +Any white-space before the assigned +.Ar value +is removed; if the value is being appended, a single space is inserted +between the previous contents of the variable and the appended value. +.Pp +Variables are expanded by surrounding the variable name with either +curly braces +.Pq Ql {} +or parentheses +.Pq Ql () +and preceding it with +a dollar sign +.Pq Ql \&$ . +If the variable name contains only a single letter, the surrounding +braces or parentheses are not required. +This shorter form is not recommended. +.Pp +If the variable name contains a dollar, then the name itself is expanded first. +This allows almost arbitrary variable names, however names containing dollar, +braces, parenthesis, or whitespace are really best avoided! +.Pp +If the result of expanding a variable contains a dollar sign +.Pq Ql \&$ +the string is expanded again. +.Pp +Variable substitution occurs at three distinct times, depending on where +the variable is being used. +.Bl -enum +.It +Variables in dependency lines are expanded as the line is read. +.It +Variables in shell commands are expanded when the shell command is +executed. +.It +.Dq .for +loop index variables are expanded on each loop iteration. +Note that other variables are not expanded inside loops so +the following example code: +.Bd -literal -offset indent + +.Dv .for i in 1 2 3 +a+= ${i} +j= ${i} +b+= ${j} +.Dv .endfor + +all: + @echo ${a} + @echo ${b} + +.Ed +will print: +.Bd -literal -offset indent +1 2 3 +3 3 3 + +.Ed +Because while ${a} contains +.Dq 1 2 3 +after the loop is executed, ${b} +contains +.Dq ${j} ${j} ${j} +which expands to +.Dq 3 3 3 +since after the loop completes ${j} contains +.Dq 3 . +.El +.Ss Variable classes +The four different classes of variables (in order of increasing precedence) +are: +.Bl -tag -width Ds +.It Environment variables +Variables defined as part of +.Nm Ns 's +environment. +.It Global variables +Variables defined in the makefile or in included makefiles. +.It Command line variables +Variables defined as part of the command line. +.It Local variables +Variables that are defined specific to a certain target. +.El +.Pp +Local variables are all built in and their values vary magically from +target to target. +It is not currently possible to define new local variables. +The seven local variables are as follows: +.Bl -tag -width ".ARCHIVE" -offset indent +.It Va .ALLSRC +The list of all sources for this target; also known as +.Ql Va \&\*[Gt] . +.It Va .ARCHIVE +The name of the archive file; also known as +.Ql Va \&! . +.It Va .IMPSRC +In suffix-transformation rules, the name/path of the source from which the +target is to be transformed (the +.Dq implied +source); also known as +.Ql Va \&\*[Lt] . +It is not defined in explicit rules. +.It Va .MEMBER +The name of the archive member; also known as +.Ql Va % . +.It Va .OODATE +The list of sources for this target that were deemed out-of-date; also +known as +.Ql Va \&? . +.It Va .PREFIX +The file prefix of the target, containing only the file portion, no suffix +or preceding directory components; also known as +.Ql Va * . +The suffix must be one of the known suffixes declared with +.Ic .SUFFIXES +or it will not be recognized. +.It Va .TARGET +The name of the target; also known as +.Ql Va @ . +.El +.Pp +The shorter forms +.Ql ( Va \*[Gt] , +.Ql Va \&! , +.Ql Va \*[Lt] , +.Ql Va % , +.Ql Va \&? , +.Ql Va * , +and +.Ql Va @ ) +are permitted for backward +compatibility with historical makefiles and legacy POSIX make and are +not recommended. +.Pp +Variants of these variables with the punctuation followed immediately by +.Ql D +or +.Ql F , +e.g. +.Ql Va $(@D) , +are legacy forms equivalent to using the +.Ql :H +and +.Ql :T +modifiers. +These forms are accepted for compatibility with +.At V +makefiles and POSIX but are not recommended. +.Pp +Four of the local variables may be used in sources on dependency lines +because they expand to the proper value for each target on the line. +These variables are +.Ql Va .TARGET , +.Ql Va .PREFIX , +.Ql Va .ARCHIVE , +and +.Ql Va .MEMBER . +.Ss Additional built-in variables +In addition, +.Nm +sets or knows about the following variables: +.Bl -tag -width .MAKEOVERRIDES +.It Va \&$ +A single dollar sign +.Ql \&$ , +i.e. +.Ql \&$$ +expands to a single dollar +sign. +.It Va .ALLTARGETS +The list of all targets encountered in the Makefile. +If evaluated during +Makefile parsing, lists only those targets encountered thus far. +.It Va .CURDIR +A path to the directory where +.Nm +was executed. +Refer to the description of +.Ql Ev PWD +for more details. +.It Va .INCLUDEDFROMDIR +The directory of the file this Makefile was included from. +.It Va .INCLUDEDFROMFILE +The filename of the file this Makefile was included from. +.It Ev MAKE +The name that +.Nm +was executed with +.Pq Va argv[0] . +For compatibility +.Nm +also sets +.Va .MAKE +with the same value. +The preferred variable to use is the environment variable +.Ev MAKE +because it is more compatible with other versions of +.Nm +and cannot be confused with the special target with the same name. +.It Va .MAKE.DEPENDFILE +Names the makefile (default +.Ql Pa .depend ) +from which generated dependencies are read. +.It Va .MAKE.EXPAND_VARIABLES +A boolean that controls the default behavior of the +.Fl V +option. +.It Va .MAKE.EXPORTED +The list of variables exported by +.Nm . +.It Va .MAKE.JOBS +The argument to the +.Fl j +option. +.It Va .MAKE.JOB.PREFIX +If +.Nm +is run with +.Ar j +then output for each target is prefixed with a token +.Ql --- target --- +the first part of which can be controlled via +.Va .MAKE.JOB.PREFIX . +If +.Va .MAKE.JOB.PREFIX +is empty, no token is printed. +.br +For example: +.Li .MAKE.JOB.PREFIX=${.newline}---${.MAKE:T}[${.MAKE.PID}] +would produce tokens like +.Ql ---make[1234] target --- +making it easier to track the degree of parallelism being achieved. +.It Ev MAKEFLAGS +The environment variable +.Ql Ev MAKEFLAGS +may contain anything that +may be specified on +.Nm Ns 's +command line. +Anything specified on +.Nm Ns 's +command line is appended to the +.Ql Ev MAKEFLAGS +variable which is then +entered into the environment for all programs which +.Nm +executes. +.It Va .MAKE.LEVEL +The recursion depth of +.Nm . +The initial instance of +.Nm +will be 0, and an incremented value is put into the environment +to be seen by the next generation. +This allows tests like: +.Li .if ${.MAKE.LEVEL} == 0 +to protect things which should only be evaluated in the initial instance of +.Nm . +.It Va .MAKE.MAKEFILE_PREFERENCE +The ordered list of makefile names +(default +.Ql Pa makefile , +.Ql Pa Makefile ) +that +.Nm +will look for. +.It Va .MAKE.MAKEFILES +The list of makefiles read by +.Nm , +which is useful for tracking dependencies. +Each makefile is recorded only once, regardless of the number of times read. +.It Va .MAKE.MODE +Processed after reading all makefiles. +Can affect the mode that +.Nm +runs in. +It can contain a number of keywords: +.Bl -hang -width ignore-cmd +.It Pa compat +Like +.Fl B , +puts +.Nm +into "compat" mode. +.It Pa meta +Puts +.Nm +into "meta" mode, where meta files are created for each target +to capture the command run, the output generated and if +.Xr filemon 4 +is available, the system calls which are of interest to +.Nm . +The captured output can be very useful when diagnosing errors. +.It Pa curdirOk= Ar bf +Normally +.Nm +will not create .meta files in +.Ql Va .CURDIR . +This can be overridden by setting +.Va bf +to a value which represents True. +.It Pa env +For debugging, it can be useful to inlcude the environment +in the .meta file. +.It Pa verbose +If in "meta" mode, print a clue about the target being built. +This is useful if the build is otherwise running silently. +The message printed the value of: +.Va .MAKE.META.PREFIX . +.It Pa ignore-cmd +Some makefiles have commands which are simply not stable. +This keyword causes them to be ignored for +determining whether a target is out of date in "meta" mode. +See also +.Ic .NOMETA_CMP . +.It Pa silent= Ar bf +If +.Va bf +is True, when a .meta file is created, mark the target +.Ic .SILENT . +.El +.It Va .MAKE.META.BAILIWICK +In "meta" mode, provides a list of prefixes which +match the directories controlled by +.Nm . +If a file that was generated outside of +.Va .OBJDIR +but within said bailiwick is missing, +the current target is considered out-of-date. +.It Va .MAKE.META.CREATED +In "meta" mode, this variable contains a list of all the meta files +updated. +If not empty, it can be used to trigger processing of +.Va .MAKE.META.FILES . +.It Va .MAKE.META.FILES +In "meta" mode, this variable contains a list of all the meta files +used (updated or not). +This list can be used to process the meta files to extract dependency +information. +.It Va .MAKE.META.IGNORE_PATHS +Provides a list of path prefixes that should be ignored; +because the contents are expected to change over time. +The default list includes: +.Ql Pa /dev /etc /proc /tmp /var/run /var/tmp +.It Va .MAKE.META.PREFIX +Defines the message printed for each meta file updated in "meta verbose" mode. +The default value is: +.Dl Building ${.TARGET:H:tA}/${.TARGET:T} +.It Va .MAKEOVERRIDES +This variable is used to record the names of variables assigned to +on the command line, so that they may be exported as part of +.Ql Ev MAKEFLAGS . +This behaviour can be disabled by assigning an empty value to +.Ql Va .MAKEOVERRIDES +within a makefile. +Extra variables can be exported from a makefile +by appending their names to +.Ql Va .MAKEOVERRIDES . +.Ql Ev MAKEFLAGS +is re-exported whenever +.Ql Va .MAKEOVERRIDES +is modified. +.It Va .MAKE.PATH_FILEMON +If +.Nm +was built with +.Xr filemon 4 +support, this is set to the path of the device node. +This allows makefiles to test for this support. +.It Va .MAKE.PID +The process-id of +.Nm . +.It Va .MAKE.PPID +The parent process-id of +.Nm . +.It Va MAKE_PRINT_VAR_ON_ERROR +When +.Nm +stops due to an error, it prints its name and the value of +.Ql Va .CURDIR +as well as the value of any variables named in +.Ql Va MAKE_PRINT_VAR_ON_ERROR . +.It Va .newline +This variable is simply assigned a newline character as its value. +This allows expansions using the +.Cm \&:@ +modifier to put a newline between +iterations of the loop rather than a space. +For example, the printing of +.Ql Va MAKE_PRINT_VAR_ON_ERROR +could be done as ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@}. +.It Va .OBJDIR +A path to the directory where the targets are built. +Its value is determined by trying to +.Xr chdir 2 +to the following directories in order and using the first match: +.Bl -enum +.It +.Ev ${MAKEOBJDIRPREFIX}${.CURDIR} +.Pp +(Only if +.Ql Ev MAKEOBJDIRPREFIX +is set in the environment or on the command line.) +.It +.Ev ${MAKEOBJDIR} +.Pp +(Only if +.Ql Ev MAKEOBJDIR +is set in the environment or on the command line.) +.It +.Ev ${.CURDIR} Ns Pa /obj. Ns Ev ${MACHINE} +.It +.Ev ${.CURDIR} Ns Pa /obj +.It +.Pa /usr/obj/ Ns Ev ${.CURDIR} +.It +.Ev ${.CURDIR} +.El +.Pp +Variable expansion is performed on the value before it's used, +so expressions such as +.Dl ${.CURDIR:S,^/usr/src,/var/obj,} +may be used. +This is especially useful with +.Ql Ev MAKEOBJDIR . +.Pp +.Ql Va .OBJDIR +may be modified in the makefile as a global variable. +In all cases, +.Nm +will +.Xr chdir 2 +to +.Ql Va .OBJDIR +and set +.Ql Ev PWD +to that directory before executing any targets. +. +.It Va .PARSEDIR +A path to the directory of the current +.Ql Pa Makefile +being parsed. +.It Va .PARSEFILE +The basename of the current +.Ql Pa Makefile +being parsed. +This variable and +.Ql Va .PARSEDIR +are both set only while the +.Ql Pa Makefiles +are being parsed. +If you want to retain their current values, assign them to a variable +using assignment with expansion: +.Pq Ql Cm \&:= . +.It Va .PATH +A variable that represents the list of directories that +.Nm +will search for files. +The search list should be updated using the target +.Ql Va .PATH +rather than the variable. +.It Ev PWD +Alternate path to the current directory. +.Nm +normally sets +.Ql Va .CURDIR +to the canonical path given by +.Xr getcwd 3 . +However, if the environment variable +.Ql Ev PWD +is set and gives a path to the current directory, then +.Nm +sets +.Ql Va .CURDIR +to the value of +.Ql Ev PWD +instead. +This behaviour is disabled if +.Ql Ev MAKEOBJDIRPREFIX +is set or +.Ql Ev MAKEOBJDIR +contains a variable transform. +.Ql Ev PWD +is set to the value of +.Ql Va .OBJDIR +for all programs which +.Nm +executes. +.It Ev .TARGETS +The list of targets explicitly specified on the command line, if any. +.It Ev VPATH +Colon-separated +.Pq Dq \&: +lists of directories that +.Nm +will search for files. +The variable is supported for compatibility with old make programs only, +use +.Ql Va .PATH +instead. +.El +.Ss Variable modifiers +Variable expansion may be modified to select or modify each word of the +variable (where a +.Dq word +is white-space delimited sequence of characters). +The general format of a variable expansion is as follows: +.Pp +.Dl ${variable[:modifier[:...]]} +.Pp +Each modifier begins with a colon, +which may be escaped with a backslash +.Pq Ql \e . +.Pp +A set of modifiers can be specified via a variable, as follows: +.Pp +.Dl modifier_variable=modifier[:...] +.Dl ${variable:${modifier_variable}[:...]} +.Pp +In this case the first modifier in the modifier_variable does not +start with a colon, since that must appear in the referencing +variable. +If any of the modifiers in the modifier_variable contain a dollar sign +.Pq Ql $ , +these must be doubled to avoid early expansion. +.Pp +The supported modifiers are: +.Bl -tag -width EEE +.It Cm \&:E +Replaces each word in the variable with its suffix. +.It Cm \&:H +Replaces each word in the variable with everything but the last component. +.It Cm \&:M Ns Ar pattern +Select only those words that match +.Ar pattern . +The standard shell wildcard characters +.Pf ( Ql * , +.Ql \&? , +and +.Ql Oo Oc ) +may +be used. +The wildcard characters may be escaped with a backslash +.Pq Ql \e . +As a consequence of the way values are split into words, matched, +and then joined, a construct like +.Dl ${VAR:M*} +will normalise the inter-word spacing, removing all leading and +trailing space, and converting multiple consecutive spaces +to single spaces. +. +.It Cm \&:N Ns Ar pattern +This is identical to +.Ql Cm \&:M , +but selects all words which do not match +.Ar pattern . +.It Cm \&:O +Order every word in variable alphabetically. +To sort words in +reverse order use the +.Ql Cm \&:O:[-1..1] +combination of modifiers. +.It Cm \&:Ox +Randomize words in variable. +The results will be different each time you are referring to the +modified variable; use the assignment with expansion +.Pq Ql Cm \&:= +to prevent such behaviour. +For example, +.Bd -literal -offset indent +LIST= uno due tre quattro +RANDOM_LIST= ${LIST:Ox} +STATIC_RANDOM_LIST:= ${LIST:Ox} + +all: + @echo "${RANDOM_LIST}" + @echo "${RANDOM_LIST}" + @echo "${STATIC_RANDOM_LIST}" + @echo "${STATIC_RANDOM_LIST}" +.Ed +may produce output similar to: +.Bd -literal -offset indent +quattro due tre uno +tre due quattro uno +due uno quattro tre +due uno quattro tre +.Ed +.It Cm \&:Q +Quotes every shell meta-character in the variable, so that it can be passed +safely through recursive invocations of +.Nm . +.It Cm \&:R +Replaces each word in the variable with everything but its suffix. +.It Cm \&:gmtime +The value is a format string for +.Xr strftime 3 , +using the current +.Xr gmtime 3 . +.It Cm \&:hash +Compute a 32bit hash of the value and encode it as hex digits. +.It Cm \&:localtime +The value is a format string for +.Xr strftime 3 , +using the current +.Xr localtime 3 . +.It Cm \&:tA +Attempt to convert variable to an absolute path using +.Xr realpath 3 , +if that fails, the value is unchanged. +.It Cm \&:tl +Converts variable to lower-case letters. +.It Cm \&:ts Ns Ar c +Words in the variable are normally separated by a space on expansion. +This modifier sets the separator to the character +.Ar c . +If +.Ar c +is omitted, then no separator is used. +The common escapes (including octal numeric codes), work as expected. +.It Cm \&:tu +Converts variable to upper-case letters. +.It Cm \&:tW +Causes the value to be treated as a single word +(possibly containing embedded white space). +See also +.Ql Cm \&:[*] . +.It Cm \&:tw +Causes the value to be treated as a sequence of +words delimited by white space. +See also +.Ql Cm \&:[@] . +.Sm off +.It Cm \&:S No \&/ Ar old_string No \&/ Ar new_string No \&/ Op Cm 1gW +.Sm on +Modify the first occurrence of +.Ar old_string +in the variable's value, replacing it with +.Ar new_string . +If a +.Ql g +is appended to the last slash of the pattern, all occurrences +in each word are replaced. +If a +.Ql 1 +is appended to the last slash of the pattern, only the first word +is affected. +If a +.Ql W +is appended to the last slash of the pattern, +then the value is treated as a single word +(possibly containing embedded white space). +If +.Ar old_string +begins with a caret +.Pq Ql ^ , +.Ar old_string +is anchored at the beginning of each word. +If +.Ar old_string +ends with a dollar sign +.Pq Ql \&$ , +it is anchored at the end of each word. +Inside +.Ar new_string , +an ampersand +.Pq Ql \*[Am] +is replaced by +.Ar old_string +(without any +.Ql ^ +or +.Ql \&$ ) . +Any character may be used as a delimiter for the parts of the modifier +string. +The anchoring, ampersand and delimiter characters may be escaped with a +backslash +.Pq Ql \e . +.Pp +Variable expansion occurs in the normal fashion inside both +.Ar old_string +and +.Ar new_string +with the single exception that a backslash is used to prevent the expansion +of a dollar sign +.Pq Ql \&$ , +not a preceding dollar sign as is usual. +.Sm off +.It Cm \&:C No \&/ Ar pattern No \&/ Ar replacement No \&/ Op Cm 1gW +.Sm on +The +.Cm \&:C +modifier is just like the +.Cm \&:S +modifier except that the old and new strings, instead of being +simple strings, are an extended regular expression (see +.Xr regex 3 ) +string +.Ar pattern +and an +.Xr ed 1 Ns \-style +string +.Ar replacement . +Normally, the first occurrence of the pattern +.Ar pattern +in each word of the value is substituted with +.Ar replacement . +The +.Ql 1 +modifier causes the substitution to apply to at most one word; the +.Ql g +modifier causes the substitution to apply to as many instances of the +search pattern +.Ar pattern +as occur in the word or words it is found in; the +.Ql W +modifier causes the value to be treated as a single word +(possibly containing embedded white space). +Note that +.Ql 1 +and +.Ql g +are orthogonal; the former specifies whether multiple words are +potentially affected, the latter whether multiple substitutions can +potentially occur within each affected word. +.Pp +As for the +.Cm \&:S +modifier, the +.Ar pattern +and +.Ar replacement +are subjected to variable expansion before being parsed as +regular expressions. +.It Cm \&:T +Replaces each word in the variable with its last component. +.It Cm \&:u +Remove adjacent duplicate words (like +.Xr uniq 1 ) . +.Sm off +.It Cm \&:\&? Ar true_string Cm \&: Ar false_string +.Sm on +If the variable name (not its value), when parsed as a .if conditional +expression, evaluates to true, return as its value the +.Ar true_string , +otherwise return the +.Ar false_string . +Since the variable name is used as the expression, \&:\&? must be the +first modifier after the variable name itself - which will, of course, +usually contain variable expansions. +A common error is trying to use expressions like +.Dl ${NUMBERS:M42:?match:no} +which actually tests defined(NUMBERS), +to determine is any words match "42" you need to use something like: +.Dl ${"${NUMBERS:M42}" != \&"\&":?match:no} . +.It Ar :old_string=new_string +This is the +.At V +style variable substitution. +It must be the last modifier specified. +If +.Ar old_string +or +.Ar new_string +do not contain the pattern matching character +.Ar % +then it is assumed that they are +anchored at the end of each word, so only suffixes or entire +words may be replaced. +Otherwise +.Ar % +is the substring of +.Ar old_string +to be replaced in +.Ar new_string . +.Pp +Variable expansion occurs in the normal fashion inside both +.Ar old_string +and +.Ar new_string +with the single exception that a backslash is used to prevent the +expansion of a dollar sign +.Pq Ql \&$ , +not a preceding dollar sign as is usual. +.Sm off +.It Cm \&:@ Ar temp Cm @ Ar string Cm @ +.Sm on +This is the loop expansion mechanism from the OSF Development +Environment (ODE) make. +Unlike +.Cm \&.for +loops expansion occurs at the time of +reference. +Assign +.Ar temp +to each word in the variable and evaluate +.Ar string . +The ODE convention is that +.Ar temp +should start and end with a period. +For example. +.Dl ${LINKS:@.LINK.@${LN} ${TARGET} ${.LINK.}@} +.Pp +However a single character variable is often more readable: +.Dl ${MAKE_PRINT_VAR_ON_ERROR:@v@$v='${$v}'${.newline}@} +.It Cm \&:U Ns Ar newval +If the variable is undefined +.Ar newval +is the value. +If the variable is defined, the existing value is returned. +This is another ODE make feature. +It is handy for setting per-target CFLAGS for instance: +.Dl ${_${.TARGET:T}_CFLAGS:U${DEF_CFLAGS}} +If a value is only required if the variable is undefined, use: +.Dl ${VAR:D:Unewval} +.It Cm \&:D Ns Ar newval +If the variable is defined +.Ar newval +is the value. +.It Cm \&:L +The name of the variable is the value. +.It Cm \&:P +The path of the node which has the same name as the variable +is the value. +If no such node exists or its path is null, then the +name of the variable is used. +In order for this modifier to work, the name (node) must at least have +appeared on the rhs of a dependency. +.Sm off +.It Cm \&:\&! Ar cmd Cm \&! +.Sm on +The output of running +.Ar cmd +is the value. +.It Cm \&:sh +If the variable is non-empty it is run as a command and the output +becomes the new value. +.It Cm \&::= Ns Ar str +The variable is assigned the value +.Ar str +after substitution. +This modifier and its variations are useful in +obscure situations such as wanting to set a variable when shell commands +are being parsed. +These assignment modifiers always expand to +nothing, so if appearing in a rule line by themselves should be +preceded with something to keep +.Nm +happy. +.Pp +The +.Ql Cm \&:: +helps avoid false matches with the +.At V +style +.Cm \&:= +modifier and since substitution always occurs the +.Cm \&::= +form is vaguely appropriate. +.It Cm \&::?= Ns Ar str +As for +.Cm \&::= +but only if the variable does not already have a value. +.It Cm \&::+= Ns Ar str +Append +.Ar str +to the variable. +.It Cm \&::!= Ns Ar cmd +Assign the output of +.Ar cmd +to the variable. +.It Cm \&:\&[ Ns Ar range Ns Cm \&] +Selects one or more words from the value, +or performs other operations related to the way in which the +value is divided into words. +.Pp +Ordinarily, a value is treated as a sequence of words +delimited by white space. +Some modifiers suppress this behaviour, +causing a value to be treated as a single word +(possibly containing embedded white space). +An empty value, or a value that consists entirely of white-space, +is treated as a single word. +For the purposes of the +.Ql Cm \&:[] +modifier, the words are indexed both forwards using positive integers +(where index 1 represents the first word), +and backwards using negative integers +(where index \-1 represents the last word). +.Pp +The +.Ar range +is subjected to variable expansion, and the expanded result is +then interpreted as follows: +.Bl -tag -width index +.\" :[n] +.It Ar index +Selects a single word from the value. +.\" :[start..end] +.It Ar start Ns Cm \&.. Ns Ar end +Selects all words from +.Ar start +to +.Ar end , +inclusive. +For example, +.Ql Cm \&:[2..-1] +selects all words from the second word to the last word. +If +.Ar start +is greater than +.Ar end , +then the words are output in reverse order. +For example, +.Ql Cm \&:[-1..1] +selects all the words from last to first. +.\" :[*] +.It Cm \&* +Causes subsequent modifiers to treat the value as a single word +(possibly containing embedded white space). +Analogous to the effect of +\&"$*\&" +in Bourne shell. +.\" :[0] +.It 0 +Means the same as +.Ql Cm \&:[*] . +.\" :[*] +.It Cm \&@ +Causes subsequent modifiers to treat the value as a sequence of words +delimited by white space. +Analogous to the effect of +\&"$@\&" +in Bourne shell. +.\" :[#] +.It Cm \&# +Returns the number of words in the value. +.El \" :[range] +.El +.Sh INCLUDE STATEMENTS, CONDITIONALS AND FOR LOOPS +Makefile inclusion, conditional structures and for loops reminiscent +of the C programming language are provided in +.Nm . +All such structures are identified by a line beginning with a single +dot +.Pq Ql \&. +character. +Files are included with either +.Cm \&.include Aq Ar file +or +.Cm \&.include Pf \*q Ar file Ns \*q . +Variables between the angle brackets or double quotes are expanded +to form the file name. +If angle brackets are used, the included makefile is expected to be in +the system makefile directory. +If double quotes are used, the including makefile's directory and any +directories specified using the +.Fl I +option are searched before the system +makefile directory. +For compatibility with other versions of +.Nm +.Ql include file ... +is also accepted. +If the include statement is written as +.Cm .-include +or as +.Cm .sinclude +then errors locating and/or opening include files are ignored. +.Pp +Conditional expressions are also preceded by a single dot as the first +character of a line. +The possible conditionals are as follows: +.Bl -tag -width Ds +.It Ic .error Ar message +The message is printed along with the name of the makefile and line number, +then +.Nm +will exit. +.It Ic .export Ar variable ... +Export the specified global variable. +If no variable list is provided, all globals are exported +except for internal variables (those that start with +.Ql \&. ) . +This is not affected by the +.Fl X +flag, so should be used with caution. +For compatibility with other +.Nm +programs +.Ql export variable=value +is also accepted. +.Pp +Appending a variable name to +.Va .MAKE.EXPORTED +is equivalent to exporting a variable. +.It Ic .export-env Ar variable ... +The same as +.Ql .export , +except that the variable is not appended to +.Va .MAKE.EXPORTED . +This allows exporting a value to the environment which is different from that +used by +.Nm +internally. +.It Ic .info Ar message +The message is printed along with the name of the makefile and line number. +.It Ic .undef Ar variable +Un-define the specified global variable. +Only global variables may be un-defined. +.It Ic .unexport Ar variable ... +The opposite of +.Ql .export . +The specified global +.Va variable +will be removed from +.Va .MAKE.EXPORTED . +If no variable list is provided, all globals are unexported, +and +.Va .MAKE.EXPORTED +deleted. +.It Ic .unexport-env +Unexport all globals previously exported and +clear the environment inherited from the parent. +This operation will cause a memory leak of the original environment, +so should be used sparingly. +Testing for +.Va .MAKE.LEVEL +being 0, would make sense. +Also note that any variables which originated in the parent environment +should be explicitly preserved if desired. +For example: +.Bd -literal -offset indent +.Li .if ${.MAKE.LEVEL} == 0 +PATH := ${PATH} +.Li .unexport-env +.Li .export PATH +.Li .endif +.Pp +.Ed +Would result in an environment containing only +.Ql Ev PATH , +which is the minimal useful environment. +Actually +.Ql Ev .MAKE.LEVEL +will also be pushed into the new environment. +.It Ic .warning Ar message +The message prefixed by +.Ql Pa warning: +is printed along with the name of the makefile and line number. +.It Ic \&.if Oo \&! Oc Ns Ar expression Op Ar operator expression ... +Test the value of an expression. +.It Ic .ifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +Test the value of a variable. +.It Ic .ifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +Test the value of a variable. +.It Ic .ifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +Test the target being built. +.It Ic .ifnmake Oo \&! Ns Oc Ar target Op Ar operator target ... +Test the target being built. +.It Ic .else +Reverse the sense of the last conditional. +.It Ic .elif Oo \&! Ns Oc Ar expression Op Ar operator expression ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .if . +.It Ic .elifdef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifdef . +.It Ic .elifndef Oo \&! Oc Ns Ar variable Op Ar operator variable ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifndef . +.It Ic .elifmake Oo \&! Oc Ns Ar target Op Ar operator target ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifmake . +.It Ic .elifnmake Oo \&! Oc Ns Ar target Op Ar operator target ... +A combination of +.Ql Ic .else +followed by +.Ql Ic .ifnmake . +.It Ic .endif +End the body of the conditional. +.El +.Pp +The +.Ar operator +may be any one of the following: +.Bl -tag -width "Cm XX" +.It Cm \&|\&| +Logical OR. +.It Cm \&\*[Am]\*[Am] +Logical +.Tn AND ; +of higher precedence than +.Dq \&|\&| . +.El +.Pp +As in C, +.Nm +will only evaluate a conditional as far as is necessary to determine +its value. +Parentheses may be used to change the order of evaluation. +The boolean operator +.Ql Ic \&! +may be used to logically negate an entire +conditional. +It is of higher precedence than +.Ql Ic \&\*[Am]\*[Am] . +.Pp +The value of +.Ar expression +may be any of the following: +.Bl -tag -width defined +.It Ic defined +Takes a variable name as an argument and evaluates to true if the variable +has been defined. +.It Ic make +Takes a target name as an argument and evaluates to true if the target +was specified as part of +.Nm Ns 's +command line or was declared the default target (either implicitly or +explicitly, see +.Va .MAIN ) +before the line containing the conditional. +.It Ic empty +Takes a variable, with possible modifiers, and evaluates to true if +the expansion of the variable would result in an empty string. +.It Ic exists +Takes a file name as an argument and evaluates to true if the file exists. +The file is searched for on the system search path (see +.Va .PATH ) . +.It Ic target +Takes a target name as an argument and evaluates to true if the target +has been defined. +.It Ic commands +Takes a target name as an argument and evaluates to true if the target +has been defined and has commands associated with it. +.El +.Pp +.Ar Expression +may also be an arithmetic or string comparison. +Variable expansion is +performed on both sides of the comparison, after which the integral +values are compared. +A value is interpreted as hexadecimal if it is +preceded by 0x, otherwise it is decimal; octal numbers are not supported. +The standard C relational operators are all supported. +If after +variable expansion, either the left or right hand side of a +.Ql Ic == +or +.Ql Ic "!=" +operator is not an integral value, then +string comparison is performed between the expanded +variables. +If no relational operator is given, it is assumed that the expanded +variable is being compared against 0 or an empty string in the case +of a string comparison. +.Pp +When +.Nm +is evaluating one of these conditional expressions, and it encounters +a (white-space separated) word it doesn't recognize, either the +.Dq make +or +.Dq defined +expression is applied to it, depending on the form of the conditional. +If the form is +.Ql Ic .ifdef , +.Ql Ic .ifndef , +or +.Ql Ic .if +the +.Dq defined +expression is applied. +Similarly, if the form is +.Ql Ic .ifmake +or +.Ql Ic .ifnmake , the +.Dq make +expression is applied. +.Pp +If the conditional evaluates to true the parsing of the makefile continues +as before. +If it evaluates to false, the following lines are skipped. +In both cases this continues until a +.Ql Ic .else +or +.Ql Ic .endif +is found. +.Pp +For loops are typically used to apply a set of rules to a list of files. +The syntax of a for loop is: +.Pp +.Bl -tag -compact -width Ds +.It Ic \&.for Ar variable Oo Ar variable ... Oc Ic in Ar expression +.It Aq make-rules +.It Ic \&.endfor +.El +.Pp +After the for +.Ic expression +is evaluated, it is split into words. +On each iteration of the loop, one word is taken and assigned to each +.Ic variable , +in order, and these +.Ic variables +are substituted into the +.Ic make-rules +inside the body of the for loop. +The number of words must come out even; that is, if there are three +iteration variables, the number of words provided must be a multiple +of three. +.Sh COMMENTS +Comments begin with a hash +.Pq Ql \&# +character, anywhere but in a shell +command line, and continue to the end of an unescaped new line. +.Sh SPECIAL SOURCES (ATTRIBUTES) +.Bl -tag -width .IGNOREx +.It Ic .EXEC +Target is never out of date, but always execute commands anyway. +.It Ic .IGNORE +Ignore any errors from the commands associated with this target, exactly +as if they all were preceded by a dash +.Pq Ql \- . +.\" .It Ic .INVISIBLE +.\" XXX +.\" .It Ic .JOIN +.\" XXX +.It Ic .MADE +Mark all sources of this target as being up-to-date. +.It Ic .MAKE +Execute the commands associated with this target even if the +.Fl n +or +.Fl t +options were specified. +Normally used to mark recursive +.Nm Ns s . +.It Ic .META +Create a meta file for the target, even if it is flagged as +.Ic .PHONY , +.Ic .MAKE , +or +.Ic .SPECIAL . +Usage in conjunction with +.Ic .MAKE +is the most likely case. +In "meta" mode, the target is out-of-date if the meta file is missing. +.It Ic .NOMETA +Do not create a meta file for the target. +Meta files are also not created for +.Ic .PHONY , +.Ic .MAKE , +or +.Ic .SPECIAL +targets. +.It Ic .NOMETA_CMP +Ignore differences in commands when deciding if target is out of date. +This is useful if the command contains a value which always changes. +If the number of commands change, though, the target will still be out of date. +The same effect applies to any command line that uses the variable +.Va .OODATE , +which can be used for that purpose even when not otherwise needed or desired: +.Bd -literal -offset indent + +skip-compare-for-some: + @echo this will be compared + @echo this will not ${.OODATE:M.NOMETA_CMP} + @echo this will also be compared + +.Ed +The +.Cm \&:M +pattern suppresses any expansion of the unwanted variable. +.It Ic .NOPATH +Do not search for the target in the directories specified by +.Ic .PATH . +.It Ic .NOTMAIN +Normally +.Nm +selects the first target it encounters as the default target to be built +if no target was specified. +This source prevents this target from being selected. +.It Ic .OPTIONAL +If a target is marked with this attribute and +.Nm +can't figure out how to create it, it will ignore this fact and assume +the file isn't needed or already exists. +.It Ic .PHONY +The target does not +correspond to an actual file; it is always considered to be out of date, +and will not be created with the +.Fl t +option. +Suffix-transformation rules are not applied to +.Ic .PHONY +targets. +.It Ic .PRECIOUS +When +.Nm +is interrupted, it normally removes any partially made targets. +This source prevents the target from being removed. +.It Ic .RECURSIVE +Synonym for +.Ic .MAKE . +.It Ic .SILENT +Do not echo any of the commands associated with this target, exactly +as if they all were preceded by an at sign +.Pq Ql @ . +.It Ic .USE +Turn the target into +.Nm Ns 's +version of a macro. +When the target is used as a source for another target, the other target +acquires the commands, sources, and attributes (except for +.Ic .USE ) +of the +source. +If the target already has commands, the +.Ic .USE +target's commands are appended +to them. +.It Ic .USEBEFORE +Exactly like +.Ic .USE , +but prepend the +.Ic .USEBEFORE +target commands to the target. +.It Ic .WAIT +If +.Ic .WAIT +appears in a dependency line, the sources that precede it are +made before the sources that succeed it in the line. +Since the dependents of files are not made until the file itself +could be made, this also stops the dependents being built unless they +are needed for another branch of the dependency tree. +So given: +.Bd -literal +x: a .WAIT b + echo x +a: + echo a +b: b1 + echo b +b1: + echo b1 + +.Ed +the output is always +.Ql a , +.Ql b1 , +.Ql b , +.Ql x . +.br +The ordering imposed by +.Ic .WAIT +is only relevant for parallel makes. +.El +.Sh SPECIAL TARGETS +Special targets may not be included with other targets, i.e. they must be +the only target specified. +.Bl -tag -width .BEGINx +.It Ic .BEGIN +Any command lines attached to this target are executed before anything +else is done. +.It Ic .DEFAULT +This is sort of a +.Ic .USE +rule for any target (that was used only as a +source) that +.Nm +can't figure out any other way to create. +Only the shell script is used. +The +.Ic .IMPSRC +variable of a target that inherits +.Ic .DEFAULT Ns 's +commands is set +to the target's own name. +.It Ic .END +Any command lines attached to this target are executed after everything +else is done. +.It Ic .ERROR +Any command lines attached to this target are executed when another target fails. +The +.Ic .ERROR_TARGET +variable is set to the target that failed. +See also +.Ic MAKE_PRINT_VAR_ON_ERROR . +.It Ic .IGNORE +Mark each of the sources with the +.Ic .IGNORE +attribute. +If no sources are specified, this is the equivalent of specifying the +.Fl i +option. +.It Ic .INTERRUPT +If +.Nm +is interrupted, the commands for this target will be executed. +.It Ic .MAIN +If no target is specified when +.Nm +is invoked, this target will be built. +.It Ic .MAKEFLAGS +This target provides a way to specify flags for +.Nm +when the makefile is used. +The flags are as if typed to the shell, though the +.Fl f +option will have +no effect. +.\" XXX: NOT YET!!!! +.\" .It Ic .NOTPARALLEL +.\" The named targets are executed in non parallel mode. +.\" If no targets are +.\" specified, then all targets are executed in non parallel mode. +.It Ic .NOPATH +Apply the +.Ic .NOPATH +attribute to any specified sources. +.It Ic .NOTPARALLEL +Disable parallel mode. +.It Ic .NO_PARALLEL +Synonym for +.Ic .NOTPARALLEL , +for compatibility with other pmake variants. +.It Ic .ORDER +The named targets are made in sequence. +This ordering does not add targets to the list of targets to be made. +Since the dependents of a target do not get built until the target itself +could be built, unless +.Ql a +is built by another part of the dependency graph, +the following is a dependency loop: +.Bd -literal +\&.ORDER: b a +b: a +.Ed +.Pp +The ordering imposed by +.Ic .ORDER +is only relevant for parallel makes. +.\" XXX: NOT YET!!!! +.\" .It Ic .PARALLEL +.\" The named targets are executed in parallel mode. +.\" If no targets are +.\" specified, then all targets are executed in parallel mode. +.It Ic .PATH +The sources are directories which are to be searched for files not +found in the current directory. +If no sources are specified, any previously specified directories are +deleted. +If the source is the special +.Ic .DOTLAST +target, then the current working +directory is searched last. +.It Ic .PATH. Ns Va suffix +Like +.Ic .PATH +but applies only to files with a particular suffix. +The suffix must have been previously declared with +.Ic .SUFFIXES . +.It Ic .PHONY +Apply the +.Ic .PHONY +attribute to any specified sources. +.It Ic .PRECIOUS +Apply the +.Ic .PRECIOUS +attribute to any specified sources. +If no sources are specified, the +.Ic .PRECIOUS +attribute is applied to every +target in the file. +.It Ic .SHELL +Sets the shell that +.Nm +will use to execute commands. +The sources are a set of +.Ar field=value +pairs. +.Bl -tag -width hasErrCtls +.It Ar name +This is the minimal specification, used to select one of the builtin +shell specs; +.Ar sh , +.Ar ksh , +and +.Ar csh . +.It Ar path +Specifies the path to the shell. +.It Ar hasErrCtl +Indicates whether the shell supports exit on error. +.It Ar check +The command to turn on error checking. +.It Ar ignore +The command to disable error checking. +.It Ar echo +The command to turn on echoing of commands executed. +.It Ar quiet +The command to turn off echoing of commands executed. +.It Ar filter +The output to filter after issuing the +.Ar quiet +command. +It is typically identical to +.Ar quiet . +.It Ar errFlag +The flag to pass the shell to enable error checking. +.It Ar echoFlag +The flag to pass the shell to enable command echoing. +.It Ar newline +The string literal to pass the shell that results in a single newline +character when used outside of any quoting characters. +.El +Example: +.Bd -literal +\&.SHELL: name=ksh path=/bin/ksh hasErrCtl=true \e + check="set \-e" ignore="set +e" \e + echo="set \-v" quiet="set +v" filter="set +v" \e + echoFlag=v errFlag=e newline="'\en'" +.Ed +.It Ic .SILENT +Apply the +.Ic .SILENT +attribute to any specified sources. +If no sources are specified, the +.Ic .SILENT +attribute is applied to every +command in the file. +.It Ic .STALE +This target gets run when a dependency file contains stale entries, having +.Va .ALLSRC +set to the name of that dependency file. +.It Ic .SUFFIXES +Each source specifies a suffix to +.Nm . +If no sources are specified, any previously specified suffixes are deleted. +It allows the creation of suffix-transformation rules. +.Pp +Example: +.Bd -literal +\&.SUFFIXES: .o +\&.c.o: + cc \-o ${.TARGET} \-c ${.IMPSRC} +.Ed +.El +.Sh ENVIRONMENT +.Nm +uses the following environment variables, if they exist: +.Ev MACHINE , +.Ev MACHINE_ARCH , +.Ev MAKE , +.Ev MAKEFLAGS , +.Ev MAKEOBJDIR , +.Ev MAKEOBJDIRPREFIX , +.Ev MAKESYSPATH , +.Ev PWD , +and +.Ev TMPDIR . +.Pp +.Ev MAKEOBJDIRPREFIX +and +.Ev MAKEOBJDIR +may only be set in the environment or on the command line to +.Nm +and not as makefile variables; +see the description of +.Ql Va .OBJDIR +for more details. +.Sh FILES +.Bl -tag -width /usr/share/mk -compact +.It .depend +list of dependencies +.It Makefile +list of dependencies +.It makefile +list of dependencies +.It sys.mk +system makefile +.It /usr/share/mk +system makefile directory +.El +.Sh COMPATIBILITY +The basic make syntax is compatible between different versions of make; +however the special variables, variable modifiers and conditionals are not. +.Ss Older versions +An incomplete list of changes in older versions of +.Nm : +.Pp +The way that .for loop variables are substituted changed after +.Nx 5.0 +so that they still appear to be variable expansions. +In particular this stops them being treated as syntax, and removes some +obscure problems using them in .if statements. +.Pp +The way that parallel makes are scheduled changed in +.Nx 4.0 +so that .ORDER and .WAIT apply recursively to the dependent nodes. +The algorithms used may change again in the future. +.Ss Other make dialects +Other make dialects (GNU make, SVR4 make, POSIX make, etc.) do not +support most of the features of +.Nm +as described in this manual. +Most notably: +.Bl -bullet -offset indent +.It +The +.Ic .WAIT +and +.Ic .ORDER +declarations and most functionality pertaining to parallelization. +(GNU make supports parallelization but lacks these features needed to +control it effectively.) +.It +Directives, including for loops and conditionals and most of the +forms of include files. +(GNU make has its own incompatible and less powerful syntax for +conditionals.) +.It +All built-in variables that begin with a dot. +.It +Most of the special sources and targets that begin with a dot, +with the notable exception of +.Ic .PHONY , +.Ic .PRECIOUS , +and +.Ic .SUFFIXES . +.It +Variable modifiers, except for the +.Dl :old=new +string substitution, which does not portably support globbing with +.Ql % +and historically only works on declared suffixes. +.It +The +.Ic $> +variable even in its short form; most makes support this functionality +but its name varies. +.El +.Pp +Some features are somewhat more portable, such as assignment with +.Ic += , +.Ic ?= , +and +.Ic != . +The +.Ic .PATH +functionality is based on an older feature +.Ic VPATH +found in GNU make and many versions of SVR4 make; however, +historically its behavior is too ill-defined (and too buggy) to rely +upon. +.Pp +The +.Ic $@ +and +.Ic $< +variables are more or less universally portable, as is the +.Ic $(MAKE) +variable. +Basic use of suffix rules (for files only in the current directory, +not trying to chain transformations together, etc.) is also reasonably +portable. +.Sh SEE ALSO +.Xr mkdep 1 +.Sh HISTORY +A +.Nm +command appeared in +.At v7 . +This +.Nm +implementation is based on Adam De Boor's pmake program which was written +for Sprite at Berkeley. +It was designed to be a parallel distributed make running jobs on different +machines using a daemon called +.Dq customs . +.Pp +Historically the target/dependency +.Dq FRC +has been used to FoRCe rebuilding (since the target/dependency +does not exist... unless someone creates an +.Dq FRC +file). +.Sh BUGS +The +.Nm +syntax is difficult to parse without actually acting of the data. +For instance finding the end of a variable use should involve scanning each +the modifiers using the correct terminator for each field. +In many places +.Nm +just counts {} and () in order to find the end of a variable expansion. +.Pp +There is no way of escaping a space character in a filename. diff --git a/buildrump.sh/src/usr.bin/make/make.c b/buildrump.sh/src/usr.bin/make/make.c new file mode 100644 index 00000000..fde18506 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/make.c @@ -0,0 +1,1561 @@ +/* $NetBSD: make.c,v 1.91 2014/10/18 08:33:30 snj Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: make.c,v 1.91 2014/10/18 08:33:30 snj Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)make.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: make.c,v 1.91 2014/10/18 08:33:30 snj Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * make.c -- + * The functions which perform the examination of targets and + * their suitability for creation + * + * Interface: + * Make_Run Initialize things for the module and recreate + * whatever needs recreating. Returns TRUE if + * work was (or would have been) done and FALSE + * otherwise. + * + * Make_Update Update all parents of a given child. Performs + * various bookkeeping chores like the updating + * of the cmgn field of the parent, filling + * of the IMPSRC context variable, etc. It will + * place the parent on the toBeMade queue if it + * should be. + * + * Make_TimeStamp Function to set the parent's cmgn field + * based on a child's modification time. + * + * Make_DoAllVar Set up the various local variables for a + * target, including the .ALLSRC variable, making + * sure that any variable that needs to exist + * at the very least has the empty value. + * + * Make_OODate Determine if a target is out-of-date. + * + * Make_HandleUse See if a child is a .USE node for a parent + * and perform the .USE actions if so. + * + * Make_ExpandUse Expand .USE nodes + */ + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" + +static unsigned int checked = 1;/* Sequence # to detect recursion */ +static Lst toBeMade; /* The current fringe of the graph. These + * are nodes which await examination by + * MakeOODate. It is added to by + * Make_Update and subtracted from by + * MakeStartJobs */ + +static int MakeAddChild(void *, void *); +static int MakeFindChild(void *, void *); +static int MakeUnmark(void *, void *); +static int MakeAddAllSrc(void *, void *); +static int MakeTimeStamp(void *, void *); +static int MakeHandleUse(void *, void *); +static Boolean MakeStartJobs(void); +static int MakePrintStatus(void *, void *); +static int MakeCheckOrder(void *, void *); +static int MakeBuildChild(void *, void *); +static int MakeBuildParent(void *, void *); + +MAKE_ATTR_DEAD static void +make_abort(GNode *gn, int line) +{ + static int two = 2; + + fprintf(debug_file, "make_abort from line %d\n", line); + Targ_PrintNode(gn, &two); + Lst_ForEach(toBeMade, Targ_PrintNode, &two); + Targ_PrintGraph(3); + abort(); +} + +/*- + *----------------------------------------------------------------------- + * Make_TimeStamp -- + * Set the cmgn field of a parent node based on the mtime stamp in its + * child. Called from MakeOODate via Lst_ForEach. + * + * Input: + * pgn the current parent + * cgn the child we've just examined + * + * Results: + * Always returns 0. + * + * Side Effects: + * The cmgn of the parent node will be changed if the mtime + * field of the child is greater than it. + *----------------------------------------------------------------------- + */ +int +Make_TimeStamp(GNode *pgn, GNode *cgn) +{ + if (pgn->cmgn == NULL || cgn->mtime > pgn->cmgn->mtime) { + pgn->cmgn = cgn; + } + return (0); +} + +/* + * Input: + * pgn the current parent + * cgn the child we've just examined + * + */ +static int +MakeTimeStamp(void *pgn, void *cgn) +{ + return Make_TimeStamp((GNode *)pgn, (GNode *)cgn); +} + +/*- + *----------------------------------------------------------------------- + * Make_OODate -- + * See if a given node is out of date with respect to its sources. + * Used by Make_Run when deciding which nodes to place on the + * toBeMade queue initially and by Make_Update to screen out USE and + * EXEC nodes. In the latter case, however, any other sort of node + * must be considered out-of-date since at least one of its children + * will have been recreated. + * + * Input: + * gn the node to check + * + * Results: + * TRUE if the node is out of date. FALSE otherwise. + * + * Side Effects: + * The mtime field of the node and the cmgn field of its parents + * will/may be changed. + *----------------------------------------------------------------------- + */ +Boolean +Make_OODate(GNode *gn) +{ + Boolean oodate; + + /* + * Certain types of targets needn't even be sought as their datedness + * doesn't depend on their modification time... + */ + if ((gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC)) == 0) { + (void)Dir_MTime(gn, 1); + if (DEBUG(MAKE)) { + if (gn->mtime != 0) { + fprintf(debug_file, "modified %s...", Targ_FmtTime(gn->mtime)); + } else { + fprintf(debug_file, "non-existent..."); + } + } + } + + /* + * A target is remade in one of the following circumstances: + * its modification time is smaller than that of its youngest child + * and it would actually be run (has commands or type OP_NOP) + * it's the object of a force operator + * it has no children, was on the lhs of an operator and doesn't exist + * already. + * + * Libraries are only considered out-of-date if the archive module says + * they are. + * + * These weird rules are brought to you by Backward-Compatibility and + * the strange people who wrote 'Make'. + */ + if (gn->type & (OP_USE|OP_USEBEFORE)) { + /* + * If the node is a USE node it is *never* out of date + * no matter *what*. + */ + if (DEBUG(MAKE)) { + fprintf(debug_file, ".USE node..."); + } + oodate = FALSE; + } else if ((gn->type & OP_LIB) && + ((gn->mtime==0) || Arch_IsLib(gn))) { + if (DEBUG(MAKE)) { + fprintf(debug_file, "library..."); + } + + /* + * always out of date if no children and :: target + * or non-existent. + */ + oodate = (gn->mtime == 0 || Arch_LibOODate(gn) || + (gn->cmgn == NULL && (gn->type & OP_DOUBLEDEP))); + } else if (gn->type & OP_JOIN) { + /* + * A target with the .JOIN attribute is only considered + * out-of-date if any of its children was out-of-date. + */ + if (DEBUG(MAKE)) { + fprintf(debug_file, ".JOIN node..."); + } + if (DEBUG(MAKE)) { + fprintf(debug_file, "source %smade...", gn->flags & CHILDMADE ? "" : "not "); + } + oodate = (gn->flags & CHILDMADE) ? TRUE : FALSE; + } else if (gn->type & (OP_FORCE|OP_EXEC|OP_PHONY)) { + /* + * A node which is the object of the force (!) operator or which has + * the .EXEC attribute is always considered out-of-date. + */ + if (DEBUG(MAKE)) { + if (gn->type & OP_FORCE) { + fprintf(debug_file, "! operator..."); + } else if (gn->type & OP_PHONY) { + fprintf(debug_file, ".PHONY node..."); + } else { + fprintf(debug_file, ".EXEC node..."); + } + } + oodate = TRUE; + } else if ((gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) || + (gn->cmgn == NULL && + ((gn->mtime == 0 && !(gn->type & OP_OPTIONAL)) + || gn->type & OP_DOUBLEDEP))) + { + /* + * A node whose modification time is less than that of its + * youngest child or that has no children (cmgn == NULL) and + * either doesn't exist (mtime == 0) and it isn't optional + * or was the object of a * :: operator is out-of-date. + * Why? Because that's the way Make does it. + */ + if (DEBUG(MAKE)) { + if (gn->cmgn != NULL && gn->mtime < gn->cmgn->mtime) { + fprintf(debug_file, "modified before source %s...", + gn->cmgn->path); + } else if (gn->mtime == 0) { + fprintf(debug_file, "non-existent and no sources..."); + } else { + fprintf(debug_file, ":: operator and no sources..."); + } + } + oodate = TRUE; + } else { + /* + * When a non-existing child with no sources + * (such as a typically used FORCE source) has been made and + * the target of the child (usually a directory) has the same + * timestamp as the timestamp just given to the non-existing child + * after it was considered made. + */ + if (DEBUG(MAKE)) { + if (gn->flags & FORCE) + fprintf(debug_file, "non existing child..."); + } + oodate = (gn->flags & FORCE) ? TRUE : FALSE; + } + +#ifdef USE_META + if (useMeta) { + oodate = meta_oodate(gn, oodate); + } +#endif + + /* + * If the target isn't out-of-date, the parents need to know its + * modification time. Note that targets that appear to be out-of-date + * but aren't, because they have no commands and aren't of type OP_NOP, + * have their mtime stay below their children's mtime to keep parents from + * thinking they're out-of-date. + */ + if (!oodate) { + Lst_ForEach(gn->parents, MakeTimeStamp, gn); + } + + return (oodate); +} + +/*- + *----------------------------------------------------------------------- + * MakeAddChild -- + * Function used by Make_Run to add a child to the list l. + * It will only add the child if its make field is FALSE. + * + * Input: + * gnp the node to add + * lp the list to which to add it + * + * Results: + * Always returns 0 + * + * Side Effects: + * The given list is extended + *----------------------------------------------------------------------- + */ +static int +MakeAddChild(void *gnp, void *lp) +{ + GNode *gn = (GNode *)gnp; + Lst l = (Lst) lp; + + if ((gn->flags & REMAKE) == 0 && !(gn->type & (OP_USE|OP_USEBEFORE))) { + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeAddChild: need to examine %s%s\n", + gn->name, gn->cohort_num); + (void)Lst_EnQueue(l, gn); + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * MakeFindChild -- + * Function used by Make_Run to find the pathname of a child + * that was already made. + * + * Input: + * gnp the node to find + * + * Results: + * Always returns 0 + * + * Side Effects: + * The path and mtime of the node and the cmgn of the parent are + * updated; the unmade children count of the parent is decremented. + *----------------------------------------------------------------------- + */ +static int +MakeFindChild(void *gnp, void *pgnp) +{ + GNode *gn = (GNode *)gnp; + GNode *pgn = (GNode *)pgnp; + + (void)Dir_MTime(gn, 0); + Make_TimeStamp(pgn, gn); + pgn->unmade--; + + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Make_HandleUse -- + * Function called by Make_Run and SuffApplyTransform on the downward + * pass to handle .USE and transformation nodes. It implements the + * .USE and transformation functionality by copying the node's commands, + * type flags and children to the parent node. + * + * A .USE node is much like an explicit transformation rule, except + * its commands are always added to the target node, even if the + * target already has commands. + * + * Input: + * cgn The .USE node + * pgn The target of the .USE node + * + * Results: + * none + * + * Side Effects: + * Children and commands may be added to the parent and the parent's + * type may be changed. + * + *----------------------------------------------------------------------- + */ +void +Make_HandleUse(GNode *cgn, GNode *pgn) +{ + LstNode ln; /* An element in the children list */ + +#ifdef DEBUG_SRC + if ((cgn->type & (OP_USE|OP_USEBEFORE|OP_TRANSFORM)) == 0) { + fprintf(debug_file, "Make_HandleUse: called for plain node %s\n", cgn->name); + return; + } +#endif + + if ((cgn->type & (OP_USE|OP_USEBEFORE)) || Lst_IsEmpty(pgn->commands)) { + if (cgn->type & OP_USEBEFORE) { + /* + * .USEBEFORE -- + * prepend the child's commands to the parent. + */ + Lst cmds = pgn->commands; + pgn->commands = Lst_Duplicate(cgn->commands, NULL); + (void)Lst_Concat(pgn->commands, cmds, LST_CONCNEW); + Lst_Destroy(cmds, NULL); + } else { + /* + * .USE or target has no commands -- + * append the child's commands to the parent. + */ + (void)Lst_Concat(pgn->commands, cgn->commands, LST_CONCNEW); + } + } + + if (Lst_Open(cgn->children) == SUCCESS) { + while ((ln = Lst_Next(cgn->children)) != NULL) { + GNode *tgn, *gn = (GNode *)Lst_Datum(ln); + + /* + * Expand variables in the .USE node's name + * and save the unexpanded form. + * We don't need to do this for commands. + * They get expanded properly when we execute. + */ + if (gn->uname == NULL) { + gn->uname = gn->name; + } else { + if (gn->name) + free(gn->name); + } + gn->name = Var_Subst(NULL, gn->uname, pgn, FALSE); + if (gn->name && gn->uname && strcmp(gn->name, gn->uname) != 0) { + /* See if we have a target for this node. */ + tgn = Targ_FindNode(gn->name, TARG_NOCREATE); + if (tgn != NULL) + gn = tgn; + } + + (void)Lst_AtEnd(pgn->children, gn); + (void)Lst_AtEnd(gn->parents, pgn); + pgn->unmade += 1; + } + Lst_Close(cgn->children); + } + + pgn->type |= cgn->type & ~(OP_OPMASK|OP_USE|OP_USEBEFORE|OP_TRANSFORM); +} + +/*- + *----------------------------------------------------------------------- + * MakeHandleUse -- + * Callback function for Lst_ForEach, used by Make_Run on the downward + * pass to handle .USE nodes. Should be called before the children + * are enqueued to be looked at by MakeAddChild. + * This function calls Make_HandleUse to copy the .USE node's commands, + * type flags and children to the parent node. + * + * Input: + * cgnp the child we've just examined + * pgnp the current parent + * + * Results: + * returns 0. + * + * Side Effects: + * After expansion, .USE child nodes are removed from the parent + * + *----------------------------------------------------------------------- + */ +static int +MakeHandleUse(void *cgnp, void *pgnp) +{ + GNode *cgn = (GNode *)cgnp; + GNode *pgn = (GNode *)pgnp; + LstNode ln; /* An element in the children list */ + int unmarked; + + unmarked = ((cgn->type & OP_MARK) == 0); + cgn->type |= OP_MARK; + + if ((cgn->type & (OP_USE|OP_USEBEFORE)) == 0) + return (0); + + if (unmarked) + Make_HandleUse(cgn, pgn); + + /* + * This child node is now "made", so we decrement the count of + * unmade children in the parent... We also remove the child + * from the parent's list to accurately reflect the number of decent + * children the parent has. This is used by Make_Run to decide + * whether to queue the parent or examine its children... + */ + if ((ln = Lst_Member(pgn->children, cgn)) != NULL) { + Lst_Remove(pgn->children, ln); + pgn->unmade--; + } + return (0); +} + + +/*- + *----------------------------------------------------------------------- + * Make_Recheck -- + * Check the modification time of a gnode, and update it as described + * in the comments below. + * + * Results: + * returns 0 if the gnode does not exist, or its filesystem + * time if it does. + * + * Side Effects: + * the gnode's modification time and path name are affected. + * + *----------------------------------------------------------------------- + */ +time_t +Make_Recheck(GNode *gn) +{ + time_t mtime = Dir_MTime(gn, 1); + +#ifndef RECHECK + /* + * We can't re-stat the thing, but we can at least take care of rules + * where a target depends on a source that actually creates the + * target, but only if it has changed, e.g. + * + * parse.h : parse.o + * + * parse.o : parse.y + * yacc -d parse.y + * cc -c y.tab.c + * mv y.tab.o parse.o + * cmp -s y.tab.h parse.h || mv y.tab.h parse.h + * + * In this case, if the definitions produced by yacc haven't changed + * from before, parse.h won't have been updated and gn->mtime will + * reflect the current modification time for parse.h. This is + * something of a kludge, I admit, but it's a useful one.. + * XXX: People like to use a rule like + * + * FRC: + * + * To force things that depend on FRC to be made, so we have to + * check for gn->children being empty as well... + */ + if (!Lst_IsEmpty(gn->commands) || Lst_IsEmpty(gn->children)) { + gn->mtime = now; + } +#else + /* + * This is what Make does and it's actually a good thing, as it + * allows rules like + * + * cmp -s y.tab.h parse.h || cp y.tab.h parse.h + * + * to function as intended. Unfortunately, thanks to the stateless + * nature of NFS (by which I mean the loose coupling of two clients + * using the same file from a common server), there are times + * when the modification time of a file created on a remote + * machine will not be modified before the local stat() implied by + * the Dir_MTime occurs, thus leading us to believe that the file + * is unchanged, wreaking havoc with files that depend on this one. + * + * I have decided it is better to make too much than to make too + * little, so this stuff is commented out unless you're sure it's ok. + * -- ardeb 1/12/88 + */ + /* + * Christos, 4/9/92: If we are saving commands pretend that + * the target is made now. Otherwise archives with ... rules + * don't work! + */ + if (NoExecute(gn) || (gn->type & OP_SAVE_CMDS) || + (mtime == 0 && !(gn->type & OP_WAIT))) { + if (DEBUG(MAKE)) { + fprintf(debug_file, " recheck(%s): update time from %s to now\n", + gn->name, Targ_FmtTime(gn->mtime)); + } + gn->mtime = now; + } + else { + if (DEBUG(MAKE)) { + fprintf(debug_file, " recheck(%s): current update time: %s\n", + gn->name, Targ_FmtTime(gn->mtime)); + } + } +#endif + return mtime; +} + +/*- + *----------------------------------------------------------------------- + * Make_Update -- + * Perform update on the parents of a node. Used by JobFinish once + * a node has been dealt with and by MakeStartJobs if it finds an + * up-to-date node. + * + * Input: + * cgn the child node + * + * Results: + * Always returns 0 + * + * Side Effects: + * The unmade field of pgn is decremented and pgn may be placed on + * the toBeMade queue if this field becomes 0. + * + * If the child was made, the parent's flag CHILDMADE field will be + * set true. + * + * If the child is not up-to-date and still does not exist, + * set the FORCE flag on the parents. + * + * If the child wasn't made, the cmgn field of the parent will be + * altered if the child's mtime is big enough. + * + * Finally, if the child is the implied source for the parent, the + * parent's IMPSRC variable is set appropriately. + * + *----------------------------------------------------------------------- + */ +void +Make_Update(GNode *cgn) +{ + GNode *pgn; /* the parent node */ + char *cname; /* the child's name */ + LstNode ln; /* Element in parents and iParents lists */ + time_t mtime = -1; + char *p1; + Lst parents; + GNode *centurion; + + /* It is save to re-examine any nodes again */ + checked++; + + cname = Var_Value(TARGET, cgn, &p1); + if (p1) + free(p1); + + if (DEBUG(MAKE)) + fprintf(debug_file, "Make_Update: %s%s\n", cgn->name, cgn->cohort_num); + + /* + * If the child was actually made, see what its modification time is + * now -- some rules won't actually update the file. If the file still + * doesn't exist, make its mtime now. + */ + if (cgn->made != UPTODATE) { + mtime = Make_Recheck(cgn); + } + + /* + * If this is a `::' node, we must consult its first instance + * which is where all parents are linked. + */ + if ((centurion = cgn->centurion) != NULL) { + if (!Lst_IsEmpty(cgn->parents)) + Punt("%s%s: cohort has parents", cgn->name, cgn->cohort_num); + centurion->unmade_cohorts -= 1; + if (centurion->unmade_cohorts < 0) + Error("Graph cycles through centurion %s", centurion->name); + } else { + centurion = cgn; + } + parents = centurion->parents; + + /* If this was a .ORDER node, schedule the RHS */ + Lst_ForEach(centurion->order_succ, MakeBuildParent, Lst_First(toBeMade)); + + /* Now mark all the parents as having one less unmade child */ + if (Lst_Open(parents) == SUCCESS) { + while ((ln = Lst_Next(parents)) != NULL) { + pgn = (GNode *)Lst_Datum(ln); + if (DEBUG(MAKE)) + fprintf(debug_file, "inspect parent %s%s: flags %x, " + "type %x, made %d, unmade %d ", + pgn->name, pgn->cohort_num, pgn->flags, + pgn->type, pgn->made, pgn->unmade-1); + + if (!(pgn->flags & REMAKE)) { + /* This parent isn't needed */ + if (DEBUG(MAKE)) + fprintf(debug_file, "- not needed\n"); + continue; + } + if (mtime == 0 && !(cgn->type & OP_WAIT)) + pgn->flags |= FORCE; + + /* + * If the parent has the .MADE attribute, its timestamp got + * updated to that of its newest child, and its unmake + * child count got set to zero in Make_ExpandUse(). + * However other things might cause us to build one of its + * children - and so we mustn't do any processing here when + * the child build finishes. + */ + if (pgn->type & OP_MADE) { + if (DEBUG(MAKE)) + fprintf(debug_file, "- .MADE\n"); + continue; + } + + if ( ! (cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE))) { + if (cgn->made == MADE) + pgn->flags |= CHILDMADE; + (void)Make_TimeStamp(pgn, cgn); + } + + /* + * A parent must wait for the completion of all instances + * of a `::' dependency. + */ + if (centurion->unmade_cohorts != 0 || centurion->made < MADE) { + if (DEBUG(MAKE)) + fprintf(debug_file, + "- centurion made %d, %d unmade cohorts\n", + centurion->made, centurion->unmade_cohorts); + continue; + } + + /* One more child of this parent is now made */ + pgn->unmade -= 1; + if (pgn->unmade < 0) { + if (DEBUG(MAKE)) { + fprintf(debug_file, "Graph cycles through %s%s\n", + pgn->name, pgn->cohort_num); + Targ_PrintGraph(2); + } + Error("Graph cycles through %s%s", pgn->name, pgn->cohort_num); + } + + /* We must always rescan the parents of .WAIT and .ORDER nodes. */ + if (pgn->unmade != 0 && !(centurion->type & OP_WAIT) + && !(centurion->flags & DONE_ORDER)) { + if (DEBUG(MAKE)) + fprintf(debug_file, "- unmade children\n"); + continue; + } + if (pgn->made != DEFERRED) { + /* + * Either this parent is on a different branch of the tree, + * or it on the RHS of a .WAIT directive + * or it is already on the toBeMade list. + */ + if (DEBUG(MAKE)) + fprintf(debug_file, "- not deferred\n"); + continue; + } + if (pgn->order_pred + && Lst_ForEach(pgn->order_pred, MakeCheckOrder, 0)) { + /* A .ORDER rule stops us building this */ + continue; + } + if (DEBUG(MAKE)) { + static int two = 2; + fprintf(debug_file, "- %s%s made, schedule %s%s (made %d)\n", + cgn->name, cgn->cohort_num, + pgn->name, pgn->cohort_num, pgn->made); + Targ_PrintNode(pgn, &two); + } + /* Ok, we can schedule the parent again */ + pgn->made = REQUESTED; + (void)Lst_EnQueue(toBeMade, pgn); + } + Lst_Close(parents); + } + + /* + * Set the .PREFIX and .IMPSRC variables for all the implied parents + * of this node. + */ + if (Lst_Open(cgn->iParents) == SUCCESS) { + char *cpref = Var_Value(PREFIX, cgn, &p1); + + while ((ln = Lst_Next(cgn->iParents)) != NULL) { + pgn = (GNode *)Lst_Datum(ln); + if (pgn->flags & REMAKE) { + Var_Set(IMPSRC, cname, pgn, 0); + if (cpref != NULL) + Var_Set(PREFIX, cpref, pgn, 0); + } + } + if (p1) + free(p1); + Lst_Close(cgn->iParents); + } +} + +/*- + *----------------------------------------------------------------------- + * MakeAddAllSrc -- + * Add a child's name to the ALLSRC and OODATE variables of the given + * node. Called from Make_DoAllVar via Lst_ForEach. A child is added only + * if it has not been given the .EXEC, .USE or .INVISIBLE attributes. + * .EXEC and .USE children are very rarely going to be files, so... + * If the child is a .JOIN node, its ALLSRC is propagated to the parent. + * + * A child is added to the OODATE variable if its modification time is + * later than that of its parent, as defined by Make, except if the + * parent is a .JOIN node. In that case, it is only added to the OODATE + * variable if it was actually made (since .JOIN nodes don't have + * modification times, the comparison is rather unfair...).. + * + * Results: + * Always returns 0 + * + * Side Effects: + * The ALLSRC variable for the given node is extended. + *----------------------------------------------------------------------- + */ +static int +MakeUnmark(void *cgnp, void *pgnp MAKE_ATTR_UNUSED) +{ + GNode *cgn = (GNode *)cgnp; + + cgn->type &= ~OP_MARK; + return (0); +} + +/* + * Input: + * cgnp The child to add + * pgnp The parent to whose ALLSRC variable it should + * be added + * + */ +static int +MakeAddAllSrc(void *cgnp, void *pgnp) +{ + GNode *cgn = (GNode *)cgnp; + GNode *pgn = (GNode *)pgnp; + + if (cgn->type & OP_MARK) + return (0); + cgn->type |= OP_MARK; + + if ((cgn->type & (OP_EXEC|OP_USE|OP_USEBEFORE|OP_INVISIBLE)) == 0) { + char *child, *allsrc; + char *p1 = NULL, *p2 = NULL; + + if (cgn->type & OP_ARCHV) + child = Var_Value(MEMBER, cgn, &p1); + else + child = cgn->path ? cgn->path : cgn->name; + if (cgn->type & OP_JOIN) { + allsrc = Var_Value(ALLSRC, cgn, &p2); + } else { + allsrc = child; + } + if (allsrc != NULL) + Var_Append(ALLSRC, allsrc, pgn); + if (p2) + free(p2); + if (pgn->type & OP_JOIN) { + if (cgn->made == MADE) { + Var_Append(OODATE, child, pgn); + } + } else if ((pgn->mtime < cgn->mtime) || + (cgn->mtime >= now && cgn->made == MADE)) + { + /* + * It goes in the OODATE variable if the parent is younger than the + * child or if the child has been modified more recently than + * the start of the make. This is to keep pmake from getting + * confused if something else updates the parent after the + * make starts (shouldn't happen, I know, but sometimes it + * does). In such a case, if we've updated the kid, the parent + * is likely to have a modification time later than that of + * the kid and anything that relies on the OODATE variable will + * be hosed. + * + * XXX: This will cause all made children to go in the OODATE + * variable, even if they're not touched, if RECHECK isn't defined, + * since cgn->mtime is set to now in Make_Update. According to + * some people, this is good... + */ + Var_Append(OODATE, child, pgn); + } + if (p1) + free(p1); + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Make_DoAllVar -- + * Set up the ALLSRC and OODATE variables. Sad to say, it must be + * done separately, rather than while traversing the graph. This is + * because Make defined OODATE to contain all sources whose modification + * times were later than that of the target, *not* those sources that + * were out-of-date. Since in both compatibility and native modes, + * the modification time of the parent isn't found until the child + * has been dealt with, we have to wait until now to fill in the + * variable. As for ALLSRC, the ordering is important and not + * guaranteed when in native mode, so it must be set here, too. + * + * Results: + * None + * + * Side Effects: + * The ALLSRC and OODATE variables of the given node is filled in. + * If the node is a .JOIN node, its TARGET variable will be set to + * match its ALLSRC variable. + *----------------------------------------------------------------------- + */ +void +Make_DoAllVar(GNode *gn) +{ + if (gn->flags & DONE_ALLSRC) + return; + + Lst_ForEach(gn->children, MakeUnmark, gn); + Lst_ForEach(gn->children, MakeAddAllSrc, gn); + + if (!Var_Exists (OODATE, gn)) { + Var_Set(OODATE, "", gn, 0); + } + if (!Var_Exists (ALLSRC, gn)) { + Var_Set(ALLSRC, "", gn, 0); + } + + if (gn->type & OP_JOIN) { + char *p1; + Var_Set(TARGET, Var_Value(ALLSRC, gn, &p1), gn, 0); + if (p1) + free(p1); + } + gn->flags |= DONE_ALLSRC; +} + +/*- + *----------------------------------------------------------------------- + * MakeStartJobs -- + * Start as many jobs as possible. + * + * Results: + * If the query flag was given to pmake, no job will be started, + * but as soon as an out-of-date target is found, this function + * returns TRUE. At all other times, this function returns FALSE. + * + * Side Effects: + * Nodes are removed from the toBeMade queue and job table slots + * are filled. + * + *----------------------------------------------------------------------- + */ + +static int +MakeCheckOrder(void *v_bn, void *ignore MAKE_ATTR_UNUSED) +{ + GNode *bn = v_bn; + + if (bn->made >= MADE || !(bn->flags & REMAKE)) + return 0; + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeCheckOrder: Waiting for .ORDER node %s%s\n", + bn->name, bn->cohort_num); + return 1; +} + +static int +MakeBuildChild(void *v_cn, void *toBeMade_next) +{ + GNode *cn = v_cn; + + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeBuildChild: inspect %s%s, made %d, type %x\n", + cn->name, cn->cohort_num, cn->made, cn->type); + if (cn->made > DEFERRED) + return 0; + + /* If this node is on the RHS of a .ORDER, check LHSs. */ + if (cn->order_pred && Lst_ForEach(cn->order_pred, MakeCheckOrder, 0)) { + /* Can't build this (or anything else in this child list) yet */ + cn->made = DEFERRED; + return 0; /* but keep looking */ + } + + if (DEBUG(MAKE)) + fprintf(debug_file, "MakeBuildChild: schedule %s%s\n", + cn->name, cn->cohort_num); + + cn->made = REQUESTED; + if (toBeMade_next == NULL) + Lst_AtEnd(toBeMade, cn); + else + Lst_InsertBefore(toBeMade, toBeMade_next, cn); + + if (cn->unmade_cohorts != 0) + Lst_ForEach(cn->cohorts, MakeBuildChild, toBeMade_next); + + /* + * If this node is a .WAIT node with unmade chlidren + * then don't add the next sibling. + */ + return cn->type & OP_WAIT && cn->unmade > 0; +} + +/* When a .ORDER LHS node completes we do this on each RHS */ +static int +MakeBuildParent(void *v_pn, void *toBeMade_next) +{ + GNode *pn = v_pn; + + if (pn->made != DEFERRED) + return 0; + + if (MakeBuildChild(pn, toBeMade_next) == 0) { + /* Mark so that when this node is built we reschedule its parents */ + pn->flags |= DONE_ORDER; + } + + return 0; +} + +static Boolean +MakeStartJobs(void) +{ + GNode *gn; + int have_token = 0; + + while (!Lst_IsEmpty (toBeMade)) { + /* Get token now to avoid cycling job-list when we only have 1 token */ + if (!have_token && !Job_TokenWithdraw()) + break; + have_token = 1; + + gn = (GNode *)Lst_DeQueue(toBeMade); + if (DEBUG(MAKE)) + fprintf(debug_file, "Examining %s%s...\n", + gn->name, gn->cohort_num); + + if (gn->made != REQUESTED) { + if (DEBUG(MAKE)) + fprintf(debug_file, "state %d\n", gn->made); + + make_abort(gn, __LINE__); + } + + if (gn->checked == checked) { + /* We've already looked at this node since a job finished... */ + if (DEBUG(MAKE)) + fprintf(debug_file, "already checked %s%s\n", + gn->name, gn->cohort_num); + gn->made = DEFERRED; + continue; + } + gn->checked = checked; + + if (gn->unmade != 0) { + /* + * We can't build this yet, add all unmade children to toBeMade, + * just before the current first element. + */ + gn->made = DEFERRED; + Lst_ForEach(gn->children, MakeBuildChild, Lst_First(toBeMade)); + /* and drop this node on the floor */ + if (DEBUG(MAKE)) + fprintf(debug_file, "dropped %s%s\n", gn->name, gn->cohort_num); + continue; + } + + gn->made = BEINGMADE; + if (Make_OODate(gn)) { + if (DEBUG(MAKE)) { + fprintf(debug_file, "out-of-date\n"); + } + if (queryFlag) { + return (TRUE); + } + Make_DoAllVar(gn); + Job_Make(gn); + have_token = 0; + } else { + if (DEBUG(MAKE)) { + fprintf(debug_file, "up-to-date\n"); + } + gn->made = UPTODATE; + if (gn->type & OP_JOIN) { + /* + * Even for an up-to-date .JOIN node, we need it to have its + * context variables so references to it get the correct + * value for .TARGET when building up the context variables + * of its parent(s)... + */ + Make_DoAllVar(gn); + } + Make_Update(gn); + } + } + + if (have_token) + Job_TokenReturn(); + + return (FALSE); +} + +/*- + *----------------------------------------------------------------------- + * MakePrintStatus -- + * Print the status of a top-level node, viz. it being up-to-date + * already or not created due to an error in a lower level. + * Callback function for Make_Run via Lst_ForEach. + * + * Input: + * gnp Node to examine + * cyclep True if gn->unmade being non-zero implies a + * cycle in the graph, not an error in an + * inferior. + * + * Results: + * Always returns 0. + * + * Side Effects: + * A message may be printed. + * + *----------------------------------------------------------------------- + */ +static int +MakePrintStatusOrder(void *ognp, void *gnp) +{ + GNode *ogn = ognp; + GNode *gn = gnp; + + if (!(ogn->flags & REMAKE) || ogn->made > REQUESTED) + /* not waiting for this one */ + return 0; + + printf(" `%s%s' has .ORDER dependency against %s%s " + "(made %d, flags %x, type %x)\n", + gn->name, gn->cohort_num, + ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); + if (DEBUG(MAKE) && debug_file != stdout) + fprintf(debug_file, " `%s%s' has .ORDER dependency against %s%s " + "(made %d, flags %x, type %x)\n", + gn->name, gn->cohort_num, + ogn->name, ogn->cohort_num, ogn->made, ogn->flags, ogn->type); + return 0; +} + +static int +MakePrintStatus(void *gnp, void *v_errors) +{ + GNode *gn = (GNode *)gnp; + int *errors = v_errors; + + if (gn->flags & DONECYCLE) + /* We've completely processed this node before, don't do it again. */ + return 0; + + if (gn->unmade == 0) { + gn->flags |= DONECYCLE; + switch (gn->made) { + case UPTODATE: + printf("`%s%s' is up to date.\n", gn->name, gn->cohort_num); + break; + case MADE: + break; + case UNMADE: + case DEFERRED: + case REQUESTED: + case BEINGMADE: + (*errors)++; + printf("`%s%s' was not built (made %d, flags %x, type %x)!\n", + gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); + if (DEBUG(MAKE) && debug_file != stdout) + fprintf(debug_file, + "`%s%s' was not built (made %d, flags %x, type %x)!\n", + gn->name, gn->cohort_num, gn->made, gn->flags, gn->type); + /* Most likely problem is actually caused by .ORDER */ + Lst_ForEach(gn->order_pred, MakePrintStatusOrder, gn); + break; + default: + /* Errors - already counted */ + printf("`%s%s' not remade because of errors.\n", + gn->name, gn->cohort_num); + if (DEBUG(MAKE) && debug_file != stdout) + fprintf(debug_file, "`%s%s' not remade because of errors.\n", + gn->name, gn->cohort_num); + break; + } + return 0; + } + + if (DEBUG(MAKE)) + fprintf(debug_file, "MakePrintStatus: %s%s has %d unmade children\n", + gn->name, gn->cohort_num, gn->unmade); + /* + * If printing cycles and came to one that has unmade children, + * print out the cycle by recursing on its children. + */ + if (!(gn->flags & CYCLE)) { + /* Fist time we've seen this node, check all children */ + gn->flags |= CYCLE; + Lst_ForEach(gn->children, MakePrintStatus, errors); + /* Mark that this node needn't be processed again */ + gn->flags |= DONECYCLE; + return 0; + } + + /* Only output the error once per node */ + gn->flags |= DONECYCLE; + Error("Graph cycles through `%s%s'", gn->name, gn->cohort_num); + if ((*errors)++ > 100) + /* Abandon the whole error report */ + return 1; + + /* Reporting for our children will give the rest of the loop */ + Lst_ForEach(gn->children, MakePrintStatus, errors); + return 0; +} + + +/*- + *----------------------------------------------------------------------- + * Make_ExpandUse -- + * Expand .USE nodes and create a new targets list + * + * Input: + * targs the initial list of targets + * + * Side Effects: + *----------------------------------------------------------------------- + */ +void +Make_ExpandUse(Lst targs) +{ + GNode *gn; /* a temporary pointer */ + Lst examine; /* List of targets to examine */ + + examine = Lst_Duplicate(targs, NULL); + + /* + * Make an initial downward pass over the graph, marking nodes to be made + * as we go down. We call Suff_FindDeps to find where a node is and + * to get some children for it if it has none and also has no commands. + * If the node is a leaf, we stick it on the toBeMade queue to + * be looked at in a minute, otherwise we add its children to our queue + * and go on about our business. + */ + while (!Lst_IsEmpty (examine)) { + gn = (GNode *)Lst_DeQueue(examine); + + if (gn->flags & REMAKE) + /* We've looked at this one already */ + continue; + gn->flags |= REMAKE; + if (DEBUG(MAKE)) + fprintf(debug_file, "Make_ExpandUse: examine %s%s\n", + gn->name, gn->cohort_num); + + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) { + /* Append all the 'cohorts' to the list of things to examine */ + Lst new; + new = Lst_Duplicate(gn->cohorts, NULL); + Lst_Concat(new, examine, LST_CONCLINK); + examine = new; + } + + /* + * Apply any .USE rules before looking for implicit dependencies + * to make sure everything has commands that should... + * Make sure that the TARGET is set, so that we can make + * expansions. + */ + if (gn->type & OP_ARCHV) { + char *eoa, *eon; + eoa = strchr(gn->name, '('); + eon = strchr(gn->name, ')'); + if (eoa == NULL || eon == NULL) + continue; + *eoa = '\0'; + *eon = '\0'; + Var_Set(MEMBER, eoa + 1, gn, 0); + Var_Set(ARCHIVE, gn->name, gn, 0); + *eoa = '('; + *eon = ')'; + } + + (void)Dir_MTime(gn, 0); + Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0); + Lst_ForEach(gn->children, MakeUnmark, gn); + Lst_ForEach(gn->children, MakeHandleUse, gn); + + if ((gn->type & OP_MADE) == 0) + Suff_FindDeps(gn); + else { + /* Pretend we made all this node's children */ + Lst_ForEach(gn->children, MakeFindChild, gn); + if (gn->unmade != 0) + printf("Warning: %s%s still has %d unmade children\n", + gn->name, gn->cohort_num, gn->unmade); + } + + if (gn->unmade != 0) + Lst_ForEach(gn->children, MakeAddChild, examine); + } + + Lst_Destroy(examine, NULL); +} + +/*- + *----------------------------------------------------------------------- + * Make_ProcessWait -- + * Convert .WAIT nodes into dependencies + * + * Input: + * targs the initial list of targets + * + *----------------------------------------------------------------------- + */ + +static int +link_parent(void *cnp, void *pnp) +{ + GNode *cn = cnp; + GNode *pn = pnp; + + Lst_AtEnd(pn->children, cn); + Lst_AtEnd(cn->parents, pn); + pn->unmade++; + return 0; +} + +static int +add_wait_dep(void *v_cn, void *v_wn) +{ + GNode *cn = v_cn; + GNode *wn = v_wn; + + if (cn == wn) + return 1; + + if (cn == NULL || wn == NULL) { + printf("bad wait dep %p %p\n", cn, wn); + exit(4); + } + if (DEBUG(MAKE)) + fprintf(debug_file, ".WAIT: add dependency %s%s -> %s\n", + cn->name, cn->cohort_num, wn->name); + + Lst_AtEnd(wn->children, cn); + wn->unmade++; + Lst_AtEnd(cn->parents, wn); + return 0; +} + +static void +Make_ProcessWait(Lst targs) +{ + GNode *pgn; /* 'parent' node we are examining */ + GNode *cgn; /* Each child in turn */ + LstNode owln; /* Previous .WAIT node */ + Lst examine; /* List of targets to examine */ + LstNode ln; + + /* + * We need all the nodes to have a common parent in order for the + * .WAIT and .ORDER scheduling to work. + * Perhaps this should be done earlier... + */ + + pgn = Targ_NewGN(".MAIN"); + pgn->flags = REMAKE; + pgn->type = OP_PHONY | OP_DEPENDS; + /* Get it displayed in the diag dumps */ + Lst_AtFront(Targ_List(), pgn); + + Lst_ForEach(targs, link_parent, pgn); + + /* Start building with the 'dummy' .MAIN' node */ + MakeBuildChild(pgn, NULL); + + examine = Lst_Init(FALSE); + Lst_AtEnd(examine, pgn); + + while (!Lst_IsEmpty (examine)) { + pgn = Lst_DeQueue(examine); + + /* We only want to process each child-list once */ + if (pgn->flags & DONE_WAIT) + continue; + pgn->flags |= DONE_WAIT; + if (DEBUG(MAKE)) + fprintf(debug_file, "Make_ProcessWait: examine %s\n", pgn->name); + + if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) { + /* Append all the 'cohorts' to the list of things to examine */ + Lst new; + new = Lst_Duplicate(pgn->cohorts, NULL); + Lst_Concat(new, examine, LST_CONCLINK); + examine = new; + } + + owln = Lst_First(pgn->children); + Lst_Open(pgn->children); + for (; (ln = Lst_Next(pgn->children)) != NULL; ) { + cgn = Lst_Datum(ln); + if (cgn->type & OP_WAIT) { + /* Make the .WAIT node depend on the previous children */ + Lst_ForEachFrom(pgn->children, owln, add_wait_dep, cgn); + owln = ln; + } else { + Lst_AtEnd(examine, cgn); + } + } + Lst_Close(pgn->children); + } + + Lst_Destroy(examine, NULL); +} + +/*- + *----------------------------------------------------------------------- + * Make_Run -- + * Initialize the nodes to remake and the list of nodes which are + * ready to be made by doing a breadth-first traversal of the graph + * starting from the nodes in the given list. Once this traversal + * is finished, all the 'leaves' of the graph are in the toBeMade + * queue. + * Using this queue and the Job module, work back up the graph, + * calling on MakeStartJobs to keep the job table as full as + * possible. + * + * Input: + * targs the initial list of targets + * + * Results: + * TRUE if work was done. FALSE otherwise. + * + * Side Effects: + * The make field of all nodes involved in the creation of the given + * targets is set to 1. The toBeMade list is set to contain all the + * 'leaves' of these subgraphs. + *----------------------------------------------------------------------- + */ +Boolean +Make_Run(Lst targs) +{ + int errors; /* Number of errors the Job module reports */ + + /* Start trying to make the current targets... */ + toBeMade = Lst_Init(FALSE); + + Make_ExpandUse(targs); + Make_ProcessWait(targs); + + if (DEBUG(MAKE)) { + fprintf(debug_file, "#***# full graph\n"); + Targ_PrintGraph(1); + } + + if (queryFlag) { + /* + * We wouldn't do any work unless we could start some jobs in the + * next loop... (we won't actually start any, of course, this is just + * to see if any of the targets was out of date) + */ + return (MakeStartJobs()); + } + /* + * Initialization. At the moment, no jobs are running and until some + * get started, nothing will happen since the remaining upward + * traversal of the graph is performed by the routines in job.c upon + * the finishing of a job. So we fill the Job table as much as we can + * before going into our loop. + */ + (void)MakeStartJobs(); + + /* + * Main Loop: The idea here is that the ending of jobs will take + * care of the maintenance of data structures and the waiting for output + * will cause us to be idle most of the time while our children run as + * much as possible. Because the job table is kept as full as possible, + * the only time when it will be empty is when all the jobs which need + * running have been run, so that is the end condition of this loop. + * Note that the Job module will exit if there were any errors unless the + * keepgoing flag was given. + */ + while (!Lst_IsEmpty(toBeMade) || jobTokensRunning > 0) { + Job_CatchOutput(); + (void)MakeStartJobs(); + } + + errors = Job_Finish(); + + /* + * Print the final status of each target. E.g. if it wasn't made + * because some inferior reported an error. + */ + if (DEBUG(MAKE)) + fprintf(debug_file, "done: errors %d\n", errors); + if (errors == 0) { + Lst_ForEach(targs, MakePrintStatus, &errors); + if (DEBUG(MAKE)) { + fprintf(debug_file, "done: errors %d\n", errors); + if (errors) + Targ_PrintGraph(4); + } + } + return errors != 0; +} diff --git a/buildrump.sh/src/usr.bin/make/make.h b/buildrump.sh/src/usr.bin/make/make.h new file mode 100644 index 00000000..caea30ad --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/make.h @@ -0,0 +1,506 @@ +/* $NetBSD: make.h,v 1.95 2014/09/07 20:55:34 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)make.h 8.3 (Berkeley) 6/13/95 + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)make.h 8.3 (Berkeley) 6/13/95 + */ + +/*- + * make.h -- + * The global definitions for pmake + */ + +#ifndef _MAKE_H_ +#define _MAKE_H_ + +#include <sys/types.h> +#include <sys/param.h> + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef BSD4_4 +# include <sys/cdefs.h> +#endif + +#if defined(__GNUC__) +#define MAKE_GNUC_PREREQ(x, y) \ + ((__GNUC__ == (x) && __GNUC_MINOR__ >= (y)) || \ + (__GNUC__ > (x))) +#else /* defined(__GNUC__) */ +#define MAKE_GNUC_PREREQ(x, y) 0 +#endif /* defined(__GNUC__) */ + +#if MAKE_GNUC_PREREQ(2, 7) +#define MAKE_ATTR_UNUSED __attribute__((__unused__)) +#else +#define MAKE_ATTR_UNUSED /* delete */ +#endif + +#if MAKE_GNUC_PREREQ(2, 5) +#define MAKE_ATTR_DEAD __attribute__((__noreturn__)) +#elif defined(__GNUC__) +#define MAKE_ATTR_DEAD __volatile +#else +#define MAKE_ATTR_DEAD /* delete */ +#endif + +#if MAKE_GNUC_PREREQ(2, 7) +#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#else +#define MAKE_ATTR_PRINTFLIKE(fmtarg, firstvararg) /* delete */ +#endif + +#include "sprite.h" +#include "lst.h" +#include "hash.h" +#include "config.h" +#include "buf.h" +#include "make_malloc.h" + +/*- + * The structure for an individual graph node. Each node has several + * pieces of data associated with it. + * 1) the name of the target it describes + * 2) the location of the target file in the file system. + * 3) the type of operator used to define its sources (qv. parse.c) + * 4) whether it is involved in this invocation of make + * 5) whether the target has been remade + * 6) whether any of its children has been remade + * 7) the number of its children that are, as yet, unmade + * 8) its modification time + * 9) the modification time of its youngest child (qv. make.c) + * 10) a list of nodes for which this is a source (parents) + * 11) a list of nodes on which this depends (children) + * 12) a list of nodes that depend on this, as gleaned from the + * transformation rules (iParents) + * 13) a list of ancestor nodes, which includes parents, iParents, + * and recursive parents of parents + * 14) a list of nodes of the same name created by the :: operator + * 15) a list of nodes that must be made (if they're made) before + * this node can be, but that do not enter into the datedness of + * this node. + * 16) a list of nodes that must be made (if they're made) before + * this node or any child of this node can be, but that do not + * enter into the datedness of this node. + * 17) a list of nodes that must be made (if they're made) after + * this node is, but that do not depend on this node, in the + * normal sense. + * 18) a Lst of ``local'' variables that are specific to this target + * and this target only (qv. var.c [$@ $< $?, etc.]) + * 19) a Lst of strings that are commands to be given to a shell + * to create this target. + */ +typedef struct GNode { + char *name; /* The target's name */ + char *uname; /* The unexpanded name of a .USE node */ + char *path; /* The full pathname of the file */ + int type; /* Its type (see the OP flags, below) */ + + int flags; +#define REMAKE 0x1 /* this target needs to be (re)made */ +#define CHILDMADE 0x2 /* children of this target were made */ +#define FORCE 0x4 /* children don't exist, and we pretend made */ +#define DONE_WAIT 0x8 /* Set by Make_ProcessWait() */ +#define DONE_ORDER 0x10 /* Build requested by .ORDER processing */ +#define FROM_DEPEND 0x20 /* Node created from .depend */ +#define DONE_ALLSRC 0x40 /* We do it once only */ +#define CYCLE 0x1000 /* Used by MakePrintStatus */ +#define DONECYCLE 0x2000 /* Used by MakePrintStatus */ + enum enum_made { + UNMADE, DEFERRED, REQUESTED, BEINGMADE, + MADE, UPTODATE, ERROR, ABORTED + } made; /* Set to reflect the state of processing + * on this node: + * UNMADE - Not examined yet + * DEFERRED - Examined once (building child) + * REQUESTED - on toBeMade list + * BEINGMADE - Target is already being made. + * Indicates a cycle in the graph. + * MADE - Was out-of-date and has been made + * UPTODATE - Was already up-to-date + * ERROR - An error occurred while it was being + * made (used only in compat mode) + * ABORTED - The target was aborted due to + * an error making an inferior (compat). + */ + int unmade; /* The number of unmade children */ + + time_t mtime; /* Its modification time */ + struct GNode *cmgn; /* The youngest child */ + + Lst iParents; /* Links to parents for which this is an + * implied source, if any */ + Lst cohorts; /* Other nodes for the :: operator */ + Lst parents; /* Nodes that depend on this one */ + Lst children; /* Nodes on which this one depends */ + Lst order_pred; /* .ORDER nodes we need made */ + Lst order_succ; /* .ORDER nodes who need us */ + + char cohort_num[8]; /* #n for this cohort */ + int unmade_cohorts;/* # of unmade instances on the + cohorts list */ + struct GNode *centurion; /* Pointer to the first instance of a :: + node; only set when on a cohorts list */ + unsigned int checked; /* Last time we tried to makle this node */ + + Hash_Table context; /* The local variables */ + Lst commands; /* Creation commands */ + + struct _Suff *suffix; /* Suffix for the node (determined by + * Suff_FindDeps and opaque to everyone + * but the Suff module) */ + const char *fname; /* filename where the GNode got defined */ + int lineno; /* line number where the GNode got defined */ +} GNode; + +/* + * The OP_ constants are used when parsing a dependency line as a way of + * communicating to other parts of the program the way in which a target + * should be made. These constants are bitwise-OR'ed together and + * placed in the 'type' field of each node. Any node that has + * a 'type' field which satisfies the OP_NOP function was never never on + * the lefthand side of an operator, though it may have been on the + * righthand side... + */ +#define OP_DEPENDS 0x00000001 /* Execution of commands depends on + * kids (:) */ +#define OP_FORCE 0x00000002 /* Always execute commands (!) */ +#define OP_DOUBLEDEP 0x00000004 /* Execution of commands depends on kids + * per line (::) */ +#define OP_OPMASK (OP_DEPENDS|OP_FORCE|OP_DOUBLEDEP) + +#define OP_OPTIONAL 0x00000008 /* Don't care if the target doesn't + * exist and can't be created */ +#define OP_USE 0x00000010 /* Use associated commands for parents */ +#define OP_EXEC 0x00000020 /* Target is never out of date, but always + * execute commands anyway. Its time + * doesn't matter, so it has none...sort + * of */ +#define OP_IGNORE 0x00000040 /* Ignore errors when creating the node */ +#define OP_PRECIOUS 0x00000080 /* Don't remove the target when + * interrupted */ +#define OP_SILENT 0x00000100 /* Don't echo commands when executed */ +#define OP_MAKE 0x00000200 /* Target is a recursive make so its + * commands should always be executed when + * it is out of date, regardless of the + * state of the -n or -t flags */ +#define OP_JOIN 0x00000400 /* Target is out-of-date only if any of its + * children was out-of-date */ +#define OP_MADE 0x00000800 /* Assume the children of the node have + * been already made */ +#define OP_SPECIAL 0x00001000 /* Special .BEGIN, .END, .INTERRUPT */ +#define OP_USEBEFORE 0x00002000 /* Like .USE, only prepend commands */ +#define OP_INVISIBLE 0x00004000 /* The node is invisible to its parents. + * I.e. it doesn't show up in the parents's + * local variables. */ +#define OP_NOTMAIN 0x00008000 /* The node is exempt from normal 'main + * target' processing in parse.c */ +#define OP_PHONY 0x00010000 /* Not a file target; run always */ +#define OP_NOPATH 0x00020000 /* Don't search for file in the path */ +#define OP_WAIT 0x00040000 /* .WAIT phony node */ +#define OP_NOMETA 0x00080000 /* .NOMETA do not create a .meta file */ +#define OP_META 0x00100000 /* .META we _do_ want a .meta file */ +#define OP_NOMETA_CMP 0x00200000 /* Do not compare commands in .meta file */ +#define OP_SUBMAKE 0x00400000 /* Possibly a submake node */ +/* Attributes applied by PMake */ +#define OP_TRANSFORM 0x80000000 /* The node is a transformation rule */ +#define OP_MEMBER 0x40000000 /* Target is a member of an archive */ +#define OP_LIB 0x20000000 /* Target is a library */ +#define OP_ARCHV 0x10000000 /* Target is an archive construct */ +#define OP_HAS_COMMANDS 0x08000000 /* Target has all the commands it should. + * Used when parsing to catch multiple + * commands for a target */ +#define OP_SAVE_CMDS 0x04000000 /* Saving commands on .END (Compat) */ +#define OP_DEPS_FOUND 0x02000000 /* Already processed by Suff_FindDeps */ +#define OP_MARK 0x01000000 /* Node found while expanding .ALLSRC */ + +#define NoExecute(gn) ((gn->type & OP_MAKE) ? noRecursiveExecute : noExecute) +/* + * OP_NOP will return TRUE if the node with the given type was not the + * object of a dependency operator + */ +#define OP_NOP(t) (((t) & OP_OPMASK) == 0x00000000) + +#define OP_NOTARGET (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM) + +/* + * The TARG_ constants are used when calling the Targ_FindNode and + * Targ_FindList functions in targ.c. They simply tell the functions what to + * do if the desired node(s) is (are) not found. If the TARG_CREATE constant + * is given, a new, empty node will be created for the target, placed in the + * table of all targets and its address returned. If TARG_NOCREATE is given, + * a NULL pointer will be returned. + */ +#define TARG_NOCREATE 0x00 /* don't create it */ +#define TARG_CREATE 0x01 /* create node if not found */ +#define TARG_NOHASH 0x02 /* don't look in/add to hash table */ + +/* + * These constants are all used by the Str_Concat function to decide how the + * final string should look. If STR_ADDSPACE is given, a space will be + * placed between the two strings. If STR_ADDSLASH is given, a '/' will + * be used instead of a space. If neither is given, no intervening characters + * will be placed between the two strings in the final output. If the + * STR_DOFREE bit is set, the two input strings will be freed before + * Str_Concat returns. + */ +#define STR_ADDSPACE 0x01 /* add a space when Str_Concat'ing */ +#define STR_ADDSLASH 0x02 /* add a slash when Str_Concat'ing */ + +/* + * Error levels for parsing. PARSE_FATAL means the process cannot continue + * once the makefile has been parsed. PARSE_WARNING means it can. Passed + * as the first argument to Parse_Error. + */ +#define PARSE_WARNING 2 +#define PARSE_FATAL 1 + +/* + * Values returned by Cond_Eval. + */ +#define COND_PARSE 0 /* Parse the next lines */ +#define COND_SKIP 1 /* Skip the next lines */ +#define COND_INVALID 2 /* Not a conditional statement */ + +/* + * Definitions for the "local" variables. Used only for clarity. + */ +#define TARGET "@" /* Target of dependency */ +#define OODATE "?" /* All out-of-date sources */ +#define ALLSRC ">" /* All sources */ +#define IMPSRC "<" /* Source implied by transformation */ +#define PREFIX "*" /* Common prefix */ +#define ARCHIVE "!" /* Archive in "archive(member)" syntax */ +#define MEMBER "%" /* Member in "archive(member)" syntax */ + +#define FTARGET "@F" /* file part of TARGET */ +#define DTARGET "@D" /* directory part of TARGET */ +#define FIMPSRC "<F" /* file part of IMPSRC */ +#define DIMPSRC "<D" /* directory part of IMPSRC */ +#define FPREFIX "*F" /* file part of PREFIX */ +#define DPREFIX "*D" /* directory part of PREFIX */ + +/* + * Global Variables + */ +extern Lst create; /* The list of target names specified on the + * command line. used to resolve #if + * make(...) statements */ +extern Lst dirSearchPath; /* The list of directories to search when + * looking for targets */ + +extern Boolean compatMake; /* True if we are make compatible */ +extern Boolean ignoreErrors; /* True if should ignore all errors */ +extern Boolean beSilent; /* True if should print no commands */ +extern Boolean noExecute; /* True if should execute nothing */ +extern Boolean noRecursiveExecute; /* True if should execute nothing */ +extern Boolean allPrecious; /* True if every target is precious */ +extern Boolean keepgoing; /* True if should continue on unaffected + * portions of the graph when have an error + * in one portion */ +extern Boolean touchFlag; /* TRUE if targets should just be 'touched' + * if out of date. Set by the -t flag */ +extern Boolean queryFlag; /* TRUE if we aren't supposed to really make + * anything, just see if the targets are out- + * of-date */ +extern Boolean doing_depend; /* TRUE if processing .depend */ + +extern Boolean checkEnvFirst; /* TRUE if environment should be searched for + * variables before the global context */ +extern Boolean jobServer; /* a jobServer already exists */ + +extern Boolean parseWarnFatal; /* TRUE if makefile parsing warnings are + * treated as errors */ + +extern Boolean varNoExportEnv; /* TRUE if we should not export variables + * set on the command line to the env. */ + +extern GNode *DEFAULT; /* .DEFAULT rule */ + +extern GNode *VAR_INTERNAL; /* Variables defined internally by make + * which should not override those set by + * makefiles. + */ +extern GNode *VAR_GLOBAL; /* Variables defined in a global context, e.g + * in the Makefile itself */ +extern GNode *VAR_CMD; /* Variables defined on the command line */ +extern GNode *VAR_FOR; /* Iteration variables */ +extern char var_Error[]; /* Value returned by Var_Parse when an error + * is encountered. It actually points to + * an empty string, so naive callers needn't + * worry about it. */ + +extern time_t now; /* The time at the start of this whole + * process */ + +extern Boolean oldVars; /* Do old-style variable substitution */ + +extern Lst sysIncPath; /* The system include path. */ +extern Lst defIncPath; /* The default include path. */ + +extern char curdir[]; /* Startup directory */ +extern char *progname; /* The program name */ +extern char *makeDependfile; /* .depend */ +extern char **savedEnv; /* if we replaced environ this will be non-NULL */ + +/* + * We cannot vfork() in a child of vfork(). + * Most systems do not enforce this but some do. + */ +#define vFork() ((getpid() == myPid) ? vfork() : fork()) +extern pid_t myPid; + +#define MAKEFLAGS ".MAKEFLAGS" +#define MAKEOVERRIDES ".MAKEOVERRIDES" +#define MAKE_JOB_PREFIX ".MAKE.JOB.PREFIX" /* prefix for job target output */ +#define MAKE_EXPORTED ".MAKE.EXPORTED" /* variables we export */ +#define MAKE_MAKEFILES ".MAKE.MAKEFILES" /* all the makefiles we read */ +#define MAKE_LEVEL ".MAKE.LEVEL" /* recursion level */ +#define MAKEFILE_PREFERENCE ".MAKE.MAKEFILE_PREFERENCE" +#define MAKE_DEPENDFILE ".MAKE.DEPENDFILE" /* .depend */ +#define MAKE_MODE ".MAKE.MODE" +#ifndef MAKE_LEVEL_ENV +# define MAKE_LEVEL_ENV "MAKELEVEL" +#endif + +/* + * debug control: + * There is one bit per module. It is up to the module what debug + * information to print. + */ +FILE *debug_file; /* Output written here - default stdout */ +extern int debug; +#define DEBUG_ARCH 0x00001 +#define DEBUG_COND 0x00002 +#define DEBUG_DIR 0x00004 +#define DEBUG_GRAPH1 0x00008 +#define DEBUG_GRAPH2 0x00010 +#define DEBUG_JOB 0x00020 +#define DEBUG_MAKE 0x00040 +#define DEBUG_SUFF 0x00080 +#define DEBUG_TARG 0x00100 +#define DEBUG_VAR 0x00200 +#define DEBUG_FOR 0x00400 +#define DEBUG_SHELL 0x00800 +#define DEBUG_ERROR 0x01000 +#define DEBUG_LOUD 0x02000 +#define DEBUG_META 0x04000 + +#define DEBUG_GRAPH3 0x10000 +#define DEBUG_SCRIPT 0x20000 +#define DEBUG_PARSE 0x40000 +#define DEBUG_CWD 0x80000 + +#define CONCAT(a,b) a##b + +#define DEBUG(module) (debug & CONCAT(DEBUG_,module)) + +#include "nonints.h" + +int Make_TimeStamp(GNode *, GNode *); +Boolean Make_OODate(GNode *); +void Make_ExpandUse(Lst); +time_t Make_Recheck(GNode *); +void Make_HandleUse(GNode *, GNode *); +void Make_Update(GNode *); +void Make_DoAllVar(GNode *); +Boolean Make_Run(Lst); +char * Check_Cwd_Cmd(const char *); +void Check_Cwd(const char **); +void PrintOnError(GNode *, const char *); +void Main_ExportMAKEFLAGS(Boolean); +Boolean Main_SetObjdir(const char *); +int mkTempFile(const char *, char **); +int str2Lst_Append(Lst, char *, const char *); + +#ifdef __GNUC__ +#define UNCONST(ptr) ({ \ + union __unconst { \ + const void *__cp; \ + void *__p; \ + } __d; \ + __d.__cp = ptr, __d.__p; }) +#else +#define UNCONST(ptr) (void *)(ptr) +#endif + +#ifndef MIN +#define MIN(a, b) ((a < b) ? a : b) +#endif +#ifndef MAX +#define MAX(a, b) ((a > b) ? a : b) +#endif + +#endif /* _MAKE_H_ */ diff --git a/buildrump.sh/src/usr.bin/make/make_malloc.c b/buildrump.sh/src/usr.bin/make/make_malloc.c new file mode 100644 index 00000000..b8ac23f8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/make_malloc.c @@ -0,0 +1,119 @@ +/* $NetBSD: make_malloc.c,v 1.10 2012/06/20 17:46:28 sjg Exp $ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifdef MAKE_NATIVE +#include <sys/cdefs.h> +__RCSID("$NetBSD: make_malloc.c,v 1.10 2012/06/20 17:46:28 sjg Exp $"); +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include "make.h" + +#ifndef USE_EMALLOC +static void enomem(void) MAKE_ATTR_DEAD; + +/* + * enomem -- + * die when out of memory. + */ +static void +enomem(void) +{ + (void)fprintf(stderr, "%s: %s.\n", progname, strerror(ENOMEM)); + exit(2); +} + +/* + * bmake_malloc -- + * malloc, but die on error. + */ +void * +bmake_malloc(size_t len) +{ + void *p; + + if ((p = malloc(len)) == NULL) + enomem(); + return(p); +} + +/* + * bmake_strdup -- + * strdup, but die on error. + */ +char * +bmake_strdup(const char *str) +{ + size_t len; + char *p; + + len = strlen(str) + 1; + if ((p = malloc(len)) == NULL) + enomem(); + return memcpy(p, str, len); +} + +/* + * bmake_strndup -- + * strndup, but die on error. + */ +char * +bmake_strndup(const char *str, size_t max_len) +{ + size_t len; + char *p; + + if (str == NULL) + return NULL; + + len = strlen(str); + if (len > max_len) + len = max_len; + p = bmake_malloc(len + 1); + memcpy(p, str, len); + p[len] = '\0'; + + return(p); +} + +/* + * bmake_realloc -- + * realloc, but die on error. + */ +void * +bmake_realloc(void *ptr, size_t size) +{ + if ((ptr = realloc(ptr, size)) == NULL) + enomem(); + return(ptr); +} +#endif diff --git a/buildrump.sh/src/usr.bin/make/make_malloc.h b/buildrump.sh/src/usr.bin/make/make_malloc.h new file mode 100644 index 00000000..36d3eff3 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/make_malloc.h @@ -0,0 +1,41 @@ +/* $NetBSD: make_malloc.h,v 1.4 2009/01/24 14:43:29 dsl Exp $ */ + +/*- + * Copyright (c) 2009 The NetBSD Foundation, Inc. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef USE_EMALLOC +void *bmake_malloc(size_t); +void *bmake_realloc(void *, size_t); +char *bmake_strdup(const char *); +char *bmake_strndup(const char *, size_t); +#else +#include <util.h> +#define bmake_malloc(x) emalloc(x) +#define bmake_realloc(x,y) erealloc(x,y) +#define bmake_strdup(x) estrdup(x) +#define bmake_strndup(x,y) estrndup(x,y) +#endif + diff --git a/buildrump.sh/src/usr.bin/make/meta.c b/buildrump.sh/src/usr.bin/make/meta.c new file mode 100644 index 00000000..19409d8d --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/meta.c @@ -0,0 +1,1462 @@ +/* $NetBSD: meta.c,v 1.38 2015/04/11 05:24:30 sjg Exp $ */ + +/* + * Implement 'meta' mode. + * Adapted from John Birrell's patches to FreeBSD make. + * --sjg + */ +/* + * Copyright (c) 2009-2010, Juniper Networks, Inc. + * Portions Copyright (c) 2009, John Birrell. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + */ +#if defined(USE_META) + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <fcntl.h> +#include <libgen.h> +#include <errno.h> +#if !defined(HAVE_CONFIG_H) || defined(HAVE_ERR_H) +#include <err.h> +#endif + +#include "make.h" +#include "job.h" + +#ifdef HAVE_FILEMON_H +# include <filemon.h> +#endif +#if !defined(USE_FILEMON) && defined(FILEMON_SET_FD) +# define USE_FILEMON +#endif + +static BuildMon Mybm; /* for compat */ +static Lst metaBailiwick; /* our scope of control */ +static Lst metaIgnorePaths; /* paths we deliberately ignore */ + +#ifndef MAKE_META_IGNORE_PATHS +#define MAKE_META_IGNORE_PATHS ".MAKE.META.IGNORE_PATHS" +#endif + +Boolean useMeta = FALSE; +static Boolean useFilemon = FALSE; +static Boolean writeMeta = FALSE; +static Boolean metaEnv = FALSE; /* don't save env unless asked */ +static Boolean metaVerbose = FALSE; +static Boolean metaIgnoreCMDs = FALSE; /* ignore CMDs in .meta files */ +static Boolean metaCurdirOk = FALSE; /* write .meta in .CURDIR Ok? */ +static Boolean metaSilent = FALSE; /* if we have a .meta be SILENT */ + +extern Boolean forceJobs; +extern Boolean comatMake; +extern char **environ; + +#define MAKE_META_PREFIX ".MAKE.META.PREFIX" + +#ifndef N2U +# define N2U(n, u) (((n) + ((u) - 1)) / (u)) +#endif +#ifndef ROUNDUP +# define ROUNDUP(n, u) (N2U((n), (u)) * (u)) +#endif + +#if !defined(HAVE_STRSEP) +# define strsep(s, d) stresep((s), (d), 0) +#endif + +/* + * Filemon is a kernel module which snoops certain syscalls. + * + * C chdir + * E exec + * F [v]fork + * L [sym]link + * M rename + * R read + * W write + * S stat + * + * See meta_oodate below - we mainly care about 'E' and 'R'. + * + * We can still use meta mode without filemon, but + * the benefits are more limited. + */ +#ifdef USE_FILEMON +# ifndef _PATH_FILEMON +# define _PATH_FILEMON "/dev/filemon" +# endif + +/* + * Open the filemon device. + */ +static void +filemon_open(BuildMon *pbm) +{ + int retry; + + pbm->mon_fd = pbm->filemon_fd = -1; + if (!useFilemon) + return; + + for (retry = 5; retry >= 0; retry--) { + if ((pbm->filemon_fd = open(_PATH_FILEMON, O_RDWR)) >= 0) + break; + } + + if (pbm->filemon_fd < 0) { + useFilemon = FALSE; + warn("Could not open %s", _PATH_FILEMON); + return; + } + + /* + * We use a file outside of '.' + * to avoid a FreeBSD kernel bug where unlink invalidates + * cwd causing getcwd to do a lot more work. + * We only care about the descriptor. + */ + pbm->mon_fd = mkTempFile("filemon.XXXXXX", NULL); + if (ioctl(pbm->filemon_fd, FILEMON_SET_FD, &pbm->mon_fd) < 0) { + err(1, "Could not set filemon file descriptor!"); + } + /* we don't need these once we exec */ + (void)fcntl(pbm->mon_fd, F_SETFD, 1); + (void)fcntl(pbm->filemon_fd, F_SETFD, 1); +} + +/* + * Read the build monitor output file and write records to the target's + * metadata file. + */ +static void +filemon_read(FILE *mfp, int fd) +{ + char buf[BUFSIZ]; + int n; + + /* Check if we're not writing to a meta data file.*/ + if (mfp == NULL) { + if (fd >= 0) + close(fd); /* not interested */ + return; + } + /* rewind */ + (void)lseek(fd, (off_t)0, SEEK_SET); + + fprintf(mfp, "\n-- filemon acquired metadata --\n"); + + while ((n = read(fd, buf, sizeof(buf))) > 0) { + fwrite(buf, 1, n, mfp); + } + fflush(mfp); + close(fd); +} +#endif + +/* + * when realpath() fails, + * we use this, to clean up ./ and ../ + */ +static void +eat_dots(char *buf, size_t bufsz, int dots) +{ + char *cp; + char *cp2; + const char *eat; + size_t eatlen; + + switch (dots) { + case 1: + eat = "/./"; + eatlen = 2; + break; + case 2: + eat = "/../"; + eatlen = 3; + break; + default: + return; + } + + do { + cp = strstr(buf, eat); + if (cp) { + cp2 = cp + eatlen; + if (dots == 2 && cp > buf) { + do { + cp--; + } while (cp > buf && *cp != '/'); + } + if (*cp == '/') { + strlcpy(cp, cp2, bufsz - (cp - buf)); + } else { + return; /* can't happen? */ + } + } + } while (cp); +} + +static char * +meta_name(struct GNode *gn, char *mname, size_t mnamelen, + const char *dname, + const char *tname) +{ + char buf[MAXPATHLEN]; + char cwd[MAXPATHLEN]; + char *rp; + char *cp; + char *tp; + char *p[4]; /* >= number of possible uses */ + int i; + + i = 0; + if (!dname) + dname = Var_Value(".OBJDIR", gn, &p[i++]); + if (!tname) + tname = Var_Value(TARGET, gn, &p[i++]); + + if (realpath(dname, cwd)) + dname = cwd; + + /* + * Weed out relative paths from the target file name. + * We have to be careful though since if target is a + * symlink, the result will be unstable. + * So we use realpath() just to get the dirname, and leave the + * basename as given to us. + */ + if ((cp = strrchr(tname, '/'))) { + if (realpath(tname, buf)) { + if ((rp = strrchr(buf, '/'))) { + rp++; + cp++; + if (strcmp(cp, rp) != 0) + strlcpy(rp, cp, sizeof(buf) - (rp - buf)); + } + tname = buf; + } else { + /* + * We likely have a directory which is about to be made. + * We pretend realpath() succeeded, to have a chance + * of generating the same meta file name that we will + * next time through. + */ + if (tname[0] == '/') { + strlcpy(buf, tname, sizeof(buf)); + } else { + snprintf(buf, sizeof(buf), "%s/%s", cwd, tname); + } + eat_dots(buf, sizeof(buf), 1); /* ./ */ + eat_dots(buf, sizeof(buf), 2); /* ../ */ + tname = buf; + } + } + /* on some systems dirname may modify its arg */ + tp = bmake_strdup(tname); + if (strcmp(dname, dirname(tp)) == 0) + snprintf(mname, mnamelen, "%s.meta", tname); + else { + snprintf(mname, mnamelen, "%s/%s.meta", dname, tname); + + /* + * Replace path separators in the file name after the + * current object directory path. + */ + cp = mname + strlen(dname) + 1; + + while (*cp != '\0') { + if (*cp == '/') + *cp = '_'; + cp++; + } + } + free(tp); + for (i--; i >= 0; i--) { + if (p[i]) + free(p[i]); + } + return (mname); +} + +/* + * Return true if running ${.MAKE} + * Bypassed if target is flagged .MAKE + */ +static int +is_submake(void *cmdp, void *gnp) +{ + static char *p_make = NULL; + static int p_len; + char *cmd = cmdp; + GNode *gn = gnp; + char *mp = NULL; + char *cp; + char *cp2; + int rc = 0; /* keep looking */ + + if (!p_make) { + p_make = Var_Value(".MAKE", gn, &cp); + p_len = strlen(p_make); + } + cp = strchr(cmd, '$'); + if ((cp)) { + mp = Var_Subst(NULL, cmd, gn, FALSE); + cmd = mp; + } + cp2 = strstr(cmd, p_make); + if ((cp2)) { + switch (cp2[p_len]) { + case '\0': + case ' ': + case '\t': + case '\n': + rc = 1; + break; + } + if (cp2 > cmd && rc > 0) { + switch (cp2[-1]) { + case ' ': + case '\t': + case '\n': + break; + default: + rc = 0; /* no match */ + break; + } + } + } + if (mp) + free(mp); + return (rc); +} + +typedef struct meta_file_s { + FILE *fp; + GNode *gn; +} meta_file_t; + +static int +printCMD(void *cmdp, void *mfpp) +{ + meta_file_t *mfp = mfpp; + char *cmd = cmdp; + char *cp = NULL; + + if (strchr(cmd, '$')) { + cmd = cp = Var_Subst(NULL, cmd, mfp->gn, FALSE); + } + fprintf(mfp->fp, "CMD %s\n", cmd); + if (cp) + free(cp); + return 0; +} + +/* + * Certain node types never get a .meta file + */ +#define SKIP_META_TYPE(_type) do { \ + if ((gn->type & __CONCAT(OP_, _type))) { \ + if (DEBUG(META)) { \ + fprintf(debug_file, "Skipping meta for %s: .%s\n", \ + gn->name, __STRING(_type)); \ + } \ + return (NULL); \ + } \ +} while (0) + +static FILE * +meta_create(BuildMon *pbm, GNode *gn) +{ + meta_file_t mf; + char buf[MAXPATHLEN]; + char objdir[MAXPATHLEN]; + char **ptr; + const char *dname; + const char *tname; + char *fname; + const char *cp; + char *p[4]; /* >= possible uses */ + int i; + struct stat fs; + + + /* This may be a phony node which we don't want meta data for... */ + /* Skip .meta for .BEGIN, .END, .ERROR etc as well. */ + /* Or it may be explicitly flagged as .NOMETA */ + SKIP_META_TYPE(NOMETA); + /* Unless it is explicitly flagged as .META */ + if (!(gn->type & OP_META)) { + SKIP_META_TYPE(PHONY); + SKIP_META_TYPE(SPECIAL); + SKIP_META_TYPE(MAKE); + } + + mf.fp = NULL; + + i = 0; + + dname = Var_Value(".OBJDIR", gn, &p[i++]); + tname = Var_Value(TARGET, gn, &p[i++]); + + /* The object directory may not exist. Check it.. */ + if (stat(dname, &fs) != 0) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: no .OBJDIR\n", + gn->name); + goto out; + } + /* Check if there are no commands to execute. */ + if (Lst_IsEmpty(gn->commands)) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: no commands\n", + gn->name); + goto out; + } + + /* make sure these are canonical */ + if (realpath(dname, objdir)) + dname = objdir; + + /* If we aren't in the object directory, don't create a meta file. */ + if (!metaCurdirOk && strcmp(curdir, dname) == 0) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: .OBJDIR == .CURDIR\n", + gn->name); + goto out; + } + if (!(gn->type & OP_META)) { + /* We do not generate .meta files for sub-makes */ + if (Lst_ForEach(gn->commands, is_submake, gn)) { + if (DEBUG(META)) + fprintf(debug_file, "Skipping meta for %s: .MAKE\n", + gn->name); + goto out; + } + } + + if (metaVerbose) { + char *mp; + + /* Describe the target we are building */ + mp = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", gn, 0); + if (*mp) + fprintf(stdout, "%s\n", mp); + free(mp); + } + /* Get the basename of the target */ + if ((cp = strrchr(tname, '/')) == NULL) { + cp = tname; + } else { + cp++; + } + + fflush(stdout); + + if (strcmp(cp, makeDependfile) == 0) + goto out; + + if (!writeMeta) + /* Don't create meta data. */ + goto out; + + fname = meta_name(gn, pbm->meta_fname, sizeof(pbm->meta_fname), + dname, tname); + +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_create: %s\n", fname); +#endif + + if ((mf.fp = fopen(fname, "w")) == NULL) + err(1, "Could not open meta file '%s'", fname); + + fprintf(mf.fp, "# Meta data file %s\n", fname); + + mf.gn = gn; + + Lst_ForEach(gn->commands, printCMD, &mf); + + fprintf(mf.fp, "CWD %s\n", getcwd(buf, sizeof(buf))); + fprintf(mf.fp, "TARGET %s\n", tname); + + if (metaEnv) { + for (ptr = environ; *ptr != NULL; ptr++) + fprintf(mf.fp, "ENV %s\n", *ptr); + } + + fprintf(mf.fp, "-- command output --\n"); + fflush(mf.fp); + + Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); + Var_Append(".MAKE.META.CREATED", fname, VAR_GLOBAL); + + gn->type |= OP_META; /* in case anyone wants to know */ + if (metaSilent) { + gn->type |= OP_SILENT; + } + out: + for (i--; i >= 0; i--) { + if (p[i]) + free(p[i]); + } + + return (mf.fp); +} + +static Boolean +boolValue(char *s) +{ + switch(*s) { + case '0': + case 'N': + case 'n': + case 'F': + case 'f': + return FALSE; + } + return TRUE; +} + +/* + * Initialization we need before reading makefiles. + */ +void +meta_init(void) +{ +#ifdef USE_FILEMON + /* this allows makefiles to test if we have filemon support */ + Var_Set(".MAKE.PATH_FILEMON", _PATH_FILEMON, VAR_GLOBAL, 0); +#endif +} + + +/* + * Initialization we need after reading makefiles. + */ +void +meta_mode_init(const char *make_mode) +{ + static int once = 0; + char *cp; + + useMeta = TRUE; + useFilemon = TRUE; + writeMeta = TRUE; + + if (make_mode) { + if (strstr(make_mode, "env")) + metaEnv = TRUE; + if (strstr(make_mode, "verb")) + metaVerbose = TRUE; + if (strstr(make_mode, "read")) + writeMeta = FALSE; + if (strstr(make_mode, "nofilemon")) + useFilemon = FALSE; + if ((cp = strstr(make_mode, "curdirok="))) { + metaCurdirOk = boolValue(&cp[9]); + } + if ((cp = strstr(make_mode, "silent="))) { + metaSilent = boolValue(&cp[7]); + } + if (strstr(make_mode, "ignore-cmd")) + metaIgnoreCMDs = TRUE; + /* for backwards compatability */ + Var_Set(".MAKE.META_CREATED", "${.MAKE.META.CREATED}", VAR_GLOBAL, 0); + Var_Set(".MAKE.META_FILES", "${.MAKE.META.FILES}", VAR_GLOBAL, 0); + } + if (metaVerbose && !Var_Exists(MAKE_META_PREFIX, VAR_GLOBAL)) { + /* + * The default value for MAKE_META_PREFIX + * prints the absolute path of the target. + * This works be cause :H will generate '.' if there is no / + * and :tA will resolve that to cwd. + */ + Var_Set(MAKE_META_PREFIX, "Building ${.TARGET:H:tA}/${.TARGET:T}", VAR_GLOBAL, 0); + } + if (once) + return; + once = 1; + memset(&Mybm, 0, sizeof(Mybm)); + /* + * We consider ourselves master of all within ${.MAKE.META.BAILIWICK} + */ + metaBailiwick = Lst_Init(FALSE); + cp = Var_Subst(NULL, "${.MAKE.META.BAILIWICK:O:u:tA}", VAR_GLOBAL, 0); + if (cp) { + str2Lst_Append(metaBailiwick, cp, NULL); + } + /* + * We ignore any paths that start with ${.MAKE.META.IGNORE_PATHS} + */ + metaIgnorePaths = Lst_Init(FALSE); + Var_Append(MAKE_META_IGNORE_PATHS, + "/dev /etc /proc /tmp /var/run /var/tmp ${TMPDIR}", VAR_GLOBAL); + cp = Var_Subst(NULL, + "${" MAKE_META_IGNORE_PATHS ":O:u:tA}", VAR_GLOBAL, 0); + if (cp) { + str2Lst_Append(metaIgnorePaths, cp, NULL); + } +} + +/* + * In each case below we allow for job==NULL + */ +void +meta_job_start(Job *job, GNode *gn) +{ + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + pbm->mfp = meta_create(pbm, gn); +#ifdef USE_FILEMON_ONCE + /* compat mode we open the filemon dev once per command */ + if (job == NULL) + return; +#endif +#ifdef USE_FILEMON + if (pbm->mfp != NULL && useFilemon) { + filemon_open(pbm); + } else { + pbm->mon_fd = pbm->filemon_fd = -1; + } +#endif +} + +/* + * The child calls this before doing anything. + * It does not disturb our state. + */ +void +meta_job_child(Job *job) +{ +#ifdef USE_FILEMON + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + close(fileno(pbm->mfp)); + if (useFilemon) { + pid_t pid; + + pid = getpid(); + if (ioctl(pbm->filemon_fd, FILEMON_SET_PID, &pid) < 0) { + err(1, "Could not set filemon pid!"); + } + } + } +#endif +} + +void +meta_job_error(Job *job, GNode *gn, int flags, int status) +{ + char cwd[MAXPATHLEN]; + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + if (!gn) + gn = job->node; + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + fprintf(pbm->mfp, "*** Error code %d%s\n", + status, + (flags & JOB_IGNERR) ? + "(ignored)" : ""); + } + if (gn) { + Var_Set(".ERROR_TARGET", gn->path ? gn->path : gn->name, VAR_GLOBAL, 0); + } + getcwd(cwd, sizeof(cwd)); + Var_Set(".ERROR_CWD", cwd, VAR_GLOBAL, 0); + if (pbm && pbm->meta_fname[0]) { + Var_Set(".ERROR_META_FILE", pbm->meta_fname, VAR_GLOBAL, 0); + } + meta_job_finish(job); +} + +void +meta_job_output(Job *job, char *cp, const char *nl) +{ + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + if (metaVerbose) { + static char *meta_prefix = NULL; + static int meta_prefix_len; + + if (!meta_prefix) { + char *cp2; + + meta_prefix = Var_Subst(NULL, "${" MAKE_META_PREFIX "}", VAR_GLOBAL, 0); + if ((cp2 = strchr(meta_prefix, '$'))) + meta_prefix_len = cp2 - meta_prefix; + else + meta_prefix_len = strlen(meta_prefix); + } + if (strncmp(cp, meta_prefix, meta_prefix_len) == 0) { + cp = strchr(cp+1, '\n'); + if (!cp++) + return; + } + } + fprintf(pbm->mfp, "%s%s", cp, nl); + } +} + +void +meta_cmd_finish(void *pbmp) +{ +#ifdef USE_FILEMON + BuildMon *pbm = pbmp; + + if (!pbm) + pbm = &Mybm; + + if (pbm->filemon_fd >= 0) { + close(pbm->filemon_fd); + filemon_read(pbm->mfp, pbm->mon_fd); + pbm->filemon_fd = pbm->mon_fd = -1; + } +#endif +} + +void +meta_job_finish(Job *job) +{ + BuildMon *pbm; + + if (job != NULL) { + pbm = &job->bm; + } else { + pbm = &Mybm; + } + if (pbm->mfp != NULL) { + meta_cmd_finish(pbm); + fclose(pbm->mfp); + pbm->mfp = NULL; + pbm->meta_fname[0] = '\0'; + } +} + +/* + * Fetch a full line from fp - growing bufp if needed + * Return length in bufp. + */ +static int +fgetLine(char **bufp, size_t *szp, int o, FILE *fp) +{ + char *buf = *bufp; + size_t bufsz = *szp; + struct stat fs; + int x; + + if (fgets(&buf[o], bufsz - o, fp) != NULL) { + check_newline: + x = o + strlen(&buf[o]); + if (buf[x - 1] == '\n') + return x; + /* + * We need to grow the buffer. + * The meta file can give us a clue. + */ + if (fstat(fileno(fp), &fs) == 0) { + size_t newsz; + char *p; + + newsz = ROUNDUP((fs.st_size / 2), BUFSIZ); + if (newsz <= bufsz) + newsz = ROUNDUP(fs.st_size, BUFSIZ); + if (DEBUG(META)) + fprintf(debug_file, "growing buffer %zu -> %zu\n", + bufsz, newsz); + p = bmake_realloc(buf, newsz); + if (p) { + *bufp = buf = p; + *szp = bufsz = newsz; + /* fetch the rest */ + if (!fgets(&buf[x], bufsz - x, fp)) + return x; /* truncated! */ + goto check_newline; + } + } + } + return 0; +} + +static int +prefix_match(void *p, void *q) +{ + const char *prefix = p; + const char *path = q; + size_t n = strlen(prefix); + + return (0 == strncmp(path, prefix, n)); +} + +static int +string_match(const void *p, const void *q) +{ + const char *p1 = p; + const char *p2 = q; + + return strcmp(p1, p2); +} + + +/* + * When running with 'meta' functionality, a target can be out-of-date + * if any of the references in its meta data file is more recent. + * We have to track the latestdir on a per-process basis. + */ +#define LCWD_VNAME_FMT ".meta.%d.lcwd" +#define LDIR_VNAME_FMT ".meta.%d.ldir" + +/* + * It is possible that a .meta file is corrupted, + * if we detect this we want to reproduce it. + * Setting oodate TRUE will have that effect. + */ +#define CHECK_VALID_META(p) if (!(p && *p)) { \ + warnx("%s: %d: malformed", fname, lineno); \ + oodate = TRUE; \ + continue; \ + } + +#define DEQUOTE(p) if (*p == '\'') { \ + char *ep; \ + p++; \ + if ((ep = strchr(p, '\''))) \ + *ep = '\0'; \ + } + +Boolean +meta_oodate(GNode *gn, Boolean oodate) +{ + static char *tmpdir = NULL; + static char cwd[MAXPATHLEN]; + char lcwd_vname[64]; + char ldir_vname[64]; + char lcwd[MAXPATHLEN]; + char latestdir[MAXPATHLEN]; + char fname[MAXPATHLEN]; + char fname1[MAXPATHLEN]; + char fname2[MAXPATHLEN]; + char fname3[MAXPATHLEN]; + char *p; + char *cp; + char *link_src; + char *move_target; + static size_t cwdlen = 0; + static size_t tmplen = 0; + FILE *fp; + Boolean needOODATE = FALSE; + Lst missingFiles; + + if (oodate) + return oodate; /* we're done */ + + missingFiles = Lst_Init(FALSE); + + /* + * We need to check if the target is out-of-date. This includes + * checking if the expanded command has changed. This in turn + * requires that all variables are set in the same way that they + * would be if the target needs to be re-built. + */ + Make_DoAllVar(gn); + + meta_name(gn, fname, sizeof(fname), NULL, NULL); + +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_oodate: %s\n", fname); +#endif + + if ((fp = fopen(fname, "r")) != NULL) { + static char *buf = NULL; + static size_t bufsz; + int lineno = 0; + int lastpid = 0; + int pid; + int f = 0; + int x; + LstNode ln; + struct stat fs; + + if (!buf) { + bufsz = 8 * BUFSIZ; + buf = bmake_malloc(bufsz); + } + + if (!cwdlen) { + if (getcwd(cwd, sizeof(cwd)) == NULL) + err(1, "Could not get current working directory"); + cwdlen = strlen(cwd); + } + strlcpy(lcwd, cwd, sizeof(lcwd)); + strlcpy(latestdir, cwd, sizeof(latestdir)); + + if (!tmpdir) { + tmpdir = getTmpdir(); + tmplen = strlen(tmpdir); + } + + /* we want to track all the .meta we read */ + Var_Append(".MAKE.META.FILES", fname, VAR_GLOBAL); + + ln = Lst_First(gn->commands); + while (!oodate && (x = fgetLine(&buf, &bufsz, 0, fp)) > 0) { + lineno++; + if (buf[x - 1] == '\n') + buf[x - 1] = '\0'; + else { + warnx("%s: %d: line truncated at %u", fname, lineno, x); + oodate = TRUE; + break; + } + link_src = NULL; + move_target = NULL; + /* Find the start of the build monitor section. */ + if (!f) { + if (strncmp(buf, "-- filemon", 10) == 0) { + f = 1; + continue; + } + if (strncmp(buf, "# buildmon", 10) == 0) { + f = 1; + continue; + } + } + + /* Delimit the record type. */ + p = buf; +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: %s\n", fname, lineno, buf); +#endif + strsep(&p, " "); + if (f) { + /* + * We are in the 'filemon' output section. + * Each record from filemon follows the general form: + * + * <key> <pid> <data> + * + * Where: + * <key> is a single letter, denoting the syscall. + * <pid> is the process that made the syscall. + * <data> is the arguments (of interest). + */ + switch(buf[0]) { + case '#': /* comment */ + case 'V': /* version */ + break; + default: + /* + * We need to track pathnames per-process. + * + * Each process run by make, starts off in the 'CWD' + * recorded in the .meta file, if it chdirs ('C') + * elsewhere we need to track that - but only for + * that process. If it forks ('F'), we initialize + * the child to have the same cwd as its parent. + * + * We also need to track the 'latestdir' of + * interest. This is usually the same as cwd, but + * not if a process is reading directories. + * + * Each time we spot a different process ('pid') + * we save the current value of 'latestdir' in a + * variable qualified by 'lastpid', and + * re-initialize 'latestdir' to any pre-saved + * value for the current 'pid' and 'CWD' if none. + */ + CHECK_VALID_META(p); + pid = atoi(p); + if (pid > 0 && pid != lastpid) { + char *ldir; + char *tp; + + if (lastpid > 0) { + /* We need to remember these. */ + Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0); + Var_Set(ldir_vname, latestdir, VAR_GLOBAL, 0); + } + snprintf(lcwd_vname, sizeof(lcwd_vname), LCWD_VNAME_FMT, pid); + snprintf(ldir_vname, sizeof(ldir_vname), LDIR_VNAME_FMT, pid); + lastpid = pid; + ldir = Var_Value(ldir_vname, VAR_GLOBAL, &tp); + if (ldir) { + strlcpy(latestdir, ldir, sizeof(latestdir)); + if (tp) + free(tp); + } + ldir = Var_Value(lcwd_vname, VAR_GLOBAL, &tp); + if (ldir) { + strlcpy(lcwd, ldir, sizeof(lcwd)); + if (tp) + free(tp); + } + } + /* Skip past the pid. */ + if (strsep(&p, " ") == NULL) + continue; +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: %d: %c: cwd=%s lcwd=%s ldir=%s\n", + fname, lineno, + pid, buf[0], cwd, lcwd, latestdir); +#endif + break; + } + + CHECK_VALID_META(p); + + /* Process according to record type. */ + switch (buf[0]) { + case 'X': /* eXit */ + Var_Delete(lcwd_vname, VAR_GLOBAL); + Var_Delete(ldir_vname, VAR_GLOBAL); + lastpid = 0; /* no need to save ldir_vname */ + break; + + case 'F': /* [v]Fork */ + { + char cldir[64]; + int child; + + child = atoi(p); + if (child > 0) { + snprintf(cldir, sizeof(cldir), LCWD_VNAME_FMT, child); + Var_Set(cldir, lcwd, VAR_GLOBAL, 0); + snprintf(cldir, sizeof(cldir), LDIR_VNAME_FMT, child); + Var_Set(cldir, latestdir, VAR_GLOBAL, 0); +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: %d: cwd=%s lcwd=%s ldir=%s\n", + fname, lineno, + child, cwd, lcwd, latestdir); +#endif + } + } + break; + + case 'C': /* Chdir */ + /* Update lcwd and latest directory. */ + strlcpy(latestdir, p, sizeof(latestdir)); + strlcpy(lcwd, p, sizeof(lcwd)); + Var_Set(lcwd_vname, lcwd, VAR_GLOBAL, 0); + Var_Set(ldir_vname, lcwd, VAR_GLOBAL, 0); +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: cwd=%s ldir=%s\n", fname, lineno, cwd, lcwd); +#endif + break; + + case 'M': /* renaMe */ + /* + * For 'M'oves we want to check + * the src as for 'R'ead + * and the target as for 'W'rite. + */ + cp = p; /* save this for a second */ + /* now get target */ + if (strsep(&p, " ") == NULL) + continue; + CHECK_VALID_META(p); + move_target = p; + p = cp; + /* 'L' and 'M' put single quotes around the args */ + DEQUOTE(p); + DEQUOTE(move_target); + /* FALLTHROUGH */ + case 'D': /* unlink */ + if (*p == '/' && !Lst_IsEmpty(missingFiles)) { + /* remove p from the missingFiles list if present */ + if ((ln = Lst_Find(missingFiles, p, string_match)) != NULL) { + char *tp = Lst_Datum(ln); + Lst_Remove(missingFiles, ln); + free(tp); + ln = NULL; /* we're done with it */ + } + } + if (buf[0] == 'M') { + /* the target of the mv is a file 'W'ritten */ +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_oodate: M %s -> %s\n", + p, move_target); +#endif + p = move_target; + goto check_write; + } + break; + case 'L': /* Link */ + /* + * For 'L'inks check + * the src as for 'R'ead + * and the target as for 'W'rite. + */ + link_src = p; + /* now get target */ + if (strsep(&p, " ") == NULL) + continue; + CHECK_VALID_META(p); + /* 'L' and 'M' put single quotes around the args */ + DEQUOTE(p); + DEQUOTE(link_src); +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_oodate: L %s -> %s\n", + link_src, p); +#endif + /* FALLTHROUGH */ + case 'W': /* Write */ + check_write: + /* + * If a file we generated within our bailiwick + * but outside of .OBJDIR is missing, + * we need to do it again. + */ + /* ignore non-absolute paths */ + if (*p != '/') + break; + + if (Lst_IsEmpty(metaBailiwick)) + break; + + /* ignore cwd - normal dependencies handle those */ + if (strncmp(p, cwd, cwdlen) == 0) + break; + + if (!Lst_ForEach(metaBailiwick, prefix_match, p)) + break; + + /* tmpdir might be within */ + if (tmplen > 0 && strncmp(p, tmpdir, tmplen) == 0) + break; + + /* ignore anything containing the string "tmp" */ + if ((strstr("tmp", p))) + break; + + if (stat(p, &fs) < 0) { + Lst_AtEnd(missingFiles, bmake_strdup(p)); + } + break; + check_link_src: + p = link_src; + link_src = NULL; +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_oodate: L src %s\n", p); +#endif + /* FALLTHROUGH */ + case 'R': /* Read */ + case 'E': /* Exec */ + /* + * Check for runtime files that can't + * be part of the dependencies because + * they are _expected_ to change. + */ + if (*p == '/' && + Lst_ForEach(metaIgnorePaths, prefix_match, p)) { +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "meta_oodate: ignoring: %s\n", + p); +#endif + break; + } + + if ((cp = strrchr(p, '/'))) { + cp++; + /* + * We don't normally expect to see this, + * but we do expect it to change. + */ + if (strcmp(cp, makeDependfile) == 0) + break; + } + + /* + * The rest of the record is the file name. + * Check if it's not an absolute path. + */ + { + char *sdirs[4]; + char **sdp; + int sdx = 0; + int found = 0; + + if (*p == '/') { + sdirs[sdx++] = p; /* done */ + } else { + if (strcmp(".", p) == 0) + continue; /* no point */ + + /* Check vs latestdir */ + snprintf(fname1, sizeof(fname1), "%s/%s", latestdir, p); + sdirs[sdx++] = fname1; + + if (strcmp(latestdir, lcwd) != 0) { + /* Check vs lcwd */ + snprintf(fname2, sizeof(fname2), "%s/%s", lcwd, p); + sdirs[sdx++] = fname2; + } + if (strcmp(lcwd, cwd) != 0) { + /* Check vs cwd */ + snprintf(fname3, sizeof(fname3), "%s/%s", cwd, p); + sdirs[sdx++] = fname3; + } + } + sdirs[sdx++] = NULL; + + for (sdp = sdirs; *sdp && !found; sdp++) { +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: looking for: %s\n", fname, lineno, *sdp); +#endif + if (stat(*sdp, &fs) == 0) { + found = 1; + p = *sdp; + } + } + if (found) { +#ifdef DEBUG_META_MODE + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: found: %s\n", fname, lineno, p); +#endif + if (!S_ISDIR(fs.st_mode) && + fs.st_mtime > gn->mtime) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: file '%s' is newer than the target...\n", fname, lineno, p); + oodate = TRUE; + } else if (S_ISDIR(fs.st_mode)) { + /* Update the latest directory. */ + realpath(p, latestdir); + } + } else if (errno == ENOENT && *p == '/' && + strncmp(p, cwd, cwdlen) != 0) { + /* + * A referenced file outside of CWD is missing. + * We cannot catch every eventuality here... + */ + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: file '%s' may have moved?...\n", fname, lineno, p); + oodate = TRUE; + } + } + if (buf[0] == 'E') { + /* previous latestdir is no longer relevant */ + strlcpy(latestdir, lcwd, sizeof(latestdir)); + } + break; + default: + break; + } + if (!oodate && buf[0] == 'L' && link_src != NULL) + goto check_link_src; + } else if (strcmp(buf, "CMD") == 0) { + /* + * Compare the current command with the one in the + * meta data file. + */ + if (ln == NULL) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: there were more build commands in the meta data file than there are now...\n", fname, lineno); + oodate = TRUE; + } else { + char *cmd = (char *)Lst_Datum(ln); + Boolean hasOODATE = FALSE; + + if (strstr(cmd, "$?")) + hasOODATE = TRUE; + else if ((cp = strstr(cmd, ".OODATE"))) { + /* check for $[{(].OODATE[:)}] */ + if (cp > cmd + 2 && cp[-2] == '$') + hasOODATE = TRUE; + } + if (hasOODATE) { + needOODATE = TRUE; + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: cannot compare command using .OODATE\n", fname, lineno); + } + cmd = Var_Subst(NULL, cmd, gn, TRUE); + + if ((cp = strchr(cmd, '\n'))) { + int n; + + /* + * This command contains newlines, we need to + * fetch more from the .meta file before we + * attempt a comparison. + */ + /* first put the newline back at buf[x - 1] */ + buf[x - 1] = '\n'; + do { + /* now fetch the next line */ + if ((n = fgetLine(&buf, &bufsz, x, fp)) <= 0) + break; + x = n; + lineno++; + if (buf[x - 1] != '\n') { + warnx("%s: %d: line truncated at %u", fname, lineno, x); + break; + } + cp = strchr(++cp, '\n'); + } while (cp); + if (buf[x - 1] == '\n') + buf[x - 1] = '\0'; + } + if (!hasOODATE && + !(gn->type & OP_NOMETA_CMP) && + strcmp(p, cmd) != 0) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: a build command has changed\n%s\nvs\n%s\n", fname, lineno, p, cmd); + if (!metaIgnoreCMDs) + oodate = TRUE; + } + free(cmd); + ln = Lst_Succ(ln); + } + } else if (strcmp(buf, "CWD") == 0) { + /* + * Check if there are extra commands now + * that weren't in the meta data file. + */ + if (!oodate && ln != NULL) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: there are extra build commands now that weren't in the meta data file\n", fname, lineno); + oodate = TRUE; + } + if (strcmp(p, cwd) != 0) { + if (DEBUG(META)) + fprintf(debug_file, "%s: %d: the current working directory has changed from '%s' to '%s'\n", fname, lineno, p, curdir); + oodate = TRUE; + } + } + } + + fclose(fp); + if (!Lst_IsEmpty(missingFiles)) { + if (DEBUG(META)) + fprintf(debug_file, "%s: missing files: %s...\n", + fname, (char *)Lst_Datum(Lst_First(missingFiles))); + oodate = TRUE; + Lst_Destroy(missingFiles, (FreeProc *)free); + } + } else { + if ((gn->type & OP_META)) { + if (DEBUG(META)) + fprintf(debug_file, "%s: required but missing\n", fname); + oodate = TRUE; + } + } + if (oodate && needOODATE) { + /* + * Target uses .OODATE which is empty; or we wouldn't be here. + * We have decided it is oodate, so .OODATE needs to be set. + * All we can sanely do is set it to .ALLSRC. + */ + Var_Delete(OODATE, gn); + Var_Set(OODATE, Var_Value(ALLSRC, gn, &cp), gn, 0); + if (cp) + free(cp); + } + return oodate; +} + +/* support for compat mode */ + +static int childPipe[2]; + +void +meta_compat_start(void) +{ +#ifdef USE_FILEMON_ONCE + /* + * We need to re-open filemon for each cmd. + */ + BuildMon *pbm = &Mybm; + + if (pbm->mfp != NULL && useFilemon) { + filemon_open(pbm); + } else { + pbm->mon_fd = pbm->filemon_fd = -1; + } +#endif + if (pipe(childPipe) < 0) + Punt("Cannot create pipe: %s", strerror(errno)); + /* Set close-on-exec flag for both */ + (void)fcntl(childPipe[0], F_SETFD, 1); + (void)fcntl(childPipe[1], F_SETFD, 1); +} + +void +meta_compat_child(void) +{ + meta_job_child(NULL); + if (dup2(childPipe[1], 1) < 0 || + dup2(1, 2) < 0) { + execError("dup2", "pipe"); + _exit(1); + } +} + +void +meta_compat_parent(void) +{ + FILE *fp; + char buf[BUFSIZ]; + + close(childPipe[1]); /* child side */ + fp = fdopen(childPipe[0], "r"); + while (fgets(buf, sizeof(buf), fp)) { + meta_job_output(NULL, buf, ""); + printf("%s", buf); + } + fclose(fp); +} + +#endif /* USE_META */ diff --git a/buildrump.sh/src/usr.bin/make/meta.h b/buildrump.sh/src/usr.bin/make/meta.h new file mode 100644 index 00000000..57c73ca9 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/meta.h @@ -0,0 +1,55 @@ +/* $NetBSD: meta.h,v 1.3 2013/03/23 05:31:29 sjg Exp $ */ + +/* + * Things needed for 'meta' mode. + */ +/* + * Copyright (c) 2009-2010, Juniper Networks, Inc. + * + * 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 copyright holders 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 COPYRIGHT HOLDERS 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 COPYRIGHT + * OWNER 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. + */ + +typedef struct BuildMon { + char meta_fname[MAXPATHLEN]; + int filemon_fd; + int mon_fd; + FILE *mfp; +} BuildMon; + +extern Boolean useMeta; + +struct Job; /* not defined yet */ +void meta_init(void); +void meta_mode_init(const char *); +void meta_job_start(struct Job *, GNode *); +void meta_job_child(struct Job *); +void meta_job_error(struct Job *, GNode *, int, int); +void meta_job_output(struct Job *, char *, const char *); +void meta_cmd_finish(void *); +void meta_job_finish(struct Job *); +Boolean meta_oodate(GNode *, Boolean); +void meta_compat_start(void); +void meta_compat_child(void); +void meta_compat_parent(void); diff --git a/buildrump.sh/src/usr.bin/make/nonints.h b/buildrump.sh/src/usr.bin/make/nonints.h new file mode 100644 index 00000000..742d3f05 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/nonints.h @@ -0,0 +1,197 @@ +/* $NetBSD: nonints.h,v 1.68 2015/05/05 21:51:09 sjg Exp $ */ + +/*- + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94 + */ + +/*- + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)nonints.h 8.3 (Berkeley) 3/19/94 + */ + +/* arch.c */ +ReturnStatus Arch_ParseArchive(char **, Lst, GNode *); +void Arch_Touch(GNode *); +void Arch_TouchLib(GNode *); +time_t Arch_MTime(GNode *); +time_t Arch_MemMTime(GNode *); +void Arch_FindLib(GNode *, Lst); +Boolean Arch_LibOODate(GNode *); +void Arch_Init(void); +void Arch_End(void); +int Arch_IsLib(GNode *); + +/* compat.c */ +int CompatRunCommand(void *, void *); +void Compat_Run(Lst); +int Compat_Make(void *, void *); + +/* cond.c */ +struct If; +int Cond_EvalExpression(const struct If *, char *, Boolean *, int, Boolean); +int Cond_Eval(char *); +void Cond_restore_depth(unsigned int); +unsigned int Cond_save_depth(void); + +/* for.c */ +int For_Eval(char *); +int For_Accum(char *); +void For_Run(int); + +/* job.c */ +void JobReapChild(pid_t, int, Boolean); + +/* main.c */ +void Main_ParseArgLine(const char *); +void MakeMode(const char *); +int main(int, char **); +char *Cmd_Exec(const char *, const char **); +void Error(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2); +void Fatal(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; +void Punt(const char *, ...) MAKE_ATTR_PRINTFLIKE(1, 2) MAKE_ATTR_DEAD; +void DieHorribly(void) MAKE_ATTR_DEAD; +int PrintAddr(void *, void *); +void Finish(int) MAKE_ATTR_DEAD; +int eunlink(const char *); +void execError(const char *, const char *); +char *getTmpdir(void); +Boolean getBoolean(const char *, Boolean); + +/* parse.c */ +void Parse_Error(int, const char *, ...) MAKE_ATTR_PRINTFLIKE(2, 3); +Boolean Parse_AnyExport(void); +Boolean Parse_IsVar(char *); +void Parse_DoVar(char *, GNode *); +void Parse_AddIncludeDir(char *); +void Parse_File(const char *, int); +void Parse_Init(void); +void Parse_End(void); +void Parse_SetInput(const char *, int, int, char *(*)(void *, size_t *), void *); +Lst Parse_MainName(void); + +/* str.c */ +char *str_concat(const char *, const char *, int); +char **brk_string(const char *, int *, Boolean, char **); +char *Str_FindSubstring(const char *, const char *); +int Str_Match(const char *, const char *); +char *Str_SYSVMatch(const char *, const char *, int *len); +void Str_SYSVSubst(Buffer *, char *, char *, int); + +/* suff.c */ +void Suff_ClearSuffixes(void); +Boolean Suff_IsTransform(char *); +GNode *Suff_AddTransform(char *); +int Suff_EndTransform(void *, void *); +void Suff_AddSuffix(char *, GNode **); +Lst Suff_GetPath(char *); +void Suff_DoPaths(void); +void Suff_AddInclude(char *); +void Suff_AddLib(char *); +void Suff_FindDeps(GNode *); +Lst Suff_FindPath(GNode *); +void Suff_SetNull(char *); +void Suff_Init(void); +void Suff_End(void); +void Suff_PrintAll(void); + +/* targ.c */ +void Targ_Init(void); +void Targ_End(void); +Lst Targ_List(void); +GNode *Targ_NewGN(const char *); +GNode *Targ_FindNode(const char *, int); +Lst Targ_FindList(Lst, int); +Boolean Targ_Ignore(GNode *); +Boolean Targ_Silent(GNode *); +Boolean Targ_Precious(GNode *); +void Targ_SetMain(GNode *); +int Targ_PrintCmd(void *, void *); +int Targ_PrintNode(void *, void *); +char *Targ_FmtTime(time_t); +void Targ_PrintType(int); +void Targ_PrintGraph(int); +void Targ_Propagate(void); +void Targ_Propagate_Wait(void); + +/* var.c */ +void Var_Delete(const char *, GNode *); +void Var_Set(const char *, const char *, GNode *, int); +void Var_Append(const char *, const char *, GNode *); +Boolean Var_Exists(const char *, GNode *); +char *Var_Value(const char *, GNode *, char **); +char *Var_Parse(const char *, GNode *, Boolean, int *, void **); +char *Var_Subst(const char *, const char *, GNode *, Boolean); +char *Var_GetTail(const char *); +char *Var_GetHead(const char *); +void Var_Init(void); +void Var_End(void); +void Var_Dump(GNode *); +void Var_ExportVars(void); +void Var_Export(char *, int); +void Var_UnExport(char *); + +/* util.c */ +void (*bmake_signal(int, void (*)(int)))(int); diff --git a/buildrump.sh/src/usr.bin/make/parse.c b/buildrump.sh/src/usr.bin/make/parse.c new file mode 100644 index 00000000..204073ae --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/parse.c @@ -0,0 +1,3258 @@ +/* $NetBSD: parse.c,v 1.204 2014/09/18 08:06:13 dholland Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: parse.c,v 1.204 2014/09/18 08:06:13 dholland Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parse.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: parse.c,v 1.204 2014/09/18 08:06:13 dholland Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * parse.c -- + * Functions to parse a makefile. + * + * One function, Parse_Init, must be called before any functions + * in this module are used. After that, the function Parse_File is the + * main entry point and controls most of the other functions in this + * module. + * + * Most important structures are kept in Lsts. Directories for + * the .include "..." function are kept in the 'parseIncPath' Lst, while + * those for the .include <...> are kept in the 'sysIncPath' Lst. The + * targets currently being defined are kept in the 'targets' Lst. + * + * The variables 'fname' and 'lineno' are used to track the name + * of the current file and the line number in that file so that error + * messages can be more meaningful. + * + * Interface: + * Parse_Init Initialization function which must be + * called before anything else in this module + * is used. + * + * Parse_End Cleanup the module + * + * Parse_File Function used to parse a makefile. It must + * be given the name of the file, which should + * already have been opened, and a function + * to call to read a character from the file. + * + * Parse_IsVar Returns TRUE if the given line is a + * variable assignment. Used by MainParseArgs + * to determine if an argument is a target + * or a variable assignment. Used internally + * for pretty much the same thing... + * + * Parse_Error Function called when an error occurs in + * parsing. Used by the variable and + * conditional modules. + * Parse_MainName Returns a Lst of the main target to create. + */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> + +#ifndef MAP_FILE +#define MAP_FILE 0 +#endif +#ifndef MAP_COPY +#define MAP_COPY MAP_PRIVATE +#endif + +#include "make.h" +#include "hash.h" +#include "dir.h" +#include "job.h" +#include "buf.h" +#include "pathnames.h" + +//////////////////////////////////////////////////////////// +// types and constants + +/* + * Structure for a file being read ("included file") + */ +typedef struct IFile { + char *fname; /* name of file */ + int lineno; /* current line number in file */ + int first_lineno; /* line number of start of text */ + int cond_depth; /* 'if' nesting when file opened */ + char *P_str; /* point to base of string buffer */ + char *P_ptr; /* point to next char of string buffer */ + char *P_end; /* point to the end of string buffer */ + char *(*nextbuf)(void *, size_t *); /* Function to get more data */ + void *nextbuf_arg; /* Opaque arg for nextbuf() */ + struct loadedfile *lf; /* loadedfile object, if any */ +} IFile; + + +/* + * These values are returned by ParseEOF to tell Parse_File whether to + * CONTINUE parsing, i.e. it had only reached the end of an include file, + * or if it's DONE. + */ +#define CONTINUE 1 +#define DONE 0 + +/* + * Tokens for target attributes + */ +typedef enum { + Begin, /* .BEGIN */ + Default, /* .DEFAULT */ + End, /* .END */ + dotError, /* .ERROR */ + Ignore, /* .IGNORE */ + Includes, /* .INCLUDES */ + Interrupt, /* .INTERRUPT */ + Libs, /* .LIBS */ + Meta, /* .META */ + MFlags, /* .MFLAGS or .MAKEFLAGS */ + Main, /* .MAIN and we don't have anything user-specified to + * make */ + NoExport, /* .NOEXPORT */ + NoMeta, /* .NOMETA */ + NoMetaCmp, /* .NOMETA_CMP */ + NoPath, /* .NOPATH */ + Not, /* Not special */ + NotParallel, /* .NOTPARALLEL */ + Null, /* .NULL */ + ExObjdir, /* .OBJDIR */ + Order, /* .ORDER */ + Parallel, /* .PARALLEL */ + ExPath, /* .PATH */ + Phony, /* .PHONY */ +#ifdef POSIX + Posix, /* .POSIX */ +#endif + Precious, /* .PRECIOUS */ + ExShell, /* .SHELL */ + Silent, /* .SILENT */ + SingleShell, /* .SINGLESHELL */ + Stale, /* .STALE */ + Suffixes, /* .SUFFIXES */ + Wait, /* .WAIT */ + Attribute /* Generic attribute */ +} ParseSpecial; + +/* + * Other tokens + */ +#define LPAREN '(' +#define RPAREN ')' + + +//////////////////////////////////////////////////////////// +// result data + +/* + * The main target to create. This is the first target on the first + * dependency line in the first makefile. + */ +static GNode *mainNode; + +//////////////////////////////////////////////////////////// +// eval state + +/* targets we're working on */ +static Lst targets; + +#ifdef CLEANUP +/* command lines for targets */ +static Lst targCmds; +#endif + +/* + * specType contains the SPECial TYPE of the current target. It is + * Not if the target is unspecial. If it *is* special, however, the children + * are linked as children of the parent but not vice versa. This variable is + * set in ParseDoDependency + */ +static ParseSpecial specType; + +/* + * Predecessor node for handling .ORDER. Initialized to NULL when .ORDER + * seen, then set to each successive source on the line. + */ +static GNode *predecessor; + +//////////////////////////////////////////////////////////// +// parser state + +/* true if currently in a dependency line or its commands */ +static Boolean inLine; + +/* number of fatal errors */ +static int fatals = 0; + +/* + * Variables for doing includes + */ + +/* current file being read */ +static IFile *curFile; + +/* stack of IFiles generated by .includes */ +static Lst includes; + +/* include paths (lists of directories) */ +Lst parseIncPath; /* dirs for "..." includes */ +Lst sysIncPath; /* dirs for <...> includes */ +Lst defIncPath; /* default for sysIncPath */ + +//////////////////////////////////////////////////////////// +// parser tables + +/* + * The parseKeywords table is searched using binary search when deciding + * if a target or source is special. The 'spec' field is the ParseSpecial + * type of the keyword ("Not" if the keyword isn't special as a target) while + * the 'op' field is the operator to apply to the list of targets if the + * keyword is used as a source ("0" if the keyword isn't special as a source) + */ +static const struct { + const char *name; /* Name of keyword */ + ParseSpecial spec; /* Type when used as a target */ + int op; /* Operator when used as a source */ +} parseKeywords[] = { +{ ".BEGIN", Begin, 0 }, +{ ".DEFAULT", Default, 0 }, +{ ".END", End, 0 }, +{ ".ERROR", dotError, 0 }, +{ ".EXEC", Attribute, OP_EXEC }, +{ ".IGNORE", Ignore, OP_IGNORE }, +{ ".INCLUDES", Includes, 0 }, +{ ".INTERRUPT", Interrupt, 0 }, +{ ".INVISIBLE", Attribute, OP_INVISIBLE }, +{ ".JOIN", Attribute, OP_JOIN }, +{ ".LIBS", Libs, 0 }, +{ ".MADE", Attribute, OP_MADE }, +{ ".MAIN", Main, 0 }, +{ ".MAKE", Attribute, OP_MAKE }, +{ ".MAKEFLAGS", MFlags, 0 }, +{ ".META", Meta, OP_META }, +{ ".MFLAGS", MFlags, 0 }, +{ ".NOMETA", NoMeta, OP_NOMETA }, +{ ".NOMETA_CMP", NoMetaCmp, OP_NOMETA_CMP }, +{ ".NOPATH", NoPath, OP_NOPATH }, +{ ".NOTMAIN", Attribute, OP_NOTMAIN }, +{ ".NOTPARALLEL", NotParallel, 0 }, +{ ".NO_PARALLEL", NotParallel, 0 }, +{ ".NULL", Null, 0 }, +{ ".OBJDIR", ExObjdir, 0 }, +{ ".OPTIONAL", Attribute, OP_OPTIONAL }, +{ ".ORDER", Order, 0 }, +{ ".PARALLEL", Parallel, 0 }, +{ ".PATH", ExPath, 0 }, +{ ".PHONY", Phony, OP_PHONY }, +#ifdef POSIX +{ ".POSIX", Posix, 0 }, +#endif +{ ".PRECIOUS", Precious, OP_PRECIOUS }, +{ ".RECURSIVE", Attribute, OP_MAKE }, +{ ".SHELL", ExShell, 0 }, +{ ".SILENT", Silent, OP_SILENT }, +{ ".SINGLESHELL", SingleShell, 0 }, +{ ".STALE", Stale, 0 }, +{ ".SUFFIXES", Suffixes, 0 }, +{ ".USE", Attribute, OP_USE }, +{ ".USEBEFORE", Attribute, OP_USEBEFORE }, +{ ".WAIT", Wait, 0 }, +}; + +//////////////////////////////////////////////////////////// +// local functions + +static int ParseIsEscaped(const char *, const char *); +static void ParseErrorInternal(const char *, size_t, int, const char *, ...) + MAKE_ATTR_PRINTFLIKE(4,5); +static void ParseVErrorInternal(FILE *, const char *, size_t, int, const char *, va_list) + MAKE_ATTR_PRINTFLIKE(5, 0); +static int ParseFindKeyword(const char *); +static int ParseLinkSrc(void *, void *); +static int ParseDoOp(void *, void *); +static void ParseDoSrc(int, const char *); +static int ParseFindMain(void *, void *); +static int ParseAddDir(void *, void *); +static int ParseClearPath(void *, void *); +static void ParseDoDependency(char *); +static int ParseAddCmd(void *, void *); +static void ParseHasCommands(void *); +static void ParseDoInclude(char *); +static void ParseSetParseFile(const char *); +static void ParseSetIncludedFile(void); +#ifdef SYSVINCLUDE +static void ParseTraditionalInclude(char *); +#endif +#ifdef GMAKEEXPORT +static void ParseGmakeExport(char *); +#endif +static int ParseEOF(void); +static char *ParseReadLine(void); +static void ParseFinishLine(void); +static void ParseMark(GNode *); + +//////////////////////////////////////////////////////////// +// file loader + +struct loadedfile { + const char *path; /* name, for error reports */ + char *buf; /* contents buffer */ + size_t len; /* length of contents */ + size_t maplen; /* length of mmap area, or 0 */ + Boolean used; /* XXX: have we used the data yet */ +}; + +/* + * Constructor/destructor for loadedfile + */ +static struct loadedfile * +loadedfile_create(const char *path) +{ + struct loadedfile *lf; + + lf = bmake_malloc(sizeof(*lf)); + lf->path = (path == NULL ? "(stdin)" : path); + lf->buf = NULL; + lf->len = 0; + lf->maplen = 0; + lf->used = FALSE; + return lf; +} + +static void +loadedfile_destroy(struct loadedfile *lf) +{ + if (lf->buf != NULL) { + if (lf->maplen > 0) { + munmap(lf->buf, lf->maplen); + } else { + free(lf->buf); + } + } + free(lf); +} + +/* + * nextbuf() operation for loadedfile, as needed by the weird and twisted + * logic below. Once that's cleaned up, we can get rid of lf->used... + */ +static char * +loadedfile_nextbuf(void *x, size_t *len) +{ + struct loadedfile *lf = x; + + if (lf->used) { + return NULL; + } + lf->used = TRUE; + *len = lf->len; + return lf->buf; +} + +/* + * Try to get the size of a file. + */ +static ReturnStatus +load_getsize(int fd, size_t *ret) +{ + struct stat st; + + if (fstat(fd, &st) < 0) { + return FAILURE; + } + + if (!S_ISREG(st.st_mode)) { + return FAILURE; + } + + /* + * st_size is an off_t, which is 64 bits signed; *ret is + * size_t, which might be 32 bits unsigned or 64 bits + * unsigned. Rather than being elaborate, just punt on + * files that are more than 2^31 bytes. We should never + * see a makefile that size in practice... + * + * While we're at it reject negative sizes too, just in case. + */ + if (st.st_size < 0 || st.st_size > 0x7fffffff) { + return FAILURE; + } + + *ret = (size_t) st.st_size; + return SUCCESS; +} + +/* + * Read in a file. + * + * Until the path search logic can be moved under here instead of + * being in the caller in another source file, we need to have the fd + * passed in already open. Bleh. + * + * If the path is NULL use stdin and (to insure against fd leaks) + * assert that the caller passed in -1. + */ +static struct loadedfile * +loadfile(const char *path, int fd) +{ + struct loadedfile *lf; + long pagesize; + ssize_t result; + size_t bufpos; + + lf = loadedfile_create(path); + + if (path == NULL) { + assert(fd == -1); + fd = STDIN_FILENO; + } else { +#if 0 /* notyet */ + fd = open(path, O_RDONLY); + if (fd < 0) { + ... + Error("%s: %s", path, strerror(errno)); + exit(1); + } +#endif + } + + if (load_getsize(fd, &lf->len) == SUCCESS) { + /* found a size, try mmap */ + pagesize = sysconf(_SC_PAGESIZE); + if (pagesize <= 0) { + pagesize = 0x1000; + } + /* round size up to a page */ + lf->maplen = pagesize * ((lf->len + pagesize - 1)/pagesize); + + /* + * XXX hack for dealing with empty files; remove when + * we're no longer limited by interfacing to the old + * logic elsewhere in this file. + */ + if (lf->maplen == 0) { + lf->maplen = pagesize; + } + + /* + * FUTURE: remove PROT_WRITE when the parser no longer + * needs to scribble on the input. + */ + lf->buf = mmap(NULL, lf->maplen, PROT_READ|PROT_WRITE, + MAP_FILE|MAP_COPY, fd, 0); + if (lf->buf != MAP_FAILED) { + /* succeeded */ + if (lf->len == lf->maplen && lf->buf[lf->len - 1] != '\n') { + char *b = malloc(lf->len + 1); + b[lf->len] = '\n'; + memcpy(b, lf->buf, lf->len++); + munmap(lf->buf, lf->maplen); + lf->maplen = 0; + lf->buf = b; + } + goto done; + } + } + + /* cannot mmap; load the traditional way */ + + lf->maplen = 0; + lf->len = 1024; + lf->buf = bmake_malloc(lf->len); + + bufpos = 0; + while (1) { + assert(bufpos <= lf->len); + if (bufpos == lf->len) { + lf->len *= 2; + lf->buf = bmake_realloc(lf->buf, lf->len); + } + result = read(fd, lf->buf + bufpos, lf->len - bufpos); + if (result < 0) { + Error("%s: read error: %s", path, strerror(errno)); + exit(1); + } + if (result == 0) { + break; + } + bufpos += result; + } + assert(bufpos <= lf->len); + lf->len = bufpos; + + /* truncate malloc region to actual length (maybe not useful) */ + if (lf->len > 0) { + lf->buf = bmake_realloc(lf->buf, lf->len); + } + +done: + if (path != NULL) { + close(fd); + } + return lf; +} + +//////////////////////////////////////////////////////////// +// old code + +/*- + *---------------------------------------------------------------------- + * ParseIsEscaped -- + * Check if the current character is escaped on the current line + * + * Results: + * 0 if the character is not backslash escaped, 1 otherwise + * + * Side Effects: + * None + *---------------------------------------------------------------------- + */ +static int +ParseIsEscaped(const char *line, const char *c) +{ + int active = 0; + for (;;) { + if (line == c) + return active; + if (*--c != '\\') + return active; + active = !active; + } +} + +/*- + *---------------------------------------------------------------------- + * ParseFindKeyword -- + * Look in the table of keywords for one matching the given string. + * + * Input: + * str String to find + * + * Results: + * The index of the keyword, or -1 if it isn't there. + * + * Side Effects: + * None + *---------------------------------------------------------------------- + */ +static int +ParseFindKeyword(const char *str) +{ + int start, end, cur; + int diff; + + start = 0; + end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; + + do { + cur = start + ((end - start) / 2); + diff = strcmp(str, parseKeywords[cur].name); + + if (diff == 0) { + return (cur); + } else if (diff < 0) { + end = cur - 1; + } else { + start = cur + 1; + } + } while (start <= end); + return (-1); +} + +/*- + * ParseVErrorInternal -- + * Error message abort function for parsing. Prints out the context + * of the error (line number and file) as well as the message with + * two optional arguments. + * + * Results: + * None + * + * Side Effects: + * "fatals" is incremented if the level is PARSE_FATAL. + */ +/* VARARGS */ +static void +ParseVErrorInternal(FILE *f, const char *cfname, size_t clineno, int type, + const char *fmt, va_list ap) +{ + static Boolean fatal_warning_error_printed = FALSE; + + (void)fprintf(f, "%s: ", progname); + + if (cfname != NULL) { + (void)fprintf(f, "\""); + if (*cfname != '/' && strcmp(cfname, "(stdin)") != 0) { + char *cp; + const char *dir; + + /* + * Nothing is more annoying than not knowing + * which Makefile is the culprit. + */ + dir = Var_Value(".PARSEDIR", VAR_GLOBAL, &cp); + if (dir == NULL || *dir == '\0' || + (*dir == '.' && dir[1] == '\0')) + dir = Var_Value(".CURDIR", VAR_GLOBAL, &cp); + if (dir == NULL) + dir = "."; + + (void)fprintf(f, "%s/%s", dir, cfname); + } else + (void)fprintf(f, "%s", cfname); + + (void)fprintf(f, "\" line %d: ", (int)clineno); + } + if (type == PARSE_WARNING) + (void)fprintf(f, "warning: "); + (void)vfprintf(f, fmt, ap); + (void)fprintf(f, "\n"); + (void)fflush(f); + if (type == PARSE_FATAL || parseWarnFatal) + fatals += 1; + if (parseWarnFatal && !fatal_warning_error_printed) { + Error("parsing warnings being treated as errors"); + fatal_warning_error_printed = TRUE; + } +} + +/*- + * ParseErrorInternal -- + * Error function + * + * Results: + * None + * + * Side Effects: + * None + */ +/* VARARGS */ +static void +ParseErrorInternal(const char *cfname, size_t clineno, int type, + const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)fflush(stdout); + ParseVErrorInternal(stderr, cfname, clineno, type, fmt, ap); + va_end(ap); + + if (debug_file != stderr && debug_file != stdout) { + va_start(ap, fmt); + ParseVErrorInternal(debug_file, cfname, clineno, type, fmt, ap); + va_end(ap); + } +} + +/*- + * Parse_Error -- + * External interface to ParseErrorInternal; uses the default filename + * Line number. + * + * Results: + * None + * + * Side Effects: + * None + */ +/* VARARGS */ +void +Parse_Error(int type, const char *fmt, ...) +{ + va_list ap; + const char *fname; + size_t lineno; + + if (curFile == NULL) { + fname = NULL; + lineno = 0; + } else { + fname = curFile->fname; + lineno = curFile->lineno; + } + + va_start(ap, fmt); + (void)fflush(stdout); + ParseVErrorInternal(stderr, fname, lineno, type, fmt, ap); + va_end(ap); + + if (debug_file != stderr && debug_file != stdout) { + va_start(ap, fmt); + ParseVErrorInternal(debug_file, fname, lineno, type, fmt, ap); + va_end(ap); + } +} + + +/* + * ParseMessage + * Parse a .info .warning or .error directive + * + * The input is the line minus the ".". We substitute + * variables, print the message and exit(1) (for .error) or just print + * a warning if the directive is malformed. + */ +static Boolean +ParseMessage(char *line) +{ + int mtype; + + switch(*line) { + case 'i': + mtype = 0; + break; + case 'w': + mtype = PARSE_WARNING; + break; + case 'e': + mtype = PARSE_FATAL; + break; + default: + Parse_Error(PARSE_WARNING, "invalid syntax: \".%s\"", line); + return FALSE; + } + + while (isalpha((u_char)*line)) + line++; + if (!isspace((u_char)*line)) + return FALSE; /* not for us */ + while (isspace((u_char)*line)) + line++; + + line = Var_Subst(NULL, line, VAR_CMD, 0); + Parse_Error(mtype, "%s", line); + free(line); + + if (mtype == PARSE_FATAL) { + /* Terminate immediately. */ + exit(1); + } + return TRUE; +} + +/*- + *--------------------------------------------------------------------- + * ParseLinkSrc -- + * Link the parent node to its new child. Used in a Lst_ForEach by + * ParseDoDependency. If the specType isn't 'Not', the parent + * isn't linked as a parent of the child. + * + * Input: + * pgnp The parent node + * cgpn The child node + * + * Results: + * Always = 0 + * + * Side Effects: + * New elements are added to the parents list of cgn and the + * children list of cgn. the unmade field of pgn is updated + * to reflect the additional child. + *--------------------------------------------------------------------- + */ +static int +ParseLinkSrc(void *pgnp, void *cgnp) +{ + GNode *pgn = (GNode *)pgnp; + GNode *cgn = (GNode *)cgnp; + + if ((pgn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (pgn->cohorts)) + pgn = (GNode *)Lst_Datum(Lst_Last(pgn->cohorts)); + (void)Lst_AtEnd(pgn->children, cgn); + if (specType == Not) + (void)Lst_AtEnd(cgn->parents, pgn); + pgn->unmade += 1; + if (DEBUG(PARSE)) { + fprintf(debug_file, "# %s: added child %s - %s\n", __func__, + pgn->name, cgn->name); + Targ_PrintNode(pgn, 0); + Targ_PrintNode(cgn, 0); + } + return (0); +} + +/*- + *--------------------------------------------------------------------- + * ParseDoOp -- + * Apply the parsed operator to the given target node. Used in a + * Lst_ForEach call by ParseDoDependency once all targets have + * been found and their operator parsed. If the previous and new + * operators are incompatible, a major error is taken. + * + * Input: + * gnp The node to which the operator is to be applied + * opp The operator to apply + * + * Results: + * Always 0 + * + * Side Effects: + * The type field of the node is altered to reflect any new bits in + * the op. + *--------------------------------------------------------------------- + */ +static int +ParseDoOp(void *gnp, void *opp) +{ + GNode *gn = (GNode *)gnp; + int op = *(int *)opp; + /* + * If the dependency mask of the operator and the node don't match and + * the node has actually had an operator applied to it before, and + * the operator actually has some dependency information in it, complain. + */ + if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && + !OP_NOP(gn->type) && !OP_NOP(op)) + { + Parse_Error(PARSE_FATAL, "Inconsistent operator for %s", gn->name); + return (1); + } + + if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { + /* + * If the node was the object of a :: operator, we need to create a + * new instance of it for the children and commands on this dependency + * line. The new instance is placed on the 'cohorts' list of the + * initial one (note the initial one is not on its own cohorts list) + * and the new instance is linked to all parents of the initial + * instance. + */ + GNode *cohort; + + /* + * Propagate copied bits to the initial node. They'll be propagated + * back to the rest of the cohorts later. + */ + gn->type |= op & ~OP_OPMASK; + + cohort = Targ_FindNode(gn->name, TARG_NOHASH); + if (doing_depend) + ParseMark(cohort); + /* + * Make the cohort invisible as well to avoid duplicating it into + * other variables. True, parents of this target won't tend to do + * anything with their local variables, but better safe than + * sorry. (I think this is pointless now, since the relevant list + * traversals will no longer see this node anyway. -mycroft) + */ + cohort->type = op | OP_INVISIBLE; + (void)Lst_AtEnd(gn->cohorts, cohort); + cohort->centurion = gn; + gn->unmade_cohorts += 1; + snprintf(cohort->cohort_num, sizeof cohort->cohort_num, "#%d", + gn->unmade_cohorts); + } else { + /* + * We don't want to nuke any previous flags (whatever they were) so we + * just OR the new operator into the old + */ + gn->type |= op; + } + + return (0); +} + +/*- + *--------------------------------------------------------------------- + * ParseDoSrc -- + * Given the name of a source, figure out if it is an attribute + * and apply it to the targets if it is. Else decide if there is + * some attribute which should be applied *to* the source because + * of some special target and apply it if so. Otherwise, make the + * source be a child of the targets in the list 'targets' + * + * Input: + * tOp operator (if any) from special targets + * src name of the source to handle + * + * Results: + * None + * + * Side Effects: + * Operator bits may be added to the list of targets or to the source. + * The targets may have a new source added to their lists of children. + *--------------------------------------------------------------------- + */ +static void +ParseDoSrc(int tOp, const char *src) +{ + GNode *gn = NULL; + static int wait_number = 0; + char wait_src[16]; + + if (*src == '.' && isupper ((unsigned char)src[1])) { + int keywd = ParseFindKeyword(src); + if (keywd != -1) { + int op = parseKeywords[keywd].op; + if (op != 0) { + Lst_ForEach(targets, ParseDoOp, &op); + return; + } + if (parseKeywords[keywd].spec == Wait) { + /* + * We add a .WAIT node in the dependency list. + * After any dynamic dependencies (and filename globbing) + * have happened, it is given a dependency on the each + * previous child back to and previous .WAIT node. + * The next child won't be scheduled until the .WAIT node + * is built. + * We give each .WAIT node a unique name (mainly for diag). + */ + snprintf(wait_src, sizeof wait_src, ".WAIT_%u", ++wait_number); + gn = Targ_FindNode(wait_src, TARG_NOHASH); + if (doing_depend) + ParseMark(gn); + gn->type = OP_WAIT | OP_PHONY | OP_DEPENDS | OP_NOTMAIN; + Lst_ForEach(targets, ParseLinkSrc, gn); + return; + } + } + } + + switch (specType) { + case Main: + /* + * If we have noted the existence of a .MAIN, it means we need + * to add the sources of said target to the list of things + * to create. The string 'src' is likely to be free, so we + * must make a new copy of it. Note that this will only be + * invoked if the user didn't specify a target on the command + * line. This is to allow #ifmake's to succeed, or something... + */ + (void)Lst_AtEnd(create, bmake_strdup(src)); + /* + * Add the name to the .TARGETS variable as well, so the user can + * employ that, if desired. + */ + Var_Append(".TARGETS", src, VAR_GLOBAL); + return; + + case Order: + /* + * Create proper predecessor/successor links between the previous + * source and the current one. + */ + gn = Targ_FindNode(src, TARG_CREATE); + if (doing_depend) + ParseMark(gn); + if (predecessor != NULL) { + (void)Lst_AtEnd(predecessor->order_succ, gn); + (void)Lst_AtEnd(gn->order_pred, predecessor); + if (DEBUG(PARSE)) { + fprintf(debug_file, "# %s: added Order dependency %s - %s\n", + __func__, predecessor->name, gn->name); + Targ_PrintNode(predecessor, 0); + Targ_PrintNode(gn, 0); + } + } + /* + * The current source now becomes the predecessor for the next one. + */ + predecessor = gn; + break; + + default: + /* + * If the source is not an attribute, we need to find/create + * a node for it. After that we can apply any operator to it + * from a special target or link it to its parents, as + * appropriate. + * + * In the case of a source that was the object of a :: operator, + * the attribute is applied to all of its instances (as kept in + * the 'cohorts' list of the node) or all the cohorts are linked + * to all the targets. + */ + + /* Find/create the 'src' node and attach to all targets */ + gn = Targ_FindNode(src, TARG_CREATE); + if (doing_depend) + ParseMark(gn); + if (tOp) { + gn->type |= tOp; + } else { + Lst_ForEach(targets, ParseLinkSrc, gn); + } + break; + } +} + +/*- + *----------------------------------------------------------------------- + * ParseFindMain -- + * Find a real target in the list and set it to be the main one. + * Called by ParseDoDependency when a main target hasn't been found + * yet. + * + * Input: + * gnp Node to examine + * + * Results: + * 0 if main not found yet, 1 if it is. + * + * Side Effects: + * mainNode is changed and Targ_SetMain is called. + * + *----------------------------------------------------------------------- + */ +static int +ParseFindMain(void *gnp, void *dummy) +{ + GNode *gn = (GNode *)gnp; + if ((gn->type & OP_NOTARGET) == 0) { + mainNode = gn; + Targ_SetMain(gn); + return (dummy ? 1 : 1); + } else { + return (dummy ? 0 : 0); + } +} + +/*- + *----------------------------------------------------------------------- + * ParseAddDir -- + * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going + * + * Results: + * === 0 + * + * Side Effects: + * See Dir_AddDir. + * + *----------------------------------------------------------------------- + */ +static int +ParseAddDir(void *path, void *name) +{ + (void)Dir_AddDir((Lst) path, (char *)name); + return(0); +} + +/*- + *----------------------------------------------------------------------- + * ParseClearPath -- + * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going + * + * Results: + * === 0 + * + * Side Effects: + * See Dir_ClearPath + * + *----------------------------------------------------------------------- + */ +static int +ParseClearPath(void *path, void *dummy) +{ + Dir_ClearPath((Lst) path); + return(dummy ? 0 : 0); +} + +/*- + *--------------------------------------------------------------------- + * ParseDoDependency -- + * Parse the dependency line in line. + * + * Input: + * line the line to parse + * + * Results: + * None + * + * Side Effects: + * The nodes of the sources are linked as children to the nodes of the + * targets. Some nodes may be created. + * + * We parse a dependency line by first extracting words from the line and + * finding nodes in the list of all targets with that name. This is done + * until a character is encountered which is an operator character. Currently + * these are only ! and :. At this point the operator is parsed and the + * pointer into the line advanced until the first source is encountered. + * The parsed operator is applied to each node in the 'targets' list, + * which is where the nodes found for the targets are kept, by means of + * the ParseDoOp function. + * The sources are read in much the same way as the targets were except + * that now they are expanded using the wildcarding scheme of the C-Shell + * and all instances of the resulting words in the list of all targets + * are found. Each of the resulting nodes is then linked to each of the + * targets as one of its children. + * Certain targets are handled specially. These are the ones detailed + * by the specType variable. + * The storing of transformation rules is also taken care of here. + * A target is recognized as a transformation rule by calling + * Suff_IsTransform. If it is a transformation rule, its node is gotten + * from the suffix module via Suff_AddTransform rather than the standard + * Targ_FindNode in the target module. + *--------------------------------------------------------------------- + */ +static void +ParseDoDependency(char *line) +{ + char *cp; /* our current position */ + GNode *gn = NULL; /* a general purpose temporary node */ + int op; /* the operator on the line */ + char savec; /* a place to save a character */ + Lst paths; /* List of search paths to alter when parsing + * a list of .PATH targets */ + int tOp; /* operator from special target */ + Lst sources; /* list of archive source names after + * expansion */ + Lst curTargs; /* list of target names to be found and added + * to the targets list */ + char *lstart = line; + + if (DEBUG(PARSE)) + fprintf(debug_file, "ParseDoDependency(%s)\n", line); + tOp = 0; + + specType = Not; + paths = NULL; + + curTargs = Lst_Init(FALSE); + + /* + * First, grind through the targets. + */ + + do { + /* + * Here LINE points to the beginning of the next word, and + * LSTART points to the actual beginning of the line. + */ + + /* Find the end of the next word. */ + for (cp = line; *cp && (ParseIsEscaped(lstart, cp) || + !(isspace((unsigned char)*cp) || + *cp == '!' || *cp == ':' || *cp == LPAREN)); + cp++) { + if (*cp == '$') { + /* + * Must be a dynamic source (would have been expanded + * otherwise), so call the Var module to parse the puppy + * so we can safely advance beyond it...There should be + * no errors in this, as they would have been discovered + * in the initial Var_Subst and we wouldn't be here. + */ + int length; + void *freeIt; + + (void)Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt); + if (freeIt) + free(freeIt); + cp += length-1; + } + } + + /* + * If the word is followed by a left parenthesis, it's the + * name of an object file inside an archive (ar file). + */ + if (!ParseIsEscaped(lstart, cp) && *cp == LPAREN) { + /* + * Archives must be handled specially to make sure the OP_ARCHV + * flag is set in their 'type' field, for one thing, and because + * things like "archive(file1.o file2.o file3.o)" are permissible. + * Arch_ParseArchive will set 'line' to be the first non-blank + * after the archive-spec. It creates/finds nodes for the members + * and places them on the given list, returning SUCCESS if all + * went well and FAILURE if there was an error in the + * specification. On error, line should remain untouched. + */ + if (Arch_ParseArchive(&line, targets, VAR_CMD) != SUCCESS) { + Parse_Error(PARSE_FATAL, + "Error in archive specification: \"%s\"", line); + goto out; + } else { + /* Done with this word; on to the next. */ + continue; + } + } + + if (!*cp) { + /* + * We got to the end of the line while we were still + * looking at targets. + * + * Ending a dependency line without an operator is a Bozo + * no-no. As a heuristic, this is also often triggered by + * undetected conflicts from cvs/rcs merges. + */ + if ((strncmp(line, "<<<<<<", 6) == 0) || + (strncmp(line, "======", 6) == 0) || + (strncmp(line, ">>>>>>", 6) == 0)) + Parse_Error(PARSE_FATAL, + "Makefile appears to contain unresolved cvs/rcs/??? merge conflicts"); + else + Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive" + : "Need an operator"); + goto out; + } + + /* Insert a null terminator. */ + savec = *cp; + *cp = '\0'; + + /* + * Got the word. See if it's a special target and if so set + * specType to match it. + */ + if (*line == '.' && isupper ((unsigned char)line[1])) { + /* + * See if the target is a special target that must have it + * or its sources handled specially. + */ + int keywd = ParseFindKeyword(line); + if (keywd != -1) { + if (specType == ExPath && parseKeywords[keywd].spec != ExPath) { + Parse_Error(PARSE_FATAL, "Mismatched special targets"); + goto out; + } + + specType = parseKeywords[keywd].spec; + tOp = parseKeywords[keywd].op; + + /* + * Certain special targets have special semantics: + * .PATH Have to set the dirSearchPath + * variable too + * .MAIN Its sources are only used if + * nothing has been specified to + * create. + * .DEFAULT Need to create a node to hang + * commands on, but we don't want + * it in the graph, nor do we want + * it to be the Main Target, so we + * create it, set OP_NOTMAIN and + * add it to the list, setting + * DEFAULT to the new node for + * later use. We claim the node is + * A transformation rule to make + * life easier later, when we'll + * use Make_HandleUse to actually + * apply the .DEFAULT commands. + * .PHONY The list of targets + * .NOPATH Don't search for file in the path + * .STALE + * .BEGIN + * .END + * .ERROR + * .INTERRUPT Are not to be considered the + * main target. + * .NOTPARALLEL Make only one target at a time. + * .SINGLESHELL Create a shell for each command. + * .ORDER Must set initial predecessor to NULL + */ + switch (specType) { + case ExPath: + if (paths == NULL) { + paths = Lst_Init(FALSE); + } + (void)Lst_AtEnd(paths, dirSearchPath); + break; + case Main: + if (!Lst_IsEmpty(create)) { + specType = Not; + } + break; + case Begin: + case End: + case Stale: + case dotError: + case Interrupt: + gn = Targ_FindNode(line, TARG_CREATE); + if (doing_depend) + ParseMark(gn); + gn->type |= OP_NOTMAIN|OP_SPECIAL; + (void)Lst_AtEnd(targets, gn); + break; + case Default: + gn = Targ_NewGN(".DEFAULT"); + gn->type |= (OP_NOTMAIN|OP_TRANSFORM); + (void)Lst_AtEnd(targets, gn); + DEFAULT = gn; + break; + case NotParallel: + maxJobs = 1; + break; + case SingleShell: + compatMake = TRUE; + break; + case Order: + predecessor = NULL; + break; + default: + break; + } + } else if (strncmp(line, ".PATH", 5) == 0) { + /* + * .PATH<suffix> has to be handled specially. + * Call on the suffix module to give us a path to + * modify. + */ + Lst path; + + specType = ExPath; + path = Suff_GetPath(&line[5]); + if (path == NULL) { + Parse_Error(PARSE_FATAL, + "Suffix '%s' not defined (yet)", + &line[5]); + goto out; + } else { + if (paths == NULL) { + paths = Lst_Init(FALSE); + } + (void)Lst_AtEnd(paths, path); + } + } + } + + /* + * Have word in line. Get or create its node and stick it at + * the end of the targets list + */ + if ((specType == Not) && (*line != '\0')) { + if (Dir_HasWildcards(line)) { + /* + * Targets are to be sought only in the current directory, + * so create an empty path for the thing. Note we need to + * use Dir_Destroy in the destruction of the path as the + * Dir module could have added a directory to the path... + */ + Lst emptyPath = Lst_Init(FALSE); + + Dir_Expand(line, emptyPath, curTargs); + + Lst_Destroy(emptyPath, Dir_Destroy); + } else { + /* + * No wildcards, but we want to avoid code duplication, + * so create a list with the word on it. + */ + (void)Lst_AtEnd(curTargs, line); + } + + /* Apply the targets. */ + + while(!Lst_IsEmpty(curTargs)) { + char *targName = (char *)Lst_DeQueue(curTargs); + + if (!Suff_IsTransform (targName)) { + gn = Targ_FindNode(targName, TARG_CREATE); + } else { + gn = Suff_AddTransform(targName); + } + if (doing_depend) + ParseMark(gn); + + (void)Lst_AtEnd(targets, gn); + } + } else if (specType == ExPath && *line != '.' && *line != '\0') { + Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); + } + + /* Don't need the inserted null terminator any more. */ + *cp = savec; + + /* + * If it is a special type and not .PATH, it's the only target we + * allow on this line... + */ + if (specType != Not && specType != ExPath) { + Boolean warning = FALSE; + + while (*cp && (ParseIsEscaped(lstart, cp) || + ((*cp != '!') && (*cp != ':')))) { + if (ParseIsEscaped(lstart, cp) || + (*cp != ' ' && *cp != '\t')) { + warning = TRUE; + } + cp++; + } + if (warning) { + Parse_Error(PARSE_WARNING, "Extra target ignored"); + } + } else { + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + } + line = cp; + } while (*line && (ParseIsEscaped(lstart, line) || + ((*line != '!') && (*line != ':')))); + + /* + * Don't need the list of target names anymore... + */ + Lst_Destroy(curTargs, NULL); + curTargs = NULL; + + if (!Lst_IsEmpty(targets)) { + switch(specType) { + default: + Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); + break; + case Default: + case Stale: + case Begin: + case End: + case dotError: + case Interrupt: + /* + * These four create nodes on which to hang commands, so + * targets shouldn't be empty... + */ + case Not: + /* + * Nothing special here -- targets can be empty if it wants. + */ + break; + } + } + + /* + * Have now parsed all the target names. Must parse the operator next. The + * result is left in op . + */ + if (*cp == '!') { + op = OP_FORCE; + } else if (*cp == ':') { + if (cp[1] == ':') { + op = OP_DOUBLEDEP; + cp++; + } else { + op = OP_DEPENDS; + } + } else { + Parse_Error(PARSE_FATAL, lstart[0] == '.' ? "Unknown directive" + : "Missing dependency operator"); + goto out; + } + + /* Advance beyond the operator */ + cp++; + + /* + * Apply the operator to the target. This is how we remember which + * operator a target was defined with. It fails if the operator + * used isn't consistent across all references. + */ + Lst_ForEach(targets, ParseDoOp, &op); + + /* + * Onward to the sources. + * + * LINE will now point to the first source word, if any, or the + * end of the string if not. + */ + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + line = cp; + + /* + * Several special targets take different actions if present with no + * sources: + * a .SUFFIXES line with no sources clears out all old suffixes + * a .PRECIOUS line makes all targets precious + * a .IGNORE line ignores errors for all targets + * a .SILENT line creates silence when making all targets + * a .PATH removes all directories from the search path(s). + */ + if (!*line) { + switch (specType) { + case Suffixes: + Suff_ClearSuffixes(); + break; + case Precious: + allPrecious = TRUE; + break; + case Ignore: + ignoreErrors = TRUE; + break; + case Silent: + beSilent = TRUE; + break; + case ExPath: + Lst_ForEach(paths, ParseClearPath, NULL); + Dir_SetPATH(); + break; +#ifdef POSIX + case Posix: + Var_Set("%POSIX", "1003.2", VAR_GLOBAL, 0); + break; +#endif + default: + break; + } + } else if (specType == MFlags) { + /* + * Call on functions in main.c to deal with these arguments and + * set the initial character to a null-character so the loop to + * get sources won't get anything + */ + Main_ParseArgLine(line); + *line = '\0'; + } else if (specType == ExShell) { + if (Job_ParseShell(line) != SUCCESS) { + Parse_Error(PARSE_FATAL, "improper shell specification"); + goto out; + } + *line = '\0'; + } else if ((specType == NotParallel) || (specType == SingleShell)) { + *line = '\0'; + } + + /* + * NOW GO FOR THE SOURCES + */ + if ((specType == Suffixes) || (specType == ExPath) || + (specType == Includes) || (specType == Libs) || + (specType == Null) || (specType == ExObjdir)) + { + while (*line) { + /* + * If the target was one that doesn't take files as its sources + * but takes something like suffixes, we take each + * space-separated word on the line as a something and deal + * with it accordingly. + * + * If the target was .SUFFIXES, we take each source as a + * suffix and add it to the list of suffixes maintained by the + * Suff module. + * + * If the target was a .PATH, we add the source as a directory + * to search on the search path. + * + * If it was .INCLUDES, the source is taken to be the suffix of + * files which will be #included and whose search path should + * be present in the .INCLUDES variable. + * + * If it was .LIBS, the source is taken to be the suffix of + * files which are considered libraries and whose search path + * should be present in the .LIBS variable. + * + * If it was .NULL, the source is the suffix to use when a file + * has no valid suffix. + * + * If it was .OBJDIR, the source is a new definition for .OBJDIR, + * and will cause make to do a new chdir to that path. + */ + while (*cp && !isspace ((unsigned char)*cp)) { + cp++; + } + savec = *cp; + *cp = '\0'; + switch (specType) { + case Suffixes: + Suff_AddSuffix(line, &mainNode); + break; + case ExPath: + Lst_ForEach(paths, ParseAddDir, line); + break; + case Includes: + Suff_AddInclude(line); + break; + case Libs: + Suff_AddLib(line); + break; + case Null: + Suff_SetNull(line); + break; + case ExObjdir: + Main_SetObjdir(line); + break; + default: + break; + } + *cp = savec; + if (savec != '\0') { + cp++; + } + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + line = cp; + } + if (paths) { + Lst_Destroy(paths, NULL); + } + if (specType == ExPath) + Dir_SetPATH(); + } else { + while (*line) { + /* + * The targets take real sources, so we must beware of archive + * specifications (i.e. things with left parentheses in them) + * and handle them accordingly. + */ + for (; *cp && !isspace ((unsigned char)*cp); cp++) { + if ((*cp == LPAREN) && (cp > line) && (cp[-1] != '$')) { + /* + * Only stop for a left parenthesis if it isn't at the + * start of a word (that'll be for variable changes + * later) and isn't preceded by a dollar sign (a dynamic + * source). + */ + break; + } + } + + if (*cp == LPAREN) { + sources = Lst_Init(FALSE); + if (Arch_ParseArchive(&line, sources, VAR_CMD) != SUCCESS) { + Parse_Error(PARSE_FATAL, + "Error in source archive spec \"%s\"", line); + goto out; + } + + while (!Lst_IsEmpty (sources)) { + gn = (GNode *)Lst_DeQueue(sources); + ParseDoSrc(tOp, gn->name); + } + Lst_Destroy(sources, NULL); + cp = line; + } else { + if (*cp) { + *cp = '\0'; + cp += 1; + } + + ParseDoSrc(tOp, line); + } + while (*cp && isspace ((unsigned char)*cp)) { + cp++; + } + line = cp; + } + } + + if (mainNode == NULL) { + /* + * If we have yet to decide on a main target to make, in the + * absence of any user input, we want the first target on + * the first dependency line that is actually a real target + * (i.e. isn't a .USE or .EXEC rule) to be made. + */ + Lst_ForEach(targets, ParseFindMain, NULL); + } + +out: + if (curTargs) + Lst_Destroy(curTargs, NULL); +} + +/*- + *--------------------------------------------------------------------- + * Parse_IsVar -- + * Return TRUE if the passed line is a variable assignment. A variable + * assignment consists of a single word followed by optional whitespace + * followed by either a += or an = operator. + * This function is used both by the Parse_File function and main when + * parsing the command-line arguments. + * + * Input: + * line the line to check + * + * Results: + * TRUE if it is. FALSE if it ain't + * + * Side Effects: + * none + *--------------------------------------------------------------------- + */ +Boolean +Parse_IsVar(char *line) +{ + Boolean wasSpace = FALSE; /* set TRUE if found a space */ + char ch; + int level = 0; +#define ISEQOPERATOR(c) \ + (((c) == '+') || ((c) == ':') || ((c) == '?') || ((c) == '!')) + + /* + * Skip to variable name + */ + for (;(*line == ' ') || (*line == '\t'); line++) + continue; + + /* Scan for one of the assignment operators outside a variable expansion */ + while ((ch = *line++) != 0) { + if (ch == '(' || ch == '{') { + level++; + continue; + } + if (ch == ')' || ch == '}') { + level--; + continue; + } + if (level != 0) + continue; + while (ch == ' ' || ch == '\t') { + ch = *line++; + wasSpace = TRUE; + } +#ifdef SUNSHCMD + if (ch == ':' && strncmp(line, "sh", 2) == 0) { + line += 2; + continue; + } +#endif + if (ch == '=') + return TRUE; + if (*line == '=' && ISEQOPERATOR(ch)) + return TRUE; + if (wasSpace) + return FALSE; + } + + return FALSE; +} + +/*- + *--------------------------------------------------------------------- + * Parse_DoVar -- + * Take the variable assignment in the passed line and do it in the + * global context. + * + * Note: There is a lexical ambiguity with assignment modifier characters + * in variable names. This routine interprets the character before the = + * as a modifier. Therefore, an assignment like + * C++=/usr/bin/CC + * is interpreted as "C+ +=" instead of "C++ =". + * + * Input: + * line a line guaranteed to be a variable assignment. + * This reduces error checks + * ctxt Context in which to do the assignment + * + * Results: + * none + * + * Side Effects: + * the variable structure of the given variable name is altered in the + * global context. + *--------------------------------------------------------------------- + */ +void +Parse_DoVar(char *line, GNode *ctxt) +{ + char *cp; /* pointer into line */ + enum { + VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL + } type; /* Type of assignment */ + char *opc; /* ptr to operator character to + * null-terminate the variable name */ + Boolean freeCp = FALSE; /* TRUE if cp needs to be freed, + * i.e. if any variable expansion was + * performed */ + int depth; + + /* + * Skip to variable name + */ + while ((*line == ' ') || (*line == '\t')) { + line++; + } + + /* + * Skip to operator character, nulling out whitespace as we go + * XXX Rather than counting () and {} we should look for $ and + * then expand the variable. + */ + for (depth = 0, cp = line + 1; depth != 0 || *cp != '='; cp++) { + if (*cp == '(' || *cp == '{') { + depth++; + continue; + } + if (*cp == ')' || *cp == '}') { + depth--; + continue; + } + if (depth == 0 && isspace ((unsigned char)*cp)) { + *cp = '\0'; + } + } + opc = cp-1; /* operator is the previous character */ + *cp++ = '\0'; /* nuke the = */ + + /* + * Check operator type + */ + switch (*opc) { + case '+': + type = VAR_APPEND; + *opc = '\0'; + break; + + case '?': + /* + * If the variable already has a value, we don't do anything. + */ + *opc = '\0'; + if (Var_Exists(line, ctxt)) { + return; + } else { + type = VAR_NORMAL; + } + break; + + case ':': + type = VAR_SUBST; + *opc = '\0'; + break; + + case '!': + type = VAR_SHELL; + *opc = '\0'; + break; + + default: +#ifdef SUNSHCMD + while (opc > line && *opc != ':') + opc--; + + if (strncmp(opc, ":sh", 3) == 0) { + type = VAR_SHELL; + *opc = '\0'; + break; + } +#endif + type = VAR_NORMAL; + break; + } + + while (isspace ((unsigned char)*cp)) { + cp++; + } + + if (type == VAR_APPEND) { + Var_Append(line, cp, ctxt); + } else if (type == VAR_SUBST) { + /* + * Allow variables in the old value to be undefined, but leave their + * invocation alone -- this is done by forcing oldVars to be false. + * XXX: This can cause recursive variables, but that's not hard to do, + * and this allows someone to do something like + * + * CFLAGS = $(.INCLUDES) + * CFLAGS := -I.. $(CFLAGS) + * + * And not get an error. + */ + Boolean oldOldVars = oldVars; + + oldVars = FALSE; + + /* + * make sure that we set the variable the first time to nothing + * so that it gets substituted! + */ + if (!Var_Exists(line, ctxt)) + Var_Set(line, "", ctxt, 0); + + cp = Var_Subst(NULL, cp, ctxt, FALSE); + oldVars = oldOldVars; + freeCp = TRUE; + + Var_Set(line, cp, ctxt, 0); + } else if (type == VAR_SHELL) { + char *res; + const char *error; + + if (strchr(cp, '$') != NULL) { + /* + * There's a dollar sign in the command, so perform variable + * expansion on the whole thing. The resulting string will need + * freeing when we're done, so set freeCmd to TRUE. + */ + cp = Var_Subst(NULL, cp, VAR_CMD, TRUE); + freeCp = TRUE; + } + + res = Cmd_Exec(cp, &error); + Var_Set(line, res, ctxt, 0); + free(res); + + if (error) + Parse_Error(PARSE_WARNING, error, cp); + } else { + /* + * Normal assignment -- just do it. + */ + Var_Set(line, cp, ctxt, 0); + } + if (strcmp(line, MAKEOVERRIDES) == 0) + Main_ExportMAKEFLAGS(FALSE); /* re-export MAKEFLAGS */ + else if (strcmp(line, ".CURDIR") == 0) { + /* + * Somone is being (too?) clever... + * Let's pretend they know what they are doing and + * re-initialize the 'cur' Path. + */ + Dir_InitCur(cp); + Dir_SetPATH(); + } else if (strcmp(line, MAKE_JOB_PREFIX) == 0) { + Job_SetPrefix(); + } else if (strcmp(line, MAKE_EXPORTED) == 0) { + Var_Export(cp, 0); + } + if (freeCp) + free(cp); +} + + +/* + * ParseMaybeSubMake -- + * Scan the command string to see if it a possible submake node + * Input: + * cmd the command to scan + * Results: + * TRUE if the command is possibly a submake, FALSE if not. + */ +static Boolean +ParseMaybeSubMake(const char *cmd) +{ + size_t i; + static struct { + const char *name; + size_t len; + } vals[] = { +#define MKV(A) { A, sizeof(A) - 1 } + MKV("${MAKE}"), + MKV("${.MAKE}"), + MKV("$(MAKE)"), + MKV("$(.MAKE)"), + MKV("make"), + }; + for (i = 0; i < sizeof(vals)/sizeof(vals[0]); i++) { + char *ptr; + if ((ptr = strstr(cmd, vals[i].name)) == NULL) + continue; + if ((ptr == cmd || !isalnum((unsigned char)ptr[-1])) + && !isalnum((unsigned char)ptr[vals[i].len])) + return TRUE; + } + return FALSE; +} + +/*- + * ParseAddCmd -- + * Lst_ForEach function to add a command line to all targets + * + * Input: + * gnp the node to which the command is to be added + * cmd the command to add + * + * Results: + * Always 0 + * + * Side Effects: + * A new element is added to the commands list of the node, + * and the node can be marked as a submake node if the command is + * determined to be that. + */ +static int +ParseAddCmd(void *gnp, void *cmd) +{ + GNode *gn = (GNode *)gnp; + + /* Add to last (ie current) cohort for :: targets */ + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) + gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + + /* if target already supplied, ignore commands */ + if (!(gn->type & OP_HAS_COMMANDS)) { + (void)Lst_AtEnd(gn->commands, cmd); + if (ParseMaybeSubMake(cmd)) + gn->type |= OP_SUBMAKE; + ParseMark(gn); + } else { +#ifdef notyet + /* XXX: We cannot do this until we fix the tree */ + (void)Lst_AtEnd(gn->commands, cmd); + Parse_Error(PARSE_WARNING, + "overriding commands for target \"%s\"; " + "previous commands defined at %s: %d ignored", + gn->name, gn->fname, gn->lineno); +#else + Parse_Error(PARSE_WARNING, + "duplicate script for target \"%s\" ignored", + gn->name); + ParseErrorInternal(gn->fname, gn->lineno, PARSE_WARNING, + "using previous script for \"%s\" defined here", + gn->name); +#endif + } + return(0); +} + +/*- + *----------------------------------------------------------------------- + * ParseHasCommands -- + * Callback procedure for Parse_File when destroying the list of + * targets on the last dependency line. Marks a target as already + * having commands if it does, to keep from having shell commands + * on multiple dependency lines. + * + * Input: + * gnp Node to examine + * + * Results: + * None + * + * Side Effects: + * OP_HAS_COMMANDS may be set for the target. + * + *----------------------------------------------------------------------- + */ +static void +ParseHasCommands(void *gnp) +{ + GNode *gn = (GNode *)gnp; + if (!Lst_IsEmpty(gn->commands)) { + gn->type |= OP_HAS_COMMANDS; + } +} + +/*- + *----------------------------------------------------------------------- + * Parse_AddIncludeDir -- + * Add a directory to the path searched for included makefiles + * bracketed by double-quotes. Used by functions in main.c + * + * Input: + * dir The name of the directory to add + * + * Results: + * None. + * + * Side Effects: + * The directory is appended to the list. + * + *----------------------------------------------------------------------- + */ +void +Parse_AddIncludeDir(char *dir) +{ + (void)Dir_AddDir(parseIncPath, dir); +} + +/*- + *--------------------------------------------------------------------- + * ParseDoInclude -- + * Push to another file. + * + * The input is the line minus the `.'. A file spec is a string + * enclosed in <> or "". The former is looked for only in sysIncPath. + * The latter in . and the directories specified by -I command line + * options + * + * Results: + * None + * + * Side Effects: + * A structure is added to the includes Lst and readProc, lineno, + * fname and curFILE are altered for the new file + *--------------------------------------------------------------------- + */ + +static void +Parse_include_file(char *file, Boolean isSystem, int silent) +{ + struct loadedfile *lf; + char *fullname; /* full pathname of file */ + char *newName; + char *prefEnd, *incdir; + int fd; + int i; + + /* + * Now we know the file's name and its search path, we attempt to + * find the durn thing. A return of NULL indicates the file don't + * exist. + */ + fullname = file[0] == '/' ? bmake_strdup(file) : NULL; + + if (fullname == NULL && !isSystem) { + /* + * Include files contained in double-quotes are first searched for + * relative to the including file's location. We don't want to + * cd there, of course, so we just tack on the old file's + * leading path components and call Dir_FindFile to see if + * we can locate the beast. + */ + + incdir = bmake_strdup(curFile->fname); + prefEnd = strrchr(incdir, '/'); + if (prefEnd != NULL) { + *prefEnd = '\0'; + /* Now do lexical processing of leading "../" on the filename */ + for (i = 0; strncmp(file + i, "../", 3) == 0; i += 3) { + prefEnd = strrchr(incdir + 1, '/'); + if (prefEnd == NULL || strcmp(prefEnd, "/..") == 0) + break; + *prefEnd = '\0'; + } + newName = str_concat(incdir, file + i, STR_ADDSLASH); + fullname = Dir_FindFile(newName, parseIncPath); + if (fullname == NULL) + fullname = Dir_FindFile(newName, dirSearchPath); + free(newName); + } + free(incdir); + + if (fullname == NULL) { + /* + * Makefile wasn't found in same directory as included makefile. + * Search for it first on the -I search path, + * then on the .PATH search path, if not found in a -I directory. + * If we have a suffix specific path we should use that. + */ + char *suff; + Lst suffPath = NULL; + + if ((suff = strrchr(file, '.'))) { + suffPath = Suff_GetPath(suff); + if (suffPath != NULL) { + fullname = Dir_FindFile(file, suffPath); + } + } + if (fullname == NULL) { + fullname = Dir_FindFile(file, parseIncPath); + if (fullname == NULL) { + fullname = Dir_FindFile(file, dirSearchPath); + } + } + } + } + + /* Looking for a system file or file still not found */ + if (fullname == NULL) { + /* + * Look for it on the system path + */ + fullname = Dir_FindFile(file, + Lst_IsEmpty(sysIncPath) ? defIncPath : sysIncPath); + } + + if (fullname == NULL) { + if (!silent) + Parse_Error(PARSE_FATAL, "Could not find %s", file); + return; + } + + /* Actually open the file... */ + fd = open(fullname, O_RDONLY); + if (fd == -1) { + if (!silent) + Parse_Error(PARSE_FATAL, "Cannot open %s", fullname); + free(fullname); + return; + } + + /* load it */ + lf = loadfile(fullname, fd); + + ParseSetIncludedFile(); + /* Start reading from this file next */ + Parse_SetInput(fullname, 0, -1, loadedfile_nextbuf, lf); + curFile->lf = lf; +} + +static void +ParseDoInclude(char *line) +{ + char endc; /* the character which ends the file spec */ + char *cp; /* current position in file spec */ + int silent = (*line != 'i') ? 1 : 0; + char *file = &line[7 + silent]; + + /* Skip to delimiter character so we know where to look */ + while (*file == ' ' || *file == '\t') + file++; + + if (*file != '"' && *file != '<') { + Parse_Error(PARSE_FATAL, + ".include filename must be delimited by '\"' or '<'"); + return; + } + + /* + * Set the search path on which to find the include file based on the + * characters which bracket its name. Angle-brackets imply it's + * a system Makefile while double-quotes imply it's a user makefile + */ + if (*file == '<') { + endc = '>'; + } else { + endc = '"'; + } + + /* Skip to matching delimiter */ + for (cp = ++file; *cp && *cp != endc; cp++) + continue; + + if (*cp != endc) { + Parse_Error(PARSE_FATAL, + "Unclosed %cinclude filename. '%c' expected", + '.', endc); + return; + } + *cp = '\0'; + + /* + * Substitute for any variables in the file name before trying to + * find the thing. + */ + file = Var_Subst(NULL, file, VAR_CMD, FALSE); + + Parse_include_file(file, endc == '>', silent); + free(file); +} + + +/*- + *--------------------------------------------------------------------- + * ParseSetIncludedFile -- + * Set the .INCLUDEDFROMFILE variable to the contents of .PARSEFILE + * and the .INCLUDEDFROMDIR variable to the contents of .PARSEDIR + * + * Results: + * None + * + * Side Effects: + * The .INCLUDEDFROMFILE variable is overwritten by the contents + * of .PARSEFILE and the .INCLUDEDFROMDIR variable is overwriten + * by the contents of .PARSEDIR + *--------------------------------------------------------------------- + */ +static void +ParseSetIncludedFile(void) +{ + char *pf, *fp = NULL; + char *pd, *dp = NULL; + + pf = Var_Value(".PARSEFILE", VAR_GLOBAL, &fp); + Var_Set(".INCLUDEDFROMFILE", pf, VAR_GLOBAL, 0); + pd = Var_Value(".PARSEDIR", VAR_GLOBAL, &dp); + Var_Set(".INCLUDEDFROMDIR", pd, VAR_GLOBAL, 0); + + if (DEBUG(PARSE)) + fprintf(debug_file, "%s: ${.INCLUDEDFROMDIR} = `%s' " + "${.INCLUDEDFROMFILE} = `%s'\n", __func__, pd, pf); + + if (fp) + free(fp); + if (dp) + free(dp); +} +/*- + *--------------------------------------------------------------------- + * ParseSetParseFile -- + * Set the .PARSEDIR and .PARSEFILE variables to the dirname and + * basename of the given filename + * + * Results: + * None + * + * Side Effects: + * The .PARSEDIR and .PARSEFILE variables are overwritten by the + * dirname and basename of the given filename. + *--------------------------------------------------------------------- + */ +static void +ParseSetParseFile(const char *filename) +{ + char *slash, *dirname; + const char *pd, *pf; + int len; + + slash = strrchr(filename, '/'); + if (slash == NULL) { + Var_Set(".PARSEDIR", pd = curdir, VAR_GLOBAL, 0); + Var_Set(".PARSEFILE", pf = filename, VAR_GLOBAL, 0); + dirname= NULL; + } else { + len = slash - filename; + dirname = bmake_malloc(len + 1); + memcpy(dirname, filename, len); + dirname[len] = '\0'; + Var_Set(".PARSEDIR", pd = dirname, VAR_GLOBAL, 0); + Var_Set(".PARSEFILE", pf = slash + 1, VAR_GLOBAL, 0); + } + if (DEBUG(PARSE)) + fprintf(debug_file, "%s: ${.PARSEDIR} = `%s' ${.PARSEFILE} = `%s'\n", + __func__, pd, pf); + free(dirname); +} + +/* + * Track the makefiles we read - so makefiles can + * set dependencies on them. + * Avoid adding anything more than once. + */ + +static void +ParseTrackInput(const char *name) +{ + char *old; + char *fp = NULL; + size_t name_len = strlen(name); + + old = Var_Value(MAKE_MAKEFILES, VAR_GLOBAL, &fp); + if (old) { + /* does it contain name? */ + for (; old != NULL; old = strchr(old, ' ')) { + if (*old == ' ') + old++; + if (memcmp(old, name, name_len) == 0 + && (old[name_len] == 0 || old[name_len] == ' ')) + goto cleanup; + } + } + Var_Append (MAKE_MAKEFILES, name, VAR_GLOBAL); + cleanup: + if (fp) { + free(fp); + } +} + + +/*- + *--------------------------------------------------------------------- + * Parse_setInput -- + * Start Parsing from the given source + * + * Results: + * None + * + * Side Effects: + * A structure is added to the includes Lst and readProc, lineno, + * fname and curFile are altered for the new file + *--------------------------------------------------------------------- + */ +void +Parse_SetInput(const char *name, int line, int fd, + char *(*nextbuf)(void *, size_t *), void *arg) +{ + char *buf; + size_t len; + + if (name == NULL) + name = curFile->fname; + else + ParseTrackInput(name); + + if (DEBUG(PARSE)) + fprintf(debug_file, "%s: file %s, line %d, fd %d, nextbuf %p, arg %p\n", + __func__, name, line, fd, nextbuf, arg); + + if (fd == -1 && nextbuf == NULL) + /* sanity */ + return; + + if (curFile != NULL) + /* Save exiting file info */ + Lst_AtFront(includes, curFile); + + /* Allocate and fill in new structure */ + curFile = bmake_malloc(sizeof *curFile); + + /* + * Once the previous state has been saved, we can get down to reading + * the new file. We set up the name of the file to be the absolute + * name of the include file so error messages refer to the right + * place. + */ + curFile->fname = bmake_strdup(name); + curFile->lineno = line; + curFile->first_lineno = line; + curFile->nextbuf = nextbuf; + curFile->nextbuf_arg = arg; + curFile->lf = NULL; + + assert(nextbuf != NULL); + + /* Get first block of input data */ + buf = curFile->nextbuf(curFile->nextbuf_arg, &len); + if (buf == NULL) { + /* Was all a waste of time ... */ + if (curFile->fname) + free(curFile->fname); + free(curFile); + return; + } + curFile->P_str = buf; + curFile->P_ptr = buf; + curFile->P_end = buf+len; + + curFile->cond_depth = Cond_save_depth(); + ParseSetParseFile(name); +} + +#ifdef SYSVINCLUDE +/*- + *--------------------------------------------------------------------- + * ParseTraditionalInclude -- + * Push to another file. + * + * The input is the current line. The file name(s) are + * following the "include". + * + * Results: + * None + * + * Side Effects: + * A structure is added to the includes Lst and readProc, lineno, + * fname and curFILE are altered for the new file + *--------------------------------------------------------------------- + */ +static void +ParseTraditionalInclude(char *line) +{ + char *cp; /* current position in file spec */ + int done = 0; + int silent = (line[0] != 'i') ? 1 : 0; + char *file = &line[silent + 7]; + char *all_files; + + if (DEBUG(PARSE)) { + fprintf(debug_file, "%s: %s\n", __func__, file); + } + + /* + * Skip over whitespace + */ + while (isspace((unsigned char)*file)) + file++; + + /* + * Substitute for any variables in the file name before trying to + * find the thing. + */ + all_files = Var_Subst(NULL, file, VAR_CMD, FALSE); + + if (*file == '\0') { + Parse_Error(PARSE_FATAL, + "Filename missing from \"include\""); + return; + } + + for (file = all_files; !done; file = cp + 1) { + /* Skip to end of line or next whitespace */ + for (cp = file; *cp && !isspace((unsigned char) *cp); cp++) + continue; + + if (*cp) + *cp = '\0'; + else + done = 1; + + Parse_include_file(file, FALSE, silent); + } + free(all_files); +} +#endif + +#ifdef GMAKEEXPORT +/*- + *--------------------------------------------------------------------- + * ParseGmakeExport -- + * Parse export <variable>=<value> + * + * And set the environment with it. + * + * Results: + * None + * + * Side Effects: + * None + *--------------------------------------------------------------------- + */ +static void +ParseGmakeExport(char *line) +{ + char *variable = &line[6]; + char *value; + + if (DEBUG(PARSE)) { + fprintf(debug_file, "%s: %s\n", __func__, variable); + } + + /* + * Skip over whitespace + */ + while (isspace((unsigned char)*variable)) + variable++; + + for (value = variable; *value && *value != '='; value++) + continue; + + if (*value != '=') { + Parse_Error(PARSE_FATAL, + "Variable/Value missing from \"export\""); + return; + } + *value++ = '\0'; /* terminate variable */ + + /* + * Expand the value before putting it in the environment. + */ + value = Var_Subst(NULL, value, VAR_CMD, FALSE); + setenv(variable, value, 1); +} +#endif + +/*- + *--------------------------------------------------------------------- + * ParseEOF -- + * Called when EOF is reached in the current file. If we were reading + * an include file, the includes stack is popped and things set up + * to go back to reading the previous file at the previous location. + * + * Results: + * CONTINUE if there's more to do. DONE if not. + * + * Side Effects: + * The old curFILE, is closed. The includes list is shortened. + * lineno, curFILE, and fname are changed if CONTINUE is returned. + *--------------------------------------------------------------------- + */ +static int +ParseEOF(void) +{ + char *ptr; + size_t len; + + assert(curFile->nextbuf != NULL); + + /* get next input buffer, if any */ + ptr = curFile->nextbuf(curFile->nextbuf_arg, &len); + curFile->P_ptr = ptr; + curFile->P_str = ptr; + curFile->P_end = ptr + len; + curFile->lineno = curFile->first_lineno; + if (ptr != NULL) { + /* Iterate again */ + return CONTINUE; + } + + /* Ensure the makefile (or loop) didn't have mismatched conditionals */ + Cond_restore_depth(curFile->cond_depth); + + if (curFile->lf != NULL) { + loadedfile_destroy(curFile->lf); + curFile->lf = NULL; + } + + /* Dispose of curFile info */ + /* Leak curFile->fname because all the gnodes have pointers to it */ + free(curFile->P_str); + free(curFile); + + curFile = Lst_DeQueue(includes); + + if (curFile == NULL) { + /* We've run out of input */ + Var_Delete(".PARSEDIR", VAR_GLOBAL); + Var_Delete(".PARSEFILE", VAR_GLOBAL); + Var_Delete(".INCLUDEDFROMDIR", VAR_GLOBAL); + Var_Delete(".INCLUDEDFROMFILE", VAR_GLOBAL); + return DONE; + } + + if (DEBUG(PARSE)) + fprintf(debug_file, "ParseEOF: returning to file %s, line %d\n", + curFile->fname, curFile->lineno); + + /* Restore the PARSEDIR/PARSEFILE variables */ + ParseSetParseFile(curFile->fname); + return (CONTINUE); +} + +#define PARSE_RAW 1 +#define PARSE_SKIP 2 + +static char * +ParseGetLine(int flags, int *length) +{ + IFile *cf = curFile; + char *ptr; + char ch; + char *line; + char *line_end; + char *escaped; + char *comment; + char *tp; + + /* Loop through blank lines and comment lines */ + for (;;) { + cf->lineno++; + line = cf->P_ptr; + ptr = line; + line_end = line; + escaped = NULL; + comment = NULL; + for (;;) { + if (cf->P_end != NULL && ptr == cf->P_end) { + /* end of buffer */ + ch = 0; + break; + } + ch = *ptr; + if (ch == 0 || (ch == '\\' && ptr[1] == 0)) { + if (cf->P_end == NULL) + /* End of string (aka for loop) data */ + break; + /* see if there is more we can parse */ + while (ptr++ < cf->P_end) { + if ((ch = *ptr) == '\n') { + if (ptr > line && ptr[-1] == '\\') + continue; + Parse_Error(PARSE_WARNING, + "Zero byte read from file, skipping rest of line."); + break; + } + } + if (cf->nextbuf != NULL) { + /* + * End of this buffer; return EOF and outer logic + * will get the next one. (eww) + */ + break; + } + Parse_Error(PARSE_FATAL, "Zero byte read from file"); + return NULL; + } + + if (ch == '\\') { + /* Don't treat next character as special, remember first one */ + if (escaped == NULL) + escaped = ptr; + if (ptr[1] == '\n') + cf->lineno++; + ptr += 2; + line_end = ptr; + continue; + } + if (ch == '#' && comment == NULL) { + /* Remember first '#' for comment stripping */ + /* Unless previous char was '[', as in modifier :[#] */ + if (!(ptr > line && ptr[-1] == '[')) + comment = line_end; + } + ptr++; + if (ch == '\n') + break; + if (!isspace((unsigned char)ch)) + /* We are not interested in trailing whitespace */ + line_end = ptr; + } + + /* Save next 'to be processed' location */ + cf->P_ptr = ptr; + + /* Check we have a non-comment, non-blank line */ + if (line_end == line || comment == line) { + if (ch == 0) + /* At end of file */ + return NULL; + /* Parse another line */ + continue; + } + + /* We now have a line of data */ + *line_end = 0; + + if (flags & PARSE_RAW) { + /* Leave '\' (etc) in line buffer (eg 'for' lines) */ + *length = line_end - line; + return line; + } + + if (flags & PARSE_SKIP) { + /* Completely ignore non-directives */ + if (line[0] != '.') + continue; + /* We could do more of the .else/.elif/.endif checks here */ + } + break; + } + + /* Brutally ignore anything after a non-escaped '#' in non-commands */ + if (comment != NULL && line[0] != '\t') { + line_end = comment; + *line_end = 0; + } + + /* If we didn't see a '\\' then the in-situ data is fine */ + if (escaped == NULL) { + *length = line_end - line; + return line; + } + + /* Remove escapes from '\n' and '#' */ + tp = ptr = escaped; + escaped = line; + for (; ; *tp++ = ch) { + ch = *ptr++; + if (ch != '\\') { + if (ch == 0) + break; + continue; + } + + ch = *ptr++; + if (ch == 0) { + /* Delete '\\' at end of buffer */ + tp--; + break; + } + + if (ch == '#' && line[0] != '\t') + /* Delete '\\' from before '#' on non-command lines */ + continue; + + if (ch != '\n') { + /* Leave '\\' in buffer for later */ + *tp++ = '\\'; + /* Make sure we don't delete an escaped ' ' from the line end */ + escaped = tp + 1; + continue; + } + + /* Escaped '\n' replace following whitespace with a single ' ' */ + while (ptr[0] == ' ' || ptr[0] == '\t') + ptr++; + ch = ' '; + } + + /* Delete any trailing spaces - eg from empty continuations */ + while (tp > escaped && isspace((unsigned char)tp[-1])) + tp--; + + *tp = 0; + *length = tp - line; + return line; +} + +/*- + *--------------------------------------------------------------------- + * ParseReadLine -- + * Read an entire line from the input file. Called only by Parse_File. + * + * Results: + * A line w/o its newline + * + * Side Effects: + * Only those associated with reading a character + *--------------------------------------------------------------------- + */ +static char * +ParseReadLine(void) +{ + char *line; /* Result */ + int lineLength; /* Length of result */ + int lineno; /* Saved line # */ + int rval; + + for (;;) { + line = ParseGetLine(0, &lineLength); + if (line == NULL) + return NULL; + + if (line[0] != '.') + return line; + + /* + * The line might be a conditional. Ask the conditional module + * about it and act accordingly + */ + switch (Cond_Eval(line)) { + case COND_SKIP: + /* Skip to next conditional that evaluates to COND_PARSE. */ + do { + line = ParseGetLine(PARSE_SKIP, &lineLength); + } while (line && Cond_Eval(line) != COND_PARSE); + if (line == NULL) + break; + continue; + case COND_PARSE: + continue; + case COND_INVALID: /* Not a conditional line */ + /* Check for .for loops */ + rval = For_Eval(line); + if (rval == 0) + /* Not a .for line */ + break; + if (rval < 0) + /* Syntax error - error printed, ignore line */ + continue; + /* Start of a .for loop */ + lineno = curFile->lineno; + /* Accumulate loop lines until matching .endfor */ + do { + line = ParseGetLine(PARSE_RAW, &lineLength); + if (line == NULL) { + Parse_Error(PARSE_FATAL, + "Unexpected end of file in for loop."); + break; + } + } while (For_Accum(line)); + /* Stash each iteration as a new 'input file' */ + For_Run(lineno); + /* Read next line from for-loop buffer */ + continue; + } + return (line); + } +} + +/*- + *----------------------------------------------------------------------- + * ParseFinishLine -- + * Handle the end of a dependency group. + * + * Results: + * Nothing. + * + * Side Effects: + * inLine set FALSE. 'targets' list destroyed. + * + *----------------------------------------------------------------------- + */ +static void +ParseFinishLine(void) +{ + if (inLine) { + Lst_ForEach(targets, Suff_EndTransform, NULL); + Lst_Destroy(targets, ParseHasCommands); + targets = NULL; + inLine = FALSE; + } +} + + +/*- + *--------------------------------------------------------------------- + * Parse_File -- + * Parse a file into its component parts, incorporating it into the + * current dependency graph. This is the main function and controls + * almost every other function in this module + * + * Input: + * name the name of the file being read + * fd Open file to makefile to parse + * + * Results: + * None + * + * Side Effects: + * closes fd. + * Loads. Nodes are added to the list of all targets, nodes and links + * are added to the dependency graph. etc. etc. etc. + *--------------------------------------------------------------------- + */ +void +Parse_File(const char *name, int fd) +{ + char *cp; /* pointer into the line */ + char *line; /* the line we're working on */ + struct loadedfile *lf; + + lf = loadfile(name, fd); + + inLine = FALSE; + fatals = 0; + + if (name == NULL) { + name = "(stdin)"; + } + + Parse_SetInput(name, 0, -1, loadedfile_nextbuf, lf); + curFile->lf = lf; + + do { + for (; (line = ParseReadLine()) != NULL; ) { + if (DEBUG(PARSE)) + fprintf(debug_file, "ParseReadLine (%d): '%s'\n", + curFile->lineno, line); + if (*line == '.') { + /* + * Lines that begin with the special character may be + * include or undef directives. + * On the other hand they can be suffix rules (.c.o: ...) + * or just dependencies for filenames that start '.'. + */ + for (cp = line + 1; isspace((unsigned char)*cp); cp++) { + continue; + } + if (strncmp(cp, "include", 7) == 0 || + ((cp[0] == 's' || cp[0] == '-') && + strncmp(&cp[1], "include", 7) == 0)) { + ParseDoInclude(cp); + continue; + } + if (strncmp(cp, "undef", 5) == 0) { + char *cp2; + for (cp += 5; isspace((unsigned char) *cp); cp++) + continue; + for (cp2 = cp; !isspace((unsigned char) *cp2) && + (*cp2 != '\0'); cp2++) + continue; + *cp2 = '\0'; + Var_Delete(cp, VAR_GLOBAL); + continue; + } else if (strncmp(cp, "export", 6) == 0) { + for (cp += 6; isspace((unsigned char) *cp); cp++) + continue; + Var_Export(cp, 1); + continue; + } else if (strncmp(cp, "unexport", 8) == 0) { + Var_UnExport(cp); + continue; + } else if (strncmp(cp, "info", 4) == 0 || + strncmp(cp, "error", 5) == 0 || + strncmp(cp, "warning", 7) == 0) { + if (ParseMessage(cp)) + continue; + } + } + + if (*line == '\t') { + /* + * If a line starts with a tab, it can only hope to be + * a creation command. + */ + cp = line + 1; + shellCommand: + for (; isspace ((unsigned char)*cp); cp++) { + continue; + } + if (*cp) { + if (!inLine) + Parse_Error(PARSE_FATAL, + "Unassociated shell command \"%s\"", + cp); + /* + * So long as it's not a blank line and we're actually + * in a dependency spec, add the command to the list of + * commands of all targets in the dependency spec + */ + if (targets) { + cp = bmake_strdup(cp); + Lst_ForEach(targets, ParseAddCmd, cp); +#ifdef CLEANUP + Lst_AtEnd(targCmds, cp); +#endif + } + } + continue; + } + +#ifdef SYSVINCLUDE + if (((strncmp(line, "include", 7) == 0 && + isspace((unsigned char) line[7])) || + ((line[0] == 's' || line[0] == '-') && + strncmp(&line[1], "include", 7) == 0 && + isspace((unsigned char) line[8]))) && + strchr(line, ':') == NULL) { + /* + * It's an S3/S5-style "include". + */ + ParseTraditionalInclude(line); + continue; + } +#endif +#ifdef GMAKEEXPORT + if (strncmp(line, "export", 6) == 0 && + isspace((unsigned char) line[6]) && + strchr(line, ':') == NULL) { + /* + * It's a Gmake "export". + */ + ParseGmakeExport(line); + continue; + } +#endif + if (Parse_IsVar(line)) { + ParseFinishLine(); + Parse_DoVar(line, VAR_GLOBAL); + continue; + } + +#ifndef POSIX + /* + * To make life easier on novices, if the line is indented we + * first make sure the line has a dependency operator in it. + * If it doesn't have an operator and we're in a dependency + * line's script, we assume it's actually a shell command + * and add it to the current list of targets. + */ + cp = line; + if (isspace((unsigned char) line[0])) { + while ((*cp != '\0') && isspace((unsigned char) *cp)) + cp++; + while (*cp && (ParseIsEscaped(line, cp) || + (*cp != ':') && (*cp != '!'))) { + cp++; + } + if (*cp == '\0') { + if (inLine) { + Parse_Error(PARSE_WARNING, + "Shell command needs a leading tab"); + goto shellCommand; + } + } + } +#endif + ParseFinishLine(); + + /* + * For some reason - probably to make the parser impossible - + * a ';' can be used to separate commands from dependencies. + * Attempt to avoid ';' inside substitution patterns. + */ + { + int level = 0; + + for (cp = line; *cp != 0; cp++) { + if (*cp == '\\' && cp[1] != 0) { + cp++; + continue; + } + if (*cp == '$' && + (cp[1] == '(' || cp[1] == '{')) { + level++; + continue; + } + if (level > 0) { + if (*cp == ')' || *cp == '}') { + level--; + continue; + } + } else if (*cp == ';') { + break; + } + } + } + if (*cp != 0) + /* Terminate the dependency list at the ';' */ + *cp++ = 0; + else + cp = NULL; + + /* + * We now know it's a dependency line so it needs to have all + * variables expanded before being parsed. Tell the variable + * module to complain if some variable is undefined... + */ + line = Var_Subst(NULL, line, VAR_CMD, TRUE); + + /* + * Need a non-circular list for the target nodes + */ + if (targets) + Lst_Destroy(targets, NULL); + + targets = Lst_Init(FALSE); + inLine = TRUE; + + ParseDoDependency(line); + free(line); + + /* If there were commands after a ';', add them now */ + if (cp != NULL) { + goto shellCommand; + } + } + /* + * Reached EOF, but it may be just EOF of an include file... + */ + } while (ParseEOF() == CONTINUE); + + if (fatals) { + (void)fflush(stdout); + (void)fprintf(stderr, + "%s: Fatal errors encountered -- cannot continue", + progname); + PrintOnError(NULL, NULL); + exit(1); + } +} + +/*- + *--------------------------------------------------------------------- + * Parse_Init -- + * initialize the parsing module + * + * Results: + * none + * + * Side Effects: + * the parseIncPath list is initialized... + *--------------------------------------------------------------------- + */ +void +Parse_Init(void) +{ + mainNode = NULL; + parseIncPath = Lst_Init(FALSE); + sysIncPath = Lst_Init(FALSE); + defIncPath = Lst_Init(FALSE); + includes = Lst_Init(FALSE); +#ifdef CLEANUP + targCmds = Lst_Init(FALSE); +#endif +} + +void +Parse_End(void) +{ +#ifdef CLEANUP + Lst_Destroy(targCmds, (FreeProc *)free); + if (targets) + Lst_Destroy(targets, NULL); + Lst_Destroy(defIncPath, Dir_Destroy); + Lst_Destroy(sysIncPath, Dir_Destroy); + Lst_Destroy(parseIncPath, Dir_Destroy); + Lst_Destroy(includes, NULL); /* Should be empty now */ +#endif +} + + +/*- + *----------------------------------------------------------------------- + * Parse_MainName -- + * Return a Lst of the main target to create for main()'s sake. If + * no such target exists, we Punt with an obnoxious error message. + * + * Results: + * A Lst of the single node to create. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Lst +Parse_MainName(void) +{ + Lst mainList; /* result list */ + + mainList = Lst_Init(FALSE); + + if (mainNode == NULL) { + Punt("no target to make."); + /*NOTREACHED*/ + } else if (mainNode->type & OP_DOUBLEDEP) { + (void)Lst_AtEnd(mainList, mainNode); + Lst_Concat(mainList, mainNode->cohorts, LST_CONCNEW); + } + else + (void)Lst_AtEnd(mainList, mainNode); + Var_Append(".TARGETS", mainNode->name, VAR_GLOBAL); + return (mainList); +} + +/*- + *----------------------------------------------------------------------- + * ParseMark -- + * Add the filename and lineno to the GNode so that we remember + * where it was first defined. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static void +ParseMark(GNode *gn) +{ + gn->fname = curFile->fname; + gn->lineno = curFile->lineno; +} diff --git a/buildrump.sh/src/usr.bin/make/pathnames.h b/buildrump.sh/src/usr.bin/make/pathnames.h new file mode 100644 index 00000000..12c4f3d7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/pathnames.h @@ -0,0 +1,53 @@ +/* $NetBSD: pathnames.h,v 1.17 2009/04/11 09:41:18 apb Exp $ */ + +/* + * Copyright (c) 1990, 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. + * + * from: @(#)pathnames.h 5.2 (Berkeley) 6/1/90 + */ + +#ifndef MAKE_NATIVE +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif +#else +#include <paths.h> +#endif + +#define _PATH_OBJDIR "obj" +#define _PATH_OBJDIRPREFIX "/usr/obj" +#ifndef _PATH_DEFSHELLDIR +#define _PATH_DEFSHELLDIR "/bin" +#endif +#define _PATH_DEFSYSMK "sys.mk" +#ifndef _PATH_DEFSYSPATH +#define _PATH_DEFSYSPATH "/usr/share/mk" +#endif +#ifndef _PATH_TMP +#define _PATH_TMP "/tmp/" /* with trailing slash */ +#endif diff --git a/buildrump.sh/src/usr.bin/make/sprite.h b/buildrump.sh/src/usr.bin/make/sprite.h new file mode 100644 index 00000000..6ec4fe2e --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/sprite.h @@ -0,0 +1,116 @@ +/* $NetBSD: sprite.h,v 1.11 2009/01/23 21:26:30 dsl Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + * + * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + * + * from: @(#)sprite.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * sprite.h -- + * + * Common constants and type declarations for Sprite. + */ + +#ifndef _SPRITE +#define _SPRITE + + +/* + * A boolean type is defined as an integer, not an enum. This allows a + * boolean argument to be an expression that isn't strictly 0 or 1 valued. + */ + +typedef int Boolean; +#ifndef TRUE +#define TRUE 1 +#endif /* TRUE */ +#ifndef FALSE +#define FALSE 0 +#endif /* FALSE */ + +/* + * Functions that must return a status can return a ReturnStatus to + * indicate success or type of failure. + */ + +typedef int ReturnStatus; + +/* + * The following statuses overlap with the first 2 generic statuses + * defined in status.h: + * + * SUCCESS There was no error. + * FAILURE There was a general error. + */ + +#define SUCCESS 0x00000000 +#define FAILURE 0x00000001 + +#endif /* _SPRITE */ diff --git a/buildrump.sh/src/usr.bin/make/str.c b/buildrump.sh/src/usr.bin/make/str.c new file mode 100644 index 00000000..0260447f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/str.c @@ -0,0 +1,514 @@ +/* $NetBSD: str.c,v 1.35 2014/02/12 01:35:56 sjg Exp $ */ + +/*- + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/*- + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: str.c,v 1.35 2014/02/12 01:35:56 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)str.c 5.8 (Berkeley) 6/1/90"; +#else +__RCSID("$NetBSD: str.c,v 1.35 2014/02/12 01:35:56 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +#include "make.h" + +/*- + * str_concat -- + * concatenate the two strings, inserting a space or slash between them, + * freeing them if requested. + * + * returns -- + * the resulting string in allocated space. + */ +char * +str_concat(const char *s1, const char *s2, int flags) +{ + int len1, len2; + char *result; + + /* get the length of both strings */ + len1 = strlen(s1); + len2 = strlen(s2); + + /* allocate length plus separator plus EOS */ + result = bmake_malloc((u_int)(len1 + len2 + 2)); + + /* copy first string into place */ + memcpy(result, s1, len1); + + /* add separator character */ + if (flags & STR_ADDSPACE) { + result[len1] = ' '; + ++len1; + } else if (flags & STR_ADDSLASH) { + result[len1] = '/'; + ++len1; + } + + /* copy second string plus EOS into place */ + memcpy(result + len1, s2, len2 + 1); + + return(result); +} + +/*- + * brk_string -- + * Fracture a string into an array of words (as delineated by tabs or + * spaces) taking quotation marks into account. Leading tabs/spaces + * are ignored. + * + * If expand is TRUE, quotes are removed and escape sequences + * such as \r, \t, etc... are expanded. + * + * returns -- + * Pointer to the array of pointers to the words. + * Memory containing the actual words in *buffer. + * Both of these must be free'd by the caller. + * Number of words in *store_argc. + */ +char ** +brk_string(const char *str, int *store_argc, Boolean expand, char **buffer) +{ + int argc, ch; + char inquote, *start, *t; + const char *p; + int len; + int argmax = 50, curlen = 0; + char **argv; + + /* skip leading space chars. */ + for (; *str == ' ' || *str == '\t'; ++str) + continue; + + /* allocate room for a copy of the string */ + if ((len = strlen(str) + 1) > curlen) + *buffer = bmake_malloc(curlen = len); + + /* + * initial argmax based on len + */ + argmax = MAX((len / 5), 50); + argv = bmake_malloc((argmax + 1) * sizeof(char *)); + + /* + * copy the string; at the same time, parse backslashes, + * quotes and build the argument list. + */ + argc = 0; + inquote = '\0'; + for (p = str, start = t = *buffer;; ++p) { + switch(ch = *p) { + case '"': + case '\'': + if (inquote) { + if (inquote == ch) + inquote = '\0'; + else + break; + } + else { + inquote = (char) ch; + /* Don't miss "" or '' */ + if (start == NULL && p[1] == inquote) { + if (!expand) { + start = t; + *t++ = ch; + } else + start = t + 1; + p++; + inquote = '\0'; + break; + } + } + if (!expand) { + if (!start) + start = t; + *t++ = ch; + } + continue; + case ' ': + case '\t': + case '\n': + if (inquote) + break; + if (!start) + continue; + /* FALLTHROUGH */ + case '\0': + /* + * end of a token -- make sure there's enough argv + * space and save off a pointer. + */ + if (!start) + goto done; + + *t++ = '\0'; + if (argc == argmax) { + argmax *= 2; /* ramp up fast */ + argv = (char **)bmake_realloc(argv, + (argmax + 1) * sizeof(char *)); + } + argv[argc++] = start; + start = NULL; + if (ch == '\n' || ch == '\0') { + if (expand && inquote) { + free(argv); + free(*buffer); + *buffer = NULL; + return NULL; + } + goto done; + } + continue; + case '\\': + if (!expand) { + if (!start) + start = t; + *t++ = '\\'; + if (*(p+1) == '\0') /* catch '\' at end of line */ + continue; + ch = *++p; + break; + } + + switch (ch = *++p) { + case '\0': + case '\n': + /* hmmm; fix it up as best we can */ + ch = '\\'; + --p; + break; + case 'b': + ch = '\b'; + break; + case 'f': + ch = '\f'; + break; + case 'n': + ch = '\n'; + break; + case 'r': + ch = '\r'; + break; + case 't': + ch = '\t'; + break; + } + break; + } + if (!start) + start = t; + *t++ = (char) ch; + } +done: argv[argc] = NULL; + *store_argc = argc; + return(argv); +} + +/* + * Str_FindSubstring -- See if a string contains a particular substring. + * + * Input: + * string String to search. + * substring Substring to find in string. + * + * Results: If string contains substring, the return value is the location of + * the first matching instance of substring in string. If string doesn't + * contain substring, the return value is NULL. Matching is done on an exact + * character-for-character basis with no wildcards or special characters. + * + * Side effects: None. + */ +char * +Str_FindSubstring(const char *string, const char *substring) +{ + const char *a, *b; + + /* + * First scan quickly through the two strings looking for a single- + * character match. When it's found, then compare the rest of the + * substring. + */ + + for (b = substring; *string != 0; string += 1) { + if (*string != *b) + continue; + a = string; + for (;;) { + if (*b == 0) + return UNCONST(string); + if (*a++ != *b++) + break; + } + b = substring; + } + return NULL; +} + +/* + * Str_Match -- + * + * See if a particular string matches a particular pattern. + * + * Results: Non-zero is returned if string matches pattern, 0 otherwise. The + * matching operation permits the following special characters in the + * pattern: *?\[] (see the man page for details on what these mean). + * + * XXX this function does not detect or report malformed patterns. + * + * Side effects: None. + */ +int +Str_Match(const char *string, const char *pattern) +{ + char c2; + + for (;;) { + /* + * See if we're at the end of both the pattern and the + * string. If, we succeeded. If we're at the end of the + * pattern but not at the end of the string, we failed. + */ + if (*pattern == 0) + return(!*string); + if (*string == 0 && *pattern != '*') + return(0); + /* + * Check for a "*" as the next pattern character. It matches + * any substring. We handle this by calling ourselves + * recursively for each postfix of string, until either we + * match or we reach the end of the string. + */ + if (*pattern == '*') { + pattern += 1; + if (*pattern == 0) + return(1); + while (*string != 0) { + if (Str_Match(string, pattern)) + return(1); + ++string; + } + return(0); + } + /* + * Check for a "?" as the next pattern character. It matches + * any single character. + */ + if (*pattern == '?') + goto thisCharOK; + /* + * Check for a "[" as the next pattern character. It is + * followed by a list of characters that are acceptable, or + * by a range (two characters separated by "-"). + */ + if (*pattern == '[') { + ++pattern; + for (;;) { + if ((*pattern == ']') || (*pattern == 0)) + return(0); + if (*pattern == *string) + break; + if (pattern[1] == '-') { + c2 = pattern[2]; + if (c2 == 0) + return(0); + if ((*pattern <= *string) && + (c2 >= *string)) + break; + if ((*pattern >= *string) && + (c2 <= *string)) + break; + pattern += 2; + } + ++pattern; + } + while ((*pattern != ']') && (*pattern != 0)) + ++pattern; + goto thisCharOK; + } + /* + * If the next pattern character is '/', just strip off the + * '/' so we do exact matching on the character that follows. + */ + if (*pattern == '\\') { + ++pattern; + if (*pattern == 0) + return(0); + } + /* + * There's no special character. Just make sure that the + * next characters of each string match. + */ + if (*pattern != *string) + return(0); +thisCharOK: ++pattern; + ++string; + } +} + + +/*- + *----------------------------------------------------------------------- + * Str_SYSVMatch -- + * Check word against pattern for a match (% is wild), + * + * Input: + * word Word to examine + * pattern Pattern to examine against + * len Number of characters to substitute + * + * Results: + * Returns the beginning position of a match or null. The number + * of characters matched is returned in len. + * + * Side Effects: + * None + * + *----------------------------------------------------------------------- + */ +char * +Str_SYSVMatch(const char *word, const char *pattern, int *len) +{ + const char *p = pattern; + const char *w = word; + const char *m; + + if (*p == '\0') { + /* Null pattern is the whole string */ + *len = strlen(w); + return UNCONST(w); + } + + if ((m = strchr(p, '%')) != NULL) { + /* check that the prefix matches */ + for (; p != m && *w && *w == *p; w++, p++) + continue; + + if (p != m) + return NULL; /* No match */ + + if (*++p == '\0') { + /* No more pattern, return the rest of the string */ + *len = strlen(w); + return UNCONST(w); + } + } + + m = w; + + /* Find a matching tail */ + do + if (strcmp(p, w) == 0) { + *len = w - m; + return UNCONST(m); + } + while (*w++ != '\0'); + + return NULL; +} + + +/*- + *----------------------------------------------------------------------- + * Str_SYSVSubst -- + * Substitute '%' on the pattern with len characters from src. + * If the pattern does not contain a '%' prepend len characters + * from src. + * + * Results: + * None + * + * Side Effects: + * Places result on buf + * + *----------------------------------------------------------------------- + */ +void +Str_SYSVSubst(Buffer *buf, char *pat, char *src, int len) +{ + char *m; + + if ((m = strchr(pat, '%')) != NULL) { + /* Copy the prefix */ + Buf_AddBytes(buf, m - pat, pat); + /* skip the % */ + pat = m + 1; + } + + /* Copy the pattern */ + Buf_AddBytes(buf, len, src); + + /* append the rest */ + Buf_AddBytes(buf, strlen(pat), pat); +} diff --git a/buildrump.sh/src/usr.bin/make/strlist.c b/buildrump.sh/src/usr.bin/make/strlist.c new file mode 100644 index 00000000..3fb2f7db --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/strlist.c @@ -0,0 +1,93 @@ +/* $NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $ */ + +/*- + * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David Laight. + * + * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: strlist.c,v 1.4 2009/01/24 11:59:39 dsl Exp $"); +#endif /* not lint */ +#endif + +#include <stddef.h> +#include <stdlib.h> +#include "strlist.h" +#include "make_malloc.h" + +void +strlist_init(strlist_t *sl) +{ + sl->sl_num = 0; + sl->sl_max = 0; + sl->sl_items = NULL; +} + +void +strlist_clean(strlist_t *sl) +{ + char *str; + int i; + + STRLIST_FOREACH(str, sl, i) + free(str); + free(sl->sl_items); + + sl->sl_num = 0; + sl->sl_max = 0; + sl->sl_items = NULL; +} + +void +strlist_add_str(strlist_t *sl, char *str, unsigned int info) +{ + unsigned int n; + strlist_item_t *items; + + if (str == NULL) + return; + + n = sl->sl_num + 1; + sl->sl_num = n; + items = sl->sl_items; + if (n >= sl->sl_max) { + items = bmake_realloc(items, (n + 7) * sizeof *sl->sl_items); + sl->sl_items = items; + sl->sl_max = n + 6; + } + items += n - 1; + items->si_str = str; + items->si_info = info; + items[1].si_str = NULL; /* STRLIST_FOREACH() terminator */ +} diff --git a/buildrump.sh/src/usr.bin/make/strlist.h b/buildrump.sh/src/usr.bin/make/strlist.h new file mode 100644 index 00000000..2fc049e8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/strlist.h @@ -0,0 +1,62 @@ +/* $NetBSD: strlist.h,v 1.3 2009/01/16 21:15:34 dsl Exp $ */ + +/*- + * Copyright (c) 2008 - 2009 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by David Laight. + * + * 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 NetBSD Foundation 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 NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#ifndef _STRLIST_H +#define _STRLIST_H + +typedef struct { + char *si_str; + unsigned int si_info; +} strlist_item_t; + +typedef struct { + unsigned int sl_num; + unsigned int sl_max; + strlist_item_t *sl_items; +} strlist_t; + +void strlist_init(strlist_t *); +void strlist_clean(strlist_t *); +void strlist_add_str(strlist_t *, char *, unsigned int); + +#define strlist_num(sl) ((sl)->sl_num) +#define strlist_str(sl, n) ((sl)->sl_items[n].si_str) +#define strlist_info(sl, n) ((sl)->sl_items[n].si_info) +#define strlist_set_info(sl, n, v) ((void)((sl)->sl_items[n].si_info = (v))) + +#define STRLIST_FOREACH(v, sl, index) \ + if ((sl)->sl_items != NULL) \ + for (index = 0; (v = strlist_str(sl, index)) != NULL; index++) + +#endif /* _STRLIST_H */ diff --git a/buildrump.sh/src/usr.bin/make/suff.c b/buildrump.sh/src/usr.bin/make/suff.c new file mode 100644 index 00000000..c42d2ec5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/suff.c @@ -0,0 +1,2654 @@ +/* $NetBSD: suff.c,v 1.73 2014/09/07 20:55:34 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: suff.c,v 1.73 2014/09/07 20:55:34 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)suff.c 8.4 (Berkeley) 3/21/94"; +#else +__RCSID("$NetBSD: suff.c,v 1.73 2014/09/07 20:55:34 joerg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * suff.c -- + * Functions to maintain suffix lists and find implicit dependents + * using suffix transformation rules + * + * Interface: + * Suff_Init Initialize all things to do with suffixes. + * + * Suff_End Cleanup the module + * + * Suff_DoPaths This function is used to make life easier + * when searching for a file according to its + * suffix. It takes the global search path, + * as defined using the .PATH: target, and appends + * its directories to the path of each of the + * defined suffixes, as specified using + * .PATH<suffix>: targets. In addition, all + * directories given for suffixes labeled as + * include files or libraries, using the .INCLUDES + * or .LIBS targets, are played with using + * Dir_MakeFlags to create the .INCLUDES and + * .LIBS global variables. + * + * Suff_ClearSuffixes Clear out all the suffixes and defined + * transformations. + * + * Suff_IsTransform Return TRUE if the passed string is the lhs + * of a transformation rule. + * + * Suff_AddSuffix Add the passed string as another known suffix. + * + * Suff_GetPath Return the search path for the given suffix. + * + * Suff_AddInclude Mark the given suffix as denoting an include + * file. + * + * Suff_AddLib Mark the given suffix as denoting a library. + * + * Suff_AddTransform Add another transformation to the suffix + * graph. Returns GNode suitable for framing, I + * mean, tacking commands, attributes, etc. on. + * + * Suff_SetNull Define the suffix to consider the suffix of + * any file that doesn't have a known one. + * + * Suff_FindDeps Find implicit sources for and the location of + * a target based on its suffix. Returns the + * bottom-most node added to the graph or NULL + * if the target had no implicit sources. + * + * Suff_FindPath Return the appropriate path to search in + * order to find the node. + */ + +#include <stdio.h> +#include "make.h" +#include "hash.h" +#include "dir.h" + +static Lst sufflist; /* Lst of suffixes */ +#ifdef CLEANUP +static Lst suffClean; /* Lst of suffixes to be cleaned */ +#endif +static Lst srclist; /* Lst of sources */ +static Lst transforms; /* Lst of transformation rules */ + +static int sNum = 0; /* Counter for assigning suffix numbers */ + +/* + * Structure describing an individual suffix. + */ +typedef struct _Suff { + char *name; /* The suffix itself */ + int nameLen; /* Length of the suffix */ + short flags; /* Type of suffix */ +#define SUFF_INCLUDE 0x01 /* One which is #include'd */ +#define SUFF_LIBRARY 0x02 /* One which contains a library */ +#define SUFF_NULL 0x04 /* The empty suffix */ + Lst searchPath; /* The path along which files of this suffix + * may be found */ + int sNum; /* The suffix number */ + int refCount; /* Reference count of list membership */ + Lst parents; /* Suffixes we have a transformation to */ + Lst children; /* Suffixes we have a transformation from */ + Lst ref; /* List of lists this suffix is referenced */ +} Suff; + +/* + * for SuffSuffIsSuffix + */ +typedef struct { + char *ename; /* The end of the name */ + int len; /* Length of the name */ +} SuffixCmpData; + +/* + * Structure used in the search for implied sources. + */ +typedef struct _Src { + char *file; /* The file to look for */ + char *pref; /* Prefix from which file was formed */ + Suff *suff; /* The suffix on the file */ + struct _Src *parent; /* The Src for which this is a source */ + GNode *node; /* The node describing the file */ + int children; /* Count of existing children (so we don't free + * this thing too early or never nuke it) */ +#ifdef DEBUG_SRC + Lst cp; /* Debug; children list */ +#endif +} Src; + +/* + * A structure for passing more than one argument to the Lst-library-invoked + * function... + */ +typedef struct { + Lst l; + Src *s; +} LstSrc; + +typedef struct { + GNode **gn; + Suff *s; + Boolean r; +} GNodeSuff; + +static Suff *suffNull; /* The NULL suffix for this run */ +static Suff *emptySuff; /* The empty suffix required for POSIX + * single-suffix transformation rules */ + + +static const char *SuffStrIsPrefix(const char *, const char *); +static char *SuffSuffIsSuffix(const Suff *, const SuffixCmpData *); +static int SuffSuffIsSuffixP(const void *, const void *); +static int SuffSuffHasNameP(const void *, const void *); +static int SuffSuffIsPrefix(const void *, const void *); +static int SuffGNHasNameP(const void *, const void *); +static void SuffUnRef(void *, void *); +static void SuffFree(void *); +static void SuffInsert(Lst, Suff *); +static void SuffRemove(Lst, Suff *); +static Boolean SuffParseTransform(char *, Suff **, Suff **); +static int SuffRebuildGraph(void *, void *); +static int SuffScanTargets(void *, void *); +static int SuffAddSrc(void *, void *); +static int SuffRemoveSrc(Lst); +static void SuffAddLevel(Lst, Src *); +static Src *SuffFindThem(Lst, Lst); +static Src *SuffFindCmds(Src *, Lst); +static void SuffExpandChildren(LstNode, GNode *); +static void SuffExpandWildcards(LstNode, GNode *); +static Boolean SuffApplyTransform(GNode *, GNode *, Suff *, Suff *); +static void SuffFindDeps(GNode *, Lst); +static void SuffFindArchiveDeps(GNode *, Lst); +static void SuffFindNormalDeps(GNode *, Lst); +static int SuffPrintName(void *, void *); +static int SuffPrintSuff(void *, void *); +static int SuffPrintTrans(void *, void *); + + /*************** Lst Predicates ****************/ +/*- + *----------------------------------------------------------------------- + * SuffStrIsPrefix -- + * See if pref is a prefix of str. + * + * Input: + * pref possible prefix + * str string to check + * + * Results: + * NULL if it ain't, pointer to character in str after prefix if so + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static const char * +SuffStrIsPrefix(const char *pref, const char *str) +{ + while (*str && *pref == *str) { + pref++; + str++; + } + + return (*pref ? NULL : str); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffIsSuffix -- + * See if suff is a suffix of str. sd->ename should point to THE END + * of the string to check. (THE END == the null byte) + * + * Input: + * s possible suffix + * sd string to examine + * + * Results: + * NULL if it ain't, pointer to character in str before suffix if + * it is. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static char * +SuffSuffIsSuffix(const Suff *s, const SuffixCmpData *sd) +{ + char *p1; /* Pointer into suffix name */ + char *p2; /* Pointer into string being examined */ + + if (sd->len < s->nameLen) + return NULL; /* this string is shorter than the suffix */ + + p1 = s->name + s->nameLen; + p2 = sd->ename; + + while (p1 >= s->name && *p1 == *p2) { + p1--; + p2--; + } + + return (p1 == s->name - 1 ? p2 : NULL); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffIsSuffixP -- + * Predicate form of SuffSuffIsSuffix. Passed as the callback function + * to Lst_Find. + * + * Results: + * 0 if the suffix is the one desired, non-zero if not. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static int +SuffSuffIsSuffixP(const void *s, const void *sd) +{ + return(!SuffSuffIsSuffix(s, sd)); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffHasNameP -- + * Callback procedure for finding a suffix based on its name. Used by + * Suff_GetPath. + * + * Input: + * s Suffix to check + * sd Desired name + * + * Results: + * 0 if the suffix is of the given name. non-zero otherwise. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +SuffSuffHasNameP(const void *s, const void *sname) +{ + return (strcmp(sname, ((const Suff *)s)->name)); +} + +/*- + *----------------------------------------------------------------------- + * SuffSuffIsPrefix -- + * See if the suffix described by s is a prefix of the string. Care + * must be taken when using this to search for transformations and + * what-not, since there could well be two suffixes, one of which + * is a prefix of the other... + * + * Input: + * s suffix to compare + * str string to examine + * + * Results: + * 0 if s is a prefix of str. non-zero otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +SuffSuffIsPrefix(const void *s, const void *str) +{ + return SuffStrIsPrefix(((const Suff *)s)->name, str) == NULL; +} + +/*- + *----------------------------------------------------------------------- + * SuffGNHasNameP -- + * See if the graph node has the desired name + * + * Input: + * gn current node we're looking at + * name name we're looking for + * + * Results: + * 0 if it does. non-zero if it doesn't + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static int +SuffGNHasNameP(const void *gn, const void *name) +{ + return (strcmp(name, ((const GNode *)gn)->name)); +} + + /*********** Maintenance Functions ************/ + +static void +SuffUnRef(void *lp, void *sp) +{ + Lst l = (Lst) lp; + + LstNode ln = Lst_Member(l, sp); + if (ln != NULL) { + Lst_Remove(l, ln); + ((Suff *)sp)->refCount--; + } +} + +/*- + *----------------------------------------------------------------------- + * SuffFree -- + * Free up all memory associated with the given suffix structure. + * + * Results: + * none + * + * Side Effects: + * the suffix entry is detroyed + *----------------------------------------------------------------------- + */ +static void +SuffFree(void *sp) +{ + Suff *s = (Suff *)sp; + + if (s == suffNull) + suffNull = NULL; + + if (s == emptySuff) + emptySuff = NULL; + +#ifdef notdef + /* We don't delete suffixes in order, so we cannot use this */ + if (s->refCount) + Punt("Internal error deleting suffix `%s' with refcount = %d", s->name, + s->refCount); +#endif + + Lst_Destroy(s->ref, NULL); + Lst_Destroy(s->children, NULL); + Lst_Destroy(s->parents, NULL); + Lst_Destroy(s->searchPath, Dir_Destroy); + + free(s->name); + free(s); +} + +/*- + *----------------------------------------------------------------------- + * SuffRemove -- + * Remove the suffix into the list + * + * Results: + * None + * + * Side Effects: + * The reference count for the suffix is decremented and the + * suffix is possibly freed + *----------------------------------------------------------------------- + */ +static void +SuffRemove(Lst l, Suff *s) +{ + SuffUnRef(l, s); + if (s->refCount == 0) { + SuffUnRef(sufflist, s); + SuffFree(s); + } +} + +/*- + *----------------------------------------------------------------------- + * SuffInsert -- + * Insert the suffix into the list keeping the list ordered by suffix + * numbers. + * + * Input: + * l the list where in s should be inserted + * s the suffix to insert + * + * Results: + * None + * + * Side Effects: + * The reference count of the suffix is incremented + *----------------------------------------------------------------------- + */ +static void +SuffInsert(Lst l, Suff *s) +{ + LstNode ln; /* current element in l we're examining */ + Suff *s2 = NULL; /* the suffix descriptor in this element */ + + if (Lst_Open(l) == FAILURE) { + return; + } + while ((ln = Lst_Next(l)) != NULL) { + s2 = (Suff *)Lst_Datum(ln); + if (s2->sNum >= s->sNum) { + break; + } + } + + Lst_Close(l); + if (DEBUG(SUFF)) { + fprintf(debug_file, "inserting %s(%d)...", s->name, s->sNum); + } + if (ln == NULL) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "at end of list\n"); + } + (void)Lst_AtEnd(l, s); + s->refCount++; + (void)Lst_AtEnd(s->ref, l); + } else if (s2->sNum != s->sNum) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "before %s(%d)\n", s2->name, s2->sNum); + } + (void)Lst_InsertBefore(l, ln, s); + s->refCount++; + (void)Lst_AtEnd(s->ref, l); + } else if (DEBUG(SUFF)) { + fprintf(debug_file, "already there\n"); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_ClearSuffixes -- + * This is gross. Nuke the list of suffixes but keep all transformation + * rules around. The transformation graph is destroyed in this process, + * but we leave the list of rules so when a new graph is formed the rules + * will remain. + * This function is called from the parse module when a + * .SUFFIXES:\n line is encountered. + * + * Results: + * none + * + * Side Effects: + * the sufflist and its graph nodes are destroyed + *----------------------------------------------------------------------- + */ +void +Suff_ClearSuffixes(void) +{ +#ifdef CLEANUP + Lst_Concat(suffClean, sufflist, LST_CONCLINK); +#endif + sufflist = Lst_Init(FALSE); + sNum = 0; + suffNull = emptySuff; +} + +/*- + *----------------------------------------------------------------------- + * SuffParseTransform -- + * Parse a transformation string to find its two component suffixes. + * + * Input: + * str String being parsed + * srcPtr Place to store source of trans. + * targPtr Place to store target of trans. + * + * Results: + * TRUE if the string is a valid transformation and FALSE otherwise. + * + * Side Effects: + * The passed pointers are overwritten. + * + *----------------------------------------------------------------------- + */ +static Boolean +SuffParseTransform(char *str, Suff **srcPtr, Suff **targPtr) +{ + LstNode srcLn; /* element in suffix list of trans source*/ + Suff *src; /* Source of transformation */ + LstNode targLn; /* element in suffix list of trans target*/ + char *str2; /* Extra pointer (maybe target suffix) */ + LstNode singleLn; /* element in suffix list of any suffix + * that exactly matches str */ + Suff *single = NULL;/* Source of possible transformation to + * null suffix */ + + srcLn = NULL; + singleLn = NULL; + + /* + * Loop looking first for a suffix that matches the start of the + * string and then for one that exactly matches the rest of it. If + * we can find two that meet these criteria, we've successfully + * parsed the string. + */ + for (;;) { + if (srcLn == NULL) { + srcLn = Lst_Find(sufflist, str, SuffSuffIsPrefix); + } else { + srcLn = Lst_FindFrom(sufflist, Lst_Succ(srcLn), str, + SuffSuffIsPrefix); + } + if (srcLn == NULL) { + /* + * Ran out of source suffixes -- no such rule + */ + if (singleLn != NULL) { + /* + * Not so fast Mr. Smith! There was a suffix that encompassed + * the entire string, so we assume it was a transformation + * to the null suffix (thank you POSIX). We still prefer to + * find a double rule over a singleton, hence we leave this + * check until the end. + * + * XXX: Use emptySuff over suffNull? + */ + *srcPtr = single; + *targPtr = suffNull; + return(TRUE); + } + return (FALSE); + } + src = (Suff *)Lst_Datum(srcLn); + str2 = str + src->nameLen; + if (*str2 == '\0') { + single = src; + singleLn = srcLn; + } else { + targLn = Lst_Find(sufflist, str2, SuffSuffHasNameP); + if (targLn != NULL) { + *srcPtr = src; + *targPtr = (Suff *)Lst_Datum(targLn); + return (TRUE); + } + } + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_IsTransform -- + * Return TRUE if the given string is a transformation rule + * + * + * Input: + * str string to check + * + * Results: + * TRUE if the string is a concatenation of two known suffixes. + * FALSE otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Suff_IsTransform(char *str) +{ + Suff *src, *targ; + + return (SuffParseTransform(str, &src, &targ)); +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddTransform -- + * Add the transformation rule described by the line to the + * list of rules and place the transformation itself in the graph + * + * Input: + * line name of transformation to add + * + * Results: + * The node created for the transformation in the transforms list + * + * Side Effects: + * The node is placed on the end of the transforms Lst and links are + * made between the two suffixes mentioned in the target name + *----------------------------------------------------------------------- + */ +GNode * +Suff_AddTransform(char *line) +{ + GNode *gn; /* GNode of transformation rule */ + Suff *s, /* source suffix */ + *t; /* target suffix */ + LstNode ln; /* Node for existing transformation */ + + ln = Lst_Find(transforms, line, SuffGNHasNameP); + if (ln == NULL) { + /* + * Make a new graph node for the transformation. It will be filled in + * by the Parse module. + */ + gn = Targ_NewGN(line); + (void)Lst_AtEnd(transforms, gn); + } else { + /* + * New specification for transformation rule. Just nuke the old list + * of commands so they can be filled in again... We don't actually + * free the commands themselves, because a given command can be + * attached to several different transformations. + */ + gn = (GNode *)Lst_Datum(ln); + Lst_Destroy(gn->commands, NULL); + Lst_Destroy(gn->children, NULL); + gn->commands = Lst_Init(FALSE); + gn->children = Lst_Init(FALSE); + } + + gn->type = OP_TRANSFORM; + + (void)SuffParseTransform(line, &s, &t); + + /* + * link the two together in the proper relationship and order + */ + if (DEBUG(SUFF)) { + fprintf(debug_file, "defining transformation from `%s' to `%s'\n", + s->name, t->name); + } + SuffInsert(t->children, s); + SuffInsert(s->parents, t); + + return (gn); +} + +/*- + *----------------------------------------------------------------------- + * Suff_EndTransform -- + * Handle the finish of a transformation definition, removing the + * transformation from the graph if it has neither commands nor + * sources. This is a callback procedure for the Parse module via + * Lst_ForEach + * + * Input: + * gnp Node for transformation + * dummy Node for transformation + * + * Results: + * === 0 + * + * Side Effects: + * If the node has no commands or children, the children and parents + * lists of the affected suffixes are altered. + * + *----------------------------------------------------------------------- + */ +int +Suff_EndTransform(void *gnp, void *dummy) +{ + GNode *gn = (GNode *)gnp; + + if ((gn->type & OP_DOUBLEDEP) && !Lst_IsEmpty (gn->cohorts)) + gn = (GNode *)Lst_Datum(Lst_Last(gn->cohorts)); + if ((gn->type & OP_TRANSFORM) && Lst_IsEmpty(gn->commands) && + Lst_IsEmpty(gn->children)) + { + Suff *s, *t; + + /* + * SuffParseTransform() may fail for special rules which are not + * actual transformation rules. (e.g. .DEFAULT) + */ + if (SuffParseTransform(gn->name, &s, &t)) { + Lst p; + + if (DEBUG(SUFF)) { + fprintf(debug_file, "deleting transformation from `%s' to `%s'\n", + s->name, t->name); + } + + /* + * Store s->parents because s could be deleted in SuffRemove + */ + p = s->parents; + + /* + * Remove the source from the target's children list. We check for a + * nil return to handle a beanhead saying something like + * .c.o .c.o: + * + * We'll be called twice when the next target is seen, but .c and .o + * are only linked once... + */ + SuffRemove(t->children, s); + + /* + * Remove the target from the source's parents list + */ + SuffRemove(p, t); + } + } else if ((gn->type & OP_TRANSFORM) && DEBUG(SUFF)) { + fprintf(debug_file, "transformation %s complete\n", gn->name); + } + + return(dummy ? 0 : 0); +} + +/*- + *----------------------------------------------------------------------- + * SuffRebuildGraph -- + * Called from Suff_AddSuffix via Lst_ForEach to search through the + * list of existing transformation rules and rebuild the transformation + * graph when it has been destroyed by Suff_ClearSuffixes. If the + * given rule is a transformation involving this suffix and another, + * existing suffix, the proper relationship is established between + * the two. + * + * Input: + * transformp Transformation to test + * sp Suffix to rebuild + * + * Results: + * Always 0. + * + * Side Effects: + * The appropriate links will be made between this suffix and + * others if transformation rules exist for it. + * + *----------------------------------------------------------------------- + */ +static int +SuffRebuildGraph(void *transformp, void *sp) +{ + GNode *transform = (GNode *)transformp; + Suff *s = (Suff *)sp; + char *cp; + LstNode ln; + Suff *s2; + SuffixCmpData sd; + + /* + * First see if it is a transformation from this suffix. + */ + cp = UNCONST(SuffStrIsPrefix(s->name, transform->name)); + if (cp != NULL) { + ln = Lst_Find(sufflist, cp, SuffSuffHasNameP); + if (ln != NULL) { + /* + * Found target. Link in and return, since it can't be anything + * else. + */ + s2 = (Suff *)Lst_Datum(ln); + SuffInsert(s2->children, s); + SuffInsert(s->parents, s2); + return(0); + } + } + + /* + * Not from, maybe to? + */ + sd.len = strlen(transform->name); + sd.ename = transform->name + sd.len; + cp = SuffSuffIsSuffix(s, &sd); + if (cp != NULL) { + /* + * Null-terminate the source suffix in order to find it. + */ + cp[1] = '\0'; + ln = Lst_Find(sufflist, transform->name, SuffSuffHasNameP); + /* + * Replace the start of the target suffix + */ + cp[1] = s->name[0]; + if (ln != NULL) { + /* + * Found it -- establish the proper relationship + */ + s2 = (Suff *)Lst_Datum(ln); + SuffInsert(s->children, s2); + SuffInsert(s2->parents, s); + } + } + return(0); +} + +/*- + *----------------------------------------------------------------------- + * SuffScanTargets -- + * Called from Suff_AddSuffix via Lst_ForEach to search through the + * list of existing targets and find if any of the existing targets + * can be turned into a transformation rule. + * + * Results: + * 1 if a new main target has been selected, 0 otherwise. + * + * Side Effects: + * If such a target is found and the target is the current main + * target, the main target is set to NULL and the next target + * examined (if that exists) becomes the main target. + * + *----------------------------------------------------------------------- + */ +static int +SuffScanTargets(void *targetp, void *gsp) +{ + GNode *target = (GNode *)targetp; + GNodeSuff *gs = (GNodeSuff *)gsp; + Suff *s, *t; + char *ptr; + + if (*gs->gn == NULL && gs->r && (target->type & OP_NOTARGET) == 0) { + *gs->gn = target; + Targ_SetMain(target); + return 1; + } + + if ((unsigned int)target->type == OP_TRANSFORM) + return 0; + + if ((ptr = strstr(target->name, gs->s->name)) == NULL || + ptr == target->name) + return 0; + + if (SuffParseTransform(target->name, &s, &t)) { + if (*gs->gn == target) { + gs->r = TRUE; + *gs->gn = NULL; + Targ_SetMain(NULL); + } + Lst_Destroy(target->children, NULL); + target->children = Lst_Init(FALSE); + target->type = OP_TRANSFORM; + /* + * link the two together in the proper relationship and order + */ + if (DEBUG(SUFF)) { + fprintf(debug_file, "defining transformation from `%s' to `%s'\n", + s->name, t->name); + } + SuffInsert(t->children, s); + SuffInsert(s->parents, t); + } + return 0; +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddSuffix -- + * Add the suffix in string to the end of the list of known suffixes. + * Should we restructure the suffix graph? Make doesn't... + * + * Input: + * str the name of the suffix to add + * + * Results: + * None + * + * Side Effects: + * A GNode is created for the suffix and a Suff structure is created and + * added to the suffixes list unless the suffix was already known. + * The mainNode passed can be modified if a target mutated into a + * transform and that target happened to be the main target. + *----------------------------------------------------------------------- + */ +void +Suff_AddSuffix(char *str, GNode **gn) +{ + Suff *s; /* new suffix descriptor */ + LstNode ln; + GNodeSuff gs; + + ln = Lst_Find(sufflist, str, SuffSuffHasNameP); + if (ln == NULL) { + s = bmake_malloc(sizeof(Suff)); + + s->name = bmake_strdup(str); + s->nameLen = strlen(s->name); + s->searchPath = Lst_Init(FALSE); + s->children = Lst_Init(FALSE); + s->parents = Lst_Init(FALSE); + s->ref = Lst_Init(FALSE); + s->sNum = sNum++; + s->flags = 0; + s->refCount = 1; + + (void)Lst_AtEnd(sufflist, s); + /* + * We also look at our existing targets list to see if adding + * this suffix will make one of our current targets mutate into + * a suffix rule. This is ugly, but other makes treat all targets + * that start with a . as suffix rules. + */ + gs.gn = gn; + gs.s = s; + gs.r = FALSE; + Lst_ForEach(Targ_List(), SuffScanTargets, &gs); + /* + * Look for any existing transformations from or to this suffix. + * XXX: Only do this after a Suff_ClearSuffixes? + */ + Lst_ForEach(transforms, SuffRebuildGraph, s); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_GetPath -- + * Return the search path for the given suffix, if it's defined. + * + * Results: + * The searchPath for the desired suffix or NULL if the suffix isn't + * defined. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Lst +Suff_GetPath(char *sname) +{ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + if (ln == NULL) { + return NULL; + } else { + s = (Suff *)Lst_Datum(ln); + return (s->searchPath); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_DoPaths -- + * Extend the search paths for all suffixes to include the default + * search path. + * + * Results: + * None. + * + * Side Effects: + * The searchPath field of all the suffixes is extended by the + * directories in dirSearchPath. If paths were specified for the + * ".h" suffix, the directories are stuffed into a global variable + * called ".INCLUDES" with each directory preceded by a -I. The same + * is done for the ".a" suffix, except the variable is called + * ".LIBS" and the flag is -L. + *----------------------------------------------------------------------- + */ +void +Suff_DoPaths(void) +{ + Suff *s; + LstNode ln; + char *ptr; + Lst inIncludes; /* Cumulative .INCLUDES path */ + Lst inLibs; /* Cumulative .LIBS path */ + + if (Lst_Open(sufflist) == FAILURE) { + return; + } + + inIncludes = Lst_Init(FALSE); + inLibs = Lst_Init(FALSE); + + while ((ln = Lst_Next(sufflist)) != NULL) { + s = (Suff *)Lst_Datum(ln); + if (!Lst_IsEmpty (s->searchPath)) { +#ifdef INCLUDES + if (s->flags & SUFF_INCLUDE) { + Dir_Concat(inIncludes, s->searchPath); + } +#endif /* INCLUDES */ +#ifdef LIBRARIES + if (s->flags & SUFF_LIBRARY) { + Dir_Concat(inLibs, s->searchPath); + } +#endif /* LIBRARIES */ + Dir_Concat(s->searchPath, dirSearchPath); + } else { + Lst_Destroy(s->searchPath, Dir_Destroy); + s->searchPath = Lst_Duplicate(dirSearchPath, Dir_CopyDir); + } + } + + Var_Set(".INCLUDES", ptr = Dir_MakeFlags("-I", inIncludes), VAR_GLOBAL, 0); + free(ptr); + Var_Set(".LIBS", ptr = Dir_MakeFlags("-L", inLibs), VAR_GLOBAL, 0); + free(ptr); + + Lst_Destroy(inIncludes, Dir_Destroy); + Lst_Destroy(inLibs, Dir_Destroy); + + Lst_Close(sufflist); +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddInclude -- + * Add the given suffix as a type of file which gets included. + * Called from the parse module when a .INCLUDES line is parsed. + * The suffix must have already been defined. + * + * Input: + * sname Name of the suffix to mark + * + * Results: + * None. + * + * Side Effects: + * The SUFF_INCLUDE bit is set in the suffix's flags field + * + *----------------------------------------------------------------------- + */ +void +Suff_AddInclude(char *sname) +{ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + if (ln != NULL) { + s = (Suff *)Lst_Datum(ln); + s->flags |= SUFF_INCLUDE; + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_AddLib -- + * Add the given suffix as a type of file which is a library. + * Called from the parse module when parsing a .LIBS line. The + * suffix must have been defined via .SUFFIXES before this is + * called. + * + * Input: + * sname Name of the suffix to mark + * + * Results: + * None. + * + * Side Effects: + * The SUFF_LIBRARY bit is set in the suffix's flags field + * + *----------------------------------------------------------------------- + */ +void +Suff_AddLib(char *sname) +{ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, sname, SuffSuffHasNameP); + if (ln != NULL) { + s = (Suff *)Lst_Datum(ln); + s->flags |= SUFF_LIBRARY; + } +} + + /********** Implicit Source Search Functions *********/ + +/*- + *----------------------------------------------------------------------- + * SuffAddSrc -- + * Add a suffix as a Src structure to the given list with its parent + * being the given Src structure. If the suffix is the null suffix, + * the prefix is used unaltered as the file name in the Src structure. + * + * Input: + * sp suffix for which to create a Src structure + * lsp list and parent for the new Src + * + * Results: + * always returns 0 + * + * Side Effects: + * A Src structure is created and tacked onto the end of the list + *----------------------------------------------------------------------- + */ +static int +SuffAddSrc(void *sp, void *lsp) +{ + Suff *s = (Suff *)sp; + LstSrc *ls = (LstSrc *)lsp; + Src *s2; /* new Src structure */ + Src *targ; /* Target structure */ + + targ = ls->s; + + if ((s->flags & SUFF_NULL) && (*s->name != '\0')) { + /* + * If the suffix has been marked as the NULL suffix, also create a Src + * structure for a file with no suffix attached. Two birds, and all + * that... + */ + s2 = bmake_malloc(sizeof(Src)); + s2->file = bmake_strdup(targ->pref); + s2->pref = targ->pref; + s2->parent = targ; + s2->node = NULL; + s2->suff = s; + s->refCount++; + s2->children = 0; + targ->children += 1; + (void)Lst_AtEnd(ls->l, s2); +#ifdef DEBUG_SRC + s2->cp = Lst_Init(FALSE); + Lst_AtEnd(targ->cp, s2); + fprintf(debug_file, "1 add %x %x to %x:", targ, s2, ls->l); + Lst_ForEach(ls->l, PrintAddr, NULL); + fprintf(debug_file, "\n"); +#endif + } + s2 = bmake_malloc(sizeof(Src)); + s2->file = str_concat(targ->pref, s->name, 0); + s2->pref = targ->pref; + s2->parent = targ; + s2->node = NULL; + s2->suff = s; + s->refCount++; + s2->children = 0; + targ->children += 1; + (void)Lst_AtEnd(ls->l, s2); +#ifdef DEBUG_SRC + s2->cp = Lst_Init(FALSE); + Lst_AtEnd(targ->cp, s2); + fprintf(debug_file, "2 add %x %x to %x:", targ, s2, ls->l); + Lst_ForEach(ls->l, PrintAddr, NULL); + fprintf(debug_file, "\n"); +#endif + + return(0); +} + +/*- + *----------------------------------------------------------------------- + * SuffAddLevel -- + * Add all the children of targ as Src structures to the given list + * + * Input: + * l list to which to add the new level + * targ Src structure to use as the parent + * + * Results: + * None + * + * Side Effects: + * Lots of structures are created and added to the list + *----------------------------------------------------------------------- + */ +static void +SuffAddLevel(Lst l, Src *targ) +{ + LstSrc ls; + + ls.s = targ; + ls.l = l; + + Lst_ForEach(targ->suff->children, SuffAddSrc, &ls); +} + +/*- + *---------------------------------------------------------------------- + * SuffRemoveSrc -- + * Free all src structures in list that don't have a reference count + * + * Results: + * Ture if an src was removed + * + * Side Effects: + * The memory is free'd. + *---------------------------------------------------------------------- + */ +static int +SuffRemoveSrc(Lst l) +{ + LstNode ln; + Src *s; + int t = 0; + + if (Lst_Open(l) == FAILURE) { + return 0; + } +#ifdef DEBUG_SRC + fprintf(debug_file, "cleaning %lx: ", (unsigned long) l); + Lst_ForEach(l, PrintAddr, NULL); + fprintf(debug_file, "\n"); +#endif + + + while ((ln = Lst_Next(l)) != NULL) { + s = (Src *)Lst_Datum(ln); + if (s->children == 0) { + free(s->file); + if (!s->parent) + free(s->pref); + else { +#ifdef DEBUG_SRC + LstNode ln = Lst_Member(s->parent->cp, s); + if (ln != NULL) + Lst_Remove(s->parent->cp, ln); +#endif + --s->parent->children; + } +#ifdef DEBUG_SRC + fprintf(debug_file, "free: [l=%x] p=%x %d\n", l, s, s->children); + Lst_Destroy(s->cp, NULL); +#endif + Lst_Remove(l, ln); + free(s); + t |= 1; + Lst_Close(l); + return TRUE; + } +#ifdef DEBUG_SRC + else { + fprintf(debug_file, "keep: [l=%x] p=%x %d: ", l, s, s->children); + Lst_ForEach(s->cp, PrintAddr, NULL); + fprintf(debug_file, "\n"); + } +#endif + } + + Lst_Close(l); + + return t; +} + +/*- + *----------------------------------------------------------------------- + * SuffFindThem -- + * Find the first existing file/target in the list srcs + * + * Input: + * srcs list of Src structures to search through + * + * Results: + * The lowest structure in the chain of transformations + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static Src * +SuffFindThem(Lst srcs, Lst slst) +{ + Src *s; /* current Src */ + Src *rs; /* returned Src */ + char *ptr; + + rs = NULL; + + while (!Lst_IsEmpty (srcs)) { + s = (Src *)Lst_DeQueue(srcs); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "\ttrying %s...", s->file); + } + + /* + * A file is considered to exist if either a node exists in the + * graph for it or the file actually exists. + */ + if (Targ_FindNode(s->file, TARG_NOCREATE) != NULL) { +#ifdef DEBUG_SRC + fprintf(debug_file, "remove %x from %x\n", s, srcs); +#endif + rs = s; + break; + } + + if ((ptr = Dir_FindFile(s->file, s->suff->searchPath)) != NULL) { + rs = s; +#ifdef DEBUG_SRC + fprintf(debug_file, "remove %x from %x\n", s, srcs); +#endif + free(ptr); + break; + } + + if (DEBUG(SUFF)) { + fprintf(debug_file, "not there\n"); + } + + SuffAddLevel(srcs, s); + Lst_AtEnd(slst, s); + } + + if (DEBUG(SUFF) && rs) { + fprintf(debug_file, "got it\n"); + } + return (rs); +} + +/*- + *----------------------------------------------------------------------- + * SuffFindCmds -- + * See if any of the children of the target in the Src structure is + * one from which the target can be transformed. If there is one, + * a Src structure is put together for it and returned. + * + * Input: + * targ Src structure to play with + * + * Results: + * The Src structure of the "winning" child, or NULL if no such beast. + * + * Side Effects: + * A Src structure may be allocated. + * + *----------------------------------------------------------------------- + */ +static Src * +SuffFindCmds(Src *targ, Lst slst) +{ + LstNode ln; /* General-purpose list node */ + GNode *t, /* Target GNode */ + *s; /* Source GNode */ + int prefLen;/* The length of the defined prefix */ + Suff *suff; /* Suffix on matching beastie */ + Src *ret; /* Return value */ + char *cp; + + t = targ->node; + (void)Lst_Open(t->children); + prefLen = strlen(targ->pref); + + for (;;) { + ln = Lst_Next(t->children); + if (ln == NULL) { + Lst_Close(t->children); + return NULL; + } + s = (GNode *)Lst_Datum(ln); + + if (s->type & OP_OPTIONAL && Lst_IsEmpty(t->commands)) { + /* + * We haven't looked to see if .OPTIONAL files exist yet, so + * don't use one as the implicit source. + * This allows us to use .OPTIONAL in .depend files so make won't + * complain "don't know how to make xxx.h' when a dependent file + * has been moved/deleted. + */ + continue; + } + + cp = strrchr(s->name, '/'); + if (cp == NULL) { + cp = s->name; + } else { + cp++; + } + if (strncmp(cp, targ->pref, prefLen) != 0) + continue; + /* + * The node matches the prefix ok, see if it has a known + * suffix. + */ + ln = Lst_Find(sufflist, &cp[prefLen], SuffSuffHasNameP); + if (ln == NULL) + continue; + /* + * It even has a known suffix, see if there's a transformation + * defined between the node's suffix and the target's suffix. + * + * XXX: Handle multi-stage transformations here, too. + */ + suff = (Suff *)Lst_Datum(ln); + + if (Lst_Member(suff->parents, targ->suff) != NULL) + break; + } + + /* + * Hot Damn! Create a new Src structure to describe + * this transformation (making sure to duplicate the + * source node's name so Suff_FindDeps can free it + * again (ick)), and return the new structure. + */ + ret = bmake_malloc(sizeof(Src)); + ret->file = bmake_strdup(s->name); + ret->pref = targ->pref; + ret->suff = suff; + suff->refCount++; + ret->parent = targ; + ret->node = s; + ret->children = 0; + targ->children += 1; +#ifdef DEBUG_SRC + ret->cp = Lst_Init(FALSE); + fprintf(debug_file, "3 add %x %x\n", targ, ret); + Lst_AtEnd(targ->cp, ret); +#endif + Lst_AtEnd(slst, ret); + if (DEBUG(SUFF)) { + fprintf(debug_file, "\tusing existing source %s\n", s->name); + } + return (ret); +} + +/*- + *----------------------------------------------------------------------- + * SuffExpandChildren -- + * Expand the names of any children of a given node that contain + * variable invocations or file wildcards into actual targets. + * + * Input: + * cln Child to examine + * pgn Parent node being processed + * + * Results: + * === 0 (continue) + * + * Side Effects: + * The expanded node is removed from the parent's list of children, + * and the parent's unmade counter is decremented, but other nodes + * may be added. + * + *----------------------------------------------------------------------- + */ +static void +SuffExpandChildren(LstNode cln, GNode *pgn) +{ + GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *gn; /* New source 8) */ + char *cp; /* Expanded value */ + + if (!Lst_IsEmpty(cgn->order_pred) || !Lst_IsEmpty(cgn->order_succ)) + /* It is all too hard to process the result of .ORDER */ + return; + + if (cgn->type & OP_WAIT) + /* Ignore these (& OP_PHONY ?) */ + return; + + /* + * First do variable expansion -- this takes precedence over + * wildcard expansion. If the result contains wildcards, they'll be gotten + * to later since the resulting words are tacked on to the end of + * the children list. + */ + if (strchr(cgn->name, '$') == NULL) { + SuffExpandWildcards(cln, pgn); + return; + } + + if (DEBUG(SUFF)) { + fprintf(debug_file, "Expanding \"%s\"...", cgn->name); + } + cp = Var_Subst(NULL, cgn->name, pgn, TRUE); + + if (cp != NULL) { + Lst members = Lst_Init(FALSE); + + if (cgn->type & OP_ARCHV) { + /* + * Node was an archive(member) target, so we want to call + * on the Arch module to find the nodes for us, expanding + * variables in the parent's context. + */ + char *sacrifice = cp; + + (void)Arch_ParseArchive(&sacrifice, members, pgn); + } else { + /* + * Break the result into a vector of strings whose nodes + * we can find, then add those nodes to the members list. + * Unfortunately, we can't use brk_string b/c it + * doesn't understand about variable specifications with + * spaces in them... + */ + char *start; + char *initcp = cp; /* For freeing... */ + + for (start = cp; *start == ' ' || *start == '\t'; start++) + continue; + for (cp = start; *cp != '\0'; cp++) { + if (*cp == ' ' || *cp == '\t') { + /* + * White-space -- terminate element, find the node, + * add it, skip any further spaces. + */ + *cp++ = '\0'; + gn = Targ_FindNode(start, TARG_CREATE); + (void)Lst_AtEnd(members, gn); + while (*cp == ' ' || *cp == '\t') { + cp++; + } + /* + * Adjust cp for increment at start of loop, but + * set start to first non-space. + */ + start = cp--; + } else if (*cp == '$') { + /* + * Start of a variable spec -- contact variable module + * to find the end so we can skip over it. + */ + char *junk; + int len; + void *freeIt; + + junk = Var_Parse(cp, pgn, TRUE, &len, &freeIt); + if (junk != var_Error) { + cp += len - 1; + } + + if (freeIt) + free(freeIt); + } else if (*cp == '\\' && *cp != '\0') { + /* + * Escaped something -- skip over it + */ + cp++; + } + } + + if (cp != start) { + /* + * Stuff left over -- add it to the list too + */ + gn = Targ_FindNode(start, TARG_CREATE); + (void)Lst_AtEnd(members, gn); + } + /* + * Point cp back at the beginning again so the variable value + * can be freed. + */ + cp = initcp; + } + + /* + * Add all elements of the members list to the parent node. + */ + while(!Lst_IsEmpty(members)) { + gn = (GNode *)Lst_DeQueue(members); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "%s...", gn->name); + } + /* Add gn to the parents child list before the original child */ + (void)Lst_InsertBefore(pgn->children, cln, gn); + (void)Lst_AtEnd(gn->parents, pgn); + pgn->unmade++; + /* Expand wildcards on new node */ + SuffExpandWildcards(Lst_Prev(cln), pgn); + } + Lst_Destroy(members, NULL); + + /* + * Free the result + */ + free(cp); + } + if (DEBUG(SUFF)) { + fprintf(debug_file, "\n"); + } + + /* + * Now the source is expanded, remove it from the list of children to + * keep it from being processed. + */ + pgn->unmade--; + Lst_Remove(pgn->children, cln); + Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); +} + +static void +SuffExpandWildcards(LstNode cln, GNode *pgn) +{ + GNode *cgn = (GNode *)Lst_Datum(cln); + GNode *gn; /* New source 8) */ + char *cp; /* Expanded value */ + Lst explist; /* List of expansions */ + + if (!Dir_HasWildcards(cgn->name)) + return; + + /* + * Expand the word along the chosen path + */ + explist = Lst_Init(FALSE); + Dir_Expand(cgn->name, Suff_FindPath(cgn), explist); + + while (!Lst_IsEmpty(explist)) { + /* + * Fetch next expansion off the list and find its GNode + */ + cp = (char *)Lst_DeQueue(explist); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "%s...", cp); + } + gn = Targ_FindNode(cp, TARG_CREATE); + + /* Add gn to the parents child list before the original child */ + (void)Lst_InsertBefore(pgn->children, cln, gn); + (void)Lst_AtEnd(gn->parents, pgn); + pgn->unmade++; + } + + /* + * Nuke what's left of the list + */ + Lst_Destroy(explist, NULL); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "\n"); + } + + /* + * Now the source is expanded, remove it from the list of children to + * keep it from being processed. + */ + pgn->unmade--; + Lst_Remove(pgn->children, cln); + Lst_Remove(cgn->parents, Lst_Member(cgn->parents, pgn)); +} + +/*- + *----------------------------------------------------------------------- + * Suff_FindPath -- + * Find a path along which to expand the node. + * + * If the word has a known suffix, use that path. + * If it has no known suffix, use the default system search path. + * + * Input: + * gn Node being examined + * + * Results: + * The appropriate path to search for the GNode. + * + * Side Effects: + * XXX: We could set the suffix here so that we don't have to scan + * again. + * + *----------------------------------------------------------------------- + */ +Lst +Suff_FindPath(GNode* gn) +{ + Suff *suff = gn->suffix; + + if (suff == NULL) { + SuffixCmpData sd; /* Search string data */ + LstNode ln; + sd.len = strlen(gn->name); + sd.ename = gn->name + sd.len; + ln = Lst_Find(sufflist, &sd, SuffSuffIsSuffixP); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "Wildcard expanding \"%s\"...", gn->name); + } + if (ln != NULL) + suff = (Suff *)Lst_Datum(ln); + /* XXX: Here we can save the suffix so we don't have to do this again */ + } + + if (suff != NULL) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "suffix is \"%s\"...", suff->name); + } + return suff->searchPath; + } else { + /* + * Use default search path + */ + return dirSearchPath; + } +} + +/*- + *----------------------------------------------------------------------- + * SuffApplyTransform -- + * Apply a transformation rule, given the source and target nodes + * and suffixes. + * + * Input: + * tGn Target node + * sGn Source node + * t Target suffix + * s Source suffix + * + * Results: + * TRUE if successful, FALSE if not. + * + * Side Effects: + * The source and target are linked and the commands from the + * transformation are added to the target node's commands list. + * All attributes but OP_DEPMASK and OP_TRANSFORM are applied + * to the target. The target also inherits all the sources for + * the transformation rule. + * + *----------------------------------------------------------------------- + */ +static Boolean +SuffApplyTransform(GNode *tGn, GNode *sGn, Suff *t, Suff *s) +{ + LstNode ln, nln; /* General node */ + char *tname; /* Name of transformation rule */ + GNode *gn; /* Node for same */ + + /* + * Form the proper links between the target and source. + */ + (void)Lst_AtEnd(tGn->children, sGn); + (void)Lst_AtEnd(sGn->parents, tGn); + tGn->unmade += 1; + + /* + * Locate the transformation rule itself + */ + tname = str_concat(s->name, t->name, 0); + ln = Lst_Find(transforms, tname, SuffGNHasNameP); + free(tname); + + if (ln == NULL) { + /* + * Not really such a transformation rule (can happen when we're + * called to link an OP_MEMBER and OP_ARCHV node), so return + * FALSE. + */ + return(FALSE); + } + + gn = (GNode *)Lst_Datum(ln); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "\tapplying %s -> %s to \"%s\"\n", s->name, t->name, tGn->name); + } + + /* + * Record last child for expansion purposes + */ + ln = Lst_Last(tGn->children); + + /* + * Pass the buck to Make_HandleUse to apply the rule + */ + (void)Make_HandleUse(gn, tGn); + + /* + * Deal with wildcards and variables in any acquired sources + */ + for (ln = Lst_Succ(ln); ln != NULL; ln = nln) { + nln = Lst_Succ(ln); + SuffExpandChildren(ln, tGn); + } + + /* + * Keep track of another parent to which this beast is transformed so + * the .IMPSRC variable can be set correctly for the parent. + */ + (void)Lst_AtEnd(sGn->iParents, tGn); + + return(TRUE); +} + + +/*- + *----------------------------------------------------------------------- + * SuffFindArchiveDeps -- + * Locate dependencies for an OP_ARCHV node. + * + * Input: + * gn Node for which to locate dependencies + * + * Results: + * None + * + * Side Effects: + * Same as Suff_FindDeps + * + *----------------------------------------------------------------------- + */ +static void +SuffFindArchiveDeps(GNode *gn, Lst slst) +{ + char *eoarch; /* End of archive portion */ + char *eoname; /* End of member portion */ + GNode *mem; /* Node for member */ + static const char *copy[] = { + /* Variables to be copied from the member node */ + TARGET, /* Must be first */ + PREFIX, /* Must be second */ + }; + int i; /* Index into copy and vals */ + Suff *ms; /* Suffix descriptor for member */ + char *name; /* Start of member's name */ + + /* + * The node is an archive(member) pair. so we must find a + * suffix for both of them. + */ + eoarch = strchr(gn->name, '('); + eoname = strchr(eoarch, ')'); + + *eoname = '\0'; /* Nuke parentheses during suffix search */ + *eoarch = '\0'; /* So a suffix can be found */ + + name = eoarch + 1; + + /* + * To simplify things, call Suff_FindDeps recursively on the member now, + * so we can simply compare the member's .PREFIX and .TARGET variables + * to locate its suffix. This allows us to figure out the suffix to + * use for the archive without having to do a quadratic search over the + * suffix list, backtracking for each one... + */ + mem = Targ_FindNode(name, TARG_CREATE); + SuffFindDeps(mem, slst); + + /* + * Create the link between the two nodes right off + */ + (void)Lst_AtEnd(gn->children, mem); + (void)Lst_AtEnd(mem->parents, gn); + gn->unmade += 1; + + /* + * Copy in the variables from the member node to this one. + */ + for (i = (sizeof(copy)/sizeof(copy[0]))-1; i >= 0; i--) { + char *p1; + Var_Set(copy[i], Var_Value(copy[i], mem, &p1), gn, 0); + if (p1) + free(p1); + + } + + ms = mem->suffix; + if (ms == NULL) { + /* + * Didn't know what it was -- use .NULL suffix if not in make mode + */ + if (DEBUG(SUFF)) { + fprintf(debug_file, "using null suffix\n"); + } + ms = suffNull; + } + + + /* + * Set the other two local variables required for this target. + */ + Var_Set(MEMBER, name, gn, 0); + Var_Set(ARCHIVE, gn->name, gn, 0); + + if (ms != NULL) { + /* + * Member has a known suffix, so look for a transformation rule from + * it to a possible suffix of the archive. Rather than searching + * through the entire list, we just look at suffixes to which the + * member's suffix may be transformed... + */ + LstNode ln; + SuffixCmpData sd; /* Search string data */ + + /* + * Use first matching suffix... + */ + sd.len = eoarch - gn->name; + sd.ename = eoarch; + ln = Lst_Find(ms->parents, &sd, SuffSuffIsSuffixP); + + if (ln != NULL) { + /* + * Got one -- apply it + */ + if (!SuffApplyTransform(gn, mem, (Suff *)Lst_Datum(ln), ms) && + DEBUG(SUFF)) + { + fprintf(debug_file, "\tNo transformation from %s -> %s\n", + ms->name, ((Suff *)Lst_Datum(ln))->name); + } + } + } + + /* + * Replace the opening and closing parens now we've no need of the separate + * pieces. + */ + *eoarch = '('; *eoname = ')'; + + /* + * Pretend gn appeared to the left of a dependency operator so + * the user needn't provide a transformation from the member to the + * archive. + */ + if (OP_NOP(gn->type)) { + gn->type |= OP_DEPENDS; + } + + /* + * Flag the member as such so we remember to look in the archive for + * its modification time. + */ + mem->type |= OP_MEMBER; +} + +/*- + *----------------------------------------------------------------------- + * SuffFindNormalDeps -- + * Locate implicit dependencies for regular targets. + * + * Input: + * gn Node for which to find sources + * + * Results: + * None. + * + * Side Effects: + * Same as Suff_FindDeps... + * + *----------------------------------------------------------------------- + */ +static void +SuffFindNormalDeps(GNode *gn, Lst slst) +{ + char *eoname; /* End of name */ + char *sopref; /* Start of prefix */ + LstNode ln, nln; /* Next suffix node to check */ + Lst srcs; /* List of sources at which to look */ + Lst targs; /* List of targets to which things can be + * transformed. They all have the same file, + * but different suff and pref fields */ + Src *bottom; /* Start of found transformation path */ + Src *src; /* General Src pointer */ + char *pref; /* Prefix to use */ + Src *targ; /* General Src target pointer */ + SuffixCmpData sd; /* Search string data */ + + + sd.len = strlen(gn->name); + sd.ename = eoname = gn->name + sd.len; + + sopref = gn->name; + + /* + * Begin at the beginning... + */ + ln = Lst_First(sufflist); + srcs = Lst_Init(FALSE); + targs = Lst_Init(FALSE); + + /* + * We're caught in a catch-22 here. On the one hand, we want to use any + * transformation implied by the target's sources, but we can't examine + * the sources until we've expanded any variables/wildcards they may hold, + * and we can't do that until we've set up the target's local variables + * and we can't do that until we know what the proper suffix for the + * target is (in case there are two suffixes one of which is a suffix of + * the other) and we can't know that until we've found its implied + * source, which we may not want to use if there's an existing source + * that implies a different transformation. + * + * In an attempt to get around this, which may not work all the time, + * but should work most of the time, we look for implied sources first, + * checking transformations to all possible suffixes of the target, + * use what we find to set the target's local variables, expand the + * children, then look for any overriding transformations they imply. + * Should we find one, we discard the one we found before. + */ + bottom = NULL; + targ = NULL; + + if (!(gn->type & OP_PHONY)) { + + while (ln != NULL) { + /* + * Look for next possible suffix... + */ + ln = Lst_FindFrom(sufflist, ln, &sd, SuffSuffIsSuffixP); + + if (ln != NULL) { + int prefLen; /* Length of the prefix */ + + /* + * Allocate a Src structure to which things can be transformed + */ + targ = bmake_malloc(sizeof(Src)); + targ->file = bmake_strdup(gn->name); + targ->suff = (Suff *)Lst_Datum(ln); + targ->suff->refCount++; + targ->node = gn; + targ->parent = NULL; + targ->children = 0; +#ifdef DEBUG_SRC + targ->cp = Lst_Init(FALSE); +#endif + + /* + * Allocate room for the prefix, whose end is found by + * subtracting the length of the suffix from + * the end of the name. + */ + prefLen = (eoname - targ->suff->nameLen) - sopref; + targ->pref = bmake_malloc(prefLen + 1); + memcpy(targ->pref, sopref, prefLen); + targ->pref[prefLen] = '\0'; + + /* + * Add nodes from which the target can be made + */ + SuffAddLevel(srcs, targ); + + /* + * Record the target so we can nuke it + */ + (void)Lst_AtEnd(targs, targ); + + /* + * Search from this suffix's successor... + */ + ln = Lst_Succ(ln); + } + } + + /* + * Handle target of unknown suffix... + */ + if (Lst_IsEmpty(targs) && suffNull != NULL) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "\tNo known suffix on %s. Using .NULL suffix\n", gn->name); + } + + targ = bmake_malloc(sizeof(Src)); + targ->file = bmake_strdup(gn->name); + targ->suff = suffNull; + targ->suff->refCount++; + targ->node = gn; + targ->parent = NULL; + targ->children = 0; + targ->pref = bmake_strdup(sopref); +#ifdef DEBUG_SRC + targ->cp = Lst_Init(FALSE); +#endif + + /* + * Only use the default suffix rules if we don't have commands + * defined for this gnode; traditional make programs used to + * not define suffix rules if the gnode had children but we + * don't do this anymore. + */ + if (Lst_IsEmpty(gn->commands)) + SuffAddLevel(srcs, targ); + else { + if (DEBUG(SUFF)) + fprintf(debug_file, "not "); + } + + if (DEBUG(SUFF)) + fprintf(debug_file, "adding suffix rules\n"); + + (void)Lst_AtEnd(targs, targ); + } + + /* + * Using the list of possible sources built up from the target + * suffix(es), try and find an existing file/target that matches. + */ + bottom = SuffFindThem(srcs, slst); + + if (bottom == NULL) { + /* + * No known transformations -- use the first suffix found + * for setting the local variables. + */ + if (!Lst_IsEmpty(targs)) { + targ = (Src *)Lst_Datum(Lst_First(targs)); + } else { + targ = NULL; + } + } else { + /* + * Work up the transformation path to find the suffix of the + * target to which the transformation was made. + */ + for (targ = bottom; targ->parent != NULL; targ = targ->parent) + continue; + } + } + + Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0); + + pref = (targ != NULL) ? targ->pref : gn->name; + Var_Set(PREFIX, pref, gn, 0); + + /* + * Now we've got the important local variables set, expand any sources + * that still contain variables or wildcards in their names. + */ + for (ln = Lst_First(gn->children); ln != NULL; ln = nln) { + nln = Lst_Succ(ln); + SuffExpandChildren(ln, gn); + } + + if (targ == NULL) { + if (DEBUG(SUFF)) { + fprintf(debug_file, "\tNo valid suffix on %s\n", gn->name); + } + +sfnd_abort: + /* + * Deal with finding the thing on the default search path. We + * always do that, not only if the node is only a source (not + * on the lhs of a dependency operator or [XXX] it has neither + * children or commands) as the old pmake did. + */ + if ((gn->type & (OP_PHONY|OP_NOPATH)) == 0) { + free(gn->path); + gn->path = Dir_FindFile(gn->name, + (targ == NULL ? dirSearchPath : + targ->suff->searchPath)); + if (gn->path != NULL) { + char *ptr; + Var_Set(TARGET, gn->path, gn, 0); + + if (targ != NULL) { + /* + * Suffix known for the thing -- trim the suffix off + * the path to form the proper .PREFIX variable. + */ + int savep = strlen(gn->path) - targ->suff->nameLen; + char savec; + + if (gn->suffix) + gn->suffix->refCount--; + gn->suffix = targ->suff; + gn->suffix->refCount++; + + savec = gn->path[savep]; + gn->path[savep] = '\0'; + + if ((ptr = strrchr(gn->path, '/')) != NULL) + ptr++; + else + ptr = gn->path; + + Var_Set(PREFIX, ptr, gn, 0); + + gn->path[savep] = savec; + } else { + /* + * The .PREFIX gets the full path if the target has + * no known suffix. + */ + if (gn->suffix) + gn->suffix->refCount--; + gn->suffix = NULL; + + if ((ptr = strrchr(gn->path, '/')) != NULL) + ptr++; + else + ptr = gn->path; + + Var_Set(PREFIX, ptr, gn, 0); + } + } + } + + goto sfnd_return; + } + + /* + * If the suffix indicates that the target is a library, mark that in + * the node's type field. + */ + if (targ->suff->flags & SUFF_LIBRARY) { + gn->type |= OP_LIB; + } + + /* + * Check for overriding transformation rule implied by sources + */ + if (!Lst_IsEmpty(gn->children)) { + src = SuffFindCmds(targ, slst); + + if (src != NULL) { + /* + * Free up all the Src structures in the transformation path + * up to, but not including, the parent node. + */ + while (bottom && bottom->parent != NULL) { + if (Lst_Member(slst, bottom) == NULL) { + Lst_AtEnd(slst, bottom); + } + bottom = bottom->parent; + } + bottom = src; + } + } + + if (bottom == NULL) { + /* + * No idea from where it can come -- return now. + */ + goto sfnd_abort; + } + + /* + * We now have a list of Src structures headed by 'bottom' and linked via + * their 'parent' pointers. What we do next is create links between + * source and target nodes (which may or may not have been created) + * and set the necessary local variables in each target. The + * commands for each target are set from the commands of the + * transformation rule used to get from the src suffix to the targ + * suffix. Note that this causes the commands list of the original + * node, gn, to be replaced by the commands of the final + * transformation rule. Also, the unmade field of gn is incremented. + * Etc. + */ + if (bottom->node == NULL) { + bottom->node = Targ_FindNode(bottom->file, TARG_CREATE); + } + + for (src = bottom; src->parent != NULL; src = src->parent) { + targ = src->parent; + + if (src->node->suffix) + src->node->suffix->refCount--; + src->node->suffix = src->suff; + src->node->suffix->refCount++; + + if (targ->node == NULL) { + targ->node = Targ_FindNode(targ->file, TARG_CREATE); + } + + SuffApplyTransform(targ->node, src->node, + targ->suff, src->suff); + + if (targ->node != gn) { + /* + * Finish off the dependency-search process for any nodes + * between bottom and gn (no point in questing around the + * filesystem for their implicit source when it's already + * known). Note that the node can't have any sources that + * need expanding, since SuffFindThem will stop on an existing + * node, so all we need to do is set the standard and System V + * variables. + */ + targ->node->type |= OP_DEPS_FOUND; + + Var_Set(PREFIX, targ->pref, targ->node, 0); + + Var_Set(TARGET, targ->node->name, targ->node, 0); + } + } + + if (gn->suffix) + gn->suffix->refCount--; + gn->suffix = src->suff; + gn->suffix->refCount++; + + /* + * Nuke the transformation path and the Src structures left over in the + * two lists. + */ +sfnd_return: + if (bottom) + if (Lst_Member(slst, bottom) == NULL) + Lst_AtEnd(slst, bottom); + + while (SuffRemoveSrc(srcs) || SuffRemoveSrc(targs)) + continue; + + Lst_Concat(slst, srcs, LST_CONCLINK); + Lst_Concat(slst, targs, LST_CONCLINK); +} + + +/*- + *----------------------------------------------------------------------- + * Suff_FindDeps -- + * Find implicit sources for the target described by the graph node + * gn + * + * Results: + * Nothing. + * + * Side Effects: + * Nodes are added to the graph below the passed-in node. The nodes + * are marked to have their IMPSRC variable filled in. The + * PREFIX variable is set for the given node and all its + * implied children. + * + * Notes: + * The path found by this target is the shortest path in the + * transformation graph, which may pass through non-existent targets, + * to an existing target. The search continues on all paths from the + * root suffix until a file is found. I.e. if there's a path + * .o -> .c -> .l -> .l,v from the root and the .l,v file exists but + * the .c and .l files don't, the search will branch out in + * all directions from .o and again from all the nodes on the + * next level until the .l,v node is encountered. + * + *----------------------------------------------------------------------- + */ + +void +Suff_FindDeps(GNode *gn) +{ + + SuffFindDeps(gn, srclist); + while (SuffRemoveSrc(srclist)) + continue; +} + + +/* + * Input: + * gn node we're dealing with + * + */ +static void +SuffFindDeps(GNode *gn, Lst slst) +{ + if (gn->type & OP_DEPS_FOUND) { + /* + * If dependencies already found, no need to do it again... + */ + return; + } else { + gn->type |= OP_DEPS_FOUND; + } + /* + * Make sure we have these set, may get revised below. + */ + Var_Set(TARGET, gn->path ? gn->path : gn->name, gn, 0); + Var_Set(PREFIX, gn->name, gn, 0); + + if (DEBUG(SUFF)) { + fprintf(debug_file, "SuffFindDeps (%s)\n", gn->name); + } + + if (gn->type & OP_ARCHV) { + SuffFindArchiveDeps(gn, slst); + } else if (gn->type & OP_LIB) { + /* + * If the node is a library, it is the arch module's job to find it + * and set the TARGET variable accordingly. We merely provide the + * search path, assuming all libraries end in ".a" (if the suffix + * hasn't been defined, there's nothing we can do for it, so we just + * set the TARGET variable to the node's name in order to give it a + * value). + */ + LstNode ln; + Suff *s; + + ln = Lst_Find(sufflist, LIBSUFF, SuffSuffHasNameP); + if (gn->suffix) + gn->suffix->refCount--; + if (ln != NULL) { + gn->suffix = s = (Suff *)Lst_Datum(ln); + gn->suffix->refCount++; + Arch_FindLib(gn, s->searchPath); + } else { + gn->suffix = NULL; + Var_Set(TARGET, gn->name, gn, 0); + } + /* + * Because a library (-lfoo) target doesn't follow the standard + * filesystem conventions, we don't set the regular variables for + * the thing. .PREFIX is simply made empty... + */ + Var_Set(PREFIX, "", gn, 0); + } else { + SuffFindNormalDeps(gn, slst); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_SetNull -- + * Define which suffix is the null suffix. + * + * Input: + * name Name of null suffix + * + * Results: + * None. + * + * Side Effects: + * 'suffNull' is altered. + * + * Notes: + * Need to handle the changing of the null suffix gracefully so the + * old transformation rules don't just go away. + * + *----------------------------------------------------------------------- + */ +void +Suff_SetNull(char *name) +{ + Suff *s; + LstNode ln; + + ln = Lst_Find(sufflist, name, SuffSuffHasNameP); + if (ln != NULL) { + s = (Suff *)Lst_Datum(ln); + if (suffNull != NULL) { + suffNull->flags &= ~SUFF_NULL; + } + s->flags |= SUFF_NULL; + /* + * XXX: Here's where the transformation mangling would take place + */ + suffNull = s; + } else { + Parse_Error(PARSE_WARNING, "Desired null suffix %s not defined.", + name); + } +} + +/*- + *----------------------------------------------------------------------- + * Suff_Init -- + * Initialize suffixes module + * + * Results: + * None + * + * Side Effects: + * Many + *----------------------------------------------------------------------- + */ +void +Suff_Init(void) +{ + sufflist = Lst_Init(FALSE); +#ifdef CLEANUP + suffClean = Lst_Init(FALSE); +#endif + srclist = Lst_Init(FALSE); + transforms = Lst_Init(FALSE); + + sNum = 0; + /* + * Create null suffix for single-suffix rules (POSIX). The thing doesn't + * actually go on the suffix list or everyone will think that's its + * suffix. + */ + emptySuff = suffNull = bmake_malloc(sizeof(Suff)); + + suffNull->name = bmake_strdup(""); + suffNull->nameLen = 0; + suffNull->searchPath = Lst_Init(FALSE); + Dir_Concat(suffNull->searchPath, dirSearchPath); + suffNull->children = Lst_Init(FALSE); + suffNull->parents = Lst_Init(FALSE); + suffNull->ref = Lst_Init(FALSE); + suffNull->sNum = sNum++; + suffNull->flags = SUFF_NULL; + suffNull->refCount = 1; + +} + + +/*- + *---------------------------------------------------------------------- + * Suff_End -- + * Cleanup the this module + * + * Results: + * None + * + * Side Effects: + * The memory is free'd. + *---------------------------------------------------------------------- + */ + +void +Suff_End(void) +{ +#ifdef CLEANUP + Lst_Destroy(sufflist, SuffFree); + Lst_Destroy(suffClean, SuffFree); + if (suffNull) + SuffFree(suffNull); + Lst_Destroy(srclist, NULL); + Lst_Destroy(transforms, NULL); +#endif +} + + +/********************* DEBUGGING FUNCTIONS **********************/ + +static int SuffPrintName(void *s, void *dummy) +{ + fprintf(debug_file, "%s ", ((Suff *)s)->name); + return (dummy ? 0 : 0); +} + +static int +SuffPrintSuff(void *sp, void *dummy) +{ + Suff *s = (Suff *)sp; + int flags; + int flag; + + fprintf(debug_file, "# `%s' [%d] ", s->name, s->refCount); + + flags = s->flags; + if (flags) { + fputs(" (", debug_file); + while (flags) { + flag = 1 << (ffs(flags) - 1); + flags &= ~flag; + switch (flag) { + case SUFF_NULL: + fprintf(debug_file, "NULL"); + break; + case SUFF_INCLUDE: + fprintf(debug_file, "INCLUDE"); + break; + case SUFF_LIBRARY: + fprintf(debug_file, "LIBRARY"); + break; + } + fputc(flags ? '|' : ')', debug_file); + } + } + fputc('\n', debug_file); + fprintf(debug_file, "#\tTo: "); + Lst_ForEach(s->parents, SuffPrintName, NULL); + fputc('\n', debug_file); + fprintf(debug_file, "#\tFrom: "); + Lst_ForEach(s->children, SuffPrintName, NULL); + fputc('\n', debug_file); + fprintf(debug_file, "#\tSearch Path: "); + Dir_PrintPath(s->searchPath); + fputc('\n', debug_file); + return (dummy ? 0 : 0); +} + +static int +SuffPrintTrans(void *tp, void *dummy) +{ + GNode *t = (GNode *)tp; + + fprintf(debug_file, "%-16s: ", t->name); + Targ_PrintType(t->type); + fputc('\n', debug_file); + Lst_ForEach(t->commands, Targ_PrintCmd, NULL); + fputc('\n', debug_file); + return(dummy ? 0 : 0); +} + +void +Suff_PrintAll(void) +{ + fprintf(debug_file, "#*** Suffixes:\n"); + Lst_ForEach(sufflist, SuffPrintSuff, NULL); + + fprintf(debug_file, "#*** Transformations:\n"); + Lst_ForEach(transforms, SuffPrintTrans, NULL); +} diff --git a/buildrump.sh/src/usr.bin/make/targ.c b/buildrump.sh/src/usr.bin/make/targ.c new file mode 100644 index 00000000..f6039262 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/targ.c @@ -0,0 +1,848 @@ +/* $NetBSD: targ.c,v 1.59 2014/09/07 20:55:34 joerg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: targ.c,v 1.59 2014/09/07 20:55:34 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)targ.c 8.2 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: targ.c,v 1.59 2014/09/07 20:55:34 joerg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * targ.c -- + * Functions for maintaining the Lst allTargets. Target nodes are + * kept in two structures: a Lst, maintained by the list library, and a + * hash table, maintained by the hash library. + * + * Interface: + * Targ_Init Initialization procedure. + * + * Targ_End Cleanup the module + * + * Targ_List Return the list of all targets so far. + * + * Targ_NewGN Create a new GNode for the passed target + * (string). The node is *not* placed in the + * hash table, though all its fields are + * initialized. + * + * Targ_FindNode Find the node for a given target, creating + * and storing it if it doesn't exist and the + * flags are right (TARG_CREATE) + * + * Targ_FindList Given a list of names, find nodes for all + * of them. If a name doesn't exist and the + * TARG_NOCREATE flag was given, an error message + * is printed. Else, if a name doesn't exist, + * its node is created. + * + * Targ_Ignore Return TRUE if errors should be ignored when + * creating the given target. + * + * Targ_Silent Return TRUE if we should be silent when + * creating the given target. + * + * Targ_Precious Return TRUE if the target is precious and + * should not be removed if we are interrupted. + * + * Targ_Propagate Propagate information between related + * nodes. Should be called after the + * makefiles are parsed but before any + * action is taken. + * + * Debugging: + * Targ_PrintGraph Print out the entire graphm all variables + * and statistics for the directory cache. Should + * print something for suffixes, too, but... + */ + +#include <stdio.h> +#include <time.h> + +#include "make.h" +#include "hash.h" +#include "dir.h" + +static Lst allTargets; /* the list of all targets found so far */ +#ifdef CLEANUP +static Lst allGNs; /* List of all the GNodes */ +#endif +static Hash_Table targets; /* a hash table of same */ + +#define HTSIZE 191 /* initial size of hash table */ + +static int TargPrintOnlySrc(void *, void *); +static int TargPrintName(void *, void *); +#ifdef CLEANUP +static void TargFreeGN(void *); +#endif +static int TargPropagateCohort(void *, void *); +static int TargPropagateNode(void *, void *); + +/*- + *----------------------------------------------------------------------- + * Targ_Init -- + * Initialize this module + * + * Results: + * None + * + * Side Effects: + * The allTargets list and the targets hash table are initialized + *----------------------------------------------------------------------- + */ +void +Targ_Init(void) +{ + allTargets = Lst_Init(FALSE); + Hash_InitTable(&targets, HTSIZE); +} + +/*- + *----------------------------------------------------------------------- + * Targ_End -- + * Finalize this module + * + * Results: + * None + * + * Side Effects: + * All lists and gnodes are cleared + *----------------------------------------------------------------------- + */ +void +Targ_End(void) +{ +#ifdef CLEANUP + Lst_Destroy(allTargets, NULL); + if (allGNs) + Lst_Destroy(allGNs, TargFreeGN); + Hash_DeleteTable(&targets); +#endif +} + +/*- + *----------------------------------------------------------------------- + * Targ_List -- + * Return the list of all targets + * + * Results: + * The list of all targets. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Lst +Targ_List(void) +{ + return allTargets; +} + +/*- + *----------------------------------------------------------------------- + * Targ_NewGN -- + * Create and initialize a new graph node + * + * Input: + * name the name to stick in the new node + * + * Results: + * An initialized graph node with the name field filled with a copy + * of the passed name + * + * Side Effects: + * The gnode is added to the list of all gnodes. + *----------------------------------------------------------------------- + */ +GNode * +Targ_NewGN(const char *name) +{ + GNode *gn; + + gn = bmake_malloc(sizeof(GNode)); + gn->name = bmake_strdup(name); + gn->uname = NULL; + gn->path = NULL; + if (name[0] == '-' && name[1] == 'l') { + gn->type = OP_LIB; + } else { + gn->type = 0; + } + gn->unmade = 0; + gn->unmade_cohorts = 0; + gn->cohort_num[0] = 0; + gn->centurion = NULL; + gn->made = UNMADE; + gn->flags = 0; + gn->checked = 0; + gn->mtime = 0; + gn->cmgn = NULL; + gn->iParents = Lst_Init(FALSE); + gn->cohorts = Lst_Init(FALSE); + gn->parents = Lst_Init(FALSE); + gn->children = Lst_Init(FALSE); + gn->order_pred = Lst_Init(FALSE); + gn->order_succ = Lst_Init(FALSE); + Hash_InitTable(&gn->context, 0); + gn->commands = Lst_Init(FALSE); + gn->suffix = NULL; + gn->lineno = 0; + gn->fname = NULL; + +#ifdef CLEANUP + if (allGNs == NULL) + allGNs = Lst_Init(FALSE); + Lst_AtEnd(allGNs, gn); +#endif + + return (gn); +} + +#ifdef CLEANUP +/*- + *----------------------------------------------------------------------- + * TargFreeGN -- + * Destroy a GNode + * + * Results: + * None. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static void +TargFreeGN(void *gnp) +{ + GNode *gn = (GNode *)gnp; + + + free(gn->name); + if (gn->uname) + free(gn->uname); + if (gn->path) + free(gn->path); + /* gn->fname points to name allocated when file was opened, don't free */ + + Lst_Destroy(gn->iParents, NULL); + Lst_Destroy(gn->cohorts, NULL); + Lst_Destroy(gn->parents, NULL); + Lst_Destroy(gn->children, NULL); + Lst_Destroy(gn->order_succ, NULL); + Lst_Destroy(gn->order_pred, NULL); + Hash_DeleteTable(&gn->context); + Lst_Destroy(gn->commands, NULL); + free(gn); +} +#endif + + +/*- + *----------------------------------------------------------------------- + * Targ_FindNode -- + * Find a node in the list using the given name for matching + * + * Input: + * name the name to find + * flags flags governing events when target not + * found + * + * Results: + * The node in the list if it was. If it wasn't, return NULL of + * flags was TARG_NOCREATE or the newly created and initialized node + * if it was TARG_CREATE + * + * Side Effects: + * Sometimes a node is created and added to the list + *----------------------------------------------------------------------- + */ +GNode * +Targ_FindNode(const char *name, int flags) +{ + GNode *gn; /* node in that element */ + Hash_Entry *he; /* New or used hash entry for node */ + Boolean isNew; /* Set TRUE if Hash_CreateEntry had to create */ + /* an entry for the node */ + + if (!(flags & (TARG_CREATE | TARG_NOHASH))) { + he = Hash_FindEntry(&targets, name); + if (he == NULL) + return NULL; + return (GNode *)Hash_GetValue(he); + } + + if (!(flags & TARG_NOHASH)) { + he = Hash_CreateEntry(&targets, name, &isNew); + if (!isNew) + return (GNode *)Hash_GetValue(he); + } + + gn = Targ_NewGN(name); + if (!(flags & TARG_NOHASH)) + Hash_SetValue(he, gn); + Var_Append(".ALLTARGETS", name, VAR_GLOBAL); + (void)Lst_AtEnd(allTargets, gn); + if (doing_depend) + gn->flags |= FROM_DEPEND; + return gn; +} + +/*- + *----------------------------------------------------------------------- + * Targ_FindList -- + * Make a complete list of GNodes from the given list of names + * + * Input: + * name list of names to find + * flags flags used if no node is found for a given name + * + * Results: + * A complete list of graph nodes corresponding to all instances of all + * the names in names. + * + * Side Effects: + * If flags is TARG_CREATE, nodes will be created for all names in + * names which do not yet have graph nodes. If flags is TARG_NOCREATE, + * an error message will be printed for each name which can't be found. + * ----------------------------------------------------------------------- + */ +Lst +Targ_FindList(Lst names, int flags) +{ + Lst nodes; /* result list */ + LstNode ln; /* name list element */ + GNode *gn; /* node in tLn */ + char *name; + + nodes = Lst_Init(FALSE); + + if (Lst_Open(names) == FAILURE) { + return (nodes); + } + while ((ln = Lst_Next(names)) != NULL) { + name = (char *)Lst_Datum(ln); + gn = Targ_FindNode(name, flags); + if (gn != NULL) { + /* + * Note: Lst_AtEnd must come before the Lst_Concat so the nodes + * are added to the list in the order in which they were + * encountered in the makefile. + */ + (void)Lst_AtEnd(nodes, gn); + } else if (flags == TARG_NOCREATE) { + Error("\"%s\" -- target unknown.", name); + } + } + Lst_Close(names); + return (nodes); +} + +/*- + *----------------------------------------------------------------------- + * Targ_Ignore -- + * Return true if should ignore errors when creating gn + * + * Input: + * gn node to check for + * + * Results: + * TRUE if should ignore errors + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Targ_Ignore(GNode *gn) +{ + if (ignoreErrors || gn->type & OP_IGNORE) { + return (TRUE); + } else { + return (FALSE); + } +} + +/*- + *----------------------------------------------------------------------- + * Targ_Silent -- + * Return true if be silent when creating gn + * + * Input: + * gn node to check for + * + * Results: + * TRUE if should be silent + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Targ_Silent(GNode *gn) +{ + if (beSilent || gn->type & OP_SILENT) { + return (TRUE); + } else { + return (FALSE); + } +} + +/*- + *----------------------------------------------------------------------- + * Targ_Precious -- + * See if the given target is precious + * + * Input: + * gn the node to check + * + * Results: + * TRUE if it is precious. FALSE otherwise + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +Boolean +Targ_Precious(GNode *gn) +{ + if (allPrecious || (gn->type & (OP_PRECIOUS|OP_DOUBLEDEP))) { + return (TRUE); + } else { + return (FALSE); + } +} + +/******************* DEBUG INFO PRINTING ****************/ + +static GNode *mainTarg; /* the main target, as set by Targ_SetMain */ +/*- + *----------------------------------------------------------------------- + * Targ_SetMain -- + * Set our idea of the main target we'll be creating. Used for + * debugging output. + * + * Input: + * gn The main target we'll create + * + * Results: + * None. + * + * Side Effects: + * "mainTarg" is set to the main target's node. + *----------------------------------------------------------------------- + */ +void +Targ_SetMain(GNode *gn) +{ + mainTarg = gn; +} + +static int +TargPrintName(void *gnp, void *pflags MAKE_ATTR_UNUSED) +{ + GNode *gn = (GNode *)gnp; + + fprintf(debug_file, "%s%s ", gn->name, gn->cohort_num); + + return 0; +} + + +int +Targ_PrintCmd(void *cmd, void *dummy) +{ + fprintf(debug_file, "\t%s\n", (char *)cmd); + return (dummy ? 0 : 0); +} + +/*- + *----------------------------------------------------------------------- + * Targ_FmtTime -- + * Format a modification time in some reasonable way and return it. + * + * Results: + * The time reformatted. + * + * Side Effects: + * The time is placed in a static area, so it is overwritten + * with each call. + * + *----------------------------------------------------------------------- + */ +char * +Targ_FmtTime(time_t tm) +{ + struct tm *parts; + static char buf[128]; + + parts = localtime(&tm); + (void)strftime(buf, sizeof buf, "%k:%M:%S %b %d, %Y", parts); + return(buf); +} + +/*- + *----------------------------------------------------------------------- + * Targ_PrintType -- + * Print out a type field giving only those attributes the user can + * set. + * + * Results: + * + * Side Effects: + * + *----------------------------------------------------------------------- + */ +void +Targ_PrintType(int type) +{ + int tbit; + +#define PRINTBIT(attr) case CONCAT(OP_,attr): fprintf(debug_file, "." #attr " "); break +#define PRINTDBIT(attr) case CONCAT(OP_,attr): if (DEBUG(TARG))fprintf(debug_file, "." #attr " "); break + + type &= ~OP_OPMASK; + + while (type) { + tbit = 1 << (ffs(type) - 1); + type &= ~tbit; + + switch(tbit) { + PRINTBIT(OPTIONAL); + PRINTBIT(USE); + PRINTBIT(EXEC); + PRINTBIT(IGNORE); + PRINTBIT(PRECIOUS); + PRINTBIT(SILENT); + PRINTBIT(MAKE); + PRINTBIT(JOIN); + PRINTBIT(INVISIBLE); + PRINTBIT(NOTMAIN); + PRINTDBIT(LIB); + /*XXX: MEMBER is defined, so CONCAT(OP_,MEMBER) gives OP_"%" */ + case OP_MEMBER: if (DEBUG(TARG))fprintf(debug_file, ".MEMBER "); break; + PRINTDBIT(ARCHV); + PRINTDBIT(MADE); + PRINTDBIT(PHONY); + } + } +} + +static const char * +made_name(enum enum_made made) +{ + switch (made) { + case UNMADE: return "unmade"; + case DEFERRED: return "deferred"; + case REQUESTED: return "requested"; + case BEINGMADE: return "being made"; + case MADE: return "made"; + case UPTODATE: return "up-to-date"; + case ERROR: return "error when made"; + case ABORTED: return "aborted"; + default: return "unknown enum_made value"; + } +} + +/*- + *----------------------------------------------------------------------- + * TargPrintNode -- + * print the contents of a node + *----------------------------------------------------------------------- + */ +int +Targ_PrintNode(void *gnp, void *passp) +{ + GNode *gn = (GNode *)gnp; + int pass = passp ? *(int *)passp : 0; + + fprintf(debug_file, "# %s%s, flags %x, type %x, made %d\n", + gn->name, gn->cohort_num, gn->flags, gn->type, gn->made); + if (gn->flags == 0) + return 0; + + if (!OP_NOP(gn->type)) { + fprintf(debug_file, "#\n"); + if (gn == mainTarg) { + fprintf(debug_file, "# *** MAIN TARGET ***\n"); + } + if (pass >= 2) { + if (gn->unmade) { + fprintf(debug_file, "# %d unmade children\n", gn->unmade); + } else { + fprintf(debug_file, "# No unmade children\n"); + } + if (! (gn->type & (OP_JOIN|OP_USE|OP_USEBEFORE|OP_EXEC))) { + if (gn->mtime != 0) { + fprintf(debug_file, "# last modified %s: %s\n", + Targ_FmtTime(gn->mtime), + made_name(gn->made)); + } else if (gn->made != UNMADE) { + fprintf(debug_file, "# non-existent (maybe): %s\n", + made_name(gn->made)); + } else { + fprintf(debug_file, "# unmade\n"); + } + } + if (!Lst_IsEmpty (gn->iParents)) { + fprintf(debug_file, "# implicit parents: "); + Lst_ForEach(gn->iParents, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + } else { + if (gn->unmade) + fprintf(debug_file, "# %d unmade children\n", gn->unmade); + } + if (!Lst_IsEmpty (gn->parents)) { + fprintf(debug_file, "# parents: "); + Lst_ForEach(gn->parents, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + if (!Lst_IsEmpty (gn->order_pred)) { + fprintf(debug_file, "# order_pred: "); + Lst_ForEach(gn->order_pred, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + if (!Lst_IsEmpty (gn->order_succ)) { + fprintf(debug_file, "# order_succ: "); + Lst_ForEach(gn->order_succ, TargPrintName, NULL); + fprintf(debug_file, "\n"); + } + + fprintf(debug_file, "%-16s", gn->name); + switch (gn->type & OP_OPMASK) { + case OP_DEPENDS: + fprintf(debug_file, ": "); break; + case OP_FORCE: + fprintf(debug_file, "! "); break; + case OP_DOUBLEDEP: + fprintf(debug_file, ":: "); break; + } + Targ_PrintType(gn->type); + Lst_ForEach(gn->children, TargPrintName, NULL); + fprintf(debug_file, "\n"); + Lst_ForEach(gn->commands, Targ_PrintCmd, NULL); + fprintf(debug_file, "\n\n"); + if (gn->type & OP_DOUBLEDEP) { + Lst_ForEach(gn->cohorts, Targ_PrintNode, &pass); + } + } + return (0); +} + +/*- + *----------------------------------------------------------------------- + * TargPrintOnlySrc -- + * Print only those targets that are just a source. + * + * Results: + * 0. + * + * Side Effects: + * The name of each file is printed preceded by #\t + * + *----------------------------------------------------------------------- + */ +static int +TargPrintOnlySrc(void *gnp, void *dummy MAKE_ATTR_UNUSED) +{ + GNode *gn = (GNode *)gnp; + if (!OP_NOP(gn->type)) + return 0; + + fprintf(debug_file, "#\t%s [%s] ", + gn->name, gn->path ? gn->path : gn->name); + Targ_PrintType(gn->type); + fprintf(debug_file, "\n"); + + return 0; +} + +/*- + *----------------------------------------------------------------------- + * Targ_PrintGraph -- + * print the entire graph. heh heh + * + * Input: + * pass Which pass this is. 1 => no processing + * 2 => processing done + * + * Results: + * none + * + * Side Effects: + * lots o' output + *----------------------------------------------------------------------- + */ +void +Targ_PrintGraph(int pass) +{ + fprintf(debug_file, "#*** Input graph:\n"); + Lst_ForEach(allTargets, Targ_PrintNode, &pass); + fprintf(debug_file, "\n\n"); + fprintf(debug_file, "#\n# Files that are only sources:\n"); + Lst_ForEach(allTargets, TargPrintOnlySrc, NULL); + fprintf(debug_file, "#*** Global Variables:\n"); + Var_Dump(VAR_GLOBAL); + fprintf(debug_file, "#*** Command-line Variables:\n"); + Var_Dump(VAR_CMD); + fprintf(debug_file, "\n"); + Dir_PrintDirectories(); + fprintf(debug_file, "\n"); + Suff_PrintAll(); +} + +/*- + *----------------------------------------------------------------------- + * TargPropagateNode -- + * Propagate information from a single node to related nodes if + * appropriate. + * + * Input: + * gnp The node that we are processing. + * + * Results: + * Always returns 0, for the benefit of Lst_ForEach(). + * + * Side Effects: + * Information is propagated from this node to cohort or child + * nodes. + * + * If the node was defined with "::", then TargPropagateCohort() + * will be called for each cohort node. + * + * If the node has recursive predecessors, then + * TargPropagateRecpred() will be called for each recursive + * predecessor. + *----------------------------------------------------------------------- + */ +static int +TargPropagateNode(void *gnp, void *junk MAKE_ATTR_UNUSED) +{ + GNode *gn = (GNode *)gnp; + + if (gn->type & OP_DOUBLEDEP) + Lst_ForEach(gn->cohorts, TargPropagateCohort, gnp); + return (0); +} + +/*- + *----------------------------------------------------------------------- + * TargPropagateCohort -- + * Propagate some bits in the type mask from a node to + * a related cohort node. + * + * Input: + * cnp The node that we are processing. + * gnp Another node that has cnp as a cohort. + * + * Results: + * Always returns 0, for the benefit of Lst_ForEach(). + * + * Side Effects: + * cnp's type bitmask is modified to incorporate some of the + * bits from gnp's type bitmask. (XXX need a better explanation.) + *----------------------------------------------------------------------- + */ +static int +TargPropagateCohort(void *cgnp, void *pgnp) +{ + GNode *cgn = (GNode *)cgnp; + GNode *pgn = (GNode *)pgnp; + + cgn->type |= pgn->type & ~OP_OPMASK; + return (0); +} + +/*- + *----------------------------------------------------------------------- + * Targ_Propagate -- + * Propagate information between related nodes. Should be called + * after the makefiles are parsed but before any action is taken. + * + * Results: + * none + * + * Side Effects: + * Information is propagated between related nodes throughout the + * graph. + *----------------------------------------------------------------------- + */ +void +Targ_Propagate(void) +{ + Lst_ForEach(allTargets, TargPropagateNode, NULL); +} diff --git a/buildrump.sh/src/usr.bin/make/trace.c b/buildrump.sh/src/usr.bin/make/trace.c new file mode 100644 index 00000000..267177ff --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/trace.c @@ -0,0 +1,116 @@ +/* $NetBSD: trace.c,v 1.11 2008/12/28 18:31:51 christos Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: trace.c,v 1.11 2008/12/28 18:31:51 christos Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: trace.c,v 1.11 2008/12/28 18:31:51 christos Exp $"); +#endif /* not lint */ +#endif + +/*- + * trace.c -- + * handle logging of trace events generated by various parts of make. + * + * Interface: + * Trace_Init Initialize tracing (called once during + * the lifetime of the process) + * + * Trace_End Finalize tracing (called before make exits) + * + * Trace_Log Log an event about a particular make job. + */ + +#include <sys/time.h> + +#include <stdio.h> +#include <unistd.h> + +#include "make.h" +#include "job.h" +#include "trace.h" + +static FILE *trfile; +static pid_t trpid; +char *trwd; + +static const char *evname[] = { + "BEG", + "END", + "ERR", + "JOB", + "DON", + "INT", +}; + +void +Trace_Init(const char *pathname) +{ + char *p1; + if (pathname != NULL) { + trpid = getpid(); + trwd = Var_Value(".CURDIR", VAR_GLOBAL, &p1); + + trfile = fopen(pathname, "a"); + } +} + +void +Trace_Log(TrEvent event, Job *job) +{ + struct timeval rightnow; + + if (trfile == NULL) + return; + + gettimeofday(&rightnow, NULL); + + fprintf(trfile, "%lld.%06ld %d %s %d %s", + (long long)rightnow.tv_sec, (long)rightnow.tv_usec, + jobTokensRunning, + evname[event], trpid, trwd); + if (job != NULL) { + fprintf(trfile, " %s %d %x %x", job->node->name, + job->pid, job->flags, job->node->type); + } + fputc('\n', trfile); + fflush(trfile); +} + +void +Trace_End(void) +{ + if (trfile != NULL) + fclose(trfile); +} diff --git a/buildrump.sh/src/usr.bin/make/trace.h b/buildrump.sh/src/usr.bin/make/trace.h new file mode 100644 index 00000000..dc0fc6cc --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/trace.h @@ -0,0 +1,49 @@ +/* $NetBSD: trace.h,v 1.3 2008/04/28 20:24:14 martin Exp $ */ + +/*- + * Copyright (c) 2000 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Bill Sommerfeld + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +/*- + * trace.h -- + * Definitions pertaining to the tracing of jobs in parallel mode. + */ + +typedef enum { + MAKESTART, + MAKEEND, + MAKEERROR, + JOBSTART, + JOBEND, + MAKEINTR +} TrEvent; + +void Trace_Init(const char *); +void Trace_Log(TrEvent, Job *); +void Trace_End(void); + diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/Makefile b/buildrump.sh/src/usr.bin/make/unit-tests/Makefile new file mode 100644 index 00000000..10503b93 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/Makefile @@ -0,0 +1,138 @@ +# $NetBSD: Makefile,v 1.52 2015/05/05 21:51:09 sjg Exp $ +# +# Unit tests for make(1) +# The main targets are: +# +# all: run all the tests +# test: run 'all', and compare to expected results +# accept: move generated output to expected results +# +# Adding a test case. +# Each feature should get its own set of tests in its own suitably +# named makefile (*.mk), with its own set of expected results (*.exp), +# and it should be added to the TESTNAMES list. +# + +.MAIN: all + +UNIT_TESTS:= ${.PARSEDIR} +.PATH: ${UNIT_TESTS} + +# Each test is in a sub-makefile. +# Keep the list sorted. +TESTNAMES= \ + comment \ + cond1 \ + cond2 \ + error \ + export \ + export-all \ + export-env \ + doterror \ + dotwait \ + forloop \ + forsubst \ + hash \ + misc \ + moderrs \ + modmatch \ + modmisc \ + modorder \ + modts \ + modword \ + order \ + posix \ + qequals \ + sunshcmd \ + sysv \ + ternary \ + unexport \ + unexport-env \ + varcmd \ + varmisc \ + varshell + +# these tests were broken by referting POSIX chanegs +STRICT_POSIX_TESTS = \ + escape \ + impsrc \ + phony-end \ + posix1 \ + suffixes + +# Override make flags for certain tests +flags.doterror= +flags.order=-j1 + +OUTFILES= ${TESTNAMES:S/$/.out/} + +all: ${OUTFILES} + +CLEANFILES += *.rawout *.out *.status *.tmp *.core *.tmp +CLEANFILES += obj*.[och] lib*.a # posix1.mk +CLEANFILES += issue* .[ab]* # suffixes.mk +CLEANRECURSIVE += dir dummy # posix1.mk + +clean: + rm -f ${CLEANFILES} +.if !empty(CLEANRECURSIVE) + rm -rf ${CLEANRECURSIVE} +.endif + +TEST_MAKE?= ${.MAKE} +TOOL_SED?= sed + +# ensure consistent results from sort(1) +LC_ALL= C +LANG= C +.export LANG LC_ALL + +# the tests are actually done with sub-makes. +.SUFFIXES: .mk .rawout .out +.mk.rawout: + @echo ${TEST_MAKE} ${flags.${.TARGET:R}:U-k} -f ${.IMPSRC} + -@cd ${.OBJDIR} && \ + { ${TEST_MAKE} ${flags.${.TARGET:R}:U-k} -f ${.IMPSRC} \ + 2>&1 ; echo $$? >${.TARGET:R}.status ; } > ${.TARGET}.tmp + @mv ${.TARGET}.tmp ${.TARGET} + +# We always pretend .MAKE was called 'make' +# and strip ${.CURDIR}/ from the output +# and replace anything after 'stopped in' with unit-tests +# so the results can be compared. +.rawout.out: + @echo postprocess ${.TARGET} + @${TOOL_SED} -e 's,^${TEST_MAKE:T:C/\./\\\./g}[][0-9]*:,make:,' \ + -e 's,${TEST_MAKE:C/\./\\\./g},make,' \ + -e '/stopped/s, /.*, unit-tests,' \ + -e 's,${.CURDIR:C/\./\\\./g}/,,g' \ + -e 's,${UNIT_TESTS:C/\./\\\./g}/,,g' \ + < ${.IMPSRC} > ${.TARGET}.tmp + @echo "exit status `cat ${.TARGET:R}.status`" >> ${.TARGET}.tmp + @mv ${.TARGET}.tmp ${.TARGET} + +# Compare all output files +test: ${OUTFILES} .PHONY + @failed= ; \ + for test in ${TESTNAMES}; do \ + diff -u ${UNIT_TESTS}/$${test}.exp $${test}.out \ + || failed="$${failed}$${failed:+ }$${test}" ; \ + done ; \ + if [ -n "$${failed}" ]; then \ + echo "Failed tests: $${failed}" ; false ; \ + else \ + echo "All tests passed" ; \ + fi + +accept: + @for test in ${TESTNAMES}; do \ + cmp -s ${UNIT_TESTS}/$${test}.exp $${test}.out \ + || { echo "Replacing $${test}.exp" ; \ + cp $${test}.out ${UNIT_TESTS}/$${test}.exp ; } \ + done + +.if exists(${TEST_MAKE}) +${TESTNAMES:S/$/.rawout/}: ${TEST_MAKE} +.endif + +.-include <bsd.obj.mk> diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/comment.exp b/buildrump.sh/src/usr.bin/make/unit-tests/comment.exp new file mode 100644 index 00000000..9a97df0b --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/comment.exp @@ -0,0 +1,5 @@ +comment testing start +this is foo +This is how a comment looks: # comment +comment testing done +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/comment.mk b/buildrump.sh/src/usr.bin/make/unit-tests/comment.mk new file mode 100644 index 00000000..7dd7dbbe --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/comment.mk @@ -0,0 +1,31 @@ +# This is a comment +.if ${MACHINE_ARCH} == something +FOO=bar +.endif + +#\ + Multiline comment + +BAR=# defined +FOOBAR= # defined + +# This is an escaped comment \ +that keeps going until the end of this line + +# Another escaped comment \ +that \ +goes \ +on + +# This is NOT an escaped comment due to the double backslashes \\ +all: hi foo bar + @echo comment testing done + +hi: + @echo comment testing start + +foo: + @echo this is $@ + +bar: + @echo This is how a comment looks: '# comment' diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/cond1.exp b/buildrump.sh/src/usr.bin/make/unit-tests/cond1.exp new file mode 100644 index 00000000..701d504f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/cond1.exp @@ -0,0 +1,23 @@ +make: "cond1.mk" line 75: warning: extra else +make: "cond1.mk" line 85: warning: extra else +2 is prime +A='other' B='unknown' C='clever' o='no,no' +Passed: + var + ("var") + (var != var) + var != var + !((var != var) && defined(name)) + var == quoted + +1 is not prime +2 is prime +3 is prime +4 is not prime +5 is prime + +make: warning: String comparison operator should be either == or != +make: Bad conditional expression `"0" > 0' in "0" > 0?OK:No + +OK +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/cond1.mk b/buildrump.sh/src/usr.bin/make/unit-tests/cond1.mk new file mode 100644 index 00000000..e3618327 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/cond1.mk @@ -0,0 +1,109 @@ +# $Id: cond1.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +# hard code these! +TEST_UNAME_S= NetBSD +TEST_UNAME_M= sparc +TEST_MACHINE= i386 + +.if ${TEST_UNAME_S} +Ok=var, +.endif +.if ("${TEST_UNAME_S}") +Ok+=(\"var\"), +.endif +.if (${TEST_UNAME_M} != ${TEST_MACHINE}) +Ok+=(var != var), +.endif +.if ${TEST_UNAME_M} != ${TEST_MACHINE} +Ok+= var != var, +.endif +.if !((${TEST_UNAME_M} != ${TEST_MACHINE}) && defined(X)) +Ok+= !((var != var) && defined(name)), +.endif +# from bsd.obj.mk +MKOBJ?=no +.if ${MKOBJ} == "no" +o= no +Ok+= var == "quoted", +.else +.if defined(notMAKEOBJDIRPREFIX) || defined(norMAKEOBJDIR) +.if defined(notMAKEOBJDIRPREFIX) +o=${MAKEOBJDIRPREFIX}${__curdir} +.else +o= ${MAKEOBJDIR} +.endif +.endif +o= o +.endif + +# repeat the above to check we get the same result +.if ${MKOBJ} == "no" +o2= no +.else +.if defined(notMAKEOBJDIRPREFIX) || defined(norMAKEOBJDIR) +.if defined(notMAKEOBJDIRPREFIX) +o2=${MAKEOBJDIRPREFIX}${__curdir} +.else +o2= ${MAKEOBJDIR} +.endif +.endif +o2= o +.endif + +PRIMES=2 3 5 7 11 +NUMBERS=1 2 3 4 5 + +n=2 +.if ${PRIMES:M$n} == "" +X=not +.else +X= +.endif + +.if ${MACHINE_ARCH} == no-such +A=one +.else +.if ${MACHINE_ARCH} == not-this +.if ${MACHINE_ARCH} == something-else +A=unlikely +.else +A=no +.endif +.endif +A=other +# We expect an extra else warning - we're not skipping here +.else +A=this should be an error +.endif + +.if $X != "" +.if $X == not +B=one +.else +B=other +# We expect an extra else warning - we are skipping here +.else +B=this should be an error +.endif +.else +B=unknown +.endif + +.if "quoted" == quoted +C=clever +.else +C=dim +.endif + +.if defined(nosuch) && ${nosuch:Mx} != "" +# this should not happen +.info nosuch is x +.endif + +all: + @echo "$n is $X prime" + @echo "A='$A' B='$B' C='$C' o='$o,${o2}'" + @echo "Passed:${.newline} ${Ok:S/,/${.newline}/}" + @echo "${NUMBERS:@n@$n is ${("${PRIMES:M$n}" == ""):?not:} prime${.newline}@}" + @echo "${"${DoNotQuoteHere:U0}" > 0:?OK:No}" + @echo "${${NoSuchNumber:U42} > 0:?OK:No}" diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/cond2.exp b/buildrump.sh/src/usr.bin/make/unit-tests/cond2.exp new file mode 100644 index 00000000..22e76a5b --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/cond2.exp @@ -0,0 +1,7 @@ +make: Bad conditional expression ` == "empty"' in == "empty"?oops:ok +make: "cond2.mk" line 13: Malformed conditional ({TEST_TYPO} == "Ok") +TEST_NOT_SET is empty or not defined +make: "cond2.mk" line 20: Malformed conditional (${TEST_NOT_SET} == "empty") +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/cond2.mk b/buildrump.sh/src/usr.bin/make/unit-tests/cond2.mk new file mode 100644 index 00000000..cccc9d6f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/cond2.mk @@ -0,0 +1,25 @@ +# $Id: cond2.mk,v 1.1 2015/05/05 21:51:09 sjg Exp $ + +TEST_UNAME_S= NetBSD + +# this should be ok +X:= ${${TEST_UNAME_S} == "NetBSD":?Ok:fail} +.if $X == "Ok" +Y= good +.endif +# expect: Bad conditional expression ` == "empty"' in == "empty"?oops:ok +X:= ${${TEST_NOT_SET} == "empty":?oops:ok} +# expect: Malformed conditional ({TEST_TYPO} == "Ok") +.if {TEST_TYPO} == "Ok" +Y= oops +.endif +.if empty(TEST_NOT_SET) +Y!= echo TEST_NOT_SET is empty or not defined >&2; echo +.endif +# expect: Malformed conditional (${TEST_NOT_SET} == "empty") +.if ${TEST_NOT_SET} == "empty" +Y= oops +.endif + +all: + @echo $@ diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/doterror.exp b/buildrump.sh/src/usr.bin/make/unit-tests/doterror.exp new file mode 100644 index 00000000..0447a519 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/doterror.exp @@ -0,0 +1,9 @@ +At first, I am +happy +and now: sad +.ERROR: Looks like 'sad' is upset. +*** Error code 1 + +Stop. +make: stopped in unit-tests +exit status 1 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/doterror.mk b/buildrump.sh/src/usr.bin/make/unit-tests/doterror.mk new file mode 100644 index 00000000..7f1c78ff --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/doterror.mk @@ -0,0 +1,20 @@ +# $Id: doterror.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + + +.BEGIN: + @echo At first, I am + +.END: + @echo not reached + +.ERROR: + @echo "$@: Looks like '${.ERROR_TARGET}' is upset." + +all: happy sad + +happy: + @echo $@ + +sad: + @echo and now: $@; exit 1 + diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/dotwait.exp b/buildrump.sh/src/usr.bin/make/unit-tests/dotwait.exp new file mode 100644 index 00000000..6bf96e30 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/dotwait.exp @@ -0,0 +1,30 @@ +simple.1 +simple.1 +simple.2 +simple.2 +recursive.1.1.* +recursive.1.1.* +recursive.1.1.* +recursive.1.1.* +recursive.1.99 +recursive.1.99 +recursive.2.1.* +recursive.2.1.* +recursive.2.1.* +recursive.2.1.* +recursive.2.99 +recursive.2.99 +shared.0 +shared.0 +shared.1.99 +shared.1.99 +shared.2.1 +shared.2.1 +shared.2.99 +shared.2.99 +make: Graph cycles through `cycle.2.99' +make: Graph cycles through `cycle.2.98' +make: Graph cycles through `cycle.2.97' +cycle.1.99 +cycle.1.99 +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/dotwait.mk b/buildrump.sh/src/usr.bin/make/unit-tests/dotwait.mk new file mode 100644 index 00000000..9bdaaba3 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/dotwait.mk @@ -0,0 +1,61 @@ +# $NetBSD: dotwait.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +THISMAKEFILE:= ${.PARSEDIR}/${.PARSEFILE} + +TESTS= simple recursive shared cycle +PAUSE= sleep 1 + +# Use a .for loop rather than dependencies here, to ensure +# that the tests are run one by one, with parallelism +# only within tests. +# Ignore "--- target ---" lines printed by parallel make. +all: +.for t in ${TESTS} + @${.MAKE} -f ${THISMAKEFILE} -j4 $t | grep -v "^--- " +.endfor + +# +# Within each test, the names of the sub-targets follow these +# conventions: +# * If it's expected that two or more targets may be made in parallel, +# then the target names will differ only in an alphabetic component +# such as ".a" or ".b". +# * If it's expected that two or more targets should be made in sequence +# then the target names will differ in numeric components, such that +# lexical ordering of the target names matches the expected order +# in which the targets should be made. +# +# Targets may echo ${PARALLEL_TARG} to print a modified version +# of their own name, in which alphabetic components like ".a" or ".b" +# are converted to ".*". Two targets that are expected to +# be made in parallel will thus print the same strings, so that the +# output is independent of the order in which these targets are made. +# +PARALLEL_TARG= ${.TARGET:C/\.[a-z]/.*/g:Q} +.DEFAULT: + @echo ${PARALLEL_TARG}; ${PAUSE}; echo ${PARALLEL_TARG} +_ECHOUSE: .USE + @echo ${PARALLEL_TARG}; ${PAUSE}; echo ${PARALLEL_TARG} + +# simple: no recursion, no cycles +simple: simple.1 .WAIT simple.2 + +# recursive: all children of the left hand side of the .WAIT +# must be made before any child of the right hand side. +recursive: recursive.1.99 .WAIT recursive.2.99 +recursive.1.99: recursive.1.1.a recursive.1.1.b _ECHOUSE +recursive.2.99: recursive.2.1.a recursive.2.1.b _ECHOUSE + +# shared: both shared.1.99 and shared.2.99 depend on shared.0. +# shared.0 must be made first, even though it is a child of +# the right hand side of the .WAIT. +shared: shared.1.99 .WAIT shared.2.99 +shared.1.99: shared.0 _ECHOUSE +shared.2.99: shared.2.1 shared.0 _ECHOUSE + +# cycle: the cyclic dependency must not cause infinite recursion +# leading to stack overflow and a crash. +cycle: cycle.1.99 .WAIT cycle.2.99 +cycle.2.99: cycle.2.98 _ECHOUSE +cycle.2.98: cycle.2.97 _ECHOUSE +cycle.2.97: cycle.2.99 _ECHOUSE diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/error.exp b/buildrump.sh/src/usr.bin/make/unit-tests/error.exp new file mode 100644 index 00000000..a2bf71b7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/error.exp @@ -0,0 +1,4 @@ +make: "error.mk" line 3: just FYI +make: "error.mk" line 4: warning: this could be serious +make: "error.mk" line 5: this is fatal +exit status 1 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/error.mk b/buildrump.sh/src/usr.bin/make/unit-tests/error.mk new file mode 100644 index 00000000..721ed505 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/error.mk @@ -0,0 +1,10 @@ +# $Id: error.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +.info just FYI +.warning this could be serious +.error this is fatal + +all: + +.info.html: + @echo this should be ignored diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/escape.exp b/buildrump.sh/src/usr.bin/make/unit-tests/escape.exp new file mode 100644 index 00000000..6238e27b --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/escape.exp @@ -0,0 +1,104 @@ +var-1bs +printf "%s=:%s:\n" VAR1BS 111\\111; printf "%s=:%s:\n" VAR1BSa 111\\aaa; printf "%s=:%s:\n" VAR1BSA 111\\aaa; printf "%s=:%s:\n" VAR1BSda 111\\\$\{a\}; printf "%s=:%s:\n" VAR1BSdA 111\\\$\{A\}; printf "%s=:%s:\n" VAR1BSc 111\#\ backslash\ escapes\ comment\ char,\ so\ this\ is\ part\ of\ the\ value; printf "%s=:%s:\n" VAR1BSsc 111\\\ ; +VAR1BS=:111\111: +VAR1BSa=:111\aaa: +VAR1BSA=:111\aaa: +VAR1BSda=:111\${a}: +VAR1BSdA=:111\${A}: +VAR1BSc=:111# backslash escapes comment char, so this is part of the value: +VAR1BSsc=:111\ : +var-2bs +printf "%s=:%s:\n" VAR2BS 222\\\\222; printf "%s=:%s:\n" VAR2BSa 222\\\\aaa; printf "%s=:%s:\n" VAR2BSA 222\\\\aaa; printf "%s=:%s:\n" VAR2BSda 222\\\\\$\{a\}; printf "%s=:%s:\n" VAR2BSdA 222\\\\\$\{A\}; printf "%s=:%s:\n" VAR2BSc 222\\\\; printf "%s=:%s:\n" VAR2BSsc 222\\\\; +VAR2BS=:222\\222: +VAR2BSa=:222\\aaa: +VAR2BSA=:222\\aaa: +VAR2BSda=:222\\${a}: +VAR2BSdA=:222\\${A}: +VAR2BSc=:222\\: +VAR2BSsc=:222\\: +var-1bsnl +printf "%s=:%s:\n" VAR1BSNL 111\ 111; printf "%s=:%s:\n" VAR1BSNLa 111\ aaa; printf "%s=:%s:\n" VAR1BSNLA 111\ aaa; printf "%s=:%s:\n" VAR1BSNLda 111\ \$\{a\}; printf "%s=:%s:\n" VAR1BSNLdA 111\ \$\{A\}; printf "%s=:%s:\n" VAR1BSNLc 111; printf "%s=:%s:\n" VAR1BSNLsc 111; +VAR1BSNL=:111 111: +VAR1BSNLa=:111 aaa: +VAR1BSNLA=:111 aaa: +VAR1BSNLda=:111 ${a}: +VAR1BSNLdA=:111 ${A}: +VAR1BSNLc=:111: +VAR1BSNLsc=:111: +var-2bsnl +printf "%s=:%s:\n" VAR2BSNL 222\\\\; printf "%s=:%s:\n" VAR2BSNLa 222\\\\; printf "%s=:%s:\n" VAR2BSNLA 222\\\\; printf "%s=:%s:\n" VAR2BSNLda 222\\\\; printf "%s=:%s:\n" VAR2BSNLdA 222\\\\; printf "%s=:%s:\n" VAR2BSNLc 222\\\\; printf "%s=:%s:\n" VAR2BSNLsc 222\\\\; +VAR2BSNL=:222\\: +VAR2BSNLa=:222\\: +VAR2BSNLA=:222\\: +VAR2BSNLda=:222\\: +VAR2BSNLdA=:222\\: +VAR2BSNLc=:222\\: +VAR2BSNLsc=:222\\: +var-3bsnl +printf "%s=:%s:\n" VAR3BSNL 333\\\\\ 333=; printf "%s=:%s:\n" VAR3BSNLa 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLA 333\\\\\ aaa=; printf "%s=:%s:\n" VAR3BSNLda 333\\\\\ \$\{a\}=; printf "%s=:%s:\n" VAR3BSNLdA 333\\\\\ \$\{A\}=; printf "%s=:%s:\n" VAR3BSNLc 333\\\\; printf "%s=:%s:\n" VAR3BSNLsc 333\\\\; +VAR3BSNL=:333\\ 333=: +VAR3BSNLa=:333\\ aaa=: +VAR3BSNLA=:333\\ aaa=: +VAR3BSNLda=:333\\ ${a}=: +VAR3BSNLdA=:333\\ ${A}=: +VAR3BSNLc=:333\\: +VAR3BSNLsc=:333\\: +var-1bsnl-space +printf "%s=:%s:\n" VAR1BSNL00 first\ line; printf "%s=:%s:\n" VAR1BSNL0 first\ line\ no\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLs first\ line\ one\ space\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLss first\ line\ two\ spaces\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLt first\ line\ one\ tab\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLtt first\ line\ two\ tabs\ on\ second\ line; printf "%s=:%s:\n" VAR1BSNLxx first\ line\ many\ spaces\ and\ tabs\ \[\ \ \ \ \]\ on\ second\ line; +VAR1BSNL00=:first line: +VAR1BSNL0=:first line no space on second line: +VAR1BSNLs=:first line one space on second line: +VAR1BSNLss=:first line two spaces on second line: +VAR1BSNLt=:first line one tab on second line: +VAR1BSNLtt=:first line two tabs on second line: +VAR1BSNLxx=:first line many spaces and tabs [ ] on second line: +cmd-1bsnl +echo :'first line\ +#second line without space\ +third line': +:first line\ +#second line without space\ +third line: +echo :'first line\ + second line spaces should be retained': +:first line\ + second line spaces should be retained: +echo :'first line\ +second line tab should be elided': +:first line\ +second line tab should be elided: +echo :'first line\ + only one tab should be elided, second tab remains' +:first line\ + only one tab should be elided, second tab remains +cmd-1bsnl-eof +echo :'command ending with backslash-newline'; \ + +:command ending with backslash-newline +cmd-2bsnl +echo take one\\ +take one\ +echo take two\\ +take two\ +echo take three\\ +take three\ +cmd-3bsnl +echo :'first line\\\ +#second line without space\\\ +third line': +:first line\\\ +#second line without space\\\ +third line: +echo :'first line\\\ + second line spaces should be retained': +:first line\\\ + second line spaces should be retained: +echo :'first line\\\ +second line tab should be elided': +:first line\\\ +second line tab should be elided: +echo :'first line\\\ + only one tab should be elided, second tab remains' +:first line\\\ + only one tab should be elided, second tab remains +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/escape.mk b/buildrump.sh/src/usr.bin/make/unit-tests/escape.mk new file mode 100644 index 00000000..829403d7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/escape.mk @@ -0,0 +1,246 @@ +# $Id: escape.mk,v 1.10 2014/09/09 10:22:27 apb Exp $ +# +# Test backslash escaping. + +# Extracts from the POSIX 2008 specification +# <http://pubs.opengroup.org/onlinepubs/9699919799/utilities/make.html>: +# +# Comments start with a <number-sign> ( '#' ) and continue until an +# unescaped <newline> is reached. +# +# When an escaped <newline> (one preceded by a <backslash>) is found +# anywhere in the makefile except in a command line, an include +# line, or a line immediately preceding an include line, it shall +# be replaced, along with any leading white space on the following +# line, with a single <space>. +# +# When an escaped <newline> is found in a command line in a +# makefile, the command line shall contain the <backslash>, the +# <newline>, and the next line, except that the first character of +# the next line shall not be included if it is a <tab>. +# +# When an escaped <newline> is found in an include line or in a +# line immediately preceding an include line, the behavior is +# unspecified. +# +# Notice that the behaviour of <backslash><backslash> or +# <backslash><anything other than newline> is not mentioned. I think +# this implies that <backslash> should be taken literally everywhere +# except before <newline>. +# +# Our practice, despite what POSIX might say, is that "\#" +# in a variable assignment stores "#" as part of the value. +# The "\" is not taken literally, and the "#" does not begin a comment. +# +# Also, our practice is that an even number of backslashes before a +# newline in a variable assignment simply stores the backslashes as part +# of the value, and treats the newline as though it was not escaped. +# Similarly, ann even number of backslashes before a newline in a +# command simply uses the backslashes as part of the command test, but +# does not escape the newline. This is compatible with GNU make. + +all: .PHONY +# We will add dependencies like "all: yet-another-test" later. + +# Some variables to be expanded in tests +# +a = aaa +A = ${a} + +# Backslash at end of line in a comment\ +should continue the comment. \ +# This is also tested in comment.mk. + +__printvars: .USE .MADE + @echo ${.TARGET} + ${.ALLSRC:@v@ printf "%s=:%s:\n" ${v:Q} ${${v}:Q}; @} + +# Embedded backslash in variable should be taken literally. +# +VAR1BS = 111\111 +VAR1BSa = 111\${a} +VAR1BSA = 111\${A} +VAR1BSda = 111\$${a} +VAR1BSdA = 111\$${A} +VAR1BSc = 111\# backslash escapes comment char, so this is part of the value +VAR1BSsc = 111\ # This is a comment. Value ends with <backslash><space> + +all: var-1bs +var-1bs: .PHONY __printvars VAR1BS VAR1BSa VAR1BSA VAR1BSda VAR1BSdA \ + VAR1BSc VAR1BSsc + +# Double backslash in variable should be taken as two literal backslashes. +# +VAR2BS = 222\\222 +VAR2BSa = 222\\${a} +VAR2BSA = 222\\${A} +VAR2BSda = 222\\$${a} +VAR2BSdA = 222\\$${A} +VAR2BSc = 222\\# backslash does not escape comment char, so this is a comment +VAR2BSsc = 222\\ # This is a comment. Value ends with <backslash><backslash> + +all: var-2bs +var-2bs: .PHONY __printvars VAR2BS VAR2BSa VAR2BSA VAR2BSda VAR2BSdA \ + VAR2BSc VAR2BSsc + +# Backslash-newline in a variable setting is replaced by a single space. +# +VAR1BSNL = 111\ +111 +VAR1BSNLa = 111\ +${a} +VAR1BSNLA = 111\ +${A} +VAR1BSNLda = 111\ +$${a} +VAR1BSNLdA = 111\ +$${A} +VAR1BSNLc = 111\ +# this should be processed as a comment +VAR1BSNLsc = 111\ + # this should be processed as a comment + +all: var-1bsnl +var-1bsnl: .PHONY +var-1bsnl: .PHONY __printvars \ + VAR1BSNL VAR1BSNLa VAR1BSNLA VAR1BSNLda VAR1BSNLdA \ + VAR1BSNLc VAR1BSNLsc + +# Double-backslash-newline in a variable setting. +# Both backslashes should be taken literally, and the newline is NOT escaped. +# +# The second lines below each end with '=' so that they will not +# generate syntax errors regardless of whether or not they are +# treated as part of the value. +# +VAR2BSNL = 222\\ +222= +VAR2BSNLa = 222\\ +${a}= +VAR2BSNLA = 222\\ +${A}= +VAR2BSNLda = 222\\ +$${a}= +VAR2BSNLdA = 222\\ +$${A}= +VAR2BSNLc = 222\\ +# this should be processed as a comment +VAR2BSNLsc = 222\\ + # this should be processed as a comment + +all: var-2bsnl +var-2bsnl: .PHONY __printvars \ + VAR2BSNL VAR2BSNLa VAR2BSNLA VAR2BSNLda VAR2BSNLdA \ + VAR2BSNLc VAR2BSNLsc + +# Triple-backslash-newline in a variable setting. +# First two should be taken literally, and last should escape the newline. +# +# The second lines below each end with '=' so that they will not +# generate syntax errors regardless of whether or not they are +# treated as part of the value. +# +VAR3BSNL = 333\\\ +333= +VAR3BSNLa = 333\\\ +${a}= +VAR3BSNLA = 333\\\ +${A}= +VAR3BSNLda = 333\\\ +$${a}= +VAR3BSNLdA = 333\\\ +$${A}= +VAR3BSNLc = 333\\\ +# this should be processed as a comment +VAR3BSNLsc = 333\\\ + # this should be processed as a comment + +all: var-3bsnl +var-3bsnl: .PHONY __printvars \ + VAR3BSNL VAR3BSNLa VAR3BSNLA VAR3BSNLda VAR3BSNLdA \ + VAR3BSNLc VAR3BSNLsc + +# Backslash-newline in a variable setting, plus any amount of white space +# on the next line, is replaced by a single space. +# +VAR1BSNL00= first line\ + +# above line is entirely empty, and this is a comment +VAR1BSNL0= first line\ +no space on second line +VAR1BSNLs= first line\ + one space on second line +VAR1BSNLss= first line\ + two spaces on second line +VAR1BSNLt= first line\ + one tab on second line +VAR1BSNLtt= first line\ + two tabs on second line +VAR1BSNLxx= first line\ + many spaces and tabs [ ] on second line + +all: var-1bsnl-space +var-1bsnl-space: .PHONY __printvars \ + VAR1BSNL00 VAR1BSNL0 VAR1BSNLs VAR1BSNLss VAR1BSNLt VAR1BSNLtt \ + VAR1BSNLxx + +# Backslash-newline in a command is retained. +# +# The "#" in "# second line without space" makes it a comment instead +# of a syntax error if the preceding line is parsed incorretly. +# The ":" in "third line':" makes it look like the start of a +# target instead of a syntax error if the first line is parsed incorrectly. +# +all: cmd-1bsnl +cmd-1bsnl: .PHONY + @echo ${.TARGET} + echo :'first line\ +#second line without space\ +third line': + echo :'first line\ + second line spaces should be retained': + echo :'first line\ + second line tab should be elided': + echo :'first line\ + only one tab should be elided, second tab remains' + +# When backslash-newline appears at the end of a command script, +# both the backslash and the newline should be passed to the shell. +# The shell should elide the backslash-newline. +# +all: cmd-1bsnl-eof +cmd-1bsnl-eof: + @echo ${.TARGET} + echo :'command ending with backslash-newline'; \ + +# above line must be blank + +# Double-backslash-newline in a command. +# Both backslashes are retained, but the newline is not escaped. +# XXX: This may differ from POSIX, but matches gmake. +# +# When make passes two backslashes to the shell, the shell will pass one +# backslash to the echo commant. +# +all: cmd-2bsnl +cmd-2bsnl: .PHONY + @echo ${.TARGET} + echo take one\\ +# this should be a comment + echo take two\\ + echo take three\\ + +# Triple-backslash-newline in a command is retained. +# +all: cmd-3bsnl +cmd-3bsnl: .PHONY + @echo ${.TARGET} + echo :'first line\\\ +#second line without space\\\ +third line': + echo :'first line\\\ + second line spaces should be retained': + echo :'first line\\\ + second line tab should be elided': + echo :'first line\\\ + only one tab should be elided, second tab remains' diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/export-all.exp b/buildrump.sh/src/usr.bin/make/unit-tests/export-all.exp new file mode 100644 index 00000000..e3aefd4f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/export-all.exp @@ -0,0 +1,12 @@ +UT_ALL=even this gets exported +UT_BADDIR=unit-tests +UT_DOLLAR=This is $UT_FU +UT_F=fine +UT_FOO=foobar is fubar +UT_FU=fubar +UT_NO=all +UT_OK=good +UT_OKDIR=unit-tests +UT_TEST=export-all +UT_ZOO=hoopie +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/export-all.mk b/buildrump.sh/src/usr.bin/make/unit-tests/export-all.mk new file mode 100644 index 00000000..576487b5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/export-all.mk @@ -0,0 +1,23 @@ +# $Id: export-all.mk,v 1.2 2015/04/10 20:41:59 sjg Exp $ + +UT_OK=good +UT_F=fine + +# the old way to do :tA +M_tAbad = C,.*,cd & \&\& 'pwd',:sh +# the new +M_tA = tA + +here := ${.PARSEDIR} + +# this will cause trouble (recursing if we let it) +UT_BADDIR = ${${here}/../${here:T}:L:${M_tAbad}:T} +# this will be ok +UT_OKDIR = ${${here}/../${here:T}:L:${M_tA}:T} + +.export + +.include "export.mk" + +UT_TEST=export-all +UT_ALL=even this gets exported diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/export-env.exp b/buildrump.sh/src/usr.bin/make/unit-tests/export-env.exp new file mode 100644 index 00000000..6221232a --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/export-env.exp @@ -0,0 +1,9 @@ +make: +UT_TEST=export-env.mk +UT_ENV=not-exported +UT_EXP=not-exported +env: +UT_TEST=export-env.mk +UT_ENV=exported +UT_EXP=exported +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/export-env.mk b/buildrump.sh/src/usr.bin/make/unit-tests/export-env.mk new file mode 100644 index 00000000..6e9993b7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/export-env.mk @@ -0,0 +1,24 @@ +# $Id: export-env.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +# our normal .export, subsequent changes affect the environment +UT_TEST=this +.export UT_TEST +UT_TEST:= ${.PARSEFILE} + +# not so with .export-env +UT_ENV=exported +.export-env UT_ENV +UT_ENV=not-exported + +# gmake style export goes further; affects nothing but the environment +UT_EXP=before-export +export UT_EXP=exported +UT_EXP=not-exported + +all: + @echo make:; ${UT_TEST UT_ENV UT_EXP:L:@v@echo $v=${$v};@} + @echo env:; ${UT_TEST UT_ENV UT_EXP:L:@v@echo $v=$${$v};@} + + + + diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/export.exp b/buildrump.sh/src/usr.bin/make/unit-tests/export.exp new file mode 100644 index 00000000..143771ce --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/export.exp @@ -0,0 +1,6 @@ +UT_DOLLAR=This is $UT_FU +UT_FOO=foobar is fubar +UT_FU=fubar +UT_TEST=export +UT_ZOO=hoopie +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/export.mk b/buildrump.sh/src/usr.bin/make/unit-tests/export.mk new file mode 100644 index 00000000..1b4ee722 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/export.mk @@ -0,0 +1,22 @@ +# $Id: export.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +UT_TEST=export +UT_FOO=foo${BAR} +UT_FU=fubar +UT_ZOO=hoopie +UT_NO=all +# belive it or not, we expect this one to come out with $UT_FU unexpanded. +UT_DOLLAR= This is $$UT_FU + +.export UT_FU UT_FOO +.export UT_DOLLAR +# this one will be ignored +.export .MAKE.PID + +BAR=bar is ${UT_FU} + +.MAKE.EXPORTED+= UT_ZOO UT_TEST + +all: + @env | grep '^UT_' | sort + diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/forloop.exp b/buildrump.sh/src/usr.bin/make/unit-tests/forloop.exp new file mode 100644 index 00000000..df14b751 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/forloop.exp @@ -0,0 +1,19 @@ +x=one +x="two and three" +x=four +x="five" +x=-I/this +x=-I"This or that" +x=-Ithat +x="-DTHIS=\"this and that\"" +cfl=-I/this -I"This or that" -Ithat "-DTHIS=\"this and that\"" +a=one b="two and three" +a=four b="five" +a=ONE b="TWO AND THREE" +a=FOUR b="FIVE" +We expect an error next: +make: "forloop.mk" line 38: Wrong number of words (9) in .for substitution list with 2 vars +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +OK +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/forloop.mk b/buildrump.sh/src/usr.bin/make/unit-tests/forloop.mk new file mode 100644 index 00000000..e0399f35 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/forloop.mk @@ -0,0 +1,45 @@ +# $Id: forloop.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +all: for-loop + +LIST = one "two and three" four "five" + +.if make(for-fail) +for-fail: + +XTRA_LIST = xtra +.else + +.for x in ${LIST} +X!= echo 'x=$x' >&2; echo +.endfor + +CFL = -I/this -I"This or that" -Ithat "-DTHIS=\"this and that\"" +cfl= +.for x in ${CFL} +X!= echo 'x=$x' >&2; echo +.if empty(cfl) +cfl= $x +.else +cfl+= $x +.endif +.endfor +X!= echo 'cfl=${cfl}' >&2; echo + +.if ${cfl} != ${CFL} +.error ${.newline}'${cfl}' != ${.newline}'${CFL}' +.endif + +.for a b in ${EMPTY} +X!= echo 'a=$a b=$b' >&2; echo +.endfor +.endif + +.for a b in ${LIST} ${LIST:tu} ${XTRA_LIST} +X!= echo 'a=$a b=$b' >&2; echo +.endfor + +for-loop: + @echo We expect an error next: + @(cd ${.CURDIR} && ${.MAKE} -f ${MAKEFILE} for-fail) && \ + { echo "Oops that should have failed!"; exit 1; } || echo OK diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/forsubst.exp b/buildrump.sh/src/usr.bin/make/unit-tests/forsubst.exp new file mode 100644 index 00000000..0a98c00a --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/forsubst.exp @@ -0,0 +1,2 @@ +.for with :S;... OK +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/forsubst.mk b/buildrump.sh/src/usr.bin/make/unit-tests/forsubst.mk new file mode 100644 index 00000000..00cd9b63 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/forsubst.mk @@ -0,0 +1,10 @@ +# $Id: forsubst.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +all: for-subst + +here := ${.PARSEDIR} +# this should not run foul of the parser +.for file in ${.PARSEFILE} +for-subst: ${file:S;^;${here}/;g} + @echo ".for with :S;... OK" +.endfor diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/hash.exp b/buildrump.sh/src/usr.bin/make/unit-tests/hash.exp new file mode 100644 index 00000000..0a242343 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/hash.exp @@ -0,0 +1,9 @@ +b2af338b +3360ac65 +7747f046 +9ca87054 +880fe816 +208fcbd3 +d5d376eb +de41416c +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/hash.mk b/buildrump.sh/src/usr.bin/make/unit-tests/hash.mk new file mode 100644 index 00000000..1ed84e77 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/hash.mk @@ -0,0 +1,18 @@ +STR1= +STR2= a +STR3= ab +STR4= abc +STR5= abcd +STR6= abcde +STR7= abcdef +STR8= abcdefghijklmnopqrstuvwxyz + +all: + @echo ${STR1:hash} + @echo ${STR2:hash} + @echo ${STR3:hash} + @echo ${STR4:hash} + @echo ${STR5:hash} + @echo ${STR6:hash} + @echo ${STR7:hash} + @echo ${STR8:hash} diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/impsrc.exp b/buildrump.sh/src/usr.bin/make/unit-tests/impsrc.exp new file mode 100644 index 00000000..23e8347d --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/impsrc.exp @@ -0,0 +1,13 @@ +expected: source4 +actual: source4 +expected: target1.x +actual: target1.x +expected: target1.y +actual: target1.y +expected: source1 +actual: source1 +expected: source2 +actual: source2 +expected: source1 +actual: source1 +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/impsrc.mk b/buildrump.sh/src/usr.bin/make/unit-tests/impsrc.mk new file mode 100644 index 00000000..95ae0c34 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/impsrc.mk @@ -0,0 +1,43 @@ +# $NetBSD: impsrc.mk,v 1.2 2014/08/30 22:21:07 sjg Exp $ + +# Does ${.IMPSRC} work properly? +# It should be set, in order of precedence, to ${.TARGET} of: +# 1) the implied source of a transformation rule, +# 2) the first prerequisite from the dependency line of an explicit rule, or +# 3) the first prerequisite of an explicit rule. +# + +all: target1.z target2 target3 target4 + +.SUFFIXES: .x .y .z + +.x.y: source1 + @echo 'expected: target1.x' + @echo 'actual: $<' + +.y.z: source2 + @echo 'expected: target1.y' + @echo 'actual: $<' + +target1.y: source3 + +target1.x: source4 + @echo 'expected: source4' + @echo 'actual: $<' + +target2: source1 source2 + @echo 'expected: source1' + @echo 'actual: $<' + +target3: source1 +target3: source2 source3 + @echo 'expected: source2' + @echo 'actual: $<' + +target4: source1 +target4: + @echo 'expected: source1' + @echo 'actual: $<' + +source1 source2 source3 source4: + diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/misc.exp b/buildrump.sh/src/usr.bin/make/unit-tests/misc.exp new file mode 100644 index 00000000..39a93839 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/misc.exp @@ -0,0 +1 @@ +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/misc.mk b/buildrump.sh/src/usr.bin/make/unit-tests/misc.mk new file mode 100644 index 00000000..2773e304 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/misc.mk @@ -0,0 +1,16 @@ +# $Id: misc.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +.if !exists(${.CURDIR}/) +.warning ${.CURDIR}/ doesn't exist ? +.endif + +.if !exists(${.CURDIR}/.) +.warning ${.CURDIR}/. doesn't exist ? +.endif + +.if !exists(${.CURDIR}/..) +.warning ${.CURDIR}/.. doesn't exist ? +.endif + +all: + @: all is well diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/moderrs.exp b/buildrump.sh/src/usr.bin/make/unit-tests/moderrs.exp new file mode 100644 index 00000000..cb51aa09 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/moderrs.exp @@ -0,0 +1,16 @@ +Expect: Unknown modifier 'Z' +make: Unknown modifier 'Z' +VAR:Z= +Expect: Unknown modifier 'Z' +make: Unknown modifier 'Z' +VAR:Z= +Expect: Unclosed variable specification for VAR +make: Unclosed variable specification (expecting '}') for "VAR" (value "Thevariable") modifier S +VAR:S,V,v,=Thevariable +Expect: Unclosed variable specification for VAR +make: Unclosed variable specification after complex modifier (expecting '}') for VAR +VAR:S,V,v,=Thevariable +Expect: Unclosed substitution for VAR (, missing) +make: Unclosed substitution for VAR (, missing) +VAR:S,V,v= +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/moderrs.mk b/buildrump.sh/src/usr.bin/make/unit-tests/moderrs.mk new file mode 100644 index 00000000..825e6c71 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/moderrs.mk @@ -0,0 +1,31 @@ +# $Id: moderrs.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ +# +# various modifier error tests + +VAR=TheVariable +# incase we have to change it ;-) +MOD_UNKN=Z +MOD_TERM=S,V,v +MOD_S:= ${MOD_TERM}, + +all: modunkn modunknV varterm vartermV modtermV + +modunkn: + @echo "Expect: Unknown modifier 'Z'" + @echo "VAR:Z=${VAR:Z}" + +modunknV: + @echo "Expect: Unknown modifier 'Z'" + @echo "VAR:${MOD_UNKN}=${VAR:${MOD_UNKN}}" + +varterm: + @echo "Expect: Unclosed variable specification for VAR" + @echo VAR:S,V,v,=${VAR:S,V,v, + +vartermV: + @echo "Expect: Unclosed variable specification for VAR" + @echo VAR:${MOD_TERM},=${VAR:${MOD_S} + +modtermV: + @echo "Expect: Unclosed substitution for VAR (, missing)" + -@echo "VAR:${MOD_TERM}=${VAR:${MOD_TERM}}" diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modmatch.exp b/buildrump.sh/src/usr.bin/make/unit-tests/modmatch.exp new file mode 100644 index 00000000..fcaf6c02 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modmatch.exp @@ -0,0 +1,17 @@ +LIB=a X_LIBS:M${LIB${LIB:tu}} is "/tmp/liba.a" +LIB=a X_LIBS:M*/lib${LIB}.a is "/tmp/liba.a" +LIB=a X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBA.A" +LIB=b X_LIBS:M${LIB${LIB:tu}} is "" +LIB=b X_LIBS:M*/lib${LIB}.a is "" +LIB=b X_LIBS:M*/lib${LIB}.a:tu is "" +LIB=c X_LIBS:M${LIB${LIB:tu}} is "" +LIB=c X_LIBS:M*/lib${LIB}.a is "" +LIB=c X_LIBS:M*/lib${LIB}.a:tu is "" +LIB=d X_LIBS:M${LIB${LIB:tu}} is "/tmp/libd.a" +LIB=d X_LIBS:M*/lib${LIB}.a is "/tmp/libd.a" +LIB=d X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBD.A" +LIB=e X_LIBS:M${LIB${LIB:tu}} is "/tmp/libe.a" +LIB=e X_LIBS:M*/lib${LIB}.a is "/tmp/libe.a" +LIB=e X_LIBS:M*/lib${LIB}.a:tu is "/TMP/LIBE.A" +Mscanner=OK +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modmatch.mk b/buildrump.sh/src/usr.bin/make/unit-tests/modmatch.mk new file mode 100644 index 00000000..48a1befb --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modmatch.mk @@ -0,0 +1,25 @@ + +X=a b c d e + +.for x in $X +LIB${x:tu}=/tmp/lib$x.a +.endfor + +X_LIBS= ${LIBA} ${LIBD} ${LIBE} + +LIB?=a + +var = head +res = no +.if !empty(var:M${:Uhead\:tail:C/:.*//}) +res = OK +.endif + +all: + @for x in $X; do ${.MAKE} -f ${MAKEFILE} show LIB=$$x; done + @echo "Mscanner=${res}" + +show: + @echo 'LIB=${LIB} X_LIBS:M$${LIB$${LIB:tu}} is "${X_LIBS:M${LIB${LIB:tu}}}"' + @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a is "${X_LIBS:M*/lib${LIB}.a}"' + @echo 'LIB=${LIB} X_LIBS:M*/lib$${LIB}.a:tu is "${X_LIBS:M*/lib${LIB}.a:tu}"' diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modmisc.exp b/buildrump.sh/src/usr.bin/make/unit-tests/modmisc.exp new file mode 100644 index 00000000..e406647b --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modmisc.exp @@ -0,0 +1,10 @@ +path=':/bin:/tmp::/:.:/no/such/dir:.' +path='/bin:/tmp:/:/no/such/dir' +path='/bin:/tmp:/:/no/such/dir' +path='/bin':'/tmp':'/':'/no/such/dir' +path='/bin':'/tmp':'/':'/no/such/dir' +path_/usr/xbin=/opt/xbin/ +paths=/bin /tmp / /no/such/dir /opt/xbin +PATHS=/BIN /TMP / /NO/SUCH/DIR /OPT/XBIN +The answer is 42 +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modmisc.mk b/buildrump.sh/src/usr.bin/make/unit-tests/modmisc.mk new file mode 100644 index 00000000..a292b966 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modmisc.mk @@ -0,0 +1,38 @@ +# $Id: modmisc.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ +# +# miscellaneous modifier tests + +# do not put any dirs in this list which exist on some +# but not all target systems - an exists() check is below. +path=:/bin:/tmp::/:.:/no/such/dir:. +# strip cwd from path. +MOD_NODOT=S/:/ /g:N.:ts: +# and decorate, note that $'s need to be doubled. Also note that +# the modifier_variable can be used with other modifiers. +MOD_NODOTX=S/:/ /g:N.:@d@'$$d'@ +# another mod - pretend it is more interesting +MOD_HOMES=S,/home/,/homes/, +MOD_OPT=@d@$${exists($$d):?$$d:$${d:S,/usr,/opt,}}@ +MOD_SEP=S,:, ,g + +all: modvar modvarloop modsysv + +modsysv: + @echo "The answer is ${libfoo.a:L:libfoo.a=42}" + +modvar: + @echo "path='${path}'" + @echo "path='${path:${MOD_NODOT}}'" + @echo "path='${path:S,home,homes,:${MOD_NODOT}}'" + @echo "path=${path:${MOD_NODOTX}:ts:}" + @echo "path=${path:${MOD_HOMES}:${MOD_NODOTX}:ts:}" + +.for d in ${path:${MOD_SEP}:N.} /usr/xbin +path_$d?= ${d:${MOD_OPT}:${MOD_HOMES}}/ +paths+= ${d:${MOD_OPT}:${MOD_HOMES}} +.endfor + +modvarloop: + @echo "path_/usr/xbin=${path_/usr/xbin}" + @echo "paths=${paths}" + @echo "PATHS=${paths:tu}" diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modorder.exp b/buildrump.sh/src/usr.bin/make/unit-tests/modorder.exp new file mode 100644 index 00000000..41174273 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modorder.exp @@ -0,0 +1,11 @@ +LIST = one two three four five six seven eight nine ten +LIST:O = eight five four nine one seven six ten three two +LIST:Ox = Ok +LIST:O:Ox = Ok +LISTX = Ok +LISTSX = Ok +make: Bad modifier `:OX' for LIST +BADMOD 1 = } +make: Bad modifier `:OxXX' for LIST +BADMOD 2 = XX} +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modorder.mk b/buildrump.sh/src/usr.bin/make/unit-tests/modorder.mk new file mode 100644 index 00000000..bc24d339 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modorder.mk @@ -0,0 +1,22 @@ +# $NetBSD: modorder.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +LIST= one two three four five six seven eight nine ten +LISTX= ${LIST:Ox} +LISTSX:= ${LIST:Ox} +TEST_RESULT= && echo Ok || echo Failed + +# unit-tests have to produce the same results on each run +# so we cannot actually include :Ox output. +all: + @echo "LIST = ${LIST}" + @echo "LIST:O = ${LIST:O}" + # Note that 1 in every 10! trials two independently generated + # randomized orderings will be the same. The test framework doesn't + # support checking probabilistic output, so we accept that the test + # will incorrectly fail with probability 2.8E-7. + @echo "LIST:Ox = `test '${LIST:Ox}' != '${LIST:Ox}' ${TEST_RESULT}`" + @echo "LIST:O:Ox = `test '${LIST:O:Ox}' != '${LIST:O:Ox}' ${TEST_RESULT}`" + @echo "LISTX = `test '${LISTX}' != '${LISTX}' ${TEST_RESULT}`" + @echo "LISTSX = `test '${LISTSX}' = '${LISTSX}' ${TEST_RESULT}`" + @echo "BADMOD 1 = ${LIST:OX}" + @echo "BADMOD 2 = ${LIST:OxXX}" diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modts.exp b/buildrump.sh/src/usr.bin/make/unit-tests/modts.exp new file mode 100644 index 00000000..cf3c91d4 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modts.exp @@ -0,0 +1,33 @@ +LIST="one two three four five six" +LIST:ts,="one,two,three,four,five,six" +LIST:ts/:tu="ONE/TWO/THREE/FOUR/FIVE/SIX" +LIST:ts::tu="ONE:TWO:THREE:FOUR:FIVE:SIX" +LIST:ts:tu="ONETWOTHREEFOURFIVESIX" +LIST:tu:ts/="ONE/TWO/THREE/FOUR/FIVE/SIX" +LIST:ts:="one:two:three:four:five:six" +LIST:ts="onetwothreefourfivesix" +LIST:ts:S/two/2/="one2threefourfivesix" +LIST:S/two/2/:ts="one2threefourfivesix" +LIST:ts/:S/two/2/="one/2/three/four/five/six" +Pretend the '/' in '/n' etc. below are back-slashes. +LIST:ts/n="one +two +three +four +five +six" +LIST:ts/t="one two three four five six" +LIST:ts/012:tu="ONE +TWO +THREE +FOUR +FIVE +SIX" +make: Bad modifier `:tx' for LIST +LIST:tx="}" +make: Bad modifier `:ts\x' for LIST +LIST:ts/x:tu="\x:tu}" +FU_mod-ts="a/b/cool" +FU_mod-ts:ts:T="cool" == cool? +B.${AAA:ts}="Baaa" == Baaa? +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modts.mk b/buildrump.sh/src/usr.bin/make/unit-tests/modts.mk new file mode 100644 index 00000000..616bd894 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modts.mk @@ -0,0 +1,43 @@ + +LIST= one two three +LIST+= four five six + +FU_mod-ts = a / b / cool + +AAA= a a a +B.aaa= Baaa + +all: mod-ts + +# Use print or printf iff they are builtin. +# XXX note that this causes problems, when make decides +# there is no need to use a shell, so avoid where possible. +.if ${type print 2> /dev/null || echo:L:sh:Mbuiltin} != "" +PRINT= print -r -- +.elif ${type printf 2> /dev/null || echo:L:sh:Mbuiltin} != "" +PRINT= printf '%s\n' +.else +PRINT= echo +.endif + +mod-ts: + @echo 'LIST="${LIST}"' + @echo 'LIST:ts,="${LIST:ts,}"' + @echo 'LIST:ts/:tu="${LIST:ts/:tu}"' + @echo 'LIST:ts::tu="${LIST:ts::tu}"' + @echo 'LIST:ts:tu="${LIST:ts:tu}"' + @echo 'LIST:tu:ts/="${LIST:tu:ts/}"' + @echo 'LIST:ts:="${LIST:ts:}"' + @echo 'LIST:ts="${LIST:ts}"' + @echo 'LIST:ts:S/two/2/="${LIST:ts:S/two/2/}"' + @echo 'LIST:S/two/2/:ts="${LIST:S/two/2/:ts}"' + @echo 'LIST:ts/:S/two/2/="${LIST:ts/:S/two/2/}"' + @echo "Pretend the '/' in '/n' etc. below are back-slashes." + @${PRINT} 'LIST:ts/n="${LIST:ts\n}"' + @${PRINT} 'LIST:ts/t="${LIST:ts\t}"' + @${PRINT} 'LIST:ts/012:tu="${LIST:ts\012:tu}"' + @${PRINT} 'LIST:tx="${LIST:tx}"' + @${PRINT} 'LIST:ts/x:tu="${LIST:ts\x:tu}"' + @${PRINT} 'FU_$@="${FU_${@:ts}:ts}"' + @${PRINT} 'FU_$@:ts:T="${FU_${@:ts}:ts:T}" == cool?' + @${PRINT} 'B.$${AAA:ts}="${B.${AAA:ts}}" == Baaa?' diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modword.exp b/buildrump.sh/src/usr.bin/make/unit-tests/modword.exp new file mode 100644 index 00000000..258d7ead --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modword.exp @@ -0,0 +1,122 @@ +make: Bad modifier `:[]' for LIST +LIST:[]="" is an error +LIST:[0]="one two three four five six" +LIST:[0x0]="one two three four five six" +LIST:[000]="one two three four five six" +LIST:[*]="one two three four five six" +LIST:[@]="one two three four five six" +LIST:[0]:C/ /,/="one,two three four five six" +LIST:[0]:C/ /,/g="one,two,three,four,five,six" +LIST:[0]:C/ /,/1g="one,two,three,four,five,six" +LIST:[*]:C/ /,/="one,two three four five six" +LIST:[*]:C/ /,/g="one,two,three,four,five,six" +LIST:[*]:C/ /,/1g="one,two,three,four,five,six" +LIST:[@]:C/ /,/="one two three four five six" +LIST:[@]:C/ /,/g="one two three four five six" +LIST:[@]:C/ /,/1g="one two three four five six" +LIST:[@]:[0]:C/ /,/="one,two three four five six" +LIST:[0]:[@]:C/ /,/="one two three four five six" +LIST:[@]:[*]:C/ /,/="one,two three four five six" +LIST:[*]:[@]:C/ /,/="one two three four five six" +EMPTY="" +EMPTY:[#]="1" == 1 ? +ESCAPEDSPACE="\ " +ESCAPEDSPACE:[#]="1" == 1 ? +REALLYSPACE=" " +REALLYSPACE:[#]="1" == 1 ? +LIST:[#]="6" +LIST:[0]:[#]="1" == 1 ? +LIST:[*]:[#]="1" == 1 ? +LIST:[@]:[#]="6" +LIST:[1]:[#]="1" +LIST:[1..3]:[#]="3" +EMPTY:[1]="" +ESCAPEDSPACE="\ " +ESCAPEDSPACE:[1]="\ " +REALLYSPACE=" " +REALLYSPACE:[1]="" == "" ? +REALLYSPACE:[*]:[1]=" " == " " ? +LIST:[1]="one" +make: Bad modifier `:[1.]' for LIST +LIST:[1.]="" is an error +make: Bad modifier `:[1].' for LIST +LIST:[1].="}" is an error +LIST:[2]="two" +LIST:[6]="six" +LIST:[7]="" +LIST:[999]="" +make: Bad modifier `:[-]' for LIST +LIST:[-]="" is an error +make: Bad modifier `:[--]' for LIST +LIST:[--]="" is an error +LIST:[-1]="six" +LIST:[-2]="five" +LIST:[-6]="one" +LIST:[-7]="" +LIST:[-999]="" +LONGLIST:[17]="17" +LONGLIST:[0x11]="17" +LONGLIST:[021]="17" +LIST:[0]:[1]="one two three four five six" +LIST:[*]:[1]="one two three four five six" +LIST:[@]:[1]="one" +LIST:[0]:[2]="" +LIST:[*]:[2]="" +LIST:[@]:[2]="two" +LIST:[*]:C/ /,/:[2]="" +LIST:[*]:C/ /,/:[*]:[2]="" +LIST:[*]:C/ /,/:[@]:[2]="three" +make: Bad modifier `:[1.]' for LIST +LIST:[1.]="" is an error +make: Bad modifier `:[1..]' for LIST +LIST:[1..]="" is an error +LIST:[1..1]="one" +make: Bad modifier `:[1..1.]' for LIST +LIST:[1..1.]="" is an error +LIST:[1..2]="one two" +LIST:[2..1]="two one" +LIST:[3..-2]="three four five" +LIST:[-4..4]="three four" +make: Bad modifier `:[0..1]' for LIST +LIST:[0..1]="" is an error +make: Bad modifier `:[-1..0]' for LIST +LIST:[-1..0]="" is an error +LIST:[-1..1]="six five four three two one" +LIST:[0..0]="one two three four five six" +LIST:[3..99]="three four five six" +LIST:[-3..-99]="four three two one" +LIST:[-99..-3]="one two three four" +HASH="#" == "#" ? +LIST:[${HASH}]="6" +LIST:[${ZERO}]="one two three four five six" +LIST:[${ZERO}x${ONE}]="one" +LIST:[${ONE}]="one" +LIST:[${MINUSONE}]="six" +LIST:[${STAR}]="one two three four five six" +LIST:[${AT}]="one two three four five six" +make: Bad modifier `:[${EMPTY' for LIST +LIST:[${EMPTY}]="" is an error +LIST:[${LONGLIST:[21]:S/2//}]="one" +LIST:[${LIST:[#]}]="six" +LIST:[${LIST:[${HASH}]}]="six" +LIST:S/ /,/="one two three four five six" +LIST:S/ /,/W="one,two three four five six" +LIST:S/ /,/gW="one,two,three,four,five,six" +EMPTY:S/^/,/="," +EMPTY:S/^/,/W="," +LIST:C/ /,/="one two three four five six" +LIST:C/ /,/W="one,two three four five six" +LIST:C/ /,/gW="one,two,three,four,five,six" +EMPTY:C/^/,/="," +EMPTY:C/^/,/W="," +LIST:tW="one two three four five six" +LIST:tw="one two three four five six" +LIST:tW:C/ /,/="one,two three four five six" +LIST:tW:C/ /,/g="one,two,three,four,five,six" +LIST:tW:C/ /,/1g="one,two,three,four,five,six" +LIST:tw:C/ /,/="one two three four five six" +LIST:tw:C/ /,/g="one two three four five six" +LIST:tw:C/ /,/1g="one two three four five six" +LIST:tw:tW:C/ /,/="one,two three four five six" +LIST:tW:tw:C/ /,/="one two three four five six" +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/modword.mk b/buildrump.sh/src/usr.bin/make/unit-tests/modword.mk new file mode 100644 index 00000000..00a56de1 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/modword.mk @@ -0,0 +1,151 @@ +# $Id: modword.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ +# +# Test behaviour of new :[] modifier + +all: mod-squarebrackets mod-S-W mod-C-W mod-tW-tw + +LIST= one two three +LIST+= four five six +LONGLIST= 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 + +EMPTY= # the space should be ignored +ESCAPEDSPACE=\ # escaped space before the '#' +REALLYSPACE:=${EMPTY:C/^/ /W} +HASH= \# +AT= @ +STAR= * +ZERO= 0 +ONE= 1 +MINUSONE= -1 + +mod-squarebrackets: mod-squarebrackets-0-star-at \ + mod-squarebrackets-hash \ + mod-squarebrackets-n \ + mod-squarebrackets-start-end \ + mod-squarebrackets-nested + +mod-squarebrackets-0-star-at: + @echo 'LIST:[]="${LIST:[]}" is an error' + @echo 'LIST:[0]="${LIST:[0]}"' + @echo 'LIST:[0x0]="${LIST:[0x0]}"' + @echo 'LIST:[000]="${LIST:[000]}"' + @echo 'LIST:[*]="${LIST:[*]}"' + @echo 'LIST:[@]="${LIST:[@]}"' + @echo 'LIST:[0]:C/ /,/="${LIST:[0]:C/ /,/}"' + @echo 'LIST:[0]:C/ /,/g="${LIST:[0]:C/ /,/g}"' + @echo 'LIST:[0]:C/ /,/1g="${LIST:[0]:C/ /,/1g}"' + @echo 'LIST:[*]:C/ /,/="${LIST:[*]:C/ /,/}"' + @echo 'LIST:[*]:C/ /,/g="${LIST:[*]:C/ /,/g}"' + @echo 'LIST:[*]:C/ /,/1g="${LIST:[*]:C/ /,/1g}"' + @echo 'LIST:[@]:C/ /,/="${LIST:[@]:C/ /,/}"' + @echo 'LIST:[@]:C/ /,/g="${LIST:[@]:C/ /,/g}"' + @echo 'LIST:[@]:C/ /,/1g="${LIST:[@]:C/ /,/1g}"' + @echo 'LIST:[@]:[0]:C/ /,/="${LIST:[@]:[0]:C/ /,/}"' + @echo 'LIST:[0]:[@]:C/ /,/="${LIST:[0]:[@]:C/ /,/}"' + @echo 'LIST:[@]:[*]:C/ /,/="${LIST:[@]:[*]:C/ /,/}"' + @echo 'LIST:[*]:[@]:C/ /,/="${LIST:[*]:[@]:C/ /,/}"' + +mod-squarebrackets-hash: + @echo 'EMPTY="${EMPTY}"' + @echo 'EMPTY:[#]="${EMPTY:[#]}" == 1 ?' + @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"' + @echo 'ESCAPEDSPACE:[#]="${ESCAPEDSPACE:[#]}" == 1 ?' + @echo 'REALLYSPACE="${REALLYSPACE}"' + @echo 'REALLYSPACE:[#]="${REALLYSPACE:[#]}" == 1 ?' + @echo 'LIST:[#]="${LIST:[#]}"' + @echo 'LIST:[0]:[#]="${LIST:[0]:[#]}" == 1 ?' + @echo 'LIST:[*]:[#]="${LIST:[*]:[#]}" == 1 ?' + @echo 'LIST:[@]:[#]="${LIST:[@]:[#]}"' + @echo 'LIST:[1]:[#]="${LIST:[1]:[#]}"' + @echo 'LIST:[1..3]:[#]="${LIST:[1..3]:[#]}"' + +mod-squarebrackets-n: + @echo 'EMPTY:[1]="${EMPTY:[1]}"' + @echo 'ESCAPEDSPACE="${ESCAPEDSPACE}"' + @echo 'ESCAPEDSPACE:[1]="${ESCAPEDSPACE:[1]}"' + @echo 'REALLYSPACE="${REALLYSPACE}"' + @echo 'REALLYSPACE:[1]="${REALLYSPACE:[1]}" == "" ?' + @echo 'REALLYSPACE:[*]:[1]="${REALLYSPACE:[*]:[1]}" == " " ?' + @echo 'LIST:[1]="${LIST:[1]}"' + @echo 'LIST:[1.]="${LIST:[1.]}" is an error' + @echo 'LIST:[1].="${LIST:[1].}" is an error' + @echo 'LIST:[2]="${LIST:[2]}"' + @echo 'LIST:[6]="${LIST:[6]}"' + @echo 'LIST:[7]="${LIST:[7]}"' + @echo 'LIST:[999]="${LIST:[999]}"' + @echo 'LIST:[-]="${LIST:[-]}" is an error' + @echo 'LIST:[--]="${LIST:[--]}" is an error' + @echo 'LIST:[-1]="${LIST:[-1]}"' + @echo 'LIST:[-2]="${LIST:[-2]}"' + @echo 'LIST:[-6]="${LIST:[-6]}"' + @echo 'LIST:[-7]="${LIST:[-7]}"' + @echo 'LIST:[-999]="${LIST:[-999]}"' + @echo 'LONGLIST:[17]="${LONGLIST:[17]}"' + @echo 'LONGLIST:[0x11]="${LONGLIST:[0x11]}"' + @echo 'LONGLIST:[021]="${LONGLIST:[021]}"' + @echo 'LIST:[0]:[1]="${LIST:[0]:[1]}"' + @echo 'LIST:[*]:[1]="${LIST:[*]:[1]}"' + @echo 'LIST:[@]:[1]="${LIST:[@]:[1]}"' + @echo 'LIST:[0]:[2]="${LIST:[0]:[2]}"' + @echo 'LIST:[*]:[2]="${LIST:[*]:[2]}"' + @echo 'LIST:[@]:[2]="${LIST:[@]:[2]}"' + @echo 'LIST:[*]:C/ /,/:[2]="${LIST:[*]:C/ /,/:[2]}"' + @echo 'LIST:[*]:C/ /,/:[*]:[2]="${LIST:[*]:C/ /,/:[*]:[2]}"' + @echo 'LIST:[*]:C/ /,/:[@]:[2]="${LIST:[*]:C/ /,/:[@]:[2]}"' + +mod-squarebrackets-start-end: + @echo 'LIST:[1.]="${LIST:[1.]}" is an error' + @echo 'LIST:[1..]="${LIST:[1..]}" is an error' + @echo 'LIST:[1..1]="${LIST:[1..1]}"' + @echo 'LIST:[1..1.]="${LIST:[1..1.]}" is an error' + @echo 'LIST:[1..2]="${LIST:[1..2]}"' + @echo 'LIST:[2..1]="${LIST:[2..1]}"' + @echo 'LIST:[3..-2]="${LIST:[3..-2]}"' + @echo 'LIST:[-4..4]="${LIST:[-4..4]}"' + @echo 'LIST:[0..1]="${LIST:[0..1]}" is an error' + @echo 'LIST:[-1..0]="${LIST:[-1..0]}" is an error' + @echo 'LIST:[-1..1]="${LIST:[-1..1]}"' + @echo 'LIST:[0..0]="${LIST:[0..0]}"' + @echo 'LIST:[3..99]="${LIST:[3..99]}"' + @echo 'LIST:[-3..-99]="${LIST:[-3..-99]}"' + @echo 'LIST:[-99..-3]="${LIST:[-99..-3]}"' + +mod-squarebrackets-nested: + @echo 'HASH="${HASH}" == "#" ?' + @echo 'LIST:[$${HASH}]="${LIST:[${HASH}]}"' + @echo 'LIST:[$${ZERO}]="${LIST:[${ZERO}]}"' + @echo 'LIST:[$${ZERO}x$${ONE}]="${LIST:[${ZERO}x${ONE}]}"' + @echo 'LIST:[$${ONE}]="${LIST:[${ONE}]}"' + @echo 'LIST:[$${MINUSONE}]="${LIST:[${MINUSONE}]}"' + @echo 'LIST:[$${STAR}]="${LIST:[${STAR}]}"' + @echo 'LIST:[$${AT}]="${LIST:[${AT}]}"' + @echo 'LIST:[$${EMPTY}]="${LIST:[${EMPTY}]}" is an error' + @echo 'LIST:[$${LONGLIST:[21]:S/2//}]="${LIST:[${LONGLIST:[21]:S/2//}]}"' + @echo 'LIST:[$${LIST:[#]}]="${LIST:[${LIST:[#]}]}"' + @echo 'LIST:[$${LIST:[$${HASH}]}]="${LIST:[${LIST:[${HASH}]}]}"' + +mod-C-W: + @echo 'LIST:C/ /,/="${LIST:C/ /,/}"' + @echo 'LIST:C/ /,/W="${LIST:C/ /,/W}"' + @echo 'LIST:C/ /,/gW="${LIST:C/ /,/gW}"' + @echo 'EMPTY:C/^/,/="${EMPTY:C/^/,/}"' + @echo 'EMPTY:C/^/,/W="${EMPTY:C/^/,/W}"' + +mod-S-W: + @echo 'LIST:S/ /,/="${LIST:S/ /,/}"' + @echo 'LIST:S/ /,/W="${LIST:S/ /,/W}"' + @echo 'LIST:S/ /,/gW="${LIST:S/ /,/gW}"' + @echo 'EMPTY:S/^/,/="${EMPTY:S/^/,/}"' + @echo 'EMPTY:S/^/,/W="${EMPTY:S/^/,/W}"' + +mod-tW-tw: + @echo 'LIST:tW="${LIST:tW}"' + @echo 'LIST:tw="${LIST:tw}"' + @echo 'LIST:tW:C/ /,/="${LIST:tW:C/ /,/}"' + @echo 'LIST:tW:C/ /,/g="${LIST:tW:C/ /,/g}"' + @echo 'LIST:tW:C/ /,/1g="${LIST:tW:C/ /,/1g}"' + @echo 'LIST:tw:C/ /,/="${LIST:tw:C/ /,/}"' + @echo 'LIST:tw:C/ /,/g="${LIST:tw:C/ /,/g}"' + @echo 'LIST:tw:C/ /,/1g="${LIST:tw:C/ /,/1g}"' + @echo 'LIST:tw:tW:C/ /,/="${LIST:tw:tW:C/ /,/}"' + @echo 'LIST:tW:tw:C/ /,/="${LIST:tW:tw:C/ /,/}"' diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/order.exp b/buildrump.sh/src/usr.bin/make/unit-tests/order.exp new file mode 100644 index 00000000..d876914f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/order.exp @@ -0,0 +1,4 @@ +Making the.c +Making the.h +Making the.o from the.h the.c +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/order.mk b/buildrump.sh/src/usr.bin/make/unit-tests/order.mk new file mode 100644 index 00000000..f90b627d --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/order.mk @@ -0,0 +1,20 @@ +# $NetBSD: order.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +# Test that .ORDER is handled correctly. +# The explicit dependency the.o: the.h will make us examine the.h +# the .ORDER will prevent us building it immediately, +# we should then examine the.c rather than stop. + +all: the.o + +.ORDER: the.c the.h + +the.c the.h: + @echo Making $@ + +.SUFFIXES: .o .c + +.c.o: + @echo Making $@ from $? + +the.o: the.h diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/phony-end.exp b/buildrump.sh/src/usr.bin/make/unit-tests/phony-end.exp new file mode 100644 index 00000000..c3c517cc --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/phony-end.exp @@ -0,0 +1,6 @@ +.TARGET="phony" .PREFIX="phony" .IMPSRC="" +.TARGET="all" .PREFIX="all" .IMPSRC="phony" +.TARGET="ok" .PREFIX="ok" .IMPSRC="" +.TARGET="also.ok" .PREFIX="also.ok" .IMPSRC="" +.TARGET="bug" .PREFIX="bug" .IMPSRC="" +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/phony-end.mk b/buildrump.sh/src/usr.bin/make/unit-tests/phony-end.mk new file mode 100644 index 00000000..92cc0e6b --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/phony-end.mk @@ -0,0 +1,9 @@ +# $Id: phony-end.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +all ok also.ok bug phony: + @echo '${.TARGET .PREFIX .IMPSRC:L:@v@$v="${$v}"@}' + +.END: ok also.ok bug + +phony bug: .PHONY +all: phony diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/posix.exp b/buildrump.sh/src/usr.bin/make/unit-tests/posix.exp new file mode 100644 index 00000000..7e74caba --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/posix.exp @@ -0,0 +1,23 @@ +Posix says we should execute the command as if run by system(3) +Expect 'Hello,' and 'World!' +Hello, +World! +a command +a command prefixed by '+' executes even with -n +another command +make -n +echo a command +echo "a command prefixed by '+' executes even with -n" +a command prefixed by '+' executes even with -n +echo another command +make -n -j1 +{ echo a command +} || exit $? +echo "a command prefixed by '+' executes even with -n" +a command prefixed by '+' executes even with -n +{ echo another command +} || exit $? +Now we expect an error... +*** Error code 1 (continuing) +`all' not remade because of errors. +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/posix.mk b/buildrump.sh/src/usr.bin/make/unit-tests/posix.mk new file mode 100644 index 00000000..a73e2e57 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/posix.mk @@ -0,0 +1,24 @@ +# $Id: posix.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +all: x plus subs err + +x: + @echo "Posix says we should execute the command as if run by system(3)" + @echo "Expect 'Hello,' and 'World!'" + @echo Hello,; false; echo "World!" + +plus: + @echo a command + +@echo "a command prefixed by '+' executes even with -n" + @echo another command + +subs: + @echo make -n + @${.MAKE} -f ${MAKEFILE} -n plus + @echo make -n -j1 + @${.MAKE} -f ${MAKEFILE} -n -j1 plus + +err: + @(echo Now we expect an error...; exit 1) + @echo "Oops! you shouldn't see this!" + diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/posix1.exp b/buildrump.sh/src/usr.bin/make/unit-tests/posix1.exp new file mode 100644 index 00000000..fa1f15d6 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/posix1.exp @@ -0,0 +1,186 @@ +${VAR} = "foo bar baz" +a +b +c +foo baR baz, bar baz, foo bar baz, fooadd baradd bazadd +mkdir -p 'dir' +touch 'dir/obj_1.h' +mkdir -p 'dir' +printf '#include "obj_1.h"\nconst char* obj_1 = "dir/obj_1.c";\n' \ + >'dir/obj_1.c' +Local variables + ${@}="dir/obj_1.o" ${<}="dir/obj_1.c" + ${*}="dir/obj_1" ${?}="dir/obj_1.h dir/obj_1.c" + ${%}="" + +Directory and filename parts of local variables + ${@D}="dir" ${@F}="obj_1.o" + ${<D}="dir" ${<F}="obj_1.c" + ${*D}="dir" ${*F}="obj_1" + ${?D}="dir dir" ${?F}="obj_1.h obj_1.c" + ${%D}="" ${%F}="" + +Local variable substitutions + ${@:.o=}="dir/obj_1" ${<:.c=.C}="dir/obj_1.C" + ${*:=.h}="dir/obj_1.h" ${?:.h=.H}="dir/obj_1.H dir/obj_1.c" + ${%:=}="" + +Target with suffix transformations + ${@D:=append}="dirappend" + ${@F:.o=.O}="obj_1.O" + + Implied source with suffix transformations + ${<D:r=rr}="dirr" + ${<F:.c=.C}="obj_1.C" + + Suffixless target with suffix transformations + ${*D:.=dot}="dir" + ${*F:.a=}="obj_1" + + Out-of-date dependencies with suffix transformations + ${?D:ir=}="d d" + ${?F:.h=.H}="obj_1.H obj_1.c" + + Member with suffix transformations + ${%D:.=}="" + ${%F:${VAR2}=${VAR}}="" + +cc -c -o 'dir/obj_1.o' 'dir/obj_1.c' +mkdir -p '.' +touch 'dummy' +Local variables + ${@}="lib.a" ${<}="dir/obj_1.o" + ${*}="obj1" ${?}="dir/obj_1.o dummy" + ${%}="obj1.o" + +Directory and filename parts of local variables + ${@D}="." ${@F}="lib.a" + ${<D}="dir" ${<F}="obj_1.o" + ${*D}="." ${*F}="obj1" + ${?D}="dir ." ${?F}="obj_1.o dummy" + ${%D}="." ${%F}="obj1.o" + +Local variable substitutions + ${@:.o=}="lib.a" ${<:.c=.C}="dir/obj_1.o" + ${*:=.h}="obj1.h" ${?:.h=.H}="dir/obj_1.o dummy" + ${%:=}="obj1.o" + +Target with suffix transformations + ${@D:=append}=".append" + ${@F:.o=.O}="lib.a" + + Implied source with suffix transformations + ${<D:r=rr}="dirr" + ${<F:.c=.C}="obj_1.o" + + Suffixless target with suffix transformations + ${*D:.=dot}="dot" + ${*F:.a=}="obj1" + + Out-of-date dependencies with suffix transformations + ${?D:ir=}="d ." + ${?F:.h=.H}="obj_1.o dummy" + + Member with suffix transformations + ${%D:.=}="" + ${%F:${VAR2}=${VAR}}="obj1foo bar baz" + +cp 'dir/obj_1.o' 'obj1.o' +ar -rcv 'lib.a' 'obj1.o' +a - obj1.o +rm -f 'obj1.o' +mkdir -p '.' +printf '#include "obj_2.h"\nconst char* obj_2 = "obj_2.c";\n' \ + >'obj_2.c' +mkdir -p '.' +touch 'obj_2.h' +Local variables + ${@}="obj2.o" ${<}="obj_2.c" + ${*}="obj2" ${?}="obj_2.c obj_2.h dir/obj_1.h" + ${%}="" + +Directory and filename parts of local variables + ${@D}="." ${@F}="obj2.o" + ${<D}="." ${<F}="obj_2.c" + ${*D}="." ${*F}="obj2" + ${?D}=". . dir" ${?F}="obj_2.c obj_2.h obj_1.h" + ${%D}="" ${%F}="" + +Local variable substitutions + ${@:.o=}="obj2" ${<:.c=.C}="obj_2.C" + ${*:=.h}="obj2.h" ${?:.h=.H}="obj_2.c obj_2.H dir/obj_1.H" + ${%:=}="" + +Target with suffix transformations + ${@D:=append}=".append" + ${@F:.o=.O}="obj2.O" + + Implied source with suffix transformations + ${<D:r=rr}="." + ${<F:.c=.C}="obj_2.C" + + Suffixless target with suffix transformations + ${*D:.=dot}="dot" + ${*F:.a=}="obj2" + + Out-of-date dependencies with suffix transformations + ${?D:ir=}=". . d" + ${?F:.h=.H}="obj_2.c obj_2.H obj_1.H" + + Member with suffix transformations + ${%D:.=}="" + ${%F:${VAR2}=${VAR}}="" + +cc -c -o 'obj2.o' 'obj_2.c' +ar -rcv 'lib.a' 'obj2.o' +a - obj2.o +mkdir -p '.' +touch 'obj3.h' +mkdir -p 'dir' +touch 'dir/dummy' +mkdir -p '.' +printf '#include "obj3.h"\nconst char* obj3 = "obj3.c";\n' \ + >'obj3.c' +Local variables + ${@}="lib.a" ${<}="obj3.c" + ${*}="obj3" ${?}="obj3.h dir/dummy obj3.c" + ${%}="obj3.o" + +Directory and filename parts of local variables + ${@D}="." ${@F}="lib.a" + ${<D}="." ${<F}="obj3.c" + ${*D}="." ${*F}="obj3" + ${?D}=". dir ." ${?F}="obj3.h dummy obj3.c" + ${%D}="." ${%F}="obj3.o" + +Local variable substitutions + ${@:.o=}="lib.a" ${<:.c=.C}="obj3.C" + ${*:=.h}="obj3.h" ${?:.h=.H}="obj3.H dir/dummy obj3.c" + ${%:=}="obj3.o" + +Target with suffix transformations + ${@D:=append}=".append" + ${@F:.o=.O}="lib.a" + + Implied source with suffix transformations + ${<D:r=rr}="." + ${<F:.c=.C}="obj3.C" + + Suffixless target with suffix transformations + ${*D:.=dot}="dot" + ${*F:.a=}="obj3" + + Out-of-date dependencies with suffix transformations + ${?D:ir=}=". d ." + ${?F:.h=.H}="obj3.H dummy obj3.c" + + Member with suffix transformations + ${%D:.=}="" + ${%F:${VAR2}=${VAR}}="obj3foo bar baz" + +cc -c -o 'obj3.o' 'obj3.c' +ar -rcv 'lib.a' 'obj3.o' +a - obj3.o +rm -f 'obj3.o' +ar -s 'lib.a' +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/posix1.mk b/buildrump.sh/src/usr.bin/make/unit-tests/posix1.mk new file mode 100644 index 00000000..1bf6a560 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/posix1.mk @@ -0,0 +1,184 @@ +# $NetBSD: posix1.mk,v 1.3 2014/08/30 22:21:08 sjg Exp $ + +# Keep the default suffixes from interfering, just in case. +.SUFFIXES: + +all: line-continuations suffix-substitution localvars + +# we need to clean for repeatable results +.BEGIN: clean +clean: + @rm -f lib.a dir/* dummy obj* + +# +# Line continuations +# + +# Escaped newlines and leading whitespace from the next line are replaced +# with single space, except in commands, where the escape and the newline +# are retained, but a single leading tab (if any) from the next line is +# removed. (PR 49085) +# Expect: +# ${VAR} = "foo bar baz" +# a +# b +# c +VAR = foo\ +\ + bar\ + baz + +line-continuations: + @echo '$${VAR} = "${VAR}"' + @echo 'aXbXc' | sed -e 's/X/\ + /g' + + +# +# Suffix substitution +# + +# The only variable modifier accepted by POSIX. +# ${VAR:s1=s2}: replace s1, if found, with s2 at end of each word in +# ${VAR}. s1 and s2 may contain macro expansions. +# Expect: foo baR baz, bar baz, foo bar baz, fooadd baradd bazadd +suffix-substitution: + @echo '${VAR:r=R}, ${VAR:foo=}, ${VAR:not_there=wrong}, ${VAR:=add}' + + +# +# Local variables: regular forms, D/F forms and suffix substitution. +# + +# In the past substitutions did not work with the D/F forms and those +# forms were not available for $?. (PR 49085) + +ARFLAGS = -rcv + +localvars: lib.a + +# $@ = target or archive name $< = implied source +# $* = target without suffix $? = sources newer than target +# $% = archive member name +LOCALS = \ + "Local variables\n\ + \$${@}=\"${@}\" \$${<}=\"${<}\"\n\ + \$${*}=\"${*}\" \$${?}=\"${?}\"\n\ + \$${%%}=\"${%}\"\n\n" + +# $XD = directory part of X $XF = file part of X +# X is one of the local variables. +LOCAL_ALTERNATIVES = \ + "Directory and filename parts of local variables\n\ + \$${@D}=\"${@D}\" \$${@F}=\"${@F}\"\n\ + \$${<D}=\"${<D}\" \$${<F}=\"${<F}\"\n\ + \$${*D}=\"${*D}\" \$${*F}=\"${*F}\"\n\ + \$${?D}=\"${?D}\" \$${?F}=\"${?F}\"\n\ + \$${%%D}=\"${%D}\" \$${%%F}=\"${%F}\"\n\n" + +# Do all kinds of meaningless substitutions on local variables to see +# if they work. Add, remove and replace things. +VAR2 = .o +VAR3 = foo +LOCAL_SUBSTITUTIONS = \ + "Local variable substitutions\n\ + \$${@:.o=}=\"${@:.o=}\" \$${<:.c=.C}=\"${<:.c=.C}\"\n\ + \$${*:=.h}=\"${*:=.h}\" \$${?:.h=.H}=\"${?:.h=.H}\"\n\ + \$${%%:=}=\"${%:=}\"\n\n" + +LOCAL_ALTERNATIVE_SUBSTITUTIONS = \ + "Target with suffix transformations\n\ + \$${@D:=append}=\"${@D:=append}\"\n\ + \$${@F:.o=.O}=\"${@F:.o=.O}\"\n\ + \n\ + Implied source with suffix transformations\n\ + \$${<D:r=rr}=\"${<D:r=rr}\"\n\ + \$${<F:.c=.C}=\"${<F:.c=.C}\"\n\ + \n\ + Suffixless target with suffix transformations\n\ + \$${*D:.=dot}=\"${*D:.=dot}\"\n\ + \$${*F:.a=}=\"${*F:.a=}\"\n\ + \n\ + Out-of-date dependencies with suffix transformations\n\ + \$${?D:ir=}=\"${?D:ir=}\"\n\ + \$${?F:.h=.H}=\"${?F:.h=.H}\"\n\ + \n\ + Member with suffix transformations\n\ + \$${%%D:.=}=\"${%D:.=}\"\n\ + \$${%%F:\$${VAR2}=\$${VAR}}=\"${%F:${VAR2}=${VAR}}\"\n\n" + +.SUFFIXES: .c .o .a + +# The system makefiles make the .c.a rule .PRECIOUS with a special source, +# but such a thing is not POSIX compatible. It's also somewhat useless +# in a test makefile. +.c.a: + @printf ${LOCALS} + @printf ${LOCAL_ALTERNATIVES} + @printf ${LOCAL_SUBSTITUTIONS} + @printf ${LOCAL_ALTERNATIVE_SUBSTITUTIONS} + cc -c -o '${%}' '${<}' + ar ${ARFLAGS} '${@}' '${%}' + rm -f '${%}' + +.c.o: + @printf ${LOCALS} + @printf ${LOCAL_ALTERNATIVES} + @printf ${LOCAL_SUBSTITUTIONS} + @printf ${LOCAL_ALTERNATIVE_SUBSTITUTIONS} + cc -c -o '${@}' '${<}' + +# Some of these rules are padded with useless extra dependencies just so +# that ${?} has more than one file. + +lib.a: lib.a(obj1.o) lib.a(obj2.o) lib.a(obj3.o) + ar -s '${@}' + +# Explicit rule where the dependency is an inferred file. The dependency +# object's name differs from the member's because there was a bug which +# forced a dependency on member even when no such dependency was specified +# (PR 49086). +lib.a(obj1.o): dir/obj_1.o dummy + @printf ${LOCALS} + @printf ${LOCAL_ALTERNATIVES} + @printf ${LOCAL_SUBSTITUTIONS} + @printf ${LOCAL_ALTERNATIVE_SUBSTITUTIONS} + cp 'dir/obj_1.o' '$%' + ar ${ARFLAGS} '${@}' '$%' + rm -f '$%' + +# Excplicit rule where the dependency also has an explicit rule. +lib.a(obj2.o): obj2.o + ar ${ARFLAGS} '${@}' '${%}' + +# Use .c.a inference with an extra dependency. +lib.a(obj3.o): obj3.h dir/dummy + +# Use .c.o inference with an extra dependency. +dir/obj_1.o: dir/obj_1.h + +# According to POSIX, $* is only required for inference rules and $<'s +# value is unspecified outside of inference rules. Strictly speaking +# we shouldn't be expanding them here but who cares. At least we get +# to check that the program does nothing stupid (like crash) with them. +# The C file is named differently from the object file because there +# was a bug which forced dependencies based on inference rules on all +# applicable targets (PR 49086). +obj2.o: obj_2.c obj_2.h dir/obj_1.h + @printf ${LOCALS} + @printf ${LOCAL_ALTERNATIVES} + @printf ${LOCAL_SUBSTITUTIONS} + @printf ${LOCAL_ALTERNATIVE_SUBSTITUTIONS} + cc -c -o '${@}' 'obj_2.c' + +# Hey, this is make, we can make our own test data setup! obj1.c +# and obj2.c are not used, so they should not get created. They're here +# as a bait for a regression into the forced dependencies discussed earlier. +obj1.c dir/obj_1.c obj2.c obj_2.c obj3.c: + mkdir -p '${@D}' + printf '#include "${@F:.c=.h}"\nconst char* ${@F:.c=} = "${@}";\n' \ + >'${@}' + +dir/obj_1.h obj_2.h obj3.h dummy dir/dummy: + mkdir -p '${@D}' + touch '${@}' diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/qequals.exp b/buildrump.sh/src/usr.bin/make/unit-tests/qequals.exp new file mode 100644 index 00000000..6b2f4dce --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/qequals.exp @@ -0,0 +1,2 @@ +V.i386 ?= OK +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/qequals.mk b/buildrump.sh/src/usr.bin/make/unit-tests/qequals.mk new file mode 100644 index 00000000..db6d9c3f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/qequals.mk @@ -0,0 +1,8 @@ +# $Id: qequals.mk,v 1.1 2014/08/21 13:44:51 apb Exp $ + +M= i386 +V.i386= OK +V.$M ?= bug + +all: + @echo 'V.$M ?= ${V.$M}' diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/suffixes.exp b/buildrump.sh/src/usr.bin/make/unit-tests/suffixes.exp new file mode 100644 index 00000000..2a46e1cf --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/suffixes.exp @@ -0,0 +1,35 @@ +make: don't know how to make issue3 (continuing) +There should be no text after the colon: +touch .a +There should be no text after the colon: +touch .a.b +There should be no text after the colon: +touch .b.a +touch issue5a.c +first set +cp issue5a.c issue5a.d +touch issue5b.d +first set +cp issue5b.d issue5b.c +touch issue5c.d +first set +cp issue5c.d issue5c +touch issue5d.d +first set +cp issue5d.d issue5d.e +touch issue5e.e +first set +cp issue5e.e issue5e.d +make: don't know how to make issue6.f (continuing) +touch issue10.d +first set +cp issue10.d issue10.e +touch issue11.h +touch issue11.first +.ALLSRC: issue11.h issue11.first +cp issue11.h issue11.i +touch issue11.second +.ALLSRC: issue11.i issue11.second +cp issue11.i issue11.j +`all' not remade because of errors. +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/suffixes.mk b/buildrump.sh/src/usr.bin/make/unit-tests/suffixes.mk new file mode 100644 index 00000000..113484a5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/suffixes.mk @@ -0,0 +1,89 @@ +# $NetBSD: suffixes.mk,v 1.3 2014/08/30 22:21:08 sjg Exp $ + +# Issues from PR 49086 + +# Issue 3: single suffix rules remain active after .SUFFIXES is cleared +# +# There's a rule for issue3.a, but .a is no longer a known suffix when +# targets are being made, so issue3 should not get made. +all: issue3 + +# Issue 4: suffix rules do not become regular rules when .SUFFIXES is cleared +# +# When the rules were encountered, .a and .b were known suffices, but later +# on they were forgotten. These should get created as regular targets. +all: .a .a.b .b.a + +# Issue 5: adding more suffixes does not make existing rules into suffix rules +# +# When the targets .c.d, .d.c, .d, .d.e, and .e.d were encountered, only .a, +# .b and .c were known suffixes, so all of them were regular rules. Later +# rest of the suffixes were made known, so they should all be suffix +# transformation rules. +all: issue5a.d issue5b.c issue5c issue5d.e issue5e.d + +# Issue 6: transformation search can end up in an infinite loop +# +# There is no file or target from which issue6.f could be made from so +# this should fail. The bug was that because rules .e.f, .d.e and .e.d +# exist, make would try to make .f from .e and then infinitely try +# to do .e from .d and vice versa. +all: issue6.f + +# Issue 10: explicit dependencies affect transformation rule selection +# +# If issue10.e is wanted and both issue10.d and issue10.f are available, +# make should choose the .d.e rule, because .d is before .f in .SUFFIXES. +# The bug was that if issue10.d had an explicit dependency on issue10.f, +# it would choose .f.e instead. +all: issue10.e + +# Issue 11: sources from transformation rules are expanded incorrectly +# +# issue11.j should depend on issue11.i and issue11.second and issue11.i +# should depend on issue11.h and issue11.first. The bug was that +# the dynamic sources were expanded before ${.PREFIX} and ${.TARGET} were +# available, so they would have expanded to a null string. +all: issue11.j + +# we need to clean for repeatable results +.BEGIN: clean +clean: + @rm -f issue* .[ab]* + +.SUFFIXES: .a .b .c + +.a .a.b .b.a: + @echo 'There should be no text after the colon: ${.IMPSRC}' + touch ${.TARGET} + +.c.d .d.c .d .d.e .e.d: + @echo 'first set' + cp ${.IMPSRC} ${.TARGET} + +.SUFFIXES: +.SUFFIXES: .c .d .e .f .g + +.e .e.f .f.e: + @echo 'second set' + cp ${.IMPSRC} ${.TARGET} + +issue3.a: + @echo 'There is a bug if you see this.' + touch ${.TARGET} + +issue5a.c issue5b.d issue5c.d issue5d.d issue5e.e issue10.d issue10.f: + touch ${.TARGET} + +.SUFFIXES: .h .i .j + +.h.i: ${.PREFIX}.first + @echo '.ALLSRC: ${.ALLSRC}' + cp ${.IMPSRC} ${.TARGET} + +.i.j: ${.PREFIX}.second + @echo '.ALLSRC: ${.ALLSRC}' + cp ${.IMPSRC} ${.TARGET} + +issue11.h issue11.first issue11.second: + touch ${.TARGET} diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/sunshcmd.exp b/buildrump.sh/src/usr.bin/make/unit-tests/sunshcmd.exp new file mode 100644 index 00000000..b14f6b68 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/sunshcmd.exp @@ -0,0 +1,4 @@ +TEST1=hello +TEST2=bye +TEST3=later +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/sunshcmd.mk b/buildrump.sh/src/usr.bin/make/unit-tests/sunshcmd.mk new file mode 100644 index 00000000..e3baf901 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/sunshcmd.mk @@ -0,0 +1,10 @@ +BYECMD = echo bye +LATERCMD = echo later +TEST1 :sh = echo hello +TEST2 :sh = ${BYECMD} +TEST3 = ${LATERCMD:sh} + +all: + @echo "TEST1=${TEST1}" + @echo "TEST2=${TEST2}" + @echo "TEST3=${TEST3}" diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/sysv.exp b/buildrump.sh/src/usr.bin/make/unit-tests/sysv.exp new file mode 100644 index 00000000..4cce2de3 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/sysv.exp @@ -0,0 +1,7 @@ +FOOBAR = +FOOBAR = foobar fubar +fun +fun +fun +In the Sun +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/sysv.mk b/buildrump.sh/src/usr.bin/make/unit-tests/sysv.mk new file mode 100644 index 00000000..3651eda6 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/sysv.mk @@ -0,0 +1,26 @@ +# $Id: sysv.mk,v 1.2 2014/08/30 22:21:08 sjg Exp $ + +FOO ?= +FOOBAR = ${FOO:=bar} + +_this := ${.PARSEDIR}/${.PARSEFILE} + +B = /b +S = / +FUN = ${B}${S}fun +SUN = the Sun + +# we expect nothing when FOO is empty +all: foo fun + +foo: + @echo FOOBAR = ${FOOBAR} +.if empty(FOO) + @FOO="foo fu" ${.MAKE} -f ${_this} foo +.endif + +fun: + @echo ${FUN:T} + @echo ${FUN:${B}${S}fun=fun} + @echo ${FUN:${B}${S}%=%} + @echo ${In:L:%=% ${SUN}} diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/ternary.exp b/buildrump.sh/src/usr.bin/make/unit-tests/ternary.exp new file mode 100644 index 00000000..ed9c1bd9 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/ternary.exp @@ -0,0 +1,10 @@ +The answer is unknown +The answer is unknown +The answer is empty +The answer is known +The answer is +The answer is empty +The answer is known +The answer is 42 +The answer is 42 +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/ternary.mk b/buildrump.sh/src/usr.bin/make/unit-tests/ternary.mk new file mode 100644 index 00000000..77f83498 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/ternary.mk @@ -0,0 +1,8 @@ + +all: + @for x in "" A= A=42; do ${.MAKE} -f ${MAKEFILE} show $$x; done + +show: + @echo "The answer is ${A:?known:unknown}" + @echo "The answer is ${A:?$A:unknown}" + @echo "The answer is ${empty(A):?empty:$A}" diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/unexport-env.exp b/buildrump.sh/src/usr.bin/make/unit-tests/unexport-env.exp new file mode 100644 index 00000000..6d43cab4 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/unexport-env.exp @@ -0,0 +1,2 @@ +UT_TEST=unexport-env +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/unexport-env.mk b/buildrump.sh/src/usr.bin/make/unit-tests/unexport-env.mk new file mode 100644 index 00000000..b8192f1d --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/unexport-env.mk @@ -0,0 +1,14 @@ +# $Id: unexport-env.mk,v 1.1 2014/08/21 13:44:52 apb Exp $ + +# pick up a bunch of exported vars +.include "export.mk" + +# an example of setting up a minimal environment. +PATH = /bin:/usr/bin:/sbin:/usr/sbin + +# now clobber the environment to just PATH and UT_TEST +UT_TEST = unexport-env + +# this removes everything +.unexport-env +.export PATH UT_TEST diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/unexport.exp b/buildrump.sh/src/usr.bin/make/unit-tests/unexport.exp new file mode 100644 index 00000000..7b16ea36 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/unexport.exp @@ -0,0 +1,4 @@ +UT_DOLLAR=This is $UT_FU +UT_FU=fubar +UT_TEST=unexport +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/unexport.mk b/buildrump.sh/src/usr.bin/make/unit-tests/unexport.mk new file mode 100644 index 00000000..b3d7d349 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/unexport.mk @@ -0,0 +1,8 @@ +# $Id: unexport.mk,v 1.1 2014/08/21 13:44:52 apb Exp $ + +# pick up a bunch of exported vars +.include "export.mk" + +.unexport UT_ZOO UT_FOO + +UT_TEST = unexport diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/varcmd.exp b/buildrump.sh/src/usr.bin/make/unit-tests/varcmd.exp new file mode 100644 index 00000000..34dd637f --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/varcmd.exp @@ -0,0 +1,9 @@ +default FU=<v>fu</v> FOO=<v>foo</v> VAR=<v></v> +two FU=<v>bar</v> FOO=<v>goo</v> VAR=<v></v> +three FU=<v>bar</v> FOO=<v>goo</v> VAR=<v></v> +four FU=<v>bar</v> FOO=<v>goo</v> VAR=<v>Internal</v> +five FU=<v>bar</v> FOO=<v>goo</v> VAR=<v>Internal</v> +five v=is x k=is x +six v=is y k=is y +show-v v=override k=override +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/varcmd.mk b/buildrump.sh/src/usr.bin/make/unit-tests/varcmd.mk new file mode 100644 index 00000000..116795fe --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/varcmd.mk @@ -0,0 +1,49 @@ +# $Id: varcmd.mk,v 1.1 2014/08/21 13:44:52 apb Exp $ +# +# Test behaviour of recursive make and vars set on command line. + +FU=fu +FOO?=foo +.if !empty(.TARGETS) +TAG=${.TARGETS} +.endif +TAG?=default + +all: one + +show: + @echo "${TAG} FU=<v>${FU}</v> FOO=<v>${FOO}</v> VAR=<v>${VAR}</v>" + +one: show + @${.MAKE} -f ${MAKEFILE} FU=bar FOO=goo two + +two: show + @${.MAKE} -f ${MAKEFILE} three + +three: show + @${.MAKE} -f ${MAKEFILE} four + + +.ifmake four +VAR=Internal +.MAKEOVERRIDES+= VAR +.endif + +four: show + @${.MAKE} -f ${MAKEFILE} five + +M = x +V.y = is y +V.x = is x +V := ${V.$M} +K := ${V} + +show-v: + @echo '${TAG} v=${V} k=${K}' + +five: show show-v + @${.MAKE} -f ${MAKEFILE} M=y six + +six: show-v + @${.MAKE} -f ${MAKEFILE} V=override show-v + diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/varmisc.exp b/buildrump.sh/src/usr.bin/make/unit-tests/varmisc.exp new file mode 100644 index 00000000..1636aafc --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/varmisc.exp @@ -0,0 +1,2 @@ + +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/varmisc.mk b/buildrump.sh/src/usr.bin/make/unit-tests/varmisc.mk new file mode 100644 index 00000000..07d8c265 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/varmisc.mk @@ -0,0 +1,8 @@ +# $Id: varmisc.mk,v 1.2 2014/08/30 22:21:08 sjg Exp $ +# +# Miscellaneous variable tests. + +all: unmatched_var_paren + +unmatched_var_paren: + @echo ${foo::=foo-text} diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/varshell.exp b/buildrump.sh/src/usr.bin/make/unit-tests/varshell.exp new file mode 100644 index 00000000..6ac8c88a --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/varshell.exp @@ -0,0 +1,12 @@ +sh: /bin/no/such/command: not found +make: "varshell.mk" line 5: warning: "/bin/no/such/command" returned non-zero status +make: "varshell.mk" line 6: warning: "kill -14 $$" exited on a signal +make: "varshell.mk" line 7: warning: "false" returned non-zero status +make: "varshell.mk" line 8: warning: "echo "output before the error"; false" returned non-zero status +EXEC_FAILED='' +TERMINATED_BY_SIGNAL='' +ERROR_NO_OUTPUT='' +ERROR_WITH_OUTPUT='output before the error' +NO_ERROR_NO_OUTPUT='' +NO_ERROR_WITH_OUTPUT='this is good' +exit status 0 diff --git a/buildrump.sh/src/usr.bin/make/unit-tests/varshell.mk b/buildrump.sh/src/usr.bin/make/unit-tests/varshell.mk new file mode 100644 index 00000000..a0067360 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/unit-tests/varshell.mk @@ -0,0 +1,18 @@ +# $Id: varshell.mk,v 1.2 2015/04/10 20:41:59 sjg Exp $ +# +# Test VAR != shell command + +EXEC_FAILED != /bin/no/such/command +TERMINATED_BY_SIGNAL != kill -14 $$$$ +ERROR_NO_OUTPUT != false +ERROR_WITH_OUTPUT != echo "output before the error"; false +NO_ERROR_NO_OUTPUT != true +NO_ERROR_WITH_OUTPUT != echo "this is good" + +allvars= EXEC_FAILED TERMINATED_BY_SIGNAL ERROR_NO_OUTPUT ERROR_WITH_OUTPUT \ + NO_ERROR_NO_OUTPUT NO_ERROR_WITH_OUTPUT + +all: +.for v in ${allvars} + @echo ${v}=\'${${v}}\' +.endfor diff --git a/buildrump.sh/src/usr.bin/make/util.c b/buildrump.sh/src/usr.bin/make/util.c new file mode 100644 index 00000000..506fb4d9 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/util.c @@ -0,0 +1,494 @@ +/* $NetBSD: util.c,v 1.54 2013/11/26 13:44:41 joerg Exp $ */ + +/* + * Missing stuff from OS's + */ +#if defined(__MINT__) || defined(__linux__) +#include <signal.h> +#endif + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: util.c,v 1.54 2013/11/26 13:44:41 joerg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: util.c,v 1.54 2013/11/26 13:44:41 joerg Exp $"); +#endif +#endif + +#include <sys/param.h> + +#include <errno.h> +#include <stdio.h> +#include <time.h> +#include <signal.h> + +#include "make.h" + +#if !defined(MAKE_NATIVE) && !defined(HAVE_STRERROR) +extern int errno, sys_nerr; +extern char *sys_errlist[]; + +char * +strerror(int e) +{ + static char buf[100]; + if (e < 0 || e >= sys_nerr) { + snprintf(buf, sizeof(buf), "Unknown error %d", e); + return buf; + } + else + return sys_errlist[e]; +} +#endif + +#if !defined(MAKE_NATIVE) && !defined(HAVE_SETENV) +extern char **environ; + +static char * +findenv(const char *name, int *offset) +{ + size_t i, len; + char *p, *q; + + len = strlen(name); + for (i = 0; (q = environ[i]); i++) { + p = strchr(q, '='); + if (p == NULL || p - q != len) + continue; + if (strncmp(name, q, len) == 0) { + *offset = i; + return q + len + 1; + } + } + *offset = i; + return NULL; +} + +char * +getenv(const char *name) +{ + int offset; + + return(findenv(name, &offset)); +} + +int +unsetenv(const char *name) +{ + char **p; + int offset; + + if (name == NULL || *name == '\0' || strchr(name, '=') != NULL) { + errno = EINVAL; + return -1; + } + + while (findenv(name, &offset)) { /* if set multiple times */ + for (p = &environ[offset];; ++p) + if (!(*p = *(p + 1))) + break; + } + return 0; +} + +int +setenv(const char *name, const char *value, int rewrite) +{ + char *c, **newenv; + const char *cc; + size_t l_value, size; + int offset; + + if (name == NULL || value == NULL) { + errno = EINVAL; + return -1; + } + + if (*value == '=') /* no `=' in value */ + ++value; + l_value = strlen(value); + + /* find if already exists */ + if ((c = findenv(name, &offset))) { + if (!rewrite) + return 0; + if (strlen(c) >= l_value) /* old larger; copy over */ + goto copy; + } else { /* create new slot */ + size = sizeof(char *) * (offset + 2); + if (savedEnv == environ) { /* just increase size */ + if ((newenv = realloc(savedEnv, size)) == NULL) + return -1; + savedEnv = newenv; + } else { /* get new space */ + /* + * We don't free here because we don't know if + * the first allocation is valid on all OS's + */ + if ((savedEnv = malloc(size)) == NULL) + return -1; + (void)memcpy(savedEnv, environ, size - sizeof(char *)); + } + environ = savedEnv; + environ[offset + 1] = NULL; + } + for (cc = name; *cc && *cc != '='; ++cc) /* no `=' in name */ + continue; + size = cc - name; + /* name + `=' + value */ + if ((environ[offset] = malloc(size + l_value + 2)) == NULL) + return -1; + c = environ[offset]; + (void)memcpy(c, name, size); + c += size; + *c++ = '='; +copy: + (void)memcpy(c, value, l_value + 1); + return 0; +} + +#ifdef TEST +int +main(int argc, char *argv[]) +{ + setenv(argv[1], argv[2], 0); + printf("%s\n", getenv(argv[1])); + unsetenv(argv[1]); + printf("%s\n", getenv(argv[1])); + return 0; +} +#endif + +#endif + +#if defined(__hpux__) || defined(__hpux) +/* strrcpy(): + * Like strcpy, going backwards and returning the new pointer + */ +static char * +strrcpy(char *ptr, char *str) +{ + int len = strlen(str); + + while (len) + *--ptr = str[--len]; + + return (ptr); +} /* end strrcpy */ + +char *sys_siglist[] = { + "Signal 0", + "Hangup", /* SIGHUP */ + "Interrupt", /* SIGINT */ + "Quit", /* SIGQUIT */ + "Illegal instruction", /* SIGILL */ + "Trace/BPT trap", /* SIGTRAP */ + "IOT trap", /* SIGIOT */ + "EMT trap", /* SIGEMT */ + "Floating point exception", /* SIGFPE */ + "Killed", /* SIGKILL */ + "Bus error", /* SIGBUS */ + "Segmentation fault", /* SIGSEGV */ + "Bad system call", /* SIGSYS */ + "Broken pipe", /* SIGPIPE */ + "Alarm clock", /* SIGALRM */ + "Terminated", /* SIGTERM */ + "User defined signal 1", /* SIGUSR1 */ + "User defined signal 2", /* SIGUSR2 */ + "Child exited", /* SIGCLD */ + "Power-fail restart", /* SIGPWR */ + "Virtual timer expired", /* SIGVTALRM */ + "Profiling timer expired", /* SIGPROF */ + "I/O possible", /* SIGIO */ + "Window size changes", /* SIGWINDOW */ + "Stopped (signal)", /* SIGSTOP */ + "Stopped", /* SIGTSTP */ + "Continued", /* SIGCONT */ + "Stopped (tty input)", /* SIGTTIN */ + "Stopped (tty output)", /* SIGTTOU */ + "Urgent I/O condition", /* SIGURG */ + "Remote lock lost (NFS)", /* SIGLOST */ + "Signal 31", /* reserved */ + "DIL signal" /* SIGDIL */ +}; +#endif /* __hpux__ || __hpux */ + +#if defined(__hpux__) || defined(__hpux) +#include <sys/types.h> +#include <sys/syscall.h> +#include <sys/signal.h> +#include <sys/stat.h> +#include <dirent.h> +#include <sys/time.h> +#include <unistd.h> + +int +killpg(int pid, int sig) +{ + return kill(-pid, sig); +} + +#if !defined(__hpux__) && !defined(__hpux) +void +srandom(long seed) +{ + srand48(seed); +} + +long +random(void) +{ + return lrand48(); +} +#endif + +#if !defined(__hpux__) && !defined(__hpux) +int +utimes(char *file, struct timeval tvp[2]) +{ + struct utimbuf t; + + t.actime = tvp[0].tv_sec; + t.modtime = tvp[1].tv_sec; + return(utime(file, &t)); +} +#endif + +#if !defined(BSD) && !defined(d_fileno) +# define d_fileno d_ino +#endif + +#ifndef DEV_DEV_COMPARE +# define DEV_DEV_COMPARE(a, b) ((a) == (b)) +#endif +#define ISDOT(c) ((c)[0] == '.' && (((c)[1] == '\0') || ((c)[1] == '/'))) +#define ISDOTDOT(c) ((c)[0] == '.' && ISDOT(&((c)[1]))) + +char * +getwd(char *pathname) +{ + DIR *dp; + struct dirent *d; + extern int errno; + + struct stat st_root, st_cur, st_next, st_dotdot; + char pathbuf[MAXPATHLEN], nextpathbuf[MAXPATHLEN * 2]; + char *pathptr, *nextpathptr, *cur_name_add; + + /* find the inode of root */ + if (stat("/", &st_root) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat \"/\" (%s)", strerror(errno)); + return NULL; + } + pathbuf[MAXPATHLEN - 1] = '\0'; + pathptr = &pathbuf[MAXPATHLEN - 1]; + nextpathbuf[MAXPATHLEN - 1] = '\0'; + cur_name_add = nextpathptr = &nextpathbuf[MAXPATHLEN - 1]; + + /* find the inode of the current directory */ + if (lstat(".", &st_cur) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat \".\" (%s)", strerror(errno)); + return NULL; + } + nextpathptr = strrcpy(nextpathptr, "../"); + + /* Descend to root */ + for (;;) { + + /* look if we found root yet */ + if (st_cur.st_ino == st_root.st_ino && + DEV_DEV_COMPARE(st_cur.st_dev, st_root.st_dev)) { + (void)strcpy(pathname, *pathptr != '/' ? "/" : pathptr); + return (pathname); + } + + /* open the parent directory */ + if (stat(nextpathptr, &st_dotdot) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat directory \"%s\" (%s)", + nextpathptr, strerror(errno)); + return NULL; + } + if ((dp = opendir(nextpathptr)) == NULL) { + (void)sprintf(pathname, + "getwd: Cannot open directory \"%s\" (%s)", + nextpathptr, strerror(errno)); + return NULL; + } + + /* look in the parent for the entry with the same inode */ + if (DEV_DEV_COMPARE(st_dotdot.st_dev, st_cur.st_dev)) { + /* Parent has same device. No need to stat every member */ + for (d = readdir(dp); d != NULL; d = readdir(dp)) + if (d->d_fileno == st_cur.st_ino) + break; + } + else { + /* + * Parent has a different device. This is a mount point so we + * need to stat every member + */ + for (d = readdir(dp); d != NULL; d = readdir(dp)) { + if (ISDOT(d->d_name) || ISDOTDOT(d->d_name)) + continue; + (void)strcpy(cur_name_add, d->d_name); + if (lstat(nextpathptr, &st_next) == -1) { + (void)sprintf(pathname, + "getwd: Cannot stat \"%s\" (%s)", + d->d_name, strerror(errno)); + (void)closedir(dp); + return NULL; + } + /* check if we found it yet */ + if (st_next.st_ino == st_cur.st_ino && + DEV_DEV_COMPARE(st_next.st_dev, st_cur.st_dev)) + break; + } + } + if (d == NULL) { + (void)sprintf(pathname, + "getwd: Cannot find \".\" in \"..\""); + (void)closedir(dp); + return NULL; + } + st_cur = st_dotdot; + pathptr = strrcpy(pathptr, d->d_name); + pathptr = strrcpy(pathptr, "/"); + nextpathptr = strrcpy(nextpathptr, "../"); + (void)closedir(dp); + *cur_name_add = '\0'; + } +} /* end getwd */ +#endif /* __hpux */ + +/* force posix signals */ +void (* +bmake_signal(int s, void (*a)(int)))(int) +{ + struct sigaction sa, osa; + + sa.sa_handler = a; + sigemptyset(&sa.sa_mask); + sa.sa_flags = SA_RESTART; + + if (sigaction(s, &sa, &osa) == -1) + return SIG_ERR; + else + return osa.sa_handler; +} + +#if !defined(MAKE_NATIVE) && !defined(HAVE_VSNPRINTF) +#include <stdarg.h> + +#if !defined(__osf__) +#ifdef _IOSTRG +#define STRFLAG (_IOSTRG|_IOWRT) /* no _IOWRT: avoid stdio bug */ +#else +#if 0 +#define STRFLAG (_IOREAD) /* XXX: Assume svr4 stdio */ +#endif +#endif /* _IOSTRG */ +#endif /* __osf__ */ + +int +vsnprintf(char *s, size_t n, const char *fmt, va_list args) +{ +#ifdef STRFLAG + FILE fakebuf; + + fakebuf._flag = STRFLAG; + /* + * Some os's are char * _ptr, others are unsigned char *_ptr... + * We cast to void * to make everyone happy. + */ + fakebuf._ptr = (void *)s; + fakebuf._cnt = n-1; + fakebuf._file = -1; + _doprnt(fmt, args, &fakebuf); + fakebuf._cnt++; + putc('\0', &fakebuf); + if (fakebuf._cnt<0) + fakebuf._cnt = 0; + return (n-fakebuf._cnt-1); +#else + (void)vsprintf(s, fmt, args); + return strlen(s); +#endif +} + +int +snprintf(char *s, size_t n, const char *fmt, ...) +{ + va_list ap; + int rv; + + va_start(ap, fmt); + rv = vsnprintf(s, n, fmt, ap); + va_end(ap); + return rv; +} + +#if !defined(MAKE_NATIVE) && !defined(HAVE_STRFTIME) +size_t +strftime(char *buf, size_t len, const char *fmt, const struct tm *tm) +{ + static char months[][4] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" + }; + + size_t s; + char *b = buf; + + while (*fmt) { + if (len == 0) + return buf - b; + if (*fmt != '%') { + *buf++ = *fmt++; + len--; + continue; + } + switch (*fmt++) { + case '%': + *buf++ = '%'; + len--; + if (len == 0) return buf - b; + /*FALLTHROUGH*/ + case '\0': + *buf = '%'; + s = 1; + break; + case 'k': + s = snprintf(buf, len, "%d", tm->tm_hour); + break; + case 'M': + s = snprintf(buf, len, "%02d", tm->tm_min); + break; + case 'S': + s = snprintf(buf, len, "%02d", tm->tm_sec); + break; + case 'b': + if (tm->tm_mon >= 12) + return buf - b; + s = snprintf(buf, len, "%s", months[tm->tm_mon]); + break; + case 'd': + s = snprintf(buf, len, "%02d", tm->tm_mday); + break; + case 'Y': + s = snprintf(buf, len, "%d", 1900 + tm->tm_year); + break; + default: + s = snprintf(buf, len, "Unsupported format %c", + fmt[-1]); + break; + } + buf += s; + len -= s; + } +} +#endif +#endif diff --git a/buildrump.sh/src/usr.bin/make/var.c b/buildrump.sh/src/usr.bin/make/var.c new file mode 100644 index 00000000..39e357b8 --- /dev/null +++ b/buildrump.sh/src/usr.bin/make/var.c @@ -0,0 +1,4193 @@ +/* $NetBSD: var.c,v 1.192 2015/05/05 21:51:09 sjg Exp $ */ + +/* + * Copyright (c) 1988, 1989, 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. + */ + +/* + * Copyright (c) 1989 by Berkeley Softworks + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Adam de Boor. + * + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. 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. + */ + +#ifndef MAKE_NATIVE +static char rcsid[] = "$NetBSD: var.c,v 1.192 2015/05/05 21:51:09 sjg Exp $"; +#else +#include <sys/cdefs.h> +#ifndef lint +#if 0 +static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 3/19/94"; +#else +__RCSID("$NetBSD: var.c,v 1.192 2015/05/05 21:51:09 sjg Exp $"); +#endif +#endif /* not lint */ +#endif + +/*- + * var.c -- + * Variable-handling functions + * + * Interface: + * Var_Set Set the value of a variable in the given + * context. The variable is created if it doesn't + * yet exist. The value and variable name need not + * be preserved. + * + * Var_Append Append more characters to an existing variable + * in the given context. The variable needn't + * exist already -- it will be created if it doesn't. + * A space is placed between the old value and the + * new one. + * + * Var_Exists See if a variable exists. + * + * Var_Value Return the value of a variable in a context or + * NULL if the variable is undefined. + * + * Var_Subst Substitute named variable, or all variables if + * NULL in a string using + * the given context as the top-most one. If the + * third argument is non-zero, Parse_Error is + * called if any variables are undefined. + * + * Var_Parse Parse a variable expansion from a string and + * return the result and the number of characters + * consumed. + * + * Var_Delete Delete a variable in a context. + * + * Var_Init Initialize this module. + * + * Debugging: + * Var_Dump Print out all variables defined in the given + * context. + * + * XXX: There's a lot of duplication in these functions. + */ + +#include <sys/stat.h> +#ifndef NO_REGEX +#include <sys/types.h> +#include <regex.h> +#endif +#include <ctype.h> +#include <inttypes.h> +#include <stdlib.h> +#include <limits.h> +#include <time.h> + +#include "make.h" +#include "buf.h" +#include "dir.h" +#include "job.h" + +extern int makelevel; +/* + * This lets us tell if we have replaced the original environ + * (which we cannot free). + */ +char **savedEnv = NULL; + +/* + * This is a harmless return value for Var_Parse that can be used by Var_Subst + * to determine if there was an error in parsing -- easier than returning + * a flag, as things outside this module don't give a hoot. + */ +char var_Error[] = ""; + +/* + * Similar to var_Error, but returned when the 'errnum' flag for Var_Parse is + * set false. Why not just use a constant? Well, gcc likes to condense + * identical string instances... + */ +static char varNoError[] = ""; + +/* + * Internally, variables are contained in four different contexts. + * 1) the environment. They may not be changed. If an environment + * variable is appended-to, the result is placed in the global + * context. + * 2) the global context. Variables set in the Makefile are located in + * the global context. It is the penultimate context searched when + * substituting. + * 3) the command-line context. All variables set on the command line + * are placed in this context. They are UNALTERABLE once placed here. + * 4) the local context. Each target has associated with it a context + * list. On this list are located the structures describing such + * local variables as $(@) and $(*) + * The four contexts are searched in the reverse order from which they are + * listed. + */ +GNode *VAR_INTERNAL; /* variables from make itself */ +GNode *VAR_GLOBAL; /* variables from the makefile */ +GNode *VAR_CMD; /* variables defined on the command-line */ + +#define FIND_CMD 0x1 /* look in VAR_CMD when searching */ +#define FIND_GLOBAL 0x2 /* look in VAR_GLOBAL as well */ +#define FIND_ENV 0x4 /* look in the environment also */ + +typedef struct Var { + char *name; /* the variable's name */ + Buffer val; /* its value */ + int flags; /* miscellaneous status flags */ +#define VAR_IN_USE 1 /* Variable's value currently being used. + * Used to avoid recursion */ +#define VAR_FROM_ENV 2 /* Variable comes from the environment */ +#define VAR_JUNK 4 /* Variable is a junk variable that + * should be destroyed when done with + * it. Used by Var_Parse for undefined, + * modified variables */ +#define VAR_KEEP 8 /* Variable is VAR_JUNK, but we found + * a use for it in some modifier and + * the value is therefore valid */ +#define VAR_EXPORTED 16 /* Variable is exported */ +#define VAR_REEXPORT 32 /* Indicate if var needs re-export. + * This would be true if it contains $'s + */ +#define VAR_FROM_CMD 64 /* Variable came from command line */ +} Var; + +/* + * Exporting vars is expensive so skip it if we can + */ +#define VAR_EXPORTED_NONE 0 +#define VAR_EXPORTED_YES 1 +#define VAR_EXPORTED_ALL 2 +static int var_exportedVars = VAR_EXPORTED_NONE; +/* + * We pass this to Var_Export when doing the initial export + * or after updating an exported var. + */ +#define VAR_EXPORT_PARENT 1 + +/* Var*Pattern flags */ +#define VAR_SUB_GLOBAL 0x01 /* Apply substitution globally */ +#define VAR_SUB_ONE 0x02 /* Apply substitution to one word */ +#define VAR_SUB_MATCHED 0x04 /* There was a match */ +#define VAR_MATCH_START 0x08 /* Match at start of word */ +#define VAR_MATCH_END 0x10 /* Match at end of word */ +#define VAR_NOSUBST 0x20 /* don't expand vars in VarGetPattern */ + +/* Var_Set flags */ +#define VAR_NO_EXPORT 0x01 /* do not export */ + +typedef struct { + /* + * The following fields are set by Var_Parse() when it + * encounters modifiers that need to keep state for use by + * subsequent modifiers within the same variable expansion. + */ + Byte varSpace; /* Word separator in expansions */ + Boolean oneBigWord; /* TRUE if we will treat the variable as a + * single big word, even if it contains + * embedded spaces (as opposed to the + * usual behaviour of treating it as + * several space-separated words). */ +} Var_Parse_State; + +/* struct passed as 'void *' to VarSubstitute() for ":S/lhs/rhs/", + * to VarSYSVMatch() for ":lhs=rhs". */ +typedef struct { + const char *lhs; /* String to match */ + int leftLen; /* Length of string */ + const char *rhs; /* Replacement string (w/ &'s removed) */ + int rightLen; /* Length of replacement */ + int flags; +} VarPattern; + +/* struct passed as 'void *' to VarLoopExpand() for ":@tvar@str@" */ +typedef struct { + GNode *ctxt; /* variable context */ + char *tvar; /* name of temp var */ + int tvarLen; + char *str; /* string to expand */ + int strLen; + int errnum; /* errnum for not defined */ +} VarLoop_t; + +#ifndef NO_REGEX +/* struct passed as 'void *' to VarRESubstitute() for ":C///" */ +typedef struct { + regex_t re; + int nsub; + regmatch_t *matches; + char *replace; + int flags; +} VarREPattern; +#endif + +/* struct passed to VarSelectWords() for ":[start..end]" */ +typedef struct { + int start; /* first word to select */ + int end; /* last word to select */ +} VarSelectWords_t; + +static Var *VarFind(const char *, GNode *, int); +static void VarAdd(const char *, const char *, GNode *); +static Boolean VarHead(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarTail(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarSuffix(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarRoot(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#ifdef SYSVVARSUB +static Boolean VarSYSVMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#endif +static Boolean VarNoMatch(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#ifndef NO_REGEX +static void VarREError(int, regex_t *, const char *); +static Boolean VarRESubstitute(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +#endif +static Boolean VarSubstitute(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static Boolean VarLoopExpand(GNode *, Var_Parse_State *, + char *, Boolean, Buffer *, void *); +static char *VarGetPattern(GNode *, Var_Parse_State *, + int, const char **, int, int *, int *, + VarPattern *); +static char *VarQuote(char *); +static char *VarHash(char *); +static char *VarModify(GNode *, Var_Parse_State *, + const char *, + Boolean (*)(GNode *, Var_Parse_State *, char *, Boolean, Buffer *, void *), + void *); +static char *VarOrder(const char *, const char); +static char *VarUniq(const char *); +static int VarWordCompare(const void *, const void *); +static void VarPrintVar(void *); + +#define BROPEN '{' +#define BRCLOSE '}' +#define PROPEN '(' +#define PRCLOSE ')' + +/*- + *----------------------------------------------------------------------- + * VarFind -- + * Find the given variable in the given context and any other contexts + * indicated. + * + * Input: + * name name to find + * ctxt context in which to find it + * flags FIND_GLOBAL set means to look in the + * VAR_GLOBAL context as well. FIND_CMD set means + * to look in the VAR_CMD context also. FIND_ENV + * set means to look in the environment + * + * Results: + * A pointer to the structure describing the desired variable or + * NULL if the variable does not exist. + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +static Var * +VarFind(const char *name, GNode *ctxt, int flags) +{ + Hash_Entry *var; + Var *v; + + /* + * If the variable name begins with a '.', it could very well be one of + * the local ones. We check the name against all the local variables + * and substitute the short version in for 'name' if it matches one of + * them. + */ + if (*name == '.' && isupper((unsigned char) name[1])) + switch (name[1]) { + case 'A': + if (!strcmp(name, ".ALLSRC")) + name = ALLSRC; + if (!strcmp(name, ".ARCHIVE")) + name = ARCHIVE; + break; + case 'I': + if (!strcmp(name, ".IMPSRC")) + name = IMPSRC; + break; + case 'M': + if (!strcmp(name, ".MEMBER")) + name = MEMBER; + break; + case 'O': + if (!strcmp(name, ".OODATE")) + name = OODATE; + break; + case 'P': + if (!strcmp(name, ".PREFIX")) + name = PREFIX; + break; + case 'T': + if (!strcmp(name, ".TARGET")) + name = TARGET; + break; + } +#ifdef notyet + /* for compatibility with gmake */ + if (name[0] == '^' && name[1] == '\0') + name = ALLSRC; +#endif + + /* + * First look for the variable in the given context. If it's not there, + * look for it in VAR_CMD, VAR_GLOBAL and the environment, in that order, + * depending on the FIND_* flags in 'flags' + */ + var = Hash_FindEntry(&ctxt->context, name); + + if ((var == NULL) && (flags & FIND_CMD) && (ctxt != VAR_CMD)) { + var = Hash_FindEntry(&VAR_CMD->context, name); + } + if (!checkEnvFirst && (var == NULL) && (flags & FIND_GLOBAL) && + (ctxt != VAR_GLOBAL)) + { + var = Hash_FindEntry(&VAR_GLOBAL->context, name); + if ((var == NULL) && (ctxt != VAR_INTERNAL)) { + /* VAR_INTERNAL is subordinate to VAR_GLOBAL */ + var = Hash_FindEntry(&VAR_INTERNAL->context, name); + } + } + if ((var == NULL) && (flags & FIND_ENV)) { + char *env; + + if ((env = getenv(name)) != NULL) { + int len; + + v = bmake_malloc(sizeof(Var)); + v->name = bmake_strdup(name); + + len = strlen(env); + + Buf_Init(&v->val, len + 1); + Buf_AddBytes(&v->val, len, env); + + v->flags = VAR_FROM_ENV; + return (v); + } else if (checkEnvFirst && (flags & FIND_GLOBAL) && + (ctxt != VAR_GLOBAL)) + { + var = Hash_FindEntry(&VAR_GLOBAL->context, name); + if ((var == NULL) && (ctxt != VAR_INTERNAL)) { + var = Hash_FindEntry(&VAR_INTERNAL->context, name); + } + if (var == NULL) { + return NULL; + } else { + return ((Var *)Hash_GetValue(var)); + } + } else { + return NULL; + } + } else if (var == NULL) { + return NULL; + } else { + return ((Var *)Hash_GetValue(var)); + } +} + +/*- + *----------------------------------------------------------------------- + * VarFreeEnv -- + * If the variable is an environment variable, free it + * + * Input: + * v the variable + * destroy true if the value buffer should be destroyed. + * + * Results: + * 1 if it is an environment variable 0 ow. + * + * Side Effects: + * The variable is free'ed if it is an environent variable. + *----------------------------------------------------------------------- + */ +static Boolean +VarFreeEnv(Var *v, Boolean destroy) +{ + if ((v->flags & VAR_FROM_ENV) == 0) + return FALSE; + free(v->name); + Buf_Destroy(&v->val, destroy); + free(v); + return TRUE; +} + +/*- + *----------------------------------------------------------------------- + * VarAdd -- + * Add a new variable of name name and value val to the given context + * + * Input: + * name name of variable to add + * val value to set it to + * ctxt context in which to set it + * + * Results: + * None + * + * Side Effects: + * The new variable is placed at the front of the given context + * The name and val arguments are duplicated so they may + * safely be freed. + *----------------------------------------------------------------------- + */ +static void +VarAdd(const char *name, const char *val, GNode *ctxt) +{ + Var *v; + int len; + Hash_Entry *h; + + v = bmake_malloc(sizeof(Var)); + + len = val ? strlen(val) : 0; + Buf_Init(&v->val, len+1); + Buf_AddBytes(&v->val, len, val); + + v->flags = 0; + + h = Hash_CreateEntry(&ctxt->context, name, NULL); + Hash_SetValue(h, v); + v->name = h->name; + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); + } +} + +/*- + *----------------------------------------------------------------------- + * Var_Delete -- + * Remove a variable from a context. + * + * Results: + * None. + * + * Side Effects: + * The Var structure is removed and freed. + * + *----------------------------------------------------------------------- + */ +void +Var_Delete(const char *name, GNode *ctxt) +{ + Hash_Entry *ln; + char *cp; + + if (strchr(name, '$')) { + cp = Var_Subst(NULL, name, VAR_GLOBAL, 0); + } else { + cp = (char *)name; + } + ln = Hash_FindEntry(&ctxt->context, cp); + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:delete %s%s\n", + ctxt->name, cp, ln ? "" : " (not found)"); + } + if (cp != name) { + free(cp); + } + if (ln != NULL) { + Var *v; + + v = (Var *)Hash_GetValue(ln); + if ((v->flags & VAR_EXPORTED)) { + unsetenv(v->name); + } + if (strcmp(MAKE_EXPORTED, v->name) == 0) { + var_exportedVars = VAR_EXPORTED_NONE; + } + if (v->name != ln->name) + free(v->name); + Hash_DeleteEntry(&ctxt->context, ln); + Buf_Destroy(&v->val, TRUE); + free(v); + } +} + + +/* + * Export a var. + * We ignore make internal variables (those which start with '.') + * Also we jump through some hoops to avoid calling setenv + * more than necessary since it can leak. + * We only manipulate flags of vars if 'parent' is set. + */ +static int +Var_Export1(const char *name, int parent) +{ + char tmp[BUFSIZ]; + Var *v; + char *val = NULL; + int n; + + if (*name == '.') + return 0; /* skip internals */ + if (!name[1]) { + /* + * A single char. + * If it is one of the vars that should only appear in + * local context, skip it, else we can get Var_Subst + * into a loop. + */ + switch (name[0]) { + case '@': + case '%': + case '*': + case '!': + return 0; + } + } + v = VarFind(name, VAR_GLOBAL, 0); + if (v == NULL) { + return 0; + } + if (!parent && + (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { + return 0; /* nothing to do */ + } + val = Buf_GetAll(&v->val, NULL); + if (strchr(val, '$')) { + if (parent) { + /* + * Flag this as something we need to re-export. + * No point actually exporting it now though, + * the child can do it at the last minute. + */ + v->flags |= (VAR_EXPORTED|VAR_REEXPORT); + return 1; + } + if (v->flags & VAR_IN_USE) { + /* + * We recursed while exporting in a child. + * This isn't going to end well, just skip it. + */ + return 0; + } + n = snprintf(tmp, sizeof(tmp), "${%s}", name); + if (n < (int)sizeof(tmp)) { + val = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + setenv(name, val, 1); + free(val); + } + } else { + if (parent) { + v->flags &= ~VAR_REEXPORT; /* once will do */ + } + if (parent || !(v->flags & VAR_EXPORTED)) { + setenv(name, val, 1); + } + } + /* + * This is so Var_Set knows to call Var_Export again... + */ + if (parent) { + v->flags |= VAR_EXPORTED; + } + return 1; +} + +/* + * This gets called from our children. + */ +void +Var_ExportVars(void) +{ + char tmp[BUFSIZ]; + Hash_Entry *var; + Hash_Search state; + Var *v; + char *val; + int n; + + /* + * Several make's support this sort of mechanism for tracking + * recursion - but each uses a different name. + * We allow the makefiles to update MAKELEVEL and ensure + * children see a correctly incremented value. + */ + snprintf(tmp, sizeof(tmp), "%d", makelevel + 1); + setenv(MAKE_LEVEL_ENV, tmp, 1); + + if (VAR_EXPORTED_NONE == var_exportedVars) + return; + + if (VAR_EXPORTED_ALL == var_exportedVars) { + /* + * Ouch! This is crazy... + */ + for (var = Hash_EnumFirst(&VAR_GLOBAL->context, &state); + var != NULL; + var = Hash_EnumNext(&state)) { + v = (Var *)Hash_GetValue(var); + Var_Export1(v->name, 0); + } + return; + } + /* + * We have a number of exported vars, + */ + n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); + if (n < (int)sizeof(tmp)) { + char **av; + char *as; + int ac; + int i; + + val = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + av = brk_string(val, &ac, FALSE, &as); + for (i = 0; i < ac; i++) { + Var_Export1(av[i], 0); + } + free(val); + free(as); + free(av); + } +} + +/* + * This is called when .export is seen or + * .MAKE.EXPORTED is modified. + * It is also called when any exported var is modified. + */ +void +Var_Export(char *str, int isExport) +{ + char *name; + char *val; + char **av; + char *as; + int track; + int ac; + int i; + + if (isExport && (!str || !str[0])) { + var_exportedVars = VAR_EXPORTED_ALL; /* use with caution! */ + return; + } + + if (strncmp(str, "-env", 4) == 0) { + track = 0; + str += 4; + } else { + track = VAR_EXPORT_PARENT; + } + val = Var_Subst(NULL, str, VAR_GLOBAL, 0); + av = brk_string(val, &ac, FALSE, &as); + for (i = 0; i < ac; i++) { + name = av[i]; + if (!name[1]) { + /* + * A single char. + * If it is one of the vars that should only appear in + * local context, skip it, else we can get Var_Subst + * into a loop. + */ + switch (name[0]) { + case '@': + case '%': + case '*': + case '!': + continue; + } + } + if (Var_Export1(name, track)) { + if (VAR_EXPORTED_ALL != var_exportedVars) + var_exportedVars = VAR_EXPORTED_YES; + if (isExport && track) { + Var_Append(MAKE_EXPORTED, name, VAR_GLOBAL); + } + } + } + free(val); + free(as); + free(av); +} + + +/* + * This is called when .unexport[-env] is seen. + */ +extern char **environ; + +void +Var_UnExport(char *str) +{ + char tmp[BUFSIZ]; + char *vlist; + char *cp; + Boolean unexport_env; + int n; + + if (!str || !str[0]) { + return; /* assert? */ + } + + vlist = NULL; + + str += 8; + unexport_env = (strncmp(str, "-env", 4) == 0); + if (unexport_env) { + char **newenv; + + cp = getenv(MAKE_LEVEL_ENV); /* we should preserve this */ + if (environ == savedEnv) { + /* we have been here before! */ + newenv = bmake_realloc(environ, 2 * sizeof(char *)); + } else { + if (savedEnv) { + free(savedEnv); + savedEnv = NULL; + } + newenv = bmake_malloc(2 * sizeof(char *)); + } + if (!newenv) + return; + /* Note: we cannot safely free() the original environ. */ + environ = savedEnv = newenv; + newenv[0] = NULL; + newenv[1] = NULL; + setenv(MAKE_LEVEL_ENV, cp, 1); + } else { + for (; *str != '\n' && isspace((unsigned char) *str); str++) + continue; + if (str[0] && str[0] != '\n') { + vlist = str; + } + } + + if (!vlist) { + /* Using .MAKE.EXPORTED */ + n = snprintf(tmp, sizeof(tmp), "${" MAKE_EXPORTED ":O:u}"); + if (n < (int)sizeof(tmp)) { + vlist = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + } + } + if (vlist) { + Var *v; + char **av; + char *as; + int ac; + int i; + + av = brk_string(vlist, &ac, FALSE, &as); + for (i = 0; i < ac; i++) { + v = VarFind(av[i], VAR_GLOBAL, 0); + if (!v) + continue; + if (!unexport_env && + (v->flags & (VAR_EXPORTED|VAR_REEXPORT)) == VAR_EXPORTED) { + unsetenv(v->name); + } + v->flags &= ~(VAR_EXPORTED|VAR_REEXPORT); + /* + * If we are unexporting a list, + * remove each one from .MAKE.EXPORTED. + * If we are removing them all, + * just delete .MAKE.EXPORTED below. + */ + if (vlist == str) { + n = snprintf(tmp, sizeof(tmp), + "${" MAKE_EXPORTED ":N%s}", v->name); + if (n < (int)sizeof(tmp)) { + cp = Var_Subst(NULL, tmp, VAR_GLOBAL, 0); + Var_Set(MAKE_EXPORTED, cp, VAR_GLOBAL, 0); + free(cp); + } + } + } + free(as); + free(av); + if (vlist != str) { + Var_Delete(MAKE_EXPORTED, VAR_GLOBAL); + free(vlist); + } + } +} + +/*- + *----------------------------------------------------------------------- + * Var_Set -- + * Set the variable name to the value val in the given context. + * + * Input: + * name name of variable to set + * val value to give to the variable + * ctxt context in which to set it + * + * Results: + * None. + * + * Side Effects: + * If the variable doesn't yet exist, a new record is created for it. + * Else the old value is freed and the new one stuck in its place + * + * Notes: + * The variable is searched for only in its context before being + * created in that context. I.e. if the context is VAR_GLOBAL, + * only VAR_GLOBAL->context is searched. Likewise if it is VAR_CMD, only + * VAR_CMD->context is searched. This is done to avoid the literally + * thousands of unnecessary strcmp's that used to be done to + * set, say, $(@) or $(<). + * If the context is VAR_GLOBAL though, we check if the variable + * was set in VAR_CMD from the command line and skip it if so. + *----------------------------------------------------------------------- + */ +void +Var_Set(const char *name, const char *val, GNode *ctxt, int flags) +{ + Var *v; + char *expanded_name = NULL; + + /* + * We only look for a variable in the given context since anything set + * here will override anything in a lower context, so there's not much + * point in searching them all just to save a bit of memory... + */ + if (strchr(name, '$') != NULL) { + expanded_name = Var_Subst(NULL, name, ctxt, 0); + if (expanded_name[0] == 0) { + if (DEBUG(VAR)) { + fprintf(debug_file, "Var_Set(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + name, val); + } + free(expanded_name); + return; + } + name = expanded_name; + } + if (ctxt == VAR_GLOBAL) { + v = VarFind(name, VAR_CMD, 0); + if (v != NULL) { + if ((v->flags & VAR_FROM_CMD)) { + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:%s = %s ignored!\n", ctxt->name, name, val); + } + goto out; + } + VarFreeEnv(v, TRUE); + } + } + v = VarFind(name, ctxt, 0); + if (v == NULL) { + if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { + /* + * This var would normally prevent the same name being added + * to VAR_GLOBAL, so delete it from there if needed. + * Otherwise -V name may show the wrong value. + */ + Var_Delete(name, VAR_GLOBAL); + } + VarAdd(name, val, ctxt); + } else { + Buf_Empty(&v->val); + Buf_AddBytes(&v->val, strlen(val), val); + + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, val); + } + if ((v->flags & VAR_EXPORTED)) { + Var_Export1(name, VAR_EXPORT_PARENT); + } + } + /* + * Any variables given on the command line are automatically exported + * to the environment (as per POSIX standard) + */ + if (ctxt == VAR_CMD && (flags & VAR_NO_EXPORT) == 0) { + if (v == NULL) { + /* we just added it */ + v = VarFind(name, ctxt, 0); + } + if (v != NULL) + v->flags |= VAR_FROM_CMD; + /* + * If requested, don't export these in the environment + * individually. We still put them in MAKEOVERRIDES so + * that the command-line settings continue to override + * Makefile settings. + */ + if (varNoExportEnv != TRUE) + setenv(name, val, 1); + + Var_Append(MAKEOVERRIDES, name, VAR_GLOBAL); + } + + out: + if (expanded_name != NULL) + free(expanded_name); + if (v != NULL) + VarFreeEnv(v, TRUE); +} + +/*- + *----------------------------------------------------------------------- + * Var_Append -- + * The variable of the given name has the given value appended to it in + * the given context. + * + * Input: + * name name of variable to modify + * val String to append to it + * ctxt Context in which this should occur + * + * Results: + * None + * + * Side Effects: + * If the variable doesn't exist, it is created. Else the strings + * are concatenated (with a space in between). + * + * Notes: + * Only if the variable is being sought in the global context is the + * environment searched. + * XXX: Knows its calling circumstances in that if called with ctxt + * an actual target, it will only search that context since only + * a local variable could be being appended to. This is actually + * a big win and must be tolerated. + *----------------------------------------------------------------------- + */ +void +Var_Append(const char *name, const char *val, GNode *ctxt) +{ + Var *v; + Hash_Entry *h; + char *expanded_name = NULL; + + if (strchr(name, '$') != NULL) { + expanded_name = Var_Subst(NULL, name, ctxt, 0); + if (expanded_name[0] == 0) { + if (DEBUG(VAR)) { + fprintf(debug_file, "Var_Append(\"%s\", \"%s\", ...) " + "name expands to empty string - ignored\n", + name, val); + } + free(expanded_name); + return; + } + name = expanded_name; + } + + v = VarFind(name, ctxt, (ctxt == VAR_GLOBAL) ? FIND_ENV : 0); + + if (v == NULL) { + VarAdd(name, val, ctxt); + } else { + Buf_AddByte(&v->val, ' '); + Buf_AddBytes(&v->val, strlen(val), val); + + if (DEBUG(VAR)) { + fprintf(debug_file, "%s:%s = %s\n", ctxt->name, name, + Buf_GetAll(&v->val, NULL)); + } + + if (v->flags & VAR_FROM_ENV) { + /* + * If the original variable came from the environment, we + * have to install it in the global context (we could place + * it in the environment, but then we should provide a way to + * export other variables...) + */ + v->flags &= ~VAR_FROM_ENV; + h = Hash_CreateEntry(&ctxt->context, name, NULL); + Hash_SetValue(h, v); + } + } + if (expanded_name != NULL) + free(expanded_name); +} + +/*- + *----------------------------------------------------------------------- + * Var_Exists -- + * See if the given variable exists. + * + * Input: + * name Variable to find + * ctxt Context in which to start search + * + * Results: + * TRUE if it does, FALSE if it doesn't + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +Boolean +Var_Exists(const char *name, GNode *ctxt) +{ + Var *v; + char *cp; + + if ((cp = strchr(name, '$')) != NULL) { + cp = Var_Subst(NULL, name, ctxt, FALSE); + } + v = VarFind(cp ? cp : name, ctxt, FIND_CMD|FIND_GLOBAL|FIND_ENV); + if (cp != NULL) { + free(cp); + } + if (v == NULL) { + return(FALSE); + } else { + (void)VarFreeEnv(v, TRUE); + } + return(TRUE); +} + +/*- + *----------------------------------------------------------------------- + * Var_Value -- + * Return the value of the named variable in the given context + * + * Input: + * name name to find + * ctxt context in which to search for it + * + * Results: + * The value if the variable exists, NULL if it doesn't + * + * Side Effects: + * None + *----------------------------------------------------------------------- + */ +char * +Var_Value(const char *name, GNode *ctxt, char **frp) +{ + Var *v; + + v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + *frp = NULL; + if (v != NULL) { + char *p = (Buf_GetAll(&v->val, NULL)); + if (VarFreeEnv(v, FALSE)) + *frp = p; + return p; + } else { + return NULL; + } +} + +/*- + *----------------------------------------------------------------------- + * VarHead -- + * Remove the tail of the given word and place the result in the given + * buffer. + * + * Input: + * word Word to trim + * addSpace True if need to add a space to the buffer + * before sticking in the head + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The trimmed word is added to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarHead(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *dummy) +{ + char *slash; + + slash = strrchr(word, '/'); + if (slash != NULL) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + *slash = '\0'; + Buf_AddBytes(buf, strlen(word), word); + *slash = '/'; + return (TRUE); + } else { + /* + * If no directory part, give . (q.v. the POSIX standard) + */ + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + Buf_AddByte(buf, '.'); + } + return(dummy ? TRUE : TRUE); +} + +/*- + *----------------------------------------------------------------------- + * VarTail -- + * Remove the head of the given word and place the result in the given + * buffer. + * + * Input: + * word Word to trim + * addSpace True if need to add a space to the buffer + * before adding the tail + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The trimmed word is added to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarTail(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *dummy) +{ + char *slash; + + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + + slash = strrchr(word, '/'); + if (slash != NULL) { + *slash++ = '\0'; + Buf_AddBytes(buf, strlen(slash), slash); + slash[-1] = '/'; + } else { + Buf_AddBytes(buf, strlen(word), word); + } + return (dummy ? TRUE : TRUE); +} + +/*- + *----------------------------------------------------------------------- + * VarSuffix -- + * Place the suffix of the given word in the given buffer. + * + * Input: + * word Word to trim + * addSpace TRUE if need to add a space before placing the + * suffix in the buffer + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The suffix from the word is placed in the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarSuffix(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *dummy) +{ + char *dot; + + dot = strrchr(word, '.'); + if (dot != NULL) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + *dot++ = '\0'; + Buf_AddBytes(buf, strlen(dot), dot); + dot[-1] = '.'; + addSpace = TRUE; + } + return (dummy ? addSpace : addSpace); +} + +/*- + *----------------------------------------------------------------------- + * VarRoot -- + * Remove the suffix of the given word and place the result in the + * buffer. + * + * Input: + * word Word to trim + * addSpace TRUE if need to add a space to the buffer + * before placing the root in it + * buf Buffer in which to store it + * + * Results: + * TRUE if characters were added to the buffer (a space needs to be + * added to the buffer before the next word). + * + * Side Effects: + * The trimmed word is added to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarRoot(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *dummy) +{ + char *dot; + + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + + dot = strrchr(word, '.'); + if (dot != NULL) { + *dot = '\0'; + Buf_AddBytes(buf, strlen(word), word); + *dot = '.'; + } else { + Buf_AddBytes(buf, strlen(word), word); + } + return (dummy ? TRUE : TRUE); +} + +/*- + *----------------------------------------------------------------------- + * VarMatch -- + * Place the word in the buffer if it matches the given pattern. + * Callback function for VarModify to implement the :M modifier. + * + * Input: + * word Word to examine + * addSpace TRUE if need to add a space to the buffer + * before adding the word, if it matches + * buf Buffer in which to store it + * pattern Pattern the word must match + * + * Results: + * TRUE if a space should be placed in the buffer before the next + * word. + * + * Side Effects: + * The word may be copied to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *pattern) +{ + if (DEBUG(VAR)) + fprintf(debug_file, "VarMatch [%s] [%s]\n", word, (char *)pattern); + if (Str_Match(word, (char *)pattern)) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, strlen(word), word); + } + return(addSpace); +} + +#ifdef SYSVVARSUB +/*- + *----------------------------------------------------------------------- + * VarSYSVMatch -- + * Place the word in the buffer if it matches the given pattern. + * Callback function for VarModify to implement the System V % + * modifiers. + * + * Input: + * word Word to examine + * addSpace TRUE if need to add a space to the buffer + * before adding the word, if it matches + * buf Buffer in which to store it + * patp Pattern the word must match + * + * Results: + * TRUE if a space should be placed in the buffer before the next + * word. + * + * Side Effects: + * The word may be copied to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarSYSVMatch(GNode *ctx, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *patp) +{ + int len; + char *ptr; + VarPattern *pat = (VarPattern *)patp; + char *varexp; + + if (addSpace && vpstate->varSpace) + Buf_AddByte(buf, vpstate->varSpace); + + addSpace = TRUE; + + if ((ptr = Str_SYSVMatch(word, pat->lhs, &len)) != NULL) { + varexp = Var_Subst(NULL, pat->rhs, ctx, 0); + Str_SYSVSubst(buf, varexp, ptr, len); + free(varexp); + } else { + Buf_AddBytes(buf, strlen(word), word); + } + + return(addSpace); +} +#endif + + +/*- + *----------------------------------------------------------------------- + * VarNoMatch -- + * Place the word in the buffer if it doesn't match the given pattern. + * Callback function for VarModify to implement the :N modifier. + * + * Input: + * word Word to examine + * addSpace TRUE if need to add a space to the buffer + * before adding the word, if it matches + * buf Buffer in which to store it + * pattern Pattern the word must match + * + * Results: + * TRUE if a space should be placed in the buffer before the next + * word. + * + * Side Effects: + * The word may be copied to the buffer. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarNoMatch(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *pattern) +{ + if (!Str_Match(word, (char *)pattern)) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, strlen(word), word); + } + return(addSpace); +} + + +/*- + *----------------------------------------------------------------------- + * VarSubstitute -- + * Perform a string-substitution on the given word, placing the + * result in the passed buffer. + * + * Input: + * word Word to modify + * addSpace True if space should be added before + * other characters + * buf Buffer for result + * patternp Pattern for substitution + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarSubstitute(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *patternp) +{ + int wordLen; /* Length of word */ + char *cp; /* General pointer */ + VarPattern *pattern = (VarPattern *)patternp; + + wordLen = strlen(word); + if ((pattern->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) != + (VAR_SUB_ONE|VAR_SUB_MATCHED)) { + /* + * Still substituting -- break it down into simple anchored cases + * and if none of them fits, perform the general substitution case. + */ + if ((pattern->flags & VAR_MATCH_START) && + (strncmp(word, pattern->lhs, pattern->leftLen) == 0)) { + /* + * Anchored at start and beginning of word matches pattern + */ + if ((pattern->flags & VAR_MATCH_END) && + (wordLen == pattern->leftLen)) { + /* + * Also anchored at end and matches to the end (word + * is same length as pattern) add space and rhs only + * if rhs is non-null. + */ + if (pattern->rightLen != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); + } + pattern->flags |= VAR_SUB_MATCHED; + } else if (pattern->flags & VAR_MATCH_END) { + /* + * Doesn't match to end -- copy word wholesale + */ + goto nosub; + } else { + /* + * Matches at start but need to copy in trailing characters + */ + if ((pattern->rightLen + wordLen - pattern->leftLen) != 0){ + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + } + Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); + Buf_AddBytes(buf, wordLen - pattern->leftLen, + (word + pattern->leftLen)); + pattern->flags |= VAR_SUB_MATCHED; + } + } else if (pattern->flags & VAR_MATCH_START) { + /* + * Had to match at start of word and didn't -- copy whole word. + */ + goto nosub; + } else if (pattern->flags & VAR_MATCH_END) { + /* + * Anchored at end, Find only place match could occur (leftLen + * characters from the end of the word) and see if it does. Note + * that because the $ will be left at the end of the lhs, we have + * to use strncmp. + */ + cp = word + (wordLen - pattern->leftLen); + if ((cp >= word) && + (strncmp(cp, pattern->lhs, pattern->leftLen) == 0)) { + /* + * Match found. If we will place characters in the buffer, + * add a space before hand as indicated by addSpace, then + * stuff in the initial, unmatched part of the word followed + * by the right-hand-side. + */ + if (((cp - word) + pattern->rightLen) != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + } + Buf_AddBytes(buf, cp - word, word); + Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); + pattern->flags |= VAR_SUB_MATCHED; + } else { + /* + * Had to match at end and didn't. Copy entire word. + */ + goto nosub; + } + } else { + /* + * Pattern is unanchored: search for the pattern in the word using + * String_FindSubstring, copying unmatched portions and the + * right-hand-side for each match found, handling non-global + * substitutions correctly, etc. When the loop is done, any + * remaining part of the word (word and wordLen are adjusted + * accordingly through the loop) is copied straight into the + * buffer. + * addSpace is set FALSE as soon as a space is added to the + * buffer. + */ + Boolean done; + int origSize; + + done = FALSE; + origSize = Buf_Size(buf); + while (!done) { + cp = Str_FindSubstring(word, pattern->lhs); + if (cp != NULL) { + if (addSpace && (((cp - word) + pattern->rightLen) != 0)){ + Buf_AddByte(buf, vpstate->varSpace); + addSpace = FALSE; + } + Buf_AddBytes(buf, cp-word, word); + Buf_AddBytes(buf, pattern->rightLen, pattern->rhs); + wordLen -= (cp - word) + pattern->leftLen; + word = cp + pattern->leftLen; + if (wordLen == 0) { + done = TRUE; + } + if ((pattern->flags & VAR_SUB_GLOBAL) == 0) { + done = TRUE; + } + pattern->flags |= VAR_SUB_MATCHED; + } else { + done = TRUE; + } + } + if (wordLen != 0) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + Buf_AddBytes(buf, wordLen, word); + } + /* + * If added characters to the buffer, need to add a space + * before we add any more. If we didn't add any, just return + * the previous value of addSpace. + */ + return ((Buf_Size(buf) != origSize) || addSpace); + } + return (addSpace); + } + nosub: + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + Buf_AddBytes(buf, wordLen, word); + return(TRUE); +} + +#ifndef NO_REGEX +/*- + *----------------------------------------------------------------------- + * VarREError -- + * Print the error caused by a regcomp or regexec call. + * + * Results: + * None. + * + * Side Effects: + * An error gets printed. + * + *----------------------------------------------------------------------- + */ +static void +VarREError(int errnum, regex_t *pat, const char *str) +{ + char *errbuf; + int errlen; + + errlen = regerror(errnum, pat, 0, 0); + errbuf = bmake_malloc(errlen); + regerror(errnum, pat, errbuf, errlen); + Error("%s: %s", str, errbuf); + free(errbuf); +} + + +/*- + *----------------------------------------------------------------------- + * VarRESubstitute -- + * Perform a regex substitution on the given word, placing the + * result in the passed buffer. + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarRESubstitute(GNode *ctx MAKE_ATTR_UNUSED, + Var_Parse_State *vpstate MAKE_ATTR_UNUSED, + char *word, Boolean addSpace, Buffer *buf, + void *patternp) +{ + VarREPattern *pat; + int xrv; + char *wp; + char *rp; + int added; + int flags = 0; + +#define MAYBE_ADD_SPACE() \ + if (addSpace && !added) \ + Buf_AddByte(buf, ' '); \ + added = 1 + + added = 0; + wp = word; + pat = patternp; + + if ((pat->flags & (VAR_SUB_ONE|VAR_SUB_MATCHED)) == + (VAR_SUB_ONE|VAR_SUB_MATCHED)) + xrv = REG_NOMATCH; + else { + tryagain: + xrv = regexec(&pat->re, wp, pat->nsub, pat->matches, flags); + } + + switch (xrv) { + case 0: + pat->flags |= VAR_SUB_MATCHED; + if (pat->matches[0].rm_so > 0) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, pat->matches[0].rm_so, wp); + } + + for (rp = pat->replace; *rp; rp++) { + if ((*rp == '\\') && ((rp[1] == '&') || (rp[1] == '\\'))) { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf,rp[1]); + rp++; + } + else if ((*rp == '&') || + ((*rp == '\\') && isdigit((unsigned char)rp[1]))) { + int n; + const char *subbuf; + int sublen; + char errstr[3]; + + if (*rp == '&') { + n = 0; + errstr[0] = '&'; + errstr[1] = '\0'; + } else { + n = rp[1] - '0'; + errstr[0] = '\\'; + errstr[1] = rp[1]; + errstr[2] = '\0'; + rp++; + } + + if (n > pat->nsub) { + Error("No subexpression %s", &errstr[0]); + subbuf = ""; + sublen = 0; + } else if ((pat->matches[n].rm_so == -1) && + (pat->matches[n].rm_eo == -1)) { + Error("No match for subexpression %s", &errstr[0]); + subbuf = ""; + sublen = 0; + } else { + subbuf = wp + pat->matches[n].rm_so; + sublen = pat->matches[n].rm_eo - pat->matches[n].rm_so; + } + + if (sublen > 0) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, sublen, subbuf); + } + } else { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf, *rp); + } + } + wp += pat->matches[0].rm_eo; + if (pat->flags & VAR_SUB_GLOBAL) { + flags |= REG_NOTBOL; + if (pat->matches[0].rm_so == 0 && pat->matches[0].rm_eo == 0) { + MAYBE_ADD_SPACE(); + Buf_AddByte(buf, *wp); + wp++; + + } + if (*wp) + goto tryagain; + } + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf, strlen(wp), wp); + } + break; + default: + VarREError(xrv, &pat->re, "Unexpected regex error"); + /* fall through */ + case REG_NOMATCH: + if (*wp) { + MAYBE_ADD_SPACE(); + Buf_AddBytes(buf,strlen(wp),wp); + } + break; + } + return(addSpace||added); +} +#endif + + + +/*- + *----------------------------------------------------------------------- + * VarLoopExpand -- + * Implements the :@<temp>@<string>@ modifier of ODE make. + * We set the temp variable named in pattern.lhs to word and expand + * pattern.rhs storing the result in the passed buffer. + * + * Input: + * word Word to modify + * addSpace True if space should be added before + * other characters + * buf Buffer for result + * pattern Datafor substitution + * + * Results: + * TRUE if a space is needed before more characters are added. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static Boolean +VarLoopExpand(GNode *ctx MAKE_ATTR_UNUSED, + Var_Parse_State *vpstate MAKE_ATTR_UNUSED, + char *word, Boolean addSpace, Buffer *buf, + void *loopp) +{ + VarLoop_t *loop = (VarLoop_t *)loopp; + char *s; + int slen; + + if (word && *word) { + Var_Set(loop->tvar, word, loop->ctxt, VAR_NO_EXPORT); + s = Var_Subst(NULL, loop->str, loop->ctxt, loop->errnum); + if (s != NULL && *s != '\0') { + if (addSpace && *s != '\n') + Buf_AddByte(buf, ' '); + Buf_AddBytes(buf, (slen = strlen(s)), s); + addSpace = (slen > 0 && s[slen - 1] != '\n'); + free(s); + } + } + return addSpace; +} + + +/*- + *----------------------------------------------------------------------- + * VarSelectWords -- + * Implements the :[start..end] modifier. + * This is a special case of VarModify since we want to be able + * to scan the list backwards if start > end. + * + * Input: + * str String whose words should be trimmed + * seldata words to select + * + * Results: + * A string of all the words selected. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarSelectWords(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + const char *str, VarSelectWords_t *seldata) +{ + Buffer buf; /* Buffer for the new string */ + Boolean addSpace; /* TRUE if need to add a space to the + * buffer before adding the trimmed + * word */ + char **av; /* word list */ + char *as; /* word list memory */ + int ac, i; + int start, end, step; + + Buf_Init(&buf, 0); + addSpace = FALSE; + + if (vpstate->oneBigWord) { + /* fake what brk_string() would do if there were only one word */ + ac = 1; + av = bmake_malloc((ac + 1) * sizeof(char *)); + as = bmake_strdup(str); + av[0] = as; + av[1] = NULL; + } else { + av = brk_string(str, &ac, FALSE, &as); + } + + /* + * Now sanitize seldata. + * If seldata->start or seldata->end are negative, convert them to + * the positive equivalents (-1 gets converted to argc, -2 gets + * converted to (argc-1), etc.). + */ + if (seldata->start < 0) + seldata->start = ac + seldata->start + 1; + if (seldata->end < 0) + seldata->end = ac + seldata->end + 1; + + /* + * We avoid scanning more of the list than we need to. + */ + if (seldata->start > seldata->end) { + start = MIN(ac, seldata->start) - 1; + end = MAX(0, seldata->end - 1); + step = -1; + } else { + start = MAX(0, seldata->start - 1); + end = MIN(ac, seldata->end); + step = 1; + } + + for (i = start; + (step < 0 && i >= end) || (step > 0 && i < end); + i += step) { + if (av[i] && *av[i]) { + if (addSpace && vpstate->varSpace) { + Buf_AddByte(&buf, vpstate->varSpace); + } + Buf_AddBytes(&buf, strlen(av[i]), av[i]); + addSpace = TRUE; + } + } + + free(as); + free(av); + + return Buf_Destroy(&buf, FALSE); +} + + +/*- + * VarRealpath -- + * Replace each word with the result of realpath() + * if successful. + */ +static Boolean +VarRealpath(GNode *ctx MAKE_ATTR_UNUSED, Var_Parse_State *vpstate, + char *word, Boolean addSpace, Buffer *buf, + void *patternp MAKE_ATTR_UNUSED) +{ + struct stat st; + char rbuf[MAXPATHLEN]; + char *rp; + + if (addSpace && vpstate->varSpace) { + Buf_AddByte(buf, vpstate->varSpace); + } + addSpace = TRUE; + rp = realpath(word, rbuf); + if (rp && *rp == '/' && stat(rp, &st) == 0) + word = rp; + + Buf_AddBytes(buf, strlen(word), word); + return(addSpace); +} + +/*- + *----------------------------------------------------------------------- + * VarModify -- + * Modify each of the words of the passed string using the given + * function. Used to implement all modifiers. + * + * Input: + * str String whose words should be trimmed + * modProc Function to use to modify them + * datum Datum to pass it + * + * Results: + * A string of all the words modified appropriately. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarModify(GNode *ctx, Var_Parse_State *vpstate, + const char *str, + Boolean (*modProc)(GNode *, Var_Parse_State *, char *, + Boolean, Buffer *, void *), + void *datum) +{ + Buffer buf; /* Buffer for the new string */ + Boolean addSpace; /* TRUE if need to add a space to the + * buffer before adding the trimmed + * word */ + char **av; /* word list */ + char *as; /* word list memory */ + int ac, i; + + Buf_Init(&buf, 0); + addSpace = FALSE; + + if (vpstate->oneBigWord) { + /* fake what brk_string() would do if there were only one word */ + ac = 1; + av = bmake_malloc((ac + 1) * sizeof(char *)); + as = bmake_strdup(str); + av[0] = as; + av[1] = NULL; + } else { + av = brk_string(str, &ac, FALSE, &as); + } + + for (i = 0; i < ac; i++) { + addSpace = (*modProc)(ctx, vpstate, av[i], addSpace, &buf, datum); + } + + free(as); + free(av); + + return Buf_Destroy(&buf, FALSE); +} + + +static int +VarWordCompare(const void *a, const void *b) +{ + int r = strcmp(*(const char * const *)a, *(const char * const *)b); + return r; +} + +/*- + *----------------------------------------------------------------------- + * VarOrder -- + * Order the words in the string. + * + * Input: + * str String whose words should be sorted. + * otype How to order: s - sort, x - random. + * + * Results: + * A string containing the words ordered. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarOrder(const char *str, const char otype) +{ + Buffer buf; /* Buffer for the new string */ + char **av; /* word list [first word does not count] */ + char *as; /* word list memory */ + int ac, i; + + Buf_Init(&buf, 0); + + av = brk_string(str, &ac, FALSE, &as); + + if (ac > 0) + switch (otype) { + case 's': /* sort alphabetically */ + qsort(av, ac, sizeof(char *), VarWordCompare); + break; + case 'x': /* randomize */ + { + int rndidx; + char *t; + + /* + * We will use [ac..2] range for mod factors. This will produce + * random numbers in [(ac-1)..0] interval, and minimal + * reasonable value for mod factor is 2 (the mod 1 will produce + * 0 with probability 1). + */ + for (i = ac-1; i > 0; i--) { + rndidx = random() % (i + 1); + if (i != rndidx) { + t = av[i]; + av[i] = av[rndidx]; + av[rndidx] = t; + } + } + } + } /* end of switch */ + + for (i = 0; i < ac; i++) { + Buf_AddBytes(&buf, strlen(av[i]), av[i]); + if (i != ac - 1) + Buf_AddByte(&buf, ' '); + } + + free(as); + free(av); + + return Buf_Destroy(&buf, FALSE); +} + + +/*- + *----------------------------------------------------------------------- + * VarUniq -- + * Remove adjacent duplicate words. + * + * Input: + * str String whose words should be sorted + * + * Results: + * A string containing the resulting words. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarUniq(const char *str) +{ + Buffer buf; /* Buffer for new string */ + char **av; /* List of words to affect */ + char *as; /* Word list memory */ + int ac, i, j; + + Buf_Init(&buf, 0); + av = brk_string(str, &ac, FALSE, &as); + + if (ac > 1) { + for (j = 0, i = 1; i < ac; i++) + if (strcmp(av[i], av[j]) != 0 && (++j != i)) + av[j] = av[i]; + ac = j + 1; + } + + for (i = 0; i < ac; i++) { + Buf_AddBytes(&buf, strlen(av[i]), av[i]); + if (i != ac - 1) + Buf_AddByte(&buf, ' '); + } + + free(as); + free(av); + + return Buf_Destroy(&buf, FALSE); +} + + +/*- + *----------------------------------------------------------------------- + * VarGetPattern -- + * Pass through the tstr looking for 1) escaped delimiters, + * '$'s and backslashes (place the escaped character in + * uninterpreted) and 2) unescaped $'s that aren't before + * the delimiter (expand the variable substitution unless flags + * has VAR_NOSUBST set). + * Return the expanded string or NULL if the delimiter was missing + * If pattern is specified, handle escaped ampersands, and replace + * unescaped ampersands with the lhs of the pattern. + * + * Results: + * A string of all the words modified appropriately. + * If length is specified, return the string length of the buffer + * If flags is specified and the last character of the pattern is a + * $ set the VAR_MATCH_END bit of flags. + * + * Side Effects: + * None. + *----------------------------------------------------------------------- + */ +static char * +VarGetPattern(GNode *ctxt, Var_Parse_State *vpstate MAKE_ATTR_UNUSED, + int errnum, const char **tstr, int delim, int *flags, + int *length, VarPattern *pattern) +{ + const char *cp; + char *rstr; + Buffer buf; + int junk; + + Buf_Init(&buf, 0); + if (length == NULL) + length = &junk; + +#define IS_A_MATCH(cp, delim) \ + ((cp[0] == '\\') && ((cp[1] == delim) || \ + (cp[1] == '\\') || (cp[1] == '$') || (pattern && (cp[1] == '&')))) + + /* + * Skim through until the matching delimiter is found; + * pick up variable substitutions on the way. Also allow + * backslashes to quote the delimiter, $, and \, but don't + * touch other backslashes. + */ + for (cp = *tstr; *cp && (*cp != delim); cp++) { + if (IS_A_MATCH(cp, delim)) { + Buf_AddByte(&buf, cp[1]); + cp++; + } else if (*cp == '$') { + if (cp[1] == delim) { + if (flags == NULL) + Buf_AddByte(&buf, *cp); + else + /* + * Unescaped $ at end of pattern => anchor + * pattern at end. + */ + *flags |= VAR_MATCH_END; + } else { + if (flags == NULL || (*flags & VAR_NOSUBST) == 0) { + char *cp2; + int len; + void *freeIt; + + /* + * If unescaped dollar sign not before the + * delimiter, assume it's a variable + * substitution and recurse. + */ + cp2 = Var_Parse(cp, ctxt, errnum, &len, &freeIt); + Buf_AddBytes(&buf, strlen(cp2), cp2); + if (freeIt) + free(freeIt); + cp += len - 1; + } else { + const char *cp2 = &cp[1]; + + if (*cp2 == PROPEN || *cp2 == BROPEN) { + /* + * Find the end of this variable reference + * and suck it in without further ado. + * It will be interperated later. + */ + int have = *cp2; + int want = (*cp2 == PROPEN) ? PRCLOSE : BRCLOSE; + int depth = 1; + + for (++cp2; *cp2 != '\0' && depth > 0; ++cp2) { + if (cp2[-1] != '\\') { + if (*cp2 == have) + ++depth; + if (*cp2 == want) + --depth; + } + } + Buf_AddBytes(&buf, cp2 - cp, cp); + cp = --cp2; + } else + Buf_AddByte(&buf, *cp); + } + } + } + else if (pattern && *cp == '&') + Buf_AddBytes(&buf, pattern->leftLen, pattern->lhs); + else + Buf_AddByte(&buf, *cp); + } + + if (*cp != delim) { + *tstr = cp; + *length = 0; + return NULL; + } + + *tstr = ++cp; + *length = Buf_Size(&buf); + rstr = Buf_Destroy(&buf, FALSE); + if (DEBUG(VAR)) + fprintf(debug_file, "Modifier pattern: \"%s\"\n", rstr); + return rstr; +} + +/*- + *----------------------------------------------------------------------- + * VarQuote -- + * Quote shell meta-characters in the string + * + * Results: + * The quoted string + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarQuote(char *str) +{ + + Buffer buf; + /* This should cover most shells :-( */ + static const char meta[] = "\n \t'`\";&<>()|*?{}[]\\$!#^~"; + const char *newline; + size_t len, nlen; + + if ((newline = Shell_GetNewline()) == NULL) + newline = "\\\n"; + nlen = strlen(newline); + + Buf_Init(&buf, 0); + while (*str != '\0') { + if ((len = strcspn(str, meta)) != 0) { + Buf_AddBytes(&buf, len, str); + str += len; + } else if (*str == '\n') { + Buf_AddBytes(&buf, nlen, newline); + ++str; + } else { + Buf_AddByte(&buf, '\\'); + Buf_AddByte(&buf, *str); + ++str; + } + } + str = Buf_Destroy(&buf, FALSE); + if (DEBUG(VAR)) + fprintf(debug_file, "QuoteMeta: [%s]\n", str); + return str; +} + +/*- + *----------------------------------------------------------------------- + * VarHash -- + * Hash the string using the MurmurHash3 algorithm. + * Output is computed using 32bit Little Endian arithmetic. + * + * Input: + * str String to modify + * + * Results: + * Hash value of str, encoded as 8 hex digits. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +static char * +VarHash(char *str) +{ + static const char hexdigits[16] = "0123456789abcdef"; + Buffer buf; + size_t len, len2; + unsigned char *ustr = (unsigned char *)str; + uint32_t h, k, c1, c2; + + h = 0x971e137bU; + c1 = 0x95543787U; + c2 = 0x2ad7eb25U; + len2 = strlen(str); + + for (len = len2; len; ) { + k = 0; + switch (len) { + default: + k = (ustr[3] << 24) | (ustr[2] << 16) | (ustr[1] << 8) | ustr[0]; + len -= 4; + ustr += 4; + break; + case 3: + k |= (ustr[2] << 16); + case 2: + k |= (ustr[1] << 8); + case 1: + k |= ustr[0]; + len = 0; + } + c1 = c1 * 5 + 0x7b7d159cU; + c2 = c2 * 5 + 0x6bce6396U; + k *= c1; + k = (k << 11) ^ (k >> 21); + k *= c2; + h = (h << 13) ^ (h >> 19); + h = h * 5 + 0x52dce729U; + h ^= k; + } + h ^= len2; + h *= 0x85ebca6b; + h ^= h >> 13; + h *= 0xc2b2ae35; + h ^= h >> 16; + + Buf_Init(&buf, 0); + for (len = 0; len < 8; ++len) { + Buf_AddByte(&buf, hexdigits[h & 15]); + h >>= 4; + } + + return Buf_Destroy(&buf, FALSE); +} + +static char * +VarStrftime(const char *fmt, int zulu) +{ + char buf[BUFSIZ]; + time_t utc; + + time(&utc); + if (!*fmt) + fmt = "%c"; + strftime(buf, sizeof(buf), fmt, zulu ? gmtime(&utc) : localtime(&utc)); + + buf[sizeof(buf) - 1] = '\0'; + return bmake_strdup(buf); +} + +/* + * Now we need to apply any modifiers the user wants applied. + * These are: + * :M<pattern> words which match the given <pattern>. + * <pattern> is of the standard file + * wildcarding form. + * :N<pattern> words which do not match the given <pattern>. + * :S<d><pat1><d><pat2><d>[1gW] + * Substitute <pat2> for <pat1> in the value + * :C<d><pat1><d><pat2><d>[1gW] + * Substitute <pat2> for regex <pat1> in the value + * :H Substitute the head of each word + * :T Substitute the tail of each word + * :E Substitute the extension (minus '.') of + * each word + * :R Substitute the root of each word + * (pathname minus the suffix). + * :O ("Order") Alphabeticaly sort words in variable. + * :Ox ("intermiX") Randomize words in variable. + * :u ("uniq") Remove adjacent duplicate words. + * :tu Converts the variable contents to uppercase. + * :tl Converts the variable contents to lowercase. + * :ts[c] Sets varSpace - the char used to + * separate words to 'c'. If 'c' is + * omitted then no separation is used. + * :tW Treat the variable contents as a single + * word, even if it contains spaces. + * (Mnemonic: one big 'W'ord.) + * :tw Treat the variable contents as multiple + * space-separated words. + * (Mnemonic: many small 'w'ords.) + * :[index] Select a single word from the value. + * :[start..end] Select multiple words from the value. + * :[*] or :[0] Select the entire value, as a single + * word. Equivalent to :tW. + * :[@] Select the entire value, as multiple + * words. Undoes the effect of :[*]. + * Equivalent to :tw. + * :[#] Returns the number of words in the value. + * + * :?<true-value>:<false-value> + * If the variable evaluates to true, return + * true value, else return the second value. + * :lhs=rhs Like :S, but the rhs goes to the end of + * the invocation. + * :sh Treat the current value as a command + * to be run, new value is its output. + * The following added so we can handle ODE makefiles. + * :@<tmpvar>@<newval>@ + * Assign a temporary local variable <tmpvar> + * to the current value of each word in turn + * and replace each word with the result of + * evaluating <newval> + * :D<newval> Use <newval> as value if variable defined + * :U<newval> Use <newval> as value if variable undefined + * :L Use the name of the variable as the value. + * :P Use the path of the node that has the same + * name as the variable as the value. This + * basically includes an implied :L so that + * the common method of refering to the path + * of your dependent 'x' in a rule is to use + * the form '${x:P}'. + * :!<cmd>! Run cmd much the same as :sh run's the + * current value of the variable. + * The ::= modifiers, actually assign a value to the variable. + * Their main purpose is in supporting modifiers of .for loop + * iterators and other obscure uses. They always expand to + * nothing. In a target rule that would otherwise expand to an + * empty line they can be preceded with @: to keep make happy. + * Eg. + * + * foo: .USE + * .for i in ${.TARGET} ${.TARGET:R}.gz + * @: ${t::=$i} + * @echo blah ${t:T} + * .endfor + * + * ::=<str> Assigns <str> as the new value of variable. + * ::?=<str> Assigns <str> as value of variable if + * it was not already set. + * ::+=<str> Appends <str> to variable. + * ::!=<cmd> Assigns output of <cmd> as the new value of + * variable. + */ + +/* we now have some modifiers with long names */ +#define STRMOD_MATCH(s, want, n) \ + (strncmp(s, want, n) == 0 && (s[n] == endc || s[n] == ':')) + +static char * +ApplyModifiers(char *nstr, const char *tstr, + int startc, int endc, + Var *v, GNode *ctxt, Boolean errnum, + int *lengthPtr, void **freePtr) +{ + const char *start; + const char *cp; /* Secondary pointer into str (place marker + * for tstr) */ + char *newStr; /* New value to return */ + char termc; /* Character which terminated scan */ + int cnt; /* Used to count brace pairs when variable in + * in parens or braces */ + char delim; + int modifier; /* that we are processing */ + Var_Parse_State parsestate; /* Flags passed to helper functions */ + + delim = '\0'; + parsestate.oneBigWord = FALSE; + parsestate.varSpace = ' '; /* word separator */ + + start = cp = tstr; + + while (*tstr && *tstr != endc) { + + if (*tstr == '$') { + /* + * We may have some complex modifiers in a variable. + */ + void *freeIt; + char *rval; + int rlen; + int c; + + rval = Var_Parse(tstr, ctxt, errnum, &rlen, &freeIt); + + /* + * If we have not parsed up to endc or ':', + * we are not interested. + */ + if (rval != NULL && *rval && + (c = tstr[rlen]) != '\0' && + c != ':' && + c != endc) { + if (freeIt) + free(freeIt); + goto apply_mods; + } + + if (DEBUG(VAR)) { + fprintf(debug_file, "Got '%s' from '%.*s'%.*s\n", + rval, rlen, tstr, rlen, tstr + rlen); + } + + tstr += rlen; + + if (rval != NULL && *rval) { + int used; + + nstr = ApplyModifiers(nstr, rval, + 0, 0, + v, ctxt, errnum, &used, freePtr); + if (nstr == var_Error + || (nstr == varNoError && errnum == 0) + || strlen(rval) != (size_t) used) { + if (freeIt) + free(freeIt); + goto out; /* error already reported */ + } + } + if (freeIt) + free(freeIt); + if (*tstr == ':') + tstr++; + else if (!*tstr && endc) { + Error("Unclosed variable specification after complex modifier (expecting '%c') for %s", endc, v->name); + goto out; + } + continue; + } + apply_mods: + if (DEBUG(VAR)) { + fprintf(debug_file, "Applying[%s] :%c to \"%s\"\n", v->name, + *tstr, nstr); + } + newStr = var_Error; + switch ((modifier = *tstr)) { + case ':': + { + if (tstr[1] == '=' || + (tstr[2] == '=' && + (tstr[1] == '!' || tstr[1] == '+' || tstr[1] == '?'))) { + /* + * "::=", "::!=", "::+=", or "::?=" + */ + GNode *v_ctxt; /* context where v belongs */ + const char *emsg; + char *sv_name; + VarPattern pattern; + int how; + + if (v->name[0] == 0) + goto bad_modifier; + + v_ctxt = ctxt; + sv_name = NULL; + ++tstr; + if (v->flags & VAR_JUNK) { + /* + * We need to bmake_strdup() it incase + * VarGetPattern() recurses. + */ + sv_name = v->name; + v->name = bmake_strdup(v->name); + } else if (ctxt != VAR_GLOBAL) { + Var *gv = VarFind(v->name, ctxt, 0); + if (gv == NULL) + v_ctxt = VAR_GLOBAL; + else + VarFreeEnv(gv, TRUE); + } + + switch ((how = *tstr)) { + case '+': + case '?': + case '!': + cp = &tstr[2]; + break; + default: + cp = ++tstr; + break; + } + delim = startc == PROPEN ? PRCLOSE : BRCLOSE; + pattern.flags = 0; + + pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, NULL, + &pattern.rightLen, + NULL); + if (v->flags & VAR_JUNK) { + /* restore original name */ + free(v->name); + v->name = sv_name; + } + if (pattern.rhs == NULL) + goto cleanup; + + termc = *--cp; + delim = '\0'; + + switch (how) { + case '+': + Var_Append(v->name, pattern.rhs, v_ctxt); + break; + case '!': + newStr = Cmd_Exec(pattern.rhs, &emsg); + if (emsg) + Error(emsg, nstr); + else + Var_Set(v->name, newStr, v_ctxt, 0); + if (newStr) + free(newStr); + break; + case '?': + if ((v->flags & VAR_JUNK) == 0) + break; + /* FALLTHROUGH */ + default: + Var_Set(v->name, pattern.rhs, v_ctxt, 0); + break; + } + free(UNCONST(pattern.rhs)); + newStr = varNoError; + break; + } + goto default_case; /* "::<unrecognised>" */ + } + case '@': + { + VarLoop_t loop; + int flags = VAR_NOSUBST; + + cp = ++tstr; + delim = '@'; + if ((loop.tvar = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, + &flags, &loop.tvarLen, + NULL)) == NULL) + goto cleanup; + + if ((loop.str = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, + &flags, &loop.strLen, + NULL)) == NULL) + goto cleanup; + + termc = *cp; + delim = '\0'; + + loop.errnum = errnum; + loop.ctxt = ctxt; + newStr = VarModify(ctxt, &parsestate, nstr, VarLoopExpand, + &loop); + free(loop.tvar); + free(loop.str); + break; + } + case 'D': + case 'U': + { + Buffer buf; /* Buffer for patterns */ + int wantit; /* want data in buffer */ + + /* + * Pass through tstr looking for 1) escaped delimiters, + * '$'s and backslashes (place the escaped character in + * uninterpreted) and 2) unescaped $'s that aren't before + * the delimiter (expand the variable substitution). + * The result is left in the Buffer buf. + */ + Buf_Init(&buf, 0); + for (cp = tstr + 1; + *cp != endc && *cp != ':' && *cp != '\0'; + cp++) { + if ((*cp == '\\') && + ((cp[1] == ':') || + (cp[1] == '$') || + (cp[1] == endc) || + (cp[1] == '\\'))) + { + Buf_AddByte(&buf, cp[1]); + cp++; + } else if (*cp == '$') { + /* + * If unescaped dollar sign, assume it's a + * variable substitution and recurse. + */ + char *cp2; + int len; + void *freeIt; + + cp2 = Var_Parse(cp, ctxt, errnum, &len, &freeIt); + Buf_AddBytes(&buf, strlen(cp2), cp2); + if (freeIt) + free(freeIt); + cp += len - 1; + } else { + Buf_AddByte(&buf, *cp); + } + } + + termc = *cp; + + if (*tstr == 'U') + wantit = ((v->flags & VAR_JUNK) != 0); + else + wantit = ((v->flags & VAR_JUNK) == 0); + if ((v->flags & VAR_JUNK) != 0) + v->flags |= VAR_KEEP; + if (wantit) { + newStr = Buf_Destroy(&buf, FALSE); + } else { + newStr = nstr; + Buf_Destroy(&buf, TRUE); + } + break; + } + case 'L': + { + if ((v->flags & VAR_JUNK) != 0) + v->flags |= VAR_KEEP; + newStr = bmake_strdup(v->name); + cp = ++tstr; + termc = *tstr; + break; + } + case 'P': + { + GNode *gn; + + if ((v->flags & VAR_JUNK) != 0) + v->flags |= VAR_KEEP; + gn = Targ_FindNode(v->name, TARG_NOCREATE); + if (gn == NULL || gn->type & OP_NOPATH) { + newStr = NULL; + } else if (gn->path) { + newStr = bmake_strdup(gn->path); + } else { + newStr = Dir_FindFile(v->name, Suff_FindPath(gn)); + } + if (!newStr) { + newStr = bmake_strdup(v->name); + } + cp = ++tstr; + termc = *tstr; + break; + } + case '!': + { + const char *emsg; + VarPattern pattern; + pattern.flags = 0; + + delim = '!'; + + cp = ++tstr; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, + NULL, &pattern.rightLen, + NULL)) == NULL) + goto cleanup; + newStr = Cmd_Exec(pattern.rhs, &emsg); + free(UNCONST(pattern.rhs)); + if (emsg) + Error(emsg, nstr); + termc = *cp; + delim = '\0'; + if (v->flags & VAR_JUNK) { + v->flags |= VAR_KEEP; + } + break; + } + case '[': + { + /* + * Look for the closing ']', recursively + * expanding any embedded variables. + * + * estr is a pointer to the expanded result, + * which we must free(). + */ + char *estr; + + cp = tstr+1; /* point to char after '[' */ + delim = ']'; /* look for closing ']' */ + estr = VarGetPattern(ctxt, &parsestate, + errnum, &cp, delim, + NULL, NULL, NULL); + if (estr == NULL) + goto cleanup; /* report missing ']' */ + /* now cp points just after the closing ']' */ + delim = '\0'; + if (cp[0] != ':' && cp[0] != endc) { + /* Found junk after ']' */ + free(estr); + goto bad_modifier; + } + if (estr[0] == '\0') { + /* Found empty square brackets in ":[]". */ + free(estr); + goto bad_modifier; + } else if (estr[0] == '#' && estr[1] == '\0') { + /* Found ":[#]" */ + + /* + * We will need enough space for the decimal + * representation of an int. We calculate the + * space needed for the octal representation, + * and add enough slop to cope with a '-' sign + * (which should never be needed) and a '\0' + * string terminator. + */ + int newStrSize = + (sizeof(int) * CHAR_BIT + 2) / 3 + 2; + + newStr = bmake_malloc(newStrSize); + if (parsestate.oneBigWord) { + strncpy(newStr, "1", newStrSize); + } else { + /* XXX: brk_string() is a rather expensive + * way of counting words. */ + char **av; + char *as; + int ac; + + av = brk_string(nstr, &ac, FALSE, &as); + snprintf(newStr, newStrSize, "%d", ac); + free(as); + free(av); + } + termc = *cp; + free(estr); + break; + } else if (estr[0] == '*' && estr[1] == '\0') { + /* Found ":[*]" */ + parsestate.oneBigWord = TRUE; + newStr = nstr; + termc = *cp; + free(estr); + break; + } else if (estr[0] == '@' && estr[1] == '\0') { + /* Found ":[@]" */ + parsestate.oneBigWord = FALSE; + newStr = nstr; + termc = *cp; + free(estr); + break; + } else { + /* + * We expect estr to contain a single + * integer for :[N], or two integers + * separated by ".." for :[start..end]. + */ + char *ep; + + VarSelectWords_t seldata = { 0, 0 }; + + seldata.start = strtol(estr, &ep, 0); + if (ep == estr) { + /* Found junk instead of a number */ + free(estr); + goto bad_modifier; + } else if (ep[0] == '\0') { + /* Found only one integer in :[N] */ + seldata.end = seldata.start; + } else if (ep[0] == '.' && ep[1] == '.' && + ep[2] != '\0') { + /* Expecting another integer after ".." */ + ep += 2; + seldata.end = strtol(ep, &ep, 0); + if (ep[0] != '\0') { + /* Found junk after ".." */ + free(estr); + goto bad_modifier; + } + } else { + /* Found junk instead of ".." */ + free(estr); + goto bad_modifier; + } + /* + * Now seldata is properly filled in, + * but we still have to check for 0 as + * a special case. + */ + if (seldata.start == 0 && seldata.end == 0) { + /* ":[0]" or perhaps ":[0..0]" */ + parsestate.oneBigWord = TRUE; + newStr = nstr; + termc = *cp; + free(estr); + break; + } else if (seldata.start == 0 || + seldata.end == 0) { + /* ":[0..N]" or ":[N..0]" */ + free(estr); + goto bad_modifier; + } + /* + * Normal case: select the words + * described by seldata. + */ + newStr = VarSelectWords(ctxt, &parsestate, + nstr, &seldata); + + termc = *cp; + free(estr); + break; + } + + } + case 'g': + cp = tstr + 1; /* make sure it is set */ + if (STRMOD_MATCH(tstr, "gmtime", 6)) { + newStr = VarStrftime(nstr, 1); + cp = tstr + 6; + termc = *cp; + } else { + goto default_case; + } + break; + case 'h': + cp = tstr + 1; /* make sure it is set */ + if (STRMOD_MATCH(tstr, "hash", 4)) { + newStr = VarHash(nstr); + cp = tstr + 4; + termc = *cp; + } else { + goto default_case; + } + break; + case 'l': + cp = tstr + 1; /* make sure it is set */ + if (STRMOD_MATCH(tstr, "localtime", 9)) { + newStr = VarStrftime(nstr, 0); + cp = tstr + 9; + termc = *cp; + } else { + goto default_case; + } + break; + case 't': + { + cp = tstr + 1; /* make sure it is set */ + if (tstr[1] != endc && tstr[1] != ':') { + if (tstr[1] == 's') { + /* + * Use the char (if any) at tstr[2] + * as the word separator. + */ + VarPattern pattern; + + if (tstr[2] != endc && + (tstr[3] == endc || tstr[3] == ':')) { + /* ":ts<unrecognised><endc>" or + * ":ts<unrecognised>:" */ + parsestate.varSpace = tstr[2]; + cp = tstr + 3; + } else if (tstr[2] == endc || tstr[2] == ':') { + /* ":ts<endc>" or ":ts:" */ + parsestate.varSpace = 0; /* no separator */ + cp = tstr + 2; + } else if (tstr[2] == '\\') { + switch (tstr[3]) { + case 'n': + parsestate.varSpace = '\n'; + cp = tstr + 4; + break; + case 't': + parsestate.varSpace = '\t'; + cp = tstr + 4; + break; + default: + if (isdigit((unsigned char)tstr[3])) { + char *ep; + + parsestate.varSpace = + strtoul(&tstr[3], &ep, 0); + if (*ep != ':' && *ep != endc) + goto bad_modifier; + cp = ep; + } else { + /* + * ":ts<backslash><unrecognised>". + */ + goto bad_modifier; + } + break; + } + } else { + /* + * Found ":ts<unrecognised><unrecognised>". + */ + goto bad_modifier; + } + + termc = *cp; + + /* + * We cannot be certain that VarModify + * will be used - even if there is a + * subsequent modifier, so do a no-op + * VarSubstitute now to for str to be + * re-expanded without the spaces. + */ + pattern.flags = VAR_SUB_ONE; + pattern.lhs = pattern.rhs = "\032"; + pattern.leftLen = pattern.rightLen = 1; + + newStr = VarModify(ctxt, &parsestate, nstr, + VarSubstitute, + &pattern); + } else if (tstr[2] == endc || tstr[2] == ':') { + /* + * Check for two-character options: + * ":tu", ":tl" + */ + if (tstr[1] == 'A') { /* absolute path */ + newStr = VarModify(ctxt, &parsestate, nstr, + VarRealpath, NULL); + cp = tstr + 2; + termc = *cp; + } else if (tstr[1] == 'u') { + char *dp = bmake_strdup(nstr); + for (newStr = dp; *dp; dp++) + *dp = toupper((unsigned char)*dp); + cp = tstr + 2; + termc = *cp; + } else if (tstr[1] == 'l') { + char *dp = bmake_strdup(nstr); + for (newStr = dp; *dp; dp++) + *dp = tolower((unsigned char)*dp); + cp = tstr + 2; + termc = *cp; + } else if (tstr[1] == 'W' || tstr[1] == 'w') { + parsestate.oneBigWord = (tstr[1] == 'W'); + newStr = nstr; + cp = tstr + 2; + termc = *cp; + } else { + /* Found ":t<unrecognised>:" or + * ":t<unrecognised><endc>". */ + goto bad_modifier; + } + } else { + /* + * Found ":t<unrecognised><unrecognised>". + */ + goto bad_modifier; + } + } else { + /* + * Found ":t<endc>" or ":t:". + */ + goto bad_modifier; + } + break; + } + case 'N': + case 'M': + { + char *pattern; + const char *endpat; /* points just after end of pattern */ + char *cp2; + Boolean copy; /* pattern should be, or has been, copied */ + Boolean needSubst; + int nest; + + copy = FALSE; + needSubst = FALSE; + nest = 1; + /* + * In the loop below, ignore ':' unless we are at + * (or back to) the original brace level. + * XXX This will likely not work right if $() and ${} + * are intermixed. + */ + for (cp = tstr + 1; + *cp != '\0' && !(*cp == ':' && nest == 1); + cp++) + { + if (*cp == '\\' && + (cp[1] == ':' || + cp[1] == endc || cp[1] == startc)) { + if (!needSubst) { + copy = TRUE; + } + cp++; + continue; + } + if (*cp == '$') { + needSubst = TRUE; + } + if (*cp == '(' || *cp == '{') + ++nest; + if (*cp == ')' || *cp == '}') { + --nest; + if (nest == 0) + break; + } + } + termc = *cp; + endpat = cp; + if (copy) { + /* + * Need to compress the \:'s out of the pattern, so + * allocate enough room to hold the uncompressed + * pattern (note that cp started at tstr+1, so + * cp - tstr takes the null byte into account) and + * compress the pattern into the space. + */ + pattern = bmake_malloc(cp - tstr); + for (cp2 = pattern, cp = tstr + 1; + cp < endpat; + cp++, cp2++) + { + if ((*cp == '\\') && (cp+1 < endpat) && + (cp[1] == ':' || cp[1] == endc)) { + cp++; + } + *cp2 = *cp; + } + *cp2 = '\0'; + endpat = cp2; + } else { + /* + * Either Var_Subst or VarModify will need a + * nul-terminated string soon, so construct one now. + */ + pattern = bmake_strndup(tstr+1, endpat - (tstr + 1)); + } + if (needSubst) { + /* + * pattern contains embedded '$', so use Var_Subst to + * expand it. + */ + cp2 = pattern; + pattern = Var_Subst(NULL, cp2, ctxt, errnum); + free(cp2); + } + if (DEBUG(VAR)) + fprintf(debug_file, "Pattern[%s] for [%s] is [%s]\n", + v->name, nstr, pattern); + if (*tstr == 'M') { + newStr = VarModify(ctxt, &parsestate, nstr, VarMatch, + pattern); + } else { + newStr = VarModify(ctxt, &parsestate, nstr, VarNoMatch, + pattern); + } + free(pattern); + break; + } + case 'S': + { + VarPattern pattern; + Var_Parse_State tmpparsestate; + + pattern.flags = 0; + tmpparsestate = parsestate; + delim = tstr[1]; + tstr += 2; + + /* + * If pattern begins with '^', it is anchored to the + * start of the word -- skip over it and flag pattern. + */ + if (*tstr == '^') { + pattern.flags |= VAR_MATCH_START; + tstr += 1; + } + + cp = tstr; + if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, + &pattern.flags, + &pattern.leftLen, + NULL)) == NULL) + goto cleanup; + + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, NULL, + &pattern.rightLen, + &pattern)) == NULL) + goto cleanup; + + /* + * Check for global substitution. If 'g' after the final + * delimiter, substitution is global and is marked that + * way. + */ + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; + case 'W': + tmpparsestate.oneBigWord = TRUE; + continue; + } + break; + } + + termc = *cp; + newStr = VarModify(ctxt, &tmpparsestate, nstr, + VarSubstitute, + &pattern); + + /* + * Free the two strings. + */ + free(UNCONST(pattern.lhs)); + free(UNCONST(pattern.rhs)); + delim = '\0'; + break; + } + case '?': + { + VarPattern pattern; + Boolean value; + + /* find ':', and then substitute accordingly */ + + pattern.flags = 0; + + cp = ++tstr; + delim = ':'; + if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, NULL, + &pattern.leftLen, + NULL)) == NULL) + goto cleanup; + + /* BROPEN or PROPEN */ + delim = endc; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, errnum, + &cp, delim, NULL, + &pattern.rightLen, + NULL)) == NULL) + goto cleanup; + + termc = *--cp; + delim = '\0'; + if (Cond_EvalExpression(NULL, v->name, &value, 0, FALSE) + == COND_INVALID) { + Error("Bad conditional expression `%s' in %s?%s:%s", + v->name, v->name, pattern.lhs, pattern.rhs); + goto cleanup; + } + + if (value) { + newStr = UNCONST(pattern.lhs); + free(UNCONST(pattern.rhs)); + } else { + newStr = UNCONST(pattern.rhs); + free(UNCONST(pattern.lhs)); + } + if (v->flags & VAR_JUNK) { + v->flags |= VAR_KEEP; + } + break; + } +#ifndef NO_REGEX + case 'C': + { + VarREPattern pattern; + char *re; + int error; + Var_Parse_State tmpparsestate; + + pattern.flags = 0; + tmpparsestate = parsestate; + delim = tstr[1]; + tstr += 2; + + cp = tstr; + + if ((re = VarGetPattern(ctxt, &parsestate, errnum, &cp, delim, + NULL, NULL, NULL)) == NULL) + goto cleanup; + + if ((pattern.replace = VarGetPattern(ctxt, &parsestate, + errnum, &cp, delim, NULL, + NULL, NULL)) == NULL){ + free(re); + goto cleanup; + } + + for (;; cp++) { + switch (*cp) { + case 'g': + pattern.flags |= VAR_SUB_GLOBAL; + continue; + case '1': + pattern.flags |= VAR_SUB_ONE; + continue; + case 'W': + tmpparsestate.oneBigWord = TRUE; + continue; + } + break; + } + + termc = *cp; + + error = regcomp(&pattern.re, re, REG_EXTENDED); + free(re); + if (error) { + *lengthPtr = cp - start + 1; + VarREError(error, &pattern.re, "RE substitution error"); + free(pattern.replace); + goto cleanup; + } + + pattern.nsub = pattern.re.re_nsub + 1; + if (pattern.nsub < 1) + pattern.nsub = 1; + if (pattern.nsub > 10) + pattern.nsub = 10; + pattern.matches = bmake_malloc(pattern.nsub * + sizeof(regmatch_t)); + newStr = VarModify(ctxt, &tmpparsestate, nstr, + VarRESubstitute, + &pattern); + regfree(&pattern.re); + free(pattern.replace); + free(pattern.matches); + delim = '\0'; + break; + } +#endif + case 'Q': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarQuote(nstr); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'T': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarTail, + NULL); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'H': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarHead, + NULL); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'E': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarSuffix, + NULL); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'R': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarModify(ctxt, &parsestate, nstr, VarRoot, + NULL); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; + case 'O': + { + char otype; + + cp = tstr + 1; /* skip to the rest in any case */ + if (tstr[1] == endc || tstr[1] == ':') { + otype = 's'; + termc = *cp; + } else if ( (tstr[1] == 'x') && + (tstr[2] == endc || tstr[2] == ':') ) { + otype = tstr[1]; + cp = tstr + 2; + termc = *cp; + } else { + goto bad_modifier; + } + newStr = VarOrder(nstr, otype); + break; + } + case 'u': + if (tstr[1] == endc || tstr[1] == ':') { + newStr = VarUniq(nstr); + cp = tstr + 1; + termc = *cp; + break; + } + goto default_case; +#ifdef SUNSHCMD + case 's': + if (tstr[1] == 'h' && (tstr[2] == endc || tstr[2] == ':')) { + const char *emsg; + newStr = Cmd_Exec(nstr, &emsg); + if (emsg) + Error(emsg, nstr); + cp = tstr + 2; + termc = *cp; + break; + } + goto default_case; +#endif + default: + default_case: + { +#ifdef SYSVVARSUB + /* + * This can either be a bogus modifier or a System-V + * substitution command. + */ + VarPattern pattern; + Boolean eqFound; + + pattern.flags = 0; + eqFound = FALSE; + /* + * First we make a pass through the string trying + * to verify it is a SYSV-make-style translation: + * it must be: <string1>=<string2>) + */ + cp = tstr; + cnt = 1; + while (*cp != '\0' && cnt) { + if (*cp == '=') { + eqFound = TRUE; + /* continue looking for endc */ + } + else if (*cp == endc) + cnt--; + else if (*cp == startc) + cnt++; + if (cnt) + cp++; + } + if (*cp == endc && eqFound) { + + /* + * Now we break this sucker into the lhs and + * rhs. We must null terminate them of course. + */ + delim='='; + cp = tstr; + if ((pattern.lhs = VarGetPattern(ctxt, &parsestate, + errnum, &cp, delim, &pattern.flags, + &pattern.leftLen, NULL)) == NULL) + goto cleanup; + delim = endc; + if ((pattern.rhs = VarGetPattern(ctxt, &parsestate, + errnum, &cp, delim, NULL, &pattern.rightLen, + &pattern)) == NULL) + goto cleanup; + + /* + * SYSV modifications happen through the whole + * string. Note the pattern is anchored at the end. + */ + termc = *--cp; + delim = '\0'; + if (pattern.leftLen == 0 && *nstr == '\0') { + newStr = nstr; /* special case */ + } else { + newStr = VarModify(ctxt, &parsestate, nstr, + VarSYSVMatch, + &pattern); + } + free(UNCONST(pattern.lhs)); + free(UNCONST(pattern.rhs)); + } else +#endif + { + Error("Unknown modifier '%c'", *tstr); + for (cp = tstr+1; + *cp != ':' && *cp != endc && *cp != '\0'; + cp++) + continue; + termc = *cp; + newStr = var_Error; + } + } + } + if (DEBUG(VAR)) { + fprintf(debug_file, "Result[%s] of :%c is \"%s\"\n", + v->name, modifier, newStr); + } + + if (newStr != nstr) { + if (*freePtr) { + free(nstr); + *freePtr = NULL; + } + nstr = newStr; + if (nstr != var_Error && nstr != varNoError) { + *freePtr = nstr; + } + } + if (termc == '\0' && endc != '\0') { + Error("Unclosed variable specification (expecting '%c') for \"%s\" (value \"%s\") modifier %c", endc, v->name, nstr, modifier); + } else if (termc == ':') { + cp++; + } + tstr = cp; + } + out: + *lengthPtr = tstr - start; + return (nstr); + + bad_modifier: + /* "{(" */ + Error("Bad modifier `:%.*s' for %s", (int)strcspn(tstr, ":)}"), tstr, + v->name); + + cleanup: + *lengthPtr = cp - start; + if (delim != '\0') + Error("Unclosed substitution for %s (%c missing)", + v->name, delim); + if (*freePtr) { + free(*freePtr); + *freePtr = NULL; + } + return (var_Error); +} + +/*- + *----------------------------------------------------------------------- + * Var_Parse -- + * Given the start of a variable invocation, extract the variable + * name and find its value, then modify it according to the + * specification. + * + * Input: + * str The string to parse + * ctxt The context for the variable + * errnum TRUE if undefined variables are an error + * lengthPtr OUT: The length of the specification + * freePtr OUT: Non-NULL if caller should free *freePtr + * + * Results: + * The (possibly-modified) value of the variable or var_Error if the + * specification is invalid. The length of the specification is + * placed in *lengthPtr (for invalid specifications, this is just + * 2...?). + * If *freePtr is non-NULL then it's a pointer that the caller + * should pass to free() to free memory used by the result. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +/* coverity[+alloc : arg-*4] */ +char * +Var_Parse(const char *str, GNode *ctxt, Boolean errnum, int *lengthPtr, + void **freePtr) +{ + const char *tstr; /* Pointer into str */ + Var *v; /* Variable in invocation */ + Boolean haveModifier;/* TRUE if have modifiers for the variable */ + char endc; /* Ending character when variable in parens + * or braces */ + char startc; /* Starting character when variable in parens + * or braces */ + int vlen; /* Length of variable name */ + const char *start; /* Points to original start of str */ + char *nstr; /* New string, used during expansion */ + Boolean dynamic; /* TRUE if the variable is local and we're + * expanding it in a non-local context. This + * is done to support dynamic sources. The + * result is just the invocation, unaltered */ + const char *extramodifiers; /* extra modifiers to apply first */ + char name[2]; + + *freePtr = NULL; + extramodifiers = NULL; + dynamic = FALSE; + start = str; + + startc = str[1]; + if (startc != PROPEN && startc != BROPEN) { + /* + * If it's not bounded by braces of some sort, life is much simpler. + * We just need to check for the first character and return the + * value if it exists. + */ + + /* Error out some really stupid names */ + if (startc == '\0' || strchr(")}:$", startc)) { + *lengthPtr = 1; + return var_Error; + } + name[0] = startc; + name[1] = '\0'; + + v = VarFind(name, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + if (v == NULL) { + *lengthPtr = 2; + + if ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL)) { + /* + * If substituting a local variable in a non-local context, + * assume it's for dynamic source stuff. We have to handle + * this specially and return the longhand for the variable + * with the dollar sign escaped so it makes it back to the + * caller. Only four of the local variables are treated + * specially as they are the only four that will be set + * when dynamic sources are expanded. + */ + switch (str[1]) { + case '@': + return UNCONST("$(.TARGET)"); + case '%': + return UNCONST("$(.ARCHIVE)"); + case '*': + return UNCONST("$(.PREFIX)"); + case '!': + return UNCONST("$(.MEMBER)"); + } + } + /* + * Error + */ + return (errnum ? var_Error : varNoError); + } else { + haveModifier = FALSE; + tstr = &str[1]; + endc = str[1]; + } + } else { + Buffer buf; /* Holds the variable name */ + int depth = 1; + + endc = startc == PROPEN ? PRCLOSE : BRCLOSE; + Buf_Init(&buf, 0); + + /* + * Skip to the end character or a colon, whichever comes first. + */ + for (tstr = str + 2; *tstr != '\0'; tstr++) + { + /* + * Track depth so we can spot parse errors. + */ + if (*tstr == startc) { + depth++; + } + if (*tstr == endc) { + if (--depth == 0) + break; + } + if (depth == 1 && *tstr == ':') { + break; + } + /* + * A variable inside a variable, expand + */ + if (*tstr == '$') { + int rlen; + void *freeIt; + char *rval = Var_Parse(tstr, ctxt, errnum, &rlen, &freeIt); + if (rval != NULL) { + Buf_AddBytes(&buf, strlen(rval), rval); + } + if (freeIt) + free(freeIt); + tstr += rlen - 1; + } + else + Buf_AddByte(&buf, *tstr); + } + if (*tstr == ':') { + haveModifier = TRUE; + } else if (*tstr == endc) { + haveModifier = FALSE; + } else { + /* + * If we never did find the end character, return NULL + * right now, setting the length to be the distance to + * the end of the string, since that's what make does. + */ + *lengthPtr = tstr - str; + Buf_Destroy(&buf, TRUE); + return (var_Error); + } + str = Buf_GetAll(&buf, &vlen); + + /* + * At this point, str points into newly allocated memory from + * buf, containing only the name of the variable. + * + * start and tstr point into the const string that was pointed + * to by the original value of the str parameter. start points + * to the '$' at the beginning of the string, while tstr points + * to the char just after the end of the variable name -- this + * will be '\0', ':', PRCLOSE, or BRCLOSE. + */ + + v = VarFind(str, ctxt, FIND_ENV | FIND_GLOBAL | FIND_CMD); + /* + * Check also for bogus D and F forms of local variables since we're + * in a local context and the name is the right length. + */ + if ((v == NULL) && (ctxt != VAR_CMD) && (ctxt != VAR_GLOBAL) && + (vlen == 2) && (str[1] == 'F' || str[1] == 'D') && + strchr("@%?*!<>", str[0]) != NULL) { + /* + * Well, it's local -- go look for it. + */ + name[0] = *str; + name[1] = '\0'; + v = VarFind(name, ctxt, 0); + + if (v != NULL) { + if (str[1] == 'D') { + extramodifiers = "H:"; + } + else { /* F */ + extramodifiers = "T:"; + } + } + } + + if (v == NULL) { + if (((vlen == 1) || + (((vlen == 2) && (str[1] == 'F' || str[1] == 'D')))) && + ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) + { + /* + * If substituting a local variable in a non-local context, + * assume it's for dynamic source stuff. We have to handle + * this specially and return the longhand for the variable + * with the dollar sign escaped so it makes it back to the + * caller. Only four of the local variables are treated + * specially as they are the only four that will be set + * when dynamic sources are expanded. + */ + switch (*str) { + case '@': + case '%': + case '*': + case '!': + dynamic = TRUE; + break; + } + } else if ((vlen > 2) && (*str == '.') && + isupper((unsigned char) str[1]) && + ((ctxt == VAR_CMD) || (ctxt == VAR_GLOBAL))) + { + int len; + + len = vlen - 1; + if ((strncmp(str, ".TARGET", len) == 0) || + (strncmp(str, ".ARCHIVE", len) == 0) || + (strncmp(str, ".PREFIX", len) == 0) || + (strncmp(str, ".MEMBER", len) == 0)) + { + dynamic = TRUE; + } + } + + if (!haveModifier) { + /* + * No modifiers -- have specification length so we can return + * now. + */ + *lengthPtr = tstr - start + 1; + if (dynamic) { + char *pstr = bmake_strndup(start, *lengthPtr); + *freePtr = pstr; + Buf_Destroy(&buf, TRUE); + return(pstr); + } else { + Buf_Destroy(&buf, TRUE); + return (errnum ? var_Error : varNoError); + } + } else { + /* + * Still need to get to the end of the variable specification, + * so kludge up a Var structure for the modifications + */ + v = bmake_malloc(sizeof(Var)); + v->name = UNCONST(str); + Buf_Init(&v->val, 1); + v->flags = VAR_JUNK; + Buf_Destroy(&buf, FALSE); + } + } else + Buf_Destroy(&buf, TRUE); + } + + if (v->flags & VAR_IN_USE) { + Fatal("Variable %s is recursive.", v->name); + /*NOTREACHED*/ + } else { + v->flags |= VAR_IN_USE; + } + /* + * Before doing any modification, we have to make sure the value + * has been fully expanded. If it looks like recursion might be + * necessary (there's a dollar sign somewhere in the variable's value) + * we just call Var_Subst to do any other substitutions that are + * necessary. Note that the value returned by Var_Subst will have + * been dynamically-allocated, so it will need freeing when we + * return. + */ + nstr = Buf_GetAll(&v->val, NULL); + if (strchr(nstr, '$') != NULL) { + nstr = Var_Subst(NULL, nstr, ctxt, errnum); + *freePtr = nstr; + } + + v->flags &= ~VAR_IN_USE; + + if ((nstr != NULL) && (haveModifier || extramodifiers != NULL)) { + void *extraFree; + int used; + + extraFree = NULL; + if (extramodifiers != NULL) { + nstr = ApplyModifiers(nstr, extramodifiers, '(', ')', + v, ctxt, errnum, &used, &extraFree); + } + + if (haveModifier) { + /* Skip initial colon. */ + tstr++; + + nstr = ApplyModifiers(nstr, tstr, startc, endc, + v, ctxt, errnum, &used, freePtr); + tstr += used; + if (extraFree) { + free(extraFree); + } + } else { + *freePtr = extraFree; + } + } + if (*tstr) { + *lengthPtr = tstr - start + 1; + } else { + *lengthPtr = tstr - start; + } + + if (v->flags & VAR_FROM_ENV) { + Boolean destroy = FALSE; + + if (nstr != Buf_GetAll(&v->val, NULL)) { + destroy = TRUE; + } else { + /* + * Returning the value unmodified, so tell the caller to free + * the thing. + */ + *freePtr = nstr; + } + VarFreeEnv(v, destroy); + } else if (v->flags & VAR_JUNK) { + /* + * Perform any free'ing needed and set *freePtr to NULL so the caller + * doesn't try to free a static pointer. + * If VAR_KEEP is also set then we want to keep str as is. + */ + if (!(v->flags & VAR_KEEP)) { + if (*freePtr) { + free(nstr); + *freePtr = NULL; + } + if (dynamic) { + nstr = bmake_strndup(start, *lengthPtr); + *freePtr = nstr; + } else { + nstr = errnum ? var_Error : varNoError; + } + } + if (nstr != Buf_GetAll(&v->val, NULL)) + Buf_Destroy(&v->val, TRUE); + free(v->name); + free(v); + } + return (nstr); +} + +/*- + *----------------------------------------------------------------------- + * Var_Subst -- + * Substitute for all variables in the given string in the given context + * If undefErr is TRUE, Parse_Error will be called when an undefined + * variable is encountered. + * + * Input: + * var Named variable || NULL for all + * str the string which to substitute + * ctxt the context wherein to find variables + * undefErr TRUE if undefineds are an error + * + * Results: + * The resulting string. + * + * Side Effects: + * None. The old string must be freed by the caller + *----------------------------------------------------------------------- + */ +char * +Var_Subst(const char *var, const char *str, GNode *ctxt, Boolean undefErr) +{ + Buffer buf; /* Buffer for forming things */ + char *val; /* Value to substitute for a variable */ + int length; /* Length of the variable invocation */ + Boolean trailingBslash; /* variable ends in \ */ + void *freeIt = NULL; /* Set if it should be freed */ + static Boolean errorReported; /* Set true if an error has already + * been reported to prevent a plethora + * of messages when recursing */ + + Buf_Init(&buf, 0); + errorReported = FALSE; + trailingBslash = FALSE; + + while (*str) { + if (*str == '\n' && trailingBslash) + Buf_AddByte(&buf, ' '); + if (var == NULL && (*str == '$') && (str[1] == '$')) { + /* + * A dollar sign may be escaped either with another dollar sign. + * In such a case, we skip over the escape character and store the + * dollar sign into the buffer directly. + */ + str++; + Buf_AddByte(&buf, *str); + str++; + } else if (*str != '$') { + /* + * Skip as many characters as possible -- either to the end of + * the string or to the next dollar sign (variable invocation). + */ + const char *cp; + + for (cp = str++; *str != '$' && *str != '\0'; str++) + continue; + Buf_AddBytes(&buf, str - cp, cp); + } else { + if (var != NULL) { + int expand; + for (;;) { + if (str[1] == '\0') { + /* A trailing $ is kind of a special case */ + Buf_AddByte(&buf, str[0]); + str++; + expand = FALSE; + } else if (str[1] != PROPEN && str[1] != BROPEN) { + if (str[1] != *var || strlen(var) > 1) { + Buf_AddBytes(&buf, 2, str); + str += 2; + expand = FALSE; + } + else + expand = TRUE; + break; + } + else { + const char *p; + + /* + * Scan up to the end of the variable name. + */ + for (p = &str[2]; *p && + *p != ':' && *p != PRCLOSE && *p != BRCLOSE; p++) + if (*p == '$') + break; + /* + * A variable inside the variable. We cannot expand + * the external variable yet, so we try again with + * the nested one + */ + if (*p == '$') { + Buf_AddBytes(&buf, p - str, str); + str = p; + continue; + } + + if (strncmp(var, str + 2, p - str - 2) != 0 || + var[p - str - 2] != '\0') { + /* + * Not the variable we want to expand, scan + * until the next variable + */ + for (;*p != '$' && *p != '\0'; p++) + continue; + Buf_AddBytes(&buf, p - str, str); + str = p; + expand = FALSE; + } + else + expand = TRUE; + break; + } + } + if (!expand) + continue; + } + + val = Var_Parse(str, ctxt, undefErr, &length, &freeIt); + + /* + * When we come down here, val should either point to the + * value of this variable, suitably modified, or be NULL. + * Length should be the total length of the potential + * variable invocation (from $ to end character...) + */ + if (val == var_Error || val == varNoError) { + /* + * If performing old-time variable substitution, skip over + * the variable and continue with the substitution. Otherwise, + * store the dollar sign and advance str so we continue with + * the string... + */ + if (oldVars) { + str += length; + } else if (undefErr || val == var_Error) { + /* + * If variable is undefined, complain and skip the + * variable. The complaint will stop us from doing anything + * when the file is parsed. + */ + if (!errorReported) { + Parse_Error(PARSE_FATAL, + "Undefined variable \"%.*s\"",length,str); + } + str += length; + errorReported = TRUE; + } else { + Buf_AddByte(&buf, *str); + str += 1; + } + } else { + /* + * We've now got a variable structure to store in. But first, + * advance the string pointer. + */ + str += length; + + /* + * Copy all the characters from the variable value straight + * into the new string. + */ + length = strlen(val); + Buf_AddBytes(&buf, length, val); + trailingBslash = length > 0 && val[length - 1] == '\\'; + } + if (freeIt) { + free(freeIt); + freeIt = NULL; + } + } + } + + return Buf_DestroyCompact(&buf); +} + +/*- + *----------------------------------------------------------------------- + * Var_GetTail -- + * Return the tail from each of a list of words. Used to set the + * System V local variables. + * + * Input: + * file Filename to modify + * + * Results: + * The resulting string. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +#if 0 +char * +Var_GetTail(char *file) +{ + return(VarModify(file, VarTail, NULL)); +} + +/*- + *----------------------------------------------------------------------- + * Var_GetHead -- + * Find the leading components of a (list of) filename(s). + * XXX: VarHead does not replace foo by ., as (sun) System V make + * does. + * + * Input: + * file Filename to manipulate + * + * Results: + * The leading components. + * + * Side Effects: + * None. + * + *----------------------------------------------------------------------- + */ +char * +Var_GetHead(char *file) +{ + return(VarModify(file, VarHead, NULL)); +} +#endif + +/*- + *----------------------------------------------------------------------- + * Var_Init -- + * Initialize the module + * + * Results: + * None + * + * Side Effects: + * The VAR_CMD and VAR_GLOBAL contexts are created + *----------------------------------------------------------------------- + */ +void +Var_Init(void) +{ + VAR_INTERNAL = Targ_NewGN("Internal"); + VAR_GLOBAL = Targ_NewGN("Global"); + VAR_CMD = Targ_NewGN("Command"); + +} + + +void +Var_End(void) +{ +} + + +/****************** PRINT DEBUGGING INFO *****************/ +static void +VarPrintVar(void *vp) +{ + Var *v = (Var *)vp; + fprintf(debug_file, "%-16s = %s\n", v->name, Buf_GetAll(&v->val, NULL)); +} + +/*- + *----------------------------------------------------------------------- + * Var_Dump -- + * print all variables in a context + *----------------------------------------------------------------------- + */ +void +Var_Dump(GNode *ctxt) +{ + Hash_Search search; + Hash_Entry *h; + + for (h = Hash_EnumFirst(&ctxt->context, &search); + h != NULL; + h = Hash_EnumNext(&search)) { + VarPrintVar(Hash_GetValue(h)); + } +} diff --git a/buildrump.sh/src/usr.bin/mkdep/Makefile b/buildrump.sh/src/usr.bin/mkdep/Makefile new file mode 100644 index 00000000..fc082070 --- /dev/null +++ b/buildrump.sh/src/usr.bin/mkdep/Makefile @@ -0,0 +1,9 @@ +# $NetBSD: Makefile,v 1.17 2009/04/12 14:23:30 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +#CFLAGS+=-g +MAN= mkdep.1 +PROG= mkdep +SRCS= mkdep.c findcc.c + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/mkdep/findcc.c b/buildrump.sh/src/usr.bin/mkdep/findcc.c new file mode 100644 index 00000000..581c0bd6 --- /dev/null +++ b/buildrump.sh/src/usr.bin/mkdep/findcc.c @@ -0,0 +1,86 @@ +/* $NetBSD: findcc.c,v 1.6 2011/09/04 20:30:06 joerg Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matthias Scheler. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1999 The NetBSD Foundation, Inc.\ + All rights reserved."); +__RCSID("$NetBSD: findcc.c,v 1.6 2011/09/04 20:30:06 joerg Exp $"); +#endif /* not lint */ + +#include <sys/param.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "findcc.h" + +char * +findcc(const char *progname) +{ + char *path, *dir, *next; + char buffer[MAXPATHLEN]; + + if ((next = strchr(progname, ' ')) != NULL) { + *next = '\0'; + } + + if (strchr(progname, '/') != NULL) + return access(progname, X_OK) ? NULL : strdup(progname); + + if (((path = getenv("PATH")) == NULL) || + ((path = strdup(path)) == NULL)) + return NULL; + + dir = path; + while (dir != NULL) { + if ((next = strchr(dir, ':')) != NULL) + *next++ = '\0'; + + if (snprintf(buffer, sizeof(buffer), + "%s/%s", dir, progname) < (int)sizeof(buffer)) { + if (!access(buffer, X_OK)) { + free(path); + return strdup(buffer); + } + } + dir = next; + } + + free(path); + return NULL; +} + diff --git a/buildrump.sh/src/usr.bin/mkdep/findcc.h b/buildrump.sh/src/usr.bin/mkdep/findcc.h new file mode 100644 index 00000000..ac729113 --- /dev/null +++ b/buildrump.sh/src/usr.bin/mkdep/findcc.h @@ -0,0 +1,3 @@ +#define DEFAULT_CC "cc" + +char *findcc(const char *); diff --git a/buildrump.sh/src/usr.bin/mkdep/mkdep.1 b/buildrump.sh/src/usr.bin/mkdep/mkdep.1 new file mode 100644 index 00000000..71061628 --- /dev/null +++ b/buildrump.sh/src/usr.bin/mkdep/mkdep.1 @@ -0,0 +1,152 @@ +.\" $NetBSD: mkdep.1,v 1.18 2013/03/05 01:59:56 christos Exp $ +.\" +.\" Copyright (c) 1987, 1990, 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. +.\" +.\" @(#)mkdep.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd March 4, 2013 +.Dt MKDEP 1 +.Os +.Sh NAME +.Nm mkdep +.Nd construct Makefile dependency list +.Sh SYNOPSIS +.Nm +.Op Fl aDdiopqv +.Op Fl f Ar file +.Op Fl P Ar prefix +.Op Fl s Ar suffixes +.Li -- +.Op Ar flags +.Ar file ... +.Sh DESCRIPTION +.Nm +takes a set of flags for the C compiler and a list +of C source files as arguments and constructs a set of include +file dependencies which are written into the file ``.depend''. +An example of its use in a Makefile might be: +.Bd -literal -offset indent +CFLAGS= -O -I../include +SRCS= file1.c file2.c + +depend: + mkdep -- ${CFLAGS} ${SRCS} +.Ed +.Pp +where the macro SRCS is the list of C source files and the macro +CFLAGS is the list of flags for the C compiler. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl a +Append to the output file, +so that multiple +.Nm Ns 's +may be run from a single Makefile. +.It Fl D +Post process (as +.Fl d ) +but read the list of filenames from stdin. +.It Fl d +Post process and merge previously created (for example by +.Dq cc -MD x.c ) +depend files into a single file. +.It Fl f +Write the include file dependencies to +.Ar file , +instead of the default ``.depend''. +.It Fl i +When +.Fl d +or +.Fl D +is used, instead of inlining the contents of the files to the resulting +depend file, use include statements to include the source dependency files. +.It Fl o +Add an additional .OPTIONAL line for each dependent file. +.It Fl P +Prepend the string given in +.Ar prefix +to every target filename. +This is useful for programs that have source files in multiple subdirectories +and a single Makefile that references all of them explicitly (without using +the VPATH functionality because there can be files with the same name in +each subdirectory). +.It Fl p +Cause +.Nm +to produce dependencies of the form: +.Bd -literal -offset indent +program: program.c +.Ed +.Pp +so that subsequent makes will produce +.Ar program +directly from its C module rather than using an intermediate +.Pa \&.o +module. +This is useful for programs whose source is contained in a single +module. +.Fl p +is equivalent to specifying a null suffix with +.Fl s . +.It Fl q +Do not print a warning for inaccessible files when +.Fl d +is given. +.It Fl s +Expand each target filename to a list, replacing the +.Ql \&.o +suffix with each element of +.Ar suffixes . +The list of suffixes may be space or comma separated. +.It Fl v +print debugging output. +.El +.Sh FILES +.Bl -tag -width .depend -compact +.It Pa .depend +File containing list of dependencies. +.El +.Sh SEE ALSO +.Xr cc 1 , +.Xr cpp 1 , +.Xr make 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 Tahoe . +.Sh BUGS +Some characters special to +.Xr make 1 , +most notably the comment character, are not escaped correctly if they +appear in file names. +This can lead to unparseable output or silently cause dependencies to +be lost. +.\" (This problem is actually in gcc -M.) diff --git a/buildrump.sh/src/usr.bin/mkdep/mkdep.c b/buildrump.sh/src/usr.bin/mkdep/mkdep.c new file mode 100644 index 00000000..37751c2f --- /dev/null +++ b/buildrump.sh/src/usr.bin/mkdep/mkdep.c @@ -0,0 +1,551 @@ +/* $NetBSD: mkdep.c,v 1.43 2013/03/05 21:57:47 christos Exp $ */ + +/*- + * Copyright (c) 1999 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Matthias Scheler. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1999 The NetBSD Foundation, Inc.\ + All rights reserved."); +__RCSID("$NetBSD: mkdep.c,v 1.43 2013/03/05 21:57:47 christos Exp $"); +#endif /* not lint */ + +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/wait.h> +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <getopt.h> +#include <locale.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "findcc.h" + +typedef struct opt opt_t; +struct opt { + opt_t *left; + opt_t *right; + int len; + int count; + char name[4]; +}; + +typedef struct suff_list { + size_t len; + char *suff; + struct suff_list *next; +} suff_list_t; + +/* tree of includes for -o processing */ +static opt_t *opt; +static int width; +static int verbose; + +#define DEFAULT_PATH _PATH_DEFPATH +#define DEFAULT_FILENAME ".depend" + +static void save_for_optional(const char *, const char *); +static size_t write_optional(int, opt_t *, size_t); + +static inline void * +deconst(const void *p) +{ + return (const char *)p - (const char *)0 + (char *)0; +} + +__dead static void +usage(void) +{ + (void)fprintf(stderr, + "usage: %s [-aDdiopqv] [-f file] [-P prefix] [-s suffixes] " + "-- [flags] file ...\n", + getprogname()); + exit(EXIT_FAILURE); +} + +static int +run_cc(int argc, char **argv, const char **fname) +{ + const char *CC, *tmpdir; + char * volatile pathname; + static char tmpfilename[MAXPATHLEN]; + char **args; + int tmpfd; + pid_t pid, cpid; + int status; + + if ((CC = getenv("CC")) == NULL) + CC = DEFAULT_CC; + if ((pathname = findcc(CC)) == NULL) + if (!setenv("PATH", DEFAULT_PATH, 1)) + pathname = findcc(CC); + if (pathname == NULL) + err(EXIT_FAILURE, "%s: not found", CC); + if ((args = malloc((argc + 3) * sizeof(char *))) == NULL) + err(EXIT_FAILURE, "malloc"); + + args[0] = deconst(CC); + args[1] = deconst("-M"); + (void)memcpy(&args[2], argv, (argc + 1) * sizeof(char *)); + + if ((tmpdir = getenv("TMPDIR")) == NULL) + tmpdir = _PATH_TMP; + (void)snprintf(tmpfilename, sizeof (tmpfilename), "%s/%s", tmpdir, + "mkdepXXXXXX"); + if ((tmpfd = mkstemp(tmpfilename)) < 0) + err(EXIT_FAILURE, "Unable to create temporary file %s", + tmpfilename); + (void)unlink(tmpfilename); + *fname = tmpfilename; + + if (verbose) { + char **a; + for (a = args; *a; a++) + printf("%s ", *a); + printf("\n"); + } + + switch (cpid = vfork()) { + case 0: + (void)dup2(tmpfd, STDOUT_FILENO); + (void)close(tmpfd); + + (void)execv(pathname, args); + _exit(EXIT_FAILURE); + /* NOTREACHED */ + + case -1: + err(EXIT_FAILURE, "unable to fork"); + } + + free(pathname); + free(args); + + while (((pid = wait(&status)) != cpid) && (pid >= 0)) + continue; + + if (status) + errx(EXIT_FAILURE, "compile failed."); + + return tmpfd; +} + +static const char * +read_fname(void) +{ + static char *fbuf; + static int fbuflen; + int len, ch; + + for (len = 0; (ch = getchar()) != EOF; len++) { + if (isspace(ch)) { + if (len != 0) + break; + len--; + continue; + } + if (len >= fbuflen - 1) { + fbuf = realloc(fbuf, fbuflen += 32); + if (fbuf == NULL) + err(EXIT_FAILURE, "no memory"); + } + fbuf[len] = ch; + } + if (len == 0) + return NULL; + fbuf[len] = 0; + return fbuf; +} + +static struct option longopt[] = { + { "sysroot", 1, NULL, 'R' }, + { NULL, 0, NULL, '\0' }, +}; + +static void +addsuff(suff_list_t **l, const char *s, size_t len) +{ + suff_list_t *p = calloc(1, sizeof(*p)); + if (p == NULL) + err(1, "calloc"); + p->suff = malloc(len + 1); + if (p->suff == NULL) + err(1, "malloc"); + memcpy(p->suff, s, len); + p->suff[len] = '\0'; + p->len = len; + p->next = *l; + *l = p; +} + +int +main(int argc, char **argv) +{ + int aflag, dflag, iflag, oflag, qflag; + const char *filename; + int dependfile; + char *buf, *lim, *ptr, *line, *suf, *colon, *eol; + int ok_ind, ch; + size_t sz; + int fd; + size_t slen; + const char *fname; + const char *prefix = NULL; + const char *suffixes = NULL, *s; + suff_list_t *suff_list = NULL, *sl; + + suf = NULL; /* XXXGCC -Wuninitialized [sun2] */ + sl = NULL; /* XXXGCC -Wuninitialized [sun2] */ + + setlocale(LC_ALL, ""); + setprogname(argv[0]); + + aflag = O_WRONLY | O_APPEND | O_CREAT | O_TRUNC; + dflag = 0; + iflag = 0; + oflag = 0; + qflag = 0; + filename = DEFAULT_FILENAME; + dependfile = -1; + + opterr = 0; /* stop getopt() bleating about errors. */ + for (;;) { + ok_ind = optind; + ch = getopt_long(argc, argv, "aDdf:ioP:pqRs:v", longopt, NULL); + switch (ch) { + case -1: + ok_ind = optind; + break; + case 'a': /* Append to output file */ + aflag &= ~O_TRUNC; + continue; + case 'D': /* Process *.d files (don't run cc -M) */ + dflag = 2; /* Read names from stdin */ + opterr = 1; + continue; + case 'd': /* Process *.d files (don't run cc -M) */ + dflag = 1; + opterr = 1; + continue; + case 'f': /* Name of output file */ + filename = optarg; + continue; + case 'i': + iflag = 1; + continue; + case 'o': /* Mark dependent files .OPTIONAL */ + oflag = 1; + continue; + case 'P': /* Prefix for each target filename */ + prefix = optarg; + continue; + case 'p': /* Program mode (x.o: -> x:) */ + suffixes = ""; + continue; + case 'q': /* Quiet */ + qflag = 1; + continue; + case 'R': + /* sysroot = optarg */ + continue; + case 's': /* Suffix list */ + suffixes = optarg; + continue; + case 'v': + verbose = 1; + continue; + default: + if (dflag) + usage(); + /* Unknown arguments are passed to "${CC} -M" */ + break; + } + break; + } + + argc -= ok_ind; + argv += ok_ind; + if ((argc == 0 && !dflag) || (argc != 0 && dflag == 2)) + usage(); + + if (suffixes != NULL) { + if (*suffixes) { + for (s = suffixes; (sz = strcspn(s, ", ")) != 0;) { + addsuff(&suff_list, s, sz); + s += sz; + while (*s && strchr(", ", *s)) + s++; + } + } else + addsuff(&suff_list, "", 0); + } + + dependfile = open(filename, aflag, 0666); + if (dependfile == -1) + goto wrerror; + + while (dflag == 2 || *argv != NULL) { + if (dflag) { + if (dflag == 2) { + fname = read_fname(); + if (fname == NULL) + break; + } else + fname = *argv++; + if (iflag) { + if (dprintf(dependfile, ".-include \"%s\"\n", + fname) < 0) + goto wrerror; + continue; + } + fd = open(fname, O_RDONLY, 0); + if (fd == -1) { + if (!qflag) + warn("ignoring %s", fname); + continue; + } + } else { + fd = run_cc(argc, argv, &fname); + /* consume all args... */ + argv += argc; + } + + sz = lseek(fd, 0, SEEK_END); + if (sz == 0) { + close(fd); + continue; + } + buf = mmap(NULL, sz, PROT_READ|PROT_WRITE, MAP_PRIVATE, fd, 0); + close(fd); + + if (buf == MAP_FAILED) + err(EXIT_FAILURE, "unable to mmap file %s", fname); + lim = buf + sz - 1; + + /* Remove leading "./" from filenames */ + for (ptr = buf; ptr < lim; ptr++) { + if (ptr[1] != '.' || ptr[2] != '/' + || !isspace((unsigned char)ptr[0])) + continue; + ptr[1] = ' '; + ptr[2] = ' '; + } + + for (line = eol = buf; eol <= lim;) { + while (eol <= lim && *eol++ != '\n') + /* Find end of this line */ + continue; + if (line == eol - 1) { + /* empty line - ignore */ + line = eol; + continue; + } + if (eol[-2] == '\\') + /* Assemble continuation lines */ + continue; + for (colon = line; *colon != ':'; colon++) { + if (colon >= eol) { + colon = NULL; + break; + } + } + if (isspace((unsigned char)*line) || colon == NULL) { + /* No dependency - just transcribe line */ + if (write(dependfile, line, eol - line) < 0) + goto wrerror; + line = eol; + continue; + } + if (suff_list != NULL) { + /* Find the .o: */ + /* First allow for any whitespace */ + for (suf = colon; suf > buf; suf--) { + if (!isspace((unsigned char)suf[-1])) + break; + } + if (suf == buf) + errx(EXIT_FAILURE, + "Corrupted file `%s'", fname); + /* Then look for any valid suffix */ + for (sl = suff_list; sl != NULL; + sl = sl->next) { + if (sl->len && buf <= suf - sl->len && + !memcmp(suf - sl->len, sl->suff, + sl->len)) + break; + } + /* + * Not found, check for .o, since the + * original file will have it. + */ + if (sl == NULL) { + if (memcmp(suf - 2, ".o", 2) == 0) + slen = 2; + else + slen = 0; + } else + slen = sl->len; + } + if (suff_list != NULL && slen != 0) { + suf -= slen; + for (sl = suff_list; sl != NULL; sl = sl->next) + { + if (sl != suff_list) + if (write(dependfile, " ", 1) + < 0) + goto wrerror; + if (prefix != NULL) + if (write(dependfile, prefix, + strlen(prefix)) < 0) + goto wrerror; + if (write(dependfile, line, + suf - line) < 0) + goto wrerror; + if (write(dependfile, sl->suff, + sl->len) < 0) + goto wrerror; + } + if (write(dependfile, colon, eol - colon) < 0) + goto wrerror; + } else { + if (prefix != NULL) + if (write(dependfile, prefix, + strlen(prefix)) < 0) + goto wrerror; + if (write(dependfile, line, eol - line) < 0) + goto wrerror; + } + + if (oflag) + save_for_optional(colon + 1, eol); + line = eol; + } + munmap(buf, sz); + } + + if (oflag && opt != NULL) { + if (write(dependfile, ".OPTIONAL:", 10) < 0) + goto wrerror; + width = 9; + sz = write_optional(dependfile, opt, 0); + if (sz == (size_t)-1) + goto wrerror; + /* 'depth' is about 39 for an i386 kernel */ + /* fprintf(stderr, "Recursion depth %d\n", sz); */ + } + close(dependfile); + + exit(EXIT_SUCCESS); +wrerror: + err(EXIT_FAILURE, "unable to %s to file %s\n", + aflag & O_TRUNC ? "write" : "append", filename); +} + + +/* + * Only save each file once - the kernel .depend is 3MB and there is + * no point doubling its size. + * The data seems to be 'random enough' so the simple binary tree + * only has a reasonable depth. + */ +static void +save_for_optional(const char *start, const char *limit) +{ + opt_t **l, *n; + const char *name, *end; + int c; + + while (start < limit && strchr(" \t\n\\", *start)) + start++; + for (name = start; ; name = end) { + while (name < limit && strchr(" \t\n\\", *name)) + name++; + for (end = name; end < limit && !strchr(" \t\n\\", *end);) + end++; + if (name >= limit) + break; + if (end[-1] == 'c' && end[-2] == '.' && name == start) + /* ignore dependency on the files own .c */ + continue; + for (l = &opt;;) { + n = *l; + if (n == NULL) { + n = malloc(sizeof *n + (end - name)); + n->left = n->right = 0; + n->len = end - name; + n->count = 1; + n->name[0] = ' '; + memcpy(n->name + 1, name, end - name); + *l = n; + break; + } + c = (end - name) - n->len; + if (c == 0) + c = memcmp(n->name + 1, name, (end - name)); + if (c == 0) { + /* Duplicate */ + n->count++; + break; + } + if (c < 0) + l = &n->left; + else + l = &n->right; + } + } +} + +static size_t +write_optional(int fd, opt_t *node, size_t depth) +{ + size_t d1 = ++depth; + + if (node->left) + d1 = write_optional(fd, node->left, d1); + if (width > 76 - node->len) { + if (write(fd, " \\\n ", 4) < 0) + return (size_t)-1; + width = 1; + } + width += 1 + node->len; + if (write(fd, node->name, 1 + node->len) < 0) + return (size_t)-1; + if (node->right) + depth = write_optional(fd, node->right, depth); + return d1 > depth ? d1 : depth; +} diff --git a/buildrump.sh/src/usr.bin/mktemp/Makefile b/buildrump.sh/src/usr.bin/mktemp/Makefile new file mode 100644 index 00000000..ec6094df --- /dev/null +++ b/buildrump.sh/src/usr.bin/mktemp/Makefile @@ -0,0 +1,5 @@ +# $NetBSD: Makefile,v 1.9 2009/04/14 22:15:24 lukem Exp $ + +PROG= mktemp + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/mktemp/mktemp.1 b/buildrump.sh/src/usr.bin/mktemp/mktemp.1 new file mode 100644 index 00000000..bd911fe1 --- /dev/null +++ b/buildrump.sh/src/usr.bin/mktemp/mktemp.1 @@ -0,0 +1,258 @@ +.\" $NetBSD: mktemp.1,v 1.22 2014/11/10 07:33:31 snj Exp $ +.\" From: $FreeBSD: src/usr.bin/mktemp/mktemp.1,v 1.5 1999/08/28 01:04:13 peter Exp $ +.\" From: $OpenBSD: mktemp.1,v 1.8 1998/03/19 06:13:37 millert Exp $ +.\" +.\" Copyright (c) 1989, 1991, 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. +.\" +.\" $FreeBSD: src/usr.bin/mktemp/mktemp.1,v 1.5 1999/08/28 01:04:13 peter Exp $ +.\" +.Dd November 4, 2012 +.Dt MKTEMP 1 +.Os +.Sh NAME +.Nm mktemp +.Nd make temporary file name (unique) +.Sh SYNOPSIS +.Nm mktemp +.Op Fl dqu +.Op Fl p Ar tmpdir +.Bro +.Fl t Ar prefix +.No | +.Ar template ... +.Brc +.Sh DESCRIPTION +The +.Nm +utility +is provided to allow shell scripts to safely use temporary files. +It creates temporary files or directories using unique names, +and prints the names. +.Pp +The name of each temporary file or directory is derived from a +template that includes several trailing +.Ql X +characters, such as +.Pa /tmp/prefix.XXXX . +The trailing +.Ql X +characters in the template are replaced by unique values derived from +the current process number and additional letters or numbers. +Any +.Ql X +characters other than at the end of the template are taken literally. +The number of unique file names +.Nm +can return depends on the number of trailing +.Ql X Ns s +in the template; six +.Ql X Ns s +will result in +.Nm +testing roughly 26 ** 6 combinations. +.Pp +The templates used to create the unique names are derived from the +.Fl t Ar prefix +option, or the +.Ar template +arguments, possibly modified by other options. +Any number of temporary files or directories may be created +in a single invocation using multiple +.Ar template +arguments. +It is possible to specify both a +.Fl t Ar prefix +option and one or more +.Ar template +arguments, +but this is not usually done. +.Pp +If neither a +.Fl t Ar prefix +option, nor any +.Ar template +arguments are specified, then the default is equivalent to +.Fl t Li mktemp . +.Pp +If +.Nm +can successfully generate a unique file name, the file +is created with mode 0600 (unless the +.Fl u +flag is given) and the filename is printed to standard output. +.Sh OPTIONS +The available options are as follows: +.Bl -tag -width indent +.It Fl d +Make a directory instead of a file. +.It Fl p Ar tmpdir +Specifies a directory in which temporary files should be created. +If this option is specified, then it applies to all temporary files, +including those created as a result of a +.Fl t Ar prefix +option, and those created as a result of a +.Ar template +argument. +.Pp +If the +.Fl p Ar tmpdir +option is not specified, then +temporary files created as a result of a +.Fl t Ar prefix +option will use a default temporary directory +(as described under the +.Fl t +option), +but temporary files created as a result of a +.Ar template +argument will not use a default temporary directory +(so they will be created relative to the current working directory, if the +.Ar template +does not begin with +.Ql \&/ ) . +.It Fl t Ar prefix +Generate a template using an appropriate directory name, followed by the +supplied +.Ar prefix , +followed by +.Ql \&.XXXXXXXX . +Any +.Ql X +characters in the supplied +.Ar prefix +are taken literally, but the trailing +.Ql X +characters in the appended +.Ql \&.XXXXXXXX +are replaced by unique values. +.Pp +The directory name used for the template generated by the +.Fl t Ar prefix +option is taken from the +.Fl p Ar tmpdir +option, or from the +.Ev TMPDIR +environment variable, or +.Pa /tmp +as a default. +.Pp +If one or more +.Ar template +arguments are used in addition to the +.Fl t Ar prefix +option, then the +.Ar prefix +does not apply to the +.Ar template +arguments. +.It Fl q +Fail silently if an error occurs. +This is useful if +a script does not want error output to go to standard error. +.It Fl u +Operate in +.Dq unsafe +mode. +The temp file will be unlinked before +.Nm +exits. +This is slightly better than +.Xr mktemp 3 +but still introduces a race condition. +Use of this option is not encouraged. +.El +.Sh NOTES +.Nm +takes care to create the files or directories in a way that is +safe from race conditions (provided the +.Fl u +option is not used). +.Pp +Traditionally, without +.Nm , +many shell scripts created temporary files +using the name of the program with +the pid as a suffix. +This kind of naming scheme is predictable and creates a race condition that +allows an attacker to subvert the program by +creating a different file, directory, or symbolic link +under the same name. +A safer, though still inferior, approach +is to make a temporary directory using the same naming scheme +While this does allow one to guarantee that a temporary file will +not be subverted, it still allows a simple denial of service attack. +For these reasons it is recommended that +.Nm +be used instead of simpler schemes. +.Pp +Care should be taken to ensure that it is appropriate to use an +environment variable potentially supplied by the user. +.Sh EXIT STATUS +The +.Nm +utility exits with a value of 0 on success, and 1 on any failure. +.Sh EXAMPLES +The following +.Xr sh 1 +fragment illustrates a simple use of +.Nm +where the script should quit if it cannot get a safe +temporary file. +.Bd -literal -offset indent +TMPFILE=`mktemp /tmp/${0##*/}.XXXXXX` || exit 1 +echo "program output" \*[Gt]\*[Gt] $TMPFILE +.Ed +.Pp +To allow the use of $TMPDIR: +.Bd -literal -offset indent +TMPFILE=`mktemp -t ${0##*/}` || exit 1 +echo "program output" \*[Gt]\*[Gt] $TMPFILE +.Ed +.Pp +In this case, we want the script to catch the error itself. +.Bd -literal -offset indent +TMPFILE=`mktemp -q /tmp/${0##*/}.XXXXXX` +if [ $? -ne 0 ]; then + echo "$0: Can't create temp file, exiting..." + exit 1 +fi +.Ed +.Sh SEE ALSO +.Xr mkdtemp 3 , +.Xr mkstemp 3 , +.Xr mktemp 3 , +.Xr environ 7 +.Sh HISTORY +The +.Nm +utility appeared in +.Nx 1.5 . +It was imported from +.Fx , +and the idea and the manual page were taken from +.Ox . diff --git a/buildrump.sh/src/usr.bin/mktemp/mktemp.c b/buildrump.sh/src/usr.bin/mktemp/mktemp.c new file mode 100644 index 00000000..b8ecc715 --- /dev/null +++ b/buildrump.sh/src/usr.bin/mktemp/mktemp.c @@ -0,0 +1,174 @@ +/* $NetBSD: mktemp.c,v 1.12 2012/11/03 13:34:08 christos Exp $ */ + +/*- + * Copyright (c) 1994, 1995, 1996, 1998 Peter Wemm <peter@netplex.com.au> + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. + * + */ + +/* + * This program was originally written long ago, originally for a non + * BSD-like OS without mkstemp(). It's been modified over the years + * to use mkstemp() rather than the original O_CREAT|O_EXCL/fstat/lstat + * etc style hacks. + * A cleanup, misc options and mkdtemp() calls were added to try and work + * more like the OpenBSD version - which was first to publish the interface. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#include <sys/types.h> +#include <err.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if defined(__RCSID) && !defined(__lint) +__RCSID("$NetBSD: mktemp.c,v 1.12 2012/11/03 13:34:08 christos Exp $"); +#endif /* !__lint */ + +static void usage(void) __dead; + +int +main(int argc, char **argv) +{ + int c, fd, ret; + char *tmpdir; + const char *prefix; + char *name; + int dflag, qflag, tflag, uflag; + + setprogname(*argv); + ret = dflag = qflag = tflag = uflag = 0; + tmpdir = NULL; + prefix = "mktemp"; + name = NULL; + + while ((c = getopt(argc, argv, "dp:qt:u")) != -1) + switch (c) { + case 'd': + dflag++; + break; + + case 'p': + tmpdir = optarg; + break; + + case 'q': + qflag++; + break; + + case 't': + prefix = optarg; + tflag++; + break; + + case 'u': + uflag++; + break; + + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (tflag == 0 && argc < 1) + tflag = 1; + + if (tflag) { + if (tmpdir == NULL) + tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + (void)asprintf(&name, "%s%s.XXXXXXXX", _PATH_TMP, + prefix); + else + (void)asprintf(&name, "%s/%s.XXXXXXXX", tmpdir, prefix); + /* if this fails, the program is in big trouble already */ + if (name == NULL) { + if (qflag) + return 1; + else + errx(1, "Cannot generate template"); + } + } else if (argc < 1) { + usage(); + } + + /* generate all requested files */ + while (name != NULL || argc > 0) { + if (name == NULL) { + if (tmpdir) + (void)asprintf(&name, "%s/%s", + tmpdir, argv[0]); + else + name = strdup(argv[0]); + argv++; + argc--; + } + + if (dflag) { + if (mkdtemp(name) == NULL) { + ret = 1; + if (!qflag) + warn("mkdtemp failed on %s", name); + } else { + (void)printf("%s\n", name); + if (uflag) + (void)rmdir(name); + } + } else { + fd = mkstemp(name); + if (fd < 0) { + ret = 1; + if (!qflag) + warn("mkstemp failed on %s", name); + } else { + (void)close(fd); + if (uflag) + (void)unlink(name); + (void)printf("%s\n", name); + } + } + if (name) + free(name); + name = NULL; + } + return ret; +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "Usage: %s [-dqu] [-p <tmpdir>] {-t prefix | template ...}\n", + getprogname()); + exit (1); +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/Makefile b/buildrump.sh/src/usr.bin/rpcgen/Makefile new file mode 100644 index 00000000..cd057676 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.13 2013/08/11 08:03:10 dholland Exp $ + +PROG= rpcgen +SRCS= rpc_clntout.c rpc_cout.c rpc_hout.c rpc_main.c rpc_parse.c rpc_scan.c \ + rpc_svcout.c rpc_util.c rpc_sample.c rpc_tblout.c + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_clntout.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_clntout.c new file mode 100644 index 00000000..0f750a4f --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_clntout.c @@ -0,0 +1,264 @@ +/* $NetBSD: rpc_clntout.c,v 1.15 2013/12/15 00:40:17 christos Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_clntout.c 1.11 89/02/22 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_clntout.c,v 1.15 2013/12/15 00:40:17 christos Exp $"); +#endif +#endif + +/* + * rpc_clntout.c, Client-stub outputter for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsytsems, Inc. + */ +#include <stdio.h> +#include <string.h> +#include <rpc/types.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +static void write_program(definition *); +static const char *ampr(const char *); +static const char *aster(const char *); +static void printbody(proc_list *); + +#define DEFAULT_TIMEOUT 25 /* in seconds */ +static char RESULT[] = "clnt_res"; + + +void +write_stubs(void) +{ + list *l; + definition *def; + + f_print(fout, + "\n/* Default timeout can be changed using clnt_control() */\n"); + f_print(fout, "static struct timeval TIMEOUT = { %d, 0 };\n", + DEFAULT_TIMEOUT); + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + write_program(def); + } + } +} + +static void +write_program(definition *def) +{ + version_list *vp; + proc_list *proc; + + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + for (proc = vp->procs; proc != NULL; proc = proc->next) { + f_print(fout, "\n"); + if (Mflag) + f_print(fout, "enum clnt_stat\n"); + else { + ptype(proc->res_prefix, proc->res_type, 1); + f_print(fout, "*\n"); + } + pvname(proc->proc_name, vp->vers_num); + printarglist(proc, RESULT, "clnt", "CLIENT *"); + f_print(fout, "{\n"); + printbody(proc); + f_print(fout, "}\n"); + } + } +} +/* Writes out declarations of procedure's argument list. + In either ANSI C style, in one of old rpcgen style (pass by reference), + or new rpcgen style (multiple arguments, pass by value); + */ + +/* sample addargname = "clnt"; sample addargtype = "CLIENT * " */ + +void +printarglist(proc_list *proc, const char *result, + const char *addargname, const char *addargtype) +{ + + decl_list *l; + + if (!newstyle) { /* old style: always pass argument by + * reference */ + f_print(fout, "("); + ptype(proc->args.decls->decl.prefix, proc->args.decls->decl.type, 1); + f_print(fout, "*argp, "); + if (Mflag) { + if (streq(proc->res_type, "void")) + f_print(fout, "char "); + else + ptype(proc->res_prefix, proc->res_type, 0); + f_print(fout, "%s%s, ", aster(proc->res_type), + result); + } + f_print(fout, "%s%s)\n", addargtype, addargname); + } else { + f_print(fout, "("); + if (!streq(proc->args.decls->decl.type, "void")) { + /* new style, 1 or multiple arguments */ + for (l = proc->args.decls; l != NULL; l = l->next) + pdeclaration(proc->args.argname, + &l->decl, 0, ", "); + } + if (Mflag) { + if (streq(proc->res_type, "void")) + f_print(fout, "char "); + else + ptype(proc->res_prefix, proc->res_type, 0); + f_print(fout, "%s%s, ", aster(proc->res_type), + result); + } + f_print(fout, "%s%s)\n", addargtype, addargname); + } +} + + +static const char * +ampr(const char *type) +{ + if (isvectordef(type, REL_ALIAS)) { + return (""); + } else { + return ("&"); + } +} + +static const char * +aster(const char *type) +{ + if (isvectordef(type, REL_ALIAS)) { + return (""); + } else { + return ("*"); + } +} + +static void +printbody(proc_list *proc) +{ + decl_list *l; + bool_t args2 = (proc->arg_num > 1); + + /* For new style with multiple arguments, need a structure in which to + * stuff the arguments. */ + if (newstyle && args2) { + f_print(fout, "\t%s", proc->args.argname); + f_print(fout, " arg;\n"); + } + if (!Mflag) { + f_print(fout, "\tstatic "); + if (streq(proc->res_type, "void")) + f_print(fout, "char "); + else + ptype(proc->res_prefix, proc->res_type, 0); + f_print(fout, "%s;\n", RESULT); + } + f_print(fout, "\n"); + if (!Mflag) + f_print(fout, "\tmemset((char *)%s%s, 0, sizeof(%s));\n", + ampr(proc->res_type), RESULT, RESULT); + if (newstyle && !args2 && (streq(proc->args.decls->decl.type, "void"))) { + /* newstyle, 0 arguments */ + if (Mflag) { + f_print(fout, "\treturn (clnt_call(clnt, %s, xdr_void", + proc->proc_name); + f_print(fout, ", NULL, xdr_%s, %s, TIMEOUT));\n", + stringfix(proc->res_type), RESULT); + } else { + f_print(fout, "\tif (clnt_call(clnt, %s, xdr_void, ", + proc->proc_name); + f_print(fout, + "NULL, xdr_%s, %s%s, TIMEOUT) != RPC_SUCCESS)\n", + stringfix(proc->res_type), ampr(proc->res_type), + RESULT); + } + } else { + if (newstyle && args2) { + /* newstyle, multiple arguments: stuff arguments into + * structure */ + for (l = proc->args.decls; l != NULL; l = l->next) { + f_print(fout, "\targ.%s = %s;\n", + l->decl.name, l->decl.name); + } + if (Mflag) { + f_print(fout, + "\treturn (clnt_call(clnt, %s, xdr_%s, &arg, xdr_%s, %s, TIMEOUT));\n", + proc->proc_name, proc->args.argname, + stringfix(proc->res_type), RESULT); + } else { + f_print(fout, + "\tif (clnt_call(clnt, %s, xdr_%s, &arg, xdr_%s, %s%s, TIMEOUT) != RPC_SUCCESS)\n", + proc->proc_name, proc->args.argname, + stringfix(proc->res_type), + ampr(proc->res_type), RESULT); + } + } else { /* single argument, new or old style */ + if (Mflag) { + f_print(fout, + "\treturn (clnt_call(clnt, %s, xdr_%s, %s%s, xdr_%s, %s, TIMEOUT));\n", + proc->proc_name, + stringfix(proc->args.decls->decl.type), + (newstyle ? "&" : ""), + (newstyle ? proc->args.decls->decl.name : "argp"), + stringfix(proc->res_type), RESULT); + } else { + f_print(fout, + "\tif (clnt_call(clnt, %s, xdr_%s, %s%s, xdr_%s, %s%s, TIMEOUT) != RPC_SUCCESS)\n", + proc->proc_name, + stringfix(proc->args.decls->decl.type), + (newstyle ? "&" : ""), + (newstyle ? proc->args.decls->decl.name : "argp"), + stringfix(proc->res_type), + ampr(proc->res_type), RESULT); + } + } + } + if (!Mflag) { + f_print(fout, "\t\treturn (NULL);\n"); + if (streq(proc->res_type, "void")) + f_print(fout, "\treturn ((void *)%s%s);\n", + ampr(proc->res_type), RESULT); + else + f_print(fout, "\treturn (%s%s);\n", + ampr(proc->res_type), RESULT); + } +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_cout.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_cout.c new file mode 100644 index 00000000..65cb389a --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_cout.c @@ -0,0 +1,726 @@ +/* $NetBSD: rpc_cout.c,v 1.36 2015/05/09 23:16:51 dholland Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_cout.c 1.13 89/02/22 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_cout.c,v 1.36 2015/05/09 23:16:51 dholland Exp $"); +#endif +#endif + +/* + * rpc_cout.c, XDR routine outputter for the RPC protocol compiler + */ +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +static int findtype(definition *, const char *); +static int undefined(const char *); +static void print_generic_header(const char *, int); +static void print_header(definition *); +static void print_prog_header(proc_list *); +static void print_trailer(void); +static void print_ifopen(int, const char *); +static void print_ifarg(const char *); +static void print_ifsizeof(const char *, const char *); +static void print_ifclose(int); +static void print_ifstat(int, const char *, const char *, relation, + const char *, const char *, const char *); +static void emit_enum(definition *); +static void emit_program(definition *); +static void emit_union(definition *); +static void emit_struct(definition *); +static void emit_typedef(definition *); +static void print_stat(int, declaration *); + +/* + * Emit the C-routine for the given definition + */ +void +emit(definition *def) +{ + if (def->def_kind == DEF_CONST) { + return; + } + if (def->def_kind == DEF_PROGRAM) { + emit_program(def); + return; + } + if (def->def_kind == DEF_TYPEDEF) { + /* now we need to handle declarations like struct typedef foo + * foo; since we dont want this to be expanded into 2 calls to + * xdr_foo */ + + if (strcmp(def->def.ty.old_type, def->def_name) == 0) + return; + }; + + print_header(def); + + switch (def->def_kind) { + case DEF_UNION: + emit_union(def); + break; + case DEF_ENUM: + emit_enum(def); + break; + case DEF_STRUCT: + emit_struct(def); + break; + case DEF_TYPEDEF: + emit_typedef(def); + break; + case DEF_PROGRAM: + case DEF_CONST: + errx(1, "Internal error at %s:%d: Case %d not handled", + __FILE__, __LINE__, def->def_kind); + break; + } + print_trailer(); +} + +static int +findtype(definition *def, const char *type) +{ + + if (def->def_kind == DEF_PROGRAM || def->def_kind == DEF_CONST) { + return (0); + } else { + return (streq(def->def_name, type)); + } +} + +static int +undefined(const char *type) +{ + definition *def; + + def = (definition *) FINDVAL(defined, type, findtype); + + + return (def == NULL); +} + +static void +print_generic_header(const char *procname, int pointerp) +{ + f_print(fout, "\n"); + f_print(fout, "bool_t\n"); + f_print(fout, "xdr_%s(", procname); + f_print(fout, "XDR *xdrs, "); + f_print(fout, "%s ", procname); + if (pointerp) + f_print(fout, "*"); + f_print(fout, "objp)\n{\n"); +} + +static void +print_header(definition *def) +{ + print_generic_header(def->def_name, + def->def_kind != DEF_TYPEDEF || + !isvectordef(def->def.ty.old_type, def->def.ty.rel)); +} + +static void +print_prog_header(proc_list *plist) +{ + print_generic_header(plist->args.argname, 1); +} + +static void +print_trailer(void) +{ + f_print(fout, "\treturn (TRUE);\n"); + f_print(fout, "}\n"); +} + + +static void +print_ifopen(int indent, const char *name) +{ + char _t_kludge[32]; + /* + * XXX Solaris seems to strip the _t. No idea why. + */ + if (!strcmp(name, "rpcprog_t") || !strcmp(name, "rpcvers_t") || + !strcmp(name, "rpcproc_t") || !strcmp(name, "rpcprot_t") || + !strcmp(name, "rpcport_t") || !strcmp(name, "rpcpinline_t")) { + strncpy(_t_kludge, name, strlen(name) - 2); + name = _t_kludge; + } + tabify(fout, indent); + f_print(fout, "if (!xdr_%s(xdrs", name); +} + +static void +print_ifarg(const char *arg) +{ + f_print(fout, ", %s", arg); +} + +static void +print_ifsizeof(const char *prefix, const char *type) +{ + if (streq(type, "bool")) { + f_print(fout, ", (u_int)sizeof(bool_t), (xdrproc_t)xdr_bool"); + } else { + f_print(fout, ", (u_int)sizeof("); + if (undefined(type) && prefix) { + f_print(fout, "%s ", prefix); + } + f_print(fout, "%s), (xdrproc_t)xdr_%s", type, type); + } +} + +static void +print_ifclose(int indent) +{ + f_print(fout, "))\n"); + tabify(fout, indent); + f_print(fout, "\treturn (FALSE);\n"); +} + +static void +print_ifstat(int indent, const char *prefix, const char *type, relation rel, + const char *amax, const char *objname, const char *name) +{ + const char *alt = NULL; + + switch (rel) { + case REL_POINTER: + print_ifopen(indent, "pointer"); + print_ifarg("(char **)(void *)"); + f_print(fout, "%s", objname); + print_ifsizeof(prefix, type); + break; + case REL_VECTOR: + if (streq(type, "string")) { + alt = "string"; + } else + if (streq(type, "opaque")) { + alt = "opaque"; + } + if (alt) { + print_ifopen(indent, alt); + print_ifarg(objname); + } else { + print_ifopen(indent, "vector"); + print_ifarg("(char *)(void *)"); + f_print(fout, "%s", objname); + } + print_ifarg(amax); + if (!alt) { + print_ifsizeof(prefix, type); + } + break; + case REL_ARRAY: + if (streq(type, "string")) { + alt = "string"; + } else + if (streq(type, "opaque")) { + alt = "bytes"; + } + if (streq(type, "string")) { + print_ifopen(indent, alt); + print_ifarg(objname); + } else { + if (alt) { + print_ifopen(indent, alt); + } else { + print_ifopen(indent, "array"); + } + print_ifarg("(char **)(void *)"); + if (*objname == '&') { + f_print(fout, "%s.%s_val, (u_int *)%s.%s_len", + objname, name, objname, name); + } else { + f_print(fout, "&%s->%s_val, (u_int *)&%s->%s_len", + objname, name, objname, name); + } + } + print_ifarg(amax); + if (!alt) { + print_ifsizeof(prefix, type); + } + break; + case REL_ALIAS: + print_ifopen(indent, type); + print_ifarg(objname); + break; + } + print_ifclose(indent); +} +/* ARGSUSED */ +static void +emit_enum(definition *def) +{ + tabify(fout, 1); + f_print(fout, "{\n"); + tabify(fout, 2); + f_print(fout, "enum_t et = (enum_t)*objp;\n"); + print_ifopen(2, "enum"); + print_ifarg("&et"); + print_ifclose(2); + tabify(fout, 2); + f_print(fout, "*objp = (%s)et;\n", def->def_name); + tabify(fout, 1); + f_print(fout, "}\n"); +} + +static void +emit_program(definition *def) +{ + decl_list *dl; + version_list *vlist; + proc_list *plist; + + for (vlist = def->def.pr.versions; vlist != NULL; vlist = vlist->next) + for (plist = vlist->procs; plist != NULL; plist = plist->next) { + if (!newstyle || plist->arg_num < 2) + continue; /* old style, or single + * argument */ + print_prog_header(plist); + for (dl = plist->args.decls; dl != NULL; + dl = dl->next) + print_stat(1, &dl->decl); + print_trailer(); + } +} + + +static void +emit_union(definition *def) +{ + declaration *dflt; + case_list *cl; + declaration *cs; + char *object; + static const char vecformat[] = "objp->%s_u.%s"; + static const char format[] = "&objp->%s_u.%s"; + + f_print(fout, "\n"); + print_stat(1, &def->def.un.enum_decl); + f_print(fout, "\tswitch (objp->%s) {\n", def->def.un.enum_decl.name); + for (cl = def->def.un.cases; cl != NULL; cl = cl->next) { + f_print(fout, "\tcase %s:\n", cl->case_name); + if (cl->contflag == 1) /* a continued case statement */ + continue; + cs = &cl->case_decl; + if (!streq(cs->type, "void")) { + object = alloc(strlen(def->def_name) + strlen(format) + + strlen(cs->name) + 1); + if (isvectordef(cs->type, cs->rel)) { + s_print(object, vecformat, def->def_name, + cs->name); + } else { + s_print(object, format, def->def_name, + cs->name); + } + print_ifstat(2, cs->prefix, cs->type, cs->rel, + cs->array_max, object, cs->name); + free(object); + } + f_print(fout, "\t\tbreak;\n"); + } + dflt = def->def.un.default_decl; + f_print(fout, "\tdefault:\n"); + if (dflt != NULL) { + if (!streq(dflt->type, "void")) { + object = alloc(strlen(def->def_name) + strlen(format) + + strlen(dflt->name) + 1); + if (isvectordef(dflt->type, dflt->rel)) { + s_print(object, vecformat, def->def_name, + dflt->name); + } else { + s_print(object, format, def->def_name, + dflt->name); + } + print_ifstat(2, dflt->prefix, dflt->type, dflt->rel, + dflt->array_max, object, dflt->name); + free(object); + } + f_print(fout, "\t\tbreak;\n"); + } else { + f_print(fout, "\t\treturn (FALSE);\n"); + } + + f_print(fout, "\t}\n"); +} + +static void +emit_struct(definition *def) +{ + decl_list *dl; + int i, j, size, flag; + decl_list *cur = NULL, *psav; + bas_type *ptr; + char *sizestr; + const char *plus; + char ptemp[256]; + int can_inline; + + + if (doinline == 0) { + f_print(fout, "\n"); + for (dl = def->def.st.decls; dl != NULL; dl = dl->next) + print_stat(1, &dl->decl); + return; + } + size = 0; + can_inline = 0; + for (dl = def->def.st.decls; dl != NULL; dl = dl->next) + if ((dl->decl.prefix == NULL) && + ((ptr = find_type(dl->decl.type)) != NULL) && + ((dl->decl.rel == REL_ALIAS) || (dl->decl.rel == REL_VECTOR))) { + + if (dl->decl.rel == REL_ALIAS) + size += ptr->length; + else { + can_inline = 1; + break; /* can be inlined */ + }; + } else { + if (size >= doinline) { + can_inline = 1; + break; /* can be inlined */ + } + size = 0; + } + if (size > doinline) + can_inline = 1; + + if (can_inline == 0) { /* can not inline, drop back to old mode */ + f_print(fout, "\n"); + for (dl = def->def.st.decls; dl != NULL; dl = dl->next) + print_stat(1, &dl->decl); + return; + }; + + /* May cause lint to complain. but ... */ + f_print(fout, "\tint32_t *buf;\n"); + + flag = PUT; + f_print(fout, "\n\tif (xdrs->x_op == XDR_ENCODE) {\n"); + + for (j = 0; j < 2; j++) { + i = 0; + size = 0; + sizestr = NULL; + for (dl = def->def.st.decls; dl != NULL; dl = dl->next) { /* xxx */ + + /* now walk down the list and check for basic types */ + if ((dl->decl.prefix == NULL) && ((ptr = find_type(dl->decl.type)) != NULL) && ((dl->decl.rel == REL_ALIAS) || (dl->decl.rel == REL_VECTOR))) { + if (i == 0) + cur = dl; + i++; + + if (dl->decl.rel == REL_ALIAS) + size += ptr->length; + else { + /* this is required to handle arrays */ + + if (sizestr == NULL) + plus = ""; + else + plus = " + "; + + if (ptr->length != 1) + s_print(ptemp, "%s%s * %d", plus, dl->decl.array_max, ptr->length); + else + s_print(ptemp, "%s%s", plus, dl->decl.array_max); + + /* now concatenate to sizestr !!!! */ + if (sizestr == NULL) + sizestr = strdup(ptemp); + else { + char *nsizestr; + + nsizestr = realloc(sizestr, strlen(sizestr) + strlen(ptemp) + 1); + if (nsizestr == NULL) { + + errx(EXIT_FAILURE, "Out of memory"); + } + sizestr = nsizestr; + sizestr = strcat(sizestr, ptemp); /* build up length of + * array */ + + } + } + + } else { + if (i > 0) { + if (sizestr == NULL && size < doinline) { + /* don't expand into inline + * code if size < doinline */ + while (cur != dl) { + print_stat(2, &cur->decl); + cur = cur->next; + } + } else { + + + + /* were already looking at a + * xdr_inlineable structure */ + if (sizestr == NULL) + f_print(fout, "\t\tbuf = (int32_t *)XDR_INLINE(xdrs, %d * BYTES_PER_XDR_UNIT);\n", + size); + else + if (size == 0) + f_print(fout, + "\t\tbuf = (int32_t *)XDR_INLINE(xdrs, %s * BYTES_PER_XDR_UNIT);\n", + sizestr); + else + f_print(fout, + "\t\tbuf = (int32_t *)XDR_INLINE(xdrs, (%d + %s) * BYTES_PER_XDR_UNIT);\n", + size, sizestr); + + f_print(fout, "\t\tif (buf == NULL) {\n"); + + psav = cur; + while (cur != dl) { + print_stat(3, &cur->decl); + cur = cur->next; + } + + f_print(fout, "\t\t} else {\n"); + + cur = psav; + while (cur != dl) { + emit_inline(&cur->decl, flag); + cur = cur->next; + } + + f_print(fout, "\t\t}\n"); + } + } + size = 0; + i = 0; + if (sizestr) { + free(sizestr); + sizestr = NULL; + } + print_stat(2, &dl->decl); + } + + } + if (i > 0) { + if (sizestr == NULL && size < doinline) { + /* don't expand into inline code if size < + * doinline */ + while (cur != dl) { + print_stat(2, &cur->decl); + cur = cur->next; + } + } else { + + /* were already looking at a xdr_inlineable + * structure */ + if (sizestr == NULL) + f_print(fout, "\t\tbuf = (int32_t *)XDR_INLINE(xdrs, %d * BYTES_PER_XDR_UNIT);\n", + size); + else + if (size == 0) + f_print(fout, + "\t\tbuf = (int32_t *)XDR_INLINE(xdrs, %s * BYTES_PER_XDR_UNIT);\n", + sizestr); + else + f_print(fout, + "\t\tbuf = (int32_t *)XDR_INLINE(xdrs, (%d + %s) * BYTES_PER_XDR_UNIT);\n", + size, sizestr); + + f_print(fout, "\t\tif (buf == NULL) {\n"); + + psav = cur; + while (cur != NULL) { + print_stat(3, &cur->decl); + cur = cur->next; + } + f_print(fout, "\t\t} else {\n"); + + cur = psav; + while (cur != dl) { + emit_inline(&cur->decl, flag); + cur = cur->next; + } + + f_print(fout, "\t\t}\n"); + + } + } + if (flag == PUT) { + flag = GET; + f_print(fout, "\t} else if (xdrs->x_op == XDR_DECODE) {\n"); + } + } + + f_print(fout, "\t} else {\n"); + + /* now take care of XDR_FREE case */ + + for (dl = def->def.st.decls; dl != NULL; dl = dl->next) + print_stat(2, &dl->decl); + + f_print(fout, "\t}\n"); +} + +static void +emit_typedef(definition *def) +{ + const char *prefix = def->def.ty.old_prefix; + const char *type = def->def.ty.old_type; + const char *amax = def->def.ty.array_max; + relation rel = def->def.ty.rel; + + f_print(fout, "\n"); + print_ifstat(1, prefix, type, rel, amax, "objp", def->def_name); +} + +static void +print_stat(int indent, declaration *dec) +{ + const char *prefix = dec->prefix; + const char *type = dec->type; + const char *amax = dec->array_max; + relation rel = dec->rel; + char name[256]; + + if (isvectordef(type, rel)) { + s_print(name, "objp->%s", dec->name); + } else { + s_print(name, "&objp->%s", dec->name); + } + print_ifstat(indent, prefix, type, rel, amax, name, dec->name); +} + + +void +emit_inline(declaration *decl, int flag) +{ + +/*check whether an array or not */ + + switch (decl->rel) { + case REL_ALIAS: + emit_single_in_line(decl, flag, REL_ALIAS); + break; + case REL_VECTOR: + f_print(fout, "\t\t\t{\n"); + f_print(fout, "\t\t\t\tint i;\n"); + f_print(fout, "\t\t\t\t%s *genp;\n", decl->type); + f_print(fout, "\n"); + f_print(fout, "\t\t\t\tfor (i = 0, genp = objp->%s;\n", + decl->name); + f_print(fout, "\t\t\t\t i < %s; i++) {\n\t\t", + decl->array_max); + emit_single_in_line(decl, flag, REL_VECTOR); + f_print(fout, "\t\t\t\t}\n\t\t\t}\n"); + break; + case REL_ARRAY: + case REL_POINTER: + errx(1, "Internal error at %s:%d: Case %d not handled", + __FILE__, __LINE__, decl->rel); + } +} + +void +emit_single_in_line(declaration *decl, int flag, relation rel) +{ + const char *upp_case; + char *freeable; + int freed = 0; + + if (flag == PUT) + f_print(fout, "\t\t\tIXDR_PUT_"); + else + if (rel == REL_ALIAS) + f_print(fout, "\t\t\tobjp->%s = IXDR_GET_", decl->name); + else + f_print(fout, "\t\t\t*genp++ = IXDR_GET_"); + + upp_case = freeable = upcase(decl->type); + + /* hack - XX */ + if (strcmp(upp_case, "INT") == 0) { + free(freeable); + freed = 1; + upp_case = "INT32"; + } else if (strcmp(upp_case, "U_INT") == 0) { + free(freeable); + freed = 1; + upp_case = "U_INT32"; + } + if (flag == PUT) { + if (rel == REL_ALIAS) + f_print(fout, "%s(buf, objp->%s);\n", upp_case, decl->name); + else + f_print(fout, "%s(buf, *genp++);\n", upp_case); + + } else + f_print(fout, "%s(buf);\n", upp_case); + if (!freed) + free(freeable); + +} + + +char * +upcase(const char *str) +{ + char *ptr, *hptr; + + + ptr = malloc(strlen(str) + 1); + if (ptr == NULL) { + errx(EXIT_FAILURE, "Out of memory"); + } + + hptr = ptr; + while (*str != '\0') + *ptr++ = toupper((unsigned char)*str++); + + *ptr = '\0'; + return (hptr); + +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_hout.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_hout.c new file mode 100644 index 00000000..741f00cd --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_hout.c @@ -0,0 +1,542 @@ +/* $NetBSD: rpc_hout.c,v 1.23 2015/05/09 23:12:57 dholland Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_hout.c 1.12 89/02/22 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_hout.c,v 1.23 2015/05/09 23:12:57 dholland Exp $"); +#endif +#endif + +/* + * rpc_hout.c, Header file outputter for the RPC protocol compiler + */ +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +static void pconstdef(definition *); +static void pargdef(definition *); +static void pstructdef(definition *); +static void puniondef(definition *); +static void pdefine(const char *, const char *); +static void puldefine(const char *, const char *); +static int define_printed(proc_list *, version_list *); +static void pprogramdef(definition *); +static void penumdef(definition *); +static void ptypedef(definition *); +static int undefined2(const char *, const char *); +static void cplusplusstart(void); +static void cplusplusend(void); + +/* + * Print the C-version of an xdr definition + */ +void +print_datadef(definition *def) +{ + + if (def->def_kind == DEF_PROGRAM) /* handle data only */ + return; + + if (def->def_kind != DEF_CONST) { + f_print(fout, "\n"); + } + switch (def->def_kind) { + case DEF_STRUCT: + pstructdef(def); + break; + case DEF_UNION: + puniondef(def); + break; + case DEF_ENUM: + penumdef(def); + break; + case DEF_TYPEDEF: + ptypedef(def); + break; + case DEF_PROGRAM: + pprogramdef(def); + break; + case DEF_CONST: + pconstdef(def); + break; + } +} + +void +print_progdef(definition *def) +{ + switch (def->def_kind) { + case DEF_PROGRAM: + f_print(fout, "\n"); + pprogramdef(def); + break; + case DEF_CONST: + case DEF_TYPEDEF: + case DEF_ENUM: + case DEF_UNION: + case DEF_STRUCT: + break; + } +} + +void +print_funcdef(definition *def, int *did) +{ + switch (def->def_kind) { + case DEF_PROGRAM: + case DEF_CONST: + break; + case DEF_TYPEDEF: + case DEF_ENUM: + case DEF_UNION: + case DEF_STRUCT: + if (!*did) { + f_print(fout, "\n"); + cplusplusstart(); + *did = 1; + } + pxdrfuncdecl(def->def_name, + def->def_kind != DEF_TYPEDEF || + !isvectordef(def->def.ty.old_type, def->def.ty.rel)); + break; + } +} + +void +print_funcend(int did) { + if (did) { + cplusplusend(); + } +} + +void +pxdrfuncdecl(const char *name, int pointerp) +{ + + f_print(fout, "bool_t xdr_%s(XDR *, %s%s);\n", name, + name, pointerp ? (" *") : ""); +} + + +static void +pconstdef(definition *def) +{ + pdefine(def->def_name, def->def.co); +} + +/* print out the definitions for the arguments of functions in the + header file +*/ +static void +pargdef(definition *def) +{ + decl_list *l; + version_list *vers; + char *name; + proc_list *plist; + int did; + + + for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) { + for (plist = vers->procs; plist != NULL; plist = plist->next) { + if (!newstyle || plist->arg_num < 2) { + continue; /* old style or single args */ + } + name = plist->args.argname; + f_print(fout, "struct %s {\n", name); + for (l = plist->args.decls; + l != NULL; l = l->next) { + pdeclaration(name, &l->decl, 1, ";\n"); + } + f_print(fout, "};\n"); + f_print(fout, "typedef struct %s %s;\n", name, name); + } + } + did = 0; + for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) { + if (!newstyle || plist->arg_num < 2) { + continue; /* old style or single args */ + } + for (plist = vers->procs; plist != NULL; plist = plist->next) { + if (!did) { + cplusplusstart(); + did = 1; + } + pxdrfuncdecl(plist->args.argname, 1); + } + } + if (did) { + cplusplusend(); + } + +} + + +static void +pstructdef(definition *def) +{ + decl_list *l; + const char *name = def->def_name; + + f_print(fout, "struct %s {\n", name); + for (l = def->def.st.decls; l != NULL; l = l->next) { + pdeclaration(name, &l->decl, 1, ";\n"); + } + f_print(fout, "};\n"); + f_print(fout, "typedef struct %s %s;\n", name, name); +} + +static void +puniondef(definition *def) +{ + case_list *l; + const char *name = def->def_name; + declaration *decl; + + f_print(fout, "struct %s {\n", name); + decl = &def->def.un.enum_decl; + if (streq(decl->type, "bool")) { + f_print(fout, "\tbool_t %s;\n", decl->name); + } else { + f_print(fout, "\t%s %s;\n", decl->type, decl->name); + } + f_print(fout, "\tunion {\n"); + for (l = def->def.un.cases; l != NULL; l = l->next) { + if (l->contflag == 0) + pdeclaration(name, &l->case_decl, 2, ";\n"); + } + decl = def->def.un.default_decl; + if (decl && !streq(decl->type, "void")) { + pdeclaration(name, decl, 2, ";\n"); + } + f_print(fout, "\t} %s_u;\n", name); + f_print(fout, "};\n"); + f_print(fout, "typedef struct %s %s;\n", name, name); +} + +static void +pdefine(const char *name, const char *num) +{ + f_print(fout, "#define %s %s\n", name, num); +} + +static void +puldefine(const char *name, const char *num) +{ + f_print(fout, "#define %s %s\n", name, num); +} + +static int +define_printed(proc_list *stop, version_list *start) +{ + version_list *vers; + proc_list *proc; + + for (vers = start; vers != NULL; vers = vers->next) { + for (proc = vers->procs; proc != NULL; proc = proc->next) { + if (proc == stop) { + return (0); + } else + if (streq(proc->proc_name, stop->proc_name)) { + return (1); + } + } + } + errx(1, "Internal error at %s:%d: procedure not found", + __FILE__, __LINE__); + /* NOTREACHED */ +} + +static void +cplusplusstart(void) +{ + if (BSDflag) + f_print(fout, "__BEGIN_DECLS\n"); + else + f_print(fout, "#ifdef __cplusplus\nextern \"C\" {\n#endif\n"); +} + +static void +cplusplusend(void) +{ + if (BSDflag) + f_print(fout, "__END_DECLS\n"); + else + f_print(fout, "#ifdef __cplusplus\n};\n#endif\n"); +} + +static void +pprogramdef(definition *def) +{ + version_list *vers; + proc_list *proc; + + pargdef(def); + + puldefine(def->def_name, def->def.pr.prog_num); + for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) { + if (tblflag) { + f_print(fout, "extern struct rpcgen_table %s_%s_table[];\n", + locase(def->def_name), vers->vers_num); + f_print(fout, "extern %s_%s_nproc;\n", + locase(def->def_name), vers->vers_num); + } + puldefine(vers->vers_name, vers->vers_num); + for (proc = vers->procs; proc != NULL; proc = proc->next) { + if (!define_printed(proc, def->def.pr.versions)) { + puldefine(proc->proc_name, proc->proc_num); + } + } + } + + /* + * Print out 3 definitions, one for ANSI-C, another for C++, a + * third for old style C + */ + f_print(fout, "\n"); + cplusplusstart(); + for (vers = def->def.pr.versions; vers != NULL; vers = vers->next) { + for (proc = vers->procs; proc != NULL; proc = proc->next) { + pprocdef(proc, vers, "CLIENT *", 0); + pprocdef(proc, vers, "struct svc_req *", 1); + } + } + cplusplusend(); +} + +void +pprocdef(proc_list *proc, version_list *vp, const char *addargtype, + int server_p) +{ + decl_list *dl; + + if (Mflag) { + if (server_p) + f_print(fout, "bool_t "); + else + f_print(fout, "enum clnt_stat "); + } else { + ptype(proc->res_prefix, proc->res_type, 1); + f_print(fout, "*"); + } + if (server_p) + pvname_svc(proc->proc_name, vp->vers_num); + else + pvname(proc->proc_name, vp->vers_num); + + f_print(fout, "("); + if (proc->arg_num < 2 && newstyle && + streq(proc->args.decls->decl.type, "void")) { + /* 0 argument in new style: do nothing */ + } else { + for (dl = proc->args.decls; dl != NULL; dl = dl->next) { + ptype(dl->decl.prefix, dl->decl.type, 1); + if (!newstyle) + f_print(fout, "*"); + f_print(fout, ", "); + } + } + if (Mflag) { + if (streq(proc->res_type, "void")) + f_print(fout, "char"); + else + ptype(proc->res_prefix, proc->res_type, 0); + if (!isvectordef(proc->res_type, REL_ALIAS)) + f_print(fout, "*"); + f_print(fout, ", "); + } + f_print(fout, "%s);\n", addargtype); +} + + +static void +penumdef(definition *def) +{ + const char *name = def->def_name; + enumval_list *l; + const char *last = NULL; + int count = 0; + const char *first = ""; + + f_print(fout, "enum %s {\n", name); + for (l = def->def.en.vals; l != NULL; l = l->next) { + f_print(fout, "%s\t%s", first, l->name); + if (l->assignment) { + f_print(fout, " = %s", l->assignment); + last = l->assignment; + count = 1; + } else { + if (last == NULL) { + f_print(fout, " = %d", count++); + } else { + f_print(fout, " = %s + %d", last, count++); + } + } + first = ",\n"; + } + f_print(fout, "\n};\n"); + f_print(fout, "typedef enum %s %s;\n", name, name); +} + +static void +ptypedef(definition *def) +{ + const char *name = def->def_name; + const char *old = def->def.ty.old_type; + char prefix[8]; /* enough to contain "struct ", including NUL */ + relation rel = def->def.ty.rel; + + + if (!streq(name, old)) { + if (streq(old, "string")) { + old = "char"; + rel = REL_POINTER; + } else + if (streq(old, "opaque")) { + old = "char"; + } else + if (streq(old, "bool")) { + old = "bool_t"; + } + if (undefined2(old, name) && def->def.ty.old_prefix) { + s_print(prefix, "%s ", def->def.ty.old_prefix); + } else { + prefix[0] = 0; + } + f_print(fout, "typedef "); + switch (rel) { + case REL_ARRAY: + f_print(fout, "struct {\n"); + f_print(fout, "\tu_int %s_len;\n", name); + f_print(fout, "\t%s%s *%s_val;\n", prefix, old, name); + f_print(fout, "} %s", name); + break; + case REL_POINTER: + f_print(fout, "%s%s *%s", prefix, old, name); + break; + case REL_VECTOR: + f_print(fout, "%s%s %s[%s]", prefix, old, name, + def->def.ty.array_max); + break; + case REL_ALIAS: + f_print(fout, "%s%s %s", prefix, old, name); + break; + } + f_print(fout, ";\n"); + } +} + +void +pdeclaration(const char *name, declaration *dec, int tab, + const char *separator) +{ + char buf[8]; /* enough to hold "struct ", include NUL */ + const char *prefix; + const char *type; + + if (streq(dec->type, "void")) { + return; + } + tabify(fout, tab); + if (streq(dec->type, name) && !dec->prefix) { + f_print(fout, "struct "); + } + if (streq(dec->type, "string")) { + f_print(fout, "char *%s", dec->name); + } else { + prefix = ""; + if (streq(dec->type, "bool")) { + type = "bool_t"; + } else + if (streq(dec->type, "opaque")) { + type = "char"; + } else { + if (dec->prefix) { + s_print(buf, "%s ", dec->prefix); + prefix = buf; + } + type = dec->type; + } + switch (dec->rel) { + case REL_ALIAS: + f_print(fout, "%s%s %s", prefix, type, dec->name); + break; + case REL_VECTOR: + f_print(fout, "%s%s %s[%s]", prefix, type, dec->name, + dec->array_max); + break; + case REL_POINTER: + f_print(fout, "%s%s *%s", prefix, type, dec->name); + break; + case REL_ARRAY: + f_print(fout, "struct {\n"); + tabify(fout, tab); + f_print(fout, "\tu_int %s_len;\n", dec->name); + tabify(fout, tab); + f_print(fout, "\t%s%s *%s_val;\n", prefix, type, dec->name); + tabify(fout, tab); + f_print(fout, "} %s", dec->name); + break; + } + } + f_print(fout, "%s", separator); +} + +static int +undefined2(const char *type, const char *stop) +{ + list *l; + definition *def; + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + if (streq(def->def_name, stop)) { + return (1); + } else + if (streq(def->def_name, type)) { + return (0); + } + } + } + return (1); +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_main.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_main.c new file mode 100644 index 00000000..1e660453 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_main.c @@ -0,0 +1,1137 @@ +/* $NetBSD: rpc_main.c,v 1.42 2015/05/09 23:12:57 dholland Exp $ */ + +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_main.c 1.30 89/03/30 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_main.c,v 1.42 2015/05/09 23:12:57 dholland Exp $"); +#endif +#endif + +/* + * rpc_main.c, Top level of the RPC protocol compiler. + */ + +#define RPCGEN_VERSION "199506"/* This program's version (year & month) */ + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/file.h> +#include <sys/stat.h> +#include <ctype.h> +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +#define EXTEND 1 /* alias for TRUE */ +#define DONT_EXTEND 0 /* alias for FALSE */ + +struct commandline { + int cflag; /* xdr C routines */ + int hflag; /* header file */ + int lflag; /* client side stubs */ + int mflag; /* server side stubs */ + int nflag; /* netid flag */ + int sflag; /* server stubs for the given transport */ + int tflag; /* dispatch Table file */ + int Ssflag; /* produce server sample code */ + int Scflag; /* produce client sample code */ + char *infile; /* input module name */ + char *outfile; /* output module name */ +}; + + +static char *cmdname; + +static const char *svcclosetime = "120"; +static const char *CPP; +static char CPPFLAGS[] = "-C"; +static char pathbuf[MAXPATHLEN + 1]; + +/* these cannot be const */ +static char allv0[] = "rpcgen"; +static char allv1[] = "-s"; +static char allv2[] = "udp"; +static char allv3[] = "-s"; +static char allv4[] = "tcp"; +static char *allv[] = { + allv0, + allv1, + allv2, + allv3, + allv4, +}; +static int allc = sizeof(allv) / sizeof(allv[0]); + +/* these cannot be const */ +static char allnv0[] = "rpcgen"; +static char allnv1[] = "-s"; +static char allnv2[] = "netpath"; +static char *allnv[] = { + allnv0, + allnv1, + allnv2, +}; +static int allnc = sizeof(allnv) / sizeof(allnv[0]); + +#define ARGLISTLEN 20 +#define FIXEDARGS 2 + +static const char *arglist[ARGLISTLEN]; +static int argcount = FIXEDARGS; + + +int nonfatalerrors; /* errors */ +int inetdflag /* = 1 */ ; /* Support for inetd *//* is now the default */ +int pmflag; /* Support for port monitors */ +int logflag; /* Use syslog instead of fprintf for errors */ +int tblflag; /* Support for dispatch table file */ +int BSDflag; /* use BSD cplusplus macros */ +int callerflag; /* Generate svc_caller() function */ +int docleanup = 1; /* cause atexit to remove files */ + +#define INLINE 3 +/*length at which to start doing an inline */ + +int doinline = INLINE; /* length at which to start doing an inline. 3 + * = default if 0, no xdr_inline code */ + +int indefinitewait; /* If started by port monitors, hang till it + * wants */ +int exitnow; /* If started by port monitors, exit after the + * call */ +int timerflag; /* TRUE if !indefinite && !exitnow */ +int newstyle; /* newstyle of passing arguments (by value) */ +int Mflag = 0; /* multithread safe */ +static int allfiles; /* generate all files */ +int tirpcflag = 1; /* generating code for tirpc, by default */ + +#ifdef __MSDOS__ +static char *dos_cppfile = NULL; +#endif + +static char *extendfile(const char *, const char *); +static void open_output(const char *, const char *); +static void add_warning(void); +static void clear_args(void); +static void open_input(const char *, const char *); +static int check_nettype(const char *, const char *[]); +static void c_output(const char *, const char *, int, const char *); +static void c_initialize(void); +static char *generate_guard(const char *); +static void h_output(const char *, const char *, int, const char *); +static void s_output(int, char *[], char *, const char *, int, const char *, + int, int); +static void l_output(const char *, const char *, int, const char *); +static void t_output(const char *, const char *, int, const char *); +static void svc_output(const char *, const char *, int, const char *); +static void clnt_output(const char *, const char *, int, const char *); +static int do_registers(int, char *[]); +static void addarg(const char *); +static void putarg(int, const char *); +static void checkfiles(const char *, const char *); +static int parseargs(int, char *[], struct commandline *); +static void usage(void) __dead; +static void options_usage(void) __dead; + +int +main(int argc, char *argv[]) +{ + struct commandline cmd; + + setprogname(argv[0]); + if ((CPP = getenv("RPCGEN_CPP")) == NULL) { + CPP = "/usr/bin/cpp"; + if (access(CPP, X_OK)) + CPP = "/usr/bin/clang-cpp"; + } + + (void) memset((char *) &cmd, 0, sizeof(struct commandline)); + clear_args(); + atexit(crash); + if (!parseargs(argc, argv, &cmd)) + usage(); + + if (cmd.cflag || cmd.hflag || cmd.lflag || cmd.tflag || cmd.sflag || + cmd.mflag || cmd.nflag || cmd.Ssflag || cmd.Scflag) { + checkfiles(cmd.infile, cmd.outfile); + } else + checkfiles(cmd.infile, NULL); + + if (cmd.cflag) { + c_output(cmd.infile, "-DRPC_XDR", DONT_EXTEND, cmd.outfile); + } else + if (cmd.hflag) { + h_output(cmd.infile, "-DRPC_HDR", DONT_EXTEND, cmd.outfile); + } else + if (cmd.lflag) { + l_output(cmd.infile, "-DRPC_CLNT", DONT_EXTEND, cmd.outfile); + } else + if (cmd.sflag || cmd.mflag || (cmd.nflag)) { + s_output(argc, argv, cmd.infile, "-DRPC_SVC", DONT_EXTEND, + cmd.outfile, cmd.mflag, cmd.nflag); + } else + if (cmd.tflag) { + t_output(cmd.infile, "-DRPC_TBL", DONT_EXTEND, cmd.outfile); + } else + if (cmd.Ssflag) { + svc_output(cmd.infile, "-DRPC_SERVER", DONT_EXTEND, cmd.outfile); + } else + if (cmd.Scflag) { + clnt_output(cmd.infile, "-DRPC_CLIENT", DONT_EXTEND, cmd.outfile); + } else { + /* the rescans + * are + * required, + * since cpp + * may effect + * input */ + c_output(cmd.infile, "-DRPC_XDR", EXTEND, "_xdr.c"); + reinitialize(); + h_output(cmd.infile, "-DRPC_HDR", EXTEND, ".h"); + reinitialize(); + l_output(cmd.infile, "-DRPC_CLNT", EXTEND, "_clnt.c"); + reinitialize(); + if (inetdflag || !tirpcflag) + s_output(allc, allv, cmd.infile, "-DRPC_SVC", EXTEND, + "_svc.c", cmd.mflag, cmd.nflag); + else + s_output(allnc, allnv, cmd.infile, "-DRPC_SVC", + EXTEND, "_svc.c", cmd.mflag, cmd.nflag); + if (tblflag) { + reinitialize(); + t_output(cmd.infile, "-DRPC_TBL", EXTEND, "_tbl.i"); + } + if (allfiles) { + reinitialize(); + svc_output(cmd.infile, "-DRPC_SERVER", EXTEND, "_server.c"); + } + if (allfiles) { + reinitialize(); + clnt_output(cmd.infile, "-DRPC_CLIENT", EXTEND, "_client.c"); + } + } +#ifdef __MSDOS__ + if (dos_cppfile != NULL) { + (void) fclose(fin); + (void) unlink(dos_cppfile); + } +#endif + docleanup = 0; + exit(nonfatalerrors); + /* NOTREACHED */ +} +/* + * add extension to filename + */ +static char * +extendfile(const char *path, const char *ext) +{ + const char *file; + char *res; + const char *p; + + if ((file = strrchr(path, '/')) == NULL) + file = path; + else + file++; + + res = alloc(strlen(file) + strlen(ext) + 1); + if (res == NULL) { + errx(1, "Out of memory"); + } + p = strrchr(file, '.'); + if (p == NULL) { + p = file + strlen(file); + } + (void) strcpy(res, file); + (void) strcpy(res + (p - file), ext); + return (res); +} +/* + * Open output file with given extension + */ +static void +open_output(const char *infile, const char *outfile) +{ + + if (outfile == NULL) { + fout = stdout; + return; + } + if (infile != NULL && streq(outfile, infile)) { + errx(EXIT_FAILURE, "Output would overwrite `%s'", infile); + } + fout = fopen(outfile, "w"); + if (fout == NULL) { + err(EXIT_FAILURE, "Can't open `%s'", outfile); + } + record_open(outfile); + +} + +static void +add_warning(void) +{ + f_print(fout, "/*\n"); + f_print(fout, " * Please do not edit this file.\n"); + f_print(fout, " * It was generated using rpcgen.\n"); + f_print(fout, " */\n\n"); +} + +/* clear list of arguments */ +static void +clear_args(void) +{ + int i; + for (i = FIXEDARGS; i < ARGLISTLEN; i++) + arglist[i] = NULL; + argcount = FIXEDARGS; +} + +/* + * Open input file with given define for C-preprocessor + */ +static void +open_input(const char *infile, const char *define) +{ + int pd[2]; + + infilename = (infile == NULL) ? "<stdin>" : infile; +#ifdef __MSDOS__ +#define DOSCPP "\\prog\\bc31\\bin\\cpp.exe" + { + int retval; + char drive[MAXDRIVE], dir[MAXDIR], name[MAXFILE], ext[MAXEXT]; + char cppfile[MAXPATH]; + char *cpp; + + if ((cpp = getenv("RPCGEN_CPP")) == NULL && + (cpp = searchpath("cpp.exe")) == NULL) + cpp = DOSCPP; + + putarg(0, cpp); + putarg(1, "-P-"); + putarg(2, CPPFLAGS); + addarg(define); + addarg(infile); + addarg(NULL); + + retval = spawnvp(P_WAIT, arglist[0], arglist); + if (retval != 0) { + err(EXIT_FAILURE, "C preprocessor failed"); + } + fnsplit(infile, drive, dir, name, ext); + fnmerge(cppfile, drive, dir, name, ".i"); + + fin = fopen(cppfile, "r"); + if (fin == NULL) { + err(EXIT_FAILURE, "Can't open `%s'", cppfile); + } + dos_cppfile = strdup(cppfile); + if (dos_cppfile == NULL) { + err(EXIT_FAILURE, "Can't copy `%s'", cppfile); + } + } +#else + (void) pipe(pd); + switch (fork()) { + case 0: + putarg(0, CPP); + putarg(1, CPPFLAGS); + addarg(define); + addarg(infile); + addarg(NULL); + (void) close(1); + (void) dup2(pd[1], 1); + (void) close(pd[0]); + execvp(arglist[0], __UNCONST(arglist)); + err(EXIT_FAILURE, "$RPCGEN_CPP: %s", CPP); + case -1: + err(EXIT_FAILURE, "fork"); + } + (void) close(pd[1]); + fin = fdopen(pd[0], "r"); +#endif + if (fin == NULL) { + err(EXIT_FAILURE, "Can't open `%s'", infilename); + } +} +/* valid tirpc nettypes */ +static const char *valid_ti_nettypes[] = +{ + "netpath", + "visible", + "circuit_v", + "datagram_v", + "circuit_n", + "datagram_n", + "udp", + "tcp", + "raw", + NULL +}; +/* valid inetd nettypes */ +static const char *valid_i_nettypes[] = +{ + "udp", + "tcp", + NULL +}; + +static int +check_nettype(const char *name, const char *list_to_check[]) +{ + int i; + for (i = 0; list_to_check[i] != NULL; i++) { + if (strcmp(name, list_to_check[i]) == 0) { + return 1; + } + } + f_print(stderr, "illegal nettype :\'%s\'\n", name); + return 0; +} +/* + * Compile into an XDR routine output file + */ + +static void +c_output(const char *infile, const char *define, int extend, + const char *outfile) +{ + definition *def; + char *include; + const char *outfilename; + long tell; + + c_initialize(); + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + add_warning(); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + /* .h file already contains rpc/rpc.h */ + } else + f_print(fout, "#include <rpc/rpc.h>\n"); + tell = ftell(fout); + while ((def = get_definition()) != NULL) { + emit(def); + } + if (extend && tell == ftell(fout)) { + (void) unlink(outfilename); + } +} + + +static void +c_initialize(void) +{ + + /* add all the starting basic types */ + + add_type(1, "int"); + add_type(1, "long"); + add_type(1, "short"); + add_type(1, "bool"); + + add_type(1, "u_int"); + add_type(1, "u_long"); + add_type(1, "u_short"); + +} + +const char rpcgen_table_dcl[] = "struct rpcgen_table {\n\ + char *(*proc)();\n\ + xdrproc_t xdr_arg;\n\ + unsigned len_arg;\n\ + xdrproc_t xdr_res;\n\ + unsigned len_res;\n\ +};\n"; + + +static char * +generate_guard(const char *pathname) +{ + const char *filename; + char *guard, *tmp, *tmp2; + + filename = strrchr(pathname, '/'); /* find last component */ + filename = ((filename == 0) ? pathname : filename + 1); + guard = strdup(filename); + /* convert to upper case */ + tmp = guard; + while (*tmp) { + *tmp = toupper((unsigned char)*tmp); + tmp++; + } + + tmp2 = extendfile(guard, "_H_RPCGEN"); + free(guard); + guard = tmp2; + return (guard); +} + +/* + * Compile into an XDR header file + */ +static void +h_output(const char *infile, const char *define, int extend, + const char *outfile) +{ + definition *def; + const char *outfilename; + long tell; + char *guard; + list *l; + int did; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + add_warning(); + if (outfilename || infile) + guard = generate_guard(outfilename ? outfilename : infile); + else { + guard = strdup("STDIN_"); + if (guard == NULL) { + err(EXIT_FAILURE, "strdup"); + } + } + + f_print(fout, "#ifndef _%s\n#define _%s\n\n", guard, + guard); + + f_print(fout, "#define RPCGEN_VERSION\t%s\n\n", RPCGEN_VERSION); + f_print(fout, "#include <rpc/rpc.h>\n\n"); + + tell = ftell(fout); + /* print data definitions */ + while ((def = get_definition()) != NULL) { + print_datadef(def); + } + + /* print function declarations. Do this after data definitions + * because they might be used as arguments for functions */ + did = 0; + for (l = defined; l != NULL; l = l->next) { + print_funcdef(l->val, &did); + } + print_funcend(did); + + for (l = defined; l != NULL; l = l->next) { + print_progdef(l->val); + } + + if (extend && tell == ftell(fout)) { + (void) unlink(outfilename); + } else + if (tblflag) { + f_print(fout, rpcgen_table_dcl); + } + f_print(fout, "\n#endif /* !_%s */\n", guard); + + free(guard); +} + +/* + * Compile into an RPC service + */ +static void +s_output(int argc, char *argv[], char *infile, + const char *define, int extend, const char *outfile, int nomain, + int netflag) +{ + char *include; + definition *def; + int foundprogram = 0; + const char *outfilename; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + add_warning(); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } else + f_print(fout, "#include <rpc/rpc.h>\n"); + + f_print(fout, "#include <sys/ioctl.h>\n"); + f_print(fout, "#include <fcntl.h>\n"); + f_print(fout, "#include <stdio.h>\n"); + f_print(fout, "#include <err.h>\n"); + f_print(fout, "#include <stdlib.h>\n"); + f_print(fout, "#include <unistd.h>\n"); + f_print(fout, "#include <rpc/pmap_clnt.h>\n"); + f_print(fout, "#include <string.h>\n"); + f_print(fout, "#include <netdb.h>\n"); + if (strcmp(svcclosetime, "-1") == 0) + indefinitewait = 1; + else + if (strcmp(svcclosetime, "0") == 0) + exitnow = 1; + else + if (inetdflag || pmflag) { + f_print(fout, "#include <signal.h>\n"); + timerflag = 1; + } + if (!tirpcflag && inetdflag) + f_print(fout, "#include <sys/ttycom.h>\n"); + if (inetdflag || pmflag) { + f_print(fout, "#ifdef __cplusplus\n"); + f_print(fout, "#include <sysent.h>\n"); + f_print(fout, "#endif /* __cplusplus */\n"); + } + if (tirpcflag) + f_print(fout, "#include <sys/types.h>\n"); + + f_print(fout, "#include <memory.h>\n"); + + if (inetdflag || !tirpcflag) { + f_print(fout, "#include <sys/socket.h>\n"); + f_print(fout, "#include <netinet/in.h>\n"); + } + if ((netflag || pmflag) && tirpcflag) { + f_print(fout, "#include <netconfig.h>\n"); + } + if ( /* timerflag && */ tirpcflag) + f_print(fout, "#include <sys/resource.h>\n"); + if (logflag || inetdflag || pmflag) + f_print(fout, "#include <syslog.h>\n"); + + f_print(fout, "\n#define SIG_PF void(*)(int)\n"); + + f_print(fout, "\n#ifdef DEBUG\n#define RPC_SVC_FG\n#endif\n"); + if (timerflag) + f_print(fout, "\n#define _RPCSVC_CLOSEDOWN %s\n", svcclosetime); + while ((def = get_definition()) != NULL) { + foundprogram |= (def->def_kind == DEF_PROGRAM); + } + if (extend && !foundprogram) { + (void) unlink(outfilename); + return; + } + if (callerflag) /* EVAS */ + f_print(fout, "\nstatic SVCXPRT *caller;\n"); /* EVAS */ + write_most(infile, netflag, nomain); + if (!nomain) { + if (!do_registers(argc, argv)) { + if (outfilename) + (void) unlink(outfilename); + usage(); + } + write_rest(); + } +} +/* + * generate client side stubs + */ +static void +l_output(const char *infile, const char *define, int extend, + const char *outfile) +{ + char *include; + definition *def; + int foundprogram = 0; + const char *outfilename; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + add_warning(); + f_print(fout, "#include <memory.h>\n"); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } else + f_print(fout, "#include <rpc/rpc.h>\n"); + while ((def = get_definition()) != NULL) { + foundprogram |= (def->def_kind == DEF_PROGRAM); + } + if (extend && !foundprogram) { + (void) unlink(outfilename); + return; + } + write_stubs(); +} +/* + * generate the dispatch table + */ +static void +t_output(const char *infile, const char *define, int extend, + const char *outfile) +{ + definition *def; + int foundprogram = 0; + const char *outfilename; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + open_output(infile, outfilename); + add_warning(); + while ((def = get_definition()) != NULL) { + foundprogram |= (def->def_kind == DEF_PROGRAM); + } + if (extend && !foundprogram) { + (void) unlink(outfilename); + return; + } + write_tables(); +} +/* sample routine for the server template */ +static void +svc_output(const char *infile, const char *define, int extend, + const char *outfile) +{ + definition *def; + char *include; + const char *outfilename; + long tell; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + checkfiles(infile, outfilename); /* check if outfile already + * exists. if so, print an + * error message and exit */ + open_output(infile, outfilename); + add_sample_msg(); + + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } else + f_print(fout, "#include <rpc/rpc.h>\n"); + + tell = ftell(fout); + while ((def = get_definition()) != NULL) { + write_sample_svc(def); + } + if (extend && tell == ftell(fout)) { + (void) unlink(outfilename); + } +} + + +/* sample main routine for client */ +static void +clnt_output(const char *infile, const char *define, int extend, + const char *outfile) +{ + definition *def; + char *include; + const char *outfilename; + long tell; + int has_program = 0; + + open_input(infile, define); + outfilename = extend ? extendfile(infile, outfile) : outfile; + checkfiles(infile, outfilename); /* check if outfile already + * exists. if so, print an + * error message and exit */ + + open_output(infile, outfilename); + add_sample_msg(); + f_print(fout, "#include <stdio.h>\n"); + f_print(fout, "#include <err.h>\n"); + if (infile && (include = extendfile(infile, ".h"))) { + f_print(fout, "#include \"%s\"\n", include); + free(include); + } else + f_print(fout, "#include <rpc/rpc.h>\n"); + tell = ftell(fout); + while ((def = get_definition()) != NULL) { + has_program += write_sample_clnt(def); + } + + if (has_program) + write_sample_clnt_main(); + + if (extend && tell == ftell(fout)) { + (void) unlink(outfilename); + } +} +/* + * Perform registrations for service output + * Return 0 if failed; 1 otherwise. + */ +static int +do_registers(int argc, char *argv[]) +{ + int i; + + if (inetdflag || !tirpcflag) { + for (i = 1; i < argc; i++) { + if (streq(argv[i], "-s")) { + if (!check_nettype(argv[i + 1], valid_i_nettypes)) + return 0; + write_inetd_register(argv[i + 1]); + i++; + } + } + } else { + for (i = 1; i < argc; i++) + if (streq(argv[i], "-s")) { + if (!check_nettype(argv[i + 1], valid_ti_nettypes)) + return 0; + write_nettype_register(argv[i + 1]); + i++; + } else + if (streq(argv[i], "-n")) { + write_netid_register(argv[i + 1]); + i++; + } + } + return 1; +} +/* + * Add another argument to the arg list + */ +static void +addarg(const char *cp) +{ + if (argcount >= ARGLISTLEN) { + errx(EXIT_FAILURE, "Internal error: too many defines"); + /* NOTREACHED */ + } + arglist[argcount++] = cp; + +} + +static void +putarg(int pwhere, const char *cp) +{ + if (pwhere >= ARGLISTLEN) { + errx(EXIT_FAILURE, "Internal error: arglist coding error"); + /* NOTREACHED */ + } + arglist[pwhere] = cp; + +} +/* + * if input file is stdin and an output file is specified then complain + * if the file already exists. Otherwise the file may get overwritten + * If input file does not exist, exit with an error + */ + +static void +checkfiles(const char *infile, const char *outfile) +{ + + struct stat buf; + + if (infile) /* infile ! = NULL */ + if (stat(infile, &buf) < 0) { + err(EXIT_FAILURE, "Can't stat `%s'", infile); + }; +#if 0 + if (outfile) { + if (stat(outfile, &buf) < 0) + return; /* file does not exist */ + else { + errx(EXIT_FAILURE, + "`%s' already exists and would be overwritten", + outfile); + } + } +#endif +} +/* + * Parse command line arguments + */ +static int +parseargs(int argc, char *argv[], struct commandline *cmd) +{ + int i; + int j; + int c; + char flag[1 << CHAR_BIT]; + int nflags; + + cmdname = argv[0]; + cmd->infile = cmd->outfile = NULL; + if (argc < 2) { + return (0); + } + allfiles = 0; + flag['c'] = 0; + flag['h'] = 0; + flag['l'] = 0; + flag['m'] = 0; + flag['o'] = 0; + flag['s'] = 0; + flag['n'] = 0; + flag['t'] = 0; + flag['S'] = 0; + flag['C'] = 0; + for (i = 1; i < argc; i++) { + if (argv[i][0] != '-') { + if (cmd->infile) { + f_print(stderr, "Cannot specify more than one input file!\n"); + + return (0); + } + cmd->infile = argv[i]; + } else { + for (j = 1; argv[i][j] != 0; j++) { + c = argv[i][j]; + switch (c) { + case 'A': + callerflag = 1; + break; + case 'a': + allfiles = 1; + break; + case 'B': + BSDflag = 1; + break; + case 'c': + case 'h': + case 'l': + case 'm': + case 't': + if (flag[c]) { + return (0); + } + flag[c] = 1; + break; + case 'S': + /* sample flag: Ss or Sc. Ss means set + * flag['S']; Sc means set flag['C']; */ + c = argv[i][++j]; /* get next char */ + if (c == 's') + c = 'S'; + else + if (c == 'c') + c = 'C'; + else + return (0); + + if (flag[c]) { + return (0); + } + flag[c] = 1; + break; + case 'C': /* deprecated ANSI C syntax */ + break; + + case 'b': /* turn TIRPC flag off for + * generating backward + * compatible */ + tirpcflag = 0; + break; + + case 'I': + inetdflag = 1; + break; + case 'M': + Mflag = 1; + break; + case 'N': + newstyle = 1; + break; + case 'L': + logflag = 1; + break; + case 'K': + if (++i == argc) { + return (0); + } + svcclosetime = argv[i]; + goto nextarg; + case 'T': + tblflag = 1; + break; + case 'i': + if (++i == argc) { + return (0); + } + doinline = atoi(argv[i]); + goto nextarg; + case 'n': + case 'o': + case 's': + if (argv[i][j - 1] != '-' || + argv[i][j + 1] != 0) { + return (0); + } + flag[c] = 1; + if (++i == argc) { + return (0); + } + if (c == 's') { + if (!streq(argv[i], "udp") && + !streq(argv[i], "tcp")) { + return (0); + } + } else + if (c == 'o') { + if (cmd->outfile) { + return (0); + } + cmd->outfile = argv[i]; + } + goto nextarg; + case 'D': + if (argv[i][j - 1] != '-') { + return (0); + } + (void) addarg(argv[i]); + goto nextarg; + case 'Y': + if (++i == argc) { + return (0); + } + (void) strlcpy(pathbuf, argv[i], + sizeof(pathbuf)); + (void) strlcat(pathbuf, "/cpp", + sizeof(pathbuf)); + CPP = pathbuf; + goto nextarg; + + case 'v': + printf("version 1.0\n"); + exit(0); + + default: + return (0); + } + } + nextarg: + ; + } + } + + cmd->cflag = flag['c']; + cmd->hflag = flag['h']; + cmd->lflag = flag['l']; + cmd->mflag = flag['m']; + cmd->nflag = flag['n']; + cmd->sflag = flag['s']; + cmd->tflag = flag['t']; + cmd->Ssflag = flag['S']; + cmd->Scflag = flag['C']; + + if (tirpcflag) { + pmflag = inetdflag ? 0 : 1; /* pmflag or inetdflag is + * always TRUE */ + if ((inetdflag && cmd->nflag)) { /* netid not allowed + * with inetdflag */ + f_print(stderr, "Cannot use netid flag with inetd flag!\n"); + return (0); + } + } else { /* 4.1 mode */ + pmflag = 0; /* set pmflag only in tirpcmode */ + inetdflag = 1; /* inetdflag is TRUE by default */ + if (cmd->nflag) { /* netid needs TIRPC */ + f_print(stderr, "Cannot use netid flag without TIRPC!\n"); + return (0); + } + } + + if (newstyle && (tblflag || cmd->tflag)) { + f_print(stderr, "Cannot use table flags with newstyle!\n"); + return (0); + } + /* check no conflicts with file generation flags */ + nflags = cmd->cflag + cmd->hflag + cmd->lflag + cmd->mflag + + cmd->sflag + cmd->nflag + cmd->tflag + cmd->Ssflag + cmd->Scflag; + + if (nflags == 0) { + if (cmd->outfile != NULL || cmd->infile == NULL) { + return (0); + } + } else + if (nflags > 1) { + f_print(stderr, "Cannot have more than one file generation flag!\n"); + return (0); + } + return (1); +} + +static void +usage(void) +{ + f_print(stderr, "usage: %s infile\n", cmdname); + f_print(stderr, "\t%s [-AaBbILMNTv] [-Dname[=value]] [-i size] [-K seconds] [-Y pathname] infile\n", + cmdname); + f_print(stderr, "\t%s [-c | -h | -l | -m | -t | -Sc | -Ss] [-o outfile] [infile]\n", + cmdname); + f_print(stderr, "\t%s [-s nettype] [-o outfile] [infile]\n", cmdname); + f_print(stderr, "\t%s [-n netid] [-o outfile] [infile]\n", cmdname); + options_usage(); + exit(1); +} + +static void +options_usage(void) +{ + f_print(stderr, "options:\n"); + f_print(stderr, "-A\t\tgenerate svc_caller() function\n"); + f_print(stderr, "-a\t\tgenerate all files, including samples\n"); + f_print(stderr, "-B\t\tgenerate BSD c++ macros\n"); + f_print(stderr, "-b\t\tbackward compatibility mode (generates code for SunOS 4.1)\n"); + f_print(stderr, "-c\t\tgenerate XDR routines\n"); + f_print(stderr, "-Dname[=value]\tdefine a symbol (same as #define)\n"); + f_print(stderr, "-h\t\tgenerate header file\n"); + f_print(stderr, "-I\t\tgenerate code for inetd support in server (for SunOS 4.1)\n"); + f_print(stderr, "-i size\t\tsize at which to start generating inline code\n"); + f_print(stderr, "-K seconds\tserver exits after K seconds of inactivity\n"); + f_print(stderr, "-L\t\tserver errors will be printed to syslog\n"); + f_print(stderr, "-l\t\tgenerate client side stubs\n"); + f_print(stderr, "-M\t\tgenerate thread-safe stubs\n"); + f_print(stderr, "-m\t\tgenerate server side stubs\n"); + f_print(stderr, "-N\t\tsupports multiple arguments and call-by-value\n"); + f_print(stderr, "-n netid\tgenerate server code that supports named netid\n"); + f_print(stderr, "-o outfile\tname of the output file\n"); + f_print(stderr, "-s nettype\tgenerate server code that supports named nettype\n"); + f_print(stderr, "-Sc\t\tgenerate sample client code that uses remote procedures\n"); + f_print(stderr, "-Ss\t\tgenerate sample server code that defines remote procedures\n"); + f_print(stderr, "-T\t\tgenerate code to support RPC dispatch tables\n"); + f_print(stderr, "-t\t\tgenerate RPC dispatch table\n"); + f_print(stderr, "-v\t\tdisplay version number\n"); + f_print(stderr, "-Y path\t\tdirectory name to find C preprocessor (cpp)\n"); + + exit(1); +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_parse.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_parse.c new file mode 100644 index 00000000..3a59e8d1 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_parse.c @@ -0,0 +1,618 @@ +/* $NetBSD: rpc_parse.c,v 1.21 2015/05/09 23:29:51 dholland Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_parse.c 1.8 89/02/22 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_parse.c,v 1.21 2015/05/09 23:29:51 dholland Exp $"); +#endif +#endif + +/* + * rpc_parse.c, Parser for the RPC protocol compiler + * Copyright (C) 1987 Sun Microsystems, Inc. + */ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "rpc/types.h" +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +#define ARGNAME "arg" + +static void isdefined(definition *); +static void def_struct(definition *); +static void def_program(definition *); +static void def_enum(definition *); +static void def_const(definition *); +static void def_union(definition *); +static void check_type_name(const char *, int); +static void def_typedef(definition *); +static void get_declaration(declaration *, defkind); +static void get_prog_declaration(declaration *, defkind, int); +static void get_type(const char **, const char **, defkind); +static void unsigned_dec(const char **); + +/* + * return the next definition you see + */ +definition * +get_definition(void) +{ + definition *defp; + token tok; + + defp = ALLOC(definition); + get_token(&tok); + switch (tok.kind) { + case TOK_STRUCT: + def_struct(defp); + break; + case TOK_UNION: + def_union(defp); + break; + case TOK_TYPEDEF: + def_typedef(defp); + break; + case TOK_ENUM: + def_enum(defp); + break; + case TOK_PROGRAM: + def_program(defp); + break; + case TOK_CONST: + def_const(defp); + break; + case TOK_EOF: + free(defp); + return (NULL); + default: + error("Expected definition keyword"); + } + scan(TOK_SEMICOLON, &tok); + isdefined(defp); + return (defp); +} + +static void +isdefined(definition *defp) +{ + STOREVAL(&defined, defp); +} + +static void +def_struct(definition *defp) +{ + token tok; + declaration dec; + decl_list *decls; + decl_list **tailp; + + defp->def_kind = DEF_STRUCT; + + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_LBRACE, &tok); + tailp = &defp->def.st.decls; + do { + get_declaration(&dec, DEF_STRUCT); + decls = ALLOC(decl_list); + decls->decl = dec; + *tailp = decls; + tailp = &decls->next; + scan(TOK_SEMICOLON, &tok); + peek(&tok); + } while (tok.kind != TOK_RBRACE); + get_token(&tok); + *tailp = NULL; +} + +static void +def_program(definition *defp) +{ + token tok; + declaration dec; + decl_list *decls; + decl_list **tailp; + version_list *vlist; + version_list **vtailp; + proc_list *plist; + proc_list **ptailp; + int num_args; + bool_t isvoid = FALSE; /* whether first argument is void */ + defp->def_kind = DEF_PROGRAM; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_LBRACE, &tok); + vtailp = &defp->def.pr.versions; + tailp = &defp->def.st.decls; + scan(TOK_VERSION, &tok); + do { + scan(TOK_IDENT, &tok); + vlist = ALLOC(version_list); + vlist->vers_name = tok.str; + scan(TOK_LBRACE, &tok); + ptailp = &vlist->procs; + do { + /* get result type */ + plist = ALLOC(proc_list); + get_type(&plist->res_prefix, &plist->res_type, + DEF_PROGRAM); + if (streq(plist->res_type, "opaque")) { + error("Illegal result type"); + } + scan(TOK_IDENT, &tok); + plist->proc_name = tok.str; + scan(TOK_LPAREN, &tok); + /* get args - first one */ + num_args = 1; + isvoid = FALSE; + /* type of DEF_PROGRAM in the first + * get_prog_declaration and DEF_STURCT in the next + * allows void as argument if it is the only argument */ + get_prog_declaration(&dec, DEF_PROGRAM, num_args); + if (streq(dec.type, "void")) + isvoid = TRUE; + decls = ALLOC(decl_list); + plist->args.decls = decls; + decls->decl = dec; + tailp = &decls->next; + /* get args */ + while (peekscan(TOK_COMMA, &tok)) { + num_args++; + get_prog_declaration(&dec, DEF_STRUCT, + num_args); + decls = ALLOC(decl_list); + decls->decl = dec; + *tailp = decls; + if (streq(dec.type, "void")) + isvoid = TRUE; + tailp = &decls->next; + } + /* multiple arguments are only allowed in newstyle */ + if (!newstyle && num_args > 1) { + error("Only one argument is allowed"); + } + if (isvoid && num_args > 1) { + error("Illegal use of void in program definition"); + } + *tailp = NULL; + scan(TOK_RPAREN, &tok); + scan(TOK_EQUAL, &tok); + scan_num(&tok); + scan(TOK_SEMICOLON, &tok); + plist->proc_num = tok.str; + plist->arg_num = num_args; + *ptailp = plist; + ptailp = &plist->next; + peek(&tok); + } while (tok.kind != TOK_RBRACE); + *ptailp = NULL; + *vtailp = vlist; + vtailp = &vlist->next; + scan(TOK_RBRACE, &tok); + scan(TOK_EQUAL, &tok); + scan_num(&tok); + vlist->vers_num = tok.str; + /* make the argument structure name for each arg */ + for (plist = vlist->procs; plist != NULL; + plist = plist->next) { + plist->args.argname = make_argname(plist->proc_name, + vlist->vers_num); + /* free the memory ?? */ + } + scan(TOK_SEMICOLON, &tok); + scan2(TOK_VERSION, TOK_RBRACE, &tok); + } while (tok.kind == TOK_VERSION); + scan(TOK_EQUAL, &tok); + scan_num(&tok); + defp->def.pr.prog_num = tok.str; + *vtailp = NULL; +} + + +static void +def_enum(definition *defp) +{ + token tok; + enumval_list *elist; + enumval_list **tailp; + + defp->def_kind = DEF_ENUM; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_LBRACE, &tok); + tailp = &defp->def.en.vals; + do { + scan(TOK_IDENT, &tok); + elist = ALLOC(enumval_list); + elist->name = tok.str; + elist->assignment = NULL; + scan3(TOK_COMMA, TOK_RBRACE, TOK_EQUAL, &tok); + if (tok.kind == TOK_EQUAL) { + scan_num(&tok); + elist->assignment = tok.str; + scan2(TOK_COMMA, TOK_RBRACE, &tok); + } + *tailp = elist; + tailp = &elist->next; + } while (tok.kind != TOK_RBRACE); + *tailp = NULL; +} + +static void +def_const(definition *defp) +{ + token tok; + + defp->def_kind = DEF_CONST; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_EQUAL, &tok); + scan2(TOK_IDENT, TOK_STRCONST, &tok); + defp->def.co = tok.str; +} + +static void +def_union(definition *defp) +{ + token tok; + declaration dec; + case_list *cases; + case_list **tailp; + + defp->def_kind = DEF_UNION; + scan(TOK_IDENT, &tok); + defp->def_name = tok.str; + scan(TOK_SWITCH, &tok); + scan(TOK_LPAREN, &tok); + get_declaration(&dec, DEF_UNION); + defp->def.un.enum_decl = dec; + tailp = &defp->def.un.cases; + scan(TOK_RPAREN, &tok); + scan(TOK_LBRACE, &tok); + scan(TOK_CASE, &tok); + while (tok.kind == TOK_CASE) { + scan2(TOK_IDENT, TOK_CHARCONST, &tok); + cases = ALLOC(case_list); + cases->case_name = tok.str; + scan(TOK_COLON, &tok); + /* now peek at next token */ + if (peekscan(TOK_CASE, &tok)) { + + do { + scan2(TOK_IDENT, TOK_CHARCONST, &tok); + cases->contflag = 1; /* continued case + * statement */ + *tailp = cases; + tailp = &cases->next; + cases = ALLOC(case_list); + cases->case_name = tok.str; + scan(TOK_COLON, &tok); + + } while (peekscan(TOK_CASE, &tok)); + } + get_declaration(&dec, DEF_UNION); + cases->case_decl = dec; + cases->contflag = 0; /* no continued case statement */ + *tailp = cases; + tailp = &cases->next; + scan(TOK_SEMICOLON, &tok); + + scan3(TOK_CASE, TOK_DEFAULT, TOK_RBRACE, &tok); + } + *tailp = NULL; + if (tok.kind == TOK_DEFAULT) { + scan(TOK_COLON, &tok); + get_declaration(&dec, DEF_UNION); + defp->def.un.default_decl = ALLOC(declaration); + *defp->def.un.default_decl = dec; + scan(TOK_SEMICOLON, &tok); + scan(TOK_RBRACE, &tok); + } else { + defp->def.un.default_decl = NULL; + } +} + +static const char *const reserved_words[] = { + "array", + "bytes", + "destroy", + "free", + "getpos", + "inline", + "pointer", + "reference", + "setpos", + "sizeof", + "union", + "vector", + NULL +}; + +static const char *const reserved_types[] = { + "opaque", + "string", + NULL +}; +/* check that the given name is not one that would eventually result in + xdr routines that would conflict with internal XDR routines. */ +static void +check_type_name(const char *name, int new_type) +{ + int i; + + for (i = 0; reserved_words[i] != NULL; i++) { + if (strcmp(name, reserved_words[i]) == 0) { + error("Illegal (reserved) name '%s' in type definition", name); + } + } + if (new_type) { + for (i = 0; reserved_types[i] != NULL; i++) { + if (strcmp(name, reserved_types[i]) == 0) { + error("Illegal (reserved) name '%s' in type definition", name); + } + } + } +} + +static void +def_typedef(definition *defp) +{ + declaration dec; + + defp->def_kind = DEF_TYPEDEF; + get_declaration(&dec, DEF_TYPEDEF); + defp->def_name = dec.name; + check_type_name(dec.name, 1); + defp->def.ty.old_prefix = dec.prefix; + defp->def.ty.old_type = dec.type; + defp->def.ty.rel = dec.rel; + defp->def.ty.array_max = dec.array_max; +} + +static void +get_declaration(declaration *dec, defkind dkind) +{ + token tok; + + get_type(&dec->prefix, &dec->type, dkind); + dec->rel = REL_ALIAS; + if (streq(dec->type, "void")) { + return; + } + check_type_name(dec->type, 0); + + scan2(TOK_STAR, TOK_IDENT, &tok); + if (tok.kind == TOK_STAR) { + dec->rel = REL_POINTER; + scan(TOK_IDENT, &tok); + } + dec->name = tok.str; + if (peekscan(TOK_LBRACKET, &tok)) { + if (dec->rel == REL_POINTER) { + error("No array-of-pointer declarations -- use typedef"); + } + dec->rel = REL_VECTOR; + scan_num(&tok); + dec->array_max = tok.str; + scan(TOK_RBRACKET, &tok); + } else + if (peekscan(TOK_LANGLE, &tok)) { + if (dec->rel == REL_POINTER) { + error("No array-of-pointer declarations -- use typedef"); + } + dec->rel = REL_ARRAY; + if (peekscan(TOK_RANGLE, &tok)) { + dec->array_max = "(u_int)~0"; + /* unspecified size, use * max */ + } else { + scan_num(&tok); + dec->array_max = tok.str; + scan(TOK_RANGLE, &tok); + } + } + if (streq(dec->type, "opaque")) { + if (dec->rel != REL_ARRAY && dec->rel != REL_VECTOR) { + error("Array declaration expected"); + } + } else + if (streq(dec->type, "string")) { + if (dec->rel != REL_ARRAY) { + error("Variable-length array declaration expected"); + } + } +} + +static void +get_prog_declaration(declaration *dec, defkind dkind, int num /* arg number */) +{ + token tok; + char name[255]; /* argument name */ + + if (dkind == DEF_PROGRAM) { + peek(&tok); + if (tok.kind == TOK_RPAREN) { /* no arguments */ + dec->rel = REL_ALIAS; + dec->type = "void"; + dec->prefix = NULL; + dec->name = NULL; + return; + } + } + get_type(&dec->prefix, &dec->type, dkind); + dec->rel = REL_ALIAS; + if (peekscan(TOK_IDENT, &tok)) /* optional name of argument */ + strcpy(name, tok.str); + else + sprintf(name, "%s%d", ARGNAME, num); /* default name of + * argument */ + + dec->name = strdup(name); + + if (streq(dec->type, "void")) { + return; + } + if (streq(dec->type, "opaque")) { + error("Opaque -- illegal argument type"); + } + if (peekscan(TOK_STAR, &tok)) { + if (streq(dec->type, "string")) { + error("Pointer to string not allowed in program arguments\n"); + } + dec->rel = REL_POINTER; + if (peekscan(TOK_IDENT, &tok)) /* optional name of argument */ + dec->name = strdup(tok.str); + } + if (peekscan(TOK_LANGLE, &tok)) { + if (!streq(dec->type, "string")) { + error("Arrays cannot be declared as arguments to procedures -- use typedef"); + } + dec->rel = REL_ARRAY; + if (peekscan(TOK_RANGLE, &tok)) { + dec->array_max = "(u_int)~0"; + /* unspecified size, use max */ + } else { + scan_num(&tok); + dec->array_max = tok.str; + scan(TOK_RANGLE, &tok); + } + } + if (streq(dec->type, "string")) { + if (dec->rel != REL_ARRAY) { /* .x specifies just string as + * type of argument - make it + * string<> */ + dec->rel = REL_ARRAY; + dec->array_max = "(u_int)~0"; + /* unspecified size, use max */ + } + } +} + + + +static void +get_type(const char **prefixp, const char **typep, defkind dkind) +{ + token tok; + + *prefixp = NULL; + get_token(&tok); + switch (tok.kind) { + case TOK_IDENT: + *typep = tok.str; + break; + case TOK_STRUCT: + case TOK_ENUM: + case TOK_UNION: + *prefixp = tok.str; + scan(TOK_IDENT, &tok); + *typep = tok.str; + break; + case TOK_UNSIGNED: + unsigned_dec(typep); + break; + case TOK_SHORT: + *typep = "short"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_LONG: + *typep = "long"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_HYPER: + *typep = "longlong_t"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_VOID: + if (dkind != DEF_UNION && dkind != DEF_PROGRAM) { + error("Void is allowed only inside union and program definitions with one argument"); + } + *typep = tok.str; + break; + case TOK_STRING: + case TOK_OPAQUE: + case TOK_CHAR: + case TOK_INT: + case TOK_FLOAT: + case TOK_DOUBLE: + case TOK_BOOL: + case TOK_QUAD: + *typep = tok.str; + break; + default: + error("Type specifier expected"); + } +} + +static void +unsigned_dec(const char **typep) +{ + token tok; + + peek(&tok); + switch (tok.kind) { + case TOK_CHAR: + get_token(&tok); + *typep = "u_char"; + break; + case TOK_SHORT: + get_token(&tok); + *typep = "u_short"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_LONG: + get_token(&tok); + *typep = "u_long"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_HYPER: + get_token(&tok); + *typep = "u_longlong_t"; + (void) peekscan(TOK_INT, &tok); + break; + case TOK_INT: + get_token(&tok); + *typep = "u_int"; + break; + default: + *typep = "u_int"; + break; + } +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_parse.h b/buildrump.sh/src/usr.bin/rpcgen/rpc_parse.h new file mode 100644 index 00000000..05f7f6cd --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_parse.h @@ -0,0 +1,168 @@ +/* $NetBSD: rpc_parse.h,v 1.6 2015/05/09 21:44:47 christos Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* @(#)rpc_parse.h 1.3 90/08/29 (C) 1987 SMI */ + +/* + * rpc_parse.h, Definitions for the RPCL parser + */ + +enum defkind { + DEF_CONST, + DEF_STRUCT, + DEF_UNION, + DEF_ENUM, + DEF_TYPEDEF, + DEF_PROGRAM +}; +typedef enum defkind defkind; + +typedef const char *const_def; + +enum relation { + REL_VECTOR, /* fixed length array */ + REL_ARRAY, /* variable length array */ + REL_POINTER, /* pointer */ + REL_ALIAS, /* simple */ +}; +typedef enum relation relation; + +struct typedef_def { + const char *old_prefix; + const char *old_type; + relation rel; + const char *array_max; +}; +typedef struct typedef_def typedef_def; + +struct enumval_list { + const char *name; + const char *assignment; + struct enumval_list *next; +}; +typedef struct enumval_list enumval_list; + +struct enum_def { + enumval_list *vals; +}; +typedef struct enum_def enum_def; + +struct declaration { + const char *prefix; + const char *type; + const char *name; + relation rel; + const char *array_max; +}; +typedef struct declaration declaration; + +struct decl_list { + declaration decl; + struct decl_list *next; +}; +typedef struct decl_list decl_list; + +struct struct_def { + decl_list *decls; +}; +typedef struct struct_def struct_def; + +struct case_list { + const char *case_name; + int contflag; + declaration case_decl; + struct case_list *next; +}; +typedef struct case_list case_list; + +struct union_def { + declaration enum_decl; + case_list *cases; + declaration *default_decl; +}; +typedef struct union_def union_def; + +struct arg_list { + char *argname; /* name of struct for arg*/ + decl_list *decls; +}; + +typedef struct arg_list arg_list; + +struct proc_list { + const char *proc_name; + const char *proc_num; + arg_list args; + int arg_num; + const char *res_type; + const char *res_prefix; + struct proc_list *next; +}; +typedef struct proc_list proc_list; + +struct version_list { + const char *vers_name; + const char *vers_num; + proc_list *procs; + struct version_list *next; +}; +typedef struct version_list version_list; + +struct program_def { + const char *prog_num; + version_list *versions; +}; +typedef struct program_def program_def; + +struct definition { + const char *def_name; + defkind def_kind; + union { + const_def co; + struct_def st; + union_def un; + enum_def en; + typedef_def ty; + program_def pr; + } def; +}; +typedef struct definition definition; + +definition *get_definition(void); + +struct bas_type +{ + const char *name; + int length; + struct bas_type *next; +}; + +typedef struct bas_type bas_type; diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_sample.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_sample.c new file mode 100644 index 00000000..5aa88628 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_sample.c @@ -0,0 +1,275 @@ +/* $NetBSD: rpc_sample.c,v 1.13 2013/12/15 00:40:17 christos Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_sample.c 1.1 90/08/30 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_sample.c,v 1.13 2013/12/15 00:40:17 christos Exp $"); +#endif +#endif + +/* + * rpc_sample.c, Sample client-server code outputter for the RPC protocol compiler + */ + +#include <stdio.h> +#include <string.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +static char RQSTP[] = "rqstp"; + +static void write_sample_client(const char *, version_list *); +static void write_sample_server(definition *); +static void return_type(proc_list *); + +void +write_sample_svc(definition *def) +{ + + if (def->def_kind != DEF_PROGRAM) + return; + write_sample_server(def); +} + + +int +write_sample_clnt(definition *def) +{ + version_list *vp; + int count = 0; + + if (def->def_kind != DEF_PROGRAM) + return (0); + /* generate sample code for each version */ + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + write_sample_client(def->def_name, vp); + ++count; + } + return (count); +} + + +static void +write_sample_client(const char *program_name, version_list *vp) +{ + proc_list *proc; + decl_list *l; + + f_print(fout, "\n\nvoid\n"); + pvname(program_name, vp->vers_num); + f_print(fout, "(char *host)\n{\n"); + f_print(fout, "\tCLIENT *clnt;\n"); + + for (proc = vp->procs; proc != NULL; proc = proc->next) { + /* print out declarations for arguments */ + if (proc->arg_num < 2 && !newstyle) { + f_print(fout, "\t"); + if (streq(proc->args.decls->decl.type, "void")) + f_print(fout, "char "); /* cannot have "void" + * type */ + else + ptype(proc->args.decls->decl.prefix, proc->args.decls->decl.type, 1); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_arg;\n"); + } else { + if (!streq(proc->args.decls->decl.type, "void")) { + for (l = proc->args.decls; l != NULL; l = l->next) { + f_print(fout, "\t"); + ptype(l->decl.prefix, l->decl.type, 1); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_%s;\n", l->decl.name); +/* pdeclaration(proc->args.argname, &l->decl, 1, ";\n" );*/ + } + } + } + /* print out declarations for results */ + f_print(fout, "\t"); + if (streq(proc->res_type, "void")) + f_print(fout, "char"); /* cannot have "void" + * type */ + else + ptype(proc->res_prefix, proc->res_type, 1); + if (!Mflag) + f_print(fout, "*"); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_res;\n"); + } + f_print(fout, "\n"); + + /* generate creation of client handle */ + f_print(fout, "\tclnt = clnt_create(host, %s, %s, \"%s\");\n", + program_name, vp->vers_name, tirpcflag ? "netpath" : "udp"); + f_print(fout, "\tif (clnt == NULL) {\n"); + f_print(fout, "\t\tclnt_pcreateerror(host);\n"); + f_print(fout, "\t\texit(1);\n\t}\n"); + + /* generate calls to procedures */ + for (proc = vp->procs; proc != NULL; proc = proc->next) { + if (Mflag) + f_print(fout, "\tif ("); + else { + f_print(fout, "\t"); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_res = "); + } + pvname(proc->proc_name, vp->vers_num); + if (proc->arg_num < 2 && !newstyle) { + f_print(fout, "("); + if (streq(proc->args.decls->decl.type, "void")) /* cast to void* */ + f_print(fout, "(void *)"); + f_print(fout, "&"); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_arg, "); + } else { + if (streq(proc->args.decls->decl.type, "void")) { + f_print(fout, "(clnt);\n"); + } else { + f_print(fout, "("); + for (l = proc->args.decls; l != NULL; l = l->next) { + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_%s, ", l->decl.name); + } + } + } + if (Mflag) { + f_print(fout, "&"); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_res, clnt) != RPC_SUCCESS)\n"); + } else { + f_print(fout, "clnt);\n"); + f_print(fout, "\tif ("); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_res == NULL)\n"); + } + f_print(fout, "\t\tclnt_perror(clnt, \"call failed\");\n"); + } + + f_print(fout, "\tclnt_destroy(clnt);\n"); + f_print(fout, "}\n"); +} + +static void +write_sample_server(definition *def) +{ + version_list *vp; + proc_list *proc; + + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + for (proc = vp->procs; proc != NULL; proc = proc->next) { + f_print(fout, "\n"); + if (Mflag) + f_print(fout, "bool_t\n"); + else { + return_type(proc); + f_print(fout, "*\n"); + } + pvname_svc(proc->proc_name, vp->vers_num); + printarglist(proc, "result", RQSTP, "struct svc_req *"); + + f_print(fout, "{\n"); + if (Mflag) { + f_print(fout, "\tbool_t retval = TRUE;\n"); + } else { + f_print(fout, "\tstatic "); + if (streq(proc->res_type, "void")) + f_print(fout, "char "); /* cannot have void type */ + else + return_type(proc); + f_print(fout, "result;\n"); + } + f_print(fout, + "\n\t/*\n\t * insert server code here\n\t */\n\n"); + if (Mflag) { + f_print(fout, "\treturn (retval);\n"); + } else { + if (streq(proc->res_type, "void")) + f_print(fout, "\treturn ((void *)&result);\n"); + else + f_print(fout, "\treturn (&result);\n"); + } + f_print(fout, "}\n"); + } + } +} + +static void +return_type(proc_list *plist) +{ + ptype(plist->res_prefix, plist->res_type, 1); +} + +void +add_sample_msg(void) +{ + f_print(fout, "/*\n"); + f_print(fout, " * This is sample code generated by rpcgen.\n"); + f_print(fout, " * These are only templates and you can use them\n"); + f_print(fout, " * as a guideline for developing your own functions.\n"); + f_print(fout, " */\n\n"); +} + +void +write_sample_clnt_main(void) +{ + list *l; + definition *def; + version_list *vp; + + f_print(fout, "\n\n"); + f_print(fout, "int\nmain(int argc, char *argv[])\n{\n"); + f_print(fout, "\tchar *host;"); + f_print(fout, "\n\n\tif (argc < 2) {"); + f_print(fout, "\n\t\tprintf(\"usage: %%s server_host\\n\", argv[0]);\n"); + f_print(fout, "\t\texit(1);\n\t}"); + f_print(fout, "\n\thost = argv[1];\n"); + + for (l = defined; l != NULL; l = l->next) { + def = l->val; + if (def->def_kind != DEF_PROGRAM) { + continue; + } + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, "\t"); + pvname(def->def_name, vp->vers_num); + f_print(fout, "(host);\n"); + } + } + f_print(fout, "\texit(0);\n"); + f_print(fout, "}\n"); +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_scan.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_scan.c new file mode 100644 index 00000000..4e236f88 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_scan.c @@ -0,0 +1,492 @@ +/* $NetBSD: rpc_scan.c,v 1.15 2015/05/09 23:28:43 dholland Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_scan.c 1.11 89/02/22 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_scan.c,v 1.15 2015/05/09 23:28:43 dholland Exp $"); +#endif +#endif + +/* + * rpc_scan.c, Scanner for the RPC protocol compiler + * Copyright (C) 1987, Sun Microsystems, Inc. + */ +#include <stdlib.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +#define startcomment(where) (where[0] == '/' && where[1] == '*') +#define endcomment(where) (where[-1] == '*' && where[0] == '/') + +static void unget_token(token *); +static void findstrconst(char **, const char **); +static void findchrconst(char **, const char **); +static void findconst(char **, const char **); +static void findkind(char **, token *); +static int cppline(const char *); +static int directive(const char *); +static void printdirective(const char *); +static void docppline(char *, int *, const char **); + +static int pushed = 0; /* is a token pushed */ +static token lasttok; /* last token, if pushed */ + +/* + * scan expecting 1 given token + */ +void +scan(tok_kind expect, token *tokp) +{ + get_token(tokp); + if (tokp->kind != expect) { + expected1(expect); + } +} +/* + * scan expecting any of the 2 given tokens + */ +void +scan2(tok_kind expect1, tok_kind expect2, token *tokp) +{ + get_token(tokp); + if (tokp->kind != expect1 && tokp->kind != expect2) { + expected2(expect1, expect2); + } +} +/* + * scan expecting any of the 3 given token + */ +void +scan3(tok_kind expect1, tok_kind expect2, tok_kind expect3, token *tokp) +{ + get_token(tokp); + if (tokp->kind != expect1 && tokp->kind != expect2 + && tokp->kind != expect3) { + expected3(expect1, expect2, expect3); + } +} +/* + * scan expecting a constant, possibly symbolic + */ +void +scan_num(token *tokp) +{ + get_token(tokp); + switch (tokp->kind) { + case TOK_IDENT: + break; + default: + error("Expected constant or identifier"); + } +} +/* + * Peek at the next token + */ +void +peek(token *tokp) +{ + get_token(tokp); + unget_token(tokp); +} +/* + * Peek at the next token and scan it if it matches what you expect + */ +int +peekscan(tok_kind expect, token *tokp) +{ + peek(tokp); + if (tokp->kind == expect) { + get_token(tokp); + return (1); + } + return (0); +} +/* + * Get the next token, printing out any directive that are encountered. + */ +void +get_token(token *tokp) +{ + int commenting; + + if (pushed) { + pushed = 0; + *tokp = lasttok; + return; + } + commenting = 0; + for (;;) { + if (*where == 0) { + for (;;) { + if (!fgets(curline, MAXLINESIZE, fin)) { + tokp->kind = TOK_EOF; + *where = 0; + return; + } + linenum++; + if (commenting) { + break; + } else + if (cppline(curline)) { + docppline(curline, &linenum, + &infilename); + } else + if (directive(curline)) { + printdirective(curline); + } else { + break; + } + } + where = curline; + } else + if (isspace((unsigned char)*where)) { + while (isspace((unsigned char)*where)) { + where++; /* eat */ + } + } else + if (commenting) { + for (where++; *where; where++) { + if (endcomment(where)) { + where++; + commenting--; + break; + } + } + } else + if (startcomment(where)) { + where += 2; + commenting++; + } else { + break; + } + } + + /* + * 'where' is not whitespace, comment or directive Must be a token! + */ + switch (*where) { + case ':': + tokp->kind = TOK_COLON; + where++; + break; + case ';': + tokp->kind = TOK_SEMICOLON; + where++; + break; + case ',': + tokp->kind = TOK_COMMA; + where++; + break; + case '=': + tokp->kind = TOK_EQUAL; + where++; + break; + case '*': + tokp->kind = TOK_STAR; + where++; + break; + case '[': + tokp->kind = TOK_LBRACKET; + where++; + break; + case ']': + tokp->kind = TOK_RBRACKET; + where++; + break; + case '{': + tokp->kind = TOK_LBRACE; + where++; + break; + case '}': + tokp->kind = TOK_RBRACE; + where++; + break; + case '(': + tokp->kind = TOK_LPAREN; + where++; + break; + case ')': + tokp->kind = TOK_RPAREN; + where++; + break; + case '<': + tokp->kind = TOK_LANGLE; + where++; + break; + case '>': + tokp->kind = TOK_RANGLE; + where++; + break; + + case '"': + tokp->kind = TOK_STRCONST; + findstrconst(&where, &tokp->str); + break; + case '\'': + tokp->kind = TOK_CHARCONST; + findchrconst(&where, &tokp->str); + break; + + case '-': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + tokp->kind = TOK_IDENT; + findconst(&where, &tokp->str); + break; + + default: + if (!(isalpha((unsigned char)*where) || *where == '_')) { + if (isprint((unsigned char)*where)) { + error("Illegal character '%c' in file", *where); + } else { + error("Illegal character %d in file", *where); + } + } + findkind(&where, tokp); + break; + } +} + +static void +unget_token(token *tokp) +{ + lasttok = *tokp; + pushed = 1; +} + +static void +findstrconst(char **str, const char **val) +{ + char *p; + int size; + char *tmp; + + p = *str; + do { + p++; + } while (*p && *p != '"'); + if (*p == 0) { + error("Unterminated string constant"); + } + p++; + size = p - *str; + tmp = alloc(size + 1); + (void) strncpy(tmp, *str, size); + tmp[size] = 0; + *val = tmp; + *str = p; +} + +static void +findchrconst(char **str, const char **val) +{ + char *p; + int size; + char *tmp; + + p = *str; + do { + p++; + } while (*p && *p != '\''); + if (*p == 0) { + error("Unterminated string constant"); + } + p++; + size = p - *str; + if (size != 3) { + error("Empty character"); + } + tmp = alloc(size + 1); + (void) strncpy(tmp, *str, size); + tmp[size] = 0; + *val = tmp; + *str = p; +} + +static void +findconst(char **str, const char **val) +{ + char *p; + int size; + char *tmp; + + p = *str; + if (*p == '0' && *(p + 1) == 'x') { + p++; + do { + p++; + } while (isxdigit((unsigned char)*p)); + } else { + do { + p++; + } while (isdigit((unsigned char)*p)); + } + size = p - *str; + tmp = alloc(size + 1); + (void) strncpy(tmp, *str, size); + tmp[size] = 0; + *val = tmp; + *str = p; +} + +static const token symbols[] = { + {TOK_CONST, "const"}, + {TOK_UNION, "union"}, + {TOK_SWITCH, "switch"}, + {TOK_CASE, "case"}, + {TOK_DEFAULT, "default"}, + {TOK_STRUCT, "struct"}, + {TOK_TYPEDEF, "typedef"}, + {TOK_ENUM, "enum"}, + {TOK_OPAQUE, "opaque"}, + {TOK_BOOL, "bool"}, + {TOK_VOID, "void"}, + {TOK_CHAR, "char"}, + {TOK_INT, "int"}, + {TOK_UNSIGNED, "unsigned"}, + {TOK_SHORT, "short"}, + {TOK_LONG, "long"}, + {TOK_HYPER, "hyper"}, + {TOK_FLOAT, "float"}, + {TOK_DOUBLE, "double"}, + {TOK_QUAD, "quadruple"}, + {TOK_STRING, "string"}, + {TOK_PROGRAM, "program"}, + {TOK_VERSION, "version"}, + {TOK_EOF, "??????"}, +}; + +static void +findkind(char **mark, token *tokp) +{ + int len; + const token *s; + char *str; + char *tmp; + + str = *mark; + for (s = symbols; s->kind != TOK_EOF; s++) { + len = strlen(s->str); + if (strncmp(str, s->str, len) == 0) { + if (!isalnum((unsigned char)str[len]) && + str[len] != '_') { + tokp->kind = s->kind; + tokp->str = s->str; + *mark = str + len; + return; + } + } + } + tokp->kind = TOK_IDENT; + for (len = 0; isalnum((unsigned char)str[len]) || + str[len] == '_'; len++); + tmp = alloc(len + 1); + (void) strncpy(tmp, str, len); + tmp[len] = 0; + tokp->str = tmp; + *mark = str + len; +} + +static int +cppline(const char *line) +{ + return (line == curline && *line == '#'); +} + +static int +directive(const char *line) +{ + return (line == curline && *line == '%'); +} + +static void +printdirective(const char *line) +{ + f_print(fout, "%s", line + 1); +} + +static void +docppline(char *line, int *lineno, const char **fname) +{ + char *file; + int num; + char *p; + + line++; + while (isspace((unsigned char)*line)) { + line++; + } + num = atoi(line); + while (isdigit((unsigned char)*line)) { + line++; + } + while (isspace((unsigned char)*line)) { + line++; + } + if (*line != '"') { + error("Preprocessor error"); + } + line++; + p = file = alloc(strlen(line) + 1); + while (*line && *line != '"') { + *p++ = *line++; + } + if (*line == 0) { + error("Preprocessor error"); + } + *p = 0; + if (*file == 0) { + *fname = NULL; + free(file); + } else { + *fname = file; + } + *lineno = num - 1; +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_scan.h b/buildrump.sh/src/usr.bin/rpcgen/rpc_scan.h new file mode 100644 index 00000000..1b63a067 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_scan.h @@ -0,0 +1,108 @@ +/* $NetBSD: rpc_scan.h,v 1.10 2015/05/09 21:44:47 christos Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* @(#)rpc_scan.h 1.3 90/08/29 (C) 1987 SMI */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +/* + * rpc_scan.h, Definitions for the RPCL scanner + */ + +/* + * kinds of tokens + */ +enum tok_kind { + TOK_IDENT, + TOK_CHARCONST, + TOK_STRCONST, + TOK_LPAREN, + TOK_RPAREN, + TOK_LBRACE, + TOK_RBRACE, + TOK_LBRACKET, + TOK_RBRACKET, + TOK_LANGLE, + TOK_RANGLE, + TOK_STAR, + TOK_COMMA, + TOK_EQUAL, + TOK_COLON, + TOK_SEMICOLON, + TOK_CONST, + TOK_STRUCT, + TOK_UNION, + TOK_SWITCH, + TOK_CASE, + TOK_DEFAULT, + TOK_ENUM, + TOK_TYPEDEF, + TOK_INT, + TOK_SHORT, + TOK_LONG, + TOK_HYPER, + TOK_UNSIGNED, + TOK_FLOAT, + TOK_DOUBLE, + TOK_QUAD, + TOK_OPAQUE, + TOK_CHAR, + TOK_STRING, + TOK_BOOL, + TOK_VOID, + TOK_PROGRAM, + TOK_VERSION, + TOK_EOF +}; +typedef enum tok_kind tok_kind; + +/* + * a token + */ +struct token { + tok_kind kind; + const char *str; +}; +typedef struct token token; + + +/* + * routine interface + */ +void scan(tok_kind, token *); +void scan2(tok_kind, tok_kind, token *); +void scan3(tok_kind, tok_kind, tok_kind, token *); +void scan_num(token *); +void peek(token *); +int peekscan(tok_kind, token *); +void get_token(token *); diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_svcout.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_svcout.c new file mode 100644 index 00000000..0f11d790 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_svcout.c @@ -0,0 +1,966 @@ +/* $NetBSD: rpc_svcout.c,v 1.29 2015/05/09 21:44:47 christos Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_svcout.c 1.29 89/03/30 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_svcout.c,v 1.29 2015/05/09 21:44:47 christos Exp $"); +#endif +#endif + +/* + * rpc_svcout.c, Server-skeleton outputter for the RPC protocol compiler + */ +#include <stdio.h> +#include <string.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +static char RQSTP[] = "rqstp"; +static char TRANSP[] = "transp"; +static char ARG[] = "argument"; +static char RESULT[] = "result"; +static char ROUTINE[] = "local"; + +static void p_xdrfunc(const char *, const char *); +static void internal_proctype(proc_list *); +static void write_real_program(definition *); +static void write_program(definition *, const char *); +static void printerr(const char *, const char *); +static void printif(const char *, const char *, const char *, const char *); +static void write_inetmost(char *); +static void print_return(const char *); +static void print_pmapunset(const char *); +static void print_err_message(const char *); +static void write_timeout_func(void); +static void write_caller_func(void); +static void write_pm_most(char *, int); +static void write_rpc_svc_fg(char *, const char *); +static void open_log_file(char *, const char *); +static const char *aster(const char *); + +char _errbuf[256]; /* For all messages */ + +static void +p_xdrfunc(const char *rname, const char *typename) +{ + f_print(fout, "\t\txdr_%s = (xdrproc_t)xdr_%s;\n", rname, + stringfix(typename)); +} + +static void +internal_proctype(proc_list *plist) +{ + f_print(fout, "static "); + ptype(plist->res_prefix, plist->res_type, 1); + if (!Mflag) + f_print(fout, "*"); +} + + +/* + * write most of the service, that is, everything but the registrations. + */ +void +write_most(char *infile /* our name */, int netflag, int nomain) +{ + if (inetdflag || pmflag) { + const char *var_type; + var_type = (nomain ? "" : "static "); + f_print(fout, "%sint _rpcpmstart;", var_type); + f_print(fout, "\t\t/* Started by a port monitor ? */\n"); + f_print(fout, "%sint _rpcfdtype;", var_type); + f_print(fout, "\t\t/* Whether Stream or Datagram ? */\n"); + if (timerflag) { + f_print(fout, "%sint _rpcsvcdirty;", var_type); + f_print(fout, "\t/* Still serving ? */\n"); + } + write_svc_aux(nomain); + } + /* write out dispatcher and stubs */ + write_programs(nomain ? NULL : "static"); + + if (nomain) + return; + + f_print(fout, "\n\n"); + f_print(fout, "int main(int, char *[]);\n"); + f_print(fout, "\nint\n"); + f_print(fout, "main(int argc, char *argv[])\n"); + f_print(fout, "{\n"); + if (inetdflag) { + write_inetmost(infile); /* Includes call to write_rpc_svc_fg() */ + } else { + if (tirpcflag) { + if (netflag) { + f_print(fout, "\tSVCXPRT *%s;\n", TRANSP); + f_print(fout, "\tstruct netconfig *nconf = NULL;\n"); + } + f_print(fout, "\tpid_t pid;\n"); + f_print(fout, "\tint i;\n"); + f_print(fout, "\tchar mname[FMNAMESZ + 1];\n\n"); + write_pm_most(infile, netflag); + f_print(fout, "\telse {\n"); + write_rpc_svc_fg(infile, "\t\t"); + f_print(fout, "\t}\n"); + } else { + f_print(fout, "\tSVCXPRT *%s;\n", TRANSP); + f_print(fout, "\n"); + print_pmapunset("\t"); + } + } + + if (logflag && !inetdflag) { + open_log_file(infile, "\t"); + } +} +/* + * write a registration for the given transport + */ +void +write_netid_register(const char *transp) +{ + list *l; + definition *def; + version_list *vp; + const char *sp; + char tmpbuf[32]; + + sp = ""; + f_print(fout, "\n"); + f_print(fout, "%s\tnconf = getnetconfigent(\"%s\");\n", sp, transp); + f_print(fout, "%s\tif (nconf == NULL) {\n", sp); + (void) sprintf(_errbuf, "cannot find %s netid.", transp); + sprintf(tmpbuf, "%s\t\t", sp); + print_err_message(tmpbuf); + f_print(fout, "%s\t\texit(1);\n", sp); + f_print(fout, "%s\t}\n", sp); + f_print(fout, "%s\t%s = svc_tli_create(RPC_ANYFD, nconf, 0, 0, 0);\n", + sp, TRANSP); + f_print(fout, "%s\tif (%s == NULL) {\n", sp, TRANSP); + (void) sprintf(_errbuf, "cannot create %s service.", transp); + print_err_message(tmpbuf); + f_print(fout, "%s\t\texit(1);\n", sp); + f_print(fout, "%s\t}\n", sp); + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + continue; + } + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, + "%s\t(void) rpcb_unset(%s, %s, nconf);\n", + sp, def->def_name, vp->vers_name); + f_print(fout, + "%s\tif (!svc_reg(%s, %s, %s, ", + sp, TRANSP, def->def_name, vp->vers_name); + pvname(def->def_name, vp->vers_num); + f_print(fout, ", nconf)) {\n"); + (void) sprintf(_errbuf, "unable to register (%s, %s, %s).", + def->def_name, vp->vers_name, transp); + print_err_message(tmpbuf); + f_print(fout, "%s\t\texit(1);\n", sp); + f_print(fout, "%s\t}\n", sp); + } + } + f_print(fout, "%s\tfreenetconfigent(nconf);\n", sp); +} +/* + * write a registration for the given transport for TLI + */ +void +write_nettype_register(const char *transp) +{ + list *l; + definition *def; + version_list *vp; + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + continue; + } + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, "\tif (!svc_create("); + pvname(def->def_name, vp->vers_num); + f_print(fout, ", %s, %s, \"%s\")) {\n ", + def->def_name, vp->vers_name, transp); + (void) sprintf(_errbuf, + "unable to create (%s, %s) for %s.", + def->def_name, vp->vers_name, transp); + print_err_message("\t\t"); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t}\n"); + } + } +} +/* + * write the rest of the service + */ +void +write_rest(void) +{ + f_print(fout, "\n"); + if (inetdflag) { + f_print(fout, "\tif (%s == NULL) {\n", TRANSP); + (void) sprintf(_errbuf, "could not create a handle"); + print_err_message("\t\t"); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t}\n"); + if (timerflag) { + f_print(fout, "\tif (_rpcpmstart) {\n"); + f_print(fout, + "\t\t(void) signal(SIGALRM, (SIG_PF)closedown);\n"); + f_print(fout, "\t\t(void) alarm(_RPCSVC_CLOSEDOWN);\n"); + f_print(fout, "\t}\n"); + } + } + f_print(fout, "\tsvc_run();\n"); + (void) sprintf(_errbuf, "svc_run returned"); + print_err_message("\t"); + f_print(fout, "\texit(1);\n"); + f_print(fout, "\t/* NOTREACHED */\n"); + f_print(fout, "}\n"); +} + +void +write_programs(const char *storage) +{ + list *l; + definition *def; + + /* write out stubs for procedure definitions */ + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + write_real_program(def); + } + } + + /* write out dispatcher for each program */ + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + write_program(def, storage); + } + } + + +} +/* write out definition of internal function (e.g. _printmsg_1(...)) + which calls server's definition of actual function (e.g. printmsg_1(...)). + Unpacks single user argument of printmsg_1 to call-by-value format + expected by printmsg_1. */ +static void +write_real_program(definition *def) +{ + version_list *vp; + proc_list *proc; + decl_list *l; + + if (!newstyle) + return; /* not needed for old style */ + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + for (proc = vp->procs; proc != NULL; proc = proc->next) { + f_print(fout, "\n"); + internal_proctype(proc); + f_print(fout, "\n_"); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "("); + /* arg name */ + if (proc->arg_num > 1) + f_print(fout, "%s ", + proc->args.argname); + else + ptype(proc->args.decls->decl.prefix, + proc->args.decls->decl.type, 0); + f_print(fout, "*argp, "); + if (Mflag) { + if (streq(proc->res_type, "void")) + f_print(fout, "char "); + else + ptype(proc->res_prefix, + proc->res_type, 0); + f_print(fout, "%sresult, ", + aster(proc->res_type)); + } + f_print(fout, "struct svc_req *%s)\n", RQSTP); + f_print(fout, "{\n"); + f_print(fout, "\treturn ("); + pvname_svc(proc->proc_name, vp->vers_num); + f_print(fout, "("); + if (proc->arg_num < 2) { /* single argument */ + if (!streq(proc->args.decls->decl.type, "void")) + f_print(fout, "*argp, "); /* non-void */ + } else { + for (l = proc->args.decls; l != NULL; l = l->next) + f_print(fout, "argp->%s, ", l->decl.name); + } + f_print(fout, "%s));\n}\n", RQSTP); + } + } +} + +static void +write_program(definition *def, const char *storage) +{ + version_list *vp; + proc_list *proc; + int filled; + + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, "\n"); + if (storage != NULL) { + f_print(fout, "%s ", storage); + } + f_print(fout, "void "); + pvname(def->def_name, vp->vers_num); + f_print(fout, "(struct svc_req *%s, ", RQSTP); + f_print(fout, "SVCXPRT *%s);\n", TRANSP); + f_print(fout, "\n"); + if (storage != NULL) { + f_print(fout, "%s ", storage); + } + f_print(fout, "void\n"); + pvname(def->def_name, vp->vers_num); + + f_print(fout, "(struct svc_req *%s, ", RQSTP); + f_print(fout, "SVCXPRT *%s)\n", TRANSP); + f_print(fout, "{\n"); + + filled = 0; + f_print(fout, "\tunion {\n"); + for (proc = vp->procs; proc != NULL; proc = proc->next) { + if (proc->arg_num < 2) { /* single argument */ + if (streq(proc->args.decls->decl.type, + "void")) { + continue; + } + filled = 1; + f_print(fout, "\t\t"); + ptype(proc->args.decls->decl.prefix, + proc->args.decls->decl.type, 0); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_arg;\n"); + + } else { + filled = 1; + f_print(fout, "\t\t%s", proc->args.argname); + f_print(fout, " "); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_arg;\n"); + } + } + if (!filled) { + f_print(fout, "\t\tint fill;\n"); + } + f_print(fout, "\t} %s;\n", ARG); + if (Mflag) { + f_print(fout, "\tunion {\n"); + for (proc = vp->procs; proc != NULL; proc = proc->next) { + f_print(fout, "\t\t"); + if (streq(proc->res_type, "void")) + f_print(fout, "char "); + else + ptype(proc->res_prefix, proc->res_type, + 1); + pvname(proc->proc_name, vp->vers_num); + f_print(fout, "_res;\n"); + } + f_print(fout, "\t} %s;\n", RESULT); + f_print(fout, "\tbool_t retval;\n"); + } else + f_print(fout, "\tchar *%s;\n", RESULT); + + f_print(fout, "\txdrproc_t xdr_%s, xdr_%s;\n", ARG, RESULT); + if (Mflag) + f_print(fout, + "\tbool_t (*%s)(char *, void *, struct svc_req *);\n", + ROUTINE); + else + f_print(fout, + "\tchar *(*%s)(char *, struct svc_req *);\n", + ROUTINE); + + f_print(fout, "\n"); + + if (callerflag) + f_print(fout, "\tcaller = transp;\n"); /* EVAS */ + if (timerflag) + f_print(fout, "\t_rpcsvcdirty = 1;\n"); + f_print(fout, "\tswitch (%s->rq_proc) {\n", RQSTP); + if (!nullproc(vp->procs)) { + f_print(fout, "\tcase NULLPROC:\n"); + f_print(fout, + "\t\t(void) svc_sendreply(%s, (xdrproc_t)xdr_void, NULL);\n", TRANSP); + print_return("\t\t"); + f_print(fout, "\n"); + } + for (proc = vp->procs; proc != NULL; proc = proc->next) { + f_print(fout, "\tcase %s:\n", proc->proc_name); + if (proc->arg_num < 2) { /* single argument */ + p_xdrfunc(ARG, proc->args.decls->decl.type); + } else { + p_xdrfunc(ARG, proc->args.argname); + } + p_xdrfunc(RESULT, proc->res_type); + if (Mflag) + f_print(fout, + "\t\t%s = (bool_t (*)(char *, void *, struct svc_req *))", + ROUTINE); + else + f_print(fout, + "\t\t%s = (char *(*)(char *, struct svc_req *))", + ROUTINE); + + if (newstyle) /* new style: calls internal routine */ + f_print(fout, "_"); + pvname_svc(proc->proc_name, vp->vers_num); + f_print(fout, ";\n"); + f_print(fout, "\t\tbreak;\n\n"); + } + f_print(fout, "\tdefault:\n"); + printerr("noproc", TRANSP); + print_return("\t\t"); + f_print(fout, "\t}\n"); + + f_print(fout, "\t(void) memset(&%s, 0, sizeof(%s));\n", ARG, ARG); + printif("getargs", TRANSP, "(caddr_t)&", ARG); + printerr("decode", TRANSP); + print_return("\t\t"); + f_print(fout, "\t}\n"); + + if (Mflag) + f_print(fout, "\tretval = (*%s)((char *)&%s, (void *)&%s, %s);\n", + ROUTINE, ARG, RESULT, RQSTP); + else + f_print(fout, "\t%s = (*%s)((char *)&%s, %s);\n", + RESULT, ROUTINE, ARG, RQSTP); + if (Mflag) + f_print(fout, + "\tif (retval > 0 && !svc_sendreply(%s, xdr_%s, (char *)&%s)) {\n", + TRANSP, RESULT, RESULT); + else + f_print(fout, + "\tif (%s != NULL && !svc_sendreply(%s, xdr_%s, %s)) {\n", + RESULT, TRANSP, RESULT, RESULT); + printerr("systemerr", TRANSP); + f_print(fout, "\t}\n"); + + printif("freeargs", TRANSP, "(caddr_t)&", ARG); + (void) sprintf(_errbuf, "unable to free arguments"); + print_err_message("\t\t"); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t}\n"); + + if (Mflag) { + f_print(fout, "\tif (!"); + pvname(def->def_name, vp->vers_num); + f_print(fout, "_freeresult"); + f_print(fout, "(%s, xdr_%s, (caddr_t)&%s)) {\n", + TRANSP, RESULT, RESULT); + (void) sprintf(_errbuf, "unable to free results"); + print_err_message("\t\t"); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t}\n"); + } + + print_return("\t"); + f_print(fout, "}\n"); + } +} + +static void +printerr(const char *err, const char *transp) +{ + f_print(fout, "\t\tsvcerr_%s(%s);\n", err, transp); +} + +static void +printif(const char *proc, const char *transp, const char *prefix, const char *arg) +{ + f_print(fout, "\tif (!svc_%s(%s, xdr_%s, %s%s)) {\n", + proc, transp, arg, prefix, arg); +} + +int +nullproc(proc_list *proc) +{ + for (; proc != NULL; proc = proc->next) { + if (streq(proc->proc_num, "0")) { + return (1); + } + } + return (0); +} + +static void +write_inetmost(char *infile) +{ + f_print(fout, "\tSVCXPRT *%s = NULL;\n", TRANSP); + f_print(fout, "\tint sock;\n"); + f_print(fout, "\tint proto = 0;\n"); + f_print(fout, "\tstruct sockaddr_in saddr;\n"); + f_print(fout, "\tsocklen_t asize = (socklen_t)sizeof(saddr);\n"); + f_print(fout, "\n"); + f_print(fout, + "\tif (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {\n"); + f_print(fout, "\t\tsocklen_t ssize = (socklen_t)sizeof(int);\n\n"); + f_print(fout, "\t\tif (saddr.sin_family != AF_INET)\n"); + f_print(fout, "\t\t\texit(1);\n"); + f_print(fout, "\t\tif (getsockopt(0, SOL_SOCKET, SO_TYPE,\n"); + f_print(fout, "\t\t\t\t(void *)&_rpcfdtype, &ssize) == -1)\n"); + f_print(fout, "\t\t\texit(1);\n"); + f_print(fout, "\t\tsock = 0;\n"); + f_print(fout, "\t\t_rpcpmstart = 1;\n"); + f_print(fout, "\t\tproto = 0;\n"); + open_log_file(infile, "\t\t"); + f_print(fout, "\t} else {\n"); + write_rpc_svc_fg(infile, "\t\t"); + f_print(fout, "\t\tsock = RPC_ANYSOCK;\n"); + print_pmapunset("\t\t"); + f_print(fout, "\t}\n"); +} + +static void +print_return(const char *space) +{ + if (exitnow) + f_print(fout, "%sexit(0);\n", space); + else { + if (timerflag) + f_print(fout, "%s_rpcsvcdirty = 0;\n", space); + f_print(fout, "%sreturn;\n", space); + } +} + +static void +print_pmapunset(const char *space) +{ + list *l; + definition *def; + version_list *vp; + + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + for (vp = def->def.pr.versions; vp != NULL; + vp = vp->next) { + f_print(fout, "%s(void) pmap_unset(%s, %s);\n", + space, def->def_name, vp->vers_name); + } + } + } +} + +static void +print_err_message(const char *space) +{ + if (logflag) + f_print(fout, "%ssyslog(LOG_ERR, \"%s\");\n", space, _errbuf); + else + if (inetdflag || pmflag) + f_print(fout, "%s_msgout(\"%s\");\n", space, _errbuf); + else + f_print(fout, "%sfprintf(stderr, \"%s\");\n", space, _errbuf); +} +/* + * Write the server auxiliary function ( _msgout, timeout) + */ +void +write_svc_aux(int nomain) +{ + if (!logflag) + write_msg_out(); + if (!nomain) + write_timeout_func(); + if (callerflag) /* EVAS */ + write_caller_func(); /* EVAS */ +} +/* + * Write the _msgout function + */ +void +write_msg_out(void) +{ + f_print(fout, "\n"); + f_print(fout, "static\n"); + f_print(fout, "void _msgout(const char *msg)\n"); + f_print(fout, "{\n"); + f_print(fout, "#ifdef RPC_SVC_FG\n"); + if (inetdflag || pmflag) + f_print(fout, "\tif (_rpcpmstart)\n"); + f_print(fout, "\t\tsyslog(LOG_ERR, \"%%s\", msg);\n"); + f_print(fout, "\telse\n"); + f_print(fout, "\t\t(void) fprintf(stderr, \"%%s\\n\", msg);\n"); + f_print(fout, "#else\n"); + f_print(fout, "\tsyslog(LOG_ERR, \"%%s\", msg);\n"); + f_print(fout, "#endif\n"); + f_print(fout, "}\n"); +} +/* + * Write the timeout function + */ +static void +write_timeout_func(void) +{ + if (!timerflag) + return; + f_print(fout, "\n"); + f_print(fout, "static void closedown(void);\n"); + f_print(fout, "\n"); + f_print(fout, "static void\n"); + f_print(fout, "closedown(void)\n"); + f_print(fout, "{\n"); + f_print(fout, "\tif (_rpcsvcdirty == 0) {\n"); + f_print(fout, "\t\textern fd_set svc_fdset;\n"); + f_print(fout, "\t\tstatic int size;\n"); + f_print(fout, "\t\tint i, openfd;\n"); + if (tirpcflag && pmflag) { + f_print(fout, "\t\tstruct t_info tinfo;\n\n"); + f_print(fout, "\t\tif (!t_getinfo(0, &tinfo) && (tinfo.servtype == T_CLTS))\n"); + } else { + f_print(fout, "\n\t\tif (_rpcfdtype == SOCK_DGRAM)\n"); + } + f_print(fout, "\t\t\texit(0);\n"); + f_print(fout, "\t\tif (size == 0) {\n"); + if (tirpcflag) { + f_print(fout, "\t\t\tstruct rlimit rl;\n\n"); + f_print(fout, "\t\t\trl.rlim_max = 0;\n"); + f_print(fout, "\t\t\tif (getrlimit(RLIMIT_NOFILE, &rl) == -1)\n"); + f_print(fout, "\t\t\t\treturn;\n"); + f_print(fout, "\t\t\tif ((size = rl.rlim_max) == 0)\n"); + f_print(fout, "\t\t\t\treturn;\n"); + } else { + f_print(fout, "\t\t\tsize = getdtablesize();\n"); + } + f_print(fout, "\t\t}\n"); + f_print(fout, "\t\tfor (i = 0, openfd = 0; i < size && openfd < 2; i++)\n"); + f_print(fout, "\t\t\tif (FD_ISSET(i, &svc_fdset))\n"); + f_print(fout, "\t\t\t\topenfd++;\n"); + f_print(fout, "\t\tif (openfd <= (_rpcpmstart?0:1))\n"); + f_print(fout, "\t\t\texit(0);\n"); + f_print(fout, "\t}\n"); + f_print(fout, "\t(void) alarm(_RPCSVC_CLOSEDOWN);\n"); + f_print(fout, "}\n"); +} + +static void +write_caller_func(void) +{ /* EVAS */ +#define P(s) f_print(fout, s); + + P("\n"); + P("char *svc_caller()\n"); + P("{\n"); + P(" struct sockaddr_in actual;\n"); + P(" struct hostent *hp;\n"); + P(" static struct in_addr prev;\n"); + P(" static char cname[128];\n\n"); + + P(" actual = *svc_getcaller(caller);\n\n"); + + P(" if (memcmp((char *)&actual.sin_addr, (char *)&prev,\n"); + P(" sizeof(struct in_addr)) == 0)\n"); + P(" return (cname);\n\n"); + + P(" prev = actual.sin_addr;\n\n"); + + P(" hp = gethostbyaddr((char *)&actual.sin_addr, sizeof(actual.sin_addr), AF_INET);\n"); + P(" if (hp == NULL) { /* dummy one up */\n"); + P(" extern char *inet_ntoa();\n"); + P(" strlcpy(cname, inet_ntoa(actual.sin_addr), sizeof(cname));\n"); + P(" } else {\n"); + P(" strlcpy(cname, hp->h_name, sizeof(cname));\n"); + P(" }\n\n"); + + P(" return (cname);\n"); + P("}\n"); + +#undef P +} +/* + * Write the most of port monitor support + */ +static void +write_pm_most(char *infile, int netflag) +{ + list *l; + definition *def; + version_list *vp; + + f_print(fout, "\tif (!ioctl(0, I_LOOK, mname) &&\n"); + f_print(fout, "\t\t(!strcmp(mname, \"sockmod\") ||"); + f_print(fout, " !strcmp(mname, \"timod\"))) {\n"); + f_print(fout, "\t\tchar *netid;\n"); + if (!netflag) { /* Not included by -n option */ + f_print(fout, "\t\tstruct netconfig *nconf = NULL;\n"); + f_print(fout, "\t\tSVCXPRT *%s;\n", TRANSP); + } + if (timerflag) + f_print(fout, "\t\tint pmclose;\n"); +/* not necessary, defined in /usr/include/stdlib */ +/* f_print(fout, "\t\textern char *getenv();\n");*/ + f_print(fout, "\n"); + f_print(fout, "\t\t_rpcpmstart = 1;\n"); + if (logflag) + open_log_file(infile, "\t\t"); + f_print(fout, "\t\tif ((netid = getenv(\"NLSPROVIDER\")) == NULL) {\n"); + sprintf(_errbuf, "cannot get transport name"); + print_err_message("\t\t\t"); + f_print(fout, "\t\t} else if ((nconf = getnetconfigent(netid)) == NULL) {\n"); + sprintf(_errbuf, "cannot get transport info"); + print_err_message("\t\t\t"); + f_print(fout, "\t\t}\n"); + /* + * A kludgy support for inetd services. Inetd only works with + * sockmod, and RPC works only with timod, hence all this jugglery + */ + f_print(fout, "\t\tif (strcmp(mname, \"sockmod\") == 0) {\n"); + f_print(fout, "\t\t\tif (ioctl(0, I_POP, 0) || ioctl(0, I_PUSH, \"timod\")) {\n"); + sprintf(_errbuf, "could not get the right module"); + print_err_message("\t\t\t\t"); + f_print(fout, "\t\t\t\texit(1);\n"); + f_print(fout, "\t\t\t}\n"); + f_print(fout, "\t\t}\n"); + if (timerflag) + f_print(fout, "\t\tpmclose = (t_getstate(0) != T_DATAXFER);\n"); + f_print(fout, "\t\tif ((%s = svc_tli_create(0, nconf, NULL, 0, 0)) == NULL) {\n", + TRANSP); + sprintf(_errbuf, "cannot create server handle"); + print_err_message("\t\t\t"); + f_print(fout, "\t\t\texit(1);\n"); + f_print(fout, "\t\t}\n"); + f_print(fout, "\t\tif (nconf)\n"); + f_print(fout, "\t\t\tfreenetconfigent(nconf);\n"); + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + continue; + } + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, + "\t\tif (!svc_reg(%s, %s, %s, ", + TRANSP, def->def_name, vp->vers_name); + pvname(def->def_name, vp->vers_num); + f_print(fout, ", 0)) {\n"); + (void) sprintf(_errbuf, "unable to register (%s, %s).", + def->def_name, vp->vers_name); + print_err_message("\t\t\t"); + f_print(fout, "\t\t\texit(1);\n"); + f_print(fout, "\t\t}\n"); + } + } + if (timerflag) { + f_print(fout, "\t\tif (pmclose) {\n"); + f_print(fout, "\t\t\t(void) signal(SIGALRM, (SIGPF)closedown);\n"); + f_print(fout, "\t\t\t(void) alarm(_RPCSVC_CLOSEDOWN);\n"); + f_print(fout, "\t\t}\n"); + } + f_print(fout, "\t\tsvc_run();\n"); + f_print(fout, "\t\texit(1);\n"); + f_print(fout, "\t\t/* NOTREACHED */\n"); + f_print(fout, "\t}\n"); +} +/* + * Support for backgrounding the server if self started. + */ +static void +write_rpc_svc_fg(char *infile, const char *sp) +{ + f_print(fout, "#ifndef RPC_SVC_FG\n"); + f_print(fout, "%sint size;\n", sp); + if (tirpcflag) + f_print(fout, "%sstruct rlimit rl;\n", sp); + if (inetdflag) + f_print(fout, "%sint pid, i;\n\n", sp); + f_print(fout, "%spid = fork();\n", sp); + f_print(fout, "%sif (pid < 0) {\n", sp); + f_print(fout, "%s\terr(EXIT_FAILURE, \"cannot fork\");\n", sp); + f_print(fout, "%s}\n", sp); + f_print(fout, "%sif (pid)\n", sp); + f_print(fout, "%s\texit(0);\n", sp); + /* get number of file descriptors */ + if (tirpcflag) { + f_print(fout, "%srl.rlim_max = 0;\n", sp); + f_print(fout, "%sif (getrlimit(RLIMIT_NOFILE, &rl) == -1)\n", sp); + f_print(fout, "%s\terr(EXIT_FAILURE, \"getrlimit(RLIMIT_NOFILE)\");\n", sp); + f_print(fout, "%sif ((size = rl.rlim_max) == 0)\n", sp); + f_print(fout, "%s\texit(1);\n", sp); + } else { + f_print(fout, "%ssize = getdtablesize();\n", sp); + } + + f_print(fout, "%sfor (i = 0; i < size; i++)\n", sp); + f_print(fout, "%s\t(void) close(i);\n", sp); + /* Redirect stderr and stdout to console */ + f_print(fout, "%si = open(\"/dev/console\", 2);\n", sp); + f_print(fout, "%s(void) dup2(i, 1);\n", sp); + f_print(fout, "%s(void) dup2(i, 2);\n", sp); + /* This removes control of the controlling terminal */ + if (tirpcflag) + f_print(fout, "%ssetsid();\n", sp); + else { + f_print(fout, "%si = open(\"/dev/tty\", 2);\n", sp); + f_print(fout, "%sif (i >= 0) {\n", sp); + f_print(fout, "%s\t(void) ioctl(i, TIOCNOTTY, NULL);\n", sp); + f_print(fout, "%s\t(void) close(i);\n", sp); + f_print(fout, "%s}\n", sp); + } + if (!logflag) + open_log_file(infile, sp); + f_print(fout, "#endif\n"); + if (logflag) + open_log_file(infile, sp); +} + +static void +open_log_file(char *infile, const char *sp) +{ + char *s, *p; + + s = strrchr(infile, '.'); + if (s) + *s = '\0'; + p = strrchr(infile, '/'); + if (p) + p++; + else + p = infile; + f_print(fout, "%sopenlog(\"%s\", LOG_PID, LOG_DAEMON);\n", sp, p); + if (s) + *s = '.'; +} + +/* + * write a registration for the given transport for Inetd + */ +void +write_inetd_register(const char *transp) +{ + list *l; + definition *def; + version_list *vp; + const char *sp; + int isudp; + char tmpbuf[32]; + + if (inetdflag) + sp = "\t"; + else + sp = ""; + if (streq(transp, "udp")) + isudp = 1; + else + isudp = 0; + f_print(fout, "\n"); + if (inetdflag) { + f_print(fout, "\tif ((_rpcfdtype == 0) || (_rpcfdtype == %s)) {\n", + isudp ? "SOCK_DGRAM" : "SOCK_STREAM"); + } + if (inetdflag && streq(transp, "tcp")) { + f_print(fout, "%s\tif (_rpcpmstart)\n", sp); + + f_print(fout, "%s\t\t%s = svc%s_create(%s", + sp, TRANSP, "fd", inetdflag ? "sock" : "RPC_ANYSOCK"); + if (!isudp) + f_print(fout, ", 0, 0"); + f_print(fout, ");\n"); + + f_print(fout, "%s\telse\n", sp); + + f_print(fout, "%s\t\t%s = svc%s_create(%s", + sp, TRANSP, transp, inetdflag ? "sock" : "RPC_ANYSOCK"); + if (!isudp) + f_print(fout, ", 0, 0"); + f_print(fout, ");\n"); + + } else { + f_print(fout, "%s\t%s = svc%s_create(%s", + sp, TRANSP, transp, inetdflag ? "sock" : "RPC_ANYSOCK"); + if (!isudp) + f_print(fout, ", 0, 0"); + f_print(fout, ");\n"); + } + f_print(fout, "%s\tif (%s == NULL) {\n", sp, TRANSP); + (void) sprintf(_errbuf, "cannot create %s service.", transp); + (void) sprintf(tmpbuf, "%s\t\t", sp); + print_err_message(tmpbuf); + f_print(fout, "%s\t\texit(1);\n", sp); + f_print(fout, "%s\t}\n", sp); + + if (inetdflag) { + f_print(fout, "%s\tif (!_rpcpmstart)\n\t", sp); + f_print(fout, "%s\tproto = IPPROTO_%s;\n", + sp, isudp ? "UDP" : "TCP"); + } + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind != DEF_PROGRAM) { + continue; + } + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + f_print(fout, "%s\tif (!svc_register(%s, %s, %s, ", + sp, TRANSP, def->def_name, vp->vers_name); + pvname(def->def_name, vp->vers_num); + if (inetdflag) + f_print(fout, ", proto)) {\n"); + else + f_print(fout, ", IPPROTO_%s)) {\n", + isudp ? "UDP" : "TCP"); + (void) sprintf(_errbuf, "unable to register (%s, %s, %s).", + def->def_name, vp->vers_name, transp); + print_err_message(tmpbuf); + f_print(fout, "%s\t\texit(1);\n", sp); + f_print(fout, "%s\t}\n", sp); + } + } + if (inetdflag) + f_print(fout, "\t}\n"); +} + +static const char * +aster(const char *type) +{ + if (isvectordef(type, REL_ALIAS)) { + return (""); + } else { + return ("*"); + } +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_tblout.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_tblout.c new file mode 100644 index 00000000..7726f6fe --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_tblout.c @@ -0,0 +1,181 @@ +/* $NetBSD: rpc_tblout.c,v 1.14 2013/12/15 00:40:17 christos Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_tblout.c 1.4 89/02/22 (C) 1988 SMI"; +#else +__RCSID("$NetBSD: rpc_tblout.c,v 1.14 2013/12/15 00:40:17 christos Exp $"); +#endif +#endif + +/* + * rpc_tblout.c, Dispatch table outputter for the RPC protocol compiler + */ +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +#define TABSIZE 8 +#define TABCOUNT 5 +#define TABSTOP (TABSIZE*TABCOUNT) + +static char tabstr[TABCOUNT + 1] = "\t\t\t\t\t"; + +static const char tbl_hdr[] = "struct rpcgen_table %s_table[] = {\n"; +static const char tbl_end[] = "};\n"; + +static const char null_entry[] = "\t(char *(*)())0,\n\ + \t(xdrproc_t)xdr_void,\t\t0,\n\ + \t(xdrproc_t)xdr_void,\t\t0,\n"; + +static const char tbl_nproc[] = + "u_int %s_nproc =\n\t(u_int)(sizeof(%s_table)/sizeof(%s_table[0]));\n\n"; + +static void write_table(definition *); +static void printit(const char *, const char *); + +void +write_tables(void) +{ + list *l; + definition *def; + + f_print(fout, "\n"); + for (l = defined; l != NULL; l = l->next) { + def = (definition *) l->val; + if (def->def_kind == DEF_PROGRAM) { + write_table(def); + } + } +} + +static void +write_table(definition *def) +{ + version_list *vp; + proc_list *proc; + int current; + int expected; + char progvers[100]; + int warning; + + for (vp = def->def.pr.versions; vp != NULL; vp = vp->next) { + warning = 0; + s_print(progvers, "%s_%s", + locase(def->def_name), vp->vers_num); + /* print the table header */ + f_print(fout, tbl_hdr, progvers); + + if (nullproc(vp->procs)) { + expected = 0; + } else { + expected = 1; + f_print(fout, null_entry); + } + for (proc = vp->procs; proc != NULL; proc = proc->next) { + if (expected != 0) + f_print(fout, "\n"); + current = atoi(proc->proc_num); + if (current != expected++) { + f_print(fout, + "/*\n * WARNING: table out of order\n */\n\n"); + if (warning == 0) { + f_print(stderr, + "WARNING %s table is out of order\n", + progvers); + warning = 1; + nonfatalerrors = 1; + } + expected = current + 1; + } + f_print(fout, "\t(char *(*)())RPCGEN_ACTION("); + + /* routine to invoke */ + if (!newstyle) + pvname_svc(proc->proc_name, vp->vers_num); + else { + if (newstyle) + f_print(fout, "_"); /* calls internal func */ + pvname(proc->proc_name, vp->vers_num); + } + f_print(fout, "),\n"); + + /* argument info */ + if (proc->arg_num > 1) + printit(NULL, proc->args.argname); + else + /* do we have to do something special for + * newstyle */ + printit(proc->args.decls->decl.prefix, + proc->args.decls->decl.type); + /* result info */ + printit(proc->res_prefix, proc->res_type); + } + + /* print the table trailer */ + f_print(fout, tbl_end); + f_print(fout, tbl_nproc, progvers, progvers, progvers); + } +} + +static void +printit(const char *prefix, const char *type) +{ + int len; + int tabs; + + + len = fprintf(fout, "\txdr_%s,", stringfix(type)); + /* account for leading tab expansion */ + len += TABSIZE - 1; + /* round up to tabs required */ + tabs = (TABSTOP - len + TABSIZE - 1) / TABSIZE; + f_print(fout, "%s", &tabstr[TABCOUNT - tabs]); + + if (streq(type, "void")) { + f_print(fout, "0"); + } else { + f_print(fout, "(u_int)sizeof("); + /* XXX: should "follow" be 1 ??? */ + ptype(prefix, type, 0); + f_print(fout, ")"); + } + f_print(fout, ",\n"); +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_util.c b/buildrump.sh/src/usr.bin/rpcgen/rpc_util.c new file mode 100644 index 00000000..4ef77397 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_util.c @@ -0,0 +1,479 @@ +/* $NetBSD: rpc_util.c,v 1.17 2015/05/09 23:28:43 dholland Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)rpc_util.c 1.11 89/02/22 (C) 1987 SMI"; +#else +__RCSID("$NetBSD: rpc_util.c,v 1.17 2015/05/09 23:28:43 dholland Exp $"); +#endif +#endif + +/* + * rpc_util.c, Utility routines for the RPC protocol compiler + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <err.h> +#include <ctype.h> +#include "rpc_scan.h" +#include "rpc_parse.h" +#include "rpc_util.h" + +#define ARGEXT "argument" + +static void printwhere(void); + +char curline[MAXLINESIZE]; /* current read line */ +char *where = curline; /* current point in line */ +int linenum = 0; /* current line number */ + +const char *infilename; /* input filename */ + +#define NFILES 7 +static const char *outfiles[NFILES]; /* output file names */ +int nfiles; + +FILE *fout; /* file pointer of current output */ +FILE *fin; /* file pointer of current input */ + +list *defined; /* list of defined things */ + +static const char *toktostr(tok_kind); +static void printbuf(void); +static void printwhere(void); +static int findit(definition *, const char *); +static const char *fixit(const char *, const char *); +static int typedefed(definition *, const char *); + +/* + * Reinitialize the world + */ +void +reinitialize(void) +{ + memset(curline, 0, MAXLINESIZE); + where = curline; + linenum = 0; + defined = NULL; +} +/* + * string equality + */ +int +streq(const char *a, const char *b) +{ + return (strcmp(a, b) == 0); +} +/* + * find a value in a list + */ +definition * +findval(list *lst, const char *val, int (*cmp)(definition *, const char *)) +{ + + for (; lst != NULL; lst = lst->next) { + if ((*cmp) (lst->val, val)) { + return (lst->val); + } + } + return (NULL); +} +/* + * store a value in a list + */ +void +storeval(list **lstp, definition *val) +{ + list **l; + list *lst; + + + for (l = lstp; *l != NULL; l = (list **) & (*l)->next); + lst = ALLOC(list); + lst->val = val; + lst->next = NULL; + *l = lst; +} + +static int +findit(definition *def, const char *type) +{ + return (streq(def->def_name, type)); +} + +static const char * +fixit(const char *type, const char *orig) +{ + definition *def; + + def = (definition *) FINDVAL(defined, type, findit); + if (def == NULL || def->def_kind != DEF_TYPEDEF) { + return (orig); + } + switch (def->def.ty.rel) { + case REL_VECTOR: + return (def->def.ty.old_type); + case REL_ALIAS: + return (fixit(def->def.ty.old_type, orig)); + default: + return (orig); + } +} + +const char * +fixtype(const char *type) +{ + return (fixit(type, type)); +} + +const char * +stringfix(const char *type) +{ + if (streq(type, "string")) { + return ("wrapstring"); + } else { + return (type); + } +} + +void +ptype(const char *prefix, const char *type, int follow) +{ + if (prefix != NULL) { + if (streq(prefix, "enum")) { + f_print(fout, "enum "); + } else { + f_print(fout, "struct "); + } + } + if (streq(type, "bool")) { + f_print(fout, "bool_t "); + } else + if (streq(type, "string")) { + f_print(fout, "char *"); + } else { + f_print(fout, "%s ", follow ? fixtype(type) : type); + } +} + +static int +typedefed(definition *def, const char *type) +{ + if (def->def_kind != DEF_TYPEDEF || def->def.ty.old_prefix != NULL) { + return (0); + } else { + return (streq(def->def_name, type)); + } +} + +int +isvectordef(const char *type, relation rel) +{ + definition *def; + + for (;;) { + switch (rel) { + case REL_VECTOR: + return (!streq(type, "string")); + case REL_ARRAY: + return (0); + case REL_POINTER: + return (0); + case REL_ALIAS: + def = (definition *) FINDVAL(defined, type, typedefed); + if (def == NULL) { + return (0); + } + type = def->def.ty.old_type; + rel = def->def.ty.rel; + } + } +} + +char * +locase(const char *str) +{ + char c; + static char buf[100]; + char *p = buf; + + while ((c = *str++) != '\0') { + *p++ = (c >= 'A' && c <= 'Z') ? (c - 'A' + 'a') : c; + } + *p = 0; + return (buf); +} + +void +pvname_svc(const char *pname, const char *vnum) +{ + f_print(fout, "%s_%s_svc", locase(pname), vnum); +} + +void +pvname(const char *pname, const char *vnum) +{ + f_print(fout, "%s_%s", locase(pname), vnum); +} +/* + * print a useful (?) error message, and then die + */ +__printflike(1, 2) void +error(const char *msg, ...) +{ + va_list ap; + + printwhere(); + fprintf(stderr, "%s:%d: ", infilename, linenum); + va_start(ap, msg); + vfprintf(stderr, msg, ap); + va_end(ap); + fprintf(stderr, "\n"); + errx(EXIT_FAILURE, "Cannot recover from this error"); +} +/* + * Something went wrong, unlink any files that we may have created and then + * die. + */ +void +crash(void) +{ + int i; + + if (!docleanup) + return; + + for (i = 0; i < nfiles; i++) { + (void) unlink(outfiles[i]); + } +} + +void +record_open(const char *file) +{ + if (nfiles < NFILES) { + outfiles[nfiles++] = file; + } else { + errx(EXIT_FAILURE, "Too many files!"); + } +} + +/* + * error, token encountered was not the expected one + */ +void +expected1(tok_kind exp1) +{ + error("Expected '%s'", toktostr(exp1)); +} +/* + * error, token encountered was not one of two expected ones + */ +void +expected2(tok_kind exp1, tok_kind exp2) +{ + error("Expected '%s' or '%s'", + toktostr(exp1), + toktostr(exp2)); +} +/* + * error, token encountered was not one of 3 expected ones + */ +void +expected3(tok_kind exp1, tok_kind exp2, tok_kind exp3) +{ + error("Expected '%s', '%s', or '%s'", + toktostr(exp1), + toktostr(exp2), + toktostr(exp3)); +} + +void +tabify(FILE *f, int tab) +{ + while (tab--) { + (void) fputc('\t', f); + } +} + + +static token tokstrings[] = { + {TOK_IDENT, "identifier"}, + {TOK_CONST, "const"}, + {TOK_RPAREN, ")"}, + {TOK_LPAREN, "("}, + {TOK_RBRACE, "}"}, + {TOK_LBRACE, "{"}, + {TOK_LBRACKET, "["}, + {TOK_RBRACKET, "]"}, + {TOK_STAR, "*"}, + {TOK_COMMA, ","}, + {TOK_EQUAL, "="}, + {TOK_COLON, ":"}, + {TOK_SEMICOLON, ";"}, + {TOK_UNION, "union"}, + {TOK_STRUCT, "struct"}, + {TOK_SWITCH, "switch"}, + {TOK_CASE, "case"}, + {TOK_DEFAULT, "default"}, + {TOK_ENUM, "enum"}, + {TOK_TYPEDEF, "typedef"}, + {TOK_INT, "int"}, + {TOK_SHORT, "short"}, + {TOK_LONG, "long"}, + {TOK_UNSIGNED, "unsigned"}, + {TOK_DOUBLE, "double"}, + {TOK_FLOAT, "float"}, + {TOK_CHAR, "char"}, + {TOK_STRING, "string"}, + {TOK_OPAQUE, "opaque"}, + {TOK_BOOL, "bool"}, + {TOK_VOID, "void"}, + {TOK_PROGRAM, "program"}, + {TOK_VERSION, "version"}, + {TOK_EOF, "??????"} +}; + +static const char * +toktostr(tok_kind kind) +{ + token *sp; + + for (sp = tokstrings; sp->kind != TOK_EOF && sp->kind != kind; sp++); + return (sp->str); +} + +static void +printbuf(void) +{ + char c; + int i; + int cnt; + +#define TABSIZE 4 + + for (i = 0; (c = curline[i]) != '\0'; i++) { + if (c == '\t') { + cnt = 8 - (i % TABSIZE); + c = ' '; + } else { + cnt = 1; + } + while (cnt--) { + (void) fputc(c, stderr); + } + } +} + +static void +printwhere(void) +{ + int i; + char c; + int cnt; + + printbuf(); + for (i = 0; i < where - curline; i++) { + c = curline[i]; + if (c == '\t') { + cnt = 8 - (i % TABSIZE); + } else { + cnt = 1; + } + while (cnt--) { + (void) fputc('^', stderr); + } + } + (void) fputc('\n', stderr); +} + +char * +make_argname(const char *pname, const char *vname) +{ + char *name; + size_t len; + + len = strlen(pname) + strlen(vname) + strlen(ARGEXT) + 3; + name = malloc(len); + if (!name) { + errx(EXIT_FAILURE, "Out of memory"); + } + snprintf(name, len, "%s_%s_%s", locase(pname), vname, ARGEXT); + return (name); +} + +bas_type *typ_list_h; +bas_type *typ_list_t; + +void +add_type(int len, const char *type) +{ + bas_type *ptr; + + if ((ptr = malloc(sizeof(bas_type))) == NULL) { + errx(EXIT_FAILURE, "Out of memory"); + } + ptr->name = type; + ptr->length = len; + ptr->next = NULL; + if (typ_list_t == NULL) { + typ_list_t = ptr; + typ_list_h = ptr; + } else { + typ_list_t->next = ptr; + typ_list_t = ptr; + } +} + +bas_type * +find_type(const char *type) +{ + bas_type *ptr; + + ptr = typ_list_h; + + + while (ptr != NULL) { + if (strcmp(ptr->name, type) == 0) + return (ptr); + else + ptr = ptr->next; + } + return (NULL); +} diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpc_util.h b/buildrump.sh/src/usr.bin/rpcgen/rpc_util.h new file mode 100644 index 00000000..45c3e14f --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpc_util.h @@ -0,0 +1,178 @@ +/* $NetBSD: rpc_util.h,v 1.12 2015/05/13 20:13:21 joerg Exp $ */ +/* + * Sun RPC is a product of Sun Microsystems, Inc. and is provided for + * unrestricted use provided that this legend is included on all tape + * media and as a part of the software program in whole or part. Users + * may copy or modify Sun RPC without charge, but are not authorized + * to license or distribute it to anyone else except as part of a product or + * program developed by the user or with the express written consent of + * Sun Microsystems, Inc. + * + * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE + * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR + * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE. + * + * Sun RPC is provided with no support and without any obligation on the + * part of Sun Microsystems, Inc. to assist in its use, correction, + * modification or enhancement. + * + * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE + * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC + * OR ANY PART THEREOF. + * + * In no event will Sun Microsystems, Inc. be liable for any lost revenue + * or profits or other special, indirect and consequential damages, even if + * Sun has been advised of the possibility of such damages. + * + * Sun Microsystems, Inc. + * 2550 Garcia Avenue + * Mountain View, California 94043 + */ + +/* @(#)rpc_util.h 1.5 90/08/29 (C) 1987 SMI */ + +/* + * rpc_util.h, Useful definitions for the RPC protocol compiler + */ + +#define alloc(size) ((char *)malloc((size_t)(size))) +#define ALLOC(object) ((object *)malloc(sizeof(object))) + +#define s_print (void) sprintf +#define f_print (void) fprintf + +struct list { + definition *val; + struct list *next; +}; +typedef struct list list; + +#define PUT 1 +#define GET 2 + +/* + * Global variables + */ +#define MAXLINESIZE 1024 +extern char curline[MAXLINESIZE]; +extern char *where; +extern int linenum; +extern int docleanup; + +extern const char *infilename; +extern FILE *fout; +extern FILE *fin; + +extern list *defined; + + +extern bas_type *typ_list_h; +extern bas_type *typ_list_t; + +/* + * All the option flags + */ +extern int inetdflag; +extern int pmflag; +extern int tblflag; +extern int BSDflag; +extern int logflag; +extern int newstyle; +extern int Mflag; /* multithread flag */ +extern int tirpcflag; /* flag for generating tirpc code */ +extern int doinline; /* if this is 0, then do not generate inline code */ +extern int callerflag; + +/* + * Other flags related with inetd jumpstart. + */ +extern int indefinitewait; +extern int exitnow; +extern int timerflag; + +extern int nonfatalerrors; + +/* + * rpc_util routines + */ + +#define STOREVAL(list,item) \ + storeval(list,item) + +#define FINDVAL(list,item,finder) \ + findval(list, item, finder) + +void reinitialize(void); +int streq(const char *, const char *); +definition *findval(list *, const char *, + int (*)(definition *, const char *)); +void storeval(list **, definition *); +const char *fixtype(const char *); +const char *stringfix(const char *); +void ptype(const char *, const char *, int); +int isvectordef(const char *, relation); +char *locase(const char *); +void pvname_svc(const char *, const char *); +void pvname(const char *, const char *); +__dead __printflike(1, 2) void error(const char *, ...); +void crash(void); +void record_open(const char *); +void expected1(tok_kind) __dead; +void expected2(tok_kind, tok_kind) __dead; +void expected3(tok_kind, tok_kind, tok_kind) __dead; +void tabify(FILE *, int); +char *make_argname(const char *, const char *); +void add_type(int, const char *); +bas_type *find_type(const char *); +/* + * rpc_cout routines + */ +void emit(definition *); +void emit_inline(declaration *, int); +void emit_single_in_line(declaration *, int, relation); +char *upcase(const char *); + +/* + * rpc_hout routines + */ + +void print_datadef(definition *); +void print_progdef(definition *); +void print_funcdef(definition *, int *); +void print_funcend(int); +void pxdrfuncdecl(const char *, int); +void pprocdef(proc_list *, version_list *, const char *, int); +void pdeclaration(const char *, declaration *, int, const char *); + +/* + * rpc_svcout routines + */ +void write_most(char *, int, int); +void write_netid_register(const char *); +void write_nettype_register(const char *); +void write_rest(void); +void write_programs(const char *); +int nullproc(proc_list *); +void write_svc_aux(int); +void write_msg_out(void); +void write_inetd_register(const char *); + +/* + * rpc_clntout routines + */ +void write_stubs(void); +void printarglist(proc_list *, const char *, const char *, const char *); + + +/* + * rpc_tblout routines + */ +void write_tables(void); + +/* + * rpc_sample routines + */ +void write_sample_svc(definition *); +int write_sample_clnt(definition *); +void add_sample_msg(void); +void write_sample_clnt_main(void); diff --git a/buildrump.sh/src/usr.bin/rpcgen/rpcgen.1 b/buildrump.sh/src/usr.bin/rpcgen/rpcgen.1 new file mode 100644 index 00000000..d4e91d0d --- /dev/null +++ b/buildrump.sh/src/usr.bin/rpcgen/rpcgen.1 @@ -0,0 +1,501 @@ +.\" $NetBSD: rpcgen.1,v 1.24 2013/12/15 09:18:14 wiz Exp $ +.\" from: @(#)rpcgen.new.1 1.1 90/11/09 TIRPC 1.0; from 40.10 of 10/10/89 +.\" Copyright (c) 1988,1990 Sun Microsystems, Inc. - All Rights Reserved. +.Dd December 14, 2013 +.Dt RPCGEN 1 +.Os +.Sh NAME +.Nm rpcgen +.Nd Remote Procedure Call (RPC) protocol compiler +.Sh SYNOPSIS +.Nm +.Ar infile +.Nm +.Op Fl AaBbILMNTv +.Op Fl D Ar name Op =value +.Op Fl i Ar size +.Op Fl K Ar secs +.Op Fl Y Ar pathname +.Ar infile +.Nm +.Fl c Li | +.Fl h Li | +.Fl l Li | +.Fl m Li | +.Fl t Li | +.Fl S\&c Li | +.Fl S\&s +.\" .Fl S\&m +.Op Fl o Ar outfile +.Op Ar infile +.Nm +.Op Fl s Ar nettype +.Op Fl o Ar outfile +.Op Ar infile +.Nm +.Op Fl n Ar netid +.Op Fl o Ar outfile +.Op Ar infile +.Sh DESCRIPTION +.Nm +is a tool that generates C code to implement an +.Tn RPC +protocol. +The input to +.Nm +is a language similar to C known as +.Tn RPC +Language (Remote Procedure Call Language). +.Nm +is normally used as in the first synopsis where +it takes an input file and generates up to four output files. +If the +.Ar infile +is named +.Pa proto.x , +then +.Nm +will generate a header file in +.Pa proto.h , +.Tn XDR +routines in +.Pa proto_xdr.c , +server-side stubs in +.Pa proto_svc.c , +and client-side stubs in +.Pa proto_clnt.c . +With the +.Fl T +option, +it will also generate the +.Tn RPC +dispatch table in +.Pa proto_tbl.i . +With the +.Fl S\&c +option, +it will also generate sample code which would illustrate how to use the +remote procedures on the client side. +This code would be created in +.Pa proto_client.c . +With the +.Fl S\&s +option, +it will also generate a sample server code which would illustrate how to write +the remote procedures. +This code would be created in +.Pa proto_server.c . +.Pp +The server created can be started both by the port monitors +(for example, +.Em inetd +or +.Em listen ) +or by itself. +When it is started by a port monitor, +it creates servers only for the transport for which +the file descriptor 0 was passed. +The name of the transport must be specified +by setting up the environmental variable +.Ev PM_TRANSPORT . +When the server generated by +.Nm +is executed, +it creates server handles for all the transports +specified in +.Ev NETPATH +environment variable, +or if it is unset, +it creates server handles for all the visible transports from +.Pa /etc/netconfig +file. +.Pp +.Em Note : +the transports are chosen at run time and not at compile time. +When the server is self-started, +it backgrounds itself by default. +A special define symbol +.Dv RPC_SVC_FG +can be used to run the server process in foreground. +.Pp +The second synopsis provides special features which allow +for the creation of more sophisticated +.Tn RPC +servers. +These features include support for user provided +.Li #defines +and +.Tn RPC +dispatch tables. +The entries in the +.Tn RPC +dispatch table contain: +.Pp +.Bl -inset -offset indent -compact +.It + +pointers to the service routine corresponding to that procedure, +.It + +a pointer to the input and output arguments, +.It + +the size of these routines +.El +.Pp +A server can use the dispatch table to check authorization +and then to execute the service routine; +a client library may use it to deal with the details of storage +management and +.Tn XDR +data conversion. +.Pp +The other three synopses shown above are used when +one does not want to generate all the output files, +but only a particular one. +Some examples of their usage is described in the +.Sx EXAMPLES +section below. +When +.Nm +is executed with the +.Fl s +option, +it creates servers for that particular class of transports. +When +executed with the +.Fl n +option, +it creates a server for the transport specified by +.Em netid . +If +.Ar infile +is not specified, +.Nm +accepts the standard input. +.Pp +The C preprocessor, +.Xr cpp 1 +is run on the input file before it is actually interpreted by +.Nm +For each type of output file, +.Nm +defines a special preprocessor symbol for use by the +.Nm +programmer: +.Bl -tag -width RPC_CLNT +.It Dv RPC_HDR +defined when compiling into header files +.It Dv RPC_XDR +defined when compiling into +.Tn XDR +routines +.It Dv RPC_SVC +defined when compiling into server-side stubs +.It Dv RPC_CLNT +defined when compiling into client-side stubs +.It Dv RPC_TBL +defined when compiling into +.Tn RPC +dispatch tables +.El +.Pp +Any line beginning with +.Sq % +is passed directly into the output file, +uninterpreted by +.Nm . +.Pp +For every data type referred to in +.Ar infile +.Nm +assumes that there exists a +routine with the string +.Dq xdr_ +prepended to the name of the data type. +If this routine does not exist in the +.Tn RPC/XDR +library, it must be provided. +Providing an undefined data type +allows customization of +.Tn XDR +routines. +.Sh OPTIONS +.Bl -tag -width indent +.It Fl A +Generate an +.Fn svc_caller +function. +.It Fl a +Generate all the files including sample code for client and server side. +.It Fl B +Generate BSD cplusplus macros (__BEGIN_DECLS, __END_DECLS). +.It Fl b +Compile stubs in "backwards compatible" mode, disabling support for +transport-independent RPC. +The +.Fl b +should always be specified when generating files for +.Nx , +since there is no transport-independent RPC support in +.Nx . +.It Fl c +Compile into +.Tn XDR +routines. +.It Fl D Ar name Ns Op Ar =value +Define a symbol +.Dv name . +Equivalent to the +.Dv #define +directive in the source. +If no +.Dv value +is given, +.Dv value +is defined as 1. +This option may be specified more than once. +.It Fl h +Compile into C data-definitions (a header file). +The +.Fl T +option can be used in conjunction to produce a +header file which supports +.Tn RPC +dispatch tables. +.It Fl I +Support +.Xr inetd 8 +in the server side stubs. +Servers generated using this flag can either be standalone or +started from +.Xr inetd 8 . +If a server is started as standalone, then it places itself +in the background, unless +.Dv RCP_SVC_FG +is defined, or the server is compiled without +.Fl I . +.It Fl i Ar size +Size to decide when to start generating inline code. +The default size is 3. +.It Fl K Ar secs +By default, services created using +.Nm +wait 120 seconds +after servicing a request before exiting. +That interval can be changed using the +.Fl K +flag. +To create a server that exits immediately upon servicing a request, +.Dq Fl K No 0 +can be used. +To create a server that never exits, the appropriate argument is +.Dq Fl K No -1 . +.Pp +When monitoring for a server, +some port monitors, like the +.At V.4 +utility +.Ic listen , +.Em always +spawn a new process in response to a service request. +If it is known that a server will be used with such a monitor, the +server should exit immediately on completion. +For such servers, +.Nm +should be used with +.Dq Fl K No -1 . +.It Fl L +Server errors will be sent to syslog instead of stderr. +.It Fl l +Compile into client-side stubs. +.Xr inetd 8 . +.It Fl M +Generate thread-safe stubs. +This alters the calling pattern of client and +server stubs so that storage for results is allocated by the caller. +Note +that all components for a particular service (stubs, client and service +wrappers, etc.) must be built either with or without the +.Fl M +flag. +.It Fl m +Compile into server-side stubs, +but do not generate a +.Fn main +routine. +This option is useful for doing callback-routines +and for users who need to write their own +.Fn main +routine to do initialization. +.It Fl N +Use the newstyle of +.Nm . +This allows procedures to have multiple arguments. +It also uses the style of parameter passing that closely resembles C. +So, when passing an argument to a remote procedure you do not have +to pass a pointer to the argument but the argument itself. +This behaviour is different from the oldstyle +of +.Nm +generated code. +The newstyle is not the default case because of backward compatibility. +.It Fl n Ar netid +Compile into server-side stubs for the transport +specified by +.Ar netid . +There should be an entry for +.Ar netid +in the +netconfig database. +This option may be specified more than once, +so as to compile a server that serves multiple transports. +.It Fl o Ar outfile +Specify the name of the output file. +If none is specified, +standard output is used +.Po +.Fl c Fl h Fl l +.Fl m Fl n Fl s +modes only +.Pc +.It Fl s Ar nettype +Compile into server-side stubs for all the +transports belonging to the class +.Ar nettype . +The supported classes are +.Em netpath , +.Em visible , +.Em circuit_n , +.Em circuit_v , +.Em datagram_n , +.Em datagram_v , +.Em tcp , +and +.Em udp +[see +.Xr rpc 3 +for the meanings associated with these classes. +.Em Note : +.Bx +currently supports only the +.Em tcp +and +.Em udp +classes]. +This option may be specified more than once. +.Em Note : +the transports are chosen at run time and not at compile time. +.It Fl S\&c +Generate sample code to show the use of remote procedure and how to bind +to the server before calling the client side stubs generated by +.Nm . +.It Fl S\&s +Generate skeleton code for the remote procedures on the server side. +You would need +to fill in the actual code for the remote procedures. +.\" .It Fl S\&m +.\" Generate a sample Makefile that can be used to compile the application. +.It Fl T +Generate the code to support +.Tn RPC +dispatch tables. +.It Fl t +Compile into +.Tn RPC +dispatch table. +.It Fl v +Display the version number. +.It Fl Y Ar pathname +Specify the directory where +.Nm +looks for the C pre-processor. +.El +.Pp +The options +.Fl c , +.Fl h , +.Fl l , +.Fl m , +.Fl s , +and +.Fl t +are used exclusively to generate a particular type of file, +while the options +.Fl D +and +.Fl T +are global and can be used with the other options. +.Sh ENVIRONMENT +If the +.Ev RPCGEN_CPP +environment variable is set, its value is used as the pathname of the +C preprocessor to be run on the input file. +.Sh NOTES +The +.Tn RPC +Language does not support nesting of structures. +As a work-around, +structures can be declared at the top-level, +and their name used inside other structures in +order to achieve the same effect. +.Pp +Name clashes can occur when using program definitions, +since the apparent scoping does not really apply. +Most of these can be avoided by giving +unique names for programs, +versions, +procedures and types. +.Pp +The server code generated with +.Fl n +option refers to the transport indicated by +.Em netid +and hence is very site specific. +.Sh EXAMPLES +The command +.Pp +.Bd -literal -offset indent +$ rpcgen -T prot.x +.Ed +.Pp +generates the five files: +.Pa prot.h , +.Pa prot_clnt.c , +.Pa prot_svc.c , +.Pa prot_xdr.c +and +.Pa prot_tbl.i . +.Pp +The following example sends the C data-definitions (header file) +to standard output. +.Pp +.Bd -literal -offset indent +$ rpcgen -h prot.x +.Ed +.Pp +To send the test version of the +.Dv -DTEST , +server side stubs for +all the transport belonging to the class +.Em datagram_n +to standard output, use: +.Pp +.Bd -literal -offset indent +$ rpcgen -s datagram_n -DTEST prot.x +.Ed +.Pp +To create the server side stubs for the transport indicated by +.Em netid +.Em tcp , +use: +.Pp +.Bd -literal -offset indent +$ rpcgen -n tcp -o prot_svc.c prot.x +.Ed +.Sh SEE ALSO +.Xr cpp 1 , +.Xr inetd 8 +.Sh HISTORY +The +.Fl M +option was first implemented in RedHat Linux, and was reimplemented by +Charles M. Hannum in +.Nx 1.6 . diff --git a/buildrump.sh/src/usr.bin/rump_allserver/Makefile b/buildrump.sh/src/usr.bin/rump_allserver/Makefile new file mode 100644 index 00000000..5af05cb0 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rump_allserver/Makefile @@ -0,0 +1,17 @@ +# $NetBSD: Makefile,v 1.7 2013/11/13 17:47:27 pooka Exp $ +# + +PROG= rump_allserver +MLINKS+=rump_allserver.1 rump_server.1 + +RUMPTOP=${.CURDIR}/../../sys/rump + +.include "${RUMPTOP}/dev/Makefile.rumpdevcomp" +.include "${RUMPTOP}/fs/Makefile.rumpfscomp" +.include "${RUMPTOP}/kern/Makefile.rumpkerncomp" +.include "${RUMPTOP}/net/Makefile.rumpnetcomp" + +LDADD+= ${RUMPDEVLDADD} ${RUMPFSLDADD} ${RUMPKERNLDADD} ${RUMPNETLDADD} +LDADD+= -lrumpdev -lrumpvfs -lrumpnet -lrump -lrumpuser -lpthread + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/rump_allserver/rump_allserver.1 b/buildrump.sh/src/usr.bin/rump_allserver/rump_allserver.1 new file mode 100644 index 00000000..dc8c0b67 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rump_allserver/rump_allserver.1 @@ -0,0 +1,230 @@ +.\" $NetBSD: rump_allserver.1,v 1.21 2014/01/16 00:32:48 pooka Exp $ +.\" +.\" Copyright (c) 2010 Antti Kantee. 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.Dd January 15, 2014 +.Dt RUMP_SERVER 1 +.Os +.Sh NAME +.Nm rump_server , +.Nm rump_allserver +.Nd rump kernel server +.Sh SYNOPSIS +.Nm +.Op Fl s +.Op Fl c Ar ncpu +.Op Fl d Ar drivespec +.Op Fl l Ar library +.Op Fl m Ar module +.Ar url +.Sh DESCRIPTION +The +.Nm +utility is used to provide a rump kernel service. +Clients can use the system calls provided by +.Nm +via +.Ar url . +.Pp +The difference between +.Nm +and +.Nm rump_allserver +is that +.Nm +offers only a minimalistic set of features, +while +.Nm rump_allserver +provides all rump kernel components which were available when the +system was built. +At execution time it is possible to load components from the command +line as described in the options section. +.Bl -tag -width indent +.It Fl c Ar ncpu +Configure +.Ar ncpu +virtual CPUs on SMP-capable archs. +By default, the number of CPUs equals the number of CPUs on the +host. +.It Fl d Ar drivespec +The argument +.Ar drivespec +maps a host file in the rump kernel fs namespace. +The string +.Ar drivespec +must be of comma-separated +.Dq name=value +format and must contain the following tokens: +.Bl -tag -width hostpath1234 +.It Ar key +Block device path in rump kernel namespace. +This must be specified according to the rules for a key in +.Xr rump_etfs 3 . +.It Ar hostpath +Host file used for storage. +If the file does not exist, it will be created. +.It Ar size +Size of the mapping. +Similar to +.Xr dd 1 , +this argument accepts a suffix as the multiplier for the number. +The special value +.Dq host +indicates that the current size of +.Ar hostpath +will be used. +In this case it is assumed that +.Ar hostpath +exists and is a regular file. +.It OR +.It Ar disklabel +Use a disklabel partition identifier to specify the offset and size +of the mapping. +.Ar hostpath +must contain an existing and valid disklabel within the first 64k. +.El +.Pp +The following are optional: +.Bl -tag -width hostpath1234 +.It Ar offset +Offset of the mapping. +The window into +.Ar hostpath +therefore is +.Fa [ offset , offset+size ] . +In case this parameter is not given, the default value 0 is used. +.It Ar type +The type of file that +.Ar key +is exposed as within the rump kernel. +The possibilities are +.Dq blk , +.Dq chr , +and +.Dq reg +for block device, character device and regular file, respectively. +The default is a block device. +.Pp +Note: the contents of block devices are cached in the rump kernel's +buffer cache. +To avoid cache incoherency, it is advisable not to access a file +through the host namespace while it is mapped as a block device in +a rump kernel. +.El +.Pp +In case +.Ar hostpath +does not exist, it will be created as a regular file with mode +0644 (plus any restrictions placed by umask). +In case +.Ar hostpath +is a regular file and is not large enough to accommodate the +specified size, it will be extended to the specified size. +.It Fl l Ar library +Call +.Fn dlopen +on library before initializing the rump kernel. +In case +.Ar library +provides a kernel module, it will appear as a builtin module in the +rump kernel. +Any rump kernel component present in +.Ar library +will also be initialized. +.Pp +The argument +.Ar library +can contain a full path or a filename, in which case the standard +dynamic library search path will be used. +By default, lazy resolution is used, and may result in a runtime +error due to missing components. +To test a configuration, run +.Nm +with +.Ev LD_BIND_NOW=1 +(see examples). +.It Fl m Ar module +Load and link a kernel module after the rump kernel is initialized. +For this to work, the rump kernel must include the vfs faction, +since the module is loaded using kernel vfs code (see +.Sx EXAMPLES ) . +.It Fl r Ar total_ram +Sets the limit of kernel memory allocatable by the server to +.Ar total_ram +as opposed to the default which allows the server to allocate as much +memory as the host will give it. +This parameter is especially useful for VFS servers, since by +default the virtual file system will attempt to consume as much +memory as it can, and accessing large files can cause an excessive +amount of memory to be used as file system cache. +.It Fl s +Do not detach from the terminal. +By default, +.Nm +detaches from the terminal once the service is running on +.Ar url . +.It Fl v +Set bootverbose. +.El +.Pp +After use, +.Nm +can be made to exit using +.Xr rump.halt 1 . +.Sh EXAMPLES +Start a server and load the tmpfs file system module, and halt the +server immediately afterwards: +.Bd -literal -offset indent +$ rump_server -lrumpvfs -m /modules/tmpfs.kmod unix://sock +$ env RUMP_SERVER=unix://sock rump.halt +.Ed +.Pp +Start a server with the one gigabyte host file +.Pa dk.img +mapped as the block device +.Pa /dev/dk +in the rump kernel. +.Bd -literal -offset indent +$ rump_allserver -d key=/dev/dk,hostpath=dk.img,size=1g unix://sock +.Ed +.Pp +Start a server which listens on INADDR_ANY port 3755 +.Bd -literal -offset indent +$ rump_server tcp://0:3755/ +.Ed +.Pp +Test that a configuration contains all of the necessary components: +.Bd -literal -offset indent +$ env LD_BIND_NOW=1 rump_server -lrumpvfs -lrumpfs_ffs unix://tsock +.Ed +.Pp +Start a FFS server with a 16MB kernel memory limit. +.Bd -literal -offset indent +$ rump_server -lrumpfs_ffs [...] -r 16m unix://ffs +.Ed +.Sh SEE ALSO +.Xr rump.halt 1 , +.Xr dlopen 3 , +.Xr rump 3 , +.Xr rump_sp 7 diff --git a/buildrump.sh/src/usr.bin/rump_allserver/rump_allserver.c b/buildrump.sh/src/usr.bin/rump_allserver/rump_allserver.c new file mode 100644 index 00000000..0104c124 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rump_allserver/rump_allserver.c @@ -0,0 +1,789 @@ +/* $NetBSD: rump_allserver.c,v 1.39 2015/04/16 10:05:43 pooka Exp $ */ + +/*- + * Copyright (c) 2010, 2011 Antti Kantee. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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 <rump/rumpuser_port.h> + +#ifndef lint +__RCSID("$NetBSD: rump_allserver.c,v 1.39 2015/04/16 10:05:43 pooka Exp $"); +#endif /* !lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <dlfcn.h> +#include <errno.h> +#include <fcntl.h> +#include <semaphore.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <string.h> +#include <unistd.h> + +#include <rump/rump.h> +#include <rump/rump_syscalls.h> +#include <rump/rumpdefs.h> +#include <rump/rumperr.h> + +__dead static void +usage(void) +{ + +#ifndef HAVE_GETPROGNAME +#define getprogname() "rump_server" +#endif + fprintf(stderr, "usage: %s [-s] [-c ncpu] [-d drivespec] [-l libs] " + "[-m modules] bindurl\n", getprogname()); + exit(1); +} + +__dead static void +diedie(int sflag, const char *reason, int error, const char *errstr) +{ + + if (reason != NULL) + fputs(reason, stderr); + if (errstr) { + fprintf(stderr, ": %s", errstr); + } + fputc('\n', stderr); + if (!sflag) + rump_daemonize_done(error); + exit(1); +} + +__dead static void +die(int sflag, int error, const char *reason) +{ + + diedie(sflag, reason, error, error == 0 ? NULL : strerror(error)); +} + +__dead static void +die_rumperr(int sflag, int error, const char *reason) +{ + + diedie(sflag, reason, error, error == 0 ? NULL : rump_strerror(error)); +} + +static sem_t sigsem; +static void +sigreboot(int sig) +{ + + sem_post(&sigsem); +} + +static const char *const disktokens[] = { +#define DKEY 0 + "key", +#define DFILE 1 + "hostpath", +#define DSIZE 2 +#define DSIZE_E -1 + "size", +#define DOFFSET 3 + "offset", +#define DLABEL 4 + "disklabel", +#define DTYPE 5 + "type", + NULL +}; + +struct etfsreg { + const char *key; + const char *hostpath; + off_t flen; + off_t foffset; + char partition; + enum rump_etfs_type type; +}; + +struct etfstype { + const char *name; + enum rump_etfs_type type; +} etfstypes[] = { + { "blk", RUMP_ETFS_BLK }, + { "chr", RUMP_ETFS_CHR }, + { "reg", RUMP_ETFS_REG }, +}; + +static void processlabel(int, int, int, off_t *, off_t *); + +#define ALLOCCHUNK 32 + +int +main(int argc, char *argv[]) +{ + const char *serverurl; + struct etfsreg *etfs = NULL; + unsigned netfs = 0, curetfs = 0; + int error; + int ch, sflag, onthepath; + unsigned i; + char **modarray = NULL, **libarray = NULL; + unsigned nmods = 0, curmod = 0, nlibs = 0, curlib = 0, libidx; + unsigned liblast = -1; /* XXXgcc */ + + setprogname(argv[0]); + sflag = 0; + while ((ch = getopt(argc, argv, "c:d:l:m:r:sv")) != -1) { + switch (ch) { + case 'c': + setenv("RUMP_NCPU", optarg, 1); + break; + case 'd': { + char *options, *value; + char *key, *hostpath; + long long flen, foffset; + char partition; + int ftype; + + flen = foffset = 0; + partition = 0; + key = hostpath = NULL; + ftype = -1; + options = optarg; + while (*options) { + switch (getsubopt(&options, + __UNCONST(disktokens), &value)) { + case DKEY: + if (key != NULL) { + fprintf(stderr, + "key already given\n"); + usage(); + } + key = value; + break; + + case DFILE: + if (hostpath != NULL) { + fprintf(stderr, + "hostpath already given\n"); + usage(); + } + hostpath = value; + break; + + case DSIZE: + if (flen != 0) { + fprintf(stderr, + "size already given\n"); + usage(); + } + if (strcmp(value, "host") == 0) { + if (foffset != 0) { + fprintf(stderr, + "cannot specify " + "offset with " + "size=host\n"); + usage(); + } + flen = DSIZE_E; + } else { +#ifdef HAVE_STRSUFTOLL + /* XXX: off_t max? */ + flen = strsuftoll("-d size", + value, 0, LLONG_MAX); +#else + flen = strtoull(value, + NULL, 10); +#endif + } + break; + case DOFFSET: + if (foffset != 0) { + fprintf(stderr, + "offset already given\n"); + usage(); + } + if (flen == DSIZE_E) { + fprintf(stderr, "cannot " + "specify offset with " + "size=host\n"); + usage(); + } +#ifdef HAVE_STRSUFTOLL + /* XXX: off_t max? */ + foffset = strsuftoll("-d offset", value, + 0, LLONG_MAX); +#else + foffset = strtoull(value, NULL, 10); +#endif + break; + + case DLABEL: + if (foffset != 0 || flen != 0) { + fprintf(stderr, + "disklabel needs to be " + "used alone\n"); + usage(); + } + if (strlen(value) != 1 || + *value < 'a' || *value > 'z') { + fprintf(stderr, + "invalid label part\n"); + usage(); + } + partition = *value; + break; + + case DTYPE: + if (ftype != -1) { + fprintf(stderr, + "type already specified\n"); + usage(); + } + + for (i = 0; + i < __arraycount(etfstypes); + i++) { + if (strcmp(etfstypes[i].name, + value) == 0) + break; + } + if (i == __arraycount(etfstypes)) { + fprintf(stderr, + "invalid type %s\n", value); + usage(); + } + ftype = etfstypes[i].type; + break; + + default: + fprintf(stderr, "invalid dtoken\n"); + usage(); + break; + } + } + + if (key == NULL || hostpath == NULL || + (flen == 0 + && partition == 0 && ftype != RUMP_ETFS_REG)) { + fprintf(stderr, "incomplete drivespec\n"); + usage(); + } + if (ftype == -1) + ftype = RUMP_ETFS_BLK; + + if (netfs - curetfs == 0) { + etfs = realloc(etfs, + (netfs+ALLOCCHUNK)*sizeof(*etfs)); + if (etfs == NULL) + die(1, errno, "realloc etfs"); + netfs += ALLOCCHUNK; + } + + etfs[curetfs].key = key; + etfs[curetfs].hostpath = hostpath; + etfs[curetfs].flen = flen; + etfs[curetfs].foffset = foffset; + etfs[curetfs].partition = partition; + etfs[curetfs].type = ftype; + curetfs++; + + break; + } + case 'l': + if (nlibs - curlib == 0) { + libarray = realloc(libarray, + (nlibs+ALLOCCHUNK) * sizeof(char *)); + if (libarray == NULL) + die(1, errno, "realloc"); + nlibs += ALLOCCHUNK; + } + libarray[curlib++] = optarg; + break; + case 'm': + if (nmods - curmod == 0) { + modarray = realloc(modarray, + (nmods+ALLOCCHUNK) * sizeof(char *)); + if (modarray == NULL) + die(1, errno, "realloc"); + nmods += ALLOCCHUNK; + } + modarray[curmod++] = optarg; + break; + case 'r': + setenv("RUMP_MEMLIMIT", optarg, 1); + break; + case 's': + sflag = 1; + break; + case 'v': + setenv("RUMP_VERBOSE", "1", 1); + break; + default: + usage(); + /*NOTREACHED*/ + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + /* + * Automatically "resolve" component dependencies, i.e. + * try to load libs in a loop until all are loaded or a + * full loop completes with no loads (latter case is an error). + */ + for (onthepath = 1, nlibs = curlib; onthepath && nlibs > 0;) { + onthepath = 0; + for (libidx = 0; libidx < curlib; libidx++) { + /* loaded already? */ + if (libarray[libidx] == NULL) + continue; + + /* try to load */ + liblast = libidx; + if (dlopen(libarray[libidx], + RTLD_LAZY|RTLD_GLOBAL) == NULL) { + char pb[MAXPATHLEN]; + /* try to mimic linker -l syntax */ + snprintf(pb, sizeof(pb), + "lib%s.so", libarray[libidx]); + if (dlopen(pb, RTLD_LAZY|RTLD_GLOBAL) == NULL) + continue; + } + + /* managed to load that one */ + libarray[libidx] = NULL; + nlibs--; + onthepath = 1; + } + } + if (nlibs > 0) { + fprintf(stderr, + "failed to load -libraries, last error from \"%s\":\n", + libarray[liblast]); + fprintf(stderr, " %s", dlerror()); + die(1, 0, NULL); + } + free(libarray); + + serverurl = argv[0]; + + if (!sflag) { + error = rump_daemonize_begin(); + if (error) + die_rumperr(1, error, "rump daemonize"); + } + + error = rump_init(); + if (error) + die_rumperr(sflag, error, "rump init failed"); + + /* load modules */ + for (i = 0; i < curmod; i++) { + struct rump_modctl_load ml; + +#define ETFSKEY "/module.mod" + if ((error = rump_pub_etfs_register(ETFSKEY, + modarray[0], RUMP_ETFS_REG)) != 0) + die_rumperr(sflag, + error, "module etfs register failed"); + memset(&ml, 0, sizeof(ml)); + ml.ml_filename = ETFSKEY; + /* + * XXX: since this is a syscall, error namespace depends + * on loaded emulations. revisit and fix. + */ + if (rump_sys_modctl(RUMP_MODCTL_LOAD, &ml) == -1) + die(sflag, errno, "module load failed"); + rump_pub_etfs_remove(ETFSKEY); +#undef ETFSKEY + } + free(modarray); + + /* register host drives */ + for (i = 0; i < curetfs; i++) { + struct stat sb; + off_t foffset, flen, fendoff; + int fd, oflags; + + oflags = etfs[i].flen == DSIZE_E ? 0 : O_CREAT; + fd = open(etfs[i].hostpath, O_RDWR | oflags, 0644); + if (fd == -1) + die(sflag, errno, "etfs hostpath open"); + + if (etfs[i].partition) { + processlabel(sflag, fd, etfs[i].partition - 'a', + &foffset, &flen); + } else { + foffset = etfs[i].foffset; + flen = etfs[i].flen; + } + + if (fstat(fd, &sb) == -1) + die(sflag, errno, "fstat etfs hostpath"); + if (flen == DSIZE_E) { + if (sb.st_size == 0) + die(sflag, EINVAL, "size=host, but cannot " + "query non-zero size"); + flen = sb.st_size; + } + fendoff = foffset + flen; + if (S_ISREG(sb.st_mode) && sb.st_size < fendoff) { + if (ftruncate(fd, fendoff) == -1) + die(sflag, errno, "truncate"); + } + close(fd); + + if ((error = rump_pub_etfs_register_withsize(etfs[i].key, + etfs[i].hostpath, etfs[i].type, foffset, flen)) != 0) + die_rumperr(sflag, error, "etfs register"); + } + + error = rump_init_server(serverurl); + if (error) + die_rumperr(sflag, error, "rump server init failed"); + + if (!sflag) + rump_daemonize_done(RUMP_DAEMONIZE_SUCCESS); + + sem_init(&sigsem, 0, 0); + signal(SIGTERM, sigreboot); + signal(SIGINT, sigreboot); + sem_wait(&sigsem); + + rump_sys_reboot(0, NULL); + /*NOTREACHED*/ + + return 0; +} + +/* + * Copyright (c) 1987, 1988, 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. + * + * @(#)disklabel.h 8.2 (Berkeley) 7/10/94 + */ + +#define RUMPSERVER_MAXPARTITIONS 22 +#define RUMPSERVER_DISKMAGIC ((uint32_t)0x82564557) /* magic */ +#define RUMPSERVER_DEVSHIFT 9 + +struct rumpserver_disklabel { + uint32_t d_magic; /* the magic number */ + uint16_t d_type; /* drive type */ + uint16_t d_subtype; /* controller/d_type specific */ + char d_typename[16]; /* type name, e.g. "eagle" */ + + /* + * d_packname contains the pack identifier and is returned when + * the disklabel is read off the disk or in-core copy. + * d_boot0 and d_boot1 are the (optional) names of the + * primary (block 0) and secondary (block 1-15) bootstraps + * as found in /usr/mdec. These are returned when using + * getdiskbyname(3) to retrieve the values from /etc/disktab. + */ + union { + char un_d_packname[16]; /* pack identifier */ + struct { + char *un_d_boot0; /* primary bootstrap name */ + char *un_d_boot1; /* secondary bootstrap name */ + } un_b; + } d_un; +#define d_packname d_un.un_d_packname +#define d_boot0 d_un.un_b.un_d_boot0 +#define d_boot1 d_un.un_b.un_d_boot1 + + /* disk geometry: */ + uint32_t d_secsize; /* # of bytes per sector */ + uint32_t d_nsectors; /* # of data sectors per track */ + uint32_t d_ntracks; /* # of tracks per cylinder */ + uint32_t d_ncylinders; /* # of data cylinders per unit */ + uint32_t d_secpercyl; /* # of data sectors per cylinder */ + uint32_t d_secperunit; /* # of data sectors per unit */ + + /* + * Spares (bad sector replacements) below are not counted in + * d_nsectors or d_secpercyl. Spare sectors are assumed to + * be physical sectors which occupy space at the end of each + * track and/or cylinder. + */ + uint16_t d_sparespertrack; /* # of spare sectors per track */ + uint16_t d_sparespercyl; /* # of spare sectors per cylinder */ + /* + * Alternative cylinders include maintenance, replacement, + * configuration description areas, etc. + */ + uint32_t d_acylinders; /* # of alt. cylinders per unit */ + + /* hardware characteristics: */ + /* + * d_interleave, d_trackskew and d_cylskew describe perturbations + * in the media format used to compensate for a slow controller. + * Interleave is physical sector interleave, set up by the + * formatter or controller when formatting. When interleaving is + * in use, logically adjacent sectors are not physically + * contiguous, but instead are separated by some number of + * sectors. It is specified as the ratio of physical sectors + * traversed per logical sector. Thus an interleave of 1:1 + * implies contiguous layout, while 2:1 implies that logical + * sector 0 is separated by one sector from logical sector 1. + * d_trackskew is the offset of sector 0 on track N relative to + * sector 0 on track N-1 on the same cylinder. Finally, d_cylskew + * is the offset of sector 0 on cylinder N relative to sector 0 + * on cylinder N-1. + */ + uint16_t d_rpm; /* rotational speed */ + uint16_t d_interleave; /* hardware sector interleave */ + uint16_t d_trackskew; /* sector 0 skew, per track */ + uint16_t d_cylskew; /* sector 0 skew, per cylinder */ + uint32_t d_headswitch; /* head switch time, usec */ + uint32_t d_trkseek; /* track-to-track seek, usec */ + uint32_t d_flags; /* generic flags */ +#define NDDATA 5 + uint32_t d_drivedata[NDDATA]; /* drive-type specific information */ +#define NSPARE 5 + uint32_t d_spare[NSPARE]; /* reserved for future use */ + uint32_t d_magic2; /* the magic number (again) */ + uint16_t d_checksum; /* xor of data incl. partitions */ + + /* filesystem and partition information: */ + uint16_t d_npartitions; /* number of partitions in following */ + uint32_t d_bbsize; /* size of boot area at sn0, bytes */ + uint32_t d_sbsize; /* max size of fs superblock, bytes */ + struct rumpserver_partition { /* the partition table */ + uint32_t p_size; /* number of sectors in partition */ + uint32_t p_offset; /* starting sector */ + union { + uint32_t fsize; /* FFS, ADOS: + filesystem basic fragment size */ + uint32_t cdsession; /* ISO9660: session offset */ + } __partition_u2; +#define p_fsize __partition_u2.fsize +#define p_cdsession __partition_u2.cdsession + uint8_t p_fstype; /* filesystem type, see below */ + uint8_t p_frag; /* filesystem fragments per block */ + union { + uint16_t cpg; /* UFS: FS cylinders per group */ + uint16_t sgs; /* LFS: FS segment shift */ + } __partition_u1; +#define p_cpg __partition_u1.cpg +#define p_sgs __partition_u1.sgs + } d_partitions[RUMPSERVER_MAXPARTITIONS]; /* actually may be more */ +}; + + +/* for swapping disklabel, so don't care about perf, just portability */ +#define bs32(x) \ + ((((x) & 0xff000000) >> 24)| \ + (((x) & 0x00ff0000) >> 8) | \ + (((x) & 0x0000ff00) << 8) | \ + (((x) & 0x000000ff) << 24)) +#define bs16(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8)) + +/* + * From: + * $NetBSD: disklabel_dkcksum.c,v 1.4 2005/05/15 21:01:34 thorpej Exp + */ + +/*- + * Copyright (c) 1991, 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. + */ + +static uint16_t +rs_dl_dkcksum(struct rumpserver_disklabel *lp, int imswapped) +{ + uint16_t *start, *end; + uint16_t sum; + uint16_t npart; + + if (imswapped) + npart = bs16(lp->d_npartitions); + else + npart = lp->d_npartitions; + + sum = 0; + start = (uint16_t *)(void *)lp; + end = (uint16_t *)(void *)&lp->d_partitions[npart]; + while (start < end) { + if (imswapped) + sum ^= bs16(*start); + else + sum ^= *start; + start++; + } + return (sum); +} + +/* + * From: + * NetBSD: disklabel_scan.c,v 1.3 2009/01/18 12:13:03 lukem Exp + */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Roland C. Dowdeswell. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +static int +rs_dl_scan(struct rumpserver_disklabel *lp, int *isswapped, + char *buf, size_t buflen) +{ + size_t i; + int imswapped; + uint16_t npart; + + /* scan for the correct magic numbers. */ + + for (i=0; i <= buflen - sizeof(*lp); i += 4) { + memcpy(lp, buf + i, sizeof(*lp)); + if (lp->d_magic == RUMPSERVER_DISKMAGIC && + lp->d_magic2 == RUMPSERVER_DISKMAGIC) { + imswapped = 0; + goto sanity; + } + if (lp->d_magic == bs32(RUMPSERVER_DISKMAGIC) && + lp->d_magic2 == bs32(RUMPSERVER_DISKMAGIC)) { + imswapped = 1; + goto sanity; + } + } + + return 1; + +sanity: + if (imswapped) + npart = bs16(lp->d_npartitions); + else + npart = lp->d_npartitions; + /* we've found something, let's sanity check it */ + if (npart > RUMPSERVER_MAXPARTITIONS + || rs_dl_dkcksum(lp, imswapped)) + return 1; + + *isswapped = imswapped; + return 0; +} + +static void +processlabel(int sflag, int fd, int partition, off_t *foffp, off_t *flenp) +{ + struct rumpserver_disklabel dl; + char buf[1<<16]; + uint32_t foffset, flen; + int imswapped; + + if (pread(fd, buf, sizeof(buf), 0) == -1) + die(sflag, errno, "could not read disk device"); + if (rs_dl_scan(&dl, &imswapped, buf, sizeof(buf))) + die(sflag, ENOENT, "disklabel not found"); + + if (partition >= dl.d_npartitions) + die(sflag, ENOENT, "partition not available"); + + foffset = dl.d_partitions[partition].p_offset << RUMPSERVER_DEVSHIFT; + flen = dl.d_partitions[partition].p_size << RUMPSERVER_DEVSHIFT; + if (imswapped) { + foffset = bs32(foffset); + flen = bs32(flen); + } + + *foffp = (off_t)foffset; + *flenp = (off_t)flen; +} diff --git a/buildrump.sh/src/usr.bin/rump_server/Makefile b/buildrump.sh/src/usr.bin/rump_server/Makefile new file mode 100644 index 00000000..492f6bd4 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rump_server/Makefile @@ -0,0 +1,13 @@ +# $NetBSD: Makefile,v 1.8 2015/01/07 22:24:03 pooka Exp $ +# + +.PATH: ${.CURDIR}/../rump_allserver + +PROG= rump_server +SRCS= rump_allserver.c +NOMAN= installed by ../rump_allserver + +LDADD+= -Wl,--whole-archive -lrumpkern_sysproxy -lrump \ + -lrumpuser -Wl,--no-whole-archive -lpthread + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/rump_wmd/Makefile b/buildrump.sh/src/usr.bin/rump_wmd/Makefile new file mode 100644 index 00000000..1208c068 --- /dev/null +++ b/buildrump.sh/src/usr.bin/rump_wmd/Makefile @@ -0,0 +1,7 @@ +# $NetBSD: Makefile,v 1.1 2014/01/16 01:54:47 pooka Exp $ +# + +MAN= rump_wmd.1 +SCRIPTS=rump_wmd.sh + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/rump_wmd/rump_wmd.1 b/buildrump.sh/src/usr.bin/rump_wmd/rump_wmd.1 new file mode 100644 index 00000000..278b5dcd --- /dev/null +++ b/buildrump.sh/src/usr.bin/rump_wmd/rump_wmd.1 @@ -0,0 +1,98 @@ +.\" $NetBSD: rump_wmd.1,v 1.3 2014/01/28 14:02:54 pooka Exp $ +.\" +.\" Copyright (c) 2014 Antti Kantee. 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.Dd January 28, 2014 +.Dt RUMP_WMD 1 +.Os +.Sh NAME +.Nm rump_wmd +.Nd Resolve rump kernel component dependencies +.Sh SYNOPSIS +.Nm +.Op Fl hv +.Op Fl L Ar dir +.Fl l Ar component +.Sh DESCRIPTION +The +.Nm +utility can be used to resolve the link dependencies of rump kernel +components. +This functionality is useful when desiring to run a rump kernel with +a given set of drivers, but not being sure what the dependencies of +those drivers are. +The output of +.Nm +is accepted as command line input by +.Xr rump_server 1 . +.Pp +The command line options are: +.Bl -tag -width indent +.It Fl h +Print the usage. +.It Fl L Ar dir +By default, rump kernel components in +.Pa /usr/lib +are examined. +If this option is supplied, components in +.Ar dir +are examined instead. +.It Fl v +Increase debug output from the utility. +This option may be given multiple times. +.El +.Pp +The desired components are given using +.Fl l Ar component . +This parameter may be specified multiple time and must be the last +set of parameters. +.Sh EXAMPLES +Resolve dependencies for FFS: +.Bd -literal -offset indent +$ rump_wmd -lrumpfs_ffs +DEBUG0: Searching component combinations. This may take a while ... +DEBUG0: Found a set +-lrumpdev -lrumpdev_disk -lrumpvfs -lrumpfs_ffs +.Ed +.Pp +Resolve dependencies for NFS, use IPv6 networking and the virtif +network interface: +.Bd -literal -offset indent +$ rump_wmd -lrumpfs_nfs -lrumpnet_netinet6 -lrumpnet_virtif +DEBUG0: Searching component combinations. This may take a while ... +DEBUG0: Found a set +-lrumpnet -lrumpnet_net -lrumpvfs -lrumpfs_nfs -lrumpnet_netinet6 -lrumpnet_virtif +.Ed +.Sh SEE ALSO +.Xr rump_server 1 +.Sh CAVEATS +Since +.Nm +uses trial-and-error brute force resolution, it runs somewhat slow. +If several seconds are spent for dependency resolution, the benefit of +a rump kernel booting in 10ms is somewhat lost. +Caching the output is highly recommended. +.Sh FUN FACTS +.Nm +is short for "rump, where's my dependency". diff --git a/buildrump.sh/src/usr.bin/rump_wmd/rump_wmd.sh b/buildrump.sh/src/usr.bin/rump_wmd/rump_wmd.sh new file mode 100755 index 00000000..26a495be --- /dev/null +++ b/buildrump.sh/src/usr.bin/rump_wmd/rump_wmd.sh @@ -0,0 +1,161 @@ +#!/bin/sh +# +# $NetBSD: rump_wmd.sh,v 1.4 2014/01/28 13:58:25 pooka Exp $ +# +# Copyright (c) 2014 Antti Kantee <pooka@iki.fi> +# +# 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. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. +# + +: ${CC:=cc} +DEBUGLEVEL=0 +LIBDIR=/usr/lib + +die () +{ + + echo $* >&2 + exit 1 +} + +usage () +{ + + die "Usage: $0 [-hv] [-L libdir] -lrump_component [...]" +} + +unset FIRSTLIB +while getopts 'hl:L:v' opt; do + case "${opt}" in + l) + : ${FIRSTLIB:=${OPTIND}} + ;; + L) + [ -z "${FIRSTLIB}" ] || usage + LIBDIR=${OPTARG} + ;; + v) + [ -z "${FIRSTLIB}" ] || usage + DEBUGLEVEL=$((DEBUGLEVEL + 1)) + ;; + -h|*) + usage + ;; + esac +done +[ -z "${FIRSTLIB}" ] && usage +shift $((${FIRSTLIB} - 2)) +[ $# -eq 0 ] && usage + +debug () +{ + + [ ${DEBUGLEVEL} -ge ${1} ] && \ + { lvl=$1; shift; echo DEBUG${lvl}: $* >&2; } +} + +# filters from list +filter () +{ + + filtee=$1 + vname=$2 + tmplist='' + found=false + for x in $(eval echo \${${vname}}); do + if [ "${filtee}" = "${x}" ]; then + found=true + else + tmplist="${tmplist} ${x}" + fi + done + ${found} || die \"${1}\" not found in \$${2} + + eval ${vname}="\${tmplist}" +} + +SEEDPROG='int rump_init(void); int main() { rump_init(); }' +CCPARAMS='-Wl,--no-as-needed -o /dev/null -x c -' + +# sanity-check +for lib in $* ; do + [ "${lib#-l}" = "${lib}" -o -z "${lib#-l}" ] \ + && die Param \"${lib}\" is not of format -llib +done + +# starting set and available components +WANTEDCOMP="$*" +RUMPBASE='-lrump -lrumpuser' +CURCOMP="${WANTEDCOMP}" +NEEDEDCOMP='' +ALLCOMP=$(ls ${LIBDIR} 2>/dev/null \ + | sed -n '/^librump.*.so$/{s/lib/-l/;s/\.so//p;}') +[ -z "${ALLCOMP}" ] && die No rump kernel components in \"${LIBDIR}\" + +# filter out ones we'll definitely not use +for f in ${CURCOMP} -lrumphijack -lrumpclient; do + filter ${f} ALLCOMP +done + +# put the factions first so that they'll be tried first. +# this is an optimization to minimize link attempts. +FACTIONS='-lrumpvfs -lrumpnet -lrumpdev' +for f in ${FACTIONS}; do + filter ${f} ALLCOMP +done +ALLCOMP="${FACTIONS} ${ALLCOMP}" + +debug 0 Searching component combinations. This may take a while ... +while :; do + debug 1 Current components: ${CURCOMP} + + IFS=' ' + out=$(echo ${SEEDPROG} \ + | ${CC} -L${LIBDIR} ${CCPARAMS} ${CURCOMP} ${RUMPBASE} 2>&1) + [ -z "${out}" ] && break + undef=$(echo ${out} \ + | sed -n '/undefined reference to/{s/.*`//;s/.$//p;q;}') + [ -z "${undef}" ] && break + + debug 1 Trying to find ${undef} + for attempt in ${ALLCOMP}; do + debug 2 Component attempt: ${attempt} + unset IFS + nowundef=$(echo ${SEEDPROG} \ + | ${CC} -L${LIBDIR} ${CCPARAMS} \ + ${attempt} ${CURCOMP} ${RUMPBASE} 2>&1 \ + | sed -n '/undefined reference to/{s/.*`//;s/.$//p;}') + for one in ${nowundef}; do + [ "${one}" = "${undef}" ] && continue 2 + done + CURCOMP="${attempt} ${CURCOMP}" + filter ${attempt} ALLCOMP + debug 1 Found ${undef} from ${attempt} + continue 2 + done + die Internal error: unreachable statement +done + +[ ! -z "${out}" ] && { echo 'ERROR:' >&2 ; echo ${out} >&2 ; exit 1 ; } +debug 0 Found a set +echo ${CURCOMP} +exit 0 diff --git a/buildrump.sh/src/usr.bin/sed/Makefile b/buildrump.sh/src/usr.bin/sed/Makefile new file mode 100644 index 00000000..48b4b485 --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/Makefile @@ -0,0 +1,10 @@ +# $NetBSD: Makefile,v 1.17 2014/06/07 16:37:32 christos Exp $ +# from: @(#)Makefile 8.1 (Berkeley) 6/6/93 + +.include <bsd.own.mk> +WARNS=6 + +PROG= sed +SRCS= compile.c main.c misc.c process.c + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/sed/POSIX b/buildrump.sh/src/usr.bin/sed/POSIX new file mode 100644 index 00000000..cbacd891 --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/POSIX @@ -0,0 +1,205 @@ +# $NetBSD: POSIX,v 1.5 2014/06/06 00:13:13 christos Exp $ +# @(#)POSIX 8.1 (Berkeley) 6/6/93 +# $FreeBSD: head/usr.bin/sed/POSIX 168417 2007-04-06 08:43:30Z yar $ + +Comments on the IEEE P1003.2 Draft 12 + Part 2: Shell and Utilities + Section 4.55: sed - Stream editor + +Diomidis Spinellis <dds@doc.ic.ac.uk> +Keith Bostic <bostic@cs.berkeley.edu> + +In the following paragraphs, "wrong" usually means "inconsistent with +historic practice", as most of the following comments refer to +undocumented inconsistencies between the historical versions of sed and +the POSIX 1003.2 standard. All the comments are notes taken while +implementing a POSIX-compatible version of sed, and should not be +interpreted as official opinions or criticism towards the POSIX committee. +All uses of "POSIX" refer to section 4.55, Draft 12 of POSIX 1003.2. + + 1. 32V and BSD derived implementations of sed strip the text + arguments of the a, c and i commands of their initial blanks, + i.e. + + #!/bin/sed -f + a\ + foo\ + \ indent\ + bar + + produces: + + foo + indent + bar + + POSIX does not specify this behavior as the System V versions of + sed do not do this stripping. The argument against stripping is + that it is difficult to write sed scripts that have leading blanks + if they are stripped. The argument for stripping is that it is + difficult to write readable sed scripts unless indentation is allowed + and ignored, and leading whitespace is obtainable by entering a + backslash in front of it. This implementation follows the BSD + historic practice. + + 2. Historical versions of sed required that the w flag be the last + flag to an s command as it takes an additional argument. This + is obvious, but not specified in POSIX. + + 3. Historical versions of sed required that whitespace follow a w + flag to an s command. This is not specified in POSIX. This + implementation permits whitespace but does not require it. + + 4. Historical versions of sed permitted any number of whitespace + characters to follow the w command. This is not specified in + POSIX. This implementation permits whitespace but does not + require it. + + 5. The rule for the l command differs from historic practice. Table + 2-15 includes the various ANSI C escape sequences, including \\ + for backslash. Some historical versions of sed displayed two + digit octal numbers, too, not three as specified by POSIX. POSIX + is a cleanup, and is followed by this implementation. + + 6. The POSIX specification for ! does not specify that for a single + command the command must not contain an address specification + whereas the command list can contain address specifications. The + specification for ! implies that "3!/hello/p" works, and it never + has, historically. Note, + + 3!{ + /hello/p + } + + does work. + + 7. POSIX does not specify what happens with consecutive ! commands + (e.g. /foo/!!!p). Historic implementations allow any number of + !'s without changing the behaviour. (It seems logical that each + one might reverse the behaviour.) This implementation follows + historic practice. + + 8. Historic versions of sed permitted commands to be separated + by semi-colons, e.g. 'sed -ne '1p;2p;3q' printed the first + three lines of a file. This is not specified by POSIX. + Note, the ; command separator is not allowed for the commands + a, c, i, w, r, :, b, t, # and at the end of a w flag in the s + command. This implementation follows historic practice and + implements the ; separator. + + 9. Historic versions of sed terminated the script if EOF was reached + during the execution of the 'n' command, i.e.: + + sed -e ' + n + i\ + hello + ' </dev/null + + did not produce any output. POSIX does not specify this behavior. + This implementation follows historic practice. + +10. Deleted. + +11. Historical implementations do not output the change text of a c + command in the case of an address range whose first line number + is greater than the second (e.g. 3,1). POSIX requires that the + text be output. Since the historic behavior doesn't seem to have + any particular purpose, this implementation follows the POSIX + behavior. + +12. POSIX does not specify whether address ranges are checked and + reset if a command is not executed due to a jump. The following + program will behave in different ways depending on whether the + 'c' command is triggered at the third line, i.e. will the text + be output even though line 3 of the input will never logically + encounter that command. + + 2,4b + 1,3c\ + text + + Historic implementations did not output the text in the above + example. Therefore it was believed that a range whose second + address was never matched extended to the end of the input. + However, the current practice adopted by this implementation, + as well as by those from GNU and SUN, is as follows: The text + from the 'c' command still isn't output because the second address + isn't actually matched; but the range is reset after all if its + second address is a line number. In the above example, only the + first line of the input will be deleted. + +13. Historical implementations allow an output suppressing #n at the + beginning of -e arguments as well as in a script file. POSIX + does not specify this. This implementation follows historical + practice. + +14. POSIX does not explicitly specify how sed behaves if no script is + specified. Since the sed Synopsis permits this form of the command, + and the language in the Description section states that the input + is output, it seems reasonable that it behave like the cat(1) + command. Historic sed implementations behave differently for "ls | + sed", where they produce no output, and "ls | sed -e#", where they + behave like cat. This implementation behaves like cat in both cases. + +15. The POSIX requirement to open all w files at the beginning makes + sed behave nonintuitively when the w commands are preceded by + addresses or are within conditional blocks. This implementation + follows historic practice and POSIX, by default, and provides the + -a option which opens the files only when they are needed. + +16. POSIX does not specify how escape sequences other than \n and \D + (where D is the delimiter character) are to be treated. This is + reasonable, however, it also doesn't state that the backslash is + to be discarded from the output regardless. A strict reading of + POSIX would be that "echo xyz | sed s/./\a" would display "\ayz". + As historic sed implementations always discarded the backslash, + this implementation does as well. + +17. POSIX specifies that an address can be "empty". This implies + that constructs like ",d" or "1,d" and ",5d" are allowed. This + is not true for historic implementations or this implementation + of sed. + +18. The b t and : commands are documented in POSIX to ignore leading + white space, but no mention is made of trailing white space. + Historic implementations of sed assigned different locations to + the labels "x" and "x ". This is not useful, and leads to subtle + programming errors, but it is historic practice and changing it + could theoretically break working scripts. This implementation + follows historic practice. + +19. Although POSIX specifies that reading from files that do not exist + from within the script must not terminate the script, it does not + specify what happens if a write command fails. Historic practice + is to fail immediately if the file cannot be opened or written. + This implementation follows historic practice. + +20. Historic practice is that the \n construct can be used for either + string1 or string2 of the y command. This is not specified by + POSIX. This implementation follows historic practice. + +21. Deleted. + +22. Historic implementations of sed ignore the RE delimiter characters + within character classes. This is not specified in POSIX. This + implementation follows historic practice. + +23. Historic implementations handle empty RE's in a special way: the + empty RE is interpreted as if it were the last RE encountered, + whether in an address or elsewhere. POSIX does not document this + behavior. For example the command: + + sed -e /abc/s//XXX/ + + substitutes XXX for the pattern abc. The semantics of "the last + RE" can be defined in two different ways: + + 1. The last RE encountered when compiling (lexical/static scope). + 2. The last RE encountered while running (dynamic scope). + + While many historical implementations fail on programs depending + on scope differences, the SunOS version exhibited dynamic scope + behaviour. This implementation does dynamic scoping, as this seems + the most useful and in order to remain consistent with historical + practice. diff --git a/buildrump.sh/src/usr.bin/sed/TEST/hanoi.sed b/buildrump.sh/src/usr.bin/sed/TEST/hanoi.sed new file mode 100644 index 00000000..e58a967f --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/TEST/hanoi.sed @@ -0,0 +1,103 @@ +# Towers of Hanoi in sed. +# +# from: @(#)hanoi.sed 8.1 (Berkeley) 6/6/93 +# $NetBSD: hanoi.sed,v 1.3 1997/01/09 20:21:35 tls Exp $ +# +# +# Ex: +# Run "sed -f hanoi.sed", and enter: +# +# :abcd: : :<CR><CR> +# +# note -- TWO carriage returns, a peculiarity of sed), this will output the +# sequence of states involved in moving 4 rings, the largest called "a" and +# the smallest called "d", from the first to the second of three towers, so +# that the rings on any tower at any time are in descending order of size. +# You can start with a different arrangement and a different number of rings, +# say :ce:b:ax: and it will give the shortest procedure for moving them all +# to the middle tower. The rules are: the names of the rings must all be +# lower-case letters, they must be input within 3 fields (representing the +# towers) and delimited by 4 colons, such that the letters within each field +# are in alphabetical order (i.e. rings are in descending order of size). +# +# For the benefit of anyone who wants to figure out the script, an "internal" +# line of the form +# b:0abx:1a2b3 :2 :3x2 +# has the following meaning: the material after the three markers :1, :2, +# and :3 represents the three towers; in this case the current set-up is +# ":ab : :x :". The numbers after a, b and x in these fields indicate +# that the next time it gets a chance, it will move a to tower 2, move b +# to tower 3, and move x to tower 2. The string after :0 just keeps track +# of the alphabetical order of the names of the rings. The b at the +# beginning means that it is now dealing with ring b (either about to move +# it, or re-evaluating where it should next be moved to). +# +# Although this version is "limited" to 26 rings because of the size of the +# alphabet, one could write a script using the same idea in which the rings +# were represented by arbitrary [strings][within][brackets], and in place of +# the built-in line of the script giving the order of the letters of the +# alphabet, it would accept from the user a line giving the ordering to be +# assumed, e.g. [ucbvax][decvax][hplabs][foo][bar]. +# +# George Bergman +# Math, UC Berkeley 94720 USA + +# cleaning, diagnostics +s/ *//g +/^$/d +/[^a-z:]/{a\ +Illegal characters: use only a-z and ":". Try again. +d +} +/^:[a-z]*:[a-z]*:[a-z]*:$/!{a\ +Incorrect format: use\ +\ : string1 : string2 : string3 :<CR><CR>\ +Try again. +d +} +/\([a-z]\).*\1/{a\ +Repeated letters not allowed. Try again. +d +} +# initial formatting +h +s/[a-z]/ /g +G +s/^:\( *\):\( *\):\( *\):\n:\([a-z]*\):\([a-z]*\):\([a-z]*\):$/:1\4\2\3:2\5\1\3:3\6\1\2:0/ +s/[a-z]/&2/g +s/^/abcdefghijklmnopqrstuvwxyz/ +:a +s/^\(.\).*\1.*/&\1/ +s/.// +/^[^:]/ba +s/\([^0]*\)\(:0.*\)/\2\1:/ +s/^[^0]*0\(.\)/\1&/ +:b +# outputting current state without markers +h +s/.*:1/:/ +s/[123]//gp +g +:c +# establishing destinations +/^\(.\).*\1:1/td +/^\(.\).*:1[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:1[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +/^\(.\).*:1[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:2[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +/^\(.\).*:2[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:2[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:3[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:3[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:3[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +bc +# iterate back to find smallest out-of-place ring +:d +s/^\(.\)\(:0[^:]*\([^:]\)\1.*:\([123]\)[^:]*\1\)\4/\3\2\4/ +td +# move said ring (right, resp. left) +s/^\(.\)\(.*\)\1\([23]\)\(.*:\3[^ ]*\) /\1\2 \4\1\3/ +s/^\(.\)\(.*:\([12]\)[^ ]*\) \(.*\)\1\3/\1\2\1\3\4 / +tb +s/.*/Done! Try another, or end with ^D./p +d diff --git a/buildrump.sh/src/usr.bin/sed/TEST/math.sed b/buildrump.sh/src/usr.bin/sed/TEST/math.sed new file mode 100644 index 00000000..82968b54 --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/TEST/math.sed @@ -0,0 +1,164 @@ +# +# from: @(#)math.sed 8.1 (Berkeley) 6/6/93 +# $NetBSD: math.sed,v 1.3 1997/01/09 20:21:36 tls Exp $ +# +# Addition and multiplication in sed. +# ++ for a limited time only do (expr) too!!! +# +# Kevin S Braunsdorf, PUCC UNIX Group, ksb@cc.purdue.edu. +# +# Ex: +# echo "4+7*3" | sed -f %f + +# make sure the expression is well formed +s/[ ]//g +/[+*\/-]$/{ + a\ + poorly formed expression, operator on the end + q +} +/^[+*\/]/{ + a\ + poorly formed expression, leading operator + q +} + +# fill hold space with done token +x +s/^.*/done/ +x + +# main loop, process operators (*, + and () ) +: loop +/^\+/{ + s/// + b loop +} +/^\(.*\)(\([^)]*\))\(.*\)$/{ + H + s//\2/ + x + s/^\(.*\)\n\(.*\)(\([^()]*\))\(.*\)$/()\2@\4@\1/ + x + b loop +} +/^[0-9]*\*/b mul +/^\([0-9]*\)\+\([0-9+*]*\*[0-9]*\)$/{ + s//\2+\1/ + b loop +} +/^[0-9]*\+/{ + s/$/=/ + b add +} +x +/^done$/{ + x + p + d +} +/^()/{ + s/// + x + G + s/\(.*\)\n\([^@]*\)@\([^@]*\)@\(.*\)/\2\1\3/ + x + s/[^@]*@[^@]*@\(.*\)/\1/ + x + b loop +} +i\ +help, stack problem +p +x +p +q + +# turn mul into add until 1*x -> x +: mul +/^0*1\*/{ + s/// + b loop +} +/^\([0-9]*\)0\*/{ + s/^\([0-9]*\)0\*\([0-9]*\)/\1*\20/ + b mul +} +s/^\([0-9]*\)1\*/\10*/ +s/^\([0-9]*\)2\*/\11*/ +s/^\([0-9]*\)3\*/\12*/ +s/^\([0-9]*\)4\*/\13*/ +s/^\([0-9]*\)5\*/\14*/ +s/^\([0-9]*\)6\*/\15*/ +s/^\([0-9]*\)7\*/\16*/ +s/^\([0-9]*\)8\*/\17*/ +s/^\([0-9]*\)9\*/\18*/ +s/\*\([0-9*]*\)/*\1+\1/ +b mul + +# get rid of a plus term until 0+x -> x +: add +/^\+\([0-9+*]*\)=/{ + s//\1/ + b loop +} +/^\([0-9*]*\)\+=/{ + s//\1/ + b loop +} +/^\([0-9]*\)\+\([0-9*+]*\)\+=/{ + s//\2+\1/ + b loop +} +/^\([0-9]*\)0\+\([0-9]*\)\([0-9]\)=/{ + s//\1+\2=\3/ + b add +} +/^\([0-9]*\)\([0-9]\)\+\([0-9]*\)0=/{ + s//\1+\3=\2/ + b add +} +/^\([0-9]*\)0\+\([0-9*+]*\)\+\([0-9]*\)\([0-9]\)=/{ + s//\1+\2+\3=\4/ + b add +} +/^\([0-9]*\)\([0-9]\)\+\([0-9*+]*\)\+\([0-9]*\)0=/{ + s//\1+\3+\4=\2/ + b add +} +s/^\([0-9]*\)1\+/\10+/ +s/^\([0-9]*\)2\+/\11+/ +s/^\([0-9]*\)3\+/\12+/ +s/^\([0-9]*\)4\+/\13+/ +s/^\([0-9]*\)5\+/\14+/ +s/^\([0-9]*\)6\+/\15+/ +s/^\([0-9]*\)7\+/\16+/ +s/^\([0-9]*\)8\+/\17+/ +s/^\([0-9]*\)9\+/\18+/ + +s/9=\([0-9]*\)$/_=\1/ +s/8=\([0-9]*\)$/9=\1/ +s/7=\([0-9]*\)$/8=\1/ +s/6=\([0-9]*\)$/7=\1/ +s/5=\([0-9]*\)$/6=\1/ +s/4=\([0-9]*\)$/5=\1/ +s/3=\([0-9]*\)$/4=\1/ +s/2=\([0-9]*\)$/3=\1/ +s/1=\([0-9]*\)$/2=\1/ +/_/{ + s//_0/ + : inc + s/9_/_0/ + s/8_/9/ + s/7_/8/ + s/6_/7/ + s/5_/6/ + s/4_/5/ + s/3_/4/ + s/2_/3/ + s/1_/2/ + s/0_/1/ + s/\+_/+1/ + /_/b inc +} +b add diff --git a/buildrump.sh/src/usr.bin/sed/TEST/sed.test b/buildrump.sh/src/usr.bin/sed/TEST/sed.test new file mode 100644 index 00000000..ff4093ac --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/TEST/sed.test @@ -0,0 +1,554 @@ +#!/bin/sh - +# $NetBSD: sed.test,v 1.5 2011/11/12 03:15:05 christos Exp $ +# +# Copyright (c) 1992 Diomidis Spinellis. +# Copyright (c) 1992, 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. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. 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. +# +# from: @(#)sed.test 8.1 (Berkeley) 6/6/93 +# $NetBSD: sed.test,v 1.5 2011/11/12 03:15:05 christos Exp $ +# + +# sed Regression Tests +# +# The following files are created: +# lines[1-4], script1, script2 +# Two directories *.out contain the test results + +main() +{ + BASE=/usr/bin/sed + BASELOG=sed.out + TEST=$(cd $(dirname $0)/.. && make -V .OBJDIR)/sed + TESTLOG=nsed.out + DICT=/usr/share/dict/words + + test_error | more + + awk 'END { for (i = 1; i < 15; i++) print "l1_" i}' </dev/null >lines1 + awk 'END { for (i = 1; i < 10; i++) print "l2_" i}' </dev/null >lines2 + + exec 4>&1 5>&2 + + # Set these flags to get messages about known problems + BSD=1 + GNU=0 + SUN=0 + tests $BASE $BASELOG + + BSD=0 + GNU=0 + SUN=0 + tests $TEST $TESTLOG + exec 1>&4 2>&5 + diff -c $BASELOG $TESTLOG | more +} + +tests() +{ + SED=$1 + DIR=$2 + rm -rf $DIR + mkdir $DIR + MARK=100 + + test_args + test_addr + echo Testing commands + test_group + test_acid + test_branch + test_pattern + test_print + test_subst +} + +mark() +{ + MARK=$(expr $MARK + 1) + exec 1>&4 2>&5 + exec >"$DIR/${MARK}_$1" + echo "Test $1:$MARK" + # Uncomment this line to match tests with sed error messages + echo "Test $1:$MARK" >&5 +} + +test_args() +{ + mark '1.1' + echo Testing argument parsing + echo First type + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED 's/^/e1_/p' lines1 + fi + mark '1.2' ; $SED -n 's/^/e1_/p' lines1 + mark '1.3' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED 's/^/e1_/p' <lines1 + fi + mark '1.4' ; $SED -n 's/^/e1_/p' <lines1 + echo Second type + mark '1.4.1' + if [ $SUN -eq 1 ] ; then + echo SunOS sed fails this + fi + $SED -e '' <lines1 + echo 's/^/s1_/p' >script1 + echo 's/^/s2_/p' >script2 + mark '1.5' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 lines1 + fi + mark '1.6' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 <lines1 + fi + mark '1.7' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' lines1 + fi + mark '1.8' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' <lines1 + fi + mark '1.9' ; $SED -n -f script1 lines1 + mark '1.10' ; $SED -n -f script1 <lines1 + mark '1.11' ; $SED -n -e 's/^/e1_/p' lines1 + mark '1.12' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -n -e 's/^/e1_/p' <lines1 + fi + mark '1.13' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' -e 's/^/e2_/p' lines1 + fi + mark '1.14' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 -f script2 lines1 + fi + mark '1.15' + if [ $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo GNU and SunOS sed fail this following older POSIX draft + else + $SED -e 's/^/e1_/p' -f script1 lines1 + fi + mark '1.16' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -e 's/^/e1_/p' lines1 lines1 + fi + # POSIX D11.2:11251 + mark '1.17' ; $SED p <lines1 lines1 +cat >script1 <<EOF +#n +# A comment + +p +EOF + mark '1.18' ; $SED -f script1 <lines1 lines1 +} + +test_addr() +{ + echo Testing address ranges + mark '2.1' ; $SED -n -e '4p' lines1 + mark '2.2' ; $SED -n -e '20p' lines1 lines2 + mark '2.3' ; $SED -n -e '$p' lines1 + mark '2.4' ; $SED -n -e '$p' lines1 lines2 + mark '2.5' ; $SED -n -e '$a\ +hello' /dev/null + mark '2.6' ; $SED -n -e '$p' lines1 /dev/null lines2 + # Should not print anything + mark '2.7' ; $SED -n -e '20p' lines1 + mark '2.8' ; $SED -n -e '0p' lines1 + mark '2.9' ; $SED -n '/l1_7/p' lines1 + mark '2.10' ; $SED -n ' /l1_7/ p' lines1 + mark '2.11' + if [ $BSD -eq 1 ] ; then + echo BSD sed fails this test + fi + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n '\_l1\_7_p' lines1 + mark '2.12' ; $SED -n '1,4p' lines1 + mark '2.13' ; $SED -n '1,$p' lines1 lines2 + mark '2.14' ; $SED -n '1,/l2_9/p' lines1 lines2 + mark '2.15' ; $SED -n '/4/,$p' lines1 lines2 + mark '2.16' ; $SED -n '/4/,20p' lines1 lines2 + mark '2.17' ; $SED -n '/4/,/10/p' lines1 lines2 + mark '2.18' ; $SED -n '/l2_3/,/l1_8/p' lines1 lines2 + mark '2.19' + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n '12,3p' lines1 lines2 + mark '2.20' + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n '/l1_7/,3p' lines1 lines2 +} + +test_group() +{ + echo Brace and other grouping + mark '3.1' ; $SED -e ' +4,12 { + s/^/^/ + s/$/$/ + s/_/T/ +}' lines1 + mark '3.2' ; $SED -e ' +4,12 { + s/^/^/ + /6/,/10/ { + s/$/$/ + /8/ s/_/T/ + } +}' lines1 + mark '3.3' ; $SED -e ' +4,12 !{ + s/^/^/ + /6/,/10/ !{ + s/$/$/ + /8/ !s/_/T/ + } +}' lines1 + mark '3.4' ; $SED -e '4,12!s/^/^/' lines1 +} + +test_acid() +{ + echo Testing a c d and i commands + mark '4.1' ; $SED -n -e ' +s/^/before_i/p +20i\ +inserted +s/^/after_i/p +' lines1 lines2 + mark '4.2' ; $SED -n -e ' +5,12s/^/5-12/ +s/^/before_a/p +/5-12/a\ +appended +s/^/after_a/p +' lines1 lines2 + mark '4.3' + if [ $GNU -eq 1 ] ; then + echo GNU sed fails this + fi + $SED -n -e ' +s/^/^/p +/l1_/a\ +appended +8,10N +s/$/$/p +' lines1 lines2 + mark '4.4' ; $SED -n -e ' +c\ +hello +' lines1 + mark '4.5' ; $SED -n -e ' +8c\ +hello +' lines1 + mark '4.6' ; $SED -n -e ' +3,14c\ +hello +' lines1 +# SunOS and GNU sed behave differently. We follow POSIX +# mark '4.7' ; $SED -n -e ' +#8,3c\ +#hello +#' lines1 + mark '4.8' ; $SED d <lines1 +} + +test_branch() +{ + echo Testing labels and branching + mark '5.1' ; $SED -n -e ' +b label4 +:label3 +s/^/label3_/p +b end +:label4 +2,12b label1 +b label2 +:label1 +s/^/label1_/p +b +:label2 +s/^/label2_/p +b label3 +:end +' lines1 + mark '5.2' + if [ $BSD -eq 1 ] ; then + echo BSD sed fails this test + fi + $SED -n -e ' +s/l1_/l2_/ +t ok +b +:ok +s/^/tested /p +' lines1 lines2 +# SunOS sed behaves differently here. Clarification needed. +# mark '5.3' ; $SED -n -e ' +#5,8b inside +#1,5 { +# s/^/^/p +# :inside +# s/$/$/p +#} +#' lines1 +# Check that t clears the substitution done flag + mark '5.4' ; $SED -n -e ' +1,8s/^/^/ +t l1 +:l1 +t l2 +s/$/$/p +b +:l2 +s/^/ERROR/ +' lines1 +# Check that reading a line clears the substitution done flag + mark '5.5' + if [ $BSD -eq 1 ] ; then + echo BSD sed fails this test + fi + $SED -n -e ' +t l2 +1,8s/^/^/p +2,7N +b +:l2 +s/^/ERROR/p +' lines1 + mark '5.6' ; $SED 5q lines1 + mark '5.7' ; $SED -e ' +5i\ +hello +5q' lines1 +# Branch across block boundary + mark '5.8' ; $SED -e ' +{ +:b +} +s/l/m/ +tb' lines1 +} + +test_pattern() +{ +echo Pattern space commands +# Check that the pattern space is deleted + mark '6.1' ; $SED -n -e ' +c\ +changed +p +' lines1 + mark '6.2' ; $SED -n -e ' +4d +p +' lines1 +# SunOS sed refused to print here +# mark '6.3' ; $SED -e ' +#N +#N +#N +#D +#P +#4p +#' lines1 + mark '6.4' ; $SED -e ' +2h +3H +4g +5G +6x +6p +6x +6p +' lines1 + mark '6.5' ; $SED -e '4n' lines1 + mark '6.6' ; $SED -n -e '4n' lines1 +} + +test_print() +{ + echo Testing print and file routines + awk 'END {for (i = 1; i < 256; i++) printf("%c", i);print "\n"}' \ + </dev/null >lines3 + # GNU and SunOS sed behave differently here + mark '7.1' + if [ $BSD -eq 1 ] ; then + echo 'BSD sed drops core on this one; TEST SKIPPED' + else + $SED -n l lines3 + fi + mark '7.2' ; $SED -e '/l2_/=' lines1 lines2 + rm -f lines4 + mark '7.3' ; $SED -e '3,12w lines4' lines1 + echo w results + cat lines4 + mark '7.4' ; $SED -e '4r lines2' lines1 + mark '7.5' ; $SED -e '5r /dev/dds' lines1 + mark '7.6' ; $SED -e '6r /dev/null' lines1 + mark '7.7' + if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo BSD, GNU and SunOS cannot pass this one + else + sed '200q' $DICT | sed 's$.*$s/^/&/w tmpdir/&$' >script1 + rm -rf tmpdir + mkdir tmpdir + $SED -f script1 lines1 + cat tmpdir/* + rm -rf tmpdir + fi + mark '7.8' + if [ $BSD -eq 1 ] ; then + echo BSD sed cannot pass 7.7 + else + echo line1 > lines3 + echo "" >> lines3 + $SED -n -e '$p' lines3 /dev/null + fi + +} + +test_subst() +{ + echo Testing substitution commands + mark '8.1' ; $SED -e 's/./X/g' lines1 + mark '8.2' ; $SED -e 's,.,X,g' lines1 +# GNU and SunOS sed thinks we are escaping . as wildcard, not as separator +# mark '8.3' ; $SED -e 's.\..X.g' lines1 +# POSIX does not say that this should work +# mark '8.4' ; $SED -e 's/[/]/Q/' lines1 + mark '8.4' ; $SED -e 's/[\/]/Q/' lines1 + mark '8.5' ; $SED -e 's_\__X_' lines1 + mark '8.6' ; $SED -e 's/./(&)/g' lines1 + mark '8.7' ; $SED -e 's/./(\&)/g' lines1 + mark '8.8' ; $SED -e 's/\(.\)\(.\)\(.\)/x\3x\2x\1/g' lines1 + mark '8.9' ; $SED -e 's/_/u0\ +u1\ +u2/g' lines1 + mark '8.10' + if [ $BSD -eq 1 -o $GNU -eq 1 ] ; then + echo 'BSD/GNU sed do not understand digit flags on s commands' + fi + $SED -e 's/./X/4' lines1 + rm -f lines4 + mark '8.11' ; $SED -e 's/1/X/w lines4' lines1 + echo s wfile results + cat lines4 + mark '8.12' ; $SED -e 's/[123]/X/g' lines1 + mark '8.13' ; $SED -e 'y/0123456789/9876543210/' lines1 + mark '8.14' ; + if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo BSD/GNU/SUN sed fail this test + else + $SED -e 'y10\123456789198765432\101' lines1 + fi + mark '8.15' ; $SED -e '1N;2y/\n/X/' lines1 + mark '8.16' + if [ $BSD -eq 1 ] ; then + echo 'BSD sed does not handle branch defined REs' + else + echo 'eeefff' | $SED -e 'p' -e 's/e/X/p' -e ':x' \ + -e 's//Y/p' -e '/f/bx' + fi +} + +test_error() +{ + exec 3<&0 4>&1 5>&2 + exec 0</dev/null + exec 2>&1 + set -x + $TEST -x && exit 1 + $TEST -f && exit 1 + $TEST -e && exit 1 + $TEST -f /dev/dds && exit 1 + $TEST p /dev/dds && exit 1 + $TEST -f /bin/sh && exit 1 + $TEST '{' && exit 1 + $TEST '{' && exit 1 + $TEST '/hello/' && exit 1 + $TEST '1,/hello/' && exit 1 + $TEST -e '-5p' && exit 1 + $TEST '/jj' && exit 1 + $TEST 'a hello' && exit 1 + $TEST 'a \ hello' && exit 1 + $TEST 'b foo' && exit 1 + $TEST 'd hello' && exit 1 + $TEST 's/aa' && exit 1 + $TEST 's/aa/' && exit 1 + $TEST 's/a/b' && exit 1 + $TEST 's/a/b/c/d' && exit 1 + $TEST 's/a/b/ 1 2' && exit 1 + $TEST 's/a/b/ 1 g' && exit 1 + $TEST 's/a/b/w' && exit 1 + $TEST 'y/aa' && exit 1 + $TEST 'y/aa/b/' && exit 1 + $TEST 'y/aa/' && exit 1 + $TEST 'y/a/b' && exit 1 + $TEST 'y/a/b/c/d' && exit 1 + $TEST '!' && exit 1 + $TEST supercalifrangolisticexprialidociussupercalifrangolisticexcius + set +x + exec 0<&3 1>&4 2>&5 +} + +main diff --git a/buildrump.sh/src/usr.bin/sed/compile.c b/buildrump.sh/src/usr.bin/sed/compile.c new file mode 100644 index 00000000..758295de --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/compile.c @@ -0,0 +1,947 @@ +/* $NetBSD: compile.c,v 1.46 2015/03/12 12:40:41 christos Exp $ */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: compile.c,v 1.46 2015/03/12 12:40:41 christos Exp $"); +#ifdef __FBSDID +__FBSDID("$FreeBSD: head/usr.bin/sed/compile.c 259132 2013-12-09 18:57:20Z eadler $"); +#endif + +#if 0 +static const char sccsid[] = "@(#)compile.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +#include "defs.h" +#include "extern.h" + +#define LHSZ 128 +#define LHMASK (LHSZ - 1) +static struct labhash { + struct labhash *lh_next; + u_int lh_hash; + struct s_command *lh_cmd; + int lh_ref; +} *labels[LHSZ]; + +static char *compile_addr(char *, struct s_addr *); +static char *compile_ccl(char **, char *); +static char *compile_delimited(char *, char *, int); +static char *compile_flags(char *, struct s_subst *); +static regex_t *compile_re(char *, int); +static char *compile_subst(char *, struct s_subst *); +static char *compile_text(void); +static char *compile_tr(char *, struct s_tr **); +static struct s_command + **compile_stream(struct s_command **); +static char *duptoeol(char *, const char *); +static void enterlabel(struct s_command *); +static struct s_command + *findlabel(char *); +static void fixuplabel(struct s_command *, struct s_command *); +static void uselabel(void); + +/* + * Command specification. This is used to drive the command parser. + */ +struct s_format { + char code; /* Command code */ + int naddr; /* Number of address args */ + enum e_args args; /* Argument type */ +}; + +static struct s_format cmd_fmts[] = { + {'{', 2, GROUP}, + {'}', 0, ENDGROUP}, + {'a', 1, TEXT}, + {'b', 2, BRANCH}, + {'c', 2, TEXT}, + {'d', 2, EMPTY}, + {'D', 2, EMPTY}, + {'g', 2, EMPTY}, + {'G', 2, EMPTY}, + {'h', 2, EMPTY}, + {'H', 2, EMPTY}, + {'i', 1, TEXT}, + {'l', 2, EMPTY}, + {'n', 2, EMPTY}, + {'N', 2, EMPTY}, + {'p', 2, EMPTY}, + {'P', 2, EMPTY}, + {'q', 1, EMPTY}, + {'r', 1, RFILE}, + {'s', 2, SUBST}, + {'t', 2, BRANCH}, + {'w', 2, WFILE}, + {'x', 2, EMPTY}, + {'y', 2, TR}, + {'!', 2, NONSEL}, + {':', 0, LABEL}, + {'#', 0, COMMENT}, + {'=', 1, EMPTY}, + {'\0', 0, COMMENT}, +}; + +/* The compiled program. */ +struct s_command *prog; + +/* + * Compile the program into prog. + * Initialise appends. + */ +void +compile(void) +{ + *compile_stream(&prog) = NULL; + fixuplabel(prog, NULL); + uselabel(); + if (appendnum > 0) + appends = xmalloc(sizeof(struct s_appends) * appendnum); + match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); +} + +#define EATSPACE() do { \ + if (p) \ + while (*p && isspace((unsigned char)*p)) \ + p++; \ + } while (0) + +static struct s_command ** +compile_stream(struct s_command **link) +{ + char *p; + static char lbuf[_POSIX2_LINE_MAX + 1]; /* To save stack */ + struct s_command *cmd, *cmd2, *stack; + struct s_format *fp; + char re[_POSIX2_LINE_MAX + 1]; + int naddr; /* Number of addresses */ + + stack = 0; + for (;;) { + if ((p = cu_fgets(lbuf, sizeof(lbuf), NULL)) == NULL) { + if (stack != 0) + errx(1, "%lu: %s: unexpected EOF (pending }'s)", + linenum, fname); + return (link); + } + +semicolon: EATSPACE(); + if (p) { + if (*p == '#' || *p == '\0') + continue; + else if (*p == ';') { + p++; + goto semicolon; + } + } + *link = cmd = xmalloc(sizeof(struct s_command)); + link = &cmd->next; + cmd->startline = cmd->nonsel = 0; + /* First parse the addresses */ + naddr = 0; + +/* Valid characters to start an address */ +#define addrchar(c) (strchr("0123456789/\\$", (c))) + if (addrchar(*p)) { + naddr++; + cmd->a1 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a1); + EATSPACE(); /* EXTENSION */ + if (*p == ',') { + p++; + EATSPACE(); /* EXTENSION */ + naddr++; + cmd->a2 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a2); + EATSPACE(); + } else + cmd->a2 = 0; + } else + cmd->a1 = cmd->a2 = 0; + +nonsel: /* Now parse the command */ + if (!*p) + errx(1, "%lu: %s: command expected", linenum, fname); + cmd->code = *p; + for (fp = cmd_fmts; fp->code; fp++) + if (fp->code == *p) + break; + if (!fp->code) + errx(1, "%lu: %s: invalid command code %c", linenum, fname, *p); + if (naddr > fp->naddr) + errx(1, + "%lu: %s: command %c expects up to %d address(es), found %d", + linenum, fname, *p, fp->naddr, naddr); + switch (fp->args) { + case NONSEL: /* ! */ + p++; + EATSPACE(); + cmd->nonsel = ! cmd->nonsel; + goto nonsel; + case GROUP: /* { */ + p++; + EATSPACE(); + cmd->next = stack; + stack = cmd; + link = &cmd->u.c; + if (*p) + goto semicolon; + break; + case ENDGROUP: + /* + * Short-circuit command processing, since end of + * group is really just a noop. + */ + cmd->nonsel = 1; + if (stack == 0) + errx(1, "%lu: %s: unexpected }", linenum, fname); + cmd2 = stack; + stack = cmd2->next; + cmd2->next = cmd; + /*FALLTHROUGH*/ + case EMPTY: /* d D g G h H l n N p P q x = \0 */ + p++; + EATSPACE(); + switch (*p) { + case ';': + p++; + link = &cmd->next; + goto semicolon; + case '}': + goto semicolon; + case '\0': + break; + default: + errx(1, "%lu: %s: extra characters at the end of %c command", + linenum, fname, cmd->code); + } + break; + case TEXT: /* a c i */ + p++; + EATSPACE(); + if (*p != '\\') + errx(1, +"%lu: %s: command %c expects \\ followed by text", linenum, fname, cmd->code); + p++; + EATSPACE(); + if (*p) + errx(1, + "%lu: %s: extra characters after \\ at the end of %c command", + linenum, fname, cmd->code); + cmd->t = compile_text(); + break; + case COMMENT: /* \0 # */ + break; + case WFILE: /* w */ + p++; + EATSPACE(); + if (*p == '\0') + errx(1, "%lu: %s: filename expected", linenum, fname); + cmd->t = duptoeol(p, "w command"); + if (aflag) + cmd->u.fd = -1; + else if ((cmd->u.fd = open(p, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(1, "%s", p); + break; + case RFILE: /* r */ + p++; + EATSPACE(); + if (*p == '\0') + errx(1, "%lu: %s: filename expected", linenum, fname); + else + cmd->t = duptoeol(p, "read command"); + break; + case BRANCH: /* b t */ + p++; + EATSPACE(); + if (*p == '\0') + cmd->t = NULL; + else + cmd->t = duptoeol(p, "branch"); + break; + case LABEL: /* : */ + p++; + EATSPACE(); + cmd->t = duptoeol(p, "label"); + if (strlen(p) == 0) + errx(1, "%lu: %s: empty label", linenum, fname); + enterlabel(cmd); + break; + case SUBST: /* s */ + p++; + if (*p == '\0' || *p == '\\') + errx(1, +"%lu: %s: substitute pattern can not be delimited by newline or backslash", + linenum, fname); + cmd->u.s = xcalloc(1, sizeof(struct s_subst)); + p = compile_delimited(p, re, 0); + if (p == NULL) + errx(1, + "%lu: %s: unterminated substitute pattern", linenum, fname); + + /* Compile RE with no case sensitivity temporarily */ + if (*re == '\0') + cmd->u.s->re = NULL; + else + cmd->u.s->re = compile_re(re, 0); + --p; + p = compile_subst(p, cmd->u.s); + p = compile_flags(p, cmd->u.s); + + /* Recompile RE with case sensitivity from "I" flag if any */ + if (*re == '\0') + cmd->u.s->re = NULL; + else + cmd->u.s->re = compile_re(re, cmd->u.s->icase); + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + break; + case TR: /* y */ + p++; + p = compile_tr(p, &cmd->u.y); + EATSPACE(); + switch (*p) { + case ';': + p++; + link = &cmd->next; + goto semicolon; + case '}': + goto semicolon; + case '\0': + break; + default: + errx(1, +"%lu: %s: extra text at the end of a transform command", linenum, fname); + } + if (*p) + break; + } + } +} + +/* + * Get a delimited string. P points to the delimeter of the string; d points + * to a buffer area. Newline and delimiter escapes are processed; other + * escapes are ignored. + * + * Returns a pointer to the first character after the final delimiter or NULL + * in the case of a non-terminated string. The character array d is filled + * with the processed string. + */ +static char * +compile_delimited(char *p, char *d, int is_tr) +{ + char c; + + c = *p++; + if (c == '\0') + return (NULL); + else if (c == '\\') + errx(1, "%lu: %s: \\ can not be used as a string delimiter", + linenum, fname); + else if (c == '\n') + errx(1, "%lu: %s: newline can not be used as a string delimiter", + linenum, fname); + while (*p) { + if (*p == '[' && *p != c) { + if ((d = compile_ccl(&p, d)) == NULL) + errx(1, "%lu: %s: unbalanced brackets ([])", linenum, fname); + continue; + } else if (*p == '\\' && p[1] == '[') { + *d++ = *p++; + } else if (*p == '\\' && p[1] == c) + p++; + else if (*p == '\\' && p[1] == 'n') { + *d++ = '\n'; + p += 2; + continue; + } else if (*p == '\\' && p[1] == '\\') { + if (is_tr) + p++; + else + *d++ = *p++; + } else if (*p == c) { + *d = '\0'; + return (p + 1); + } + *d++ = *p++; + } + return (NULL); +} + + +/* compile_ccl: expand a POSIX character class */ +static char * +compile_ccl(char **sp, char *t) +{ + int c, d; + char *s = *sp; + + *t++ = *s++; + if (*s == '^') + *t++ = *s++; + if (*s == ']') + *t++ = *s++; + for (; *s && (*t = *s) != ']'; s++, t++) + if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { + *++t = *++s, t++, s++; + for (c = *s; (*t = *s) != ']' || c != d; s++, t++) + if ((c = *s) == '\0') + return NULL; + } + return (*s == ']') ? *sp = ++s, ++t : NULL; +} + +/* + * Compiles the regular expression in RE and returns a pointer to the compiled + * regular expression. + * Cflags are passed to regcomp. + */ +static regex_t * +compile_re(char *re, int case_insensitive) +{ + regex_t *rep; + int eval, flags; + + + flags = rflags; + if (case_insensitive) + flags |= REG_ICASE; + rep = xmalloc(sizeof(regex_t)); + if ((eval = regcomp(rep, re, flags)) != 0) + errx(1, "%lu: %s: RE error: %s", + linenum, fname, strregerror(eval, rep)); + if (maxnsub < rep->re_nsub) + maxnsub = rep->re_nsub; + return (rep); +} + +/* + * Compile the substitution string of a regular expression and set res to + * point to a saved copy of it. Nsub is the number of parenthesized regular + * expressions. + */ +static char * +compile_subst(char *p, struct s_subst *s) +{ + static char lbuf[_POSIX2_LINE_MAX + 1]; + size_t asize, size; + u_char ref; + char c, *text, *op, *sp; + int more = 1, sawesc = 0; + + c = *p++; /* Terminator character */ + if (c == '\0') + return (NULL); + + s->maxbref = 0; + s->linenum = linenum; + asize = 2 * _POSIX2_LINE_MAX + 1; + text = xmalloc(asize); + size = 0; + do { + op = sp = text + size; + for (; *p; p++) { + if (*p == '\\' || sawesc) { + /* + * If this is a continuation from the last + * buffer, we won't have a character to + * skip over. + */ + if (sawesc) + sawesc = 0; + else + p++; + + if (*p == '\0') { + /* + * This escaped character is continued + * in the next part of the line. Note + * this fact, then cause the loop to + * exit w/ normal EOL case and reenter + * above with the new buffer. + */ + sawesc = 1; + p--; + continue; + } else if (strchr("123456789", *p) != NULL) { + *sp++ = '\\'; + ref = (u_char)(*p - '0'); + if (s->re != NULL && + ref > s->re->re_nsub) + errx(1, "%lu: %s: \\%c not defined in the RE", + linenum, fname, *p); + if (s->maxbref < ref) + s->maxbref = ref; + } else if (*p == '&' || *p == '\\') + *sp++ = '\\'; + } else if (*p == c) { + if (*++p == '\0' && more) { + if (cu_fgets(lbuf, sizeof(lbuf), &more)) + p = lbuf; + } + *sp++ = '\0'; + size += (size_t)(sp - op); + s->new = xrealloc(text, size); + return (p); + } else if (*p == '\n') { + errx(1, +"%lu: %s: unescaped newline inside substitute pattern", linenum, fname); + /* NOTREACHED */ + } + *sp++ = *p; + } + size += (size_t)(sp - op); + if (asize - size < _POSIX2_LINE_MAX + 1) { + asize *= 2; + text = xrealloc(text, asize); + } + } while (cu_fgets(p = lbuf, sizeof(lbuf), &more)); + errx(1, "%lu: %s: unterminated substitute in regular expression", + linenum, fname); + /* NOTREACHED */ +} + +/* + * Compile the flags of the s command + */ +static char * +compile_flags(char *p, struct s_subst *s) +{ + int gn; /* True if we have seen g or n */ + unsigned long nval; + char wfile[_POSIX2_LINE_MAX + 1], *q; + + s->n = 1; /* Default */ + s->p = 0; + s->wfile = NULL; + s->wfd = -1; + s->icase = 0; + for (gn = 0;;) { + EATSPACE(); /* EXTENSION */ + switch (*p) { + case 'g': + if (gn) + errx(1, +"%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); + gn = 1; + s->n = 0; + break; + case '\0': + case '\n': + case ';': + return (p); + case 'p': + s->p = 1; + break; + case 'i': + case 'I': + s->icase = 1; + break; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (gn) + errx(1, +"%lu: %s: more than one number or 'g' in substitute flags", linenum, fname); + gn = 1; + errno = 0; + nval = strtoul(p, &p, 10); + if (errno == ERANGE || nval > INT_MAX) + errx(1, +"%lu: %s: overflow in the 'N' substitute flag", linenum, fname); + s->n = (int)nval; + p--; + break; + case 'w': + p++; +#ifdef HISTORIC_PRACTICE + if (*p != ' ') { + warnx("%lu: %s: space missing before w wfile", linenum, fname); + return (p); + } +#endif + EATSPACE(); + q = wfile; + while (*p) { + if (*p == '\n') + break; + *q++ = *p++; + } + *q = '\0'; + if (q == wfile) + errx(1, "%lu: %s: no wfile specified", linenum, fname); + s->wfile = strdup(wfile); + if (!aflag && (s->wfd = open(wfile, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(1, "%s", wfile); + return (p); + default: + errx(1, "%lu: %s: bad flag in substitute command: '%c'", + linenum, fname, *p); + break; + } + p++; + } +} + +/* + * Compile a translation set of strings into a lookup table. + */ +static char * +compile_tr(char *p, struct s_tr **py) +{ + struct s_tr *y; + size_t i; + const char *op, *np; + char old[_POSIX2_LINE_MAX + 1]; + char new[_POSIX2_LINE_MAX + 1]; + size_t oclen, oldlen, nclen, newlen; + mbstate_t mbs1, mbs2; + + *py = y = xmalloc(sizeof(*y)); + y->multis = NULL; + y->nmultis = 0; + + if (*p == '\0' || *p == '\\') + errx(1, + "%lu: %s: transform pattern can not be delimited by newline or backslash", + linenum, fname); + p = compile_delimited(p, old, 1); + if (p == NULL) + errx(1, "%lu: %s: unterminated transform source string", + linenum, fname); + p = compile_delimited(p - 1, new, 1); + if (p == NULL) + errx(1, "%lu: %s: unterminated transform target string", + linenum, fname); + EATSPACE(); + op = old; + oldlen = mbsrtowcs(NULL, &op, 0, NULL); + if (oldlen == (size_t)-1) + err(1, NULL); + np = new; + newlen = mbsrtowcs(NULL, &np, 0, NULL); + if (newlen == (size_t)-1) + err(1, NULL); + if (newlen != oldlen) + errx(1, "%lu: %s: transform strings are not the same length", + linenum, fname); + if (MB_CUR_MAX == 1) { + /* + * The single-byte encoding case is easy: generate a + * lookup table. + */ + for (i = 0; i <= UCHAR_MAX; i++) + y->bytetab[i] = (u_char)i; + for (; *op; op++, np++) + y->bytetab[(u_char)*op] = (u_char)*np; + } else { + /* + * Multi-byte encoding case: generate a lookup table as + * above, but only for single-byte characters. The first + * bytes of multi-byte characters have their lookup table + * entries set to 0, which causes do_tr() to search through + * an auxiliary vector of multi-byte mappings. + */ + memset(&mbs1, 0, sizeof(mbs1)); + memset(&mbs2, 0, sizeof(mbs2)); + for (i = 0; i <= UCHAR_MAX; i++) + y->bytetab[i] = (u_char)((btowc((int)i) != WEOF) ? i : 0); + while (*op != '\0') { + oclen = mbrlen(op, MB_LEN_MAX, &mbs1); + if (oclen == (size_t)-1 || oclen == (size_t)-2) + errc(1, EILSEQ, NULL); + nclen = mbrlen(np, MB_LEN_MAX, &mbs2); + if (nclen == (size_t)-1 || nclen == (size_t)-2) + errc(1, EILSEQ, NULL); + if (oclen == 1 && nclen == 1) + y->bytetab[(u_char)*op] = (u_char)*np; + else { + y->bytetab[(u_char)*op] = 0; + y->multis = xrealloc(y->multis, + (y->nmultis + 1) * sizeof(*y->multis)); + i = y->nmultis++; + y->multis[i].fromlen = oclen; + memcpy(y->multis[i].from, op, oclen); + y->multis[i].tolen = nclen; + memcpy(y->multis[i].to, np, nclen); + } + op += oclen; + np += nclen; + } + } + return (p); +} + +/* + * Compile the text following an a or i command. + */ +static char * +compile_text(void) +{ + size_t asize, size; + int esc_nl; + char *text, *p, *op, *s; + char lbuf[_POSIX2_LINE_MAX + 1]; + + asize = 2 * _POSIX2_LINE_MAX + 1; + text = xmalloc(asize); + size = 0; + while (cu_fgets(lbuf, sizeof(lbuf), NULL)) { + op = s = text + size; + p = lbuf; + EATSPACE(); + for (esc_nl = 0; *p != '\0'; p++) { + if (*p == '\\' && p[1] != '\0' && *++p == '\n') + esc_nl = 1; + *s++ = *p; + } + size += (size_t)(s - op); + if (!esc_nl) { + *s = '\0'; + break; + } + if (asize - size < _POSIX2_LINE_MAX + 1) { + asize *= 2; + text = xrealloc(text, asize); + } + } + text[size] = '\0'; + p = xrealloc(text, size + 1); + return (p); +} + +/* + * Get an address and return a pointer to the first character after + * it. Fill the structure pointed to according to the address. + */ +static char * +compile_addr(char *p, struct s_addr *a) +{ + char *end, re[_POSIX2_LINE_MAX + 1]; + int icase; + + icase = 0; + + a->type = 0; + switch (*p) { + case '\\': /* Context address */ + ++p; + /* FALLTHROUGH */ + case '/': /* Context address */ + p = compile_delimited(p, re, 0); + if (p == NULL) + errx(1, "%lu: %s: unterminated regular expression", linenum, fname); + /* Check for case insensitive regexp flag */ + if (*p == 'I') { + icase = 1; + p++; + } + if (*re == '\0') + a->u.r = NULL; + else + a->u.r = compile_re(re, icase); + a->type = AT_RE; + return (p); + + case '$': /* Last line */ + a->type = AT_LAST; + return (p + 1); + + case '+': /* Relative line number */ + a->type = AT_RELLINE; + p++; + /* FALLTHROUGH */ + /* Line number */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (a->type == 0) + a->type = AT_LINE; + a->u.l = strtoul(p, &end, 10); + return (end); + default: + errx(1, "%lu: %s: expected context address", linenum, fname); + return (NULL); + } +} + +/* + * duptoeol -- + * Return a copy of all the characters up to \n or \0. + */ +static char * +duptoeol(char *s, const char *ctype) +{ + size_t len; + int ws; + char *p, *start; + + ws = 0; + for (start = s; *s != '\0' && *s != '\n'; ++s) + ws = isspace((unsigned char)*s); + *s = '\0'; + if (ws) + warnx("%lu: %s: whitespace after %s", linenum, fname, ctype); + len = (size_t)(s - start + 1); + p = xmalloc(len); + return (memmove(p, start, len)); +} + +/* + * Convert goto label names to addresses, and count a and r commands, in + * the given subset of the script. Free the memory used by labels in b + * and t commands (but not by :). + * + * TODO: Remove } nodes + */ +static void +fixuplabel(struct s_command *cp, struct s_command *end) +{ + + for (; cp != end; cp = cp->next) + switch (cp->code) { + case 'a': + case 'r': + appendnum++; + break; + case 'b': + case 't': + /* Resolve branch target. */ + if (cp->t == NULL) { + cp->u.c = NULL; + break; + } + if ((cp->u.c = findlabel(cp->t)) == NULL) + errx(1, "%lu: %s: undefined label '%s'", linenum, fname, cp->t); + free(cp->t); + break; + case '{': + /* Do interior commands. */ + fixuplabel(cp->u.c, cp->next); + break; + } +} + +/* + * Associate the given command label for later lookup. + */ +static void +enterlabel(struct s_command *cp) +{ + struct labhash **lhp, *lh; + u_char *p; + u_int h, c; + + for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) + h = (h << 5) + h + c; + lhp = &labels[h & LHMASK]; + for (lh = *lhp; lh != NULL; lh = lh->lh_next) + if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) + errx(1, "%lu: %s: duplicate label '%s'", linenum, fname, cp->t); + lh = xmalloc(sizeof *lh); + lh->lh_next = *lhp; + lh->lh_hash = h; + lh->lh_cmd = cp; + lh->lh_ref = 0; + *lhp = lh; +} + +/* + * Find the label contained in the command l in the command linked + * list cp. L is excluded from the search. Return NULL if not found. + */ +static struct s_command * +findlabel(char *name) +{ + struct labhash *lh; + u_char *p; + u_int h, c; + + for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) + h = (h << 5) + h + c; + for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { + if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { + lh->lh_ref = 1; + return (lh->lh_cmd); + } + } + return (NULL); +} + +/* + * Warn about any unused labels. As a side effect, release the label hash + * table space. + */ +static void +uselabel(void) +{ + struct labhash *lh, *next; + int i; + + for (i = 0; i < LHSZ; i++) { + for (lh = labels[i]; lh != NULL; lh = next) { + next = lh->lh_next; + if (!lh->lh_ref) + warnx("%lu: %s: unused label '%s'", + linenum, fname, lh->lh_cmd->t); + free(lh); + } + } +} diff --git a/buildrump.sh/src/usr.bin/sed/defs.h b/buildrump.sh/src/usr.bin/sed/defs.h new file mode 100644 index 00000000..b5641154 --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/defs.h @@ -0,0 +1,150 @@ +/* $NetBSD: defs.h,v 1.12 2014/06/06 21:56:39 wiz Exp $ */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * 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. + * + * @(#)defs.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: head/usr.bin/sed/defs.h 192732 2009-05-25 06:45:33Z brian $ + */ + +/* + * Types of address specifications + */ +enum e_atype { + AT_RE = 1, /* Line that match RE */ + AT_LINE, /* Specific line */ + AT_RELLINE, /* Relative line */ + AT_LAST /* Last line */ +}; + +/* + * Format of an address + */ +struct s_addr { + enum e_atype type; /* Address type */ + union { + u_long l; /* Line number */ + regex_t *r; /* Regular expression */ + } u; +}; + +/* + * Substitution command + */ +struct s_subst { + int n; /* Occurrence to subst. */ + int p; /* True if p flag */ + int icase; /* True if I flag */ + char *wfile; /* NULL if no wfile */ + int wfd; /* Cached file descriptor */ + regex_t *re; /* Regular expression */ + unsigned int maxbref; /* Largest backreference. */ + u_long linenum; /* Line number. */ + char *new; /* Replacement text */ +}; + +/* + * Translate command. + */ +struct s_tr { + unsigned char bytetab[256]; + struct trmulti { + size_t fromlen; + char from[MB_LEN_MAX]; + size_t tolen; + char to[MB_LEN_MAX]; + } *multis; + size_t nmultis; +}; + +/* + * An internally compiled command. + * Initialy, label references are stored in t, on a second pass they + * are updated to pointers. + */ +struct s_command { + struct s_command *next; /* Pointer to next command */ + struct s_addr *a1, *a2; /* Start and end address */ + u_long startline; /* Start line number or zero */ + char *t; /* Text for : a c i r w */ + union { + struct s_command *c; /* Command(s) for b t { */ + struct s_subst *s; /* Substitute command */ + struct s_tr *y; /* Replace command array */ + int fd; /* File descriptor for w */ + } u; + char code; /* Command code */ + u_int nonsel:1; /* True if ! */ +}; + +/* + * Types of command arguments recognised by the parser + */ +enum e_args { + EMPTY, /* d D g G h H l n N p P q x = \0 */ + TEXT, /* a c i */ + NONSEL, /* ! */ + GROUP, /* { */ + ENDGROUP, /* } */ + COMMENT, /* # */ + BRANCH, /* b t */ + LABEL, /* : */ + RFILE, /* r */ + WFILE, /* w */ + SUBST, /* s */ + TR /* y */ +}; + +/* + * Structure containing things to append before a line is read + */ +struct s_appends { + enum {AP_STRING, AP_FILE} type; + char *s; + size_t len; +}; + +enum e_spflag { + APPEND, /* Append to the contents. */ + REPLACE /* Replace the contents. */ +}; + +/* + * Structure for a space (process, hold, otherwise). + */ +typedef struct { + char *space; /* Current space pointer. */ + size_t len; /* Current length. */ + int deleted; /* If deleted. */ + char *back; /* Backing memory. */ + size_t blen; /* Backing memory length. */ +} SPACE; diff --git a/buildrump.sh/src/usr.bin/sed/extern.h b/buildrump.sh/src/usr.bin/sed/extern.h new file mode 100644 index 00000000..0d28591f --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/extern.h @@ -0,0 +1,61 @@ +/* $NetBSD: extern.h,v 1.20 2015/03/12 12:40:41 christos Exp $ */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * 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. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: head/usr.bin/sed/extern.h 170608 2007-06-12 12:05:24Z yar $ + */ + +extern struct s_command *prog; +extern struct s_appends *appends; +extern regmatch_t *match; +extern size_t maxnsub; +extern u_long linenum; +extern size_t appendnum; +extern int aflag, eflag, nflag; +extern const char *fname, *outfname; +extern FILE *infile, *outfile; +extern int rflags; /* regex flags to use */ + +void cfclose(struct s_command *, struct s_command *); +void compile(void); +void cspace(SPACE *, const char *, size_t, enum e_spflag); +char *cu_fgets(char *, int, int *); +int mf_fgets(SPACE *, enum e_spflag); +int lastline(void); +void process(void); +void resetstate(void); +char *strregerror(int, regex_t *); +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +void *xcalloc(size_t, size_t); diff --git a/buildrump.sh/src/usr.bin/sed/main.c b/buildrump.sh/src/usr.bin/sed/main.c new file mode 100644 index 00000000..cfe9d357 --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/main.c @@ -0,0 +1,517 @@ +/* $NetBSD: main.c,v 1.34 2015/03/12 12:40:41 christos Exp $ */ + +/*- + * Copyright (c) 2013 Johann 'Myrkraverk' Oskarsson. + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: main.c,v 1.34 2015/03/12 12:40:41 christos Exp $"); +#ifdef __FBSDID +__FBSDID("$FreeBSD: head/usr.bin/sed/main.c 252231 2013-06-26 04:14:19Z pfg $"); +#endif + +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993\ + The Regents of the University of California. All rights reserved."); +#endif + +#if 0 +static const char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94"; +#endif + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <limits.h> +#include <locale.h> +#include <regex.h> +#include <stddef.h> +#define _WITH_GETLINE +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "defs.h" +#include "extern.h" + +/* + * Linked list of units (strings and files) to be compiled + */ +struct s_compunit { + struct s_compunit *next; + enum e_cut {CU_FILE, CU_STRING} type; + char *s; /* Pointer to string or fname */ +}; + +/* + * Linked list pointer to compilation units and pointer to current + * next pointer. + */ +static struct s_compunit *script, **cu_nextp = &script; + +/* + * Linked list of files to be processed + */ +struct s_flist { + char *fname; + struct s_flist *next; +}; + +/* + * Linked list pointer to files and pointer to current + * next pointer. + */ +static struct s_flist *files, **fl_nextp = &files; + +FILE *infile; /* Current input file */ +FILE *outfile; /* Current output file */ + +int aflag, eflag, nflag; +int rflags = 0; +static int rval; /* Exit status */ + +static int ispan; /* Whether inplace editing spans across files */ + +/* + * Current file and line number; line numbers restart across compilation + * units, but span across input files. The latter is optional if editing + * in place. + */ +const char *fname; /* File name. */ +const char *outfname; /* Output file name */ +static char oldfname[PATH_MAX]; /* Old file name (for in-place editing) */ +static char tmpfname[PATH_MAX]; /* Temporary file name (for in-place editing) */ +static const char *inplace; /* Inplace edit file extension. */ +u_long linenum; + +static void add_compunit(enum e_cut, char *); +static void add_file(char *); +static void usage(void) __dead; + +int +main(int argc, char *argv[]) +{ + int c, fflag; + char *temp_arg; + + setprogname(argv[0]); + (void) setlocale(LC_ALL, ""); + + fflag = 0; + inplace = NULL; + + while ((c = getopt(argc, argv, "EI::ae:f:i::lnru")) != -1) + switch (c) { + case 'r': /* Gnu sed compat */ + case 'E': + rflags = REG_EXTENDED; + break; + case 'I': + inplace = optarg ? optarg : __UNCONST(""); + ispan = 1; /* span across input files */ + break; + case 'a': + aflag = 1; + break; + case 'e': + eflag = 1; + temp_arg = xmalloc(strlen(optarg) + 2); + strcpy(temp_arg, optarg); + strcat(temp_arg, "\n"); + add_compunit(CU_STRING, temp_arg); + break; + case 'f': + fflag = 1; + add_compunit(CU_FILE, optarg); + break; + case 'i': + inplace = optarg ? optarg : __UNCONST(""); + ispan = 0; /* don't span across input files */ + break; + case 'l': +#ifdef _IOLBF + c = setvbuf(stdout, NULL, _IOLBF, 0); +#else + c = setlinebuf(stdout); +#endif + if (c) + warn("setting line buffered output failed"); + break; + case 'n': + nflag = 1; + break; + case 'u': +#ifdef _IONBF + c = setvbuf(stdout, NULL, _IONBF, 0); +#else + c = -1; + errno = EOPNOTSUPP; +#endif + if (c) + warn("setting unbuffered output failed"); + break; + default: + case '?': + usage(); + } + argc -= optind; + argv += optind; + + /* First usage case; script is the first arg */ + if (!eflag && !fflag && *argv) { + add_compunit(CU_STRING, *argv); + argv++; + } + + compile(); + + /* Continue with first and start second usage */ + if (*argv) + for (; *argv; argv++) + add_file(*argv); + else + add_file(NULL); + process(); + cfclose(prog, NULL); + if (fclose(stdout)) + err(1, "stdout"); + exit(rval); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "Usage: %s [-aElnru] command [file ...]\n" + "\t%s [-aElnru] [-e command] [-f command_file] [-I[extension]]\n" + "\t [-i[extension]] [file ...]\n", getprogname(), getprogname()); + exit(1); +} + +/* + * Like fgets, but go through the chain of compilation units chaining them + * together. Empty strings and files are ignored. + */ +char * +cu_fgets(char *buf, int n, int *more) +{ + static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; + static FILE *f; /* Current open file */ + static char *s; /* Current pointer inside string */ + static char string_ident[30]; + char *p; + +again: + switch (state) { + case ST_EOF: + if (script == NULL) { + if (more != NULL) + *more = 0; + return (NULL); + } + linenum = 0; + switch (script->type) { + case CU_FILE: + if ((f = fopen(script->s, "r")) == NULL) + err(1, "%s", script->s); + fname = script->s; + state = ST_FILE; + goto again; + case CU_STRING: + if (((size_t)snprintf(string_ident, + sizeof(string_ident), "\"%s\"", script->s)) >= + sizeof(string_ident) - 1) + (void)strcpy(string_ident + + sizeof(string_ident) - 6, " ...\""); + fname = string_ident; + s = script->s; + state = ST_STRING; + goto again; + } + case ST_FILE: + if ((p = fgets(buf, n, f)) != NULL) { + linenum++; + if (linenum == 1 && buf[0] == '#' && buf[1] == 'n') + nflag = 1; + if (more != NULL) + *more = !feof(f); + return (p); + } + script = script->next; + (void)fclose(f); + state = ST_EOF; + goto again; + case ST_STRING: + if (linenum == 0 && s[0] == '#' && s[1] == 'n') + nflag = 1; + p = buf; + for (;;) { + if (n-- <= 1) { + *p = '\0'; + linenum++; + if (more != NULL) + *more = 1; + return (buf); + } + switch (*s) { + case '\0': + state = ST_EOF; + if (s == script->s) { + script = script->next; + goto again; + } else { + script = script->next; + *p = '\0'; + linenum++; + if (more != NULL) + *more = 0; + return (buf); + } + case '\n': + *p++ = '\n'; + *p = '\0'; + s++; + linenum++; + if (more != NULL) + *more = 0; + return (buf); + default: + *p++ = *s++; + } + } + } + /* NOTREACHED */ + return (NULL); +} + +/* + * Like fgets, but go through the list of files chaining them together. + * Set len to the length of the line. + */ +int +mf_fgets(SPACE *sp, enum e_spflag spflag) +{ + struct stat sb; + size_t len; + static char *p = NULL; + static size_t plen = 0; + int c; + static int firstfile; + + if (infile == NULL) { + /* stdin? */ + if (files->fname == NULL) { + if (inplace != NULL) + errx(1, "-I or -i may not be used with stdin"); + infile = stdin; + fname = "stdin"; + outfile = stdout; + outfname = "stdout"; + } + firstfile = 1; + } + + for (;;) { + if (infile != NULL && (c = getc(infile)) != EOF) { + (void)ungetc(c, infile); + break; + } + /* If we are here then either eof or no files are open yet */ + if (infile == stdin) { + sp->len = 0; + return (0); + } + if (infile != NULL) { + fclose(infile); + if (*oldfname != '\0') { + /* if there was a backup file, remove it */ + unlink(oldfname); + /* + * Backup the original. Note that hard links + * are not supported on all filesystems. + */ + if ((link(fname, oldfname) != 0) && + (rename(fname, oldfname) != 0)) { + warn("rename()"); + if (*tmpfname) + unlink(tmpfname); + exit(1); + } + *oldfname = '\0'; + } + if (*tmpfname != '\0') { + if (outfile != NULL && outfile != stdout) + if (fclose(outfile) != 0) { + warn("fclose()"); + unlink(tmpfname); + exit(1); + } + outfile = NULL; + if (rename(tmpfname, fname) != 0) { + /* this should not happen really! */ + warn("rename()"); + unlink(tmpfname); + exit(1); + } + *tmpfname = '\0'; + } + outfname = NULL; + } + if (firstfile == 0) + files = files->next; + else + firstfile = 0; + if (files == NULL) { + sp->len = 0; + return (0); + } + fname = files->fname; + if (inplace != NULL) { + if (lstat(fname, &sb) != 0) + err(1, "%s", fname); + if (!(sb.st_mode & S_IFREG)) + errx(1, "%s: %s %s", fname, + "in-place editing only", + "works for regular files"); + if (*inplace != '\0') { + strlcpy(oldfname, fname, + sizeof(oldfname)); + len = strlcat(oldfname, inplace, + sizeof(oldfname)); + if (len > sizeof(oldfname)) + errx(1, "%s: name too long", fname); + } + char d_name[PATH_MAX], f_name[PATH_MAX]; + (void)strlcpy(d_name, fname, sizeof(d_name)); + (void)strlcpy(f_name, fname, sizeof(f_name)); + len = (size_t)snprintf(tmpfname, sizeof(tmpfname), + "%s/.!%ld!%s", dirname(d_name), (long)getpid(), + basename(f_name)); + if (len >= sizeof(tmpfname)) + errx(1, "%s: name too long", fname); + unlink(tmpfname); + if (outfile != NULL && outfile != stdout) + fclose(outfile); + if ((outfile = fopen(tmpfname, "w")) == NULL) + err(1, "%s", fname); + fchown(fileno(outfile), sb.st_uid, sb.st_gid); + fchmod(fileno(outfile), sb.st_mode & ALLPERMS); + outfname = tmpfname; + if (!ispan) { + linenum = 0; + resetstate(); + } + } else { + outfile = stdout; + outfname = "stdout"; + } + if ((infile = fopen(fname, "r")) == NULL) { + warn("%s", fname); + rval = 1; + continue; + } + } + /* + * We are here only when infile is open and we still have something + * to read from it. + * + * Use getline() so that we can handle essentially infinite input + * data. The p and plen are static so each invocation gives + * getline() the same buffer which is expanded as needed. + */ + ssize_t slen = getline(&p, &plen, infile); + if (slen == -1) + err(1, "%s", fname); + if (slen != 0 && p[slen - 1] == '\n') + slen--; + cspace(sp, p, (size_t)slen, spflag); + + linenum++; + + return (1); +} + +/* + * Add a compilation unit to the linked list + */ +static void +add_compunit(enum e_cut type, char *s) +{ + struct s_compunit *cu; + + cu = xmalloc(sizeof(struct s_compunit)); + cu->type = type; + cu->s = s; + cu->next = NULL; + *cu_nextp = cu; + cu_nextp = &cu->next; +} + +/* + * Add a file to the linked list + */ +static void +add_file(char *s) +{ + struct s_flist *fp; + + fp = xmalloc(sizeof(struct s_flist)); + fp->next = NULL; + *fl_nextp = fp; + fp->fname = s; + fl_nextp = &fp->next; +} + +int +lastline(void) +{ + int ch; + + if (files->next != NULL && (inplace == NULL || ispan)) + return (0); + if ((ch = getc(infile)) == EOF) + return (1); + ungetc(ch, infile); + return (0); +} diff --git a/buildrump.sh/src/usr.bin/sed/misc.c b/buildrump.sh/src/usr.bin/sed/misc.c new file mode 100644 index 00000000..bd0ecdff --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/misc.c @@ -0,0 +1,119 @@ +/* $NetBSD: misc.c,v 1.15 2014/06/26 02:14:32 christos Exp $ */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: misc.c,v 1.15 2014/06/26 02:14:32 christos Exp $"); +#ifdef __FBSDID +__FBSDID("$FreeBSD: head/usr.bin/sed/misc.c 200462 2009-12-13 03:14:06Z delphij $"); +#endif + +#if 0 +static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include <sys/types.h> + +#include <err.h> +#include <limits.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "defs.h" +#include "extern.h" + +/* + * malloc with result test + */ +void * +xmalloc(size_t size) +{ + void *p; + + if ((p = malloc(size)) == NULL) + err(1, "malloc(%zu)", size); + return p; +} + +/* + * realloc with result test + */ +void * +xrealloc(void *p, size_t size) +{ + if (p == NULL) /* Compatibility hack. */ + return (xmalloc(size)); + + if ((p = realloc(p, size)) == NULL) + err(1, "realloc(%zu)", size); + return p; +} + +/* + * realloc with result test + */ +void * +xcalloc(size_t c, size_t n) +{ + void *p; + + if ((p = calloc(c, n)) == NULL) + err(1, "calloc(%zu, %zu)", c, n); + return p; +} +/* + * Return a string for a regular expression error passed. This is overkill, + * because of the silly semantics of regerror (we can never know the size of + * the buffer). + */ +char * +strregerror(int errcode, regex_t *preg) +{ + char buf[1]; + static char *oe; + size_t s; + + if (oe != NULL) + free(oe); + s = regerror(errcode, preg, buf, 0); + oe = xmalloc(s); + (void)regerror(errcode, preg, oe, s); + return (oe); +} diff --git a/buildrump.sh/src/usr.bin/sed/process.c b/buildrump.sh/src/usr.bin/sed/process.c new file mode 100644 index 00000000..896df5a6 --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/process.c @@ -0,0 +1,792 @@ +/* $NetBSD: process.c,v 1.52 2015/03/12 12:40:41 christos Exp $ */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +__RCSID("$NetBSD: process.c,v 1.52 2015/03/12 12:40:41 christos Exp $"); +#ifdef __FBSDID +__FBSDID("$FreeBSD: head/usr.bin/sed/process.c 192732 2009-05-25 06:45:33Z brian $"); +#endif + +#if 0 +static const char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94"; +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/uio.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +#include "defs.h" +#include "extern.h" + +static SPACE HS, PS, SS, YS; +#define pd PS.deleted +#define ps PS.space +#define psl PS.len +#define hs HS.space +#define hsl HS.len + +static __inline int applies(struct s_command *); +static void do_tr(struct s_tr *); +static void flush_appends(void); +static void lputs(char *, size_t); +static __inline int regexec_e(regex_t *, const char *, int, int, size_t); +static void regsub(SPACE *, char *, char *); +static int substitute(struct s_command *); + +struct s_appends *appends; /* Array of pointers to strings to append. */ +static size_t appendx; /* Index into appends array. */ +size_t appendnum; /* Size of appends array. */ + +static int lastaddr; /* Set by applies if last address of a range. */ +static int sdone; /* If any substitutes since last line input. */ + /* Iov structure for 'w' commands. */ +static regex_t *defpreg; +size_t maxnsub; +regmatch_t *match; + +#define OUT() do {fwrite(ps, 1, psl, outfile); fputc('\n', outfile);} while (0) + +void +process(void) +{ + struct s_command *cp; + SPACE tspace; + size_t oldpsl = 0; + char *p; + + p = NULL; + + for (linenum = 0; mf_fgets(&PS, REPLACE);) { + pd = 0; +top: + cp = prog; +redirect: + while (cp != NULL) { + if (!applies(cp)) { + cp = cp->next; + continue; + } + switch (cp->code) { + case '{': + cp = cp->u.c; + goto redirect; + case 'a': + if (appendx >= appendnum) + appends = xrealloc(appends, + sizeof(struct s_appends) * + (appendnum *= 2)); + appends[appendx].type = AP_STRING; + appends[appendx].s = cp->t; + appends[appendx].len = strlen(cp->t); + appendx++; + break; + case 'b': + cp = cp->u.c; + goto redirect; + case 'c': + pd = 1; + psl = 0; + if (cp->a2 == NULL || lastaddr || lastline()) + (void)fprintf(outfile, "%s", cp->t); + goto new; + case 'd': + pd = 1; + goto new; + case 'D': + if (pd) + goto new; + if (psl == 0 || + (p = memchr(ps, '\n', psl - 1)) == NULL) { + pd = 1; + goto new; + } else { + psl -= (size_t)((p + 1) - ps); + memmove(ps, p + 1, psl); + goto top; + } + case 'g': + cspace(&PS, hs, hsl, REPLACE); + break; + case 'G': + cspace(&PS, "\n", 1, APPEND); + cspace(&PS, hs, hsl, APPEND); + break; + case 'h': + cspace(&HS, ps, psl, REPLACE); + break; + case 'H': + cspace(&HS, "\n", 1, APPEND); + cspace(&HS, ps, psl, APPEND); + break; + case 'i': + (void)fprintf(outfile, "%s", cp->t); + break; + case 'l': + lputs(ps, psl); + break; + case 'n': + if (!nflag && !pd) + OUT(); + flush_appends(); + if (!mf_fgets(&PS, REPLACE)) + exit(0); + pd = 0; + break; + case 'N': + flush_appends(); + cspace(&PS, "\n", 1, APPEND); + if (!mf_fgets(&PS, APPEND)) + exit(0); + break; + case 'p': + if (pd) + break; + OUT(); + break; + case 'P': + if (pd) + break; + if ((p = memchr(ps, '\n', psl - 1)) != NULL) { + oldpsl = psl; + psl = (size_t)(p - ps); + } + OUT(); + if (p != NULL) + psl = oldpsl; + break; + case 'q': + if (!nflag && !pd) + OUT(); + flush_appends(); + exit(0); + case 'r': + if (appendx >= appendnum) + appends = xrealloc(appends, + sizeof(struct s_appends) * + (appendnum *= 2)); + appends[appendx].type = AP_FILE; + appends[appendx].s = cp->t; + appends[appendx].len = strlen(cp->t); + appendx++; + break; + case 's': + sdone |= substitute(cp); + break; + case 't': + if (sdone) { + sdone = 0; + cp = cp->u.c; + goto redirect; + } + break; + case 'w': + if (pd) + break; + if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(1, "%s", cp->t); + if (write(cp->u.fd, ps, psl) != (ssize_t)psl || + write(cp->u.fd, "\n", 1) != 1) + err(1, "%s", cp->t); + break; + case 'x': + /* + * If the hold space is null, make it empty + * but not null. Otherwise the pattern space + * will become null after the swap, which is + * an abnormal condition. + */ + if (hs == NULL) + cspace(&HS, "", 0, REPLACE); + tspace = PS; + PS = HS; + HS = tspace; + break; + case 'y': + if (pd || psl == 0) + break; + do_tr(cp->u.y); + break; + case ':': + case '}': + break; + case '=': + (void)fprintf(outfile, "%lu\n", linenum); + } + cp = cp->next; + } /* for all cp */ + +new: if (!nflag && !pd) + OUT(); + flush_appends(); + } /* for all lines */ +} + +/* + * TRUE if the address passed matches the current program state + * (lastline, linenumber, ps). + */ +#define MATCH(a) \ + ((a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) : \ + (a)->type == AT_LINE ? linenum == (a)->u.l : lastline()) + +/* + * Return TRUE if the command applies to the current line. Sets the start + * line for process ranges. Interprets the non-select (``!'') flag. + */ +static __inline int +applies(struct s_command *cp) +{ + int r; + + lastaddr = 0; + if (cp->a1 == NULL && cp->a2 == NULL) + r = 1; + else if (cp->a2) + if (cp->startline > 0) { + switch (cp->a2->type) { + case AT_RELLINE: + if (linenum - cp->startline <= cp->a2->u.l) + r = 1; + else { + cp->startline = 0; + r = 0; + } + break; + default: + if (MATCH(cp->a2)) { + cp->startline = 0; + lastaddr = 1; + r = 1; + } else if (cp->a2->type == AT_LINE && + linenum > cp->a2->u.l) { + /* + * We missed the 2nd address due to a + * branch, so just close the range and + * return false. + */ + cp->startline = 0; + r = 0; + } else + r = 1; + } + } else if (cp->a1 && MATCH(cp->a1)) { + /* + * If the second address is a number less than or + * equal to the line number first selected, only + * one line shall be selected. + * -- POSIX 1003.2 + * Likewise if the relative second line address is zero. + */ + if ((cp->a2->type == AT_LINE && + linenum >= cp->a2->u.l) || + (cp->a2->type == AT_RELLINE && cp->a2->u.l == 0)) + lastaddr = 1; + else { + cp->startline = linenum; + } + r = 1; + } else + r = 0; + else + r = MATCH(cp->a1); + return (cp->nonsel ? ! r : r); +} + +/* + * Reset the sed processor to its initial state. + */ +void +resetstate(void) +{ + struct s_command *cp; + + /* + * Reset all in-range markers. + */ + for (cp = prog; cp; cp = cp->code == '{' ? cp->u.c : cp->next) + if (cp->a2) + cp->startline = 0; + + /* + * Clear out the hold space. + */ + cspace(&HS, "", 0, REPLACE); +} + +/* + * substitute -- + * Do substitutions in the pattern space. Currently, we build a + * copy of the new pattern space in the substitute space structure + * and then swap them. + */ +static int +substitute(struct s_command *cp) +{ + SPACE tspace; + regex_t *re; + regoff_t re_off, slen; + int lastempty, n; + char *s; + + s = ps; + re = cp->u.s->re; + if (re == NULL) { + if (defpreg != NULL && cp->u.s->maxbref > defpreg->re_nsub) { + linenum = cp->u.s->linenum; + errx(1, "%lu: %s: \\%u not defined in the RE", + linenum, fname, cp->u.s->maxbref); + } + } + if (!regexec_e(re, s, 0, 0, psl)) + return (0); + + SS.len = 0; /* Clean substitute space. */ + slen = (regoff_t)psl; + n = cp->u.s->n; + lastempty = 1; + + switch (n) { + case 0: /* Global */ + do { + if (lastempty || match[0].rm_so != match[0].rm_eo) { + /* Locate start of replaced string. */ + re_off = match[0].rm_so; + /* Copy leading retained string. */ + cspace(&SS, s, (size_t)re_off, APPEND); + /* Add in regular expression. */ + regsub(&SS, s, cp->u.s->new); + } + + /* Move past this match. */ + if (match[0].rm_so != match[0].rm_eo) { + s += match[0].rm_eo; + slen -= match[0].rm_eo; + lastempty = 0; + } else { + if (match[0].rm_so < slen) + cspace(&SS, s + match[0].rm_so, 1, + APPEND); + s += match[0].rm_so + 1; + slen -= match[0].rm_so + 1; + lastempty = 1; + } + } while (slen >= 0 && regexec_e(re, s, REG_NOTBOL, 0, (size_t)slen)); + /* Copy trailing retained string. */ + if (slen > 0) + cspace(&SS, s, (size_t)slen, APPEND); + break; + default: /* Nth occurrence */ + while (--n) { + if (match[0].rm_eo == match[0].rm_so) + match[0].rm_eo = match[0].rm_so + 1; + s += match[0].rm_eo; + slen -= match[0].rm_eo; + if (slen < 0) + return (0); + if (!regexec_e(re, s, REG_NOTBOL, 0, (size_t)slen)) + return (0); + } + /* FALLTHROUGH */ + case 1: /* 1st occurrence */ + /* Locate start of replaced string. */ + re_off = match[0].rm_so + (s - ps); + /* Copy leading retained string. */ + cspace(&SS, ps, (size_t)re_off, APPEND); + /* Add in regular expression. */ + regsub(&SS, s, cp->u.s->new); + /* Copy trailing retained string. */ + s += match[0].rm_eo; + slen -= match[0].rm_eo; + cspace(&SS, s, (size_t)slen, APPEND); + break; + } + + /* + * Swap the substitute space and the pattern space, and make sure + * that any leftover pointers into stdio memory get lost. + */ + tspace = PS; + PS = SS; + SS = tspace; + SS.space = SS.back; + + /* Handle the 'p' flag. */ + if (cp->u.s->p) + OUT(); + + /* Handle the 'w' flag. */ + if (cp->u.s->wfile && !pd) { + if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) + err(1, "%s", cp->u.s->wfile); + if (write(cp->u.s->wfd, ps, psl) != (ssize_t)psl || + write(cp->u.s->wfd, "\n", 1) != 1) + err(1, "%s", cp->u.s->wfile); + } + return (1); +} + +/* + * do_tr -- + * Perform translation ('y' command) in the pattern space. + */ +static void +do_tr(struct s_tr *y) +{ + SPACE tmp; + char c, *p; + size_t clen, left; + size_t i; + + if (MB_CUR_MAX == 1) { + /* + * Single-byte encoding: perform in-place translation + * of the pattern space. + */ + for (p = ps; p < &ps[psl]; p++) + *p = (char)y->bytetab[(u_char)*p]; + } else { + /* + * Multi-byte encoding: perform translation into the + * translation space, then swap the translation and + * pattern spaces. + */ + /* Clean translation space. */ + YS.len = 0; + for (p = ps, left = psl; left > 0; p += clen, left -= clen) { + if ((c = (char)y->bytetab[(u_char)*p]) != '\0') { + cspace(&YS, &c, 1, APPEND); + clen = 1; + continue; + } + for (i = 0; i < y->nmultis; i++) + if (left >= y->multis[i].fromlen && + memcmp(p, y->multis[i].from, + y->multis[i].fromlen) == 0) + break; + if (i < y->nmultis) { + cspace(&YS, y->multis[i].to, + y->multis[i].tolen, APPEND); + clen = y->multis[i].fromlen; + } else { + cspace(&YS, p, 1, APPEND); + clen = 1; + } + } + /* Swap the translation space and the pattern space. */ + tmp = PS; + PS = YS; + YS = tmp; + YS.space = YS.back; + } +} + +/* + * Flush append requests. Always called before reading a line, + * therefore it also resets the substitution done (sdone) flag. + */ +static void +flush_appends(void) +{ + FILE *f; + size_t count, i; + char buf[8 * 1024]; + + for (i = 0; i < appendx; i++) + switch (appends[i].type) { + case AP_STRING: + fwrite(appends[i].s, sizeof(char), appends[i].len, + outfile); + break; + case AP_FILE: + /* + * Read files probably shouldn't be cached. Since + * it's not an error to read a non-existent file, + * it's possible that another program is interacting + * with the sed script through the filesystem. It + * would be truly bizarre, but possible. It's probably + * not that big a performance win, anyhow. + */ + if ((f = fopen(appends[i].s, "r")) == NULL) + break; + while ((count = fread(buf, sizeof(char), sizeof(buf), f))) + (void)fwrite(buf, sizeof(char), count, outfile); + (void)fclose(f); + break; + } + if (ferror(outfile)) + errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); + appendx = 0; + sdone = 0; +} + +static void +lputs(char *s, size_t len) +{ + static const char escapes[] = "\\\a\b\f\r\t\v"; + int c; + size_t col, width; + const char *p; +#ifdef TIOCGWINSZ + struct winsize win; +#endif + static size_t termwidth = (size_t)-1; + size_t clen, i; + wchar_t wc; + mbstate_t mbs; + + if (outfile != stdout) + termwidth = 60; + if (termwidth == (size_t)-1) { + if ((p = getenv("COLUMNS")) && *p != '\0') + termwidth = (size_t)atoi(p); +#ifdef TIOCGWINSZ + else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && + win.ws_col > 0) + termwidth = win.ws_col; +#endif + else + termwidth = 60; + } + if (termwidth == 0) + termwidth = 1; + + memset(&mbs, 0, sizeof(mbs)); + col = 0; + while (len != 0) { + clen = mbrtowc(&wc, s, len, &mbs); + if (clen == 0) + clen = 1; + if (clen == (size_t)-1 || clen == (size_t)-2) { + wc = (unsigned char)*s; + clen = 1; + memset(&mbs, 0, sizeof(mbs)); + } + if (wc == '\n') { + if (col + 1 >= termwidth) + fprintf(outfile, "\\\n"); + fputc('$', outfile); + fputc('\n', outfile); + col = 0; + } else if (iswprint(wc)) { + width = (size_t)wcwidth(wc); + if (col + width >= termwidth) { + fprintf(outfile, "\\\n"); + col = 0; + } + fwrite(s, 1, clen, outfile); + col += width; + } else if (wc != L'\0' && (c = wctob(wc)) != EOF && + (p = strchr(escapes, c)) != NULL) { + if (col + 2 >= termwidth) { + fprintf(outfile, "\\\n"); + col = 0; + } + fprintf(outfile, "\\%c", "\\abfrtv"[p - escapes]); + col += 2; + } else { + if (col + 4 * clen >= termwidth) { + fprintf(outfile, "\\\n"); + col = 0; + } + for (i = 0; i < clen; i++) + fprintf(outfile, "\\%03o", + (int)(unsigned char)s[i]); + col += 4 * clen; + } + s += clen; + len -= clen; + } + if (col + 1 >= termwidth) + fprintf(outfile, "\\\n"); + (void)fputc('$', outfile); + (void)fputc('\n', outfile); + if (ferror(outfile)) + errx(1, "%s: %s", outfname, strerror(errno ? errno : EIO)); +} + +static __inline int +regexec_e(regex_t *preg, const char *string, int eflags, int nomatch, + size_t slen) +{ + int eval; +#ifndef REG_STARTEND + char *buf; +#endif + + if (preg == NULL) { + if (defpreg == NULL) + errx(1, "first RE may not be empty"); + } else + defpreg = preg; + + /* Set anchors */ +#ifndef REG_STARTEND + buf = xmalloc(slen + 1); + (void)memcpy(buf, string, slen); + buf[slen] = '\0'; + eval = regexec(defpreg, buf, + nomatch ? 0 : maxnsub + 1, match, eflags); + free(buf); +#else + match[0].rm_so = 0; + match[0].rm_eo = (regoff_t)slen; + eval = regexec(defpreg, string, + nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND); +#endif + switch(eval) { + case 0: + return (1); + case REG_NOMATCH: + return (0); + } + errx(1, "RE error: %s", strregerror(eval, defpreg)); + /* NOTREACHED */ +} + +/* + * regsub - perform substitutions after a regexp match + * Based on a routine by Henry Spencer + */ +static void +regsub(SPACE *sp, char *string, char *src) +{ + size_t len; + int no; + char c, *dst; + +#define NEEDSP(reqlen) \ + /* XXX What is the +1 for? */ \ + if (sp->len + (reqlen) + 1 >= sp->blen) { \ + sp->blen += (reqlen) + 1024; \ + sp->space = sp->back = xrealloc(sp->back, sp->blen); \ + dst = sp->space + sp->len; \ + } + + dst = sp->space + sp->len; + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '\\' && isdigit((unsigned char)*src)) + no = *src++ - '0'; + else + no = -1; + if (no < 0) { /* Ordinary character. */ + if (c == '\\' && (*src == '\\' || *src == '&')) + c = *src++; + NEEDSP(1); + *dst++ = c; + ++sp->len; + } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { + len = (size_t)(match[no].rm_eo - match[no].rm_so); + NEEDSP(len); + memmove(dst, string + match[no].rm_so, len); + dst += len; + sp->len += len; + } + } + NEEDSP(1); + *dst = '\0'; +} + +/* + * cspace -- + * Concatenate space: append the source space to the destination space, + * allocating new space as necessary. + */ +void +cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag) +{ + size_t tlen; + + /* Make sure SPACE has enough memory and ramp up quickly. */ + tlen = sp->len + len + 1; + if (tlen > sp->blen) { + sp->blen = tlen + 1024; + sp->space = sp->back = xrealloc(sp->back, sp->blen); + } + + if (spflag == REPLACE) + sp->len = 0; + + memmove(sp->space + sp->len, p, len); + + sp->space[sp->len += len] = '\0'; +} + +/* + * Close all cached opened files and report any errors + */ +void +cfclose(struct s_command *cp, struct s_command *end) +{ + + for (; cp != end; cp = cp->next) + switch(cp->code) { + case 's': + if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) + err(1, "%s", cp->u.s->wfile); + cp->u.s->wfd = -1; + break; + case 'w': + if (cp->u.fd != -1 && close(cp->u.fd)) + err(1, "%s", cp->t); + cp->u.fd = -1; + break; + case '{': + cfclose(cp->u.c, cp->next); + break; + } +} diff --git a/buildrump.sh/src/usr.bin/sed/sed.1 b/buildrump.sh/src/usr.bin/sed/sed.1 new file mode 100644 index 00000000..82b3cbd0 --- /dev/null +++ b/buildrump.sh/src/usr.bin/sed/sed.1 @@ -0,0 +1,640 @@ +.\" $NetBSD: sed.1,v 1.40 2014/06/25 02:05:58 uwe Exp $ +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" 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. +.\" +.\" @(#)sed.1 8.2 (Berkeley) 12/30/93 +.\" $FreeBSD: head/usr.bin/sed/sed.1 259132 2013-12-09 18:57:20Z eadler $ +.\" +.Dd June 18, 2014 +.Dt SED 1 +.Os +.Sh NAME +.Nm sed +.Nd stream editor +.Sh SYNOPSIS +.Nm +.Op Fl aElnru +.Ar command +.Op Ar +.Nm +.Op Fl aElnru +.Op Fl e Ar command +.Op Fl f Ar command_file +.Op Fl I Ns Op Ar extension +.Op Fl i Ns Op Ar extension +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility reads the specified files, or the standard input if no files +are specified, modifying the input as specified by a list of commands. +The input is then written to the standard output. +.Pp +A single command may be specified as the first argument to +.Nm . +Multiple commands may be specified by using the +.Fl e +or +.Fl f +options. +All commands are applied to the input in the order they are specified +regardless of their origin. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +The files listed as parameters for the +.Dq w +functions are created (or truncated) before any processing begins, +by default. +The +.Fl a +option causes +.Nm +to delay opening each file until a command containing the related +.Dq w +function is applied to a line of input. +.It Fl E +Interpret regular expressions as extended (modern) regular expressions +rather than basic regular expressions (BRE's). +The +.Xr re_format 7 +manual page fully describes both formats. +.It Fl e Ar command +Append the editing commands specified by the +.Ar command +argument +to the list of commands. +.It Fl f Ar command_file +Append the editing commands found in the file +.Ar command_file +to the list of commands. +The editing commands should each be listed on a separate line. +.It Fl I Ns Op Ar extension +Edit files in-place, saving backups with the specified +.Ar extension . +If no +.Ar extension +is given, no backup will be saved. +It is not recommended to give a zero-length +.Ar extension +when in-place editing files, as you risk corruption or partial content +in situations where disk space is exhausted, etc. +.Pp +Note that in-place editing with +.Fl I +still takes place in a single continuous line address space covering +all files, although each file preserves its individuality instead of +forming one output stream. +The line counter is never reset between files, address ranges can span +file boundaries, and the +.Dq $ +address matches only the last line of the last file. +(See +.Sx "Sed Addresses" . ) +That can lead to unexpected results in many cases of in-place editing, +where using +.Fl i +is desired. +.It Fl i Ns Op Ar extension +Edit files in-place similarly to +.Fl I , +but treat each file independently from other files. +In particular, line numbers in each file start at 1, +the +.Dq $ +address matches the last line of the current file, +and address ranges are limited to the current file. +(See +.Sx "Sed Addresses" . ) +The net result is as though each file were edited by a separate +.Nm +instance. +.It Fl l +Make output line buffered. +.It Fl n +By default, each line of input is echoed to the standard output after +all of the commands have been applied to it. +The +.Fl n +option suppresses this behavior. +.It Fl r +Same as +.Fl E +for compatibility with GNU sed. +.It Fl u +Make output unbuffered. +.El +.Pp +The form of a +.Nm +command is as follows: +.Pp +.Dl [address[,address]]function[arguments] +.Pp +Whitespace may be inserted before the first address and the function +portions of the command. +.Pp +Normally, +.Nm +cyclically copies a line of input, not including its terminating newline +character, into a +.Em "pattern space" , +(unless there is something left after a +.Dq D +function), +applies all of the commands with addresses that select that pattern space, +copies the pattern space to the standard output, appending a newline, and +deletes the pattern space. +.Pp +Some of the functions use a +.Em "hold space" +to save all or part of the pattern space for subsequent retrieval. +.Ss "Sed Addresses" +An address is not required, but if specified must have one of the +following formats: +.Bl -bullet -offset indent +.It +a number that counts +input lines +cumulatively across input files (or in each file independently +if a +.Fl i +option is in effect); +.It +a dollar +.Pq Dq $ +character that addresses the last line of input (or the last line +of the current file if a +.Fl i +option was specified); +.It +a context address +that consists of a regular expression preceded and followed by a +delimiter. +The closing delimiter can also optionally be followed by the +.Dq i +character, to indicate that the regular expression is to be matched +in a case-insensitive way. +.El +.Pp +A command line with no addresses selects every pattern space. +.Pp +A command line with one address selects all of the pattern spaces +that match the address. +.Pp +A command line with two addresses selects an inclusive range. +This +range starts with the first pattern space that matches the first +address. +The end of the range is the next following pattern space +that matches the second address. +If the second address is a number +less than or equal to the line number first selected, only that +line is selected. +The number in the second address may be prefixed with a +.Pq Dq \&+ +to specify the number of lines to match after the first pattern. +In the case when the second address is a context +address, +.Nm +does not re-match the second address against the +pattern space that matched the first address. +Starting at the +first line following the selected range, +.Nm +starts looking again for the first address. +.Pp +Editing commands can be applied to non-selected pattern spaces by use +of the exclamation character +.Pq Dq \&! +function. +.Ss "Sed Regular Expressions" +The regular expressions used in +.Nm , +by default, are basic regular expressions (BREs, see +.Xr re_format 7 +for more information), but extended (modern) regular expressions can be used +instead if the +.Fl E +flag is given. +In addition, +.Nm +has the following two additions to regular expressions: +.Pp +.Bl -enum -compact +.It +In a context address, any character other than a backslash +.Pq Dq \e +or newline character may be used to delimit the regular expression. +The opening delimiter needs to be preceded by a backslash +unless it is a slash. +For example, the context address +.Li \exabcx +is equivalent to +.Li /abc/ . +Also, putting a backslash character before the delimiting character +within the regular expression causes the character to be treated literally. +For example, in the context address +.Li \exabc\exdefx , +the RE delimiter is an +.Dq x +and the second +.Dq x +stands for itself, so that the regular expression is +.Dq abcxdef . +.Pp +.It +The escape sequence \en matches a newline character embedded in the +pattern space. +You cannot, however, use a literal newline character in an address or +in the substitute command. +.El +.Pp +One special feature of +.Nm +regular expressions is that they can default to the last regular +expression used. +If a regular expression is empty, i.e., just the delimiter characters +are specified, the last regular expression encountered is used instead. +The last regular expression is defined as the last regular expression +used as part of an address or substitute command, and at run-time, not +compile-time. +For example, the command +.Dq /abc/s//XXX/ +will substitute +.Dq XXX +for the pattern +.Dq abc . +.Ss "Sed Functions" +In the following list of commands, the maximum number of permissible +addresses for each command is indicated by [0addr], [1addr], or [2addr], +representing zero, one, or two addresses. +.Pp +The argument +.Em text +consists of one or more lines. +To embed a newline in the text, precede it with a backslash. +Other backslashes in text are deleted and the following character +taken literally. +.Pp +The +.Dq r +and +.Dq w +functions take an optional file parameter, which should be separated +from the function letter by white space. +Each file given as an argument to +.Nm +is created (or its contents truncated) before any input processing begins. +.Pp +The +.Dq b , +.Dq r , +.Dq s , +.Dq t , +.Dq w , +.Dq y , +.Dq \&! , +and +.Dq \&: +functions all accept additional arguments. +The following synopses indicate which arguments have to be separated from +the function letters by white space characters. +.Pp +Two of the functions take a function-list. +This is a list of +.Nm +functions separated by newlines, as follows: +.Bd -literal -offset indent +{ function + function + ... + function +} +.Ed +.Pp +The +.Dq { +can be preceded by white space and can be followed by white space. +The function can be preceded by white space. +The terminating +.Dq } +must be preceded by a newline, and may also be preceded by white space. +.Pp +.Bl -tag -width "XXXXXX" -compact +.It [2addr] function-list +Execute function-list only when the pattern space is selected. +.Pp +.It [1addr]a\e +.It text +Write +.Em text +to standard output immediately before each attempt to read a line of input, +whether by executing the +.Dq N +function or by beginning a new cycle. +.Pp +.It [2addr]b[label] +Branch to the +.Dq \&: +function with the specified label. +If the label is not specified, branch to the end of the script. +.Pp +.It [2addr]c\e +.It text +Delete the pattern space. +With 0 or 1 address or at the end of a 2-address range, +.Em text +is written to the standard output. +.Pp +.It [2addr]d +Delete the pattern space and start the next cycle. +.Pp +.It [2addr]D +Delete the initial segment of the pattern space through the first +newline character and start the next cycle. +.Pp +.It [2addr]g +Replace the contents of the pattern space with the contents of the +hold space. +.Pp +.It [2addr]G +Append a newline character followed by the contents of the hold space +to the pattern space. +.Pp +.It [2addr]h +Replace the contents of the hold space with the contents of the +pattern space. +.Pp +.It [2addr]H +Append a newline character followed by the contents of the pattern space +to the hold space. +.Pp +.It [1addr]i\e +.It text +Write +.Em text +to the standard output. +.Pp +.It [2addr]l +(The letter ell.) +Write the pattern space to the standard output in a visually unambiguous +form. +This form is as follows: +.Pp +.Bl -tag -width "carriage-returnXX" -offset indent -compact +.It backslash +\e\e +.It alert +\ea +.It form-feed +\ef +.It carriage-return +\er +.It tab +\et +.It vertical tab +\ev +.El +.Pp +Nonprintable characters are written as three-digit octal numbers (with a +preceding backslash) for each byte in the character (most significant byte +first). +Long lines are folded, with the point of folding indicated by displaying +a backslash followed by a newline. +The end of each line is marked with a +.Dq $ . +.Pp +.It [2addr]n +Write the pattern space to the standard output if the default output has +not been suppressed, and replace the pattern space with the next line of +input. +.Pp +.It [2addr]N +Append the next line of input to the pattern space, using an embedded +newline character to separate the appended material from the original +contents. +Note that the current line number changes. +.Pp +.It [2addr]p +Write the pattern space to standard output. +.Pp +.It [2addr]P +Write the pattern space, up to the first newline character to the +standard output. +.Pp +.It [1addr]q +Branch to the end of the script and quit without starting a new cycle. +.Pp +.It [1addr]r file +Copy the contents of +.Em file +to the standard output immediately before the next attempt to read a +line of input. +If +.Em file +cannot be read for any reason, it is silently ignored and no error +condition is set. +.Pp +.It [2addr]s/regular expression/replacement/flags +Substitute the replacement string for the first instance of the regular +expression in the pattern space. +Any character other than backslash or newline can be used instead of +a slash to delimit the RE and the replacement. +Within the RE and the replacement, the RE delimiter itself can be used as +a literal character if it is preceded by a backslash. +.Pp +An ampersand +.Pq Dq & +appearing in the replacement is replaced by the string matching the RE. +The special meaning of +.Dq & +in this context can be suppressed by preceding it by a backslash. +The string +.Dq \e# , +where +.Dq # +is a digit, is replaced by the text matched +by the corresponding backreference expression (see +.Xr re_format 7 ) . +.Pp +A line can be split by substituting a newline character into it. +To specify a newline character in the replacement string, precede it with +a backslash. +.Pp +The value of +.Em flags +in the substitute function is zero or more of the following: +.Bl -tag -width "XXXXXX" -offset indent +.It Ar N +Make the substitution only for the +.Ar N Ns 'th +occurrence of the regular expression in the pattern space. +.It g +Make the substitution for all non-overlapping matches of the +regular expression, not just the first one. +.It p +Write the pattern space to standard output if a replacement was made. +If the replacement string is identical to that which it replaces, it +is still considered to have been a replacement. +.It w Em file +Append the pattern space to +.Em file +if a replacement was made. +If the replacement string is identical to that which it replaces, it +is still considered to have been a replacement. +.It i or I +Match the regular expression in a case-insensitive way. +.El +.Pp +.It [2addr]t [label] +Branch to the +.Dq \&: +function bearing the label if any substitutions have been made since the +most recent reading of an input line or execution of a +.Dq t +function. +If no label is specified, branch to the end of the script. +.Pp +.It [2addr]w Em file +Append the pattern space to the +.Em file . +.Pp +.It [2addr]x +Swap the contents of the pattern and hold spaces. +.Pp +.It [2addr]y/string1/string2/ +Replace all occurrences of characters in +.Em string1 +in the pattern space with the corresponding characters from +.Em string2 . +Any character other than a backslash or newline can be used instead of +a slash to delimit the strings. +Within +.Em string1 +and +.Em string2 , +a backslash followed by any character other than a newline is that literal +character, and a backslash followed by an ``n'' is replaced by a newline +character. +.Pp +.It [2addr]!function +.It [2addr]!function-list +Apply the function or function-list only to the lines that are +.Em not +selected by the address(es). +.Pp +.It [0addr]:label +This function does nothing; it bears a label to which the +.Dq b +and +.Dq t +commands may branch. +.Pp +.It [1addr]= +Write the line number to the standard output followed by a newline +character. +.Pp +.It [0addr] +Empty lines are ignored. +.Pp +.It [0addr]# +The +.Dq # +and the remainder of the line are ignored (treated as a comment), with +the single exception that if the first two characters in the file are +.Dq #n , +the default output is suppressed. +This is the same as specifying the +.Fl n +option on the command line. +.El +.Sh ENVIRONMENT +The +.Ev COLUMNS , LANG , LC_ALL , LC_CTYPE +and +.Ev LC_COLLATE +environment variables affect the execution of +.Nm +as described in +.Xr environ 7 . +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr awk 1 , +.Xr ed 1 , +.Xr grep 1 , +.Xr regex 3 , +.Xr re_format 7 +.Sh STANDARDS +The +.Nm +utility is expected to be a superset of the +.St -p1003.2 +specification. +.Pp +The +.Fl a , E , I , +and +.Fl i +options, the prefixing +.Dq \&+ +in the second member of an address range, +as well as the +.Dq I +flag to the address regular expression and substitution command are +non-standard +.Fx +extensions and may not be available on other operating systems. +.Sh HISTORY +A +.Nm +command, written by +.An L. E. McMahon , +appeared in +.At v7 . +.Sh AUTHORS +.An "Diomidis D. Spinellis" Aq dds@FreeBSD.org +.Sh BUGS +Multibyte characters containing a byte with value 0x5C +.Tn ( ASCII +.Ql \e ) +may be incorrectly treated as line continuation characters in arguments to the +.Dq a , +.Dq c +and +.Dq i +commands. +Multibyte characters cannot be used as delimiters with the +.Dq s +and +.Dq y +commands. diff --git a/buildrump.sh/src/usr.bin/shmif_dumpbus/Makefile b/buildrump.sh/src/usr.bin/shmif_dumpbus/Makefile new file mode 100644 index 00000000..1eb32428 --- /dev/null +++ b/buildrump.sh/src/usr.bin/shmif_dumpbus/Makefile @@ -0,0 +1,16 @@ +# $NetBSD: Makefile,v 1.3 2013/12/20 09:44:16 pooka Exp $ +# + +PROG= shmif_dumpbus + +.include <bsd.own.mk> + +SHMIFD= ${NETBSDSRCDIR}/sys/rump/net/lib/libshmif +.PATH: ${SHMIFD} + +SRCS+= shmif_dumpbus.c shmif_busops.c +CPPFLAGS+= -I${SHMIFD} + +LDADD+= -lpcap + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/shmif_dumpbus/shmif_dumpbus.1 b/buildrump.sh/src/usr.bin/shmif_dumpbus/shmif_dumpbus.1 new file mode 100644 index 00000000..0be5b3d5 --- /dev/null +++ b/buildrump.sh/src/usr.bin/shmif_dumpbus/shmif_dumpbus.1 @@ -0,0 +1,80 @@ +.\" $NetBSD: shmif_dumpbus.1,v 1.2 2011/01/12 19:53:12 wiz Exp $ +.\" +.\" Copyright (c) 2011 Antti Kantee. 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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. +.\" +.Dd January 12, 2011 +.Dt SHMIF_DUMPBUS 1 +.Os +.Sh NAME +.Nm shmif_dumpbus +.Nd examine shmif bus contents +.Sh SYNOPSIS +.Nm +.Op Fl h +.Op Fl p Ar pcapfile +.Ar busfile +.Sh DESCRIPTION +The +.Nm +utility examines the bus of an +.Xr shmif 4 +Ethernet interface. +The most useful feature is converting the bus to the +.Xr pcap 3 +file format for later examination. +.Nm +itself is limited to displaying only very basic information about +each frame. +.Pp +.Nm +accepts the following flags: +.Bl -tag -width xxxpcapfilexxx +.It Fl h +Print bus header only and skip contents. +.It Fl p Ar pcapfile +Convert bus contents to the +.Xr pcap 3 +format and write the result to +.Ar pcapfile . +The file +.Fl +signifies stdout. +.El +.Sh EXAMPLES +Feed the busfile contents to pcap: +.Bd -literal -offset indent +$ shmif_dumpbus -p - busfile | tcpdump -r - +.Ed +.Sh SEE ALSO +.Xr pcap 3 , +.Xr shmif 4 , +.Xr tcpdump 8 +.Sh CAVEATS +.Nm +does not lock the busfile and is best used for post-mortem analysis +of the bus traffic. +.Pp +The timestamp for each frame contains the sender's timestamp and +may not be monotonically increasing with respect to the frame order +in the dump. diff --git a/buildrump.sh/src/usr.bin/shmif_dumpbus/shmif_dumpbus.c b/buildrump.sh/src/usr.bin/shmif_dumpbus/shmif_dumpbus.c new file mode 100644 index 00000000..f9326372 --- /dev/null +++ b/buildrump.sh/src/usr.bin/shmif_dumpbus/shmif_dumpbus.c @@ -0,0 +1,282 @@ +/* $NetBSD: shmif_dumpbus.c,v 1.18 2014/11/04 19:05:17 pooka Exp $ */ + +/*- + * Copyright (c) 2010 Antti Kantee. 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. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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. + */ + +/* + * Convert shmif bus traffic to a pcap file which can be then + * examined with tcpdump -r, wireshark, etc. + */ + +#include <rump/rumpuser_port.h> + +#ifndef lint +__RCSID("$NetBSD: shmif_dumpbus.c,v 1.18 2014/11/04 19:05:17 pooka Exp $"); +#endif /* !lint */ + +#include <sys/types.h> +#include <sys/mman.h> +#include <sys/stat.h> +#ifdef __NetBSD__ +#include <sys/bswap.h> +#endif + +#include <assert.h> +#include <err.h> +#include <fcntl.h> +#include <inttypes.h> +#include <pcap.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "shmifvar.h" + +__dead static void +usage(void) +{ + +#ifndef HAVE_GETPROGNAME +#define getprogname() "shmif_dumpbus" +#endif + + fprintf(stderr, "usage: %s [-h] [-p pcapfile] buspath\n",getprogname()); + exit(1); +} + +#define BUFSIZE 64*1024 + +/* + * byte swapdom + */ +static uint32_t +swp32(uint32_t x) +{ + uint32_t v; + + v = (((x) & 0xff000000) >> 24) | + (((x) & 0x00ff0000) >> 8) | + (((x) & 0x0000ff00) << 8) | + (((x) & 0x000000ff) << 24); + return v; +} + +static uint64_t +swp64(uint64_t x) +{ + uint64_t v; + + v = (((x) & 0xff00000000000000ull) >> 56) | + (((x) & 0x00ff000000000000ull) >> 40) | + (((x) & 0x0000ff0000000000ull) >> 24) | + (((x) & 0x000000ff00000000ull) >> 8) | + (((x) & 0x00000000ff000000ull) << 8) | + (((x) & 0x0000000000ff0000ull) << 24) | + (((x) & 0x000000000000ff00ull) << 40) | + (((x) & 0x00000000000000ffull) << 56); + return v; +} + +#define FIXENDIAN32(x) (doswap ? swp32(x) : (x)) +#define FIXENDIAN64(x) (doswap ? swp64(x) : (x)) + +/* compat for bus version 2 */ +struct shmif_pkthdr2 { + uint32_t sp_len; + + uint32_t sp_sec; + uint32_t sp_usec; +}; + +int +main(int argc, char *argv[]) +{ + struct stat sb; + void *busmem; + const char *pcapfile = NULL; + uint32_t curbus, buslast; + struct shmif_mem *bmem; + int fd, i, ch; + int bonus; + char *buf; + bool hflag = false, doswap = false; + pcap_dumper_t *pdump; + FILE *dumploc = stdout; + int useversion; + + setprogname(argv[0]); + while ((ch = getopt(argc, argv, "hp:")) != -1) { + switch (ch) { + case 'h': + hflag = true; + break; + case 'p': + pcapfile = optarg; + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + buf = malloc(BUFSIZE); + if (buf == NULL) + err(1, "malloc"); + + fd = open(argv[0], O_RDONLY); + if (fd == -1) + err(1, "open bus"); + + if (fstat(fd, &sb) == -1) + err(1, "stat"); + + busmem = mmap(NULL, sb.st_size, PROT_READ, MAP_FILE|MAP_SHARED, fd, 0); + if (busmem == MAP_FAILED) + err(1, "mmap"); + bmem = busmem; + + if (bmem->shm_magic != SHMIF_MAGIC) { + if (bmem->shm_magic != swp32(SHMIF_MAGIC)) + errx(1, "%s not a shmif bus", argv[0]); + doswap = true; + } + if (FIXENDIAN32(bmem->shm_version) != SHMIF_VERSION) { + if (FIXENDIAN32(bmem->shm_version) != 2) { + errx(1, "bus version %d, program %d", + FIXENDIAN32(bmem->shm_version), SHMIF_VERSION); + } + useversion = 2; + } else { + useversion = 3; + } + + if (pcapfile && strcmp(pcapfile, "-") == 0) + dumploc = stderr; + + fprintf(dumploc, "bus version %d, lock: %d, generation: %" PRIu64 + ", firstoff: 0x%04x, lastoff: 0x%04x\n", + FIXENDIAN32(bmem->shm_version), FIXENDIAN32(bmem->shm_lock), + FIXENDIAN64(bmem->shm_gen), + FIXENDIAN32(bmem->shm_first), FIXENDIAN32(bmem->shm_last)); + + if (hflag) + exit(0); + + if (pcapfile) { + pcap_t *pcap = pcap_open_dead(DLT_EN10MB, 1518); + pdump = pcap_dump_open(pcap, pcapfile); + if (pdump == NULL) + err(1, "cannot open pcap dump file"); + } else { + /* XXXgcc */ + pdump = NULL; + } + + curbus = FIXENDIAN32(bmem->shm_first); + buslast = FIXENDIAN32(bmem->shm_last); + if (curbus == BUSMEM_DATASIZE) + curbus = 0; + + bonus = 0; + if (buslast < curbus) + bonus = 1; + + i = 0; + + while (curbus <= buslast || bonus) { + struct pcap_pkthdr packhdr; + uint32_t oldoff; + uint32_t curlen; + uint32_t sp_sec, sp_usec, sp_len; + bool wrap; + + assert(curbus < sb.st_size); + + wrap = false; + oldoff = curbus; + + if (useversion == 3) { + struct shmif_pkthdr sp; + + curbus = shmif_busread(bmem, + &sp, oldoff, sizeof(sp), &wrap); + sp_len = FIXENDIAN32(sp.sp_len); + sp_sec = FIXENDIAN32(sp.sp_sec); + sp_usec = FIXENDIAN32(sp.sp_usec); + } else { + struct shmif_pkthdr2 sp2; + + curbus = shmif_busread(bmem, + &sp2, oldoff, sizeof(sp2), &wrap); + sp_len = FIXENDIAN32(sp2.sp_len); + sp_sec = FIXENDIAN32(sp2.sp_sec); + sp_usec = FIXENDIAN32(sp2.sp_usec); + } + if (wrap) + bonus = 0; + + assert(curbus < sb.st_size); + curlen = sp_len; + + if (curlen == 0) { + continue; + } + + fprintf(dumploc, "packet %d, offset 0x%04x, length 0x%04x, " + "ts %d/%06d\n", i++, curbus, curlen, + sp_sec, sp_usec); + + if (!pcapfile) { + curbus = shmif_busread(bmem, + buf, curbus, curlen, &wrap); + if (wrap) + bonus = 0; + continue; + } + + memset(&packhdr, 0, sizeof(packhdr)); + packhdr.caplen = packhdr.len = curlen; + packhdr.ts.tv_sec = sp_sec; + packhdr.ts.tv_usec = sp_usec; + assert(curlen <= BUFSIZE); + + curbus = shmif_busread(bmem, buf, curbus, curlen, &wrap); + pcap_dump((u_char *)pdump, &packhdr, (u_char *)buf); + if (wrap) + bonus = 0; + } + + if (pcapfile) + pcap_dump_close(pdump); + + return 0; +} diff --git a/buildrump.sh/src/usr.bin/stat/Makefile b/buildrump.sh/src/usr.bin/stat/Makefile new file mode 100644 index 00000000..5bdf76e4 --- /dev/null +++ b/buildrump.sh/src/usr.bin/stat/Makefile @@ -0,0 +1,12 @@ +# $NetBSD: Makefile,v 1.8 2011/08/17 13:22:33 christos Exp $ + +PROG= stat + +.if !defined(HOSTPROG) +LINKS= ${BINDIR}/stat ${BINDIR}/readlink +MLINKS= stat.1 readlink.1 +.endif + +COPTS.stat.c += -Wno-format-nonliteral + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/stat/stat.1 b/buildrump.sh/src/usr.bin/stat/stat.1 new file mode 100644 index 00000000..8d1e30cb --- /dev/null +++ b/buildrump.sh/src/usr.bin/stat/stat.1 @@ -0,0 +1,634 @@ +.\" $NetBSD: stat.1,v 1.37 2014/04/13 01:45:34 snj Exp $ +.\" +.\" Copyright (c) 2002-2011 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Andrew Brown and Jan Schaumann. +.\" +.\" 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. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. +.\" +.Dd December 2, 2012 +.Dt STAT 1 +.Os +.Sh NAME +.Nm stat , +.Nm readlink +.Nd display file status +.Sh SYNOPSIS +.Nm +.Op Fl FLnq +.Oo +.Fl f Ar format | +.Fl l | +.Fl r | +.Fl s | +.Fl x +.Oc +.Op Fl t Ar timefmt +.Op Ar +.Nm readlink +.Op Fl fnqsv +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility displays information about the file pointed to by +.Ar file . +Read, write, or execute permissions of the named file are not required, but +all directories listed in the pathname leading to the file must be +searchable. +If no argument is given, +.Nm +displays information about the file descriptor for standard input. +.Pp +When invoked as +.Nm readlink , +only the target of the symbolic link is printed. +If the given argument is not a symbolic link and the +.Fl f +option is not specified, +.Nm readlink +will print nothing and exit with an error. +If the +.Fl f +option is specified, the output is canonicalized by following every symlink +in every component of the given path recursively. +.Nm readlink +will resolve both absolute and relative paths, and return the absolute pathname +corresponding to +.Ar file . +In this case, the argument does not need to be a symbolic link. +.Pp +The information displayed is obtained by calling +.Xr lstat 2 +with the given argument and evaluating the returned structure. +The default format displays the +.Fa st_dev , +.Fa st_ino , +.Fa st_mode , +.Fa st_nlink , +.Fa st_uid , +.Fa st_gid , +.Fa st_rdev , +.Fa st_size , +.Fa st_atime , +.Fa st_mtime , +.Fa st_ctime , +.Fa st_birthtime , +.Fa st_blksize , +.Fa st_blocks , +and +.Fa st_flags +fields, in that order. +.Pp +The options are as follows: +.Bl -tag -width XFXformatXXX +.It Fl F +As in +.Xr ls 1 , +display a slash +.Pq Sq / +immediately after each pathname that is a directory, an +asterisk +.Pq Sq * +after each that is executable, an at sign +.Pq Sq @ +after each symbolic link, a percent sign +.Pq Sq % +after each whiteout, an equal sign +.Pq Sq = +after each socket, and a vertical bar +.Pq Sq \&| +after each that is a FIFO. +The use of +.Fl F +implies +.Fl l . +.It Fl f Ar format +Display information using the specified format. +See the +.Sx FORMATS +section for a description of valid formats. +.It Fl L +Use +.Xr stat 2 +instead of +.Xr lstat 2 . +The information reported by +.Nm +will refer to the target of +.Ar file , +if file is a symbolic link, and not to +.Ar file +itself. +.It Fl l +Display output in +.Ic ls Fl lT +format. +.It Fl n +Do not force a newline to appear at the end of each piece of output. +.It Fl q +Suppress failure messages if calls to +.Xr stat 2 +or +.Xr lstat 2 +fail. +When run as +.Nm readlink , +error messages are automatically suppressed. +.It Fl r +Display raw information. +That is, for all the fields in the stat-structure, +display the raw, numerical value (for example, times in seconds since the +epoch, etc.) +.It Fl s +Display information in +.Dq shell output , +suitable for initializing variables. +When run as +.Nm readlink , +suppress error messages. +This is equivalent to specifying +.Bd -literal +FMT="st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l st_uid=%u st_gid=%g" +FMT="$FMT st_rdev=%r st_size=%z st_atime=%Sa st_mtime=%Sm st_ctime=%Sc" +FMT="$FMT st_birthtime=%SB st_blksize=%k st_blocks=%b st_flags=%f" +stat -t %s -f "$FMT" . +.Ed +Note that if you use a timeformat that contains embedded whitespace or shell +meta-characters you will need to include appropriate quoting so the +.Fl s +output remains valid. +.It Fl t Ar timefmt +Display timestamps using the specified format. +This format is +passed directly to +.Xr strftime 3 . +.It Fl v +Turn off quiet mode. +.It Fl x +Display information in a more verbose way as known from some Linux +distributions. +.El +.Ss FORMATS +Format strings are similar to +.Xr printf 3 +formats in that they start with +.Cm % , +are then followed by a sequence of formatting characters, and end in +a character that selects the field of the struct stat which is to be +formatted. +If the +.Cm % +is immediately followed by one of +.Cm n , +.Cm t , +.Cm % , +or +.Cm @ , +then a newline character, a tab character, a percent character, +or the current file number is printed, otherwise the string is +examined for the following: +.Pp +Any of the following optional flags: +.Bl -tag -width Ds +.It Cm # +Selects an alternate output form for string, octal and hexadecimal output. +String output will be encoded in +.Xr vis 3 +style. +Non-zero octal output will have a leading zero. +Non-zero hexadecimal output will have +.Dq 0x +prepended to it. +.It Cm + +Asserts that a sign indicating whether a number is positive or negative +should always be printed. +Non-negative numbers are not usually printed with a sign. +.It Cm - +Aligns string output to the left of the field, instead of to the right. +.It Cm 0 +Sets the fill character for left padding to the 0 character, instead of +a space. +.It space +Reserves a space at the front of non-negative signed output fields. +A +.Sq Cm + +overrides a space if both are used. +.El +.Pp +Then the following fields: +.Bl -tag -width Ds +.It Cm size +An optional decimal digit string specifying the minimum field width. +.It Cm prec +An optional precision composed of a decimal point +.Sq Cm \&. +and a decimal digit string that indicates the maximum string length, +the number of digits to appear after the decimal point in floating point +output, or the minimum number of digits to appear in numeric output. +.It Cm fmt +An optional output format specifier which is one of +.Cm D , +.Cm O , +.Cm U , +.Cm X , +.Cm F , +or +.Cm S . +These represent signed decimal output, octal output, unsigned decimal +output, hexadecimal output, floating point output, and string output, +respectively. +Some output formats do not apply to all fields. +Floating point output only applies to timespec fields (the +.Cm a , +.Cm m , +and +.Cm c +fields). +.Pp +The special output specifier +.Cm S +may be used to indicate that the output, if +applicable, should be in string format. +May be used in combination with +.Bl -tag -width Ds +.It Cm amc +Display date in strftime(3) format. +.It Cm dr +Display actual device name. +.It Cm gu +Display group or user name. +.It Cm p +Display the mode of +.Ar file +as in +.Ic ls -lTd . +.It Cm N +Displays the name of +.Ar file . +.It Cm T +Displays the type of +.Ar file . +.It Cm Y +Insert a `` -\*[Gt] '' into the output. +Note that the default output format for +.Cm Y +is a string, but if specified explicitly, these four characters are +prepended. +.El +.It Cm sub +An optional sub field specifier (high, middle, or low). +Only applies to the +.Cm p , +.Cm d , +.Cm r , +.Cm T , +.Cm N , +and +.Cm z +output formats. +It can be one of the following: +.Bl -tag -width Ds +.It Cm H +.Dq High +-- depending on the +.Cm datum : +.Bl -tag -compact -width door +.It Cm d , r +Major number for devices +.It Cm p +.Dq User +bits from the string form of permissions or the file +.Dq type +bits from the numeric forms +.It Cm T +The long output form of file type +.It Cm N +Directory path of the file, similar to what +.Xr dirname 1 +would show +.It Cm z +File size, rounded to the nearest gigabyte +.El +.It Cm M +.Dq Middle +-- depending on the +.Cm datum : +.Bl -tag -compact -width door +.It Cm p +The +.Dq group +bits from the string form of permissions or the +.Dq suid , +.Dq sgid , +and +.Dq sticky +bits from the numeric forms +.It Cm z +File size, rounded to the nearest megabyte +.El +.It Cm L +.Dq Low +-- depending on the +.Cm datum : +.Bl -tag -compact -width door +.It Cm r , d +Minor number for devices +.It Cm p +The +.Dq other +bits from the string form of permissions or the +.Dq user , +.Dq group , +and +.Dq other +bits from the numeric forms +.It Cm T +The +.Ic ls -F +style output character for file type (the use of +.Cm L +here is optional) +.It Cm N +Base filename of the file, similar to what +.Xr basename 1 +would show +.It Cm z +File size, rounded to the nearest kilobyte +.El +.El +.It Cm datum +A required field specifier, being one of the following: +.Bl -tag -width 11n +.It Cm d +Device upon which +.Ar file +resides +.Pq Fa st_dev . +.It Cm i +.Ar file Ap s +inode number +.Pq Fa st_ino . +.It Cm p +File type and permissions +.Pq Fa st_mode . +.It Cm l +Number of hard links to +.Ar file +.Pq Fa st_nlink . +.It Cm u , g +User-id and group-id of +.Ar file Ap s +owner +.Pq Fa st_uid , st_gid . +.It Cm r +Device number for character and block device special files +.Pq Fa st_rdev . +.It Cm a , m , c , B +The time +.Ar file +was last accessed or modified, or when the inode was last changed, or +the birth time of the inode +.Pq Fa st_atime , st_mtime , st_ctime, st_birthtime . +.It Cm z +The size of +.Ar file +in bytes +.Pq Fa st_size . +.It Cm b +Number of blocks allocated for +.Ar file +.Pq Fa st_blocks . +.It Cm k +Optimal file system I/O operation block size +.Pq Fa st_blksize . +.It Cm f +User defined flags for +.Ar file +.Pq Fa st_flags . +.It Cm v +Inode generation number +.Pq Fa st_gen . +.El +.Pp +The following five field specifiers are not drawn directly from the +data in struct stat, but are: +.Bl -tag -width Ds +.It Cm N +The name of the file. +.It Cm R +The absolute pathname corresponding to the file. +.It Cm T +The file type, either as in +.Ic ls -F +or in a more descriptive form if the sub field specifier +.Cm H +is given. +.It Cm Y +The target of a symbolic link. +.It Cm Z +Expands to +.Dq Ar major , Ns Ar minor +from the rdev field for character or block +special devices and gives size output for all others. +.El +.El +.Pp +Only the +.Cm % +and the field specifier are required. +Most field specifiers default to +.Cm U +as an output form, with the +exception of +.Cm p +which defaults to +.Cm O ; +.Cm a , m , +and +.Cm c +which default to +.Cm D ; +and +.Cm Y , T , +and +.Cm N , +which default to +.Cm S . +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +If no options are specified, the default format is +"%d %i %Sp %l %Su %Sg %r %z \e"%Sa\e" \e"%Sm\e" \e"%Sc\e" \e"%SB\e" %k %b %#Xf %N". +.Bd -literal -offset indent +\*[Gt] stat /tmp/bar +0 78852 -rw-r--r-- 1 root wheel 0 0 "Jul 8 10:26:03 2004" "Jul 8 10:26:03 2004" "Jul 8 10:28:13 2004" "Jan 1 09:00:00 1970" 16384 0 0 /tmp/bar +.Ed +.Pp +This example produces output very similar to that from +.Ic find ... -ls +(except that +.Xr find 1 +displays the time in a different format, and +.Xr find 1 +sometimes adds one or more spaces after the comma in +.Dq Ar major , Ns Ar minor +for device nodes): +.Bd -literal -offset indent +\*[Gt] stat -f "%7i %6b %-11Sp %3l %-17Su %-17Sg %9Z %Sm %N%SY" /tmp/bar + 78852 0 -rw-r--r-- 1 root wheel 0 Jul 8 10:26:03 2004 /tmp/bar + +\*[Gt] find /tmp/bar -ls -exit + 78852 0 -rw-r--r-- 1 root wheel 0 Jul 8 2004 /tmp/bar +.Ed +.Pp +This example produces output very similar to that from +.Ic ls -lTd +(except that +.Xr ls 1 +adjusts the column spacing differently when listing multiple files, +and +.Xr ls 1 +adds at least one space after the comma in +.Dq Ar major , Ns Ar minor +for device nodes): +.Bd -literal -offset indent +\*[Gt] stat -f "%-11Sp %l %Su %Sg %Z %Sm %N%SY" /tmp/bar +-rw-r--r-- 1 root wheel 0 Jul 8 10:26:03 2004 /tmp/bar + +\*[Gt] ls -lTd /tmp/bar +-rw-r--r-- 1 root wheel 0 Jul 8 10:26:03 2004 /tmp/bar +.Ed +.Pp +Given a symbolic link +.Dq foo +that points from +.Pa /tmp/foo +to +.Pa / , +you would use +.Nm +as follows: +.Bd -literal -offset indent +\*[Gt] stat -F /tmp/foo +lrwxrwxrwx 1 jschauma cs 1 Apr 24 16:37:28 2002 /tmp/foo@ -\*[Gt] / + +\*[Gt] stat -LF /tmp/foo +drwxr-xr-x 16 root wheel 512 Apr 19 10:57:54 2002 /tmp/foo/ +.Ed +.Pp +To initialize some shell-variables, you could use the +.Fl s +flag as follows: +.Bd -literal -offset indent +\*[Gt] csh +% eval set `stat -s .cshrc` +% echo $st_size $st_mtime +1148 1015432481 + +\*[Gt] sh +$ eval $(stat -s .profile) +$ echo $st_size $st_mtime +1148 1015432481 +.Ed +.Pp +In order to get a list of the kind of files including files pointed to if the +file is a symbolic link, you could use the following format: +.Bd -literal -offset indent +$ stat -f "%N: %HT%SY" /tmp/* +/tmp/bar: Symbolic Link -\*[Gt] /tmp/foo +/tmp/output25568: Regular File +/tmp/blah: Directory +/tmp/foo: Symbolic Link -\*[Gt] / +.Ed +.Pp +In order to get a list of the devices, their types and the major and minor +device numbers, formatted with tabs and linebreaks, you could use the +following format: +.Bd -literal -offset indent +stat -f "Name: %N%n%tType: %HT%n%tMajor: %Hr%n%tMinor: %Lr%n%n" /dev/* +[...] +Name: /dev/wt8 + Type: Block Device + Major: 3 + Minor: 8 + +Name: /dev/zero + Type: Character Device + Major: 2 + Minor: 12 +.Ed +.Pp +In order to determine the permissions set on a file separately, you could use +the following format: +.Bd -literal -offset indent +\*[Gt] stat -f "%Sp -\*[Gt] owner=%SHp group=%SMp other=%SLp" . +drwxr-xr-x -\*[Gt] owner=rwx group=r-x other=r-x +.Ed +.Pp +In order to determine the three files that have been modified most recently, +you could use the following format: +.Bd -literal -offset indent +\*[Gt] stat -f "%m%t%Sm %N" /tmp/* | sort -rn | head -3 | cut -f2- +Apr 25 11:47:00 2002 /tmp/blah +Apr 25 10:36:34 2002 /tmp/bar +Apr 24 16:47:35 2002 /tmp/foo +.Ed +.Pp +User names, group names, and file names that contain spaces +or other special characters may be encoded in +.Xr vis 3 +style, using the +.Cm \&# +modifier: +.Bd -literal -offset indent +\*[Gt] ln -s 'target with spaces' 'link with spaces' +\*[Gt] stat -f "%#N%#SY" 'link with spaces' +link\eswith\esspaces -\*[Gt] target\eswith\esspaces +.Ed +.Sh SEE ALSO +.Xr basename 1 , +.Xr dirname 1 , +.Xr file 1 , +.Xr ls 1 , +.Xr lstat 2 , +.Xr readlink 2 , +.Xr stat 2 , +.Xr printf 3 , +.Xr strftime 3 +.Sh HISTORY +The +.Nm +utility appeared in +.Nx 1.6 . +.Sh AUTHORS +.An -nosplit +The +.Nm +utility was written by +.An Andrew Brown +.Aq atatat@NetBSD.org . +This man page was written by +.An Jan Schaumann +.Aq jschauma@NetBSD.org . diff --git a/buildrump.sh/src/usr.bin/stat/stat.c b/buildrump.sh/src/usr.bin/stat/stat.c new file mode 100644 index 00000000..b08a346c --- /dev/null +++ b/buildrump.sh/src/usr.bin/stat/stat.c @@ -0,0 +1,1151 @@ +/* $NetBSD: stat.c,v 1.38 2013/01/03 13:28:41 dsl Exp $ */ + +/* + * Copyright (c) 2002-2011 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Andrew Brown. + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +/* config checked libc, we need the prototype as well */ +#undef HAVE_DEVNAME +#endif + +#include <sys/cdefs.h> +#if !defined(lint) +__RCSID("$NetBSD: stat.c,v 1.38 2013/01/03 13:28:41 dsl Exp $"); +#endif + +#if ! HAVE_NBTOOL_CONFIG_H +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#define HAVE_STRUCT_STAT_ST_GEN 1 +#define HAVE_STRUCT_STAT_ST_BIRTHTIME 1 +#define HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC 1 +#define HAVE_STRUCT_STAT_ST_MTIMENSEC 1 +#define HAVE_DEVNAME 1 +#endif /* HAVE_NBTOOL_CONFIG_H */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <grp.h> +#include <limits.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <vis.h> + +#if HAVE_STRUCT_STAT_ST_FLAGS +#define DEF_F "%#Xf " +#define RAW_F "%f " +#define SHELL_F " st_flags=%f" +#else /* HAVE_STRUCT_STAT_ST_FLAGS */ +#define DEF_F +#define RAW_F +#define SHELL_F +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ + +#if HAVE_STRUCT_STAT_ST_BIRTHTIME +#define DEF_B "\"%SB\" " +#define RAW_B "%B " +#define SHELL_B "st_birthtime=%SB " +#else /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ +#define DEF_B +#define RAW_B +#define SHELL_B +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ + +#if HAVE_STRUCT_STAT_ST_ATIM +#define st_atimespec st_atim +#define st_ctimespec st_ctim +#define st_mtimespec st_mtim +#endif /* HAVE_STRUCT_STAT_ST_ATIM */ + +#define DEF_FORMAT \ + "%d %i %Sp %l %Su %Sg %r %z \"%Sa\" \"%Sm\" \"%Sc\" " DEF_B \ + "%k %b " DEF_F "%N" +#define RAW_FORMAT "%d %i %#p %l %u %g %r %z %a %m %c " RAW_B \ + "%k %b " RAW_F "%N" +#define LS_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%SY" +#define LSF_FORMAT "%Sp %l %Su %Sg %Z %Sm %N%T%SY" +#define SHELL_FORMAT \ + "st_dev=%d st_ino=%i st_mode=%#p st_nlink=%l " \ + "st_uid=%u st_gid=%g st_rdev=%r st_size=%z " \ + "st_atime=%Sa st_mtime=%Sm st_ctime=%Sc " SHELL_B \ + "st_blksize=%k st_blocks=%b" SHELL_F +#define LINUX_FORMAT \ + " File: \"%N\"%n" \ + " Size: %-11z FileType: %HT%n" \ + " Mode: (%04OLp/%.10Sp) Uid: (%5u/%8Su) Gid: (%5g/%8Sg)%n" \ + "Device: %Hd,%Ld Inode: %i Links: %l%n" \ + "Access: %Sa%n" \ + "Modify: %Sm%n" \ + "Change: %Sc" + +#define TIME_FORMAT "%b %e %T %Y" + +#define FLAG_POUND 0x01 +#define FLAG_SPACE 0x02 +#define FLAG_PLUS 0x04 +#define FLAG_ZERO 0x08 +#define FLAG_MINUS 0x10 + +/* + * These format characters must all be unique, except the magic one. + */ +#define FMT_MAGIC '%' +#define FMT_DOT '.' + +#define SIMPLE_NEWLINE 'n' +#define SIMPLE_TAB 't' +#define SIMPLE_PERCENT '%' +#define SIMPLE_NUMBER '@' + +#define FMT_POUND '#' +#define FMT_SPACE ' ' +#define FMT_PLUS '+' +#define FMT_ZERO '0' +#define FMT_MINUS '-' + +#define FMT_DECIMAL 'D' +#define FMT_OCTAL 'O' +#define FMT_UNSIGNED 'U' +#define FMT_HEX 'X' +#define FMT_FLOAT 'F' +#define FMT_STRING 'S' + +#define FMTF_DECIMAL 0x01 +#define FMTF_OCTAL 0x02 +#define FMTF_UNSIGNED 0x04 +#define FMTF_HEX 0x08 +#define FMTF_FLOAT 0x10 +#define FMTF_STRING 0x20 + +#define HIGH_PIECE 'H' +#define MIDDLE_PIECE 'M' +#define LOW_PIECE 'L' + +#define SHOW_realpath 'R' +#define SHOW_st_dev 'd' +#define SHOW_st_ino 'i' +#define SHOW_st_mode 'p' +#define SHOW_st_nlink 'l' +#define SHOW_st_uid 'u' +#define SHOW_st_gid 'g' +#define SHOW_st_rdev 'r' +#define SHOW_st_atime 'a' +#define SHOW_st_mtime 'm' +#define SHOW_st_ctime 'c' +#define SHOW_st_btime 'B' +#define SHOW_st_size 'z' +#define SHOW_st_blocks 'b' +#define SHOW_st_blksize 'k' +#define SHOW_st_flags 'f' +#define SHOW_st_gen 'v' +#define SHOW_symlink 'Y' +#define SHOW_filetype 'T' +#define SHOW_filename 'N' +#define SHOW_sizerdev 'Z' + +static void usage(const char *) __dead; +static void output(const struct stat *, const char *, + const char *, int, int, int); +static int format1(const struct stat *, /* stat info */ + const char *, /* the file name */ + const char *, int, /* the format string itself */ + char *, size_t, /* a place to put the output */ + int, int, int, int, /* the parsed format */ + int, int, int); + +static const char *timefmt; +static int linkfail; + +#define addchar(s, c, nl) \ + do { \ + (void)fputc((c), (s)); \ + (*nl) = ((c) == '\n'); \ + } while (0/*CONSTCOND*/) + +int +main(int argc, char *argv[]) +{ + struct stat st; + int ch, rc, errs, am_readlink; + int lsF, fmtchar, usestat, fn, nonl, quiet; + const char *statfmt, *options, *synopsis; + + am_readlink = 0; + lsF = 0; + fmtchar = '\0'; + usestat = 0; + nonl = 0; + quiet = 0; + linkfail = 0; + statfmt = NULL; + timefmt = NULL; + + setprogname(argv[0]); + + if (strcmp(getprogname(), "readlink") == 0) { + am_readlink = 1; + options = "fnqsv"; + synopsis = "[-fnqsv] [file ...]"; + statfmt = "%Y"; + fmtchar = 'f'; + quiet = 1; + } else { + options = "f:FlLnqrst:x"; + synopsis = "[-FlLnqrsx] [-f format] [-t timefmt] [file ...]"; + } + + while ((ch = getopt(argc, argv, options)) != -1) + switch (ch) { + case 'F': + lsF = 1; + break; + case 'L': + usestat = 1; + break; + case 'n': + nonl = 1; + break; + case 'q': + quiet = 1; + break; + case 'f': + if (am_readlink) { + statfmt = "%R"; + break; + } + statfmt = optarg; + /* FALLTHROUGH */ + case 'l': + case 'r': + case 's': + if (am_readlink) { + quiet = 1; + break; + } + /*FALLTHROUGH*/ + case 'x': + if (fmtchar != 0) + errx(1, "can't use format '%c' with '%c'", + fmtchar, ch); + fmtchar = ch; + break; + case 't': + timefmt = optarg; + break; + case 'v': + quiet = 0; + break; + default: + usage(synopsis); + } + + argc -= optind; + argv += optind; + fn = 1; + + if (fmtchar == '\0') { + if (lsF) + fmtchar = 'l'; + else { + fmtchar = 'f'; + statfmt = DEF_FORMAT; + } + } + + if (lsF && fmtchar != 'l') + errx(1, "can't use format '%c' with -F", fmtchar); + + switch (fmtchar) { + case 'f': + /* statfmt already set */ + break; + case 'l': + statfmt = lsF ? LSF_FORMAT : LS_FORMAT; + break; + case 'r': + statfmt = RAW_FORMAT; + break; + case 's': + statfmt = SHELL_FORMAT; + if (timefmt == NULL) + timefmt = "%s"; + break; + case 'x': + statfmt = LINUX_FORMAT; + if (timefmt == NULL) + timefmt = "%c"; + break; + default: + usage(synopsis); + /*NOTREACHED*/ + } + + if (timefmt == NULL) + timefmt = TIME_FORMAT; + + errs = 0; + do { + if (argc == 0) + rc = fstat(STDIN_FILENO, &st); + else if (usestat) { + /* + * Try stat() and if it fails, fall back to + * lstat() just in case we're examining a + * broken symlink. + */ + if ((rc = stat(argv[0], &st)) == -1 && + errno == ENOENT && + (rc = lstat(argv[0], &st)) == -1) + errno = ENOENT; + } + else + rc = lstat(argv[0], &st); + + if (rc == -1) { + errs = 1; + linkfail = 1; + if (!quiet) + warn("%s: %s", + argc == 0 ? "(stdin)" : argv[0], + usestat ? "stat" : "lstat"); + } + else + output(&st, argv[0], statfmt, fn, nonl, quiet); + + argv++; + argc--; + fn++; + } while (argc > 0); + + return (am_readlink ? linkfail : errs); +} + +static void +usage(const char *synopsis) +{ + + (void)fprintf(stderr, "usage: %s %s\n", getprogname(), synopsis); + exit(1); +} + +/* + * Parses a format string. + */ +static void +output(const struct stat *st, const char *file, + const char *statfmt, int fn, int nonl, int quiet) +{ + int flags, size, prec, ofmt, hilo, what; + /* + * buf size is enough for an item of length PATH_MAX, + * multiplied by 4 for vis encoding, plus 4 for symlink + * " -> " prefix, plus 1 for \0 terminator. + */ + char buf[PATH_MAX * 4 + 4 + 1]; + const char *subfmt; + int nl, t, i; + + nl = 1; + while (*statfmt != '\0') { + + /* + * Non-format characters go straight out. + */ + if (*statfmt != FMT_MAGIC) { + addchar(stdout, *statfmt, &nl); + statfmt++; + continue; + } + + /* + * The current format "substring" starts here, + * and then we skip the magic. + */ + subfmt = statfmt; + statfmt++; + + /* + * Some simple one-character "formats". + */ + switch (*statfmt) { + case SIMPLE_NEWLINE: + addchar(stdout, '\n', &nl); + statfmt++; + continue; + case SIMPLE_TAB: + addchar(stdout, '\t', &nl); + statfmt++; + continue; + case SIMPLE_PERCENT: + addchar(stdout, '%', &nl); + statfmt++; + continue; + case SIMPLE_NUMBER: { + char num[12], *p; + + snprintf(num, sizeof(num), "%d", fn); + for (p = &num[0]; *p; p++) + addchar(stdout, *p, &nl); + statfmt++; + continue; + } + } + + /* + * This must be an actual format string. Format strings are + * similar to printf(3) formats up to a point, and are of + * the form: + * + * % required start of format + * [-# +0] opt. format characters + * size opt. field width + * . opt. decimal separator, followed by + * prec opt. precision + * fmt opt. output specifier (string, numeric, etc.) + * sub opt. sub field specifier (high, middle, low) + * datum required field specifier (size, mode, etc) + * + * Only the % and the datum selector are required. All data + * have reasonable default output forms. The "sub" specifier + * only applies to certain data (mode, dev, rdev, filetype). + * The symlink output defaults to STRING, yet will only emit + * the leading " -> " if STRING is explicitly specified. The + * sizerdev datum will generate rdev output for character or + * block devices, and size output for all others. + * For STRING output, the # format requests vis encoding. + */ + flags = 0; + do { + if (*statfmt == FMT_POUND) + flags |= FLAG_POUND; + else if (*statfmt == FMT_SPACE) + flags |= FLAG_SPACE; + else if (*statfmt == FMT_PLUS) + flags |= FLAG_PLUS; + else if (*statfmt == FMT_ZERO) + flags |= FLAG_ZERO; + else if (*statfmt == FMT_MINUS) + flags |= FLAG_MINUS; + else + break; + statfmt++; + } while (1/*CONSTCOND*/); + + size = -1; + if (isdigit((unsigned)*statfmt)) { + size = 0; + while (isdigit((unsigned)*statfmt)) { + size = (size * 10) + (*statfmt - '0'); + statfmt++; + if (size < 0) + goto badfmt; + } + } + + prec = -1; + if (*statfmt == FMT_DOT) { + statfmt++; + + prec = 0; + while (isdigit((unsigned)*statfmt)) { + prec = (prec * 10) + (*statfmt - '0'); + statfmt++; + if (prec < 0) + goto badfmt; + } + } + +#define fmtcase(x, y) case (y): (x) = (y); statfmt++; break +#define fmtcasef(x, y, z) case (y): (x) = (z); statfmt++; break + switch (*statfmt) { + fmtcasef(ofmt, FMT_DECIMAL, FMTF_DECIMAL); + fmtcasef(ofmt, FMT_OCTAL, FMTF_OCTAL); + fmtcasef(ofmt, FMT_UNSIGNED, FMTF_UNSIGNED); + fmtcasef(ofmt, FMT_HEX, FMTF_HEX); + fmtcasef(ofmt, FMT_FLOAT, FMTF_FLOAT); + fmtcasef(ofmt, FMT_STRING, FMTF_STRING); + default: + ofmt = 0; + break; + } + + switch (*statfmt) { + fmtcase(hilo, HIGH_PIECE); + fmtcase(hilo, MIDDLE_PIECE); + fmtcase(hilo, LOW_PIECE); + default: + hilo = 0; + break; + } + + switch (*statfmt) { + fmtcase(what, SHOW_realpath); + fmtcase(what, SHOW_st_dev); + fmtcase(what, SHOW_st_ino); + fmtcase(what, SHOW_st_mode); + fmtcase(what, SHOW_st_nlink); + fmtcase(what, SHOW_st_uid); + fmtcase(what, SHOW_st_gid); + fmtcase(what, SHOW_st_rdev); + fmtcase(what, SHOW_st_atime); + fmtcase(what, SHOW_st_mtime); + fmtcase(what, SHOW_st_ctime); + fmtcase(what, SHOW_st_btime); + fmtcase(what, SHOW_st_size); + fmtcase(what, SHOW_st_blocks); + fmtcase(what, SHOW_st_blksize); + fmtcase(what, SHOW_st_flags); + fmtcase(what, SHOW_st_gen); + fmtcase(what, SHOW_symlink); + fmtcase(what, SHOW_filetype); + fmtcase(what, SHOW_filename); + fmtcase(what, SHOW_sizerdev); + default: + goto badfmt; + } +#undef fmtcasef +#undef fmtcase + + t = format1(st, + file, + subfmt, statfmt - subfmt, + buf, sizeof(buf), + flags, size, prec, ofmt, hilo, what, quiet); + + for (i = 0; i < t && i < (int)(sizeof(buf) - 1); i++) + addchar(stdout, buf[i], &nl); + + continue; + + badfmt: + errx(1, "%.*s: bad format", + (int)(statfmt - subfmt + 1), subfmt); + } + + if (!nl && !nonl) + (void)fputc('\n', stdout); + (void)fflush(stdout); +} + +/* + * Arranges output according to a single parsed format substring. + */ +static int +format1(const struct stat *st, + const char *file, + const char *fmt, int flen, + char *buf, size_t blen, + int flags, int size, int prec, int ofmt, + int hilo, int what, int quiet) +{ + u_int64_t data; + char *stmp, lfmt[24], tmp[20]; + const char *sdata; + char smode[12], sid[12], path[PATH_MAX + 4], visbuf[PATH_MAX * 4 + 4]; + struct passwd *pw; + struct group *gr; + struct tm *tm; + time_t secs; + long nsecs; + int l; + int formats; /* bitmap of allowed formats for this datum */ + int small; /* true if datum is a small integer */ + int gottime; /* true if secs and nsecs are valid */ + int shift; /* powers of 2 to scale numbers before printing */ + size_t prefixlen; /* length of constant prefix for string data */ + + formats = 0; + small = 0; + gottime = 0; + secs = 0; + nsecs = 0; + shift = 0; + prefixlen = 0; + + /* + * First, pick out the data and tweak it based on hilo or + * specified output format (symlink output only). + */ + switch (what) { + case SHOW_st_dev: + case SHOW_st_rdev: + small = (sizeof(st->st_dev) == 4); + data = (what == SHOW_st_dev) ? st->st_dev : st->st_rdev; +#if HAVE_DEVNAME + sdata = (what == SHOW_st_dev) ? + devname(st->st_dev, S_IFBLK) : + devname(st->st_rdev, + S_ISCHR(st->st_mode) ? S_IFCHR : + S_ISBLK(st->st_mode) ? S_IFBLK : + 0U); + if (sdata == NULL) + sdata = "???"; +#endif /* HAVE_DEVNAME */ + if (hilo == HIGH_PIECE) { + data = major(data); + hilo = 0; + } + else if (hilo == LOW_PIECE) { + data = minor((unsigned)data); + hilo = 0; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | +#if HAVE_DEVNAME + FMTF_STRING; +#else /* HAVE_DEVNAME */ + 0; +#endif /* HAVE_DEVNAME */ + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_ino: + small = (sizeof(st->st_ino) == 4); + data = st->st_ino; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_mode: + small = (sizeof(st->st_mode) == 4); + data = st->st_mode; + strmode(st->st_mode, smode); + stmp = smode; + l = strlen(stmp); + if (stmp[l - 1] == ' ') + stmp[--l] = '\0'; + if (hilo == HIGH_PIECE) { + data >>= 12; + stmp += 1; + stmp[3] = '\0'; + hilo = 0; + } + else if (hilo == MIDDLE_PIECE) { + data = (data >> 9) & 07; + stmp += 4; + stmp[3] = '\0'; + hilo = 0; + } + else if (hilo == LOW_PIECE) { + data &= 0777; + stmp += 7; + stmp[3] = '\0'; + hilo = 0; + } + sdata = stmp; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_OCTAL; + break; + case SHOW_st_nlink: + small = (sizeof(st->st_dev) == 4); + data = st->st_nlink; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_uid: + small = (sizeof(st->st_uid) == 4); + data = st->st_uid; + if ((pw = getpwuid(st->st_uid)) != NULL) + sdata = pw->pw_name; + else { + snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_uid); + sdata = sid; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_gid: + small = (sizeof(st->st_gid) == 4); + data = st->st_gid; + if ((gr = getgrgid(st->st_gid)) != NULL) + sdata = gr->gr_name; + else { + snprintf(sid, sizeof(sid), "(%ld)", (long)st->st_gid); + sdata = sid; + } + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_atime: + gottime = 1; + secs = st->st_atime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + nsecs = st->st_atimensec; +#endif + /* FALLTHROUGH */ + case SHOW_st_mtime: + if (!gottime) { + gottime = 1; + secs = st->st_mtime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + nsecs = st->st_mtimensec; +#endif + } + /* FALLTHROUGH */ + case SHOW_st_ctime: + if (!gottime) { + gottime = 1; + secs = st->st_ctime; +#if HAVE_STRUCT_STAT_ST_MTIMENSEC + nsecs = st->st_ctimensec; +#endif + } + /* FALLTHROUGH */ +#if HAVE_STRUCT_STAT_ST_BIRTHTIME + case SHOW_st_btime: + if (!gottime) { + gottime = 1; + secs = st->st_birthtime; +#if HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC + nsecs = st->st_birthtimensec; +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIMENSEC */ + } +#endif /* HAVE_STRUCT_STAT_ST_BIRTHTIME */ + small = (sizeof(secs) == 4); + data = secs; + tm = localtime(&secs); + if (tm == NULL) { + secs = 0; + tm = localtime(&secs); + } + (void)strftime(path, sizeof(path), timefmt, tm); + sdata = path; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX | + FMTF_FLOAT | FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_DECIMAL; + break; + case SHOW_st_size: + small = (sizeof(st->st_size) == 4); + data = st->st_size; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + switch (hilo) { + case HIGH_PIECE: + shift = 30; /* gigabytes */ + hilo = 0; + break; + case MIDDLE_PIECE: + shift = 20; /* megabytes */ + hilo = 0; + break; + case LOW_PIECE: + shift = 10; /* kilobytes */ + hilo = 0; + break; + } + break; + case SHOW_st_blocks: + small = (sizeof(st->st_blocks) == 4); + data = st->st_blocks; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; + case SHOW_st_blksize: + small = (sizeof(st->st_blksize) == 4); + data = st->st_blksize; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#if HAVE_STRUCT_STAT_ST_FLAGS + case SHOW_st_flags: + small = (sizeof(st->st_flags) == 4); + data = st->st_flags; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#endif /* HAVE_STRUCT_STAT_ST_FLAGS */ +#if HAVE_STRUCT_STAT_ST_GEN + case SHOW_st_gen: + small = (sizeof(st->st_gen) == 4); + data = st->st_gen; + sdata = NULL; + formats = FMTF_DECIMAL | FMTF_OCTAL | FMTF_UNSIGNED | FMTF_HEX; + if (ofmt == 0) + ofmt = FMTF_UNSIGNED; + break; +#endif /* HAVE_STRUCT_STAT_ST_GEN */ + case SHOW_realpath: + small = 0; + data = 0; + if (file == NULL) { + (void)strlcpy(path, "(stdin)", sizeof(path)); + sdata = path; + } else { + snprintf(path, sizeof(path), " -> "); + if (realpath(file, path + 4) == NULL) { + if (!quiet) + warn("realpath `%s'", file); + linkfail = 1; + l = 0; + path[0] = '\0'; + } + sdata = path + (ofmt == FMTF_STRING ? 0 : 4); + prefixlen = (ofmt == FMTF_STRING ? 4 : 0); + } + + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_symlink: + small = 0; + data = 0; + if (S_ISLNK(st->st_mode)) { + snprintf(path, sizeof(path), " -> "); + l = readlink(file, path + 4, sizeof(path) - 4 - 1); + if (l == -1) { + if (!quiet) + warn("readlink `%s'", file); + linkfail = 1; + l = 0; + path[0] = '\0'; + } + path[l + 4] = '\0'; + sdata = path + (ofmt == FMTF_STRING ? 0 : 4); + prefixlen = (ofmt == FMTF_STRING ? 4 : 0); + } + else { + linkfail = 1; + sdata = ""; + } + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_filetype: + small = 0; + data = 0; + sdata = ""; + if (hilo == 0 || hilo == LOW_PIECE) { + switch (st->st_mode & S_IFMT) { + case S_IFIFO: sdata = "|"; break; + case S_IFDIR: sdata = "/"; break; + case S_IFREG: + if (st->st_mode & + (S_IXUSR | S_IXGRP | S_IXOTH)) + sdata = "*"; + break; + case S_IFLNK: sdata = "@"; break; +#ifdef S_IFSOCK + case S_IFSOCK: sdata = "="; break; +#endif +#ifdef S_IFWHT + case S_IFWHT: sdata = "%"; break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: sdata = ">"; break; +#endif /* S_IFDOOR */ + } + hilo = 0; + } + else if (hilo == HIGH_PIECE) { + switch (st->st_mode & S_IFMT) { + case S_IFIFO: sdata = "Fifo File"; break; + case S_IFCHR: sdata = "Character Device"; break; + case S_IFDIR: sdata = "Directory"; break; + case S_IFBLK: sdata = "Block Device"; break; + case S_IFREG: sdata = "Regular File"; break; + case S_IFLNK: sdata = "Symbolic Link"; break; +#ifdef S_IFSOCK + case S_IFSOCK: sdata = "Socket"; break; +#endif +#ifdef S_IFWHT + case S_IFWHT: sdata = "Whiteout File"; break; +#endif /* S_IFWHT */ +#ifdef S_IFDOOR + case S_IFDOOR: sdata = "Door"; break; +#endif /* S_IFDOOR */ + default: sdata = "???"; break; + } + hilo = 0; + } + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_filename: + small = 0; + data = 0; + if (file == NULL) { + (void)strlcpy(path, "(stdin)", sizeof(path)); + if (hilo == HIGH_PIECE || hilo == LOW_PIECE) + hilo = 0; + } + else if (hilo == 0) + (void)strlcpy(path, file, sizeof(path)); + else { + char *s; + (void)strlcpy(path, file, sizeof(path)); + s = strrchr(path, '/'); + if (s != NULL) { + /* trim off trailing /'s */ + while (s != path && + s[0] == '/' && s[1] == '\0') + *s-- = '\0'; + s = strrchr(path, '/'); + } + if (hilo == HIGH_PIECE) { + if (s == NULL) + (void)strlcpy(path, ".", sizeof(path)); + else { + while (s != path && s[0] == '/') + *s-- = '\0'; + } + hilo = 0; + } + else if (hilo == LOW_PIECE) { + if (s != NULL && s[1] != '\0') + (void)strlcpy(path, s + 1, + sizeof(path)); + hilo = 0; + } + } + sdata = path; + formats = FMTF_STRING; + if (ofmt == 0) + ofmt = FMTF_STRING; + break; + case SHOW_sizerdev: + if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + char majdev[20], mindev[20]; + int l1, l2; + + l1 = format1(st, + file, + fmt, flen, + majdev, sizeof(majdev), + flags, size, prec, + ofmt, HIGH_PIECE, SHOW_st_rdev, quiet); + l2 = format1(st, + file, + fmt, flen, + mindev, sizeof(mindev), + flags, size, prec, + ofmt, LOW_PIECE, SHOW_st_rdev, quiet); + return (snprintf(buf, blen, "%.*s,%.*s", + l1, majdev, l2, mindev)); + } + else { + return (format1(st, + file, + fmt, flen, + buf, blen, + flags, size, prec, + ofmt, 0, SHOW_st_size, quiet)); + } + /*NOTREACHED*/ + default: + errx(1, "%.*s: bad format", (int)flen, fmt); + } + + /* + * If a subdatum was specified but not supported, or an output + * format was selected that is not supported, that's an error. + */ + if (hilo != 0 || (ofmt & formats) == 0) + errx(1, "%.*s: bad format", (int)flen, fmt); + + /* + * FLAG_POUND with FMTF_STRING means use vis(3) encoding. + * First prefixlen chars are not encoded. + */ + if ((flags & FLAG_POUND) != 0 && ofmt == FMTF_STRING) { + flags &= !FLAG_POUND; + strncpy(visbuf, sdata, prefixlen); + strnvis(visbuf + prefixlen, sizeof(visbuf) - prefixlen, + sdata + prefixlen, VIS_WHITE | VIS_OCTAL | VIS_CSTYLE); + sdata = visbuf; + } + + /* + * Assemble the format string for passing to printf(3). + */ + lfmt[0] = '\0'; + (void)strcat(lfmt, "%"); + if (flags & FLAG_POUND) + (void)strcat(lfmt, "#"); + if (flags & FLAG_SPACE) + (void)strcat(lfmt, " "); + if (flags & FLAG_PLUS) + (void)strcat(lfmt, "+"); + if (flags & FLAG_MINUS) + (void)strcat(lfmt, "-"); + if (flags & FLAG_ZERO) + (void)strcat(lfmt, "0"); + + /* + * Only the timespecs support the FLOAT output format, and that + * requires work that differs from the other formats. + */ + if (ofmt == FMTF_FLOAT) { + /* + * Nothing after the decimal point, so just print seconds. + */ + if (prec == 0) { + if (size != -1) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + (void)strcat(lfmt, "lld"); + return (snprintf(buf, blen, lfmt, + (long long)secs)); + } + + /* + * Unspecified precision gets all the precision we have: + * 9 digits. + */ + if (prec == -1) + prec = 9; + + /* + * Adjust the size for the decimal point and the digits + * that will follow. + */ + size -= prec + 1; + + /* + * Any leftover size that's legitimate will be used. + */ + if (size > 0) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + /* Seconds: time_t cast to long long. */ + (void)strcat(lfmt, "lld"); + + /* + * The stuff after the decimal point always needs zero + * filling. + */ + (void)strcat(lfmt, ".%0"); + + /* + * We can "print" at most nine digits of precision. The + * rest we will pad on at the end. + * + * Nanoseconds: long. + */ + (void)snprintf(tmp, sizeof(tmp), "%dld", prec > 9 ? 9 : prec); + (void)strcat(lfmt, tmp); + + /* + * For precision of less that nine digits, trim off the + * less significant figures. + */ + for (; prec < 9; prec++) + nsecs /= 10; + + /* + * Use the format, and then tack on any zeroes that + * might be required to make up the requested precision. + */ + l = snprintf(buf, blen, lfmt, (long long)secs, nsecs); + for (; prec > 9 && l < (int)blen; prec--, l++) + (void)strcat(buf, "0"); + return (l); + } + + /* + * Add on size and precision, if specified, to the format. + */ + if (size != -1) { + (void)snprintf(tmp, sizeof(tmp), "%d", size); + (void)strcat(lfmt, tmp); + } + if (prec != -1) { + (void)snprintf(tmp, sizeof(tmp), ".%d", prec); + (void)strcat(lfmt, tmp); + } + + /* + * String output uses the temporary sdata. + */ + if (ofmt == FMTF_STRING) { + if (sdata == NULL) + errx(1, "%.*s: bad format", (int)flen, fmt); + (void)strcat(lfmt, "s"); + return (snprintf(buf, blen, lfmt, sdata)); + } + + /* + * Ensure that sign extension does not cause bad looking output + * for some forms. + */ + if (small && ofmt != FMTF_DECIMAL) + data = (u_int32_t)data; + + /* + * The four "numeric" output forms. + */ + (void)strcat(lfmt, "ll"); + switch (ofmt) { + case FMTF_DECIMAL: (void)strcat(lfmt, "d"); break; + case FMTF_OCTAL: (void)strcat(lfmt, "o"); break; + case FMTF_UNSIGNED: (void)strcat(lfmt, "u"); break; + case FMTF_HEX: (void)strcat(lfmt, "x"); break; + } + + /* + * shift and round to nearest for kilobytes, megabytes, + * gigabytes. + */ + if (shift > 0) { + data >>= (shift - 1); + data++; + data >>= 1; + } + + return (snprintf(buf, blen, lfmt, data)); +} diff --git a/buildrump.sh/src/usr.bin/tsort/Makefile b/buildrump.sh/src/usr.bin/tsort/Makefile new file mode 100644 index 00000000..06e78c7b --- /dev/null +++ b/buildrump.sh/src/usr.bin/tsort/Makefile @@ -0,0 +1,6 @@ +# $NetBSD: Makefile,v 1.7 2009/04/14 22:15:27 lukem Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/9/93 + +PROG= tsort + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/tsort/tsort.1 b/buildrump.sh/src/usr.bin/tsort/tsort.1 new file mode 100644 index 00000000..061824b7 --- /dev/null +++ b/buildrump.sh/src/usr.bin/tsort/tsort.1 @@ -0,0 +1,87 @@ +.\" $NetBSD: tsort.1,v 1.10 2003/08/07 11:16:50 agc Exp $ +.\" +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This manual is derived from one contributed to Berkeley by +.\" Michael Rendell of Memorial University of Newfoundland. +.\" +.\" 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. +.\" +.\" @(#)tsort.1 8.3 (Berkeley) 4/1/94 +.\" +.Dd April 1, 1994 +.Dt TSORT 1 +.Os +.Sh NAME +.Nm tsort +.Nd topological sort of a directed graph +.Sh SYNOPSIS +.Nm +.Op Fl l +.Op Fl q +.Op Ar file +.Sh DESCRIPTION +.Nm +takes a list of pairs of node names representing directed arcs in +a graph and prints the nodes in topological order on standard output. +Input is taken from the named +.Ar file , +or from standard input if no file +is given. +.Pp +Node names in the input are separated by white space and there must +be an even number of node names. +.Pp +Presence of a node in a graph can be represented by an arc from the node +to itself. +This is useful when a node is not connected to any other nodes. +.Pp +If the graph contains a cycle (and therefore cannot be properly sorted), +one of the arcs in the cycle is ignored and the sort continues. +Cycles are reported on standard error. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl l +Search for and display the longest cycle. +Can take a very long time. +.It Fl q +Do not display informational messages about cycles. +This is primarily +intended for building libraries, where optimal ordering is not critical, +and cycles occur often. +.El +.Sh SEE ALSO +.Xr ar 1 +.Sh HISTORY +A +.Nm +command appeared in +.At v7 . +This +.Nm +command and manual page are derived from sources contributed to Berkeley by +Michael Rendell of Memorial University of Newfoundland. diff --git a/buildrump.sh/src/usr.bin/tsort/tsort.c b/buildrump.sh/src/usr.bin/tsort/tsort.c new file mode 100644 index 00000000..3e8d5692 --- /dev/null +++ b/buildrump.sh/src/usr.bin/tsort/tsort.c @@ -0,0 +1,433 @@ +/* $NetBSD: tsort.c,v 1.23 2011/09/06 18:34:37 joerg Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Rendell of Memorial University of Newfoundland. + * + * 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include <sys/cdefs.h> +#if !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1989, 1993, 1994\ + The Regents of the University of California. All rights reserved."); +#if 0 +static char sccsid[] = "@(#)tsort.c 8.3 (Berkeley) 5/4/95"; +#endif +__RCSID("$NetBSD: tsort.c,v 1.23 2011/09/06 18:34:37 joerg Exp $"); +#endif /* not lint */ + +#include <sys/types.h> +#include <ctype.h> +#include <db.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* + * Topological sort. Input is a list of pairs of strings separated by + * white space (spaces, tabs, and/or newlines); strings are written to + * standard output in sorted order, one per line. + * + * usage: + * tsort [-l] [inputfile] + * If no input file is specified, standard input is read. + * + * Should be compatible with AT&T tsort HOWEVER the output is not identical + * (i.e. for most graphs there is more than one sorted order, and this tsort + * usually generates a different one then the AT&T tsort). Also, cycle + * reporting seems to be more accurate in this version (the AT&T tsort + * sometimes says a node is in a cycle when it isn't). + * + * Michael Rendell, michael@stretch.cs.mun.ca - Feb 26, '90 + */ +#define HASHSIZE 53 /* doesn't need to be big */ +#define NF_MARK 0x1 /* marker for cycle detection */ +#define NF_ACYCLIC 0x2 /* this node is cycle free */ +#define NF_NODEST 0x4 /* Unreachable */ + +typedef struct node_str NODE; + +struct node_str { + NODE **n_prevp; /* pointer to previous node's n_next */ + NODE *n_next; /* next node in graph */ + NODE **n_arcs; /* array of arcs to other nodes */ + int n_narcs; /* number of arcs in n_arcs[] */ + int n_arcsize; /* size of n_arcs[] array */ + int n_refcnt; /* # of arcs pointing to this node */ + int n_flags; /* NF_* */ + char n_name[1]; /* name of this node */ +}; + +typedef struct _buf { + char *b_buf; + int b_bsize; +} BUF; + +static DB *db; +static NODE *graph, **cycle_buf, **longest_cycle; +static int debug, longest, quiet; + +static void add_arc(char *, char *); +static void clear_cycle(void); +static int find_cycle(NODE *, NODE *, int, int); +static NODE *get_node(char *); +static void *grow_buf(void *, int); +static void remove_node(NODE *); +static void tsort(void); +__dead static void usage(void); + +int +main(int argc, char *argv[]) +{ + BUF *b; + int c, n; + FILE *fp; + int bsize, ch, nused; + BUF bufs[2]; + + setprogname(argv[0]); + + fp = NULL; + while ((ch = getopt(argc, argv, "dlq")) != -1) + switch (ch) { + case 'd': + debug = 1; + break; + case 'l': + longest = 1; + break; + case 'q': + quiet = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + switch (argc) { + case 0: + fp = stdin; + break; + case 1: + if ((fp = fopen(*argv, "r")) == NULL) + err(1, "%s", *argv); + break; + default: + usage(); + } + + for (b = bufs, n = 2; --n >= 0; b++) + b->b_buf = grow_buf(NULL, b->b_bsize = 1024); + + /* parse input and build the graph */ + for (n = 0, c = getc(fp);;) { + while (c != EOF && isspace(c)) + c = getc(fp); + if (c == EOF) + break; + + nused = 0; + b = &bufs[n]; + bsize = b->b_bsize; + do { + b->b_buf[nused++] = c; + if (nused == bsize) + b->b_buf = grow_buf(b->b_buf, bsize *= 2); + c = getc(fp); + } while (c != EOF && !isspace(c)); + + b->b_buf[nused] = '\0'; + b->b_bsize = bsize; + if (n) + add_arc(bufs[0].b_buf, bufs[1].b_buf); + n = !n; + } + (void)fclose(fp); + if (n) + errx(1, "odd data count"); + + /* do the sort */ + tsort(); + return(0); +} + +/* double the size of oldbuf and return a pointer to the new buffer. */ +static void * +grow_buf(void *bp, int size) +{ + void *n; + + if ((n = realloc(bp, (u_int)size)) == NULL) + err(1, "realloc"); + bp = n; + return (bp); +} + +/* + * add an arc from node s1 to node s2 in the graph. If s1 or s2 are not in + * the graph, then add them. + */ +static void +add_arc(char *s1, char *s2) +{ + NODE *n1; + NODE *n2; + int bsize, i; + + n1 = get_node(s1); + + if (!strcmp(s1, s2)) + return; + + n2 = get_node(s2); + + /* + * Check if this arc is already here. + */ + for (i = 0; i < n1->n_narcs; i++) + if (n1->n_arcs[i] == n2) + return; + /* + * Add it. + */ + if (n1->n_narcs == n1->n_arcsize) { + if (!n1->n_arcsize) + n1->n_arcsize = 10; + bsize = n1->n_arcsize * sizeof(*n1->n_arcs) * 2; + n1->n_arcs = grow_buf(n1->n_arcs, bsize); + n1->n_arcsize = bsize / sizeof(*n1->n_arcs); + } + n1->n_arcs[n1->n_narcs++] = n2; + ++n2->n_refcnt; +} + +/* Find a node in the graph (insert if not found) and return a pointer to it. */ +static NODE * +get_node(char *name) +{ + DBT data, key; + NODE *n; + + if (db == NULL && + (db = dbopen(NULL, O_RDWR, 0, DB_HASH, NULL)) == NULL) + err(1, "db: %s", name); + + key.data = name; + key.size = strlen(name) + 1; + + switch ((*db->get)(db, &key, &data, 0)) { + case 0: + (void)memmove(&n, data.data, sizeof(n)); + return (n); + case 1: + break; + default: + case -1: + err(1, "db: %s", name); + } + + if ((n = malloc(sizeof(NODE) + key.size)) == NULL) + err(1, "malloc"); + + n->n_narcs = 0; + n->n_arcsize = 0; + n->n_arcs = NULL; + n->n_refcnt = 0; + n->n_flags = 0; + (void)memmove(n->n_name, name, key.size); + + /* Add to linked list. */ + if ((n->n_next = graph) != NULL) + graph->n_prevp = &n->n_next; + n->n_prevp = &graph; + graph = n; + + /* Add to hash table. */ + data.data = &n; + data.size = sizeof(n); + if ((*db->put)(db, &key, &data, 0)) + err(1, "db: %s", name); + return (n); +} + + +/* + * Clear the NODEST flag from all nodes. + */ +static void +clear_cycle(void) +{ + NODE *n; + + for (n = graph; n != NULL; n = n->n_next) + n->n_flags &= ~NF_NODEST; +} + +/* do topological sort on graph */ +static void +tsort(void) +{ + NODE *n, *next; + int cnt, i; + + while (graph != NULL) { + /* + * Keep getting rid of simple cases until there are none left, + * if there are any nodes still in the graph, then there is + * a cycle in it. + */ + do { + for (cnt = 0, n = graph; n != NULL; n = next) { + next = n->n_next; + if (n->n_refcnt == 0) { + remove_node(n); + ++cnt; + } + } + } while (graph != NULL && cnt); + + if (graph == NULL) + break; + + if (!cycle_buf) { + /* + * Allocate space for two cycle logs - one to be used + * as scratch space, the other to save the longest + * cycle. + */ + for (cnt = 0, n = graph; n != NULL; n = n->n_next) + ++cnt; + cycle_buf = malloc((u_int)sizeof(NODE *) * cnt); + longest_cycle = malloc((u_int)sizeof(NODE *) * cnt); + if (cycle_buf == NULL || longest_cycle == NULL) + err(1, "malloc"); + } + for (n = graph; n != NULL; n = n->n_next) { + if (!(n->n_flags & NF_ACYCLIC)) { + if ((cnt = find_cycle(n, n, 0, 0)) != 0) { + if (!quiet) { + warnx("cycle in data"); + for (i = 0; i < cnt; i++) + warnx("%s", + longest_cycle[i]->n_name); + } + remove_node(n); + clear_cycle(); + break; + } else { + /* to avoid further checks */ + n->n_flags |= NF_ACYCLIC; + clear_cycle(); + } + } + } + if (n == NULL) + errx(1, "internal error -- could not find cycle"); + } +} + +/* print node and remove from graph (does not actually free node) */ +static void +remove_node(NODE *n) +{ + NODE **np; + int i; + + (void)printf("%s\n", n->n_name); + for (np = n->n_arcs, i = n->n_narcs; --i >= 0; np++) + --(*np)->n_refcnt; + n->n_narcs = 0; + *n->n_prevp = n->n_next; + if (n->n_next) + n->n_next->n_prevp = n->n_prevp; +} + + +/* look for the longest? cycle from node from to node to. */ +static int +find_cycle(NODE *from, NODE *to, int longest_len, int depth) +{ + NODE **np; + int i, len; + + /* + * avoid infinite loops and ignore portions of the graph known + * to be acyclic + */ + if (from->n_flags & (NF_NODEST|NF_MARK|NF_ACYCLIC)) + return (0); + from->n_flags |= NF_MARK; + + for (np = from->n_arcs, i = from->n_narcs; --i >= 0; np++) { + cycle_buf[depth] = *np; + if (*np == to) { + if (depth + 1 > longest_len) { + longest_len = depth + 1; + (void)memcpy(longest_cycle, cycle_buf, + longest_len * sizeof(NODE *)); + } + } else { + if ((*np)->n_flags & (NF_MARK|NF_ACYCLIC|NF_NODEST)) + continue; + len = find_cycle(*np, to, longest_len, depth + 1); + + if (debug) + (void)printf("%*s %s->%s %d\n", depth, "", + from->n_name, to->n_name, len); + + if (len == 0) + (*np)->n_flags |= NF_NODEST; + + if (len > longest_len) + longest_len = len; + + if (len > 0 && !longest) + break; + } + } + from->n_flags &= ~NF_MARK; + return (longest_len); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: tsort [-lq] [file]\n"); + exit(1); +} diff --git a/buildrump.sh/src/usr.bin/xinstall/Makefile b/buildrump.sh/src/usr.bin/xinstall/Makefile new file mode 100644 index 00000000..c1862e2a --- /dev/null +++ b/buildrump.sh/src/usr.bin/xinstall/Makefile @@ -0,0 +1,23 @@ +# $NetBSD: Makefile,v 1.22 2011/08/17 14:00:30 christos Exp $ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +.include <bsd.own.mk> + +PROG= xinstall +SRCS= xinstall.c getid.c +MAN= install.1 + +.PATH: ${NETBSDSRCDIR}/usr.sbin/mtree +CPPFLAGS+= -I${NETBSDSRCDIR}/usr.sbin/mtree + +.if (${HOSTPROG:U} == "") +DPADD+= ${LIBUTIL} +LDADD+= -lutil +.endif + +COPTS.xinstall.c += -Wno-format-nonliteral + + +PROGNAME=install + +.include <bsd.prog.mk> diff --git a/buildrump.sh/src/usr.bin/xinstall/install.1 b/buildrump.sh/src/usr.bin/xinstall/install.1 new file mode 100644 index 00000000..79677595 --- /dev/null +++ b/buildrump.sh/src/usr.bin/xinstall/install.1 @@ -0,0 +1,338 @@ +.\" $NetBSD: install.1,v 1.47 2012/04/08 22:00:40 wiz Exp $ +.\" +.\" Copyright (c) 1987, 1990, 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. +.\" +.\" @(#)install.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd May 1, 2009 +.Dt INSTALL 1 +.Os +.Sh NAME +.Nm install +.Nd install binaries +.Sh SYNOPSIS +.Nm +.Op Fl bcprsU +.Op Fl a Ar command +.Op Fl B Ar suffix +.Op Fl D Ar destdir +.Op Fl f Ar flags +.Op Fl g Ar group +.Op Fl h Ar hash +.Op Fl l Ar linkflags +.Op Fl M Ar metalog +.Op Fl m Ar mode +.Op Fl N Ar dbdir +.Op Fl o Ar owner +.Op Fl S Ar stripflag +.Op Fl T Ar tags +.Ar file1 file2 +.Nm +.Op Fl bcprsU +.Op Fl a Ar command +.Op Fl B Ar suffix +.Op Fl D Ar destdir +.Op Fl f Ar flags +.Op Fl g Ar group +.Op Fl h Ar hash +.Op Fl l Ar linkflags +.Op Fl M Ar metalog +.Op Fl m Ar mode +.Op Fl N Ar dbdir +.Op Fl o Ar owner +.Op Fl S Ar stripflag +.Op Fl T Ar tags +.Ar file1 ...\& +.Ar fileN directory +.Nm +.Fl d +.Op Fl pU +.Op Fl a Ar command +.Op Fl D Ar destdir +.Op Fl g Ar group +.Op Fl M Ar metalog +.Op Fl m Ar mode +.Op Fl N Ar dbdir +.Op Fl o Ar owner +.Op Fl T Ar tags +.Ar directory ...\& +.Sh DESCRIPTION +The file(s) are copied +(or linked if the +.Fl l +option is specified) to the target file or directory. +If the destination is a directory, then the +.Ar file +is copied into +.Ar directory +with its original filename. +If the target file already exists, it is +either renamed to +.Ar file.old +if the +.Fl b +option is given +or overwritten +if permissions allow; an alternate backup suffix may be specified via the +.Fl B +option's argument. +.Pp +.Bl -tag -width XsXXstripflagsXX +.It Fl a Ar command +Run +.Ar command +on the target after installation and stripping +.Pq Fl s , +but before +ownership, permissions or timestamps are set and before renaming +.Pq Fl r +occurs. +.Ar command +is invoked via the +.Xr sh 1 +shell, allowing a single +.Fl a +argument be to specified to +.Nm +which the shell can then tokenize. +.It Fl B Ar suffix +Use +.Ar suffix +as the backup suffix if +.Fl b +is given. +If +.Ar suffix +contains a '%' sign, a numbered backup will be performed, and the +%-pattern will be expanded using +.Xr sprintf 3 , +given an integer counter as the backup number. +The counter used starts from 0, and the first available name resulting +from the expansion is used. +.It Fl b +Backup any existing files before overwriting them by renaming +them to +.Ar file.old . See +.Fl B +for specifying a different backup suffix. +.It Fl c +Copy the file. +This is the default behavior; the flag is maintained for backwards +compatibility only. +.It Fl D Ar destdir +Specify the +.Ev DESTDIR +(top of the file hierarchy) that the items are installed in to. +If +.Fl M Ar metalog +is in use, a leading string of +.Dq Ar destdir +will be removed from the file names logged to the +.Ar metalog . +This option does not affect where the actual files are installed. +.It Fl d +Create directories. +Missing parent directories are created as required. +.It Fl f Ar flags +Specify the target's file flags. +(See +.Xr chflags 1 +for a list of possible flags and their meanings.) +.It Fl g Ar group +Specify a group. +.It Fl h Ar hash +When copying, calculate the digest of the files with +.Ar hash +to store in the +.Fl M Ar metalog . +Supported digests: +.Bl -tag -width rmd160 -offset indent +.It Sy none +No hash. +This is the default. +.It Sy md5 +The MD5 cryptographic message digest. +.It Sy rmd160 +The RMD-160 cryptographic message digest. +.It Sy sha1 +The SHA-1 cryptographic message digest. +.It Sy sha256 +The 256-bits +.Tn SHA-2 +cryptographic message digest of the file. +.It Sy sha384 +The 384-bits +.Tn SHA-2 +cryptographic message digest of the file. +.It Sy sha512 +The 512-bits +.Tn SHA-2 +cryptographic message digest of the file. +.El +.It Fl l Ar linkflags +Instead of copying the file make a link to the source. +The type of the link is determined by the +.Ar linkflags +argument. +Valid +.Ar linkflags +are: +.Ar a +(absolute), +.Ar r +(relative), +.Ar h +(hard), +.Ar s +(symbolic), +.Ar m +(mixed). +Absolute and relative have effect only for symbolic links. +Mixed links +are hard links for files on the same filesystem, symbolic otherwise. +.It Fl M Ar metalog +Write the metadata associated with each item installed to +.Ar metalog +in an +.Xr mtree 8 +.Dq full path +specification line. +The metadata includes: the file name and file type, and depending upon +other options, the owner, group, file flags, modification time, and tags. +.It Fl m Ar mode +Specify an alternative mode. +The default mode is set to rwxr-xr-x (0755). +The specified mode may be either an octal or symbolic value; see +.Xr chmod 1 +for a description of possible mode values. +.It Fl N Ar dbdir +Use the user database text file +.Pa master.passwd +and group database text file +.Pa group +from +.Ar dbdir , +rather than using the results from the system's +.Xr getpwnam 3 +and +.Xr getgrnam 3 +(and related) library calls. +.It Fl o Ar owner +Specify an owner. +.It Fl p +Preserve the source files access and modification times. +.It Fl r +Install to a temporary file and then rename the file to its final destination +name. +This can be used for precious files, to avoid truncation of the original +when error conditions (filesystem full etc.) occur. +.It Fl S Ar stripflags +.Nm +passes +.Ar stripflags +as option arguments to +.Xr strip 1 . +When +.Fl S +is used, +.Xr strip 1 +is invoked via the +.Xr sh 1 +shell, allowing a single +.Fl S +argument be to specified to +.Nm +which the shell can then tokenize. +Normally, +.Nm +invokes +.Xr strip 1 +directly. +This flag implies +.Fl s . +.It Fl s +.Nm +exec's the command +.Xr strip 1 +to strip binaries so that install can be portable over a large +number of systems and binary types. +If the environment variable +.Ev STRIP +is set, it is used as the +.Xr strip 1 +program. +.It Fl T Ar tags +Specify the +.Xr mtree 8 +tags to write out for the file when using +.Fl M Ar metalog . +.It Fl U +Indicate that install is running unprivileged, and that it should not +try to change the owner, the group, or the file flags of the destination. +The information that would have been updated can be stored in a log +file with +.Fl M Ar metalog . +.El +.Pp +By default, +.Nm +preserves all file flags, with the exception of the ``nodump'' flag. +.Pp +The +.Nm +utility attempts to prevent copying a file onto itself. +.Pp +Installing +.Pa /dev/null +creates an empty file. +.Sh ENVIRONMENT +.Bl -tag -width Fl +.It Ev STRIP +The program used to strip installed binaries when the +.Fl s +option is used. +If unspecified, +.Pa /usr/bin/strip +is used. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chgrp 1 , +.Xr chmod 1 , +.Xr cp 1 , +.Xr mv 1 , +.Xr strip 1 , +.Xr chown 8 , +.Xr mtree 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.2 . diff --git a/buildrump.sh/src/usr.bin/xinstall/pathnames.h b/buildrump.sh/src/usr.bin/xinstall/pathnames.h new file mode 100644 index 00000000..88ad1e25 --- /dev/null +++ b/buildrump.sh/src/usr.bin/xinstall/pathnames.h @@ -0,0 +1,36 @@ +/* $NetBSD: pathnames.h,v 1.6 2003/08/07 11:17:50 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/6/93 + */ + +#ifndef _PATH_STRIP +#define _PATH_STRIP "/usr/bin/strip" +#endif diff --git a/buildrump.sh/src/usr.bin/xinstall/xinstall.c b/buildrump.sh/src/usr.bin/xinstall/xinstall.c new file mode 100644 index 00000000..b2ced620 --- /dev/null +++ b/buildrump.sh/src/usr.bin/xinstall/xinstall.c @@ -0,0 +1,1275 @@ +/* $NetBSD: xinstall.c,v 1.117 2014/07/06 20:54:47 apb Exp $ */ + +/* + * Copyright (c) 1987, 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. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#else +#define HAVE_FUTIMES 1 +#define HAVE_STRUCT_STAT_ST_FLAGS 1 +#endif + +#include <sys/cdefs.h> +#if defined(__COPYRIGHT) && !defined(lint) +__COPYRIGHT("@(#) Copyright (c) 1987, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#if defined(__RCSID) && !defined(lint) +#if 0 +static char sccsid[] = "@(#)xinstall.c 8.1 (Berkeley) 7/21/93"; +#else +__RCSID("$NetBSD: xinstall.c,v 1.117 2014/07/06 20:54:47 apb Exp $"); +#endif +#endif /* not lint */ + +#define __MKTEMP_OK__ /* All uses of mktemp have been checked */ +#include <sys/param.h> +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/time.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <grp.h> +#include <libgen.h> +#include <paths.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <util.h> +#include <vis.h> + +#include <md5.h> +#include <rmd160.h> +#include <sha1.h> +#include <sha2.h> + +#include "pathnames.h" +#include "mtree.h" + +#define STRIP_ARGS_MAX 32 +#define BACKUP_SUFFIX ".old" + +static int dobackup, dodir, dostrip, dolink, dopreserve, dorename, dounpriv; +static int haveopt_f, haveopt_g, haveopt_m, haveopt_o; +static int numberedbackup; +static int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH; +static char pathbuf[MAXPATHLEN]; +static uid_t uid = -1; +static gid_t gid = -1; +static char *group, *owner, *fflags, *tags; +static FILE *metafp; +static char *metafile; +static u_long fileflags; +static char *stripArgs; +static char *afterinstallcmd; +static const char *suffix = BACKUP_SUFFIX; +static char *destdir; + +enum { + DIGEST_NONE = 0, + DIGEST_MD5, + DIGEST_RMD160, + DIGEST_SHA1, + DIGEST_SHA256, + DIGEST_SHA384, + DIGEST_SHA512, +} digesttype = DIGEST_NONE; + +static char *digest; + +#define LN_ABSOLUTE 0x01 +#define LN_RELATIVE 0x02 +#define LN_HARD 0x04 +#define LN_SYMBOLIC 0x08 +#define LN_MIXED 0x10 + +#define DIRECTORY 0x01 /* Tell install it's a directory. */ +#define SETFLAGS 0x02 /* Tell install to set flags. */ +#define HASUID 0x04 /* Tell install the uid was given */ +#define HASGID 0x08 /* Tell install the gid was given */ + +static void afterinstall(const char *, const char *, int); +static void backup(const char *); +static char *copy(int, char *, int, char *, off_t); +static int do_link(char *, char *); +static void do_symlink(char *, char *); +static void install(char *, char *, u_int); +static void install_dir(char *, u_int); +static void makelink(char *, char *); +static void metadata_log(const char *, const char *, struct timeval *, + const char *, const char *, off_t); +static int parseid(char *, id_t *); +static void strip(char *); +__dead static void usage(void); +static char *xbasename(char *); +static char *xdirname(char *); + +int +main(int argc, char *argv[]) +{ + struct stat from_sb, to_sb; + void *set; + u_int iflags; + int ch, no_target; + char *p, *to_name; + + setprogname(argv[0]); + + iflags = 0; + while ((ch = getopt(argc, argv, "a:cbB:dD:f:g:h:l:m:M:N:o:prsS:T:U")) + != -1) + switch((char)ch) { + case 'a': + afterinstallcmd = strdup(optarg); + if (afterinstallcmd == NULL) + errx(1, "%s", strerror(ENOMEM)); + break; + case 'B': + suffix = optarg; + numberedbackup = 0; + { + /* Check if given suffix really generates + different suffixes - catch e.g. ".%" */ + char suffix_expanded0[FILENAME_MAX], + suffix_expanded1[FILENAME_MAX]; + (void)snprintf(suffix_expanded0, FILENAME_MAX, + suffix, 0); + (void)snprintf(suffix_expanded1, FILENAME_MAX, + suffix, 1); + if (strcmp(suffix_expanded0, suffix_expanded1) + != 0) + numberedbackup = 1; + } + /* fall through; -B implies -b */ + /*FALLTHROUGH*/ + case 'b': + dobackup = 1; + break; + case 'c': + /* ignored; was "docopy" which is now the default. */ + break; + case 'd': + dodir = 1; + break; + case 'D': + destdir = optarg; + break; +#if ! HAVE_NBTOOL_CONFIG_H + case 'f': + haveopt_f = 1; + fflags = optarg; + break; +#endif + case 'g': + haveopt_g = 1; + group = optarg; + break; + case 'h': + digest = optarg; + break; + case 'l': + for (p = optarg; *p; p++) + switch (*p) { + case 's': + dolink &= ~(LN_HARD|LN_MIXED); + dolink |= LN_SYMBOLIC; + break; + case 'h': + dolink &= ~(LN_SYMBOLIC|LN_MIXED); + dolink |= LN_HARD; + break; + case 'm': + dolink &= ~(LN_SYMBOLIC|LN_HARD); + dolink |= LN_MIXED; + break; + case 'a': + dolink &= ~LN_RELATIVE; + dolink |= LN_ABSOLUTE; + break; + case 'r': + dolink &= ~LN_ABSOLUTE; + dolink |= LN_RELATIVE; + break; + default: + errx(1, "%c: invalid link type", *p); + /* NOTREACHED */ + } + break; + case 'm': + haveopt_m = 1; + if (!(set = setmode(optarg))) + err(1, "Cannot set file mode `%s'", optarg); + mode = getmode(set, 0); + free(set); + break; + case 'M': + metafile = optarg; + break; + case 'N': + if (! setup_getid(optarg)) + errx(1, + "Unable to use user and group databases in `%s'", + optarg); + break; + case 'o': + haveopt_o = 1; + owner = optarg; + break; + case 'p': + dopreserve = 1; + break; + case 'r': + dorename = 1; + break; + case 'S': + stripArgs = strdup(optarg); + if (stripArgs == NULL) + errx(1, "%s", strerror(ENOMEM)); + /* fall through; -S implies -s */ + /*FALLTHROUGH*/ + case 's': + dostrip = 1; + break; + case 'T': + tags = optarg; + break; + case 'U': + dounpriv = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* strip and link options make no sense when creating directories */ + if ((dostrip || dolink) && dodir) + usage(); + + /* strip and flags make no sense with links */ + if ((dostrip || fflags) && dolink) + usage(); + + /* must have at least two arguments, except when creating directories */ + if (argc < 2 && !dodir) + usage(); + + if (digest) { + if (0) { + } else if (strcmp(digest, "none") == 0) { + digesttype = DIGEST_NONE; + } else if (strcmp(digest, "md5") == 0) { + digesttype = DIGEST_MD5; + } else if (strcmp(digest, "rmd160") == 0) { + digesttype = DIGEST_RMD160; + } else if (strcmp(digest, "sha1") == 0) { + digesttype = DIGEST_SHA1; + } else if (strcmp(digest, "sha256") == 0) { + digesttype = DIGEST_SHA256; + } else if (strcmp(digest, "sha384") == 0) { + digesttype = DIGEST_SHA384; + } else if (strcmp(digest, "sha512") == 0) { + digesttype = DIGEST_SHA512; + } else { + warnx("unknown digest `%s'", digest); + usage(); + } + } + + /* get group and owner id's */ + if (group && !dounpriv) { + if (gid_from_group(group, &gid) == -1) { + id_t id; + if (!parseid(group, &id)) + errx(1, "unknown group %s", group); + gid = id; + } + iflags |= HASGID; + } + if (owner && !dounpriv) { + if (uid_from_user(owner, &uid) == -1) { + id_t id; + if (!parseid(owner, &id)) + errx(1, "unknown user %s", owner); + uid = id; + } + iflags |= HASUID; + } + +#if ! HAVE_NBTOOL_CONFIG_H + if (fflags && !dounpriv) { + if (string_to_flags(&fflags, &fileflags, NULL)) + errx(1, "%s: invalid flag", fflags); + /* restore fflags since string_to_flags() changed it */ + fflags = flags_to_string(fileflags, "-"); + iflags |= SETFLAGS; + } +#endif + + if (metafile) { + if ((metafp = fopen(metafile, "a")) == NULL) + warn("open %s", metafile); + } else + digesttype = DIGEST_NONE; + + if (dodir) { + for (; *argv != NULL; ++argv) + install_dir(*argv, iflags); + exit (0); + } + + no_target = stat(to_name = argv[argc - 1], &to_sb); + if (!no_target && S_ISDIR(to_sb.st_mode)) { + for (; *argv != to_name; ++argv) + install(*argv, to_name, iflags | DIRECTORY); + exit(0); + } + + /* can't do file1 file2 directory/file */ + if (argc != 2) { + errx(EXIT_FAILURE, "the last argument (%s) " + "must name an existing directory", argv[argc - 1]); + /* NOTREACHED */ + } + + if (!no_target) { + /* makelink() handles checks for links */ + if (!dolink) { + if (stat(*argv, &from_sb)) + err(1, "%s: stat", *argv); + if (!S_ISREG(to_sb.st_mode)) + errx(1, "%s: not a regular file", to_name); + if (to_sb.st_dev == from_sb.st_dev && + to_sb.st_ino == from_sb.st_ino) + errx(1, "%s and %s are the same file", *argv, + to_name); + } + /* + * Unlink now... avoid ETXTBSY errors later. Try and turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ +#if ! HAVE_NBTOOL_CONFIG_H +#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND) + if (to_sb.st_flags & NOCHANGEBITS) + (void)chflags(to_name, + to_sb.st_flags & ~(NOCHANGEBITS)); +#endif + if (dobackup) + backup(to_name); + else if (!dorename) + (void)unlink(to_name); + } + install(*argv, to_name, iflags); + exit(0); +} + +/* + * parseid -- + * parse uid or gid from arg into id, returning non-zero if successful + */ +static int +parseid(char *name, id_t *id) +{ + char *ep; + + errno = 0; + *id = (id_t)strtoul(name, &ep, 10); + if (errno || *ep != '\0') + return (0); + return (1); +} + +/* + * do_link -- + * make a hard link, obeying dorename if set + * return -1 on failure + */ +static int +do_link(char *from_name, char *to_name) +{ + char tmpl[MAXPATHLEN]; + int ret; + + if (dorename) { + (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); + /* This usage is safe. */ + if (mktemp(tmpl) == NULL) + err(1, "%s: mktemp", tmpl); + ret = link(from_name, tmpl); + if (ret == 0) { + ret = rename(tmpl, to_name); + /* If rename has posix semantics, then the temporary + * file may still exist when from_name and to_name point + * to the same file, so unlink it unconditionally. + */ + (void)unlink(tmpl); + } + return (ret); + } else + return (link(from_name, to_name)); +} + +/* + * do_symlink -- + * make a symbolic link, obeying dorename if set + * exit on failure + */ +static void +do_symlink(char *from_name, char *to_name) +{ + char tmpl[MAXPATHLEN]; + + if (dorename) { + (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); + /* This usage is safe. */ + if (mktemp(tmpl) == NULL) + err(1, "%s: mktemp", tmpl); + + if (symlink(from_name, tmpl) == -1) + err(1, "symlink %s -> %s", from_name, tmpl); + if (rename(tmpl, to_name) == -1) { + /* remove temporary link before exiting */ + (void)unlink(tmpl); + err(1, "%s: rename", to_name); + } + } else { + if (symlink(from_name, to_name) == -1) + err(1, "symlink %s -> %s", from_name, to_name); + } +} + +/* + * makelink -- + * make a link from source to destination + */ +static void +makelink(char *from_name, char *to_name) +{ + char src[MAXPATHLEN], dst[MAXPATHLEN], lnk[MAXPATHLEN]; + struct stat to_sb; + + /* Try hard links first */ + if (dolink & (LN_HARD|LN_MIXED)) { + if (do_link(from_name, to_name) == -1) { + if ((dolink & LN_HARD) || errno != EXDEV) + err(1, "link %s -> %s", from_name, to_name); + } else { + if (stat(to_name, &to_sb)) + err(1, "%s: stat", to_name); + if (S_ISREG(to_sb.st_mode)) { + /* XXX: hard links to anything + * other than plain files are not + * metalogged + */ + int omode; + char *oowner, *ogroup, *offlags; + char *dres; + + /* XXX: use underlying perms, + * unless overridden on command line. + */ + omode = mode; + if (!haveopt_m) + mode = (to_sb.st_mode & 0777); + oowner = owner; + if (!haveopt_o) + owner = NULL; + ogroup = group; + if (!haveopt_g) + group = NULL; + offlags = fflags; + if (!haveopt_f) + fflags = NULL; + switch (digesttype) { + case DIGEST_MD5: + dres = MD5File(from_name, NULL); + break; + case DIGEST_RMD160: + dres = RMD160File(from_name, NULL); + break; + case DIGEST_SHA1: + dres = SHA1File(from_name, NULL); + break; + case DIGEST_SHA256: + dres = SHA256_File(from_name, NULL); + break; + case DIGEST_SHA384: + dres = SHA384_File(from_name, NULL); + break; + case DIGEST_SHA512: + dres = SHA512_File(from_name, NULL); + break; + default: + dres = NULL; + } + metadata_log(to_name, "file", NULL, NULL, + dres, to_sb.st_size); + free(dres); + mode = omode; + owner = oowner; + group = ogroup; + fflags = offlags; + } + return; + } + } + + /* Symbolic links */ + if (dolink & LN_ABSOLUTE) { + /* Convert source path to absolute */ + if (realpath(from_name, src) == NULL) + err(1, "%s: realpath", from_name); + do_symlink(src, to_name); + /* XXX: src may point outside of destdir */ + metadata_log(to_name, "link", NULL, src, NULL, 0); + return; + } + + if (dolink & LN_RELATIVE) { + char *cp, *d, *s; + + /* Resolve pathnames */ + if (realpath(from_name, src) == NULL) + err(1, "%s: realpath", from_name); + + /* + * The last component of to_name may be a symlink, + * so use realpath to resolve only the directory. + */ + cp = xdirname(to_name); + if (realpath(cp, dst) == NULL) + err(1, "%s: realpath", cp); + /* .. and add the last component */ + if (strcmp(dst, "/") != 0) { + if (strlcat(dst, "/", sizeof(dst)) > sizeof(dst)) + errx(1, "resolved pathname too long"); + } + cp = xbasename(to_name); + if (strlcat(dst, cp, sizeof(dst)) > sizeof(dst)) + errx(1, "resolved pathname too long"); + + /* trim common path components */ + for (s = src, d = dst; *s == *d; s++, d++) + continue; + while (*s != '/') + s--, d--; + + /* count the number of directories we need to backtrack */ + for (++d, lnk[0] = '\0'; *d; d++) + if (*d == '/') + (void)strlcat(lnk, "../", sizeof(lnk)); + + (void)strlcat(lnk, ++s, sizeof(lnk)); + + do_symlink(lnk, to_name); + /* XXX: lnk may point outside of destdir */ + metadata_log(to_name, "link", NULL, lnk, NULL, 0); + return; + } + + /* + * If absolute or relative was not specified, + * try the names the user provided + */ + do_symlink(from_name, to_name); + /* XXX: from_name may point outside of destdir */ + metadata_log(to_name, "link", NULL, from_name, NULL, 0); +} + +/* + * install -- + * build a path name and install the file + */ +static void +install(char *from_name, char *to_name, u_int flags) +{ + struct stat from_sb; + struct stat to_sb; + struct timeval tv[2]; + off_t size; + int devnull, from_fd, to_fd, serrno, tmpmode; + char *p, tmpl[MAXPATHLEN], *oto_name, *digestresult; + + size = -1; + if (!dolink) { + /* ensure that from_sb & tv are sane if !dolink */ + if (stat(from_name, &from_sb)) + err(1, "%s: stat", from_name); + size = from_sb.st_size; +#if BSD4_4 && !HAVE_NBTOOL_CONFIG_H + TIMESPEC_TO_TIMEVAL(&tv[0], &from_sb.st_atimespec); + TIMESPEC_TO_TIMEVAL(&tv[1], &from_sb.st_mtimespec); +#else + tv[0].tv_sec = from_sb.st_atime; + tv[0].tv_usec = 0; + tv[1].tv_sec = from_sb.st_mtime; + tv[1].tv_usec = 0; +#endif + } + + if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL) != 0) { + devnull = 0; + if (!dolink) { + if (!S_ISREG(from_sb.st_mode)) + errx(1, "%s: not a regular file", from_name); + } + /* Build the target path. */ + if (flags & DIRECTORY) { + (void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s", + to_name, + (p = strrchr(from_name, '/')) ? ++p : from_name); + to_name = pathbuf; + } + } else { + devnull = 1; + size = 0; +#if HAVE_STRUCT_STAT_ST_FLAGS + from_sb.st_flags = 0; /* XXX */ +#endif + } + + /* + * Unlink now... avoid ETXTBSY errors later. Try and turn + * off the append/immutable bits -- if we fail, go ahead, + * it might work. + */ +#if ! HAVE_NBTOOL_CONFIG_H + if (stat(to_name, &to_sb) == 0 && + to_sb.st_flags & (NOCHANGEBITS)) + (void)chflags(to_name, to_sb.st_flags & ~(NOCHANGEBITS)); +#endif + if (dorename) { + (void)snprintf(tmpl, sizeof(tmpl), "%s.inst.XXXXXX", to_name); + oto_name = to_name; + to_name = tmpl; + } else { + oto_name = NULL; /* pacify gcc */ + if (dobackup) + backup(to_name); + else + (void)unlink(to_name); + } + + if (dolink) { + makelink(from_name, dorename ? oto_name : to_name); + return; + } + + /* Create target. */ + if (dorename) { + if ((to_fd = mkstemp(to_name)) == -1) + err(1, "%s: mkstemp", to_name); + } else { + if ((to_fd = open(to_name, + O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) + err(1, "%s: open", to_name); + } + digestresult = NULL; + if (!devnull) { + if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) { + (void)unlink(to_name); + err(1, "%s: open", from_name); + } + digestresult = + copy(from_fd, from_name, to_fd, to_name, from_sb.st_size); + (void)close(from_fd); + } + + if (dostrip) { + strip(to_name); + + /* + * Re-open our fd on the target, in case we used a strip + * that does not work in-place -- like gnu binutils strip. + */ + close(to_fd); + if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0) + err(1, "stripping %s", to_name); + + /* + * Recalculate size and digestresult after stripping. + */ + if (fstat(to_fd, &to_sb) != 0) + err(1, "%s: fstat", to_name); + size = to_sb.st_size; + digestresult = + copy(to_fd, to_name, -1, NULL, size); + + } + + if (afterinstallcmd != NULL) { + afterinstall(afterinstallcmd, to_name, 1); + + /* + * Re-open our fd on the target, in case we used an + * after-install command that does not work in-place + */ + close(to_fd); + if ((to_fd = open(to_name, O_RDONLY, S_IRUSR | S_IWUSR)) < 0) + err(1, "running after install command on %s", to_name); + } + + /* + * Set owner, group, mode for target; do the chown first, + * chown may lose the setuid bits. + */ + if (!dounpriv && + (flags & (HASUID | HASGID)) && fchown(to_fd, uid, gid) == -1) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chown/chgrp: %s", to_name, strerror(serrno)); + } + tmpmode = mode; + if (dounpriv) + tmpmode &= S_IRWXU|S_IRWXG|S_IRWXO; + if (fchmod(to_fd, tmpmode) == -1) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: chmod: %s", to_name, strerror(serrno)); + } + + /* + * Preserve the date of the source file. + */ + if (dopreserve) { +#if HAVE_FUTIMES + if (futimes(to_fd, tv) == -1) + warn("%s: futimes", to_name); +#else + if (utimes(to_name, tv) == -1) + warn("%s: utimes", to_name); +#endif + } + + (void)close(to_fd); + + if (dorename) { + if (rename(to_name, oto_name) == -1) + err(1, "%s: rename", to_name); + to_name = oto_name; + } + + /* + * If provided a set of flags, set them, otherwise, preserve the + * flags, except for the dump flag. + */ +#if ! HAVE_NBTOOL_CONFIG_H + if (!dounpriv && chflags(to_name, + flags & SETFLAGS ? fileflags : from_sb.st_flags & ~UF_NODUMP) == -1) + { + if (errno != EOPNOTSUPP || (from_sb.st_flags & ~UF_NODUMP) != 0) + warn("%s: chflags", to_name); + } +#endif + + metadata_log(to_name, "file", tv, NULL, digestresult, size); + free(digestresult); +} + +/* + * copy -- + * copy from one file to another, returning a digest. + * + * If to_fd < 0, just calculate a digest, don't copy. + */ +static char * +copy(int from_fd, char *from_name, int to_fd, char *to_name, off_t size) +{ + ssize_t nr, nw; + int serrno; + u_char *p; + u_char buf[MAXBSIZE]; + MD5_CTX ctxMD5; + RMD160_CTX ctxRMD160; + SHA1_CTX ctxSHA1; + SHA256_CTX ctxSHA256; + SHA384_CTX ctxSHA384; + SHA512_CTX ctxSHA512; + + switch (digesttype) { + case DIGEST_MD5: + MD5Init(&ctxMD5); + break; + case DIGEST_RMD160: + RMD160Init(&ctxRMD160); + break; + case DIGEST_SHA1: + SHA1Init(&ctxSHA1); + break; + case DIGEST_SHA256: + SHA256_Init(&ctxSHA256); + break; + case DIGEST_SHA384: + SHA384_Init(&ctxSHA384); + break; + case DIGEST_SHA512: + SHA512_Init(&ctxSHA512); + break; + case DIGEST_NONE: + if (to_fd < 0) + return NULL; /* no need to do anything */ + default: + break; + } + /* + * There's no reason to do anything other than close the file + * now if it's empty, so let's not bother. + */ + if (size > 0) { + + /* + * Mmap and write if less than 8M (the limit is so we + * don't totally trash memory on big files). This is + * really a minor hack, but it wins some CPU back. + */ + + if (size <= 8 * 1048576) { + if ((p = mmap(NULL, (size_t)size, PROT_READ, + MAP_FILE|MAP_SHARED, from_fd, (off_t)0)) + == MAP_FAILED) { + goto mmap_failed; + } +#if defined(MADV_SEQUENTIAL) && !defined(__APPLE__) + if (madvise(p, (size_t)size, MADV_SEQUENTIAL) == -1 + && errno != EOPNOTSUPP) + warnx("madvise: %s", strerror(errno)); +#endif + + if (to_fd >= 0 && write(to_fd, p, size) != size) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: write: %s", + to_name, strerror(serrno)); + } + switch (digesttype) { + case DIGEST_MD5: + MD5Update(&ctxMD5, p, size); + break; + case DIGEST_RMD160: + RMD160Update(&ctxRMD160, p, size); + break; + case DIGEST_SHA1: + SHA1Update(&ctxSHA1, p, size); + break; + case DIGEST_SHA256: + SHA256_Update(&ctxSHA256, p, size); + break; + case DIGEST_SHA384: + SHA384_Update(&ctxSHA384, p, size); + break; + case DIGEST_SHA512: + SHA512_Update(&ctxSHA512, p, size); + break; + default: + break; + } + (void)munmap(p, size); + } else { + mmap_failed: + while ((nr = read(from_fd, buf, sizeof(buf))) > 0) { + if (to_fd >= 0 && + (nw = write(to_fd, buf, nr)) != nr) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: write: %s", to_name, + strerror(nw > 0 ? EIO : serrno)); + } + switch (digesttype) { + case DIGEST_MD5: + MD5Update(&ctxMD5, buf, nr); + break; + case DIGEST_RMD160: + RMD160Update(&ctxRMD160, buf, nr); + break; + case DIGEST_SHA1: + SHA1Update(&ctxSHA1, buf, nr); + break; + case DIGEST_SHA256: + SHA256_Update(&ctxSHA256, buf, nr); + break; + case DIGEST_SHA384: + SHA384_Update(&ctxSHA384, buf, nr); + break; + case DIGEST_SHA512: + SHA512_Update(&ctxSHA512, buf, nr); + break; + default: + break; + } + } + if (nr != 0) { + serrno = errno; + (void)unlink(to_name); + errx(1, "%s: read: %s", from_name, strerror(serrno)); + } + } + } + switch (digesttype) { + case DIGEST_MD5: + return MD5End(&ctxMD5, NULL); + case DIGEST_RMD160: + return RMD160End(&ctxRMD160, NULL); + case DIGEST_SHA1: + return SHA1End(&ctxSHA1, NULL); + case DIGEST_SHA256: + return SHA256_End(&ctxSHA256, NULL); + case DIGEST_SHA384: + return SHA384_End(&ctxSHA384, NULL); + case DIGEST_SHA512: + return SHA512_End(&ctxSHA512, NULL); + default: + return NULL; + } +} + +/* + * strip -- + * use strip(1) to strip the target file + */ +static void +strip(char *to_name) +{ + static const char exec_failure[] = ": exec of strip failed: "; + int serrno, status; + const char * volatile stripprog, *progname; + char *cmd; + + if ((stripprog = getenv("STRIP")) == NULL || *stripprog == '\0') { +#ifdef TARGET_STRIP + stripprog = TARGET_STRIP; +#else + stripprog = _PATH_STRIP; +#endif + } + + cmd = NULL; + + if (stripArgs) { + /* + * Build up a command line and let /bin/sh + * parse the arguments. + */ + int ret = asprintf(&cmd, "%s %s %s", stripprog, stripArgs, + to_name); + + if (ret == -1 || cmd == NULL) + err(1, "asprintf failed"); + } + + switch (vfork()) { + case -1: + serrno = errno; + (void)unlink(to_name); + errx(1, "vfork: %s", strerror(serrno)); + /*NOTREACHED*/ + case 0: + + if (stripArgs) + execl(_PATH_BSHELL, "sh", "-c", cmd, NULL); + else + execlp(stripprog, "strip", to_name, NULL); + + progname = getprogname(); + write(STDERR_FILENO, progname, strlen(progname)); + write(STDERR_FILENO, exec_failure, strlen(exec_failure)); + write(STDERR_FILENO, stripprog, strlen(stripprog)); + write(STDERR_FILENO, "\n", 1); + _exit(1); + /*NOTREACHED*/ + default: + if (wait(&status) == -1 || status) + (void)unlink(to_name); + } + + free(cmd); +} + +/* + * afterinstall -- + * run provided command on the target file or directory after it's been + * installed and stripped, but before permissions are set or it's renamed + */ +static void +afterinstall(const char *command, const char *to_name, int errunlink) +{ + int serrno, status; + char *cmd; + + switch (vfork()) { + case -1: + serrno = errno; + if (errunlink) + (void)unlink(to_name); + errx(1, "vfork: %s", strerror(serrno)); + /*NOTREACHED*/ + case 0: + /* + * build up a command line and let /bin/sh + * parse the arguments + */ + cmd = (char*)malloc(sizeof(char)* + (2+strlen(command)+ + strlen(to_name))); + + if (cmd == NULL) + errx(1, "%s", strerror(ENOMEM)); + + sprintf(cmd, "%s %s", command, to_name); + + execl(_PATH_BSHELL, "sh", "-c", cmd, NULL); + + warn("%s: exec of after install command", command); + _exit(1); + /*NOTREACHED*/ + default: + if ((wait(&status) == -1 || status) && errunlink) + (void)unlink(to_name); + } +} + +/* + * backup -- + * backup file "to_name" to to_name.suffix + * if suffix contains a "%", it's taken as a printf(3) pattern + * used for a numbered backup. + */ +static void +backup(const char *to_name) +{ + char bname[FILENAME_MAX]; + + if (numberedbackup) { + /* Do numbered backup */ + int cnt; + char suffix_expanded[FILENAME_MAX]; + + cnt=0; + do { + (void)snprintf(suffix_expanded, FILENAME_MAX, suffix, + cnt); + (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, + suffix_expanded); + cnt++; + } while (access(bname, F_OK) == 0); + } else { + /* Do simple backup */ + (void)snprintf(bname, FILENAME_MAX, "%s%s", to_name, suffix); + } + + (void)rename(to_name, bname); +} + +/* + * install_dir -- + * build directory hierarchy + */ +static void +install_dir(char *path, u_int flags) +{ + char *p; + struct stat sb; + int ch; + + for (p = path;; ++p) + if (!*p || (p != path && *p == '/')) { + ch = *p; + *p = '\0'; + if (mkdir(path, 0777) < 0) { + /* + * Can't create; path exists or no perms. + * stat() path to determine what's there now. + */ + int sverrno; + sverrno = errno; + if (stat(path, &sb) < 0) { + /* Not there; use mkdir()s error */ + errno = sverrno; + err(1, "%s: mkdir", path); + } + if (!S_ISDIR(sb.st_mode)) { + errx(1, + "%s exists but is not a directory", + path); + } + } + if (!(*p = ch)) + break; + } + + if (afterinstallcmd != NULL) + afterinstall(afterinstallcmd, path, 0); + + if (!dounpriv && ( + ((flags & (HASUID | HASGID)) && chown(path, uid, gid) == -1) + || chmod(path, mode) == -1 )) { + warn("%s: chown/chmod", path); + } + metadata_log(path, "dir", NULL, NULL, NULL, 0); +} + +/* + * metadata_log -- + * if metafp is not NULL, output mtree(8) full path name and settings to + * metafp, to allow permissions to be set correctly by other tools, + * or to allow integrity checks to be performed. + */ +static void +metadata_log(const char *path, const char *type, struct timeval *tv, + const char *slink, const char *digestresult, off_t size) +{ + static const char extra[] = { ' ', '\t', '\n', '\\', '#', '\0' }; + const char *p; + char *buf; + size_t destlen; + struct flock metalog_lock; + + if (!metafp) + return; + buf = (char *)malloc(4 * strlen(path) + 1); /* buf for strsvis(3) */ + if (buf == NULL) { + warnx("%s", strerror(ENOMEM)); + return; + } + /* lock log file */ + metalog_lock.l_start = 0; + metalog_lock.l_len = 0; + metalog_lock.l_whence = SEEK_SET; + metalog_lock.l_type = F_WRLCK; + if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) { + warn("can't lock %s", metafile); + free(buf); + return; + } + + p = path; /* remove destdir */ + if (destdir) { + destlen = strlen(destdir); + if (strncmp(p, destdir, destlen) == 0 && + (p[destlen] == '/' || p[destlen] == '\0')) + p += destlen; + } + while (*p && *p == '/') /* remove leading /s */ + p++; + strsvis(buf, p, VIS_CSTYLE, extra); /* encode name */ + p = buf; + /* print details */ + fprintf(metafp, ".%s%s type=%s", *p ? "/" : "", p, type); + if (owner) + fprintf(metafp, " uname=%s", owner); + if (group) + fprintf(metafp, " gname=%s", group); + fprintf(metafp, " mode=%#o", mode); + if (slink) { + strsvis(buf, slink, VIS_CSTYLE, extra); /* encode link */ + fprintf(metafp, " link=%s", buf); + } + if (*type == 'f') /* type=file */ + fprintf(metafp, " size=%lld", (long long)size); + if (tv != NULL && dopreserve) + fprintf(metafp, " time=%lld.%0*lld", + (long long)tv[1].tv_sec, + (tv[1].tv_usec == 0 ? 1 : 9), + (long long)tv[1].tv_usec * 1000); + if (digestresult && digest) + fprintf(metafp, " %s=%s", digest, digestresult); + if (fflags) + fprintf(metafp, " flags=%s", fflags); + if (tags) + fprintf(metafp, " tags=%s", tags); + fputc('\n', metafp); + fflush(metafp); /* flush output */ + /* unlock log file */ + metalog_lock.l_type = F_UNLCK; + if (fcntl(fileno(metafp), F_SETLKW, &metalog_lock) == -1) { + warn("can't unlock %s", metafile); + } + free(buf); +} + +/* + * xbasename -- + * libc basename(3) that returns a pointer to a static buffer + * instead of overwriting that passed-in string. + */ +static char * +xbasename(char *path) +{ + static char tmp[MAXPATHLEN]; + + (void)strlcpy(tmp, path, sizeof(tmp)); + return (basename(tmp)); +} + +/* + * xdirname -- + * libc dirname(3) that returns a pointer to a static buffer + * instead of overwriting that passed-in string. + */ +static char * +xdirname(char *path) +{ + static char tmp[MAXPATHLEN]; + + (void)strlcpy(tmp, path, sizeof(tmp)); + return (dirname(tmp)); +} + +/* + * usage -- + * print a usage message and die + */ +static void +usage(void) +{ + const char *prog; + + prog = getprogname(); + + (void)fprintf(stderr, +"usage: %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n" +" [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group] \n" +" [-l linkflags] [-h hash] [-S stripflags] file1 file2\n" +" %s [-Ubcprs] [-M log] [-D dest] [-T tags] [-B suffix]\n" +" [-a aftercmd] [-f flags] [-m mode] [-N dbdir] [-o owner] [-g group]\n" +" [-l linkflags] [-h hash] [-S stripflags] file1 ... fileN directory\n" +" %s -d [-Up] [-M log] [-D dest] [-T tags] [-a aftercmd] [-m mode]\n" +" [-N dbdir] [-o owner] [-g group] directory ...\n", + prog, prog, prog); + exit(1); +} |