diff options
Diffstat (limited to 'xc/util')
49 files changed, 11460 insertions, 0 deletions
diff --git a/xc/util/compress/Makefile b/xc/util/compress/Makefile new file mode 100644 index 000000000..8fd79e577 --- /dev/null +++ b/xc/util/compress/Makefile @@ -0,0 +1,66 @@ +# +# Copyright (c) 1987 The Regents of the University of California. +# All rights reserved. +# +# Redistribution and use in source and binary forms are permitted +# provided that the above copyright notice and this paragraph are +# duplicated in all such forms and that any documentation, +# advertising materials, and other materials related to such +# distribution and use acknowledge that the software was developed +# by the University of California, Berkeley. The name of the +# University may not be used to endorse or promote products derived +# from this software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# @(#)Makefile 5.9 (Berkeley) 10/15/88 +# +CFLAGS= -O -DBSD4_2 -DSACREDMEM=256000 -DUSERMEM=`cat USERMEM` +LIBC= /lib/libc.a +SRCS= compress.c +OBJS= +MAN= compress.0 + +all: compress + +compress: USERMEM ${LIBC} + ${CC} ${CFLAGS} -o $@ $@.c + +#this one is for OS/2, change location of BINMODE +BINMODE=/emx/lib/binmode.o +compress.exe: compress.c + gcc -O -o compress.exe compress.c ${BINMODE} + +# USERMEM may have to be set by hand. It should contain the amount of +# available user memory in bytes. Set it to zero, for physical memory +# less than 1 Meg. +USERMEM: FRC + sh usermem.sh > USERMEM + +clean: + rm -f ${OBJS} core compress + +cleandir: clean + rm -f ${MAN} tags .depend + +depend: ${SRCS} + mkdep -p ${CFLAGS} ${SRCS} + +install: ${MAN} + install -s -o bin -g bin -m 755 compress ${DESTDIR}/usr/ucb/compress + rm -f ${DESTDIR}/usr/ucb/uncompress ${DESTDIR}/usr/ucb/zcat + ln ${DESTDIR}/usr/ucb/compress ${DESTDIR}/usr/ucb/uncompress + ln ${DESTDIR}/usr/ucb/compress ${DESTDIR}/usr/ucb/zcat + rm -f ${DESTDIR}/usr/man/cat1/uncompress.0 ${DESTDIR}/usr/man/cat1/zcat.0 + install -c -o bin -g bin -m 444 compress.0 ${DESTDIR}/usr/man/cat1 + ln ${DESTDIR}/usr/man/cat1/compress.0 ${DESTDIR}/usr/man/cat1/uncompress.0 + ln ${DESTDIR}/usr/man/cat1/compress.0 ${DESTDIR}/usr/man/cat1/zcat.0 + +lint: ${SRCS} + lint ${CFLAGS} ${SRCS} + +tags: ${SRCS} + ctags ${SRCS} + +FRC: diff --git a/xc/util/compress/README b/xc/util/compress/README new file mode 100644 index 000000000..c8375c85c --- /dev/null +++ b/xc/util/compress/README @@ -0,0 +1,283 @@ + + @(#)README 5.3 (Berkeley) 9/17/85 + +Compress version 4.0 improvements over 3.0: + o compress() speedup (10-50%) by changing division hash to xor + o decompress() speedup (5-10%) + o Memory requirements reduced (3-30%) + o Stack requirements reduced to less than 4kb + o Removed 'Big+Fast' compress code (FBITS) because of compress speedup + o Portability mods for Z8000 and PC/XT (but not zeus 3.2) + o Default to 'quiet' mode + o Unification of 'force' flags + o Manual page overhaul + o Portability enhancement for M_XENIX + o Removed text on #else and #endif + o Added "-V" switch to print version and options + o Added #defines for SIGNED_COMPARE_SLOW + o Added Makefile and "usermem" program + o Removed all floating point computations + o New programs: [deleted] + +The "usermem" script attempts to determine the maximum process size. Some +editing of the script may be necessary (see the comments). [It should work +fine on 4.3 bsd.] If you can't get it to work at all, just create file +"USERMEM" containing the maximum process size in decimal. + +The following preprocessor symbols control the compilation of "compress.c": + + o USERMEM Maximum process memory on the system + o SACREDMEM Amount to reserve for other proceses + o SIGNED_COMPARE_SLOW Unsigned compare instructions are faster + o NO_UCHAR Don't use "unsigned char" types + o BITS Overrules default set by USERMEM-SACREDMEM + o vax Generate inline assembler + o interdata Defines SIGNED_COMPARE_SLOW + o M_XENIX Makes arrays < 65536 bytes each + o pdp11 BITS=12, NO_UCHAR + o z8000 BITS=12 + o pcxt BITS=12 + o BSD4_2 Allow long filenames ( > 14 characters) & + Call setlinebuf(stderr) + +The difference "usermem-sacredmem" determines the maximum BITS that can be +specified with the "-b" flag. + +memory: at least BITS +------ -- ----- ---- + 433,484 16 + 229,600 15 + 127,536 14 + 73,464 13 + 0 12 + +The default is BITS=16. + +The maximum bits can be overrulled by specifying "-DBITS=bits" at +compilation time. + +WARNING: files compressed on a large machine with more bits than allowed by +a version of compress on a smaller machine cannot be decompressed! Use the +"-b12" flag to generate a file on a large machine that can be uncompressed +on a 16-bit machine. + +The output of compress 4.0 is fully compatible with that of compress 3.0. +In other words, the output of compress 4.0 may be fed into uncompress 3.0 or +the output of compress 3.0 may be fed into uncompress 4.0. + +The output of compress 4.0 not compatible with that of +compress 2.0. However, compress 4.0 still accepts the output of +compress 2.0. To generate output that is compatible with compress +2.0, use the undocumented "-C" flag. + + -from mod.sources, submitted by vax135!petsd!joe (Joe Orost), 8/1/85 +-------------------------------- + +Enclosed is compress version 3.0 with the following changes: + +1. "Block" compression is performed. After the BITS run out, the + compression ratio is checked every so often. If it is decreasing, + the table is cleared and a new set of substrings are generated. + + This makes the output of compress 3.0 not compatible with that of + compress 2.0. However, compress 3.0 still accepts the output of + compress 2.0. To generate output that is compatible with compress + 2.0, use the undocumented "-C" flag. + +2. A quiet "-q" flag has been added for use by the news system. + +3. The character chaining has been deleted and the program now uses + hashing. This improves the speed of the program, especially + during decompression. Other speed improvements have been made, + such as using putc() instead of fwrite(). + +4. A large table is used on large machines when a relatively small + number of bits is specified. This saves much time when compressing + for a 16-bit machine on a 32-bit virtual machine. Note that the + speed improvement only occurs when the input file is > 30000 + characters, and the -b BITS is less than or equal to the cutoff + described below. + +Most of these changes were made by James A. Woods (ames!jaw). Thank you +James! + +To compile compress: + + cc -O -DUSERMEM=usermem -o compress compress.c + +Where "usermem" is the amount of physical user memory available (in bytes). +If any physical memory is to be reserved for other processes, put in +"-DSACREDMEM sacredmem", where "sacredmem" is the amount to be reserved. + +The difference "usermem-sacredmem" determines the maximum BITS that can be +specified, and the cutoff bits where the large+fast table is used. + +memory: at least BITS cutoff +------ -- ----- ---- ------ + 4,718,592 16 13 + 2,621,440 16 12 + 1,572,864 16 11 + 1,048,576 16 10 + 631,808 16 -- + 329,728 15 -- + 178,176 14 -- + 99,328 13 -- + 0 12 -- + +The default memory size is 750,000 which gives a maximum BITS=16 and no +large+fast table. + +The maximum bits can be overruled by specifying "-DBITS=bits" at +compilation time. + +If your machine doesn't support unsigned characters, define "NO_UCHAR" +when compiling. + +If your machine has "int" as 16-bits, define "SHORT_INT" when compiling. + +After compilation, move "compress" to a standard executable location, such +as /usr/local. Then: + cd /usr/local + ln compress uncompress + ln compress zcat + +On machines that have a fixed stack size (such as Perkin-Elmer), set the +stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). + +Next, install the manual (compress.l). + cp compress.l /usr/man/manl + cd /usr/man/manl + ln compress.l uncompress.l + ln compress.l zcat.l + + - or - + + cp compress.l /usr/man/man1/compress.1 + cd /usr/man/man1 + ln compress.1 uncompress.1 + ln compress.1 zcat.1 + + regards, + petsd!joe + +Here is a note from the net: + +>From hplabs!pesnta!amd!turtlevax!ken Sat Jan 5 03:35:20 1985 +Path: ames!hplabs!pesnta!amd!turtlevax!ken +From: ken@turtlevax.UUCP (Ken Turkowski) +Newsgroups: net.sources +Subject: Re: Compress release 3.0 : sample Makefile +Organization: CADLINC, Inc. @ Menlo Park, CA + +In the compress 3.0 source recently posted to mod.sources, there is a +#define variable which can be set for optimum performance on a machine +with a large amount of memory. A program (usermem) to calculate the +useable amount of physical user memory is enclosed, as well as a sample +4.2bsd Vax Makefile for compress. + +Here is the README file from the previous version of compress (2.0): + +>Enclosed is compress.c version 2.0 with the following bugs fixed: +> +>1. The packed files produced by compress are different on different +> machines and dependent on the vax sysgen option. +> The bug was in the different byte/bit ordering on the +> various machines. This has been fixed. +> +> This version is NOT compatible with the original vax posting +> unless the '-DCOMPATIBLE' option is specified to the C +> compiler. The original posting has a bug which I fixed, +> causing incompatible files. I recommend you NOT to use this +> option unless you already have a lot of packed files from +> the original posting by thomas. +>2. The exit status is not well defined (on some machines) causing the +> scripts to fail. +> The exit status is now 0,1 or 2 and is documented in +> compress.l. +>3. The function getopt() is not available in all C libraries. +> The function getopt() is no longer referenced by the +> program. +>4. Error status is not being checked on the fwrite() and fflush() calls. +> Fixed. +> +>The following enhancements have been made: +> +>1. Added facilities of "compact" into the compress program. "Pack", +> "Unpack", and "Pcat" are no longer required (no longer supplied). +>2. Installed work around for C compiler bug with "-O". +>3. Added a magic number header (\037\235). Put the bits specified +> in the file. +>4. Added "-f" flag to force overwrite of output file. +>5. Added "-c" flag and "zcat" program. 'ln compress zcat' after you +> compile. +>6. The 'uncompress' script has been deleted; simply +> 'ln compress uncompress' after you compile and it will work. +>7. Removed extra bit masking for machines that support unsigned +> characters. If your machine doesn't support unsigned characters, +> define "NO_UCHAR" when compiling. +> +>Compile "compress.c" with "-O -o compress" flags. Move "compress" to a +>standard executable location, such as /usr/local. Then: +> cd /usr/local +> ln compress uncompress +> ln compress zcat +> +>On machines that have a fixed stack size (such as Perkin-Elmer), set the +>stack to at least 12kb. ("setstack compress 12" on Perkin-Elmer). +> +>Next, install the manual (compress.l). +> cp compress.l /usr/man/manl - or - +> cp compress.l /usr/man/man1/compress.1 +> +>Here is the README that I sent with my first posting: +> +>>Enclosed is a modified version of compress.c, along with scripts to make it +>>run identically to pack(1), unpack(1), an pcat(1). Here is what I +>>(petsd!joe) and a colleague (petsd!peora!srd) did: +>> +>>1. Removed VAX dependencies. +>>2. Changed the struct to separate arrays; saves mucho memory. +>>3. Did comparisons in unsigned, where possible. (Faster on Perkin-Elmer.) +>>4. Sorted the character next chain and changed the search to stop +>>prematurely. This saves a lot on the execution time when compressing. +>> +>>This version is totally compatible with the original version. Even though +>>lint(1) -p has no complaints about compress.c, it won't run on a 16-bit +>>machine, due to the size of the arrays. +>> +>>Here is the README file from the original author: +>> +>>>Well, with all this discussion about file compression (for news batching +>>>in particular) going around, I decided to implement the text compression +>>>algorithm described in the June Computer magazine. The author claimed +>>>blinding speed and good compression ratios. It's certainly faster than +>>>compact (but, then, what wouldn't be), but it's also the same speed as +>>>pack, and gets better compression than both of them. On 350K bytes of +>>>unix-wizards, compact took about 8 minutes of CPU, pack took about 80 +>>>seconds, and compress (herein) also took 80 seconds. But, compact and +>>>pack got about 30% compression, whereas compress got over 50%. So, I +>>>decided I had something, and that others might be interested, too. +>>> +>>>As is probably true of compact and pack (although I haven't checked), +>>>the byte order within a word is probably relevant here, but as long as +>>>you stay on a single machine type, you should be ok. (Can anybody +>>>elucidate on this?) There are a couple of asm's in the code (extv and +>>>insv instructions), so anyone porting it to another machine will have to +>>>deal with this anyway (and could probably make it compatible with Vax +>>>byte order at the same time). Anyway, I've linted the code (both with +>>>and without -p), so it should run elsewhere. Note the longs in the +>>>code, you can take these out if you reduce BITS to <= 15. +>>> +>>>Have fun, and as always, if you make good enhancements, or bug fixes, +>>>I'd like to see them. +>>> +>>>=Spencer (thomas@utah-20, {harpo,hplabs,arizona}!utah-cs!thomas) +>> +>> regards, +>> joe +>> +>>-- +>>Full-Name: Joseph M. Orost +>>UUCP: ..!{decvax,ucbvax,ihnp4}!vax135!petsd!joe +>>US Mail: MS 313; Perkin-Elmer; 106 Apple St; Tinton Falls, NJ 07724 +>>Phone: (201) 870-5844 diff --git a/xc/util/compress/USERMEM b/xc/util/compress/USERMEM new file mode 100644 index 000000000..79ee5ef74 --- /dev/null +++ b/xc/util/compress/USERMEM @@ -0,0 +1 @@ +6144000 diff --git a/xc/util/compress/compress.1 b/xc/util/compress/compress.1 new file mode 100644 index 000000000..572e078f9 --- /dev/null +++ b/xc/util/compress/compress.1 @@ -0,0 +1,252 @@ +.\" Copyright (c) 1986 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" James A. Woods, derived from original work by Spencer Thomas +.\" and Joseph Orost. +.\" +.\" Redistribution and use in source and binary forms are permitted +.\" provided that the above copyright notice and this paragraph are +.\" duplicated in all such forms and that any documentation, +.\" advertising materials, and other materials related to such +.\" distribution and use acknowledge that the software was developed +.\" by the University of California, Berkeley. The name of the +.\" University may not be used to endorse or promote products derived +.\" from this software without specific prior written permission. +.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +.\" WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +.\" +.\" @(#)compress.1 6.6 (Berkeley) 10/15/88 +.\" +.TH COMPRESS 1 "October 15, 1988" +.UC 6 +.SH NAME +compress, uncompress, zcat \- compress and expand data +.SH SYNOPSIS +.PU +.ll +8 +.B compress +[ +.B \-f +] [ +.B \-v +] [ +.B \-c +] [ +.B \-b +.I bits +] [ +.I "name \&..." +] +.ll -8 +.br +.B uncompress +[ +.B \-f +] [ +.B \-v +] [ +.B \-c +] [ +.I "name \&..." +] +.br +.B zcat +[ +.I "name \&..." +] +.SH DESCRIPTION +.I Compress +reduces the size of the named files using adaptive Lempel-Ziv coding. +Whenever possible, +each file is replaced by one with the extension +.B "\&.Z," +while keeping the same ownership modes, access and modification times. +If no files are specified, the standard input is compressed to the +standard output. +Compressed files can be restored to their original form using +.I uncompress +or +.I zcat. +.PP +The +.B \-f +option will force compression of +.IR name , +even if it does not actually shrink +or the corresponding +.IR name .Z +file already exists. +Except when run in the background under +.IR /bin/sh , +if +.B \-f +is not given the user is prompted as to whether an existing +.IR name .Z +file should be overwritten. +.PP +The +.B \-c +(``cat'') option makes +.I compress/uncompress +write to the standard output; no files are changed. +The nondestructive behavior of +.I zcat +is identical to that of +.I uncompress +.B \-c. +.PP +.I Compress +uses the modified Lempel-Ziv algorithm popularized in +"A Technique for High Performance Data Compression", +Terry A. Welch, +.I "IEEE Computer," +vol. 17, no. 6 (June 1984), pp. 8-19. +Common substrings in the file are first replaced by 9-bit codes 257 and up. +When code 512 is reached, the algorithm switches to 10-bit codes and +continues to use more bits until the +limit specified by the +.B \-b +flag is reached (default 16). +.I Bits +must be between 9 and 16. The default can be changed in the source to allow +.I compress +to be run on a smaller machine. +.PP +After the +.I bits +limit is attained, +.I compress +periodically checks the compression ratio. If it is increasing, +.I compress +continues to use the existing code dictionary. However, +if the compression ratio decreases, +.I compress +discards the table of substrings and rebuilds it from scratch. This allows +the algorithm to adapt to the next "block" of the file. +.PP +Note that the +.B \-b +flag is omitted for +.I uncompress, +since the +.I bits +parameter specified during compression +is encoded within the output, along with +a magic number to ensure that neither decompression of random data nor +recompression of compressed data is attempted. +.PP +.ne 8 +The amount of compression obtained depends on the size of the +input, the number of +.I bits +per code, and the distribution of common substrings. +Typically, text such as source code or English +is reduced by 50\-60%. +Compression is generally much better than that achieved by +Huffman coding (as used in +.IR pack ), +or adaptive Huffman coding +.RI ( compact ), +and takes less time to compute. +.PP +The +.B \-v +option causes +the printing of the percentage reduction of each file. +.PP +If an error occurs, exit status is 1, else +if the last file was not compressed because it became larger, the status +is 2; else the status is 0. +.SH "DIAGNOSTICS" +Usage: compress [\-fvc] [\-b maxbits] [file ...] +.in +8 +Invalid options were specified on the command line. +.in -8 +Missing maxbits +.in +8 +Maxbits must follow +.BR \-b \. +.in -8 +.IR file : +not in compressed format +.in +8 +The file specified to +.I uncompress +has not been compressed. +.in -8 +.IR file : +compressed with +.I xx +bits, can only handle +.I yy +bits +.in +8 +.I File +was compressed by a program that could deal with +more +.I bits +than the compress code on this machine. +Recompress the file with smaller +.IR bits \. +.in -8 +.IR file : +already has .Z suffix -- no change +.in +8 +The file is assumed to be already compressed. +Rename the file and try again. +.in -8 +.IR file : +filename too long to tack on .Z +.in +8 +The file cannot be compressed because its name is longer than +12 characters. +Rename and try again. +This message does not occur on BSD systems. +.in -8 +.I file +already exists; do you wish to overwrite (y or n)? +.in +8 +Respond "y" if you want the output file to be replaced; "n" if not. +.in -8 +uncompress: corrupt input +.in +8 +A SIGSEGV violation was detected which usually means that the input file is +corrupted. +.in -8 +Compression: +.I "xx.xx%" +.in +8 +Percentage of the input saved by compression. +(Relevant only for +.BR \-v \.) +.in -8 +-- not a regular file: unchanged +.in +8 +When the input file is not a regular file, +(e.g. a directory), it is +left unaltered. +.in -8 +-- has +.I xx +other links: unchanged +.in +8 +The input file has links; it is left unchanged. See +.IR ln "(1)" +for more information. +.in -8 +-- file unchanged +.in +8 +No savings is achieved by +compression. The input remains virgin. +.in -8 +.SH "BUGS" +Although compressed files are compatible between machines with large memory, +.BR \-b \12 +should be used for file transfer to architectures with +a small process data space (64KB or less, as exhibited by the DEC PDP +series, the Intel 80286, etc.) +.PP +.I compress +should be more flexible about the existence of the `.Z' suffix. diff --git a/xc/util/compress/compress.c b/xc/util/compress/compress.c new file mode 100644 index 000000000..1d4fb1ed3 --- /dev/null +++ b/xc/util/compress/compress.c @@ -0,0 +1,1550 @@ +/* + * Copyright (c) 1985, 1986 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods, derived from original work by Spencer Thomas + * and Joseph Orost. + * + * Redistribution and use in source and binary forms are permitted + * provided that the above copyright notice and this paragraph are + * duplicated in all such forms and that any documentation, + * advertising materials, and other materials related to such + * distribution and use acknowledge that the software was developed + * by the University of California, Berkeley. The name of the + * University may not be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1985, 1986 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)compress.c 5.10 (Berkeley) 10/15/88"; +#endif /* not lint */ + +/* + * Compress - data compression program + */ +#define min(a,b) ((a>b) ? b : a) + +/* + * machine variants which require cc -Dmachine: pdp11, z8000, pcxt + */ + +/* + * Set USERMEM to the maximum amount of physical user memory available + * in bytes. USERMEM is used to determine the maximum BITS that can be used + * for compression. + * + * SACREDMEM is the amount of physical memory saved for others; compress + * will hog the rest. + */ +#ifndef SACREDMEM +#define SACREDMEM 0 +#endif + +#ifndef USERMEM +# define USERMEM 450000 /* default user memory */ +#endif + +#ifdef interdata /* (Perkin-Elmer) */ +#define SIGNED_COMPARE_SLOW /* signed compare is slower than unsigned */ +#endif + +#ifdef pdp11 +# define BITS 12 /* max bits/code for 16-bit machine */ +# define NO_UCHAR /* also if "unsigned char" functions as signed char */ +# undef USERMEM +#endif /* pdp11 */ /* don't forget to compile with -i */ + +#ifdef z8000 +# define BITS 12 +# undef vax /* weird preprocessor */ +# undef USERMEM +#endif /* z8000 */ + +#ifdef pcxt +# define BITS 12 +# undef USERMEM +#endif /* pcxt */ + +#ifdef USERMEM +# if USERMEM >= (433484+SACREDMEM) +# define PBITS 16 +# else +# if USERMEM >= (229600+SACREDMEM) +# define PBITS 15 +# else +# if USERMEM >= (127536+SACREDMEM) +# define PBITS 14 +# else +# if USERMEM >= (73464+SACREDMEM) +# define PBITS 13 +# else +# define PBITS 12 +# endif +# endif +# endif +# endif +# undef USERMEM +#endif /* USERMEM */ + +#ifdef PBITS /* Preferred BITS for this memory size */ +# ifndef BITS +# define BITS PBITS +# endif /* BITS */ +#endif /* PBITS */ + +#if BITS == 16 +# define HSIZE 69001 /* 95% occupancy */ +#endif +#if BITS == 15 +# define HSIZE 35023 /* 94% occupancy */ +#endif +#if BITS == 14 +# define HSIZE 18013 /* 91% occupancy */ +#endif +#if BITS == 13 +# define HSIZE 9001 /* 91% occupancy */ +#endif +#if BITS <= 12 +# define HSIZE 5003 /* 80% occupancy */ +#endif + +#ifdef M_XENIX /* Stupid compiler can't handle arrays with */ +# if BITS == 16 /* more than 65535 bytes - so we fake it */ +# define XENIX_16 +# else +# if BITS > 13 /* Code only handles BITS = 12, 13, or 16 */ +# define BITS 13 +# endif +# endif +#endif + +/* + * a code_int must be able to hold 2**BITS values of type int, and also -1 + */ +#if BITS > 15 +typedef long int code_int; +#else +typedef int code_int; +#endif + +#ifdef SIGNED_COMPARE_SLOW +typedef unsigned long int count_int; +typedef unsigned short int count_short; +#else +typedef long int count_int; +#endif + +#ifdef NO_UCHAR + typedef char char_type; +#else + typedef unsigned char char_type; +#endif /* UCHAR */ +char_type magic_header[] = { "\037\235" }; /* 1F 9D */ + +/* Defines for third byte of header */ +#define BIT_MASK 0x1f +#define BLOCK_MASK 0x80 +/* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is + a fourth header byte (for expansion). +*/ +#define INIT_BITS 9 /* initial number of bits/code */ + +/* + * compress.c - File compression ala IEEE Computer, June 1984. + * + * Authors: Spencer W. Thomas (decvax!utah-cs!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * + * Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release + * Log: compress.c,v + * Revision 4.0 85/07/30 12:50:00 joe + * Removed ferror() calls in output routine on every output except first. + * Prepared for release to the world. + * + * Revision 3.6 85/07/04 01:22:21 joe + * Remove much wasted storage by overlaying hash table with the tables + * used by decompress: tab_suffix[1<<BITS], stack[8000]. Updated USERMEM + * computations. Fixed dump_tab() DEBUG routine. + * + * Revision 3.5 85/06/30 20:47:21 jaw + * Change hash function to use exclusive-or. Rip out hash cache. These + * speedups render the megamemory version defunct, for now. Make decoder + * stack global. Parts of the RCS trunks 2.7, 2.6, and 2.1 no longer apply. + * + * Revision 3.4 85/06/27 12:00:00 ken + * Get rid of all floating-point calculations by doing all compression ratio + * calculations in fixed point. + * + * Revision 3.3 85/06/24 21:53:24 joe + * Incorporate portability suggestion for M_XENIX. Got rid of text on #else + * and #endif lines. Cleaned up #ifdefs for vax and interdata. + * + * Revision 3.2 85/06/06 21:53:24 jaw + * Incorporate portability suggestions for Z8000, IBM PC/XT from mailing list. + * Default to "quiet" output (no compression statistics). + * + * Revision 3.1 85/05/12 18:56:13 jaw + * Integrate decompress() stack speedups (from early pointer mods by McKie). + * Repair multi-file USERMEM gaffe. Unify 'force' flags to mimic semantics + * of SVR2 'pack'. Streamline block-compress table clear logic. Increase + * output byte count by magic number size. + * + * Revision 3.0 84/11/27 11:50:00 petsd!joe + * Set HSIZE depending on BITS. Set BITS depending on USERMEM. Unrolled + * loops in clear routines. Added "-C" flag for 2.0 compatibility. Used + * unsigned compares on Perkin-Elmer. Fixed foreground check. + * + * Revision 2.7 84/11/16 19:35:39 ames!jaw + * Cache common hash codes based on input statistics; this improves + * performance for low-density raster images. Pass on #ifdef bundle + * from Turkowski. + * + * Revision 2.6 84/11/05 19:18:21 ames!jaw + * Vary size of hash tables to reduce time for small files. + * Tune PDP-11 hash function. + * + * Revision 2.5 84/10/30 20:15:14 ames!jaw + * Junk chaining; replace with the simpler (and, on the VAX, faster) + * double hashing, discussed within. Make block compression standard. + * + * Revision 2.4 84/10/16 11:11:11 ames!jaw + * Introduce adaptive reset for block compression, to boost the rate + * another several percent. (See mailing list notes.) + * + * Revision 2.3 84/09/22 22:00:00 petsd!joe + * Implemented "-B" block compress. Implemented REVERSE sorting of tab_next. + * Bug fix for last bits. Changed fwrite to putchar loop everywhere. + * + * Revision 2.2 84/09/18 14:12:21 ames!jaw + * Fold in news changes, small machine typedef from thomas, + * #ifdef interdata from joe. + * + * Revision 2.1 84/09/10 12:34:56 ames!jaw + * Configured fast table lookup for 32-bit machines. + * This cuts user time in half for b <= FBITS, and is useful for news batching + * from VAX to PDP sites. Also sped up decompress() [fwrite->putc] and + * added signal catcher [plus beef in writeerr()] to delete effluvia. + * + * Revision 2.0 84/08/28 22:00:00 petsd!joe + * Add check for foreground before prompting user. Insert maxbits into + * compressed file. Force file being uncompressed to end with ".Z". + * Added "-c" flag and "zcat". Prepared for release. + * + * Revision 1.10 84/08/24 18:28:00 turtlevax!ken + * Will only compress regular files (no directories), added a magic number + * header (plus an undocumented -n flag to handle old files without headers), + * added -f flag to force overwriting of possibly existing destination file, + * otherwise the user is prompted for a response. Will tack on a .Z to a + * filename if it doesn't have one when decompressing. Will only replace + * file if it was compressed. + * + * Revision 1.9 84/08/16 17:28:00 turtlevax!ken + * Removed scanargs(), getopt(), added .Z extension and unlimited number of + * filenames to compress. Flags may be clustered (-Ddvb12) or separated + * (-D -d -v -b 12), or combination thereof. Modes and other status is + * copied with copystat(). -O bug for 4.2 seems to have disappeared with + * 1.8. + * + * Revision 1.8 84/08/09 23:15:00 joe + * Made it compatible with vax version, installed jim's fixes/enhancements + * + * Revision 1.6 84/08/01 22:08:00 joe + * Sped up algorithm significantly by sorting the compress chain. + * + * Revision 1.5 84/07/13 13:11:00 srd + * Added C version of vax asm routines. Changed structure to arrays to + * save much memory. Do unsigned compares where possible (faster on + * Perkin-Elmer) + * + * Revision 1.4 84/07/05 03:11:11 thomas + * Clean up the code a little and lint it. (Lint complains about all + * the regs used in the asm, but I'm not going to "fix" this.) + * + * Revision 1.3 84/07/05 02:06:54 thomas + * Minor fixes. + * + * Revision 1.2 84/07/05 00:27:27 thomas + * Add variable bit length output. + * + */ +static char rcs_ident[] = "Header: compress.c,v 4.0 85/07/30 12:50:00 joe Release"; + +#include <stdio.h> +#include <ctype.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/stat.h> +#ifdef notdef +#include <sys/ioctl.h> +#endif + +#define ARGVAL() (*++(*argv) || (--argc && *++argv)) + +int n_bits; /* number of bits/code */ +int maxbits = BITS; /* user settable max # bits/code */ +code_int maxcode; /* maximum code, given n_bits */ +code_int maxmaxcode = 1 << BITS; /* should NEVER generate this code */ +#ifdef COMPATIBLE /* But wrong! */ +# define MAXCODE(n_bits) (1 << (n_bits) - 1) +#else +# define MAXCODE(n_bits) ((1 << (n_bits)) - 1) +#endif /* COMPATIBLE */ + +#ifdef XENIX_16 +count_int htab0[8192]; +count_int htab1[8192]; +count_int htab2[8192]; +count_int htab3[8192]; +count_int htab4[8192]; +count_int htab5[8192]; +count_int htab6[8192]; +count_int htab7[8192]; +count_int htab8[HSIZE-65536]; +count_int * htab[9] = { + htab0, htab1, htab2, htab3, htab4, htab5, htab6, htab7, htab8 }; + +#define htabof(i) (htab[(i) >> 13][(i) & 0x1fff]) +unsigned short code0tab[16384]; +unsigned short code1tab[16384]; +unsigned short code2tab[16384]; +unsigned short code3tab[16384]; +unsigned short code4tab[16384]; +unsigned short * codetab[5] = { + code0tab, code1tab, code2tab, code3tab, code4tab }; + +#define codetabof(i) (codetab[(i) >> 14][(i) & 0x3fff]) + +#else /* Normal machine */ + +#ifdef sel /* gould base register braindamage */ +/*NOBASE*/ +count_int htab [HSIZE]; +unsigned short codetab [HSIZE]; +/*NOBASE*/ +#else +count_int htab [HSIZE]; +unsigned short codetab [HSIZE]; +#endif /* sel */ + +#define htabof(i) htab[i] +#define codetabof(i) codetab[i] +#endif /* XENIX_16 */ +code_int hsize = HSIZE; /* for dynamic table sizing */ +count_int fsize; + +/* + * To save much memory, we overlay the table used by compress() with those + * used by decompress(). The tab_prefix table is the same size and type + * as the codetab. The tab_suffix table needs 2**BITS characters. We + * get this from the beginning of htab. The output stack uses the rest + * of htab, and contains characters. There is plenty of room for any + * possible stack (stack used to be 8000 characters). + */ + +#define tab_prefixof(i) codetabof(i) +#ifdef XENIX_16 +# define tab_suffixof(i) ((char_type *)htab[(i)>>15])[(i) & 0x7fff] +# define de_stack ((char_type *)(htab2)) +#else /* Normal machine */ +# define tab_suffixof(i) ((char_type *)(htab))[i] +# define de_stack ((char_type *)&tab_suffixof(1<<BITS)) +#endif /* XENIX_16 */ + +code_int free_ent = 0; /* first unused entry */ +int exit_stat = 0; /* per-file status */ +int perm_stat = 0; /* permanent status */ + +code_int getcode(); + +Usage() { +#ifdef DEBUG +fprintf(stderr,"Usage: compress [-dDVfc] [-b maxbits] [file ...]\n"); +} +int debug = 0; +#else +fprintf(stderr,"Usage: compress [-fvc] [-b maxbits] [file ...]\n"); +} +#endif /* DEBUG */ +int nomagic = 0; /* Use a 3-byte magic number header, unless old file */ +int zcat_flg = 0; /* Write output on stdout, suppress messages */ +int precious = 1; /* Don't unlink output file on interrupt */ +int quiet = 1; /* don't tell me about compression */ + +/* + * block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ +int block_compress = BLOCK_MASK; +int clear_flg = 0; +long int ratio = 0; +#define CHECK_GAP 10000 /* ratio check interval */ +count_int checkpoint = CHECK_GAP; +/* + * the next two codes should not be changed lightly, as they must not + * lie within the contiguous general code space. + */ +#define FIRST 257 /* first free entry */ +#define CLEAR 256 /* table clear output code */ + +int force = 0; +char ofname [100]; +#ifdef DEBUG +int verbose = 0; +#endif /* DEBUG */ +int (*oldint)(); +int bgnd_flag; + +int do_decomp = 0; + +/***************************************************************** + * TAG( main ) + * + * Algorithm from "A Technique for High Performance Data Compression", + * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19. + * + * Usage: compress [-dfvc] [-b bits] [file ...] + * Inputs: + * -d: If given, decompression is done instead. + * + * -c: Write output on stdout, don't remove original. + * + * -b: Parameter limits the max number of bits/code. + * + * -f: Forces output file to be generated, even if one already + * exists, and even if no space is saved by compressing. + * If -f is not used, the user will be prompted if stdin is + * a tty, otherwise, the output file will not be overwritten. + * + * -v: Write compression statistics + * + * file ...: Files to be compressed. If none specified, stdin + * is used. + * Outputs: + * file.Z: Compressed form of file with same mode, owner, and utimes + * or stdout (if stdin used as input) + * + * Assumptions: + * When filenames are given, replaces with the compressed version + * (.Z suffix) only if the file decreases in size. + * Algorithm: + * Modified Lempel-Ziv method (LZW). Basically finds common + * substrings and replaces them with a variable size code. This is + * deterministic, and can be done on the fly. Thus, the decompression + * procedure needs no input table, but tracks the way the table was built. + */ + +main( argc, argv ) +register int argc; char **argv; +{ + int overwrite = 0; /* Do not overwrite unless given -f flag */ + char tempname[100]; + char **filelist, **fileptr; + char *cp, *rindex(), *malloc(); + struct stat statbuf; + extern onintr(), oops(); + + /* This bg check only works for sh. */ + if ( (oldint = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) { + signal ( SIGINT, onintr ); + signal ( SIGSEGV, oops ); + } + bgnd_flag = oldint != SIG_DFL; +#ifdef notdef /* This works for csh but we don't want it. */ + { int tgrp; + if (bgnd_flag == 0 && ioctl(2, TIOCGPGRP, (char *)&tgrp) == 0 && + getpgrp(0) != tgrp) + bgnd_flag = 1; + } +#endif + +#ifdef COMPATIBLE + nomagic = 1; /* Original didn't have a magic number */ +#endif /* COMPATIBLE */ + + filelist = fileptr = (char **)(malloc(argc * sizeof(*argv))); + *filelist = NULL; + + if((cp = rindex(argv[0], '/')) != 0) { + cp++; + } else { + cp = argv[0]; + } + if(strcmp(cp, "uncompress") == 0) { + do_decomp = 1; + } else if(strcmp(cp, "zcat") == 0) { + do_decomp = 1; + zcat_flg = 1; + } + +#if defined(BSD4_2) && !defined(__EMX__) + /* 4.2BSD dependent - take it out if not */ + setlinebuf( stderr ); +#endif /* BSD4_2 */ + + /* Argument Processing + * All flags are optional. + * -D => debug + * -V => print Version; debug verbose + * -d => do_decomp + * -v => unquiet + * -f => force overwrite of output file + * -n => no header: useful to uncompress old files + * -b maxbits => maxbits. If -b is specified, then maxbits MUST be + * given also. + * -c => cat all output to stdout + * -C => generate output compatible with compress 2.0. + * if a string is left, must be an input filename. + */ + for (argc--, argv++; argc > 0; argc--, argv++) { + if (**argv == '-') { /* A flag argument */ + while (*++(*argv)) { /* Process all flags in this arg */ + switch (**argv) { +#ifdef DEBUG + case 'D': + debug = 1; + break; + case 'V': + verbose = 1; + version(); + break; +#else + case 'V': + version(); + break; +#endif /* DEBUG */ + case 'v': + quiet = 0; + break; + case 'd': + do_decomp = 1; + break; + case 'f': + case 'F': + overwrite = 1; + force = 1; + break; + case 'n': + nomagic = 1; + break; + case 'C': + block_compress = 0; + break; + case 'b': + if (!ARGVAL()) { + fprintf(stderr, "Missing maxbits\n"); + Usage(); + exit(1); + } + maxbits = atoi(*argv); + goto nextarg; + case 'c': + zcat_flg = 1; + break; + case 'q': + quiet = 1; + break; + default: + fprintf(stderr, "Unknown flag: '%c'; ", **argv); + Usage(); + exit(1); + } + } + } + else { /* Input file name */ + *fileptr++ = *argv; /* Build input file list */ + *fileptr = NULL; + /* process nextarg; */ + } + nextarg: continue; + } + + if(maxbits < INIT_BITS) maxbits = INIT_BITS; + if (maxbits > BITS) maxbits = BITS; + maxmaxcode = 1 << maxbits; + + if (*filelist != NULL) { + for (fileptr = filelist; *fileptr; fileptr++) { + exit_stat = 0; + if (do_decomp) { /* DECOMPRESSION */ + /* Check for .Z suffix */ + if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") != 0) { + /* No .Z: tack one on */ + strcpy(tempname, *fileptr); + strcat(tempname, ".Z"); + *fileptr = tempname; + } + /* Open input file */ + if ((freopen(*fileptr, "r", stdin)) == NULL) { + perror(*fileptr); + perm_stat = 1; + continue; + } + /* Check the magic number */ + if (nomagic == 0) { + if ((getchar() != (magic_header[0] & 0xFF)) + || (getchar() != (magic_header[1] & 0xFF))) { + fprintf(stderr, "%s: not in compressed format\n", + *fileptr); + continue; + } + maxbits = getchar(); /* set -b from file */ + block_compress = maxbits & BLOCK_MASK; + maxbits &= BIT_MASK; + maxmaxcode = 1 << maxbits; + if(maxbits > BITS) { + fprintf(stderr, + "%s: compressed with %d bits, can only handle %d bits\n", + *fileptr, maxbits, BITS); + continue; + } + } + /* Generate output filename */ + strcpy(ofname, *fileptr); + ofname[strlen(*fileptr) - 2] = '\0'; /* Strip off .Z */ + } else { /* COMPRESSION */ + if (strcmp(*fileptr + strlen(*fileptr) - 2, ".Z") == 0) { + fprintf(stderr, "%s: already has .Z suffix -- no change\n", + *fileptr); + continue; + } + /* Open input file */ + if ((freopen(*fileptr, "r", stdin)) == NULL) { + perror(*fileptr); + perm_stat = 1; + continue; + } + stat ( *fileptr, &statbuf ); + fsize = (long) statbuf.st_size; + /* + * tune hash table size for small files -- ad hoc, + * but the sizes match earlier #defines, which + * serve as upper bounds on the number of output codes. + */ + hsize = HSIZE; + if ( fsize < (1 << 12) ) + hsize = min ( 5003, HSIZE ); + else if ( fsize < (1 << 13) ) + hsize = min ( 9001, HSIZE ); + else if ( fsize < (1 << 14) ) + hsize = min ( 18013, HSIZE ); + else if ( fsize < (1 << 15) ) + hsize = min ( 35023, HSIZE ); + else if ( fsize < 47000 ) + hsize = min ( 50021, HSIZE ); + + /* Generate output filename */ + strcpy(ofname, *fileptr); +#ifndef BSD4_2 /* Short filenames */ + if ((cp=rindex(ofname,'/')) != NULL) cp++; + else cp = ofname; + if (strlen(cp) > 12) { + fprintf(stderr,"%s: filename too long to tack on .Z\n",cp); + continue; + } +#endif /* BSD4_2 Long filenames allowed */ + strcat(ofname, ".Z"); + } + /* Check for overwrite of existing file */ + if (overwrite == 0 && zcat_flg == 0) { + if (stat(ofname, &statbuf) == 0) { + char response[2]; + response[0] = 'n'; + fprintf(stderr, "%s already exists;", ofname); + if (bgnd_flag == 0 && isatty(2)) { + fprintf(stderr, " do you wish to overwrite %s (y or n)? ", + ofname); + fflush(stderr); + read(2, response, 2); + while (response[1] != '\n') { + if (read(2, response+1, 1) < 0) { /* Ack! */ + perror("stderr"); break; + } + } + } + if (response[0] != 'y') { + fprintf(stderr, "\tnot overwritten\n"); + continue; + } + } + } + if(zcat_flg == 0) { /* Open output file */ + if (freopen(ofname, "w", stdout) == NULL) { + perror(ofname); + perm_stat = 1; + continue; + } + precious = 0; + if(!quiet) + fprintf(stderr, "%s: ", *fileptr); + } + +#ifdef __EMX__ + /* force setting binary mode */ + _fsetmode(stdin,"b"); + _fsetmode(stdout,"b"); +#endif + + /* Actually do the compression/decompression */ + if (do_decomp == 0) compress(); +#ifndef DEBUG + else decompress(); +#else + else if (debug == 0) decompress(); + else printcodes(); + if (verbose) dump_tab(); +#endif /* DEBUG */ + if(zcat_flg == 0) { + copystat(*fileptr, ofname); /* Copy stats */ + precious = 1; + if((exit_stat == 1) || (!quiet)) + putc('\n', stderr); + } + } + } else { /* Standard input */ +#ifdef __EMX__ + /* force setting binary mode */ + _fsetmode(stdin,"b"); + _fsetmode(stdout,"b"); +#endif + if (do_decomp == 0) { + compress(); +#ifdef DEBUG + if(verbose) dump_tab(); +#endif /* DEBUG */ + if(!quiet) + putc('\n', stderr); + } else { + /* Check the magic number */ + if (nomagic == 0) { + if ((getchar()!=(magic_header[0] & 0xFF)) + || (getchar()!=(magic_header[1] & 0xFF))) { + fprintf(stderr, "stdin: not in compressed format\n"); + exit(1); + } + maxbits = getchar(); /* set -b from file */ + block_compress = maxbits & BLOCK_MASK; + maxbits &= BIT_MASK; + maxmaxcode = 1 << maxbits; + fsize = 100000; /* assume stdin large for USERMEM */ + if(maxbits > BITS) { + fprintf(stderr, + "stdin: compressed with %d bits, can only handle %d bits\n", + maxbits, BITS); + exit(1); + } + } +#ifndef DEBUG + decompress(); +#else + if (debug == 0) decompress(); + else printcodes(); + if (verbose) dump_tab(); +#endif /* DEBUG */ + } + } + exit(perm_stat ? perm_stat : exit_stat); +} + +static int offset; +long int in_count = 1; /* length of input */ +long int bytes_out; /* length of compressed output */ +long int out_count = 0; /* # of codes output (for debugging) */ + +/* + * compress stdin to stdout + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ + +compress() { + register long fcode; + register code_int i = 0; + register int c; + register code_int ent; +#ifdef XENIX_16 + register code_int disp; +#else /* Normal machine */ + register int disp; +#endif + register code_int hsize_reg; + register int hshift; + +#ifndef COMPATIBLE + if (nomagic == 0) { + putchar(magic_header[0]); putchar(magic_header[1]); + putchar((char)(maxbits | block_compress)); + if(ferror(stdout)) + writeerr(); + } +#endif /* COMPATIBLE */ + + offset = 0; + bytes_out = 3; /* includes 3-byte header mojo */ + out_count = 0; + clear_flg = 0; + ratio = 0; + in_count = 1; + checkpoint = CHECK_GAP; + maxcode = MAXCODE(n_bits = INIT_BITS); + free_ent = ((block_compress) ? FIRST : 256 ); + + ent = getchar (); + + hshift = 0; + for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L ) + hshift++; + hshift = 8 - hshift; /* set hash code range bound */ + + hsize_reg = hsize; + cl_hash( (count_int) hsize_reg); /* clear hash table */ + +#ifdef SIGNED_COMPARE_SLOW + while ( (c = getchar()) != (unsigned) EOF ) { +#else + while ( (c = getchar()) != EOF ) { +#endif + in_count++; + fcode = (long) (((long) c << maxbits) + ent); + i = ((c << hshift) ^ ent); /* xor hashing */ + + if ( htabof (i) == fcode ) { + ent = codetabof (i); + continue; + } else if ( (long)htabof (i) < 0 ) /* empty slot */ + goto nomatch; + disp = hsize_reg - i; /* secondary hash (after G. Knott) */ + if ( i == 0 ) + disp = 1; +probe: + if ( (i -= disp) < 0 ) + i += hsize_reg; + + if ( htabof (i) == fcode ) { + ent = codetabof (i); + continue; + } + if ( (long)htabof (i) > 0 ) + goto probe; +nomatch: + output ( (code_int) ent ); + out_count++; + ent = c; +#ifdef SIGNED_COMPARE_SLOW + if ( (unsigned) free_ent < (unsigned) maxmaxcode) { +#else + if ( free_ent < maxmaxcode ) { +#endif + codetabof (i) = free_ent++; /* code -> hashtable */ + htabof (i) = fcode; + } + else if ( (count_int)in_count >= checkpoint && block_compress ) + cl_block (); + } + /* + * Put out the final code. + */ + output( (code_int)ent ); + out_count++; + output( (code_int)-1 ); + + /* + * Print out stats on stderr + */ + if(zcat_flg == 0 && !quiet) { +#ifdef DEBUG + fprintf( stderr, + "%ld chars in, %ld codes (%ld bytes) out, compression factor: ", + in_count, out_count, bytes_out ); + prratio( stderr, in_count, bytes_out ); + fprintf( stderr, "\n"); + fprintf( stderr, "\tCompression as in compact: " ); + prratio( stderr, in_count-bytes_out, in_count ); + fprintf( stderr, "\n"); + fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n", + free_ent - 1, n_bits ); +#else /* !DEBUG */ + fprintf( stderr, "Compression: " ); + prratio( stderr, in_count-bytes_out, in_count ); +#endif /* DEBUG */ + } + if(bytes_out > in_count) /* exit(2) if no savings */ + exit_stat = 2; + return; +} + +/***************************************************************** + * TAG( output ) + * + * Output the given code. + * Inputs: + * code: A n_bits-bit integer. If == -1, then EOF. This assumes + * that n_bits =< (long)wordsize - 1. + * Outputs: + * Outputs code to the file. + * Assumptions: + * Chars are 8 bits long. + * Algorithm: + * Maintain a BITS character long buffer (so that 8 codes will + * fit in it exactly). Use the VAX insv instruction to insert each + * code in turn. When the buffer fills up empty it and start over. + */ + +static char buf[BITS]; + +#ifndef vax +char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00}; +char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; +#endif /* vax */ + +output( code ) +code_int code; +{ +#ifdef DEBUG + static int col = 0; +#endif /* DEBUG */ + + /* + * On the VAX, it is important to have the register declarations + * in exactly the order given, or the asm will break. + */ + register int r_off = offset, bits= n_bits; + register char * bp = buf; + +#ifdef DEBUG + if ( verbose ) + fprintf( stderr, "%5d%c", code, + (col+=6) >= 74 ? (col = 0, '\n') : ' ' ); +#endif /* DEBUG */ + if ( code >= 0 ) { +#ifdef vax + /* VAX DEPENDENT!! Implementation on other machines is below. + * + * Translation: Insert BITS bits from the argument starting at + * offset bits from the beginning of buf. + */ + 0; /* Work around for pcc -O bug with asm and if stmt */ + asm( "insv 4(ap),r11,r10,(r9)" ); +#else /* not a vax */ +/* + * byte/bit numbering on the VAX is simulated by the following code + */ + /* + * Get to the first byte. + */ + bp += (r_off >> 3); + r_off &= 7; + /* + * Since code is always >= 8 bits, only need to mask the first + * hunk on the left. + */ + *bp = (*bp & rmask[r_off]) | (code << r_off) & lmask[r_off]; + bp++; + bits -= (8 - r_off); + code >>= 8 - r_off; + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if ( bits >= 8 ) { + *bp++ = code; + code >>= 8; + bits -= 8; + } + /* Last bits. */ + if(bits) + *bp = code; +#endif /* vax */ + offset += n_bits; + if ( offset == (n_bits << 3) ) { + bp = buf; + bits = n_bits; + bytes_out += bits; + do + putchar(*bp++); + while(--bits); + offset = 0; + } + + /* + * If the next entry is going to be too big for the code size, + * then increase it, if possible. + */ + if ( free_ent > maxcode || (clear_flg > 0)) + { + /* + * Write the whole buffer, because the input side won't + * discover the size increase until after it has read it. + */ + if ( offset > 0 ) { + if( fwrite( buf, 1, n_bits, stdout ) != n_bits) + writeerr(); + bytes_out += n_bits; + } + offset = 0; + + if ( clear_flg ) { + maxcode = MAXCODE (n_bits = INIT_BITS); + clear_flg = 0; + } + else { + n_bits++; + if ( n_bits == maxbits ) + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } +#ifdef DEBUG + if ( debug ) { + fprintf( stderr, "\nChange to %d bits\n", n_bits ); + col = 0; + } +#endif /* DEBUG */ + } + } else { + /* + * At EOF, write the rest of the buffer. + */ + if ( offset > 0 ) + fwrite( buf, 1, (offset + 7) / 8, stdout ); + bytes_out += (offset + 7) / 8; + offset = 0; + fflush( stdout ); +#ifdef DEBUG + if ( verbose ) + fprintf( stderr, "\n" ); +#endif /* DEBUG */ + if( ferror( stdout ) ) + writeerr(); + } +} + +/* + * Decompress stdin to stdout. This routine adapts to the codes in the + * file building the "string" table on-the-fly; requiring no table to + * be stored in the compressed file. The tables used herein are shared + * with those of the compress() routine. See the definitions above. + */ + +decompress() { + register char_type *stackp; + register int finchar; + register code_int code, oldcode, incode; + + /* + * As above, initialize the first 256 entries in the table. + */ + maxcode = MAXCODE(n_bits = INIT_BITS); + for ( code = 255; code >= 0; code-- ) { + tab_prefixof(code) = 0; + tab_suffixof(code) = (char_type)code; + } + free_ent = ((block_compress) ? FIRST : 256 ); + + finchar = oldcode = getcode(); + if(oldcode == -1) /* EOF already? */ + return; /* Get out of here */ + putchar( (char)finchar ); /* first code must be 8 bits = char */ + if(ferror(stdout)) /* Crash if can't write */ + writeerr(); + stackp = de_stack; + + while ( (code = getcode()) > -1 ) { + + if ( (code == CLEAR) && block_compress ) { + for ( code = 255; code >= 0; code-- ) + tab_prefixof(code) = 0; + clear_flg = 1; + free_ent = FIRST - 1; + if ( (code = getcode ()) == -1 ) /* O, untimely death! */ + break; + } + incode = code; + /* + * Special case for KwKwK string. + */ + if ( code >= free_ent ) { + *stackp++ = finchar; + code = oldcode; + } + + /* + * Generate output characters in reverse order + */ +#ifdef SIGNED_COMPARE_SLOW + while ( ((unsigned long)code) >= ((unsigned long)256) ) { +#else + while ( code >= 256 ) { +#endif + *stackp++ = tab_suffixof(code); + code = tab_prefixof(code); + } + *stackp++ = finchar = tab_suffixof(code); + + /* + * And put them out in forward order + */ + do + putchar ( *--stackp ); + while ( stackp > de_stack ); + + /* + * Generate the new entry. + */ + if ( (code=free_ent) < maxmaxcode ) { + tab_prefixof(code) = (unsigned short)oldcode; + tab_suffixof(code) = finchar; + free_ent = code+1; + } + /* + * Remember previous code. + */ + oldcode = incode; + } + fflush( stdout ); + if(ferror(stdout)) + writeerr(); +} + +/***************************************************************** + * TAG( getcode ) + * + * Read one code from the standard input. If EOF, return -1. + * Inputs: + * stdin + * Outputs: + * code or -1 is returned. + */ + +code_int +getcode() { + /* + * On the VAX, it is important to have the register declarations + * in exactly the order given, or the asm will break. + */ + register code_int code; + static int offset = 0, size = 0; + static char_type buf[BITS]; + register int r_off, bits; + register char_type *bp = buf; + + if ( clear_flg > 0 || offset >= size || free_ent > maxcode ) { + /* + * If the next entry will be too big for the current code + * size, then we must increase the size. This implies reading + * a new buffer full, too. + */ + if ( free_ent > maxcode ) { + n_bits++; + if ( n_bits == maxbits ) + maxcode = maxmaxcode; /* won't get any bigger now */ + else + maxcode = MAXCODE(n_bits); + } + if ( clear_flg > 0) { + maxcode = MAXCODE (n_bits = INIT_BITS); + clear_flg = 0; + } + size = fread( buf, 1, n_bits, stdin ); + if ( size <= 0 ) + return -1; /* end of file */ + offset = 0; + /* Round size down to integral number of codes */ + size = (size << 3) - (n_bits - 1); + } + r_off = offset; + bits = n_bits; +#ifdef vax + asm( "extzv r10,r9,(r8),r11" ); +#else /* not a vax */ + /* + * Get to the first byte. + */ + bp += (r_off >> 3); + r_off &= 7; + /* Get first part (low order bits) */ +#ifdef NO_UCHAR + code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff; +#else + code = (*bp++ >> r_off); +#endif /* NO_UCHAR */ + bits -= (8 - r_off); + r_off = 8 - r_off; /* now, offset into code word */ + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if ( bits >= 8 ) { +#ifdef NO_UCHAR + code |= (*bp++ & 0xff) << r_off; +#else + code |= *bp++ << r_off; +#endif /* NO_UCHAR */ + r_off += 8; + bits -= 8; + } + /* high order bits. */ + code |= (*bp & rmask[bits]) << r_off; +#endif /* vax */ + offset += n_bits; + + return code; +} + +char * +rindex(s, c) /* For those who don't have it in libc.a */ +register char *s, c; +{ + char *p; + for (p = NULL; *s; s++) + if (*s == c) + p = s; + return(p); +} + +#ifdef DEBUG +printcodes() +{ + /* + * Just print out codes from input file. For debugging. + */ + code_int code; + int col = 0, bits; + + bits = n_bits = INIT_BITS; + maxcode = MAXCODE(n_bits); + free_ent = ((block_compress) ? FIRST : 256 ); + while ( ( code = getcode() ) >= 0 ) { + if ( (code == CLEAR) && block_compress ) { + free_ent = FIRST - 1; + clear_flg = 1; + } + else if ( free_ent < maxmaxcode ) + free_ent++; + if ( bits != n_bits ) { + fprintf(stderr, "\nChange to %d bits\n", n_bits ); + bits = n_bits; + col = 0; + } + fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' ); + } + putc( '\n', stderr ); + exit( 0 ); +} + +code_int sorttab[1<<BITS]; /* sorted pointers into htab */ + +dump_tab() /* dump string table */ +{ + register int i, first; + register ent; +#define STACK_SIZE 15000 + int stack_top = STACK_SIZE; + register c; + + if(do_decomp == 0) { /* compressing */ + register int flag = 1; + + for(i=0; i<hsize; i++) { /* build sort pointers */ + if((long)htabof(i) >= 0) { + sorttab[codetabof(i)] = i; + } + } + first = block_compress ? FIRST : 256; + for(i = first; i < free_ent; i++) { + fprintf(stderr, "%5d: \"", i); + de_stack[--stack_top] = '\n'; + de_stack[--stack_top] = '"'; + stack_top = in_stack((htabof(sorttab[i])>>maxbits)&0xff, + stack_top); + for(ent=htabof(sorttab[i]) & ((1<<maxbits)-1); + ent > 256; + ent=htabof(sorttab[ent]) & ((1<<maxbits)-1)) { + stack_top = in_stack(htabof(sorttab[ent]) >> maxbits, + stack_top); + } + stack_top = in_stack(ent, stack_top); + fwrite( &de_stack[stack_top], 1, STACK_SIZE-stack_top, stderr); + stack_top = STACK_SIZE; + } + } else if(!debug) { /* decompressing */ + + for ( i = 0; i < free_ent; i++ ) { + ent = i; + c = tab_suffixof(ent); + if ( isascii(c) && isprint(c) ) + fprintf( stderr, "%5d: %5d/'%c' \"", + ent, tab_prefixof(ent), c ); + else + fprintf( stderr, "%5d: %5d/\\%03o \"", + ent, tab_prefixof(ent), c ); + de_stack[--stack_top] = '\n'; + de_stack[--stack_top] = '"'; + for ( ; ent != NULL; + ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) { + stack_top = in_stack(tab_suffixof(ent), stack_top); + } + fwrite( &de_stack[stack_top], 1, STACK_SIZE - stack_top, stderr ); + stack_top = STACK_SIZE; + } + } +} + +int +in_stack(c, stack_top) + register c, stack_top; +{ + if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) { + de_stack[--stack_top] = c; + } else { + switch( c ) { + case '\n': de_stack[--stack_top] = 'n'; break; + case '\t': de_stack[--stack_top] = 't'; break; + case '\b': de_stack[--stack_top] = 'b'; break; + case '\f': de_stack[--stack_top] = 'f'; break; + case '\r': de_stack[--stack_top] = 'r'; break; + case '\\': de_stack[--stack_top] = '\\'; break; + default: + de_stack[--stack_top] = '0' + c % 8; + de_stack[--stack_top] = '0' + (c / 8) % 8; + de_stack[--stack_top] = '0' + c / 64; + break; + } + de_stack[--stack_top] = '\\'; + } + return stack_top; +} +#endif /* DEBUG */ + +writeerr() +{ + perror ( ofname ); + unlink ( ofname ); + exit ( 1 ); +} + +copystat(ifname, ofname) +char *ifname, *ofname; +{ + struct stat statbuf; + int mode; + time_t timep[2]; + + fclose(stdout); + if (stat(ifname, &statbuf)) { /* Get stat on input file */ + perror(ifname); + return; + } + if ((statbuf.st_mode & S_IFMT/*0170000*/) != S_IFREG/*0100000*/) { + if(quiet) + fprintf(stderr, "%s: ", ifname); + fprintf(stderr, " -- not a regular file: unchanged"); + exit_stat = 1; + perm_stat = 1; + } else if (statbuf.st_nlink > 1) { + if(quiet) + fprintf(stderr, "%s: ", ifname); + fprintf(stderr, " -- has %d other links: unchanged", + statbuf.st_nlink - 1); + exit_stat = 1; + perm_stat = 1; + } else if (exit_stat == 2 && (!force)) { /* No compression: remove file.Z */ + if(!quiet) + fprintf(stderr, " -- file unchanged"); + } else { /* ***** Successful Compression ***** */ + exit_stat = 0; + mode = statbuf.st_mode & 07777; + if (chmod(ofname, mode)) /* Copy modes */ + perror(ofname); +#ifndef __EMX__ + chown(ofname, statbuf.st_uid, statbuf.st_gid); /* Copy ownership */ +#endif + timep[0] = statbuf.st_atime; + timep[1] = statbuf.st_mtime; + utime(ofname, timep); /* Update last accessed and modified times */ +#ifdef __EMX__ + fclose(stdin); +#endif + if (unlink(ifname)) /* Remove input file */ + perror(ifname); + if(!quiet) + fprintf(stderr, " -- replaced with %s", ofname); + return; /* Successful return */ + } + + /* Unsuccessful return -- one of the tests failed */ + if (unlink(ofname)) + perror(ofname); +} + +onintr ( ) +{ + if (!precious) + unlink ( ofname ); + exit ( 1 ); +} + +oops ( ) /* wild pointer -- assume bad input */ +{ + if ( do_decomp ) + fprintf ( stderr, "uncompress: corrupt input\n" ); + unlink ( ofname ); + exit ( 1 ); +} + +cl_block () /* table clear for block compress */ +{ + register long int rat; + + checkpoint = in_count + CHECK_GAP; +#ifdef DEBUG + if ( debug ) { + fprintf ( stderr, "count: %ld, ratio: ", in_count ); + prratio ( stderr, in_count, bytes_out ); + fprintf ( stderr, "\n"); + } +#endif /* DEBUG */ + + if(in_count > 0x007fffff) { /* shift will overflow */ + rat = bytes_out >> 8; + if(rat == 0) { /* Don't divide by zero */ + rat = 0x7fffffff; + } else { + rat = in_count / rat; + } + } else { + rat = (in_count << 8) / bytes_out; /* 8 fractional bits */ + } + if ( rat > ratio ) { + ratio = rat; + } else { + ratio = 0; +#ifdef DEBUG + if(verbose) + dump_tab(); /* dump string table */ +#endif + cl_hash ( (count_int) hsize ); + free_ent = FIRST; + clear_flg = 1; + output ( (code_int) CLEAR ); +#ifdef DEBUG + if(debug) + fprintf ( stderr, "clear\n" ); +#endif /* DEBUG */ + } +} + +cl_hash(hsize) /* reset code table */ + register count_int hsize; +{ +#ifndef XENIX_16 /* Normal machine */ + register count_int *htab_p = htab+hsize; +#else + register j; + register long k = hsize; + register count_int *htab_p; +#endif + register long i; + register long m1 = -1; + +#ifdef XENIX_16 + for(j=0; j<=8 && k>=0; j++,k-=8192) { + i = 8192; + if(k < 8192) { + i = k; + } + htab_p = &(htab[j][i]); + i -= 16; + if(i > 0) { +#else + i = hsize - 16; +#endif + do { /* might use Sys V memset(3) here */ + *(htab_p-16) = m1; + *(htab_p-15) = m1; + *(htab_p-14) = m1; + *(htab_p-13) = m1; + *(htab_p-12) = m1; + *(htab_p-11) = m1; + *(htab_p-10) = m1; + *(htab_p-9) = m1; + *(htab_p-8) = m1; + *(htab_p-7) = m1; + *(htab_p-6) = m1; + *(htab_p-5) = m1; + *(htab_p-4) = m1; + *(htab_p-3) = m1; + *(htab_p-2) = m1; + *(htab_p-1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); +#ifdef XENIX_16 + } + } +#endif + for ( i += 16; i > 0; i-- ) + *--htab_p = m1; +} + +prratio(stream, num, den) +FILE *stream; +long int num, den; +{ + register int q; /* Doesn't need to be long */ + + if(num > 214748L) { /* 2147483647/10000 */ + q = num / (den / 10000L); + } else { + q = 10000L * num / den; /* Long calculations, though */ + } + if (q < 0) { + putc('-', stream); + q = -q; + } + fprintf(stream, "%d.%02d%%", q / 100, q % 100); +} + +version() +{ + fprintf(stderr, "%s, Berkeley 5.10 10/15/88\n", rcs_ident); + fprintf(stderr, "Options: "); +#ifdef vax + fprintf(stderr, "vax, "); +#endif +#ifdef NO_UCHAR + fprintf(stderr, "NO_UCHAR, "); +#endif +#ifdef SIGNED_COMPARE_SLOW + fprintf(stderr, "SIGNED_COMPARE_SLOW, "); +#endif +#ifdef XENIX_16 + fprintf(stderr, "XENIX_16, "); +#endif +#ifdef COMPATIBLE + fprintf(stderr, "COMPATIBLE, "); +#endif +#ifdef DEBUG + fprintf(stderr, "DEBUG, "); +#endif +#ifdef BSD4_2 + fprintf(stderr, "BSD4_2, "); +#endif + fprintf(stderr, "BITS = %d\n", BITS); +} diff --git a/xc/util/compress/usermem.sh b/xc/util/compress/usermem.sh new file mode 100644 index 000000000..e6e976325 --- /dev/null +++ b/xc/util/compress/usermem.sh @@ -0,0 +1,94 @@ +#!/bin/sh - +# +# Copyright (c) 1985 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# James A. Woods, derived from original work by Spencer Thomas +# and Joseph Orost. +# +# Redistribution and use in source and binary forms are permitted +# provided that the above copyright notice and this paragraph are +# duplicated in all such forms and that any documentation, +# advertising materials, and other materials related to such +# distribution and use acknowledge that the software was developed +# by the University of California, Berkeley. The name of the +# University may not be used to endorse or promote products derived +# from this software without specific prior written permission. +# THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED +# WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. +# +# @(#)usermem.sh 5.5 (Berkeley) 10/15/88 +# +: This shell script snoops around to find the maximum amount of available +: user memory. These variables need to be set only if there is no +: /usr/adm/messages. KMEM, UNIX, and CLICKSIZE can be set on the command +: line, if desired, e.g. UNIX=/unix +KMEM=/dev/kmem # User needs read access to KMEM +UNIX= +# VAX CLICKSIZE=512, UNIX=/vmunix +# PDP-11 CLICKSIZE=64, UNIX=/unix +# CADLINC 68000 CLICKSIZE=4096, UNIX=/unix +# Perkin-Elmer 3205 CLICKSIZE=4096, UNIX=/edition7 +# Perkin-Elmer all others, CLICKSIZE=2048, UNIX=/edition7 +CLICKSIZE=512 +eval $* + +if test -n "$UNIX" +then + : User must have specified it already. +elif test -r /vmunix +then + UNIX=/vmunix + CLICKSIZE=512 # Probably VAX +elif test -r /edition7 +then + UNIX=/edition7 + CLICKSIZE=2048 # Perkin-Elmer: change to 4096 on a 3205 +elif test -r /unix +then + UNIX=/unix # Could be anything +fi + +SIZE=0 +# messages: probably the most transportable +if test -r /usr/adm/messages -a -s /usr/adm/messages +then + SIZE=`grep avail /usr/adm/messages | sed -n '$s/.*[ ]//p'` +fi + +if test 0$SIZE -le 0 # no SIZE in /usr/adm/messages +then + if test -r $KMEM # Readable KMEM + then + if test -n "$UNIX" + then + SIZE=`echo maxmem/D | adb $UNIX $KMEM | sed -n '$s/.*[ ]//p'` + if test 0$SIZE -le 0 + then + SIZE=`echo physmem/D | adb $UNIX $KMEM | sed -n '$s/.*[ ]//p'` + fi + SIZE=`expr 0$SIZE '*' $CLICKSIZE` + fi + fi +fi + +case $UNIX in + /vmunix) # Assume 4.2bsd: check for resource limits + MAXSIZE=`csh -c limit | awk 'BEGIN { MAXSIZE = 1000000 } +/datasize|memoryuse/ && NF == 3 { if ($2 < MAXSIZE) MAXSIZE = $2 } +END { print MAXSIZE * 1000 }'` + if test $MAXSIZE -lt $SIZE + then + SIZE=$MAXSIZE + fi + ;; +esac + +if test 0$SIZE -le 0 +then + echo 0;exit 1 +else + echo $SIZE +fi diff --git a/xc/util/memleak/Imakefile b/xc/util/memleak/Imakefile new file mode 100644 index 000000000..cfab3781b --- /dev/null +++ b/xc/util/memleak/Imakefile @@ -0,0 +1,71 @@ +XCOMM $XConsortium: Imakefile /main/9 1996/09/28 17:19:05 rws $ +XCOMM $XFree86: xc/util/memleak/Imakefile,v 3.2 1996/12/23 07:19:34 dawes Exp $ +#define DoNormalLib YES +#define DoSharedLib NO +#define DoDebugLib NO +#define DoProfileLib NO +#define IncSubdir X11 +#include <Library.tmpl> + +#ifdef MipsArchitecture +#define TopOfStack 0x7fffbbb0 +#define BottomOfData 0x10000000 +#define HasGetReturnAddress YES +GRA_OBJS = getretmips.o mipsstack.o +GRA_SRCS = getretmips.c +#endif + +#ifdef SparcArchitecture +#define HasGetReturnAddress YES +#define BottomOfData \&environ +#ifdef SystemV4 +GRA_OBJS = getretspar.o sparcsolstack.o +#define TopOfStack 0xeffffc70 +#else +LOCAL_DEFS = -Datexit=on_exit +GRA_OBJS = getretspar.o sparcstack.o +#define TopOfStack 0xf7fffbdc +#endif +GRA_SRCS = getretspar.c +#endif + +#ifdef i386BsdArchitecture +#define HasGetReturnAddress YES +#define TopOfStack 'GC_get_stack_base()' +#define BottomOfData \&etext +GRA_OBJS = getreti386.o stackbottom.o +GRA_SRCS = getreti386.c stackbottom.c +#endif + +#ifndef HasGetReturnAddress +#define HasGetReturnAddress NO +#endif + +#if HasGetReturnAddress + GRA_DEFS = -DHAS_GET_RETURN_ADDRESS +#endif + +DEFINES = -DTOP_OF_STACK=TopOfStack -DBOTTOM_OF_DATA=BottomOfData\ + $(GRA_DEFS) $(LOCAL_DEFS) + +CDEBUGFLAGS = DebuggableCDebugFlags + +SRCS = fmalloc.c $(GRA_SRCS) + +OBJS = fmalloc.o $(GRA_OBJS) + +LibraryObjectRule() + +#if DoNormalLib +NormalLibraryTarget(memleak,$(OBJS)) +InstallLibrary(memleak,$(USRLIBDIR)) +#endif + +InstallNamedProg(find-rtns.sh,find-routines,$(BINDIR)) + +LintLibraryTarget(memleak,$(SRCS)) +InstallLintLibrary(memleak,$(LINTLIBDIR)) + +DependTarget() + +NormalLintTarget($(SRCS)) diff --git a/xc/util/memleak/README b/xc/util/memleak/README new file mode 100644 index 000000000..70bbaae6b --- /dev/null +++ b/xc/util/memleak/README @@ -0,0 +1,70 @@ +.\" $XConsortium: README,v 1.3 92/05/15 14:04:04 keith Exp $ + +This library replaces the C library allocator; +providing malloc, free, realloc and calloc (sorry, no valloc) + +In doing so, it provides extensive memory bug checking, locating: + + Lost memory; memory which has not been freed and which has no + references + + In use free memory; memorhy which has been freed and still has + references to it. + + Stores to freed memory + + free/realloc with invalid pointers -- if you pass in a pointer to + the middle of an allocated block, it will even tell you which one + +For each of these errors, a report entry is generated which includes +the stack backtrace of either the allocation or free (which ever occured +last) along with the current stack, when relevant. + +Unreferenced allocated memory, stores to freed memory and referenced freed +memory are only caught when CheckMemory is called. It is automatically +called each time 1m of data has been freed, and is called when the program +exits (by registering with atexit(3)/on_exit(3)). You can call it whenever +you want. + +Both the X server and font servers call CheckMemory after each reset when +their respective os/utils.c are compiled -DMEMBUG. + +There are a few global variables you can set with the debugger to +help isolate problems: + + FindLeakWarnMiddlePointers + Normally, memleak ignores pointers to the middle of + freed memory. These are frequently simply random data + which happens to look like a pointer. Turning this + on will generate additional messages. + FindLeakAllocBreakpoint + At each allocation, memleak increments a serial number + and stores it in the allocation header. By rerunning + the program, you can stop when that piece of memory + is going to be allocated. Store the serial number + in this global variable and put a debugger breakpoint inside + AddActiveBlock at the indicated line. + FindLeakFreeBreakpoint + Similarly for freeing memory. + FindLeakTime + The current serial number + FindLeakCheckAlways + When set, memleak checks the entire memory system after + each allocation or free. This is very expensive, but + may catch errors not otherwise found until too late. + +To include this in your application, simply place libmemleak.a before the +end of the link line; it will then override the C library allocator. + +To port this system to a new machine, you must provide two values, one +indicating the lowest data address in a program and one indicating the +highest stack address. In addition, to get return stack traces (which are +almost essential for debugging), you must provide the function +getReturnStack. Samples for MIPS and SPARC are included already. + +The output from the leak tracer includes only PC values in the stack +traces. To convert these into useful values, run the output of +the leak tracer through the find-routines script; after making sure you have +built the modified version of gdb-4.4 on your machine. + +-keith diff --git a/xc/util/memleak/find-rtns.sh b/xc/util/memleak/find-rtns.sh new file mode 100755 index 000000000..1d5ee62ee --- /dev/null +++ b/xc/util/memleak/find-rtns.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# $TOG: find-rtns.sh /main/2 1998/02/09 11:39:44 kaleb $ +# +# find-routines - convert leak tracer stack traces into file/lineno traces +# using a modified version of gdb-4.4 +# +# Usage: find-routines <program-name> {leak-tracing-output-files} +# +TMP1=find-routine.tmp1 +TMP=find-routine.tmp +trap "rm -f $TMP $TMP1" 0 +OBJ=$1 +shift +grep 'return stack:' $* | + tr ' ' '\012' | + grep 0x | sort -u | sed 's;^;x/i ;' | + gdb $OBJ | grep '>:' | + sed 's/>.*$/>/' | sed 's/(gdb) //' > $TMP1 + +awk '/^"/ { printf("s;%s;%s line %s %s;\n", $4, $1, $3, $5) } +/^0/ { printf("s;%s;%s %s;\n", $1, $2, $1);}' $TMP1 > $TMP + +awk '/return stack/ { printf ("return stack\n"); + for (i = 3; i <= NF; i++) + printf ("\troutine %s\n", $i); } + /^[A-Z]/ { print }' $* | + sed -f $TMP diff --git a/xc/util/memleak/fmalloc.c b/xc/util/memleak/fmalloc.c new file mode 100644 index 000000000..abad3452a --- /dev/null +++ b/xc/util/memleak/fmalloc.c @@ -0,0 +1,1135 @@ +/* + * $TOG: fmalloc.c /main/8 1998/02/09 11:39:50 kaleb $ + * +Copyright 1992, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + +/* $XFree86: xc/util/memleak/fmalloc.c,v 3.4 1998/10/04 08:19:36 dawes Exp $ */ + + +/* + * Leak tracing allocator -- using C lib malloc/free, tracks + * all allocations. When requested, performs a garbage-collection + * style mark/sweep on static memory (data and stack), locating + * objects referenced therein. Recursively marks objects. + * Sweeps through all allocations, warning of possible violations + * (unreferenced allocated, referenced freed etc). + */ + +#include <stdio.h> + +extern char **environ; +extern etext; + +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef TRUE +#define TRUE 1 +#endif + +#ifdef X_NOT_POSIX +#define NO_ATEXIT +#endif + +typedef unsigned long mem; + +#ifdef HAS_GET_RETURN_ADDRESS +#define MAX_RETURN_STACK 16 +#endif + +#define MAX_FREED_MEMORY (1*1024*1024) + +#define ACTIVE_HEAD_MAGIC 0xff1111ff +#define ACTIVE_TAIL_MAGIC 0xee2222ee +#define ACTIVE_DATA_MAGIC 0xdd3333dd +#define FREED_HEAD_MAGIC 0xcc4444cc +#define FREED_TAIL_MAGIC 0xbb5555bb +#define FREED_DATA_MAGIC 0xcc6666cc + +/* + * the marked fields in each head have two bits - one indicating + * references to the head of the block, and one indicating references + * to the middle of the block + */ +#define UNREFERENCED 0 +#define REFERENCED_HEAD 1 +#define REFERENCED_MIDDLE 2 + +typedef struct _head { + struct _head *left, *right; + struct _head *next; + int balance; +#ifdef HAS_GET_RETURN_ADDRESS + mem returnStack[MAX_RETURN_STACK]; +#endif + mem *from; + unsigned long allocTime; + unsigned long freeTime; + int size; + int desiredsize; + int actualSize; + int marked; + int headMagic; +} HeadRec, *HeadPtr; + +typedef struct _tail { + int tailMagic; +#if defined(__alpha) || defined(__alpha__) + int tailPad; +#endif +} TailRec, *TailPtr; + +#define Header(p) ((HeadPtr) (((char *) (p)) - sizeof (HeadRec))) +#define DataForHead(h) ((mem *) ((h) + 1)) +#define Tailer(p) ((TailPtr) (((char *) (p)) + Header(p)->size)) +#define TailForHead(h) (Tailer(DataForHead(h))) +#define RoundSize (sizeof (mem)) +#define RoundUp(s) (((s) + RoundSize - 1) & ~(RoundSize - 1)) +#define TotalSize(s) ((s) + sizeof (HeadRec) + sizeof (TailRec)) +#define CheckInit() if (!endOfStaticMemory) endOfStaticMemory = sbrk(0) +#define BlockContains(h,p) (DataForHead(h) <= (p) && (p) < (mem *) TailForHead(h)) + +typedef HeadRec tree; +typedef mem *tree_data; + +#define COMPARE_ADDR(a,b,op) (((unsigned) (a)) op ((unsigned) (b))) +#define COMPARE(a,b,op,s) ((!s) ? \ + COMPARE_ADDR(a,b,op) :\ + (((a)->actualSize op (b)->actualSize) || \ + ((a)->actualSize == (b)->actualSize && \ + COMPARE_ADDR(a,b,op)))) + + +#define LESS_THAN(a,b,s) COMPARE(a,b,<,s) +#define GREATER_THAN(a,b,s) COMPARE(a,b,>,s) + +#define SEARCH(top,result,p) for (result = top; result;) {\ + if ((mem *) (p) < DataForHead(result)) \ + result = result->left; \ + else if ((mem *) TailForHead(result) < (mem *) (p)) \ + result = result->right; \ + else \ + break; \ +} + + +static tree *activeMemory, *freedMemory, *deadMemory; + +static mem *endOfStaticMemory; +static mem *highestAllocatedMemory; + +static int freedMemoryTotal; +static int freedMemoryCount; +static int activeMemoryTotal; +static int activeMemoryCount; +static int deadMemoryTotal; + +int FindLeakWarnMiddlePointers = 0; +unsigned long FindLeakAllocBreakpoint = ~0; +unsigned long FindLeakFreeBreakpoint = ~0; +unsigned long FindLeakTime; +int FindLeakCheckAlways = 0; + +static void MarkActiveBlock (); +static int tree_insert (), tree_delete (); +void CheckMemory (); +char *malloc (), *realloc (), *calloc (); +void free (); +extern char *sbrk (); + +#ifdef HAS_GET_RETURN_ADDRESS +static void +PrintReturnStack (m, ra) + char *m; + mem *ra; +{ + int i; + + fprintf (stderr, " %s:", m); + for (i = 0; i < MAX_RETURN_STACK && ra[i]; i++) + fprintf (stderr, " 0x%lx", ra[i]); + fprintf (stderr, "\n"); +} +#endif + +static void +MemError (s, h, ourRet) + char *s; + HeadPtr h; + int ourRet; +{ + mem *ra; + int i; + + if (h) + { + fprintf (stderr, "%s 0x%08lx (size %d) (from 0x%lx)\n", + s, DataForHead(h), h->desiredsize, h->from); +#ifdef HAS_GET_RETURN_ADDRESS + PrintReturnStack ("Saved return stack", h->returnStack); +#endif + } + else + fprintf (stderr, "%s\n", s); +#ifdef HAS_GET_RETURN_ADDRESS + if (ourRet) + { + mem returnStack[MAX_RETURN_STACK]; + + getStackTrace (returnStack, MAX_RETURN_STACK); + PrintReturnStack ("Current return stack", returnStack); + } +#endif +} + +static void +MarkMemoryRegion (low, high) + mem *low, *high; +{ + mem **start = (mem **) low, **end = (mem **) high; + mem *p; + + while (start < end) { + p = *start; + if (endOfStaticMemory <= p && p < highestAllocatedMemory) + MarkActiveBlock (p, (mem *) start); + start++; + } +} + +static void +MarkActiveBlock (p, from) + mem *p, *from; +{ + HeadPtr h; + int marked; + int oldMarked; + + SEARCH(activeMemory, h, p) + if (h) { + marked = REFERENCED_HEAD; + if (p != DataForHead(h)) + marked = REFERENCED_MIDDLE; + oldMarked = h->marked; + if (!(oldMarked & marked)) + { + h->marked |= marked; + h->from = from; + if (!oldMarked) + MarkMemoryRegion (DataForHead(h), (mem *) TailForHead(h)); + } + return; + } + SEARCH(freedMemory, h, p) + if (h) + { + marked = REFERENCED_HEAD; + if (p != DataForHead(h)) + marked = REFERENCED_MIDDLE; + if (!(h->marked & marked)) + { + h->marked |= marked; + h->from = from; + } + return; + } +} + +static void +ClearTree (t) + tree *t; +{ + if (!t) + return; + ClearTree (t->left); + t->marked = 0; + t->from = 0; + ClearTree (t->right); +} + +static void +SweepActiveTree (t) + tree *t; +{ + if (!t) + return; + SweepActiveTree (t->left); + if (!t->marked) + MemError ("Unreferenced allocated", t, FALSE); + else if (!(t->marked & REFERENCED_HEAD)) + MemError ("Referenced allocated middle", t, FALSE); + SweepActiveTree (t->right); +} + +/* + * run a thread through the tree at the same time + * - the thread runs + * + * root -> left_child ... -> right_child ... -> null + */ + +static tree * +SweepFreedTree (t) + tree *t; +{ + tree *left_last, *right_last; + + if (!t) + return 0; + + left_last = SweepFreedTree (t->left); + if (t->marked) + { + if (t->marked & REFERENCED_HEAD) + MemError ("Referenced freed base", t, FALSE); + else if (FindLeakWarnMiddlePointers) + MemError ("Referenced freed middle", t, FALSE); + } + right_last = SweepFreedTree (t->right); + + if (t->left) + t->next = t->left; + else + t->next = t->right; + if (left_last) + left_last->next = t->right; + if (!right_last) + right_last = left_last; + if (!right_last) + right_last = t; + return right_last; +} + +static void +SweepFreedMemory () +{ + tree *t, *n; + int count, shouldCount; + + (void) SweepFreedTree (freedMemory); + count = 0; + shouldCount = freedMemoryCount; + for (t = freedMemory; t; t = n) { + n = t->next; + count++; + if (!t->marked) + { + (void) tree_delete (&freedMemory, t, FALSE); + freedMemoryTotal -= t->desiredsize; + freedMemoryCount--; + tree_insert (&deadMemory, t, TRUE); + } + } + if (count != shouldCount) + abort (); +} + +static void +ValidateTree (head, headMagic, tailMagic, bodyMagic, mesg) + tree *head; + mem headMagic, tailMagic, bodyMagic; + char *mesg; +{ + TailPtr tail; + mem *p; + int i; + + if (!head) + return; + ValidateTree (head->left, headMagic, tailMagic, bodyMagic, mesg); + tail = TailForHead (head); + if (head->headMagic != headMagic) + MemError (mesg, head, FALSE); + if (tail->tailMagic != tailMagic) + MemError (mesg, head, FALSE); + if (bodyMagic) { + i = head->size / sizeof (mem); + p = DataForHead(head); + while (i--) { + if (*p++ != bodyMagic) + MemError (mesg, head, FALSE); + } + } + ValidateTree (head->right, headMagic, tailMagic, bodyMagic, mesg); +} + +static void +ValidateActiveMemory () +{ + ValidateTree (activeMemory, ACTIVE_HEAD_MAGIC, ACTIVE_TAIL_MAGIC, + 0, "Store outside of active memory"); +} + +static void +ValidateFreedMemory () +{ + ValidateTree (freedMemory, FREED_HEAD_MAGIC, FREED_TAIL_MAGIC, + FREED_DATA_MAGIC, "Store into freed memory"); +} + +static void +AddActiveBlock (h) + HeadPtr h; +{ + TailPtr t = TailForHead(h); + mem *p; + int i; + + tree_insert (&activeMemory, h, FALSE); + if ((mem *) t > highestAllocatedMemory) + highestAllocatedMemory = (mem *) t; + + /* + * Breakpoint position - assign FindLeakAllocBreakpoint with + * debugger and set a breakpoint in the conditional clause below + */ + if (FindLeakTime == FindLeakAllocBreakpoint) + h->headMagic = ACTIVE_HEAD_MAGIC; /* set breakpoint here */ + + h->allocTime = FindLeakTime++; + + h->headMagic = ACTIVE_HEAD_MAGIC; + t->tailMagic = ACTIVE_TAIL_MAGIC; + i = h->size / sizeof (mem); + p = DataForHead(h); + while (i--) + *p++ = ACTIVE_DATA_MAGIC; + activeMemoryTotal += h->desiredsize; + activeMemoryCount++; +} + +static void +RemoveActiveBlock (h) + HeadPtr h; +{ + activeMemoryTotal -= h->desiredsize; + activeMemoryCount--; + tree_delete (&activeMemory, h, FALSE); +} + +static void +AddFreedBlock (h) + HeadPtr h; +{ + TailPtr t = TailForHead(h); + int i; + mem *p; + + tree_insert (&freedMemory, h, FALSE); + + /* + * Breakpoint position - assign FindLeakFreeBreakpoint with + * debugger and set a breakpoint in the conditional clause below + */ + if (FindLeakTime == FindLeakFreeBreakpoint) + h->headMagic = FREED_HEAD_MAGIC; /* set breakpoint here */ + + h->freeTime = FindLeakTime++; + + h->headMagic = FREED_HEAD_MAGIC; + t->tailMagic = FREED_TAIL_MAGIC; + i = h->size / sizeof (mem); + p = DataForHead(h); + while (i--) + *p++ = FREED_DATA_MAGIC; + freedMemoryTotal += h->desiredsize; + freedMemoryCount++; + /* GC if we've got piles of unused memory */ + if (freedMemoryTotal - deadMemoryTotal >= MAX_FREED_MEMORY) + CheckMemory (); +} + +/* + * Entry points: + * + * CheckMemory () -- Verifies heap + * malloc (size) -- Allocates memory + * free (old) -- Deallocates memory + * realloc (old, size) -- Allocate, copy, free + * calloc (num, size_per) -- Allocate and zero + */ + +void +CheckMemory () +{ + mem foo; + + fprintf (stderr, "\nCheckMemory\n"); + fprintf (stderr, "%d bytes active memory in %d allocations\n", + activeMemoryTotal, activeMemoryCount); + fprintf (stderr, "%d bytes freed memory held from %d allocations\n", + freedMemoryTotal, freedMemoryCount); + ValidateActiveMemory (); + ValidateFreedMemory (); + ClearTree (activeMemory); + ClearTree (freedMemory); + MarkMemoryRegion (BOTTOM_OF_DATA, endOfStaticMemory); + MarkMemoryRegion (&foo, TOP_OF_STACK); + SweepActiveTree (activeMemory); + SweepFreedMemory (); + fprintf (stderr, "%d bytes freed memory still held from %d allocations\n", + freedMemoryTotal, freedMemoryCount); + deadMemoryTotal = freedMemoryTotal; + fprintf (stderr, "CheckMemory done\n"); +} + +/* + * Allocator interface -- malloc and free (others in separate files) + */ + +#define CORE_CHUNK 16384 + +static char *core; +static unsigned core_left; +static unsigned total_core_used; + +static char * +morecore (size) + unsigned size; +{ + unsigned alloc_size; + char *alloc, *newcore; + + if (core_left < size) + { + alloc_size = (size + CORE_CHUNK - 1) & ~(CORE_CHUNK-1); + newcore = sbrk (alloc_size); + if (((int) newcore) == -1) + return 0; + core = newcore; + core_left = alloc_size; + total_core_used += alloc_size; + } + alloc = core; + core += size; + core_left -= size; + return alloc; +} + +char * +malloc (desiredsize) + unsigned desiredsize; +{ + char *ret; + unsigned size; + unsigned totalsize; + HeadPtr h; + + if (!endOfStaticMemory) + endOfStaticMemory = (mem *) sbrk(0); + if (FindLeakCheckAlways) + CheckMemory (); + size = RoundUp(desiredsize); + totalsize = TotalSize (size); + + h = deadMemory; + while (h) + { + if (h->actualSize == size) + break; + else if (h->actualSize < size) + h = h->right; + else { + if (!h->left) + break; + h = h->left; + } + } + if (h) + { + tree_delete (&deadMemory, h, TRUE); + } + else + { + h = (HeadPtr) morecore (totalsize); + if (!h) + return NULL; + h->actualSize = size; + } + h->desiredsize = desiredsize; + h->size = size; +#ifdef HAS_GET_RETURN_ADDRESS + getStackTrace (h->returnStack, MAX_RETURN_STACK); +#endif + AddActiveBlock (h); + return (char *) DataForHead(h); +} + +void +free (p) + char *p; +{ + HeadPtr h; + static int beenHere; + +#ifndef NO_ATEXIT + /* do it at free instead of malloc to avoid recursion? */ + if (!beenHere) + { + beenHere = TRUE; + atexit (CheckMemory); + } +#endif + if (!p) + { + MemError ("Freeing NULL", (HeadPtr) 0, TRUE); + return; + } + SEARCH (activeMemory, h, p); + if (!h) + { + SEARCH(freedMemory, h, p); + if (h) + MemError ("Freeing something twice", h, TRUE); + else + MemError ("Freeing something never allocated", h, TRUE); + return; + } + if (DataForHead(h) != (mem *) p) + { + MemError ("Freeing pointer to middle of allocated block", h, TRUE); + return; + } + if (h->headMagic != ACTIVE_HEAD_MAGIC || + TailForHead(h)->tailMagic != ACTIVE_TAIL_MAGIC) + MemError ("Freeing corrupted data", h, TRUE); + RemoveActiveBlock (h); +#ifdef HAS_GET_RETURN_ADDRESS + getStackTrace (h->returnStack, MAX_RETURN_STACK); +#endif + AddFreedBlock (h); + if (FindLeakCheckAlways) + CheckMemory (); +} + +char * +realloc (old, desiredsize) + char *old; + unsigned desiredsize; +{ + char *new; + HeadPtr h, fh; + int copysize; + + new = malloc (desiredsize); + if (!new) + return NULL; + SEARCH(activeMemory, h, old); + if (!h) + { + SEARCH(freedMemory, fh, old); + if (fh) + MemError ("Reallocing from freed data", fh, TRUE); + else + MemError ("Reallocing from something not allocated", h, TRUE); + } + else + { + if (DataForHead(h) != (mem *) old) + { + MemError ("Reallocing from pointer to middle of allocated block", h, TRUE); + } + else + { + if (h->headMagic != ACTIVE_HEAD_MAGIC || + TailForHead(h)->tailMagic != ACTIVE_TAIL_MAGIC) + MemError ("Reallocing corrupted data", h, TRUE); + copysize = desiredsize; + if (h->desiredsize < desiredsize) + copysize = h->desiredsize; +#ifdef SVR4 + memmove (new, old, copysize); +#else + bcopy (old, new, copysize); +#endif + RemoveActiveBlock (h); +#ifdef HAS_GET_RETURN_ADDRESS + getStackTrace (h->returnStack, MAX_RETURN_STACK); +#endif + AddFreedBlock (h); + } + } + return new; +} + +char * +calloc (num, size) + unsigned num, size; +{ + char *ret; + + size *= num; + ret = malloc (size); + if (!ret) + return NULL; +#ifdef SVR4 + memset (ret, 0, size); +#else + bzero (ret, size); +#endif + return ret; +} + +/* + * Semi-Balanced trees (avl). This only contains two + * routines - insert and delete. Searching is + * reserved for the client to write. + */ + +static rebalance_right (), rebalance_left (); + +/* + * insert a new node + * + * this routine returns non-zero if the tree has grown + * taller + */ + +static int +tree_insert (treep, new, bySize) +tree **treep; +tree *new; +int bySize; +{ + if (!(*treep)) { + (*treep) = new; + (*treep)->left = 0; + (*treep)->right = 0; + (*treep)->balance = 0; + return 1; + } else { + if (LESS_THAN (*treep, new, bySize)) { + if (tree_insert (&((*treep)->right), new, bySize)) + switch (++(*treep)->balance) { + case 0: + return 0; + case 1: + return 1; + case 2: + (void) rebalance_right (treep); + } + return 0; + } else if (GREATER_THAN(*treep, new, bySize)) { + if (tree_insert (&((*treep)->left), new, bySize)) + switch (--(*treep)->balance) { + case 0: + return 0; + case -1: + return 1; + case -2: + (void) rebalance_left (treep); + } + return 0; + } else { + return 0; + } + } + /*NOTREACHED*/ +} + +/* + * delete a node from a tree + * + * this routine return non-zero if the tree has been shortened + */ + +static int +tree_delete (treep, old, bySize) +tree **treep; +tree *old; +int bySize; +{ + tree *to_be_deleted; + tree *replacement; + tree *replacement_parent; + int replacement_direction; + int delete_direction; + tree *swap_temp; + int balance_temp; + + if (!*treep) + /* node not found */ + return 0; + if (LESS_THAN(*treep, old, bySize)) { + if (tree_delete (&(*treep)->right, old, bySize)) + /* + * check the balance factors + * Note that the conditions are + * inverted from the insertion case + */ + switch (--(*treep)->balance) { + case 0: + return 1; + case -1: + return 0; + case -2: + return rebalance_left (treep); + } + return 0; + } else if (GREATER_THAN(*treep, old, bySize)) { + if (tree_delete (&(*treep)->left, old, bySize)) + switch (++(*treep)->balance) { + case 0: + return 1; + case 1: + return 0; + case 2: + return rebalance_right (treep); + } + return 0; + } else { + to_be_deleted = *treep; + /* + * find an empty down pointer (if any) + * and rehook the tree + */ + if (!to_be_deleted->right) { + (*treep) = to_be_deleted->left; + return 1; + } else if (!to_be_deleted->left) { + (*treep) = to_be_deleted->right; + return 1; + } else { + /* + * if both down pointers are full, then + * move a node from the bottom of the tree up here. + * + * This builds an incorrect tree -- the replacement + * node and the to_be_deleted node will not + * be in correct order. This doesn't matter as + * the to_be_deleted node will obviously not leave + * this routine alive. + */ + /* + * if the tree is left heavy, then go left + * else go right + */ + replacement_parent = to_be_deleted; + if (to_be_deleted->balance == -1) { + delete_direction = -1; + replacement_direction = -1; + replacement = to_be_deleted->left; + while (replacement->right) { + replacement_parent = replacement; + replacement_direction = 1; + replacement = replacement->right; + } + } else { + delete_direction = 1; + replacement_direction = 1; + replacement = to_be_deleted->right; + while (replacement->left) { + replacement_parent = replacement; + replacement_direction = -1; + replacement = replacement->left; + } + } + /* + * swap the replacement node into + * the tree where the node is to be removed + * + * this would be faster if only the data + * element was swapped -- but that + * won't work for findleak. The alternate + * code would be: + data_temp = to_be_deleted->data; + to _be_deleted->data = replacement->data; + replacement->data = data_temp; + */ + swap_temp = to_be_deleted->left; + to_be_deleted->left = replacement->left; + replacement->left = swap_temp; + swap_temp = to_be_deleted->right; + to_be_deleted->right = replacement->right; + replacement->right = swap_temp; + balance_temp = to_be_deleted->balance; + to_be_deleted->balance = replacement->balance; + replacement->balance = balance_temp; + /* + * if the replacement node is directly below + * the to-be-removed node, hook the to_be_deleted + * node below it (instead of below itself!) + */ + if (replacement_parent == to_be_deleted) + replacement_parent = replacement; + if (replacement_direction == -1) + replacement_parent->left = to_be_deleted; + else + replacement_parent->right = to_be_deleted; + (*treep) = replacement; + /* + * delete the node from the sub-tree + */ + if (delete_direction == -1) { + if (tree_delete (&(*treep)->left, old, bySize)) { + switch (++(*treep)->balance) { + case 2: + abort (); + case 1: + return 0; + case 0: + return 1; + } + } + return 0; + } else { + if (tree_delete (&(*treep)->right, old, bySize)) { + switch (--(*treep)->balance) { + case -2: + abort (); + case -1: + return 0; + case 0: + return 1; + } + } + return 0; + } + } + } + /*NOTREACHED*/ +} + +/* + * two routines to rebalance the tree. + * + * rebalance_right -- the right sub-tree is too long + * rebalance_left -- the left sub-tree is too long + * + * These routines are the heart of avl trees, I've tried + * to make their operation reasonably clear with comments, + * but some study will be necessary to understand the + * algorithm. + * + * these routines return non-zero if the resultant + * tree is shorter than the un-balanced version. This + * is only of interest to the delete routine as the + * balance after insertion can never actually shorten + * the tree. + */ + +static +rebalance_right (treep) +tree **treep; +{ + tree *temp; + /* + * rebalance the tree + */ + if ((*treep)->right->balance == -1) { + /* + * double whammy -- the inner sub-sub tree + * is longer than the outer sub-sub tree + * + * this is the "double rotation" from + * knuth. Scheme: replace the tree top node + * with the inner sub-tree top node and + * adjust the maze of pointers and balance + * factors accordingly. + */ + temp = (*treep)->right->left; + (*treep)->right->left = temp->right; + temp->right = (*treep)->right; + switch (temp->balance) { + case -1: + temp->right->balance = 1; + (*treep)->balance = 0; + break; + case 0: + temp->right->balance = 0; + (*treep)->balance = 0; + break; + case 1: + temp->right->balance = 0; + (*treep)->balance = -1; + break; + } + temp->balance = 0; + (*treep)->right = temp->left; + temp->left = (*treep); + (*treep) = temp; + return 1; + } else { + /* + * a simple single rotation + * + * Scheme: replace the tree top node + * with the sub-tree top node + */ + temp = (*treep)->right->left; + (*treep)->right->left = (*treep); + (*treep) = (*treep)->right; + (*treep)->left->right = temp; + /* + * only two possible configurations -- + * if the right sub-tree was balanced, then + * *both* sides of it were longer than the + * left side, so the resultant tree will + * have a long leg (the left inner leg being + * the same length as the right leg) + */ + if ((*treep)->balance == 0) { + (*treep)->balance = -1; + (*treep)->left->balance = 1; + return 0; + } else { + (*treep)->balance = 0; + (*treep)->left->balance = 0; + return 1; + } + } +} + +static +rebalance_left (treep) +tree **treep; +{ + tree *temp; + /* + * rebalance the tree + */ + if ((*treep)->left->balance == 1) { + /* + * double whammy -- the inner sub-sub tree + * is longer than the outer sub-sub tree + * + * this is the "double rotation" from + * knuth. Scheme: replace the tree top node + * with the inner sub-tree top node and + * adjust the maze of pointers and balance + * factors accordingly. + */ + temp = (*treep)->left->right; + (*treep)->left->right = temp->left; + temp->left = (*treep)->left; + switch (temp->balance) { + case 1: + temp->left->balance = -1; + (*treep)->balance = 0; + break; + case 0: + temp->left->balance = 0; + (*treep)->balance = 0; + break; + case -1: + temp->left->balance = 0; + (*treep)->balance = 1; + break; + } + temp->balance = 0; + (*treep)->left = temp->right; + temp->right = (*treep); + (*treep) = temp; + return 1; + } else { + /* + * a simple single rotation + * + * Scheme: replace the tree top node + * with the sub-tree top node + */ + temp = (*treep)->left->right; + (*treep)->left->right = (*treep); + (*treep) = (*treep)->left; + (*treep)->right->left = temp; + /* + * only two possible configurations -- + * if the left sub-tree was balanced, then + * *both* sides of it were longer than the + * right side, so the resultant tree will + * have a long leg (the right inner leg being + * the same length as the left leg) + */ + if ((*treep)->balance == 0) { + (*treep)->balance = 1; + (*treep)->right->balance = -1; + return 0; + } else { + (*treep)->balance = 0; + (*treep)->right->balance = 0; + return 1; + } + } +} + +#ifdef DEBUG + +static +depth (treep) +tree *treep; +{ + int ldepth, rdepth; + + if (!treep) + return 0; + ldepth = depth (treep->left); + rdepth = depth (treep->right); + if (ldepth > rdepth) + return ldepth + 1; + return rdepth + 1; +} + +static tree * +left_most (treep) +tree *treep; +{ + while (treep && treep->left) + treep = treep->left; + return treep; +} + +static tree * +right_most (treep) +tree *treep; +{ + while (treep && treep->right) + treep = treep->right; + return treep; +} + +tree_verify (treep) +tree *treep; +{ + tree_data left_data, right_data; + + if (!treep) + return 1; + if (treep->left) + left_data = right_most (treep->left)->data; + else + left_data = treep->data - 1; + if (treep->right) + right_data = left_most (treep->right)->data; + else + right_data = treep->data + 1; + if (treep->data < left_data || treep->data > right_data) { + abort (); + return 0; + } + if (treep->balance != depth (treep->right) - depth (treep->left)) { + abort (); + return 0; + } + return tree_verify (treep->left) && tree_verify (treep->right); +} + +#endif diff --git a/xc/util/memleak/ftest.c b/xc/util/memleak/ftest.c new file mode 100644 index 000000000..13d3030d8 --- /dev/null +++ b/xc/util/memleak/ftest.c @@ -0,0 +1,49 @@ +/* + * $TOG: ftest.c /main/5 1998/02/09 11:39:56 kaleb $ + * +Copyright 1992, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + +static char *foo, *bar, *bletch; +static char *glorf[100]; + +extern char *malloc (); + +main () +{ + int i; + + foo = malloc (1000); + bar = malloc (2000); + bletch = malloc (3000); + for (i = 0; i < 100; i++) + glorf[i] = malloc (i * 200); + for (i = 0; i < 100; i++) { + free (glorf[i]); + glorf[i] = 0; + } + free (foo); + free (bletch); + bletch = 0; + *foo = 'a'; + bar = 0; + CheckMemory (); +} diff --git a/xc/util/memleak/getreti386.c b/xc/util/memleak/getreti386.c new file mode 100644 index 000000000..77ac82a05 --- /dev/null +++ b/xc/util/memleak/getreti386.c @@ -0,0 +1,36 @@ +/* + * some bits copied from mprof by Ben Zorn + * + * Copyright (c) 1995 Jeffrey Hsu + */ + +/* $XFree86: xc/util/memleak/getreti386.c,v 3.2 1996/10/16 14:46:28 dawes Exp $ */ + +#define get_current_fp(first_local) ((unsigned)&(first_local) + 4) +#define prev_fp_from_fp(fp) *((unsigned *) fp) +#define ret_addr_from_fp(fp) *((unsigned *) (fp+4)) + +#ifdef __FreeBSD__ +#define CRT0_ADDRESS 0x10d3 +#endif +#if defined(__NetBSD__) || defined(__OpenBSD__) +#define CRT0_ADDRESS 0x109a +#endif + +void +getStackTrace(unsigned long *results, int max) +{ + + int first_local; + unsigned long fp, ret_addr; + + fp = get_current_fp(first_local); + ret_addr = ret_addr_from_fp(fp); + + while (ret_addr > CRT0_ADDRESS && max--) { + *results++ = ret_addr; + fp = prev_fp_from_fp(fp); + ret_addr = ret_addr_from_fp(fp); + } + +} diff --git a/xc/util/memleak/getretmips.c b/xc/util/memleak/getretmips.c new file mode 100644 index 000000000..7a65fb887 --- /dev/null +++ b/xc/util/memleak/getretmips.c @@ -0,0 +1,191 @@ +/* + * $TOG: getretmips.c /main/5 1998/02/09 11:40:00 kaleb $ + * +Copyright 1992, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + +/* Return stack generation for MIPS processors + * This is tricky as MIPS stack frames aren't + * easily unrolled -- we look for pc restoration + * and stack adjustment instructions beyond the return + * address to discover the correct values + */ + +/* lw $31,const($sp) is : 100 011 11101 11111 const */ +/* 1000 1111 1011 1111 */ + +#define RESTORE_RETURNVAL 0x8fbf0000 +#define RESTORE_RETURNVAL_MASK 0xffff0000 + +/* addiu $sp, $sp, const is 001 001 11101 11101 const */ +/* 0010 0111 1011 1101 const */ + +#define ADJUST_STACKP_C 0x27bd0000 +#define ADJUST_STACKP_C_MASK 0xffff0000 + +/* addu $sp, $sp, $at is 000 000 11101 00001 11101 00000 100 001 */ +/* 0000 0011 1010 0001 1110 1000 0010 0001 */ + +#define ADJUST_STACKP_V 0x03a1e821 +#define ADJUST_STACKP_V_MASK 0xffffffff + +/* lui $at, const is 001 111 00000 00001 const */ +/* 0011 1100 0000 0001 const */ + +#define SET_UPPER_C 0x3c010000 +#define SET_UPPER_C_MASK 0xffff0000 + +/* ori $at, $at, const is 001 101 00001 00001 const */ +/* 0011 0100 0010 0001 const */ + +#define OR_LOWER_C 0x34210000 +#define OR_LOWER_C_MASK 0xffff0000 + +/* ori $at, $zero, const is 001 101 00000 00001 const */ +/* 0011 0100 0000 0001 const */ + +#define SET_LOWER_C 0x34010000 +#define SET_LOWER_C_MASK 0xffff0000 + +/* jr $ra */ +#define RETURN 0x03e00008 + +#define CALL(f) (0x0c000000 | (((int) (f)) >> 2)) + +/* + * This computation is expensive, so we cache the results; + * a simple hash function and straight-forward replacement. + */ + +#define HASH_SIZE 256 + +typedef struct _returnCache { + unsigned long *returnAddress; + long raOffset; + long spAdjust; +} ReturnCacheRec, *ReturnCachePtr; + +static ReturnCacheRec returnCache[HASH_SIZE]; + +#define HASH(ra) ((((int) (ra)) >> 2) & (HASH_SIZE - 1)) + +typedef int Bool; + +#define TRUE 1 +#define FALSE 0 + +getStackTrace (results, max) + unsigned long *results; + int max; +{ + extern unsigned long *getReturnAddress (), *getStackPointer (); + extern int main (); + unsigned long *ra, *ra_limit; + unsigned long *sp; + unsigned long inst; + unsigned long mainCall; + unsigned short const_upper; + unsigned short const_lower; + long ra_offset; + long sp_adjust; + Bool found_ra_offset, found_sp_adjust; + Bool found_const_upper, found_const_lower; + ReturnCachePtr rc; + + ra = getReturnAddress (); + sp = getStackPointer (); + mainCall = CALL(main); + while (ra && max) { + rc = &returnCache[HASH(ra)]; + if (rc->returnAddress != ra) + { + found_ra_offset = FALSE; + found_sp_adjust = FALSE; + found_const_upper = FALSE; + found_const_lower = FALSE; + const_upper = 0; + const_lower = 0; + rc->returnAddress = ra; + ra_limit = (unsigned long *) 0x10000000; + ra_offset = 0; + sp_adjust = -1; + while ((!found_ra_offset || !found_sp_adjust) && ra < ra_limit) + { + inst = *ra; + /* look for the offset of the PC in the stack frame */ + if ((inst & RESTORE_RETURNVAL_MASK) == RESTORE_RETURNVAL) + { + ra_offset = inst & ~RESTORE_RETURNVAL_MASK; + found_ra_offset = TRUE; + } + else if ((inst & ADJUST_STACKP_C_MASK) == ADJUST_STACKP_C) + { + sp_adjust = inst & ~ADJUST_STACKP_C_MASK; + found_sp_adjust = TRUE; + } + else if ((inst & ADJUST_STACKP_V_MASK) == ADJUST_STACKP_V) + { + sp_adjust = 0; + found_sp_adjust = TRUE; + } + else if ((inst & SET_UPPER_C_MASK) == SET_UPPER_C) + { + const_upper = inst & ~SET_UPPER_C_MASK; + const_lower = 0; + found_const_upper = TRUE; + } + else if ((inst & OR_LOWER_C_MASK) == OR_LOWER_C) + { + const_lower = inst & ~OR_LOWER_C_MASK; + found_const_lower = TRUE; + } + else if ((inst & SET_LOWER_C_MASK) == SET_LOWER_C) + { + const_lower = inst & ~SET_LOWER_C_MASK; + const_upper = 0; + found_const_lower = TRUE; + } + else if (inst == RETURN) + ra_limit = ra + 2; + ra++; + } + if (sp_adjust == 0 && (found_const_upper || found_const_lower)) + sp_adjust = (const_upper << 16) | const_lower; + rc->raOffset = ra_offset; + rc->spAdjust = sp_adjust; + } + /* if something went wrong, punt */ + if (rc->spAdjust <= 0) + { + *results++ = 0; + break; + } + ra = (unsigned long *) sp[rc->raOffset>>2]; + sp += rc->spAdjust >> 2; + *results++ = ((unsigned long) ra) - 8; + if (ra[-2] == mainCall) + { + *results++ = 0; + break; + } + max--; + } +} diff --git a/xc/util/memleak/getretspar.c b/xc/util/memleak/getretspar.c new file mode 100644 index 000000000..c72b1e394 --- /dev/null +++ b/xc/util/memleak/getretspar.c @@ -0,0 +1,62 @@ +/* + * $TOG: getretspar.c /main/3 1998/02/09 11:40:04 kaleb $ + * +Copyright 1992, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + +/* Trace up the stack and build a call history -- SPARC specific code */ + +/* hack -- flush out the register windows by recursing */ + +static void +flushWindows (depth) +{ + if (depth == 0) + return; + flushWindows (depth-1); +} + +getStackTrace (results, max) + unsigned long *results; + int max; +{ + unsigned long *sp, *getStackPointer (), *getFramePointer(); + unsigned long *ra, mainCall; + extern int main (); + + flushWindows (32); + sp = getFramePointer (); + while (max) + { + /* sparc stack traces are easy -- chain up the saved FP/SP values */ + ra = (unsigned long *) sp[15]; + sp = (unsigned long *) sp[14]; + /* stop when we get the call to main */ + mainCall = ((((unsigned long) main) - ((unsigned long) ra)) >> 2) | 0x40000000; + if (ra[0] == mainCall) + { + *results++ = 0; + break; + } + *results++ = (unsigned long) ra; + max--; + } +} diff --git a/xc/util/memleak/getrettest.c b/xc/util/memleak/getrettest.c new file mode 100644 index 000000000..671a0b429 --- /dev/null +++ b/xc/util/memleak/getrettest.c @@ -0,0 +1,44 @@ +/* + * $TOG: getrettest.c /main/5 1998/02/09 11:40:08 kaleb $ + * +Copyright 1992, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + +main () +{ + f (); +} + +f () +{ + g (); +} + + +g () +{ + unsigned long returnStack[16]; + int i; + + getStackTrace (returnStack, 16); + for (i = 0; i < 16 && returnStack[i]; i++) + printf ("%2d: 0x%lx\n", i, returnStack[i]); +} diff --git a/xc/util/memleak/mipsstack.s b/xc/util/memleak/mipsstack.s new file mode 100644 index 000000000..9dfb94d41 --- /dev/null +++ b/xc/util/memleak/mipsstack.s @@ -0,0 +1,39 @@ +/* + * $TOG: mipsstack.s /main/4 1998/02/09 11:40:17 kaleb $ + * +Copyright 1992, 1998 The Open Group + +All Rights Reserved. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + * + * Author: Keith Packard, MIT X Consortium + */ + + .globl getReturnAddress + .ent getReturnAddress +getReturnAddress: + .frame $sp, 0, $31 + move $2,$31 + j $31 + .end getReturnAddress + + .globl getStackPointer + .ent getStackPointer +getStackPointer: + .frame $sp, 0, $31 + move $2,$29 + j $31 + .end getStackPointer diff --git a/xc/util/memleak/sparcsolstack.s b/xc/util/memleak/sparcsolstack.s new file mode 100644 index 000000000..4eac4ed5f --- /dev/null +++ b/xc/util/memleak/sparcsolstack.s @@ -0,0 +1,11 @@ +! $XConsortium: sparcsolstack.s,v 1.1 93/12/03 09:26:24 kaleb Exp $ + .seg "text" + .proc 16 + .globl getStackPointer +getStackPointer: + retl + mov %sp,%o0 + .globl getFramePointer +getFramePointer: + retl + mov %fp,%o0 diff --git a/xc/util/memleak/sparcstack.s b/xc/util/memleak/sparcstack.s new file mode 100644 index 000000000..99f5221eb --- /dev/null +++ b/xc/util/memleak/sparcstack.s @@ -0,0 +1,11 @@ +# $XConsortium: sparcstack.s,v 1.2 92/04/08 17:19:18 keith Exp $ + .seg "text" + .proc 16 + .globl _getStackPointer +_getStackPointer: + retl + mov %sp,%o0 + .globl _getFramePointer +_getFramePointer: + retl + mov %fp,%o0 diff --git a/xc/util/memleak/stackbottom.c b/xc/util/memleak/stackbottom.c new file mode 100644 index 000000000..b29400eb6 --- /dev/null +++ b/xc/util/memleak/stackbottom.c @@ -0,0 +1,123 @@ +/* + * cut and paste from gc 4.4 by Hans-J. Boehm and Alan J. Demers + * + * Cutter and Paster: Jeffrey Hsu + */ + +/* $XFree86: xc/util/memleak/stackbottom.c,v 3.1 1996/12/31 05:02:27 dawes Exp $ */ + +#include <signal.h> + +typedef char * ptr_t; /* A generic pointer to which we can add */ + /* byte displacements. */ + /* Preferably identical to caddr_t, if it */ + /* exists. */ + +#ifndef bool +typedef int bool; +#endif + +#define VOLATILE volatile + +# ifndef STACK_GROWS_UP +# define STACK_GROWS_DOWN +# endif + +typedef unsigned long word; + +#define TRUE 1 + +#if defined(__alpha) || defined(__alpha__) + extern __start +# define HEURISTIC2_LIMIT ((ptr_t)((word)(&__start) & ~(getpagesize()-1))) +#endif + +void GC_noop() {} + + /* Some tools to implement HEURISTIC2 */ +# define MIN_PAGE_SIZE 256 /* Smallest conceivable page size, bytes */ +# include <setjmp.h> + /* static */ jmp_buf GC_jmp_buf; + + /*ARGSUSED*/ + void GC_fault_handler(sig) + int sig; + { + longjmp(GC_jmp_buf, 1); + } + +# ifdef __STDC__ + typedef void (*handler)(int); +# else + typedef void (*handler)(); +# endif + + /* Return the first nonaddressible location > p (up) or */ + /* the smallest location q s.t. [q,p] is addressible (!up). */ + ptr_t GC_find_limit(p, up) + ptr_t p; + bool up; + { + static VOLATILE ptr_t result; + /* Needs to be static, since otherwise it may not be */ + /* preserved across the longjmp. Can safely be */ + /* static since it's only called once, with the */ + /* allocation lock held. */ + + static handler old_segv_handler, old_bus_handler; + /* See above for static declaration. */ + + old_segv_handler = signal(SIGSEGV, GC_fault_handler); +# ifdef SIGBUS + old_bus_handler = signal(SIGBUS, GC_fault_handler); +# endif + if (setjmp(GC_jmp_buf) == 0) { + result = (ptr_t)(((word)(p)) + & ~(MIN_PAGE_SIZE-1)); + for (;;) { + if (up) { + result += MIN_PAGE_SIZE; + } else { + result -= MIN_PAGE_SIZE; + } + GC_noop(*result); + } + } + (void) signal(SIGSEGV, old_segv_handler); +# ifdef SIGBUS + (void) signal(SIGBUS, old_bus_handler); +# endif + if (!up) { + result += MIN_PAGE_SIZE; + } + return(result); + } + +ptr_t GC_get_stack_base() +{ + word dummy; + static ptr_t result; + + if (!result) { + +# ifdef STACK_GROWS_DOWN + result = GC_find_limit((ptr_t)(&dummy), TRUE); +# ifdef HEURISTIC2_LIMIT + if (result > HEURISTIC2_LIMIT + && (ptr_t)(&dummy) < HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# else + result = GC_find_limit((ptr_t)(&dummy), FALSE); +# ifdef HEURISTIC2_LIMIT + if (result < HEURISTIC2_LIMIT + && (ptr_t)(&dummy) > HEURISTIC2_LIMIT) { + result = HEURISTIC2_LIMIT; + } +# endif +# endif + } + + return(result); +} diff --git a/xc/util/misc/dlsym.c b/xc/util/misc/dlsym.c new file mode 100644 index 000000000..2e66d42a4 --- /dev/null +++ b/xc/util/misc/dlsym.c @@ -0,0 +1,29 @@ +/* $XConsortium: dlsym.c,v 1.1 93/12/06 16:24:15 kaleb Exp $ */ +/* + * Stub interface to dynamic linker routines + * that SunOS uses but didn't ship with 4.1. + * + * The C library routine wcstombs in SunOS 4.1 tries to dynamically + * load some routines using the dlsym interface, described in dlsym(3x). + * Unfortunately SunOS 4.1 does not include the necessary library, libdl. + * + * The R5 Xlib uses wcstombs. If you link dynamcally, your program can + * run even with the unresolved reference to dlsym. However, if you + * link statically, you will encounter this bug. One workaround + * is to include these stub routines when you link. + */ + +void *dlopen() +{ + return 0; +} + +void *dlsym() +{ + return 0; +} + +int dlclose() +{ + return -1; +} diff --git a/xc/util/misc/rt.stdarg.h b/xc/util/misc/rt.stdarg.h new file mode 100644 index 000000000..99cc06e7d --- /dev/null +++ b/xc/util/misc/rt.stdarg.h @@ -0,0 +1,8 @@ +/* $XConsortium: rt.stdarg.h,v 1.1 93/12/06 16:24:27 kaleb Exp $ */ +#ifndef _STDARG_H +#define _STDARG_H +typedef int *va_list; +#define va_start(ap, arg) ap = ((int *)&arg) + ((sizeof(arg) + 3) / 4) +#define va_end(ap) +#define va_arg(ap, type) ((type *)(ap += (sizeof(type)+3)/4))[-1] +#endif diff --git a/xc/util/misc/thr_stubs.c b/xc/util/misc/thr_stubs.c new file mode 100644 index 000000000..3e841dc21 --- /dev/null +++ b/xc/util/misc/thr_stubs.c @@ -0,0 +1,27 @@ +/* $XConsortium: thr_stubs.c,v 1.3 94/01/13 13:59:50 kaleb Exp $ */ +/* + * Stub interface to thread routines that Solaris needs but shipped + * broken/buggy versions in 5.2 and 5.3 + * + * One workaround is to include this stub routine when you link. + * + * These routines don't need to have accurate interfaces -- they will + * never be called. They just need to be there in order to be resolved + * at link time by non-threaded programs. + */ + +extern int errno; + +typedef int thread_t; + +thread_t thr_self(void) { errno = -1; return 0; } +int thr_create(void) { errno = -1; return -1; } +int mutex_init(void) { errno = -1; return -1; } +int mutex_destroy(void) { errno = -1; return -1; } +int mutex_lock(void) { errno = -1; return -1; } +int mutex_unlock(void) { errno = -1; return -1; } +int cond_init(void) { errno = -1; return -1; } +int cond_destroy(void) { errno = -1; return -1; } +int cond_wait(void) { errno = -1; return -1; } +int cond_signal(void) { errno = -1; return -1; } +int cond_broadcast(void) { errno = -1; return -1; } diff --git a/xc/util/patch/ChangeLog b/xc/util/patch/ChangeLog new file mode 100644 index 000000000..305293787 --- /dev/null +++ b/xc/util/patch/ChangeLog @@ -0,0 +1,290 @@ +Mon May 31 00:49:40 1993 Paul Eggert (eggert@twinsun.com) + + * patchlevel.h: PATCHLEVEL 12u9. + + * inp.c (plan_a): Don't lock the checked-out file if `patch -o' + redirected the output elsewhere. + * common.h (CHECKOUT_LOCKED, GET_LOCKED): New macros. GET and + CHECKOUT now just checkout unlocked copies. + + * Makefile (dist): Use gzip, not compress. + +Fri May 28 08:44:50 1993 Paul Eggert (eggert@twinsun.com) + + * backupfile.c (basename): Define even if NODIR isn't defined. + * patch.c (main): Ask just once to apply a reversed patch. + +Tue Sep 15 00:36:15 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u8. + +Mon Sep 14 22:01:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Makefile.SH: Add uninstall target. Simplify install target. + + * util.c (fatal, pfatal): Add some asterisks to make fatal + messages stand out more. + +Tue Aug 25 22:13:36 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patch.c (main, get_some_switches), common.h, inp.c (plan_a, + plan_b), pch.c (there_is_another_patch): Add -t option, + similar to -f. + +Mon Jul 27 11:27:07 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (plan_a, util.c (fetchname): Use a macro to simplify code. + * common.h: Define SCCSDIFF and RCSDIFF. + * inp.c (plan_a): Use them to make sure it's safe to check out + the default RCS or SCCS version. + From Paul Eggert. + +Wed Jul 22 14:37:08 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * patch.man: Use the standard comment syntax -- '\" -- instead + of '''. + +Tue Jul 21 15:26:01 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Configure: Add /etc /usr/lib /lib to pth. + +Mon Jul 20 14:10:32 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.h: Declare basename. + * inp.c (plan_a), util.c (fetchname): Use it to isolate the + leading path when testing for RCS and SCCS files. + +Sat Jul 11 18:03:26 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Configure: Use the user's PATH and build pth from it. + +Fri Jul 10 16:03:23 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * Configure: Change cc -S to cc -c and tr '[ - ]' '[\012-\012]' + to tr ' ' '\012' for AIX 3.2. + From chip@tct.com (Chip Salzenberg). + + * util.c (makedirs): Only make the directories that don't exist. + From chip@tct.com (Chip Salzenberg). + +Wed Jul 8 01:21:15 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (fatal, pfatal): Print "patch: " before message. + * pch.c, inp.c, patch.c, util.c: Remove "patch: " from the + callers that had it. + + * util.c (pfatal): New function. + * util.h: Declare it and pfatal[1-4] macros. + * various files: Use it instead of fatal where appropriate. + + * Configure: Make /usr/local/man/man1 the first choice for the + man pages. + + * patch.c (main): Open ofp after checking for ed script. + Close ofp and rejfp before trying plan B. + From epang@sfu.ca (Eugene Pang). + + * backupfile.h: Declare get_version. + + * Move decls of rindex and popen to common.h. + + * common.h (myuid): New variable. + * patch.c (main): Initialize it. + * inp.c (myuid): Function removed. + (plan_a): Use the variable, not the function. + + * patch.c: Reinstate -E option. + +Tue Jul 7 23:19:28 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (myuid): New function. + (plan_a): Call it. Optimize stat calls. Be smarter about + detecting checked out RCS and SCCS files. + From Paul Eggert (eggert@twinsun.com). + + * inp.c, util.c, patch.c: Don't bother checking for stat() > 0. + +Mon Jul 6 13:01:52 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * version.c (version): Don't print the RCS stuff, since we're + not updating it regularly. + + * patch.c (get_some_switches): Make the usage message more accurate. + + * patchlevel.h: PATCHLEVEL 12u7. + + * Makefile.SH (dist): New target. + Makedist: File removed. + + * inp.c (plan_a): Check whether the user can write to the + file, not whether anyone can write to the file. + +Sat Jul 4 00:06:58 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * inp.c (plan_a): Try to check out read-only files from RCS or SCCS. + + * util.c (move_file): If backing up by linking fails, try copying. + From cek@sdc.boeing.com (Conrad Kimball). + + * patch.c (get_some_switches): Eliminate -E option; always + remove empty output files. + + * util.c (fetchname): Only undo slash removal for relative + paths if -p was not given. + + * Makefile.sh: Add mostlyclean target. + +Fri Jul 3 23:48:14 1992 David J. MacKenzie (djm@nutrimat.gnu.ai.mit.edu) + + * util.c (fetchname): Accept whitespace between `Index:' and filename. + Also plug a small memory leak for diffs against /dev/null. + From eggert@twinsun.com (Paul Eggert). + + * common.h: Don't define TRUE and FALSE if already defined. + From phk@data.fls.dk (Poul-Henning Kamp). + +Wed Apr 29 10:19:33 1992 David J. MacKenzie (djm@churchy.gnu.ai.mit.edu) + + * backupfile.c (get_version): Exit if given a bad backup type. + +Fri Mar 27 09:57:14 1992 Karl Berry (karl at hayley) + + * common.h (S_ISDIR, S_ISREG): define these. + * inp.c (plan_a): use S_ISREG, not S_IFREG. + * util.c (fetchname): use S_ISDIR, not S_IFDIR. + +Mon Mar 16 14:10:42 1992 David J. MacKenzie (djm@wookumz.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u6. + +Sat Mar 14 13:13:29 1992 David J. MacKenzie (djm at frob.eng.umd.edu) + + * Configure, config.h.SH: Check for directory header and unistd.h. + + * patch.c (main): If -E was given and output file is empty after + patching, remove it. + (get_some_switches): Recognize -E option. + + * patch.c (copy_till): Make garbled output an error, not a warning + that doesn't change the exit status. + + * common.h: Protect against system declarations of malloc and realloc. + + * Makedist: Add backupfile.[ch]. + + * Configure: Look for C library where NeXT and SVR4 put it. + Look in /usr/ucb after /bin and /usr/bin for utilities, + and look in /usr/ccs/bin, to make SVR4 happier. + Recognize m68k predefine. + + * util.c (fetchname): Test of stat return value was backward. + From csss@scheme.cs.ubc.ca. + + * version.c (version): Exit with status 0, not 1. + + * Makefile.SH: Add backupfile.[cho]. + * patch.c (main): Initialize backup file generation. + (get_some_switches): Add -V option. + * common.h, util,c, patch.c: Replace origext with simple_backup_suffix. + * util.c (move_file): Use find_backup_file_name. + +Tue Dec 3 11:27:16 1991 David J. MacKenzie (djm at wookumz.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u5. + + * Makefile.SH: Change clean, distclean, and realclean targets a + little so they agree with the GNU coding standards. + Add Makefile to addedbyconf, so distclean removes it. + + * Configure: Recognize Domain/OS C library in /lib/libc. + From mmuegel@mot.com (Michael S. Muegel). + + * pch.c: Fixes from Wayne Davison: + Patch now accepts no-context context diffs that are + specified with an assumed one line hunk (e.g. "*** 10 ****"). + Fixed a bug in both context and unified diff processing that would + put a zero-context hunk in the wrong place (one line too soon). + Fixed a minor problem with p_max in unified diffs where it would + set p_max to hunkmax unnecessarily (the only adverse effect was to + not supply empty lines at eof by assuming they were truncated). + +Tue Jul 2 03:25:51 1991 David J. MacKenzie (djm at geech.gnu.ai.mit.edu) + + * Configure: Check for signal declaration in + /usr/include/sys/signal.h as well as /usr/include/signal.h. + + * Configure, common.h, config.h.SH: Comment out the sprintf + declaration and tests to determine its return value type. It + conflicts with ANSI C systems' prototypes in stdio.h and the + return value of sprintf is never used anyway -- it's always cast + to void. + +Thu Jun 27 13:05:32 1991 David J. MacKenzie (djm at churchy.gnu.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u4. + +Thu Feb 21 15:18:14 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * pch.c (another_hunk): Fix off by 1 error. From + iverson@xstor.com (Tim Iverson). + +Sun Jan 20 20:18:58 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * Makefile.SH (all): Don't make a dummy `all' file. + + * patchlevel.h: PATCHLEVEL 12u3. + + * patch.c (nextarg): New function. + (get_some_switches): Use it, to prevent dereferencing a null + pointer if an option that takes an arg is not given one (is last + on the command line). From Paul Eggert. + + * pch.c (another_hunk): Fix from Wayne Davison to recognize + single-line hunks in unified diffs (with a single line number + instead of a range). + + * inp.c (rev_in_string): Don't use `s' before defining it. From + Wayne Davison. + +Mon Jan 7 06:25:11 1991 David J. MacKenzie (djm at geech.ai.mit.edu) + + * patchlevel.h: PATCHLEVEL 12u2. + + * pch.c (intuit_diff_type): Recognize `+++' in diff headers, for + unified diff format. From unidiff patch 1. + +Mon Dec 3 00:14:25 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * patch.c (get_some_switches): Make the usage message more + informative. + +Sun Dec 2 23:20:18 1990 David J. MacKenzie (djm at albert.ai.mit.edu) + + * Configure: When checking for C preprocessor, look for 'abc.*xyz' + instead of 'abc.xyz', so ANSI C preprocessors work. + + * Apply fix for -D from ksb@mentor.cc.purdue.edu (Kevin Braunsdorf). + + * Apply unidiff patches from davison@dri.com (Wayne Davison). + +Wed Mar 7 23:47:25 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * pch.c: Call malformed instead of goto malformed + (just allows easier debugging). + +Tue Jan 23 21:27:00 1990 Jim Kingdon (kingdon at pogo.ai.mit.edu) + + * common.h (TMP*NAME): Make these char *, not char []. + patch.c (main): Use TMPDIR (if present) to set TMP*NAME. + common.h: Declare getenv. + +Sun Dec 17 17:29:48 1989 Jim Kingdon (kingdon at hobbes.ai.mit.edu) + + * patch.c (reverse_flag_specified): New variable. + (get_some_switches, reinitialize_almost_everything): Use it. + +Local Variables: +mode: indented-text +left-margin: 8 +version-control: never +end: diff --git a/xc/util/patch/Configure b/xc/util/patch/Configure new file mode 100755 index 000000000..0636a7258 --- /dev/null +++ b/xc/util/patch/Configure @@ -0,0 +1,1476 @@ +#! /bin/sh +# +# If these # comments don't work, trim them. Don't worry about any other +# shell scripts, Configure will trim # comments from them for you. +# +# (If you are trying to port this package to a machine without sh, I would +# suggest you cut out the prototypical config.h from the end of Configure +# and edit it to reflect your system. Some packages may include samples +# of config.h for certain machines, so you might look for one of those.) +# +# oldHeader: Head.U,v 1.0 87/05/22 12:28:10 lwall Exp $ +# $XConsortium: Configure,v 1.1 94/09/09 20:04:01 gildea Exp $ +# +# Yes, you may rip this off to use in other distribution packages. +# (Note: this Configure script was generated automatically. Rather than +# working with this copy of Configure, you may wish to get metaconfig.) + +: sanity checks +: the user has a better chance than we do of setting a reasonable PATH +: but add some directories we need that are probably not there +PATH=.:${PATH}:/etc:/usr/lib:/lib +export PATH || (echo "OOPS, this isn't sh. Desperation time. I will feed myself to sh."; sh $0; kill $$) + +if test ! -t 0; then + echo "Say 'sh Configure', not 'sh <Configure'" + exit 1 +fi + +(alias) >/dev/null 2>&1 && \ + echo "(I see you are using the Korn shell. Some ksh's blow up on Configure," && \ + echo "especially on exotic machines. If yours does, try the Bourne shell instead.)" + +if test ! -d ../UU; then + if test ! -d UU; then + mkdir UU + fi + cd UU +fi + +case "$1" in +-d) shift; fastread='yes';; +esac + +d_eunice='' +eunicefix='' +define='' +loclist='' +expr='' +sed='' +echo='' +cat='' +rm='' +mv='' +cp='' +tail='' +tr='' +mkdir='' +sort='' +uniq='' +grep='' +trylist='' +test='' +inews='' +egrep='' +more='' +pg='' +Mcc='' +vi='' +mailx='' +mail='' +cpp='' +Log='' +Header='' +bin='' +cc='' +contains='' +cppstdin='' +cppminus='' +d_charsprf='' +d_flexfnam='' +d_index='' +d_voidsig='' +d_dirheader='' +d_unistd='' +libc='' +mansrc='' +manext='' +models='' +split='' +small='' +medium='' +large='' +huge='' +ccflags='' +ldflags='' +n='' +c='' +package='' +registers='' +reg1='' +reg2='' +reg3='' +reg4='' +reg5='' +reg6='' +reg7='' +reg8='' +reg9='' +reg10='' +reg11='' +reg12='' +reg13='' +reg14='' +reg15='' +reg16='' +spitshell='' +shsharp='' +sharpbang='' +startsh='' +voidflags='' +defvoidused='' +CONFIG='' +: set package name +package=patch + +echo " " +echo "Beginning of configuration questions for $package kit." +: Eunice requires " " instead of "", can you believe it +echo " " + +define='define' +undef='/*undef' +libpth='/usr/lib /usr/local/lib /lib /usr/ccs/lib' +smallmach='pdp11 i8086 z8000 i80286 iAPX286' +rmlist='kit[1-9]isdone kit[1-9][0-9]isdone' +trap 'echo " "; rm -f $rmlist; exit 1' 1 2 3 + +: We must find out about Eunice early +eunicefix=':' +if test -f /etc/unixtovms; then + eunicefix=/etc/unixtovms +fi +if test -f /etc/unixtovms.exe; then + eunicefix=/etc/unixtovms.exe +fi + +attrlist="mc68000 sun gcos unix ibm gimpel interdata tss os mert pyr" +attrlist="$attrlist vax pdp11 i8086 z8000 u3b2 u3b5 u3b20 u3b200" +attrlist="$attrlist ns32000 ns16000 iAPX286 mc300 mc500 mc700 sparc" +attrlist="$attrlist nsc32000 sinix xenix venix posix ansi M_XENIX" +attrlist="$attrlist mc68k m68k __STDC__" +pth=`echo $PATH | tr : ' '` +d_newshome="../../NeWS" +defvoidused=7 + +: some greps do not return status, grrr. +echo "grimblepritz" >grimble +if grep blurfldyick grimble >/dev/null 2>&1 ; then + contains=contains +elif grep grimblepritz grimble >/dev/null 2>&1 ; then + contains=grep +else + contains=contains +fi +rm -f grimble +: the following should work in any shell +case "$contains" in +contains*) + echo " " + echo "AGH! Grep doesn't return a status. Attempting remedial action." + cat >contains <<'EOSS' +grep "$1" "$2" >.greptmp && cat .greptmp && test -s .greptmp +EOSS +chmod +x contains +esac + +: first determine how to suppress newline on echo command +echo "Checking echo to see how to suppress newlines..." +(echo "hi there\c" ; echo " ") >.echotmp +if $contains c .echotmp >/dev/null 2>&1 ; then + echo "...using -n." + n='-n' + c='' +else + cat <<'EOM' +...using \c +EOM + n='' + c='\c' +fi +echo $n "Type carriage return to continue. Your cursor should be here-->$c" +read ans +rm -f .echotmp + +: now set up to do reads with possible shell escape and default assignment +cat <<EOSC >myread +case "\$fastread" in +yes) ans=''; echo " " ;; +*) ans='!';; +esac +while expr "X\$ans" : "X!" >/dev/null; do + read ans + case "\$ans" in + !) + sh + echo " " + echo $n "\$rp $c" + ;; + !*) + set \`expr "X\$ans" : "X!\(.*\)\$"\` + sh -c "\$*" + echo " " + echo $n "\$rp $c" + ;; + esac +done +rp='Your answer:' +case "\$ans" in +'') ans="\$dflt";; +esac +EOSC + +: general instructions +cat <<EOH + +This installation shell script will examine your system and ask you questions +to determine how the $package package should be installed. If you get stuck +on a question, you may use a ! shell escape to start a subshell or execute +a command. Many of the questions will have default answers in square +brackets--typing carriage return will give you the default. + +On some of the questions which ask for file or directory names you are +allowed to use the ~name construct to specify the login directory belonging +to "name", even if you don't have a shell which knows about that. Questions +where this is allowed will be marked "(~name ok)". + +EOH +rp="[Type carriage return to continue]" +echo $n "$rp $c" +. myread +cat <<EOH + +Much effort has been expended to ensure that this shell script will run +on any Unix system. If despite that it blows up on you, your best bet is +to edit Configure and run it again. Also, let me (lwall@netlabs.com) +know how I blew it. If you can't run Configure for some reason, you'll have +to generate a config.sh file by hand. + +This installation script affects things in two ways: 1) it may do direct +variable substitutions on some of the files included in this kit, and +2) it builds a config.h file for inclusion in C programs. You may edit +any of these files as the need arises after running this script. + +If you make a mistake on a question, there is no easy way to back up to it +currently. The easiest thing to do is to edit config.sh and rerun all the +SH files. Configure will offer to let you do this before it runs the SH files. + +EOH +rp="[Type carriage return to continue]" +echo $n "$rp $c" +. myread + +: get old answers, if there is a config file out there +if test -f ../config.sh; then + echo " " + dflt=y + rp="I see a config.sh file. Did Configure make it on THIS system? [$dflt]" + echo $n "$rp $c" + . myread + case "$ans" in + n*) echo "OK, I'll ignore it.";; + *) echo "Fetching default answers from your old config.sh file..." + tmp="$n" + ans="$c" + . ../config.sh + n="$tmp" + c="$ans" + ;; + esac +fi + +: find out where common programs are +echo " " +echo "Locating common programs..." +cat <<EOSC >loc +$startsh +case \$# in +0) exit 1;; +esac +thing=\$1 +shift +dflt=\$1 +shift +for dir in \$*; do + case "\$thing" in + .) + if test -d \$dir/\$thing; then + echo \$dir + exit 0 + fi + ;; + *) + if test -f \$dir/\$thing; then + echo \$dir/\$thing + exit 0 + elif test -f \$dir/\$thing.exe; then + : on Eunice apparently + echo \$dir/\$thing + exit 0 + fi + ;; + esac +done +echo \$dflt +exit 1 +EOSC +chmod +x loc +$eunicefix loc +loclist=" +expr +sed +echo +cat +rm +tr +grep +" +trylist=" +test +Mcc +cpp +" +for file in $loclist; do + xxx=`loc $file $file $pth` + eval $file=$xxx + eval _$file=$xxx + case "$xxx" in + /*) + echo $file is in $xxx. + ;; + *) + echo "I don't know where $file is. I hope it's in everyone's PATH." + ;; + esac +done +echo " " +echo "Don't worry if any of the following aren't found..." +ans=offhand +for file in $trylist; do + xxx=`loc $file $file $pth` + eval $file=$xxx + eval _$file=$xxx + case "$xxx" in + /*) + echo $file is in $xxx. + ;; + *) + echo "I don't see $file out there, $ans." + ans=either + ;; + esac +done +case "$egrep" in +egrep) + echo "Substituting grep for egrep." + egrep=$grep + ;; +esac +case "$test" in +test) + echo "Hopefully test is built into your sh." + ;; +/bin/test) + echo " " + dflt=n + rp="Is your "'"'"test"'"'" built into sh? [$dflt] (OK to guess)" + echo $n "$rp $c" + . myread + case "$ans" in + y*) test=test ;; + esac + ;; +*) + test=test + ;; +esac +case "$echo" in +echo) + echo "Hopefully echo is built into your sh." + ;; +/bin/echo) + echo " " + echo "Checking compatibility between /bin/echo and builtin echo (if any)..." + $echo $n "hi there$c" >foo1 + echo $n "hi there$c" >foo2 + if cmp foo1 foo2 >/dev/null 2>&1; then + echo "They are compatible. In fact, they may be identical." + else + case "$n" in + '-n') n='' c='\c' ans='\c' ;; + *) n='-n' c='' ans='-n' ;; + esac + cat <<FOO +They are not compatible! You are probably running ksh on a non-USG system. +I'll have to use /bin/echo instead of the builtin, since Bourne shell doesn't +have echo built in and we may have to run some Bourne shell scripts. That +means I'll have to use $ans to suppress newlines now. Life is ridiculous. + +FOO + rp="Your cursor should be here-->" + $echo $n "$rp$c" + . myread + fi + $rm -f foo1 foo2 + ;; +*) + : cross your fingers + echo=echo + ;; +esac +rmlist="$rmlist loc" + +: get list of predefined functions in a handy place +echo " " +if test -f /lib/libc.a; then + echo "Your C library is in /lib/libc.a. You're normal." + libc=/lib/libc.a +else + if test -f /lib/clib -a -f /lib/libc; then + echo "Your standard C library is in /lib/libc. Must be Domain/OS." + libc=/lib/libc + elif test -f /lib/libsys_s.a; then + echo "Your C library is in /lib/libsys_s.a. Must be a NeXT." + libc=/lib/libsys_s.a + else + ans=`loc libc.a blurfl/dyick $libpth` + if test ! -f $ans; then + ans=`loc clib blurfl/dyick $libpth` + fi + if test ! -f $ans; then + ans=`loc libc blurfl/dyick $libpth` + fi + if test -f $ans; then + echo "Your C library is in $ans, of all places." + libc=$ans + else + if test -f "$libc"; then + echo "Your C library is in $libc, like you said before." + else + cat <<EOM + +I can't seem to find your C library. I've looked in the following places: + + $libpth + +None of these seems to contain your C library. What is the full name +EOM + dflt=None + $echo $n "of your C library? $c" + rp='C library full name?' + . myread + libc="$ans" + fi + fi + fi +fi +echo " " +$echo $n "Extracting names from $libc for later perusal...$c" +nm $libc 2>/dev/null | sed -n -e 's/^.* T _//p' -e 's/^.* T //p' > libc.list +if $contains '^printf$' libc.list >/dev/null 2>&1; then + echo "done" +else + nm $libc 2>/dev/null | sed -n -e 's/^.* D _//p' -e 's/^.* D //p' > libc.list + if $contains '^printf$' libc.list >/dev/null 2>&1; then + echo "done" + else + echo " " + echo "nm didn't seem to work right." + echo "Trying ar instead..." + if ar t $libc | sed -e 's/\.o$//' > libc.list; then + echo "Ok." + else + echo "That didn't work either. Giving up." + exit 1 + fi + fi +fi +rmlist="$rmlist libc.list" + +: make some quick guesses about what we are up against +echo " " +$echo $n "Hmm... $c" +if $contains SIGTSTP /usr/include/signal.h >/dev/null 2>&1 ; then + echo "Looks kind of like a BSD system, but we'll see..." + echo exit 0 >bsd + echo exit 1 >usg + echo exit 1 >v7 +elif $contains '^fcntl$' libc.list >/dev/null 2>&1 ; then + echo "Looks kind of like a USG system, but we'll see..." + echo exit 1 >bsd + echo exit 0 >usg + echo exit 1 >v7 +else + echo "Looks kind of like a version 7 system, but we'll see..." + echo exit 1 >bsd + echo exit 1 >usg + echo exit 0 >v7 +fi +if $contains '^vmssystem$' libc.list >/dev/null 2>&1 ; then + cat <<'EOI' +There is, however, a strange, musty smell in the air that reminds me of +something...hmm...yes...I've got it...there's a VMS nearby, or I'm a Blit. +EOI + echo "exit 0" >eunice + eunicefix=unixtovms + d_eunice="$define" +: it so happens the Eunice I know will not run shell scripts in Unix format +else + echo " " + echo "Congratulations. You aren't running Eunice." + eunicefix=':' + d_eunice="$undef" + echo "exit 1" >eunice +fi +if test -f /xenix; then + echo "Actually, this looks more like a XENIX system..." + echo "exit 0" >xenix +else + echo " " + echo "It's not Xenix..." + echo "exit 1" >xenix +fi +chmod +x xenix +$eunicefix xenix +if test -f /venix; then + echo "Actually, this looks more like a VENIX system..." + echo "exit 0" >venix +else + echo " " + if xenix; then + : null + else + echo "Nor is it Venix..." + fi + echo "exit 1" >venix +fi +chmod +x bsd usg v7 eunice venix +$eunicefix bsd usg v7 eunice venix +rmlist="$rmlist bsd usg v7 eunice venix xenix" + +: see if sh knows # comments +echo " " +echo "Checking your sh to see if it knows about # comments..." +if sh -c '#' >/dev/null 2>&1 ; then + echo "Your sh handles # comments correctly." + shsharp=true + spitshell=cat + echo " " + echo "Okay, let's see if #! works on this system..." + echo "#!/bin/echo hi" > try + $eunicefix try + chmod +x try + try > today + if $contains hi today >/dev/null 2>&1; then + echo "It does." + sharpbang='#!' + else + echo "#! /bin/echo hi" > try + $eunicefix try + chmod +x try + try > today + if test -s today; then + echo "It does." + sharpbang='#! ' + else + echo "It doesn't." + sharpbang=': use ' + fi + fi +else + echo "Your sh doesn't grok # comments--I will strip them later on." + shsharp=false + echo "exec grep -v '^#'" >spitshell + chmod +x spitshell + $eunicefix spitshell + spitshell=`pwd`/spitshell + echo "I presume that if # doesn't work, #! won't work either!" + sharpbang=': use ' +fi + +: figure out how to guarantee sh startup +echo " " +echo "Checking out how to guarantee sh startup..." +startsh=$sharpbang'/bin/sh' +echo "Let's see if '$startsh' works..." +cat >try <<EOSS +$startsh +set abc +test "$?abc" != 1 +EOSS + +chmod +x try +$eunicefix try +if try; then + echo "Yup, it does." +else + echo "Nope. You may have to fix up the shell scripts to make sure sh runs them." +fi +rm -f try today + +#: see if sprintf is declared as int or pointer to char +#echo " " +#cat >.ucbsprf.c <<'EOF' +#main() { char buf[10]; exit((unsigned long)sprintf(buf,"%s","foo") > 10L); } +#EOF +#if cc .ucbsprf.c -o .ucbsprf >/dev/null 2>&1 && .ucbsprf; then +# echo "Your sprintf() returns (int)." +# d_charsprf="$undef" +#else +# echo "Your sprintf() returns (char*)." +# d_charsprf="$define" +#fi +#/bin/rm -f .ucbsprf.c .ucbsprf + +: see if we can have long filenames +echo " " +rm -f 123456789abcde +if (echo hi >123456789abcdef) 2>/dev/null; then + : not version 8 + if test -f 123456789abcde; then + echo 'You cannot have filenames longer than 14 characters. Sigh.' + d_flexfnam="$undef" + else + echo 'You can have filenames longer than 14 characters.' + d_flexfnam="$define" + fi +else + : version 8 probably + echo "You can't have filenames longer than 14 chars. V8 can't even think about them!" + d_flexfnam="$undef" +fi +: index or strcpy +echo " " +case "$d_index" in +n) dflt=n;; +*) dflt=y;; +esac +if $contains '^index$' libc.list >/dev/null 2>&1 ; then + if $contains '^strchr$' libc.list >/dev/null 2>&1 ; then + echo "Your system has both index() and strchr(). Shall I use" + rp="index() rather than strchr()? [$dflt]" + $echo $n "$rp $c" + . myread + case "$ans" in + n*) d_index="$define" ;; + *) d_index="$undef" ;; + esac + else + d_index="$undef" + echo "index() found." + fi +else + if $contains '^strchr$' libc.list >/dev/null 2>&1 ; then + d_index="$define" + echo "strchr() found." + else + echo "No index() or strchr() found!" + d_index="$undef" + fi +fi + +: see if signal is declared as pointer to function returning int or void +echo " " +if $contains 'void.*signal' /usr/include/signal.h >/dev/null 2>&1 || +$contains 'void.*signal' /usr/include/sys/signal.h >/dev/null 2>&1 ; then + echo "You have void (*signal())() instead of int." + d_voidsig="$define" +else + echo "You have int (*signal())() instead of void." + d_voidsig="$undef" +fi + +: check for directory library +echo " " +if test -f /usr/include/dirent.h; then + echo "You have dirent.h." + d_dirheader="#define DIRENT" +elif test -f /usr/include/ndir.h; then + echo "You have ndir.h." + d_dirheader="#define USG" +elif test -f /usr/include/sys/ndir.h; then + echo "You have sys/ndir.h." + d_dirheader="#define SYSNDIR +#define USG" +elif test -f /usr/include/sys/dir.h; then + echo "You have sys/dir.h; I hope that's the BSD version." + d_dirheader="#define SYSDIR" +else + echo "I can't find a directory library header file. +That means you won't have numbered backups available." + d_dirheader="#define NODIR" +fi + +: check for unistd.h +echo " " +if test -f /usr/include/unistd.h; then + echo "You have unistd.h." + d_unistd="$define" +else + echo "I don't see unistd.h, but that's OK." + d_unistd="$undef" +fi + +: check for void type +echo " " +$cat <<EOM +Checking to see how well your C compiler groks the void type... + + Support flag bits are: + 1: basic void declarations. + 2: arrays of pointers to functions returning void. + 4: operations between pointers to and addresses of void functions. + +EOM +case "$voidflags" in +'') + $cat >try.c <<'EOCP' +#if TRY & 1 +void main() { +#else +main() { +#endif + extern void *moo(); + void *(*goo)(); +#if TRY & 2 + void (*foo[10])(); +#endif + +#if TRY & 4 + if(*goo == moo) { + exit(0); + } +#endif + exit(0); +} +EOCP + : Argh -- AIX 3.2 does not have cc -S! + if cc -c -DTRY=7 try.c >.out 2>&1 ; then + voidflags=7 + echo "It appears to support void fully." + if $contains warning .out >/dev/null 2>&1; then + echo "However, you might get some warnings that look like this:" + $cat .out + fi + else + echo "Hmm, you compiler has some difficulty with void. Checking further..." + if cc -c -DTRY=1 try.c >/dev/null 2>&1 ; then + echo "It supports 1..." + if cc -c -DTRY=3 try.c >/dev/null 2>&1 ; then + voidflags=3 + echo "And it supports 2 but not 4." + else + echo "It doesn't support 2..." + if cc -c -DTRY=3 try.c >/dev/null 2>&1 ; then + voidflags=5 + echo "But it supports 4." + else + voidflags=1 + echo "And it doesn't support 4." + fi + fi + else + echo "There is no support at all for void." + voidflags=0 + fi + fi +esac +dflt="$voidflags"; +rp="Your void support flags add up to what? [$dflt]" +$echo $n "$rp $c" +. myread +voidflags="$ans" +$rm -f try.* .out + +: see how we invoke the C preprocessor +echo " " +echo "Now, how can we feed standard input to your C preprocessor..." +cat <<'EOT' >testcpp.c +#define ABC abc +#define XYZ xyz +ABC.XYZ +EOT +echo 'Maybe "'$cpp'" will work...' +$cpp <testcpp.c >testcpp.out 2>&1 +if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Yup, it does." + cppstdin="$cpp" + cppminus=''; +else + echo 'Nope, maybe "'$cpp' -" will work...' + $cpp - <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Yup, it does." + cppstdin="$cpp" + cppminus='-'; + else + echo 'No such luck...maybe "cc -E" will work...' + cc -E <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "It works!" + cppstdin='cc -E' + cppminus=''; + else + echo 'Nixed again...maybe "cc -E -" will work...' + cc -E - <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Hooray, it works! I was beginning to wonder." + cppstdin='cc -E' + cppminus='-'; + else + echo 'Nope...maybe "cc -P" will work...' + cc -P <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Yup, that does." + cppstdin='cc -P' + cppminus=''; + else + echo 'Nope...maybe "cc -P -" will work...' + cc -P - <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Yup, that does." + cppstdin='cc -P' + cppminus='-'; + else + echo 'Hmm...perhaps you already told me...' + case "$cppstdin" in + '') ;; + *) $cppstdin $cppminus <testcpp.c >testcpp.out 2>&1;; + esac + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Hooray, you did! I was beginning to wonder." + else + echo 'Uh-uh. Time to get fancy...' + echo 'Trying (cat >/tmp/$$.c; cc -E /tmp/$$.c; rm /tmp/$$.c)' + cppstdin='(cat >/tmp/$$.c; cc -E /tmp/$$.c; rm /tmp/$$.c)' + cppminus=''; + $cppstdin <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "Eureka!." + else + dflt=blurfl + $echo $n "No dice. I can't find a C preprocessor. Name one: $c" + rp='Name a C preprocessor:' + . myread + cppstdin="$ans" + $cppstdin <testcpp.c >testcpp.out 2>&1 + if $contains 'abc.*xyz' testcpp.out >/dev/null 2>&1 ; then + echo "OK, that will do." + else + echo "Sorry, I can't get that to work. Go find one." + exit 1 + fi + fi + fi + fi + fi + fi + fi + fi +fi +rm -f testcpp.c testcpp.out + +: get C preprocessor symbols handy +echo " " +: AIX 3.2 rejects tr '[ - ]' because the range endpoints are the same. +echo $attrlist | $tr ' ' '\012' >Cppsym.know +$cat <<EOSS >Cppsym +$startsh +case "\$1" in +-l) list=true + shift + ;; +esac +unknown='' +case "\$list\$#" in +1|2) + for sym do + if $contains "^\$1$" Cppsym.true >/dev/null 2>&1; then + exit 0 + elif $contains "^\$1$" Cppsym.know >/dev/null 2>&1; then + : + else + unknown="\$unknown \$sym" + fi + done + set X \$unknown + shift + ;; +esac +case \$# in +0) exit 1;; +esac +echo \$* | $tr ' ' '\012' | $sed -e 's/\(.*\)/\\ +#ifdef \1\\ +exit 0; _ _ _ _\1\\ \1\\ +#endif\\ +/' >/tmp/Cppsym\$\$ +echo exit 1 >>/tmp/Cppsym\$\$ +$cppstdin $cppminus </tmp/Cppsym\$\$ >/tmp/Cppsym2\$\$ +case "\$list" in +true) awk 'NF > 5 {print substr(\$6,2,100)}' </tmp/Cppsym2\$\$ ;; +*) + sh /tmp/Cppsym2\$\$ + status=\$? + ;; +esac +$rm -f /tmp/Cppsym\$\$ /tmp/Cppsym2\$\$ +exit \$status +EOSS +chmod +x Cppsym +$eunicefix Cppsym +echo "Your C preprocessor defines the following symbols:" +Cppsym -l $attrlist >Cppsym.true +cat Cppsym.true +rmlist="$rmlist Cppsym Cppsym.know Cppsym.true" + +: see how many register declarations we want to use +case "$registers" in +'') + if Cppsym vax; then + dflt=6 + elif Cppsym sun mc68000 mc68k m68k; then + dflt=10 + elif Cppsym pyr; then + dflt=14 + elif Cppsym ns32000 ns16000; then + dflt=5 + elif Cppsym $smallmach; then + dflt=3 + else + : if you have any other numbers for me, send them in + dflt=6 + fi + ;; +*) dflt=$registers ;; +esac +cat <<EOM + +Different C compilers on different machines pay attention to different +numbers of register declarations. About how many register declarations in +EOM +$echo $n "each routine does your C compiler pay attention to? (OK to guess) [$dflt] $c" +rp="# register declarations used? [$dflt]" +. myread +registers=$ans +reg1='' +awk "BEGIN { for (i=1; i<=16; i++) printf \"reg%d=''\n\", i}" </dev/null >.foo +. .foo +awk "BEGIN { for (i=1; i<=$registers; i++) printf \"reg%d=register\n\", i}" \ + </dev/null >.foo +. .foo +rm -f .foo + +: preserve RCS keywords in files with variable substitution, grrr +Log='$Log' +Header='$Header' + +: set up shell script to do ~ expansion +cat >filexp <<EOSS +$startsh +: expand filename +case "\$1" in + ~/*|~) + echo \$1 | $sed "s|~|\${HOME-\$LOGDIR}|" + ;; + ~*) + if $test -f /bin/csh; then + /bin/csh -f -c "glob \$1" + echo "" + else + name=\`$expr x\$1 : '..\([^/]*\)'\` + dir=\`$sed -n -e "/^\${name}:/{s/^[^:]*:[^:]*:[^:]*:[^:]*:[^:]*:\([^:]*\).*"'\$'"/\1/" -e p -e q -e '}' </etc/passwd\` + if $test ! -d "\$dir"; then + me=\`basename \$0\` + echo "\$me: can't locate home directory for: \$name" >&2 + exit 1 + fi + case "\$1" in + */*) + echo \$dir/\`$expr x\$1 : '..[^/]*/\(.*\)'\` + ;; + *) + echo \$dir + ;; + esac + fi + ;; +*) + echo \$1 + ;; +esac +EOSS +chmod +x filexp +$eunicefix filexp + +: determine where public executables go +case "$bin" in +'') + dflt=`loc . /bin /usr/local/bin /usr/lbin /usr/local /usr/bin` + ;; +*) dflt="$bin" + ;; +esac +cont=true +while $test "$cont" ; do + echo " " + rp="Where do you want to put the public executables? [$dflt]" + $echo $n "$rp $c" + . myread + bin="$ans" + bin=`filexp $bin` + if test -d $bin; then + cont='' + else + dflt=n + rp="Directory $bin doesn't exist. Use that name anyway? [$dflt]" + $echo $n "$rp $c" + . myread + dflt='' + case "$ans" in + y*) cont='';; + esac + fi +done + +: determine where manual pages go +case "$mansrc" in +'') + dflt=`loc . /usr/man/man1 /usr/local/man/man1 /usr/man/mann /usr/man/local/man1 /usr/man/u_man/man1` + ;; +*) dflt="$mansrc" + ;; +esac +cont=true +while $test "$cont" ; do + echo " " + rp="Where do the manual pages (source) go? [$dflt]" + $echo $n "$rp $c" + . myread + mansrc=`filexp "$ans"` + if test -d $mansrc; then + cont='' + else + dflt=n + rp="Directory $mansrc doesn't exist. Use that name anyway? [$dflt]" + $echo $n "$rp $c" + . myread + dflt='' + case "$ans" in + y*) cont='';; + esac + fi +done +case "$mansrc" in +*l) + manext=l + ;; +*n) + manext=n + ;; +*C) + manext=C + ;; +*) + manext=1 + ;; +esac + +: see what memory models we can support +case "$models" in +'') + if Cppsym pdp11; then + dflt='unsplit split' + else + ans=`loc . X /lib/small /lib/large /usr/lib/small /usr/lib/large /lib/medium /usr/lib/medium /lib/huge` + case "$ans" in + X) dflt='none';; + *) if $test -d /lib/small || $test -d /usr/lib/small; then + dflt='small' + else + dflt='' + fi + if $test -d /lib/medium || $test -d /usr/lib/medium; then + dflt="$dflt medium" + fi + if $test -d /lib/large || $test -d /usr/lib/large; then + dflt="$dflt large" + fi + if $test -d /lib/huge || $test -d /usr/lib/huge; then + dflt="$dflt huge" + fi + esac + fi + ;; +*) dflt="$models" ;; +esac +$cat <<EOM + +Some systems have different model sizes. On most systems they are called +small, medium, large, and huge. On the PDP11 they are called unsplit and +split. If your system doesn't support different memory models, say "none". +If you wish to force everything to one memory model, say "none" here and +put the appropriate flags later when it asks you for other cc and ld flags. +Venix systems may wish to put "none" and let the compiler figure things out. +(In the following question multiple model names should be space separated.) + +EOM +rp="Which models are supported? [$dflt]" +$echo $n "$rp $c" +. myread +models="$ans" + +case "$models" in +none) + small='' + medium='' + large='' + huge='' + unsplit='' + split='' + ;; +*split) + case "$split" in + '') + if $contains '-i' $mansrc/ld.1 >/dev/null 2>&1 || \ + $contains '-i' $mansrc/cc.1 >/dev/null 2>&1; then + dflt='-i' + else + dflt='none' + fi + ;; + *) dflt="$split";; + esac + rp="What flag indicates separate I and D space? [$dflt]" + $echo $n "$rp $c" + . myread + case "$ans" in + none) ans='';; + esac + split="$ans" + unsplit='' + ;; +*large*|*small*|*medium*|*huge*) + case "$model" in + *large*) + case "$large" in + '') dflt='-Ml';; + *) dflt="$large";; + esac + rp="What flag indicates large model? [$dflt]" + $echo $n "$rp $c" + . myread + case "$ans" in + none) ans=''; + esac + large="$ans" + ;; + *) large='';; + esac + case "$model" in + *huge*) + case "$huge" in + '') dflt='-Mh';; + *) dflt="$huge";; + esac + rp="What flag indicates huge model? [$dflt]" + $echo $n "$rp $c" + . myread + case "$ans" in + none) ans=''; + esac + huge="$ans" + ;; + *) huge="$large";; + esac + case "$model" in + *medium*) + case "$medium" in + '') dflt='-Mm';; + *) dflt="$medium";; + esac + rp="What flag indicates medium model? [$dflt]" + $echo $n "$rp $c" + . myread + case "$ans" in + none) ans=''; + esac + medium="$ans" + ;; + *) medium="$large";; + esac + case "$model" in + *small*) + case "$small" in + '') dflt='none';; + *) dflt="$small";; + esac + rp="What flag indicates small model? [$dflt]" + $echo $n "$rp $c" + . myread + case "$ans" in + none) ans=''; + esac + small="$ans" + ;; + *) small='';; + esac + ;; +*) + echo "Unrecognized memory models--you may have to edit Makefile.SH" + ;; +esac + +case "$ccflags" in +'') dflt='none';; +*) dflt="$ccflags";; +esac +echo " " +rp="Any additional cc flags? [$dflt]" +$echo $n "$rp $c" +. myread +case "$ans" in +none) ans=''; +esac +ccflags="$ans" + +case "$ldflags" in +'') if venix; then + dflt='-i -z' + elif xenix; then + if test -f /usr/include/dirent.h; then + dflt='-ldir -lx' + else + dflt='-lx' + fi + else + dflt='none' + fi + ;; +*) dflt="$ldflags";; +esac +echo " " +rp="Any additional ld flags? [$dflt]" +$echo $n "$rp $c" +. myread +case "$ans" in +none) ans=''; +esac +ldflags="$ans" + +: see if we need a special compiler +echo " " +if usg; then + case "$cc" in + '') + case "$Mcc" in + /*) dflt='Mcc' + ;; + *) + case "$large" in + -M*) + dflt='cc' + ;; + *) + if $contains '\-M' $mansrc/cc.1 >/dev/null 2>&1 ; then + dflt='cc -M' + else + dflt='cc' + fi + ;; + esac + ;; + esac + ;; + *) dflt="$cc";; + esac + $cat <<'EOM' + +On some systems the default C compiler will not resolve multiple global +references that happen to have the same name. On some such systems the +"Mcc" command may be used to force these to be resolved. On other systems +a "cc -M" command is required. (Note that the -M flag on other systems +indicates a memory model to use!) What command will force resolution on +EOM + $echo $n "this system? [$dflt] $c" + rp="Command to resolve multiple refs? [$dflt]" + . myread + cc="$ans" +else + echo "Not a USG system--assuming cc can resolve multiple definitions." + cc=cc +fi + +echo " " +echo "End of configuration questions." +echo " " + +: create config.sh file +echo " " +if test -d ../UU; then + cd .. +fi +echo "Creating config.sh..." +$spitshell <<EOT >config.sh +$startsh +# config.sh +# This file was produced by running the Configure script. + +d_eunice='$d_eunice' +eunicefix='$eunicefix' +define='$define' +loclist='$loclist' +expr='$expr' +sed='$sed' +echo='$echo' +cat='$cat' +rm='$rm' +mv='$mv' +cp='$cp' +tail='$tail' +tr='$tr' +mkdir='$mkdir' +sort='$sort' +uniq='$uniq' +grep='$grep' +trylist='$trylist' +test='$test' +inews='$inews' +egrep='$egrep' +more='$more' +pg='$pg' +Mcc='$Mcc' +vi='$vi' +mailx='$mailx' +mail='$mail' +cpp='$cpp' +Log='$Log' +Header='$Header' +bin='$bin' +cc='$cc' +contains='$contains' +cppstdin='$cppstdin' +cppminus='$cppminus' +d_charsprf='$d_charsprf' +d_flexfnam='$d_flexfnam' +d_index='$d_index' +d_voidsig='$d_voidsig' +d_dirheader='$d_dirheader' +d_unistd='$d_unistd' +libc='$libc' +mansrc='$mansrc' +manext='$manext' +models='$models' +split='$split' +small='$small' +medium='$medium' +large='$large' +huge='$huge' +ccflags='$ccflags' +ldflags='$ldflags' +n='$n' +c='$c' +package='$package' +registers='$registers' +reg1='$reg1' +reg2='$reg2' +reg3='$reg3' +reg4='$reg4' +reg5='$reg5' +reg6='$reg6' +reg7='$reg7' +reg8='$reg8' +reg9='$reg9' +reg10='$reg10' +reg11='$reg11' +reg12='$reg12' +reg13='$reg13' +reg14='$reg14' +reg15='$reg15' +reg16='$reg16' +spitshell='$spitshell' +shsharp='$shsharp' +sharpbang='$sharpbang' +startsh='$startsh' +voidflags='$voidflags' +defvoidused='$defvoidused' +CONFIG=true +EOT + +CONFIG=true + +echo " " +dflt='' +fastread='' +echo "If you didn't make any mistakes, then just type a carriage return here." +rp="If you need to edit config.sh, do it as a shell escape here:" +$echo $n "$rp $c" +. UU/myread +case "$ans" in +'') ;; +*) : in case they cannot read + eval $ans;; +esac +. ./config.sh + +echo " " +echo "Doing variable substitutions on .SH files..." +set x `awk '{print $1}' <MANIFEST | $grep '\.SH'` +shift +case $# in +0) set x *.SH; shift;; +esac +if test ! -f $1; then + shift +fi +for file in $*; do + case "$file" in + */*) + dir=`$expr X$file : 'X\(.*\)/'` + file=`$expr X$file : 'X.*/\(.*\)'` + (cd $dir && . $file) + ;; + *) + . $file + ;; + esac +done +if test -f config.h.SH; then + if test ! -f config.h; then + : oops, they left it out of MANIFEST, probably, so do it anyway. + . config.h.SH + fi +fi + +if $contains '^depend:' Makefile >/dev/null 2>&1; then + dflt=n + $cat <<EOM + +Now you need to generate make dependencies by running "make depend". +You might prefer to run it in background: "make depend > makedepend.out &" +It can take a while, so you might not want to run it right now. + +EOM + rp="Run make depend now? [$dflt]" + $echo $n "$rp $c" + . UU/myread + case "$ans" in + y*) make depend + echo "Now you must run a make." + ;; + *) echo "You must run 'make depend' then 'make'." + ;; + esac +elif test -f Makefile; then + echo " " + echo "Now you must run a make." +else + echo "Done." +fi + +$rm -f kit*isdone +: the following is currently useless +cd UU && $rm -f $rmlist +: since this removes it all anyway +cd .. && $rm -rf UU +: end of Configure diff --git a/xc/util/patch/EXTERN.h b/xc/util/patch/EXTERN.h new file mode 100644 index 000000000..1f0a6da23 --- /dev/null +++ b/xc/util/patch/EXTERN.h @@ -0,0 +1,21 @@ +/* oldHeader: EXTERN.h,v 2.0 86/09/17 15:35:37 lwall Exp $ + * $XConsortium: EXTERN.h,v 2.1 94/09/09 20:04:44 gildea Exp $ + * + * Revision 2.0 86/09/17 15:35:37 lwall + * Baseline for netwide release. + * + */ + +#ifdef EXT +#undef EXT +#endif +#define EXT extern + +#ifdef INIT +#undef INIT +#endif +#define INIT(x) + +#ifdef DOINIT +#undef DOINIT +#endif diff --git a/xc/util/patch/INTERN.h b/xc/util/patch/INTERN.h new file mode 100644 index 000000000..abaa57adf --- /dev/null +++ b/xc/util/patch/INTERN.h @@ -0,0 +1,19 @@ +/* oldHeader: INTERN.h,v 2.0 86/09/17 15:35:58 lwall Exp $ + * $XConsortium: INTERN.h,v 2.1 94/09/09 20:08:54 gildea Exp $ + * + * Revision 2.0 86/09/17 15:35:58 lwall + * Baseline for netwide release. + * + */ + +#ifdef EXT +#undef EXT +#endif +#define EXT + +#ifdef INIT +#undef INIT +#endif +#define INIT(x) = x + +#define DOINIT diff --git a/xc/util/patch/MANIFEST b/xc/util/patch/MANIFEST new file mode 100644 index 000000000..05fc9417c --- /dev/null +++ b/xc/util/patch/MANIFEST @@ -0,0 +1,27 @@ +After all the patch kits are run you should have the following files: + +Filename Kit Description +-------- --- ----------- +Configure 1 A shell script that installs everything system dependent. +EXTERN.h 4 Toggle .h files to look foreign. +INTERN.h 4 Toggle .h files to look domestic. +MANIFEST 2 This list of files. +Makefile.SH 2 The makefile. +README 1 Installation instructions. +backupfile.c 4 Make Emacs style backup file names. +backupfile.h 4 Declarations to make Emacs style backup file names. +common.h 3 Common definitions. +config.H 4 Sample config.h, in case Configure won't run. +config.h.SH 3 Produces config.h. +inp.c 3 Input file abstract data type routines. +inp.h 3 Public defs for above. +malloc.c 1 An optional malloc package. +patch.c 3 The patch program. +patch.man 2 Manual page for patch. +patchlevel.h 4 The patch level of the patch program. +pch.c 2 Patch abstract data type routines. +pch.h 3 Public defs for above. +util.c 3 Utility routines. +util.h 3 Public defs for above. +version.c 1 Version number routine. +version.h 4 Public defs for above. diff --git a/xc/util/patch/Makefile.SH b/xc/util/patch/Makefile.SH new file mode 100644 index 000000000..ab5d049fd --- /dev/null +++ b/xc/util/patch/Makefile.SH @@ -0,0 +1,116 @@ +case $CONFIG in + '') . config.sh ;; +esac +echo "Extracting Makefile (with variable substitutions)" +cat >Makefile <<!GROK!THIS! +# oldHeader: Makefile.SH,v 2.0.1.2 88/06/22 20:43:40 lwall Locked $ +# $XConsortium: Makefile.SH,v 3.1 94/09/09 20:04:28 gildea Exp $ +# +# Revision 2.0.1.2 88/06/22 20:43:40 lwall +# patch12: config.h now depends on config.h.SH +# +# Revision 2.0.1.1 88/06/03 15:00:48 lwall +# patch10: upgraded to match some new metaconfig stuff +# +# Revision 2.0 86/09/17 15:36:15 lwall +# Baseline for netwide release. +# +# Revision 1.2 86/09/08 14:07:42 lwall +# Split up patch.c. +# +# Revision 1.1 86/08/01 20:18:35 lwall +# Initial revision +# + +CC = $cc +bin = $bin +mansrc = $mansrc +manext = $manext +CFLAGS = $ccflags -O +LDFLAGS = $ldflags +SMALL = $small +LARGE = $large $split + +!GROK!THIS! +cat >>Makefile <<'!NO!SUBS!' + +public = patch +private = +manpages = patch.man +util = Makefile + +c = patch.c pch.c inp.c util.c version.c backupfile.c + +obj = patch.o pch.o inp.o util.o version.o backupfile.o + +lintflags = -phbvxac + +addedbyconf = Makefile Makefile.old bsd config.h config.sh eunice loc pdp11 usg v7 + +# grrr +SHELL = /bin/sh + +.c.o: + $(CC) -c $(CFLAGS) $(LARGE) $*.c + +all: $(public) $(private) $(util) + @echo Done. + +patch: $(obj) + $(CC) $(LDFLAGS) $(obj) $(libs) -o patch + +config.h: config.h.SH + sh config.h.SH + +# won't work with csh +install: patch + export PATH || exit 1 + -mv $(bin)/patch $(bin)/patch.old + cp patch $(bin)/patch + chmod 755 $(bin)/patch + -cp patch.man $(mansrc)/patch.$(manext) + +uninstall: + rm -f $(bin)/patch $(mansrc)/patch.$(manext) + test ! -f $(bin)/patch.old || mv $(bin)/patch.old $(bin)/patch + +clean: + rm -f patch *.o core + +mostlyclean: clean + +distclean: clean + rm -f $(addedbyconf) + +realclean: distclean + +DISTFILES=ChangeLog Configure EXTERN.h INTERN.h MANIFEST Makefile.SH \ +README common.h config.H config.h.SH inp.c inp.h malloc.c patch.c patch.man \ +patchlevel.h pch.c pch.h util.c util.h version.c version.h backupfile.c \ +backupfile.h + +dist: $(DISTFILES) + echo patch-2.0.`sed -e '/PATCHLEVEL/!d' -e 's/[^0-9]*\([0-9a-z.]*\).*/\1/' -e q patchlevel.h` > .fname + rm -rf `cat .fname` + mkdir `cat .fname` + ln $(DISTFILES) `cat .fname` + tar chZf `cat .fname`.tar.Z `cat .fname` + rm -rf `cat .fname` .fname + +# The following lint has practically everything turned on. Unfortunately, +# you have to wade through a lot of mumbo jumbo that can't be suppressed. +# If the source file has a /*NOSTRICT*/ somewhere, ignore the lint message +# for that spot. + +lint: + lint $(lintflags) $(defs) $(c) > patch.fuzz + +patch.o: config.h common.h patch.c inp.h pch.h util.h version.h backupfile.h +pch.o: config.h common.h pch.c pch.h util.h +inp.o: config.h common.h inp.c inp.h util.h +util.o: config.h common.h util.c util.h backupfile.h +version.o: config.h common.h version.c version.h patchlevel.h util.h +backupfile.o: config.h backupfile.c backupfile.h + +!NO!SUBS! +$eunicefix Makefile diff --git a/xc/util/patch/Makefile.nt b/xc/util/patch/Makefile.nt new file mode 100644 index 000000000..34c9df897 --- /dev/null +++ b/xc/util/patch/Makefile.nt @@ -0,0 +1,41 @@ +# $XConsortium: Makefile.nt,v 1.2 94/09/12 20:19:31 kaleb Exp $ + +# Simple Makefile for MS Windows NT. + +CC = cl + +LIBS = crtdll.lib kernel32.lib + +CFLAGS = -Zi -Od -DWIN32 -D_DLL -D_MT -D__STDC__ -DX_86_ -nologo +LDFLAGS = -link -debug:full -debugtype:cv + +SRCS = backupfile.c getopt.c getopt1.c inp.c patch.c pch.c util.c \ + version.c + +OBJS = backupfile.obj getopt.obj getopt1.obj inp.obj patch.obj pch.obj \ + util.obj version.obj + +HDRS = EXTERN.h INTERN.h backupfile.h common.h getopt.h \ + inp.h patchlevel.h pch.h util.h version.h + +all: patch.exe + +patch.exe: $(OBJS) + $(CC) $(OBJS) $(LIBS) -o $@ $(LDFLAGS) + +.c.obj: + if exist $@ del $@ + $(CC) -c -DHAVE_CONFIG_H -I. $(CPPFLAGS) $(CFLAGS) $< + +clean: + rm -f patch.exe *.obj + +backupfile.obj: $(HDRS) +getopt.obj: $(HDRS) +getopt1.obj: $(HDRS) +inp.obj: $(HDRS) +patch.obj: $(HDRS) +pch.obj: $(HDRS) +util.obj: $(HDRS) +version.obj: $(HDRS) + diff --git a/xc/util/patch/README b/xc/util/patch/README new file mode 100644 index 000000000..c8bd0e9c4 --- /dev/null +++ b/xc/util/patch/README @@ -0,0 +1,98 @@ +This version of patch contains modifications made by the Free Software +Foundation, summarized in the file ChangeLog. Primarily they are to +support the unified context diff format that GNU diff can produce, and +to support making GNU Emacs-style backup files. They also include +fixes for some bugs. + +There are two GNU variants of patch: this one, which retains Larry +Wall's interactive Configure script and has patchlevels starting with +`12u'; and another one that has a GNU-style non-interactive configure +script and accepts long-named options, and has patchlevels starting +with `12g'. Unlike the 12g variant, the 12u variant contains no +copylefted code, for the paranoid. The two variants are otherwise the +same. They should be available from the same places. + +The FSF is distributing this version of patch independently because as +of this writing, Larry Wall has not released a new version of patch +since mid-1988. I have heard that he has been too busy working on +other things, like Perl. + +Here is a wish list of some projects to improve patch: + +1. Correctly handle files and patchfiles that contain NUL characters. +This is hard to do straightforwardly; it would be less work to +adopt a kind of escape encoding internally. +Let ESC be a "control prefix". ESC @ stands for NUL. ESC [ stands for ESC. +You need to crunch this when reading input (replace fgets), +and when writing the output file (replace fputs), +but otherwise everything can go along as it does now. +Be careful to handle reject files correctly; +I think they are currently created using `write', not `fputs'. + +2. Correctly handle patches produced by GNU diff for files that do +not end with a newline. + +Please send bug reports for this version of patch to +bug-gnu-utils@prep.ai.mit.edu as well as to Larry Wall (lwall@netlabs.com). + --djm@gnu.ai.mit.edu (David MacKenzie) + + Patch Kit, Version 2.0 + + Copyright (c) 1988, Larry Wall + +You may copy the patch kit in whole or in part as long as you don't try to +make money off it, or pretend that you wrote it. +-------------------------------------------------------------------------- + +Please read all the directions below before you proceed any further, and +then follow them carefully. Failure to do so may void your warranty. :-) + +After you have unpacked your kit, you should have all the files listed +in MANIFEST. + +Installation + +1) Run Configure. This will figure out various things about your system. + Some things Configure will figure out for itself, other things it will + ask you about. It will then proceed to make config.h, config.sh, and + Makefile. + + You might possibly have to trim # comments from the front of Configure + if your sh doesn't handle them, but all other # comments will be taken + care of. + + If you don't have sh, you'll have to rip the prototype of config.h out + of Configure and generate the defines by hand. + +2) Glance through config.h to make sure system dependencies are correct. + Most of them should have been taken care of by running the + Configure script. + + If you have any additional changes to make to the C definitions, they + can be done in the Makefile, or in config.h. Bear in mind that they may + get undone next time you run Configure. + +3) make + + This will attempt to make patch in the current directory. + +4) make install + + This will put patch into a public directory (normally /usr/local/bin). + It will also try to put the man pages in a reasonable place. It will not + nroff the man page, however. + +5) Read the manual entry before running patch. + +6) IMPORTANT! Help save the world! Communicate any problems and + suggested patches to me, lwall@netlabs.com (Larry Wall), + so we can keep the world in sync. If you have a problem, there's + someone else out there who either has had or will have the same problem. + + If possible, send in patches such that the patch program will apply them. + Context diffs are the best, then normal diffs. Don't send ed scripts-- + I've probably changed my copy since the version you have. + + Watch for patch patches in comp.sources.bugs. Patches will generally be + in a form usable by the patch program. Your current patch level + is shown in patchlevel.h. diff --git a/xc/util/patch/backupfile.c b/xc/util/patch/backupfile.c new file mode 100644 index 000000000..d3862e336 --- /dev/null +++ b/xc/util/patch/backupfile.c @@ -0,0 +1,340 @@ +/* backupfile.c -- make Emacs style backup file names + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it without restriction. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ + +/* $XConsortium: backupfile.c,v 3.1 94/09/09 20:33:43 kaleb Exp $ */ + +/* David MacKenzie <djm@ai.mit.edu>. + Some algorithms adapted from GNU Emacs. */ + +#include <stdio.h> +#include <ctype.h> +#include <sys/types.h> +#include "backupfile.h" +#include "config.h" +char *index (); +char *rindex (); +char *malloc (); + +#ifdef DIRENT +#include <dirent.h> +#ifdef direct +#undef direct +#endif +#define direct dirent +#define NLENGTH(direct) (strlen((direct)->d_name)) +#else /* !DIRENT */ +#define NLENGTH(direct) ((direct)->d_namlen) +#ifdef USG +#ifdef SYSNDIR +#include <sys/ndir.h> +#else /* !SYSNDIR */ +#include <ndir.h> +#endif /* !SYSNDIR */ +#else /* !USG */ +#ifdef SYSDIR +#include <sys/dir.h> +#endif /* SYSDIR */ +#endif /* !USG */ +#endif /* !DIRENT */ + +#ifndef isascii +#define ISDIGIT(c) (isdigit ((unsigned char) (c))) +#else +#define ISDIGIT(c) (isascii (c) && isdigit (c)) +#endif + +#if defined (HAVE_UNISTD_H) +#include <unistd.h> +#endif + +#if defined (_POSIX_VERSION) +/* POSIX does not require that the d_ino field be present, and some + systems do not provide it. */ +#define REAL_DIR_ENTRY(dp) 1 +#else +#define REAL_DIR_ENTRY(dp) ((dp)->d_ino != 0) +#endif + +/* Which type of backup file names are generated. */ +enum backup_type backup_type = none; + +/* The extension added to file names to produce a simple (as opposed + to numbered) backup file name. */ +char *simple_backup_suffix = "~"; + +char *basename (); +char *dirname (); +static char *concat (); +char *find_backup_file_name (); +static char *make_version_name (); +static int max_backup_version (); +static int version_number (); + +/* Return NAME with any leading path stripped off. */ + +char * +basename (name) + char *name; +{ + char *base; + + base = rindex (name, '/'); + return base ? base + 1 : name; +} + +#ifndef NODIR +/* Return the name of the new backup file for file FILE, + allocated with malloc. Return 0 if out of memory. + FILE must not end with a '/' unless it is the root directory. + Do not call this function if backup_type == none. */ + +char * +find_backup_file_name (file) + char *file; +{ + char *dir; + char *base_versions; + int highest_backup; + + if (backup_type == simple) + return concat (file, simple_backup_suffix); + base_versions = concat (basename (file), ".~"); + if (base_versions == 0) + return 0; + dir = dirname (file); + if (dir == 0) + { + free (base_versions); + return 0; + } + highest_backup = max_backup_version (base_versions, dir); + free (base_versions); + free (dir); + if (backup_type == numbered_existing && highest_backup == 0) + return concat (file, simple_backup_suffix); + return make_version_name (file, highest_backup + 1); +} + +/* Return the number of the highest-numbered backup file for file + FILE in directory DIR. If there are no numbered backups + of FILE in DIR, or an error occurs reading DIR, return 0. + FILE should already have ".~" appended to it. */ + +static int +max_backup_version (file, dir) + char *file, *dir; +{ + DIR *dirp; + struct direct *dp; + int highest_version; + int this_version; + int file_name_length; + + dirp = opendir (dir); + if (!dirp) + return 0; + + highest_version = 0; + file_name_length = strlen (file); + + while ((dp = readdir (dirp)) != 0) + { + if (!REAL_DIR_ENTRY (dp) || NLENGTH (dp) <= file_name_length) + continue; + + this_version = version_number (file, dp->d_name, file_name_length); + if (this_version > highest_version) + highest_version = this_version; + } + closedir (dirp); + return highest_version; +} + +/* Return a string, allocated with malloc, containing + "FILE.~VERSION~". Return 0 if out of memory. */ + +static char * +make_version_name (file, version) + char *file; + int version; +{ + char *backup_name; + + backup_name = malloc (strlen (file) + 16); + if (backup_name == 0) + return 0; + sprintf (backup_name, "%s.~%d~", file, version); + return backup_name; +} + +/* If BACKUP is a numbered backup of BASE, return its version number; + otherwise return 0. BASE_LENGTH is the length of BASE. + BASE should already have ".~" appended to it. */ + +static int +version_number (base, backup, base_length) + char *base; + char *backup; + int base_length; +{ + int version; + char *p; + + version = 0; + if (!strncmp (base, backup, base_length) && ISDIGIT (backup[base_length])) + { + for (p = &backup[base_length]; ISDIGIT (*p); ++p) + version = version * 10 + *p - '0'; + if (p[0] != '~' || p[1]) + version = 0; + } + return version; +} + +/* Return the newly-allocated concatenation of STR1 and STR2. + If out of memory, return 0. */ + +static char * +concat (str1, str2) + char *str1, *str2; +{ + char *newstr; + char str1_length = strlen (str1); + + newstr = malloc (str1_length + strlen (str2) + 1); + if (newstr == 0) + return 0; + strcpy (newstr, str1); + strcpy (newstr + str1_length, str2); + return newstr; +} + +/* Return the leading directories part of PATH, + allocated with malloc. If out of memory, return 0. + Assumes that trailing slashes have already been + removed. */ + +char * +dirname (path) + char *path; +{ + char *newpath; + char *slash; + int length; /* Length of result, not including NUL. */ + + slash = rindex (path, '/'); + if (slash == 0) + { + /* File is in the current directory. */ + path = "."; + length = 1; + } + else + { + /* Remove any trailing slashes from result. */ + while (slash > path && *slash == '/') + --slash; + + length = slash - path + 1; + } + newpath = malloc (length + 1); + if (newpath == 0) + return 0; + strncpy (newpath, path, length); + newpath[length] = 0; + return newpath; +} + +/* If ARG is an unambiguous match for an element of the + null-terminated array OPTLIST, return the index in OPTLIST + of the matched element, else -1 if it does not match any element + or -2 if it is ambiguous (is a prefix of more than one element). */ + +int +argmatch (arg, optlist) + char *arg; + char **optlist; +{ + int i; /* Temporary index in OPTLIST. */ + int arglen; /* Length of ARG. */ + int matchind = -1; /* Index of first nonexact match. */ + int ambiguous = 0; /* If nonzero, multiple nonexact match(es). */ + + arglen = strlen (arg); + + /* Test all elements for either exact match or abbreviated matches. */ + for (i = 0; optlist[i]; i++) + { + if (!strncmp (optlist[i], arg, arglen)) + { + if (strlen (optlist[i]) == arglen) + /* Exact match found. */ + return i; + else if (matchind == -1) + /* First nonexact match found. */ + matchind = i; + else + /* Second nonexact match found. */ + ambiguous = 1; + } + } + if (ambiguous) + return -2; + else + return matchind; +} + +/* Error reporting for argmatch. + KIND is a description of the type of entity that was being matched. + VALUE is the invalid value that was given. + PROBLEM is the return value from argmatch. */ + +void +invalid_arg (kind, value, problem) + char *kind; + char *value; + int problem; +{ + fprintf (stderr, "patch: "); + if (problem == -1) + fprintf (stderr, "invalid"); + else /* Assume -2. */ + fprintf (stderr, "ambiguous"); + fprintf (stderr, " %s `%s'\n", kind, value); +} + +static char *backup_args[] = +{ + "never", "simple", "nil", "existing", "t", "numbered", 0 +}; + +static enum backup_type backup_types[] = +{ + simple, simple, numbered_existing, numbered_existing, numbered, numbered +}; + +/* Return the type of backup indicated by VERSION. + Unique abbreviations are accepted. */ + +enum backup_type +get_version (version) + char *version; +{ + int i; + + if (version == 0 || *version == 0) + return numbered_existing; + i = argmatch (version, backup_args); + if (i >= 0) + return backup_types[i]; + invalid_arg ("version control type", version, i); + exit (1); +} +#endif /* NODIR */ diff --git a/xc/util/patch/backupfile.h b/xc/util/patch/backupfile.h new file mode 100644 index 000000000..43240a130 --- /dev/null +++ b/xc/util/patch/backupfile.h @@ -0,0 +1,37 @@ +/* backupfile.h -- declarations for making Emacs style backup file names + Copyright (C) 1990 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it without restriction. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. */ + +/* When to make backup files. */ +enum backup_type +{ + /* Never make backups. */ + none, + + /* Make simple backups of every file. */ + simple, + + /* Make numbered backups of files that already have numbered backups, + and simple backups of the others. */ + numbered_existing, + + /* Make numbered backups of every file. */ + numbered +}; + +extern enum backup_type backup_type; +extern char *simple_backup_suffix; + +#ifdef __STDC__ +char *find_backup_file_name (char *file); +enum backup_type get_version (char *version); +#else +char *find_backup_file_name (); +enum backup_type get_version (); +#endif diff --git a/xc/util/patch/common.h b/xc/util/patch/common.h new file mode 100644 index 000000000..7209d6f90 --- /dev/null +++ b/xc/util/patch/common.h @@ -0,0 +1,214 @@ +/* oldHeader: common.h,v 2.0.1.2 88/06/22 20:44:53 lwall Locked $ + * $XConsortium: common.h,v 3.3 94/09/14 21:13:08 gildea Exp $ + * + * Revision 2.0.1.2 88/06/22 20:44:53 lwall + * patch12: sprintf was declared wrong + * + * Revision 2.0.1.1 88/06/03 15:01:56 lwall + * patch10: support for shorter extensions. + * + * Revision 2.0 86/09/17 15:36:39 lwall + * Baseline for netwide release. + * + */ + +#define DEBUGGING + +#define VOIDUSED 7 +#include "config.h" + +/* shut lint up about the following when return value ignored */ + +#define Signal (void)signal +#define Unlink (void)unlink +#define Lseek (void)lseek +#define Fseek (void)fseek +#define Fstat (void)fstat +#define Pclose (void)pclose +#define Close (void)close +#define Fclose (void)fclose +#define Fflush (void)fflush +#define Sprintf (void)sprintf +#define Mktemp (void)mktemp +#define Strcpy (void)strcpy +#define Strcat (void)strcat + +/* NeXT declares malloc and realloc incompatibly from us in some of + these files. Temporarily redefine them to prevent errors. */ +#define malloc system_malloc +#define realloc system_realloc +#include <stdio.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <signal.h> +#include <fcntl.h> +#undef malloc +#undef realloc + +#ifdef WIN32 +#include "winnt.h" +#endif + +/* constants */ + +/* AIX predefines these. */ +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif +#define TRUE (1) +#define FALSE (0) + +#define MAXHUNKSIZE 100000 /* is this enough lines? */ +#define INITHUNKMAX 125 /* initial dynamic allocation size */ +#define MAXLINELEN 1024 +#define BUFFERSIZE 1024 + +#define SCCSPREFIX "s." +#define GET "get %s" +#define GET_LOCKED "get -e %s" +#define SCCSDIFF "get -p %s | diff - %s >/dev/null" + +#define RCSSUFFIX ",v" +#define CHECKOUT "co %s" +#define CHECKOUT_LOCKED "co -l %s" +#define RCSDIFF "rcsdiff %s > /dev/null" + +#ifdef FLEXFILENAMES +#define ORIGEXT ".orig" +#define REJEXT ".rej" +#else +#define ORIGEXT "~" +#define REJEXT "#" +#endif + +/* handy definitions */ + +#define Null(t) ((t)0) +#define Nullch Null(char *) +#define Nullfp Null(FILE *) +#define Nulline Null(LINENUM) + +#define Ctl(ch) ((ch) & 037) + +#define strNE(s1,s2) (strcmp(s1, s2)) +#define strEQ(s1,s2) (!strcmp(s1, s2)) +#define strnNE(s1,s2,l) (strncmp(s1, s2, l)) +#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l)) + +/* typedefs */ + +typedef char bool; +typedef long LINENUM; /* must be signed */ +typedef unsigned MEM; /* what to feed malloc */ + +/* globals */ + +EXT int Argc; /* guess */ +EXT char **Argv; +EXT int Argc_last; /* for restarting plan_b */ +EXT char **Argv_last; + +EXT struct stat filestat; /* file statistics area */ +EXT int filemode INIT(0644); + +EXT char buf[MAXLINELEN]; /* general purpose buffer */ +EXT FILE *ofp INIT(Nullfp); /* output file pointer */ +EXT FILE *rejfp INIT(Nullfp); /* reject file pointer */ + +#ifndef WIN32 +EXT int myuid; /* cache getuid return value */ +#endif + +EXT bool using_plan_a INIT(TRUE); /* try to keep everything in memory */ +EXT bool out_of_mem INIT(FALSE); /* ran out of memory in plan a */ + +#define MAXFILEC 2 +EXT int filec INIT(0); /* how many file arguments? */ +EXT char *filearg[MAXFILEC]; +EXT bool ok_to_create_file INIT(FALSE); +EXT char *bestguess INIT(Nullch); /* guess at correct filename */ + +EXT char *outname INIT(Nullch); +EXT char rejname[128]; + +EXT char *origprae INIT(Nullch); + +EXT char *TMPOUTNAME; +EXT char *TMPINNAME; +EXT char *TMPREJNAME; +EXT char *TMPPATNAME; +EXT bool toutkeep INIT(FALSE); +EXT bool trejkeep INIT(FALSE); + +EXT LINENUM last_offset INIT(0); +#ifdef DEBUGGING +EXT int debug INIT(0); +#endif +EXT LINENUM maxfuzz INIT(2); +EXT bool force INIT(FALSE); +EXT bool batch INIT(FALSE); +EXT bool verbose INIT(TRUE); +EXT bool reverse INIT(FALSE); +EXT bool noreverse INIT(FALSE); +EXT bool skip_rest_of_patch INIT(FALSE); +EXT int strippath INIT(957); +EXT bool canonicalize INIT(FALSE); + +#define CONTEXT_DIFF 1 +#define NORMAL_DIFF 2 +#define ED_DIFF 3 +#define NEW_CONTEXT_DIFF 4 +#define UNI_DIFF 5 +EXT int diff_type INIT(0); + +EXT bool do_defines INIT(FALSE); /* patch using ifdef, ifndef, etc. */ +EXT char if_defined[128]; /* #ifdef xyzzy */ +EXT char not_defined[128]; /* #ifndef xyzzy */ +EXT char else_defined[] INIT("#else\n");/* #else */ +EXT char end_defined[128]; /* #endif xyzzy */ + +EXT char *revision INIT(Nullch); /* prerequisite revision, if any */ + +#include <errno.h> +#ifndef errno +extern int errno; +#endif + +FILE *popen(); +char *malloc(); +char *realloc(); +long atol(); +char *getenv(); +char *strcpy(); +char *strcat(); +char *rindex(); +#if 0 +long lseek(); +#endif +char *mktemp(); +#if 0 /* This can cause a prototype conflict. */ +#ifdef CHARSPRINTF +char *sprintf(); +#else +int sprintf(); +#endif +#endif + +#ifndef WIN32 +#if !defined(S_ISDIR) && defined(S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif +#if !defined(S_ISREG) && defined(S_IFREG) +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) +#endif +#else +#define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +#define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) +#endif + + diff --git a/xc/util/patch/config.H b/xc/util/patch/config.H new file mode 100644 index 000000000..93cdf0327 --- /dev/null +++ b/xc/util/patch/config.H @@ -0,0 +1,33 @@ +/* config.h + * This file was produced by running the Configure script. + * Feel free to modify any of this as the need arises. + */ + + +#/*undef EUNICE /* no file linking? */ +#/*undef VMS + +#/*undef index strchr /* cultural */ +#/*undef rindex strrchr /* differences? */ + +#/*undef void int /* is void to be avoided? */ + +/* How many register declarations are paid attention to? */ + +#define Reg1 register +#define Reg2 register +#define Reg3 register +#define Reg4 register +#define Reg5 register +#define Reg6 register +#define Reg7 +#define Reg8 +#define Reg9 +#define Reg10 +#define Reg11 +#define Reg12 +#define Reg13 +#define Reg14 +#define Reg15 +#define Reg16 + diff --git a/xc/util/patch/config.h.SH b/xc/util/patch/config.h.SH new file mode 100644 index 000000000..e34562fcc --- /dev/null +++ b/xc/util/patch/config.h.SH @@ -0,0 +1,146 @@ +case $CONFIG in +'') + if test ! -f config.sh; then + ln ../config.sh . || \ + ln ../../config.sh . || \ + ln ../../../config.sh . || \ + (echo "Can't find config.sh."; exit 1) + echo "Using config.sh from above..." + fi + . ./config.sh + ;; +esac +echo "Extracting config.h (with variable substitutions)" +cat <<!GROK!THIS! >config.h +/* config.h + * This file was produced by running the config.h.SH script, which + * gets its values from config.sh, which is generally produced by + * running Configure. + * + * Feel free to modify any of this as the need arises. Note, however, + * that running config.h.SH again will wipe out any changes you've made. + * For a more permanent change edit config.sh and rerun config.h.SH. + */ + + +/* EUNICE: + * This symbol, if defined, indicates that the program is being compiled + * under the EUNICE package under VMS. The program will need to handle + * things like files that don't go away the first time you unlink them, + * due to version numbering. It will also need to compensate for lack + * of a respectable link() command. + */ +/* VMS: + * This symbol, if defined, indicates that the program is running under + * VMS. It is currently only set in conjunction with the EUNICE symbol. + */ +#$d_eunice EUNICE /**/ +#$d_eunice VMS /**/ + +/* CPPSTDIN: + * This symbol contains the first part of the string which will invoke + * the C preprocessor on the standard input and produce to standard + * output. Typical value of "cc -E" or "/lib/cpp". + */ +/* CPPMINUS: + * This symbol contains the second part of the string which will invoke + * the C preprocessor on the standard input and produce to standard + * output. This symbol will have the value "-" if CPPSTDIN needs a minus + * to specify standard input, otherwise the value is "". + */ +#define CPPSTDIN "$cppstdin" +#define CPPMINUS "$cppminus" + +/* CHARSPRINTF: + * This symbol is defined if this system declares "char *sprintf()" in + * stdio.h. The trend seems to be to declare it as "int sprintf()". It + * is up to the package author to declare sprintf correctly based on the + * symbol. + */ +/* #$d_charsprf CHARSPRINTF /**/ + +/* FLEXFILENAMES: + * This symbol, if defined, indicates that the system supports filenames + * longer than 14 characters. + */ +#$d_flexfnam FLEXFILENAMES /**/ + +/* index: + * This preprocessor symbol is defined, along with rindex, if the system + * uses the strchr and strrchr routines instead. + */ +/* rindex: + * This preprocessor symbol is defined, along with index, if the system + * uses the strchr and strrchr routines instead. + */ +#$d_index index strchr /* cultural */ +#$d_index rindex strrchr /* differences? */ + +/* VOIDSIG: + * This symbol is defined if this system declares "void (*signal())()" in + * signal.h. The old way was to declare it as "int (*signal())()". It + * is up to the package author to declare things correctly based on the + * symbol. + */ +#$d_voidsig VOIDSIG /**/ + +/* DIRHEADER: + * This definition indicates which directory library header to use. + */ +$d_dirheader + +/* HAVE_UNISTD_H: + * This is defined if the system has unistd.h. + */ +#$d_unistd HAVE_UNISTD_H /**/ + +/* Reg1: + * This symbol, along with Reg2, Reg3, etc. is either the word "register" + * or null, depending on whether the C compiler pays attention to this + * many register declarations. The intent is that you don't have to + * order your register declarations in the order of importance, so you + * can freely declare register variables in sub-blocks of code and as + * function parameters. Do not use Reg<n> more than once per routine. + */ + +#define Reg1 $reg1 /**/ +#define Reg2 $reg2 /**/ +#define Reg3 $reg3 /**/ +#define Reg4 $reg4 /**/ +#define Reg5 $reg5 /**/ +#define Reg6 $reg6 /**/ +#define Reg7 $reg7 /**/ +#define Reg8 $reg8 /**/ +#define Reg9 $reg9 /**/ +#define Reg10 $reg10 /**/ +#define Reg11 $reg11 /**/ +#define Reg12 $reg12 /**/ +#define Reg13 $reg13 /**/ +#define Reg14 $reg14 /**/ +#define Reg15 $reg15 /**/ +#define Reg16 $reg16 /**/ + +/* VOIDFLAGS: + * This symbol indicates how much support of the void type is given by this + * compiler. What various bits mean: + * + * 1 = supports declaration of void + * 2 = supports arrays of pointers to functions returning void + * 4 = supports comparisons between pointers to void functions and + * addresses of void functions + * + * The package designer should define VOIDUSED to indicate the requirements + * of the package. This can be done either by #defining VOIDUSED before + * including config.h, or by defining defvoidused in Myinit.U. If the + * level of void support necessary is not present, defines void to int. + */ +#ifndef VOIDUSED +#define VOIDUSED $defvoidused +#endif +#define VOIDFLAGS $voidflags +#if (VOIDFLAGS & VOIDUSED) != VOIDUSED +#$define void int /* is void to be avoided? */ +#$define M_VOID /* Xenix strikes again */ +#endif + +!GROK!THIS! diff --git a/xc/util/patch/config.h.nt b/xc/util/patch/config.h.nt new file mode 100644 index 000000000..b346523b9 --- /dev/null +++ b/xc/util/patch/config.h.nt @@ -0,0 +1,82 @@ + +/* $XConsortium: config.h.nt,v 1.1 94/09/09 20:26:16 kaleb Exp $ */ + +/* Portability variables. -*- C -*- */ + +/* Define if the system does not support the `const' keyword. */ +#undef const + +/* Define if the system supports file names longer than 14 characters. */ +#define HAVE_LONG_FILE_NAMES 1 + +/* Define if the system has pathconf(). */ +#undef HAVE_PATHCONF + +/* Define if the system has strerror(). */ +#define HAVE_STRERROR + +/* Define if the system has ANSI C header files and library functions. */ +#define STDC_HEADERS + +/* Define if the system uses strchr instead of index + and strrchr instead of rindex. */ +#define HAVE_STRING_H + +#if defined(STDC_HEADERS) || defined(HAVE_STRING_H) +#define index strchr +#define rindex strrchr +#endif + +/* Define if the system has unistd.h. */ +#undef HAVE_UNISTD_H + +/* Define as either int or void -- the type that signal handlers return. */ +#undef RETSIGTYPE + +#ifndef RETSIGTYPE +#define RETSIGTYPE void +#endif + +/* Which directory library header to use. */ +#undef DIRENT /* dirent.h */ +#undef SYSNDIR /* sys/ndir.h */ +#undef SYSDIR /* sys/dir.h */ +#undef NDIR /* ndir.h */ +#define DIRECT /* Windoze NT direct.h */ +#define NODIR /* none -- don't make numbered backup files */ + +/* Define if the system lets you pass fewer arguments to a function + than the function actually accepts (in the absence of a prototype). + Defining it makes I/O calls slightly more efficient. + You need not bother defining it unless your C preprocessor chokes on + multi-line arguments to macros. */ +#undef CANVARARG + +/* Define Reg* as either `register' or nothing, depending on whether + the C compiler pays attention to this many register declarations. + The intent is that you don't have to order your register declarations + in the order of importance, so you can freely declare register variables + in sub-blocks of code and as function parameters. + Do not use Reg<n> more than once per routine. + + These don't really matter a lot, since most modern C compilers ignore + register declarations and often do a better job of allocating + registers than people do. */ + +#define Reg1 +#define Reg2 +#define Reg3 +#define Reg4 +#define Reg5 +#define Reg6 +#define Reg7 +#define Reg8 +#define Reg9 +#define Reg10 +#define Reg11 +#define Reg12 +#define Reg13 +#define Reg14 +#define Reg15 +#define Reg16 + diff --git a/xc/util/patch/inp.c b/xc/util/patch/inp.c new file mode 100644 index 000000000..20e5709a7 --- /dev/null +++ b/xc/util/patch/inp.c @@ -0,0 +1,387 @@ +/* $XConsortium: inp.c,v 3.4 94/09/14 21:18:35 gildea Exp $ + * + * Revision 3.1 94/03/29 13:39:15 gildea + * check that a patch that creates a file has not already been applied. + * version "XC1" + * + * Revision 2.0.1.1 88/06/03 15:06:13 lwall + * patch10: made a little smarter about sccs files + * + * Revision 2.0 86/09/17 15:37:02 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "pch.h" +#include "INTERN.h" +#include "inp.h" + +/* Input-file-with-indexable-lines abstract type */ + +static long i_size; /* size of the input file */ +static char *i_womp; /* plan a buffer for entire file */ +static char **i_ptr; /* pointers to lines in i_womp */ + +static int tifd = -1; /* plan b virtual string array */ +static char *tibuf[2]; /* plan b buffers */ +static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ +static LINENUM lines_per_buf; /* how many lines per buffer */ +static int tireclen; /* length of records in tmp file */ + +/* New patch--prepare to edit another file. */ + +void +re_input() +{ + if (using_plan_a) { + i_size = 0; +#ifndef lint + if (i_ptr != Null(char**)) + free((char *)i_ptr); +#endif + if (i_womp != Nullch) + free(i_womp); + i_womp = Nullch; + i_ptr = Null(char **); + } + else { + using_plan_a = TRUE; /* maybe the next one is smaller */ + Close(tifd); + tifd = -1; + free(tibuf[0]); + free(tibuf[1]); + tibuf[0] = tibuf[1] = Nullch; + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Constuct the line index, somehow or other. */ + +void +scan_input(filename) +char *filename; +{ + if (!plan_a(filename)) + plan_b(filename); + if (verbose) { + say3("Patching file %s using Plan %s...\n", filename, + (using_plan_a ? "A" : "B") ); + } +} + +/* Try keeping everything in memory. */ + +bool +plan_a(filename) +char *filename; +{ + int ifd, statfailed; + Reg1 char *s; + Reg2 LINENUM iline; + char lbuf[MAXLINELEN]; + int output_elsewhere = strcmp(filename, outname); + + statfailed = stat(filename, &filestat); + if (statfailed && ok_to_create_file) { + if (verbose) + say2("(Creating file %s...)\n",filename); + makedirs(filename, TRUE); + close(creat(filename, 0666)); + statfailed = stat(filename, &filestat); + } else if (!statfailed && ok_to_create_file) { + /* ok_to_create_file means the file either doesn't exist + or is zero-length. Since there is no context in the patch, + avoid doubling the file if the patch has already been applied + by bailing out here if these conditions are not met. */ + if (filestat.st_size != 0) + fatal2("supposedly new file \"%s\" already exists\n", filename); + } +#ifndef WIN32 + /* For nonexistent or read-only files, look for RCS or SCCS versions. */ + if (statfailed + || (! output_elsewhere + && (/* No one can write to it. */ + (filestat.st_mode & 0222) == 0 + /* I can't write to it. */ + || ((filestat.st_mode & 0022) == 0 + && filestat.st_uid != myuid)))) { + struct stat cstat; + char *cs = Nullch; + char *filebase; + int pathlen; + + filebase = basename(filename); + pathlen = filebase - filename; + + /* Put any leading path into `s'. + Leave room in lbuf for the diff command. */ + s = lbuf + 20; + strncpy(s, filename, pathlen); + +#define try(f, a1, a2) (Sprintf(s + pathlen, f, a1, a2), stat(s, &cstat) == 0) + if ( try("RCS/%s%s", filebase, RCSSUFFIX) + || try("RCS/%s" , filebase, 0) + || try( "%s%s", filebase, RCSSUFFIX)) { + Sprintf(buf, output_elsewhere?CHECKOUT:CHECKOUT_LOCKED, filename); + Sprintf(lbuf, RCSDIFF, filename); + cs = "RCS"; + } else if ( try("SCCS/%s%s", SCCSPREFIX, filebase) + || try( "%s%s", SCCSPREFIX, filebase)) { + Sprintf(buf, output_elsewhere?GET:GET_LOCKED, s); + Sprintf(lbuf, SCCSDIFF, s, filename); + cs = "SCCS"; + } else if (statfailed) + fatal2("can't find %s\n", filename); + /* else we can't write to it but it's not under a version + control system, so just proceed. */ + if (cs) { + if (!statfailed) { + if ((filestat.st_mode & 0222) != 0) + /* The owner can write to it. */ + fatal3("file %s seems to be locked by somebody else under %s\n", + filename, cs); + /* It might be checked out unlocked. See if it's safe to + check out the default version locked. */ + if (verbose) + say3("Comparing file %s to default %s version...\n", + filename, cs); + if (system(lbuf)) + fatal3("can't check out file %s: differs from default %s version\n", + filename, cs); + } + if (verbose) + say3("Checking out file %s from %s...\n", filename, cs); + if (system(buf) || stat(filename, &filestat)) + fatal3("can't check out file %s from %s\n", filename, cs); + } + } +#endif + filemode = filestat.st_mode; + if (!S_ISREG(filemode)) + fatal2("%s is not a normal file--can't patch\n", filename); + i_size = filestat.st_size; + if (out_of_mem) { + set_hunkmax(); /* make sure dynamic arrays are allocated */ + out_of_mem = FALSE; + return FALSE; /* force plan b because plan a bombed */ + } +#ifdef lint + i_womp = Nullch; +#else + i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */ + /* i_size, but that's okay, I think. */ +#endif + if (i_womp == Nullch) + return FALSE; +#ifndef WIN32 + if ((ifd = open(filename, 0)) < 0) +#else + if ((ifd = open(filename, _O_RDONLY | _O_BINARY)) < 0) +#endif + pfatal2("can't open file %s", filename); +#ifndef lint + if (read(ifd, i_womp, (int)i_size) != i_size) { + Close(ifd); /* probably means i_size > 15 or 16 bits worth */ + free(i_womp); /* at this point it doesn't matter if i_womp was */ + return FALSE; /* undersized. */ + } +#endif + Close(ifd); + if (i_size && i_womp[i_size-1] != '\n') + i_womp[i_size++] = '\n'; + i_womp[i_size] = '\0'; + + /* count the lines in the buffer so we know how many pointers we need */ + + iline = 0; + for (s=i_womp; *s; s++) { + if (*s == '\n') + iline++; + } +#ifdef lint + i_ptr = Null(char**); +#else + i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); +#endif + if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ + free((char *)i_womp); + return FALSE; + } + + /* now scan the buffer and build pointer array */ + + iline = 1; + i_ptr[iline] = i_womp; + for (s=i_womp; *s; s++) { + if (*s == '\n') + i_ptr[++iline] = s+1; /* these are NOT null terminated */ + } + input_lines = iline - 1; + + /* now check for revision, if any */ + + if (revision != Nullch) { + if (!rev_in_string(i_womp)) { + if (force) { + if (verbose) + say2( +"Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else if (batch) { + fatal2( +"this file doesn't appear to be the %s version--aborting.\n", revision); + } + else { + ask2( +"This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("aborted\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + return TRUE; /* plan a will work */ +} + +/* Keep (virtually) nothing in memory. */ + +void +plan_b(filename) +char *filename; +{ + Reg3 FILE *ifp; + Reg1 int i = 0; + Reg2 int maxlen = 1; + Reg4 bool found_revision = (revision == Nullch); + + using_plan_a = FALSE; +#ifndef WIN32 + if ((ifp = fopen(filename, "r")) == Nullfp) +#else + if ((ifp = fopen(filename, "rb")) == Nullfp) +#endif + pfatal2("can't open file %s", filename); +#ifndef WIN32 + if ((tifd = creat(TMPINNAME, 0666)) < 0) +#else + if ((tifd = open(TMPINNAME, _O_CREAT | _O_TRUNC | _O_BINARY, _S_IWRITE)) < 0) +#endif + pfatal2("can't open file %s", TMPINNAME); + while (fgets(buf, sizeof buf, ifp) != Nullch) { + if (revision != Nullch && !found_revision && rev_in_string(buf)) + found_revision = TRUE; + if ((i = strlen(buf)) > maxlen) + maxlen = i; /* find longest line */ + } + if (revision != Nullch) { + if (!found_revision) { + if (force) { + if (verbose) + say2( +"Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else if (batch) { + fatal2( +"this file doesn't appear to be the %s version--aborting.\n", revision); + } + else { + ask2( +"This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("aborted\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + Fseek(ifp, 0L, 0); /* rewind file */ + lines_per_buf = BUFFERSIZE / maxlen; + tireclen = maxlen; + tibuf[0] = malloc((MEM)(BUFFERSIZE + 1)); + tibuf[1] = malloc((MEM)(BUFFERSIZE + 1)); + if (tibuf[1] == Nullch) + fatal1("out of memory\n"); + for (i=1; ; i++) { + if (! (i % lines_per_buf)) /* new block */ + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + pfatal1("can't write temp file"); + if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) + == Nullch) { + input_lines = i - 1; + if (i % lines_per_buf) + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + pfatal1("can't write temp file"); + break; + } + } + Fclose(ifp); + Close(tifd); + if ((tifd = open(TMPINNAME, 0)) < 0) { + pfatal2("can't reopen file %s", TMPINNAME); + } +} + +/* Fetch a line from the input file, \n terminated, not necessarily \0. */ + +char * +ifetch(line,whichbuf) +Reg1 LINENUM line; +int whichbuf; /* ignored when file in memory */ +{ + if (line < 1 || line > input_lines) + return ""; + if (using_plan_a) + return i_ptr[line]; + else { + LINENUM offline = line % lines_per_buf; + LINENUM baseline = line - offline; + + if (tiline[0] == baseline) + whichbuf = 0; + else if (tiline[1] == baseline) + whichbuf = 1; + else { + tiline[whichbuf] = baseline; +#ifndef lint /* complains of long accuracy */ + Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0); +#endif + if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) + pfatal2("error reading tmp file %s", TMPINNAME); + } + return tibuf[whichbuf] + (tireclen*offline); + } +} + +/* True if the string argument contains the revision number we want. */ + +bool +rev_in_string(string) +char *string; +{ + Reg1 char *s; + Reg2 int patlen; + + if (revision == Nullch) + return TRUE; + patlen = strlen(revision); + if (strnEQ(string,revision,patlen) && isspace(string[patlen])) + return TRUE; + for (s = string; *s; s++) { + if (isspace(*s) && strnEQ(s+1, revision, patlen) && + isspace(s[patlen+1] )) { + return TRUE; + } + } + return FALSE; +} diff --git a/xc/util/patch/inp.h b/xc/util/patch/inp.h new file mode 100644 index 000000000..6fd4ec8f2 --- /dev/null +++ b/xc/util/patch/inp.h @@ -0,0 +1,18 @@ +/* oldHeader: inp.h,v 2.0 86/09/17 15:37:25 lwall Exp $ + * $XConsortium: inp.h,v 2.1 94/09/09 20:04:28 gildea Exp $ + * + * Revision 2.0 86/09/17 15:37:25 lwall + * Baseline for netwide release. + * + */ + +EXT LINENUM input_lines INIT(0); /* how long is input file in lines */ +EXT LINENUM last_frozen_line INIT(0); /* how many input lines have been */ + /* irretractibly output */ + +bool rev_in_string(); +void scan_input(); +bool plan_a(); /* returns false if insufficient memory */ +void plan_b(); +char *ifetch(); + diff --git a/xc/util/patch/malloc.c b/xc/util/patch/malloc.c new file mode 100644 index 000000000..a8436c440 --- /dev/null +++ b/xc/util/patch/malloc.c @@ -0,0 +1,469 @@ +/* + * @(#)nmalloc.c 1 (Caltech) 2/21/82 + * + * U of M Modified: 20 Jun 1983 ACT: strange hacks for Emacs + * + * Nov 1983, Mike@BRL, Added support for 4.1C/4.2 BSD. + * + * This is a very fast storage allocator. It allocates blocks of a small + * number of different sizes, and keeps free lists of each size. Blocks + * that don't exactly fit are passed up to the next larger size. In this + * implementation, the available sizes are (2^n)-4 (or -16) bytes long. + * This is designed for use in a program that uses vast quantities of + * memory, but bombs when it runs out. To make it a little better, it + * warns the user when he starts to get near the end. + * + * June 84, ACT: modified rcheck code to check the range given to malloc, + * rather than the range determined by the 2-power used. + * + * Jan 85, RMS: calls malloc_warning to issue warning on nearly full. + * No longer Emacs-specific; can serve as all-purpose malloc for GNU. + * You should call malloc_init to reinitialize after loading dumped Emacs. + * Call malloc_stats to get info on memory stats if MSTATS turned on. + * realloc knows how to return same block given, just changing its size, + * if the power of 2 is correct. + */ + +/* + * nextf[i] is the pointer to the next free block of size 2^(i+3). The + * smallest allocatable block is 8 bytes. The overhead information will + * go in the first int of the block, and the returned pointer will point + * to the second. + * +#ifdef MSTATS + * nmalloc[i] is the difference between the number of mallocs and frees + * for a given block size. +#endif + */ + +#define ISALLOC ((char) 0xf7) /* magic byte that implies allocation */ +#define ISFREE ((char) 0x54) /* magic byte that implies free block */ + /* this is for error checking only */ + +extern char etext; + +/* end of the program; can be changed by calling init_malloc */ +static char *endofpure = &etext; + +#ifdef MSTATS +static int nmalloc[30]; +static int nmal, nfre; +#endif /* MSTATS */ + +/* If range checking is not turned on, all we have is a flag indicating + whether memory is allocated, an index in nextf[], and a size field; to + realloc() memory we copy either size bytes or 1<<(index+3) bytes depending + on whether the former can hold the exact size (given the value of + 'index'). If range checking is on, we always need to know how much space + is allocated, so the 'size' field is never used. */ + +struct mhead { + char mh_alloc; /* ISALLOC or ISFREE */ + char mh_index; /* index in nextf[] */ +/* Remainder are valid only when block is allocated */ + unsigned short mh_size; /* size, if < 0x10000 */ +#ifdef rcheck + unsigned mh_nbytes; /* number of bytes allocated */ + int mh_magic4; /* should be == MAGIC4 */ +#endif /* rcheck */ + }; + +/* Access free-list pointer of a block. + It is stored at block + 4. + This is not a field in the mhead structure + because we want sizeof (struct mhead) + to describe the overhead for when the block is in use, + and we do not want the free-list pointer to count in that. */ + +#define CHAIN(a) \ + (*(struct mhead **) (sizeof (char *) + (char *) (a))) + +#ifdef rcheck + +/* To implement range checking, we write magic values in at the beginning and + end of each allocated block, and make sure they are undisturbed whenever a + free or a realloc occurs. */ +/* Written in each of the 4 bytes following the block's real space */ +#define MAGIC1 0x55 +/* Written in the 4 bytes before the block's real space */ +#define MAGIC4 0x55555555 +#define ASSERT(p) if (!(p)) botch("p"); else +static +botch(s) + char *s; +{ + + printf("assertion botched: %s\n", s); + abort(); +} +#define EXTRA 4 /* 4 bytes extra for MAGIC1s */ +#else +#define ASSERT(p) +#define EXTRA 0 +#endif /* rcheck */ + +/* nextf[i] is free list of blocks of size 2**(i + 3) */ + +static struct mhead *nextf[30]; + +#ifdef M_WARN +/* Number of bytes of writable memory we can expect to be able to get */ +static int lim_data; +/* Level number of warnings already issued. + 0 -- no warnings issued. + 1 -- 75% warning already issued. + 2 -- 85% warning already issued. +*/ +static int warnlevel; +#endif /* M_WARN */ + +/* nonzero once initial bunch of free blocks made */ +static int gotpool; + +/* Cause reinitialization based on job parameters; + also declare where the end of pure storage is. */ +malloc_init (end) + char *end; { + endofpure = end; +#ifdef M_WARN + lim_data = 0; + warnlevel = 0; +#endif /* M_WARN */ + } + +static +morecore (nu) /* ask system for more memory */ + register int nu; { /* size index to get more of */ + char *sbrk (); + register char *cp; + register int nblks; + register int siz; + +#ifdef M_WARN +#ifndef BSD42 +#ifdef USG + extern long ulimit (); + if (lim_data == 0) /* find out how much we can get */ + lim_data = ulimit (3, 0) - TEXT_START; +#else /*HMS: was endif */ + if (lim_data == 0) /* find out how much we can get */ + lim_data = vlimit (LIM_DATA, -1); +#endif /* USG */ /HMS:* was not here */ +#else + if (lim_data == 0) { + struct rlimit XXrlimit; + + getrlimit (RLIMIT_DATA, &XXrlimit); + lim_data = XXrlimit.rlim_cur;} /* soft limit */ +#endif /* BSD42 */ +#endif /* M_WARN */ + + /* On initial startup, get two blocks of each size up to 1k bytes */ + if (!gotpool) + getpool (), getpool (), gotpool = 1; + + /* Find current end of memory and issue warning if getting near max */ + + cp = sbrk (0); + siz = cp - endofpure; +#ifdef M_WARN + switch (warnlevel) { + case 0: + if (siz > (lim_data / 4) * 3) { + warnlevel++; + malloc_warning ("Warning: past 75% of memory limit");} + break; + case 1: + if (siz > (lim_data / 20) * 17) { + warnlevel++; + malloc_warning ("Warning: past 85% of memory limit");} + break; + case 2: + if (siz > (lim_data / 20) * 19) { + warnlevel++; + malloc_warning ("Warning: past 95% of memory limit");} + break;} +#endif /* M_WARN */ + + if ((int) cp & 0x3ff) /* land on 1K boundaries */ + sbrk (1024 - ((int) cp & 0x3ff)); + + /* Take at least 2k, and figure out how many blocks of the desired size we're about to get */ + nblks = 1; + if ((siz = nu) < 8) + nblks = 1 << ((siz = 8) - nu); + + if ((cp = sbrk (1 << (siz + 3))) == (char *) -1) + return; /* no more room! */ + if ((int) cp & 7) { /* shouldn't happen, but just in case */ + cp = (char *) (((int) cp + 8) & ~7); + nblks--;} + + /* save new header and link the nblks blocks together */ + nextf[nu] = (struct mhead *) cp; + siz = 1 << (nu + 3); + while (1) { + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = nu; + if (--nblks <= 0) break; + CHAIN ((struct mhead *) cp) = (struct mhead *) (cp + siz); + cp += siz;} +#if 0 + CHAIN ((struct mhead *) cp) = 0; /* since sbrk() returns cleared core, this is already set */ +#endif + } + +static +getpool () { + register int nu; + register char *cp = sbrk (0); + + if ((int) cp & 0x3ff) /* land on 1K boundaries */ + sbrk (1024 - ((int) cp & 0x3ff)); + + /* Get 2k of storage */ + + cp = sbrk (04000); + if (cp == (char *) -1) + return; + + /* Divide it into an initial 8-word block + plus one block of size 2**nu for nu = 3 ... 10. */ + + CHAIN (cp) = nextf[0]; + nextf[0] = (struct mhead *) cp; + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = 0; + cp += 8; + + for (nu = 0; nu < 7; nu++) { + CHAIN (cp) = nextf[nu]; + nextf[nu] = (struct mhead *) cp; + ((struct mhead *) cp) -> mh_alloc = ISFREE; + ((struct mhead *) cp) -> mh_index = nu; + cp += 8 << nu;}} + +char * +malloc (n) /* get a block */ + unsigned n; { + register struct mhead *p; + register unsigned int nbytes; + register int nunits = 0; + + /* Figure out how many bytes are required, rounding up to the nearest + multiple of 4, then figure out which nextf[] area to use */ + nbytes = (n + sizeof *p + EXTRA + 3) & ~3; + { + register unsigned int shiftr = (nbytes - 1) >> 2; + + while (shiftr >>= 1) + nunits++; + } + + /* If there are no blocks of the appropriate size, go get some */ + /* COULD SPLIT UP A LARGER BLOCK HERE ... ACT */ + if (nextf[nunits] == 0) + morecore (nunits); + + /* Get one block off the list, and set the new list head */ + if ((p = nextf[nunits]) == 0) + return 0; + nextf[nunits] = CHAIN (p); + + /* Check for free block clobbered */ + /* If not for this check, we would gobble a clobbered free chain ptr */ + /* and bomb out on the NEXT allocate of this size block */ + if (p -> mh_alloc != ISFREE || p -> mh_index != nunits) +#ifdef rcheck + botch ("block on free list clobbered"); +#else + abort (); +#endif /* rcheck */ + + /* Fill in the info, and if range checking, set up the magic numbers */ + p -> mh_alloc = ISALLOC; +#ifdef rcheck + p -> mh_nbytes = n; + p -> mh_magic4 = MAGIC4; + { + register char *m = (char *) (p + 1) + n; + + *m++ = MAGIC1, *m++ = MAGIC1, *m++ = MAGIC1, *m = MAGIC1; + } +#else + p -> mh_size = n; +#endif /* rcheck */ +#ifdef MSTATS + nmalloc[nunits]++; + nmal++; +#endif /* MSTATS */ + return (char *) (p + 1);} + +free (mem) + char *mem; { + register struct mhead *p; + { + register char *ap = mem; + + ASSERT (ap != 0); + p = (struct mhead *) ap - 1; + ASSERT (p -> mh_alloc == ISALLOC); +#ifdef rcheck + ASSERT (p -> mh_magic4 == MAGIC4); + ap += p -> mh_nbytes; + ASSERT (*ap++ == MAGIC1); ASSERT (*ap++ == MAGIC1); + ASSERT (*ap++ == MAGIC1); ASSERT (*ap == MAGIC1); +#endif /* rcheck */ + } + { + register int nunits = p -> mh_index; + + ASSERT (nunits <= 29); + p -> mh_alloc = ISFREE; + CHAIN (p) = nextf[nunits]; + nextf[nunits] = p; +#ifdef MSTATS + nmalloc[nunits]--; + nfre++; +#endif /* MSTATS */ + } + } + +char * +realloc (mem, n) + char *mem; + register unsigned n; { + register struct mhead *p; + register unsigned int tocopy; + register int nbytes; + register int nunits; + + if ((p = (struct mhead *) mem) == 0) + return malloc (n); + p--; + nunits = p -> mh_index; + ASSERT (p -> mh_alloc == ISALLOC); +#ifdef rcheck + ASSERT (p -> mh_magic4 == MAGIC4); + { + register char *m = mem + (tocopy = p -> mh_nbytes); + ASSERT (*m++ == MAGIC1); ASSERT (*m++ == MAGIC1); + ASSERT (*m++ == MAGIC1); ASSERT (*m == MAGIC1); + } +#else + if (p -> mh_index >= 13) + tocopy = (1 << (p -> mh_index + 3)) - sizeof *p; + else + tocopy = p -> mh_size; +#endif /* rcheck */ + + /* See if desired size rounds to same power of 2 as actual size. */ + nbytes = (n + sizeof *p + EXTRA + 7) & ~7; + + /* If ok, use the same block, just marking its size as changed. */ + if (nbytes > (4 << nunits) && nbytes <= (8 << nunits)) { +#ifdef rcheck + register char *m = mem + tocopy; + *m++ = 0; *m++ = 0; *m++ = 0; *m++ = 0; + p-> mh_nbytes = n; + m = mem + n; + *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; *m++ = MAGIC1; +#else + p -> mh_size = n; +#endif /* rcheck */ + return mem;} + + if (n < tocopy) + tocopy = n; + { + register char *new; + void bcopy(); /*HMS: here? */ + + if ((new = malloc (n)) == 0) + return 0; + bcopy (mem, new, tocopy); + free (mem); + return new; + } + } + +#ifdef MSTATS +/* Return statistics describing allocation of blocks of size 2**n. */ + +struct mstats_value { + int blocksize; + int nfree; + int nused; + }; + +struct mstats_value +malloc_stats (size) + int size; { + struct mstats_value v; + register int i; + register struct mhead *p; + + v.nfree = 0; + + if (size < 0 || size >= 30) { + v.blocksize = 0; + v.nused = 0; + return v;} + + v.blocksize = 1 << (size + 3); + v.nused = nmalloc[size]; + + for (p = nextf[size]; p; p = CHAIN (p)) + v.nfree++; + + return v;} +#endif + +/* how much space is available? */ + +unsigned freespace() { + register int i, j; + register struct mhead *p; + register unsigned space = 0; + int local; /* address only is used */ + + space = (char *)&local - sbrk(0); /* stack space */ + + for (i = 0; i < 30; i++) { + for (j = 0, p = nextf[i]; p; p = CHAIN (p), j++) ; + space += j * (1 << (i + 3));} + + return(space);} + +/* How big is this cell? */ + +unsigned mc_size(cp) + char *cp;{ + register struct mhead *p; + + if ((p = (struct mhead *) cp) == 0) { + /*HMS? */ + } + p--; +#ifdef rcheck + return p -> mh_nbytes; +#else + return (1 << (p -> mh_index + 3)) - sizeof *p; +#if 0 + if (p -> mh_index >= 13) + return (1 << (p -> mh_index + 3)) - sizeof *p; + else + return p -> mh_size; +#endif +#endif /* rcheck */ + } + +/*HMS: Really should use memcpy, if available... */ + +void bcopy(source, dest, len) + register char *source, *dest; + register len; { + register i; + + for (i = 0; i < len; i++) + *dest++ = *source++;} diff --git a/xc/util/patch/patch.c b/xc/util/patch/patch.c new file mode 100644 index 000000000..28048be97 --- /dev/null +++ b/xc/util/patch/patch.c @@ -0,0 +1,941 @@ +char rcsid[] = + "$XConsortium: patch.c,v 3.3 94/09/14 21:21:33 gildea Exp $"; +/* "oldHeader: patch.c,v 2.0.2.0 90/05/01 22:17:50 davison Locked $"; */ + +/* patch - a program to apply diffs to original files + * + * Copyright 1986, Larry Wall + * + * This program may be copied as long as you don't try to make any + * money off of it, or pretend that you wrote it. + * + * Revision 2.0.2.0 90/05/01 22:17:50 davison + * patch12u: unidiff support added + * + * Revision 2.0.1.6 88/06/22 20:46:39 lwall + * patch12: rindex() wasn't declared + * + * Revision 2.0.1.5 88/06/03 15:09:37 lwall + * patch10: exit code improved. + * patch10: better support for non-flexfilenames. + * + * Revision 2.0.1.4 87/02/16 14:00:04 lwall + * Short replacement caused spurious "Out of sync" message. + * + * Revision 2.0.1.3 87/01/30 22:45:50 lwall + * Improved diagnostic on sync error. + * Moved do_ed_script() to pch.c. + * + * Revision 2.0.1.2 86/11/21 09:39:15 lwall + * Fuzz factor caused offset of installed lines. + * + * Revision 2.0.1.1 86/10/29 13:10:22 lwall + * Backwards search could terminate prematurely. + * + * Revision 2.0 86/09/17 15:37:32 lwall + * Baseline for netwide release. + * + * Revision 1.5 86/08/01 20:53:24 lwall + * Changed some %d's to %ld's. + * Linted. + * + * Revision 1.4 86/08/01 19:17:29 lwall + * Fixes for machines that can't vararg. + * Added fuzz factor. + * Generalized -p. + * General cleanup. + * + * 85/08/15 van%ucbmonet@berkeley + * Changes for 4.3bsd diff -c. + * + * Revision 1.3 85/03/26 15:07:43 lwall + * Frozen. + * + * Revision 1.2.1.9 85/03/12 17:03:35 lwall + * Changed pfp->_file to fileno(pfp). + * + * Revision 1.2.1.8 85/03/12 16:30:43 lwall + * Check i_ptr and i_womp to make sure they aren't null before freeing. + * Also allow ed output to be suppressed. + * + * Revision 1.2.1.7 85/03/12 15:56:13 lwall + * Added -p option from jromine@uci-750a. + * + * Revision 1.2.1.6 85/03/12 12:12:51 lwall + * Now checks for normalness of file to patch. + * + * Revision 1.2.1.5 85/03/12 11:52:12 lwall + * Added -D (#ifdef) option from joe@fluke. + * + * Revision 1.2.1.4 84/12/06 11:14:15 lwall + * Made smarter about SCCS subdirectories. + * + * Revision 1.2.1.3 84/12/05 11:18:43 lwall + * Added -l switch to do loose string comparison. + * + * Revision 1.2.1.2 84/12/04 09:47:13 lwall + * Failed hunk count not reset on multiple patch file. + * + * Revision 1.2.1.1 84/12/04 09:42:37 lwall + * Branch for sdcrdcf changes. + * + * Revision 1.2 84/11/29 13:29:51 lwall + * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed + * multiple calls to mktemp(). Will now work on machines that can only + * read 32767 chars. Added -R option for diffs with new and old swapped. + * Various cosmetic changes. + * + * Revision 1.1 84/11/09 17:03:58 lwall + * Initial revision + * + */ + +#include "INTERN.h" +#include "common.h" +#include "EXTERN.h" +#include "version.h" +#include "util.h" +#include "pch.h" +#include "inp.h" +#include "backupfile.h" + +/* procedures */ + +void reinitialize_almost_everything(); +void get_some_switches(); +LINENUM locate_hunk(); +void abort_hunk(); +void apply_hunk(); +void init_output(); +void init_reject(); +void copy_till(); +void spew_output(); +void dump_line(); +bool patch_match(); +bool similar(); +void re_input(); +void my_exit(); + +/* TRUE if -E was specified on command line. */ +static int remove_empty_files = FALSE; + +/* TRUE if -R was specified on command line. */ +static int reverse_flag_specified = FALSE; + +/* Apply a set of diffs as appropriate. */ + +int +main(argc,argv) +int argc; +char **argv; +{ + LINENUM where; + LINENUM newwhere; + LINENUM fuzz; + LINENUM mymaxfuzz; + int hunk = 0; + int failed = 0; + int failtotal = 0; + bool rev_okayed = 0; + int i; + + setbuf(stderr, serrbuf); + for (i = 0; i<MAXFILEC; i++) + filearg[i] = Nullch; + +#ifndef WIN32 + myuid = getuid(); +#endif + + /* Cons up the names of the temporary files. */ + { + /* Directory for temporary files. */ + char *tmpdir; + int tmpname_len; + + tmpdir = getenv ("TMPDIR"); + if (tmpdir == NULL) { + tmpdir = "/tmp"; + } + tmpname_len = strlen (tmpdir) + 20; + + TMPOUTNAME = (char *) malloc (tmpname_len); + strcpy (TMPOUTNAME, tmpdir); + strcat (TMPOUTNAME, "/patchoXXXXXX"); + Mktemp(TMPOUTNAME); + + TMPINNAME = (char *) malloc (tmpname_len); + strcpy (TMPINNAME, tmpdir); + strcat (TMPINNAME, "/patchiXXXXXX"); + Mktemp(TMPINNAME); + + TMPREJNAME = (char *) malloc (tmpname_len); + strcpy (TMPREJNAME, tmpdir); + strcat (TMPREJNAME, "/patchrXXXXXX"); + Mktemp(TMPREJNAME); + + TMPPATNAME = (char *) malloc (tmpname_len); + strcpy (TMPPATNAME, tmpdir); + strcat (TMPPATNAME, "/patchpXXXXXX"); + Mktemp(TMPPATNAME); + } + + { + char *v; + + v = getenv ("SIMPLE_BACKUP_SUFFIX"); + if (v) + simple_backup_suffix = v; + else + simple_backup_suffix = ORIGEXT; +#ifndef NODIR + v = getenv ("VERSION_CONTROL"); + backup_type = get_version (v); /* OK to pass NULL. */ +#endif + } + + /* parse switches */ + Argc = argc; + Argv = argv; + get_some_switches(); + + /* make sure we clean up /tmp in case of disaster */ + set_signals(0); + + for ( + open_patch_file(filearg[1]); + there_is_another_patch(); + reinitialize_almost_everything() + ) { /* for each patch in patch file */ + + if (outname == Nullch) + outname = savestr(filearg[0]); + +#ifndef WIN32 + /* for ed script just up and do it and exit */ + if (diff_type == ED_DIFF) { + do_ed_script(); + continue; + } +#endif + + /* initialize the patched file */ + if (!skip_rest_of_patch) + init_output(TMPOUTNAME); + + /* initialize reject file */ + init_reject(TMPREJNAME); + + /* find out where all the lines are */ + if (!skip_rest_of_patch) + scan_input(filearg[0]); + + /* from here on, open no standard i/o files, because malloc */ + /* might misfire and we can't catch it easily */ + + /* apply each hunk of patch */ + hunk = 0; + failed = 0; + rev_okayed = FALSE; + out_of_mem = FALSE; + while (another_hunk()) { + hunk++; + fuzz = Nulline; + mymaxfuzz = pch_context(); + if (maxfuzz < mymaxfuzz) + mymaxfuzz = maxfuzz; + if (!skip_rest_of_patch) { + do { + where = locate_hunk(fuzz); + if (hunk == 1 && where == Nulline && !(force|rev_okayed)) { + /* dwim for reversed patch? */ + if (!pch_swap()) { + if (fuzz == Nulline) + say1( +"Not enough memory to try swapped hunk! Assuming unswapped.\n"); + continue; + } + reverse = !reverse; + where = locate_hunk(fuzz); /* try again */ + if (where == Nulline) { /* didn't find it swapped */ + if (!pch_swap()) /* put it back to normal */ + fatal1("lost hunk on alloc error!\n"); + reverse = !reverse; + } + else if (noreverse) { + if (!pch_swap()) /* put it back to normal */ + fatal1("lost hunk on alloc error!\n"); + reverse = !reverse; + say1( +"Ignoring previously applied (or reversed) patch.\n"); + skip_rest_of_patch = TRUE; + } + else if (batch) { + if (verbose) + say3( +"%seversed (or previously applied) patch detected! %s -R.", + reverse ? "R" : "Unr", + reverse ? "Assuming" : "Ignoring"); + } + else { + ask3( +"%seversed (or previously applied) patch detected! %s -R? [y] ", + reverse ? "R" : "Unr", + reverse ? "Assume" : "Ignore"); + if (*buf == 'n') { + ask1("Apply anyway? [n] "); + if (*buf == 'y') + rev_okayed = TRUE; + else + skip_rest_of_patch = TRUE; + where = Nulline; + reverse = !reverse; + if (!pch_swap()) /* put it back to normal */ + fatal1("lost hunk on alloc error!\n"); + } + } + } + } while (!skip_rest_of_patch && where == Nulline && + ++fuzz <= mymaxfuzz); + + if (skip_rest_of_patch) { /* just got decided */ + Fclose(ofp); + ofp = Nullfp; + } + } + + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { + abort_hunk(); + failed++; + if (verbose) + say3("Hunk #%d ignored at %ld.\n", hunk, newwhere); + } + else if (where == Nulline) { + abort_hunk(); + failed++; + if (verbose) + say3("Hunk #%d failed at %ld.\n", hunk, newwhere); + } + else { + apply_hunk(where); + if (verbose) { + say3("Hunk #%d succeeded at %ld", hunk, newwhere); + if (fuzz) + say2(" with fuzz %ld", fuzz); + if (last_offset) + say3(" (offset %ld line%s)", + last_offset, last_offset==1L?"":"s"); + say1(".\n"); + } + } + } + + if (out_of_mem && using_plan_a) { + Argc = Argc_last; + Argv = Argv_last; + say1("\n\nRan out of memory using Plan A--trying again...\n\n"); + if (ofp) + Fclose(ofp); + ofp = Nullfp; + if (rejfp) + Fclose(rejfp); + rejfp = Nullfp; + continue; + } + + assert(hunk); + + /* finish spewing out the new file */ + if (!skip_rest_of_patch) + spew_output(); + + /* and put the output where desired */ + ignore_signals(); + if (!skip_rest_of_patch) { + struct stat statbuf; + char *realout = outname; + + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = TRUE; + realout = TMPOUTNAME; + chmod(TMPOUTNAME, filemode); + } + else + chmod(outname, filemode); + + if (remove_empty_files && stat(realout, &statbuf) == 0 + && statbuf.st_size == 0) { + if (verbose) + say2("Removing %s (empty after patching).\n", realout); + while (unlink(realout) >= 0) ; /* while is for Eunice. */ + } + } + Fclose(rejfp); + rejfp = Nullfp; + if (failed) { + failtotal += failed; + if (!*rejname) { + Strcpy(rejname, outname); +#ifndef FLEXFILENAMES + { + char *s = rindex(rejname,'/'); + + if (!s) + s = rejname; + if (strlen(s) > 13) + if (s[12] == '.') /* try to preserve difference */ + s[12] = s[13]; /* between .h, .c, .y, etc. */ + s[13] = '\0'; + } +#endif + Strcat(rejname, REJEXT); + } + if (skip_rest_of_patch) { + say4("%d out of %d hunks ignored--saving rejects to %s\n", + failed, hunk, rejname); + } + else { + say4("%d out of %d hunks failed--saving rejects to %s\n", + failed, hunk, rejname); + } + if (move_file(TMPREJNAME, rejname) < 0) + trejkeep = TRUE; + } + set_signals(1); + } + my_exit(failtotal); +} + +/* Prepare to find the next patch to do in the patch file. */ + +void +reinitialize_almost_everything() +{ + re_patch(); + re_input(); + + input_lines = 0; + last_frozen_line = 0; + + filec = 0; + if (filearg[0] != Nullch && !out_of_mem) { + free(filearg[0]); + filearg[0] = Nullch; + } + + if (outname != Nullch) { + free(outname); + outname = Nullch; + } + + last_offset = 0; + + diff_type = 0; + + if (revision != Nullch) { + free(revision); + revision = Nullch; + } + + reverse = reverse_flag_specified; + skip_rest_of_patch = FALSE; + + get_some_switches(); + + if (filec >= 2) + fatal1("you may not change to a different patch file\n"); +} + +static char * +nextarg() +{ + if (!--Argc) + fatal2("missing argument after `%s'\n", *Argv); + return *++Argv; +} + +/* Process switches and filenames up to next '+' or end of list. */ + +void +get_some_switches() +{ + Reg1 char *s; + + rejname[0] = '\0'; + Argc_last = Argc; + Argv_last = Argv; + if (!Argc) + return; + for (Argc--,Argv++; Argc; Argc--,Argv++) { + s = Argv[0]; + if (strEQ(s, "+")) { + return; /* + will be skipped by for loop */ + } + if (*s != '-' || !s[1]) { + if (filec == MAXFILEC) + fatal1("too many file arguments\n"); + filearg[filec++] = savestr(s); + } + else { + switch (*++s) { + case 'b': + simple_backup_suffix = savestr(nextarg()); + break; + case 'B': + origprae = savestr(nextarg()); + break; + case 'c': + diff_type = CONTEXT_DIFF; + break; + case 'd': + if (!*++s) + s = nextarg(); + if (chdir(s) < 0) + pfatal2("can't cd to %s", s); + break; + case 'D': + do_defines = TRUE; + if (!*++s) + s = nextarg(); + if (!isalpha(*s) && '_' != *s) + fatal1("argument to -D is not an identifier\n"); + Sprintf(if_defined, "#ifdef %s\n", s); + Sprintf(not_defined, "#ifndef %s\n", s); + Sprintf(end_defined, "#endif /* %s */\n", s); + break; +#ifndef WIN32 + case 'e': + diff_type = ED_DIFF; + break; +#endif + case 'E': + remove_empty_files = TRUE; + break; + case 'f': + force = TRUE; + break; + case 'F': + if (*++s == '=') + s++; + maxfuzz = atoi(s); + break; + case 'l': + canonicalize = TRUE; + break; + case 'n': + diff_type = NORMAL_DIFF; + break; + case 'N': + noreverse = TRUE; + break; + case 'o': + outname = savestr(nextarg()); + break; + case 'p': + if (*++s == '=') + s++; + strippath = atoi(s); + break; + case 'r': + Strcpy(rejname, nextarg()); + break; + case 'R': + reverse = TRUE; + reverse_flag_specified = TRUE; + break; + case 's': + verbose = FALSE; + break; + case 'S': + skip_rest_of_patch = TRUE; + break; + case 't': + batch = TRUE; + break; + case 'u': + diff_type = UNI_DIFF; + break; + case 'v': + version(); + break; + case 'V': +#ifndef NODIR + backup_type = get_version (nextarg ()); +#endif + break; +#ifdef DEBUGGING + case 'x': + debug = atoi(s+1); + break; +#endif + default: + fprintf(stderr, "patch: unrecognized option `%s'\n", Argv[0]); + fprintf(stderr, "\ +Usage: patch [options] [origfile [patchfile]] [+ [options] [origfile]]...\n\ +Options:\n\ + [-ceEflnNRsStuv] [-b backup-ext] [-B backup-prefix] [-d directory]\n\ + [-D symbol] [-Fmax-fuzz] [-o out-file] [-p[strip-count]]\n\ + [-r rej-name] [-V {numbered,existing,simple}]\n"); + my_exit(1); + } + } + } +} + +/* Attempt to find the right place to apply this hunk of patch. */ + +LINENUM +locate_hunk(fuzz) +LINENUM fuzz; +{ + Reg1 LINENUM first_guess = pch_first() + last_offset; + Reg2 LINENUM offset; + LINENUM pat_lines = pch_ptrn_lines(); + Reg3 LINENUM max_pos_offset = input_lines - first_guess + - pat_lines + 1; + Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + + pch_context(); + + if (!pat_lines) /* null range matches always */ + return first_guess; + if (max_neg_offset >= first_guess) /* do not try lines < 0 */ + max_neg_offset = first_guess - 1; + if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz)) + return first_guess; + for (offset = 1; ; offset++) { + Reg5 bool check_after = (offset <= max_pos_offset); + Reg6 bool check_before = (offset <= max_neg_offset); + + if (check_after && patch_match(first_guess, offset, fuzz)) { +#ifdef DEBUGGING + if (debug & 1) + say3("Offset changing from %ld to %ld\n", last_offset, offset); +#endif + last_offset = offset; + return first_guess+offset; + } + else if (check_before && patch_match(first_guess, -offset, fuzz)) { +#ifdef DEBUGGING + if (debug & 1) + say3("Offset changing from %ld to %ld\n", last_offset, -offset); +#endif + last_offset = -offset; + return first_guess-offset; + } + else if (!check_before && !check_after) + return Nulline; + } +} + +/* We did not find the pattern, dump out the hunk so they can handle it. */ + +void +abort_hunk() +{ + Reg1 LINENUM i; + Reg2 LINENUM pat_end = pch_end(); + /* add in last_offset to guess the same as the previous successful hunk */ + LINENUM oldfirst = pch_first() + last_offset; + LINENUM newfirst = pch_newfirst() + last_offset; + LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; + LINENUM newlast = newfirst + pch_repl_lines() - 1; + char *stars = (diff_type >= NEW_CONTEXT_DIFF ? " ****" : ""); + char *minuses = (diff_type >= NEW_CONTEXT_DIFF ? " ----" : " -----"); + + fprintf(rejfp, "***************\n"); + for (i=0; i<=pat_end; i++) { + switch (pch_char(i)) { + case '*': + if (oldlast < oldfirst) + fprintf(rejfp, "*** 0%s\n", stars); + else if (oldlast == oldfirst) + fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); + else + fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars); + break; + case '=': + if (newlast < newfirst) + fprintf(rejfp, "--- 0%s\n", minuses); + else if (newlast == newfirst) + fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); + else + fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses); + break; + case '\n': + fprintf(rejfp, "%s", pfetch(i)); + break; + case ' ': case '-': case '+': case '!': + fprintf(rejfp, "%c %s", pch_char(i), pfetch(i)); + break; + default: + fatal1("fatal internal error in abort_hunk\n"); + } + } +} + +/* We found where to apply it (we hope), so do it. */ + +void +apply_hunk(where) +LINENUM where; +{ + Reg1 LINENUM old = 1; + Reg2 LINENUM lastline = pch_ptrn_lines(); + Reg3 LINENUM new = lastline+1; +#define OUTSIDE 0 +#define IN_IFNDEF 1 +#define IN_IFDEF 2 +#define IN_ELSE 3 + Reg4 int def_state = OUTSIDE; + Reg5 bool R_do_defines = do_defines; + Reg6 LINENUM pat_end = pch_end(); + + where--; + while (pch_char(new) == '=' || pch_char(new) == '\n') + new++; + + while (old <= lastline) { + if (pch_char(old) == '-') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == OUTSIDE) { + fputs(not_defined, ofp); + def_state = IN_IFNDEF; + } + else if (def_state == IN_IFDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + fputs(pfetch(old), ofp); + } + last_frozen_line++; + old++; + } + else if (new > pat_end) { + break; + } + else if (pch_char(new) == '+') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == IN_IFNDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + else if (def_state == OUTSIDE) { + fputs(if_defined, ofp); + def_state = IN_IFDEF; + } + } + fputs(pfetch(new), ofp); + new++; + } + else if (pch_char(new) != pch_char(old)) { + say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n", + pch_hunk_beg() + old, + pch_hunk_beg() + new); +#ifdef DEBUGGING + say3("oldchar = '%c', newchar = '%c'\n", + pch_char(old), pch_char(new)); +#endif + my_exit(1); + } + else if (pch_char(new) == '!') { + copy_till(where + old - 1); + if (R_do_defines) { + fputs(not_defined, ofp); + def_state = IN_IFNDEF; + } + while (pch_char(old) == '!') { + if (R_do_defines) { + fputs(pfetch(old), ofp); + } + last_frozen_line++; + old++; + } + if (R_do_defines) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + while (pch_char(new) == '!') { + fputs(pfetch(new), ofp); + new++; + } + } + else { + assert(pch_char(new) == ' '); + old++; + new++; + if (R_do_defines && def_state != OUTSIDE) { + fputs(end_defined, ofp); + def_state = OUTSIDE; + } + } + } + if (new <= pat_end && pch_char(new) == '+') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == OUTSIDE) { + fputs(if_defined, ofp); + def_state = IN_IFDEF; + } + else if (def_state == IN_IFNDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + } + while (new <= pat_end && pch_char(new) == '+') { + fputs(pfetch(new), ofp); + new++; + } + } + if (R_do_defines && def_state != OUTSIDE) { + fputs(end_defined, ofp); + } +} + +/* Open the new file. */ + +void +init_output(name) +char *name; +{ +#ifndef WIN32 + ofp = fopen(name, "w"); +#else + ofp = fopen(name, "wb"); +#endif + if (ofp == Nullfp) + pfatal2("can't create %s", name); +} + +/* Open a file to put hunks we can't locate. */ + +void +init_reject(name) +char *name; +{ +#ifndef WIN32 + rejfp = fopen(name, "w"); +#else + rejfp = fopen(name, "wb"); +#endif + if (rejfp == Nullfp) + pfatal2("can't create %s", name); +} + +/* Copy input file to output, up to wherever hunk is to be applied. */ + +void +copy_till(lastline) +Reg1 LINENUM lastline; +{ + Reg2 LINENUM R_last_frozen_line = last_frozen_line; + + if (R_last_frozen_line > lastline) + fatal1("misordered hunks! output would be garbled\n"); + while (R_last_frozen_line < lastline) { + dump_line(++R_last_frozen_line); + } + last_frozen_line = R_last_frozen_line; +} + +/* Finish copying the input file to the output file. */ + +void +spew_output() +{ +#ifdef DEBUGGING + if (debug & 256) + say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line); +#endif + if (input_lines) + copy_till(input_lines); /* dump remainder of file */ + Fclose(ofp); + ofp = Nullfp; +} + +/* Copy one line from input to output. */ + +void +dump_line(line) +LINENUM line; +{ + Reg1 char *s; + Reg2 char R_newline = '\n'; + + /* Note: string is not null terminated. */ + for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ; +} + +/* Does the patch pattern match at line base+offset? */ + +bool +patch_match(base, offset, fuzz) +LINENUM base; +LINENUM offset; +LINENUM fuzz; +{ + Reg1 LINENUM pline = 1 + fuzz; + Reg2 LINENUM iline; + Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz; + + for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) { + if (canonicalize) { + if (!similar(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + else if (strnNE(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + return TRUE; +} + +/* Do two lines match with canonicalized white space? */ + +bool +similar(a,b,len) +Reg1 char *a; +Reg2 char *b; +Reg3 int len; +{ + while (len) { + if (isspace(*b)) { /* whitespace (or \n) to match? */ + if (!isspace(*a)) /* no corresponding whitespace? */ + return FALSE; + while (len && isspace(*b) && *b != '\n') + b++,len--; /* skip pattern whitespace */ + while (isspace(*a) && *a != '\n') + a++; /* skip target whitespace */ + if (*a == '\n' || *b == '\n') + return (*a == *b); /* should end in sync */ + } + else if (*a++ != *b++) /* match non-whitespace chars */ + return FALSE; + else + len--; /* probably not necessary */ + } + return TRUE; /* actually, this is not reached */ + /* since there is always a \n */ +} + +/* Exit with cleanup. */ + +void +my_exit(status) +int status; +{ + Unlink(TMPINNAME); + if (!toutkeep) { + Unlink(TMPOUTNAME); + } + if (!trejkeep) { + Unlink(TMPREJNAME); + } + Unlink(TMPPATNAME); + exit(status); +} diff --git a/xc/util/patch/patch.man b/xc/util/patch/patch.man new file mode 100644 index 000000000..653647b25 --- /dev/null +++ b/xc/util/patch/patch.man @@ -0,0 +1,551 @@ +.\" -*- nroff -*- +.rn '' }` +'\" $XConsortium: patch.man,v 1.2 94/09/09 20:04:28 gildea Exp $ +'\" +'\" Revision 2.0.1.2 88/06/22 20:47:18 lwall +'\" patch12: now avoids Bell System Logo +'\" +'\" Revision 2.0.1.1 88/06/03 15:12:51 lwall +'\" patch10: -B switch was contributed. +'\" +'\" Revision 2.0 86/09/17 15:39:09 lwall +'\" Baseline for netwide release. +'\" +'\" Revision 1.4 86/08/01 19:23:22 lwall +'\" Documented -v, -p, -F. +'\" Added notes to patch senders. +'\" +'\" Revision 1.3 85/03/26 15:11:06 lwall +'\" Frozen. +'\" +'\" Revision 1.2.1.4 85/03/12 16:14:27 lwall +'\" Documented -p. +'\" +'\" Revision 1.2.1.3 85/03/12 16:09:41 lwall +'\" Documented -D. +'\" +'\" Revision 1.2.1.2 84/12/05 11:06:55 lwall +'\" Added -l switch, and noted bistability bug. +'\" +'\" Revision 1.2.1.1 84/12/04 17:23:39 lwall +'\" Branch for sdcrdcf changes. +'\" +'\" Revision 1.2 84/12/04 17:22:02 lwall +'\" Baseline version. +'\" +.de Sh +.br +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +'\" +'\" Set up \*(-- to give an unbreakable dash; +'\" string Tr holds user defined translation string. +'\" Bell System Logo is used as a dummy character. +'\" +'\" Shut up a groff -ww warning. +.if \n(.g .if !dTr .ds Tr +.ie n \{\ +.tr \(*W-\*(Tr +.ds -- \(*W- +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +.ds L' ' +.ds R' ' +'br \} +.el \{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds L' ` +.ds R' ' +'br\} +.TH PATCH 1 LOCAL +.SH NAME +patch - apply a diff file to an original +.SH SYNOPSIS +.B patch +[options] [origfile [patchfile]] [+ [options] [origfile]]... +.sp +but usually just +.sp +.B patch +<patchfile +.SH DESCRIPTION +.I Patch +will take a patch file containing any of the four forms of difference +listing produced by the +.I diff +program and apply those differences to an original file, producing a patched +version. +By default, the patched version is put in place of the original, with +the original file backed up to the same name with the +extension \*(L".orig\*(R" (\*(L"~\*(R" on systems that do not +support long filenames), or as specified by the +.BR -b , +.BR -B , +or +.B -V +switches. +The extension used for making backup files may also be specified in the +.B SIMPLE_BACKUP_SUFFIX +environment variable, which is overridden by above switches. +.PP +If the backup file already exists, +.B patch +creates a new backup file name by changing the first lowercase letter +in the last component of the file's name into uppercase. If there are +no more lowercase letters in the name, it removes the first character +from the name. It repeats this process until it comes up with a +backup file that does not already exist. +.PP +You may also specify where you want the output to go with a +.B -o +switch; if that file already exists, it is backed up first. +.PP +If +.I patchfile +is omitted, or is a hyphen, the patch will be read from standard input. +.PP +Upon startup, patch will attempt to determine the type of the diff listing, +unless over-ruled by a +.BR -c , +.BR -e , +.BR -n , +or +.B -u +switch. +Context diffs (old-style, new-style, and unified) and +normal diffs are applied by the +.I patch +program itself, while ed diffs are simply fed to the +.I ed +editor via a pipe. +.PP +.I Patch +will try to skip any leading garbage, apply the diff, +and then skip any trailing garbage. +Thus you could feed an article or message containing a +diff listing to +.IR patch , +and it should work. +If the entire diff is indented by a consistent amount, +this will be taken into account. +.PP +With context diffs, and to a lesser extent with normal diffs, +.I patch +can detect when the line numbers mentioned in the patch are incorrect, +and will attempt to find the correct place to apply each hunk of the patch. +As a first guess, it takes the line number mentioned for the hunk, plus or +minus any offset used in applying the previous hunk. +If that is not the correct place, +.I patch +will scan both forwards and backwards for a set of lines matching the context +given in the hunk. +First +.I patch +looks for a place where all lines of the context match. +If no such place is found, and it's a context diff, and the maximum fuzz factor +is set to 1 or more, then another scan takes place ignoring the first and last +line of context. +If that fails, and the maximum fuzz factor is set to 2 or more, +the first two and last two lines of context are ignored, +and another scan is made. +(The default maximum fuzz factor is 2.) +If +.I patch +cannot find a place to install that hunk of the patch, it will put the +hunk out to a reject file, which normally is the name of the output file +plus \*(L".rej\*(R" (\*(L"#\*(R" on systems that do not support +long filenames). +(Note that the rejected hunk will come out in context diff form whether the +input patch was a context diff or a normal diff. +If the input was a normal diff, many of the contexts will simply be null.) +The line numbers on the hunks in the reject file may be different than +in the patch file: they reflect the approximate location patch thinks the +failed hunks belong in the new file rather than the old one. +.PP +As each hunk is completed, you will be told whether the hunk succeeded or +failed, and which line (in the new file) +.I patch +thought the hunk should go on. +If this is different from the line number specified in the diff you will +be told the offset. +A single large offset MAY be an indication that a hunk was installed in the +wrong place. +You will also be told if a fuzz factor was used to make the match, in which +case you should also be slightly suspicious. +.PP +If no original file is specified on the command line, +.I patch +will try to figure out from the leading garbage what the name of the file +to edit is. +In the header of a context diff, the filename is found from lines beginning +with \*(L"***\*(R" or \*(L"---\*(R", with the shortest name of an existing +file winning. +Only context diffs have lines like that, but if there is an \*(L"Index:\*(R" +line in the leading garbage, +.I patch +will try to use the filename from that line. +The context diff header takes precedence over an Index line. +If no filename can be intuited from the leading garbage, you will be asked +for the name of the file to patch. +.PP +If the original file cannot be found or is read-only, but a suitable +SCCS or RCS file is handy, +.I patch +will attempt to get or check out the file. +.PP +Additionally, if the leading garbage contains a \*(L"Prereq: \*(R" line, +.I patch +will take the first word from the prerequisites line (normally a version +number) and check the input file to see if that word can be found. +If not, +.I patch +will ask for confirmation before proceeding. +.PP +The upshot of all this is that you should be able to say, while in a news +interface, the following: +.Sp + | patch -d /usr/src/local/blurfl +.Sp +and patch a file in the blurfl directory directly from the article containing +the patch. +.PP +If the patch file contains more than one patch, +.I patch +will try to apply each of them as if they came from separate patch files. +This means, among other things, that it is assumed that the name of the file +to patch must be determined for each diff listing, +and that the garbage before each diff listing will +be examined for interesting things such as filenames and revision level, as +mentioned previously. +You can give switches (and another original file name) for the second and +subsequent patches by separating the corresponding argument lists +by a \*(L'+\*(R'. +(The argument list for a second or subsequent patch may not specify a new +patch file, however.) +.PP +.I Patch +recognizes the following switches: +.TP 5 +.B \-b +causes the next argument to be interpreted as the backup extension, to be +used in place of \*(L".orig\*(R" or \*(L"~\*(R". +.TP 5 +.B \-B +causes the next argument to be interpreted as a prefix to the backup file +name. If this argument is specified any argument from -b will be ignored. +.TP 5 +.B \-c +forces +.I patch +to interpret the patch file as a context diff. +.TP 5 +.B \-d +causes +.I patch +to interpret the next argument as a directory, and cd to it before doing +anything else. +.TP 5 +.B \-D +causes +.I patch +to use the "#ifdef...#endif" construct to mark changes. +The argument following will be used as the differentiating symbol. +Note that, unlike the C compiler, there must be a space between the +.B \-D +and the argument. +.TP 5 +.B \-e +forces +.I patch +to interpret the patch file as an ed script. +.TP 5 +.B \-E +causes +.I patch +to remove output files that are empty after the patches have been applied. +.TP 5 +.B \-f +forces +.I patch +to assume that the user knows exactly what he or she is doing, and to not +ask any questions. It assumes the following: skip patches for which a +file to patch can't be found; patch files even though they have the +wrong version for the ``Prereq:'' line in the patch; and assume that +patches are not reversed even if they look like they are. +This option does not suppress commentary; use +.B \-s +for that. +.TP 5 +.B \-t +similar to +.BR \-f , +in that it suppresses questions, but makes some different assumptions: +skip patches for which a file to patch can't be found (the same as \fB\-f\fP); +skip patches for which the file has the wrong version for the ``Prereq:'' line +in the patch; and assume that patches are reversed if they look like +they are. +.TP 5 +.B \-F<number> +sets the maximum fuzz factor. +This switch only applies to context diffs, and causes +.I patch +to ignore up to that many lines in looking for places to install a hunk. +Note that a larger fuzz factor increases the odds of a faulty patch. +The default fuzz factor is 2, and it may not be set to more than +the number of lines of context in the context diff, ordinarily 3. +.TP 5 +.B \-l +causes the pattern matching to be done loosely, in case the tabs and +spaces have been munged in your input file. +Any sequence of whitespace in the pattern line will match any sequence +in the input file. +Normal characters must still match exactly. +Each line of the context must still match a line in the input file. +.TP 5 +.B \-n +forces +.I patch +to interpret the patch file as a normal diff. +.TP 5 +.B \-N +causes +.I patch +to ignore patches that it thinks are reversed or already applied. +See also +.B \-R . +.TP 5 +.B \-o +causes the next argument to be interpreted as the output file name. +.TP 5 +.B \-p<number> +sets the pathname strip count, +which controls how pathnames found in the patch file are treated, in case +the you keep your files in a different directory than the person who sent +out the patch. +The strip count specifies how many slashes are to be stripped from +the front of the pathname. +(Any intervening directory names also go away.) +For example, supposing the filename in the patch file was +.sp + /u/howard/src/blurfl/blurfl.c +.sp +setting +.B \-p +or +.B \-p0 +gives the entire pathname unmodified, +.B \-p1 +gives +.sp + u/howard/src/blurfl/blurfl.c +.sp +without the leading slash, +.B \-p4 +gives +.sp + blurfl/blurfl.c +.sp +and not specifying +.B \-p +at all just gives you "blurfl.c", unless all of the directories in the +leading path (u/howard/src/blurfl) exist and that path is relative, +in which case you get the entire pathname unmodified. +Whatever you end up with is looked for either in the current directory, +or the directory specified by the +.B \-d +switch. +.TP 5 +.B \-r +causes the next argument to be interpreted as the reject file name. +.TP 5 +.B \-R +tells +.I patch +that this patch was created with the old and new files swapped. +(Yes, I'm afraid that does happen occasionally, human nature being what it +is.) +.I Patch +will attempt to swap each hunk around before applying it. +Rejects will come out in the swapped format. +The +.B \-R +switch will not work with ed diff scripts because there is too little +information to reconstruct the reverse operation. +.Sp +If the first hunk of a patch fails, +.I patch +will reverse the hunk to see if it can be applied that way. +If it can, you will be asked if you want to have the +.B \-R +switch set. +If it can't, the patch will continue to be applied normally. +(Note: this method cannot detect a reversed patch if it is a normal diff +and if the first command is an append (i.e. it should have been a delete) +since appends always succeed, due to the fact that a null context will match +anywhere. +Luckily, most patches add or change lines rather than delete them, so most +reversed normal diffs will begin with a delete, which will fail, triggering +the heuristic.) +.TP 5 +.B \-s +makes +.I patch +do its work silently, unless an error occurs. +.TP 5 +.B \-S +causes +.I patch +to ignore this patch from the patch file, but continue on looking +for the next patch in the file. +Thus +.sp + patch -S + -S + <patchfile +.sp +will ignore the first and second of three patches. +.TP 5 +.B \-u +forces +.I patch +to interpret the patch file as a unified context diff (a unidiff). +.TP 5 +.B \-v +causes +.I patch +to print out its revision header and patch level. +.TP 5 +.B \-V +causes the next argument to be interpreted as a method for creating +backup file names. The type of backups made can also be given in the +.B VERSION_CONTROL +environment variable, which is overridden by this option. +The +.B -B +option overrides this option, causing the prefix to always be used for +making backup file names. +The value of the +.B VERSION_CONTROL +environment variable and the argument to the +.B -V +option are like the GNU +Emacs `version-control' variable; they also recognize synonyms that +are more descriptive. The valid values are (unique abbreviations are +accepted): +.RS +.TP +`t' or `numbered' +Always make numbered backups. +.TP +`nil' or `existing' +Make numbered backups of files that already +have them, simple backups of the others. +This is the default. +.TP +`never' or `simple' +Always make simple backups. +.RE +.TP 5 +.B \-x<number> +sets internal debugging flags, and is of interest only to +.I patch +patchers. +.SH AUTHOR +Larry Wall <lwall@netlabs.com> +.br +with many other contributors. +.SH ENVIRONMENT +.TP +.B TMPDIR +Directory to put temporary files in; default is /tmp. +.TP +.B SIMPLE_BACKUP_SUFFIX +Extension to use for backup file names instead of \*(L".orig\*(R" or +\*(L"~\*(R". +.TP +.B VERSION_CONTROL +Selects when numbered backup files are made. +.SH FILES +$TMPDIR/patch* +.SH SEE ALSO +diff(1) +.SH NOTES FOR PATCH SENDERS +There are several things you should bear in mind if you are going to +be sending out patches. +First, you can save people a lot of grief by keeping a patchlevel.h file +which is patched to increment the patch level as the first diff in the +patch file you send out. +If you put a Prereq: line in with the patch, it won't let them apply +patches out of order without some warning. +Second, make sure you've specified the filenames right, either in a +context diff header, or with an Index: line. +If you are patching something in a subdirectory, be sure to tell the patch +user to specify a +.B \-p +switch as needed. +Third, you can create a file by sending out a diff that compares a +null file to the file you want to create. +This will only work if the file you want to create doesn't exist already in +the target directory. +Fourth, take care not to send out reversed patches, since it makes people wonder +whether they already applied the patch. +Fifth, while you may be able to get away with putting 582 diff listings into +one file, it is probably wiser to group related patches into separate files in +case something goes haywire. +.SH DIAGNOSTICS +Too many to list here, but generally indicative that +.I patch +couldn't parse your patch file. +.PP +The message \*(L"Hmm...\*(R" indicates that there is unprocessed text in +the patch file and that +.I patch +is attempting to intuit whether there is a patch in that text and, if so, +what kind of patch it is. +.PP +.I Patch +will exit with a non-zero status if any reject files were created. +When applying a set of patches in a loop it behooves you to check this +exit status so you don't apply a later patch to a partially patched file. +.SH CAVEATS +.I Patch +cannot tell if the line numbers are off in an ed script, and can only detect +bad line numbers in a normal diff when it finds a \*(L"change\*(R" or +a \*(L"delete\*(R" command. +A context diff using fuzz factor 3 may have the same problem. +Until a suitable interactive interface is added, you should probably do +a context diff in these cases to see if the changes made sense. +Of course, compiling without errors is a pretty good indication that the patch +worked, but not always. +.PP +.I Patch +usually produces the correct results, even when it has to do a lot of +guessing. +However, the results are guaranteed to be correct only when the patch is +applied to exactly the same version of the file that the patch was +generated from. +.SH BUGS +Could be smarter about partial matches, excessively \&deviant offsets and +swapped code, but that would take an extra pass. +.PP +If code has been duplicated (for instance with #ifdef OLDCODE ... #else ... +#endif), +.I patch +is incapable of patching both versions, and, if it works at all, will likely +patch the wrong one, and tell you that it succeeded to boot. +.PP +If you apply a patch you've already applied, +.I patch +will think it is a reversed patch, and offer to un-apply the patch. +This could be construed as a feature. +.rn }` '' diff --git a/xc/util/patch/patchlevel.h b/xc/util/patch/patchlevel.h new file mode 100644 index 000000000..c1169f19a --- /dev/null +++ b/xc/util/patch/patchlevel.h @@ -0,0 +1,2 @@ +/* $XConsortium: patchlevel.h,v 3.2 94/09/14 21:31:55 gildea Exp $ */ +#define PATCHLEVEL "12u9.XC2" diff --git a/xc/util/patch/pch.c b/xc/util/patch/pch.c new file mode 100644 index 000000000..5bb7fd141 --- /dev/null +++ b/xc/util/patch/pch.c @@ -0,0 +1,1313 @@ +/* oldHeader: pch.c,v 2.0.1.7 88/06/03 15:13:28 lwall Locked $ + * $XConsortium: pch.c,v 3.3 94/09/14 21:22:55 gildea Exp $ + * + * Revision 2.0.2.0 90/05/01 22:17:51 davison + * patch12u: unidiff support added + * + * Revision 2.0.1.7 88/06/03 15:13:28 lwall + * patch10: Can now find patches in shar scripts. + * patch10: Hunks that swapped and then swapped back could core dump. + * + * Revision 2.0.1.6 87/06/04 16:18:13 lwall + * pch_swap didn't swap p_bfake and p_efake. + * + * Revision 2.0.1.5 87/01/30 22:47:42 lwall + * Improved responses to mangled patches. + * + * Revision 2.0.1.4 87/01/05 16:59:53 lwall + * New-style context diffs caused double call to free(). + * + * Revision 2.0.1.3 86/11/14 10:08:33 lwall + * Fixed problem where a long pattern wouldn't grow the hunk. + * Also restored p_input_line when backtracking so error messages are right. + * + * Revision 2.0.1.2 86/11/03 17:49:52 lwall + * New-style delete triggers spurious assertion error. + * + * Revision 2.0.1.1 86/10/29 15:52:08 lwall + * Could falsely report new-style context diff. + * + * Revision 2.0 86/09/17 15:39:37 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "pch.h" + +/* Patch (diff listing) abstract type. */ + +static long p_filesize; /* size of the patch file */ +static LINENUM p_first; /* 1st line number */ +static LINENUM p_newfirst; /* 1st line number of replacement */ +static LINENUM p_ptrn_lines; /* # lines in pattern */ +static LINENUM p_repl_lines; /* # lines in replacement text */ +static LINENUM p_end = -1; /* last line in hunk */ +static LINENUM p_max; /* max allowed value of p_end */ +static LINENUM p_context = 3; /* # of context lines */ +static LINENUM p_input_line = 0; /* current line # from patch file */ +static char **p_line = Null(char**); /* the text of the hunk */ +static short *p_len = Null(short*); /* length of each line */ +static char *p_char = Nullch; /* +, -, and ! */ +static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ +static int p_indent; /* indent to patch */ +static LINENUM p_base; /* where to intuit this time */ +static LINENUM p_bline; /* line # of p_base */ +static LINENUM p_start; /* where intuit found a patch */ +static LINENUM p_sline; /* and the line number for it */ +static LINENUM p_hunk_beg; /* line number of current hunk */ +static LINENUM p_efake = -1; /* end of faked up lines--don't free */ +static LINENUM p_bfake = -1; /* beg of faked up lines */ + +/* Prepare to look for the next patch in the patch file. */ + +void +re_patch() +{ + p_first = Nulline; + p_newfirst = Nulline; + p_ptrn_lines = Nulline; + p_repl_lines = Nulline; + p_end = (LINENUM)-1; + p_max = Nulline; + p_indent = 0; +} + +/* Open the patch file at the beginning of time. */ + +void +open_patch_file(filename) +char *filename; +{ + if (filename == Nullch || !*filename || strEQ(filename, "-")) { + pfp = fopen(TMPPATNAME, "w"); + if (pfp == Nullfp) + pfatal2("can't create %s", TMPPATNAME); + while (fgets(buf, sizeof buf, stdin) != Nullch) + fputs(buf, pfp); + Fclose(pfp); + filename = TMPPATNAME; + } + pfp = fopen(filename, "r"); + if (pfp == Nullfp) + pfatal2("patch file %s not found", filename); + Fstat(fileno(pfp), &filestat); + p_filesize = filestat.st_size; + next_intuit_at(0L,1L); /* start at the beginning */ + set_hunkmax(); +} + +/* Make sure our dynamically realloced tables are malloced to begin with. */ + +void +set_hunkmax() +{ +#ifndef lint + if (p_line == Null(char**)) + p_line = (char**) malloc((MEM)hunkmax * sizeof(char *)); + if (p_len == Null(short*)) + p_len = (short*) malloc((MEM)hunkmax * sizeof(short)); +#endif + if (p_char == Nullch) + p_char = (char*) malloc((MEM)hunkmax * sizeof(char)); +} + +/* Enlarge the arrays containing the current hunk of patch. */ + +void +grow_hunkmax() +{ + hunkmax *= 2; + /* + * Note that on most systems, only the p_line array ever gets fresh memory + * since p_len can move into p_line's old space, and p_char can move into + * p_len's old space. Not on PDP-11's however. But it doesn't matter. + */ + assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch); +#ifndef lint + p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *)); + p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short)); + p_char = (char*) realloc((char*)p_char, (MEM)hunkmax * sizeof(char)); +#endif + if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch) + return; + if (!using_plan_a) + fatal1("out of memory\n"); + out_of_mem = TRUE; /* whatever is null will be allocated again */ + /* from within plan_a(), of all places */ +} + +/* True if the remainder of the patch file contains a diff of some sort. */ + +bool +there_is_another_patch() +{ + if (p_base != 0L && p_base >= p_filesize) { + if (verbose) + say1("done\n"); + return FALSE; + } + if (verbose) + say1("Hmm..."); + diff_type = intuit_diff_type(); + if (!diff_type) { + if (p_base != 0L) { + if (verbose) + say1(" Ignoring the trailing garbage.\ndone\n"); + } + else + say1(" I can't seem to find a patch in there anywhere.\n"); + return FALSE; + } + if (verbose) + say3(" %sooks like %s to me...\n", + (p_base == 0L ? "L" : "The next patch l"), + diff_type == UNI_DIFF ? "a unified diff" : + diff_type == CONTEXT_DIFF ? "a context diff" : + diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : + diff_type == NORMAL_DIFF ? "a normal diff" : + "an ed script" ); + if (p_indent && verbose) + say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); + skip_to(p_start,p_sline); + while (filearg[0] == Nullch) { + if (force || batch) { + say1("No file to patch. Skipping...\n"); + filearg[0] = savestr(bestguess); + skip_rest_of_patch = TRUE; + return TRUE; + } + ask1("File to patch: "); + if (*buf != '\n') { + if (bestguess) + free(bestguess); + bestguess = savestr(buf); + filearg[0] = fetchname(buf, 0, FALSE); + } + if (filearg[0] == Nullch) { +#ifndef WIN32 + ask1("No file found--skip this patch? [n] "); + if (*buf != 'y') { + continue; + } + if (verbose) + say1("Skipping patch...\n"); +#else + if (verbose) + say1("File not found--skipping patch...\n"); +#endif + filearg[0] = fetchname(bestguess, 0, TRUE); + skip_rest_of_patch = TRUE; + return TRUE; + } + } + return TRUE; +} + +/* Determine what kind of diff is in the remaining part of the patch file. */ + +int +intuit_diff_type() +{ + Reg4 long this_line = 0; + Reg5 long previous_line; + Reg6 long first_command_line = -1; + long fcl_line; + Reg7 bool last_line_was_command = FALSE; + Reg8 bool this_is_a_command = FALSE; + Reg9 bool stars_last_line = FALSE; + Reg10 bool stars_this_line = FALSE; + Reg3 int indent; + Reg1 char *s; + Reg2 char *t; + char *indtmp = Nullch; + char *oldtmp = Nullch; + char *newtmp = Nullch; + char *indname = Nullch; + char *oldname = Nullch; + char *newname = Nullch; + Reg11 int retval; + bool no_filearg = (filearg[0] == Nullch); + + ok_to_create_file = FALSE; + Fseek(pfp, p_base, 0); + p_input_line = p_bline - 1; + for (;;) { + previous_line = this_line; + last_line_was_command = this_is_a_command; + stars_last_line = stars_this_line; + this_line = ftell(pfp); + indent = 0; + p_input_line++; + if (fgets(buf, sizeof buf, pfp) == Nullch) { + if (first_command_line >= 0L) { + /* nothing but deletes!? */ + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + else { + p_start = this_line; + p_sline = p_input_line; + retval = 0; + goto scan_exit; + } + } + for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { + if (*s == '\t') + indent += 8 - (indent % 8); + else + indent++; + } + for (t=s; isdigit(*t) || *t == ','; t++) ; + this_is_a_command = (isdigit(*s) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (first_command_line < 0L && this_is_a_command) { + first_command_line = this_line; + fcl_line = p_input_line; + p_indent = indent; /* assume this for now */ + } + if (!stars_last_line && strnEQ(s, "*** ", 4)) + oldtmp = savestr(s+4); + else if (strnEQ(s, "--- ", 4)) + newtmp = savestr(s+4); + else if (strnEQ(s, "+++ ", 4)) + oldtmp = savestr(s+4); /* pretend it is the old name */ + else if (strnEQ(s, "Index:", 6)) + indtmp = savestr(s+6); + else if (strnEQ(s, "Prereq:", 7)) { + for (t=s+7; isspace(*t); t++) ; + revision = savestr(t); + for (t=revision; *t && !isspace(*t); t++) ; + *t = '\0'; + if (!*revision) { + free(revision); + revision = Nullch; + } + } + if ((!diff_type || diff_type == ED_DIFF) && + first_command_line >= 0L && + strEQ(s, ".\n") ) { + p_indent = indent; + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + if ((!diff_type || diff_type == UNI_DIFF) && strnEQ(s, "@@ -", 4)) { + if (!atol(s+3)) + ok_to_create_file = TRUE; + p_indent = indent; + p_start = this_line; + p_sline = p_input_line; + retval = UNI_DIFF; + goto scan_exit; + } + stars_this_line = strnEQ(s, "********", 8); + if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && + strnEQ(s, "*** ", 4)) { + if (!atol(s+4)) + ok_to_create_file = TRUE; + /* if this is a new context diff the character just before */ + /* the newline is a '*'. */ + while (*s != '\n') + s++; + p_indent = indent; + p_start = previous_line; + p_sline = p_input_line - 1; + retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); + goto scan_exit; + } + if ((!diff_type || diff_type == NORMAL_DIFF) && + last_line_was_command && + (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { + p_start = previous_line; + p_sline = p_input_line - 1; + p_indent = indent; + retval = NORMAL_DIFF; + goto scan_exit; + } + } + scan_exit: + if (no_filearg) { + if (indtmp != Nullch) + indname = fetchname(indtmp, strippath, ok_to_create_file); + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, ok_to_create_file); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, ok_to_create_file); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + filearg[0] = savestr(oldname); + else + filearg[0] = savestr(newname); + } + else if (oldname) + filearg[0] = savestr(oldname); + else if (newname) + filearg[0] = savestr(newname); + else if (indname) + filearg[0] = savestr(indname); + } + if (bestguess) { + free(bestguess); + bestguess = Nullch; + } + if (filearg[0] != Nullch) + bestguess = savestr(filearg[0]); + else if (indtmp != Nullch) + bestguess = fetchname(indtmp, strippath, TRUE); + else { + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, TRUE); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, TRUE); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + bestguess = savestr(oldname); + else + bestguess = savestr(newname); + } + else if (oldname) + bestguess = savestr(oldname); + else if (newname) + bestguess = savestr(newname); + } + if (indtmp != Nullch) + free(indtmp); + if (oldtmp != Nullch) + free(oldtmp); + if (newtmp != Nullch) + free(newtmp); + if (indname != Nullch) + free(indname); + if (oldname != Nullch) + free(oldname); + if (newname != Nullch) + free(newname); + return retval; +} + +/* Remember where this patch ends so we know where to start up again. */ + +void +next_intuit_at(file_pos,file_line) +long file_pos; +long file_line; +{ + p_base = file_pos; + p_bline = file_line; +} + +/* Basically a verbose fseek() to the actual diff listing. */ + +void +skip_to(file_pos,file_line) +long file_pos; +long file_line; +{ + char *ret; + + assert(p_base <= file_pos); + if (verbose && p_base < file_pos) { + Fseek(pfp, p_base, 0); + say1("The text leading up to this was:\n--------------------------\n"); + while (ftell(pfp) < file_pos) { + ret = fgets(buf, sizeof buf, pfp); + assert(ret != Nullch); + say2("|%s", buf); + } + say1("--------------------------\n"); + } + else + Fseek(pfp, file_pos, 0); + p_input_line = file_line - 1; +} + +/* Make this a function for better debugging. */ +static void +malformed () +{ + fatal3("malformed patch at line %ld: %s", p_input_line, buf); + /* about as informative as "Syntax error" in C */ +} + +/* True if there is more of the current diff listing to process. */ + +bool +another_hunk() +{ + Reg1 char *s; + Reg8 char *ret; + Reg2 int context = 0; + + while (p_end >= 0) { + if (p_end == p_efake) + p_end = p_bfake; /* don't free twice */ + else + free(p_line[p_end]); + p_end--; + } + assert(p_end == -1); + p_efake = -1; + + p_max = hunkmax; /* gets reduced when --- found */ + if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { + long line_beginning = ftell(pfp); + /* file pos of the current line */ + LINENUM repl_beginning = 0; /* index of --- line */ + Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ + Reg5 LINENUM fillsrc; /* index of first line to copy */ + Reg6 LINENUM filldst; /* index of first missing line */ + bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ + Reg9 bool repl_could_be_missing = TRUE; + /* no + or ! lines in this hunk */ + bool repl_missing = FALSE; /* we are now backtracking */ + long repl_backtrack_position = 0; + /* file pos of first repl line */ + LINENUM repl_patch_line; /* input line number for same */ + Reg7 LINENUM ptrn_copiable = 0; + /* # of copiable lines in ptrn */ + + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || strnNE(buf, "********", 8)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + p_context = 100; + p_hunk_beg = p_input_line + 1; + while (p_end < p_max) { + line_beginning = ftell(pfp); + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) { + if (p_max - p_end < 4) + Strcpy(buf, " \n"); /* assume blank lines got chopped */ + else { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal1("unexpected end of file in patch\n"); + } + } + p_end++; + assert(p_end < hunkmax); + p_char[p_end] = *buf; +#ifdef zilog + p_line[(short)p_end] = Nullch; +#else + p_line[p_end] = Nullch; +#endif + switch (*buf) { + case '*': + if (strnEQ(buf, "********", 8)) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + else + fatal2("unexpected end of hunk at line %ld\n", + p_input_line); + } + if (p_end != 0) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal3("unexpected *** at line %ld: %s", p_input_line, buf); + } + context = 0; + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } + for (s=buf; *s && !isdigit(*s); s++) ; + if (!*s) + malformed (); + if (strnEQ(s,"0,0",3)) + strcpy(s,s+2); + p_first = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + malformed (); + p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; + } + else if (p_first) + p_ptrn_lines = 1; + else { + p_ptrn_lines = 0; + p_first = 1; + } + p_max = p_ptrn_lines + 6; /* we need this much at least */ + while (p_max >= hunkmax) + grow_hunkmax(); + p_max = hunkmax; + break; + case '-': + if (buf[1] == '-') { + if (repl_beginning || + (p_end != p_ptrn_lines + 1 + (p_char[p_end-1] == '\n'))) + { + if (p_end == 1) { + /* `old' lines were omitted - set up to fill */ + /* them in from 'new' context lines. */ + p_end = p_ptrn_lines + 1; + fillsrc = p_end + 1; + filldst = 1; + fillcnt = p_ptrn_lines; + } + else { + if (repl_beginning) { + if (repl_could_be_missing){ + repl_missing = TRUE; + goto hunk_done; + } + fatal3( +"duplicate \"---\" at line %ld--check line numbers at line %ld\n", + p_input_line, p_hunk_beg + repl_beginning); + } + else { + fatal4( +"%s \"---\" at line %ld--check line numbers at line %ld\n", + (p_end <= p_ptrn_lines + ? "Premature" + : "Overdue" ), + p_input_line, p_hunk_beg); + } + } + } + repl_beginning = p_end; + repl_backtrack_position = ftell(pfp); + repl_patch_line = p_input_line; + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } + p_char[p_end] = '='; + for (s=buf; *s && !isdigit(*s); s++) ; + if (!*s) + malformed (); + p_newfirst = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + malformed (); + p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1; + } + else if (p_newfirst) + p_repl_lines = 1; + else { + p_repl_lines = 0; + p_newfirst = 1; + } + p_max = p_repl_lines + p_end; + if (p_max > MAXHUNKSIZE) + fatal4("hunk too large (%ld lines) at line %ld: %s", + p_max, p_input_line, buf); + while (p_max >= hunkmax) + grow_hunkmax(); + if (p_repl_lines != ptrn_copiable + && (p_context != 0 || p_repl_lines != 1)) + repl_could_be_missing = FALSE; + break; + } + goto change_line; + case '+': case '!': + repl_could_be_missing = FALSE; + change_line: + if (buf[1] == '\n' && canonicalize) + strcpy(buf+1," \n"); + if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' && + repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + if (context >= 0) { + if (context < p_context) + p_context = context; + context = -1000; + } + p_line[p_end] = savestr(buf+2); + if (out_of_mem) { + p_end--; + return FALSE; + } + break; + case '\t': case '\n': /* assume the 2 spaces got eaten */ + if (repl_beginning && repl_could_be_missing && + (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) { + repl_missing = TRUE; + goto hunk_done; + } + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } + if (p_end != p_ptrn_lines + 1) { + ptrn_spaces_eaten |= (repl_beginning != 0); + context++; + if (!repl_beginning) + ptrn_copiable++; + p_char[p_end] = ' '; + } + break; + case ' ': + if (!isspace(buf[1]) && + repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + context++; + if (!repl_beginning) + ptrn_copiable++; + p_line[p_end] = savestr(buf+2); + if (out_of_mem) { + p_end--; + return FALSE; + } + break; + default: + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + malformed (); + } + /* set up p_len for strncmp() so we don't have to */ + /* assume null termination */ + if (p_line[p_end]) + p_len[p_end] = strlen(p_line[p_end]); + else + p_len[p_end] = 0; + } + + hunk_done: + if (p_end >=0 && !repl_beginning) + fatal2("no --- found in patch at line %ld\n", pch_hunk_beg()); + + if (repl_missing) { + + /* reset state back to just after --- */ + p_input_line = repl_patch_line; + for (p_end--; p_end > repl_beginning; p_end--) + free(p_line[p_end]); + Fseek(pfp, repl_backtrack_position, 0); + + /* redundant 'new' context lines were omitted - set */ + /* up to fill them in from the old file context */ + if (!p_context && p_repl_lines == 1) { + p_repl_lines = 0; + p_max--; + } + fillsrc = 1; + filldst = repl_beginning+1; + fillcnt = p_repl_lines; + p_end = p_max; + } + else if (!p_context && fillcnt == 1) { + /* the first hunk was a null hunk with no context */ + /* and we were expecting one line -- fix it up. */ + while (filldst < p_end) { + p_line[filldst] = p_line[filldst+1]; + p_char[filldst] = p_char[filldst+1]; + p_len[filldst] = p_len[filldst+1]; + filldst++; + } +#if 0 + repl_beginning--; /* this doesn't need to be fixed */ +#endif + p_end--; + p_first++; /* do append rather than insert */ + fillcnt = 0; + p_ptrn_lines = 0; + } + + if (diff_type == CONTEXT_DIFF && + (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) { + if (verbose) + say4("%s\n%s\n%s\n", +"(Fascinating--this is really a new-style context diff but without", +"the telltale extra asterisks on the *** line that usually indicate", +"the new style...)"); + diff_type = NEW_CONTEXT_DIFF; + } + + /* if there were omitted context lines, fill them in now */ + if (fillcnt) { + p_bfake = filldst; /* remember where not to free() */ + p_efake = filldst + fillcnt - 1; + while (fillcnt-- > 0) { + while (fillsrc <= p_end && p_char[fillsrc] != ' ') + fillsrc++; + if (fillsrc > p_end) + fatal2("replacement text or line numbers mangled in hunk at line %ld\n", + p_hunk_beg); + p_line[filldst] = p_line[fillsrc]; + p_char[filldst] = p_char[fillsrc]; + p_len[filldst] = p_len[fillsrc]; + fillsrc++; filldst++; + } + while (fillsrc <= p_end && fillsrc != repl_beginning && + p_char[fillsrc] != ' ') + fillsrc++; +#ifdef DEBUGGING + if (debug & 64) + printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", + fillsrc,filldst,repl_beginning,p_end+1); +#endif + assert(fillsrc==p_end+1 || fillsrc==repl_beginning); + assert(filldst==p_end+1 || filldst==repl_beginning); + } + } + else if (diff_type == UNI_DIFF) { + long line_beginning = ftell(pfp); + /* file pos of the current line */ + Reg4 LINENUM fillsrc; /* index of old lines */ + Reg5 LINENUM filldst; /* index of new lines */ + char ch; + + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || strnNE(buf, "@@ -", 4)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + s = buf+4; + if (!*s) + malformed (); + p_first = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + p_ptrn_lines = (LINENUM) atol(++s); + while (isdigit(*s)) s++; + } else + p_ptrn_lines = 1; + if (*s == ' ') s++; + if (*s != '+' || !*++s) + malformed (); + p_newfirst = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + p_repl_lines = (LINENUM) atol(++s); + while (isdigit(*s)) s++; + } else + p_repl_lines = 1; + if (*s == ' ') s++; + if (*s != '@') + malformed (); + if (!p_ptrn_lines) + p_first++; /* do append rather than insert */ + p_max = p_ptrn_lines + p_repl_lines + 1; + while (p_max >= hunkmax) + grow_hunkmax(); + fillsrc = 1; + filldst = fillsrc + p_ptrn_lines; + p_end = filldst + p_repl_lines; + Sprintf(buf,"*** %ld,%ld ****\n",p_first,p_first + p_ptrn_lines - 1); + p_line[0] = savestr(buf); + if (out_of_mem) { + p_end = -1; + return FALSE; + } + p_char[0] = '*'; + Sprintf(buf,"--- %ld,%ld ----\n",p_newfirst,p_newfirst+p_repl_lines-1); + p_line[filldst] = savestr(buf); + if (out_of_mem) { + p_end = 0; + return FALSE; + } + p_char[filldst++] = '='; + p_context = 100; + context = 0; + p_hunk_beg = p_input_line + 1; + while (fillsrc <= p_ptrn_lines || filldst <= p_end) { + line_beginning = ftell(pfp); + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) { + if (p_max - filldst < 3) + Strcpy(buf, " \n"); /* assume blank lines got chopped */ + else { + fatal1("unexpected end of file in patch\n"); + } + } + if (*buf == '\t' || *buf == '\n') { + ch = ' '; /* assume the space got eaten */ + s = savestr(buf); + } + else { + ch = *buf; + s = savestr(buf+1); + } + if (out_of_mem) { + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + return FALSE; + } + switch (ch) { + case '-': + if (fillsrc > p_ptrn_lines) { + free(s); + p_end = filldst-1; + malformed (); + } + p_char[fillsrc] = ch; + p_line[fillsrc] = s; + p_len[fillsrc++] = strlen(s); + break; + case '=': + ch = ' '; + /* FALL THROUGH */ + case ' ': + if (fillsrc > p_ptrn_lines) { + free(s); + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + malformed (); + } + context++; + p_char[fillsrc] = ch; + p_line[fillsrc] = s; + p_len[fillsrc++] = strlen(s); + s = savestr(s); + if (out_of_mem) { + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + return FALSE; + } + /* FALL THROUGH */ + case '+': + if (filldst > p_end) { + free(s); + while (--filldst > p_ptrn_lines) + free(p_line[filldst]); + p_end = fillsrc-1; + malformed (); + } + p_char[filldst] = ch; + p_line[filldst] = s; + p_len[filldst++] = strlen(s); + break; + default: + p_end = filldst; + malformed (); + } + if (ch != ' ' && context > 0) { + if (context < p_context) + p_context = context; + context = -1000; + } + }/* while */ + } + else { /* normal diff--fake it up */ + char hunk_type; + Reg3 int i; + LINENUM min, max; + long line_beginning = ftell(pfp); + + p_context = 0; + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || !isdigit(*buf)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + p_first = (LINENUM)atol(buf); + for (s=buf; isdigit(*s); s++) ; + if (*s == ',') { + p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; + while (isdigit(*s)) s++; + } + else + p_ptrn_lines = (*s != 'a'); + hunk_type = *s; + if (hunk_type == 'a') + p_first++; /* do append rather than insert */ + min = (LINENUM)atol(++s); + for (; isdigit(*s); s++) ; + if (*s == ',') + max = (LINENUM)atol(++s); + else + max = min; + if (hunk_type == 'd') + min++; + p_end = p_ptrn_lines + 1 + max - min + 1; + if (p_end > MAXHUNKSIZE) + fatal4("hunk too large (%ld lines) at line %ld: %s", + p_end, p_input_line, buf); + while (p_end >= hunkmax) + grow_hunkmax(); + p_newfirst = min; + p_repl_lines = max - min + 1; + Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); + p_line[0] = savestr(buf); + if (out_of_mem) { + p_end = -1; + return FALSE; + } + p_char[0] = '*'; + for (i=1; i<=p_ptrn_lines; i++) { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("unexpected end of file in patch at line %ld\n", + p_input_line); + if (*buf != '<') + fatal2("< expected at line %ld of patch\n", p_input_line); + p_line[i] = savestr(buf+2); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } + p_len[i] = strlen(p_line[i]); + p_char[i] = '-'; + } + if (hunk_type == 'c') { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("unexpected end of file in patch at line %ld\n", + p_input_line); + if (*buf != '-') + fatal2("--- expected at line %ld of patch\n", p_input_line); + } + Sprintf(buf, "--- %ld,%ld\n", min, max); + p_line[i] = savestr(buf); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } + p_char[i] = '='; + for (i++; i<=p_end; i++) { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("unexpected end of file in patch at line %ld\n", + p_input_line); + if (*buf != '>') + fatal2("> expected at line %ld of patch\n", p_input_line); + p_line[i] = savestr(buf+2); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } + p_len[i] = strlen(p_line[i]); + p_char[i] = '+'; + } + } + if (reverse) /* backwards patch? */ + if (!pch_swap()) + say1("Not enough memory to swap next hunk!\n"); +#ifdef DEBUGGING + if (debug & 2) { + int i; + char special; + + for (i=0; i <= p_end; i++) { + if (i == p_ptrn_lines) + special = '^'; + else + special = ' '; + fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]); + Fflush(stderr); + } + } +#endif + if (p_end+1 < hunkmax) /* paranoia reigns supreme... */ + p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */ + return TRUE; +} + +/* Input a line from the patch file, worrying about indentation. */ + +char * +pgets(bf,sz,fp) +char *bf; +int sz; +FILE *fp; +{ + char *ret = fgets(bf, sz, fp); + Reg1 char *s; + Reg2 int indent = 0; + + if (p_indent && ret != Nullch) { + for (s=buf; + indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) { + if (*s == '\t') + indent += 8 - (indent % 7); + else + indent++; + } + if (buf != s) + Strcpy(buf, s); + } + return ret; +} + +/* Reverse the old and new portions of the current hunk. */ + +bool +pch_swap() +{ + char **tp_line; /* the text of the hunk */ + short *tp_len; /* length of each line */ + char *tp_char; /* +, -, and ! */ + Reg1 LINENUM i; + Reg2 LINENUM n; + bool blankline = FALSE; + Reg3 char *s; + + i = p_first; + p_first = p_newfirst; + p_newfirst = i; + + /* make a scratch copy */ + + tp_line = p_line; + tp_len = p_len; + tp_char = p_char; + p_line = Null(char**); /* force set_hunkmax to allocate again */ + p_len = Null(short*); + p_char = Nullch; + set_hunkmax(); + if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) { +#ifndef lint + if (p_line == Null(char**)) + free((char*)p_line); + p_line = tp_line; + if (p_len == Null(short*)) + free((char*)p_len); + p_len = tp_len; +#endif + if (p_char == Nullch) + free((char*)p_char); + p_char = tp_char; + return FALSE; /* not enough memory to swap hunk! */ + } + + /* now turn the new into the old */ + + i = p_ptrn_lines + 1; + if (tp_char[i] == '\n') { /* account for possible blank line */ + blankline = TRUE; + i++; + } + if (p_efake >= 0) { /* fix non-freeable ptr range */ + if (p_efake <= i) + n = p_end - i + 1; + else + n = -i; + p_efake += n; + p_bfake += n; + } + for (n=0; i <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + if (p_char[n] == '+') + p_char[n] = '-'; + p_len[n] = tp_len[i]; + } + if (blankline) { + i = p_ptrn_lines + 1; + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + p_len[n] = tp_len[i]; + n++; + } + assert(p_char[0] == '='); + p_char[0] = '*'; + for (s=p_line[0]; *s; s++) + if (*s == '-') + *s = '*'; + + /* now turn the old into the new */ + + assert(tp_char[0] == '*'); + tp_char[0] = '='; + for (s=tp_line[0]; *s; s++) + if (*s == '*') + *s = '-'; + for (i=0; n <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + if (p_char[n] == '-') + p_char[n] = '+'; + p_len[n] = tp_len[i]; + } + assert(i == p_ptrn_lines + 1); + i = p_ptrn_lines; + p_ptrn_lines = p_repl_lines; + p_repl_lines = i; +#ifndef lint + if (tp_line == Null(char**)) + free((char*)tp_line); + if (tp_len == Null(short*)) + free((char*)tp_len); +#endif + if (tp_char == Nullch) + free((char*)tp_char); + return TRUE; +} + +/* Return the specified line position in the old file of the old context. */ + +LINENUM +pch_first() +{ + return p_first; +} + +/* Return the number of lines of old context. */ + +LINENUM +pch_ptrn_lines() +{ + return p_ptrn_lines; +} + +/* Return the probable line position in the new file of the first line. */ + +LINENUM +pch_newfirst() +{ + return p_newfirst; +} + +/* Return the number of lines in the replacement text including context. */ + +LINENUM +pch_repl_lines() +{ + return p_repl_lines; +} + +/* Return the number of lines in the whole hunk. */ + +LINENUM +pch_end() +{ + return p_end; +} + +/* Return the number of context lines before the first changed line. */ + +LINENUM +pch_context() +{ + return p_context; +} + +/* Return the length of a particular patch line. */ + +short +pch_line_len(line) +LINENUM line; +{ + return p_len[line]; +} + +/* Return the control character (+, -, *, !, etc) for a patch line. */ + +char +pch_char(line) +LINENUM line; +{ + return p_char[line]; +} + +/* Return a pointer to a particular patch line. */ + +char * +pfetch(line) +LINENUM line; +{ + return p_line[line]; +} + +/* Return where in the patch file this hunk began, for error messages. */ + +LINENUM +pch_hunk_beg() +{ + return p_hunk_beg; +} + +/* Apply an ed script by feeding ed itself. */ + +#ifndef WIN32 +void +do_ed_script() +{ + Reg1 char *t; + Reg2 long beginning_of_this_line; + Reg3 bool this_line_is_command = FALSE; + Reg4 FILE *pipefp; + + if (!skip_rest_of_patch) { + Unlink(TMPOUTNAME); + copy_file(filearg[0], TMPOUTNAME); + if (verbose) + Sprintf(buf, "/bin/ed %s", TMPOUTNAME); + else + Sprintf(buf, "/bin/ed - %s", TMPOUTNAME); + pipefp = popen(buf, "w"); + } + for (;;) { + beginning_of_this_line = ftell(pfp); + if (pgets(buf, sizeof buf, pfp) == Nullch) { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + p_input_line++; + for (t=buf; isdigit(*t) || *t == ','; t++) ; + this_line_is_command = (isdigit(*buf) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (this_line_is_command) { + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (*t != 'd') { + while (pgets(buf, sizeof buf, pfp) != Nullch) { + p_input_line++; + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (strEQ(buf, ".\n")) + break; + } + } + } + else { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + } + if (skip_rest_of_patch) + return; + fprintf(pipefp, "w\n"); + fprintf(pipefp, "q\n"); + Fflush(pipefp); + Pclose(pipefp); + ignore_signals(); + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = TRUE; + chmod(TMPOUTNAME, filemode); + } + else + chmod(outname, filemode); + set_signals(1); +} +#endif diff --git a/xc/util/patch/pch.h b/xc/util/patch/pch.h new file mode 100644 index 000000000..3873f28a5 --- /dev/null +++ b/xc/util/patch/pch.h @@ -0,0 +1,38 @@ +/* oldHeader: pch.h,v 2.0.1.1 87/01/30 22:47:16 lwall Exp $ + * $XConsortium: pch.h,v 3.3 94/09/14 21:24:15 gildea Exp $ + * + * Revision 2.0.1.1 87/01/30 22:47:16 lwall + * Added do_ed_script(). + * + * Revision 2.0 86/09/17 15:39:57 lwall + * Baseline for netwide release. + * + */ + +EXT FILE *pfp INIT(Nullfp); /* patch file pointer */ + +void re_patch(); +void open_patch_file(); +void set_hunkmax(); +void grow_hunkmax(); +bool there_is_another_patch(); +int intuit_diff_type(); +void next_intuit_at(); +void skip_to(); +bool another_hunk(); +bool pch_swap(); +char *pfetch(); +short pch_line_len(); +LINENUM pch_first(); +LINENUM pch_ptrn_lines(); +LINENUM pch_newfirst(); +LINENUM pch_repl_lines(); +LINENUM pch_end(); +LINENUM pch_context(); +LINENUM pch_hunk_beg(); +char pch_char(); +char *pfetch(); +char *pgets(); +#ifndef WIN32 +void do_ed_script(); +#endif diff --git a/xc/util/patch/util.c b/xc/util/patch/util.c new file mode 100644 index 000000000..ef70642a9 --- /dev/null +++ b/xc/util/patch/util.c @@ -0,0 +1,474 @@ +/* $XConsortium: util.c,v 3.3 94/09/14 21:26:09 gildea Exp $ */ + +#define const /*nothing*/ + +#include "EXTERN.h" +#include "common.h" +#include "INTERN.h" +#include "util.h" +#include "backupfile.h" + +void my_exit(); + +static char * +private_strerror (errnum) + int errnum; +{ + extern char *sys_errlist[]; + extern int sys_nerr; + + if (errnum > 0 && errnum <= sys_nerr) + return sys_errlist[errnum]; + return "Unknown system error"; +} +#define strerror private_strerror + +/* Rename a file, copying it if necessary. */ + +int +move_file(from,to) +char *from, *to; +{ + char bakname[512]; + Reg1 char *s; + Reg2 int i; + Reg3 int fromfd; + + /* to stdout? */ + + if (strEQ(to, "-")) { +#ifdef DEBUGGING + if (debug & 4) + say2("Moving %s to stdout.\n", from); +#endif + fromfd = open(from, 0); + if (fromfd < 0) + pfatal2("internal error, can't reopen %s", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(1, buf, i) != 1) + pfatal1("write failed"); + Close(fromfd); + return 0; + } + + if (origprae) { + Strcpy(bakname, origprae); + Strcat(bakname, to); + } else { +#ifndef NODIR + char *backupname = find_backup_file_name(to); + if (backupname == (char *) 0) + fatal1("out of memory\n"); + Strcpy(bakname, backupname); + free(backupname); +#else /* NODIR */ + Strcpy(bakname, to); + Strcat(bakname, simple_backup_suffix); +#endif /* NODIR */ + } + + if (stat(to, &filestat) == 0) { /* output file exists */ + dev_t to_device = filestat.st_dev; + ino_t to_inode = filestat.st_ino; + char *simplename = bakname; + + for (s=bakname; *s; s++) { + if (*s == '/') + simplename = s+1; + } + /* Find a backup name that is not the same file. + Change the first lowercase char into uppercase; + if that isn't sufficient, chop off the first char and try again. */ + while (stat(bakname, &filestat) == 0 && + to_device == filestat.st_dev && to_inode == filestat.st_ino) { + /* Skip initial non-lowercase chars. */ + for (s=simplename; *s && !islower(*s); s++) ; + if (*s) + *s = toupper(*s); + else + Strcpy(simplename, simplename+1); + } + while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */ +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", to, bakname); +#endif +#ifndef WIN32 + if (link(to, bakname) < 0) { + /* Maybe `to' is a symlink into a different file system. + Copying replaces the symlink with a file; using rename + would be better. */ + Reg4 int tofd; + Reg5 int bakfd; + + bakfd = creat(bakname, 0666); + if (bakfd < 0) { + say4("Can't backup %s, output is in %s: %s\n", to, from, + strerror(errno)); + return -1; + } + tofd = open(to, 0); + if (tofd < 0) + pfatal2("internal error, can't open %s", to); + while ((i=read(tofd, buf, sizeof buf)) > 0) + if (write(bakfd, buf, i) != i) + pfatal1("write failed"); + Close(tofd); + Close(bakfd); + } +#endif + while (unlink(to) >= 0) ; + } +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", from, to); +#endif +#ifndef WIN32 + if (link(from, to) < 0) { /* different file system? */ + Reg4 int tofd; + + tofd = creat(to, 0666); + if (tofd < 0) { + say4("Can't create %s, output is in %s: %s\n", + to, from, strerror(errno)); + return -1; + } + fromfd = open(from, 0); + if (fromfd < 0) + pfatal2("internal error, can't reopen %s", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + pfatal1("write failed"); + Close(fromfd); + Close(tofd); + } + Unlink(from); +#else + rename (from, to); +#endif + return 0; +} + +/* Copy a file. */ + +void +copy_file(from,to) +char *from, *to; +{ + Reg3 int tofd; + Reg2 int fromfd; + Reg1 int i; + + tofd = creat(to, 0666); + if (tofd < 0) + pfatal2("can't create %s", to); + fromfd = open(from, 0); + if (fromfd < 0) + pfatal2("internal error, can't reopen %s", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + pfatal2("write to %s failed", to); + Close(fromfd); + Close(tofd); +} + +/* Allocate a unique area for a string. */ + +char * +savestr(s) +Reg1 char *s; +{ + Reg3 char *rv; + Reg2 char *t; + + if (!s) + s = "Oops"; + t = s; + while (*t++); + rv = malloc((MEM) (t - s)); + if (rv == Nullch) { + if (using_plan_a) + out_of_mem = TRUE; + else + fatal1("out of memory\n"); + } + else { + t = rv; + while (*t++ = *s++); + } + return rv; +} + +#if defined(lint) && defined(CANVARARG) + +/*VARARGS ARGSUSED*/ +say(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +fatal(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +pfatal(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +ask(pat) char *pat; { ; } + +#else + +/* Vanilla terminal output (buffered). */ + +void +say(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + fprintf(stderr, pat, arg1, arg2, arg3); + Fflush(stderr); +} + +/* Terminal output, pun intended. */ + +void /* very void */ +fatal(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + fprintf(stderr, "patch: **** "); + fprintf(stderr, pat, arg1, arg2, arg3); + my_exit(1); +} + +/* Say something from patch, something from the system, then silence . . . */ + +void /* very void */ +pfatal(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + int errnum = errno; + + fprintf(stderr, "patch: **** "); + fprintf(stderr, pat, arg1, arg2, arg3); + fprintf(stderr, ": %s\n", strerror(errnum)); + my_exit(1); +} + +/* Get a response from the user, somehow or other. */ + +void +ask(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ +#ifndef WIN32 + int ttyfd; + int r; + bool tty2 = isatty(2); + + Sprintf(buf, pat, arg1, arg2, arg3); + Fflush(stderr); + write(2, buf, strlen(buf)); + if (tty2) { /* might be redirected to a file */ + r = read(2, buf, sizeof buf); + } + else if (isatty(1)) { /* this may be new file output */ + Fflush(stdout); + write(1, buf, strlen(buf)); + r = read(1, buf, sizeof buf); + } + else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { + /* might be deleted or unwriteable */ + write(ttyfd, buf, strlen(buf)); + r = read(ttyfd, buf, sizeof buf); + Close(ttyfd); + } + else if (isatty(0)) { /* this is probably patch input */ + Fflush(stdin); + write(0, buf, strlen(buf)); + r = read(0, buf, sizeof buf); + } + else { /* no terminal at all--default it */ + buf[0] = '\n'; + r = 1; + } + if (r <= 0) + buf[0] = 0; + else + buf[r] = '\0'; + if (!tty2) + say1(buf); +#else + buf[0] = '\n'; buf[1] = '\0'; +#endif +} +#endif /* lint */ + +/* How to handle certain events when not in a critical region. */ + +void +set_signals(reset) +int reset; +{ +#ifndef lint +#ifdef VOIDSIG + static void (*hupval)(),(*intval)(); +#else + static int (*hupval)(),(*intval)(); +#endif + + if (!reset) { +#ifndef WIN32 + hupval = signal(SIGHUP, SIG_IGN); + if (hupval != SIG_IGN) +#ifdef VOIDSIG + hupval = my_exit; +#else + hupval = (int(*)())my_exit; +#endif +#endif + intval = signal(SIGINT, SIG_IGN); + if (intval != SIG_IGN) +#ifdef VOIDSIG + intval = my_exit; +#else + intval = (int(*)())my_exit; +#endif + } +#ifndef WIN32 + Signal(SIGHUP, hupval); +#endif + Signal(SIGINT, intval); +#endif +} + +/* How to handle certain events when in a critical region. */ + +void +ignore_signals() +{ +#ifndef lint +#ifndef WIN32 + Signal(SIGHUP, SIG_IGN); +#endif + Signal(SIGINT, SIG_IGN); +#endif +} + +/* Make sure we'll have the directories to create a file. + If `striplast' is TRUE, ignore the last element of `filename'. */ + +void +makedirs(filename,striplast) +Reg1 char *filename; +bool striplast; +{ + char tmpbuf[256]; + Reg2 char *s = tmpbuf; + char *dirv[20]; /* Point to the NULs between elements. */ + Reg3 int i; + Reg4 int dirvp = 0; /* Number of finished entries in dirv. */ + + /* Copy `filename' into `tmpbuf' with a NUL instead of a slash + between the directories. */ + while (*filename) { + if (*filename == '/') { + filename++; + dirv[dirvp++] = s; + *s++ = '\0'; + } + else { + *s++ = *filename++; + } + } + *s = '\0'; + dirv[dirvp] = s; + if (striplast) + dirvp--; + if (dirvp < 0) + return; + + strcpy(buf, "mkdir"); + s = buf; + for (i=0; i<=dirvp; i++) { + struct stat sbuf; + + if (stat(tmpbuf, &sbuf) && errno == ENOENT) { + while (*s) s++; + *s++ = ' '; + strcpy(s, tmpbuf); + } +#ifndef WIN32 + *dirv[i] = '/'; +#else + *dirv[i] = '\\'; +#endif + } + if (s != buf) + system(buf); +} + +/* Make filenames more reasonable. */ + +char * +fetchname(at,strip_leading,assume_exists) +char *at; +int strip_leading; +int assume_exists; +{ + char *fullname; + char *name; + Reg1 char *t; + char tmpbuf[200]; + int sleading = strip_leading; + + if (!at) + return Nullch; + while (isspace(*at)) + at++; +#ifdef DEBUGGING + if (debug & 128) + say4("fetchname %s %d %d\n",at,strip_leading,assume_exists); +#endif + if (strnEQ(at, "/dev/null", 9)) /* so files can be created by diffing */ + return Nullch; /* against /dev/null. */ + name = fullname = t = savestr(at); + + /* Strip off up to `sleading' leading slashes and null terminate. */ + for (; *t && !isspace(*t); t++) + if (*t == '/') + if (--sleading >= 0) + name = t+1; + *t = '\0'; + + /* If no -p option was given (957 is the default value!), + we were given a relative pathname, + and the leading directories that we just stripped off all exist, + put them back on. */ + if (strip_leading == 957 && name != fullname && *fullname != '/') { + name[-1] = '\0'; + if (stat(fullname, &filestat) == 0 && S_ISDIR (filestat.st_mode)) { + name[-1] = '/'; + name=fullname; + } + } + + name = savestr(name); + free(fullname); + + if (stat(name, &filestat) && !assume_exists) { + char *filebase = basename(name); + int pathlen = filebase - name; + + /* Put any leading path into `tmpbuf'. */ + strncpy(tmpbuf, name, pathlen); + +#define try(f, a1, a2) (Sprintf(tmpbuf + pathlen, f, a1, a2), stat(tmpbuf, &filestat) == 0) + if ( try("RCS/%s%s", filebase, RCSSUFFIX) + || try("RCS/%s" , filebase, 0) + || try( "%s%s", filebase, RCSSUFFIX) + || try("SCCS/%s%s", SCCSPREFIX, filebase) + || try( "%s%s", SCCSPREFIX, filebase)) + return name; + free(name); + name = Nullch; + } + + return name; +} diff --git a/xc/util/patch/util.h b/xc/util/patch/util.h new file mode 100644 index 000000000..ad385c30c --- /dev/null +++ b/xc/util/patch/util.h @@ -0,0 +1,88 @@ +/* oldHeader: util.h,v 2.0 86/09/17 15:40:06 lwall Exp $ + * $XConsortium: util.h,v 2.1 94/09/09 20:04:28 gildea Exp $ + * + * Revision 2.0 86/09/17 15:40:06 lwall + * Baseline for netwide release. + * + */ + +/* and for those machine that can't handle a variable argument list */ + +#ifdef CANVARARG + +#define say1 say +#define say2 say +#define say3 say +#define say4 say +#define ask1 ask +#define ask2 ask +#define ask3 ask +#define ask4 ask +#define fatal1 fatal +#define fatal2 fatal +#define fatal3 fatal +#define fatal4 fatal +#define pfatal1 pfatal +#define pfatal2 pfatal +#define pfatal3 pfatal +#define pfatal4 pfatal + +#else /* hope they allow multi-line macro actual arguments */ + +#ifdef lint + +#define say1(a) say(a, 0, 0, 0) +#define say2(a,b) say(a, (b)==(b), 0, 0) +#define say3(a,b,c) say(a, (b)==(b), (c)==(c), 0) +#define say4(a,b,c,d) say(a, (b)==(b), (c)==(c), (d)==(d)) +#define ask1(a) ask(a, 0, 0, 0) +#define ask2(a,b) ask(a, (b)==(b), 0, 0) +#define ask3(a,b,c) ask(a, (b)==(b), (c)==(c), 0) +#define ask4(a,b,c,d) ask(a, (b)==(b), (c)==(c), (d)==(d)) +#define fatal1(a) fatal(a, 0, 0, 0) +#define fatal2(a,b) fatal(a, (b)==(b), 0, 0) +#define fatal3(a,b,c) fatal(a, (b)==(b), (c)==(c), 0) +#define fatal4(a,b,c,d) fatal(a, (b)==(b), (c)==(c), (d)==(d)) +#define pfatal1(a) pfatal(a, 0, 0, 0) +#define pfatal2(a,b) pfatal(a, (b)==(b), 0, 0) +#define pfatal3(a,b,c) pfatal(a, (b)==(b), (c)==(c), 0) +#define pfatal4(a,b,c,d) pfatal(a, (b)==(b), (c)==(c), (d)==(d)) + +#else /* lint */ + /* if this doesn't work, try defining CANVARARG above */ +#define say1(a) say(a, Nullch, Nullch, Nullch) +#define say2(a,b) say(a, b, Nullch, Nullch) +#define say3(a,b,c) say(a, b, c, Nullch) +#define say4 say +#define ask1(a) ask(a, Nullch, Nullch, Nullch) +#define ask2(a,b) ask(a, b, Nullch, Nullch) +#define ask3(a,b,c) ask(a, b, c, Nullch) +#define ask4 ask +#define fatal1(a) fatal(a, Nullch, Nullch, Nullch) +#define fatal2(a,b) fatal(a, b, Nullch, Nullch) +#define fatal3(a,b,c) fatal(a, b, c, Nullch) +#define fatal4 fatal +#define pfatal1(a) pfatal(a, Nullch, Nullch, Nullch) +#define pfatal2(a,b) pfatal(a, b, Nullch, Nullch) +#define pfatal3(a,b,c) pfatal(a, b, c, Nullch) +#define pfatal4 pfatal + +#endif /* lint */ + +/* if neither of the above work, join all multi-line macro calls. */ +#endif + +EXT char serrbuf[BUFSIZ]; /* buffer for stderr */ + +char *fetchname(); +int move_file(); +void copy_file(); +void say(); +void fatal(); +void pfatal(); +void ask(); +char *savestr(); +void set_signals(); +void ignore_signals(); +void makedirs(); +char *basename(); diff --git a/xc/util/patch/version.c b/xc/util/patch/version.c new file mode 100644 index 000000000..86e86842a --- /dev/null +++ b/xc/util/patch/version.c @@ -0,0 +1,25 @@ +/* oldHeader: version.c,v 2.0 86/09/17 15:40:11 lwall Exp $ + * $XConsortium: version.c,v 2.1 94/09/09 20:04:28 gildea Exp $ + * + * Revision 2.0 86/09/17 15:40:11 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "patchlevel.h" +#include "version.h" + +void my_exit(); + +/* Print out the version number and die. */ + +void +version() +{ + fprintf(stderr, "Patch version 2.0, patch level %s\n", PATCHLEVEL); + my_exit(0); +} diff --git a/xc/util/patch/version.h b/xc/util/patch/version.h new file mode 100644 index 000000000..bc249e0f8 --- /dev/null +++ b/xc/util/patch/version.h @@ -0,0 +1,9 @@ +/* oldHeader: version.h,v 2.0 86/09/17 15:40:14 lwall Exp $ + * $XConsortium: version.h,v 2.1 94/09/09 20:04:28 gildea Exp $ + * + * Revision 2.0 86/09/17 15:40:14 lwall + * Baseline for netwide release. + * + */ + +void version(); diff --git a/xc/util/patch/winnt.h b/xc/util/patch/winnt.h new file mode 100644 index 000000000..3f45159f0 --- /dev/null +++ b/xc/util/patch/winnt.h @@ -0,0 +1,25 @@ + +/* $XConsortium: winnt.h,v 1.1 94/09/09 20:27:49 kaleb Exp $ */ + + +#define stat _stat +#define dev_t _dev_t +#define ino_t _ino_t +#define chdir _chdir +#define chmod(p1,p2) _chmod(p1,_S_IWRITE) +#define open _open +#define close _close +#define creat(p1,p2) _creat(p1,_S_IWRITE) +#define fileno _fileno +#define fstat _fstat +#define isatty _isatty +#define lseek _lseek +#define mktemp _mktemp +#define pclose _pclose +#define popen _popen +#define read _read +#define write _write +#define unlink _unlink +#define sys_errlist _sys_errlist +#define sys_nerr (*_sys_nerr_dll) + |