|
3 | 3 | * input. By default, n is set to 10, let us say, but it can be changed by an |
4 | 4 | * optional argument so that |
5 | 5 | * |
6 | | - * tail -n |
| 6 | + * tail -n |
7 | 7 | * |
8 | 8 | * prints the last n lines. The program should behave rationally no matter how |
9 | | - * unreasonable the input or value of n. Write the program so it makes the |
10 | | - * best use of available storage; lines should be stored as in the sorting |
11 | | - * program of section 5.6, not in a two-dimensional array of fixed size. |
| 9 | + * unreasonable the input or value of n. Write the program so it makes the best |
| 10 | + * use of available storage; lines should be stored as in the sorting program |
| 11 | + * of section 5.6, not in a two-dimensional array of fixed size. |
| 12 | + * |
12 | 13 | * By Faisal Saadatmand |
13 | 14 | */ |
14 | 15 |
|
15 | 16 | #include <stdio.h> |
16 | | -#include <string.h> |
17 | 17 | #include <stdlib.h> |
18 | | -#include <ctype.h> |
| 18 | +#include <string.h> |
| 19 | + |
| 20 | +#define MAXLEN 1000 /* max length of any input line */ |
| 21 | +#define MAXLINES 5000 /* max #lines to be stored */ |
| 22 | +#define ALLOCSIZE 10000 /* size of available space */ |
| 23 | + |
| 24 | +static char allocbuf[ALLOCSIZE]; /* storage for alloc */ |
| 25 | +static char *allocp = allocbuf; /* next free position */ |
19 | 26 |
|
20 | | -#define MAXLINES 5000 /* max #lines to be stored */ |
21 | | -#define MAXLEN 1000 /* max length of any input line */ |
22 | | -#define ALLOCSIZE 100000 /* storage for alloc */ |
23 | | -#define N 10 /* default value of last lines to print */ |
24 | 27 |
|
25 | 28 | /* functions */ |
26 | | -int readlines(char *[], int); |
27 | | -int getLine(char *, int); |
| 29 | +int getLine(char *, int); |
28 | 30 | char *alloc(int); |
29 | | -int isDigitStr(char *[]); |
| 31 | +int readlines(char *[], int); |
| 32 | +void writelines(char *[], int); |
| 33 | +int expandArg(int, char **, int *); |
30 | 34 |
|
31 | | -/* globals */ |
32 | | -char *lineptr[MAXLINES]; /* pointers to text lines */ |
33 | | -static char allocbuf[ALLOCSIZE]; /* storage for alloc */ |
34 | | -static char *allocp = allocbuf; /* next free position */ |
| 35 | +/* getLine function: read a line into s, return length */ |
| 36 | +int getLine(char *s, int lim) |
| 37 | +{ |
| 38 | + int c; |
| 39 | + char *len; |
| 40 | + |
| 41 | + len = s; |
| 42 | + while (--lim > 0 && (c = getchar()) != EOF && c != '\n') |
| 43 | + *s++ = c; |
| 44 | + if (c == '\n') |
| 45 | + *s++ = c; |
| 46 | + *s = '\0'; |
| 47 | + return strlen(len); |
| 48 | +} |
| 49 | + |
| 50 | +/* alloc: return pointer to n characters */ |
| 51 | +char *alloc(int n) |
| 52 | +{ |
| 53 | + if (allocbuf + ALLOCSIZE - allocp >= n) { /* it fits */ |
| 54 | + allocp += n; |
| 55 | + return allocp - n; /*old p */ |
| 56 | + } |
| 57 | + return NULL; /* not enough room */ |
| 58 | +} |
35 | 59 |
|
36 | 60 | /* readlines: read input lines */ |
37 | | -int readlines(char *lineptr[], int maxlines) |
| 61 | +int readlines(char *linerptr[], int maxlines) |
38 | 62 | { |
39 | 63 | int len, nlines; |
40 | 64 | char *p, line[MAXLEN]; |
41 | 65 |
|
42 | 66 | nlines = 0; |
43 | | - while ((len = getLine(line, MAXLEN)) > 0) |
| 67 | + while ((len = getLine(line, MAXLEN)) > 0) { |
44 | 68 | if (nlines >= maxlines || (p = alloc(len)) == NULL) |
45 | 69 | return -1; |
46 | | - else { |
47 | | - line[len - 1] = '\0'; /* delete newline character */ |
48 | | - strcpy(p, line); |
49 | | - lineptr[nlines++] = p; |
50 | | - } |
51 | | - return nlines; |
52 | | -} |
53 | | - |
54 | | -/* getLine: get line into s, return length of s -- pointer version */ |
55 | | -int getLine(char *s, int lim) |
56 | | -{ |
57 | | - int c, len; |
58 | | - |
59 | | - len = 0; |
60 | | - while (--lim > 0 && (c = getchar()) != EOF && c != '\n') { |
61 | | - *s++ = c; |
62 | | - ++len; |
63 | | - } |
64 | | - if ( c == '\n') { |
65 | | - *s++ = c; |
66 | | - ++len; |
| 70 | + line[len - 1] = '\0'; /* delete newline */ |
| 71 | + strcpy(p, line); |
| 72 | + linerptr[nlines++] = p; |
67 | 73 | } |
68 | | - *s = '\0'; |
69 | | - return len; |
| 74 | + return nlines; |
70 | 75 | } |
71 | 76 |
|
72 | | -/* alloc: allocate memory */ |
73 | | -char *alloc(int n) /* return pointer to n characters */ |
| 77 | +/* writelines: write output lines */ |
| 78 | +void writelines(char *lineptr[], int nlines) |
74 | 79 | { |
75 | | - if (allocbuf + ALLOCSIZE - allocp >=n) { /* it fits */ |
76 | | - allocp += n; |
77 | | - return allocp - n; /* old p */ |
78 | | - } else /* not enough room */ |
79 | | - return 0; |
| 80 | + while (nlines-- > 0) |
| 81 | + printf("%s\n", *lineptr++); |
80 | 82 | } |
81 | 83 |
|
82 | | -/* isDigitStr: check if string is made of positive integers characters. Return |
83 | | - * 1 if true; 0 if false */ |
84 | | -int isDigitStr(char *s[]) |
| 84 | +int expandArg(int count, char **list, int *n) |
85 | 85 | { |
86 | | - int i; |
| 86 | + char *prog = *list, *end; |
87 | 87 |
|
88 | | - for (i = 0; (*s)[i]; ++i) /* omitted '\0', since s is a pointer */ |
89 | | - if (!isdigit((*s)[i])) |
| 88 | + while (--count > 0) |
| 89 | + if (!strcmp(*++list, "-n")) { |
| 90 | + end = NULL; |
| 91 | + if (!(*n = strtol(*++list, &end, 10)) || *end) { |
| 92 | + printf("%s: invalid number of lines: %s\n", prog, *list); |
90 | 93 | return 0; |
| 94 | + } |
| 95 | + *n = abs(*n); /* treat negative numbers as positives */ |
| 96 | + --count; |
| 97 | + } else { |
| 98 | + printf("Usage: %s [-n lines]\n", prog); |
| 99 | + return 0; |
| 100 | + } |
91 | 101 | return 1; |
92 | 102 | } |
93 | 103 |
|
94 | 104 | int main(int argc, char *argv[]) |
95 | 105 | { |
96 | | - int nlines; /* number of input lines read */ |
97 | | - int type; /* type of argument operator */ |
98 | | - int i; /* index variable */ |
99 | | - int n; /* cli argument value of n */ |
100 | | - |
101 | | - while (--argc > 0) { |
102 | | - type = *(++argv)[0]; |
103 | | - switch (type) { |
104 | | - case ('-'): |
105 | | - n = *++argv[0]; |
106 | | - if (isDigitStr(argv)) { |
107 | | - n = atoi(*argv); |
108 | | - argc = 0; /* signal to exit loop */ |
109 | | - } |
110 | | - else { |
111 | | - n = 0; /* signal invalid value */ |
112 | | - argc =0; |
113 | | - } |
114 | | - break; |
115 | | - default: |
116 | | - n = 0; /* signal invalid input */ |
117 | | - argc = 0; |
118 | | - break; |
119 | | - } |
120 | | - } |
| 106 | + char *lineptr[MAXLINES]; /* pointers to text lines */ |
| 107 | + int nlines; /* number of read lines */ |
| 108 | + int n = 10; /* default last n lines to print */ |
121 | 109 |
|
122 | | - if ((nlines = readlines(lineptr, MAXLINES)) >= 0) { |
123 | | - if (n <= 0 || n > nlines) /* check input or value of argument */ |
124 | | - n = N; /* default to N, if unreasonable */ |
125 | | - for (i = 0; i < n; ++i) |
126 | | - printf("%s\n", lineptr[(nlines - n) + i]); |
127 | | - return 0; |
128 | | - } else { |
129 | | - printf("error: input too big to sort\n"); |
130 | | - return 1; |
| 110 | + if (!expandArg(argc, argv, &n)) |
| 111 | + return -1; |
| 112 | + if ((nlines = readlines(lineptr, MAXLINES)) < 0) { |
| 113 | + printf("Error: input is too big\n"); |
| 114 | + return -1; |
131 | 115 | } |
| 116 | + if (n > nlines) /* max limit check */ |
| 117 | + n = nlines; /* print entire input, not more */ |
| 118 | + writelines(lineptr + nlines - n, n); |
| 119 | + return 0; |
132 | 120 | } |
0 commit comments