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 <l4/sys/err.h> 23 24 #include <stdio.h> 25 #include <stdlib.h> 26 #include <string.h> 27 28 /* NOTE: For inclusion in the C library: stream acquisition and access. */ 29 30 #include <fsclient/client.h> 31 #include <systypes/env.h> 32 #include <systypes/fcntl.h> 33 34 static file_t *input, *output; 35 static char output_buffer[256]; 36 37 #define _fprintf(file, s, ...) \ 38 ( \ 39 sprintf(output_buffer, s, ##__VA_ARGS__), \ 40 client_write(file, output_buffer, strlen(output_buffer)) \ 41 ) 42 43 44 45 /* Read a line from the file. */ 46 47 static int readline(file_t *file, int *lineno, char **start, char **end) 48 { 49 static char buf[256]; 50 static offset_t current = 0, limit = 0; 51 static int next_lineno = 1; 52 char *newline; 53 54 /* Advance to the next line if previously found. */ 55 56 if (next_lineno > *lineno) 57 *lineno = next_lineno; 58 59 /* Obtain file content. */ 60 61 if (!limit) 62 { 63 limit = client_read(file, buf, 256); 64 65 if (!limit) 66 { 67 *start = NULL; 68 *end = NULL; 69 return 0; 70 } 71 } 72 73 /* Find newline. */ 74 75 newline = (char *) memchr(buf + current, (int) '\n', limit - current); 76 *start = (buf + current); 77 78 /* Return final line fragment, potentially leaving more available text. */ 79 80 if (newline != NULL) 81 { 82 /* Advance the line number when reading again. */ 83 84 next_lineno = (*lineno) + 1; 85 86 /* Define the end of the line and the start of the next line. */ 87 88 *end = newline + 1; 89 current = *end - buf; 90 91 if (current >= limit) 92 { 93 current = 0; 94 limit = 0; 95 } 96 97 return 1; 98 } 99 100 /* Or return mid-line fragment reaching the limit of the available text. */ 101 102 else 103 { 104 *end = buf + limit; 105 current = 0; 106 limit = 0; 107 108 return 1; 109 } 110 } 111 112 113 114 int main(int argc, char *argv[]) 115 { 116 file_t *file; 117 int lineno, startline, numlines; 118 char *start, *end; 119 120 /* NOTE: For inclusion in the C library: stream acquisition and access. */ 121 122 input = client_get_stream(ENV_INPUT_STREAM_NAME, O_RDONLY); 123 output = client_get_stream(ENV_OUTPUT_STREAM_NAME, O_WRONLY); 124 125 if (argc < 4) 126 return 1; 127 128 if (!strcmp(argv[1], "-")) 129 file = input; 130 else 131 file = client_open(argv[1], O_RDONLY); 132 133 if (!client_opened(file)) 134 { 135 if (file != NULL) 136 _fprintf(output, "Error: %s\n", l4sys_errtostr(file->error)); 137 else 138 _fprintf(output, "Could not open file.\n"); 139 140 client_flush(output); 141 return 1; 142 } 143 144 startline = atoi(argv[2]); 145 numlines = atoi(argv[3]); 146 147 lineno = 1; 148 149 while (lineno < startline + numlines) 150 { 151 /* Read a line, updating the current line number. */ 152 153 if (!readline(file, &lineno, &start, &end)) 154 { 155 /* If the last line was passed, an empty line is an acceptable end-of-file 156 condition. */ 157 158 if (lineno >= startline + numlines) 159 break; 160 161 /* Otherwise, indicate that end-of-file occurred. */ 162 163 _fprintf(output, "EOF error at line %d.\n", lineno); 164 client_flush(output); 165 return 1; 166 } 167 168 /* Emit line content while within the desired range of lines. */ 169 170 if ((lineno >= startline) && (lineno < startline + numlines)) 171 client_write(output, start, end - start); 172 } 173 174 client_close(file); 175 176 /* NOTE: For inclusion in the C library: stream acquisition and access. */ 177 178 client_flush(output); 179 180 return 0; 181 } 182 183 /* vim: tabstop=2 expandtab shiftwidth=2 184 */