[net.sources] Code for Labeled Breaks in C

george@idis.UUCP (01/31/85)

:		Labeled Breaks and Continues for C
:
: 'This is a shell archive.  Remove anything before this line,'
: 'then unpack it by saving it in a file and typing "sh file".'
:
: Wrapped by idis!george on Thu Jan 31 10:18:49 EST 1985
: Contents:  break/ break/Makefile break/Read-me break/break1.h break/break2.h
:	break/break3.h break/bsdsize break/goto.c break/mbreak.c
:	break/regtst.ed break/select.ed break/tst.c
 
echo mkdir - break
mkdir break
chmod u=rwx,g=rx,o=rx break
 
echo x - break/Makefile
sed 's/^@//' > "break/Makefile" <<'@//E*O*F break/Makefile//'
CFLAGS= -O
LIB=/lib
LIBC=${LIB}/libc.a
INCLUDE=/usr/include
REG=-DCLASS=register
H=break.h
I=${INCLUDE}/$H

all: break.h mbreak.o

break.h: select
	rm -f break.h
	sh ./select

select: select.ed regtst
	sort -n +9 -10 +3 -4 regtst >select
	ed select <select.ed

regtst: regtst.ed tst1.o tst2.o tst3.o reg1.o reg2.o reg3.o bsdsize
	rm -f tst.o
	bsdsize tst?.o reg?.o | grep -v text | sort +5.3 -5.4 +5 >regtst
	ed regtst <regtst.ed

tst1.o: tst.c break1.h
	rm -f break.h
	ln break1.h break.h
	${CC} ${CFLAGS} -c tst.c
	cp tst.o tst1.o

tst2.o: tst.c break2.h
	rm -f break.h
	ln break2.h break.h
	${CC} ${CFLAGS} -c tst.c
	cp tst.o tst2.o

tst3.o: tst.c break3.h
	rm -f break.h
	ln break3.h break.h
	${CC} ${CFLAGS} -c tst.c
	cp tst.o tst3.o

reg1.o: tst.c break1.h
	rm -f break.h
	ln break1.h break.h
	${CC} ${CFLAGS} -c ${REG} tst.c
	cp tst.o reg1.o

reg2.o: tst.c break2.h
	rm -f break.h
	ln break2.h break.h
	${CC} ${CFLAGS} -c ${REG} tst.c
	cp tst.o reg2.o

reg3.o: tst.c break3.h
	rm -f break.h
	ln break3.h break.h
	${CC} ${CFLAGS} -c ${REG} tst.c
	cp tst.o reg3.o

measure: all goto.o
	rm -rf tst.o
	${CC} ${CFLAGS} -c tst.c
	size tst.o goto.o

install: all
	rm -f $I
	if ln $H $I; then :; elif ln -s `pwd`/$H $I; then :; else cp $H $I; fi
	if ar ru ${LIBC} mbreak.o ; then ranlib ${LIBC} ; : ; fi

clean:
	rm -f regtst select tst*.o reg?.o goto.o

pristine: clean
	rm -f break.h mbreak.o

sterile: pristine
	;
@//E*O*F break/Makefile//
chmod u=rw,g=r,o=r break/Makefile
 
echo x - break/Read-me
sed 's/^@//' > "break/Read-me" <<'@//E*O*F break/Read-me//'
		Labeled Breaks and Continues for C
			    15 Jan 85
			 George Rosenberg


Note:	I currently consider this code experimental
	and its specifications are subject to change.
	Since I have recently written the macros,
	and one rarely has use for multi-level breaks
	or continues in C programs,
	I have not yet had occasion to actually use them.


This is a set of macros to perform labeled multi-level
breaks and continues in C programs.
There are three versions, "break1.h", "break2.h", and "break3.h".
They all have the same specifications.
The preferred version is dependent on your compiler with optimizer.
That one should be called (linked to) "break.h".
There is a Makefile provided to do this.

The macros are:
	Labels
	Label(id)
	Break(id)
	Continue(id)

"Break(id) ;" is a statement that causes control to flow
past a "labeled-breakable-statement", S,
labeled with identifier "id" via "Label(id)".
The "Break" must be contained in the scope of the body of statement S.

"Continue(id) ;" is a statement that causes control to flow to the beginning
of a "labeled-continuable-statement", W,
labeled with identifier "id" via "Label(id)".
The underlying statement in W must be a "while" statement.
It must not be a "for" statement or a "do" statement.
The "Continue" must be contained in the scope of the body of statement W.

The macro "Label(id)" is prefixed to a statement to be used in conjunction
with "Break" or "Continue" as described above.
The syntax for using "Label" is below.

	label-macro:
		Label left-parenthesis identifier right-parenthesis

	labeled-continuable-statement:
		label-macro while-statement
		label-macro labeled-continuable-statement

	labeled-breakable-statement:
		label-macro breakable-statement
		label-macro labeled-breakable-statement

	breakable-statement:
		for-statement
		while-statement
		do-statement
		switch-statement
		conditional-statement
		compound-statement

"Labels" is part of an optional declaration.
"Labels ;"  or "register Labels ;" may occur
in the declaration section of a block.
This declaration is not intended to be used outside
the scope of a routine (i.e. at the top level).
Presumably this block will contain one or more occurrences
of the macro "Label".
All references to the same "id" must be within the scope
of the same "Labels" declaration or they must all be free.
This declaration may be necessary if there is asynchronous control flow.
Use of this declaration may result in the compiler and optimizer
producing better code.
It is recommended that this declaration always be used.
The file "mbreak.c" contains a global object that may be used with free
occurrences of "Label".
It may be desirable to have this in the library (i.e. "libc.a").

See the file "tst.c" for an example.

Although there is apparent overhead involved with these macros,
it is not unreasonable for a simple (e.g. "c2" like) optimizer
to eliminate much of that overhead.
Of course, just because it is not unreasonable, does not mean it will
be optimized on any particular system.

Below are the make options:
	make			default = make all
	make all		make locally: break.h mbreak.o
	make measure		size tst.o goto.o
	make install		modify /usr/include/break.h and /lib/libc.a
	make clean		delete intermediary objects
	make pristine		put local things back to their pristine state

The "install" option is untested.

The shell script "bsdsize" may need to be modified if your "size" command
formats its output inappropriately.
It currently works with both the vax-11 4.2 BSD unix "size" command
and the pdp-11 v7 unix "size" command.
@//E*O*F break/Read-me//
chmod u=rw,g=r,o=r break/Read-me
 
echo x - break/break1.h
sed 's/^@//' > "break/break1.h" <<'@//E*O*F break/break1.h//'
/*
 * break.h
 * version 1
 * 12 Jan 85
 * George Rosenberg
 *
 * Warning:	Continue(id) will only work with a while statement.
 */

#ifndef Break

#define	Labels int Breaking = 0
#define	Label(id) id: if (Breaking) Breaking=0; else
#define	Break(id) for (Breaking=1;;) goto id
#define	Continue(id) goto id

extern Breaking ;

#endif
@//E*O*F break/break1.h//
chmod u=rw,g=r,o=r break/break1.h
 
echo x - break/break2.h
sed 's/^@//' > "break/break2.h" <<'@//E*O*F break/break2.h//'
/*
 * break.h
 * version 2
 * 12 Jan 85
 * George Rosenberg
 *
 * Warning:	Continue(id) will only work with a while statement.
 */

#ifndef Break

#define	Labels int Breaking
#define	Label(id) if (Breaking=0) ; else id: if (Breaking) Breaking=0; else
#define	Break(id) for (Breaking=1;;) goto id
#define	Continue(id) goto id

extern Breaking ;

#endif
@//E*O*F break/break2.h//
chmod u=rw,g=r,o=r break/break2.h
 
echo x - break/break3.h
sed 's/^@//' > "break/break3.h" <<'@//E*O*F break/break3.h//'
/*
 * break.h
 * version 3
 * 12 Jan 85
 * George Rosenberg
 *
 * Warning:	Continue(id) will only work with a while statement.
 */

#ifndef Break

#define	Labels int Breaking = 0
#define	Label(id) id: if (Breaking) Breaking=0; else
#define	Break(id) for (++Breaking;;) goto id
#define	Continue(id) goto id

extern Breaking ;

#endif
@//E*O*F break/break3.h//
chmod u=rw,g=r,o=r break/break3.h
 
echo x - break/bsdsize
sed 's/^@//' > "break/bsdsize" <<'@//E*O*F break/bsdsize//'
: This is supposed to
: produce size output in bsd format
: from either v7 size or 4.2bsd size.
: It garbles error messages.
: It is untested.

TMP=/tmp/'#'size.$$

trap 'rm -f $TMP' 0 1 2 13 15
size $@ >$TMP

ed $TMP >/dev/null <<EOF
g/text	data	bss/q
g/b$/s///
g/+/s//	/g
g/b = /s//	/g
g/ = /s//	/g
g/:/s/\(.*\)\(:[ 	]*\)\(.*\)/\3	\1/
1i
text	data	bss	dec	oct
@.
w
q
EOF

cat $TMP /dev/null
@//E*O*F break/bsdsize//
chmod u=rwx,g=rx,o=rx break/bsdsize
 
echo x - break/goto.c
sed 's/^@//' > "break/goto.c" <<'@//E*O*F break/goto.c//'
test()
{
	 for (;;) {

		fun1() ;

		while (fun2())
			if (fun3())
				goto out ;

		fun4() ;
	 }
	out:

	 fun5() ;
}
@//E*O*F break/goto.c//
chmod u=rw,g=r,o=r break/goto.c
 
echo x - break/mbreak.c
sed 's/^@//' > "break/mbreak.c" <<'@//E*O*F break/mbreak.c//'
int Breaking = 0 ;
@//E*O*F break/mbreak.c//
chmod u=rw,g=r,o=r break/mbreak.c
 
echo x - break/regtst.ed
sed 's/^@//' > "break/regtst.ed" <<'@//E*O*F break/regtst.ed//'
g/reg/s/$/	/
g/reg/j
w
q
@//E*O*F break/regtst.ed//
chmod u=rw,g=r,o=r break/regtst.ed
 
echo x - break/select.ed
sed 's/^@//' > "break/select.ed" <<'@//E*O*F break/select.ed//'
2,$d
s/.*tst/ln break/
s/o.*/h break.h/
w
q
@//E*O*F break/select.ed//
chmod u=rw,g=r,o=r break/select.ed
 
echo x - break/tst.c
sed 's/^@//' > "break/tst.c" <<'@//E*O*F break/tst.c//'
#include "break.h"

#ifndef CLASS
#define	CLASS
#endif

test()
{
	CLASS Labels ;

	Label(outer)
	 for (;;) {

		fun1() ;

		while (fun2())
			if (fun3())
				Break(outer) ;

		fun4() ;
	 }

	 fun5() ;
}
@//E*O*F break/tst.c//
chmod u=rw,g=r,o=r break/tst.c
 
echo Inspecting for damage in transit...
temp=/tmp/shar$$; dtemp=/tmp/.shar$$
trap "rm -f $temp $dtemp; exit" 0 1 2 3 15
cat > $temp <<\!!!
      77     225    1416 Makefile
     101     532    3572 Read-me
      19      54     333 break1.h
      19      56     352 break2.h
      19      54     333 break3.h
      26      76     392 bsdsize
      16      21     118 goto.c
       1       5      19 mbreak.c
       4       5      25 regtst.ed
       5       7      44 select.ed
      24      30     203 tst.c
     311    1065    6807 total
!!!
wc  break/Makefile break/Read-me break/break1.h break/break2.h break/break3.h break/bsdsize break/goto.c break/mbreak.c break/regtst.ed break/select.ed break/tst.c | sed 's=[^ ]*/==' | diff -b $temp - >$dtemp
if [ -s $dtemp ]
then echo "Ouch [diff of wc output]:" ; cat $dtemp
else echo "No problems found."
fi
exit 0