parse.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693
  1. /*
  2. * Copyright (c) 2010 Philippe Pepiot <phil@philpep.org>
  3. * Copyright (c) 2011 Martin Duquesnoy <xorg62@gmail.com>
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. #ifndef _GNU_SOURCE
  18. #define _GNU_SOURCE
  19. #endif
  20. #ifndef _BSD_SOURCE
  21. #define _BSD_SOURCE
  22. #endif
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <stdarg.h>
  27. #include <limits.h>
  28. #include <unistd.h>
  29. #include <fcntl.h>
  30. #include <libgen.h>
  31. #include <pwd.h>
  32. #include <sys/stat.h>
  33. #include <sys/param.h>
  34. #include <err.h>
  35. #include "parse.h"
  36. #include "util.h"
  37. extern char *__progname;
  38. enum keyword_t { SEC_START, SEC_END, INCLUDE, WORD, EQUAL, LIST_START, LIST_END, NONE };
  39. #ifdef DEBUG
  40. static const struct
  41. {
  42. const char *name;
  43. enum keyword_t type;
  44. } kw_t_name[] =
  45. {
  46. {"SEC_START", SEC_START},
  47. {"SEC_END", SEC_END},
  48. {"INCLUDE", INCLUDE},
  49. {"WORD", WORD},
  50. {"EQUAL", EQUAL},
  51. {"LIST_START", LIST_START},
  52. {"LIST_END", LIST_END},
  53. {"NONE", NONE},
  54. };
  55. #endif
  56. struct files
  57. {
  58. char *name;
  59. struct files *parent;
  60. };
  61. struct keyword
  62. {
  63. enum keyword_t type;
  64. /* if WORD */
  65. int line;
  66. struct files *file;
  67. char *name;
  68. struct keyword *next;
  69. };
  70. struct state
  71. {
  72. bool quote;
  73. bool comment;
  74. char quote_char;
  75. };
  76. /* TO REMOVE (use a identifier for config and fallback XDG in api functions) */
  77. TAILQ_HEAD(, conf_sec) config;
  78. static struct keyword *keywords = NULL;
  79. static struct keyword *
  80. push_keyword(struct keyword *tail, enum keyword_t type, char *buf, size_t *offset, struct files *file, int line)
  81. {
  82. struct keyword *kw;
  83. #ifdef DEBUG
  84. int i = 0;
  85. #endif
  86. if(type == WORD && *offset == 0)
  87. return tail;
  88. kw = xcalloc(1, sizeof(*kw));
  89. kw->type = type;
  90. kw->line = line;
  91. kw->file = file;
  92. kw->next = NULL;
  93. if(*offset)
  94. {
  95. buf[*offset] = '\0';
  96. if(!strcmp(buf, INCLUDE_CMD))
  97. kw->type = INCLUDE;
  98. else
  99. kw->name = strdup(buf);
  100. *offset = 0;
  101. }
  102. else
  103. kw->name = NULL;
  104. if(tail)
  105. tail->next = kw;
  106. #ifdef DEBUG
  107. for(i = 0; kw_t_name[i].type != NONE; ++i)
  108. if(kw_t_name[i].type == kw->type)
  109. warnx("%s %s %s:%d\n", kw_t_name[i].name,
  110. (kw->name) ? kw->name : "",
  111. kw->file->name, kw->line);
  112. #endif
  113. return kw;
  114. }
  115. static void
  116. syntax(struct keyword *kw, const char *fmt, ...)
  117. {
  118. va_list args;
  119. fprintf(stderr, "%s:", __progname);
  120. if(kw && kw->file && kw->file->name)
  121. fprintf(stderr, "%s:%d", kw->file->name, kw->line);
  122. if(kw && kw->name)
  123. fprintf(stderr, ", near '%s'", kw->name);
  124. fprintf(stderr, ": ");
  125. va_start(args, fmt);
  126. vfprintf(stderr, fmt, args);
  127. va_end(args);
  128. fprintf(stderr, "\n");
  129. }
  130. #define PUSH_KEYWORD(type) tail = push_keyword(tail, type, bufname, &j, file, line)
  131. static struct keyword *
  132. parse_keywords(const char *filename)
  133. {
  134. int fd;
  135. struct stat st;
  136. char *buf;
  137. struct keyword *head = NULL;
  138. struct keyword *tail = NULL;
  139. struct files *file;
  140. enum keyword_t type; /* keyword type to push */
  141. struct state s = { false, false, '\0'};
  142. char *bufname;
  143. char path[PATH_MAX];
  144. size_t i, j;
  145. int line;
  146. bool error = false;
  147. if((fd = open(filename, O_RDONLY)) == -1 || stat(filename, &st) == -1)
  148. {
  149. warn("%s", filename);
  150. return NULL;
  151. }
  152. if(!st.st_size)
  153. {
  154. warnx("%s: empty file", filename);
  155. close(fd);
  156. return NULL;
  157. }
  158. if(!realpath(filename, path))
  159. {
  160. warn("%s", filename);
  161. close(fd);
  162. return NULL;
  163. }
  164. buf = xmalloc(1, st.st_size + 1);
  165. if(read(fd, buf, st.st_size) == -1)
  166. {
  167. warn("%s", filename);
  168. free(buf);
  169. close(fd);
  170. return NULL;
  171. }
  172. buf[st.st_size] = '\0';
  173. file = xcalloc(1, sizeof(*file));
  174. bufname = xcalloc(BUFSIZ, sizeof(*bufname));
  175. file->name = strdup(path);
  176. file->parent = NULL;
  177. for(i = j = 0, line = 1; i < (size_t)st.st_size; ++i)
  178. {
  179. if(!head && tail)
  180. head = tail;
  181. if(buf[i] == '\n' && s.comment)
  182. {
  183. line++;
  184. s.comment = false;
  185. continue;
  186. }
  187. if(buf[i] == '#' && !s.quote)
  188. {
  189. s.comment = true;
  190. continue;
  191. }
  192. if(s.comment)
  193. continue;
  194. /* end of quotted string */
  195. if(s.quote && buf[i] == s.quote_char)
  196. {
  197. PUSH_KEYWORD(WORD);
  198. s.quote = false;
  199. continue;
  200. }
  201. if(!s.quote)
  202. {
  203. if((buf[i] == '"' || buf[i] == '\''))
  204. {
  205. PUSH_KEYWORD(WORD);
  206. /* begin quotted string */
  207. s.quote_char = buf[i];
  208. s.quote = true;
  209. continue;
  210. }
  211. if(buf[i] == '[')
  212. {
  213. PUSH_KEYWORD(WORD);
  214. if(buf[i + 1] == '/')
  215. {
  216. i += 2;
  217. type = SEC_END;
  218. }
  219. else
  220. {
  221. ++i;
  222. type = SEC_START;
  223. }
  224. /* get section name */
  225. while(buf[i] != ']')
  226. {
  227. if(i >= ((size_t)st.st_size-1) || j >= (BUFSIZ-1))
  228. {
  229. bufname[j] = '\0';
  230. syntax(NULL, "word too long in %s:%d near '%s'",
  231. file->name, line, bufname);
  232. error = true;
  233. break;
  234. }
  235. bufname[j++] = buf[i++];
  236. }
  237. PUSH_KEYWORD(type);
  238. continue;
  239. }
  240. if(buf[i] == '{')
  241. {
  242. PUSH_KEYWORD(WORD);
  243. PUSH_KEYWORD(LIST_START);
  244. continue;
  245. }
  246. if(buf[i] == '}')
  247. {
  248. PUSH_KEYWORD(WORD);
  249. PUSH_KEYWORD(LIST_END);
  250. continue;
  251. }
  252. if(buf[i] == ',')
  253. {
  254. PUSH_KEYWORD(WORD);
  255. continue;
  256. }
  257. if(buf[i] == '=')
  258. {
  259. PUSH_KEYWORD(WORD);
  260. PUSH_KEYWORD(EQUAL);
  261. continue;
  262. }
  263. if(strchr("\t\n ", buf[i]))
  264. {
  265. PUSH_KEYWORD(WORD);
  266. if(buf[i] == '\n')
  267. ++line;
  268. continue;
  269. }
  270. }
  271. if(j >= (BUFSIZ - 1))
  272. {
  273. bufname[j] = '\0';
  274. syntax(NULL, "word too long in %s:%d near '%s'",
  275. file->name, line, bufname);
  276. error = true;
  277. break;
  278. }
  279. bufname[j++] = buf[i];
  280. }
  281. free(buf);
  282. free(bufname);
  283. close(fd);
  284. warnx("%s read", file->name);
  285. return (error ? NULL: head);
  286. }
  287. /*
  288. * return NULL on failure and head->next if
  289. * no config found (of file doesn't exist)
  290. * NOTE to devs: head->name is the file to include
  291. */
  292. static struct keyword *
  293. include(struct keyword *head)
  294. {
  295. struct keyword *kw;
  296. struct keyword *tail;
  297. struct files *file;
  298. struct passwd *user;
  299. char *filename = NULL;
  300. char *base = NULL;
  301. head = head->next;
  302. if(!head || head->type != WORD)
  303. {
  304. syntax(head, "missing filename to include");
  305. return NULL;
  306. }
  307. /* replace ~ by user directory */
  308. if(head->name && head->name[0] == '~')
  309. {
  310. if((user = getpwuid(getuid())) && user->pw_dir)
  311. xasprintf(&filename, "%s%s", user->pw_dir, head->name + 1);
  312. else if(getenv("HOME"))
  313. xasprintf(&filename, "%s%s", getenv("HOME"), head->name + 1);
  314. else /* to warning ? */
  315. filename = head->name;
  316. }
  317. /* relative path from parent file */
  318. else if(head->name && head->name[0] != '/')
  319. {
  320. base = strdup(head->file->name);
  321. xasprintf(&filename, "%s/%s", dirname(base), head->name);
  322. free(base);
  323. }
  324. else
  325. filename = head->name;
  326. if(!(kw = parse_keywords(filename)))
  327. {
  328. warnx("no config found in include file %s", head->name);
  329. if(filename != head->name)
  330. free(filename);
  331. return NULL;
  332. }
  333. kw->file->parent = head->file;
  334. /* detect circular include */
  335. for(file = kw->file->parent; file != NULL; file = file->parent)
  336. if(!strcmp(file->name, kw->file->name))
  337. {
  338. syntax(kw, "circular include of %s", kw->file->name);
  339. if(filename != head->name)
  340. free(filename);
  341. return NULL;
  342. }
  343. if(filename != head->name)
  344. free(filename);
  345. head = head->next;
  346. if(kw)
  347. {
  348. for(tail = kw; tail->next; tail = tail->next);
  349. tail->next = head;
  350. }
  351. return kw;
  352. }
  353. static void *
  354. free_opt(struct conf_opt *o)
  355. {
  356. free(o);
  357. return NULL;
  358. }
  359. static struct conf_opt *
  360. get_option(struct keyword **head)
  361. {
  362. struct conf_opt *o;
  363. size_t j = 0;
  364. struct keyword *kw = *head;
  365. o = xcalloc(1, sizeof(*o));
  366. o->name = kw->name;
  367. o->used = false;
  368. o->line = kw->line;
  369. o->filename = kw->file->name;
  370. kw = kw->next;
  371. if(kw->type != EQUAL)
  372. {
  373. syntax(kw, "missing '=' here");
  374. return free_opt(o);
  375. }
  376. if(!(kw = kw->next))
  377. {
  378. syntax(kw, "missing value");
  379. return free_opt(o);
  380. }
  381. switch(kw->type)
  382. {
  383. case INCLUDE:
  384. if(!(kw = include(kw)))
  385. return free_opt(o);
  386. break;
  387. case WORD:
  388. o->val[0] = kw->name;
  389. o->val[1] = NULL;
  390. kw = kw->next;
  391. break;
  392. case LIST_START:
  393. kw = kw->next;
  394. while(kw && kw->type != LIST_END)
  395. {
  396. switch(kw->type)
  397. {
  398. case WORD:
  399. if(j >= (PARSE_MAX_LIST - 1))
  400. {
  401. syntax(kw, "too much values in list");
  402. return free_opt(o);
  403. }
  404. o->val[j++] = kw->name;
  405. kw = kw->next;
  406. break;
  407. case INCLUDE:
  408. if(!(kw = include(kw)))
  409. return free_opt(o);
  410. break;
  411. default:
  412. syntax(kw, "declaration into a list");
  413. return free_opt(o);
  414. break;
  415. }
  416. }
  417. if(!kw)
  418. {
  419. syntax(kw, "list unclosed");
  420. return free_opt(o);
  421. }
  422. kw = kw->next;
  423. break;
  424. default:
  425. syntax(kw, "missing value");
  426. return free_opt(o);
  427. break;
  428. }
  429. *head = kw;
  430. return o;
  431. }
  432. static void *
  433. free_sec(struct conf_sec *sec)
  434. {
  435. struct conf_opt *o;
  436. struct conf_sec *s;
  437. if(sec)
  438. {
  439. while(!SLIST_EMPTY(&sec->optlist))
  440. {
  441. o = SLIST_FIRST(&sec->optlist);
  442. SLIST_REMOVE_HEAD(&sec->optlist, entry);
  443. free_opt(o);
  444. }
  445. while(!TAILQ_EMPTY(&sec->sub))
  446. {
  447. s = TAILQ_FIRST(&sec->sub);
  448. TAILQ_REMOVE(&sec->sub, s, entry);
  449. free_sec(s);
  450. }
  451. free(sec);
  452. }
  453. return NULL;
  454. }
  455. static struct conf_sec *
  456. get_section(struct keyword **head)
  457. {
  458. struct conf_sec *s;
  459. struct conf_opt *o;
  460. struct conf_sec *sub;
  461. struct keyword *kw = *head;
  462. s = xcalloc(1, sizeof(*s));
  463. s->name = kw->name;
  464. TAILQ_INIT(&s->sub);
  465. SLIST_INIT(&s->optlist);
  466. kw = kw->next;
  467. while(kw && kw->type != SEC_END)
  468. {
  469. switch(kw->type)
  470. {
  471. case INCLUDE:
  472. if(!(kw = include(kw)))
  473. return free_sec(s);
  474. break;
  475. case SEC_START:
  476. if(!(sub = get_section(&kw)))
  477. return free_sec(s);
  478. TAILQ_INSERT_TAIL(&s->sub, sub, entry);
  479. ++s->nsub;
  480. break;
  481. case WORD:
  482. if(!(o = get_option(&kw)))
  483. return free_sec(s);
  484. SLIST_INSERT_HEAD(&s->optlist, o, entry);
  485. ++s->nopt;
  486. break;
  487. default:
  488. syntax(kw, "syntax error");
  489. return free_sec(s);
  490. break;
  491. }
  492. }
  493. if(!kw || strcmp(kw->name, s->name))
  494. {
  495. syntax(kw, "missing end section %s", s->name);
  496. return free_sec(s);
  497. }
  498. kw = kw->next;
  499. *head = kw;
  500. return s;
  501. }
  502. int
  503. free_conf(void)
  504. {
  505. struct conf_sec *s;
  506. struct keyword *kw, *nkw;
  507. struct files **f = NULL;
  508. int i, nf = 0;
  509. while(!TAILQ_EMPTY(&config))
  510. {
  511. s = TAILQ_FIRST(&config);
  512. TAILQ_REMOVE(&config, s, entry);
  513. free_sec(s);
  514. }
  515. kw = keywords;
  516. while(kw)
  517. {
  518. nkw = kw->next;
  519. free(kw->name);
  520. for(i = 0; i < nf; ++i)
  521. if(f[i] == kw->file)
  522. {
  523. if(!(f = realloc(f, sizeof(*f) * (++i))))
  524. err(EXIT_FAILURE, "realloc");
  525. f[i - 1] = kw->file;
  526. }
  527. kw = nkw;
  528. }
  529. if(nf > 0)
  530. {
  531. for(i = 0; i < nf; ++i)
  532. {
  533. free(f[i]->name);
  534. free(f[i]);
  535. }
  536. free(f);
  537. }
  538. return -1;
  539. }
  540. int
  541. get_conf(const char *filename)
  542. {
  543. struct conf_sec *s;
  544. struct keyword *head, *kw;
  545. head = kw = parse_keywords(filename);
  546. if(!head)
  547. return -1; /* TODO ERREUR */
  548. keywords = head;
  549. TAILQ_INIT(&config);
  550. while(kw)
  551. {
  552. switch(kw->type)
  553. {
  554. case INCLUDE:
  555. if(!(kw = include(kw)))
  556. return free_conf();
  557. break;
  558. case SEC_START:
  559. if(!(s = get_section(&kw)))
  560. return free_conf();
  561. TAILQ_INSERT_TAIL(&config, s, entry);
  562. break;
  563. default:
  564. syntax(kw, "out of any section");
  565. return free_conf();
  566. break;
  567. }
  568. }
  569. return 0;
  570. }