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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4e6a610 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
RevLine 
[09b0b1fb]1/*
2 * Copyright (c) 2011 Jiri Zarevucky
[4f4b4e7]3 * Copyright (c) 2011 Petr Koupy
[4e6a610]4 * Copyright (c) 2018 Jiri Svoboda
[09b0b1fb]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 */
[4cf8ca6]34/** @file Standard buffered input/output.
[09b0b1fb]35 */
36
[9b1503e]37#include "internal/common.h"
[a3da2b2]38#include "posix/stdio.h"
[a6d908c1]39
[e8d3c6f5]40#include <assert.h>
[0d0b319]41#include <errno.h>
[4e6a610]42#include <stdbool.h>
43#include <tmpfile.h>
[0d0b319]44
[4e6a610]45#include "posix/fcntl.h"
[a3da2b2]46#include "posix/stdlib.h"
47#include "posix/string.h"
[4e6a610]48#include "posix/sys/stat.h"
[a3da2b2]49#include "posix/sys/types.h"
50#include "posix/unistd.h"
[4f4b4e7]51
[32b3a12]52#include "libc/stdio.h"
[a6d908c1]53#include "libc/io/printf_core.h"
54#include "libc/str.h"
[08053f7]55#include "libc/malloc.h"
[fd4b636]56#include "libc/adt/list.h"
[a6d908c1]57
[8b5fb5e]58/**
[1978a5f]59 * Generate a pathname for the controlling terminal.
[8b5fb5e]60 *
[1978a5f]61 * @param s Allocated buffer to which the pathname shall be put.
62 * @return Either s or static location filled with the requested pathname.
[8b5fb5e]63 */
[7f9df7b9]64char *ctermid(char *s)
[8b5fb5e]65{
66 /* Currently always returns an error value (empty string). */
67 // TODO: return a real terminal path
68
[1433ecda]69 static char dummy_path[L_ctermid] = { '\0' };
[8b5fb5e]70
71 if (s == NULL) {
72 return dummy_path;
73 }
74
75 s[0] = '\0';
76 return s;
77}
78
[1978a5f]79/**
[08053f7]80 * Read a stream until the delimiter (or EOF) is encountered.
[1978a5f]81 *
[08053f7]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).
[1978a5f]91 */
[7f9df7b9]92ssize_t getdelim(char **restrict lineptr, size_t *restrict n,
[8b5fb5e]93 int delimiter, FILE *restrict stream)
94{
[08053f7]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) {
[7c3fb9b]132 /*
133 * Delimiter was just stored. Provide EOF as the next
[08053f7]134 * character - it will be masked as NUL and output string
[7c3fb9b]135 * will be properly terminated.
136 */
[08053f7]137 c = EOF;
138 } else {
[7c3fb9b]139 /*
140 * Neither delimiter nor EOF were encountered. Just fetch
141 * the next character from the stream.
142 */
[08053f7]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 }
[8b5fb5e]154}
155
[1978a5f]156/**
[08053f7]157 * Read a stream until the newline (or EOF) is encountered.
[1b20da0]158 *
[08053f7]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).
[1978a5f]167 */
[7f9df7b9]168ssize_t getline(char **restrict lineptr, size_t *restrict n,
[8b5fb5e]169 FILE *restrict stream)
170{
[7f9df7b9]171 return getdelim(lineptr, n, '\n', stream);
[09b0b1fb]172}
173
[8b5fb5e]174/**
[1978a5f]175 * Reposition a file-position indicator in a stream.
[1b20da0]176 *
[1978a5f]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.
[8b5fb5e]181 */
[7f9df7b9]182int fseeko(FILE *stream, off_t offset, int whence)
[b08ef1fd]183{
[e0f47f5]184 return fseek64(stream, offset, whence);
[8b5fb5e]185}
186
187/**
[1978a5f]188 * Discover current file offset in a stream.
[1b20da0]189 *
[1978a5f]190 * @param stream Stream for which the offset shall be retrieved.
191 * @return Current offset or -1 if not possible.
[8b5fb5e]192 */
[7f9df7b9]193off_t ftello(FILE *stream)
[b08ef1fd]194{
[e0f47f5]195 return ftell64(stream);
[8b5fb5e]196}
197
[1978a5f]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 */
[7f9df7b9]205int dprintf(int fildes, const char *restrict format, ...)
[8b5fb5e]206{
207 va_list list;
208 va_start(list, format);
[7f9df7b9]209 int result = vdprintf(fildes, format, list);
[8b5fb5e]210 va_end(list);
211 return result;
212}
213
[1978a5f]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 */
[8b5fb5e]222static int _dprintf_str_write(const char *str, size_t size, void *fd)
223{
[58898d1d]224 const int fildes = *(int *) fd;
[8e3498b]225 size_t wr;
[0d0b319]226 if (failed(vfs_write(fildes, &posix_pos[fildes], str, size, &wr)))
227 return -1;
[8b5fb5e]228 return str_nlength(str, wr);
229}
230
[1978a5f]231/**
232 * Write wide string to the opened file.
[1b20da0]233 *
[1978a5f]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 */
[8b5fb5e]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];
[a35b458]245
[8b5fb5e]246 while (offset < size) {
247 sz = 0;
248 if (chr_encode(str[chars], buf, &sz, sizeof(buf)) != EOK) {
249 break;
250 }
[a35b458]251
[58898d1d]252 const int fildes = *(int *) fd;
[8e3498b]253 size_t nwr;
254 if (vfs_write(fildes, &posix_pos[fildes], buf, sz, &nwr) != EOK)
[8b5fb5e]255 break;
[a35b458]256
[8b5fb5e]257 chars++;
258 offset += sizeof(wchar_t);
259 }
[a35b458]260
[8b5fb5e]261 return chars;
262}
263
[1978a5f]264/**
265 * Print formatted output to the opened file.
[1b20da0]266 *
[1978a5f]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 */
[7f9df7b9]272int vdprintf(int fildes, const char *restrict format, va_list ap)
[8b5fb5e]273{
274 printf_spec_t spec = {
275 .str_write = _dprintf_str_write,
276 .wstr_write = _dprintf_wstr_write,
277 .data = &fildes
278 };
[a35b458]279
[8b5fb5e]280 return printf_core(format, &spec, ap);
[b08ef1fd]281}
282
[1978a5f]283/**
284 * Acquire file stream for the thread.
285 *
286 * @param file File stream to lock.
287 */
[7f9df7b9]288void flockfile(FILE *file)
[8b5fb5e]289{
290 /* dummy */
291}
292
[1978a5f]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 */
[7f9df7b9]299int ftrylockfile(FILE *file)
[8b5fb5e]300{
301 /* dummy */
302 return 0;
303}
304
[1978a5f]305/**
306 * Relinquish the ownership of the locked file stream.
307 *
308 * @param file File stream to unlock.
309 */
[7f9df7b9]310void funlockfile(FILE *file)
[8b5fb5e]311{
312 /* dummy */
313}
314
[1978a5f]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 */
[7f9df7b9]321int getc_unlocked(FILE *stream)
[8b5fb5e]322{
323 return getc(stream);
324}
325
[1978a5f]326/**
327 * Get a byte from the standard input stream (thread-unsafe).
328 *
329 * @return Either read byte or EOF.
330 */
[7f9df7b9]331int getchar_unlocked(void)
[8b5fb5e]332{
333 return getchar();
334}
335
[1978a5f]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 */
[7f9df7b9]343int putc_unlocked(int c, FILE *stream)
[8b5fb5e]344{
345 return putc(c, stream);
346}
347
[1978a5f]348/**
349 * Put a byte on the standard output stream (thread-unsafe).
[1b20da0]350 *
[1978a5f]351 * @param c Byte to output.
352 * @return Either written byte or EOF.
353 */
[7f9df7b9]354int putchar_unlocked(int c)
[8b5fb5e]355{
356 return putchar(c);
357}
358
[4e6a610]359/** Determine if directory is an 'appropriate' temporary directory.
[11544f4]360 *
[4e6a610]361 * @param dir Directory path
362 * @return @c true iff directory is appropriate.
[823a929]363 */
[4e6a610]364static bool is_appropriate_tmpdir(const char *dir)
[823a929]365{
[4e6a610]366 struct stat sbuf;
[a35b458]367
[4e6a610]368 /* Must not be NULL */
369 if (dir == NULL)
370 return false;
[a35b458]371
[4e6a610]372 /* Must not be empty */
373 if (dir[0] == '\0')
374 return false;
[a35b458]375
[4e6a610]376 if (stat(dir, &sbuf) != 0)
377 return false;
[a35b458]378
[4e6a610]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;
[11544f4]388}
389
[4e6a610]390/** Construct unique file name.
391 *
392 * Never use this function.
[11544f4]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 */
[7f9df7b9]398char *tempnam(const char *dir, const char *pfx)
[11544f4]399{
[4e6a610]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)
[11544f4]421 return NULL;
[a35b458]422
[4e6a610]423 rc = __tmpfile_templ(buf, false);
424 if (rc != 0) {
425 free(buf);
[11544f4]426 return NULL;
427 }
[a35b458]428
[4e6a610]429 return buf;
[823a929]430}
431
[09b0b1fb]432/** @}
433 */
Note: See TracBrowser for help on using the repository browser.