Write the program
tail
, which prints the lastn
lines of its input. By default,n
is 10, let us say, but it can be changed by an optional argument, si thattail -n
prints the lastn
lines. The program should behave rationally no matter how unresonable the input or the valuen
. Write the program so it makes the best use of available storage; lines should be stored as in the sorting program of section 5.6, not in a two-dimensional array of fixed size.
The exercise can be found on page 133 in K&R.
I will explain the way my program works. First it parses the value of the symbolic argument(if any). Based on the value of the symbolic argument the program will initialize a queue which stores pointer to each line.
Then, the program will read lines and push every line in the queue until the end of files is reached. When the end of file is reached all elements of the queue are printed out.
There are multiple files in my project:
main.c
:
#include <stdio.h>
#include <stdlib.h>
#include "tail.h"
#define MAXLINES 100
#define LINESIZE 100
int main(int argc, char *argv[]) {
int linesNumber = 10;
if(argc > 2) {
return 1;
}
else if(argc == 2) {
linesNumber = atoi(argv[1] + 1);
if(linesNumber == 0) {
return 0;
}
initQueue(linesNumber, LINESIZE);
}
else {
initQueue(linesNumber, LINESIZE);
}
readLines(LINESIZE, MAXLINES);
printQueueElements();
return 0;
}
linesNumber
represent the variable that will store the size of the queue. By default this variable is 10
. If the value of the symbolic argument is 0
there is no reasone to continue reading lines, so the program will terminate execution.
queue.c
#include <stdio.h>
#include <string.h>
#include "alloc.h"
static char **queue;
static int queueSize;
static int head = -1;
static int rear = -1;
static int isEmpty(void) {
return (head == -1 && rear == -1);
}
static int isFull(void) {
return (rear + 1) % queueSize == head ? 1 : 0;
}
void initQueue(int lineNumbers, int sizeOfEachLine) {
int i;
queue = palloc(lineNumbers);
queueSize = lineNumbers;
for(i = 0; i < lineNumbers; i++) {
queue[i] = alloc(sizeOfEachLine);
}
}
char *dequeue(void) {
char *returnValue;
if(isEmpty()) {
return NULL;
}
else if(head == rear) {
returnValue = queue[head];
head = -1;
rear = -1;
return returnValue;
}
returnValue = queue[head];
head = (head + 1) % queueSize;
return returnValue;
}
void enqueue(char *source) {
if(isFull()) {
dequeue();
}
if(isEmpty()) {
rear = 0;
head = 0;
}
else {
rear = (rear + 1) % queueSize;
}
strcpy(queue[rear], source);
}
void printQueueElements(void) {
while(!isEmpty()) {
printf("%s", dequeue());
}
}
This is an array based implementation of a queue. I use the circular array data structure for my queue.
When the queue is full, enqueue
will call dequeue
to make space for the incoming element.
printQueueElements
will dequeue
each element and print it, since I don't need anymore the lines.
readlines.c
:
#include <stdio.h>
#include "alloc.h"
#include "tail.h"
static int getLine(char *source, int lim) {
int c;
int i;
c = getchar();
lim--; /* 1 space for null charachter */
for(i = 0; c != EOF && c != '\n' && lim > 0; i++, c = getchar(), lim--) {
source[i] = c;
}
if(c == '\n') {
source[i] = '\n';
i++;
}
source[i] = '\0';
return i;
}
void readLines(int lineSize, int maxLines) {
char *temp = alloc(lineSize);
for(; maxLines && getLine(temp, lineSize); maxLines--) {
enqueue(temp);
}
}
This function will read each line and enqueue
it.
alloc.c
#include <stdio.h>
#define ALLOCSIZE 10000
static char allocBuf[ALLOCSIZE];
static char *allocp = allocBuf; /* next free position */
char *alloc(int allocSize) {
if(allocBuf + ALLOCSIZE - allocp >= allocSize) {
allocp += allocSize;
return allocp - allocSize;
}
else {
return NULL;
}
}
This function will return a pointer to a block of allocSize
chars
.
palloc.c
#include <stdio.h>
#define BLOCKSIZE 100000
static char *sharedBlock[BLOCKSIZE];
static char **pSharedBlock = sharedBlock;
char **palloc(int size) {
if(sharedBlock + BLOCKSIZE - pSharedBlock >= size) {
pSharedBlock += size;
return pSharedBlock - size;
}
else {
return NULL;
}
}
This function wil return a pointer to a block of size
pointers to char
.
palloc
and alloc
are used to set the size of the queue dynamically. The malloc
function was not introduced at this point, so I don't think it will be a valid solution if I use this function.
alloc.h
char *alloc(int);
char **palloc(int);
tail.h
/* input functions */
void readLines(int lineSize, int maxLines);
/* queue related functions */
void initQueue(int lineNumbers, int sizeOfEachElement);
void enqueue(char *source);
void printQueueElements(void);
char *dequeue(void);
int getSimbolicArgValue(char* source);