]>
Commit | Line | Data |
---|---|---|
6dbe3af9 KZ |
1 | /* |
2 | * Copyright (c) 1989, 1993, 1994 | |
3 | * The Regents of the University of California. All rights reserved. | |
4 | * | |
5 | * Redistribution and use in source and binary forms, with or without | |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
32 | */ | |
7eda085c | 33 | |
eb63b9b8 | 34 | /* |
b50945d4 | 35 | * 1999-02-22 Arkadiusz MiĆkiewicz <misiek@pld.ORG.PL> |
eb63b9b8 KZ |
36 | * added Native Language Support |
37 | * 1999-09-19 Bruno Haible <haible@clisp.cons.org> | |
38 | * modified to work correctly in multi-byte locales | |
7eda085c | 39 | */ |
6dbe3af9 | 40 | |
6dbe3af9 KZ |
41 | #include <sys/types.h> |
42 | #include <sys/ioctl.h> | |
43 | ||
44 | #include <ctype.h> | |
6dbe3af9 | 45 | #include <stdio.h> |
fd6b7a7f | 46 | #include <unistd.h> |
6dbe3af9 KZ |
47 | #include <stdlib.h> |
48 | #include <string.h> | |
a4cc8dfe SK |
49 | #include <errno.h> |
50 | #include <getopt.h> | |
6dbe3af9 | 51 | |
3b56eea7 | 52 | #include "nls.h" |
eb76ca98 | 53 | #include "c.h" |
9ea8ded3 | 54 | #include "widechar.h" |
a29e40ca | 55 | #include "xalloc.h" |
02b77f7b | 56 | #include "strutils.h" |
b87cbe84 | 57 | #include "closestream.h" |
3b56eea7 | 58 | #include "ttyutils.h" |
eb63b9b8 | 59 | |
06b04b23 | 60 | #ifdef HAVE_WIDECHAR |
eb63b9b8 KZ |
61 | static wchar_t *mbs_to_wcs(const char *); |
62 | #else | |
a29e40ca | 63 | #define mbs_to_wcs(s) xstrdup(s) |
eb63b9b8 KZ |
64 | static char *mtsafe_strtok(char *, const char *, char **); |
65 | #define wcstok mtsafe_strtok | |
66 | #endif | |
67 | ||
1ae90932 SK |
68 | #define DEFCOLS 25 |
69 | #define TAB 8 | |
70 | #define DEFNUM 1000 | |
71 | #define MAXLINELEN (LINE_MAX + 1) | |
72 | ||
daf093d2 SK |
73 | static int input(FILE *fp, int *maxlength, wchar_t ***list, int *entries); |
74 | static void c_columnate(int maxlength, long termwidth, wchar_t **list, int entries); | |
75 | static void r_columnate(int maxlength, long termwidth, wchar_t **list, int entries); | |
732e3dec | 76 | static wchar_t *local_wcstok(wchar_t *p, const wchar_t *separator, int greedy, wchar_t **wcstok_state); |
47bd8ddc | 77 | static void maketbl(wchar_t **list, int entries, wchar_t *separator, int greedy, wchar_t *colsep); |
daf093d2 | 78 | static void print(wchar_t **list, int entries); |
6dbe3af9 | 79 | |
1ae90932 SK |
80 | typedef struct _tbl { |
81 | wchar_t **list; | |
82 | int cols, *len; | |
83 | } TBL; | |
84 | ||
683ddbd5 KZ |
85 | |
86 | #ifdef HAVE_WIDECHAR | |
87 | /* Don't use wcswidth(), we need to ignore non-printable chars. */ | |
88 | static int width(const wchar_t *str) | |
89 | { | |
90 | int x, width = 0; | |
91 | ||
92 | for (; *str != '\0'; str++) { | |
93 | x = wcwidth(*str); | |
94 | if (x > 0) | |
95 | width += x; | |
96 | } | |
97 | return width; | |
98 | } | |
99 | #else | |
100 | static int width(const char *str) | |
101 | { | |
102 | int width = 0; | |
103 | ||
104 | for (; *str != '\0'; str++) { | |
105 | if (isprint(*str)) | |
106 | width++; | |
107 | } | |
108 | return width; | |
109 | } | |
110 | #endif | |
111 | ||
a4cc8dfe SK |
112 | static void __attribute__((__noreturn__)) usage(int rc) |
113 | { | |
114 | FILE *out = rc == EXIT_FAILURE ? stderr : stdout; | |
115 | ||
3eed42f3 | 116 | fputs(USAGE_HEADER, out); |
4ce393f4 | 117 | fprintf(out, _(" %s [options] [<file>...]\n"), program_invocation_short_name); |
451dbcfa BS |
118 | |
119 | fputs(USAGE_SEPARATOR, out); | |
120 | fputs(_("Columnate lists.\n"), out); | |
121 | ||
3eed42f3 | 122 | fputs(USAGE_OPTIONS, out); |
d7a3bf94 KZ |
123 | fputs(_(" -t, --table create a table\n"), out); |
124 | fputs(_(" -s, --separator <string> possible table delimiters\n"), out); | |
125 | fputs(_(" -o, --output-separator <string> columns separator for table output\n" | |
126 | " (default is two spaces)\n"), out); | |
127 | fputs(_(" -c, --output-width <width> width of output in number of characters\n"), out); | |
128 | fputs(_(" -x, --fillrows fill rows before columns\n"), out); | |
3eed42f3 SK |
129 | fputs(USAGE_SEPARATOR, out); |
130 | fputs(USAGE_HELP, out); | |
131 | fputs(USAGE_VERSION, out); | |
132 | fprintf(out, USAGE_MAN_TAIL("column(1)")); | |
a4cc8dfe | 133 | |
a4cc8dfe SK |
134 | exit(rc); |
135 | } | |
1df29104 SK |
136 | |
137 | int main(int argc, char **argv) | |
6dbe3af9 | 138 | { |
a46e644e | 139 | int ch, tflag = 0, xflag = 0; |
6a41edfa | 140 | int i; |
3b56eea7 | 141 | int termwidth = 80; |
daf093d2 SK |
142 | int entries = 0; /* number of records */ |
143 | unsigned int eval = 0; /* exit value */ | |
a46e644e | 144 | int maxlength = 0; /* longest record */ |
daf093d2 | 145 | wchar_t **list = NULL; /* array of pointers to records */ |
732e3dec | 146 | int greedy = 1; |
47bd8ddc | 147 | wchar_t *colsep; /* table column output separator */ |
a46e644e | 148 | |
daf093d2 SK |
149 | /* field separator for table option */ |
150 | wchar_t default_separator[] = { '\t', ' ', 0 }; | |
151 | wchar_t *separator = default_separator; | |
152 | ||
153 | static const struct option longopts[] = | |
154 | { | |
87918040 SK |
155 | { "columns", required_argument, NULL, 'c' }, /* deprecated */ |
156 | { "fillrows", no_argument, NULL, 'x' }, | |
157 | { "help", no_argument, NULL, 'h' }, | |
158 | { "output-separator", required_argument, NULL, 'o' }, | |
159 | { "output-width", required_argument, NULL, 'c' }, | |
160 | { "separator", required_argument, NULL, 's' }, | |
161 | { "table", no_argument, NULL, 't' }, | |
162 | { "version", no_argument, NULL, 'V' }, | |
163 | { NULL, 0, NULL, 0 }, | |
daf093d2 SK |
164 | }; |
165 | ||
7eda085c KZ |
166 | setlocale(LC_ALL, ""); |
167 | bindtextdomain(PACKAGE, LOCALEDIR); | |
168 | textdomain(PACKAGE); | |
b87cbe84 | 169 | atexit(close_stdout); |
6dbe3af9 | 170 | |
43b4f7ea | 171 | termwidth = get_terminal_width(80); |
47bd8ddc | 172 | colsep = mbs_to_wcs(" "); |
6dbe3af9 | 173 | |
47bd8ddc | 174 | while ((ch = getopt_long(argc, argv, "hVc:s:txo:", longopts, NULL)) != -1) |
6dbe3af9 | 175 | switch(ch) { |
a4cc8dfe | 176 | case 'h': |
1ae90932 SK |
177 | usage(EXIT_SUCCESS); |
178 | break; | |
4ef21375 | 179 | case 'V': |
f6277500 SK |
180 | printf(UTIL_LINUX_VERSION); |
181 | return EXIT_SUCCESS; | |
6dbe3af9 | 182 | case 'c': |
3b56eea7 | 183 | termwidth = strtou32_or_err(optarg, _("invalid columns argument")); |
6dbe3af9 KZ |
184 | break; |
185 | case 's': | |
eb63b9b8 | 186 | separator = mbs_to_wcs(optarg); |
732e3dec | 187 | greedy = 0; |
6dbe3af9 | 188 | break; |
47bd8ddc SK |
189 | case 'o': |
190 | free(colsep); | |
191 | colsep = mbs_to_wcs(optarg); | |
192 | break; | |
6dbe3af9 KZ |
193 | case 't': |
194 | tflag = 1; | |
195 | break; | |
196 | case 'x': | |
197 | xflag = 1; | |
198 | break; | |
6dbe3af9 | 199 | default: |
677ec86c | 200 | errtryhelp(EXIT_FAILURE); |
1df29104 | 201 | } |
6dbe3af9 KZ |
202 | argc -= optind; |
203 | argv += optind; | |
204 | ||
205 | if (!*argv) | |
daf093d2 | 206 | eval += input(stdin, &maxlength, &list, &entries); |
1df29104 | 207 | else |
1ae90932 | 208 | for (; *argv; ++argv) { |
a46e644e KZ |
209 | FILE *fp; |
210 | ||
1ae90932 | 211 | if ((fp = fopen(*argv, "r")) != NULL) { |
daf093d2 SK |
212 | eval += input(fp, &maxlength, &list, &entries); |
213 | fclose(fp); | |
1ae90932 SK |
214 | } else { |
215 | warn("%s", *argv); | |
daf093d2 | 216 | eval += EXIT_FAILURE; |
1ae90932 | 217 | } |
6dbe3af9 KZ |
218 | } |
219 | ||
220 | if (!entries) | |
221 | exit(eval); | |
222 | ||
223 | if (tflag) | |
47bd8ddc | 224 | maketbl(list, entries, separator, greedy, colsep); |
6dbe3af9 | 225 | else if (maxlength >= termwidth) |
daf093d2 | 226 | print(list, entries); |
6dbe3af9 | 227 | else if (xflag) |
daf093d2 | 228 | c_columnate(maxlength, termwidth, list, entries); |
6dbe3af9 | 229 | else |
daf093d2 | 230 | r_columnate(maxlength, termwidth, list, entries); |
dcbca568 | 231 | |
6a41edfa | 232 | for (i = 0; i < entries; i++) |
dcbca568 SK |
233 | free(list[i]); |
234 | free(list); | |
235 | ||
daf093d2 SK |
236 | if (eval == 0) |
237 | return EXIT_SUCCESS; | |
238 | else | |
239 | return EXIT_FAILURE; | |
6dbe3af9 KZ |
240 | } |
241 | ||
daf093d2 | 242 | static void c_columnate(int maxlength, long termwidth, wchar_t **list, int entries) |
6dbe3af9 KZ |
243 | { |
244 | int chcnt, col, cnt, endcol, numcols; | |
eb63b9b8 | 245 | wchar_t **lp; |
6dbe3af9 KZ |
246 | |
247 | maxlength = (maxlength + TAB) & ~(TAB - 1); | |
248 | numcols = termwidth / maxlength; | |
249 | endcol = maxlength; | |
250 | for (chcnt = col = 0, lp = list;; ++lp) { | |
eb63b9b8 | 251 | fputws(*lp, stdout); |
683ddbd5 | 252 | chcnt += width(*lp); |
6dbe3af9 KZ |
253 | if (!--entries) |
254 | break; | |
255 | if (++col == numcols) { | |
256 | chcnt = col = 0; | |
257 | endcol = maxlength; | |
eb63b9b8 | 258 | putwchar('\n'); |
6dbe3af9 | 259 | } else { |
fd6b7a7f | 260 | while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { |
eb63b9b8 | 261 | putwchar('\t'); |
6dbe3af9 KZ |
262 | chcnt = cnt; |
263 | } | |
264 | endcol += maxlength; | |
265 | } | |
266 | } | |
267 | if (chcnt) | |
eb63b9b8 | 268 | putwchar('\n'); |
6dbe3af9 KZ |
269 | } |
270 | ||
daf093d2 | 271 | static void r_columnate(int maxlength, long termwidth, wchar_t **list, int entries) |
6dbe3af9 KZ |
272 | { |
273 | int base, chcnt, cnt, col, endcol, numcols, numrows, row; | |
274 | ||
275 | maxlength = (maxlength + TAB) & ~(TAB - 1); | |
276 | numcols = termwidth / maxlength; | |
683ddbd5 | 277 | if (!numcols) |
1ae90932 | 278 | numcols = 1; |
6dbe3af9 KZ |
279 | numrows = entries / numcols; |
280 | if (entries % numcols) | |
281 | ++numrows; | |
282 | ||
283 | for (row = 0; row < numrows; ++row) { | |
284 | endcol = maxlength; | |
285 | for (base = row, chcnt = col = 0; col < numcols; ++col) { | |
eb63b9b8 | 286 | fputws(list[base], stdout); |
683ddbd5 | 287 | chcnt += width(list[base]); |
6dbe3af9 KZ |
288 | if ((base += numrows) >= entries) |
289 | break; | |
fd6b7a7f | 290 | while ((cnt = ((chcnt + TAB) & ~(TAB - 1))) <= endcol) { |
eb63b9b8 | 291 | putwchar('\t'); |
6dbe3af9 KZ |
292 | chcnt = cnt; |
293 | } | |
294 | endcol += maxlength; | |
295 | } | |
eb63b9b8 | 296 | putwchar('\n'); |
6dbe3af9 KZ |
297 | } |
298 | } | |
299 | ||
daf093d2 | 300 | static void print(wchar_t **list, int entries) |
6dbe3af9 KZ |
301 | { |
302 | int cnt; | |
eb63b9b8 | 303 | wchar_t **lp; |
6dbe3af9 | 304 | |
eb63b9b8 KZ |
305 | for (cnt = entries, lp = list; cnt--; ++lp) { |
306 | fputws(*lp, stdout); | |
307 | putwchar('\n'); | |
308 | } | |
6dbe3af9 KZ |
309 | } |
310 | ||
2ba641e5 SK |
311 | static wchar_t *local_wcstok(wchar_t *p, const wchar_t *separator, int greedy, |
312 | wchar_t **wcstok_state) | |
732e3dec SK |
313 | { |
314 | wchar_t *result; | |
315 | if (greedy) | |
316 | return wcstok(p, separator, wcstok_state); | |
317 | ||
318 | if (p == NULL) { | |
319 | if (*wcstok_state == NULL) | |
320 | return NULL; | |
321 | else | |
322 | p = *wcstok_state; | |
323 | } | |
324 | result = p; | |
325 | p = wcspbrk(result, separator); | |
326 | if (p == NULL) | |
327 | *wcstok_state = NULL; | |
328 | else { | |
329 | *p = '\0'; | |
330 | *wcstok_state = p + 1; | |
331 | } | |
332 | return result; | |
333 | } | |
334 | ||
47bd8ddc | 335 | static void maketbl(wchar_t **list, int entries, wchar_t *separator, int greedy, wchar_t *colsep) |
6dbe3af9 KZ |
336 | { |
337 | TBL *t; | |
d2b7bc74 | 338 | int cnt; |
eb63b9b8 | 339 | wchar_t *p, **lp; |
4eaeb0ef SK |
340 | ssize_t *lens; |
341 | ssize_t maxcols = DEFCOLS, coloff; | |
6dbe3af9 | 342 | TBL *tbl; |
eb63b9b8 | 343 | wchar_t **cols; |
732e3dec | 344 | wchar_t *wcstok_state = NULL; |
6dbe3af9 | 345 | |
12bef812 SK |
346 | t = tbl = xcalloc(entries, sizeof(TBL)); |
347 | cols = xcalloc(maxcols, sizeof(wchar_t *)); | |
4eaeb0ef SK |
348 | lens = xcalloc(maxcols, sizeof(ssize_t)); |
349 | ||
350 | for (lp = list, cnt = 0; cnt < entries; ++cnt, ++lp, ++t) { | |
351 | coloff = 0; | |
352 | p = *lp; | |
732e3dec | 353 | while ((cols[coloff] = local_wcstok(p, separator, greedy, &wcstok_state)) != NULL) { |
1ae90932 | 354 | if (++coloff == maxcols) { |
1ae90932 | 355 | maxcols += DEFCOLS; |
4eaeb0ef SK |
356 | cols = xrealloc(cols, maxcols * sizeof(wchar_t *)); |
357 | lens = xrealloc(lens, maxcols * sizeof(ssize_t)); | |
358 | /* zero fill only new memory */ | |
26ae00a7 JM |
359 | memset(lens + (maxcols - DEFCOLS), 0, |
360 | DEFCOLS * sizeof(*lens)); | |
1ae90932 | 361 | } |
4eaeb0ef | 362 | p = NULL; |
1ae90932 | 363 | } |
12bef812 SK |
364 | t->list = xcalloc(coloff, sizeof(wchar_t *)); |
365 | t->len = xcalloc(coloff, sizeof(int)); | |
1ae90932 SK |
366 | for (t->cols = coloff; --coloff >= 0;) { |
367 | t->list[coloff] = cols[coloff]; | |
683ddbd5 | 368 | t->len[coloff] = width(cols[coloff]); |
1ae90932 SK |
369 | if (t->len[coloff] > lens[coloff]) |
370 | lens[coloff] = t->len[coloff]; | |
6dbe3af9 KZ |
371 | } |
372 | } | |
4eaeb0ef SK |
373 | |
374 | for (t = tbl, cnt = 0; cnt < entries; ++cnt, ++t) { | |
1ae90932 SK |
375 | for (coloff = 0; coloff < t->cols - 1; ++coloff) { |
376 | fputws(t->list[coloff], stdout); | |
683ddbd5 | 377 | #ifdef HAVE_WIDECHAR |
d2b7bc74 | 378 | wprintf(L"%*s", lens[coloff] - t->len[coloff], ""); |
683ddbd5 KZ |
379 | #else |
380 | printf("%*s", (int) lens[coloff] - t->len[coloff], ""); | |
381 | #endif | |
47bd8ddc | 382 | fputws(colsep, stdout); |
1ae90932 | 383 | } |
3f7fc4d4 KZ |
384 | if (coloff < t->cols) { |
385 | fputws(t->list[coloff], stdout); | |
386 | putwchar('\n'); | |
387 | } | |
6dbe3af9 | 388 | } |
dcbca568 SK |
389 | |
390 | for (cnt = 0; cnt < entries; ++cnt) { | |
391 | free((tbl+cnt)->list); | |
392 | free((tbl+cnt)->len); | |
393 | } | |
394 | free(cols); | |
395 | free(lens); | |
396 | free(tbl); | |
6dbe3af9 KZ |
397 | } |
398 | ||
daf093d2 | 399 | static int input(FILE *fp, int *maxlength, wchar_t ***list, int *entries) |
6dbe3af9 | 400 | { |
12bef812 | 401 | static int maxentry = DEFNUM; |
daf093d2 | 402 | int len, lineno = 1, reportedline = 0, eval = 0; |
eb63b9b8 | 403 | wchar_t *p, buf[MAXLINELEN]; |
a46e644e | 404 | wchar_t **local_list = *list; |
daf093d2 SK |
405 | int local_entries = *entries; |
406 | ||
407 | if (!local_list) | |
408 | local_list = xcalloc(maxentry, sizeof(wchar_t *)); | |
6dbe3af9 | 409 | |
acb5f9b5 SK |
410 | while (1) { |
411 | if (fgetws(buf, MAXLINELEN, fp) == NULL) { | |
412 | if (feof(fp)) | |
413 | break; | |
414 | else | |
415 | err(EXIT_FAILURE, _("read failed")); | |
416 | } | |
1df29104 SK |
417 | for (p = buf; *p && iswspace(*p); ++p) |
418 | ; | |
6dbe3af9 KZ |
419 | if (!*p) |
420 | continue; | |
80fa094c | 421 | if (!(p = wcschr(p, '\n')) && !feof(fp)) { |
bf90b8a6 | 422 | if (reportedline < lineno) { |
1ae90932 SK |
423 | warnx(_("line %d is too long, output will be truncated"), |
424 | lineno); | |
bf90b8a6 SK |
425 | reportedline = lineno; |
426 | } | |
daf093d2 | 427 | eval = 1; |
6dbe3af9 KZ |
428 | continue; |
429 | } | |
bf90b8a6 | 430 | lineno++; |
c630db89 | 431 | if (!feof(fp) && p) |
80fa094c | 432 | *p = '\0'; |
683ddbd5 | 433 | len = width(buf); /* len = p - buf; */ |
daf093d2 SK |
434 | if (*maxlength < len) |
435 | *maxlength = len; | |
436 | if (local_entries == maxentry) { | |
6dbe3af9 | 437 | maxentry += DEFNUM; |
daf093d2 SK |
438 | local_list = xrealloc(local_list, |
439 | (u_int)maxentry * sizeof(wchar_t *)); | |
6dbe3af9 | 440 | } |
daf093d2 | 441 | local_list[local_entries++] = wcsdup(buf); |
6dbe3af9 | 442 | } |
daf093d2 SK |
443 | |
444 | *list = local_list; | |
445 | *entries = local_entries; | |
446 | ||
447 | return eval; | |
6dbe3af9 KZ |
448 | } |
449 | ||
06b04b23 | 450 | #ifdef HAVE_WIDECHAR |
eb63b9b8 KZ |
451 | static wchar_t *mbs_to_wcs(const char *s) |
452 | { | |
395801be | 453 | ssize_t n; |
eb63b9b8 KZ |
454 | wchar_t *wcs; |
455 | ||
456 | n = mbstowcs((wchar_t *)0, s, 0); | |
457 | if (n < 0) | |
458 | return NULL; | |
cce4d25a | 459 | wcs = xmalloc((n + 1) * sizeof(wchar_t)); |
395801be | 460 | n = mbstowcs(wcs, s, n + 1); |
cd70a460 KZ |
461 | if (n < 0) { |
462 | free(wcs); | |
eb63b9b8 | 463 | return NULL; |
cd70a460 | 464 | } |
eb63b9b8 KZ |
465 | return wcs; |
466 | } | |
467 | #endif | |
468 | ||
06b04b23 | 469 | #ifndef HAVE_WIDECHAR |
eb63b9b8 KZ |
470 | static char *mtsafe_strtok(char *str, const char *delim, char **ptr) |
471 | { | |
472 | if (str == NULL) { | |
473 | str = *ptr; | |
474 | if (str == NULL) | |
475 | return NULL; | |
476 | } | |
477 | str += strspn(str, delim); | |
478 | if (*str == '\0') { | |
479 | *ptr = NULL; | |
480 | return NULL; | |
481 | } else { | |
482 | char *token_end = strpbrk(str, delim); | |
483 | if (token_end) { | |
484 | *token_end = '\0'; | |
485 | *ptr = token_end + 1; | |
486 | } else | |
487 | *ptr = NULL; | |
488 | return str; | |
489 | } | |
490 | } | |
491 | #endif |