1 /* 2 * Show lines from a file. 3 * 4 * Copyright (C) 2022, 2023, 2024 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <stdio.h> 23 #include <stdlib.h> 24 #include <string.h> 25 26 27 28 /* Read a line from the file. */ 29 30 static int readline(FILE *file, int *lineno, char **start, char **end) 31 { 32 static char buf[256]; 33 static offset_t current = 0, limit = 0; 34 static int next_lineno = 1; 35 char *newline; 36 37 /* Advance to the next line if previously found. */ 38 39 if (next_lineno > *lineno) 40 *lineno = next_lineno; 41 42 /* Obtain file content. */ 43 44 if (!limit) 45 { 46 limit = fread(buf, sizeof(char), 256, file); 47 48 if (!limit) 49 { 50 *start = NULL; 51 *end = NULL; 52 return 0; 53 } 54 } 55 56 /* Find newline. */ 57 58 newline = (char *) memchr(buf + current, (int) '\n', limit - current); 59 *start = (buf + current); 60 61 /* Return final line fragment, potentially leaving more available text. */ 62 63 if (newline != NULL) 64 { 65 /* Advance the line number when reading again. */ 66 67 next_lineno = (*lineno) + 1; 68 69 /* Define the end of the line and the start of the next line. */ 70 71 *end = newline + 1; 72 current = *end - buf; 73 74 if (current >= limit) 75 { 76 current = 0; 77 limit = 0; 78 } 79 80 return 1; 81 } 82 83 /* Or return mid-line fragment reaching the limit of the available text. */ 84 85 else 86 { 87 *end = buf + limit; 88 current = 0; 89 limit = 0; 90 91 return 1; 92 } 93 } 94 95 96 97 int main(int argc, char *argv[]) 98 { 99 FILE *file; 100 int lineno, startline, numlines; 101 char *start, *end; 102 103 if (argc < 4) 104 return 1; 105 106 if (!strcmp(argv[1], "-")) 107 file = stdin; 108 else 109 file = fopen(argv[1], "r"); 110 111 if (file == NULL) 112 { 113 fprintf(stderr, "Could not open file.\n"); 114 return 1; 115 } 116 117 startline = atoi(argv[2]); 118 numlines = atoi(argv[3]); 119 120 lineno = 1; 121 122 while (lineno < startline + numlines) 123 { 124 /* Read a line, updating the current line number. */ 125 126 if (!readline(file, &lineno, &start, &end)) 127 { 128 /* If the last line was passed, an empty line is an acceptable end-of-file 129 condition. */ 130 131 if (lineno >= startline + numlines) 132 break; 133 134 /* Otherwise, indicate that end-of-file occurred. */ 135 136 fprintf(stderr, "EOF error at line %d.\n", lineno); 137 return 1; 138 } 139 140 /* Emit line content while within the desired range of lines. */ 141 142 if ((lineno >= startline) && (lineno < startline + numlines)) 143 fwrite(start, sizeof(char), end - start, stdout); 144 } 145 146 fclose(file); 147 148 return 0; 149 } 150 151 /* vim: tabstop=2 expandtab shiftwidth=2 152 */