aboutsummaryrefslogtreecommitdiff
path: root/ch5/5-13_tail.c
blob: 642c7c55a093c4aaaf6e5077322b0c467e31fbd3 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* The C Programming Language: 2nd Edition
 *
 * Exercise 5-13: Write the program `tail`, which prints the last n lines of
 * its input. By default, n is 10, let us say, but it can be changed by an
 * optional argument, so that `tail -n` prints the last n lines. The program
 * should behave rationally no matter how unreasonable the input or the value
 * of n. Write the program so it make 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.
 */

#define MAXLINES 50000
#define MAXLEN 1000
#define DEFAULT_LINES 10
#define ALLOCSIZE 10000000

static char allocbuf[ALLOCSIZE];
static char *allocp = allocbuf;

char *lineptr[MAXLINES];
int numlines;

int mygetline(char *line, int lim) {
	int c, i;
	for (i = 0; i < lim && (c = getchar()) != EOF && c != '\n'; i++) {
		*(line++) = c;
	}
	if (c == '\n') {
		i++; /* I count newlines; some don't */
		*(line++) = c;
	}
	*line = '\0';
	return i;
}

char *alloc(int n) {
	if (allocbuf + ALLOCSIZE - allocp >= n) {
		allocp += n;
		return allocp - n;
	} else {
		return 0;
	}
}

int readlines(char *lineptr[], int maxlines) {
	int len, nlines;
	char *p, line[MAXLEN];
	nlines = 0;
	while ((len = mygetline(line, MAXLEN)) > 0) {
		if (nlines >= maxlines || (p = alloc(len)) == NULL) {
			return -1;
		} else {
			line[len - 1] = '\0';
			strcpy(p, line);
			lineptr[nlines++] = p;
		}
	}
	return nlines;
}

void writelines(char *lineptr[], int sline, int nlines) {
	int i;
	if (sline > 0) {
		i = sline;
	} else {
		i = 0;
	}
	for (; i < nlines && lineptr[i] != NULL; i++) {
		printf("%s\n", lineptr[i]);
	}
}

void parse_args(int argc, char *argv[]) {
	if (argc == 2 && *(++argv)[0] == '-') {
		int i = 1;
		char arg[10];
		for (i = 1; i < 10 && (*argv)[i] != '\0'; i++) {
			arg[i - 1] = (*argv)[i];
		}
		numlines = atoi(arg);
	} else {
		numlines = DEFAULT_LINES;
	}
}

int main(int argc, char *argv[]) {
	int nlines;
	parse_args(argc, argv);
	if ((nlines = readlines(lineptr, MAXLINES)) >= 0) {
		writelines(lineptr, nlines - numlines, nlines);
		return 0;
	} else {
		/* we should repeat ourselves here so tail fails, but gracefully. We
		 * _did_ gather some input, and should print it! */
		nlines = MAXLINES;
		writelines(lineptr, nlines - numlines, nlines);
		printf("-----------------------\n");
		printf("ERROR: Input too large.\n");
		return 1;
	}
}