2
* Copyright (c) 2011 Jiri Zarevucky
3
* Copyright (c) 2011 Petr Koupy
6
* Redistribution and use in source and binary forms, with or without
7
* modification, are permitted provided that the following conditions
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.
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.
30
/** @addtogroup libposix
33
/** @file Standard buffered input/output.
36
#define LIBPOSIX_INTERNAL
38
/* Has to be first. */
41
#include "internal/common.h"
48
#include "sys/types.h"
51
#include "libc/io/printf_core.h"
53
#include "libc/malloc.h"
54
#include "libc/adt/list.h"
55
#include "libc/sys/stat.h"
58
/* not the best of solutions, but freopen and ungetc will eventually
59
* need to be implemented in libc anyway
61
#include "../c/generic/private/stdio.h"
63
/** Clears the stream's error and end-of-file indicators.
65
* @param stream Stream whose indicators shall be cleared.
67
void posix_clearerr(FILE *stream)
74
* Generate a pathname for the controlling terminal.
76
* @param s Allocated buffer to which the pathname shall be put.
77
* @return Either s or static location filled with the requested pathname.
79
char *posix_ctermid(char *s)
81
/* Currently always returns an error value (empty string). */
82
// TODO: return a real terminal path
84
static char dummy_path[L_ctermid] = {'\0'};
95
* Put a string on the stream.
97
* @param s String to be written.
98
* @param stream Output stream.
99
* @return Non-negative on success, EOF on failure.
101
int posix_fputs(const char *restrict s, FILE *restrict stream)
103
int rc = fputs(s, stream);
112
* Push byte back into input stream.
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.
118
int posix_ungetc(int c, FILE *stream)
120
uint8_t b = (uint8_t) c;
123
/* Provided character is legal. */
125
/* Stream is consistent. */
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;
143
stream->buf_tail[0] = b;
152
* Read a stream until the delimiter (or EOF) is encountered.
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
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).
164
ssize_t posix_getdelim(char **restrict lineptr, size_t *restrict n,
165
int delimiter, FILE *restrict stream)
167
/* Check arguments for sanity. */
168
if (!lineptr || !n) {
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. */
179
/* Mask EOF as NUL to terminate string. */
184
/* Ensure there is still space left in the buffer. */
185
if (pos == *lineptr + *n) {
186
*lineptr = realloc(*lineptr, *n + alloc_step);
196
/* Store the fetched character. */
199
/* Fetch the next character according to the current character. */
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. */
209
/* Neither delimiter nor EOF were encountered. Just fetch
210
* the next character from the stream. */
216
if (errno == EOK && cnt > 0) {
219
/* Either some error occured or the stream was already at EOF. */
225
* Read a stream until the newline (or EOF) is encountered.
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
232
* @param stream Input stream.
233
* @return Number of fetched characters (including newline if encountered)
234
* or -1 on error (set in errno).
236
ssize_t posix_getline(char **restrict lineptr, size_t *restrict n,
237
FILE *restrict stream)
239
return posix_getdelim(lineptr, n, '\n', stream);
243
* Reopen a file stream.
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.
253
FILE *posix_freopen(const char *restrict filename,
254
const char *restrict mode, FILE *restrict stream)
256
assert(mode != NULL);
257
assert(stream != NULL);
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
266
/* Open a new stream. */
267
FILE* new = fopen(filename, mode);
270
/* errno was set by fopen() */
274
/* Close the original stream without freeing it (ignoring errors). */
275
if (stream->buf != NULL) {
278
if (stream->sess != NULL) {
279
async_hangup(stream->sess);
281
if (stream->fd >= 0) {
284
list_remove(&stream->link);
286
/* Move the new stream to the original location. */
287
memcpy(stream, new, sizeof (FILE));
290
/* Update references in the file list. */
291
stream->link.next->prev = &stream->link;
292
stream->link.prev->next = &stream->link;
298
* Write error messages to standard error.
300
* @param s Error message.
302
void posix_perror(const char *s)
304
if (s == NULL || s[0] == '\0') {
305
fprintf(stderr, "%s\n", posix_strerror(errno));
307
fprintf(stderr, "%s: %s\n", s, posix_strerror(errno));
315
/** Restores stream a to position previously saved with fgetpos().
317
* @param stream Stream to restore
318
* @param pos Position to restore
319
* @return Zero on success, non-zero (with errno set) on failure
321
int posix_fsetpos(FILE *stream, const posix_fpos_t *pos)
323
return fseek(stream, pos->offset, SEEK_SET);
326
/** Saves the stream's position for later use by fsetpos().
328
* @param stream Stream to save
329
* @param pos Place to store the position
330
* @return Zero on success, non-zero (with errno set) on failure
332
int posix_fgetpos(FILE *restrict stream, posix_fpos_t *restrict pos)
334
off64_t ret = ftell(stream);
344
* Reposition a file-position indicator in a stream.
346
* @param stream Stream to seek in.
347
* @param offset Direction and amount of bytes to seek.
348
* @param whence From where to seek.
349
* @return Zero on success, -1 otherwise.
351
int posix_fseek(FILE *stream, long offset, int whence)
353
return fseek(stream, (off64_t) offset, whence);
357
* Reposition a file-position indicator in a stream.
359
* @param stream Stream to seek in.
360
* @param offset Direction and amount of bytes to seek.
361
* @param whence From where to seek.
362
* @return Zero on success, -1 otherwise.
364
int posix_fseeko(FILE *stream, posix_off_t offset, int whence)
366
return fseek(stream, (off64_t) offset, whence);
370
* Discover current file offset in a stream.
372
* @param stream Stream for which the offset shall be retrieved.
373
* @return Current offset or -1 if not possible.
375
long posix_ftell(FILE *stream)
377
return (long) ftell(stream);
381
* Discover current file offset in a stream.
383
* @param stream Stream for which the offset shall be retrieved.
384
* @return Current offset or -1 if not possible.
386
posix_off_t posix_ftello(FILE *stream)
388
return (posix_off_t) ftell(stream);
392
* Discard prefetched data or write unwritten data.
394
* @param stream Stream that shall be flushed.
395
* @return Zero on success, EOF on failure.
397
int posix_fflush(FILE *stream)
399
int rc = fflush(stream);
409
* Print formatted output to the opened file.
411
* @param fildes File descriptor of the opened file.
412
* @param format Format description.
413
* @return Either the number of printed characters or negative value on error.
415
int posix_dprintf(int fildes, const char *restrict format, ...)
418
va_start(list, format);
419
int result = posix_vdprintf(fildes, format, list);
425
* Write ordinary string to the opened file.
427
* @param str String to be written.
428
* @param size Size of the string (in bytes)..
429
* @param fd File descriptor of the opened file.
430
* @return The number of written characters.
432
static int _dprintf_str_write(const char *str, size_t size, void *fd)
434
ssize_t wr = write(*(int *) fd, str, size);
435
return str_nlength(str, wr);
439
* Write wide string to the opened file.
441
* @param str String to be written.
442
* @param size Size of the string (in bytes).
443
* @param fd File descriptor of the opened file.
444
* @return The number of written characters.
446
static int _dprintf_wstr_write(const wchar_t *str, size_t size, void *fd)
453
while (offset < size) {
455
if (chr_encode(str[chars], buf, &sz, sizeof(buf)) != EOK) {
459
if (write(*(int *) fd, buf, sz) != (ssize_t) sz) {
464
offset += sizeof(wchar_t);
471
* Print formatted output to the opened file.
473
* @param fildes File descriptor of the opened file.
474
* @param format Format description.
475
* @param ap Print arguments.
476
* @return Either the number of printed characters or negative value on error.
478
int posix_vdprintf(int fildes, const char *restrict format, va_list ap)
480
printf_spec_t spec = {
481
.str_write = _dprintf_str_write,
482
.wstr_write = _dprintf_wstr_write,
486
return printf_core(format, &spec, ap);
490
* Print formatted output to the string.
492
* @param s Output string.
493
* @param format Format description.
494
* @return Either the number of printed characters (excluding null byte) or
495
* negative value on error.
497
int posix_sprintf(char *s, const char *restrict format, ...)
500
va_start(list, format);
501
int result = posix_vsprintf(s, format, list);
507
* Print formatted output to the string.
509
* @param s Output string.
510
* @param format Format description.
511
* @param ap Print arguments.
512
* @return Either the number of printed characters (excluding null byte) or
513
* negative value on error.
515
int posix_vsprintf(char *s, const char *restrict format, va_list ap)
517
return vsnprintf(s, STR_NO_LIMIT, format, ap);
521
* Convert formatted input from the stream.
523
* @param stream Input stream.
524
* @param format Format description.
525
* @return The number of converted output items or EOF on failure.
527
int posix_fscanf(FILE *restrict stream, const char *restrict format, ...)
530
va_start(list, format);
531
int result = posix_vfscanf(stream, format, list);
537
* Convert formatted input from the standard input.
539
* @param format Format description.
540
* @return The number of converted output items or EOF on failure.
542
int posix_scanf(const char *restrict format, ...)
545
va_start(list, format);
546
int result = posix_vscanf(format, list);
552
* Convert formatted input from the standard input.
554
* @param format Format description.
555
* @param arg Output items.
556
* @return The number of converted output items or EOF on failure.
558
int posix_vscanf(const char *restrict format, va_list arg)
560
return posix_vfscanf(stdin, format, arg);
564
* Convert formatted input from the string.
566
* @param s Input string.
567
* @param format Format description.
568
* @return The number of converted output items or EOF on failure.
570
int posix_sscanf(const char *restrict s, const char *restrict format, ...)
573
va_start(list, format);
574
int result = posix_vsscanf(s, format, list);
580
* Acquire file stream for the thread.
582
* @param file File stream to lock.
584
void posix_flockfile(FILE *file)
590
* Acquire file stream for the thread (non-blocking).
592
* @param file File stream to lock.
593
* @return Zero for success and non-zero if the lock cannot be acquired.
595
int posix_ftrylockfile(FILE *file)
602
* Relinquish the ownership of the locked file stream.
604
* @param file File stream to unlock.
606
void posix_funlockfile(FILE *file)
612
* Get a byte from a stream (thread-unsafe).
614
* @param stream Input file stream.
615
* @return Either read byte or EOF.
617
int posix_getc_unlocked(FILE *stream)
623
* Get a byte from the standard input stream (thread-unsafe).
625
* @return Either read byte or EOF.
627
int posix_getchar_unlocked(void)
633
* Put a byte on a stream (thread-unsafe).
635
* @param c Byte to output.
636
* @param stream Output file stream.
637
* @return Either written byte or EOF.
639
int posix_putc_unlocked(int c, FILE *stream)
641
return putc(c, stream);
645
* Put a byte on the standard output stream (thread-unsafe).
647
* @param c Byte to output.
648
* @return Either written byte or EOF.
650
int posix_putchar_unlocked(int c)
656
* Remove a file or directory.
658
* @param path Pathname of the file that shall be removed.
659
* @return Zero on success, -1 (with errno set) otherwise.
661
int posix_remove(const char *path)
664
int rc = stat(path, &st);
671
if (st.is_directory) {
685
* Rename a file or directory.
687
* @param old Old pathname.
688
* @param new New pathname.
689
* @return Zero on success, -1 (with errno set) otherwise.
691
int posix_rename(const char *old, const char *new)
693
return errnify(rename, old, new);
697
* Get a unique temporary file name (obsolete).
699
* @param s Buffer for the file name. Must be at least L_tmpnam bytes long.
700
* @return The value of s on success, NULL on failure.
702
char *posix_tmpnam(char *s)
704
assert(L_tmpnam >= posix_strlen("/tmp/tnXXXXXX"));
706
static char buffer[L_tmpnam + 1];
711
posix_strcpy(s, "/tmp/tnXXXXXX");
715
/* Errno set by mktemp(). */
723
* Get an unique temporary file name with additional constraints (obsolete).
725
* @param dir Path to directory, where the file should be created.
726
* @param pfx Optional prefix up to 5 characters long.
727
* @return Newly allocated unique path for temporary file. NULL on failure.
729
char *posix_tempnam(const char *dir, const char *pfx)
731
/* Sequence number of the filename. */
734
size_t dir_len = posix_strlen(dir);
735
if (dir[dir_len - 1] == '/') {
739
size_t pfx_len = posix_strlen(pfx);
744
char *result = malloc(dir_len + /* slash*/ 1 +
745
pfx_len + /* three-digit seq */ 3 + /* .tmp */ 4 + /* nul */ 1);
747
if (result == NULL) {
752
char *res_ptr = result;
753
posix_strncpy(res_ptr, dir, dir_len);
755
posix_strncpy(res_ptr, pfx, pfx_len);
758
for (; seq < 1000; ++seq) {
759
snprintf(res_ptr, 8, "%03d.tmp", seq);
761
int orig_errno = errno;
763
/* Check if the file exists. */
764
if (posix_access(result, F_OK) == -1) {
765
if (errno == ENOENT) {
769
/* errno set by access() */
785
* Create and open an unique temporary file.
786
* The file is automatically removed when the stream is closed.
788
* @param dir Path to directory, where the file should be created.
789
* @param pfx Optional prefix up to 5 characters long.
790
* @return Newly allocated unique path for temporary file. NULL on failure.
792
FILE *posix_tmpfile(void)
794
char filename[] = "/tmp/tfXXXXXX";
795
int fd = posix_mkstemp(filename);
797
/* errno set by mkstemp(). */
801
/* Unlink the created file, so that it's removed on close(). */
802
posix_unlink(filename);
803
return fdopen(fd, "w+");