So I have an assignment that I have been attempting for 2 weeks now and the class has been given extensions twice now. Everyone seems to be struggling with this part of the assignment. The assignment is to do K&R Exercise 5-13, unix tail command.
Instructions: "The program takes lines from standard input and keeps the last n of them in memory as it goes through standard input. When it gets to an EOF, it prints the last n lines out. You may assume n is less than 2000 and each individual line is not longer than 1000 including the newline and the null terminator.

Use two source files plus a header file for this, just to show you know how to make multiple source files work together.

tail.c:
interprets the command line argument.
Calls init_lineholder(int nlines) with the numebr from the command line.
Does a loop calling getline and insert_line(char *line)
when getline returns 0 (indicating EOF on stdin), it calls print_lines().

lineholder.c
contains a static array of pointers for lines.
Implements init_lineholder, insert_line, and print_lines.
Init_lineholder initializes the "first" slot and related variables.
Insert_line adds a line to the array.
It must allocate memory for the new line.
It must free the memory for a line no longer needed, if any.
Print_lines prints the lines in the array and frees the memory used for them.

lineholder.h: Just has prototypes for the three calls with appropriate comments explaining what they do for the caller.

Sources:

blade71(148)% cat lineholder.h
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* This header file contains the prototypes of the three functions
   in lineholder.c                                                 */

int init_lineholder(int nlines);

int insert_line(char *line);

void print_lines();

lineholder.c is correct, however I need to implement memory allocation.

blade71(160)% cat lineholder.c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

static char lines;
lines = *(*lines + MAXLEN) + MAXLINE);
static char (*first) [MAXLEN], (*last) [MAXLEN], (*end) [MAXLEN];
static int wrapped;

void init_lineholder(int lines)
{
if (nlines > MAXLINE)
  nlines = MAXLINE;
  first = last = lines;
  end = lines + nlines;
  wrapped = 0;
}

void insert_line(char *line)
{
  strcpy(*last++, line);

  if (wrapped)
  first = (++first >= end) ?
    lines : first;
    if (last >= end)
      {
        last = lines;
        wrapped = 1;
      }
}

void print_lines(void)
{
  do {
    if (*first != NULL)
      printf("%s", *first);

      first  = (++first >= end) ?
        lines : first;
     }
}

Biggest mess of them all....:

blade71(188)% cat tail.c
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLINE 2000
#define maxlen 1000

int getline(char *line, int max);
int main (int argc, char *argv[])
{
  int nlines = 0;
  char line[MAXLINE];

  if (argc != 2)
  printf("Too few of arguments");
  else
  {
  while (getline(line, MAXLINE) > 0)
    {
      nlines++;
    }
 return nlines;
 print_lines();
  /* Calls init_lineholder(int nlines) with nlines */
  /* Does loop callinggetline and insert_line(char*line) */
  /* When getline returns 0, call print_lines() */
  }
}
  int getline(char *line, int max)
  {
  int nch = 0;
  int c;
  max = max - 1;                        /* leave room for '\0' */

  #ifndef FGETLINE
  while((c = getchar()) != EOF)
  #else
  while((c = getc(fp)) != EOF)
  #endif
    {
      if(c == '\n')
      break;
      if(nch < max)
      {
        line[nch] = c;
        nch = nch + 1;
      }
    }
  if(c == EOF && nch == 0)
  return EOF;
  line[nch] = '\0';
  return nch;
  }

Dani AI

Generated

Good work getting this far, — the high-level algorithm is fine (circular buffer of the last n lines), but the implementation in both files has a few clear problems: the declarations in lineholder.c are not a pointer-array and won’t compile, buffer management needs explicit malloc/free per line, and tail.c returns before calling print_lines and mixes up buffer sizes and function names. Focus on three things: a pointer array for the lines, per-line allocation (and freeing when overwritten), and correct circular indexing.

A straightforward, safe layout for lineholder.c is: keep static char **buf; with capacity, start and count integers. On init allocate the pointer array with calloc(capacity, sizeof *buf) and set indices to zero. For insert: allocate malloc(strlen(line)+1), free the slot you’re about to overwrite if the buffer is full, store the new pointer into (start + count) % capacity (or overwrite start and increment start when full), and update count. For print_lines: iterate count items from start, printf each string, free it, then free(buf) and reset pointers. Check all malloc/calloc returns.

Example (conceptual) pattern:

static char **buf; int capacity, start, count;

int init_lineholder(int n) { buf = calloc(n, sizeof *buf); capacity=n; start=count=0; return buf?0:-1; }

int insert_line(char *s) {
  char *p = malloc(strlen(s)+1); if(!p) return -1; strcpy(p,s);
  if (count < capacity) { buf[(start+count)%capacity]=p; count++; }
  else { free(buf[start]); buf[start]=p; start=(start+1)%capacity; }
  return 0;
}

Tail.c fixes: validate argc==2, parse and clamp n, call init_lineholder(n) then loop reading lines (use fgets or rename your getline to avoid colliding with POSIX getline). After EOF call print_lines() and then return 0;. Always compile with -Wall -Wextra, test with small inputs, and run valgrind to catch leaks and invalid frees.

83 and no response....does it look right? can someone at least point out what I'm doing wrong. Do my algorithms make sense?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.