source: mainline/uspace/lib/posix/src/stdio.c@ 8565a42

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 8565a42 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 7 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 14.0 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Zarevucky
3 * Copyright (c) 2011 Petr Koupy
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - 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 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libposix
31 * @{
32 */
33/** @file Standard buffered input/output.
34 */
35
36#include "internal/common.h"
37#include "posix/stdio.h"
38
39#include <assert.h>
40
41#include <errno.h>
42
43#include "posix/stdlib.h"
44#include "posix/string.h"
45#include "posix/sys/types.h"
46#include "posix/unistd.h"
47
48#include "libc/stdio.h"
49#include "libc/io/printf_core.h"
50#include "libc/str.h"
51#include "libc/malloc.h"
52#include "libc/adt/list.h"
53
54/**
55 * Generate a pathname for the controlling terminal.
56 *
57 * @param s Allocated buffer to which the pathname shall be put.
58 * @return Either s or static location filled with the requested pathname.
59 */
60char *ctermid(char *s)
61{
62 /* Currently always returns an error value (empty string). */
63 // TODO: return a real terminal path
64
65 static char dummy_path[L_ctermid] = {'\0'};
66
67 if (s == NULL) {
68 return dummy_path;
69 }
70
71 s[0] = '\0';
72 return s;
73}
74
75/**
76 * Read a stream until the delimiter (or EOF) is encountered.
77 *
78 * @param lineptr Pointer to the output buffer in which there will be stored
79 * nul-terminated string together with the delimiter (if encountered).
80 * Will be resized if necessary.
81 * @param n Pointer to the size of the output buffer. Will be increased if
82 * necessary.
83 * @param delimiter Delimiter on which to finish reading the stream.
84 * @param stream Input stream.
85 * @return Number of fetched characters (including delimiter if encountered)
86 * or -1 on error (set in errno).
87 */
88ssize_t getdelim(char **restrict lineptr, size_t *restrict n,
89 int delimiter, FILE *restrict stream)
90{
91 /* Check arguments for sanity. */
92 if (!lineptr || !n) {
93 errno = EINVAL;
94 return -1;
95 }
96
97 size_t alloc_step = 80; /* Buffer size gain during reallocation. */
98 char *pos = *lineptr; /* Next free byte of the output buffer. */
99 size_t cnt = 0; /* Number of fetched characters. */
100 int c = fgetc(stream); /* Current input character. Might be EOF. */
101
102 do {
103 /* Mask EOF as NUL to terminate string. */
104 if (c == EOF) {
105 c = '\0';
106 }
107
108 /* Ensure there is still space left in the buffer. */
109 if (pos == *lineptr + *n) {
110 *lineptr = realloc(*lineptr, *n + alloc_step);
111 if (*lineptr) {
112 pos = *lineptr + *n;
113 *n += alloc_step;
114 } else {
115 errno = ENOMEM;
116 return -1;
117 }
118 }
119
120 /* Store the fetched character. */
121 *pos = c;
122
123 /* Fetch the next character according to the current character. */
124 if (c != '\0') {
125 ++pos;
126 ++cnt;
127 if (c == delimiter) {
128 /* Delimiter was just stored. Provide EOF as the next
129 * character - it will be masked as NUL and output string
130 * will be properly terminated. */
131 c = EOF;
132 } else {
133 /* Neither delimiter nor EOF were encountered. Just fetch
134 * the next character from the stream. */
135 c = fgetc(stream);
136 }
137 }
138 } while (c != '\0');
139
140 if (errno == EOK && cnt > 0) {
141 return cnt;
142 } else {
143 /* Either some error occured or the stream was already at EOF. */
144 return -1;
145 }
146}
147
148/**
149 * Read a stream until the newline (or EOF) is encountered.
150 *
151 * @param lineptr Pointer to the output buffer in which there will be stored
152 * nul-terminated string together with the delimiter (if encountered).
153 * Will be resized if necessary.
154 * @param n Pointer to the size of the output buffer. Will be increased if
155 * necessary.
156 * @param stream Input stream.
157 * @return Number of fetched characters (including newline if encountered)
158 * or -1 on error (set in errno).
159 */
160ssize_t getline(char **restrict lineptr, size_t *restrict n,
161 FILE *restrict stream)
162{
163 return getdelim(lineptr, n, '\n', stream);
164}
165
166/**
167 * Write error messages to standard error.
168 *
169 * @param s Error message.
170 */
171void perror(const char *s)
172{
173 if (s == NULL || s[0] == '\0') {
174 fprintf(stderr, "%s\n", strerror(errno));
175 } else {
176 fprintf(stderr, "%s: %s\n", s, strerror(errno));
177 }
178}
179
180/** Restores stream a to position previously saved with fgetpos().
181 *
182 * @param stream Stream to restore
183 * @param pos Position to restore
184 * @return Zero on success, non-zero (with errno set) on failure
185 */
186int fsetpos(FILE *stream, const fpos_t *pos)
187{
188 return fseek64(stream, pos->offset, SEEK_SET);
189}
190
191/** Saves the stream's position for later use by fsetpos().
192 *
193 * @param stream Stream to save
194 * @param pos Place to store the position
195 * @return Zero on success, non-zero (with errno set) on failure
196 */
197int fgetpos(FILE *restrict stream, fpos_t *restrict pos)
198{
199 off64_t ret = ftell64(stream);
200 if (ret != -1) {
201 pos->offset = ret;
202 return 0;
203 } else {
204 return -1;
205 }
206}
207
208/**
209 * Reposition a file-position indicator in a stream.
210 *
211 * @param stream Stream to seek in.
212 * @param offset Direction and amount of bytes to seek.
213 * @param whence From where to seek.
214 * @return Zero on success, -1 otherwise.
215 */
216int fseeko(FILE *stream, off_t offset, int whence)
217{
218 return fseek64(stream, offset, whence);
219}
220
221/**
222 * Discover current file offset in a stream.
223 *
224 * @param stream Stream for which the offset shall be retrieved.
225 * @return Current offset or -1 if not possible.
226 */
227off_t ftello(FILE *stream)
228{
229 return ftell64(stream);
230}
231
232/**
233 * Print formatted output to the opened file.
234 *
235 * @param fildes File descriptor of the opened file.
236 * @param format Format description.
237 * @return Either the number of printed characters or negative value on error.
238 */
239int dprintf(int fildes, const char *restrict format, ...)
240{
241 va_list list;
242 va_start(list, format);
243 int result = vdprintf(fildes, format, list);
244 va_end(list);
245 return result;
246}
247
248/**
249 * Write ordinary string to the opened file.
250 *
251 * @param str String to be written.
252 * @param size Size of the string (in bytes)..
253 * @param fd File descriptor of the opened file.
254 * @return The number of written characters.
255 */
256static int _dprintf_str_write(const char *str, size_t size, void *fd)
257{
258 const int fildes = *(int *) fd;
259 size_t wr;
260 if (failed(vfs_write(fildes, &posix_pos[fildes], str, size, &wr)))
261 return -1;
262 return str_nlength(str, wr);
263}
264
265/**
266 * Write wide string to the opened file.
267 *
268 * @param str String to be written.
269 * @param size Size of the string (in bytes).
270 * @param fd File descriptor of the opened file.
271 * @return The number of written characters.
272 */
273static int _dprintf_wstr_write(const wchar_t *str, size_t size, void *fd)
274{
275 size_t offset = 0;
276 size_t chars = 0;
277 size_t sz;
278 char buf[4];
279
280 while (offset < size) {
281 sz = 0;
282 if (chr_encode(str[chars], buf, &sz, sizeof(buf)) != EOK) {
283 break;
284 }
285
286 const int fildes = *(int *) fd;
287 size_t nwr;
288 if (vfs_write(fildes, &posix_pos[fildes], buf, sz, &nwr) != EOK)
289 break;
290
291 chars++;
292 offset += sizeof(wchar_t);
293 }
294
295 return chars;
296}
297
298/**
299 * Print formatted output to the opened file.
300 *
301 * @param fildes File descriptor of the opened file.
302 * @param format Format description.
303 * @param ap Print arguments.
304 * @return Either the number of printed characters or negative value on error.
305 */
306int vdprintf(int fildes, const char *restrict format, va_list ap)
307{
308 printf_spec_t spec = {
309 .str_write = _dprintf_str_write,
310 .wstr_write = _dprintf_wstr_write,
311 .data = &fildes
312 };
313
314 return printf_core(format, &spec, ap);
315}
316
317/**
318 * Print formatted output to the string.
319 *
320 * @param s Output string.
321 * @param format Format description.
322 * @return Either the number of printed characters (excluding null byte) or
323 * negative value on error.
324 */
325int sprintf(char *s, const char *restrict format, ...)
326{
327 va_list list;
328 va_start(list, format);
329 int result = vsprintf(s, format, list);
330 va_end(list);
331 return result;
332}
333
334/**
335 * Print formatted output to the string.
336 *
337 * @param s Output string.
338 * @param format Format description.
339 * @param ap Print arguments.
340 * @return Either the number of printed characters (excluding null byte) or
341 * negative value on error.
342 */
343int vsprintf(char *s, const char *restrict format, va_list ap)
344{
345 return vsnprintf(s, STR_NO_LIMIT, format, ap);
346}
347
348/**
349 * Convert formatted input from the stream.
350 *
351 * @param stream Input stream.
352 * @param format Format description.
353 * @return The number of converted output items or EOF on failure.
354 */
355int fscanf(FILE *restrict stream, const char *restrict format, ...)
356{
357 va_list list;
358 va_start(list, format);
359 int result = vfscanf(stream, format, list);
360 va_end(list);
361 return result;
362}
363
364/**
365 * Convert formatted input from the standard input.
366 *
367 * @param format Format description.
368 * @return The number of converted output items or EOF on failure.
369 */
370int scanf(const char *restrict format, ...)
371{
372 va_list list;
373 va_start(list, format);
374 int result = vscanf(format, list);
375 va_end(list);
376 return result;
377}
378
379/**
380 * Convert formatted input from the standard input.
381 *
382 * @param format Format description.
383 * @param arg Output items.
384 * @return The number of converted output items or EOF on failure.
385 */
386int vscanf(const char *restrict format, va_list arg)
387{
388 return vfscanf(stdin, format, arg);
389}
390
391/**
392 * Convert formatted input from the string.
393 *
394 * @param s Input string.
395 * @param format Format description.
396 * @return The number of converted output items or EOF on failure.
397 */
398int sscanf(const char *restrict s, const char *restrict format, ...)
399{
400 va_list list;
401 va_start(list, format);
402 int result = vsscanf(s, format, list);
403 va_end(list);
404 return result;
405}
406
407/**
408 * Acquire file stream for the thread.
409 *
410 * @param file File stream to lock.
411 */
412void flockfile(FILE *file)
413{
414 /* dummy */
415}
416
417/**
418 * Acquire file stream for the thread (non-blocking).
419 *
420 * @param file File stream to lock.
421 * @return Zero for success and non-zero if the lock cannot be acquired.
422 */
423int ftrylockfile(FILE *file)
424{
425 /* dummy */
426 return 0;
427}
428
429/**
430 * Relinquish the ownership of the locked file stream.
431 *
432 * @param file File stream to unlock.
433 */
434void funlockfile(FILE *file)
435{
436 /* dummy */
437}
438
439/**
440 * Get a byte from a stream (thread-unsafe).
441 *
442 * @param stream Input file stream.
443 * @return Either read byte or EOF.
444 */
445int getc_unlocked(FILE *stream)
446{
447 return getc(stream);
448}
449
450/**
451 * Get a byte from the standard input stream (thread-unsafe).
452 *
453 * @return Either read byte or EOF.
454 */
455int getchar_unlocked(void)
456{
457 return getchar();
458}
459
460/**
461 * Put a byte on a stream (thread-unsafe).
462 *
463 * @param c Byte to output.
464 * @param stream Output file stream.
465 * @return Either written byte or EOF.
466 */
467int putc_unlocked(int c, FILE *stream)
468{
469 return putc(c, stream);
470}
471
472/**
473 * Put a byte on the standard output stream (thread-unsafe).
474 *
475 * @param c Byte to output.
476 * @return Either written byte or EOF.
477 */
478int putchar_unlocked(int c)
479{
480 return putchar(c);
481}
482
483/**
484 * Get a unique temporary file name (obsolete).
485 *
486 * @param s Buffer for the file name. Must be at least L_tmpnam bytes long.
487 * @return The value of s on success, NULL on failure.
488 */
489char *tmpnam(char *s)
490{
491 assert(L_tmpnam >= strlen("/tmp/tnXXXXXX"));
492
493 static char buffer[L_tmpnam + 1];
494 if (s == NULL) {
495 s = buffer;
496 }
497
498 strcpy(s, "/tmp/tnXXXXXX");
499 mktemp(s);
500
501 if (*s == '\0') {
502 /* Errno set by mktemp(). */
503 return NULL;
504 }
505
506 return s;
507}
508
509/**
510 * Get an unique temporary file name with additional constraints (obsolete).
511 *
512 * @param dir Path to directory, where the file should be created.
513 * @param pfx Optional prefix up to 5 characters long.
514 * @return Newly allocated unique path for temporary file. NULL on failure.
515 */
516char *tempnam(const char *dir, const char *pfx)
517{
518 /* Sequence number of the filename. */
519 static int seq = 0;
520
521 size_t dir_len = strlen(dir);
522 if (dir[dir_len - 1] == '/') {
523 dir_len--;
524 }
525
526 size_t pfx_len = strlen(pfx);
527 if (pfx_len > 5) {
528 pfx_len = 5;
529 }
530
531 char *result = malloc(dir_len + /* slash*/ 1 +
532 pfx_len + /* three-digit seq */ 3 + /* .tmp */ 4 + /* nul */ 1);
533
534 if (result == NULL) {
535 errno = ENOMEM;
536 return NULL;
537 }
538
539 char *res_ptr = result;
540 strncpy(res_ptr, dir, dir_len);
541 res_ptr += dir_len;
542 strncpy(res_ptr, pfx, pfx_len);
543 res_ptr += pfx_len;
544
545 for (; seq < 1000; ++seq) {
546 snprintf(res_ptr, 8, "%03d.tmp", seq);
547
548 int orig_errno = errno;
549 errno = EOK;
550 /* Check if the file exists. */
551 if (access(result, F_OK) == -1) {
552 if (errno == ENOENT) {
553 errno = orig_errno;
554 break;
555 } else {
556 /* errno set by access() */
557 return NULL;
558 }
559 }
560 }
561
562 if (seq == 1000) {
563 free(result);
564 errno = EINVAL;
565 return NULL;
566 }
567
568 return result;
569}
570
571/**
572 * Create and open an unique temporary file.
573 * The file is automatically removed when the stream is closed.
574 *
575 * @param dir Path to directory, where the file should be created.
576 * @param pfx Optional prefix up to 5 characters long.
577 * @return Newly allocated unique path for temporary file. NULL on failure.
578 */
579FILE *tmpfile(void)
580{
581 char filename[] = "/tmp/tfXXXXXX";
582 int fd = mkstemp(filename);
583 if (fd == -1) {
584 /* errno set by mkstemp(). */
585 return NULL;
586 }
587
588 /* Unlink the created file, so that it's removed on close(). */
589 unlink(filename);
590 return fdopen(fd, "w+");
591}
592
593/** @}
594 */
Note: See TracBrowser for help on using the repository browser.