source: mainline/uspace/lib/posix/source/stdio.c@ e3c960e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since e3c960e was e3c960e, checked in by Vojtech Horky <vojtechhorky@…>, 11 years ago

fpos_t must be declared in the header (thx Esteban Campostrini)

There is no function for allocating the structure for the user and
by using only the forward declaration the user effectively cannot
create a variable of fpos_t type.

  • Property mode set to 100644
File size: 19.1 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#define LIBPOSIX_INTERNAL
37#define __POSIX_DEF__(x) posix_##x
38
39#include "internal/common.h"
40#include "posix/stdio.h"
41
42#include "posix/assert.h"
43#include "posix/errno.h"
44#include "posix/stdlib.h"
45#include "posix/string.h"
46#include "posix/sys/types.h"
47#include "posix/unistd.h"
48
49#include "libc/stdio.h"
50#include "libc/io/printf_core.h"
51#include "libc/stdbool.h"
52#include "libc/str.h"
53#include "libc/malloc.h"
54#include "libc/adt/list.h"
55#include "libc/sys/stat.h"
56
57
58/* not the best of solutions, but freopen and ungetc will eventually
59 * need to be implemented in libc anyway
60 */
61#include "../../c/generic/private/stdio.h"
62
63/** Clears the stream's error and end-of-file indicators.
64 *
65 * @param stream Stream whose indicators shall be cleared.
66 */
67void posix_clearerr(FILE *stream)
68{
69 stream->error = 0;
70 stream->eof = 0;
71}
72
73/**
74 * Generate a pathname for the controlling terminal.
75 *
76 * @param s Allocated buffer to which the pathname shall be put.
77 * @return Either s or static location filled with the requested pathname.
78 */
79char *posix_ctermid(char *s)
80{
81 /* Currently always returns an error value (empty string). */
82 // TODO: return a real terminal path
83
84 static char dummy_path[L_ctermid] = {'\0'};
85
86 if (s == NULL) {
87 return dummy_path;
88 }
89
90 s[0] = '\0';
91 return s;
92}
93
94/**
95 * Put a string on the stream.
96 *
97 * @param s String to be written.
98 * @param stream Output stream.
99 * @return Non-negative on success, EOF on failure.
100 */
101int posix_fputs(const char *restrict s, FILE *restrict stream)
102{
103 int rc = fputs(s, stream);
104 if (rc == 0) {
105 return EOF;
106 } else {
107 return 0;
108 }
109}
110
111/**
112 * Push byte back into input stream.
113 *
114 * @param c Byte to be pushed back.
115 * @param stream Stream to where the byte shall be pushed.
116 * @return Provided byte on success or EOF if not possible.
117 */
118int posix_ungetc(int c, FILE *stream)
119{
120 uint8_t b = (uint8_t) c;
121
122 bool can_unget =
123 /* Provided character is legal. */
124 c != EOF &&
125 /* Stream is consistent. */
126 !stream->error &&
127 /* Stream is buffered. */
128 stream->btype != _IONBF &&
129 /* Last operation on the stream was a read operation. */
130 stream->buf_state == _bs_read &&
131 /* Stream buffer is already allocated (i.e. there was already carried
132 * out either write or read operation on the stream). This is probably
133 * redundant check but let's be safe. */
134 stream->buf != NULL &&
135 /* There is still space in the stream to retreat. POSIX demands the
136 * possibility to unget at least 1 character. It should be always
137 * possible, assuming the last operation on the stream read at least 1
138 * character, because the buffer is refilled in the lazily manner. */
139 stream->buf_tail > stream->buf;
140
141 if (can_unget) {
142 --stream->buf_tail;
143 stream->buf_tail[0] = b;
144 stream->eof = false;
145 return (int) b;
146 } else {
147 return EOF;
148 }
149}
150
151/**
152 * Read a stream until the delimiter (or EOF) is encountered.
153 *
154 * @param lineptr Pointer to the output buffer in which there will be stored
155 * nul-terminated string together with the delimiter (if encountered).
156 * Will be resized if necessary.
157 * @param n Pointer to the size of the output buffer. Will be increased if
158 * necessary.
159 * @param delimiter Delimiter on which to finish reading the stream.
160 * @param stream Input stream.
161 * @return Number of fetched characters (including delimiter if encountered)
162 * or -1 on error (set in errno).
163 */
164ssize_t posix_getdelim(char **restrict lineptr, size_t *restrict n,
165 int delimiter, FILE *restrict stream)
166{
167 /* Check arguments for sanity. */
168 if (!lineptr || !n) {
169 errno = EINVAL;
170 return -1;
171 }
172
173 size_t alloc_step = 80; /* Buffer size gain during reallocation. */
174 char *pos = *lineptr; /* Next free byte of the output buffer. */
175 size_t cnt = 0; /* Number of fetched characters. */
176 int c = fgetc(stream); /* Current input character. Might be EOF. */
177
178 do {
179 /* Mask EOF as NUL to terminate string. */
180 if (c == EOF) {
181 c = '\0';
182 }
183
184 /* Ensure there is still space left in the buffer. */
185 if (pos == *lineptr + *n) {
186 *lineptr = realloc(*lineptr, *n + alloc_step);
187 if (*lineptr) {
188 pos = *lineptr + *n;
189 *n += alloc_step;
190 } else {
191 errno = ENOMEM;
192 return -1;
193 }
194 }
195
196 /* Store the fetched character. */
197 *pos = c;
198
199 /* Fetch the next character according to the current character. */
200 if (c != '\0') {
201 ++pos;
202 ++cnt;
203 if (c == delimiter) {
204 /* Delimiter was just stored. Provide EOF as the next
205 * character - it will be masked as NUL and output string
206 * will be properly terminated. */
207 c = EOF;
208 } else {
209 /* Neither delimiter nor EOF were encountered. Just fetch
210 * the next character from the stream. */
211 c = fgetc(stream);
212 }
213 }
214 } while (c != '\0');
215
216 if (errno == EOK && cnt > 0) {
217 return cnt;
218 } else {
219 /* Either some error occured or the stream was already at EOF. */
220 return -1;
221 }
222}
223
224/**
225 * Read a stream until the newline (or EOF) is encountered.
226 *
227 * @param lineptr Pointer to the output buffer in which there will be stored
228 * nul-terminated string together with the delimiter (if encountered).
229 * Will be resized if necessary.
230 * @param n Pointer to the size of the output buffer. Will be increased if
231 * necessary.
232 * @param stream Input stream.
233 * @return Number of fetched characters (including newline if encountered)
234 * or -1 on error (set in errno).
235 */
236ssize_t posix_getline(char **restrict lineptr, size_t *restrict n,
237 FILE *restrict stream)
238{
239 return posix_getdelim(lineptr, n, '\n', stream);
240}
241
242/**
243 * Reopen a file stream.
244 *
245 * @param filename Pathname of a file to be reopened or NULL for changing
246 * the mode of the stream.
247 * @param mode Mode to be used for reopening the file or changing current
248 * mode of the stream.
249 * @param stream Current stream associated with the opened file.
250 * @return On success, either a stream of the reopened file or the provided
251 * stream with a changed mode. NULL otherwise.
252 */
253FILE *posix_freopen(const char *restrict filename,
254 const char *restrict mode, FILE *restrict stream)
255{
256 assert(mode != NULL);
257 assert(stream != NULL);
258
259 if (filename == NULL) {
260 /* POSIX allows this to be imlementation-defined. HelenOS currently
261 * does not support changing the mode. */
262 // FIXME: handle mode change once it is supported
263 return stream;
264 }
265
266 /* Open a new stream. */
267 FILE* new = fopen(filename, mode);
268 if (new == NULL) {
269 fclose(stream);
270 /* errno was set by fopen() */
271 return NULL;
272 }
273
274 /* Close the original stream without freeing it (ignoring errors). */
275 if (stream->buf != NULL) {
276 fflush(stream);
277 }
278 if (stream->sess != NULL) {
279 async_hangup(stream->sess);
280 }
281 if (stream->fd >= 0) {
282 close(stream->fd);
283 }
284 list_remove(&stream->link);
285
286 /* Move the new stream to the original location. */
287 memcpy(stream, new, sizeof (FILE));
288 free(new);
289
290 /* Update references in the file list. */
291 stream->link.next->prev = &stream->link;
292 stream->link.prev->next = &stream->link;
293
294 return stream;
295}
296
297/**
298 * Write error messages to standard error.
299 *
300 * @param s Error message.
301 */
302void posix_perror(const char *s)
303{
304 if (s == NULL || s[0] == '\0') {
305 fprintf(stderr, "%s\n", posix_strerror(errno));
306 } else {
307 fprintf(stderr, "%s: %s\n", s, posix_strerror(errno));
308 }
309}
310
311/** Restores stream a to position previously saved with fgetpos().
312 *
313 * @param stream Stream to restore
314 * @param pos Position to restore
315 * @return Zero on success, non-zero (with errno set) on failure
316 */
317int posix_fsetpos(FILE *stream, const posix_fpos_t *pos)
318{
319 return fseek(stream, pos->offset, SEEK_SET);
320}
321
322/** Saves the stream's position for later use by fsetpos().
323 *
324 * @param stream Stream to save
325 * @param pos Place to store the position
326 * @return Zero on success, non-zero (with errno set) on failure
327 */
328int posix_fgetpos(FILE *restrict stream, posix_fpos_t *restrict pos)
329{
330 off64_t ret = ftell(stream);
331 if (ret != -1) {
332 pos->offset = ret;
333 return 0;
334 } else {
335 return -1;
336 }
337}
338
339/**
340 * Reposition a file-position indicator in a stream.
341 *
342 * @param stream Stream to seek in.
343 * @param offset Direction and amount of bytes to seek.
344 * @param whence From where to seek.
345 * @return Zero on success, -1 otherwise.
346 */
347int posix_fseek(FILE *stream, long offset, int whence)
348{
349 return fseek(stream, (off64_t) offset, whence);
350}
351
352/**
353 * Reposition a file-position indicator in a stream.
354 *
355 * @param stream Stream to seek in.
356 * @param offset Direction and amount of bytes to seek.
357 * @param whence From where to seek.
358 * @return Zero on success, -1 otherwise.
359 */
360int posix_fseeko(FILE *stream, posix_off_t offset, int whence)
361{
362 return fseek(stream, (off64_t) offset, whence);
363}
364
365/**
366 * Discover current file offset in a stream.
367 *
368 * @param stream Stream for which the offset shall be retrieved.
369 * @return Current offset or -1 if not possible.
370 */
371long posix_ftell(FILE *stream)
372{
373 return (long) ftell(stream);
374}
375
376/**
377 * Discover current file offset in a stream.
378 *
379 * @param stream Stream for which the offset shall be retrieved.
380 * @return Current offset or -1 if not possible.
381 */
382posix_off_t posix_ftello(FILE *stream)
383{
384 return (posix_off_t) ftell(stream);
385}
386
387/**
388 * Discard prefetched data or write unwritten data.
389 *
390 * @param stream Stream that shall be flushed.
391 * @return Zero on success, EOF on failure.
392 */
393int posix_fflush(FILE *stream)
394{
395 int rc = fflush(stream);
396 if (rc < 0) {
397 errno = -rc;
398 return EOF;
399 } else {
400 return 0;
401 }
402}
403
404/**
405 * Print formatted output to the opened file.
406 *
407 * @param fildes File descriptor of the opened file.
408 * @param format Format description.
409 * @return Either the number of printed characters or negative value on error.
410 */
411int posix_dprintf(int fildes, const char *restrict format, ...)
412{
413 va_list list;
414 va_start(list, format);
415 int result = posix_vdprintf(fildes, format, list);
416 va_end(list);
417 return result;
418}
419
420/**
421 * Write ordinary string to the opened file.
422 *
423 * @param str String to be written.
424 * @param size Size of the string (in bytes)..
425 * @param fd File descriptor of the opened file.
426 * @return The number of written characters.
427 */
428static int _dprintf_str_write(const char *str, size_t size, void *fd)
429{
430 ssize_t wr = write(*(int *) fd, str, size);
431 return str_nlength(str, wr);
432}
433
434/**
435 * Write wide string to the opened file.
436 *
437 * @param str String to be written.
438 * @param size Size of the string (in bytes).
439 * @param fd File descriptor of the opened file.
440 * @return The number of written characters.
441 */
442static int _dprintf_wstr_write(const wchar_t *str, size_t size, void *fd)
443{
444 size_t offset = 0;
445 size_t chars = 0;
446 size_t sz;
447 char buf[4];
448
449 while (offset < size) {
450 sz = 0;
451 if (chr_encode(str[chars], buf, &sz, sizeof(buf)) != EOK) {
452 break;
453 }
454
455 if (write(*(int *) fd, buf, sz) != (ssize_t) sz) {
456 break;
457 }
458
459 chars++;
460 offset += sizeof(wchar_t);
461 }
462
463 return chars;
464}
465
466/**
467 * Print formatted output to the opened file.
468 *
469 * @param fildes File descriptor of the opened file.
470 * @param format Format description.
471 * @param ap Print arguments.
472 * @return Either the number of printed characters or negative value on error.
473 */
474int posix_vdprintf(int fildes, const char *restrict format, va_list ap)
475{
476 printf_spec_t spec = {
477 .str_write = _dprintf_str_write,
478 .wstr_write = _dprintf_wstr_write,
479 .data = &fildes
480 };
481
482 return printf_core(format, &spec, ap);
483}
484
485/**
486 * Print formatted output to the string.
487 *
488 * @param s Output string.
489 * @param format Format description.
490 * @return Either the number of printed characters (excluding null byte) or
491 * negative value on error.
492 */
493int posix_sprintf(char *s, const char *restrict format, ...)
494{
495 va_list list;
496 va_start(list, format);
497 int result = posix_vsprintf(s, format, list);
498 va_end(list);
499 return result;
500}
501
502/**
503 * Print formatted output to the string.
504 *
505 * @param s Output string.
506 * @param format Format description.
507 * @param ap Print arguments.
508 * @return Either the number of printed characters (excluding null byte) or
509 * negative value on error.
510 */
511int posix_vsprintf(char *s, const char *restrict format, va_list ap)
512{
513 return vsnprintf(s, STR_NO_LIMIT, format, ap);
514}
515
516/**
517 * Convert formatted input from the stream.
518 *
519 * @param stream Input stream.
520 * @param format Format description.
521 * @return The number of converted output items or EOF on failure.
522 */
523int posix_fscanf(FILE *restrict stream, const char *restrict format, ...)
524{
525 va_list list;
526 va_start(list, format);
527 int result = posix_vfscanf(stream, format, list);
528 va_end(list);
529 return result;
530}
531
532/**
533 * Convert formatted input from the standard input.
534 *
535 * @param format Format description.
536 * @return The number of converted output items or EOF on failure.
537 */
538int posix_scanf(const char *restrict format, ...)
539{
540 va_list list;
541 va_start(list, format);
542 int result = posix_vscanf(format, list);
543 va_end(list);
544 return result;
545}
546
547/**
548 * Convert formatted input from the standard input.
549 *
550 * @param format Format description.
551 * @param arg Output items.
552 * @return The number of converted output items or EOF on failure.
553 */
554int posix_vscanf(const char *restrict format, va_list arg)
555{
556 return posix_vfscanf(stdin, format, arg);
557}
558
559/**
560 * Convert formatted input from the string.
561 *
562 * @param s Input string.
563 * @param format Format description.
564 * @return The number of converted output items or EOF on failure.
565 */
566int posix_sscanf(const char *restrict s, const char *restrict format, ...)
567{
568 va_list list;
569 va_start(list, format);
570 int result = posix_vsscanf(s, format, list);
571 va_end(list);
572 return result;
573}
574
575/**
576 * Acquire file stream for the thread.
577 *
578 * @param file File stream to lock.
579 */
580void posix_flockfile(FILE *file)
581{
582 /* dummy */
583}
584
585/**
586 * Acquire file stream for the thread (non-blocking).
587 *
588 * @param file File stream to lock.
589 * @return Zero for success and non-zero if the lock cannot be acquired.
590 */
591int posix_ftrylockfile(FILE *file)
592{
593 /* dummy */
594 return 0;
595}
596
597/**
598 * Relinquish the ownership of the locked file stream.
599 *
600 * @param file File stream to unlock.
601 */
602void posix_funlockfile(FILE *file)
603{
604 /* dummy */
605}
606
607/**
608 * Get a byte from a stream (thread-unsafe).
609 *
610 * @param stream Input file stream.
611 * @return Either read byte or EOF.
612 */
613int posix_getc_unlocked(FILE *stream)
614{
615 return getc(stream);
616}
617
618/**
619 * Get a byte from the standard input stream (thread-unsafe).
620 *
621 * @return Either read byte or EOF.
622 */
623int posix_getchar_unlocked(void)
624{
625 return getchar();
626}
627
628/**
629 * Put a byte on a stream (thread-unsafe).
630 *
631 * @param c Byte to output.
632 * @param stream Output file stream.
633 * @return Either written byte or EOF.
634 */
635int posix_putc_unlocked(int c, FILE *stream)
636{
637 return putc(c, stream);
638}
639
640/**
641 * Put a byte on the standard output stream (thread-unsafe).
642 *
643 * @param c Byte to output.
644 * @return Either written byte or EOF.
645 */
646int posix_putchar_unlocked(int c)
647{
648 return putchar(c);
649}
650
651/**
652 * Remove a file or directory.
653 *
654 * @param path Pathname of the file that shall be removed.
655 * @return Zero on success, -1 (with errno set) otherwise.
656 */
657int posix_remove(const char *path)
658{
659 struct stat st;
660 int rc = stat(path, &st);
661
662 if (rc != EOK) {
663 errno = -rc;
664 return -1;
665 }
666
667 if (st.is_directory) {
668 rc = rmdir(path);
669 } else {
670 rc = unlink(path);
671 }
672
673 if (rc != EOK) {
674 errno = -rc;
675 return -1;
676 }
677 return 0;
678}
679
680/**
681 * Rename a file or directory.
682 *
683 * @param old Old pathname.
684 * @param new New pathname.
685 * @return Zero on success, -1 (with errno set) otherwise.
686 */
687int posix_rename(const char *old, const char *new)
688{
689 return errnify(rename, old, new);
690}
691
692/**
693 * Get a unique temporary file name (obsolete).
694 *
695 * @param s Buffer for the file name. Must be at least L_tmpnam bytes long.
696 * @return The value of s on success, NULL on failure.
697 */
698char *posix_tmpnam(char *s)
699{
700 assert(L_tmpnam >= posix_strlen("/tmp/tnXXXXXX"));
701
702 static char buffer[L_tmpnam + 1];
703 if (s == NULL) {
704 s = buffer;
705 }
706
707 posix_strcpy(s, "/tmp/tnXXXXXX");
708 posix_mktemp(s);
709
710 if (*s == '\0') {
711 /* Errno set by mktemp(). */
712 return NULL;
713 }
714
715 return s;
716}
717
718/**
719 * Get an unique temporary file name with additional constraints (obsolete).
720 *
721 * @param dir Path to directory, where the file should be created.
722 * @param pfx Optional prefix up to 5 characters long.
723 * @return Newly allocated unique path for temporary file. NULL on failure.
724 */
725char *posix_tempnam(const char *dir, const char *pfx)
726{
727 /* Sequence number of the filename. */
728 static int seq = 0;
729
730 size_t dir_len = posix_strlen(dir);
731 if (dir[dir_len - 1] == '/') {
732 dir_len--;
733 }
734
735 size_t pfx_len = posix_strlen(pfx);
736 if (pfx_len > 5) {
737 pfx_len = 5;
738 }
739
740 char *result = malloc(dir_len + /* slash*/ 1 +
741 pfx_len + /* three-digit seq */ 3 + /* .tmp */ 4 + /* nul */ 1);
742
743 if (result == NULL) {
744 errno = ENOMEM;
745 return NULL;
746 }
747
748 char *res_ptr = result;
749 posix_strncpy(res_ptr, dir, dir_len);
750 res_ptr += dir_len;
751 posix_strncpy(res_ptr, pfx, pfx_len);
752 res_ptr += pfx_len;
753
754 for (; seq < 1000; ++seq) {
755 snprintf(res_ptr, 8, "%03d.tmp", seq);
756
757 int orig_errno = errno;
758 errno = 0;
759 /* Check if the file exists. */
760 if (posix_access(result, F_OK) == -1) {
761 if (errno == ENOENT) {
762 errno = orig_errno;
763 break;
764 } else {
765 /* errno set by access() */
766 return NULL;
767 }
768 }
769 }
770
771 if (seq == 1000) {
772 free(result);
773 errno = EINVAL;
774 return NULL;
775 }
776
777 return result;
778}
779
780/**
781 * Create and open an unique temporary file.
782 * The file is automatically removed when the stream is closed.
783 *
784 * @param dir Path to directory, where the file should be created.
785 * @param pfx Optional prefix up to 5 characters long.
786 * @return Newly allocated unique path for temporary file. NULL on failure.
787 */
788FILE *posix_tmpfile(void)
789{
790 char filename[] = "/tmp/tfXXXXXX";
791 int fd = posix_mkstemp(filename);
792 if (fd == -1) {
793 /* errno set by mkstemp(). */
794 return NULL;
795 }
796
797 /* Unlink the created file, so that it's removed on close(). */
798 posix_unlink(filename);
799 return fdopen(fd, "w+");
800}
801
802/** @}
803 */
Note: See TracBrowser for help on using the repository browser.