diff options
Diffstat (limited to 'usr.bin')
424 files changed, 33810 insertions, 2808 deletions
diff --git a/usr.bin/Makefile b/usr.bin/Makefile index 6497743..b6fdcae 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -5,7 +5,7 @@ # XXX MISSING: deroff diction graph learn plot # spell spline struct xsend -# XXX Use GNU versions: apropos bc dc diff grep ld man patch whatis +# XXX Use GNU versions: apropos diff grep ld man patch whatis # Moved to secure: bdes # @@ -18,6 +18,7 @@ SUBDIR= alias \ awk \ banner \ basename \ + ${_bc} \ ${_biff} \ ${_bluetooth} \ brandelf \ @@ -49,6 +50,7 @@ SUBDIR= alias \ ${_csup} \ ${_ctags} \ cut \ + ${_dc} \ ${_dig} \ dirname \ du \ @@ -64,7 +66,7 @@ SUBDIR= alias \ ${_file2c} \ find \ finger \ - ${_fmt} \ + fmt \ fold \ ${_from} \ fstat \ @@ -170,6 +172,7 @@ SUBDIR= alias \ ${_rwho} \ script \ sed \ + seq \ shar \ showmount \ ${_smbutil} \ @@ -203,6 +206,7 @@ SUBDIR= alias \ unexpand \ ${_unifdef} \ uniq \ + unzip \ units \ unvis \ ${_usbhidaction} \ @@ -224,6 +228,7 @@ SUBDIR= alias \ who \ whois \ write \ + wtmpcvt \ xargs \ xinstall \ ${_xlint} \ @@ -276,7 +281,9 @@ _hesinfo= hesinfo .endif .if ${MK_OPENSSL} != "no" +_bc= bc _chkey= chkey +_dc= dc _newkey= newkey .if ${MK_LIBTHR} != "no" _csup= csup @@ -290,7 +297,6 @@ _locate= locate # XXX msgs? .if ${MK_MAIL} != "no" _biff= biff -_fmt= fmt _from= from _mail= mail _msgs= msgs @@ -384,4 +390,8 @@ _smbutil= smbutil _smbutil= smbutil .endif +.if ${MACHINE_ARCH} == "sparc64" +_smbutil= smbutil +.endif + .include <bsd.subdir.mk> diff --git a/usr.bin/Makefile.inc b/usr.bin/Makefile.inc index 2ee98c3..534349f 100644 --- a/usr.bin/Makefile.inc +++ b/usr.bin/Makefile.inc @@ -3,6 +3,4 @@ BINDIR?= /usr/bin -# Commented out for the time being. -# I intend to make this default at some stage. -#WARNS?= 2 +WARNS?= 6 diff --git a/usr.bin/apply/Makefile b/usr.bin/apply/Makefile index 0355588..ca0f10a 100644 --- a/usr.bin/apply/Makefile +++ b/usr.bin/apply/Makefile @@ -2,6 +2,5 @@ # $FreeBSD$ PROG= apply -WARNS?= 4 .include <bsd.prog.mk> diff --git a/usr.bin/ar/Makefile b/usr.bin/ar/Makefile index 533f931..1f00aca 100644 --- a/usr.bin/ar/Makefile +++ b/usr.bin/ar/Makefile @@ -3,8 +3,6 @@ PROG= ar SRCS= ar.c acplex.l acpyacc.y read.c util.c write.c y.tab.h -WARNS?= 5 - DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ} ${LIBELF} LDADD= -larchive -lbz2 -lz -lelf diff --git a/usr.bin/ar/acpyacc.y b/usr.bin/ar/acpyacc.y index bc25b62..b686c3f5 100644 --- a/usr.bin/ar/acpyacc.y +++ b/usr.bin/ar/acpyacc.y @@ -251,7 +251,7 @@ arscp_open(char *fname) if ((a = archive_read_new()) == NULL) bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed"); archive_read_support_compression_all(a); - archive_read_support_format_all(a); + archive_read_support_format_ar(a); AC(archive_read_open_file(a, fname, DEF_BLKSZ)); if ((r = archive_read_next_header(a, &entry))) bsdar_warnc(bsdar, 0, "%s", archive_error_string(a)); @@ -414,7 +414,7 @@ arscp_extract(struct list *list) /* List modules of archive. (Simple Mode) */ static void -arscp_list() +arscp_list(void) { if (!arscp_target_exist()) @@ -483,7 +483,7 @@ arscp_replace(struct list *list) /* Rename the temporary archive to the target archive. */ static void -arscp_save() +arscp_save(void) { mode_t mask; @@ -512,7 +512,7 @@ arscp_save() * invoking CREATE cmd on current archive. */ static void -arscp_clear() +arscp_clear(void) { char *new_target; @@ -549,7 +549,7 @@ arscp_end(int eval) * issued by user. */ static int -arscp_target_exist() +arscp_target_exist(void) { if (target) @@ -624,7 +624,7 @@ arscp_mlist2argv(struct list *list) /* Free space allocated for argv array and its elements. */ static void -arscp_free_argv() +arscp_free_argv(void) { int i; @@ -636,7 +636,7 @@ arscp_free_argv() /* Show a prompt if we are in interactive mode */ static void -arscp_prompt() +arscp_prompt(void) { if (interactive) { diff --git a/usr.bin/ar/ar.c b/usr.bin/ar/ar.c index 35a0380..dc4b6c4 100644 --- a/usr.bin/ar/ar.c +++ b/usr.bin/ar/ar.c @@ -349,7 +349,7 @@ only_mode(struct bsdar *bsdar, const char *opt, const char *valid_modes) } static void -bsdar_usage() +bsdar_usage(void) { (void)fprintf(stderr, "usage: ar -d [-Tjsvz] archive file ...\n"); @@ -367,7 +367,7 @@ bsdar_usage() } static void -ranlib_usage() +ranlib_usage(void) { (void)fprintf(stderr, "usage: ranlib [-t] archive ...\n"); @@ -376,14 +376,14 @@ ranlib_usage() } static void -bsdar_version() +bsdar_version(void) { (void)printf("BSD ar %s - %s\n", BSDAR_VERSION, archive_version()); exit(EX_OK); } static void -ranlib_version() +ranlib_version(void) { (void)printf("ranlib %s - %s\n", BSDAR_VERSION, archive_version()); exit(EX_OK); diff --git a/usr.bin/ar/read.c b/usr.bin/ar/read.c index a9e2ed1..37bd0a1 100644 --- a/usr.bin/ar/read.c +++ b/usr.bin/ar/read.c @@ -88,7 +88,7 @@ read_archive(struct bsdar *bsdar, char mode) if ((a = archive_read_new()) == NULL) bsdar_errc(bsdar, EX_SOFTWARE, 0, "archive_read_new failed"); archive_read_support_compression_all(a); - archive_read_support_format_all(a); + archive_read_support_format_ar(a); AC(archive_read_open_file(a, bsdar->filename, DEF_BLKSZ)); for (;;) { diff --git a/usr.bin/at/Makefile b/usr.bin/at/Makefile index 47d2574..75546fc 100644 --- a/usr.bin/at/Makefile +++ b/usr.bin/at/Makefile @@ -11,6 +11,8 @@ MLINKS= at.1 batch.1 \ at.1 atq.1 \ at.1 atrm.1 +WARNS?= 1 + BINOWN= root BINMODE= 4555 CLEANFILES+= at.1 diff --git a/usr.bin/at/at.c b/usr.bin/at/at.c index 71d8e60..fbba7bd 100644 --- a/usr.bin/at/at.c +++ b/usr.bin/at/at.c @@ -179,7 +179,7 @@ static char *cwdname(void) } static long -nextjob() +nextjob(void) { long jobno; FILE *fid; diff --git a/usr.bin/at/at.man b/usr.bin/at/at.man index bca1051..a876974 100644 --- a/usr.bin/at/at.man +++ b/usr.bin/at/at.man @@ -313,7 +313,7 @@ letter pair is not specified, the value defaults to 0. directory containing job files .It Pa _ATSPOOL_DIR directory containing output spool files -.It Pa /var/run/utmp +.It Pa /var/run/utx.active login records .It Pa _PERM_PATH/at.allow allow permission control @@ -338,7 +338,7 @@ with minor enhancements by .An Joe Halpin Aq joe.halpin@attbi.com . .Sh BUGS If the file -.Pa /var/run/utmp +.Pa /var/run/utx.active is not available or corrupted, or if the user is not logged on at the time .Nm diff --git a/usr.bin/atm/sscop/Makefile b/usr.bin/atm/sscop/Makefile index 16703bb..36d3dcf 100644 --- a/usr.bin/atm/sscop/Makefile +++ b/usr.bin/atm/sscop/Makefile @@ -6,7 +6,6 @@ CONTRIB= ${.CURDIR}/../../../contrib/ngatm/sscop PROG= sscop SRCS= common.c sscop_main.c -WARNS?= 6 CFLAGS+= -I${CONTRIB} -DUSE_LIBBEGEMOT DPADD= ${LIBBEGEMOT} ${LIBNETGRAPH} ${LIBNGATM} diff --git a/usr.bin/awk/Makefile b/usr.bin/awk/Makefile index 7ec2e57..6538636 100644 --- a/usr.bin/awk/Makefile +++ b/usr.bin/awk/Makefile @@ -8,6 +8,8 @@ SRCS= awkgram.y b.c lex.c lib.c main.c parse.c proctab.c run.c tran.c ytab.h CFLAGS+= -DHAS_ISBLANK -I. -I${AWKSRC} -DFOPEN_MAX=64 +WARNS?= 1 + DPADD= ${LIBM} LDADD= -lm @@ -25,10 +27,4 @@ proctab.c: maketab build-tools: maketab maketab: ytab.h ${AWKSRC}/maketab.c -.for f in b.c main.c run.c -${f}: ${AWKSRC}/${f} ${.CURDIR}/${f}.diff - patch -s -b .orig -o ${.TARGET} < ${.CURDIR}/${f}.diff ${AWKSRC}/${f} -CLEANFILES+= ${f} -.endfor - .include <bsd.prog.mk> diff --git a/usr.bin/awk/b.c.diff b/usr.bin/awk/b.c.diff deleted file mode 100644 index 525fcbc..0000000 --- a/usr.bin/awk/b.c.diff +++ /dev/null @@ -1,53 +0,0 @@ -$FreeBSD$ - -Index: b.c -=================================================================== -RCS file: /home/ncvs/src/contrib/one-true-awk/b.c,v -retrieving revision 1.1.1.8 -diff -u -p -r1.1.1.8 b.c ---- b.c 16 May 2005 19:11:31 -0000 1.1.1.8 -+++ b.c 16 May 2005 19:12:40 -0000 -@@ -282,9 +282,21 @@ int quoted(char **pp) /* pick up next th - return c; - } - -+static int collate_range_cmp(int a, int b) -+{ -+ static char s[2][2]; -+ -+ if ((uschar)a == (uschar)b) -+ return 0; -+ s[0][0] = a; -+ s[1][0] = b; -+ return (strcoll(s[0], s[1])); -+} -+ - char *cclenter(const char *argp) /* add a character class */ - { - int i, c, c2; -+ int j; - uschar *p = (uschar *) argp; - uschar *op, *bp; - static uschar *buf = 0; -@@ -303,15 +315,18 @@ char *cclenter(const char *argp) /* add - c2 = *p++; - if (c2 == '\\') - c2 = quoted((char **) &p); -- if (c > c2) { /* empty; ignore */ -+ if (collate_range_cmp(c, c2) > 0) { - bp--; - i--; - continue; - } -- while (c < c2) { -+ for (j = 0; j < NCHARS; j++) { -+ if ((collate_range_cmp(c, j) > 0) || -+ collate_range_cmp(j, c2) > 0) -+ continue; - if (!adjbuf((char **) &buf, &bufsz, bp-buf+2, 100, (char **) &bp, "cclenter1")) - FATAL("out of space for character class [%.10s...] 2", p); -- *bp++ = ++c; -+ *bp++ = j; - i++; - } - continue; diff --git a/usr.bin/awk/main.c.diff b/usr.bin/awk/main.c.diff deleted file mode 100644 index 252cd5a..0000000 --- a/usr.bin/awk/main.c.diff +++ /dev/null @@ -1,69 +0,0 @@ -$FreeBSD$ - -Index: main.c -=================================================================== -RCS file: /home/ncvs/src/contrib/one-true-awk/main.c,v -retrieving revision 1.1.1.10 -diff -u -p -r1.1.1.10 main.c ---- main.c 16 May 2005 19:11:31 -0000 1.1.1.10 -+++ main.c 15 Sep 2006 13:21:30 -0000 -@@ -22,7 +22,7 @@ ARISING OUT OF OR IN CONNECTION WITH THE - THIS SOFTWARE. - ****************************************************************/ - --const char *version = "version 20070501"; -+const char *version = "version 20070501 (FreeBSD)"; - - #define DEBUG - #include <stdio.h> -@@ -58,6 +58,7 @@ int main(int argc, char *argv[]) - const char *fs = NULL; - - setlocale(LC_CTYPE, ""); -+ setlocale(LC_COLLATE, ""); - setlocale(LC_NUMERIC, "C"); /* for parsing cmdline & prog */ - cmdname = argv[0]; - if (argc == 1) { -@@ -79,13 +80,18 @@ int main(int argc, char *argv[]) - safe = 1; - break; - case 'f': /* next argument is program filename */ -- argc--; -- argv++; -- if (argc <= 1) -- FATAL("no program filename"); -- if (npfile >= MAX_PFILE - 1) -- FATAL("too many -f options"); -- pfile[npfile++] = argv[1]; -+ if (argv[1][2] != 0) { /* arg is -fsomething */ -+ if (npfile >= MAX_PFILE - 1) -+ FATAL("too many -f options"); -+ pfile[npfile++] = &argv[1][2]; -+ } else { /* arg is -f something */ -+ argc--; argv++; -+ if (argc <= 1) -+ FATAL("no program filename"); -+ if (npfile >= MAX_PFILE - 1) -+ FATAL("too many -f options"); -+ pfile[npfile++] = argv[1]; -+ } - break; - case 'F': /* set field separator */ - if (argv[1][2] != 0) { /* arg is -Fsomething */ -@@ -104,8 +110,14 @@ int main(int argc, char *argv[]) - WARNING("field separator FS is empty"); - break; - case 'v': /* -v a=1 to be done NOW. one -v for each */ -- if (argv[1][2] == '\0' && --argc > 1 && isclvar((++argv)[1])) -- setclvar(argv[1]); -+ if (argv[1][2] != 0) { /* arg is -vsomething */ -+ if (argv[1][2] != 0) -+ setclvar(&argv[1][2]); -+ } else { /* arg is -v something */ -+ argc--; argv++; -+ if (argc > 1 && isclvar(argv[1])) -+ setclvar(argv[1]); -+ } - break; - case 'm': /* more memory: -mr=record, -mf=fields */ - /* no longer supported */ diff --git a/usr.bin/awk/run.c.diff b/usr.bin/awk/run.c.diff deleted file mode 100644 index d491250..0000000 --- a/usr.bin/awk/run.c.diff +++ /dev/null @@ -1,18 +0,0 @@ -$FreeBSD$ - -Index: run.c -=================================================================== -RCS file: /home/ncvs/src/contrib/one-true-awk/run.c,v -retrieving revision 1.1.1.8 -diff -u -p -r1.1.1.8 run.c ---- run.c 16 May 2005 19:11:35 -0000 1.1.1.8 -+++ run.c 16 May 2005 19:12:47 -0000 -@@ -651,7 +651,7 @@ Cell *relop(Node **a, int n) /* a[0 < a[ - j = x->fval - y->fval; - i = j<0? -1: (j>0? 1: 0); - } else { -- i = strcmp(getsval(x), getsval(y)); -+ i = strcoll(getsval(x), getsval(y)); - } - tempfree(x); - tempfree(y); diff --git a/usr.bin/banner/banner.c b/usr.bin/banner/banner.c index eb96a15..b87aa40 100644 --- a/usr.bin/banner/banner.c +++ b/usr.bin/banner/banner.c @@ -1178,7 +1178,7 @@ main(int argc, char *argv[]) } static void -usage() +usage(void) { fprintf(stderr, "usage: banner [-d] [-t] [-w width] message ...\n"); exit(1); diff --git a/usr.bin/bc/Makefile b/usr.bin/bc/Makefile new file mode 100644 index 0000000..55f465d --- /dev/null +++ b/usr.bin/bc/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD$ +# $OpenBSD: Makefile,v 1.4 2006/06/30 19:02:28 otto Exp $ + +PROG= bc +SRCS= bc.y scan.l +CFLAGS+= -I. -I${.CURDIR} + +DPADD= ${LIBEDIT} ${LIBTERMCAP} +LDADD= -ledit -ltermcap + +FILES+= bc.library +FILESDIR=${SHAREDIR}/misc + +.include <bsd.prog.mk> diff --git a/usr.bin/bc/USD.doc/bc b/usr.bin/bc/USD.doc/bc new file mode 100644 index 0000000..c4e68c6 --- /dev/null +++ b/usr.bin/bc/USD.doc/bc @@ -0,0 +1,1241 @@ +.\" $FreeBSD$ +.\" $OpenBSD: bc,v 1.9 2004/07/09 10:23:05 jmc Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. +.\" 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 and documentation 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 or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of other +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, 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 CALDERA INTERNATIONAL, INC. 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. +.\" +.\" @(#)bc 6.2 (Berkeley) 4/17/91 +.\" +.if n \{\ +.po 5n +.ll 70n +.\} +.EH 'USD:6-%''BC \- An Arbitrary Precision Desk-Calculator Language' +.OH 'BC \- An Arbitrary Precision Desk-Calculator Language''USD:6-%' +.\".RP +.TL +BC \- An Arbitrary Precision Desk-Calculator Language +.AU +Lorinda Cherry +.AU +Robert Morris +.AI +.\" .MH +.AB +BC is a language and a compiler for doing arbitrary precision arithmetic +on the PDP-11 under the +.UX +time-sharing +system. The output of the compiler is interpreted and executed by +a collection of routines which can input, output, and do +arithmetic on indefinitely large integers and on scaled fixed-point +numbers. +.PP +These routines are themselves based on a dynamic storage allocator. +Overflow does not occur until all available core storage +is exhausted. +.PP +The language has a complete control structure as well as immediate-mode +operation. Functions can be defined and saved for later execution. +.PP +Two five hundred-digit numbers can be multiplied to give a +thousand digit result in about ten seconds. +.PP +A small collection of library functions is also available, +including sin, cos, arctan, log, exponential, and Bessel functions of +integer order. +.PP +Some of the uses of this compiler are +.IP \- +to do computation with large integers, +.IP \- +to do computation accurate to many decimal places, +.IP \- +conversion of numbers from one base to another base. +.AE +.PP +.SH +Introduction +.PP +BC is a language and a compiler for doing arbitrary precision +arithmetic on the +.UX +time-sharing system [1]. +The compiler was written to make conveniently available a +collection of routines (called DC [5]) which are capable of doing +arithmetic on integers of arbitrary size. The compiler +is by no means intended to provide a complete programming +language. +It is a minimal language facility. +.PP +There is a scaling provision that permits the +use of decimal point notation. +Provision is made for input and output in bases other than +decimal. Numbers can be converted from decimal to octal by +simply setting the output base to equal 8. +.PP +The actual limit on the number of digits that can +be handled depends on the amount of storage available on the machine. +Manipulation of numbers with many hundreds of digits +is possible even on the smallest versions of +.UX . +.PP +The syntax of BC has been deliberately selected to agree +substantially with the C language [2]. Those who +are familiar with C will find few surprises in this language. +.SH +Simple Computations with Integers +.PP +The simplest kind of statement is an arithmetic expression +on a line by itself. +For instance, if you type in the line: +.DS +.ft B +142857 + 285714 +.ft P +.DE +the program responds immediately with the line +.DS +.ft B +428571 +.ft P +.DE +The operators \-, *, /, %, and ^ can also be used; they +indicate subtraction, multiplication, division, remaindering, and +exponentiation, respectively. Division of integers produces an +integer result truncated toward zero. +Division by zero produces an error +comment. +.PP +Any term in an expression may be prefixed by a minus sign to +indicate that it is to be negated (the `unary' minus sign). +The expression +.DS +.ft B +7+\-3 +.ft P +.DE +is interpreted to mean that \-3 is to be added to 7. +.PP +More complex expressions with several operators and with +parentheses are interpreted just as in +Fortran, with ^ having the greatest binding +power, then * and % and /, and finally + and \-. +Contents of parentheses are evaluated before material +outside the parentheses. +Exponentiations are +performed from right to left and the other operators +from left to right. +The two expressions +.DS +.ft B +a^b^c and a^(b^c) +.ft P +.DE +are equivalent, as are the two expressions +.DS +.ft B +a*b*c and (a*b)*c +.ft P +.DE +BC shares with Fortran and C the undesirable convention that +.DS +\fBa/b*c\fP is equivalent to \fB(a/b)*c\fP +.ft P +.DE +.PP +Internal storage registers to hold numbers have single lower-case +letter names. The value of an expression can be assigned to +a register in the usual way. The statement +.DS +.ft B +x = x + 3 +.ft P +.DE +has the effect of increasing by three the value of the contents of the +register named x. +When, as in this case, the outermost operator is an =, the +assignment is performed but the result is not printed. +Only 26 of these named storage registers are available. +.PP +There is a built-in square root function whose +result is truncated to an integer (but see scaling below). +The lines +.DS +.ft B +x = sqrt(191) +x +.ft P +.DE +produce the printed result +.DS +.ft B +13 +.ft P +.DE +.SH +Bases +.PP +There are special internal quantities, called `ibase' and `obase'. +The contents of `ibase', initially set to 10, +determines the base used for interpreting numbers read in. +For example, the lines +.DS +.ft B +ibase = 8 +11 +.ft P +.DE +will produce the output line +.DS +.ft B +9 +.ft P +.DE +and you are all set up to do octal to decimal conversions. +Beware, however of trying to change the input base back +to decimal by typing +.DS +.ft B +ibase = 10 +.ft P +.DE +Because the number 10 is interpreted as octal, this statement will +have no effect. +For those who deal in hexadecimal notation, +the characters A\-F are permitted in numbers +(no matter what base is in effect) +and are +interpreted as digits having values 10\-15 respectively. +The statement +.DS +.ft B +ibase = A +.ft P +.DE +will change you back to decimal input base no matter what the +current input base is. +Negative and large positive input bases are +permitted but useless. +No mechanism has been provided for the input of arbitrary +numbers in bases less than 1 and greater than 16. +.PP +The contents of `obase', initially set to 10, are used as the base for output +numbers. The lines +.DS +.ft B +obase = 16 +1000 +.ft P +.DE +will produce the output line +.DS +.ft B +3E8 +.ft P +.DE +which is to be interpreted as a 3-digit hexadecimal number. +Very large output bases are permitted, and they are sometimes useful. +For example, large numbers can be output in groups of five digits +by setting `obase' to 100000. +Strange (i.e. 1, 0, or negative) output bases are +handled appropriately. +.PP +Very large numbers are split across lines with 70 characters per line. +Lines which are continued end with \\. +Decimal output conversion is practically instantaneous, but output +of very large numbers (i.e., more than 100 digits) with other bases +is rather slow. +Non-decimal output conversion of +a one hundred digit number takes about +three seconds. +.PP +It is best to remember that `ibase' and `obase' have no effect +whatever on the course of internal computation or +on the evaluation of expressions, but only affect input and +output conversion, respectively. +.SH +Scaling +.PP +A third special internal quantity called `scale' is +used to determine the scale of calculated +quantities. +Numbers may have +up to a specific number of decimal digits after the decimal point. +This fractional part is retained in further computations. +We refer to the number of digits after the decimal point of +a number as its scale. +The current implementation allows scales to be as large as can be +represented by a 32-bit unsigned number minus one. +This is a non-portable extension. +The original implementation allowed for a maximum scale of 99. +.PP +When two scaled numbers are combined by +means of one of the arithmetic operations, the result +has a scale determined by the following rules. For +addition and subtraction, the scale of the result is the larger +of the scales of the two operands. In this case, +there is never any truncation of the result. +For multiplications, the scale of the result is never +less than the maximum of the two scales of the operands, +never more than the sum of the scales of the operands +and, subject to those two restrictions, +the scale of the result is set equal to the contents of the internal +quantity `scale'. +The scale of a quotient is the contents of the internal +quantity `scale'. The scale of a remainder is +the sum of the scales of the quotient and the divisor. +The result of an exponentiation is scaled as if +the implied multiplications were performed. +An exponent must be an integer. +The scale of a square root is set to the maximum of the scale +of the argument and the contents of `scale'. +.PP +All of the internal operations are actually carried out in terms +of integers, with digits being discarded when necessary. +In every case where digits are discarded, truncation and +not rounding is performed. +.PP +The contents of +`scale' must be no greater than +4294967294 and no less than 0. It is initially set to 0. +.PP +The internal quantities `scale', `ibase', and `obase' can be +used in expressions just like other variables. +The line +.DS +.ft B +scale = scale + 1 +.ft P +.DE +increases the value of `scale' by one, and the line +.DS +.ft B +scale +.ft P +.DE +causes the current value of `scale' to be printed. +.PP +The value of `scale' retains its meaning as a +number of decimal digits to be retained in internal +computation even when `ibase' or `obase' are not equal to 10. +The internal computations (which are still conducted in decimal, +regardless of the bases) are performed to the specified number +of decimal digits, never hexadecimal or octal or any +other kind of digits. +.SH +Functions +.PP +The name of a function is a single lower-case letter. +Function names are permitted to collide with simple +variable names. +Twenty-six different defined functions are permitted +in addition to the twenty-six variable names. +The line +.DS +.ft B + define a(x){ +.ft P +.DE +begins the definition of a function with one argument. +This line must be followed by one or more statements, +which make up the body of the function, ending +with a right brace }. +Return of control from a function occurs when a return +statement is executed or when the end of the function is reached. +The return statement can take either +of the two forms +.DS +.ft B +return +return(x) +.ft P +.DE +In the first case, the value of the function is 0, and in +the second, the value of the expression in parentheses. +.PP +Variables used in the function can be declared as automatic +by a statement of the form +.DS +.ft B +auto x,y,z +.ft P +.DE +There can be only one `auto' statement in a function and it must +be the first statement in the definition. +These automatic variables are allocated space and initialized +to zero on entry to the function and thrown away on return. The +values of any variables with the same names outside the function +are not disturbed. +Functions may be called recursively and the automatic variables +at each level of call are protected. +The parameters named in a function definition are treated in +the same way as the automatic variables of that function +with the single exception that they are given a value +on entry to the function. +An example of a function definition is +.DS +.ft B + define a(x,y){ + auto z + z = x*y + return(z) + } +.ft P +.DE +The value of this function, when called, will be the +product of its +two arguments. +.PP +A function is called by the appearance of its name +followed by a string of arguments enclosed in +parentheses and separated by commas. +The result +is unpredictable if the wrong number of arguments is used. +.PP +Functions with no arguments are defined and called using +parentheses with nothing between them: b(). +.PP +If the function +.ft I +a +.ft +above has been defined, then the line +.DS +.ft B +a(7,3.14) +.ft P +.DE +would cause the result 21.98 to be printed and the line +.DS +.ft B +x = a(a(3,4),5) +.ft P +.DE +would cause the value of x to become 60. +.SH +Subscripted Variables +.PP +A single lower-case letter variable name +followed by an expression in brackets is called a subscripted +variable (an array element). +The variable name is called the array name and the expression +in brackets is called the subscript. +Only one-dimensional arrays are +permitted. The names of arrays are permitted to +collide with the names of simple variables and function names. +Any fractional +part of a subscript is discarded before use. +Subscripts must be greater than or equal to zero and +less than or equal to 2047. +.PP +Subscripted variables may be freely used in expressions, in +function calls, and in return statements. +.PP +An array name may be used as an argument to a function, +or may be declared as automatic in +a function definition by the use of empty brackets: +.DS +.ft B +f(a[\|]) +define f(a[\|]) +auto a[\|] +.ft P +.DE +When an array name is so used, the whole contents of the array +are copied for the use of the function, and thrown away on exit +from the function. +Array names which refer to whole arrays cannot be used +in any other contexts. +.SH +Control Statements +.PP +The `if', the `while', and the `for' statements +may be used to alter the flow within programs or to cause iteration. +The range of each of them is a statement or +a compound statement consisting of a collection of +statements enclosed in braces. +They are written in the following way +.DS +.ft B +if(relation) statement +if(relation) statement else statement +while(relation) statement +for(expression1; relation; expression2) statement +.ft P +.DE +or +.DS +.ft B +if(relation) {statements} +if(relation) {statements} else {statements} +while(relation) {statements} +for(expression1; relation; expression2) {statements} +.ft P +.DE +.PP +A relation in one of the control statements is an expression of the form +.DS +.ft B +x>y +.ft P +.DE +where two expressions are related by one of the six relational +operators `<', `>', `<=', `>=', `==', or `!='. +The relation `==' +stands for `equal to' and `!=' stands for `not equal to'. +The meaning of the remaining relational operators is +clear. +.PP +BEWARE of using `=' instead of `==' in a relational. Unfortunately, +both of them are legal, so you will not get a diagnostic +message, but `=' really will not do a comparison. +.PP +The `if' statement causes execution of its range +if and only if the relation is true. +Then control passes to the next statement in sequence. +If an `else' branch is present, the statements in this branch are +executed if the relation is false. +The `else' keyword is a non-portable extension. +.PP +The `while' statement causes execution of its range +repeatedly as long as the relation +is true. The relation is tested before each execution +of its range and if the relation +is false, control passes to the next statement beyond the range +of the while. +.PP +The `for' statement begins +by executing `expression1'. Then the relation is tested +and, if true, the statements in the range of the `for' are executed. +Then `expression2' is executed. The relation is tested, and so on. +The typical use of the `for' statement is for a controlled iteration, +as in the statement +.DS +.ft B +for(i=1; i<=10; i=i+1) i +.ft P +.DE +which will print the integers from 1 to 10. +Here are some examples of the use of the control statements. +.DS +.ft B +define f(n){ +auto i, x +x=1 +for(i=1; i<=n; i=i+1) x=x*i +return(x) +} +.ft P +.DE +The line +.DS +.ft B + f(a) +.ft P +.DE +will print +.ft I +a +.ft +factorial if +.ft I +a +.ft +is a positive integer. +Here is the definition of a function which will +compute values of the binomial coefficient +(m and n are assumed to be positive integers). +.DS +.ft B +define b(n,m){ +auto x, j +x=1 +for(j=1; j<=m; j=j+1) x=x*(n\-j+1)/j +return(x) +} +.ft P +.DE +The following function computes values of the exponential function +by summing the appropriate series +without regard for possible truncation errors: +.DS +.ft B +scale = 20 +define e(x){ + auto a, b, c, d, n + a = 1 + b = 1 + c = 1 + d = 0 + n = 1 + while(1==1){ + a = a*x + b = b*n + c = c + a/b + n = n + 1 + if(c==d) return(c) + d = c + } +} +.ft P +.DE +.SH +Some Details +.PP +There are some language features that every user should know +about even if he will not use them. +.PP +Normally statements are typed one to a line. It is also permissible +to type several statements on a line separated by semicolons. +.PP +If an assignment statement is parenthesized, it then has +a value and it can be used anywhere that an expression can. +For example, the line +.DS +.ft B +(x=y+17) +.ft P +.DE +not only makes the indicated assignment, but also prints the +resulting value. +.PP +Here is an example of a use of the value of an +assignment statement even when it is not parenthesized. +.DS +.ft B +x = a[i=i+1] +.ft P +.DE +causes a value to be assigned to x and also increments i +before it is used as a subscript. +.PP +The following constructs work in BC in exactly the same manner +as they do in the C language. Consult the appendix or the +C manuals [2] for their exact workings. +.DS +.ft B +.ta 2i +x=y=z is the same as x=(y=z) +x += y x = x+y +x \-= y x = x\-y +x *= y x = x*y +x /= y x = x/y +x %= y x = x%y +x ^= y x = x^y +x++ (x=x+1)\-1 +x\-\- (x=x\-1)+1 +++x x = x+1 +\-\-x x = x\-1 +.ft P +.DE +Even if you don't intend to use the constructs, +if you type one inadvertently, something correct but unexpected +may happen. +.SH +Three Important Things +.PP +1. To exit a BC program, type `quit'. +.PP +2. There is a comment convention identical to that of C and +of PL/I. Comments begin with `/*' and end with `*/'. +As a non-portable extension, comments may also start with a `#' and end with +a newline. +The newline is not part of the comment. +.PP +3. There is a library of math functions which may be obtained by +typing at command level +.DS +.ft B +bc \-l +.ft P +.DE +This command will load a set of library functions +which, at the time of writing, consists of sine (named `s'), +cosine (`c'), arctangent (`a'), natural logarithm (`l'), +exponential (`e') and Bessel functions of integer order (`j(n,x)'). Doubtless more functions will be added +in time. +The library sets the scale to 20. You can reset it to something +else if you like. +The design of these mathematical library routines +is discussed elsewhere [3]. +.PP +If you type +.DS +.ft B +bc file ... +.ft P +.DE +BC will read and execute the named file or files before accepting +commands from the keyboard. In this way, you may load your +favorite programs and function definitions. +.SH +Acknowledgement +.PP +The compiler is written in YACC [4]; its original +version was written by S. C. Johnson. +.SH +References +.IP [1] +K. Thompson and D. M. Ritchie, +.ft I +UNIX Programmer's Manual, +.ft +Bell Laboratories, +1978. +.IP [2] +B. W. Kernighan and +D. M. Ritchie, +.ft I +The C Programming Language, +.ft +Prentice-Hall, 1978. +.IP [3] +R. Morris, +.ft I +A Library of Reference Standard Mathematical Subroutines, +.ft +Bell Laboratories internal memorandum, 1975. +.IP [4] +S. C. Johnson, +.ft I +YACC \(em Yet Another Compiler-Compiler. +.ft +Bell Laboratories Computing Science Technical Report #32, 1978. +.IP [5] +R. Morris and L. L. Cherry, +.ft I +DC \- An Interactive Desk Calculator. +.ft +.LP +.bp +.ft B +.DS C +Appendix +.DE +.ft +.NH +Notation +.PP +In the following pages syntactic categories are in \fIitalics\fP; +literals are in \fBbold\fP; material in brackets [\|] is optional. +.NH +Tokens +.PP +Tokens consist of keywords, identifiers, constants, operators, +and separators. +Token separators may be blanks, tabs or comments. +Newline characters or semicolons separate statements. +.NH 2 +Comments +.PP +Comments are introduced by the characters /* and terminated by +*/. +As a non-portable extension, comments may also start with a # and +end with a newline. +The newline is not part of the comment. +.NH 2 +Identifiers +.PP +There are three kinds of identifiers \- ordinary identifiers, array identifiers +and function identifiers. +All three types consist of single lower-case letters. +Array identifiers are followed by square brackets, possibly +enclosing an expression describing a subscript. +Arrays are singly dimensioned and may contain up to 2048 +elements. +Indexing begins at zero so an array may be indexed from 0 to 2047. +Subscripts are truncated to integers. +Function identifiers are followed by parentheses, possibly enclosing arguments. +The three types of identifiers do not conflict; +a program can have a variable named \fBx\fP, +an array named \fBx\fP and a function named \fBx\fP, all of which are separate and +distinct. +.NH 2 +Keywords +.PP +The following are reserved keywords: +.ft B +.ta .5i 1.0i +.nf + ibase if + obase break + scale define + sqrt auto + length return + while quit + for continue + else last + print +.fi +.ft +.NH 2 +Constants +.PP +Constants consist of arbitrarily long numbers +with an optional decimal point. +The hexadecimal digits \fBA\fP\-\fBF\fP are also recognized as digits with +values 10\-15, respectively. +.NH 1 +Expressions +.PP +The value of an expression is printed unless the main +operator is an assignment. +The value printed is assigned to the special variable \fBlast\fP. +A single dot may be used as a synonym for \fBlast\fP. +This is a non-portable extension. +Precedence is the same as the order +of presentation here, with highest appearing first. +Left or right associativity, where applicable, is +discussed with each operator. +.bp +.NH 2 +Primitive expressions +.NH 3 +Named expressions +.PP +Named expressions are +places where values are stored. +Simply stated, +named expressions are legal on the left +side of an assignment. +The value of a named expression is the value stored in the place named. +.NH 4 +\fIidentifiers\fR +.PP +Simple identifiers are named expressions. +They have an initial value of zero. +.NH 4 +\fIarray-name\fP\|[\|\fIexpression\fP\|] +.PP +Array elements are named expressions. +They have an initial value of zero. +.NH 4 +\fBscale\fR, \fBibase\fR and \fBobase\fR +.PP +The internal registers +\fBscale\fP, \fBibase\fP and \fBobase\fP are all named expressions. +\fBscale\fP is the number of digits after the decimal point to be +retained in arithmetic operations. +\fBscale\fR has an initial value of zero. +\fBibase\fP and \fBobase\fP are the input and output number +radix respectively. +Both \fBibase\fR and \fBobase\fR have initial values of 10. +.NH 3 +Function calls +.NH 4 +\fIfunction-name\fB\|(\fR[\fIexpression\fR\|[\fB,\|\fIexpression\|\fR.\|.\|.\|]\|]\fB) +.PP +A function call consists of a function name followed by parentheses +containing a comma-separated list of +expressions, which are the function arguments. +A whole array passed as an argument is specified by the +array name followed by empty square brackets. +All function arguments are passed by +value. +As a result, changes made to the formal parameters have +no effect on the actual arguments. +If the function terminates by executing a return +statement, the value of the function is +the value of the expression in the parentheses of the return +statement or is zero if no expression is provided +or if there is no return statement. +.NH 4 +sqrt\|(\|\fIexpression\fP\|) +.PP +The result is the square root of the expression. +The result is truncated in the least significant decimal place. +The scale of the result is +the scale of the expression or the +value of +.ft B +scale, +.ft +whichever is larger. +.NH 4 +length\|(\|\fIexpression\fP\|) +.PP +The result is the total number of significant decimal digits in the expression. +The scale of the result is zero. +.NH 4 +scale\|(\|\fIexpression\fP\|) +.PP +The result is the scale of the expression. +The scale of the result is zero. +.NH 3 +Constants +.PP +Constants are primitive expressions. +.NH 3 +Parentheses +.PP +An expression surrounded by parentheses is +a primitive expression. +The parentheses are used to alter the +normal precedence. +.NH 2 +Unary operators +.PP +The unary operators +bind right to left. +.NH 3 +\-\|\fIexpression\fP +.PP +The result is the negative of the expression. +.NH 3 +++\|\fInamed-expression\fP +.PP +The named expression is +incremented by one. +The result is the value of the named expression after +incrementing. +.NH 3 +\-\-\|\fInamed-expression\fP +.PP +The named expression is +decremented by one. +The result is the value of the named expression after +decrementing. +.NH 3 +\fInamed-expression\fP\|++ +.PP +The named expression is +incremented by one. +The result is the value of the named expression before +incrementing. +.NH 3 +\fInamed-expression\fP\|\-\- +.PP +The named expression is +decremented by one. +The result is the value of the named expression before +decrementing. +.NH 2 +Exponentiation operator +.PP +The exponentiation operator binds right to left. +.NH 3 +\fIexpression\fP ^ \fIexpression\fP +.PP +The result is the first +expression raised to the power of the +second expression. +The second expression must be an integer. +If \fIa\fP +is the scale of the left expression +and \fIb\fP is the absolute value +of the right expression, +then the scale of the result is: +.PP +min\|(\|\fIa\(mub\fP,\|max\|(\|\fBscale\fP,\|\fIa\fP\|)\|) +.NH 2 +Multiplicative operators +.PP +The operators *, /, % bind left to right. +.NH 3 +\fIexpression\fP * \fIexpression\fP +.PP +The result is the product +of the two expressions. +If \fIa\fP and \fIb\fP are the +scales of the two expressions, +then the scale of the result is: +.PP +min\|(\|\fIa+b\fP,\|max\|(\|\fBscale\fP,\|\fIa\fP,\|\fIb\fP\|)\|) +.NH 3 +\fIexpression\fP / \fIexpression\fP +.PP +The result is the quotient of the two expressions. +The scale of the result is the value of \fBscale\fR. +.NH 3 +\fIexpression\fP % \fIexpression\fP +.PP +The % operator produces the remainder of the division +of the two expressions. +More precisely, +\fIa\fP%\fIb\fP is \fIa\fP\-\fIa\fP/\fIb\fP*\fIb\fP. +.PP +The scale of the result is the sum of the scale of +the divisor and the value of +.ft B +scale +.ft +.NH 2 +Additive operators +.PP +The additive operators bind left to right. +.NH 3 +\fIexpression\fP + \fIexpression\fP +.PP +The result is the sum of the two expressions. +The scale of the result is +the maximum of the scales of the expressions. +.NH 3 +\fIexpression\fP \- \fIexpression\fP +.PP +The result is the difference of the two expressions. +The scale of the result is the +maximum of the scales of the expressions. +.NH 2 +assignment operators +.PP +The assignment operators bind right to left. +.NH 3 +\fInamed-expression\fP = \fIexpression\fP +.PP +This expression results in assigning the value of the expression +on the right +to the named expression on the left. +.NH 3 +\fInamed-expression\fP += \fIexpression\fP +.NH 3 +\fInamed-expression\fP \-= \fIexpression\fP +.NH 3 +\fInamed-expression\fP *= \fIexpression\fP +.NH 3 +\fInamed-expression\fP /= \fIexpression\fP +.NH 3 +\fInamed-expression\fP %= \fIexpression\fP +.NH 3 +\fInamed-expression\fP ^= \fIexpression\fP +.PP +The result of the above expressions is equivalent +to ``named expression = named expression OP expression'', +where OP is the operator after the = sign. +.NH 1 +Relations +.PP +Unlike all other operators, the relational operators +are only valid as the object of an \fBif\fP, \fBwhile\fP, +or inside a \fBfor\fP statement. +.NH 2 +\fIexpression\fP < \fIexpression\fP +.NH 2 +\fIexpression\fP > \fIexpression\fP +.NH 2 +\fIexpression\fP <= \fIexpression\fP +.NH 2 +\fIexpression\fP >= \fIexpression\fP +.NH 2 +\fIexpression\fP == \fIexpression\fP +.NH 2 +\fIexpression\fP != \fIexpression\fP +.NH 1 +Storage classes +.PP +There are only two storage classes in BC, global and automatic +(local). +Only identifiers that are to be local to a function need be +declared with the \fBauto\fP command. +The arguments to a function +are local to the function. +All other identifiers are assumed to be global +and available to all functions. +All identifiers, global and local, have initial values +of zero. +Identifiers declared as \fBauto\fP are allocated on entry to the function +and released on returning from the function. +They therefore do not retain values between function calls. +\fBauto\fP arrays are specified by the array name followed by empty square brackets. +.PP +Automatic variables in BC do not work in exactly the same way +as in either C or PL/I. On entry to a function, the old values of +the names that appear as parameters and as automatic +variables are pushed onto a stack. +Until return is made from the function, reference to these +names refers only to the new values. +.NH 1 +Statements +.PP +Statements must be separated by semicolon or newline. +Except where altered by control statements, execution +is sequential. +.NH 2 +Expression statements +.PP +When a statement is an expression, unless +the main operator is an assignment, the value +of the expression is printed, followed by a newline character. +.NH 2 +Compound statements +.PP +Statements may be grouped together and used when one statement is expected +by surrounding them with { }. +.NH 2 +Quoted string statements +.PP +"any string" +.sp .5 +This statement prints the string inside the quotes. +.NH 2 +If statements +.sp .5 +\fBif\|(\|\fIrelation\fB\|)\|\fIstatement\fR +.PP +The substatement is executed if the relation is true. +.NH 2 +If-else statements +.sp .5 +\fBif\|(\|\fIrelation\fB\|)\|\fIstatement\fB\|else\|\fIstatement\fR +.PP +The first substatement is executed if the relation is true, the second +substatement if the relation is false. +The \fBif-else\fR statement is a non-portable extension. +.NH 2 +While statements +.sp .5 +\fBwhile\|(\|\fIrelation\fB\|)\|\fIstatement\fR +.PP +The statement is executed while the relation +is true. +The test occurs before each execution of the statement. +.NH 2 +For statements +.sp .5 +\fBfor\|(\|\fIexpression\fB; \fIrelation\fB; \fIexpression\fB\|)\|\fIstatement\fR +.PP +The \fBfor\fR statement is the same as +.nf +.ft I + first-expression + \fBwhile\|(\fPrelation\|\fB) {\fP + statement + last-expression + } +.ft R +.fi +.PP +All three expressions may be left out. +This is a non-portable extension. +.NH 2 +Break statements +.sp .5 +\fBbreak\fP +.PP +\fBbreak\fP causes termination of a \fBfor\fP or \fBwhile\fP statement. +.NH 2 +Continue statements +.sp .5 +\fBcontinue\fP +.PP +\fBcontinue\fP causes the next iteration of a \fBfor\fP or \fBwhile\fP +statement to start, skipping the remainder of the loop. +For a \fBwhile\fP statement, execution continues with the evaluation +of the condition. +For a \fBfor\fP statement, execution continues with evaluation of +the last-expression. +The \fBcontinue\fP statement is a non-portable extension. +.NH 2 +Auto statements +.sp .5 +\fBauto \fIidentifier\fR\|[\|\fB,\fIidentifier\fR\|] +.PP +The \fBauto\fR statement causes the values of the identifiers to be pushed down. +The identifiers can be ordinary identifiers or array identifiers. +Array identifiers are specified by following the array name by empty square +brackets. +The auto statement must be the first statement +in a function definition. +.NH 2 +Define statements +.sp .5 +.nf +\fBdefine(\|\fR[\fIparameter\|\fR[\fB\|,\|\fIparameter\|.\|.\|.\|\fR]\|]\|\fB)\|{\fI + statements\|\fB}\fR +.fi +.PP +The \fBdefine\fR statement defines a function. +The parameters may +be ordinary identifiers or array names. +Array names must be followed by empty square brackets. +As a non-portable extension, the opening brace may also appear on the +next line. +.NH 2 +Return statements +.sp .5 +\fBreturn\fP +.sp .5 +\fBreturn(\fI\|expression\|\fB)\fR +.PP +The \fBreturn\fR statement causes termination of a function, +popping of its auto variables, and +specifies the result of the function. +The first form is equivalent to \fBreturn(0)\fR. +The result of the function is the result of the expression +in parentheses. +Leaving out the expression between parentheses is equivalent to +\fBreturn(0)\fR. +As a non-portable extension, the parentheses may be left out. +.NH 2 +Print +.PP +The \fBprint\fR statement takes a list of comma-separated expressions. +Each expression in the list is evaluated and the computed +value is printed and assigned to the variable `last'. +No trailing newline is printed. +The expression may also be a string enclosed in double quotes. +Within these strings the following escape sequences may be used: +\ea +for bell (alert), +`\eb' +for backspace, +`\ef' +for formfeed, +`\en' +for newline, +`\er' +for carriage return, +`\et' +`for tab, +`\eq' +for double quote and +`\e\e' +for backslash. +Any other character following a backslash will be ignored. +Strings will not be assigned to `last'. +The \fBprint\fR statement is a non-portable extension. +.NH 2 +Quit +.PP +The \fBquit\fR statement stops execution of a BC program and returns +control to UNIX when it is first encountered. +Because it is not treated as an executable statement, +it cannot be used +in a function definition or in an +.ft B +if, for, +.ft +or +.ft B +while +.ft +statement. diff --git a/usr.bin/bc/bc.1 b/usr.bin/bc/bc.1 new file mode 100644 index 0000000..8fe21c6 --- /dev/null +++ b/usr.bin/bc/bc.1 @@ -0,0 +1,396 @@ +.\" $FreeBSD$ +.\" $OpenBSD: bc.1,v 1.25 2010/01/02 19:48:56 schwarze Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. +.\" 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 and documentation 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 or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of other +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, 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 CALDERA INTERNATIONAL, INC. 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. +.\" +.\" @(#)bc.1 6.8 (Berkeley) 8/8/91 +.\" +.Dd January 22, 2010 +.Dt BC 1 +.Os +.Sh NAME +.Nm bc +.Nd arbitrary-precision arithmetic language and calculator +.Sh SYNOPSIS +.Nm bc +.Op Fl chlqv +.Op Fl e Ar expression +.Op Ar file ... +.Sh DESCRIPTION +.Nm +is an interactive processor for a language which resembles +C but provides unlimited precision arithmetic. +It takes input from any expressions on the command line and +any files given, then reads the standard input. +.Pp +Options available: +.Bl -tag -width Ds +.It Fl c +.Nm +is actually a preprocessor for +.Xr dc 1 , +which it invokes automatically, unless the +.Fl c +.Pq compile only +option is present. +In this case the generated +.Xr dc 1 +instructions are sent to the standard output, +instead of being interpreted by a running +.Xr dc 1 +process. +.It Fl e Ar expression , Fl Fl expression Ar expression +Evaluate +.Ar expression . +If multiple +.Fl e +options are specified, they are processed in the order given, +separated by newlines. +.It Fl h , Fl Fl help +Prints usage information. +.It Fl l , Fl Fl mathlib +Allow specification of an arbitrary precision math library. +The definitions in the library are available to command line +expressions. +Synonym for +.Fl l . +.It Fl v , Fl Fl version +Prints version information. +.El +.Pp +The syntax for +.Nm +programs is as follows: +.Sq L +means letter a-z; +.Sq E +means expression; +.Sq S +means statement. +As a non-portable extension, it is possible to use long names +in addition to single letter names. +A long name is a sequence starting with a lowercase letter +followed by any number of lowercase letters and digits. +The underscore character +.Pq Sq _ +counts as a letter. +.Pp +Comments +.Bd -unfilled -offset indent -compact +are enclosed in /* and */ +are enclosed in # and the next newline +.Ed +.Pp +The newline is not part of the line comment, +which in itself is a non-portable extension. +.Pp +Names +.Bd -unfilled -offset indent -compact +simple variables: L +array elements: L [ E ] +The words `ibase', `obase', and `scale' +The word `last' or a single dot +.Ed +.Pp +Other operands +.Bd -unfilled -offset indent -compact +arbitrarily long numbers with optional sign and decimal point +( E ) +sqrt ( E ) +length ( E ) number of significant decimal digits +scale ( E ) number of digits right of decimal point +L ( E , ... , E ) +.Ed +.Pp +The sequence +.Sq \e<newline><whitespace> +is ignored within numbers. +.Pp +Operators +.Pp +The following arithmetic and logical operators can be used. +The semantics of the operators is the same as in the C language. +They are listed in order of decreasing precedence. +Operators in the same group have the same precedence. +.Bl -column -offset indent "= += \-= *= /= %= ^=" "Associativity" \ +"multiply, divide, modulus" +.It Sy "Operator" Ta Sy "Associativity" Ta Sy "Description" +.It "++ \-\-" Ta "none" Ta "increment, decrement" +.It "\-" Ta "none" Ta "unary minus" +.It "^" Ta "right" Ta "power" +.It "* / %" Ta "left" Ta "multiply, divide, modulus" +.It "+ \-" Ta "left" Ta "plus, minus" +.It "= += -= *= /= %= ^=" Ta "right" Ta "assignment" +.It "== <= >= != < >" Ta "none" Ta "relational" +.It "!" Ta "none" Ta "boolean not" +.It "&&" Ta "left" Ta "boolean and" +.It "||" Ta "left" Ta "boolean or" +.El +.Pp +Note the following: +.Bl -bullet -offset indent +.It +The relational operators may appear in any expression. +The +.St -p1003.2 +standard only allows them in the conditional expression of an +.Sq if , +.Sq while +or +.Sq for +statement. +.It +The relational operators have a lower precedence than the assignment +operators. +This has the consequence that the expression +.Sy a = b < c +is interpreted as +.Sy (a = b) < c , +which is probably not what the programmer intended. +.It +In contrast with the C language, the relational operators all have +the same precedence, and are non-associative. +The expression +.Sy a < b < c +will produce a syntax error. +.It +The boolean operators (!, && and ||) are non-portable extensions. +.It +The boolean not +(!) operator has much lower precedence than the same operator in the +C language. +This has the consequence that the expression +.Sy !a < b +is interpreted as +.Sy !(a < b) . +Prudent programmers use parentheses when writing expressions involving +boolean operators. +.El +.Pp +Statements +.Bd -unfilled -offset indent -compact +E +{ S ; ... ; S } +if ( E ) S +if ( E ) S else S +while ( E ) S +for ( E ; E ; E ) S +null statement +break +continue +quit +a string of characters, enclosed in double quotes +print E ,..., E +.Ed +.Pp +A string may contain any character, except double quote. +The if statement with an else branch is a non-portable extension. +All three E's in a for statement may be empty. +This is a non-portable extension. +The continue and print statements are also non-portable extensions. +.Pp +The print statement takes a list of comma-separated expressions. +Each expression in the list is evaluated and the computed +value is printed and assigned to the variable `last'. +No trailing newline is printed. +The expression may also be a string enclosed in double quotes. +Within these strings the following escape sequences may be used: +.Sq \ea +for bell (alert), +.Sq \eb +for backspace, +.Sq \ef +for formfeed, +.Sq \en +for newline, +.Sq \er +for carriage return, +.Sq \et +for tab, +.Sq \eq +for double quote and +.Sq \e\e +for backslash. +Any other character following a backslash will be ignored. +Strings will not be assigned to `last'. +.Pp +Function definitions +.Bd -unfilled -offset indent +define L ( L ,..., L ) { + auto L, ... , L + S; ... S + return ( E ) +} +.Ed +.Pp +As a non-portable extension, the opening brace of the define statement +may appear on the next line. +The return statement may also appear in the following forms: +.Bd -unfilled -offset indent +return +return () +return E +.Ed +.Pp +The first two are equivalent to the statement +.Dq return 0 . +The last form is a non-portable extension. +Not specifying a return statement is equivalent to writing +.Dq return (0) . +.Pp +Functions available in the math library, which is loaded by specifying the +.Fl l +flag on the command line +.Pp +.Bl -tag -width j(n,x) -offset indent -compact +.It s(x) +sine +.It c(x) +cosine +.It e(x) +exponential +.It l(x) +log +.It a(x) +arctangent +.It j(n,x) +Bessel function +.El +.Pp +All function arguments are passed by value. +.Pp +The value of a statement that is an expression is printed +unless the main operator is an assignment. +The value printed is assigned to the special variable `last'. +This is a non-portable extension. +A single dot may be used as a synonym for `last'. +Either semicolons or newlines may separate statements. +Assignment to +.Ar scale +influences the number of digits to be retained on arithmetic +operations in the manner of +.Xr dc 1 . +Assignments to +.Ar ibase +or +.Ar obase +set the input and output number radix respectively. +.Pp +The same letter may be used as an array, a function, +and a simple variable simultaneously. +All variables are global to the program. +`Auto' variables are pushed down during function calls. +When using arrays as function arguments +or defining them as automatic variables, +empty square brackets must follow the array name. +.Pp +For example +.Bd -literal -offset indent +scale = 20 +define e(x){ + auto a, b, c, i, s + a = 1 + b = 1 + s = 1 + for(i=1; 1==1; i++){ + a = a*x + b = b*i + c = a/b + if(c == 0) return(s) + s = s+c + } +} +.Ed +.Pp +defines a function to compute an approximate value of +the exponential function and +.Pp +.Dl for(i=1; i<=10; i++) e(i) +.Pp +prints approximate values of the exponential function of +the first ten integers. +.Bd -literal -offset indent +$ bc -l -e 'scale = 500; 2 * a(2^10000)' -e quit +.Ed +.Pp +prints an approximation of pi. +.Sh FILES +.Bl -tag -width /usr/share/misc/bc.library -compact +.It Pa /usr/share/misc/bc.library +math library, read when the +.Fl l +option is specified on the command line. +.El +.Sh SEE ALSO +.Xr dc 1 +.Pp +"BC \- An Arbitrary Precision Desk-Calculator Language", +.Pa /usr/share/doc/usd/06.bc/ . +.Sh STANDARDS +The +.Nm +utility is compliant with the +.St -p1003.1-2008 +specification. +.Pp +The flags +.Op Fl ce +are extensions to that specification. +.Sh HISTORY +The +.Nm +command first appeared in +.At v6 . +A complete rewrite of the +.Nm +command first appeared in +.Ox 3.5 . +.Sh AUTHORS +.An -nosplit +The original version of the +.Nm +command was written by +.An Robert Morris +and +.An Lorinda Cherry . +The current version of the +.Nm +utility was written by +.An Otto Moerbeek . +.Sh BUGS +.Ql Quit +is interpreted when read, not when executed. +.Pp +Some non-portable extensions, as found in the GNU version of the +.Nm +utility are not implemented (yet). diff --git a/usr.bin/bc/bc.library b/usr.bin/bc/bc.library new file mode 100644 index 0000000..c3145ab --- /dev/null +++ b/usr.bin/bc/bc.library @@ -0,0 +1,263 @@ +/* $FreeBSD$ */ +/* $OpenBSD: bc.library,v 1.3 2007/02/03 21:15:06 otto Exp $ */ + +/* + * Copyright (C) Caldera International Inc. 2001-2002. + * 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 and documentation 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 or owned by Caldera + * International, Inc. + * 4. Neither the name of Caldera International, Inc. nor the names of other + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA + * INTERNATIONAL, 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 CALDERA INTERNATIONAL, INC. 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. + */ + +/* + * @(#)bc.library 5.1 (Berkeley) 4/17/91 + */ + +scale = 20 +define e(x) { + auto a, b, c, d, e, g, t, w, y, r + + r = ibase + ibase = A + t = scale + scale = t + .434*x + 1 + + w = 0 + if (x < 0) { + x = -x + w = 1 + } + y = 0 + while (x > 2) { + x = x/2 + y = y + 1 + } + + a = 1 + b = 1 + c = b + d = 1 + e = 1 + for (a = 1; 1 == 1; a++) { + b = b*x + c = c*a + b + d = d*a + g = c/d + if (g == e) { + g = g/1 + while (y--) { + g = g*g + } + scale = t + ibase = r + if (w == 1) return (1/g) + return (g/1) + } + e = g + } +} + +define l(x) { + auto a, b, c, d, e, f, g, u, s, t, r + r = ibase + ibase = A + if (x <= 0) { + a = (1 - 10^scale) + ibase = r + return (a) + } + t = scale + + f = 1 + scale = scale + scale(x) - length(x) + 1 + s = scale + while (x > 2) { + s = s + (length(x) - scale(x))/2 + 1 + if (s > 0) scale = s + x = sqrt(x) + f = f*2 + } + while (x < .5) { + s = s + (length(x) - scale(x))/2 + 1 + if (s > 0) scale = s + x = sqrt(x) + f = f*2 + } + + scale = t + length(f) - scale(f) + 1 + u = (x - 1)/(x + 1) + + scale = scale + 1.1*length(t) - 1.1*scale(t) + s = u*u + b = 2*f + c = b + d = 1 + e = 1 + for (a = 3; 1 == 1 ; a = a + 2) { + b = b*s + c = c*a + d*b + d = d*a + g = c/d + if (g == e) { + scale = t + ibase = r + return (u*c/d) + } + e = g + } +} + +define s(x) { + auto a, b, c, s, t, y, p, n, i, r + r = ibase + ibase = A + t = scale + y = x/.7853 + s = t + length(y) - scale(y) + if (s < t) s = t + scale = s + p = a(1) + + scale = 0 + if (x >= 0) n = (x/(2*p) + 1)/2 + if (x < 0) n = (x/(2*p) - 1)/2 + x = x - 4*n*p + if (n % 2 != 0) x = -x + + scale = t + length(1.2*t) - scale(1.2*t) + y = -x*x + a = x + b = 1 + s = x + for (i =3 ; 1 == 1; i = i + 2) { + a = a*y + b = b*i*(i - 1) + c = a/b + if (c == 0) { + scale = t + ibase = r + return (s/1) + } + s = s + c + } +} + +define c(x) { + auto t, r + r = ibase + ibase = A + t = scale + scale = scale + 1 + x = s(x + 2*a(1)) + scale = t + ibase = r + return (x/1) +} + +define a(x) { + auto a, b, c, d, e, f, g, s, t, r + if (x == 0) return(0) + + r = ibase + ibase = A + if (x == 1) { + if (scale < 52) { + a = .7853981633974483096156608458198757210492923498437764/1 + ibase = r + return (a) + } + } + t = scale + f = 1 + while (x > .5) { + scale = scale + 1 + x = -(1 - sqrt(1. + x*x))/x + f = f*2 + } + while (x < -.5) { + scale = scale + 1 + x = -(1 - sqrt(1. + x*x))/x + f = f*2 + } + s = -x*x + b = f + c = f + d = 1 + e = 1 + for (a = 3; 1 == 1; a = a + 2) { + b = b*s + c = c*a + d*b + d = d*a + g = c/d + if (g == e) { + ibase = r + scale = t + return (x*c/d) + } + e = g + } +} + +define j(n,x) { + auto a, b, c, d, e, g, i, s, k, t, r + + r = ibase + ibase = A + t = scale + k = 1.36*x + 1.16*t - n + k = length(k) - scale(k) + if (k > 0) scale = scale + k + + s = -x*x/4 + if (n < 0) { + n = -n + x = -x + } + a = 1 + c = 1 + for (i = 1; i <= n; i++) { + a = a*x + c = c*2*i + } + b = a + d = 1 + e = 1 + for (i = 1; 1; i++) { + a = a*s + b = b*i*(n + i) + a + c = c*i*(n + i) + g = b/c + if (g == e) { + ibase = r + scale = t + return (g/1) + } + e = g + } +} diff --git a/usr.bin/bc/bc.y b/usr.bin/bc/bc.y new file mode 100644 index 0000000..b00d5ac --- /dev/null +++ b/usr.bin/bc/bc.y @@ -0,0 +1,1205 @@ +%{ +/* $OpenBSD: bc.y,v 1.33 2009/10/27 23:59:36 deraadt Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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. + */ + +/* + * This implementation of bc(1) uses concepts from the original 4.4 + * BSD bc(1). The code itself is a complete rewrite, based on the + * Posix defined bc(1) grammar. Other differences include type safe + * usage of pointers to build the tree of emitted code, typed yacc + * rule values, dynamic allocation of all data structures and a + * completely rewritten lexical analyzer using lex(1). + * + * Some effort has been made to make sure that the generated code is + * the same as the code generated by the older version, to provide + * easy regression testing. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/wait.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <histedit.h> +#include <limits.h> +#include <search.h> +#include <signal.h> +#include <stdarg.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" +#include "pathnames.h" + +#define BC_VER "1.0-FreeBSD" +#define END_NODE ((ssize_t) -1) +#define CONST_STRING ((ssize_t) -2) +#define ALLOC_STRING ((ssize_t) -3) + +extern char *yytext; +extern FILE *yyin; + +struct tree { + union { + char *astr; + const char *cstr; + } u; + ssize_t index; +}; + +int yyparse(void); +int yywrap(void); + +int fileindex; +int sargc; +const char **sargv; +const char *filename; +char *cmdexpr; + +static void grow(void); +static ssize_t cs(const char *); +static ssize_t as(const char *); +static ssize_t node(ssize_t, ...); +static void emit(ssize_t); +static void emit_macro(int, ssize_t); +static void free_tree(void); +static ssize_t numnode(int); +static ssize_t lookup(char *, size_t, char); +static ssize_t letter_node(char *); +static ssize_t array_node(char *); +static ssize_t function_node(char *); + +static void add_par(ssize_t); +static void add_local(ssize_t); +static void warning(const char *); +static void init(void); +static void usage(void); +static char *escape(const char *); + +static ssize_t instr_sz = 0; +static struct tree *instructions = NULL; +static ssize_t current = 0; +static int macro_char = '0'; +static int reset_macro_char = '0'; +static int nesting = 0; +static int breakstack[16]; +static int breaksp = 0; +static ssize_t prologue; +static ssize_t epilogue; +static bool st_has_continue; +static char str_table[UCHAR_MAX][2]; +static bool do_fork = true; +static u_short var_count; +static pid_t dc; + +static void sigchld(int); + +extern char *__progname; + +#define BREAKSTACK_SZ (sizeof(breakstack)/sizeof(breakstack[0])) + +/* These values are 4.4BSD bc compatible */ +#define FUNC_CHAR 0x01 +#define ARRAY_CHAR 0xa1 + +/* Skip '\0', [, \ and ] */ +#define ENCODE(c) ((c) < '[' ? (c) : (c) + 3); +#define VAR_BASE (256-4) +#define MAX_VARIABLES (VAR_BASE * VAR_BASE) + +const struct option long_options[] = +{ + {"expression", required_argument, NULL, 'e'}, + {"help", no_argument, NULL, 'h'}, + {"mathlib", no_argument, NULL, 'l'}, + /* compatibility option */ + {"quiet", no_argument, NULL, 'q'}, + {"version", no_argument, NULL, 'v'}, + {NULL, no_argument, NULL, 0} +}; + +%} + +%start program + +%union { + struct lvalue lvalue; + const char *str; + char *astr; + ssize_t node; +} + +%token COMMA SEMICOLON LPAR RPAR LBRACE RBRACE LBRACKET RBRACKET DOT +%token NEWLINE +%token <astr> LETTER +%token <str> NUMBER STRING +%token DEFINE BREAK QUIT LENGTH +%token RETURN FOR IF WHILE SQRT +%token SCALE IBASE OBASE AUTO +%token CONTINUE ELSE PRINT + +%left BOOL_OR +%left BOOL_AND +%nonassoc BOOL_NOT +%nonassoc EQUALS LESS_EQ GREATER_EQ UNEQUALS LESS GREATER +%right <str> ASSIGN_OP +%left PLUS MINUS +%left MULTIPLY DIVIDE REMAINDER +%right EXPONENT +%nonassoc UMINUS +%nonassoc INCR DECR + +%type <lvalue> named_expression +%type <node> argument_list +%type <node> alloc_macro +%type <node> expression +%type <node> function +%type <node> function_header +%type <node> input_item +%type <node> opt_argument_list +%type <node> opt_expression +%type <node> opt_relational_expression +%type <node> opt_statement +%type <node> print_expression +%type <node> print_expression_list +%type <node> relational_expression +%type <node> return_expression +%type <node> semicolon_list +%type <node> statement +%type <node> statement_list + +%% + +program : /* empty */ + | program input_item + ; + +input_item : semicolon_list NEWLINE + { + emit($1); + macro_char = reset_macro_char; + putchar('\n'); + free_tree(); + st_has_continue = false; + } + | function + { + putchar('\n'); + free_tree(); + st_has_continue = false; + } + | error NEWLINE + { + yyerrok; + } + | error QUIT + { + yyerrok; + } + ; + +semicolon_list : /* empty */ + { + $$ = cs(""); + } + | statement + | semicolon_list SEMICOLON statement + { + $$ = node($1, $3, END_NODE); + } + | semicolon_list SEMICOLON + ; + +statement_list : /* empty */ + { + $$ = cs(""); + } + | statement + | statement_list NEWLINE + | statement_list NEWLINE statement + { + $$ = node($1, $3, END_NODE); + } + | statement_list SEMICOLON + | statement_list SEMICOLON statement + { + $$ = node($1, $3, END_NODE); + } + ; + + +opt_statement : /* empty */ + { + $$ = cs(""); + } + | statement + ; + +statement : expression + { + $$ = node($1, cs("ps."), END_NODE); + } + | named_expression ASSIGN_OP expression + { + if ($2[0] == '\0') + $$ = node($3, cs($2), $1.store, + END_NODE); + else + $$ = node($1.load, $3, cs($2), $1.store, + END_NODE); + } + | STRING + { + $$ = node(cs("["), as($1), + cs("]P"), END_NODE); + } + | BREAK + { + if (breaksp == 0) { + warning("break not in for or while"); + YYERROR; + } else { + $$ = node( + numnode(nesting - + breakstack[breaksp-1]), + cs("Q"), END_NODE); + } + } + | CONTINUE + { + if (breaksp == 0) { + warning("continue not in for or while"); + YYERROR; + } else { + st_has_continue = true; + $$ = node(numnode(nesting - + breakstack[breaksp-1] - 1), + cs("J"), END_NODE); + } + } + | QUIT + { + sigset_t mask; + + putchar('q'); + fflush(stdout); + if (dc) { + sigprocmask(SIG_BLOCK, NULL, &mask); + sigsuspend(&mask); + } else + exit(0); + } + | RETURN return_expression + { + if (nesting == 0) { + warning("return must be in a function"); + YYERROR; + } + $$ = $2; + } + | FOR LPAR alloc_macro opt_expression SEMICOLON + opt_relational_expression SEMICOLON + opt_expression RPAR opt_statement pop_nesting + { + ssize_t n; + + if (st_has_continue) + n = node($10, cs("M"), $8, cs("s."), + $6, $3, END_NODE); + else + n = node($10, $8, cs("s."), $6, $3, + END_NODE); + + emit_macro($3, n); + $$ = node($4, cs("s."), $6, $3, cs(" "), + END_NODE); + } + | IF LPAR alloc_macro pop_nesting relational_expression RPAR + opt_statement + { + emit_macro($3, $7); + $$ = node($5, $3, cs(" "), END_NODE); + } + | IF LPAR alloc_macro pop_nesting relational_expression RPAR + opt_statement ELSE alloc_macro pop_nesting opt_statement + { + emit_macro($3, $7); + emit_macro($9, $11); + $$ = node($5, $3, cs("e"), $9, cs(" "), + END_NODE); + } + | WHILE LPAR alloc_macro relational_expression RPAR + opt_statement pop_nesting + { + ssize_t n; + + if (st_has_continue) + n = node($6, cs("M"), $4, $3, END_NODE); + else + n = node($6, $4, $3, END_NODE); + emit_macro($3, n); + $$ = node($4, $3, cs(" "), END_NODE); + } + | LBRACE statement_list RBRACE + { + $$ = $2; + } + | PRINT print_expression_list + { + $$ = $2; + } + ; + +alloc_macro : /* empty */ + { + $$ = cs(str_table[macro_char]); + macro_char++; + /* Do not use [, \ and ] */ + if (macro_char == '[') + macro_char += 3; + /* skip letters */ + else if (macro_char == 'a') + macro_char = '{'; + else if (macro_char == ARRAY_CHAR) + macro_char += 26; + else if (macro_char == 255) + fatal("program too big"); + if (breaksp == BREAKSTACK_SZ) + fatal("nesting too deep"); + breakstack[breaksp++] = nesting++; + } + ; + +pop_nesting : /* empty */ + { + breaksp--; + } + ; + +function : function_header opt_parameter_list RPAR opt_newline + LBRACE NEWLINE opt_auto_define_list + statement_list RBRACE + { + int n = node(prologue, $8, epilogue, + cs("0"), numnode(nesting), + cs("Q"), END_NODE); + emit_macro($1, n); + reset_macro_char = macro_char; + nesting = 0; + breaksp = 0; + } + ; + +function_header : DEFINE LETTER LPAR + { + $$ = function_node($2); + free($2); + prologue = cs(""); + epilogue = cs(""); + nesting = 1; + breaksp = 0; + breakstack[breaksp] = 0; + } + ; + +opt_newline : /* empty */ + | NEWLINE + ; + +opt_parameter_list + : /* empty */ + | parameter_list + ; + + +parameter_list : LETTER + { + add_par(letter_node($1)); + free($1); + } + | LETTER LBRACKET RBRACKET + { + add_par(array_node($1)); + free($1); + } + | parameter_list COMMA LETTER + { + add_par(letter_node($3)); + free($3); + } + | parameter_list COMMA LETTER LBRACKET RBRACKET + { + add_par(array_node($3)); + free($3); + } + ; + + + +opt_auto_define_list + : /* empty */ + | AUTO define_list NEWLINE + | AUTO define_list SEMICOLON + ; + + +define_list : LETTER + { + add_local(letter_node($1)); + free($1); + } + | LETTER LBRACKET RBRACKET + { + add_local(array_node($1)); + free($1); + } + | define_list COMMA LETTER + { + add_local(letter_node($3)); + free($3); + } + | define_list COMMA LETTER LBRACKET RBRACKET + { + add_local(array_node($3)); + free($3); + } + ; + + +opt_argument_list + : /* empty */ + { + $$ = cs(""); + } + | argument_list + ; + + +argument_list : expression + | argument_list COMMA expression + { + $$ = node($1, $3, END_NODE); + } + | argument_list COMMA LETTER LBRACKET RBRACKET + { + $$ = node($1, cs("l"), array_node($3), + END_NODE); + free($3); + } + ; + +opt_relational_expression + : /* empty */ + { + $$ = cs(" 0 0="); + } + | relational_expression + ; + +relational_expression + : expression EQUALS expression + { + $$ = node($1, $3, cs("="), END_NODE); + } + | expression UNEQUALS expression + { + $$ = node($1, $3, cs("!="), END_NODE); + } + | expression LESS expression + { + $$ = node($1, $3, cs(">"), END_NODE); + } + | expression LESS_EQ expression + { + $$ = node($1, $3, cs("!<"), END_NODE); + } + | expression GREATER expression + { + $$ = node($1, $3, cs("<"), END_NODE); + } + | expression GREATER_EQ expression + { + $$ = node($1, $3, cs("!>"), END_NODE); + } + | expression + { + $$ = node($1, cs(" 0!="), END_NODE); + } + ; + + +return_expression + : /* empty */ + { + $$ = node(cs("0"), epilogue, + numnode(nesting), cs("Q"), END_NODE); + } + | expression + { + $$ = node($1, epilogue, + numnode(nesting), cs("Q"), END_NODE); + } + | LPAR RPAR + { + $$ = node(cs("0"), epilogue, + numnode(nesting), cs("Q"), END_NODE); + } + ; + + +opt_expression : /* empty */ + { + $$ = cs(" 0"); + } + | expression + ; + +expression : named_expression + { + $$ = node($1.load, END_NODE); + } + | DOT { + $$ = node(cs("l."), END_NODE); + } + | NUMBER + { + $$ = node(cs(" "), as($1), END_NODE); + } + | LPAR expression RPAR + { + $$ = $2; + } + | LETTER LPAR opt_argument_list RPAR + { + $$ = node($3, cs("l"), + function_node($1), cs("x"), + END_NODE); + free($1); + } + | MINUS expression %prec UMINUS + { + $$ = node(cs(" 0"), $2, cs("-"), + END_NODE); + } + | expression PLUS expression + { + $$ = node($1, $3, cs("+"), END_NODE); + } + | expression MINUS expression + { + $$ = node($1, $3, cs("-"), END_NODE); + } + | expression MULTIPLY expression + { + $$ = node($1, $3, cs("*"), END_NODE); + } + | expression DIVIDE expression + { + $$ = node($1, $3, cs("/"), END_NODE); + } + | expression REMAINDER expression + { + $$ = node($1, $3, cs("%"), END_NODE); + } + | expression EXPONENT expression + { + $$ = node($1, $3, cs("^"), END_NODE); + } + | INCR named_expression + { + $$ = node($2.load, cs("1+d"), $2.store, + END_NODE); + } + | DECR named_expression + { + $$ = node($2.load, cs("1-d"), + $2.store, END_NODE); + } + | named_expression INCR + { + $$ = node($1.load, cs("d1+"), + $1.store, END_NODE); + } + | named_expression DECR + { + $$ = node($1.load, cs("d1-"), + $1.store, END_NODE); + } + | named_expression ASSIGN_OP expression + { + if ($2[0] == '\0') + $$ = node($3, cs($2), cs("d"), $1.store, + END_NODE); + else + $$ = node($1.load, $3, cs($2), cs("d"), + $1.store, END_NODE); + } + | LENGTH LPAR expression RPAR + { + $$ = node($3, cs("Z"), END_NODE); + } + | SQRT LPAR expression RPAR + { + $$ = node($3, cs("v"), END_NODE); + } + | SCALE LPAR expression RPAR + { + $$ = node($3, cs("X"), END_NODE); + } + | BOOL_NOT expression + { + $$ = node($2, cs("N"), END_NODE); + } + | expression BOOL_AND alloc_macro pop_nesting expression + { + ssize_t n = node(cs("R"), $5, END_NODE); + emit_macro($3, n); + $$ = node($1, cs("d0!="), $3, END_NODE); + } + | expression BOOL_OR alloc_macro pop_nesting expression + { + ssize_t n = node(cs("R"), $5, END_NODE); + emit_macro($3, n); + $$ = node($1, cs("d0="), $3, END_NODE); + } + | expression EQUALS expression + { + $$ = node($1, $3, cs("G"), END_NODE); + } + | expression UNEQUALS expression + { + $$ = node($1, $3, cs("GN"), END_NODE); + } + | expression LESS expression + { + $$ = node($3, $1, cs("("), END_NODE); + } + | expression LESS_EQ expression + { + $$ = node($3, $1, cs("{"), END_NODE); + } + | expression GREATER expression + { + $$ = node($1, $3, cs("("), END_NODE); + } + | expression GREATER_EQ expression + { + $$ = node($1, $3, cs("{"), END_NODE); + } + ; + +named_expression + : LETTER + { + $$.load = node(cs("l"), letter_node($1), + END_NODE); + $$.store = node(cs("s"), letter_node($1), + END_NODE); + free($1); + } + | LETTER LBRACKET expression RBRACKET + { + $$.load = node($3, cs(";"), + array_node($1), END_NODE); + $$.store = node($3, cs(":"), + array_node($1), END_NODE); + free($1); + } + | SCALE + { + $$.load = cs("K"); + $$.store = cs("k"); + } + | IBASE + { + $$.load = cs("I"); + $$.store = cs("i"); + } + | OBASE + { + $$.load = cs("O"); + $$.store = cs("o"); + } + ; + +print_expression_list + : print_expression + | print_expression_list COMMA print_expression + { + $$ = node($1, $3, END_NODE); + } + +print_expression + : expression + { + $$ = node($1, cs("ds.n"), END_NODE); + } + | STRING + { + char *p = escape($1); + $$ = node(cs("["), as(p), cs("]n"), END_NODE); + free(p); + } +%% + + +static void +grow(void) +{ + struct tree *p; + size_t newsize; + + if (current == instr_sz) { + newsize = instr_sz * 2 + 1; + p = realloc(instructions, newsize * sizeof(*p)); + if (p == NULL) { + free(instructions); + err(1, NULL); + } + instructions = p; + instr_sz = newsize; + } +} + +static ssize_t +cs(const char *str) +{ + + grow(); + instructions[current].index = CONST_STRING; + instructions[current].u.cstr = str; + return (current++); +} + +static ssize_t +as(const char *str) +{ + + grow(); + instructions[current].index = ALLOC_STRING; + instructions[current].u.astr = strdup(str); + if (instructions[current].u.astr == NULL) + err(1, NULL); + return (current++); +} + +static ssize_t +node(ssize_t arg, ...) +{ + va_list ap; + ssize_t ret; + + va_start(ap, arg); + + ret = current; + grow(); + instructions[current++].index = arg; + + do { + arg = va_arg(ap, ssize_t); + grow(); + instructions[current++].index = arg; + } while (arg != END_NODE); + + va_end(ap); + return (ret); +} + +static void +emit(ssize_t i) +{ + + if (instructions[i].index >= 0) + while (instructions[i].index != END_NODE) + emit(instructions[i++].index); + else + fputs(instructions[i].u.cstr, stdout); +} + +static void +emit_macro(int nodeidx, ssize_t code) +{ + + putchar('['); + emit(code); + printf("]s%s\n", instructions[nodeidx].u.cstr); + nesting--; +} + +static void +free_tree(void) +{ + ssize_t i; + + for (i = 0; i < current; i++) + if (instructions[i].index == ALLOC_STRING) + free(instructions[i].u.astr); + current = 0; +} + +static ssize_t +numnode(int num) +{ + const char *p; + + if (num < 10) + p = str_table['0' + num]; + else if (num < 16) + p = str_table['A' - 10 + num]; + else + errx(1, "internal error: break num > 15"); + return (node(cs(" "), cs(p), END_NODE)); +} + + +static ssize_t +lookup(char * str, size_t len, char type) +{ + ENTRY entry, *found; + u_char *p; + u_short num; + + /* The scanner allocated an extra byte already */ + if (str[len-1] != type) { + str[len] = type; + str[len+1] = '\0'; + } + entry.key = str; + found = hsearch(entry, FIND); + if (found == NULL) { + if (var_count == MAX_VARIABLES) + errx(1, "too many variables"); + p = malloc(4); + if (p == NULL) + err(1, NULL); + num = var_count++; + p[0] = 255; + p[1] = ENCODE(num / VAR_BASE + 1); + p[2] = ENCODE(num % VAR_BASE + 1); + p[3] = '\0'; + + entry.data = (char *)p; + entry.key = strdup(str); + if (entry.key == NULL) + err(1, NULL); + found = hsearch(entry, ENTER); + if (found == NULL) + err(1, NULL); + } + return (cs(found->data)); +} + +static ssize_t +letter_node(char *str) +{ + size_t len; + + len = strlen(str); + if (len == 1 && str[0] != '_') + return (cs(str_table[(int)str[0]])); + else + return (lookup(str, len, 'L')); +} + +static ssize_t +array_node(char *str) +{ + size_t len; + + len = strlen(str); + if (len == 1 && str[0] != '_') + return (cs(str_table[(int)str[0] - 'a' + ARRAY_CHAR])); + else + return (lookup(str, len, 'A')); +} + +static ssize_t +function_node(char *str) +{ + size_t len; + + len = strlen(str); + if (len == 1 && str[0] != '_') + return (cs(str_table[(int)str[0] - 'a' + FUNC_CHAR])); + else + return (lookup(str, len, 'F')); +} + +static void +add_par(ssize_t n) +{ + + prologue = node(cs("S"), n, prologue, END_NODE); + epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); +} + +static void +add_local(ssize_t n) +{ + + prologue = node(cs("0S"), n, prologue, END_NODE); + epilogue = node(epilogue, cs("L"), n, cs("s."), END_NODE); +} + +void +yyerror(const char *s) +{ + char *p, *str; + int n; + + if (yyin != NULL && feof(yyin)) + n = asprintf(&str, "%s: %s:%d: %s: unexpected EOF", + __progname, filename, lineno, s); + else if (isspace(yytext[0]) || !isprint(yytext[0])) + n = asprintf(&str, + "%s: %s:%d: %s: ascii char 0x%02x unexpected", + __progname, filename, lineno, s, yytext[0]); + else + n = asprintf(&str, "%s: %s:%d: %s: %s unexpected", + __progname, filename, lineno, s, yytext); + if (n == -1) + err(1, NULL); + + fputs("c[", stdout); + for (p = str; *p != '\0'; p++) { + if (*p == '[' || *p == ']' || *p =='\\') + putchar('\\'); + putchar(*p); + } + fputs("]pc\n", stdout); + free(str); +} + +void +fatal(const char *s) +{ + + errx(1, "%s:%d: %s", filename, lineno, s); +} + +static void +warning(const char *s) +{ + + warnx("%s:%d: %s", filename, lineno, s); +} + +static void +init(void) +{ + unsigned int i; + + for (i = 0; i < UCHAR_MAX; i++) { + str_table[i][0] = i; + str_table[i][1] = '\0'; + } + if (hcreate(1 << 16) == 0) + err(1, NULL); +} + + +static void +usage(void) +{ + + fprintf(stderr, "usage: %s [-chlqv] [-e expression] [file ...]\n", + __progname); + exit(1); +} + +static char * +escape(const char *str) +{ + char *p, *ret; + + ret = malloc(strlen(str) + 1); + if (ret == NULL) + err(1, NULL); + + p = ret; + while (*str != '\0') { + /* + * We get _escaped_ strings here. Single backslashes are + * already converted to double backslashes + */ + if (*str == '\\') { + if (*++str == '\\') { + switch (*++str) { + case 'a': + *p++ = '\a'; + break; + case 'b': + *p++ = '\b'; + break; + case 'f': + *p++ = '\f'; + break; + case 'n': + *p++ = '\n'; + break; + case 'q': + *p++ = '"'; + break; + case 'r': + *p++ = '\r'; + break; + case 't': + *p++ = '\t'; + break; + case '\\': + *p++ = '\\'; + break; + } + str++; + } else { + *p++ = '\\'; + *p++ = *str++; + } + } else + *p++ = *str++; + } + *p = '\0'; + return (ret); +} + +/* ARGSUSED */ +void +sigchld(int signo) +{ + pid_t pid; + int status; + + switch (signo) { + default: + for (;;) { + pid = waitpid(dc, &status, WCONTINUED); + if (pid == -1) { + if (errno == EINTR) + continue; + _exit(0); + } + if (WIFEXITED(status) || WIFSIGNALED(status)) + _exit(0); + else + break; + } + } +} + +static const char * +dummy_prompt(void) +{ + + return (""); +} + +int +main(int argc, char *argv[]) +{ + char *q; + int p[2]; + int ch, i; + + init(); + setlinebuf(stdout); + + sargv = malloc(argc * sizeof(char *)); + if (sargv == NULL) + err(1, NULL); + + if ((cmdexpr = strdup("")) == NULL) + err(1, NULL); + /* The d debug option is 4.4 BSD bc(1) compatible */ + while ((ch = getopt_long(argc, argv, "cde:hlqv", + long_options, NULL)) != -1) { + switch (ch) { + case 'c': + case 'd': + do_fork = false; + break; + case 'e': + q = cmdexpr; + if (asprintf(&cmdexpr, "%s%s\n", cmdexpr, optarg) == -1) + err(1, NULL); + free(q); + break; + case 'h': + usage(); + break; + case 'l': + sargv[sargc++] = _PATH_LIBB; + break; + case 'q': + /* compatibility option */ + break; + case 'v': + fprintf(stderr, "%s (BSD bc) %s\n", __progname, BC_VER); + exit(0); + break; + default: + usage(); + } + } + + argc -= optind; + argv += optind; + + interactive = isatty(STDIN_FILENO); + for (i = 0; i < argc; i++) + sargv[sargc++] = argv[i]; + + if (do_fork) { + if (pipe(p) == -1) + err(1, "cannot create pipe"); + dc = fork(); + if (dc == -1) + err(1, "cannot fork"); + else if (dc != 0) { + signal(SIGCHLD, sigchld); + close(STDOUT_FILENO); + dup(p[1]); + close(p[0]); + close(p[1]); + if (interactive) { + el = el_init("bc", stdin, stderr, stderr); + hist = history_init(); + history(hist, &he, H_SETSIZE, 100); + el_set(el, EL_HIST, history, hist); + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_SIGNAL, 1); + el_set(el, EL_PROMPT, dummy_prompt); + el_source(el, NULL); + } + } else { + close(STDIN_FILENO); + dup(p[0]); + close(p[0]); + close(p[1]); + execl(_PATH_DC, "dc", "-x", (char *)NULL); + err(1, "cannot find dc"); + } + } + yywrap(); + return (yyparse()); +} diff --git a/usr.bin/bc/extern.h b/usr.bin/bc/extern.h new file mode 100644 index 0000000..d1e9fe8 --- /dev/null +++ b/usr.bin/bc/extern.h @@ -0,0 +1,42 @@ +/* $FreeBSD$ */ +/* $OpenBSD: extern.h,v 1.6 2006/03/18 20:44:43 otto Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <stdio.h> + +struct lvalue { + ssize_t load; + ssize_t store; +}; + +int yylex(void); +void yyerror(const char *); +void fatal(const char *); +void abort_line(int); + +extern int lineno; +extern int fileindex; +extern int sargc; +extern const char **sargv; +extern const char *filename; +extern char *cmdexpr; +extern bool interactive; +extern EditLine *el; +extern History *hist; +extern HistEvent he; + diff --git a/usr.bin/bc/pathnames.h b/usr.bin/bc/pathnames.h new file mode 100644 index 0000000..defb766 --- /dev/null +++ b/usr.bin/bc/pathnames.h @@ -0,0 +1,21 @@ +/* $FreeBSD$ */ +/* $OpenBSD: pathnames.h,v 1.1 2003/09/25 19:32:44 otto Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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. + */ + +#define _PATH_LIBB "/usr/share/misc/bc.library" +#define _PATH_DC "/usr/bin/dc" diff --git a/usr.bin/bc/scan.l b/usr.bin/bc/scan.l new file mode 100644 index 0000000..0de6dc8 --- /dev/null +++ b/usr.bin/bc/scan.l @@ -0,0 +1,327 @@ +%{ +/* $OpenBSD: scan.l,v 1.23 2009/10/27 23:59:36 deraadt Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <errno.h> +#include <histedit.h> +#include <signal.h> +#include <stdbool.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" +#include "bc.h" +#include "pathnames.h" + +int lineno; + +bool interactive; +HistEvent he; +EditLine *el; +History *hist; + +static char *strbuf = NULL; +static size_t strbuf_sz = 1; +static bool dot_seen; + +static void init_strbuf(void); +static void add_str(const char *); +static int bc_yyinput(char *, int); + +#undef YY_INPUT +#define YY_INPUT(buf,retval,max) \ + (retval = bc_yyinput(buf, max)) +%} + +%option always-interactive + +DIGIT [0-9A-F] +ALPHA [a-z_] +ALPHANUM [a-z_0-9] + +%x comment string number + +%% + +"/*" BEGIN(comment); +<comment>{ + "*/" BEGIN(INITIAL); + \n lineno++; + \* ; + [^*\n]+ ; + <<EOF>> fatal("end of file in comment"); +} + +\" BEGIN(string); init_strbuf(); +<string>{ + [^"\n\\\[\]]+ add_str(yytext); + \[ add_str("\\["); + \] add_str("\\]"); + \\ add_str("\\\\"); + \n add_str("\n"); lineno++; + \" BEGIN(INITIAL); yylval.str = strbuf; return STRING; + <<EOF>> fatal("end of file in string"); +} + +{DIGIT}+ { + BEGIN(number); + dot_seen = false; + init_strbuf(); + add_str(yytext); + } +\. { + BEGIN(number); + dot_seen = true; + init_strbuf(); + add_str("."); + } +<number>{ + {DIGIT}+ add_str(yytext); + \. { + if (dot_seen) { + BEGIN(INITIAL); + yylval.str = strbuf; + unput('.'); + return (NUMBER); + } else { + dot_seen = true; + add_str("."); + } + } + \\\n[ \t]* lineno++; + [^0-9A-F\.] { + BEGIN(INITIAL); + unput(yytext[0]); + if (strcmp(strbuf, ".") == 0) + return (DOT); + else { + yylval.str = strbuf; + return (NUMBER); + } + } +} + +"auto" return (AUTO); +"break" return (BREAK); +"continue" return (CONTINUE); +"define" return (DEFINE); +"else" return (ELSE); +"ibase" return (IBASE); +"if" return (IF); +"last" return (DOT); +"for" return (FOR); +"length" return (LENGTH); +"obase" return (OBASE); +"print" return (PRINT); +"quit" return (QUIT); +"return" return (RETURN); +"scale" return (SCALE); +"sqrt" return (SQRT); +"while" return (WHILE); + +"^" return (EXPONENT); +"*" return (MULTIPLY); +"/" return (DIVIDE); +"%" return (REMAINDER); + +"!" return (BOOL_NOT); +"&&" return (BOOL_AND); +"||" return (BOOL_OR); + +"+" return (PLUS); +"-" return (MINUS); + +"++" return (INCR); +"--" return (DECR); + +"=" yylval.str = ""; return (ASSIGN_OP); +"+=" yylval.str = "+"; return (ASSIGN_OP); +"-=" yylval.str = "-"; return (ASSIGN_OP); +"*=" yylval.str = "*"; return (ASSIGN_OP); +"/=" yylval.str = "/"; return (ASSIGN_OP); +"%=" yylval.str = "%"; return (ASSIGN_OP); +"^=" yylval.str = "^"; return (ASSIGN_OP); + +"==" return (EQUALS); +"<=" return (LESS_EQ); +">=" return (GREATER_EQ); +"!=" return (UNEQUALS); +"<" return (LESS); +">" return (GREATER); + +"," return (COMMA); +";" return (SEMICOLON); + +"(" return (LPAR); +")" return (RPAR); + +"[" return (LBRACKET); +"]" return (RBRACKET); + +"{" return (LBRACE); +"}" return (RBRACE); + +{ALPHA}{ALPHANUM}* { + /* alloc an extra byte for the type marker */ + char *p = malloc(yyleng + 2); + if (p == NULL) + err(1, NULL); + strlcpy(p, yytext, yyleng + 1); + yylval.astr = p; + return (LETTER); + } + +\\\n lineno++; +\n lineno++; return (NEWLINE); + +#[^\n]* ; +[ \t] ; +<<EOF>> return (QUIT); +. yyerror("illegal character"); + +%% + +static void +init_strbuf(void) +{ + + if (strbuf == NULL) { + strbuf = malloc(strbuf_sz); + if (strbuf == NULL) + err(1, NULL); + } + strbuf[0] = '\0'; +} + +static void +add_str(const char *str) +{ + size_t arglen; + + arglen = strlen(str); + + if (strlen(strbuf) + arglen + 1 > strbuf_sz) { + size_t newsize; + char *p; + + newsize = strbuf_sz + arglen + 1; + p = realloc(strbuf, newsize); + if (p == NULL) { + free(strbuf); + err(1, NULL); + } + strbuf_sz = newsize; + strbuf = p; + } + strlcat(strbuf, str, strbuf_sz); +} + +/* ARGSUSED */ +void +abort_line(int sig) +{ + static const char str[] = "[\n]P\n"; + int save_errno; + + switch (sig) { + default: + save_errno = errno; + YY_FLUSH_BUFFER; /* XXX signal race? */ + write(STDOUT_FILENO, str, sizeof(str) - 1); + errno = save_errno; + } +} + +int +yywrap(void) +{ + static YY_BUFFER_STATE buf; + static int state; + + if (fileindex == 0 && sargc > 0 && strcmp(sargv[0], _PATH_LIBB) == 0) { + filename = sargv[fileindex++]; + yyin = fopen(filename, "r"); + lineno = 1; + if (yyin == NULL) + err(1, "cannot open %s", filename); + return (0); + } + if (state == 0 && cmdexpr[0] != '\0') { + buf = yy_scan_string(cmdexpr); + state++; + lineno = 1; + filename = "command line"; + return (0); + } else if (state == 1) { + yy_delete_buffer(buf); + free(cmdexpr); + state++; + } + if (yyin != NULL && yyin != stdin) + fclose(yyin); + if (fileindex < sargc) { + filename = sargv[fileindex++]; + yyin = fopen(filename, "r"); + lineno = 1; + if (yyin == NULL) + err(1, "cannot open %s", filename); + return (0); + } else if (fileindex == sargc) { + fileindex++; + yyin = stdin; + if (interactive) + signal(SIGINT, abort_line); + lineno = 1; + filename = "stdin"; + return (0); + } + return (1); +} + +static int +bc_yyinput(char *buf, int maxlen) +{ + int num; + if (yyin == stdin && interactive) { + const char *bp; + + if ((bp = el_gets(el, &num)) == NULL || num == 0) + return (0); + if (num > maxlen) { + el_push(el, (char *)(uintptr_t)(bp) + maxlen); + num = maxlen; + } + memcpy(buf, bp, num); + history(hist, &he, H_ENTER, bp); + } else { + int c = '*'; + for (num = 0; num < maxlen && + (c = getc(yyin)) != EOF && c != '\n'; ++num) + buf[num] = (char) c; + if (c == '\n') + buf[num++] = (char) c; + if (c == EOF && ferror(yyin)) + YY_FATAL_ERROR( "input in flex scanner failed" ); + } + return (num); +} + diff --git a/usr.bin/biff/biff.c b/usr.bin/biff/biff.c index c04c8d3..70d2d85 100644 --- a/usr.bin/biff/biff.c +++ b/usr.bin/biff/biff.c @@ -110,7 +110,7 @@ main(int argc, char *argv[]) } static void -usage() +usage(void) { (void)fprintf(stderr, "usage: biff [n | y | b]\n"); exit(2); diff --git a/usr.bin/bluetooth/bthost/Makefile b/usr.bin/bluetooth/bthost/Makefile index fe2c479..833d107 100644 --- a/usr.bin/bluetooth/bthost/Makefile +++ b/usr.bin/bluetooth/bthost/Makefile @@ -2,7 +2,6 @@ # $FreeBSD$ PROG= bthost -WARNS?= 2 DPADD= ${LIBBLUETOOTH} LDADD= -lbluetooth diff --git a/usr.bin/brandelf/brandelf.1 b/usr.bin/brandelf/brandelf.1 index 9c7439e..689f0cc 100644 --- a/usr.bin/brandelf/brandelf.1 +++ b/usr.bin/brandelf/brandelf.1 @@ -96,7 +96,7 @@ brandelf -t Linux file .%A The Santa Cruz Operation, Inc. .%T System V Application Binary Interface .%D April 29, 1998 (DRAFT) -.%O http://www.sco.com/developer/devspecs/ +.%U http://www.sco.com/developer/devspecs/ .Re .Sh HISTORY The diff --git a/usr.bin/bsdiff/Makefile.inc b/usr.bin/bsdiff/Makefile.inc index 198d906..265f86d 100644 --- a/usr.bin/bsdiff/Makefile.inc +++ b/usr.bin/bsdiff/Makefile.inc @@ -1,6 +1,3 @@ # $FreeBSD$ -WARNS?= 6 - .include "../Makefile.inc" - diff --git a/usr.bin/bsdiff/bsdiff/bsdiff.1 b/usr.bin/bsdiff/bsdiff/bsdiff.1 index 5c608b4..4ceb9ed 100644 --- a/usr.bin/bsdiff/bsdiff/bsdiff.1 +++ b/usr.bin/bsdiff/bsdiff/bsdiff.1 @@ -65,5 +65,24 @@ an absolute minimum working set size of 8 times the size of .Ar oldfile . .Sh SEE ALSO .Xr bspatch 1 +.Sh BUGS +The +.Nm +utility does not store the hashes of +.Ar oldfile +or +.Ar newfile +in +.Ar patchfile . +As a result, it is possible to apply a patch to the wrong file; this +will usually produce garbage. +It is recommended that users of +.Nm +store the hashes of +.Ar oldfile +and +.Ar newfile +and compare against them before and after applying +.Ar patchfile . .Sh AUTHORS .An Colin Percival Aq cperciva@FreeBSD.org diff --git a/usr.bin/bsdiff/bspatch/bspatch.1 b/usr.bin/bsdiff/bspatch/bspatch.1 index 894bc50..29b8db3 100644 --- a/usr.bin/bsdiff/bspatch/bspatch.1 +++ b/usr.bin/bsdiff/bspatch/bspatch.1 @@ -61,5 +61,26 @@ but can tolerate a very small working set without a dramatic loss of performance. .Sh SEE ALSO .Xr bsdiff 1 +.Sh BUGS +The +.Nm +utility does not verify that +.Ar oldfile +is the correct source file for +.Ar patchfile . +Attempting to apply a patch to the wrong file will usually produce +garbage; consequently it is strongly recommended that users of +.Nm +verify that +.Ar oldfile +matches the source file from which +.Ar patchfile +was built, by comparing cryptographic hashes, for example. +Users may also wish to verify after running +.Nm +that +.Ar newfile +matches the target file from which +.Ar was built. .Sh AUTHORS .An Colin Percival Aq cperciva@FreeBSD.org diff --git a/usr.bin/bzip2/Makefile b/usr.bin/bzip2/Makefile index 0460fff..c2490a2 100644 --- a/usr.bin/bzip2/Makefile +++ b/usr.bin/bzip2/Makefile @@ -6,6 +6,8 @@ BZ2DIR= ${.CURDIR}/../../contrib/bzip2 PROG= bzip2 CFLAGS+= -D_FILE_OFFSET_BITS=64 +WARNS?= 3 + DPADD= ${LIBBZ2} LDADD= -lbz2 diff --git a/usr.bin/calendar/Makefile b/usr.bin/calendar/Makefile index 481713a..27df8b6 100644 --- a/usr.bin/calendar/Makefile +++ b/usr.bin/calendar/Makefile @@ -9,6 +9,8 @@ DE_LINKS= de_DE.ISO8859-15 FR_LINKS= fr_FR.ISO8859-15 TEXTMODE?= 444 +WARNS?= 3 + beforeinstall: ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${TEXTMODE} \ ${.CURDIR}/calendars/calendar.* ${DESTDIR}${SHAREDIR}/calendar diff --git a/usr.bin/calendar/calendar.c b/usr.bin/calendar/calendar.c index d1bb745..39f2c9c 100644 --- a/usr.bin/calendar/calendar.c +++ b/usr.bin/calendar/calendar.c @@ -55,7 +55,6 @@ __FBSDID("$FreeBSD$"); #include <time.h> #include <unistd.h> -#include "pathnames.h" #include "calendar.h" struct passwd *pw; diff --git a/usr.bin/calendar/calendars/calendar.all b/usr.bin/calendar/calendars/calendar.all index b46dcb4..545c0e4 100644 --- a/usr.bin/calendar/calendars/calendar.all +++ b/usr.bin/calendar/calendars/calendar.all @@ -8,13 +8,16 @@ #define _calendar_all_ #include <calendar.world> +#include <calendar.australia> #include <calendar.croatian> #include <calendar.dutch> #include <calendar.french> #include <calendar.german> #include <calendar.hungarian> -#include <calendar.southafrica> +#include <calendar.newzealand> #include <calendar.russian> +#include <calendar.southafrica> +#include <calendar.ukrainian> #include <calendar.usholiday> #endif /* !_calendar_all_ */ diff --git a/usr.bin/calendar/calendars/calendar.dutch b/usr.bin/calendar/calendars/calendar.dutch index 3094ddd..47b1bf5 100644 --- a/usr.bin/calendar/calendars/calendar.dutch +++ b/usr.bin/calendar/calendars/calendar.dutch @@ -5,6 +5,7 @@ */ LANG=nl_NL.ISO8859-15 +Easter=Pasen /* * Feestdagen @@ -32,21 +33,21 @@ LANG=nl_NL.ISO8859-15 /* * Pasen gerelateerd */ -Easter-50 Carnaval -Easter-49 Carnaval -Easter-48 Carnaval -Easter-47 Carnaval (Vastenavond) -Easter-46 Aswoensdag -Easter-7 Palmzondag -Easter-3 Witte Donderdag -Easter-2 Goede vrijdag -Easter-1 Stille zaterdag -Easter Eerste paasdag -Easter+1 Tweede paasdag -Easter+39 Hemelvaartsdag -Easter+49 Eerste Pinksterdag -Easter+50 Tweede Pinksterdag -Easter+56 Trinitatis +Pasen-50 Carnaval +Pasen-49 Carnaval +Pasen-48 Carnaval +Pasen-47 Carnaval (Vastenavond) +Pasen-46 Aswoensdag +Pasen-7 Palmzondag +Pasen-3 Witte Donderdag +Pasen-2 Goede vrijdag +Pasen-1 Stille zaterdag +Pasen Eerste paasdag +Pasen+1 Tweede paasdag +Pasen+39 Hemelvaartsdag +Pasen+49 Eerste Pinksterdag +Pasen+50 Tweede Pinksterdag +Pasen+56 Trinitatis /* * Misc diff --git a/usr.bin/calendar/calendars/calendar.freebsd b/usr.bin/calendar/calendars/calendar.freebsd index a280337..3c7d71d 100644 --- a/usr.bin/calendar/calendars/calendar.freebsd +++ b/usr.bin/calendar/calendars/calendar.freebsd @@ -134,6 +134,7 @@ 05/11 Jesus Rodriguez <jesusr@FreeBSD.org> born in Barcelona, Spain, 1972 05/11 Roman Kurakin <rik@FreeBSD.org> born in Moscow, USSR, 1979 05/13 Pete Fritchman <petef@FreeBSD.org> born in Lansdale, Pennsylvania, United States, 1983 +05/14 Bruce Cran <brucec@FreeBSD.org> born in Cambridge, United Kingdom, 1981 05/14 Tatsumi Hosokawa <hosokawa@FreeBSD.org> born in Tokyo, Japan, 1968 05/14 Shigeyuku Fukushima <shige@FreeBSD.org> born in Osaka, Japan, 1974 05/16 Johann Kois <jkois@FreeBSD.org> born in Wolfsberg, Austria, 1975 @@ -158,6 +159,7 @@ 06/02 Jean-Marc Zucconi <jmz@FreeBSD.org> born in Pontarlier, France, 1954 06/02 Alexander Botero-Lowry <alexbl@FreeBSD.org> born in Austin, TX, USA, 1986 06/03 CHOI Junho <cjh@FreeBSD.org> born in Seoul, Korea, 1974 +06/03 Wesley Shields <wxs@FreeBSD.org> born in Binghamton, NY, USA, 1981 06/04 Julian Elischer <julian@FreeBSD.org> born in Perth, Australia, 1959 06/04 Jason Evans <jasone@FreeBSD.org> born in Greeley, Colorado, United States, 1973 06/04 Justin Gibbs <gibbs@FreeBSD.org> born in San Pedro, California, United States, 1973 @@ -203,6 +205,7 @@ 07/22 Lukas Ertl <le@FreeBSD.org> born in Weissenbach/Enns, Steiermark, Austria, 1976 07/23 Sergey A. Osokin <osa@FreeBSD.org> born in Krasnogorsky, Stepnogorsk, Akmolinskaya region, Kazakhstan, 1972 07/24 Alexander Nedotsukov <bland@FreeBSD.org> born in Ulyanovsk, Russian Federation, 1974 +07/24 Alberto Villa <avilla@FreeBSD.org> born in Vercelli, Italy, 1987 07/28 Jim Mock <jim@FreeBSD.org> born in Bethlehem, Pennsylvania, United States, 1974 07/28 Tom Hukins <tom@FreeBSD.org> born in Manchester, United Kingdom, 1976 07/29 Dirk Meyer <dinoex@FreeBSD.org> born in Kassel, Hessen, Germany, 1965 @@ -244,6 +247,7 @@ 09/10 Wesley R. Peters <wes@FreeBSD.org> born in Hartford, Alabama, United States, 1961 09/12 Weongyo Jeong <weongyo@FreeBSD.org> born in Haman, Korea, 1980 09/12 William C. Fumerola II <billf@FreeBSD.org> born in Detroit, Michigan, United States, 1981 +09/12 Benedict Christopher Reuschling <bcr@FreeBSD.org> born in Darmstadt, Germany, 1981 09/15 Dima Panov <fluffy@FreeBSD.org> born in Khabarovsk, Russian Federation, 1978 09/17 Maxim Bolotin <mb@FreeBSD.org> born in Rostov-on-Don, Russian Federation, 1976 09/20 Kevin Lo <kevlo@FreeBSD.org> born in Taipei, Taiwan, Republic of China, 1972 @@ -253,6 +257,7 @@ 09/28 Alex Dupre <ale@FreeBSD.org> born in Milano, Italy, 1980 09/29 Matthew Hunt <mph@FreeBSD.org> born in Johnstown, Pennsylvania, United States, 1976 09/30 Hiten M. Pandya <hmp@FreeBSD.org> born in Dar-es-Salaam, Tanzania, East Africa, 1986 +10/02 Beat Gaetzi <beat@FreeBSD.org> born in Zurich, Switzerland, 1980 10/05 Hiroki Sato <hrs@FreeBSD.org> born in Yamagata, Japan, 1977 10/05 Chris Costello <chris@FreeBSD.org> born in Houston, Texas, United States, 1985 10/09 Stefan Walter <stefan@FreeBSD.org> born in Werne, Nordrhein-Westfalen, 1978 @@ -283,6 +288,7 @@ 11/18 Thomas Quinot <thomas@FreeBSD.org> born in Paris, France, 1977 11/19 Konstantin Belousov <kib@FreeBSD.org> born in Kiev, USSR, 1972 11/20 Dmitry Morozovsky <marck@FreeBSD.org> born in Moscow, USSR, 1968 +11/20 Gavin Atkinson <gavin@FreeBSD.org> born in Middlesbrough, United Kingdom, 1979 11/23 Josef Lawrence Karthauser <joe@FreeBSD.org> born in Pembury, Kent, United Kingdom, 1972 11/24 Andrey Zakhvatov <andy@FreeBSD.org> born in Chelyabinsk, Russian Federation, 1974 11/24 Daniel Gerzo <danger@FreeBSD.org> born in Bratislava, Slovakia, 1986 diff --git a/usr.bin/calendar/day.c b/usr.bin/calendar/day.c index d3f3c1e..e40481e 100644 --- a/usr.bin/calendar/day.c +++ b/usr.bin/calendar/day.c @@ -44,7 +44,6 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include <time.h> -#include "pathnames.h" #include "calendar.h" struct tm *tp; diff --git a/usr.bin/calendar/io.c b/usr.bin/calendar/io.c index 92df7be..03f28fe 100644 --- a/usr.bin/calendar/io.c +++ b/usr.bin/calendar/io.c @@ -467,7 +467,7 @@ closecal(FILE *fp) if (!doall) return; - (void)rewind(fp); + rewind(fp); if (fstat(fileno(fp), &sbuf) || !sbuf.st_size) goto done; if (pipe(pdes) < 0) diff --git a/usr.bin/calendar/ostern.c b/usr.bin/calendar/ostern.c index 76e5933..89e7b1c 100644 --- a/usr.bin/calendar/ostern.c +++ b/usr.bin/calendar/ostern.c @@ -30,7 +30,6 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <time.h> #include "calendar.h" @@ -88,7 +87,7 @@ geteaster(char *s, int year) else return (0); -#if DEBUG +#ifdef DEBUG printf("%s %d %d\n", s, year, EASTERNAMELEN); #endif diff --git a/usr.bin/calendar/paskha.c b/usr.bin/calendar/paskha.c index d96ffc2..e713f5f 100644 --- a/usr.bin/calendar/paskha.c +++ b/usr.bin/calendar/paskha.c @@ -30,7 +30,6 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <time.h> #include "calendar.h" diff --git a/usr.bin/catman/Makefile b/usr.bin/catman/Makefile index d80a5fd..ab4c014 100644 --- a/usr.bin/catman/Makefile +++ b/usr.bin/catman/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= catman -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/catman/catman.c b/usr.bin/catman/catman.c index ba3ad24..c17a091 100644 --- a/usr.bin/catman/catman.c +++ b/usr.bin/catman/catman.c @@ -589,9 +589,15 @@ process_section(char *mandir, char *section) } static int -select_sections(struct dirent *entry) +select_sections(const struct dirent *entry) { - return directory_type(entry->d_name) == MAN_SECTION_DIR; + char *name; + int ret; + + name = strdup(entry->d_name); + ret = directory_type(name) == MAN_SECTION_DIR; + free(name); + return (ret); } /* diff --git a/usr.bin/chkey/Makefile b/usr.bin/chkey/Makefile index 2813ca1..847e5c9 100644 --- a/usr.bin/chkey/Makefile +++ b/usr.bin/chkey/Makefile @@ -13,6 +13,4 @@ CFLAGS+= -DYP DPADD= ${LIBRPCSVC} ${LIBMP} ${LIBCRYPTO} LDADD= -lrpcsvc -lmp -lcrypto -WARNS?= 6 - .include <bsd.prog.mk> diff --git a/usr.bin/chpass/Makefile b/usr.bin/chpass/Makefile index e958956..7f7ac51 100644 --- a/usr.bin/chpass/Makefile +++ b/usr.bin/chpass/Makefile @@ -9,7 +9,6 @@ PROG= chpass SRCS= chpass.c edit.c field.c pw_scan.c table.c util.c BINOWN= root BINMODE=4555 -WARNS?= 5 .if ${MK_NIS} != "no" CFLAGS+= -DYP .endif diff --git a/usr.bin/chpass/chpass.1 b/usr.bin/chpass/chpass.1 index 24b1759..a926607 100644 --- a/usr.bin/chpass/chpass.1 +++ b/usr.bin/chpass/chpass.1 @@ -168,7 +168,7 @@ that manipulate these files will often return only one of the multiple entries, and that one by random selection. .Pp The -.Ar group +.Ar gid field is the group that the user will be placed in at login. Since .Bx diff --git a/usr.bin/colldef/Makefile b/usr.bin/colldef/Makefile index 6cb213f..5f62bc5 100644 --- a/usr.bin/colldef/Makefile +++ b/usr.bin/colldef/Makefile @@ -8,4 +8,6 @@ CFLAGS+=-DCOLLATE_DEBUG -DYY_NO_UNPUT LDADD= -ll DPADD= ${LIBL} +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/comm/comm.1 b/usr.bin/comm/comm.1 index 2402ab5..02b508c 100644 --- a/usr.bin/comm/comm.1 +++ b/usr.bin/comm/comm.1 @@ -35,7 +35,7 @@ .\" From: @(#)comm.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd January 26, 2005 +.Dd December 12, 2009 .Os .Dt COMM 1 .Sh NAME @@ -118,7 +118,3 @@ A .Nm command appeared in .At v4 . -.Sh BUGS -Input lines are limited to -.Dv LINE_MAX -(2048) characters in length. diff --git a/usr.bin/comm/comm.c b/usr.bin/comm/comm.c index ea92673..afda0e7 100644 --- a/usr.bin/comm/comm.c +++ b/usr.bin/comm/comm.c @@ -52,6 +52,7 @@ __FBSDID("$FreeBSD$"); #include <err.h> #include <limits.h> #include <locale.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -59,7 +60,8 @@ __FBSDID("$FreeBSD$"); #include <wchar.h> #include <wctype.h> -#define MAXLINELEN (LINE_MAX + 1) +#define INITLINELEN (LINE_MAX + 1) +#define MAXLINELEN ((SIZE_MAX / sizeof(wchar_t)) / 2) const wchar_t *tabs[] = { L"", L"\t", L"\t\t" }; @@ -83,8 +85,8 @@ main(int argc, char *argv[]) flag1 = flag2 = flag3 = 1; iflag = 0; - line1len = MAXLINELEN; - line2len = MAXLINELEN; + line1len = INITLINELEN; + line2len = INITLINELEN; line1 = malloc(line1len * sizeof(*line1)); line2 = malloc(line2len * sizeof(*line2)); if (line1 == NULL || line2 == NULL) @@ -163,7 +165,7 @@ main(int argc, char *argv[]) if (!comp) { read1 = read2 = 1; if (col3 != NULL) - (void)printf("%ls%ls", col3, line1); + (void)printf("%ls%ls\n", col3, line1); continue; } @@ -172,12 +174,12 @@ main(int argc, char *argv[]) read1 = 1; read2 = 0; if (col1 != NULL) - (void)printf("%ls%ls", col1, line1); + (void)printf("%ls%ls\n", col1, line1); } else { read1 = 0; read2 = 1; if (col2 != NULL) - (void)printf("%ls%ls", col2, line2); + (void)printf("%ls%ls\n", col2, line2); } } exit(0); @@ -190,19 +192,20 @@ getline(wchar_t *buf, size_t *buflen, FILE *fp) wint_t ch; bufpos = 0; - do { - if ((ch = getwc(fp)) != WEOF) { - if (bufpos + 2 >= *buflen) { - *buflen = *buflen * 2; - buf = reallocf(buf, *buflen * sizeof(*buf)); - if (buf == NULL) - return (NULL); - } - buf[bufpos++] = ch; + while ((ch = getwc(fp)) != WEOF && ch != '\n') { + if (bufpos + 1 >= *buflen) { + *buflen = *buflen * 2; + if (*buflen > MAXLINELEN) + errx(1, + "Maximum line buffer length (%zu) exceeded", + MAXLINELEN); + buf = reallocf(buf, *buflen * sizeof(*buf)); + if (buf == NULL) + err(1, "reallocf"); } - } while (ch != WEOF && ch != '\n'); - if (bufpos + 1 != *buflen) - buf[bufpos] = '\0'; + buf[bufpos++] = ch; + } + buf[bufpos] = '\0'; return (bufpos != 0 || ch == '\n' ? buf : NULL); } @@ -212,7 +215,7 @@ show(FILE *fp, const char *fn, const wchar_t *offset, wchar_t *buf, size_t *bufl { do { - (void)printf("%ls%ls", offset, buf); + (void)printf("%ls%ls\n", offset, buf); } while ((buf = getline(buf, buflen, fp)) != NULL); if (ferror(fp)) err(1, "%s", fn); @@ -254,13 +257,13 @@ wcsicoll(const wchar_t *s1, const wchar_t *s2) new_l2_buflen = wcsicoll_l2_buflen; while (new_l1_buflen < l1) { if (new_l1_buflen == 0) - new_l1_buflen = MAXLINELEN; + new_l1_buflen = INITLINELEN; else new_l1_buflen *= 2; } while (new_l2_buflen < l2) { if (new_l2_buflen == 0) - new_l2_buflen = MAXLINELEN; + new_l2_buflen = INITLINELEN; else new_l2_buflen *= 2; } diff --git a/usr.bin/compile_et/Makefile b/usr.bin/compile_et/Makefile index 3d4ab15..262e696 100644 --- a/usr.bin/compile_et/Makefile +++ b/usr.bin/compile_et/Makefile @@ -6,4 +6,6 @@ PROG= compile_et SRCS= compile_et.c parse.y lex.l getarg.c CFLAGS+=-I. -I${.CURDIR}/../../contrib/com_err +WARNS?= 0 + .include <bsd.prog.mk> diff --git a/usr.bin/compress/Makefile b/usr.bin/compress/Makefile index 45c0814..a586b97f 100644 --- a/usr.bin/compress/Makefile +++ b/usr.bin/compress/Makefile @@ -5,7 +5,6 @@ PROG= compress SRCS= compress.c zopen.c LINKS= ${BINDIR}/compress ${BINDIR}/uncompress MLINKS= compress.1 uncompress.1 -WARNS?= 6 # XXX zopen is not part of libc # MAN=zopen.3 diff --git a/usr.bin/cpio/Makefile b/usr.bin/cpio/Makefile index c64ab6c..bfcbe97 100644 --- a/usr.bin/cpio/Makefile +++ b/usr.bin/cpio/Makefile @@ -5,8 +5,6 @@ PROG= bsdcpio BSDCPIO_VERSION_STRING=2.7.0 SRCS= cpio.c cmdline.c err.c matching.c pathmatch.c -WARNS?= 6 -DPADD= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2} CFLAGS+= -DBSDCPIO_VERSION_STRING=\"${BSDCPIO_VERSION_STRING}\" CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\" .ifdef RELEASE_CRUNCH @@ -14,8 +12,10 @@ CFLAGS+= -DPLATFORM_CONFIG_H=\"config_freebsd.h\" # statically linked, cannot use -lcrypto, and are size sensitive. CFLAGS+= -DSMALLER .endif -LDADD+= -larchive -lz -lbz2 -lmd +DPADD= ${LIBARCHIVE} ${LIBZ} ${LIBBZ2} ${LIBMD} +LDADD= -larchive -lz -lbz2 -lmd .if ${MK_OPENSSL} != "no" +DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto .endif diff --git a/usr.bin/cpio/bsdcpio.1 b/usr.bin/cpio/bsdcpio.1 index 81b3462..6017bc5 100644 --- a/usr.bin/cpio/bsdcpio.1 +++ b/usr.bin/cpio/bsdcpio.1 @@ -266,7 +266,7 @@ for more information. .Sh EXAMPLES The .Nm -command is traditionally used to copy file heirarchies in conjunction +command is traditionally used to copy file hierarchies in conjunction with the .Xr find 1 command. diff --git a/usr.bin/cpio/cmdline.c b/usr.bin/cpio/cmdline.c index 9b61728..b6a56be 100644 --- a/usr.bin/cpio/cmdline.c +++ b/usr.bin/cpio/cmdline.c @@ -50,7 +50,7 @@ __FBSDID("$FreeBSD$"); /* * Short options for cpio. Please keep this sorted. */ -static const char *short_options = "0AaBC:F:O:cdE:f:H:hijLlmnopR:rtuvW:yZz"; +static const char *short_options = "0AaBC:F:O:cdE:f:H:hijLlmnopR:rtuvVW:yZz"; /* * Long options for cpio. Please keep this sorted. @@ -61,6 +61,7 @@ static const struct option { int equivalent; /* Equivalent short option. */ } cpio_longopts[] = { { "create", 0, 'o' }, + { "dot", 0, 'V' }, { "extract", 0, 'i' }, { "file", 1, 'F' }, { "format", 1, 'H' }, diff --git a/usr.bin/cpio/cpio.c b/usr.bin/cpio/cpio.c index 3b626f7..53195aa 100644 --- a/usr.bin/cpio/cpio.c +++ b/usr.bin/cpio/cpio.c @@ -278,6 +278,9 @@ main(int argc, char *argv[]) case 'v': /* POSIX 1997 */ cpio->verbose++; break; + case 'V': /* GNU cpio */ + cpio->dot++; + break; case OPTION_VERSION: /* GNU convention */ version(); break; @@ -331,6 +334,9 @@ main(int argc, char *argv[]) /* -l requires -p */ if (cpio->option_link && cpio->mode != 'p') cpio_errc(1, 0, "Option -l requires -p"); + /* -v overrides -V */ + if (cpio->dot && cpio->verbose) + cpio->dot = 0; /* TODO: Flag other nonsensical combinations. */ switch (cpio->mode) { @@ -388,7 +394,7 @@ static const char *long_help_msg = "First option must be a mode specifier:\n" " -i Input -o Output -p Pass\n" "Common Options:\n" - " -v Verbose\n" + " -v Verbose filenames -V one dot per file\n" "Create: %p -o [options] < [list of files] > [archive]\n" #ifdef HAVE_BZLIB_H " -y Compress archive with bzip2\n" @@ -521,6 +527,8 @@ mode_out(struct cpio *cpio) } r = archive_write_close(cpio->archive); + if (cpio->dot) + fprintf(stderr, "\n"); if (r != ARCHIVE_OK) cpio_errc(1, 0, archive_error_string(cpio->archive)); @@ -659,6 +667,8 @@ entry_to_archive(struct cpio *cpio, struct archive_entry *entry) /* Print out the destination name to the user. */ if (cpio->verbose) fprintf(stderr,"%s", destpath); + if (cpio->dot) + fprintf(stderr, "."); /* * Option_link only makes sense in pass mode and for @@ -857,7 +867,9 @@ mode_in(struct cpio *cpio) if (destpath == NULL) continue; if (cpio->verbose) - fprintf(stdout, "%s\n", destpath); + fprintf(stderr, "%s\n", destpath); + if (cpio->dot) + fprintf(stderr, "."); if (cpio->uid_override >= 0) archive_entry_set_uid(entry, cpio->uid_override); if (cpio->gid_override >= 0) @@ -872,6 +884,8 @@ mode_in(struct cpio *cpio) } } r = archive_read_close(a); + if (cpio->dot) + fprintf(stderr, "\n"); if (r != ARCHIVE_OK) cpio_errc(1, 0, archive_error_string(a)); r = archive_write_close(ext); @@ -1078,6 +1092,8 @@ mode_pass(struct cpio *cpio, const char *destdir) archive_entry_linkresolver_free(cpio->linkresolver); r = archive_write_close(cpio->archive); + if (cpio->dot) + fprintf(stderr, "\n"); if (r != ARCHIVE_OK) cpio_errc(1, 0, archive_error_string(cpio->archive)); diff --git a/usr.bin/cpio/cpio.h b/usr.bin/cpio/cpio.h index 84932992..8c18737 100644 --- a/usr.bin/cpio/cpio.h +++ b/usr.bin/cpio/cpio.h @@ -52,6 +52,7 @@ struct cpio { const char *format; /* -H format */ int bytes_per_block; /* -b block_size */ int verbose; /* -v */ + int dot; /* -V */ int quiet; /* --quiet */ int extract_flags; /* Flags for extract operation */ char symlink_mode; /* H or L, per BSD conventions */ diff --git a/usr.bin/cpio/test/Makefile b/usr.bin/cpio/test/Makefile index 7da5a60..ace9516 100644 --- a/usr.bin/cpio/test/Makefile +++ b/usr.bin/cpio/test/Makefile @@ -53,7 +53,6 @@ CFLAGS+= -I${CPIO_SRCDIR} # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC -WARNS=6 check test: bsdcpio_test ${.OBJDIR}/bsdcpio_test -p ${.OBJDIR}/../bsdcpio -r ${.CURDIR} diff --git a/usr.bin/cpuset/Makefile b/usr.bin/cpuset/Makefile index 85a5148..660a096 100644 --- a/usr.bin/cpuset/Makefile +++ b/usr.bin/cpuset/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= cpuset -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/csup/Makefile b/usr.bin/csup/Makefile index 37536fa..af1815c 100644 --- a/usr.bin/csup/Makefile +++ b/usr.bin/csup/Makefile @@ -1,40 +1,24 @@ # $FreeBSD$ -.PATH: ${.CURDIR}/../../contrib/csup +PREFIX?= /usr/local +BINDIR?= ${PREFIX}/bin +MANDIR?= ${PREFIX}/man/man + +UNAME!= /usr/bin/uname -s PROG= csup -SRCS= attrstack.c \ - config.c \ - detailer.c \ - diff.c \ - fattr.c \ - fixups.c \ - fnmatch.c \ - globtree.c \ - idcache.c \ - keyword.c \ - lex.rcs.c \ - lister.c \ - main.c \ - misc.c \ - mux.c \ - parse.y \ - pathcomp.c \ - proto.c \ - rcsfile.c \ - rcsparse.c \ - rsyncfile.c \ - status.c \ - stream.c \ - threads.c \ - token.l \ - updater.c +SRCS= attrstack.c auth.c config.c detailer.c diff.c fattr.c fixups.c fnmatch.c \ + globtree.c idcache.c keyword.c lister.c main.c misc.c mux.c parse.y \ + pathcomp.c proto.c status.c stream.c threads.c token.l updater.c \ + rcsfile.c rcsparse.c lex.rcs.c rsyncfile.c + +CFLAGS+= -I. -I${.CURDIR} -g -pthread -DHAVE_FFLAGS -DNDEBUG +WARNS?= 1 -CFLAGS+= -I. -I${.CURDIR}/../../contrib/csup -CFLAGS+= -DHAVE_FFLAGS -DNDEBUG -WARNS?= 1 +DPADD= ${LIBCRYPTO} ${LIBZ} +LDADD= -lcrypto -lz -DPADD= ${LIBCRYPTO} ${LIBZ} ${LIBPTHREAD} -LDADD= -lcrypto -lz -lpthread +SCRIPTS= cpasswd.sh +MAN= csup.1 cpasswd.1 .include <bsd.prog.mk> diff --git a/usr.bin/csup/README b/usr.bin/csup/README new file mode 100644 index 0000000..879c481 --- /dev/null +++ b/usr.bin/csup/README @@ -0,0 +1,39 @@ +$FreeBSD$ + +Authors +------- + +CVSup was originally written in Modula-3 by + John Polstra <jdp@polstra.com>. + +Csup is a rewrite of CVSup in C. It has been mostly written by + Maxime Henrion <mux@FreeBSD.org>. + +A few contributors have helped him in his task and they are listed here in +alphabetical order : + + Olivier Houchard <cognet@FreeBSD.org> + Ulf Lilleengen <lulf@kerneled.org> + Christoph Mathys <cmathys@bluewin.ch> (Google SoC Project) + Etienne Vidal <etienne.vidal@gmail.com> + + +Building & Installing +--------------------- + +Csup should build and run fine under any *BSD OS (that includes FreeBSD, +NetBSD, OpenBSD and DragonFlyBSD), as well as Linux and Darwin. If you +have a problem building from source, drop me a mail! + +There is one Makefile specifically tailored for *BSD systems named +Makefile and another one that is gmake-specific for Darwin and Linux +users named GNUmakefile. You don't really need to worry about that +since whatever your "make" command is, it should pick up the correct +Makefile. + +As usual, to build the source code, just run "make". Once this is done, +just run "make install" to install the binary and manual page. + +Be warned however that if the packaging system of your OS knows about +csup, it is certainly better to install it from there rather than by +hand, so that it can then be properly deinstalled. diff --git a/usr.bin/csup/TODO b/usr.bin/csup/TODO new file mode 100644 index 0000000..9854d35 --- /dev/null +++ b/usr.bin/csup/TODO @@ -0,0 +1,28 @@ +$FreeBSD$ + +BUGS: + +- Fix every XXX in the code :-). +- The stream API needs some polishing. It needs proper error numbers + and a stream_error() function similar to the ferror() function. +- The yacc/lex code to parse the configuration file is sub-optimal. It + has global variables because of yacc, but I think it should be possible + to do it better by using YYFUNC_PROTOTYPE or something. I think it + should also be possible to completely get rid of the lex file. +- The $Log$ CVS keyword is not supported. +- Add missing support for supfile keywords and add sanity checks for + some of them. Also, we're not supposed to choke on unknown keywords + to stay in line with CVSup, which just ignores them in order to + maintain compatibility with sup configuration files. + +MISSING FEATURES: + +- Add support for shell commands sent by the server. +- Add missing support for various CVSup options : -D, -e and -E (requires + shell commands support) and the destDir parameter. +- For now, this code should build fine on FreeBSD, NetBSD, OpenBSD, + Linux and Darwin. Solaris support would also be nice at some point. +- Implement some new useful options : the ability to generate CVS + checkout files (files in CVS/ subdirectores), a command line override + to only update a specific collection and a third verbosity level to + display commit log messages. diff --git a/usr.bin/csup/attrstack.c b/usr.bin/csup/attrstack.c new file mode 100644 index 0000000..f5f56b3 --- /dev/null +++ b/usr.bin/csup/attrstack.c @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $Id$ + */ + +#include <assert.h> +#include <stdlib.h> + +#include "attrstack.h" +#include "fattr.h" +#include "misc.h" + +#define ATTRSTACK_DEFSIZE 16 /* Initial size of the stack. */ + +struct attrstack { + struct fattr **stack; + size_t cur; + size_t size; +}; + +struct attrstack * +attrstack_new(void) +{ + struct attrstack *as; + + as = xmalloc(sizeof(struct attrstack)); + as->stack = xmalloc(sizeof(struct fattr *) * ATTRSTACK_DEFSIZE); + as->size = ATTRSTACK_DEFSIZE; + as->cur = 0; + return (as); +} + +struct fattr * +attrstack_pop(struct attrstack *as) +{ + + assert(as->cur > 0); + return (as->stack[--as->cur]); +} + +void +attrstack_push(struct attrstack *as, struct fattr *fa) +{ + + if (as->cur >= as->size) { + as->size *= 2; + as->stack = xrealloc(as->stack, + sizeof(struct fattr *) * as->size); + } + as->stack[as->cur++] = fa; +} + +size_t +attrstack_size(struct attrstack *as) +{ + + return (as->cur); +} + +void +attrstack_free(struct attrstack *as) +{ + + assert(as->cur == 0); + free(as->stack); + free(as); +} diff --git a/usr.bin/csup/attrstack.h b/usr.bin/csup/attrstack.h new file mode 100644 index 0000000..721313c --- /dev/null +++ b/usr.bin/csup/attrstack.h @@ -0,0 +1,40 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $Id$ + */ +#ifndef _ATTRSTACK_H_ +#define _ATTRSTACK_H_ + +struct fattr; +struct attrstack; + +struct attrstack *attrstack_new(void); +void attrstack_push(struct attrstack *, struct fattr *); +struct fattr *attrstack_pop(struct attrstack *); +size_t attrstack_size(struct attrstack *); +void attrstack_free(struct attrstack *); + +#endif /* !_ATTRSTACK_H_ */ diff --git a/usr.bin/csup/auth.c b/usr.bin/csup/auth.c new file mode 100644 index 0000000..4e79bc5 --- /dev/null +++ b/usr.bin/csup/auth.c @@ -0,0 +1,331 @@ +/*- + * Copyright (c) 2003-2007, Petar Zhivkov Petrov <pesho.petrov@gmail.com> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/time.h> +#include <sys/types.h> + +#include <arpa/inet.h> +#include <netinet/in.h> + +#include <ctype.h> +#include <openssl/md5.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "auth.h" +#include "config.h" +#include "misc.h" +#include "proto.h" +#include "stream.h" + +#define MD5_BYTES 16 + +/* This should be at least 2 * MD5_BYTES + 6 (length of "$md5$" + 1) */ +#define MD5_CHARS_MAX (2*(MD5_BYTES)+6) + +struct srvrecord { + char server[MAXHOSTNAMELEN]; + char client[256]; + char password[256]; +}; + +static int auth_domd5auth(struct config *); +static int auth_lookuprecord(char *, struct srvrecord *); +static int auth_parsetoken(char **, char *, int); +static void auth_makesecret(struct srvrecord *, char *); +static void auth_makeresponse(char *, char *, char *); +static void auth_readablesum(unsigned char *, char *); +static void auth_makechallenge(struct config *, char *); +static int auth_checkresponse(char *, char *, char *); + +int auth_login(struct config *config) +{ + struct stream *s; + char hostbuf[MAXHOSTNAMELEN]; + char *login, *host; + int error; + + s = config->server; + error = gethostname(hostbuf, sizeof(hostbuf)); + hostbuf[sizeof(hostbuf) - 1] = '\0'; + if (error) + host = NULL; + else + host = hostbuf; + login = getlogin(); + proto_printf(s, "USER %s %s\n", login != NULL ? login : "?", + host != NULL ? host : "?"); + stream_flush(s); + error = auth_domd5auth(config); + return (error); +} + +static int +auth_domd5auth(struct config *config) +{ + struct stream *s; + char *line, *cmd, *challenge, *realm, *client, *srvresponse, *msg; + char shrdsecret[MD5_CHARS_MAX], response[MD5_CHARS_MAX]; + char clichallenge[MD5_CHARS_MAX]; + struct srvrecord auth; + int error; + + lprintf(2, "MD5 authentication started\n"); + s = config->server; + line = stream_getln(s, NULL); + cmd = proto_get_ascii(&line); + realm = proto_get_ascii(&line); + challenge = proto_get_ascii(&line); + if (challenge == NULL || + line != NULL || + (strcmp(cmd, "AUTHMD5") != 0)) { + lprintf(-1, "Invalid server reply to USER\n"); + return (STATUS_FAILURE); + } + + client = NULL; + response[0] = clichallenge[0] = '.'; + response[1] = clichallenge[1] = 0; + if (config->reqauth || (strcmp(challenge, ".") != 0)) { + if (strcmp(realm, ".") == 0) { + lprintf(-1, "Authentication required, but not enabled on server\n"); + return (STATUS_FAILURE); + } + error = auth_lookuprecord(realm, &auth); + if (error != STATUS_SUCCESS) + return (error); + client = auth.client; + auth_makesecret(&auth, shrdsecret); + } + + if (strcmp(challenge, ".") != 0) + auth_makeresponse(challenge, shrdsecret, response); + if (config->reqauth) + auth_makechallenge(config, clichallenge); + proto_printf(s, "AUTHMD5 %s %s %s\n", + client == NULL ? "." : client, response, clichallenge); + stream_flush(s); + line = stream_getln(s, NULL); + cmd = proto_get_ascii(&line); + if (cmd == NULL || line == NULL) + goto bad; + if (strcmp(cmd, "OK") == 0) { + srvresponse = proto_get_ascii(&line); + if (srvresponse == NULL) + goto bad; + if (config->reqauth && + !auth_checkresponse(srvresponse, clichallenge, shrdsecret)) { + lprintf(-1, "Server failed to authenticate itself to client\n"); + return (STATUS_FAILURE); + } + lprintf(2, "MD5 authentication successfull\n"); + return (STATUS_SUCCESS); + } + if (strcmp(cmd, "!") == 0) { + msg = proto_get_rest(&line); + if (msg == NULL) + goto bad; + lprintf(-1, "Server error: %s\n", msg); + return (STATUS_FAILURE); + } +bad: + lprintf(-1, "Invalid server reply to AUTHMD5\n"); + return (STATUS_FAILURE); +} + +static int +auth_lookuprecord(char *server, struct srvrecord *auth) +{ + char *home, *line, authfile[FILENAME_MAX]; + struct stream *s; + int linenum = 0, error; + + home = getenv("HOME"); + if (home == NULL) { + lprintf(-1, "Environment variable \"HOME\" is not set\n"); + return (STATUS_FAILURE); + } + snprintf(authfile, sizeof(authfile), "%s/%s", home, AUTHFILE); + s = stream_open_file(authfile, O_RDONLY); + if (s == NULL) { + lprintf(-1, "Could not open file %s\n", authfile); + return (STATUS_FAILURE); + } + + while ((line = stream_getln(s, NULL)) != NULL) { + linenum++; + if (line[0] == '#' || line[0] == '\0') + continue; + error = auth_parsetoken(&line, auth->server, + sizeof(auth->server)); + if (error != STATUS_SUCCESS) { + lprintf(-1, "%s:%d Missng client name\n", authfile, linenum); + goto close; + } + /* Skip the rest of this line, it isn't what we are looking for. */ + if (strcmp(auth->server, server) != 0) + continue; + error = auth_parsetoken(&line, auth->client, + sizeof(auth->client)); + if (error != STATUS_SUCCESS) { + lprintf(-1, "%s:%d Missng password\n", authfile, linenum); + goto close; + } + error = auth_parsetoken(&line, auth->password, + sizeof(auth->password)); + if (error != STATUS_SUCCESS) { + lprintf(-1, "%s:%d Missng comment\n", authfile, linenum); + goto close; + } + stream_close(s); + lprintf(2, "Found authentication record for server \"%s\"\n", + server); + return (STATUS_SUCCESS); + } + lprintf(-1, "Unknown server \"%s\". Fix your %s\n", server , authfile); + memset(auth->password, 0, sizeof(auth->password)); +close: + stream_close(s); + return (STATUS_FAILURE); +} + +static int +auth_parsetoken(char **line, char *buf, int len) +{ + char *colon; + + colon = strchr(*line, ':'); + if (colon == NULL) + return (STATUS_FAILURE); + *colon = 0; + buf[len - 1] = 0; + strncpy(buf, *line, len - 1); + *line = colon + 1; + return (STATUS_SUCCESS); +} + +static void +auth_makesecret(struct srvrecord *auth, char *secret) +{ + char *s, ch; + const char *md5salt = "$md5$"; + unsigned char md5sum[MD5_BYTES]; + MD5_CTX md5; + + MD5_Init(&md5); + for (s = auth->client; *s != 0; ++s) { + ch = tolower(*s); + MD5_Update(&md5, &ch, 1); + } + MD5_Update(&md5, ":", 1); + for (s = auth->server; *s != 0; ++s) { + ch = tolower(*s); + MD5_Update(&md5, &ch, 1); + } + MD5_Update(&md5, ":", 1); + MD5_Update(&md5, auth->password, strlen(auth->password)); + MD5_Final(md5sum, &md5); + memset(secret, 0, sizeof(secret)); + strcpy(secret, md5salt); + auth_readablesum(md5sum, secret + strlen(md5salt)); +} + +static void +auth_makeresponse(char *challenge, char *sharedsecret, char *response) +{ + MD5_CTX md5; + unsigned char md5sum[MD5_BYTES]; + + MD5_Init(&md5); + MD5_Update(&md5, sharedsecret, strlen(sharedsecret)); + MD5_Update(&md5, ":", 1); + MD5_Update(&md5, challenge, strlen(challenge)); + MD5_Final(md5sum, &md5); + auth_readablesum(md5sum, response); +} + +/* + * Generates a challenge string which is an MD5 sum + * of a fairly random string. The purpose is to decrease + * the possibility of generating the same challenge + * string (even by different clients) more then once + * for the same server. + */ +static void +auth_makechallenge(struct config *config, char *challenge) +{ + MD5_CTX md5; + unsigned char md5sum[MD5_BYTES]; + char buf[128]; + struct timeval tv; + struct sockaddr_in laddr; + pid_t pid, ppid; + int error, addrlen; + + gettimeofday(&tv, NULL); + pid = getpid(); + ppid = getppid(); + srand(tv.tv_usec ^ tv.tv_sec ^ pid); + addrlen = sizeof(laddr); + error = getsockname(config->socket, (struct sockaddr *)&laddr, &addrlen); + if (error < 0) { + memset(&laddr, 0, sizeof(laddr)); + } + gettimeofday(&tv, NULL); + MD5_Init(&md5); + snprintf(buf, sizeof(buf), "%s:%ld:%ld:%ld:%d:%d", + inet_ntoa(laddr.sin_addr), tv.tv_sec, tv.tv_usec, random(), pid, ppid); + MD5_Update(&md5, buf, strlen(buf)); + MD5_Final(md5sum, &md5); + auth_readablesum(md5sum, challenge); +} + +static int +auth_checkresponse(char *response, char *challenge, char *secret) +{ + char correctresponse[MD5_CHARS_MAX]; + + auth_makeresponse(challenge, secret, correctresponse); + return (strcmp(response, correctresponse) == 0); +} + +static void +auth_readablesum(unsigned char *md5sum, char *readable) +{ + unsigned int i; + char *s = readable; + + for (i = 0; i < MD5_BYTES; ++i, s+=2) { + sprintf(s, "%.2x", md5sum[i]); + } +} + diff --git a/usr.bin/csup/auth.h b/usr.bin/csup/auth.h new file mode 100644 index 0000000..61052e3 --- /dev/null +++ b/usr.bin/csup/auth.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2003-2007, Petar Zhivkov Petrov <pesho.petrov@gmail.com> + * 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. + * + * $FreeBSD$ + */ +#ifndef _AUTH_H_ +#define _AUTH_H_ + +#define AUTHFILE ".csup/auth" /* user home relative */ + +struct config; + +int auth_login(struct config *); + +#endif /* !_AUTH_H_ */ + diff --git a/usr.bin/csup/config.c b/usr.bin/csup/config.c new file mode 100644 index 0000000..46ae6cd --- /dev/null +++ b/usr.bin/csup/config.c @@ -0,0 +1,579 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "config.h" +#include "globtree.h" +#include "keyword.h" +#include "misc.h" +#include "parse.h" +#include "stream.h" +#include "token.h" + +static int config_parse_refusefiles(struct coll *); +static int config_parse_refusefile(struct coll *, char *); + +extern FILE *yyin; + +/* These are globals because I can't think of a better way with yacc. */ +static STAILQ_HEAD(, coll) colls; +static struct coll *cur_coll; +static struct coll *defaults; +static struct coll *ovcoll; +static int ovmask; +static const char *cfgfile; + +/* + * Extract all the configuration information from the config + * file and some command line parameters. + */ +struct config * +config_init(const char *file, struct coll *override, int overridemask) +{ + struct config *config; + struct coll *coll; + size_t slen; + char *prefix; + int error; + mode_t mask; + + config = xmalloc(sizeof(struct config)); + memset(config, 0, sizeof(struct config)); + STAILQ_INIT(&colls); + + defaults = coll_new(NULL); + /* Set the default umask. */ + mask = umask(0); + umask(mask); + defaults->co_umask = mask; + ovcoll = override; + ovmask = overridemask; + + /* Extract a list of collections from the configuration file. */ + cur_coll = coll_new(defaults); + yyin = fopen(file, "r"); + if (yyin == NULL) { + lprintf(-1, "Cannot open \"%s\": %s\n", file, strerror(errno)); + goto bad; + } + cfgfile = file; + error = yyparse(); + fclose(yyin); + if (error) + goto bad; + + memcpy(&config->colls, &colls, sizeof(colls)); + if (STAILQ_EMPTY(&config->colls)) { + lprintf(-1, "Empty supfile\n"); + goto bad; + } + + /* Fixup the list of collections. */ + STAILQ_FOREACH(coll, &config->colls, co_next) { + if (coll->co_base == NULL) + coll->co_base = xstrdup("/usr/local/etc/cvsup"); + if (coll->co_colldir == NULL) + coll->co_colldir = "sup"; + if (coll->co_prefix == NULL) { + coll->co_prefix = xstrdup(coll->co_base); + /* + * If prefix is not an absolute pathname, it is + * interpreted relative to base. + */ + } else if (coll->co_prefix[0] != '/') { + slen = strlen(coll->co_base); + if (slen > 0 && coll->co_base[slen - 1] != '/') + xasprintf(&prefix, "%s/%s", coll->co_base, + coll->co_prefix); + else + xasprintf(&prefix, "%s%s", coll->co_base, + coll->co_prefix); + free(coll->co_prefix); + coll->co_prefix = prefix; + } + coll->co_prefixlen = strlen(coll->co_prefix); + /* Determine whether to checksum RCS files or not. */ + if (coll->co_options & CO_EXACTRCS) + coll->co_options |= CO_CHECKRCS; + else + coll->co_options &= ~CO_CHECKRCS; + /* In recent versions, we always try to set the file modes. */ + coll->co_options |= CO_SETMODE; + coll->co_options |= CO_NORSYNC; + error = config_parse_refusefiles(coll); + if (error) + goto bad; + } + + coll_free(cur_coll); + coll_free(defaults); + config->host = STAILQ_FIRST(&config->colls)->co_host; + return (config); +bad: + coll_free(cur_coll); + coll_free(defaults); + config_free(config); + return (NULL); +} + +int +config_checkcolls(struct config *config) +{ + char linkname[4]; + struct stat sb; + struct coll *coll; + int error, numvalid, ret; + + numvalid = 0; + STAILQ_FOREACH(coll, &config->colls, co_next) { + error = stat(coll->co_prefix, &sb); + if (error || !S_ISDIR(sb.st_mode)) { + /* Skip this collection, and warn about it unless its + prefix is a symbolic link pointing to "SKIP". */ + coll->co_options |= CO_SKIP; + ret = readlink(coll->co_prefix, linkname, + sizeof(linkname)); + if (ret != 4 || memcmp(linkname, "SKIP", 4) != 0) { + lprintf(-1,"Nonexistent prefix \"%s\" for " + "%s/%s\n", coll->co_prefix, coll->co_name, + coll->co_release); + } + continue; + } + numvalid++; + } + return (numvalid); +} + +static int +config_parse_refusefiles(struct coll *coll) +{ + char *collstem, *suffix, *supdir, *path; + int error; + + if (coll->co_colldir[0] == '/') + supdir = xstrdup(coll->co_colldir); + else + xasprintf(&supdir, "%s/%s", coll->co_base, coll->co_colldir); + + /* First, the global refuse file that applies to all collections. */ + xasprintf(&path, "%s/refuse", supdir); + error = config_parse_refusefile(coll, path); + free(path); + if (error) { + free(supdir); + return (error); + } + + /* Next the per-collection refuse files that applies to all release/tag + combinations. */ + xasprintf(&collstem, "%s/%s/refuse", supdir, coll->co_name); + free(supdir); + error = config_parse_refusefile(coll, collstem); + if (error) { + free(collstem); + return (error); + } + + /* Finally, the per-release and per-tag refuse file. */ + suffix = coll_statussuffix(coll); + if (suffix != NULL) { + xasprintf(&path, "%s%s", collstem, suffix); + free(suffix); + error = config_parse_refusefile(coll, path); + free(path); + } + free(collstem); + return (error); +} + +/* + * Parses a "refuse" file, and records the relevant information in + * coll->co_refusals. If the file does not exist, it is silently + * ignored. + */ +static int +config_parse_refusefile(struct coll *coll, char *path) +{ + struct stream *rd; + char *cp, *line, *pat; + + rd = stream_open_file(path, O_RDONLY); + if (rd == NULL) + return (0); + while ((line = stream_getln(rd, NULL)) != NULL) { + pat = line; + for (;;) { + /* Trim leading whitespace. */ + pat += strspn(pat, " \t"); + if (pat[0] == '\0') + break; + cp = strpbrk(pat, " \t"); + if (cp != NULL) + *cp = '\0'; + pattlist_add(coll->co_refusals, pat); + if (cp == NULL) + break; + pat = cp + 1; + } + } + if (!stream_eof(rd)) { + stream_close(rd); + lprintf(-1, "Read failure from \"%s\": %s\n", path, + strerror(errno)); + return (-1); + } + stream_close(rd); + return (0); +} + +void +config_free(struct config *config) +{ + struct coll *coll; + + while (!STAILQ_EMPTY(&config->colls)) { + coll = STAILQ_FIRST(&config->colls); + STAILQ_REMOVE_HEAD(&config->colls, co_next); + coll_free(coll); + } + if (config->server != NULL) + stream_close(config->server); + if (config->laddr != NULL) + free(config->laddr); + free(config); +} + +/* Create a new collection, inheriting options from the default collection. */ +struct coll * +coll_new(struct coll *def) +{ + struct coll *new; + + new = xmalloc(sizeof(struct coll)); + memset(new, 0, sizeof(struct coll)); + if (def != NULL) { + new->co_options = def->co_options; + new->co_umask = def->co_umask; + if (def->co_host != NULL) + new->co_host = xstrdup(def->co_host); + if (def->co_base != NULL) + new->co_base = xstrdup(def->co_base); + if (def->co_date != NULL) + new->co_date = xstrdup(def->co_date); + if (def->co_prefix != NULL) + new->co_prefix = xstrdup(def->co_prefix); + if (def->co_release != NULL) + new->co_release = xstrdup(def->co_release); + if (def->co_tag != NULL) + new->co_tag = xstrdup(def->co_tag); + if (def->co_listsuffix != NULL) + new->co_listsuffix = xstrdup(def->co_listsuffix); + } else { + new->co_tag = xstrdup("."); + new->co_date = xstrdup("."); + } + new->co_keyword = keyword_new(); + new->co_accepts = pattlist_new(); + new->co_refusals = pattlist_new(); + new->co_attrignore = FA_DEV | FA_INODE; + return (new); +} + +void +coll_override(struct coll *coll, struct coll *from, int mask) +{ + size_t i; + int newoptions, oldoptions; + + newoptions = from->co_options & mask; + oldoptions = coll->co_options & (CO_MASK & ~mask); + + if (from->co_release != NULL) { + if (coll->co_release != NULL) + free(coll->co_release); + coll->co_release = xstrdup(from->co_release); + } + if (from->co_host != NULL) { + if (coll->co_host != NULL) + free(coll->co_host); + coll->co_host = xstrdup(from->co_host); + } + if (from->co_base != NULL) { + if (coll->co_base != NULL) + free(coll->co_base); + coll->co_base = xstrdup(from->co_base); + } + if (from->co_colldir != NULL) + coll->co_colldir = from->co_colldir; + if (from->co_prefix != NULL) { + if (coll->co_prefix != NULL) + free(coll->co_prefix); + coll->co_prefix = xstrdup(from->co_prefix); + } + if (newoptions & CO_CHECKOUTMODE) { + if (from->co_tag != NULL) { + if (coll->co_tag != NULL) + free(coll->co_tag); + coll->co_tag = xstrdup(from->co_tag); + } + if (from->co_date != NULL) { + if (coll->co_date != NULL) + free(coll->co_date); + coll->co_date = xstrdup(from->co_date); + } + } + if (from->co_listsuffix != NULL) { + if (coll->co_listsuffix != NULL) + free(coll->co_listsuffix); + coll->co_listsuffix = xstrdup(from->co_listsuffix); + } + for (i = 0; i < pattlist_size(from->co_accepts); i++) { + pattlist_add(coll->co_accepts, + pattlist_get(from->co_accepts, i)); + } + for (i = 0; i < pattlist_size(from->co_refusals); i++) { + pattlist_add(coll->co_refusals, + pattlist_get(from->co_refusals, i)); + } + coll->co_options = oldoptions | newoptions; +} + +char * +coll_statussuffix(struct coll *coll) +{ + const char *tag; + char *suffix; + + if (coll->co_listsuffix != NULL) { + xasprintf(&suffix, ".%s", coll->co_listsuffix); + } else if (coll->co_options & CO_USERELSUFFIX) { + if (coll->co_tag == NULL) + tag = "."; + else + tag = coll->co_tag; + if (coll->co_release != NULL) { + if (coll->co_options & CO_CHECKOUTMODE) { + xasprintf(&suffix, ".%s:%s", + coll->co_release, tag); + } else { + xasprintf(&suffix, ".%s", coll->co_release); + } + } else if (coll->co_options & CO_CHECKOUTMODE) { + xasprintf(&suffix, ":%s", tag); + } + } else + suffix = NULL; + return (suffix); +} + +char * +coll_statuspath(struct coll *coll) +{ + char *path, *suffix; + + suffix = coll_statussuffix(coll); + if (suffix != NULL) { + if (coll->co_colldir[0] == '/') + xasprintf(&path, "%s/%s/checkouts%s", coll->co_colldir, + coll->co_name, suffix); + else + xasprintf(&path, "%s/%s/%s/checkouts%s", coll->co_base, + coll->co_colldir, coll->co_name, suffix); + } else { + if (coll->co_colldir[0] == '/') + xasprintf(&path, "%s/%s/checkouts", coll->co_colldir, + coll->co_name); + else + xasprintf(&path, "%s/%s/%s/checkouts", coll->co_base, + coll->co_colldir, coll->co_name); + } + free(suffix); + return (path); +} + +void +coll_add(char *name) +{ + struct coll *coll; + + cur_coll->co_name = name; + coll_override(cur_coll, ovcoll, ovmask); + if (cur_coll->co_release == NULL) { + lprintf(-1, "Release not specified for collection " + "\"%s\"\n", cur_coll->co_name); + exit(1); + } + if (cur_coll->co_host == NULL) { + lprintf(-1, "Host not specified for collection " + "\"%s\"\n", cur_coll->co_name); + exit(1); + } + if (!STAILQ_EMPTY(&colls)) { + coll = STAILQ_LAST(&colls, coll, co_next); + if (strcmp(coll->co_host, cur_coll->co_host) != 0) { + lprintf(-1, "All \"host\" fields in the supfile " + "must be the same\n"); + exit(1); + } + } + STAILQ_INSERT_TAIL(&colls, cur_coll, co_next); + cur_coll = coll_new(defaults); +} + +void +coll_free(struct coll *coll) +{ + + if (coll == NULL) + return; + if (coll->co_host != NULL) + free(coll->co_host); + if (coll->co_base != NULL) + free(coll->co_base); + if (coll->co_date != NULL) + free(coll->co_date); + if (coll->co_prefix != NULL) + free(coll->co_prefix); + if (coll->co_release != NULL) + free(coll->co_release); + if (coll->co_tag != NULL) + free(coll->co_tag); + if (coll->co_cvsroot != NULL) + free(coll->co_cvsroot); + if (coll->co_name != NULL) + free(coll->co_name); + if (coll->co_listsuffix != NULL) + free(coll->co_listsuffix); + keyword_free(coll->co_keyword); + if (coll->co_dirfilter != NULL) + globtree_free(coll->co_dirfilter); + if (coll->co_dirfilter != NULL) + globtree_free(coll->co_filefilter); + if (coll->co_norsync != NULL) + globtree_free(coll->co_norsync); + if (coll->co_accepts != NULL) + pattlist_free(coll->co_accepts); + if (coll->co_refusals != NULL) + pattlist_free(coll->co_refusals); + free(coll); +} + +void +coll_setopt(int opt, char *value) +{ + struct coll *coll; + int error, mask; + + coll = cur_coll; + switch (opt) { + case PT_HOST: + if (coll->co_host != NULL) + free(coll->co_host); + coll->co_host = value; + break; + case PT_BASE: + if (coll->co_base != NULL) + free(coll->co_base); + coll->co_base = value; + break; + case PT_DATE: + if (coll->co_date != NULL) + free(coll->co_date); + coll->co_date = value; + coll->co_options |= CO_CHECKOUTMODE; + break; + case PT_PREFIX: + if (coll->co_prefix != NULL) + free(coll->co_prefix); + coll->co_prefix = value; + break; + case PT_RELEASE: + if (coll->co_release != NULL) + free(coll->co_release); + coll->co_release = value; + break; + case PT_TAG: + if (coll->co_tag != NULL) + free(coll->co_tag); + coll->co_tag = value; + coll->co_options |= CO_CHECKOUTMODE; + break; + case PT_LIST: + if (strchr(value, '/') != NULL) { + lprintf(-1, "Parse error in \"%s\": \"list\" suffix " + "must not contain slashes\n", cfgfile); + exit(1); + } + if (coll->co_listsuffix != NULL) + free(coll->co_listsuffix); + coll->co_listsuffix = value; + break; + case PT_UMASK: + error = asciitoint(value, &mask, 8); + free(value); + if (error) { + lprintf(-1, "Parse error in \"%s\": Invalid " + "umask value\n", cfgfile); + exit(1); + } + coll->co_umask = mask; + break; + case PT_USE_REL_SUFFIX: + coll->co_options |= CO_USERELSUFFIX; + break; + case PT_DELETE: + coll->co_options |= CO_DELETE | CO_EXACTRCS; + break; + case PT_COMPRESS: + coll->co_options |= CO_COMPRESS; + break; + case PT_NORSYNC: + coll->co_options |= CO_NORSYNC; + break; + } +} + +/* Set "coll" as being the default collection. */ +void +coll_setdef(void) +{ + + coll_free(defaults); + defaults = cur_coll; + cur_coll = coll_new(defaults); +} diff --git a/usr.bin/csup/config.h b/usr.bin/csup/config.h new file mode 100644 index 0000000..859013c --- /dev/null +++ b/usr.bin/csup/config.h @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _CONFIG_H_ +#define _CONFIG_H_ + +#include <sys/types.h> +#include <sys/socket.h> + +#include <time.h> + +#include "fattr.h" +#include "queue.h" +#include "misc.h" + +/* + * Collection options. + */ +#define CO_BACKUP 0x00000001 +#define CO_DELETE 0x00000002 +#define CO_KEEP 0x00000004 +#define CO_OLD 0x00000008 +#define CO_UNLINKBUSY 0x00000010 +#define CO_NOUPDATE 0x00000020 +#define CO_COMPRESS 0x00000040 +#define CO_USERELSUFFIX 0x00000080 +#define CO_EXACTRCS 0x00000100 +#define CO_CHECKRCS 0x00000200 +#define CO_SKIP 0x00000400 +#define CO_CHECKOUTMODE 0x00000800 +#define CO_NORSYNC 0x00001000 +#define CO_KEEPBADFILES 0x00002000 +#define CO_EXECUTE 0x00004000 +#define CO_SETOWNER 0x00008000 +#define CO_SETMODE 0x00010000 +#define CO_SETFLAGS 0x00020000 +#define CO_NORCS 0x00040000 +#define CO_STRICTCHECKRCS 0x00080000 +#define CO_TRUSTSTATUSFILE 0x00100000 +#define CO_DODELETESONLY 0x00200000 +#define CO_DETAILALLRCSFILES 0x00400000 + +#define CO_MASK 0x007fffff + +/* Options that the server is allowed to set. */ +#define CO_SERVMAYSET (CO_SKIP | CO_NORSYNC | CO_NORCS) +/* Options that the server is allowed to clear. */ +#define CO_SERVMAYCLEAR CO_CHECKRCS + +struct coll { + char *co_name; + char *co_host; + char *co_base; + char *co_date; + char *co_prefix; + size_t co_prefixlen; + char *co_release; + char *co_tag; + char *co_cvsroot; + int co_attrignore; + struct pattlist *co_accepts; + struct pattlist *co_refusals; + struct globtree *co_dirfilter; + struct globtree *co_filefilter; + struct globtree *co_norsync; + const char *co_colldir; + char *co_listsuffix; + time_t co_scantime; /* Set by the detailer thread. */ + int co_options; + mode_t co_umask; + struct keyword *co_keyword; + STAILQ_ENTRY(coll) co_next; +}; + +struct config { + STAILQ_HEAD(, coll) colls; + struct fixups *fixups; + char *host; + struct sockaddr *laddr; + socklen_t laddrlen; + int deletelim; + int socket; + struct chan *chan0; + struct chan *chan1; + struct stream *server; + fattr_support_t fasupport; + int reqauth; +}; + +struct config *config_init(const char *, struct coll *, int); +int config_checkcolls(struct config *); +void config_free(struct config *); + +struct coll *coll_new(struct coll *); +void coll_override(struct coll *, struct coll *, int); +char *coll_statuspath(struct coll *); +char *coll_statussuffix(struct coll *); +void coll_add(char *); +void coll_free(struct coll *); +void coll_setdef(void); +void coll_setopt(int, char *); + +#endif /* !_CONFIG_H_ */ diff --git a/usr.bin/csup/cpasswd.1 b/usr.bin/csup/cpasswd.1 new file mode 100644 index 0000000..946783f --- /dev/null +++ b/usr.bin/csup/cpasswd.1 @@ -0,0 +1,120 @@ +.\" Copyright 1999-2003 John D. Polstra. +.\" 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 acknowledgment: +.\" This product includes software developed by John D. Polstra. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission. +.\" +.\" 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. +.\" +.\" $Id: cvpasswd.1,v 1.4 2003/03/04 18:24:42 jdp Exp $ +.\" $FreeBSD $ +.\" +.Dd June 27, 2007 +.Os FreeBSD +.Dt CPASSWD 1 +.Sh NAME +.Nm cpasswd +.Nd scramble passwords for csup authentication +.Sh SYNOPSIS +.Nm +.Ar clientName +.Ar serverName +.Sh DESCRIPTION +The +.Nm +utility creates scrambled passwords for the +.Nm CVSup +server's authentication database. It is invoked with a client name +and a server name. +.Ar ClientName +is the name the client uses to gain access to the +server. By convention, e-mail addresses are used for all client +names, e.g., +.Ql BillyJoe@FreeBSD.ORG . +Client names are case-insensitive. +.Pp +.Ar ServerName +is the name of the +.Nm CVSup +server which the client wishes to access. By convention, +it is the canonical fully-qualified domain name of the server, e.g., +.Ql CVSup.FreeBSD.ORG . +This must agree with the server's own idea of its name. The name is +case-insensitive. +.Pp +To set up authentication for a given server, one must perform the +following steps: +.Bl -enum +.It +Obtain the official +.Ar serverName +from the administrator of the server or from some other source. +.It +Choose an appropriate +.Ar clientName . +It should be in the form of a valid e-mail address, to make it easy +for the server administrator to contact the user if necessary. +.It +Choose an arbitrary secret +.Ar password . +.It +Run +.Nm cpasswd , +and type in the +.Ar password +when prompted for it. The utility will print out a line to send +to the server administrator, and instruct you how to modify your +.Li $ Ns Ev HOME Ns Pa /.csup/auth +file. You should use a secure channel to send the line to the +server administrator. +.El +.Pp +Since +.Li $ Ns Ev HOME Ns Pa /.csup/auth +contains passwords, you should ensure that it is not readable by +anyone except yourself. +.Sh FILES +.Bl -tag -width $HOME/.csup/authxx -compact +.It Li $ Ns Ev HOME Ns Pa /.csup/auth +Authentication password file. +.El +.Sh SEE ALSO +.Xr csup 1 , +.Xr cvsup 1 , +.Xr cvsupd 8 . +.Pp +.Bd -literal +http://www.cvsup.org/ +.Ed +.Sh AUTHORS +.An -nosplit +.An Petar Zhivkov Petrov Aq pesho.petrov@gmail.com +is the author of +.Nm , +the rewrite of +.Nm cvpasswd . +.An John Polstra Aq jdp@polstra.com +is the author of +.Nm CVSup . +.Sh LEGALITIES +CVSup is a registered trademark of John D. Polstra. diff --git a/usr.bin/csup/cpasswd.sh b/usr.bin/csup/cpasswd.sh new file mode 100755 index 0000000..71e17c5 --- /dev/null +++ b/usr.bin/csup/cpasswd.sh @@ -0,0 +1,135 @@ +#! /bin/sh +# +# Copyright 2007. Petar Zhivkov Petrov +# pesho.petrov@gmail.com +# +# $FreeBSD$ + +usage() { + echo "Usage: $0 clientName serverName" + echo " $0 -v" +} + +countChars() { + _count="`echo "$1" | sed -e "s/[^$2]//g" | tr -d "\n" | wc -c`" + return 0 +} + +readPassword() { + while [ true ]; do + stty -echo + read -p "$1" _password + stty echo + echo "" + countChars "$_password" ":" + if [ $_count != 0 ]; then + echo "Sorry, password must not contain \":\" characters" + echo "" + else + break + fi + done + return 0 +} + +makeSecret() { + local clientLower="`echo "$1" | tr "[:upper:]" "[:lower:]"`" + local serverLower="`echo "$2" | tr "[:upper:]" "[:lower:]"`" + local secret="`md5 -qs "$clientLower:$serverLower:$3"`" + _secret="\$md5\$$secret" +} + +if [ $# -eq 1 -a "X$1" = "X-v" ]; then + echo "Csup authentication key generator" + usage + exit +elif [ $# -ne 2 ]; then + usage + exit +fi + +clientName=$1 +serverName=$2 + +# +# Client name must contain exactly one '@' and at least one '.'. +# It must not contain a ':'. +# + +countChars "$clientName" "@" +aCount=$_count + +countChars "$clientName" "." +dotCount=$_count +if [ $aCount -ne 1 -o $dotCount -eq 0 ]; then + echo "Client name must have the form of an e-mail address," + echo "e.g., \"user@domain.com\"" + exit +fi + +countChars "$clientName" ":" +colonCount=$_count +if [ $colonCount -gt 0 ]; then + echo "Client name must not contain \":\" characters" + exit +fi + +# +# Server name must not contain '@' and must have at least one '.'. +# It also must not contain a ':'. +# + +countChars "$serverName" "@" +aCount=$_count + +countChars "$serverName" "." +dotCount=$_count +if [ $aCount != 0 -o $dotCount = 0 ]; then + echo "Server name must be a fully-qualified domain name." + echo "e.g., \"host.domain.com\"" + exit +fi + +countChars "$serverName" ":" +colonCount=$_count +if [ $colonCount -gt 0 ]; then + echo "Server name must not contain \":\" characters" + exit +fi + +# +# Ask for password and generate secret. +# + +while [ true ]; do + readPassword "Enter password: " + makeSecret "$clientName" "$serverName" "$_password" + secret=$_secret + + readPassword "Enter same password again: " + makeSecret "$clientName" "$serverName" "$_password" + secret2=$_secret + + if [ "X$secret" = "X$secret2" ]; then + break + else + echo "Passwords did not match. Try again." + echo "" + fi +done + +echo "" +echo "Send this line to the server administrator at $serverName:" +echo "-------------------------------------------------------------------------------" +echo "$clientName:$secret::" +echo "-------------------------------------------------------------------------------" +echo "Be sure to send it using a secure channel!" +echo "" +echo "Add this line to your file \"$HOME/.csup/auth\", replacing \"XXX\"" +echo "with the password you typed in:" +echo "-------------------------------------------------------------------------------" +echo "$serverName:$clientName:XXX:" +echo "-------------------------------------------------------------------------------" +echo "Make sure the file is readable and writable only by you!" +echo "" + diff --git a/usr.bin/csup/csup.1 b/usr.bin/csup/csup.1 new file mode 100644 index 0000000..2690863 --- /dev/null +++ b/usr.bin/csup/csup.1 @@ -0,0 +1,1000 @@ +.\" Copyright 1996-2003 John D. Polstra. +.\" 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. +.\" +.\" $Id: cvsup.1,v 1.70 2003/03/04 18:23:46 jdp Exp $ +.\" $FreeBSD$ +.\" +.Dd February 1, 2006 +.Os FreeBSD +.Dt CSUP 1 +.Sh NAME +.Nm csup +.Nd network distribution package for CVS repositories +.Sh SYNOPSIS +.Nm +.Op Fl 146aksvzZ +.Op Fl A Ar addr +.Op Fl b Ar base +.Op Fl c Ar collDir +.Op Fl d Ar delLimit +.Op Fl h Ar host +.Op Fl i Ar pattern +.Op Fl l Ar lockfile +.Op Fl L Ar verbosity +.Op Fl p Ar port +.Op Fl r Ar maxRetries +.Ar supfile +.Sh DESCRIPTION +.Nm +is a software package for updating collections of files across a network. +It is a rewrite of the +.Nm CVSup +software in C. +This manual page describes the usage of the +.Nm +client program. +.Pp +Unlike more traditional network distribution packages, such as +.Nm rdist +and +.Nm sup , +.Nm +has specific optimizations for distributing CVS repositories. +.Nm +takes advantage of the properties of CVS repositories and the files they +contain (in particular, RCS files), enabling it to perform updates much +faster than traditional systems. +.Pp +.Nm +is a general-purpose network file updating package. +It is extremely fast, +even for collections of files which have nothing to do with CVS or +RCS. +.Sh OPTIONS +The client program +.Nm +requires at least a single argument, +.Ar supfile . +It names a file describing one or more collections of files to be +transferred and/or updated from the server. +The +.Ar supfile +has a format similar to the corresponding file used by +.Nm sup . +In most cases, +.Nm +can use existing +.Nm sup Ar supfiles . +.Pp +The following options are supported by +.Nm : +.Bl -tag -width Fl +.It Fl 1 +Disables automatic retries when transient failures occur. +Without this option, a transient failure such as a dropped network +connection causes +.Nm +to retry repeatedly, using randomized exponential backoff to space the +retries. +This option is equivalent to +.Fl r Cm 0 . +.It Fl 4 +Forces +.Nm +to use IPv4 addresses only. +.It Fl 6 +Forces +.Nm +to use IPv6 addresses only. +.It Fl a +Requires the server to authenticate itself (prove its identity) to +the client. If authentication of the server fails, the update is +canceled. See +.Sx AUTHENTICATION , +below. +.It Fl A Ar addr +Specifies a local address to bind to when connecting to the server. +The local address might be a hostname or a numeric host address string +consisting of a dotted decimal IPv4 address or an IPv6 address. +This may be useful on hosts which have multiple IP addresses. +.It Fl b Ar base +Specifies the base directory under which +.Nm +will maintain its bookkeeping files, overriding any +.Cm base +specifications in the +.Ar supfile . +.It Fl c Ar collDir +Specifies the subdirectory of +.Ar base +where the information about the collections is maintained. +The default is +.Pa sup . +.It Fl d Ar delLimit +Specifies the maximum number of files that may be deleted in a +single update run. +Any attempt to exceed the limit results in a fatal error. +This can provide some protection against temporary configuration +mistakes on the server. +The default limit is infinity. +.It Fl h Ar host +Specifies the server host to contact, overriding any +.Cm host +specifications in the +.Ar supfile . +.It Fl i Ar pattern +Causes +.Nm +to include only files and directories matching +.Ar pattern +in the update. If a directory matches the pattern, then the entire +subtree rooted at the directory is included. If this option is +specified multiple times, the patterns are combined using the +.Ql or +operation. If no +.Fl i +options are given, the default is to update all files in each +collection. +.Pp +The +.Ar pattern +is a standard file name pattern. +It is interpreted relative to the collection's prefix directory. +Slash characters are matched only by explicit slashes in the pattern. +Leading periods in file name are not treated specially. +.It Fl k +Causes +.Nm +to keep the temporary copies of any incorrectly edited files, in the +event of checksum mismatches. +This option is for debugging, to help determine why the files were +edited incorrectly. +Regardless of whether this option is specified, the permanent versions +of faulty files are replaced with correct versions obtained by +transferring the files in their entirety. +Such transfers are called fixups. +.It Fl l Ar lockfile +Creates and locks the +.Ar lockfile +while the update is in progress. +If +.Ar lockfile +is already locked, +.Nm +fails without performing automatic retries. +This option is useful when +.Nm +is executed periodically from +.Nm cron . +It prevents a job from interfering with an earlier job that is perhaps +taking extra long because of network problems. +.Pp +The process-ID is written to the lock file in text form when the lock +is successfully acquired. +Upon termination of the update, the lock file is removed. +.It Fl L Ar verbosity +Sets the verbosity level for output. +A level of 0 causes +.Nm +to be completely silent unless errors occur. +A level of 1 (the default) causes each updated file to be listed. +A level of 2 provides more detailed information about the updates +performed on each file. +All messages are directed to the standard output. +.It Fl p Ar port +Sets the TCP port to which +.Nm +attempts to connect on the server host. +The default port is 5999. +.It Fl r Ar maxRetries +Limits the number of automatic retries that will be attempted when +transient errors such as lost network connections are encountered. +By default, +.Nm +will retry indefinitely until an update is successfully completed. +The retries are spaced using randomized exponential backoff. +Note that +.Fl r Cm 0 +is equivalent to the +.Fl 1 +option. +.It Fl s +Suppresses the check of each client file's status against what is +recorded in the list file. Instead, the list file is assumed to be +accurate. This option greatly reduces the amount of disk activity and +results in faster updates with less load on the client host. However +it should only be used if client's files are never modified locally in +any way. Mirror sites may find this option beneficial to reduce the +disk load on their systems. For safety, even mirror sites should run +.Nm +occasionally (perhaps once a day) without the +.Fl s +option. +.Pp +Without the +.Fl s +option, +.Nm +performs a +.Xr stat 2 +call on each file and verifies that its attributes match those +recorded in the list file. This ensures that any file changes made +outside of +.Nm +are detected and corrected. +.Pp +If the +.Fl s +option is used when one or more files have been modified locally, the +results are undefined. Local file damage may remain uncorrected, +updates may be missed, or +.Nm +may abort prematurely. +.It Fl v +Prints the version number and exits, without contacting the server. +.It Fl z +Enables compression for all collections, as if the +.Cm compress +keyword were added to every collection in the +.Ar supfile . +.It Fl Z +Disables compression for all collections, as if the +.Cm compress +keyword were removed from every collection in the +.Ar supfile . +.El +.Pp +The +.Ar supfile +is a text file which specifies the file collections to be updated. +Comments begin with +.Ql # +and extend to the end of the line. Lines that are empty except for +comments and white space are ignored. Each remaining line begins +with the name of a server-defined collection of files. Following the +collection name on the line are zero or more keywords or keyword=value +pairs. +.Pp +Default settings may be specified in lines whose collection name is +.Cm *default . +Such defaults will apply to subsequent lines in the +.Ar supfile . +Multiple +.Cm *default +lines may be present. +New values augment or override any defaults specified earlier in the +.Ar supfile . +Values specified explicitly for a collection override any default +values. +.Pp +The most commonly used keywords are: +.Bl -tag -width Fl +.It Cm release= Ns Ar releaseName +This specifies the release of the files within a collection. +Like collection names, release names are defined by the server +configuration files. Usually there is only one release in each +collection, but there may be any number. Collections which come from +a CVS repository often use +.Cm release=cvs +by convention. Non-CVS collections conventionally use +.Cm release=current . +.It Cm base= Ns Ar base +This specifies a directory under which +.Nm +will maintain its bookkeeping files, describing the state of each +collection on the client machine. +The +.Ar base +directory must already exist; +.Nm +will not create it. +The default +.Ar base +directory is +.Pa /usr/local/etc/csup . +.It Cm prefix= Ns Ar prefix +This is the directory under which updated files will be placed. +By default, it is the same as +.Ar base . +If it is not an absolute pathname, it is interpreted relative to +.Ar base . +The +.Ar prefix +directory must already exist; +.Nm +will not create it. +.Pp +As a special case, if +.Ar prefix +is a symbolic link pointing to a nonexistent file named +.Ql SKIP , +then +.Nm +will skip the collection. +The parameters associated with the collection are still checked for +validity, but none of its files will be updated. +This feature allows a site to use a standard +.Ar supfile +on several machines, yet control which collections get updated on a +per-machine basis. +.It Cm host= Ns Ar hostname +This specifies the server machine from which all files will be taken. +.Nm +requires that all collections in a single run come from the same host. +If you wish to update collections from several different hosts, you must +run +.Nm +several times. +.It Cm delete +The presence of this keyword gives +.Nm +permission to delete files. +If it is missing, no files will be deleted. +.Pp +The presence of the +.Cm delete +keyword puts +.Nm +into so-called +.Em exact +mode. In exact mode, +.Nm +does its best to make the client's files correspond to those on the server. +This includes deleting individual deltas and symbolic tags from RCS +files, as well as deleting entire files. +In exact mode, +.Nm +verifies every edited file with a checksum, to ensure that the edits +have produced a file identical to the master copy on the server. +If the checksum test fails for a file, then +.Nm +falls back upon transferring the entire file. +.Pp +In general, +.Nm +deletes only files which are known to the server. +Extra files present in the client's tree are left alone, even in exact +mode. +More precisely, +.Nm +is willing to delete two classes of files: +.Bl -bullet -compact +.It +Files that were previously created or updated by +.Nm +itself. +.It +Checked-out versions of files which are marked as dead on the server. +.El +.It Cm use-rel-suffix +Causes +.Nm +to append a suffix constructed from the release and tag to the name of +each list file that it maintains. +See +.Sx THE LIST FILE +for details. +.It Cm compress +This enables compression of all data sent across the network. +Compression is quite effective, normally eliminating 65% to 75% of the +bytes that would otherwise need to be transferred. +However, it is costly in terms of CPU time on both the client and the +server. +On local area networks, compression is generally counter-productive; it +actually slows down file updates. +On links with speeds of 56K bits/second or less, compression is almost +always beneficial. +For network links with speeds between these two extremes, let +experimentation be your guide. +.Pp +The +.Fl z +command line option enables the +.Cm compress +keyword for all collections, regardless of what is specified in the supfile. +Likewise, the +.Fl Z +command line option disables the +.Cm compress +option for all collections. +.Nm +uses a looser checksum for RCS files, which ignores harmless +differences in white space. Different versions of CVS and RCS produce +a variety of differences in white space for the same RCS files. Thus +the strict checksum can report spurious mismatches for files which are +logically identical. This can lead to numerous unneeded +.Dq fixups , +and thus to slow updates. +.It Cm umask= Ns Ar n +Causes +.Nm +to use a umask value of +.Ar n +(an octal number) when updating the files in the collection. +This option is ignored if +.Cm preserve +is specified. +.El +.Pp +Some additional, more specialized keywords are described below. +Unrecognized keywords are silently ignored for backward compatibility +with +.Nm sup . +.Sh CVS MODE +.Nm CVSup +supports two primary modes of operation. +They are called +.Em CVS +mode and +.Em checkout +mode. +.Pp +In CVS mode, the client receives copies of the actual RCS files making +up the master CVS repository. CVS mode is the default mode of operation. +It is appropriate when the user wishes to maintain a full copy of the +CVS repository on the client machine. +.Pp +CVS mode is also appropriate for file collections which are not +based upon a CVS repository. The files are simply transferred +verbatim, without interpretation. +.Sh CHECKOUT MODE +In checkout mode, the client receives specific revisions of files, +checked out directly from the server's CVS repository. +Checkout mode allows the client to receive any version from the +repository, without requiring any extra disk space on the server for +storing multiple versions in checked-out form. +Checkout mode provides much flexibility beyond that basic functionality, +however. +The client can specify any CVS symbolic tag, or any date, or both, and +.Nm +will provide the corresponding checked-out versions of the files in the +repository. +.Pp +Checkout mode is selected on a per-collection basis, by the presence of +one or both of the following keywords in the +.Ar supfile : +.Bl -tag -width Fl +.It Cm tag= Ns Ar tagname +This specifies a symbolic tag that should be used to select the +revisions that are checked out from the CVS repository. +The tag may refer to either a branch or a specific revision. +It must be symbolic; numeric revision numbers are not supported. +.Pp +For the FreeBSD source repository, the most commonly used tags will be: +.Bl -tag -width RELENG_6 +.It Li RELENG_6 +The +.Ql stable +branch. +.It Li \&. +The main branch (the +.Ql current +release). +This is the default, if only the +.Cm date +keyword is given. +.El +.Sm off +.It Xo Cm date= +.Op Ar cc +.Ar yy.mm.dd.hh.mm.ss +.Xc +.Sm on +This specifies a date that should be used to select the revisions that +are checked out from the CVS repository. +The client will receive the revisions that were in effect at the +specified date and time. +.Pp +At present, the date format is inflexible. All 17 or 19 characters must +be specified, exactly as shown. +For the years 2000 and beyond, specify the century +.Ar cc . +For earlier years, specify only the last two digits +.Ar yy . +Dates and times are considered to +be GMT. +The default date is +.Ql \&. , +which means +.Dq as late as possible . +.El +.Pp +To enable checkout mode, you must specify at least one of these keywords. +If both are missing, +.Nm +defaults to CVS mode. +.Pp +If both a branch tag and a date are specified, then the revisions on the +given branch, as of the given date, will be checked out. It is +permitted, but not particularly useful, to specify a date with a +specific release tag. +.Pp +In checkout mode, the tag and/or date may be changed between updates. +For example, suppose that a collection has been transferred using the +specification +.Ql tag=. . +The user could later change the specification to +.Ql tag=RELENG_3 . +This would cause +.Nm +to edit the checked-out files in such a way as to transform them from the +.Ql current +versions to the +.Ql stable +versions. +In general, +.Nm +is willing to transform any tag/date combination into any other tag/date +combination, by applying the intervening RCS deltas to the existing files. +.Pp +When transforming a collection of checked-out files from one tag to +another, it is important to specify the +.Cm list +keyword in the +.Ar supfile , +to ensure that the same list file is used both before and after the +transformation. +The list file is described in +.Sx THE LIST FILE , +below. +.Sh THE LIST FILE +For efficiency, +.Nm +maintains a bookkeeping file for each collection, called the list file. +The list file contains information about which files and revisions the client +currently possesses. +It also contains information used for verifying that the list file +is consistent with the actual files in the client's tree. +.Pp +The list file is not strictly necessary. If it is deleted, or becomes +inconsistent with the actual client files, +.Nm +falls back upon a less efficient method of identifying the client's +files and performing its updates. +Depending on +.Nm csup Ns No 's +mode of operation, the fallback method employs time stamps, checksums, or +analysis of RCS files. +.Pp +Because the list file is not essential, +.Nm +is able to +.Dq adopt +an existing file tree acquired by FTP or from a CD-ROM. +.Nm +identifies the client's versions of the files, updates them as +necessary, and creates a list file for future use. +Adopting a foreign file tree is not as fast as performing a normal +update. +It also produces a heavier load on the server. +.Pp +The list file is stored in a collection-specific directory; see +.Sx FILES +for details. +Its name always begins with +.Ql checkouts . +If the keyword +.Cm use-rel-suffix +is specified in the +.Ar supfile , +a suffix, formed from the release and tag, is appended to the name. +The default suffix can be overridden by specifying an explicit suffix in +the +.Ar supfile : +.Bl -tag -width Fl +.It Cm list= Ns Ar suffix +This specifies a suffix for the name of the list file. A leading dot is +provided automatically. +For example, +.Ql list=stable +would produce a list file named +.Pa checkouts.stable , +regardless of the release, tag, or +.Cm use-rel-suffix +keyword. +.El +.Sh REFUSE FILES +The user can specify sets of files that he does not wish to receive. +The files are specified as file name patterns in so-called +.Em refuse +files. +The patterns are separated by whitespace, and multiple patterns are +permitted on each line. +Files and directories matching the patterns are neither updated nor +deleted; they are simply ignored. +.Pp +There is currently no provision for comments in refuse files. +.Pp +The patterns are similar to those of +.Xr sh 1 , +except that there is no special treatment for slashes or for +filenames that begin with a period. +For example, the pattern +.Ql *.c +will match any file name ending with +.Ql \&.c +including those in subdirectories, such as +.Ql foo/bar/lam.c . +All patterns are interpreted relative to the collection's prefix +directory. +.Pp +If the files are coming from a CVS repository, as is usually +the case, then they will be RCS files. These have a +.Ql \&,v +suffix which must be taken into account in the patterns. For +example, the FreeBSD documentation files are in a sub-directory of +.Ar base +called +.Ql doc . +If +.Ql Makefile +from that directory is not required then the line +.Pp +.Bl -item -compact -offset indent +.It +.Pa doc/Makefile +.El +.Pp +will not work because the file on the server is called +.Ql Makefile,v. +A better solution would be +.Pp +.Bl -item -compact -offset indent +.It +.Pa doc/Makefile* +.El +.Pp +which will match whether +.Ql Makefile +is an RCS file or not. +.Pp +As another example, to receive the FreeBSD documentation files without +the Japanese, Russian, and Chinese translations, create a refuse file +containing the following lines: +.Pp +.Bl -item -compact -offset indent +.It +.Pa doc/ja* +.It +.Pa doc/ru* +.It +.Pa doc/zh* +.El +.Pp +As many as three refuse files are examined for each +.Ar supfile +line. +There can be a global refuse file named +.Sm off +.Ar base / Ar collDir Pa /refuse +.Sm on +which applies to all collections and releases. +There can be a per-collection refuse file named +.Sm off +.Xo Ar base / Ar collDir / Ar collection +.Pa /refuse +.Xc +.Sm on +which applies to a specific collection. +Finally, there can be a per-release and tag refuse file which applies only +to a given release/tag combination within a collection. +The name of the latter is formed by suffixing the name of the +per-collection refuse file in the same manner as described above for the +list file. +None of the refuse files are required to exist. +.Pp +.Nm +has a built-in default value of +.Ar /usr/local/etc/cvsup +for +.Ar base +and +.Ar sup +for +.Ar collDir +but it is possible to override both of these. The value of +.Ar base +can be changed using the +.Fl b +option or a +.Ar base=pathname +entry in the +.Ar supfile . +(If both are used the +.Fl b +option will override the +.Ar supfile +entry.) The value of +.Ar collDir +can only be changed with the +.Fl c +option; there is no +.Ar supfile +command to change it. +.Pp +As an example, suppose that the +.Ar base +and +.Ar collDir +both have their default values, and that the collection and release are +.Ql src-all +and +.Ql cvs , +respectively. +Assume further that checkout mode is being used with +.Ql tag=RELENG_3 . +The three possible refuse files would then be named: +.Pp +.Bl -item -compact -offset indent +.It +.Pa /usr/local/etc/cvsup/sup/refuse +.It +.Pa /usr/local/etc/cvsup/sup/src-all/refuse +.It +.Pa /usr/local/etc/cvsup/sup/src-all/refuse.cvs:RELENG_3 +.El +.Pp +If the +.Ar supfile +includes the command +.Ar base=/foo +the refuse files would be: +.Pp +.Bl -item -compact -offset indent +.It +.Pa /foo/sup/refuse +.It +.Pa /foo/sup/src-all/refuse +.It +.Pa /foo/sup/src-all/refuse.cvs:RELENG_3 +.El +.Pp +If +.Fl b +.Ar /bar +is used (even with +.Ar base=/foo +in the +.Ar supfile ) : +.Pp +.Bl -item -compact -offset indent +.It +.Pa /bar/sup/refuse +.It +.Pa /bar/sup/src-all/refuse +.It +.Pa /bar/sup/src-all/refuse.cvs:RELENG_3 +.El +.Pp +and with +.Fl c +.Ar stool +as well: +.Pp +.Bl -item -compact -offset indent +.It +.Pa /bar/stool/refuse +.It +.Pa /bar/stool/src-all/refuse +.It +.Pa /bar/stool/src-all/refuse.cvs:RELENG_3 +.El +.Sh AUTHENTICATION +.Nm +implements an optional authentication mechanism which can be used by the +client and server to verify each other's identities. +Public CVSup servers normally do not enable authentication. +.Nm +users may ignore this section unless they have been informed +that authentication is required by the administrator of their server. +.Pp +The authentication subsystem uses a +challenge-response protocol which is immune to packet sniffing and +replay attacks. No passwords are sent over the network in either +direction. Both the client and the server can independently verify +the identities of each other. +.Pp +The file +.Li $ Ns Ev HOME Ns Pa /.csup/auth +holds the information used for authentication. This file contains a +record for each server that the client is allowed to access. Each +record occupies one line in the file. Lines beginning with +.Ql # +are ignored, as are lines containing only white space. White space is +significant everywhere else in the file. Fields are separated by +.Ql \&: +characters. +.Pp +Each record of the file has the following form: +.Bd -literal -offset indent +.Sm off +.Xo Ar serverName No : Ar clientName No : +.Ar password No : Ar comment +.Xc +.Sm on +.Ed +.Pp +All fields must be present even if some of them are empty. +.Ar ServerName +is the name of the server to which the record applies. By convention, +it is the canonical fully-qualified domain name of the server, e.g., +.Ql CVSup177.FreeBSD.ORG . +This must agree with the server's own idea of its name. The name is +case-insensitive. +.Pp +.Ar ClientName +is the name the client uses to gain access to the server. By +convention, e-mail addresses are used for all client names, e.g., +.Ql BillyJoe@FreeBSD.ORG . +Client names are case-insensitive. +.Pp +.Ar Password +is a secret string of characters that the client uses to prove its +identity. It may not contain any +.Ql \&: +or newline characters. +.Pp +.Ar Comment +may contain any additional information to identify the record. It +is not interpreted by the program. +.Pp +To set up authentication for a given server, one must perform the +following steps: +.Bl -enum +.It +Obtain the official +.Ar serverName +from the administrator of the server or from some other source. +.It +Choose an appropriate +.Ar clientName . +It should be in the form of a valid e-mail address, to make it easy +for the server administrator to contact the user if necessary. +.It +Choose an arbitrary secret +.Ar password . +.It +Run the +.Nm cpasswd +utility, and type in the +.Ar password +when prompted for it. The utility will print out a line to send +to the server administrator, and instruct you how to modify your +.Li $ Ns Ev HOME Ns Pa /.csup/auth +file. You should use a secure channel to send the line to the +server administrator. +.El +.Pp +Since +.Li $ Ns Ev HOME Ns Pa /.csup/auth +contains passwords, you should ensure that it is not readable by +anyone except yourself. +.Pp +Authentication works independently in both directions. The server +administrator controls whether you must prove your identity. +You control whether to check the server's identity, by means of the +.Fl a +command line option. +.Sh csup AND FIREWALLS +In its default mode, +.Nm +will work through any firewall which permits outbound connections to +port 5999 of the server host. +.Sh USING csup WITH SOCKS +.Nm +can be used through a SOCKS proxy server with the standard +.Nm runsocks +command. +Your +.Nm +executable needs to be dynamically-linked with the system +libraries for +.Nm runsocks +to work properly. +.Sh USING ssh PORT FORWARDING +As an alternative to SOCKS, a user behind a firewall can penetrate it +with the TCP port forwarding provided by the Secure Shell package +.Nm ssh . +The user must have a login account on the +.Nm CVSup +server host in order to do this. +The procedure is as follows: +.Bl -enum +.It +Establish a connection to the server host with +.Nm ssh , +like this: +.Bd -literal +ssh -f -x -L 5999:localhost:5999 serverhost sleep 60 +.Ed +.Pp +Replace +.Ar serverhost +with the hostname of the CVSup server, but type +.Ql localhost +literally. +This sets up the required port forwarding. +You must start +.Nm +before the 60-second +.Nm sleep +finishes. +Once the update has begun, +.Nm ssh +will keep the forwarded channels open as long as they are needed. +.It +Run +.Nm +on the local host, including the arguments +.Ql -h localhost +on the command line. +.El +.Sh FILES +.Bl -tag -width base/sup/collection/checkouts*xx -compact +.It Pa /usr/local/etc/cvsup +Default +.Ar base +directory. +.It Pa sup +Default +.Ar collDir +subdirectory. +.Sm off +.It Xo Ar base / Ar collDir / Ar collection +.Pa /checkouts* +.Xc +.Sm on +List files. +.El +.Sh SEE ALSO +.Xr cpasswd 1 , +.Xr cvs 1 , +.Xr rcsintro 1 , +.Xr ssh 1 . +.Pp +.Bd -literal +http://mu.org/~mux/csup.html +.Ed +.Sh AUTHORS +.An -nosplit +.An Maxime Henrion Aq mux@FreeBSD.org +is the author of +.Nm , +the rewrite of +.Nm CVSup +in C. +.An John Polstra Aq jdp@polstra.com +is the author of +.Nm CVSup . +.Sh LEGALITIES +CVSup is a registered trademark of John D. Polstra. +.Pp +.Nm +is released under a 2-clauses BSD license. +.Sh BUGS +An RCS file is not recognized as such unless its name ends with +.Ql \&,v . +.Pp +Any directory named +.Ql Attic +is assumed to be a CVS Attic, and is treated specially. diff --git a/usr.bin/csup/detailer.c b/usr.bin/csup/detailer.c new file mode 100644 index 0000000..5592655 --- /dev/null +++ b/usr.bin/csup/detailer.c @@ -0,0 +1,603 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "config.h" +#include "detailer.h" +#include "fixups.h" +#include "globtree.h" +#include "misc.h" +#include "mux.h" +#include "proto.h" +#include "rcsfile.h" +#include "rsyncfile.h" +#include "status.h" +#include "stream.h" + +/* Internal error codes. */ +#define DETAILER_ERR_PROTO (-1) /* Protocol error. */ +#define DETAILER_ERR_MSG (-2) /* Error is in detailer->errmsg. */ +#define DETAILER_ERR_READ (-3) /* Error reading from server. */ +#define DETAILER_ERR_WRITE (-4) /* Error writing to server. */ + +struct detailer { + struct config *config; + struct stream *rd; + struct stream *wr; + char *errmsg; +}; + +static int detailer_batch(struct detailer *); +static int detailer_coll(struct detailer *, struct coll *, + struct status *); +static int detailer_dofile_co(struct detailer *, struct coll *, + struct status *, char *); +static int detailer_dofile_rcs(struct detailer *, struct coll *, + char *, char *); +static int detailer_dofile_regular(struct detailer *, char *, char *); +static int detailer_dofile_rsync(struct detailer *, char *, char *); +static int detailer_checkrcsattr(struct detailer *, struct coll *, char *, + struct fattr *, int); +int detailer_send_details(struct detailer *, struct coll *, char *, + char *, struct fattr *); + +void * +detailer(void *arg) +{ + struct thread_args *args; + struct detailer dbuf, *d; + int error; + + args = arg; + + d = &dbuf; + d->config = args->config; + d->rd = args->rd; + d->wr = args->wr; + d->errmsg = NULL; + + error = detailer_batch(d); + switch (error) { + case DETAILER_ERR_PROTO: + xasprintf(&args->errmsg, "Detailer failed: Protocol error"); + args->status = STATUS_FAILURE; + break; + case DETAILER_ERR_MSG: + xasprintf(&args->errmsg, "Detailer failed: %s", d->errmsg); + free(d->errmsg); + args->status = STATUS_FAILURE; + break; + case DETAILER_ERR_READ: + if (stream_eof(d->rd)) { + xasprintf(&args->errmsg, "Detailer failed: " + "Premature EOF from server"); + } else { + xasprintf(&args->errmsg, "Detailer failed: " + "Network read failure: %s", strerror(errno)); + } + args->status = STATUS_TRANSIENTFAILURE; + break; + case DETAILER_ERR_WRITE: + xasprintf(&args->errmsg, "Detailer failed: " + "Network write failure: %s", strerror(errno)); + args->status = STATUS_TRANSIENTFAILURE; + break; + default: + assert(error == 0); + args->status = STATUS_SUCCESS; + } + return (NULL); +} + +static int +detailer_batch(struct detailer *d) +{ + struct config *config; + struct stream *rd, *wr; + struct coll *coll; + struct status *st; + struct fixup *fixup; + char *cmd, *collname, *line, *release; + int error, fixupseof; + + config = d->config; + rd = d->rd; + wr = d->wr; + STAILQ_FOREACH(coll, &config->colls, co_next) { + if (coll->co_options & CO_SKIP) + continue; + line = stream_getln(rd, NULL); + cmd = proto_get_ascii(&line); + collname = proto_get_ascii(&line); + release = proto_get_ascii(&line); + error = proto_get_time(&line, &coll->co_scantime); + if (error || line != NULL || strcmp(cmd, "COLL") != 0 || + strcmp(collname, coll->co_name) != 0 || + strcmp(release, coll->co_release) != 0) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "COLL %s %s\n", coll->co_name, + coll->co_release); + if (error) + return (DETAILER_ERR_WRITE); + stream_flush(wr); + if (coll->co_options & CO_COMPRESS) { + stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); + stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); + } + st = status_open(coll, -1, &d->errmsg); + if (st == NULL) + return (DETAILER_ERR_MSG); + error = detailer_coll(d, coll, st); + status_close(st, NULL); + if (error) + return (error); + if (coll->co_options & CO_COMPRESS) { + stream_filter_stop(rd); + stream_filter_stop(wr); + } + stream_flush(wr); + } + line = stream_getln(rd, NULL); + if (line == NULL) + return (DETAILER_ERR_READ); + if (strcmp(line, ".") != 0) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, ".\n"); + if (error) + return (DETAILER_ERR_WRITE); + stream_flush(wr); + + /* Now send fixups if needed. */ + fixup = NULL; + fixupseof = 0; + STAILQ_FOREACH(coll, &config->colls, co_next) { + if (coll->co_options & CO_SKIP) + continue; + error = proto_printf(wr, "COLL %s %s\n", coll->co_name, + coll->co_release); + if (error) + return (DETAILER_ERR_WRITE); + if (coll->co_options & CO_COMPRESS) + stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); + while (!fixupseof) { + if (fixup == NULL) + fixup = fixups_get(config->fixups); + if (fixup == NULL) { + fixupseof = 1; + break; + } + if (fixup->f_coll != coll) + break; + if (coll->co_options & CO_CHECKOUTMODE) + error = proto_printf(wr, "Y %s %s %s\n", + fixup->f_name, coll->co_tag, coll->co_date); + else { + error = proto_printf(wr, "A %s\n", + fixup->f_name); + } + if (error) + return (DETAILER_ERR_WRITE); + fixup = NULL; + } + error = proto_printf(wr, ".\n"); + if (error) + return (DETAILER_ERR_WRITE); + if (coll->co_options & CO_COMPRESS) + stream_filter_stop(wr); + stream_flush(wr); + } + error = proto_printf(wr, ".\n"); + if (error) + return (DETAILER_ERR_WRITE); + return (0); +} + +static int +detailer_coll(struct detailer *d, struct coll *coll, struct status *st) +{ + struct fattr *rcsattr; + struct stream *rd, *wr; + char *attr, *cmd, *file, *line, *msg, *path, *target; + int error, attic; + + rd = d->rd; + wr = d->wr; + attic = 0; + line = stream_getln(rd, NULL); + if (line == NULL) + return (DETAILER_ERR_READ); + while (strcmp(line, ".") != 0) { + cmd = proto_get_ascii(&line); + if (cmd == NULL || strlen(cmd) != 1) + return (DETAILER_ERR_PROTO); + switch (cmd[0]) { + case 'D': + /* Delete file. */ + file = proto_get_ascii(&line); + if (file == NULL || line != NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "D %s\n", file); + if (error) + return (DETAILER_ERR_WRITE); + break; + case 'I': + case 'i': + case 'j': + /* Directory operations. */ + file = proto_get_ascii(&line); + if (file == NULL || line != NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s\n", cmd, file); + if (error) + return (DETAILER_ERR_WRITE); + break; + case 'J': + /* Set directory attributes. */ + file = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (file == NULL || line != NULL || attr == NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s %s\n", cmd, file, attr); + if (error) + return (DETAILER_ERR_WRITE); + break; + case 'H': + case 'h': + /* Create a hard link. */ + file = proto_get_ascii(&line); + target = proto_get_ascii(&line); + if (file == NULL || target == NULL) + return (DETAILER_ERR_PROTO); + error = proto_printf(wr, "%s %s %s\n", cmd, file, + target); + break; + case 't': + file = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (file == NULL || attr == NULL || line != NULL) { + return (DETAILER_ERR_PROTO); + } + rcsattr = fattr_decode(attr); + if (rcsattr == NULL) { + return (DETAILER_ERR_PROTO); + } + error = detailer_checkrcsattr(d, coll, file, rcsattr, + 1); + break; + + case 'T': + file = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (file == NULL || attr == NULL || line != NULL) + return (DETAILER_ERR_PROTO); + rcsattr = fattr_decode(attr); + if (rcsattr == NULL) + return (DETAILER_ERR_PROTO); + error = detailer_checkrcsattr(d, coll, file, rcsattr, + 0); + break; + + case 'U': + /* Add or update file. */ + file = proto_get_ascii(&line); + if (file == NULL || line != NULL) + return (DETAILER_ERR_PROTO); + if (coll->co_options & CO_CHECKOUTMODE) { + error = detailer_dofile_co(d, coll, st, file); + } else { + path = cvspath(coll->co_prefix, file, 0); + rcsattr = fattr_frompath(path, FATTR_NOFOLLOW); + error = detailer_send_details(d, coll, file, + path, rcsattr); + if (rcsattr != NULL) + fattr_free(rcsattr); + free(path); + } + if (error) + return (error); + break; + case '!': + /* Warning from server. */ + msg = proto_get_rest(&line); + if (msg == NULL) + return (DETAILER_ERR_PROTO); + lprintf(-1, "Server warning: %s\n", msg); + break; + default: + return (DETAILER_ERR_PROTO); + } + stream_flush(wr); + line = stream_getln(rd, NULL); + if (line == NULL) + return (DETAILER_ERR_READ); + } + error = proto_printf(wr, ".\n"); + if (error) + return (DETAILER_ERR_WRITE); + return (0); +} + +/* + * Tell the server to update a regular file. + */ +static int +detailer_dofile_regular(struct detailer *d, char *name, char *path) +{ + struct stream *wr; + struct stat st; + char md5[MD5_DIGEST_SIZE]; + int error; + + wr = d->wr; + error = stat(path, &st); + /* If we don't have it or it's unaccessible, we want it again. */ + if (error) { + proto_printf(wr, "A %s\n", name); + return (0); + } + + /* If not, we want the file to be updated. */ + error = MD5_File(path, md5); + if (error) { + lprintf(-1, "Error reading \"%s\"\n", name); + return (error); + } + error = proto_printf(wr, "R %s %O %s\n", name, st.st_size, md5); + if (error) + return (DETAILER_ERR_WRITE); + return (0); +} + +/* + * Tell the server to update a file with the rsync algorithm. + */ +static int +detailer_dofile_rsync(struct detailer *d, char *name, char *path) +{ + struct stream *wr; + struct rsyncfile *rf; + + wr = d->wr; + rf = rsync_open(path, 0, 1); + if (rf == NULL) { + /* Fallback if we fail in opening it. */ + proto_printf(wr, "A %s\n", name); + return (0); + } + proto_printf(wr, "r %s %z %z\n", name, rsync_filesize(rf), + rsync_blocksize(rf)); + /* Detail the blocks. */ + while (rsync_nextblock(rf) != 0) + proto_printf(wr, "%s %s\n", rsync_rsum(rf), rsync_blockmd5(rf)); + proto_printf(wr, ".\n"); + rsync_close(rf); + return (0); +} + +/* + * Tell the server to update an RCS file that we have, or send it if we don't. + */ +static int +detailer_dofile_rcs(struct detailer *d, struct coll *coll, char *name, + char *path) +{ + struct stream *wr; + struct fattr *fa; + struct rcsfile *rf; + int error; + + wr = d->wr; + path = atticpath(coll->co_prefix, name); + fa = fattr_frompath(path, FATTR_NOFOLLOW); + if (fa == NULL) { + /* We don't have it, so send request to get it. */ + error = proto_printf(wr, "A %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + free(path); + return (0); + } + + rf = rcsfile_frompath(path, name, coll->co_cvsroot, coll->co_tag, 1); + free(path); + if (rf == NULL) { + error = proto_printf(wr, "A %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + return (0); + } + /* Tell to update the RCS file. The client version details follow. */ + rcsfile_send_details(rf, wr); + rcsfile_free(rf); + fattr_free(fa); + return (0); +} + +static int +detailer_dofile_co(struct detailer *d, struct coll *coll, struct status *st, + char *file) +{ + struct stream *wr; + struct fattr *fa; + struct statusrec *sr; + char md5[MD5_DIGEST_SIZE]; + char *path; + int error, ret; + + wr = d->wr; + path = checkoutpath(coll->co_prefix, file); + if (path == NULL) + return (DETAILER_ERR_PROTO); + fa = fattr_frompath(path, FATTR_NOFOLLOW); + if (fa == NULL) { + /* We don't have the file, so the only option at this + point is to tell the server to send it. The server + may figure out that the file is dead, in which case + it will tell us. */ + error = proto_printf(wr, "C %s %s %s\n", + file, coll->co_tag, coll->co_date); + free(path); + if (error) + return (DETAILER_ERR_WRITE); + return (0); + } + ret = status_get(st, file, 0, 0, &sr); + if (ret == -1) { + d->errmsg = status_errmsg(st); + free(path); + return (DETAILER_ERR_MSG); + } + if (ret == 0) + sr = NULL; + + /* If our recorded information doesn't match the file that the + client has, then ignore the recorded information. */ + if (sr != NULL && (sr->sr_type != SR_CHECKOUTLIVE || + !fattr_equal(sr->sr_clientattr, fa))) + sr = NULL; + fattr_free(fa); + if (sr != NULL && strcmp(sr->sr_revdate, ".") != 0) { + error = proto_printf(wr, "U %s %s %s %s %s\n", file, + coll->co_tag, coll->co_date, sr->sr_revnum, sr->sr_revdate); + free(path); + if (error) + return (DETAILER_ERR_WRITE); + return (0); + } + + /* + * We don't have complete and/or accurate recorded information + * about what version of the file we have. Compute the file's + * checksum as an aid toward identifying which version it is. + */ + error = MD5_File(path, md5); + if (error) { + xasprintf(&d->errmsg, + "Cannot calculate checksum for \"%s\": %s", path, + strerror(errno)); + return (DETAILER_ERR_MSG); + } + free(path); + if (sr == NULL) { + error = proto_printf(wr, "S %s %s %s %s\n", file, + coll->co_tag, coll->co_date, md5); + } else { + error = proto_printf(wr, "s %s %s %s %s %s\n", file, + coll->co_tag, coll->co_date, sr->sr_revnum, md5); + } + if (error) + return (DETAILER_ERR_WRITE); + return (0); +} + +int +detailer_checkrcsattr(struct detailer *d, struct coll *coll, char *name, + struct fattr *server_attr, int attic) +{ + struct fattr *client_attr; + char *attr, *path; + int error; + + /* + * I don't think we can use the status file, since it only records file + * attributes in cvsmode. + */ + client_attr = NULL; + path = cvspath(coll->co_prefix, name, attic); + if (path == NULL) { + return (DETAILER_ERR_PROTO); + } + + if (access(path, F_OK) == 0 && + ((client_attr = fattr_frompath(path, FATTR_NOFOLLOW)) != NULL) && + fattr_equal(client_attr, server_attr)) { + attr = fattr_encode(client_attr, NULL, 0); + if (attic) { + error = proto_printf(d->wr, "l %s %s\n", name, attr); + } else { + error = proto_printf(d->wr, "L %s %s\n", name, attr); + } + free(attr); + free(path); + fattr_free(client_attr); + if (error) + return (DETAILER_ERR_WRITE); + return (0); + } + /* We don't have it, so tell the server to send it. */ + error = detailer_send_details(d, coll, name, path, client_attr); + fattr_free(client_attr); + free(path); + return (error); +} + +int +detailer_send_details(struct detailer *d, struct coll *coll, char *name, + char *path, struct fattr *fa) +{ + int error; + size_t len; + + /* + * Try to check if the file exists either live or dead to see if we can + * edit it and put it live or dead, rather than receiving the entire + * file. + */ + if (fa == NULL) { + path = atticpath(coll->co_prefix, name); + fa = fattr_frompath(path, FATTR_NOFOLLOW); + } + if (fa == NULL) { + error = proto_printf(d->wr, "A %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + } else if (fattr_type(fa) == FT_FILE) { + if (isrcs(name, &len) && !(coll->co_options & CO_NORCS)) { + detailer_dofile_rcs(d, coll, name, path); + } else if (!(coll->co_options & CO_NORSYNC) && + !globtree_test(coll->co_norsync, name)) { + detailer_dofile_rsync(d, name, path); + } else { + detailer_dofile_regular(d, name, path); + } + } else { + error = proto_printf(d->wr, "N %s\n", name); + if (error) + return (DETAILER_ERR_WRITE); + } + return (0); +} diff --git a/usr.bin/csup/detailer.h b/usr.bin/csup/detailer.h new file mode 100644 index 0000000..fe82b27 --- /dev/null +++ b/usr.bin/csup/detailer.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _DETAILER_H_ +#define _DETAILER_H_ + +void *detailer(void *); + +#endif /* !_DETAILER_H_ */ diff --git a/usr.bin/csup/diff.c b/usr.bin/csup/diff.c new file mode 100644 index 0000000..8059676 --- /dev/null +++ b/usr.bin/csup/diff.c @@ -0,0 +1,438 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/limits.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "diff.h" +#include "keyword.h" +#include "misc.h" +#include "stream.h" +#include "queue.h" + +typedef long lineno_t; + +#define EC_ADD 0 +#define EC_DEL 1 +#define MAXKEY LONG_MAX + +/* Editing command and state. */ +struct editcmd { + int cmd; + long key; + int havetext; + int offset; + lineno_t where; + lineno_t count; + lineno_t lasta; + lineno_t lastd; + lineno_t editline; + /* For convenience. */ + struct keyword *keyword; + struct diffinfo *di; + struct stream *orig; + struct stream *dest; + LIST_ENTRY(editcmd) next; +}; + +struct diffstart { + LIST_HEAD(, editcmd) dhead; +}; + +static int diff_geteditcmd(struct editcmd *, char *); +static int diff_copyln(struct editcmd *, lineno_t); +static int diff_ignoreln(struct editcmd *, lineno_t); +static void diff_write(struct editcmd *, void *, size_t); +static int diff_insert_edit(struct diffstart *, struct editcmd *); +static void diff_free(struct diffstart *); + +int +diff_apply(struct stream *rd, struct stream *orig, struct stream *dest, + struct keyword *keyword, struct diffinfo *di, int comode) +{ + struct editcmd ec; + lineno_t i; + size_t size; + char *line; + int empty, error, noeol; + + memset(&ec, 0, sizeof(ec)); + empty = 0; + noeol = 0; + ec.di = di; + ec.keyword = keyword; + ec.orig = orig; + ec.dest = dest; + line = stream_getln(rd, NULL); + while (line != NULL && strcmp(line, ".") != 0 && + strcmp(line, ".+") != 0) { + /* + * The server sends an empty line and then terminates + * with .+ for forced (and thus empty) commits. + */ + if (*line == '\0') { + if (empty) + return (-1); + empty = 1; + line = stream_getln(rd, NULL); + continue; + } + error = diff_geteditcmd(&ec, line); + if (error) + return (-1); + + if (ec.cmd == EC_ADD) { + error = diff_copyln(&ec, ec.where); + if (error) + return (-1); + for (i = 0; i < ec.count; i++) { + line = stream_getln(rd, &size); + if (line == NULL) + return (-1); + if (comode && line[0] == '.') { + line++; + size--; + } + diff_write(&ec, line, size); + } + } else { + assert(ec.cmd == EC_DEL); + error = diff_copyln(&ec, ec.where - 1); + if (error) + return (-1); + for (i = 0; i < ec.count; i++) { + line = stream_getln(orig, NULL); + if (line == NULL) + return (-1); + ec.editline++; + } + } + line = stream_getln(rd, NULL); + } + if (comode && line == NULL) + return (-1); + /* If we got ".+", there's no ending newline. */ + if (comode && strcmp(line, ".+") == 0 && !empty) + noeol = 1; + ec.where = 0; + while ((line = stream_getln(orig, &size)) != NULL) + diff_write(&ec, line, size); + stream_flush(dest); + if (noeol) { + error = stream_truncate_rel(dest, -1); + if (error) { + warn("stream_truncate_rel"); + return (-1); + } + } + return (0); +} + +/* + * Reverse a diff using the same algorithm as in cvsup. + */ +static int +diff_write_reverse(struct stream *dest, struct diffstart *ds) +{ + struct editcmd *ec, *nextec; + long editline, endline, firstoutputlinedeleted; + long num_added, num_deleted, startline; + int num; + + nextec = LIST_FIRST(&ds->dhead); + editline = 0; + num = 0; + while (nextec != NULL) { + ec = nextec; + nextec = LIST_NEXT(nextec, next); + if (nextec == NULL) + break; + num++; + num_deleted = 0; + if (ec->havetext) + num_deleted = ec->count; + num_added = num_deleted + nextec->offset - ec->offset; + if (num_deleted > 0) { + firstoutputlinedeleted = ec->key - num_deleted + 1; + stream_printf(dest, "d%ld %ld\n", firstoutputlinedeleted, + num_deleted); + if (num_added <= 0) + continue; + } + if (num_added > 0) { + stream_printf(dest, "a%ld %ld\n", ec->key, num_added); + startline = ec->key - num_deleted + 1 + ec->offset; + endline = startline + num_added - 1; + + /* Copy lines from original file. First ignore some. */ + ec->editline = editline; + diff_ignoreln(ec, startline - 1); + diff_copyln(ec, endline); + editline = ec->editline; + } + } + return (0); +} + +/* + * Insert a diff into the list sorted on key. Should perhaps use quicker + * algorithms than insertion sort, but do this for now. + */ +static int +diff_insert_edit(struct diffstart *ds, struct editcmd *ec) +{ + struct editcmd *curec; + + if (ec == NULL) + return (0); + + if (LIST_EMPTY(&ds->dhead)) { + LIST_INSERT_HEAD(&ds->dhead, ec, next); + return (0); + } + + /* Insertion sort based on key. */ + LIST_FOREACH(curec, &ds->dhead, next) { + if (ec->key < curec->key) { + LIST_INSERT_BEFORE(curec, ec, next); + return (0); + } + if (LIST_NEXT(curec, next) == NULL) + break; + } + /* Just insert it after. */ + LIST_INSERT_AFTER(curec, ec, next); + return (0); +} + +static void +diff_free(struct diffstart *ds) +{ + struct editcmd *ec; + + while(!LIST_EMPTY(&ds->dhead)) { + ec = LIST_FIRST(&ds->dhead); + LIST_REMOVE(ec, next); + free(ec); + } +} + +/* + * Write the reverse diff from the diff in rd, and original file into + * destination. This algorithm is the same as used in cvsup. + */ +int +diff_reverse(struct stream *rd, struct stream *orig, struct stream *dest, + struct keyword *keyword, struct diffinfo *di) +{ + struct diffstart ds; + struct editcmd ec, *addec, *delec; + lineno_t i; + char *line; + int error, offset; + + memset(&ec, 0, sizeof(ec)); + ec.orig = orig; + ec.dest = dest; + ec.keyword = keyword; + ec.di = di; + addec = NULL; + delec = NULL; + ec.havetext = 0; + offset = 0; + LIST_INIT(&ds.dhead); + + /* Start with next since we need it. */ + line = stream_getln(rd, NULL); + /* First we build up the list of diffs from input. */ + while (line != NULL) { + error = diff_geteditcmd(&ec, line); + if (error) + break; + if (ec.cmd == EC_ADD) { + addec = xmalloc(sizeof(struct editcmd)); + *addec = ec; + addec->havetext = 1; + /* Ignore the lines we was supposed to add. */ + for (i = 0; i < ec.count; i++) { + line = stream_getln(rd, NULL); + if (line == NULL) + return (-1); + } + + /* Get the next diff command if we have one. */ + addec->key = addec->where + addec->count - offset; + if (delec != NULL && + delec->key == addec->key - addec->count) { + delec->key = addec->key; + delec->havetext = addec->havetext; + delec->count = addec->count; + diff_insert_edit(&ds, delec); + free(addec); + delec = NULL; + addec = NULL; + } else { + if (delec != NULL) { + diff_insert_edit(&ds, delec); + } + delec = NULL; + addec->offset = offset; + diff_insert_edit(&ds, addec); + addec = NULL; + } + offset -= ec.count; + } else if (ec.cmd == EC_DEL) { + if (delec != NULL) { + /* Update offset to our next. */ + diff_insert_edit(&ds, delec); + delec = NULL; + } + delec = xmalloc(sizeof(struct editcmd)); + *delec = ec; + delec->key = delec->where - 1 - offset; + delec->offset = offset; + delec->count = 0; + delec->havetext = 0; + /* Important to use the count we had before reset.*/ + offset += ec.count; + } + line = stream_getln(rd, NULL); + } + + while (line != NULL) + line = stream_getln(rd, NULL); + if (delec != NULL) { + diff_insert_edit(&ds, delec); + delec = NULL; + } + + addec = xmalloc(sizeof(struct editcmd)); + /* Should be filesize, but we set it to max value. */ + addec->key = MAXKEY; + addec->offset = offset; + addec->havetext = 0; + addec->count = 0; + diff_insert_edit(&ds, addec); + addec = NULL; + diff_write_reverse(dest, &ds); + diff_free(&ds); + stream_flush(dest); + return (0); +} + +/* Get an editing command from the diff. */ +static int +diff_geteditcmd(struct editcmd *ec, char *line) +{ + char *end; + + if (line[0] == 'a') + ec->cmd = EC_ADD; + else if (line[0] == 'd') + ec->cmd = EC_DEL; + else + return (-1); + errno = 0; + ec->where = strtol(line + 1, &end, 10); + if (errno || ec->where < 0 || *end != ' ') + return (-1); + line = end + 1; + errno = 0; + ec->count = strtol(line, &end, 10); + if (errno || ec->count <= 0 || *end != '\0') + return (-1); + if (ec->cmd == EC_ADD) { + if (ec->where < ec->lasta) + return (-1); + ec->lasta = ec->where + 1; + } else { + if (ec->where < ec->lasta || ec->where < ec->lastd) + return (-1); + ec->lasta = ec->where; + ec->lastd = ec->where + ec->count; + } + return (0); +} + +/* Copy lines from the original version of the file up to line "to". */ +static int +diff_copyln(struct editcmd *ec, lineno_t to) +{ + size_t size; + char *line; + + while (ec->editline < to) { + line = stream_getln(ec->orig, &size); + if (line == NULL) + return (-1); + ec->editline++; + diff_write(ec, line, size); + } + return (0); +} + +/* Ignore lines from the original version of the file up to line "to". */ +static int +diff_ignoreln(struct editcmd *ec, lineno_t to) +{ + size_t size; + char *line; + + while (ec->editline < to) { + line = stream_getln(ec->orig, &size); + if (line == NULL) + return (-1); + ec->editline++; + } + return (0); +} + +/* Write a new line to the file, expanding RCS keywords appropriately. */ +static void +diff_write(struct editcmd *ec, void *buf, size_t size) +{ + size_t newsize; + char *line, *newline; + int ret; + + line = buf; + ret = keyword_expand(ec->keyword, ec->di, line, size, + &newline, &newsize); + if (ret) { + stream_write(ec->dest, newline, newsize); + free(newline); + } else { + stream_write(ec->dest, buf, size); + } +} diff --git a/usr.bin/csup/diff.h b/usr.bin/csup/diff.h new file mode 100644 index 0000000..b0c8c97 --- /dev/null +++ b/usr.bin/csup/diff.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _DIFF_H_ +#define _DIFF_H_ + +struct stream; +struct keyword; +struct file_update; + +/* Description of an RCS delta. */ +struct diffinfo { + char *di_rcsfile; /* RCS filename */ + char *di_cvsroot; /* CVS root prefix */ + char *di_revnum; /* Revision number */ + char *di_revdate; /* Revision date */ + char *di_author; /* Author of the delta */ + char *di_tag; /* CVS tag, if any */ + char *di_state; /* State of the branch */ + int di_expand; /* CVS expansion mode */ +}; + +int diff_apply(struct stream *, struct stream *, struct stream *, + struct keyword *, struct diffinfo *, int); +int diff_reverse(struct stream *, struct stream *, + struct stream *, struct keyword *, struct diffinfo *); + +#endif /* !_DIFF_H_ */ diff --git a/usr.bin/csup/fattr.c b/usr.bin/csup/fattr.c new file mode 100644 index 0000000..b141c2c --- /dev/null +++ b/usr.bin/csup/fattr.c @@ -0,0 +1,981 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/time.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <assert.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "fattr.h" +#include "idcache.h" +#include "misc.h" + +/* + * Include the appropriate definition for the file attributes we support. + * There are two different files: fattr_bsd.h for BSD-like systems that + * support the extended file flags a la chflags() and fattr_posix.h for + * bare POSIX systems that don't. + */ +#ifdef HAVE_FFLAGS +#include "fattr_bsd.h" +#else +#include "fattr_posix.h" +#endif + +#ifdef __FreeBSD__ +#include <osreldate.h> +#endif + +/* Define fflags_t if we're on a system that doesn't have it. */ +#if !defined(__FreeBSD_version) || __FreeBSD_version < 500030 +typedef uint32_t fflags_t; +#endif + +#define FA_MASKRADIX 16 +#define FA_FILETYPERADIX 10 +#define FA_MODTIMERADIX 10 +#define FA_SIZERADIX 10 +#define FA_RDEVRADIX 16 +#define FA_MODERADIX 8 +#define FA_FLAGSRADIX 16 +#define FA_LINKCOUNTRADIX 10 +#define FA_DEVRADIX 16 +#define FA_INODERADIX 10 + +#define FA_PERMMASK (S_IRWXU | S_IRWXG | S_IRWXO) +#define FA_SETIDMASK (S_ISUID | S_ISGID | S_ISVTX) + +struct fattr { + int mask; + int type; + time_t modtime; + off_t size; + char *linktarget; + dev_t rdev; + uid_t uid; + gid_t gid; + mode_t mode; + fflags_t flags; + nlink_t linkcount; + dev_t dev; + ino_t inode; +}; + +static const struct fattr bogus = { + FA_MODTIME | FA_SIZE | FA_MODE, + FT_UNKNOWN, + 1, + 0, + NULL, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0 +}; + +static struct fattr *defaults[FT_NUMBER]; + +void +fattr_init(void) +{ + struct fattr *fa; + int i; + + for (i = 0; i < FT_NUMBER; i++) { + fa = fattr_new(i, -1); + if (i == FT_DIRECTORY) + fa->mode = 0777; + else + fa->mode = 0666; + fa->mask |= FA_MODE; + defaults[i] = fa; + } + /* Initialize the uid/gid lookup cache. */ + idcache_init(); +} + +void +fattr_fini(void) +{ + int i; + + idcache_fini(); + for (i = 0; i < FT_NUMBER; i++) + fattr_free(defaults[i]); +} + +const struct fattr *fattr_bogus = &bogus; + +static char *fattr_scanattr(struct fattr *, int, const char *); + +int +fattr_supported(int type) +{ + + return (fattr_support[type]); +} + +struct fattr * +fattr_new(int type, time_t modtime) +{ + struct fattr *new; + + new = xmalloc(sizeof(struct fattr)); + memset(new, 0, sizeof(struct fattr)); + new->type = type; + if (type != FT_UNKNOWN) + new->mask |= FA_FILETYPE; + if (modtime != -1) { + new->modtime = modtime; + new->mask |= FA_MODTIME; + } + if (fattr_supported(new->type) & FA_LINKCOUNT) { + new->mask |= FA_LINKCOUNT; + new->linkcount = 1; + } + return (new); +} + +/* Returns a new file attribute structure based on a stat structure. */ +struct fattr * +fattr_fromstat(struct stat *sb) +{ + struct fattr *fa; + + fa = fattr_new(FT_UNKNOWN, -1); + if (S_ISREG(sb->st_mode)) + fa->type = FT_FILE; + else if (S_ISDIR(sb->st_mode)) + fa->type = FT_DIRECTORY; + else if (S_ISCHR(sb->st_mode)) + fa->type = FT_CDEV; + else if (S_ISBLK(sb->st_mode)) + fa->type = FT_BDEV; + else if (S_ISLNK(sb->st_mode)) + fa->type = FT_SYMLINK; + else + fa->type = FT_UNKNOWN; + + fa->mask = FA_FILETYPE | fattr_supported(fa->type); + if (fa->mask & FA_MODTIME) + fa->modtime = sb->st_mtime; + if (fa->mask & FA_SIZE) + fa->size = sb->st_size; + if (fa->mask & FA_RDEV) + fa->rdev = sb->st_rdev; + if (fa->mask & FA_OWNER) + fa->uid = sb->st_uid; + if (fa->mask & FA_GROUP) + fa->gid = sb->st_gid; + if (fa->mask & FA_MODE) + fa->mode = sb->st_mode & (FA_SETIDMASK | FA_PERMMASK); +#ifdef HAVE_FFLAGS + if (fa->mask & FA_FLAGS) + fa->flags = sb->st_flags; +#endif + if (fa->mask & FA_LINKCOUNT) + fa->linkcount = sb->st_nlink; + if (fa->mask & FA_DEV) + fa->dev = sb->st_dev; + if (fa->mask & FA_INODE) + fa->inode = sb->st_ino; + return (fa); +} + +struct fattr * +fattr_frompath(const char *path, int nofollow) +{ + struct fattr *fa; + struct stat sb; + int error, len; + + if (nofollow) + error = lstat(path, &sb); + else + error = stat(path, &sb); + if (error) + return (NULL); + fa = fattr_fromstat(&sb); + if (fa->mask & FA_LINKTARGET) { + char buf[1024]; + + len = readlink(path, buf, sizeof(buf)); + if (len == -1) { + fattr_free(fa); + return (NULL); + } + if ((unsigned)len > sizeof(buf) - 1) { + fattr_free(fa); + errno = ENAMETOOLONG; + return (NULL); + } + buf[len] = '\0'; + fa->linktarget = xstrdup(buf); + } + return (fa); +} + +struct fattr * +fattr_fromfd(int fd) +{ + struct fattr *fa; + struct stat sb; + int error; + + error = fstat(fd, &sb); + if (error) + return (NULL); + fa = fattr_fromstat(&sb); + return (fa); +} + +int +fattr_type(const struct fattr *fa) +{ + + return (fa->type); +} + +/* Returns a new file attribute structure from its encoded text form. */ +struct fattr * +fattr_decode(char *attr) +{ + struct fattr *fa; + char *next; + + fa = fattr_new(FT_UNKNOWN, -1); + next = fattr_scanattr(fa, FA_MASK, attr); + if (next == NULL || (fa->mask & ~FA_MASK) > 0) + goto bad; + if (fa->mask & FA_FILETYPE) { + next = fattr_scanattr(fa, FA_FILETYPE, next); + if (next == NULL) + goto bad; + if (fa->type < 0 || fa->type > FT_MAX) + fa->type = FT_UNKNOWN; + } else { + /* The filetype attribute is always valid. */ + fa->mask |= FA_FILETYPE; + fa->type = FT_UNKNOWN; + } + fa->mask = fa->mask & fattr_supported(fa->type); + if (fa->mask & FA_MODTIME) + next = fattr_scanattr(fa, FA_MODTIME, next); + if (fa->mask & FA_SIZE) + next = fattr_scanattr(fa, FA_SIZE, next); + if (fa->mask & FA_LINKTARGET) + next = fattr_scanattr(fa, FA_LINKTARGET, next); + if (fa->mask & FA_RDEV) + next = fattr_scanattr(fa, FA_RDEV, next); + if (fa->mask & FA_OWNER) + next = fattr_scanattr(fa, FA_OWNER, next); + if (fa->mask & FA_GROUP) + next = fattr_scanattr(fa, FA_GROUP, next); + if (fa->mask & FA_MODE) + next = fattr_scanattr(fa, FA_MODE, next); + if (fa->mask & FA_FLAGS) + next = fattr_scanattr(fa, FA_FLAGS, next); + if (fa->mask & FA_LINKCOUNT) { + next = fattr_scanattr(fa, FA_LINKCOUNT, next); + } else if (fattr_supported(fa->type) & FA_LINKCOUNT) { + /* If the link count is missing but supported, fake it as 1. */ + fa->mask |= FA_LINKCOUNT; + fa->linkcount = 1; + } + if (fa->mask & FA_DEV) + next = fattr_scanattr(fa, FA_DEV, next); + if (fa->mask & FA_INODE) + next = fattr_scanattr(fa, FA_INODE, next); + if (next == NULL) + goto bad; + return (fa); +bad: + fattr_free(fa); + return (NULL); +} + +char * +fattr_encode(const struct fattr *fa, fattr_support_t support, int ignore) +{ + struct { + char val[32]; + char len[4]; + int extval; + char *ext; + } pieces[FA_NUMBER], *piece; + char *cp, *s, *username, *groupname; + size_t len, vallen; + mode_t mode, modemask; + int mask, n, i; + + username = NULL; + groupname = NULL; + if (support == NULL) + mask = fa->mask; + else + mask = fa->mask & support[fa->type]; + mask &= ~ignore; + if (fa->mask & FA_OWNER) { + username = getuserbyid(fa->uid); + if (username == NULL) + mask &= ~FA_OWNER; + } + if (fa->mask & FA_GROUP) { + groupname = getgroupbyid(fa->gid); + if (groupname == NULL) + mask &= ~FA_GROUP; + } + if (fa->mask & FA_LINKCOUNT && fa->linkcount == 1) + mask &= ~FA_LINKCOUNT; + + memset(pieces, 0, FA_NUMBER * sizeof(*pieces)); + len = 0; + piece = pieces; + vallen = snprintf(piece->val, sizeof(piece->val), "%x", mask); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + if (mask & FA_FILETYPE) { + vallen = snprintf(piece->val, sizeof(piece->val), + "%d", fa->type); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_MODTIME) { + vallen = snprintf(piece->val, sizeof(piece->val), + "%lld", (long long)fa->modtime); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_SIZE) { + vallen = snprintf(piece->val, sizeof(piece->val), + "%lld", (long long)fa->size); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_LINKTARGET) { + vallen = strlen(fa->linktarget); + piece->extval = 1; + piece->ext = fa->linktarget; + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_RDEV) { + vallen = snprintf(piece->val, sizeof(piece->val), + "%lld", (long long)fa->rdev); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_OWNER) { + vallen = strlen(username); + piece->extval = 1; + piece->ext = username; + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_GROUP) { + vallen = strlen(groupname); + piece->extval = 1; + piece->ext = groupname; + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_MODE) { + if (mask & FA_OWNER && mask & FA_GROUP) + modemask = FA_SETIDMASK | FA_PERMMASK; + else + modemask = FA_PERMMASK; + mode = fa->mode & modemask; + vallen = snprintf(piece->val, sizeof(piece->val), + "%o", mode); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_FLAGS) { + vallen = snprintf(piece->val, sizeof(piece->val), "%llx", + (long long)fa->flags); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_LINKCOUNT) { + vallen = snprintf(piece->val, sizeof(piece->val), "%lld", + (long long)fa->linkcount); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_DEV) { + vallen = snprintf(piece->val, sizeof(piece->val), "%llx", + (long long)fa->dev); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + if (mask & FA_INODE) { + vallen = snprintf(piece->val, sizeof(piece->val), "%lld", + (long long)fa->inode); + len += snprintf(piece->len, sizeof(piece->len), "%lld", + (long long)vallen) + vallen + 1; + piece++; + } + + s = xmalloc(len + 1); + + n = piece - pieces; + piece = pieces; + cp = s; + for (i = 0; i < n; i++) { + if (piece->extval) + len = sprintf(cp, "%s#%s", piece->len, piece->ext); + else + len = sprintf(cp, "%s#%s", piece->len, piece->val); + cp += len; + piece++; + } + return (s); +} + +struct fattr * +fattr_dup(const struct fattr *from) +{ + struct fattr *fa; + + fa = fattr_new(FT_UNKNOWN, -1); + fattr_override(fa, from, FA_MASK); + return (fa); +} + +void +fattr_free(struct fattr *fa) +{ + + if (fa == NULL) + return; + if (fa->linktarget != NULL) + free(fa->linktarget); + free(fa); +} + +void +fattr_umask(struct fattr *fa, mode_t newumask) +{ + + if (fa->mask & FA_MODE) + fa->mode = fa->mode & ~newumask; +} + +void +fattr_maskout(struct fattr *fa, int mask) +{ + + /* Don't forget to free() the linktarget attribute if we remove it. */ + if (mask & FA_LINKTARGET && fa->mask & FA_LINKTARGET) { + free(fa->linktarget); + fa->linktarget = NULL; + } + fa->mask &= ~mask; +} + +int +fattr_getmask(const struct fattr *fa) +{ + + return (fa->mask); +} + +nlink_t +fattr_getlinkcount(const struct fattr *fa) +{ + + return (fa->linkcount); +} + +char * +fattr_getlinktarget(const struct fattr *fa) +{ + + return (fa->linktarget); +} + +/* + * Eat the specified attribute and put it in the file attribute + * structure. Returns NULL on error, or a pointer to the next + * attribute to parse. + * + * This would be much prettier if we had strntol() so that we're + * not forced to write '\0' to the string before calling strtol() + * and then put back the old value... + * + * We need to use (unsigned) long long types here because some + * of the opaque types we're parsing (off_t, time_t...) may need + * 64bits to fit. + */ +static char * +fattr_scanattr(struct fattr *fa, int type, const char *attr) +{ + char *attrend, *attrstart, *end; + size_t len; + unsigned long attrlen; + int error; + mode_t modemask; + char tmp; + + if (attr == NULL) + return (NULL); + errno = 0; + attrlen = strtoul(attr, &end, 10); + if (errno || *end != '#') + return (NULL); + len = strlen(attr); + attrstart = end + 1; + attrend = attrstart + attrlen; + tmp = *attrend; + *attrend = '\0'; + switch (type) { + /* Using FA_MASK here is a bit bogus semantically. */ + case FA_MASK: + errno = 0; + fa->mask = (int)strtol(attrstart, &end, FA_MASKRADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_FILETYPE: + errno = 0; + fa->type = (int)strtol(attrstart, &end, FA_FILETYPERADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_MODTIME: + errno = 0; + fa->modtime = (time_t)strtoll(attrstart, &end, FA_MODTIMERADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_SIZE: + errno = 0; + fa->size = (off_t)strtoll(attrstart, &end, FA_SIZERADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_LINKTARGET: + fa->linktarget = xstrdup(attrstart); + break; + case FA_RDEV: + errno = 0; + fa->rdev = (dev_t)strtoll(attrstart, &end, FA_RDEVRADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_OWNER: + error = getuidbyname(attrstart, &fa->uid); + if (error) + fa->mask &= ~FA_OWNER; + break; + case FA_GROUP: + error = getgidbyname(attrstart, &fa->gid); + if (error) + fa->mask &= ~FA_GROUP; + break; + case FA_MODE: + errno = 0; + fa->mode = (mode_t)strtol(attrstart, &end, FA_MODERADIX); + if (errno || end != attrend) + goto bad; + if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) + modemask = FA_SETIDMASK | FA_PERMMASK; + else + modemask = FA_PERMMASK; + fa->mode &= modemask; + break; + case FA_FLAGS: + errno = 0; + fa->flags = (fflags_t)strtoul(attrstart, &end, FA_FLAGSRADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_LINKCOUNT: + errno = 0; + fa->linkcount = (nlink_t)strtol(attrstart, &end, FA_FLAGSRADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_DEV: + errno = 0; + fa->dev = (dev_t)strtoll(attrstart, &end, FA_DEVRADIX); + if (errno || end != attrend) + goto bad; + break; + case FA_INODE: + errno = 0; + fa->inode = (ino_t)strtoll(attrstart, &end, FA_INODERADIX); + if (errno || end != attrend) + goto bad; + break; + } + *attrend = tmp; + return (attrend); +bad: + *attrend = tmp; + return (NULL); +} + +/* Return a file attribute structure built from the RCS file attributes. */ +struct fattr * +fattr_forcheckout(const struct fattr *rcsattr, mode_t mask) +{ + struct fattr *fa; + + fa = fattr_new(FT_FILE, -1); + if (rcsattr->mask & FA_MODE) { + if ((rcsattr->mode & 0111) > 0) + fa->mode = 0777; + else + fa->mode = 0666; + fa->mode &= ~mask; + fa->mask |= FA_MODE; + } + return (fa); +} + +/* Merge attributes from "from" that aren't present in "fa". */ +void +fattr_merge(struct fattr *fa, const struct fattr *from) +{ + + fattr_override(fa, from, from->mask & ~fa->mask); +} + +/* Merge default attributes. */ +void +fattr_mergedefault(struct fattr *fa) +{ + + fattr_merge(fa, defaults[fa->type]); +} + +/* Override selected attributes of "fa" with values from "from". */ +void +fattr_override(struct fattr *fa, const struct fattr *from, int mask) +{ + + mask &= from->mask; + if (fa->mask & FA_LINKTARGET && mask & FA_LINKTARGET) + free(fa->linktarget); + fa->mask |= mask; + if (mask & FA_FILETYPE) + fa->type = from->type; + if (mask & FA_MODTIME) + fa->modtime = from->modtime; + if (mask & FA_SIZE) + fa->size = from->size; + if (mask & FA_LINKTARGET) + fa->linktarget = xstrdup(from->linktarget); + if (mask & FA_RDEV) + fa->rdev = from->rdev; + if (mask & FA_OWNER) + fa->uid = from->uid; + if (mask & FA_GROUP) + fa->gid = from->gid; + if (mask & FA_MODE) + fa->mode = from->mode; + if (mask & FA_FLAGS) + fa->flags = from->flags; + if (mask & FA_LINKCOUNT) + fa->linkcount = from->linkcount; + if (mask & FA_DEV) + fa->dev = from->dev; + if (mask & FA_INODE) + fa->inode = from->inode; +} + +/* Create a node. */ +int +fattr_makenode(const struct fattr *fa, const char *path) +{ + mode_t modemask, mode; + int error; + + error = 0; + + if (fa->mask & FA_OWNER && fa->mask & FA_GROUP) + modemask = FA_SETIDMASK | FA_PERMMASK; + else + modemask = FA_PERMMASK; + + /* We only implement fattr_makenode() for dirs for now. */ + if (fa->mask & FA_MODE) + mode = fa->mode & modemask; + else + mode = 0700; + + if (fa->type == FT_DIRECTORY) + error = mkdir(path, mode); + else if (fa->type == FT_SYMLINK) { + error = symlink(fa->linktarget, path); + } else if (fa->type == FT_CDEV) { + lprintf(-1, "Character devices not supported!\n"); + } else if (fa->type == FT_BDEV) { + lprintf(-1, "Block devices not supported!\n"); + } + return (error); +} + +int +fattr_delete(const char *path) +{ + struct fattr *fa; + int error; + + fa = fattr_frompath(path, FATTR_NOFOLLOW); + if (fa == NULL) { + if (errno == ENOENT) + return (0); + return (-1); + } + +#ifdef HAVE_FFLAGS + /* Clear flags. */ + if (fa->mask & FA_FLAGS && fa->flags != 0) { + fa->flags = 0; + (void)chflags(path, fa->flags); + } +#endif + + if (fa->type == FT_DIRECTORY) + error = rmdir(path); + else + error = unlink(path); + fattr_free(fa); + return (error); +} + +/* + * Changes those attributes we can change. Returns -1 on error, + * 0 if no update was needed, and 1 if an update was needed and + * it has been applied successfully. + */ +int +fattr_install(struct fattr *fa, const char *topath, const char *frompath) +{ + struct timeval tv[2]; + struct fattr *old; + int error, inplace, mask; + mode_t modemask, newmode; + uid_t uid; + gid_t gid; + + mask = fa->mask & fattr_supported(fa->type); + if (mask & FA_OWNER && mask & FA_GROUP) + modemask = FA_SETIDMASK | FA_PERMMASK; + else + modemask = FA_PERMMASK; + + inplace = 0; + if (frompath == NULL) { + /* Changing attributes in place. */ + frompath = topath; + inplace = 1; + } + old = fattr_frompath(topath, FATTR_NOFOLLOW); + if (old != NULL) { + if (inplace && fattr_equal(fa, old)) { + fattr_free(old); + return (0); + } + +#ifdef HAVE_FFLAGS + /* + * Determine whether we need to clear the flags of the target. + * This is bogus in that it assumes a value of 0 is safe and + * that non-zero is unsafe. I'm not really worried by that + * since as far as I know that's the way things are. + */ + if ((old->mask & FA_FLAGS) && old->flags > 0) { + (void)chflags(topath, 0); + old->flags = 0; + } +#endif + + /* + * If it is changed from a file to a symlink, remove the file + * and create the symlink. + */ + if (inplace && (fa->type == FT_SYMLINK) && + (old->type == FT_FILE)) { + error = unlink(topath); + if (error) + goto bad; + error = symlink(fa->linktarget, topath); + if (error) + goto bad; + } + /* Determine whether we need to remove the target first. */ + if (!inplace && (fa->type == FT_DIRECTORY) != + (old->type == FT_DIRECTORY)) { + if (old->type == FT_DIRECTORY) + error = rmdir(topath); + else + error = unlink(topath); + if (error) + goto bad; + } + } + + /* Change those attributes that we can before moving the file + * into place. That makes installation atomic in most cases. */ + if (mask & FA_MODTIME) { + gettimeofday(tv, NULL); /* Access time. */ + tv[1].tv_sec = fa->modtime; /* Modification time. */ + tv[1].tv_usec = 0; + error = utimes(frompath, tv); + if (error) + goto bad; + } + if (mask & FA_OWNER || mask & FA_GROUP) { + uid = -1; + gid = -1; + if (mask & FA_OWNER) + uid = fa->uid; + if (mask & FA_GROUP) + gid = fa->gid; + error = chown(frompath, uid, gid); + if (error) { + goto bad; + } + } + if (mask & FA_MODE) { + newmode = fa->mode & modemask; + /* Merge in set*id bits from the old attribute. */ + if (old != NULL && old->mask & FA_MODE) { + newmode |= (old->mode & ~modemask); + newmode &= (FA_SETIDMASK | FA_PERMMASK); + } + error = chmod(frompath, newmode); + if (error) + goto bad; + } + + if (!inplace) { + error = rename(frompath, topath); + if (error) + goto bad; + } + +#ifdef HAVE_FFLAGS + /* Set the flags. */ + if (mask & FA_FLAGS) + (void)chflags(topath, fa->flags); +#endif + fattr_free(old); + return (1); +bad: + fattr_free(old); + return (-1); +} + +/* + * Returns 1 if both attributes are equal, 0 otherwise. + * + * This function only compares attributes that are valid in both + * files. A file of unknown type ("FT_UNKNOWN") is unequal to + * anything, including itself. + */ +int +fattr_equal(const struct fattr *fa1, const struct fattr *fa2) +{ + int mask; + + mask = fa1->mask & fa2->mask; + if (fa1->type == FT_UNKNOWN || fa2->type == FT_UNKNOWN) + return (0); + if (mask & FA_FILETYPE) + if (fa1->type != fa2->type) + return (0); + if (mask & FA_MODTIME) + if (fa1->modtime != fa2->modtime) + return (0); + if (mask & FA_SIZE) + if (fa1->size != fa2->size) + return (0); + if (mask & FA_LINKTARGET) + if (strcmp(fa1->linktarget, fa2->linktarget) != 0) + return (0); + if (mask & FA_RDEV) + if (fa1->rdev != fa2->rdev) + return (0); + if (mask & FA_OWNER) + if (fa1->uid != fa2->uid) + return (0); + if (mask & FA_GROUP) + if (fa1->gid != fa2->gid) + return (0); + if (mask & FA_MODE) + if (fa1->mode != fa2->mode) + return (0); + if (mask & FA_FLAGS) + if (fa1->flags != fa2->flags) + return (0); + if (mask & FA_LINKCOUNT) + if (fa1->linkcount != fa2->linkcount) + return (0); + if (mask & FA_DEV) + if (fa1->dev != fa2->dev) + return (0); + if (mask & FA_INODE) + if (fa1->inode != fa2->inode) + return (0); + return (1); +} + +/* + * Must have to get the correct filesize sendt by the server. + */ +off_t +fattr_filesize(const struct fattr *fa) +{ + return (fa->size); +} diff --git a/usr.bin/csup/fattr.h b/usr.bin/csup/fattr.h new file mode 100644 index 0000000..bd4e649 --- /dev/null +++ b/usr.bin/csup/fattr.h @@ -0,0 +1,118 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _FATTR_H_ +#define _FATTR_H_ + +#include <sys/types.h> + +#include <fcntl.h> +#include <time.h> + +/* + * File types. + */ +#define FT_UNKNOWN 0 /* Unknown file type. */ +#define FT_FILE 1 /* Regular file. */ +#define FT_DIRECTORY 2 /* Directory. */ +#define FT_CDEV 3 /* Character device. */ +#define FT_BDEV 4 /* Block device. */ +#define FT_SYMLINK 5 /* Symbolic link. */ +#define FT_MAX FT_SYMLINK /* Maximum file type number. */ +#define FT_NUMBER (FT_MAX + 1) /* Number of file types. */ + +/* + * File attributes. + */ +#define FA_FILETYPE 0x0001 /* True for all supported file types. */ +#define FA_MODTIME 0x0002 /* Last file modification time. */ +#define FA_SIZE 0x0004 /* Size of the file. */ +#define FA_LINKTARGET 0x0008 /* Target of a symbolic link. */ +#define FA_RDEV 0x0010 /* Device for a device node. */ +#define FA_OWNER 0x0020 /* Owner of the file. */ +#define FA_GROUP 0x0040 /* Group of the file. */ +#define FA_MODE 0x0080 /* File permissions. */ +#define FA_FLAGS 0x0100 /* 4.4BSD flags, a la chflags(2). */ +#define FA_LINKCOUNT 0x0200 /* Hard link count. */ +#define FA_DEV 0x0400 /* Device holding the inode. */ +#define FA_INODE 0x0800 /* Inode number. */ + +#define FA_MASK 0x0fff + +#define FA_NUMBER 12 /* Number of file attributes. */ + +/* Attributes that we might be able to change. */ +#define FA_CHANGEABLE (FA_MODTIME | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS) + +/* + * Attributes that we don't want to save in the "checkouts" file + * when in checkout mode. + */ +#define FA_COIGNORE (FA_MASK & ~(FA_FILETYPE|FA_MODTIME|FA_SIZE|FA_MODE)) + +/* These are for fattr_frompath(). */ +#define FATTR_FOLLOW 0 +#define FATTR_NOFOLLOW 1 + +struct stat; +struct fattr; + +typedef int fattr_support_t[FT_NUMBER]; + +extern const struct fattr *fattr_bogus; + +void fattr_init(void); +void fattr_fini(void); + +struct fattr *fattr_new(int, time_t); +struct fattr *fattr_default(int); +struct fattr *fattr_fromstat(struct stat *); +struct fattr *fattr_frompath(const char *, int); +struct fattr *fattr_fromfd(int); +struct fattr *fattr_decode(char *); +struct fattr *fattr_forcheckout(const struct fattr *, mode_t); +struct fattr *fattr_dup(const struct fattr *); +char *fattr_encode(const struct fattr *, fattr_support_t, int); +int fattr_type(const struct fattr *); +void fattr_maskout(struct fattr *, int); +int fattr_getmask(const struct fattr *); +nlink_t fattr_getlinkcount(const struct fattr *); +char *fattr_getlinktarget(const struct fattr *); +void fattr_umask(struct fattr *, mode_t); +void fattr_merge(struct fattr *, const struct fattr *); +void fattr_mergedefault(struct fattr *); +void fattr_override(struct fattr *, const struct fattr *, int); +int fattr_makenode(const struct fattr *, const char *); +int fattr_delete(const char *path); +int fattr_install(struct fattr *, const char *, const char *); +int fattr_equal(const struct fattr *, const struct fattr *); +void fattr_free(struct fattr *); +int fattr_supported(int); +off_t fattr_filesize(const struct fattr *); + + +#endif /* !_FATTR_H_ */ diff --git a/usr.bin/csup/fattr_bsd.h b/usr.bin/csup/fattr_bsd.h new file mode 100644 index 0000000..112a824 --- /dev/null +++ b/usr.bin/csup/fattr_bsd.h @@ -0,0 +1,52 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $Id$ + */ + +/* + * The file attributes we support in a BSD environment. + * + * This is similar to fattr_posix.h, except that we support the FA_FLAGS + * attribute when it makes sense. The FA_FLAGS attribute is for the + * extended BSD file flags, see chflags(2). + */ +fattr_support_t fattr_support = { + /* FT_UNKNOWN */ + 0, + /* FT_FILE */ + FA_FILETYPE | FA_MODTIME | FA_SIZE | FA_OWNER | FA_GROUP | FA_MODE | + FA_FLAGS | FA_LINKCOUNT | FA_INODE | FA_DEV, + /* FT_DIRECTORY */ + FA_FILETYPE | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS, + /* FT_CDEV */ + FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS | + FA_LINKCOUNT | FA_DEV | FA_INODE, + /* FT_BDEV */ + FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_FLAGS | + FA_LINKCOUNT | FA_DEV | FA_INODE, + /* FT_SYMLINK */ + FA_FILETYPE | FA_LINKTARGET +}; diff --git a/usr.bin/csup/fattr_posix.h b/usr.bin/csup/fattr_posix.h new file mode 100644 index 0000000..c4650e4 --- /dev/null +++ b/usr.bin/csup/fattr_posix.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $Id$ + */ + +/* + * The file attributes we support in a POSIX environment. + */ +fattr_support_t fattr_support = { + /* FT_UNKNOWN */ + 0, + /* FT_FILE */ + FA_FILETYPE | FA_MODTIME | FA_SIZE | FA_OWNER | FA_GROUP | FA_MODE | + FA_LINKCOUNT | FA_INODE | FA_DEV, + /* FT_DIRECTORY */ + FA_FILETYPE | FA_OWNER | FA_GROUP | FA_MODE, + /* FT_CDEV */ + FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_LINKCOUNT | + FA_DEV | FA_INODE, + /* FT_BDEV */ + FA_FILETYPE | FA_RDEV | FA_OWNER | FA_GROUP | FA_MODE | FA_LINKCOUNT | + FA_DEV | FA_INODE, + /* FT_SYMLINK */ + FA_FILETYPE | FA_LINKTARGET +}; diff --git a/usr.bin/csup/fixups.c b/usr.bin/csup/fixups.c new file mode 100644 index 0000000..b105a8f --- /dev/null +++ b/usr.bin/csup/fixups.c @@ -0,0 +1,198 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <pthread.h> +#include <stdlib.h> +#include <string.h> + +#include "fixups.h" +#include "misc.h" +#include "queue.h" + +/* + * A synchronized queue to implement fixups. The updater thread adds + * fixup requests to the queue with fixups_put() when a checksum + * mismatch error occured. It then calls fixups_close() when he's + * done requesting fixups. The detailer thread gets the fixups with + * fixups_get() and then send the requests to the server. + * + * The queue is synchronized with a mutex and a condition variable. + */ + +struct fixups { + pthread_mutex_t lock; + pthread_cond_t cond; + STAILQ_HEAD(, fixup) fixupq; + struct fixup *cur; + size_t size; + int closed; +}; + +static void fixups_lock(struct fixups *); +static void fixups_unlock(struct fixups *); + +static struct fixup *fixup_new(struct coll *, const char *); +static void fixup_free(struct fixup *); + +static void +fixups_lock(struct fixups *f) +{ + int error; + + error = pthread_mutex_lock(&f->lock); + assert(!error); +} + +static void +fixups_unlock(struct fixups *f) +{ + int error; + + error = pthread_mutex_unlock(&f->lock); + assert(!error); +} + +static struct fixup * +fixup_new(struct coll *coll, const char *name) +{ + struct fixup *fixup; + + fixup = xmalloc(sizeof(struct fixup)); + fixup->f_name = xstrdup(name); + fixup->f_coll = coll; + return (fixup); +} + +static void +fixup_free(struct fixup *fixup) +{ + + free(fixup->f_name); + free(fixup); +} + +/* Create a new fixup queue. */ +struct fixups * +fixups_new(void) +{ + struct fixups *f; + + f = xmalloc(sizeof(struct fixups)); + f->size = 0; + f->closed = 0; + f->cur = NULL; + STAILQ_INIT(&f->fixupq); + pthread_mutex_init(&f->lock, NULL); + pthread_cond_init(&f->cond, NULL); + return (f); +} + +/* Add a fixup request to the queue. */ +void +fixups_put(struct fixups *f, struct coll *coll, const char *name) +{ + struct fixup *fixup; + int dosignal; + + dosignal = 0; + fixup = fixup_new(coll, name); + fixups_lock(f); + assert(!f->closed); + STAILQ_INSERT_TAIL(&f->fixupq, fixup, f_link); + if (f->size++ == 0) + dosignal = 1; + fixups_unlock(f); + if (dosignal) + pthread_cond_signal(&f->cond); +} + +/* Get a fixup request from the queue. */ +struct fixup * +fixups_get(struct fixups *f) +{ + struct fixup *fixup, *tofree; + + fixups_lock(f); + while (f->size == 0 && !f->closed) + pthread_cond_wait(&f->cond, &f->lock); + if (f->closed) { + fixups_unlock(f); + return (NULL); + } + assert(f->size > 0); + fixup = STAILQ_FIRST(&f->fixupq); + tofree = f->cur; + f->cur = fixup; + STAILQ_REMOVE_HEAD(&f->fixupq, f_link); + f->size--; + fixups_unlock(f); + if (tofree != NULL) + fixup_free(tofree); + return (fixup); +} + +/* Close the writing end of the queue. */ +void +fixups_close(struct fixups *f) +{ + int dosignal; + + dosignal = 0; + fixups_lock(f); + if (f->size == 0 && !f->closed) + dosignal = 1; + f->closed = 1; + fixups_unlock(f); + if (dosignal) + pthread_cond_signal(&f->cond); +} + +/* Free a fixups queue. */ +void +fixups_free(struct fixups *f) +{ + struct fixup *fixup, *fixup2; + + assert(f->closed); + /* + * Free any fixup that has been left on the queue. + * This can happen if we have been aborted prematurely. + */ + fixup = STAILQ_FIRST(&f->fixupq); + while (fixup != NULL) { + fixup2 = STAILQ_NEXT(fixup, f_link); + fixup_free(fixup); + fixup = fixup2; + } + if (f->cur != NULL) + fixup_free(f->cur); + pthread_cond_destroy(&f->cond); + pthread_mutex_destroy(&f->lock); + free(f); +} diff --git a/usr.bin/csup/fixups.h b/usr.bin/csup/fixups.h new file mode 100644 index 0000000..0dddc90 --- /dev/null +++ b/usr.bin/csup/fixups.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _FIXUPS_H_ +#define _FIXUPS_H_ + +#include "queue.h" + +struct coll; +struct fixups; + +struct fixup { + struct coll *f_coll; + char *f_name; + STAILQ_ENTRY(fixup) f_link; /* Not for consumers. */ +}; + +struct fixups *fixups_new(void); +void fixups_put(struct fixups *, struct coll *, const char *); +struct fixup *fixups_get(struct fixups *); +void fixups_close(struct fixups *); +void fixups_free(struct fixups *); + +#endif /* !_FIXUPS_H_ */ diff --git a/usr.bin/csup/fnmatch.c b/usr.bin/csup/fnmatch.c new file mode 100644 index 0000000..a63f016 --- /dev/null +++ b/usr.bin/csup/fnmatch.c @@ -0,0 +1,199 @@ +/* + * 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 + * Guido van Rossum. + * + * 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 FreeBSD fnmatch.c 1.11 + * $Id: fnmatch.c,v 1.3 1997/08/19 02:34:30 jdp Exp $ + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)fnmatch.c 8.2 (Berkeley) 4/16/94"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Function fnmatch() as specified in POSIX 1003.2-1992, section B.6. + * Compares a filename or pathname to a pattern. + */ + +#include <ctype.h> +#include <string.h> +#include <stdio.h> + +#include "fnmatch.h" + +#define EOS '\0' + +static const char *rangematch(const char *, char, int); + +int +fnmatch(const char *pattern, const char *string, int flags) +{ + const char *stringstart; + char c, test; + + for (stringstart = string;;) + switch (c = *pattern++) { + case EOS: + if ((flags & FNM_LEADING_DIR) && *string == '/') + return (0); + return (*string == EOS ? 0 : FNM_NOMATCH); + case '?': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && (flags & FNM_PATHNAME)) + return (FNM_NOMATCH); + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + ++string; + break; + case '*': + c = *pattern; + /* Collapse multiple stars. */ + while (c == '*') + c = *++pattern; + + if (*string == '.' && (flags & FNM_PERIOD) && + (string == stringstart || + ((flags & FNM_PATHNAME) && *(string - 1) == '/'))) + return (FNM_NOMATCH); + + /* Optimize for pattern with * at end or before /. */ + if (c == EOS) + if (flags & FNM_PATHNAME) + return ((flags & FNM_LEADING_DIR) || + strchr(string, '/') == NULL ? + 0 : FNM_NOMATCH); + else + return (0); + else if (c == '/' && flags & FNM_PATHNAME) { + if ((string = strchr(string, '/')) == NULL) + return (FNM_NOMATCH); + break; + } + + /* General case, use recursion. */ + while ((test = *string) != EOS) { + if (!fnmatch(pattern, string, flags & ~FNM_PERIOD)) + return (0); + if (test == '/' && flags & FNM_PATHNAME) + break; + ++string; + } + return (FNM_NOMATCH); + case '[': + if (*string == EOS) + return (FNM_NOMATCH); + if (*string == '/' && flags & FNM_PATHNAME) + return (FNM_NOMATCH); + if ((pattern = + rangematch(pattern, *string, flags)) == NULL) + return (FNM_NOMATCH); + ++string; + break; + case '\\': + if (!(flags & FNM_NOESCAPE)) { + if ((c = *pattern++) == EOS) { + c = '\\'; + --pattern; + } + } + /* FALLTHROUGH */ + default: + if (c == *string) + ; + else if ((flags & FNM_CASEFOLD) && + (tolower((unsigned char)c) == + tolower((unsigned char)*string))) + ; + else if ((flags & FNM_PREFIX_DIRS) && *string == EOS && + ((c == '/' && string != stringstart) || + (string == stringstart+1 && *stringstart == '/'))) + return (0); + else + return (FNM_NOMATCH); + string++; + break; + } + /* NOTREACHED */ +} + +static const char * +rangematch(const char *pattern, char test, int flags) +{ + int negate, ok; + char c, c2; + + /* + * A bracket expression starting with an unquoted circumflex + * character produces unspecified results (IEEE 1003.2-1992, + * 3.13.2). This implementation treats it like '!', for + * consistency with the regular expression syntax. + * J.T. Conklin (conklin@ngai.kaleida.com) + */ + if ( (negate = (*pattern == '!' || *pattern == '^')) ) + ++pattern; + + if (flags & FNM_CASEFOLD) + test = tolower((unsigned char)test); + + for (ok = 0; (c = *pattern++) != ']';) { + if (c == '\\' && !(flags & FNM_NOESCAPE)) + c = *pattern++; + if (c == EOS) + return (NULL); + + if (flags & FNM_CASEFOLD) + c = tolower((unsigned char)c); + + if (*pattern == '-' + && (c2 = *(pattern+1)) != EOS && c2 != ']') { + pattern += 2; + if (c2 == '\\' && !(flags & FNM_NOESCAPE)) + c2 = *pattern++; + if (c2 == EOS) + return (NULL); + + if (flags & FNM_CASEFOLD) + c2 = tolower((unsigned char)c2); + + if ((unsigned char)c <= (unsigned char)test && + (unsigned char)test <= (unsigned char)c2) + ok = 1; + } else if (c == test) + ok = 1; + } + return (ok == negate ? NULL : pattern); +} diff --git a/usr.bin/csup/fnmatch.h b/usr.bin/csup/fnmatch.h new file mode 100644 index 0000000..21d2f64 --- /dev/null +++ b/usr.bin/csup/fnmatch.h @@ -0,0 +1,58 @@ +/*- + * 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. + * + * @(#)fnmatch.h 8.1 (Berkeley) 6/2/93 + * + * From FreeBSD fnmatch.h 1.7 + * $Id: fnmatch.h,v 1.4 2001/10/04 02:46:21 jdp Exp $ + */ + +#ifndef _FNMATCH_H_ +#define _FNMATCH_H_ + +#define FNM_NOMATCH 1 /* Match failed. */ + +#define FNM_NOESCAPE 0x01 /* Disable backslash escaping. */ +#define FNM_PATHNAME 0x02 /* Slash must be matched by slash. */ +#define FNM_PERIOD 0x04 /* Period must be matched by period. */ +#define FNM_LEADING_DIR 0x08 /* Ignore /<tail> after Imatch. */ +#define FNM_CASEFOLD 0x10 /* Case insensitive search. */ +#define FNM_PREFIX_DIRS 0x20 /* Directory prefixes of pattern match too. */ + +/* Make this compile successfully with "gcc -traditional" */ +#ifndef __STDC__ +#define const /* empty */ +#endif + +int fnmatch(const char *, const char *, int); + +#endif /* !_FNMATCH_H_ */ diff --git a/usr.bin/csup/globtree.c b/usr.bin/csup/globtree.c new file mode 100644 index 0000000..74ac2c1 --- /dev/null +++ b/usr.bin/csup/globtree.c @@ -0,0 +1,393 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> + +#include <assert.h> +#include <regex.h> +#include <stdlib.h> + +#include "fnmatch.h" +#include "globtree.h" +#include "misc.h" + +/* + * The "GlobTree" interface allows one to construct arbitrarily complex + * boolean expressions for evaluating whether to accept or reject a + * filename. The globtree_test() function returns true or false + * according to whether the name is accepted or rejected by the + * expression. + * + * Expressions are trees constructed from nodes representing either + * primitive matching operations (primaries) or operators that are + * applied to their subexpressions. The simplest primitives are + * globtree_false(), which matches nothing, and globtree_true(), which + * matches everything. + * + * A more useful primitive is the matching operation, constructed with + * globtree_match(). It will call fnmatch() with the suppliedi + * shell-style pattern to determine if the filename matches. + * + * Expressions can be combined with the boolean operators AND, OR, and + * NOT, to form more complex expressions. + */ + +/* Node types. */ +#define GLOBTREE_NOT 0 +#define GLOBTREE_AND 1 +#define GLOBTREE_OR 2 +#define GLOBTREE_MATCH 3 +#define GLOBTREE_REGEX 4 +#define GLOBTREE_TRUE 5 +#define GLOBTREE_FALSE 6 + +/* A node. */ +struct globtree { + int type; + struct globtree *left; + struct globtree *right; + + /* The "data" field points to the text pattern for GLOBTREE_MATCH + nodes, and to the regex_t for GLOBTREE_REGEX nodes. For any + other node, it is set to NULL. */ + void *data; + /* The "flags" field contains the flags to pass to fnmatch() for + GLOBTREE_MATCH nodes. */ + int flags; +}; + +static struct globtree *globtree_new(int); +static int globtree_eval(struct globtree *, const char *); + +static struct globtree * +globtree_new(int type) +{ + struct globtree *gt; + + gt = xmalloc(sizeof(struct globtree)); + gt->type = type; + gt->data = NULL; + gt->flags = 0; + gt->left = NULL; + gt->right = NULL; + return (gt); +} + +struct globtree * +globtree_true(void) +{ + struct globtree *gt; + + gt = globtree_new(GLOBTREE_TRUE); + return (gt); +} + +struct globtree * +globtree_false(void) +{ + struct globtree *gt; + + gt = globtree_new(GLOBTREE_FALSE); + return (gt); +} + +struct globtree * +globtree_match(const char *pattern, int flags) +{ + struct globtree *gt; + + gt = globtree_new(GLOBTREE_MATCH); + gt->data = xstrdup(pattern); + gt->flags = flags; + return (gt); +} + +struct globtree * +globtree_regex(const char *pattern) +{ + struct globtree *gt; + int error; + + gt = globtree_new(GLOBTREE_REGEX); + gt->data = xmalloc(sizeof(regex_t)); + error = regcomp(gt->data, pattern, REG_NOSUB); + assert(!error); + return (gt); +} + +struct globtree * +globtree_and(struct globtree *left, struct globtree *right) +{ + struct globtree *gt; + + if (left->type == GLOBTREE_FALSE || right->type == GLOBTREE_FALSE) { + globtree_free(left); + globtree_free(right); + gt = globtree_false(); + return (gt); + } + if (left->type == GLOBTREE_TRUE) { + globtree_free(left); + return (right); + } + if (right->type == GLOBTREE_TRUE) { + globtree_free(right); + return (left); + } + gt = globtree_new(GLOBTREE_AND); + gt->left = left; + gt->right = right; + return (gt); +} + +struct globtree * +globtree_or(struct globtree *left, struct globtree *right) +{ + struct globtree *gt; + + if (left->type == GLOBTREE_TRUE || right->type == GLOBTREE_TRUE) { + globtree_free(left); + globtree_free(right); + gt = globtree_true(); + return (gt); + } + if (left->type == GLOBTREE_FALSE) { + globtree_free(left); + return (right); + } + if (right->type == GLOBTREE_FALSE) { + globtree_free(right); + return (left); + } + gt = globtree_new(GLOBTREE_OR); + gt->left = left; + gt->right = right; + return (gt); +} + +struct globtree * +globtree_not(struct globtree *child) +{ + struct globtree *gt; + + if (child->type == GLOBTREE_TRUE) { + globtree_free(child); + gt = globtree_new(GLOBTREE_FALSE); + return (gt); + } + if (child->type == GLOBTREE_FALSE) { + globtree_free(child); + gt = globtree_new(GLOBTREE_TRUE); + return (gt); + } + gt = globtree_new(GLOBTREE_NOT); + gt->left = child; + return (gt); +} + +/* Evaluate one node (must be a leaf node). */ +static int +globtree_eval(struct globtree *gt, const char *path) +{ + int rv; + + switch (gt->type) { + case GLOBTREE_TRUE: + return (1); + case GLOBTREE_FALSE: + return (0); + case GLOBTREE_MATCH: + assert(gt->data != NULL); + rv = fnmatch(gt->data, path, gt->flags); + if (rv == 0) + return (1); + assert(rv == FNM_NOMATCH); + return (0); + case GLOBTREE_REGEX: + assert(gt->data != NULL); + rv = regexec(gt->data, path, 0, NULL, 0); + if (rv == 0) + return (1); + assert(rv == REG_NOMATCH); + return (0); + } + + assert(0); + return (-1); +} + +/* Small stack API to walk the tree iteratively. */ +typedef enum { + STATE_DOINGLEFT, + STATE_DOINGRIGHT +} walkstate_t; + +struct stack { + struct stackelem *stack; + size_t size; + size_t in; +}; + +struct stackelem { + struct globtree *node; + walkstate_t state; +}; + +static void +stack_init(struct stack *stack) +{ + + stack->in = 0; + stack->size = 8; /* Initial size. */ + stack->stack = xmalloc(sizeof(struct stackelem) * stack->size); +} + +static size_t +stack_size(struct stack *stack) +{ + + return (stack->in); +} + +static void +stack_push(struct stack *stack, struct globtree *node, walkstate_t state) +{ + struct stackelem *e; + + if (stack->in == stack->size) { + stack->size *= 2; + stack->stack = xrealloc(stack->stack, + sizeof(struct stackelem) * stack->size); + } + e = stack->stack + stack->in++; + e->node = node; + e->state = state; +} + +static void +stack_pop(struct stack *stack, struct globtree **node, walkstate_t *state) +{ + struct stackelem *e; + + assert(stack->in > 0); + e = stack->stack + --stack->in; + *node = e->node; + *state = e->state; +} + +static void +stack_free(struct stack *s) +{ + + free(s->stack); +} + +/* Tests if the supplied filename matches. */ +int +globtree_test(struct globtree *gt, const char *path) +{ + struct stack stack; + walkstate_t state; + int val; + + stack_init(&stack); + for (;;) { +doleft: + /* Descend to the left until we hit bottom. */ + while (gt->left != NULL) { + stack_push(&stack, gt, STATE_DOINGLEFT); + gt = gt->left; + } + + /* Now we're at a leaf node. Evaluate it. */ + val = globtree_eval(gt, path); + /* Ascend, propagating the value through operator nodes. */ + for (;;) { + if (stack_size(&stack) == 0) { + stack_free(&stack); + return (val); + } + stack_pop(&stack, >, &state); + switch (gt->type) { + case GLOBTREE_NOT: + val = !val; + break; + case GLOBTREE_AND: + /* If we haven't yet evaluated the right subtree + and the partial result is true, descend to + the right. Otherwise the result is already + determined to be val. */ + if (state == STATE_DOINGLEFT && val) { + stack_push(&stack, gt, + STATE_DOINGRIGHT); + gt = gt->right; + goto doleft; + } + break; + case GLOBTREE_OR: + /* If we haven't yet evaluated the right subtree + and the partial result is false, descend to + the right. Otherwise the result is already + determined to be val. */ + if (state == STATE_DOINGLEFT && !val) { + stack_push(&stack, gt, + STATE_DOINGRIGHT); + gt = gt->right; + goto doleft; + } + break; + default: + /* We only push nodes that have children. */ + assert(0); + return (-1); + } + } + } +} + +/* + * We could de-recursify this function using a stack, but it would be + * overkill since it is never called from a thread context with a + * limited stack size nor used in a critical path, so I think we can + * afford keeping it recursive. + */ +void +globtree_free(struct globtree *gt) +{ + + if (gt->data != NULL) { + if (gt->type == GLOBTREE_REGEX) + regfree(gt->data); + free(gt->data); + } + if (gt->left != NULL) + globtree_free(gt->left); + if (gt->right != NULL) + globtree_free(gt->right); + free(gt); +} diff --git a/usr.bin/csup/globtree.h b/usr.bin/csup/globtree.h new file mode 100644 index 0000000..43882e3 --- /dev/null +++ b/usr.bin/csup/globtree.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _GLOBTREE_H_ +#define _GLOBTREE_H_ + +#include "fnmatch.h" + +struct globtree; + +struct globtree *globtree_true(void); +struct globtree *globtree_false(void); +struct globtree *globtree_match(const char *, int); +struct globtree *globtree_regex(const char *); +struct globtree *globtree_and(struct globtree *, struct globtree *); +struct globtree *globtree_or(struct globtree *, struct globtree *); +struct globtree *globtree_not(struct globtree *); +int globtree_test(struct globtree *, const char *); +void globtree_free(struct globtree *); + +#endif /* !_GLOBTREE_H_ */ diff --git a/usr.bin/csup/idcache.c b/usr.bin/csup/idcache.c new file mode 100644 index 0000000..47a3e71 --- /dev/null +++ b/usr.bin/csup/idcache.c @@ -0,0 +1,421 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#include <sys/types.h> + +#include <assert.h> +#include <grp.h> +#include <pthread.h> +#include <pwd.h> +#include <stdlib.h> +#include <string.h> + +#include "idcache.h" +#include "misc.h" + +/* + * Constants and data structures used to implement the thread-safe + * group and password file caches. Cache sizes must be prime. + */ +#define UIDTONAME_SZ 317 /* Size of uid -> user name cache */ +#define NAMETOUID_SZ 317 /* Size of user name -> uid cache */ +#define GIDTONAME_SZ 317 /* Size of gid -> group name cache */ +#define NAMETOGID_SZ 317 /* Size of group name -> gid cache */ + +/* Node structures used to cache lookups. */ +struct uidc { + char *name; /* user name */ + uid_t uid; /* cached uid */ + int valid; /* is this a valid or a miss entry */ + struct uidc *next; /* for collisions */ +}; + +struct gidc { + char *name; /* group name */ + gid_t gid; /* cached gid */ + int valid; /* is this a valid or a miss entry */ + struct gidc *next; /* for collisions */ +}; + +static struct uidc **uidtoname; /* uid to user name cache */ +static struct gidc **gidtoname; /* gid to group name cache */ +static struct uidc **nametouid; /* user name to uid cache */ +static struct gidc **nametogid; /* group name to gid cache */ + +static pthread_mutex_t uid_mtx; +static pthread_mutex_t gid_mtx; + +static void uid_lock(void); +static void uid_unlock(void); +static void gid_lock(void); +static void gid_unlock(void); + +static uint32_t hash(const char *); + +/* A 32-bit version of Peter Weinberger's (PJW) hash algorithm, + as used by ELF for hashing function names. */ +static uint32_t +hash(const char *name) +{ + uint32_t g, h; + + h = 0; + while(*name != '\0') { + h = (h << 4) + *name++; + if ((g = h & 0xF0000000)) { + h ^= g >> 24; + h &= 0x0FFFFFFF; + } + } + return (h); +} + +static void +uid_lock(void) +{ + int error; + + error = pthread_mutex_lock(&uid_mtx); + assert(!error); +} + +static void +uid_unlock(void) +{ + int error; + + error = pthread_mutex_unlock(&uid_mtx); + assert(!error); +} + +static void +gid_lock(void) +{ + int error; + + error = pthread_mutex_lock(&gid_mtx); + assert(!error); +} + +static void +gid_unlock(void) +{ + int error; + + error = pthread_mutex_unlock(&gid_mtx); + assert(!error); +} + +static void +uidc_insert(struct uidc **tbl, struct uidc *uidc, uint32_t key) +{ + + uidc->next = tbl[key]; + tbl[key] = uidc; +} + +static void +gidc_insert(struct gidc **tbl, struct gidc *gidc, uint32_t key) +{ + + gidc->next = tbl[key]; + tbl[key] = gidc; +} + +/* Return the user name for this uid, or NULL if it's not found. */ +char * +getuserbyid(uid_t uid) +{ + struct passwd *pw; + struct uidc *uidc, *uidc2; + uint32_t key, key2; + + key = uid % UIDTONAME_SZ; + uid_lock(); + uidc = uidtoname[key]; + while (uidc != NULL) { + if (uidc->uid == uid) + break; + uidc = uidc->next; + } + + if (uidc == NULL) { + /* We didn't find this uid, look it up and add it. */ + uidc = xmalloc(sizeof(struct uidc)); + uidc->uid = uid; + pw = getpwuid(uid); + if (pw != NULL) { + /* This uid is in the password file. */ + uidc->name = xstrdup(pw->pw_name); + uidc->valid = 1; + /* Also add it to the name -> gid table. */ + uidc2 = xmalloc(sizeof(struct uidc)); + uidc2->uid = uid; + uidc2->name = uidc->name; /* We reuse the pointer. */ + uidc2->valid = 1; + key2 = hash(uidc->name) % NAMETOUID_SZ; + uidc_insert(nametouid, uidc2, key2); + } else { + /* Add a miss entry for this uid. */ + uidc->name = NULL; + uidc->valid = 0; + } + uidc_insert(uidtoname, uidc, key); + } + /* It is safe to unlock here since the cache structure + is not going to get freed or changed. */ + uid_unlock(); + return (uidc->name); +} + +/* Return the group name for this gid, or NULL if it's not found. */ +char * +getgroupbyid(gid_t gid) +{ + struct group *gr; + struct gidc *gidc, *gidc2; + uint32_t key, key2; + + key = gid % GIDTONAME_SZ; + gid_lock(); + gidc = gidtoname[key]; + while (gidc != NULL) { + if (gidc->gid == gid) + break; + gidc = gidc->next; + } + + if (gidc == NULL) { + /* We didn't find this gid, look it up and add it. */ + gidc = xmalloc(sizeof(struct gidc)); + gidc->gid = gid; + gr = getgrgid(gid); + if (gr != NULL) { + /* This gid is in the group file. */ + gidc->name = xstrdup(gr->gr_name); + gidc->valid = 1; + /* Also add it to the name -> gid table. */ + gidc2 = xmalloc(sizeof(struct gidc)); + gidc2->gid = gid; + gidc2->name = gidc->name; /* We reuse the pointer. */ + gidc2->valid = 1; + key2 = hash(gidc->name) % NAMETOGID_SZ; + gidc_insert(nametogid, gidc2, key2); + } else { + /* Add a miss entry for this gid. */ + gidc->name = NULL; + gidc->valid = 0; + } + gidc_insert(gidtoname, gidc, key); + } + /* It is safe to unlock here since the cache structure + is not going to get freed or changed. */ + gid_unlock(); + return (gidc->name); +} + +/* Finds the uid for this user name. If it's found, the gid is stored + in *uid and 0 is returned. Otherwise, -1 is returned. */ +int +getuidbyname(const char *name, uid_t *uid) +{ + struct passwd *pw; + struct uidc *uidc, *uidc2; + uint32_t key, key2; + + uid_lock(); + key = hash(name) % NAMETOUID_SZ; + uidc = nametouid[key]; + while (uidc != NULL) { + if (strcmp(uidc->name, name) == 0) + break; + uidc = uidc->next; + } + + if (uidc == NULL) { + uidc = xmalloc(sizeof(struct uidc)); + uidc->name = xstrdup(name); + pw = getpwnam(name); + if (pw != NULL) { + /* This user name is in the password file. */ + uidc->valid = 1; + uidc->uid = pw->pw_uid; + /* Also add it to the uid -> name table. */ + uidc2 = xmalloc(sizeof(struct uidc)); + uidc2->name = uidc->name; /* We reuse the pointer. */ + uidc2->uid = uidc->uid; + uidc2->valid = 1; + key2 = uidc2->uid % UIDTONAME_SZ; + uidc_insert(uidtoname, uidc2, key2); + } else { + /* Add a miss entry for this user name. */ + uidc->valid = 0; + uidc->uid = (uid_t)-1; /* Should not be accessed. */ + } + uidc_insert(nametouid, uidc, key); + } + /* It is safe to unlock here since the cache structure + is not going to get freed or changed. */ + uid_unlock(); + if (!uidc->valid) + return (-1); + *uid = uidc->uid; + return (0); +} + +/* Finds the gid for this group name. If it's found, the gid is stored + in *gid and 0 is returned. Otherwise, -1 is returned. */ +int +getgidbyname(const char *name, gid_t *gid) +{ + struct group *gr; + struct gidc *gidc, *gidc2; + uint32_t key, key2; + + gid_lock(); + key = hash(name) % NAMETOGID_SZ; + gidc = nametogid[key]; + while (gidc != NULL) { + if (strcmp(gidc->name, name) == 0) + break; + gidc = gidc->next; + } + + if (gidc == NULL) { + gidc = xmalloc(sizeof(struct gidc)); + gidc->name = xstrdup(name); + gr = getgrnam(name); + if (gr != NULL) { + /* This group name is in the group file. */ + gidc->gid = gr->gr_gid; + gidc->valid = 1; + /* Also add it to the gid -> name table. */ + gidc2 = xmalloc(sizeof(struct gidc)); + gidc2->name = gidc->name; /* We reuse the pointer. */ + gidc2->gid = gidc->gid; + gidc2->valid = 1; + key2 = gidc2->gid % GIDTONAME_SZ; + gidc_insert(gidtoname, gidc2, key2); + } else { + /* Add a miss entry for this group name. */ + gidc->gid = (gid_t)-1; /* Should not be accessed. */ + gidc->valid = 0; + } + gidc_insert(nametogid, gidc, key); + } + /* It is safe to unlock here since the cache structure + is not going to get freed or changed. */ + gid_unlock(); + if (!gidc->valid) + return (-1); + *gid = gidc->gid; + return (0); +} + +/* Initialize the cache structures. */ +void +idcache_init(void) +{ + + pthread_mutex_init(&uid_mtx, NULL); + pthread_mutex_init(&gid_mtx, NULL); + uidtoname = xmalloc(UIDTONAME_SZ * sizeof(struct uidc *)); + gidtoname = xmalloc(GIDTONAME_SZ * sizeof(struct gidc *)); + nametouid = xmalloc(NAMETOUID_SZ * sizeof(struct uidc *)); + nametogid = xmalloc(NAMETOGID_SZ * sizeof(struct gidc *)); + memset(uidtoname, 0, UIDTONAME_SZ * sizeof(struct uidc *)); + memset(gidtoname, 0, GIDTONAME_SZ * sizeof(struct gidc *)); + memset(nametouid, 0, NAMETOUID_SZ * sizeof(struct uidc *)); + memset(nametogid, 0, NAMETOGID_SZ * sizeof(struct gidc *)); +} + +/* Cleanup the cache structures. */ +void +idcache_fini(void) +{ + struct uidc *uidc, *uidc2; + struct gidc *gidc, *gidc2; + size_t i; + + for (i = 0; i < UIDTONAME_SZ; i++) { + uidc = uidtoname[i]; + while (uidc != NULL) { + if (uidc->name != NULL) { + assert(uidc->valid); + free(uidc->name); + } + uidc2 = uidc->next; + free(uidc); + uidc = uidc2; + } + } + free(uidtoname); + for (i = 0; i < NAMETOUID_SZ; i++) { + uidc = nametouid[i]; + while (uidc != NULL) { + assert(uidc->name != NULL); + /* If it's a valid entry, it has been added to both the + uidtoname and nametouid tables, and the name pointer + has been reused for both entries. Thus, the name + pointer has already been freed in the loop above. */ + if (!uidc->valid) + free(uidc->name); + uidc2 = uidc->next; + free(uidc); + uidc = uidc2; + } + } + free(nametouid); + for (i = 0; i < GIDTONAME_SZ; i++) { + gidc = gidtoname[i]; + while (gidc != NULL) { + if (gidc->name != NULL) { + assert(gidc->valid); + free(gidc->name); + } + gidc2 = gidc->next; + free(gidc); + gidc = gidc2; + } + } + free(gidtoname); + for (i = 0; i < NAMETOGID_SZ; i++) { + gidc = nametogid[i]; + while (gidc != NULL) { + assert(gidc->name != NULL); + /* See above comment. */ + if (!gidc->valid) + free(gidc->name); + gidc2 = gidc->next; + free(gidc); + gidc = gidc2; + } + } + free(nametogid); + pthread_mutex_destroy(&uid_mtx); + pthread_mutex_destroy(&gid_mtx); +} diff --git a/usr.bin/csup/idcache.h b/usr.bin/csup/idcache.h new file mode 100644 index 0000000..558af0c --- /dev/null +++ b/usr.bin/csup/idcache.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _IDCACHE_H_ +#define _IDCACHE_H_ + +#include <sys/types.h> + +void idcache_init(void); +void idcache_fini(void); + +char *getuserbyid(uid_t); +char *getgroupbyid(gid_t); +int getuidbyname(const char *, uid_t *); +int getgidbyname(const char *, gid_t *); + +#endif /* !_IDCACHE_H_ */ diff --git a/usr.bin/csup/keyword.c b/usr.bin/csup/keyword.c new file mode 100644 index 0000000..049a011 --- /dev/null +++ b/usr.bin/csup/keyword.c @@ -0,0 +1,525 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> + +#include "diff.h" +#include "keyword.h" +#include "misc.h" +#include "queue.h" +#include "stream.h" + +/* + * The keyword API is used to expand the CVS/RCS keywords in files, + * such as $Id$, $Revision$, etc. The server does it for us when it + * sends us entire files, but we need to handle the expansion when + * applying a diff update. + */ + +enum rcskey { + RCSKEY_AUTHOR, + RCSKEY_CVSHEADER, + RCSKEY_DATE, + RCSKEY_HEADER, + RCSKEY_ID, + RCSKEY_LOCKER, + RCSKEY_LOG, + RCSKEY_NAME, + RCSKEY_RCSFILE, + RCSKEY_REVISION, + RCSKEY_SOURCE, + RCSKEY_STATE +}; + +typedef enum rcskey rcskey_t; + +struct tag { + char *ident; + rcskey_t key; + int enabled; + STAILQ_ENTRY(tag) next; +}; + +static struct tag *tag_new(const char *, rcskey_t); +static char *tag_expand(struct tag *, struct diffinfo *); +static void tag_free(struct tag *); + +struct keyword { + STAILQ_HEAD(, tag) keywords; /* Enabled keywords. */ + size_t minkeylen; + size_t maxkeylen; +}; + +/* Default CVS keywords. */ +static struct { + const char *ident; + rcskey_t key; +} tag_defaults[] = { + { "Author", RCSKEY_AUTHOR }, + { "CVSHeader", RCSKEY_CVSHEADER }, + { "Date", RCSKEY_DATE }, + { "Header", RCSKEY_HEADER }, + { "Id", RCSKEY_ID }, + { "Locker", RCSKEY_LOCKER }, + { "Log", RCSKEY_LOG }, + { "Name", RCSKEY_NAME }, + { "RCSfile", RCSKEY_RCSFILE }, + { "Revision", RCSKEY_REVISION }, + { "Source", RCSKEY_SOURCE }, + { "State", RCSKEY_STATE }, + { NULL, 0, } +}; + +struct keyword * +keyword_new(void) +{ + struct keyword *new; + struct tag *tag; + size_t len; + int i; + + new = xmalloc(sizeof(struct keyword)); + STAILQ_INIT(&new->keywords); + new->minkeylen = ~0; + new->maxkeylen = 0; + for (i = 0; tag_defaults[i].ident != NULL; i++) { + tag = tag_new(tag_defaults[i].ident, tag_defaults[i].key); + STAILQ_INSERT_TAIL(&new->keywords, tag, next); + len = strlen(tag->ident); + /* + * These values are only computed here and not updated when + * adding an alias. This is a bug, but CVSup has it and we + * need to be bug-to-bug compatible since the server will + * expect us to do the same, and we will fail with an MD5 + * checksum mismatch if we don't. + */ + new->minkeylen = min(new->minkeylen, len); + new->maxkeylen = max(new->maxkeylen, len); + } + return (new); +} + +int +keyword_decode_expand(const char *expand) +{ + + if (strcmp(expand, ".") == 0) + return (EXPAND_DEFAULT); + else if (strcmp(expand, "kv") == 0) + return (EXPAND_KEYVALUE); + else if (strcmp(expand, "kvl") == 0) + return (EXPAND_KEYVALUELOCKER); + else if (strcmp(expand, "k") == 0) + return (EXPAND_KEY); + else if (strcmp(expand, "o") == 0) + return (EXPAND_OLD); + else if (strcmp(expand, "b") == 0) + return (EXPAND_BINARY); + else if (strcmp(expand, "v") == 0) + return (EXPAND_VALUE); + else + return (-1); +} + +const char * +keyword_encode_expand(int expand) +{ + + switch (expand) { + case EXPAND_DEFAULT: + return ("."); + case EXPAND_KEYVALUE: + return ("kv"); + case EXPAND_KEYVALUELOCKER: + return ("kvl"); + case EXPAND_KEY: + return ("k"); + case EXPAND_OLD: + return ("o"); + case EXPAND_BINARY: + return ("b"); + case EXPAND_VALUE: + return ("v"); + } + return (NULL); +} + +void +keyword_free(struct keyword *keyword) +{ + struct tag *tag; + + if (keyword == NULL) + return; + while (!STAILQ_EMPTY(&keyword->keywords)) { + tag = STAILQ_FIRST(&keyword->keywords); + STAILQ_REMOVE_HEAD(&keyword->keywords, next); + tag_free(tag); + } + free(keyword); +} + +int +keyword_alias(struct keyword *keyword, const char *ident, const char *rcskey) +{ + struct tag *new, *tag; + + STAILQ_FOREACH(tag, &keyword->keywords, next) { + if (strcmp(tag->ident, rcskey) == 0) { + new = tag_new(ident, tag->key); + STAILQ_INSERT_HEAD(&keyword->keywords, new, next); + return (0); + } + } + errno = ENOENT; + return (-1); +} + +int +keyword_enable(struct keyword *keyword, const char *ident) +{ + struct tag *tag; + int all; + + all = 0; + if (strcmp(ident, ".") == 0) + all = 1; + + STAILQ_FOREACH(tag, &keyword->keywords, next) { + if (!all && strcmp(tag->ident, ident) != 0) + continue; + tag->enabled = 1; + if (!all) + return (0); + } + if (!all) { + errno = ENOENT; + return (-1); + } + return (0); +} + +int +keyword_disable(struct keyword *keyword, const char *ident) +{ + struct tag *tag; + int all; + + all = 0; + if (strcmp(ident, ".") == 0) + all = 1; + + STAILQ_FOREACH(tag, &keyword->keywords, next) { + if (!all && strcmp(tag->ident, ident) != 0) + continue; + tag->enabled = 0; + if (!all) + return (0); + } + + if (!all) { + errno = ENOENT; + return (-1); + } + return (0); +} + +void +keyword_prepare(struct keyword *keyword) +{ + struct tag *tag, *temp; + + STAILQ_FOREACH_SAFE(tag, &keyword->keywords, next, temp) { + if (!tag->enabled) { + STAILQ_REMOVE(&keyword->keywords, tag, tag, next); + tag_free(tag); + continue; + } + } +} + +/* + * Expand appropriate RCS keywords. If there's no tag to expand, + * keyword_expand() returns 0, otherwise it returns 1 and writes a + * pointer to the new line in *buf and the new len in *len. The + * new line is allocated with malloc() and needs to be freed by the + * caller after use. + */ +int +keyword_expand(struct keyword *keyword, struct diffinfo *di, char *line, + size_t size, char **buf, size_t *len) +{ + struct tag *tag; + char *dollar, *keystart, *valstart, *vallim, *next; + char *linestart, *newline, *newval, *cp, *tmp; + size_t left, newsize, vallen; + + if (di->di_expand == EXPAND_OLD || di->di_expand == EXPAND_BINARY) + return (0); + newline = NULL; + newsize = 0; + left = size; + linestart = cp = line; +again: + dollar = memchr(cp, '$', left); + if (dollar == NULL) { + if (newline != NULL) { + *buf = newline; + *len = newsize; + return (1); + } + return (0); + } + keystart = dollar + 1; + left -= keystart - cp; + vallim = memchr(keystart, '$', left); + if (vallim == NULL) { + if (newline != NULL) { + *buf = newline; + *len = newsize; + return (1); + } + return (0); + } + if (vallim == keystart) { + cp = keystart; + goto again; + } + valstart = memchr(keystart, ':', left); + if (valstart == keystart) { + cp = vallim; + left -= vallim - keystart; + goto again; + } + if (valstart == NULL || valstart > vallim) + valstart = vallim; + + if (valstart < keystart + keyword->minkeylen || + valstart > keystart + keyword->maxkeylen) { + cp = vallim; + left -= vallim -keystart; + goto again; + } + STAILQ_FOREACH(tag, &keyword->keywords, next) { + if (strncmp(tag->ident, keystart, valstart - keystart) == 0 && + tag->ident[valstart - keystart] == '\0') { + if (newline != NULL) + tmp = newline; + else + tmp = NULL; + newval = NULL; + if (di->di_expand == EXPAND_KEY) { + newsize = dollar - linestart + 1 + + valstart - keystart + 1 + + size - (vallim + 1 - linestart); + newline = xmalloc(newsize); + cp = newline; + memcpy(cp, linestart, dollar - linestart); + cp += dollar - linestart; + *cp++ = '$'; + memcpy(cp, keystart, valstart - keystart); + cp += valstart - keystart; + *cp++ = '$'; + next = cp; + memcpy(cp, vallim + 1, + size - (vallim + 1 - linestart)); + } else if (di->di_expand == EXPAND_VALUE) { + newval = tag_expand(tag, di); + if (newval == NULL) + vallen = 0; + else + vallen = strlen(newval); + newsize = dollar - linestart + + vallen + + size - (vallim + 1 - linestart); + newline = xmalloc(newsize); + cp = newline; + memcpy(cp, linestart, dollar - linestart); + cp += dollar - linestart; + if (newval != NULL) { + memcpy(cp, newval, vallen); + cp += vallen; + } + next = cp; + memcpy(cp, vallim + 1, + size - (vallim + 1 - linestart)); + } else { + assert(di->di_expand == EXPAND_DEFAULT || + di->di_expand == EXPAND_KEYVALUE || + di->di_expand == EXPAND_KEYVALUELOCKER); + newval = tag_expand(tag, di); + if (newval == NULL) + vallen = 0; + else + vallen = strlen(newval); + newsize = dollar - linestart + 1 + + valstart - keystart + 2 + + vallen + 2 + + size - (vallim + 1 - linestart); + newline = xmalloc(newsize); + cp = newline; + memcpy(cp, linestart, dollar - linestart); + cp += dollar - linestart; + *cp++ = '$'; + memcpy(cp, keystart, valstart - keystart); + cp += valstart - keystart; + *cp++ = ':'; + *cp++ = ' '; + if (newval != NULL) { + memcpy(cp, newval, vallen); + cp += vallen; + } + *cp++ = ' '; + *cp++ = '$'; + next = cp; + memcpy(cp, vallim + 1, + size - (vallim + 1 - linestart)); + } + if (newval != NULL) + free(newval); + if (tmp != NULL) + free(tmp); + /* + * Continue looking for tags in the rest of the line. + */ + cp = next; + size = newsize; + left = size - (cp - newline); + linestart = newline; + goto again; + } + } + cp = vallim; + left = size - (cp - linestart); + goto again; +} + +static struct tag * +tag_new(const char *ident, rcskey_t key) +{ + struct tag *new; + + new = xmalloc(sizeof(struct tag)); + new->ident = xstrdup(ident); + new->key = key; + new->enabled = 1; + return (new); +} + +static void +tag_free(struct tag *tag) +{ + + free(tag->ident); + free(tag); +} + +/* + * Expand a specific tag and return the new value. If NULL + * is returned, the tag is empty. + */ +static char * +tag_expand(struct tag *tag, struct diffinfo *di) +{ + /* + * CVS formats dates as "XXXX/XX/XX XX:XX:XX". 32 bytes + * is big enough until year 10,000,000,000,000,000 :-). + */ + char cvsdate[32]; + struct tm tm; + char *filename, *val; + int error; + + error = rcsdatetotm(di->di_revdate, &tm); + if (error) + err(1, "strptime"); + if (strftime(cvsdate, sizeof(cvsdate), "%Y/%m/%d %H:%M:%S", &tm) == 0) + err(1, "strftime"); + filename = strrchr(di->di_rcsfile, '/'); + if (filename == NULL) + filename = di->di_rcsfile; + else + filename++; + + switch (tag->key) { + case RCSKEY_AUTHOR: + xasprintf(&val, "%s", di->di_author); + break; + case RCSKEY_CVSHEADER: + xasprintf(&val, "%s %s %s %s %s", di->di_rcsfile, + di->di_revnum, cvsdate, di->di_author, di->di_state); + break; + case RCSKEY_DATE: + xasprintf(&val, "%s", cvsdate); + break; + case RCSKEY_HEADER: + xasprintf(&val, "%s/%s %s %s %s %s", di->di_cvsroot, + di->di_rcsfile, di->di_revnum, cvsdate, di->di_author, + di->di_state); + break; + case RCSKEY_ID: + xasprintf(&val, "%s %s %s %s %s", filename, di->di_revnum, + cvsdate, di->di_author, di->di_state); + break; + case RCSKEY_LOCKER: + /* + * Unimplemented even in CVSup sources. It seems we don't + * even have this information sent by the server. + */ + return (NULL); + case RCSKEY_LOG: + /* XXX */ + printf("%s: Implement Log keyword expansion\n", __func__); + return (NULL); + case RCSKEY_NAME: + if (di->di_tag != NULL) + xasprintf(&val, "%s", di->di_tag); + else + return (NULL); + break; + case RCSKEY_RCSFILE: + xasprintf(&val, "%s", filename); + break; + case RCSKEY_REVISION: + xasprintf(&val, "%s", di->di_revnum); + break; + case RCSKEY_SOURCE: + xasprintf(&val, "%s/%s", di->di_cvsroot, di->di_rcsfile); + break; + case RCSKEY_STATE: + xasprintf(&val, "%s", di->di_state); + break; + } + return (val); +} diff --git a/usr.bin/csup/keyword.h b/usr.bin/csup/keyword.h new file mode 100644 index 0000000..033cb0a --- /dev/null +++ b/usr.bin/csup/keyword.h @@ -0,0 +1,54 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _KEYWORD_H_ +#define _KEYWORD_H_ + +/* CVS expansion modes. */ +#define EXPAND_DEFAULT 0 +#define EXPAND_KEYVALUE 1 +#define EXPAND_KEYVALUELOCKER 2 +#define EXPAND_KEY 3 +#define EXPAND_OLD 4 +#define EXPAND_BINARY 5 +#define EXPAND_VALUE 6 + +struct diffinfo; +struct keyword; + +struct keyword *keyword_new(void); +int keyword_decode_expand(const char *); +const char *keyword_encode_expand(int); +int keyword_alias(struct keyword *, const char *, const char *); +int keyword_enable(struct keyword *, const char *); +int keyword_disable(struct keyword *, const char *); +void keyword_prepare(struct keyword *); +int keyword_expand(struct keyword *, struct diffinfo *, char *, + size_t, char **, size_t *); +void keyword_free(struct keyword *); + +#endif /* !_KEYWORD_H_ */ diff --git a/usr.bin/csup/lex.rcs.c b/usr.bin/csup/lex.rcs.c new file mode 100644 index 0000000..5db7a7b --- /dev/null +++ b/usr.bin/csup/lex.rcs.c @@ -0,0 +1,2094 @@ + +#line 3 "lex.rcs.c" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* Returned upon end-of-file. */ +#define YY_NULL 0 + +/* Promotes a possibly negative, possibly signed char to an unsigned + * integer for use as an array index. If the signed char is negative, + * we want to instead treat it as an 8-bit unsigned char, hence the + * double cast. + */ +#define YY_SC_TO_UI(c) ((unsigned int) (unsigned char) c) + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN. + */ +#define BEGIN yyg->yy_start = 1 + 2 * + +/* Translate the current start state into a value that can be later handed + * to BEGIN to return to the state. The YYSTATE alias is for lex + * compatibility. + */ +#define YY_START ((yyg->yy_start - 1) / 2) +#define YYSTATE YY_START + +/* Action number for EOF rule of a given start state. */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* Special action meaning "start processing a new file". */ +#define YY_NEW_FILE rcsrestart(yyin ,yyscanner ) + +#define YY_END_OF_BUFFER_CHAR 0 + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +/* The state buf must be large enough to hold one state per character in the main buffer. + */ +#define YY_STATE_BUF_SIZE ((YY_BUF_SIZE + 2) * sizeof(yy_state_type)) + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + + #define YY_LESS_LINENO(n) + +/* Return all but the first "n" matched characters back to the input stream. */ +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + *yy_cp = yyg->yy_hold_char; \ + YY_RESTORE_YY_MORE_OFFSET \ + yyg->yy_c_buf_p = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yyg->yytext_ptr , yyscanner ) + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + +#define YY_BUFFER_NEW 0 +#define YY_BUFFER_NORMAL 1 + /* When an EOF's been seen but there's still some text to process + * then we mark the buffer as YY_EOF_PENDING, to indicate that we + * shouldn't try reading from the input source any more. We might + * still have a bunch of tokens to match, though, because of + * possible backing-up. + * + * When we actually see the EOF, we change the status to "new" + * (via rcsrestart()), so that the user can continue scanning by + * just pointing yyin at a new input file. + */ +#define YY_BUFFER_EOF_PENDING 2 + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +/* We provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state". + * + * Returns the top of the stack, or NULL. + */ +#define YY_CURRENT_BUFFER ( yyg->yy_buffer_stack \ + ? yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] \ + : NULL) + +/* Same as previous macro, but useful when we know that the buffer stack is not + * NULL or when we need an lvalue. For internal use only. + */ +#define YY_CURRENT_BUFFER_LVALUE yyg->yy_buffer_stack[yyg->yy_buffer_stack_top] + +void rcsrestart (FILE *input_file ,yyscan_t yyscanner ); +void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void rcs_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcs_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcspush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void rcspop_buffer_state (yyscan_t yyscanner ); + +static void rcsensure_buffer_stack (yyscan_t yyscanner ); +static void rcs_load_buffer_state (yyscan_t yyscanner ); +static void rcs_init_buffer (YY_BUFFER_STATE b,FILE *file ,yyscan_t yyscanner ); + +#define YY_FLUSH_BUFFER rcs_flush_buffer(YY_CURRENT_BUFFER ,yyscanner) + +YY_BUFFER_STATE rcs_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); + +void *rcsalloc (yy_size_t ,yyscan_t yyscanner ); +void *rcsrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void rcsfree (void * ,yyscan_t yyscanner ); + +#define yy_new_buffer rcs_create_buffer + +#define yy_set_interactive(is_interactive) \ + { \ + if ( ! YY_CURRENT_BUFFER ){ \ + rcsensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \ + } + +#define yy_set_bol(at_bol) \ + { \ + if ( ! YY_CURRENT_BUFFER ){\ + rcsensure_buffer_stack (yyscanner); \ + YY_CURRENT_BUFFER_LVALUE = \ + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); \ + } \ + YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \ + } + +#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol) + +/* Begin user sect3 */ + +#define rcswrap(n) 1 +#define YY_SKIP_YYWRAP + +typedef unsigned char YY_CHAR; + +typedef int yy_state_type; + +#define yytext_ptr yytext_r + +static yy_state_type yy_get_previous_state (yyscan_t yyscanner ); +static yy_state_type yy_try_NUL_trans (yy_state_type current_state ,yyscan_t yyscanner); +static int yy_get_next_buffer (yyscan_t yyscanner ); +static void yy_fatal_error (yyconst char msg[] ,yyscan_t yyscanner ); + +/* Done after the current pattern has been matched and before the + * corresponding action - sets up yytext. + */ +#define YY_DO_BEFORE_ACTION \ + yyg->yytext_ptr = yy_bp; \ + yyleng = (size_t) (yy_cp - yy_bp); \ + yyg->yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yyg->yy_c_buf_p = yy_cp; + +#define YY_NUM_RULES 10 +#define YY_END_OF_BUFFER 11 +/* This struct is not used in this scanner, + but its presence is necessary. */ +struct yy_trans_info + { + flex_int32_t yy_verify; + flex_int32_t yy_nxt; + }; +static yyconst flex_int16_t yy_accept[89] = + { 0, + 0, 0, 11, 5, 9, 8, 10, 4, 4, 7, + 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 9, 5, 4, 4, 5, + 4, 4, 5, 0, 0, 5, 5, 3, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 3, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 2, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, + 5, 5, 5, 1, 5, 5, 5, 0 + } ; + +static yyconst flex_int32_t yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 1, 1, 1, 4, 1, 1, 1, 1, + 1, 1, 1, 4, 1, 5, 1, 6, 6, 6, + 6, 6, 6, 6, 6, 6, 6, 7, 8, 1, + 1, 1, 1, 9, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 10, 11, 12, 13, + + 14, 1, 15, 16, 17, 1, 18, 19, 20, 21, + 22, 23, 1, 24, 25, 26, 27, 1, 1, 28, + 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int32_t yy_meta[30] = + { 0, + 1, 2, 2, 2, 1, 1, 2, 2, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1 + } ; + +static yyconst flex_int16_t yy_base[94] = + { 0, + 0, 0, 136, 25, 127, 297, 297, 27, 29, 297, + 297, 34, 39, 41, 47, 44, 50, 54, 57, 66, + 68, 70, 76, 80, 82, 91, 84, 86, 90, 93, + 95, 97, 102, 58, 61, 0, 0, 107, 109, 112, + 114, 117, 120, 122, 125, 129, 137, 127, 135, 145, + 148, 74, 152, 155, 157, 162, 167, 174, 164, 178, + 182, 184, 187, 189, 191, 193, 196, 200, 204, 206, + 214, 211, 218, 224, 228, 230, 236, 239, 241, 243, + 245, 248, 250, 254, 260, 265, 267, 297, 76, 56, + 47, 292, 294 + + } ; + +static yyconst flex_int16_t yy_def[94] = + { 0, + 88, 1, 88, 89, 88, 88, 88, 90, 91, 88, + 88, 92, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 88, 89, 90, 91, 89, + 91, 91, 92, 93, 93, 33, 33, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 88, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 89, 89, 89, + 89, 89, 89, 89, 89, 89, 89, 0, 88, 88, + 88, 88, 88 + + } ; + +static yyconst flex_int16_t yy_nxt[327] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, + 14, 15, 16, 17, 4, 18, 4, 4, 19, 4, + 20, 4, 4, 4, 21, 22, 4, 4, 4, 24, + 25, 28, 29, 31, 32, 34, 35, 34, 36, 37, + 34, 34, 38, 24, 25, 24, 25, 30, 24, 25, + 39, 24, 25, 43, 24, 25, 27, 44, 24, 25, + 35, 24, 25, 35, 41, 40, 52, 46, 42, 52, + 24, 25, 24, 25, 24, 25, 23, 45, 47, 48, + 24, 25, 34, 51, 24, 25, 24, 25, 24, 25, + 28, 29, 26, 49, 31, 32, 50, 24, 25, 31, + + 32, 31, 32, 34, 35, 34, 36, 37, 34, 34, + 38, 24, 25, 24, 25, 33, 24, 25, 24, 25, + 53, 24, 25, 55, 24, 25, 24, 25, 26, 24, + 25, 24, 25, 24, 25, 88, 56, 54, 60, 24, + 25, 24, 25, 88, 64, 57, 58, 59, 61, 24, + 25, 62, 24, 25, 63, 88, 24, 25, 65, 24, + 25, 24, 25, 88, 66, 68, 24, 25, 24, 25, + 69, 24, 25, 72, 88, 67, 88, 70, 24, 25, + 62, 71, 24, 25, 88, 62, 24, 25, 24, 25, + 62, 24, 25, 24, 25, 24, 25, 24, 25, 73, + + 24, 25, 88, 76, 24, 25, 88, 75, 24, 25, + 24, 25, 62, 88, 74, 24, 25, 79, 24, 25, + 88, 62, 24, 25, 77, 78, 88, 80, 24, 25, + 88, 81, 24, 25, 24, 25, 88, 62, 88, 82, + 24, 25, 62, 24, 25, 24, 25, 24, 25, 24, + 25, 83, 24, 25, 24, 25, 84, 62, 24, 25, + 62, 88, 62, 85, 24, 25, 88, 87, 86, 24, + 25, 24, 25, 62, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 62, 88, 88, 88, 62, + 88, 62, 33, 33, 34, 34, 3, 88, 88, 88, + + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88 + } ; + +static yyconst flex_int16_t yy_chk[327] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 4, + 4, 8, 8, 9, 9, 12, 12, 12, 12, 12, + 12, 12, 12, 13, 13, 14, 14, 91, 16, 16, + 13, 15, 15, 16, 17, 17, 90, 16, 18, 18, + 34, 19, 19, 35, 14, 13, 34, 18, 15, 35, + 20, 20, 21, 21, 22, 22, 89, 17, 19, 20, + 23, 23, 52, 22, 24, 24, 25, 25, 27, 27, + 28, 28, 26, 21, 29, 29, 21, 30, 30, 31, + + 31, 32, 32, 33, 33, 33, 33, 33, 33, 33, + 33, 38, 38, 39, 39, 38, 40, 40, 41, 41, + 39, 42, 42, 41, 43, 43, 44, 44, 5, 45, + 45, 48, 48, 46, 46, 3, 42, 40, 46, 49, + 49, 47, 47, 0, 49, 43, 44, 45, 47, 50, + 50, 47, 51, 51, 48, 0, 53, 53, 49, 54, + 54, 55, 55, 0, 50, 53, 56, 56, 59, 59, + 54, 57, 57, 59, 0, 51, 0, 55, 58, 58, + 57, 56, 60, 60, 0, 58, 61, 61, 62, 62, + 60, 63, 63, 64, 64, 65, 65, 66, 66, 61, + + 67, 67, 0, 66, 68, 68, 0, 65, 69, 69, + 70, 70, 63, 0, 64, 72, 72, 70, 71, 71, + 0, 67, 73, 73, 68, 69, 0, 71, 74, 74, + 0, 72, 75, 75, 76, 76, 0, 74, 0, 75, + 77, 77, 73, 78, 78, 79, 79, 80, 80, 81, + 81, 76, 82, 82, 83, 83, 79, 81, 84, 84, + 77, 0, 78, 80, 85, 85, 0, 84, 83, 86, + 86, 87, 87, 82, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 85, 0, 0, 0, 86, + 0, 87, 92, 92, 93, 93, 88, 88, 88, 88, + + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88, 88, 88, 88, 88, + 88, 88, 88, 88, 88, 88 + } ; + +/* The intent behind this definition is that it'll catch + * any uses of REJECT which flex missed. + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 +#define YY_RESTORE_YY_MORE_OFFSET +#line 1 "rcstokenizer.l" +/*- + * Copyright (c) 2007-2008, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + * + */ +/* + * This tokenizer must be generated by a lexxer with support for reentrancy. + */ +#line 34 "rcstokenizer.l" +#include <string.h> +#include "misc.h" +#include "rcsparse.h" + +#line 567 "lex.rcs.c" + +#define INITIAL 0 + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +/* Holds the entire state of the reentrant scanner. */ +struct yyguts_t + { + + /* User-defined. Not touched by flex. */ + YY_EXTRA_TYPE yyextra_r; + + /* The rest are the same as the globals declared in the non-reentrant scanner. */ + FILE *yyin_r, *yyout_r; + size_t yy_buffer_stack_top; /**< index of top of stack. */ + size_t yy_buffer_stack_max; /**< capacity of stack. */ + YY_BUFFER_STATE * yy_buffer_stack; /**< Stack as an array. */ + char yy_hold_char; + int yy_n_chars; + int yyleng_r; + char *yy_c_buf_p; + int yy_init; + int yy_start; + int yy_did_buffer_switch_on_eof; + int yy_start_stack_ptr; + int yy_start_stack_depth; + int *yy_start_stack; + yy_state_type yy_last_accepting_state; + char* yy_last_accepting_cpos; + + int yylineno_r; + int yy_flex_debug_r; + + char *yytext_r; + int yy_more_flag; + int yy_more_len; + + }; /* end struct yyguts_t */ + +static int yy_init_globals (yyscan_t yyscanner ); + +int rcslex_init (yyscan_t* scanner); + +int rcslex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int rcslex_destroy (yyscan_t yyscanner ); + +int rcsget_debug (yyscan_t yyscanner ); + +void rcsset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner ); + +void rcsset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *rcsget_in (yyscan_t yyscanner ); + +void rcsset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *rcsget_out (yyscan_t yyscanner ); + +void rcsset_out (FILE * out_str ,yyscan_t yyscanner ); + +int rcsget_leng (yyscan_t yyscanner ); + +char *rcsget_text (yyscan_t yyscanner ); + +int rcsget_lineno (yyscan_t yyscanner ); + +void rcsset_lineno (int line_number ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int rcswrap (yyscan_t yyscanner ); +#else +extern int rcswrap (yyscan_t yyscanner ); +#endif +#endif + + static void yyunput (int c,char *buf_ptr ,yyscan_t yyscanner); + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#ifdef __cplusplus +static int yyinput (yyscan_t yyscanner ); +#else +static int input (yyscan_t yyscanner ); +#endif + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Copy whatever the last rule matched to the standard output. */ +#ifndef ECHO +/* This used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite(). + */ +#define ECHO fwrite( yytext, yyleng, 1, yyout ) +#endif + +/* Gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#ifndef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + if ( YY_CURRENT_BUFFER_LVALUE->yy_is_interactive ) \ + { \ + int c = '*'; \ + int n; \ + for ( n = 0; n < max_size && \ + (c = getc( yyin )) != EOF && c != '\n'; ++n ) \ + buf[n] = (char) c; \ + if ( c == '\n' ) \ + buf[n++] = (char) c; \ + if ( c == EOF && ferror( yyin ) ) \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + result = n; \ + } \ + else \ + { \ + errno=0; \ + while ( (result = fread(buf, 1, max_size, yyin))==0 && ferror(yyin)) \ + { \ + if( errno != EINTR) \ + { \ + YY_FATAL_ERROR( "input in flex scanner failed" ); \ + break; \ + } \ + errno=0; \ + clearerr(yyin); \ + } \ + }\ +\ + +#endif + +/* No semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#ifndef yyterminate +#define yyterminate() return YY_NULL +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Report a fatal error. */ +#ifndef YY_FATAL_ERROR +#define YY_FATAL_ERROR(msg) yy_fatal_error( msg , yyscanner) +#endif + +/* end tables serialization structures and prototypes */ + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int rcslex (yyscan_t yyscanner); + +#define YY_DECL int rcslex (yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* Code executed at the beginning of each rule, after yytext and yyleng + * have been set up. + */ +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +/* Code executed at the end of each rule. */ +#ifndef YY_BREAK +#define YY_BREAK break; +#endif + +#define YY_RULE_SETUP \ + YY_USER_ACTION + +/** The main scanner function which does all the work. + */ +YY_DECL +{ + register yy_state_type yy_current_state; + register char *yy_cp, *yy_bp; + register int yy_act; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + +#line 51 "rcstokenizer.l" + + +#line 791 "lex.rcs.c" + + if ( !yyg->yy_init ) + { + yyg->yy_init = 1; + +#ifdef YY_USER_INIT + YY_USER_INIT; +#endif + + if ( ! yyg->yy_start ) + yyg->yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( ! YY_CURRENT_BUFFER ) { + rcsensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + rcs_load_buffer_state(yyscanner ); + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yyg->yy_c_buf_p; + + /* Support of yytext. */ + *yy_cp = yyg->yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of + * the current run. + */ + yy_bp = yy_cp; + + yy_current_state = yyg->yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[YY_SC_TO_UI(*yy_cp)]; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 89 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 297 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + if ( yy_act == 0 ) + { /* have to back up */ + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + yy_act = yy_accept[yy_current_state]; + } + + YY_DO_BEFORE_ACTION; + +do_action: /* This label is used only to access EOF actions. */ + + switch ( yy_act ) + { /* beginning of action switch */ + case 0: /* must back up */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yyg->yy_hold_char; + yy_cp = yyg->yy_last_accepting_cpos; + yy_current_state = yyg->yy_last_accepting_state; + goto yy_find_action; + +case 1: +YY_RULE_SETUP +#line 53 "rcstokenizer.l" +{ + return (KEYWORD_TWO); +} + YY_BREAK +case 2: +YY_RULE_SETUP +#line 56 "rcstokenizer.l" +{ + return (KEYWORD); +} + YY_BREAK +case 3: +/* rule 3 can match eol */ +YY_RULE_SETUP +#line 59 "rcstokenizer.l" +{ + return (STRING); +} + YY_BREAK +case 4: +YY_RULE_SETUP +#line 62 "rcstokenizer.l" +{ + return (NUM); +} + YY_BREAK +case 5: +YY_RULE_SETUP +#line 65 "rcstokenizer.l" +{ +/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/ + return (ID); +} + YY_BREAK +case 6: +YY_RULE_SETUP +#line 69 "rcstokenizer.l" +{ return (SEMIC); } + YY_BREAK +case 7: +YY_RULE_SETUP +#line 70 "rcstokenizer.l" +{ return (COLON); } + YY_BREAK +case 8: +/* rule 8 can match eol */ +YY_RULE_SETUP +#line 71 "rcstokenizer.l" +; + YY_BREAK +case 9: +YY_RULE_SETUP +#line 72 "rcstokenizer.l" +; + YY_BREAK +case 10: +YY_RULE_SETUP +#line 73 "rcstokenizer.l" +ECHO; + YY_BREAK +#line 937 "lex.rcs.c" +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* Amount of text matched not including the EOB char. */ + int yy_amount_of_matched_text = (int) (yy_cp - yyg->yytext_ptr) - 1; + + /* Undo the effects of YY_DO_BEFORE_ACTION. */ + *yy_cp = yyg->yy_hold_char; + YY_RESTORE_YY_MORE_OFFSET + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW ) + { + /* We're scanning a new file or input source. It's + * possible that this happened because the user + * just pointed yyin at a new source and called + * rcslex(). If so, then we have to assure + * consistency between YY_CURRENT_BUFFER and our + * globals. Here is the right place to do so, because + * this is the first action (other than possibly a + * back-up) that will match for the new input source. + */ + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL; + } + + /* Note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the + * end-of-buffer state). Contrast this with the test + * in input(). + */ + if ( yyg->yy_c_buf_p <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + { /* This was really a NUL. */ + yy_state_type yy_next_state; + + yyg->yy_c_buf_p = yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + /* Okay, we're now positioned to make the NUL + * transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we don't + * want to build jamming into it because then it + * will run more slowly). + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state , yyscanner); + + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* Consume the NUL. */ + yy_cp = ++yyg->yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + yy_cp = yyg->yy_c_buf_p; + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_END_OF_FILE: + { + yyg->yy_did_buffer_switch_on_eof = 0; + + if ( rcswrap(yyscanner ) ) + { + /* Note: because we've taken care in + * yy_get_next_buffer() to have set up + * yytext, we can now set up + * yy_c_buf_p so that if some total + * hoser (like flex itself) wants to + * call the scanner after we return the + * YY_NULL, it'll still work - another + * YY_NULL will get returned. + */ + yyg->yy_c_buf_p = yyg->yytext_ptr + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF(YY_START); + goto do_action; + } + + else + { + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + break; + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = + yyg->yytext_ptr + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yyg->yy_c_buf_p = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars]; + + yy_current_state = yy_get_previous_state( yyscanner ); + + yy_cp = yyg->yy_c_buf_p; + yy_bp = yyg->yytext_ptr + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } /* end of action switch */ + } /* end of scanning one token */ +} /* end of rcslex */ + +/* yy_get_next_buffer - try to read in a new buffer + * + * Returns a code representing an action: + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ +static int yy_get_next_buffer (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + register char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf; + register char *source = yyg->yytext_ptr; + register int number_to_move, i; + int ret_val; + + if ( yyg->yy_c_buf_p > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 ) + { /* Don't try to fill the buffer, so this is an EOF. */ + if ( yyg->yy_c_buf_p - yyg->yytext_ptr - YY_MORE_ADJ == 1 ) + { + /* We matched a single character, the EOB, so + * treat this as a final EOF. + */ + return EOB_ACT_END_OF_FILE; + } + + else + { + /* We matched some text prior to the EOB, first + * process it. + */ + return EOB_ACT_LAST_MATCH; + } + } + + /* Try to read more data. */ + + /* First move last chars to start of buffer. */ + number_to_move = (int) (yyg->yy_c_buf_p - yyg->yytext_ptr) - 1; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars = 0; + + else + { + int num_to_read = + YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1; + + while ( num_to_read <= 0 ) + { /* Not enough room in the buffer - grow it. */ + + /* just a shorter name for the current buffer */ + YY_BUFFER_STATE b = YY_CURRENT_BUFFER; + + int yy_c_buf_p_offset = + (int) (yyg->yy_c_buf_p - b->yy_ch_buf); + + if ( b->yy_is_our_buffer ) + { + int new_size = b->yy_buf_size * 2; + + if ( new_size <= 0 ) + b->yy_buf_size += b->yy_buf_size / 8; + else + b->yy_buf_size *= 2; + + b->yy_ch_buf = (char *) + /* Include room in for 2 EOB chars. */ + rcsrealloc((void *) b->yy_ch_buf,b->yy_buf_size + 2 ,yyscanner ); + } + else + /* Can't grow it, we don't own it. */ + b->yy_ch_buf = 0; + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( + "fatal error - scanner input buffer overflow" ); + + yyg->yy_c_buf_p = &b->yy_ch_buf[yy_c_buf_p_offset]; + + num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size - + number_to_move - 1; + + } + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + /* Read in more data. */ + YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]), + yyg->yy_n_chars, (size_t) num_to_read ); + + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + if ( yyg->yy_n_chars == 0 ) + { + if ( number_to_move == YY_MORE_ADJ ) + { + ret_val = EOB_ACT_END_OF_FILE; + rcsrestart(yyin ,yyscanner); + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = + YY_BUFFER_EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + if ((yy_size_t) (yyg->yy_n_chars + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) { + /* Extend the array by 50%, plus the number we really need. */ + yy_size_t new_size = yyg->yy_n_chars + number_to_move + (yyg->yy_n_chars >> 1); + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) rcsrealloc((void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf,new_size ,yyscanner ); + if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" ); + } + + yyg->yy_n_chars += number_to_move; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] = YY_END_OF_BUFFER_CHAR; + YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + yyg->yytext_ptr = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0]; + + return ret_val; +} + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + + static yy_state_type yy_get_previous_state (yyscan_t yyscanner) +{ + register yy_state_type yy_current_state; + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_current_state = yyg->yy_start; + + for ( yy_cp = yyg->yytext_ptr + YY_MORE_ADJ; yy_cp < yyg->yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1); + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 89 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + } + + return yy_current_state; +} + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + static yy_state_type yy_try_NUL_trans (yy_state_type yy_current_state , yyscan_t yyscanner) +{ + register int yy_is_jam; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; /* This var may be unused depending upon options. */ + register char *yy_cp = yyg->yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yyg->yy_last_accepting_state = yy_current_state; + yyg->yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = (int) yy_def[yy_current_state]; + if ( yy_current_state >= 89 ) + yy_c = yy_meta[(unsigned int) yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + (unsigned int) yy_c]; + yy_is_jam = (yy_current_state == 88); + + return yy_is_jam ? 0 : yy_current_state; +} + + static void yyunput (int c, register char * yy_bp , yyscan_t yyscanner) +{ + register char *yy_cp; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + yy_cp = yyg->yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yyg->yy_hold_char; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + /* +2 for EOB chars. */ + register int number_to_move = yyg->yy_n_chars + 2; + register char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[ + YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2]; + register char *source = + &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]; + + while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf ) + *--dest = *--source; + + yy_cp += (int) (dest - source); + yy_bp += (int) (dest - source); + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_buf_size; + + if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + *--yy_cp = (char) c; + + yyg->yytext_ptr = yy_bp; + yyg->yy_hold_char = *yy_cp; + yyg->yy_c_buf_p = yy_cp; +} + +#ifndef YY_NO_INPUT +#ifdef __cplusplus + static int yyinput (yyscan_t yyscanner) +#else + static int input (yyscan_t yyscanner) +#endif + +{ + int c; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + *yyg->yy_c_buf_p = yyg->yy_hold_char; + + if ( *yyg->yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yyg->yy_c_buf_p < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[yyg->yy_n_chars] ) + /* This was really a NUL. */ + *yyg->yy_c_buf_p = '\0'; + + else + { /* need more input */ + int offset = yyg->yy_c_buf_p - yyg->yytext_ptr; + ++yyg->yy_c_buf_p; + + switch ( yy_get_next_buffer( yyscanner ) ) + { + case EOB_ACT_LAST_MATCH: + /* This happens because yy_g_n_b() + * sees that we've accumulated a + * token and flags that we need to + * try matching the token before + * proceeding. But for input(), + * there's no matching to consider. + * So convert the EOB_ACT_LAST_MATCH + * to EOB_ACT_END_OF_FILE. + */ + + /* Reset buffer status. */ + rcsrestart(yyin ,yyscanner); + + /*FALLTHROUGH*/ + + case EOB_ACT_END_OF_FILE: + { + if ( rcswrap(yyscanner ) ) + return EOF; + + if ( ! yyg->yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; +#ifdef __cplusplus + return yyinput(yyscanner); +#else + return input(yyscanner); +#endif + } + + case EOB_ACT_CONTINUE_SCAN: + yyg->yy_c_buf_p = yyg->yytext_ptr + offset; + break; + } + } + } + + c = *(unsigned char *) yyg->yy_c_buf_p; /* cast for 8-bit char's */ + *yyg->yy_c_buf_p = '\0'; /* preserve yytext */ + yyg->yy_hold_char = *++yyg->yy_c_buf_p; + + return c; +} +#endif /* ifndef YY_NO_INPUT */ + +/** Immediately switch to a different input stream. + * @param input_file A readable stream. + * @param yyscanner The scanner object. + * @note This function does not reset the start condition to @c INITIAL . + */ + void rcsrestart (FILE * input_file , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! YY_CURRENT_BUFFER ){ + rcsensure_buffer_stack (yyscanner); + YY_CURRENT_BUFFER_LVALUE = + rcs_create_buffer(yyin,YY_BUF_SIZE ,yyscanner); + } + + rcs_init_buffer(YY_CURRENT_BUFFER,input_file ,yyscanner); + rcs_load_buffer_state(yyscanner ); +} + +/** Switch to a different input buffer. + * @param new_buffer The new input buffer. + * @param yyscanner The scanner object. + */ + void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* TODO. We should be able to replace this entire function body + * with + * rcspop_buffer_state(); + * rcspush_buffer_state(new_buffer); + */ + rcsensure_buffer_stack (yyscanner); + if ( YY_CURRENT_BUFFER == new_buffer ) + return; + + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + YY_CURRENT_BUFFER_LVALUE = new_buffer; + rcs_load_buffer_state(yyscanner ); + + /* We don't actually know whether we did this switch during + * EOF (rcswrap()) processing, but the only time this flag + * is looked at is after rcswrap() is called, so it's safe + * to go ahead and always set it. + */ + yyg->yy_did_buffer_switch_on_eof = 1; +} + +static void rcs_load_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyg->yy_n_chars = YY_CURRENT_BUFFER_LVALUE->yy_n_chars; + yyg->yytext_ptr = yyg->yy_c_buf_p = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos; + yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file; + yyg->yy_hold_char = *yyg->yy_c_buf_p; +} + +/** Allocate and initialize an input buffer state. + * @param file A readable stream. + * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE. + * @param yyscanner The scanner object. + * @return the allocated buffer state. + */ + YY_BUFFER_STATE rcs_create_buffer (FILE * file, int size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) rcsalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (char *) rcsalloc(b->yy_buf_size + 2 ,yyscanner ); + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_create_buffer()" ); + + b->yy_is_our_buffer = 1; + + rcs_init_buffer(b,file ,yyscanner); + + return b; +} + +/** Destroy the buffer. + * @param b a buffer created with rcs_create_buffer() + * @param yyscanner The scanner object. + */ + void rcs_delete_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if ( ! b ) + return; + + if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */ + YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0; + + if ( b->yy_is_our_buffer ) + rcsfree((void *) b->yy_ch_buf ,yyscanner ); + + rcsfree((void *) b ,yyscanner ); +} + +#ifndef __cplusplus +extern int isatty (int ); +#endif /* __cplusplus */ + +/* Initializes or reinitializes a buffer. + * This function is sometimes called more than once on the same buffer, + * such as during a rcsrestart() or at EOF. + */ + static void rcs_init_buffer (YY_BUFFER_STATE b, FILE * file , yyscan_t yyscanner) + +{ + int oerrno = errno; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + rcs_flush_buffer(b ,yyscanner); + + b->yy_input_file = file; + b->yy_fill_buffer = 1; + + /* If b is the current buffer, then rcs_init_buffer was _probably_ + * called from rcsrestart() or through yy_get_next_buffer. + * In that case, we don't want to reset the lineno or column. + */ + if (b != YY_CURRENT_BUFFER){ + b->yy_bs_lineno = 1; + b->yy_bs_column = 0; + } + + b->yy_is_interactive = file ? (isatty( fileno(file) ) > 0) : 0; + + errno = oerrno; +} + +/** Discard all buffered characters. On the next scan, YY_INPUT will be called. + * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER. + * @param yyscanner The scanner object. + */ + void rcs_flush_buffer (YY_BUFFER_STATE b , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if ( ! b ) + return; + + b->yy_n_chars = 0; + + /* We always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[0]; + + b->yy_at_bol = 1; + b->yy_buffer_status = YY_BUFFER_NEW; + + if ( b == YY_CURRENT_BUFFER ) + rcs_load_buffer_state(yyscanner ); +} + +/** Pushes the new state onto the stack. The new state becomes + * the current state. This function will allocate the stack + * if necessary. + * @param new_buffer The new state. + * @param yyscanner The scanner object. + */ +void rcspush_buffer_state (YY_BUFFER_STATE new_buffer , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (new_buffer == NULL) + return; + + rcsensure_buffer_stack(yyscanner); + + /* This block is copied from rcs_switch_to_buffer. */ + if ( YY_CURRENT_BUFFER ) + { + /* Flush out information for old buffer. */ + *yyg->yy_c_buf_p = yyg->yy_hold_char; + YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = yyg->yy_c_buf_p; + YY_CURRENT_BUFFER_LVALUE->yy_n_chars = yyg->yy_n_chars; + } + + /* Only push if top exists. Otherwise, replace top. */ + if (YY_CURRENT_BUFFER) + yyg->yy_buffer_stack_top++; + YY_CURRENT_BUFFER_LVALUE = new_buffer; + + /* copied from rcs_switch_to_buffer. */ + rcs_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; +} + +/** Removes and deletes the top of the stack, if present. + * The next element becomes the new top. + * @param yyscanner The scanner object. + */ +void rcspop_buffer_state (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + if (!YY_CURRENT_BUFFER) + return; + + rcs_delete_buffer(YY_CURRENT_BUFFER ,yyscanner); + YY_CURRENT_BUFFER_LVALUE = NULL; + if (yyg->yy_buffer_stack_top > 0) + --yyg->yy_buffer_stack_top; + + if (YY_CURRENT_BUFFER) { + rcs_load_buffer_state(yyscanner ); + yyg->yy_did_buffer_switch_on_eof = 1; + } +} + +/* Allocates the stack if it does not exist. + * Guarantees space for at least one push. + */ +static void rcsensure_buffer_stack (yyscan_t yyscanner) +{ + int num_to_alloc; + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (!yyg->yy_buffer_stack) { + + /* First allocation is just for 2 elements, since we don't know if this + * scanner will even need a stack. We use 2 instead of 1 to avoid an + * immediate realloc on the next call. + */ + num_to_alloc = 1; + yyg->yy_buffer_stack = (struct yy_buffer_state**)rcsalloc + (num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in rcsensure_buffer_stack()" ); + + memset(yyg->yy_buffer_stack, 0, num_to_alloc * sizeof(struct yy_buffer_state*)); + + yyg->yy_buffer_stack_max = num_to_alloc; + yyg->yy_buffer_stack_top = 0; + return; + } + + if (yyg->yy_buffer_stack_top >= (yyg->yy_buffer_stack_max) - 1){ + + /* Increase the buffer to prepare for a possible push. */ + int grow_size = 8 /* arbitrary grow size */; + + num_to_alloc = yyg->yy_buffer_stack_max + grow_size; + yyg->yy_buffer_stack = (struct yy_buffer_state**)rcsrealloc + (yyg->yy_buffer_stack, + num_to_alloc * sizeof(struct yy_buffer_state*) + , yyscanner); + if ( ! yyg->yy_buffer_stack ) + YY_FATAL_ERROR( "out of dynamic memory in rcsensure_buffer_stack()" ); + + /* zero only the new slots.*/ + memset(yyg->yy_buffer_stack + yyg->yy_buffer_stack_max, 0, grow_size * sizeof(struct yy_buffer_state*)); + yyg->yy_buffer_stack_max = num_to_alloc; + } +} + +/** Setup the input buffer state to scan directly from a user-specified character buffer. + * @param base the character buffer + * @param size the size in bytes of the character buffer + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE rcs_scan_buffer (char * base, yy_size_t size , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + + if ( size < 2 || + base[size-2] != YY_END_OF_BUFFER_CHAR || + base[size-1] != YY_END_OF_BUFFER_CHAR ) + /* They forgot to leave room for the EOB's. */ + return 0; + + b = (YY_BUFFER_STATE) rcsalloc(sizeof( struct yy_buffer_state ) ,yyscanner ); + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_scan_buffer()" ); + + b->yy_buf_size = size - 2; /* "- 2" to take care of EOB's */ + b->yy_buf_pos = b->yy_ch_buf = base; + b->yy_is_our_buffer = 0; + b->yy_input_file = 0; + b->yy_n_chars = b->yy_buf_size; + b->yy_is_interactive = 0; + b->yy_at_bol = 1; + b->yy_fill_buffer = 0; + b->yy_buffer_status = YY_BUFFER_NEW; + + rcs_switch_to_buffer(b ,yyscanner ); + + return b; +} + +/** Setup the input buffer state to scan a string. The next call to rcslex() will + * scan from a @e copy of @a str. + * @param yystr a NUL-terminated string to scan + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + * @note If you want to scan bytes that may contain NUL values, then use + * rcs_scan_bytes() instead. + */ +YY_BUFFER_STATE rcs_scan_string (yyconst char * yystr , yyscan_t yyscanner) +{ + + return rcs_scan_bytes(yystr,strlen(yystr) ,yyscanner); +} + +/** Setup the input buffer state to scan the given bytes. The next call to rcslex() will + * scan from a @e copy of @a bytes. + * @param bytes the byte buffer to scan + * @param len the number of bytes in the buffer pointed to by @a bytes. + * @param yyscanner The scanner object. + * @return the newly allocated buffer state object. + */ +YY_BUFFER_STATE rcs_scan_bytes (yyconst char * yybytes, int _yybytes_len , yyscan_t yyscanner) +{ + YY_BUFFER_STATE b; + char *buf; + yy_size_t n; + int i; + + /* Get memory for full buffer, including space for trailing EOB's. */ + n = _yybytes_len + 2; + buf = (char *) rcsalloc(n ,yyscanner ); + if ( ! buf ) + YY_FATAL_ERROR( "out of dynamic memory in rcs_scan_bytes()" ); + + for ( i = 0; i < _yybytes_len; ++i ) + buf[i] = yybytes[i]; + + buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR; + + b = rcs_scan_buffer(buf,n ,yyscanner); + if ( ! b ) + YY_FATAL_ERROR( "bad buffer in rcs_scan_bytes()" ); + + /* It's okay to grow etc. this buffer, and we should throw it + * away when we're done. + */ + b->yy_is_our_buffer = 1; + + return b; +} + +#ifndef YY_EXIT_FAILURE +#define YY_EXIT_FAILURE 2 +#endif + +static void yy_fatal_error (yyconst char* msg , yyscan_t yyscanner) +{ + (void) fprintf( stderr, "%s\n", msg ); + exit( YY_EXIT_FAILURE ); +} + +/* Redefine yyless() so it works in section 3 code. */ + +#undef yyless +#define yyless(n) \ + do \ + { \ + /* Undo effects of setting up yytext. */ \ + int yyless_macro_arg = (n); \ + YY_LESS_LINENO(yyless_macro_arg);\ + yytext[yyleng] = yyg->yy_hold_char; \ + yyg->yy_c_buf_p = yytext + yyless_macro_arg; \ + yyg->yy_hold_char = *yyg->yy_c_buf_p; \ + *yyg->yy_c_buf_p = '\0'; \ + yyleng = yyless_macro_arg; \ + } \ + while ( 0 ) + +/* Accessor methods (get/set functions) to struct members. */ + +/** Get the user-defined data for this scanner. + * @param yyscanner The scanner object. + */ +YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyextra; +} + +/** Get the current line number. + * @param yyscanner The scanner object. + */ +int rcsget_lineno (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yylineno; +} + +/** Get the current column number. + * @param yyscanner The scanner object. + */ +int rcsget_column (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + if (! YY_CURRENT_BUFFER) + return 0; + + return yycolumn; +} + +/** Get the input stream. + * @param yyscanner The scanner object. + */ +FILE *rcsget_in (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyin; +} + +/** Get the output stream. + * @param yyscanner The scanner object. + */ +FILE *rcsget_out (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyout; +} + +/** Get the length of the current token. + * @param yyscanner The scanner object. + */ +int rcsget_leng (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yyleng; +} + +/** Get the current token. + * @param yyscanner The scanner object. + */ + +char *rcsget_text (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yytext; +} + +/** Set the user-defined data. This data is never touched by the scanner. + * @param user_defined The data to be associated with this scanner. + * @param yyscanner The scanner object. + */ +void rcsset_extra (YY_EXTRA_TYPE user_defined , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyextra = user_defined ; +} + +/** Set the current line number. + * @param line_number + * @param yyscanner The scanner object. + */ +void rcsset_lineno (int line_number , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* lineno is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + yy_fatal_error( "rcsset_lineno called with no buffer" , yyscanner); + + yylineno = line_number; +} + +/** Set the current column. + * @param line_number + * @param yyscanner The scanner object. + */ +void rcsset_column (int column_no , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* column is only valid if an input buffer exists. */ + if (! YY_CURRENT_BUFFER ) + yy_fatal_error( "rcsset_column called with no buffer" , yyscanner); + + yycolumn = column_no; +} + +/** Set the input stream. This does not discard the current + * input buffer. + * @param in_str A readable stream. + * @param yyscanner The scanner object. + * @see rcs_switch_to_buffer + */ +void rcsset_in (FILE * in_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyin = in_str ; +} + +void rcsset_out (FILE * out_str , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yyout = out_str ; +} + +int rcsget_debug (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + return yy_flex_debug; +} + +void rcsset_debug (int bdebug , yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + yy_flex_debug = bdebug ; +} + +/* Accessor methods for yylval and yylloc */ + +/* User-visible API */ + +/* rcslex_init is special because it creates the scanner itself, so it is + * the ONLY reentrant function that doesn't take the scanner as the last argument. + * That's why we explicitly handle the declaration, instead of using our macros. + */ + +int rcslex_init(yyscan_t* ptr_yy_globals) + +{ + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) rcsalloc ( sizeof( struct yyguts_t ), NULL ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + return yy_init_globals ( *ptr_yy_globals ); +} + +/* rcslex_init_extra has the same functionality as rcslex_init, but follows the + * convention of taking the scanner as the last argument. Note however, that + * this is a *pointer* to a scanner, as it will be allocated by this call (and + * is the reason, too, why this function also must handle its own declaration). + * The user defined value in the first argument will be available to rcsalloc in + * the yyextra field. + */ + +int rcslex_init_extra(YY_EXTRA_TYPE yy_user_defined,yyscan_t* ptr_yy_globals ) + +{ + struct yyguts_t dummy_yyguts; + + rcsset_extra (yy_user_defined, &dummy_yyguts); + + if (ptr_yy_globals == NULL){ + errno = EINVAL; + return 1; + } + + *ptr_yy_globals = (yyscan_t) rcsalloc ( sizeof( struct yyguts_t ), &dummy_yyguts ); + + if (*ptr_yy_globals == NULL){ + errno = ENOMEM; + return 1; + } + + /* By setting to 0xAA, we expose bugs in + yy_init_globals. Leave at 0x00 for releases. */ + memset(*ptr_yy_globals,0x00,sizeof(struct yyguts_t)); + + rcsset_extra (yy_user_defined, *ptr_yy_globals); + + return yy_init_globals ( *ptr_yy_globals ); +} + +static int yy_init_globals (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + /* Initialization is the same as for the non-reentrant scanner. + * This function is called from rcslex_destroy(), so don't allocate here. + */ + + yyg->yy_buffer_stack = 0; + yyg->yy_buffer_stack_top = 0; + yyg->yy_buffer_stack_max = 0; + yyg->yy_c_buf_p = (char *) 0; + yyg->yy_init = 0; + yyg->yy_start = 0; + + yyg->yy_start_stack_ptr = 0; + yyg->yy_start_stack_depth = 0; + yyg->yy_start_stack = NULL; + +/* Defined in main.c */ +#ifdef YY_STDINIT + yyin = stdin; + yyout = stdout; +#else + yyin = (FILE *) 0; + yyout = (FILE *) 0; +#endif + + /* For future reference: Set errno on error, since we are called by + * rcslex_init() + */ + return 0; +} + +/* rcslex_destroy is for both reentrant and non-reentrant scanners. */ +int rcslex_destroy (yyscan_t yyscanner) +{ + struct yyguts_t * yyg = (struct yyguts_t*)yyscanner; + + /* Pop the buffer stack, destroying each element. */ + while(YY_CURRENT_BUFFER){ + rcs_delete_buffer(YY_CURRENT_BUFFER ,yyscanner ); + YY_CURRENT_BUFFER_LVALUE = NULL; + rcspop_buffer_state(yyscanner); + } + + /* Destroy the stack itself. */ + rcsfree(yyg->yy_buffer_stack ,yyscanner); + yyg->yy_buffer_stack = NULL; + + /* Destroy the start condition stack. */ + rcsfree(yyg->yy_start_stack ,yyscanner ); + yyg->yy_start_stack = NULL; + + /* Reset the globals. This is important in a non-reentrant scanner so the next time + * rcslex() is called, initialization will occur. */ + yy_init_globals( yyscanner); + + /* Destroy the main struct (reentrant only). */ + rcsfree ( yyscanner , yyscanner ); + yyscanner = NULL; + return 0; +} + +/* + * Internal utility routines. + */ + +#ifndef yytext_ptr +static void yy_flex_strncpy (char* s1, yyconst char * s2, int n , yyscan_t yyscanner) +{ + register int i; + for ( i = 0; i < n; ++i ) + s1[i] = s2[i]; +} +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * s , yyscan_t yyscanner) +{ + register int n; + for ( n = 0; s[n]; ++n ) + ; + + return n; +} +#endif + +void *rcsalloc (yy_size_t size , yyscan_t yyscanner) +{ + return (void *) malloc( size ); +} + +void *rcsrealloc (void * ptr, yy_size_t size , yyscan_t yyscanner) +{ + /* The cast to (char *) in the following accommodates both + * implementations that use char* generic pointers, and those + * that use void* generic pointers. It works with the latter + * because both ANSI C and C++ allow castless assignment from + * any pointer type to void*, and deal with argument conversions + * as though doing an assignment. + */ + return (void *) realloc( (char *) ptr, size ); +} + +void rcsfree (void * ptr , yyscan_t yyscanner) +{ + free( (char *) ptr ); /* see rcsrealloc() for (char *) cast */ +} + +#define YYTABLES_NAME "yytables" + +#line 73 "rcstokenizer.l" + + + diff --git a/usr.bin/csup/lister.c b/usr.bin/csup/lister.c new file mode 100644 index 0000000..b10dbd3 --- /dev/null +++ b/usr.bin/csup/lister.c @@ -0,0 +1,569 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <errno.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "attrstack.h" +#include "config.h" +#include "fattr.h" +#include "globtree.h" +#include "lister.h" +#include "misc.h" +#include "mux.h" +#include "proto.h" +#include "status.h" +#include "stream.h" + +/* Internal error codes. */ +#define LISTER_ERR_WRITE (-1) /* Error writing to server. */ +#define LISTER_ERR_STATUS (-2) /* Status file error in lstr->errmsg. */ + +struct lister { + struct config *config; + struct stream *wr; + char *errmsg; +}; + +static int lister_batch(struct lister *); +static int lister_coll(struct lister *, struct coll *, struct status *); +static int lister_dodirdown(struct lister *, struct coll *, + struct statusrec *, struct attrstack *as); +static int lister_dodirup(struct lister *, struct coll *, + struct statusrec *, struct attrstack *as); +static int lister_dofile(struct lister *, struct coll *, + struct statusrec *); +static int lister_dodead(struct lister *, struct coll *, + struct statusrec *); +static int lister_dorcsfile(struct lister *, struct coll *, + struct statusrec *); +static int lister_dorcsdead(struct lister *, struct coll *, + struct statusrec *); + +void * +lister(void *arg) +{ + struct thread_args *args; + struct lister lbuf, *l; + int error; + + args = arg; + l = &lbuf; + l->config = args->config; + l->wr = args->wr; + l->errmsg = NULL; + error = lister_batch(l); + switch (error) { + case LISTER_ERR_WRITE: + xasprintf(&args->errmsg, + "TreeList failed: Network write failure: %s", + strerror(errno)); + args->status = STATUS_TRANSIENTFAILURE; + break; + case LISTER_ERR_STATUS: + xasprintf(&args->errmsg, + "TreeList failed: %s. Delete it and try again.", + l->errmsg); + free(l->errmsg); + args->status = STATUS_FAILURE; + break; + default: + assert(error == 0); + args->status = STATUS_SUCCESS; + }; + return (NULL); +} + +static int +lister_batch(struct lister *l) +{ + struct config *config; + struct stream *wr; + struct status *st; + struct coll *coll; + int error; + + config = l->config; + wr = l->wr; + STAILQ_FOREACH(coll, &config->colls, co_next) { + if (coll->co_options & CO_SKIP) + continue; + st = status_open(coll, -1, &l->errmsg); + if (st == NULL) + return (LISTER_ERR_STATUS); + error = proto_printf(wr, "COLL %s %s\n", coll->co_name, + coll->co_release); + if (error) + return (LISTER_ERR_WRITE); + stream_flush(wr); + if (coll->co_options & CO_COMPRESS) + stream_filter_start(wr, STREAM_FILTER_ZLIB, NULL); + error = lister_coll(l, coll, st); + status_close(st, NULL); + if (error) + return (error); + if (coll->co_options & CO_COMPRESS) + stream_filter_stop(wr); + stream_flush(wr); + } + error = proto_printf(wr, ".\n"); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} + +/* List a single collection based on the status file. */ +static int +lister_coll(struct lister *l, struct coll *coll, struct status *st) +{ + struct stream *wr; + struct attrstack *as; + struct statusrec *sr; + struct fattr *fa; + size_t i; + int depth, error, ret, prunedepth; + + wr = l->wr; + depth = 0; + prunedepth = INT_MAX; + as = attrstack_new(); + while ((ret = status_get(st, NULL, 0, 0, &sr)) == 1) { + switch (sr->sr_type) { + case SR_DIRDOWN: + depth++; + if (depth < prunedepth) { + error = lister_dodirdown(l, coll, sr, as); + if (error < 0) + goto bad; + if (error) + prunedepth = depth; + } + break; + case SR_DIRUP: + if (depth < prunedepth) { + error = lister_dodirup(l, coll, sr, as); + if (error) + goto bad; + } else if (depth == prunedepth) { + /* Finished pruning. */ + prunedepth = INT_MAX; + } + depth--; + continue; + case SR_CHECKOUTLIVE: + if (depth < prunedepth) { + error = lister_dofile(l, coll, sr); + if (error) + goto bad; + } + break; + case SR_CHECKOUTDEAD: + if (depth < prunedepth) { + error = lister_dodead(l, coll, sr); + if (error) + goto bad; + } + break; + case SR_FILEDEAD: + if (depth < prunedepth) { + if (!(coll->co_options & CO_CHECKOUTMODE)) { + error = lister_dorcsdead(l, coll, sr); + if (error) + goto bad; + } + } + break; + case SR_FILELIVE: + if (depth < prunedepth) { + if (!(coll->co_options & CO_CHECKOUTMODE)) { + error = lister_dorcsfile(l, coll, sr); + if (error) + goto bad; + } + } + break; + } + } + if (ret == -1) { + l->errmsg = status_errmsg(st); + error = LISTER_ERR_STATUS; + goto bad; + } + assert(status_eof(st)); + assert(depth == 0); + error = proto_printf(wr, ".\n"); + attrstack_free(as); + if (error) + return (LISTER_ERR_WRITE); + return (0); +bad: + for (i = 0; i < attrstack_size(as); i++) { + fa = attrstack_pop(as); + fattr_free(fa); + } + attrstack_free(as); + return (error); +} + +/* Handle a directory up entry found in the status file. */ +static int +lister_dodirdown(struct lister *l, struct coll *coll, struct statusrec *sr, + struct attrstack *as) +{ + struct config *config; + struct stream *wr; + struct fattr *fa, *fa2; + char *path; + int error; + + config = l->config; + wr = l->wr; + if (!globtree_test(coll->co_dirfilter, sr->sr_file)) + return (1); + if (coll->co_options & CO_TRUSTSTATUSFILE) { + fa = fattr_new(FT_DIRECTORY, -1); + } else { + xasprintf(&path, "%s/%s", coll->co_prefix, sr->sr_file); + fa = fattr_frompath(path, FATTR_NOFOLLOW); + if (fa == NULL) { + /* The directory doesn't exist, prune + * everything below it. */ + free(path); + return (1); + } + if (fattr_type(fa) == FT_SYMLINK) { + fa2 = fattr_frompath(path, FATTR_FOLLOW); + if (fa2 != NULL && fattr_type(fa2) == FT_DIRECTORY) { + /* XXX - When not in checkout mode, CVSup warns + * here about the file being a symlink to a + * directory instead of a directory. */ + fattr_free(fa); + fa = fa2; + } else { + fattr_free(fa2); + } + } + free(path); + } + + if (fattr_type(fa) != FT_DIRECTORY) { + fattr_free(fa); + /* Report it as something bogus so + * that it will be replaced. */ + error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), + fattr_bogus, config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (1); + } + + /* It really is a directory. */ + attrstack_push(as, fa); + error = proto_printf(wr, "D %s\n", pathlast(sr->sr_file)); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} + +/* Handle a directory up entry found in the status file. */ +static int +lister_dodirup(struct lister *l, struct coll *coll, struct statusrec *sr, + struct attrstack *as) +{ + struct config *config; + const struct fattr *sendattr; + struct stream *wr; + struct fattr *fa, *fa2; + int error; + + config = l->config; + wr = l->wr; + fa = attrstack_pop(as); + if (coll->co_options & CO_TRUSTSTATUSFILE) { + fattr_free(fa); + fa = sr->sr_clientattr; + } + + fa2 = sr->sr_clientattr; + if (fattr_equal(fa, fa2)) + sendattr = fa; + else + sendattr = fattr_bogus; + error = proto_printf(wr, "U %F\n", sendattr, config->fasupport, + coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + if (!(coll->co_options & CO_TRUSTSTATUSFILE)) + fattr_free(fa); + /* XXX CVSup flushes here for some reason with a comment saying + "Be smarter". We don't flush when listing other file types. */ + stream_flush(wr); + return (0); +} + +/* Handle a checkout live entry found in the status file. */ +static int +lister_dofile(struct lister *l, struct coll *coll, struct statusrec *sr) +{ + struct config *config; + struct stream *wr; + const struct fattr *sendattr, *fa; + struct fattr *fa2, *rfa; + char *path, *spath; + int error; + + if (!globtree_test(coll->co_filefilter, sr->sr_file)) + return (0); + config = l->config; + wr = l->wr; + rfa = NULL; + sendattr = NULL; + error = 0; + if (!(coll->co_options & CO_TRUSTSTATUSFILE)) { + path = checkoutpath(coll->co_prefix, sr->sr_file); + if (path == NULL) { + spath = coll_statuspath(coll); + xasprintf(&l->errmsg, "Error in \"%s\": " + "Invalid filename \"%s\"", spath, sr->sr_file); + free(spath); + return (LISTER_ERR_STATUS); + } + rfa = fattr_frompath(path, FATTR_NOFOLLOW); + free(path); + if (rfa == NULL) { + /* + * According to the checkouts file we should have + * this file but we don't. Maybe the user deleted + * the file, or maybe the checkouts file is wrong. + * List the file with bogus attributes to cause the + * server to get things back in sync again. + */ + sendattr = fattr_bogus; + goto send; + } + fa = rfa; + } else { + fa = sr->sr_clientattr; + } + fa2 = fattr_forcheckout(sr->sr_serverattr, coll->co_umask); + if (!fattr_equal(fa, sr->sr_clientattr) || !fattr_equal(fa, fa2) || + strcmp(coll->co_tag, sr->sr_tag) != 0 || + strcmp(coll->co_date, sr->sr_date) != 0) { + /* + * The file corresponds to the information we have + * recorded about it, and its moded is correct for + * the requested umask setting. + */ + sendattr = fattr_bogus; + } else { + /* + * Either the file has been touched, or we are asking + * for a different revision than the one we recorded + * information about, or its mode isn't right (because + * it was last updated using a version of CVSup that + * wasn't so strict about modes). + */ + sendattr = sr->sr_serverattr; + } + fattr_free(fa2); + if (rfa != NULL) + fattr_free(rfa); +send: + error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr, + config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} + +/* Handle a rcs file live entry found in the status file. */ +static int +lister_dorcsfile(struct lister *l, struct coll *coll, struct statusrec *sr) +{ + struct config *config; + struct stream *wr; + const struct fattr *sendattr; + struct fattr *fa; + char *path, *spath; + size_t len; + int error; + + if (!globtree_test(coll->co_filefilter, sr->sr_file)) + return (0); + config = l->config; + wr = l->wr; + if (!(coll->co_options & CO_TRUSTSTATUSFILE)) { + path = cvspath(coll->co_prefix, sr->sr_file, 0); + if (path == NULL) { + spath = coll_statuspath(coll); + xasprintf(&l->errmsg, "Error in \"%s\": " + "Invalid filename \"%s\"", spath, sr->sr_file); + free(spath); + return (LISTER_ERR_STATUS); + } + fa = fattr_frompath(path, FATTR_NOFOLLOW); + free(path); + } else + fa = sr->sr_clientattr; + if (fa != NULL && fattr_equal(fa, sr->sr_clientattr)) { + /* + * If the file is an RCS file, we use "loose" equality, so sizes + * may disagress because of differences in whitespace. + */ + if (isrcs(sr->sr_file, &len) && + !(coll->co_options & CO_NORCS) && + !(coll->co_options & CO_STRICTCHECKRCS)) { + fattr_maskout(fa, FA_SIZE); + } + sendattr = fa; + } else { + /* + * If different, the user may have changed it, so we report + * bogus attributes to force a full comparison. + */ + sendattr = fattr_bogus; + } + error = proto_printf(wr, "F %s %F\n", pathlast(sr->sr_file), sendattr, + config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} + +/* Handle a checkout dead entry found in the status file. */ +static int +lister_dodead(struct lister *l, struct coll *coll, struct statusrec *sr) +{ + struct config *config; + struct stream *wr; + const struct fattr *sendattr; + struct fattr *fa; + char *path, *spath; + int error; + + if (!globtree_test(coll->co_filefilter, sr->sr_file)) + return (0); + config = l->config; + wr = l->wr; + if (!(coll->co_options & CO_TRUSTSTATUSFILE)) { + path = checkoutpath(coll->co_prefix, sr->sr_file); + if (path == NULL) { + spath = coll_statuspath(coll); + xasprintf(&l->errmsg, "Error in \"%s\": " + "Invalid filename \"%s\"", spath, sr->sr_file); + free(spath); + return (LISTER_ERR_STATUS); + } + fa = fattr_frompath(path, FATTR_NOFOLLOW); + free(path); + if (fa != NULL && fattr_type(fa) != FT_DIRECTORY) { + /* + * We shouldn't have this file but we do. Report + * it to the server, which will either send a + * deletion request, of (if the file has come alive) + * sent the correct version. + */ + fattr_free(fa); + error = proto_printf(wr, "F %s %F\n", + pathlast(sr->sr_file), fattr_bogus, + config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (0); + } + fattr_free(fa); + } + if (strcmp(coll->co_tag, sr->sr_tag) != 0 || + strcmp(coll->co_date, sr->sr_date) != 0) + sendattr = fattr_bogus; + else + sendattr = sr->sr_serverattr; + error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr, + config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} + +/* Handle a rcs file dead entry found in the status file. */ +static int +lister_dorcsdead(struct lister *l, struct coll *coll, struct statusrec *sr) +{ + struct config *config; + struct stream *wr; + const struct fattr *sendattr; + struct fattr *fa; + char *path, *spath; + size_t len; + int error; + + if (!globtree_test(coll->co_filefilter, sr->sr_file)) + return (0); + config = l->config; + wr = l->wr; + if (!coll->co_options & CO_TRUSTSTATUSFILE) { + path = cvspath(coll->co_prefix, sr->sr_file, 1); + if (path == NULL) { + spath = coll_statuspath(coll); + xasprintf(&l->errmsg, "Error in \"%s\": " + "Invalid filename \"%s\"", spath, sr->sr_file); + free(spath); + return (LISTER_ERR_STATUS); + } + fa = fattr_frompath(path, FATTR_NOFOLLOW); + free(path); + } else + fa = sr->sr_clientattr; + if (fattr_equal(fa, sr->sr_clientattr)) { + /* + * If the file is an RCS file, we use "loose" equality, so sizes + * may disagress because of differences in whitespace. + */ + if (isrcs(sr->sr_file, &len) && + !(coll->co_options & CO_NORCS) && + !(coll->co_options & CO_STRICTCHECKRCS)) { + fattr_maskout(fa, FA_SIZE); + } + sendattr = fa; + } else { + /* + * If different, the user may have changed it, so we report + * bogus attributes to force a full comparison. + */ + sendattr = fattr_bogus; + } + error = proto_printf(wr, "f %s %F\n", pathlast(sr->sr_file), sendattr, + config->fasupport, coll->co_attrignore); + if (error) + return (LISTER_ERR_WRITE); + return (0); +} diff --git a/usr.bin/csup/lister.h b/usr.bin/csup/lister.h new file mode 100644 index 0000000..a0a9bbe --- /dev/null +++ b/usr.bin/csup/lister.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _LISTER_H_ +#define _LISTER_H_ + +void *lister(void *); + +#endif /* !_LISTER_H_ */ diff --git a/usr.bin/csup/main.c b/usr.bin/csup/main.c new file mode 100644 index 0000000..e7711b3 --- /dev/null +++ b/usr.bin/csup/main.c @@ -0,0 +1,347 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/file.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <errno.h> +#include <fcntl.h> +#include <libgen.h> +#include <netdb.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "config.h" +#include "fattr.h" +#include "misc.h" +#include "proto.h" +#include "stream.h" + +#define USAGE_OPTFMT " %-12s %s\n" +#define USAGE_OPTFMTSUB " %-14s %s\n", "" + +int verbose = 1; + +static void +usage(char *argv0) +{ + + lprintf(-1, "Usage: %s [options] supfile\n", basename(argv0)); + lprintf(-1, " Options:\n"); + lprintf(-1, USAGE_OPTFMT, "-1", "Don't retry automatically on failure " + "(same as \"-r 0\")"); + lprintf(-1, USAGE_OPTFMT, "-4", "Force usage of IPv4 addresses"); + lprintf(-1, USAGE_OPTFMT, "-6", "Force usage of IPv6 addresses"); + lprintf(-1, USAGE_OPTFMT, "-a", + "Require server to authenticate itself to us"); + lprintf(-1, USAGE_OPTFMT, "-A addr", + "Bind local socket to a specific address"); + lprintf(-1, USAGE_OPTFMT, "-b base", + "Override supfile's \"base\" directory"); + lprintf(-1, USAGE_OPTFMT, "-c collDir", + "Subdirectory of \"base\" for collections (default \"sup\")"); + lprintf(-1, USAGE_OPTFMT, "-d delLimit", + "Allow at most \"delLimit\" file deletions (default unlimited)"); + lprintf(-1, USAGE_OPTFMT, "-h host", + "Override supfile's \"host\" name"); + lprintf(-1, USAGE_OPTFMT, "-i pattern", + "Include only files/directories matching pattern."); + lprintf(-1, USAGE_OPTFMTSUB, + "May be repeated for an OR operation. Default is"); + lprintf(-1, USAGE_OPTFMTSUB, "to include each entire collection."); + lprintf(-1, USAGE_OPTFMT, "-k", + "Keep bad temporary files when fixups are required"); + lprintf(-1, USAGE_OPTFMT, "-l lockfile", + "Lock file during update; fail if already locked"); + lprintf(-1, USAGE_OPTFMT, "-L n", + "Verbosity level (0..2, default 1)"); + lprintf(-1, USAGE_OPTFMT, "-p port", + "Alternate server port (default 5999)"); + lprintf(-1, USAGE_OPTFMT, "-r n", + "Maximum retries on transient errors (default unlimited)"); + lprintf(-1, USAGE_OPTFMT, "-s", + "Don't stat client files; trust the checkouts file"); + lprintf(-1, USAGE_OPTFMT, "-v", "Print version and exit"); + lprintf(-1, USAGE_OPTFMT, "-z", "Enable compression for all " + "collections"); + lprintf(-1, USAGE_OPTFMT, "-Z", "Disable compression for all " + "collections"); +} + +int +main(int argc, char *argv[]) +{ + struct tm tm; + struct backoff_timer *timer; + struct config *config; + struct coll *override; + struct addrinfo *res; + struct sockaddr *laddr; + socklen_t laddrlen; + struct stream *lock; + char *argv0, *file, *lockfile; + int family, error, lockfd, lflag, overridemask; + int c, i, deletelim, port, retries, status, reqauth; + time_t nexttry; + + error = 0; + family = PF_UNSPEC; + deletelim = -1; + port = 0; + lflag = 0; + lockfd = 0; + nexttry = 0; + retries = -1; + argv0 = argv[0]; + laddr = NULL; + laddrlen = 0; + lockfile = NULL; + override = coll_new(NULL); + overridemask = 0; + reqauth = 0; + + while ((c = getopt(argc, argv, + "146aA:b:c:d:gh:i:kl:L:p:P:r:svzZ")) != -1) { + switch (c) { + case '1': + retries = 0; + break; + case '4': + family = AF_INET; + break; + case '6': + family = AF_INET6; + break; + case 'a': + /* Require server authentication */ + reqauth = 1; + break; + case 'A': + error = getaddrinfo(optarg, NULL, NULL, &res); + if (error) { + lprintf(-1, "%s: %s\n", optarg, + gai_strerror(error)); + return (1); + } + laddrlen = res->ai_addrlen; + laddr = xmalloc(laddrlen); + memcpy(laddr, res->ai_addr, laddrlen); + freeaddrinfo(res); + break; + case 'b': + if (override->co_base != NULL) + free(override->co_base); + override->co_base = xstrdup(optarg); + break; + case 'c': + override->co_colldir = optarg; + break; + case 'd': + error = asciitoint(optarg, &deletelim, 0); + if (error || deletelim < 0) { + lprintf(-1, "Invalid deletion limit\n"); + usage(argv0); + return (1); + } + break; + case 'g': + /* For compatibility. */ + break; + case 'h': + if (override->co_host != NULL) + free(override->co_host); + override->co_host = xstrdup(optarg); + break; + case 'i': + pattlist_add(override->co_accepts, optarg); + break; + case 'k': + override->co_options |= CO_KEEPBADFILES; + overridemask |= CO_KEEPBADFILES; + break; + case 'l': + lockfile = optarg; + lflag = 1; + lockfd = open(lockfile, + O_CREAT | O_WRONLY | O_TRUNC, 0700); + if (lockfd != -1) { + error = flock(lockfd, LOCK_EX | LOCK_NB); + if (error == -1 && errno == EWOULDBLOCK) { + if (lockfd != -1) + close(lockfd); + lprintf(-1, "\"%s\" is already locked " + "by another process\n", lockfile); + return (1); + } + } + if (lockfd == -1 || error == -1) { + if (lockfd != -1) + close(lockfd); + lprintf(-1, "Error locking \"%s\": %s\n", + lockfile, strerror(errno)); + return (1); + } + lock = stream_open_fd(lockfd, + NULL, stream_write_fd, NULL); + (void)stream_printf(lock, "%10ld\n", (long)getpid()); + stream_close(lock); + break; + case 'L': + error = asciitoint(optarg, &verbose, 0); + if (error) { + lprintf(-1, "Invalid verbosity\n"); + usage(argv0); + return (1); + } + break; + case 'p': + /* Use specified server port. */ + error = asciitoint(optarg, &port, 0); + if (error) { + lprintf(-1, "Invalid server port\n"); + usage(argv0); + return (1); + } + if (port <= 0 || port >= 65536) { + lprintf(-1, "Invalid port %d\n", port); + return (1); + } + if (port < 1024) { + lprintf(-1, "Reserved port %d not permitted\n", + port); + return (1); + } + break; + case 'P': + /* For compatibility. */ + if (strcmp(optarg, "m") != 0) { + lprintf(-1, + "Client only supports multiplexed mode\n"); + return (1); + } + break; + case 'r': + error = asciitoint(optarg, &retries, 0); + if (error || retries < 0) { + lprintf(-1, "Invalid retry limit\n"); + usage(argv0); + return (1); + } + break; + case 's': + override->co_options |= CO_TRUSTSTATUSFILE; + overridemask |= CO_TRUSTSTATUSFILE; + break; + case 'v': + lprintf(0, "CVSup client written in C\n"); + lprintf(0, "Software version: %s\n", PROTO_SWVER); + lprintf(0, "Protocol version: %d.%d\n", + PROTO_MAJ, PROTO_MIN); + lprintf(0, "http://mu.org/~mux/csup.html\n"); + return (0); + break; + case 'z': + /* Force compression on all collections. */ + override->co_options |= CO_COMPRESS; + overridemask |= CO_COMPRESS; + break; + case 'Z': + /* Disables compression on all collections. */ + override->co_options &= ~CO_COMPRESS; + overridemask &= ~CO_COMPRESS; + break; + case '?': + default: + usage(argv0); + return (1); + } + } + + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(argv0); + return (1); + } + + file = argv[0]; + lprintf(2, "Parsing supfile \"%s\"\n", file); + config = config_init(file, override, overridemask); + coll_free(override); + if (config == NULL) + return (1); + + if (config_checkcolls(config) == 0) { + lprintf(-1, "No collections selected\n"); + return (1); + } + + if (laddr != NULL) { + config->laddr = laddr; + config->laddrlen = laddrlen; + } + config->deletelim = deletelim; + config->reqauth = reqauth; + lprintf(2, "Connecting to %s\n", config->host); + + i = 0; + fattr_init(); /* Initialize the fattr API. */ + timer = bt_new(300, 7200, 2.0, 0.1); + for (;;) { + status = proto_connect(config, family, port); + if (status == STATUS_SUCCESS) { + status = proto_run(config); + if (status != STATUS_TRANSIENTFAILURE) + break; + } + if (retries >= 0 && i >= retries) + break; + nexttry = time(0) + bt_get(timer); + localtime_r(&nexttry, &tm); + lprintf(1, "Will retry at %02d:%02d:%02d\n", + tm.tm_hour, tm.tm_min, tm.tm_sec); + bt_pause(timer); + lprintf(1, "Retrying\n"); + i++; + } + bt_free(timer); + fattr_fini(); + if (lflag) { + unlink(lockfile); + flock(lockfd, LOCK_UN); + close(lockfd); + } + config_free(config); + if (status != STATUS_SUCCESS) + return (1); + return (0); +} diff --git a/usr.bin/csup/main.h b/usr.bin/csup/main.h new file mode 100644 index 0000000..217c7eb --- /dev/null +++ b/usr.bin/csup/main.h @@ -0,0 +1,29 @@ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $Id$ + */ + +extern int verbose; diff --git a/usr.bin/csup/misc.c b/usr.bin/csup/misc.c new file mode 100644 index 0000000..ae16b59 --- /dev/null +++ b/usr.bin/csup/misc.c @@ -0,0 +1,645 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <openssl/md5.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#include "fattr.h" +#include "main.h" +#include "misc.h" + +struct pattlist { + char **patterns; + size_t size; + size_t in; +}; + +struct backoff_timer { + time_t min; + time_t max; + time_t interval; + float backoff; + float jitter; +}; + +static void bt_update(struct backoff_timer *); +static void bt_addjitter(struct backoff_timer *); + +int +asciitoint(const char *s, int *val, int base) +{ + char *end; + long longval; + + errno = 0; + longval = strtol(s, &end, base); + if (errno || *end != '\0') + return (-1); + if (longval > INT_MAX || longval < INT_MIN) { + errno = ERANGE; + return (-1); + } + *val = longval; + return (0); +} + +int +lprintf(int level, const char *fmt, ...) +{ + FILE *to; + va_list ap; + int ret; + + if (level > verbose) + return (0); + if (level == -1) + to = stderr; + else + to = stdout; + va_start(ap, fmt); + ret = vfprintf(to, fmt, ap); + va_end(ap); + fflush(to); + return (ret); +} + +/* + * Compute the MD5 checksum of a file. The md parameter must + * point to a buffer containing at least MD5_DIGEST_SIZE bytes. + * + * Do not confuse OpenSSL's MD5_DIGEST_LENGTH with our own + * MD5_DIGEST_SIZE macro. + */ +int +MD5_File(char *path, char *md) +{ + char buf[1024]; + MD5_CTX ctx; + ssize_t n; + int fd; + + fd = open(path, O_RDONLY); + if (fd == -1) + return (-1); + MD5_Init(&ctx); + while ((n = read(fd, buf, sizeof(buf))) > 0) + MD5_Update(&ctx, buf, n); + close(fd); + if (n == -1) + return (-1); + MD5_End(md, &ctx); + return (0); +} + +/* + * Wrapper around MD5_Final() that converts the 128 bits MD5 hash + * to an ASCII string representing this value in hexadecimal. + */ +void +MD5_End(char *md, MD5_CTX *c) +{ + unsigned char md5[MD5_DIGEST_LENGTH]; + const char hex[] = "0123456789abcdef"; + int i, j; + + MD5_Final(md5, c); + j = 0; + for (i = 0; i < MD5_DIGEST_LENGTH; i++) { + md[j++] = hex[md5[i] >> 4]; + md[j++] = hex[md5[i] & 0xf]; + } + md[j] = '\0'; +} + +int +pathcmp(const char *s1, const char *s2) +{ + char c1, c2; + + do { + c1 = *s1++; + if (c1 == '/') + c1 = 1; + c2 = *s2++; + if (c2 == '/') + c2 = 1; + } while (c1 == c2 && c1 != '\0'); + + return (c1 - c2); +} + +size_t +commonpathlength(const char *a, size_t alen, const char *b, size_t blen) +{ + size_t i, minlen, lastslash; + + minlen = min(alen, blen); + lastslash = 0; + for (i = 0; i < minlen; i++) { + if (a[i] != b[i]) + return (lastslash); + if (a[i] == '/') { + if (i == 0) /* Include the leading slash. */ + lastslash = 1; + else + lastslash = i; + } + } + + /* One path is a prefix of the other/ */ + if (alen > minlen) { /* Path "b" is a prefix of "a". */ + if (a[minlen] == '/') + return (minlen); + else + return (lastslash); + } else if (blen > minlen) { /* Path "a" is a prefix of "b". */ + if (b[minlen] == '/') + return (minlen); + else + return (lastslash); + } + + /* The paths are identical. */ + return (minlen); +} + +const char * +pathlast(const char *path) +{ + const char *s; + + s = strrchr(path, '/'); + if (s == NULL) + return (path); + return (++s); +} + +int +rcsdatetotm(const char *revdate, struct tm *tm) +{ + char *cp; + size_t len; + + cp = strchr(revdate, '.'); + if (cp == NULL) + return (-1); + len = cp - revdate; + if (len >= 4) + cp = strptime(revdate, "%Y.%m.%d.%H.%M.%S", tm); + else if (len == 2) + cp = strptime(revdate, "%y.%m.%d.%H.%M.%S", tm); + else + return (-1); + if (cp == NULL || *cp != '\0') + return (-1); + return (0); +} + +time_t +rcsdatetotime(const char *revdate) +{ + struct tm tm; + time_t t; + int error; + + error = rcsdatetotm(revdate, &tm); + if (error) + return (error); + t = timegm(&tm); + return (t); +} + +/* + * Checks if a file is an RCS file. + */ +int +isrcs(const char *file, size_t *len) +{ + const char *cp; + + if (file[0] == '/') + return (0); + cp = file; + while ((cp = strstr(cp, "..")) != NULL) { + if (cp == file || cp[2] == '\0' || + (cp[-1] == '/' && cp[2] == '/')) + return (0); + cp += 2; + } + *len = strlen(file); + if (*len < 2 || file[*len - 1] != 'v' || file[*len - 2] != ',') { + return (0); + } + + return (1); +} + +/* + * Returns a buffer allocated with malloc() containing the absolute + * pathname to the checkout file made from the prefix and the path + * of the corresponding RCS file relatively to the prefix. If the + * filename is not an RCS filename, NULL will be returned. + */ +char * +checkoutpath(const char *prefix, const char *file) +{ + char *path; + size_t len; + + if (!isrcs(file, &len)) + return (NULL); + xasprintf(&path, "%s/%.*s", prefix, (int)len - 2, file); + return (path); +} + +/* + * Returns a cvs path allocated with malloc() containing absolute pathname to a + * file in cvs mode which can reside in the attic. XXX: filename has really no + * restrictions. + */ +char * +cvspath(const char *prefix, const char *file, int attic) +{ + const char *last; + char *path; + + last = pathlast(file); + if (attic) + xasprintf(&path, "%s/%.*sAttic/%s", prefix, (int)(last - file), + file, last); + else + xasprintf(&path, "%s/%s", prefix, file); + + return (path); +} + +/* + * Regular or attic path if regular fails. + * XXX: This should perhaps also check if the Attic file exists too, and return + * NULL if not. + */ +char * +atticpath(const char *prefix, const char *file) +{ + char *path; + + path = cvspath(prefix, file, 0); + if (access(path, F_OK) != 0) { + free(path); + path = cvspath(prefix, file, 1); + } + return (path); +} + +int +mkdirhier(char *path, mode_t mask) +{ + struct fattr *fa; + size_t i, last, len; + int error, finish, rv; + + finish = 0; + last = 0; + len = strlen(path); + for (i = len - 1; i > 0; i--) { + if (path[i] == '/') { + path[i] = '\0'; + if (access(path, F_OK) == 0) { + path[i] = '/'; + break; + } + if (errno != ENOENT) { + path[i] = '/'; + if (last == 0) + return (-1); + finish = 1; + break; + } + last = i; + } + } + if (last == 0) + return (0); + + i = strlen(path); + fa = fattr_new(FT_DIRECTORY, -1); + fattr_mergedefault(fa); + fattr_umask(fa, mask); + while (i < len) { + if (!finish) { + rv = 0; + error = fattr_makenode(fa, path); + if (!error) + rv = fattr_install(fa, path, NULL); + if (error || rv == -1) + finish = 1; + } + path[i] = '/'; + i += strlen(path + i); + } + assert(i == len); + if (finish) + return (-1); + return (0); +} + +/* + * Compute temporary pathnames. + * This can look a bit like overkill but we mimic CVSup's behaviour. + */ +#define TEMPNAME_PREFIX "#cvs.csup" + +static pthread_mutex_t tempname_mtx = PTHREAD_MUTEX_INITIALIZER; +static pid_t tempname_pid = -1; +static int tempname_count; + +char * +tempname(const char *path) +{ + char *cp, *temp; + int count, error; + + error = pthread_mutex_lock(&tempname_mtx); + assert(!error); + if (tempname_pid == -1) { + tempname_pid = getpid(); + tempname_count = 0; + } + count = tempname_count++; + error = pthread_mutex_unlock(&tempname_mtx); + assert(!error); + cp = strrchr(path, '/'); + if (cp == NULL) + xasprintf(&temp, "%s-%ld.%d", TEMPNAME_PREFIX, + (long)tempname_pid, count); + else + xasprintf(&temp, "%.*s%s-%ld.%d", (int)(cp - path + 1), path, + TEMPNAME_PREFIX, (long)tempname_pid, count); + return (temp); +} + +void * +xmalloc(size_t size) +{ + void *buf; + + buf = malloc(size); + if (buf == NULL) + err(1, "malloc"); + return (buf); +} + +void * +xrealloc(void *buf, size_t size) +{ + + buf = realloc(buf, size); + if (buf == NULL) + err(1, "realloc"); + return (buf); +} + +char * +xstrdup(const char *str) +{ + char *buf; + + buf = strdup(str); + if (buf == NULL) + err(1, "strdup"); + return (buf); +} + +int +xasprintf(char **ret, const char *format, ...) +{ + va_list ap; + int rv; + + va_start(ap, format); + rv = vasprintf(ret, format, ap); + va_end(ap); + if (*ret == NULL) + err(1, "asprintf"); + return (rv); +} + +struct pattlist * +pattlist_new(void) +{ + struct pattlist *p; + + p = xmalloc(sizeof(struct pattlist)); + p->size = 4; /* Initial size. */ + p->patterns = xmalloc(p->size * sizeof(char *)); + p->in = 0; + return (p); +} + +void +pattlist_add(struct pattlist *p, const char *pattern) +{ + + if (p->in == p->size) { + p->size *= 2; + p->patterns = xrealloc(p->patterns, p->size * sizeof(char *)); + } + assert(p->in < p->size); + p->patterns[p->in++] = xstrdup(pattern); +} + +char * +pattlist_get(struct pattlist *p, size_t i) +{ + + assert(i < p->in); + return (p->patterns[i]); +} + +size_t +pattlist_size(struct pattlist *p) +{ + + return (p->in); +} + +void +pattlist_free(struct pattlist *p) +{ + size_t i; + + for (i = 0; i < p->in; i++) + free(p->patterns[i]); + free(p->patterns); + free(p); +} + +/* Creates a backoff timer. */ +struct backoff_timer * +bt_new(time_t min, time_t max, float backoff, float jitter) +{ + struct backoff_timer *bt; + + bt = xmalloc(sizeof(struct backoff_timer)); + bt->min = min; + bt->max = max; + bt->backoff = backoff; + bt->jitter = jitter; + bt->interval = min; + bt_addjitter(bt); + srandom(time(0)); + return (bt); +} + +/* Updates the backoff timer. */ +static void +bt_update(struct backoff_timer *bt) +{ + + bt->interval = (time_t)min(bt->interval * bt->backoff, bt->max); + bt_addjitter(bt); +} + +/* Adds some jitter. */ +static void +bt_addjitter(struct backoff_timer *bt) +{ + long mag; + + mag = (long)(bt->jitter * bt->interval); + /* We want a random number between -mag and mag. */ + bt->interval += (time_t)(random() % (2 * mag) - mag); +} + +/* Returns the current timer value. */ +time_t +bt_get(struct backoff_timer *bt) +{ + + return (bt->interval); +} + +/* Times out for bt->interval seconds. */ +void +bt_pause(struct backoff_timer *bt) +{ + + sleep(bt->interval); + bt_update(bt); +} + +void +bt_free(struct backoff_timer *bt) +{ + + free(bt); +} + +/* Compare two revisions. */ +int +rcsnum_cmp(char *revision1, char *revision2) +{ + char *ptr1, *ptr2, *dot1, *dot2; + int num1len, num2len, ret; + + ptr1 = revision1; + ptr2 = revision2; + while (*ptr1 != '\0' && *ptr2 != '\0') { + dot1 = strchr(ptr1, '.'); + dot2 = strchr(ptr2, '.'); + if (dot1 == NULL) + dot1 = strchr(ptr1, '\0'); + if (dot2 == NULL) + dot2 = strchr(ptr2, '\0'); + + num1len = dot1 - ptr1; + num2len = dot2 - ptr2; + /* Check the distance between each, showing how many digits */ + if (num1len > num2len) + return (1); + else if (num1len < num2len) + return (-1); + + /* Equal distance means we must check each character. */ + ret = strncmp(ptr1, ptr2, num1len); + if (ret != 0) + return (ret); + ptr1 = (*dot1 == '.') ? (dot1 + 1) : dot1; + ptr2 = (*dot2 == '.') ? (dot2 + 1) : dot2; + } + + if (*ptr1 != '\0' && *ptr2 == '\0') + return (1); + if (*ptr1 == '\0' && *ptr2 != '\0') + return (-1); + return (0); + +} + +/* Returns 0 if a rcsrev is not a trunk revision number. */ +int +rcsrev_istrunk(char *revnum) +{ + char *tmp; + + tmp = strchr(revnum, '.'); + tmp++; + if (strchr(tmp, '.') != NULL) + return (0); + return (1); +} + +/* Return prefix of rcsfile. */ +char * +rcsrev_prefix(char *revnum) +{ + char *modrev, *pos; + + modrev = xstrdup(revnum); + pos = strrchr(modrev, '.'); + if (pos == NULL) { + free(modrev); + return (NULL); + } + *pos = '\0'; + return (modrev); +} diff --git a/usr.bin/csup/misc.h b/usr.bin/csup/misc.h new file mode 100644 index 0000000..a7ca3a6 --- /dev/null +++ b/usr.bin/csup/misc.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _MISC_H_ +#define _MISC_H_ + +#include <openssl/md5.h> + +#include <sys/types.h> + +/* If we're not compiling in a C99 environment, define the C99 types. */ +#if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901 + +#ifdef uint32_t +#undef uint32_t +#endif +#define uint32_t u_int32_t + +#ifdef uint16_t +#undef uint16_t +#endif +#define uint16_t u_int16_t + +#ifdef uint8_t +#undef uint8_t +#endif +#define uint8_t u_int8_t + +#else +#include <stdint.h> +#endif + +/* This is a GCC-specific keyword but some other compilers (namely icc) + understand it, and the code won't work if we can't disable padding + anyways. */ +#undef __packed +#define __packed __attribute__((__packed__)) + +/* We explicitely don't define this with icc because it defines __GNUC__ + but doesn't support it. */ +#undef __printflike +#if defined(__GNUC__) && !defined(__INTEL_COMPILER) && \ + (__GNUC__ > 2 || __GNUC__ == 2 && __GNUC__MINOR__ >= 7) +#define __printflike(fmtarg, firstvararg) \ + __attribute__((__format__ (__printf__, fmtarg, firstvararg))) +#else +#define __printflike(fmtarg, firstvararg) +#endif + +/* Exit codes. */ +#define STATUS_SUCCESS 0 +#define STATUS_FAILURE 1 +#define STATUS_TRANSIENTFAILURE 2 +#define STATUS_INTERRUPTED 3 + +struct config; +struct stream; + +/* Thread parameters. */ +struct thread_args { + struct config *config; + struct stream *rd; + struct stream *wr; + int status; + char *errmsg; +}; + +/* Minimum size for MD5_File() and MD5_End() buffers. */ +#define MD5_DIGEST_SIZE 33 + +#define min(a, b) ((a) > (b) ? (b) : (a)) +#define max(a, b) ((a) < (b) ? (b) : (a)) + +struct backoff_timer; +struct pattlist; +struct tm; + +int asciitoint(const char *, int *, int); +int lprintf(int, const char *, ...) __printflike(2, 3); +int MD5_File(char *, char *); +void MD5_End(char *, MD5_CTX *); +int rcsdatetotm(const char *, struct tm *); +time_t rcsdatetotime(const char *); +int pathcmp(const char *, const char *); +size_t commonpathlength(const char *, size_t, const char *, size_t); +const char *pathlast(const char *); +int isrcs(const char *, size_t *); +char *checkoutpath(const char *, const char *); +char *cvspath(const char *, const char *, int); +char *atticpath(const char *, const char *); +char *path_prefix(char *); +char *path_first(char *); +int mkdirhier(char *, mode_t); +char *tempname(const char *); +void *xmalloc(size_t); +void *xrealloc(void *, size_t); +char *xstrdup(const char *); +int xasprintf(char **, const char *, ...) __printflike(2, 3); +int rcsnum_cmp(char *, char *); +int rcsrev_istrunk(char *); +char *rcsrev_prefix(char *); + +struct pattlist *pattlist_new(void); +void pattlist_add(struct pattlist *, const char *); +char *pattlist_get(struct pattlist *, size_t); +size_t pattlist_size(struct pattlist *); +void pattlist_free(struct pattlist *); + +struct backoff_timer *bt_new(time_t, time_t, float, float); +time_t bt_get(struct backoff_timer *); +void bt_pause(struct backoff_timer *); +void bt_free(struct backoff_timer *); + +#endif /* !_MISC_H_ */ diff --git a/usr.bin/csup/mux.c b/usr.bin/csup/mux.c new file mode 100644 index 0000000..b344be1 --- /dev/null +++ b/usr.bin/csup/mux.c @@ -0,0 +1,1202 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/socket.h> +#include <sys/uio.h> + +#include <netinet/in.h> + +#include <assert.h> +#include <errno.h> +#include <pthread.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "misc.h" +#include "mux.h" + +/* + * Packet types. + */ +#define MUX_STARTUPREQ 0 +#define MUX_STARTUPREP 1 +#define MUX_CONNECT 2 +#define MUX_ACCEPT 3 +#define MUX_RESET 4 +#define MUX_DATA 5 +#define MUX_WINDOW 6 +#define MUX_CLOSE 7 + +/* + * Header sizes. + */ +#define MUX_STARTUPHDRSZ 3 +#define MUX_CONNECTHDRSZ 8 +#define MUX_ACCEPTHDRSZ 8 +#define MUX_RESETHDRSZ 2 +#define MUX_DATAHDRSZ 4 +#define MUX_WINDOWHDRSZ 6 +#define MUX_CLOSEHDRSZ 2 + +#define MUX_PROTOVER 0 /* Protocol version. */ + +struct mux_header { + uint8_t type; + union { + struct { + uint16_t version; + } __packed mh_startup; + struct { + uint8_t id; + uint16_t mss; + uint32_t window; + } __packed mh_connect; + struct { + uint8_t id; + uint16_t mss; + uint32_t window; + } __packed mh_accept; + struct { + uint8_t id; + } __packed mh_reset; + struct { + uint8_t id; + uint16_t len; + } __packed mh_data; + struct { + uint8_t id; + uint32_t window; + } __packed mh_window; + struct { + uint8_t id; + } __packed mh_close; + } mh_u; +} __packed; + +#define mh_startup mh_u.mh_startup +#define mh_connect mh_u.mh_connect +#define mh_accept mh_u.mh_accept +#define mh_reset mh_u.mh_reset +#define mh_data mh_u.mh_data +#define mh_window mh_u.mh_window +#define mh_close mh_u.mh_close + +#define MUX_MAXCHAN 2 + +/* Channel states. */ +#define CS_UNUSED 0 +#define CS_LISTENING 1 +#define CS_CONNECTING 2 +#define CS_ESTABLISHED 3 +#define CS_RDCLOSED 4 +#define CS_WRCLOSED 5 +#define CS_CLOSED 6 + +/* Channel flags. */ +#define CF_CONNECT 0x01 +#define CF_ACCEPT 0x02 +#define CF_RESET 0x04 +#define CF_WINDOW 0x08 +#define CF_DATA 0x10 +#define CF_CLOSE 0x20 + +#define CHAN_SBSIZE (16 * 1024) /* Send buffer size. */ +#define CHAN_RBSIZE (16 * 1024) /* Receive buffer size. */ +#define CHAN_MAXSEGSIZE 1024 /* Maximum segment size. */ + +/* Circular buffer. */ +struct buf { + uint8_t *data; + size_t size; + size_t in; + size_t out; +}; + +struct chan { + int flags; + int state; + pthread_mutex_t lock; + struct mux *mux; + + /* Receiver state variables. */ + struct buf *recvbuf; + pthread_cond_t rdready; + uint32_t recvseq; + uint16_t recvmss; + + /* Sender state variables. */ + struct buf *sendbuf; + pthread_cond_t wrready; + uint32_t sendseq; + uint32_t sendwin; + uint16_t sendmss; +}; + +struct mux { + int closed; + int status; + int socket; + pthread_mutex_t lock; + pthread_cond_t done; + struct chan *channels[MUX_MAXCHAN]; + int nchans; + + /* Sender thread data. */ + pthread_t sender; + pthread_cond_t sender_newwork; + pthread_cond_t sender_started; + int sender_waiting; + int sender_ready; + int sender_lastid; + + /* Receiver thread data. */ + pthread_t receiver; +}; + +static int sock_writev(int, struct iovec *, int); +static int sock_write(int, void *, size_t); +static ssize_t sock_read(int, void *, size_t); +static int sock_readwait(int, void *, size_t); + +static int mux_init(struct mux *); +static void mux_lock(struct mux *); +static void mux_unlock(struct mux *); + +static struct chan *chan_new(struct mux *); +static struct chan *chan_get(struct mux *, int); +static struct chan *chan_connect(struct mux *, int); +static void chan_lock(struct chan *); +static void chan_unlock(struct chan *); +static int chan_insert(struct mux *, struct chan *); +static void chan_free(struct chan *); + +static struct buf *buf_new(size_t); +static size_t buf_count(struct buf *); +static size_t buf_avail(struct buf *); +static void buf_get(struct buf *, void *, size_t); +static void buf_put(struct buf *, const void *, size_t); +static void buf_free(struct buf *); + +static void sender_wakeup(struct mux *); +static void *sender_loop(void *); +static int sender_waitforwork(struct mux *, int *); +static int sender_scan(struct mux *, int *); +static void sender_cleanup(void *); + +static void *receiver_loop(void *); + +static int +sock_writev(int s, struct iovec *iov, int iovcnt) +{ + ssize_t nbytes; + +again: + nbytes = writev(s, iov, iovcnt); + if (nbytes != -1) { + while (nbytes > 0 && (size_t)nbytes >= iov->iov_len) { + nbytes -= iov->iov_len; + iov++; + iovcnt--; + } + if (nbytes == 0) + return (0); + iov->iov_len -= nbytes; + iov->iov_base = (char *)iov->iov_base + nbytes; + } else if (errno != EINTR) { + return (-1); + } + goto again; +} + +static int +sock_write(int s, void *buf, size_t size) +{ + struct iovec iov; + int ret; + + iov.iov_base = buf; + iov.iov_len = size; + ret = sock_writev(s, &iov, 1); + return (ret); +} + +static ssize_t +sock_read(int s, void *buf, size_t size) +{ + ssize_t nbytes; + +again: + nbytes = read(s, buf, size); + if (nbytes == -1 && errno == EINTR) + goto again; + return (nbytes); +} + +static int +sock_readwait(int s, void *buf, size_t size) +{ + char *cp; + ssize_t nbytes; + size_t left; + + cp = buf; + left = size; + while (left > 0) { + nbytes = sock_read(s, cp, left); + if (nbytes == 0) { + errno = ECONNRESET; + return (-1); + } + if (nbytes < 0) + return (-1); + left -= nbytes; + cp += nbytes; + } + return (0); +} + +static void +mux_lock(struct mux *m) +{ + int error; + + error = pthread_mutex_lock(&m->lock); + assert(!error); +} + +static void +mux_unlock(struct mux *m) +{ + int error; + + error = pthread_mutex_unlock(&m->lock); + assert(!error); +} + +/* Create a TCP multiplexer on the given socket. */ +struct mux * +mux_open(int sock, struct chan **chan) +{ + struct mux *m; + struct chan *chan0; + int error; + + m = xmalloc(sizeof(struct mux)); + memset(m->channels, 0, sizeof(m->channels)); + m->nchans = 0; + m->closed = 0; + m->status = -1; + m->socket = sock; + + m->sender_waiting = 0; + m->sender_lastid = 0; + m->sender_ready = 0; + pthread_mutex_init(&m->lock, NULL); + pthread_cond_init(&m->done, NULL); + pthread_cond_init(&m->sender_newwork, NULL); + pthread_cond_init(&m->sender_started, NULL); + + error = mux_init(m); + if (error) + goto bad; + chan0 = chan_connect(m, 0); + if (chan0 == NULL) + goto bad; + *chan = chan0; + return (m); +bad: + mux_shutdown(m, NULL, STATUS_FAILURE); + (void)mux_close(m); + return (NULL); +} + +int +mux_close(struct mux *m) +{ + struct chan *chan; + int i, status; + + assert(m->closed); + for (i = 0; i < m->nchans; i++) { + chan = m->channels[i]; + if (chan != NULL) + chan_free(chan); + } + pthread_cond_destroy(&m->sender_started); + pthread_cond_destroy(&m->sender_newwork); + pthread_cond_destroy(&m->done); + pthread_mutex_destroy(&m->lock); + status = m->status; + free(m); + return (status); +} + +/* Close a channel. */ +int +chan_close(struct chan *chan) +{ + + chan_lock(chan); + if (chan->state == CS_ESTABLISHED) { + chan->state = CS_WRCLOSED; + chan->flags |= CF_CLOSE; + } else if (chan->state == CS_RDCLOSED) { + chan->state = CS_CLOSED; + chan->flags |= CF_CLOSE; + } else if (chan->state == CS_WRCLOSED || chan->state == CS_CLOSED) { + chan_unlock(chan); + return (0); + } else { + chan_unlock(chan); + return (-1); + } + chan_unlock(chan); + sender_wakeup(chan->mux); + return (0); +} + +void +chan_wait(struct chan *chan) +{ + + chan_lock(chan); + while (chan->state != CS_CLOSED) + pthread_cond_wait(&chan->rdready, &chan->lock); + chan_unlock(chan); +} + +/* Returns the ID of an available channel in the listening state. */ +int +chan_listen(struct mux *m) +{ + struct chan *chan; + int i; + + mux_lock(m); + for (i = 0; i < m->nchans; i++) { + chan = m->channels[i]; + chan_lock(chan); + if (chan->state == CS_UNUSED) { + mux_unlock(m); + chan->state = CS_LISTENING; + chan_unlock(chan); + return (i); + } + chan_unlock(chan); + } + mux_unlock(m); + chan = chan_new(m); + chan->state = CS_LISTENING; + i = chan_insert(m, chan); + if (i == -1) + chan_free(chan); + return (i); +} + +struct chan * +chan_accept(struct mux *m, int id) +{ + struct chan *chan; + + chan = chan_get(m, id); + while (chan->state == CS_LISTENING) + pthread_cond_wait(&chan->rdready, &chan->lock); + if (chan->state != CS_ESTABLISHED) { + errno = ECONNRESET; + chan_unlock(chan); + return (NULL); + } + chan_unlock(chan); + return (chan); +} + +/* Read bytes from a channel. */ +ssize_t +chan_read(struct chan *chan, void *buf, size_t size) +{ + char *cp; + size_t count, n; + + cp = buf; + chan_lock(chan); + for (;;) { + if (chan->state == CS_RDCLOSED || chan->state == CS_CLOSED) { + chan_unlock(chan); + return (0); + } + if (chan->state != CS_ESTABLISHED && + chan->state != CS_WRCLOSED) { + chan_unlock(chan); + errno = EBADF; + return (-1); + } + count = buf_count(chan->recvbuf); + if (count > 0) + break; + pthread_cond_wait(&chan->rdready, &chan->lock); + } + n = min(count, size); + buf_get(chan->recvbuf, cp, n); + chan->recvseq += n; + chan->flags |= CF_WINDOW; + chan_unlock(chan); + /* We need to wake up the sender so that it sends a window update. */ + sender_wakeup(chan->mux); + return (n); +} + +/* Write bytes to a channel. */ +ssize_t +chan_write(struct chan *chan, const void *buf, size_t size) +{ + const char *cp; + size_t avail, n, pos; + + pos = 0; + cp = buf; + chan_lock(chan); + while (pos < size) { + for (;;) { + if (chan->state != CS_ESTABLISHED && + chan->state != CS_RDCLOSED) { + chan_unlock(chan); + errno = EPIPE; + return (-1); + } + avail = buf_avail(chan->sendbuf); + if (avail > 0) + break; + pthread_cond_wait(&chan->wrready, &chan->lock); + } + n = min(avail, size - pos); + buf_put(chan->sendbuf, cp + pos, n); + pos += n; + } + chan_unlock(chan); + sender_wakeup(chan->mux); + return (size); +} + +/* + * Internal channel API. + */ + +static struct chan * +chan_connect(struct mux *m, int id) +{ + struct chan *chan; + + chan = chan_get(m, id); + if (chan->state != CS_UNUSED) { + chan_unlock(chan); + return (NULL); + } + chan->state = CS_CONNECTING; + chan->flags |= CF_CONNECT; + chan_unlock(chan); + sender_wakeup(m); + chan_lock(chan); + while (chan->state == CS_CONNECTING) + pthread_cond_wait(&chan->wrready, &chan->lock); + if (chan->state != CS_ESTABLISHED) { + chan_unlock(chan); + return (NULL); + } + chan_unlock(chan); + return (chan); +} + +/* + * Get a channel from its ID, creating it if necessary. + * The channel is returned locked. + */ +static struct chan * +chan_get(struct mux *m, int id) +{ + struct chan *chan; + + assert(id < MUX_MAXCHAN); + mux_lock(m); + chan = m->channels[id]; + if (chan == NULL) { + chan = chan_new(m); + m->channels[id] = chan; + m->nchans++; + } + chan_lock(chan); + mux_unlock(m); + return (chan); +} + +/* Lock a channel. */ +static void +chan_lock(struct chan *chan) +{ + int error; + + error = pthread_mutex_lock(&chan->lock); + assert(!error); +} + +/* Unlock a channel. */ +static void +chan_unlock(struct chan *chan) +{ + int error; + + error = pthread_mutex_unlock(&chan->lock); + assert(!error); +} + +/* + * Create a new channel. + */ +static struct chan * +chan_new(struct mux *m) +{ + struct chan *chan; + + chan = xmalloc(sizeof(struct chan)); + chan->state = CS_UNUSED; + chan->flags = 0; + chan->mux = m; + chan->sendbuf = buf_new(CHAN_SBSIZE); + chan->sendseq = 0; + chan->sendwin = 0; + chan->sendmss = 0; + chan->recvbuf = buf_new(CHAN_RBSIZE); + chan->recvseq = 0; + chan->recvmss = CHAN_MAXSEGSIZE; + pthread_mutex_init(&chan->lock, NULL); + pthread_cond_init(&chan->rdready, NULL); + pthread_cond_init(&chan->wrready, NULL); + return (chan); +} + +/* Free any resources associated with a channel. */ +static void +chan_free(struct chan *chan) +{ + + pthread_cond_destroy(&chan->rdready); + pthread_cond_destroy(&chan->wrready); + pthread_mutex_destroy(&chan->lock); + buf_free(chan->recvbuf); + buf_free(chan->sendbuf); + free(chan); +} + +/* Insert the new channel in the channel list. */ +static int +chan_insert(struct mux *m, struct chan *chan) +{ + int i; + + mux_lock(m); + for (i = 0; i < MUX_MAXCHAN; i++) { + if (m->channels[i] == NULL) { + m->channels[i] = chan; + m->nchans++; + mux_unlock(m); + return (i); + } + } + errno = ENOBUFS; + return (-1); +} + +/* + * Initialize the multiplexer protocol. + * + * This means negotiating protocol version and starting + * the receiver and sender threads. + */ +static int +mux_init(struct mux *m) +{ + struct mux_header mh; + int error; + + mh.type = MUX_STARTUPREQ; + mh.mh_startup.version = htons(MUX_PROTOVER); + error = sock_write(m->socket, &mh, MUX_STARTUPHDRSZ); + if (error) + return (-1); + error = sock_readwait(m->socket, &mh, MUX_STARTUPHDRSZ); + if (error) + return (-1); + if (mh.type != MUX_STARTUPREP || + ntohs(mh.mh_startup.version) != MUX_PROTOVER) + return (-1); + mux_lock(m); + error = pthread_create(&m->sender, NULL, sender_loop, m); + if (error) { + mux_unlock(m); + return (-1); + } + /* + * Make sure the sender thread has run and is waiting for new work + * before going on. Otherwise, it might lose the race and a + * request, which will cause a deadlock. + */ + while (!m->sender_ready) + pthread_cond_wait(&m->sender_started, &m->lock); + + mux_unlock(m); + error = pthread_create(&m->receiver, NULL, receiver_loop, m); + if (error) + return (-1); + return (0); +} + +/* + * Close all the channels, terminate the sender and receiver thread. + * This is an important function because it is used everytime we need + * to wake up all the worker threads to abort the program. + * + * This function accepts an error message that will be printed if the + * multiplexer wasn't already closed. This is useful because it ensures + * that only the first error message will be printed, and that it will + * be printed before doing the actual shutdown work. If this is a + * normal shutdown, NULL can be passed instead. + * + * The "status" parameter of the first mux_shutdown() call is retained + * and then returned by mux_close(), so that the main thread can know + * what type of error happened in the end, if any. + */ +void +mux_shutdown(struct mux *m, const char *errmsg, int status) +{ + pthread_t self, sender, receiver; + struct chan *chan; + const char *name; + void *val; + int i, ret; + + mux_lock(m); + if (m->closed) { + mux_unlock(m); + return; + } + m->closed = 1; + m->status = status; + self = pthread_self(); + sender = m->sender; + receiver = m->receiver; + if (errmsg != NULL) { + if (pthread_equal(self, receiver)) + name = "Receiver"; + else if (pthread_equal(self, sender)) + name = "Sender"; + else + name = NULL; + if (name == NULL) + lprintf(-1, "%s\n", errmsg); + else + lprintf(-1, "%s: %s\n", name, errmsg); + } + + for (i = 0; i < MUX_MAXCHAN; i++) { + if (m->channels[i] != NULL) { + chan = m->channels[i]; + chan_lock(chan); + if (chan->state != CS_UNUSED) { + chan->state = CS_CLOSED; + chan->flags = 0; + pthread_cond_broadcast(&chan->rdready); + pthread_cond_broadcast(&chan->wrready); + } + chan_unlock(chan); + } + } + mux_unlock(m); + + if (!pthread_equal(self, receiver)) { + ret = pthread_cancel(receiver); + assert(!ret); + pthread_join(receiver, &val); + assert(val == PTHREAD_CANCELED); + } + if (!pthread_equal(self, sender)) { + ret = pthread_cancel(sender); + assert(!ret); + pthread_join(sender, &val); + assert(val == PTHREAD_CANCELED); + } +} + +static void +sender_wakeup(struct mux *m) +{ + int waiting; + + mux_lock(m); + waiting = m->sender_waiting; + mux_unlock(m); + /* + * We don't care about the race here: if the sender was + * waiting and is not anymore, we'll just send a useless + * signal; if he wasn't waiting then he won't go to sleep + * before having sent what we want him to. + */ + if (waiting) + pthread_cond_signal(&m->sender_newwork); +} + +static void * +sender_loop(void *arg) +{ + struct iovec iov[3]; + struct mux_header mh; + struct mux *m; + struct chan *chan; + struct buf *buf; + uint32_t winsize; + uint16_t hdrsize, size, len; + int error, id, iovcnt, what = 0; + + m = (struct mux *)arg; + what = 0; +again: + id = sender_waitforwork(m, &what); + chan = chan_get(m, id); + hdrsize = size = 0; + switch (what) { + case CF_CONNECT: + mh.type = MUX_CONNECT; + mh.mh_connect.id = id; + mh.mh_connect.mss = htons(chan->recvmss); + mh.mh_connect.window = htonl(chan->recvseq + + chan->recvbuf->size); + hdrsize = MUX_CONNECTHDRSZ; + break; + case CF_ACCEPT: + mh.type = MUX_ACCEPT; + mh.mh_accept.id = id; + mh.mh_accept.mss = htons(chan->recvmss); + mh.mh_accept.window = htonl(chan->recvseq + + chan->recvbuf->size); + hdrsize = MUX_ACCEPTHDRSZ; + break; + case CF_RESET: + mh.type = MUX_RESET; + mh.mh_reset.id = id; + hdrsize = MUX_RESETHDRSZ; + break; + case CF_WINDOW: + mh.type = MUX_WINDOW; + mh.mh_window.id = id; + mh.mh_window.window = htonl(chan->recvseq + + chan->recvbuf->size); + hdrsize = MUX_WINDOWHDRSZ; + break; + case CF_DATA: + mh.type = MUX_DATA; + mh.mh_data.id = id; + size = min(buf_count(chan->sendbuf), chan->sendmss); + winsize = chan->sendwin - chan->sendseq; + if (winsize < size) + size = winsize; + mh.mh_data.len = htons(size); + hdrsize = MUX_DATAHDRSZ; + break; + case CF_CLOSE: + mh.type = MUX_CLOSE; + mh.mh_close.id = id; + hdrsize = MUX_CLOSEHDRSZ; + break; + } + if (size > 0) { + assert(mh.type == MUX_DATA); + /* + * Older FreeBSD versions (and maybe other OSes) have the + * iov_base field defined as char *. Cast to char * to + * silence a warning in this case. + */ + iov[0].iov_base = (char *)&mh; + iov[0].iov_len = hdrsize; + iovcnt = 1; + /* We access the buffer directly to avoid some copying. */ + buf = chan->sendbuf; + len = min(size, buf->size + 1 - buf->out); + iov[iovcnt].iov_base = buf->data + buf->out; + iov[iovcnt].iov_len = len; + iovcnt++; + if (size > len) { + /* Wrapping around. */ + iov[iovcnt].iov_base = buf->data; + iov[iovcnt].iov_len = size - len; + iovcnt++; + } + /* + * Since we're the only thread sending bytes from the + * buffer and modifying buf->out, it's safe to unlock + * here during I/O. It avoids keeping the channel lock + * too long, since write() might block. + */ + chan_unlock(chan); + error = sock_writev(m->socket, iov, iovcnt); + if (error) + goto bad; + chan_lock(chan); + chan->sendseq += size; + buf->out += size; + if (buf->out > buf->size) + buf->out -= buf->size + 1; + pthread_cond_signal(&chan->wrready); + chan_unlock(chan); + } else { + chan_unlock(chan); + error = sock_write(m->socket, &mh, hdrsize); + if (error) + goto bad; + } + goto again; +bad: + if (error == EPIPE) + mux_shutdown(m, strerror(errno), STATUS_TRANSIENTFAILURE); + else + mux_shutdown(m, strerror(errno), STATUS_FAILURE); + return (NULL); +} + +static void +sender_cleanup(void *arg) +{ + struct mux *m; + + m = (struct mux *)arg; + mux_unlock(m); +} + +static int +sender_waitforwork(struct mux *m, int *what) +{ + int id; + + mux_lock(m); + pthread_cleanup_push(sender_cleanup, m); + if (!m->sender_ready) { + pthread_cond_signal(&m->sender_started); + m->sender_ready = 1; + } + while ((id = sender_scan(m, what)) == -1) { + m->sender_waiting = 1; + pthread_cond_wait(&m->sender_newwork, &m->lock); + } + m->sender_waiting = 0; + pthread_cleanup_pop(1); + return (id); +} + +/* + * Scan for work to do for the sender. Has to be called with + * the multiplexer lock held. + */ +static int +sender_scan(struct mux *m, int *what) +{ + struct chan *chan; + int id; + + if (m->nchans <= 0) + return (-1); + id = m->sender_lastid; + do { + id++; + if (id >= m->nchans) + id = 0; + chan = m->channels[id]; + chan_lock(chan); + if (chan->state != CS_UNUSED) { + if (chan->sendseq != chan->sendwin && + buf_count(chan->sendbuf) > 0) + chan->flags |= CF_DATA; + if (chan->flags) { + /* By order of importance. */ + if (chan->flags & CF_CONNECT) + *what = CF_CONNECT; + else if (chan->flags & CF_ACCEPT) + *what = CF_ACCEPT; + else if (chan->flags & CF_RESET) + *what = CF_RESET; + else if (chan->flags & CF_WINDOW) + *what = CF_WINDOW; + else if (chan->flags & CF_DATA) + *what = CF_DATA; + else if (chan->flags & CF_CLOSE) + *what = CF_CLOSE; + chan->flags &= ~*what; + chan_unlock(chan); + m->sender_lastid = id; + return (id); + } + } + chan_unlock(chan); + } while (id != m->sender_lastid); + return (-1); +} + +/* Read the rest of a packet header depending on its type. */ +#define SOCK_READREST(s, mh, hsize) \ + sock_readwait(s, (char *)&mh + sizeof(mh.type), (hsize) - sizeof(mh.type)) + +void * +receiver_loop(void *arg) +{ + struct mux_header mh; + struct mux *m; + struct chan *chan; + struct buf *buf; + uint16_t size, len; + int error; + + m = (struct mux *)arg; + while ((error = sock_readwait(m->socket, &mh.type, + sizeof(mh.type))) == 0) { + switch (mh.type) { + case MUX_CONNECT: + error = SOCK_READREST(m->socket, mh, MUX_CONNECTHDRSZ); + if (error) + goto bad; + chan = chan_get(m, mh.mh_connect.id); + if (chan->state == CS_LISTENING) { + chan->state = CS_ESTABLISHED; + chan->sendmss = ntohs(mh.mh_connect.mss); + chan->sendwin = ntohl(mh.mh_connect.window); + chan->flags |= CF_ACCEPT; + pthread_cond_signal(&chan->rdready); + } else + chan->flags |= CF_RESET; + chan_unlock(chan); + sender_wakeup(m); + break; + case MUX_ACCEPT: + error = SOCK_READREST(m->socket, mh, MUX_ACCEPTHDRSZ); + if (error) + goto bad; + chan = chan_get(m, mh.mh_accept.id); + if (chan->state == CS_CONNECTING) { + chan->sendmss = ntohs(mh.mh_accept.mss); + chan->sendwin = ntohl(mh.mh_accept.window); + chan->state = CS_ESTABLISHED; + pthread_cond_signal(&chan->wrready); + chan_unlock(chan); + } else { + chan->flags |= CF_RESET; + chan_unlock(chan); + sender_wakeup(m); + } + break; + case MUX_RESET: + error = SOCK_READREST(m->socket, mh, MUX_RESETHDRSZ); + if (error) + goto bad; + goto badproto; + case MUX_WINDOW: + error = SOCK_READREST(m->socket, mh, MUX_WINDOWHDRSZ); + if (error) + goto bad; + chan = chan_get(m, mh.mh_window.id); + if (chan->state == CS_ESTABLISHED || + chan->state == CS_RDCLOSED) { + chan->sendwin = ntohl(mh.mh_window.window); + chan_unlock(chan); + sender_wakeup(m); + } else { + chan_unlock(chan); + } + break; + case MUX_DATA: + error = SOCK_READREST(m->socket, mh, MUX_DATAHDRSZ); + if (error) + goto bad; + chan = chan_get(m, mh.mh_data.id); + len = ntohs(mh.mh_data.len); + buf = chan->recvbuf; + if ((chan->state != CS_ESTABLISHED && + chan->state != CS_WRCLOSED) || + (len > buf_avail(buf) || + len > chan->recvmss)) { + chan_unlock(chan); + goto badproto; + return (NULL); + } + /* + * Similarly to the sender code, it's safe to + * unlock the channel here. + */ + chan_unlock(chan); + size = min(buf->size + 1 - buf->in, len); + error = sock_readwait(m->socket, + buf->data + buf->in, size); + if (error) + goto bad; + if (len > size) { + /* Wrapping around. */ + error = sock_readwait(m->socket, + buf->data, len - size); + if (error) + goto bad; + } + chan_lock(chan); + buf->in += len; + if (buf->in > buf->size) + buf->in -= buf->size + 1; + pthread_cond_signal(&chan->rdready); + chan_unlock(chan); + break; + case MUX_CLOSE: + error = SOCK_READREST(m->socket, mh, MUX_CLOSEHDRSZ); + if (error) + goto bad; + chan = chan_get(m, mh.mh_close.id); + if (chan->state == CS_ESTABLISHED) + chan->state = CS_RDCLOSED; + else if (chan->state == CS_WRCLOSED) + chan->state = CS_CLOSED; + else + goto badproto; + pthread_cond_signal(&chan->rdready); + chan_unlock(chan); + break; + default: + goto badproto; + } + } +bad: + if (errno == ECONNRESET || errno == ECONNABORTED) + mux_shutdown(m, strerror(errno), STATUS_TRANSIENTFAILURE); + else + mux_shutdown(m, strerror(errno), STATUS_FAILURE); + return (NULL); +badproto: + mux_shutdown(m, "Protocol error", STATUS_FAILURE); + return (NULL); +} + +/* + * Circular buffers API. + */ + +static struct buf * +buf_new(size_t size) +{ + struct buf *buf; + + buf = xmalloc(sizeof(struct buf)); + buf->data = xmalloc(size + 1); + buf->size = size; + buf->in = 0; + buf->out = 0; + return (buf); +} + +static void +buf_free(struct buf *buf) +{ + + free(buf->data); + free(buf); +} + +/* Number of bytes stored in the buffer. */ +static size_t +buf_count(struct buf *buf) +{ + size_t count; + + if (buf->in >= buf->out) + count = buf->in - buf->out; + else + count = buf->size + 1 + buf->in - buf->out; + return (count); +} + +/* Number of bytes available in the buffer. */ +static size_t +buf_avail(struct buf *buf) +{ + size_t avail; + + if (buf->out > buf->in) + avail = buf->out - buf->in - 1; + else + avail = buf->size + buf->out - buf->in; + return (avail); +} + +static void +buf_put(struct buf *buf, const void *data, size_t size) +{ + const char *cp; + size_t len; + + assert(size > 0); + assert(buf_avail(buf) >= size); + cp = data; + len = buf->size + 1 - buf->in; + if (len < size) { + /* Wrapping around. */ + memcpy(buf->data + buf->in, cp, len); + memcpy(buf->data, cp + len, size - len); + } else { + /* Not wrapping around. */ + memcpy(buf->data + buf->in, cp, size); + } + buf->in += size; + if (buf->in > buf->size) + buf->in -= buf->size + 1; +} + +static void +buf_get(struct buf *buf, void *data, size_t size) +{ + char *cp; + size_t len; + + assert(size > 0); + assert(buf_count(buf) >= size); + cp = data; + len = buf->size + 1 - buf->out; + if (len < size) { + /* Wrapping around. */ + memcpy(cp, buf->data + buf->out, len); + memcpy(cp + len, buf->data, size - len); + } else { + /* Not wrapping around. */ + memcpy(cp, buf->data + buf->out, size); + } + buf->out += size; + if (buf->out > buf->size) + buf->out -= buf->size + 1; +} diff --git a/usr.bin/csup/mux.h b/usr.bin/csup/mux.h new file mode 100644 index 0000000..ff36083 --- /dev/null +++ b/usr.bin/csup/mux.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _MUX_H_ +#define _MUX_H_ + +struct mux; +struct chan; + +struct mux *mux_open(int, struct chan **); +void mux_shutdown(struct mux *, const char *, int); +int mux_close(struct mux *); + +void chan_wait(struct chan *); +int chan_listen(struct mux *); +struct chan *chan_accept(struct mux *, int); +ssize_t chan_read(struct chan *, void *, size_t); +ssize_t chan_write(struct chan *, const void *, size_t); +int chan_close(struct chan *); + +#endif /* !_MUX_H_ */ diff --git a/usr.bin/csup/parse.y b/usr.bin/csup/parse.y new file mode 100644 index 0000000..3dcd617 --- /dev/null +++ b/usr.bin/csup/parse.y @@ -0,0 +1,91 @@ +%{ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> + +#include "config.h" +#include "token.h" + +%} + +%union { + char *str; + int i; +} + +%token DEFAULT +%token <i> NAME +%token <i> BOOLEAN +%token EQUAL +%token <str> STRING + +%% + +config_file + : config_list + | + ; + +config_list + : config + | config_list config + ; + +config + : default_line + | collection + ; + +default_line + : DEFAULT options + { coll_setdef(); } + ; + +collection + : STRING options + { coll_add($1); } + ; + +options + : + | options option + ; + +option + : BOOLEAN + { coll_setopt($1, NULL); } + | value + ; + +value + : NAME EQUAL STRING + { coll_setopt($1, $3); } + ; + +%% diff --git a/usr.bin/csup/pathcomp.c b/usr.bin/csup/pathcomp.c new file mode 100644 index 0000000..380d042 --- /dev/null +++ b/usr.bin/csup/pathcomp.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <stdlib.h> +#include <string.h> + +#include "misc.h" +#include "pathcomp.h" + +struct pathcomp { + char *target; + size_t targetlen; + char *trashed; + char *prev; + size_t prevlen; + size_t goal; + size_t curlen; +}; + +struct pathcomp * +pathcomp_new(void) +{ + struct pathcomp *pc; + + pc = xmalloc(sizeof(struct pathcomp)); + pc->curlen = 0; + pc->target = NULL; + pc->targetlen = 0; + pc->trashed = NULL; + pc->prev = NULL; + pc->prevlen = 0; + return (pc); +} + +int +pathcomp_put(struct pathcomp *pc, int type, char *path) +{ + char *cp; + + assert(pc->target == NULL); + if (*path == '/') + return (-1); + + switch (type) { + case PC_DIRDOWN: + pc->target = path; + pc->targetlen = strlen(path); + break; + case PC_FILE: + case PC_DIRUP: + cp = strrchr(path, '/'); + pc->target = path; + if (cp != NULL) + pc->targetlen = cp - path; + else + pc->targetlen = 0; + break; + } + if (pc->prev != NULL) + pc->goal = commonpathlength(pc->prev, pc->prevlen, pc->target, + pc->targetlen); + else + pc->goal = 0; + if (pc->curlen == pc->goal) /* No need to go up. */ + pc->goal = pc->targetlen; + return (0); +} + +int +pathcomp_get(struct pathcomp *pc, int *type, char **name) +{ + char *cp; + size_t slashpos, start; + + if (pc->curlen > pc->goal) { /* Going up. */ + assert(pc->prev != NULL); + pc->prev[pc->curlen] = '\0'; + cp = pc->prev + pc->curlen - 1; + while (cp >= pc->prev) { + if (*cp == '/') + break; + cp--; + } + if (cp >= pc->prev) + slashpos = cp - pc->prev; + else + slashpos = 0; + pc->curlen = slashpos; + if (pc->curlen <= pc->goal) { /* Done going up. */ + assert(pc->curlen == pc->goal); + pc->goal = pc->targetlen; + } + *type = PC_DIRUP; + *name = pc->prev; + return (1); + } else if (pc->curlen < pc->goal) { /* Going down. */ + /* Restore the previously overwritten '/' character. */ + if (pc->trashed != NULL) { + *pc->trashed = '/'; + pc->trashed = NULL; + } + if (pc->curlen == 0) + start = pc->curlen; + else + start = pc->curlen + 1; + slashpos = start; + while (slashpos < pc->goal) { + if (pc->target[slashpos] == '/') + break; + slashpos++; + } + if (pc->target[slashpos] != '\0') { + assert(pc->target[slashpos] == '/'); + pc->trashed = pc->target + slashpos; + pc->target[slashpos] = '\0'; + } + pc->curlen = slashpos; + *type = PC_DIRDOWN; + *name = pc->target; + return (1); + } else { /* Done. */ + if (pc->target != NULL) { + if (pc->trashed != NULL) { + *pc->trashed = '/'; + pc->trashed = NULL; + } + if (pc->prev != NULL) + free(pc->prev); + pc->prev = xmalloc(pc->targetlen + 1); + memcpy(pc->prev, pc->target, pc->targetlen); + pc->prev[pc->targetlen] = '\0'; + pc->prevlen = pc->targetlen; + pc->target = NULL; + pc->targetlen = 0; + } + return (0); + } +} + +void +pathcomp_finish(struct pathcomp *pc) +{ + + pc->target = NULL; + pc->targetlen = 0; + pc->goal = 0; +} + +void +pathcomp_free(struct pathcomp *pc) +{ + + if (pc->prev != NULL) + free(pc->prev); + free(pc); +} diff --git a/usr.bin/csup/pathcomp.h b/usr.bin/csup/pathcomp.h new file mode 100644 index 0000000..3cea410 --- /dev/null +++ b/usr.bin/csup/pathcomp.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _PATHCOMP_H +#define _PATHCOMP_H + +/* File types */ +#define PC_DIRDOWN 0 +#define PC_FILE 1 +#define PC_DIRUP 2 + +struct pathcomp; + +struct pathcomp *pathcomp_new(void); +int pathcomp_put(struct pathcomp *, int, char *); +int pathcomp_get(struct pathcomp *, int *, char **); +void pathcomp_finish(struct pathcomp *); +void pathcomp_free(struct pathcomp *); + +#endif /* !_PATHCOMP_H */ diff --git a/usr.bin/csup/proto.c b/usr.bin/csup/proto.c new file mode 100644 index 0000000..166a134 --- /dev/null +++ b/usr.bin/csup/proto.c @@ -0,0 +1,997 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/select.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <sys/stat.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <netdb.h> +#include <pthread.h> +#include <signal.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "auth.h" +#include "config.h" +#include "detailer.h" +#include "fattr.h" +#include "fixups.h" +#include "globtree.h" +#include "keyword.h" +#include "lister.h" +#include "misc.h" +#include "mux.h" +#include "proto.h" +#include "queue.h" +#include "stream.h" +#include "threads.h" +#include "updater.h" + +struct killer { + pthread_t thread; + sigset_t sigset; + struct mux *mux; + int killedby; +}; + +static void killer_start(struct killer *, struct mux *); +static void *killer_run(void *); +static void killer_stop(struct killer *); + +static int proto_waitconnect(int); +static int proto_greet(struct config *); +static int proto_negproto(struct config *); +static int proto_fileattr(struct config *); +static int proto_xchgcoll(struct config *); +static struct mux *proto_mux(struct config *); + +static int proto_escape(struct stream *, const char *); +static void proto_unescape(char *); + +static int +proto_waitconnect(int s) +{ + fd_set readfd; + socklen_t len; + int error, rv, soerror; + + FD_ZERO(&readfd); + FD_SET(s, &readfd); + + do { + rv = select(s + 1, &readfd, NULL, NULL, NULL); + } while (rv == -1 && errno == EINTR); + if (rv == -1) + return (-1); + /* Check that the connection was really successful. */ + len = sizeof(soerror); + error = getsockopt(s, SOL_SOCKET, SO_ERROR, &soerror, &len); + if (error) { + /* We have no choice but faking an error here. */ + errno = ECONNREFUSED; + return (-1); + } + if (soerror) { + errno = soerror; + return (-1); + } + return (0); +} + +/* Connect to the CVSup server. */ +int +proto_connect(struct config *config, int family, uint16_t port) +{ + char addrbuf[NI_MAXHOST]; + /* Enough to hold sizeof("cvsup") or any port number. */ + char servname[8]; + struct addrinfo *res, *ai, hints; + int error, opt, s; + + s = -1; + if (port != 0) + snprintf(servname, sizeof(servname), "%d", port); + else { + strncpy(servname, "cvsup", sizeof(servname) - 1); + servname[sizeof(servname) - 1] = '\0'; + } + memset(&hints, 0, sizeof(hints)); + hints.ai_family = family; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(config->host, servname, &hints, &res); + /* + * Try with the hardcoded port number for OSes that don't + * have cvsup defined in the /etc/services file. + */ + if (error == EAI_SERVICE) { + strncpy(servname, "5999", sizeof(servname) - 1); + servname[sizeof(servname) - 1] = '\0'; + error = getaddrinfo(config->host, servname, &hints, &res); + } + if (error) { + lprintf(0, "Name lookup failure for \"%s\": %s\n", config->host, + gai_strerror(error)); + return (STATUS_TRANSIENTFAILURE); + } + for (ai = res; ai != NULL; ai = ai->ai_next) { + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s != -1) { + error = 0; + if (config->laddr != NULL) { + opt = 1; + (void)setsockopt(s, SOL_SOCKET, SO_REUSEADDR, + &opt, sizeof(opt)); + error = bind(s, config->laddr, + config->laddrlen); + } + if (!error) { + error = connect(s, ai->ai_addr, ai->ai_addrlen); + if (error && errno == EINTR) + error = proto_waitconnect(s); + } + if (error) + close(s); + } + (void)getnameinfo(ai->ai_addr, ai->ai_addrlen, addrbuf, + sizeof(addrbuf), NULL, 0, NI_NUMERICHOST); + if (s == -1 || error) { + lprintf(0, "Cannot connect to %s: %s\n", addrbuf, + strerror(errno)); + continue; + } + lprintf(1, "Connected to %s\n", addrbuf); + freeaddrinfo(res); + config->socket = s; + return (STATUS_SUCCESS); + } + freeaddrinfo(res); + return (STATUS_TRANSIENTFAILURE); +} + +/* Greet the server. */ +static int +proto_greet(struct config *config) +{ + char *line, *cmd, *msg, *swver; + struct stream *s; + + s = config->server; + line = stream_getln(s, NULL); + cmd = proto_get_ascii(&line); + if (cmd == NULL) + goto bad; + if (strcmp(cmd, "OK") == 0) { + (void)proto_get_ascii(&line); /* major number */ + (void)proto_get_ascii(&line); /* minor number */ + swver = proto_get_ascii(&line); + } else if (strcmp(cmd, "!") == 0) { + msg = proto_get_rest(&line); + if (msg == NULL) + goto bad; + lprintf(-1, "Rejected by server: %s\n", msg); + return (STATUS_TRANSIENTFAILURE); + } else + goto bad; + lprintf(2, "Server software version: %s\n", + swver != NULL ? swver : "."); + return (STATUS_SUCCESS); +bad: + lprintf(-1, "Invalid greeting from server\n"); + return (STATUS_FAILURE); +} + +/* Negotiate protocol version with the server. */ +static int +proto_negproto(struct config *config) +{ + struct stream *s; + char *cmd, *line, *msg; + int error, maj, min; + + s = config->server; + proto_printf(s, "PROTO %d %d %s\n", PROTO_MAJ, PROTO_MIN, PROTO_SWVER); + stream_flush(s); + line = stream_getln(s, NULL); + cmd = proto_get_ascii(&line); + if (cmd == NULL || line == NULL) + goto bad; + if (strcmp(cmd, "!") == 0) { + msg = proto_get_rest(&line); + lprintf(-1, "Protocol negotiation failed: %s\n", msg); + return (1); + } else if (strcmp(cmd, "PROTO") != 0) + goto bad; + error = proto_get_int(&line, &maj, 10); + if (!error) + error = proto_get_int(&line, &min, 10); + if (error) + goto bad; + if (maj != PROTO_MAJ || min != PROTO_MIN) { + lprintf(-1, "Server protocol version %d.%d not supported " + "by client\n", maj, min); + return (STATUS_FAILURE); + } + return (STATUS_SUCCESS); +bad: + lprintf(-1, "Invalid PROTO command from server\n"); + return (STATUS_FAILURE); +} + +/* + * File attribute support negotiation. + */ +static int +proto_fileattr(struct config *config) +{ + fattr_support_t support; + struct stream *s; + char *line, *cmd; + int error, i, n, attr; + + s = config->server; + lprintf(2, "Negotiating file attribute support\n"); + proto_printf(s, "ATTR %d\n", FT_NUMBER); + for (i = 0; i < FT_NUMBER; i++) + proto_printf(s, "%x\n", fattr_supported(i)); + proto_printf(s, ".\n"); + stream_flush(s); + line = stream_getln(s, NULL); + if (line == NULL) + goto bad; + cmd = proto_get_ascii(&line); + error = proto_get_int(&line, &n, 10); + if (error || line != NULL || strcmp(cmd, "ATTR") != 0 || n > FT_NUMBER) + goto bad; + for (i = 0; i < n; i++) { + line = stream_getln(s, NULL); + if (line == NULL) + goto bad; + error = proto_get_int(&line, &attr, 16); + if (error) + goto bad; + support[i] = fattr_supported(i) & attr; + } + for (i = n; i < FT_NUMBER; i++) + support[i] = 0; + line = stream_getln(s, NULL); + if (line == NULL || strcmp(line, ".") != 0) + goto bad; + memcpy(config->fasupport, support, sizeof(config->fasupport)); + return (STATUS_SUCCESS); +bad: + lprintf(-1, "Protocol error negotiating attribute support\n"); + return (STATUS_FAILURE); +} + +/* + * Exchange collection information. + */ +static int +proto_xchgcoll(struct config *config) +{ + struct coll *coll; + struct stream *s; + struct globtree *diraccept, *dirrefuse; + struct globtree *fileaccept, *filerefuse; + char *line, *cmd, *collname, *pat; + char *msg, *release, *ident, *rcskey, *prefix; + size_t i, len; + int error, flags, options; + + s = config->server; + lprintf(2, "Exchanging collection information\n"); + STAILQ_FOREACH(coll, &config->colls, co_next) { + if (coll->co_options & CO_SKIP) + continue; + proto_printf(s, "COLL %s %s %o %d\n", coll->co_name, + coll->co_release, coll->co_umask, coll->co_options); + for (i = 0; i < pattlist_size(coll->co_accepts); i++) { + proto_printf(s, "ACC %s\n", + pattlist_get(coll->co_accepts, i)); + } + for (i = 0; i < pattlist_size(coll->co_refusals); i++) { + proto_printf(s, "REF %s\n", + pattlist_get(coll->co_refusals, i)); + } + proto_printf(s, ".\n"); + } + proto_printf(s, ".\n"); + stream_flush(s); + + STAILQ_FOREACH(coll, &config->colls, co_next) { + if (coll->co_options & CO_SKIP) + continue; + coll->co_norsync = globtree_false(); + line = stream_getln(s, NULL); + if (line == NULL) + goto bad; + cmd = proto_get_ascii(&line); + collname = proto_get_ascii(&line); + release = proto_get_ascii(&line); + error = proto_get_int(&line, &options, 10); + if (error || line != NULL) + goto bad; + if (strcmp(cmd, "COLL") != 0 || + strcmp(collname, coll->co_name) != 0 || + strcmp(release, coll->co_release) != 0) + goto bad; + coll->co_options = + (coll->co_options | (options & CO_SERVMAYSET)) & + ~(~options & CO_SERVMAYCLEAR); + while ((line = stream_getln(s, NULL)) != NULL) { + if (strcmp(line, ".") == 0) + break; + cmd = proto_get_ascii(&line); + if (cmd == NULL) + goto bad; + if (strcmp(cmd, "!") == 0) { + msg = proto_get_rest(&line); + if (msg == NULL) + goto bad; + lprintf(-1, "Server message: %s\n", msg); + } else if (strcmp(cmd, "PRFX") == 0) { + prefix = proto_get_ascii(&line); + if (prefix == NULL || line != NULL) + goto bad; + coll->co_cvsroot = xstrdup(prefix); + } else if (strcmp(cmd, "KEYALIAS") == 0) { + ident = proto_get_ascii(&line); + rcskey = proto_get_ascii(&line); + if (rcskey == NULL || line != NULL) + goto bad; + error = keyword_alias(coll->co_keyword, ident, + rcskey); + if (error) + goto bad; + } else if (strcmp(cmd, "KEYON") == 0) { + ident = proto_get_ascii(&line); + if (ident == NULL || line != NULL) + goto bad; + error = keyword_enable(coll->co_keyword, ident); + if (error) + goto bad; + } else if (strcmp(cmd, "KEYOFF") == 0) { + ident = proto_get_ascii(&line); + if (ident == NULL || line != NULL) + goto bad; + error = keyword_disable(coll->co_keyword, + ident); + if (error) + goto bad; + } else if (strcmp(cmd, "NORS") == 0) { + pat = proto_get_ascii(&line); + if (pat == NULL || line != NULL) + goto bad; + coll->co_norsync = globtree_or(coll->co_norsync, + globtree_match(pat, FNM_PATHNAME)); + } else if (strcmp(cmd, "RNORS") == 0) { + pat = proto_get_ascii(&line); + if (pat == NULL || line != NULL) + goto bad; + coll->co_norsync = globtree_or(coll->co_norsync, + globtree_match(pat, FNM_PATHNAME | + FNM_LEADING_DIR)); + } else + goto bad; + } + if (line == NULL) + goto bad; + keyword_prepare(coll->co_keyword); + + diraccept = globtree_true(); + fileaccept = globtree_true(); + dirrefuse = globtree_false(); + filerefuse = globtree_false(); + + if (pattlist_size(coll->co_accepts) > 0) { + globtree_free(diraccept); + globtree_free(fileaccept); + diraccept = globtree_false(); + fileaccept = globtree_false(); + flags = FNM_PATHNAME | FNM_LEADING_DIR | + FNM_PREFIX_DIRS; + for (i = 0; i < pattlist_size(coll->co_accepts); i++) { + pat = pattlist_get(coll->co_accepts, i); + diraccept = globtree_or(diraccept, + globtree_match(pat, flags)); + + len = strlen(pat); + if (coll->co_options & CO_CHECKOUTMODE && + (len == 0 || pat[len - 1] != '*')) { + /* We must modify the pattern so that it + refers to the RCS file, rather than + the checked-out file. */ + xasprintf(&pat, "%s,v", pat); + fileaccept = globtree_or(fileaccept, + globtree_match(pat, flags)); + free(pat); + } else { + fileaccept = globtree_or(fileaccept, + globtree_match(pat, flags)); + } + } + } + + for (i = 0; i < pattlist_size(coll->co_refusals); i++) { + pat = pattlist_get(coll->co_refusals, i); + dirrefuse = globtree_or(dirrefuse, + globtree_match(pat, 0)); + len = strlen(pat); + if (coll->co_options & CO_CHECKOUTMODE && + (len == 0 || pat[len - 1] != '*')) { + /* We must modify the pattern so that it refers + to the RCS file, rather than the checked-out + file. */ + xasprintf(&pat, "%s,v", pat); + filerefuse = globtree_or(filerefuse, + globtree_match(pat, 0)); + free(pat); + } else { + filerefuse = globtree_or(filerefuse, + globtree_match(pat, 0)); + } + } + + coll->co_dirfilter = globtree_and(diraccept, + globtree_not(dirrefuse)); + coll->co_filefilter = globtree_and(fileaccept, + globtree_not(filerefuse)); + + /* Set up a mask of file attributes that we don't want to sync + with the server. */ + if (!(coll->co_options & CO_SETOWNER)) + coll->co_attrignore |= FA_OWNER | FA_GROUP; + if (!(coll->co_options & CO_SETMODE)) + coll->co_attrignore |= FA_MODE; + if (!(coll->co_options & CO_SETFLAGS)) + coll->co_attrignore |= FA_FLAGS; + } + return (STATUS_SUCCESS); +bad: + lprintf(-1, "Protocol error during collection exchange\n"); + return (STATUS_FAILURE); +} + +static struct mux * +proto_mux(struct config *config) +{ + struct mux *m; + struct stream *s, *wr; + struct chan *chan0, *chan1; + int id; + + s = config->server; + lprintf(2, "Establishing multiplexed-mode data connection\n"); + proto_printf(s, "MUX\n"); + stream_flush(s); + m = mux_open(config->socket, &chan0); + if (m == NULL) { + lprintf(-1, "Cannot open the multiplexer\n"); + return (NULL); + } + id = chan_listen(m); + if (id == -1) { + lprintf(-1, "ChannelMux.Listen failed: %s\n", strerror(errno)); + mux_close(m); + return (NULL); + } + wr = stream_open(chan0, NULL, (stream_writefn_t *)chan_write, NULL); + proto_printf(wr, "CHAN %d\n", id); + stream_close(wr); + chan1 = chan_accept(m, id); + if (chan1 == NULL) { + lprintf(-1, "ChannelMux.Accept failed: %s\n", strerror(errno)); + mux_close(m); + return (NULL); + } + config->chan0 = chan0; + config->chan1 = chan1; + return (m); +} + +/* + * Initializes the connection to the CVSup server, that is handle + * the protocol negotiation, logging in, exchanging file attributes + * support and collections information, and finally run the update + * session. + */ +int +proto_run(struct config *config) +{ + struct thread_args lister_args; + struct thread_args detailer_args; + struct thread_args updater_args; + struct thread_args *args; + struct killer killer; + struct threads *workers; + struct mux *m; + int i, status; + + /* + * We pass NULL for the close() function because we'll reuse + * the socket after the stream is closed. + */ + config->server = stream_open_fd(config->socket, stream_read_fd, + stream_write_fd, NULL); + status = proto_greet(config); + if (status == STATUS_SUCCESS) + status = proto_negproto(config); + if (status == STATUS_SUCCESS) + status = auth_login(config); + if (status == STATUS_SUCCESS) + status = proto_fileattr(config); + if (status == STATUS_SUCCESS) + status = proto_xchgcoll(config); + if (status != STATUS_SUCCESS) + return (status); + + /* Multi-threaded action starts here. */ + m = proto_mux(config); + if (m == NULL) + return (STATUS_FAILURE); + + stream_close(config->server); + config->server = NULL; + config->fixups = fixups_new(); + killer_start(&killer, m); + + /* Start the worker threads. */ + workers = threads_new(); + args = &lister_args; + args->config = config; + args->status = -1; + args->errmsg = NULL; + args->rd = NULL; + args->wr = stream_open(config->chan0, + NULL, (stream_writefn_t *)chan_write, NULL); + threads_create(workers, lister, args); + + args = &detailer_args; + args->config = config; + args->status = -1; + args->errmsg = NULL; + args->rd = stream_open(config->chan0, + (stream_readfn_t *)chan_read, NULL, NULL); + args->wr = stream_open(config->chan1, + NULL, (stream_writefn_t *)chan_write, NULL); + threads_create(workers, detailer, args); + + args = &updater_args; + args->config = config; + args->status = -1; + args->errmsg = NULL; + args->rd = stream_open(config->chan1, + (stream_readfn_t *)chan_read, NULL, NULL); + args->wr = NULL; + threads_create(workers, updater, args); + + lprintf(2, "Running\n"); + /* Wait for all the worker threads to finish. */ + status = STATUS_SUCCESS; + for (i = 0; i < 3; i++) { + args = threads_wait(workers); + if (args->rd != NULL) + stream_close(args->rd); + if (args->wr != NULL) + stream_close(args->wr); + if (args->status != STATUS_SUCCESS) { + assert(args->errmsg != NULL); + if (status == STATUS_SUCCESS) { + status = args->status; + /* Shutdown the multiplexer to wake up all + the other threads. */ + mux_shutdown(m, args->errmsg, status); + } + free(args->errmsg); + } + } + threads_free(workers); + if (status == STATUS_SUCCESS) { + lprintf(2, "Shutting down connection to server\n"); + chan_close(config->chan0); + chan_close(config->chan1); + chan_wait(config->chan0); + chan_wait(config->chan1); + mux_shutdown(m, NULL, STATUS_SUCCESS); + } + killer_stop(&killer); + fixups_free(config->fixups); + status = mux_close(m); + if (status == STATUS_SUCCESS) { + lprintf(1, "Finished successfully\n"); + } else if (status == STATUS_INTERRUPTED) { + lprintf(-1, "Interrupted\n"); + if (killer.killedby != -1) + kill(getpid(), killer.killedby); + } + return (status); +} + +/* + * Write a string into the stream, escaping characters as needed. + * Characters escaped: + * + * SPACE -> "\_" + * TAB -> "\t" + * NEWLINE -> "\n" + * CR -> "\r" + * \ -> "\\" + */ +static int +proto_escape(struct stream *wr, const char *s) +{ + size_t len; + ssize_t n; + char c; + + /* Handle characters that need escaping. */ + do { + len = strcspn(s, " \t\r\n\\"); + n = stream_write(wr, s, len); + if (n == -1) + return (-1); + c = s[len]; + switch (c) { + case ' ': + n = stream_write(wr, "\\_", 2); + break; + case '\t': + n = stream_write(wr, "\\t", 2); + break; + case '\r': + n = stream_write(wr, "\\r", 2); + break; + case '\n': + n = stream_write(wr, "\\n", 2); + break; + case '\\': + n = stream_write(wr, "\\\\", 2); + break; + } + if (n == -1) + return (-1); + s += len + 1; + } while (c != '\0'); + return (0); +} + +/* + * A simple printf() implementation specifically tailored for csup. + * List of the supported formats: + * + * %c Print a char. + * %d or %i Print an int as decimal. + * %x Print an int as hexadecimal. + * %o Print an int as octal. + * %t Print a time_t as decimal. + * %s Print a char * escaping some characters as needed. + * %S Print a char * without escaping. + * %f Print an encoded struct fattr *. + * %F Print an encoded struct fattr *, specifying the supported + * attributes. + */ +int +proto_printf(struct stream *wr, const char *format, ...) +{ + fattr_support_t *support; + long long longval; + struct fattr *fa; + const char *fmt; + va_list ap; + char *cp, *s, *attr; + ssize_t n; + size_t size; + off_t off; + int rv, val, ignore; + char c; + + n = 0; + rv = 0; + fmt = format; + va_start(ap, format); + while ((cp = strchr(fmt, '%')) != NULL) { + if (cp > fmt) { + n = stream_write(wr, fmt, cp - fmt); + if (n == -1) + return (-1); + } + if (*++cp == '\0') + goto done; + switch (*cp) { + case 'c': + c = va_arg(ap, int); + rv = stream_printf(wr, "%c", c); + break; + case 'd': + case 'i': + val = va_arg(ap, int); + rv = stream_printf(wr, "%d", val); + break; + case 'x': + val = va_arg(ap, int); + rv = stream_printf(wr, "%x", val); + break; + case 'o': + val = va_arg(ap, int); + rv = stream_printf(wr, "%o", val); + break; + case 'O': + off = va_arg(ap, off_t); + rv = stream_printf(wr, "%llu", off); + break; + case 'S': + s = va_arg(ap, char *); + assert(s != NULL); + rv = stream_printf(wr, "%s", s); + break; + case 's': + s = va_arg(ap, char *); + assert(s != NULL); + rv = proto_escape(wr, s); + break; + case 't': + longval = (long long)va_arg(ap, time_t); + rv = stream_printf(wr, "%lld", longval); + break; + case 'f': + fa = va_arg(ap, struct fattr *); + attr = fattr_encode(fa, NULL, 0); + rv = proto_escape(wr, attr); + free(attr); + break; + case 'F': + fa = va_arg(ap, struct fattr *); + support = va_arg(ap, fattr_support_t *); + ignore = va_arg(ap, int); + attr = fattr_encode(fa, *support, ignore); + rv = proto_escape(wr, attr); + free(attr); + break; + case 'z': + size = va_arg(ap, size_t); + rv = stream_printf(wr, "%zu", size); + break; + + case '%': + n = stream_write(wr, "%", 1); + if (n == -1) + return (-1); + break; + } + if (rv == -1) + return (-1); + fmt = cp + 1; + } + if (*fmt != '\0') { + rv = stream_printf(wr, "%s", fmt); + if (rv == -1) + return (-1); + } +done: + va_end(ap); + return (0); +} + +/* + * Unescape the string, see proto_escape(). + */ +static void +proto_unescape(char *s) +{ + char *cp, *cp2; + + cp = s; + while ((cp = strchr(cp, '\\')) != NULL) { + switch (cp[1]) { + case '_': + *cp = ' '; + break; + case 't': + *cp = '\t'; + break; + case 'r': + *cp = '\r'; + break; + case 'n': + *cp = '\n'; + break; + case '\\': + *cp = '\\'; + break; + default: + *cp = *(cp + 1); + } + cp2 = ++cp; + while (*cp2 != '\0') { + *cp2 = *(cp2 + 1); + cp2++; + } + } +} + +/* + * Get an ascii token in the string. + */ +char * +proto_get_ascii(char **s) +{ + char *ret; + + ret = strsep(s, " "); + if (ret == NULL) + return (NULL); + /* Make sure we disallow 0-length fields. */ + if (*ret == '\0') { + *s = NULL; + return (NULL); + } + proto_unescape(ret); + return (ret); +} + +/* + * Get the rest of the string. + */ +char * +proto_get_rest(char **s) +{ + char *ret; + + if (s == NULL) + return (NULL); + ret = *s; + proto_unescape(ret); + *s = NULL; + return (ret); +} + +/* + * Get an int token. + */ +int +proto_get_int(char **s, int *val, int base) +{ + char *cp; + int error; + + cp = proto_get_ascii(s); + if (cp == NULL) + return (-1); + error = asciitoint(cp, val, base); + return (error); +} + +/* + * Get a size_t token. + */ +int +proto_get_sizet(char **s, size_t *val, int base) +{ + unsigned long long tmp; + char *cp, *end; + + cp = proto_get_ascii(s); + if (cp == NULL) + return (-1); + errno = 0; + tmp = strtoll(cp, &end, base); + if (errno || *end != '\0') + return (-1); + *val = (size_t)tmp; + return (0); +} + +/* + * Get a time_t token. + * + * Ideally, we would use an intmax_t and strtoimax() here, but strtoll() + * is more portable and 64bits should be enough for a timestamp. + */ +int +proto_get_time(char **s, time_t *val) +{ + long long tmp; + char *cp, *end; + + cp = proto_get_ascii(s); + if (cp == NULL) + return (-1); + errno = 0; + tmp = strtoll(cp, &end, 10); + if (errno || *end != '\0') + return (-1); + *val = (time_t)tmp; + return (0); +} + +/* Start the killer thread. It is used to protect against some signals + during the multi-threaded run so that we can gracefully fail. */ +static void +killer_start(struct killer *k, struct mux *m) +{ + int error; + + k->mux = m; + k->killedby = -1; + sigemptyset(&k->sigset); + sigaddset(&k->sigset, SIGINT); + sigaddset(&k->sigset, SIGHUP); + sigaddset(&k->sigset, SIGTERM); + sigaddset(&k->sigset, SIGPIPE); + pthread_sigmask(SIG_BLOCK, &k->sigset, NULL); + error = pthread_create(&k->thread, NULL, killer_run, k); + if (error) + err(1, "pthread_create"); +} + +/* The main loop of the killer thread. */ +static void * +killer_run(void *arg) +{ + struct killer *k; + int error, sig, old; + + k = arg; +again: + error = sigwait(&k->sigset, &sig); + assert(!error); + if (sig == SIGINT || sig == SIGHUP || sig == SIGTERM) { + if (k->killedby == -1) { + k->killedby = sig; + /* Ensure we don't get canceled during the shutdown. */ + pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old); + mux_shutdown(k->mux, "Cleaning up ...", + STATUS_INTERRUPTED); + pthread_setcancelstate(old, NULL); + } + } + goto again; +} + +/* Stop the killer thread. */ +static void +killer_stop(struct killer *k) +{ + void *val; + int error; + + error = pthread_cancel(k->thread); + assert(!error); + pthread_join(k->thread, &val); + assert(val == PTHREAD_CANCELED); + pthread_sigmask(SIG_UNBLOCK, &k->sigset, NULL); +} diff --git a/usr.bin/csup/proto.h b/usr.bin/csup/proto.h new file mode 100644 index 0000000..0c4a782 --- /dev/null +++ b/usr.bin/csup/proto.h @@ -0,0 +1,50 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _PROTO_H_ +#define _PROTO_H_ + +#include <time.h> + +#include "misc.h" + +#define PROTO_MAJ 17 +#define PROTO_MIN 0 +#define PROTO_SWVER "CSUP_1_0" + +struct stream; + +int proto_connect(struct config *, int, uint16_t); +int proto_run(struct config *); +int proto_printf(struct stream *, const char *, ...); +char *proto_get_ascii(char **); +char *proto_get_rest(char **); +int proto_get_int(char **, int *, int); +int proto_get_sizet(char **, size_t *, int); +int proto_get_time(char **, time_t *); + +#endif /* !_PROTO_H_ */ diff --git a/usr.bin/csup/queue.h b/usr.bin/csup/queue.h new file mode 100644 index 0000000..aa9cac1 --- /dev/null +++ b/usr.bin/csup/queue.h @@ -0,0 +1,227 @@ +/*- + * 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. + * 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. + * + * @(#)queue.h 8.5 (Berkeley) 8/20/94 + * $FreeBSD$ + * + * $FreeBSD$ + */ + +#ifndef _QUEUE_H_ +#define _QUEUE_H_ + +#undef __offsetof +#define __offsetof(type, field) ((size_t)(&((type *)0)->field)) + +/* + * Singly-linked Tail queue declarations. + */ +#undef STAILQ_HEAD +#define STAILQ_HEAD(name, type) \ +struct name { \ + struct type *stqh_first;/* first element */ \ + struct type **stqh_last;/* addr of last next element */ \ +} + +#undef STAILQ_HEAD_INITIALIZER +#define STAILQ_HEAD_INITIALIZER(head) \ + { NULL, &(head).stqh_first } + +#undef STAILQ_ENTRY +#define STAILQ_ENTRY(type) \ +struct { \ + struct type *stqe_next; /* next element */ \ +} + +#undef STAILQ_EMPTY +#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL) + +#undef STAILQ_FIRST +#define STAILQ_FIRST(head) ((head)->stqh_first) + +#undef STAILQ_FOREACH +#define STAILQ_FOREACH(var, head, field) \ + for((var) = STAILQ_FIRST((head)); \ + (var); \ + (var) = STAILQ_NEXT((var), field)) + +#undef STAILQ_FOREACH_SAFE +#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = STAILQ_FIRST((head)); \ + (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \ + (var) = (tvar)) + +#undef STAILQ_INIT +#define STAILQ_INIT(head) do { \ + STAILQ_FIRST((head)) = NULL; \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#undef STAILQ_INSERT_AFTER +#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_NEXT((tqelm), field) = (elm); \ +} while (0) + +#undef STAILQ_INSERT_HEAD +#define STAILQ_INSERT_HEAD(head, elm, field) do { \ + if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ + STAILQ_FIRST((head)) = (elm); \ +} while (0) + +#undef STAILQ_INSERT_TAIL +#define STAILQ_INSERT_TAIL(head, elm, field) do { \ + STAILQ_NEXT((elm), field) = NULL; \ + *(head)->stqh_last = (elm); \ + (head)->stqh_last = &STAILQ_NEXT((elm), field); \ +} while (0) + +#undef STAILQ_LAST +#define STAILQ_LAST(head, type, field) \ + (STAILQ_EMPTY((head)) ? \ + NULL : \ + ((struct type *)(void *) \ + ((char *)((head)->stqh_last) - __offsetof(struct type, field)))) + +#undef STAILQ_NEXT +#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next) + +#undef STAILQ_REMOVE +#define STAILQ_REMOVE(head, elm, type, field) do { \ + if (STAILQ_FIRST((head)) == (elm)) { \ + STAILQ_REMOVE_HEAD((head), field); \ + } \ + else { \ + struct type *curelm = STAILQ_FIRST((head)); \ + while (STAILQ_NEXT(curelm, field) != (elm)) \ + curelm = STAILQ_NEXT(curelm, field); \ + if ((STAILQ_NEXT(curelm, field) = \ + STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\ + (head)->stqh_last = &STAILQ_NEXT((curelm), field);\ + } \ +} while (0) + +#undef STAILQ_REMOVE_HEAD +#define STAILQ_REMOVE_HEAD(head, field) do { \ + if ((STAILQ_FIRST((head)) = \ + STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +#undef STAILQ_REMOVE_HEAD_UNTIL +#define STAILQ_REMOVE_HEAD_UNTIL(head, elm, field) do { \ + if ((STAILQ_FIRST((head)) = STAILQ_NEXT((elm), field)) == NULL) \ + (head)->stqh_last = &STAILQ_FIRST((head)); \ +} while (0) + +/* + * List declarations. + */ +#undef LIST_HEAD +#define LIST_HEAD(name, type) \ +struct name { \ + struct type *lh_first; /* first element */ \ +} + +#undef LIST_HEAD_INITIALIZER +#define LIST_HEAD_INITIALIZER(head) \ + { NULL } + +#undef LIST_ENTRY +#define LIST_ENTRY(type) \ +struct { \ + struct type *le_next; /* next element */ \ + struct type **le_prev; /* address of previous next element */ \ +} + +/* + * List functions. + */ + +#undef LIST_EMPTY +#define LIST_EMPTY(head) ((head)->lh_first == NULL) + +#undef LIST_FIRST +#define LIST_FIRST(head) ((head)->lh_first) + +#undef LIST_FOREACH +#define LIST_FOREACH(var, head, field) \ + for ((var) = LIST_FIRST((head)); \ + (var); \ + (var) = LIST_NEXT((var), field)) + +#undef LIST_FOREACH_SAFE +#define LIST_FOREACH_SAFE(var, head, field, tvar) \ + for ((var) = LIST_FIRST((head)); \ + (var) && ((tvar) = LIST_NEXT((var), field), 1); \ + (var) = (tvar)) + +#undef LIST_INIT +#define LIST_INIT(head) do { \ + LIST_FIRST((head)) = NULL; \ +} while (0) + +#undef LIST_INSERT_AFTER +#define LIST_INSERT_AFTER(listelm, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\ + LIST_NEXT((listelm), field)->field.le_prev = \ + &LIST_NEXT((elm), field); \ + LIST_NEXT((listelm), field) = (elm); \ + (elm)->field.le_prev = &LIST_NEXT((listelm), field); \ +} while (0) + +#undef LIST_INSERT_BEFORE +#define LIST_INSERT_BEFORE(listelm, elm, field) do { \ + (elm)->field.le_prev = (listelm)->field.le_prev; \ + LIST_NEXT((elm), field) = (listelm); \ + *(listelm)->field.le_prev = (elm); \ + (listelm)->field.le_prev = &LIST_NEXT((elm), field); \ +} while (0) + +#undef LIST_INSERT_HEAD +#define LIST_INSERT_HEAD(head, elm, field) do { \ + if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \ + LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\ + LIST_FIRST((head)) = (elm); \ + (elm)->field.le_prev = &LIST_FIRST((head)); \ +} while (0) + +#undef LIST_NEXT +#define LIST_NEXT(elm, field) ((elm)->field.le_next) + +#undef LIST_REMOVE +#define LIST_REMOVE(elm, field) do { \ + if (LIST_NEXT((elm), field) != NULL) \ + LIST_NEXT((elm), field)->field.le_prev = \ + (elm)->field.le_prev; \ + *(elm)->field.le_prev = LIST_NEXT((elm), field); \ +} while (0) + +#endif /* !_QUEUE_H_ */ diff --git a/usr.bin/csup/rcsfile.c b/usr.bin/csup/rcsfile.c new file mode 100644 index 0000000..1f3ede1 --- /dev/null +++ b/usr.bin/csup/rcsfile.c @@ -0,0 +1,1412 @@ +/*- + * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "diff.h" +#include "keyword.h" +#include "misc.h" +#include "proto.h" +#include "queue.h" +#include "rcsfile.h" +#include "rcsparse.h" +#include "stream.h" + +#define BUF_SIZE_DEFAULT 128 + +/* + * RCS parser library. This is the part of the library that handles the + * importing, editing and exporting of RCS files. It currently supports only the + * part of the RCS file specification that is needed for csup (for instance, + * newphrases are not supported), and assumes that you can store the whole RCS + * file in memory. + */ + +/* + * Linked list for string tokens. + */ +struct string { + char *str; + STAILQ_ENTRY(string) string_next; +}; + +/* + * Linked list of tags and revision numbers, in the RCS file header. + */ +struct tag { + char *tag; + char *revnum; + STAILQ_ENTRY(tag) tag_next; +}; + +/* + * A RCS delta. The delta is identified by a revision number, and contains the + * most important RCS attributes that is needed by csup. It also contains + * pointers to other nodes in the RCS file delta structure. + */ +struct delta { + char *revdate; + char *revnum; + char *author; + char *state; + struct buf *log; + struct buf *text; + int placeholder; + struct delta *diffbase; + struct delta *prev; + + LIST_ENTRY(delta) delta_next; + STAILQ_ENTRY(delta) delta_prev; + LIST_ENTRY(delta) table_next; + STAILQ_ENTRY(delta) stack_next; + LIST_HEAD(, branch) branchlist; + LIST_ENTRY(delta) branch_next_date; +}; + +/* + * A branch data structure containing information about deltas in the branch as + * well as a base revision number. + */ +struct branch { + char *revnum; + LIST_HEAD(, delta) deltalist; /* Next delta in our branch. */ + LIST_ENTRY(branch) branch_next; +}; + +/* + * The rcsfile structure is the "main" structure of the RCS parser library. It + * contains administrative data as well as pointers to the deltas within the + * file. + */ +struct rcsfile { + char *name; + char *head; + char *branch; /* Default branch. */ + char *cvsroot; + char *colltag; + STAILQ_HEAD(, string) accesslist; + STAILQ_HEAD(, tag) taglist; + int strictlock; + char *comment; + int expand; + int ro; + struct branch *trunk; /* The tip delta. */ + + LIST_HEAD(, delta) deltatable; + + char *desc; +}; + +static void rcsfile_freedelta(struct delta *); +static void rcsfile_insertdelta(struct branch *, struct delta *, + int); +static struct delta *rcsfile_createdelta(char *); +static int rcsfile_write_deltatext(struct rcsfile *, + struct stream *); +static int rcsfile_puttext(struct rcsfile *, struct stream *, + struct delta *, struct delta *); +static struct branch *rcsfile_getbranch(struct rcsfile *, char *); +static void rcsfile_insertsorteddelta(struct rcsfile *, + struct delta *); +static struct stream *rcsfile_getdeltatext(struct rcsfile *, struct delta *, + struct buf **); +static int rcsdelta_writestring(char *, size_t, struct stream *); +static void rcsdelta_insertbranch(struct delta *, struct branch *); + +/* Space formatting of RCS file. */ +const char *head_space = "\t"; +const char *branch_space = "\t"; +const char *tag_space = "\t"; +const char *date_space = "\t"; +const char *auth_space = "\t"; +const char *state_space = "\t"; +const char *next_space = "\t"; +const char *branches_space = "\t"; +const char *comment_space ="\t"; +const char *expand_space = "\t"; + +void print_stream(struct stream *); + +/* Print the contents of a stream, for debugging. */ +void +print_stream(struct stream *s) +{ + char *line; + + line = stream_getln(s, NULL); + while (line != NULL) { + lprintf(-1, "%s\n", line); + line = stream_getln(s, NULL); + } + lprintf(-1, "\n"); +} + +/* + * Parse rcsfile from path and return a pointer to it. + */ +struct rcsfile * +rcsfile_frompath(char *path, char *name, char *cvsroot, char *colltag, int ro) +{ + struct rcsfile *rf; + FILE *infp; + int error; + + if (path == NULL || name == NULL || cvsroot == NULL || colltag == NULL) + return (NULL); + + rf = xmalloc(sizeof(struct rcsfile)); + rf->name = xstrdup(name); + rf->cvsroot = xstrdup(cvsroot); + rf->colltag = xstrdup(colltag); + + /* Initialize head branch. */ + rf->trunk = xmalloc(sizeof(struct branch)); + rf->trunk->revnum = xstrdup("1"); + LIST_INIT(&rf->trunk->deltalist); + /* Initialize delta list. */ + LIST_INIT(&rf->deltatable); + /* Initialize tag list. */ + STAILQ_INIT(&rf->taglist); + /* Initialize accesslist. */ + STAILQ_INIT(&rf->accesslist); + + /* Initialize all fields. */ + rf->head = NULL; + rf->branch = NULL; + rf->strictlock = 0; + rf->comment = NULL; + rf->expand = EXPAND_DEFAULT; + rf->desc = NULL; + rf->ro = ro; + + infp = fopen(path, "r"); + if (infp == NULL) { + lprintf(-1, "Cannot open \"%s\": %s\n", path, strerror(errno)); + rcsfile_free(rf); + return (NULL); + } + error = rcsparse_run(rf, infp, ro); + fclose(infp); + if (error) { + lprintf(-1, "Error parsing \"%s\"\n", name); + rcsfile_free(rf); + return (NULL); + } + return (rf); +} + +/* + * Write content of rcsfile to server. Assumes we have a complete RCS file + * loaded. + */ +int +rcsfile_send_details(struct rcsfile *rf, struct stream *wr) +{ + struct delta *d; + struct tag *t; + const char *keyword; + int error; + + assert(rf != NULL); + + error = proto_printf(wr, "V %s\n", rf->name); + if (error) + return(error); + + /* Write default branch. */ + if (rf->branch == NULL) + error = proto_printf(wr, "b\n"); + else + error = proto_printf(wr, "B %s\n", rf->branch); + if (error) + return(error); + + /* Write deltas to server. */ + error = proto_printf(wr, "D\n"); + if (error) + return(error); + + LIST_FOREACH(d, &rf->deltatable, table_next) { + error = proto_printf(wr, "%s %s\n", d->revnum, d->revdate); + if (error) + return(error); + } + error = proto_printf(wr, ".\n"); + + if (error) + return(error); + /* Write expand. */ + if (rf->expand != EXPAND_DEFAULT) { + keyword = keyword_encode_expand(rf->expand); + if (keyword != NULL) { + error = proto_printf(wr, "E %s\n", + keyword_encode_expand(rf->expand)); + if (error) + return(error); + } + } + + /* Write tags to server. */ + error = proto_printf(wr, "T\n"); + if (error) + return(error); + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + error = proto_printf(wr, "%s %s\n", t->tag, t->revnum); + if (error) + return(error); + } + error = proto_printf(wr, ".\n"); + if (error) + return(error); + error = proto_printf(wr, ".\n"); + return (error); +} + +/* + * Write a RCS file to disk represented by the destination stream. Keep track of + * deltas with a stack and an inverted stack. + */ +int +rcsfile_write(struct rcsfile *rf, struct stream *dest) +{ + STAILQ_HEAD(, delta) deltastack; + STAILQ_HEAD(, delta) deltalist_inverted; + struct tag *t; + struct branch *b; + struct delta *d, *d_tmp, *d_next; + int error; + + /* First write head. */ + d = LIST_FIRST(&rf->trunk->deltalist); + if (stream_printf(dest, "head%s%s;\n", head_space, d->revnum) < 0) + return (-1); + + /* Write branch, if we have. */ + if (rf->branch != NULL) { + if (stream_printf(dest, "branch%s%s;\n", branch_space, + rf->branch) < 0) + return (-1); + } + + /* Write access. */ + if (stream_printf(dest, "access") < 0) + return (-1); +#if 0 + if (!STAILQ_EMPTY(&rf->accesslist)) { + /* + * XXX: Write out access. This doesn't seem to be necessary for + * the time being. + */ + } +#endif + if (stream_printf(dest, ";\n") < 0) + return (-1); + + /* Write out taglist. */ + if (stream_printf(dest, "symbols") < 0) + return (-1); + if (!STAILQ_EMPTY(&rf->taglist)) { + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + if (stream_printf(dest, "\n%s%s:%s", tag_space, t->tag, + t->revnum) < 0) + return (-1); + } + } + + /* Write out locks and strict. */ + if (stream_printf(dest, ";\nlocks;") < 0) + return (-1); + if (rf->strictlock) { + if (stream_printf(dest, " strict;") < 0) + return (-1); + } + if (stream_printf(dest, "\n") < 0) + return (-1); + + /* Write out the comment. */ + if (rf->comment != NULL) { + if (stream_printf(dest, "comment%s%s;\n", comment_space, + rf->comment) < 0) + return (-1); + } + if (rf->expand != EXPAND_DEFAULT) { + if (stream_printf(dest, "expand%s@%s@;\n", expand_space, + keyword_encode_expand(rf->expand)) < 0) + return (-1); + } + + if (stream_printf(dest, "\n\n") < 0) + return (-1); + + /* + * Write out deltas. We use a stack where we push the appropriate deltas + * that is to be written out during the loop. + */ + STAILQ_INIT(&deltastack); + d = LIST_FIRST(&rf->trunk->deltalist); + STAILQ_INSERT_HEAD(&deltastack, d, stack_next); + while (!STAILQ_EMPTY(&deltastack)) { + d = STAILQ_FIRST(&deltastack); + STAILQ_REMOVE_HEAD(&deltastack, stack_next); + /* Do not write out placeholders just to be safe. */ + if (d->placeholder) + continue; + if (stream_printf(dest, "%s\n", d->revnum) < 0) + return (-1); + if (stream_printf(dest, "date%s%s;%sauthor %s;%sstate", + date_space, d->revdate, auth_space, d->author, + state_space) < 0) + return (-1); + if (d->state != NULL) { + if (stream_printf(dest, " %s", d->state) < 0) + return (-1); + } + if (stream_printf(dest, ";\nbranches") < 0) + return (-1); + /* + * Write out our branches. Add them to a reversed list for use + * later when we write out the text. + */ + STAILQ_INIT(&deltalist_inverted); + LIST_FOREACH(b, &d->branchlist, branch_next) { + d_tmp = LIST_FIRST(&b->deltalist); + STAILQ_INSERT_HEAD(&deltalist_inverted, d_tmp, delta_prev); + STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); + } + + /* Push branch heads on stack. */ + STAILQ_FOREACH(d_tmp, &deltalist_inverted, delta_prev) { + if (d_tmp == NULL) { + lprintf(2, "Empty branch!\n"); + return (-1); + } + if (stream_printf(dest, "\n%s%s", branches_space, + d_tmp->revnum) < 0) + return (-1); + } + + if (stream_printf(dest, ";\nnext%s", next_space) < 0) + return (-1); + /* Push next delta on stack. */ + d_next = LIST_NEXT(d, delta_next); + if (d_next != NULL) { + if (stream_printf(dest, "%s", d_next->revnum) < 0) + return (-1); + STAILQ_INSERT_HEAD(&deltastack, d_next, stack_next); + } + if (stream_printf(dest, ";\n\n") < 0) + return (-1); + } + /* Write out desc. */ + if (stream_printf(dest, "\ndesc\n@@") < 0) + return (-1); + d = LIST_FIRST(&rf->trunk->deltalist); + + /* Write out deltatexts. */ + error = rcsfile_write_deltatext(rf, dest); + if (stream_printf(dest, "\n") < 0) + return (-1); + return (error); +} + +/* + * Write out deltatexts of a delta and it's subbranches recursively. + */ +int +rcsfile_write_deltatext(struct rcsfile *rf, struct stream *dest) +{ + STAILQ_HEAD(, delta) deltastack; + LIST_HEAD(, delta) branchlist_datesorted; + struct delta *d, *d_tmp, *d_next, *d_tmp2, *d_tmp3; + struct stream *in; + struct branch *b; + size_t size; + char *line; + int error; + + error = 0; + STAILQ_INIT(&deltastack); + d = LIST_FIRST(&rf->trunk->deltalist); + d->prev = NULL; + STAILQ_INSERT_HEAD(&deltastack, d, stack_next); + while (!STAILQ_EMPTY(&deltastack)) { + d = STAILQ_FIRST(&deltastack); + STAILQ_REMOVE_HEAD(&deltastack, stack_next); + /* Do not write out placeholders just to be safe. */ + if (d->placeholder) + return (0); + if (stream_printf(dest, "\n\n\n%s\n", d->revnum) < 0) + return (-1); + if (stream_printf(dest, "log\n@") < 0) + return (-1); + in = stream_open_buf(d->log); + line = stream_getln(in, &size); + while (line != NULL) { + if (stream_write(dest, line, size) == -1) + return (-1); + line = stream_getln(in, &size); + } + stream_close(in); + if (stream_printf(dest, "@\ntext\n@") < 0) + return (-1); + error = rcsfile_puttext(rf, dest, d, d->prev); + if (error) + return (error); + if (stream_printf(dest, "@") < 0) + return (-1); + + LIST_INIT(&branchlist_datesorted); + d_next = LIST_NEXT(d, delta_next); + if (d_next != NULL) { + d_next->prev = d; + /* + * If it's trunk, treat it like the oldest, if not treat + * it like a child. + */ + if (rcsrev_istrunk(d_next->revnum)) + STAILQ_INSERT_HEAD(&deltastack, d_next, + stack_next); + else + LIST_INSERT_HEAD(&branchlist_datesorted, d_next, + branch_next_date); + } + + /* + * First, we need to sort our branches based on their date to + * take into account some self-hacked RCS files. + */ + LIST_FOREACH(b, &d->branchlist, branch_next) { + d_tmp = LIST_FIRST(&b->deltalist); + if (LIST_EMPTY(&branchlist_datesorted)) { + LIST_INSERT_HEAD(&branchlist_datesorted, d_tmp, + branch_next_date); + continue; + } + + d_tmp2 = LIST_FIRST(&branchlist_datesorted); + if (rcsnum_cmp(d_tmp->revdate, d_tmp2->revdate) <= 0) { + LIST_INSERT_BEFORE(d_tmp2, d_tmp, + branch_next_date); + continue; + } + while ((d_tmp3 = LIST_NEXT(d_tmp2, branch_next_date)) + != NULL) { + if (rcsnum_cmp(d_tmp->revdate, d_tmp3->revdate) + <= 0) + break; + d_tmp2 = d_tmp3; + } + LIST_INSERT_AFTER(d_tmp2, d_tmp, branch_next_date); + } + /* + * Invert the deltalist of a branch, since we're writing them + * the opposite way. + */ + LIST_FOREACH(d_tmp, &branchlist_datesorted, branch_next_date) { + d_tmp->prev = d; + STAILQ_INSERT_HEAD(&deltastack, d_tmp, stack_next); + } + } + return (0); +} + +/* + * Generates text given a delta and a diffbase. + */ +static int +rcsfile_puttext(struct rcsfile *rf, struct stream *dest, struct delta *d, + struct delta *diffbase) +{ + struct stream *in, *rd, *orig; + struct keyword *k; + struct diffinfo dibuf, *di; + struct buf *b; + size_t size; + char *line; + int error; + + di = &dibuf; + b = NULL; + error = 0; + + /* Write if the diffbase is the previous */ + if (d->diffbase == diffbase) { + + /* Write out the text. */ + in = stream_open_buf(d->text); + line = stream_getln(in, &size); + while (line != NULL) { + if (stream_write(dest, line, size) == -1) { + error = -1; + goto cleanup; + } + line = stream_getln(in, &size); + } + stream_close(in); + /* We need to apply diff to produce text, this is probably HEAD. */ + } else if (diffbase == NULL) { + /* Apply diff. */ + orig = rcsfile_getdeltatext(rf, d, &b); + if (orig == NULL) { + error = -1; + goto cleanup; + } + line = stream_getln(orig, &size); + while (line != NULL) { + if (stream_write(dest, line, size) == -1) { + error = -1; + goto cleanup; + } + line = stream_getln(orig, &size); + } + stream_close(orig); + /* + * A new head was probably added, and now the previous HEAD must be + * changed to include the diff instead. + */ + } else if (diffbase->diffbase == d) { + /* Get reverse diff. */ + orig = rcsfile_getdeltatext(rf, d, &b); + if (orig == NULL) { + error = -1; + goto cleanup; + } + di->di_rcsfile = rf->name; + di->di_cvsroot = rf->cvsroot; + di->di_revnum = d->revnum; + di->di_revdate = d->revdate; + di->di_author = d->author; + di->di_tag = rf->colltag; + di->di_state = d->state; + di->di_expand = EXPAND_OLD; + k = keyword_new(); + + rd = stream_open_buf(diffbase->text); + error = diff_reverse(rd, orig, dest, k, di); + if (error) { + lprintf(-1, "Error applying reverse diff: %d\n", error); + goto cleanup; + } + keyword_free(k); + stream_close(rd); + stream_close(orig); + } +cleanup: + if (b != NULL) + buf_free(b); + return (error); +} + +/* + * Return a stream with an applied diff of a delta. + * XXX: extra overhead on the last apply. Could write directly to file, but + * makes things complicated though. + */ +static struct stream * +rcsfile_getdeltatext(struct rcsfile *rf, struct delta *d, struct buf **buf_dest) +{ + struct diffinfo dibuf, *di; + struct stream *orig, *dest, *rd; + struct buf *buf_orig; + struct keyword *k; + int error; + + buf_orig = NULL; + error = 0; + + /* + * If diffbase is NULL or we are head (the old head), we have a normal + * complete deltatext. + */ + if (d->diffbase == NULL && !strcmp(rf->head, d->revnum)) { + orig = stream_open_buf(d->text); + return (orig); + } + + di = &dibuf; + /* If not, we need to apply our diff to that of our diffbase. */ + orig = rcsfile_getdeltatext(rf, d->diffbase, &buf_orig); + if (orig == NULL) + return (NULL); + + /* + * Now that we are sure we have a complete deltatext in ret, let's apply + * our diff to it. + */ + *buf_dest = buf_new(BUF_SIZE_DEFAULT); + dest = stream_open_buf(*buf_dest); + + di->di_rcsfile = rf->name; + di->di_cvsroot = rf->cvsroot; + di->di_revnum = d->revnum; + di->di_revdate = d->revdate; + di->di_author = d->author; + di->di_tag = rf->colltag; + di->di_state = d->state; + di->di_expand = EXPAND_OLD; + rd = stream_open_buf(d->text); + k = keyword_new(); + error = diff_apply(rd, orig, dest, k, di, 0); + stream_flush(dest); + stream_close(rd); + stream_close(orig); + stream_close(dest); + keyword_free(k); + if (buf_orig != NULL) + buf_free(buf_orig); + if (error) { + lprintf(-1, "Error applying diff: %d\n", error); + return (NULL); + } + + /* Now reopen the stream for the reading. */ + dest = stream_open_buf(*buf_dest); + return (dest); +} + +/* Print content of rcsfile. Useful for debugging. */ +void +rcsfile_print(struct rcsfile *rf) +{ + struct delta *d; + struct tag *t; + struct string *s; + struct stream *in; + char *line; + + lprintf(1, "\n"); + if (rf->name != NULL) + lprintf(1, "name: '%s'\n", rf->name); + if (rf->head != NULL) + lprintf(1, "head: '%s'\n", rf->head); + if (rf->branch != NULL) + lprintf(1, "branch: '%s'\n", rf->branch); + lprintf(1, "Access: "); + STAILQ_FOREACH(s, &rf->accesslist, string_next) + lprintf(1, "'%s' ", s->str); + lprintf(1, "\n"); + + /* Print all tags. */ + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + lprintf(1, "Tag: "); + if (t->tag != NULL) + lprintf(1, "name: %s ", t->tag); + if (t->revnum != NULL) + lprintf(1, "rev: %s", t->revnum); + lprintf(1, "\n"); + } + + if (rf->strictlock) + lprintf(1, "Strict!\n"); + if (rf->comment != NULL) + lprintf(1, "comment: '%s'\n", rf->comment); + if (rf->expand != EXPAND_DEFAULT) + lprintf(1, "expand: '%s'\n", keyword_encode_expand(rf->expand)); + + /* Print all deltas. */ + LIST_FOREACH(d, &rf->deltatable, table_next) { + lprintf(1, "Delta: "); + if (d->revdate != NULL) + lprintf(1, "date: %s ", d->revdate); + if (d->revnum != NULL) + lprintf(1, "rev: %s", d->revnum); + if (d->author != NULL) + lprintf(1, "author: %s", d->author); + if (d->state != NULL) + lprintf(1, "state: %s", d->state); + + lprintf(1, "Text:\n"); + in = stream_open_buf(d->text); + line = stream_getln(in, NULL); + while (line != NULL) { + lprintf(1, "TEXT: %s\n", line); + line = stream_getln(in, NULL); + } + stream_close(in); + lprintf(1, "\n"); + } + + if (rf->desc != NULL) + lprintf(1, "desc: '%s'\n", rf->desc); +} + +/* Free all memory associated with a struct rcsfile. */ +void +rcsfile_free(struct rcsfile *rf) +{ + struct delta *d; + struct tag *t; + struct string *s; + + if (rf->name != NULL) + free(rf->name); + if (rf->head != NULL) + free(rf->head); + if (rf->branch != NULL) + free(rf->branch); + if (rf->cvsroot != NULL) + free(rf->cvsroot); + if (rf->colltag != NULL) + free(rf->colltag); + + /* Free all access ids. */ + while (!STAILQ_EMPTY(&rf->accesslist)) { + s = STAILQ_FIRST(&rf->accesslist); + STAILQ_REMOVE_HEAD(&rf->accesslist, string_next); + if (s->str != NULL) + free(s->str); + free(s); + } + + /* Free all tags. */ + while (!STAILQ_EMPTY(&rf->taglist)) { + t = STAILQ_FIRST(&rf->taglist); + STAILQ_REMOVE_HEAD(&rf->taglist, tag_next); + if (t->tag != NULL) + free(t->tag); + if (t->revnum != NULL) + free(t->revnum); + free(t); + } + + if (rf->comment != NULL) + free(rf->comment); + + /* Free all deltas in global list */ + while (!LIST_EMPTY(&rf->deltatable)) { + d = LIST_FIRST(&rf->deltatable); + if (!rf->ro) + LIST_REMOVE(d, delta_next); + LIST_REMOVE(d, table_next); + rcsfile_freedelta(d); + } + + /* Free global branch. */ + if (rf->trunk->revnum != NULL) + free(rf->trunk->revnum); + free(rf->trunk); + + if (rf->desc != NULL) + free(rf->desc); + + free(rf); +} + +/* + * Free a RCS delta. + */ +static void +rcsfile_freedelta(struct delta *d) +{ + struct branch *b; + + if (d->revdate != NULL) + free(d->revdate); + if (d->revnum != NULL) + free(d->revnum); + if (d->author != NULL) + free(d->author); + if (d->state != NULL) + free(d->state); + if (d->log != NULL) + buf_free(d->log); + if (d->text != NULL) + buf_free(d->text); + + /* Free all subbranches of a delta. */ + while (!LIST_EMPTY(&d->branchlist)) { + b = LIST_FIRST(&d->branchlist); + LIST_REMOVE(b, branch_next); + free(b->revnum); + free(b); + } + free(d); +} + +/* + * Functions for editing RCS deltas. + */ + +/* Add a new entry to the access list. */ +void +rcsfile_addaccess(struct rcsfile *rf, char *id) +{ + struct string *s; + + s = xmalloc(sizeof(struct string)); + s->str = xstrdup(id); + STAILQ_INSERT_TAIL(&rf->accesslist, s, string_next); +} + +/* Add a tag to a RCS file. */ +void +rcsfile_addtag(struct rcsfile *rf, char *tag, char *revnum) +{ + struct tag *t; + + t = xmalloc(sizeof(struct tag)); + t->tag = xstrdup(tag); + t->revnum = xstrdup(revnum); + + STAILQ_INSERT_HEAD(&rf->taglist, t, tag_next); +} + +/* Import a tag to a RCS file. */ +void +rcsfile_importtag(struct rcsfile *rf, char *tag, char *revnum) +{ + struct tag *t; + + t = xmalloc(sizeof(struct tag)); + t->tag = xstrdup(tag); + t->revnum = xstrdup(revnum); + + STAILQ_INSERT_TAIL(&rf->taglist, t, tag_next); +} + +/* + * Delete a revision from the global delta list and the branch it is in. Csup + * will tell us to delete the tags involved. + */ +void +rcsfile_deleterev(struct rcsfile *rf, char *revname) +{ + struct delta *d; + + d = rcsfile_getdelta(rf, revname); + if (!rf->ro) + LIST_REMOVE(d, delta_next); + LIST_REMOVE(d, table_next); + rcsfile_freedelta(d); +} + +/* Delete a tag from the tag list. */ +void +rcsfile_deletetag(struct rcsfile *rf, char *tag, char *revnum) +{ + struct tag *t; + + STAILQ_FOREACH(t, &rf->taglist, tag_next) { + if ((strcmp(tag, t->tag) == 0) && + (strcmp(revnum, t->revnum) == 0)) { + STAILQ_REMOVE(&rf->taglist, t, tag, tag_next); + free(t->tag); + free(t->revnum); + free(t); + return; + } + } +} + +/* + * Searches the global deltalist for a delta. + */ +struct delta * +rcsfile_getdelta(struct rcsfile *rf, char *revnum) +{ + struct delta *d; + + LIST_FOREACH(d, &rf->deltatable, table_next) { + if (strcmp(revnum, d->revnum) == 0) + return (d); + } + return (NULL); +} + +/* Set rcsfile head. */ +void +rcsfile_setval(struct rcsfile *rf, int field, char *val) +{ + size_t len; + + switch (field) { + case RCSFILE_HEAD: + if (rf->head != NULL) + free(rf->head); + rf->head = xstrdup(val); + break; + case RCSFILE_BRANCH: + if (rf->branch != NULL) + free(rf->branch); + rf->branch = (val == NULL) ? NULL : xstrdup(val); + break; + case RCSFILE_STRICT: + if (val != NULL) + rf->strictlock = 1; + break; + case RCSFILE_COMMENT: + if (rf->comment != NULL) + free(rf->comment); + rf->comment = xstrdup(val); + break; + case RCSFILE_EXPAND: + len = strlen(val) - 1; + val++; + val[len - 1] = '\0'; + rf->expand = keyword_decode_expand(val); + break; + case RCSFILE_DESC: + if (rf->desc != NULL) + free(rf->desc); + rf->desc = xstrdup(val); + break; + default: + lprintf(-1, "Setting invalid RCSfile value.\n"); + break; + } +} + +/* Create and initialize a delta. */ +static struct delta * +rcsfile_createdelta(char *revnum) +{ + struct delta *d; + + d = xmalloc(sizeof(struct delta)); + d->revnum = xstrdup(revnum); + d->revdate = NULL; + d->state = NULL; + d->author = NULL; + d->log = buf_new(BUF_SIZE_DEFAULT); + d->text = buf_new(BUF_SIZE_DEFAULT); + d->diffbase = NULL; + + LIST_INIT(&d->branchlist); + return (d); +} + +/* Add a delta to a imported delta tree. Used by the updater. */ +struct delta * +rcsfile_addelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, + char *diffbase) +{ + struct branch *b; + struct delta *d, *d_bp, *d_next; + char *brev, *bprev; + int trunk; + + d_next = NULL; + d = rcsfile_getdelta(rf, revnum); + if (d != NULL) { + lprintf(-1, "Delta %s already exists!\n", revnum); + return (NULL); + } + d = rcsfile_createdelta(revnum); + d->placeholder = 0; + d->revdate = xstrdup(revdate); + d->author = xstrdup(author); + d->diffbase = rcsfile_getdelta(rf, diffbase); + + /* If it's trunk, insert it in the head branch list. */ + b = rcsrev_istrunk(d->revnum) ? rf->trunk : + rcsfile_getbranch(rf, d->revnum); + + /* + * We didn't find a branch, check if we can find a branchpoint and + * create a branch there. + */ + if (b == NULL) { + brev = rcsrev_prefix(d->revnum); + bprev = rcsrev_prefix(brev); + + d_bp = rcsfile_getdelta(rf, bprev); + free(bprev); + if (d_bp == NULL) { + lprintf(-1, "No branch point for adding delta %s\n", + d->revnum); + return (NULL); + } + + /* Create the branch and insert in delta. */ + b = xmalloc(sizeof(struct branch)); + b->revnum = brev; + LIST_INIT(&b->deltalist); + rcsdelta_insertbranch(d_bp, b); + } + + /* Insert both into the tree, and into the lookup list. */ + trunk = rcsrev_istrunk(d->revnum); + rcsfile_insertdelta(b, d, trunk); + rcsfile_insertsorteddelta(rf, d); + return (d); +} + +/* Adds a delta to a rcsfile struct. Used by the parser. */ +void +rcsfile_importdelta(struct rcsfile *rf, char *revnum, char *revdate, char *author, + char *state, char *next) +{ + struct branch *b; + struct delta *d, *d_bp, *d_next; + char *brev, *bprev; + int trunk; + + d_next = NULL; + d = rcsfile_getdelta(rf, revnum); + + if (d == NULL) { + /* If not, we'll just create a new entry. */ + d = rcsfile_createdelta(revnum); + d->placeholder = 0; + } else { + if (d->placeholder == 0) { + lprintf(-1, "Trying to import already existing delta\n"); + return; + } + } + /* + * If already exists, assume that only revnum is filled out, and set the + * rest of the fields. This should be an OK assumption given that we can + * be sure internally that the structure is sufficiently initialized so + * we won't have any unfreed memory. + */ + d->revdate = xstrdup(revdate); + d->author = xstrdup(author); + if (state != NULL) + d->state = xstrdup(state); + + /* If we have a next, create a placeholder for it. */ + if (next != NULL) { + d_next = rcsfile_createdelta(next); + d_next->placeholder = 1; + /* Diffbase should be the previous. */ + d_next->diffbase = d; + } + + /* If we're opening read-only, do minimal work. */ + if (rf->ro) { + if (!d->placeholder) + rcsfile_insertsorteddelta(rf, d); + else + d->placeholder = 0; + if (d_next != NULL) + rcsfile_insertsorteddelta(rf, d_next); + return; + } + + /* If it's trunk, insert it in the head branch list. */ + b = rcsrev_istrunk(d->revnum) ? rf->trunk : rcsfile_getbranch(rf, + d->revnum); + + /* + * We didn't find a branch, check if we can find a branchpoint and + * create a branch there. + */ + if (b == NULL) { + brev = rcsrev_prefix(d->revnum); + bprev = rcsrev_prefix(brev); + + d_bp = rcsfile_getdelta(rf, bprev); + free(bprev); + if (d_bp == NULL) { + lprintf(-1, "No branch point for adding delta %s\n", + d->revnum); + return; + } + + /* Create the branch and insert in delta. */ + b = xmalloc(sizeof(struct branch)); + b->revnum = brev; + LIST_INIT(&b->deltalist); + rcsdelta_insertbranch(d_bp, b); + } + + /* Insert if not a placeholder. */ + if (!d->placeholder) { + /* Insert both into the tree, and into the lookup list. */ + if (rcsrev_istrunk(d->revnum)) + rcsfile_insertdelta(b, d, 1); + else { + rcsfile_insertdelta(b, d, 0); + /* + * On import we need to set the diffbase to our + * branchpoint for writing out later. + */ + if (LIST_FIRST(&b->deltalist) == d) { + brev = rcsrev_prefix(d->revnum); + bprev = rcsrev_prefix(brev); + d_bp = rcsfile_getdelta(rf, bprev); + /* This should really not happen. */ + assert(d_bp != NULL); + d->diffbase = d_bp; + free(brev); + free(bprev); + } + } + rcsfile_insertsorteddelta(rf, d); + } else /* Not a placeholder anymore. */ { + d->placeholder = 0; + /* Put it into the tree. */ + trunk = rcsrev_istrunk(d->revnum); + rcsfile_insertdelta(b, d, trunk); + } + + /* If we have a next, insert the placeholder into the lookup list. */ + if (d_next != NULL) + rcsfile_insertsorteddelta(rf, d_next); +} + +/* + * Find the branch of a revision number. + */ +static struct branch * +rcsfile_getbranch(struct rcsfile *rf, char *revnum) +{ + struct branch *b; + struct delta *d; + char *branchrev, *bprev; + + branchrev = rcsrev_prefix(revnum); + bprev = rcsrev_prefix(branchrev); + d = rcsfile_getdelta(rf, bprev); + free(bprev); + LIST_FOREACH(b, &d->branchlist, branch_next) { + if(rcsnum_cmp(b->revnum, branchrev) == 0) { + free(branchrev); + return (b); + } + } + free(branchrev); + return (NULL); +} + +/* Insert a branch into a delta, sorted by branch revision date. */ +static void +rcsdelta_insertbranch(struct delta *d, struct branch *b) +{ + struct branch *b_iter; + + /* If it's empty, insert into head. */ + if (LIST_EMPTY(&d->branchlist)) { + LIST_INSERT_HEAD(&d->branchlist, b, branch_next); + return; + } + + /* Just put it in before the revdate that is lower. */ + LIST_FOREACH(b_iter, &d->branchlist, branch_next) { + if (rcsnum_cmp(b->revnum, b_iter->revnum) > 0) { + LIST_INSERT_BEFORE(b_iter, b, branch_next); + return; + } + if (LIST_NEXT(b_iter, branch_next) == NULL) + break; + } + /* Insert after last element. */ + LIST_INSERT_AFTER(b_iter, b, branch_next); +} + +/* Insert a delta into the correct place in the table of the rcsfile. */ +static void +rcsfile_insertsorteddelta(struct rcsfile *rf, struct delta *d) +{ + struct delta *d2; + + /* If it's empty, insert into head. */ + if (LIST_EMPTY(&rf->deltatable)) { + LIST_INSERT_HEAD(&rf->deltatable, d, table_next); + return; + } + + /* Just put it in before the revdate that is lower. */ + LIST_FOREACH(d2, &rf->deltatable, table_next) { + if (rcsnum_cmp(d->revnum, d2->revnum) <= 0) { + LIST_INSERT_BEFORE(d2, d, table_next); + return; + } + if (LIST_NEXT(d2, table_next) == NULL) + break; + } + /* Insert after last element. */ + LIST_INSERT_AFTER(d2, d, table_next); +} + +/* + * Insert a delta into the correct place in branch. A trunk branch will have + * different ordering scheme and be sorted by revision number, but a normal + * branch will be sorted by date to maintain compability with branches that is + * "hand-hacked". + */ +static void +rcsfile_insertdelta(struct branch *b, struct delta *d, int trunk) +{ + struct delta *d2; + + /* If it's empty, insert into head. */ + if (LIST_EMPTY(&b->deltalist)) { + LIST_INSERT_HEAD(&b->deltalist, d, delta_next); + return; + } + + /* + * Just put it in before the revnum that is lower. Sort trunk branch by + * branchnum but the subbranches after deltadate. + */ + LIST_FOREACH(d2, &b->deltalist, delta_next) { + if (trunk) { + if (rcsnum_cmp(d->revnum, d2->revnum) >= 0) { + LIST_INSERT_BEFORE(d2, d, delta_next); + return; + } + } else { + /* XXX: here we depend on the date being set, but it + * should be before this is called anyway. */ + if (rcsnum_cmp(d->revnum, d2->revnum) < 0) { + LIST_INSERT_BEFORE(d2, d, delta_next); + return; + } + } + if (LIST_NEXT(d2, delta_next) == NULL) + break; + } + /* Insert after last element. */ + LIST_INSERT_AFTER(d2, d, delta_next); +} + + +/* Add logtext to a delta. Assume the delta already exists. */ +int +rcsdelta_addlog(struct delta *d, char *log, int len) +{ + struct stream *dest; + int nbytes; + + assert(d != NULL); + /* Strip away '@' at beginning and end. */ + log++; + len--; + log[len - 1] = '\0'; + dest = stream_open_buf(d->log); + nbytes = stream_write(dest, log, len - 1); + stream_close(dest); + return ((nbytes == -1) ? -1 : 0); +} + +/* Add deltatext to a delta. Assume the delta already exists. */ +int +rcsdelta_addtext(struct delta *d, char *text, int len) +{ + struct stream *dest; + int nbytes; + + assert(d != NULL); + /* Strip away '@' at beginning and end. */ + text++; + len--; + text[len - 1] = '\0'; + + dest = stream_open_buf(d->text); + nbytes = stream_write(dest, text, len - 1); + stream_close(dest); + return ((nbytes == -1) ? -1 : 0); +} + +/* Add a deltatext logline to a delta. */ +int +rcsdelta_appendlog(struct delta *d, char *logline, size_t size) +{ + struct stream *dest; + int error; + + assert(d != NULL); + dest = stream_open_buf(d->log); + error = rcsdelta_writestring(logline, size, dest); + stream_close(dest); + return (error); +} + +/* Add a deltatext textline to a delta. */ +int +rcsdelta_appendtext(struct delta *d, char *textline, size_t size) +{ + struct stream *dest; + int error; + + assert(d != NULL); + dest = stream_open_buf(d->text); + error = rcsdelta_writestring(textline, size, dest); + stream_close(dest); + return (error); +} + +static int +rcsdelta_writestring(char *textline, size_t size, struct stream *dest) +{ + char buf[3]; + size_t i; + int count; + + for (i = 0; i < size; i++) { + buf[0] = textline[i]; + buf[1] = '\0'; + count = 1; + /* Expand @'s */ + if (buf[0] == '@') { + buf[1] = '@'; + buf[2] = '\0'; + count = 2; + } + if (stream_write(dest, buf, count) == -1) + return (-1); + } + return (0); +} + +/* Set delta state. */ +void +rcsdelta_setstate(struct delta *d, char *state) +{ + + if (d->state != NULL) + free(state); + if (state != NULL) { + d->state = xstrdup(state); + return; + } + d->state = NULL; +} + +/* Truncate the deltalog with a certain offset. */ +void +rcsdelta_truncatelog(struct delta *d, off_t offset) +{ + + stream_truncate_buf(d->log, offset); +} + +/* Truncate the deltatext with a certain offset. */ +void +rcsdelta_truncatetext(struct delta *d, off_t offset) +{ + + stream_truncate_buf(d->text, offset); +} diff --git a/usr.bin/csup/rcsfile.h b/usr.bin/csup/rcsfile.h new file mode 100644 index 0000000..5fa9f31 --- /dev/null +++ b/usr.bin/csup/rcsfile.h @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _RCSFILE_H_ +#define _RCSFILE_H_ + +/* RCSFILE fields. */ +#define RCSFILE_HEAD 0 +#define RCSFILE_BRANCH 1 +#define RCSFILE_STRICT 2 +#define RCSFILE_COMMENT 3 +#define RCSFILE_EXPAND 4 +#define RCSFILE_DESC 5 + +struct rcsfile; +struct delta; +struct stream; + +/* Fetching, sending and writing an RCS file. */ +struct rcsfile *rcsfile_frompath(char *, char *, char *, char *, int); +int rcsfile_send_details(struct rcsfile *, struct stream *); +int rcsfile_write(struct rcsfile *, struct stream *); +void rcsfile_print(struct rcsfile *); +void rcsfile_free(struct rcsfile *); + +/* Used for adding and setting rcsfile values. */ +void rcsfile_addaccess(struct rcsfile *, char *); +void rcsfile_addtag(struct rcsfile *, char *, char *); +void rcsfile_importtag(struct rcsfile *, char *, char *); +void rcsfile_deleterev(struct rcsfile *, char *); +void rcsfile_deletetag(struct rcsfile *, char *, char *); +struct delta *rcsfile_getdelta(struct rcsfile *, char *); +void rcsfile_setval(struct rcsfile *, int, char *); + +/* Functions used for operating on RCS deltas. */ +struct delta *rcsfile_addelta(struct rcsfile *, char *, char *, char *, + char *); +void rcsfile_importdelta(struct rcsfile *, char *, char *, char *, + char *, char *); + +int rcsdelta_addlog(struct delta *, char *, int); +int rcsdelta_addtext(struct delta *, char *, int); +int rcsdelta_appendlog(struct delta *, char *, size_t); +int rcsdelta_appendtext(struct delta *, char *, size_t); +void rcsdelta_setstate(struct delta *, char *); +void rcsdelta_truncatetext(struct delta *, off_t); +void rcsdelta_truncatelog(struct delta *, off_t); +#endif /* !_RCSFILE_H_ */ diff --git a/usr.bin/csup/rcsparse.c b/usr.bin/csup/rcsparse.c new file mode 100644 index 0000000..5ea690c --- /dev/null +++ b/usr.bin/csup/rcsparse.c @@ -0,0 +1,357 @@ +/*- + * Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +#include "misc.h" +#include "queue.h" +#include "rcsfile.h" +#include "rcsparse.h" +#include "rcstokenizer.h" + +/* + * This is an RCS-parser using lex for tokenizing and makes sure the RCS syntax + * is correct as it constructs an RCS file that is used by csup. + */ + +static void asserttoken(yyscan_t *, int); +static int parse_admin(struct rcsfile *, yyscan_t *); +static int parse_deltas(struct rcsfile *, yyscan_t *, int); +static int parse_deltatexts(struct rcsfile *, yyscan_t *, int); +static char *duptext(yyscan_t *, int *); + +struct string { + char *str; + STAILQ_ENTRY(string) next; +}; + +static void +asserttoken(yyscan_t *sp, int token) +{ + int t; + + t = token; + t = rcslex(*sp); + assert(t == token); +} + +static char * +duptext(yyscan_t *sp, int *arglen) +{ + char *tmp, *val; + int len; + + tmp = rcsget_text(*sp); + len = rcsget_leng(*sp); + val = xmalloc(len + 1); + memcpy(val, tmp, len); + val[len] = '\0'; + if (arglen != NULL) + *arglen = len; + return (val); +} + +/* + * Start up parser, and use the rcsfile hook to add objects. + */ +int +rcsparse_run(struct rcsfile *rf, FILE *infp, int ro) +{ + yyscan_t scanner; + char *desc; + int error, tok; + + error = 0; + rcslex_init(&scanner); + rcsset_in(infp, scanner); + tok = parse_admin(rf, &scanner); + tok = parse_deltas(rf, &scanner, tok); + assert(tok == KEYWORD); + asserttoken(&scanner, STRING); + desc = duptext(&scanner, NULL); + rcsfile_setval(rf, RCSFILE_DESC, desc); + free(desc); + tok = rcslex(scanner); + /* Parse deltatexts if we need to edit. */ + if (!ro) { + error = parse_deltatexts(rf, &scanner, tok); + if (error) + return (error); + } + rcslex_destroy(scanner); + return (0); +} + +/* + * Parse the admin part of a RCS file. + */ +static int +parse_admin(struct rcsfile *rf, yyscan_t *sp) +{ + char *branch, *comment, *expand, *head, *id, *revnum, *tag, *tmp; + int strict, token; + + strict = 0; + branch = NULL; + + /* head {num}; */ + asserttoken(sp, KEYWORD); + asserttoken(sp, NUM); + head = duptext(sp, NULL); + rcsfile_setval(rf, RCSFILE_HEAD, head); + free(head); + asserttoken(sp, SEMIC); + + /* { branch {num}; } */ + token = rcslex(*sp); + if (token == KEYWORD_TWO) { + asserttoken(sp, NUM); + branch = duptext(sp, NULL); + rcsfile_setval(rf, RCSFILE_BRANCH, branch); + free(branch); + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + + /* access {id]*; */ + assert(token == KEYWORD); + token = rcslex(*sp); + while (token == ID) { + id = duptext(sp, NULL); + rcsfile_addaccess(rf, id); + free(id); + token = rcslex(*sp); + } + assert(token == SEMIC); + + /* symbols {sym : num}*; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + while (token == ID) { + tag = duptext(sp, NULL); + asserttoken(sp, COLON); + asserttoken(sp, NUM); + revnum = duptext(sp, NULL); + rcsfile_importtag(rf, tag, revnum); + free(tag); + free(revnum); + token = rcslex(*sp); + } + assert(token == SEMIC); + + /* locks {id : num}*; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + while (token == ID) { + /* XXX: locks field is skipped */ + asserttoken(sp, COLON); + asserttoken(sp, NUM); + token = rcslex(*sp); + } + assert(token == SEMIC); + token = rcslex(*sp); + while (token == KEYWORD) { + tmp = rcsget_text(*sp); + + /* {strict ;} */ + if (!strcmp(tmp, "strict")) { + rcsfile_setval(rf, RCSFILE_STRICT, tmp); + asserttoken(sp, SEMIC); + /* { comment {string}; } */ + } else if (!strcmp(tmp, "comment")) { + token = rcslex(*sp); + if (token == STRING) { + comment = duptext(sp, NULL); + rcsfile_setval(rf, RCSFILE_COMMENT, comment); + free(comment); + } + asserttoken(sp, SEMIC); + /* { expand {string}; } */ + } else if (!strcmp(tmp, "expand")) { + token = rcslex(*sp); + if (token == STRING) { + expand = duptext(sp, NULL); + rcsfile_setval(rf, RCSFILE_EXPAND, expand); + free(expand); + } + asserttoken(sp, SEMIC); + } + /* {newphrase }* */ + token = rcslex(*sp); + while (token == ID) { + token = rcslex(*sp); + /* XXX: newphrases ignored */ + while (token == ID || token == NUM || token == STRING || + token == COLON) { + token = rcslex(*sp); + } + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + } + return (token); +} + +/* + * Parse RCS deltas. + */ +static int +parse_deltas(struct rcsfile *rf, yyscan_t *sp, int token) +{ + STAILQ_HEAD(, string) branchlist; + char *revnum, *revdate, *author, *state, *next; + + /* In case we don't have deltas. */ + if (token != NUM) + return (token); + do { + next = NULL; + state = NULL; + + /* num */ + assert(token == NUM); + revnum = duptext(sp, NULL); + /* date num; */ + asserttoken(sp, KEYWORD); + asserttoken(sp, NUM); + revdate = duptext(sp, NULL); + asserttoken(sp, SEMIC); + /* author id; */ + asserttoken(sp, KEYWORD); + asserttoken(sp, ID); + author = duptext(sp, NULL); + asserttoken(sp, SEMIC); + /* state {id}; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + if (token == ID) { + state = duptext(sp, NULL); + token = rcslex(*sp); + } + assert(token == SEMIC); + /* branches {num}*; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + STAILQ_INIT(&branchlist); + while (token == NUM) + token = rcslex(*sp); + assert(token == SEMIC); + /* next {num}; */ + asserttoken(sp, KEYWORD); + token = rcslex(*sp); + if (token == NUM) { + next = duptext(sp, NULL); + token = rcslex(*sp); + } + assert(token == SEMIC); + /* {newphrase }* */ + token = rcslex(*sp); + while (token == ID) { + token = rcslex(*sp); + /* XXX: newphrases ignored. */ + while (token == ID || token == NUM || token == STRING || + token == COLON) { + token = rcslex(*sp); + } + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + rcsfile_importdelta(rf, revnum, revdate, author, state, next); + free(revnum); + free(revdate); + free(author); + if (state != NULL) + free(state); + if (next != NULL) + free(next); + } while (token == NUM); + + return (token); +} + +/* + * Parse RCS deltatexts. + */ +static int +parse_deltatexts(struct rcsfile *rf, yyscan_t *sp, int token) +{ + struct delta *d; + char *log, *revnum, *text; + int error, len; + + error = 0; + /* In case we don't have deltatexts. */ + if (token != NUM) + return (-1); + do { + /* num */ + assert(token == NUM); + revnum = duptext(sp, NULL); + /* Get delta we're adding text to. */ + d = rcsfile_getdelta(rf, revnum); + free(revnum); + + /* log string */ + asserttoken(sp, KEYWORD); + asserttoken(sp, STRING); + log = duptext(sp, &len); + error = rcsdelta_addlog(d, log, len); + free(log); + if (error) + return (-1); + /* { newphrase }* */ + token = rcslex(*sp); + while (token == ID) { + token = rcslex(*sp); + /* XXX: newphrases ignored. */ + while (token == ID || token == NUM || token == STRING || + token == COLON) { + token = rcslex(*sp); + } + asserttoken(sp, SEMIC); + token = rcslex(*sp); + } + /* text string */ + assert(token == KEYWORD); + asserttoken(sp, STRING); + text = duptext(sp, &len); + error = rcsdelta_addtext(d, text, len); + /* + * If this happens, something is wrong with the RCS file, and it + * should be resent. + */ + free(text); + if (error) + return (-1); + token = rcslex(*sp); + } while (token == NUM); + + return (0); +} diff --git a/usr.bin/csup/rcsparse.h b/usr.bin/csup/rcsparse.h new file mode 100644 index 0000000..01b0156 --- /dev/null +++ b/usr.bin/csup/rcsparse.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _RCSPARSE_H_ +#define _RCSPARSE_H_ +#define ID 0 +#define NUM 1 +#define KEYWORD 2 +#define KEYWORD_TWO 3 +#define STRING 4 +#define SEMIC 5 +#define COLON 6 + +struct rcsfile; +int rcsparse_run(struct rcsfile *, FILE *, int); +#endif /* !_RCSPARSE_H_ */ diff --git a/usr.bin/csup/rcstokenizer.h b/usr.bin/csup/rcstokenizer.h new file mode 100644 index 0000000..66ea724 --- /dev/null +++ b/usr.bin/csup/rcstokenizer.h @@ -0,0 +1,333 @@ +#ifndef rcsHEADER_H +#define rcsHEADER_H 1 +#define rcsIN_HEADER 1 + +#line 6 "rcstokenizer.h" + +#define YY_INT_ALIGNED short int + +/* A lexical scanner generated by flex */ + +#define FLEX_SCANNER +#define YY_FLEX_MAJOR_VERSION 2 +#define YY_FLEX_MINOR_VERSION 5 +#define YY_FLEX_SUBMINOR_VERSION 35 +#if YY_FLEX_SUBMINOR_VERSION > 0 +#define FLEX_BETA +#endif + +/* First, we deal with platform-specific or compiler-specific issues. */ + +/* begin standard C headers. */ +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> + +/* end standard C headers. */ + +/* flex integer type definitions */ + +#ifndef FLEXINT_H +#define FLEXINT_H + +/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */ + +#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L + +/* C99 says to define __STDC_LIMIT_MACROS before including stdint.h, + * if you want the limit (max/min) macros for int types. + */ +#ifndef __STDC_LIMIT_MACROS +#define __STDC_LIMIT_MACROS 1 +#endif + +#include <inttypes.h> +typedef int8_t flex_int8_t; +typedef uint8_t flex_uint8_t; +typedef int16_t flex_int16_t; +typedef uint16_t flex_uint16_t; +typedef int32_t flex_int32_t; +typedef uint32_t flex_uint32_t; +#else +typedef signed char flex_int8_t; +typedef short int flex_int16_t; +typedef int flex_int32_t; +typedef unsigned char flex_uint8_t; +typedef unsigned short int flex_uint16_t; +typedef unsigned int flex_uint32_t; +#endif /* ! C99 */ + +/* Limits of integral types. */ +#ifndef INT8_MIN +#define INT8_MIN (-128) +#endif +#ifndef INT16_MIN +#define INT16_MIN (-32767-1) +#endif +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif +#ifndef INT8_MAX +#define INT8_MAX (127) +#endif +#ifndef INT16_MAX +#define INT16_MAX (32767) +#endif +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif +#ifndef UINT8_MAX +#define UINT8_MAX (255U) +#endif +#ifndef UINT16_MAX +#define UINT16_MAX (65535U) +#endif +#ifndef UINT32_MAX +#define UINT32_MAX (4294967295U) +#endif + +#endif /* ! FLEXINT_H */ + +#ifdef __cplusplus + +/* The "const" storage-class-modifier is valid. */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +/* C99 requires __STDC__ to be defined as 1. */ +#if defined (__STDC__) + +#define YY_USE_CONST + +#endif /* defined (__STDC__) */ +#endif /* ! __cplusplus */ + +#ifdef YY_USE_CONST +#define yyconst const +#else +#define yyconst +#endif + +/* An opaque pointer. */ +#ifndef YY_TYPEDEF_YY_SCANNER_T +#define YY_TYPEDEF_YY_SCANNER_T +typedef void* yyscan_t; +#endif + +/* For convenience, these vars (plus the bison vars far below) + are macros in the reentrant scanner. */ +#define yyin yyg->yyin_r +#define yyout yyg->yyout_r +#define yyextra yyg->yyextra_r +#define yyleng yyg->yyleng_r +#define yytext yyg->yytext_r +#define yylineno (YY_CURRENT_BUFFER_LVALUE->yy_bs_lineno) +#define yycolumn (YY_CURRENT_BUFFER_LVALUE->yy_bs_column) +#define yy_flex_debug yyg->yy_flex_debug_r + +/* Size of default input buffer. */ +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE 16384 +#endif + +#ifndef YY_TYPEDEF_YY_BUFFER_STATE +#define YY_TYPEDEF_YY_BUFFER_STATE +typedef struct yy_buffer_state *YY_BUFFER_STATE; +#endif + +#ifndef YY_TYPEDEF_YY_SIZE_T +#define YY_TYPEDEF_YY_SIZE_T +typedef size_t yy_size_t; +#endif + +#ifndef YY_STRUCT_YY_BUFFER_STATE +#define YY_STRUCT_YY_BUFFER_STATE +struct yy_buffer_state + { + FILE *yy_input_file; + + char *yy_ch_buf; /* input buffer */ + char *yy_buf_pos; /* current position in input buffer */ + + /* Size of input buffer in bytes, not including room for EOB + * characters. + */ + yy_size_t yy_buf_size; + + /* Number of characters read into yy_ch_buf, not including EOB + * characters. + */ + int yy_n_chars; + + /* Whether we "own" the buffer - i.e., we know we created it, + * and can realloc() it to grow it, and should free() it to + * delete it. + */ + int yy_is_our_buffer; + + /* Whether this is an "interactive" input source; if so, and + * if we're using stdio for input, then we want to use getc() + * instead of fread(), to make sure we stop fetching input after + * each newline. + */ + int yy_is_interactive; + + /* Whether we're considered to be at the beginning of a line. + * If so, '^' rules will be active on the next match, otherwise + * not. + */ + int yy_at_bol; + + int yy_bs_lineno; /**< The line count. */ + int yy_bs_column; /**< The column count. */ + + /* Whether to try to fill the input buffer when we reach the + * end of it. + */ + int yy_fill_buffer; + + int yy_buffer_status; + + }; +#endif /* !YY_STRUCT_YY_BUFFER_STATE */ + +void rcsrestart (FILE *input_file ,yyscan_t yyscanner ); +void rcs_switch_to_buffer (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_create_buffer (FILE *file,int size ,yyscan_t yyscanner ); +void rcs_delete_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcs_flush_buffer (YY_BUFFER_STATE b ,yyscan_t yyscanner ); +void rcspush_buffer_state (YY_BUFFER_STATE new_buffer ,yyscan_t yyscanner ); +void rcspop_buffer_state (yyscan_t yyscanner ); + +YY_BUFFER_STATE rcs_scan_buffer (char *base,yy_size_t size ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_string (yyconst char *yy_str ,yyscan_t yyscanner ); +YY_BUFFER_STATE rcs_scan_bytes (yyconst char *bytes,int len ,yyscan_t yyscanner ); + +void *rcsalloc (yy_size_t ,yyscan_t yyscanner ); +void *rcsrealloc (void *,yy_size_t ,yyscan_t yyscanner ); +void rcsfree (void * ,yyscan_t yyscanner ); + +/* Begin user sect3 */ + +#define rcswrap(n) 1 +#define YY_SKIP_YYWRAP + +#define yytext_ptr yytext_r + +#ifdef YY_HEADER_EXPORT_START_CONDITIONS +#define INITIAL 0 + +#endif + +#ifndef YY_NO_UNISTD_H +/* Special case for "unistd.h", since it is non-ANSI. We include it way + * down here because we want the user's section 1 to have been scanned first. + * The user has a chance to override it with an option. + */ +#include <unistd.h> +#endif + +#ifndef YY_EXTRA_TYPE +#define YY_EXTRA_TYPE void * +#endif + +int rcslex_init (yyscan_t* scanner); + +int rcslex_init_extra (YY_EXTRA_TYPE user_defined,yyscan_t* scanner); + +/* Accessor methods to globals. + These are made visible to non-reentrant scanners for convenience. */ + +int rcslex_destroy (yyscan_t yyscanner ); + +int rcsget_debug (yyscan_t yyscanner ); + +void rcsset_debug (int debug_flag ,yyscan_t yyscanner ); + +YY_EXTRA_TYPE rcsget_extra (yyscan_t yyscanner ); + +void rcsset_extra (YY_EXTRA_TYPE user_defined ,yyscan_t yyscanner ); + +FILE *rcsget_in (yyscan_t yyscanner ); + +void rcsset_in (FILE * in_str ,yyscan_t yyscanner ); + +FILE *rcsget_out (yyscan_t yyscanner ); + +void rcsset_out (FILE * out_str ,yyscan_t yyscanner ); + +int rcsget_leng (yyscan_t yyscanner ); + +char *rcsget_text (yyscan_t yyscanner ); + +int rcsget_lineno (yyscan_t yyscanner ); + +void rcsset_lineno (int line_number ,yyscan_t yyscanner ); + +/* Macros after this point can all be overridden by user definitions in + * section 1. + */ + +#ifndef YY_SKIP_YYWRAP +#ifdef __cplusplus +extern "C" int rcswrap (yyscan_t yyscanner ); +#else +extern int rcswrap (yyscan_t yyscanner ); +#endif +#endif + +#ifndef yytext_ptr +static void yy_flex_strncpy (char *,yyconst char *,int ,yyscan_t yyscanner); +#endif + +#ifdef YY_NEED_STRLEN +static int yy_flex_strlen (yyconst char * ,yyscan_t yyscanner); +#endif + +#ifndef YY_NO_INPUT + +#endif + +/* Amount of stuff to slurp up with each read. */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* Number of entries by which start-condition stack grows. */ +#ifndef YY_START_STACK_INCR +#define YY_START_STACK_INCR 25 +#endif + +/* Default declaration of generated scanner - a define so the user can + * easily add parameters. + */ +#ifndef YY_DECL +#define YY_DECL_IS_OURS 1 + +extern int rcslex (yyscan_t yyscanner); + +#define YY_DECL int rcslex (yyscan_t yyscanner) +#endif /* !YY_DECL */ + +/* yy_get_previous_state - get the state just before the EOB char was reached */ + +#undef YY_NEW_FILE +#undef YY_FLUSH_BUFFER +#undef yy_set_bol +#undef yy_new_buffer +#undef yy_set_interactive +#undef YY_DO_BEFORE_ACTION + +#ifdef YY_DECL_IS_OURS +#undef YY_DECL_IS_OURS +#undef YY_DECL +#endif + +#line 73 "rcstokenizer.l" + + +#line 332 "rcstokenizer.h" +#undef rcsIN_HEADER +#endif /* rcsHEADER_H */ diff --git a/usr.bin/csup/rcstokenizer.l b/usr.bin/csup/rcstokenizer.l new file mode 100644 index 0000000..56f0f41 --- /dev/null +++ b/usr.bin/csup/rcstokenizer.l @@ -0,0 +1,73 @@ +/*- + * Copyright (c) 2007-2009, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +/* + * This tokenizer must be generated by a lexxer with support for reentrancy. + */ +%{ +#include <string.h> + +#include "misc.h" +#include "rcsparse.h" + +%} +%option reentrant noyywrap +%option header-file="rcstokenizer.h" + +everything (.|\n)* +num [0-9\.]+ +whitespace [\t\n ] +digit [0-9] +idchar [^$,.:;\t\n ] +string @([^@]|\n|"@@")*@ +keyword head|access|symbols|locks|comment|expand|strict|date|author|state|branches|next|desc|log|text +keyword2 branch +newline \n +%% + +{keyword2} { + return (KEYWORD_TWO); +} +{keyword} { + return (KEYWORD); +} +{string} { + return (STRING); +} +{num} { + return (NUM); +} +{num}?{idchar}({idchar}|{num})* { +/* This will use ID as both ID and SYM. Do extra checking elsewhere.*/ + return (ID); +} +; { return (SEMIC); } +: { return (COLON); } +\n ; +[ \t]+ ; +%% diff --git a/usr.bin/csup/rsyncfile.c b/usr.bin/csup/rsyncfile.c new file mode 100644 index 0000000..7680bcc --- /dev/null +++ b/usr.bin/csup/rsyncfile.c @@ -0,0 +1,223 @@ +/*- + * Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/mman.h> +#include <fcntl.h> +#include <unistd.h> + +#include "misc.h" +#include "rsyncfile.h" + +#define MINBLOCKSIZE 1024 +#define MAXBLOCKSIZE (16 * 1024) +#define RECEIVEBUFFERSIZE (15 * 1024) +#define BLOCKINFOSIZE 26 +#define SEARCHREGION 10 +#define MAXBLOCKS (RECEIVEBUFFERSIZE / BLOCKINFOSIZE) + +#define CHAR_OFFSET 3 +#define RSUM_SIZE 9 + +struct rsyncfile { + char *start; + char *buf; + char *end; + size_t blocksize; + size_t fsize; + int fd; + + char *blockptr; + int blocknum; + char blockmd5[MD5_DIGEST_SIZE]; + char rsumstr[RSUM_SIZE]; + uint32_t rsum; +}; + +static size_t rsync_chooseblocksize(size_t); +static uint32_t rsync_rollsum(char *, size_t); + +/* Open a file and initialize variable for rsync operation. */ +struct rsyncfile * +rsync_open(char *path, size_t blocksize, int rdonly) +{ + struct rsyncfile *rf; + struct stat st; + int error; + + rf = xmalloc(sizeof(*rf)); + error = stat(path, &st); + if (error) { + free(rf); + return (NULL); + } + rf->fsize = st.st_size; + + rf->fd = open(path, rdonly ? O_RDONLY : O_RDWR); + if (rf->fd < 0) { + free(rf); + return (NULL); + } + rf->buf = mmap(0, rf->fsize, PROT_READ, MAP_SHARED, rf->fd, 0); + if (rf->buf == MAP_FAILED) { + free(rf); + return (NULL); + } + rf->start = rf->buf; + rf->end = rf->buf + rf->fsize; + rf->blocksize = (blocksize == 0 ? rsync_chooseblocksize(rf->fsize) : + blocksize); + rf->blockptr = rf->buf; + rf->blocknum = 0; + return (rf); +} + +/* Close and free all resources related to an rsync file transfer. */ +int +rsync_close(struct rsyncfile *rf) +{ + int error; + + error = munmap(rf->buf, rf->fsize); + if (error) + return (error); + close(rf->fd); + free(rf); + return (0); +} + +/* + * Choose the most appropriate block size for an rsync transfer. Modeled + * algorithm after cvsup. + */ +static size_t +rsync_chooseblocksize(size_t fsize) +{ + size_t bestrem, blocksize, bs, hisearch, losearch, rem; + + blocksize = fsize / MAXBLOCKS; + losearch = blocksize - SEARCHREGION; + hisearch = blocksize + SEARCHREGION; + + if (losearch < MINBLOCKSIZE) { + losearch = MINBLOCKSIZE; + hisearch = losearch + (2 * SEARCHREGION); + } else if (hisearch > MAXBLOCKSIZE) { + hisearch = MAXBLOCKSIZE; + losearch = hisearch - (2 * SEARCHREGION); + } + + bestrem = MAXBLOCKSIZE; + for (bs = losearch; bs <= hisearch; bs++) { + rem = fsize % bs; + if (rem < bestrem) { + bestrem = rem; + blocksize = bs; + } + } + return (bestrem); +} + +/* Get the next rsync block of a file. */ +int +rsync_nextblock(struct rsyncfile *rf) +{ + MD5_CTX ctx; + size_t blocksize; + + if (rf->blockptr >= rf->end) + return (0); + blocksize = min((size_t)(rf->end - rf->blockptr), rf->blocksize); + /* Calculate MD5 of the block. */ + MD5_Init(&ctx); + MD5_Update(&ctx, rf->blockptr, blocksize); + MD5_End(rf->blockmd5, &ctx); + + rf->rsum = rsync_rollsum(rf->blockptr, blocksize); + snprintf(rf->rsumstr, RSUM_SIZE, "%x", rf->rsum); + rf->blocknum++; + rf->blockptr += blocksize; + return (1); +} + +/* Get the rolling checksum of a file. */ +static uint32_t +rsync_rollsum(char *buf, size_t len) +{ + uint32_t a, b; + char *ptr, *limit; + + a = b = 0; + ptr = buf; + limit = buf + len; + + while (ptr < limit) { + a += *ptr + CHAR_OFFSET; + b += a; + ptr++; + } + return ((b << 16) | a); +} + +/* Get running sum so far. */ +char * +rsync_rsum(struct rsyncfile *rf) +{ + + return (rf->rsumstr); +} + +/* Get MD5 of current block. */ +char * +rsync_blockmd5(struct rsyncfile *rf) +{ + + return (rf->blockmd5); +} + +/* Accessor for blocksize. */ +size_t +rsync_blocksize(struct rsyncfile *rf) +{ + + return (rf->blocksize); +} + +/* Accessor for filesize. */ +size_t +rsync_filesize(struct rsyncfile *rf) +{ + + return (rf->fsize); +} diff --git a/usr.bin/csup/rsyncfile.h b/usr.bin/csup/rsyncfile.h new file mode 100644 index 0000000..2c41a28 --- /dev/null +++ b/usr.bin/csup/rsyncfile.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2008-2009, Ulf Lilleengen <lulf@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#ifndef _RSYNCFILE_H_ +#define _RSYNCFILE_H_ + +struct rsyncfile; +struct rsyncfile *rsync_open(char *, size_t, int); +int rsync_nextblock(struct rsyncfile *); +char *rsync_rsum(struct rsyncfile *); +char *rsync_blockmd5(struct rsyncfile *); +int rsync_close(struct rsyncfile *); +size_t rsync_blocksize(struct rsyncfile *); +size_t rsync_filesize(struct rsyncfile *); + +#endif /* !_RSYNCFILE_H_ */ diff --git a/usr.bin/csup/status.c b/usr.bin/csup/status.c new file mode 100644 index 0000000..3482e8e --- /dev/null +++ b/usr.bin/csup/status.c @@ -0,0 +1,874 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "config.h" +#include "fattr.h" +#include "misc.h" +#include "pathcomp.h" +#include "proto.h" +#include "queue.h" +#include "status.h" +#include "stream.h" + +#define STATUS_VERSION 5 + +/* Internal error codes. */ +#define STATUS_ERR_READ (-1) +#define STATUS_ERR_WRITE (-2) +#define STATUS_ERR_PARSE (-3) +#define STATUS_ERR_UNSORTED (-4) +#define STATUS_ERR_TRUNC (-5) +#define STATUS_ERR_BOGUS_DIRUP (-6) +#define STATUS_ERR_BAD_TYPE (-7) +#define STATUS_ERR_RENAME (-8) + +static struct status *status_new(char *, time_t, struct stream *); +static struct statusrec *status_rd(struct status *); +static struct statusrec *status_rdraw(struct status *, char **); +static int status_wr(struct status *, struct statusrec *); +static int status_wrraw(struct status *, struct statusrec *, + char *); +static struct status *status_fromrd(char *, struct stream *); +static struct status *status_fromnull(char *); +static void status_free(struct status *); + +static void statusrec_init(struct statusrec *); +static void statusrec_fini(struct statusrec *); +static int statusrec_cook(struct statusrec *, char *); +static int statusrec_cmp(struct statusrec *, struct statusrec *); + +struct status { + char *path; + char *tempfile; + int error; + int suberror; + struct pathcomp *pc; + struct statusrec buf; + struct statusrec *previous; + struct statusrec *current; + struct stream *rd; + struct stream *wr; + time_t scantime; + int eof; + int linenum; + int depth; + int dirty; +}; + +static void +statusrec_init(struct statusrec *sr) +{ + + memset(sr, 0, sizeof(*sr)); +} + +static int +statusrec_cook(struct statusrec *sr, char *line) +{ + char *clientattr, *serverattr; + + switch (sr->sr_type) { + case SR_FILEDEAD: + case SR_FILELIVE: + clientattr = proto_get_ascii(&line); + if (clientattr == NULL || line != NULL) + return (-1); + sr->sr_clientattr = fattr_decode(clientattr); + if (sr->sr_clientattr == NULL) + return (-1); + break; + case SR_DIRDOWN: + /* Nothing to do. */ + if (line != NULL) + return (-1); + break; + case SR_CHECKOUTLIVE: + sr->sr_tag = proto_get_ascii(&line); + sr->sr_date = proto_get_ascii(&line); + serverattr = proto_get_ascii(&line); + sr->sr_revnum = proto_get_ascii(&line); + sr->sr_revdate = proto_get_ascii(&line); + clientattr = proto_get_ascii(&line); + if (clientattr == NULL || line != NULL) + return (-1); + sr->sr_serverattr = fattr_decode(serverattr); + if (sr->sr_serverattr == NULL) + return (-1); + sr->sr_clientattr = fattr_decode(clientattr); + if (sr->sr_clientattr == NULL) { + fattr_free(sr->sr_serverattr); + return (-1); + } + break; + case SR_CHECKOUTDEAD: + sr->sr_tag = proto_get_ascii(&line); + sr->sr_date = proto_get_ascii(&line); + serverattr = proto_get_ascii(&line); + if (serverattr == NULL || line != NULL) + return (-1); + sr->sr_serverattr = fattr_decode(serverattr); + if (sr->sr_serverattr == NULL) + return (-1); + break; + case SR_DIRUP: + clientattr = proto_get_ascii(&line); + if (clientattr == NULL || line != NULL) + return (-1); + sr->sr_clientattr = fattr_decode(clientattr); + if (sr->sr_clientattr == NULL) + return (-1); + break; + default: + return (-1); + } + return (0); +} + +static struct statusrec * +status_rd(struct status *st) +{ + struct statusrec *sr; + char *line; + int error; + + sr = status_rdraw(st, &line); + if (sr == NULL) + return (NULL); + error = statusrec_cook(sr, line); + if (error) { + st->error = STATUS_ERR_PARSE; + return (NULL); + } + return (sr); +} + +static struct statusrec * +status_rdraw(struct status *st, char **linep) +{ + struct statusrec sr; + char *cmd, *line, *file; + + if (st->rd == NULL || st->eof) + return (NULL); + line = stream_getln(st->rd, NULL); + if (line == NULL) { + if (stream_eof(st->rd)) { + if (st->depth != 0) { + st->error = STATUS_ERR_TRUNC; + return (NULL); + } + st->eof = 1; + return (NULL); + } + st->error = STATUS_ERR_READ; + st->suberror = errno; + return (NULL); + } + st->linenum++; + cmd = proto_get_ascii(&line); + file = proto_get_ascii(&line); + if (file == NULL || strlen(cmd) != 1) { + st->error = STATUS_ERR_PARSE; + return (NULL); + } + + switch (cmd[0]) { + case 'A': + sr.sr_type = SR_FILELIVE; + break; + case 'D': + sr.sr_type = SR_DIRDOWN; + st->depth++; + break; + case 'C': + sr.sr_type = SR_CHECKOUTLIVE; + break; + case 'c': + sr.sr_type = SR_CHECKOUTDEAD; + break; + case 'U': + sr.sr_type = SR_DIRUP; + if (st->depth <= 0) { + st->error = STATUS_ERR_BOGUS_DIRUP; + return (NULL); + } + st->depth--; + break; + case 'V': + sr.sr_type = SR_FILELIVE; + break; + case 'v': + sr.sr_type = SR_FILEDEAD; + break; + default: + st->error = STATUS_ERR_BAD_TYPE; + st->suberror = cmd[0]; + return (NULL); + } + + sr.sr_file = xstrdup(file); + if (st->previous != NULL && + statusrec_cmp(st->previous, &sr) >= 0) { + st->error = STATUS_ERR_UNSORTED; + free(sr.sr_file); + return (NULL); + } + + if (st->previous == NULL) { + st->previous = &st->buf; + } else { + statusrec_fini(st->previous); + statusrec_init(st->previous); + } + st->previous->sr_type = sr.sr_type; + st->previous->sr_file = sr.sr_file; + *linep = line; + return (st->previous); +} + +static int +status_wr(struct status *st, struct statusrec *sr) +{ + struct pathcomp *pc; + const struct fattr *fa; + char *name; + int error, type, usedirupattr; + + pc = st->pc; + error = 0; + usedirupattr = 0; + if (sr->sr_type == SR_DIRDOWN) { + pathcomp_put(pc, PC_DIRDOWN, sr->sr_file); + } else if (sr->sr_type == SR_DIRUP) { + pathcomp_put(pc, PC_DIRUP, sr->sr_file); + usedirupattr = 1; + } else { + pathcomp_put(pc, PC_FILE, sr->sr_file); + } + + while (pathcomp_get(pc, &type, &name)) { + if (type == PC_DIRDOWN) { + error = proto_printf(st->wr, "D %s\n", name); + } else if (type == PC_DIRUP) { + if (usedirupattr) + fa = sr->sr_clientattr; + else + fa = fattr_bogus; + usedirupattr = 0; + error = proto_printf(st->wr, "U %s %f\n", name, fa); + } + if (error) + goto bad; + } + + switch (sr->sr_type) { + case SR_DIRDOWN: + case SR_DIRUP: + /* Already emitted above. */ + break; + case SR_CHECKOUTLIVE: + error = proto_printf(st->wr, "C %s %s %s %f %s %s %f\n", + sr->sr_file, sr->sr_tag, sr->sr_date, sr->sr_serverattr, + sr->sr_revnum, sr->sr_revdate, sr->sr_clientattr); + break; + case SR_CHECKOUTDEAD: + error = proto_printf(st->wr, "c %s %s %s %f\n", sr->sr_file, + sr->sr_tag, sr->sr_date, sr->sr_serverattr); + break; + case SR_FILELIVE: + error = proto_printf(st->wr, "V %s %f\n", sr->sr_file, + sr->sr_clientattr); + break; + case SR_FILEDEAD: + error = proto_printf(st->wr, "v %s %f\n", sr->sr_file, + sr->sr_clientattr); + break; + } + if (error) + goto bad; + return (0); +bad: + st->error = STATUS_ERR_WRITE; + st->suberror = errno; + return (-1); +} + +static int +status_wrraw(struct status *st, struct statusrec *sr, char *line) +{ + char *name; + char cmd; + int error, ret, type; + + if (st->wr == NULL) + return (0); + + /* + * Keep the compressor in sync. At this point, the necessary + * DirDowns and DirUps should have already been emitted, so the + * compressor should return exactly one value in the PC_DIRDOWN + * and PC_DIRUP case and none in the PC_FILE case. + */ + if (sr->sr_type == SR_DIRDOWN) + pathcomp_put(st->pc, PC_DIRDOWN, sr->sr_file); + else if (sr->sr_type == SR_DIRUP) + pathcomp_put(st->pc, PC_DIRUP, sr->sr_file); + else + pathcomp_put(st->pc, PC_FILE, sr->sr_file); + if (sr->sr_type == SR_DIRDOWN || sr->sr_type == SR_DIRUP) { + ret = pathcomp_get(st->pc, &type, &name); + assert(ret); + if (sr->sr_type == SR_DIRDOWN) + assert(type == PC_DIRDOWN); + else + assert(type == PC_DIRUP); + } + ret = pathcomp_get(st->pc, &type, &name); + assert(!ret); + + switch (sr->sr_type) { + case SR_DIRDOWN: + cmd = 'D'; + break; + case SR_DIRUP: + cmd = 'U'; + break; + case SR_CHECKOUTLIVE: + cmd = 'C'; + break; + case SR_CHECKOUTDEAD: + cmd = 'c'; + break; + case SR_FILELIVE: + cmd = 'V'; + break; + case SR_FILEDEAD: + cmd = 'v'; + break; + default: + assert(0); + return (-1); + } + if (sr->sr_type == SR_DIRDOWN) + error = proto_printf(st->wr, "%c %S\n", cmd, sr->sr_file); + else + error = proto_printf(st->wr, "%c %s %S\n", cmd, sr->sr_file, + line); + if (error) { + st->error = STATUS_ERR_WRITE; + st->suberror = errno; + return (-1); + } + return (0); +} + +static void +statusrec_fini(struct statusrec *sr) +{ + + fattr_free(sr->sr_serverattr); + fattr_free(sr->sr_clientattr); + free(sr->sr_file); +} + +static int +statusrec_cmp(struct statusrec *a, struct statusrec *b) +{ + size_t lena, lenb; + + if (a->sr_type == SR_DIRUP || b->sr_type == SR_DIRUP) { + lena = strlen(a->sr_file); + lenb = strlen(b->sr_file); + if (a->sr_type == SR_DIRUP && + ((lena < lenb && b->sr_file[lena] == '/') || lena == lenb) + && strncmp(a->sr_file, b->sr_file, lena) == 0) + return (1); + if (b->sr_type == SR_DIRUP && + ((lenb < lena && a->sr_file[lenb] == '/') || lenb == lena) + && strncmp(a->sr_file, b->sr_file, lenb) == 0) + return (-1); + } + return (pathcmp(a->sr_file, b->sr_file)); +} + +static struct status * +status_new(char *path, time_t scantime, struct stream *file) +{ + struct status *st; + + st = xmalloc(sizeof(struct status)); + st->path = path; + st->error = 0; + st->suberror = 0; + st->tempfile = NULL; + st->scantime = scantime; + st->rd = file; + st->wr = NULL; + st->previous = NULL; + st->current = NULL; + st->dirty = 0; + st->eof = 0; + st->linenum = 0; + st->depth = 0; + st->pc = pathcomp_new(); + statusrec_init(&st->buf); + return (st); +} + +static void +status_free(struct status *st) +{ + + if (st->previous != NULL) + statusrec_fini(st->previous); + if (st->rd != NULL) + stream_close(st->rd); + if (st->wr != NULL) + stream_close(st->wr); + if (st->tempfile != NULL) + free(st->tempfile); + free(st->path); + pathcomp_free(st->pc); + free(st); +} + +static struct status * +status_fromrd(char *path, struct stream *file) +{ + struct status *st; + char *id, *line; + time_t scantime; + int error, ver; + + /* Get the first line of the file and validate it. */ + line = stream_getln(file, NULL); + if (line == NULL) { + stream_close(file); + return (NULL); + } + id = proto_get_ascii(&line); + error = proto_get_int(&line, &ver, 10); + if (error) { + stream_close(file); + return (NULL); + } + error = proto_get_time(&line, &scantime); + if (error || line != NULL) { + stream_close(file); + return (NULL); + } + + if (strcmp(id, "F") != 0 || ver != STATUS_VERSION) { + stream_close(file); + return (NULL); + } + + st = status_new(path, scantime, file); + st->linenum = 1; + return (st); +} + +static struct status * +status_fromnull(char *path) +{ + struct status *st; + + st = status_new(path, -1, NULL); + st->eof = 1; + return (st); +} + +/* + * Open the status file. If scantime is not -1, the file is opened + * for updating, otherwise, it is opened read-only. If the status file + * couldn't be opened, NULL is returned and errmsg is set to the error + * message. + */ +struct status * +status_open(struct coll *coll, time_t scantime, char **errmsg) +{ + struct status *st; + struct stream *file; + struct fattr *fa; + char *destpath, *path; + int error, rv; + + path = coll_statuspath(coll); + file = stream_open_file(path, O_RDONLY); + if (file == NULL) { + if (errno != ENOENT) { + xasprintf(errmsg, "Could not open \"%s\": %s\n", + path, strerror(errno)); + free(path); + return (NULL); + } + st = status_fromnull(path); + } else { + st = status_fromrd(path, file); + if (st == NULL) { + xasprintf(errmsg, "Error in \"%s\": Bad header line", + path); + free(path); + return (NULL); + } + } + + if (scantime != -1) { + /* Open for writing too. */ + xasprintf(&destpath, "%s/%s/%s/", coll->co_base, + coll->co_colldir, coll->co_name); + st->tempfile = tempname(destpath); + if (mkdirhier(destpath, coll->co_umask) != 0) { + xasprintf(errmsg, "Cannot create directories leading " + "to \"%s\": %s", destpath, strerror(errno)); + free(destpath); + status_free(st); + return (NULL); + } + free(destpath); + st->wr = stream_open_file(st->tempfile, + O_CREAT | O_TRUNC | O_WRONLY, 0644); + if (st->wr == NULL) { + xasprintf(errmsg, "Cannot create \"%s\": %s", + st->tempfile, strerror(errno)); + status_free(st); + return (NULL); + } + fa = fattr_new(FT_FILE, -1); + fattr_mergedefault(fa); + fattr_umask(fa, coll->co_umask); + rv = fattr_install(fa, st->tempfile, NULL); + fattr_free(fa); + if (rv == -1) { + xasprintf(errmsg, + "Cannot set attributes for \"%s\": %s", + st->tempfile, strerror(errno)); + status_free(st); + return (NULL); + } + if (scantime != st->scantime) + st->dirty = 1; + error = proto_printf(st->wr, "F %d %t\n", STATUS_VERSION, + scantime); + if (error) { + st->error = STATUS_ERR_WRITE; + st->suberror = errno; + *errmsg = status_errmsg(st); + status_free(st); + return (NULL); + } + } + return (st); +} + +/* + * Get an entry from the status file. If name is NULL, the next entry + * is returned. If name is not NULL, the entry matching this name is + * returned, or NULL if it couldn't be found. If deleteto is set to 1, + * all the entries read from the status file while looking for the + * given name are deleted. + */ +int +status_get(struct status *st, char *name, int isdirup, int deleteto, + struct statusrec **psr) +{ + struct statusrec key; + struct statusrec *sr; + char *line; + int c, error; + + if (st->eof) + return (0); + + if (st->error) + return (-1); + + if (name == NULL) { + sr = status_rd(st); + if (sr == NULL) { + if (st->error) + return (-1); + return (0); + } + *psr = sr; + return (1); + } + + if (st->current != NULL) { + sr = st->current; + st->current = NULL; + } else { + sr = status_rd(st); + if (sr == NULL) { + if (st->error) + return (-1); + return (0); + } + } + + key.sr_file = name; + if (isdirup) + key.sr_type = SR_DIRUP; + else + key.sr_type = SR_CHECKOUTLIVE; + + c = statusrec_cmp(sr, &key); + if (c < 0) { + if (st->wr != NULL && !deleteto) { + error = status_wr(st, sr); + if (error) + return (-1); + } + /* Loop until we find the good entry. */ + for (;;) { + sr = status_rdraw(st, &line); + if (sr == NULL) { + if (st->error) + return (-1); + return (0); + } + c = statusrec_cmp(sr, &key); + if (c >= 0) + break; + if (st->wr != NULL && !deleteto) { + error = status_wrraw(st, sr, line); + if (error) + return (-1); + } + } + error = statusrec_cook(sr, line); + if (error) { + st->error = STATUS_ERR_PARSE; + return (-1); + } + } + st->current = sr; + if (c != 0) + return (0); + *psr = sr; + return (1); +} + +/* + * Put this entry into the status file. If an entry with the same name + * existed in the status file, it is replaced by this one, otherwise, + * the entry is added to the status file. + */ +int +status_put(struct status *st, struct statusrec *sr) +{ + struct statusrec *old; + int error, ret; + + ret = status_get(st, sr->sr_file, sr->sr_type == SR_DIRUP, 0, &old); + if (ret == -1) + return (-1); + if (ret) { + if (old->sr_type == SR_DIRDOWN) { + /* DirUp should never match DirDown */ + assert(old->sr_type != SR_DIRUP); + if (sr->sr_type == SR_CHECKOUTLIVE || + sr->sr_type == SR_CHECKOUTDEAD) { + /* We are replacing a directory with a file. + Delete all entries inside the directory we + are replacing. */ + ret = status_get(st, sr->sr_file, 1, 1, &old); + if (ret == -1) + return (-1); + assert(ret); + } + } else + st->current = NULL; + } + st->dirty = 1; + error = status_wr(st, sr); + if (error) + return (-1); + return (0); +} + +/* + * Delete the specified entry from the status file. + */ +int +status_delete(struct status *st, char *name, int isdirup) +{ + struct statusrec *sr; + int ret; + + ret = status_get(st, name, isdirup, 0, &sr); + if (ret == -1) + return (-1); + if (ret) { + st->current = NULL; + st->dirty = 1; + } + return (0); +} + +/* + * Check whether we hit the end of file. + */ +int +status_eof(struct status *st) +{ + + return (st->eof); +} + +/* + * Returns the error message if there was an error, otherwise returns + * NULL. The error message is allocated dynamically and needs to be + * freed by the caller after use. + */ +char * +status_errmsg(struct status *st) +{ + char *errmsg; + + if (!st->error) + return (NULL); + switch (st->error) { + case STATUS_ERR_READ: + xasprintf(&errmsg, "Read failure on \"%s\": %s", + st->path, strerror(st->suberror)); + break; + case STATUS_ERR_WRITE: + xasprintf(&errmsg, "Write failure on \"%s\": %s", + st->tempfile, strerror(st->suberror)); + break; + case STATUS_ERR_PARSE: + xasprintf(&errmsg, "Error in \"%s\": %d: " + "Could not parse status record", st->path, st->linenum); + break; + case STATUS_ERR_UNSORTED: + xasprintf(&errmsg, "Error in \"%s\": %d: " + "File is not sorted properly", st->path, st->linenum); + break; + case STATUS_ERR_TRUNC: + xasprintf(&errmsg, "Error in \"%s\": " + "File is truncated", st->path); + break; + case STATUS_ERR_BOGUS_DIRUP: + xasprintf(&errmsg, "Error in \"%s\": %d: " + "\"U\" entry has no matching \"D\"", st->path, st->linenum); + break; + case STATUS_ERR_BAD_TYPE: + xasprintf(&errmsg, "Error in \"%s\": %d: " + "Invalid file type \"%c\"", st->path, st->linenum, + st->suberror); + break; + case STATUS_ERR_RENAME: + xasprintf(&errmsg, "Cannot rename \"%s\" to \"%s\": %s", + st->tempfile, st->path, strerror(st->suberror)); + break; + default: + assert(0); + return (NULL); + } + return (errmsg); +} + +/* + * Close the status file and free any resource associated with it. + * It is OK to pass NULL for errmsg only if the status file was + * opened read-only. If it wasn't opened read-only, status_close() + * can produce an error and errmsg is not allowed to be NULL. If + * there was no errors, errmsg is set to NULL. + */ +void +status_close(struct status *st, char **errmsg) +{ + struct statusrec *sr; + char *line, *name; + int error, type; + + if (st->wr != NULL) { + if (st->dirty) { + if (st->current != NULL) { + error = status_wr(st, st->current); + if (error) { + *errmsg = status_errmsg(st); + goto bad; + } + st->current = NULL; + } + /* Copy the rest of the file. */ + while ((sr = status_rdraw(st, &line)) != NULL) { + error = status_wrraw(st, sr, line); + if (error) { + *errmsg = status_errmsg(st); + goto bad; + } + } + if (st->error) { + *errmsg = status_errmsg(st); + goto bad; + } + + /* Close off all the open directories. */ + pathcomp_finish(st->pc); + while (pathcomp_get(st->pc, &type, &name)) { + assert(type == PC_DIRUP); + error = proto_printf(st->wr, "U %s %f\n", + name, fattr_bogus); + if (error) { + st->error = STATUS_ERR_WRITE; + st->suberror = errno; + *errmsg = status_errmsg(st); + goto bad; + } + } + + /* Rename tempfile. */ + error = rename(st->tempfile, st->path); + if (error) { + st->error = STATUS_ERR_RENAME; + st->suberror = errno; + *errmsg = status_errmsg(st); + goto bad; + } + } else { + /* Just discard the tempfile. */ + unlink(st->tempfile); + } + *errmsg = NULL; + } + status_free(st); + return; +bad: + status_free(st); +} diff --git a/usr.bin/csup/status.h b/usr.bin/csup/status.h new file mode 100644 index 0000000..86efdda --- /dev/null +++ b/usr.bin/csup/status.h @@ -0,0 +1,72 @@ +/*- + * Copyright (c) 2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _STATUS_H_ +#define _STATUS_H_ + +#include <time.h> + +struct coll; +struct fattr; +struct status; + +#define SR_DIRDOWN 0 +#define SR_CHECKOUTLIVE 1 +#define SR_CHECKOUTDEAD 2 +#define SR_FILELIVE 3 +#define SR_FILEDEAD 4 +#define SR_DIRUP 5 + +struct statusrec { + int sr_type; + char *sr_file; + char *sr_tag; + char *sr_date; + char *sr_revnum; + char *sr_revdate; + + /* + * "clientrttr" contains the attributes of the client's file if there + * is one. "serverattr" contains the attributes of the corresponding + * file on the server. In CVS mode, these are identical. But in + * checkout mode, "clientattr" represents the checked-out file while + * "serverattr" represents the corresponding RCS file on the server. + */ + struct fattr *sr_serverattr; + struct fattr *sr_clientattr; +}; + +struct status *status_open(struct coll *, time_t, char **); +int status_get(struct status *, char *, int, int, + struct statusrec **); +int status_put(struct status *, struct statusrec *); +int status_eof(struct status *); +char *status_errmsg(struct status *); +int status_delete(struct status *, char *, int); +void status_close(struct status *, char **); + +#endif /* !_STATUS_H_ */ diff --git a/usr.bin/csup/stream.c b/usr.bin/csup/stream.c new file mode 100644 index 0000000..aa229b4 --- /dev/null +++ b/usr.bin/csup/stream.c @@ -0,0 +1,1303 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <assert.h> +#include <zlib.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "misc.h" +#include "stream.h" + +/* + * Simple stream API to make my life easier. If the fgetln() and + * funopen() functions were standard and if funopen() wasn't using + * wrong types for the function pointers, I could have just used + * stdio, but life sucks. + * + * For now, streams are always block-buffered. + */ + +/* + * Try to quiet warnings as much as possible with GCC while staying + * compatible with other compilers. + */ +#ifndef __unused +#if defined(__GNUC__) && (__GNUC__ > 2 || __GNUC__ == 2 && __GNUC_MINOR__ >= 7) +#define __unused __attribute__((__unused__)) +#else +#define __unused +#endif +#endif + +/* + * Flags passed to the flush methods. + * + * STREAM_FLUSH_CLOSING is passed during the last flush call before + * closing a stream. This allows the zlib filter to emit the EOF + * marker as appropriate. In all other cases, STREAM_FLUSH_NORMAL + * should be passed. + * + * These flags are completely unused in the default flush method, + * but they are very important for the flush method of the zlib + * filter. + */ +typedef enum { + STREAM_FLUSH_NORMAL, + STREAM_FLUSH_CLOSING +} stream_flush_t; + +/* + * This is because buf_new() will always allocate size + 1 bytes, + * so our buffer sizes will still be power of 2 values. + */ +#define STREAM_BUFSIZ 1023 + +struct buf { + char *buf; + size_t size; + size_t in; + size_t off; +}; + +struct stream { + void *cookie; + int fd; + int buf; + struct buf *rdbuf; + struct buf *wrbuf; + stream_readfn_t *readfn; + stream_writefn_t *writefn; + stream_closefn_t *closefn; + int eof; + struct stream_filter *filter; + void *fdata; +}; + +typedef int stream_filter_initfn_t(struct stream *, void *); +typedef void stream_filter_finifn_t(struct stream *); +typedef int stream_filter_flushfn_t(struct stream *, struct buf *, + stream_flush_t); +typedef ssize_t stream_filter_fillfn_t(struct stream *, struct buf *); + +struct stream_filter { + stream_filter_t id; + stream_filter_initfn_t *initfn; + stream_filter_finifn_t *finifn; + stream_filter_fillfn_t *fillfn; + stream_filter_flushfn_t *flushfn; +}; + +/* Low-level buffer API. */ +#define buf_avail(buf) ((buf)->size - (buf)->off - (buf)->in) +#define buf_count(buf) ((buf)->in) +#define buf_size(buf) ((buf)->size) + +static void buf_more(struct buf *, size_t); +static void buf_less(struct buf *, size_t); +static void buf_grow(struct buf *, size_t); + +/* Internal stream functions. */ +static ssize_t stream_fill(struct stream *); +static ssize_t stream_fill_default(struct stream *, struct buf *); +static int stream_flush_int(struct stream *, stream_flush_t); +static int stream_flush_default(struct stream *, struct buf *, + stream_flush_t); + +/* Filters specific functions. */ +static struct stream_filter *stream_filter_lookup(stream_filter_t); +static int stream_filter_init(struct stream *, void *); +static void stream_filter_fini(struct stream *); + +/* The zlib stream filter declarations. */ +#define ZFILTER_EOF 1 /* Got Z_STREAM_END. */ + +struct zfilter { + int flags; + struct buf *rdbuf; + struct buf *wrbuf; + z_stream *rdstate; + z_stream *wrstate; +}; + +static int zfilter_init(struct stream *, void *); +static void zfilter_fini(struct stream *); +static ssize_t zfilter_fill(struct stream *, struct buf *); +static int zfilter_flush(struct stream *, struct buf *, + stream_flush_t); + +/* The MD5 stream filter. */ +struct md5filter { + MD5_CTX ctx; + char *md5; + char lastc; +#define PRINT 1 +#define WS 2 +#define STRING 3 +#define SEEN 4 + int state; +}; + +static int md5filter_init(struct stream *, void *); +static void md5filter_fini(struct stream *); +static ssize_t md5filter_fill(struct stream *, struct buf *); +static int md5filter_flush(struct stream *, struct buf *, + stream_flush_t); +static int md5rcsfilter_flush(struct stream *, struct buf *, + stream_flush_t); + +/* The available stream filters. */ +struct stream_filter stream_filters[] = { + { + STREAM_FILTER_NULL, + NULL, + NULL, + stream_fill_default, + stream_flush_default + }, + { + STREAM_FILTER_ZLIB, + zfilter_init, + zfilter_fini, + zfilter_fill, + zfilter_flush + }, + { + STREAM_FILTER_MD5, + md5filter_init, + md5filter_fini, + md5filter_fill, + md5filter_flush + }, + { + STREAM_FILTER_MD5RCS, + md5filter_init, + md5filter_fini, + md5filter_fill, + md5rcsfilter_flush + } + +}; + + +/* Create a new buffer. */ +struct buf * +buf_new(size_t size) +{ + struct buf *buf; + + buf = xmalloc(sizeof(struct buf)); + /* + * We keep one spare byte so that stream_getln() can put a '\0' + * there in case the stream doesn't have an ending newline. + */ + buf->buf = xmalloc(size + 1); + memset(buf->buf, 0, size + 1); + buf->size = size; + buf->in = 0; + buf->off = 0; + return (buf); +} + +/* + * Grow the size of the buffer. If "need" is 0, bump its size to the + * next power of 2 value. Otherwise, bump it to the next power of 2 + * value bigger than "need". + */ +static void +buf_grow(struct buf *buf, size_t need) +{ + + if (need == 0) + buf->size = buf->size * 2 + 1; /* Account for the spare byte. */ + else { + assert(need > buf->size); + while (buf->size < need) + buf->size = buf->size * 2 + 1; + } + buf->buf = xrealloc(buf->buf, buf->size + 1); +} + +/* Make more room in the buffer if needed. */ +static void +buf_prewrite(struct buf *buf) +{ + + if (buf_count(buf) == buf_size(buf)) + buf_grow(buf, 0); + if (buf_count(buf) > 0 && buf_avail(buf) == 0) { + memmove(buf->buf, buf->buf + buf->off, buf_count(buf)); + buf->off = 0; + } +} + +/* Account for "n" bytes being added in the buffer. */ +static void +buf_more(struct buf *buf, size_t n) +{ + + assert(n <= buf_avail(buf)); + buf->in += n; +} + +/* Account for "n" bytes having been read in the buffer. */ +static void +buf_less(struct buf *buf, size_t n) +{ + + assert(n <= buf_count(buf)); + buf->in -= n; + if (buf->in == 0) + buf->off = 0; + else + buf->off += n; +} + +/* Free a buffer. */ +void +buf_free(struct buf *buf) +{ + + free(buf->buf); + free(buf); +} + +static struct stream * +stream_new(stream_readfn_t *readfn, stream_writefn_t *writefn, + stream_closefn_t *closefn) +{ + struct stream *stream; + + stream = xmalloc(sizeof(struct stream)); + if (readfn == NULL && writefn == NULL) { + errno = EINVAL; + return (NULL); + } + if (readfn != NULL) + stream->rdbuf = buf_new(STREAM_BUFSIZ); + else + stream->rdbuf = NULL; + if (writefn != NULL) + stream->wrbuf = buf_new(STREAM_BUFSIZ); + else + stream->wrbuf = NULL; + stream->cookie = NULL; + stream->fd = -1; + stream->buf = 0; + stream->readfn = readfn; + stream->writefn = writefn; + stream->closefn = closefn; + stream->filter = stream_filter_lookup(STREAM_FILTER_NULL); + stream->fdata = NULL; + stream->eof = 0; + return (stream); +} + +/* Create a new stream associated with a void *. */ +struct stream * +stream_open(void *cookie, stream_readfn_t *readfn, stream_writefn_t *writefn, + stream_closefn_t *closefn) +{ + struct stream *stream; + + stream = stream_new(readfn, writefn, closefn); + stream->cookie = cookie; + return (stream); +} + +/* Associate a file descriptor with a stream. */ +struct stream * +stream_open_fd(int fd, stream_readfn_t *readfn, stream_writefn_t *writefn, + stream_closefn_t *closefn) +{ + struct stream *stream; + + stream = stream_new(readfn, writefn, closefn); + stream->cookie = &stream->fd; + stream->fd = fd; + return (stream); +} + +/* Associate a buf with a stream. */ +struct stream * +stream_open_buf(struct buf *b) +{ + struct stream *stream; + + stream = stream_new(stream_read_buf, stream_append_buf, stream_close_buf); + stream->cookie = b; + stream->buf = 1; + b->in = 0; + return (stream); +} + +/* + * Truncate a buffer, just decrease offset pointer. + * XXX: this can be dangerous if not used correctly. + */ +void +stream_truncate_buf(struct buf *b, off_t off) +{ + b->off += off; +} + +/* Like open() but returns a stream. */ +struct stream * +stream_open_file(const char *path, int flags, ...) +{ + struct stream *stream; + stream_readfn_t *readfn; + stream_writefn_t *writefn; + va_list ap; + mode_t mode; + int fd; + + va_start(ap, flags); + if (flags & O_CREAT) { + /* + * GCC says I should not be using mode_t here since it's + * promoted to an int when passed through `...'. + */ + mode = va_arg(ap, int); + fd = open(path, flags, mode); + } else + fd = open(path, flags); + va_end(ap); + if (fd == -1) + return (NULL); + + flags &= O_ACCMODE; + if (flags == O_RDONLY) { + readfn = stream_read_fd; + writefn = NULL; + } else if (flags == O_WRONLY) { + readfn = NULL; + writefn = stream_write_fd; + } else if (flags == O_RDWR) { + assert(flags == O_RDWR); + readfn = stream_read_fd; + writefn = stream_write_fd; + } else { + errno = EINVAL; + close(fd); + return (NULL); + } + + stream = stream_open_fd(fd, readfn, writefn, stream_close_fd); + if (stream == NULL) + close(fd); + return (stream); +} + +/* Return the file descriptor associated with this stream, or -1. */ +int +stream_fileno(struct stream *stream) +{ + + return (stream->fd); +} + +/* Convenience read function for character buffers. */ +ssize_t +stream_read_buf(void *cookie, void *buf, size_t size) +{ + struct buf *b; + size_t avail; + + /* Use in to be read offset. */ + b = (struct buf *)cookie; + /* Just return what we have if the request is to large. */ + avail = b->off - b->in; + if (avail < size) { + memcpy(buf, (b->buf + b->in), avail); + b->in += avail; + return (avail); + } + memcpy(buf, (b->buf + b->in), size); + b->in += size; + return (size); +} + +/* Convenience write function for appending character buffers. */ +ssize_t +stream_append_buf(void *cookie, const void *buf, size_t size) +{ + struct buf *b; + size_t avail; + + /* Use off to be write offset. */ + b = (struct buf *)cookie; + + avail = b->size - b->off; + if (size > avail) + buf_grow(b, b->size + size); + memcpy((b->buf + b->off), buf, size); + b->off += size; + b->buf[b->off] = '\0'; + return (size); +} + +/* Convenience close function for freeing character buffers. */ +int +stream_close_buf(void *cookie) +{ + void *data; + + data = cookie; + /* Basically a NOP. */ + return (0); +} + +/* Convenience read function for file descriptors. */ +ssize_t +stream_read_fd(void *cookie, void *buf, size_t size) +{ + ssize_t nbytes; + int fd; + + fd = *(int *)cookie; + nbytes = read(fd, buf, size); + return (nbytes); +} + +/* Convenience write function for file descriptors. */ +ssize_t +stream_write_fd(void *cookie, const void *buf, size_t size) +{ + ssize_t nbytes; + int fd; + + fd = *(int *)cookie; + nbytes = write(fd, buf, size); + return (nbytes); +} + +/* Convenience close function for file descriptors. */ +int +stream_close_fd(void *cookie) +{ + int fd, ret; + + fd = *(int *)cookie; + ret = close(fd); + return (ret); +} + +/* Read some bytes from the stream. */ +ssize_t +stream_read(struct stream *stream, void *buf, size_t size) +{ + struct buf *rdbuf; + ssize_t ret; + size_t n; + + rdbuf = stream->rdbuf; + if (buf_count(rdbuf) == 0) { + ret = stream_fill(stream); + if (ret <= 0) + return (-1); + } + n = min(size, buf_count(rdbuf)); + memcpy(buf, rdbuf->buf + rdbuf->off, n); + buf_less(rdbuf, n); + return (n); +} + +/* A blocking stream_read call. */ +ssize_t +stream_read_blocking(struct stream *stream, void *buf, size_t size) +{ + struct buf *rdbuf; + ssize_t ret; + size_t n; + + rdbuf = stream->rdbuf; + while (buf_count(rdbuf) <= size) { + ret = stream_fill(stream); + if (ret <= 0) + return (-1); + } + /* XXX: Should be at least size bytes in the buffer, right? */ + /* Just do this to make sure. */ + n = min(size, buf_count(rdbuf)); + memcpy(buf, rdbuf->buf + rdbuf->off, n); + buf_less(rdbuf, n); + return (n); +} + +/* + * Read a line from the stream and return a pointer to it. + * + * If "len" is non-NULL, the length of the string will be put into it. + * The pointer is only valid until the next stream API call. The line + * can be modified by the caller, provided he doesn't write before or + * after it. + * + * This is somewhat similar to the BSD fgetln() function, except that + * "len" can be NULL here. In that case the string is terminated by + * overwriting the '\n' character with a NUL character. If it's the + * last line in the stream and it has no ending newline, we can still + * add '\0' after it, because we keep one spare byte in the buffers. + * + * However, be warned that one can't handle binary lines properly + * without knowing the size of the string since those can contain + * NUL characters. + */ +char * +stream_getln(struct stream *stream, size_t *len) +{ + struct buf *buf; + char *cp, *line; + ssize_t n; + size_t done, size; + + buf = stream->rdbuf; + if (buf_count(buf) == 0) { + n = stream_fill(stream); + if (n <= 0) + return (NULL); + } + cp = memchr(buf->buf + buf->off, '\n', buf_count(buf)); + for (done = buf_count(buf); cp == NULL; done += n) { + n = stream_fill(stream); + if (n < 0) + return (NULL); + if (n == 0) + /* Last line of the stream. */ + cp = buf->buf + buf->off + buf->in - 1; + else + cp = memchr(buf->buf + buf->off + done, '\n', + buf_count(buf) - done); + } + line = buf->buf + buf->off; + assert(cp >= line); + size = cp - line + 1; + buf_less(buf, size); + if (len != NULL) { + *len = size; + } else { + /* Terminate the string when len == NULL. */ + if (line[size - 1] == '\n') + line[size - 1] = '\0'; + else + line[size] = '\0'; + } + return (line); +} + +/* Write some bytes to a stream. */ +ssize_t +stream_write(struct stream *stream, const void *src, size_t nbytes) +{ + struct buf *buf; + int error; + + buf = stream->wrbuf; + if (nbytes > buf_size(buf)) + buf_grow(buf, nbytes); + if (nbytes > buf_avail(buf)) { + error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); + if (error) + return (-1); + } + memcpy(buf->buf + buf->off + buf->in, src, nbytes); + buf_more(buf, nbytes); + return (nbytes); +} + +/* Formatted output to a stream. */ +int +stream_printf(struct stream *stream, const char *fmt, ...) +{ + struct buf *buf; + va_list ap; + int error, ret; + + buf = stream->wrbuf; +again: + va_start(ap, fmt); + ret = vsnprintf(buf->buf + buf->off + buf->in, buf_avail(buf), fmt, ap); + va_end(ap); + if (ret < 0) + return (ret); + if ((unsigned)ret >= buf_avail(buf)) { + if ((unsigned)ret >= buf_size(buf)) + buf_grow(buf, ret + 1); + if ((unsigned)ret >= buf_avail(buf)) { + error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); + if (error) + return (-1); + } + goto again; + } + buf_more(buf, ret); + return (ret); +} + +/* Flush the entire write buffer of the stream. */ +int +stream_flush(struct stream *stream) +{ + int error; + + error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); + return (error); +} + +/* Internal flush API. */ +static int +stream_flush_int(struct stream *stream, stream_flush_t how) +{ + struct buf *buf; + int error; + + buf = stream->wrbuf; + error = (*stream->filter->flushfn)(stream, buf, how); + assert(buf_count(buf) == 0); + return (error); +} + +/* The default flush method. */ +static int +stream_flush_default(struct stream *stream, struct buf *buf, + stream_flush_t __unused how) +{ + ssize_t n; + + while (buf_count(buf) > 0) { + do { + n = (*stream->writefn)(stream->cookie, + buf->buf + buf->off, buf_count(buf)); + } while (n == -1 && errno == EINTR); + if (n <= 0) + return (-1); + buf_less(buf, n); + } + return (0); +} + +/* Flush the write buffer and call fsync() on the file descriptor. */ +int +stream_sync(struct stream *stream) +{ + int error; + + if (stream->fd == -1) { + errno = EINVAL; + return (-1); + } + error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); + if (error) + return (-1); + error = fsync(stream->fd); + return (error); +} + +/* Like truncate() but on a stream. */ +int +stream_truncate(struct stream *stream, off_t size) +{ + int error; + + if (stream->fd == -1) { + errno = EINVAL; + return (-1); + } + error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); + if (error) + return (-1); + error = ftruncate(stream->fd, size); + return (error); +} + +/* Like stream_truncate() except the off_t parameter is an offset. */ +int +stream_truncate_rel(struct stream *stream, off_t off) +{ + struct stat sb; + int error; + + if (stream->buf) { + stream_truncate_buf(stream->cookie, off); + return (0); + } + if (stream->fd == -1) { + errno = EINVAL; + return (-1); + } + error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); + if (error) + return (-1); + error = fstat(stream->fd, &sb); + if (error) + return (-1); + error = stream_truncate(stream, sb.st_size + off); + return (error); +} + +/* Rewind the stream. */ +int +stream_rewind(struct stream *stream) +{ + int error; + + if (stream->fd == -1) { + errno = EINVAL; + return (-1); + } + if (stream->rdbuf != NULL) + buf_less(stream->rdbuf, buf_count(stream->rdbuf)); + if (stream->wrbuf != NULL) { + error = stream_flush_int(stream, STREAM_FLUSH_NORMAL); + if (error) + return (error); + } + error = lseek(stream->fd, 0, SEEK_SET); + return (error); +} + +/* Return EOF status. */ +int +stream_eof(struct stream *stream) +{ + + return (stream->eof); +} + +/* Close a stream and free any resources held by it. */ +int +stream_close(struct stream *stream) +{ + int error; + + if (stream == NULL) + return (0); + + error = 0; + if (stream->wrbuf != NULL) + error = stream_flush_int(stream, STREAM_FLUSH_CLOSING); + stream_filter_fini(stream); + if (stream->closefn != NULL) + /* + * We might overwrite a previous error from stream_flush(), + * but we have no choice, because wether it had worked or + * not, we need to close the file descriptor. + */ + error = (*stream->closefn)(stream->cookie); + if (stream->rdbuf != NULL) + buf_free(stream->rdbuf); + if (stream->wrbuf != NULL) + buf_free(stream->wrbuf); + free(stream); + return (error); +} + +/* The default fill method. */ +static ssize_t +stream_fill_default(struct stream *stream, struct buf *buf) +{ + ssize_t n; + + if (stream->eof) + return (0); + assert(buf_avail(buf) > 0); + n = (*stream->readfn)(stream->cookie, buf->buf + buf->off + buf->in, + buf_avail(buf)); + if (n < 0) + return (-1); + if (n == 0) { + stream->eof = 1; + return (0); + } + buf_more(buf, n); + return (n); +} + +/* + * Refill the read buffer. This function is not permitted to return + * without having made more bytes available, unless there was an error. + * Moreover, stream_fill() returns the number of bytes added. + */ +static ssize_t +stream_fill(struct stream *stream) +{ + struct stream_filter *filter; + struct buf *buf; +#ifndef NDEBUG + size_t oldcount; +#endif + ssize_t n; + + filter = stream->filter; + buf = stream->rdbuf; + buf_prewrite(buf); +#ifndef NDEBUG + oldcount = buf_count(buf); +#endif + n = (*filter->fillfn)(stream, buf); + assert((n > 0 && n == (signed)(buf_count(buf) - oldcount)) || + (n <= 0 && buf_count(buf) == oldcount)); + return (n); +} + +/* + * Lookup a stream filter. + * + * We are not supposed to get passed an invalid filter id, since + * filter ids are an enum type and we don't have invalid filter + * ids in the enum :-). Thus, we are not checking for out of + * bounds access here. If it happens, it's the caller's fault + * anyway. + */ +static struct stream_filter * +stream_filter_lookup(stream_filter_t id) +{ + struct stream_filter *filter; + + filter = stream_filters; + while (filter->id != id) + filter++; + return (filter); +} + +static int +stream_filter_init(struct stream *stream, void *data) +{ + struct stream_filter *filter; + int error; + + filter = stream->filter; + if (filter->initfn == NULL) + return (0); + error = (*filter->initfn)(stream, data); + return (error); +} + +static void +stream_filter_fini(struct stream *stream) +{ + struct stream_filter *filter; + + filter = stream->filter; + if (filter->finifn != NULL) + (*filter->finifn)(stream); +} + +/* + * Start a filter on a stream. + */ +int +stream_filter_start(struct stream *stream, stream_filter_t id, void *data) +{ + struct stream_filter *filter; + int error; + + filter = stream->filter; + if (id == filter->id) + return (0); + stream_filter_fini(stream); + stream->filter = stream_filter_lookup(id); + stream->fdata = NULL; + error = stream_filter_init(stream, data); + return (error); +} + + +/* Stop a filter, this is equivalent to setting the null filter. */ +void +stream_filter_stop(struct stream *stream) +{ + + stream_filter_start(stream, STREAM_FILTER_NULL, NULL); +} + +/* The zlib stream filter implementation. */ + +/* Take no chances with zlib... */ +static void * +zfilter_alloc(void __unused *opaque, unsigned int items, unsigned int size) +{ + + return (xmalloc(items * size)); +} + +static void +zfilter_free(void __unused *opaque, void *ptr) +{ + + free(ptr); +} + +static int +zfilter_init(struct stream *stream, void __unused *data) +{ + struct zfilter *zf; + struct buf *buf; + z_stream *state; + int rv; + + zf = xmalloc(sizeof(struct zfilter)); + memset(zf, 0, sizeof(struct zfilter)); + if (stream->rdbuf != NULL) { + state = xmalloc(sizeof(z_stream)); + state->zalloc = zfilter_alloc; + state->zfree = zfilter_free; + state->opaque = Z_NULL; + rv = inflateInit(state); + if (rv != Z_OK) + errx(1, "inflateInit: %s", state->msg); + buf = buf_new(buf_size(stream->rdbuf)); + zf->rdbuf = stream->rdbuf; + stream->rdbuf = buf; + zf->rdstate = state; + } + if (stream->wrbuf != NULL) { + state = xmalloc(sizeof(z_stream)); + state->zalloc = zfilter_alloc; + state->zfree = zfilter_free; + state->opaque = Z_NULL; + rv = deflateInit(state, Z_DEFAULT_COMPRESSION); + if (rv != Z_OK) + errx(1, "deflateInit: %s", state->msg); + buf = buf_new(buf_size(stream->wrbuf)); + zf->wrbuf = stream->wrbuf; + stream->wrbuf = buf; + zf->wrstate = state; + } + stream->fdata = zf; + return (0); +} + +static void +zfilter_fini(struct stream *stream) +{ + struct zfilter *zf; + struct buf *zbuf; + z_stream *state; + ssize_t n; + + zf = stream->fdata; + if (zf->rdbuf != NULL) { + state = zf->rdstate; + zbuf = zf->rdbuf; + /* + * Even if it has produced all the bytes, zlib sometimes + * hasn't seen the EOF marker, so we need to call inflate() + * again to make sure we have eaten all the zlib'ed bytes. + */ + if ((zf->flags & ZFILTER_EOF) == 0) { + n = zfilter_fill(stream, stream->rdbuf); + assert(n == 0 && zf->flags & ZFILTER_EOF); + } + inflateEnd(state); + free(state); + buf_free(stream->rdbuf); + stream->rdbuf = zbuf; + } + if (zf->wrbuf != NULL) { + state = zf->wrstate; + zbuf = zf->wrbuf; + /* + * Compress the remaining bytes in the buffer, if any, + * and emit an EOF marker as appropriate. We ignore + * the error because we can't do anything about it at + * this point, and it can happen if we're getting + * disconnected. + */ + (void)zfilter_flush(stream, stream->wrbuf, + STREAM_FLUSH_CLOSING); + deflateEnd(state); + free(state); + buf_free(stream->wrbuf); + stream->wrbuf = zbuf; + } + free(zf); +} + +static int +zfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how) +{ + struct zfilter *zf; + struct buf *zbuf; + z_stream *state; + size_t lastin, lastout, ate, prod; + int done, error, flags, rv; + + zf = stream->fdata; + state = zf->wrstate; + zbuf = zf->wrbuf; + + if (how == STREAM_FLUSH_NORMAL) + flags = Z_SYNC_FLUSH; + else + flags = Z_FINISH; + + done = 0; + rv = Z_OK; + +again: + /* + * According to zlib.h, we should have at least 6 bytes + * available when using deflate() with Z_SYNC_FLUSH. + */ + if ((buf_avail(zbuf) < 6 && flags == Z_SYNC_FLUSH) || + rv == Z_BUF_ERROR || buf_avail(buf) == 0) { + error = stream_flush_default(stream, zbuf, how); + if (error) + return (error); + } + + state->next_in = (Bytef *)(buf->buf + buf->off); + state->avail_in = buf_count(buf); + state->next_out = (Bytef *)(zbuf->buf + zbuf->off + zbuf->in); + state->avail_out = buf_avail(zbuf); + lastin = state->avail_in; + lastout = state->avail_out; + rv = deflate(state, flags); + if (rv != Z_BUF_ERROR && rv != Z_OK && rv != Z_STREAM_END) + errx(1, "deflate: %s", state->msg); + ate = lastin - state->avail_in; + prod = lastout - state->avail_out; + buf_less(buf, ate); + buf_more(zbuf, prod); + if ((flags == Z_SYNC_FLUSH && buf_count(buf) > 0) || + (flags == Z_FINISH && rv != Z_STREAM_END) || + (rv == Z_BUF_ERROR)) + goto again; + + assert(rv == Z_OK || (rv == Z_STREAM_END && flags == Z_FINISH)); + error = stream_flush_default(stream, zbuf, how); + return (error); +} + +static ssize_t +zfilter_fill(struct stream *stream, struct buf *buf) +{ + struct zfilter *zf; + struct buf *zbuf; + z_stream *state; + size_t lastin, lastout, new; + ssize_t n; + int rv; + + zf = stream->fdata; + state = zf->rdstate; + zbuf = zf->rdbuf; + + assert(buf_avail(buf) > 0); + if (buf_count(zbuf) == 0) { + n = stream_fill_default(stream, zbuf); + if (n <= 0) + return (n); + } +again: + assert(buf_count(zbuf) > 0); + state->next_in = (Bytef *)(zbuf->buf + zbuf->off); + state->avail_in = buf_count(zbuf); + state->next_out = (Bytef *)(buf->buf + buf->off + buf->in); + state->avail_out = buf_avail(buf); + lastin = state->avail_in; + lastout = state->avail_out; + rv = inflate(state, Z_SYNC_FLUSH); + buf_less(zbuf, lastin - state->avail_in); + new = lastout - state->avail_out; + if (new == 0 && rv != Z_STREAM_END) { + n = stream_fill_default(stream, zbuf); + if (n == -1) + return (-1); + if (n == 0) + return (0); + goto again; + } + if (rv != Z_STREAM_END && rv != Z_OK) + errx(1, "inflate: %s", state->msg); + if (rv == Z_STREAM_END) + zf->flags |= ZFILTER_EOF; + buf_more(buf, new); + return (new); +} + +/* The MD5 stream filter implementation. */ +static int +md5filter_init(struct stream *stream, void *data) +{ + struct md5filter *mf; + + mf = xmalloc(sizeof(struct md5filter)); + MD5_Init(&mf->ctx); + mf->md5 = data; + mf->lastc = ';'; + mf->state = PRINT; + stream->fdata = mf; + return (0); +} + +static void +md5filter_fini(struct stream *stream) +{ + struct md5filter *mf; + + mf = stream->fdata; + MD5_End(mf->md5, &mf->ctx); + free(stream->fdata); +} + +static ssize_t +md5filter_fill(struct stream *stream, struct buf *buf) +{ + ssize_t n; + + assert(buf_avail(buf) > 0); + n = stream_fill_default(stream, buf); + return (n); +} + +static int +md5filter_flush(struct stream *stream, struct buf *buf, stream_flush_t how) +{ + struct md5filter *mf; + int error; + + mf = stream->fdata; + MD5_Update(&mf->ctx, buf->buf + buf->off, buf->in); + error = stream_flush_default(stream, buf, how); + return (error); +} + +/* MD5 flush for RCS, where whitespaces are omitted. */ +static int +md5rcsfilter_flush(struct stream *stream, struct buf *buf, stream_flush_t how) +{ + struct md5filter *mf; + char *ptr, *end; + char *start; + char space[2]; + int error; + + mf = stream->fdata; + space[0] = ' '; + space[1] = '\0'; + ptr = buf->buf + buf->off; + end = buf->buf + buf->off + buf->in; + +#define IS_WS(var) ((var) == ' ' || (var) == '\n' || (var) == '\t' || \ + (var) == '\010' || (var) == '\013' || (var) == '\f' || \ + (var) == '\r') + +#define IS_SPECIAL(var) ((var) == '$' || (var) == ',' || (var) == ':' || \ + (var) == ';' || (var) == '@') + +#define IS_PRINT(var) (!IS_WS(var) && (var) != '@') + + /* XXX: We can do better than this state machine. */ + while (ptr < end) { + switch (mf->state) { + /* Outside RCS statements. */ + case PRINT: + start = ptr; + while (ptr < end && IS_PRINT(*ptr)) { + mf->lastc = *ptr; + ptr++; + } + MD5_Update(&mf->ctx, start, (ptr - start)); + if (ptr < end) { + if (*ptr == '@') { + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = STRING; + } else { + mf->state = WS; + } + } + break; + case WS: + while (ptr < end && IS_WS(*ptr)) { + ptr++; + } + if (ptr < end) { + if (*ptr == '@') { + if (mf->lastc == '@') { + MD5_Update(&mf->ctx, + space, 1); + } + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = STRING; + } else { + if (!IS_SPECIAL(*ptr) && + !IS_SPECIAL(mf->lastc)) { + MD5_Update(&mf->ctx, + space, 1); + } + mf->state = PRINT; + } + } + break; + case STRING: + start = ptr; + while (ptr < end && *ptr != '@') { + ptr++; + } + MD5_Update(&mf->ctx, start, (ptr - start)); + if (ptr < end) { + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = SEEN; + } + break; + case SEEN: + if (*ptr == '@') { + MD5_Update(&mf->ctx, ptr, 1); + ptr++; + mf->state = STRING; + } else if(IS_WS(*ptr)) { + mf->lastc = '@'; + mf->state = WS; + } else { + mf->state = PRINT; + } + break; + default: + err(1, "Invalid state"); + break; + } + } + + error = stream_flush_default(stream, buf, how); + return (error); +} + diff --git a/usr.bin/csup/stream.h b/usr.bin/csup/stream.h new file mode 100644 index 0000000..2128635 --- /dev/null +++ b/usr.bin/csup/stream.h @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _STREAM_H_ +#define _STREAM_H_ + +#include "misc.h" + +/* Stream filters. */ +typedef enum { + STREAM_FILTER_NULL, + STREAM_FILTER_ZLIB, + STREAM_FILTER_MD5, + STREAM_FILTER_MD5RCS +} stream_filter_t; + +struct stream; +struct buf; + +typedef ssize_t stream_readfn_t(void *, void *, size_t); +typedef ssize_t stream_writefn_t(void *, const void *, size_t); +typedef int stream_closefn_t(void *); + +/* Convenience functions for handling file descriptors. */ +stream_readfn_t stream_read_fd; +stream_writefn_t stream_write_fd; +stream_closefn_t stream_close_fd; + +/* Convenience functions for handling character buffers. */ +stream_readfn_t stream_read_buf; +stream_writefn_t stream_append_buf; +stream_closefn_t stream_close_buf; + +struct stream *stream_open(void *, stream_readfn_t *, stream_writefn_t *, + stream_closefn_t *); +struct stream *stream_open_fd(int, stream_readfn_t *, stream_writefn_t *, + stream_closefn_t *); +struct stream *stream_open_buf(struct buf *); +struct stream *stream_open_file(const char *, int, ...); +int stream_fileno(struct stream *); +ssize_t stream_read(struct stream *, void *, size_t); +ssize_t stream_read_blocking(struct stream *, void *, size_t); +ssize_t stream_write(struct stream *, const void *, size_t); +char *stream_getln(struct stream *, size_t *); +int stream_printf(struct stream *, const char *, ...) + __printflike(2, 3); +int stream_flush(struct stream *); +int stream_sync(struct stream *); +int stream_truncate(struct stream *, off_t); +void stream_truncate_buf(struct buf *, off_t); +int stream_truncate_rel(struct stream *, off_t); +int stream_rewind(struct stream *); +int stream_eof(struct stream *); +int stream_close(struct stream *); +int stream_filter_start(struct stream *, stream_filter_t, void *); +void stream_filter_stop(struct stream *); + +struct buf *buf_new(size_t); +void buf_free(struct buf *); +#endif /* !_STREAM_H_ */ diff --git a/usr.bin/csup/threads.c b/usr.bin/csup/threads.c new file mode 100644 index 0000000..46a9860 --- /dev/null +++ b/usr.bin/csup/threads.c @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2004-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <assert.h> +#include <err.h> +#include <pthread.h> +#include <stdlib.h> + +#include "misc.h" +#include "queue.h" +#include "threads.h" + +/* + * This API is a wrapper around the pthread(3) API, which mainly + * allows me to wait for multiple threads to exit. We use a + * condition variable to signal a thread's death. All threads + * created with this API have a common entry/exit point, so we + * don't need to add any code in the threads themselves. + */ + +/* Structure describing a thread. */ +struct thread { + pthread_t thread; + void *(*start)(void *); + void *data; + struct threads *threads; + LIST_ENTRY(thread) runlist; + STAILQ_ENTRY(thread) deadlist; +}; + +/* A set of threads. */ +struct threads { + pthread_mutex_t threads_mtx; + pthread_cond_t thread_exited; + LIST_HEAD(, thread) threads_running; + STAILQ_HEAD(, thread) threads_dead; +}; + +static void *thread_start(void *); /* Common entry point for threads. */ + +static void threads_lock(struct threads *); +static void threads_unlock(struct threads *); + +static void +threads_lock(struct threads *tds) +{ + int error; + + error = pthread_mutex_lock(&tds->threads_mtx); + assert(!error); +} + +static void +threads_unlock(struct threads *tds) +{ + int error; + + error = pthread_mutex_unlock(&tds->threads_mtx); + assert(!error); +} + +/* Create a new set of threads. */ +struct threads * +threads_new(void) +{ + struct threads *tds; + + tds = xmalloc(sizeof(struct threads)); + pthread_mutex_init(&tds->threads_mtx, NULL); + pthread_cond_init(&tds->thread_exited, NULL); + LIST_INIT(&tds->threads_running); + STAILQ_INIT(&tds->threads_dead); + return (tds); +} + +/* Create a new thread in this set. */ +void +threads_create(struct threads *tds, void *(*start)(void *), void *data) +{ + pthread_attr_t attr; + struct thread *td; + int error; + + td = xmalloc(sizeof(struct thread)); + td->threads = tds; + td->start = start; + td->data = data; + /* We don't use pthread_join() to wait for the threads to finish. */ + pthread_attr_init(&attr); + pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + threads_lock(tds); + error = pthread_create(&td->thread, &attr, thread_start, td); + if (error) + err(1, "pthread_create"); + LIST_INSERT_HEAD(&tds->threads_running, td, runlist); + threads_unlock(tds); +} + +/* Wait for a thread in the set to exit, and return its data pointer. */ +void * +threads_wait(struct threads *tds) +{ + struct thread *td; + void *data; + + threads_lock(tds); + while (STAILQ_EMPTY(&tds->threads_dead)) { + assert(!LIST_EMPTY(&tds->threads_running)); + pthread_cond_wait(&tds->thread_exited, &tds->threads_mtx); + } + td = STAILQ_FIRST(&tds->threads_dead); + STAILQ_REMOVE_HEAD(&tds->threads_dead, deadlist); + threads_unlock(tds); + data = td->data; + free(td); + return (data); +} + +/* Free a threads set. */ +void +threads_free(struct threads *tds) +{ + + assert(LIST_EMPTY(&tds->threads_running)); + assert(STAILQ_EMPTY(&tds->threads_dead)); + pthread_cond_destroy(&tds->thread_exited); + pthread_mutex_destroy(&tds->threads_mtx); + free(tds); +} + +/* + * Common entry point for threads. This just calls the real start + * routine, and then signals the thread's death, after having + * removed the thread from the list. + */ +static void * +thread_start(void *data) +{ + struct threads *tds; + struct thread *td; + + td = data; + tds = td->threads; + td->start(td->data); + threads_lock(tds); + LIST_REMOVE(td, runlist); + STAILQ_INSERT_TAIL(&tds->threads_dead, td, deadlist); + pthread_cond_signal(&tds->thread_exited); + threads_unlock(tds); + return (NULL); +} diff --git a/usr.bin/csup/threads.h b/usr.bin/csup/threads.h new file mode 100644 index 0000000..66153ce --- /dev/null +++ b/usr.bin/csup/threads.h @@ -0,0 +1,38 @@ +/*- + * Copyright (c) 2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _THREADS_H_ +#define _THREADS_H_ + +struct threads; + +struct threads *threads_new(void); +void threads_create(struct threads *, void *(*)(void *), void *); +void *threads_wait(struct threads *); +void threads_free(struct threads *); + +#endif /* !_THREADS_H_ */ diff --git a/usr.bin/csup/token.h b/usr.bin/csup/token.h new file mode 100644 index 0000000..0dc3ec0 --- /dev/null +++ b/usr.bin/csup/token.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _TOKEN_H_ +#define _TOKEN_H_ + +void yyerror(const char *); +int yylex(void); +int yyparse(void); + +/* Parsing tokens. */ +#define PT_BASE 0 +#define PT_DATE 1 +#define PT_HOST 2 +#define PT_PREFIX 3 +#define PT_RELEASE 4 +#define PT_TAG 5 +#define PT_UMASK 6 +#define PT_COMPRESS 7 +#define PT_DELETE 8 +#define PT_USE_REL_SUFFIX 9 +#define PT_LIST 10 +#define PT_NORSYNC 11 + +#endif /* !_TOKEN_H_ */ diff --git a/usr.bin/csup/token.l b/usr.bin/csup/token.l new file mode 100644 index 0000000..267e61f --- /dev/null +++ b/usr.bin/csup/token.l @@ -0,0 +1,80 @@ +%{ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include "parse.h" +#include "misc.h" +#include "token.h" + +#define YY_NO_UNPUT + +int lineno = 1; + +%} + +%option noyywrap + +%% + +[ \t]+ ; +#.* ; +\*default { return DEFAULT; } +base { yylval.i = PT_BASE; return NAME; } +date { yylval.i = PT_DATE; return NAME; } +host { yylval.i = PT_HOST; return NAME; } +prefix { yylval.i = PT_PREFIX; return NAME; } +release { yylval.i = PT_RELEASE; return NAME; } +tag { yylval.i = PT_TAG; return NAME; } +umask { yylval.i = PT_UMASK; return NAME; } +list { yylval.i = PT_LIST; return NAME; } +norsync { yylval.i = PT_NORSYNC; return NAME; } += { return EQUAL; } +compress { yylval.i = PT_COMPRESS; return BOOLEAN; } +delete { yylval.i = PT_DELETE; return BOOLEAN; } +use-rel-suffix { yylval.i = PT_USE_REL_SUFFIX; return BOOLEAN; } +[a-zA-Z0-9./_-]+ { + yylval.str = strdup(yytext); + if (yylval.str == NULL) + err(1, "strdup"); + return STRING; + } +\n lineno++; + +%% + +void +yyerror(const char *s) +{ + + lprintf(-1, "Parse error line %d: %s: %s\n", lineno, s, yytext); + exit(1); +} diff --git a/usr.bin/csup/updater.c b/usr.bin/csup/updater.c new file mode 100644 index 0000000..d9f4210 --- /dev/null +++ b/usr.bin/csup/updater.c @@ -0,0 +1,2044 @@ +/*- + * Copyright (c) 2003-2006, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "config.h" +#include "diff.h" +#include "fattr.h" +#include "fixups.h" +#include "keyword.h" +#include "updater.h" +#include "misc.h" +#include "mux.h" +#include "proto.h" +#include "rcsfile.h" +#include "status.h" +#include "stream.h" + +/* Internal error codes. */ +#define UPDATER_ERR_PROTO (-1) /* Protocol error. */ +#define UPDATER_ERR_MSG (-2) /* Error is in updater->errmsg. */ +#define UPDATER_ERR_READ (-3) /* Error reading from server. */ +#define UPDATER_ERR_DELETELIM (-4) /* File deletion limit exceeded. */ + +#define BUFSIZE 4096 + +/* Everything needed to update a file. */ +struct file_update { + struct statusrec srbuf; + char *destpath; + char *temppath; + char *origpath; + char *coname; /* Points somewhere in destpath. */ + char *wantmd5; + struct coll *coll; + struct status *st; + /* Those are only used for diff updating. */ + char *author; + struct stream *orig; + struct stream *to; + int attic; + int expand; +}; + +struct updater { + struct config *config; + struct stream *rd; + char *errmsg; + int deletecount; +}; + +static struct file_update *fup_new(struct coll *, struct status *); +static int fup_prepare(struct file_update *, char *, int); +static void fup_cleanup(struct file_update *); +static void fup_free(struct file_update *); + +static void updater_prunedirs(char *, char *); +static int updater_batch(struct updater *, int); +static int updater_docoll(struct updater *, struct file_update *, int); +static int updater_delete(struct updater *, struct file_update *); +static void updater_deletefile(const char *); +static int updater_checkout(struct updater *, struct file_update *, int); +static int updater_addfile(struct updater *, struct file_update *, + char *, int); +int updater_addelta(struct rcsfile *, struct stream *, char *); +static int updater_setattrs(struct updater *, struct file_update *, + char *, char *, char *, char *, char *, struct fattr *); +static int updater_setdirattrs(struct updater *, struct coll *, + struct file_update *, char *, char *); +static int updater_updatefile(struct updater *, struct file_update *fup, + const char *, int); +static int updater_updatenode(struct updater *, struct coll *, + struct file_update *, char *, char *); +static int updater_diff(struct updater *, struct file_update *); +static int updater_diff_batch(struct updater *, struct file_update *); +static int updater_diff_apply(struct updater *, struct file_update *, + char *); +static int updater_rcsedit(struct updater *, struct file_update *, char *, + char *); +int updater_append_file(struct updater *, struct file_update *, + off_t); +static int updater_rsync(struct updater *, struct file_update *, size_t); +static int updater_read_checkout(struct stream *, struct stream *); + +static struct file_update * +fup_new(struct coll *coll, struct status *st) +{ + struct file_update *fup; + + fup = xmalloc(sizeof(struct file_update)); + memset(fup, 0, sizeof(*fup)); + fup->coll = coll; + fup->st = st; + return (fup); +} + +static int +fup_prepare(struct file_update *fup, char *name, int attic) +{ + struct coll *coll; + + coll = fup->coll; + fup->attic = 0; + fup->origpath = NULL; + + if (coll->co_options & CO_CHECKOUTMODE) + fup->destpath = checkoutpath(coll->co_prefix, name); + else { + fup->destpath = cvspath(coll->co_prefix, name, attic); + fup->origpath = atticpath(coll->co_prefix, name); + /* If they're equal, we don't need special care. */ + if (fup->origpath != NULL && + strcmp(fup->origpath, fup->destpath) == 0) { + free(fup->origpath); + fup->origpath = NULL; + } + fup->attic = attic; + } + if (fup->destpath == NULL) + return (-1); + fup->coname = fup->destpath + coll->co_prefixlen + 1; + return (0); +} + +/* Called after each file update to reinit the structure. */ +static void +fup_cleanup(struct file_update *fup) +{ + struct statusrec *sr; + + sr = &fup->srbuf; + + if (fup->destpath != NULL) { + free(fup->destpath); + fup->destpath = NULL; + } + if (fup->temppath != NULL) { + free(fup->temppath); + fup->temppath = NULL; + } + if (fup->origpath != NULL) { + free(fup->origpath); + fup->origpath = NULL; + } + fup->coname = NULL; + if (fup->author != NULL) { + free(fup->author); + fup->author = NULL; + } + fup->expand = 0; + if (fup->wantmd5 != NULL) { + free(fup->wantmd5); + fup->wantmd5 = NULL; + } + if (fup->orig != NULL) { + stream_close(fup->orig); + fup->orig = NULL; + } + if (fup->to != NULL) { + stream_close(fup->to); + fup->to = NULL; + } + if (sr->sr_file != NULL) + free(sr->sr_file); + if (sr->sr_tag != NULL) + free(sr->sr_tag); + if (sr->sr_date != NULL) + free(sr->sr_date); + if (sr->sr_revnum != NULL) + free(sr->sr_revnum); + if (sr->sr_revdate != NULL) + free(sr->sr_revdate); + fattr_free(sr->sr_clientattr); + fattr_free(sr->sr_serverattr); + memset(sr, 0, sizeof(*sr)); +} + +static void +fup_free(struct file_update *fup) +{ + + fup_cleanup(fup); + free(fup); +} + +void * +updater(void *arg) +{ + struct thread_args *args; + struct updater upbuf, *up; + int error; + + args = arg; + + up = &upbuf; + up->config = args->config; + up->rd = args->rd; + up->errmsg = NULL; + up->deletecount = 0; + + error = updater_batch(up, 0); + + /* + * Make sure to close the fixups even in case of an error, + * so that the lister thread doesn't block indefinitely. + */ + fixups_close(up->config->fixups); + if (!error) + error = updater_batch(up, 1); + switch (error) { + case UPDATER_ERR_PROTO: + xasprintf(&args->errmsg, "Updater failed: Protocol error"); + args->status = STATUS_FAILURE; + break; + case UPDATER_ERR_MSG: + xasprintf(&args->errmsg, "Updater failed: %s", up->errmsg); + free(up->errmsg); + args->status = STATUS_FAILURE; + break; + case UPDATER_ERR_READ: + if (stream_eof(up->rd)) { + xasprintf(&args->errmsg, "Updater failed: " + "Premature EOF from server"); + } else { + xasprintf(&args->errmsg, "Updater failed: " + "Network read failure: %s", strerror(errno)); + } + args->status = STATUS_TRANSIENTFAILURE; + break; + case UPDATER_ERR_DELETELIM: + xasprintf(&args->errmsg, "Updater failed: " + "File deletion limit exceeded"); + args->status = STATUS_FAILURE; + break; + default: + assert(error == 0); + args->status = STATUS_SUCCESS; + }; + return (NULL); +} + +static int +updater_batch(struct updater *up, int isfixups) +{ + struct stream *rd; + struct coll *coll; + struct status *st; + struct file_update *fup; + char *line, *cmd, *errmsg, *collname, *release; + int error; + + rd = up->rd; + STAILQ_FOREACH(coll, &up->config->colls, co_next) { + if (coll->co_options & CO_SKIP) + continue; + umask(coll->co_umask); + line = stream_getln(rd, NULL); + if (line == NULL) + return (UPDATER_ERR_READ); + cmd = proto_get_ascii(&line); + collname = proto_get_ascii(&line); + release = proto_get_ascii(&line); + if (release == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + if (strcmp(cmd, "COLL") != 0 || + strcmp(collname, coll->co_name) != 0 || + strcmp(release, coll->co_release) != 0) + return (UPDATER_ERR_PROTO); + + if (!isfixups) + lprintf(1, "Updating collection %s/%s\n", coll->co_name, + coll->co_release); + + if (coll->co_options & CO_COMPRESS) + stream_filter_start(rd, STREAM_FILTER_ZLIB, NULL); + + st = status_open(coll, coll->co_scantime, &errmsg); + if (st == NULL) { + up->errmsg = errmsg; + return (UPDATER_ERR_MSG); + } + fup = fup_new(coll, st); + error = updater_docoll(up, fup, isfixups); + status_close(st, &errmsg); + fup_free(fup); + if (errmsg != NULL) { + /* Discard previous error. */ + if (up->errmsg != NULL) + free(up->errmsg); + up->errmsg = errmsg; + return (UPDATER_ERR_MSG); + } + if (error) + return (error); + + if (coll->co_options & CO_COMPRESS) + stream_filter_stop(rd); + } + line = stream_getln(rd, NULL); + if (line == NULL) + return (UPDATER_ERR_READ); + if (strcmp(line, ".") != 0) + return (UPDATER_ERR_PROTO); + return (0); +} + +static int +updater_docoll(struct updater *up, struct file_update *fup, int isfixups) +{ + struct stream *rd; + struct coll *coll; + struct statusrec srbuf, *sr; + struct fattr *rcsattr, *tmp; + char *attr, *cmd, *blocksize, *line, *msg; + char *name, *tag, *date, *revdate; + char *expand, *wantmd5, *revnum; + char *optstr, *rcsopt, *pos; + time_t t; + off_t position; + int attic, error, needfixupmsg; + + error = 0; + rd = up->rd; + coll = fup->coll; + needfixupmsg = isfixups; + while ((line = stream_getln(rd, NULL)) != NULL) { + if (strcmp(line, ".") == 0) + break; + memset(&srbuf, 0, sizeof(srbuf)); + if (needfixupmsg) { + lprintf(1, "Applying fixups for collection %s/%s\n", + coll->co_name, coll->co_release); + needfixupmsg = 0; + } + cmd = proto_get_ascii(&line); + if (cmd == NULL || strlen(cmd) != 1) + return (UPDATER_ERR_PROTO); + switch (cmd[0]) { + case 'T': + /* Update recorded information for checked-out file. */ + name = proto_get_ascii(&line); + tag = proto_get_ascii(&line); + date = proto_get_ascii(&line); + revnum = proto_get_ascii(&line); + revdate = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + + rcsattr = fattr_decode(attr); + if (rcsattr == NULL) + return (UPDATER_ERR_PROTO); + + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + error = updater_setattrs(up, fup, name, tag, date, + revnum, revdate, rcsattr); + fattr_free(rcsattr); + if (error) + return (error); + break; + case 'c': + /* Checkout dead file. */ + name = proto_get_ascii(&line); + tag = proto_get_ascii(&line); + date = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + /* Theoritically, the file does not exist on the client. + Just to make sure, we'll delete it here, if it + exists. */ + if (access(fup->destpath, F_OK) == 0) { + error = updater_delete(up, fup); + if (error) + return (error); + } + + sr = &srbuf; + sr->sr_type = SR_CHECKOUTDEAD; + sr->sr_file = name; + sr->sr_tag = tag; + sr->sr_date = date; + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + + error = status_put(fup->st, sr); + fattr_free(sr->sr_serverattr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'U': + /* Update live checked-out file. */ + name = proto_get_ascii(&line); + tag = proto_get_ascii(&line); + date = proto_get_ascii(&line); + proto_get_ascii(&line); /* XXX - oldRevNum */ + proto_get_ascii(&line); /* XXX - fromAttic */ + proto_get_ascii(&line); /* XXX - logLines */ + expand = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + wantmd5 = proto_get_ascii(&line); + if (wantmd5 == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + + sr = &fup->srbuf; + sr->sr_type = SR_CHECKOUTLIVE; + sr->sr_file = xstrdup(name); + sr->sr_date = xstrdup(date); + sr->sr_tag = xstrdup(tag); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + + fup->expand = keyword_decode_expand(expand); + if (fup->expand == -1) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + + fup->wantmd5 = xstrdup(wantmd5); + fup->temppath = tempname(fup->destpath); + error = updater_diff(up, fup); + if (error) + return (error); + break; + case 'u': + /* Update dead checked-out file. */ + name = proto_get_ascii(&line); + tag = proto_get_ascii(&line); + date = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + error = updater_delete(up, fup); + if (error) + return (error); + sr = &srbuf; + sr->sr_type = SR_CHECKOUTDEAD; + sr->sr_file = name; + sr->sr_tag = tag; + sr->sr_date = date; + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + error = status_put(fup->st, sr); + fattr_free(sr->sr_serverattr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'C': + case 'Y': + /* Checkout file. */ + name = proto_get_ascii(&line); + tag = proto_get_ascii(&line); + date = proto_get_ascii(&line); + revnum = proto_get_ascii(&line); + revdate = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + + sr = &fup->srbuf; + sr->sr_type = SR_CHECKOUTLIVE; + sr->sr_file = xstrdup(name); + sr->sr_tag = xstrdup(tag); + sr->sr_date = xstrdup(date); + sr->sr_revnum = xstrdup(revnum); + sr->sr_revdate = xstrdup(revdate); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + + t = rcsdatetotime(revdate); + if (t == -1) + return (UPDATER_ERR_PROTO); + + sr->sr_clientattr = fattr_new(FT_FILE, t); + tmp = fattr_forcheckout(sr->sr_serverattr, + coll->co_umask); + fattr_override(sr->sr_clientattr, tmp, FA_MASK); + fattr_free(tmp); + fattr_mergedefault(sr->sr_clientattr); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + fup->temppath = tempname(fup->destpath); + if (*cmd == 'Y') + error = updater_checkout(up, fup, 1); + else + error = updater_checkout(up, fup, 0); + if (error) + return (error); + break; + case 'D': + /* Delete file. */ + name = proto_get_ascii(&line); + if (name == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + error = updater_delete(up, fup); + if (error) + return (error); + error = status_delete(fup->st, name, 0); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'A': + case 'a': + case 'R': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (name == NULL || attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'a'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + if (attic) + lprintf(1, " Create %s -> Attic\n", name); + else + lprintf(1, " Create %s\n", name); + error = updater_addfile(up, fup, attr, 0); + if (error) + return (error); + break; + case 'r': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + blocksize = proto_get_ascii(&line); + wantmd5 = proto_get_ascii(&line); + if (name == NULL || attr == NULL || blocksize == NULL || + wantmd5 == NULL) { + return (UPDATER_ERR_PROTO); + } + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + fup->wantmd5 = xstrdup(wantmd5); + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + sr->sr_type = SR_FILELIVE; + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + error = updater_rsync(up, fup, strtol(blocksize, NULL, + 10)); + if (error) + return (error); + break; + case 'I': + /* + * Create directory and add DirDown entry in status + * file. + */ + name = proto_get_ascii(&line); + if (name == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + sr = &fup->srbuf; + sr->sr_type = SR_DIRDOWN; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = NULL; + sr->sr_clientattr = fattr_new(FT_DIRECTORY, -1); + fattr_mergedefault(sr->sr_clientattr); + + error = mkdirhier(fup->destpath, coll->co_umask); + if (error) + return (UPDATER_ERR_PROTO); + if (access(fup->destpath, F_OK) != 0) { + lprintf(1, " Mkdir %s\n", name); + error = fattr_makenode(sr->sr_clientattr, + fup->destpath); + if (error) + return (UPDATER_ERR_PROTO); + } + error = status_put(fup->st, sr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'i': + /* Remove DirDown entry in status file. */ + name = proto_get_ascii(&line); + if (name == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + error = status_delete(fup->st, name, 0); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'J': + /* + * Set attributes of directory and update DirUp entry in + * status file. + */ + name = proto_get_ascii(&line); + if (name == NULL) + return (UPDATER_ERR_PROTO); + attr = proto_get_ascii(&line); + if (attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + error = updater_setdirattrs(up, coll, fup, name, attr); + if (error) + return (error); + break; + case 'j': + /* + * Remove directory and delete its DirUp entry in status + * file. + */ + name = proto_get_ascii(&line); + if (name == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + if (error) + return (UPDATER_ERR_PROTO); + lprintf(1, " Rmdir %s\n", name); + updater_deletefile(fup->destpath); + error = status_delete(fup->st, name, 0); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'L': + case 'l': + name = proto_get_ascii(&line); + if (name == NULL) + return (UPDATER_ERR_PROTO); + attr = proto_get_ascii(&line); + if (attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'l'); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + sr->sr_clientattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL || + sr->sr_clientattr == NULL) + return (UPDATER_ERR_PROTO); + + /* Save space. Described in detail in updatefile. */ + if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) + || fattr_getlinkcount(sr->sr_clientattr) <= 1) + fattr_maskout(sr->sr_clientattr, + FA_DEV | FA_INODE); + fattr_maskout(sr->sr_clientattr, FA_FLAGS); + error = status_put(fup->st, sr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + break; + case 'N': + case 'n': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (name == NULL || attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'n'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + sr = &fup->srbuf; + sr->sr_type = (attic ? SR_FILEDEAD : SR_FILELIVE); + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + sr->sr_clientattr = fattr_new(FT_SYMLINK, -1); + fattr_mergedefault(sr->sr_clientattr); + fattr_maskout(sr->sr_clientattr, FA_FLAGS); + error = updater_updatenode(up, coll, fup, name, attr); + if (error) + return (error); + break; + case 'V': + case 'v': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + optstr = proto_get_ascii(&line); + wantmd5 = proto_get_ascii(&line); + rcsopt = NULL; /* XXX: Not supported. */ + if (attr == NULL || line != NULL || wantmd5 == NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'v'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + fup->temppath = tempname(fup->destpath); + fup->wantmd5 = xstrdup(wantmd5); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + + error = 0; + error = updater_rcsedit(up, fup, name, rcsopt); + if (error) + return (error); + break; + case 'X': + case 'x': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + if (name == NULL || attr == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + attic = (cmd[0] == 'x'); + error = fup_prepare(fup, name, attic); + if (error) + return (UPDATER_ERR_PROTO); + + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_type = attic ? SR_FILEDEAD : SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + lprintf(1, " Fixup %s\n", name); + error = updater_addfile(up, fup, attr, 1); + if (error) + return (error); + break; + case 'Z': + name = proto_get_ascii(&line); + attr = proto_get_ascii(&line); + pos = proto_get_ascii(&line); + if (name == NULL || attr == NULL || pos == NULL || + line != NULL) + return (UPDATER_ERR_PROTO); + error = fup_prepare(fup, name, 0); + fup->temppath = tempname(fup->destpath); + sr = &fup->srbuf; + sr->sr_type = SR_FILELIVE; + sr->sr_file = xstrdup(name); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + position = strtol(pos, NULL, 10); + lprintf(1, " Append to %s\n", name); + error = updater_append_file(up, fup, position); + if (error) + return (error); + break; + case '!': + /* Warning from server. */ + msg = proto_get_rest(&line); + if (msg == NULL) + return (UPDATER_ERR_PROTO); + lprintf(-1, "Server warning: %s\n", msg); + break; + default: + return (UPDATER_ERR_PROTO); + } + fup_cleanup(fup); + } + if (line == NULL) + return (UPDATER_ERR_READ); + return (0); +} + +/* Delete file. */ +static int +updater_delete(struct updater *up, struct file_update *fup) +{ + struct config *config; + struct coll *coll; + + config = up->config; + coll = fup->coll; + if (coll->co_options & CO_DELETE) { + lprintf(1, " Delete %s\n", fup->coname); + if (config->deletelim >= 0 && + up->deletecount >= config->deletelim) + return (UPDATER_ERR_DELETELIM); + up->deletecount++; + updater_deletefile(fup->destpath); + if (coll->co_options & CO_CHECKOUTMODE) + updater_prunedirs(coll->co_prefix, fup->destpath); + } else { + lprintf(1," NoDelete %s\n", fup->coname); + } + return (0); +} + +static void +updater_deletefile(const char *path) +{ + int error; + + error = fattr_delete(path); + if (error && errno != ENOENT) { + lprintf(-1, "Cannot delete \"%s\": %s\n", + path, strerror(errno)); + } +} + +static int +updater_setattrs(struct updater *up, struct file_update *fup, char *name, + char *tag, char *date, char *revnum, char *revdate, struct fattr *rcsattr) +{ + struct statusrec sr; + struct status *st; + struct coll *coll; + struct fattr *fileattr, *fa; + char *path; + int error, rv; + + coll = fup->coll; + st = fup->st; + path = fup->destpath; + + fileattr = fattr_frompath(path, FATTR_NOFOLLOW); + if (fileattr == NULL) { + /* The file has vanished. */ + error = status_delete(st, name, 0); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + return (0); + } + fa = fattr_forcheckout(rcsattr, coll->co_umask); + fattr_override(fileattr, fa, FA_MASK); + fattr_free(fa); + + rv = fattr_install(fileattr, path, NULL); + if (rv == -1) { + lprintf(1, " SetAttrs %s\n", fup->coname); + fattr_free(fileattr); + xasprintf(&up->errmsg, "Cannot set attributes for \"%s\": %s", + path, strerror(errno)); + return (UPDATER_ERR_MSG); + } + if (rv == 1) { + lprintf(1, " SetAttrs %s\n", fup->coname); + fattr_free(fileattr); + fileattr = fattr_frompath(path, FATTR_NOFOLLOW); + if (fileattr == NULL) { + /* We're being very unlucky. */ + error = status_delete(st, name, 0); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + return (0); + } + } + + fattr_maskout(fileattr, FA_COIGNORE); + + sr.sr_type = SR_CHECKOUTLIVE; + sr.sr_file = name; + sr.sr_tag = tag; + sr.sr_date = date; + sr.sr_revnum = revnum; + sr.sr_revdate = revdate; + sr.sr_clientattr = fileattr; + sr.sr_serverattr = rcsattr; + + error = status_put(st, &sr); + fattr_free(fileattr); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + return (0); +} + +static int +updater_updatefile(struct updater *up, struct file_update *fup, + const char *md5, int isfixup) +{ + struct coll *coll; + struct status *st; + struct statusrec *sr; + struct fattr *fileattr; + int error, rv; + + coll = fup->coll; + sr = &fup->srbuf; + st = fup->st; + + if (strcmp(fup->wantmd5, md5) != 0) { + if (isfixup) { + lprintf(-1, "%s: Checksum mismatch -- " + "file not updated\n", fup->destpath); + } else { + lprintf(-1, "%s: Checksum mismatch -- " + "will transfer entire file\n", fup->destpath); + fixups_put(up->config->fixups, fup->coll, sr->sr_file); + } + if (coll->co_options & CO_KEEPBADFILES) + lprintf(-1, "Bad version saved in %s\n", fup->temppath); + else + updater_deletefile(fup->temppath); + return (0); + } + + fattr_umask(sr->sr_clientattr, coll->co_umask); + rv = fattr_install(sr->sr_clientattr, fup->destpath, fup->temppath); + if (rv == -1) { + xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s", + fup->temppath, fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + + /* XXX Executes */ + /* + * We weren't necessarily able to set all the file attributes to the + * desired values, and any executes may have altered the attributes. + * To make sure we record the actual attribute values, we fetch + * them from the file. + * + * However, we preserve the link count as received from the + * server. This is important for preserving hard links in mirror + * mode. + */ + fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (fileattr == NULL) { + xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT); + fattr_free(sr->sr_clientattr); + sr->sr_clientattr = fileattr; + + /* + * To save space, don't write out the device and inode unless + * the link count is greater than 1. These attributes are used + * only for detecting hard links. If the link count is 1 then we + * know there aren't any hard links. + */ + if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || + fattr_getlinkcount(sr->sr_clientattr) <= 1) + fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); + + if (coll->co_options & CO_CHECKOUTMODE) + fattr_maskout(sr->sr_clientattr, FA_COIGNORE); + + error = status_put(st, sr); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + return (0); +} + +/* + * Update attributes of a directory. + */ +static int +updater_setdirattrs(struct updater *up, struct coll *coll, + struct file_update *fup, char *name, char *attr) +{ + struct statusrec *sr; + struct fattr *fa; + int error, rv; + + sr = &fup->srbuf; + sr->sr_type = SR_DIRUP; + sr->sr_file = xstrdup(name); + sr->sr_clientattr = fattr_decode(attr); + sr->sr_serverattr = fattr_decode(attr); + if (sr->sr_clientattr == NULL || sr->sr_serverattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_mergedefault(sr->sr_clientattr); + fattr_umask(sr->sr_clientattr, coll->co_umask); + rv = fattr_install(sr->sr_clientattr, fup->destpath, NULL); + lprintf(1, " SetAttrs %s\n", name); + if (rv == -1) { + xasprintf(&up->errmsg, "Cannot install \"%s\" to \"%s\": %s", + fup->temppath, fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + /* + * Now, make sure they were set and record what was set in the status + * file. + */ + fa = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (fa == NULL) { + xasprintf(&up->errmsg, "Cannot open \%s\": %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_free(sr->sr_clientattr); + fattr_maskout(fa, FA_FLAGS); + sr->sr_clientattr = fa; + error = status_put(fup->st, sr); + if (error) { + up->errmsg = status_errmsg(fup->st); + return (UPDATER_ERR_MSG); + } + + return (0); +} + +static int +updater_diff(struct updater *up, struct file_update *fup) +{ + char md5[MD5_DIGEST_SIZE]; + struct coll *coll; + struct statusrec *sr; + struct fattr *fa, *tmp; + char *author, *path, *revnum, *revdate; + char *line, *cmd; + int error; + + coll = fup->coll; + sr = &fup->srbuf; + path = fup->destpath; + + lprintf(1, " Edit %s\n", fup->coname); + while ((line = stream_getln(up->rd, NULL)) != NULL) { + if (strcmp(line, ".") == 0) + break; + cmd = proto_get_ascii(&line); + if (cmd == NULL || strcmp(cmd, "D") != 0) + return (UPDATER_ERR_PROTO); + revnum = proto_get_ascii(&line); + proto_get_ascii(&line); /* XXX - diffbase */ + revdate = proto_get_ascii(&line); + author = proto_get_ascii(&line); + if (author == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + if (sr->sr_revnum != NULL) + free(sr->sr_revnum); + if (sr->sr_revdate != NULL) + free(sr->sr_revdate); + if (fup->author != NULL) + free(fup->author); + sr->sr_revnum = xstrdup(revnum); + sr->sr_revdate = xstrdup(revdate); + fup->author = xstrdup(author); + if (fup->orig == NULL) { + /* First patch, the "origin" file is the one we have. */ + fup->orig = stream_open_file(path, O_RDONLY); + if (fup->orig == NULL) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", + path, strerror(errno)); + return (UPDATER_ERR_MSG); + } + } else { + /* Subsequent patches. */ + stream_close(fup->orig); + fup->orig = fup->to; + stream_rewind(fup->orig); + unlink(fup->temppath); + free(fup->temppath); + fup->temppath = tempname(path); + } + fup->to = stream_open_file(fup->temppath, + O_RDWR | O_CREAT | O_TRUNC, 0600); + if (fup->to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + lprintf(2, " Add delta %s %s %s\n", sr->sr_revnum, + sr->sr_revdate, fup->author); + error = updater_diff_batch(up, fup); + if (error) + return (error); + } + if (line == NULL) + return (UPDATER_ERR_READ); + + fa = fattr_frompath(path, FATTR_FOLLOW); + tmp = fattr_forcheckout(sr->sr_serverattr, coll->co_umask); + fattr_override(fa, tmp, FA_MASK); + fattr_free(tmp); + fattr_maskout(fa, FA_MODTIME); + sr->sr_clientattr = fa; + + if (MD5_File(fup->temppath, md5) == -1) { + xasprintf(&up->errmsg, + "Cannot calculate checksum for \"%s\": %s", + path, strerror(errno)); + return (UPDATER_ERR_MSG); + } + error = updater_updatefile(up, fup, md5, 0); + return (error); +} + +/* + * Edit a file and add delta. + */ +static int +updater_diff_batch(struct updater *up, struct file_update *fup) +{ + struct stream *rd; + char *cmd, *line, *state, *tok; + int error; + + state = NULL; + rd = up->rd; + while ((line = stream_getln(rd, NULL)) != NULL) { + if (strcmp(line, ".") == 0) + break; + cmd = proto_get_ascii(&line); + if (cmd == NULL || strlen(cmd) != 1) { + error = UPDATER_ERR_PROTO; + goto bad; + } + switch (cmd[0]) { + case 'L': + line = stream_getln(rd, NULL); + /* XXX - We're just eating the log for now. */ + while (line != NULL && strcmp(line, ".") != 0 && + strcmp(line, ".+") != 0) + line = stream_getln(rd, NULL); + if (line == NULL) { + error = UPDATER_ERR_READ; + goto bad; + } + break; + case 'S': + tok = proto_get_ascii(&line); + if (tok == NULL || line != NULL) { + error = UPDATER_ERR_PROTO; + goto bad; + } + if (state != NULL) + free(state); + state = xstrdup(tok); + break; + case 'T': + error = updater_diff_apply(up, fup, state); + if (error) + goto bad; + break; + default: + error = UPDATER_ERR_PROTO; + goto bad; + } + } + if (line == NULL) { + error = UPDATER_ERR_READ; + goto bad; + } + if (state != NULL) + free(state); + return (0); +bad: + if (state != NULL) + free(state); + return (error); +} + +int +updater_diff_apply(struct updater *up, struct file_update *fup, char *state) +{ + struct diffinfo dibuf, *di; + struct coll *coll; + struct statusrec *sr; + int error; + + coll = fup->coll; + sr = &fup->srbuf; + di = &dibuf; + + di->di_rcsfile = sr->sr_file; + di->di_cvsroot = coll->co_cvsroot; + di->di_revnum = sr->sr_revnum; + di->di_revdate = sr->sr_revdate; + di->di_author = fup->author; + di->di_tag = sr->sr_tag; + di->di_state = state; + di->di_expand = fup->expand; + + error = diff_apply(up->rd, fup->orig, fup->to, coll->co_keyword, di, 1); + if (error) { + /* XXX Bad error message */ + xasprintf(&up->errmsg, "Bad diff from server"); + return (UPDATER_ERR_MSG); + } + return (0); +} + +/* Update or create a node. */ +static int +updater_updatenode(struct updater *up, struct coll *coll, + struct file_update *fup, char *name, char *attr) +{ + struct fattr *fa, *fileattr; + struct status *st; + struct statusrec *sr; + int error, rv; + + sr = &fup->srbuf; + st = fup->st; + fa = fattr_decode(attr); + + if (fattr_type(fa) == FT_SYMLINK) { + lprintf(1, " Symlink %s -> %s\n", name, + fattr_getlinktarget(fa)); + } else { + lprintf(1, " Mknod %s\n", name); + } + + /* Create directory. */ + error = mkdirhier(fup->destpath, coll->co_umask); + if (error) + return (UPDATER_ERR_PROTO); + + /* If it does not exist, create it. */ + if (access(fup->destpath, F_OK) != 0) + fattr_makenode(fa, fup->destpath); + + /* + * Coming from attic? I don't think this is a problem since we have + * determined attic before we call this function (Look at UpdateNode in + * cvsup). + */ + fattr_umask(fa, coll->co_umask); + rv = fattr_install(fa, fup->destpath, fup->temppath); + if (rv == -1) { + xasprintf(&up->errmsg, "Cannot update attributes on " + "\"%s\": %s", fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + /* + * XXX: Executes not implemented. Have not encountered much use for it + * yet. + */ + /* + * We weren't necessarily able to set all the file attributes to the + * desired values, and any executes may have altered the attributes. + * To make sure we record the actual attribute values, we fetch + * them from the file. + * + * However, we preserve the link count as received from the + * server. This is important for preserving hard links in mirror + * mode. + */ + fileattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (fileattr == NULL) { + xasprintf(&up->errmsg, "Cannot stat \"%s\": %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_override(fileattr, sr->sr_clientattr, FA_LINKCOUNT); + fattr_free(sr->sr_clientattr); + sr->sr_clientattr = fileattr; + + /* + * To save space, don't write out the device and inode unless + * the link count is greater than 1. These attributes are used + * only for detecting hard links. If the link count is 1 then we + * know there aren't any hard links. + */ + if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || + fattr_getlinkcount(sr->sr_clientattr) <= 1) + fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); + + /* If it is a symlink, write only out it's path. */ + if (fattr_type(fa) == FT_SYMLINK) { + fattr_maskout(sr->sr_clientattr, ~(FA_FILETYPE | + FA_LINKTARGET)); + } + fattr_maskout(sr->sr_clientattr, FA_FLAGS); + error = status_put(st, sr); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + fattr_free(fa); + + return (0); +} + +/* + * Fetches a new file in CVS mode. + */ +static int +updater_addfile(struct updater *up, struct file_update *fup, char *attr, + int isfixup) +{ + struct coll *coll; + struct stream *to; + struct statusrec *sr; + struct fattr *fa; + char buf[BUFSIZE]; + char md5[MD5_DIGEST_SIZE]; + ssize_t nread; + off_t fsize, remains; + char *cmd, *line, *path; + int error; + + coll = fup->coll; + path = fup->destpath; + sr = &fup->srbuf; + fa = fattr_decode(attr); + fsize = fattr_filesize(fa); + + error = mkdirhier(path, coll->co_umask); + if (error) + return (UPDATER_ERR_PROTO); + to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, 0755); + if (to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot create: %s", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + stream_filter_start(to, STREAM_FILTER_MD5, md5); + remains = fsize; + do { + nread = stream_read(up->rd, buf, (BUFSIZE > remains ? + remains : BUFSIZE)); + if (nread == -1) + return (UPDATER_ERR_PROTO); + remains -= nread; + if (stream_write(to, buf, nread) == -1) + goto bad; + } while (remains > 0); + stream_close(to); + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + /* Check for EOF. */ + if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) + return (UPDATER_ERR_PROTO); + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + + cmd = proto_get_ascii(&line); + fup->wantmd5 = proto_get_ascii(&line); + if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) + return (UPDATER_ERR_PROTO); + + sr->sr_clientattr = fattr_frompath(fup->temppath, FATTR_NOFOLLOW); + if (sr->sr_clientattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + error = updater_updatefile(up, fup, md5, isfixup); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + return (error); +bad: + xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, + strerror(errno)); + return (UPDATER_ERR_MSG); +} + +static int +updater_checkout(struct updater *up, struct file_update *fup, int isfixup) +{ + char md5[MD5_DIGEST_SIZE]; + struct statusrec *sr; + struct coll *coll; + struct stream *to; + ssize_t nbytes; + size_t size; + char *cmd, *path, *line; + int error, first; + + coll = fup->coll; + sr = &fup->srbuf; + path = fup->destpath; + + if (isfixup) + lprintf(1, " Fixup %s\n", fup->coname); + else + lprintf(1, " Checkout %s\n", fup->coname); + error = mkdirhier(path, coll->co_umask); + if (error) { + xasprintf(&up->errmsg, + "Cannot create directories leading to \"%s\": %s", + path, strerror(errno)); + return (UPDATER_ERR_MSG); + } + + to = stream_open_file(fup->temppath, + O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot create: %s", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + stream_filter_start(to, STREAM_FILTER_MD5, md5); + line = stream_getln(up->rd, &size); + first = 1; + while (line != NULL) { + if (line[size - 1] == '\n') + size--; + if ((size == 1 && *line == '.') || + (size == 2 && memcmp(line, ".+", 2) == 0)) + break; + if (size >= 2 && memcmp(line, "..", 2) == 0) { + size--; + line++; + } + if (!first) { + nbytes = stream_write(to, "\n", 1); + if (nbytes == -1) + goto bad; + } + nbytes = stream_write(to, line, size); + if (nbytes == -1) + goto bad; + line = stream_getln(up->rd, &size); + first = 0; + } + if (line == NULL) { + stream_close(to); + return (UPDATER_ERR_READ); + } + if (size == 1 && *line == '.') { + nbytes = stream_write(to, "\n", 1); + if (nbytes == -1) + goto bad; + } + stream_close(to); + /* Get the checksum line. */ + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_READ); + cmd = proto_get_ascii(&line); + fup->wantmd5 = proto_get_ascii(&line); + if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) + return (UPDATER_ERR_PROTO); + error = updater_updatefile(up, fup, md5, isfixup); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + if (error) + return (error); + return (0); +bad: + xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, + strerror(errno)); + return (UPDATER_ERR_MSG); +} + +/* + * Remove all empty directories below file. + * This function will trash the path passed to it. + */ +static void +updater_prunedirs(char *base, char *file) +{ + char *cp; + int error; + + while ((cp = strrchr(file, '/')) != NULL) { + *cp = '\0'; + if (strcmp(base, file) == 0) + return; + error = rmdir(file); + if (error) + return; + } +} + +/* + * Edit an RCS file. + */ +static int +updater_rcsedit(struct updater *up, struct file_update *fup, char *name, + char *rcsopt) +{ + struct coll *coll; + struct stream *dest; + struct statusrec *sr; + struct status *st; + struct rcsfile *rf; + struct fattr *oldfattr; + char md5[MD5_DIGEST_SIZE]; + char *branch, *cmd, *expand, *line, *path, *revnum, *tag, *temppath; + int error; + + coll = fup->coll; + sr = &fup->srbuf; + st = fup->st; + temppath = fup->temppath; + path = fup->origpath != NULL ? fup->origpath : fup->destpath; + error = 0; + + /* If the path is new, we must create the Attic dir if needed. */ + if (fup->origpath != NULL) { + error = mkdirhier(fup->destpath, coll->co_umask); + if (error) { + xasprintf(&up->errmsg, "Unable to create Attic dir for " + "%s\n", fup->origpath); + return (UPDATER_ERR_MSG); + } + } + /* + * XXX: we could avoid parsing overhead if we're reading ahead before we + * parse the file. + */ + oldfattr = fattr_frompath(path, FATTR_NOFOLLOW); + if (oldfattr == NULL) { + xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", path, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_merge(sr->sr_serverattr, oldfattr); + rf = NULL; + + /* Macro for making touching an RCS file faster. */ +#define UPDATER_OPENRCS(rf, up, path, name, cvsroot, tag) do { \ + if ((rf) == NULL) { \ + lprintf(1, " Edit %s", fup->coname); \ + if (fup->attic) \ + lprintf(1, " -> Attic"); \ + lprintf(1, "\n"); \ + (rf) = rcsfile_frompath((path), (name), (cvsroot), \ + (tag), 0); \ + if ((rf) == NULL) { \ + xasprintf(&(up)->errmsg, \ + "Error reading rcsfile %s\n", (name)); \ + return (UPDATER_ERR_MSG); \ + } \ + } \ +} while (0) + + while ((line = stream_getln(up->rd, NULL)) != NULL) { + if (strcmp(line, ".") == 0) + break; + cmd = proto_get_ascii(&line); + if (cmd == NULL) { + lprintf(-1, "Error editing %s\n", name); + return (UPDATER_ERR_PROTO); + } + switch(cmd[0]) { + case 'B': + branch = proto_get_ascii(&line); + if (branch == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + break; + case 'b': + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_setval(rf, RCSFILE_BRANCH, NULL); + break; + case 'D': + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + error = updater_addelta(rf, up->rd, line); + if (error) + return (error); + break; + case 'd': + revnum = proto_get_ascii(&line); + if (revnum == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_deleterev(rf, revnum); + break; + case 'E': + expand = proto_get_ascii(&line); + if (expand == NULL || line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_setval(rf, RCSFILE_EXPAND, expand); + break; + case 'T': + tag = proto_get_ascii(&line); + revnum = proto_get_ascii(&line); + if (tag == NULL || revnum == NULL || + line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_addtag(rf, tag, revnum); + break; + case 't': + tag = proto_get_ascii(&line); + revnum = proto_get_ascii(&line); + if (tag == NULL || revnum == NULL || + line != NULL) + return (UPDATER_ERR_PROTO); + UPDATER_OPENRCS(rf, up, path, name, + coll->co_cvsroot, coll->co_tag); + rcsfile_deletetag(rf, tag, revnum); + break; + default: + return (UPDATER_ERR_PROTO); + } + } + + if (rf == NULL) { + fattr_maskout(oldfattr, ~FA_MODTIME); + if (fattr_equal(oldfattr, sr->sr_serverattr)) + lprintf(1, " SetAttrs %s", fup->coname); + else + lprintf(1, " Touch %s", fup->coname); + /* Install new attributes. */ + fattr_umask(sr->sr_serverattr, coll->co_umask); + fattr_install(sr->sr_serverattr, fup->destpath, NULL); + if (fup->attic) + lprintf(1, " -> Attic"); + lprintf(1, "\n"); + fattr_free(oldfattr); + goto finish; + } + + /* Write and rename temp file. */ + dest = stream_open_file(fup->temppath, + O_RDWR | O_CREAT | O_TRUNC, 0600); + if (dest == NULL) { + xasprintf(&up->errmsg, "Error opening file %s for writing: %s\n", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + stream_filter_start(dest, STREAM_FILTER_MD5RCS, md5); + error = rcsfile_write(rf, dest); + stream_close(dest); + rcsfile_free(rf); + if (error) { + xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + +finish: + sr->sr_clientattr = fattr_frompath(path, FATTR_NOFOLLOW); + if (sr->sr_clientattr == NULL) { + xasprintf(&up->errmsg, "%s: Cannot get attributes: %s", + fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + if (rf != NULL) { + error = updater_updatefile(up, fup, md5, 0); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + if (error) + return (error); + } else { + /* Record its attributes since we touched it. */ + if (!(fattr_getmask(sr->sr_clientattr) & FA_LINKCOUNT) || + fattr_getlinkcount(sr->sr_clientattr) <= 1) + fattr_maskout(sr->sr_clientattr, FA_DEV | FA_INODE); + error = status_put(st, sr); + if (error) { + up->errmsg = status_errmsg(st); + return (UPDATER_ERR_MSG); + } + } + + /* In this case, we need to remove the old file afterwards. */ + /* XXX: Can we be sure that a file not edited is moved? I don't think + * this is a problem, since if a file is moved, it should be edited to + * show if it's dead or not. + */ + if (fup->origpath != NULL) + updater_deletefile(fup->origpath); + return (0); +} + +/* + * Add a delta to a RCS file. + */ +int +updater_addelta(struct rcsfile *rf, struct stream *rd, char *cmdline) +{ + struct delta *d; + size_t size; + char *author, *cmd, *diffbase, *line, *logline; + char *revdate, *revnum, *state, *textline; + + revnum = proto_get_ascii(&cmdline); + diffbase = proto_get_ascii(&cmdline); + revdate = proto_get_ascii(&cmdline); + author = proto_get_ascii(&cmdline); + size = 0; + + if (revnum == NULL || revdate == NULL || author == NULL) + return (UPDATER_ERR_PROTO); + + /* First add the delta so we have it. */ + d = rcsfile_addelta(rf, revnum, revdate, author, diffbase); + if (d == NULL) { + lprintf(-1, "Error adding delta %s\n", revnum); + return (UPDATER_ERR_READ); + } + while ((line = stream_getln(rd, NULL)) != NULL) { + if (strcmp(line, ".") == 0) + break; + cmd = proto_get_ascii(&line); + switch (cmd[0]) { + case 'L': + /* Do the same as in 'C' command. */ + logline = stream_getln(rd, &size); + while (logline != NULL) { + if (size == 2 && *logline == '.') + break; + if (size == 3 && + memcmp(logline, ".+", 2) == 0) { + rcsdelta_truncatelog(d, -1); + break; + } + if (size >= 3 && + memcmp(logline, "..", 2) == 0) { + size--; + logline++; + } + if (rcsdelta_appendlog(d, logline, size) + < 0) + return (-1); + logline = stream_getln(rd, &size); + } + break; + case 'N': + case 'n': + /* XXX: Not supported. */ + break; + case 'S': + state = proto_get_ascii(&line); + if (state == NULL) + return (UPDATER_ERR_PROTO); + rcsdelta_setstate(d, state); + break; + case 'T': + /* Do the same as in 'C' command. */ + textline = stream_getln(rd, &size); + while (textline != NULL) { + if (size == 2 && *textline == '.') + break; + if (size == 3 && + memcmp(textline, ".+", 2) == 0) { + /* Truncate newline. */ + rcsdelta_truncatetext(d, -1); + break; + } + if (size >= 3 && + memcmp(textline, "..", 2) == 0) { + size--; + textline++; + } + if (rcsdelta_appendtext(d, textline, + size) < 0) + return (-1); + textline = stream_getln(rd, &size); + } + break; + } + } + + return (0); +} + +int +updater_append_file(struct updater *up, struct file_update *fup, off_t pos) +{ + struct fattr *fa; + struct stream *to; + struct statusrec *sr; + ssize_t nread; + off_t bytes; + char buf[BUFSIZE], md5[MD5_DIGEST_SIZE]; + char *line, *cmd; + int error, fd; + + sr = &fup->srbuf; + fa = sr->sr_serverattr; + to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, + 0755); + if (to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->temppath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + fd = open(fup->destpath, O_RDONLY); + if (fd < 0) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + + stream_filter_start(to, STREAM_FILTER_MD5, md5); + /* First write the existing content. */ + while ((nread = read(fd, buf, BUFSIZE)) > 0) { + if (stream_write(to, buf, nread) == -1) + goto bad; + } + if (nread == -1) { + xasprintf(&up->errmsg, "%s: Error reading: %s", fup->destpath, + strerror(errno)); + return (UPDATER_ERR_MSG); + } + close(fd); + + bytes = fattr_filesize(fa) - pos; + /* Append the new data. */ + do { + nread = stream_read(up->rd, buf, + (BUFSIZE > bytes) ? bytes : BUFSIZE); + if (nread == -1) + return (UPDATER_ERR_PROTO); + bytes -= nread; + if (stream_write(to, buf, nread) == -1) + goto bad; + } while (bytes > 0); + stream_close(to); + + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + /* Check for EOF. */ + if (!(*line == '.' || (strncmp(line, ".<", 2) != 0))) + return (UPDATER_ERR_PROTO); + line = stream_getln(up->rd, NULL); + if (line == NULL) + return (UPDATER_ERR_PROTO); + + cmd = proto_get_ascii(&line); + fup->wantmd5 = proto_get_ascii(&line); + if (fup->wantmd5 == NULL || line != NULL || strcmp(cmd, "5") != 0) + return (UPDATER_ERR_PROTO); + + sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (sr->sr_clientattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + error = updater_updatefile(up, fup, md5, 0); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ + return (error); +bad: + xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, + strerror(errno)); + return (UPDATER_ERR_MSG); +} + +/* + * Read file data from stream of checkout commands, and write it to the + * destination. + */ +static int +updater_read_checkout(struct stream *src, struct stream *dest) +{ + ssize_t nbytes; + size_t size; + char *line; + int first; + + first = 1; + line = stream_getln(src, &size); + while (line != NULL) { + if (line[size - 1] == '\n') + size--; + if ((size == 1 && *line == '.') || + (size == 2 && strncmp(line, ".+", 2) == 0)) + break; + if (size >= 2 && strncmp(line, "..", 2) == 0) { + size--; + line++; + } + if (!first) { + nbytes = stream_write(dest, "\n", 1); + if (nbytes == -1) + return (UPDATER_ERR_MSG); + } + nbytes = stream_write(dest, line, size); + if (nbytes == -1) + return (UPDATER_ERR_MSG); + line = stream_getln(src, &size); + first = 0; + } + if (line == NULL) + return (UPDATER_ERR_READ); + if (size == 1 && *line == '.') { + nbytes = stream_write(dest, "\n", 1); + if (nbytes == -1) + return (UPDATER_ERR_MSG); + } + return (0); +} + +/* Update file using the rsync protocol. */ +static int +updater_rsync(struct updater *up, struct file_update *fup, size_t blocksize) +{ + struct statusrec *sr; + struct stream *to; + char md5[MD5_DIGEST_SIZE]; + ssize_t nbytes; + size_t blocknum, blockstart, blockcount; + char *buf, *line; + int error, orig; + + sr = &fup->srbuf; + + lprintf(1, " Rsync %s\n", fup->coname); + /* First open all files that we are going to work on. */ + to = stream_open_file(fup->temppath, O_WRONLY | O_CREAT | O_TRUNC, + 0600); + if (to == NULL) { + xasprintf(&up->errmsg, "%s: Cannot create: %s", + fup->temppath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + orig = open(fup->destpath, O_RDONLY); + if (orig < 0) { + xasprintf(&up->errmsg, "%s: Cannot open: %s", + fup->destpath, strerror(errno)); + return (UPDATER_ERR_MSG); + } + stream_filter_start(to, STREAM_FILTER_MD5, md5); + + error = updater_read_checkout(up->rd, to); + if (error) { + xasprintf(&up->errmsg, "%s: Cannot write: %s", fup->temppath, + strerror(errno)); + return (error); + } + + /* Buffer must contain blocksize bytes. */ + buf = xmalloc(blocksize); + /* Done with the initial text, read and write chunks. */ + line = stream_getln(up->rd, NULL); + while (line != NULL) { + if (strcmp(line, ".") == 0) + break; + error = UPDATER_ERR_PROTO; + if (proto_get_sizet(&line, &blockstart, 10) != 0) + goto bad; + if (proto_get_sizet(&line, &blockcount, 10) != 0) + goto bad; + /* Read blocks from original file. */ + lseek(orig, (blocksize * blockstart), SEEK_SET); + error = UPDATER_ERR_MSG; + for (blocknum = 0; blocknum < blockcount; blocknum++) { + nbytes = read(orig, buf, blocksize); + if (nbytes < 0) { + xasprintf(&up->errmsg, "%s: Cannot read: %s", + fup->destpath, strerror(errno)); + goto bad; + } + nbytes = stream_write(to, buf, nbytes); + if (nbytes == -1) { + xasprintf(&up->errmsg, "%s: Cannot write: %s", + fup->temppath, strerror(errno)); + goto bad; + } + } + /* Get the remaining text from the server. */ + error = updater_read_checkout(up->rd, to); + if (error) { + xasprintf(&up->errmsg, "%s: Cannot write: %s", + fup->temppath, strerror(errno)); + goto bad; + } + line = stream_getln(up->rd, NULL); + } + stream_close(to); + close(orig); + + sr->sr_clientattr = fattr_frompath(fup->destpath, FATTR_NOFOLLOW); + if (sr->sr_clientattr == NULL) + return (UPDATER_ERR_PROTO); + fattr_override(sr->sr_clientattr, sr->sr_serverattr, + FA_MODTIME | FA_MASK); + + error = updater_updatefile(up, fup, md5, 0); + fup->wantmd5 = NULL; /* So that it doesn't get freed. */ +bad: + free(buf); + return (error); +} diff --git a/usr.bin/csup/updater.h b/usr.bin/csup/updater.h new file mode 100644 index 0000000..9ec9ed7 --- /dev/null +++ b/usr.bin/csup/updater.h @@ -0,0 +1,33 @@ +/*- + * Copyright (c) 2003-2004, Maxime Henrion <mux@FreeBSD.org> + * 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. + * + * $FreeBSD$ + */ +#ifndef _UPDATER_H_ +#define _UPDATER_H + +void *updater(void *); + +#endif /* !_UPDATER_H_ */ diff --git a/usr.bin/ctags/ctags.c b/usr.bin/ctags/ctags.c index eda4145..2d9e3f3 100644 --- a/usr.bin/ctags/ctags.c +++ b/usr.bin/ctags/ctags.c @@ -244,7 +244,7 @@ void init(void) { int i; - const char *sp; + const unsigned char *sp; for (i = 0; i < 256; i++) { _wht[i] = _etk[i] = _itk[i] = _btk[i] = NO; diff --git a/usr.bin/ctags/fortran.c b/usr.bin/ctags/fortran.c index 3761615..a494752 100644 --- a/usr.bin/ctags/fortran.c +++ b/usr.bin/ctags/fortran.c @@ -52,7 +52,7 @@ static void takeprec(void); char *lbp; /* line buffer pointer */ int -PF_funcs() +PF_funcs(void) { bool pfcnt; /* pascal/fortran functions found */ char *cp; diff --git a/usr.bin/dc/Makefile b/usr.bin/dc/Makefile new file mode 100644 index 0000000..ee91c95 --- /dev/null +++ b/usr.bin/dc/Makefile @@ -0,0 +1,10 @@ +# $FreeBSD$ +# $OpenBSD: Makefile,v 1.2 2006/11/26 11:31:09 deraadt Exp $ + +PROG= dc +SRCS= dc.c bcode.c inout.c mem.c stack.c +CFLAGS+=--param max-inline-insns-single=64 +DPADD= ${LIBCRYPTO} +LDADD= -lcrypto + +.include <bsd.prog.mk> diff --git a/usr.bin/dc/USD.doc/dc b/usr.bin/dc/USD.doc/dc new file mode 100644 index 0000000..4caa0f4 --- /dev/null +++ b/usr.bin/dc/USD.doc/dc @@ -0,0 +1,753 @@ +.\" $FreeBSD$ +.\" $OpenBSD: dc,v 1.2 2003/09/22 19:08:27 otto Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. +.\" 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 and documentation 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 or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of other +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, 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 CALDERA INTERNATIONAL, INC. 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. +.\" +.\" @(#)dc 8.1 (Berkeley) 6/8/93 +.\" +.EH 'USD:5-%''DC \- An Interactive Desk Calculator' +.OH 'DC \- An Interactive Desk Calculator''USD:5-%' +.\".RP +.\" ....TM 75-1271-8 39199 39199-11 +.ND +.TL +DC \- An Interactive Desk Calculator +.AU "MH 2C-524" 3878 +Robert Morris +.AU +Lorinda Cherry +.AI +.\" .MH +.AB +DC is an interactive desk calculator program implemented +on the +.UX +time-sharing system to do arbitrary-precision +integer arithmetic. +It has provision for manipulating scaled fixed-point numbers and +for input and output in bases other than decimal. +.PP +The size of numbers that can be manipulated is limited +only by available core storage. +On typical implementations of +.UX , +the size of numbers that +can be handled varies from several hundred digits on the smallest +systems to several thousand on the largest. +.AE +.PP +.SH +.PP +.ft I +Editor's note: the description of the implementation details of DC in this +paper is only valid for the original version of DC. +The current version of DC uses a different approach. +.ft +.PP +DC is an arbitrary precision arithmetic package implemented +on the +.UX +time-sharing system +in the form of an interactive desk calculator. +It works like a stacking calculator using reverse Polish notation. +Ordinarily DC operates on decimal integers, but one may +specify an input base, output base, and a number of fractional +digits to be maintained. +.PP +A language called BC [1] has been developed which accepts +programs written in the familiar style of higher-level +programming languages and compiles output which is +interpreted by DC. +Some of the commands described below were designed +for the compiler interface and are not easy for a human user +to manipulate. +.PP +Numbers that are typed into DC are put on a push-down +stack. +DC commands work by taking the top number or two +off the stack, performing the desired operation, and pushing the result +on the stack. +If an argument is given, +input is taken from that file until its end, +then from the standard input. +.SH +SYNOPTIC DESCRIPTION +.PP +Here we describe the DC commands that are intended +for use by people. The additional commands that are +intended to be invoked by compiled output are +described in the detailed description. +.PP +Any number of commands are permitted on a line. +Blanks and new-line characters are ignored except within numbers +and in places where a register name is expected. +.PP +The following constructions are recognized: +.SH +number +.IP +The value of the number is pushed onto the main stack. +A number is an unbroken string of the digits 0-9 +and the capital letters A\-F which are treated as digits +with values 10\-15 respectively. +The number may be preceded by an underscore _ to input a +negative number. +Numbers may contain decimal points. +.SH ++ \- * % ^ +.IP +The +top two values on the stack are added +(\fB+\fP), +subtracted +(\fB\-\fP), +multiplied (\fB*\fP), +divided (\fB/\fP), +remaindered (\fB%\fP), +or exponentiated (^). +The two entries are popped off the stack; +the result is pushed on the stack in their place. +The result of a division is an integer truncated toward zero. +See the detailed description below for the treatment of +numbers with decimal points. +An exponent must not have any digits after the decimal point. +.SH +s\fIx\fP +.IP +The +top of the main stack is popped and stored into +a register named \fIx\fP, where \fIx\fP may be any character. +If +the +.ft B +s +.ft +is capitalized, +.ft I +x +.ft +is treated as a stack and the value is pushed onto it. +Any character, even blank or new-line, is a valid register name. +.SH +l\fIx\fP +.IP +The +value in register +.ft I +x +.ft +is pushed onto the stack. +The register +.ft I +x +.ft +is not altered. +If the +.ft B +l +.ft +is capitalized, +register +.ft I +x +.ft +is treated as a stack and its top value is popped onto the main stack. +.LP +All registers start with empty value which is treated as a zero +by the command \fBl\fP and is treated as an error by the command \fBL\fP. +.SH +d +.IP +The +top value on the stack is duplicated. +.SH +p +.IP +The top value on the stack is printed. +The top value remains unchanged. +.SH +f +.IP +All values on the stack and in registers are printed. +.SH +x +.IP +treats the top element of the stack as a character string, +removes it from the stack, and +executes it as a string of DC commands. +.SH +[ ... ] +.IP +puts the bracketed character string onto the top of the stack. +.SH +q +.IP +exits the program. +If executing a string, the recursion level is +popped by two. +If +.ft B +q +.ft +is capitalized, +the top value on the stack is popped and the string execution level is popped +by that value. +.SH +<\fIx\fP >\fIx\fP =\fIx\fP !<\fIx\fP !>\fIx\fP !=\fIx\fP +.IP +The +top two elements of the stack are popped and compared. +Register +.ft I +x +.ft +is executed if they obey the stated +relation. +Exclamation point is negation. +.SH +v +.IP +replaces the top element on the stack by its square root. +The square root of an integer is truncated to an integer. +For the treatment of numbers with decimal points, see +the detailed description below. +.SH +! +.IP +interprets the rest of the line as a +.UX +command. +Control returns to DC when the +.UX +command terminates. +.SH +c +.IP +All values on the stack are popped; the stack becomes empty. +.SH +i +.IP +The top value on the stack is popped and used as the +number radix for further input. +If \fBi\fP is capitalized, the value of +the input base is pushed onto the stack. +No mechanism has been provided for the input of arbitrary +numbers in bases less than 1 or greater than 16. +.SH +o +.IP +The top value on the stack is popped and used as the +number radix for further output. +If \fBo\fP is capitalized, the value of the output +base is pushed onto the stack. +.SH +k +.IP +The top of the stack is popped, and that value is used as +a scale factor +that influences the number of decimal places +that are maintained during multiplication, division, and exponentiation. +The scale factor must be greater than or equal to zero and +less than 100. +If \fBk\fP is capitalized, the value of the scale factor +is pushed onto the stack. +.SH +z +.IP +The value of the stack level is pushed onto the stack. +.SH +? +.IP +A line of input is taken from the input source (usually the console) +and executed. +.SH +DETAILED DESCRIPTION +.SH +Internal Representation of Numbers +.PP +Numbers are stored internally using a dynamic storage allocator. +Numbers are kept in the form of a string +of digits to the base 100 stored one digit per byte +(centennial digits). +The string is stored with the low-order digit at the +beginning of the string. +For example, the representation of 157 +is 57,1. +After any arithmetic operation on a number, care is taken +that all digits are in the range 0\-99 and that +the number has no leading zeros. +The number zero is represented by the empty string. +.PP +Negative numbers are represented in the 100's complement +notation, which is analogous to two's complement notation for binary +numbers. +The high order digit of a negative number is always \-1 +and all other digits are in the range 0\-99. +The digit preceding the high order \-1 digit is never a 99. +The representation of \-157 is 43,98,\-1. +We shall call this the canonical form of a number. +The advantage of this kind of representation of negative +numbers is ease of addition. When addition is performed digit +by digit, the result is formally correct. The result need only +be modified, if necessary, to put it into canonical form. +.PP +Because the largest valid digit is 99 and the byte can +hold numbers twice that large, addition can be carried out +and the handling of carries done later when +that is convenient, as it sometimes is. +.PP +An additional byte is stored with each number beyond +the high order digit to indicate the number of +assumed decimal digits after the decimal point. The representation +of .001 is 1,\fI3\fP +where the scale has been italicized to emphasize the fact that it +is not the high order digit. +The value of this extra byte is called the +.ft B +scale factor +.ft +of the number. +.SH +The Allocator +.PP +DC uses a dynamic string storage allocator +for all of its internal storage. +All reading and writing of numbers internally is done through +the allocator. +Associated with each string in the allocator is a four-word header containing pointers +to the beginning of the string, the end of the string, +the next place to write, and the next place to read. +Communication between the allocator and DC +is done via pointers to these headers. +.PP +The allocator initially has one large string on a list +of free strings. All headers except the one pointing +to this string are on a list of free headers. +Requests for strings are made by size. +The size of the string actually supplied is the next higher +power of 2. +When a request for a string is made, the allocator +first checks the free list to see if there is +a string of the desired size. +If none is found, the allocator finds the next larger free string and splits it repeatedly until +it has a string of the right size. +Left-over strings are put on the free list. +If there are no larger strings, +the allocator tries to coalesce smaller free strings into +larger ones. +Since all strings are the result +of splitting large strings, +each string has a neighbor that is next to it in core +and, if free, can be combined with it to make a string twice as long. +This is an implementation of the `buddy system' of allocation +described in [2]. +.PP +Failing to find a string of the proper length after coalescing, +the allocator asks the system for more space. +The amount of space on the system is the only limitation +on the size and number of strings in DC. +If at any time in the process of trying to allocate a string, the allocator runs out of +headers, it also asks the system for more space. +.PP +There are routines in the allocator for reading, writing, copying, rewinding, +forward-spacing, and backspacing strings. +All string manipulation is done using these routines. +.PP +The reading and writing routines +increment the read pointer or write pointer so that +the characters of a string are read or written in +succession by a series of read or write calls. +The write pointer is interpreted as the end of the +information-containing portion of a string and a call +to read beyond that point returns an end-of-string indication. +An attempt to write beyond the end of a string +causes the allocator to +allocate a larger space and then copy +the old string into the larger block. +.SH +Internal Arithmetic +.PP +All arithmetic operations are done on integers. +The operands (or operand) needed for the operation are popped +from the main stack and their scale factors stripped off. +Zeros are added or digits removed as necessary to get +a properly scaled result from the internal arithmetic routine. +For example, if the scale of the operands is different and decimal +alignment is required, as it is for +addition, zeros are appended to the operand with the smaller +scale. +After performing the required arithmetic operation, +the proper scale factor is appended to the end of the number before +it is pushed on the stack. +.PP +A register called \fBscale\fP plays a part +in the results of most arithmetic operations. +\fBscale\fP is the bound on the number of decimal places retained in +arithmetic computations. +\fBscale\fP may be set to the number on the top of the stack +truncated to an integer with the \fBk\fP command. +\fBK\fP may be used to push the value of \fBscale\fP on the stack. +\fBscale\fP must be greater than or equal to 0 and less than 100. +The descriptions of the individual arithmetic operations will +include the exact effect of \fBscale\fP on the computations. +.SH +Addition and Subtraction +.PP +The scales of the two numbers are compared and trailing +zeros are supplied to the number with the lower scale to give both +numbers the same scale. The number with the smaller scale is +multiplied by 10 if the difference of the scales is odd. +The scale of the result is then set to the larger of the scales +of the two operands. +.PP +Subtraction is performed by negating the number +to be subtracted and proceeding as in addition. +.PP +Finally, the addition is performed digit by digit from the +low order end of the number. The carries are propagated +in the usual way. +The resulting number is brought into canonical form, which may +require stripping of leading zeros, or for negative numbers +replacing the high-order configuration 99,\-1 by the digit \-1. +In any case, digits which are not in the range 0\-99 must +be brought into that range, propagating any carries or borrows +that result. +.SH +Multiplication +.PP +The scales are removed from the two operands and saved. +The operands are both made positive. +Then multiplication is performed in +a digit by digit manner that exactly mimics the hand method +of multiplying. +The first number is multiplied by each digit of the second +number, beginning with its low order digit. The intermediate +products are accumulated into a partial sum which becomes the +final product. +The product is put into the canonical form and its sign is +computed from the signs of the original operands. +.PP +The scale of the result is set equal to the sum +of the scales of the two operands. +If that scale is larger than the internal register +.ft B +scale +.ft +and also larger than both of the scales of the two operands, +then the scale of the result is set equal to the largest +of these three last quantities. +.SH +Division +.PP +The scales are removed from the two operands. +Zeros are appended or digits removed from the dividend to make +the scale of the result of the integer division equal to +the internal quantity +\fBscale\fP. +The signs are removed and saved. +.PP +Division is performed much as it would be done by hand. +The difference of the lengths of the two numbers +is computed. +If the divisor is longer than the dividend, +zero is returned. +Otherwise the top digit of the divisor is divided into the top +two digits of the dividend. +The result is used as the first (high-order) digit of the +quotient. +It may turn out be one unit too low, but if it is, the next +trial quotient will be larger than 99 and this will be +adjusted at the end of the process. +The trial digit is multiplied by the divisor and the result subtracted +from the dividend and the process is repeated to get +additional quotient digits until the remaining +dividend is smaller than the divisor. +At the end, the digits of the quotient are put into +the canonical form, with propagation of carry as needed. +The sign is set from the sign of the operands. +.SH +Remainder +.PP +The division routine is called and division is performed +exactly as described. The quantity returned is the remains of the +dividend at the end of the divide process. +Since division truncates toward zero, remainders have the same +sign as the dividend. +The scale of the remainder is set to +the maximum of the scale of the dividend and +the scale of the quotient plus the scale of the divisor. +.SH +Square Root +.PP +The scale is stripped from the operand. +Zeros are added if necessary to make the +integer result have a scale that is the larger of +the internal quantity +\fBscale\fP +and the scale of the operand. +.PP +The method used to compute sqrt(y) is Newton's method +with successive approximations by the rule +.EQ +x sub {n+1} ~=~ half ( x sub n + y over x sub n ) +.EN +The initial guess is found by taking the integer square root +of the top two digits. +.SH +Exponentiation +.PP +Only exponents with zero scale factor are handled. If the exponent is +zero, then the result is 1. If the exponent is negative, then +it is made positive and the base is divided into one. The scale +of the base is removed. +.PP +The integer exponent is viewed as a binary number. +The base is repeatedly squared and the result is +obtained as a product of those powers of the base that +correspond to the positions of the one-bits in the binary +representation of the exponent. +Enough digits of the result +are removed to make the scale of the result the same as if the +indicated multiplication had been performed. +.SH +Input Conversion and Base +.PP +Numbers are converted to the internal representation as they are read +in. +The scale stored with a number is simply the number of fractional digits input. +Negative numbers are indicated by preceding the number with a \fB\_\fP (an +underscore). +The hexadecimal digits A\-F correspond to the numbers 10\-15 regardless of input base. +The \fBi\fP command can be used to change the base of the input numbers. +This command pops the stack, truncates the resulting number to an integer, +and uses it as the input base for all further input. +The input base is initialized to 10 but may, for example be changed to +8 or 16 to do octal or hexadecimal to decimal conversions. +The command \fBI\fP will push the value of the input base on the stack. +.SH +Output Commands +.PP +The command \fBp\fP causes the top of the stack to be printed. +It does not remove the top of the stack. +All of the stack and internal registers can be output +by typing the command \fBf\fP. +The \fBo\fP command can be used to change the output base. +This command uses the top of the stack, truncated to an integer as +the base for all further output. +The output base in initialized to 10. +It will work correctly for any base. +The command \fBO\fP pushes the value of the output base on the stack. +.SH +Output Format and Base +.PP +The input and output bases only affect +the interpretation of numbers on input and output; they have no +effect on arithmetic computations. +Large numbers are output with 70 characters per line; +a \\ indicates a continued line. +All choices of input and output bases work correctly, although not all are +useful. +A particularly useful output base is 100000, which has the effect of +grouping digits in fives. +Bases of 8 and 16 can be used for decimal-octal or decimal-hexadecimal +conversions. +.SH +Internal Registers +.PP +Numbers or strings may be stored in internal registers or loaded on the stack +from registers with the commands \fBs\fP and \fBl\fP. +The command \fBs\fIx\fR pops the top of the stack and +stores the result in register \fBx\fP. +\fIx\fP can be any character. +\fBl\fIx\fR puts the contents of register \fBx\fP on the top of the stack. +The \fBl\fP command has no effect on the contents of register \fIx\fP. +The \fBs\fP command, however, is destructive. +.SH +Stack Commands +.PP +The command \fBc\fP clears the stack. +The command \fBd\fP pushes a duplicate of the number on the top of the stack +on the stack. +The command \fBz\fP pushes the stack size on the stack. +The command \fBX\fP replaces the number on the top of the stack +with its scale factor. +The command \fBZ\fP replaces the top of the stack +with its length. +.SH +Subroutine Definitions and Calls +.PP +Enclosing a string in \fB[ ]\fP pushes the ascii string on the stack. +The \fBq\fP command quits or in executing a string, pops the recursion levels by two. +.SH +Internal Registers \- Programming DC +.PP +The load and store +commands together with \fB[ ]\fP to store strings, \fBx\fP to execute +and the testing commands `<', `>', `=', `!<', `!>', `!=' can be used to program +DC. +The \fBx\fP command assumes the top of the stack is an string of DC commands +and executes it. +The testing commands compare the top two elements on the stack and if the relation holds, execute the register +that follows the relation. +For example, to print the numbers 0-9, +.DS +[lip1+ si li10>a]sa +0si lax +.DE +.SH +Push-Down Registers and Arrays +.PP +These commands were designed for used by a compiler, not by +people. +They involve push-down registers and arrays. +In addition to the stack that commands work on, DC can be thought +of as having individual stacks for each register. +These registers are operated on by the commands \fBS\fP and \fBL\fP. +\fBS\fIx\fR pushes the top value of the main stack onto the stack for +the register \fIx\fP. +\fBL\fIx\fR pops the stack for register \fIx\fP and puts the result on the main +stack. +The commands \fBs\fP and \fBl\fP also work on registers but not as push-down +stacks. +\fBl\fP doesn't effect the top of the +register stack, and \fBs\fP destroys what was there before. +.PP +The commands to work on arrays are \fB:\fP and \fB;\fP. +\fB:\fIx\fR pops the stack and uses this value as an index into +the array \fIx\fP. +The next element on the stack is stored at this index in \fIx\fP. +An index must be greater than or equal to 0 and +less than 2048. +\fB;\fIx\fR is the command to load the main stack from the array \fIx\fP. +The value on the top of the stack is the index +into the array \fIx\fP of the value to be loaded. +.SH +Miscellaneous Commands +.PP +The command \fB!\fP interprets the rest of the line as a +.UX +command and passes it to +.UX +to execute. +One other compiler command is \fBQ\fP. +This command uses the top of the stack as the number of levels of recursion to skip. +.SH +DESIGN CHOICES +.PP +The real reason for the use of a dynamic storage allocator was +that a general purpose program could be (and in fact has been) +used for a variety of other tasks. +The allocator has some value for input and for compiling (i.e. +the bracket [...] commands) where it cannot be known in advance +how long a string will be. +The result was that at a modest +cost in execution time, all considerations of string allocation +and sizes of strings were removed from the remainder of the program +and debugging was made easier. The allocation method +used wastes approximately 25% of available space. +.PP +The choice of 100 as a base for internal arithmetic +seemingly has no compelling advantage. Yet the base cannot +exceed 127 because of hardware limitations and at the cost +of 5% in space, debugging was made a great deal easier and +decimal output was made much faster. +.PP +The reason for a stack-type arithmetic design was +to permit all DC commands from addition to subroutine execution +to be implemented in essentially the same way. The result +was a considerable degree of logical separation of the final +program into modules with very little communication between +modules. +.PP +The rationale for the lack of interaction between the scale and the bases +was to provide an understandable means of proceeding after +a change of base or scale when numbers had already been entered. +An earlier implementation which had global notions of +scale and base did not work out well. +If the value of +.ft B +scale +.ft +were to be interpreted in the current +input or output base, +then a change of base or scale in the midst of a +computation would cause great confusion in the interpretation +of the results. +The current scheme has the advantage that the value of +the input and output bases +are only used for input and output, respectively, and they +are ignored in all other operations. +The value of +scale +is not used for any essential purpose by any part of the program +and it is used only to prevent the number of +decimal places resulting from the arithmetic operations from +growing beyond all bounds. +.PP +The design rationale for the choices for the scales of +the results of arithmetic were that in no case should +any significant digits be thrown away if, on appearances, the +user actually wanted them. Thus, if the user wants +to add the numbers 1.5 and 3.517, it seemed reasonable to give +him the result 5.017 without requiring him to unnecessarily +specify his rather obvious requirements for precision. +.PP +On the other hand, multiplication and exponentiation produce +results with many more digits than their operands and it +seemed reasonable to give as a minimum the number of decimal +places in the operands but not to give more than that +number of digits +unless the user asked for them by specifying a value for \fBscale\fP. +Square root can be handled in just the same way as multiplication. +The operation of division gives arbitrarily many decimal places +and there is simply no way to guess how many places the user +wants. +In this case only, the user must +specify a \fBscale\fP to get any decimal places at all. +.PP +The scale of remainder was chosen to make it possible +to recreate the dividend from the quotient and remainder. +This is easy to implement; no digits are thrown away. +.SH +References +.IP [1] +L. L. Cherry, R. Morris, +.ft I +BC \- An Arbitrary Precision Desk-Calculator Language. +.ft +.IP [2] +K. C. Knowlton, +.ft I +A Fast Storage Allocator, +.ft +Comm. ACM \fB8\fP, pp. 623-625 (Oct. 1965). diff --git a/usr.bin/dc/bcode.c b/usr.bin/dc/bcode.c new file mode 100644 index 0000000..e80c635 --- /dev/null +++ b/usr.bin/dc/bcode.c @@ -0,0 +1,1775 @@ +/* $OpenBSD: bcode.c,v 1.40 2009/10/27 23:59:37 deraadt Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <limits.h> +#include <openssl/ssl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "extern.h" + +BIGNUM zero; + +#define __inline + +#define MAX_ARRAY_INDEX 2048 +#define READSTACK_SIZE 8 + +#define NO_ELSE -2 /* -1 is EOF */ +#define REG_ARRAY_SIZE_SMALL (UCHAR_MAX + 1) +#define REG_ARRAY_SIZE_BIG (UCHAR_MAX + 1 + USHRT_MAX + 1) + +struct bmachine { + struct source *readstack; + struct stack *reg; + struct stack stack; + volatile sig_atomic_t interrupted; + u_int scale; + u_int obase; + u_int ibase; + size_t readsp; + size_t reg_array_size; + size_t readstack_sz; + bool extended_regs; +}; + +static struct bmachine bmachine; +static void sighandler(int); + +static __inline int readch(void); +static __inline void unreadch(void); +static __inline char *readline(void); +static __inline void src_free(void); + +static __inline u_int max(u_int, u_int); +static u_long get_ulong(struct number *); + +static __inline void push_number(struct number *); +static __inline void push_string(char *); +static __inline void push(struct value *); +static __inline struct value *tos(void); +static __inline struct number *pop_number(void); +static __inline char *pop_string(void); +static __inline void clear_stack(void); +static __inline void print_tos(void); +static void pop_print(void); +static void pop_printn(void); +static __inline void print_stack(void); +static __inline void dup(void); +static void swap(void); +static void drop(void); + +static void get_scale(void); +static void set_scale(void); +static void get_obase(void); +static void set_obase(void); +static void get_ibase(void); +static void set_ibase(void); +static void stackdepth(void); +static void push_scale(void); +static u_int count_digits(const struct number *); +static void num_digits(void); +static void to_ascii(void); +static void push_line(void); +static void comment(void); +static void bexec(char *); +static void badd(void); +static void bsub(void); +static void bmul(void); +static void bdiv(void); +static void bmod(void); +static void bdivmod(void); +static void bexp(void); +static bool bsqrt_stop(const BIGNUM *, const BIGNUM *, u_int *); +static void bsqrt(void); +static void not(void); +static void equal_numbers(void); +static void less_numbers(void); +static void lesseq_numbers(void); +static void equal(void); +static void not_equal(void); +static void less(void); +static void not_less(void); +static void greater(void); +static void not_greater(void); +static void not_compare(void); +static bool compare_numbers(enum bcode_compare, struct number *, + struct number *); +static void compare(enum bcode_compare); +static int readreg(void); +static void load(void); +static void store(void); +static void load_stack(void); +static void store_stack(void); +static void load_array(void); +static void store_array(void); +static void nop(void); +static void quit(void); +static void quitN(void); +static void skipN(void); +static void skip_until_mark(void); +static void parse_number(void); +static void unknown(void); +static void eval_string(char *); +static void eval_line(void); +static void eval_tos(void); + + +typedef void (*opcode_function)(void); + +struct jump_entry { + u_char ch; + opcode_function f; +}; + +static opcode_function jump_table[UCHAR_MAX]; + +static const struct jump_entry jump_table_data[] = { + { ' ', nop }, + { '!', not_compare }, + { '#', comment }, + { '%', bmod }, + { '(', less_numbers }, + { '*', bmul }, + { '+', badd }, + { '-', bsub }, + { '.', parse_number }, + { '/', bdiv }, + { '0', parse_number }, + { '1', parse_number }, + { '2', parse_number }, + { '3', parse_number }, + { '4', parse_number }, + { '5', parse_number }, + { '6', parse_number }, + { '7', parse_number }, + { '8', parse_number }, + { '9', parse_number }, + { ':', store_array }, + { ';', load_array }, + { '<', less }, + { '=', equal }, + { '>', greater }, + { '?', eval_line }, + { 'A', parse_number }, + { 'B', parse_number }, + { 'C', parse_number }, + { 'D', parse_number }, + { 'E', parse_number }, + { 'F', parse_number }, + { 'G', equal_numbers }, + { 'I', get_ibase }, + { 'J', skipN }, + { 'K', get_scale }, + { 'L', load_stack }, + { 'M', nop }, + { 'N', not }, + { 'O', get_obase }, + { 'P', pop_print }, + { 'Q', quitN }, + { 'R', drop }, + { 'S', store_stack }, + { 'X', push_scale }, + { 'Z', num_digits }, + { '[', push_line }, + { '\f', nop }, + { '\n', nop }, + { '\r', nop }, + { '\t', nop }, + { '^', bexp }, + { '_', parse_number }, + { 'a', to_ascii }, + { 'c', clear_stack }, + { 'd', dup }, + { 'f', print_stack }, + { 'i', set_ibase }, + { 'k', set_scale }, + { 'l', load }, + { 'n', pop_printn }, + { 'o', set_obase }, + { 'p', print_tos }, + { 'q', quit }, + { 'r', swap }, + { 's', store }, + { 'v', bsqrt }, + { 'x', eval_tos }, + { 'z', stackdepth }, + { '{', lesseq_numbers }, + { '~', bdivmod } +}; + +#define JUMP_TABLE_DATA_SIZE \ + (sizeof(jump_table_data)/sizeof(jump_table_data[0])) + +static void +sighandler(int ignored) +{ + + switch (ignored) + { + default: + bmachine.interrupted = true; + } +} + +void +init_bmachine(bool extended_registers) +{ + unsigned int i; + + bmachine.extended_regs = extended_registers; + bmachine.reg_array_size = bmachine.extended_regs ? + REG_ARRAY_SIZE_BIG : REG_ARRAY_SIZE_SMALL; + + bmachine.reg = calloc(bmachine.reg_array_size, + sizeof(bmachine.reg[0])); + if (bmachine.reg == NULL) + err(1, NULL); + + for (i = 0; i < UCHAR_MAX; i++) + jump_table[i] = unknown; + for (i = 0; i < JUMP_TABLE_DATA_SIZE; i++) + jump_table[jump_table_data[i].ch] = jump_table_data[i].f; + + stack_init(&bmachine.stack); + + for (i = 0; i < bmachine.reg_array_size; i++) + stack_init(&bmachine.reg[i]); + + bmachine.readstack_sz = READSTACK_SIZE; + bmachine.readstack = calloc(sizeof(struct source), + bmachine.readstack_sz); + if (bmachine.readstack == NULL) + err(1, NULL); + bmachine.obase = bmachine.ibase = 10; + BN_init(&zero); + bn_check(BN_zero(&zero)); + signal(SIGINT, sighandler); +} + +/* Reset the things needed before processing a (new) file */ +void +reset_bmachine(struct source *src) +{ + + bmachine.readsp = 0; + bmachine.readstack[0] = *src; +} + +static __inline int +readch(void) +{ + struct source *src = &bmachine.readstack[bmachine.readsp]; + + return (src->vtable->readchar(src)); +} + +static __inline void +unreadch(void) +{ + struct source *src = &bmachine.readstack[bmachine.readsp]; + + src->vtable->unreadchar(src); +} + +static __inline char * +readline(void) +{ + struct source *src = &bmachine.readstack[bmachine.readsp]; + + return (src->vtable->readline(src)); +} + +static __inline void +src_free(void) +{ + struct source *src = &bmachine.readstack[bmachine.readsp]; + + src->vtable->free(src); +} + +#ifdef DEBUGGING +void +pn(const char *str, const struct number *n) +{ + char *p = BN_bn2dec(n->number); + + if (p == NULL) + err(1, "BN_bn2dec failed"); + fputs(str, stderr); + fprintf(stderr, " %s (%u)\n" , p, n->scale); + OPENSSL_free(p); +} + +void +pbn(const char *str, const BIGNUM *n) +{ + char *p = BN_bn2dec(n); + + if (p == NULL) + err(1, "BN_bn2dec failed"); + fputs(str, stderr); + fprintf(stderr, " %s\n", p); + OPENSSL_free(p); +} + +#endif + +static __inline u_int +max(u_int a, u_int b) +{ + + return (a > b ? a : b); +} + +static unsigned long factors[] = { + 0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, + 100000000, 1000000000 +}; + +void +scale_number(BIGNUM *n, int s) +{ + unsigned int abs_scale; + + if (s == 0) + return; + + abs_scale = s > 0 ? s : -s; + + if (abs_scale < sizeof(factors)/sizeof(factors[0])) { + if (s > 0) + bn_check(BN_mul_word(n, factors[abs_scale])); + else + BN_div_word(n, factors[abs_scale]); + } else { + BIGNUM *a, *p; + BN_CTX *ctx; + + a = BN_new(); + bn_checkp(a); + p = BN_new(); + bn_checkp(p); + ctx = BN_CTX_new(); + bn_checkp(ctx); + + bn_check(BN_set_word(a, 10)); + bn_check(BN_set_word(p, abs_scale)); + bn_check(BN_exp(a, a, p, ctx)); + if (s > 0) + bn_check(BN_mul(n, n, a, ctx)); + else + bn_check(BN_div(n, NULL, n, a, ctx)); + BN_CTX_free(ctx); + BN_free(a); + BN_free(p); + } +} + +void +split_number(const struct number *n, BIGNUM *i, BIGNUM *f) +{ + u_long rem; + + bn_checkp(BN_copy(i, n->number)); + + if (n->scale == 0 && f != NULL) + bn_check(BN_zero(f)); + else if (n->scale < sizeof(factors)/sizeof(factors[0])) { + rem = BN_div_word(i, factors[n->scale]); + if (f != NULL) + bn_check(BN_set_word(f, rem)); + } else { + BIGNUM *a, *p; + BN_CTX *ctx; + + a = BN_new(); + bn_checkp(a); + p = BN_new(); + bn_checkp(p); + ctx = BN_CTX_new(); + bn_checkp(ctx); + + bn_check(BN_set_word(a, 10)); + bn_check(BN_set_word(p, n->scale)); + bn_check(BN_exp(a, a, p, ctx)); + bn_check(BN_div(i, f, n->number, a, ctx)); + BN_CTX_free(ctx); + BN_free(a); + BN_free(p); + } +} + +__inline void +normalize(struct number *n, u_int s) +{ + + scale_number(n->number, s - n->scale); + n->scale = s; +} + +static u_long +get_ulong(struct number *n) +{ + + normalize(n, 0); + return (BN_get_word(n->number)); +} + +void +negate(struct number *n) +{ + + bn_check(BN_sub(n->number, &zero, n->number)); +} + +static __inline void +push_number(struct number *n) +{ + + stack_pushnumber(&bmachine.stack, n); +} + +static __inline void +push_string(char *string) +{ + + stack_pushstring(&bmachine.stack, string); +} + +static __inline void +push(struct value *v) +{ + + stack_push(&bmachine.stack, v); +} + +static __inline struct value * +tos(void) +{ + + return (stack_tos(&bmachine.stack)); +} + +static __inline struct value * +pop(void) +{ + + return (stack_pop(&bmachine.stack)); +} + +static __inline struct number * +pop_number(void) +{ + + return (stack_popnumber(&bmachine.stack)); +} + +static __inline char * +pop_string(void) +{ + + return (stack_popstring(&bmachine.stack)); +} + +static __inline void +clear_stack(void) +{ + + stack_clear(&bmachine.stack); +} + +static __inline void +print_stack(void) +{ + + stack_print(stdout, &bmachine.stack, "", bmachine.obase); +} + +static __inline void +print_tos(void) +{ + struct value *value = tos(); + + if (value != NULL) { + print_value(stdout, value, "", bmachine.obase); + putchar('\n'); + } + else + warnx("stack empty"); +} + +static void +pop_print(void) +{ + struct value *value = pop(); + + if (value != NULL) { + switch (value->type) { + case BCODE_NONE: + break; + case BCODE_NUMBER: + normalize(value->u.num, 0); + print_ascii(stdout, value->u.num); + fflush(stdout); + break; + case BCODE_STRING: + fputs(value->u.string, stdout); + fflush(stdout); + break; + } + stack_free_value(value); + } +} + +static void +pop_printn(void) +{ + struct value *value = pop(); + + if (value != NULL) { + print_value(stdout, value, "", bmachine.obase); + fflush(stdout); + stack_free_value(value); + } +} + +static __inline void +dup(void) +{ + + stack_dup(&bmachine.stack); +} + +static void +swap(void) +{ + + stack_swap(&bmachine.stack); +} + +static void +drop(void) +{ + struct value *v = pop(); + if (v != NULL) + stack_free_value(v); +} + +static void +get_scale(void) +{ + struct number *n; + + n = new_number(); + bn_check(BN_set_word(n->number, bmachine.scale)); + push_number(n); +} + +static void +set_scale(void) +{ + struct number *n; + u_long scale; + + n = pop_number(); + if (n != NULL) { + if (BN_cmp(n->number, &zero) < 0) + warnx("scale must be a nonnegative number"); + else { + scale = get_ulong(n); + if (scale != BN_MASK2 && scale <= UINT_MAX) + bmachine.scale = (u_int)scale; + else + warnx("scale too large"); + } + free_number(n); + } +} + +static void +get_obase(void) +{ + struct number *n; + + n = new_number(); + bn_check(BN_set_word(n->number, bmachine.obase)); + push_number(n); +} + +static void +set_obase(void) +{ + struct number *n; + u_long base; + + n = pop_number(); + if (n != NULL) { + base = get_ulong(n); + if (base != BN_MASK2 && base > 1 && base <= UINT_MAX) + bmachine.obase = (u_int)base; + else + warnx("output base must be a number greater than 1"); + free_number(n); + } +} + +static void +get_ibase(void) +{ + struct number *n; + + n = new_number(); + bn_check(BN_set_word(n->number, bmachine.ibase)); + push_number(n); +} + +static void +set_ibase(void) +{ + struct number *n; + u_long base; + + n = pop_number(); + if (n != NULL) { + base = get_ulong(n); + if (base != BN_MASK2 && 2 <= base && base <= 16) + bmachine.ibase = (u_int)base; + else + warnx("input base must be a number between 2 and 16 " + "(inclusive)"); + free_number(n); + } +} + +static void +stackdepth(void) +{ + struct number *n; + size_t i; + + i = stack_size(&bmachine.stack); + n = new_number(); + bn_check(BN_set_word(n->number, i)); + push_number(n); +} + +static void +push_scale(void) +{ + struct number *n; + struct value *value; + u_int scale = 0; + + value = pop(); + if (value != NULL) { + switch (value->type) { + case BCODE_NONE: + return; + case BCODE_NUMBER: + scale = value->u.num->scale; + break; + case BCODE_STRING: + break; + } + stack_free_value(value); + n = new_number(); + bn_check(BN_set_word(n->number, scale)); + push_number(n); + } +} + +static u_int +count_digits(const struct number *n) +{ + struct number *int_part, *fract_part; + u_int i; + + if (BN_is_zero(n->number)) + return (1); + + int_part = new_number(); + fract_part = new_number(); + fract_part->scale = n->scale; + split_number(n, int_part->number, fract_part->number); + + i = 0; + while (!BN_is_zero(int_part->number)) { + BN_div_word(int_part->number, 10); + i++; + } + free_number(int_part); + free_number(fract_part); + return (i + n->scale); +} + +static void +num_digits(void) +{ + struct number *n = NULL; + struct value *value; + size_t digits; + + value = pop(); + if (value != NULL) { + switch (value->type) { + case BCODE_NONE: + return; + case BCODE_NUMBER: + digits = count_digits(value->u.num); + n = new_number(); + bn_check(BN_set_word(n->number, digits)); + break; + case BCODE_STRING: + digits = strlen(value->u.string); + n = new_number(); + bn_check(BN_set_word(n->number, digits)); + break; + } + stack_free_value(value); + push_number(n); + } +} + +static void +to_ascii(void) +{ + struct number *n; + struct value *value; + char str[2]; + + value = pop(); + if (value != NULL) { + str[1] = '\0'; + switch (value->type) { + case BCODE_NONE: + return; + case BCODE_NUMBER: + n = value->u.num; + normalize(n, 0); + if (BN_num_bits(n->number) > 8) + bn_check(BN_mask_bits(n->number, 8)); + str[0] = (char)BN_get_word(n->number); + break; + case BCODE_STRING: + str[0] = value->u.string[0]; + break; + } + stack_free_value(value); + push_string(bstrdup(str)); + } +} + +static int +readreg(void) +{ + int ch1, ch2, idx; + + idx = readch(); + if (idx == 0xff && bmachine.extended_regs) { + ch1 = readch(); + ch2 = readch(); + if (ch1 == EOF || ch2 == EOF) { + warnx("unexpected eof"); + idx = -1; + } else + idx = (ch1 << 8) + ch2 + UCHAR_MAX + 1; + } + if (idx < 0 || (unsigned)idx >= bmachine.reg_array_size) { + warnx("internal error: reg num = %d", idx); + idx = -1; + } + return (idx); +} + +static void +load(void) +{ + struct number *n; + struct value *v; + struct value copy; + int idx; + + idx = readreg(); + if (idx >= 0) { + v = stack_tos(&bmachine.reg[idx]); + if (v == NULL) { + n = new_number(); + bn_check(BN_zero(n->number)); + push_number(n); + } else + push(stack_dup_value(v, ©)); + } +} + +static void +store(void) +{ + struct value *val; + int idx; + + idx = readreg(); + if (idx >= 0) { + val = pop(); + if (val == NULL) { + return; + } + stack_set_tos(&bmachine.reg[idx], val); + } +} + +static void +load_stack(void) +{ + struct stack *stack; + struct value *value; + int idx; + + idx = readreg(); + if (idx >= 0) { + stack = &bmachine.reg[idx]; + value = NULL; + if (stack_size(stack) > 0) { + value = stack_pop(stack); + } + if (value != NULL) + push(value); + else + warnx("stack register '%c' (0%o) is empty", + idx, idx); + } +} + +static void +store_stack(void) +{ + struct value *value; + int idx; + + idx = readreg(); + if (idx >= 0) { + value = pop(); + if (value == NULL) + return; + stack_push(&bmachine.reg[idx], value); + } +} + +static void +load_array(void) +{ + struct number *inumber, *n; + struct stack *stack; + struct value *v; + struct value copy; + u_long idx; + int reg; + + reg = readreg(); + if (reg >= 0) { + inumber = pop_number(); + if (inumber == NULL) + return; + idx = get_ulong(inumber); + if (BN_cmp(inumber->number, &zero) < 0) + warnx("negative idx"); + else if (idx == BN_MASK2 || idx > MAX_ARRAY_INDEX) + warnx("idx too big"); + else { + stack = &bmachine.reg[reg]; + v = frame_retrieve(stack, idx); + if (v == NULL || v->type == BCODE_NONE) { + n = new_number(); + bn_check(BN_zero(n->number)); + push_number(n); + } + else + push(stack_dup_value(v, ©)); + } + free_number(inumber); + } +} + +static void +store_array(void) +{ + struct number *inumber; + struct value *value; + struct stack *stack; + u_long idx; + int reg; + + reg = readreg(); + if (reg >= 0) { + inumber = pop_number(); + if (inumber == NULL) + return; + value = pop(); + if (value == NULL) { + free_number(inumber); + return; + } + idx = get_ulong(inumber); + if (BN_cmp(inumber->number, &zero) < 0) { + warnx("negative idx"); + stack_free_value(value); + } else if (idx == BN_MASK2 || idx > MAX_ARRAY_INDEX) { + warnx("idx too big"); + stack_free_value(value); + } else { + stack = &bmachine.reg[reg]; + frame_assign(stack, idx, value); + } + free_number(inumber); + } +} + +static void +push_line(void) +{ + + push_string(read_string(&bmachine.readstack[bmachine.readsp])); +} + +static void +comment(void) +{ + + free(readline()); +} + +static void +bexec(char *line) +{ + + system(line); + free(line); +} + +static void +badd(void) +{ + struct number *a, *b, *r; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + r = new_number(); + r->scale = max(a->scale, b->scale); + if (r->scale > a->scale) + normalize(a, r->scale); + else if (r->scale > b->scale) + normalize(b, r->scale); + bn_check(BN_add(r->number, a->number, b->number)); + push_number(r); + free_number(a); + free_number(b); +} + +static void +bsub(void) +{ + struct number *a, *b, *r; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + r = new_number(); + + r->scale = max(a->scale, b->scale); + if (r->scale > a->scale) + normalize(a, r->scale); + else if (r->scale > b->scale) + normalize(b, r->scale); + bn_check(BN_sub(r->number, b->number, a->number)); + push_number(r); + free_number(a); + free_number(b); +} + +void +bmul_number(struct number *r, struct number *a, struct number *b) +{ + BN_CTX *ctx; + + /* Create copies of the scales, since r might be equal to a or b */ + u_int ascale = a->scale; + u_int bscale = b->scale; + u_int rscale = ascale + bscale; + + ctx = BN_CTX_new(); + bn_checkp(ctx); + bn_check(BN_mul(r->number, a->number, b->number, ctx)); + BN_CTX_free(ctx); + + if (rscale > bmachine.scale && rscale > ascale && rscale > bscale) { + r->scale = rscale; + normalize(r, max(bmachine.scale, max(ascale, bscale))); + } else + r->scale = rscale; +} + +static void +bmul(void) +{ + struct number *a, *b, *r; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + r = new_number(); + bmul_number(r, a, b); + + push_number(r); + free_number(a); + free_number(b); +} + +static void +bdiv(void) +{ + struct number *a, *b, *r; + BN_CTX *ctx; + u_int scale; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + r = new_number(); + r->scale = bmachine.scale; + scale = max(a->scale, b->scale); + + if (BN_is_zero(a->number)) + warnx("divide by zero"); + else { + normalize(a, scale); + normalize(b, scale + r->scale); + + ctx = BN_CTX_new(); + bn_checkp(ctx); + bn_check(BN_div(r->number, NULL, b->number, a->number, ctx)); + BN_CTX_free(ctx); + } + push_number(r); + free_number(a); + free_number(b); +} + +static void +bmod(void) +{ + struct number *a, *b, *r; + BN_CTX *ctx; + u_int scale; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + r = new_number(); + scale = max(a->scale, b->scale); + r->scale = max(b->scale, a->scale + bmachine.scale); + + if (BN_is_zero(a->number)) + warnx("remainder by zero"); + else { + normalize(a, scale); + normalize(b, scale + bmachine.scale); + + ctx = BN_CTX_new(); + bn_checkp(ctx); + bn_check(BN_mod(r->number, b->number, a->number, ctx)); + BN_CTX_free(ctx); + } + push_number(r); + free_number(a); + free_number(b); +} + +static void +bdivmod(void) +{ + struct number *a, *b, *rdiv, *rmod; + BN_CTX *ctx; + u_int scale; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + rdiv = new_number(); + rmod = new_number(); + rdiv->scale = bmachine.scale; + rmod->scale = max(b->scale, a->scale + bmachine.scale); + scale = max(a->scale, b->scale); + + if (BN_is_zero(a->number)) + warnx("divide by zero"); + else { + normalize(a, scale); + normalize(b, scale + bmachine.scale); + + ctx = BN_CTX_new(); + bn_checkp(ctx); + bn_check(BN_div(rdiv->number, rmod->number, + b->number, a->number, ctx)); + BN_CTX_free(ctx); + } + push_number(rdiv); + push_number(rmod); + free_number(a); + free_number(b); +} + +static void +bexp(void) +{ + struct number *a, *p, *r; + u_int scale; + bool neg; + + p = pop_number(); + if (p == NULL) { + return; + } + a = pop_number(); + if (a == NULL) { + push_number(p); + return; + } + + if (p->scale != 0) + warnx("Runtime warning: non-zero scale in exponent"); + normalize(p, 0); + + neg = false; + if (BN_cmp(p->number, &zero) < 0) { + neg = true; + negate(p); + scale = bmachine.scale; + } else { + /* Posix bc says min(a.scale * b, max(a.scale, scale) */ + u_long b; + u_int m; + + b = BN_get_word(p->number); + m = max(a->scale, bmachine.scale); + scale = a->scale * (u_int)b; + if (scale > m || (a->scale > 0 && (b == BN_MASK2 || + b > UINT_MAX))) + scale = m; + } + + if (BN_is_zero(p->number)) { + r = new_number(); + bn_check(BN_one(r->number)); + normalize(r, scale); + } else { + while (!BN_is_bit_set(p->number, 0)) { + bmul_number(a, a, a); + bn_check(BN_rshift1(p->number, p->number)); + } + + r = dup_number(a); + normalize(r, scale); + bn_check(BN_rshift1(p->number, p->number)); + + while (!BN_is_zero(p->number)) { + bmul_number(a, a, a); + if (BN_is_bit_set(p->number, 0)) + bmul_number(r, r, a); + bn_check(BN_rshift1(p->number, p->number)); + } + + if (neg) { + BN_CTX *ctx; + BIGNUM *one; + + one = BN_new(); + bn_checkp(one); + bn_check(BN_one(one)); + ctx = BN_CTX_new(); + bn_checkp(ctx); + scale_number(one, r->scale + scale); + normalize(r, scale); + bn_check(BN_div(r->number, NULL, one, r->number, ctx)); + BN_free(one); + BN_CTX_free(ctx); + } else + normalize(r, scale); + } + push_number(r); + free_number(a); + free_number(p); +} + +static bool +bsqrt_stop(const BIGNUM *x, const BIGNUM *y, u_int *onecount) +{ + BIGNUM *r; + bool ret; + + r = BN_new(); + bn_checkp(r); + bn_check(BN_sub(r, x, y)); + if (BN_is_one(r)) + (*onecount)++; + ret = BN_is_zero(r); + BN_free(r); + return (ret || *onecount > 1); +} + +static void +bsqrt(void) +{ + struct number *n, *r; + BIGNUM *x, *y; + BN_CTX *ctx; + u_int onecount, scale; + + onecount = 0; + n = pop_number(); + if (n == NULL) { + return; + } + if (BN_is_zero(n->number)) { + r = new_number(); + push_number(r); + } else if (BN_cmp(n->number, &zero) < 0) + warnx("square root of negative number"); + else { + scale = max(bmachine.scale, n->scale); + normalize(n, 2*scale); + x = BN_dup(n->number); + bn_checkp(x); + bn_check(BN_rshift(x, x, BN_num_bits(x)/2)); + y = BN_new(); + bn_checkp(y); + ctx = BN_CTX_new(); + bn_checkp(ctx); + for (;;) { + bn_checkp(BN_copy(y, x)); + bn_check(BN_div(x, NULL, n->number, x, ctx)); + bn_check(BN_add(x, x, y)); + bn_check(BN_rshift1(x, x)); + if (bsqrt_stop(x, y, &onecount)) + break; + } + r = bmalloc(sizeof(*r)); + r->scale = scale; + r->number = y; + BN_free(x); + BN_CTX_free(ctx); + push_number(r); + } + + free_number(n); +} + +static void +not(void) +{ + struct number *a; + + a = pop_number(); + if (a == NULL) { + return; + } + a->scale = 0; + bn_check(BN_set_word(a->number, BN_get_word(a->number) ? 0 : 1)); + push_number(a); +} + +static void +equal(void) +{ + + compare(BCODE_EQUAL); +} + +static void +equal_numbers(void) +{ + struct number *a, *b, *r; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + r = new_number(); + bn_check(BN_set_word(r->number, + compare_numbers(BCODE_EQUAL, a, b) ? 1 : 0)); + push_number(r); +} + +static void +less_numbers(void) +{ + struct number *a, *b, *r; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + r = new_number(); + bn_check(BN_set_word(r->number, + compare_numbers(BCODE_LESS, a, b) ? 1 : 0)); + push_number(r); +} + +static void +lesseq_numbers(void) +{ + struct number *a, *b, *r; + + a = pop_number(); + if (a == NULL) { + return; + } + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + r = new_number(); + bn_check(BN_set_word(r->number, + compare_numbers(BCODE_NOT_GREATER, a, b) ? 1 : 0)); + push_number(r); +} + +static void +not_equal(void) +{ + + compare(BCODE_NOT_EQUAL); +} + +static void +less(void) +{ + + compare(BCODE_LESS); +} + +static void +not_compare(void) +{ + + switch (readch()) { + case '<': + not_less(); + break; + case '>': + not_greater(); + break; + case '=': + not_equal(); + break; + default: + unreadch(); + bexec(readline()); + break; + } +} + +static void +not_less(void) +{ + + compare(BCODE_NOT_LESS); +} + +static void +greater(void) +{ + + compare(BCODE_GREATER); +} + +static void +not_greater(void) +{ + + compare(BCODE_NOT_GREATER); +} + +static bool +compare_numbers(enum bcode_compare type, struct number *a, struct number *b) +{ + u_int scale; + int cmp; + + scale = max(a->scale, b->scale); + + if (scale > a->scale) + normalize(a, scale); + else if (scale > b->scale) + normalize(b, scale); + + cmp = BN_cmp(a->number, b->number); + + free_number(a); + free_number(b); + + switch (type) { + case BCODE_EQUAL: + return (cmp == 0); + case BCODE_NOT_EQUAL: + return (cmp != 0); + case BCODE_LESS: + return (cmp < 0); + case BCODE_NOT_LESS: + return (cmp >= 0); + case BCODE_GREATER: + return (cmp > 0); + case BCODE_NOT_GREATER: + return (cmp <= 0); + } + return (false); +} + +static void +compare(enum bcode_compare type) +{ + struct number *a, *b; + struct value *v; + int idx, elseidx; + bool ok; + + elseidx = NO_ELSE; + idx = readreg(); + if (readch() == 'e') + elseidx = readreg(); + else + unreadch(); + + a = pop_number(); + if (a == NULL) + return; + b = pop_number(); + if (b == NULL) { + push_number(a); + return; + } + + ok = compare_numbers(type, a, b); + + if (!ok && elseidx != NO_ELSE) + idx = elseidx; + + if (idx >= 0 && (ok || (!ok && elseidx != NO_ELSE))) { + v = stack_tos(&bmachine.reg[idx]); + if (v == NULL) + warnx("register '%c' (0%o) is empty", idx, idx); + else { + switch(v->type) { + case BCODE_NONE: + warnx("register '%c' (0%o) is empty", idx, idx); + break; + case BCODE_NUMBER: + warn("eval called with non-string argument"); + break; + case BCODE_STRING: + eval_string(bstrdup(v->u.string)); + break; + } + } + } +} + + +static void +nop(void) +{ + +} + +static void +quit(void) +{ + + if (bmachine.readsp < 2) + exit(0); + src_free(); + bmachine.readsp--; + src_free(); + bmachine.readsp--; +} + +static void +quitN(void) +{ + struct number *n; + u_long i; + + n = pop_number(); + if (n == NULL) + return; + i = get_ulong(n); + free_number(n); + if (i == BN_MASK2 || i == 0) + warnx("Q command requires a number >= 1"); + else if (bmachine.readsp < i) + warnx("Q command argument exceeded string execution depth"); + else { + while (i-- > 0) { + src_free(); + bmachine.readsp--; + } + } +} + +static void +skipN(void) +{ + struct number *n; + u_long i; + + n = pop_number(); + if (n == NULL) + return; + i = get_ulong(n); + if (i == BN_MASK2) + warnx("J command requires a number >= 0"); + else if (i > 0 && bmachine.readsp < i) + warnx("J command argument exceeded string execution depth"); + else { + while (i-- > 0) { + src_free(); + bmachine.readsp--; + } + skip_until_mark(); + } +} + +static void +skip_until_mark(void) +{ + + for (;;) { + switch (readch()) { + case 'M': + return; + case EOF: + errx(1, "mark not found"); + return; + case 'l': + case 'L': + case 's': + case 'S': + case ':': + case ';': + case '<': + case '>': + case '=': + readreg(); + if (readch() == 'e') + readreg(); + else + unreadch(); + break; + case '[': + free(read_string(&bmachine.readstack[bmachine.readsp])); + break; + case '!': + switch (readch()) { + case '<': + case '>': + case '=': + readreg(); + if (readch() == 'e') + readreg(); + else + unreadch(); + break; + default: + free(readline()); + break; + } + break; + default: + break; + } + } +} + +static void +parse_number(void) +{ + + unreadch(); + push_number(readnumber(&bmachine.readstack[bmachine.readsp], + bmachine.ibase)); +} + +static void +unknown(void) +{ + int ch = bmachine.readstack[bmachine.readsp].lastchar; + warnx("%c (0%o) is unimplemented", ch, ch); +} + +static void +eval_string(char *p) +{ + int ch; + + if (bmachine.readsp > 0) { + /* Check for tail call. Do not recurse in that case. */ + ch = readch(); + if (ch == EOF) { + src_free(); + src_setstring(&bmachine.readstack[bmachine.readsp], p); + return; + } else + unreadch(); + } + if (bmachine.readsp == bmachine.readstack_sz - 1) { + size_t newsz = bmachine.readstack_sz * 2; + struct source *stack; + stack = realloc(bmachine.readstack, newsz * + sizeof(struct source)); + if (stack == NULL) + err(1, "recursion too deep"); + bmachine.readstack_sz = newsz; + bmachine.readstack = stack; + } + src_setstring(&bmachine.readstack[++bmachine.readsp], p); +} + +static void +eval_line(void) +{ + /* Always read from stdin */ + struct source in; + char *p; + + clearerr(stdin); + src_setstream(&in, stdin); + p = (*in.vtable->readline)(&in); + eval_string(p); +} + +static void +eval_tos(void) +{ + char *p; + + p = pop_string(); + if (p == NULL) + return; + eval_string(p); +} + +void +eval(void) +{ + int ch; + + for (;;) { + ch = readch(); + if (ch == EOF) { + if (bmachine.readsp == 0) + return; + src_free(); + bmachine.readsp--; + continue; + } + if (bmachine.interrupted) { + if (bmachine.readsp > 0) { + src_free(); + bmachine.readsp--; + continue; + } else + bmachine.interrupted = false; + } +#ifdef DEBUGGING + fprintf(stderr, "# %c\n", ch); + stack_print(stderr, &bmachine.stack, "* ", + bmachine.obase); + fprintf(stderr, "%zd =>\n", bmachine.readsp); +#endif + + if (0 <= ch && ch < (signed)UCHAR_MAX) + (*jump_table[ch])(); + else + warnx("internal error: opcode %d", ch); + +#ifdef DEBUGGING + stack_print(stderr, &bmachine.stack, "* ", + bmachine.obase); + fprintf(stderr, "%zd ==\n", bmachine.readsp); +#endif + } +} diff --git a/usr.bin/dc/bcode.h b/usr.bin/dc/bcode.h new file mode 100644 index 0000000..9290cf1 --- /dev/null +++ b/usr.bin/dc/bcode.h @@ -0,0 +1,98 @@ +/* $FreeBSD$ */ +/* $OpenBSD: bcode.h,v 1.5 2006/01/16 08:09:25 otto Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <sys/types.h> +#include <openssl/bn.h> + +struct number { + BIGNUM *number; + u_int scale; +}; + +enum stacktype { + BCODE_NONE, + BCODE_NUMBER, + BCODE_STRING +}; + +enum bcode_compare { + BCODE_EQUAL, + BCODE_NOT_EQUAL, + BCODE_LESS, + BCODE_NOT_LESS, + BCODE_GREATER, + BCODE_NOT_GREATER +}; + +struct array; + +struct value { + union { + struct number *num; + char *string; + } u; + struct array *array; + enum stacktype type; +}; + +struct array { + struct value *data; + size_t size; +}; + +struct stack { + struct value *stack; + ssize_t size; + ssize_t sp; +}; + +struct source; + +struct vtable { + int (*readchar)(struct source *); + void (*unreadchar)(struct source *); + char *(*readline)(struct source *); + void (*free)(struct source *); +}; + +struct source { + union { + struct { + u_char *buf; + size_t pos; + } string; + FILE *stream; + } u; + struct vtable *vtable; + int lastchar; +}; + +void init_bmachine(bool); +void reset_bmachine(struct source *); +void scale_number(BIGNUM *, int); +void normalize(struct number *, u_int); +void eval(void); +void pn(const char *, const struct number *); +void pbn(const char *, const BIGNUM *); +void negate(struct number *); +void split_number(const struct number *, BIGNUM *, BIGNUM *); +void bmul_number(struct number *, struct number *, + struct number *); + +extern BIGNUM zero; diff --git a/usr.bin/dc/dc.1 b/usr.bin/dc/dc.1 new file mode 100644 index 0000000..03fc762 --- /dev/null +++ b/usr.bin/dc/dc.1 @@ -0,0 +1,548 @@ +.\" $FreeBSD$ +.\" $OpenBSD: dc.1,v 1.24 2010/01/02 19:48:56 schwarze Exp $ +.\" +.\" Copyright (C) Caldera International Inc. 2001-2002. +.\" 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 and documentation 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 or owned by Caldera +.\" International, Inc. +.\" 4. Neither the name of Caldera International, Inc. nor the names of other +.\" contributors may be used to endorse or promote products derived from +.\" this software without specific prior written permission. +.\" +.\" USE OF THE SOFTWARE PROVIDED FOR UNDER THIS LICENSE BY CALDERA +.\" INTERNATIONAL, 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 CALDERA INTERNATIONAL, INC. 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. +.\" +.\" @(#)dc.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd January 22, 2010 +.Dt DC 1 +.Os +.Sh NAME +.Nm dc +.Nd desk calculator +.Sh SYNOPSIS +.Nm +.Op Fl hxV +.Op Fl e Ar expression +.Op Fl f Ar filename +.Op Ar filename +.Sh DESCRIPTION +.Nm +is an arbitrary precision arithmetic package. +The overall structure of +.Nm +is +a stacking (reverse Polish) calculator i.e.\& +numbers are stored on a stack. +Adding a number pushes it onto the stack. +Arithmetic operations pop arguments off the stack +and push the results. +See also the +.Xr bc 1 +utility, which is a preprocessor for +.Nm +providing infix notation and a C-like syntax +which implements functions and reasonable control +structures for programs. +The options are as follows: +.Bl -tag -width Ds +.It Fl e Ar expr , Fl Fl expression Ar expr +Evaluate +.Ar expression . +If multiple +.Fl e +options are specified, they will be processed in the order given. +If no +.Ar filename +argument is given, execution will stop after processing the expressions +given on the command line, +otherwise processing will continue with the contents of +.Ar filename . +.It Fl f Ar filename , Fl Fl file Ar filename +Process the content of the given file before further calculations are done. +If multiple +.Fl f +options are specified, they will be processed in the order given. +.It Fl h , Fl Fl help +Print short usage info. +.It Fl V , Fl Fl version +Print version info. +.It Fl x +Enable extended register mode. +This mode is used by +.Xr bc 1 +to allow more than 256 registers. +See +.Sx Registers +for a more detailed description. +.El +.Pp +Ordinarily, +.Nm +operates on decimal integers, +but one may specify an input base, output base, +and a number of fractional digits (scale) to be maintained. +If an argument is given, +input is taken from that file until its end, +then from the standard input. +Whitespace is ignored, except where it signals the end of a number, +end of a line or when a register name is expected. +The following constructions are recognized: +.Bl -tag -width "number" +.It Va number +The value of the number is pushed on the stack. +A number is an unbroken string of the digits 0\-9 and letters A\-F. +It may be preceded by an underscore +.Pq Sq _ +to input a negative number. +A number may contain a single decimal point. +A number may also contain the characters A\-F, with the values 10\-15. +.It Cm "+ - / * % ~ ^" +The +top two values on the stack are added +(+), +subtracted +(\-), +multiplied (*), +divided (/), +remaindered (%), +divided and remaindered (~), +or exponentiated (^). +The two entries are popped off the stack; +the result is pushed on the stack in their place. +Any fractional part of an exponent is ignored. +.Pp +For addition and subtraction, the scale of the result is the maximum +of scales of the operands. +For division the scale of the result is defined +by the scale set by the +.Ic k +operation. +For multiplication, the scale is defined by the expression +.Sy min(a+b,max(a,b,scale)) , +where +.Sy a +and +.Sy b +are the scales of the operands, and +.Sy scale +is the scale defined by the +.Ic k +operation. +For exponentiation with a non-negative exponent, the scale of the result is +.Sy min(a*b,max(scale,a)) , +where +.Sy a +is the scale of the base, and +.Sy b +is the +.Em value +of the exponent. +If the exponent is negative, the scale of the result is the scale +defined by the +.Ic k +operation. +.Pp +In the case of the division and modulus operator (~), +the resultant quotient is pushed first followed by the remainder. +This is a shorthand for the sequence: +.Bd -literal -offset indent -compact +x y / x y % +.Ed +The division and modulus operator is a non-portable extension. +.It Ic a +Pop the top value from the stack. +If that value is a number, compute the integer part of the number modulo 256. +If the result is zero, push an empty string. +Otherwise push a one character string by interpreting the computed value +as an +.Tn ASCII +character. +.Pp +If the top value is a string, push a string containing the first character +of the original string. +If the original string is empty, an empty string is pushed back. +The +.Ic a +operator is a non-portable extension. +.It Ic c +All values on the stack are popped. +.It Ic d +The top value on the stack is duplicated. +.It Ic f +All values on the stack are printed, separated by newlines. +.It Ic G +The top two numbers are popped from the stack and compared. +A one is pushed if the top of the stack is equal to the second number +on the stack. +A zero is pushed otherwise. +This is a non-portable extension. +.It Ic I +Pushes the input base on the top of the stack. +.It Ic i +The top value on the stack is popped and used as the +base for further input. +The initial input base is 10. +.It Ic J +Pop the top value from the stack. +The recursion level is popped by that value and, following that, +the input is skipped until the first occurrence of the +.Ic M +operator. +The +.Ic J +operator is a non-portable extension, used by the +.Xr bc 1 +command. +.It Ic K +The current scale factor is pushed onto the stack. +.It Ic k +The top of the stack is popped, and that value is used as +a non-negative scale factor: +the appropriate number of places +are printed on output, +and maintained during multiplication, division, and exponentiation. +The interaction of scale factor, +input base, and output base will be reasonable if all are changed +together. +.It Ic L Ns Ar x +Register +.Ar x +is treated as a stack and its top value is popped onto the main stack. +.It Ic l Ns Ar x +The +value in register +.Ar x +is pushed on the stack. +The register +.Ar x +is not altered. +Initially, all registers contain the value zero. +.It Ic M +Mark used by the +.Ic J +operator. +The +.Ic M +operator is a non-portable extensions, used by the +.Xr bc 1 +command. +.It Ic N +The top of the stack is replaced by one if the top of the stack +is equal to zero. +If the top of the stack is unequal to zero, it is replaced by zero. +This is a non-portable extension. +.It Ic n +The top value on the stack is popped and printed without a newline. +This is a non-portable extension. +.It Ic O +Pushes the output base on the top of the stack. +.It Ic o +The top value on the stack is popped and used as the +base for further output. +The initial output base is 10. +.It Ic P +The top of the stack is popped. +If the top of the stack is a string, it is printed without a trailing newline. +If the top of the stack is a number, it is interpreted as a +base 256 number, and each digit of this base 256 number is printed as +an +.Tn ASCII +character, without a trailing newline. +.It Ic p +The top value on the stack is printed with a trailing newline. +The top value remains unchanged. +.It Ic Q +The top value on the stack is popped and the string execution level is popped +by that value. +.It Ic q +Exits the program. +If executing a string, the recursion level is +popped by two. +.It Ic R +The top of the stack is removed (popped). +This is a non-portable extension. +.It Ic r +The top two values on the stack are reversed (swapped). +This is a non-portable extension. +.It Ic S Ns Ar x +Register +.Ar x +is treated as a stack. +The top value of the main stack is popped and pushed on it. +.It Ic s Ns Ar x +The +top of the stack is popped and stored into +a register named +.Ar x . +.It Ic v +Replaces the top element on the stack by its square root. +The scale of the result is the maximum of the scale of the argument +and the current value of scale. +.It Ic X +Replaces the number on the top of the stack with its scale factor. +If the top of the stack is a string, replace it with the integer 0. +.It Ic x +Treats the top element of the stack as a character string +and executes it as a string of +.Nm +commands. +.It Ic Z +Replaces the number on the top of the stack with its length. +The length of a string is its number of characters. +The length of a number is its number of digits, not counting the minus sign +and decimal point. +.It Ic z +The stack level is pushed onto the stack. +.It Cm [ Ns ... Ns Cm ] +Puts the bracketed +.Tn ASCII +string onto the top of the stack. +If the string includes brackets, these must be properly balanced. +The backslash character +.Pq Sq \e +may be used as an escape character, making it +possible to include unbalanced brackets in strings. +To include a backslash in a string, use a double backslash. +.It Xo +.Cm < Ns Va x +.Cm > Ns Va x +.Cm = Ns Va x +.Cm !< Ns Va x +.Cm !> Ns Va x +.Cm != Ns Va x +.Xc +The top two elements of the stack are popped and compared. +Register +.Ar x +is executed if they obey the stated +relation. +.It Xo +.Cm < Ns Va x Ns e Ns Va y +.Cm > Ns Va x Ns e Ns Va y +.Cm = Ns Va x Ns e Ns Va y +.Cm !< Ns Va x Ns e Ns Va y +.Cm !> Ns Va x Ns e Ns Va y +.Cm != Ns Va x Ns e Ns Va y +.Xc +These operations are variants of the comparison operations above. +The first register name is followed by the letter +.Sq e +and another register name. +Register +.Ar x +will be executed if the relation is true, and register +.Ar y +will be executed if the relation is false. +This is a non-portable extension. +.It Ic \&( +The top two numbers are popped from the stack and compared. +A one is pushed if the top of the stack is less than the second number +on the stack. +A zero is pushed otherwise. +This is a non-portable extension. +.It Ic { +The top two numbers are popped from the stack and compared. +A one is pushed if the top of stack is less than or equal to the +second number on the stack. +A zero is pushed otherwise. +This is a non-portable extension. +.It Ic \&! +Interprets the rest of the line as a +.Ux +command. +.It Ic \&? +A line of input is taken from the input source (usually the terminal) +and executed. +.It Ic : Ns Ar r +Pop two values from the stack. +The second value on the stack is stored into the array +.Ar r +indexed by the top of stack. +.It Ic ; Ns Ar r +Pop a value from the stack. +The value is used as an index into register +.Ar r . +The value in this register is pushed onto the stack. +.Pp +Array elements initially have the value zero. +Each level of a stacked register has its own array associated with +it. +The command sequence +.Bd -literal -offset indent +[first] 0:a [dummy] Sa [second] 0:a 0;a p La 0;a p +.Ed +.Pp +will print +.Bd -literal -offset indent +second +first +.Ed +.Pp +since the string +.Ql second +is written in an array that is later popped, to reveal the array that +stored +.Ql first . +.It Ic # +Skip the rest of the line. +This is a non-portable extension. +.El +.Ss Registers +Registers have a single character name +.Ar x , +where +.Ar x +may be any character, including space, tab or any other special character. +If extended register mode is enabled using the +.Fl x +option and the register identifier +.Ar x +has the value 255, the next two characters are interpreted as a +two-byte register index. +The set of standard single character registers and the set of extended +registers do not overlap. +Extended register mode is a non-portable extension. +.Sh EXAMPLES +An example which prints the first ten values of +.Ic n! : +.Bd -literal -offset indent +[la1+dsa*pla10>y]sy +0sa1 +lyx +.Ed +.Pp +Independent of the current input base, the command +.Bd -literal -offset indent +Ai +.Ed +.Pp +will reset the input base to decimal 10. +.Sh DIAGNOSTICS +.Bl -diag +.It %c (0%o) is unimplemented +an undefined operation was called. +.It stack empty +for not enough elements on the stack to do what was asked. +.It stack register '%c' (0%o) is empty +for an +.Ar L +operation from a stack register that is empty. +.It Runtime warning: non-zero scale in exponent +for a fractional part of an exponent that is being ignored. +.It divide by zero +for trying to divide by zero. +.It remainder by zero +for trying to take a remainder by zero. +.It square root of negative number +for trying to take the square root of a negative number. +.It index too big +for an array index that is larger than 2048. +.It negative index +for a negative array index. +.It "input base must be a number between 2 and 16" +for trying to set an illegal input base. +.It output base must be a number greater than 1 +for trying to set an illegal output base. +.It scale must be a nonnegative number +for trying to set a negative or zero scale. +.It scale too large +for trying to set a scale that is too large. +A scale must be representable as a 32-bit unsigned number. +.It Q command argument exceeded string execution depth +for trying to pop the recursion level more than the current +recursion level. +.It Q command requires a number >= 1 +for trying to pop an illegal number of recursion levels. +.It recursion too deep +for too many levels of nested execution. +.Pp +The recursion level is increased by one if the +.Ar x +or +.Ar ?\& +operation or one of the compare operations resulting in the execution +of register is executed. +As an exception, the recursion level is not increased if the operation +is executed as the last command of a string. +For example, the commands +.Bd -literal -offset indent +[lax]sa +1 lax +.Ed +.Pp +will execute an endless loop, while the commands +.Bd -literal -offset indent +[laxp]sa +1 lax +.Ed +.Pp +will terminate because of a too deep recursion level. +.It J command argument exceeded string execution depth +for trying to pop the recursion level more than the current +recursion level. +.It mark not found +for a failed scan for an occurrence of the +.Ic M +operator. +.El +.Sh SEE ALSO +.Xr bc 1 +.Pp +.An -nosplit +.An L. L. Cherry , +.An R. Morris +"DC \- An Interactive Desk Calculator" +.Pa /usr/share/doc/usd/05.dc/ . +.Sh STANDARDS +The arithmetic operations of the +.Nm +utility are expected to conform to the definition listed in the +.Xr bc 1 +section of the +.St -p1003.2 +specification. +.Sh HISTORY +The +.Nm +command first appeared in +.At v6 . +A complete rewrite of the +.Nm +command using the +.Xr bn 3 +big number routines first appeared in +.Ox 3.5 . +.Sh AUTHORS +.An -nosplit +The original version of the +.Nm +command was written by +.An Robert Morris +and +.An Lorinda Cherry . +The current version of the +.Nm +utility was written by +.An Otto Moerbeek . diff --git a/usr.bin/dc/dc.c b/usr.bin/dc/dc.c new file mode 100644 index 0000000..1376653 --- /dev/null +++ b/usr.bin/dc/dc.c @@ -0,0 +1,135 @@ +/* $OpenBSD: dc.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * Copyright (c) 2009, Gabor Kovesdan <gabor@FreeBSD.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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <getopt.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define DC_VER "1.3-FreeBSD" + +static void usage(void); + +extern char *__progname; + +struct source src; + +struct option long_options[] = +{ + {"expression", required_argument, NULL, 'e'}, + {"file", required_argument, NULL, 'f'}, + {"help", no_argument, NULL, 'h'}, + {"version", no_argument, NULL, 'V'} +}; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s [-hVx] [-e expression] [file]\n", + __progname); + exit(1); +} + +static void +procfile(char *fname) { + struct stat st; + FILE *file; + + file = fopen(fname, "r"); + if (file == NULL) + err(1, "cannot open file %s", fname); + if (fstat(fileno(file), &st) == -1) + err(1, "%s", fname); + if (S_ISDIR(st.st_mode)) { + errno = EISDIR; + err(1, "%s", fname); + } + src_setstream(&src, file); + reset_bmachine(&src); + eval(); + fclose(file); +} + +int +main(int argc, char *argv[]) +{ + int ch; + bool extended_regs = false, preproc_done = false; + + /* accept and ignore a single dash to be 4.4BSD dc(1) compatible */ + while ((ch = getopt_long(argc, argv, "e:f:Vx", long_options, NULL)) != -1) { + switch (ch) { + case 'e': + src_setstring(&src, optarg); + reset_bmachine(&src); + eval(); + preproc_done = true; + break; + case 'f': + procfile(optarg); + preproc_done = true; + break; + case 'x': + extended_regs = true; + break; + case 'V': + fprintf(stderr, "%s (BSD bc) %s\n", __progname, DC_VER); + exit(0); + break; + case '-': + break; + case 'h': + /* FALLTHROUGH */ + default: + usage(); + } + } + argc -= optind; + argv += optind; + + init_bmachine(extended_regs); + setlinebuf(stdout); + setlinebuf(stderr); + + if (argc > 1) + usage(); + if (argc == 1) { + procfile(argv[0]); + preproc_done = true; + } + if (preproc_done) + return (0); + + src_setstream(&src, stdin); + reset_bmachine(&src); + eval(); + + return (0); +} diff --git a/usr.bin/dc/extern.h b/usr.bin/dc/extern.h new file mode 100644 index 0000000..4abf063 --- /dev/null +++ b/usr.bin/dc/extern.h @@ -0,0 +1,63 @@ +/* $FreeBSD$ */ +/* $OpenBSD: extern.h,v 1.3 2006/01/16 08:09:25 otto Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <stdbool.h> +#include "bcode.h" + + +/* inout.c */ +void src_setstream(struct source *, FILE *); +void src_setstring(struct source *, char *); +struct number *readnumber(struct source *, u_int); +void printnumber(FILE *, const struct number *, u_int); +char *read_string(struct source *); +void print_value(FILE *, const struct value *, const char *, u_int); +void print_ascii(FILE *, const struct number *); + +/* mem.c */ +struct number *new_number(void); +void free_number(struct number *); +struct number *dup_number(const struct number *); +void *bmalloc(size_t); +void *brealloc(void *, size_t); +char *bstrdup(const char *p); +void bn_check(int); +void bn_checkp(const void *); + +/* stack.c */ +void stack_init(struct stack *); +void stack_free_value(struct value *); +struct value *stack_dup_value(const struct value *, struct value *); +void stack_swap(struct stack *); +size_t stack_size(const struct stack *); +void stack_dup(struct stack *); +void stack_pushnumber(struct stack *, struct number *); +void stack_pushstring(struct stack *stack, char *); +void stack_push(struct stack *, struct value *); +void stack_set_tos(struct stack *, struct value *); +struct value *stack_tos(const struct stack *); +struct value *stack_pop(struct stack *); +struct number *stack_popnumber(struct stack *); +char *stack_popstring(struct stack *); +void stack_clear(struct stack *); +void stack_print(FILE *, const struct stack *, const char *, + u_int base); +void frame_assign(struct stack *, size_t, const struct value *); +struct value *frame_retrieve(const struct stack *, size_t); +/* void frame_free(struct stack *); */ diff --git a/usr.bin/dc/inout.c b/usr.bin/dc/inout.c new file mode 100644 index 0000000..4a2bb70 --- /dev/null +++ b/usr.bin/dc/inout.c @@ -0,0 +1,417 @@ +/* $OpenBSD: inout.c,v 1.15 2009/10/27 23:59:37 deraadt Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <openssl/ssl.h> +#include <ctype.h> +#include <err.h> +#include <string.h> + +#include "extern.h" + +#define MAX_CHARS_PER_LINE 68 + +static int lastchar; +static int charcount; + +static int src_getcharstream(struct source *); +static void src_ungetcharstream(struct source *); +static char *src_getlinestream(struct source *); +static int src_getcharstring(struct source *); +static void src_ungetcharstring(struct source *); +static char *src_getlinestring(struct source *); +static void src_freestring(struct source *); +static void flushwrap(FILE *); +static void putcharwrap(FILE *, int); +static void printwrap(FILE *, const char *); +static char *get_digit(u_long, int, u_int); + +static struct vtable stream_vtable = { + src_getcharstream, + src_ungetcharstream, + src_getlinestream, + NULL +}; + +static struct vtable string_vtable = { + src_getcharstring, + src_ungetcharstring, + src_getlinestring, + src_freestring +}; + +void +src_setstream(struct source *src, FILE *stream) +{ + + src->u.stream = stream; + src->vtable = &stream_vtable; +} + +void +src_setstring(struct source *src, char *p) +{ + + src->u.string.buf = (u_char *)p; + src->u.string.pos = 0; + src->vtable = &string_vtable; +} + +static int +src_getcharstream(struct source *src) +{ + + return (src->lastchar = getc(src->u.stream)); +} + +static void +src_ungetcharstream(struct source *src) +{ + + ungetc(src->lastchar, src->u.stream); +} + +static char * +src_getlinestream(struct source *src) +{ + char buf[BUFSIZ]; + + if (fgets(buf, BUFSIZ, src->u.stream) == NULL) + return (bstrdup("")); + return bstrdup(buf); +} + +static int +src_getcharstring(struct source *src) +{ + + src->lastchar = src->u.string.buf[src->u.string.pos]; + if (src->lastchar == '\0') + return (EOF); + else { + src->u.string.pos++; + return (src->lastchar); + } +} + +static void +src_ungetcharstring(struct source *src) +{ + + if (src->u.string.pos > 0) { + if (src->lastchar != '\0') + --src->u.string.pos; + } +} + +static char * +src_getlinestring(struct source *src) +{ + char buf[BUFSIZ]; + int i, ch; + + i = 0; + while (i < BUFSIZ-1) { + ch = src_getcharstring(src); + if (ch == EOF) + break; + buf[i++] = ch; + if (ch == '\n') + break; + } + buf[i] = '\0'; + return (bstrdup(buf)); +} + +static void +src_freestring(struct source *src) +{ + + free(src->u.string.buf); +} + +static void +flushwrap(FILE *f) +{ + + if (lastchar != -1) + putc(lastchar, f); +} + +static void +putcharwrap(FILE *f, int ch) +{ + + if (charcount >= MAX_CHARS_PER_LINE) { + charcount = 0; + fputs("\\\n", f); + } + if (lastchar != -1) { + charcount++; + putc(lastchar, f); + } + lastchar = ch; +} + +static void +printwrap(FILE *f, const char *p) +{ + char *q; + char buf[12]; + + q = buf; + strlcpy(buf, p, sizeof(buf)); + while (*q) + putcharwrap(f, *q++); +} + +struct number * +readnumber(struct source *src, u_int base) +{ + struct number *n; + BN_ULONG v; + u_int i; + int ch; + bool dot = false, sign = false; + + n = new_number(); + bn_check(BN_zero(n->number)); + + while ((ch = (*src->vtable->readchar)(src)) != EOF) { + + if ('0' <= ch && ch <= '9') + v = ch - '0'; + else if ('A' <= ch && ch <= 'F') + v = ch - 'A' + 10; + else if (ch == '_') { + sign = true; + continue; + } else if (ch == '.') { + if (dot) + break; + dot = true; + continue; + } else { + (*src->vtable->unreadchar)(src); + break; + } + if (dot) + n->scale++; + + bn_check(BN_mul_word(n->number, base)); + +#if 0 + /* work around a bug in BN_add_word: 0 += 0 is buggy.... */ + if (v > 0) +#endif + bn_check(BN_add_word(n->number, v)); + } + if (base != 10) { + scale_number(n->number, n->scale); + for (i = 0; i < n->scale; i++) + BN_div_word(n->number, base); + } + if (sign) + negate(n); + return (n); +} + +char * +read_string(struct source *src) +{ + char *p; + int count, ch, i, new_sz, sz; + bool escape; + + escape = false; + count = 1; + i = 0; + sz = 15; + p = bmalloc(sz + 1); + + while ((ch = (*src->vtable->readchar)(src)) != EOF) { + if (!escape) { + if (ch == '[') + count++; + else if (ch == ']') + count--; + if (count == 0) + break; + } + if (ch == '\\' && !escape) + escape = true; + else { + escape = false; + if (i == sz) { + new_sz = sz * 2; + p = brealloc(p, new_sz + 1); + sz = new_sz; + } + p[i++] = ch; + } + } + p[i] = '\0'; + return (p); +} + +static char * +get_digit(u_long num, int digits, u_int base) +{ + char *p; + + if (base <= 16) { + p = bmalloc(2); + p[0] = num >= 10 ? num + 'A' - 10 : num + '0'; + p[1] = '\0'; + } else { + if (asprintf(&p, "%0*lu", digits, num) == -1) + err(1, NULL); + } + return (p); +} + +void +printnumber(FILE *f, const struct number *b, u_int base) +{ + struct number *fract_part, *int_part; + struct stack stack; + char *p; + char buf[11]; + size_t sz; + unsigned int i; + int digits; + + charcount = 0; + lastchar = -1; + if (BN_is_zero(b->number)) + putcharwrap(f, '0'); + + int_part = new_number(); + fract_part = new_number(); + fract_part->scale = b->scale; + + if (base <= 16) + digits = 1; + else { + digits = snprintf(buf, sizeof(buf), "%u", base-1); + } + split_number(b, int_part->number, fract_part->number); + + i = 0; + stack_init(&stack); + while (!BN_is_zero(int_part->number)) { + BN_ULONG rem = BN_div_word(int_part->number, base); + stack_pushstring(&stack, get_digit(rem, digits, base)); + i++; + } + sz = i; + if (BN_cmp(b->number, &zero) < 0) + putcharwrap(f, '-'); + for (i = 0; i < sz; i++) { + p = stack_popstring(&stack); + if (base > 16) + putcharwrap(f, ' '); + printwrap(f, p); + free(p); + } + stack_clear(&stack); + if (b->scale > 0) { + struct number *num_base; + BIGNUM mult, stop; + + putcharwrap(f, '.'); + num_base = new_number(); + bn_check(BN_set_word(num_base->number, base)); + BN_init(&mult); + bn_check(BN_one(&mult)); + BN_init(&stop); + bn_check(BN_one(&stop)); + scale_number(&stop, b->scale); + + i = 0; + while (BN_cmp(&mult, &stop) < 0) { + u_long rem; + + if (i && base > 16) + putcharwrap(f, ' '); + i = 1; + + bmul_number(fract_part, fract_part, num_base); + split_number(fract_part, int_part->number, NULL); + rem = BN_get_word(int_part->number); + p = get_digit(rem, digits, base); + int_part->scale = 0; + normalize(int_part, fract_part->scale); + bn_check(BN_sub(fract_part->number, fract_part->number, + int_part->number)); + printwrap(f, p); + free(p); + bn_check(BN_mul_word(&mult, base)); + } + free_number(num_base); + BN_free(&mult); + BN_free(&stop); + } + flushwrap(f); + free_number(int_part); + free_number(fract_part); +} + +void +print_value(FILE *f, const struct value *value, const char *prefix, u_int base) +{ + + fputs(prefix, f); + switch (value->type) { + case BCODE_NONE: + if (value->array != NULL) + fputs("<array>", f); + break; + case BCODE_NUMBER: + printnumber(f, value->u.num, base); + break; + case BCODE_STRING: + fputs(value->u.string, f); + break; + } +} + +void +print_ascii(FILE *f, const struct number *n) +{ + BIGNUM *v; + int ch, i, numbits; + + v = BN_dup(n->number); + bn_checkp(v); + + if (BN_cmp(v, &zero) < 0) + bn_check(BN_sub(v, &zero, v)); + + numbits = BN_num_bytes(v) * 8; + while (numbits > 0) { + ch = 0; + for (i = 0; i < 8; i++) + ch |= BN_is_bit_set(v, numbits-i-1) << (7 - i); + putc(ch, f); + numbits -= 8; + } + BN_free(v); +} diff --git a/usr.bin/dc/mem.c b/usr.bin/dc/mem.c new file mode 100644 index 0000000..78fb429 --- /dev/null +++ b/usr.bin/dc/mem.c @@ -0,0 +1,110 @@ +/* $OpenBSD: mem.c,v 1.5 2009/10/27 23:59:37 deraadt Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <openssl/err.h> + +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include "extern.h" + +struct number * +new_number(void) +{ + struct number *n; + + n = bmalloc(sizeof(*n)); + n->scale = 0; + n->number = BN_new(); + if (n->number == NULL) + err(1, NULL); + return (n); +} + +void +free_number(struct number *n) +{ + + BN_free(n->number); + free(n); +} + +struct number * +dup_number(const struct number *a) +{ + struct number *n; + + n = bmalloc(sizeof(*n)); + n->scale = a->scale; + n->number = BN_dup(a->number); + bn_checkp(n->number); + return (n); +} + +void * +bmalloc(size_t sz) +{ + void *p; + + p = malloc(sz); + if (p == NULL) + err(1, NULL); + return (p); +} + +void * +brealloc(void *p, size_t sz) +{ + void *q; + + q = realloc(p, sz); + if (q == NULL) + err(1, NULL); + return (q); +} + +char * +bstrdup(const char *p) +{ + char *q; + + q = strdup(p); + if (q == NULL) + err(1, NULL); + return (q); +} + +void +bn_check(int x) \ +{ + + if (x == 0) + err(1, "big number failure %lx", ERR_get_error()); +} + +void +bn_checkp(const void *p) \ +{ + + if (p == NULL) + err(1, "allocation failure %lx", ERR_get_error()); +} diff --git a/usr.bin/dc/stack.c b/usr.bin/dc/stack.c new file mode 100644 index 0000000..950b4e5 --- /dev/null +++ b/usr.bin/dc/stack.c @@ -0,0 +1,379 @@ +/* $OpenBSD: stack.c,v 1.11 2009/10/27 23:59:37 deraadt Exp $ */ + +/* + * Copyright (c) 2003, Otto Moerbeek <otto@drijf.net> + * + * 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 <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <stdlib.h> +#include <string.h> + +#include "extern.h" + +static __inline bool stack_empty(const struct stack *); +static void stack_grow(struct stack *); +static struct array *array_new(void); +static __inline void array_free(struct array *); +static struct array *array_dup(const struct array *); +static __inline void array_grow(struct array *, size_t); +static __inline void array_assign(struct array *, size_t, const struct value *); +static __inline struct value *array_retrieve(const struct array *, size_t); + +void +stack_init(struct stack *stack) +{ + + stack->size = 0; + stack->sp = -1; + stack->stack = NULL; +} + +static __inline bool +stack_empty(const struct stack *stack) +{ + bool empty = stack->sp == -1; + + if (empty) + warnx("stack empty"); + return empty; +} + +/* Clear number or string, but leave value itself */ +void +stack_free_value(struct value *v) +{ + + switch (v->type) { + case BCODE_NONE: + break; + case BCODE_NUMBER: + free_number(v->u.num); + break; + case BCODE_STRING: + free(v->u.string); + break; + } + if (v->array != NULL) { + array_free(v->array); + v->array = NULL; + } +} + +/* Copy number or string content into already allocated target */ +struct value * +stack_dup_value(const struct value *a, struct value *copy) +{ + + copy->type = a->type; + + switch (a->type) { + case BCODE_NONE: + break; + case BCODE_NUMBER: + copy->u.num = dup_number(a->u.num); + break; + case BCODE_STRING: + copy->u.string = strdup(a->u.string); + if (copy->u.string == NULL) + err(1, NULL); + break; + } + + copy->array = a->array == NULL ? NULL : array_dup(a->array); + + return (copy); +} + +size_t +stack_size(const struct stack *stack) +{ + + return (stack->sp + 1); +} + +void +stack_dup(struct stack *stack) +{ + struct value *value; + struct value copy; + + value = stack_tos(stack); + if (value == NULL) { + warnx("stack empty"); + return; + } + stack_push(stack, stack_dup_value(value, ©)); +} + +void +stack_swap(struct stack *stack) +{ + struct value copy; + + if (stack->sp < 1) { + warnx("stack empty"); + return; + } + copy = stack->stack[stack->sp]; + stack->stack[stack->sp] = stack->stack[stack->sp-1]; + stack->stack[stack->sp-1] = copy; +} + +static void +stack_grow(struct stack *stack) +{ + size_t i, new_size; + + if (++stack->sp == stack->size) { + new_size = stack->size * 2 + 1; + stack->stack = brealloc(stack->stack, + new_size * sizeof(*stack->stack)); + for (i = stack->size; i < new_size; i++) + stack->stack[i].array = NULL; + stack->size = new_size; + } +} + +void +stack_pushnumber(struct stack *stack, struct number *b) +{ + + stack_grow(stack); + stack->stack[stack->sp].type = BCODE_NUMBER; + stack->stack[stack->sp].u.num = b; +} + +void +stack_pushstring(struct stack *stack, char *string) +{ + + stack_grow(stack); + stack->stack[stack->sp].type = BCODE_STRING; + stack->stack[stack->sp].u.string = string; +} + +void +stack_push(struct stack *stack, struct value *v) +{ + + switch (v->type) { + case BCODE_NONE: + stack_grow(stack); + stack->stack[stack->sp].type = BCODE_NONE; + break; + case BCODE_NUMBER: + stack_pushnumber(stack, v->u.num); + break; + case BCODE_STRING: + stack_pushstring(stack, v->u.string); + break; + } + stack->stack[stack->sp].array = v->array == NULL ? + NULL : array_dup(v->array); +} + +struct value * +stack_tos(const struct stack *stack) +{ + + if (stack->sp == -1) + return (NULL); + return &stack->stack[stack->sp]; +} + +void +stack_set_tos(struct stack *stack, struct value *v) +{ + + if (stack->sp == -1) + stack_push(stack, v); + else { + stack_free_value(&stack->stack[stack->sp]); + stack->stack[stack->sp] = *v; + stack->stack[stack->sp].array = v->array == NULL ? + NULL : array_dup(v->array); + } +} + +struct value * +stack_pop(struct stack *stack) +{ + + if (stack_empty(stack)) + return (NULL); + return &stack->stack[stack->sp--]; +} + +struct number * +stack_popnumber(struct stack *stack) +{ + + if (stack_empty(stack)) + return (NULL); + if (stack->stack[stack->sp].array != NULL) { + array_free(stack->stack[stack->sp].array); + stack->stack[stack->sp].array = NULL; + } + if (stack->stack[stack->sp].type != BCODE_NUMBER) { + warnx("not a number"); /* XXX remove */ + return (NULL); + } + return stack->stack[stack->sp--].u.num; +} + +char * +stack_popstring(struct stack *stack) +{ + + if (stack_empty(stack)) + return (NULL); + if (stack->stack[stack->sp].array != NULL) { + array_free(stack->stack[stack->sp].array); + stack->stack[stack->sp].array = NULL; + } + if (stack->stack[stack->sp].type != BCODE_STRING) { + warnx("not a string"); /* XXX remove */ + return (NULL); + } + return stack->stack[stack->sp--].u.string; +} + +void +stack_clear(struct stack *stack) +{ + + while (stack->sp >= 0) { + stack_free_value(&stack->stack[stack->sp--]); + } + free(stack->stack); + stack_init(stack); +} + +void +stack_print(FILE *f, const struct stack *stack, const char *prefix, u_int base) +{ + ssize_t i; + + for (i = stack->sp; i >= 0; i--) { + print_value(f, &stack->stack[i], prefix, base); + putc('\n', f); + } +} + + +static struct array * +array_new(void) +{ + struct array *a; + + a = bmalloc(sizeof(*a)); + a->data = NULL; + a->size = 0; + return a; +} + +static __inline void +array_free(struct array *a) +{ + size_t i; + + if (a == NULL) + return; + for (i = 0; i < a->size; i++) + stack_free_value(&a->data[i]); + free(a->data); + free(a); +} + +static struct array * +array_dup(const struct array *a) +{ + struct array *n; + size_t i; + + if (a == NULL) + return (NULL); + n = array_new(); + array_grow(n, a->size); + for (i = 0; i < a->size; i++) + stack_dup_value(&a->data[i], &n->data[i]); + return (n); +} + +static __inline void +array_grow(struct array *array, size_t newsize) +{ + size_t i; + + array->data = brealloc(array->data, newsize * sizeof(*array->data)); + for (i = array->size; i < newsize; i++) { + array->data[i].type = BCODE_NONE; + array->data[i].array = NULL; + } + array->size = newsize; +} + +static __inline void +array_assign(struct array *array, size_t i, const struct value *v) +{ + + if (i >= array->size) + array_grow(array, i + 1); + stack_free_value(&array->data[i]); + array->data[i] = *v; +} + +static __inline struct value * +array_retrieve(const struct array *array, size_t i) +{ + + if (i >= array->size) + return (NULL); + return &array->data[i]; +} + +void +frame_assign(struct stack *stack, size_t i, const struct value *v) +{ + struct array *a; + struct value n; + + if (stack->sp == -1) { + n.type = BCODE_NONE; + n.array = NULL; + stack_push(stack, &n); + } + + a = stack->stack[stack->sp].array; + if (a == NULL) + a = stack->stack[stack->sp].array = array_new(); + array_assign(a, i, v); +} + +struct value * +frame_retrieve(const struct stack *stack, size_t i) +{ + struct array *a; + + if (stack->sp == -1) + return (NULL); + a = stack->stack[stack->sp].array; + if (a == NULL) + a = stack->stack[stack->sp].array = array_new(); + return array_retrieve(a, i); +} diff --git a/usr.bin/dig/Makefile b/usr.bin/dig/Makefile index 585e379..ec11dc4 100644 --- a/usr.bin/dig/Makefile +++ b/usr.bin/dig/Makefile @@ -20,6 +20,8 @@ CFLAGS+= -DWITH_IDN -I/usr/local/include CFLAGS+= -L/usr/local/lib -lidnkit -R/usr/local/lib -liconv .endif +WARNS?= 1 + DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} diff --git a/usr.bin/du/Makefile b/usr.bin/du/Makefile index 12e80b9..f614866 100644 --- a/usr.bin/du/Makefile +++ b/usr.bin/du/Makefile @@ -2,7 +2,6 @@ # $FreeBSD$ PROG= du -WARNS?= 6 DPADD= ${LIBUTIL} LDADD= -lutil diff --git a/usr.bin/ee/Makefile b/usr.bin/ee/Makefile index 43f0432..ffa47b9 100644 --- a/usr.bin/ee/Makefile +++ b/usr.bin/ee/Makefile @@ -14,7 +14,7 @@ LDADD= -lncurses WARNS?= 2 NLS= en_US.US-ASCII fr_FR.ISO8859-1 de_DE.ISO8859-1 pl_PL.ISO8859-2 \ - uk_UA.KOI8-U ru_RU.KOI8-R hu_HU.ISO8859-2 + uk_UA.KOI8-U pt_BR.ISO8859-1 ru_RU.KOI8-R hu_HU.ISO8859-2 NLSLINKS_en_US.US-ASCII= en_US.ISO8859-1 en_US.ISO8859-15 NLSLINKS_fr_FR.ISO8859-1= fr_BE.ISO8859-1 fr_BE.ISO8859-15 \ @@ -22,6 +22,7 @@ NLSLINKS_fr_FR.ISO8859-1= fr_BE.ISO8859-1 fr_BE.ISO8859-15 \ fr_FR.ISO8859-15 NLSLINKS_de_DE.ISO8859-1= de_AT.ISO8859-1 de_AT.ISO8859-15 de_CH.ISO8859-1 \ de_CH.ISO8859-15 de_DE.ISO8859-15 +NLSLINKS_pt_BR.ISO8859-1= pt_PT.ISO8859-1 NLSSRCFILES=ee.msg .for lang in ${NLS} diff --git a/usr.bin/ee/nls/pt_BR.ISO8859-1/ee.msg b/usr.bin/ee/nls/pt_BR.ISO8859-1/ee.msg new file mode 100644 index 0000000..592ea17 --- /dev/null +++ b/usr.bin/ee/nls/pt_BR.ISO8859-1/ee.msg @@ -0,0 +1,186 @@ +$ This file contains the messages for ee ("easy editor"). See the file +$ ee.i18n.guide for more information +$ +$ For ee patchlevel 3 +$ +$ $Header: /home/hugh/sources/old_ae/RCS/ee.msg,v 1.8 1996/11/30 03:23:40 hugh Exp $ +$ $FreeBSD$ +$ +$ +$set 1 +$quote " +1 "modo menu" +2 "tabs para espaços " +3 "busca com case sensitive " +4 "observar margens " +5 "formatação de auto-parágrafo" +6 "caracteres de oito bits " +7 "informação da janela " +8 "margem direita " +9 "deixar o menu" +10 "salvar mudanças" +11 "sem salvar" +12 "menu arquivo" +13 "ler um arquivo" +14 "escrever uma arquivo" +15 "salvar arquivo" +16 "editar conteúdo de impressão" +17 "menu localizar" +18 "localizar por ..." +19 "localizar" +20 "menu dicionário" +21 "usar 'spell'" +22 "usar 'ispell'" +23 "menu diversos" +24 "formatação de parágrafo" +25 "comando shell" +26 "checar dicionário" +27 "menu principal" +28 "deixar editor" +29 "ajuda" +30 "operações com arquivos" +31 "redesenhar tela" +32 "configurações" +33 "localizar" +34 "diversos" +35 "Teclas de controle: " +36 "^a código ascii ^i tab ^r direita " +37 "^b botão de texto ^j nova linha ^t início do texto " +38 "^c comando ^k deletar caracter ^u para acima " +39 "^d para baixo ^l esquerda ^v restaurar palavra " +40 "^e localizar prompt ^m nova linha ^w deletar palavra " +41 "^f restaurar caracter ^n próxima página ^x localizar " +42 "^g início da linha ^o fim da linha ^y deletar linha " +43 "^h backspace ^p página anterior ^z restaurar linha " +44 "^[ (escape) menu " +45 " " +46 "Comandos: " +47 "help : obter esta informação file : imprimir nome do arquivo " +48 "read : ler um arquivo char : código ascii de caracter " +49 "write : escrever a arquivo case : localizar com case sensitive " +50 "exit : salva e sair nocase : localizar sem case insensitive " +51 "quit : sair, sem salvar !cmd : executar \"cmd\" no shell " +52 "line : visualizar linhas # 0-9 : ir para linha \"#\" " +53 "expand : expandir tabs noexpand: não expande tabs " +54 " " +55 " ee [+#] [-i] [-e] [-h] [arquivos(s)] " +56 "+# :ir para linha # -i :sem informação da janela -e : não expandir tabs -h :sem destaque " +57 "^[ (escape) menu ^e prompt localizar ^y deletar linha ^u para cima ^p página anterior " +58 "^a código ascii ^x localizar ^z restaurar linha ^d para baixo ^n próxima página " +59 "^b botão de texto ^g início da linha ^w deletar palavra ^l esquerda " +60 "^t início do texto ^o fim da linha ^v restaurar palavra ^r direita " +61 "^c comando ^k deletar caracter ^f restaurar caracter " +62 "help : obter ajuda |file : imprimir nome do arquivo |line : imprimir linha # " +63 "read : ler um arquivo |char : código ascii de caracter |0-9 : ir para linha \"#\"" +64 "write: escrever um arquivo |case : localizar com case sensitive |exit : salvar e sair " +65 "!cmd : shell \"cmd\" |nocase: ignorar case na busca |quit : sair, sem salvar" +66 "expand: expandir tabs |noexpand: não expandir tabs " +67 " pressione Escape (^[) para menu" +68 "sem arquivo" +69 "código ascii: " +70 "enviar conteúdo do buffer para \"%s\" " +71 "comando: " +72 "nome do arquivo para escrever: " +73 "nome do arquivo para leitura: " +74 "caracter = %d" +75 "comando desconhecido \"%s\"" +76 "mais de um comando digitado" +77 "linha %d " +78 "comprimento = %d" +79 "arquivo atual \"%s\" " +80 "uso: %s [-i] [-e] [-h] [+número_da_linha] [arquivo(s)]\n" +81 " -i desligar informações da janela\n" +82 " -e não converter tabs para espaços\n" +83 " -h não usar destaque\n" +84 "arquivo \"%s\" é um diretório" +85 "novo arquivo \"%s\"" +86 "não posso abrir \"%s\"" +87 "arquivos \"%s\", %d linhas" +88 "finalizar leitura de arquivo \"%s\"" +89 "lendo arquivo \"%s\"" +90 ", somente leitura" +91 "arquivo \"%s\", %d linhas" +92 "digite o nome do arquivo: " +93 "nenhum arquivo digitado: arquivo não foi salvo" +94 "Foram feitas alterações, você tem certeza? (y/n [n]) " +95 "y" +96 "arquivo já existe, sobrescrever? (y/n) [n] " +97 "impossível criar arquivo \"%s\"" +98 "escrevendo arquivo \"%s\"" +99 "\"%s\" %d linhas, %d caracteres" +100 " ...localizando" +101 "string \"%s\" não encontrada" +102 "localizar por: " +103 "execução não permitida %s\n" +104 "pressione enter para continuar " +105 "pressione Esc para cancelar" +106 "menu muito grande para a janela" +107 "pressione qualquer tecla para continuar " +108 "comando shell: " +109 "...formatando parágrafo..." +110 "<!echo 'lista de palavras não reconhecidas'; echo -=-=-=-=-=-" +111 "enviando conteúdo no buffer do editor para 'correção'" +112 "margem direita é: " +113 "modo restrito: incapaz de executar a operação solicitada" +114 "LIGADO" +115 "DESLIGADO" +116 "AJUDA" +117 "ESCRITA" +118 "LEITURA" +119 "LINHA" +120 "ARQUIVO" +121 "CARACTER" +122 "REDESENHAR" +123 "RESEQUENCIA" +124 "AUTOR" +125 "VERSÃO" +126 "CASE" +127 "SEMCASE" +128 "EXPANDIR" +129 "NÃOEXPANDIR" +130 "SAIRSALVANDO" +131 "SAIRSEMSALVAR" +132 "INFO" +133 "SEMINFO" +134 "MARGENS" +135 "SEMMARGENS" +136 "AUTOFORMATAÇÃO" +137 "SEMAUTOFORMATAÇÃO" +138 "ECHO" +139 "IMPRIMIRCOMANDO" +140 "MARGEMDIREITA" +141 "DESTAQUE" +142 "SEMDESTAQUE" +143 "OITOBIT" +144 "SEMOITOBIT" +145 "vínculo com teclas emacs " +146 "^a início da linha ^i tab ^r restaurar palavra " +147 "^b voltar 1 caracter ^j resturar caracter ^t início do texto " +148 "^c comando ^k deletar linha ^u botão de texto " +149 "^d deletar caracter ^l restaurar linha ^v próxima página " +150 "^e fim da linha ^m nova linha ^w deletar palavra " +151 "^f avançar 1 caracter ^n próxima linha ^x localizar " +152 "^g voltar 1 página ^o inserir caracter ascii ^y prompt localizar " +153 "^h backspace ^p linha anterior ^z próxima palavra " +154 "^[ (escape) menu ^y prompt localizar ^k deletar linha ^p linha anterior ^g página anterior" +155 "^o código ascii ^x localizar ^l restaurar linha ^n próxima linha ^v próxima página " +156 "^u fim do arquivo ^a início da linha ^w deletar palavra ^b regressar 1 caracter " +157 "^t início do texto ^e fim da linha ^r restaurar palavra ^f avançar 1 caracter " +158 "^c comando ^d deletar caracter ^j restaurar caracter ^z próxima palavra " +159 "EMACS" +160 "SEMEMACS" +161 " +# colocar cursor na linha #\n" +162 "impossível abrir .init.ee para escrita, nenhuma configuração salva!" +163 "ee configuração salva no arquivo %s" +164 "salvar configurações do editor" +165 "salvar configurações do ee" +166 "salvar no diretório corrente" +167 "salvar no diretório home" +168 "configurações do ee não salvas" +169 "ao invocar ree, deve-se especificar um arquivo" +180 "menu muito grande para a janela" +181 "^^mais^^" +182 "VVmaisVV" +183 "caracteres de 16 bit " +184 "16BIT" +185 "SEM16BIT" diff --git a/usr.bin/elf2aout/Makefile b/usr.bin/elf2aout/Makefile index 4ba44e7..2959539 100644 --- a/usr.bin/elf2aout/Makefile +++ b/usr.bin/elf2aout/Makefile @@ -3,6 +3,5 @@ PROG= elf2aout NO_WERROR= -WARNS?= 5 .include <bsd.prog.mk> diff --git a/usr.bin/elf2aout/elf2aout.1 b/usr.bin/elf2aout/elf2aout.1 index 7b153e9..0f4be22 100644 --- a/usr.bin/elf2aout/elf2aout.1 +++ b/usr.bin/elf2aout/elf2aout.1 @@ -32,7 +32,7 @@ .Nd "Convert ELF binary to a.out format" .Sh SYNOPSIS .Nm -.Op Fl o outfile +.Op Fl o Ar outfile .Ar infile .Sh DESCRIPTION The diff --git a/usr.bin/elf2aout/elf2aout.c b/usr.bin/elf2aout/elf2aout.c index 4168f4e..7e1ece6 100644 --- a/usr.bin/elf2aout/elf2aout.c +++ b/usr.bin/elf2aout/elf2aout.c @@ -35,6 +35,8 @@ __FBSDID("$FreeBSD$"); #include <err.h> #include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> #include <string.h> #include <unistd.h> @@ -155,5 +157,6 @@ static void usage(void) { - errx(1, "usage: elf2aout [-o outfile] infile"); + fprintf(stderr, "usage: elf2aout [-o outfile] infile\n"); + exit(1); } diff --git a/usr.bin/elfdump/Makefile b/usr.bin/elfdump/Makefile index 95ce514..22e1ca9 100644 --- a/usr.bin/elfdump/Makefile +++ b/usr.bin/elfdump/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= elfdump -WARNS?= 5 .include <bsd.prog.mk> diff --git a/usr.bin/elfdump/elfdump.1 b/usr.bin/elfdump/elfdump.1 index 1902e41..5818297 100644 --- a/usr.bin/elfdump/elfdump.1 +++ b/usr.bin/elfdump/elfdump.1 @@ -89,7 +89,7 @@ command: .Rs .%A "AT&T Unix Systems Labs" .%T "System V Application Binary Interface" -.%O http://www.sco.com/developers/gabi/ +.%U http://www.sco.com/developers/gabi/ .Re .Sh HISTORY The diff --git a/usr.bin/env/Makefile b/usr.bin/env/Makefile index bc1eea4..89ab594 100644 --- a/usr.bin/env/Makefile +++ b/usr.bin/env/Makefile @@ -3,6 +3,5 @@ PROG= env SRCS= env.c envopts.c -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/fetch/Makefile b/usr.bin/fetch/Makefile index c422af8..6f0db80 100644 --- a/usr.bin/fetch/Makefile +++ b/usr.bin/fetch/Makefile @@ -4,9 +4,8 @@ PROG= fetch CSTD?= c99 -WARNS?= 6 -DPADD= ${LIBFETCH} -LDADD= -lfetch +DPADD= ${LIBFETCH} ${LIBMD} +LDADD= -lfetch -lmd .if ${MK_OPENSSL} != "no" DPADD+= ${LIBSSL} ${LIBCRYPTO} LDADD+= -lssl -lcrypto diff --git a/usr.bin/fetch/fetch.c b/usr.bin/fetch/fetch.c index 2512a2e..7553bd8 100644 --- a/usr.bin/fetch/fetch.c +++ b/usr.bin/fetch/fetch.c @@ -340,6 +340,11 @@ fetch(char *URL, const char *path) fetchDebug = 1; /* parse URL */ + url = NULL; + if (*URL == '\0') { + warnx("empty URL"); + goto failure; + } if ((url = fetchParseURL(URL)) == NULL) { warnx("%s: parse error", URL); goto failure; diff --git a/usr.bin/file2c/Makefile b/usr.bin/file2c/Makefile index c3c6ddc..82b7e97 100644 --- a/usr.bin/file2c/Makefile +++ b/usr.bin/file2c/Makefile @@ -1,5 +1,4 @@ # $FreeBSD$ PROG= file2c -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/find/Makefile b/usr.bin/find/Makefile index b20dc03..0c7bb70 100644 --- a/usr.bin/find/Makefile +++ b/usr.bin/find/Makefile @@ -4,8 +4,6 @@ PROG= find SRCS= find.c function.c ls.c main.c misc.c operator.c option.c \ getdate.y -WARNS?=6 -CFLAGS+= -DHAVE_SYS_TIMEB_H -I${.CURDIR} YFLAGS= .include <bsd.prog.mk> diff --git a/usr.bin/find/extern.h b/usr.bin/find/extern.h index 716c1f5..cc6143c 100644 --- a/usr.bin/find/extern.h +++ b/usr.bin/find/extern.h @@ -43,8 +43,7 @@ PLAN *find_formplan(char **); PLAN *not_squish(PLAN *); PLAN *or_squish(PLAN *); PLAN *paren_squish(PLAN *); -struct timeb; -time_t get_date(char *, struct timeb *); +time_t get_date(char *); struct stat; void printlong(char *, char *, struct stat *); int queryuser(char **); diff --git a/usr.bin/find/function.c b/usr.bin/find/function.c index c11d24f..1714627 100644 --- a/usr.bin/find/function.c +++ b/usr.bin/find/function.c @@ -50,7 +50,6 @@ __FBSDID("$FreeBSD$"); #include <sys/acl.h> #include <sys/wait.h> #include <sys/mount.h> -#include <sys/timeb.h> #include <dirent.h> #include <err.h> @@ -770,7 +769,7 @@ done: *argvp = argv + 1; /* Finish any pending -exec ... {} + functions. */ void -finish_execplus() +finish_execplus(void) { PLAN *p; @@ -1155,7 +1154,7 @@ c_newer(OPTION *option, char ***argvp) new = palloc(option); /* compare against what */ if (option->flags & F_TIME2_T) { - new->t_data = get_date(fn_or_tspec, (struct timeb *) 0); + new->t_data = get_date(fn_or_tspec); if (new->t_data == (time_t) -1) errx(1, "Can't parse date/time: %s", fn_or_tspec); } else { @@ -1165,6 +1164,8 @@ c_newer(OPTION *option, char ***argvp) new->t_data = sb.st_ctime; else if (option->flags & F_TIME2_A) new->t_data = sb.st_atime; + else if (option->flags & F_TIME2_B) + new->t_data = sb.st_birthtime; else new->t_data = sb.st_mtime; } diff --git a/usr.bin/find/getdate.y b/usr.bin/find/getdate.y index de7750c..81a9c47 100644 --- a/usr.bin/find/getdate.y +++ b/usr.bin/find/getdate.y @@ -28,7 +28,6 @@ __FBSDID("$FreeBSD$"); #else /* defined(vms) */ # include <sys/types.h> # include <sys/time.h> -# include <sys/timeb.h> #endif /* !defined(vms) */ #if defined (__STDC__) || defined (USG) @@ -69,7 +68,7 @@ static int yyparse(void); static int yylex(void); static int yyerror(const char *); -time_t get_date(char *, struct timeb *); +time_t get_date(char *); #define EPOCH 1970 #define HOUR(x) ((time_t)(x) * 60) @@ -849,58 +848,50 @@ difftm (struct tm *a, struct tm *b) } time_t -get_date(char *p, struct timeb *now) +get_date(char *p) { - struct tm *tm, gmt; - struct timeb ftz; + struct tm *tm, *gmt_ptr, gmt; + int tzoff; time_t Start; time_t tod; time_t nowtime; bzero (&gmt, sizeof(struct tm)); yyInput = p; - if (now == NULL) { - struct tm *gmt_ptr; - now = &ftz; - (void)time (&nowtime); + (void)time (&nowtime); - gmt_ptr = gmtime (&nowtime); - if (gmt_ptr != NULL) - { - /* Make a copy, in case localtime modifies *tm (I think - that comment now applies to *gmt_ptr, but I am too - lazy to dig into how gmtime and locatime allocate the - structures they return pointers to). */ - gmt = *gmt_ptr; - } + gmt_ptr = gmtime (&nowtime); + if (gmt_ptr != NULL) + { + /* Make a copy, in case localtime modifies *tm (I think + that comment now applies to *gmt_ptr, but I am too + lazy to dig into how gmtime and locatime allocate the + structures they return pointers to). */ + gmt = *gmt_ptr; + } - if (! (tm = localtime (&nowtime))) - return -1; + if (! (tm = localtime (&nowtime))) + return -1; - if (gmt_ptr != NULL) - ftz.timezone = difftm (&gmt, tm) / 60; - else - /* We are on a system like VMS, where the system clock is - in local time and the system has no concept of timezones. - Hopefully we can fake this out (for the case in which the - user specifies no timezone) by just saying the timezone - is zero. */ - ftz.timezone = 0; - - if(tm->tm_isdst) - ftz.timezone += 60; - } + if (gmt_ptr != NULL) + tzoff = difftm (&gmt, tm) / 60; else - { - nowtime = now->time; - } + /* We are on a system like VMS, where the system clock is + in local time and the system has no concept of timezones. + Hopefully we can fake this out (for the case in which the + user specifies no timezone) by just saying the timezone + is zero. */ + tzoff = 0; + + if(tm->tm_isdst) + tzoff += 60; tm = localtime(&nowtime); yyYear = tm->tm_year + 1900; yyMonth = tm->tm_mon + 1; yyDay = tm->tm_mday; - yyTimezone = now->timezone; + yyTimezone = tzoff; yyDSTmode = DSTmaybe; yyHour = 0; yyMinutes = 0; @@ -956,7 +947,7 @@ main(int ac, char *av[]) (void)printf("Enter date, or blank line to exit.\n\t> "); (void)fflush(stdout); while (gets(buff) && buff[0]) { - d = get_date(buff, (struct timeb *)NULL); + d = get_date(buff); if (d == -1) (void)printf("Bad format - couldn't convert.\n"); else diff --git a/usr.bin/finger/Makefile b/usr.bin/finger/Makefile index 983392b..30e04df 100644 --- a/usr.bin/finger/Makefile +++ b/usr.bin/finger/Makefile @@ -5,4 +5,6 @@ PROG= finger SRCS= finger.c lprint.c net.c sprint.c util.c MAN= finger.1 finger.conf.5 +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/finger/extern.h b/usr.bin/finger/extern.h index 498c296..0014b74 100644 --- a/usr.bin/finger/extern.h +++ b/usr.bin/finger/extern.h @@ -51,8 +51,8 @@ extern int invoker_root; /* Invoked by root */ void enter_lastlog(PERSON *); PERSON *enter_person(struct passwd *); -void enter_where(struct utmp *, PERSON *); -PERSON *find_person(const char *); +void enter_where(struct utmpx *, PERSON *); +PERSON *find_person(char *); int hide(struct passwd *); void lflag_print(void); int match(struct passwd *, const char *); diff --git a/usr.bin/finger/finger.1 b/usr.bin/finger/finger.1 index 590a382..c643c18 100644 --- a/usr.bin/finger/finger.1 +++ b/usr.bin/finger/finger.1 @@ -100,8 +100,7 @@ of the remote host when used in conjunction with the .Fl h option. .It Fl k -Disable all use of -.Xr utmp 5 . +Disable all use of the user accounting database. .It Fl l Produce a multi-line format displaying all of the information described for the @@ -221,10 +220,10 @@ This variable may be set with favored options to .Nm . .El .Sh FILES -.Bl -tag -width /var/log/lastlog -compact +.Bl -tag -width /var/log/utx.lastlogin -compact .It Pa /etc/finger.conf alias definition data base -.It Pa /var/log/lastlog +.It Pa /var/log/utx.lastlogin last login data base .El .Sh SEE ALSO diff --git a/usr.bin/finger/finger.c b/usr.bin/finger/finger.c index b278752..ae2766f 100644 --- a/usr.bin/finger/finger.c +++ b/usr.bin/finger/finger.c @@ -83,7 +83,7 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include <time.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include <locale.h> #include "finger.h" @@ -233,29 +233,26 @@ loginlist(void) PERSON *pn; DBT data, key; struct passwd *pw; - struct utmp user; + struct utmpx *user; int r, sflag1; - char name[UT_NAMESIZE + 1]; if (kflag) errx(1, "can't list logins without reading utmp"); - if (!freopen(_PATH_UTMP, "r", stdin)) - err(1, "%s", _PATH_UTMP); - name[UT_NAMESIZE] = '\0'; - while (fread((char *)&user, sizeof(user), 1, stdin) == 1) { - if (!user.ut_name[0]) + setutxent(); + while ((user = getutxent()) != NULL) { + if (user->ut_type != USER_PROCESS) continue; - if ((pn = find_person(user.ut_name)) == NULL) { - bcopy(user.ut_name, name, UT_NAMESIZE); - if ((pw = getpwnam(name)) == NULL) + if ((pn = find_person(user->ut_user)) == NULL) { + if ((pw = getpwnam(user->ut_user)) == NULL) continue; if (hide(pw)) continue; pn = enter_person(pw); } - enter_where(&user, pn); + enter_where(user, pn); } + endutxent(); if (db && lflag) for (sflag1 = R_FIRST;; sflag1 = R_NEXT) { PERSON *tmp; @@ -275,7 +272,7 @@ userlist(int argc, char **argv) { PERSON *pn; DBT data, key; - struct utmp user; + struct utmpx *user; struct passwd *pw; int r, sflag1, *used, *ip; char **ap, **nargv, **np, **p; @@ -373,6 +370,7 @@ net: for (p = nargv; *p;) { printf("\n"); } + free(used); if (entries == 0) return; @@ -383,15 +381,15 @@ net: for (p = nargv; *p;) { * Scan thru the list of users currently logged in, saving * appropriate data whenever a match occurs. */ - if (!freopen(_PATH_UTMP, "r", stdin)) - err(1, "%s", _PATH_UTMP); - while (fread((char *)&user, sizeof(user), 1, stdin) == 1) { - if (!user.ut_name[0]) + setutxent(); + while ((user = getutxent()) != NULL) { + if (user->ut_type != USER_PROCESS) continue; - if ((pn = find_person(user.ut_name)) == NULL) + if ((pn = find_person(user->ut_user)) == NULL) continue; - enter_where(&user, pn); + enter_where(user, pn); } + endutxent(); if (db) for (sflag1 = R_FIRST;; sflag1 = R_NEXT) { PERSON *tmp; diff --git a/usr.bin/finger/finger.h b/usr.bin/finger/finger.h index 6b18146..72a5554 100644 --- a/usr.bin/finger/finger.h +++ b/usr.bin/finger/finger.h @@ -62,8 +62,8 @@ typedef struct where { short writable; /* tty is writable */ time_t loginat; /* time of (last) login */ time_t idletime; /* how long idle (if logged in) */ - char tty[UT_LINESIZE+1]; /* null terminated tty line */ - char host[UT_HOSTSIZE+1]; /* null terminated remote host name */ + char tty[sizeof ((struct utmpx *)0)->ut_line]; /* tty line */ + char host[sizeof ((struct utmpx *)0)->ut_host]; /* host name */ } WHERE; #define UNPRIV_NAME "nobody" /* Preferred privilege level */ diff --git a/usr.bin/finger/lprint.c b/usr.bin/finger/lprint.c index 848bbee..628aab6 100644 --- a/usr.bin/finger/lprint.c +++ b/usr.bin/finger/lprint.c @@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <string.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include "finger.h" #include "pathnames.h" diff --git a/usr.bin/finger/net.c b/usr.bin/finger/net.c index 5260a2d..14a69c9 100644 --- a/usr.bin/finger/net.c +++ b/usr.bin/finger/net.c @@ -55,7 +55,7 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include "finger.h" static void cleanup(int sig); diff --git a/usr.bin/finger/sprint.c b/usr.bin/finger/sprint.c index e4f768b..d4091a8 100644 --- a/usr.bin/finger/sprint.c +++ b/usr.bin/finger/sprint.c @@ -43,6 +43,7 @@ static char sccsid[] = "@(#)sprint.c 8.3 (Berkeley) 4/28/95"; #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); +#include <sys/param.h> #include <sys/types.h> #include <sys/socket.h> #include <db.h> @@ -52,7 +53,7 @@ __FBSDID("$FreeBSD$"); #include <stdio.h> #include <string.h> #include <time.h> -#include <utmp.h> +#include <utmpx.h> #include "finger.h" static void stimeprint(WHERE *); @@ -88,7 +89,7 @@ sflag_print(void) */ #define MAXREALNAME 16 #define MAXHOSTNAME 17 /* in reality, hosts are never longer than 16 */ - (void)printf("%-*s %-*s%s %s\n", UT_NAMESIZE, "Login", MAXREALNAME, + (void)printf("%-*s %-*s%s %s\n", MAXLOGNAME, "Login", MAXREALNAME, "Name", " TTY Idle Login Time ", (gflag) ? "" : oflag ? "Office Phone" : "Where"); @@ -105,7 +106,7 @@ sflag_print(void) namelen = MAXREALNAME; if (w->info == LOGGEDIN && !w->writable) --namelen; /* leave space before `*' */ - (void)printf("%-*.*s %-*.*s", UT_NAMESIZE, UT_NAMESIZE, + (void)printf("%-*.*s %-*.*s", MAXLOGNAME, MAXLOGNAME, pn->name, MAXREALNAME, namelen, pn->realname ? pn->realname : ""); if (!w->loginat) { diff --git a/usr.bin/finger/util.c b/usr.bin/finger/util.c index a84c711..8e3812b 100644 --- a/usr.bin/finger/util.c +++ b/usr.bin/finger/util.c @@ -57,7 +57,7 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include "finger.h" #include "pathnames.h" @@ -109,29 +109,18 @@ void enter_lastlog(PERSON *pn) { WHERE *w; - static int opened, fd; - struct lastlog ll; + struct utmpx *ut = NULL; char doit = 0; - /* some systems may not maintain lastlog, don't report errors. */ - if (!opened) { - fd = open(_PATH_LASTLOG, O_RDONLY, 0); - opened = 1; - } - if (fd == -1 || - lseek(fd, (long)pn->uid * sizeof(ll), SEEK_SET) != - (long)pn->uid * sizeof(ll) || - read(fd, (char *)&ll, sizeof(ll)) != sizeof(ll)) { - /* as if never logged in */ - ll.ll_line[0] = ll.ll_host[0] = '\0'; - ll.ll_time = 0; - } + if (setutxdb(UTXDB_LASTLOGIN, NULL) == 0) + ut = getutxuser(pn->name); if ((w = pn->whead) == NULL) doit = 1; - else if (ll.ll_time != 0) { + else if (ut != NULL && ut->ut_type == USER_PROCESS) { /* if last login is earlier than some current login */ for (; !doit && w != NULL; w = w->next) - if (w->info == LOGGEDIN && w->loginat < ll.ll_time) + if (w->info == LOGGEDIN && + w->loginat < ut->ut_tv.tv_sec) doit = 1; /* * and if it's not any of the current logins @@ -140,32 +129,29 @@ enter_lastlog(PERSON *pn) */ for (w = pn->whead; doit && w != NULL; w = w->next) if (w->info == LOGGEDIN && - strncmp(w->tty, ll.ll_line, UT_LINESIZE) == 0) + strcmp(w->tty, ut->ut_line) == 0) doit = 0; } - if (doit) { + if (ut != NULL && doit) { w = walloc(pn); w->info = LASTLOG; - bcopy(ll.ll_line, w->tty, UT_LINESIZE); - w->tty[UT_LINESIZE] = 0; - bcopy(ll.ll_host, w->host, UT_HOSTSIZE); - w->host[UT_HOSTSIZE] = 0; - w->loginat = ll.ll_time; + strcpy(w->tty, ut->ut_line); + strcpy(w->host, ut->ut_host); + w->loginat = ut->ut_tv.tv_sec; } + endutxent(); } void -enter_where(struct utmp *ut, PERSON *pn) +enter_where(struct utmpx *ut, PERSON *pn) { WHERE *w; w = walloc(pn); w->info = LOGGEDIN; - bcopy(ut->ut_line, w->tty, UT_LINESIZE); - w->tty[UT_LINESIZE] = 0; - bcopy(ut->ut_host, w->host, UT_HOSTSIZE); - w->host[UT_HOSTSIZE] = 0; - w->loginat = (time_t)ut->ut_time; + strcpy(w->tty, ut->ut_line); + strcpy(w->host, ut->ut_host); + w->loginat = ut->ut_tv.tv_sec; find_idle_and_ttywrite(w); } @@ -205,14 +191,12 @@ enter_person(struct passwd *pw) } PERSON * -find_person(const char *name) +find_person(char *name) { struct passwd *pw; - int cnt; DBT data, key; PERSON *p; - char buf[UT_NAMESIZE + 1]; if (!db) return(NULL); @@ -220,12 +204,8 @@ find_person(const char *name) if ((pw = getpwnam(name)) && hide(pw)) return(NULL); - /* Name may be only UT_NAMESIZE long and not NUL terminated. */ - for (cnt = 0; cnt < UT_NAMESIZE && *name; ++name, ++cnt) - buf[cnt] = *name; - buf[cnt] = '\0'; - key.data = buf; - key.size = cnt; + key.data = name; + key.size = strlen(name); if ((*db->get)(db, &key, &data, 0)) return (NULL); diff --git a/usr.bin/fstat/Makefile b/usr.bin/fstat/Makefile index d55fce4..23e907b 100644 --- a/usr.bin/fstat/Makefile +++ b/usr.bin/fstat/Makefile @@ -9,7 +9,6 @@ DPADD= ${LIBKVM} LDADD= -lkvm BINGRP= kmem BINMODE=2555 -WARNS?= 6 CFLAGS+=-D_KVM_VNODE diff --git a/usr.bin/fsync/Makefile b/usr.bin/fsync/Makefile index 42aa6dd..c9bc524 100644 --- a/usr.bin/fsync/Makefile +++ b/usr.bin/fsync/Makefile @@ -2,5 +2,4 @@ PROG= fsync -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/ftp/Makefile b/usr.bin/ftp/Makefile index c2fe59c..6f42e69 100644 --- a/usr.bin/ftp/Makefile +++ b/usr.bin/ftp/Makefile @@ -23,6 +23,8 @@ CFLAGS+= -I${.CURDIR} -I${LUKEMFTP} LDADD= -ledit -ltermcap -lutil DPADD= ${LIBEDIT} ${LIBTERMCAP} ${LIBUTIL} +WARNS?= 2 + LINKS= ${BINDIR}/ftp ${BINDIR}/pftp \ ${BINDIR}/ftp ${BINDIR}/gate-ftp MLINKS= ftp.1 pftp.1 \ diff --git a/usr.bin/gcore/Makefile b/usr.bin/gcore/Makefile index 5ab51cf..e83a48f 100644 --- a/usr.bin/gcore/Makefile +++ b/usr.bin/gcore/Makefile @@ -3,5 +3,9 @@ PROG= gcore SRCS= elfcore.c gcore.c +DPADD= ${LIBUTIL} +LDADD= -lutil + +WARNS?= 1 .include <bsd.prog.mk> diff --git a/usr.bin/gcore/elfcore.c b/usr.bin/gcore/elfcore.c index 6c90708..73d92b4 100644 --- a/usr.bin/gcore/elfcore.c +++ b/usr.bin/gcore/elfcore.c @@ -1,4 +1,5 @@ /*- + * Copyright (c) 2007 Sandvine Incorporated * Copyright (c) 1998 John D. Polstra * All rights reserved. * @@ -29,8 +30,12 @@ __FBSDID("$FreeBSD$"); #include <sys/param.h> #include <sys/procfs.h> +#include <sys/ptrace.h> #include <sys/queue.h> #include <sys/linker_set.h> +#include <sys/sysctl.h> +#include <sys/user.h> +#include <sys/wait.h> #include <machine/elf.h> #include <vm/vm_param.h> #include <vm/vm.h> @@ -44,6 +49,7 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <libutil.h> #include "extern.h" @@ -69,16 +75,15 @@ static void cb_put_phdr(vm_map_entry_t, void *); static void cb_size_segment(vm_map_entry_t, void *); static void each_writable_segment(vm_map_entry_t, segment_callback, void *closure); -static void elf_corehdr(int fd, pid_t, vm_map_entry_t, int numsegs, - void *hdr, size_t hdrsize); -static void elf_puthdr(vm_map_entry_t, void *, size_t *, - const prstatus_t *, const prfpregset_t *, const prpsinfo_t *, int numsegs); +static void elf_detach(void); /* atexit() handler. */ +static void elf_puthdr(pid_t, vm_map_entry_t, void *, size_t *, int numsegs); static void elf_putnote(void *dst, size_t *off, const char *name, int type, const void *desc, size_t descsz); static void freemap(vm_map_entry_t); -static void readhdrinfo(pid_t, prstatus_t *, prfpregset_t *, prpsinfo_t *); static vm_map_entry_t readmap(pid_t); +static pid_t g_pid; /* Pid being dumped, global for elf_detach */ + static int elf_ident(int efd, pid_t pid __unused, char *binfile __unused) { @@ -93,6 +98,14 @@ elf_ident(int efd, pid_t pid __unused, char *binfile __unused) return (0); } +static void +elf_detach(void) +{ + + if (g_pid != 0) + ptrace(PT_DETACH, g_pid, (caddr_t)1, 0); +} + /* * Write an ELF coredump for the given pid to the given fd. */ @@ -103,11 +116,20 @@ elf_coredump(int efd __unused, int fd, pid_t pid) struct sseg_closure seginfo; void *hdr; size_t hdrsize; - char memname[64]; - int memfd; Elf_Phdr *php; int i; + /* Attach to process to dump. */ + g_pid = pid; + if (atexit(elf_detach) != 0) + err(1, "atexit"); + errno = 0; + ptrace(PT_ATTACH, pid, NULL, 0); + if (errno) + err(1, "PT_ATTACH"); + if (waitpid(pid, NULL, 0) == -1) + err(1, "waitpid"); + /* Get the program's memory map. */ map = readmap(pid); @@ -122,28 +144,31 @@ elf_coredump(int efd __unused, int fd, pid_t pid) * size is calculated. */ hdrsize = 0; - elf_puthdr(map, (void *)NULL, &hdrsize, - (const prstatus_t *)NULL, (const prfpregset_t *)NULL, - (const prpsinfo_t *)NULL, seginfo.count); + elf_puthdr(pid, map, NULL, &hdrsize, seginfo.count); /* * Allocate memory for building the header, fill it up, * and write it out. */ - if ((hdr = malloc(hdrsize)) == NULL) + if ((hdr = calloc(1, hdrsize)) == NULL) errx(1, "out of memory"); - elf_corehdr(fd, pid, map, seginfo.count, hdr, hdrsize); - /* Write the contents of all of the writable segments. */ - snprintf(memname, sizeof memname, "/proc/%d/mem", pid); - if ((memfd = open(memname, O_RDONLY)) == -1) - err(1, "cannot open %s", memname); + /* Fill in the header. */ + hdrsize = 0; + elf_puthdr(pid, map, hdr, &hdrsize, seginfo.count); + + /* Write it to the core file. */ + if (write(fd, hdr, hdrsize) == -1) + err(1, "write"); + /* Write the contents of all of the writable segments. */ php = (Elf_Phdr *)((char *)hdr + sizeof(Elf_Ehdr)) + 1; for (i = 0; i < seginfo.count; i++) { + struct ptrace_io_desc iorequest; uintmax_t nleft = php->p_filesz; - lseek(memfd, (off_t)php->p_vaddr, SEEK_SET); + iorequest.piod_op = PIOD_READ_D; + iorequest.piod_offs = (caddr_t)php->p_vaddr; while (nleft > 0) { char buf[8*1024]; size_t nwant; @@ -153,12 +178,12 @@ elf_coredump(int efd __unused, int fd, pid_t pid) nwant = sizeof buf; else nwant = nleft; - ngot = read(memfd, buf, nwant); - if (ngot == -1) - err(1, "read from %s", memname); + iorequest.piod_addr = buf; + iorequest.piod_len = nwant; + ptrace(PT_IO, pid, (caddr_t)&iorequest, 0); + ngot = iorequest.piod_len; if ((size_t)ngot < nwant) - errx(1, "short read from %s:" - " wanted %zu, got %zd", memname, + errx(1, "short read wanted %d, got %d", nwant, ngot); ngot = write(fd, buf, nwant); if (ngot == -1) @@ -166,10 +191,10 @@ elf_coredump(int efd __unused, int fd, pid_t pid) if ((size_t)ngot != nwant) errx(1, "short write"); nleft -= nwant; + iorequest.piod_offs += ngot; } php++; } - close(memfd); free(hdr); freemap(map); } @@ -231,30 +256,25 @@ each_writable_segment(vm_map_entry_t map, segment_callback func, void *closure) (*func)(entry, closure); } -/* - * Write the core file header to the file, including padding up to - * the page boundary. - */ static void -elf_corehdr(int fd, pid_t pid, vm_map_entry_t map, int numsegs, void *hdr, - size_t hdrsize) +elf_getstatus(pid_t pid, prpsinfo_t *psinfo) { - size_t off; - prstatus_t status; - prfpregset_t fpregset; - prpsinfo_t psinfo; - - /* Gather the information for the header. */ - readhdrinfo(pid, &status, &fpregset, &psinfo); - - /* Fill in the header. */ - memset(hdr, 0, hdrsize); - off = 0; - elf_puthdr(map, hdr, &off, &status, &fpregset, &psinfo, numsegs); - - /* Write it to the core file. */ - if (write(fd, hdr, hdrsize) == -1) - err(1, "write"); + struct kinfo_proc kobj; + int name[4]; + size_t len; + + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PID; + name[3] = pid; + + len = sizeof(kobj); + if (sysctl(name, 4, &kobj, &len, NULL, 0) == -1) + err(1, "error accessing kern.proc.pid.%u sysctl", pid); + if (kobj.ki_pid != pid) + err(1, "error accessing kern.proc.pid.%u sysctl datas", pid); + strncpy(psinfo->pr_fname, kobj.ki_comm, MAXCOMLEN); + strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ); } /* @@ -262,13 +282,24 @@ elf_corehdr(int fd, pid_t pid, vm_map_entry_t map, int numsegs, void *hdr, * be NULL, in which case the header is sized but not actually generated. */ static void -elf_puthdr(vm_map_entry_t map, void *dst, size_t *off, const prstatus_t *status, - const prfpregset_t *fpregset, const prpsinfo_t *psinfo, int numsegs) +elf_puthdr(pid_t pid, vm_map_entry_t map, void *dst, size_t *off, int numsegs) { + struct { + prstatus_t status; + prfpregset_t fpregset; + prpsinfo_t psinfo; + } *tempdata; size_t ehoff; size_t phoff; size_t noteoff; size_t notesz; + size_t threads; + lwpid_t *tids; + int i; + + prstatus_t *status; + prfpregset_t *fpregset; + prpsinfo_t *psinfo; ehoff = *off; *off += sizeof(Elf_Ehdr); @@ -277,14 +308,68 @@ elf_puthdr(vm_map_entry_t map, void *dst, size_t *off, const prstatus_t *status, *off += (numsegs + 1) * sizeof(Elf_Phdr); noteoff = *off; - elf_putnote(dst, off, "FreeBSD", NT_PRSTATUS, status, - sizeof *status); - elf_putnote(dst, off, "FreeBSD", NT_FPREGSET, fpregset, - sizeof *fpregset); + + if (dst != NULL) { + if ((tempdata = calloc(1, sizeof(*tempdata))) == NULL) + errx(1, "out of memory"); + status = &tempdata->status; + fpregset = &tempdata->fpregset; + psinfo = &tempdata->psinfo; + } else { + tempdata = NULL; + status = NULL; + fpregset = NULL; + psinfo = NULL; + } + + errno = 0; + threads = ptrace(PT_GETNUMLWPS, pid, NULL, 0); + if (errno) + err(1, "PT_GETNUMLWPS"); + + if (dst != NULL) { + psinfo->pr_version = PRPSINFO_VERSION; + psinfo->pr_psinfosz = sizeof(prpsinfo_t); + elf_getstatus(pid, psinfo); + + } elf_putnote(dst, off, "FreeBSD", NT_PRPSINFO, psinfo, sizeof *psinfo); + + if (dst != NULL) { + tids = malloc(threads * sizeof(*tids)); + if (tids == NULL) + errx(1, "out of memory"); + errno = 0; + ptrace(PT_GETLWPLIST, pid, (void *)tids, threads); + if (errno) + err(1, "PT_GETLWPLIST"); + } + for (i = 0; i < threads; ++i) { + if (dst != NULL) { + status->pr_version = PRSTATUS_VERSION; + status->pr_statussz = sizeof(prstatus_t); + status->pr_gregsetsz = sizeof(gregset_t); + status->pr_fpregsetsz = sizeof(fpregset_t); + status->pr_osreldate = __FreeBSD_version; + status->pr_pid = tids[i]; + + ptrace(PT_GETREGS, tids[i], (void *)&status->pr_reg, 0); + ptrace(PT_GETFPREGS, tids[i], (void *)fpregset, 0); + } + elf_putnote(dst, off, "FreeBSD", NT_PRSTATUS, status, + sizeof *status); + elf_putnote(dst, off, "FreeBSD", NT_FPREGSET, fpregset, + sizeof *fpregset); + } + notesz = *off - noteoff; + if (dst != NULL) { + free(tids); + free(tempdata); + } + /* Align up to a page boundary for the program segments. */ *off = round_page(*off); @@ -381,70 +466,7 @@ freemap(vm_map_entry_t map) } /* - * Read the process information necessary to fill in the core file's header. - */ -static void -readhdrinfo(pid_t pid, prstatus_t *status, prfpregset_t *fpregset, - prpsinfo_t *psinfo) -{ - char name[64]; - char line[256]; - int fd; - int i; - int n; - - memset(status, 0, sizeof *status); - status->pr_version = PRSTATUS_VERSION; - status->pr_statussz = sizeof(prstatus_t); - status->pr_gregsetsz = sizeof(gregset_t); - status->pr_fpregsetsz = sizeof(fpregset_t); - status->pr_osreldate = __FreeBSD_version; - status->pr_pid = pid; - - memset(fpregset, 0, sizeof *fpregset); - - memset(psinfo, 0, sizeof *psinfo); - psinfo->pr_version = PRPSINFO_VERSION; - psinfo->pr_psinfosz = sizeof(prpsinfo_t); - - /* Read the general registers. */ - snprintf(name, sizeof name, "/proc/%d/regs", pid); - if ((fd = open(name, O_RDONLY)) == -1) - err(1, "cannot open %s", name); - if ((n = read(fd, &status->pr_reg, sizeof status->pr_reg)) == -1) - err(1, "read error from %s", name); - if ((size_t)n < sizeof(status->pr_reg)) - errx(1, "short read from %s: wanted %zu, got %d", name, - sizeof status->pr_reg, n); - close(fd); - - /* Read the floating point registers. */ - snprintf(name, sizeof name, "/proc/%d/fpregs", pid); - if ((fd = open(name, O_RDONLY)) == -1) - err(1, "cannot open %s", name); - if ((n = read(fd, fpregset, sizeof *fpregset)) == -1) - err(1, "read error from %s", name); - if ((size_t)n < sizeof(*fpregset)) - errx(1, "short read from %s: wanted %zu, got %d", name, - sizeof *fpregset, n); - close(fd); - - /* Read and parse the process status. */ - snprintf(name, sizeof name, "/proc/%d/status", pid); - if ((fd = open(name, O_RDONLY)) == -1) - err(1, "cannot open %s", name); - if ((n = read(fd, line, sizeof line - 1)) == -1) - err(1, "read error from %s", name); - if (n > MAXCOMLEN) - n = MAXCOMLEN; - for (i = 0; i < n && line[i] != ' '; i++) - psinfo->pr_fname[i] = line[i]; - strncpy(psinfo->pr_psargs, psinfo->pr_fname, PRARGSZ); - close(fd); -} - -/* - * Read the process's memory map using procfs, and return a list of + * Read the process's memory map using kinfo_getvmmap(), and return a list of * VM map entries. Only the non-device read/writable segments are * returned. The map entries in the list aren't fully filled in; only * the items we need are present. @@ -452,83 +474,44 @@ readhdrinfo(pid_t pid, prstatus_t *status, prfpregset_t *fpregset, static vm_map_entry_t readmap(pid_t pid) { - char mapname[64]; - int mapfd; - ssize_t mapsize; - size_t bufsize; - char *mapbuf; - int pos; - vm_map_entry_t map; - vm_map_entry_t *linkp; + vm_map_entry_t ent, *linkp, map; + struct kinfo_vmentry *vmentl, *kve; + int i, nitems; - snprintf(mapname, sizeof mapname, "/proc/%d/map", pid); - if ((mapfd = open(mapname, O_RDONLY)) == -1) - err(1, "cannot open %s", mapname); + vmentl = kinfo_getvmmap(pid, &nitems); + if (vmentl == NULL) + err(1, "cannot retrieve mappings for %u process", pid); - /* - * Procfs requires (for consistency) that the entire memory map - * be read with a single read() call. Start with a reasonably sized - * buffer, and double it until it is big enough. - */ - bufsize = 8 * 1024; - mapbuf = NULL; - for ( ; ; ) { - if ((mapbuf = realloc(mapbuf, bufsize + 1)) == NULL) - errx(1, "out of memory"); - mapsize = read(mapfd, mapbuf, bufsize); - if (mapsize != -1 || errno != EFBIG) - break; - bufsize *= 2; - /* This lseek shouldn't be necessary, but it is. */ - lseek(mapfd, (off_t)0, SEEK_SET); - } - if (mapsize == -1) - err(1, "read error from %s", mapname); - if (mapsize == 0) - errx(1, "empty map file %s", mapname); - mapbuf[mapsize] = 0; - close(mapfd); - - pos = 0; map = NULL; linkp = ↦ - while (pos < mapsize) { - vm_map_entry_t ent; - u_long start; - u_long end; - char prot[4]; - char type[16]; - int n; - int len; - - len = 0; - n = sscanf(mapbuf + pos, "%lx %lx %*d %*d %*x %3[-rwx]" - " %*d %*d %*x %*s %*s %16s %*s%*[\n]%n", - &start, &end, prot, type, &len); - if (n != 4 || len == 0) - errx(1, "ill-formed line in %s starting at character %d", mapname, pos + 1); - pos += len; - - /* Ignore segments of the wrong kind, and unwritable ones */ - if (strncmp(prot, "rw", 2) != 0 || - (strcmp(type, "default") != 0 && - strcmp(type, "vnode") != 0 && - strcmp(type, "swap") != 0)) + for (i = 0; i < nitems; i++) { + kve = &vmentl[i]; + + /* + * Ignore segments of the wrong kind and ones which are not + * readable and writable. + */ + if ((kve->kve_protection & KVME_PROT_WRITE) == 0 || + (kve->kve_protection & KVME_PROT_READ) == 0 || + (kve->kve_type != KVME_TYPE_DEFAULT && + kve->kve_type != KVME_TYPE_VNODE && + kve->kve_type != KVME_TYPE_SWAP)) continue; - if ((ent = (vm_map_entry_t)calloc(1, sizeof *ent)) == NULL) + ent = calloc(1, sizeof(*ent)); + if (ent == NULL) errx(1, "out of memory"); - ent->start = start; - ent->end = end; + ent->start = (vm_offset_t)kve->kve_start; + ent->end = (vm_offset_t)kve->kve_end; ent->protection = VM_PROT_READ | VM_PROT_WRITE; - if (prot[2] == 'x') - ent->protection |= VM_PROT_EXECUTE; + if ((kve->kve_protection & KVME_PROT_EXEC) != 0) + ent->protection |= VM_PROT_EXECUTE; *linkp = ent; linkp = &ent->next; } - free(mapbuf); - return map; + free(vmentl); + return (map); } struct dumpers elfdump = { elf_ident, elf_coredump }; diff --git a/usr.bin/gcore/extern.h b/usr.bin/gcore/extern.h index 1bd7095..68e05a78 100644 --- a/usr.bin/gcore/extern.h +++ b/usr.bin/gcore/extern.h @@ -38,3 +38,4 @@ struct dumpers { int (*ident)(int efd, pid_t pid, char *binfile); void (*dump)(int efd, int fd, pid_t pid); }; +extern int sflag; diff --git a/usr.bin/gcore/gcore.1 b/usr.bin/gcore/gcore.1 index 981e0be..a5be26a 100644 --- a/usr.bin/gcore/gcore.1 +++ b/usr.bin/gcore/gcore.1 @@ -32,7 +32,7 @@ .\" @(#)gcore.1 8.2 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd April 18, 1994 +.Dd November 18, 2009 .Dt GCORE 1 .Os .Sh NAME @@ -55,11 +55,6 @@ By default, the core is written to the file The process identifier, .Ar pid , must be given on the command line. -If no executable image is -specified, -.Nm -will use -.Dq Pa /proc/<pid>/file . .Pp The following options are available: .Bl -tag -width indent @@ -80,8 +75,6 @@ The same effect can be achieved manually with .Bl -tag -width /var/log/messages -compact .It Pa core.<pid> the core image -.It Pa /proc/<pid>/file -the executable image .El .Sh HISTORY A @@ -89,12 +82,15 @@ A utility appeared in .Bx 4.2 . .Sh BUGS -Context switches or paging activity that occur while +Because of the +.Xr ptrace 2 +usage .Nm -is running may cause the program to become confused. -For best results, use -.Fl s -to temporarily stop the target process. +may not work with processes which are actively investigated with +.Xr truss 1 +or +.Xr gdb 1 . +Additionally, interruptable sleeps may exit with EINTR. .Pp The .Nm diff --git a/usr.bin/gcore/gcore.c b/usr.bin/gcore/gcore.c index 7005e83..b02a4a9 100644 --- a/usr.bin/gcore/gcore.c +++ b/usr.bin/gcore/gcore.c @@ -61,19 +61,19 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/stat.h> #include <sys/linker_set.h> +#include <sys/sysctl.h> #include <err.h> #include <fcntl.h> -#include <signal.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include "extern.h" +int sflag; static void killed(int); -static void restart_target(void); static void usage(void) __dead2; static pid_t pid; @@ -83,10 +83,11 @@ SET_DECLARE(dumpset, struct dumpers); int main(int argc, char *argv[]) { - int ch, efd, fd, sflag; + int ch, efd, fd, name[4]; char *binfile, *corefile; - char fname[MAXPATHLEN]; + char passpath[MAXPATHLEN], fname[MAXPATHLEN]; struct dumpers **d, *dumper; + size_t len; sflag = 0; corefile = NULL; @@ -109,9 +110,14 @@ main(int argc, char *argv[]) switch (argc) { case 1: pid = atoi(argv[0]); - asprintf(&binfile, "/proc/%d/file", pid); - if (binfile == NULL) - errx(1, "allocation failure"); + name[0] = CTL_KERN; + name[1] = KERN_PROC; + name[2] = KERN_PROC_PATHNAME; + name[3] = pid; + len = sizeof(passpath); + if (sysctl(name, 4, passpath, &len, NULL, 0) == -1) + errx(1, "kern.proc.pathname failure"); + binfile = passpath; break; case 2: pid = atoi(argv[1]); @@ -141,36 +147,28 @@ main(int argc, char *argv[]) fd = open(corefile, O_RDWR|O_CREAT|O_TRUNC, DEFFILEMODE); if (fd < 0) err(1, "%s", corefile); - if (sflag) { - signal(SIGHUP, killed); - signal(SIGINT, killed); - signal(SIGTERM, killed); - if (kill(pid, SIGSTOP) == -1) - err(1, "%d: stop signal", pid); - atexit(restart_target); - } + /* + * The semantics of the 's' flag is to stop the target process. + * Previous versions of gcore would manage this by trapping SIGHUP, + * SIGINT and SIGTERM (to be passed to the target pid), and then + * signal the child to stop. + * + * However, this messes up if the selected dumper uses ptrace calls + * that leave the child already stopped. The waitpid call in elfcore + * never returns. + * + * The best thing to do here is to externalize the 's' flag and let + * each dumper dispose of what that means, if anything. For the elfcore + * dumper, the 's' flag is a no-op since the ptrace attach stops the + * process in question already. + */ + dumper->dump(efd, fd, pid); (void)close(fd); (void)close(efd); exit(0); } -static void -killed(int sig) -{ - - restart_target(); - signal(sig, SIG_DFL); - kill(getpid(), sig); -} - -static void -restart_target(void) -{ - - kill(pid, SIGCONT); -} - void usage(void) { diff --git a/usr.bin/gencat/Makefile b/usr.bin/gencat/Makefile index a6c9991..2cf86df 100644 --- a/usr.bin/gencat/Makefile +++ b/usr.bin/gencat/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= gencat -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/gencat/gencat.c b/usr.bin/gencat/gencat.c index 306f48d..2ac5828 100644 --- a/usr.bin/gencat/gencat.c +++ b/usr.bin/gencat/gencat.c @@ -18,13 +18,6 @@ * 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 NetBSD - * Foundation, Inc. and its contributors. - * 4. 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 @@ -94,13 +87,13 @@ __FBSDID("$FreeBSD$"); struct _msgT { long msgId; char *str; - LIST_ENTRY(_msgT) entries; + LIST_ENTRY(_msgT) entries; }; struct _setT { long setId; - LIST_HEAD(msghead, _msgT) msghead; - LIST_ENTRY(_setT) entries; + LIST_HEAD(msghead, _msgT) msghead; + LIST_ENTRY(_setT) entries; }; LIST_HEAD(sethead, _setT) sethead; @@ -130,17 +123,17 @@ void usage(void); int main(int, char **); void -usage() +usage(void) { fprintf(stderr, "usage: %s catfile msgfile ...\n", getprogname()); - exit(1); + exit(1); } int main(int argc, char **argv) { int ofd, ifd; - char *catfile = NULL; + char *catfile = NULL; int c; #define DEPRECATEDMSG 1 @@ -419,23 +412,23 @@ MCParse(int fd) cptr += 5; if (!*cptr) quote = 0; - else { + else { cptr = wskip(cptr); if (!*cptr) quote = 0; else quote = *cptr; - } + } } else if (isspace((unsigned char) *cptr)) { ; - } else { + } else { if (*cptr) { cptr = wskip(cptr); if (*cptr) warning(cptr, "unrecognized line"); } - } - } else { + } + } else { /* * First check for (and eat) empty lines.... */ @@ -453,7 +446,7 @@ MCParse(int fd) } else { warning(cptr, "neither blank line nor start of a message id"); continue; - } + } /* * If we have a message ID, but no message, * then this means "delete this message id @@ -461,12 +454,12 @@ MCParse(int fd) */ if (!*cptr) { MCDelMsg(msgid); - } else { + } else { str = getmsg(fd, cptr, quote); MCAddMsg(msgid, str); - } + } + } } - } } void @@ -686,7 +679,7 @@ MCAddSet(int setId) if (p && p->setId == setId) { ; - } else { + } else { p = xmalloc(sizeof(struct _setT)); memset(p, '\0', sizeof(struct _setT)); LIST_INIT(&p->msghead); @@ -697,8 +690,8 @@ MCAddSet(int setId) LIST_INSERT_HEAD(&sethead, p, entries); } else { LIST_INSERT_AFTER(q, p, entries); - } -} + } + } curSet = p; } @@ -718,7 +711,7 @@ MCAddMsg(int msgId, const char *str) if (msgId > NL_MSGMAX) { error("msgID exceeds limit"); /* NOTREACHED */ - } + } p = curSet->msghead.lh_first; q = NULL; @@ -739,7 +732,7 @@ MCAddMsg(int msgId, const char *str) p->msgId = msgId; p->str = xstrdup(str); - } +} void MCDelSet(int setId) @@ -756,13 +749,13 @@ MCDelSet(int setId) while (msg) { free(msg->str); LIST_REMOVE(msg, entries); - } + } LIST_REMOVE(set, entries); return; - } - warning(NULL, "specified set doesn't exist"); } + warning(NULL, "specified set doesn't exist"); +} void MCDelMsg(int msgId) @@ -779,6 +772,6 @@ MCDelMsg(int msgId) free(msg->str); LIST_REMOVE(msg, entries); return; - } + } warning(NULL, "specified msg doesn't exist"); } diff --git a/usr.bin/getent/Makefile b/usr.bin/getent/Makefile index 6cc3d56..85bc1b1 100644 --- a/usr.bin/getent/Makefile +++ b/usr.bin/getent/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= getent -WARNS?= 3 .include <bsd.prog.mk> diff --git a/usr.bin/getent/getent.1 b/usr.bin/getent/getent.1 index a18d341..a14bdfa 100644 --- a/usr.bin/getent/getent.1 +++ b/usr.bin/getent/getent.1 @@ -14,13 +14,6 @@ .\" 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 NetBSD -.\" Foundation, Inc. and its contributors. -.\" 4. 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 @@ -75,6 +68,7 @@ argument may be one of: .It Li rpc Ta name number [alias ...] .It Li services Ta name port/protocol [alias ...] .It Li shells Ta /path/to/shell +.It Li utmpx Ta [time] type: properties .El .Pp If one or more @@ -109,6 +103,7 @@ utility exits 0 on success, or 3 if there is no support for enumeration on .Ar database . .Sh SEE ALSO +.Xr getutxent 3 , .Xr ethers 5 , .Xr group 5 , .Xr hosts 5 , diff --git a/usr.bin/getent/getent.c b/usr.bin/getent/getent.c index d755813..e958665 100644 --- a/usr.bin/getent/getent.c +++ b/usr.bin/getent/getent.c @@ -15,13 +15,6 @@ * 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 NetBSD - * Foundation, Inc. and its contributors. - * 4. 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 @@ -55,11 +48,13 @@ __FBSDID("$FreeBSD$"); #include <limits.h> #include <netdb.h> #include <pwd.h> -#include <stdio.h> #include <stdarg.h> +#include <stdint.h> +#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <utmpx.h> static int usage(void); static int parsenum(const char *, unsigned long *); @@ -72,6 +67,7 @@ static int protocols(int, char *[]); static int rpc(int, char *[]); static int services(int, char *[]); static int shells(int, char *[]); +static int utmpx(int, char *[]); enum { RV_OK = 0, @@ -93,6 +89,7 @@ static struct getentdb { { "rpc", rpc, }, { "services", services, }, { "shells", shells, }, + { "utmpx", utmpx, }, { NULL, NULL, }, }; @@ -562,3 +559,91 @@ shells(int argc, char *argv[]) endusershell(); return rv; } + +/* + * utmpx + */ + +#define UTMPXPRINTID do { \ + size_t i; \ + for (i = 0; i < sizeof ut->ut_id; i++) \ + printf("%02hhx", ut->ut_id[i]); \ +} while (0) + +static void +utmpxprint(const struct utmpx *ut) +{ + + if (ut->ut_type == EMPTY) + return; + + printf("[%jd.%06u -- %.24s] ", + (intmax_t)ut->ut_tv.tv_sec, (unsigned int)ut->ut_tv.tv_usec, + ctime(&ut->ut_tv.tv_sec)); + + switch (ut->ut_type) { + case BOOT_TIME: + printf("system boot\n"); + return; + case SHUTDOWN_TIME: + printf("system shutdown\n"); + return; + case OLD_TIME: + printf("old system time\n"); + return; + case NEW_TIME: + printf("new system time\n"); + return; + case USER_PROCESS: + printf("user process: id=\""); + UTMPXPRINTID; + printf("\" pid=\"%d\" user=\"%s\" line=\"%s\" host=\"%s\"\n", + ut->ut_pid, ut->ut_user, ut->ut_line, ut->ut_host); + break; + case DEAD_PROCESS: + printf("dead process: id=\""); + UTMPXPRINTID; + printf("\" pid=\"%d\"\n", ut->ut_pid); + break; + default: + printf("unknown record type\n"); + break; + } +} + +static int +utmpx(int argc, char *argv[]) +{ + const struct utmpx *ut; + int rv = RV_OK, db; + + assert(argc > 1); + assert(argv != NULL); + + if (argc == 2) { + db = UTXDB_ACTIVE; + } else if (argc == 3) { + if (strcmp(argv[2], "active") == 0) + db = UTXDB_ACTIVE; + else if (strcmp(argv[2], "lastlogin") == 0) + db = UTXDB_LASTLOGIN; + else if (strcmp(argv[2], "log") == 0) + db = UTXDB_LOG; + else + rv = RV_USAGE; + } else { + rv = RV_USAGE; + } + + if (rv == RV_USAGE) { + fprintf(stderr, "Usage: %s utmpx [active | lastlogin | log]\n", + getprogname()); + } else if (rv == RV_OK) { + if (setutxdb(db, NULL) != 0) + return (RV_NOTFOUND); + while ((ut = getutxent()) != NULL) + utmpxprint(ut); + endutxent(); + } + return (rv); +} diff --git a/usr.bin/gprof/Makefile b/usr.bin/gprof/Makefile index a24d9cd..5062325 100644 --- a/usr.bin/gprof/Makefile +++ b/usr.bin/gprof/Makefile @@ -7,4 +7,6 @@ SRCS= gprof.c aout.c arcs.c dfn.c elf.c lookup.c hertz.c \ FILES= gprof.flat gprof.callg FILESDIR= ${SHAREDIR}/misc +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/gprof/aout.c b/usr.bin/gprof/aout.c index 2ccf818..9103148 100644 --- a/usr.bin/gprof/aout.c +++ b/usr.bin/gprof/aout.c @@ -45,6 +45,7 @@ __FBSDID("$FreeBSD$"); #include <a.out.h> #include <err.h> +#include <string.h> #include "gprof.h" diff --git a/usr.bin/gzip/Makefile b/usr.bin/gzip/Makefile index dae5f84..0480337 100644 --- a/usr.bin/gzip/Makefile +++ b/usr.bin/gzip/Makefile @@ -8,7 +8,6 @@ MAN= gzip.1 gzexe.1 zdiff.1 zforce.1 zmore.1 znew.1 DPADD= ${LIBZ} LDADD= -lz -WARNS?= 6 .if ${MK_BZIP2_SUPPORT} != "no" DPADD+= ${LIBBZ2} diff --git a/usr.bin/gzip/unbzip2.c b/usr.bin/gzip/unbzip2.c index e8990d7..c744e56 100644 --- a/usr.bin/gzip/unbzip2.c +++ b/usr.bin/gzip/unbzip2.c @@ -1,4 +1,4 @@ -/* $NetBSD: unbzip2.c,v 1.11 2008/04/28 20:24:13 martin Exp $ */ +/* $NetBSD: unbzip2.c,v 1.12 2009/10/11 05:17:20 mrg Exp $ */ /*- * Copyright (c) 2006 The NetBSD Foundation, Inc. @@ -36,7 +36,7 @@ static off_t unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in) { - int ret, end_of_file; + int ret, end_of_file, cold = 0; off_t bytes_out = 0; bz_stream bzs; static char *inbuf, *outbuf; @@ -64,7 +64,7 @@ unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in) if (bytes_in) *bytes_in = prelen; - while (ret >= BZ_OK && ret != BZ_STREAM_END) { + while (ret == BZ_OK) { if (bzs.avail_in == 0 && !end_of_file) { ssize_t n; @@ -86,9 +86,19 @@ unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in) switch (ret) { case BZ_STREAM_END: case BZ_OK: - if (ret == BZ_OK && end_of_file) - maybe_err("read"); - if (!tflag) { + if (ret == BZ_OK && end_of_file) { + /* + * If we hit this after a stream end, consider + * it as the end of the whole file and don't + * bail out. + */ + if (cold == 1) + ret = BZ_STREAM_END; + else + maybe_errx("truncated file"); + } + cold = 0; + if (!tflag && bzs.avail_out != BUFLEN) { ssize_t n; n = write(out, outbuf, BUFLEN - bzs.avail_out); @@ -96,7 +106,14 @@ unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in) maybe_err("write"); bytes_out += n; } - break; + if (ret == BZ_STREAM_END && !end_of_file) { + if (BZ2_bzDecompressEnd(&bzs) != BZ_OK || + BZ2_bzDecompressInit(&bzs, 0, 0) != BZ_OK) + maybe_errx("bzip2 re-init"); + cold = 1; + ret = BZ_OK; + } + break; case BZ_DATA_ERROR: maybe_warnx("bzip2 data integrity error"); @@ -109,7 +126,10 @@ unbzip2(int in, int out, char *pre, size_t prelen, off_t *bytes_in) case BZ_MEM_ERROR: maybe_warnx("bzip2 out of memory"); break; - + + default: + maybe_warnx("unknown bzip2 error: %d", ret); + break; } } diff --git a/usr.bin/head/Makefile b/usr.bin/head/Makefile index 002b3f3..60b0a2f 100644 --- a/usr.bin/head/Makefile +++ b/usr.bin/head/Makefile @@ -2,6 +2,5 @@ # $FreeBSD$ PROG= head -WARNS= 6 .include <bsd.prog.mk> diff --git a/usr.bin/hexdump/Makefile b/usr.bin/hexdump/Makefile index 3252fc9..4cd3cc5 100644 --- a/usr.bin/hexdump/Makefile +++ b/usr.bin/hexdump/Makefile @@ -7,6 +7,5 @@ MAN= hexdump.1 od.1 MLINKS= hexdump.1 hd.1 LINKS= ${BINDIR}/hexdump ${BINDIR}/od LINKS+= ${BINDIR}/hexdump ${BINDIR}/hd -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/hexdump/hexdump.1 b/usr.bin/hexdump/hexdump.1 index 4d8c2da..baf9d46 100644 --- a/usr.bin/hexdump/hexdump.1 +++ b/usr.bin/hexdump/hexdump.1 @@ -32,7 +32,7 @@ .\" @(#)hexdump.1 8.2 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd July 10, 2004 +.Dd February 18, 2010 .Dt HEXDUMP 1 .Os .Sh NAME @@ -258,7 +258,7 @@ strings. .It "\&00C\ FF\t00D\ CR\t00E\ SO\t00F\ SI\t010\ DLE\t011\ DC1 .It "\&012\ DC2\t013\ DC3\t014\ DC4\t015\ NAK\t016\ SYN\t017\ ETB .It "\&018\ CAN\t019\ EM\t01A\ SUB\t01B\ ESC\t01C\ FS\t01D\ GS -.It "\&01E\ RS\t01F\ US\t0FF\ DEL +.It "\&01E\ RS\t01F\ US\t07F\ DEL .El .El .Pp diff --git a/usr.bin/hexdump/od.1 b/usr.bin/hexdump/od.1 index 9bf7967..dd67ff5 100644 --- a/usr.bin/hexdump/od.1 +++ b/usr.bin/hexdump/od.1 @@ -32,7 +32,7 @@ .\" @(#)od.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd December 24, 2006 +.Dd February 18, 2010 .Os .Dt OD 1 .Sh NAME @@ -155,7 +155,7 @@ Control characters are displayed using the following names: .It "00c FF 00d CR 00e SO 00f SI 010 DLE 011 DC1" .It "012 DC2 013 DC3 014 DC4 015 NAK 016 SYN 017 ETB" .It "018 CAN 019 EM 01a SUB 01b ESC 01c FS 01d GS" -.It "01e RS 01f US 020 SP 0ff DEL" +.It "01e RS 01f US 020 SP 07f DEL" .El .It Cm c Characters in the default character set. diff --git a/usr.bin/host/Makefile b/usr.bin/host/Makefile index 9861914..e2909e1 100644 --- a/usr.bin/host/Makefile +++ b/usr.bin/host/Makefile @@ -15,6 +15,8 @@ SRCS+= dighost.c host.c CFLAGS+= -I${SRCDIR}/include CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include +WARNS?= 1 + DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} diff --git a/usr.bin/id/Makefile b/usr.bin/id/Makefile index 96c162f..7d78b50 100644 --- a/usr.bin/id/Makefile +++ b/usr.bin/id/Makefile @@ -4,7 +4,6 @@ .include <bsd.own.mk> PROG= id -WARNS?= 6 LINKS= ${BINDIR}/id ${BINDIR}/groups LINKS+= ${BINDIR}/id ${BINDIR}/whoami MAN= id.1 groups.1 whoami.1 diff --git a/usr.bin/ipcrm/Makefile b/usr.bin/ipcrm/Makefile index 47e4ccc..f55311c 100644 --- a/usr.bin/ipcrm/Makefile +++ b/usr.bin/ipcrm/Makefile @@ -7,4 +7,6 @@ LDADD= -lkvm CFLAGS+=-I${.CURDIR}/../ipcs .PATH: ${.CURDIR}/../ipcs +WARNS?= 0 + .include <bsd.prog.mk> diff --git a/usr.bin/ipcs/Makefile b/usr.bin/ipcs/Makefile index 8ee1d43..0d422e1 100644 --- a/usr.bin/ipcs/Makefile +++ b/usr.bin/ipcs/Makefile @@ -5,4 +5,6 @@ SRCS= ipcs.c ipc.c DPADD= ${LIBKVM} LDADD= -lkvm +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/jot/jot.1 b/usr.bin/jot/jot.1 index 77cebb4..3e198a34 100644 --- a/usr.bin/jot/jot.1 +++ b/usr.bin/jot/jot.1 @@ -32,7 +32,7 @@ .\" @(#)jot.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd November 6, 2006 +.Dd February 19, 2010 .Dt JOT 1 .Os .Sh NAME @@ -239,6 +239,7 @@ but only one is allowed. .Xr ed 1 , .Xr expand 1 , .Xr rs 1 , +.Xr seq 1 , .Xr yes 1 , .Xr arc4random 3 , .Xr printf 3 , diff --git a/usr.bin/kdump/Makefile b/usr.bin/kdump/Makefile index 2ca8561..832b2dc 100644 --- a/usr.bin/kdump/Makefile +++ b/usr.bin/kdump/Makefile @@ -7,6 +7,8 @@ PROG= kdump SRCS= kdump.c ioctl.c kdump_subr.c subr.c CFLAGS+= -I${.CURDIR}/../ktrace -I${.CURDIR} -I${.CURDIR}/../.. +WARNS?= 0 + CLEANFILES= ioctl.c kdump_subr.c ioctl.c: mkioctls diff --git a/usr.bin/kdump/kdump.c b/usr.bin/kdump/kdump.c index 3d8b93a..9c86fff 100644 --- a/usr.bin/kdump/kdump.c +++ b/usr.bin/kdump/kdump.c @@ -182,14 +182,16 @@ main(int argc, char *argv[]) if (ktr_header.ktr_type & KTR_DROP) { ktr_header.ktr_type &= ~KTR_DROP; if (!drop_logged && threads) { - (void)printf("%6d %6d %-8.*s Events dropped.\n", - ktr_header.ktr_pid, ktr_header.ktr_tid > - 0 ? ktr_header.ktr_tid : 0, MAXCOMLEN, - ktr_header.ktr_comm); + (void)printf( + "%6jd %6jd %-8.*s Events dropped.\n", + (intmax_t)ktr_header.ktr_pid, + ktr_header.ktr_tid > 0 ? + (intmax_t)ktr_header.ktr_tid : 0, + MAXCOMLEN, ktr_header.ktr_comm); drop_logged = 1; } else if (!drop_logged) { - (void)printf("%6d %-8.*s Events dropped.\n", - ktr_header.ktr_pid, MAXCOMLEN, + (void)printf("%6jd %-8.*s Events dropped.\n", + (intmax_t)ktr_header.ktr_pid, MAXCOMLEN, ktr_header.ktr_comm); drop_logged = 1; } @@ -309,10 +311,11 @@ dumpheader(struct ktr_header *kth) * negative tid's as 0. */ if (threads) - (void)printf("%6d %6d %-8.*s ", kth->ktr_pid, kth->ktr_tid > - 0 ? kth->ktr_tid : 0, MAXCOMLEN, kth->ktr_comm); + (void)printf("%6jd %6jd %-8.*s ", (intmax_t)kth->ktr_pid, + kth->ktr_tid > 0 ? (intmax_t)kth->ktr_tid : 0, + MAXCOMLEN, kth->ktr_comm); else - (void)printf("%6d %-8.*s ", kth->ktr_pid, MAXCOMLEN, + (void)printf("%6jd %-8.*s ", (intmax_t)kth->ktr_pid, MAXCOMLEN, kth->ktr_comm); if (timestamp) { if (timestamp == 3) { @@ -325,8 +328,8 @@ dumpheader(struct ktr_header *kth) timevalsub(&kth->ktr_time, &prevtime); prevtime = temp; } - (void)printf("%ld.%06ld ", - kth->ktr_time.tv_sec, kth->ktr_time.tv_usec); + (void)printf("%jd.%06ld ", (intmax_t)kth->ktr_time.tv_sec, + kth->ktr_time.tv_usec); } (void)printf("%s ", type); } @@ -799,7 +802,7 @@ ktrsyscall(struct ktr_syscall *ktr) narg--; } } - while (narg) { + while (narg > 0) { print_number(ip,narg,c); } (void)putchar(')'); @@ -821,7 +824,7 @@ ktrsysret(struct ktr_sysret *ktr) if (error == 0) { if (fancy) { - (void)printf("%d", ret); + (void)printf("%ld", (long)ret); if (ret < 0 || ret > 9) (void)printf("/%#lx", (long)ret); } else { @@ -1121,17 +1124,14 @@ ktruser_malloc(int len, unsigned char *p) { struct utrace_malloc *ut = (struct utrace_malloc *)p; - if (ut->p == NULL) { - if (ut->s == 0 && ut->r == NULL) - printf("malloc_init()\n"); - else - printf("%p = malloc(%zu)\n", ut->r, ut->s); - } else { - if (ut->s == 0) - printf("free(%p)\n", ut->p); - else - printf("%p = realloc(%p, %zu)\n", ut->r, ut->p, ut->s); - } + if (ut->p == (void *)(intptr_t)(-1)) + printf("malloc_init()\n"); + else if (ut->s == 0) + printf("free(%p)\n", ut->p); + else if (ut->p == NULL) + printf("%p = malloc(%zu)\n", ut->r, ut->s); + else + printf("%p = realloc(%p, %zu)\n", ut->r, ut->p, ut->s); } void @@ -1270,7 +1270,7 @@ ktrstat(struct stat *statp) printf("rdev=%ju, ", (uintmax_t)statp->st_rdev); printf("atime="); if (resolv == 0) - printf("%ld", statp->st_atimespec.tv_sec); + printf("%jd", (intmax_t)statp->st_atimespec.tv_sec); else { tm = localtime(&statp->st_atimespec.tv_sec); (void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); @@ -1282,7 +1282,7 @@ ktrstat(struct stat *statp) printf(", "); printf("stime="); if (resolv == 0) - printf("%ld", statp->st_mtimespec.tv_sec); + printf("%jd", (intmax_t)statp->st_mtimespec.tv_sec); else { tm = localtime(&statp->st_mtimespec.tv_sec); (void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); @@ -1294,7 +1294,7 @@ ktrstat(struct stat *statp) printf(", "); printf("ctime="); if (resolv == 0) - printf("%ld", statp->st_ctimespec.tv_sec); + printf("%jd", (intmax_t)statp->st_ctimespec.tv_sec); else { tm = localtime(&statp->st_ctimespec.tv_sec); (void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); @@ -1306,7 +1306,7 @@ ktrstat(struct stat *statp) printf(", "); printf("birthtime="); if (resolv == 0) - printf("%ld", statp->st_birthtimespec.tv_sec); + printf("%jd", (intmax_t)statp->st_birthtimespec.tv_sec); else { tm = localtime(&statp->st_birthtimespec.tv_sec); (void)strftime(timestr, sizeof(timestr), TIME_FORMAT, tm); @@ -1328,6 +1328,8 @@ ktrstruct(char *buf, size_t buflen) char *name, *data; size_t namelen, datalen; int i; + struct stat sb; + struct sockaddr_storage ss; for (name = buf, namelen = 0; namelen < buflen && name[namelen] != '\0'; @@ -1348,12 +1350,16 @@ ktrstruct(char *buf, size_t buflen) if (strcmp(name, "stat") == 0) { if (datalen != sizeof(struct stat)) goto invalid; - ktrstat((struct stat *)data); + memcpy(&sb, data, datalen); + ktrstat(&sb); } else if (strcmp(name, "sockaddr") == 0) { + if (datalen > sizeof(ss)) + goto invalid; + memcpy(&ss, data, datalen); if (datalen < sizeof(struct sockaddr) || - datalen != ((struct sockaddr *)(data))->sa_len) + datalen != ss.ss_len) goto invalid; - ktrsockaddr((struct sockaddr *)data); + ktrsockaddr((struct sockaddr *)&ss); } else { printf("unknown structure\n"); } diff --git a/usr.bin/keylogin/Makefile b/usr.bin/keylogin/Makefile index bf279eb..4c03051 100644 --- a/usr.bin/keylogin/Makefile +++ b/usr.bin/keylogin/Makefile @@ -5,4 +5,6 @@ PROG= keylogin DPADD= ${LIBRPCSVC} LDADD= -lrpcsvc +WARNS?= 0 + .include <bsd.prog.mk> diff --git a/usr.bin/killall/killall.1 b/usr.bin/killall/killall.1 index 632c066..19362bc 100644 --- a/usr.bin/killall/killall.1 +++ b/usr.bin/killall/killall.1 @@ -24,7 +24,7 @@ .\" .\" $FreeBSD$ .\" -.Dd May 27, 2009 +.Dd December 25, 2009 .Os .Dt KILLALL 1 .Sh NAME @@ -145,6 +145,17 @@ command appeared in It has been modeled after the .Nm command as available on other platforms. +.Sh IMPLEMENTATION NOTES +This +.Fx +implementation of +.Nm +has completely different semantics as compared to the traditional +.Ux +System V behavior of +.Nm . +The latter will kill all processes that the current user is able to +kill, and is intended to be used by the system shutdown process only. .Sh AUTHORS .An -nosplit The diff --git a/usr.bin/ktrace/Makefile b/usr.bin/ktrace/Makefile index c00bb75..2679923 100644 --- a/usr.bin/ktrace/Makefile +++ b/usr.bin/ktrace/Makefile @@ -5,4 +5,6 @@ PROG= ktrace SRCS= ktrace.c subr.c MLINKS= ktrace.1 trace.1 +WARNS?= 4 + .include <bsd.prog.mk> diff --git a/usr.bin/ktrdump/Makefile b/usr.bin/ktrdump/Makefile index 6b5ff12..c30dc3d 100644 --- a/usr.bin/ktrdump/Makefile +++ b/usr.bin/ktrdump/Makefile @@ -5,4 +5,6 @@ DPADD= ${LIBKVM} LDADD= -lkvm MAN= ktrdump.8 +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/lam/lam.c b/usr.bin/lam/lam.c index 333549f..0cb681a 100644 --- a/usr.bin/lam/lam.c +++ b/usr.bin/lam/lam.c @@ -221,7 +221,7 @@ gatherline(struct openfile *ip) } static void -usage() +usage(void) { fprintf(stderr, "%s\n%s\n", "usage: lam [ -f min.max ] [ -s sepstring ] [ -t c ] file ...", diff --git a/usr.bin/last/Makefile b/usr.bin/last/Makefile index f7e8811..0bd2f05 100644 --- a/usr.bin/last/Makefile +++ b/usr.bin/last/Makefile @@ -3,4 +3,6 @@ PROG= last +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/last/last.1 b/usr.bin/last/last.1 index f838c4a..223a2c6 100644 --- a/usr.bin/last/last.1 +++ b/usr.bin/last/last.1 @@ -83,7 +83,7 @@ be reported. This may be used with the .Fl f option to derive the results from stored -.Pa wtmp +.Pa utx.log files. When this argument is provided, all other options except for .Fl f @@ -141,7 +141,7 @@ letter pair is not specified, the value defaults to 0. Read the file .Ar file instead of the default, -.Pa /var/log/wtmp . +.Pa /var/log/utx.log . .It Fl h Ar host .Ar Host names may be names or internet numbers. @@ -196,13 +196,13 @@ If interrupted with a quit signal indicates how far the search has progressed and then continues. .Sh FILES -.Bl -tag -width /var/log/wtmp -compact -.It Pa /var/log/wtmp +.Bl -tag -width /var/log/utx.log -compact +.It Pa /var/log/utx.log login data base .El .Sh SEE ALSO .Xr lastcomm 1 , -.Xr utmp 5 , +.Xr getutxent 3 , .Xr ac 8 .Sh HISTORY A @@ -212,7 +212,7 @@ utility appeared in .Sh BUGS If a login shell should terminate abnormally for some reason, it is likely that a logout record will not be written to the -.Pa wtmp +.Pa utx.log file. In this case, .Nm diff --git a/usr.bin/last/last.c b/usr.bin/last/last.c index d9877b4..eb38b64 100644 --- a/usr.bin/last/last.c +++ b/usr.bin/last/last.c @@ -59,15 +59,13 @@ __FBSDID("$FreeBSD$"); #include <time.h> #include <timeconv.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include <sys/queue.h> #define NO 0 /* false/no */ #define YES 1 /* true/yes */ #define ATOI2(ar) ((ar)[0] - '0') * 10 + ((ar)[1] - '0'); (ar) += 2; -static struct utmp buf[1024]; /* utmp read buffer */ - typedef struct arg { char *name; /* argument */ #define HOST_TYPE -2 @@ -78,18 +76,18 @@ typedef struct arg { } ARG; ARG *arglist; /* head of linked list */ -LIST_HEAD(ttylisthead, ttytab) ttylist; +LIST_HEAD(idlisthead, idtab) idlist; -struct ttytab { +struct idtab { time_t logout; /* log out time */ - char tty[UT_LINESIZE + 1]; /* terminal name */ - LIST_ENTRY(ttytab) list; + char id[sizeof ((struct utmpx *)0)->ut_id]; /* identifier */ + LIST_ENTRY(idtab) list; }; static const char *crmsg; /* cause of last reboot */ -static long currentout, /* current logout value */ - maxrec; /* records to display */ -static const char *file = _PATH_WTMP; /* wtmp file */ +static time_t currentout; /* current logout value */ +static long maxrec; /* records to display */ +static const char *file = NULL; /* wtmp file */ static int sflag = 0; /* show delta in seconds */ static int width = 5; /* show seconds in delta */ static int yflag; /* show year */ @@ -102,12 +100,11 @@ static time_t snaptime; /* if != 0, we will only void addarg(int, char *); time_t dateconv(char *); -void doentry(struct utmp *); +void doentry(struct utmpx *); void hostconv(char *); -void onintr(int); -void printentry(struct utmp *, struct ttytab *); +void printentry(struct utmpx *, struct idtab *); char *ttyconv(char *); -int want(struct utmp *); +int want(struct utmpx *); void usage(void); void wtmp(void); @@ -206,33 +203,35 @@ main(int argc, char *argv[]) void wtmp(void) { - struct utmp *bp; /* current structure */ - struct stat stb; /* stat of file for size */ - long bl; - int bytes, wfd; + struct utmpx *buf = NULL; + struct utmpx *ut; + static unsigned int amount = 0; + time_t t; char ct[80]; struct tm *tm; - time_t t; - LIST_INIT(&ttylist); + LIST_INIT(&idlist); + (void)time(&t); - if ((wfd = open(file, O_RDONLY, 0)) < 0 || fstat(wfd, &stb) == -1) + /* Load the last entries from the file. */ + if (setutxdb(UTXDB_LOG, file) != 0) err(1, "%s", file); - bl = (stb.st_size + sizeof(buf) - 1) / sizeof(buf); - - (void)time(&t); - buf[0].ut_time = _time_to_int(t); - (void)signal(SIGINT, onintr); - (void)signal(SIGQUIT, onintr); - - while (--bl >= 0) { - if (lseek(wfd, (off_t)(bl * sizeof(buf)), L_SET) == -1 || - (bytes = read(wfd, buf, sizeof(buf))) == -1) - err(1, "%s", file); - for (bp = &buf[bytes / sizeof(buf[0]) - 1]; bp >= buf; --bp) - doentry(bp); + while ((ut = getutxent()) != NULL) { + if (amount % 128 == 0) { + buf = realloc(buf, (amount + 128) * sizeof *ut); + if (buf == NULL) + err(1, "realloc"); + } + memcpy(&buf[amount++], ut, sizeof *ut); + if (t > ut->ut_tv.tv_sec) + t = ut->ut_tv.tv_sec; } - t = _int_to_time(buf[0].ut_time); + endutxent(); + + /* Display them in reverse order. */ + while (amount > 0) + doentry(&buf[--amount]); + tm = localtime(&t); (void) strftime(ct, sizeof(ct), "\nwtmp begins %+\n", tm); printf("%s", ct); @@ -243,24 +242,21 @@ wtmp(void) * process a single wtmp entry */ void -doentry(struct utmp *bp) +doentry(struct utmpx *bp) { - struct ttytab *tt, *ttx; /* ttylist entry */ + struct idtab *tt, *ttx; /* idlist entry */ - /* - * if the terminal line is '~', the machine stopped. - * see utmp(5) for more info. - */ - if (bp->ut_line[0] == '~' && !bp->ut_line[1]) { + /* the machine stopped */ + if (bp->ut_type == BOOT_TIME || bp->ut_type == SHUTDOWN_TIME) { /* everybody just logged out */ - for (tt = LIST_FIRST(&ttylist); tt;) { + for (tt = LIST_FIRST(&idlist); tt;) { LIST_REMOVE(tt, list); ttx = tt; tt = LIST_NEXT(tt, list); free(ttx); } - currentout = -bp->ut_time; - crmsg = strncmp(bp->ut_name, "shutdown", UT_NAMESIZE) ? + currentout = -bp->ut_tv.tv_sec; + crmsg = bp->ut_type != SHUTDOWN_TIME ? "crash" : "shutdown"; /* * if we're in snapshot mode, we want to exit if this @@ -276,50 +272,42 @@ doentry(struct utmp *bp) printentry(bp, NULL); return; } - /* - * if the line is '{' or '|', date got set; see - * utmp(5) for more info. - */ - if ((bp->ut_line[0] == '{' || bp->ut_line[0] == '|') && - !bp->ut_line[1]) { + /* date got set */ + if (bp->ut_type == OLD_TIME || bp->ut_type == NEW_TIME) { if (want(bp) && !snaptime) printentry(bp, NULL); return; } - /* find associated tty */ - LIST_FOREACH(tt, &ttylist, list) - if (!strncmp(tt->tty, bp->ut_line, UT_LINESIZE)) + + if (bp->ut_type != USER_PROCESS && bp->ut_type != DEAD_PROCESS) + return; + + /* find associated identifier */ + LIST_FOREACH(tt, &idlist, list) + if (!memcmp(tt->id, bp->ut_id, sizeof bp->ut_id)) break; if (tt == NULL) { /* add new one */ - tt = malloc(sizeof(struct ttytab)); + tt = malloc(sizeof(struct idtab)); if (tt == NULL) errx(1, "malloc failure"); tt->logout = currentout; - strncpy(tt->tty, bp->ut_line, UT_LINESIZE); - LIST_INSERT_HEAD(&ttylist, tt, list); + memcpy(tt->id, bp->ut_id, sizeof bp->ut_id); + LIST_INSERT_HEAD(&idlist, tt, list); } /* * print record if not in snapshot mode and wanted * or in snapshot mode and in snapshot range */ - if (bp->ut_name[0] && (want(bp) || (bp->ut_time < snaptime && + if (bp->ut_type == USER_PROCESS && (want(bp) || + (bp->ut_tv.tv_sec < snaptime && (tt->logout > snaptime || tt->logout < 1)))) { snapfound = 1; - /* - * when uucp and ftp log in over a network, the entry in - * the utmp file is the name plus their process id. See - * etc/ftpd.c and usr.bin/uucp/uucpd.c for more information. - */ - if (!strncmp(bp->ut_line, "ftp", sizeof("ftp") - 1)) - bp->ut_line[3] = '\0'; - else if (!strncmp(bp->ut_line, "uucp", sizeof("uucp") - 1)) - bp->ut_line[4] = '\0'; printentry(bp, tt); } - tt->logout = bp->ut_time; + tt->logout = bp->ut_tv.tv_sec; } /* @@ -330,7 +318,7 @@ doentry(struct utmp *bp) * logout type (crash/shutdown) as appropriate. */ void -printentry(struct utmp *bp, struct ttytab *tt) +printentry(struct utmpx *bp, struct idtab *tt) { char ct[80]; struct tm *tm; @@ -339,16 +327,30 @@ printentry(struct utmp *bp, struct ttytab *tt) if (maxrec != -1 && !maxrec--) exit(0); - t = _int_to_time(bp->ut_time); + t = bp->ut_tv.tv_sec; tm = localtime(&t); (void) strftime(ct, sizeof(ct), d_first ? (yflag ? "%a %e %b %Y %R" : "%a %e %b %R") : (yflag ? "%a %b %e %Y %R" : "%a %b %e %R"), tm); - printf("%-*.*s %-*.*s %-*.*s %s%c", - UT_NAMESIZE, UT_NAMESIZE, bp->ut_name, - UT_LINESIZE, UT_LINESIZE, bp->ut_line, - UT_HOSTSIZE, UT_HOSTSIZE, bp->ut_host, - ct, tt == NULL ? '\n' : ' '); + switch (bp->ut_type) { + case BOOT_TIME: + printf("%-42s", "boot time"); + break; + case SHUTDOWN_TIME: + printf("%-42s", "shutdown time"); + break; + case OLD_TIME: + printf("%-42s", "old time"); + break; + case NEW_TIME: + printf("%-42s", "new time"); + break; + case USER_PROCESS: + printf("%-10s %-8s %-22.22s", + bp->ut_user, bp->ut_line, bp->ut_host); + break; + } + printf(" %s%c", ct, tt == NULL ? '\n' : ' '); if (tt == NULL) return; if (!tt->logout) { @@ -363,7 +365,7 @@ printentry(struct utmp *bp, struct ttytab *tt) (void) strftime(ct, sizeof(ct), "%R", tm); printf("- %s", ct); } - delta = tt->logout - bp->ut_time; + delta = tt->logout - bp->ut_tv.tv_sec; if (sflag) { printf(" (%8ld)\n", (long)delta); } else { @@ -381,7 +383,7 @@ printentry(struct utmp *bp, struct ttytab *tt) * see if want this entry */ int -want(struct utmp *bp) +want(struct utmpx *bp) { ARG *step; @@ -394,15 +396,15 @@ want(struct utmp *bp) for (step = arglist; step; step = step->next) switch(step->type) { case HOST_TYPE: - if (!strncasecmp(step->name, bp->ut_host, UT_HOSTSIZE)) + if (!strcasecmp(step->name, bp->ut_host)) return (YES); break; case TTY_TYPE: - if (!strncmp(step->name, bp->ut_line, UT_LINESIZE)) + if (!strcmp(step->name, bp->ut_line)) return (YES); break; case USER_TYPE: - if (!strncmp(step->name, bp->ut_name, UT_NAMESIZE)) + if (!strcmp(step->name, bp->ut_user)) return (YES); break; } @@ -552,25 +554,3 @@ terr: errx(1, "out of range or illegal time specification: [[CC]YY]MMDDhhmm[.SS]"); return timet; } - - -/* - * onintr -- - * on interrupt, we inform the user how far we've gotten - */ -void -onintr(int signo) -{ - char ct[80]; - struct tm *tm; - time_t t = _int_to_time(buf[0].ut_time); - - tm = localtime(&t); - (void) strftime(ct, sizeof(ct), - d_first ? "%a %e %b %R" : "%a %b %e %R", - tm); - printf("\ninterrupted %s\n", ct); - if (signo == SIGINT) - exit(1); - (void)fflush(stdout); /* fix required for rsh */ -} diff --git a/usr.bin/lastcomm/Makefile b/usr.bin/lastcomm/Makefile index c79cd74..84708d9 100644 --- a/usr.bin/lastcomm/Makefile +++ b/usr.bin/lastcomm/Makefile @@ -3,6 +3,5 @@ PROG= lastcomm SRCS= lastcomm.c readrec.c -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/lastcomm/lastcomm.c b/usr.bin/lastcomm/lastcomm.c index 8c55123..ab12ec2 100644 --- a/usr.bin/lastcomm/lastcomm.c +++ b/usr.bin/lastcomm/lastcomm.c @@ -59,7 +59,6 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <utmp.h> #include "pathnames.h" /*XXX*/#include <inttypes.h> @@ -154,11 +153,11 @@ main(int argc, char *argv[]) if (*argv && !requested(argv, &ab)) continue; - (void)printf("%-*.*s %-7s %-*s %-*s", + (void)printf("%-*.*s %-7s %-*s %-8s", AC_COMM_LEN, AC_COMM_LEN, ab.ac_comm, flagbits(ab.ac_flagx), - UT_NAMESIZE, user_from_uid(ab.ac_uid, 0), - UT_LINESIZE, getdev(ab.ac_tty)); + MAXLOGNAME - 1, user_from_uid(ab.ac_uid, 0), + getdev(ab.ac_tty)); /* user + system time */ diff --git a/usr.bin/lastcomm/pathnames.h b/usr.bin/lastcomm/pathnames.h index 0737970..04af440 100644 --- a/usr.bin/lastcomm/pathnames.h +++ b/usr.bin/lastcomm/pathnames.h @@ -31,8 +31,8 @@ * SUCH DAMAGE. * * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + * + * $FreeBSD$ */ -#include <paths.h> - #define _PATH_ACCT "/var/account/acct" diff --git a/usr.bin/ldd/Makefile b/usr.bin/ldd/Makefile index 357f9f3..cd17228 100644 --- a/usr.bin/ldd/Makefile +++ b/usr.bin/ldd/Makefile @@ -5,6 +5,5 @@ SRCS= ldd.c .if ${MACHINE_ARCH} == "i386" SRCS+= sods.c .endif -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/ldd/ldd.1 b/usr.bin/ldd/ldd.1 index 72afb39..b245631 100644 --- a/usr.bin/ldd/ldd.1 +++ b/usr.bin/ldd/ldd.1 @@ -63,7 +63,7 @@ The following is an example of a shell pipeline which uses the option. It will print a report of all ELF binaries in the current directory, which link against libc.so.6: -.Dl "find . -type f | xargs -n1 file -F " " | grep ELF | cut -f1 -d' ' | xargs ldd -f '%A %o\en' | grep libc.so.6" +.Dl "find . -type f | xargs -n1 file -F ' ' | grep ELF | cut -f1 -d' ' | xargs ldd -f '%A %o\en' | grep libc.so.6" .Sh SEE ALSO .Xr ld 1 , .Xr nm 1 , diff --git a/usr.bin/leave/Makefile b/usr.bin/leave/Makefile index 859c11c..5484797 100644 --- a/usr.bin/leave/Makefile +++ b/usr.bin/leave/Makefile @@ -3,4 +3,6 @@ PROG= leave +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/leave/leave.c b/usr.bin/leave/leave.c index 89c3f52..c7f93c6 100644 --- a/usr.bin/leave/leave.c +++ b/usr.bin/leave/leave.c @@ -53,7 +53,7 @@ __FBSDID("$FreeBSD$"); #include <time.h> #include <unistd.h> -void doalarm(u_int); +static void doalarm(u_int); static void usage(void); /* diff --git a/usr.bin/less/Makefile.common b/usr.bin/less/Makefile.common index fcc6ccf..cffb85a 100644 --- a/usr.bin/less/Makefile.common +++ b/usr.bin/less/Makefile.common @@ -4,6 +4,7 @@ LSDIR= ${.CURDIR}/../../contrib/less .PATH: ${LSDIR} CFLAGS+=-I${.CURDIR}/../less -I${LSDIR} +WARNS?= 1 .SUFFIXES: .nro .1 diff --git a/usr.bin/lex/Makefile b/usr.bin/lex/Makefile index 6d0ea5b..e16568f 100644 --- a/usr.bin/lex/Makefile +++ b/usr.bin/lex/Makefile @@ -24,6 +24,8 @@ MLINKS+= lex.1 flex.1 MLINKS+= lex.1 flex++.1 MLINKS+= lex.1 lex++.1 +WARNS?= 2 + CLEANFILES= scan.c skel.c SUBDIR= lib diff --git a/usr.bin/lex/flex.skl b/usr.bin/lex/flex.skl index 460b2fa..2111711 100644 --- a/usr.bin/lex/flex.skl +++ b/usr.bin/lex/flex.skl @@ -747,7 +747,7 @@ void yyFlexLexer::LexerOutput( const char* buf, int size ) */ %- -static int yy_get_next_buffer() +static int yy_get_next_buffer(void) %+ int yyFlexLexer::yy_get_next_buffer() %* @@ -883,7 +883,7 @@ int yyFlexLexer::yy_get_next_buffer() /* yy_get_previous_state - get the state just before the EOB char was reached */ %- -static yy_state_type yy_get_previous_state() +static yy_state_type yy_get_previous_state(void) %+ yy_state_type yyFlexLexer::yy_get_previous_state() %* @@ -982,7 +982,7 @@ void yyFlexLexer::yyunput( int c, char* yy_bp ) #ifdef __cplusplus static int yyinput() #else -static int input() +static int input(void) #endif %+ int yyFlexLexer::yyinput() diff --git a/usr.bin/lex/initscan.c b/usr.bin/lex/initscan.c index 7617c69..59c81f7 100644 --- a/usr.bin/lex/initscan.c +++ b/usr.bin/lex/initscan.c @@ -2894,7 +2894,7 @@ case YY_STATE_EOF(LINEDIR): * EOB_ACT_END_OF_FILE - end of file */ -static int yy_get_next_buffer() +static int yy_get_next_buffer(void) { char *dest = yy_current_buffer->yy_ch_buf; char *source = yytext_ptr; @@ -3026,7 +3026,7 @@ static int yy_get_next_buffer() /* yy_get_previous_state - get the state just before the EOB char was reached */ -static yy_state_type yy_get_previous_state() +static yy_state_type yy_get_previous_state(void) { yy_state_type yy_current_state; char *yy_cp; @@ -3138,7 +3138,7 @@ char *yy_bp; #ifdef __cplusplus static int yyinput() #else -static int input() +static int input(void) #endif { int c; diff --git a/usr.bin/lex/lib/Makefile b/usr.bin/lex/lib/Makefile index dcc7518..b43d803 100644 --- a/usr.bin/lex/lib/Makefile +++ b/usr.bin/lex/lib/Makefile @@ -6,6 +6,8 @@ LIB= ln SRCS= libmain.c libyywrap.c NO_PIC= +WARNS?= 2 + .if ${MK_INSTALLLIB} != "no" LINKS= ${LIBDIR}/libln.a ${LIBDIR}/libl.a LINKS+= ${LIBDIR}/libln.a ${LIBDIR}/libfl.a diff --git a/usr.bin/limits/Makefile b/usr.bin/limits/Makefile index 5999cdb..0133178 100644 --- a/usr.bin/limits/Makefile +++ b/usr.bin/limits/Makefile @@ -3,6 +3,5 @@ PROG= limits DPADD= ${LIBUTIL} LDADD= -lutil -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/locale/Makefile b/usr.bin/locale/Makefile index d7f67e1..5b8932c 100644 --- a/usr.bin/locale/Makefile +++ b/usr.bin/locale/Makefile @@ -2,6 +2,6 @@ PROG = locale CFLAGS += -I${.CURDIR}/../../lib/libc/locale -WARNS ?= 1 +WARNS ?= 3 .include <bsd.prog.mk> diff --git a/usr.bin/locale/locale.1 b/usr.bin/locale/locale.1 index e6b6451..b2aba5d 100644 --- a/usr.bin/locale/locale.1 +++ b/usr.bin/locale/locale.1 @@ -35,8 +35,12 @@ .Nm .Op Fl a | m .Nm -.Op Fl ck -.Op Ar keyword ... +.Fl k +.Ic list +.Op Ar prefix +.Nm +.Op Fl ck +.Ar keyword ... .Sh DESCRIPTION The .Nm @@ -79,6 +83,8 @@ The special specific) keyword .Cm list can be used to retrieve the human readable list of all available keywords. +If so, +a prefix string can be defined to limit the amount of keywords returned. .Sh EXIT STATUS .Ex -std .Sh SEE ALSO diff --git a/usr.bin/locale/locale.c b/usr.bin/locale/locale.c index a4b6b40..cad3afe 100644 --- a/usr.bin/locale/locale.c +++ b/usr.bin/locale/locale.c @@ -55,7 +55,7 @@ const char *lookup_localecat(int); char *kwval_lconv(int); int kwval_lookup(char *, char **, int *, int *); void showdetails(char *); -void showkeywordslist(void); +void showkeywordslist(char *substring); void showlocale(void); void usage(void); @@ -190,6 +190,18 @@ struct _kwinfo { { "abmon_10", 1, LC_TIME, ABMON_10, "" }, { "abmon_11", 1, LC_TIME, ABMON_11, "" }, { "abmon_12", 1, LC_TIME, ABMON_12, "" }, + { "altmon_1", 1, LC_TIME, ALTMON_1, "(FreeBSD only)" }, + { "altmon_2", 1, LC_TIME, ALTMON_2, "(FreeBSD only)" }, + { "altmon_3", 1, LC_TIME, ALTMON_3, "(FreeBSD only)" }, + { "altmon_4", 1, LC_TIME, ALTMON_4, "(FreeBSD only)" }, + { "altmon_5", 1, LC_TIME, ALTMON_5, "(FreeBSD only)" }, + { "altmon_6", 1, LC_TIME, ALTMON_6, "(FreeBSD only)" }, + { "altmon_7", 1, LC_TIME, ALTMON_7, "(FreeBSD only)" }, + { "altmon_8", 1, LC_TIME, ALTMON_8, "(FreeBSD only)" }, + { "altmon_9", 1, LC_TIME, ALTMON_9, "(FreeBSD only)" }, + { "altmon_10", 1, LC_TIME, ALTMON_10, "(FreeBSD only)" }, + { "altmon_11", 1, LC_TIME, ALTMON_11, "(FreeBSD only)" }, + { "altmon_12", 1, LC_TIME, ALTMON_12, "(FreeBSD only)" }, { "era", 1, LC_TIME, ERA, "(unavailable)" }, { "era_d_fmt", 1, LC_TIME, ERA_D_FMT, "(unavailable)" }, { "era_d_t_fmt", 1, LC_TIME, ERA_D_T_FMT, "(unavailable)" }, @@ -217,7 +229,7 @@ main(int argc, char *argv[]) int ch; int tmp; - while ((ch = getopt(argc, argv, "ackm")) != -1) { + while ((ch = getopt(argc, argv, "ackms:")) != -1) { switch (ch) { case 'a': all_locales = 1; @@ -265,7 +277,7 @@ main(int argc, char *argv[]) if (prt_keywords && argc > 0) while (tmp < argc) if (strcasecmp(argv[tmp++], "list") == 0) { - showkeywordslist(); + showkeywordslist(argv[tmp]); exit(0); } @@ -290,7 +302,8 @@ void usage(void) { printf("Usage: locale [ -a | -m ]\n" - " locale [ -ck ] name ...\n"); + " locale -k list [prefix]\n" + " locale [ -ck ] keyword ...\n"); exit(1); } @@ -594,6 +607,7 @@ showdetails(char *kw) * invalid keyword specified. * XXX: any actions? */ + fprintf(stderr, "Unknown keyword: `%s'\n", kw); return; } @@ -639,16 +653,25 @@ lookup_localecat(int cat) * Show list of keywords */ void -showkeywordslist(void) +showkeywordslist(char *substring) { size_t i; #define FMT "%-20s %-12s %-7s %-20s\n" - printf("List of available keywords\n\n"); + if (substring == NULL) + printf("List of available keywords\n\n"); + else + printf("List of available keywords starting with '%s'\n\n", + substring); printf(FMT, "Keyword", "Category", "Type", "Comment"); printf("-------------------- ------------ ------- --------------------\n"); for (i = 0; i < NKWINFO; i++) { + if (substring != NULL) { + if (strncmp(kwinfo[i].name, substring, + strlen(substring)) != 0) + continue; + } printf(FMT, kwinfo[i].name, lookup_localecat(kwinfo[i].catid), diff --git a/usr.bin/locate/Makefile.inc b/usr.bin/locate/Makefile.inc index 4817408..5ce397d 100644 --- a/usr.bin/locate/Makefile.inc +++ b/usr.bin/locate/Makefile.inc @@ -1,3 +1,5 @@ # $FreeBSD$ LIBEXECDIR?= /usr/libexec + +WARNS?= 0 diff --git a/usr.bin/locate/locate/Makefile b/usr.bin/locate/locate/Makefile index 9a618da..a139ff2 100644 --- a/usr.bin/locate/locate/Makefile +++ b/usr.bin/locate/locate/Makefile @@ -7,6 +7,8 @@ CFLAGS+= -I${.CURDIR} -DMMAP # -DDEBUG (print time) -O2 (10% faster) SCRIPTS=updatedb.sh mklocatedb.sh concatdb.sh MAN= locate.1 locate.updatedb.8 +WARNS?= 2 + SCRIPTSDIR= ${LIBEXECDIR} .for script in ${SCRIPTS} SCRIPTSNAME_${script}= locate.${script:R} diff --git a/usr.bin/lock/Makefile b/usr.bin/lock/Makefile index dc09a87..8093ca5 100644 --- a/usr.bin/lock/Makefile +++ b/usr.bin/lock/Makefile @@ -7,4 +7,6 @@ BINMODE=4555 DPADD= ${LIBCRYPT} LDADD= -lcrypt +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/logger/Makefile b/usr.bin/logger/Makefile index 6ff7224..922192c 100644 --- a/usr.bin/logger/Makefile +++ b/usr.bin/logger/Makefile @@ -4,7 +4,6 @@ .include <bsd.own.mk> PROG= logger -WARNS?= 6 .if ${MK_INET6_SUPPORT} != "no" CFLAGS+= -DINET6 diff --git a/usr.bin/logger/logger.c b/usr.bin/logger/logger.c index 5d9ec4d..9027105 100644 --- a/usr.bin/logger/logger.c +++ b/usr.bin/logger/logger.c @@ -114,6 +114,7 @@ main(int argc, char *argv[]) case 'f': /* file to log */ if (freopen(optarg, "r", stdin) == NULL) err(1, "%s", optarg); + setvbuf(stdin, 0, _IONBF, 0); break; case 'h': /* hostname to deliver to */ host = optarg; diff --git a/usr.bin/login/Makefile b/usr.bin/login/Makefile index 22c73b9..bd34ed9 100644 --- a/usr.bin/login/Makefile +++ b/usr.bin/login/Makefile @@ -9,6 +9,8 @@ CFLAGS+=-DLOGALL DPADD= ${LIBUTIL} ${LIBPAM} LDADD= -lutil ${MINUSLPAM} +WARNS?= 5 + .if ${MK_AUDIT} != "no" SRCS+= login_audit.c CFLAGS+= -DUSE_BSM_AUDIT diff --git a/usr.bin/login/login.c b/usr.bin/login/login.c index 2a42924..da182aa 100644 --- a/usr.bin/login/login.c +++ b/usr.bin/login/login.c @@ -737,7 +737,7 @@ auth_pam(void) * Export any environment variables PAM modules may have set */ static void -export_pam_environment() +export_pam_environment(void) { char **pam_env; char **pp; @@ -786,7 +786,7 @@ export(const char *s) } static void -usage() +usage(void) { (void)fprintf(stderr, "usage: login [-fp] [-h hostname] [username]\n"); @@ -797,7 +797,7 @@ usage() * Prompt user and read login name from stdin. */ static char * -getloginname() +getloginname(void) { char *nbuf, *p; int ch; @@ -941,7 +941,7 @@ pam_syslog(const char *msg) * Shut down PAM */ static void -pam_cleanup() +pam_cleanup(void) { if (pamh != NULL) { diff --git a/usr.bin/login/login_fbtab.c b/usr.bin/login/login_fbtab.c index f6111a1..afa320f 100644 --- a/usr.bin/login/login_fbtab.c +++ b/usr.bin/login/login_fbtab.c @@ -81,10 +81,7 @@ static void login_protect(const char *, char *, int, uid_t, gid_t); /* login_fbtab - apply protections specified in /etc/fbtab or logindevperm */ void -login_fbtab(tty, uid, gid) -char *tty; -uid_t uid; -gid_t gid; +login_fbtab(char *tty, uid_t uid, gid_t gid) { FILE *fp; char buf[BUFSIZ]; @@ -124,12 +121,7 @@ gid_t gid; /* login_protect - protect one device entry */ void -login_protect(table, pattern, mask, uid, gid) -const char *table; -char *pattern; -int mask; -uid_t uid; -gid_t gid; +login_protect(const char *table, char *pattern, int mask, uid_t uid, gid_t gid) { glob_t gl; char *path; diff --git a/usr.bin/logins/Makefile b/usr.bin/logins/Makefile index 42840fb..ad88cd9 100644 --- a/usr.bin/logins/Makefile +++ b/usr.bin/logins/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= logins -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/logname/Makefile b/usr.bin/logname/Makefile index 4e910ce..875df2c 100644 --- a/usr.bin/logname/Makefile +++ b/usr.bin/logname/Makefile @@ -2,6 +2,5 @@ # $FreeBSD$ PROG= logname -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/look/Makefile b/usr.bin/look/Makefile index 64d7788..a620039 100644 --- a/usr.bin/look/Makefile +++ b/usr.bin/look/Makefile @@ -1,5 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/9/93 +# $FreeBSD$ PROG= look +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/lsvfs/Makefile b/usr.bin/lsvfs/Makefile index 0c33583..324b6fd 100644 --- a/usr.bin/lsvfs/Makefile +++ b/usr.bin/lsvfs/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= lsvfs -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/m4/Makefile b/usr.bin/m4/Makefile index 702b3f3..feceb0c 100644 --- a/usr.bin/m4/Makefile +++ b/usr.bin/m4/Makefile @@ -9,4 +9,6 @@ CFLAGS+=-DEXTENDED SRCS= eval.c expr.c look.c main.c misc.c gnum4.c trace.c +WARNS?= 0 + .include <bsd.prog.mk> diff --git a/usr.bin/mail/Makefile b/usr.bin/mail/Makefile index 5fa455b..da3e433 100644 --- a/usr.bin/mail/Makefile +++ b/usr.bin/mail/Makefile @@ -11,6 +11,8 @@ EFILES= mail.rc LINKS= ${BINDIR}/mail ${BINDIR}/Mail ${BINDIR}/mail ${BINDIR}/mailx MLINKS= mail.1 Mail.1 mail.1 mailx.1 +WARNS?= 1 + .PATH: ${.CURDIR}/misc etc-mailrc: diff --git a/usr.bin/make/Makefile b/usr.bin/make/Makefile index deb076f..c581385 100644 --- a/usr.bin/make/Makefile +++ b/usr.bin/make/Makefile @@ -8,7 +8,6 @@ SRCS= arch.c buf.c cond.c dir.c for.c hash.c hash_tables.c job.c \ lst.c main.c make.c parse.c proc.c shell.c str.c suff.c targ.c \ util.c var.c -WARNS?= 6 NO_SHARED?= YES CFLAGS+=-DMAKE_VERSION=\"5200408120\" diff --git a/usr.bin/make/arch.c b/usr.bin/make/arch.c index 90801cd..9cd0a1c 100644 --- a/usr.bin/make/arch.c +++ b/usr.bin/make/arch.c @@ -1131,7 +1131,7 @@ Arch_MemMTime(GNode *gn) * 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. + * as returned by Path_FindFile. * *----------------------------------------------------------------------- */ diff --git a/usr.bin/make/dir.c b/usr.bin/make/dir.c index c66b2bf..7cee3d4 100644 --- a/usr.bin/make/dir.c +++ b/usr.bin/make/dir.c @@ -61,6 +61,10 @@ __FBSDID("$FreeBSD$"); * 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. @@ -83,14 +87,13 @@ __FBSDID("$FreeBSD$"); * Dir_PrintDirectories Print stats about the directory cache. */ -#include <sys/types.h> +#include <sys/param.h> #include <sys/stat.h> #include <dirent.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <unistd.h> #include "arch.h" #include "dir.h" @@ -98,7 +101,6 @@ __FBSDID("$FreeBSD$"); #include "GNode.h" #include "hash.h" #include "lst.h" -#include "make.h" #include "str.h" #include "targ.h" #include "util.h" @@ -832,21 +834,6 @@ Path_FindFile(char *name, struct Path *path) * When searching for $(FILE), we will find it in $(INSTALLDIR) * b/c we added it here. This is not good... */ -#ifdef notdef - cp[-1] = '\0'; - Path_AddDir(path, name); - cp[-1] = '/'; - - bigmisses += 1; - pe = TAILQ_LAST(path, Path); - if (pe == NULL) - return (NULL); - - if (Hash_FindEntry(&pe->dir->files, cp) != NULL) { - return (estrdup(name)); - - return (NULL); -#else /* !notdef */ DEBUGF(DIR, ("Looking for \"%s\"...", name)); bigmisses += 1; @@ -864,7 +851,83 @@ Path_FindFile(char *name, struct Path *path) DEBUGF(DIR, ("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); } /*- @@ -878,7 +941,7 @@ Path_FindFile(char *name, struct Path *path) * * 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 + * If the node didn't have a path entry before, and Path_FindFile * found one for it, the full name is placed in the path slot. *----------------------------------------------------------------------- */ diff --git a/usr.bin/make/dir.h b/usr.bin/make/dir.h index 03481b8..1ae89ae 100644 --- a/usr.bin/make/dir.h +++ b/usr.bin/make/dir.h @@ -56,6 +56,7 @@ TAILQ_HEAD(Path, PathElement); void Dir_Init(void); void Dir_InitDot(void); Boolean Dir_HasWildcards(const char *); +int Dir_FindHereOrAbove(char *, char *, char *, int); int Dir_MTime(struct GNode *); void Dir_PrintDirectories(void); diff --git a/usr.bin/make/for.c b/usr.bin/make/for.c index 8df5471..f2eccce 100644 --- a/usr.bin/make/for.c +++ b/usr.bin/make/for.c @@ -50,11 +50,9 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include "buf.h" -#include "dir.h" #include "for.h" #include "globals.h" #include "lst.h" -#include "make.h" #include "parse.h" #include "str.h" #include "util.h" diff --git a/usr.bin/make/globals.h b/usr.bin/make/globals.h index 6f75eaf..06c302e 100644 --- a/usr.bin/make/globals.h +++ b/usr.bin/make/globals.h @@ -45,9 +45,6 @@ * Global Variables */ -#include <time.h> -#include <stdint.h> - #include "lst.h" #include "util.h" diff --git a/usr.bin/make/job.c b/usr.bin/make/job.c index e37c62c..beb83dc 100644 --- a/usr.bin/make/job.c +++ b/usr.bin/make/job.c @@ -114,6 +114,8 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <fcntl.h> #include <inttypes.h> +#include <limits.h> +#include <paths.h> #include <string.h> #include <signal.h> #include <stdlib.h> @@ -137,7 +139,7 @@ __FBSDID("$FreeBSD$"); #include "util.h" #include "var.h" -#define TMPPAT "/tmp/makeXXXXXXXXXX" +#define TMPPAT "makeXXXXXXXXXX" #ifndef USE_KQUEUE /* @@ -236,7 +238,7 @@ typedef struct Job { */ struct { /* Name of file to which shell output was rerouted */ - char of_outFile[sizeof(TMPPAT)]; + char of_outFile[PATH_MAX]; /* * Stream open to the output file. Used to funnel all @@ -485,7 +487,7 @@ catch_child(int sig __unused) /** */ void -Proc_Init() +Proc_Init(void) { /* * Catch SIGCHLD so that we get kicked out of select() when we @@ -1008,17 +1010,6 @@ JobFinish(Job *job, int *status) if (!(job->flags & JOB_CONTINUING)) { DEBUGF(JOB, ("Warning: process %jd was not " "continuing.\n", (intmax_t) job->pid)); -#ifdef notdef - /* - * We don't really want to restart a - * job from scratch just because it - * continued, especially not without - * killing the continuing process! - * That's why this is ifdef'ed out. - * FD - 9/17/90 - */ - JobRestart(job); -#endif } job->flags &= ~JOB_CONTINUING; TAILQ_INSERT_TAIL(&jobs, job, link); @@ -1577,7 +1568,8 @@ JobStart(GNode *gn, int flags, Job *previous) Boolean noExec; /* Set true if we decide not to run the job */ int tfd; /* File descriptor for temp file */ LstNode *ln; - char tfile[sizeof(TMPPAT)]; + char tfile[PATH_MAX]; + const char *tdir; if (interrupted) { JobPassSig(interrupted); @@ -1618,6 +1610,9 @@ JobStart(GNode *gn, int flags, Job *previous) cmdsOK = TRUE; } + if ((tdir = getenv("TMPDIR")) == NULL) + tdir = _PATH_TMP; + /* * 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 @@ -1633,7 +1628,7 @@ JobStart(GNode *gn, int flags, Job *previous) DieHorribly(); } - strcpy(tfile, TMPPAT); + snprintf(tfile, sizeof(tfile), "%s/%s", tdir, TMPPAT); if ((tfd = mkstemp(tfile)) == -1) Punt("Cannot create temp file: %s", strerror(errno)); job->cmdFILE = fdopen(tfd, "w+"); @@ -1812,7 +1807,8 @@ JobStart(GNode *gn, int flags, Job *previous) } else { fprintf(stdout, "Remaking `%s'\n", gn->name); fflush(stdout); - strcpy(job->outFile, TMPPAT); + snprintf(job->outFile, sizeof(job->outFile), "%s/%s", + tdir, TMPPAT); if ((job->outFd = mkstemp(job->outFile)) == -1) Punt("cannot create temp file: %s", strerror(errno)); @@ -3380,6 +3376,8 @@ Compat_Run(Lst *targs) printf("`%s' not remade because of errors.\n", gn->name); makeErrors++; + } else if (gn->made == ERROR) { + makeErrors++; } } diff --git a/usr.bin/make/lst.c b/usr.bin/make/lst.c index a5b7f06..be30621 100644 --- a/usr.bin/make/lst.c +++ b/usr.bin/make/lst.c @@ -41,7 +41,6 @@ #include <stdlib.h> #include "lst.h" -#include "make.h" #include "util.h" /** diff --git a/usr.bin/make/lst.h b/usr.bin/make/lst.h index 497d59c..1199e94 100644 --- a/usr.bin/make/lst.h +++ b/usr.bin/make/lst.h @@ -48,8 +48,6 @@ * Header for using the list library */ -#include "util.h" - /* * Structure of a list node. */ diff --git a/usr.bin/make/main.c b/usr.bin/make/main.c index 0e16506..91638d4 100644 --- a/usr.bin/make/main.c +++ b/usr.bin/make/main.c @@ -368,6 +368,7 @@ MainParseArgs(int argc, char **argv) { int c; Boolean found_dd = FALSE; + char found_dir[MAXPATHLEN + 1]; /* for searching for sys.mk */ rearg: optind = 1; /* since we're called more than once */ @@ -394,6 +395,8 @@ rearg: case 'C': if (chdir(optarg) == -1) err(1, "chdir %s", optarg); + if (getcwd(curdir, MAXPATHLEN) == NULL) + err(2, NULL); break; case 'D': Var_SetGlobal(optarg, "1"); @@ -492,7 +495,15 @@ rearg: MFLAGS_append("-k", NULL); break; case 'm': - Path_AddDir(&sysIncPath, optarg); + /* look for magic parent directory search string */ + if (strncmp(".../", optarg, 4) == 0) { + if (!Dir_FindHereOrAbove(curdir, optarg + 4, + found_dir, sizeof(found_dir))) + break; /* nothing doing */ + Path_AddDir(&sysIncPath, found_dir); + } else { + Path_AddDir(&sysIncPath, optarg); + } MFLAGS_append("-m", optarg); break; case 'n': @@ -706,6 +717,7 @@ Main_AddSourceMakefile(const char *name) static void Remake_Makefiles(void) { + Lst cleanup; LstNode *ln; int error_cnt = 0; int remade_cnt = 0; @@ -716,6 +728,7 @@ Remake_Makefiles(void) Fatal("Failed to change directory to %s.", curdir); } + Lst_Init(&cleanup); LST_FOREACH(ln, &source_makefiles) { LstNode *ln2; struct GNode *gn; @@ -791,21 +804,7 @@ Remake_Makefiles(void) gn->name); error_cnt++; } else if (gn->made == UPTODATE) { - Lst examine; - - Lst_Init(&examine); - Lst_EnQueue(&examine, gn); - while (!Lst_IsEmpty(&examine)) { - LstNode *eln; - GNode *egn = Lst_DeQueue(&examine); - - egn->make = FALSE; - LST_FOREACH(eln, &egn->children) { - GNode *cgn = Lst_Datum(eln); - - Lst_EnQueue(&examine, cgn); - } - } + Lst_EnQueue(&cleanup, gn); } } @@ -827,6 +826,24 @@ Remake_Makefiles(void) } } + while (!Lst_IsEmpty(&cleanup)) { + GNode *gn = Lst_DeQueue(&cleanup); + + gn->unmade = 0; + gn->make = FALSE; + gn->made = UNMADE; + gn->childMade = FALSE; + gn->mtime = gn->cmtime = 0; + gn->cmtime_gn = NULL; + + LST_FOREACH(ln, &gn->children) { + GNode *cgn = Lst_Datum(ln); + + gn->unmade++; + Lst_EnQueue(&cleanup, cgn); + } + } + if (curdir != objdir) { if (chdir(objdir) < 0) Fatal("Failed to change directory to %s.", objdir); @@ -863,6 +880,7 @@ main(int argc, char **argv) char mdpath[MAXPATHLEN]; char obpath[MAXPATHLEN]; char cdpath[MAXPATHLEN]; + char found_dir[MAXPATHLEN + 1]; /* for searching for sys.mk */ char *cp = NULL, *start; save_argv = argv; @@ -1016,6 +1034,12 @@ main(int argc, char **argv) Job_SetPrefix(); /* + * Find where we are... + */ + if (getcwd(curdir, MAXPATHLEN) == NULL) + err(2, NULL); + + /* * First snag things out of the MAKEFLAGS environment * variable. Then parse the command line arguments. */ @@ -1024,11 +1048,8 @@ main(int argc, char **argv) MainParseArgs(argc, argv); /* - * Find where we are... + * Verify that cwd is sane (after -C may have changed it). */ - if (getcwd(curdir, MAXPATHLEN) == NULL) - err(2, NULL); - { struct stat sa; @@ -1126,18 +1147,37 @@ main(int argc, char **argv) * as dir1:...:dirn) to the system include path. */ if (TAILQ_EMPTY(&sysIncPath)) { - char syspath[] = PATH_DEFSYSPATH; + char defsyspath[] = PATH_DEFSYSPATH; + char *syspath = getenv("MAKESYSPATH"); + + /* + * If no user-supplied system path was given (thru -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 = estrdup(syspath); for (start = syspath; *start != '\0'; start = cp) { for (cp = start; *cp != '\0' && *cp != ':'; cp++) continue; - if (*cp == '\0') { - Path_AddDir(&sysIncPath, start); - } else { + if (*cp == ':') { *cp++ = '\0'; + } + /* look for magic parent directory search string */ + if (strncmp(".../", start, 4) == 0) { + if (Dir_FindHereOrAbove(curdir, start + 4, + found_dir, sizeof(found_dir))) { + Path_AddDir(&sysIncPath, found_dir); + } + } else { Path_AddDir(&sysIncPath, start); } } + if (syspath != defsyspath) + free(syspath); } /* diff --git a/usr.bin/make/make.1 b/usr.bin/make/make.1 index a4e837c..4e43538 100644 --- a/usr.bin/make/make.1 +++ b/usr.bin/make/make.1 @@ -240,6 +240,36 @@ The system include path will always be appended to the search path used for "..."-style inclusions and makefile searches (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). +Note that a +.Fl C +that are earlier on the command line affect where +.Fl m Qq \&.../ +searches. .It Fl n Display the commands that would have been executed, but do not actually execute them. @@ -822,7 +852,7 @@ These directories will be searched for source files by .Nm after it has finished parsing all input makefiles. .El -.Pp +.Ss Variable Modifiers Variable expansion may be modified to select or modify each word of the variable (where a .Dq word @@ -837,7 +867,7 @@ The colon may be escaped with a backslash .Pq Ql \e . .Bl -tag -width Cm .Sm off -.It Cm C No / Ar pattern Xo +.It Cm :C No / Ar pattern Xo .No / Ar replacement .No / Op Cm 1g .Xc @@ -866,13 +896,13 @@ and are orthogonal; the former specifies whether multiple words are potentially affected, the latter whether multiple substitutions can potentially occur within each affected word. -.It Cm E +.It Cm :E Replaces each word in the variable with its suffix. -.It Cm H +.It Cm :H Replaces each word in the variable with everything but the last component. -.It Cm L +.It Cm :L Converts variable to lower-case letters. -.It Cm M Ns Ar pattern +.It Cm :M Ns Ar pattern Select only those words that match the rest of the modifier. The standard shell wildcard characters .Pf ( Ql * , @@ -883,21 +913,21 @@ may be used. The wildcard characters may be escaped with a backslash .Pq Ql \e . -.It Cm N Ns Ar pattern +.It Cm :N Ns Ar pattern This is identical to -.Cm M , +.Cm :M , but selects all words which do not match the rest of the modifier. -.It Cm O +.It Cm :O Order every word in the variable alphabetically. -.It Cm Q +.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 +.It Cm :R Replaces each word in the variable with everything but its suffix. .Sm off -.It Cm S No / Ar old_string Xo +.It Cm :S No / Ar old_string Xo .No / Ar new_string .No / Op Cm g .Xc @@ -941,7 +971,7 @@ 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. -.It Ar old_string=new_string +.It Ar :old_string=new_string This is the .At V style variable substitution. @@ -961,11 +991,11 @@ is the substring of .Ar old_string to be replaced in .Ar new_string -.It Cm T +.It Cm :T Replaces each word in the variable with its last component. -.It Cm U +.It Cm :U Converts variable to upper-case letters. -.It Cm u +.It Cm :u Remove adjacent duplicate words (like .Xr uniq 1 ) . .El @@ -1665,8 +1695,9 @@ utility uses the following environment variables, if they exist: .Ev MAKE , .Ev MAKEFLAGS , .Ev MAKEOBJDIR , +.Ev MAKEOBJDIRPREFIX , and -.Ev MAKEOBJDIRPREFIX . +.Ev MAKESYSPATH . .Sh FILES .Bl -tag -width /usr/share/doc/psd/12.make -compact .It Pa .depend diff --git a/usr.bin/make/make.c b/usr.bin/make/make.c index a6ddc85..3e6cb42 100644 --- a/usr.bin/make/make.c +++ b/usr.bin/make/make.c @@ -72,8 +72,6 @@ __FBSDID("$FreeBSD$"); * and perform the .USE actions if so. */ -#include <stdlib.h> - #include "arch.h" #include "config.h" #include "dir.h" diff --git a/usr.bin/make/parse.c b/usr.bin/make/parse.c index 2c65164..134d905 100644 --- a/usr.bin/make/parse.c +++ b/usr.bin/make/parse.c @@ -699,6 +699,7 @@ static void ParseDoDependency(char *line) { char *cp; /* our current position */ + char *lstart = line; /* original input line */ GNode *gn; /* a general purpose temporary node */ int op; /* the operator on the line */ char savec; /* a place to save a character */ @@ -802,13 +803,15 @@ ParseDoDependency(char *line) * merges. */ if (strncmp(line, "<<<<<<", 6) == 0 || + 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, "Need an operator"); + Parse_Error(PARSE_FATAL, lstart[0] == '.' ? + "Unknown directive" : "Need an operator"); return; } *cp = '\0'; @@ -1028,7 +1031,8 @@ ParseDoDependency(char *line) op = OP_DEPENDS; } } else { - Parse_Error(PARSE_FATAL, "Missing dependency operator"); + Parse_Error(PARSE_FATAL, lstart[0] == '.' ? + "Unknown directive" : "Missing dependency operator"); return; } diff --git a/usr.bin/make/proc.c b/usr.bin/make/proc.c index 711f605..3a7e4f6 100644 --- a/usr.bin/make/proc.c +++ b/usr.bin/make/proc.c @@ -116,7 +116,7 @@ Proc_Exec(const ProcStuff *ps) execvp(ps->argv[0], ps->argv); write(STDERR_FILENO, ps->argv[0], strlen(ps->argv[0])); - write(STDERR_FILENO, ":", 1); + write(STDERR_FILENO, ": ", 2); write(STDERR_FILENO, strerror(errno), strlen(strerror(errno))); write(STDERR_FILENO, "\n", 1); } else { diff --git a/usr.bin/make/str.c b/usr.bin/make/str.c index 81bb7a3..44ba79d 100644 --- a/usr.bin/make/str.c +++ b/usr.bin/make/str.c @@ -41,12 +41,10 @@ #include <sys/cdefs.h> __FBSDID("$FreeBSD$"); -#include <ctype.h> #include <stdlib.h> #include <string.h> #include "buf.h" -#include "globals.h" #include "str.h" #include "util.h" diff --git a/usr.bin/make/targ.c b/usr.bin/make/targ.c index eac121e..75ced89 100644 --- a/usr.bin/make/targ.c +++ b/usr.bin/make/targ.c @@ -78,13 +78,11 @@ __FBSDID("$FreeBSD$"); */ #include <stdio.h> -#include <string.h> #include "dir.h" #include "globals.h" #include "GNode.h" #include "hash.h" -#include "make.h" #include "suff.h" #include "targ.h" #include "util.h" diff --git a/usr.bin/make/var.c b/usr.bin/make/var.c index 983bb2a..534abca 100644 --- a/usr.bin/make/var.c +++ b/usr.bin/make/var.c @@ -84,7 +84,6 @@ __FBSDID("$FreeBSD$"); * XXX: There's a lot of duplication in these functions. */ -#include <assert.h> #include <ctype.h> #include <stdlib.h> #include <string.h> @@ -97,7 +96,6 @@ __FBSDID("$FreeBSD$"); #include "GNode.h" #include "job.h" #include "lst.h" -#include "make.h" #include "parse.h" #include "str.h" #include "targ.h" diff --git a/usr.bin/makewhatis/makewhatis.c b/usr.bin/makewhatis/makewhatis.c index 6b2dce4..f2ae370 100644 --- a/usr.bin/makewhatis/makewhatis.c +++ b/usr.bin/makewhatis/makewhatis.c @@ -879,9 +879,9 @@ process_section(char *section_dir) * Returns whether the directory entry is a man page section. */ static int -select_sections(struct dirent *entry) +select_sections(const struct dirent *entry) { - char *p = &entry->d_name[3]; + const char *p = &entry->d_name[3]; if (strncmp(entry->d_name, "man", 3) != 0) return 0; diff --git a/usr.bin/minigzip/Makefile b/usr.bin/minigzip/Makefile index 226ba76..9a0ab95 100644 --- a/usr.bin/minigzip/Makefile +++ b/usr.bin/minigzip/Makefile @@ -5,4 +5,6 @@ LDADD= -lz DPADD= ${LIBZ} .PATH: ${.CURDIR}/../../lib/libz +WARNS?= 5 + .include <bsd.prog.mk> diff --git a/usr.bin/mkfifo/Makefile b/usr.bin/mkfifo/Makefile index bab308b..30f013e 100644 --- a/usr.bin/mkfifo/Makefile +++ b/usr.bin/mkfifo/Makefile @@ -2,6 +2,5 @@ # $FreeBSD$ PROG= mkfifo -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/mklocale/Makefile b/usr.bin/mklocale/Makefile index 9873d4e..e75fe99 100644 --- a/usr.bin/mklocale/Makefile +++ b/usr.bin/mklocale/Makefile @@ -2,7 +2,6 @@ # $FreeBSD$ PROG= mklocale -WARNS?= 6 SRCS= yacc.y lex.l y.tab.h CFLAGS+= -I. -I${.CURDIR} -I${.CURDIR}/../../lib/libc/locale diff --git a/usr.bin/mklocale/lex.l b/usr.bin/mklocale/lex.l index eeed535..34ff627 100644 --- a/usr.bin/mklocale/lex.l +++ b/usr.bin/mklocale/lex.l @@ -170,7 +170,7 @@ ENCODING { return(ENCODING); } #if !defined(yywrap) int -yywrap() +yywrap(void) { return(1); } diff --git a/usr.bin/mklocale/yacc.y b/usr.bin/mklocale/yacc.y index 344af22..b86ab88 100644 --- a/usr.bin/mklocale/yacc.y +++ b/usr.bin/mklocale/yacc.y @@ -266,22 +266,20 @@ main(int ac, char *av[]) } static void -usage() +usage(void) { fprintf(stderr, "usage: mklocale [-d] [-o output] [source]\n"); exit(1); } void -yyerror(s) - const char *s; +yyerror(const char *s) { fprintf(stderr, "%s\n", s); } static void * -xmalloc(sz) - unsigned int sz; +xmalloc(unsigned int sz) { void *r = malloc(sz); if (!r) @@ -290,8 +288,7 @@ xmalloc(sz) } static uint32_t * -xlalloc(sz) - unsigned int sz; +xlalloc(unsigned int sz) { uint32_t *r = (uint32_t *)malloc(sz * sizeof(uint32_t)); if (!r) @@ -300,9 +297,7 @@ xlalloc(sz) } static uint32_t * -xrelalloc(old, sz) - uint32_t *old; - unsigned int sz; +xrelalloc(uint32_t *old, unsigned int sz) { uint32_t *r = (uint32_t *)realloc((char *)old, sz * sizeof(uint32_t)); @@ -312,10 +307,7 @@ xrelalloc(old, sz) } void -set_map(map, list, flag) - rune_map *map; - rune_list *list; - uint32_t flag; +set_map(rune_map *map, rune_list *list, uint32_t flag) { while (list) { rune_list *nlist = list->next; @@ -325,9 +317,7 @@ set_map(map, list, flag) } void -set_digitmap(map, list) - rune_map *map; - rune_list *list; +set_digitmap(rune_map *map, rune_list *list) { int32_t i; @@ -347,10 +337,7 @@ set_digitmap(map, list) } void -add_map(map, list, flag) - rune_map *map; - rune_list *list; - uint32_t flag; +add_map(rune_map *map, rune_list *list, uint32_t flag) { int32_t i; rune_list *lr = 0; @@ -555,7 +542,7 @@ add_map(map, list, flag) } static void -dump_tables() +dump_tables(void) { int x, first_d, curr_d; rune_list *list; diff --git a/usr.bin/mkstr/Makefile b/usr.bin/mkstr/Makefile index 805c013..b4e3620 100644 --- a/usr.bin/mkstr/Makefile +++ b/usr.bin/mkstr/Makefile @@ -3,4 +3,6 @@ PROG= mkstr +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/mktemp/mktemp.c b/usr.bin/mktemp/mktemp.c index b58ac4b..72b89d6 100644 --- a/usr.bin/mktemp/mktemp.c +++ b/usr.bin/mktemp/mktemp.c @@ -143,7 +143,7 @@ main(int argc, char **argv) } static void -usage() +usage(void) { fprintf(stderr, "usage: mktemp [-d] [-q] [-t prefix] [-u] template ...\n"); diff --git a/usr.bin/mkuzip/Makefile b/usr.bin/mkuzip/Makefile index 11ed083..c5eac20 100644 --- a/usr.bin/mkuzip/Makefile +++ b/usr.bin/mkuzip/Makefile @@ -6,6 +6,5 @@ MAN= mkuzip.8 DPADD= ${LIBZ} LDADD= -lz -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/msgs/Makefile b/usr.bin/msgs/Makefile index 9f59c6a..21cb151 100644 --- a/usr.bin/msgs/Makefile +++ b/usr.bin/msgs/Makefile @@ -2,7 +2,6 @@ # $FreeBSD$ PROG= msgs -WARNS?= 6 DPADD= ${LIBTERMCAP} LDADD= -ltermcap diff --git a/usr.bin/nc/Makefile b/usr.bin/nc/Makefile index a4400bc..84d20d3 100644 --- a/usr.bin/nc/Makefile +++ b/usr.bin/nc/Makefile @@ -9,4 +9,6 @@ CFLAGS+=-DIPSEC LDADD= -lipsec DPADD= ${LIBIPSEC} +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/ncplist/Makefile b/usr.bin/ncplist/Makefile index 8e6d189..b5baed4 100644 --- a/usr.bin/ncplist/Makefile +++ b/usr.bin/ncplist/Makefile @@ -2,6 +2,8 @@ PROG= ncplist +WARNS?= 0 + DPADD= ${LIBNCP} ${LIBIPX} LDADD= -lncp -lipx diff --git a/usr.bin/ncplist/ncplist.1 b/usr.bin/ncplist/ncplist.1 index 753be8f..335fcc7 100644 --- a/usr.bin/ncplist/ncplist.1 +++ b/usr.bin/ncplist/ncplist.1 @@ -69,7 +69,7 @@ Displays mounted volumes on a given .Sh IMPLEMENTATION NOTES This utility is provided mostly for educational purposes. .Sh FILES -.Bl -tag -width /var/log/wtmp -compact +.Bl -tag -width /var/log/utx.log -compact .It Pa ~/.nwfsrc keeps description for each connection. See diff --git a/usr.bin/netstat/Makefile b/usr.bin/netstat/Makefile index df31aec..8f00901 100644 --- a/usr.bin/netstat/Makefile +++ b/usr.bin/netstat/Makefile @@ -4,7 +4,7 @@ .include <bsd.own.mk> PROG= netstat -SRCS= if.c inet.c main.c mbuf.c mroute.c route.c \ +SRCS= if.c inet.c main.c mbuf.c mroute.c netisr.c route.c \ unix.c atalk.c mroute6.c ipsec.c bpf.c pfkey.c sctp.c WARNS?= 3 diff --git a/usr.bin/netstat/if.c b/usr.bin/netstat/if.c index aa28a6f..50c8fdc 100644 --- a/usr.bin/netstat/if.c +++ b/usr.bin/netstat/if.c @@ -198,8 +198,8 @@ intpr(int interval1, u_long ifnetaddr, void (*pfunc)(char *)) u_long imcasts; u_long oerrors; u_long ierrors; + u_long idrops; u_long collisions; - short timer; int drops; struct sockaddr *sa = NULL; char name[IFNAMSIZ]; @@ -225,16 +225,14 @@ intpr(int interval1, u_long ifnetaddr, void (*pfunc)(char *)) printf("%-7.7s", "Name"); else printf("%-5.5s", "Name"); - printf(" %5.5s %-13.13s %-17.17s %8.8s %5.5s", - "Mtu", "Network", "Address", "Ipkts", "Ierrs"); + printf(" %5.5s %-13.13s %-17.17s %8.8s %5.5s %5.5s", + "Mtu", "Network", "Address", "Ipkts", "Ierrs", "Idrop"); if (bflag) printf(" %10.10s","Ibytes"); printf(" %8.8s %5.5s", "Opkts", "Oerrs"); if (bflag) printf(" %10.10s","Obytes"); printf(" %5s", "Coll"); - if (tflag) - printf(" %s", "Time"); if (dflag) printf(" %s", "Drop"); putchar('\n'); @@ -285,8 +283,8 @@ intpr(int interval1, u_long ifnetaddr, void (*pfunc)(char *)) imcasts = ifnet.if_imcasts; oerrors = ifnet.if_oerrors; ierrors = ifnet.if_ierrors; + idrops = ifnet.if_iqdrops; collisions = ifnet.if_collisions; - timer = ifnet.if_timer; drops = ifnet.if_snd.ifq_drops; if (ifaddraddr == 0) { @@ -423,6 +421,7 @@ intpr(int interval1, u_long ifnetaddr, void (*pfunc)(char *)) show_stat("lu", 8, ipackets, link_layer|network_layer); show_stat("lu", 5, ierrors, link_layer); + show_stat("lu", 5, idrops, link_layer); if (bflag) show_stat("lu", 10, ibytes, link_layer|network_layer); @@ -432,8 +431,6 @@ intpr(int interval1, u_long ifnetaddr, void (*pfunc)(char *)) show_stat("lu", 10, obytes, link_layer|network_layer); show_stat("NRSlu", 5, collisions, link_layer); - if (tflag) - show_stat("LSd", 4, timer, link_layer); if (dflag) show_stat("LSd", 4, drops, link_layer); putchar('\n'); @@ -513,6 +510,7 @@ struct iftot { char ift_name[IFNAMSIZ]; /* interface name */ u_long ift_ip; /* input packets */ u_long ift_ie; /* input errors */ + u_long ift_id; /* input drops */ u_long ift_op; /* output packets */ u_long ift_oe; /* output errors */ u_long ift_co; /* collisions */ @@ -598,8 +596,9 @@ banner: printf("%17s %14s %16s", "input", interesting ? interesting->ift_name : "(Total)", "output"); putchar('\n'); - printf("%10s %5s %10s %10s %5s %10s %5s", - "packets", "errs", "bytes", "packets", "errs", "bytes", "colls"); + printf("%10s %5s %5s %10s %10s %5s %10s %5s", + "packets", "errs", "idrops", "bytes", "packets", "errs", "bytes", + "colls"); if (dflag) printf(" %5.5s", "drops"); putchar('\n'); @@ -615,6 +614,7 @@ loop: if (!first) { show_stat("lu", 10, ifnet.if_ipackets - ip->ift_ip, 1); show_stat("lu", 5, ifnet.if_ierrors - ip->ift_ie, 1); + show_stat("lu", 5, ifnet.if_iqdrops - ip->ift_id, 1); show_stat("lu", 10, ifnet.if_ibytes - ip->ift_ib, 1); show_stat("lu", 10, ifnet.if_opackets - ip->ift_op, 1); show_stat("lu", 5, ifnet.if_oerrors - ip->ift_oe, 1); @@ -636,6 +636,7 @@ loop: } else { sum->ift_ip = 0; sum->ift_ie = 0; + sum->ift_id = 0; sum->ift_ib = 0; sum->ift_op = 0; sum->ift_oe = 0; @@ -651,6 +652,7 @@ loop: } sum->ift_ip += ifnet.if_ipackets; sum->ift_ie += ifnet.if_ierrors; + sum->ift_id += ifnet.if_iqdrops; sum->ift_ib += ifnet.if_ibytes; sum->ift_op += ifnet.if_opackets; sum->ift_oe += ifnet.if_oerrors; @@ -662,6 +664,7 @@ loop: if (!first) { show_stat("lu", 10, sum->ift_ip - total->ift_ip, 1); show_stat("lu", 5, sum->ift_ie - total->ift_ie, 1); + show_stat("lu", 5, sum->ift_id - total->ift_id, 1); show_stat("lu", 10, sum->ift_ib - total->ift_ib, 1); show_stat("lu", 10, sum->ift_op - total->ift_op, 1); show_stat("lu", 5, sum->ift_oe - total->ift_oe, 1); @@ -676,6 +679,8 @@ loop: if (!first) putchar('\n'); fflush(stdout); + if ((noutputs != 0) && (--noutputs == 0)) + exit(0); oldmask = sigblock(sigmask(SIGALRM)); while (!signalled) sigpause(0); diff --git a/usr.bin/netstat/ipsec.c b/usr.bin/netstat/ipsec.c index bfd8ce0..d3276bf 100644 --- a/usr.bin/netstat/ipsec.c +++ b/usr.bin/netstat/ipsec.c @@ -418,6 +418,7 @@ esp_stats(u_long off, const char *name, int family __unused, int proto __unused) static void print_ipcompstats(const struct ipcompstat *ipcompstat) { + uint32_t version; #define p32(f, m) if (ipcompstat->f || sflag <= 1) \ printf("\t%u" m, (unsigned int)ipcompstat->f, plural(ipcompstat->f)) #define p64(f, m) if (ipcompstat->f || sflag <= 1) \ @@ -425,6 +426,11 @@ print_ipcompstats(const struct ipcompstat *ipcompstat) #define hist(f, n, t) \ ipsec_hist_new((f), sizeof(f)/sizeof(f[0]), (n), (t)); +#ifndef IPCOMPSTAT_VERSION + version = 0; +#else + version = ipcompstat->version; +#endif p32(ipcomps_hdrops, " packet%s shorter than header shows\n"); p32(ipcomps_nopf, " packet%s dropped; protocol family not supported\n"); p32(ipcomps_notdb, " packet%s dropped; no TDB\n"); @@ -441,6 +447,10 @@ print_ipcompstats(const struct ipcompstat *ipcompstat) p32(ipcomps_pdrops, " packet%s blocked due to policy\n"); p32(ipcomps_crypto, " crypto processing failure%s\n"); hist(ipcompstat->ipcomps_hist, ipsec_compnames, "COMP output"); + if (version >= 1) { + p32(ipcomps_threshold, " packet%s sent uncompressed; size < compr. algo. threshold\n"); + p32(ipcomps_uncompr, " packet%s sent uncompressed; compression was useless\n"); + } #undef p32 #undef p64 diff --git a/usr.bin/netstat/main.c b/usr.bin/netstat/main.c index ebbe1d2..f54db4e 100644 --- a/usr.bin/netstat/main.c +++ b/usr.bin/netstat/main.c @@ -186,6 +186,8 @@ static struct nlist nl[] = { { .n_name = "_mfctablesize" }, #define N_ARPSTAT 55 { .n_name = "_arpstat" }, +#define N_UNP_SPHEAD 56 + { .n_name = "unp_sphead" }, { .n_name = NULL }, }; @@ -332,12 +334,13 @@ int hflag; /* show counters in human readable format */ int iflag; /* show interfaces */ int Lflag; /* show size of listen queues */ int mflag; /* show memory stats */ +int noutputs = 0; /* how much outputs before we exit */ int numeric_addr; /* show addresses numerically */ int numeric_port; /* show ports numerically */ static int pflag; /* show given protocol */ +int Qflag; /* show netisr information */ int rflag; /* show routing tables (or routing stats) */ int sflag; /* show protocol statistics */ -int tflag; /* show i/f watchdog timers */ int Wflag; /* wide display */ int xflag; /* extra information, includes all socket buffer info */ int zflag; /* zero stats */ @@ -358,7 +361,8 @@ main(int argc, char *argv[]) af = AF_UNSPEC; - while ((ch = getopt(argc, argv, "AaBbdf:ghI:iLlM:mN:np:rSstuWw:xz")) != -1) + while ((ch = getopt(argc, argv, "AaBbdf:ghI:iLlM:mN:np:Qq:rSsuWw:xz")) + != -1) switch(ch) { case 'A': Aflag = 1; @@ -444,6 +448,14 @@ main(int argc, char *argv[]) } pflag = 1; break; + case 'Q': + Qflag = 1; + break; + case 'q': + noutputs = atoi(optarg); + if (noutputs != 0) + noutputs++; + break; case 'r': rflag = 1; break; @@ -453,9 +465,6 @@ main(int argc, char *argv[]) case 'S': numeric_addr = 1; break; - case 't': - tflag = 1; - break; case 'u': af = AF_UNIX; break; @@ -520,6 +529,14 @@ main(int argc, char *argv[]) mbpr(NULL, 0); exit(0); } + if (Qflag) { + if (!live) { + if (kread(0, NULL, 0) == 0) + netisr_stats(kvmd); + } else + netisr_stats(NULL); + exit(0); + } #if 0 /* * Keep file descriptors open to avoid overhead @@ -601,7 +618,8 @@ main(int argc, char *argv[]) #endif /* NETGRAPH */ if ((af == AF_UNIX || af == AF_UNSPEC) && !sflag) unixpr(nl[N_UNP_COUNT].n_value, nl[N_UNP_GENCNT].n_value, - nl[N_UNP_DHEAD].n_value, nl[N_UNP_SHEAD].n_value); + nl[N_UNP_DHEAD].n_value, nl[N_UNP_SHEAD].n_value, + nl[N_UNP_SPHEAD].n_value); exit(0); } @@ -775,12 +793,12 @@ name2protox(const char *name) static void usage(void) { - (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + (void)fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n%s\n", "usage: netstat [-AaLnSWx] [-f protocol_family | -p protocol]\n" " [-M core] [-N system]", -" netstat -i | -I interface [-abdhntW] [-f address_family]\n" +" netstat -i | -I interface [-abdhnW] [-f address_family]\n" " [-M core] [-N system]", -" netstat -w wait [-I interface] [-d] [-M core] [-N system]", +" netstat -w wait [-I interface] [-d] [-M core] [-N system] [-q howmany]", " netstat -s [-s] [-z] [-f protocol_family | -p protocol]\n" " [-M core] [-N system]", " netstat -i | -I interface -s [-f protocol_family | -p protocol]\n" @@ -790,6 +808,7 @@ usage(void) " netstat -r [-AanW] [-f address_family] [-M core] [-N system]", " netstat -rs [-s] [-M core] [-N system]", " netstat -g [-W] [-f address_family] [-M core] [-N system]", -" netstat -gs [-s] [-f address_family] [-M core] [-N system]"); +" netstat -gs [-s] [-f address_family] [-M core] [-N system]", +" netstat -Q"); exit(1); } diff --git a/usr.bin/netstat/netisr.c b/usr.bin/netstat/netisr.c new file mode 100644 index 0000000..733a847 --- /dev/null +++ b/usr.bin/netstat/netisr.c @@ -0,0 +1,518 @@ +/*- + * Copyright (c) 2010 Juniper Networks, Inc. + * All rights reserved. + * + * This software was developed by Robert N. M. Watson under contract + * to 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. + * + * 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. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/sysctl.h> + +#include <sys/_lock.h> +#include <sys/_mutex.h> + +#define _WANT_NETISR_INTERNAL +#include <net/netisr.h> +#include <net/netisr_internal.h> + +#include <err.h> +#include <kvm.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "netstat.h" + +/* + * Print statistics for the kernel netisr subsystem. + */ +static u_int bindthreads; +static u_int maxthreads; +static u_int numthreads; + +static u_int defaultqlimit; +static u_int maxqlimit; + +static u_int direct; +static u_int direct_force; + +static struct sysctl_netisr_proto *proto_array; +static u_int proto_array_len; + +static struct sysctl_netisr_workstream *workstream_array; +static u_int workstream_array_len; + +static struct sysctl_netisr_work *work_array; +static u_int work_array_len; + +static u_int *nws_array; + +static u_int maxprot; + +static void +netisr_load_kvm_uint(kvm_t *kd, char *name, u_int *p) +{ + struct nlist nl[] = { + { .n_name = name }, + { .n_name = NULL }, + }; + int ret; + + ret = kvm_nlist(kd, nl); + if (ret < 0) + errx(-1, "%s: kvm_nlist(%s): %s", __func__, name, + kvm_geterr(kd)); + if (ret != 0) + errx(-1, "%s: kvm_nlist(%s): unresolved symbol", __func__, + name); + if (kvm_read(kd, nl[0].n_value, p, sizeof(*p)) != sizeof(*p)) + errx(-1, "%s: kvm_read(%s): %s", __func__, name, + kvm_geterr(kd)); +} + +/* + * Load a nul-terminated string from KVM up to 'limit', guarantee that the + * string in local memory is nul-terminated. + */ +static void +netisr_load_kvm_string(kvm_t *kd, uintptr_t addr, char *dest, u_int limit) +{ + u_int i; + + for (i = 0; i < limit; i++) { + if (kvm_read(kd, addr + i, &dest[i], sizeof(dest[i])) != + sizeof(dest[i])) + err(-1, "%s: kvm_read: %s", __func__, + kvm_geterr(kd)); + if (dest[i] == '\0') + break; + } + dest[limit - 1] = '\0'; +} + +static const char * +netisr_proto2name(u_int proto) +{ + u_int i; + + for (i = 0; i < proto_array_len; i++) { + if (proto_array[i].snp_proto == proto) + return (proto_array[i].snp_name); + } + return ("unknown"); +} + +static int +netisr_protoispresent(u_int proto) +{ + u_int i; + + for (i = 0; i < proto_array_len; i++) { + if (proto_array[i].snp_proto == proto) + return (1); + } + return (0); +} + +static void +netisr_load_kvm_config(kvm_t *kd) +{ + + netisr_load_kvm_uint(kd, "_netisr_bindthreads", &bindthreads); + netisr_load_kvm_uint(kd, "_netisr_maxthreads", &maxthreads); + netisr_load_kvm_uint(kd, "_nws_count", &numthreads); + + netisr_load_kvm_uint(kd, "_netisr_defaultqlimit", &defaultqlimit); + netisr_load_kvm_uint(kd, "_netisr_maxqlimit", &maxqlimit); + + netisr_load_kvm_uint(kd, "_netisr_direct", &direct); + netisr_load_kvm_uint(kd, "_netisr_direct_force", &direct_force); +} + +static void +netisr_load_sysctl_uint(const char *name, u_int *p) +{ + size_t retlen; + + retlen = sizeof(u_int); + if (sysctlbyname(name, p, &retlen, NULL, 0) < 0) + err(-1, "%s", name); + if (retlen != sizeof(u_int)) + errx(-1, "%s: invalid len %ju", name, (uintmax_t)retlen); +} + +static void +netisr_load_sysctl_config(void) +{ + + netisr_load_sysctl_uint("net.isr.bindthreads", &bindthreads); + netisr_load_sysctl_uint("net.isr.maxthreads", &maxthreads); + netisr_load_sysctl_uint("net.isr.numthreads", &numthreads); + + netisr_load_sysctl_uint("net.isr.defaultqlimit", &defaultqlimit); + netisr_load_sysctl_uint("net.isr.maxqlimit", &maxqlimit); + + netisr_load_sysctl_uint("net.isr.direct", &direct); + netisr_load_sysctl_uint("net.isr.direct_force", &direct_force); +} + +static void +netisr_load_kvm_proto(kvm_t *kd) +{ + struct nlist nl[] = { +#define NLIST_NETISR_PROTO 0 + { .n_name = "_netisr_proto" }, + { .n_name = NULL }, + }; + struct netisr_proto *np_array, *npp; + u_int i, protocount; + struct sysctl_netisr_proto *snpp; + size_t len; + int ret; + + /* + * Kernel compile-time and user compile-time definitions of + * NETISR_MAXPROT must match, as we use that to size work arrays. + */ + netisr_load_kvm_uint(kd, "_netisr_maxprot", &maxprot); + if (maxprot != NETISR_MAXPROT) + errx(-1, "%s: NETISR_MAXPROT mismatch", __func__); + len = maxprot * sizeof(*np_array); + np_array = malloc(len); + if (np_array == NULL) + err(-1, "%s: malloc", __func__); + ret = kvm_nlist(kd, nl); + if (ret < 0) + errx(-1, "%s: kvm_nlist(_netisr_proto): %s", __func__, + kvm_geterr(kd)); + if (ret != 0) + errx(-1, "%s: kvm_nlist(_netisr_proto): unresolved symbol", + __func__); + if (kvm_read(kd, nl[NLIST_NETISR_PROTO].n_value, np_array, len) != + (ssize_t)len) + errx(-1, "%s: kvm_read(_netisr_proto): %s", __func__, + kvm_geterr(kd)); + + /* + * Size and allocate memory to hold only live protocols. + */ + protocount = 0; + for (i = 0; i < maxprot; i++) { + if (np_array[i].np_name == NULL) + continue; + protocount++; + } + proto_array = calloc(protocount, sizeof(*proto_array)); + if (proto_array == NULL) + err(-1, "malloc"); + protocount = 0; + for (i = 0; i < maxprot; i++) { + npp = &np_array[i]; + if (npp->np_name == NULL) + continue; + snpp = &proto_array[protocount]; + snpp->snp_version = sizeof(*snpp); + netisr_load_kvm_string(kd, (uintptr_t)npp->np_name, + snpp->snp_name, sizeof(snpp->snp_name)); + snpp->snp_proto = i; + snpp->snp_qlimit = npp->np_qlimit; + snpp->snp_policy = npp->np_policy; + if (npp->np_m2flow != NULL) + snpp->snp_flags |= NETISR_SNP_FLAGS_M2FLOW; + if (npp->np_m2cpuid != NULL) + snpp->snp_flags |= NETISR_SNP_FLAGS_M2CPUID; + if (npp->np_drainedcpu != NULL) + snpp->snp_flags |= NETISR_SNP_FLAGS_DRAINEDCPU; + protocount++; + } + proto_array_len = protocount; + free(np_array); +} + +static void +netisr_load_sysctl_proto(void) +{ + size_t len; + + if (sysctlbyname("net.isr.proto", NULL, &len, NULL, 0) < 0) + err(-1, "net.isr.proto: query len"); + if (len % sizeof(*proto_array) != 0) + errx(-1, "net.isr.proto: invalid len"); + proto_array = malloc(len); + if (proto_array == NULL) + err(-1, "malloc"); + if (sysctlbyname("net.isr.proto", proto_array, &len, NULL, 0) < 0) + err(-1, "net.isr.proto: query data"); + if (len % sizeof(*proto_array) != 0) + errx(-1, "net.isr.proto: invalid len"); + proto_array_len = len / sizeof(*proto_array); + if (proto_array_len < 1) + errx(-1, "net.isr.proto: no data"); + if (proto_array[0].snp_version != sizeof(proto_array[0])) + errx(-1, "net.isr.proto: invalid version"); +} + +static void +netisr_load_kvm_workstream(kvm_t *kd) +{ + struct nlist nl[] = { +#define NLIST_NWS_ARRAY 0 + { .n_name = "_nws_array" }, + { .n_name = NULL }, + }; + struct netisr_workstream nws; + struct sysctl_netisr_workstream *snwsp; + struct sysctl_netisr_work *snwp; + struct netisr_work *nwp; + struct nlist nl_nws[2]; + u_int counter, cpuid, proto, wsid; + size_t len; + int ret; + + len = numthreads * sizeof(*nws_array); + nws_array = malloc(len); + if (nws_array == NULL) + err(-1, "malloc"); + ret = kvm_nlist(kd, nl); + if (ret < 0) + errx(-1, "%s: kvm_nlist: %s", __func__, kvm_geterr(kd)); + if (ret != 0) + errx(-1, "%s: kvm_nlist: unresolved symbol", __func__); + if (kvm_read(kd, nl[NLIST_NWS_ARRAY].n_value, nws_array, len) != + (ssize_t)len) + errx(-1, "%s: kvm_read(_nws_array): %s", __func__, + kvm_geterr(kd)); + workstream_array = calloc(numthreads, sizeof(*workstream_array)); + if (workstream_array == NULL) + err(-1, "calloc"); + workstream_array_len = numthreads; + work_array = calloc(numthreads * proto_array_len, sizeof(*work_array)); + if (work_array == NULL) + err(-1, "calloc"); + counter = 0; + for (wsid = 0; wsid < numthreads; wsid++) { + cpuid = nws_array[wsid]; + if (kvm_dpcpu_setcpu(kd, cpuid) < 0) + errx(-1, "%s: kvm_dpcpu_setcpu(%u): %s", __func__, + cpuid, kvm_geterr(kd)); + bzero(nl_nws, sizeof(nl_nws)); + nl_nws[0].n_name = "_nws"; + ret = kvm_nlist(kd, nl_nws); + if (ret < 0) + errx(-1, "%s: kvm_nlist looking up nws on CPU %u: %s", + __func__, cpuid, kvm_geterr(kd)); + if (ret != 0) + errx(-1, "%s: kvm_nlist(nws): unresolved symbol on " + "CPU %u", __func__, cpuid); + if (kvm_read(kd, nl_nws[0].n_value, &nws, sizeof(nws)) != + sizeof(nws)) + errx(-1, "%s: kvm_read(nw): %s", __func__, + kvm_geterr(kd)); + snwsp = &workstream_array[wsid]; + snwsp->snws_version = sizeof(*snwsp); + snwsp->snws_wsid = cpuid; + snwsp->snws_cpu = cpuid; + if (nws.nws_intr_event != NULL) + snwsp->snws_flags |= NETISR_SNWS_FLAGS_INTR; + + /* + * Extract the CPU's per-protocol work information. + */ + printf("counting to maxprot: %u\n", maxprot); + for (proto = 0; proto < maxprot; proto++) { + if (!netisr_protoispresent(proto)) + continue; + nwp = &nws.nws_work[proto]; + snwp = &work_array[counter]; + snwp->snw_version = sizeof(*snwp); + snwp->snw_wsid = cpuid; + snwp->snw_proto = proto; + snwp->snw_len = nwp->nw_len; + snwp->snw_watermark = nwp->nw_watermark; + snwp->snw_dispatched = nwp->nw_dispatched; + snwp->snw_hybrid_dispatched = + nwp->nw_hybrid_dispatched; + snwp->snw_qdrops = nwp->nw_qdrops; + snwp->snw_queued = nwp->nw_queued; + snwp->snw_handled = nwp->nw_handled; + counter++; + } + } + work_array_len = counter; +} + +static void +netisr_load_sysctl_workstream(void) +{ + size_t len; + + if (sysctlbyname("net.isr.workstream", NULL, &len, NULL, 0) < 0) + err(-1, "net.isr.workstream: query len"); + if (len % sizeof(*workstream_array) != 0) + errx(-1, "net.isr.workstream: invalid len"); + workstream_array = malloc(len); + if (workstream_array == NULL) + err(-1, "malloc"); + if (sysctlbyname("net.isr.workstream", workstream_array, &len, NULL, + 0) < 0) + err(-1, "net.isr.workstream: query data"); + if (len % sizeof(*workstream_array) != 0) + errx(-1, "net.isr.workstream: invalid len"); + workstream_array_len = len / sizeof(*workstream_array); + if (workstream_array_len < 1) + errx(-1, "net.isr.workstream: no data"); + if (workstream_array[0].snws_version != sizeof(workstream_array[0])) + errx(-1, "net.isr.workstream: invalid version"); +} + +static void +netisr_load_sysctl_work(void) +{ + size_t len; + + if (sysctlbyname("net.isr.work", NULL, &len, NULL, 0) < 0) + err(-1, "net.isr.work: query len"); + if (len % sizeof(*work_array) != 0) + errx(-1, "net.isr.work: invalid len"); + work_array = malloc(len); + if (work_array == NULL) + err(-1, "malloc"); + if (sysctlbyname("net.isr.work", work_array, &len, NULL, 0) < 0) + err(-1, "net.isr.work: query data"); + if (len % sizeof(*work_array) != 0) + errx(-1, "net.isr.work: invalid len"); + work_array_len = len / sizeof(*work_array); + if (work_array_len < 1) + errx(-1, "net.isr.work: no data"); + if (work_array[0].snw_version != sizeof(work_array[0])) + errx(-1, "net.isr.work: invalid version"); +} + +static void +netisr_print_proto(struct sysctl_netisr_proto *snpp) +{ + + printf("%-6s", snpp->snp_name); + printf(" %5u", snpp->snp_proto); + printf(" %6u", snpp->snp_qlimit); + printf(" %6s", + (snpp->snp_policy == NETISR_POLICY_SOURCE) ? "source" : + (snpp->snp_policy == NETISR_POLICY_FLOW) ? "flow" : + (snpp->snp_policy == NETISR_POLICY_CPU) ? "cpu" : "-"); + printf(" %s%s%s\n", + (snpp->snp_flags & NETISR_SNP_FLAGS_M2CPUID) ? "C" : "-", + (snpp->snp_flags & NETISR_SNP_FLAGS_DRAINEDCPU) ? "D" : "-", + (snpp->snp_flags & NETISR_SNP_FLAGS_M2FLOW) ? "F" : "-"); +} + +static void +netisr_print_workstream(struct sysctl_netisr_workstream *snwsp) +{ + struct sysctl_netisr_work *snwp; + int first; + u_int i; + + first = 1; + for (i = 0; i < work_array_len; i++) { + snwp = &work_array[i]; + if (snwp->snw_wsid != snwsp->snws_wsid) + continue; + if (first) { + printf("%4u", snwsp->snws_wsid); + printf(" %3u", snwsp->snws_cpu); + first = 0; + } else + printf("%4s %3s", "", ""); + printf("%2s", ""); + printf("%-6s", netisr_proto2name(snwp->snw_proto)); + printf(" %5u", snwp->snw_len); + printf(" %5u", snwp->snw_watermark); + printf(" %8ju", snwp->snw_dispatched); + printf(" %8ju", snwp->snw_hybrid_dispatched); + printf(" %8ju", snwp->snw_qdrops); + printf(" %8ju", snwp->snw_queued); + printf(" %8ju", snwp->snw_handled); + printf("\n"); + } +} + +void +netisr_stats(void *kvmd) +{ + struct sysctl_netisr_workstream *snwsp; + struct sysctl_netisr_proto *snpp; + kvm_t *kd = kvmd; + u_int i; + + if (live) { + netisr_load_sysctl_config(); + netisr_load_sysctl_proto(); + netisr_load_sysctl_workstream(); + netisr_load_sysctl_work(); + } else { + if (kd == NULL) + errx(-1, "netisr_stats: !live but !kd"); + netisr_load_kvm_config(kd); + netisr_load_kvm_proto(kd); + netisr_load_kvm_workstream(kd); /* Also does work. */ + } + + printf("Configuration:\n"); + printf("%-25s %12s %12s\n", "Setting", "Current", "Limit"); + printf("%-25s %12u %12u\n", "Thread count", numthreads, maxthreads); + printf("%-25s %12u %12u\n", "Default queue limit", defaultqlimit, + maxqlimit); + printf("%-25s %12s %12s\n", "Direct dispatch", + direct ? "enabled" : "disabled", "n/a"); + printf("%-25s %12s %12s\n", "Forced direct dispatch", + direct_force ? "enabled" : "disabled", "n/a"); + printf("%-25s %12s %12s\n", "Threads bound to CPUs", + bindthreads ? "enabled" : "disabled", "n/a"); + printf("\n"); + + printf("Protocols:\n"); + printf("%-6s %5s %6s %-6s %-5s\n", "Name", "Proto", "QLimit", + "Policy", "Flags"); + for (i = 0; i < proto_array_len; i++) { + snpp = &proto_array[i]; + netisr_print_proto(snpp); + } + printf("\n"); + + printf("Workstreams:\n"); + printf("%4s %3s ", "WSID", "CPU"); + printf("%2s", ""); + printf("%-6s %5s %5s %8s %8s %8s %8s %8s\n", "Name", "Len", "WMark", + "Disp'd", "HDisp'd", "QDrops", "Queued", "Handled"); + for (i = 0; i < workstream_array_len; i++) { + snwsp = &workstream_array[i]; + netisr_print_workstream(snwsp); + } +} diff --git a/usr.bin/netstat/netstat.1 b/usr.bin/netstat/netstat.1 index cddaaaf..6cf895b 100644 --- a/usr.bin/netstat/netstat.1 +++ b/usr.bin/netstat/netstat.1 @@ -32,7 +32,7 @@ .\" @(#)netstat.1 8.8 (Berkeley) 4/18/94 .\" $FreeBSD$ .\" -.Dd July 9, 2009 +.Dd February 22, 2010 .Dt NETSTAT 1 .Os .Sh NAME @@ -92,7 +92,7 @@ is present, display socket buffer and tcp timer statistics for each internet soc .Bk -words .Nm .Fl i | I Ar interface -.Op Fl abdhntW +.Op Fl abdhnW .Op Fl f Ar address_family .Op Fl M Ar core .Op Fl N Ar system @@ -123,9 +123,6 @@ If .Fl h is also present, print all counters in human readable form. If -.Fl t -is also present, show the contents of watchdog timers. -If .Fl W is also present, print interface names using a wider field size. .It Xo @@ -136,6 +133,7 @@ is also present, print interface names using a wider field size. .Op Fl d .Op Fl M Ar core .Op Fl N Ar system +.Op Fl q Ar howmany .Ek .Xc At intervals of @@ -146,6 +144,11 @@ traffic on all configured network interfaces or a single .Ar interface . If +.Fl q +is also present, exit after +.Ar howmany +outputs. +If .Fl d is also present, show the number of dropped packets. .It Xo @@ -289,6 +292,15 @@ Show multicast routing statistics. If .Fl s is repeated, counters with a value of zero are suppressed. +.It Xo +.Bk -words +.Nm +.Fl Q +.Ek +.Xc +Show +.Xr netisr 9 +statistics. .El .Pp Some options have the general meaning: diff --git a/usr.bin/netstat/netstat.h b/usr.bin/netstat/netstat.h index f834495..da3f8f3 100644 --- a/usr.bin/netstat/netstat.h +++ b/usr.bin/netstat/netstat.h @@ -45,11 +45,11 @@ extern int hflag; /* show counters in human readable format */ extern int iflag; /* show interfaces */ extern int Lflag; /* show size of listen queues */ extern int mflag; /* show memory stats */ +extern int noutputs; /* how much outputs before we exit */ extern int numeric_addr; /* show addresses numerically */ extern int numeric_port; /* show ports numerically */ extern int rflag; /* show routing tables (or routing stats) */ extern int sflag; /* show protocol statistics */ -extern int tflag; /* show i/f watchdog timers */ extern int Wflag; /* wide display */ extern int xflag; /* extended display, includes all socket buffer info */ extern int zflag; /* zero stats */ @@ -67,6 +67,9 @@ const char *plural(uintmax_t); const char *plurales(uintmax_t); const char *pluralies(uintmax_t); +struct sockaddr; +struct socket; +struct xsocket; int sotoxsocket(struct socket *, struct xsocket *); void protopr(u_long, const char *, int, int); void tcp_stats(u_long, const char *, int, int); @@ -112,6 +115,8 @@ void pfkey_stats(u_long, const char *, int, int); void mbpr(void *, u_long); +void netisr_stats(void *); + void hostpr(u_long, u_long); void impstats(u_long, u_long); @@ -150,7 +155,7 @@ void ddp_stats(u_long, const char *, int, int); void netgraphprotopr(u_long, const char *, int, int); #endif -void unixpr(u_long, u_long, u_long, u_long); +void unixpr(u_long, u_long, u_long, u_long, u_long); void esis_stats(u_long, const char *, int, int); void clnp_stats(u_long, const char *, int, int); diff --git a/usr.bin/netstat/route.c b/usr.bin/netstat/route.c index 735612b..2e9e919 100644 --- a/usr.bin/netstat/route.c +++ b/usr.bin/netstat/route.c @@ -1008,11 +1008,11 @@ rt_stats(u_long rtsaddr, u_long rttaddr) #define p(f, m) if (rtstat.f || sflag <= 1) \ printf(m, rtstat.f, plural(rtstat.f)) - p(rts_badredirect, "\t%u bad routing redirect%s\n"); - p(rts_dynamic, "\t%u dynamically created route%s\n"); - p(rts_newgateway, "\t%u new gateway%s due to redirects\n"); - p(rts_unreach, "\t%u destination%s found unreachable\n"); - p(rts_wildcard, "\t%u use%s of a wildcard route\n"); + p(rts_badredirect, "\t%hu bad routing redirect%s\n"); + p(rts_dynamic, "\t%hu dynamically created route%s\n"); + p(rts_newgateway, "\t%hu new gateway%s due to redirects\n"); + p(rts_unreach, "\t%hu destination%s found unreachable\n"); + p(rts_wildcard, "\t%hu use%s of a wildcard route\n"); #undef p if (rttrash || sflag <= 1) diff --git a/usr.bin/netstat/unix.c b/usr.bin/netstat/unix.c index 209fc8d..0ad8f34 100644 --- a/usr.bin/netstat/unix.c +++ b/usr.bin/netstat/unix.c @@ -193,21 +193,37 @@ fail: } void -unixpr(u_long count_off, u_long gencnt_off, u_long dhead_off, u_long shead_off) +unixpr(u_long count_off, u_long gencnt_off, u_long dhead_off, u_long shead_off, + u_long sphead_off) { char *buf; int ret, type; struct xsocket *so; struct xunpgen *xug, *oxug; struct xunpcb *xunp; + u_long head_off; for (type = SOCK_STREAM; type <= SOCK_SEQPACKET; type++) { if (live) ret = pcblist_sysctl(type, &buf); - else - ret = pcblist_kvm(count_off, gencnt_off, - type == SOCK_STREAM ? shead_off : - (type == SOCK_DGRAM ? dhead_off : 0), &buf); + else { + head_off = 0; + switch (type) { + case SOCK_STREAM: + head_off = shead_off; + break; + + case SOCK_DGRAM: + head_off = dhead_off; + break; + + case SOCK_SEQPACKET: + head_off = sphead_off; + break; + } + ret = pcblist_kvm(count_off, gencnt_off, head_off, + &buf); + } if (ret == -1) continue; if (ret < 0) diff --git a/usr.bin/newkey/Makefile b/usr.bin/newkey/Makefile index 1460f87..b3b5b51 100644 --- a/usr.bin/newkey/Makefile +++ b/usr.bin/newkey/Makefile @@ -11,6 +11,4 @@ MAN= newkey.8 DPADD= ${LIBRPCSVC} ${LIBMP} ${LIBCRYPTO} LDADD= -lrpcsvc -lmp -lcrypto -WARNS?= 6 - .include <bsd.prog.mk> diff --git a/usr.bin/nfsstat/Makefile b/usr.bin/nfsstat/Makefile index ce4c7c6..d31db0a 100644 --- a/usr.bin/nfsstat/Makefile +++ b/usr.bin/nfsstat/Makefile @@ -6,4 +6,6 @@ CFLAGS+=-DNFS DPADD= ${LIBKVM} LDADD= -lkvm +WARNS?= 3 + .include <bsd.prog.mk> diff --git a/usr.bin/nl/Makefile b/usr.bin/nl/Makefile index 9b1f292..07bef878 100644 --- a/usr.bin/nl/Makefile +++ b/usr.bin/nl/Makefile @@ -2,4 +2,6 @@ PROG= nl +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/nl/nl.1 b/usr.bin/nl/nl.1 index f21ce93..064971a 100644 --- a/usr.bin/nl/nl.1 +++ b/usr.bin/nl/nl.1 @@ -14,13 +14,6 @@ .\" 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 NetBSD -.\" Foundation, Inc. and its contributors. -.\" 4. 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 diff --git a/usr.bin/nl/nl.c b/usr.bin/nl/nl.c index 172eb6c..b3b78b1 100644 --- a/usr.bin/nl/nl.c +++ b/usr.bin/nl/nl.c @@ -13,13 +13,6 @@ * 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 NetBSD - * Foundation, Inc. and its contributors. - * 4. 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 diff --git a/usr.bin/nohup/Makefile b/usr.bin/nohup/Makefile index cd635ce..5ec4057 100644 --- a/usr.bin/nohup/Makefile +++ b/usr.bin/nohup/Makefile @@ -2,6 +2,5 @@ # $FreeBSD$ PROG= nohup -WARNS?= 4 .include <bsd.prog.mk> diff --git a/usr.bin/nslookup/Makefile b/usr.bin/nslookup/Makefile index 004efa2..b3ba117 100644 --- a/usr.bin/nslookup/Makefile +++ b/usr.bin/nslookup/Makefile @@ -18,6 +18,8 @@ CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} +WARNS?= 1 + MANFILTER= sed -e "s@^host \[server\]@\\\fBhost\\\fR \\\fI[server]\\\fR@" .include <bsd.prog.mk> diff --git a/usr.bin/nsupdate/Makefile b/usr.bin/nsupdate/Makefile index 0b81c5e..941c7f3 100644 --- a/usr.bin/nsupdate/Makefile +++ b/usr.bin/nsupdate/Makefile @@ -18,6 +18,8 @@ CFLAGS+= -I${BIND_DIR}/lib/isc/${ISC_ATOMIC_ARCH}/include DPADD+= ${BIND_DPADD} ${CRYPTO_DPADD} ${PTHREAD_DPADD} LDADD+= ${BIND_LDADD} ${CRYPTO_LDADD} ${PTHREAD_LDADD} +WARNS?= 1 + MAN= nsupdate.1 MANFILTER= sed -e "s@/etc/named\.conf@/etc/namedb/named.conf@g" \ diff --git a/usr.bin/opieinfo/Makefile b/usr.bin/opieinfo/Makefile index 0375111..b3ba166 100644 --- a/usr.bin/opieinfo/Makefile +++ b/usr.bin/opieinfo/Makefile @@ -8,6 +8,8 @@ CFLAGS+=-I${.CURDIR}/../../lib/libopie CFLAGS+=-I${OPIE_DIST} CFLAGS+=-DINSECURE_OVERRIDE +WARNS?= 0 + DPADD= ${LIBOPIE} ${LIBMD} LDADD= -lopie -lmd diff --git a/usr.bin/opiekey/Makefile b/usr.bin/opiekey/Makefile index 215c86b..e017f4d 100644 --- a/usr.bin/opiekey/Makefile +++ b/usr.bin/opiekey/Makefile @@ -8,6 +8,8 @@ CFLAGS+=-I${.CURDIR}/../../lib/libopie CFLAGS+=-I${OPIE_DIST} CFLAGS+=-DINSECURE_OVERRIDE +WARNS?= 0 + DPADD= ${LIBOPIE} ${LIBMD} LDADD= -lopie -lmd diff --git a/usr.bin/opiepasswd/Makefile b/usr.bin/opiepasswd/Makefile index 0d75a21..b05e3b2 100644 --- a/usr.bin/opiepasswd/Makefile +++ b/usr.bin/opiepasswd/Makefile @@ -8,6 +8,8 @@ CFLAGS+=-I${.CURDIR}/../../lib/libopie CFLAGS+=-I${OPIE_DIST} CFLAGS+=-DINSECURE_OVERRIDE +WARNS?= 0 + DPADD= ${LIBOPIE} ${LIBMD} LDADD= -lopie -lmd diff --git a/usr.bin/passwd/Makefile b/usr.bin/passwd/Makefile index a1ce76b..7aeee56 100644 --- a/usr.bin/passwd/Makefile +++ b/usr.bin/passwd/Makefile @@ -12,7 +12,6 @@ LDADD = ${MINUSLPAM} LINKS = ${BINDIR}/passwd ${BINDIR}/yppasswd MLINKS = passwd.1 yppasswd.1 .endif -WARNS ?= 4 beforeinstall: .for i in passwd yppasswd diff --git a/usr.bin/passwd/passwd.c b/usr.bin/passwd/passwd.c index d35458c..2d399c5 100644 --- a/usr.bin/passwd/passwd.c +++ b/usr.bin/passwd/passwd.c @@ -67,7 +67,7 @@ int main(int argc, char *argv[]) { char hostname[MAXHOSTNAMELEN]; - struct passwd *pwd; + struct passwd *pwd = NULL; /* Keep compiler happy. */ int o, pam_err; uid_t uid; diff --git a/usr.bin/perror/perror.c b/usr.bin/perror/perror.c index 6d852a7..ef3db35 100644 --- a/usr.bin/perror/perror.c +++ b/usr.bin/perror/perror.c @@ -32,6 +32,7 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <string.h> #include <err.h> +#include <locale.h> #include <sys/errno.h> static void usage(void); @@ -43,6 +44,7 @@ main(int argc, char **argv) char *errstr; long errnum; + (void) setlocale(LC_MESSAGES, ""); if (argc != 2) usage(); diff --git a/usr.bin/pr/Makefile b/usr.bin/pr/Makefile index 70a921d..15652dc 100644 --- a/usr.bin/pr/Makefile +++ b/usr.bin/pr/Makefile @@ -1,4 +1,5 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ PROG= pr SRCS= pr.c egetopt.c diff --git a/usr.bin/pr/egetopt.c b/usr.bin/pr/egetopt.c index 277459e..4b41b4a 100644 --- a/usr.bin/pr/egetopt.c +++ b/usr.bin/pr/egetopt.c @@ -46,7 +46,6 @@ __FBSDID("$FreeBSD$"); #include <ctype.h> #include <stdio.h> -#include <stdlib.h> #include <string.h> #include "extern.h" diff --git a/usr.bin/pr/pr.c b/usr.bin/pr/pr.c index fed27f2..ae7ae07 100644 --- a/usr.bin/pr/pr.c +++ b/usr.bin/pr/pr.c @@ -1265,9 +1265,7 @@ FILE * nxtfile(int argc, char **argv, const char **fname, char *buf, int dt) { FILE *inf = NULL; - struct timeval tv; time_t tv_sec; - struct timezone tz; struct tm *timeptr = NULL; struct stat statbuf; static int twice = -1; @@ -1287,14 +1285,13 @@ nxtfile(int argc, char **argv, const char **fname, char *buf, int dt) *fname = fnamedefault; if (nohead) return(inf); - if (gettimeofday(&tv, &tz) < 0) { + if ((tv_sec = time(NULL)) == -1) { ++errcnt; (void)fprintf(err, "pr: cannot get time of day, %s\n", strerror(errno)); eoptind = argc - 1; return(NULL); } - tv_sec = tv.tv_sec; timeptr = localtime(&tv_sec); } for (; eoptind < argc; ++eoptind) { @@ -1311,14 +1308,13 @@ nxtfile(int argc, char **argv, const char **fname, char *buf, int dt) ++eoptind; if (nohead || (dt && twice)) return(inf); - if (gettimeofday(&tv, &tz) < 0) { + if ((tv_sec = time(NULL)) == -1) { ++errcnt; (void)fprintf(err, "pr: cannot get time of day, %s\n", strerror(errno)); return(NULL); } - tv_sec = tv.tv_sec; timeptr = localtime(&tv_sec); } else { /* @@ -1343,14 +1339,13 @@ nxtfile(int argc, char **argv, const char **fname, char *buf, int dt) return(inf); if (dt) { - if (gettimeofday(&tv, &tz) < 0) { + if ((tv_sec = time(NULL)) == -1) { ++errcnt; (void)fprintf(err, "pr: cannot get time of day, %s\n", strerror(errno)); return(NULL); } - tv_sec = tv.tv_sec; timeptr = localtime(&tv_sec); } else { if (fstat(fileno(inf), &statbuf) < 0) { diff --git a/usr.bin/printf/Makefile b/usr.bin/printf/Makefile index 78cdd0d..c91c4e3 100644 --- a/usr.bin/printf/Makefile +++ b/usr.bin/printf/Makefile @@ -2,6 +2,5 @@ # $FreeBSD$ PROG= printf -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/procstat/Makefile b/usr.bin/procstat/Makefile index 6cb5895..1c187b0 100644 --- a/usr.bin/procstat/Makefile +++ b/usr.bin/procstat/Makefile @@ -14,6 +14,5 @@ SRCS= procstat.c \ LDADD+= -lutil DPADD+= ${LIBUTIL} -WARNS?= 4 .include <bsd.prog.mk> diff --git a/usr.bin/procstat/procstat.1 b/usr.bin/procstat/procstat.1 index 0fdff9a..f4dc731 100644 --- a/usr.bin/procstat/procstat.1 +++ b/usr.bin/procstat/procstat.1 @@ -141,6 +141,8 @@ The following file descriptor types may be displayed: .Bl -tag -width X -compact .It c crypto +.It e +POSIX semaphore .It f fifo .It h @@ -168,10 +170,14 @@ not a vnode block device .It c character device +.It d +directory .It f fifo .It l symbolic link +.It r +regular file .It s socket .It x diff --git a/usr.bin/renice/renice.c b/usr.bin/renice/renice.c index 21bb4c2..55f6537 100644 --- a/usr.bin/renice/renice.c +++ b/usr.bin/renice/renice.c @@ -177,7 +177,7 @@ getnum(const char *com, const char *str, int *val) } static void -usage() +usage(void) { fprintf(stderr, "%s\n%s\n", "usage: renice priority [[-p] pid ...] [[-g] pgrp ...] [[-u] user ...]", diff --git a/usr.bin/revoke/Makefile b/usr.bin/revoke/Makefile index b3c4ee6..92fe221 100644 --- a/usr.bin/revoke/Makefile +++ b/usr.bin/revoke/Makefile @@ -2,6 +2,4 @@ PROG= revoke -WARNS?= 6 - .include <bsd.prog.mk> diff --git a/usr.bin/rlogin/Makefile b/usr.bin/rlogin/Makefile index 195bffb..abf4a82 100644 --- a/usr.bin/rlogin/Makefile +++ b/usr.bin/rlogin/Makefile @@ -7,4 +7,6 @@ BINOWN= root BINMODE=4555 PRECIOUSPROG= +WARNS?= 3 + .include <bsd.prog.mk> diff --git a/usr.bin/rpcgen/Makefile b/usr.bin/rpcgen/Makefile index c298820..f78fa64 100644 --- a/usr.bin/rpcgen/Makefile +++ b/usr.bin/rpcgen/Makefile @@ -4,6 +4,4 @@ PROG= rpcgen SRCS= rpc_main.c rpc_clntout.c rpc_cout.c rpc_hout.c rpc_parse.c \ rpc_sample.c rpc_scan.c rpc_svcout.c rpc_tblout.c rpc_util.c -WARNS?= 2 - .include <bsd.prog.mk> diff --git a/usr.bin/rpcgen/rpc_hout.c b/usr.bin/rpcgen/rpc_hout.c index 7068363..7607ef7 100644 --- a/usr.bin/rpcgen/rpc_hout.c +++ b/usr.bin/rpcgen/rpc_hout.c @@ -148,8 +148,7 @@ print_xdr_func_def(const char *name, int pointerp) static void -pconstdef(def) - definition *def; +pconstdef(definition *def) { pdefine(def->def_name, def->def.co); } @@ -205,8 +204,7 @@ pstructdef(definition *def) } static void -puniondef(def) - definition *def; +puniondef(definition *def) { case_list *l; const char *name = def->def_name; @@ -374,8 +372,7 @@ parglist(proc_list *proc, const char *addargtype) } static void -penumdef(def) - definition *def; +penumdef(definition *def) { const char *name = def->def_name; enumval_list *l; @@ -406,8 +403,7 @@ penumdef(def) } static void -ptypedef(def) - definition *def; +ptypedef(definition *def) { const char *name = def->def_name; const char *old = def->def.ty.old_type; diff --git a/usr.bin/rpcgen/rpc_main.c b/usr.bin/rpcgen/rpc_main.c index fb3813f..00d85b8 100644 --- a/usr.bin/rpcgen/rpc_main.c +++ b/usr.bin/rpcgen/rpc_main.c @@ -1203,7 +1203,7 @@ parseargs(int argc, const char *argv[], struct commandline *cmd) } static void -usage() +usage(void) { f_print(stderr, "%s\n%s\n%s\n%s\n%s\n", "usage: rpcgen infile", @@ -1218,7 +1218,7 @@ usage() } static void -options_usage() +options_usage(void) { f_print(stderr, "options:\n"); f_print(stderr, "-a\t\tgenerate all files, including samples\n"); diff --git a/usr.bin/rpcinfo/Makefile b/usr.bin/rpcinfo/Makefile index c89b5b3..3c8e51c 100644 --- a/usr.bin/rpcinfo/Makefile +++ b/usr.bin/rpcinfo/Makefile @@ -7,4 +7,6 @@ MAN= rpcinfo.8 CFLAGS+= -DPORTMAP +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/rsh/Makefile b/usr.bin/rsh/Makefile index cf052f7..5c6951c 100644 --- a/usr.bin/rsh/Makefile +++ b/usr.bin/rsh/Makefile @@ -4,6 +4,8 @@ PROG= rsh CFLAGS+=-I${.CURDIR}/../../libexec/rlogind +WARNS?= 2 + BINOWN= root BINMODE=4555 PRECIOUSPROG= diff --git a/usr.bin/rup/Makefile b/usr.bin/rup/Makefile index 6d32977..e872201 100644 --- a/usr.bin/rup/Makefile +++ b/usr.bin/rup/Makefile @@ -2,6 +2,8 @@ PROG= rup +WARNS?= 1 + DPADD= ${LIBRPCSVC} LDADD= -lrpcsvc diff --git a/usr.bin/ruptime/Makefile b/usr.bin/ruptime/Makefile index 57f1e15..351d0d8 100644 --- a/usr.bin/ruptime/Makefile +++ b/usr.bin/ruptime/Makefile @@ -1,5 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ PROG= ruptime +WARNS?= 3 + .include <bsd.prog.mk> diff --git a/usr.bin/rusers/Makefile b/usr.bin/rusers/Makefile index 21d0ce2..6cb39ac 100644 --- a/usr.bin/rusers/Makefile +++ b/usr.bin/rusers/Makefile @@ -1,7 +1,6 @@ # $FreeBSD$ PROG = rusers -WARNS?= 6 DPADD= ${LIBRPCSVC} LDADD= -lrpcsvc diff --git a/usr.bin/rwho/Makefile b/usr.bin/rwho/Makefile index 328aa38..aee9ff7 100644 --- a/usr.bin/rwho/Makefile +++ b/usr.bin/rwho/Makefile @@ -1,5 +1,8 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ PROG= rwho +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/rwho/rwho.c b/usr.bin/rwho/rwho.c index be82ad3..b41bed8 100644 --- a/usr.bin/rwho/rwho.c +++ b/usr.bin/rwho/rwho.c @@ -61,7 +61,6 @@ __FBSDID("$FreeBSD$"); #include <time.h> #include <timeconv.h> #include <unistd.h> -#include <utmp.h> DIR *dirp; @@ -172,7 +171,7 @@ main(int argc, char *argv[]) (void)sprintf(buf, "%s:%-.*s", mp->myhost, sizeof(mp->myutmp.out_line), mp->myutmp.out_line); printf("%-*.*s %-*s %s", - UT_NAMESIZE, sizeof(mp->myutmp.out_name), + sizeof(mp->myutmp.out_name), sizeof(mp->myutmp.out_name), mp->myutmp.out_name, width, buf, diff --git a/usr.bin/script/script.c b/usr.bin/script/script.c index ad706dc..bd2de28 100644 --- a/usr.bin/script/script.c +++ b/usr.bin/script/script.c @@ -126,7 +126,7 @@ main(int argc, char *argv[]) if ((fscript = fopen(fname, aflg ? "a" : "w")) == NULL) err(1, "%s", fname); - if (ttyflg = isatty(STDIN_FILENO)) { + if ((ttyflg = isatty(STDIN_FILENO)) != 0) { if (tcgetattr(STDIN_FILENO, &tt) == -1) err(1, "tcgetattr"); if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1) diff --git a/usr.bin/sed/Makefile b/usr.bin/sed/Makefile index 9d9886f..1fbce17 100644 --- a/usr.bin/sed/Makefile +++ b/usr.bin/sed/Makefile @@ -4,4 +4,6 @@ PROG= sed SRCS= compile.c main.c misc.c process.c +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/seq/Makefile b/usr.bin/seq/Makefile new file mode 100644 index 0000000..58b16ae --- /dev/null +++ b/usr.bin/seq/Makefile @@ -0,0 +1,8 @@ +# $NetBSD: Makefile,v 1.3 2009/04/14 22:15:26 lukem Exp $ +# $FreeBSD$ + +PROG= seq +DPADD= ${LIBM} +LDADD= -lm + +.include <bsd.prog.mk> diff --git a/usr.bin/seq/seq.1 b/usr.bin/seq/seq.1 new file mode 100644 index 0000000..e3bccee --- /dev/null +++ b/usr.bin/seq/seq.1 @@ -0,0 +1,187 @@ +.\" $NetBSD: seq.1,v 1.6 2008/11/26 15:03:47 ginsbach Exp $ +.\" +.\" Copyright (c) 2005 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Brian Ginsbach. +.\" +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd February 19, 2010 +.Dt SEQ 1 +.Os +.Sh NAME +.Nm seq +.Nd print sequences of numbers +.Sh SYNOPSIS +.Nm +.Op Fl w +.Op Fl f Ar format +.Op Fl s Ar string +.Op Fl t Ar string +.Op Ar first Op Ar incr +.Ar last +.Sh DESCRIPTION +The +.Nm +utility prints a sequence of numbers, one per line +.Pq default , +from +.Ar first +.Pq default 1 , +to near +.Ar last +as possible, in increments of +.Ar incr +.Pq default 1 . +When +.Ar first +is larger than +.Ar last +the default +.Ar incr +is -1. +.Pp +All numbers are interpreted as floating point. +.Pp +Normally integer values are printed as decimal integers. +.Pp +The +.Nm +utility accepts the following options: +.Bl -tag -width Ar +.It Fl f Ar format +Use a +.Xr printf 3 +style +.Ar format +to print each number. +Only the +.Cm E , +.Cm e , +.Cm f , +.Cm G , +.Cm g , +and +.Cm % +conversion characters are valid, along with any optional +flags and an optional numeric minimum field width or precision. +The +.Ar format +can contain character escape sequences in backslash notation as +defined in +.St -ansiC . +The default is +.Cm %g . +.It Fl s Ar string +Use +.Ar string +to separate numbers. +The +.Ar string +can contain character escape sequences in backslash notation as +defined in +.St -ansiC . +The default is +.Cm \en . +.It Fl t Ar string +Use +.Ar string +to terminate sequence of numbers. +The +.Ar string +can contain character escape sequences in backslash notation as +defined in +.St -ansiC . +This option is useful when the default separator +does not contain a +.Cm \en . +.It Fl w +Equalize the widths of all numbers by padding with zeros as necessary. +This option has no effect with the +.Fl f +option. +If any sequence numbers will be printed in exponential notation, +the default conversion is changed to +.Cm %e . +.El +.Pp +The +.Nm +utility exits 0 on success and non-zero if an error occurs. +.Sh EXAMPLES +.Bd -literal -offset indent +# seq 1 3 +1 +2 +3 + +# seq 3 1 +3 +2 +1 + +# seq -w 0 .05 .1 +0.00 +0.05 +0.10 +.Ed +.Sh SEE ALSO +.Xr jot 1 , +.Xr printf 1 , +.Xr printf 3 +.Sh HISTORY +The +.Nm +command first appeared in +.Tn "Plan 9 from Bell Labs" . +A +.Nm +command appeared in +.Nx 3.0 , +and ported to +.Fx 9.0 . +This command was based on the command of the same name in +.Tn "Plan 9 from Bell Labs" +and the +.Tn GNU +core utilities. +The +.Tn GNU +.Nm +command first appeared in the 1.13 shell utilities release. +.Sh BUGS +The +.Fl w +option does not handle the transition from pure floating point +to exponent representation very well. +The +.Nm +command is not bug for bug compatible with the +.Tn "Plan 9 from Bell Labs" +or +.Tn GNU +versions of +.Nm . diff --git a/usr.bin/seq/seq.c b/usr.bin/seq/seq.c new file mode 100644 index 0000000..f94ca00 --- /dev/null +++ b/usr.bin/seq/seq.c @@ -0,0 +1,453 @@ +/* $NetBSD: seq.c,v 1.5 2008/07/21 14:19:26 lukem Exp $ */ +/* + * Copyright (c) 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Brian Ginsbach. + * + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <math.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define ZERO '0' +#define SPACE ' ' + +#define MAX(a, b) (((a) < (b))? (b) : (a)) +#define ISSIGN(c) ((int)(c) == '-' || (int)(c) == '+') +#define ISEXP(c) ((int)(c) == 'e' || (int)(c) == 'E') +#define ISODIGIT(c) ((int)(c) >= '0' && (int)(c) <= '7') + +/* Globals */ + +const char *decimal_point = "."; /* default */ +char default_format[] = { "%g" }; /* default */ + +/* Prototypes */ + +double e_atof(const char *); + +int decimal_places(const char *); +int main(int, char *[]); +int numeric(const char *); +int valid_format(const char *); + +char *generate_format(double, double, double, int, char); +char *unescape(char *); + +/* + * The seq command will print out a numeric sequence from 1, the default, + * to a user specified upper limit by 1. The lower bound and increment + * maybe indicated by the user on the command line. The sequence can + * be either whole, the default, or decimal numbers. + */ +int +main(int argc, char *argv[]) +{ + int c = 0, errflg = 0; + int equalize = 0; + double first = 1.0; + double last = 0.0; + double incr = 0.0; + struct lconv *locale; + char *fmt = NULL; + const char *sep = "\n"; + const char *term = NULL; + char pad = ZERO; + + /* Determine the locale's decimal point. */ + locale = localeconv(); + if (locale && locale->decimal_point && locale->decimal_point[0] != '\0') + decimal_point = locale->decimal_point; + + /* + * Process options, but handle negative numbers separately + * least they trip up getopt(3). + */ + while ((optind < argc) && !numeric(argv[optind]) && + (c = getopt(argc, argv, "f:hs:t:w")) != -1) { + + switch (c) { + case 'f': /* format (plan9) */ + fmt = optarg; + equalize = 0; + break; + case 's': /* separator (GNU) */ + sep = unescape(optarg); + break; + case 't': /* terminator (new) */ + term = unescape(optarg); + break; + case 'w': /* equal width (plan9) */ + if (!fmt) + if (equalize++) + pad = SPACE; + break; + case 'h': /* help (GNU) */ + default: + errflg++; + break; + } + } + + argc -= optind; + argv += optind; + if (argc < 1 || argc > 3) + errflg++; + + if (errflg) { + fprintf(stderr, + "usage: %s [-w] [-f format] [-s string] [-t string] [first [incr]] last\n", + getprogname()); + exit(1); + } + + last = e_atof(argv[argc - 1]); + + if (argc > 1) + first = e_atof(argv[0]); + + if (argc > 2) { + incr = e_atof(argv[1]); + /* Plan 9/GNU don't do zero */ + if (incr == 0.0) + errx(1, "zero %screment", (first < last)? "in" : "de"); + } + + /* default is one for Plan 9/GNU work alike */ + if (incr == 0.0) + incr = (first < last) ? 1.0 : -1.0; + + if (incr <= 0.0 && first < last) + errx(1, "needs positive increment"); + + if (incr >= 0.0 && first > last) + errx(1, "needs negative decrement"); + + if (fmt != NULL) { + if (!valid_format(fmt)) + errx(1, "invalid format string: `%s'", fmt); + fmt = unescape(fmt); + /* + * XXX to be bug for bug compatible with Plan 9 add a + * newline if none found at the end of the format string. + */ + } else + fmt = generate_format(first, incr, last, equalize, pad); + + if (incr > 0) { + for (; first <= last; first += incr) { + printf(fmt, first); + fputs(sep, stdout); + } + } else { + for (; first >= last; first += incr) { + printf(fmt, first); + fputs(sep, stdout); + } + } + if (term != NULL) + fputs(term, stdout); + + return (0); +} + +/* + * numeric - verify that string is numeric + */ +int +numeric(const char *s) +{ + int seen_decimal_pt, decimal_pt_len; + + /* skip any sign */ + if (ISSIGN((unsigned char)*s)) + s++; + + seen_decimal_pt = 0; + decimal_pt_len = strlen(decimal_point); + while (*s) { + if (!isdigit((unsigned char)*s)) { + if (!seen_decimal_pt && + strncmp(s, decimal_point, decimal_pt_len) == 0) { + s += decimal_pt_len; + seen_decimal_pt = 1; + continue; + } + if (ISEXP((unsigned char)*s)) { + s++; + if (ISSIGN((unsigned char)*s) || + isdigit((unsigned char)*s)) { + s++; + continue; + } + } + break; + } + s++; + } + return (*s == '\0'); +} + +/* + * valid_format - validate user specified format string + */ +int +valid_format(const char *fmt) +{ + int conversions = 0; + + while (*fmt != '\0') { + /* scan for conversions */ + if (*fmt != '\0' && *fmt != '%') { + do { + fmt++; + } while (*fmt != '\0' && *fmt != '%'); + } + /* scan a conversion */ + if (*fmt != '\0') { + do { + fmt++; + + /* ok %% */ + if (*fmt == '%') { + fmt++; + break; + } + /* valid conversions */ + if (strchr("eEfgG", *fmt) && + conversions++ < 1) { + fmt++; + break; + } + /* flags, width and precsision */ + if (isdigit((unsigned char)*fmt) || + strchr("+- 0#.", *fmt)) + continue; + + /* oops! bad conversion format! */ + return (0); + } while (*fmt != '\0'); + } + } + + return (conversions <= 1); +} + +/* + * unescape - handle C escapes in a string + */ +char * +unescape(char *orig) +{ + char c, *cp, *new = orig; + int i; + + for (cp = orig; (*orig = *cp); cp++, orig++) { + if (*cp != '\\') + continue; + + switch (*++cp) { + case 'a': /* alert (bell) */ + *orig = '\a'; + continue; + case 'b': /* backspace */ + *orig = '\b'; + continue; + case 'e': /* escape */ + *orig = '\e'; + continue; + case 'f': /* formfeed */ + *orig = '\f'; + continue; + case 'n': /* newline */ + *orig = '\n'; + continue; + case 'r': /* carriage return */ + *orig = '\r'; + continue; + case 't': /* horizontal tab */ + *orig = '\t'; + continue; + case 'v': /* vertical tab */ + *orig = '\v'; + continue; + case '\\': /* backslash */ + *orig = '\\'; + continue; + case '\'': /* single quote */ + *orig = '\''; + continue; + case '\"': /* double quote */ + *orig = '"'; + continue; + case '0': + case '1': + case '2': + case '3': /* octal */ + case '4': + case '5': + case '6': + case '7': /* number */ + for (i = 0, c = 0; + ISODIGIT((unsigned char)*cp) && i < 3; + i++, cp++) { + c <<= 3; + c |= (*cp - '0'); + } + *orig = c; + --cp; + continue; + case 'x': /* hexidecimal number */ + cp++; /* skip 'x' */ + for (i = 0, c = 0; + isxdigit((unsigned char)*cp) && i < 2; + i++, cp++) { + c <<= 4; + if (isdigit((unsigned char)*cp)) + c |= (*cp - '0'); + else + c |= ((toupper((unsigned char)*cp) - + 'A') + 10); + } + *orig = c; + --cp; + continue; + default: + --cp; + break; + } + } + + return (new); +} + +/* + * e_atof - convert an ASCII string to a double + * exit if string is not a valid double, or if converted value would + * cause overflow or underflow + */ +double +e_atof(const char *num) +{ + char *endp; + double dbl; + + errno = 0; + dbl = strtod(num, &endp); + + if (errno == ERANGE) + /* under or overflow */ + err(2, "%s", num); + else if (*endp != '\0') + /* "junk" left in number */ + errx(2, "invalid floating point argument: %s", num); + + /* zero shall have no sign */ + if (dbl == -0.0) + dbl = 0; + return (dbl); +} + +/* + * decimal_places - count decimal places in a number (string) + */ +int +decimal_places(const char *number) +{ + int places = 0; + char *dp; + + /* look for a decimal point */ + if ((dp = strstr(number, decimal_point))) { + dp += strlen(decimal_point); + + while (isdigit((unsigned char)*dp++)) + places++; + } + return (places); +} + +/* + * generate_format - create a format string + * + * XXX to be bug for bug compatable with Plan9 and GNU return "%g" + * when "%g" prints as "%e" (this way no width adjustments are made) + */ +char * +generate_format(double first, double incr, double last, int equalize, char pad) +{ + static char buf[256]; + char cc = '\0'; + int precision, width1, width2, places; + + if (equalize == 0) + return (default_format); + + /* figure out "last" value printed */ + if (first > last) + last = first - incr * floor((first - last) / incr); + else + last = first + incr * floor((last - first) / incr); + + sprintf(buf, "%g", incr); + if (strchr(buf, 'e')) + cc = 'e'; + precision = decimal_places(buf); + + width1 = sprintf(buf, "%g", first); + if (strchr(buf, 'e')) + cc = 'e'; + if ((places = decimal_places(buf))) + width1 -= (places + strlen(decimal_point)); + + precision = MAX(places, precision); + + width2 = sprintf(buf, "%g", last); + if (strchr(buf, 'e')) + cc = 'e'; + if ((places = decimal_places(buf))) + width2 -= (places + strlen(decimal_point)); + + if (precision) { + sprintf(buf, "%%%c%d.%d%c", pad, + MAX(width1, width2) + (int) strlen(decimal_point) + + precision, precision, (cc) ? cc : 'f'); + } else { + sprintf(buf, "%%%c%d%c", pad, MAX(width1, width2), + (cc) ? cc : 'g'); + } + + return (buf); +} diff --git a/usr.bin/showmount/showmount.c b/usr.bin/showmount/showmount.c index 5e67cc1..c70688c 100644 --- a/usr.bin/showmount/showmount.c +++ b/usr.bin/showmount/showmount.c @@ -110,9 +110,7 @@ int tcp_callrpc(const char *host, int prognum, int versnum, int procnum, * for detailed information on the protocol. */ int -main(argc, argv) - int argc; - char **argv; +main(int argc, char **argv) { register struct exportslist *exp; register struct grouplist *grp; @@ -213,15 +211,8 @@ main(argc, argv) * use tcp as transport method in order to handle large replies. */ int -tcp_callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out) - const char *host; - int prognum; - int versnum; - int procnum; - xdrproc_t inproc; - char *in; - xdrproc_t outproc; - char *out; +tcp_callrpc(const char *host, int prognum, int versnum, int procnum, + xdrproc_t inproc, char *in, xdrproc_t outproc, char *out) { CLIENT *client; struct timeval timeout; @@ -245,9 +236,7 @@ tcp_callrpc(host, prognum, versnum, procnum, inproc, in, outproc, out) * Xdr routine for retrieving the mount dump list */ int -xdr_mntdump(xdrsp, mlp) - XDR *xdrsp; - struct mountlist **mlp; +xdr_mntdump(XDR *xdrsp, struct mountlist **mlp) { register struct mountlist *mp; register struct mountlist *tp; @@ -327,9 +316,7 @@ next: * Xdr routine to retrieve exports list */ int -xdr_exportslist(xdrsp, exp) - XDR *xdrsp; - struct exportslist **exp; +xdr_exportslist(XDR *xdrsp, struct exportslist **exp) { register struct exportslist *ep; register struct grouplist *gp; @@ -370,7 +357,7 @@ xdr_exportslist(xdrsp, exp) } static void -usage() +usage(void) { fprintf(stderr, "usage: showmount [-a | -d] [-e3] [host]\n"); exit(1); @@ -380,8 +367,7 @@ usage() * Print the binary tree in inorder so that output is sorted. */ void -print_dump(mp) - struct mountlist *mp; +print_dump(struct mountlist *mp) { if (mp == NULL) diff --git a/usr.bin/smbutil/Makefile b/usr.bin/smbutil/Makefile index 13b5078..19ac5fd 100644 --- a/usr.bin/smbutil/Makefile +++ b/usr.bin/smbutil/Makefile @@ -9,6 +9,8 @@ LDADD= -lsmb -lkiconv CONTRIBDIR= ${.CURDIR}/../../contrib/smbfs CFLAGS+= -I${CONTRIBDIR}/include +WARNS?= 0 + .PATH: ${CONTRIBDIR}/smbutil .include <bsd.prog.mk> diff --git a/usr.bin/sockstat/Makefile b/usr.bin/sockstat/Makefile index 55e7025..8093367 100644 --- a/usr.bin/sockstat/Makefile +++ b/usr.bin/sockstat/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= sockstat -WARNS?= 4 .include <bsd.prog.mk> diff --git a/usr.bin/sockstat/sockstat.1 b/usr.bin/sockstat/sockstat.1 index 206dad7..64e163b 100644 --- a/usr.bin/sockstat/sockstat.1 +++ b/usr.bin/sockstat/sockstat.1 @@ -167,3 +167,10 @@ The .Nm command and this manual page were written by .An Dag-Erling Sm\(/orgrav Aq des@FreeBSD.org . +.Sh BUGS +Unlike +.Xr netstat 1 , +.Nm +lists sockets by walking file descriptor tables and will not output +the ones owned by the kernel, e.g. NLM sockets created by +.Xr rpc.lockd 8 . diff --git a/usr.bin/sockstat/sockstat.c b/usr.bin/sockstat/sockstat.c index 1b2f141..1a5af42 100644 --- a/usr.bin/sockstat/sockstat.c +++ b/usr.bin/sockstat/sockstat.c @@ -496,8 +496,8 @@ printaddr(int af, struct sockaddr_storage *ss) { char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' }; struct sockaddr_un *sun; - void *addr; - int off, port; + void *addr = NULL; /* Keep compiler happy. */ + int off, port = 0; switch (af) { case AF_INET: diff --git a/usr.bin/stat/stat.1 b/usr.bin/stat/stat.1 index b629341..6856efa 100644 --- a/usr.bin/stat/stat.1 +++ b/usr.bin/stat/stat.1 @@ -14,13 +14,6 @@ .\" 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 NetBSD -.\" Foundation, Inc. and its contributors. -.\" 4. 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 diff --git a/usr.bin/stat/stat.c b/usr.bin/stat/stat.c index 0b32f11..83d389b 100644 --- a/usr.bin/stat/stat.c +++ b/usr.bin/stat/stat.c @@ -13,13 +13,6 @@ * 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 NetBSD - * Foundation, Inc. and its contributors. - * 4. 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 diff --git a/usr.bin/su/Makefile b/usr.bin/su/Makefile index 25216cf..0002e86 100644 --- a/usr.bin/su/Makefile +++ b/usr.bin/su/Makefile @@ -5,6 +5,8 @@ PROG= su +WARNS?= 5 + DPADD= ${LIBUTIL} ${LIBPAM} LDADD= -lutil ${MINUSLPAM} diff --git a/usr.bin/systat/Makefile b/usr.bin/systat/Makefile index 4f5f229..6a7e53d 100644 --- a/usr.bin/systat/Makefile +++ b/usr.bin/systat/Makefile @@ -14,7 +14,9 @@ SRCS+= icmp6.c ip6.c CFLAGS+= -DINET6 .endif -DPADD= ${LIBCURSES} ${LIBM} ${LIBDEVSTAT} ${LIBKVM} -LDADD= -lcurses -lm -ldevstat -lkvm +WARNS?= 0 + +DPADD= ${LIBNCURSESW} ${LIBM} ${LIBDEVSTAT} ${LIBKVM} +LDADD= -lncursesw -lm -ldevstat -lkvm .include <bsd.prog.mk> diff --git a/usr.bin/systat/keyboard.c b/usr.bin/systat/keyboard.c index 7784f5b..0ab28eb 100644 --- a/usr.bin/systat/keyboard.c +++ b/usr.bin/systat/keyboard.c @@ -39,8 +39,10 @@ __FBSDID("$FreeBSD$"); static const char sccsid[] = "@(#)keyboard.c 8.1 (Berkeley) 6/6/93"; #endif +#include <errno.h> #include <ctype.h> #include <signal.h> +#include <stdlib.h> #include <termios.h> #include "systat.h" @@ -57,10 +59,11 @@ keyboard(void) move(CMDLINE, 0); do { refresh(); - ch = getch() & 0177; - if (ch == 0177 && ferror(stdin)) { - clearerr(stdin); - continue; + ch = getch(); + if (ch == ERR) { + if (errno == EINTR) + continue; + exit(1); } if (ch >= 'A' && ch <= 'Z') ch += 'a' - 'A'; diff --git a/usr.bin/systat/main.c b/usr.bin/systat/main.c index 28673e5..d092f10 100644 --- a/usr.bin/systat/main.c +++ b/usr.bin/systat/main.c @@ -87,7 +87,7 @@ main(int argc, char **argv) char errbuf[_POSIX2_LINE_MAX], dummy; size_t size; - (void) setlocale(LC_TIME, ""); + (void) setlocale(LC_ALL, ""); argc--, argv++; while (argc > 0) { @@ -133,6 +133,7 @@ main(int argc, char **argv) exit(1); } } + signal(SIGHUP, die); signal(SIGINT, die); signal(SIGQUIT, die); signal(SIGTERM, die); diff --git a/usr.bin/systat/vmstat.c b/usr.bin/systat/vmstat.c index 3866438..1a02197 100644 --- a/usr.bin/systat/vmstat.c +++ b/usr.bin/systat/vmstat.c @@ -66,7 +66,7 @@ static const char sccsid[] = "@(#)vmstat.c 8.2 (Berkeley) 1/12/94"; #include <string.h> #include <time.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include <devstat.h> #include "systat.h" #include "extern.h" @@ -141,7 +141,6 @@ static void putlongdouble(long double, int, int, int, int, int); static int ucount(void); static int ncpu; -static int ut; static char buf[26]; static time_t t; static double etime; @@ -150,16 +149,10 @@ static long *intrloc; static char **intrname; static int nextintsrow; -struct utmp utmp; - - WINDOW * openkre(void) { - ut = open(_PATH_UTMP, O_RDONLY); - if (ut < 0) - error("No utmp"); return (stdscr); } @@ -167,7 +160,6 @@ void closekre(WINDOW *w) { - (void) close(ut); if (w == NULL) return; wclear(w); @@ -634,14 +626,14 @@ static int ucount(void) { int nusers = 0; + struct utmpx *ut; - if (ut < 0) - return (0); - while (read(ut, &utmp, sizeof(utmp))) - if (utmp.ut_name[0] != '\0') + setutxent(); + while ((ut = getutxent()) != NULL) + if (ut->ut_type == USER_PROCESS) nusers++; + endutxent(); - lseek(ut, 0L, L_SET); return (nusers); } diff --git a/usr.bin/tail/Makefile b/usr.bin/tail/Makefile index c816693..672cbed 100644 --- a/usr.bin/tail/Makefile +++ b/usr.bin/tail/Makefile @@ -3,6 +3,5 @@ PROG= tail SRCS= forward.c misc.c read.c reverse.c tail.c -WARNS?= 4 .include <bsd.prog.mk> diff --git a/usr.bin/tail/misc.c b/usr.bin/tail/misc.c index 7e57504..26b162a 100644 --- a/usr.bin/tail/misc.c +++ b/usr.bin/tail/misc.c @@ -63,7 +63,7 @@ ierr(const char *fname) } void -oerr() +oerr(void) { err(1, "stdout"); } diff --git a/usr.bin/tail/tail.1 b/usr.bin/tail/tail.1 index 7f91e71..5e382e9 100644 --- a/usr.bin/tail/tail.1 +++ b/usr.bin/tail/tail.1 @@ -35,7 +35,7 @@ .\" @(#)tail.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd June 05, 2009 +.Dd June 5, 2009 .Dt TAIL 1 .Os .Sh NAME diff --git a/usr.bin/talk/ctl_transact.c b/usr.bin/talk/ctl_transact.c index 9c8665d..ff5b462 100644 --- a/usr.bin/talk/ctl_transact.c +++ b/usr.bin/talk/ctl_transact.c @@ -42,7 +42,6 @@ static const char sccsid[] = "@(#)ctl_transact.c 8.1 (Berkeley) 6/6/93"; #include <arpa/inet.h> #include <errno.h> -#include <string.h> #include "talk.h" #include "talk_ctl.h" diff --git a/usr.bin/talk/display.c b/usr.bin/talk/display.c index f565354..11f5c80 100644 --- a/usr.bin/talk/display.c +++ b/usr.bin/talk/display.c @@ -44,6 +44,7 @@ static const char sccsid[] = "@(#)display.c 8.1 (Berkeley) 6/6/93"; * displaying of text */ #include <ctype.h> +#include <unistd.h> #include "talk.h" diff --git a/usr.bin/talk/get_addrs.c b/usr.bin/talk/get_addrs.c index 2923021..fbd415b 100644 --- a/usr.bin/talk/get_addrs.c +++ b/usr.bin/talk/get_addrs.c @@ -42,6 +42,7 @@ static const char sccsid[] = "@(#)get_addrs.c 8.1 (Berkeley) 6/6/93"; #include <err.h> #include <netdb.h> #include <string.h> +#include <unistd.h> #include "talk.h" #include "talk_ctl.h" diff --git a/usr.bin/talk/get_iface.c b/usr.bin/talk/get_iface.c index ebf08bd..96d3d6e 100644 --- a/usr.bin/talk/get_iface.c +++ b/usr.bin/talk/get_iface.c @@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$"); #include <errno.h> #include <string.h> +#include <unistd.h> #include "talk.h" diff --git a/usr.bin/talk/get_names.c b/usr.bin/talk/get_names.c index b3e0f7b..a4ea39d 100644 --- a/usr.bin/talk/get_names.c +++ b/usr.bin/talk/get_names.c @@ -45,6 +45,7 @@ static const char sccsid[] = "@(#)get_names.c 8.1 (Berkeley) 6/6/93"; #include <pwd.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include "talk.h" diff --git a/usr.bin/talk/invite.c b/usr.bin/talk/invite.c index 99c2514..d94362b 100644 --- a/usr.bin/talk/invite.c +++ b/usr.bin/talk/invite.c @@ -47,6 +47,7 @@ static const char sccsid[] = "@(#)invite.c 8.1 (Berkeley) 6/6/93"; #include <errno.h> #include <setjmp.h> #include <signal.h> +#include <unistd.h> #include "talk_ctl.h" #include "talk.h" diff --git a/usr.bin/talk/look_up.c b/usr.bin/talk/look_up.c index 1ccf23c..7a66e6f 100644 --- a/usr.bin/talk/look_up.c +++ b/usr.bin/talk/look_up.c @@ -45,6 +45,7 @@ static const char sccsid[] = "@(#)look_up.c 8.1 (Berkeley) 6/6/93"; #include <errno.h> #include <string.h> +#include <unistd.h> #include "talk_ctl.h" #include "talk.h" diff --git a/usr.bin/talk/talk.1 b/usr.bin/talk/talk.1 index 775b6d1..6af62d7 100644 --- a/usr.bin/talk/talk.1 +++ b/usr.bin/talk/talk.1 @@ -116,10 +116,10 @@ Permission to talk may be denied or granted by use of the command. At the outset talking is allowed. .Sh FILES -.Bl -tag -width /var/run/utmp -compact +.Bl -tag -width /var/run/utx.active -compact .It Pa /etc/hosts to find the recipient's machine -.It Pa /var/run/utmp +.It Pa /var/run/utx.active to find the recipient's tty .El .Sh SEE ALSO diff --git a/usr.bin/talk/talk.c b/usr.bin/talk/talk.c index 5fc8337..578e370 100644 --- a/usr.bin/talk/talk.c +++ b/usr.bin/talk/talk.c @@ -46,6 +46,7 @@ static const char copyright[] = #endif #include <locale.h> +#include <unistd.h> #include "talk.h" diff --git a/usr.bin/talk/talk.h b/usr.bin/talk/talk.h index e20c8ec..47cbbae 100644 --- a/usr.bin/talk/talk.h +++ b/usr.bin/talk/talk.h @@ -42,7 +42,6 @@ #include <arpa/inet.h> #include <protocols/talkd.h> #include <curses.h> -#include <unistd.h> extern int sockt; extern int curses_initialized; diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile index 22f8ff7..1828952 100644 --- a/usr.bin/tar/Makefile +++ b/usr.bin/tar/Makefile @@ -3,11 +3,22 @@ PROG= bsdtar BSDTAR_VERSION_STRING=2.7.0 -SRCS= bsdtar.c cmdline.c getdate.c matching.c read.c siginfo.c subst.c tree.c util.c write.c -WARNS?= 5 -DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ} +SRCS= bsdtar.c \ + cmdline.c \ + err.c \ + getdate.c \ + line_reader.c \ + matching.c \ + pathmatch.c \ + read.c \ + subst.c \ + tree.c \ + util.c \ + write.c +DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ} ${LIBMD} LDADD= -larchive -lbz2 -lz -lmd .if ${MK_OPENSSL} != "no" +DPADD+= ${LIBCRYPTO} LDADD+= -lcrypto .endif CFLAGS+= -DBSDTAR_VERSION_STRING=\"${BSDTAR_VERSION_STRING}\" diff --git a/usr.bin/tar/bsdtar.c b/usr.bin/tar/bsdtar.c index cd6fa63..c858ba3 100644 --- a/usr.bin/tar/bsdtar.c +++ b/usr.bin/tar/bsdtar.c @@ -47,6 +47,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_PATHS_H #include <paths.h> #endif +#ifdef HAVE_SIGNAL_H +#include <signal.h> +#endif #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -60,11 +63,10 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_UNISTD_H #include <unistd.h> #endif -#if HAVE_ZLIB_H -#include <zlib.h> -#endif #include "bsdtar.h" +#include "err.h" +#include "matching.h" /* * Per POSIX.1-1988, tar defaults to reading/writing archives to/from @@ -81,10 +83,39 @@ __FBSDID("$FreeBSD$"); #define _PATH_DEFTAPE "/dev/tape" #endif -/* External function to parse a date/time string (from getdate.y) */ +#ifdef __MINGW32__ +int _CRT_glob = 0; /* Disable broken CRT globbing. */ +#endif + +#if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1)) +static volatile int siginfo_occurred; + +static void +siginfo_handler(int sig) +{ + (void)sig; /* UNUSED */ + siginfo_occurred = 1; +} + +int +need_report(void) +{ + int r = siginfo_occurred; + siginfo_occurred = 0; + return (r); +} +#else +int +need_report(void) +{ + return (0); +} +#endif + +/* External function to parse a date/time string */ time_t get_date(time_t, const char *); -static void long_help(struct bsdtar *); +static void long_help(void); static void only_mode(struct bsdtar *, const char *opt, const char *valid); static void set_mode(struct bsdtar *, char opt); @@ -113,32 +144,44 @@ main(int argc, char **argv) memset(bsdtar, 0, sizeof(*bsdtar)); bsdtar->fd = -1; /* Mark as "unused" */ option_o = 0; -#if defined(_WIN32) && !defined(__CYGWIN__) - /* Make sure open() function will be used with a binary mode. */ - /* on cygwin, we need something similar, but instead link against */ - /* a special startup object, binmode.o */ - _set_fmode(_O_BINARY); + +#if defined(HAVE_SIGACTION) && (defined(SIGINFO) || defined(SIGUSR1)) + { /* Catch SIGINFO and SIGUSR1, if they exist. */ + struct sigaction sa; + sa.sa_handler = siginfo_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; +#ifdef SIGINFO + if (sigaction(SIGINFO, &sa, NULL)) + bsdtar_errc(1, errno, "sigaction(SIGINFO) failed"); +#endif +#ifdef SIGUSR1 + /* ... and treat SIGUSR1 the same way as SIGINFO. */ + if (sigaction(SIGUSR1, &sa, NULL)) + bsdtar_errc(1, errno, "sigaction(SIGUSR1) failed"); +#endif + } #endif - /* Need bsdtar->progname before calling bsdtar_warnc. */ + /* Need bsdtar_progname before calling bsdtar_warnc. */ if (*argv == NULL) - bsdtar->progname = "bsdtar"; + bsdtar_progname = "bsdtar"; else { #if defined(_WIN32) && !defined(__CYGWIN__) - bsdtar->progname = strrchr(*argv, '\\'); + bsdtar_progname = strrchr(*argv, '\\'); #else - bsdtar->progname = strrchr(*argv, '/'); + bsdtar_progname = strrchr(*argv, '/'); #endif - if (bsdtar->progname != NULL) - bsdtar->progname++; + if (bsdtar_progname != NULL) + bsdtar_progname++; else - bsdtar->progname = *argv; + bsdtar_progname = *argv; } time(&now); if (setlocale(LC_ALL, "") == NULL) - bsdtar_warnc(bsdtar, 0, "Failed to set default locale"); + bsdtar_warnc(0, "Failed to set default locale"); #if defined(HAVE_NL_LANGINFO) && defined(HAVE_D_MD_ORDER) bsdtar->day_first = (*nl_langinfo(D_MD_ORDER) == 'd'); #endif @@ -158,8 +201,10 @@ main(int argc, char **argv) /* Default: Perform basic security checks. */ bsdtar->extract_flags |= SECURITY; - /* Defaults for root user: */ - if (bsdtar_is_privileged(bsdtar)) { +#ifndef _WIN32 + /* On POSIX systems, assume --same-owner and -p when run by + * the root user. This doesn't make any sense on Windows. */ + if (bsdtar->user_uid == 0) { /* --same-owner */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER; /* -p */ @@ -168,6 +213,7 @@ main(int argc, char **argv) bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR; bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS; } +#endif bsdtar->argv = argv; bsdtar->argc = argc; @@ -185,9 +231,9 @@ main(int argc, char **argv) break; case 'b': /* SUSv2 */ t = atoi(bsdtar->optarg); - if (t <= 0 || t > 1024) - bsdtar_errc(bsdtar, 1, 0, - "Argument to -b is out of range (1..1024)"); + if (t <= 0 || t > 8192) + bsdtar_errc(1, 0, + "Argument to -b is out of range (1..8192)"); bsdtar->bytes_per_block = 512 * t; break; case 'C': /* GNU tar */ @@ -203,8 +249,8 @@ main(int argc, char **argv) bsdtar->option_chroot = 1; break; case OPTION_EXCLUDE: /* GNU tar */ - if (exclude(bsdtar, bsdtar->optarg)) - bsdtar_errc(bsdtar, 1, 0, + if (lafe_exclude(&bsdtar->matching, bsdtar->optarg)) + bsdtar_errc(1, 0, "Couldn't exclude %s\n", bsdtar->optarg); break; case OPTION_FORMAT: /* GNU tar, others */ @@ -227,7 +273,7 @@ main(int argc, char **argv) possible_help_request = 1; break; case OPTION_HELP: /* GNU tar, others */ - long_help(bsdtar); + long_help(); exit(0); break; case 'I': /* GNU tar */ @@ -249,36 +295,24 @@ main(int argc, char **argv) * noone else needs this to filter entries * when transforming archives. */ - if (include(bsdtar, bsdtar->optarg)) - bsdtar_errc(bsdtar, 1, 0, + if (lafe_include(&bsdtar->matching, bsdtar->optarg)) + bsdtar_errc(1, 0, "Failed to add %s to inclusion list", bsdtar->optarg); break; case 'j': /* GNU tar */ -#if HAVE_LIBBZ2 if (bsdtar->create_compression != '\0') - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; -#else - bsdtar_warnc(bsdtar, 0, - "bzip2 compression not supported by this version of bsdtar"); - usage(bsdtar); -#endif break; case 'J': /* GNU tar 1.21 and later */ -#if HAVE_LIBLZMA if (bsdtar->create_compression != '\0') - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; -#else - bsdtar_warnc(bsdtar, 0, - "xz compression not supported by this version of bsdtar"); - usage(bsdtar); -#endif break; case 'k': /* GNU tar */ bsdtar->extract_flags |= ARCHIVE_EXTRACT_NO_OVERWRITE; @@ -294,17 +328,11 @@ main(int argc, char **argv) bsdtar->option_warn_links = 1; break; case OPTION_LZMA: -#if HAVE_LIBLZMA if (bsdtar->create_compression != '\0') - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; -#else - bsdtar_warnc(bsdtar, 0, - "lzma compression not supported by this version of bsdtar"); - usage(bsdtar); -#endif break; case 'm': /* SUSv2 */ bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_TIME; @@ -326,7 +354,7 @@ main(int argc, char **argv) { struct stat st; if (stat(bsdtar->optarg, &st) != 0) - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't open file %s", bsdtar->optarg); bsdtar->newer_ctime_sec = st.st_ctime; bsdtar->newer_ctime_nsec = @@ -340,7 +368,7 @@ main(int argc, char **argv) { struct stat st; if (stat(bsdtar->optarg, &st) != 0) - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't open file %s", bsdtar->optarg); bsdtar->newer_mtime_sec = st.st_mtime; bsdtar->newer_mtime_nsec = @@ -411,9 +439,9 @@ main(int argc, char **argv) #if HAVE_REGEX_H add_substitution(bsdtar, bsdtar->optarg); #else - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "-s is not supported by this version of bsdtar"); - usage(bsdtar); + usage(); #endif break; case OPTION_SAME_OWNER: /* GNU tar */ @@ -457,8 +485,8 @@ main(int argc, char **argv) bsdtar->option_interactive = 1; break; case 'X': /* GNU tar */ - if (exclude_from_file(bsdtar, bsdtar->optarg)) - bsdtar_errc(bsdtar, 1, 0, + if (lafe_exclude_from_file(&bsdtar->matching, bsdtar->optarg)) + bsdtar_errc(1, 0, "failed to process exclusions from file %s", bsdtar->optarg); break; @@ -466,43 +494,31 @@ main(int argc, char **argv) set_mode(bsdtar, opt); break; case 'y': /* FreeBSD version of GNU tar */ -#if HAVE_LIBBZ2 if (bsdtar->create_compression != '\0') - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; -#else - bsdtar_warnc(bsdtar, 0, - "bzip2 compression not supported by this version of bsdtar"); - usage(bsdtar); -#endif break; case 'Z': /* GNU tar */ if (bsdtar->create_compression != '\0') - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; break; case 'z': /* GNU tar, star, many others */ -#if HAVE_LIBZ if (bsdtar->create_compression != '\0') - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->create_compression); bsdtar->create_compression = opt; -#else - bsdtar_warnc(bsdtar, 0, - "gzip compression not supported by this version of bsdtar"); - usage(bsdtar); -#endif break; case OPTION_USE_COMPRESS_PROGRAM: bsdtar->compress_program = bsdtar->optarg; break; default: - usage(bsdtar); + usage(); } } @@ -512,13 +528,13 @@ main(int argc, char **argv) /* If no "real" mode was specified, treat -h as --help. */ if ((bsdtar->mode == '\0') && possible_help_request) { - long_help(bsdtar); + long_help(); exit(0); } /* Otherwise, a mode is required. */ if (bsdtar->mode == '\0') - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Must specify one of -c, -r, -t, -u, -x"); /* Check boolean options only permitted in certain modes. */ @@ -592,13 +608,13 @@ main(int argc, char **argv) break; } - cleanup_exclusions(bsdtar); + lafe_cleanup_exclusions(&bsdtar->matching); #if HAVE_REGEX_H cleanup_substitution(bsdtar); #endif if (bsdtar->return_value != 0) - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Error exit delayed from previous errors."); return (bsdtar->return_value); } @@ -607,7 +623,7 @@ static void set_mode(struct bsdtar *bsdtar, char opt) { if (bsdtar->mode != '\0' && bsdtar->mode != opt) - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't specify both -%c and -%c", opt, bsdtar->mode); bsdtar->mode = opt; } @@ -619,18 +635,18 @@ static void only_mode(struct bsdtar *bsdtar, const char *opt, const char *valid_modes) { if (strchr(valid_modes, bsdtar->mode) == NULL) - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Option %s is not permitted in mode -%c", opt, bsdtar->mode); } void -usage(struct bsdtar *bsdtar) +usage(void) { const char *p; - p = bsdtar->progname; + p = bsdtar_progname; fprintf(stderr, "Usage:\n"); fprintf(stderr, " List: %s -tf <archive-filename>\n", p); @@ -685,12 +701,12 @@ static const char *long_help_msg = * echo bsdtar; else echo not bsdtar; fi */ static void -long_help(struct bsdtar *bsdtar) +long_help(void) { const char *prog; const char *p; - prog = bsdtar->progname; + prog = bsdtar_progname; fflush(stderr); diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h index f0f3e54..919156a 100644 --- a/usr.bin/tar/bsdtar.h +++ b/usr.bin/tar/bsdtar.h @@ -28,6 +28,8 @@ #include "bsdtar_platform.h" #include <stdio.h> +#include "matching.h" + #define DEFAULT_BYTES_PER_BLOCK (20*512) /* @@ -77,8 +79,6 @@ struct bsdtar { int fd; /* Miscellaneous state information */ - struct archive *archive; - const char *progname; int argc; char **argv; const char *optarg; @@ -98,7 +98,7 @@ struct bsdtar { struct archive_dir *archive_dir; /* for write.c */ struct name_cache *gname_cache; /* for write.c */ char *buff; /* for write.c */ - struct matching *matching; /* for matching.c */ + struct lafe_matching *matching; /* for matching.c */ struct security *security; /* for read.c */ struct name_cache *uname_cache; /* for write.c */ struct siginfo_data *siginfo; /* for siginfo.c */ @@ -134,37 +134,20 @@ enum { OPTION_VERSION }; - -void bsdtar_errc(struct bsdtar *, int _eval, int _code, - const char *fmt, ...) __LA_DEAD; int bsdtar_getopt(struct bsdtar *); -void bsdtar_warnc(struct bsdtar *, int _code, const char *fmt, ...); -void cleanup_exclusions(struct bsdtar *); void do_chdir(struct bsdtar *); int edit_pathname(struct bsdtar *, struct archive_entry *); -int exclude(struct bsdtar *, const char *pattern); -int exclude_from_file(struct bsdtar *, const char *pathname); -int excluded(struct bsdtar *, const char *pathname); -int include(struct bsdtar *, const char *pattern); -int include_from_file(struct bsdtar *, const char *pathname); +int need_report(void); int pathcmp(const char *a, const char *b); -int process_lines(struct bsdtar *bsdtar, const char *pathname, - int (*process)(struct bsdtar *, const char *)); void safe_fprintf(FILE *, const char *fmt, ...); void set_chdir(struct bsdtar *, const char *newdir); -void siginfo_init(struct bsdtar *); -void siginfo_setinfo(struct bsdtar *, const char * oper, - const char * path, int64_t size); -void siginfo_printinfo(struct bsdtar *, off_t progress); -void siginfo_done(struct bsdtar *); +const char *tar_i64toa(int64_t); void tar_mode_c(struct bsdtar *bsdtar); void tar_mode_r(struct bsdtar *bsdtar); void tar_mode_t(struct bsdtar *bsdtar); void tar_mode_u(struct bsdtar *bsdtar); void tar_mode_x(struct bsdtar *bsdtar); -int unmatched_inclusions(struct bsdtar *bsdtar); -int unmatched_inclusions_warn(struct bsdtar *bsdtar, const char *msg); -void usage(struct bsdtar *); +void usage(void); int yes(const char *fmt, ...); #if HAVE_REGEX_H diff --git a/usr.bin/tar/bsdtar_platform.h b/usr.bin/tar/bsdtar_platform.h index fb14442..5ad8d30 100644 --- a/usr.bin/tar/bsdtar_platform.h +++ b/usr.bin/tar/bsdtar_platform.h @@ -37,19 +37,18 @@ #if defined(PLATFORM_CONFIG_H) /* Use hand-built config.h in environments that need it. */ #include PLATFORM_CONFIG_H -#elif defined(HAVE_CONFIG_H) -/* Most POSIX platforms use the 'configure' script to build config.h */ -#include "config.h" #else -/* Warn if bsdtar hasn't been (automatically or manually) configured. */ -#error Oops: No config.h and no built-in configuration in bsdtar_platform.h. -#endif /* !HAVE_CONFIG_H */ +/* Not having a config.h of some sort is a serious problem. */ +#include "config.h" +#endif -/* No non-FreeBSD platform will have __FBSDID, so just define it here. */ -#ifdef __FreeBSD__ -#include <sys/cdefs.h> /* For __FBSDID */ -#else -/* Just leaving this macro replacement empty leads to a dangling semicolon. */ +/* Get a real definition for __FBSDID if we can */ +#if HAVE_SYS_CDEFS_H +#include <sys/cdefs.h> +#endif + +/* If not, define it so as to avoid dangling semicolons. */ +#ifndef __FBSDID #define __FBSDID(a) struct _undefined_hack #endif @@ -64,29 +63,6 @@ #endif /* - * Does this platform have complete-looking POSIX-style ACL support, - * including some variant of the acl_get_perm() function (which was - * omitted from the POSIX.1e draft)? - */ -#if HAVE_SYS_ACL_H && HAVE_ACL_PERMSET_T && HAVE_ACL_USER -#if HAVE_ACL_GET_PERM || HAVE_ACL_GET_PERM_NP -#define HAVE_POSIX_ACL 1 -#endif -#endif - -#ifdef HAVE_LIBACL -#include <acl/libacl.h> -#endif - -#if HAVE_ACL_GET_PERM -#define ACL_GET_PERM acl_get_perm -#else -#if HAVE_ACL_GET_PERM_NP -#define ACL_GET_PERM acl_get_perm_np -#endif -#endif - -/* * Include "dirent.h" (or it's equivalent on several different platforms). * * This is slightly modified from the GNU autoconf recipe. @@ -115,25 +91,6 @@ # endif #endif - -/* - * We need to be able to display a filesize using printf(). The type - * and format string here must be compatible with one another and - * large enough for any file. - */ -#if HAVE_UINTMAX_T -#define BSDTAR_FILESIZE_TYPE uintmax_t -#define BSDTAR_FILESIZE_PRINTF "%ju" -#else -#if HAVE_UNSIGNED_LONG_LONG -#define BSDTAR_FILESIZE_TYPE unsigned long long -#define BSDTAR_FILESIZE_PRINTF "%llu" -#else -#define BSDTAR_FILESIZE_TYPE unsigned long -#define BSDTAR_FILESIZE_PRINTF "%lu" -#endif -#endif - #if HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC #define ARCHIVE_STAT_CTIME_NANOS(st) (st)->st_ctimespec.tv_nsec #define ARCHIVE_STAT_MTIME_NANOS(st) (st)->st_mtimespec.tv_nsec @@ -164,12 +121,8 @@ #define __LA_DEAD #endif -#if defined(__CYGWIN__) -#include "bsdtar_cygwin.h" -#elif defined(_WIN32) /* && !__CYGWIN__ */ +#if defined(_WIN32) && !defined(__CYGWIN__) #include "bsdtar_windows.h" -#else -#define bsdtar_is_privileged(bsdtar) (bsdtar->user_uid == 0) #endif #endif /* !BSDTAR_PLATFORM_H_INCLUDED */ diff --git a/usr.bin/tar/cmdline.c b/usr.bin/tar/cmdline.c index efaeafd..dfe7cf6 100644 --- a/usr.bin/tar/cmdline.c +++ b/usr.bin/tar/cmdline.c @@ -41,6 +41,7 @@ __FBSDID("$FreeBSD$"); #endif #include "bsdtar.h" +#include "err.h" /* * Short options for tar. Please keep this sorted. @@ -220,7 +221,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) if (p[1] == ':') { bsdtar->optarg = *bsdtar->argv; if (bsdtar->optarg == NULL) { - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Option %c requires an argument", opt); return ('?'); @@ -287,7 +288,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) /* Otherwise, pick up the next word. */ opt_word = *bsdtar->argv; if (opt_word == NULL) { - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Option -%c requires an argument", opt); return ('?'); @@ -338,13 +339,13 @@ bsdtar_getopt(struct bsdtar *bsdtar) /* Fail if there wasn't a unique match. */ if (match == NULL) { - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Option %s%s is not supported", long_prefix, opt_word); return ('?'); } if (match2 != NULL) { - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Ambiguous option %s%s (matches --%s and --%s)", long_prefix, opt_word, match->name, match2->name); return ('?'); @@ -356,7 +357,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) if (bsdtar->optarg == NULL) { bsdtar->optarg = *bsdtar->argv; if (bsdtar->optarg == NULL) { - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Option %s%s requires an argument", long_prefix, match->name); return ('?'); @@ -367,7 +368,7 @@ bsdtar_getopt(struct bsdtar *bsdtar) } else { /* Argument forbidden: fail if there is one. */ if (bsdtar->optarg != NULL) { - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Option %s%s does not allow an argument", long_prefix, match->name); return ('?'); diff --git a/usr.bin/tar/config_freebsd.h b/usr.bin/tar/config_freebsd.h index 64e0d98..965fe857 100644 --- a/usr.bin/tar/config_freebsd.h +++ b/usr.bin/tar/config_freebsd.h @@ -26,21 +26,10 @@ */ /* A default configuration for FreeBSD, used if there is no config.h. */ - #include <sys/param.h> /* __FreeBSD_version */ -#if __FreeBSD__ > 4 -#define HAVE_ACL_GET_PERM 0 -#define HAVE_ACL_GET_PERM_NP 1 -#define HAVE_ACL_PERMSET_T 1 -#define HAVE_ACL_USER 1 -#endif #undef HAVE_ATTR_XATTR_H -#define HAVE_BZLIB_H 1 -#define HAVE_CHFLAGS 1 #define HAVE_CHROOT 1 -#define HAVE_DECL_OPTARG 1 -#define HAVE_DECL_OPTIND 1 #define HAVE_DIRENT_D_NAMLEN 1 #define HAVE_DIRENT_H 1 #define HAVE_D_MD_ORDER 1 @@ -48,29 +37,13 @@ #undef HAVE_EXT2FS_EXT2_FS_H #define HAVE_FCHDIR 1 #define HAVE_FCNTL_H 1 -#define HAVE_FNMATCH 1 -#define HAVE_FNMATCH_H 1 -#define HAVE_FNM_LEADING_DIR 1 -#define HAVE_FTRUNCATE 1 -#undef HAVE_GETXATTR #define HAVE_GRP_H 1 -#define HAVE_INTTYPES_H 1 #define HAVE_LANGINFO_H 1 -#undef HAVE_LGETXATTR -#undef HAVE_LIBACL #define HAVE_LIBARCHIVE 1 -#define HAVE_LIBBZ2 1 -#define HAVE_LIBZ 1 #define HAVE_LIMITS_H 1 #undef HAVE_LINUX_EXT2_FS_H #undef HAVE_LINUX_FS_H -#undef HAVE_LISTXATTR -#undef HAVE_LLISTXATTR #define HAVE_LOCALE_H 1 -#define HAVE_MALLOC 1 -#define HAVE_MEMMOVE 1 -#define HAVE_MEMORY_H 1 -#define HAVE_MEMSET 1 #if __FreeBSD_version >= 450002 /* nl_langinfo introduced */ #define HAVE_NL_LANGINFO 1 #endif @@ -79,19 +52,11 @@ #define HAVE_REGEX_H 1 #define HAVE_SETLOCALE 1 #define HAVE_STDARG_H 1 -#define HAVE_STDINT_H 1 #define HAVE_STDLIB_H 1 -#define HAVE_STRCHR 1 -#define HAVE_STRDUP 1 -#define HAVE_STRERROR 1 -#define HAVE_STRFTIME 1 -#define HAVE_STRINGS_H 1 #define HAVE_STRING_H 1 -#define HAVE_STRRCHR 1 #define HAVE_STRUCT_STAT_ST_FLAGS 1 #undef HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC #define HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC 1 -#define HAVE_SYS_ACL_H 1 #define HAVE_SYS_IOCTL_H 1 #define HAVE_SYS_PARAM_H 1 #define HAVE_SYS_STAT_H 1 @@ -100,9 +65,5 @@ #define HAVE_UINTMAX_T 1 #define HAVE_UNISTD_H 1 #define HAVE_UNSIGNED_LONG_LONG -#define HAVE_VPRINTF 1 #define HAVE_WCTYPE_H 1 -#define HAVE_ZLIB_H 1 #undef MAJOR_IN_MKDEV -#define STDC_HEADERS 1 - diff --git a/usr.bin/tar/err.c b/usr.bin/tar/err.c new file mode 100644 index 0000000..3770561 --- /dev/null +++ b/usr.bin/tar/err.c @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 2003-2010 Tim Kientzle + * 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 + * in this position and unchanged. + * 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(S) ``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(S) 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 "bsdtar_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_STDARG_H +#include <stdarg.h> +#endif +#include <stdio.h> +#ifdef HAVE_STDLIB_H +#include <stdlib.h> +#endif +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "err.h" + +const char *bsdtar_progname; + +static void +bsdtar_vwarnc(int code, const char *fmt, va_list ap) +{ + fprintf(stderr, "%s: ", bsdtar_progname); + vfprintf(stderr, fmt, ap); + if (code != 0) + fprintf(stderr, ": %s", strerror(code)); + fprintf(stderr, "\n"); +} + +void +bsdtar_warnc(int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + bsdtar_vwarnc(code, fmt, ap); + va_end(ap); +} + +void +bsdtar_errc(int eval, int code, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + bsdtar_vwarnc(code, fmt, ap); + va_end(ap); + exit(eval); +} diff --git a/usr.bin/tar/err.h b/usr.bin/tar/err.h new file mode 100644 index 0000000..84ed3f4 --- /dev/null +++ b/usr.bin/tar/err.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2009 Joerg Sonnenberger + * 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(S) ``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(S) 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$ + */ + +#ifndef LAFE_ERR_H +#define LAFE_ERR_H + +#if defined(__GNUC__) && (__GNUC__ > 2 || \ + (__GNUC__ == 2 && __GNUC_MINOR__ >= 5)) +#define __LA_DEAD __attribute__((__noreturn__)) +#else +#define __LA_DEAD +#endif + +extern const char *bsdtar_progname; + +void bsdtar_warnc(int code, const char *fmt, ...); +void bsdtar_errc(int eval, int code, const char *fmt, ...) __LA_DEAD; + +#endif diff --git a/usr.bin/tar/getdate.c b/usr.bin/tar/getdate.c index 8df1e26..ffaa679 100644 --- a/usr.bin/tar/getdate.c +++ b/usr.bin/tar/getdate.c @@ -219,7 +219,8 @@ datephrase(struct gdstate *gds) gds->Year = gds->tokenp[0].value; gds->Month = gds->tokenp[2].value; gds->Day = gds->tokenp[4].value; - } else if ((gds->tokenp[4].value >= 13) || (gds->tokenp[2].value >= 13)) { + } else if ((gds->tokenp[4].value >= 13) + || (gds->tokenp[2].value >= 13)) { /* Last number is big: 01/07/98 */ /* Middle number is big: 01/29/04 */ gds->Month = gds->tokenp[0].value; @@ -681,20 +682,6 @@ static struct LEXICON { }; /* - * Convert hour/minute/second to count of seconds. - */ -static time_t -ToSeconds(time_t Hours, time_t Minutes, time_t Seconds) -{ - if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) - return -1; - if (Hours < 0 || Hours > 23) - return -1; - return Hours * HOUR + Minutes * MINUTE + Seconds; -} - - -/* * Year is either: * = A number from 0 to 99, which means a year from 1970 to 2069, or * = The actual year (>=100). @@ -707,7 +694,6 @@ Convert(time_t Month, time_t Day, time_t Year, static int DaysInMonth[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; - time_t tod; time_t Julian; int i; @@ -722,7 +708,10 @@ Convert(time_t Month, time_t Day, time_t Year, if (Year < EPOCH || Year > 2038 || Month < 1 || Month > 12 /* Lint fluff: "conversion from long may lose accuracy" */ - || Day < 1 || Day > DaysInMonth[(int)--Month]) + || Day < 1 || Day > DaysInMonth[(int)--Month] + || Hours < 0 || Hours > 23 + || Minutes < 0 || Minutes > 59 + || Seconds < 0 || Seconds > 59) return -1; Julian = Day - 1; @@ -732,9 +721,7 @@ Convert(time_t Month, time_t Day, time_t Year, Julian += 365 + (i % 4 == 0); Julian *= DAY; Julian += Timezone; - if ((tod = ToSeconds(Hours, Minutes, Seconds)) < 0) - return -1; - Julian += tod; + Julian += Hours * HOUR + Minutes * MINUTE + Seconds; if (DSTmode == DSTon || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) Julian -= HOUR; @@ -838,7 +825,7 @@ nexttoken(char **in, time_t *value) } src++; } - buff[i++] = '\0'; + buff[i] = '\0'; /* * Find the first match. If the word can be diff --git a/usr.bin/tar/line_reader.c b/usr.bin/tar/line_reader.c new file mode 100644 index 0000000..c9df1b0 --- /dev/null +++ b/usr.bin/tar/line_reader.c @@ -0,0 +1,171 @@ +/*- + * Copyright (c) 2008 Tim Kientzle + * 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 + * in this position and unchanged. + * 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(S) ``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(S) 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 "bsdtar_platform.h" +__FBSDID("$FreeBSD$"); + +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "err.h" +#include "line_reader.h" + +#if defined(_WIN32) && !defined(__CYGWIN__) && !defined(__BORLANDC__) +#define strdup _strdup +#endif + +/* + * Read lines from file and do something with each one. If option_null + * is set, lines are terminated with zero bytes; otherwise, they're + * terminated with newlines. + * + * This uses a self-sizing buffer to handle arbitrarily-long lines. + */ +struct lafe_line_reader { + FILE *f; + char *buff, *buff_end, *line_start, *line_end, *p; + char *pathname; + size_t buff_length; + int nullSeparator; /* Lines separated by null, not CR/CRLF/etc. */ + int ret; +}; + +struct lafe_line_reader * +lafe_line_reader(const char *pathname, int nullSeparator) +{ + struct lafe_line_reader *lr; + + lr = calloc(1, sizeof(*lr)); + if (lr == NULL) + bsdtar_errc(1, ENOMEM, "Can't open %s", pathname); + + lr->nullSeparator = nullSeparator; + lr->pathname = strdup(pathname); + + if (strcmp(pathname, "-") == 0) + lr->f = stdin; + else + lr->f = fopen(pathname, "r"); + if (lr->f == NULL) + bsdtar_errc(1, errno, "Couldn't open %s", pathname); + lr->buff_length = 8192; + lr->buff = malloc(lr->buff_length); + if (lr->buff == NULL) + bsdtar_errc(1, ENOMEM, "Can't read %s", pathname); + lr->line_start = lr->line_end = lr->buff_end = lr->buff; + + return (lr); +} + +const char * +lafe_line_reader_next(struct lafe_line_reader *lr) +{ + size_t bytes_wanted, bytes_read, new_buff_size; + char *line_start, *p; + + for (;;) { + /* If there's a line in the buffer, return it immediately. */ + while (lr->line_end < lr->buff_end) { + if (lr->nullSeparator) { + if (*lr->line_end == '\0') { + line_start = lr->line_start; + lr->line_start = lr->line_end + 1; + lr->line_end = lr->line_start; + return (line_start); + } + } else if (*lr->line_end == '\x0a' || *lr->line_end == '\x0d') { + *lr->line_end = '\0'; + line_start = lr->line_start; + lr->line_start = lr->line_end + 1; + lr->line_end = lr->line_start; + if (line_start[0] != '\0') + return (line_start); + } + lr->line_end++; + } + + /* If we're at end-of-file, process the final data. */ + if (lr->f == NULL) { + /* If there's more text, return one last line. */ + if (lr->line_end > lr->line_start) { + *lr->line_end = '\0'; + line_start = lr->line_start; + lr->line_start = lr->line_end + 1; + lr->line_end = lr->line_start; + return (line_start); + } + /* Otherwise, we're done. */ + return (NULL); + } + + /* Buffer only has part of a line. */ + if (lr->line_start > lr->buff) { + /* Move a leftover fractional line to the beginning. */ + memmove(lr->buff, lr->line_start, + lr->buff_end - lr->line_start); + lr->buff_end -= lr->line_start - lr->buff; + lr->line_end -= lr->line_start - lr->buff; + lr->line_start = lr->buff; + } else { + /* Line is too big; enlarge the buffer. */ + new_buff_size = lr->buff_length * 2; + if (new_buff_size <= lr->buff_length) + bsdtar_errc(1, ENOMEM, + "Line too long in %s", lr->pathname); + lr->buff_length = new_buff_size; + p = realloc(lr->buff, new_buff_size); + if (p == NULL) + bsdtar_errc(1, ENOMEM, + "Line too long in %s", lr->pathname); + lr->buff_end = p + (lr->buff_end - lr->buff); + lr->line_end = p + (lr->line_end - lr->buff); + lr->line_start = lr->buff = p; + } + + /* Get some more data into the buffer. */ + bytes_wanted = lr->buff + lr->buff_length - lr->buff_end; + bytes_read = fread(lr->buff_end, 1, bytes_wanted, lr->f); + lr->buff_end += bytes_read; + + if (ferror(lr->f)) + bsdtar_errc(1, errno, "Can't read %s", lr->pathname); + if (feof(lr->f)) { + if (lr->f != stdin) + fclose(lr->f); + lr->f = NULL; + } + } +} + +void +lafe_line_reader_free(struct lafe_line_reader *lr) +{ + free(lr->buff); + free(lr->pathname); + free(lr); +} diff --git a/usr.bin/tar/line_reader.h b/usr.bin/tar/line_reader.h new file mode 100644 index 0000000..e4c3729 --- /dev/null +++ b/usr.bin/tar/line_reader.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2009 Joerg Sonnenberger + * 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(S) ``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(S) 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$ + */ + +#ifndef LAFE_LINE_READER_H +#define LAFE_LINE_READER_H + +struct lafe_line_reader; + +struct lafe_line_reader *lafe_line_reader(const char *, int nullSeparator); +const char *lafe_line_reader_next(struct lafe_line_reader *); +void lafe_line_reader_free(struct lafe_line_reader *); + +#endif diff --git a/usr.bin/tar/matching.c b/usr.bin/tar/matching.c index 36bc84b..184d29a 100644 --- a/usr.bin/tar/matching.c +++ b/usr.bin/tar/matching.c @@ -36,7 +36,10 @@ __FBSDID("$FreeBSD$"); #include <string.h> #endif -#include "bsdtar.h" +#include "err.h" +#include "line_reader.h" +#include "matching.h" +#include "pathmatch.h" struct match { struct match *next; @@ -44,7 +47,7 @@ struct match { char pattern[1]; }; -struct matching { +struct lafe_matching { struct match *exclusions; int exclusions_count; struct match *inclusions; @@ -52,14 +55,10 @@ struct matching { int inclusions_unmatched_count; }; - -static void add_pattern(struct bsdtar *, struct match **list, - const char *pattern); -static int bsdtar_fnmatch(const char *p, const char *s); -static void initialize_matching(struct bsdtar *); +static void add_pattern(struct match **list, const char *pattern); +static void initialize_matching(struct lafe_matching **); static int match_exclusion(struct match *, const char *pathname); static int match_inclusion(struct match *, const char *pathname); -static int pathmatch(const char *p, const char *s); /* * The matching logic here needs to be re-thought. I started out to @@ -73,55 +72,74 @@ static int pathmatch(const char *p, const char *s); */ int -exclude(struct bsdtar *bsdtar, const char *pattern) +lafe_exclude(struct lafe_matching **matching, const char *pattern) { - struct matching *matching; - if (bsdtar->matching == NULL) - initialize_matching(bsdtar); - matching = bsdtar->matching; - add_pattern(bsdtar, &(matching->exclusions), pattern); - matching->exclusions_count++; + if (*matching == NULL) + initialize_matching(matching); + add_pattern(&((*matching)->exclusions), pattern); + (*matching)->exclusions_count++; return (0); } int -exclude_from_file(struct bsdtar *bsdtar, const char *pathname) +lafe_exclude_from_file(struct lafe_matching **matching, const char *pathname) { - return (process_lines(bsdtar, pathname, &exclude)); + struct lafe_line_reader *lr; + const char *p; + int ret = 0; + + lr = lafe_line_reader(pathname, '\n'); + while ((p = lafe_line_reader_next(lr)) != NULL) { + if (lafe_exclude(matching, p) != 0) + ret = -1; + } + lafe_line_reader_free(lr); + return (ret); } int -include(struct bsdtar *bsdtar, const char *pattern) +lafe_include(struct lafe_matching **matching, const char *pattern) { - struct matching *matching; - - if (bsdtar->matching == NULL) - initialize_matching(bsdtar); - matching = bsdtar->matching; - add_pattern(bsdtar, &(matching->inclusions), pattern); - matching->inclusions_count++; - matching->inclusions_unmatched_count++; + + if (*matching == NULL) + initialize_matching(matching); + add_pattern(&((*matching)->inclusions), pattern); + (*matching)->inclusions_count++; + (*matching)->inclusions_unmatched_count++; return (0); } int -include_from_file(struct bsdtar *bsdtar, const char *pathname) +lafe_include_from_file(struct lafe_matching **matching, const char *pathname, + int nullSeparator) { - return (process_lines(bsdtar, pathname, &include)); + struct lafe_line_reader *lr; + const char *p; + int ret = 0; + + lr = lafe_line_reader(pathname, nullSeparator); + while ((p = lafe_line_reader_next(lr)) != NULL) { + if (lafe_include(matching, p) != 0) + ret = -1; + } + lafe_line_reader_free(lr); + return (ret); } static void -add_pattern(struct bsdtar *bsdtar, struct match **list, const char *pattern) +add_pattern(struct match **list, const char *pattern) { struct match *match; + size_t len; - match = malloc(sizeof(*match) + strlen(pattern) + 1); + len = strlen(pattern); + match = malloc(sizeof(*match) + len + 1); if (match == NULL) - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + bsdtar_errc(1, errno, "Out of memory"); strcpy(match->pattern, pattern); /* Both "foo/" and "foo" should match "foo/bar". */ - if (match->pattern[strlen(match->pattern)-1] == '/') + if (len && match->pattern[len - 1] == '/') match->pattern[strlen(match->pattern)-1] = '\0'; match->next = *list; *list = match; @@ -130,13 +148,11 @@ add_pattern(struct bsdtar *bsdtar, struct match **list, const char *pattern) int -excluded(struct bsdtar *bsdtar, const char *pathname) +lafe_excluded(struct lafe_matching *matching, const char *pathname) { - struct matching *matching; struct match *match; struct match *matched; - matching = bsdtar->matching; if (matching == NULL) return (0); @@ -191,288 +207,74 @@ excluded(struct bsdtar *bsdtar, const char *pathname) static int match_exclusion(struct match *match, const char *pathname) { - const char *p; - - if (*match->pattern == '*' || *match->pattern == '/') - return (pathmatch(match->pattern, pathname) == 0); - - for (p = pathname; p != NULL; p = strchr(p, '/')) { - if (*p == '/') - p++; - if (pathmatch(match->pattern, p) == 0) - return (1); - } - return (0); + return (lafe_pathmatch(match->pattern, + pathname, + PATHMATCH_NO_ANCHOR_START | PATHMATCH_NO_ANCHOR_END)); } /* * Again, mimic gtar: inclusions are always anchored (have to match * the beginning of the path) even though exclusions are not anchored. */ -int +static int match_inclusion(struct match *match, const char *pathname) { - return (pathmatch(match->pattern, pathname) == 0); + return (lafe_pathmatch(match->pattern, pathname, PATHMATCH_NO_ANCHOR_END)); } void -cleanup_exclusions(struct bsdtar *bsdtar) +lafe_cleanup_exclusions(struct lafe_matching **matching) { struct match *p, *q; - if (bsdtar->matching) { - p = bsdtar->matching->inclusions; - while (p != NULL) { - q = p; - p = p->next; - free(q); - } - p = bsdtar->matching->exclusions; - while (p != NULL) { - q = p; - p = p->next; - free(q); - } - free(bsdtar->matching); + if (*matching == NULL) + return; + + for (p = (*matching)->inclusions; p != NULL; ) { + q = p; + p = p->next; + free(q); } + + for (p = (*matching)->exclusions; p != NULL; ) { + q = p; + p = p->next; + free(q); + } + + free(*matching); + *matching = NULL; } static void -initialize_matching(struct bsdtar *bsdtar) +initialize_matching(struct lafe_matching **matching) { - bsdtar->matching = malloc(sizeof(*bsdtar->matching)); - if (bsdtar->matching == NULL) - bsdtar_errc(bsdtar, 1, errno, "No memory"); - memset(bsdtar->matching, 0, sizeof(*bsdtar->matching)); + *matching = calloc(sizeof(**matching), 1); + if (*matching == NULL) + bsdtar_errc(1, errno, "No memory"); } int -unmatched_inclusions(struct bsdtar *bsdtar) +lafe_unmatched_inclusions(struct lafe_matching *matching) { - struct matching *matching; - matching = bsdtar->matching; if (matching == NULL) return (0); return (matching->inclusions_unmatched_count); } - int -unmatched_inclusions_warn(struct bsdtar *bsdtar, const char *msg) +lafe_unmatched_inclusions_warn(struct lafe_matching *matching, const char *msg) { - struct matching *matching; struct match *p; - matching = bsdtar->matching; if (matching == NULL) return (0); - p = matching->inclusions; - while (p != NULL) { - if (p->matches == 0) { - bsdtar->return_value = 1; - bsdtar_warnc(bsdtar, 0, "%s: %s", - p->pattern, msg); - } - p = p->next; - } - return (matching->inclusions_unmatched_count); -} - -/* - * TODO: Extend this so that the following matches work: - * "foo//bar" == "foo/bar" - * "foo/./bar" == "foo/bar" - * "./foo" == "foo" - * - * The POSIX fnmatch() function doesn't handle any of these, but - * all are common situations that arise when paths are generated within - * large scripts. E.g., the following is quite common: - * MYPATH=foo/ TARGET=$MYPATH/bar - * It may be worthwhile to edit such paths at write time as well, - * especially when such editing may avoid the need for long pathname - * extensions. - */ -static int -pathmatch(const char *pattern, const char *string) -{ - /* - * Strip leading "./" or ".//" so that, e.g., - * "foo" matches "./foo". In particular, this - * opens up an optimization for the writer to - * elide leading "./". - */ - if (pattern[0] == '.' && pattern[1] == '/') { - pattern += 2; - while (pattern[0] == '/') - ++pattern; + for (p = matching->inclusions; p != NULL; p = p->next) { + if (p->matches == 0) + bsdtar_warnc(0, "%s: %s", p->pattern, msg); } - if (string[0] == '.' && string[1] == '/') { - string += 2; - while (string[0] == '/') - ++string; - } - return (bsdtar_fnmatch(pattern, string)); -} - -#if defined(HAVE_FNMATCH) && defined(HAVE_FNM_LEADING_DIR) - -/* Use system fnmatch() if it suits our needs. */ -/* On Linux, _GNU_SOURCE must be defined to get FNM_LEADING_DIR. */ -#define _GNU_SOURCE -#include <fnmatch.h> -static int -bsdtar_fnmatch(const char *pattern, const char *string) -{ - return (fnmatch(pattern, string, FNM_LEADING_DIR)); -} - -#else -/* - * The following was hacked from BSD C library - * code: src/lib/libc/gen/fnmatch.c,v 1.15 2002/02/01 - * - * In particular, most of the flags were ripped out: this always - * behaves like FNM_LEADING_DIR is set and other flags specified - * by POSIX are unset. - * - * Normally, I would not conditionally compile something like this: If - * I have to support it anyway, everyone may as well use it. ;-) - * However, the full POSIX spec for fnmatch() includes a lot of - * advanced character handling that I'm not ready to put in here, so - * it's probably best if people use a local version when it's available. - */ - -/* - * 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 - * Guido van Rossum. - * - * 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. - * 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. - */ - -static int -bsdtar_fnmatch(const char *pattern, const char *string) -{ - const char *saved_pattern; - int negate, matched; - char c; - - for (;;) { - switch (c = *pattern++) { - case '\0': - if (*string == '/' || *string == '\0') - return (0); - return (1); - case '?': - if (*string == '\0') - return (1); - ++string; - break; - case '*': - c = *pattern; - /* Collapse multiple stars. */ - while (c == '*') - c = *++pattern; - - /* Optimize for pattern with * at end. */ - if (c == '\0') - return (0); - - /* General case, use recursion. */ - while (*string != '\0') { - if (!bsdtar_fnmatch(pattern, string)) - return (0); - ++string; - } - return (1); - case '[': - if (*string == '\0') - return (1); - saved_pattern = pattern; - if (*pattern == '!' || *pattern == '^') { - negate = 1; - ++pattern; - } else - negate = 0; - matched = 0; - c = *pattern++; - do { - if (c == '\\') - c = *pattern++; - if (c == '\0') { - pattern = saved_pattern; - c = '['; - goto norm; - } - if (*pattern == '-') { - char c2 = *(pattern + 1); - if (c2 == '\0') { - pattern = saved_pattern; - c = '['; - goto norm; - } - if (c2 == ']') { - /* [a-] is not a range. */ - if (c == *string - || '-' == *string) - matched = 1; - pattern ++; - } else { - if (c <= *string - && *string <= c2) - matched = 1; - pattern += 2; - } - } else if (c == *string) - matched = 1; - c = *pattern++; - } while (c != ']'); - if (matched == negate) - return (1); - ++string; - break; - case '\\': - if ((c = *pattern++) == '\0') { - c = '\\'; - --pattern; - } - /* FALLTHROUGH */ - default: - norm: - if (c != *string) - return (1); - string++; - break; - } - } - /* NOTREACHED */ + return (matching->inclusions_unmatched_count); } - -#endif diff --git a/usr.bin/tar/matching.h b/usr.bin/tar/matching.h new file mode 100644 index 0000000..f4edebd --- /dev/null +++ b/usr.bin/tar/matching.h @@ -0,0 +1,46 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * 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 + * in this position and unchanged. + * 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(S) ``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(S) 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$ + */ + +#ifndef MATCHING_H +#define MATCHING_H + +struct lafe_matching; + +int lafe_exclude(struct lafe_matching **matching, const char *pattern); +int lafe_exclude_from_file(struct lafe_matching **matching, + const char *pathname); +int lafe_include(struct lafe_matching **matching, const char *pattern); +int lafe_include_from_file(struct lafe_matching **matching, + const char *pathname, int nullSeparator); + +int lafe_excluded(struct lafe_matching *, const char *pathname); +void lafe_cleanup_exclusions(struct lafe_matching **); +int lafe_unmatched_inclusions(struct lafe_matching *); +int lafe_unmatched_inclusions_warn(struct lafe_matching *, const char *msg); + +#endif diff --git a/usr.bin/tar/pathmatch.c b/usr.bin/tar/pathmatch.c new file mode 100644 index 0000000..8b43138 --- /dev/null +++ b/usr.bin/tar/pathmatch.c @@ -0,0 +1,255 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * 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 + * in this position and unchanged. + * 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(S) ``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(S) 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 "bsdtar_platform.h" +__FBSDID("$FreeBSD$"); + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include "pathmatch.h" + +/* + * Check whether a character 'c' is matched by a list specification [...]: + * * Leading '!' negates the class. + * * <char>-<char> is a range of characters + * * \<char> removes any special meaning for <char> + * + * Some interesting boundary cases: + * a-d-e is one range (a-d) followed by two single characters - and e. + * \a-\d is same as a-d + * a\-d is three single characters: a, d, - + * Trailing - is not special (so [a-] is two characters a and -). + * Initial - is not special ([a-] is same as [-a] is same as [\\-a]) + * This function never sees a trailing \. + * [] always fails + * [!] always succeeds + */ +static int +pm_list(const char *start, const char *end, const char c, int flags) +{ + const char *p = start; + char rangeStart = '\0', nextRangeStart; + int match = 1, nomatch = 0; + + /* This will be used soon... */ + (void)flags; /* UNUSED */ + + /* If this is a negated class, return success for nomatch. */ + if (*p == '!' && p < end) { + match = 0; + nomatch = 1; + ++p; + } + + while (p < end) { + nextRangeStart = '\0'; + switch (*p) { + case '-': + /* Trailing or initial '-' is not special. */ + if ((rangeStart == '\0') || (p == end - 1)) { + if (*p == c) + return (match); + } else { + char rangeEnd = *++p; + if (rangeEnd == '\\') + rangeEnd = *++p; + if ((rangeStart <= c) && (c <= rangeEnd)) + return (match); + } + break; + case '\\': + ++p; + /* Fall through */ + default: + if (*p == c) + return (match); + nextRangeStart = *p; /* Possible start of range. */ + } + rangeStart = nextRangeStart; + ++p; + } + return (nomatch); +} + +/* + * If s is pointing to "./", ".//", "./././" or the like, skip it. + */ +static const char * +pm_slashskip(const char *s) { + while ((*s == '/') + || (s[0] == '.' && s[1] == '/') + || (s[0] == '.' && s[1] == '\0')) + ++s; + return (s); +} + +static int +pm(const char *p, const char *s, int flags) +{ + const char *end; + + /* + * Ignore leading './', './/', '././', etc. + */ + if (s[0] == '.' && s[1] == '/') + s = pm_slashskip(s + 1); + if (p[0] == '.' && p[1] == '/') + p = pm_slashskip(p + 1); + + for (;;) { + switch (*p) { + case '\0': + if (s[0] == '/') { + if (flags & PATHMATCH_NO_ANCHOR_END) + return (1); + /* "dir" == "dir/" == "dir/." */ + s = pm_slashskip(s); + } + return (*s == '\0'); + case '?': + /* ? always succeds, unless we hit end of 's' */ + if (*s == '\0') + return (0); + break; + case '*': + /* "*" == "**" == "***" ... */ + while (*p == '*') + ++p; + /* Trailing '*' always succeeds. */ + if (*p == '\0') + return (1); + while (*s) { + if (lafe_pathmatch(p, s, flags)) + return (1); + ++s; + } + return (0); + case '[': + /* + * Find the end of the [...] character class, + * ignoring \] that might occur within the class. + */ + end = p + 1; + while (*end != '\0' && *end != ']') { + if (*end == '\\' && end[1] != '\0') + ++end; + ++end; + } + if (*end == ']') { + /* We found [...], try to match it. */ + if (!pm_list(p + 1, end, *s, flags)) + return (0); + p = end; /* Jump to trailing ']' char. */ + break; + } else + /* No final ']', so just match '['. */ + if (*p != *s) + return (0); + break; + case '\\': + /* Trailing '\\' matches itself. */ + if (p[1] == '\0') { + if (*s != '\\') + return (0); + } else { + ++p; + if (*p != *s) + return (0); + } + break; + case '/': + if (*s != '/' && *s != '\0') + return (0); + /* Note: pattern "/\./" won't match "/"; + * pm_slashskip() correctly stops at backslash. */ + p = pm_slashskip(p); + s = pm_slashskip(s); + if (*p == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)) + return (1); + --p; /* Counteract the increment below. */ + --s; + break; + case '$': + /* '$' is special only at end of pattern and only + * if PATHMATCH_NO_ANCHOR_END is specified. */ + if (p[1] == '\0' && (flags & PATHMATCH_NO_ANCHOR_END)){ + /* "dir" == "dir/" == "dir/." */ + return (*pm_slashskip(s) == '\0'); + } + /* Otherwise, '$' is not special. */ + /* FALL THROUGH */ + default: + if (*p != *s) + return (0); + break; + } + ++p; + ++s; + } +} + +/* Main entry point. */ +int +lafe_pathmatch(const char *p, const char *s, int flags) +{ + /* Empty pattern only matches the empty string. */ + if (p == NULL || *p == '\0') + return (s == NULL || *s == '\0'); + + /* Leading '^' anchors the start of the pattern. */ + if (*p == '^') { + ++p; + flags &= ~PATHMATCH_NO_ANCHOR_START; + } + + if (*p == '/' && *s != '/') + return (0); + + /* Certain patterns and file names anchor implicitly. */ + if (*p == '*' || *p == '/' || *p == '/') { + while (*p == '/') + ++p; + while (*s == '/') + ++s; + return (pm(p, s, flags)); + } + + /* If start is unanchored, try to match start of each path element. */ + if (flags & PATHMATCH_NO_ANCHOR_START) { + for ( ; s != NULL; s = strchr(s, '/')) { + if (*s == '/') + s++; + if (pm(p, s, flags)) + return (1); + } + return (0); + } + + /* Default: Match from beginning. */ + return (pm(p, s, flags)); +} diff --git a/usr.bin/tar/pathmatch.h b/usr.bin/tar/pathmatch.h new file mode 100644 index 0000000..a92f3ae --- /dev/null +++ b/usr.bin/tar/pathmatch.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2003-2007 Tim Kientzle + * 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 + * in this position and unchanged. + * 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(S) ``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(S) 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$ + */ + +#ifndef LAFE_PATHMATCH_H +#define LAFE_PATHMATCH_H + +/* Don't anchor at beginning unless the pattern starts with "^" */ +#define PATHMATCH_NO_ANCHOR_START 1 +/* Don't anchor at end unless the pattern ends with "$" */ +#define PATHMATCH_NO_ANCHOR_END 2 + +/* Note that "^" and "$" are not special unless you set the corresponding + * flag above. */ + +int lafe_pathmatch(const char *p, const char *s, int flags); + +#endif diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c index 0756cd8..c0e5bcb 100644 --- a/usr.bin/tar/read.c +++ b/usr.bin/tar/read.c @@ -29,11 +29,6 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif -#ifdef MAJOR_IN_MKDEV -#include <sys/mkdev.h> -#elif defined(MAJOR_IN_SYSMACROS) -#include <sys/sysmacros.h> -#endif #ifdef HAVE_SYS_PARAM_H #include <sys/param.h> #endif @@ -53,6 +48,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_PWD_H #include <pwd.h> #endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -68,6 +66,13 @@ __FBSDID("$FreeBSD$"); #endif #include "bsdtar.h" +#include "err.h" + +struct progress_data { + struct bsdtar *bsdtar; + struct archive *archive; + struct archive_entry *entry; +}; static void list_item_verbose(struct bsdtar *, FILE *, struct archive_entry *); @@ -77,28 +82,48 @@ void tar_mode_t(struct bsdtar *bsdtar) { read_archive(bsdtar, 't'); - unmatched_inclusions_warn(bsdtar, "Not found in archive"); + if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0) + bsdtar->return_value = 1; } void tar_mode_x(struct bsdtar *bsdtar) { - /* We want to catch SIGINFO and SIGUSR1. */ - siginfo_init(bsdtar); - read_archive(bsdtar, 'x'); - unmatched_inclusions_warn(bsdtar, "Not found in archive"); - /* Restore old SIGINFO + SIGUSR1 handlers. */ - siginfo_done(bsdtar); + if (lafe_unmatched_inclusions_warn(bsdtar->matching, "Not found in archive") != 0) + bsdtar->return_value = 1; } static void -progress_func(void * cookie) +progress_func(void *cookie) { - struct bsdtar * bsdtar = cookie; - - siginfo_printinfo(bsdtar, 0); + struct progress_data *progress_data = cookie; + struct bsdtar *bsdtar = progress_data->bsdtar; + struct archive *a = progress_data->archive; + struct archive_entry *entry = progress_data->entry; + uint64_t comp, uncomp; + + if (!need_report()) + return; + + if (bsdtar->verbose) + fprintf(stderr, "\n"); + if (a != NULL) { + comp = archive_position_compressed(a); + uncomp = archive_position_uncompressed(a); + fprintf(stderr, + "In: %s bytes, compression %d%%;", + tar_i64toa(comp), (int)((uncomp - comp) * 100 / uncomp)); + fprintf(stderr, " Out: %d files, %s bytes\n", + archive_file_count(a), tar_i64toa(uncomp)); + } + if (entry != NULL) { + safe_fprintf(stderr, "Current: %s", + archive_entry_pathname(entry)); + fprintf(stderr, " (%s bytes)\n", + tar_i64toa(archive_entry_size(entry))); + } } /* @@ -107,6 +132,7 @@ progress_func(void * cookie) static void read_archive(struct bsdtar *bsdtar, char mode) { + struct progress_data progress_data; FILE *out; struct archive *a; struct archive_entry *entry; @@ -114,12 +140,13 @@ read_archive(struct bsdtar *bsdtar, char mode) int r; while (*bsdtar->argv) { - include(bsdtar, *bsdtar->argv); + lafe_include(&bsdtar->matching, *bsdtar->argv); bsdtar->argv++; } if (bsdtar->names_from_file != NULL) - include_from_file(bsdtar, bsdtar->names_from_file); + lafe_include_from_file(&bsdtar->matching, + bsdtar->names_from_file, bsdtar->option_null); a = archive_read_new(); if (bsdtar->compress_program != NULL) @@ -128,27 +155,29 @@ read_archive(struct bsdtar *bsdtar, char mode) archive_read_support_compression_all(a); archive_read_support_format_all(a); if (ARCHIVE_OK != archive_read_set_options(a, bsdtar->option_options)) - bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + bsdtar_errc(1, 0, "%s", archive_error_string(a)); if (archive_read_open_file(a, bsdtar->filename, bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : DEFAULT_BYTES_PER_BLOCK)) - bsdtar_errc(bsdtar, 1, 0, "Error opening archive: %s", + bsdtar_errc(1, 0, "Error opening archive: %s", archive_error_string(a)); do_chdir(bsdtar); if (mode == 'x') { /* Set an extract callback so that we can handle SIGINFO. */ + progress_data.bsdtar = bsdtar; + progress_data.archive = a; archive_read_extract_set_progress_callback(a, progress_func, - bsdtar); + &progress_data); } if (mode == 'x' && bsdtar->option_chroot) { #if HAVE_CHROOT if (chroot(".") != 0) - bsdtar_errc(bsdtar, 1, errno, "Can't chroot to \".\""); + bsdtar_errc(1, errno, "Can't chroot to \".\""); #else - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "chroot isn't supported on this platform"); #endif } @@ -156,19 +185,20 @@ read_archive(struct bsdtar *bsdtar, char mode) for (;;) { /* Support --fast-read option */ if (bsdtar->option_fast_read && - unmatched_inclusions(bsdtar) == 0) + lafe_unmatched_inclusions(bsdtar->matching) == 0) break; r = archive_read_next_header(a, &entry); + progress_data.entry = entry; if (r == ARCHIVE_EOF) break; if (r < ARCHIVE_OK) - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + bsdtar_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; if (r == ARCHIVE_RETRY) { /* Retryable error: try again */ - bsdtar_warnc(bsdtar, 0, "Retrying..."); + bsdtar_warnc(0, "Retrying..."); continue; } if (r == ARCHIVE_FATAL) @@ -209,7 +239,7 @@ read_archive(struct bsdtar *bsdtar, char mode) * rewrite, there would be no way to exclude foo1/bar * while allowing foo2/bar.) */ - if (excluded(bsdtar, archive_entry_pathname(entry))) + if (lafe_excluded(bsdtar->matching, archive_entry_pathname(entry))) continue; /* Excluded by a pattern test. */ if (mode == 't') { @@ -232,17 +262,17 @@ read_archive(struct bsdtar *bsdtar, char mode) r = archive_read_data_skip(a); if (r == ARCHIVE_WARN) { fprintf(out, "\n"); - bsdtar_warnc(bsdtar, 0, "%s", + bsdtar_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_RETRY) { fprintf(out, "\n"); - bsdtar_warnc(bsdtar, 0, "%s", + bsdtar_warnc(0, "%s", archive_error_string(a)); } if (r == ARCHIVE_FATAL) { fprintf(out, "\n"); - bsdtar_warnc(bsdtar, 0, "%s", + bsdtar_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; break; @@ -267,10 +297,7 @@ read_archive(struct bsdtar *bsdtar, char mode) fflush(stderr); } - /* Tell the SIGINFO-handler code what we're doing. */ - siginfo_setinfo(bsdtar, "extracting", - archive_entry_pathname(entry), 0); - siginfo_printinfo(bsdtar, 0); + // TODO siginfo_printinfo(bsdtar, 0); if (bsdtar->option_stdout) r = archive_read_data_into_fd(a, 1); @@ -297,7 +324,7 @@ read_archive(struct bsdtar *bsdtar, char mode) r = archive_read_close(a); if (r != ARCHIVE_OK) - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + bsdtar_warnc(0, "%s", archive_error_string(a)); if (r <= ARCHIVE_WARN) bsdtar->return_value = 1; @@ -320,7 +347,6 @@ read_archive(struct bsdtar *bsdtar, char mode) static void list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) { - const struct stat *st; char tmp[100]; size_t w; const char *p; @@ -328,8 +354,6 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) time_t tim; static time_t now; - st = archive_entry_stat(entry); - /* * We avoid collecting the entire list in memory at once by * listing things as we see them. However, that also means we can't @@ -345,12 +369,13 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) time(&now); fprintf(out, "%s %d ", archive_entry_strmode(entry), - (int)(st->st_nlink)); + archive_entry_nlink(entry)); /* Use uname if it's present, else uid. */ p = archive_entry_uname(entry); if ((p == NULL) || (*p == '\0')) { - sprintf(tmp, "%lu ", (unsigned long)st->st_uid); + sprintf(tmp, "%lu ", + (unsigned long)archive_entry_uid(entry)); p = tmp; } w = strlen(p); @@ -364,7 +389,8 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) fprintf(out, "%s", p); w = strlen(p); } else { - sprintf(tmp, "%lu", (unsigned long)st->st_gid); + sprintf(tmp, "%lu", + (unsigned long)archive_entry_gid(entry)); w = strlen(tmp); fprintf(out, "%s", tmp); } @@ -374,37 +400,30 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) * total width of group and devnum/filesize fields be gs_width. * If gs_width is too small, grow it. */ - if (S_ISCHR(st->st_mode) || S_ISBLK(st->st_mode)) { + if (archive_entry_filetype(entry) == AE_IFCHR + || archive_entry_filetype(entry) == AE_IFBLK) { sprintf(tmp, "%lu,%lu", - (unsigned long)major(st->st_rdev), - (unsigned long)minor(st->st_rdev)); /* ls(1) also casts here. */ + (unsigned long)archive_entry_rdevmajor(entry), + (unsigned long)archive_entry_rdevminor(entry)); } else { - /* - * Note the use of platform-dependent macros to format - * the filesize here. We need the format string and the - * corresponding type for the cast. - */ - sprintf(tmp, BSDTAR_FILESIZE_PRINTF, - (BSDTAR_FILESIZE_TYPE)st->st_size); + strcpy(tmp, tar_i64toa(archive_entry_size(entry))); } if (w + strlen(tmp) >= bsdtar->gs_width) bsdtar->gs_width = w+strlen(tmp)+1; fprintf(out, "%*s", (int)(bsdtar->gs_width - w), tmp); /* Format the time using 'ls -l' conventions. */ - tim = (time_t)st->st_mtime; + tim = archive_entry_mtime(entry); +#define HALF_YEAR (time_t)365 * 86400 / 2 #if defined(_WIN32) && !defined(__CYGWIN__) - /* Windows' strftime function does not support %e format. */ - if (abs(tim - now) > (365/2)*86400) - fmt = bsdtar->day_first ? "%d %b %Y" : "%b %d %Y"; - else - fmt = bsdtar->day_first ? "%d %b %H:%M" : "%b %d %H:%M"; +#define DAY_FMT "%d" /* Windows' strftime function does not support %e format. */ #else - if (abs(tim - now) > (365/2)*86400) - fmt = bsdtar->day_first ? "%e %b %Y" : "%b %e %Y"; - else - fmt = bsdtar->day_first ? "%e %b %H:%M" : "%b %e %H:%M"; +#define DAY_FMT "%e" /* Day number without leading zeros */ #endif + if (tim < now - HALF_YEAR || tim > now + HALF_YEAR) + fmt = bsdtar->day_first ? DAY_FMT " %b %Y" : "%b " DAY_FMT " %Y"; + else + fmt = bsdtar->day_first ? DAY_FMT " %b %H:%M" : "%b " DAY_FMT " %H:%M"; strftime(tmp, sizeof(tmp), fmt, localtime(&tim)); fprintf(out, " %s ", tmp); safe_fprintf(out, "%s", archive_entry_pathname(entry)); @@ -413,6 +432,6 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry) if (archive_entry_hardlink(entry)) /* Hard link */ safe_fprintf(out, " link to %s", archive_entry_hardlink(entry)); - else if (S_ISLNK(st->st_mode)) /* Symbolic link */ + else if (archive_entry_symlink(entry)) /* Symbolic link */ safe_fprintf(out, " -> %s", archive_entry_symlink(entry)); } diff --git a/usr.bin/tar/siginfo.c b/usr.bin/tar/siginfo.c deleted file mode 100644 index e0881ac..0000000 --- a/usr.bin/tar/siginfo.c +++ /dev/null @@ -1,151 +0,0 @@ -/*- - * Copyright 2008 Colin Percival - * 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(S) ``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(S) 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 "bsdtar_platform.h" -__FBSDID("$FreeBSD$"); - -#include <errno.h> -#include <signal.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "bsdtar.h" - -/* Is there a pending SIGINFO or SIGUSR1? */ -static volatile sig_atomic_t siginfo_received = 0; - -struct siginfo_data { - /* What sort of operation are we doing? */ - char * oper; - - /* What path are we handling? */ - char * path; - - /* How large is the archive entry? */ - int64_t size; - - /* Old signal handlers. */ -#ifdef SIGINFO - void (*siginfo_old)(int); -#endif - void (*sigusr1_old)(int); -}; - -static void siginfo_handler(int sig); - -/* Handler for SIGINFO / SIGUSR1. */ -static void -siginfo_handler(int sig) -{ - - (void)sig; /* UNUSED */ - - /* Record that SIGINFO or SIGUSR1 has been received. */ - siginfo_received = 1; -} - -void -siginfo_init(struct bsdtar *bsdtar) -{ - - /* Allocate space for internal structure. */ - if ((bsdtar->siginfo = malloc(sizeof(struct siginfo_data))) == NULL) - bsdtar_errc(bsdtar, 1, errno, "malloc failed"); - - /* Set the strings to NULL so that free() is safe. */ - bsdtar->siginfo->path = bsdtar->siginfo->oper = NULL; - -#ifdef SIGINFO - /* We want to catch SIGINFO, if it exists. */ - bsdtar->siginfo->siginfo_old = signal(SIGINFO, siginfo_handler); -#endif -#ifdef SIGUSR1 - /* ... and treat SIGUSR1 the same way as SIGINFO. */ - bsdtar->siginfo->sigusr1_old = signal(SIGUSR1, siginfo_handler); -#endif -} - -void -siginfo_setinfo(struct bsdtar *bsdtar, const char * oper, const char * path, - int64_t size) -{ - - /* Free old operation and path strings. */ - free(bsdtar->siginfo->oper); - free(bsdtar->siginfo->path); - - /* Duplicate strings and store entry size. */ - if ((bsdtar->siginfo->oper = strdup(oper)) == NULL) - bsdtar_errc(bsdtar, 1, errno, "Cannot strdup"); - if ((bsdtar->siginfo->path = strdup(path)) == NULL) - bsdtar_errc(bsdtar, 1, errno, "Cannot strdup"); - bsdtar->siginfo->size = size; -} - -void -siginfo_printinfo(struct bsdtar *bsdtar, off_t progress) -{ - - /* If there's a signal to handle and we know what we're doing... */ - if ((siginfo_received == 1) && - (bsdtar->siginfo->path != NULL) && - (bsdtar->siginfo->oper != NULL)) { - if (bsdtar->verbose) - fprintf(stderr, "\n"); - if (bsdtar->siginfo->size > 0) { - safe_fprintf(stderr, "%s %s (%ju / %" PRId64 ")", - bsdtar->siginfo->oper, bsdtar->siginfo->path, - (uintmax_t)progress, bsdtar->siginfo->size); - } else { - safe_fprintf(stderr, "%s %s", - bsdtar->siginfo->oper, bsdtar->siginfo->path); - } - if (!bsdtar->verbose) - fprintf(stderr, "\n"); - siginfo_received = 0; - } -} - -void -siginfo_done(struct bsdtar *bsdtar) -{ - -#ifdef SIGINFO - /* Restore old SIGINFO handler. */ - signal(SIGINFO, bsdtar->siginfo->siginfo_old); -#endif -#ifdef SIGUSR1 - /* And the old SIGUSR1 handler, too. */ - signal(SIGUSR1, bsdtar->siginfo->sigusr1_old); -#endif - - /* Free strings. */ - free(bsdtar->siginfo->path); - free(bsdtar->siginfo->oper); - - /* Free internal data structure. */ - free(bsdtar->siginfo); -} diff --git a/usr.bin/tar/subst.c b/usr.bin/tar/subst.c index 13464cd..44fd87b 100644 --- a/usr.bin/tar/subst.c +++ b/usr.bin/tar/subst.c @@ -28,6 +28,7 @@ __FBSDID("$FreeBSD$"); #if HAVE_REGEX_H #include "bsdtar.h" +#include "err.h" #include <errno.h> #include <regex.h> @@ -56,7 +57,7 @@ init_substitution(struct bsdtar *bsdtar) bsdtar->substitution = subst = malloc(sizeof(*subst)); if (subst == NULL) - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + bsdtar_errc(1, errno, "Out of memory"); subst->first_rule = subst->last_rule = NULL; } @@ -76,7 +77,7 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text) rule = malloc(sizeof(*rule)); if (rule == NULL) - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + bsdtar_errc(1, errno, "Out of memory"); rule->next = NULL; if (subst->last_rule == NULL) @@ -86,32 +87,32 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text) subst->last_rule = rule; if (*rule_text == '\0') - bsdtar_errc(bsdtar, 1, 0, "Empty replacement string"); + bsdtar_errc(1, 0, "Empty replacement string"); end_pattern = strchr(rule_text + 1, *rule_text); if (end_pattern == NULL) - bsdtar_errc(bsdtar, 1, 0, "Invalid replacement string"); + bsdtar_errc(1, 0, "Invalid replacement string"); pattern = malloc(end_pattern - rule_text); if (pattern == NULL) - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + bsdtar_errc(1, errno, "Out of memory"); memcpy(pattern, rule_text + 1, end_pattern - rule_text - 1); pattern[end_pattern - rule_text - 1] = '\0'; if ((r = regcomp(&rule->re, pattern, REG_BASIC)) != 0) { char buf[80]; regerror(r, &rule->re, buf, sizeof(buf)); - bsdtar_errc(bsdtar, 1, 0, "Invalid regular expression: %s", buf); + bsdtar_errc(1, 0, "Invalid regular expression: %s", buf); } free(pattern); start_subst = end_pattern + 1; end_pattern = strchr(start_subst, *rule_text); if (end_pattern == NULL) - bsdtar_errc(bsdtar, 1, 0, "Invalid replacement string"); + bsdtar_errc(1, 0, "Invalid replacement string"); rule->result = malloc(end_pattern - start_subst + 1); if (rule->result == NULL) - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + bsdtar_errc(1, errno, "Out of memory"); memcpy(rule->result, start_subst, end_pattern - start_subst); rule->result[end_pattern - start_subst] = '\0'; @@ -134,13 +135,13 @@ add_substitution(struct bsdtar *bsdtar, const char *rule_text) rule->symlink = 1; break; default: - bsdtar_errc(bsdtar, 1, 0, "Invalid replacement flag %c", *end_pattern); + bsdtar_errc(1, 0, "Invalid replacement flag %c", *end_pattern); } } } static void -realloc_strncat(struct bsdtar *bsdtar, char **str, const char *append, size_t len) +realloc_strncat(char **str, const char *append, size_t len) { char *new_str; size_t old_len; @@ -152,7 +153,7 @@ realloc_strncat(struct bsdtar *bsdtar, char **str, const char *append, size_t le new_str = malloc(old_len + len + 1); if (new_str == NULL) - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + bsdtar_errc(1, errno, "Out of memory"); memcpy(new_str, *str, old_len); memcpy(new_str + old_len, append, len); new_str[old_len + len] = '\0'; @@ -161,7 +162,7 @@ realloc_strncat(struct bsdtar *bsdtar, char **str, const char *append, size_t le } static void -realloc_strcat(struct bsdtar *bsdtar, char **str, const char *append) +realloc_strcat(char **str, const char *append) { char *new_str; size_t old_len; @@ -173,7 +174,7 @@ realloc_strcat(struct bsdtar *bsdtar, char **str, const char *append) new_str = malloc(old_len + strlen(append) + 1); if (new_str == NULL) - bsdtar_errc(bsdtar, 1, errno, "Out of memory"); + bsdtar_errc(1, errno, "Out of memory"); memcpy(new_str, *str, old_len); strcpy(new_str + old_len, append); free(*str); @@ -206,12 +207,12 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int s got_match = 1; print_match |= rule->print; - realloc_strncat(bsdtar, result, name, matches[0].rm_so); + realloc_strncat(result, name, matches[0].rm_so); for (i = 0, j = 0; rule->result[i] != '\0'; ++i) { if (rule->result[i] == '~') { - realloc_strncat(bsdtar, result, rule->result + j, i - j); - realloc_strncat(bsdtar, result, name, matches[0].rm_eo); + realloc_strncat(result, rule->result + j, i - j); + realloc_strncat(result, name, matches[0].rm_eo); j = i + 1; continue; } @@ -223,7 +224,7 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int s switch (c) { case '~': case '\\': - realloc_strncat(bsdtar, result, rule->result + j, i - j - 1); + realloc_strncat(result, rule->result + j, i - j - 1); j = i; break; case '1': @@ -235,13 +236,13 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int s case '7': case '8': case '9': - realloc_strncat(bsdtar, result, rule->result + j, i - j - 1); + realloc_strncat(result, rule->result + j, i - j - 1); if ((size_t)(c - '0') > (size_t)(rule->re.re_nsub)) { free(*result); *result = NULL; return -1; } - realloc_strncat(bsdtar, result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); + realloc_strncat(result, name + matches[c - '0'].rm_so, matches[c - '0'].rm_eo - matches[c - '0'].rm_so); j = i + 1; break; default: @@ -251,7 +252,7 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int s } - realloc_strcat(bsdtar, result, rule->result + j); + realloc_strcat(result, rule->result + j); name += matches[0].rm_eo; @@ -260,7 +261,7 @@ apply_substitution(struct bsdtar *bsdtar, const char *name, char **result, int s } if (got_match) - realloc_strcat(bsdtar, result, name); + realloc_strcat(result, name); if (print_match) fprintf(stderr, "%s >> %s\n", path, *result); diff --git a/usr.bin/tar/test/Makefile b/usr.bin/tar/test/Makefile index 2f6f056..3880be0 100644 --- a/usr.bin/tar/test/Makefile +++ b/usr.bin/tar/test/Makefile @@ -45,7 +45,6 @@ CFLAGS+= -I${TAR_SRCDIR} # Uncomment to link against dmalloc #LDADD+= -L/usr/local/lib -ldmalloc #CFLAGS+= -I/usr/local/include -DUSE_DMALLOC -WARNS=6 check test: bsdtar_test ./bsdtar_test -p ${.OBJDIR}/../bsdtar -r ${.CURDIR} diff --git a/usr.bin/tar/test/test_option_T.c b/usr.bin/tar/test/test_option_T.c index 9f4a2b3..72dcd54 100644 --- a/usr.bin/tar/test/test_option_T.c +++ b/usr.bin/tar/test/test_option_T.c @@ -43,7 +43,7 @@ DEFINE_TEST(test_option_T) int r; struct stat st; - /* Create a simple dir heirarchy; bail if anything fails. */ + /* Create a simple dir hierarchy; bail if anything fails. */ if (!assertEqualInt(0, mkdir("d1", 0755))) return; if (!assertEqualInt(0, mkdir("d1/d2", 0755))) return; if (!touch("d1/f1")) return; diff --git a/usr.bin/tar/test/test_option_s.c b/usr.bin/tar/test/test_option_s.c index 1059066..c9a6899 100644 --- a/usr.bin/tar/test/test_option_s.c +++ b/usr.bin/tar/test/test_option_s.c @@ -44,7 +44,7 @@ DEFINE_TEST(test_option_s) { struct stat st; - /* Create a sample file heirarchy. */ + /* Create a sample file hierarchy. */ assertEqualInt(0, mkdir("in", 0755)); assertEqualInt(0, mkdir("in/d1", 0755)); assertEqualInt(0, mkfile("in/d1/foo", "foo")); diff --git a/usr.bin/tar/tree.c b/usr.bin/tar/tree.c index f70b6d5..58e9feb 100644 --- a/usr.bin/tar/tree.c +++ b/usr.bin/tar/tree.c @@ -313,7 +313,7 @@ tree_next(struct tree *t) * violation. Just crash now. */ if (t->visit_type == TREE_ERROR_FATAL) { const char *msg = "Unable to continue traversing" - " directory heirarchy after a fatal error."; + " directory hierarchy after a fatal error."; write(2, msg, strlen(msg)); *(int *)0 = 1; /* Deliberate SEGV; NULL pointer dereference. */ exit(1); /* In case the SEGV didn't work. */ diff --git a/usr.bin/tar/util.c b/usr.bin/tar/util.c index 7b2da48..858d472 100644 --- a/usr.bin/tar/util.c +++ b/usr.bin/tar/util.c @@ -36,9 +36,15 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_ERRNO_H #include <errno.h> #endif +#ifdef HAVE_IO_H +#include <io.h> +#endif #ifdef HAVE_STDARG_H #include <stdarg.h> #endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -54,12 +60,15 @@ __FBSDID("$FreeBSD$"); #endif #include "bsdtar.h" +#include "err.h" -static void bsdtar_vwarnc(struct bsdtar *, int code, - const char *fmt, va_list ap); static size_t bsdtar_expand_char(char *, size_t, char); static const char *strip_components(const char *path, int elements); +#if defined(_WIN32) && !defined(__CYGWIN__) +#define read _read +#endif + /* TODO: Hack up a version of mbtowc for platforms with no wide * character support at all. I think the following might suffice, * but it needs careful testing. @@ -88,7 +97,7 @@ safe_fprintf(FILE *f, const char *fmt, ...) char *fmtbuff_heap; /* If fmtbuff_stack is too small, we use malloc */ char *fmtbuff; /* Pointer to fmtbuff_stack or fmtbuff_heap. */ int fmtbuff_length; - int length; + int length, n; va_list ap; const char *p; unsigned i; @@ -125,14 +134,13 @@ safe_fprintf(FILE *f, const char *fmt, ...) /* Note: mbrtowc() has a cleaner API, but mbtowc() seems a bit * more portable, so we use that here instead. */ - mbtowc(NULL, NULL, 0); /* Reset the shift state. */ + n = mbtowc(NULL, NULL, 1); /* Reset the shift state. */ /* Write data, expanding unprintable characters. */ p = fmtbuff; i = 0; try_wc = 1; while (*p != '\0') { - int n; /* Convert to wide char, test if the wide * char is printable in the current locale. */ @@ -145,24 +153,24 @@ safe_fprintf(FILE *f, const char *fmt, ...) } else { /* Not printable, format the bytes. */ while (n-- > 0) - i += bsdtar_expand_char( + i += (unsigned)bsdtar_expand_char( outbuff, i, *p++); } } else { /* After any conversion failure, don't bother * trying to convert the rest. */ - i += bsdtar_expand_char(outbuff, i, *p++); + i += (unsigned)bsdtar_expand_char(outbuff, i, *p++); try_wc = 0; } /* If our output buffer is full, dump it and keep going. */ if (i > (sizeof(outbuff) - 20)) { - outbuff[i++] = '\0'; + outbuff[i] = '\0'; fprintf(f, "%s", outbuff); i = 0; } } - outbuff[i++] = '\0'; + outbuff[i] = '\0'; fprintf(f, "%s", outbuff); /* If we allocated a heap-based formatting buffer, free it now. */ @@ -203,37 +211,6 @@ bsdtar_expand_char(char *buff, size_t offset, char c) return (i - offset); } -static void -bsdtar_vwarnc(struct bsdtar *bsdtar, int code, const char *fmt, va_list ap) -{ - fprintf(stderr, "%s: ", bsdtar->progname); - vfprintf(stderr, fmt, ap); - if (code != 0) - fprintf(stderr, ": %s", strerror(code)); - fprintf(stderr, "\n"); -} - -void -bsdtar_warnc(struct bsdtar *bsdtar, int code, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - bsdtar_vwarnc(bsdtar, code, fmt, ap); - va_end(ap); -} - -void -bsdtar_errc(struct bsdtar *bsdtar, int eval, int code, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - bsdtar_vwarnc(bsdtar, code, fmt, ap); - va_end(ap); - exit(eval); -} - int yes(const char *fmt, ...) { @@ -269,95 +246,6 @@ yes(const char *fmt, ...) return (0); } -/* - * Read lines from file and do something with each one. If option_null - * is set, lines are terminated with zero bytes; otherwise, they're - * terminated with newlines. - * - * This uses a self-sizing buffer to handle arbitrarily-long lines. - * If the "process" function returns non-zero for any line, this - * function will return non-zero after attempting to process all - * remaining lines. - */ -int -process_lines(struct bsdtar *bsdtar, const char *pathname, - int (*process)(struct bsdtar *, const char *)) -{ - FILE *f; - char *buff, *buff_end, *line_start, *line_end, *p; - size_t buff_length, new_buff_length, bytes_read, bytes_wanted; - int separator; - int ret; - - separator = bsdtar->option_null ? '\0' : '\n'; - ret = 0; - - if (strcmp(pathname, "-") == 0) - f = stdin; - else - f = fopen(pathname, "r"); - if (f == NULL) - bsdtar_errc(bsdtar, 1, errno, "Couldn't open %s", pathname); - buff_length = 8192; - buff = malloc(buff_length); - if (buff == NULL) - bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read %s", pathname); - line_start = line_end = buff_end = buff; - for (;;) { - /* Get some more data into the buffer. */ - bytes_wanted = buff + buff_length - buff_end; - bytes_read = fread(buff_end, 1, bytes_wanted, f); - buff_end += bytes_read; - /* Process all complete lines in the buffer. */ - while (line_end < buff_end) { - if (*line_end == separator) { - *line_end = '\0'; - if ((*process)(bsdtar, line_start) != 0) - ret = -1; - line_start = line_end + 1; - line_end = line_start; - } else - line_end++; - } - if (feof(f)) - break; - if (ferror(f)) - bsdtar_errc(bsdtar, 1, errno, - "Can't read %s", pathname); - if (line_start > buff) { - /* Move a leftover fractional line to the beginning. */ - memmove(buff, line_start, buff_end - line_start); - buff_end -= line_start - buff; - line_end -= line_start - buff; - line_start = buff; - } else { - /* Line is too big; enlarge the buffer. */ - new_buff_length = buff_length * 2; - if (new_buff_length <= buff_length) - bsdtar_errc(bsdtar, 1, ENOMEM, - "Line too long in %s", pathname); - buff_length = new_buff_length; - p = realloc(buff, buff_length); - if (p == NULL) - bsdtar_errc(bsdtar, 1, ENOMEM, - "Line too long in %s", pathname); - buff_end = p + (buff_end - buff); - line_end = p + (line_end - buff); - line_start = buff = p; - } - } - /* At end-of-file, handle the final line. */ - if (line_end > line_start) { - *line_end = '\0'; - if ((*process)(bsdtar, line_start) != 0) - ret = -1; - } - free(buff); - if (f != stdin) - fclose(f); - return (ret); -} - /*- * The logic here for -C <dir> attempts to avoid * chdir() as long as possible. For example: @@ -374,6 +262,8 @@ process_lines(struct bsdtar *bsdtar, const char *pathname, * This way, programs that build tar command lines don't have to worry * about -C with non-existent directories; such requests will only * fail if the directory must be accessed. + * + * TODO: Make this handle Windows paths correctly. */ void set_chdir(struct bsdtar *bsdtar, const char *newdir) @@ -399,7 +289,7 @@ set_chdir(struct bsdtar *bsdtar, const char *newdir) free(old_pending); } if (bsdtar->pending_chdir == NULL) - bsdtar_errc(bsdtar, 1, errno, "No memory"); + bsdtar_errc(1, errno, "No memory"); } void @@ -409,23 +299,24 @@ do_chdir(struct bsdtar *bsdtar) return; if (chdir(bsdtar->pending_chdir) != 0) { - bsdtar_errc(bsdtar, 1, 0, "could not chdir to '%s'\n", + bsdtar_errc(1, 0, "could not chdir to '%s'\n", bsdtar->pending_chdir); } free(bsdtar->pending_chdir); bsdtar->pending_chdir = NULL; } -const char * -strip_components(const char *path, int elements) +static const char * +strip_components(const char *p, int elements) { - const char *p = path; - + /* Skip as many elements as necessary. */ while (elements > 0) { switch (*p++) { case '/': +#if defined(_WIN32) && !defined(__CYGWIN__) + case '\\': /* Support \ path sep on Windows ONLY. */ +#endif elements--; - path = p; break; case '\0': /* Path is too short, skip it. */ @@ -433,12 +324,25 @@ strip_components(const char *path, int elements) } } - while (*path == '/') - ++path; - if (*path == '\0') - return (NULL); - - return (path); + /* Skip any / characters. This handles short paths that have + * additional / termination. This also handles the case where + * the logic above stops in the middle of a duplicate // + * sequence (which would otherwise get converted to an + * absolute path). */ + for (;;) { + switch (*p) { + case '/': +#if defined(_WIN32) && !defined(__CYGWIN__) + case '\\': /* Support \ path sep on Windows ONLY. */ +#endif + ++p; + break; + case '\0': + return (NULL); + default: + return (p); + } + } } /* @@ -459,7 +363,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) #if HAVE_REGEX_H r = apply_substitution(bsdtar, name, &subst_name, 0); if (r == -1) { - bsdtar_warnc(bsdtar, 0, "Invalid substitution, skipping entry"); + bsdtar_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -475,7 +379,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (archive_entry_hardlink(entry)) { r = apply_substitution(bsdtar, archive_entry_hardlink(entry), &subst_name, 1); if (r == -1) { - bsdtar_warnc(bsdtar, 0, "Invalid substitution, skipping entry"); + bsdtar_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -486,7 +390,7 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (archive_entry_symlink(entry) != NULL) { r = apply_substitution(bsdtar, archive_entry_symlink(entry), &subst_name, 1); if (r == -1) { - bsdtar_warnc(bsdtar, 0, "Invalid substitution, skipping entry"); + bsdtar_warnc(0, "Invalid substitution, skipping entry"); return 1; } if (r == 1) { @@ -560,11 +464,11 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) if (p != name && !bsdtar->warned_lead_slash) { /* Generate a warning the first time this happens. */ if (slashonly) - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Removing leading '%c' from member names", name[0]); else - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "Removing leading drive letter from " "member names"); bsdtar->warned_lead_slash = 1; @@ -591,6 +495,28 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) } /* + * It would be nice to just use printf() for formatting large numbers, + * but the compatibility problems are quite a headache. Hence the + * following simple utility function. + */ +const char * +tar_i64toa(int64_t n0) +{ + static char buff[24]; + int64_t n = n0 < 0 ? -n0 : n0; + char *p = buff + sizeof(buff); + + *--p = '\0'; + do { + *--p = '0' + (int)(n % 10); + n /= 10; + } while (n > 0); + if (n0 < 0) + *--p = '-'; + return p; +} + +/* * Like strcmp(), but try to be a little more aware of the fact that * we're comparing two paths. Right now, it just handles leading * "./" and trailing '/' specially, so that "a/b/" == "./a/b" @@ -600,6 +526,9 @@ edit_pathname(struct bsdtar *bsdtar, struct archive_entry *entry) * TODO: Publish the path normalization routines in libarchive so * that bsdtar can normalize paths and use fast strcmp() instead * of this. + * + * Note: This is currently only used within write.c, so should + * not handle \ path separators. */ int diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c index efc94de..31005eb 100644 --- a/usr.bin/tar/write.c +++ b/usr.bin/tar/write.c @@ -29,9 +29,6 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_SYS_TYPES_H #include <sys/types.h> #endif -#ifdef HAVE_SYS_ACL_H -#include <sys/acl.h> -#endif #ifdef HAVE_SYS_IOCTL_H #include <sys/ioctl.h> #endif @@ -47,12 +44,12 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_FCNTL_H #include <fcntl.h> #endif -#ifdef HAVE_FNMATCH_H -#include <fnmatch.h> -#endif #ifdef HAVE_GRP_H #include <grp.h> #endif +#ifdef HAVE_IO_H +#include <io.h> +#endif #ifdef HAVE_LIMITS_H #include <limits.h> #endif @@ -73,6 +70,9 @@ __FBSDID("$FreeBSD$"); #ifdef HAVE_PWD_H #include <pwd.h> #endif +#ifdef HAVE_STDINT_H +#include <stdint.h> +#endif #include <stdio.h> #ifdef HAVE_STDLIB_H #include <stdlib.h> @@ -85,6 +85,8 @@ __FBSDID("$FreeBSD$"); #endif #include "bsdtar.h" +#include "err.h" +#include "line_reader.h" #include "tree.h" /* Size of buffer for holding file data prior to writing. */ @@ -124,12 +126,12 @@ static int append_archive_filename(struct bsdtar *, struct archive *, const char *fname); static void archive_names_from_file(struct bsdtar *bsdtar, struct archive *a); -static int archive_names_from_file_helper(struct bsdtar *bsdtar, - const char *line); -static int copy_file_data(struct bsdtar *bsdtar, - struct archive *a, struct archive *ina); +static int copy_file_data(struct bsdtar *, struct archive *a, + struct archive *ina, struct archive_entry *); static int new_enough(struct bsdtar *, const char *path, const struct stat *); +static void report_write(struct bsdtar *, struct archive *, + struct archive_entry *, int64_t progress); static void test_for_append(struct bsdtar *); static void write_archive(struct archive *, struct bsdtar *); static void write_entry_backend(struct bsdtar *, struct archive *, @@ -139,6 +141,23 @@ static int write_file_data(struct bsdtar *, struct archive *, static void write_hierarchy(struct bsdtar *, struct archive *, const char *); +#if defined(_WIN32) && !defined(__CYGWIN__) +/* Not a full lseek() emulation, but enough for our needs here. */ +static int +seek_file(int fd, int64_t offset, int whence) +{ + LARGE_INTEGER distance; + (void)whence; /* UNUSED */ + distance.QuadPart = offset; + return (SetFilePointerEx((HANDLE)_get_osfhandle(fd), + distance, NULL, FILE_BEGIN) ? 1 : -1); +} +#define open _open +#define close _close +#define read _read +#define lseek seek_file +#endif + void tar_mode_c(struct bsdtar *bsdtar) { @@ -146,7 +165,7 @@ tar_mode_c(struct bsdtar *bsdtar) int r; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) - bsdtar_errc(bsdtar, 1, 0, "no files or directories specified"); + bsdtar_errc(1, 0, "no files or directories specified"); a = archive_write_new(); @@ -161,7 +180,7 @@ tar_mode_c(struct bsdtar *bsdtar) fprintf(stderr, "Can't use format %s: %s\n", bsdtar->create_format, archive_error_string(a)); - usage(bsdtar); + usage(); } /* @@ -182,40 +201,39 @@ tar_mode_c(struct bsdtar *bsdtar) } else { switch (bsdtar->create_compression) { case 0: - archive_write_set_compression_none(a); + r = archive_write_set_compression_none(a); break; -#ifdef HAVE_LIBBZ2 case 'j': case 'y': - archive_write_set_compression_bzip2(a); + r = archive_write_set_compression_bzip2(a); break; -#endif -#ifdef HAVE_LIBLZMA case 'J': - archive_write_set_compression_xz(a); + r = archive_write_set_compression_xz(a); break; case OPTION_LZMA: - archive_write_set_compression_lzma(a); + r = archive_write_set_compression_lzma(a); break; -#endif -#ifdef HAVE_LIBZ case 'z': - archive_write_set_compression_gzip(a); + r = archive_write_set_compression_gzip(a); break; -#endif case 'Z': - archive_write_set_compression_compress(a); + r = archive_write_set_compression_compress(a); break; default: - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Unrecognized compression option -%c", bsdtar->create_compression); } + if (r != ARCHIVE_OK) { + bsdtar_errc(1, 0, + "Unsupported compression option -%c", + bsdtar->create_compression); + } } if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) - bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + bsdtar_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_file(a, bsdtar->filename)) - bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + bsdtar_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); } @@ -226,7 +244,7 @@ tar_mode_c(struct bsdtar *bsdtar) void tar_mode_r(struct bsdtar *bsdtar) { - off_t end_offset; + int64_t end_offset; int format; struct archive *a; struct archive_entry *entry; @@ -237,9 +255,13 @@ tar_mode_r(struct bsdtar *bsdtar) format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; +#if defined(__BORLANDC__) + bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT); +#else bsdtar->fd = open(bsdtar->filename, O_RDWR | O_CREAT, 0666); +#endif if (bsdtar->fd < 0) - bsdtar_errc(bsdtar, 1, errno, + bsdtar_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); @@ -248,14 +270,14 @@ tar_mode_r(struct bsdtar *bsdtar) archive_read_support_format_gnutar(a); r = archive_read_open_fd(a, bsdtar->fd, 10240); if (r != ARCHIVE_OK) - bsdtar_errc(bsdtar, 1, archive_errno(a), + bsdtar_errc(1, archive_errno(a), "Can't read archive %s: %s", bsdtar->filename, archive_error_string(a)); while (0 == archive_read_next_header(a, &entry)) { if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) { archive_read_finish(a); close(bsdtar->fd); - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Cannot append to compressed archive."); } /* Keep going until we hit end-of-archive */ @@ -284,7 +306,7 @@ tar_mode_r(struct bsdtar *bsdtar) format &= ARCHIVE_FORMAT_BASE_MASK; if (format != (int)(archive_format(a) & ARCHIVE_FORMAT_BASE_MASK) && format != ARCHIVE_FORMAT_EMPTY) { - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Format %s is incompatible with the archive %s.", bsdtar->create_format, bsdtar->filename); } @@ -300,11 +322,12 @@ tar_mode_r(struct bsdtar *bsdtar) format = ARCHIVE_FORMAT_TAR_PAX_RESTRICTED; archive_write_set_format(a, format); } - lseek(bsdtar->fd, end_offset, SEEK_SET); /* XXX check return val XXX */ + if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) + bsdtar_errc(1, errno, "Could not seek to archive end"); if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) - bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + bsdtar_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) - bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + bsdtar_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); /* XXX check return val XXX */ @@ -315,7 +338,7 @@ tar_mode_r(struct bsdtar *bsdtar) void tar_mode_u(struct bsdtar *bsdtar) { - off_t end_offset; + int64_t end_offset; struct archive *a; struct archive_entry *entry; int format; @@ -332,7 +355,7 @@ tar_mode_u(struct bsdtar *bsdtar) bsdtar->fd = open(bsdtar->filename, O_RDWR); if (bsdtar->fd < 0) - bsdtar_errc(bsdtar, 1, errno, + bsdtar_errc(1, errno, "Cannot open %s", bsdtar->filename); a = archive_read_new(); @@ -342,7 +365,7 @@ tar_mode_u(struct bsdtar *bsdtar) if (archive_read_open_fd(a, bsdtar->fd, bsdtar->bytes_per_block != 0 ? bsdtar->bytes_per_block : DEFAULT_BYTES_PER_BLOCK) != ARCHIVE_OK) { - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Can't open %s: %s", bsdtar->filename, archive_error_string(a)); } @@ -352,7 +375,7 @@ tar_mode_u(struct bsdtar *bsdtar) if (archive_compression(a) != ARCHIVE_COMPRESSION_NONE) { archive_read_finish(a); close(bsdtar->fd); - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Cannot append to compressed archive."); } add_dir_list(bsdtar, archive_entry_pathname(entry), @@ -382,12 +405,12 @@ tar_mode_u(struct bsdtar *bsdtar) bsdtar->bytes_per_block); } else archive_write_set_bytes_per_block(a, DEFAULT_BYTES_PER_BLOCK); - lseek(bsdtar->fd, end_offset, SEEK_SET); - ftruncate(bsdtar->fd, end_offset); + if (lseek(bsdtar->fd, end_offset, SEEK_SET) < 0) + bsdtar_errc(1, errno, "Could not seek to archive end"); if (ARCHIVE_OK != archive_write_set_options(a, bsdtar->option_options)) - bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + bsdtar_errc(1, 0, "%s", archive_error_string(a)); if (ARCHIVE_OK != archive_write_open_fd(a, bsdtar->fd)) - bsdtar_errc(bsdtar, 1, 0, archive_error_string(a)); + bsdtar_errc(1, 0, "%s", archive_error_string(a)); write_archive(a, bsdtar); @@ -413,19 +436,16 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) const char *arg; struct archive_entry *entry, *sparse_entry; - /* We want to catch SIGINFO and SIGUSR1. */ - siginfo_init(bsdtar); - /* Allocate a buffer for file data. */ if ((bsdtar->buff = malloc(FILEDATABUFLEN)) == NULL) - bsdtar_errc(bsdtar, 1, 0, "cannot allocate memory"); + bsdtar_errc(1, 0, "cannot allocate memory"); if ((bsdtar->resolver = archive_entry_linkresolver_new()) == NULL) - bsdtar_errc(bsdtar, 1, 0, "cannot create link resolver"); + bsdtar_errc(1, 0, "cannot create link resolver"); archive_entry_linkresolver_set_strategy(bsdtar->resolver, archive_format(a)); if ((bsdtar->diskreader = archive_read_disk_new()) == NULL) - bsdtar_errc(bsdtar, 1, 0, "Cannot create read_disk object"); + bsdtar_errc(1, 0, "Cannot create read_disk object"); archive_read_disk_set_standard_lookup(bsdtar->diskreader); if (bsdtar->names_from_file != NULL) @@ -439,7 +459,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) bsdtar->argv++; arg = *bsdtar->argv; if (arg == NULL) { - bsdtar_warnc(bsdtar, 1, 0, + bsdtar_warnc(0, "%s", "Missing argument for -C"); bsdtar->return_value = 1; goto cleanup; @@ -454,12 +474,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) arg + 1) != 0) break; } else -#if defined(_WIN32) && !defined(__CYGWIN__) - write_hierarchy_win(bsdtar, a, arg, - write_hierarchy); -#else write_hierarchy(bsdtar, a, arg); -#endif } bsdtar->argv++; } @@ -474,7 +489,7 @@ write_archive(struct archive *a, struct bsdtar *bsdtar) } if (archive_write_close(a)) { - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + bsdtar_warnc(0, "%s", archive_error_string(a)); bsdtar->return_value = 1; } @@ -487,14 +502,11 @@ cleanup: bsdtar->diskreader = NULL; if (bsdtar->option_totals) { - fprintf(stderr, "Total bytes written: " BSDTAR_FILESIZE_PRINTF "\n", - (BSDTAR_FILESIZE_TYPE)archive_position_compressed(a)); + fprintf(stderr, "Total bytes written: %s\n", + tar_i64toa(archive_position_compressed(a))); } archive_write_finish(a); - - /* Restore old SIGINFO + SIGUSR1 handlers. */ - siginfo_done(bsdtar); } /* @@ -504,36 +516,34 @@ cleanup: * cause the next line to be a directory to pass to chdir(). If * --null is specified, then a line "-C" is just another filename. */ -void +static void archive_names_from_file(struct bsdtar *bsdtar, struct archive *a) { - bsdtar->archive = a; + struct lafe_line_reader *lr; + const char *line; bsdtar->next_line_is_dir = 0; - process_lines(bsdtar, bsdtar->names_from_file, - archive_names_from_file_helper); + + lr = lafe_line_reader(bsdtar->names_from_file, bsdtar->option_null); + while ((line = lafe_line_reader_next(lr)) != NULL) { + if (bsdtar->next_line_is_dir) { + set_chdir(bsdtar, line); + bsdtar->next_line_is_dir = 0; + } else if (!bsdtar->option_null && strcmp(line, "-C") == 0) + bsdtar->next_line_is_dir = 1; + else { + if (*line != '/') + do_chdir(bsdtar); /* Handle a deferred -C */ + write_hierarchy(bsdtar, a, line); + } + } + lafe_line_reader_free(lr); if (bsdtar->next_line_is_dir) - bsdtar_errc(bsdtar, 1, errno, + bsdtar_errc(1, errno, "Unexpected end of filename list; " "directory expected after -C"); } -static int -archive_names_from_file_helper(struct bsdtar *bsdtar, const char *line) -{ - if (bsdtar->next_line_is_dir) { - set_chdir(bsdtar, line); - bsdtar->next_line_is_dir = 0; - } else if (!bsdtar->option_null && strcmp(line, "-C") == 0) - bsdtar->next_line_is_dir = 1; - else { - if (*line != '/') - do_chdir(bsdtar); /* Handle a deferred -C */ - write_hierarchy(bsdtar, bsdtar->archive, line); - } - return (0); -} - /* * Copy from specified archive to current archive. Returns non-zero * for write errors (which force us to terminate the entire archiving @@ -555,15 +565,15 @@ append_archive_filename(struct bsdtar *bsdtar, struct archive *a, archive_read_support_format_all(ina); archive_read_support_compression_all(ina); if (archive_read_open_file(ina, filename, 10240)) { - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(ina)); + bsdtar_warnc(0, "%s", archive_error_string(ina)); bsdtar->return_value = 1; return (0); } rc = append_archive(bsdtar, a, ina); - if (archive_errno(ina)) { - bsdtar_warnc(bsdtar, 0, "Error reading archive %s: %s", + if (rc != ARCHIVE_OK) { + bsdtar_warnc(0, "Error reading archive %s: %s", filename, archive_error_string(ina)); bsdtar->return_value = 1; } @@ -582,7 +592,7 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) if (!new_enough(bsdtar, archive_entry_pathname(in_entry), archive_entry_stat(in_entry))) continue; - if (excluded(bsdtar, archive_entry_pathname(in_entry))) + if (lafe_excluded(bsdtar->matching, archive_entry_pathname(in_entry))) continue; if (bsdtar->option_interactive && !yes("copy '%s'", archive_entry_pathname(in_entry))) @@ -590,15 +600,13 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) if (bsdtar->verbose) safe_fprintf(stderr, "a %s", archive_entry_pathname(in_entry)); - siginfo_setinfo(bsdtar, "copying", - archive_entry_pathname(in_entry), - archive_entry_size(in_entry)); - siginfo_printinfo(bsdtar, 0); + if (need_report()) + report_write(bsdtar, a, in_entry, 0); e = archive_write_header(a, in_entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) - bsdtar_warnc(bsdtar, 0, "%s: %s", + bsdtar_warnc(0, "%s: %s", archive_entry_pathname(in_entry), archive_error_string(a)); else @@ -610,7 +618,7 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) if (e >= ARCHIVE_WARN) { if (archive_entry_size(in_entry) == 0) archive_read_data_skip(ina); - else if (copy_file_data(bsdtar, a, ina)) + else if (copy_file_data(bsdtar, a, ina, in_entry)) exit(1); } @@ -624,20 +632,22 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) /* Helper function to copy data between archives. */ static int -copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina) +copy_file_data(struct bsdtar *bsdtar, struct archive *a, + struct archive *ina, struct archive_entry *entry) { ssize_t bytes_read; ssize_t bytes_written; - off_t progress = 0; + int64_t progress = 0; bytes_read = archive_read_data(ina, bsdtar->buff, FILEDATABUFLEN); while (bytes_read > 0) { - siginfo_printinfo(bsdtar, progress); + if (need_report()) + report_write(bsdtar, a, entry, progress); bytes_written = archive_write_data(a, bsdtar->buff, bytes_read); if (bytes_written < bytes_read) { - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + bsdtar_warnc(0, "%s", archive_error_string(a)); return (-1); } progress += bytes_written; @@ -664,12 +674,12 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) tree = tree_open(path); if (!tree) { - bsdtar_warnc(bsdtar, errno, "%s: Cannot open", path); + bsdtar_warnc(errno, "%s: Cannot open", path); bsdtar->return_value = 1; return; } - while ((tree_ret = tree_next(tree))) { + while ((tree_ret = tree_next(tree)) != 0) { int r; const char *name = tree_current_path(tree); const struct stat *st = NULL; /* info to use for this entry */ @@ -677,11 +687,11 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) int descend; if (tree_ret == TREE_ERROR_FATAL) - bsdtar_errc(bsdtar, 1, tree_errno(tree), + bsdtar_errc(1, tree_errno(tree), "%s: Unable to continue traversing directory tree", name); if (tree_ret == TREE_ERROR_DIR) { - bsdtar_warnc(bsdtar, errno, + bsdtar_warnc(errno, "%s: Couldn't visit directory", name); bsdtar->return_value = 1; } @@ -692,7 +702,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) * If this file/dir is excluded by a filename * pattern, skip it. */ - if (excluded(bsdtar, name)) + if (lafe_excluded(bsdtar->matching, name)) continue; /* @@ -701,7 +711,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) lst = tree_current_lstat(tree); if (lst == NULL) { /* Couldn't lstat(); must not exist. */ - bsdtar_warnc(bsdtar, errno, "%s: Cannot stat", name); + bsdtar_warnc(errno, "%s: Cannot stat", name); /* Return error if files disappear during traverse. */ bsdtar->return_value = 1; continue; @@ -739,17 +749,38 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) } /* - * If user has asked us not to cross mount points, - * then don't descend into into a dir on a different - * device. + * Are we about to cross to a new filesystem? */ if (!dev_recorded) { + /* This is the initial file system. */ first_dev = lst->st_dev; dev_recorded = 1; - } - if (bsdtar->option_dont_traverse_mounts) { - if (lst->st_dev != first_dev) - descend = 0; + } else if (lst->st_dev == first_dev) { + /* The starting file system is always acceptable. */ + } else if (descend == 0) { + /* We're not descending, so no need to check. */ + } else if (bsdtar->option_dont_traverse_mounts) { + /* User has asked us not to cross mount points. */ + descend = 0; + } else { + /* We're prepared to cross a mount point. */ + + /* XXX TODO: check whether this filesystem is + * synthetic and/or local. Add a new + * --local-only option to skip non-local + * filesystems. Skip synthetic filesystems + * regardless. + * + * The results should be cached, since + * tree.c doesn't usually visit a directory + * and the directory contents together. A simple + * move-to-front list should perform quite well. + * + * This is going to be heavily OS dependent: + * FreeBSD's statfs() in conjunction with getvfsbyname() + * provides all of this; NetBSD's statvfs() does + * most of it; other systems will vary. + */ } /* @@ -771,11 +802,27 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) * calling this so we can pass in an fd and shorten * the race to query metadata. The linkify dance * makes this more complex than it might sound. */ +#if defined(_WIN32) && !defined(__CYGWIN__) + /* TODO: tree.c uses stat(), which is badly broken + * on Windows. To fix this, we should + * deprecate tree_current_stat() and provide a new + * call tree_populate_entry(t, entry). This call + * would use stat() internally on POSIX and + * GetInfoByFileHandle() internally on Windows. + * This would be another step towards a tree-walker + * that can be integrated deep into libarchive. + * For now, just set st to NULL on Windows; + * archive_read_disk_entry_from_file() should + * be smart enough to use platform-appropriate + * ways to probe file information. + */ + st = NULL; +#endif r = archive_read_disk_entry_from_file(bsdtar->diskreader, entry, -1, st); if (r != ARCHIVE_OK) - bsdtar_warnc(bsdtar, archive_errno(bsdtar->diskreader), - archive_error_string(bsdtar->diskreader)); + bsdtar_warnc(archive_errno(bsdtar->diskreader), + "%s", archive_error_string(bsdtar->diskreader)); if (r < ARCHIVE_WARN) continue; @@ -786,7 +833,7 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) * If this file/dir is flagged "nodump" and we're * honoring such flags, skip this file/dir. */ -#ifdef HAVE_STRUCT_STAT_ST_FLAGS +#if defined(HAVE_STRUCT_STAT_ST_FLAGS) && defined(UF_NODUMP) /* BSD systems store flags in struct stat */ if (bsdtar->option_honor_nodump && (lst->st_flags & UF_NODUMP)) @@ -835,17 +882,11 @@ write_hierarchy(struct bsdtar *bsdtar, struct archive *a, const char *path) archive_entry_pathname(entry)); /* Non-regular files get archived with zero size. */ - if (!S_ISREG(st->st_mode)) + if (archive_entry_filetype(entry) != AE_IFREG) archive_entry_set_size(entry, 0); - /* Record what we're doing, for SIGINFO / SIGUSR1. */ - siginfo_setinfo(bsdtar, "adding", - archive_entry_pathname(entry), archive_entry_size(entry)); archive_entry_linkify(bsdtar->resolver, &entry, &spare_entry); - /* Handle SIGINFO / SIGUSR1 request if one was made. */ - siginfo_printinfo(bsdtar, 0); - while (entry != NULL) { write_entry_backend(bsdtar, a, entry); archive_entry_free(entry); @@ -875,7 +916,7 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a, fd = open(pathname, O_RDONLY); if (fd == -1) { if (!bsdtar->verbose) - bsdtar_warnc(bsdtar, errno, + bsdtar_warnc(errno, "%s: could not open file", pathname); else fprintf(stderr, ": %s", strerror(errno)); @@ -886,7 +927,7 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a, e = archive_write_header(a, entry); if (e != ARCHIVE_OK) { if (!bsdtar->verbose) - bsdtar_warnc(bsdtar, 0, "%s: %s", + bsdtar_warnc(0, "%s: %s", archive_entry_pathname(entry), archive_error_string(a)); else @@ -915,6 +956,28 @@ write_entry_backend(struct bsdtar *bsdtar, struct archive *a, close(fd); } +static void +report_write(struct bsdtar *bsdtar, struct archive *a, + struct archive_entry *entry, int64_t progress) +{ + uint64_t comp, uncomp; + if (bsdtar->verbose) + fprintf(stderr, "\n"); + comp = archive_position_compressed(a); + uncomp = archive_position_uncompressed(a); + fprintf(stderr, "In: %d files, %s bytes;", + archive_file_count(a), tar_i64toa(uncomp)); + fprintf(stderr, + " Out: %s bytes, compression %d%%\n", + tar_i64toa(comp), (int)((uncomp - comp) * 100 / uncomp)); + /* Can't have two calls to tar_i64toa() pending, so split the output. */ + safe_fprintf(stderr, "Current: %s (%s", + archive_entry_pathname(entry), + tar_i64toa(progress)); + fprintf(stderr, "/%s bytes)\n", + tar_i64toa(archive_entry_size(entry))); +} + /* Helper function to copy file to archive. */ static int @@ -923,22 +986,23 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, { ssize_t bytes_read; ssize_t bytes_written; - off_t progress = 0; + int64_t progress = 0; bytes_read = read(fd, bsdtar->buff, FILEDATABUFLEN); while (bytes_read > 0) { - siginfo_printinfo(bsdtar, progress); + if (need_report()) + report_write(bsdtar, a, entry, progress); bytes_written = archive_write_data(a, bsdtar->buff, bytes_read); if (bytes_written < 0) { /* Write failed; this is bad */ - bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a)); + bsdtar_warnc(0, "%s", archive_error_string(a)); return (-1); } if (bytes_written < bytes_read) { /* Write was truncated; warn but continue. */ - bsdtar_warnc(bsdtar, 0, + bsdtar_warnc(0, "%s: Truncated write; file may have grown while being archived.", archive_entry_pathname(entry)); return (0); @@ -952,7 +1016,7 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, /* * Test if the specified file is new enough to include in the archive. */ -int +static int new_enough(struct bsdtar *bsdtar, const char *path, const struct stat *st) { struct archive_dir_entry *p; @@ -1023,11 +1087,11 @@ add_dir_list(struct bsdtar *bsdtar, const char *path, p = malloc(sizeof(*p)); if (p == NULL) - bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read archive directory"); + bsdtar_errc(1, ENOMEM, "Can't read archive directory"); p->name = strdup(path); if (p->name == NULL) - bsdtar_errc(bsdtar, 1, ENOMEM, "Can't read archive directory"); + bsdtar_errc(1, ENOMEM, "Can't read archive directory"); p->mtime_sec = mtime_sec; p->mtime_nsec = mtime_nsec; p->next = NULL; @@ -1039,25 +1103,32 @@ add_dir_list(struct bsdtar *bsdtar, const char *path, } } -void +static void test_for_append(struct bsdtar *bsdtar) { struct stat s; if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL) - bsdtar_errc(bsdtar, 1, 0, "no files or directories specified"); + bsdtar_errc(1, 0, "no files or directories specified"); if (bsdtar->filename == NULL) - bsdtar_errc(bsdtar, 1, 0, "Cannot append to stdout."); + bsdtar_errc(1, 0, "Cannot append to stdout."); if (bsdtar->create_compression != 0) - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Cannot append to %s with compression", bsdtar->filename); if (stat(bsdtar->filename, &s) != 0) return; if (!S_ISREG(s.st_mode) && !S_ISBLK(s.st_mode)) - bsdtar_errc(bsdtar, 1, 0, + bsdtar_errc(1, 0, "Cannot append to %s: not a regular file.", bsdtar->filename); + +/* Is this an appropriate check here on Windows? */ +/* + if (GetFileType(handle) != FILE_TYPE_DISK) + bsdtar_errc(1, 0, "Cannot append"); +*/ + } diff --git a/usr.bin/tcopy/Makefile b/usr.bin/tcopy/Makefile index 1bae0b2..feb5769 100644 --- a/usr.bin/tcopy/Makefile +++ b/usr.bin/tcopy/Makefile @@ -3,4 +3,6 @@ PROG= tcopy +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/tcopy/tcopy.c b/usr.bin/tcopy/tcopy.c index 540ac06..4d9d516 100644 --- a/usr.bin/tcopy/tcopy.c +++ b/usr.bin/tcopy/tcopy.c @@ -70,17 +70,15 @@ FILE *msg; void *getspace(int); void intr(int); -static void usage(void); +static void usage(void) __dead2; void verify(int, int, char *); void writeop(int, int); void rewind_tape(int); int -main(argc, argv) - int argc; - char *argv[]; +main(int argc, char *argv[]) { - register int lastnread, nread, nw, inp, outp; + int lastnread, nread, nw, inp, outp; enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; sig_t oldsig; int ch, needeof; @@ -227,12 +225,10 @@ r1: guesslen = 0; } void -verify(inp, outp, outb) - register int inp, outp; - register char *outb; +verify(int inp, int outp, char *outb) { - register int eot, inmaxblk, inn, outmaxblk, outn; - register char *inb; + int eot, inmaxblk, inn, outmaxblk, outn; + char *inb; inb = getspace(maxblk); inmaxblk = outmaxblk = maxblk; @@ -281,8 +277,7 @@ r2: if (inn != outn) { } void -intr(signo) - int signo __unused; +intr(int signo __unused) { if (record) { if (record - lastrec > 1) @@ -296,8 +291,7 @@ intr(signo) } void * -getspace(blk) - int blk; +getspace(int blk) { void *bp; @@ -307,8 +301,7 @@ getspace(blk) } void -writeop(fd, type) - int fd, type; +writeop(int fd, int type) { struct mtop op; @@ -319,7 +312,7 @@ writeop(fd, type) } static void -usage() +usage(void) { fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n"); exit(1); diff --git a/usr.bin/telnet/Makefile b/usr.bin/telnet/Makefile index ee112ff..0ef55c5 100644 --- a/usr.bin/telnet/Makefile +++ b/usr.bin/telnet/Makefile @@ -17,6 +17,8 @@ CFLAGS+= -DKLUDGELINEMODE -DUSE_TERMIO -DENV_HACK -DOPIE \ CFLAGS+= -DINET6 .endif +WARNS?= 2 + LIBTELNET= ${.OBJDIR}/../../lib/libtelnet/libtelnet.a DPADD= ${LIBTERMCAP} ${LIBTELNET} diff --git a/usr.bin/tftp/tftp.c b/usr.bin/tftp/tftp.c index 2f032df..4300902 100644 --- a/usr.bin/tftp/tftp.c +++ b/usr.bin/tftp/tftp.c @@ -140,6 +140,7 @@ send_data: (struct sockaddr *)&peer, peer.ss_len); if (n != size + 4) { warn("sendto"); + txrx_error = 1; goto abort; } read_ahead(file, convert); @@ -153,6 +154,7 @@ send_data: alarm(0); if (n < 0) { warn("recvfrom"); + txrx_error = 1; goto abort; } if (!serv.ss_family) @@ -160,6 +162,7 @@ send_data: else if (!cmpport((struct sockaddr *)&serv, (struct sockaddr *)&from)) { warn("server port mismatch"); + txrx_error = 1; goto abort; } peer = from; @@ -202,7 +205,6 @@ abort: stopclock(); if (amount > 0) printstats("Sent", amount); - txrx_error = 1; } /* @@ -255,6 +257,7 @@ send_ack: peer.ss_len) != size) { alarm(0); warn("sendto"); + txrx_error = 1; goto abort; } write_behind(file, convert); @@ -268,6 +271,7 @@ send_ack: alarm(0); if (n < 0) { warn("recvfrom"); + txrx_error = 1; goto abort; } if (!serv.ss_family) @@ -275,6 +279,7 @@ send_ack: else if (!cmpport((struct sockaddr *)&serv, (struct sockaddr *)&from)) { warn("server port mismatch"); + txrx_error = 1; goto abort; } peer = from; @@ -325,7 +330,6 @@ abort: /* ok to ack, since user */ stopclock(); if (amount > 0) printstats("Received", amount); - txrx_error = 1; } static int diff --git a/usr.bin/time/Makefile b/usr.bin/time/Makefile index 5a580d2..e462af66 100644 --- a/usr.bin/time/Makefile +++ b/usr.bin/time/Makefile @@ -2,6 +2,5 @@ # $FreeBSD$ PROG= time -WARNS?=6 .include <bsd.prog.mk> diff --git a/usr.bin/top/Makefile b/usr.bin/top/Makefile index 182714a..864473f 100644 --- a/usr.bin/top/Makefile +++ b/usr.bin/top/Makefile @@ -10,6 +10,8 @@ SRCS+= sigdesc.h top.local.h CFLAGS+= -DHAVE_GETOPT -DHAVE_STRERROR -DORDER CFLAGS+= -I${.CURDIR} -I${TOPDIR} -I. +WARNS?= 0 + # # The table size should be a prime number approximately twice as # large as the number of lines in /etc/passwd. The default number diff --git a/usr.bin/touch/touch.c b/usr.bin/touch/touch.c index 624ce05..5ceb175 100644 --- a/usr.bin/touch/touch.c +++ b/usr.bin/touch/touch.c @@ -164,6 +164,11 @@ main(int argc, char *argv[]) for (rval = 0; *argv; ++argv) { /* See if the file exists. */ if (stat_f(*argv, &sb) != 0) { + if (errno != ENOENT) { + rval = 1; + warn("%s", *argv); + continue; + } if (!cflag) { /* Create the file. */ fd = open(*argv, @@ -206,7 +211,7 @@ main(int argc, char *argv[]) continue; /* If the user specified a time, nothing else we can do. */ - if (timeset) { + if (timeset || Aflag) { rval = 1; warn("%s", *argv); continue; @@ -222,11 +227,13 @@ main(int argc, char *argv[]) continue; /* Try reading/writing. */ - if (!S_ISLNK(sb.st_mode) && !S_ISDIR(sb.st_mode) && - rw(*argv, &sb, fflag)) + if (!S_ISLNK(sb.st_mode) && !S_ISDIR(sb.st_mode)) { + if (rw(*argv, &sb, fflag)) + rval = 1; + } else { rval = 1; - else warn("%s", *argv); + } } exit(rval); } diff --git a/usr.bin/tr/Makefile b/usr.bin/tr/Makefile index 00bdd8d..c5c5297 100644 --- a/usr.bin/tr/Makefile +++ b/usr.bin/tr/Makefile @@ -4,4 +4,6 @@ PROG= tr SRCS= cmap.c cset.c str.c tr.c +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/truss/Makefile b/usr.bin/truss/Makefile index 104cd96..d907ad5 100644 --- a/usr.bin/truss/Makefile +++ b/usr.bin/truss/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ -WARNS?= 6 NO_WERROR= PROG= truss SRCS= main.c setup.c syscalls.c syscalls.h ioctl.c ${MACHINE_ARCH}-fbsd.c diff --git a/usr.bin/truss/amd64-fbsd32.c b/usr.bin/truss/amd64-fbsd32.c index eab841c..3b1b882 100644 --- a/usr.bin/truss/amd64-fbsd32.c +++ b/usr.bin/truss/amd64-fbsd32.c @@ -315,19 +315,6 @@ amd64_fbsd32_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) } } - /* - * The pipe syscall returns its fds in two registers and has assembly glue - * to provide the libc API, so it cannot be handled like regular syscalls. - * The nargs check is so we don't have to do yet another strcmp on every - * syscall. - */ - if (!errorp && fsc.nargs == 0 && fsc.name && strcmp(fsc.name, "pipe") == 0) { - fsc.nargs = 1; - fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*)); - asprintf(&fsc.s_args[0], "[%d,%d]", (int)retval, (int)regs.r_rdx); - retval = 0; - } - if (fsc.name != NULL && (!strcmp(fsc.name, "freebsd32_execve") || !strcmp(fsc.name, "exit"))) { trussinfo->curthread->in_syscall = 1; diff --git a/usr.bin/truss/i386-fbsd.c b/usr.bin/truss/i386-fbsd.c index 8500f32..3a81392 100644 --- a/usr.bin/truss/i386-fbsd.c +++ b/usr.bin/truss/i386-fbsd.c @@ -305,19 +305,6 @@ i386_syscall_exit(struct trussinfo *trussinfo, int syscall_num __unused) } } - /* - * The pipe syscall returns its fds in two registers and has assembly glue - * to provide the libc API, so it cannot be handled like regular syscalls. - * The nargs check is so we don't have to do yet another strcmp on every - * syscall. - */ - if (!errorp && fsc.nargs == 0 && fsc.name && strcmp(fsc.name, "pipe") == 0) { - fsc.nargs = 1; - fsc.s_args = malloc((1+fsc.nargs) * sizeof(char*)); - asprintf(&fsc.s_args[0], "[%d,%d]", (int)retval, regs.r_edx); - retval = 0; - } - if (fsc.name != NULL && (!strcmp(fsc.name, "execve") || !strcmp(fsc.name, "exit"))) { trussinfo->curthread->in_syscall = 1; diff --git a/usr.bin/truss/main.c b/usr.bin/truss/main.c index d0dd073..586fcd6 100644 --- a/usr.bin/truss/main.c +++ b/usr.bin/truss/main.c @@ -239,6 +239,12 @@ main(int ac, char **av) if ((trussinfo->outfile = fopen(fname, "w")) == NULL) errx(1, "cannot open %s", fname); } + /* + * Set FD_CLOEXEC, so that the output file is not shared with + * the traced process. + */ + if (fcntl(fileno(trussinfo->outfile), F_SETFD, FD_CLOEXEC) == -1) + warn("fcntl()"); /* * If truss starts the process itself, it will ignore some signals -- diff --git a/usr.bin/truss/mips-fbsd.c b/usr.bin/truss/mips-fbsd.c index 0ef4ac9..cf5e2e1 100644 --- a/usr.bin/truss/mips-fbsd.c +++ b/usr.bin/truss/mips-fbsd.c @@ -155,8 +155,8 @@ mips_syscall_entry(struct trussinfo *trussinfo, int nargs) { if (nargs == 0) return; -#if 0 // XXX fsc.args = malloc((1+nargs) * sizeof(unsigned long)); +#if 0 // XXX iorequest.piod_op = PIOD_READ_D; iorequest.piod_offs = (void *)parm_offset; iorequest.piod_addr = fsc.args; diff --git a/usr.bin/truss/syscalls.c b/usr.bin/truss/syscalls.c index d927815..f927e1e 100644 --- a/usr.bin/truss/syscalls.c +++ b/usr.bin/truss/syscalls.c @@ -92,6 +92,18 @@ static const char rcsid[] = struct syscall syscalls[] = { { .name = "fcntl", .ret_type = 1, .nargs = 3, .args = { { Int, 0 } , { Fcntl, 1 }, { Fcntlflag | OUT, 2 } } }, + { .name = "fork", .ret_type = 1, .nargs = 0 }, + { .name = "getegid", .ret_type = 1, .nargs = 0 }, + { .name = "geteuid", .ret_type = 1, .nargs = 0 }, + { .name = "getgid", .ret_type = 1, .nargs = 0 }, + { .name = "getpid", .ret_type = 1, .nargs = 0 }, + { .name = "getpgid", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, + { .name = "getpgrp", .ret_type = 1, .nargs = 0 }, + { .name = "getppid", .ret_type = 1, .nargs = 0 }, + { .name = "getsid", .ret_type = 1, .nargs = 1, + .args = { { Int, 0 } } }, + { .name = "getuid", .ret_type = 1, .nargs = 0 }, { .name = "readlink", .ret_type = 1, .nargs = 3, .args = { { Name, 0 } , { Readlinkres | OUT, 1 }, { Int, 2 } } }, { .name = "lseek", .ret_type = 2, .nargs = 3, @@ -230,6 +242,8 @@ struct syscall syscalls[] = { .args = { { Name | IN, 0 }, { Hex, 1 } } }, { .name = "pathconf", .ret_type = 1, .nargs = 2, .args = { { Name | IN, 0 }, { Pathconf, 1 } } }, + { .name = "pipe", .ret_type = 1, .nargs = 1, + .args = { { Ptr, 0 } } }, { .name = "truncate", .ret_type = 1, .nargs = 3, .args = { { Name | IN, 0 }, { Int | IN, 1 }, { Quad | IN, 2 } } }, { .name = "ftruncate", .ret_type = 1, .nargs = 3, @@ -244,6 +258,8 @@ struct syscall syscalls[] = { .args = { { Name , 0 } , { Name, 1 } } }, { .name = "symlink", .ret_type = 1, .nargs = 2, .args = { { Name , 0 } , { Name, 1 } } }, + { .name = "posix_openpt", .ret_type = 1, .nargs = 1, + .args = { { Open, 0 } } }, { .name = 0 }, }; @@ -259,7 +275,7 @@ struct xlat { static struct xlat kevent_filters[] = { X(EVFILT_READ) X(EVFILT_WRITE) X(EVFILT_AIO) X(EVFILT_VNODE) X(EVFILT_PROC) X(EVFILT_SIGNAL) X(EVFILT_TIMER) - X(EVFILT_NETDEV) X(EVFILT_FS) X(EVFILT_READ) XEND + X(EVFILT_FS) X(EVFILT_READ) XEND }; static struct xlat kevent_flags[] = { @@ -1125,6 +1141,12 @@ print_syscall_ret(struct trussinfo *trussinfo, const char *name, int nargs, if (errorp) { fprintf(trussinfo->outfile, " ERR#%ld '%s'\n", retval, strerror(retval)); } else { + /* + * Because pipe(2) has a special assembly glue to provide the + * libc API, we have to adjust retval. + */ + if (name != NULL && !strcmp(name, "pipe")) + retval = 0; fprintf(trussinfo->outfile, " = %ld (0x%lx)\n", retval, retval); } } @@ -1141,15 +1163,15 @@ print_summary(struct trussinfo *trussinfo) ncall = nerror = 0; for (sc = syscalls; sc->name != NULL; sc++) if (sc->ncalls) { - fprintf(trussinfo->outfile, "%-20s%5d.%09ld%8d%8d\n", - sc->name, sc->time.tv_sec, sc->time.tv_nsec, - sc->ncalls, sc->nerror); + fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", + sc->name, (intmax_t)sc->time.tv_sec, + sc->time.tv_nsec, sc->ncalls, sc->nerror); timespecadd(&total, &sc->time, &total); ncall += sc->ncalls; nerror += sc->nerror; } fprintf(trussinfo->outfile, "%20s%15s%8s%8s\n", "", "-------------", "-------", "-------"); - fprintf(trussinfo->outfile, "%-20s%5d.%09ld%8d%8d\n", - "", total.tv_sec, total.tv_nsec, ncall, nerror); + fprintf(trussinfo->outfile, "%-20s%5jd.%09ld%8d%8d\n", + "", (intmax_t)total.tv_sec, total.tv_nsec, ncall, nerror); } diff --git a/usr.bin/tset/extern.h b/usr.bin/tset/extern.h index f02d0c5..8ec3f20 100644 --- a/usr.bin/tset/extern.h +++ b/usr.bin/tset/extern.h @@ -34,8 +34,6 @@ * $FreeBSD$ */ -#include <termcap.h> - extern struct termios mode, oldmode; extern int Columns, isreset, Lines; extern int erasech, intrchar, killch; diff --git a/usr.bin/tset/map.c b/usr.bin/tset/map.c index d3db6b6..80a894f 100644 --- a/usr.bin/tset/map.c +++ b/usr.bin/tset/map.c @@ -75,9 +75,7 @@ MAP *cur, *maplist; * The baud rate tests are: >, <, @, =, ! */ void -add_mapping(port, arg) - const char *port; - char *arg; +add_mapping(const char *port, char *arg) { MAP *mapp; char *copy, *p, *termp; @@ -190,8 +188,7 @@ badmopt: errx(1, "illegal -m option format: %s", copy); * 'type'. */ const char * -mapped(type) - const char *type; +mapped(const char *type) { MAP *mapp; int match; @@ -240,8 +237,7 @@ SPEEDS speeds[] = { }; speed_t -tset_baudrate(rate) - char *rate; +tset_baudrate(char *rate) { SPEEDS *sp; speed_t speed; diff --git a/usr.bin/tset/misc.c b/usr.bin/tset/misc.c index 52358e7..16e0e1e 100644 --- a/usr.bin/tset/misc.c +++ b/usr.bin/tset/misc.c @@ -42,15 +42,12 @@ static const char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/9/93"; #include <err.h> #include <fcntl.h> #include <stdio.h> -#include <stdlib.h> -#include <string.h> #include <unistd.h> #include "extern.h" void -cat(file) - char *file; +cat(char *file) { register int fd, nr, nw; char buf[1024]; @@ -67,8 +64,7 @@ cat(file) } int -outc(c) - int c; +outc(int c) { return putc(c, stderr); } diff --git a/usr.bin/tset/set.c b/usr.bin/tset/set.c index 85965b3..39a0d96 100644 --- a/usr.bin/tset/set.c +++ b/usr.bin/tset/set.c @@ -40,6 +40,7 @@ static const char sccsid[] = "@(#)set.c 8.2 (Berkeley) 2/28/94"; #endif #include <stdio.h> +#include <termcap.h> #include <termios.h> #include <unistd.h> @@ -54,7 +55,7 @@ int set_tabs(void); * a child program dies in raw mode. */ void -reset_mode() +reset_mode(void) { tcgetattr(STDERR_FILENO, &mode); @@ -155,7 +156,7 @@ reset_mode() * entry and command line and update their values in 'mode'. */ void -set_control_chars() +set_control_chars(void) { char *bp, *p, bs_char, buf[1024]; @@ -192,8 +193,7 @@ set_control_chars() * uppercase to internal lowercase. */ void -set_conversions(usingupper) - int usingupper; +set_conversions(int usingupper) { if (tgetflag("UC") || usingupper) { #ifdef IUCLC @@ -238,7 +238,7 @@ set_conversions(usingupper) /* Output startup string. */ void -set_init() +set_init(void) { char *bp, buf[1024]; int settle; @@ -282,7 +282,7 @@ set_init() * Return nonzero if we set any tab stops, zero if not. */ int -set_tabs() +set_tabs(void) { int c; char *capsp, *clear_tabs; diff --git a/usr.bin/tset/term.c b/usr.bin/tset/term.c index 54a349e..16acb23 100644 --- a/usr.bin/tset/term.c +++ b/usr.bin/tset/term.c @@ -45,6 +45,7 @@ static const char sccsid[] = "@(#)term.c 8.1 (Berkeley) 6/9/93"; #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <termcap.h> #include <unistd.h> #include <ttyent.h> #include "extern.h" @@ -59,8 +60,7 @@ char *ttys(char *); * its termcap entry. */ const char * -get_termcap_entry(userarg, tcapbufp) - char *userarg, **tcapbufp; +get_termcap_entry(char *userarg, char **tcapbufp) { struct ttyent *t; int rval; @@ -125,8 +125,7 @@ found: if ((p = getenv("TERMCAP")) != NULL && *p != '/') /* Prompt the user for a terminal type. */ const char * -askuser(dflt) - const char *dflt; +askuser(const char *dflt) { static char answer[256]; char *p; diff --git a/usr.bin/tset/tset.c b/usr.bin/tset/tset.c index dc4c333..ddcc2dac 100644 --- a/usr.bin/tset/tset.c +++ b/usr.bin/tset/tset.c @@ -53,6 +53,7 @@ static const char sccsid[] = "@(#)tset.c 8.1 (Berkeley) 6/9/93"; #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <termcap.h> #include <termios.h> #include <unistd.h> diff --git a/usr.bin/tset/wrterm.c b/usr.bin/tset/wrterm.c index 9d14918..29b1921 100644 --- a/usr.bin/tset/wrterm.c +++ b/usr.bin/tset/wrterm.c @@ -53,8 +53,7 @@ static const char sccsid[] = "@(#)wrterm.c 8.1 (Berkeley) 6/9/93"; * shell problems and omitting empty fields. */ void -wrtermcap(bp) - char *bp; +wrtermcap(char *bp) { register int ch; register char *p; diff --git a/usr.bin/uname/uname.1 b/usr.bin/uname/uname.1 index 2a28464..f78e437c 100644 --- a/usr.bin/uname/uname.1 +++ b/usr.bin/uname/uname.1 @@ -32,7 +32,7 @@ .\" @(#)uname.1 8.3 (Berkeley) 4/8/94 .\" $FreeBSD$ .\" -.Dd April 2, 2003 +.Dd January 26, 2010 .Dt UNAME 1 .Os .Sh NAME @@ -40,7 +40,7 @@ .Nd display information about the system .Sh SYNOPSIS .Nm -.Op Fl aimnprsv +.Op Fl aimnoprsv .Sh DESCRIPTION The .Nm @@ -63,6 +63,10 @@ Write the kernel ident to standard output. Write the type of the current hardware platform to standard output. .It Fl n Write the name of the system to standard output. +.It Fl o +This is a synonym for the +.Fl s +option, for compatibility with other systems. .It Fl p Write the type of the machine processor architecture to standard output. .It Fl r diff --git a/usr.bin/uname/uname.c b/usr.bin/uname/uname.c index 81aef45..0e120b9 100644 --- a/usr.bin/uname/uname.c +++ b/usr.bin/uname/uname.c @@ -88,7 +88,7 @@ main(int argc, char *argv[]) setup_get(); flags = 0; - while ((ch = getopt(argc, argv, "aimnprsv")) != -1) + while ((ch = getopt(argc, argv, "aimnoprsv")) != -1) switch(ch) { case 'a': flags |= (MFLAG | NFLAG | RFLAG | SFLAG | VFLAG); @@ -109,6 +109,7 @@ main(int argc, char *argv[]) flags |= RFLAG; break; case 's': + case 'o': flags |= SFLAG; break; case 'v': @@ -244,6 +245,6 @@ NATIVE_SYSCTLNAME_GET(ident, "kern.ident") { void usage(void) { - fprintf(stderr, "usage: uname [-aimnprsv]\n"); + fprintf(stderr, "usage: uname [-aimnoprsv]\n"); exit(1); } diff --git a/usr.bin/unifdef/Makefile b/usr.bin/unifdef/Makefile index b31709a..dcd358f 100644 --- a/usr.bin/unifdef/Makefile +++ b/usr.bin/unifdef/Makefile @@ -4,6 +4,5 @@ PROG= unifdef SCRIPTS=unifdefall.sh MLINKS= unifdef.1 unifdefall.1 -WARNS?= 5 .include <bsd.prog.mk> diff --git a/usr.bin/unifdef/unifdef.1 b/usr.bin/unifdef/unifdef.1 index 12a5438..da7cde8 100644 --- a/usr.bin/unifdef/unifdef.1 +++ b/usr.bin/unifdef/unifdef.1 @@ -1,6 +1,6 @@ .\" Copyright (c) 1985, 1991, 1993 .\" The Regents of the University of California. All rights reserved. -.\" Copyright (c) 2002 - 2005 Tony Finch <dot@dotat.at>. All rights reserved. +.\" Copyright (c) 2002 - 2010 Tony Finch <dot@dotat.at>. All rights reserved. .\" .\" This code is derived from software contributed to Berkeley by .\" Dave Yost. It was rewritten to support ANSI C by Tony Finch. @@ -30,10 +30,10 @@ .\" SUCH DAMAGE. .\" .\" @(#)unifdef.1 8.2 (Berkeley) 4/1/94 -.\" $dotat: things/unifdef.1,v 1.51 2005/03/08 12:39:01 fanf2 Exp $ +.\" $dotat: unifdef/unifdef.1,v 1.63 2010/02/19 16:41:15 fanf2 Exp $ .\" $FreeBSD$ .\" -.Dd September 24, 2002 +.Dd January 19, 2010 .Dt UNIFDEF 1 .Os .Sh NAME @@ -41,14 +41,15 @@ .Nd remove preprocessor conditionals from code .Sh SYNOPSIS .Nm -.Op Fl cdeklnst +.Op Fl BbcdeKknst .Op Fl I Ns Ar path .Op Fl D Ns Ar sym Ns Op = Ns Ar val .Op Fl U Ns Ar sym .Op Fl iD Ns Ar sym Ns Op = Ns Ar val .Op Fl iU Ns Ar sym .Ar ... -.Op Ar file +.Op Fl o Ar outfile +.Op Ar infile .Nm unifdefall .Op Fl I Ns Ar path .Ar ... @@ -70,46 +71,85 @@ utility acts on .Ic #if , #ifdef , #ifndef , #elif , #else , and .Ic #endif -lines, -and it understands only the commonly-used subset +lines. +A directive is only processed +if the symbols specified on the command line are sufficient to allow +.Nm +to get a definite value for its control expression. +If the result is false, +the directive and the following lines under its control are removed. +If the result is true, +only the directive is removed. +An +.Ic #ifdef +or +.Ic #ifndef +directive is passed through unchanged +if its controlling symbol is not specified on the command line. +Any +.Ic #if +or +.Ic #elif +control expression that has an unknown value or that +.Nm +cannot parse is passed through unchanged. +By default, +.Nm +ignores +.Ic #if +and +.Ic #elif +lines with constant expressions; +it can be told to process them by specifying the +.Fl k +flag on the command line. +.Pp +It understands a commonly-used subset of the expression syntax for .Ic #if and .Ic #elif -lines. -It handles +lines: +integer constants, integer values of symbols defined on the command line, the .Fn defined -operator applied to symbols defined or undefined on the command line, +operator, the operators .Ic \&! , < , > , <= , >= , == , != , && , || , and parenthesized expressions. -Anything that it does not understand is passed through unharmed. -It only processes -.Ic #ifdef -and -.Ic #ifndef -directives if the symbol is specified on the command line, -otherwise they are also passed through unchanged. -By default, it ignores -.Ic #if -and -.Ic #elif -lines with constant expressions, -or they may be processed by specifying the -.Fl k -flag on the command line. +A kind of +.Dq "short circuit" +evaluation is used for the +.Ic && +operator: +if either operand is definitely false then the result is false, +even if the value of the other operand is unknown. +Similarly, +if either operand of +.Ic || +is definitely true then the result is true. +.Pp +In most cases, the +.Nm +utility does not distinguish between object-like macros +(without arguments) and function-like arguments (with arguments). +If a macro is not explicitly defined, or is defined with the +.Fl D +flag on the command-line, its arguments are ignored. +If a macro is explicitly undefined on the command line with the +.Fl U +flag, it may not have any arguments since this leads to a syntax error. .Pp The .Nm -utility also understands just enough about C +utility understands just enough about C to know when one of the directives is inactive because it is inside a comment, or affected by a backslash-continued line. It spots unusually-formatted preprocessor directives -and knows when the layout is too odd to handle. +and knows when the layout is too odd for it to handle. .Pp A script called .Nm unifdefall @@ -125,24 +165,38 @@ and their definitions (or lack thereof), then invokes .Nm with appropriate arguments to process the file. -.Pp -Available options: +.Sh OPTIONS .Pp .Bl -tag -width indent -compact -.It Fl D Ns Ar sym Ns Op = Ns Ar val -Specify that a symbol is defined, -and optionally specify what value to give it -for the purpose of handling +.It Fl D Ns Ar sym Ns = Ns Ar val +Specify that a symbol is defined to a given value +which is used when evaluating .Ic #if and .Ic #elif -directives. +control expressions. +.Pp +.It Fl D Ns Ar sym +Specify that a symbol is defined to the value 1. .Pp .It Fl U Ns Ar sym Specify that a symbol is undefined. If the same symbol appears in more than one argument, the last occurrence dominates. .Pp +.It Fl B +Compress blank lines around a deleted section. +Mutually exclusive with the +.Fl b +option. +.Pp +.It Fl b +Replace removed lines with blank lines +instead of deleting them. +Mutually exclusive with the +.Fl B +option. +.Pp .It Fl c If the .Fl c @@ -174,6 +228,16 @@ option changes the behaviour so that, where possible, such lines are left unprocessed instead of reporting an error. .Pp +.It Fl K +Always treat the result of +.Ic && +and +.Ic || +operators as unknown if either operand is unknown, +instead of short-circuiting when unknown operands can't affect the result. +This option is for compatibility with older versions of +.Nm . +.Pp .It Fl k Process .Ic #if @@ -186,10 +250,6 @@ because they typically start and are used as a kind of comment to sketch out future or past development. It would be rude to strip them out, just as it would be for normal comments. .Pp -.It Fl l -Replace removed lines with blank lines -instead of deleting them. -.Pp .It Fl n Add .Li #line @@ -197,6 +257,18 @@ directives to the output following any deleted lines, so that errors produced when compiling the output file correspond to line numbers in the input file. .Pp +.It Fl o Ar outfile +Write output to the file +.Ar outfile +instead of the standard output. +If +.Ar outfile +is the same as the input file, +the output is written to a temporary file +which is renamed into place when +.Nm +completes successfully. +.Pp .It Fl s Instead of processing the input file as usual, this option causes @@ -235,7 +307,7 @@ comments and line continuations inside those .Ic #ifdef Ns s . -One specifies ignored symbols with +You can specify ignored symbols with .Fl iD Ns Ar sym Ns Oo = Ns Ar val Oc and .Fl iU Ns Ar sym @@ -313,7 +385,7 @@ command appeared in support was added in .Fx 4.7 . .Sh AUTHORS -This implementation was originally written by +The original implementation was written by .An Dave Yost Aq Dave@Yost.com . .An Tony Finch Aq dot@dotat.at rewrote it to support diff --git a/usr.bin/unifdef/unifdef.c b/usr.bin/unifdef/unifdef.c index a8427d1..cdcc403 100644 --- a/usr.bin/unifdef/unifdef.c +++ b/usr.bin/unifdef/unifdef.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2002 - 2008 Tony Finch <dot@dotat.at> + * Copyright (c) 2002 - 2010 Tony Finch <dot@dotat.at> * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -24,33 +24,13 @@ */ /* - * This code is derived from software contributed to Berkeley by Dave Yost. + * unifdef - remove ifdef'ed lines + * + * This code was derived from software contributed to Berkeley by Dave Yost. * It was rewritten to support ANSI C by Tony Finch. The original version * of unifdef carried the 4-clause BSD copyright licence. None of its code * remains in this version (though some of the names remain) so it now * carries a more liberal licence. - */ - -#include <sys/cdefs.h> - -#ifndef lint -#if 0 -static const char copyright[] = -"@(#) Copyright (c) 1985, 1993\n\ - The Regents of the University of California. All rights reserved.\n"; -#endif -#ifdef __IDSTRING -__IDSTRING(Berkeley, "@(#)unifdef.c 8.1 (Berkeley) 6/6/93"); -__IDSTRING(NetBSD, "$NetBSD: unifdef.c,v 1.8 2000/07/03 02:51:36 matt Exp $"); -__IDSTRING(dotat, "$dotat: things/unifdef.c,v 1.178 2008/03/02 22:23:32 fanf2 Exp $"); -#endif -#endif /* not lint */ -#ifdef __FBSDID -__FBSDID("$FreeBSD$"); -#endif - -/* - * unifdef - remove ifdef'ed lines * * Wishlist: * provide an option which will append the name of the @@ -59,12 +39,16 @@ __FBSDID("$FreeBSD$"); * #else's and #endif's to see that they match their * corresponding #ifdef or #ifndef * - * The first two items above require better buffer handling, which would - * also make it possible to handle all "dodgy" directives correctly. + * These require better buffer handling, which would also make + * it possible to handle all "dodgy" directives correctly. */ +#include <sys/types.h> +#include <sys/stat.h> + #include <ctype.h> #include <err.h> +#include <errno.h> #include <stdarg.h> #include <stdbool.h> #include <stdio.h> @@ -72,6 +56,13 @@ __FBSDID("$FreeBSD$"); #include <string.h> #include <unistd.h> +#ifdef __IDSTRING +__IDSTRING(dotat, "$dotat: unifdef/unifdef.c,v 1.198 2010/02/19 16:37:05 fanf2 Exp $"); +#endif +#ifdef __FBSDID +__FBSDID("$FreeBSD$"); +#endif + /* types of input lines: */ typedef enum { LT_TRUEI, /* a true #if with ignore flag */ @@ -88,6 +79,7 @@ typedef enum { LT_DODGY_LAST = LT_DODGY + LT_ENDIF, LT_PLAIN, /* ordinary line */ LT_EOF, /* end of file */ + LT_ERROR, /* unevaluable #if */ LT_COUNT } Linetype; @@ -98,7 +90,7 @@ static char const * const linetype_name[] = { "DODGY IF", "DODGY TRUE", "DODGY FALSE", "DODGY ELIF", "DODGY ELTRUE", "DODGY ELFALSE", "DODGY ELSE", "DODGY ENDIF", - "PLAIN", "EOF" + "PLAIN", "EOF", "ERROR" }; /* state of #if processing */ @@ -163,14 +155,21 @@ static char const * const linestate_name[] = { #define EDITSLOP 10 /* + * For temporary filenames + */ +#define TEMPLATE "unifdef.XXXXXX" + +/* * Globals. */ +static bool compblank; /* -B: compress blank lines */ +static bool lnblank; /* -b: blank deleted lines */ static bool complement; /* -c: do the complement */ static bool debugging; /* -d: debugging reports */ static bool iocccok; /* -e: fewer IOCCC errors */ +static bool strictlogic; /* -K: keep ambiguous #ifs */ static bool killconsts; /* -k: eval constant #ifs */ -static bool lnblank; /* -l: blank deleted lines */ static bool lnnum; /* -n: add #line directives */ static bool symlist; /* -s: output symbol list */ static bool text; /* -t: this is a text file */ @@ -183,10 +182,18 @@ static int nsyms; /* number of symbols */ static FILE *input; /* input file pointer */ static const char *filename; /* input file name */ static int linenum; /* current line number */ +static FILE *output; /* output file pointer */ +static const char *ofilename; /* output file name */ +static bool overwriting; /* output overwrites input */ +static char tempname[FILENAME_MAX]; /* used when overwriting */ static char tline[MAXLINE+EDITSLOP];/* input buffer plus space */ static char *keyword; /* used for editing #elif's */ +static const char *newline; /* input file format */ +static const char newline_unix[] = "\n"; +static const char newline_crlf[] = "\r\n"; + static Comment_state incomment; /* comment parser state */ static Line_state linestate; /* #if line parser state */ static Ifstate ifstate[MAXDEPTH]; /* #if processor state */ @@ -194,23 +201,27 @@ static bool ignoring[MAXDEPTH]; /* ignore comments state */ static int stifline[MAXDEPTH]; /* start of current #if */ static int depth; /* current #if nesting */ static int delcount; /* count of deleted lines */ -static bool keepthis; /* don't delete constant #if */ +static unsigned blankcount; /* count of blank lines */ +static unsigned blankmax; /* maximum recent blankcount */ +static bool constexpr; /* constant #if expression */ static int exitstat; /* program exit status */ static void addsym(bool, bool, char *); +static void closeout(void); static void debug(const char *, ...); static void done(void); static void error(const char *); static int findsym(const char *); static void flushline(bool); -static Linetype getline(void); +static Linetype parseline(void); static Linetype ifeval(const char **); static void ignoreoff(void); static void ignoreon(void); static void keywordedit(const char *); static void nest(void); static void process(void); +static const char *skipargs(const char *); static const char *skipcomment(const char *); static const char *skipsym(const char *); static void state(Ifstate); @@ -218,7 +229,7 @@ static int strlcmp(const char *, const char *, size_t); static void unnest(void); static void usage(void); -#define endsym(c) (!isalpha((unsigned char)c) && !isdigit((unsigned char)c) && c != '_') +#define endsym(c) (!isalnum((unsigned char)c) && c != '_') /* * The main program. @@ -228,7 +239,7 @@ main(int argc, char *argv[]) { int opt; - while ((opt = getopt(argc, argv, "i:D:U:I:cdeklnst")) != -1) + while ((opt = getopt(argc, argv, "i:D:U:I:o:BbcdeKklnst")) != -1) switch (opt) { case 'i': /* treat stuff controlled by these symbols as text */ /* @@ -253,6 +264,13 @@ main(int argc, char *argv[]) case 'I': /* no-op for compatibility with cpp */ break; + case 'B': /* compress blank lines around removed section */ + compblank = true; + break; + case 'b': /* blank deleted lines instead of omitting them */ + case 'l': /* backwards compatibility */ + lnblank = true; + break; case 'c': /* treat -D as -U and vice versa */ complement = true; break; @@ -262,15 +280,18 @@ main(int argc, char *argv[]) case 'e': /* fewer errors from dodgy lines */ iocccok = true; break; + case 'K': /* keep ambiguous #ifs */ + strictlogic = true; + break; case 'k': /* process constant #ifs */ killconsts = true; break; - case 'l': /* blank deleted lines instead of omitting them */ - lnblank = true; - break; case 'n': /* add #line directive after deleted lines */ lnnum = true; break; + case 'o': /* output to a file */ + ofilename = optarg; + break; case 's': /* only output list of symbols that control #ifs */ symlist = true; break; @@ -282,6 +303,8 @@ main(int argc, char *argv[]) } argc -= optind; argv += optind; + if (compblank && lnblank) + errx(2, "-B and -b are mutually exclusive"); if (argc > 1) { errx(2, "can only do one file"); } else if (argc == 1 && strcmp(*argv, "-") != 0) { @@ -293,6 +316,45 @@ main(int argc, char *argv[]) filename = "[stdin]"; input = stdin; } + if (ofilename == NULL) { + ofilename = "[stdout]"; + output = stdout; + } else { + struct stat ist, ost; + memset(&ist, 0, sizeof(ist)); + memset(&ost, 0, sizeof(ost)); + + if (fstat(fileno(input), &ist) != 0) + err(2, "can't fstat %s", filename); + if (stat(ofilename, &ost) != 0 && errno != ENOENT) + warn("can't stat %s", ofilename); + + overwriting = (ist.st_dev == ost.st_dev + && ist.st_ino == ost.st_ino); + if (overwriting) { + const char *dirsep; + int ofd; + + dirsep = strrchr(ofilename, '/'); + if (dirsep != NULL) + snprintf(tempname, sizeof(tempname), + "%.*s/" TEMPLATE, + (int)(dirsep - ofilename), ofilename); + else + snprintf(tempname, sizeof(tempname), + TEMPLATE); + ofd = mkstemp(tempname); + if (ofd != -1) + output = fdopen(ofd, "w+"); + if (output == NULL) + err(2, "can't create temporary file"); + fchmod(ofd, ist.st_mode & ACCESSPERMS); + } else { + output = fopen(ofilename, "w"); + if (output == NULL) + err(2, "can't open %s", ofilename); + } + } process(); abort(); /* bug */ } @@ -300,7 +362,7 @@ main(int argc, char *argv[]) static void usage(void) { - fprintf(stderr, "usage: unifdef [-cdeklnst] [-Ipath]" + fprintf(stderr, "usage: unifdef [-BbcdeKknst] [-Ipath]" " [-Dsym[=val]] [-Usym] [-iDsym[=val]] [-iUsym] ... [file]\n"); exit(2); } @@ -373,67 +435,60 @@ static void Itrue (void) { Ftrue(); ignoreon(); } static void Ifalse(void) { Ffalse(); ignoreon(); } /* edit this line */ static void Mpass (void) { strncpy(keyword, "if ", 4); Pelif(); } -static void Mtrue (void) { keywordedit("else\n"); state(IS_TRUE_MIDDLE); } -static void Melif (void) { keywordedit("endif\n"); state(IS_FALSE_TRAILER); } -static void Melse (void) { keywordedit("endif\n"); state(IS_FALSE_ELSE); } +static void Mtrue (void) { keywordedit("else"); state(IS_TRUE_MIDDLE); } +static void Melif (void) { keywordedit("endif"); state(IS_FALSE_TRAILER); } +static void Melse (void) { keywordedit("endif"); state(IS_FALSE_ELSE); } static state_fn * const trans_table[IS_COUNT][LT_COUNT] = { /* IS_OUTSIDE */ { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Eendif, Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eendif, - print, done }, + print, done, abort }, /* IS_FALSE_PREFIX */ { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Strue, Sfalse,Selse, Dendif, Idrop, Idrop, Fdrop, Fdrop, Fdrop, Mpass, Eioccc,Eioccc,Eioccc,Eioccc, - drop, Eeof }, + drop, Eeof, abort }, /* IS_TRUE_PREFIX */ { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Dfalse,Dfalse,Dfalse,Delse, Dendif, Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc, - print, Eeof }, + print, Eeof, abort }, /* IS_PASS_MIDDLE */ { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Pelif, Mtrue, Delif, Pelse, Pendif, Oiffy, Oiffy, Fpass, Oif, Oif, Pelif, Oelif, Oelif, Pelse, Pendif, - print, Eeof }, + print, Eeof, abort }, /* IS_FALSE_MIDDLE */ { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Pelif, Mtrue, Delif, Pelse, Pendif, Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eioccc,Eioccc,Eioccc,Eioccc,Eioccc, - drop, Eeof }, + drop, Eeof, abort }, /* IS_TRUE_MIDDLE */ { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Melif, Melif, Melif, Melse, Pendif, Oiffy, Oiffy, Fpass, Oif, Oif, Eioccc,Eioccc,Eioccc,Eioccc,Pendif, - print, Eeof }, + print, Eeof, abort }, /* IS_PASS_ELSE */ { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Pendif, Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Pendif, - print, Eeof }, + print, Eeof, abort }, /* IS_FALSE_ELSE */ { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Dendif, Idrop, Idrop, Fdrop, Fdrop, Fdrop, Eelif, Eelif, Eelif, Eelse, Eioccc, - drop, Eeof }, + drop, Eeof, abort }, /* IS_TRUE_ELSE */ { Itrue, Ifalse,Fpass, Ftrue, Ffalse,Eelif, Eelif, Eelif, Eelse, Dendif, Oiffy, Oiffy, Fpass, Oif, Oif, Eelif, Eelif, Eelif, Eelse, Eioccc, - print, Eeof }, + print, Eeof, abort }, /* IS_FALSE_TRAILER */ { Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Dendif, Idrop, Idrop, Fdrop, Fdrop, Fdrop, Dfalse,Dfalse,Dfalse,Delse, Eioccc, - drop, Eeof } + drop, Eeof, abort } /*TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF TRUEI FALSEI IF TRUE FALSE ELIF ELTRUE ELFALSE ELSE ENDIF (DODGY) - PLAIN EOF */ + PLAIN EOF ERROR */ }; /* * State machine utility functions */ static void -done(void) -{ - if (incomment) - error("EOF in comment"); - exit(exitstat); -} -static void ignoreoff(void) { if (depth == 0) @@ -448,15 +503,18 @@ ignoreon(void) static void keywordedit(const char *replacement) { - strlcpy(keyword, replacement, tline + sizeof(tline) - keyword); + snprintf(keyword, tline + sizeof(tline) - keyword, + "%s%s", replacement, newline); print(); } static void nest(void) { - depth += 1; - if (depth >= MAXDEPTH) + if (depth > MAXDEPTH-1) + abort(); /* bug */ + if (depth == MAXDEPTH-1) error("Too many levels of nesting"); + depth += 1; stifline[depth] = linenum; } static void @@ -481,15 +539,23 @@ flushline(bool keep) if (symlist) return; if (keep ^ complement) { - if (lnnum && delcount > 0) - printf("#line %d\n", linenum); - fputs(tline, stdout); - delcount = 0; + bool blankline = tline[strspn(tline, " \t\r\n")] == '\0'; + if (blankline && compblank && blankcount != blankmax) { + delcount += 1; + blankcount += 1; + } else { + if (lnnum && delcount > 0) + printf("#line %d%s", linenum, newline); + fputs(tline, output); + delcount = 0; + blankmax = blankcount = blankline ? blankcount + 1 : 0; + } } else { if (lnblank) - putc('\n', stdout); + fputs(newline, output); exitstat = 1; delcount += 1; + blankcount = 0; } } @@ -501,9 +567,12 @@ process(void) { Linetype lineval; + /* When compressing blank lines, act as if the file + is preceded by a large number of blank lines. */ + blankmax = blankcount = 1000; for (;;) { linenum++; - lineval = getline(); + lineval = parseline(); trans_table[ifstate[depth]][lineval](); debug("process %s -> %s depth %d", linetype_name[lineval], @@ -512,12 +581,46 @@ process(void) } /* + * Flush the output and handle errors. + */ +static void +closeout(void) +{ + if (fclose(output) == EOF) { + warn("couldn't write to %s", ofilename); + if (overwriting) { + unlink(tempname); + errx(2, "%s unchanged", filename); + } else { + exit(2); + } + } +} + +/* + * Clean up and exit. + */ +static void +done(void) +{ + if (incomment) + error("EOF in comment"); + closeout(); + if (overwriting && rename(tempname, filename) == -1) { + warn("couldn't rename temporary file"); + unlink(tempname); + errx(2, "%s unchanged", filename); + } + exit(exitstat); +} + +/* * Parse a line and determine its type. We keep the preprocessor line * parser state between calls in the global variable linestate, with * help from skipcomment(). */ static Linetype -getline(void) +parseline(void) { const char *cp; int cursym; @@ -527,6 +630,12 @@ getline(void) if (fgets(tline, MAXLINE, input) == NULL) return (LT_EOF); + if (newline == NULL) { + if (strrchr(tline, '\n') == strrchr(tline, '\r') + 1) + newline = newline_crlf; + else + newline = newline_unix; + } retval = LT_PLAIN; wascomment = incomment; cp = skipcomment(tline); @@ -542,7 +651,8 @@ getline(void) cp = skipsym(cp); kwlen = cp - keyword; /* no way can we deal with a continuation inside a keyword */ - if (strncmp(cp, "\\\n", 2) == 0) + if (strncmp(cp, "\\\r\n", 3) == 0 || + strncmp(cp, "\\\n", 2) == 0) Eioccc(); if (strlcmp("ifdef", keyword, kwlen) == 0 || strlcmp("ifndef", keyword, kwlen) == 0) { @@ -593,9 +703,8 @@ getline(void) size_t len = cp - tline; if (fgets(tline + len, MAXLINE - len, input) == NULL) { /* append the missing newline */ - tline[len+0] = '\n'; - tline[len+1] = '\0'; - cp++; + strcpy(tline + len, newline); + cp += strlen(newline); linestate = LS_START; } else { linestate = LS_DIRTY; @@ -613,17 +722,40 @@ getline(void) /* * These are the binary operators that are supported by the expression - * evaluator. Note that if support for division is added then we also - * need short-circuiting booleans because of divide-by-zero. + * evaluator. */ -static int op_lt(int a, int b) { return (a < b); } -static int op_gt(int a, int b) { return (a > b); } -static int op_le(int a, int b) { return (a <= b); } -static int op_ge(int a, int b) { return (a >= b); } -static int op_eq(int a, int b) { return (a == b); } -static int op_ne(int a, int b) { return (a != b); } -static int op_or(int a, int b) { return (a || b); } -static int op_and(int a, int b) { return (a && b); } +static Linetype op_strict(int *p, int v, Linetype at, Linetype bt) { + if(at == LT_IF || bt == LT_IF) return (LT_IF); + return (*p = v, v ? LT_TRUE : LT_FALSE); +} +static Linetype op_lt(int *p, Linetype at, int a, Linetype bt, int b) { + return op_strict(p, a < b, at, bt); +} +static Linetype op_gt(int *p, Linetype at, int a, Linetype bt, int b) { + return op_strict(p, a > b, at, bt); +} +static Linetype op_le(int *p, Linetype at, int a, Linetype bt, int b) { + return op_strict(p, a <= b, at, bt); +} +static Linetype op_ge(int *p, Linetype at, int a, Linetype bt, int b) { + return op_strict(p, a >= b, at, bt); +} +static Linetype op_eq(int *p, Linetype at, int a, Linetype bt, int b) { + return op_strict(p, a == b, at, bt); +} +static Linetype op_ne(int *p, Linetype at, int a, Linetype bt, int b) { + return op_strict(p, a != b, at, bt); +} +static Linetype op_or(int *p, Linetype at, int a, Linetype bt, int b) { + if (!strictlogic && (at == LT_TRUE || bt == LT_TRUE)) + return (*p = 1, LT_TRUE); + return op_strict(p, a || b, at, bt); +} +static Linetype op_and(int *p, Linetype at, int a, Linetype bt, int b) { + if (!strictlogic && (at == LT_FALSE || bt == LT_FALSE)) + return (*p = 0, LT_FALSE); + return op_strict(p, a && b, at, bt); +} /* * An evaluation function takes three arguments, as follows: (1) a pointer to @@ -632,8 +764,8 @@ static int op_and(int a, int b) { return (a && b); } * value of the expression; and (3) a pointer to a char* that points to the * expression to be evaluated and that is updated to the end of the expression * when evaluation is complete. The function returns LT_FALSE if the value of - * the expression is zero, LT_TRUE if it is non-zero, or LT_IF if the - * expression could not be evaluated. + * the expression is zero, LT_TRUE if it is non-zero, LT_IF if the expression + * depends on an unknown symbol, or LT_ERROR if there is a parse failure. */ struct ops; @@ -652,7 +784,7 @@ static const struct ops { eval_fn *inner; struct op { const char *str; - int (*fn)(int, int); + Linetype (*fn)(int *, Linetype, int, Linetype, int); } op[5]; } eval_ops[] = { { eval_table, { { "||", op_or } } }, @@ -667,8 +799,8 @@ static const struct ops { /* * Function for evaluating the innermost parts of expressions, - * viz. !expr (expr) defined(symbol) symbol number - * We reset the keepthis flag when we find a non-constant subexpression. + * viz. !expr (expr) number defined(symbol) symbol + * We reset the constexpr flag in the last two cases. */ static Linetype eval_unary(const struct ops *ops, int *valp, const char **cpp) @@ -677,25 +809,34 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) char *ep; int sym; bool defparen; + Linetype lt; cp = skipcomment(*cpp); if (*cp == '!') { debug("eval%d !", ops - eval_ops); cp++; - if (eval_unary(ops, valp, &cp) == LT_IF) - return (LT_IF); - *valp = !*valp; + lt = eval_unary(ops, valp, &cp); + if (lt == LT_ERROR) + return (LT_ERROR); + if (lt != LT_IF) { + *valp = !*valp; + lt = *valp ? LT_TRUE : LT_FALSE; + } } else if (*cp == '(') { cp++; debug("eval%d (", ops - eval_ops); - if (eval_table(eval_ops, valp, &cp) == LT_IF) - return (LT_IF); + lt = eval_table(eval_ops, valp, &cp); + if (lt == LT_ERROR) + return (LT_ERROR); cp = skipcomment(cp); if (*cp++ != ')') - return (LT_IF); + return (LT_ERROR); } else if (isdigit((unsigned char)*cp)) { debug("eval%d number", ops - eval_ops); *valp = strtol(cp, &ep, 0); + if (ep == cp) + return (LT_ERROR); + lt = *valp ? LT_TRUE : LT_FALSE; cp = skipsym(cp); } else if (strncmp(cp, "defined", 7) == 0 && endsym(cp[7])) { cp = skipcomment(cp+7); @@ -707,36 +848,43 @@ eval_unary(const struct ops *ops, int *valp, const char **cpp) defparen = false; } sym = findsym(cp); - if (sym < 0) - return (LT_IF); - *valp = (value[sym] != NULL); + if (sym < 0) { + lt = LT_IF; + } else { + *valp = (value[sym] != NULL); + lt = *valp ? LT_TRUE : LT_FALSE; + } cp = skipsym(cp); cp = skipcomment(cp); if (defparen && *cp++ != ')') - return (LT_IF); - keepthis = false; + return (LT_ERROR); + constexpr = false; } else if (!endsym(*cp)) { debug("eval%d symbol", ops - eval_ops); sym = findsym(cp); - if (sym < 0) - return (LT_IF); - if (value[sym] == NULL) + cp = skipsym(cp); + if (sym < 0) { + lt = LT_IF; + cp = skipargs(cp); + } else if (value[sym] == NULL) { *valp = 0; - else { + lt = LT_FALSE; + } else { *valp = strtol(value[sym], &ep, 0); if (*ep != '\0' || ep == value[sym]) - return (LT_IF); + return (LT_ERROR); + lt = *valp ? LT_TRUE : LT_FALSE; + cp = skipargs(cp); } - cp = skipsym(cp); - keepthis = false; + constexpr = false; } else { debug("eval%d bad expr", ops - eval_ops); - return (LT_IF); + return (LT_ERROR); } *cpp = cp; debug("eval%d = %d", ops - eval_ops, *valp); - return (*valp ? LT_TRUE : LT_FALSE); + return (lt); } /* @@ -748,11 +896,13 @@ eval_table(const struct ops *ops, int *valp, const char **cpp) const struct op *op; const char *cp; int val; + Linetype lt, rt; debug("eval%d", ops - eval_ops); cp = *cpp; - if (ops->inner(ops+1, valp, &cp) == LT_IF) - return (LT_IF); + lt = ops->inner(ops+1, valp, &cp); + if (lt == LT_ERROR) + return (LT_ERROR); for (;;) { cp = skipcomment(cp); for (op = ops->op; op->str != NULL; op++) @@ -762,14 +912,16 @@ eval_table(const struct ops *ops, int *valp, const char **cpp) break; cp += strlen(op->str); debug("eval%d %s", ops - eval_ops, op->str); - if (ops->inner(ops+1, &val, &cp) == LT_IF) - return (LT_IF); - *valp = op->fn(*valp, val); + rt = ops->inner(ops+1, &val, &cp); + if (rt == LT_ERROR) + return (LT_ERROR); + lt = op->fn(valp, lt, *valp, rt, val); } *cpp = cp; debug("eval%d = %d", ops - eval_ops, *valp); - return (*valp ? LT_TRUE : LT_FALSE); + debug("eval%d lt = %s", ops - eval_ops, linetype_name[lt]); + return (lt); } /* @@ -784,10 +936,10 @@ ifeval(const char **cpp) int val = 0; debug("eval %s", *cpp); - keepthis = killconsts ? false : true; + constexpr = killconsts ? false : true; ret = eval_table(eval_ops, &val, cpp); debug("eval = %d", val); - return (keepthis ? LT_IF : ret); + return (constexpr ? LT_IF : ret == LT_ERROR ? LT_IF : ret); } /* @@ -808,11 +960,16 @@ skipcomment(const char *cp) } while (*cp != '\0') /* don't reset to LS_START after a line continuation */ - if (strncmp(cp, "\\\n", 2) == 0) + if (strncmp(cp, "\\\r\n", 3) == 0) + cp += 3; + else if (strncmp(cp, "\\\n", 2) == 0) cp += 2; else switch (incomment) { case NO_COMMENT: - if (strncmp(cp, "/\\\n", 3) == 0) { + if (strncmp(cp, "/\\\r\n", 4) == 0) { + incomment = STARTING_COMMENT; + cp += 4; + } else if (strncmp(cp, "/\\\n", 3) == 0) { incomment = STARTING_COMMENT; cp += 3; } else if (strncmp(cp, "/*", 2) == 0) { @@ -832,7 +989,7 @@ skipcomment(const char *cp) } else if (strncmp(cp, "\n", 1) == 0) { linestate = LS_START; cp += 1; - } else if (strchr(" \t", *cp) != NULL) { + } else if (strchr(" \r\t", *cp) != NULL) { cp += 1; } else return (cp); @@ -864,7 +1021,10 @@ skipcomment(const char *cp) cp += 1; continue; case C_COMMENT: - if (strncmp(cp, "*\\\n", 3) == 0) { + if (strncmp(cp, "*\\\r\n", 4) == 0) { + incomment = FINISHING_COMMENT; + cp += 4; + } else if (strncmp(cp, "*\\\n", 3) == 0) { incomment = FINISHING_COMMENT; cp += 3; } else if (strncmp(cp, "*/", 2) == 0) { @@ -899,6 +1059,31 @@ skipcomment(const char *cp) } /* + * Skip macro arguments. + */ +static const char * +skipargs(const char *cp) +{ + const char *ocp = cp; + int level = 0; + cp = skipcomment(cp); + if (*cp != '(') + return (cp); + do { + if (*cp == '(') + level++; + if (*cp == ')') + level--; + cp = skipcomment(cp+1); + } while (level != 0 && *cp != '\0'); + if (level == 0) + return (cp); + else + /* Rewind and re-detect the syntax error later. */ + return (ocp); +} + +/* * Skip over an identifier. */ static const char * @@ -960,7 +1145,7 @@ addsym(bool ignorethis, bool definethis, char *sym) value[symind] = val+1; *val = '\0'; } else if (*val == '\0') - value[symind] = ""; + value[symind] = "1"; else usage(); } else { @@ -1008,5 +1193,6 @@ error(const char *msg) else warnx("%s: %d: %s (#if line %d depth %d)", filename, linenum, msg, stifline[depth], depth); + closeout(); errx(2, "output may be truncated"); } diff --git a/usr.bin/unifdef/unifdefall.sh b/usr.bin/unifdef/unifdefall.sh index bcba08c..179fc93 100644 --- a/usr.bin/unifdef/unifdefall.sh +++ b/usr.bin/unifdef/unifdefall.sh @@ -1,29 +1,70 @@ #!/bin/sh # -# remove all the #if's from a source file +# unifdefall: remove all the #if's from a source file # -# $dotat: things/unifdefall.sh,v 1.9 2002/09/24 19:43:57 fanf2 Exp $ +# Copyright (c) 2002 - 2010 Tony Finch <dot@dotat.at> +# Copyright (c) 2009 - 2010 Jonathan Nieder <jrnieder@gmail.com> +# +# 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 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 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. +# +# $dotat: unifdef/unifdefall.sh,v 1.27 2010/01/19 16:09:50 fanf2 Exp $ # $FreeBSD$ set -e -basename=`basename $0` -tmp=`mktemp -d -t $basename` || exit 2 +unifdef="$(dirname "$0")/unifdef" +if [ ! -e "$unifdef" ] +then + unifdef=unifdef +fi +# export to the final shell command +export unifdef -unifdef -s "$@" | sort | uniq > $tmp/ctrl -cpp -dM "$@" | sort | - sed -Ee 's/^#define[ ]+(.*[^ ])[ ]*$/\1/' > $tmp/hashdefs -sed -Ee 's/^([A-Za-z0-9_]+).*$/\1/' $tmp/hashdefs > $tmp/alldef -comm -23 $tmp/ctrl $tmp/alldef > $tmp/undef -comm -12 $tmp/ctrl $tmp/alldef > $tmp/def +basename=$(basename "$0") +tmp=$(mktemp -d "${TMPDIR:-/tmp}/$basename.XXXXXXXXXX") || exit 2 +trap 'rm -r "$tmp" || exit 1' EXIT -echo unifdef -k \\ > $tmp/cmd -sed -Ee 's/^(.*)$/-U\1 \\/' $tmp/undef >> $tmp/cmd -while read sym -do sed -Ee '/^('"$sym"')([(][^)]*[)])?([ ]+(.*))?$/!d;s//-D\1=\4/' $tmp/hashdefs -done < $tmp/def | - sed -Ee 's/\\/\\\\/g;s/"/\\"/g;s/^/"/;s/$/" \\/' >> $tmp/cmd -echo '"$@"' >> $tmp/cmd -sh $tmp/cmd "$@" +export LC_ALL=C -rm -r $tmp +# list of all controlling macros +"$unifdef" -s "$@" | sort | uniq >"$tmp/ctrl" +# list of all macro definitions +cpp -dM "$@" | sort | sed 's/^#define //' >"$tmp/hashdefs" +# list of defined macro names +sed 's/[^A-Za-z0-9_].*$//' <"$tmp/hashdefs" >"$tmp/alldef" +# list of undefined and defined controlling macros +comm -23 "$tmp/ctrl" "$tmp/alldef" >"$tmp/undef" +comm -12 "$tmp/ctrl" "$tmp/alldef" >"$tmp/def" +# create a sed script that extracts the controlling macro definitions +# and converts them to unifdef command-line arguments +sed 's|.*|s/^&\\(([^)]*)\\)\\{0,1\\} /-D&=/p|' <"$tmp/def" >"$tmp/script" +# create the final unifdef command +{ echo '"$unifdef" -k \' + # convert the controlling undefined macros to -U arguments + sed 's/.*/-U& \\/' <"$tmp/undef" + # convert the controlling defined macros to quoted -D arguments + sed -nf "$tmp/script" <"$tmp/hashdefs" | + sed "s/'/'\\\\''/g;s/.*/'&' \\\\/" + echo '"$@"' +} >"$tmp/cmd" +# run the command we just created +sh "$tmp/cmd" "$@" diff --git a/usr.bin/uniq/uniq.1 b/usr.bin/uniq/uniq.1 index a12ad3a..ec94d05 100644 --- a/usr.bin/uniq/uniq.1 +++ b/usr.bin/uniq/uniq.1 @@ -35,7 +35,7 @@ .\" From: @(#)uniq.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd July 3, 2004 +.Dd December 17, 2009 .Dt UNIQ 1 .Os .Sh NAME @@ -153,7 +153,3 @@ A .Nm command appeared in .At v3 . -.Sh BUGS -Input lines are limited to -.Dv LINE_MAX -(2048) bytes in length. diff --git a/usr.bin/uniq/uniq.c b/usr.bin/uniq/uniq.c index bc04db7..2b11fe4 100644 --- a/usr.bin/uniq/uniq.c +++ b/usr.bin/uniq/uniq.c @@ -52,6 +52,7 @@ static const char rcsid[] = #include <err.h> #include <limits.h> #include <locale.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -59,7 +60,8 @@ static const char rcsid[] = #include <wchar.h> #include <wctype.h> -#define MAXLINELEN (LINE_MAX + 1) +#define INITLINELEN (LINE_MAX + 1) +#define MAXLINELEN ((SIZE_MAX / sizeof(wchar_t)) / 2) int cflag, dflag, uflag; int numchars, numfields, repeats; @@ -137,8 +139,8 @@ main (int argc, char *argv[]) if (argc > 1) ofp = file(argv[1], "w"); - prevbuflen = MAXLINELEN; - thisbuflen = MAXLINELEN; + prevbuflen = INITLINELEN; + thisbuflen = INITLINELEN; prevline = malloc(prevbuflen * sizeof(*prevline)); thisline = malloc(thisbuflen * sizeof(*thisline)); if (prevline == NULL || thisline == NULL) @@ -198,16 +200,19 @@ getline(wchar_t *buf, size_t *buflen, FILE *fp) bufpos = 0; while ((ch = getwc(fp)) != WEOF && ch != '\n') { - if (bufpos + 2 >= *buflen) { + if (bufpos + 1 >= *buflen) { *buflen = *buflen * 2; + if (*buflen > MAXLINELEN) + errx(1, + "Maximum line buffer length (%zu) exceeded", + MAXLINELEN); buf = reallocf(buf, *buflen * sizeof(*buf)); if (buf == NULL) - return (NULL); + err(1, "reallocf"); } buf[bufpos++] = ch; } - if (bufpos + 1 != *buflen) - buf[bufpos] = '\0'; + buf[bufpos] = '\0'; return (bufpos != 0 || ch == '\n' ? buf : NULL); } @@ -305,13 +310,13 @@ wcsicoll(wchar_t *s1, wchar_t *s2) new_l2_buflen = wcsicoll_l2_buflen; while (new_l1_buflen < l1) { if (new_l1_buflen == 0) - new_l1_buflen = MAXLINELEN; + new_l1_buflen = INITLINELEN; else new_l1_buflen *= 2; } while (new_l2_buflen < l2) { if (new_l2_buflen == 0) - new_l2_buflen = MAXLINELEN; + new_l2_buflen = INITLINELEN; else new_l2_buflen *= 2; } diff --git a/usr.bin/unzip/Makefile b/usr.bin/unzip/Makefile index cc31dea..ef8a690 100644 --- a/usr.bin/unzip/Makefile +++ b/usr.bin/unzip/Makefile @@ -1,9 +1,8 @@ # $FreeBSD$ PROG = unzip -WARNS ?= 6 CSTD = c99 -DPADD = ${LIBARCHIVE} -LDADD = -larchive +DPADD = ${LIBARCHIVE} ${LIBZ} +LDADD = -larchive -lz .include <bsd.prog.mk> diff --git a/usr.bin/unzip/unzip.1 b/usr.bin/unzip/unzip.1 index b6ee87a..3d4de46 100644 --- a/usr.bin/unzip/unzip.1 +++ b/usr.bin/unzip/unzip.1 @@ -25,7 +25,7 @@ .\" .\" $FreeBSD$ .\" -.Dd September 7, 2009 +.Dd February 16, 2010 .Dt UNZIP 1 .Os .Sh NAME @@ -158,17 +158,6 @@ utility is only able to process ZIP archives handled by Depending on the installed version of .Xr libarchive , this may or may not include self-extracting archives. -.Sh BUGS -The -.Nm -utility currently does not support asking the user whether to -overwrite or skip a file that already exists on disk. -To be on the safe side, it will fail if it encounters a file that -already exists and neither the -.Fl n -nor the -.Fl o -command line option was specified. .Sh SEE ALSO .Xr libarchive 3 .Sh HISTORY diff --git a/usr.bin/unzip/unzip.c b/usr.bin/unzip/unzip.c index f0856a4..b80810c 100644 --- a/usr.bin/unzip/unzip.c +++ b/usr.bin/unzip/unzip.c @@ -383,7 +383,7 @@ extract_dir(struct archive *a, struct archive_entry *e, const char *path) { int mode; - mode = archive_entry_filetype(e) & 0777; + mode = archive_entry_mode(e) & 0777; if (mode == 0) mode = 0755; @@ -411,52 +411,104 @@ extract_dir(struct archive *a, struct archive_entry *e, const char *path) static unsigned char buffer[8192]; static char spinner[] = { '|', '/', '-', '\\' }; +static int +handle_existing_file(char **path) +{ + size_t alen; + ssize_t len; + char buf[4]; + + for (;;) { + fprintf(stderr, + "replace %s? [y]es, [n]o, [A]ll, [N]one, [r]ename: ", + *path); + if (fgets(buf, sizeof(buf), stdin) == 0) { + clearerr(stdin); + printf("NULL\n(EOF or read error, " + "treating as \"[N]one\"...)\n"); + n_opt = 1; + return -1; + } + switch (*buf) { + case 'A': + o_opt = 1; + /* FALLTHROUGH */ + case 'y': + case 'Y': + (void)unlink(*path); + return 1; + case 'N': + n_opt = 1; + /* FALLTHROUGH */ + case 'n': + return -1; + case 'r': + case 'R': + printf("New name: "); + fflush(stdout); + free(*path); + *path = NULL; + alen = 0; + len = getdelim(path, &alen, '\n', stdin); + if ((*path)[len - 1] == '\n') + (*path)[len - 1] = '\0'; + return 0; + default: + break; + } + } +} + /* * Extract a regular file. */ static void -extract_file(struct archive *a, struct archive_entry *e, const char *path) +extract_file(struct archive *a, struct archive_entry *e, char **path) { int mode; time_t mtime; struct stat sb; struct timeval tv[2]; - int cr, fd, text, warn; + int cr, fd, text, warn, check; ssize_t len; unsigned char *p, *q, *end; - mode = archive_entry_filetype(e) & 0777; + mode = archive_entry_mode(e) & 0777; if (mode == 0) mode = 0644; mtime = archive_entry_mtime(e); /* look for existing file of same name */ - if (lstat(path, &sb) == 0) { +recheck: + if (lstat(*path, &sb) == 0) { if (u_opt || f_opt) { /* check if up-to-date */ if (S_ISREG(sb.st_mode) && sb.st_mtime >= mtime) return; - (void)unlink(path); + (void)unlink(*path); } else if (o_opt) { /* overwrite */ - (void)unlink(path); + (void)unlink(*path); } else if (n_opt) { /* do not overwrite */ return; } else { - /* XXX ask user */ - errorx("not implemented"); + check = handle_existing_file(path); + if (check == 0) + goto recheck; + if (check == -1) + return; /* do not overwrite */ } } else { if (f_opt) return; } - if ((fd = open(path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) - error("open('%s')", path); + if ((fd = open(*path, O_RDWR|O_CREAT|O_TRUNC, mode)) < 0) + error("open('%s')", *path); /* loop over file contents and write to disk */ - info(" extracting: %s", path); + info(" extracting: %s", *path); text = a_opt; warn = 0; cr = 0; @@ -473,7 +525,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path) if (a_opt && cr) { if (len == 0 || buffer[0] != '\n') if (write(fd, "\r", 1) != 1) - error("write('%s')", path); + error("write('%s')", *path); cr = 0; } @@ -504,7 +556,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path) /* simple case */ if (!a_opt || !text) { if (write(fd, buffer, len) != len) - error("write('%s')", path); + error("write('%s')", *path); continue; } @@ -514,7 +566,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path) if (!warn && !isascii(*q)) { warningx("%s may be corrupted due" " to weak text file detection" - " heuristic", path); + " heuristic", *path); warn = 1; } if (q[0] != '\r') @@ -527,7 +579,7 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path) break; } if (write(fd, p, q - p) != q - p) - error("write('%s')", path); + error("write('%s')", *path); } } if (tty) @@ -542,9 +594,9 @@ extract_file(struct archive *a, struct archive_entry *e, const char *path) tv[1].tv_sec = mtime; tv[1].tv_usec = 0; if (futimes(fd, tv) != 0) - error("utimes('%s')", path); + error("utimes('%s')", *path); if (close(fd) != 0) - error("close('%s')", path); + error("close('%s')", *path); } /* @@ -620,7 +672,7 @@ extract(struct archive *a, struct archive_entry *e) if (S_ISDIR(filetype)) extract_dir(a, e, realpathname); else - extract_file(a, e, realpathname); + extract_file(a, e, &realpathname); free(realpathname); free(pathname); @@ -814,7 +866,8 @@ unzip(const char *fn) ac(archive_read_support_format_zip(a)); ac(archive_read_open_fd(a, fd, 8192)); - printf("Archive: %s\n", fn); + if (!p_opt && !q_opt) + printf("Archive: %s\n", fn); if (v_opt == 1) { printf(" Length Date Time Name\n"); printf(" -------- ---- ---- ----\n"); diff --git a/usr.bin/usbhidaction/usbhidaction.1 b/usr.bin/usbhidaction/usbhidaction.1 index 95420c8f..148c6d8 100644 --- a/usr.bin/usbhidaction/usbhidaction.1 +++ b/usr.bin/usbhidaction/usbhidaction.1 @@ -15,13 +15,6 @@ .\" 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 NetBSD -.\" Foundation, Inc. and its contributors. -.\" 4. 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 diff --git a/usr.bin/usbhidaction/usbhidaction.c b/usr.bin/usbhidaction/usbhidaction.c index a5e2598..fc78d4e 100644 --- a/usr.bin/usbhidaction/usbhidaction.c +++ b/usr.bin/usbhidaction/usbhidaction.c @@ -16,13 +16,6 @@ * 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 NetBSD - * Foundation, Inc. and its contributors. - * 4. 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 diff --git a/usr.bin/usbhidctl/usbhid.c b/usr.bin/usbhidctl/usbhid.c index 485418f..2ac947d 100644 --- a/usr.bin/usbhidctl/usbhid.c +++ b/usr.bin/usbhidctl/usbhid.c @@ -16,13 +16,6 @@ * 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 NetBSD - * Foundation, Inc. and its contributors. - * 4. 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 diff --git a/usr.bin/usbhidctl/usbhidctl.1 b/usr.bin/usbhidctl/usbhidctl.1 index 8ba5ec1..37c7c7a 100644 --- a/usr.bin/usbhidctl/usbhidctl.1 +++ b/usr.bin/usbhidctl/usbhidctl.1 @@ -15,13 +15,6 @@ .\" 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 NetBSD -.\" Foundation, Inc. and its contributors. -.\" 4. 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 diff --git a/usr.bin/users/users.1 b/usr.bin/users/users.1 index c3454c6..2179c5e 100644 --- a/usr.bin/users/users.1 +++ b/usr.bin/users/users.1 @@ -46,14 +46,14 @@ The utility lists the login names of the users currently on the system, in sorted order, space separated, on a single line. .Sh FILES -.Bl -tag -width /var/run/utmp -.It Pa /var/run/utmp +.Bl -tag -width /var/run/utx.active +.It Pa /var/run/utx.active .El .Sh SEE ALSO .Xr finger 1 , .Xr last 1 , .Xr who 1 , -.Xr utmp 5 +.Xr getutxent 3 .Sh HISTORY The .Nm diff --git a/usr.bin/users/users.c b/usr.bin/users/users.c index c90bfbd..ccf8006 100644 --- a/usr.bin/users/users.c +++ b/usr.bin/users/users.c @@ -45,15 +45,16 @@ static const char rcsid[] = "$FreeBSD$"; #endif /* not lint */ +#include <sys/param.h> #include <sys/types.h> #include <err.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> -typedef char namebuf[UT_NAMESIZE]; +typedef char namebuf[MAXLOGNAME]; int scmp(const void *, const void *); static void usage(void); @@ -65,7 +66,7 @@ main(int argc, char **argv) int ncnt = 0; int nmax = 0; int cnt; - struct utmp utmp; + struct utmpx *ut; int ch; while ((ch = getopt(argc, argv, "")) != -1) @@ -77,28 +78,28 @@ main(int argc, char **argv) argc -= optind; argv += optind; - if (!freopen(_PATH_UTMP, "r", stdin)) - errx(1, "can't open %s", _PATH_UTMP); - while (fread((char *)&utmp, sizeof(utmp), 1, stdin) == 1) { - if (*utmp.ut_name) { - if (ncnt >= nmax) { - nmax += 32; - names = realloc(names, sizeof (*names) * nmax); - if (!names) { - errx(1, "realloc"); - /* NOTREACHED */ - } + setutxent(); + while ((ut = getutxent()) != NULL) { + if (ut->ut_type != USER_PROCESS) + continue; + if (ncnt >= nmax) { + nmax += 32; + names = realloc(names, sizeof(*names) * nmax); + if (!names) { + errx(1, "realloc"); + /* NOTREACHED */ } - (void)strncpy(names[ncnt], utmp.ut_name, UT_NAMESIZE); - ++ncnt; } + (void)strlcpy(names[ncnt], ut->ut_user, sizeof(*names)); + ++ncnt; } - if (ncnt) { - qsort(names, ncnt, UT_NAMESIZE, scmp); - (void)printf("%.*s", UT_NAMESIZE, names[0]); + endutxent(); + if (ncnt > 0) { + qsort(names, ncnt, sizeof(namebuf), scmp); + (void)printf("%s", names[0]); for (cnt = 1; cnt < ncnt; ++cnt) - if (strncmp(names[cnt], names[cnt - 1], UT_NAMESIZE)) - (void)printf(" %.*s", UT_NAMESIZE, names[cnt]); + if (strcmp(names[cnt], names[cnt - 1]) != 0) + (void)printf(" %s", names[cnt]); (void)printf("\n"); } exit(0); @@ -114,5 +115,6 @@ usage(void) int scmp(const void *p, const void *q) { - return(strncmp(p, q, UT_NAMESIZE)); + + return (strcmp(p, q)); } diff --git a/usr.bin/uudecode/Makefile b/usr.bin/uudecode/Makefile index 37005f3..909ce3d 100644 --- a/usr.bin/uudecode/Makefile +++ b/usr.bin/uudecode/Makefile @@ -2,7 +2,6 @@ # $FreeBSD$ PROG= uudecode -WARNS?= 4 LINKS= ${BINDIR}/uudecode ${BINDIR}/b64decode NO_MAN= diff --git a/usr.bin/uuencode/Makefile b/usr.bin/uuencode/Makefile index c795753..9a8b991 100644 --- a/usr.bin/uuencode/Makefile +++ b/usr.bin/uuencode/Makefile @@ -2,7 +2,6 @@ # $FreeBSD$ PROG= uuencode -WARNS?= 4 MAN= uuencode.1 uuencode.format.5 LINKS= ${BINDIR}/uuencode ${BINDIR}/b64encode MLINKS= uuencode.1 uudecode.1 \ diff --git a/usr.bin/vacation/Makefile b/usr.bin/vacation/Makefile index 63ba3ab..056f576 100644 --- a/usr.bin/vacation/Makefile +++ b/usr.bin/vacation/Makefile @@ -9,6 +9,8 @@ CFLAGS+=-I${SENDMAIL_DIR}/src -I${SENDMAIL_DIR}/include -I. CFLAGS+=-DNEWDB -DNOT_SENDMAIL CFLAGS+=-D_FFR_LISTDB -D_FFR_DEBUG +WARNS?= 2 + LIBSMDIR= ${.OBJDIR}/../../lib/libsm LIBSM= ${LIBSMDIR}/libsm.a diff --git a/usr.bin/vgrind/Makefile b/usr.bin/vgrind/Makefile index 1a64c1c..612e504 100644 --- a/usr.bin/vgrind/Makefile +++ b/usr.bin/vgrind/Makefile @@ -11,6 +11,8 @@ FILESDIR= ${SHAREDIR}/misc FILESDIR_tmac.vgrind= ${SHAREDIR}/tmac MAN= vgrind.1 vgrindefs.5 +WARNS?= 2 + BINDIR= /usr/libexec SCRIPTSDIR=/usr/bin diff --git a/usr.bin/vi/Makefile b/usr.bin/vi/Makefile index 7276b7b..048c35b 100644 --- a/usr.bin/vi/Makefile +++ b/usr.bin/vi/Makefile @@ -9,6 +9,8 @@ CFLAGS+= -DGTAGS #if using ncurses: CFLAGS+= -DSYSV_CURSES +WARNS?= 0 + VI= nvi EX= nex VIEW= nview diff --git a/usr.bin/vis/foldit.c b/usr.bin/vis/foldit.c index 6ad82c2..3c977cb 100644 --- a/usr.bin/vis/foldit.c +++ b/usr.bin/vis/foldit.c @@ -44,9 +44,7 @@ static const char sccsid[] = "@(#)foldit.c 8.1 (Berkeley) 6/6/93"; #include "extern.h" int -foldit(chunk, col, max) - char *chunk; - int col, max; +foldit(char *chunk, int col, int max) { char *cp; diff --git a/usr.bin/vmstat/Makefile b/usr.bin/vmstat/Makefile index 6e87a6a..d413d25 100644 --- a/usr.bin/vmstat/Makefile +++ b/usr.bin/vmstat/Makefile @@ -6,4 +6,6 @@ MAN= vmstat.8 DPADD= ${LIBDEVSTAT} ${LIBKVM} ${LIBMEMSTAT} ${LIBUTIL} LDADD= -ldevstat -lkvm -lmemstat -lutil +WARNS?= 1 + .include <bsd.prog.mk> diff --git a/usr.bin/vmstat/vmstat.c b/usr.bin/vmstat/vmstat.c index aa1d13b..cf4b73a 100644 --- a/usr.bin/vmstat/vmstat.c +++ b/usr.bin/vmstat/vmstat.c @@ -58,6 +58,7 @@ __FBSDID("$FreeBSD$"); #include <sys/resource.h> #include <sys/sysctl.h> #include <sys/vmmeter.h> +#include <sys/pcpu.h> #include <vm/vm_param.h> @@ -418,10 +419,90 @@ getuptime(void) } static void +fill_pcpu(struct pcpu ***pcpup, int* maxcpup) +{ + struct pcpu **pcpu; + + int maxcpu, size, i; + + *pcpup = NULL; + + if (kd == NULL) + return; + + maxcpu = kvm_getmaxcpu(kd); + if (maxcpu < 0) + errx(1, "kvm_getmaxcpu: %s", kvm_geterr(kd)); + + pcpu = calloc(maxcpu, sizeof(struct pcpu *)); + if (pcpu == NULL) + err(1, "calloc"); + + for (i = 0; i < maxcpu; i++) { + pcpu[i] = kvm_getpcpu(kd, i); + if (pcpu[i] == (struct pcpu *)-1) + errx(1, "kvm_getpcpu: %s", kvm_geterr(kd)); + } + + *maxcpup = maxcpu; + *pcpup = pcpu; +} + +static void +free_pcpu(struct pcpu **pcpu, int maxcpu) +{ + int i; + + for (i = 0; i < maxcpu; i++) + free(pcpu[i]); + free(pcpu); +} + +static void fill_vmmeter(struct vmmeter *vmmp) { + struct pcpu **pcpu; + int maxcpu, i; + if (kd != NULL) { kread(X_SUM, vmmp, sizeof(*vmmp)); + fill_pcpu(&pcpu, &maxcpu); + for (i = 0; i < maxcpu; i++) { + if (pcpu[i] == NULL) + continue; +#define ADD_FROM_PCPU(i, name) \ + vmmp->name += pcpu[i]->pc_cnt.name + ADD_FROM_PCPU(i, v_swtch); + ADD_FROM_PCPU(i, v_trap); + ADD_FROM_PCPU(i, v_syscall); + ADD_FROM_PCPU(i, v_intr); + ADD_FROM_PCPU(i, v_soft); + ADD_FROM_PCPU(i, v_vm_faults); + ADD_FROM_PCPU(i, v_cow_faults); + ADD_FROM_PCPU(i, v_cow_optim); + ADD_FROM_PCPU(i, v_zfod); + ADD_FROM_PCPU(i, v_ozfod); + ADD_FROM_PCPU(i, v_swapin); + ADD_FROM_PCPU(i, v_swapout); + ADD_FROM_PCPU(i, v_swappgsin); + ADD_FROM_PCPU(i, v_swappgsout); + ADD_FROM_PCPU(i, v_vnodein); + ADD_FROM_PCPU(i, v_vnodeout); + ADD_FROM_PCPU(i, v_vnodepgsin); + ADD_FROM_PCPU(i, v_vnodepgsout); + ADD_FROM_PCPU(i, v_intrans); + ADD_FROM_PCPU(i, v_tfree); + ADD_FROM_PCPU(i, v_forks); + ADD_FROM_PCPU(i, v_vforks); + ADD_FROM_PCPU(i, v_rforks); + ADD_FROM_PCPU(i, v_kthreads); + ADD_FROM_PCPU(i, v_forkpages); + ADD_FROM_PCPU(i, v_vforkpages); + ADD_FROM_PCPU(i, v_rforkpages); + ADD_FROM_PCPU(i, v_kthreadpages); +#undef ADD_FROM_PCPU + } + free_pcpu(pcpu, maxcpu); } else { size_t size = sizeof(unsigned int); #define GET_VM_STATS(cat, name) \ diff --git a/usr.bin/w/w.1 b/usr.bin/w/w.1 index 0dfbd1b..26ca42a 100644 --- a/usr.bin/w/w.1 +++ b/usr.bin/w/w.1 @@ -87,8 +87,8 @@ If one or more .Ar user names are specified, the output is restricted to those users. .Sh FILES -.Bl -tag -width ".Pa /var/run/utmp" -compact -.It Pa /var/run/utmp +.Bl -tag -width ".Pa /var/run/utx.active" -compact +.It Pa /var/run/utx.active list of users on the system .El .Sh COMPATIBILITY diff --git a/usr.bin/w/w.c b/usr.bin/w/w.c index 10ec7b0..fb97e8a 100644 --- a/usr.bin/w/w.c +++ b/usr.bin/w/w.c @@ -84,13 +84,13 @@ static const char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94"; #include <string.h> #include <timeconv.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include <vis.h> #include "extern.h" struct timeval boottime; -struct utmp utmp; +struct utmpx *utmp; struct winsize ws; kvm_t *kd; time_t now; /* the current time of day */ @@ -109,7 +109,7 @@ char **sel_users; /* login array of particular users selected */ */ struct entry { struct entry *next; - struct utmp utmp; + struct utmpx utmp; dev_t tdev; /* dev_t of terminal */ time_t idle; /* idle time of terminal in seconds */ struct kinfo_proc *kp; /* `most interesting' proc */ @@ -117,13 +117,14 @@ struct entry { struct kinfo_proc *dkp; /* debug option proc list */ } *ep, *ehead = NULL, **nextp = &ehead; -#define debugproc(p) *((struct kinfo_proc **)&(p)->ki_udata) +#define debugproc(p) *(&((struct kinfo_proc *)p)->ki_udata) -/* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */ -#define W_DISPHOSTSIZE 16 +#define W_DISPUSERSIZE 10 +#define W_DISPLINESIZE 8 +#define W_DISPHOSTSIZE 24 static void pr_header(time_t *, int); -static struct stat *ttystat(char *, int); +static struct stat *ttystat(char *); static void usage(int); static int this_is_uptime(const char *s); @@ -135,7 +136,6 @@ main(int argc, char *argv[]) struct kinfo_proc *kp; struct kinfo_proc *dkp; struct stat *stp; - FILE *ut; time_t touched; int ch, i, nentries, nusers, wcmd, longidle, longattime, dropgid; const char *memf, *nlistf, *p; @@ -158,7 +158,8 @@ main(int argc, char *argv[]) } dropgid = 0; - memf = nlistf = _PATH_DEVNULL; + memf = _PATH_DEVNULL; + nlistf = NULL; while ((ch = getopt(argc, argv, p)) != -1) switch (ch) { case 'd': @@ -208,16 +209,15 @@ main(int argc, char *argv[]) errx(1, "%s", errbuf); (void)time(&now); - if ((ut = fopen(_PATH_UTMP, "r")) == NULL) - err(1, "%s", _PATH_UTMP); if (*argv) sel_users = argv; - for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { - if (utmp.ut_name[0] == '\0') + setutxent(); + for (nusers = 0; (utmp = getutxent()) != NULL;) { + if (utmp->ut_type != USER_PROCESS) continue; - if (!(stp = ttystat(utmp.ut_line, UT_LINESIZE))) + if (!(stp = ttystat(utmp->ut_line))) continue; /* corrupted record */ ++nusers; if (wcmd == 0) @@ -228,7 +228,7 @@ main(int argc, char *argv[]) usermatch = 0; for (user = sel_users; !usermatch && *user; user++) - if (!strncmp(utmp.ut_name, *user, UT_NAMESIZE)) + if (!strcmp(utmp->ut_user, *user)) usermatch = 1; if (!usermatch) continue; @@ -237,7 +237,7 @@ main(int argc, char *argv[]) errx(1, "calloc"); *nextp = ep; nextp = &ep->next; - memmove(&ep->utmp, &utmp, sizeof(struct utmp)); + memmove(&ep->utmp, utmp, sizeof *utmp); ep->tdev = stp->st_rdev; /* * If this is the console device, attempt to ascertain @@ -250,14 +250,14 @@ main(int argc, char *argv[]) (void)sysctlbyname("machdep.consdev", &ep->tdev, &size, NULL, 0); } touched = stp->st_atime; - if (touched < ep->utmp.ut_time) { + if (touched < ep->utmp.ut_tv.tv_sec) { /* tty untouched since before login */ - touched = ep->utmp.ut_time; + touched = ep->utmp.ut_tv.tv_sec; } if ((ep->idle = now - touched) < 0) ep->idle = 0; } - (void)fclose(ut); + endutxent(); if (header || wcmd == 0) { pr_header(&now, nusers); @@ -271,11 +271,11 @@ main(int argc, char *argv[]) #define HEADER_FROM "FROM" #define HEADER_LOGIN_IDLE "LOGIN@ IDLE " #define HEADER_WHAT "WHAT\n" -#define WUSED (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \ +#define WUSED (W_DISPUSERSIZE + W_DISPLINESIZE + W_DISPHOSTSIZE + \ sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ (void)printf("%-*.*s %-*.*s %-*.*s %s", - UT_NAMESIZE, UT_NAMESIZE, HEADER_USER, - UT_LINESIZE, UT_LINESIZE, HEADER_TTY, + W_DISPUSERSIZE, W_DISPUSERSIZE, HEADER_USER, + W_DISPLINESIZE, W_DISPLINESIZE, HEADER_TTY, W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM, HEADER_LOGIN_IDLE HEADER_WHAT); } @@ -283,7 +283,8 @@ main(int argc, char *argv[]) if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) err(1, "%s", kvm_geterr(kd)); for (i = 0; i < nentries; i++, kp++) { - if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB) + if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB || + kp->ki_tdev == NODEV) continue; for (ep = ehead; ep != NULL; ep = ep->next) { if (ep->tdev == kp->ki_tdev) { @@ -347,7 +348,7 @@ main(int argc, char *argv[]) } for (ep = ehead; ep != NULL; ep = ep->next) { - char host_buf[UT_HOSTSIZE + 1]; + struct addrinfo hints, *res; struct sockaddr_storage ss; struct sockaddr *sa = (struct sockaddr *)&ss; struct sockaddr_in *lsin = (struct sockaddr_in *)&ss; @@ -355,9 +356,7 @@ main(int argc, char *argv[]) time_t t; int isaddr; - host_buf[UT_HOSTSIZE] = '\0'; - strncpy(host_buf, ep->utmp.ut_host, UT_HOSTSIZE); - p = *host_buf ? host_buf : "-"; + p = *ep->utmp.ut_host ? ep->utmp.ut_host : "-"; if ((x_suffix = strrchr(p, ':')) != NULL) { if ((dot = strchr(x_suffix, '.')) != NULL && strchr(dot+1, '.') == NULL) @@ -365,23 +364,42 @@ main(int argc, char *argv[]) else x_suffix = NULL; } + + isaddr = 0; + memset(&ss, '\0', sizeof(ss)); + if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) { + lsin6->sin6_len = sizeof(*lsin6); + lsin6->sin6_family = AF_INET6; + isaddr = 1; + } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) { + lsin->sin_len = sizeof(*lsin); + lsin->sin_family = AF_INET; + isaddr = 1; + } if (!nflag) { /* Attempt to change an IP address into a name */ - isaddr = 0; - memset(&ss, '\0', sizeof(ss)); - if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) { - lsin6->sin6_len = sizeof(*lsin6); - lsin6->sin6_family = AF_INET6; - isaddr = 1; - } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) { - lsin->sin_len = sizeof(*lsin); - lsin->sin_family = AF_INET; - isaddr = 1; - } if (isaddr && realhostname_sa(fn, sizeof(fn), sa, sa->sa_len) == HOSTNAME_FOUND) p = fn; + } else if (!isaddr) { + /* + * If a host has only one A/AAAA RR, change a + * name into an IP address + */ + memset(&hints, 0, sizeof(hints)); + hints.ai_flags = AI_PASSIVE; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + if (getaddrinfo(p, NULL, &hints, &res) == 0) { + if (res->ai_next == NULL && + getnameinfo(res->ai_addr, res->ai_addrlen, + fn, sizeof(fn), NULL, 0, + NI_NUMERICHOST) == 0) + p = fn; + freeaddrinfo(res); + } } + if (x_suffix) { (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix); p = buf; @@ -399,13 +417,14 @@ main(int argc, char *argv[]) } } (void)printf("%-*.*s %-*.*s %-*.*s ", - UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, - UT_LINESIZE, UT_LINESIZE, - strncmp(ep->utmp.ut_line, "tty", 3) && + W_DISPUSERSIZE, W_DISPUSERSIZE, ep->utmp.ut_user, + W_DISPLINESIZE, W_DISPLINESIZE, + *ep->utmp.ut_line ? + (strncmp(ep->utmp.ut_line, "tty", 3) && strncmp(ep->utmp.ut_line, "cua", 3) ? - ep->utmp.ut_line : ep->utmp.ut_line + 3, + ep->utmp.ut_line : ep->utmp.ut_line + 3) : "-", W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-"); - t = _time_to_time32(ep->utmp.ut_time); + t = ep->utmp.ut_tv.tv_sec; longattime = pr_attime(&t, &now); longidle = pr_idle(ep->idle); (void)printf("%.*s\n", argwidth - longidle - longattime, @@ -476,12 +495,12 @@ pr_header(time_t *nowp, int nusers) } static struct stat * -ttystat(char *line, int sz) +ttystat(char *line) { static struct stat sb; char ttybuf[MAXPATHLEN]; - (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); + (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); if (stat(ttybuf, &sb) == 0) { return (&sb); } else diff --git a/usr.bin/wall/Makefile b/usr.bin/wall/Makefile index 65abb61..c5deb3b 100644 --- a/usr.bin/wall/Makefile +++ b/usr.bin/wall/Makefile @@ -1,4 +1,5 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ PROG= wall SRCS= ttymsg.c wall.c diff --git a/usr.bin/wall/wall.c b/usr.bin/wall/wall.c index be5329a..b92edd4 100644 --- a/usr.bin/wall/wall.c +++ b/usr.bin/wall/wall.c @@ -65,7 +65,7 @@ static const char sccsid[] = "@(#)wall.c 8.2 (Berkeley) 11/16/93"; #include <string.h> #include <time.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> #include "ttymsg.h" @@ -82,12 +82,12 @@ int mbufsize; char *mbuf; static int -ttystat(char *line, int sz) +ttystat(char *line) { struct stat sb; char ttybuf[MAXPATHLEN]; - (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); + (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); if (stat(ttybuf, &sb) == 0) { return (0); } else @@ -98,17 +98,14 @@ int main(int argc, char *argv[]) { struct iovec iov; - struct utmp utmp; + struct utmpx *utmp; int ch; int ingroup; - FILE *fp; struct wallgroup *g; struct group *grp; char **np; const char *p; struct passwd *pw; - char line[sizeof(utmp.ut_line) + 1]; - char username[sizeof(utmp.ut_name) + 1]; (void)setlocale(LC_CTYPE, ""); @@ -145,20 +142,17 @@ main(int argc, char *argv[]) makemsg(*argv); - if (!(fp = fopen(_PATH_UTMP, "r"))) - err(1, "cannot read %s", _PATH_UTMP); iov.iov_base = mbuf; iov.iov_len = mbufsize; /* NOSTRICT */ - while (fread((char *)&utmp, sizeof(utmp), 1, fp) == 1) { - if (!utmp.ut_name[0]) + while ((utmp = getutxent()) != NULL) { + if (utmp->ut_type != USER_PROCESS) continue; - if (ttystat(utmp.ut_line, UT_LINESIZE) != 0) + if (ttystat(utmp->ut_line) != 0) continue; if (grouplist) { ingroup = 0; - strlcpy(username, utmp.ut_name, sizeof(utmp.ut_name)); - pw = getpwnam(username); + pw = getpwnam(utmp->ut_user); if (!pw) continue; for (g = grouplist; g && ingroup == 0; g = g->next) { @@ -168,7 +162,7 @@ main(int argc, char *argv[]) ingroup = 1; else if ((grp = getgrgid(g->gid)) != NULL) { for (np = grp->gr_mem; *np; np++) { - if (strcmp(*np, username) == 0) { + if (strcmp(*np, utmp->ut_user) == 0) { ingroup = 1; break; } @@ -178,16 +172,14 @@ main(int argc, char *argv[]) if (ingroup == 0) continue; } - strncpy(line, utmp.ut_line, sizeof(utmp.ut_line)); - line[sizeof(utmp.ut_line)] = '\0'; - if ((p = ttymsg(&iov, 1, line, 60*5)) != NULL) + if ((p = ttymsg(&iov, 1, utmp->ut_line, 60*5)) != NULL) warnx("%s", p); } exit(0); } static void -usage() +usage(void) { (void)fprintf(stderr, "usage: wall [-g group] [file]\n"); exit(1); diff --git a/usr.bin/wc/Makefile b/usr.bin/wc/Makefile index edce9c1..4fa9f30 100644 --- a/usr.bin/wc/Makefile +++ b/usr.bin/wc/Makefile @@ -2,5 +2,4 @@ # $FreeBSD$ PROG= wc -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/wc/wc.c b/usr.bin/wc/wc.c index b787612..5b02474 100644 --- a/usr.bin/wc/wc.c +++ b/usr.bin/wc/wc.c @@ -287,7 +287,7 @@ word: gotsp = 1; } static void -usage() +usage(void) { (void)fprintf(stderr, "usage: wc [-Lclmw] [file ...]\n"); exit(1); diff --git a/usr.bin/whereis/Makefile b/usr.bin/whereis/Makefile index d48b30d..47e2c3d 100644 --- a/usr.bin/whereis/Makefile +++ b/usr.bin/whereis/Makefile @@ -1,6 +1,5 @@ # $FreeBSD$ PROG= whereis -WARNS?= 5 .include <bsd.prog.mk> diff --git a/usr.bin/who/Makefile b/usr.bin/who/Makefile index 8695ca2..0478d73 100644 --- a/usr.bin/who/Makefile +++ b/usr.bin/who/Makefile @@ -1,4 +1,5 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ PROG= who diff --git a/usr.bin/who/who.1 b/usr.bin/who/who.1 index e36b07c..4a5c6a4 100644 --- a/usr.bin/who/who.1 +++ b/usr.bin/who/who.1 @@ -90,36 +90,36 @@ Equivalent to By default, .Nm gathers information from the file -.Pa /var/run/utmp . +.Pa /var/run/utx.active . An alternate .Ar file may be specified which is usually -.Pa /var/log/wtmp +.Pa /var/log/utx.log (or -.Pa /var/log/wtmp.[0-6] +.Pa /var/log/utx.log.[0-6] depending on site policy as -.Pa wtmp +.Pa utx.log can grow quite large and daily versions may or may not be kept around after compression by .Xr ac 8 ) . The -.Pa wtmp +.Pa utx.log file contains a record of every login, logout, crash, shutdown and date change since -.Pa wtmp +.Pa utx.log was last truncated or created. .Pp If -.Pa /var/log/wtmp +.Pa /var/log/utx.log is being used as the file, the user name may be empty or one of the special characters '|', '}' and '~'. Logouts produce an output line without any user name. For more information on the special characters, see -.Xr utmp 5 . +.Xr getutxent 3 . .Sh ENVIRONMENT The .Ev COLUMNS , LANG , LC_ALL @@ -130,10 +130,10 @@ environment variables affect the execution of as described in .Xr environ 7 . .Sh FILES -.Bl -tag -width /var/log/wtmp.[0-6] -compact -.It Pa /var/run/utmp -.It Pa /var/log/wtmp -.It Pa /var/log/wtmp.[0-6] +.Bl -tag -width /var/log/utx.log.[0-6] -compact +.It Pa /var/run/utx.active +.It Pa /var/log/utx.log +.It Pa /var/log/utx.log.[0-6] .El .Sh EXIT STATUS .Ex -std @@ -141,7 +141,7 @@ as described in .Xr last 1 , .Xr users 1 , .Xr w 1 , -.Xr utmp 5 +.Xr getutxent 3 .Sh STANDARDS The .Nm diff --git a/usr.bin/who/who.c b/usr.bin/who/who.c index f94fcdb..d6f38dd 100644 --- a/usr.bin/who/who.c +++ b/usr.bin/who/who.c @@ -45,15 +45,15 @@ __FBSDID("$FreeBSD$"); #include <time.h> #include <timeconv.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> static void heading(void); -static void process_utmp(FILE *); -static void quick(FILE *); -static void row(struct utmp *); +static void process_utmp(void); +static void quick(void); +static void row(struct utmpx *); static int ttywidth(void); static void usage(void); -static void whoami(FILE *); +static void whoami(void); static int Hflag; /* Write column headings */ static int mflag; /* Show info about current terminal */ @@ -66,8 +66,6 @@ int main(int argc, char *argv[]) { int ch; - const char *file; - FILE *fp; setlocale(LC_TIME, ""); @@ -109,27 +107,25 @@ main(int argc, char *argv[]) if (argc > 1) usage(); - if (*argv != NULL) - file = *argv; - else - file = _PATH_UTMP; - if ((fp = fopen(file, "r")) == NULL) - err(1, "%s", file); + if (*argv != NULL) { + if (setutxdb(UTXDB_ACTIVE, *argv) != 0) + err(1, "%s", *argv); + } if (qflag) - quick(fp); + quick(); else { if (sflag) Tflag = uflag = 0; if (Hflag) heading(); if (mflag) - whoami(fp); + whoami(); else - process_utmp(fp); + process_utmp(); } - fclose(fp); + endutxent(); exit(0); } @@ -146,21 +142,19 @@ static void heading(void) { - printf("%-*s ", UT_NAMESIZE, "NAME"); + printf("%-16s ", "NAME"); if (Tflag) printf("S "); - printf("%-*s ", UT_LINESIZE, "LINE"); - printf("%-*s ", 12, "TIME"); + printf("%-8s %-12s ", "LINE", "TIME"); if (uflag) printf("IDLE "); - printf("%-*s", UT_HOSTSIZE, "FROM"); - putchar('\n'); + printf("%-16s\n", "FROM"); } static void -row(struct utmp *ut) +row(struct utmpx *ut) { - char buf[80], tty[sizeof(_PATH_DEV) + UT_LINESIZE]; + char buf[80], tty[PATH_MAX]; struct stat sb; time_t idle, t; static int d_first = -1; @@ -173,8 +167,7 @@ row(struct utmp *ut) state = '?'; idle = 0; if (Tflag || uflag) { - snprintf(tty, sizeof(tty), "%s%.*s", _PATH_DEV, - UT_LINESIZE, ut->ut_line); + snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, ut->ut_line); if (stat(tty, &sb) == 0) { state = sb.st_mode & (S_IWOTH|S_IWGRP) ? '+' : '-'; @@ -182,11 +175,11 @@ row(struct utmp *ut) } } - printf("%-*.*s ", UT_NAMESIZE, UT_NAMESIZE, ut->ut_name); + printf("%-16s ", ut->ut_user); if (Tflag) printf("%c ", state); - printf("%-*.*s ", UT_LINESIZE, UT_LINESIZE, ut->ut_line); - t = _time32_to_time(ut->ut_time); + printf("%-8s ", ut->ut_line); + t = ut->ut_tv.tv_sec; tm = localtime(&t); strftime(buf, sizeof(buf), d_first ? "%e %b %R" : "%b %e %R", tm); printf("%-*s ", 12, buf); @@ -200,17 +193,17 @@ row(struct utmp *ut) printf(" old "); } if (*ut->ut_host != '\0') - printf("(%.*s)", UT_HOSTSIZE, ut->ut_host); + printf("(%s)", ut->ut_host); putchar('\n'); } static int -ttystat(char *line, int sz) +ttystat(char *line) { struct stat sb; char ttybuf[MAXPATHLEN]; - (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); + (void)snprintf(ttybuf, sizeof(ttybuf), "%s%s", _PATH_DEV, line); if (stat(ttybuf, &sb) == 0) { return (0); } else @@ -218,32 +211,32 @@ ttystat(char *line, int sz) } static void -process_utmp(FILE *fp) +process_utmp(void) { - struct utmp ut; + struct utmpx *utx; - while (fread(&ut, sizeof(ut), 1, fp) == 1) { - if (*ut.ut_name == '\0') + while ((utx = getutxent()) != NULL) { + if (utx->ut_type != USER_PROCESS) continue; - if (ttystat(ut.ut_line, UT_LINESIZE) != 0) + if (ttystat(utx->ut_line) != 0) continue; - row(&ut); + row(utx); } } static void -quick(FILE *fp) +quick(void) { - struct utmp ut; + struct utmpx *utx; int col, ncols, num; ncols = ttywidth(); col = num = 0; - while (fread(&ut, sizeof(ut), 1, fp) == 1) { - if (*ut.ut_name == '\0') + while ((utx = getutxent()) != NULL) { + if (utx->ut_type != USER_PROCESS) continue; - printf("%-*.*s", UT_NAMESIZE, UT_NAMESIZE, ut.ut_name); - if (++col < ncols / (UT_NAMESIZE + 1)) + printf("%-16s", utx->ut_user); + if (++col < ncols / (16 + 1)) putchar(' '); else { col = 0; @@ -258,24 +251,23 @@ quick(FILE *fp) } static void -whoami(FILE *fp) +whoami(void) { - struct utmp ut; + struct utmpx ut, *utx; struct passwd *pwd; - const char *name, *p, *tty; + const char *name, *tty; if ((tty = ttyname(STDIN_FILENO)) == NULL) tty = "tty??"; - else if ((p = strrchr(tty, '/')) != NULL) - tty = p + 1; + else if (strncmp(tty, _PATH_DEV, sizeof _PATH_DEV - 1) == 0) + tty += sizeof _PATH_DEV - 1; + strlcpy(ut.ut_line, tty, sizeof ut.ut_line); /* Search utmp for our tty, dump first matching record. */ - while (fread(&ut, sizeof(ut), 1, fp) == 1) - if (*ut.ut_name != '\0' && strncmp(ut.ut_line, tty, - UT_LINESIZE) == 0) { - row(&ut); - return; - } + if ((utx = getutxline(&ut)) != NULL && utx->ut_type == USER_PROCESS) { + row(utx); + return; + } /* Not found; fill the utmp structure with the information we have. */ memset(&ut, 0, sizeof(ut)); @@ -283,9 +275,8 @@ whoami(FILE *fp) name = pwd->pw_name; else name = "?"; - strncpy(ut.ut_name, name, UT_NAMESIZE); - strncpy(ut.ut_line, tty, UT_LINESIZE); - ut.ut_time = _time_to_time32(time(NULL)); + strlcpy(ut.ut_user, name, sizeof ut.ut_user); + gettimeofday(&ut.ut_tv, NULL); row(&ut); } diff --git a/usr.bin/whois/whois.1 b/usr.bin/whois/whois.1 index 52da220..45b3867 100644 --- a/usr.bin/whois/whois.1 +++ b/usr.bin/whois/whois.1 @@ -32,7 +32,7 @@ .\" From: @(#)whois.1 8.1 (Berkeley) 6/6/93 .\" $FreeBSD$ .\" -.Dd January 23, 2006 +.Dd October 2, 2009 .Dt WHOIS 1 .Os .Sh NAME @@ -40,7 +40,7 @@ .Nd "Internet domain name and network number directory service" .Sh SYNOPSIS .Nm -.Op Fl aAbdfgiIklmQrR6 +.Op Fl aAbfgiIklmQrR .Op Fl c Ar country-code | Fl h Ar host .Op Fl p Ar port .Ar name ... @@ -82,11 +82,6 @@ This is the equivalent of using the .Fl h option with an argument of .Qq Ar country-code Ns Li .whois-servers.net . -.It Fl d -Use the US Department of Defense -database. -It contains points of contact for subdomains of -.Pa .MIL . .It Fl f Use the African Network Information Centre .Pq Tn AfriNIC @@ -212,17 +207,14 @@ This option is deprecated; use the option with an argument of .Qq Li RU instead. -.It Fl 6 -Use the IPv6 Resource Center -.Pq Tn 6bone -database. -It contains network names and addresses for the IPv6 network. -.El .Pp The operands specified to .Nm are treated independently and may be used as queries on different whois servers. +.El +.Sh EXIT STATUS +.Ex -std .Sh EXAMPLES Most types of data, such as domain names and .Tn IP @@ -255,15 +247,6 @@ but other .Tn TLDs can be queried by using a similar syntax.) .Pp -The following example demonstrates how to obtain information about an -.Tn IPv6 -address or hostname using the -.Fl 6 -option, which directs the query to -.Tn 6bone . -.Pp -.Dl "whois -6 IPv6-IP-Address" -.Pp The following example demonstrates how to query a whois server using a non-standard port, where .Dq Li query-data diff --git a/usr.bin/whois/whois.c b/usr.bin/whois/whois.c index c216556..864a585 100644 --- a/usr.bin/whois/whois.c +++ b/usr.bin/whois/whois.c @@ -63,7 +63,6 @@ __FBSDID("$FreeBSD$"); #define ABUSEHOST "whois.abuse.net" #define NICHOST "whois.crsnic.net" #define INICHOST "whois.networksolutions.com" -#define DNICHOST "whois.nic.mil" #define GNICHOST "whois.nic.gov" #define ANICHOST "whois.arin.net" #define LNICHOST "whois.lacnic.net" @@ -72,7 +71,6 @@ __FBSDID("$FreeBSD$"); #define PNICHOST "whois.apnic.net" #define MNICHOST "whois.ra.net" #define QNICHOST_TAIL ".whois-servers.net" -#define SNICHOST "whois.6bone.net" #define BNICHOST "whois.registro.br" #define NORIDHOST "whois.norid.no" #define IANAHOST "whois.iana.org" @@ -110,7 +108,7 @@ main(int argc, char *argv[]) country = host = qnichost = NULL; flags = use_qnichost = 0; - while ((ch = getopt(argc, argv, "aAbc:dfgh:iIklmp:QrR6")) != -1) { + while ((ch = getopt(argc, argv, "aAbc:fgh:iIklmp:QrR6")) != -1) { switch (ch) { case 'a': host = ANICHOST; @@ -124,9 +122,6 @@ main(int argc, char *argv[]) case 'c': country = optarg; break; - case 'd': - host = DNICHOST; - break; case 'f': host = FNICHOST; break; @@ -164,8 +159,10 @@ main(int argc, char *argv[]) warnx("-R is deprecated; use '-c ru' instead"); country = "ru"; break; + /* Remove in FreeBSD 10 */ case '6': - host = SNICHOST; + errx(EX_USAGE, + "-6 is deprecated; use -[aAflr] instead"); break; case '?': default: @@ -218,6 +215,10 @@ choose_server(char *domain) { char *pos, *retval; + if (strchr(domain, ':')) { + s_asprintf(&retval, "%s", ANICHOST); + return (retval); + } for (pos = strchr(domain, '\0'); pos > domain && *--pos == '.';) *pos = '\0'; if (*domain == '\0') @@ -363,7 +364,7 @@ static void usage(void) { fprintf(stderr, - "usage: whois [-aAbdfgiIklmQrR6] [-c country-code | -h hostname] " + "usage: whois [-aAbfgiIklmQrR6] [-c country-code | -h hostname] " "[-p port] name ...\n"); exit(EX_USAGE); } diff --git a/usr.bin/write/Makefile b/usr.bin/write/Makefile index 8e6454b..94345c4 100644 --- a/usr.bin/write/Makefile +++ b/usr.bin/write/Makefile @@ -1,4 +1,5 @@ # @(#)Makefile 8.1 (Berkeley) 6/6/93 +# $FreeBSD$ PROG= write BINMODE=2555 diff --git a/usr.bin/write/write.c b/usr.bin/write/write.c index 878c8c1..17f6775 100644 --- a/usr.bin/write/write.c +++ b/usr.bin/write/write.c @@ -63,7 +63,7 @@ __FBSDID("$FreeBSD$"); #include <stdlib.h> #include <string.h> #include <unistd.h> -#include <utmp.h> +#include <utmpx.h> void done(int); void do_write(char *, char *, uid_t); @@ -146,20 +146,17 @@ usage(void) int utmp_chk(char *user, char *tty) { - struct utmp u; - int ufd; - - if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) - return(0); /* ignore error, shouldn't happen anyway */ - - while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) - if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && - strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { - (void)close(ufd); + struct utmpx lu, *u; + + strncpy(lu.ut_line, tty, sizeof lu.ut_line); + setutxent(); + while ((u = getutxline(&lu)) != NULL) + if (u->ut_type == USER_PROCESS && + strcmp(user, u->ut_user) == 0) { + endutxent(); return(0); } - - (void)close(ufd); + endutxent(); return(1); } @@ -177,43 +174,40 @@ utmp_chk(char *user, char *tty) void search_utmp(char *user, char *tty, char *mytty, uid_t myuid) { - struct utmp u; + struct utmpx *u; time_t bestatime, atime; - int ufd, nloggedttys, nttys, msgsok, user_is_me; - char atty[UT_LINESIZE + 1]; - - if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) - err(1, "utmp"); + int nloggedttys, nttys, msgsok, user_is_me; nloggedttys = nttys = 0; bestatime = 0; user_is_me = 0; - while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) - if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { + + setutxent(); + while ((u = getutxent()) != NULL) + if (u->ut_type == USER_PROCESS && + strcmp(user, u->ut_user) == 0) { ++nloggedttys; - (void)strncpy(atty, u.ut_line, UT_LINESIZE); - atty[UT_LINESIZE] = '\0'; - if (term_chk(atty, &msgsok, &atime, 0)) + if (term_chk(u->ut_line, &msgsok, &atime, 0)) continue; /* bad term? skip */ if (myuid && !msgsok) continue; /* skip ttys with msgs off */ - if (strcmp(atty, mytty) == 0) { + if (strcmp(u->ut_line, mytty) == 0) { user_is_me = 1; continue; /* don't write to yourself */ } ++nttys; if (atime > bestatime) { bestatime = atime; - (void)strcpy(tty, atty); + (void)strlcpy(tty, u->ut_line, MAXPATHLEN); } } + endutxent(); - (void)close(ufd); if (nloggedttys == 0) errx(1, "%s is not logged in", user); if (nttys == 0) { if (user_is_me) { /* ok, so write to yourself! */ - (void)strcpy(tty, mytty); + (void)strlcpy(tty, mytty, MAXPATHLEN); return; } errx(1, "%s has messages disabled", user); diff --git a/usr.bin/wtmpcvt/Makefile b/usr.bin/wtmpcvt/Makefile new file mode 100644 index 0000000..0caa097 --- /dev/null +++ b/usr.bin/wtmpcvt/Makefile @@ -0,0 +1,5 @@ +# $FreeBSD$ + +PROG= wtmpcvt + +.include <bsd.prog.mk> diff --git a/usr.bin/wtmpcvt/wtmpcvt.1 b/usr.bin/wtmpcvt/wtmpcvt.1 new file mode 100644 index 0000000..fdc995b --- /dev/null +++ b/usr.bin/wtmpcvt/wtmpcvt.1 @@ -0,0 +1,66 @@ +.\" Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> +.\" 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. +.\" +.\" $FreeBSD$ +.\" +.Dd January 14, 2010 +.Os +.Dt WTMPCVT 1 +.Sh NAME +.Nm wtmpcvt +.Nd convert wtmp files to the utmpx format +.Sh SYNOPSIS +.Nm +.Ar input +.Ar output +.Sh DESCRIPTION +The +.Nm +utility converts traditional +.Pa wtmp +user accounting database files to the same format that is used by +.Pa /var/log/utx.log . +This makes it possible to view their contents using existing accounting +utilities, such as +.Xr last 1 +and +.Xr ac 8 . +.Sh SEE ALSO +.Xr last 1 , +.Xr getutxent 3 , +.Xr ac 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 9.0 . +.Sh BUGS +The application assumes the +.Ar input +file has the same byte order as the host system. +The +.Ar output +file can be used on any architecture. +.Sh AUTHORS +.An Ed Schouten Aq ed@FreeBSD.org diff --git a/usr.bin/wtmpcvt/wtmpcvt.c b/usr.bin/wtmpcvt/wtmpcvt.c new file mode 100644 index 0000000..aa4e25e --- /dev/null +++ b/usr.bin/wtmpcvt/wtmpcvt.c @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2010 Ed Schouten <ed@FreeBSD.org> + * 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. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/endian.h> +#include <sys/param.h> + +#include <err.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> +#include <utmpx.h> + +#include "../../lib/libc/gen/utxdb.h" + +struct outmp { + char ut_line[8]; + char ut_user[16]; + char ut_host[16]; + int32_t ut_time; +}; + +static void +usage(void) +{ + + fprintf(stderr, "usage: wtmpcvt input output\n"); + exit(1); +} + +static void +outmp_to_futx(const struct outmp *ui, struct futx *uo) +{ + + memset(uo, 0, sizeof *uo); +#define COPY_STRING(field) do { \ + strncpy(uo->fu_ ## field, ui->ut_ ## field, \ + MIN(sizeof uo->fu_ ## field, sizeof ui->ut_ ## field)); \ +} while (0) +#define COPY_LINE_TO_ID() do { \ + strncpy(uo->fu_id, ui->ut_line, \ + MIN(sizeof uo->fu_id, sizeof ui->ut_line)); \ +} while (0) +#define MATCH(field, value) (strncmp(ui->ut_ ## field, (value), \ + sizeof(ui->ut_ ## field)) == 0) + if (MATCH(user, "reboot") && MATCH(line, "~")) + uo->fu_type = BOOT_TIME; + else if (MATCH(user, "date") && MATCH(line, "|")) + uo->fu_type = OLD_TIME; + else if (MATCH(user, "date") && MATCH(line, "{")) + uo->fu_type = NEW_TIME; + else if (MATCH(user, "shutdown") && MATCH(line, "~")) + uo->fu_type = SHUTDOWN_TIME; + else if (MATCH(user, "") && MATCH(host, "") && !MATCH(line, "")) { + uo->fu_type = DEAD_PROCESS; + COPY_LINE_TO_ID(); + } else if (!MATCH(user, "") && !MATCH(line, "") && ui->ut_time != 0) { + uo->fu_type = USER_PROCESS; + COPY_STRING(user); + COPY_STRING(line); + COPY_STRING(host); + COPY_LINE_TO_ID(); + } else { + uo->fu_type = EMPTY; + return; + } +#undef COPY_STRING +#undef COPY_LINE_TO_ID +#undef MATCH + + /* Timestamp conversion. XXX: Assumes host byte order! */ + uo->fu_tv = htobe64((uint64_t)ui->ut_time * 1000000); +} + +int +main(int argc, char *argv[]) +{ + FILE *in, *out; + struct outmp ui; + struct futx uo; + size_t l; + uint16_t lo; + + if (argc != 3) + usage(); + + /* Open files. */ + in = fopen(argv[1], "r"); + if (in == NULL) + err(1, argv[1]); + out = fopen(argv[2], "w"); + if (out == NULL) + err(1, argv[2]); + + /* Process entries. */ + while (fread(&ui, sizeof ui, 1, in) == 1) { + outmp_to_futx(&ui, &uo); + if (uo.fu_type == EMPTY) + continue; + + /* Write new entry to output file. */ + for (l = sizeof uo; l > 0 && + ((const char *)&uo)[l - 1] == '\0'; l--); + lo = htobe16(l); + fwrite(&lo, sizeof lo, 1, out); + fwrite(&uo, l, 1, out); + } + + fclose(in); + fclose(out); + return (0); +} diff --git a/usr.bin/xargs/Makefile b/usr.bin/xargs/Makefile index 16162a1..642e953 100644 --- a/usr.bin/xargs/Makefile +++ b/usr.bin/xargs/Makefile @@ -3,6 +3,5 @@ PROG= xargs SRCS= xargs.c strnsubst.c -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/xinstall/Makefile b/usr.bin/xinstall/Makefile index 594f1dc..e6ff88e 100644 --- a/usr.bin/xinstall/Makefile +++ b/usr.bin/xinstall/Makefile @@ -4,6 +4,5 @@ PROG= xinstall PROGNAME= install MAN= install.1 -WARNS?= 6 .include <bsd.prog.mk> diff --git a/usr.bin/xinstall/xinstall.c b/usr.bin/xinstall/xinstall.c index 49e47c6..449dea3 100644 --- a/usr.bin/xinstall/xinstall.c +++ b/usr.bin/xinstall/xinstall.c @@ -53,7 +53,6 @@ __FBSDID("$FreeBSD$"); #include <sys/time.h> #include <sys/wait.h> -#include <ctype.h> #include <err.h> #include <errno.h> #include <fcntl.h> @@ -86,16 +85,16 @@ int dobackup, docompare, dodir, dopreserve, dostrip, nommap, safecopy, verbose; mode_t mode = S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH; const char *suffix = BACKUP_SUFFIX; -void copy(int, const char *, int, const char *, off_t); -int compare(int, const char *, size_t, int, const char *, size_t); -int create_newfile(const char *, int, struct stat *); -int create_tempfile(const char *, char *, size_t); -void install(const char *, const char *, u_long, u_int); -void install_dir(char *); -u_long numeric_id(const char *, const char *); -void strip(const char *); -int trymmap(int); -void usage(void); +static int compare(int, const char *, size_t, int, const char *, size_t); +static void copy(int, const char *, int, const char *, off_t); +static int create_newfile(const char *, int, struct stat *); +static int create_tempfile(const char *, char *, size_t); +static void install(const char *, const char *, u_long, u_int); +static void install_dir(char *); +static u_long numeric_id(const char *, const char *); +static void strip(const char *); +static int trymmap(int); +static void usage(void); int main(int argc, char *argv[]) @@ -247,7 +246,7 @@ main(int argc, char *argv[]) /* NOTREACHED */ } -u_long +static u_long numeric_id(const char *name, const char *type) { u_long val; @@ -270,7 +269,7 @@ numeric_id(const char *name, const char *type) * install -- * build a path name and install the file */ -void +static void install(const char *from_name, const char *to_name, u_long fset, u_int flags) { struct stat from_sb, temp_sb, to_sb; @@ -524,7 +523,7 @@ install(const char *from_name, const char *to_name, u_long fset, u_int flags) * compare -- * compare two files; non-zero means files differ */ -int +static int compare(int from_fd, const char *from_name __unused, size_t from_len, int to_fd, const char *to_name __unused, size_t to_len) { @@ -588,7 +587,7 @@ compare(int from_fd, const char *from_name __unused, size_t from_len, * create_tempfile -- * create a temporary file based on path and open it */ -int +static int create_tempfile(const char *path, char *temp, size_t tsize) { char *p; @@ -608,7 +607,7 @@ create_tempfile(const char *path, char *temp, size_t tsize) * create_newfile -- * create a new file, overwriting an existing one if necessary */ -int +static int create_newfile(const char *path, int target, struct stat *sbp) { char backup[MAXPATHLEN]; @@ -651,7 +650,7 @@ create_newfile(const char *path, int target, struct stat *sbp) * copy -- * copy from one file to another */ -void +static void copy(int from_fd, const char *from_name, int to_fd, const char *to_name, off_t size) { @@ -704,7 +703,7 @@ copy(int from_fd, const char *from_name, int to_fd, const char *to_name, * strip -- * use strip(1) to strip the target file */ -void +static void strip(const char *to_name) { const char *stripbin; @@ -734,9 +733,9 @@ strip(const char *to_name) /* * install_dir -- - * build directory heirarchy + * build directory hierarchy */ -void +static void install_dir(char *path) { char *p; @@ -770,8 +769,8 @@ install_dir(char *path) * usage -- * print a usage message and die */ -void -usage() +static void +usage(void) { (void)fprintf(stderr, "usage: install [-bCcpSsv] [-B suffix] [-f flags] [-g group] [-m mode]\n" @@ -787,7 +786,7 @@ usage() * trymmap -- * return true (1) if mmap should be tried, false (0) if not. */ -int +static int trymmap(int fd) { /* diff --git a/usr.bin/xlint/lint1/makeman b/usr.bin/xlint/lint1/makeman index 9c25846..d5be8bf 100644 --- a/usr.bin/xlint/lint1/makeman +++ b/usr.bin/xlint/lint1/makeman @@ -15,13 +15,6 @@ # 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 NetBSD -# Foundation, Inc. and its contributors. -# 4. 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 diff --git a/usr.bin/xlint/xlint/xlint.c b/usr.bin/xlint/xlint/xlint.c index 82a118e..553ac19 100644 --- a/usr.bin/xlint/xlint/xlint.c +++ b/usr.bin/xlint/xlint/xlint.c @@ -135,7 +135,7 @@ static char *concat3(const char *, const char *, const char *); static void terminate(int) __attribute__((__noreturn__)); static const char *lbasename(const char *, int); static void appdef(char ***, const char *); -static void usage(void); +static void usage(void) __dead2; static void fname(const char *); static void runchild(const char *, char *const *, const char *, int); static void findlibs(char *const *); @@ -304,11 +304,12 @@ int main(int argc, char *argv[]) { int c; - char flgbuf[3], *tmp, *s; + char flgbuf[3], *s; + const char *tmp; size_t len; if ((tmp = getenv("TMPDIR")) == NULL || (len = strlen(tmp)) == 0) { - tmpdir = xstrdup(_PATH_TMP); + tmpdir = _PATH_TMP; } else { s = xmalloc(len + 2); (void)sprintf(s, "%s%s", tmp, tmp[len - 1] == '/' ? "" : "/"); @@ -555,9 +556,9 @@ main(int argc, char *argv[]) terminate(0); if (!oflag) { - if ((s = getenv("LIBDIR")) == NULL || strlen(s) == 0) - s = PATH_LINTLIB; - appcstrg(&libsrchpath, s); + if ((tmp = getenv("LIBDIR")) == NULL || strlen(tmp) == 0) + tmp = PATH_LINTLIB; + appcstrg(&libsrchpath, tmp); findlibs(libs); findlibs(deflibs); } @@ -620,7 +621,7 @@ fname(const char *name) return; } ofn = xmalloc(strlen(bn) + (bn == suff ? 4 : 2)); - len = bn == suff ? strlen(bn) : (suff - 1) - bn; + len = bn == suff ? strlen(bn) : (size_t)((suff - 1) - bn); (void)sprintf(ofn, "%.*s", (int)len, bn); (void)strcat(ofn, ".ln"); } else { diff --git a/usr.bin/yacc/Makefile b/usr.bin/yacc/Makefile index e8b1024..c2f31a7 100644 --- a/usr.bin/yacc/Makefile +++ b/usr.bin/yacc/Makefile @@ -9,4 +9,6 @@ MAN= yacc.1 yyfix.1 LINKS= ${BINDIR}/yacc ${BINDIR}/byacc MLINKS= yacc.1 byacc.1 +WARNS?= 2 + .include <bsd.prog.mk> diff --git a/usr.bin/yacc/defs.h b/usr.bin/yacc/defs.h index 5219e1b..772e912 100644 --- a/usr.bin/yacc/defs.h +++ b/usr.bin/yacc/defs.h @@ -220,7 +220,6 @@ extern char tflag; extern char vflag; extern const char *symbol_prefix; -extern char *myname; extern char *cptr; extern char *line; extern int lineno; diff --git a/usr.bin/yacc/skeleton.c b/usr.bin/yacc/skeleton.c index 8ff7c8a..434a7c2 100644 --- a/usr.bin/yacc/skeleton.c +++ b/usr.bin/yacc/skeleton.c @@ -140,7 +140,11 @@ const char *header[] = const char *body[] = { "/* allocate initial stack or double stack size, up to YYMAXDEPTH */", + "#if defined(__cplusplus) || __STDC__", + "static int yygrowstack(void)", + "#else", "static int yygrowstack()", + "#endif", "{", " int newsize, i;", " short *newss;", diff --git a/usr.bin/ypcat/Makefile b/usr.bin/ypcat/Makefile index 58ee690..782fdcc 100644 --- a/usr.bin/ypcat/Makefile +++ b/usr.bin/ypcat/Makefile @@ -3,4 +3,6 @@ PROG= ypcat +WARNS?= 3 + .include <bsd.prog.mk> diff --git a/usr.bin/ypmatch/Makefile b/usr.bin/ypmatch/Makefile index 22e6299..d4a5b95 100644 --- a/usr.bin/ypmatch/Makefile +++ b/usr.bin/ypmatch/Makefile @@ -3,4 +3,6 @@ PROG= ypmatch +WARNS?= 3 + .include <bsd.prog.mk> diff --git a/usr.bin/ypwhich/Makefile b/usr.bin/ypwhich/Makefile index f28f864..3f6928f 100644 --- a/usr.bin/ypwhich/Makefile +++ b/usr.bin/ypwhich/Makefile @@ -3,4 +3,6 @@ PROG= ypwhich +WARNS?= 2 + .include <bsd.prog.mk> |