blob: dd2a784675182f95265ac754601d439e92448e9e [file] [log] [blame]
/* ----------------------------------------------------------------------- *
*
* parse_subs.c - misc parser subroutines
* automounter map
*
* Copyright 1997 Transmeta Corporation - All Rights Reserved
* Copyright 2000 Jeremy Fitzhardinge <jeremy@goop.org>
* Copyright 2004-2006 Ian Kent <raven@themaw.net>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
* USA; either version 2 of the License, or (at your option) any later
* version; incorporated herein by reference.
*
* ----------------------------------------------------------------------- */
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "automount.h"
struct types {
char *type;
unsigned int len;
};
static struct types map_type[] = {
{ "file", 4 },
{ "program", 7 },
{ "yp", 2 },
{ "nis", 3 },
{ "nisplus", 7 },
{ "ldap", 4 },
{ "ldaps", 5 },
{ "hesiod", 6 },
{ "userdir", 7 },
{ "hosts", 5 },
};
static unsigned int map_type_count = sizeof(map_type)/sizeof(struct types);
static struct types format_type[] = {
{ "sun", 3 },
{ "hesiod", 6 },
};
static unsigned int format_type_count = sizeof(format_type)/sizeof(struct types);
/*
* Skip whitespace in a string; if we hit a #, consider the rest of the
* entry a comment.
*/
const char *skipspace(const char *whence)
{
while (1) {
switch (*whence) {
case ' ':
case '\b':
case '\t':
case '\n':
case '\v':
case '\f':
case '\r':
whence++;
break;
case '#': /* comment: skip to end of string */
while (*whence != '\0')
whence++;
/* FALLTHROUGH */
default:
return whence;
}
}
}
/*
* Check a string to see if a colon appears before the next '/'.
*/
int check_colon(const char *str)
{
char *ptr = (char *) str;
/* Colon escape */
if (!strncmp(ptr, ":/", 2))
return 1;
while (*ptr && strncmp(ptr, ":/", 2))
ptr++;
if (!*ptr)
return 0;
return 1;
}
/* Get the length of a chunk delimitered by whitespace */
int chunklen(const char *whence, int expect_colon)
{
char *str = (char *) whence;
int n = 0;
int quote = 0;
for (; *str; str++, n++) {
switch (*str) {
case '\\':
if( quote ) {
break;
} else {
quote = 1;
continue;
}
case '"':
if (quote)
break;
while (*str) {
str++;
n++;
if (*str == '"')
break;
if (!strncmp(str, ":/", 2))
expect_colon = 0;
}
break;
case ':':
if (expect_colon && !strncmp(str, ":/", 2))
expect_colon = 0;
continue;
case ' ':
case '\t':
/* Skip space or tab if we expect a colon */
if (expect_colon)
continue;
case '\b':
case '\n':
case '\v':
case '\f':
case '\r':
case '\0':
if (!quote)
return n;
/* FALLTHROUGH */
default:
break;
}
quote = 0;
}
return n;
}
/*
* Compare str with pat. Return 0 if compare equal or
* str is an abbreviation of pat of no less than mchr characters.
*/
int strmcmp(const char *str, const char *pat, int mchr)
{
int nchr = 0;
while (*str == *pat) {
if (!*str)
return 0;
str++;
pat++;
nchr++;
}
if (!*str && nchr > mchr)
return 0;
return *pat - *str;
}
char *dequote(const char *str, int origlen, unsigned int logopt)
{
char *ret = malloc(origlen + 1);
char *cp = ret;
const char *scp;
int len = origlen;
int quote = 0, dquote = 0;
int i, j;
if (ret == NULL)
return NULL;
/* first thing to do is strip white space from the end */
i = len - 1;
while (isspace(str[i])) {
/* of course, we have to keep escaped white-space */
j = i - 1;
if (j > 0 && (str[j] == '\\' || str[j] == '"'))
break;
i--;
len--;
}
for (scp = str; len > 0 && *scp; scp++, len--) {
if (!quote) {
if (*scp == '"') {
if (dquote)
dquote = 0;
else
dquote = 1;
continue;
}
if (!dquote) {
if (*scp == '\\') {
quote = 1;
continue;
}
}
}
quote = 0;
*cp++ = *scp;
}
*cp = '\0';
if (dquote) {
debug(logopt, "unmatched quote in %.*s", origlen, str);
free(ret);
return NULL;
}
return ret;
}
int span_space(const char *str, unsigned int maxlen)
{
const char *p = str;
unsigned int len = 0;
while (*p && !isblank(*p) && len < maxlen) {
if (*p == '"') {
while (*p++ && len++ < maxlen) {
if (*p == '"')
break;
}
} else if (*p == '\\') {
p += 2;
len += 2;
continue;
}
p++;
len++;
}
return len;
}
char *sanitize_path(const char *path, int origlen, unsigned int type, unsigned int logopt)
{
char *slash, *cp, *s_path;
const char *scp;
int len = origlen;
unsigned int seen_slash = 0, quote = 0, dquote = 0;
if (type & (LKP_INDIRECT | LKP_DIRECT)) {
slash = strchr(path, '/');
if (slash) {
if (type == LKP_INDIRECT)
return NULL;
if (*path != '/')
return NULL;
} else {
if (type == LKP_DIRECT)
return NULL;
}
}
s_path = malloc(origlen + 1);
if (!s_path)
return NULL;
for (cp = s_path, scp = path; len > 0; scp++, len--) {
if (!quote) {
if (*scp == '"') {
if (dquote)
dquote = 0;
else
dquote = 1;
continue;
}
if (!dquote) {
/* Badness in string - go away */
if (*scp < 32) {
free(s_path);
return NULL;
}
if (*scp == '\\') {
quote = 1;
continue;
}
}
/*
* Not really proper but we get problems with
* paths with multiple slashes. The kernel
* compresses them so when we get a query there
* should be only single slashes.
*/
if (*scp == '/') {
if (seen_slash)
continue;
seen_slash = 1;
} else
seen_slash = 0;
}
quote = 0;
*cp++ = *scp;
}
*cp = '\0';
if (dquote) {
debug(logopt, "unmatched quote in %.*s", origlen, path);
free(s_path);
return NULL;
}
/* Remove trailing / but watch out for a quoted / alone */
if (strlen(cp) > 1 && origlen > 1 && *(cp - 1) == '/')
*(cp - 1) = '\0';
return s_path;
}
void free_map_type_info(struct map_type_info *info)
{
if (info->type)
free(info->type);
if (info->format)
free(info->format);
if (info->map)
free(info->map);
free(info);
return;
}
struct map_type_info *parse_map_type_info(const char *str)
{
struct map_type_info *info;
char *buf, *type, *fmt, *map, *tmp;
char *pos;
buf = strdup(str);
if (!buf)
return NULL;
info = malloc(sizeof(struct map_type_info));
if (!info) {
free(buf);
return NULL;
}
memset(info, 0, sizeof(struct map_type_info));
type = fmt = map = NULL;
tmp = strchr(buf, ':');
if (!tmp) {
pos = buf;
while (*pos == ' ')
*pos++ = '\0';
map = pos;
} else {
int i, j;
for (i = 0; i < map_type_count; i++) {
char *m_type = map_type[i].type;
unsigned int m_len = map_type[i].len;
pos = buf;
if (strncmp(m_type, pos, m_len))
continue;
type = pos;
pos += m_len;
if (*pos == ' ' || *pos == ':') {
while (*pos == ' ')
*pos++ = '\0';
if (*pos != ':') {
free(buf);
free(info);
return NULL;
} else {
*pos++ = '\0';
while (*pos && *pos == ' ')
*pos++ = '\0';
map = pos;
break;
}
}
if (*pos == ',') {
*pos++ = '\0';
for (j = 0; j < format_type_count; j++) {
char *f_type = format_type[j].type;
unsigned int f_len = format_type[j].len;
if (strncmp(f_type, pos, f_len))
continue;
fmt = pos;
pos += f_len;
if (*pos == ' ' || *pos == ':') {
while (*pos == ' ')
*pos++ = '\0';
if (*pos != ':') {
free(buf);
free(info);
return NULL;
} else {
*pos++ = '\0';
while (*pos && *pos == ' ')
*pos++ = '\0';
map = pos;
break;
}
}
}
}
}
if (!type) {
pos = buf;
while (*pos == ' ')
*pos++ = '\0';
map = pos;
}
}
/* Look for space terminator - ignore local options */
for (tmp = buf; *tmp; tmp++) {
if (*tmp == ' ') {
*tmp = '\0';
break;
}
if (*tmp == '\\')
tmp++;
}
if (type) {
info->type = strdup(type);
if (!info->type) {
free(buf);
free_map_type_info(info);
return NULL;
}
}
if (fmt) {
info->format = strdup(fmt);
if (!info->format) {
free(buf);
free_map_type_info(info);
return NULL;
}
}
if (map) {
info->map = strdup(map);
if (!info->map) {
free(buf);
free_map_type_info(info);
return NULL;
}
}
free(buf);
return info;
}