source: mainline/uspace/lib/posix/source/stdio.c@ fdf97f6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since fdf97f6 was fdf97f6, checked in by Vojtech Horky <vojtechhorky@…>, 12 years ago

Libposix functions are without posix_ prefix

Prior this commit, libposix headers declared all functions as posix_*
and used macros to rename e.g. strncpy to posix_strncpy in all (ported)
sources.

After this change, libposix headers look as normal POSIX compliant headers
(well, almost) and no renaming is done in the source codei (of the ported
applications). Instead, the renaming is done at object files level to
bypass weird problems that are bound to happen if you use macros.

The scheme is following. libposix headers use special macro to declare
the names. When included from outside, the functions have their normal
(standard) names. When included from the libposix sources, posix_ prefix
is added. Thus, when libposix is compiled and linked, it contains the
posix_* naming while compiling of ported software uses the normal
non-prefixed versions. This way the posix_* can use HelenOS libc without
any problem. Before linking, the posix_* prefix is removed from all
symbols and special prefix helenos_libc_ is added to all functions
that exists in our (HelenOS) libc and its name clashes with the POSIX
one.

The following happens, for example, to the open() function that exists in
both libposix and in libc.

  • Headers and sources of libc are left intact.
  • Copy of libc.a is made and to all clashing functions is added the helenos_libc prefix. This library is called libc4posix.a.
  • POSIX_DEF(open)(const char *) is used in libposix headers. This macro expands to plain open when included from the "outside world". But it expands to posix_open when included from libposix sources.
  • Libposix is compiled and linked, containing posix_open() that internally calls open() [the original one from libc].
  • Libposix is transformed - all open() are replaced with prefix variant: helenos_libc_open() and all posix_open() are replaced with open(). The transformed library is stored as libposixaslibc.a

Binutils and PCC are then linked with libc4posix and libposixaslibc
libraries instead of libc and libposix as was done previously.

WARNING: it looks that binutils, PCC and MSIM still works but not all
architectures were tested.

  • Property mode set to 100644
File size: 19.2 KB
Line 
1/*
2 * Copyright (c) 2011 Jiri Zarevucky
3 * Copyright (c) 2011 Petr Koupy
4 * All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * - Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * - Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * - The name of the author may not be used to endorse or promote products
16 * derived from this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30/** @addtogroup libposix
31 * @{
32 */
33/** @file Standard buffered input/output.
34 */
35
36#define LIBPOSIX_INTERNAL
37#define __POSIX_DEF__(x) posix_##x
38
39#include "internal/common.h"
40#include "posix/stdio.h"
41
42#include "posix/assert.h"
43#include "posix/errno.h"
44#include "posix/stdlib.h"
45#include "posix/string.h"
46#include "posix/sys/types.h"
47#include "posix/unistd.h"
48
49#include "libc/stdio.h"
50#include "libc/io/printf_core.h"
51#include "libc/stdbool.h"
52#include "libc/str.h"
53#include "libc/malloc.h"
54#include "libc/adt/list.h"
55#include "libc/sys/stat.h"
56
57
58/* not the best of solutions, but freopen and ungetc will eventually
59 * need to be implemented in libc anyway
60 */
61#include "../../c/generic/private/stdio.h"
62
63/** Clears the stream's error and end-of-file indicators.
64 *
65 * @param stream Stream whose indicators shall be cleared.
66 */
67void posix_clearerr(FILE *stream)
68{
69 stream->error = 0;
70 stream->eof = 0;
71}
72
73/**
74 * Generate a pathname for the controlling terminal.
75 *
76 * @param s Allocated buffer to which the pathname shall be put.
77 * @return Either s or static location filled with the requested pathname.
78 */
79char *posix_ctermid(char *s)
80{
81 /* Currently always returns an error value (empty string). */
82 // TODO: return a real terminal path
83
84 static char dummy_path[L_ctermid] = {'\0'};
85
86 if (s == NULL) {
87 return dummy_path;
88 }
89
90 s[0] = '\0';
91 return s;
92}
93
94/**
95 * Put a string on the stream.
96 *
97 * @param s String to be written.
98 * @param stream Output stream.
99 * @return Non-negative on success, EOF on failure.
100 */
101int posix_fputs(const char *restrict s, FILE *restrict stream)
102{
103 int rc = fputs(s, stream);
104 if (rc == 0) {
105 return EOF;
106 } else {
107 return 0;
108 }
109}
110
111/**
112 * Push byte back into input stream.
113 *
114 * @param c Byte to be pushed back.
115 * @param stream Stream to where the byte shall be pushed.
116 * @return Provided byte on success or EOF if not possible.
117 */
118int posix_ungetc(int c, FILE *stream)
119{
120 uint8_t b = (uint8_t) c;
121
122 bool can_unget =
123 /* Provided character is legal. */
124 c != EOF &&
125 /* Stream is consistent. */
126 !stream->error &&
127 /* Stream is buffered. */
128 stream->btype != _IONBF &&
129 /* Last operation on the stream was a read operation. */
130 stream->buf_state == _bs_read &&
131 /* Stream buffer is already allocated (i.e. there was already carried
132 * out either write or read operation on the stream). This is probably
133 * redundant check but let's be safe. */
134 stream->buf != NULL &&
135 /* There is still space in the stream to retreat. POSIX demands the
136 * possibility to unget at least 1 character. It should be always
137 * possible, assuming the last operation on the stream read at least 1
138 * character, because the buffer is refilled in the lazily manner. */
139 stream->buf_tail > stream->buf;
140
141 if (can_unget) {
142 --stream->buf_tail;
143 stream->buf_tail[0] = b;
144 stream->eof = false;
145 return (int) b;
146 } else {
147 return EOF;
148 }
149}
150
151/**
152 * Read a stream until the delimiter (or EOF) is encountered.
153 *
154 * @param lineptr Pointer to the output buffer in which there will be stored
155 * nul-terminated string together with the delimiter (if encountered).
156 * Will be resized if necessary.
157 * @param n Pointer to the size of the output buffer. Will be increased if
158 * necessary.
159 * @param delimiter Delimiter on which to finish reading the stream.
160 * @param stream Input stream.
161 * @return Number of fetched characters (including delimiter if encountered)
162 * or -1 on error (set in errno).
163 */
164ssize_t posix_getdelim(char **restrict lineptr, size_t *restrict n,
165 int delimiter, FILE *restrict stream)
166{
167 /* Check arguments for sanity. */
168 if (!lineptr || !n) {
169 errno = EINVAL;
170 return -1;
171 }
172
173 size_t alloc_step = 80; /* Buffer size gain during reallocation. */
174 char *pos = *lineptr; /* Next free byte of the output buffer. */
175 size_t cnt = 0; /* Number of fetched characters. */
176 int c = fgetc(stream); /* Current input character. Might be EOF. */
177
178 do {
179 /* Mask EOF as NUL to terminate string. */
180 if (c == EOF) {
181 c = '\0';
182 }
183
184 /* Ensure there is still space left in the buffer. */
185 if (pos == *lineptr + *n) {
186 *lineptr = realloc(*lineptr, *n + alloc_step);
187 if (*lineptr) {
188 pos = *lineptr + *n;
189 *n += alloc_step;
190 } else {
191 errno = ENOMEM;
192 return -1;
193 }
194 }
195
196 /* Store the fetched character. */
197 *pos = c;
198
199 /* Fetch the next character according to the current character. */
200 if (c != '\0') {
201 ++pos;
202 ++cnt;
203 if (c == delimiter) {
204 /* Delimiter was just stored. Provide EOF as the next
205 * character - it will be masked as NUL and output string
206 * will be properly terminated. */
207 c = EOF;
208 } else {
209 /* Neither delimiter nor EOF were encountered. Just fetch
210 * the next character from the stream. */
211 c = fgetc(stream);
212 }
213 }
214 } while (c != '\0');
215
216 if (errno == EOK && cnt > 0) {
217 return cnt;
218 } else {
219 /* Either some error occured or the stream was already at EOF. */
220 return -1;
221 }
222}
223
224/**
225 * Read a stream until the newline (or EOF) is encountered.
226 *
227 * @param lineptr Pointer to the output buffer in which there will be stored
228 * nul-terminated string together with the delimiter (if encountered).
229 * Will be resized if necessary.
230 * @param n Pointer to the size of the output buffer. Will be increased if
231 * necessary.
232 * @param stream Input stream.
233 * @return Number of fetched characters (including newline if encountered)
234 * or -1 on error (set in errno).
235 */
236ssize_t posix_getline(char **restrict lineptr, size_t *restrict n,
237 FILE *restrict stream)
238{
239 return posix_getdelim(lineptr, n, '\n', stream);
240}
241
242/**
243 * Reopen a file stream.
244 *
245 * @param filename Pathname of a file to be reopened or NULL for changing
246 * the mode of the stream.
247 * @param mode Mode to be used for reopening the file or changing current
248 * mode of the stream.
249 * @param stream Current stream associated with the opened file.
250 * @return On success, either a stream of the reopened file or the provided
251 * stream with a changed mode. NULL otherwise.
252 */
253FILE *posix_freopen(const char *restrict filename,
254 const char *restrict mode, FILE *restrict stream)
255{
256 assert(mode != NULL);
257 assert(stream != NULL);
258
259 if (filename == NULL) {
260 /* POSIX allows this to be imlementation-defined. HelenOS currently
261 * does not support changing the mode. */
262 // FIXME: handle mode change once it is supported
263 return stream;
264 }
265
266 /* Open a new stream. */
267 FILE* new = fopen(filename, mode);
268 if (new == NULL) {
269 fclose(stream);
270 /* errno was set by fopen() */
271 return NULL;
272 }
273
274 /* Close the original stream without freeing it (ignoring errors). */
275 if (stream->buf != NULL) {
276 fflush(stream);
277 }
278 if (stream->sess != NULL) {
279 async_hangup(stream->sess);
280 }
281 if (stream->fd >= 0) {
282 close(stream->fd);
283 }
284 list_remove(&stream->link);
285
286 /* Move the new stream to the original location. */
287 memcpy(stream, new, sizeof (FILE));
288 free(new);
289
290 /* Update references in the file list. */
291 stream->link.next->prev = &stream->link;
292 stream->link.prev->next = &stream->link;
293
294 return stream;
295}
296
297/**
298 * Write error messages to standard error.
299 *
300 * @param s Error message.
301 */
302void posix_perror(const char *s)
303{
304 if (s == NULL || s[0] == '\0') {
305 fprintf(stderr, "%s\n", posix_strerror(errno));
306 } else {
307 fprintf(stderr, "%s: %s\n", s, posix_strerror(errno));
308 }
309}
310
311struct _posix_fpos {
312 off64_t offset;
313};
314
315/** Restores stream a to position previously saved with fgetpos().
316 *
317 * @param stream Stream to restore
318 * @param pos Position to restore
319 * @return Zero on success, non-zero (with errno set) on failure
320 */
321int posix_fsetpos(FILE *stream, const posix_fpos_t *pos)
322{
323 return fseek(stream, pos->offset, SEEK_SET);
324}
325
326/** Saves the stream's position for later use by fsetpos().
327 *
328 * @param stream Stream to save
329 * @param pos Place to store the position
330 * @return Zero on success, non-zero (with errno set) on failure
331 */
332int posix_fgetpos(FILE *restrict stream, posix_fpos_t *restrict pos)
333{
334 off64_t ret = ftell(stream);
335 if (ret != -1) {
336 pos->offset = ret;
337 return 0;
338 } else {
339 return -1;
340 }
341}
342
343/**
344 * Reposition a file-position indicator in a stream.
345 *
346 * @param stream Stream to seek in.
347 * @param offset Direction and amount of bytes to seek.
348 * @param whence From where to seek.
349 * @return Zero on success, -1 otherwise.
350 */
351int posix_fseek(FILE *stream, long offset, int whence)
352{
353 return fseek(stream, (off64_t) offset, whence);
354}
355
356/**
357 * Reposition a file-position indicator in a stream.
358 *
359 * @param stream Stream to seek in.
360 * @param offset Direction and amount of bytes to seek.
361 * @param whence From where to seek.
362 * @return Zero on success, -1 otherwise.
363 */
364int posix_fseeko(FILE *stream, posix_off_t offset, int whence)
365{
366 return fseek(stream, (off64_t) offset, whence);
367}
368
369/**
370 * Discover current file offset in a stream.
371 *
372 * @param stream Stream for which the offset shall be retrieved.
373 * @return Current offset or -1 if not possible.
374 */
375long posix_ftell(FILE *stream)
376{
377 return (long) ftell(stream);
378}
379
380/**
381 * Discover current file offset in a stream.
382 *
383 * @param stream Stream for which the offset shall be retrieved.
384 * @return Current offset or -1 if not possible.
385 */
386posix_off_t posix_ftello(FILE *stream)
387{
388 return (posix_off_t) ftell(stream);
389}
390
391/**
392 * Discard prefetched data or write unwritten data.
393 *
394 * @param stream Stream that shall be flushed.
395 * @return Zero on success, EOF on failure.
396 */
397int posix_fflush(FILE *stream)
398{
399 int rc = fflush(stream);
400 if (rc < 0) {
401 errno = -rc;
402 return EOF;
403 } else {
404 return 0;
405 }
406}
407
408/**
409 * Print formatted output to the opened file.
410 *
411 * @param fildes File descriptor of the opened file.
412 * @param format Format description.
413 * @return Either the number of printed characters or negative value on error.
414 */
415int posix_dprintf(int fildes, const char *restrict format, ...)
416{
417 va_list list;
418 va_start(list, format);
419 int result = posix_vdprintf(fildes, format, list);
420 va_end(list);
421 return result;
422}
423
424/**
425 * Write ordinary string to the opened file.
426 *
427 * @param str String to be written.
428 * @param size Size of the string (in bytes)..
429 * @param fd File descriptor of the opened file.
430 * @return The number of written characters.
431 */
432static int _dprintf_str_write(const char *str, size_t size, void *fd)
433{
434 ssize_t wr = write(*(int *) fd, str, size);
435 return str_nlength(str, wr);
436}
437
438/**
439 * Write wide string to the opened file.
440 *
441 * @param str String to be written.
442 * @param size Size of the string (in bytes).
443 * @param fd File descriptor of the opened file.
444 * @return The number of written characters.
445 */
446static int _dprintf_wstr_write(const wchar_t *str, size_t size, void *fd)
447{
448 size_t offset = 0;
449 size_t chars = 0;
450 size_t sz;
451 char buf[4];
452
453 while (offset < size) {
454 sz = 0;
455 if (chr_encode(str[chars], buf, &sz, sizeof(buf)) != EOK) {
456 break;
457 }
458
459 if (write(*(int *) fd, buf, sz) != (ssize_t) sz) {
460 break;
461 }
462
463 chars++;
464 offset += sizeof(wchar_t);
465 }
466
467 return chars;
468}
469
470/**
471 * Print formatted output to the opened file.
472 *
473 * @param fildes File descriptor of the opened file.
474 * @param format Format description.
475 * @param ap Print arguments.
476 * @return Either the number of printed characters or negative value on error.
477 */
478int posix_vdprintf(int fildes, const char *restrict format, va_list ap)
479{
480 printf_spec_t spec = {
481 .str_write = _dprintf_str_write,
482 .wstr_write = _dprintf_wstr_write,
483 .data = &fildes
484 };
485
486 return printf_core(format, &spec, ap);
487}
488
489/**
490 * Print formatted output to the string.
491 *
492 * @param s Output string.
493 * @param format Format description.
494 * @return Either the number of printed characters (excluding null byte) or
495 * negative value on error.
496 */
497int posix_sprintf(char *s, const char *restrict format, ...)
498{
499 va_list list;
500 va_start(list, format);
501 int result = posix_vsprintf(s, format, list);
502 va_end(list);
503 return result;
504}
505
506/**
507 * Print formatted output to the string.
508 *
509 * @param s Output string.
510 * @param format Format description.
511 * @param ap Print arguments.
512 * @return Either the number of printed characters (excluding null byte) or
513 * negative value on error.
514 */
515int posix_vsprintf(char *s, const char *restrict format, va_list ap)
516{
517 return vsnprintf(s, STR_NO_LIMIT, format, ap);
518}
519
520/**
521 * Convert formatted input from the stream.
522 *
523 * @param stream Input stream.
524 * @param format Format description.
525 * @return The number of converted output items or EOF on failure.
526 */
527int posix_fscanf(FILE *restrict stream, const char *restrict format, ...)
528{
529 va_list list;
530 va_start(list, format);
531 int result = posix_vfscanf(stream, format, list);
532 va_end(list);
533 return result;
534}
535
536/**
537 * Convert formatted input from the standard input.
538 *
539 * @param format Format description.
540 * @return The number of converted output items or EOF on failure.
541 */
542int posix_scanf(const char *restrict format, ...)
543{
544 va_list list;
545 va_start(list, format);
546 int result = posix_vscanf(format, list);
547 va_end(list);
548 return result;
549}
550
551/**
552 * Convert formatted input from the standard input.
553 *
554 * @param format Format description.
555 * @param arg Output items.
556 * @return The number of converted output items or EOF on failure.
557 */
558int posix_vscanf(const char *restrict format, va_list arg)
559{
560 return posix_vfscanf(stdin, format, arg);
561}
562
563/**
564 * Convert formatted input from the string.
565 *
566 * @param s Input string.
567 * @param format Format description.
568 * @return The number of converted output items or EOF on failure.
569 */
570int posix_sscanf(const char *restrict s, const char *restrict format, ...)
571{
572 va_list list;
573 va_start(list, format);
574 int result = posix_vsscanf(s, format, list);
575 va_end(list);
576 return result;
577}
578
579/**
580 * Acquire file stream for the thread.
581 *
582 * @param file File stream to lock.
583 */
584void posix_flockfile(FILE *file)
585{
586 /* dummy */
587}
588
589/**
590 * Acquire file stream for the thread (non-blocking).
591 *
592 * @param file File stream to lock.
593 * @return Zero for success and non-zero if the lock cannot be acquired.
594 */
595int posix_ftrylockfile(FILE *file)
596{
597 /* dummy */
598 return 0;
599}
600
601/**
602 * Relinquish the ownership of the locked file stream.
603 *
604 * @param file File stream to unlock.
605 */
606void posix_funlockfile(FILE *file)
607{
608 /* dummy */
609}
610
611/**
612 * Get a byte from a stream (thread-unsafe).
613 *
614 * @param stream Input file stream.
615 * @return Either read byte or EOF.
616 */
617int posix_getc_unlocked(FILE *stream)
618{
619 return getc(stream);
620}
621
622/**
623 * Get a byte from the standard input stream (thread-unsafe).
624 *
625 * @return Either read byte or EOF.
626 */
627int posix_getchar_unlocked(void)
628{
629 return getchar();
630}
631
632/**
633 * Put a byte on a stream (thread-unsafe).
634 *
635 * @param c Byte to output.
636 * @param stream Output file stream.
637 * @return Either written byte or EOF.
638 */
639int posix_putc_unlocked(int c, FILE *stream)
640{
641 return putc(c, stream);
642}
643
644/**
645 * Put a byte on the standard output stream (thread-unsafe).
646 *
647 * @param c Byte to output.
648 * @return Either written byte or EOF.
649 */
650int posix_putchar_unlocked(int c)
651{
652 return putchar(c);
653}
654
655/**
656 * Remove a file or directory.
657 *
658 * @param path Pathname of the file that shall be removed.
659 * @return Zero on success, -1 (with errno set) otherwise.
660 */
661int posix_remove(const char *path)
662{
663 struct stat st;
664 int rc = stat(path, &st);
665
666 if (rc != EOK) {
667 errno = -rc;
668 return -1;
669 }
670
671 if (st.is_directory) {
672 rc = rmdir(path);
673 } else {
674 rc = unlink(path);
675 }
676
677 if (rc != EOK) {
678 errno = -rc;
679 return -1;
680 }
681 return 0;
682}
683
684/**
685 * Rename a file or directory.
686 *
687 * @param old Old pathname.
688 * @param new New pathname.
689 * @return Zero on success, -1 (with errno set) otherwise.
690 */
691int posix_rename(const char *old, const char *new)
692{
693 return errnify(rename, old, new);
694}
695
696/**
697 * Get a unique temporary file name (obsolete).
698 *
699 * @param s Buffer for the file name. Must be at least L_tmpnam bytes long.
700 * @return The value of s on success, NULL on failure.
701 */
702char *posix_tmpnam(char *s)
703{
704 assert(L_tmpnam >= posix_strlen("/tmp/tnXXXXXX"));
705
706 static char buffer[L_tmpnam + 1];
707 if (s == NULL) {
708 s = buffer;
709 }
710
711 posix_strcpy(s, "/tmp/tnXXXXXX");
712 posix_mktemp(s);
713
714 if (*s == '\0') {
715 /* Errno set by mktemp(). */
716 return NULL;
717 }
718
719 return s;
720}
721
722/**
723 * Get an unique temporary file name with additional constraints (obsolete).
724 *
725 * @param dir Path to directory, where the file should be created.
726 * @param pfx Optional prefix up to 5 characters long.
727 * @return Newly allocated unique path for temporary file. NULL on failure.
728 */
729char *posix_tempnam(const char *dir, const char *pfx)
730{
731 /* Sequence number of the filename. */
732 static int seq = 0;
733
734 size_t dir_len = posix_strlen(dir);
735 if (dir[dir_len - 1] == '/') {
736 dir_len--;
737 }
738
739 size_t pfx_len = posix_strlen(pfx);
740 if (pfx_len > 5) {
741 pfx_len = 5;
742 }
743
744 char *result = malloc(dir_len + /* slash*/ 1 +
745 pfx_len + /* three-digit seq */ 3 + /* .tmp */ 4 + /* nul */ 1);
746
747 if (result == NULL) {
748 errno = ENOMEM;
749 return NULL;
750 }
751
752 char *res_ptr = result;
753 posix_strncpy(res_ptr, dir, dir_len);
754 res_ptr += dir_len;
755 posix_strncpy(res_ptr, pfx, pfx_len);
756 res_ptr += pfx_len;
757
758 for (; seq < 1000; ++seq) {
759 snprintf(res_ptr, 8, "%03d.tmp", seq);
760
761 int orig_errno = errno;
762 errno = 0;
763 /* Check if the file exists. */
764 if (posix_access(result, F_OK) == -1) {
765 if (errno == ENOENT) {
766 errno = orig_errno;
767 break;
768 } else {
769 /* errno set by access() */
770 return NULL;
771 }
772 }
773 }
774
775 if (seq == 1000) {
776 free(result);
777 errno = EINVAL;
778 return NULL;
779 }
780
781 return result;
782}
783
784/**
785 * Create and open an unique temporary file.
786 * The file is automatically removed when the stream is closed.
787 *
788 * @param dir Path to directory, where the file should be created.
789 * @param pfx Optional prefix up to 5 characters long.
790 * @return Newly allocated unique path for temporary file. NULL on failure.
791 */
792FILE *posix_tmpfile(void)
793{
794 char filename[] = "/tmp/tfXXXXXX";
795 int fd = posix_mkstemp(filename);
796 if (fd == -1) {
797 /* errno set by mkstemp(). */
798 return NULL;
799 }
800
801 /* Unlink the created file, so that it's removed on close(). */
802 posix_unlink(filename);
803 return fdopen(fd, "w+");
804}
805
806/** @}
807 */
Note: See TracBrowser for help on using the repository browser.