source: mainline/uspace/lib/posix/src/stdio.c@ 460ea7e

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

libposix: Change header organization and remove passthrough headers

Posix headers now function like an overlay. The system include directories
are searched after posix directories. The headers don't need to be patched
for export now. libposix files now include headers using bracket notation
instead of quoted notation.

  • Property mode set to 100644
File size: 10.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Zarevucky
3 * Copyright (c) 2011 Petr Koupy
4 * Copyright (c) 2018 Jiri Svoboda
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
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.
18 *
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.
29 */
30
31/** @addtogroup libposix
32 * @{
33 */
34/** @file Standard buffered input/output.
35 */
36
37#include "internal/common.h"
38#include <stdio.h>
39
40#include <assert.h>
41#include <errno.h>
42#include <stdbool.h>
43#include <tmpfile.h>
44
45#include <fcntl.h>
46#include <stdlib.h>
47#include <string.h>
48#include <sys/stat.h>
49#include <sys/types.h>
50#include <unistd.h>
51
52#include <io/printf_core.h>
53#include <str.h>
54#include <malloc.h>
55#include <adt/list.h>
56
57/**
58 * Generate a pathname for the controlling terminal.
59 *
60 * @param s Allocated buffer to which the pathname shall be put.
61 * @return Either s or static location filled with the requested pathname.
62 */
63char *ctermid(char *s)
64{
65 /* Currently always returns an error value (empty string). */
66 // TODO: return a real terminal path
67
68 static char dummy_path[L_ctermid] = { '\0' };
69
70 if (s == NULL) {
71 return dummy_path;
72 }
73
74 s[0] = '\0';
75 return s;
76}
77
78/**
79 * Read a stream until the delimiter (or EOF) is encountered.
80 *
81 * @param lineptr Pointer to the output buffer in which there will be stored
82 * nul-terminated string together with the delimiter (if encountered).
83 * Will be resized if necessary.
84 * @param n Pointer to the size of the output buffer. Will be increased if
85 * necessary.
86 * @param delimiter Delimiter on which to finish reading the stream.
87 * @param stream Input stream.
88 * @return Number of fetched characters (including delimiter if encountered)
89 * or -1 on error (set in errno).
90 */
91ssize_t getdelim(char **restrict lineptr, size_t *restrict n,
92 int delimiter, FILE *restrict stream)
93{
94 /* Check arguments for sanity. */
95 if (!lineptr || !n) {
96 errno = EINVAL;
97 return -1;
98 }
99
100 size_t alloc_step = 80; /* Buffer size gain during reallocation. */
101 char *pos = *lineptr; /* Next free byte of the output buffer. */
102 size_t cnt = 0; /* Number of fetched characters. */
103 int c = fgetc(stream); /* Current input character. Might be EOF. */
104
105 do {
106 /* Mask EOF as NUL to terminate string. */
107 if (c == EOF) {
108 c = '\0';
109 }
110
111 /* Ensure there is still space left in the buffer. */
112 if (pos == *lineptr + *n) {
113 *lineptr = realloc(*lineptr, *n + alloc_step);
114 if (*lineptr) {
115 pos = *lineptr + *n;
116 *n += alloc_step;
117 } else {
118 errno = ENOMEM;
119 return -1;
120 }
121 }
122
123 /* Store the fetched character. */
124 *pos = c;
125
126 /* Fetch the next character according to the current character. */
127 if (c != '\0') {
128 ++pos;
129 ++cnt;
130 if (c == delimiter) {
131 /*
132 * Delimiter was just stored. Provide EOF as the next
133 * character - it will be masked as NUL and output string
134 * will be properly terminated.
135 */
136 c = EOF;
137 } else {
138 /*
139 * Neither delimiter nor EOF were encountered. Just fetch
140 * the next character from the stream.
141 */
142 c = fgetc(stream);
143 }
144 }
145 } while (c != '\0');
146
147 if (errno == EOK && cnt > 0) {
148 return cnt;
149 } else {
150 /* Either some error occured or the stream was already at EOF. */
151 return -1;
152 }
153}
154
155/**
156 * Read a stream until the newline (or EOF) is encountered.
157 *
158 * @param lineptr Pointer to the output buffer in which there will be stored
159 * nul-terminated string together with the delimiter (if encountered).
160 * Will be resized if necessary.
161 * @param n Pointer to the size of the output buffer. Will be increased if
162 * necessary.
163 * @param stream Input stream.
164 * @return Number of fetched characters (including newline if encountered)
165 * or -1 on error (set in errno).
166 */
167ssize_t getline(char **restrict lineptr, size_t *restrict n,
168 FILE *restrict stream)
169{
170 return getdelim(lineptr, n, '\n', stream);
171}
172
173/**
174 * Reposition a file-position indicator in a stream.
175 *
176 * @param stream Stream to seek in.
177 * @param offset Direction and amount of bytes to seek.
178 * @param whence From where to seek.
179 * @return Zero on success, -1 otherwise.
180 */
181int fseeko(FILE *stream, off_t offset, int whence)
182{
183 return fseek64(stream, offset, whence);
184}
185
186/**
187 * Discover current file offset in a stream.
188 *
189 * @param stream Stream for which the offset shall be retrieved.
190 * @return Current offset or -1 if not possible.
191 */
192off_t ftello(FILE *stream)
193{
194 return ftell64(stream);
195}
196
197/**
198 * Print formatted output to the opened file.
199 *
200 * @param fildes File descriptor of the opened file.
201 * @param format Format description.
202 * @return Either the number of printed characters or negative value on error.
203 */
204int dprintf(int fildes, const char *restrict format, ...)
205{
206 va_list list;
207 va_start(list, format);
208 int result = vdprintf(fildes, format, list);
209 va_end(list);
210 return result;
211}
212
213/**
214 * Write ordinary string to the opened file.
215 *
216 * @param str String to be written.
217 * @param size Size of the string (in bytes)..
218 * @param fd File descriptor of the opened file.
219 * @return The number of written characters.
220 */
221static int _dprintf_str_write(const char *str, size_t size, void *fd)
222{
223 const int fildes = *(int *) fd;
224 size_t wr;
225 if (failed(vfs_write(fildes, &posix_pos[fildes], str, size, &wr)))
226 return -1;
227 return str_nlength(str, wr);
228}
229
230/**
231 * Write wide string to the opened file.
232 *
233 * @param str String to be written.
234 * @param size Size of the string (in bytes).
235 * @param fd File descriptor of the opened file.
236 * @return The number of written characters.
237 */
238static int _dprintf_wstr_write(const wchar_t *str, size_t size, void *fd)
239{
240 size_t offset = 0;
241 size_t chars = 0;
242 size_t sz;
243 char buf[4];
244
245 while (offset < size) {
246 sz = 0;
247 if (chr_encode(str[chars], buf, &sz, sizeof(buf)) != EOK) {
248 break;
249 }
250
251 const int fildes = *(int *) fd;
252 size_t nwr;
253 if (vfs_write(fildes, &posix_pos[fildes], buf, sz, &nwr) != EOK)
254 break;
255
256 chars++;
257 offset += sizeof(wchar_t);
258 }
259
260 return chars;
261}
262
263/**
264 * Print formatted output to the opened file.
265 *
266 * @param fildes File descriptor of the opened file.
267 * @param format Format description.
268 * @param ap Print arguments.
269 * @return Either the number of printed characters or negative value on error.
270 */
271int vdprintf(int fildes, const char *restrict format, va_list ap)
272{
273 printf_spec_t spec = {
274 .str_write = _dprintf_str_write,
275 .wstr_write = _dprintf_wstr_write,
276 .data = &fildes
277 };
278
279 return printf_core(format, &spec, ap);
280}
281
282/**
283 * Acquire file stream for the thread.
284 *
285 * @param file File stream to lock.
286 */
287void flockfile(FILE *file)
288{
289 /* dummy */
290}
291
292/**
293 * Acquire file stream for the thread (non-blocking).
294 *
295 * @param file File stream to lock.
296 * @return Zero for success and non-zero if the lock cannot be acquired.
297 */
298int ftrylockfile(FILE *file)
299{
300 /* dummy */
301 return 0;
302}
303
304/**
305 * Relinquish the ownership of the locked file stream.
306 *
307 * @param file File stream to unlock.
308 */
309void funlockfile(FILE *file)
310{
311 /* dummy */
312}
313
314/**
315 * Get a byte from a stream (thread-unsafe).
316 *
317 * @param stream Input file stream.
318 * @return Either read byte or EOF.
319 */
320int getc_unlocked(FILE *stream)
321{
322 return getc(stream);
323}
324
325/**
326 * Get a byte from the standard input stream (thread-unsafe).
327 *
328 * @return Either read byte or EOF.
329 */
330int getchar_unlocked(void)
331{
332 return getchar();
333}
334
335/**
336 * Put a byte on a stream (thread-unsafe).
337 *
338 * @param c Byte to output.
339 * @param stream Output file stream.
340 * @return Either written byte or EOF.
341 */
342int putc_unlocked(int c, FILE *stream)
343{
344 return putc(c, stream);
345}
346
347/**
348 * Put a byte on the standard output stream (thread-unsafe).
349 *
350 * @param c Byte to output.
351 * @return Either written byte or EOF.
352 */
353int putchar_unlocked(int c)
354{
355 return putchar(c);
356}
357
358/** Determine if directory is an 'appropriate' temporary directory.
359 *
360 * @param dir Directory path
361 * @return @c true iff directory is appropriate.
362 */
363static bool is_appropriate_tmpdir(const char *dir)
364{
365 struct stat sbuf;
366
367 /* Must not be NULL */
368 if (dir == NULL)
369 return false;
370
371 /* Must not be empty */
372 if (dir[0] == '\0')
373 return false;
374
375 if (stat(dir, &sbuf) != 0)
376 return false;
377
378 /* Must be a directory */
379 if ((sbuf.st_mode & S_IFMT) != S_IFDIR)
380 return false;
381
382 /* Must be writable */
383 if (access(dir, W_OK) != 0)
384 return false;
385
386 return true;
387}
388
389/** Construct unique file name.
390 *
391 * Never use this function.
392 *
393 * @param dir Path to directory, where the file should be created.
394 * @param pfx Optional prefix up to 5 characters long.
395 * @return Newly allocated unique path for temporary file. NULL on failure.
396 */
397char *tempnam(const char *dir, const char *pfx)
398{
399 const char *dpref;
400 char *d;
401 char *buf;
402 int rc;
403
404 d = getenv("TMPDIR");
405 if (is_appropriate_tmpdir(d))
406 dpref = d;
407 else if (is_appropriate_tmpdir(dir))
408 dpref = dir;
409 else if (is_appropriate_tmpdir(P_tmpdir))
410 dpref = P_tmpdir;
411 else
412 dpref = "/";
413
414 if (dpref[strlen(dpref) - 1] != '/')
415 rc = asprintf(&buf, "%s/%sXXXXXX", dpref, pfx);
416 else
417 rc = asprintf(&buf, "%s%sXXXXXX", dpref, pfx);
418
419 if (rc < 0)
420 return NULL;
421
422 rc = __tmpfile_templ(buf, false);
423 if (rc != 0) {
424 free(buf);
425 return NULL;
426 }
427
428 return buf;
429}
430
431/** @}
432 */
Note: See TracBrowser for help on using the repository browser.