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