fileio.c revision c4cd1d4d93e4a45a088edb6517555aa7e06e5f86
/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
/***
This file is part of systemd.
Copyright 2010 Lennart Poettering
under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
systemd is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with systemd; If not, see <http://www.gnu.org/licenses/>.
***/
#include <unistd.h>
#include "util.h"
#include "strv.h"
#include "utf8.h"
#include "ctype.h"
#include "fileio.h"
assert(f);
fputc('\n', f);
return fflush_and_check(f);
}
_cleanup_free_ char *p = NULL;
int r;
r = fopen_temporary(fn, &f, &p);
if (r < 0)
return r;
if (r >= 0) {
r = -errno;
}
if (r < 0)
unlink(p);
return r;
}
if (flags & WRITE_STRING_FILE_ATOMIC) {
}
if (flags & WRITE_STRING_FILE_CREATE) {
if (!f)
return -errno;
} else {
int fd;
/* We manually build our own version of fopen(..., "we") that
* works without O_CREAT */
if (fd < 0)
return -errno;
if (!f) {
safe_close(fd);
return -errno;
}
}
}
char t[LINE_MAX], *c;
if (!f)
return -errno;
if (!fgets(t, sizeof(t), f)) {
if (ferror(f))
t[0] = 0;
}
c = strdup(t);
if (!c)
return -ENOMEM;
truncate_nl(c);
*line = c;
return 0;
}
int r;
if (r < 0)
return r;
}
size_t n, l;
assert(f);
return -errno;
n = LINE_MAX;
/* Safety check */
return -E2BIG;
/* Start with the right file size, but be prepared for
* files from /proc which generally report a file size
* of 0 */
}
l = 0;
for (;;) {
char *t;
size_t k;
if (!t)
return -ENOMEM;
buf = t;
if (k <= 0) {
if (ferror(f))
return -errno;
break;
}
l += k;
n *= 2;
/* Safety check */
if (n > 4*1024*1024)
return -E2BIG;
}
buf[l] = 0;
if (size)
*size = l;
return 0;
}
if (!f)
return -errno;
}
static int parse_env_file_internal(
FILE *f,
const char *fname,
const char *newline,
void *userdata,
int *n_pushed) {
size_t key_alloc = 0, n_key = 0, value_alloc = 0, n_value = 0, last_value_whitespace = (size_t) -1, last_key_whitespace = (size_t) -1;
int r;
unsigned line = 1;
enum {
KEY,
if (f)
else
if (r < 0)
return r;
for (p = contents; *p; p++) {
char c = *p;
switch (state) {
case PRE_KEY:
else if (!strchr(WHITESPACE, c)) {
r = -ENOMEM;
goto fail;
}
}
break;
case KEY:
line ++;
n_key = 0;
} else if (c == '=') {
} else {
if (!strchr(WHITESPACE, c))
r = -ENOMEM;
goto fail;
}
}
break;
case PRE_VALUE:
line ++;
if (value)
/* strip trailing whitespace from key */
key[last_key_whitespace] = 0;
if (r < 0)
goto fail;
n_key = 0;
value_alloc = n_value = 0;
} else if (c == '\'')
else if (c == '\"')
else if (c == '\\')
else if (!strchr(WHITESPACE, c)) {
r = -ENOMEM;
goto fail;
}
}
break;
case VALUE:
line ++;
if (value)
/* Chomp off trailing whitespace from value */
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
key[last_key_whitespace] = 0;
if (r < 0)
goto fail;
n_key = 0;
value_alloc = n_value = 0;
} else if (c == '\\') {
} else {
if (!strchr(WHITESPACE, c))
r = -ENOMEM;
goto fail;
}
}
break;
case VALUE_ESCAPE:
/* Escaped newlines we eat up entirely */
r = -ENOMEM;
goto fail;
}
}
break;
case SINGLE_QUOTE_VALUE:
if (c == '\'')
else if (c == '\\')
else {
r = -ENOMEM;
goto fail;
}
}
break;
r = -ENOMEM;
goto fail;
}
}
break;
case DOUBLE_QUOTE_VALUE:
if (c == '\"')
else if (c == '\\')
else {
r = -ENOMEM;
goto fail;
}
}
break;
r = -ENOMEM;
goto fail;
}
}
break;
case COMMENT:
if (c == '\\')
line ++;
}
break;
case COMMENT_ESCAPE:
break;
}
}
state == VALUE_ESCAPE ||
state == SINGLE_QUOTE_VALUE ||
state == DOUBLE_QUOTE_VALUE ||
if (value)
value[last_value_whitespace] = 0;
/* strip trailing whitespace from key */
key[last_key_whitespace] = 0;
if (r < 0)
goto fail;
}
return 0;
fail:
return r;
}
static int parse_env_file_push(
void *userdata,
int *n_pushed) {
const char *k;
if (!utf8_is_valid(key)) {
_cleanup_free_ char *p;
p = utf8_escape_invalid(key);
return -EINVAL;
}
_cleanup_free_ char *p;
p = utf8_escape_invalid(value);
return -EINVAL;
}
char **v;
free(*v);
*v = value;
if (n_pushed)
(*n_pushed)++;
return 1;
}
}
return 0;
}
int parse_env_file(
const char *fname,
const char *newline, ...) {
int r, n_pushed = 0;
if (!newline)
return r < 0 ? r : n_pushed;
}
static int load_env_file_push(
void *userdata,
int *n_pushed) {
char ***m = userdata;
char *p;
int r;
if (!utf8_is_valid(key)) {
return -EINVAL;
}
return -EINVAL;
}
if (!p)
return -ENOMEM;
r = strv_consume(m, p);
if (r < 0)
return r;
if (n_pushed)
(*n_pushed)++;
return 0;
}
char **m = NULL;
int r;
if (!newline)
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static int load_env_file_push_pairs(
void *userdata,
int *n_pushed) {
char ***m = userdata;
int r;
if (!utf8_is_valid(key)) {
return -EINVAL;
}
return -EINVAL;
}
r = strv_extend(m, key);
if (r < 0)
return -ENOMEM;
if (!value) {
r = strv_extend(m, "");
if (r < 0)
return -ENOMEM;
} else {
if (r < 0)
return r;
}
if (n_pushed)
(*n_pushed)++;
return 0;
}
char **m = NULL;
int r;
if (!newline)
if (r < 0) {
strv_free(m);
return r;
}
*rl = m;
return 0;
}
static void write_env_var(FILE *f, const char *v) {
const char *p;
p = strchr(v, '=');
if (!p) {
/* Fallback */
fputs(v, f);
fputc('\n', f);
return;
}
p++;
fwrite(v, 1, p-v, f);
fputc('\"', f);
for (; *p; p++) {
if (strchr(SHELL_NEED_ESCAPE, *p))
fputc('\\', f);
fputc(*p, f);
}
fputc('\"', f);
} else
fputs(p, f);
fputc('\n', f);
}
int write_env_file(const char *fname, char **l) {
_cleanup_free_ char *p = NULL;
char **i;
int r;
r = fopen_temporary(fname, &f, &p);
if (r < 0)
return r;
STRV_FOREACH(i, l)
write_env_var(f, *i);
r = fflush_and_check(f);
if (r >= 0) {
return 0;
r = -errno;
}
unlink(p);
return r;
}
int r;
int len;
char *ans;
if (r < 0)
return r;
return 0;
if (len == 0)
return 0;
if (!ans)
return -ENOMEM;
*interpreter = ans;
return 1;
}
/**
* should not include whitespace or the delimiter (':'). pattern matches only
* the beginning of a line. Whitespace before ':' is skipped. Whitespace and
* zeros after the ':' will be skipped. field must be freed afterwards.
* terminator specifies the terminating characters of the field value (not
* included in the value).
*/
int get_proc_field(const char *filename, const char *pattern, const char *terminator, char **field) {
char *t, *f;
int r;
if (r < 0)
return r;
t = status;
do {
bool pattern_ok;
do {
if (!t)
return -ENOENT;
/* Check that pattern occurs in beginning of line. */
} while (!pattern_ok);
t += strspn(t, " \t");
if (!*t)
return -ENOENT;
} while (*t != ':');
t++;
if (*t) {
t += strspn(t, " \t");
/* Also skip zeros, because when this is used for
* capabilities, we don't want the zeros. This way the
* same capability set always maps to the same string,
* irrespective of the total capability set size. For
* other numbers it shouldn't matter. */
t += strspn(t, "0");
/* Back off one char if there's nothing but whitespace
and zeros */
if (!*t || isspace(*t))
t --;
}
if (!f)
return -ENOMEM;
*field = f;
return 0;
}