master xplshn/aruu / cmd / posix / sh / output.c
  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/*
 36 * Shell output routines.  We use our own output routines because:
 37 *	When a builtin command is interrupted we have to discard
 38 *		any pending output.
 39 *	When a builtin command appears in back quotes, we want to
 40 *		save the output of the command in a region obtained
 41 *		via malloc, rather than doing a fork and reading the
 42 *		output of the command via a pipe.
 43 */
 44
 45#include <errno.h>
 46#include <stdarg.h>
 47#include <stdio.h> /* defines BUFSIZ */
 48#include <stdlib.h>
 49#include <string.h>
 50#include <unistd.h>
 51#include <wchar.h>
 52#include <wctype.h>
 53
 54#if defined(__linux__) || defined(__CYGWIN__)
 55struct fwopen_cookie {
 56  void *cookie;
 57  int (*writefn)(void *, const char *, int);
 58};
 59
 60static ssize_t
 61compat_fwopen_write(void *cookie, const char *buf, size_t size)
 62{
 63  struct fwopen_cookie *c = cookie;
 64  return c->writefn(c->cookie, buf, (int)size);
 65}
 66
 67static int
 68compat_fwopen_close(void *cookie)
 69{
 70  free(cookie);
 71  return 0;
 72}
 73
 74static FILE *
 75fwopen(void *cookie, int (*writefn)(void *, const char *, int))
 76{
 77  struct fwopen_cookie *c;
 78  cookie_io_functions_t io_funcs = {
 79      .read = NULL, .write = compat_fwopen_write, .seek = NULL, .close = compat_fwopen_close
 80  };
 81  FILE *fp;
 82
 83  c = malloc(sizeof(struct fwopen_cookie));
 84  if (c == NULL)
 85    return NULL;
 86  c->cookie  = cookie;
 87  c->writefn = writefn;
 88
 89  fp = fopencookie(c, "w", io_funcs);
 90  if (fp == NULL) {
 91    free(c);
 92    return NULL;
 93  }
 94  return fp;
 95}
 96#endif
 97
 98#include "error.h"
 99#include "memalloc.h"
100#include "output.h"
101#include "shell.h"
102#include "syntax.h"
103#include "var.h"
104
105#define OUTBUFSIZ  BUFSIZ
106#define MEM_OUT    -2 /* output to dynamically allocated memory */
107#define OUTPUT_ERR 01 /* error occurred on output */
108
109static int doformat_wr(void *, const char *, int);
110
111struct output  output = {NULL, NULL, NULL, OUTBUFSIZ, 1, 0};
112struct output  errout = {NULL, NULL, NULL, 256, 2, 0};
113struct output  memout = {NULL, NULL, NULL, 64, MEM_OUT, 0};
114struct output *out1   = &output;
115struct output *out2   = &errout;
116
117void
118outcslow(int c, struct output *file)
119{
120  outc(c, file);
121}
122
123void
124out1str(const char *p)
125{
126  outstr(p, out1);
127}
128
129void
130out1qstr(const char *p)
131{
132  outqstr(p, out1);
133}
134
135void
136out2str(const char *p)
137{
138  outstr(p, out2);
139}
140
141void
142out2qstr(const char *p)
143{
144  outqstr(p, out2);
145}
146
147void
148outstr(const char *p, struct output *file)
149{
150  outbin(p, strlen(p), file);
151}
152
153static void
154byteseq(int ch, struct output *file)
155{
156  char seq[4];
157
158  seq[0] = '\\';
159  seq[1] = (ch >> 6 & 0x3) + '0';
160  seq[2] = (ch >> 3 & 0x7) + '0';
161  seq[3] = (ch & 0x7) + '0';
162  outbin(seq, 4, file);
163}
164
165static void
166outdqstr(const char *p, struct output *file)
167{
168  const char *end;
169  mbstate_t   mbs;
170  size_t      clen;
171  wchar_t     wc;
172
173  memset(&mbs, '\0', sizeof(mbs));
174  end = p + strlen(p);
175  outstr("$'", file);
176  while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) {
177    if (clen == (size_t)-2) {
178      while (p < end)
179        byteseq(*p++, file);
180      break;
181    }
182    if (clen == (size_t)-1) {
183      memset(&mbs, '\0', sizeof(mbs));
184      byteseq(*p++, file);
185      continue;
186    }
187    if (wc == L'\n')
188      outcslow('\n', file), p++;
189    else if (wc == L'\r')
190      outstr("\\r", file), p++;
191    else if (wc == L'\t')
192      outstr("\\t", file), p++;
193    else if (!iswprint(wc)) {
194      for (; clen > 0; clen--)
195        byteseq(*p++, file);
196    } else {
197      if (wc == L'\'' || wc == L'\\')
198        outcslow('\\', file);
199      outbin(p, clen, file);
200      p += clen;
201    }
202  }
203  outcslow('\'', file);
204}
205
206/* Like outstr(), but quote for re-input into the shell. */
207void
208outqstr(const char *p, struct output *file)
209{
210  int i;
211
212  if (p[0] == '\0') {
213    outstr("''", file);
214    return;
215  }
216  for (i = 0; p[i] != '\0'; i++) {
217    if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') || (p[i] & 0x80) != 0 || p[i] == '\'') {
218      outdqstr(p, file);
219      return;
220    }
221  }
222
223  if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' || strcmp(p, "[") == 0) {
224    outstr(p, file);
225    return;
226  }
227
228  outcslow('\'', file);
229  outstr(p, file);
230  outcslow('\'', file);
231}
232
233void
234outbin(const void *data, size_t len, struct output *file)
235{
236  const char *p;
237
238  p = data;
239  while (len-- > 0)
240    outc(*p++, file);
241}
242
243void
244emptyoutbuf(struct output *dest)
245{
246  int offset, newsize;
247
248  if (dest->buf == NULL) {
249    INTOFF;
250    dest->buf    = ckmalloc(dest->bufsize);
251    dest->nextc  = dest->buf;
252    dest->bufend = dest->buf + dest->bufsize;
253    INTON;
254  } else if (dest->fd == MEM_OUT) {
255    offset  = dest->nextc - dest->buf;
256    newsize = dest->bufsize << 1;
257    INTOFF;
258    dest->buf     = ckrealloc(dest->buf, newsize);
259    dest->bufsize = newsize;
260    dest->bufend  = dest->buf + newsize;
261    dest->nextc   = dest->buf + offset;
262    INTON;
263  } else {
264    flushout(dest);
265  }
266}
267
268void
269flushall(void)
270{
271  flushout(&output);
272  flushout(&errout);
273}
274
275void
276flushout(struct output *dest)
277{
278  if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0)
279    return;
280  if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0)
281    dest->flags |= OUTPUT_ERR;
282  dest->nextc = dest->buf;
283}
284
285void
286freestdout(void)
287{
288  output.nextc = output.buf;
289}
290
291int
292outiserror(struct output *file)
293{
294  return (file->flags & OUTPUT_ERR);
295}
296
297void
298outclearerror(struct output *file)
299{
300  file->flags &= ~OUTPUT_ERR;
301}
302
303void
304outfmt(struct output *file, const char *fmt, ...)
305{
306  va_list ap;
307
308  va_start(ap, fmt);
309  doformat(file, fmt, ap);
310  va_end(ap);
311}
312
313void
314out1fmt(const char *fmt, ...)
315{
316  va_list ap;
317
318  va_start(ap, fmt);
319  doformat(out1, fmt, ap);
320  va_end(ap);
321}
322
323void
324out2fmt_flush(const char *fmt, ...)
325{
326  va_list ap;
327
328  va_start(ap, fmt);
329  doformat(out2, fmt, ap);
330  va_end(ap);
331  flushout(out2);
332}
333
334void
335fmtstr(char *outbuf, int length, const char *fmt, ...)
336{
337  va_list ap;
338
339  INTOFF;
340  va_start(ap, fmt);
341  vsnprintf(outbuf, length, fmt, ap);
342  va_end(ap);
343  INTON;
344}
345
346static int
347doformat_wr(void *cookie, const char *buf, int len)
348{
349  struct output *o;
350
351  o = (struct output *)cookie;
352  outbin(buf, len, o);
353
354  return (len);
355}
356
357void
358doformat(struct output *dest, const char *f, va_list ap)
359{
360  FILE *fp;
361
362  if ((fp = fwopen(dest, doformat_wr)) != NULL) {
363    vfprintf(fp, f, ap);
364    fclose(fp);
365  }
366}
367
368FILE *
369out1fp(void)
370{
371  return fwopen(out1, doformat_wr);
372}
373
374/*
375 * Version of write which resumes after a signal is caught.
376 */
377
378int
379xwrite(int fd, const char *buf, int nbytes)
380{
381  int ntry;
382  int i;
383  int n;
384
385  n    = nbytes;
386  ntry = 0;
387  for (;;) {
388    i = write(fd, buf, n);
389    if (i > 0) {
390      if ((n -= i) <= 0)
391        return nbytes;
392      buf += i;
393      ntry = 0;
394    } else if (i == 0) {
395      if (++ntry > 10)
396        return nbytes - n;
397    } else if (errno != EINTR) {
398      return -1;
399    }
400  }
401}