Jump to: navigation, search

The C Programming Language, 2nd Edition, by Kernighan and Ritchie
Exercise 4.07 on page 79

Write a routine ungets(s) that will push back an entire string onto the input. Should ungets know about buf and bufp , or should it just use ungetch ?



Solution by Steven Huang

/* K&R Exercise 4-7 */
/* Steven Huang */

#include <string.h>
#include <stdio.h>

#define BUFSIZE 100

char buf[BUFSIZE]; /* buffer for ungetch */
int bufp = 0; /* next free position in buf */

int getch(void) /* get a (possibly pushed back) character */
{
  return (bufp > 0) ? buf[--bufp] : getchar();
}
 
void ungetch(int c) /* push character back on input */
{
  if(bufp >= BUFSIZE)
    printf("ungetch: too many characters\n");
  else  
    buf[bufp++] = c;  
}
 
/*
   ungets() actually takes a little bit of thought.  Should the
   first character in "s" be sent to ungetch() first, or should
   it be sent last?  I assumed that most code calling getch()
   would be of this form:

     char array[...];
     int i;   

     while (...) {
       array[i++] = getch();
     }                  

   In such cases, the same code might call ungets() as:

     ungets(array);

   and expect to repeat the while loop to get the same string
   back.  This requires that the last character be sent first
   to ungetch() first, because getch() and ungetch() work with 
   a stack.     

   To answer K&R2's additional question for this problem,
   it's usually preferable for something like ungets() to just
   build itself on top of ungetch().  This allows us to change 
   ungetch() and getch() in the future, perhaps to use a linked 
   list instead, without affecting ungets().
*/ 
void ungets(const char *s)
{    
  size_t i = strlen(s);

  while (i > 0)
    ungetch(s[--i]);
}
 
int main(void)
{
  char *s = "hello, world.  this is a test.";
  int c;

  ungets(s);
  while ((c = getch()) != EOF)
    putchar(c);               
  return 0;
}


Solution by menonsahab

/* There are a few situations wherein ungets() using ungetch() may cause a problem.
It is because ungets() doesn't do any bounds checking. If I have a string of length one million and one and the 
available space in buf[] is only one million, then only after copying the first one million characters will I come
to know that there isn't any space for the last character and depending upon the situation I might have to undo the
entire process. So ungets() must do its own bounds checking for which it will require access to both buf[] and bufp.
Notice that if you provide ungets() with access to buf[] and bufp, the whole idea of using ungetch() becomes moot.
I'll still use it for brevity.*/

#include <stdio.h>
#include <string.h>

#define BUFSIZE 10
char buf[BUFSIZE];
int bufp = 0;

int getch(void)
{
	return bufp > 0 ? buf[--bufp] : getchar();
}

void ungetch(int c)
{
	if(bufp >= BUFSIZE)
		printf("error: stack is full, can't execute ungetch()\n");
	else
		buf[bufp++] = c;
}

void ungets(char *s)
{
	int i, len;
	len = strlen(s);
	if(BUFSIZE - bufp >= len)  // ungets() must do its own bounds checking
	{
		for(i = strlen(s) - 1; i >= 0; i--)
			ungetch(s[i]);
	}
	else
		printf("error: insufficient space in buffer, can't execute ungets()\n");
}

int main()
{
	char s[] = "Rapa chika paka, raja babu\n";
	int c;
	ungets(s);
	while((c = getch()) != EOF)
		putchar(c);
	return 0;
}

/* You might think that now bounds checking is being done twice, both by ungets() and ungetch(). Isn't it a waste
of time. No, because there might be other functions using ungetch() to store back just one variable. In such cases,
ungetch() must have its own bounds checking as well. */ 

Solution by anonymous

I found that ungets didn't need to know about buf and bufp, but it is helpful to know that buf is a stack. This means the string needs to be put in reverse order so it will be in the correct order when retrieved by getch. I made a small test program for ungets since I didn't see a purpose for it in the actual reverse Polish calculator program.

#include <stdio.h>
#include <string.h> // for strlen()

/*
    Exercise 4-7. Write a routine ungets(s) that will push back an entire string onto the input. Should ungets know about buf and bufp, or should it just use ungetch?
*/

#define BUFSIZE 100 // buffer size for getch and ungetch

char buf[BUFSIZE];  // buffer for ungetch
int bufp = 0;       // next free position in buf

int getch(void);
void ungetch(int c);
void ungets(char s[]);

int main()
{
    int i;
    char s[] = "Hello World";
    ungets(s);
    for (i = 0; i < 11; i++)
        printf("%c", getch());
    return 0;
}

// get a (possibly pushed back) character
// checks to see if there are any chars in buffer. If there are, get those and return it. If not, call getchar() from stdio.h to get next char from input
int getch(void)
{
    return (bufp > 0) ? buf[--bufp] : getchar();
}

// push character back on input
// if bufp is less than BUFSIZE, there is room to store more chars to be read by getch next and it stores c and updates the index for it
void ungetch(int c)
{
    if (bufp >= BUFSIZE)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++] = c;
}

// puts the reversed string on buffer so it comes out in the original order when getch is called
void ungets(char s[])
{
    int i = strlen(s);
    while (i >= 0)
        ungetch(s[i--]);
}
Personal tools