/*
* Copyright (c) 2011 Jiri Zarevucky
* Copyright (c) 2011 Petr Koupy
* Copyright (c) 2018 Jiri Svoboda
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* - The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @addtogroup libposix
* @{
*/
/** @file Standard buffered input/output.
*/
#define _LARGEFILE64_SOURCE
#undef _FILE_OFFSET_BITS
#include "internal/common.h"
#include <stdio.h>
#include <assert.h>
#include <errno.h>
#include <stdbool.h>
#include <tmpfile.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <printf_core.h>
#include <str.h>
#include <stdlib.h>
#include <adt/list.h>
/**
* Generate a pathname for the controlling terminal.
*
* @param s Allocated buffer to which the pathname shall be put.
* @return Either s or static location filled with the requested pathname.
*/
char *ctermid(char *s)
{
/* Currently always returns an error value (empty string). */
// TODO: return a real terminal path
static char dummy_path[L_ctermid] = { '\0' };
if (s == NULL) {
return dummy_path;
}
s[0] = '\0';
return s;
}
/**
* Read a stream until the delimiter (or EOF) is encountered.
*
* @param lineptr Pointer to the output buffer in which there will be stored
* nul-terminated string together with the delimiter (if encountered).
* Will be resized if necessary.
* @param n Pointer to the size of the output buffer. Will be increased if
* necessary.
* @param delimiter Delimiter on which to finish reading the stream.
* @param stream Input stream.
* @return Number of fetched characters (including delimiter if encountered)
* or -1 on error (set in errno).
*/
ssize_t getdelim(char **restrict lineptr, size_t *restrict n,
int delimiter, FILE *restrict stream)
{
/* Check arguments for sanity. */
if (!lineptr || !n) {
errno = EINVAL;
return -1;
}
size_t alloc_step = 80; /* Buffer size gain during reallocation. */
char *pos = *lineptr; /* Next free byte of the output buffer. */
size_t cnt = 0; /* Number of fetched characters. */
int c = fgetc(stream); /* Current input character. Might be EOF. */
do {
/* Mask EOF as NUL to terminate string. */
if (c == EOF) {
c = '\0';
}
/* Ensure there is still space left in the buffer. */
if (pos == *lineptr + *n) {
*lineptr = realloc(*lineptr, *n + alloc_step);
if (*lineptr) {
pos = *lineptr + *n;
*n += alloc_step;
} else {
errno = ENOMEM;
return -1;
}
}
/* Store the fetched character. */
*pos = c;
/* Fetch the next character according to the current character. */
if (c != '\0') {
++pos;
++cnt;
if (c == delimiter) {
/*
* Delimiter was just stored. Provide EOF as the next
* character - it will be masked as NUL and output string
* will be properly terminated.
*/
c = EOF;
} else {
/*
* Neither delimiter nor EOF were encountered. Just fetch
* the next character from the stream.
*/
c = fgetc(stream);
}
}
} while (c != '\0');
if (errno == EOK && cnt > 0) {
return cnt;
} else {
/* Either some error occured or the stream was already at EOF. */
return -1;
}
}
/**
* Read a stream until the newline (or EOF) is encountered.
*
* @param lineptr Pointer to the output buffer in which there will be stored
* nul-terminated string together with the delimiter (if encountered).
* Will be resized if necessary.
* @param n Pointer to the size of the output buffer. Will be increased if
* necessary.
* @param stream Input stream.
* @return Number of fetched characters (including newline if encountered)
* or -1 on error (set in errno).
*/
ssize_t getline(char **restrict lineptr, size_t *restrict n,
FILE *restrict stream)
{
return getdelim(lineptr, n, '\n', stream);
}
/**
* Reposition a file-position indicator in a stream.
*
* @param stream Stream to seek in.
* @param offset Direction and amount of bytes to seek.
* @param whence From where to seek.
* @return Zero on success, -1 otherwise.
*/
int fseeko(FILE *stream, off_t offset, int whence)
{
return fseek(stream, offset, whence);
}
/**
* Discover current file offset in a stream.
*
* @param stream Stream for which the offset shall be retrieved.
* @return Current offset or -1 if not possible.
*/
off_t ftello(FILE *stream)
{
return ftell(stream);
}
int fseeko64(FILE *stream, off64_t offset, int whence)
{
return fseek64(stream, offset, whence);
}
off64_t ftello64(FILE *stream)
{
return ftell64(stream);
}
/**
* Print formatted output to the opened file.
*
* @param fildes File descriptor of the opened file.
* @param format Format description.
* @return Either the number of printed characters or negative value on error.
*/
int dprintf(int fildes, const char *restrict format, ...)
{
va_list list;
va_start(list, format);
int result = vdprintf(fildes, format, list);
va_end(list);
return result;
}
/**
* Write ordinary string to the opened file.
*
* @param str String to be written.
* @param size Size of the string (in bytes)..
* @param fd File descriptor of the opened file.
* @return The number of written characters.
*/
static errno_t _dprintf_str_write(const char *str, size_t size, void *fd)
{
const int fildes = *(int *) fd;
size_t wr;
return vfs_write(fildes, &posix_pos[fildes], str, size, &wr);
}
/**
* Print formatted output to the opened file.
*
* @param fildes File descriptor of the opened file.
* @param format Format description.
* @param ap Print arguments.
* @return Either the number of printed characters or negative value on error.
*/
int vdprintf(int fildes, const char *restrict format, va_list ap)
{
printf_spec_t spec = {
.write = _dprintf_str_write,
.data = &fildes
};
return printf_core(format, &spec, ap);
}
/**
* Acquire file stream for the thread.
*
* @param file File stream to lock.
*/
void flockfile(FILE *file)
{
/* dummy */
}
/**
* Acquire file stream for the thread (non-blocking).
*
* @param file File stream to lock.
* @return Zero for success and non-zero if the lock cannot be acquired.
*/
int ftrylockfile(FILE *file)
{
/* dummy */
return 0;
}
/**
* Relinquish the ownership of the locked file stream.
*
* @param file File stream to unlock.
*/
void funlockfile(FILE *file)
{
/* dummy */
}
/**
* Get a byte from a stream (thread-unsafe).
*
* @param stream Input file stream.
* @return Either read byte or EOF.
*/
int getc_unlocked(FILE *stream)
{
return getc(stream);
}
/**
* Get a byte from the standard input stream (thread-unsafe).
*
* @return Either read byte or EOF.
*/
int getchar_unlocked(void)
{
return getchar();
}
/**
* Put a byte on a stream (thread-unsafe).
*
* @param c Byte to output.
* @param stream Output file stream.
* @return Either written byte or EOF.
*/
int putc_unlocked(int c, FILE *stream)
{
return putc(c, stream);
}
/**
* Put a byte on the standard output stream (thread-unsafe).
*
* @param c Byte to output.
* @return Either written byte or EOF.
*/
int putchar_unlocked(int c)
{
return putchar(c);
}
/** Determine if directory is an 'appropriate' temporary directory.
*
* @param dir Directory path
* @return @c true iff directory is appropriate.
*/
static bool is_appropriate_tmpdir(const char *dir)
{
struct stat sbuf;
/* Must not be NULL */
if (dir == NULL)
return false;
/* Must not be empty */
if (dir[0] == '\0')
return false;
if (stat(dir, &sbuf) != 0)
return false;
/* Must be a directory */
if ((sbuf.st_mode & S_IFMT) != S_IFDIR)
return false;
/* Must be writable */
if (access(dir, W_OK) != 0)
return false;
return true;
}
/** Construct unique file name.
*
* Never use this function.
*
* @param dir Path to directory, where the file should be created.
* @param pfx Optional prefix up to 5 characters long.
* @return Newly allocated unique path for temporary file. NULL on failure.
*/
char *tempnam(const char *dir, const char *pfx)
{
const char *dpref;
char *d;
char *buf;
int rc;
d = getenv("TMPDIR");
if (is_appropriate_tmpdir(d))
dpref = d;
else if (is_appropriate_tmpdir(dir))
dpref = dir;
else if (is_appropriate_tmpdir(P_tmpdir))
dpref = P_tmpdir;
else
dpref = "/";
if (dpref[strlen(dpref) - 1] != '/')
rc = asprintf(&buf, "%s/%sXXXXXX", dpref, pfx);
else
rc = asprintf(&buf, "%s%sXXXXXX", dpref, pfx);
if (rc < 0)
return NULL;
rc = __tmpfile_templ(buf, false);
if (rc != 0) {
free(buf);
return NULL;
}
return buf;
}
/** @}
*/