From f789fee254bfd8e2872f294ed742b61d548759f1 Mon Sep 17 00:00:00 2001 From: Ben Gras Date: Wed, 20 Jul 2011 14:51:58 +0200 Subject: [PATCH] import netbsd sed . speeds up mkdep (i.e. world builds) significantly . have to keep minix /bin/sed for a while because previous usr/etc/rc depends on it . force mkdep to use /usr/bin/sed for speedup --- commands/mkdep/mkdep.sh | 2 +- etc/usr/rc | 4 +- tools/nbsd_ports | 1 + tools/release.sh | 4 +- usr.bin/Makefile | 2 +- usr.bin/sed/Makefile | 9 + usr.bin/sed/POSIX | 199 +++++++++ usr.bin/sed/TEST/hanoi.sed | 103 +++++ usr.bin/sed/TEST/math.sed | 164 +++++++ usr.bin/sed/TEST/sed.test | 554 +++++++++++++++++++++++ usr.bin/sed/compile.c | 877 +++++++++++++++++++++++++++++++++++++ usr.bin/sed/defs.h | 187 ++++++++ usr.bin/sed/extern.h | 97 ++++ usr.bin/sed/main.c | 408 +++++++++++++++++ usr.bin/sed/misc.c | 164 +++++++ usr.bin/sed/process.c | 691 +++++++++++++++++++++++++++++ usr.bin/sed/sed.1 | 518 ++++++++++++++++++++++ 17 files changed, 3979 insertions(+), 5 deletions(-) create mode 100644 usr.bin/sed/Makefile create mode 100644 usr.bin/sed/POSIX create mode 100644 usr.bin/sed/TEST/hanoi.sed create mode 100644 usr.bin/sed/TEST/math.sed create mode 100644 usr.bin/sed/TEST/sed.test create mode 100644 usr.bin/sed/compile.c create mode 100644 usr.bin/sed/defs.h create mode 100644 usr.bin/sed/extern.h create mode 100644 usr.bin/sed/main.c create mode 100644 usr.bin/sed/misc.c create mode 100644 usr.bin/sed/process.c create mode 100644 usr.bin/sed/sed.1 diff --git a/commands/mkdep/mkdep.sh b/commands/mkdep/mkdep.sh index db8328065..8bc3fee0d 100644 --- a/commands/mkdep/mkdep.sh +++ b/commands/mkdep/mkdep.sh @@ -43,7 +43,7 @@ case $# in echo $cpp "$f" | \ - sed -e '/^#/!d + /usr/bin/sed -e '/^#/!d s/.*"\(.*\)".*/\1/ s:^\./::' \ -e '/^$/d' \ diff --git a/etc/usr/rc b/etc/usr/rc index 3d0d8d8c0..6009acfe8 100644 --- a/etc/usr/rc +++ b/etc/usr/rc @@ -77,7 +77,9 @@ get_eth_labels() { # Filter out the non-vlan ethernet entries from inet.conf. # Produce as output a list of "drivername_instancenr"-formatted labels. # The first sed is taken from /bin as older GNU sed versions don't know '\t'. - /bin/sed 's/\t/ /g' /etc/inet.conf | \ + # /bin/sed is the minix sed, /usr/bin/sed is the netbsd sed. Earlier versions + # of this script used \t that only the minix sed understands. + /bin/sed 's/\008/ /g' /etc/inet.conf | \ sed -n 's/^ *eth[0-9][0-9]* *\([^ ][^ ]*\) *\([0-9][0-9]*\).*$/\1_\2/p' | \ grep -v '^vlan_' } diff --git a/tools/nbsd_ports b/tools/nbsd_ports index 52418722f..ebe807eb6 100644 --- a/tools/nbsd_ports +++ b/tools/nbsd_ports @@ -6,5 +6,6 @@ lib/libterminfo src/lib/libterminfo nbsd_include src/include usr.bin/m4 src/usr.bin/m4 usr.bin/indent src/usr.bin/indent +usr.bin/sed src/usr.bin/sed usr.bin/stat src/usr.bin/stat usr.bin/tic src/usr.bin/tic diff --git a/tools/release.sh b/tools/release.sh index 23579f7ad..91d52f733 100755 --- a/tools/release.sh +++ b/tools/release.sh @@ -192,10 +192,10 @@ mkdir -p $RELEASEPACKAGE echo " * Transfering bootstrap dirs to $RELEASEDIR" cp -p /bin/* /usr/bin/* /sbin/* $RELEASEDIR/$XBIN cp -rp /usr/lib $RELEASEDIR/usr -cp -rp /bin/sh /bin/echo /bin/install /bin/rm /bin/sed \ +cp -rp /bin/sh /bin/echo /bin/install /bin/rm \ /bin/date $RELEASEDIR/bin cp -rp /usr/bin/make /usr/bin/yacc /usr/bin/lex /usr/bin/asmconv \ - /usr/bin/grep /usr/bin/egrep /usr/bin/awk $RELEASEDIR/usr/bin + /usr/bin/grep /usr/bin/egrep /usr/bin/awk /usr/bin/sed $RELEASEDIR/usr/bin CONFIGHEADER=$RELEASEDIR/usr/src/common/include/minix/sys_config.h diff --git a/usr.bin/Makefile b/usr.bin/Makefile index c036f9b31..52d1d06cf 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -3,7 +3,7 @@ .include # NetBSD imports -SUBDIR= indent m4 mkimage stat tic +SUBDIR= indent m4 mkimage stat tic sed # Non-NetBSD imports SUBDIR+= ministat diff --git a/usr.bin/sed/Makefile b/usr.bin/sed/Makefile new file mode 100644 index 000000000..5c0b8c4e0 --- /dev/null +++ b/usr.bin/sed/Makefile @@ -0,0 +1,9 @@ +# $NetBSD: Makefile,v 1.14 2009/04/14 22:15:26 lukem Exp $ +# from: @(#)Makefile 8.1 (Berkeley) 6/6/93 + +.include + +PROG= sed +SRCS= compile.c main.c misc.c process.c + +.include diff --git a/usr.bin/sed/POSIX b/usr.bin/sed/POSIX new file mode 100644 index 000000000..607207c44 --- /dev/null +++ b/usr.bin/sed/POSIX @@ -0,0 +1,199 @@ +# $NetBSD: POSIX,v 1.4 1997/01/09 20:21:25 tls Exp $ +# from: @(#)POSIX 8.1 (Berkeley) 6/6/93 + +Comments on the IEEE P1003.2 Draft 12 + Part 2: Shell and Utilities + Section 4.55: sed - Stream editor + +Diomidis Spinellis +Keith Bostic + +In the following paragraphs, "wrong" usually means "inconsistent with +historic practice", as most of the following comments refer to +undocumented inconsistencies between the historical versions of sed and +the POSIX 1003.2 standard. All the comments are notes taken while +implementing a POSIX-compatible version of sed, and should not be +interpreted as official opinions or criticism towards the POSIX committee. +All uses of "POSIX" refer to section 4.55, Draft 12 of POSIX 1003.2. + + 1. 32V and BSD derived implementations of sed strip the text + arguments of the a, c and i commands of their initial blanks, + i.e. + + #!/bin/sed -f + a\ + foo\ + \ indent\ + bar + + produces: + + foo + indent + bar + + POSIX does not specify this behavior as the System V versions of + sed do not do this stripping. The argument against stripping is + that it is difficult to write sed scripts that have leading blanks + if they are stripped. The argument for stripping is that it is + difficult to write readable sed scripts unless indentation is allowed + and ignored, and leading whitespace is obtainable by entering a + backslash in front of it. This implementation follows the BSD + historic practice. + + 2. Historical versions of sed required that the w flag be the last + flag to an s command as it takes an additional argument. This + is obvious, but not specified in POSIX. + + 3. Historical versions of sed required that whitespace follow a w + flag to an s command. This is not specified in POSIX. This + implementation permits whitespace but does not require it. + + 4. Historical versions of sed permitted any number of whitespace + characters to follow the w command. This is not specified in + POSIX. This implementation permits whitespace but does not + require it. + + 5. The rule for the l command differs from historic practice. Table + 2-15 includes the various ANSI C escape sequences, including \\ + for backslash. Some historical versions of sed displayed two + digit octal numbers, too, not three as specified by POSIX. POSIX + is a cleanup, and is followed by this implementation. + + 6. The POSIX specification for ! does not specify that for a single + command the command must not contain an address specification + whereas the command list can contain address specifications. The + specification for ! implies that "3!/hello/p" works, and it never + has, historically. Note, + + 3!{ + /hello/p + } + + does work. + + 7. POSIX does not specify what happens with consecutive ! commands + (e.g. /foo/!!!p). Historic implementations allow any number of + !'s without changing the behaviour. (It seems logical that each + one might reverse the behaviour.) This implementation follows + historic practice. + + 8. Historic versions of sed permitted commands to be separated + by semi-colons, e.g. 'sed -ne '1p;2p;3q' printed the first + three lines of a file. This is not specified by POSIX. + Note, the ; command separator is not allowed for the commands + a, c, i, w, r, :, b, t, # and at the end of a w flag in the s + command. This implementation follows historic practice and + implements the ; separator. + + 9. Historic versions of sed terminated the script if EOF was reached + during the execution of the 'n' command, i.e.: + + sed -e ' + n + i\ + hello + ' +# +# note -- TWO carriage returns, a peculiarity of sed), this will output the +# sequence of states involved in moving 4 rings, the largest called "a" and +# the smallest called "d", from the first to the second of three towers, so +# that the rings on any tower at any time are in descending order of size. +# You can start with a different arrangement and a different number of rings, +# say :ce:b:ax: and it will give the shortest procedure for moving them all +# to the middle tower. The rules are: the names of the rings must all be +# lower-case letters, they must be input within 3 fields (representing the +# towers) and delimited by 4 colons, such that the letters within each field +# are in alphabetical order (i.e. rings are in descending order of size). +# +# For the benefit of anyone who wants to figure out the script, an "internal" +# line of the form +# b:0abx:1a2b3 :2 :3x2 +# has the following meaning: the material after the three markers :1, :2, +# and :3 represents the three towers; in this case the current set-up is +# ":ab : :x :". The numbers after a, b and x in these fields indicate +# that the next time it gets a chance, it will move a to tower 2, move b +# to tower 3, and move x to tower 2. The string after :0 just keeps track +# of the alphabetical order of the names of the rings. The b at the +# beginning means that it is now dealing with ring b (either about to move +# it, or re-evaluating where it should next be moved to). +# +# Although this version is "limited" to 26 rings because of the size of the +# alphabet, one could write a script using the same idea in which the rings +# were represented by arbitrary [strings][within][brackets], and in place of +# the built-in line of the script giving the order of the letters of the +# alphabet, it would accept from the user a line giving the ordering to be +# assumed, e.g. [ucbvax][decvax][hplabs][foo][bar]. +# +# George Bergman +# Math, UC Berkeley 94720 USA + +# cleaning, diagnostics +s/ *//g +/^$/d +/[^a-z:]/{a\ +Illegal characters: use only a-z and ":". Try again. +d +} +/^:[a-z]*:[a-z]*:[a-z]*:$/!{a\ +Incorrect format: use\ +\ : string1 : string2 : string3 :\ +Try again. +d +} +/\([a-z]\).*\1/{a\ +Repeated letters not allowed. Try again. +d +} +# initial formatting +h +s/[a-z]/ /g +G +s/^:\( *\):\( *\):\( *\):\n:\([a-z]*\):\([a-z]*\):\([a-z]*\):$/:1\4\2\3:2\5\1\3:3\6\1\2:0/ +s/[a-z]/&2/g +s/^/abcdefghijklmnopqrstuvwxyz/ +:a +s/^\(.\).*\1.*/&\1/ +s/.// +/^[^:]/ba +s/\([^0]*\)\(:0.*\)/\2\1:/ +s/^[^0]*0\(.\)/\1&/ +:b +# outputting current state without markers +h +s/.*:1/:/ +s/[123]//gp +g +:c +# establishing destinations +/^\(.\).*\1:1/td +/^\(.\).*:1[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:1[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +/^\(.\).*:1[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:2[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +/^\(.\).*:2[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:2[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:3[^:]*\11/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\32/ +/^\(.\).*:3[^:]*\12/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\31/ +/^\(.\).*:3[^:]*\13/s/^\(.\)\(.*\1\([a-z]\).*\)\3./\3\2\33/ +bc +# iterate back to find smallest out-of-place ring +:d +s/^\(.\)\(:0[^:]*\([^:]\)\1.*:\([123]\)[^:]*\1\)\4/\3\2\4/ +td +# move said ring (right, resp. left) +s/^\(.\)\(.*\)\1\([23]\)\(.*:\3[^ ]*\) /\1\2 \4\1\3/ +s/^\(.\)\(.*:\([12]\)[^ ]*\) \(.*\)\1\3/\1\2\1\3\4 / +tb +s/.*/Done! Try another, or end with ^D./p +d diff --git a/usr.bin/sed/TEST/math.sed b/usr.bin/sed/TEST/math.sed new file mode 100644 index 000000000..82968b547 --- /dev/null +++ b/usr.bin/sed/TEST/math.sed @@ -0,0 +1,164 @@ +# +# from: @(#)math.sed 8.1 (Berkeley) 6/6/93 +# $NetBSD: math.sed,v 1.3 1997/01/09 20:21:36 tls Exp $ +# +# Addition and multiplication in sed. +# ++ for a limited time only do (expr) too!!! +# +# Kevin S Braunsdorf, PUCC UNIX Group, ksb@cc.purdue.edu. +# +# Ex: +# echo "4+7*3" | sed -f %f + +# make sure the expression is well formed +s/[ ]//g +/[+*\/-]$/{ + a\ + poorly formed expression, operator on the end + q +} +/^[+*\/]/{ + a\ + poorly formed expression, leading operator + q +} + +# fill hold space with done token +x +s/^.*/done/ +x + +# main loop, process operators (*, + and () ) +: loop +/^\+/{ + s/// + b loop +} +/^\(.*\)(\([^)]*\))\(.*\)$/{ + H + s//\2/ + x + s/^\(.*\)\n\(.*\)(\([^()]*\))\(.*\)$/()\2@\4@\1/ + x + b loop +} +/^[0-9]*\*/b mul +/^\([0-9]*\)\+\([0-9+*]*\*[0-9]*\)$/{ + s//\2+\1/ + b loop +} +/^[0-9]*\+/{ + s/$/=/ + b add +} +x +/^done$/{ + x + p + d +} +/^()/{ + s/// + x + G + s/\(.*\)\n\([^@]*\)@\([^@]*\)@\(.*\)/\2\1\3/ + x + s/[^@]*@[^@]*@\(.*\)/\1/ + x + b loop +} +i\ +help, stack problem +p +x +p +q + +# turn mul into add until 1*x -> x +: mul +/^0*1\*/{ + s/// + b loop +} +/^\([0-9]*\)0\*/{ + s/^\([0-9]*\)0\*\([0-9]*\)/\1*\20/ + b mul +} +s/^\([0-9]*\)1\*/\10*/ +s/^\([0-9]*\)2\*/\11*/ +s/^\([0-9]*\)3\*/\12*/ +s/^\([0-9]*\)4\*/\13*/ +s/^\([0-9]*\)5\*/\14*/ +s/^\([0-9]*\)6\*/\15*/ +s/^\([0-9]*\)7\*/\16*/ +s/^\([0-9]*\)8\*/\17*/ +s/^\([0-9]*\)9\*/\18*/ +s/\*\([0-9*]*\)/*\1+\1/ +b mul + +# get rid of a plus term until 0+x -> x +: add +/^\+\([0-9+*]*\)=/{ + s//\1/ + b loop +} +/^\([0-9*]*\)\+=/{ + s//\1/ + b loop +} +/^\([0-9]*\)\+\([0-9*+]*\)\+=/{ + s//\2+\1/ + b loop +} +/^\([0-9]*\)0\+\([0-9]*\)\([0-9]\)=/{ + s//\1+\2=\3/ + b add +} +/^\([0-9]*\)\([0-9]\)\+\([0-9]*\)0=/{ + s//\1+\3=\2/ + b add +} +/^\([0-9]*\)0\+\([0-9*+]*\)\+\([0-9]*\)\([0-9]\)=/{ + s//\1+\2+\3=\4/ + b add +} +/^\([0-9]*\)\([0-9]\)\+\([0-9*+]*\)\+\([0-9]*\)0=/{ + s//\1+\3+\4=\2/ + b add +} +s/^\([0-9]*\)1\+/\10+/ +s/^\([0-9]*\)2\+/\11+/ +s/^\([0-9]*\)3\+/\12+/ +s/^\([0-9]*\)4\+/\13+/ +s/^\([0-9]*\)5\+/\14+/ +s/^\([0-9]*\)6\+/\15+/ +s/^\([0-9]*\)7\+/\16+/ +s/^\([0-9]*\)8\+/\17+/ +s/^\([0-9]*\)9\+/\18+/ + +s/9=\([0-9]*\)$/_=\1/ +s/8=\([0-9]*\)$/9=\1/ +s/7=\([0-9]*\)$/8=\1/ +s/6=\([0-9]*\)$/7=\1/ +s/5=\([0-9]*\)$/6=\1/ +s/4=\([0-9]*\)$/5=\1/ +s/3=\([0-9]*\)$/4=\1/ +s/2=\([0-9]*\)$/3=\1/ +s/1=\([0-9]*\)$/2=\1/ +/_/{ + s//_0/ + : inc + s/9_/_0/ + s/8_/9/ + s/7_/8/ + s/6_/7/ + s/5_/6/ + s/4_/5/ + s/3_/4/ + s/2_/3/ + s/1_/2/ + s/0_/1/ + s/\+_/+1/ + /_/b inc +} +b add diff --git a/usr.bin/sed/TEST/sed.test b/usr.bin/sed/TEST/sed.test new file mode 100644 index 000000000..36562f6da --- /dev/null +++ b/usr.bin/sed/TEST/sed.test @@ -0,0 +1,554 @@ +#!/bin/sh - +# $NetBSD: sed.test,v 1.4 2003/10/27 20:09:49 uwe Exp $ +# +# Copyright (c) 1992 Diomidis Spinellis. +# Copyright (c) 1992, 1993 +# The Regents of the University of California. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# from: @(#)sed.test 8.1 (Berkeley) 6/6/93 +# $NetBSD: sed.test,v 1.4 2003/10/27 20:09:49 uwe Exp $ +# + +# sed Regression Tests +# +# The following files are created: +# lines[1-4], script1, script2 +# Two directories *.out contain the test results + +main() +{ + BASE=/usr/old/bin/sed + BASELOG=sed.out + TEST=../obj/sed + TESTLOG=nsed.out + DICT=/usr/share/dict/words + + test_error | more + + awk 'END { for (i = 1; i < 15; i++) print "l1_" i}' lines1 + awk 'END { for (i = 1; i < 10; i++) print "l2_" i}' lines2 + + exec 4>&1 5>&2 + + # Set these flags to get messages about known problems + BSD=1 + GNU=0 + SUN=0 + tests $BASE $BASELOG + + BSD=0 + GNU=0 + SUN=0 + tests $TEST $TESTLOG + exec 1>&4 2>&5 + diff -c $BASELOG $TESTLOG | more +} + +tests() +{ + SED=$1 + DIR=$2 + rm -rf $DIR + mkdir $DIR + MARK=100 + + test_args + test_addr + echo Testing commands + test_group + test_acid + test_branch + test_pattern + test_print + test_subst +} + +mark() +{ + MARK=`expr $MARK + 1` + exec 1>&4 2>&5 + exec >"$DIR/${MARK}_$1" + echo "Test $1:$MARK" + # Uncomment this line to match tests with sed error messages + echo "Test $1:$MARK" >&5 +} + +test_args() +{ + mark '1.1' + echo Testing argument parsing + echo First type + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED 's/^/e1_/p' lines1 + fi + mark '1.2' ; $SED -n 's/^/e1_/p' lines1 + mark '1.3' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED 's/^/e1_/p' script1 + echo 's/^/s2_/p' >script2 + mark '1.5' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 lines1 + fi + mark '1.6' + if [ $SUN -eq 1 ] ; then + echo SunOS sed prints only with -n + else + $SED -f script1 script1 <lines3 + # GNU and SunOS sed behave differently here + mark '7.1' + if [ $BSD -eq 1 ] ; then + echo 'BSD sed drops core on this one; TEST SKIPPED' + else + $SED -n l lines3 + fi + mark '7.2' ; $SED -e '/l2_/=' lines1 lines2 + rm -f lines4 + mark '7.3' ; $SED -e '3,12w lines4' lines1 + echo w results + cat lines4 + mark '7.4' ; $SED -e '4r lines2' lines1 + mark '7.5' ; $SED -e '5r /dev/dds' lines1 + mark '7.6' ; $SED -e '6r /dev/null' lines1 + mark '7.7' + if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo BSD, GNU and SunOS cannot pass this one + else + sed '200q' $DICT | sed 's$.*$s/^/&/w tmpdir/&$' >script1 + rm -rf tmpdir + mkdir tmpdir + $SED -f script1 lines1 + cat tmpdir/* + rm -rf tmpdir + fi + mark '7.8' + if [ $BSD -eq 1 ] ; then + echo BSD sed cannot pass 7.7 + else + echo line1 > lines3 + echo "" >> lines3 + $SED -n -e '$p' lines3 /dev/null + fi + +} + +test_subst() +{ + echo Testing substitution commands + mark '8.1' ; $SED -e 's/./X/g' lines1 + mark '8.2' ; $SED -e 's,.,X,g' lines1 +# GNU and SunOS sed thinks we are escaping . as wildcard, not as separator +# mark '8.3' ; $SED -e 's.\..X.g' lines1 +# POSIX does not say that this should work +# mark '8.4' ; $SED -e 's/[/]/Q/' lines1 + mark '8.4' ; $SED -e 's/[\/]/Q/' lines1 + mark '8.5' ; $SED -e 's_\__X_' lines1 + mark '8.6' ; $SED -e 's/./(&)/g' lines1 + mark '8.7' ; $SED -e 's/./(\&)/g' lines1 + mark '8.8' ; $SED -e 's/\(.\)\(.\)\(.\)/x\3x\2x\1/g' lines1 + mark '8.9' ; $SED -e 's/_/u0\ +u1\ +u2/g' lines1 + mark '8.10' + if [ $BSD -eq 1 -o $GNU -eq 1 ] ; then + echo 'BSD/GNU sed do not understand digit flags on s commands' + fi + $SED -e 's/./X/4' lines1 + rm -f lines4 + mark '8.11' ; $SED -e 's/1/X/w lines4' lines1 + echo s wfile results + cat lines4 + mark '8.12' ; $SED -e 's/[123]/X/g' lines1 + mark '8.13' ; $SED -e 'y/0123456789/9876543210/' lines1 + mark '8.14' ; + if [ $BSD -eq 1 -o $GNU -eq 1 -o $SUN -eq 1 ] ; then + echo BSD/GNU/SUN sed fail this test + else + $SED -e 'y10\123456789198765432\101' lines1 + fi + mark '8.15' ; $SED -e '1N;2y/\n/X/' lines1 + mark '8.16' + if [ $BSD -eq 1 ] ; then + echo 'BSD sed does not handle branch defined REs' + else + echo 'eeefff' | $SED -e 'p' -e 's/e/X/p' -e ':x' \ + -e 's//Y/p' -e '/f/bx' + fi +} + +test_error() +{ + exec 3<&0 4>&1 5>&2 + exec 0&1 + set -x + $TEST -x && exit 1 + $TEST -f && exit 1 + $TEST -e && exit 1 + $TEST -f /dev/dds && exit 1 + $TEST p /dev/dds && exit 1 + $TEST -f /bin/sh && exit 1 + $TEST '{' && exit 1 + $TEST '{' && exit 1 + $TEST '/hello/' && exit 1 + $TEST '1,/hello/' && exit 1 + $TEST -e '-5p' && exit 1 + $TEST '/jj' && exit 1 + $TEST 'a hello' && exit 1 + $TEST 'a \ hello' && exit 1 + $TEST 'b foo' && exit 1 + $TEST 'd hello' && exit 1 + $TEST 's/aa' && exit 1 + $TEST 's/aa/' && exit 1 + $TEST 's/a/b' && exit 1 + $TEST 's/a/b/c/d' && exit 1 + $TEST 's/a/b/ 1 2' && exit 1 + $TEST 's/a/b/ 1 g' && exit 1 + $TEST 's/a/b/w' && exit 1 + $TEST 'y/aa' && exit 1 + $TEST 'y/aa/b/' && exit 1 + $TEST 'y/aa/' && exit 1 + $TEST 'y/a/b' && exit 1 + $TEST 'y/a/b/c/d' && exit 1 + $TEST '!' && exit 1 + $TEST supercalifrangolisticexprialidociussupercalifrangolisticexcius + set +x + exec 0<&3 1>&4 2>&5 +} + +main diff --git a/usr.bin/sed/compile.c b/usr.bin/sed/compile.c new file mode 100644 index 000000000..7b9a7a03e --- /dev/null +++ b/usr.bin/sed/compile.c @@ -0,0 +1,877 @@ +/* $NetBSD: compile.c,v 1.37 2010/02/19 16:35:27 tnn Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)compile.c 8.2 (Berkeley) 4/28/95"; +#else +__RCSID("$NetBSD: compile.c,v 1.37 2010/02/19 16:35:27 tnn Exp $"); +#endif +#endif /* not lint */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "extern.h" + +#ifndef _POSIX2_LINE_MAX +#define _POSIX2_LINE_MAX (2 * BUFSIZ) +#endif + +#define LHSZ 128 +#define LHMASK (LHSZ - 1) +static struct labhash { + struct labhash *lh_next; + u_int lh_hash; + struct s_command *lh_cmd; + int lh_ref; +} *labels[LHSZ]; + +static char *compile_addr(char *, struct s_addr *); +static char *compile_ccl(char **, char *); +static char *compile_delimited(char *, char *); +static char *compile_flags(char *, struct s_subst *); +static char *compile_re(char *, regex_t **); +static char *compile_subst(char *, struct s_subst *); +static char *compile_text(void); +static char *compile_tr(char *, char **); +static struct s_command + **compile_stream(struct s_command **); +static char *duptoeol(char *, const char *); +static void enterlabel(struct s_command *); +static struct s_command + *findlabel(char *); +static void fixuplabel(struct s_command *, struct s_command *); +static void uselabel(void); + +/* + * Command specification. This is used to drive the command parser. + */ +struct s_format { + char code; /* Command code */ + int naddr; /* Number of address args */ + enum e_args args; /* Argument type */ +}; + +static struct s_format cmd_fmts[] = { + {'{', 2, GROUP}, + {'}', 0, ENDGROUP}, + {'a', 1, TEXT}, + {'b', 2, BRANCH}, + {'c', 2, TEXT}, + {'d', 2, EMPTY}, + {'D', 2, EMPTY}, + {'g', 2, EMPTY}, + {'G', 2, EMPTY}, + {'h', 2, EMPTY}, + {'H', 2, EMPTY}, + {'i', 1, TEXT}, + {'l', 2, EMPTY}, + {'n', 2, EMPTY}, + {'N', 2, EMPTY}, + {'p', 2, EMPTY}, + {'P', 2, EMPTY}, + {'q', 1, EMPTY}, + {'r', 1, RFILE}, + {'s', 2, SUBST}, + {'t', 2, BRANCH}, + {'w', 2, WFILE}, + {'x', 2, EMPTY}, + {'y', 2, TR}, + {'!', 2, NONSEL}, + {':', 0, LABEL}, + {'#', 0, COMMENT}, + {'=', 1, EMPTY}, + {'\0', 0, COMMENT}, +}; + +/* The compiled program. */ +struct s_command *prog; + +/* + * Compile the program into prog. + * Initialise appends. + */ +void +compile(void) +{ + *compile_stream(&prog) = NULL; + fixuplabel(prog, NULL); + uselabel(); + if (appendnum > 0) + appends = xmalloc(sizeof(struct s_appends) * appendnum); + match = xmalloc((maxnsub + 1) * sizeof(regmatch_t)); +} + +#define EATSPACE() \ + while (*p && isascii((unsigned char)*p) && \ + isspace((unsigned char)*p)) \ + p++ \ + +static struct s_command ** +compile_stream(struct s_command **link) +{ + char *p; + static char *lbuf; /* To avoid excessive malloc calls */ + static size_t bufsize; + struct s_command *cmd, *cmd2, *stack; + struct s_format *fp; + int naddr; /* Number of addresses */ + + stack = 0; + for (;;) { + if ((p = cu_fgets(&lbuf, &bufsize)) == NULL) { + if (stack != 0) + err(COMPILE, "unexpected EOF (pending }'s)"); + return (link); + } + +semicolon: EATSPACE(); + if (*p == '#' || *p == '\0') + continue; + else if (*p == ';') { + p++; + goto semicolon; + } + *link = cmd = xmalloc(sizeof(struct s_command)); + link = &cmd->next; + cmd->nonsel = cmd->inrange = 0; + /* First parse the addresses */ + naddr = 0; + +/* Valid characters to start an address */ +#define addrchar(c) (strchr("0123456789/\\$", (c))) + if (addrchar(*p)) { + naddr++; + cmd->a1 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a1); + EATSPACE(); /* EXTENSION */ + if (*p == ',') { + p++; + EATSPACE(); /* EXTENSION */ + naddr++; + cmd->a2 = xmalloc(sizeof(struct s_addr)); + p = compile_addr(p, cmd->a2); + EATSPACE(); + } else + cmd->a2 = 0; + } else + cmd->a1 = cmd->a2 = 0; + +nonsel: /* Now parse the command */ + if (!*p) + err(COMPILE, "command expected"); + cmd->code = *p; + for (fp = cmd_fmts; fp->code; fp++) + if (fp->code == *p) + break; + if (!fp->code) + err(COMPILE, "invalid command code %c", *p); + if (naddr > fp->naddr) + err(COMPILE, +"command %c expects up to %d address(es), found %d", *p, fp->naddr, naddr); + switch (fp->args) { + case NONSEL: /* ! */ + p++; + EATSPACE(); + cmd->nonsel = ! cmd->nonsel; + goto nonsel; + case GROUP: /* { */ + p++; + EATSPACE(); + cmd->next = stack; + stack = cmd; + link = &cmd->u.c; + if (*p) + goto semicolon; + break; + case ENDGROUP: + /* + * Short-circuit command processing, since end of + * group is really just a noop. + */ + cmd->nonsel = 1; + if (stack == 0) + err(COMPILE, "unexpected }"); + cmd2 = stack; + stack = cmd2->next; + cmd2->next = cmd; + /*FALLTHROUGH*/ + case EMPTY: /* d D g G h H l n N p P q x = \0 */ + p++; + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + if (*p) + err(COMPILE, +"extra characters at the end of %c command", cmd->code); + break; + case TEXT: /* a c i */ + p++; + EATSPACE(); + if (*p != '\\') + err(COMPILE, +"command %c expects \\ followed by text", cmd->code); + p++; + EATSPACE(); + if (*p) + err(COMPILE, +"extra characters after \\ at the end of %c command", cmd->code); + cmd->t = compile_text(); + break; + case COMMENT: /* \0 # */ + break; + case WFILE: /* w */ + p++; + EATSPACE(); + if (*p == '\0') + err(COMPILE, "filename expected"); + cmd->t = duptoeol(p, "w command"); + if (aflag) + cmd->u.fd = -1; + else if ((cmd->u.fd = open(p, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s", p, strerror(errno)); + break; + case RFILE: /* r */ + p++; + EATSPACE(); + if (*p == '\0') + err(COMPILE, "filename expected"); + else + cmd->t = duptoeol(p, "read command"); + break; + case BRANCH: /* b t */ + p++; + EATSPACE(); + if (*p == '\0') + cmd->t = NULL; + else + cmd->t = duptoeol(p, "branch"); + break; + case LABEL: /* : */ + p++; + EATSPACE(); + cmd->t = duptoeol(p, "label"); + if (strlen(p) == 0) + err(COMPILE, "empty label"); + enterlabel(cmd); + break; + case SUBST: /* s */ + p++; + if (*p == '\0' || *p == '\\') + err(COMPILE, +"substitute pattern can not be delimited by newline or backslash"); + cmd->u.s = xmalloc(sizeof(struct s_subst)); + p = compile_re(p, &cmd->u.s->re); + if (p == NULL) + err(COMPILE, "unterminated substitute pattern"); + --p; + p = compile_subst(p, cmd->u.s); + p = compile_flags(p, cmd->u.s); + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + break; + case TR: /* y */ + p++; + p = compile_tr(p, (char **)(void *)&cmd->u.y); + EATSPACE(); + if (*p == ';') { + p++; + link = &cmd->next; + goto semicolon; + } + if (*p) + err(COMPILE, +"extra text at the end of a transform command"); + break; + } + } +} + +/* + * Get a delimited string. P points to the delimiter of the string; d points + * to a buffer area. Newline and delimiter escapes are processed; other + * escapes are ignored. + * + * Returns a pointer to the first character after the final delimiter or NULL + * in the case of a non-terminated string. The character array d is filled + * with the processed string. + */ +static char * +compile_delimited(char *p, char *d) +{ + char c; + + c = *p++; + if (c == '\0') + return (NULL); + else if (c == '\\') + err(COMPILE, "\\ can not be used as a string delimiter"); + else if (c == '\n') + err(COMPILE, "newline can not be used as a string delimiter"); + while (*p) { + if (*p == '[') { + if ((d = compile_ccl(&p, d)) == NULL) + err(COMPILE, "unbalanced brackets ([])"); + continue; + } else if (*p == '\\' && p[1] == '[') { + *d++ = *p++; + } else if (*p == '\\' && p[1] == c) + p++; + else if (*p == '\\' && p[1] == 'n') { + *d++ = '\n'; + p += 2; + continue; + } else if (*p == '\\' && p[1] == '\\') + *d++ = *p++; + else if (*p == c) { + *d = '\0'; + return (p + 1); + } + *d++ = *p++; + } + return (NULL); +} + + +/* compile_ccl: expand a POSIX character class */ +static char * +compile_ccl(char **sp, char *t) +{ + int c, d; + char *s = *sp; + + *t++ = *s++; + if (*s == '^') + *t++ = *s++; + if (*s == ']') + *t++ = *s++; + for (; *s && (*t = *s) != ']'; s++, t++) + if (*s == '[' && ((d = *(s+1)) == '.' || d == ':' || d == '=')) { + *++t = *++s, t++, s++; + for (c = *s; (*t = *s) != ']' || c != d; s++, t++) + if ((c = *s) == '\0') + return NULL; + } else if (*s == '\\' && s[1] == 'n') + *t = '\n', s++; + return (*s == ']') ? *sp = ++s, ++t : NULL; +} + +/* + * Get a regular expression. P points to the delimiter of the regular + * expression; repp points to the address of a regexp pointer. Newline + * and delimiter escapes are processed; other escapes are ignored. + * Returns a pointer to the first character after the final delimiter + * or NULL in the case of a non terminated regular expression. The regexp + * pointer is set to the compiled regular expression. + * Cflags are passed to regcomp. + */ +static char * +compile_re(char *p, regex_t **repp) +{ + int eval; + char *re; + + re = xmalloc(strlen(p) + 1); /* strlen(re) <= strlen(p) */ + p = compile_delimited(p, re); + if (p && strlen(re) == 0) { + *repp = NULL; + free(re); + return (p); + } + *repp = xmalloc(sizeof(regex_t)); + if (p && (eval = regcomp(*repp, re, ere)) != 0) + err(COMPILE, "RE error: %s", strregerror(eval, *repp)); + if (maxnsub < (*repp)->re_nsub) + maxnsub = (*repp)->re_nsub; + return (p); +} + +/* + * Compile the substitution string of a regular expression and set res to + * point to a saved copy of it. Nsub is the number of parenthesized regular + * expressions. + */ +static char * +compile_subst(char *p, struct s_subst *s) +{ + static char *lbuf; + static size_t bufsize; + int asize, ref, size, len; + char c, *text, *op, *sp; + int sawesc = 0; + + c = *p++; /* Terminator character */ + if (c == '\0') + return (NULL); + + s->maxbref = 0; + s->linenum = linenum; + text = NULL; + asize = size = 0; + do { + len = ROUNDLEN(strlen(p) + 1); + if (asize - size < len) { + do { + asize += len; + } while (asize - size < len); + text = xrealloc(text, asize); + } + op = sp = text + size; + for (; *p; p++) { + if (*p == '\\' || sawesc) { + /* + * If this is a continuation from the last + * buffer, we won't have a character to + * skip over. + */ + if (sawesc) + sawesc = 0; + else + p++; + + if (*p == '\0') { + /* + * This escaped character is continued + * in the next part of the line. Note + * this fact, then cause the loop to + * exit w/ normal EOL case and reenter + * above with the new buffer. + */ + sawesc = 1; + p--; + continue; + } else if (strchr("123456789", *p) != NULL) { + *sp++ = '\\'; + ref = *p - '0'; + if (s->re != NULL && + (size_t)ref > s->re->re_nsub) + err(COMPILE, +"\\%c not defined in the RE", *p); + if (s->maxbref < ref) + s->maxbref = ref; + } else if (*p == '&' || *p == '\\') + *sp++ = '\\'; + } else if (*p == c) { + p++; + *sp++ = '\0'; + size += sp - op; + s->new = xrealloc(text, size); + return (p); + } else if (*p == '\n') { + err(COMPILE, +"unescaped newline inside substitute pattern"); + /* NOTREACHED */ + } + *sp++ = *p; + } + size += sp - op; + } while ((p = cu_fgets(&lbuf, &bufsize))); + err(COMPILE, "unterminated substitute in regular expression"); + /* NOTREACHED */ + return (NULL); +} + +/* + * Compile the flags of the s command + */ +static char * +compile_flags(char *p, struct s_subst *s) +{ + int gn; /* True if we have seen g or n */ + char wfile[PATH_MAX], *q; + + s->n = 1; /* Default */ + s->p = 0; + s->wfile = NULL; + s->wfd = -1; + for (gn = 0;;) { + EATSPACE(); /* EXTENSION */ + switch (*p) { + case 'g': + if (gn) + err(COMPILE, +"more than one number or 'g' in substitute flags"); + gn = 1; + s->n = 0; + break; + case '\0': + case '\n': + case ';': + return (p); + case 'p': + s->p = 1; + break; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if (gn) + err(COMPILE, +"more than one number or 'g' in substitute flags"); + gn = 1; + /* XXX Check for overflow */ + s->n = (int)strtol(p, &p, 10); + p--; + break; + case 'w': + p++; +#ifdef HISTORIC_PRACTICE + if (*p != ' ') { + err(WARNING, "space missing before w wfile"); + return (p); + } +#endif + EATSPACE(); + q = wfile; + while (*p) { + if (*p == '\n') + break; + *q++ = *p++; + } + *q = '\0'; + if (q == wfile) + err(COMPILE, "no wfile specified"); + s->wfile = strdup(wfile); + if (!aflag && (s->wfd = open(wfile, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s", wfile, strerror(errno)); + return (p); + default: + err(COMPILE, + "bad flag in substitute command: '%c'", *p); + break; + } + p++; + } +} + +/* + * Compile a translation set of strings into a lookup table. + */ +static char * +compile_tr(char *p, char **transtab) +{ + int i; + char *lt, *op, *np; + char *old = NULL, *new = NULL; + + if (*p == '\0' || *p == '\\') + err(COMPILE, +"transform pattern can not be delimited by newline or backslash"); + old = xmalloc(strlen(p) + 1); + p = compile_delimited(p, old); + if (p == NULL) { + err(COMPILE, "unterminated transform source string"); + goto bad; + } + new = xmalloc(strlen(p) + 1); + p = compile_delimited(--p, new); + if (p == NULL) { + err(COMPILE, "unterminated transform target string"); + goto bad; + } + EATSPACE(); + if (strlen(new) != strlen(old)) { + err(COMPILE, "transform strings are not the same length"); + goto bad; + } + /* We assume characters are 8 bits */ + lt = xmalloc(UCHAR_MAX+1); + for (i = 0; i <= UCHAR_MAX; i++) + lt[i] = (char)i; + for (op = old, np = new; *op; op++, np++) + lt[(u_char)*op] = *np; + *transtab = lt; + free(old); + free(new); + return (p); +bad: + free(old); + free(new); + return (NULL); +} + +/* + * Compile the text following an a, c, or i command. + */ +static char * +compile_text(void) +{ + int asize, size, len; + char *lbuf, *text, *p, *op, *s; + size_t bufsize; + + lbuf = text = NULL; + asize = size = 0; + while ((p = cu_fgets(&lbuf, &bufsize))) { + len = ROUNDLEN(strlen(p) + 1); + if (asize - size < len) { + do { + asize += len; + } while (asize - size < len); + text = xrealloc(text, asize); + } + op = s = text + size; + for (; *p; p++) { + if (*p == '\\') + p++; + *s++ = *p; + } + size += s - op; + if (p[-2] != '\\') { + *s = '\0'; + break; + } + } + free(lbuf); + return (xrealloc(text, size + 1)); +} + +/* + * Get an address and return a pointer to the first character after + * it. Fill the structure pointed to according to the address. + */ +static char * +compile_addr(char *p, struct s_addr *a) +{ + char *end; + + switch (*p) { + case '\\': /* Context address */ + ++p; + /* FALLTHROUGH */ + case '/': /* Context address */ + p = compile_re(p, &a->u.r); + if (p == NULL) + err(COMPILE, "unterminated regular expression"); + a->type = AT_RE; + return (p); + + case '$': /* Last line */ + a->type = AT_LAST; + return (p + 1); + /* Line number */ + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + a->type = AT_LINE; + a->u.l = strtol(p, &end, 10); + return (end); + default: + err(COMPILE, "expected context address"); + return (NULL); + } +} + +/* + * duptoeol -- + * Return a copy of all the characters up to \n or \0. + */ +static char * +duptoeol(char *s, const char *ctype) +{ + size_t len; + int ws; + char *start; + + ws = 0; + for (start = s; *s != '\0' && *s != '\n'; ++s) + ws = isspace((unsigned char)*s); + *s = '\0'; + if (ws) + err(WARNING, "whitespace after %s", ctype); + len = s - start + 1; + return (memmove(xmalloc(len), start, len)); +} + +/* + * Convert goto label names to addresses, and count a and r commands, in + * the given subset of the script. Free the memory used by labels in b + * and t commands (but not by :). + * + * TODO: Remove } nodes + */ +static void +fixuplabel(struct s_command *cp, struct s_command *end) +{ + + for (; cp != end; cp = cp->next) + switch (cp->code) { + case 'a': + case 'r': + appendnum++; + break; + case 'b': + case 't': + /* Resolve branch target. */ + if (cp->t == NULL) { + cp->u.c = NULL; + break; + } + if ((cp->u.c = findlabel(cp->t)) == NULL) + err(COMPILE2, "undefined label '%s'", cp->t); + free(cp->t); + break; + case '{': + /* Do interior commands. */ + fixuplabel(cp->u.c, cp->next); + break; + } +} + +/* + * Associate the given command label for later lookup. + */ +static void +enterlabel(struct s_command *cp) +{ + struct labhash **lhp, *lh; + u_char *p; + u_int h, c; + + for (h = 0, p = (u_char *)cp->t; (c = *p) != 0; p++) + h = (h << 5) + h + c; + lhp = &labels[h & LHMASK]; + for (lh = *lhp; lh != NULL; lh = lh->lh_next) + if (lh->lh_hash == h && strcmp(cp->t, lh->lh_cmd->t) == 0) + err(COMPILE2, "duplicate label '%s'", cp->t); + lh = xmalloc(sizeof *lh); + lh->lh_next = *lhp; + lh->lh_hash = h; + lh->lh_cmd = cp; + lh->lh_ref = 0; + *lhp = lh; +} + +/* + * Find the label contained in the command l in the command linked + * list cp. L is excluded from the search. Return NULL if not found. + */ +static struct s_command * +findlabel(char *name) +{ + struct labhash *lh; + u_char *p; + u_int h, c; + + for (h = 0, p = (u_char *)name; (c = *p) != 0; p++) + h = (h << 5) + h + c; + for (lh = labels[h & LHMASK]; lh != NULL; lh = lh->lh_next) { + if (lh->lh_hash == h && strcmp(name, lh->lh_cmd->t) == 0) { + lh->lh_ref = 1; + return (lh->lh_cmd); + } + } + return (NULL); +} + +/* + * Warn about any unused labels. As a side effect, release the label hash + * table space. + */ +static void +uselabel(void) +{ + struct labhash *lh, *next; + int i; + + for (i = 0; i < LHSZ; i++) { + for (lh = labels[i]; lh != NULL; lh = next) { + next = lh->lh_next; + if (!lh->lh_ref) + err(WARNING, "unused label '%s'", + lh->lh_cmd->t); + free(lh); + } + } +} diff --git a/usr.bin/sed/defs.h b/usr.bin/sed/defs.h new file mode 100644 index 000000000..12830aacc --- /dev/null +++ b/usr.bin/sed/defs.h @@ -0,0 +1,187 @@ +/* $NetBSD: defs.h,v 1.10 2010/02/19 16:35:27 tnn Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)defs.h 8.1 (Berkeley) 6/6/93 + * $NetBSD: defs.h,v 1.10 2010/02/19 16:35:27 tnn Exp $ + */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)defs.h 8.1 (Berkeley) 6/6/93 + * $NetBSD: defs.h,v 1.10 2010/02/19 16:35:27 tnn Exp $ + */ + +/* + * Types of address specifications + */ +enum e_atype { + AT_RE, /* Line that match RE */ + AT_LINE, /* Specific line */ + AT_LAST /* Last line */ +}; + +/* + * Format of an address + */ +struct s_addr { + enum e_atype type; /* Address type */ + union { + u_long l; /* Line number */ + regex_t *r; /* Regular expression */ + } u; +}; + +/* + * Substitution command + */ +struct s_subst { + int n; /* Occurrence to subst. */ + int p; /* True if p flag */ + char *wfile; /* NULL if no wfile */ + int wfd; /* Cached file descriptor */ + regex_t *re; /* Regular expression */ + int maxbref; /* Largest backreference. */ + u_long linenum; /* Line number. */ + char *new; /* Replacement text */ +}; + + +/* + * An internally compiled command. + * Initialy, label references are stored in t, on a second pass they + * are updated to pointers. + */ +struct s_command { + struct s_command *next; /* Pointer to next command */ + struct s_addr *a1, *a2; /* Start and end address */ + char *t; /* Text for : a c i r w */ + union { + struct s_command *c; /* Command(s) for b t { */ + struct s_subst *s; /* Substitute command */ + u_char *y; /* Replace command array */ + int fd; /* File descriptor for w */ + } u; + char code; /* Command code */ + u_int nonsel:1; /* True if ! */ + u_int inrange:1; /* True if in range */ +}; + +/* + * Types of command arguments recognised by the parser + */ +enum e_args { + EMPTY, /* d D g G h H l n N p P q x = \0 */ + TEXT, /* a c i */ + NONSEL, /* ! */ + GROUP, /* { */ + ENDGROUP, /* } */ + COMMENT, /* # */ + BRANCH, /* b t */ + LABEL, /* : */ + RFILE, /* r */ + WFILE, /* w */ + SUBST, /* s */ + TR /* y */ +}; + +/* + * Structure containing things to append before a line is read + */ +struct s_appends { + enum {AP_STRING, AP_FILE} type; + char *s; + size_t len; +}; + +enum e_spflag { + APPEND, /* Append to the contents. */ + REPLACE /* Replace the contents. */ +}; + +/* + * Structure for a space (process, hold, otherwise). + */ +typedef struct { + char *space; /* Current space pointer. */ + size_t len; /* Current length. */ + int deleted; /* If deleted. */ + char *back; /* Backing memory. */ + size_t blen; /* Backing memory length. */ +} SPACE; + +/* + * Error severity codes: + */ +#define FATAL 0 /* Exit immediately with 1 */ +#define ERROR 1 /* Continue, but change exit value */ +#define WARNING 2 /* Just print the warning */ +#define COMPILE 3 /* Print error, count and finish script */ +#define COMPILE2 3 /* Print error, count and finish script */ + +/* + * Round up to the nearest multiple of _POSIX2_LINE_MAX + */ +#define ROUNDLEN(x) \ + (((x) + _POSIX2_LINE_MAX - 1) & ~(_POSIX2_LINE_MAX - 1)) diff --git a/usr.bin/sed/extern.h b/usr.bin/sed/extern.h new file mode 100644 index 000000000..310449725 --- /dev/null +++ b/usr.bin/sed/extern.h @@ -0,0 +1,97 @@ +/* $NetBSD: extern.h,v 1.11 2010/02/19 16:35:27 tnn Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $NetBSD: extern.h,v 1.11 2010/02/19 16:35:27 tnn Exp $ + */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $NetBSD: extern.h,v 1.11 2010/02/19 16:35:27 tnn Exp $ + */ + +extern struct s_command *prog; +extern struct s_appends *appends; +extern regmatch_t *match; +extern size_t maxnsub; +extern u_long linenum; +extern int appendnum; +extern int lastline; +extern int aflag, eflag, nflag; +extern int ere; +extern const char *fname; + +void cfclose(struct s_command *, struct s_command *); +void compile(void); +void cspace(SPACE *, const char *, size_t, enum e_spflag); +char *cu_fgets(char **, size_t *); +void err(int, const char *, ...) + __attribute__((__format__(__printf__, 2, 3))); +int mf_fgets(SPACE *, enum e_spflag); +void process(void); +char *strregerror(int, regex_t *); +void *xmalloc(size_t); +void *xrealloc(void *, size_t); diff --git a/usr.bin/sed/main.c b/usr.bin/sed/main.c new file mode 100644 index 000000000..68c3a00cd --- /dev/null +++ b/usr.bin/sed/main.c @@ -0,0 +1,408 @@ +/* $NetBSD: main.c,v 1.21 2010/02/19 16:35:27 tnn Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1992, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.2 (Berkeley) 1/3/94"; +#else +__RCSID("$NetBSD: main.c,v 1.21 2010/02/19 16:35:27 tnn Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "extern.h" + +/* + * Linked list of units (strings and files) to be compiled + */ +struct s_compunit { + struct s_compunit *next; + enum e_cut {CU_FILE, CU_STRING} type; + char *s; /* Pointer to string or fname */ +}; + +/* + * Linked list pointer to compilation units and pointer to current + * next pointer. + */ +static struct s_compunit *script, **cu_nextp = &script; + +/* + * Linked list of files to be processed + */ +struct s_flist { + char *fname; + struct s_flist *next; +}; + +/* + * Linked list pointer to files and pointer to current + * next pointer. + */ +static struct s_flist *files, **fl_nextp = &files; + +int aflag, eflag, nflag, ere; + +/* + * Current file and line number; line numbers restart across compilation + * units, but span across input files. + */ +const char *fname; /* File name. */ +u_long linenum; +int lastline; /* TRUE on the last line of the last file */ + +static void add_compunit(enum e_cut, char *); +static void add_file(char *); +int main(int, char **); + +int +main(int argc, char *argv[]) +{ + int c, fflag; + + setprogname(*argv); + fflag = 0; + while ((c = getopt(argc, argv, "ae:f:nrE")) != -1) + switch (c) { + case 'a': + aflag = 1; + break; + case 'e': + eflag = 1; + add_compunit(CU_STRING, optarg); + break; + case 'f': + fflag = 1; + add_compunit(CU_FILE, optarg); + break; + case 'n': + nflag = 1; + break; + case 'r': + case 'E': + ere = REG_EXTENDED; + break; + default: + case '?': + (void)fprintf(stderr, +"usage:\t%s [-aEnr] script [file ...]\n\t%s [-aEnr] [-e script] ... [-f script_file] ... [file ...]\n", + getprogname(), getprogname()); + exit(1); + } + argc -= optind; + argv += optind; + + /* First usage case; script is the first arg */ + if (!eflag && !fflag && *argv) { + add_compunit(CU_STRING, *argv); + argv++; + } + + compile(); + + /* Continue with first and start second usage */ + if (*argv) + for (; *argv; argv++) + add_file(*argv); + else + add_file(NULL); + process(); + cfclose(prog, NULL); + if (fclose(stdout)) + err(FATAL, "stdout: %s", strerror(errno)); + exit (0); +} + +/* + * Like fgets, but go through the chain of compilation units chaining them + * together. Empty strings and files are ignored. + */ +char * +cu_fgets(char **outbuf, size_t *outsize) +{ + static enum {ST_EOF, ST_FILE, ST_STRING} state = ST_EOF; + static FILE *f; /* Current open file */ + static char *s; /* Current pointer inside string */ + static char string_ident[30]; + size_t len; + char *p; + + if (*outbuf == NULL) + *outsize = 0; + +again: + switch (state) { + case ST_EOF: + if (script == NULL) + return (NULL); + linenum = 0; + switch (script->type) { + case CU_FILE: + if ((f = fopen(script->s, "r")) == NULL) + err(FATAL, + "%s: %s", script->s, strerror(errno)); + fname = script->s; + state = ST_FILE; + goto again; + case CU_STRING: + if ((snprintf(string_ident, + sizeof(string_ident), "\"%s\"", script->s)) >= + (int)(sizeof(string_ident) - 1)) + (void)strcpy(string_ident + + sizeof(string_ident) - 6, " ...\""); + fname = string_ident; + s = script->s; + state = ST_STRING; + goto again; + } + case ST_FILE: + if ((p = fgetln(f, &len)) != NULL) { + linenum++; + if (len >= *outsize) { + free(*outbuf); + *outsize = ROUNDLEN(len + 1); + *outbuf = xmalloc(*outsize); + } + memcpy(*outbuf, p, len); + (*outbuf)[len] = '\0'; + if (linenum == 1 && p[0] == '#' && p[1] == 'n') + nflag = 1; + return (*outbuf); + } + script = script->next; + (void)fclose(f); + state = ST_EOF; + goto again; + case ST_STRING: + if (linenum == 0 && s[0] == '#' && s[1] == 'n') + nflag = 1; + p = *outbuf; + len = *outsize; + for (;;) { + if (len <= 1) { + *outbuf = xrealloc(*outbuf, + *outsize + _POSIX2_LINE_MAX); + p = *outbuf + *outsize - len; + len += _POSIX2_LINE_MAX; + *outsize += _POSIX2_LINE_MAX; + } + switch (*s) { + case '\0': + state = ST_EOF; + if (s == script->s) { + script = script->next; + goto again; + } else { + script = script->next; + *p = '\0'; + linenum++; + return (*outbuf); + } + case '\n': + *p++ = '\n'; + *p = '\0'; + s++; + linenum++; + return (*outbuf); + default: + *p++ = *s++; + len--; + } + } + } + /* NOTREACHED */ + return (NULL); +} + +/* + * Like fgets, but go through the list of files chaining them together. + * Set len to the length of the line. + */ +int +mf_fgets(SPACE *sp, enum e_spflag spflag) +{ + static FILE *f; /* Current open file */ + size_t len; + char *p; + int c; + + if (f == NULL) + /* Advance to first non-empty file */ + for (;;) { + if (files == NULL) { + lastline = 1; + return (0); + } + if (files->fname == NULL) { + f = stdin; + fname = "stdin"; + } else { + fname = files->fname; + if ((f = fopen(fname, "r")) == NULL) + err(FATAL, "%s: %s", + fname, strerror(errno)); + } + if ((c = getc(f)) != EOF) { + (void)ungetc(c, f); + break; + } + (void)fclose(f); + files = files->next; + } + + if (lastline) { + sp->len = 0; + return (0); + } + + /* + * Use fgetln so that we can handle essentially infinite input data. + * Can't use the pointer into the stdio buffer as the process space + * because the ungetc() can cause it to move. + */ + p = fgetln(f, &len); + if (ferror(f)) + err(FATAL, "%s: %s", fname, strerror(errno ? errno : EIO)); + cspace(sp, p, len, spflag); + + linenum++; + /* Advance to next non-empty file */ + while ((c = getc(f)) == EOF) { + (void)fclose(f); + files = files->next; + if (files == NULL) { + lastline = 1; + return (1); + } + if (files->fname == NULL) { + f = stdin; + fname = "stdin"; + } else { + fname = files->fname; + if ((f = fopen(fname, "r")) == NULL) + err(FATAL, "%s: %s", fname, strerror(errno)); + } + } + (void)ungetc(c, f); + return (1); +} + +/* + * Add a compilation unit to the linked list + */ +static void +add_compunit(enum e_cut type, char *s) +{ + struct s_compunit *cu; + + cu = xmalloc(sizeof(struct s_compunit)); + cu->type = type; + cu->s = s; + cu->next = NULL; + *cu_nextp = cu; + cu_nextp = &cu->next; +} + +/* + * Add a file to the linked list + */ +static void +add_file(char *s) +{ + struct s_flist *fp; + + fp = xmalloc(sizeof(struct s_flist)); + fp->next = NULL; + *fl_nextp = fp; + fp->fname = s; + fl_nextp = &fp->next; +} diff --git a/usr.bin/sed/misc.c b/usr.bin/sed/misc.c new file mode 100644 index 000000000..76fe7ea31 --- /dev/null +++ b/usr.bin/sed/misc.c @@ -0,0 +1,164 @@ +/* $NetBSD: misc.c,v 1.11 2010/02/19 16:35:27 tnn Exp $ */ + +/*- + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93"; +#else +__RCSID("$NetBSD: misc.c,v 1.11 2010/02/19 16:35:27 tnn Exp $"); +#endif +#endif /* not lint */ + +#include + +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "extern.h" + +/* + * malloc with result test + */ +void * +xmalloc(size_t size) +{ + void *p; + + if ((p = malloc(size)) == NULL) + err(FATAL, "%s", strerror(errno)); + return (p); +} + +/* + * realloc with result test + */ +void * +xrealloc(void *p, size_t size) +{ + if (p == NULL) /* Compatibility hack. */ + return (xmalloc(size)); + + if ((p = realloc(p, size)) == NULL) + err(FATAL, "%s", strerror(errno)); + return (p); +} + +/* + * Return a string for a regular expression error passed. This is a overkill, + * because of the silly semantics of regerror (we can never know the size of + * the buffer). + */ +char * +strregerror(int errcode, regex_t *preg) +{ + char buf[1]; + static char *oe; + size_t s; + + if (oe != NULL) + free(oe); + s = regerror(errcode, preg, buf, 0); + oe = xmalloc(s); + (void)regerror(errcode, preg, oe, s); + return (oe); +} + +/* + * Error reporting function + */ +void +err(int severity, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)fprintf(stderr, "sed: "); + switch (severity) { + case WARNING: + case COMPILE: + (void)fprintf(stderr, "%lu: %s: ", linenum, fname); + } + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + if (severity == WARNING) + return; + exit(1); + /* NOTREACHED */ +} diff --git a/usr.bin/sed/process.c b/usr.bin/sed/process.c new file mode 100644 index 000000000..27dbce128 --- /dev/null +++ b/usr.bin/sed/process.c @@ -0,0 +1,691 @@ +/* $NetBSD: process.c,v 1.38 2009/04/13 07:29:55 lukem Exp $ */ + +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/*- + * Copyright (c) 1992 Diomidis Spinellis. + * + * This code is derived from software contributed to Berkeley by + * Diomidis Spinellis of Imperial College, University of London. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if HAVE_NBTOOL_CONFIG_H +#include "nbtool_config.h" +#endif + +#include +#ifndef lint +#if 0 +static char sccsid[] = "@(#)process.c 8.6 (Berkeley) 4/20/94"; +#else +__RCSID("$NetBSD: process.c,v 1.38 2009/04/13 07:29:55 lukem Exp $"); +#endif +#endif /* not lint */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "defs.h" +#include "extern.h" + +static SPACE HS, PS, SS; +#define pd PS.deleted +#define ps PS.space +#define psl PS.len +#define hs HS.space +#define hsl HS.len + +static inline int applies(struct s_command *); +static void flush_appends(void); +static void lputs(char *); +static inline int regexec_e(regex_t *, const char *, int, int, size_t); +static void regsub(SPACE *, char *, char *); +static int substitute(struct s_command *); + +struct s_appends *appends; /* Array of pointers to strings to append. */ +static int appendx; /* Index into appends array. */ +int appendnum; /* Size of appends array. */ + +static int lastaddr; /* Set by applies if last address of a range. */ +static int sdone; /* If any substitutes since last line input. */ + /* Iov structure for 'w' commands. */ +static regex_t *defpreg; +size_t maxnsub; +regmatch_t *match; + +#define OUT(s) { fwrite(s, sizeof(u_char), psl, stdout); } + +void +process(void) +{ + struct s_command *cp; + SPACE tspace; + size_t len, oldpsl; + char *p; + + oldpsl = 0; + for (linenum = 0; mf_fgets(&PS, REPLACE);) { + pd = 0; +top: + cp = prog; +redirect: + while (cp != NULL) { + if (!applies(cp)) { + cp = cp->next; + continue; + } + switch (cp->code) { + case '{': + cp = cp->u.c; + goto redirect; + case 'a': + if (appendx >= appendnum) { + appends = xrealloc(appends, + sizeof(struct s_appends) * + (appendnum * 2)); + appendnum *= 2; + } + appends[appendx].type = AP_STRING; + appends[appendx].s = cp->t; + appends[appendx].len = strlen(cp->t); + appendx++; + break; + case 'b': + cp = cp->u.c; + goto redirect; + case 'c': + pd = 1; + psl = 0; + if (cp->a2 == NULL || lastaddr) + (void)printf("%s", cp->t); + break; + case 'd': + pd = 1; + goto new; + case 'D': + if (psl == 0) + pd = 1; + if (pd) + goto new; + if ((p = memchr(ps, '\n', psl - 1)) == NULL) { + pd = 1; + goto new; + } else { + psl -= (p + 1) - ps; + memmove(ps, p + 1, psl); + goto top; + } + case 'g': + cspace(&PS, hs, hsl, REPLACE); + break; + case 'G': + if (hs == NULL) + cspace(&HS, "\n", 1, REPLACE); + cspace(&PS, hs, hsl, 0); + break; + case 'h': + cspace(&HS, ps, psl, REPLACE); + break; + case 'H': + cspace(&HS, ps, psl, 0); + break; + case 'i': + (void)printf("%s", cp->t); + break; + case 'l': + lputs(ps); + break; + case 'n': + if (!nflag && !pd) + OUT(ps) + flush_appends(); + if (!mf_fgets(&PS, REPLACE)) + exit(0); + pd = 0; + break; + case 'N': + flush_appends(); + if (!mf_fgets(&PS, 0)) { + if (!nflag && !pd) + OUT(ps) + exit(0); + } + break; + case 'p': + if (pd) + break; + OUT(ps) + break; + case 'P': + if (pd) + break; + if ((p = memchr(ps, '\n', psl - 1)) != NULL) { + oldpsl = psl; + psl = (p + 1) - ps; + } + OUT(ps) + if (p != NULL) + psl = oldpsl; + break; + case 'q': + if (!nflag && !pd) + OUT(ps) + flush_appends(); + exit(0); + case 'r': + if (appendx >= appendnum) { + appends = xrealloc(appends, + sizeof(struct s_appends) * + (appendnum * 2)); + appendnum *= 2; + } + appends[appendx].type = AP_FILE; + appends[appendx].s = cp->t; + appends[appendx].len = strlen(cp->t); + appendx++; + break; + case 's': + sdone |= substitute(cp); + break; + case 't': + if (sdone) { + sdone = 0; + cp = cp->u.c; + goto redirect; + } + break; + case 'w': + if (pd) + break; + if (cp->u.fd == -1 && (cp->u.fd = open(cp->t, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, + DEFFILEMODE)) == -1) + err(FATAL, "%s: %s", + cp->t, strerror(errno)); + if ((size_t)write(cp->u.fd, ps, psl) != psl) + err(FATAL, "%s: %s", + cp->t, strerror(errno)); + break; + case 'x': + if (hs == NULL) + cspace(&HS, "\n", 1, REPLACE); + tspace = PS; + PS = HS; + HS = tspace; + break; + case 'y': + if (pd) + break; + for (p = ps, len = psl; --len; ++p) + *p = cp->u.y[(int)*p]; + break; + case ':': + case '}': + break; + case '=': + (void)printf("%lu\n", linenum); + } + cp = cp->next; + } /* for all cp */ + +new: if (!nflag && !pd) + OUT(ps) + flush_appends(); + } /* for all lines */ +} + +/* + * TRUE if the address passed matches the current program state + * (lastline, linenumber, ps). + */ +#define MATCH(a) \ + (a)->type == AT_RE ? regexec_e((a)->u.r, ps, 0, 1, psl) : \ + (a)->type == AT_LINE ? linenum == (a)->u.l : lastline + +/* + * Return TRUE if the command applies to the current line. Sets the inrange + * flag to process ranges. Interprets the non-select (``!'') flag. + */ +static inline int +applies(struct s_command *cp) +{ + int r; + + lastaddr = 0; + if (cp->a1 == NULL && cp->a2 == NULL) + r = 1; + else if (cp->a2) { + if (cp->inrange) { + if (MATCH(cp->a2)) { + cp->inrange = 0; + lastaddr = 1; + } + r = 1; + } else if (cp->a1 && MATCH(cp->a1)) { + /* + * If the second address is a number less than or + * equal to the line number first selected, only + * one line shall be selected. + * -- POSIX 1003.2 + */ + if (cp->a2->type == AT_LINE && + linenum >= cp->a2->u.l) + lastaddr = 1; + else + cp->inrange = 1; + r = 1; + } else + r = 0; + } else + r = MATCH(cp->a1); + return (cp->nonsel ? ! r : r); +} + +/* + * substitute -- + * Do substitutions in the pattern space. Currently, we build a + * copy of the new pattern space in the substitute space structure + * and then swap them. + */ +static int +substitute(struct s_command *cp) +{ + SPACE tspace; + regex_t *re; + size_t re_off, slen; + int lastempty, n; + char *s; + + s = ps; + re = cp->u.s->re; + if (re == NULL) { + if (defpreg != NULL && (size_t)cp->u.s->maxbref > defpreg->re_nsub) { + linenum = cp->u.s->linenum; + err(COMPILE, "\\%d not defined in the RE", + cp->u.s->maxbref); + } + } + if (!regexec_e(re, s, 0, 0, psl)) + return (0); + + SS.len = 0; /* Clean substitute space. */ + slen = psl; + n = cp->u.s->n; + lastempty = 1; + + switch (n) { + case 0: /* Global */ + do { + if (lastempty || match[0].rm_so != match[0].rm_eo) { + /* Locate start of replaced string. */ + re_off = match[0].rm_so; + /* Copy leading retained string. */ + cspace(&SS, s, re_off, APPEND); + /* Add in regular expression. */ + regsub(&SS, s, cp->u.s->new); + } + + /* Move past this match. */ + if (match[0].rm_so != match[0].rm_eo) { + s += match[0].rm_eo; + slen -= match[0].rm_eo; + lastempty = 0; + } else { + if (match[0].rm_so == 0) + cspace(&SS, + s, match[0].rm_so + 1, APPEND); + else + cspace(&SS, + s + match[0].rm_so, 1, APPEND); + s += match[0].rm_so + 1; + slen -= match[0].rm_so + 1; + lastempty = 1; + } + } while (slen > 0 && regexec_e(re, s, REG_NOTBOL, 0, slen)); + /* Copy trailing retained string. */ + if (slen > 0) + cspace(&SS, s, slen, APPEND); + break; + default: /* Nth occurrence */ + while (--n) { + s += match[0].rm_eo; + slen -= match[0].rm_eo; + if (!regexec_e(re, s, REG_NOTBOL, 0, slen)) + return (0); + } + /* FALLTHROUGH */ + case 1: /* 1st occurrence */ + /* Locate start of replaced string. */ + re_off = match[0].rm_so + (s - ps); + /* Copy leading retained string. */ + cspace(&SS, ps, re_off, APPEND); + /* Add in regular expression. */ + regsub(&SS, s, cp->u.s->new); + /* Copy trailing retained string. */ + s += match[0].rm_eo; + slen -= match[0].rm_eo; + cspace(&SS, s, slen, APPEND); + break; + } + + /* + * Swap the substitute space and the pattern space, and make sure + * that any leftover pointers into stdio memory get lost. + */ + tspace = PS; + PS = SS; + SS = tspace; + SS.space = SS.back; + + /* Handle the 'p' flag. */ + if (cp->u.s->p) + OUT(ps) + + /* Handle the 'w' flag. */ + if (cp->u.s->wfile && !pd) { + if (cp->u.s->wfd == -1 && (cp->u.s->wfd = open(cp->u.s->wfile, + O_WRONLY|O_APPEND|O_CREAT|O_TRUNC, DEFFILEMODE)) == -1) + err(FATAL, "%s: %s", cp->u.s->wfile, strerror(errno)); + if ((size_t)write(cp->u.s->wfd, ps, psl) != psl) + err(FATAL, "%s: %s", cp->u.s->wfile, strerror(errno)); + } + return (1); +} + +/* + * Flush append requests. Always called before reading a line, + * therefore it also resets the substitution done (sdone) flag. + */ +static void +flush_appends(void) +{ + FILE *f; + int count, i; + char buf[8 * 1024]; + + for (i = 0; i < appendx; i++) + switch (appends[i].type) { + case AP_STRING: + fwrite(appends[i].s, sizeof(char), appends[i].len, + stdout); + break; + case AP_FILE: + /* + * Read files probably shouldn't be cached. Since + * it's not an error to read a non-existent file, + * it's possible that another program is interacting + * with the sed script through the file system. It + * would be truly bizarre, but possible. It's probably + * not that big a performance win, anyhow. + */ + if ((f = fopen(appends[i].s, "r")) == NULL) + break; + while ((count = + fread(buf, sizeof(char), sizeof(buf), f)) > 0) + (void)fwrite(buf, sizeof(char), count, stdout); + (void)fclose(f); + break; + } + if (ferror(stdout)) + err(FATAL, "stdout: %s", strerror(errno ? errno : EIO)); + appendx = sdone = 0; +} + +static void +lputs(char *s) +{ + int count; + const char *escapes, *p; +#ifndef HAVE_NBTOOL_CONFIG_H + struct winsize win; +#endif + static int termwidth = -1; + + if (termwidth == -1) { + if ((p = getenv("COLUMNS")) != NULL) + termwidth = atoi(p); +#ifndef HAVE_NBTOOL_CONFIG_H + else if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &win) == 0 && + win.ws_col > 0) + termwidth = win.ws_col; +#endif + else + termwidth = 60; + } + for (count = 0; *s; ++s) { + if (count >= termwidth) { + (void)printf("\\\n"); + count = 0; + } + if (isascii((unsigned char)*s) && isprint((unsigned char)*s) && + *s != '\\') { + (void)putchar(*s); + count++; + } else { + escapes = "\\\a\b\f\n\r\t\v"; + (void)putchar('\\'); + if ((p = strchr(escapes, *s)) != NULL) { + (void)putchar("\\abfnrtv"[p - escapes]); + count += 2; + } else { + (void)printf("%03o", *(u_char *)s); + count += 4; + } + } + } + (void)putchar('$'); + (void)putchar('\n'); + if (ferror(stdout)) + err(FATAL, "stdout: %s", strerror(errno ? errno : EIO)); +} + +static inline int +regexec_e(regex_t *preg, const char *string, int eflags, int nomatch, size_t slen) +{ + int eval; +#ifndef REG_STARTEND + char *buf; +#endif + + if (preg == NULL) { + if (defpreg == NULL) + err(FATAL, "first RE may not be empty"); + } else + defpreg = preg; + + /* Set anchors, discounting trailing newline (if any). */ + if (slen > 0 && string[slen - 1] == '\n') + slen--; + +#ifndef REG_STARTEND + if ((buf = malloc(slen + 1)) == NULL) + err(1, NULL); + (void)memcpy(buf, string, slen); + buf[slen] = '\0'; + eval = regexec(defpreg, buf, + nomatch ? 0 : maxnsub + 1, match, eflags); + free(buf); +#else + match[0].rm_so = 0; + match[0].rm_eo = slen; + eval = regexec(defpreg, string, + nomatch ? 0 : maxnsub + 1, match, eflags | REG_STARTEND); +#endif + switch(eval) { + case 0: + return (1); + case REG_NOMATCH: + return (0); + } + err(FATAL, "RE error: %s", strregerror(eval, defpreg)); + /* NOTREACHED */ + return (0); +} + +/* + * regsub - perform substitutions after a regexp match + * Based on a routine by Henry Spencer + */ +static void +regsub(SPACE *sp, char *string, char *src) +{ + int len, no; + char c, *dst; + +#define NEEDSP(reqlen) \ + if (sp->len + (reqlen) + 1 >= sp->blen) { \ + size_t newlen = sp->blen + (reqlen) + 1024; \ + sp->space = sp->back = xrealloc(sp->back, newlen); \ + sp->blen = newlen; \ + dst = sp->space + sp->len; \ + } + + dst = sp->space + sp->len; + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '\\' && isdigit((unsigned char)*src)) + no = *src++ - '0'; + else + no = -1; + if (no < 0) { /* Ordinary character. */ + if (c == '\\' && (*src == '\\' || *src == '&')) + c = *src++; + NEEDSP(1); + *dst++ = c; + ++sp->len; + } else if (match[no].rm_so != -1 && match[no].rm_eo != -1) { + len = match[no].rm_eo - match[no].rm_so; + NEEDSP(len); + memmove(dst, string + match[no].rm_so, len); + dst += len; + sp->len += len; + } + } + NEEDSP(1); + *dst = '\0'; +} + +/* + * aspace -- + * Append the source space to the destination space, allocating new + * space as necessary. + */ +void +cspace(SPACE *sp, const char *p, size_t len, enum e_spflag spflag) +{ + size_t tlen; + + /* Make sure SPACE has enough memory and ramp up quickly. */ + tlen = sp->len + len + 1; + if (tlen > sp->blen) { + size_t newlen = tlen + 1024; + sp->space = sp->back = xrealloc(sp->back, newlen); + sp->blen = newlen; + } + + if (spflag == REPLACE) + sp->len = 0; + + memmove(sp->space + sp->len, p, len); + + sp->space[sp->len += len] = '\0'; +} + +/* + * Close all cached opened files and report any errors + */ +void +cfclose(struct s_command *cp, struct s_command *end) +{ + + for (; cp != end; cp = cp->next) + switch(cp->code) { + case 's': + if (cp->u.s->wfd != -1 && close(cp->u.s->wfd)) + err(FATAL, + "%s: %s", cp->u.s->wfile, strerror(errno)); + cp->u.s->wfd = -1; + break; + case 'w': + if (cp->u.fd != -1 && close(cp->u.fd)) + err(FATAL, "%s: %s", cp->t, strerror(errno)); + cp->u.fd = -1; + break; + case '{': + cfclose(cp->u.c, cp->next); + break; + } +} diff --git a/usr.bin/sed/sed.1 b/usr.bin/sed/sed.1 new file mode 100644 index 000000000..9d2fe34b3 --- /dev/null +++ b/usr.bin/sed/sed.1 @@ -0,0 +1,518 @@ +.\" $NetBSD: sed.1,v 1.27 2008/09/21 16:46:01 wiz Exp $ +.\" +.\" Copyright (c) 1992, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sed.1 8.2 (Berkeley) 12/30/93 +.\" +.Dd September 21, 2008 +.Dt SED 1 +.Os +.Sh NAME +.Nm sed +.Nd stream editor +.Sh SYNOPSIS +.Nm +.Op Fl aEnr +.Ar command +.Op Ar file ... +.Nm +.Op Fl aEnr +.Op Fl e Ar command +.Op Fl f Ar command_file +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm +utility reads the specified files, or the standard input if no files +are specified, modifying the input as specified by a list of commands. +The input is then written to the standard output. +.Pp +A single command may be specified as the first argument to +.Nm . +Multiple commands may be specified by using the +.Fl e +or +.Fl f +options. +All commands are applied to the input in the order they are specified +regardless of their origin. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +The files listed as parameters for the +.Dq w +functions are created (or truncated) before any processing begins, +by default. +The +.Fl a +option causes +.Nm +to delay opening each file until a command containing the related +.Dq w +function is applied to a line of input. +.It Fl E +Enables the use of extended regular expressions instead of the +usual basic regular expression syntax. +.It Fl e Ar command +Append the editing commands specified by the +.Ar command +argument +to the list of commands. +.It Fl f Ar command_file +Append the editing commands found in the file +.Ar command_file +to the list of commands. +The editing commands should each be listed on a separate line. +.It Fl n +By default, each line of input is echoed to the standard output after +all of the commands have been applied to it. +The +.Fl n +option suppresses this behavior. +.It Fl r +Identical to +.Fl E , +present for compatibility with GNU sed. +.El +.Pp +The form of a +.Nm +command is as follows: +.sp +.Dl [address[,address]]function[arguments] +.sp +Whitespace may be inserted before the first address and the function +portions of the command. +.Pp +Normally, +.Nm +cyclically copies a line of input, not including its terminating newline +character, into a +.Em "pattern space" , +(unless there is something left after a +.Dq D +function), +applies all of the commands with addresses that select that pattern space, +copies the pattern space to the standard output, appending a newline, and +deletes the pattern space. +.Pp +Some of the functions use a +.Em "hold space" +to save all or part of the pattern space for subsequent retrieval. +.Sh SED ADDRESSES +An address is not required, but if specified must be a number (that counts +input lines +cumulatively across input files), a dollar +.Po +.Dq $ +.Pc +character that addresses the last line of input, or a context address +(which consists of a regular expression preceded and followed by a +delimiter). +.Pp +A command line with no addresses selects every pattern space. +.Pp +A command line with one address selects all of the pattern spaces +that match the address. +.Pp +A command line with two addresses selects the inclusive range from +the first pattern space that matches the first address through the next +pattern space that matches the second. +(If the second address is a number less than or equal to the line number +first selected, only that line is selected.) +Starting at the first line following the selected range, +.Nm +starts looking again for the first address. +.Pp +Editing commands can be applied to non-selected pattern spaces by use +of the exclamation character +.Pq Dq \&! +function. +.Sh SED REGULAR EXPRESSIONS +The +.Nm +regular expressions are basic regular expressions (BRE's, see +.Xr re_format 7 +for more information). +In addition, +.Nm +has the following two additions to BRE's: +.sp +.Bl -enum -compact +.It +In a context address, any character other than a backslash +.Po +.Dq \e +.Pc +or newline character may be used to delimit the regular expression +by prefixing the first use of that delimiter with a backslash. +Also, putting a backslash character before the delimiting character +causes the character to be treated literally. +For example, in the context address \exabc\exdefx, the RE delimiter +is an +.Dq x +and the second +.Dq x +stands for itself, so that the regular expression is +.Dq abcxdef . +.sp +.It +The escape sequence \en matches a newline character embedded in the +pattern space. +You can't, however, use a literal newline character in an address or +in the substitute command. +.El +.Pp +One special feature of +.Nm +regular expressions is that they can default to the last regular +expression used. +If a regular expression is empty, i.e. just the delimiter characters +are specified, the last regular expression encountered is used instead. +The last regular expression is defined as the last regular expression +used as part of an address or substitute command, and at run-time, not +compile-time. +For example, the command +.Dq /abc/s//XXX/ +will substitute +.Dq XXX +for the pattern +.Dq abc . +.Sh SED FUNCTIONS +In the following list of commands, the maximum number of permissible +addresses for each command is indicated by [0addr], [1addr], or [2addr], +representing zero, one, or two addresses. +.Pp +The argument +.Em text +consists of one or more lines. +To embed a newline in the text, precede it with a backslash. +Other backslashes in text are deleted and the following character +taken literally. +.Pp +The +.Dq r +and +.Dq w +functions take an optional file parameter, which should be separated +from the function letter by white space. +Each file given as an argument to +.Nm +is created (or its contents truncated) before any input processing begins. +.Pp +The +.Dq b , +.Dq r , +.Dq s , +.Dq t , +.Dq w , +.Dq y , +.Dq \&! , +and +.Dq \&: +functions all accept additional arguments. +The following synopses indicate which arguments have to be separated from +the function letters by white space characters. +.Pp +Two of the functions take a function-list. +This is a list of +.Nm +functions separated by newlines, as follows: +.Bd -literal -offset indent +{ function + function + ... + function +} +.Ed +.Pp +The +.Dq { +can be preceded by white space and can be followed by white space. +The function can be preceded by white space. +The terminating +.Dq } +must be preceded by a newline (and optionally white space). +.sp +.Bl -tag -width "XXXXXX" -compact +.It [2addr] function-list +Execute function-list only when the pattern space is selected. +.sp +.It [1addr]a\e +.It text +.br +Write +.Em text +to standard output immediately before each attempt to read a line of input, +whether by executing the +.Dq N +function or by beginning a new cycle. +.sp +.It [2addr]b[label] +Branch to the +.Dq \&: +function with the specified label. +If the label is not specified, branch to the end of the script. +.sp +.It [2addr]c\e +.It text +.br +Delete the pattern space. +With 0 or 1 address or at the end of a 2-address range, +.Em text +is written to the standard output. +.sp +.It [2addr]d +Delete the pattern space and start the next cycle. +.sp +.It [2addr]D +Delete the initial segment of the pattern space through the first +newline character and start the next cycle. +.sp +.It [2addr]g +Replace the contents of the pattern space with the contents of the +hold space. +.sp +.It [2addr]G +Append a newline character followed by the contents of the hold space +to the pattern space. +.sp +.It [2addr]h +Replace the contents of the hold space with the contents of the +pattern space. +.sp +.It [2addr]H +Append a newline character followed by the contents of the pattern space +to the hold space. +.sp +.It [1addr]i\e +.It text +.br +Write +.Em text +to the standard output. +.sp +.It [2addr]l +(The letter ell.) +Write the pattern space to the standard output in a visually unambiguous +form. +This form is as follows: +.sp +.Bl -tag -width "carriage-returnXX" -offset indent -compact +.It backslash +\e\e +.It alert +\ea +.It form-feed +\ef +.It newline +\en +.It carriage-return +\er +.It tab +\et +.It vertical tab +\ev +.El +.Pp +Nonprintable characters are written as three-digit octal numbers (with a +preceding backslash) for each byte in the character (most significant byte +first). +Long lines are folded, with the point of folding indicated by displaying +a backslash followed by a newline. +The end of each line is marked with a +.Dq $ . +.sp +.It [2addr]n +Write the pattern space to the standard output if the default output has +not been suppressed, and replace the pattern space with the next line of +input. (Does not begin a new cycle.) +.sp +.It [2addr]N +Append the next line of input to the pattern space, using an embedded +newline character to separate the appended material from the original +contents. +Note that the current line number changes. +.sp +.It [2addr]p +Write the pattern space to standard output. +.sp +.It [2addr]P +Write the pattern space, up to the first newline character to the +standard output. +.sp +.It [1addr]q +Branch to the end of the script and quit without starting a new cycle. +.sp +.It [1addr]r file +Copy the contents of +.Em file +to the standard output immediately before the next attempt to read a +line of input. +If +.Em file +cannot be read for any reason, it is silently ignored and no error +condition is set. +.sp +.It [2addr]s/regular expression/replacement/flags +Substitute the replacement string for the first instance of the regular +expression in the pattern space. +Any character other than backslash or newline can be used instead of +a slash to delimit the RE and the replacement. +Within the RE and the replacement, the RE delimiter itself can be used as +a literal character if it is preceded by a backslash. +.Pp +An ampersand +.Po +.Dq \*[Am] +.Pc +appearing in the replacement is replaced by the string matching the RE. +The special meaning of +.Dq \*[Am] +in this context can be suppressed by preceding it by a backslash. +The string +.Dq \e# , +where +.Dq # +is a digit, is replaced by the text matched +by the corresponding backreference expression (see +.Xr re_format 7 ) . +.Pp +A line can be split by substituting a newline character into it. +To specify a newline character in the replacement string, precede it with +a backslash. +.Pp +The value of +.Em flags +in the substitute function is zero or more of the following: +.Bl -tag -width "XXXXXX" -offset indent +.It "0 ... 9" +Make the substitution only for the N'th occurrence of the regular +expression in the pattern space. +.It g +Make the substitution for all non-overlapping matches of the +regular expression, not just the first one. +.It p +Write the pattern space to standard output if a replacement was made. +If the replacement string is identical to that which it replaces, it +is still considered to have been a replacement. +.It w Em file +Append the pattern space to +.Em file +if a replacement was made. +If the replacement string is identical to that which it replaces, it +is still considered to have been a replacement. +.El +.sp +.It [2addr]t [label] +Branch to the +.Dq \&: +function bearing the label if any substitutions have been made since the +most recent reading of an input line or execution of a +.Dq t +function. +If no label is specified, branch to the end of the script. +.sp +.It [2addr]w Em file +Append the pattern space to the +.Em file . +.sp +.It [2addr]x +Swap the contents of the pattern and hold spaces. +.sp +.It [2addr]y/string1/string2/ +Replace all occurrences of characters in +.Em string1 +in the pattern space with the corresponding characters from +.Em string2 . +Any character other than a backslash or newline can be used instead of +a slash to delimit the strings. +Within +.Em string1 +and +.Em string2 , +a backslash followed by any character other than a newline is that literal +character, and a backslash followed by an ``n'' is replaced by a newline +character. +.sp +.It [2addr]!function +.It [2addr]!function-list +Apply the function or function-list only to the lines that are +.Em not +selected by the address(es). +.sp +.It [0addr]:label +This function does nothing; it bears a label to which the +.Dq b +and +.Dq t +commands may branch. +.sp +.It [1addr]= +Write the line number to the standard output followed by a newline +character. +.sp +.It [0addr] +Empty lines are ignored. +.sp +.It [0addr]# +The +.Dq # +and the remainder of the line are ignored (treated as a comment), with +the single exception that if the first two characters in the file are +.Dq #n , +the default output is suppressed. +This is the same as specifying the +.Fl n +option on the command line. +.El +.Pp +The +.Nm +utility exits 0 on success and \*[Gt]0 if an error occurs. +.Sh SEE ALSO +.Xr awk 1 , +.Xr ed 1 , +.Xr grep 1 , +.Xr regex 3 , +.Xr re_format 7 +.Sh STANDARDS +The +.Nm +function is expected to be a superset of the +.St -p1003.2 +specification. +.Sh HISTORY +A +.Nm +command appeared in +.At v7 .