#include #include /* Operation mode. */ #ifdef HEAD static int mode = 1; #elif defined(TAIL) static int mode = -1; #else static int mode = 0; #endif /* Line number or count associated with operation. This is positive if measured from start of file, or negative if measured from end of file. */ static int line; /* Current read position. */ static int current_line = 1; static char *buffer = NULL; /* Input buffer. */ static int capacity = 0; /* Buffer capacity. */ static int size = 0; /* Number of bytes read. */ static int eof = 0; /* End of file. */ static char *p, *q; static char *e; static void Flush(void) { fwrite(buffer, q - buffer, 1, stdout); } static void ShiftToFront(char *s) { size -= s - buffer; for(p = buffer; p != buffer + size;) *p++ = *s++; } int main(int argc, char **argv) { if( argc - 2 ) { return printf("%s {line_%s}\n", *argv, mode ? "count" : "number"); } line = atoi(argv[1]); line = mode ? abs(line) * mode : line; while( !eof ) { /* Extend input buffer. */ if( size == capacity ) { if( !(buffer = (char*)realloc(buffer, capacity += 4096)) ) { fprintf(stderr, "Out of memory (need %d)\n", capacity); return 1; } } size += fread(buffer + size, 1, capacity - size, stdin); eof = size < capacity; if( line >= 0 && mode >= 0 ) { /* Scanning forward. */ for(p = q = buffer; p != buffer + size; current_line += *p++ == '\n') { if( mode ? current_line <= line : current_line != line ) *q++ = *p; } Flush(); size = 0; } else if( size && line ) { /* Scanning backward. This block is skipped if we didn't read anything (size==0), or if there is nothing to output (tail!=0 and line==0). */ q = e = buffer + size; q -= q[-1] == '\n'; for(current_line = -1; buffer - q--;) { if( *q == '\n' ) { current_line--; if( current_line < line ) { current_line++; break; } e = q + 1; } } q++; /* "q" now points at the first byte of the desired line, or at the start of the buffer. "e" points at the end of the desired line. */ if( !mode ) { /* Write prefix in slash mode, since we know none of these bytes would be cut. */ Flush(); } /* Move start of desired line and all subsequent bytes to start of buffer. */ ShiftToFront(q); /* If we have read everything, we can write the trailing parts out, optionally skipping over the first line if we are in slash mode. */ if( eof ) { if( !mode && current_line == line ) { ShiftToFront(e -= q - buffer); } q = buffer + size; Flush(); } } } return 0; }