From @mail.uunet.ca:beejay@micor Mon Nov 9 01:24:40 1992 Received: from calvin.sfasu.edu by umaxc.weeg.uiowa.edu (5.61.jnf/920629) on Mon, 9 Nov 92 01:24:37 -0600 id AA09831 with SMTP Received: from mail.uunet.ca (uunet.ca) by calvin.sfasu.EDU with SMTP (5.59/25-eef) id AA06389; Mon, 9 Nov 92 01:18:05 CST Return-Path: <@mail.uunet.ca:beejay@micor> Received: from ocunix by mail.uunet.ca with UUCP id <10042>; Mon, 9 Nov 1992 02:15:16 -0500 Received: by micor.OCUnix.On.Ca (smail2.5) id AA02871; 9 Nov 92 02:05:12 EST (Mon) To: hyperc-l@calvin.sfasu.edu Subject: Traps - scanf Date: Sun, 8 Nov 1992 21:05:10 -0500 Message-Id: <9211090205.AA02869@micor.OCUnix.On.Ca> From: beejay@micor.ocunix.on.ca (Basil Johnson) Status: R Traps - scanf ------------- I was writing a program that required prompting the user for a filename. Nowhere in the HyperC documentation could I find such an example. All its references had the file name embedded in the code, like if (0 >= (myfile = open("myData"))) error("Can't open \"myData\", NIL); I looked at some C texts but they seem to have a preoccupation with command line interfaces and heavy dependence on argv, argc. The HyperC library function conRead was a possibility but the string buffer would have a newline before the string terminator. The scanf function was another possibility but there's no documentation that came with the HyperC port. I took a chance that it would provide a string without the newline and used it like this: scanf("Filename: %s\n", filename); I then tried to access the file with the file name I entered from the keyboard using the filename variable. No way, Jose! A little bit of debugging ensued. The strlen function revealed that the length was one byte longer that the filename entered. A pointer to the string and a hex dump of the string showed that an 0xd preceeded the '\0' string terminator. So what was necessary to get the correct filename was: filename[strlen(filename) - 1] = '\0'; In retrospect, it would have been better to use conRead which I had rejected earlier. It provides the same functionality as scanf plus I could control the input length for the filename as long as it is less than 15 (Note, ProDOS allows for 15 chars filenames, HyperC max is 14). flen = conRead(filename, 16); /* 14 chars + newline + terminator */ filename[flen - 1] = '\0'; or filename[conRead(filename, 16) - 1] = '\0'; You may have seen my mail asking for sscanf source. Kevin Henigan recommended an approach using the existing scanf source. One of Kevin's instructions was that I should remove the function call to conRead. I don't know if the input string in ANSI scanf should include the newline, but I think we now know (at least, I do) that it's caused by the use of conRead. That can be easily fixed as shown above, but first we need to know which is the proper way. Can someone find out/confirm whether or not the ANSI scanf input string includes the newline? I implemented Kevin's recommendations on how to convert scanf into sscanf and compiled it. That was the easy part. I then wrote a little test program that had multiple arguments in the string that sscanf would get its arguments from. It didn't work. After doing the edit, compile cycle many many times, I discovered a pattern. In every case, the first argument was always displayed correctly; the second argument was _always_ displayed in incorrect format and subsequent arguments were displayed unpredictably - sometimes right, othertimes wrong. I began to suspect that the problem may lie in the original code of scanf. I modified the sscanf test file for scanf and entered the same input in the sscanf string from the keyboard. I had the same problem. As the problem begins occurring on the second argument, I suspected that only _one_ argument was being processed but that's not the case. The correct number of arguments is being returned. The problem with scanf and multiple arguments has now been solved. A revised scanf and a sscanf based on it plus Kevin's ideas will be subsequently released. The correct form of the input string will be included (see above). Here's something else I'd like to have clarified before these two functions get released. scanf has a function, convert() which calculates and returns the field width in number of character spaces for each data type, i.e. [ d i u o x h c s]. The defaults, respectively are: 6 8 5 6 4 3 1 and 98 for a string - [BUFSIZ - 2]. The code allows you to set the display width at less than the the default. For example, if the format string has %2d, and the 5 digit decimal number (12345) is entered from the keyboard, what is displayed is 12 (2 digits). From what I recall reading, the numeric specifier in the string should be the MINIMUM. If the field width needed to represent an argument is greater than the minimum field width, then the minimum field width should have no effect. That is to say, if as in this case, 5 diits are needed for the display then 5 digits should be what gets displayed, not 2. To summarize: 1. Don't use a control string with multiple format specifiers. (revision pending) 2. The string variable has a newline embedded within it. The scanf %s format specifier exists primarily to allow users to input a string in the same line that contains values of type INT, i.e. multiple arguments (see above). Use conRead and adjust for its embedded newline, if needed. 3. The numeric field width in a format specifier takes precedenc over the default width for a data type. Comments, confirmations, disagreements are welcomed. Disclaimer: Comments enclosed herewith should not in any way be construed as a denigration of the scanf port. Comments are offered in the spirit of sharing knowledge and with the hope that the dessimination of that knowledge may prevent others from falling in the same traps. _