/* Remove a single line from input. Usage: ./slash {line_number} < input.txt > output.txt Where {line_number} is the line to be removed. Use positive integer to measure from start of file, or negative integer to measure from end of file. `./slash 123` is equivalent to `sed -e '123d'`. "cut" might have been a better name for this tool, since we are cutting one line from the input, but there is already a cut(1) tool that behaves very differently from this tool. There isn't a standard "slash" or "slice" tool so either of these would work. Of these, "slice" sounds like it's extracting a single slice, so "slash" seems like a better fit. */ #include #include #include int main(int argc, char **argv) { char *buffer = NULL; int capacity = 0, size = 0; int line_number, current_line; char *r, *w, *e; if( argc != 2 ) return printf("%s {line_number}\n", *argv); line_number = atoi(argv[1]); /* Load entire file to memory. */ do { if( size == capacity ) { capacity = (capacity != 0 ? capacity * 2 : 256); if( (buffer = (char*)realloc(buffer, capacity)) == NULL ) { fprintf(stderr, "Out of memory (need %d)\n", capacity); return 1; } } size += fread(buffer + size, 1, capacity - size, stdin); } while( size == capacity ); if( line_number >= 0 ) { /* Scan forward and skip over requested line. */ current_line = 1; for(r = w = buffer; r != buffer + size; r++) { if( current_line != line_number ) *w++ = *r; if( *r == '\n' ) current_line++; } fwrite(buffer, w - buffer, 1, stdout); } else { if( size > 0 ) { /* Scan backward for the requested line. */ r = e = buffer + size; if( r[-1] == '\n' ) r--; current_line = -1; for(; r-- != buffer;) { if( *r == '\n' ) { current_line--; if( current_line < line_number ) { current_line++; break; } e = r + 1; } } r++; /* Overwrite the line that we want to skip over. */ if( current_line == line_number ) { memmove(r, e, size - (e - buffer)); size -= e - r; } fwrite(buffer, size, 1, stdout); } } free(buffer); return 0; }