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

topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 694ca3d6 was 694ca3d6, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 20 months ago

Deduplicate printf_core()

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