2 * Copyright (c) 2011 Jiri Zarevucky
3 * Copyright (c) 2011 Petr Koupy
4 * Copyright (c) 2018 Jiri Svoboda
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
11 * - Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * - Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * - The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 /** @addtogroup libposix
34 /** @file Standard buffered input/output.
37 #define _LARGEFILE64_SOURCE
38 #undef _FILE_OFFSET_BITS
40 #include "internal/common.h"
52 #include <sys/types.h>
55 #include <printf_core.h>
61 * Generate a pathname for the controlling terminal.
63 * @param s Allocated buffer to which the pathname shall be put.
64 * @return Either s or static location filled with the requested pathname.
66 char *ctermid(char *s
)
68 /* Currently always returns an error value (empty string). */
69 // TODO: return a real terminal path
71 static char dummy_path
[L_ctermid
] = { '\0' };
82 * Read a stream until the delimiter (or EOF) is encountered.
84 * @param lineptr Pointer to the output buffer in which there will be stored
85 * nul-terminated string together with the delimiter (if encountered).
86 * Will be resized if necessary.
87 * @param n Pointer to the size of the output buffer. Will be increased if
89 * @param delimiter Delimiter on which to finish reading the stream.
90 * @param stream Input stream.
91 * @return Number of fetched characters (including delimiter if encountered)
92 * or -1 on error (set in errno).
94 ssize_t
getdelim(char **restrict lineptr
, size_t *restrict n
,
95 int delimiter
, FILE *restrict stream
)
97 /* Check arguments for sanity. */
103 size_t alloc_step
= 80; /* Buffer size gain during reallocation. */
104 char *pos
= *lineptr
; /* Next free byte of the output buffer. */
105 size_t cnt
= 0; /* Number of fetched characters. */
106 int c
= fgetc(stream
); /* Current input character. Might be EOF. */
109 /* Mask EOF as NUL to terminate string. */
114 /* Ensure there is still space left in the buffer. */
115 if (pos
== *lineptr
+ *n
) {
116 *lineptr
= realloc(*lineptr
, *n
+ alloc_step
);
126 /* Store the fetched character. */
129 /* Fetch the next character according to the current character. */
133 if (c
== delimiter
) {
135 * Delimiter was just stored. Provide EOF as the next
136 * character - it will be masked as NUL and output string
137 * will be properly terminated.
142 * Neither delimiter nor EOF were encountered. Just fetch
143 * the next character from the stream.
150 if (errno
== EOK
&& cnt
> 0) {
153 /* Either some error occured or the stream was already at EOF. */
159 * Read a stream until the newline (or EOF) is encountered.
161 * @param lineptr Pointer to the output buffer in which there will be stored
162 * nul-terminated string together with the delimiter (if encountered).
163 * Will be resized if necessary.
164 * @param n Pointer to the size of the output buffer. Will be increased if
166 * @param stream Input stream.
167 * @return Number of fetched characters (including newline if encountered)
168 * or -1 on error (set in errno).
170 ssize_t
getline(char **restrict lineptr
, size_t *restrict n
,
171 FILE *restrict stream
)
173 return getdelim(lineptr
, n
, '\n', stream
);
177 * Reposition a file-position indicator in a stream.
179 * @param stream Stream to seek in.
180 * @param offset Direction and amount of bytes to seek.
181 * @param whence From where to seek.
182 * @return Zero on success, -1 otherwise.
184 int fseeko(FILE *stream
, off_t offset
, int whence
)
186 return fseek(stream
, offset
, whence
);
190 * Discover current file offset in a stream.
192 * @param stream Stream for which the offset shall be retrieved.
193 * @return Current offset or -1 if not possible.
195 off_t
ftello(FILE *stream
)
197 return ftell(stream
);
200 int fseeko64(FILE *stream
, off64_t offset
, int whence
)
202 return fseek64(stream
, offset
, whence
);
205 off64_t
ftello64(FILE *stream
)
207 return ftell64(stream
);
211 * Print formatted output to the opened file.
213 * @param fildes File descriptor of the opened file.
214 * @param format Format description.
215 * @return Either the number of printed characters or negative value on error.
217 int dprintf(int fildes
, const char *restrict format
, ...)
220 va_start(list
, format
);
221 int result
= vdprintf(fildes
, format
, list
);
227 * Write ordinary string to the opened file.
229 * @param str String to be written.
230 * @param size Size of the string (in bytes)..
231 * @param fd File descriptor of the opened file.
232 * @return The number of written characters.
234 static int _dprintf_str_write(const char *str
, size_t size
, void *fd
)
236 const int fildes
= *(int *) fd
;
238 if (failed(vfs_write(fildes
, &posix_pos
[fildes
], str
, size
, &wr
)))
240 return str_nlength(str
, wr
);
244 * Write wide string to the opened file.
246 * @param str String to be written.
247 * @param size Size of the string (in bytes).
248 * @param fd File descriptor of the opened file.
249 * @return The number of written characters.
251 static int _dprintf_wstr_write(const char32_t
*str
, size_t size
, void *fd
)
258 while (offset
< size
) {
260 if (chr_encode(str
[chars
], buf
, &sz
, sizeof(buf
)) != EOK
) {
264 const int fildes
= *(int *) fd
;
266 if (vfs_write(fildes
, &posix_pos
[fildes
], buf
, sz
, &nwr
) != EOK
)
270 offset
+= sizeof(char32_t
);
277 * Print formatted output to the opened file.
279 * @param fildes File descriptor of the opened file.
280 * @param format Format description.
281 * @param ap Print arguments.
282 * @return Either the number of printed characters or negative value on error.
284 int vdprintf(int fildes
, const char *restrict format
, va_list ap
)
286 printf_spec_t spec
= {
287 .str_write
= _dprintf_str_write
,
288 .wstr_write
= _dprintf_wstr_write
,
292 return printf_core(format
, &spec
, ap
);
296 * Acquire file stream for the thread.
298 * @param file File stream to lock.
300 void flockfile(FILE *file
)
306 * Acquire file stream for the thread (non-blocking).
308 * @param file File stream to lock.
309 * @return Zero for success and non-zero if the lock cannot be acquired.
311 int ftrylockfile(FILE *file
)
318 * Relinquish the ownership of the locked file stream.
320 * @param file File stream to unlock.
322 void funlockfile(FILE *file
)
328 * Get a byte from a stream (thread-unsafe).
330 * @param stream Input file stream.
331 * @return Either read byte or EOF.
333 int getc_unlocked(FILE *stream
)
339 * Get a byte from the standard input stream (thread-unsafe).
341 * @return Either read byte or EOF.
343 int getchar_unlocked(void)
349 * Put a byte on a stream (thread-unsafe).
351 * @param c Byte to output.
352 * @param stream Output file stream.
353 * @return Either written byte or EOF.
355 int putc_unlocked(int c
, FILE *stream
)
357 return putc(c
, stream
);
361 * Put a byte on the standard output stream (thread-unsafe).
363 * @param c Byte to output.
364 * @return Either written byte or EOF.
366 int putchar_unlocked(int c
)
371 /** Determine if directory is an 'appropriate' temporary directory.
373 * @param dir Directory path
374 * @return @c true iff directory is appropriate.
376 static bool is_appropriate_tmpdir(const char *dir
)
380 /* Must not be NULL */
384 /* Must not be empty */
388 if (stat(dir
, &sbuf
) != 0)
391 /* Must be a directory */
392 if ((sbuf
.st_mode
& S_IFMT
) != S_IFDIR
)
395 /* Must be writable */
396 if (access(dir
, W_OK
) != 0)
402 /** Construct unique file name.
404 * Never use this function.
406 * @param dir Path to directory, where the file should be created.
407 * @param pfx Optional prefix up to 5 characters long.
408 * @return Newly allocated unique path for temporary file. NULL on failure.
410 char *tempnam(const char *dir
, const char *pfx
)
417 d
= getenv("TMPDIR");
418 if (is_appropriate_tmpdir(d
))
420 else if (is_appropriate_tmpdir(dir
))
422 else if (is_appropriate_tmpdir(P_tmpdir
))
427 if (dpref
[strlen(dpref
) - 1] != '/')
428 rc
= asprintf(&buf
, "%s/%sXXXXXX", dpref
, pfx
);
430 rc
= asprintf(&buf
, "%s%sXXXXXX", dpref
, pfx
);
435 rc
= __tmpfile_templ(buf
, false);