/*
* Copyright (c) 2008,2009 by Dmitry V. Levin. See LICENSE.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include "passwdqc.h"
#include "concat.h"
static char *mkreason(const char *what, const char *pathname,
unsigned int lineno, const char *why)
{
char buf[sizeof(unsigned int) * 3 + 1];
const char *at_line = (lineno ? " at line " : "");
const char *at_num = (lineno ? buf : "");
if (lineno)
sprintf(buf, "%u", lineno);
return concat(what, " \"", pathname, "\"", at_line, at_num, ": ",
(why ? why : strerror(errno)), NULL);
}
static char *
skip_whitespaces(char *str)
{
char *p;
for (p = str; *p == ' ' || *p == '\t' || *p == '\r' || *p == '\n'; ++p)
;
return p;
}
static char *
skip_nonwhitespaces(char *str)
{
char *p;
for (p = str;
*p && *p != ' ' && *p != '\t' && *p != '\r' && *p != '\n'; ++p)
;
return p;
}
static int
parse_file(FILE *fp, passwdqc_params_t *params, char **reason,
const char *pathname)
{
unsigned int lineno;
char buf[8192];
for (lineno = 1; fgets(buf, sizeof(buf), fp); ++lineno) {
char *str, *end, *rt;
const char *cstr;
int rc;
if (strlen(buf) >= sizeof(buf) - 1) {
*reason = mkreason("Error reading", pathname,
lineno, "Line too long");
return -1;
}
str = skip_whitespaces(buf);
if (!*str || *str == '#')
continue;
end = skip_nonwhitespaces(str);
if (*skip_whitespaces(end)) {
*reason = mkreason("Error loading", pathname,
lineno, "Unexpected token");
return -1;
}
*end = '\0';
cstr = str;
if ((rc = passwdqc_params_parse(params, &rt, 1, &cstr))) {
*reason = mkreason("Error loading", pathname,
lineno, (rt ? rt : "Out of memory"));
free(rt);
return rc;
}
}
if (!feof(fp) || ferror(fp)) {
*reason = mkreason("Error reading", pathname, 0, NULL);
return -1;
}
return 0;
}
struct dev_ino_t;
struct dev_ino_t {
struct dev_ino_t *next;
dev_t dev;
ino_t ino;
};
static struct dev_ino_t *dev_ino_head;
int
passwdqc_params_load(passwdqc_params_t *params, char **reason,
const char *pathname)
{
int rc;
FILE *fp;
struct dev_ino_t di, *di_p;
struct stat st;
if (!(fp = fopen(pathname, "r"))) {
*reason = mkreason("Error opening", pathname, 0, NULL);
return -1;
}
if (fstat(fileno(fp), &st)) {
*reason = mkreason("Error stat", pathname, 0, NULL);
fclose(fp);
return -1;
}
di.dev = st.st_dev;
di.ino = st.st_ino;
for (di_p = dev_ino_head; di_p; di_p = di_p->next)
if (di_p->dev == di.dev && di_p->ino == di.ino)
break;
if (di_p) {
*reason = mkreason("Error opening", pathname, 0,
"Loop detected");
fclose(fp);
return -1;
}
di.next = dev_ino_head;
dev_ino_head = &di;
rc = parse_file(fp, params, reason, pathname);
fclose(fp);
dev_ino_head = dev_ino_head->next;
return rc;
}