186d7f5d3SJohn Marino /*
286d7f5d3SJohn Marino * Copyright (c) 2009-2011 Alex Hornung <alex@alexhornung.com>.
386d7f5d3SJohn Marino * All rights reserved.
486d7f5d3SJohn Marino *
586d7f5d3SJohn Marino * Redistribution and use in source and binary forms, with or without
686d7f5d3SJohn Marino * modification, are permitted provided that the following conditions
786d7f5d3SJohn Marino * are met:
886d7f5d3SJohn Marino *
986d7f5d3SJohn Marino * 1. Redistributions of source code must retain the above copyright
1086d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer.
1186d7f5d3SJohn Marino * 2. Redistributions in binary form must reproduce the above copyright
1286d7f5d3SJohn Marino * notice, this list of conditions and the following disclaimer in
1386d7f5d3SJohn Marino * the documentation and/or other materials provided with the
1486d7f5d3SJohn Marino * distribution.
1586d7f5d3SJohn Marino *
1686d7f5d3SJohn Marino * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1786d7f5d3SJohn Marino * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1886d7f5d3SJohn Marino * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
1986d7f5d3SJohn Marino * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
2086d7f5d3SJohn Marino * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
2186d7f5d3SJohn Marino * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
2286d7f5d3SJohn Marino * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
2386d7f5d3SJohn Marino * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
2486d7f5d3SJohn Marino * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
2586d7f5d3SJohn Marino * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
2686d7f5d3SJohn Marino * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2786d7f5d3SJohn Marino * SUCH DAMAGE.
2886d7f5d3SJohn Marino */
2986d7f5d3SJohn Marino
3086d7f5d3SJohn Marino #include <stdio.h>
3186d7f5d3SJohn Marino #include <stdarg.h>
3286d7f5d3SJohn Marino #include <string.h>
3386d7f5d3SJohn Marino #include <stdlib.h>
3486d7f5d3SJohn Marino #include <unistd.h>
3586d7f5d3SJohn Marino #include <errno.h>
3686d7f5d3SJohn Marino #include <err.h>
3786d7f5d3SJohn Marino #include "parser.h"
3886d7f5d3SJohn Marino
3986d7f5d3SJohn Marino #define _iswhitespace(X) ((((X) == ' ') || ((X) == '\t'))?1:0)
4086d7f5d3SJohn Marino
4186d7f5d3SJohn Marino
4286d7f5d3SJohn Marino static int line_no = 1;
4386d7f5d3SJohn Marino
iswhitespace(char c)4486d7f5d3SJohn Marino static int iswhitespace(char c)
4586d7f5d3SJohn Marino {
4686d7f5d3SJohn Marino return _iswhitespace(c);
4786d7f5d3SJohn Marino }
4886d7f5d3SJohn Marino
iscomma(char c)4986d7f5d3SJohn Marino static int iscomma(char c)
5086d7f5d3SJohn Marino {
5186d7f5d3SJohn Marino return (c == ',');
5286d7f5d3SJohn Marino }
5386d7f5d3SJohn Marino
5486d7f5d3SJohn Marino void
syntax_error(const char * fmt,...)5586d7f5d3SJohn Marino syntax_error(const char *fmt, ...)
5686d7f5d3SJohn Marino {
5786d7f5d3SJohn Marino char buf[1024];
5886d7f5d3SJohn Marino va_list ap;
5986d7f5d3SJohn Marino
6086d7f5d3SJohn Marino va_start(ap, fmt);
6186d7f5d3SJohn Marino vsnprintf(buf, sizeof(buf), fmt, ap);
6286d7f5d3SJohn Marino va_end(ap);
6386d7f5d3SJohn Marino errx(1, "syntax error on line %d: %s\n", line_no, buf);
6486d7f5d3SJohn Marino }
6586d7f5d3SJohn Marino
6686d7f5d3SJohn Marino
6786d7f5d3SJohn Marino int
entry_check_num_args(char ** tokens,int num)6886d7f5d3SJohn Marino entry_check_num_args(char **tokens, int num)
6986d7f5d3SJohn Marino {
7086d7f5d3SJohn Marino int i;
7186d7f5d3SJohn Marino
7286d7f5d3SJohn Marino for (i = 0; tokens[i] != NULL; i++)
7386d7f5d3SJohn Marino ;
7486d7f5d3SJohn Marino
7586d7f5d3SJohn Marino if (i < num) {
7686d7f5d3SJohn Marino syntax_error("at least %d tokens were expected but only %d "
7786d7f5d3SJohn Marino "were found", num, i);
7886d7f5d3SJohn Marino return 1;
7986d7f5d3SJohn Marino }
8086d7f5d3SJohn Marino return 0;
8186d7f5d3SJohn Marino }
8286d7f5d3SJohn Marino
8386d7f5d3SJohn Marino static int
line_tokenize(char * buffer,int (* is_sep)(char),char comment_char,char ** tokens)8486d7f5d3SJohn Marino line_tokenize(char *buffer, int (*is_sep)(char), char comment_char, char **tokens)
8586d7f5d3SJohn Marino {
8686d7f5d3SJohn Marino int c, n, i;
8786d7f5d3SJohn Marino int quote = 0;
8886d7f5d3SJohn Marino
8986d7f5d3SJohn Marino i = strlen(buffer) + 1;
9086d7f5d3SJohn Marino c = 0;
9186d7f5d3SJohn Marino
9286d7f5d3SJohn Marino /* Skip leading white-space */
9386d7f5d3SJohn Marino while ((_iswhitespace(buffer[c])) && (c < i)) c++;
9486d7f5d3SJohn Marino
9586d7f5d3SJohn Marino /*
9686d7f5d3SJohn Marino * If this line effectively (after indentation) begins with the comment
9786d7f5d3SJohn Marino * character, we ignore the rest of the line.
9886d7f5d3SJohn Marino */
9986d7f5d3SJohn Marino if (buffer[c] == comment_char)
10086d7f5d3SJohn Marino return 0;
10186d7f5d3SJohn Marino
10286d7f5d3SJohn Marino tokens[0] = &buffer[c];
10386d7f5d3SJohn Marino for (n = 1; c < i; c++) {
10486d7f5d3SJohn Marino if (buffer[c] == '"') {
10586d7f5d3SJohn Marino quote = !quote;
10686d7f5d3SJohn Marino if (quote) {
10786d7f5d3SJohn Marino if ((c >= 1) && (&buffer[c] != tokens[n-1])) {
10886d7f5d3SJohn Marino #if 0
10986d7f5d3SJohn Marino syntax_error("stray opening quote not "
11086d7f5d3SJohn Marino "at beginning of token");
11186d7f5d3SJohn Marino /* NOTREACHED */
11286d7f5d3SJohn Marino #endif
11386d7f5d3SJohn Marino } else {
11486d7f5d3SJohn Marino tokens[n-1] = &buffer[c+1];
11586d7f5d3SJohn Marino }
11686d7f5d3SJohn Marino } else {
11786d7f5d3SJohn Marino if ((c < i-1) && (!is_sep(buffer[c+1]))) {
11886d7f5d3SJohn Marino #if 0
11986d7f5d3SJohn Marino syntax_error("stray closing quote not "
12086d7f5d3SJohn Marino "at end of token");
12186d7f5d3SJohn Marino /* NOTREACHED */
12286d7f5d3SJohn Marino #endif
12386d7f5d3SJohn Marino } else {
12486d7f5d3SJohn Marino buffer[c] = '\0';
12586d7f5d3SJohn Marino }
12686d7f5d3SJohn Marino }
12786d7f5d3SJohn Marino }
12886d7f5d3SJohn Marino
12986d7f5d3SJohn Marino if (quote) {
13086d7f5d3SJohn Marino continue;
13186d7f5d3SJohn Marino }
13286d7f5d3SJohn Marino
13386d7f5d3SJohn Marino if (is_sep(buffer[c])) {
13486d7f5d3SJohn Marino buffer[c++] = '\0';
13586d7f5d3SJohn Marino while ((_iswhitespace(buffer[c])) && (c < i)) c++;
13686d7f5d3SJohn Marino tokens[n++] = &buffer[c--];
13786d7f5d3SJohn Marino }
13886d7f5d3SJohn Marino }
13986d7f5d3SJohn Marino tokens[n] = NULL;
14086d7f5d3SJohn Marino
14186d7f5d3SJohn Marino if (quote) {
14286d7f5d3SJohn Marino tokens[0] = NULL;
14386d7f5d3SJohn Marino return 0;
14486d7f5d3SJohn Marino }
14586d7f5d3SJohn Marino
14686d7f5d3SJohn Marino return n;
14786d7f5d3SJohn Marino }
14886d7f5d3SJohn Marino
14986d7f5d3SJohn Marino static int
process_line(FILE * fd,parser_t parser,void * arg)15086d7f5d3SJohn Marino process_line(FILE* fd, parser_t parser, void *arg)
15186d7f5d3SJohn Marino {
15286d7f5d3SJohn Marino char buffer[4096];
15386d7f5d3SJohn Marino char *tokens[256];
15486d7f5d3SJohn Marino int c, n, i = 0;
15586d7f5d3SJohn Marino int ret = 0;
15686d7f5d3SJohn Marino
15786d7f5d3SJohn Marino while (((c = fgetc(fd)) != EOF) && (c != '\n')) {
15886d7f5d3SJohn Marino buffer[i++] = (char)c;
15986d7f5d3SJohn Marino if (i == (sizeof(buffer) -1))
16086d7f5d3SJohn Marino break;
16186d7f5d3SJohn Marino }
16286d7f5d3SJohn Marino buffer[i] = '\0';
16386d7f5d3SJohn Marino
16486d7f5d3SJohn Marino if (feof(fd) || ferror(fd))
16586d7f5d3SJohn Marino ret = 1;
16686d7f5d3SJohn Marino
16786d7f5d3SJohn Marino
16886d7f5d3SJohn Marino n = line_tokenize(buffer, &iswhitespace, '#', tokens);
16986d7f5d3SJohn Marino
17086d7f5d3SJohn Marino /*
17186d7f5d3SJohn Marino * If there are not enough arguments for any function or it is
17286d7f5d3SJohn Marino * a line full of whitespaces, we just return here. Or if a
17386d7f5d3SJohn Marino * quote wasn't closed.
17486d7f5d3SJohn Marino */
17586d7f5d3SJohn Marino if ((n < 1) || (tokens[0][0] == '\0'))
17686d7f5d3SJohn Marino return ret;
17786d7f5d3SJohn Marino
17886d7f5d3SJohn Marino parser(arg, tokens);
17986d7f5d3SJohn Marino
18086d7f5d3SJohn Marino return ret;
18186d7f5d3SJohn Marino }
18286d7f5d3SJohn Marino
18386d7f5d3SJohn Marino int
parse_options(char * str,char ** options)18486d7f5d3SJohn Marino parse_options(char *str, char **options)
18586d7f5d3SJohn Marino {
18686d7f5d3SJohn Marino int i;
18786d7f5d3SJohn Marino
18886d7f5d3SJohn Marino i = line_tokenize(str, &iscomma, '#', options);
18986d7f5d3SJohn Marino if (i == 0)
19086d7f5d3SJohn Marino syntax_error("Invalid expression in options token");
19186d7f5d3SJohn Marino /* NOTREACHED */
19286d7f5d3SJohn Marino
19386d7f5d3SJohn Marino return i;
19486d7f5d3SJohn Marino }
19586d7f5d3SJohn Marino
19686d7f5d3SJohn Marino int
process_file(const char * file,parser_t parser,void * arg,int * nlines)19786d7f5d3SJohn Marino process_file(const char *file, parser_t parser, void *arg, int *nlines)
19886d7f5d3SJohn Marino {
19986d7f5d3SJohn Marino FILE *fd;
20086d7f5d3SJohn Marino
20186d7f5d3SJohn Marino line_no = 0;
20286d7f5d3SJohn Marino
20386d7f5d3SJohn Marino fd = fopen(file, "r");
20486d7f5d3SJohn Marino if (fd == NULL)
20586d7f5d3SJohn Marino err(1, "fopen");
20686d7f5d3SJohn Marino /* NOTREACHED */
20786d7f5d3SJohn Marino
20886d7f5d3SJohn Marino while (process_line(fd, parser, arg) == 0)
20986d7f5d3SJohn Marino ++line_no;
21086d7f5d3SJohn Marino
21186d7f5d3SJohn Marino fclose(fd);
21286d7f5d3SJohn Marino
21386d7f5d3SJohn Marino if (nlines != NULL)
21486d7f5d3SJohn Marino *nlines = line_no;
21586d7f5d3SJohn Marino
21686d7f5d3SJohn Marino return 0;
21786d7f5d3SJohn Marino }
21886d7f5d3SJohn Marino
219