[comp.lang.c] The final word on GOTO

bill@twwells.com (T. William Wells) (09/14/89)

In article <7917@goofy.megatest.UUCP> djones@megatest.UUCP (Dave Jones) writes:
: ) Well yeah, so would I. But what is the bug? I've just reread the code
: ) and don't see where I've broken it. I'm prepared to be red-faced
: ) because this has go to be something really obvious, but what?
:
: DON'T PANIC.

Good thing, my Panic Button is broken! I hit it too many times
already today. :-)

: Your face is going to be just fine. But it's lucky for me that I don't blush.
: You didn't misread it. I did. In fact, I read it half a dozen times before I saw
: the "break" at the end of the while-loop.
:
: Boy, that's wierd.
:
: No offense, but you won't catch me using this device. Different strokes,
: I guess.

Yeah. No doubt the strangeness of it will put many people off. Well
one day I just got so tired of icky argument parsing loops that I
devised a thing with a break past the bottom of the switch. Once I
realized that this solved many cases of the two level break problem,
I started using it fairly regularly. For me it is now a familiar
idiom; I don't have any problem with it any more. My feeling is that C
idiom is already sufficiently strange, what's one more wierdness among
programmers? :-)

As far as I'm concerned, *every* method of solving the two level
break problem is flawed in one way or another. The right solution is
a language innovation, but I'm not going to hold my breath.

But anyway, that isn't really why I'm posting. You may recall an
article I wrote on this subject the last time it came 'round (over in
comp.misc). I'm enclosing an updated version here because it really
*is* the final word on goto. (Ok, it certainly could be elaborated.
But as the guy said, all else is commentary....)

---

Under ordinary circumstances, there is exactly one place I know of
where a human C coder might properly use a goto. This is to implement
multi-level breaks and continues.

I say this, having written hundreds of thousands of C code, and
managed several programmers also writing mainly C code. I have
programmed in C since 1981 and have NEVER used a goto. We have
uncounted megabytes of C code written in-house. There are no gotos in
it.

With that up front, let me say why the goto discussion is really
fruitless: it completely misses the point. People have observed that
gotos are used in a lot of bad code. From this it is concluded that
gotos are bad. This is really bad logic. Try this: programmers have
been observed to write bad code; therefore, programmers are bad!

THERE IS NOTHING INHERENTLY WRONG WITH GOTO. And how do I reconcile
with my comments above? Wait and see...

One important point in the structured programming argument is this:
every program has a control structure; some of these control
structures are better than others. Whether you use gotos or some other
language feature to implement the control structure does not change
what the control structure is, nor does it affect (much) the goodness
of the control structure.

The quality of your program is strongly influenced by the quality of
its control structures. Furthermore, you want that control structure
to be obvious and understandable to the reader of the program. This
implies that you use language features that make your use of a
control structure as obvious as possible.

So, the first question should be: what are the good control
structures?

The second question is then: given a particular language, which
control structures should be implemented? And how?

Ok, so what makes a control structure good? Well, the basic answers
are: a control structure is good if it is

    1) appropriate to solving programming problems.
    2) easy to write.
    3) easy to understand.
    4) easy to maintain.
    5) ... add your own as long as they do not contradict the above

There are obviously lots of control structures that meet these
requirements and you do not have to use all of them. In fact, you
should pick a set of those which are most appropriate for your
programming environment and use just those. This set should be, in
some sense, a minimum one; for example, if you have two control
structures which can accomplish the same thing, but one is easier to
use than the other (for you), pick the easier one and forget the
other. If you code a repeat forever loop as while (1), always do it
that way, don't switch to for(;;) every third tuesday. All other
things being equal, a smaller number of control structures helps make
your program easier to understand.

Now, I hope my claim about our C programs is understandable. But if
not, here is what it amounts to: we have chosen a set of control
structures which is appropriate to programming in C, for the kind of
programming tasks that we do. It happens that, while this set of
control structures includes multi-level breaks and continues (which
could be implemented with a goto), we have never had need to implement
one with a goto. Given the amount of code involved, it seems to me
that one might never have a good reason to use an explicit goto in C
code.

For other languages, one would come to a different conclusion. In
assembly language (unless you have a fairly powerful assembler),
*every* control structure is implemented with a goto. In COBOL, many
control structures are implemented with a goto. In Pascal, there are
just a few things that are best expressed with a goto. In most
varieties of Lisp, there is, from what I've seen, always a better way
than a goto.

There is, however, a reason to avoid naked gotos in your code: for
all other keywords, the control structure being implemented is
obvious. But for goto it isn't. One way to make the control structure
obvious is to properly name the target of the loop (e.g.,
end_of_main_traversal_loop). Another is to make the control structure
obvious by clothing the goto in some preprocessor magic. As an
example, suppose that you are using the state machine control
structure. One way to code it is:

	state = STATE_INIT;
	while (state != STATE_DONE) {
		switch (state) {
		case STATE_INIT:
		...
		}
	}

However, this is not the most efficient way to do it. If efficiency
were a concern (and you knew that you weren't going to deal with a
brain-damaged optimizer that won't optimize functions that have even
a single goto) you could also implement it as:

/* Wherever you see these macros being used, you will be seeing a
   state machine. The state macro defines the start of a state. The
   enter_state macro causes a transfer of control to another state of
   the same machine. The leave macro sends you to the end of the
   machine, the end_machine macro marks the place where the machine
   ends. */

#ifndef SDEBUG
#define state(m, label)       m##label:;
#define end_machine(m)        m##_finish:;
#else
#define state(m, label)                 printf("warning: falling though\n"); \
			      m##label: printf("entering state " #label \
					       " of machine " #m "\n");
#define end_machine(m)        m##_finish:printf("leaving machine " #m "\n");
#endif
#define enter_state(m, label) goto m##label
#define leave(m)              goto m##_finish

	state(input_interp, INIT)
		... code for the INIT state
		enter_state(input_interp, MORE);

	state(input_interp, MORE)
		... code for the MORE state
		if (nomore) {
			leave(input_interp);
		} else if (error) {
			enter_state(input_interp, RECOVER);
		}
		enter_state(input_interp, MORE);

	state(input_interp, RECOVER)
		... code for the RECOVER state
		enter_state(input_interp, FUDGE_INPUT);

	...

	end_machine(input_interp)

Some of you will no doubt be thinking: but why should I go to all
this effort when I could just use the goto directly? Well, if you do
only one kind of thing with goto, I don't really see any reason why
not (but I do think your program should include a comment saying what
control structure you have implemented using the goto). If, however,
you have more than one way of using goto, you should clothe the gotos
somehow so that the reader of the program knows what control
structures each of your gotos belongs to.

---
Bill                    { uunet | novavax | ankh | sunvice } !twwells!bill
bill@twwells.com

mcdonald@uxe.cso.uiuc.edu (09/15/89)

Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re:
Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re: Re:
Re: Goto Considered Harmful


I would consider a goto harmful IF and ONLY IF it produced a
bug in a program.

Doug McDonald

gwyn@smoke.BRL.MIL (Doug Gwyn) (09/17/89)

In article <225800222@uxe.cso.uiuc.edu> mcdonald@uxe.cso.uiuc.edu writes:
>I would consider a goto harmful IF and ONLY IF it produced a bug in a program.

The argument against use of GOTO is that it does lead to unreliable code.
I think this is indisputable for uncontrolled use of GOTO.
Some tightly-disciplined uses seem to me to not get in the way of
verifying code correctness.

jeenglis@nunki.usc.edu (Joe English) (09/17/89)

mcdonald@uxe.cso.uiuc.edu writes:
>I would consider a goto harmful IF and ONLY IF it produced a bug in a program.

How about a bunch of goto's that rendered the code
totally unreadable?  Have you seen the recent posting
of the permutation algorithm translated from Fortran?
That's what's considered harmful about it.

--Joe English

  jeenglis@nunki.usc.edu

ijk@cbnewsh.ATT.COM (ihor.j.kinal) (09/26/89)

In article <225800222@uxe.cso.uiuc.edu>, mcdonald@uxe.cso.uiuc.edu writes:
> 
> I would consider a goto harmful IF and ONLY IF it produced a
> bug in a program.
> 

Well, what if it took the NEXT poor programmer who maintains  that
program an extra week to understand what it does ???  Or even
an extra minute ??? [each and every time it was examined - it might
add up to a lot].

Suppose that the goto was bug-free, but increased the chance of
bugs in subsequent fixes/ changes to the program??  Even if the
bugs never actually occur?

I think we forget that in the software world, MOST of the effort
goes in maintaining code that was written by someone else.

Ihor Kinal
<include standard disclaimers.

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (09/26/89)

In article <4208@cbnewsh.ATT.COM>, ijk@cbnewsh.ATT.COM (ihor.j.kinal) writes:

|  Well, what if it took the NEXT poor programmer who maintains  that
|  program an extra week to understand what it does ???  Or even
|  an extra minute ??? [each and every time it was examined - it might
|  add up to a lot].

  It depends on what you use instead of the goto. If you compare a goto
against setting a flag variable and testing it to break out of three
levels of loop, the goto is certainly easier to understand, particularly
if the loops have a lot of code. If you use a goto where a break would
do it makes the code more subject to error (you can now mess up
placement of the goto or the label).
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

kim@kim.misemi (Kim Letkeman) (09/27/89)

In article <514@crdos1.crd.ge.COM> davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes:
>In article <4208@cbnewsh.ATT.COM>, ijk@cbnewsh.ATT.COM (ihor.j.kinal) writes:
>|  Well, what if it took the NEXT poor programmer who maintains  that
>|  program an extra week to understand what it does ???  Or even
>|  an extra minute ??? [each and every time it was examined - it might
>|  add up to a lot].
>  It depends on what you use instead of the goto. If you compare a goto
>against setting a flag variable and testing it to break out of three
>levels of loop, the goto is certainly easier to understand, particularly
                                                             ^^^^^^^^^^^^
>if the loops have a lot of code. If you use a goto where a break would
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>do it makes the code more subject to error (you can now mess up
>placement of the goto or the label).

If you have three levels of looping with "a lot of code" in them, then
a goto versus a flag is hardly your biggest problem.

If you keep your programs well structured, with *small* functions that
fan out smoothly from control level functions to low level (working)
functions, then this situation just doesn't arise. 

Only when you succumb to the temptation to add "just a little more"
functionality to an existing function without properly restructuring
it into its component functional units will you end up with rat's
nests that require gotos to handle control flow.

I apologize if this message has a pious tone. It is not intended to be
a preaching article, but this thread has been filled with attacks on
and defenses of gotos without really acknowledging the fact that a
goto is usually the result of too much complexity in the control flow.
The complexity arises (usually) when there isn't the time or
inclination to properly structure the code in question.

I have read and applied the concepts discussed within the book
Software Tools (to name perhaps the most famous of this ilk) and it
works. Code need not be ugly. It need not contain gotos (except in the
most extreme of circumstances.)

And optimization of that code by the compiler is a red herring if ever
there was one. Code that is badly structured enough to require a goto
already has macro algorithmic problems that can't be solved by a micro
optimizer. (And as most know, macro algorithm problems account for
orders of magnitude more execution time than the more micro compiler
optimization problems.)



-- 
Kim Letkeman    uunet!mitel!spock!kim

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (09/28/89)

In article <1017@kim.misemi>, kim@kim.misemi (Kim Letkeman) writes:

|  If you keep your programs well structured, with *small* functions that
|  fan out smoothly from control level functions to low level (working)
|  functions, then this situation just doesn't arise. 

  Size and structure are not exclusive. A switch within two for loops
seems like a reasonable structure to me, but it can contain a lot of
cases. If the code for each case is small, say 1-2 statements each,
using a table lookup and procedure per case can add a lot of overhead,
and encourages pollution of global namespace to make variables
available.

  All big procedures are not badly structured, and efforts to keep the
lines per procedure down to some arbitrary limit cost time to write, to
run, and to maintain.

|  I apologize if this message has a pious tone.

  It certainly could be read that way...
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

kim@kim.misemi (Kim Letkeman) (09/29/89)

In article <598@crdos1.crd.ge.COM> davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) writes:
> In article <1017@kim.misemi>, kim@kim.misemi (Kim Letkeman) writes:
> |  If you keep your programs well structured, with *small* functions that
> |  fan out smoothly from control level functions to low level (working)
> |  functions, then this situation just doesn't arise. 
>   Size and structure are not exclusive. A switch within two for loops
> seems like a reasonable structure to me, but it can contain a lot of
> cases. If the code for each case is small, say 1-2 statements each,
> using a table lookup and procedure per case can add a lot of overhead,
> and encourages pollution of global namespace to make variables
> available.

This is a fairly rare occurence in my experience. In a situation where
you have a couple of nested loops where the lowest level contains many
cases with only one or two lines each, a clean way in which to express
this is to separate the function being performed at the lowest level
into a separate procedure. (You can pass that procedure the data it
needs.) 

This leaves the higher level "control" procedure at a reasonable size
with its structure quite obvious. Essentially, this part of the goto
thread is about (IMO) software structure as it pertains to
readablility and maintainability. The optimization part of this thread
is a separate issue.

So, if we consider readability and maintainability as desirable
attributes (likely a general consensus), then anything that
contributes to that is also desirable.

Small procedures contribute.

>   All big procedures are not badly structured, and efforts to keep the
> lines per procedure down to some arbitrary limit cost time to write, to
> run, and to maintain.

True. A big procedure is not necessarily badly structured. But that
does not make it easy to read. In fact, a procedure that grows beyond
your immediate field of view (24 lines on terminals, more on listings
and workstations) is *automatically* harder to read since you have
less context within your immediate grasp.

Some procedures *must* grow larger than your normal field of view.
Granted. But this does not excuse someone who habitually codes 100
line functions or procedures. The circumstances in which a single
control procedure needs to be longer than say, 50 lines (which I
consider too large as it is) are quite rare.

If you examine almost any procedure of this size, you will see that it
performs functions at two levels of control. Or even worse, two
functions at the same level of control. Or really horrible, two
completely independent functions at the same level of control where a
switch passed in determines the action performed.

As a general principle, writing a single procedure to perform a single
function (task) is the whole point behind so called structured
programming.  Coupling and cohesion and all that.

So where does the goto fit after all this? It doesn't.

> |  I apologize if this message has a pious tone.
>   It certainly could be read that way...

True. But then so could anything written by someone with strongly held
beliefs. Especially if you disagree with that person's most basic
premises.
-- 
Kim Letkeman    uunet!mitel!spock!kim

jeenglis@nunki.usc.edu (Joe English) (09/30/89)

[ Sorry if this is a repost; I forgot to expand the 
  tabs and cancelled the original as quick as I could... ]

kim@kim.misemi (Kim Letkeman) writes:
>If you examine almost any procedure of [large] size, you will see that it
>performs functions at two levels of control. Or even worse, two
>functions at the same level of control. Or really horrible, two
>completely independent functions at the same level of control where a
>switch passed in determines the action performed.
>
>As a general principle, writing a single procedure to perform a single
>function (task) is the whole point behind so called structured
>programming.  Coupling and cohesion and all that.
>
>So where does the goto fit after all this? It doesn't.


What kind of control flow you use doesn't have
that much to do with coupling or cohesion; the
goto still has some valid uses.  For example:


void foo() {
    int k;
    initialization_sequence;

    while (k = get_input()) {
      switch (k) {
        case 'A' : a_action(); break;
        case 'B' : b_action(); break; 
        ...
        case 'Q' : q_termination_stuff(); goto done;
        case 'X' : x_termination_stuff(); goto done; 
      }
      end_loop_processing;
    }
done:
    cleanup_sequence;

    return;
}

This, I submit, is an example of a cohesive function
that uses a goto. If end_loop_processing and
cleanup_sequence are nontrivial, this can't be cleanly
done any other way.

This example wasn't just invented to prove a point; I
actually wrote something similar once.  (I shouldn't
have said that...  now half the net will probably
accuse me of dubious programming practices :-)

Another example of (I think) a valid use for the
goto is the multi-level exception handling case
posted a while back.

--Joe English

  jeenglis@nunki.usc.edu

kim@kim.misemi (Kim Letkeman) (10/02/89)

In article <5416@merlin.usc.edu> jeenglis@nunki.usc.edu (Joe English) writes:
>kim@kim.misemi (Kim Letkeman) writes:
>>[blah blah blah]
>>As a general principle, writing a single procedure to perform a single
>>function (task) is the whole point behind so called structured
>>programming.  Coupling and cohesion and all that.
>>
>>So where does the goto fit after all this? It doesn't.
>What kind of control flow you use doesn't have
>that much to do with coupling or cohesion; the
>goto still has some valid uses.  For example:

You got me. I took a chance and mixed two points in successive
paragraphs without making my point particularly clear. I was really
trying to say that I feel that structured programming concepts are
adequate for the vast majority of situations and that gotos are only
necessary in extreme cases. I apologize for the lack of clarity there.

>void foo() {
>    int k;
>    initialization_sequence;
>
>    while (k = get_input()) {
>	 switch (k) {
>	   case 'A' : a_action(); break;
>	   case 'B' : b_action(); break; 
>	   ...
>	   case 'Q' : q_termination_stuff(); goto done;
>	   case 'X' : x_termination_stuff(); goto done; 
>	 }
>	 end_loop_processing;
>    }
>done:
>    cleanup_sequence;
>
>    return;
>}
>This, I submit, is an example of a cohesive function
>that uses a goto. If end_loop_processing and
>cleanup_sequence are nontrivial, this can't be cleanly
>done any other way.

I agree that this is an example of a cohesive function if the
end_loop_processing and cleanup_sequence is fairly trivial. If not, it
is likely that they are performing fairly significant functions that
are not entirely related to the actual running of the pseudo state
machine. In that case, they would be better isolated in their own
procedures. This improves cohesion and makes this procedure more
readable. (Your example is quite readable as shown, but would
deteriorate with additional code.)

>This example wasn't just invented to prove a point; I
>actually wrote something similar once.  (I shouldn't
>have said that...  now half the net will probably
>accuse me of dubious programming practices :-)

Just one (very quickly conceived) alternative:

void foo() {
    int k;
    int done;

    initialization_sequence (some parms?);   /* function if non-trivial */
    done = 0;
    while (k = get_input()) {
	 switch (k) {
	   case 'A' : a_action(); break;
	   case 'B' : b_action(); break; 
	   ...
	   case 'Q' : q_termination_stuff(); done = 1;
	   case 'X' : x_termination_stuff(); done = 1; 
	 }
         if (done) break;
	 end_loop_processing (some parms?);  /* function if non-trivial */
    }
    cleanup_sequence (some parms?);          /* function if non-trivial */

    return;
}

This version looks as good to me and if the case statement gets long
it means you don't get your "oh no ... a goto" bell rung every time
you encounter a termination situation. To make it read better, I might
use a state variable with readable names in an enumeration, or at
least constants like TRUE and FALSE. But the point is that this case
is not (IMO) extreme enough to warrant a goto.

>Another example of (I think) a valid use for the
>goto is the multi-level exception handling case
>posted a while back.

I believe someone posted a version without a goto. It looked fine to
me. 

>--Joe English

-- 
Kim Letkeman    uunet!mitel!spock!kim

jlg@lanl.gov (Jim Giles) (10/03/89)

From article <1044@kim.misemi>, by kim@kim.misemi (Kim Letkeman):
> True. A big procedure is not necessarily badly structured. But that
> does not make it easy to read. In fact, a procedure that grows beyond
> your immediate field of view (24 lines on terminals, more on listings
> and workstations) is *automatically* harder to read since you have
> less context within your immediate grasp.

Splitting some codes into several different routines may also be
*automatically* harder to read.  There is a difference between
modularization and fragmentation.  The line between varies according
to the type of problem and the style of programming used.  I have
seen thousand line codes which were perfectly coherent.  I have also
seen 24 line code which should have been modularized.

kim@kim.misemi (Kim Letkeman) (10/03/89)

In article <14061@lanl.gov> jlg@lanl.gov (Jim Giles) writes:
>Path: mitel!uunet!cs.utexas.edu!tut.cis.ohio-state.edu!rutgers!cmcl2!lanl!jlg
>From: jlg@lanl.gov (Jim Giles)
>Newsgroups: comp.lang.c
>Date: 2 Oct 89 17:36:57 GMT
>References: <1044@kim.misemi>
>Organization: Los Alamos National Laboratory
>Lines: 13

>From article <1044@kim.misemi>, by kim@kim.misemi (Kim Letkeman):
>> True. A big procedure is not necessarily badly structured. But that
>> does not make it easy to read. In fact, a procedure that grows beyond
>> your immediate field of view (24 lines on terminals, more on listings
>> and workstations) is *automatically* harder to read since you have
>> less context within your immediate grasp.

>Splitting some codes into several different routines may also be
>*automatically* harder to read.  There is a difference between
>modularization and fragmentation.  The line between varies according
>to the type of problem and the style of programming used.  I have
>seen thousand line codes which were perfectly coherent.  I have also
>seen 24 line code which should have been modularized.

I think that I made it perfectly clear later on in that article that I
advocate structured coding that follows the rules of functional
cohesion. This precludes the very stupid practice of cutting up
cohesive functions just for the sake of modularization.

I find it hard to believe that you have seen a function that was 1,000
lines long and was still "perfectly coherent" (I assume that you are
using this as a euphomism for readable.) I do believe that you have
seen 24 line functions that could have been modularized.  Lots of
people just write that way.

This thread is getting a bit twisted by those who feel that one
pathological case can prove that gotos are ok or that long procedures
are unavoidable. I'd even start to believe this myself if it weren't
for the fact that most of the cases put forth were refuted by others
with code that appeared to do the job just as well without
unnecessarily breaking the rules of structured coding.


-- 
Kim Letkeman    uunet!mitel!spock!kim

peter@ficc.uu.net (Peter da Silva) (10/03/89)

The last time I looked at a stdio library there was a goto in _doprnt:

	if you see a %
	handle number.number stuff
	see what the format is

	switch(format) {
		case 'd':
			base = 10;
			if(number < 0) {
				sign = '-';
				number = -number;
			} else
				sign = 0;
			...
			goto donum;
		case 'u':
			base = 10;
			sign = 0;
			...
			goto donum;
		case 'o':
			base = 8;
			sign = 0;
			...
			goto donum;
		case 'x':
			base = 16;
			sign = 0;
			...
			goto donum;
		donum:
			format number with base, sign, etc.

(yes, I know this won't work for -32768. It's just an example)

You could probably rearrange this to not need the gotos, but I really
don't see that it would make the code any clearer. That's the problem.
Sometimes just leaving the goto in makes the code easier to read.
Really. A goto isn't the devil, it's just a seldom used ingredient.
Like blue food coloring... you don't use it often, but would you ban
it from the kitchen?
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
"That is not the Usenet tradition, but it's a solidly-entrenched            U
 delusion now." -- brian@ucsd.Edu (Brian Kantor)

bkottman@thor.wright.edu (Brett Kottmann) (10/05/89)

From article <6396@ficc.uu.net>, by peter@ficc.uu.net (Peter da Silva):
/ The last time I looked at a stdio library there was a goto in _doprnt:
/ 
/ 	if you see a %
/ 	handle number.number stuff
/ 	see what the format is
/ 
/ 	switch(format) {
/ 		case 'd':
/ 			base = 10;
/ 			if(number < 0) {
/ 				sign = '-';
/ 				number = -number;
/ 			} else
/ 				sign = 0;
/ 			...
/ 			goto donum;
/ 		case 'u':
/ 			base = 10;
/ 			sign = 0;
/ 			...
/ 			goto donum;
/ 		case 'o':
/ 			base = 8;
/ 			sign = 0;
/ 			...
/ 			goto donum;
/ 		case 'x':
/ 			base = 16;
/ 			sign = 0;
/ 			...
/ 			goto donum;
/ 		donum:
/ 			format number with base, sign, etc.
/

	This must be really old code since in C, execution falls through
to the next case anywasy; in every case without the goto, it would hit
donum: anyways...(albiet after trying the rest of the case statements)
	A break usually replaces the goto in that type of code.
 

cpcahil@virtech.UUCP (Conor P. Cahill) (10/05/89)

In article <725@thor.wright.EDU>, bkottman@thor.wright.edu (Brett Kottmann) writes:
> From article <6396@ficc.uu.net>, by peter@ficc.uu.net (Peter da Silva):
> / The last time I looked at a stdio library there was a goto in _doprnt:
> / 
> /     [sample switch with gotos deleted]
> / 

> 	This must be really old code since in C, execution falls through
> to the next case anywasy; in every case without the goto, it would hit
> donum: anyways...(albiet after trying the rest of the case statements)
> 	A break usually replaces the goto in that type of code.

The operations of a switch statement make a single comparison and jump
to the specified case (or default).  Once that jump has been made, there
is no further "trying the rest of the case statements".  without the 
listed gotos, the code would always act like the last case (base=16...).

Since the entir code sample was not presented, one cannot guess as to the
likelyhood that the goto's could easily be replaced with a break.


-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

gwyn@smoke.BRL.MIL (Doug Gwyn) (10/05/89)

In article <725@thor.wright.EDU> bkottman@thor.wright.edu writes:
>	This must be really old code since in C, execution falls through
>to the next case anywasy; in every case without the goto, it would hit
>donum: anyways...(albiet after trying the rest of the case statements)
>	A break usually replaces the goto in that type of code.

For the benefit of anyone who might have been misled by that response,
I should point out that bkottman completely misread the code.  Those
particular gotos could not have been omitted unless the code were
substantially rearranged, and the age of the code has nothing to do
with it.

peter@ficc.uu.net (Peter da Silva) (10/05/89)

Code deleted, see parent article.

In article <725@thor.wright.EDU>, bkottman@thor.wright.edu (Brett Kottmann) writes:
> 	This must be really old code since in C, execution falls through
> to the next case anywasy; in every case without the goto, it would hit
> donum: anyways...(albiet after trying the rest of the case statements)

Yes, but if you looked at what the code *does*, you would see that you
don't want to execute those statements.

> 	A break usually replaces the goto in that type of code.

In this case it wouldn't. Here's a goto-less version of the multiple
lead-in case that should have you all puking:

	switch(format_char) {
		case 'd': the stuff I put under d; if(0) {
		case 'o': the stuff I put under o; if(0) {
		case 'x': the stuff I put under x; if(0) {
		case 'u': the stuff I put under u; }}}
			the stuff I put under donum;
	}

I know it's not the best way to remove the gotos, but it's legal C
without a single "unstructured" element.

It's always possible to "remove" the gotos. It's not always worth it.
Structured programming is a discipline, not a game.
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
``I feel that any [environment] with users in it is "adverse".''           'U`
	-- Eric Peterson <lcc.eric@seas.ucla.edu>

bph@buengc.BU.EDU (Blair P. Houghton) (10/06/89)

In article <6430@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
>Code deleted, see parent article.
>Here's a goto-less version of the multiple
>lead-in case that should have you all puking:

Hwuawp!  Thanks, Pete.  Had the wrong thing for lunch, anyway... :-)

>	switch(format_char) {
>		case 'd': the stuff I put under d; if(0) {
>		case 'o': the stuff I put under o; if(0) {
>		case 'x': the stuff I put under x; if(0) {
>		case 'u': the stuff I put under u; }}}
>			the stuff I put under donum;
>	}
>
>I know it's not the best way to remove the gotos, but it's legal C
>without a single "unstructured" element.

I'd go out of my way to fix it so that I could just do

	switch(format_char) {
	    case 'd': the stuff I put under d; break;
	    case 'o': the stuff I put under o; break;
	    case 'x': the stuff I put under x; break;
	    case 'u': the stuff I put under u; break;
	}

	the stuff I put under donum;

Even if it meant (Gasp!) declaring a few variables outside the
switch and structuring the rest of my program in such a way
as to prevent needing such kludgery...

				--Blair
				  "At least they'd get
				   initialized properly..."

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/06/89)

In article <4447@buengc.BU.EDU>, bph@buengc.BU.EDU (Blair P. Houghton) writes:

|  I'd go out of my way to fix it so that I could just do
|  
|  	switch(format_char) {
|  	    case 'd': the stuff I put under d; break;
|  	    case 'o': the stuff I put under o; break;
|  	    case 'x': the stuff I put under x; break;
|  	    case 'u': the stuff I put under u; break;
|  	}
|  
|  	the stuff I put under donum;

  This doesn't do the same thing at all. In the original the "donum:"
code was only executed if a case was matched in the switch. Your example
executes it everytime. As you mentioned but didn't show you would have
to define a bunch of flags and stuff to make this work.
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

bph@buengc.BU.EDU (Blair P. Houghton) (10/07/89)

In article <867@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
>In article <4447@buengc.BU.EDU>, bph@buengc.BU.EDU (Blair P. Houghton) writes:
>
>|  I'd go out of my way to fix it so that I could just do
>|  
>|  	switch(format_char) {
>|  	    case 'd': the stuff I put under d; break;
>|  	    case 'o': the stuff I put under o; break;
>|  	    case 'x': the stuff I put under x; break;
>|  	    case 'u': the stuff I put under u; break;
>|  	}
>|  
>|  	the stuff I put under donum;
>
>  This doesn't do the same thing at all. In the original the "donum:"
>code was only executed if a case was matched in the switch. Your example

Quite so.

>executes it everytime. As you mentioned but didn't show you would have
>to define a bunch of flags and stuff to make this work.

Not really:

	switch(ch = format_char) {
		/* Switch body exactly as above */
	}

	switch(ch) {
	    case 'd':
	    case 'o':
	    case 'x':
	    case 'u':
		the stuff I put under donum;
		break;
	    default:
		break;
	}

I only did the extra assignment to give the switch body full use of
the variable format_char.  It may well be unnecessary in our hypothetical
example.

				--Blair
				  "One variable of indeterminate
				   necessity and an improvement
				   in the maintainability of the
				   do-for-all-flags code.
				   How many marks out of ten,
				   Dr. Structmember-Offset?"

dwho@nmtvax.nmt.edu (David Olix) (10/07/89)

In article <867@crdos1.crd.ge.COM> davidsen@crdos1.UUCP (bill davidsen) writes:
>
>  [...] In the original the "donum:"
>code was only executed if a case was matched in the switch. [...]

Would the following be too terribly unreadble?

case_matched = 1;
switch (format_char)
{
  case 'd':  the stuff I put under d; break;
  case 'o':  the stuff I put under o; break;
  case 'x':  the stuff I put under x; break;
  case 'u':  the stuff I put under u; break;
  default:  case_matched = 0;
}
if (case_matched)
  the stuff I put under donum;


I'm not really arguing whether or not to use goto's here; I'm arguing that
adding one flag (case_matched) does not a 'whole lot of flags' make.

--David Olix (dwho@nmtvax.nmt.edu)

bph@buengc.BU.EDU (Blair P. Houghton) (10/07/89)

Rich Salz pointed out (via email) that I'd made a mistake in
my rearrangement of the switch/goto construct currently under
debate.  I've come up with an idea I'd like to see rent asunder
by the people I know will do it best.  :-)

(BTW, I happen to think there's absolutely nothing wrong with goto's,
so long as you think in terms of structure and use the goto's only when
they appear perfectly apt.)

A correct functionality, as Rich showed it, without gotos:

        is_doxu = 0;
        switch(format_char) {
            case 'c': stuff; break;
            case 's': stuff; break;
            case 'd': the stuff I put under d; is_doxu++; break;
            case 'o': the stuff I put under o; is_doxu++; break;
            case 'x': the stuff I put under x; is_doxu++; break;
            case 'u': the stuff I put under u; is_doxu++; break;
        }

        if (is_doxu) {
            stuff
        }

This is probably version requiring least effort to maintain.  In order
to add or delete cases in the switch, you only have to copy any other
case and change the case-constant to fit the new case.  The way I
showed, you have to do it the same in both switches, so it's nominally
more difficult.

The way I'd probably do it if I had tried it from scratch is to
in-my-head expand the "is_doxu" logic and do

	switch(format_char) {
		case 'c': stuff under c; break;
		case 's': stuff under s; break;
		case 'd': stuff under d; break;
		case 'o': stuff under o; break;
		case 'x': stuff under x; break;
		case 'u': stuff under u; break;
        }

        if ( c == 'd' || c == 'o' || c == 'x' || c == 'u' ) {
		stuff for doxu cases
	}

Switch provides a method of conditionally splitting the flow-graph,
but there doesn't exist a dual of it that would describe the joining of
some subset of the split branches of the flow-graph.

Something like

	switch(fc) {
		case 'c': stuff under c; break;
		case 's': stuff under s; break;
		...
		case 'u': stuff under u; break;

		cases 'd', 'o', 'x', 'u': stuff for doxu cases; break;
		cases 'd', 'c', 'u': stuff for dcu cases; break;
	}

and let the compiler figure out how to keep track of the flow (which
obviously can get very very complicated very quickly) of the machine
language, which only knows from JUMP, JNZ, etc., anyway...

Notice that with gotos you could use maybe two or three common-cases,
and then only if you added a lot of extra control structures to track
the flow.  By adding the "cases" keyword, you have the compiler do it
in whatever way its graph-reduction and conflict-arbitration routines
decide is the best.

I haven't really considered order-of-evaluation.  This is a
high-level, low-baked concept at the moment.  The obvious method
is to just replace every "cases 'x','y'" with the equivalent of
"if ( (fc=='x') || (fc=='y') )".  This gets rid of the common
implementation that case-labels will be treated as goto-targets,
but that's an assumption, anyway.  Nothing stops a compiler from
just using a skein of "if...then...else-if...then...else-if...then...else"
constructs as a translation for the switch.

The alternative is to turn common code into a subroutine, and
insert its invocation immediately before the machine-code
construct that implements the break keyword in each branch.

Given that there are a number of programming tools being researched
that use a _graphical_ interface to code a program (i.e., you just
type a few logical statements into a box that looks like a flowchart
symbol and the computer writes the actual C code for you; create a
few arrows connecting boxes and you have flow-of-control) something
like "cases" would be very handy.  Then again, a the computer doing
the coding wouldn't have any compunction in using gotos all over the
place.

What we're doing here (in this discussion) is optimizing structurally,
for the benefit of programmers, while the compiler is obligated to
optimize (most often) for speed first and size second, and readability
of the flow structure is moot.

What we're also doing here (and now) is implementing this desired flow
construct using the current definition of the language.  There's
nothing impossible or ambiguous about using the "if (0) {...}"
pseudo-break to get the job done.  We'll all still get paid for
doing it.

				--Blair
				  "'Stop the Standard!  We have
				   a new keyword to add!' ...

				   :-)

				   You can come down off the
				   ceiling, now, Doug..."

P.S.  Exercise:  Show that allowing multiple "case 'x':" labels
for each constant 'x' would give exactly the results that the "cases" 
keyword provides...the meaning of "break" changes, however, from
"termination of the smallest enclosing while, do, for, or switch," to
"termination of the smallest enclosing while, do, or for; or, jump
to the next matching case-label in the smallest enclosing switch;
or, iff no further cases match, terminate the smallest enclosing switch..."

peter@ficc.uu.net (Peter da Silva) (10/07/89)

In article <4462@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
>	switch(ch = format_char) {
>		/* Switch body exactly as above */
>	}

>	switch(ch) {
		/* duplicate switch with just the donum code */
>	}

I'm not at all sure this is clearer than the goto-full version. You have taken
a single logical operation and split it in two parts. If you consider the goto
the work of the devil, then of course this represents progress. If you don't,
but consider it a rather esoteric tool to be used for unusual circumstances,
then it doesn't.

Consider that it's now less maintainable, since you have the same set of cases
included in two places. What happens if you put in a '%b' format? Now you
have two places to add the code for printing in binary.

Personally, I find gotos not a lot diffreent from switches and return
statements, and a lot less confusing than longjmp.
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
"Seems to me that posting an entire RFC in PostScript is like posting a    'U`
 Sun-3 binary to comp.sources.unix." -- sgrimm@sun.com (Steven Grimm)

peter@ficc.uu.net (Peter da Silva) (10/08/89)

In article <154@nmtvax.nmt.edu> dwho@nmtvax.UUCP (David Olix) writes:
>Would the following be too terribly unreadble?

[does "donum" code if the case didn't match]

In the original situation where you would have had a bunch of flags for
format string, format float, and so on? Yes.
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
"Seems to me that posting an entire RFC in PostScript is like posting a    'U`
 Sun-3 binary to comp.sources.unix." -- sgrimm@sun.com (Steven Grimm)

ggg@sunquest.UUCP (Guy Greenwald) (10/09/89)

The structure

	switch(c) {
		case 'd':
			...
			goto something;
		case 'u':
			...
			goto something;
		...:
			...
			goto something;
		something:	/* Where's the default? */
	}

can easily be replaced with

	switch(c) {
		case 'd':
			...
			something();
			break;
		case 'u':
			...
			something();
			break;
		...:
			...
			something();
			break;
		default:
			...
			break;
	}

with no loss of clarity. Other transformations are possible, too.
The fact that some standard library implementation uses it isn't
convincing, either. I had to write my own strpbrk for VAX C because
the standard library routine performed miserably for the long
strings my code was processing (10K+). Useful routines can be
poorly implemented.

The first implementation given above will execute slightly faster than
the second, but why is it "easier to read?" I can't see that the
goto's are really necessary. (something() could be implemented as a
macro if it needs to be faster.)

connelly@deimos.cis.ksu.edu (Paul Connelly) (10/10/89)

In article <4467@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:

-P.S.  Exercise:  Show that allowing multiple "case 'x':" labels
-for each constant 'x' would give exactly the results that the "cases" 
-keyword provides...the meaning of "break" changes, however, from
-"termination of the smallest enclosing while, do, for, or switch," to
-"termination of the smallest enclosing while, do, or for; or, jump
-to the next matching case-label in the smallest enclosing switch;
-or, iff no further cases match, terminate the smallest enclosing switch..."

I think "break" should take you out of whatever, less confusion that way,
but, since we're living in a dream world, let's use "continue" to jump
to the next matching case constant in the smallest enclosing switch. :-)

-- 
W. Paul Connelly                                     Dept. of Comp. & Info. Sci.
BITNET:   connelly@ksuvax1.bitnet                      Kansas State University
INTERNET: connelly@ksuvax1.cis.ksu.edu                   Manhattan, KS 66506
UUCP:  ...!{rutgers,texbell}!ksuvax1!connelly              (913) 532-6350

peter@ficc.uu.net (Peter da Silva) (10/10/89)

The referenced article discusses replacing the Goto with a subroutine call.
The question is raised... why would that reduce clarity?

In a block-structured language it wouldn't. In C, however, there is the
problem of local variables.

Really:

	case whatever:
		special-code...;
		goto common;

Is a lot clearer than:

	case whatever:
		special-code...;
		common(a, whole, bunch, of, random, arguments);
		break;

-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
                                                                           'U`
Quote: Structured Programming is a discipline -- not a straitjacket.

davidsen@crdos1.crd.ge.COM (Wm E Davidsen Jr) (10/10/89)

In article <154@nmtvax.nmt.edu>, dwho@nmtvax.nmt.edu (David Olix) writes:

	[ how to avoid goto with a flag ]

|  case_matched = 1;
|  switch (format_char)
|  {
|    case 'd':  the stuff I put under d; break;
|    case 'o':  the stuff I put under o; break;
|    case 'x':  the stuff I put under x; break;
|    case 'u':  the stuff I put under u; break;
|    default:  case_matched = 0;
|  }
|  if (case_matched)
|    the stuff I put under donum;

|  I'm not really arguing whether or not to use goto's here; I'm arguing that
|  adding one flag (case_matched) does not a 'whole lot of flags' make.

  I believe the original problem had a default case also, therefore not
allowing the detection of no case matched. The flag would have to be
cleared in each case. I *think* that's what I posted as an example of
how to do it without a goto, but my disk monitor had to do 1 massive
expire over the weekend and I don't have that stuff online.

  There is no question that anything can be done without a goto,
although I still feel that some of the things done in the quest of
avoiding gotos result in equally obscure code. When I first became
convinced that structured was the way to go (and about all we had in
those days was Algol-60), I did stuff like that. I'm more likely to
rethink and avoid both the flags and the goto these days, but I no
longer feel that a goto is a religious issue.

  Just a note: use of goto in net software.
	B news P/L 17, 91 gotos in 22k lines, one macro expands to goto
	rn 124 in 22k lines
	C news 3 in 28k lines

  I conclude that other people find goto useful once in a while, too.
-- 
bill davidsen	(davidsen@crdos1.crd.GE.COM -or- uunet!crdgw1!crdos1!davidsen)
"The world is filled with fools. They blindly follow their so-called
'reason' in the face of the church and common sense. Any fool can see
that the world is flat!" - anon

ggg@sunquest.UUCP (Guy Greenwald) (10/13/89)

In article <6490@ficc.uu.net|, peter@ficc.uu.net (Peter da Silva) writes:
| The referenced article discusses replacing the Goto with a subroutine call.
| The question is raised... why would that reduce clarity?
| 
| In a block-structured language it wouldn't. In C, however, there is the
| problem of local variables.
| 
| Really:
| 	case whatever:
| 		special-code...;
| 		goto common;
| 
| Is a lot clearer than:
| 	case whatever:
| 		special-code...;
| 		common(a, whole, bunch, of, random, arguments);
| 		break;
| -- 
| Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation
| Quote: Structured Programming is a discipline -- not a straitjacket.

If the arguments are really random, how can the function work? They
can't just be chosen at random. As to their number, it depends on how
many arguments the function needs. Regarding local variables, you know 
very well that C can have external variables. I think that no one is going
to win this argument against you because you're going to come up with
contrived rebuttals no matter what anyone says. If goto's have a place
in C, you haven't made a "case" for them.

/* Really: */
	case whatever:
		special-code...;
		goto common;
/* Is no clearer than: */
	case whatever:
		special-code...;
		common();
		break;

--G. Guy Greenwald II

jlg@lanl.gov (Jim Giles) (10/13/89)

From article <566@sunquest.UUCP>, by ggg@sunquest.UUCP (Guy Greenwald):
> /* Really: */
> 	case whatever:
> 		special-code...;
> 		goto common;
> /* Is no clearer than: */
  /* But it IS _MUCH_ more efficient than: */
> 	case whatever:
> 		special-code...;
> 		common();
> 		break;

Also, which one is clearer depends on the context.  I've seen a lot
of code in which the GOTO version is much clearer because it doesn't
separate the common code from the rest by a factor of several pages.
In any event, the GOTO may be imperative for the efficiency alone.
And you can't necessarily do better than the GOTO - even if you have
inline procedures.  Part of your efficiency requirement may be to
minimize the size of the code.

chris@mimsy.UUCP (Chris Torek) (10/13/89)

In many articles many people write preantepenultimate words on `goto'
when handling common code sequences with different leadins in switch
statements, specifically in a `printf' format interpreter.

If you all want to argue about something this pointless, you might as
well agree on the base code first, so here is _doprnt (in antepenultimate
form :-) i.e., not final, and untested).

/*
 * Copyright (c) 1988 Regents of the University of California.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of California, Berkeley.  The name of the
 * University may not be used to endorse or promote products derived
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "%W% (Berkeley) %G%";
#endif /* LIBC_SCCS and not lint */

#include <sys/types.h>
#include <stdio.h>
#include <varargs.h>
#include <ctype.h>

/*
 * Define FLOATING_POINT to get floating point.
 *
 * Define STDIO to get a stdio-specific version.  Otherwise this
 * just calls putc (and #undef's it so that it will get a real function).
 */

#define	STDIO
#define	FLOATING_POINT

#ifdef STDIO
#include "stdiom.h"
#else
#undef putc
#endif

#ifdef FLOATING_POINT
/* 11-bit exponent (VAX G floating point) is 308 decimal digits */
#define	MAXEXP		308
/* 128 bit fraction takes up 39 decimal digits; max reasonable precision */
#define	MAXFRACT	39

#define	DEFPREC		6

#define	BUF		(MAXEXP+MAXFRACT+1)	/* + decimal point */

static int cvt();
#else
#define	BUF		40
#endif

/* to extend shorts properly, we need signed and unsigned versions */
#define	SARG() \
	_ulong = flags&LONGINT ? va_arg(argp, long) : \
	    flags&SHORTINT ? (long)(short)va_arg(argp, int) : \
	    (long)va_arg(argp, int)
#define	UARG() \
	_ulong = flags&LONGINT ? va_arg(argp, u_long) : \
	    flags&SHORTINT ? (u_long)(u_short)va_arg(argp, int) : \
	    (u_long)va_arg(argp, u_int)

#define	todigit(c)	((c) - '0')
#define	tochar(n)	((n) + '0')

#define	LONGINT		0x01		/* long integer */
#define	LONGDBL		0x02		/* long double; unimplemented */
#define	SHORTINT	0x04		/* short integer */
#define	ALT		0x08		/* alternate form */
#define	LADJUST		0x10		/* left adjustment */
#define	ZEROPAD		0x20		/* zero (as opposed to blank) pad */
#define	HEXPREFIX	0x40		/* add 0x or 0X prefix */

_doprnt(fmt0, argp, fp)
	u_char *fmt0;
	va_list argp;
	register FILE *fp;
{
	register u_char *fmt;	/* format string */
	register int ch;	/* character from fmt */
	register int cnt;	/* return value accumulator */
	register int n;		/* random handy integer */
	register char *t;	/* buffer pointer */
#ifdef FLOATING_POINT
	double _double;		/* double precision arguments %[eEfgG] */
	int fpprec;		/* `extra' floating precision in [eEfgG] */
#endif
	u_long _ulong;		/* integer arguments %[diouxX] */
	int base;		/* base for [diouxX] conversion */
	int dprec;		/* decimal precision in [diouxX] */
	int fieldsz;		/* field size expanded by sign, etc */
	int flags;		/* flags as above */
	int prec;		/* precision from format (%.3d), or -1 */
	int realsz;		/* field size expanded by decimal precision */
	int size;		/* size of converted field or string */
	int width;		/* width from format (%8d), or 0 */
	char sign;		/* sign prefix (' ', '+', '-', or \0) */
	char softsign;		/* temporary negative sign for floats */
	char *digs;		/* digits for [diouxX] conversion */
	char buf[BUF];		/* space for %c, %[diouxX], %[eEfgG] */

#if defined(STDIO) && !defined(lint)
/* variant of putc that does NOT handle line buffered newlines */
#define	PUTC(c) \
	(--fp->_w >= 0 || fp->_flags&_IOLBF && fp->_w > fp->_bf._size ? \
		*fp->_p = c, fp->_p++, 0 : __putc((int)c, fp))

	/*
	 * Make sure we can write.  A side effect is that _IOLBF is set
	 * if it would get set by __putc.
	 */
	if (cantwrite(fp))
		return (EOF);
#else /* not STDIO or lint */
#define	PUTC(c) (void) putc(c, fp)
#endif

	fmt = fmt0;
	digs = "0123456789abcdef";
	for (cnt = 0;; ++fmt) {
#ifdef STDIO
		/*
		 * Copy characters until % or \0.
		 */
		n = fp->_w, t = (char *)fp->_p;
		if (fp->_flags & _IOLBF) {
			for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
				if (--n > fp->_bf._size && ch != '\n')
					*t++ = ch;
				else {
					fp->_w = n, fp->_p = (u_char *)t;
					(void) __putc(ch, fp);
					n = fp->_w, t = (char *)fp->_p;
				}
		} else {
			for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
				if (--n >= 0)
					*t++ = ch;
				else {
					fp->_w = n, fp->_p = (u_char *)t;
					(void) __putc(ch, fp);
					n = fp->_w, t = (char *)fp->_p;
				}
		}
		fp->_w = n, fp->_p = (u_char *)t;
#else /* STDIO */
		for (; (ch = *fmt) && ch != '%'; ++cnt, ++fmt)
			(void) putc(ch, fp);
#endif /* STDIO */
		if (!ch)
			return (cnt);

		flags = 0;
		dprec = 0;
#ifdef FLOATING_POINT
		fpprec = 0;
#endif
		width = 0;
		prec = -1;
		sign = '\0';

rflag:		switch (*++fmt) {
		case ' ':
			/*
			 * ``If the space and + flags both appear, the space
			 * flag will be ignored.''
			 *	-- ANSI X3J11
			 */
			if (!sign)
				sign = ' ';
			goto rflag;
		case '#':
			flags |= ALT;
			goto rflag;
		case '*':
			/*
			 * ``A negative field width argument is taken as a
			 * - flag followed by a positive field width.''
			 *	-- ANSI X3J11
			 * They don't exclude field widths read from args.
			 */
			if ((width = va_arg(argp, int)) >= 0)
				goto rflag;
			width = -width;
			/* FALLTHROUGH */
		case '-':
			flags |= LADJUST;
			goto rflag;
		case '+':
			sign = '+';
			goto rflag;
		case '.':
			if (*++fmt == '*')
				n = va_arg(argp, int);
			else {
				n = 0;
				while (isascii(*fmt) && isdigit(*fmt))
					n = 10 * n + todigit(*fmt++);
				--fmt;
			}
			prec = n < 0 ? -1 : n;
			goto rflag;
		case '0':
			/*
			 * ``Note that 0 is taken as a flag, not as the
			 * beginning of a field width.''
			 *	-- ANSI X3J11
			 */
			flags |= ZEROPAD;
			goto rflag;
		case '1': case '2': case '3': case '4':
		case '5': case '6': case '7': case '8': case '9':
			n = 0;
			do {
				n = 10 * n + todigit(*fmt);
			} while (isascii(*++fmt) && isdigit(*fmt));
			width = n;
			--fmt;
			goto rflag;
#ifdef FLOATING_POINT
		case 'L':
			flags |= LONGDBL;
			goto rflag;
#endif
		case 'h':
			flags |= SHORTINT;
			goto rflag;
		case 'l':
			flags |= LONGINT;
			goto rflag;
		case 'c':
			*(t = buf) = va_arg(argp, int);
			size = 1;
			sign = '\0';
			break;
		case 'D':
			flags |= LONGINT;
			/*FALLTHROUGH*/
		case 'd':
		case 'i':
			SARG();
			if ((long)_ulong < 0) {
				_ulong = -_ulong;
				sign = '-';
			}
			base = 10;
			goto number;
#ifdef FLOATING_POINT
		case 'e':
		case 'E':
		case 'f':
		case 'g':
		case 'G':
			_double = va_arg(argp, double);
			/*
			 * don't do unrealistic precision; just pad it with
			 * zeroes later, so buffer size stays rational.
			 */
			if (prec > MAXFRACT) {
				if (*fmt != 'g' && *fmt != 'G' || (flags&ALT))
					fpprec = prec - MAXFRACT;
				prec = MAXFRACT;
			} else if (prec == -1)
				prec = DEFPREC;
			/*
			 * softsign avoids negative 0 if _double is < 0 and
			 * no significant digits will be shown
			 */
			if (_double < 0) {
				softsign = '-';
				_double = -_double;
			} else
				softsign = 0;
			/*
			 * cvt may have to round up past the "start" of the
			 * buffer, i.e. ``intf("%.2f", (double)9.999);'';
			 * if the first char isn't NULL, it did.
			 */
			*buf = NULL;
			size = cvt(_double, prec, flags, &softsign, *fmt, buf,
			    buf + sizeof(buf));
			if (softsign)
				sign = '-';
			t = *buf ? buf : buf + 1;
			break;
#endif /* FLOATING_POINT */
		case 'n':
			if (flags & LONGINT)
				*va_arg(argp, long *) = cnt;
			else if (flags & SHORTINT)
				*va_arg(argp, short *) = cnt;
			else
				*va_arg(argp, int *) = cnt;
			continue;	/* no output */
		case 'O':
			flags |= LONGINT;
			/*FALLTHROUGH*/
		case 'o':
			UARG();
			base = 8;
			goto nosign;
		case 'p':
			/*
			 * ``The argument shall be a pointer to void.  The
			 * value of the pointer is converted to a sequence
			 * of printable characters, in an implementation-
			 * defined manner.''
			 *	-- ANSI X3J11
			 */
			/* NOSTRICT */
			_ulong = (u_long)va_arg(argp, void *);
			base = 16;
			goto nosign;
		case 's':
			if (!(t = va_arg(argp, char *)))
				t = "(null)";
			if (prec >= 0) {
				/*
				 * can't use strlen; can only look for the
				 * NUL in the first `prec' characters, and
				 * strlen() will go further.
				 */
				char *p, *memchr();

				if (p = memchr(t, 0, prec)) {
					size = p - t;
					if (size > prec)
						size = prec;
				} else
					size = prec;
			} else
				size = strlen(t);
			sign = '\0';
			break;
		case 'U':
			flags |= LONGINT;
			/*FALLTHROUGH*/
		case 'u':
			UARG();
			base = 10;
			goto nosign;
		case 'X':
			digs = "0123456789ABCDEF";
			/* FALLTHROUGH */
		case 'x':
			UARG();
			base = 16;
			/* leading 0x/X only if non-zero */
			if (flags & ALT && _ulong != 0)
				flags |= HEXPREFIX;

			/* unsigned conversions */
nosign:			sign = '\0';
			/*
			 * ``... diouXx conversions ... if a precision is
			 * specified, the 0 flag will be ignored.''
			 *	-- ANSI X3J11
			 */
number:			if ((dprec = prec) >= 0)
				flags &= ~ZEROPAD;

			/*
			 * ``The result of converting a zero value with an
			 * explicit precision of zero is no characters.''
			 *	-- ANSI X3J11
			 */
			t = buf + BUF;
			if (_ulong != 0 || prec != 0) {
				do {
					*--t = digs[_ulong % base];
					_ulong /= base;
				} while (_ulong);
				digs = "0123456789abcdef";
				if (flags & ALT && base == 8 && *t != '0')
					*--t = '0'; /* octal leading 0 */
			}
			size = buf + BUF - t;
			break;
		case '\0':	/* "%?" prints ?, unless ? is NUL */
			return (cnt);
		default:
			(void) putc((int)*fmt, fp);
			cnt++;
			continue;	/* no other output */
		}

		/*
		 * All reasonable formats wind up here.  At this point,
		 * `t' points to a string which (if not flags&LADJUST)
		 * should be padded out to `width' places.  If
		 * flags&ZEROPAD, it should first be prefixed by any
		 * sign or other prefix; otherwise, it should be blank
		 * padded before the prefix is emitted.  After any
		 * left-hand padding and prefixing, emit zeroes
		 * required by a decimal [diouxX] precision, then print
		 * the string proper, then emit zeroes required by any
		 * leftover floating precision; finally, if LADJUST,
		 * pad with blanks.
		 */

		/*
		 * compute actual size, so we know how much to pad
		 * fieldsz excludes decimal prec; realsz includes it
		 */
#ifdef FLOATING_POINT
		fieldsz = size + fpprec;
#else
		fieldsz = size;
#endif
		if (sign)
			fieldsz++;
		if (flags & HEXPREFIX)
			fieldsz += 2;
		realsz = dprec > fieldsz ? dprec : fieldsz;

		/* right-adjusting blank padding */
		if ((flags & (LADJUST|ZEROPAD)) == 0 && width)
			for (n = realsz; n < width; n++)
				PUTC(' ');
		/* prefix */
		if (sign)
			PUTC(sign);
		if (flags & HEXPREFIX) {
			PUTC('0');
			PUTC((int)*fmt);
		}
		/* right-adjusting zero padding */
		if ((flags & (LADJUST|ZEROPAD)) == ZEROPAD)
			for (n = realsz; n < width; n++)
				PUTC('0');
		/* leading zeroes from decimal precision */
		for (n = fieldsz; n < dprec; n++)
			PUTC('0');

		/* the string or number proper */
		n = size;
#ifdef STDIO
		if (fp->_flags & _IOLBF)
			while (--n >= 0) {
				ch = *t++;
				if (--fp->_w > fp->_bf._size && ch != '\n')
					*fp->_p++ = ch;
				else
					(void) __putc(ch, fp);
			}
		else
			while (n) {
				if ((ch = fp->_w) > n)
					ch = n;
				if (ch) {
					bcopy((void *)fp->_p, (void *)t, ch);
					fp->_w -= ch;
					fp->_p += ch;
					n -= ch;
				} else {
					(void) __putc(*t++, fp);
					n--;
				}
			}
#else /* STDIO */
		while (--n >= 0)
			(void) putc(*t++, fp);
#endif /* STDIO */

#ifdef FLOATING_POINT
		/* trailing f.p. zeroes */
		while (--fpprec >= 0)
			PUTC('0');
#endif
		/* left-adjusting padding (always blank) */
		if (flags & LADJUST)
			for (n = realsz; n < width; n++)
				PUTC(' ');
		/* finally, adjust cnt */
		cnt += width > realsz ? width : realsz;
	}
	/* NOTREACHED */
}

#ifdef FLOATING_POINT
static char *exponent();
static char *round();

static
cvt(number, prec, flags, signp, fmtch, startp, endp)
	double number;
	register int prec;
	int flags;
	u_char fmtch;
	char *signp, *startp, *endp;
{
	register char *p, *t;
	register double fract;
	int dotrim, expcnt, gformat;
	double integer, tmp, modf();

	dotrim = expcnt = gformat = 0;
	fract = modf(number, &integer);

	/* get an extra slot for rounding. */
	t = ++startp;

	/*
	 * get integer portion of number; put into the end of the buffer; the
	 * .01 is added for modf(356.0 / 10, &integer) returning .59999999...
	 */
	for (p = endp - 1; integer; ++expcnt) {
		tmp = modf(integer / 10, &integer);
		*p-- = tochar((int)((tmp + .01) * 10));
	}
	switch(fmtch) {
	case 'f':
		/* reverse integer into beginning of buffer */
		if (expcnt)
			for (; ++p < endp; *t++ = *p);
		else
			*t++ = '0';
		/*
		 * if precision required or alternate flag set, add in a
		 * decimal point.
		 */
		if (prec || flags&ALT)
			*t++ = '.';
		/* if requires more precision and some fraction left */
		if (fract) {
			if (prec)
				do {
					fract = modf(fract * 10, &tmp);
					*t++ = tochar((int)tmp);
				} while (--prec && fract);
			if (fract)
				startp = round(fract, (int *)NULL, startp,
				    t - 1, (char)0, signp);
		}
		for (; prec--; *t++ = '0');
		break;
	case 'e':
	case 'E':
eformat:	if (expcnt) {
			*t++ = *++p;
			if (prec || flags&ALT)
				*t++ = '.';
			/* if requires more precision and some integer left */
			for (; prec && ++p < endp; --prec)
				*t++ = *p;
			/*
			 * if done precision and more of the integer component,
			 * round using it; adjust fract so we don't re-round
			 * later.
			 */
			if (!prec && ++p < endp) {
				fract = 0;
				startp = round((double)0, &expcnt, startp,
				    t - 1, *p, signp);
			}
			/* adjust expcnt for digit in front of decimal */
			--expcnt;
		}
		/* until first fractional digit, decrement exponent */
		else if (fract) {
			/* adjust expcnt for digit in front of decimal */
			for (expcnt = -1;; --expcnt) {
				fract = modf(fract * 10, &tmp);
				if (tmp)
					break;
			}
			*t++ = tochar((int)tmp);
			if (prec || flags&ALT)
				*t++ = '.';
		}
		else {
			*t++ = '0';
			if (prec || flags&ALT)
				*t++ = '.';
		}
		/* if requires more precision and some fraction left */
		if (fract) {
			if (prec)
				do {
					fract = modf(fract * 10, &tmp);
					*t++ = tochar((int)tmp);
				} while (--prec && fract);
			if (fract)
				startp = round(fract, &expcnt, startp,
				    t - 1, (char)0, signp);
		}
		/* if requires more precision */
		for (; prec--; *t++ = '0');

		/* unless alternate flag, trim any g/G format trailing 0's */
		if (gformat && !(flags&ALT)) {
			while (t > startp && *--t == '0');
			if (*t == '.')
				--t;
			++t;
		}
		t = exponent(t, expcnt, fmtch);
		break;
	case 'g':
	case 'G':
		/* a precision of 0 is treated as a precision of 1. */
		if (!prec)
			++prec;
		/*
		 * ``The style used depends on the value converted; style e
		 * will be used only if the exponent resulting from the
		 * conversion is less than -4 or greater than the precision.''
		 *	-- ANSI X3J11
		 */
		if (expcnt > prec || !expcnt && fract && fract < .0001) {
			/*
			 * g/G format counts "significant digits, not digits of
			 * precision; for the e/E format, this just causes an
			 * off-by-one problem, i.e. g/G considers the digit
			 * before the decimal point significant and e/E doesn't
			 * count it as precision.
			 */
			--prec;
			fmtch -= 2;		/* G->E, g->e */
			gformat = 1;
			goto eformat;
		}
		/*
		 * reverse integer into beginning of buffer,
		 * note, decrement precision
		 */
		if (expcnt)
			for (; ++p < endp; *t++ = *p, --prec);
		else
			*t++ = '0';
		/*
		 * if precision required or alternate flag set, add in a
		 * decimal point.  If no digits yet, add in leading 0.
		 */
		if (prec || flags&ALT) {
			dotrim = 1;
			*t++ = '.';
		}
		else
			dotrim = 0;
		/* if requires more precision and some fraction left */
		if (fract) {
			if (prec) {
				do {
					fract = modf(fract * 10, &tmp);
					*t++ = tochar((int)tmp);
				} while(!tmp);
				while (--prec && fract) {
					fract = modf(fract * 10, &tmp);
					*t++ = tochar((int)tmp);
				}
			}
			if (fract)
				startp = round(fract, (int *)NULL, startp,
				    t - 1, (char)0, signp);
		}
		/* alternate format, adds 0's for precision, else trim 0's */
		if (flags&ALT)
			for (; prec--; *t++ = '0');
		else if (dotrim) {
			while (t > startp && *--t == '0');
			if (*t != '.')
				++t;
		}
	}
	return(t - startp);
}

static char *
round(fract, exp, start, end, ch, signp)
	double fract;
	int *exp;
	register char *start, *end;
	char ch, *signp;
{
	double tmp;

	if (fract)
	(void)modf(fract * 10, &tmp);
	else
		tmp = todigit(ch);
	if (tmp > 4)
		for (;; --end) {
			if (*end == '.')
				--end;
			if (++*end <= '9')
				break;
			*end = '0';
			if (end == start) {
				if (exp) {	/* e/E; increment exponent */
					*end = '1';
					++*exp;
				}
				else {		/* f; add extra digit */
				*--end = '1';
				--start;
				}
				break;
			}
		}
	/* ``"%.3f", (double)-0.0004'' gives you a negative 0. */
	else if (*signp == '-')
		for (;; --end) {
			if (*end == '.')
				--end;
			if (*end != '0')
				break;
			if (end == start)
				*signp = 0;
		}
	return(start);
}

static char *
exponent(p, exp, fmtch)
	register char *p;
	register int exp;
	u_char fmtch;
{
	register char *t;
	char expbuf[MAXEXP];

	*p++ = fmtch;
	if (exp < 0) {
		exp = -exp;
		*p++ = '-';
	}
	else
		*p++ = '+';
	t = expbuf + MAXEXP;
	if (exp > 9) {
		do {
			*--t = tochar(exp % 10);
		} while ((exp /= 10) > 9);
		*--t = tochar(exp);
		for (; t < expbuf + MAXEXP; *p++ = *t++);
	}
	else {
		*p++ = '0';
		*p++ = tochar(exp);
	}
	return(p);
}
#endif /* FLOATING_POINT */
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

dhesi@sunscreen.UUCP (Rahul Dhesi) (10/13/89)

In article <566@sunquest.UUCP> ggg@sunquest.UUCP (Guy Greenwald) writes:
>/* Really: */
>	case whatever:
>		special-code...;
>		goto common;
>/* Is no clearer than: */
>	case whatever:
>		special-code...;
>		common();
>		break;

Possible counter-example:

   int counterexample(...)
   {
     ... other stuff ...

     switch (yyy) {
       case x:
          ... other stuff ...
	  goto err_ret;
       case y:
	  ... other stuff ...
	  goto err_ret;
       case other-stuff:
	  ... other stuff ...
	  break;
	  ...
       }

       return 0;		/* no need to close file;  return success */

     common:
       errno = last_error;/* save latest error for caller's sake */
       if (last_error != CE_NOFILE)
         fclose(f);		/* close file if still open */
       return -1;		/* return error code */
   } /* counterexample() */

Rahul Dhesi <dhesi%cirrusl@oliveb.ATC.olivetti.com>
UUCP:  oliveb!cirrusl!dhesi

cik@l.cc.purdue.edu (Herman Rubin) (10/13/89)

In article <566@sunquest.UUCP>, ggg@sunquest.UUCP (Guy Greenwald) writes:
< In article <6490@ficc.uu.net|, peter@ficc.uu.net (Peter da Silva) writes:

			.....................
	
< | Really:
< | 	case whatever:
< | 		special-code...;
< | 		goto common;
< | 
< | Is a lot clearer than:
< | 	case whatever:
< | 		special-code...;
< | 		common(a, whole, bunch, of, random, arguments);
< | 		break;
< | -- 
< | Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation
< | Quote: Structured Programming is a discipline -- not a straitjacket.
> 
> If the arguments are really random, how can the function work? They
> can't just be chosen at random. As to their number, it depends on how
> many arguments the function needs. Regarding local variables, you know 
> very well that C can have external variables. I think that no one is going
> to win this argument against you because you're going to come up with
> contrived rebuttals no matter what anyone says. If goto's have a place
> in C, you haven't made a "case" for them.
> 
> /* Really: */
> 	case whatever:
> 		special-code...;
> 		goto common;
> /* Is no clearer than: */
> 	case whatever:
> 		special-code...;
> 		common();
> 		break;

One problem with the function call is that, as da Silva writes, there can
be a lot of other variables being used.  It can be quite difficult to manage
the implicit passage of arguments that Greenwald is assuming away.

There are other problems.  The cost of a subroutine call is necessarily
considerably greater than that of a transfer.  Furthermore, the first block
of code indicates that, at the termination of common, the program continues
from that point and does not do a return to the place from which it is called.
I find this rather a common situation, and have not seen an alternative to
the goto for this situation which is not horribly expensive and less clear.
Also, at the continuation, the "whole bunch of random arguments" is still
there for the rest of the program to use (or use as modified by common).
This is not provided for by a call.
-- 
Herman Rubin, Dept. of Statistics, Purdue Univ., West Lafayette IN47907
Phone: (317)494-6054
hrubin@l.cc.purdue.edu (Internet, bitnet, UUCP)

peter@ficc.uu.net (Peter da Silva) (10/13/89)

I said, in an example, "common(a, whole, bunch, of, random, arguments);"...

In article <566@sunquest.UUCP> ggg@sunquest.UUCP (Guy Greenwald) writes:
> If the arguments are really random, how can the function work?

[ A whole lot of pedantic stuff ]

> Regarding local variables, you know 
> very well that C can have external variables.

I can be pedantic too.

Local variables, properly used, add considerably to the clarity and
maintainability of a program. Data hiding is acknowledged as a good
thing, because it increases the locality of the information required
to understand a block of code, and ensures that unsuspected back doors
are not created by careless programmers. In fact, one of the main
reasons for following the discipline of structured programming is to
improve this locality, and hide control structures from each other.

If on the one hand you improve the structure of control flow, by eliminating
gotos, and on the other degrade the structure of your data, by adding a
bunch of global (or even file-local) variables, what have you really
gained?

>  I think that no one is going
> to win this argument against you because you're going to come up with
> contrived rebuttals no matter what anyone says.

Well would you care to address the merits of my argument rather than either
coming up with contrived examples of your own or by blindly restating
religious doctrine?

To rebut your rebuttal of my contrived example, what about re-entrant
routines?
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
                                                                           'U`
Quote: Structured Programming is a discipline -- not a straitjacket.

peter@ficc.uu.net (Peter da Silva) (10/15/89)

Well, that's not the version of _doprnt I remember.
-- 
Peter da Silva, *NIX support guy @ Ferranti International Controls Corporation.
Biz: peter@ficc.uu.net, +1 713 274 5180. Fun: peter@sugar.hackercorp.com. `-_-'
                                                                           'U`
Quote: Structured Programming is a discipline -- not a straitjacket.

greyfox@agtoa.UUCP (Bruce Ide) (10/24/89)

Basic programmers use GOTO's excessively. I usually never use any, but if
you are nested four loops deep and need to get all the way out, you can
either use a GOTO or set a condition and test it four times. In cases such
as this, the GOTO is the Logical Choice. The examples I have seen here:

>case 1:
>     command();
>     break;

as opposed to

>case 1:
>     goto command;
>     break;

I wouldn't use a GOTO here. Putting a goto here would be something a BASIC
programmer would do. When your program gets > 20K or so, keeping track of
global variables and GOTO's is a MAJOR pain. You'll know that when the guy
who has to maintain it after you're gone comes after you with a shot gun.
(I like local arrays of structures and pointers to functions too. Keeps the
code portable :-)

              -Bruce Ide (Grey Fox)   uunet!agtoa!greyfox