From eb44b112e7ac2ffbd7d48d47f26c5e210fed677c Mon Sep 17 00:00:00 2001 From: zlg Date: Fri, 18 Oct 2013 02:14:57 -0500 Subject: Solve Exercise 5-13: tail MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This exercise was somewhat irritating until I caved and used the code in section 5.6 like the exercise suggested. The solution is not what I think is clean, but given that [mc]*alloc haven't been covered yet, it's probably the best one can muster. Once I used the code in ยง5.6, the program fell into place. Still, it was a neat exercise. --- ch5/5-13_tail.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 106 insertions(+) create mode 100644 ch5/5-13_tail.c diff --git a/ch5/5-13_tail.c b/ch5/5-13_tail.c new file mode 100644 index 0000000..3a8ea30 --- /dev/null +++ b/ch5/5-13_tail.c @@ -0,0 +1,106 @@ +#include +#include +#include + +/* 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; + } +} -- cgit v1.2.3-70-g09d2 zlg7-10/+26 2018-10-10cli: Add "export" commandzlg2-5/+54 2018-10-10cli: Add "import" commandzlg5-1/+76 2018-10-09Bump to 0.3alpha6 for PyPIzlg1-1/+1 2018-10-09cli: Add "notes" commandzlg2-4/+74 2018-10-09update_game: ensure notes are also savedzlg1-2/+2 2018-10-09cli: add 'update' commandzlg3-20/+92 2018-10-06cli: Add "delete" commandzlg2-0/+19 2018-10-06Remove ID field from DBzlg3-38/+46 2018-10-06cli: change "Status" heading to "Progress"zlg2-36/+40 2018-09-29Bump to 0.3alpha5 for PyPIzlg1-1/+1 2018-09-29cli: Add pretty printing to 'list' commandzlg3-17/+107 2018-09-08setup.py: Bump to alpha4 for PyPIzlg1-1/+1 2018-09-08cli: add '--raw' option to list commandzlg2-9/+45 2018-09-08Add remaining filters to vgstash packagezlg1-2/+11 2018-09-04Update LICENSE to match setup.pyzlg1-80/+67 2018-09-03Branch off from master with pytest, tox, clickzlg16-778/+779 2018-03-18Flesh out filter types and ownership statuszlg3-82/+144 2018-03-18README.mdown: break line correctlyzlg1-1/+1 2018-03-18add 'playlog' list filterzlg2-2/+9 2018-03-13Update helpers a bitzlg1-2/+9 2018-03-13Make VGSTASH_DB_LOCATION point to a filezlg2-21/+20 2016-11-18Remove settings from helpers.shZe Libertine Gamer1-5/+0 2016-11-15Correct phrasing in README.Ze Libertine Gamer1-4/+4 2016-11-13DerpZe Libertine Gamer1-0/+1 2016-11-03Improve error handling in shell scriptsZe Libertine Gamer4-3/+23 2016-10-24Correct run_again, add recursionZe Libertine Gamer1-0/+4 2016-10-21Add quotes to correct behavior for arglistZe Libertine Gamer1-1/+1 2016-10-14updater.sh: add recursion, error handlingZe Libertine Gamer1-43/+101 2016-10-14Correct pipe-handling behaviorZe Libertine Gamer1-1/+9 2016-10-12Clarify a method to move between platformsZe Libertine Gamer1-2/+5