1 /* 2 * Copyright (c) 2009-2011 Alex Hornung <alex@alexhornung.com>. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in 13 * the documentation and/or other materials provided with the 14 * distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 18 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 19 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 20 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 21 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, 22 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 23 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 24 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 25 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 26 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 * SUCH DAMAGE. 28 */ 29 30 #include <stdio.h> 31 #include <stdarg.h> 32 #include <string.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <errno.h> 36 #include <err.h> 37 #include "parser.h" 38 39 #define _iswhitespace(X) ((((X) == ' ') || ((X) == '\t'))?1:0) 40 41 42 static int line_no = 1; 43 44 static int iswhitespace(char c) 45 { 46 return _iswhitespace(c); 47 } 48 49 static int iscomma(char c) 50 { 51 return (c == ','); 52 } 53 54 void 55 syntax_error(const char *fmt, ...) 56 { 57 char buf[1024]; 58 va_list ap; 59 60 va_start(ap, fmt); 61 vsnprintf(buf, sizeof(buf), fmt, ap); 62 va_end(ap); 63 errx(1, "syntax error on line %d: %s\n", line_no, buf); 64 } 65 66 67 int 68 entry_check_num_args(char **tokens, int num) 69 { 70 int i; 71 72 for (i = 0; tokens[i] != NULL; i++) 73 ; 74 75 if (i < num) { 76 syntax_error("at least %d tokens were expected but only %d " 77 "were found", num, i); 78 return 1; 79 } 80 return 0; 81 } 82 83 static int 84 line_tokenize(char *buffer, int (*is_sep)(char), char comment_char, char **tokens) 85 { 86 int c, n, i; 87 int quote = 0; 88 89 i = strlen(buffer) + 1; 90 c = 0; 91 92 /* Skip leading white-space */ 93 while ((_iswhitespace(buffer[c])) && (c < i)) c++; 94 95 /* 96 * If this line effectively (after indentation) begins with the comment 97 * character, we ignore the rest of the line. 98 */ 99 if (buffer[c] == comment_char) 100 return 0; 101 102 tokens[0] = &buffer[c]; 103 for (n = 1; c < i; c++) { 104 if (buffer[c] == '"') { 105 quote = !quote; 106 if (quote) { 107 if ((c >= 1) && (&buffer[c] != tokens[n-1])) { 108 #if 0 109 syntax_error("stray opening quote not " 110 "at beginning of token"); 111 /* NOTREACHED */ 112 #endif 113 } else { 114 tokens[n-1] = &buffer[c+1]; 115 } 116 } else { 117 if ((c < i-1) && (!is_sep(buffer[c+1]))) { 118 #if 0 119 syntax_error("stray closing quote not " 120 "at end of token"); 121 /* NOTREACHED */ 122 #endif 123 } else { 124 buffer[c] = '\0'; 125 } 126 } 127 } 128 129 if (quote) { 130 continue; 131 } 132 133 if (is_sep(buffer[c])) { 134 buffer[c++] = '\0'; 135 while ((_iswhitespace(buffer[c])) && (c < i)) c++; 136 tokens[n++] = &buffer[c--]; 137 } 138 } 139 tokens[n] = NULL; 140 141 if (quote) { 142 tokens[0] = NULL; 143 return 0; 144 } 145 146 return n; 147 } 148 149 static int 150 process_line(FILE* fd, parser_t parser, void *arg) 151 { 152 char buffer[4096]; 153 char *tokens[256]; 154 int c, n, i = 0; 155 int ret = 0; 156 157 while (((c = fgetc(fd)) != EOF) && (c != '\n')) { 158 buffer[i++] = (char)c; 159 if (i == (sizeof(buffer) -1)) 160 break; 161 } 162 buffer[i] = '\0'; 163 164 if (feof(fd) || ferror(fd)) 165 ret = 1; 166 167 168 n = line_tokenize(buffer, &iswhitespace, '#', tokens); 169 170 /* 171 * If there are not enough arguments for any function or it is 172 * a line full of whitespaces, we just return here. Or if a 173 * quote wasn't closed. 174 */ 175 if ((n < 1) || (tokens[0][0] == '\0')) 176 return ret; 177 178 parser(arg, tokens); 179 180 return ret; 181 } 182 183 int 184 parse_options(char *str, char **options) 185 { 186 int i; 187 188 i = line_tokenize(str, &iscomma, '#', options); 189 if (i == 0) 190 syntax_error("Invalid expression in options token"); 191 /* NOTREACHED */ 192 193 return i; 194 } 195 196 int 197 process_file(const char *file, parser_t parser, void *arg, int *nlines) 198 { 199 FILE *fd; 200 201 line_no = 0; 202 203 fd = fopen(file, "r"); 204 if (fd == NULL) 205 err(1, "fopen"); 206 /* NOTREACHED */ 207 208 while (process_line(fd, parser, arg) == 0) 209 ++line_no; 210 211 fclose(fd); 212 213 if (nlines != NULL) 214 *nlines = line_no; 215 216 return 0; 217 } 218 219