[comp.sys.mac.programmer] LSC 3.0 scanf deletes

phd@speech1.cs.cmu.edu (Paul Dietz) (08/03/88)

I'm tired of fighting this one. Does anybody know how to delete
characters when you make a mistake typing during an LSC scanf?
Any suggestions would be appreciated. Thanks!

Paul H. Dietz                                        ____          ____
Dept. of Electrical and Computer Engineering        / oo \        <_<\\\
Carnegie Mellon University                        /|  \/  |\        \\ \\
--------------------------------------------     | | (  ) | |       | ||\\
"If God had meant for penguins to fly,             -->--<--        / / |\\\  /
he would have given them wings."            _________^__^_________/ / / \\\\-

beard@ux1.lbl.gov (Patrick C Beard) (08/03/88)

In article <2559@pt.cs.cmu.edu> phd@speech1.cs.cmu.edu (Paul Dietz) writes:
>I'm tired of fighting this one. Does anybody know how to delete
>characters when you make a mistake typing during an LSC scanf?
>Any suggestions would be appreciated. Thanks!
>
>Paul H. Dietz                                        ____          ____

The answer to your question is, DON'T USE SCANF!  I have always hated
the way scanf treats the user who assumes he can edit his command line.
A better approach to getting lines of input and parsing them from the user
is to use gets() and then sscanf().  For example:

Instead of:

float foo()
{
	float in_value;

	printf("enter a floating point value:  ");
	scanf("%f",&in_value);	/* yuck, user can't edit his input! */
	return(in_value);
}

use:

float foo()
{
	float in_value;
	char buf[80];	/* overconservative */

	printf("enter ... ");
	gets(buf);	/* user can use backspace to edit */
	sscanf(buf,"%f",&in_value);
	return(in_value);
}

I hope this helps.

+=============================================================+
|                    Patrick C. Beard                         |
|              Lawrence Berkeley Laboratory                   |
|               Automated Supernova Search                    |
+-------------------------------------------------------------+
|              PCBeard@LBL.gov (arpa only)                    |
+=============================================================+

suitti@haddock.ISC.COM (Steve Uitti) (08/04/88)

In article <587@helios.ee.lbl.gov> beard@ux1.lbl.gov (Patrick C Beard) writes:
>In article <2559@pt.cs.cmu.edu> phd@speech1.cs.cmu.edu (Paul Dietz) writes:
>>I'm tired of fighting this one. Does anybody know how to delete
>>characters when you make a mistake typing during an LSC scanf?
>>Paul H. Dietz
>
>The answer to your question is, DON'T USE SCANF!  I have always hated
>the way scanf treats the user who assumes he can edit his command line.
	Under UNIX, the user typically can edit the command line.  The
Mac interface doesn't really have a command line, so scanf must
implement one.

>A better approach to getting lines of input and parsing them from the user
>is to use gets() and then sscanf().  For example:
>float foo()
>{
>	float in_value;
>	char buf[80];	/* overconservative */
>
>	printf("enter ... ");
>	gets(buf);	/* user can use backspace to edit */
>	sscanf(buf,"%f",&in_value);
>	return(in_value);
>}

	Under UNIX, scanf lets you edit stuff.  Trouble is, if (for
example) input is redirected from a file, and the pattern scanf wanted
didn't exist in the file, it would infinite loop.  This is bad.  One
more step - "gets" doesn't do bounds checking.

	Most C compilers still do all floating point with doubles.
For exmple, most compilers still actually convert floats to doubles when
passing values to/from subroutines.  I don't know offhand if LSC does this.

#include <stdio.h>		/* define stdin */
#define BS 80			/* often good enough */
double
foo()
{
	double in_value;
	char buf[BS];

	printf("enter ... ");
	if (fgets(buf, BS, stdin) == NULL) {
		/* handle the EOF error */
	}
	if (sscanf(buf, "%lf", &in_value) != 1) {
		/* handle the parsing error - expected one conversion */
	}
	return in_value;
}

	Programs should be written such that they can't over run
buffers.  When (not if) errors happen, they should be handled
gracefully.  All of them.  Another of the problems with sscanf (the
only remotely useful function in the series) is that it doesn't tell
you much about why it couldn't do something (if it even tells you that
it couldn't do something).  Thus, not only do I never use "scanf" or
"fscanf", I seldom use "sscanf".

	The LSC standard C library isn't the most robust.  It does
"seem to work" for most things, and the "console" window is utterly
painless.  The compiler itself does seem to be robust, produces code
that runs pretty quickly, and produces it VERY quickly.  If I were to
write Mac applications, there wouldn't be a second choice development
environment.  (A fully blown Mac II with two monitors and LSC 3.0 is
really hard to beat).

	Stephen Uitti

strasser@murdu.OZ (Mike Strasser) (08/04/88)

In article <587@helios.ee.lbl.gov> beard@ux1.lbl.gov (Patrick C Beard) writes:

[quote from Paul Dietz]

>The answer to your question is, DON'T USE SCANF!  I have always hated
>the way scanf treats the user who assumes he can edit his command line.
>A better approach to getting lines of input and parsing them from the user
>is to use gets() and then sscanf().  For example:

[etc.]

The problem with doing your own buffering via gets(), strings and
sscanf() is when you want a flexible interface where a user can type
more than one item on a line.  You then need to parse the line yourself
(when scanf() on an input stream will do that adequately) or find a way
of knowing how much of the string has been parsed by sscanf(), so second
and subsequent calls will not start at the beginning of the string again.

---------------------------------------------------------------------------
Mike Strasser                ACSnet, CSnet:  strasser@murdu.oz
                             Internet:       strasser%murdu.oz@uunet.uu.net
Forestry Section
University of Melbourne
Creswick, Victoria.  3363    Phone: (053) 45 2405
A u s t r a l i a                   +61 53 45 2405
---------------------------------------------------------------------------

swilson%thetone@Sun.COM (Scott Wilson) (08/04/88)

scanf is not a useless function!  While I believe that to approach 100%
bullet-proof i/o you have to do everything yourself one character at a
time, scanf is still useful.  The problem is that most people don't
know how to use scanf.  Consider this example:

#include <stdio.h>

main()
{
    int i;

    do {
        printf("Enter a number between 1 and 100:  ");
        scanf("%d", &i);
    } while (i < 1 || i > 100);
    printf("You typed: %d\n", i);
}

This is a common problem when using scanf.  The user types a letter,
scanf pushes it back and the program goes into an infinite loop
(assuming the garbage value of an uninitialzed i is out of range).  The
solution:  don't use scanf?  Not necessarily.  Scanf returns a value
to help out in this situation.  From the Sun0S 4.0 scanf man page:

     scanf() returns  the  number  of  successfully  matched  and
     assigned  input  items; this number can be zero in the event
     of an early conflict between an input character and the con-
     trol  string.   The  constant  EOF  is  returned upon end of
     input. Note: this is different from 0, which means  that  no
     conversion  was  done;  if  conversion  was intended, it was
     frustrated by an inappropriate character in the input.
				  
Now I know that I have seen some scanfs that return something different
like the number of characters read.  ANSI should clear this up (using the
above I think).  My LSC manual is at home so I don't know what that
scanf returns.

So we change the example to this:

#include <stdio.h>

main()
{
    int i;

    do {
        printf("Enter a number between 1 and 100:  ");
        if (scanf("%d", &i) != 1) {
            while ((i = getchar()) != '\n')  /* skip bad stuff */
                ;
            i = 0;     /* or something else out of range */
        }
    } while (i < 1 || i > 100);
    printf("You typed: %d\n", i);
}

Note that the program does not go into an infinite loop because the
offending character that caused scanf not to match a decimal integer
has been removed from the input stream.

--
Scott Wilson		arpa: swilson@sun.com		"Why do dogs lick
Sun Microsystems	uucp: ...!sun!swilson		their balls?  Because
Mt. View, CA						they can!"

phd@speech1.cs.cmu.edu (Paul Dietz) (08/04/88)

I asked:
>>>I'm tired of fighting this one. Does anybody know how to delete
>>>characters when you make a mistake typing during an LSC scanf?

Apparently, noboby condones the use of scanf:

>>The answer to your question is, DON'T USE SCANF!

>Thus, not only do I never use "scanf" or "fscanf", I seldom use "sscanf".

SO: What do you use for input on your quick little pieces of
code????

Since I move code freely back and forth between UNIX and the Mac,
I'd like a solution which works equally well on both. My first
idea was to fix the scanf routine in the LSC library to handle
deletes properly. Unfortunately, this looked like a great deal
of work. (Apparently, the routine does not buffer the input...)
Next idea was to write a macro to replace scanf with the
appropriate fgets and sscanf calls. This stumbled when I realized
I didn't know how to write macros which took variable number of
arguments. The last idea was to write my own general scanf
replacement and use it everywhere, but this failed with my
not knowing a machine independent way of moving the cursor
and deleting characters.

So, what should I do??? Help.....

Thanks!

Paul H. Dietz                                        ____          ____
Dept. of Electrical and Computer Engineering        / oo \        <_<\\\
Carnegie Mellon University                        /|  \/  |\        \\ \\
--------------------------------------------     | | (  ) | |       | ||\\
"If God had meant for penguins to fly,             -->--<--        / / |\\\  /
he would have given them wings."            _________^__^_________/ / / \\\\-

swilson@sun.uucp (Scott Wilson) (08/04/88)

In article <62725@sun.uucp> swilson@sun.UUCP (Scott Wilson) writes:
>scanf is not a useless function!  While I believe that to approach 100%
>bullet-proof i/o you have to do everything yourself one character at a
>time, scanf is still useful.  The problem is that most people don't
>know how to use scanf.  Consider this example:

This bit was really intended to describe the usefulness of scanf on
other systems such as UNIX.  I agree that LSC's scanf leaves something
to be desired in terms of error correction.  At least the 3.0 version
is better than the 2.15 version which didn't echo what you typed (at least
that's how I remember it, I could be wrong, ever since I tried to bitch
about a bug that was my own stupidity I don't trust myself anymore).
So I guess scanf is not good for much in LSC after all.  I really wish
ANSI could do something for us here, but I'm told it doesn't.

Scott Wilson

jdevries@zodiac.ads.com (Jeff De Vries) (08/05/88)

If memory serves, I believe that LSC provides separate functions for
handling interactive I/O.  I think the functions are called 
 cscanf (for console scanf)
 cprintf (for console printf)

(Someone with a manual handy can very if I'm out in lala land)

Jeff De Vries
jdevries@ads.com (ARPA)
/* usual disclaimer */

oster@dewey.soe.berkeley.edu (David Phillip Oster) (08/07/88)

In article <587@helios.ee.lbl.gov> beard@ux1.lbl.gov (Patrick C Beard) writes:
>{
>	float in_value;
>	char buf[80];	/* overconservative */
>
>	printf("enter ... ");
>	gets(buf);	/* user can use backspace to edit */
>	sscanf(buf,"%f",&in_value);
>	return(in_value);
>}

Are you from the moon? Why would anyone want to do such a silly thing?

The correct solution is:

#if MAC

#include <DialogMgr.h>
#define TEXTITEM 3

/* GetDIHandle - return handle from current dialog item
 */
Handle GetDIHandle(item)short item;{
   short type;
   Handle hand;
   Rect r;

   GetDItem(thePort, item, &type, &hand, &r);
   return hand;
}

/* foo- get a floating point number from the user
 */
float foo(){
    float f;
    Str255 s;

    SetPort(GetNewDialog(FLOAT1, NIL, -1L));
    for(;;){
	ModalDialog(NIL, &item);
	switch(item){
	case OK:
		GetIText(GetDIHandle(TEXTITEM), s);
		PtoCstr( (char *) s);
		if(1 != sscanf(s,"%f", &f) || OutOfBounds(f)){
		/* bad number, beep and reselect */
		    SysBeep(1);
		    SelIText(thePort, TEXTITEM, 0, 9999);
		    break;
		}
		DisposDialog(thePort);
		return f;	/* user wants out */
	case Cancel:
		DisposDialog(thePort);
		return BADFLOAT;	/* user wants out */
	}
    }
}
/*
Where FLOAT1 is the resource id of a dialog with an okay button as item 1, 
a cancel button as item 2, 
an edit text box as item 3, 
and a prompt statictext string as item 4.
Just create it in ResEdit.
 */
#else
... whatever ludicrous nonsense you have to do on other machines
#endif

Get with it guys, this is Macintosh, not some 1960's vintage teletype.

--- David Phillip Oster            --When you asked me to live in sin with you
Arpa: oster@dewey.soe.berkeley.edu --I didn't know you meant sloth.
Uucp: {uwvax,decvax,ihnp4}!ucbvax!oster%dewey.soe.berkeley.edu