]> git.saurik.com Git - apple/libc.git/blob - libdarwin/stdio.c
c1d1585e73521e5e04af1c1d4ffce973203a0eb8
[apple/libc.git] / libdarwin / stdio.c
1 /*
2 * Copyright (c) 2018 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include "internal.h"
24
25 #pragma mark API
26 int
27 fcheck_np(FILE *f, size_t n, size_t expected)
28 {
29 if (n == expected) {
30 return 0;
31 }
32 if (feof(f)) {
33 return EOF;
34 }
35 if (ferror(f)) {
36 return 1;
37 }
38 __builtin_unreachable();
39 }
40
41 os_fd_t
42 dup_np(os_fd_t fd)
43 {
44 os_fd_t dfd = -1;
45
46 while (true) {
47 dfd = dup(fd);
48
49 if (os_fd_valid(dfd)) {
50 break;
51 }
52
53 switch (errno) {
54 case EINTR:
55 break;
56 case EBADF:
57 os_crash("bad fd");
58 case EMFILE:
59 case ENFILE:
60 os_crash("failed to dup fd");
61 default:
62 os_crash("unhandled error: %s", symerror_np(errno));
63 }
64 }
65
66 return dfd;
67 }
68
69 size_t
70 zsnprintf_np(char *buff, size_t len, const char *fmt, ...)
71 {
72 int np = 0;
73 va_list ap;
74
75 va_start(ap, fmt);
76 np = vsnprintf(buff, len, fmt, ap);
77 va_end(ap);
78
79 if (np < 0) {
80 np = 0;
81 } else if ((size_t)np >= len) {
82 np = (int)len - 1;
83 }
84
85 return (size_t)np;
86 }
87
88 void
89 crfprintf_np(FILE *f, const char *fmt, ...)
90 {
91 va_list ap;
92
93 va_start(ap, fmt);
94 vcrfprintf_np(f, fmt, ap);
95 va_end(ap);
96 }
97
98 void
99 vcrfprintf_np(FILE *f, const char *fmt, va_list ap)
100 {
101 vfprintf(f, fmt, ap);
102 fprintf(f, "\n");
103 }
104
105 void
106 wfprintf_np(FILE *f, ssize_t initpad, size_t pad, size_t width,
107 const char *fmt, ...)
108 {
109 va_list ap;
110
111 va_start(ap, fmt);
112 vwfprintf_np(f, initpad, pad, width, fmt, ap);
113 va_end(ap);
114 }
115
116 void
117 vwfprintf_np(FILE *f, ssize_t initpad, size_t pad, size_t width,
118 const char *fmt, va_list ap)
119 {
120 char *__os_free string = NULL;
121 char *__os_free working = NULL;
122 char *__os_free init_padding = NULL;
123 char *__os_free padding = NULL;
124 const char *curline = NULL;;
125 size_t left = 0;
126 size_t initpad_labs = (size_t)labs(initpad);
127 int ret = -1;
128
129 if (width && width <= pad) {
130 os_crash("width cannot be smaller than pad");
131 }
132 if (width && (initpad > 0) && width <= initpad_labs) {
133 os_crash("width cannot be smaller than initpad");
134 }
135 if (width && (initpad < 0) && width <= initpad_labs) {
136 os_crash("width cannot be smaller than negative initpad");
137 }
138
139 ret = vasprintf(&string, fmt, ap);
140 if (ret < 0 || !string) {
141 return;
142 }
143
144 left = (size_t)ret;
145 curline = string;
146
147 // The working buffer will always be large enough to handle any individual
148 // line. vasprintf(3) returns the number of characters printed not including
149 // the null terminator, so add space for that.
150 working = malloc(left + 1);
151 if (!working) {
152 return;
153 }
154
155 init_padding = malloc(initpad_labs + 1);
156 if (!init_padding) {
157 return;
158 }
159
160 if (initpad >= 0) {
161 memset(init_padding, ' ', initpad);
162 init_padding[initpad] = 0;
163 } else {
164 init_padding[0] = 0;
165 }
166
167 padding = malloc(pad + 1);
168 if (!padding) {
169 return;
170 }
171
172 memset(padding, ' ', pad);
173 padding[pad] = 0;
174
175 do {
176 size_t which_pad = pad;
177 char *which_padding = padding;
178 bool findspace = true;
179 size_t n2consume = 0;
180 char *breakchar = NULL;
181
182 if (curline == string) {
183 which_padding = init_padding;
184 which_pad = initpad_labs;
185 }
186
187 if (width == 0) {
188 // Width is unconstrained so just consume the entire string and
189 // indent any new lines within.
190 n2consume = left;
191 findspace = false;
192 } else {
193 n2consume = width - which_pad;
194 if (n2consume >= left) {
195 n2consume = left;
196 findspace = false;
197 }
198 }
199
200 strlcpy(working, curline, n2consume + 1);
201 breakchar = strchr(working, '\n');
202 if (!breakchar && findspace) {
203 // No new line within our maximally-constrained width of characters,
204 // so search for a space instead.
205 breakchar = strrchr(working, ' ');
206 }
207
208 if (breakchar) {
209 // Found something to break on, so nerf it and only consume the
210 // characters up until that break character.
211 *breakchar = 0;
212 n2consume = (size_t)(breakchar - working);
213 curline += n2consume + 1;
214 }
215
216 fprintf(f, "%s%s\n", which_padding, working);
217 left -= n2consume;
218 } while (left);
219 }