diff options
author | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:33:43 +0000 |
---|---|---|
committer | rgrimes <rgrimes@FreeBSD.org> | 1994-05-27 12:33:43 +0000 |
commit | 34f25f0a2a416c910d4bbd058e510c46816355fe (patch) | |
tree | 617ba061dcc614170f39c0c4bdbd6efee6f9c3d0 | |
parent | 366dfaba192d02d567e9a39508305b9c547c3586 (diff) | |
download | FreeBSD-src-34f25f0a2a416c910d4bbd058e510c46816355fe.zip FreeBSD-src-34f25f0a2a416c910d4bbd058e510c46816355fe.tar.gz |
BSD 4.4 Lite Usr.bin Sources
-rw-r--r-- | usr.bin/m4/Makefile | 10 | ||||
-rw-r--r-- | usr.bin/m4/NOTES | 64 | ||||
-rw-r--r-- | usr.bin/m4/PSD.doc/Makefile | 10 | ||||
-rw-r--r-- | usr.bin/m4/TEST/ack.m4 | 40 | ||||
-rw-r--r-- | usr.bin/m4/TEST/hanoi.m4 | 45 | ||||
-rw-r--r-- | usr.bin/m4/TEST/hash.m4 | 55 | ||||
-rw-r--r-- | usr.bin/m4/TEST/sqroot.m4 | 45 | ||||
-rw-r--r-- | usr.bin/m4/TEST/string.m4 | 45 | ||||
-rw-r--r-- | usr.bin/m4/TEST/test.m4 | 243 | ||||
-rw-r--r-- | usr.bin/m4/eval.c | 789 | ||||
-rw-r--r-- | usr.bin/m4/expr.c | 626 | ||||
-rw-r--r-- | usr.bin/m4/extern.h | 96 | ||||
-rw-r--r-- | usr.bin/m4/look.c | 145 | ||||
-rw-r--r-- | usr.bin/m4/main.c | 425 | ||||
-rw-r--r-- | usr.bin/m4/mdef.h | 176 | ||||
-rw-r--r-- | usr.bin/m4/misc.c | 266 | ||||
-rw-r--r-- | usr.bin/m4/pathnames.h | 57 | ||||
-rw-r--r-- | usr.bin/m4/stdd.h | 56 | ||||
-rw-r--r-- | usr.bin/patch/Makefile | 6 | ||||
-rw-r--r-- | usr.bin/patch/README | 79 | ||||
-rw-r--r-- | usr.bin/patch/common.h | 138 | ||||
-rw-r--r-- | usr.bin/patch/inp.c | 313 | ||||
-rw-r--r-- | usr.bin/patch/inp.h | 18 | ||||
-rw-r--r-- | usr.bin/patch/patch.1 | 446 | ||||
-rw-r--r-- | usr.bin/patch/patch.c | 800 | ||||
-rw-r--r-- | usr.bin/patch/pch.c | 1108 | ||||
-rw-r--r-- | usr.bin/patch/pch.h | 36 | ||||
-rw-r--r-- | usr.bin/patch/util.c | 339 | ||||
-rw-r--r-- | usr.bin/patch/util.h | 74 |
29 files changed, 6550 insertions, 0 deletions
diff --git a/usr.bin/m4/Makefile b/usr.bin/m4/Makefile new file mode 100644 index 0000000..17145fd --- /dev/null +++ b/usr.bin/m4/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +# -DEXTENDED +# if you want the paste & spaste macros. + +PROG= m4 +CFLAGS+=-DEXTENDED +SRCS= eval.c expr.c look.c main.c misc.c + +.include <bsd.prog.mk> diff --git a/usr.bin/m4/NOTES b/usr.bin/m4/NOTES new file mode 100644 index 0000000..d60f80e --- /dev/null +++ b/usr.bin/m4/NOTES @@ -0,0 +1,64 @@ +m4 - macro processor + +PD m4 is based on the macro tool distributed with the software +tools (VOS) package, and described in the "SOFTWARE TOOLS" and +"SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include +most of the command set of SysV m4, the standard UN*X macro processor. + +Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro, +there may be certain implementation similarities between +the two. The PD m4 was produced without ANY references to m4 +sources. + +written by: Ozan S. Yigit + +References: + + Software Tools distribution: macro + + Kernighan, Brian W. and P. J. Plauger, SOFTWARE + TOOLS IN PASCAL, Addison-Wesley, Mass. 1981 + + Kernighan, Brian W. and P. J. Plauger, SOFTWARE + TOOLS, Addison-Wesley, Mass. 1976 + + Kernighan, Brian W. and Dennis M. Ritchie, + THE M4 MACRO PROCESSOR, Unix Programmer's Manual, + Seventh Edition, Vol. 2, Bell Telephone Labs, 1979 + + System V man page for M4 + + +Implementation Notes: + +[1] PD m4 uses a different (and simpler) stack mechanism than the one + described in Software Tools and Software Tools in Pascal books. + The triple stack thing is replaced with a single stack containing + the call frames and the arguments. Each frame is back-linked to a + previous stack frame, which enables us to rewind the stack after + each nested call is completed. Each argument is a character pointer + to the beginning of the argument string within the string space. + The only exceptions to this are (*) arg 0 and arg 1, which are + the macro definition and macro name strings, stored dynamically + for the hash table. + + . . + | . | <-- sp | . | + +-------+ +-----+ + | arg 3 ------------------------------->| str | + +-------+ | . | + | arg 2 --------------+ . + +-------+ | + * | | | + +-------+ | +-----+ + | plev | <-- fp +---------------->| str | + +-------+ | . | + | type | . + +-------+ + | prcf -----------+ plev: paren level + +-------+ | type: call type + | . | | prcf: prev. call frame + . | + +-------+ | + | <----------+ + +-------+ diff --git a/usr.bin/m4/PSD.doc/Makefile b/usr.bin/m4/PSD.doc/Makefile new file mode 100644 index 0000000..0613e08 --- /dev/null +++ b/usr.bin/m4/PSD.doc/Makefile @@ -0,0 +1,10 @@ +# @(#)Makefile 8.1 (Berkeley) 6/8/93 + +DIR= psd/17.m4 +SRCS= m4.ms +MACROS= -msU + +paper.ps: ${SRCS} + ${ROFF} ${SRCS} > ${.TARGET} + +.include <bsd.doc.mk> diff --git a/usr.bin/m4/TEST/ack.m4 b/usr.bin/m4/TEST/ack.m4 new file mode 100644 index 0000000..de914bd --- /dev/null +++ b/usr.bin/m4/TEST/ack.m4 @@ -0,0 +1,40 @@ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)ack.m4 8.1 (Berkeley) 6/6/93 +# + +define(ack, `ifelse($1,0,incr($2),$2,0,`ack(DECR($1),1)', +`ack(DECR($1), ack($1,DECR($2)))')') diff --git a/usr.bin/m4/TEST/hanoi.m4 b/usr.bin/m4/TEST/hanoi.m4 new file mode 100644 index 0000000..9371b34 --- /dev/null +++ b/usr.bin/m4/TEST/hanoi.m4 @@ -0,0 +1,45 @@ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)hanoi.m4 8.1 (Berkeley) 6/6/93 +# + +define(hanoi, `trans(A, B, C, $1)') + +define(moved,`move disk from $1 to $2 +') + +define(trans, `ifelse($4,1,`moved($1,$2)', + `trans($1,$3,$2,DECR($4))moved($1,$2)trans($3,$2,$1,DECR($4))')') diff --git a/usr.bin/m4/TEST/hash.m4 b/usr.bin/m4/TEST/hash.m4 new file mode 100644 index 0000000..85d5aa8 --- /dev/null +++ b/usr.bin/m4/TEST/hash.m4 @@ -0,0 +1,55 @@ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)hash.m4 8.1 (Berkeley) 6/6/93 +# + +dnl This probably will not run on any m4 that cannot +dnl handle char constants in eval. +dnl +changequote(<,>) define(HASHVAL,99) dnl +define(hash,<eval(str(substr($1,1),0)%HASHVAL)>) dnl +define(str, + <ifelse($1,",$2, + <str(substr(<$1>,1),<eval($2+'substr($1,0,1)')>)>) + >) dnl +define(KEYWORD,<$1,hash($1),>) dnl +define(TSTART, +<struct prehash { + char *keyword; + int hashval; +} keytab[] = {>) dnl +define(TEND,< "",0 +};>) dnl diff --git a/usr.bin/m4/TEST/sqroot.m4 b/usr.bin/m4/TEST/sqroot.m4 new file mode 100644 index 0000000..3c7501f --- /dev/null +++ b/usr.bin/m4/TEST/sqroot.m4 @@ -0,0 +1,45 @@ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)sqroot.m4 8.1 (Berkeley) 6/6/93 +# + +define(square_root, + `ifelse(eval($1<0),1,negative-square-root, + `square_root_aux($1, 1, eval(($1+1)/2))')') +define(square_root_aux, + `ifelse($3, $2, $3, + $3, eval($1/$2), $3, + `square_root_aux($1, $3, eval(($3+($1/$3))/2))')') diff --git a/usr.bin/m4/TEST/string.m4 b/usr.bin/m4/TEST/string.m4 new file mode 100644 index 0000000..bff741a --- /dev/null +++ b/usr.bin/m4/TEST/string.m4 @@ -0,0 +1,45 @@ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)string.m4 8.1 (Berkeley) 6/6/93 +# + +define(string,`integer $1(len(substr($2,1))) +str($1,substr($2,1),0) +data $1(len(substr($2,1)))/EOS/ +') + +define(str,`ifelse($2,",,data $1(incr($3))/`LET'substr($2,0,1)/ +`str($1,substr($2,1),incr($3))')') diff --git a/usr.bin/m4/TEST/test.m4 b/usr.bin/m4/TEST/test.m4 new file mode 100644 index 0000000..df8b78c --- /dev/null +++ b/usr.bin/m4/TEST/test.m4 @@ -0,0 +1,243 @@ +# +# Copyright (c) 1989, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Ozan Yigit. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)test.m4 8.1 (Berkeley) 6/6/93 +# + +# test file for mp (not comprehensive) +# +# v7 m4 does not have `decr'. +# +define(DECR,`eval($1-1)') +# +# include string macros +# +include(string.m4) +# +# create some fortrash strings for an even uglier language +# +string(TEXT, "text") +string(DATA, "data") +string(BEGIN, "begin") +string(END, "end") +string(IF, "if") +string(THEN, "then") +string(ELSE, "else") +string(CASE, "case") +string(REPEAT, "repeat") +string(WHILE, "while") +string(DEFAULT, "default") +string(UNTIL, "until") +string(FUNCTION, "function") +string(PROCEDURE, "procedure") +string(EXTERNAL, "external") +string(FORWARD, "forward") +string(TYPE, "type") +string(VAR, "var") +string(CONST, "const") +string(PROGRAM, "program") +string(INPUT, "input") +string(OUTPUT, "output") +# +divert(2) +diversion #1 +divert(3) +diversion #2 +divert(4) +diversion #3 +divert(5) +diversion #4 +divert(0) +define(abc,xxx) +ifdef(`abc',defined,undefined) +# +# v7 m4 does this wrong. The right output is +# this is A vEry lon sEntEnCE +# see m4 documentation for translit. +# +translit(`this is a very long sentence', abcdefg, ABCDEF) +# +# include towers-of-hanoi +# +include(hanoi.m4) +# +# some reasonable set of disks +# +hanoi(6) +# +# include ackermann's function +# +include(ack.m4) +# +# something like (3,3) will blow away un*x m4. +# +ack(2,3) +# +# include a square_root function for fixed nums +# +include(sqroot.m4) +# +# some square roots. +# +square_root(15) +square_root(100) +square_root(-4) +square_root(21372) +# +# some textual material for enjoyment. +# +[taken from the 'Clemson University Computer Newsletter', + September 1981, pp. 6-7] + +I am a wizard in the magical Kingdom of Transformation and I +slay dragons for a living. Actually, I am a systems programmer. +One of the problems with systems programming is explaining to +non-computer enthusiasts what that is. All of the terms I use to +describe my job are totally meaningless to them. Usually my response +to questions about my work is to say as little as possible. For +instance, if someone asks what happened at work this week, I say +"Nothing much" and then I change the subject. + +With the assistance of my brother, a mechanical engineer, I have devised +an analogy that everyone can understand. The analogy describes the +"Kingdom of Transformation" where travelers wander and are magically +transformed. This kingdom is the computer and the travelers are information. +The purpose of the computer is to change information to a more meaningful +forma. The law of conservation applies here: The computer never creates +and never intentionally destroys data. With no further ado, let us travel +to the Kingdom of Transformation: + +In a land far, far away, there is a magical kingdom called the Kingdom of +Transformation. A king rules over this land and employs a Council of +Wizardry. The main purpose of this kingdom is to provide a way for +neighboring kingdoms to transform citizens into more useful citizens. This +is done by allowing the citizens to enter the kingdom at one of its ports +and to travel any of the many routes in the kingdom. They are magically +transformed along the way. The income of the Kingdom of Transformation +comes from the many toll roads within its boundaries. + +The Kingdom of Transformation was created when several kingdoms got +together and discovered a mutual need for new talents and abilities for +citizens. They employed CTK, Inc. (Creators of Transformation, Inc.) to +create this kingdom. CTK designed the country, its transportation routes, +and its laws of transformation, and created the major highway system. + +Hazards +======= + +Because magic is not truly controllable, CTK invariably, but unknowingly, +creates dragons. Dragons are huge fire-breathing beasts which sometimes +injure or kill travelers. Fortunately, they do not travel, but always +remain near their den. + +Other hazards also exist which are potentially harmful. As the roads +become older and more weatherbeaten, pot-holes will develop, trees will +fall on travelers, etc. CTK maintenance men are called to fix these +problems. + +Wizards +======= + +The wizards play a major role in creating and maintaining the kingdom but +get little credit for their work because it is performed secretly. The +wizards do not wan the workers or travelers to learn their incantations +because many laws would be broken and chaos would result. + +CTK's grand design is always general enough to be applicable in many +different situations. As a result, it is often difficult to use. The +first duty of the wizards is to tailor the transformation laws so as to be +more beneficial and easier to use in their particular environment. + +After creation of the kingdom, a major duty of the wizards is to search for +and kill dragons. If travelers do not return on time or if they return +injured, the ruler of the country contacts the wizards. If the wizards +determine that the injury or death occurred due to the traveler's +negligence, they provide the traveler's country with additional warnings. +If not, they must determine if the cause was a road hazard or a dragon. If +the suspect a road hazard, they call in a CTK maintenance man to locate the +hazard and to eliminate it, as in repairing the pothole in the road. If +they think that cause was a dragon, then they must find and slay it. + +The most difficult part of eliminating a dragon is finding it. Sometimes +the wizard magically knows where the dragon's lair it, but often the wizard +must send another traveler along the same route and watch to see where he +disappears. This sounds like a failsafe method for finding dragons (and a +suicide mission for thr traveler) but the second traveler does not always +disappear. Some dragons eat any traveler who comes too close; others are +very picky. + +The wizards may call in CTK who designed the highway system and +transformation laws to help devise a way to locate the dragon. CTK also +helps provide the right spell or incantation to slay the dragon. (There is +no general spell to slay dragons; each dragon must be eliminated with a +different spell.) + +Because neither CTK nor wizards are perfect, spells to not always work +correctly. At best, nothing happens when the wrong spell is uttered. At +worst, the dragon becomes a much larger dragon or multiplies into several +smaller ones. In either case, new spells must be found. + +If all existing dragons are quiet (i.e. have eaten sufficiently), wizards +have time to do other things. They hide in castles and practice spells and +incatations. They also devise shortcuts for travelers and new laws of +transformation. + +Changes in the Kingdom +====================== + +As new transformation kingdoms are created and old ones are maintained, +CTK, Inc. is constantly learning new things. It learns ways to avoid +creating some of the dragons that they have previously created. It also +discovers new and better laws of transformation. As a result, CTK will +periodically create a new grand design which is far better than the old. +The wizards determine when is a good time to implement this new design. +This is when the tourist season is slow or when no important travelers +(VIPs) are to arrive. The kingdom must be closed for the actual +implementation and is leter reopened as a new and better place to go. + +A final question you might ask is what happens when the number of tourists +becomes too great for the kingdom to handle in a reasonable period of time +(i.e., the tourist lines at the ports are too long). The Kingdom of +Transformation has three options: (1) shorten the paths that a tourist must +travel, or (2) convince CTK to develop a faster breed of horses so that the +travelers can finish sooner, or (3) annex more territories so that the +kingdom can handle more travelers. + +Thus ends the story of the Kingdom of Transformation. I hope this has +explained my job to you: I slay dragons for a living. + +# +#should do an automatic undivert.. +# diff --git a/usr.bin/m4/eval.c b/usr.bin/m4/eval.c new file mode 100644 index 0000000..0e77c6b --- /dev/null +++ b/usr.bin/m4/eval.c @@ -0,0 +1,789 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)eval.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * eval.c + * Facility: m4 macro processor + * by: oz + */ + +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" +#include "pathnames.h" + +/* + * eval - evaluate built-in macros. + * argc - number of elements in argv. + * argv - element vector : + * argv[0] = definition of a user + * macro or nil if built-in. + * argv[1] = name of the macro or + * built-in. + * argv[2] = parameters to user-defined + * . macro or built-in. + * . + * + * Note that the minimum value for argc is 3. A call in the form + * of macro-or-builtin() will result in: + * argv[0] = nullstr + * argv[1] = macro-or-builtin + * argv[2] = nullstr + */ + +void +eval(argv, argc, td) +register char *argv[]; +register int argc; +register int td; +{ + register int c, n; + static int sysval = 0; + +#ifdef DEBUG + printf("argc = %d\n", argc); + for (n = 0; n < argc; n++) + printf("argv[%d] = %s\n", n, argv[n]); +#endif + /* + * if argc == 3 and argv[2] is null, then we + * have macro-or-builtin() type call. We adjust + * argc to avoid further checking.. + */ + if (argc == 3 && !*(argv[2])) + argc--; + + switch (td & ~STATIC) { + + case DEFITYPE: + if (argc > 2) + dodefine(argv[2], (argc > 3) ? argv[3] : null); + break; + + case PUSDTYPE: + if (argc > 2) + dopushdef(argv[2], (argc > 3) ? argv[3] : null); + break; + + case DUMPTYPE: + dodump(argv, argc); + break; + + case EXPRTYPE: + /* + * doexpr - evaluate arithmetic + * expression + */ + if (argc > 2) + pbnum(expr(argv[2])); + break; + + case IFELTYPE: + if (argc > 4) + doifelse(argv, argc); + break; + + case IFDFTYPE: + /* + * doifdef - select one of two + * alternatives based on the existence of + * another definition + */ + if (argc > 3) { + if (lookup(argv[2]) != nil) + pbstr(argv[3]); + else if (argc > 4) + pbstr(argv[4]); + } + break; + + case LENGTYPE: + /* + * dolen - find the length of the + * argument + */ + if (argc > 2) + pbnum((argc > 2) ? strlen(argv[2]) : 0); + break; + + case INCRTYPE: + /* + * doincr - increment the value of the + * argument + */ + if (argc > 2) + pbnum(atoi(argv[2]) + 1); + break; + + case DECRTYPE: + /* + * dodecr - decrement the value of the + * argument + */ + if (argc > 2) + pbnum(atoi(argv[2]) - 1); + break; + + case SYSCTYPE: + /* + * dosys - execute system command + */ + if (argc > 2) + sysval = system(argv[2]); + break; + + case SYSVTYPE: + /* + * dosysval - return value of the last + * system call. + * + */ + pbnum(sysval); + break; + + case INCLTYPE: + if (argc > 2) + if (!doincl(argv[2])) + oops("%s: %s", argv[2], strerror(errno)); + break; + + case SINCTYPE: + if (argc > 2) + (void) doincl(argv[2]); + break; +#ifdef EXTENDED + case PASTTYPE: + if (argc > 2) + if (!dopaste(argv[2])) + oops("%s: %s", argv[2], strerror(errno)); + break; + + case SPASTYPE: + if (argc > 2) + (void) dopaste(argv[2]); + break; +#endif + case CHNQTYPE: + dochq(argv, argc); + break; + + case CHNCTYPE: + dochc(argv, argc); + break; + + case SUBSTYPE: + /* + * dosub - select substring + * + */ + if (argc > 3) + dosub(argv, argc); + break; + + case SHIFTYPE: + /* + * doshift - push back all arguments + * except the first one (i.e. skip + * argv[2]) + */ + if (argc > 3) { + for (n = argc - 1; n > 3; n--) { + putback(rquote); + pbstr(argv[n]); + putback(lquote); + putback(','); + } + putback(rquote); + pbstr(argv[3]); + putback(lquote); + } + break; + + case DIVRTYPE: + if (argc > 2 && (n = atoi(argv[2])) != 0) + dodiv(n); + else { + active = stdout; + oindex = 0; + } + break; + + case UNDVTYPE: + doundiv(argv, argc); + break; + + case DIVNTYPE: + /* + * dodivnum - return the number of + * current output diversion + */ + pbnum(oindex); + break; + + case UNDFTYPE: + /* + * doundefine - undefine a previously + * defined macro(s) or m4 keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + remhash(argv[n], ALL); + break; + + case POPDTYPE: + /* + * dopopdef - remove the topmost + * definitions of macro(s) or m4 + * keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + remhash(argv[n], TOP); + break; + + case MKTMTYPE: + /* + * dotemp - create a temporary file + */ + if (argc > 2) + pbstr(mktemp(argv[2])); + break; + + case TRNLTYPE: + /* + * dotranslit - replace all characters in + * the source string that appears in the + * "from" string with the corresponding + * characters in the "to" string. + */ + if (argc > 3) { + char temp[MAXTOK]; + if (argc > 4) + map(temp, argv[2], argv[3], argv[4]); + else + map(temp, argv[2], argv[3], null); + pbstr(temp); + } + else if (argc > 2) + pbstr(argv[2]); + break; + + case INDXTYPE: + /* + * doindex - find the index of the second + * argument string in the first argument + * string. -1 if not present. + */ + pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); + break; + + case ERRPTYPE: + /* + * doerrp - print the arguments to stderr + * file + */ + if (argc > 2) { + for (n = 2; n < argc; n++) + fprintf(stderr, "%s ", argv[n]); + fprintf(stderr, "\n"); + } + break; + + case DNLNTYPE: + /* + * dodnl - eat-up-to and including + * newline + */ + while ((c = gpbc()) != '\n' && c != EOF) + ; + break; + + case M4WRTYPE: + /* + * dom4wrap - set up for + * wrap-up/wind-down activity + */ + m4wraps = (argc > 2) ? xstrdup(argv[2]) : null; + break; + + case EXITTYPE: + /* + * doexit - immediate exit from m4. + */ + exit((argc > 2) ? atoi(argv[2]) : 0); + break; + + case DEFNTYPE: + if (argc > 2) + for (n = 2; n < argc; n++) + dodefn(argv[n]); + break; + + default: + oops("%s: major botch.", "eval"); + break; + } +} + +char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ + +/* + * expand - user-defined macro expansion + */ +void +expand(argv, argc) +register char *argv[]; +register int argc; +{ + register char *t; + register char *p; + register int n; + register int argno; + + t = argv[0]; /* defn string as a whole */ + p = t; + while (*p) + p++; + p--; /* last character of defn */ + while (p > t) { + if (*(p - 1) != ARGFLAG) + putback(*p); + else { + switch (*p) { + + case '#': + pbnum(argc - 2); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if ((argno = *p - '0') < argc - 1) + pbstr(argv[argno + 1]); + break; + case '*': + for (n = argc - 1; n > 2; n--) { + pbstr(argv[n]); + putback(','); + } + pbstr(argv[2]); + break; + default: + putback(*p); + putback('$'); + break; + } + p--; + } + p--; + } + if (p == t) /* do last character */ + putback(*p); +} + +/* + * dodefine - install definition in the table + */ +void +dodefine(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + oops("null definition."); + if (STREQ(name, defn)) + oops("%s: recursive definition.", name); + if ((p = lookup(name)) == nil) + p = addent(name); + else if (p->defn != null) + free((char *) p->defn); + if (!*defn) + p->defn = null; + else + p->defn = xstrdup(defn); + p->type = MACRTYPE; +} + +/* + * dodefn - push back a quoted definition of + * the given name. + */ +void +dodefn(name) +char *name; +{ + register ndptr p; + + if ((p = lookup(name)) != nil && p->defn != null) { + putback(rquote); + pbstr(p->defn); + putback(lquote); + } +} + +/* + * dopushdef - install a definition in the hash table + * without removing a previous definition. Since + * each new entry is entered in *front* of the + * hash bucket, it hides a previous definition from + * lookup. + */ +void +dopushdef(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + oops("null definition"); + if (STREQ(name, defn)) + oops("%s: recursive definition.", name); + p = addent(name); + if (!*defn) + p->defn = null; + else + p->defn = xstrdup(defn); + p->type = MACRTYPE; +} + +/* + * dodumpdef - dump the specified definitions in the hash + * table to stderr. If nothing is specified, the entire + * hash table is dumped. + */ +void +dodump(argv, argc) +register char *argv[]; +register int argc; +{ + register int n; + ndptr p; + + if (argc > 2) { + for (n = 2; n < argc; n++) + if ((p = lookup(argv[n])) != nil) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } + else { + for (n = 0; n < HASHSIZE; n++) + for (p = hashtab[n]; p != nil; p = p->nxtptr) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } +} + +/* + * doifelse - select one of two alternatives - loop. + */ +void +doifelse(argv, argc) +register char *argv[]; +register int argc; +{ + cycle { + if (STREQ(argv[2], argv[3])) + pbstr(argv[4]); + else if (argc == 6) + pbstr(argv[5]); + else if (argc > 6) { + argv += 3; + argc -= 3; + continue; + } + break; + } +} + +/* + * doinclude - include a given file. + */ +int +doincl(ifile) +char *ifile; +{ + if (ilevel + 1 == MAXINP) + oops("too many include files."); + if ((infile[ilevel + 1] = fopen(ifile, "r")) != NULL) { + ilevel++; + bbase[ilevel] = bufbase = bp; + return (1); + } + else + return (0); +} + +#ifdef EXTENDED +/* + * dopaste - include a given file without any + * macro processing. + */ +int +dopaste(pfile) +char *pfile; +{ + FILE *pf; + register int c; + + if ((pf = fopen(pfile, "r")) != NULL) { + while ((c = getc(pf)) != EOF) + putc(c, active); + (void) fclose(pf); + return (1); + } + else + return (0); +} +#endif + +/* + * dochq - change quote characters + */ +void +dochq(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + lquote = *argv[2]; + if (argc > 3) { + if (*argv[3]) + rquote = *argv[3]; + } + else + rquote = lquote; + } + else { + lquote = LQUOTE; + rquote = RQUOTE; + } +} + +/* + * dochc - change comment characters + */ +void +dochc(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + scommt = *argv[2]; + if (argc > 3) { + if (*argv[3]) + ecommt = *argv[3]; + } + else + ecommt = ECOMMT; + } + else { + scommt = SCOMMT; + ecommt = ECOMMT; + } +} + +/* + * dodivert - divert the output to a temporary file + */ +void +dodiv(n) +register int n; +{ + if (n < 0 || n >= MAXOUT) + n = 0; /* bitbucket */ + if (outfile[n] == NULL) { + m4temp[UNIQUE] = n + '0'; + if ((outfile[n] = fopen(m4temp, "w")) == NULL) + oops("%s: cannot divert.", m4temp); + } + oindex = n; + active = outfile[n]; +} + +/* + * doundivert - undivert a specified output, or all + * other outputs, in numerical order. + */ +void +doundiv(argv, argc) +register char *argv[]; +register int argc; +{ + register int ind; + register int n; + + if (argc > 2) { + for (ind = 2; ind < argc; ind++) { + n = atoi(argv[ind]); + if (n > 0 && n < MAXOUT && outfile[n] != NULL) + getdiv(n); + + } + } + else + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) + getdiv(n); +} + +/* + * dosub - select substring + */ +void +dosub(argv, argc) +register char *argv[]; +register int argc; +{ + register char *ap, *fc, *k; + register int nc; + + if (argc < 5) + nc = MAXTOK; + else +#ifdef EXPR + nc = expr(argv[4]); +#else + nc = atoi(argv[4]); +#endif + ap = argv[2]; /* target string */ +#ifdef EXPR + fc = ap + expr(argv[3]); /* first char */ +#else + fc = ap + atoi(argv[3]); /* first char */ +#endif + if (fc >= ap && fc < ap + strlen(ap)) + for (k = fc + min(nc, strlen(fc)) - 1; k >= fc; k--) + putback(*k); +} + +/* + * map: + * map every character of s1 that is specified in from + * into s3 and replace in s. (source s1 remains untouched) + * + * This is a standard implementation of map(s,from,to) function of ICON + * language. Within mapvec, we replace every character of "from" with + * the corresponding character in "to". If "to" is shorter than "from", + * than the corresponding entries are null, which means that those + * characters dissapear altogether. Furthermore, imagine + * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, + * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' + * ultimately maps to `*'. In order to achieve this effect in an efficient + * manner (i.e. without multiple passes over the destination string), we + * loop over mapvec, starting with the initial source character. if the + * character value (dch) in this location is different than the source + * character (sch), sch becomes dch, once again to index into mapvec, until + * the character value stabilizes (i.e. sch = dch, in other words + * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary + * character, it will stabilize, since mapvec[0] == 0 at all times. At the + * end, we restore mapvec* back to normal where mapvec[n] == n for + * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is + * about 5 times faster than any algorithm that makes multiple passes over + * destination string. + */ +void +map(dest, src, from, to) +register char *dest; +register char *src; +register char *from; +register char *to; +{ + register char *tmp; + register char sch, dch; + static char mapvec[128] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127 + }; + + if (*src) { + tmp = from; + /* + * create a mapping between "from" and + * "to" + */ + while (*from) + mapvec[*from++] = (*to) ? *to++ : (char) 0; + + while (*src) { + sch = *src++; + dch = mapvec[sch]; + while (dch != sch) { + sch = dch; + dch = mapvec[sch]; + } + if (*dest = dch) + dest++; + } + /* + * restore all the changed characters + */ + while (*tmp) { + mapvec[*tmp] = *tmp; + tmp++; + } + } + *dest = (char) 0; +} diff --git a/usr.bin/m4/expr.c b/usr.bin/m4/expr.c new file mode 100644 index 0000000..8503733 --- /dev/null +++ b/usr.bin/m4/expr.c @@ -0,0 +1,626 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)expr.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include <sys/cdefs.h> +#include <stdio.h> + +/* + * expression evaluator: performs a standard recursive + * descent parse to evaluate any expression permissible + * within the following grammar: + * + * expr : query EOS + * query : lor + * | lor "?" query ":" query + * lor : land { "||" land } + * land : bor { "&&" bor } + * bor : bxor { "|" bxor } + * bxor : band { "^" band } + * band : eql { "&" eql } + * eql : relat { eqrel relat } + * relat : shift { rel shift } + * shift : primary { shop primary } + * primary : term { addop term } + * term : unary { mulop unary } + * unary : factor + * | unop unary + * factor : constant + * | "(" query ")" + * constant: num + * | "'" CHAR "'" + * num : DIGIT + * | DIGIT num + * shop : "<<" + * | ">>" + * eqlrel : "=" + * | "==" + * | "!=" + * rel : "<" + * | ">" + * | "<=" + * | ">=" + * + * + * This expression evaluator is lifted from a public-domain + * C Pre-Processor included with the DECUS C Compiler distribution. + * It is hacked somewhat to be suitable for m4. + * + * Originally by: Mike Lutz + * Bob Harper + */ + +#define TRUE 1 +#define FALSE 0 +#define EOS (char) 0 +#define EQL 0 +#define NEQ 1 +#define LSS 2 +#define LEQ 3 +#define GTR 4 +#define GEQ 5 +#define OCTAL 8 +#define DECIMAL 10 + +static char *nxtch; /* Parser scan pointer */ + +static int query __P((void)); +static int lor __P((void)); +static int land __P((void)); +static int bor __P((void)); +static int bxor __P((void)); +static int band __P((void)); +static int eql __P((void)); +static int relat __P((void)); +static int shift __P((void)); +static int primary __P((void)); +static int term __P((void)); +static int unary __P((void)); +static int factor __P((void)); +static int constant __P((void)); +static int num __P((void)); +static int geteql __P((void)); +static int getrel __P((void)); +static int skipws __P((void)); +static void experr __P((char *)); + +/* + * For longjmp + */ +#include <setjmp.h> +static jmp_buf expjump; + +/* + * macros: + * ungetch - Put back the last character examined. + * getch - return the next character from expr string. + */ +#define ungetch() nxtch-- +#define getch() *nxtch++ + +int +expr(expbuf) +char *expbuf; +{ + register int rval; + + nxtch = expbuf; + if (setjmp(expjump) != 0) + return FALSE; + + rval = query(); + if (skipws() == EOS) + return rval; + + printf("m4: ill-formed expression.\n"); + return FALSE; +} + +/* + * query : lor | lor '?' query ':' query + */ +static int +query() +{ + register int bool, true_val, false_val; + + bool = lor(); + if (skipws() != '?') { + ungetch(); + return bool; + } + + true_val = query(); + if (skipws() != ':') + experr("bad query"); + + false_val = query(); + return bool ? true_val : false_val; +} + +/* + * lor : land { '||' land } + */ +static int +lor() +{ + register int c, vl, vr; + + vl = land(); + while ((c = skipws()) == '|' && getch() == '|') { + vr = land(); + vl = vl || vr; + } + + if (c == '|') + ungetch(); + ungetch(); + return vl; +} + +/* + * land : bor { '&&' bor } + */ +static int +land() +{ + register int c, vl, vr; + + vl = bor(); + while ((c = skipws()) == '&' && getch() == '&') { + vr = bor(); + vl = vl && vr; + } + + if (c == '&') + ungetch(); + ungetch(); + return vl; +} + +/* + * bor : bxor { '|' bxor } + */ +static int +bor() +{ + register int vl, vr, c; + + vl = bxor(); + while ((c = skipws()) == '|' && getch() != '|') { + ungetch(); + vr = bxor(); + vl |= vr; + } + + if (c == '|') + ungetch(); + ungetch(); + return vl; +} + +/* + * bxor : band { '^' band } + */ +static int +bxor() +{ + register int vl, vr; + + vl = band(); + while (skipws() == '^') { + vr = band(); + vl ^= vr; + } + + ungetch(); + return vl; +} + +/* + * band : eql { '&' eql } + */ +static int +band() +{ + register int vl, vr, c; + + vl = eql(); + while ((c = skipws()) == '&' && getch() != '&') { + ungetch(); + vr = eql(); + vl &= vr; + } + + if (c == '&') + ungetch(); + ungetch(); + return vl; +} + +/* + * eql : relat { eqrel relat } + */ +static int +eql() +{ + register int vl, vr, rel; + + vl = relat(); + while ((rel = geteql()) != -1) { + vr = relat(); + + switch (rel) { + + case EQL: + vl = (vl == vr); + break; + case NEQ: + vl = (vl != vr); + break; + } + } + return vl; +} + +/* + * relat : shift { rel shift } + */ +static int +relat() +{ + register int vl, vr, rel; + + vl = shift(); + while ((rel = getrel()) != -1) { + + vr = shift(); + switch (rel) { + + case LEQ: + vl = (vl <= vr); + break; + case LSS: + vl = (vl < vr); + break; + case GTR: + vl = (vl > vr); + break; + case GEQ: + vl = (vl >= vr); + break; + } + } + return vl; +} + +/* + * shift : primary { shop primary } + */ +static int +shift() +{ + register int vl, vr, c; + + vl = primary(); + while (((c = skipws()) == '<' || c == '>') && c == getch()) { + vr = primary(); + + if (c == '<') + vl <<= vr; + else + vl >>= vr; + } + + if (c == '<' || c == '>') + ungetch(); + ungetch(); + return vl; +} + +/* + * primary : term { addop term } + */ +static int +primary() +{ + register int c, vl, vr; + + vl = term(); + while ((c = skipws()) == '+' || c == '-') { + vr = term(); + if (c == '+') + vl += vr; + else + vl -= vr; + } + + ungetch(); + return vl; +} + +/* + * <term> := <unary> { <mulop> <unary> } + */ +static int +term() +{ + register int c, vl, vr; + + vl = unary(); + while ((c = skipws()) == '*' || c == '/' || c == '%') { + vr = unary(); + + switch (c) { + case '*': + vl *= vr; + break; + case '/': + vl /= vr; + break; + case '%': + vl %= vr; + break; + } + } + ungetch(); + return vl; +} + +/* + * unary : factor | unop unary + */ +static int +unary() +{ + register int val, c; + + if ((c = skipws()) == '!' || c == '~' || c == '-') { + val = unary(); + + switch (c) { + case '!': + return !val; + case '~': + return ~val; + case '-': + return -val; + } + } + + ungetch(); + return factor(); +} + +/* + * factor : constant | '(' query ')' + */ +static int +factor() +{ + register int val; + + if (skipws() == '(') { + val = query(); + if (skipws() != ')') + experr("bad factor"); + return val; + } + + ungetch(); + return constant(); +} + +/* + * constant: num | 'char' + * Note: constant() handles multi-byte constants + */ +static int +constant() +{ + register int i; + register int value; + register char c; + int v[sizeof(int)]; + + if (skipws() != '\'') { + ungetch(); + return num(); + } + for (i = 0; i < sizeof(int); i++) { + if ((c = getch()) == '\'') { + ungetch(); + break; + } + if (c == '\\') { + switch (c = getch()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + ungetch(); + c = num(); + break; + case 'n': + c = 012; + break; + case 'r': + c = 015; + break; + case 't': + c = 011; + break; + case 'b': + c = 010; + break; + case 'f': + c = 014; + break; + } + } + v[i] = c; + } + if (i == 0 || getch() != '\'') + experr("illegal character constant"); + for (value = 0; --i >= 0;) { + value <<= 8; + value += v[i]; + } + return value; +} + +/* + * num : digit | num digit + */ +static int +num() +{ + register int rval, c, base; + int ndig; + + base = ((c = skipws()) == '0') ? OCTAL : DECIMAL; + rval = 0; + ndig = 0; + while (c >= '0' && c <= (base == OCTAL ? '7' : '9')) { + rval *= base; + rval += (c - '0'); + c = getch(); + ndig++; + } + ungetch(); + + if (ndig == 0) + experr("bad constant"); + + return rval; + +} + +/* + * eqlrel : '=' | '==' | '!=' + */ +static int +geteql() +{ + register int c1, c2; + + c1 = skipws(); + c2 = getch(); + + switch (c1) { + + case '=': + if (c2 != '=') + ungetch(); + return EQL; + + case '!': + if (c2 == '=') + return NEQ; + ungetch(); + ungetch(); + return -1; + + default: + ungetch(); + ungetch(); + return -1; + } +} + +/* + * rel : '<' | '>' | '<=' | '>=' + */ +static int +getrel() +{ + register int c1, c2; + + c1 = skipws(); + c2 = getch(); + + switch (c1) { + + case '<': + if (c2 == '=') + return LEQ; + ungetch(); + return LSS; + + case '>': + if (c2 == '=') + return GEQ; + ungetch(); + return GTR; + + default: + ungetch(); + ungetch(); + return -1; + } +} + +/* + * Skip over any white space and return terminating char. + */ +static int +skipws() +{ + register char c; + + while ((c = getch()) <= ' ' && c > EOS) + ; + return c; +} + +/* + * resets environment to eval(), prints an error + * and forces eval to return FALSE. + */ +static void +experr(msg) +char *msg; +{ + printf("m4: %s in expr.\n", msg); + longjmp(expjump, -1); +} diff --git a/usr.bin/m4/extern.h b/usr.bin/m4/extern.h new file mode 100644 index 0000000..b54a9b9 --- /dev/null +++ b/usr.bin/m4/extern.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + */ + +char *basename __P((char *)); +char *xalloc __P((unsigned long)); +int expr __P((char *)); +ndptr addent __P((char *)); +void chrsave __P((int)); +void dochc __P((char *[], int)); +void dochq __P((char *[], int)); +void dodefine __P((char *, char *)); +void dodefn __P((char *)); +void dodiv __P((int)); +void dodump __P((char *[], int)); +void doifelse __P((char *[], int)); +int doincl __P((char *)); +int dopaste __P((char *)); +void dopushdef __P((char *, char *)); +void dosub __P((char *[], int)); +void doundiv __P((char *[], int)); +void eval __P((char *[], int, int)); +void expand __P((char *[], int)); +void getdiv __P((int)); +char *xstrdup __P((const char *)); +int hash __P((char *)); +int indx __P((char *, char *)); +void killdiv __P((void)); +ndptr lookup __P((char *)); +void map __P((char *, char *, char *, char *)); +void onintr __P((int)); +void oops __P((const char *, ...)); +void pbnum __P((int)); +void pbstr __P((char *)); +void putback __P((int)); +void remhash __P((char *, int)); +void usage __P((void)); + +extern ndptr hashtab[]; /* hash table for macros etc. */ +extern stae mstack[]; /* stack of m4 machine */ +extern FILE *active; /* active output file pointer */ +extern FILE *infile[]; /* input file stack (0=stdin) */ +extern FILE *outfile[]; /* diversion array(0=bitbucket) */ +extern int fp; /* m4 call frame pointer */ +extern int ilevel; /* input file stack pointer */ +extern int oindex; /* diversion index. */ +extern int sp; /* current m4 stack pointer */ +extern char *bp; /* first available character */ +extern char buf[]; /* push-back buffer */ +extern char *bufbase; /* buffer base for this ilevel */ +extern char *bbase[]; /* buffer base per ilevel */ +extern char ecommt; /* end character for comment */ +extern char *endest; /* end of string space */ +extern char *endpbb; /* end of push-back buffer */ +extern char *ep; /* first free char in strspace */ +extern char lquote; /* left quote character (`) */ +extern char *m4temp; /* filename for diversions */ +extern char *m4wraps; /* m4wrap string default. */ +extern char *null; /* as it says.. just a null. */ +extern char *progname; /* program name */ +extern char rquote; /* right quote character (') */ +extern char scommt; /* start character for comment */ diff --git a/usr.bin/m4/look.c b/usr.bin/m4/look.c new file mode 100644 index 0000000..7c750b0 --- /dev/null +++ b/usr.bin/m4/look.c @@ -0,0 +1,145 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)look.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * look.c + * Facility: m4 macro processor + * by: oz + */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" + +int +hash(name) +register char *name; +{ + register unsigned long h = 0; + while (*name) + h = (h << 5) + h + *name++; + return (h % HASHSIZE); +} + +/* + * find name in the hash table + */ +ndptr +lookup(name) +char *name; +{ + register ndptr p; + + for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr) + if (STREQ(name, p->name)) + break; + return (p); +} + +/* + * hash and create an entry in the hash table. + * The new entry is added in front of a hash bucket. + */ +ndptr +addent(name) +char *name; +{ + register int h; + ndptr p; + + h = hash(name); + p = (ndptr) xalloc(sizeof(struct ndblock)); + p->nxtptr = hashtab[h]; + hashtab[h] = p; + p->name = xstrdup(name); + return p; +} + +static void +freent(p) +ndptr p; +{ + if (!(p->type & STATIC)) { + free((char *) p->name); + if (p->defn != null) + free((char *) p->defn); + } + free((char *) p); +} + +/* + * remove an entry from the hashtable + */ +void +remhash(name, all) +char *name; +int all; +{ + register int h; + register ndptr xp, tp, mp; + + h = hash(name); + mp = hashtab[h]; + tp = nil; + while (mp != nil) { + if (STREQ(mp->name, name)) { + mp = mp->nxtptr; + if (tp == nil) { + freent(hashtab[h]); + hashtab[h] = mp; + } + else { + xp = tp->nxtptr; + tp->nxtptr = mp; + freent(xp); + } + if (!all) + break; + } + else { + tp = mp; + mp = mp->nxtptr; + } + } +} diff --git a/usr.bin/m4/main.c b/usr.bin/m4/main.c new file mode 100644 index 0000000..221b865 --- /dev/null +++ b/usr.bin/m4/main.c @@ -0,0 +1,425 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * main.c + * Facility: m4 macro processor + * by: oz + */ + +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <ctype.h> +#include <string.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" +#include "pathnames.h" + +ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ +char buf[BUFSIZE]; /* push-back buffer */ +char *bufbase = buf; /* the base for current ilevel */ +char *bbase[MAXINP]; /* the base for each ilevel */ +char *bp = buf; /* first available character */ +char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ +stae mstack[STACKMAX+1]; /* stack of m4 machine */ +char strspace[STRSPMAX+1]; /* string space for evaluation */ +char *ep = strspace; /* first free char in strspace */ +char *endest= strspace+STRSPMAX;/* end of string space */ +int sp; /* current m4 stack pointer */ +int fp; /* m4 call frame pointer */ +FILE *infile[MAXINP]; /* input file stack (0=stdin) */ +FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ +FILE *active; /* active output file pointer */ +char *m4temp; /* filename for diversions */ +int ilevel = 0; /* input file stack pointer */ +int oindex = 0; /* diversion index.. */ +char *null = ""; /* as it says.. just a null.. */ +char *m4wraps = ""; /* m4wrap string default.. */ +char *progname; /* name of this program */ +char lquote = LQUOTE; /* left quote character (`) */ +char rquote = RQUOTE; /* right quote character (') */ +char scommt = SCOMMT; /* start character for comment */ +char ecommt = ECOMMT; /* end character for comment */ + +struct keyblk keywrds[] = { /* m4 keywords to be installed */ + "include", INCLTYPE, + "sinclude", SINCTYPE, + "define", DEFITYPE, + "defn", DEFNTYPE, + "divert", DIVRTYPE, + "expr", EXPRTYPE, + "eval", EXPRTYPE, + "substr", SUBSTYPE, + "ifelse", IFELTYPE, + "ifdef", IFDFTYPE, + "len", LENGTYPE, + "incr", INCRTYPE, + "decr", DECRTYPE, + "dnl", DNLNTYPE, + "changequote", CHNQTYPE, + "changecom", CHNCTYPE, + "index", INDXTYPE, +#ifdef EXTENDED + "paste", PASTTYPE, + "spaste", SPASTYPE, +#endif + "popdef", POPDTYPE, + "pushdef", PUSDTYPE, + "dumpdef", DUMPTYPE, + "shift", SHIFTYPE, + "translit", TRNLTYPE, + "undefine", UNDFTYPE, + "undivert", UNDVTYPE, + "divnum", DIVNTYPE, + "maketemp", MKTMTYPE, + "errprint", ERRPTYPE, + "m4wrap", M4WRTYPE, + "m4exit", EXITTYPE, + "syscmd", SYSCTYPE, + "sysval", SYSVTYPE, + +#ifdef unix + "unix", MACRTYPE, +#else +#ifdef vms + "vms", MACRTYPE, +#endif +#endif +}; + +#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) + +extern int optind; +extern char *optarg; + +void macro(); +void initkwds(); +extern int getopt(); + +int +main(argc,argv) + int argc; + char *argv[]; +{ + register int c; + register int n; + char *p; + register FILE *ifp; + + progname = basename(argv[0]); + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); + + initkwds(); + + while ((c = getopt(argc, argv, "tD:U:o:")) != EOF) + switch(c) { + + case 'D': /* define something..*/ + for (p = optarg; *p; p++) + if (*p == '=') + break; + if (*p) + *p++ = EOS; + dodefine(optarg, p); + break; + case 'U': /* undefine... */ + remhash(optarg, TOP); + break; + case 'o': /* specific output */ + case '?': + usage(); + } + + argc -= optind; + argv += optind; + + active = stdout; /* default active output */ + /* filename for diversions */ + m4temp = mktemp(xstrdup(_PATH_DIVNAME)); + + bbase[0] = bufbase; + if (!argc) { + sp = -1; /* stack pointer initialized */ + fp = 0; /* frame pointer initialized */ + infile[0] = stdin; /* default input (naturally) */ + macro(); + } else + for (; argc--; ++argv) { + p = *argv; + if (p[0] == '-' && p[1] == '\0') + ifp = stdin; + else if ((ifp = fopen(p, "r")) == NULL) + oops("%s: %s", p, strerror(errno)); + sp = -1; + fp = 0; + infile[0] = ifp; + macro(); + if (ifp != stdin) + (void)fclose(ifp); + } + + if (*m4wraps) { /* anything for rundown ?? */ + ilevel = 0; /* in case m4wrap includes.. */ + bufbase = bp = buf; /* use the entire buffer */ + putback(EOF); /* eof is a must !! */ + pbstr(m4wraps); /* user-defined wrapup act */ + macro(); /* last will and testament */ + } + + if (active != stdout) + active = stdout; /* reset output just in case */ + for (n = 1; n < MAXOUT; n++) /* default wrap-up: undivert */ + if (outfile[n] != NULL) + getdiv(n); + /* remove bitbucket if used */ + if (outfile[0] != NULL) { + (void) fclose(outfile[0]); + m4temp[UNIQUE] = '0'; +#ifdef vms + (void) remove(m4temp); +#else + (void) unlink(m4temp); +#endif + } + + return 0; +} + +ndptr inspect(); + +/* + * macro - the work horse.. + */ +void +macro() { + char token[MAXTOK]; + register char *s; + register int t, l; + register ndptr p; + register int nlpar; + + cycle { + if ((t = gpbc()) == '_' || isalpha(t)) { + putback(t); + if ((p = inspect(s = token)) == nil) { + if (sp < 0) + while (*s) + putc(*s++, active); + else + while (*s) + chrsave(*s++); + } + else { + /* + * real thing.. First build a call frame: + */ + pushf(fp); /* previous call frm */ + pushf(p->type); /* type of the call */ + pushf(0); /* parenthesis level */ + fp = sp; /* new frame pointer */ + /* + * now push the string arguments: + */ + pushs(p->defn); /* defn string */ + pushs(p->name); /* macro name */ + pushs(ep); /* start next..*/ + + putback(l = gpbc()); + if (l != LPAREN) { /* add bracks */ + putback(RPAREN); + putback(LPAREN); + } + } + } + else if (t == EOF) { + if (sp > -1) + oops("unexpected end of input", ""); + if (ilevel <= 0) + break; /* all done thanks.. */ + --ilevel; + (void) fclose(infile[ilevel+1]); + bufbase = bbase[ilevel]; + continue; + } + /* + * non-alpha single-char token seen.. + * [the order of else if .. stmts is important.] + */ + else if (t == lquote) { /* strip quotes */ + nlpar = 1; + do { + if ((l = gpbc()) == rquote) + nlpar--; + else if (l == lquote) + nlpar++; + else if (l == EOF) + oops("missing right quote", ""); + if (nlpar > 0) { + if (sp < 0) + putc(l, active); + else + chrsave(l); + } + } + while (nlpar != 0); + } + + else if (sp < 0) { /* not in a macro at all */ + if (t == scommt) { /* comment handling here */ + putc(t, active); + while ((t = gpbc()) != ecommt) + putc(t, active); + } + putc(t, active); /* output directly.. */ + } + + else switch(t) { + + case LPAREN: + if (PARLEV > 0) + chrsave(t); + while (isspace(l = gpbc())) + ; /* skip blank, tab, nl.. */ + putback(l); + PARLEV++; + break; + + case RPAREN: + if (--PARLEV > 0) + chrsave(t); + else { /* end of argument list */ + chrsave(EOS); + + if (sp == STACKMAX) + oops("internal stack overflow", ""); + + if (CALTYP == MACRTYPE) + expand((char **) mstack+fp+1, sp-fp); + else + eval((char **) mstack+fp+1, sp-fp, CALTYP); + + ep = PREVEP; /* flush strspace */ + sp = PREVSP; /* previous sp.. */ + fp = PREVFP; /* rewind stack...*/ + } + break; + + case COMMA: + if (PARLEV == 1) { + chrsave(EOS); /* new argument */ + while (isspace(l = gpbc())) + ; + putback(l); + pushs(ep); + } else + chrsave(t); + break; + + default: + chrsave(t); /* stack the char */ + break; + } + } +} + +/* + * build an input token.. + * consider only those starting with _ or A-Za-z. This is a + * combo with lookup to speed things up. + */ +ndptr +inspect(tp) +register char *tp; +{ + register char c; + register char *name = tp; + register char *etp = tp+MAXTOK; + register ndptr p; + register unsigned long h = 0; + + while ((isalnum(c = gpbc()) || c == '_') && tp < etp) + h = (h << 5) + h + (*tp++ = c); + putback(c); + if (tp == etp) + oops("token too long", ""); + + *tp = EOS; + + for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) + if (STREQ(name, p->name)) + break; + return p; +} + +/* + * initkwds - initialise m4 keywords as fast as possible. + * This very similar to install, but without certain overheads, + * such as calling lookup. Malloc is not used for storing the + * keyword strings, since we simply use the static pointers + * within keywrds block. + */ +void +initkwds() { + register int i; + register int h; + register ndptr p; + + for (i = 0; i < MAXKEYS; i++) { + h = hash(keywrds[i].knam); + p = (ndptr) xalloc(sizeof(struct ndblock)); + p->nxtptr = hashtab[h]; + hashtab[h] = p; + p->name = keywrds[i].knam; + p->defn = null; + p->type = keywrds[i].ktyp | STATIC; + } +} diff --git a/usr.bin/m4/mdef.h b/usr.bin/m4/mdef.h new file mode 100644 index 0000000..cc256b0 --- /dev/null +++ b/usr.bin/m4/mdef.h @@ -0,0 +1,176 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mdef.h 8.1 (Berkeley) 6/6/93 + */ + +#define MACRTYPE 1 +#define DEFITYPE 2 +#define EXPRTYPE 3 +#define SUBSTYPE 4 +#define IFELTYPE 5 +#define LENGTYPE 6 +#define CHNQTYPE 7 +#define SYSCTYPE 8 +#define UNDFTYPE 9 +#define INCLTYPE 10 +#define SINCTYPE 11 +#define PASTTYPE 12 +#define SPASTYPE 13 +#define INCRTYPE 14 +#define IFDFTYPE 15 +#define PUSDTYPE 16 +#define POPDTYPE 17 +#define SHIFTYPE 18 +#define DECRTYPE 19 +#define DIVRTYPE 20 +#define UNDVTYPE 21 +#define DIVNTYPE 22 +#define MKTMTYPE 23 +#define ERRPTYPE 24 +#define M4WRTYPE 25 +#define TRNLTYPE 26 +#define DNLNTYPE 27 +#define DUMPTYPE 28 +#define CHNCTYPE 29 +#define INDXTYPE 30 +#define SYSVTYPE 31 +#define EXITTYPE 32 +#define DEFNTYPE 33 + +#define STATIC 128 + +/* + * m4 special characters + */ + +#define ARGFLAG '$' +#define LPAREN '(' +#define RPAREN ')' +#define LQUOTE '`' +#define RQUOTE '\'' +#define COMMA ',' +#define SCOMMT '#' +#define ECOMMT '\n' + +#ifdef msdos +#define system(str) (-1) +#endif + +/* + * other important constants + */ + +#define EOS (char) 0 +#define MAXINP 10 /* maximum include files */ +#define MAXOUT 10 /* maximum # of diversions */ +#define MAXSTR 512 /* maximum size of string */ +#define BUFSIZE 4096 /* size of pushback buffer */ +#define STACKMAX 1024 /* size of call stack */ +#define STRSPMAX 4096 /* size of string space */ +#define MAXTOK MAXSTR /* maximum chars in a tokn */ +#define HASHSIZE 199 /* maximum size of hashtab */ + +#define ALL 1 +#define TOP 0 + +#define TRUE 1 +#define FALSE 0 +#define cycle for(;;) + +/* + * m4 data structures + */ + +typedef struct ndblock *ndptr; + +struct ndblock { /* hastable structure */ + char *name; /* entry name.. */ + char *defn; /* definition.. */ + int type; /* type of the entry.. */ + ndptr nxtptr; /* link to next entry.. */ +}; + +#define nil ((ndptr) 0) + +struct keyblk { + char *knam; /* keyword name */ + int ktyp; /* keyword type */ +}; + +typedef union { /* stack structure */ + int sfra; /* frame entry */ + char *sstr; /* string entry */ +} stae; + +/* + * macros for readibility and/or speed + * + * gpbc() - get a possibly pushed-back character + * pushf() - push a call frame entry onto stack + * pushs() - push a string pointer onto stack + */ +#define gpbc() (bp > bufbase) ? *--bp : getc(infile[ilevel]) +#define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x) +#define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x) + +/* + * . . + * | . | <-- sp | . | + * +-------+ +-----+ + * | arg 3 ----------------------->| str | + * +-------+ | . | + * | arg 2 ---PREVEP-----+ . + * +-------+ | + * . | | | + * +-------+ | +-----+ + * | plev | PARLEV +-------->| str | + * +-------+ | . | + * | type | CALTYP . + * +-------+ + * | prcf ---PREVFP--+ + * +-------+ | + * | . | PREVSP | + * . | + * +-------+ | + * | <----------+ + * +-------+ + * + */ +#define PARLEV (mstack[fp].sfra) +#define CALTYP (mstack[fp-1].sfra) +#define PREVEP (mstack[fp+3].sstr) +#define PREVSP (fp-3) +#define PREVFP (mstack[fp-2].sfra) diff --git a/usr.bin/m4/misc.c b/usr.bin/m4/misc.c new file mode 100644 index 0000000..2ed115b --- /dev/null +++ b/usr.bin/m4/misc.c @@ -0,0 +1,266 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +#include <sys/types.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "mdef.h" +#include "stdd.h" +#include "extern.h" +#include "pathnames.h" + +/* + * find the index of second str in the first str. + */ +int +indx(s1, s2) +char *s1; +char *s2; +{ + register char *t; + register char *p; + register char *m; + + for (p = s1; *p; p++) { + for (t = p, m = s2; *m && *m == *t; m++, t++); + if (!*m) + return (p - s1); + } + return (-1); +} +/* + * putback - push character back onto input + */ +void +putback(c) +char c; +{ + if (bp < endpbb) + *bp++ = c; + else + oops("too many characters pushed back"); +} + +/* + * pbstr - push string back onto input + * putback is replicated to improve + * performance. + */ +void +pbstr(s) +register char *s; +{ + register char *es; + register char *zp; + + es = s; + zp = bp; + + while (*es) + es++; + es--; + while (es >= s) + if (zp < endpbb) + *zp++ = *es--; + if ((bp = zp) == endpbb) + oops("too many characters pushed back"); +} + +/* + * pbnum - convert number to string, push back on input. + */ +void +pbnum(n) +int n; +{ + register int num; + + num = (n < 0) ? -n : n; + do { + putback(num % 10 + '0'); + } + while ((num /= 10) > 0); + + if (n < 0) + putback('-'); +} + +/* + * chrsave - put single char on string space + */ +void +chrsave(c) +char c; +{ + if (ep < endest) + *ep++ = c; + else + oops("string space overflow"); +} + +/* + * read in a diversion file, and dispose it. + */ +void +getdiv(n) +int n; +{ + register int c; + register FILE *dfil; + + if (active == outfile[n]) + oops("%s: diversion still active.", "undivert"); + (void) fclose(outfile[n]); + outfile[n] = NULL; + m4temp[UNIQUE] = n + '0'; + if ((dfil = fopen(m4temp, "r")) == NULL) + oops("%s: cannot undivert.", m4temp); + else + while ((c = getc(dfil)) != EOF) + putc(c, active); + (void) fclose(dfil); + +#ifdef vms + if (remove(m4temp)) +#else + if (unlink(m4temp) == -1) +#endif + oops("%s: cannot unlink.", m4temp); +} + +void +onintr(signo) + int signo; +{ + oops("interrupted."); +} + +/* + * killdiv - get rid of the diversion files + */ +void +killdiv() +{ + register int n; + + for (n = 0; n < MAXOUT; n++) + if (outfile[n] != NULL) { + (void) fclose(outfile[n]); + m4temp[UNIQUE] = n + '0'; +#ifdef vms + (void) remove(m4temp); +#else + (void) unlink(m4temp); +#endif + } +} + +char * +xalloc(n) +unsigned long n; +{ + register char *p = malloc(n); + + if (p == NULL) + oops("malloc: %s", strerror(errno)); + return p; +} + +char * +xstrdup(s) +const char *s; +{ + register char *p = strdup(s); + if (p == NULL) + oops("strdup: %s", strerror(errno)); + return p; +} + +char * +basename(s) +register char *s; +{ + register char *p; + extern char *strrchr(); + + if ((p = strrchr(s, '/')) == NULL) + return s; + + return ++p; +} + +void +usage() +{ + fprintf(stderr, "usage: m4 [-Dname[=val]] [-Uname]\n"); + exit(1); +} + +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +void +#if __STDC__ +oops(const char *fmt, ...) +#else +oops(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list ap; +#if __STDC__ + va_start(ap, fmt); +#else + va_start(ap); +#endif + (void)fprintf(stderr, "%s: ", progname); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/usr.bin/m4/pathnames.h b/usr.bin/m4/pathnames.h new file mode 100644 index 0000000..72f66d3 --- /dev/null +++ b/usr.bin/m4/pathnames.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * Definitions of diversion files. If the name of the file is changed, + * adjust UNIQUE to point to the wildcard (*) character in the filename. + */ + +#ifdef msdos +#define _PATH_DIVNAME "\\M4*XXXXXX" /* msdos diversion files */ +#define UNIQUE 3 /* unique char location */ +#endif + +#ifdef unix +#define _PATH_DIVNAME "/tmp/m4.0XXXXXX" /* unix diversion files */ +#define UNIQUE 8 /* unique char location */ +#endif + +#ifdef vms +#define _PATH_DIVNAME "sys$login:m4*XXXXXX" /* vms diversion files */ +#define UNIQUE 12 /* unique char location */ +#endif diff --git a/usr.bin/m4/stdd.h b/usr.bin/m4/stdd.h new file mode 100644 index 0000000..16c2840 --- /dev/null +++ b/usr.bin/m4/stdd.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ozan Yigit at York University. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)stdd.h 8.1 (Berkeley) 6/6/93 + */ + +/* + * standard defines + */ + +#define max(a,b) ((a) > (b)? (a): (b)) +#define min(a,b) ((a) < (b)? (a): (b)) + +#define iswhite(c) ((c) == ' ' || (c) == '\t') + +/* + * STREQ is an optimised strcmp(a,b)==0 + * STREQN is an optimised strncmp(a,b,n)==0; assumes n > 0 + */ +#define STREQ(a, b) ((a)[0] == (b)[0] && strcmp(a, b) == 0) +#define STREQN(a, b, n) ((a)[0] == (b)[0] && strncmp(a, b, n) == 0) + +#define YES 1 +#define NO 0 diff --git a/usr.bin/patch/Makefile b/usr.bin/patch/Makefile new file mode 100644 index 0000000..d6db9f7 --- /dev/null +++ b/usr.bin/patch/Makefile @@ -0,0 +1,6 @@ +# @(#)Makefile 8.1 (Berkeley) 6/6/93 + +PROG= patch +SRCS= patch.c pch.c inp.c version.c util.c + +.include <bsd.prog.mk> diff --git a/usr.bin/patch/README b/usr.bin/patch/README new file mode 100644 index 0000000..017b1a0 --- /dev/null +++ b/usr.bin/patch/README @@ -0,0 +1,79 @@ + +The Makefile and config.h files in this directory work with the current +BSD release. Don't run the Configure script, you'll get wrong results. + +Keith Bostic 1/10/88 +----------------------------------------------------------------------------- + + Patch Kit, Version 2.0 + + Copyright (c) 1986, 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@sdcrdcf.UUCP (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 net.sources.bugs. Patches will generally be + in a form usable by the patch program. If you are just now bringing up + patch and aren't sure how many patches there are, write to me and I'll + send any you don't have. Your current patch level is shown in patchlevel.h. + + +NEW FEATURES IN THIS RELEASE + +(Correct) support for 4.3bsd-style context diffs. +Files can be created from scratch. +You can specify a fuzz-factor for context matching. +You can force patch to ask no questions. +You can specify how much of the leading pathname to strip off filenames. +Uses a Configure script for greater portability. +You are now asked if you want to apply a reversed patch. +No limit (apart from memory) on the size of hunks. diff --git a/usr.bin/patch/common.h b/usr.bin/patch/common.h new file mode 100644 index 0000000..42d6883 --- /dev/null +++ b/usr.bin/patch/common.h @@ -0,0 +1,138 @@ +/* $Header: common.h,v 2.0 86/09/17 15:36:39 lwall Exp $ + * + * $Log: common.h,v $ + * Revision 2.0 86/09/17 15:36:39 lwall + * Baseline for netwide release. + * + */ + +#define DEBUGGING + +#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 + +#include <stdio.h> +#include <assert.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <signal.h> + +/* constants */ + +#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 ORIGEXT ".orig" +#define SCCSPREFIX "s." +#define GET "get -e %s" +#define RCSSUFFIX ",v" +#define CHECKOUT "co -l %s" + +/* 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 */ + +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 *origext INIT(Nullch); + +EXT char TMPOUTNAME[] INIT("/tmp/patchoXXXXXX"); +EXT char TMPINNAME[] INIT("/tmp/patchiXXXXXX"); /* might want /usr/tmp here */ +EXT char TMPREJNAME[] INIT("/tmp/patchrXXXXXX"); +EXT char TMPPATNAME[] INIT("/tmp/patchpXXXXXX"); +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 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 +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 */ + +char *malloc(); +char *realloc(); +char *strcpy(); +char *strcat(); +long atol(); +char *mktemp(); diff --git a/usr.bin/patch/inp.c b/usr.bin/patch/inp.c new file mode 100644 index 0000000..a3eeb90 --- /dev/null +++ b/usr.bin/patch/inp.c @@ -0,0 +1,313 @@ +/* $Header: inp.c,v 2.0 86/09/17 15:37:02 lwall Exp $ + * + * $Log: inp.c,v $ + * 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; + Reg1 char *s; + Reg2 LINENUM iline; + + if (ok_to_create_file && stat(filename, &filestat) < 0) { + if (verbose) + say2("(Creating file %s...)\n",filename); + makedirs(filename, TRUE); + close(creat(filename, 0666)); + } + if (stat(filename, &filestat) < 0) { + Sprintf(buf, "RCS/%s%s", filename, RCSSUFFIX); + if (stat(buf, &filestat) >= 0 || stat(buf+4, &filestat) >= 0) { + Sprintf(buf, CHECKOUT, filename); + if (verbose) + say2("Can't find %s--attempting to check it out from RCS.\n", + filename); + if (system(buf) || stat(filename, &filestat)) + fatal2("Can't check out %s.\n", filename); + } + else { + Sprintf(buf, "SCCS/%s%s", SCCSPREFIX, filename); + if (stat(buf, &filestat) >= 0 || stat(buf+5, &filestat) >= 0) { + Sprintf(buf, GET, filename); + if (verbose) + say2("Can't find %s--attempting to get it from SCCS.\n", + filename); + if (system(buf) || stat(filename, &filestat)) + fatal2("Can't get %s.\n", filename); + } + else + fatal2("Can't find %s.\n", filename); + } + } + filemode = filestat.st_mode; + if ((filemode & S_IFMT) & ~S_IFREG) + 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; + if ((ifd = open(filename, 0)) < 0) + fatal2("Can't open file %s\n", 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 { + 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; + if ((ifp = fopen(filename, "r")) == Nullfp) + fatal2("Can't open file %s\n", filename); + if ((tifd = creat(TMPINNAME, 0666)) < 0) + fatal2("Can't open file %s\n", 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 { + 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("Can't seem to get enough memory.\n"); + for (i=1; ; i++) { + if (! (i % lines_per_buf)) /* new block */ + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + fatal1("patch: can't write temp file.\n"); + 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) + fatal1("patch: can't write temp file.\n"); + break; + } + } + Fclose(ifp); + Close(tifd); + if ((tifd = open(TMPINNAME, 0)) < 0) { + fatal2("Can't reopen file %s\n", 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, (off_t)baseline / lines_per_buf * BUFFERSIZE, 0); +#endif + if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) + fatal2("Error reading tmp file %s.\n", 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); + for (s = string; *s; s++) { + if (isspace(*s) && strnEQ(s+1, revision, patlen) && + isspace(s[patlen+1] )) { + return TRUE; + } + } + return FALSE; +} + diff --git a/usr.bin/patch/inp.h b/usr.bin/patch/inp.h new file mode 100644 index 0000000..c6d2a91 --- /dev/null +++ b/usr.bin/patch/inp.h @@ -0,0 +1,18 @@ +/* $Header: inp.h,v 2.0 86/09/17 15:37:25 lwall Exp $ + * + * $Log: inp.h,v $ + * 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/usr.bin/patch/patch.1 b/usr.bin/patch/patch.1 new file mode 100644 index 0000000..3e4a12e --- /dev/null +++ b/usr.bin/patch/patch.1 @@ -0,0 +1,446 @@ +''' $Header: patch.man,v 2.0 86/09/17 15:39:09 lwall Exp $ +''' +''' $Log: patch.man,v $ +''' 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. +''' +.ie n \{\ +.tr \(bs-\*(Tr +.ds -- \(bs- +.if (\n(.H=4u)&(1m=24u) .ds -- \(bs\h'-12u'\(bs\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(bs\h'-12u'\(bs\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 "June 30, 1993" +.SH NAME +patch \- a program for applying a diff file to an original +.SH SYNOPSIS +.B patch +[options] orig patchfile [+ [options] orig] +.sp +but usually just +.sp +.B patch +<patchfile +.SH DESCRIPTION +.I Patch +will take a patch file containing any of the three 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", or as specified by the +.B -b +switch. +You may also specify where you want the output to go with a +.B -o +switch. +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 , +or +.B -n +switch. +Context diffs 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". +(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, 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". +.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 \-f +forces +.I patch +to assume that the user knows exactly what he or she is doing, and to not +ask any questions. +It does not suppress commentary, however. +Use +.B \-s +for that. +.TP 5 +.B \-F<number> +sets the maximum fuzz factor. +This switch only applied 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 backslashes 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". +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 \-v +causes +.I patch +to print out it's revision header and patch level. +.TP 5 +.B \-x<number> +sets internal debugging flags, and is of interest only to +.I patch +patchers. +.SH ENVIRONMENT +No environment variables are used by +.IR patch . +.SH FILES +/tmp/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. +.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. diff --git a/usr.bin/patch/patch.c b/usr.bin/patch/patch.c new file mode 100644 index 0000000..0f91c5c --- /dev/null +++ b/usr.bin/patch/patch.c @@ -0,0 +1,800 @@ +#ifndef lint +static char sccsid[] = "@(#)patch.c 8.1 (Berkeley) 6/6/93"; +#endif not lint + +char rcsid[] = + "$Header: patch.c,v 2.0.1.4 87/02/16 14:00:04 lwall Exp $"; + +/* 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. + * + * $Log: patch.c,v $ + * 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" + +/* 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(); + +/* Apply a set of diffs as appropriate. */ + +main(argc,argv) +int argc; +char **argv; +{ + LINENUM where; + LINENUM newwhere; + LINENUM fuzz; + LINENUM mymaxfuzz; + int hunk = 0; + int failed = 0; + int i; + + setbuf(stderr, serrbuf); + for (i = 0; i<MAXFILEC; i++) + filearg[i] = Nullch; + Mktemp(TMPOUTNAME); + Mktemp(TMPINNAME); + Mktemp(TMPREJNAME); + Mktemp(TMPPATNAME); + + /* parse switches */ + Argc = argc; + Argv = argv; + get_some_switches(); + + /* make sure we clean up /tmp in case of disaster */ + set_signals(); + + 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]); + + /* initialize the patched file */ + if (!skip_rest_of_patch) + init_output(TMPOUTNAME); + + /* for ed script just up and do it and exit */ + if (diff_type == ED_DIFF) { + do_ed_script(); + continue; + } + + /* 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; + 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) { + /* 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 { + 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') + 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"); + 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) { + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = TRUE; + chmod(TMPOUTNAME, filemode); + } + else + chmod(outname, filemode); + } + Fclose(rejfp); + rejfp = Nullfp; + if (failed) { + if (!*rejname) { + Strcpy(rejname, outname); + Strcat(rejname, ".rej"); + } + 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(); + } + my_exit(0); +} + +/* 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 = FALSE; + skip_rest_of_patch = FALSE; + + get_some_switches(); + + if (filec >= 2) + fatal1("You may not change to a different patch file.\n"); +} + +/* 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': + origext = savestr(Argv[1]); + Argc--,Argv++; + break; + case 'c': + diff_type = CONTEXT_DIFF; + break; + case 'd': + if (!*++s) { + Argc--,Argv++; + s = Argv[0]; + } + if (chdir(s) < 0) + fatal2("Can't cd to %s.\n", s); + break; + case 'D': + do_defines = TRUE; + if (!*++s) { + Argc--,Argv++; + s = Argv[0]; + } + Sprintf(if_defined, "#ifdef %s\n", s); + Sprintf(not_defined, "#ifndef %s\n", s); + Sprintf(end_defined, "#endif /* %s */\n", s); + break; + case 'e': + diff_type = ED_DIFF; + 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(Argv[1]); + Argc--,Argv++; + break; + case 'p': + if (*++s == '=') + s++; + strippath = atoi(s); + break; + case 'r': + Strcpy(rejname, Argv[1]); + Argc--,Argv++; + break; + case 'R': + reverse = TRUE; + break; + case 's': + verbose = FALSE; + break; + case 'S': + skip_rest_of_patch = TRUE; + break; + case 'v': + version(); + break; +#ifdef DEBUGGING + case 'x': + debug = atoi(s+1); + break; +#endif + default: + fatal2("Unrecognized switch: %s\n", Argv[0]); + } + } + } +} + +/* 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: + say1("Fatal internal error in abort_hunk().\n"); + abort(); + } + } +} + +/* 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); + } + 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++; + } + if (R_do_defines) { + fputs(end_defined, ofp); + def_state = OUTSIDE; + } + } + else { + assert(pch_char(new) == ' '); + old++; + new++; + } + } + } + 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; +{ + ofp = fopen(name, "w"); + if (ofp == Nullfp) + fatal2("patch: can't create %s.\n", name); +} + +/* Open a file to put hunks we can't locate. */ + +void +init_reject(name) +char *name; +{ + rejfp = fopen(name, "w"); + if (rejfp == Nullfp) + fatal2("patch: can't create %s.\n", 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) + say1("patch: misordered hunks! output will 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/usr.bin/patch/pch.c b/usr.bin/patch/pch.c new file mode 100644 index 0000000..8837212 --- /dev/null +++ b/usr.bin/patch/pch.c @@ -0,0 +1,1108 @@ +/* $Header: pch.c,v 2.0.1.6 87/06/04 16:18:13 lwall Exp $ + * + * $Log: pch.c,v $ + * 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) + fatal2("patch: can't create %s.\n", TMPPATNAME); + while (fgets(buf, sizeof buf, stdin) != Nullch) + fputs(buf, pfp); + Fclose(pfp); + filename = TMPPATNAME; + } + pfp = fopen(filename, "r"); + if (pfp == Nullfp) + fatal2("patch file %s not found\n", 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("patch: out of memory (grow_hunkmax)\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 == 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) { + say1("No file to patch. Skipping...\n"); + filearg[0] = savestr(bestguess); + 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) { + ask1("No file found--skip this patch? [n] "); + if (*buf != 'y') { + continue; + } + if (verbose) + say1("Skipping patch...\n"); + 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++) { + 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, "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; + } + 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; +} + +/* 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; + p_line[p_end] = Nullch; + 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) + goto malformed; + p_first = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + goto 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) + goto malformed; + p_newfirst = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + goto 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) + repl_could_be_missing = FALSE; + break; + } + goto change_line; + case '+': case '!': + repl_could_be_missing = FALSE; + change_line: + 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; + } + goto 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 */ + fillsrc = 1; + filldst = repl_beginning+1; + fillcnt = p_repl_lines; + p_end = p_max; + } + + if (diff_type == CONTEXT_DIFF && + (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) { + if (verbose) + say1("\ +(Fascinating--this is really a new-style context diff but without the telltale\n\ +extra asterisks on the *** line that usually indicate the new style...)\n"); + 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 { /* 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; + +malformed: + fatal3("Malformed patch at line %ld: %s", p_input_line, buf); + /* about as informative as "Syntax error" in C */ + return FALSE; /* for lint */ +} + +/* 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++) { + 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 */ + n = p_end - i + 1; + if (p_efake > i) + n = -n; + 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. */ + +void +do_ed_script() +{ + Reg1 char *t; + Reg2 long beginning_of_this_line; + Reg3 bool this_line_is_command = FALSE; + Reg4 FILE *pipefp; + FILE *popen(); + + 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(); +} diff --git a/usr.bin/patch/pch.h b/usr.bin/patch/pch.h new file mode 100644 index 0000000..97a5b28 --- /dev/null +++ b/usr.bin/patch/pch.h @@ -0,0 +1,36 @@ +/* $Header: pch.h,v 2.0.1.1 87/01/30 22:47:16 lwall Exp $ + * + * $Log: pch.h,v $ + * 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(); +void do_ed_script(); diff --git a/usr.bin/patch/util.c b/usr.bin/patch/util.c new file mode 100644 index 0000000..5582d18 --- /dev/null +++ b/usr.bin/patch/util.c @@ -0,0 +1,339 @@ +#include "EXTERN.h" +#include "common.h" +#include "INTERN.h" +#include "util.h" + +/* 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) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(1, buf, i) != 1) + fatal1("patch: write failed\n"); + Close(fromfd); + return 0; + } + + Strcpy(bakname, to); + Strcat(bakname, origext?origext:ORIGEXT); + 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 */ + while (stat(bakname, &filestat) >= 0 && + to_device == filestat.st_dev && to_inode == filestat.st_ino) { + 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 + if (link(to, bakname) < 0) { + say3("patch: can't backup %s, output is in %s\n", + to, from); + return -1; + } + while (unlink(to) >= 0) ; + } +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", from, to); +#endif + if (link(from, to) < 0) { /* different file system? */ + Reg4 int tofd; + + tofd = creat(to, 0666); + if (tofd < 0) { + say3("patch: can't create %s, output is in %s.\n", + to, from); + return -1; + } + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + fatal1("patch: write failed\n"); + Close(fromfd); + Close(tofd); + } + Unlink(from); + 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) + fatal2("patch: can't create %s.\n", to); + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + fatal2("patch: write (%s) failed\n", 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("patch: out of memory (savestr)\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*/ +ask(pat) char *pat; { ; } + +#else + +/* Vanilla terminal output (buffered). */ + +void +say(pat,arg1,arg2,arg3) +char *pat; +int 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; +int arg1,arg2,arg3; +{ + void my_exit(); + + say(pat, arg1, arg2, arg3); + my_exit(1); +} + +/* Get a response from the user, somehow or other. */ + +void +ask(pat,arg1,arg2,arg3) +char *pat; +int arg1,arg2,arg3; +{ + 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); +} +#endif lint + +/* How to handle certain events when not in a critical region. */ + +void +set_signals() +{ + void my_exit(); + +#ifndef lint + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + Signal(SIGHUP, my_exit); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + Signal(SIGINT, my_exit); +#endif +} + +/* How to handle certain events when in a critical region. */ + +void +ignore_signals() +{ +#ifndef lint + Signal(SIGHUP, SIG_IGN); + Signal(SIGINT, SIG_IGN); +#endif +} + +/* Make sure we'll have the directories to create a file. */ + +void +makedirs(filename,striplast) +Reg1 char *filename; +bool striplast; +{ + char tmpbuf[256]; + Reg2 char *s = tmpbuf; + char *dirv[20]; + Reg3 int i; + Reg4 int dirvp = 0; + + 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++) { + while (*s) s++; + *s++ = ' '; + strcpy(s, tmpbuf); + *dirv[i] = '/'; + } + system(buf); +} + +/* Make filenames more reasonable. */ + +char * +fetchname(at,strip_leading,assume_exists) +char *at; +int strip_leading; +int assume_exists; +{ + char *s; + char *name; + Reg1 char *t; + char tmpbuf[200]; + + if (!at) + return Nullch; + s = savestr(at); + for (t=s; isspace(*t); t++) ; + name = t; +#ifdef DEBUGGING + if (debug & 128) + say4("fetchname %s %d %d\n",name,strip_leading,assume_exists); +#endif + if (strnEQ(name, "/dev/null", 9)) /* so files can be created by diffing */ + return Nullch; /* against /dev/null. */ + for (; *t && !isspace(*t); t++) + if (*t == '/') + if (--strip_leading >= 0) + name = t+1; + *t = '\0'; + if (name != s && *s != '/') { + name[-1] = '\0'; + if (stat(s, &filestat) && filestat.st_mode & S_IFDIR) { + name[-1] = '/'; + name=s; + } + } + name = savestr(name); + Sprintf(tmpbuf, "RCS/%s", name); + free(s); + if (stat(name, &filestat) < 0 && !assume_exists) { + Strcat(tmpbuf, RCSSUFFIX); + if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+4, &filestat) < 0) { + Sprintf(tmpbuf, "SCCS/%s%s", SCCSPREFIX, name); + if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+5, &filestat) < 0) { + free(name); + name = Nullch; + } + } + } + return name; +} diff --git a/usr.bin/patch/util.h b/usr.bin/patch/util.h new file mode 100644 index 0000000..9896c63 --- /dev/null +++ b/usr.bin/patch/util.h @@ -0,0 +1,74 @@ +/* $Header: util.h,v 2.0 86/09/17 15:40:06 lwall Exp $ + * + * $Log: util.h,v $ + * 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 + +#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)) + +#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 + +#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 ask(); +char *savestr(); +void set_signals(); +void ignore_signals(); +void makedirs(); |