views:

175

answers:

2
#include <stdio.h>

#define MAXLINES    5000    /* Maximum number of lines to display. */

char *lineptr[MAXLINES];    /* Pointer to input lines. */

#define BUFFERSIZE 1000

#define DEFAULT_LAST 10

int readlines(char *lineptr[], char *buffer, int maxlines);
static void unwrap(char *buffer, int index);
static void reverse(char *lineptr[], int nlines);

main(int argc, char *argv[])
{
    int nlines, i, last, offset;
    char buffer[BUFFERSIZE];
    char *p;

    last = DEFAULT_LAST;
    for (i = 0; i < argc; i++) {
        p = argv[i];
        if (*p++ == '-') {
            last = 0;
            while (isdigit(*p)) {
                last = last * 10 + *p - '0';
                p++;
            }
            if (*p != '\0') {
                printf("invalid argument: %s\n", argv[i]);
                last = DEFAULT_LAST;
            }
        }
    }

    nlines = readlines(lineptr, buffer, MAXLINES);
    if (nlines < 0) {
        printf("error: input too big to process\n");
        return 1;
    }
    if (nlines < last) {
        printf("error: only printing the last %d lines.\n", nlines);
        offset = 0;
    } else if (last > MAXLINES) {
        offset = nlines - MAXLINES;
    } else {
        offset = nlines - last;
    }
    for (i = 0; i < nlines && i < last; i++)
        printf("%s\n", lineptr[offset + i]);

    return 0;
}

int readlines(char *lineptr[], char *buffer, int maxlines)
{
    int c, nlines;
    int wrapped;
    char *p;

    /* The input lines are stored end-to-end in the buffer, with
       newlines converted to null bytes. */
    wrapped = 0;
    p = buffer;
    while ((c = getchar()) != EOF) {
        if (c == '\n')
            *p = '\0';
        else
            *p = c;
        p++;
        if (p >= buffer + BUFFERSIZE) {
            p = buffer;
            wrapped = 1;
        }
    }
    /* Rearrange the buffer so the oldest byte comes first. */
    if (wrapped) {
        unwrap(buffer, p - buffer);
        p = buffer + BUFFERSIZE;
    }
    p--;
    *p = '\0';
    nlines = 0;
    while (p >= buffer && nlines < maxlines) {
        p--;
        if (*p == '\0')
            lineptr[nlines++] = p + 1;
    }
    reverse(lineptr, nlines);

    return nlines;
}

static void unwrap(char *buffer, int index)
{
    char work[BUFFERSIZE];

    memmove(work, buffer + index, BUFFERSIZE - index);
    memmove(work + BUFFERSIZE - index, buffer, index);
    memmove(buffer, work, BUFFERSIZE);

    return;
}

static void reverse(char *lineptr[], int nlines)
{
    char *tmp;
    int i;

    for (i = 0; i < nlines / 2; i++) {
        tmp = lineptr[i];
        lineptr[i] = lineptr[nlines - i - 1];
        lineptr[nlines - i - 1] = tmp;
    }
return;
}

This program prints last -n lines of input, storing lines into array of pointers.

In readlines functions, if pointer to buffer crosses its maximum size it gets wraped. But i dont understand what does the wraping/unwraping function do exactly. Can someone explain it to me? The way wrap works and why didnt the writer of this code just return -1 if the buffer overflowed?

A: 

This program reads all the lines into an array of lines. Each element in the array has fixed size. If a line is longer than the max size of a line it "wraps" it and restarts filling the buffer at the beginning of the buffer.

Unwrap then puts the oldest stuff at the end so the line looks truncated from the beginning of the line. (A 12 character line in a 10 character buffer would show the last 10 characters starting at the 3rd character.)

Hogan
+4  A: 

To demonstrate the principle: say you were putting 10 characters, '0' to '9', into an 8-byte buffer, using the same scheme:

After 7 characters:

+---+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |   |
+---+---+---+---+---+---+---+---+
  ^                           ^
buffer                        p

After the 8th character:

+---+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+---+
  ^                               ^
buffer                            p

so now p is reset and wrapped is set to 1:

+---+---+---+---+---+---+---+---+
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+---+
  ^
buffer
  p

After the 10th character:

+---+---+---+---+---+---+---+---+
| 8 | 9 | 2 | 3 | 4 | 5 | 6 | 7 |
+---+---+---+---+---+---+---+---+
  ^       ^
buffer    p

Now the unwrap() code rearranges the buffer to look like this:

+---+---+---+---+---+---+---+---+
| 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
+---+---+---+---+---+---+---+---+
  ^                               ^
buffer                            p

The program is doing this (rather than just giving up) so that it will still work even if the file is much larger than the buffer. (Unless the total length of the last 10 lines is larger than the buffer, in which case some of the earlier of the last 10 lines will be lost).

Matthew Slattery
@Matthew, nice diagrams!
Hogan
Thank you :), this explained it!
Tool