473,322 Members | 1,778 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,322 software developers and data experts.

Custom Scanf Routine

I have tried to write my custom scanf function on the lines of minprintf
provided in K&R2.In the function myscanf() i access memory directly
using the address passed to the function .Can it be dangerous ?

I am getting the correct output though.Any help is appreciated.

/*Include Files*/

/*Assisting Functions*/
int flushln(FILE *f){ /*Code*/}
char *input(char *message){/*Code*/}
static int getInt(void){/*Code*/}
/*My Custom Scanf Routine*/

int myscanf(const char* format,...)
{
va_list ap;
const char *p;
int count = 0;
int temp;

va_start(ap,format);
for( p = format ; *p ;p++) {
if( *p != '%'){
continue;
}
switch(*++p){
case 'd':
if(temp = getInt()){
*(long *)va_arg(ap,int) = temp;/*Direct Memory Access*/
count ++;
}
else{ puts("Input Error"); }
break;
}
}
va_end(ap);
return count;
}

int main(void)
{
int a = 0,b = 0;
myscanf("%d %d",&a,&b);
printf("%d %d",a,b);

return 0;
}

Thanks!
Jul 3 '08 #1
30 3395
Hi

On Fri, 04 Jul 2008 03:12:09 +0530, Tarique wrote:
I have tried to write my custom scanf function
*(long *)va_arg(ap,int) = temp;
That is a very bad idea, it might work on certain systems where pointers
are the same size as integers, but that is often not the case.

Even if this does work once, the next operation on ap might fail.

va_arg needs to be told the type of the argument that was written in
place of the ... in when the function was called. You should always put
the correct type in the second argument. In most cases that means you
won't need to cast the return.

* va_arg(ap,long*) = temp;

will work perfectly, as long as a long pointer was really written at the
current place in the argument list.

HTH
viza
Jul 4 '08 #2
Tarique wrote:
....snip...

This is my code for a minimal custom scanf function(for entering valid
ints n longs with error checking)
I would be really grateful if someone can review it.Comments awaited!

Thank You

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2 /*Used by input()*/

/*Clear The Input stream if required*/
int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
continue;
return ch;
}

/*Accept a string from user (parse later on)*/
char *input(const char* message,char *buff)
{
char buffer[ BUFFSIZE ];
char *p = &buffer[ BUFFSIZE-1 ];

if(message != "")
puts(message);

buffer[BUFFSIZE -1] = '$';

if( fgets(buffer,BUFFSIZE,stdin) == NULL) {
if(ferror(stdin)) {
perror("Input Stream Error");
clearerr( stdin );
return "I/O";
}
if(feof(stdin)) {
perror("EOF Encountered");
clearerr( stdin );
return "EOF";
}
}
else{
if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
flushln(stdin);
puts("Too long input!\n\n");
return NULL;
}
}
strcpy(buff,buffer);
return buff;
}

/*Accept a string and pass a valid long int if possible,else return 0*/
long getInt(void)
{
char intbuff[100];
char *buffptr = intbuff;
char *end_ptr;
long int lVal;
int trial = 1; /*No of tries on wrong input*/
int dist=0;
errno = 0;

while(trial-- != 0)
{
buffptr = input("Enter :",intbuff);
if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
return 0;

lVal= strtol(buffptr, &end_ptr, 0);
if (ERANGE == errno){
perror("Out of Range");
}
else if (lVal INT_MAX){
perror("Too Large!");
}
else if (lVal < INT_MIN){
perror("Too Small");
}
else if (end_ptr == buffptr){
printf("Not a Valid Integer\n\n");
}
else
return lVal;
}
return 0;
}

int myscanf(const char* format,...)
{
va_list ap;
const char *p;
int count = 0;
int temp;

va_start(ap,format);
for( p = format ; *p ;p++) {
if( *p != '%'){
continue;
}
switch(*++p){
case 'd':
if(temp =(int)getInt()){
*va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
count ++;
}
else break;
break;

case 'l':
if((*++p) == 'd'){
if(temp =getInt()){
*va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
count ++;
p--;
}
else {
*va_arg(ap,long *) = 0;/*Invalid input ,so set variable to zero*/
p--;
break;
}
}
break;
default:
break;
}
}
va_end(ap);
return count;
}

int main(void)
{
long int a = 0,b = 0;
int c = 0,d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);
printf("%d %d",c,d);

return 0;
}
Jul 4 '08 #3
Tarique wrote:
Tarique wrote:
...snip...

This is my code for a minimal custom scanf function(for entering valid
ints n longs with error checking)
I would be really grateful if someone can review it.Comments awaited!

Thank You
Just a note. Haven't seen it in detail, sorry.

Also in future please use spaces in place of tabs when posting to
Usenet. As you can see below, some software on the Usenet stripped out
all your tabs, rendering unformatted code, which is very difficult to
read.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2 /*Used by input()*/

/*Clear The Input stream if required*/
int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
continue;
return ch;
}

/*Accept a string from user (parse later on)*/
char *input(const char* message,char *buff)
{
char buffer[ BUFFSIZE ];
char *p = &buffer[ BUFFSIZE-1 ];

if(message != "")
puts(message);

buffer[BUFFSIZE -1] = '$';

if( fgets(buffer,BUFFSIZE,stdin) == NULL) {
if(ferror(stdin)) {
perror("Input Stream Error");
clearerr( stdin );
return "I/O";
}
if(feof(stdin)) {
perror("EOF Encountered");
clearerr( stdin );
return "EOF";
}
}
else{
if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
flushln(stdin);
puts("Too long input!\n\n");
return NULL;
}
}
strcpy(buff,buffer);
return buff;
}

/*Accept a string and pass a valid long int if possible,else return
0*/ long getInt(void)
{
char intbuff[100];
char *buffptr = intbuff;
char *end_ptr;
long int lVal;
int trial = 1; /*No of tries on wrong input*/
int dist=0;
errno = 0;

while(trial-- != 0)
{
buffptr = input("Enter :",intbuff);
if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
return 0;

lVal= strtol(buffptr, &end_ptr, 0);
if (ERANGE == errno){
perror("Out of Range");
}
else if (lVal INT_MAX){
perror("Too Large!");
}
else if (lVal < INT_MIN){
perror("Too Small");
}
What will you do on implementations where INT_MAX == LONG_MAX and
INT_MIN == LONG_MIN?

<snip rest>

Jul 4 '08 #4
On Fri, 04 Jul 2008 18:10:51 +0530, Tarique wrote:
I would be really grateful if someone can review it.Comments awaited!
#define BUFFSIZE 98 + 2 /*Used by input()*/
Generally it's not smart to use arbitrary fixed length buffers. Are you
certain that the input won't be bigger? If your code is well written
then it that would cause it to fail, and if it is not well written then
it could FEYC (f***ing explode your computer).
/*Clear The Input stream if required*/ int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
continue;
return ch;
}
Gah! ugly. It's best that you don't use assignments as truth values,
especially as a beginner. That continue is superfluous too. I would
also ungetc() the character and return void.
/*Accept a string from user (parse later on)*/ char *input(const char*
message,char *buff) {
char buffer[ BUFFSIZE ];
char *p = &buffer[ BUFFSIZE-1 ];
char *p= buffer + BUFSIZE - 1; might be easier to read.
if(message != "")
You can't do that in C. Well, you can, but it doesn't mean what you
think. Lookup the strcmp() function.
puts(message);
buffer[BUFFSIZE -1] = '$';
if( fgets(buffer,BUFFSIZE,stdin) == NULL) {
if(ferror(stdin)) {
perror("Input Stream Error");
clearerr( stdin );
return "I/O";
It's not usual to return strings like that one. In case of error perhaps
you might want to return NULL.
}
if(feof(stdin)) {
perror("EOF Encountered");
clearerr( stdin );
return "EOF";
}
}
else{
if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') )))
{
flushln(stdin);
puts("Too long input!\n\n");
return NULL;
}
}
strcpy(buff,buffer);
If you know that buff is at least as big as buffer, why do you need
buffer in the first place? Read to buff.
return buff;
}

/*Accept a string and pass a valid long int if possible,else return 0*/
long getInt(void)
{
char intbuff[100];
char *buffptr = intbuff;
char *end_ptr;
long int lVal;
int trial = 1; /*No of tries on wrong input*/ int dist=0;
errno = 0;

while(trial-- != 0)
{
buffptr = input("Enter :",intbuff);
if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
return 0;

lVal= strtol(buffptr, &end_ptr, 0);
if (ERANGE == errno){
You didn't set errno to zero. Well you did, but it might have changed
many times since then. Do it immediately before each conversion.

I haven't gome through it all, but HTH
viza
Jul 4 '08 #5
viza wrote:
...snip...
>
I haven't gome through it all, but HTH
viza
@Santosh and Viza

Thanks for the quick review.Ive pasted the code here :
http://phpfi.com/329176
Hope it is easier to read.
Jul 4 '08 #6
On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <pe*****@yahoo.comwrote:
>This is my code for a minimal custom scanf function(for entering valid
ints n longs with error checking)
I would be really grateful if someone can review it.Comments awaited!

Thank You

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2 /*Used by input()*/

/*Clear The Input stream if required*/
int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
continue;
return ch;
}

/*Accept a string from user (parse later on)*/
char *input(const char* message,char *buff)
{
char buffer[ BUFFSIZE ];
char *p = &buffer[ BUFFSIZE-1 ];

if(message != "")
puts(message);

buffer[BUFFSIZE -1] = '$';

if( fgets(buffer,BUFFSIZE,stdin) == NULL) {
if(ferror(stdin)) {
perror("Input Stream Error");
clearerr( stdin );
return "I/O";
}
if(feof(stdin)) {
perror("EOF Encountered");
clearerr( stdin );
return "EOF";
}
}
else{
if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
Please don't use tabs, especially within a line.

This test is backwards. If *p is '$', then a short line was entered
and there is nothing to flush. If the second half is true, exactly
BUFFSIZE-1 characters were entered (including the Enter key) and there
is still nothing to flush.

An easier test would be to set buffer[BUFFSIZE-2] to '\n'. If it is a
different non-zero value after the fgets, then too much data was
entered.
> flushln(stdin);
puts("Too long input!\n\n");
return NULL;
}
}
strcpy(buff,buffer);
return buff;
}

/*Accept a string and pass a valid long int if possible,else return 0*/
long getInt(void)
{
char intbuff[100];
char *buffptr = intbuff;
char *end_ptr;
long int lVal;
int trial = 1; /*No of tries on wrong input*/
int dist=0;
errno = 0;

while(trial-- != 0)
{
buffptr = input("Enter :",intbuff);
if (buffptr == "I/O" || buffptr == "EOF" || buffptr == NULL)
return 0;

lVal= strtol(buffptr, &end_ptr, 0);
You need to indent consistently. This statement is not part of the
range of the preceding if. It should be indented to the same level as
that if.
> if (ERANGE == errno){
You should reset errno before calling strtol so that you don't process
residual data. strtol is not allowed to reset errno so you must do
it.
> perror("Out of Range");
}
else if (lVal INT_MAX){
perror("Too Large!");
}
else if (lVal < INT_MIN){
perror("Too Small");
}
else if (end_ptr == buffptr){
printf("Not a Valid Integer\n\n");
This is insufficient. Input of the form "123abc" will appear valid.
To be sure that strtol processed all the characters, make sure end_ptr
points to either '\n' or '\0'.
> }
else
return lVal;
How do you distinguish between input of 0 and one of the "error"
conditions such as "I/O"?
> }
return 0;
}

int myscanf(const char* format,...)
{
va_list ap;
const char *p;
int count = 0;
int temp;

va_start(ap,format);
for( p = format ; *p ;p++) {
if( *p != '%'){
continue;
}
switch(*++p){
These two statements are within the range of the for but you indenting
makes them look independent.
> case 'd':
if(temp =(int)getInt()){
The cast is not needed.

Why are you using %d to describe a long and confuse everyone.

Why do you not accept 0 as valid input?
> *va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
count ++;
}
else break;
This is superfluous. If you remove it, the code will behave the same.
> break;

case 'l':
if((*++p) == 'd'){
if(temp =getInt()){
*va_arg(ap,long *) = temp;/* Direct Memory Access to MEM */
count ++;
p--;
}
else {
*va_arg(ap,long *) = 0;/*Invalid input ,so set variable to zero*/
I still don't understand why 0 is illegal but it sure seems like a
deliberate decision.
> p--;
break;
}
}
break;
default:
break;
}
If the value processed for %d is 0, you do not store any value for the
corresponding argument AND you do not pop the argument off the list.

If the value processed for %ld is 0, you store 0 and pop the argument
but don't increment count.

There could be an inconsistency between the value of count returned
and which arguments contain valid data.
> }
va_end(ap);
return count;
}

int main(void)
{
long int a = 0,b = 0;
int c = 0,d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);
Here you invoke undefined behavior. myscanf will treat &c and &d as
long int* when they aren't. I think you meant for myscanf to extract
an int*, not a long*, for the %d case.
> printf("%d %d",c,d);

return 0;
}

Remove del for email
Jul 4 '08 #7
On Fri, 04 Jul 2008 14:30:08 -0700, Barry Schwarz <sc******@dqel.com>
wrote:
>On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <pe*****@yahoo.comwrote:
>>This is my code for a minimal custom scanf function(for entering valid
ints n longs with error checking)
I would be really grateful if someone can review it.Comments awaited!

Thank You

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2 /*Used by input()*/

/*Clear The Input stream if required*/
int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
continue;
return ch;
}

/*Accept a string from user (parse later on)*/
char *input(const char* message,char *buff)
{
char buffer[ BUFFSIZE ];
char *p = &buffer[ BUFFSIZE-1 ];

if(message != "")
puts(message);

buffer[BUFFSIZE -1] = '$';

if( fgets(buffer,BUFFSIZE,stdin) == NULL) {
if(ferror(stdin)) {
perror("Input Stream Error");
clearerr( stdin );
return "I/O";
}
if(feof(stdin)) {
perror("EOF Encountered");
clearerr( stdin );
return "EOF";
}
}
else{
if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {

Please don't use tabs, especially within a line.

This test is backwards.
Obviously I missed the !.
Remove del for email
Jul 5 '08 #8
viza wrote:
Tarique wrote:
.... snip ...
>
>/*Clear The Input stream if required*/ int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
continue;
return ch;
}

Gah! ugly. It's best that you don't use assignments as truth
values, especially as a beginner. That continue is superfluous
too. I would also ungetc() the character and return void.
Not in the least ugly. Apart from the awkward location of the
initial comment. Works like a charm, too. The continue prevents
misreading an isolated semi. Your ungetc recommendation would make
the routine fail to perform the function indicated by the name.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Jul 5 '08 #9
On Fri, 04 Jul 2008 19:48:08 -0400, CBFalconer wrote:
viza wrote:
>Tarique wrote:
>>/*Clear The Input stream if required*/ int flushln(FILE *f) {
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
continue;
return ch;
}

Gah! ugly. It's best that you don't use assignments as truth values,
especially as a beginner. That continue is superfluous too. I would
also ungetc() the character and return void.

Not in the least ugly.
I wouldn't take it home.
Apart from the awkward location of the initial comment. Works like a
charm, too. The continue prevents misreading an isolated semi.
Your ungetc recommendation would make the routine fail to perform the
function indicated by the name.
True. I misread the ugly loop construct and thought it read one-past the
newline.
Jul 5 '08 #10
Barry Schwarz wrote:
On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <pe*****@yahoo.comwrote:
....snip..
>int main(void)
{
long int a = 0,b = 0;
int c = 0,d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);

Here you invoke undefined behavior. myscanf will treat &c and &d as
long int* when they aren't. I think you meant for myscanf to extract
an int*, not a long*, for the %d case.
Yes that is what i want it to do ! But why does that invoke UB ? I
thought the cast should produce correct result !!

Secondly Santosh had asked "What will you do on implementations where
INT_MAX == LONG_MAX and INT_MIN == LONG_MIN? "
On my system i have INT_MAX == LONG_MAX and INT_MIN == LONG_MIN
and the program seems to produce correct results.Am I missing something
there?

Well I,ve made the changes suggested and this the modified one . This
one produces results more consistently as expected(at least it seems to me!)

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2
int flushln(FILE *f) { /*Clear The Input stream if required*/
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
;
return ch;
}
/*Accept a string from user*/

char *input(const char* message,char *buff,size_t buff_len)
{
char buffer[ BUFFSIZE ];
char *p = buffer + BUFFSIZE - 1;

if(strcmp(message,""))
puts(message);

buffer[BUFFSIZE -1] = '$';

if( fgets(buffer,BUFFSIZE,stdin) == NULL) {
if(ferror(stdin)) {
perror("Input Stream Error");
clearerr( stdin );
return "I/O";
}
else if(feof(stdin)) {
perror("EOF Encountered");
clearerr( stdin );
return "EOF";
}
}
else{
if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
flushln(stdin);
puts("Too long input!\n\n");
return NULL;
}
}

if(buff_len >= strlen(buffer)){ /*Input longer than the dest buff can hold*/
strcpy(buff,buffer);
return buff;
}
else
return NULL;
}

/*Accept a string and pass a valid long int if possible,else return 0*/
long getInt(void)
{
char intbuff[20];
char *buffptr = intbuff;
char *end_ptr = "\n";
long int lVal;
int trial = 1; /*No of trials allowed on error */
int dist=0;
errno = 0;

while(trial-- != 0)
{
buffptr = input("Enter :", intbuff, 19);

if(!strcmp(intbuff,"I/O")){
fprintf(stderr,"I/O Errro\n");
return -1;
}
else if (!strcmp(intbuff,"EOF")){
fprintf(stderr,"EOF\n");
return -1;
}
else if(buffptr == NULL){
fprintf(stderr,"Too Long Input\n");
return -1;
}

errno = 0;
lVal= strtol(buffptr, &end_ptr, 0);
if (ERANGE == errno){
perror("Out of Range");
}
else if (lVal LONG_MAX){
perror("Too Large!");
}
else if (lVal < LONG_MIN){
perror("Too Small");
}
else if (end_ptr == buffptr){
printf("Not a Valid Integer\n\n");
}
else
return lVal;
}
return -1;
}

int myscanf(const char* format,...)
{
va_list ap;
const char *p;
int count = 0;
int temp = 0 ;

va_start(ap,format);
for( p = format ; *p ;p++) {
if( *p != '%'){
continue;
}
switch(*++p){
case 'd':
if(temp = (int)getInt()){
if(temp == 0)
*va_arg(ap,long *) = 0;/*Ugly Hack ?*/
*va_arg(ap,long *) = temp;/* Direct Memory Access*/
count ++;
}
else {
*va_arg(ap,long *) = 0;
p--;
break;
}

case 'l':
if((*++p) == 'd'){
if(temp = getInt()){
if(temp == 0)
*va_arg(ap,long *) = 0;/*Ugly Hack!*/
*va_arg(ap,long *) = temp;
count ++;
p--;
}
else {
*va_arg(ap,long *) = 0;
p--;
break;
}
}
default:
break;
}
}
va_end(ap);
return count;
}

int main(void)
{
long int a = 0,b = 0;
int c = 0,d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);
printf("%d %d",c,d);

return 0;
}
Thanks for the suggestions.

Tarique
Jul 5 '08 #11
On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <pe*****@yahoo.comwrote:
>Barry Schwarz wrote:
>On Fri, 04 Jul 2008 18:10:51 +0530, Tarique <pe*****@yahoo.comwrote:
...snip..
>>int main(void)
{
long int a = 0,b = 0;
int c = 0,d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);

Here you invoke undefined behavior. myscanf will treat &c and &d as
long int* when they aren't. I think you meant for myscanf to extract
an int*, not a long*, for the %d case.

Yes that is what i want it to do ! But why does that invoke UB ? I
thought the cast should produce correct result !!
There is no cast in the statement under discussion. &c is obviously
an int*. Your code in myscanf use the va_arg macro with an argument
of long*.

If the two types don't have the same size, va_arg will extract
the wrong number of bytes from the "argument list".
If an int* and long* are passed by different mechanisms (e.g.,
different registers), it may not even find the argument.
If the two pointer types don't have the same representation, the
address will be misinterpreted.
myscanf dereferences the argument on the left side of an
assignment. The address of the int could be not properly aligned for
a long.
If sizeof(long)>sizeof(int), you attempt to store too many bytes
in the int.

Each of these errors invokes undefined behavior.

Remove del for email
Jul 5 '08 #12
On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <pe*****@yahoo.comwrote:

snip
>Well I,ve made the changes suggested and this the modified one . This
one produces results more consistently as expected(at least it seems to me!)
Actually it is considerably worse.
>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2
int flushln(FILE *f) { /*Clear The Input stream if required*/
int ch;
while (('\n' != (ch = getc( f ))) && (EOF != ch))
;
return ch;
You are still not indenting with any consistency. The return is not
part of the while. You have the same problem in getInt.
>}
/*Accept a string from user*/

char *input(const char* message,char *buff,size_t buff_len)
{
char buffer[ BUFFSIZE ];
char *p = buffer + BUFFSIZE - 1;

if(strcmp(message,""))
puts(message);

buffer[BUFFSIZE -1] = '$';

if( fgets(buffer,BUFFSIZE,stdin) == NULL) {
if(ferror(stdin)) {
perror("Input Stream Error");
clearerr( stdin );
return "I/O";
}
else if(feof(stdin)) {
perror("EOF Encountered");
clearerr( stdin );
return "EOF";
}
}
else{
if(!((*p == '$') || ( (*p == '\0') && (*--p == '\n') ))) {
flushln(stdin);
puts("Too long input!\n\n");
return NULL;
}
}

if(buff_len >= strlen(buffer)){ /*Input longer than the dest buff can hold*/
strcpy(buff,buffer);
return buff;
}
else
return NULL;
}

/*Accept a string and pass a valid long int if possible,else return 0*/
long getInt(void)
{
char intbuff[20];
char *buffptr = intbuff;
char *end_ptr = "\n";
long int lVal;
int trial = 1; /*No of trials allowed on error */
int dist=0;
errno = 0;

while(trial-- != 0)
{
buffptr = input("Enter :", intbuff, 19);

if(!strcmp(intbuff,"I/O")){
fprintf(stderr,"I/O Errro\n");
return -1;
}
else if (!strcmp(intbuff,"EOF")){
fprintf(stderr,"EOF\n");
return -1;
}
else if(buffptr == NULL){
fprintf(stderr,"Too Long Input\n");
return -1;
}

errno = 0;
lVal= strtol(buffptr, &end_ptr, 0);
If the user enters a number with a leading 0, it will be treated as
octal. Is that what you want?
>

if (ERANGE == errno){
perror("Out of Range");
}
else if (lVal LONG_MAX){
What makes you think strtol can ever return a value greater than
LONG_MAX?
> perror("Too Large!");
}
else if (lVal < LONG_MIN){
Or less than LONG_MIN?
> perror("Too Small");
}
else if (end_ptr == buffptr){
You still do not catch the error on input of "123abc".
> printf("Not a Valid Integer\n\n");
}
else
return lVal;
}
return -1;
So you have decided that -1 will never be an acceptable input.
>}

int myscanf(const char* format,...)
{
va_list ap;
const char *p;
int count = 0;
int temp = 0 ;

va_start(ap,format);
for( p = format ; *p ;p++) {
if( *p != '%'){
continue;
}
switch(*++p){
And here you have messed up the indenting even worse. It looks as if
the for loop terminates before the switch statement executes.
> case 'd':
if(temp = (int)getInt()){
if(temp == 0)
If you reach this statement, can it ever evaluate to true?
> *va_arg(ap,long *) = 0;/*Ugly Hack ?*/
Why do you think these two lines are needed? If temp is 0, what do
they do that the next line does not?

If temp is 0, you store 0 into two different objects, one in the above
statement and one in the following statement. Is that what you really
want to do?
> *va_arg(ap,long *) = temp;/* Direct Memory Access*/
The term Direct Memory Access has a special meaning in some hardware
designs. You are simply dereferencing a pointer.
> count ++;
There is no break here. You will fall into the 'l' case.
> }
else {
*va_arg(ap,long *) = 0;
p--;
Why are you backing up to the % symbol?
> break;
}

case 'l':
if((*++p) == 'd'){
if(temp = getInt()){
Why do you care if the input value is 0?
> if(temp == 0)
*va_arg(ap,long *) = 0;/*Ugly Hack!*/
*va_arg(ap,long *) = temp;
count ++;
p--;
Why are you backing up to the l?
> }
else {
*va_arg(ap,long *) = 0;
p--;
break;
}
}
default:
break;
}
}
va_end(ap);
return count;
}

int main(void)
{
long int a = 0,b = 0;
int c = 0,d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);
The undefined behavior is still here since myscan only processes long*
but you are passing int*.
>printf("%d %d",c,d);

return 0;
}

Remove del for email
Jul 5 '08 #13
Barry Schwarz wrote:
On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <pe*****@yahoo.comwrote:

snip
>Well I,ve made the changes suggested and this the modified one . This
one produces results more consistently as expected(at least it seems to me!)

Actually it is considerably worse.
Just could not believe that i had actually messed it up completely!
One more attempt ..phew!

I've re-typed the entire code and hope that the indentation doesn't get
messed up on usenet.This is the default indentation produced by MSVC++
08.I tried reducing it in the last post and the result was far worse!
If it still does, please read it here : http://phpfi.com/329403

Thanks again for all the patience :)
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2

int flushln(FILE *f)
{
int ch;
while(('\n' != (ch = getc( f ))) && (EOF != ch))
;
return ch;
}

char* input(const char* message,char* buff,size_t buff_len)
{
char buffer[ BUFFSIZE ];
char* p = buffer + BUFFSIZE - 1;

if( strcmp( message, "" ) )
puts( message );

buffer[ BUFFSIZE - 1 ] = '$';

if( fgets( buffer, BUFFSIZE, stdin) == NULL ) {
if( ferror(stdin) ) {
perror( "Input Stream Error" );
clearerr( stdin );
return "I/O";
}
else if( feof(stdin) ) {
perror( "Input Stream Error" );
clearerr( stdin );
return "EOF";
}
}

else {
if(!((*p == '$') || ((*p == '\0') && (*--p == '\n')))) {
flushln( stdin );
return NULL ;
}
}

if( buff_len >= strlen(buffer) ) {
strcpy( buff, buffer );
return buff;
}
else {
return NULL;
}
}
long getInt( int* err_flag )
{
char intbuff[21] = {0} ;
char* buffptr = intbuff;
char* end_ptr = NULL;
long int lval = 0;
int trial = 1;
errno = 0;

while( trial-- )
{
buffptr = input( "Enter :", intbuff, 20 );

if( !strcmp( intbuff , "I/O" ) ) {
fprintf( stderr, "I/O Error \n" );
*err_flag = 0;
return -1;
}
else if( !strcmp( intbuff , "EOF" ) ) {
fprintf( stderr, "EOF \n" );
*err_flag = 0;
return -1;
}
else if( buffptr == NULL ) {
fprintf( stderr, "Too Long Input\n" );
*err_flag = 0;
return -1;
}

errno = 0;
lval = strtol( buffptr, &end_ptr, 10 );

if( ERANGE == errno ) {
*err_flag = 0;
perror( " Out of Range " );
}
else if ( lval INT_MAX ) {
*err_flag = 0;
perror( "Too Large " );
}
else if ( lval < INT_MIN ) {
*err_flag = 0;
perror( "Too Small" );
}
else if (!((*end_ptr == '\n') || (*end_ptr == '\0'))) {
*err_flag = 0;
fprintf( stderr, "Not a Valid Integer\n" );
}
else {
return lval;
}
}

*err_flag = 0;
return -1;
}
int myscanf( const char* format, ... )
{
va_list ap;
const char* p;
int count = 0;
int temp = 0;
int flag = 1;

va_start( ap, format );

for( p = format ; *p ; p++ ) {
if( *p != '%' ) {
continue;
}
switch( *++p ) {

case 'd' :
temp = (int)getInt( &flag );
if( flag ) {
*va_arg( ap, int* ) = temp;
count ++ ;
}
else {
*va_arg( ap, int* ) = 0;
flag = 1;
}

break;

case 'l':
if( (*++p) == 'd' ) {
temp = getInt( &flag );
if( flag ) {
*va_arg( ap, long * ) = temp;
count ++;
}
else {
*va_arg( ap, long * ) = 0;
flag = 1;
}
}

break;

default :
break;
}
}
va_end(ap);
return count;
}

int main(void)
{
long int a = 0, b = 0;
int c = 0, d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);
printf("%d %d",c,d);

return 0;
}

Tarique
Jul 5 '08 #14
Tarique <pe*****@yahoo.comwrites:
Barry Schwarz wrote:
>On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <pe*****@yahoo.comwrote:
snip
>>Well I,ve made the changes suggested and this the modified one
. This one produces results more consistently as expected(at least
it seems to me!)
Actually it is considerably worse.

Just could not believe that i had actually messed it up completely!
One more attempt ..phew!

I've re-typed the entire code and hope that the indentation doesn't
get messed up on usenet.This is the default indentation produced by
MSVC++ 08.I tried reducing it in the last post and the result was far
worse!
If it still does, please read it here : http://phpfi.com/329403

Thanks again for all the patience :)
[...]

I haven't read the entire program, just bits and piece of it. At
least for now, all I have to offer is a few style points.

In flushln(), you use the (rather odd IMHO) idiom of "constant !=
expression":
while(('\n' != (ch = getc( f ))) && (EOF != ch))
In other places, you put the constant on the right hand side of the
comparison:
if(!((*p == '$') || ((*p == '\0') && (*--p == '\n')))) {
The usual reason to put the constant on the left is to avoid the error
of writing "=" rather than "==". Some programmers extend this to
operators other than "==". This:

if (x = 42) { ... }

is perfectly legal, but it assigns 42 to x rather than comparing x to
42. Some programmers cultivate the habit of writing:

if (42 == x) { ... }

so that, if they accidentally write "=" rather than "==", the
compiler will catch the error. You do this in some places, but not in
others.

My own opinion is that "if (42 == x)" is just ugly, and I never use it
myself; I find "if (x == 42") much more natural. But if you're going
to use it you should probably do so consistently. Or you can just be
careful not to write "=" when you mean "==".

Your function getInt() returns a result of type long. You call it
twice; both times, you assign the result to a variable of type int.
(In one you use an unnecessary cast; in the other you don't.) Why not
just declare getInt() to return an int? Or declare temp as a long?
Often (but by no means always), the need to convert something from one
type to another means that both things should have been of the same
type in the first place.

I stumbled over your invocations of va_arg, such as:
*va_arg( ap, int* ) = temp;
va_arg() is required to be a macro, and va_arg(ap, int*) expands to an
expression of type int*, so this seems to be valid.

But note that an invocation of va_arg *looks like* a function call.
If it really were, then the unary "*" operator would apply to the name
"va_arg", not to the result of the call, and the assignment would be
invalid.

So the assignment is valid, but it looks wrong. I'd definitely add
parentheses:

*(va_arg(ap, int*)) = temp;

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jul 5 '08 #15
Tarique <pe*****@yahoo.comwrites:

<snip>
Just could not believe that i had actually messed it up completely!
One more attempt ..phew!

I've re-typed the entire code and hope that the indentation doesn't
get messed up on usenet.This is the default indentation produced by
MSVC++ 08.I tried reducing it in the last post and the result was far
worse!
If it still does, please read it here : http://phpfi.com/329403

Thanks again for all the patience :)
A few things that occur to me...
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2
(98 + 2) or you might get a surprise.
int flushln(FILE *f)
{
int ch;
while(('\n' != (ch = getc( f ))) && (EOF != ch))
;
return ch;
}

char* input(const char* message,char* buff,size_t buff_len)
{
char buffer[ BUFFSIZE ];
char* p = buffer + BUFFSIZE - 1;

if( strcmp( message, "" ) )
puts( message );

buffer[ BUFFSIZE - 1 ] = '$';
Having set p, I'd write *p = '$'; It makes the test latter more
obvious. See also below for some an alternative.
if( fgets( buffer, BUFFSIZE, stdin) == NULL ) {
if( ferror(stdin) ) {
perror( "Input Stream Error" );
clearerr( stdin );
return "I/O";
I fond this odd -- returning a string to indicate an error. An enum
or a number if usually easier.
}
else if( feof(stdin) ) {
perror( "Input Stream Error" );
clearerr( stdin );
return "EOF";
}
}

else {
if(!((*p == '$') || ((*p == '\0') && (*--p == '\n'))))
I'd write p[-1] == '\n' since you don't need p to change. It is
clearer that an expression with a side-effect. My preference, since
the '$' is entirely arbitrary, is to set *p = !0; above. All you care
about is that it is some value other than zero and this makes that
clear. Your test here is then simplified to !(*p || p[-1] == '\n').
{
flushln( stdin );
return NULL ;
}
}

if( buff_len >= strlen(buffer) ) {
strcpy( buff, buffer );
return buff;
}
else {
return NULL;
}
}
long getInt( int* err_flag )
{
char intbuff[21] = {0} ;
char* buffptr = intbuff;
char* end_ptr = NULL;
long int lval = 0;
int trial = 1;
errno = 0;

while( trial-- )
{
buffptr = input( "Enter :", intbuff, 20 );

if( !strcmp( intbuff , "I/O" ) ) {
I think this is wrong. You return "I/O" rather than copy into the
array passed to input.
fprintf( stderr, "I/O Error \n" );
*err_flag = 0;
return -1;
}
else if( !strcmp( intbuff , "EOF" ) ) {
Ditto.
fprintf( stderr, "EOF \n" );
*err_flag = 0;
return -1;
}
else if( buffptr == NULL ) {
fprintf( stderr, "Too Long Input\n" );
*err_flag = 0;
return -1;
}

errno = 0;
lval = strtol( buffptr, &end_ptr, 10 );

if( ERANGE == errno ) {
*err_flag = 0;
perror( " Out of Range " );
}
else if ( lval INT_MAX ) {
*err_flag = 0;
perror( "Too Large " );
}
else if ( lval < INT_MIN ) {
*err_flag = 0;
perror( "Too Small" );
}
else if (!((*end_ptr == '\n') || (*end_ptr == '\0')))
{
This seems a bit harsh! Do you want to reject " 123 " as an integer?
*err_flag = 0;
fprintf( stderr, "Not a Valid Integer\n" );
}
else {
return lval;
}
}

*err_flag = 0;
return -1;
}
int myscanf( const char* format, ... )
{
va_list ap;
const char* p;
int count = 0;
int temp = 0;
int flag = 1;
Yucky name. You one that tell the reader what is signified by a true
value. If you can't (as I think is the case here because you use it
for lots of things, then it is probably better to redesign that).
va_start( ap, format );

for( p = format ; *p ; p++ ) {
if( *p != '%' ) {
continue;
}
strchr is simpler though you have to test the result instead of
relying on the default case of the switch.
switch( *++p ) {

case 'd' :
temp = (int)getInt( &flag );
if( flag ) {
*va_arg( ap, int* ) = temp;
count ++ ;
}
else {
*va_arg( ap, int* ) = 0;
flag = 1;
}

break;

case 'l':
if( (*++p) == 'd' ) {
temp = getInt( &flag );
if( flag ) {
*va_arg( ap, long * ) = temp;
count ++;
}
else {
*va_arg( ap, long * ) = 0;
flag = 1;
}
}

break;

default :
break;
}
}
va_end(ap);
return count;
}

int main(void)
{
long int a = 0, b = 0;
int c = 0, d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);
printf("%d %d",c,d);

return 0;
}

Tarique
--
Ben.
Jul 5 '08 #16
On Sat, 05 Jul 2008 23:45:06 +0530, Tarique <pe*****@yahoo.comwrote:
>Barry Schwarz wrote:
>On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <pe*****@yahoo.comwrote:

snip
>>Well I,ve made the changes suggested and this the modified one . This
one produces results more consistently as expected(at least it seems to me!)

Actually it is considerably worse.

Just could not believe that i had actually messed it up completely!
One more attempt ..phew!

I've re-typed the entire code and hope that the indentation doesn't get
messed up on usenet.This is the default indentation produced by MSVC++
08.I tried reducing it in the last post and the result was far worse!
If it still does, please read it here : http://phpfi.com/329403

Thanks again for all the patience :)
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>

#define BUFFSIZE 98 + 2

int flushln(FILE *f)
{
int ch;
while(('\n' != (ch = getc( f ))) && (EOF != ch))
;
return ch;
}

char* input(const char* message,char* buff,size_t buff_len)
{
char buffer[ BUFFSIZE ];
char* p = buffer + BUFFSIZE - 1;

if( strcmp( message, "" ) )
puts( message );

buffer[ BUFFSIZE - 1 ] = '$';

if( fgets( buffer, BUFFSIZE, stdin) == NULL ) {
if( ferror(stdin) ) {
perror( "Input Stream Error" );
clearerr( stdin );
return "I/O";
}
else if( feof(stdin) ) {
perror( "Input Stream Error" );
clearerr( stdin );
return "EOF";
}
}

else {
if(!((*p == '$') || ((*p == '\0') && (*--p == '\n')))) {
flushln( stdin );
return NULL ;
}
}

if( buff_len >= strlen(buffer) ) {
strcpy( buff, buffer );
return buff;
}
else {
return NULL;
}
}
long getInt( int* err_flag )
{
char intbuff[21] = {0} ;
char* buffptr = intbuff;
char* end_ptr = NULL;
long int lval = 0;
int trial = 1;
errno = 0;

while( trial-- )
{
buffptr = input( "Enter :", intbuff, 20 );

if( !strcmp( intbuff , "I/O" ) ) {
fprintf( stderr, "I/O Error \n" );
*err_flag = 0;
return -1;
}
else if( !strcmp( intbuff , "EOF" ) ) {
fprintf( stderr, "EOF \n" );
*err_flag = 0;
return -1;
}
else if( buffptr == NULL ) {
fprintf( stderr, "Too Long Input\n" );
*err_flag = 0;
return -1;
}

errno = 0;
lval = strtol( buffptr, &end_ptr, 10 );

if( ERANGE == errno ) {
*err_flag = 0;
perror( " Out of Range " );
perror will print two messages, the one you provided and the one
corresponding to ERANGE. While it is implementation dependent, I'll
bet you cannot find an implementation where they don't say the same
thing.
> }
else if ( lval INT_MAX ) {
*err_flag = 0;
perror( "Too Large " );
At this point we know errno is 0. I have no idea what the
"corresponding message" will be but it could be something like "no
error detected". This will then be followed by your message saying
"yes there was". It is not nice to confuse the poor user.
> }
else if ( lval < INT_MIN ) {
*err_flag = 0;
perror( "Too Small" );
}
else if (!((*end_ptr == '\n') || (*end_ptr == '\0'))) {
*err_flag = 0;
fprintf( stderr, "Not a Valid Integer\n" );
I would use fprintf in place of the previous calls to perror.
> }
else {
return lval;
}
}

*err_flag = 0;
return -1;
}
This function cannot return a non-int value, even though it is
packaged inside a long.
>

int myscanf( const char* format, ... )
{
va_list ap;
const char* p;
int count = 0;
int temp = 0;
int flag = 1;

va_start( ap, format );

for( p = format ; *p ; p++ ) {
if( *p != '%' ) {
continue;
}
switch( *++p ) {

case 'd' :
temp = (int)getInt( &flag );
if( flag ) {
*va_arg( ap, int* ) = temp;
count ++ ;
}
else {
*va_arg( ap, int* ) = 0;
flag = 1;
}

break;

case 'l':
if( (*++p) == 'd' ) {
temp = getInt( &flag );
if( flag ) {
*va_arg( ap, long * ) = temp;
You are processing %ld and treating it as a specification for long.
Unfortunately, getInt will only return int values. You can store the
value in a long but you can never process a "true" long.
> count ++;
}
else {
*va_arg( ap, long * ) = 0;
flag = 1;
}
}

break;

default :
break;
}
}
va_end(ap);
return count;
}

int main(void)
{
long int a = 0, b = 0;
int c = 0, d = 0;

myscanf("%ld %ld",&a,&b);
printf("%ld %ld \n\n",a,b);

myscanf("%d %d",&c,&d);
printf("%d %d",c,d);

return 0;
}

Tarique

Remove del for email
Jul 6 '08 #17
Barry Schwarz wrote:
On Sat, 05 Jul 2008 23:45:06 +0530, Tarique <pe*****@yahoo.comwrote:
>Barry Schwarz wrote:
>>On Sat, 05 Jul 2008 09:14:17 +0530, Tarique <pe*****@yahoo.comwrote:

..snip...
I would use fprintf in place of the previous calls to perror.
> }
else {
return lval;
}
}

*err_flag = 0;
return -1;
}

This function cannot return a non-int value, even though it is
packaged inside a long.
I didn't understand this ?
>int myscanf( const char* format, ... )
{
va_list ap;
const char* p;
int count = 0;
int temp = 0;
int flag = 1;
long int temp_long = 0; /*Added*/

...snip...
> case 'l':
if( (*++p) == 'd' ) {
temp = getInt( &flag );
Changed this to temp_long = getInt ( &flag );
> if( flag ) {
*va_arg( ap, long * ) = temp;
And this to *(va_arg( ap, long* )) = temp_long;
>
You are processing %ld and treating it as a specification for long.
Unfortunately, getInt will only return int values. You can store the
value in a long but you can never process a "true" long.

I believe i am right this time.I completely missed it the previous
time.Incorporated the above changes suggested by all.

Thanks
Tarique
Jul 6 '08 #18
Tarique wrote:
>
.... snip ...
>
I believe i am right this time.I completely missed it the previous
time.Incorporated the above changes suggested by all.
In English, a period is normally followed by at least one space,
usually two. I see no time structure in your code, especially none
with the fields time.I and time.Incorporated.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.
Jul 6 '08 #19
On Sun, 06 Jul 2008 14:26:12 +0530, Tarique <pe*****@yahoo.comwrote:
>Barry Schwarz wrote:
>This function cannot return a non-int value, even though it is
packaged inside a long.

I didn't understand this ?
The function is named getInt. It returns a long. (This in itself is
a little confusing but there are several library functions that do
something similar such as getchar. However, when they do it there is
a reason.)

Before your function returns, it compares the return value to both
INT_MAX and INT_MIN and returns an error condition (-1 in your latest
iteration) if the value is outside those limits.

Therefore, even though the function returns a value of type long, the
value itself is always in the range of int. There is nothing wrong
with this in the abstract but you attempt to process longs in myscanf.
myscanf calls getInt. This is a serious lack of consistency that
would cause serious problems for users of the functions that really
want to process longs.
>
>>int myscanf( const char* format, ... )
{
va_list ap;
const char* p;
int count = 0;
int temp = 0;
int flag = 1;
long int temp_long = 0; /*Added*/

...snip...
>> case 'l':
if( (*++p) == 'd' ) {
temp = getInt( &flag );

Changed this to temp_long = getInt ( &flag );
This doesn't change anything. getInt still refuses to return any
value outside the range of an int.
>> if( flag ) {
*va_arg( ap, long * ) = temp;

And this to *(va_arg( ap, long* )) = temp_long;
When you fix getInt, this will help.
>>
You are processing %ld and treating it as a specification for long.
Unfortunately, getInt will only return int values. You can store the
value in a long but you can never process a "true" long.


I believe i am right this time.I completely missed it the previous
time.Incorporated the above changes suggested by all.
Sorry but you fixed only half the problem.
Remove del for email
Jul 6 '08 #20
Barry Schwarz wrote:
On Sun, 06 Jul 2008 14:26:12 +0530, Tarique <pe*****@yahoo.comwrote:
>Barry Schwarz wrote:
....snip..

Sorry but you fixed only half the problem.
The Best i've come so far :I've re-designed the error handling (looks
sane to me! ) and now process signed "Longs" and "Ints" separately !

/*
* File: main.c
* Author: Tarique
*
* Created on July 4, 2008, 11:07 PM
*/

#include <stdio.h >
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdarg.h>
#include <errno.h >
/*
*
*/
struct ErrorCode {
int code ;
/*Pack any other info if required later on */
};

/*Provide Error Code to Differentiate between Errors*/
enum Ecodes {
E_OK = 0, /*Input Ok */
E_IO = 1, /*Input Stream Error */
E_EOF = 2, /*End of File Envountered */
E_TOOLONG = 3, /*Input Exceeds Buffer Length */
E_SHORTBUFF = 4, /*Destination Buffer is Short */
E_RANGE = 5, /*Number Exceeds Range */
E_BADINPUT = 6, /*Invalid Input Obtained */
E_DEFAULT = 7 /*All Unhandled Errors if Any */
};

/*
*If there is garbage in the input stream,pull off everything from the
*input stream to clear it for further use.
*/

int flushln(FILE *f) {
int ch;
while(('\n' != (ch = getc( f ))) && (EOF != ch))
;
return ch;
}

/*Takes a destination buffer and its size as arg (With Error Code) ;
*copies the user entered string into the destination buffer if input
* is successful and the caller's buffer is long enough to hold it.
*
*On Error ,clears the error and sets the appropriate error code which
*can be suitably utilized if necessary.
*/

void input( char* buff,
size_t buff_len,
struct ErrorCode* error ) {

#define BUFFSIZE 100 + 2

char buffer[ BUFFSIZE ];
char* p = buffer + BUFFSIZE - 1;
*p = !0;

if( fgets( buffer, BUFFSIZE, stdin ) == NULL ) {
if( ferror( stdin ) ) {
fprintf( stderr, "Input Stream Error \n" );
error->code = E_IO;
clearerr( stdin );
return ;
}
else if( feof( stdin ) ) {
fprintf( stderr, "EOF Encountered \n" );
error->code = E_EOF;
clearerr( stdin );
return ;
}
}

else {
if( !(*p || (*--p) == '\n') ) {
fprintf( stderr, "Too Long Input\n" );
error->code = E_TOOLONG;
flushln( stdin );
return ;
}
}

if( buff_len >= strlen(buffer) ) {
strcpy( buff, buffer );
error->code = E_OK;
return ;
}

else {
fprintf( stderr, "Too Long Input-b \n" );
error->code = E_SHORTBUFF;
return ;
}
}

/*
* If a string is successfully obtained from the input function,
*it is parsed using strtol. If no parsing error occurs, a long
*If there is any error zero is returned.
*/

long getLong(int isint,
struct ErrorCode* error ) {

char intbuff[21] = {0} ;
char* end_ptr = NULL;
long int lval = 0;
errno = 0;

printf("Enter :");
input( intbuff, 20, error );

if ( error->code == E_IO ) return 0;
else if( error->code == E_EOF ) return 0;
else if( error->code == E_TOOLONG ) return 0;
else if( error->code == E_SHORTBUFF ) return 0;

errno = 0;
lval = strtol( intbuff, &end_ptr, 10 );
if( ERANGE == errno ) {
fprintf(stderr,"Out of Range \n");
error->code = E_RANGE;
return 0;
}
else if (!((*end_ptr == '\n') || (*end_ptr == '\0') || (*end_ptr ==
' '))) {
error->code = E_BADINPUT;
fprintf( stderr, "Not a Valid Integer\n" );
return 0;
}
if(!isint) { /*Process long */
return lval;
}

else { /*Process Int*/
if ( lval INT_MAX ) {
error->code = E_RANGE;
fprintf( stderr, "Number too Large \n" );
return 0;
}
else if ( lval < INT_MIN ) {
error->code = E_RANGE;
fprintf( stderr, "Number too Small \n" );
return 0;
}
else
return lval;
}
}

/*Minimal Scanf routine only for signed
*'ints' and 'longs' as of now
*/

int myscanf( const char* format, ... ) {

va_list ap ;
const char* p;
int count = 0;
int temp = 0;
long int temp_long = 0;
struct ErrorCode iserror;
iserror.code = 0;

va_start( ap, format );

for( p = format ; *p ; p++ ) {
if( *p != '%' ) {
continue;
}
switch( *++p ) {
case 'd' :
temp = (int)getLong( 0, &iserror );
if( !iserror.code ) {
*va_arg( ap, int* ) = temp;
count ++ ;
}
else {
*va_arg( ap, int* ) = 0;
iserror.code = 0;
}

break;

case 'l':
if( (*++p) == 'd' ) {
temp_long = getLong( 1, &iserror );
if( !iserror.code ) {
*va_arg( ap, long * ) = temp_long;
count ++;
}
else {
*va_arg( ap, long * ) = 0;
iserror.code = 0;
}
}

break;

default :
break;
}
}
va_end(ap);
return count;
}
int main( void ) {

long a = 0, b = 0;
int c = 0, d = 0;

myscanf("%ld %ld" ,&a ,&b);
printf("%d %d \n",a,b);

myscanf("%d %d" ,&c ,&d);
printf("%d %d\n",c,d);

return (EXIT_SUCCESS);
}

Tarique.

Jul 7 '08 #21
I have just one small correction here...

In article <ln************@nuthaus.mib.org>
Keith Thompson <ks***@mib.orgwrote:
>I stumbled over your invocations of va_arg, such as:
> *va_arg( ap, int* ) = temp;
va_arg() is required to be a macro, and va_arg(ap, int*) expands to an
expression of type int*, so this seems to be valid.
It is.
>But note that an invocation of va_arg *looks like* a function call.
If it really were, then the unary "*" operator would apply to the name
"va_arg", not to the result of the call, and the assignment would be
invalid.
No, it is still valid even then:

*f() = var;

binds the same as:

(*(f())) = var;

because the function-call operator (the parentheses following an
identifier) binds more tightly than the unary-* dereference operator.

(As an aside, va_arg looks like a function call only superficially,
since its second argument is a type-name. Note also that the type
name is required to be one that can have one more unary "*" added
to the end, so va_arg is the only place in C where typedef is
actually required, though even then only sometimes. In particular,
to extract a parameter of type "pointer to function (args) returning
T", you cannot write:

va_arg(ap, T (args))

because the syntatic construct for "pointer to function returning T"
is not "T (args) *" but rather "T (*)(args)". But given:

typedef T alias(args);

we can then write:

va_arg(ap, alias)

This uses, or at least potentially uses, the syntactic construct
"alias *", which has type "pointer to function (args) returning
T", which is what we need.)
--
In-Real-Life: Chris Torek, Wind River Systems
Salt Lake City, UT, USA (40°39.22'N, 111°50.29'W) +1 801 277 2603
email: gmail (figure it out) http://web.torek.net/torek/index.html
Jul 10 '08 #22
Chris Torek <no****@torek.netwrites:
I have just one small correction here...

In article <ln************@nuthaus.mib.org>
Keith Thompson <ks***@mib.orgwrote:
>>I stumbled over your invocations of va_arg, such as:
>> *va_arg( ap, int* ) = temp;
va_arg() is required to be a macro, and va_arg(ap, int*) expands to an
expression of type int*, so this seems to be valid.

It is.
>>But note that an invocation of va_arg *looks like* a function call.
If it really were, then the unary "*" operator would apply to the name
"va_arg", not to the result of the call, and the assignment would be
invalid.

No, it is still valid even then:

*f() = var;

binds the same as:

(*(f())) = var;

because the function-call operator (the parentheses following an
identifier) binds more tightly than the unary-* dereference operator.
[...]

You're right, as usual.

I'd still be happier with more parentheses, even though they're not
strictly necessary:

*(va_arg(ap, int*)) = temp;

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jul 14 '08 #23
Chris Torek <no****@torek.netwrites:
I have just one small correction here...

In article <ln************@nuthaus.mib.org>
Keith Thompson <ks***@mib.orgwrote:
>>I stumbled over your invocations of va_arg, such as:
>> *va_arg( ap, int* ) = temp;
va_arg() is required to be a macro, and va_arg(ap, int*) expands to an
expression of type int*, so this seems to be valid.

It is.
>>But note that an invocation of va_arg *looks like* a function call.
If it really were, then the unary "*" operator would apply to the name
"va_arg", not to the result of the call, and the assignment would be
invalid.

No, it is still valid even then:

*f() = var;

binds the same as:

(*(f())) = var;

because the function-call operator (the parentheses following an
identifier) binds more tightly than the unary-* dereference operator.
[...]

You're right, as usual.

I'd still be happier with more parentheses, even though they're not
strictly necessary:

*(va_arg(ap, int*)) = temp;

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jul 14 '08 #24
Chris Torek <no****@torek.netwrites:
I have just one small correction here...

In article <ln************@nuthaus.mib.org>
Keith Thompson <ks***@mib.orgwrote:
>>I stumbled over your invocations of va_arg, such as:
>> *va_arg( ap, int* ) = temp;
va_arg() is required to be a macro, and va_arg(ap, int*) expands to an
expression of type int*, so this seems to be valid.

It is.
>>But note that an invocation of va_arg *looks like* a function call.
If it really were, then the unary "*" operator would apply to the name
"va_arg", not to the result of the call, and the assignment would be
invalid.

No, it is still valid even then:

*f() = var;

binds the same as:

(*(f())) = var;

because the function-call operator (the parentheses following an
identifier) binds more tightly than the unary-* dereference operator.
[...]

You're right, as usual.

I'd still be happier with more parentheses, even though they're not
strictly necessary:

*(va_arg(ap, int*)) = temp;

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jul 14 '08 #25
Chris Torek <no****@torek.netwrites:
I have just one small correction here...

In article <ln************@nuthaus.mib.org>
Keith Thompson <ks***@mib.orgwrote:
>>I stumbled over your invocations of va_arg, such as:
>> *va_arg( ap, int* ) = temp;
va_arg() is required to be a macro, and va_arg(ap, int*) expands to an
expression of type int*, so this seems to be valid.

It is.
>>But note that an invocation of va_arg *looks like* a function call.
If it really were, then the unary "*" operator would apply to the name
"va_arg", not to the result of the call, and the assignment would be
invalid.

No, it is still valid even then:

*f() = var;

binds the same as:

(*(f())) = var;

because the function-call operator (the parentheses following an
identifier) binds more tightly than the unary-* dereference operator.
You're right, as usual.

I'd still be happier with more parentheses, even though they're not
strictly necessary:

*(va_arg(ap, int*)) = temp;

(Having trouble with my news server; I hope this shows up no more or
less than once.)

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jul 14 '08 #26
Keith Thompson <ks***@mib.orgwrites:
[...]
(Having trouble with my news server; I hope this shows up no more or
less than once.)
Naturally it was posted 4 times. I might have to change news servers
again. I'll try posting this exactly once; if it fails, I won't
retry. We'll see what happens.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jul 14 '08 #27
On Jul 14, 9:20 pm, Keith Thompson <ks...@mib.orgwrote:
Keith Thompson <ks...@mib.orgwrites:

[...]
(Having trouble with my news server; I hope this shows up no more or
less than once.)

Naturally it was posted 4 times. I might have to change news servers
again. I'll try posting this exactly once; if it fails, I won't
retry. We'll see what happens.
You could try alt.test instead of posting here.
Your message appeared, I think you're just impatient with your slow?
news server :)
Jul 14 '08 #28
vi******@gmail.com writes:
On Jul 14, 9:20 pm, Keith Thompson <ks...@mib.orgwrote:
>Keith Thompson <ks...@mib.orgwrites:

[...]
(Having trouble with my news server; I hope this shows up no more or
less than once.)

Naturally it was posted 4 times. I might have to change news servers
again. I'll try posting this exactly once; if it fails, I won't
retry. We'll see what happens.
You could try alt.test instead of posting here.
Your message appeared, I think you're just impatient with your slow?
news server :)
Sorry again for the junk posts. None of them were intended to be test
posts, they just turned out that way. In at least one case, the
server apparently posted the article and then gave me an error
message. Most of the time it posts right away. I'll be more careful
in the future.

--
Keith Thompson (The_Other_Keith) ks***@mib.org <http://www.ghoti.net/~kst>
Nokia
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"
Jul 14 '08 #29
Keith Thompson wrote:
vi******@gmail.com writes:
>On Jul 14, 9:20 pm, Keith Thompson <ks...@mib.orgwrote:
>>Keith Thompson <ks...@mib.orgwrites:

[...]

(Having trouble with my news server; I hope this shows up no more
or less than once.)

Naturally it was posted 4 times. I might have to change news
servers
again. I'll try posting this exactly once; if it fails, I won't
retry. We'll see what happens.
You could try alt.test instead of posting here.
Your message appeared, I think you're just impatient with your slow?
news server :)

Sorry again for the junk posts. None of them were intended to be test
posts, they just turned out that way. In at least one case, the
server apparently posted the article and then gave me an error
message. Most of the time it posts right away. I'll be more careful
in the future.
If your news server is motzarella.org then yes, this happens
occasionally to me too. If I receive an error message after I attempt
to post an article, I wait for five minutes, then check the group and
retry if the article has not appeared despite the error. Naturally it's
annoying but thankfully it doesn't happen often.

Jul 14 '08 #30
Keith Thompson wrote:
Keith Thompson <ks***@mib.orgwrites:
[...]
>(Having trouble with my news server; I hope this shows up no
more or less than once.)

Naturally it was posted 4 times. I might have to change news
servers again. I'll try posting this exactly once; if it fails,
I won't retry. We'll see what happens.
I suspect your problem is gnus, not motzarella. Motzarella goes
busy at times, and you have to wait for it to get back to you.

--
[mail]: Chuck F (cbfalconer at maineline dot net)
[page]: <http://cbfalconer.home.att.net>
Try the download section.

Jul 14 '08 #31

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

39
by: Teh Charleh | last post by:
OK I have 2 similar programmes, why does the first one work and the second does not? Basically the problem is that the program seems to ignore the gets call if it comes after a scanf call. Please...
14
by: Peter Mount | last post by:
Hello I'm having trouble with " scanf("%c", &answer);" on line 20 below. When I run the program in cygwin on Windows 98SE it skips that line completely and ends the program. Does scanf have...
4
by: Vig | last post by:
Is scanf or any other function capable of reading numbers in the format 1.2345d-13 where 'd' serves the same role as 'e' usually does in scientific notation? This operation is iterated through...
6
by: Rob Thorpe | last post by:
Given the code:- r = sscanf (s, "%lf", x); What is the correct output if the string s is simply "-" ? If "-" is considered the beginning of a number, that has been cut-short then the...
3
by: linguae | last post by:
Hello. In my C program, I have an array of character pointers. I'm trying to input character strings to each index of the character pointer array using scanf(), but when I run the program, I get...
185
by: Martin Jørgensen | last post by:
Hi, Consider: ------------ char stringinput ..bla. bla. bla. do {
62
by: Argento | last post by:
I was curious at the start about how ungetc() returns the character to the stream, so i did the following coding. Things work as expected except if I change the scanf("%c",&j) to scanf("%d",&j). I...
4
by: Val | last post by:
I have a complex object that I need to serialize. Rather than rely on a standard routine, which is called during the serialization/deserialization, I would like to be able to use my own functions...
26
by: vid512 | last post by:
hi. i wanted to know why doesn't the scanf functions check for overflow when reading number. For example scanf("%d" on 32bit machine considers "1" and "4294967297" to be the same. I tracked...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Defcon1945 | last post by:
I'm trying to learn Python using Pycharm but import shutil doesn't work
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 3 Apr 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome former...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.