1 1.12 rillig /* $NetBSD: io.c,v 1.12 2025/09/17 20:35:11 rillig Exp $ */ 2 1.2 cgd 3 1.1 alm /* io.c: This file contains the i/o routines for the ed line editor */ 4 1.1 alm /*- 5 1.1 alm * Copyright (c) 1993 Andrew Moore, Talke Studio. 6 1.1 alm * All rights reserved. 7 1.1 alm * 8 1.1 alm * Redistribution and use in source and binary forms, with or without 9 1.1 alm * modification, are permitted provided that the following conditions 10 1.1 alm * are met: 11 1.1 alm * 1. Redistributions of source code must retain the above copyright 12 1.1 alm * notice, this list of conditions and the following disclaimer. 13 1.1 alm * 2. Redistributions in binary form must reproduce the above copyright 14 1.1 alm * notice, this list of conditions and the following disclaimer in the 15 1.1 alm * documentation and/or other materials provided with the distribution. 16 1.1 alm * 17 1.1 alm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 1.1 alm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 1.1 alm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 1.1 alm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 1.1 alm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 1.1 alm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 1.1 alm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 1.1 alm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 1.1 alm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 1.1 alm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 1.1 alm * SUCH DAMAGE. 28 1.1 alm */ 29 1.1 alm 30 1.4 thorpej #include <sys/cdefs.h> 31 1.1 alm #ifndef lint 32 1.2 cgd #if 0 33 1.1 alm static char *rcsid = "@(#)io.c,v 1.1 1994/02/01 00:34:41 alm Exp"; 34 1.2 cgd #else 35 1.12 rillig __RCSID("$NetBSD: io.c,v 1.12 2025/09/17 20:35:11 rillig Exp $"); 36 1.2 cgd #endif 37 1.1 alm #endif /* not lint */ 38 1.1 alm 39 1.1 alm #include "ed.h" 40 1.1 alm 41 1.1 alm 42 1.1 alm /* read_file: read a named file/pipe into the buffer; return line count */ 43 1.1 alm long 44 1.7 xtraeme read_file(char *fn, long n) 45 1.1 alm { 46 1.1 alm FILE *fp; 47 1.1 alm long size; 48 1.1 alm 49 1.1 alm 50 1.1 alm fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); 51 1.1 alm if (fp == NULL) { 52 1.1 alm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 53 1.10 dholland seterrmsg("cannot open input file"); 54 1.1 alm return ERR; 55 1.1 alm } else if ((size = read_stream(fp, n)) < 0) 56 1.1 alm return ERR; 57 1.1 alm else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { 58 1.1 alm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 59 1.10 dholland seterrmsg("cannot close input file"); 60 1.1 alm return ERR; 61 1.1 alm } 62 1.9 joerg if (!scripted) 63 1.9 joerg fprintf(stderr, "%lu\n", size); 64 1.1 alm return current_addr - n; 65 1.1 alm } 66 1.1 alm 67 1.1 alm 68 1.1 alm char *sbuf; /* file i/o buffer */ 69 1.1 alm int sbufsz; /* file i/o buffer size */ 70 1.1 alm int newline_added; /* if set, newline appended to input file */ 71 1.1 alm 72 1.1 alm /* read_stream: read a stream into the editor buffer; return status */ 73 1.1 alm long 74 1.7 xtraeme read_stream(FILE *fp, long n) 75 1.1 alm { 76 1.1 alm line_t *lp = get_addressed_line_node(n); 77 1.1 alm undo_t *up = NULL; 78 1.1 alm unsigned long size = 0; 79 1.1 alm int o_newline_added = newline_added; 80 1.1 alm int o_isbinary = isbinary; 81 1.1 alm int appended = (n == addr_last); 82 1.1 alm int len; 83 1.1 alm 84 1.1 alm isbinary = newline_added = 0; 85 1.1 alm if (des) 86 1.1 alm init_des_cipher(); 87 1.1 alm for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) { 88 1.1 alm SPL1(); 89 1.1 alm if (put_sbuf_line(sbuf) == NULL) { 90 1.1 alm SPL0(); 91 1.1 alm return ERR; 92 1.1 alm } 93 1.1 alm lp = lp->q_forw; 94 1.1 alm if (up) 95 1.1 alm up->t = lp; 96 1.1 alm else if ((up = push_undo_stack(UADD, current_addr, 97 1.1 alm current_addr)) == NULL) { 98 1.1 alm SPL0(); 99 1.1 alm return ERR; 100 1.1 alm } 101 1.1 alm SPL0(); 102 1.1 alm } 103 1.1 alm if (len < 0) 104 1.1 alm return ERR; 105 1.1 alm if (appended && size && o_isbinary && o_newline_added) 106 1.1 alm fputs("newline inserted\n", stderr); 107 1.4 thorpej else if (newline_added && (!appended || (!isbinary && !o_isbinary))) 108 1.1 alm fputs("newline appended\n", stderr); 109 1.1 alm if (isbinary && newline_added && !appended) 110 1.12 rillig size += 1; 111 1.1 alm if (!size) 112 1.1 alm newline_added = 1; 113 1.1 alm newline_added = appended ? newline_added : o_newline_added; 114 1.1 alm isbinary = isbinary | o_isbinary; 115 1.1 alm if (des) 116 1.1 alm size += 8 - size % 8; /* adjust DES size */ 117 1.1 alm return size; 118 1.1 alm } 119 1.1 alm 120 1.1 alm 121 1.1 alm /* get_stream_line: read a line of text from a stream; return line length */ 122 1.1 alm int 123 1.7 xtraeme get_stream_line(FILE *fp) 124 1.1 alm { 125 1.3 tls int c; 126 1.3 tls int i = 0; 127 1.1 alm 128 1.4 thorpej while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) && 129 1.4 thorpej !ferror(fp))) && c != '\n') { 130 1.1 alm REALLOC(sbuf, sbufsz, i + 1, ERR); 131 1.1 alm if (!(sbuf[i++] = c)) 132 1.1 alm isbinary = 1; 133 1.1 alm } 134 1.1 alm REALLOC(sbuf, sbufsz, i + 2, ERR); 135 1.1 alm if (c == '\n') 136 1.1 alm sbuf[i++] = c; 137 1.1 alm else if (ferror(fp)) { 138 1.1 alm fprintf(stderr, "%s\n", strerror(errno)); 139 1.10 dholland seterrmsg("cannot read input file"); 140 1.1 alm return ERR; 141 1.1 alm } else if (i) { 142 1.1 alm sbuf[i++] = '\n'; 143 1.1 alm newline_added = 1; 144 1.1 alm } 145 1.1 alm sbuf[i] = '\0'; 146 1.1 alm return (isbinary && newline_added && i) ? --i : i; 147 1.1 alm } 148 1.1 alm 149 1.1 alm 150 1.1 alm /* write_file: write a range of lines to a named file/pipe; return line count */ 151 1.1 alm long 152 1.8 christos write_file(const char *fn, const char *mode, long n, long m) 153 1.1 alm { 154 1.1 alm FILE *fp; 155 1.1 alm long size; 156 1.1 alm 157 1.1 alm fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); 158 1.1 alm if (fp == NULL) { 159 1.1 alm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 160 1.10 dholland seterrmsg("cannot open output file"); 161 1.1 alm return ERR; 162 1.1 alm } else if ((size = write_stream(fp, n, m)) < 0) 163 1.1 alm return ERR; 164 1.1 alm else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { 165 1.1 alm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 166 1.10 dholland seterrmsg("cannot close output file"); 167 1.1 alm return ERR; 168 1.1 alm } 169 1.9 joerg if (!scripted) 170 1.9 joerg fprintf(stderr, "%lu\n", size); 171 1.1 alm return n ? m - n + 1 : 0; 172 1.1 alm } 173 1.1 alm 174 1.1 alm 175 1.1 alm /* write_stream: write a range of lines to a stream; return status */ 176 1.1 alm long 177 1.7 xtraeme write_stream(FILE *fp, long n, long m) 178 1.1 alm { 179 1.1 alm line_t *lp = get_addressed_line_node(n); 180 1.1 alm unsigned long size = 0; 181 1.1 alm char *s; 182 1.1 alm int len; 183 1.1 alm 184 1.1 alm if (des) 185 1.1 alm init_des_cipher(); 186 1.1 alm for (; n && n <= m; n++, lp = lp->q_forw) { 187 1.1 alm if ((s = get_sbuf_line(lp)) == NULL) 188 1.1 alm return ERR; 189 1.1 alm len = lp->len; 190 1.1 alm if (n != addr_last || !isbinary || !newline_added) 191 1.1 alm s[len++] = '\n'; 192 1.1 alm if (put_stream_line(fp, s, len) < 0) 193 1.1 alm return ERR; 194 1.1 alm size += len; 195 1.1 alm } 196 1.1 alm if (des) { 197 1.1 alm flush_des_file(fp); /* flush buffer */ 198 1.1 alm size += 8 - size % 8; /* adjust DES size */ 199 1.1 alm } 200 1.1 alm return size; 201 1.1 alm } 202 1.1 alm 203 1.1 alm 204 1.1 alm /* put_stream_line: write a line of text to a stream; return status */ 205 1.1 alm int 206 1.7 xtraeme put_stream_line(FILE *fp, char *s, int len) 207 1.1 alm { 208 1.1 alm while (len--) 209 1.1 alm if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) { 210 1.1 alm fprintf(stderr, "%s\n", strerror(errno)); 211 1.10 dholland seterrmsg("cannot write file"); 212 1.1 alm return ERR; 213 1.1 alm } 214 1.1 alm return 0; 215 1.1 alm } 216 1.1 alm 217 1.1 alm /* get_extended_line: get a an extended line from stdin */ 218 1.1 alm char * 219 1.7 xtraeme get_extended_line(int *sizep, int nonl) 220 1.1 alm { 221 1.1 alm static char *cvbuf = NULL; /* buffer */ 222 1.1 alm static int cvbufsz = 0; /* buffer size */ 223 1.1 alm 224 1.1 alm int l, n; 225 1.1 alm char *t = ibufp; 226 1.1 alm 227 1.1 alm while (*t++ != '\n') 228 1.1 alm ; 229 1.1 alm if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) { 230 1.1 alm *sizep = l; 231 1.1 alm return ibufp; 232 1.1 alm } 233 1.1 alm *sizep = -1; 234 1.1 alm REALLOC(cvbuf, cvbufsz, l, NULL); 235 1.1 alm memcpy(cvbuf, ibufp, l); 236 1.12 rillig *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ 237 1.12 rillig if (nonl) l--; /* strip newline */ 238 1.1 alm for (;;) { 239 1.1 alm if ((n = get_tty_line()) < 0) 240 1.1 alm return NULL; 241 1.1 alm else if (n == 0 || ibuf[n - 1] != '\n') { 242 1.10 dholland seterrmsg("unexpected end-of-file"); 243 1.1 alm return NULL; 244 1.1 alm } 245 1.1 alm REALLOC(cvbuf, cvbufsz, l + n, NULL); 246 1.1 alm memcpy(cvbuf + l, ibuf, n); 247 1.1 alm l += n; 248 1.1 alm if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1)) 249 1.1 alm break; 250 1.12 rillig *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ 251 1.12 rillig if (nonl) l--; /* strip newline */ 252 1.1 alm } 253 1.1 alm REALLOC(cvbuf, cvbufsz, l + 1, NULL); 254 1.1 alm cvbuf[l] = '\0'; 255 1.1 alm *sizep = l; 256 1.1 alm return cvbuf; 257 1.1 alm } 258 1.1 alm 259 1.1 alm 260 1.1 alm /* get_tty_line: read a line of text from stdin; return line length */ 261 1.1 alm int 262 1.7 xtraeme get_tty_line(void) 263 1.1 alm { 264 1.3 tls int oi = 0; 265 1.3 tls int i = 0; 266 1.1 alm int c; 267 1.1 alm 268 1.1 alm for (;;) 269 1.1 alm switch (c = getchar()) { 270 1.1 alm default: 271 1.1 alm oi = 0; 272 1.1 alm REALLOC(ibuf, ibufsz, i + 2, ERR); 273 1.1 alm if (!(ibuf[i++] = c)) isbinary = 1; 274 1.1 alm if (c != '\n') 275 1.1 alm continue; 276 1.1 alm lineno++; 277 1.1 alm ibuf[i] = '\0'; 278 1.1 alm ibufp = ibuf; 279 1.1 alm return i; 280 1.1 alm case EOF: 281 1.1 alm if (ferror(stdin)) { 282 1.1 alm fprintf(stderr, "stdin: %s\n", strerror(errno)); 283 1.10 dholland seterrmsg("cannot read stdin"); 284 1.1 alm clearerr(stdin); 285 1.1 alm ibufp = NULL; 286 1.1 alm return ERR; 287 1.1 alm } else { 288 1.1 alm clearerr(stdin); 289 1.1 alm if (i != oi) { 290 1.1 alm oi = i; 291 1.1 alm continue; 292 1.1 alm } else if (i) 293 1.1 alm ibuf[i] = '\0'; 294 1.1 alm ibufp = ibuf; 295 1.1 alm return i; 296 1.1 alm } 297 1.1 alm } 298 1.1 alm } 299 1.1 alm 300 1.1 alm 301 1.1 alm 302 1.1 alm #define ESCAPES "\a\b\f\n\r\t\v\\" 303 1.1 alm #define ESCCHARS "abfnrtv\\" 304 1.1 alm 305 1.1 alm /* put_tty_line: print text to stdout */ 306 1.1 alm int 307 1.7 xtraeme put_tty_line(char *s, int l, long n, int gflag) 308 1.1 alm { 309 1.1 alm int col = 0; 310 1.11 rillig const char *cp; 311 1.6 christos #ifndef BACKWARDS 312 1.6 christos int lc = 0; 313 1.6 christos #endif 314 1.1 alm 315 1.1 alm if (gflag & GNP) { 316 1.1 alm printf("%ld\t", n); 317 1.1 alm col = 8; 318 1.1 alm } 319 1.1 alm for (; l--; s++) { 320 1.1 alm if ((gflag & GLS) && ++col > cols) { 321 1.1 alm fputs("\\\n", stdout); 322 1.1 alm col = 1; 323 1.1 alm #ifndef BACKWARDS 324 1.1 alm if (!scripted && !isglobal && ++lc > rows) { 325 1.1 alm lc = 0; 326 1.1 alm fputs("Press <RETURN> to continue... ", stdout); 327 1.1 alm fflush(stdout); 328 1.1 alm if (get_tty_line() < 0) 329 1.1 alm return ERR; 330 1.1 alm } 331 1.1 alm #endif 332 1.1 alm } 333 1.1 alm if (gflag & GLS) { 334 1.1 alm if (31 < *s && *s < 127 && *s != '\\') 335 1.1 alm putchar(*s); 336 1.1 alm else { 337 1.1 alm putchar('\\'); 338 1.1 alm col++; 339 1.1 alm if (*s && (cp = strchr(ESCAPES, *s)) != NULL) 340 1.1 alm putchar(ESCCHARS[cp - ESCAPES]); 341 1.1 alm else { 342 1.1 alm putchar((((unsigned char) *s & 0300) >> 6) + '0'); 343 1.1 alm putchar((((unsigned char) *s & 070) >> 3) + '0'); 344 1.1 alm putchar(((unsigned char) *s & 07) + '0'); 345 1.1 alm col += 2; 346 1.1 alm } 347 1.1 alm } 348 1.1 alm 349 1.1 alm } else 350 1.1 alm putchar(*s); 351 1.1 alm } 352 1.1 alm #ifndef BACKWARDS 353 1.1 alm if (gflag & GLS) 354 1.1 alm putchar('$'); 355 1.1 alm #endif 356 1.1 alm putchar('\n'); 357 1.1 alm return 0; 358 1.1 alm } 359