Je t’embrasse Salutations from Silicon Valley, California

24Dec/110

Traversing a file in C

I can not begin to tell you how useful the following code has been in my endeavors to do complicated forward-backward grep-esque searching. Along with the regular-expression matching that I have put together previously, this will round off pretty much everything you need to do your own fancy-grepping.

The code below is based upon the fgetc() and fgets() functions. The first two, rgetc() and rgets() are essentially the reverse of the original functions. They read from the file, but instead of moving the file-pointer forward, they move it back. Thus, you could start at end-of-file, and traverse all the way back to beginning-of-file.

int
rgetc(FILE *stream)
{
  if (fseek(stream, -2, SEEK_CUR) == -1) return EOF;
  return fgetc(stream);
}

int
rgets(char *s, int size, FILE *stream)
{
  int n=0;
  int c;

  while (1) {
    if ((c = rgetc(stream)) == EOF) {
      /* if we are too close to BOF to rgetc() */
      if ((ftell(stream) <= 2) && (n+1 == 2)) {
        rewind(stream);
        n=2;
      }
      /* otherwise EOF == ERROR */
      else return EINVAL;
    }
    if (c == '\n') n++;
    if (n == 2) break;
  }

  if (fgets(s, size, stream) == NULL) return EINVAL;
  return 0;
}

Finally, there is always the time in which what you really want is to read without moving the file-pointer at all. This way, you get a character/line into a buffer, but you still have the same character/line pointed to at the end as you did at the beginning. (Very useful when you need to double-parse a line)

int
tgetc(FILE *stream)
{
  if (fseek(stream, -1, SEEK_CUR) == -1) return EOF;
  return fgetc(stream);
}

int
tgets(char *s, int size, FILE *stream)
{
  int n=0;
  int c;

  while (1) {
    if ((c = rgetc(stream)) == EOF) {
      /* if we are too close to BOF to rgetc() */
      if ((ftell(stream) <= 2) && (n+1 == 2)) {
        rewind(stream);
        n=2;
      }
      /* otherwise EOF == ERROR */
      else return EINVAL;
    }
    if (c == '\n') n++;
    if (n == 1) break;
  }

  /* Look for a newline, otherwise EOF */
  if (fgets(s, size, stream) == NULL) return EINVAL;
  if (strstr(s, "\n") == NULL) return EOF;
  return 0;
}

Trust me, if you want to grep through logs, going forward till X, backward from there till Y, find Z & re-grep entire file for Z, and then locate the 3rd occurrence of the word "ERROR" also associated with Z... anyway, you get the point. Grep is useless. My functions RULE!

Filed under: C/C++ No Comments