1*d47295ccSrillig /* $NetBSD: io.c,v 1.11 2024/10/03 20:14:01 rillig Exp $ */ 249f0ad86Scgd 31357f155Salm /* io.c: This file contains the i/o routines for the ed line editor */ 41357f155Salm /*- 51357f155Salm * Copyright (c) 1993 Andrew Moore, Talke Studio. 61357f155Salm * All rights reserved. 71357f155Salm * 81357f155Salm * Redistribution and use in source and binary forms, with or without 91357f155Salm * modification, are permitted provided that the following conditions 101357f155Salm * are met: 111357f155Salm * 1. Redistributions of source code must retain the above copyright 121357f155Salm * notice, this list of conditions and the following disclaimer. 131357f155Salm * 2. Redistributions in binary form must reproduce the above copyright 141357f155Salm * notice, this list of conditions and the following disclaimer in the 151357f155Salm * documentation and/or other materials provided with the distribution. 161357f155Salm * 171357f155Salm * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 181357f155Salm * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191357f155Salm * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201357f155Salm * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 211357f155Salm * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221357f155Salm * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231357f155Salm * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241357f155Salm * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251357f155Salm * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261357f155Salm * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271357f155Salm * SUCH DAMAGE. 281357f155Salm */ 291357f155Salm 308b7ade1aSthorpej #include <sys/cdefs.h> 311357f155Salm #ifndef lint 3249f0ad86Scgd #if 0 331357f155Salm static char *rcsid = "@(#)io.c,v 1.1 1994/02/01 00:34:41 alm Exp"; 3449f0ad86Scgd #else 35*d47295ccSrillig __RCSID("$NetBSD: io.c,v 1.11 2024/10/03 20:14:01 rillig Exp $"); 3649f0ad86Scgd #endif 371357f155Salm #endif /* not lint */ 381357f155Salm 391357f155Salm #include "ed.h" 401357f155Salm 411357f155Salm 421357f155Salm /* read_file: read a named file/pipe into the buffer; return line count */ 431357f155Salm long 44d33a7206Sxtraeme read_file(char *fn, long n) 451357f155Salm { 461357f155Salm FILE *fp; 471357f155Salm long size; 481357f155Salm 491357f155Salm 501357f155Salm fp = (*fn == '!') ? popen(fn + 1, "r") : fopen(strip_escapes(fn), "r"); 511357f155Salm if (fp == NULL) { 521357f155Salm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 53a3542600Sdholland seterrmsg("cannot open input file"); 541357f155Salm return ERR; 551357f155Salm } else if ((size = read_stream(fp, n)) < 0) 561357f155Salm return ERR; 571357f155Salm else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { 581357f155Salm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 59a3542600Sdholland seterrmsg("cannot close input file"); 601357f155Salm return ERR; 611357f155Salm } 62a86027b1Sjoerg if (!scripted) 63a86027b1Sjoerg fprintf(stderr, "%lu\n", size); 641357f155Salm return current_addr - n; 651357f155Salm } 661357f155Salm 671357f155Salm 681357f155Salm char *sbuf; /* file i/o buffer */ 691357f155Salm int sbufsz; /* file i/o buffer size */ 701357f155Salm int newline_added; /* if set, newline appended to input file */ 711357f155Salm 721357f155Salm /* read_stream: read a stream into the editor buffer; return status */ 731357f155Salm long 74d33a7206Sxtraeme read_stream(FILE *fp, long n) 751357f155Salm { 761357f155Salm line_t *lp = get_addressed_line_node(n); 771357f155Salm undo_t *up = NULL; 781357f155Salm unsigned long size = 0; 791357f155Salm int o_newline_added = newline_added; 801357f155Salm int o_isbinary = isbinary; 811357f155Salm int appended = (n == addr_last); 821357f155Salm int len; 831357f155Salm 841357f155Salm isbinary = newline_added = 0; 851357f155Salm if (des) 861357f155Salm init_des_cipher(); 871357f155Salm for (current_addr = n; (len = get_stream_line(fp)) > 0; size += len) { 881357f155Salm SPL1(); 891357f155Salm if (put_sbuf_line(sbuf) == NULL) { 901357f155Salm SPL0(); 911357f155Salm return ERR; 921357f155Salm } 931357f155Salm lp = lp->q_forw; 941357f155Salm if (up) 951357f155Salm up->t = lp; 961357f155Salm else if ((up = push_undo_stack(UADD, current_addr, 971357f155Salm current_addr)) == NULL) { 981357f155Salm SPL0(); 991357f155Salm return ERR; 1001357f155Salm } 1011357f155Salm SPL0(); 1021357f155Salm } 1031357f155Salm if (len < 0) 1041357f155Salm return ERR; 1051357f155Salm if (appended && size && o_isbinary && o_newline_added) 1061357f155Salm fputs("newline inserted\n", stderr); 1078b7ade1aSthorpej else if (newline_added && (!appended || (!isbinary && !o_isbinary))) 1081357f155Salm fputs("newline appended\n", stderr); 1091357f155Salm if (isbinary && newline_added && !appended) 1101357f155Salm size += 1; 1111357f155Salm if (!size) 1121357f155Salm newline_added = 1; 1131357f155Salm newline_added = appended ? newline_added : o_newline_added; 1141357f155Salm isbinary = isbinary | o_isbinary; 1151357f155Salm if (des) 1161357f155Salm size += 8 - size % 8; /* adjust DES size */ 1171357f155Salm return size; 1181357f155Salm } 1191357f155Salm 1201357f155Salm 1211357f155Salm /* get_stream_line: read a line of text from a stream; return line length */ 1221357f155Salm int 123d33a7206Sxtraeme get_stream_line(FILE *fp) 1241357f155Salm { 1253811362cStls int c; 1263811362cStls int i = 0; 1271357f155Salm 1288b7ade1aSthorpej while (((c = des ? get_des_char(fp) : getc(fp)) != EOF || (!feof(fp) && 1298b7ade1aSthorpej !ferror(fp))) && c != '\n') { 1301357f155Salm REALLOC(sbuf, sbufsz, i + 1, ERR); 1311357f155Salm if (!(sbuf[i++] = c)) 1321357f155Salm isbinary = 1; 1331357f155Salm } 1341357f155Salm REALLOC(sbuf, sbufsz, i + 2, ERR); 1351357f155Salm if (c == '\n') 1361357f155Salm sbuf[i++] = c; 1371357f155Salm else if (ferror(fp)) { 1381357f155Salm fprintf(stderr, "%s\n", strerror(errno)); 139a3542600Sdholland seterrmsg("cannot read input file"); 1401357f155Salm return ERR; 1411357f155Salm } else if (i) { 1421357f155Salm sbuf[i++] = '\n'; 1431357f155Salm newline_added = 1; 1441357f155Salm } 1451357f155Salm sbuf[i] = '\0'; 1461357f155Salm return (isbinary && newline_added && i) ? --i : i; 1471357f155Salm } 1481357f155Salm 1491357f155Salm 1501357f155Salm /* write_file: write a range of lines to a named file/pipe; return line count */ 1511357f155Salm long 1526310b596Schristos write_file(const char *fn, const char *mode, long n, long m) 1531357f155Salm { 1541357f155Salm FILE *fp; 1551357f155Salm long size; 1561357f155Salm 1571357f155Salm fp = (*fn == '!') ? popen(fn+1, "w") : fopen(strip_escapes(fn), mode); 1581357f155Salm if (fp == NULL) { 1591357f155Salm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 160a3542600Sdholland seterrmsg("cannot open output file"); 1611357f155Salm return ERR; 1621357f155Salm } else if ((size = write_stream(fp, n, m)) < 0) 1631357f155Salm return ERR; 1641357f155Salm else if (((*fn == '!') ? pclose(fp) : fclose(fp)) < 0) { 1651357f155Salm fprintf(stderr, "%s: %s\n", fn, strerror(errno)); 166a3542600Sdholland seterrmsg("cannot close output file"); 1671357f155Salm return ERR; 1681357f155Salm } 169a86027b1Sjoerg if (!scripted) 170a86027b1Sjoerg fprintf(stderr, "%lu\n", size); 1711357f155Salm return n ? m - n + 1 : 0; 1721357f155Salm } 1731357f155Salm 1741357f155Salm 1751357f155Salm /* write_stream: write a range of lines to a stream; return status */ 1761357f155Salm long 177d33a7206Sxtraeme write_stream(FILE *fp, long n, long m) 1781357f155Salm { 1791357f155Salm line_t *lp = get_addressed_line_node(n); 1801357f155Salm unsigned long size = 0; 1811357f155Salm char *s; 1821357f155Salm int len; 1831357f155Salm 1841357f155Salm if (des) 1851357f155Salm init_des_cipher(); 1861357f155Salm for (; n && n <= m; n++, lp = lp->q_forw) { 1871357f155Salm if ((s = get_sbuf_line(lp)) == NULL) 1881357f155Salm return ERR; 1891357f155Salm len = lp->len; 1901357f155Salm if (n != addr_last || !isbinary || !newline_added) 1911357f155Salm s[len++] = '\n'; 1921357f155Salm if (put_stream_line(fp, s, len) < 0) 1931357f155Salm return ERR; 1941357f155Salm size += len; 1951357f155Salm } 1961357f155Salm if (des) { 1971357f155Salm flush_des_file(fp); /* flush buffer */ 1981357f155Salm size += 8 - size % 8; /* adjust DES size */ 1991357f155Salm } 2001357f155Salm return size; 2011357f155Salm } 2021357f155Salm 2031357f155Salm 2041357f155Salm /* put_stream_line: write a line of text to a stream; return status */ 2051357f155Salm int 206d33a7206Sxtraeme put_stream_line(FILE *fp, char *s, int len) 2071357f155Salm { 2081357f155Salm while (len--) 2091357f155Salm if ((des ? put_des_char(*s++, fp) : fputc(*s++, fp)) < 0) { 2101357f155Salm fprintf(stderr, "%s\n", strerror(errno)); 211a3542600Sdholland seterrmsg("cannot write file"); 2121357f155Salm return ERR; 2131357f155Salm } 2141357f155Salm return 0; 2151357f155Salm } 2161357f155Salm 2171357f155Salm /* get_extended_line: get a an extended line from stdin */ 2181357f155Salm char * 219d33a7206Sxtraeme get_extended_line(int *sizep, int nonl) 2201357f155Salm { 2211357f155Salm static char *cvbuf = NULL; /* buffer */ 2221357f155Salm static int cvbufsz = 0; /* buffer size */ 2231357f155Salm 2241357f155Salm int l, n; 2251357f155Salm char *t = ibufp; 2261357f155Salm 2271357f155Salm while (*t++ != '\n') 2281357f155Salm ; 2291357f155Salm if ((l = t - ibufp) < 2 || !has_trailing_escape(ibufp, ibufp + l - 1)) { 2301357f155Salm *sizep = l; 2311357f155Salm return ibufp; 2321357f155Salm } 2331357f155Salm *sizep = -1; 2341357f155Salm REALLOC(cvbuf, cvbufsz, l, NULL); 2351357f155Salm memcpy(cvbuf, ibufp, l); 2361357f155Salm *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ 2371357f155Salm if (nonl) l--; /* strip newline */ 2381357f155Salm for (;;) { 2391357f155Salm if ((n = get_tty_line()) < 0) 2401357f155Salm return NULL; 2411357f155Salm else if (n == 0 || ibuf[n - 1] != '\n') { 242a3542600Sdholland seterrmsg("unexpected end-of-file"); 2431357f155Salm return NULL; 2441357f155Salm } 2451357f155Salm REALLOC(cvbuf, cvbufsz, l + n, NULL); 2461357f155Salm memcpy(cvbuf + l, ibuf, n); 2471357f155Salm l += n; 2481357f155Salm if (n < 2 || !has_trailing_escape(cvbuf, cvbuf + l - 1)) 2491357f155Salm break; 2501357f155Salm *(cvbuf + --l - 1) = '\n'; /* strip trailing esc */ 2511357f155Salm if (nonl) l--; /* strip newline */ 2521357f155Salm } 2531357f155Salm REALLOC(cvbuf, cvbufsz, l + 1, NULL); 2541357f155Salm cvbuf[l] = '\0'; 2551357f155Salm *sizep = l; 2561357f155Salm return cvbuf; 2571357f155Salm } 2581357f155Salm 2591357f155Salm 2601357f155Salm /* get_tty_line: read a line of text from stdin; return line length */ 2611357f155Salm int 262d33a7206Sxtraeme get_tty_line(void) 2631357f155Salm { 2643811362cStls int oi = 0; 2653811362cStls int i = 0; 2661357f155Salm int c; 2671357f155Salm 2681357f155Salm for (;;) 2691357f155Salm switch (c = getchar()) { 2701357f155Salm default: 2711357f155Salm oi = 0; 2721357f155Salm REALLOC(ibuf, ibufsz, i + 2, ERR); 2731357f155Salm if (!(ibuf[i++] = c)) isbinary = 1; 2741357f155Salm if (c != '\n') 2751357f155Salm continue; 2761357f155Salm lineno++; 2771357f155Salm ibuf[i] = '\0'; 2781357f155Salm ibufp = ibuf; 2791357f155Salm return i; 2801357f155Salm case EOF: 2811357f155Salm if (ferror(stdin)) { 2821357f155Salm fprintf(stderr, "stdin: %s\n", strerror(errno)); 283a3542600Sdholland seterrmsg("cannot read stdin"); 2841357f155Salm clearerr(stdin); 2851357f155Salm ibufp = NULL; 2861357f155Salm return ERR; 2871357f155Salm } else { 2881357f155Salm clearerr(stdin); 2891357f155Salm if (i != oi) { 2901357f155Salm oi = i; 2911357f155Salm continue; 2921357f155Salm } else if (i) 2931357f155Salm ibuf[i] = '\0'; 2941357f155Salm ibufp = ibuf; 2951357f155Salm return i; 2961357f155Salm } 2971357f155Salm } 2981357f155Salm } 2991357f155Salm 3001357f155Salm 3011357f155Salm 3021357f155Salm #define ESCAPES "\a\b\f\n\r\t\v\\" 3031357f155Salm #define ESCCHARS "abfnrtv\\" 3041357f155Salm 3051357f155Salm /* put_tty_line: print text to stdout */ 3061357f155Salm int 307d33a7206Sxtraeme put_tty_line(char *s, int l, long n, int gflag) 3081357f155Salm { 3091357f155Salm int col = 0; 310*d47295ccSrillig const char *cp; 311d1631dd9Schristos #ifndef BACKWARDS 312d1631dd9Schristos int lc = 0; 313d1631dd9Schristos #endif 3141357f155Salm 3151357f155Salm if (gflag & GNP) { 3161357f155Salm printf("%ld\t", n); 3171357f155Salm col = 8; 3181357f155Salm } 3191357f155Salm for (; l--; s++) { 3201357f155Salm if ((gflag & GLS) && ++col > cols) { 3211357f155Salm fputs("\\\n", stdout); 3221357f155Salm col = 1; 3231357f155Salm #ifndef BACKWARDS 3241357f155Salm if (!scripted && !isglobal && ++lc > rows) { 3251357f155Salm lc = 0; 3261357f155Salm fputs("Press <RETURN> to continue... ", stdout); 3271357f155Salm fflush(stdout); 3281357f155Salm if (get_tty_line() < 0) 3291357f155Salm return ERR; 3301357f155Salm } 3311357f155Salm #endif 3321357f155Salm } 3331357f155Salm if (gflag & GLS) { 3341357f155Salm if (31 < *s && *s < 127 && *s != '\\') 3351357f155Salm putchar(*s); 3361357f155Salm else { 3371357f155Salm putchar('\\'); 3381357f155Salm col++; 3391357f155Salm if (*s && (cp = strchr(ESCAPES, *s)) != NULL) 3401357f155Salm putchar(ESCCHARS[cp - ESCAPES]); 3411357f155Salm else { 3421357f155Salm putchar((((unsigned char) *s & 0300) >> 6) + '0'); 3431357f155Salm putchar((((unsigned char) *s & 070) >> 3) + '0'); 3441357f155Salm putchar(((unsigned char) *s & 07) + '0'); 3451357f155Salm col += 2; 3461357f155Salm } 3471357f155Salm } 3481357f155Salm 3491357f155Salm } else 3501357f155Salm putchar(*s); 3511357f155Salm } 3521357f155Salm #ifndef BACKWARDS 3531357f155Salm if (gflag & GLS) 3541357f155Salm putchar('$'); 3551357f155Salm #endif 3561357f155Salm putchar('\n'); 3571357f155Salm return 0; 3581357f155Salm } 359