xref: /freebsd/lib/libstdbuf/stdbuf.c (revision 0957b409a90fd597c1e9124cbaf3edd2b488f4ac)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2012 Jeremie Le Hen <jlh@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  *
28  * $FreeBSD$
29  */
30 
31 #include <err.h>
32 #include <errno.h>
33 #include <limits.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 
38 static const char *
39 stream_name(FILE *s)
40 {
41 
42 	if (s == stdin)
43 		return "stdin";
44 	if (s == stdout)
45 		return "stdout";
46 	if (s == stderr)
47 		return "stderr";
48 	/* This should not happen. */
49 	abort();
50 }
51 
52 static void
53 change_buf(FILE *s, const char *bufmode)
54 {
55 	char *unit;
56 	size_t bufsize;
57 	int mode;
58 
59 	bufsize = 0;
60 	if (bufmode[0] == '0' && bufmode[1] == '\0')
61 		mode = _IONBF;
62 	else if (bufmode[0] == 'L' && bufmode[1] == '\0')
63 		mode = _IOLBF;
64 	else if (bufmode[0] == 'B' && bufmode[1] == '\0') {
65 		mode = _IOFBF;
66 		bufsize = 0;
67 	} else {
68 		/*
69 		 * This library being preloaded, depending on libutil
70 		 * would lead to excessive namespace pollution.
71 		 * Thus we do not use expand_number().
72 		 */
73 		errno = 0;
74 		bufsize = strtol(bufmode, &unit, 0);
75 		if (errno == EINVAL || errno == ERANGE || unit == bufmode)
76 			warn("Wrong buffer mode '%s' for %s", bufmode,
77 			    stream_name(s));
78 		switch (*unit) {
79 		case 'G':
80 			bufsize *= 1024 * 1024 * 1024;
81 			break;
82 		case 'M':
83 			bufsize *= 1024 * 1024;
84 			break;
85 		case 'k':
86 			bufsize *= 1024;
87 			break;
88 		case '\0':
89 			break;
90 		default:
91 			warnx("Unknown suffix '%c' for %s", *unit,
92 			    stream_name(s));
93 			return;
94 		}
95 		mode = _IOFBF;
96 	}
97 	if (setvbuf(s, NULL, mode, bufsize) != 0)
98 		warn("Cannot set buffer mode '%s' for %s", bufmode,
99 		    stream_name(s));
100 }
101 
102 __attribute__ ((constructor)) static void
103 stdbuf(void)
104 {
105 	char *i_mode, *o_mode, *e_mode;
106 
107 	i_mode = getenv("_STDBUF_I");
108 	o_mode = getenv("_STDBUF_O");
109 	e_mode = getenv("_STDBUF_E");
110 
111 	if (e_mode != NULL)
112 		change_buf(stderr, e_mode);
113 	if (i_mode != NULL)
114 		change_buf(stdin, i_mode);
115 	if (o_mode != NULL)
116 		change_buf(stdout, o_mode);
117 }
118