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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since bfe90b6 was 4e6a610, checked in by Jiri Svoboda <jiri@…>, 7 years ago

Temporary file functions rework. Fix libposix access() not working on directories.

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