1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1991, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Kenneth Almquist. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35 #ifndef lint 36 #endif /* not lint */ 37 #include <sys/cdefs.h> 38 /* 39 * Shell output routines. We use our own output routines because: 40 * When a builtin command is interrupted we have to discard 41 * any pending output. 42 * When a builtin command appears in back quotes, we want to 43 * save the output of the command in a region obtained 44 * via malloc, rather than doing a fork and reading the 45 * output of the command via a pipe. 46 */ 47 48 #include <stdio.h> /* defines BUFSIZ */ 49 #include <string.h> 50 #include <stdarg.h> 51 #include <errno.h> 52 #include <unistd.h> 53 #include <stdlib.h> 54 #include <wchar.h> 55 #include <wctype.h> 56 57 #include "shell.h" 58 #include "syntax.h" 59 #include "output.h" 60 #include "memalloc.h" 61 #include "error.h" 62 #include "var.h" 63 64 65 #define OUTBUFSIZ BUFSIZ 66 #define MEM_OUT -2 /* output to dynamically allocated memory */ 67 #define OUTPUT_ERR 01 /* error occurred on output */ 68 69 static int doformat_wr(void *, const char *, int); 70 71 struct output output = {NULL, NULL, NULL, OUTBUFSIZ, 1, 0}; 72 struct output errout = {NULL, NULL, NULL, 256, 2, 0}; 73 struct output memout = {NULL, NULL, NULL, 64, MEM_OUT, 0}; 74 struct output *out1 = &output; 75 struct output *out2 = &errout; 76 77 void 78 outcslow(int c, struct output *file) 79 { 80 outc(c, file); 81 } 82 83 void 84 out1str(const char *p) 85 { 86 outstr(p, out1); 87 } 88 89 void 90 out1qstr(const char *p) 91 { 92 outqstr(p, out1); 93 } 94 95 void 96 out2str(const char *p) 97 { 98 outstr(p, out2); 99 } 100 101 void 102 out2qstr(const char *p) 103 { 104 outqstr(p, out2); 105 } 106 107 void 108 outstr(const char *p, struct output *file) 109 { 110 outbin(p, strlen(p), file); 111 } 112 113 static void 114 byteseq(int ch, struct output *file) 115 { 116 char seq[4]; 117 118 seq[0] = '\\'; 119 seq[1] = (ch >> 6 & 0x3) + '0'; 120 seq[2] = (ch >> 3 & 0x7) + '0'; 121 seq[3] = (ch & 0x7) + '0'; 122 outbin(seq, 4, file); 123 } 124 125 static void 126 outdqstr(const char *p, struct output *file) 127 { 128 const char *end; 129 mbstate_t mbs; 130 size_t clen; 131 wchar_t wc; 132 133 memset(&mbs, '\0', sizeof(mbs)); 134 end = p + strlen(p); 135 outstr("$'", file); 136 while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) { 137 if (clen == (size_t)-2) { 138 while (p < end) 139 byteseq(*p++, file); 140 break; 141 } 142 if (clen == (size_t)-1) { 143 memset(&mbs, '\0', sizeof(mbs)); 144 byteseq(*p++, file); 145 continue; 146 } 147 if (wc == L'\n') 148 outcslow('\n', file), p++; 149 else if (wc == L'\r') 150 outstr("\\r", file), p++; 151 else if (wc == L'\t') 152 outstr("\\t", file), p++; 153 else if (!iswprint(wc)) { 154 for (; clen > 0; clen--) 155 byteseq(*p++, file); 156 } else { 157 if (wc == L'\'' || wc == L'\\') 158 outcslow('\\', file); 159 outbin(p, clen, file); 160 p += clen; 161 } 162 } 163 outcslow('\'', file); 164 } 165 166 /* Like outstr(), but quote for re-input into the shell. */ 167 void 168 outqstr(const char *p, struct output *file) 169 { 170 int i; 171 172 if (p[0] == '\0') { 173 outstr("''", file); 174 return; 175 } 176 for (i = 0; p[i] != '\0'; i++) { 177 if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') || 178 (p[i] & 0x80) != 0 || p[i] == '\'') { 179 outdqstr(p, file); 180 return; 181 } 182 } 183 184 if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' || 185 strcmp(p, "[") == 0) { 186 outstr(p, file); 187 return; 188 } 189 190 outcslow('\'', file); 191 outstr(p, file); 192 outcslow('\'', file); 193 } 194 195 void 196 outbin(const void *data, size_t len, struct output *file) 197 { 198 const char *p; 199 200 p = data; 201 while (len-- > 0) 202 outc(*p++, file); 203 } 204 205 void 206 emptyoutbuf(struct output *dest) 207 { 208 int offset, newsize; 209 210 if (dest->buf == NULL) { 211 INTOFF; 212 dest->buf = ckmalloc(dest->bufsize); 213 dest->nextc = dest->buf; 214 dest->bufend = dest->buf + dest->bufsize; 215 INTON; 216 } else if (dest->fd == MEM_OUT) { 217 offset = dest->nextc - dest->buf; 218 newsize = dest->bufsize << 1; 219 INTOFF; 220 dest->buf = ckrealloc(dest->buf, newsize); 221 dest->bufsize = newsize; 222 dest->bufend = dest->buf + newsize; 223 dest->nextc = dest->buf + offset; 224 INTON; 225 } else { 226 flushout(dest); 227 } 228 } 229 230 231 void 232 flushall(void) 233 { 234 flushout(&output); 235 flushout(&errout); 236 } 237 238 239 void 240 flushout(struct output *dest) 241 { 242 243 if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) 244 return; 245 if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) 246 dest->flags |= OUTPUT_ERR; 247 dest->nextc = dest->buf; 248 } 249 250 251 void 252 freestdout(void) 253 { 254 output.nextc = output.buf; 255 } 256 257 258 int 259 outiserror(struct output *file) 260 { 261 return (file->flags & OUTPUT_ERR); 262 } 263 264 265 void 266 outclearerror(struct output *file) 267 { 268 file->flags &= ~OUTPUT_ERR; 269 } 270 271 272 void 273 outfmt(struct output *file, const char *fmt, ...) 274 { 275 va_list ap; 276 277 va_start(ap, fmt); 278 doformat(file, fmt, ap); 279 va_end(ap); 280 } 281 282 283 void 284 out1fmt(const char *fmt, ...) 285 { 286 va_list ap; 287 288 va_start(ap, fmt); 289 doformat(out1, fmt, ap); 290 va_end(ap); 291 } 292 293 void 294 out2fmt_flush(const char *fmt, ...) 295 { 296 va_list ap; 297 298 va_start(ap, fmt); 299 doformat(out2, fmt, ap); 300 va_end(ap); 301 flushout(out2); 302 } 303 304 void 305 fmtstr(char *outbuf, int length, const char *fmt, ...) 306 { 307 va_list ap; 308 309 INTOFF; 310 va_start(ap, fmt); 311 vsnprintf(outbuf, length, fmt, ap); 312 va_end(ap); 313 INTON; 314 } 315 316 static int 317 doformat_wr(void *cookie, const char *buf, int len) 318 { 319 struct output *o; 320 321 o = (struct output *)cookie; 322 outbin(buf, len, o); 323 324 return (len); 325 } 326 327 void 328 doformat(struct output *dest, const char *f, va_list ap) 329 { 330 FILE *fp; 331 332 if ((fp = fwopen(dest, doformat_wr)) != NULL) { 333 vfprintf(fp, f, ap); 334 fclose(fp); 335 } 336 } 337 338 FILE * 339 out1fp(void) 340 { 341 return fwopen(out1, doformat_wr); 342 } 343 344 /* 345 * Version of write which resumes after a signal is caught. 346 */ 347 348 int 349 xwrite(int fd, const char *buf, int nbytes) 350 { 351 int ntry; 352 int i; 353 int n; 354 355 n = nbytes; 356 ntry = 0; 357 for (;;) { 358 i = write(fd, buf, n); 359 if (i > 0) { 360 if ((n -= i) <= 0) 361 return nbytes; 362 buf += i; 363 ntry = 0; 364 } else if (i == 0) { 365 if (++ntry > 10) 366 return nbytes - n; 367 } else if (errno != EINTR) { 368 return -1; 369 } 370 } 371 } 372