#include #include /* Operation mode. */ #ifdef HEAD static int head = 1; #else static int head = 0; #endif #ifdef TAIL static int tail = 1; #else static int tail = 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 *r, *w; /* General purpose pointers inside buffer. */ static char *e; /* End of previous line (1 byte after newline). */ static char *s; /* Source address for ShiftToFront. */ static void ShiftToFront(void) { for(w = buffer; w != buffer + size;) *w++ = *s++; } static void Flush(void) { fwrite(buffer, w - buffer, 1, stdout); } int main(int argc, char **argv) { if( argc != 2 ) { return printf("%s {line_%s}\n", *argv, head || tail ? "count" : "number"); } line = atoi(argv[1]); if( head ) line = abs(line); else if( tail ) line = -abs(line); while( !eof ) { /* Extend input buffer. */ if( size == capacity ) { if( (buffer = (char*)realloc(buffer, capacity += 4096)) == NULL ) { 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 ) { /* Scanning forward. */ for(r = w = buffer; r != buffer + size; r++) { if( (head && current_line <= line) || (!head && current_line != line) ) { *w++ = *r; } if( *r == '\n' ) current_line++; } if( !tail ) { /* If tail mode is called with line count set to zero, we will end up in the forward scanning branch. We will drop all output bytes in that case. */ Flush(); } size = 0; } else { /* Scanning backward. */ if( size == 0 ) continue; r = e = buffer + size; if( r[-1] == '\n' ) r--; current_line = -1; for(; r-- != buffer;) { if( *r == '\n' ) { current_line--; if( current_line < line ) { current_line++; break; } e = r + 1; } } r++; /* "r" 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( !tail ) { /* Write prefix in slash mode, since we know none of these bytes would be cut. */ w = r; Flush(); } /* Move start of desired line and all subsequent bytes to start of buffer. */ s = r; size -= (r - buffer); ShiftToFront(); /* 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( !tail && current_line == line ) { e -= r - buffer; size -= (e - buffer); s = e; ShiftToFront(); } w = buffer + size; Flush(); } } } return 0; }