Google Answers Logo
View Question
 
Q: For: Maniac Re:C programming ( Answered 5 out of 5 stars,   2 Comments )
Question  
Subject: For: Maniac Re:C programming
Category: Computers > Programming
Asked by: teddy78-ga
List Price: $20.00
Posted: 17 Apr 2003 07:36 PDT
Expires: 17 May 2003 07:36 PDT
Question ID: 191729
Write the software called tail in the C programming language (any
operating system). We can find the manual page on any UNIX/Linux
system. The program should read the number of lines specified on the
command line from a text file
     	tail -6 abc.txt -8 cde.txt def.txt ...
will read the 6 last lines of the file. The default is the 10last
lines of the files, e.g.
 	tail abc.txt

My hints: We can open the file with open (), go to the end of the file
minus the buffer size using lseek (), read data into a buffer, scan
the buffer backwards one character a time until we have found the
needed number of new line characters. When reaching the start, read in
another buffer.
Answer  
Subject: Re: For: Maniac Re:C programming
Answered By: maniac-ga on 17 Apr 2003 18:21 PDT
Rated:5 out of 5 stars
 
Hello Teddy78,

Thanks for the question. Here's an answer that should do just fine. It
follows the suggested hints plus takes care of a few special cases:
 - input file is empty (no output)
 - invalid input parameter (indicate & print usage information)
 - print usage information if no arguments provided
It certainly doesn't do everything that the "real" tail program does,
but does match the output for all the test cases I tried. The program
follows at the end. It builds without error nor warning with gcc.

  --Maniac

/*
 * tail.c - print the last few lines of a file
 * similar to tail(1) for Unix
 */

#include <sys/types.h>
#include <sys/uio.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static char *myname;
static char buffer[BUFSIZ];

static void usage ()
{
    fprintf(stderr, "Usage: %s [-#] [file ...]\n", myname);
    exit(EXIT_FAILURE);
}

static void dotail (char * name,
		    int lines)
{
  int fd;
  off_t nowat;
  ssize_t chars;
  ssize_t endbuf;

  fd = open(name, O_RDONLY, 0);
  if (fd < 0) {
    fprintf(stderr, "%s: cannot open %s\n", myname, name);
    perror(myname);
    exit(EXIT_FAILURE);
  }
  /* get a buffer at the end */
  nowat = lseek(fd, -BUFSIZ, SEEK_END);
  if (nowat == -1) {
    fprintf(stderr, "%s: seek in %s failed\n", myname, name);
    perror(myname);
    (void)close(fd);
    exit(EXIT_FAILURE);
  }
  else if (nowat < 0) {
    nowat = lseek(fd, 0, SEEK_SET);
  }
  /* read the last few characters */
  chars = read(fd, &buffer, BUFSIZ);
  endbuf = chars; /* remember current end of buffer */
  if (chars < 0) {
    fprintf(stderr, "%s: read failed in %s\n", myname, name);
    perror(myname);
    (void)close(fd);
    exit(EXIT_FAILURE);
  }
  else if (chars == 0) {
    (void)close(fd);
  }
  else {
    /* scan backwards the number of lines */
    lines++; /* bump to avoid one off */
    while (lines > 0) {
      if (buffer[--chars] == '\n') {
	lines--;
      }
      else if (chars == 0) {
	if (nowat == 0)
	  break; /* can't go back farther */
	else if (nowat < BUFSIZ) {
	  chars = nowat-1; /* only get a few characters */
	  nowat = lseek(fd, 0, SEEK_SET);
	  chars = read(fd, &buffer, chars);
	  endbuf = chars;
	}
	else {
	  nowat = lseek(fd, nowat-BUFSIZ, SEEK_SET);
	  chars = read(fd, &buffer, BUFSIZ);
	  endbuf = chars;
	  /* note at this point - we have a full buffer */
	}
      }
      /* otherwise, we keep moving back */
    }
    if ((nowat != 0) || ((nowat == 0) & (chars > 0)))
      chars = chars+1; /* bump past \n if needed */
    /* 
     * at this point, buffer[chars+1] is the first line
     * to be output & we need to output characters
     * in the remaining part of the buffer & any later
     * buffers
     */
    chars = write(fileno(stdout), buffer+chars, endbuf-chars);
    while ((chars = read(fd, &buffer, BUFSIZ)) > 0) {
      (void)write(fileno(stdout), &buffer, chars);
    }
    (void)close(fd); /* all done with this file */
  }
/*   fprintf(stderr, "File %s for %d lines\n", name, lines); */
}

int main (int argc,
	  char * argv[])
{
  int curarg;
  int lines = 10;

  myname = argv[0];

  if (argc < 2) {
    usage();
  }

  /*
   * for each argument, set number of lines to dump or
   * get a file to process 
   */

  for (curarg = 1; curarg < argc; curarg++) {
    if (argv[curarg][0]=='-') {
      if (sscanf(argv[curarg], "-%d", &lines) == 0) {
	fprintf(stderr, "%s: don't support %s\n", myname, argv[curarg]);
	usage();
      }
    }
    else {
      dotail(argv[curarg], lines);
    }
  }
  return EXIT_SUCCESS;
}

Request for Answer Clarification by teddy78-ga on 22 Apr 2003 07:19 PDT
Hi Maniac 
I have got some problems when I tried to execute the program with the
command
tail.c  abc.txt
(Basically I have named the program as tail.c and created a text file
which is abc.txt)
When I tried to execute the program, which should read the number of
lines specified on the command line from abc.txt.
Could you explain me the commands I should enter for the execution of
the program or in other words how to make work of the code.

Clarification of Answer by maniac-ga on 22 Apr 2003 15:52 PDT
Hello Teddy78,

Assuming a Unix (or Linux) system and typical C compiler, enter
  cc -Wall -g -O    tail.c   -o tail
to compile tail.c to produce the executable program tail. You should
get no warnings nor error messages - if not, let me know.

At this point, you should be able to run a command like
  ./tail tail.c
to get output like
        fprintf(stderr, "%s: don't support %s\n", myname,
argv[curarg]);
        usage();
      }
    }
    else {
      dotail(argv[curarg], lines);
    }
  }
  return EXIT_SUCCESS;
}

If you have another type of system or compiler - please let me know so
I can see if I can find instructions for using that product. If you
don't have a compiler, let me know which operating system you have - I
may be able to find a free one for you to use. For example, in a
couple hours (using a 56k modem) you can download a free software
development environment for MS Windows.

  --Maniac
teddy78-ga rated this answer:5 out of 5 stars
Excellent Answer. Thanks for the quick response

Comments  
Subject: Re: For: Maniac Re:C programming
From: plumpy-ga on 17 Apr 2003 18:02 PDT
 
Wait, is this question open to anyone?

The "For: Maniac" makes me think you have someone specific in mind?
Subject: Re: For: Maniac Re:C programming
From: teddy78-ga on 11 May 2003 05:39 PDT
 
Hi Maniac

The program it did work. Thanks for the explanation. Sorry for the
late response.
I have got some problems in writing perl script to connect with a
Database. Is there any chance you can help me out with this issue?

Ted

Important Disclaimer: Answers and comments provided on Google Answers are general information, and are not intended to substitute for informed professional medical, psychiatric, psychological, tax, legal, investment, accounting, or other professional advice. Google does not endorse, and expressly disclaims liability for any product, manufacturer, distributor, service or service provider mentioned or any opinion expressed in answers or comments. Please read carefully the Google Answers Terms of Service.

If you feel that you have found inappropriate content, please let us know by emailing us at answers-support@google.com with the question ID listed above. Thank you.
Search Google Answers for
Google Answers  


Google Home - Answers FAQ - Terms of Service - Privacy Policy