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