PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/ntp-4.2.6p5/ntpd/ntp_scanner.c

#
C | 659 lines | 501 code | 74 blank | 84 comment | 79 complexity | 7180c0fa9ff1ad97229265a50dcabc18 MD5 | raw file
Possible License(s): GPL-2.0, GPL-3.0, LGPL-3.0
  1. /* ntp_scanner.c
  2. *
  3. * The source code for a simple lexical analyzer.
  4. *
  5. * Written By: Sachin Kamboj
  6. * University of Delaware
  7. * Newark, DE 19711
  8. * Copyright (c) 2006
  9. */
  10. #ifdef HAVE_CONFIG_H
  11. # include <config.h>
  12. #endif
  13. #include <stdio.h>
  14. #include <ctype.h>
  15. #include <stdlib.h>
  16. #include <errno.h>
  17. #include <string.h>
  18. #include "ntp_config.h"
  19. #include "ntpsim.h"
  20. #include "ntp_scanner.h"
  21. #include "ntp_parser.h"
  22. #include "ntp_debug.h"
  23. /* ntp_keyword.h declares finite state machine and token text */
  24. #include "ntp_keyword.h"
  25. /* SCANNER GLOBAL VARIABLES
  26. * ------------------------
  27. */
  28. #define MAX_LEXEME (1024 + 1) /* The maximum size of a lexeme */
  29. char yytext[MAX_LEXEME]; /* Buffer for storing the input text/lexeme */
  30. extern int input_from_file;
  31. /* CONSTANTS
  32. * ---------
  33. */
  34. /* SCANNER GLOBAL VARIABLES
  35. * ------------------------
  36. */
  37. const char special_chars[] = "{}(),;|=";
  38. /* FUNCTIONS
  39. * ---------
  40. */
  41. int get_next_char(void);
  42. static int is_keyword(char *lexeme, follby *pfollowedby);
  43. /*
  44. * keyword() - Return the keyword associated with token T_ identifier.
  45. * See also token_name() for the string-ized T_ identifier.
  46. * Example: keyword(T_Server) returns "server"
  47. * token_name(T_Server) returns "T_Server"
  48. */
  49. const char *
  50. keyword(
  51. int token
  52. )
  53. {
  54. int i;
  55. const char *text;
  56. i = token - LOWEST_KEYWORD_ID;
  57. if (i >= 0 && i < COUNTOF(keyword_text))
  58. text = keyword_text[i];
  59. else
  60. text = NULL;
  61. return (text != NULL)
  62. ? text
  63. : "(keyword not found)";
  64. }
  65. /* FILE INTERFACE
  66. * --------------
  67. * We define a couple of wrapper functions around the standard C fgetc
  68. * and ungetc functions in order to include positional bookkeeping
  69. */
  70. struct FILE_INFO *
  71. F_OPEN(
  72. const char *path,
  73. const char *mode
  74. )
  75. {
  76. struct FILE_INFO *my_info;
  77. my_info = emalloc(sizeof *my_info);
  78. my_info->line_no = 1;
  79. my_info->col_no = 0;
  80. my_info->prev_line_col_no = 0;
  81. my_info->prev_token_col_no = 0;
  82. my_info->fname = path;
  83. my_info->fd = fopen(path, mode);
  84. if (NULL == my_info->fd) {
  85. free(my_info);
  86. return NULL;
  87. }
  88. return my_info;
  89. }
  90. int
  91. FGETC(
  92. struct FILE_INFO *stream
  93. )
  94. {
  95. int ch = fgetc(stream->fd);
  96. ++stream->col_no;
  97. if (ch == '\n') {
  98. stream->prev_line_col_no = stream->col_no;
  99. ++stream->line_no;
  100. stream->col_no = 1;
  101. }
  102. return ch;
  103. }
  104. /* BUGS: 1. Function will fail on more than one line of pushback
  105. * 2. No error checking is done to see if ungetc fails
  106. * SK: I don't think its worth fixing these bugs for our purposes ;-)
  107. */
  108. int
  109. UNGETC(
  110. int ch,
  111. struct FILE_INFO *stream
  112. )
  113. {
  114. if (ch == '\n') {
  115. stream->col_no = stream->prev_line_col_no;
  116. stream->prev_line_col_no = -1;
  117. --stream->line_no;
  118. }
  119. --stream->col_no;
  120. return ungetc(ch, stream->fd);
  121. }
  122. int
  123. FCLOSE(
  124. struct FILE_INFO *stream
  125. )
  126. {
  127. int ret_val = fclose(stream->fd);
  128. if (!ret_val)
  129. free(stream);
  130. return ret_val;
  131. }
  132. /* STREAM INTERFACE
  133. * ----------------
  134. * Provide a wrapper for the stream functions so that the
  135. * stream can either read from a file or from a character
  136. * array.
  137. * NOTE: This is not very efficient for reading from character
  138. * arrays, but needed to allow remote configuration where the
  139. * configuration command is provided through ntpq.
  140. *
  141. * The behavior of there two functions is determined by the
  142. * input_from_file flag.
  143. */
  144. int
  145. get_next_char(
  146. void
  147. )
  148. {
  149. char ch;
  150. if (input_from_file)
  151. return FGETC(ip_file);
  152. else {
  153. if (remote_config.buffer[remote_config.pos] == '\0')
  154. return EOF;
  155. else {
  156. ip_file->col_no++;
  157. ch = remote_config.buffer[remote_config.pos++];
  158. if (ch == '\n') {
  159. ip_file->prev_line_col_no = ip_file->col_no;
  160. ++ip_file->line_no;
  161. ip_file->col_no = 1;
  162. }
  163. return ch;
  164. }
  165. }
  166. }
  167. void
  168. push_back_char(
  169. int ch
  170. )
  171. {
  172. if (input_from_file)
  173. UNGETC(ch, ip_file);
  174. else {
  175. if (ch == '\n') {
  176. ip_file->col_no = ip_file->prev_line_col_no;
  177. ip_file->prev_line_col_no = -1;
  178. --ip_file->line_no;
  179. }
  180. --ip_file->col_no;
  181. remote_config.pos--;
  182. }
  183. }
  184. /* STATE MACHINES
  185. * --------------
  186. */
  187. /* Keywords */
  188. static int
  189. is_keyword(
  190. char *lexeme,
  191. follby *pfollowedby
  192. )
  193. {
  194. follby fb;
  195. int curr_s; /* current state index */
  196. int token;
  197. int i;
  198. curr_s = SCANNER_INIT_S;
  199. token = 0;
  200. for (i = 0; lexeme[i]; i++) {
  201. while (curr_s && (lexeme[i] != SS_CH(sst[curr_s])))
  202. curr_s = SS_OTHER_N(sst[curr_s]);
  203. if (curr_s && (lexeme[i] == SS_CH(sst[curr_s]))) {
  204. if ('\0' == lexeme[i + 1]
  205. && FOLLBY_NON_ACCEPTING
  206. != SS_FB(sst[curr_s])) {
  207. fb = SS_FB(sst[curr_s]);
  208. *pfollowedby = fb;
  209. token = curr_s;
  210. break;
  211. }
  212. curr_s = SS_MATCH_N(sst[curr_s]);
  213. } else
  214. break;
  215. }
  216. return token;
  217. }
  218. /* Integer */
  219. static int
  220. is_integer(
  221. char *lexeme
  222. )
  223. {
  224. int i = 0;
  225. /* Allow a leading minus sign */
  226. if (lexeme[i] == '-')
  227. ++i;
  228. /* Check that all the remaining characters are digits */
  229. for (; lexeme[i]; ++i) {
  230. if (!isdigit(lexeme[i]))
  231. return 0;
  232. }
  233. return 1;
  234. }
  235. /* Double */
  236. static int
  237. is_double(
  238. char *lexeme
  239. )
  240. {
  241. u_int num_digits = 0; /* Number of digits read */
  242. u_int i;
  243. i = 0;
  244. /* Check for an optional '+' or '-' */
  245. if ('+' == lexeme[i] || '-' == lexeme[i])
  246. i++;
  247. /* Read the integer part */
  248. for (; lexeme[i] && isdigit(lexeme[i]); i++)
  249. num_digits++;
  250. /* Check for the required decimal point */
  251. if ('.' == lexeme[i])
  252. i++;
  253. else
  254. return 0;
  255. /* Check for any digits after the decimal point */
  256. for (; lexeme[i] && isdigit(lexeme[i]); i++)
  257. num_digits++;
  258. /*
  259. * The number of digits in both the decimal part and the
  260. * fraction part must not be zero at this point
  261. */
  262. if (!num_digits)
  263. return 0;
  264. /* Check if we are done */
  265. if (!lexeme[i])
  266. return 1;
  267. /* There is still more input, read the exponent */
  268. if ('e' == tolower(lexeme[i]))
  269. i++;
  270. else
  271. return 0;
  272. /* Read an optional Sign */
  273. if ('+' == lexeme[i] || '-' == lexeme[i])
  274. i++;
  275. /* Now read the exponent part */
  276. while (lexeme[i] && isdigit(lexeme[i]))
  277. i++;
  278. /* Check if we are done */
  279. if (!lexeme[i])
  280. return 1;
  281. else
  282. return 0;
  283. }
  284. /* is_special() - Test whether a character is a token */
  285. static inline int
  286. is_special(
  287. int ch
  288. )
  289. {
  290. return (int)strchr(special_chars, ch);
  291. }
  292. static int
  293. is_EOC(
  294. int ch
  295. )
  296. {
  297. if ((old_config_style && (ch == '\n')) ||
  298. (!old_config_style && (ch == ';')))
  299. return 1;
  300. return 0;
  301. }
  302. char *
  303. quote_if_needed(char *str)
  304. {
  305. char *ret;
  306. size_t len;
  307. size_t octets;
  308. len = strlen(str);
  309. octets = len + 2 + 1;
  310. ret = emalloc(octets);
  311. if ('"' != str[0]
  312. && (strcspn(str, special_chars) < len
  313. || strchr(str, ' ') != NULL)) {
  314. snprintf(ret, octets, "\"%s\"", str);
  315. } else
  316. strncpy(ret, str, octets);
  317. return ret;
  318. }
  319. static int
  320. create_string_token(
  321. char *lexeme
  322. )
  323. {
  324. char *pch;
  325. /*
  326. * ignore end of line whitespace
  327. */
  328. pch = lexeme;
  329. while (*pch && isspace(*pch))
  330. pch++;
  331. if (!*pch) {
  332. yylval.Integer = T_EOC;
  333. return yylval.Integer;
  334. }
  335. yylval.String = estrdup(lexeme);
  336. return T_String;
  337. }
  338. /*
  339. * yylex() - function that does the actual scanning.
  340. * Bison expects this function to be called yylex and for it to take no
  341. * input and return an int.
  342. * Conceptually yylex "returns" yylval as well as the actual return
  343. * value representing the token or type.
  344. */
  345. int
  346. yylex(
  347. void
  348. )
  349. {
  350. int i, instring = 0;
  351. int yylval_was_set = 0;
  352. int token; /* The return value/the recognized token */
  353. int ch;
  354. static follby followedby = FOLLBY_TOKEN;
  355. do {
  356. /* Ignore whitespace at the beginning */
  357. while (EOF != (ch = get_next_char()) &&
  358. isspace(ch) &&
  359. !is_EOC(ch))
  360. ; /* Null Statement */
  361. if (EOF == ch) {
  362. if (!input_from_file || !curr_include_level)
  363. return 0;
  364. FCLOSE(fp[curr_include_level]);
  365. ip_file = fp[--curr_include_level];
  366. token = T_EOC;
  367. goto normal_return;
  368. } else if (is_EOC(ch)) {
  369. /* end FOLLBY_STRINGS_TO_EOC effect */
  370. followedby = FOLLBY_TOKEN;
  371. token = T_EOC;
  372. goto normal_return;
  373. } else if (is_special(ch) && FOLLBY_TOKEN == followedby) {
  374. /* special chars are their own token values */
  375. token = ch;
  376. /*
  377. * '=' implies a single string following as in:
  378. * setvar Owner = "The Boss" default
  379. * This could alternatively be handled by
  380. * removing '=' from special_chars and adding
  381. * it to the keyword table.
  382. */
  383. if ('=' == ch)
  384. followedby = FOLLBY_STRING;
  385. yytext[0] = (char)ch;
  386. yytext[1] = '\0';
  387. goto normal_return;
  388. } else
  389. push_back_char(ch);
  390. /* save the position of start of the token */
  391. ip_file->prev_token_line_no = ip_file->line_no;
  392. ip_file->prev_token_col_no = ip_file->col_no;
  393. /* Read in the lexeme */
  394. i = 0;
  395. while (EOF != (ch = get_next_char())) {
  396. yytext[i] = (char)ch;
  397. /* Break on whitespace or a special character */
  398. if (isspace(ch) || is_EOC(ch)
  399. || '"' == ch
  400. || (FOLLBY_TOKEN == followedby
  401. && is_special(ch)))
  402. break;
  403. /* Read the rest of the line on reading a start
  404. of comment character */
  405. if ('#' == ch) {
  406. while (EOF != (ch = get_next_char())
  407. && '\n' != ch)
  408. ; /* Null Statement */
  409. break;
  410. }
  411. i++;
  412. if (i >= COUNTOF(yytext))
  413. goto lex_too_long;
  414. }
  415. /* Pick up all of the string inside between " marks, to
  416. * end of line. If we make it to EOL without a
  417. * terminating " assume it for them.
  418. *
  419. * XXX - HMS: I'm not sure we want to assume the closing "
  420. */
  421. if ('"' == ch) {
  422. instring = 1;
  423. while (EOF != (ch = get_next_char()) &&
  424. ch != '"' && ch != '\n') {
  425. yytext[i++] = (char)ch;
  426. if (i >= COUNTOF(yytext))
  427. goto lex_too_long;
  428. }
  429. /*
  430. * yytext[i] will be pushed back as not part of
  431. * this lexeme, but any closing quote should
  432. * not be pushed back, so we read another char.
  433. */
  434. if ('"' == ch)
  435. ch = get_next_char();
  436. }
  437. /* Pushback the last character read that is not a part
  438. * of this lexeme.
  439. * If the last character read was an EOF, pushback a
  440. * newline character. This is to prevent a parse error
  441. * when there is no newline at the end of a file.
  442. */
  443. if (EOF == ch)
  444. push_back_char('\n');
  445. else
  446. push_back_char(ch);
  447. yytext[i] = '\0';
  448. } while (i == 0);
  449. /* Now return the desired token */
  450. /* First make sure that the parser is *not* expecting a string
  451. * as the next token (based on the previous token that was
  452. * returned) and that we haven't read a string.
  453. */
  454. if (followedby == FOLLBY_TOKEN && !instring) {
  455. token = is_keyword(yytext, &followedby);
  456. if (token)
  457. goto normal_return;
  458. else if (is_integer(yytext)) {
  459. yylval_was_set = 1;
  460. errno = 0;
  461. if ((yylval.Integer = strtol(yytext, NULL, 10)) == 0
  462. && ((errno == EINVAL) || (errno == ERANGE))) {
  463. msyslog(LOG_ERR,
  464. "Integer cannot be represented: %s",
  465. yytext);
  466. exit(1);
  467. } else {
  468. token = T_Integer;
  469. goto normal_return;
  470. }
  471. }
  472. else if (is_double(yytext)) {
  473. yylval_was_set = 1;
  474. errno = 0;
  475. if ((yylval.Double = atof(yytext)) == 0 && errno == ERANGE) {
  476. msyslog(LOG_ERR,
  477. "Double too large to represent: %s",
  478. yytext);
  479. exit(1);
  480. } else {
  481. token = T_Double;
  482. goto normal_return;
  483. }
  484. } else {
  485. /* Default: Everything is a string */
  486. yylval_was_set = 1;
  487. token = create_string_token(yytext);
  488. goto normal_return;
  489. }
  490. }
  491. /*
  492. * Either followedby is not FOLLBY_TOKEN or this lexeme is part
  493. * of a string. Hence, we need to return T_String.
  494. *
  495. * _Except_ we might have a -4 or -6 flag on a an association
  496. * configuration line (server, peer, pool, etc.).
  497. *
  498. * This is a terrible hack, but the grammar is ambiguous so we
  499. * don't have a choice. [SK]
  500. *
  501. * The ambiguity is in the keyword scanner, not ntp_parser.y.
  502. * We do not require server addresses be quoted in ntp.conf,
  503. * complicating the scanner's job. To avoid trying (and
  504. * failing) to match an IP address or DNS name to a keyword,
  505. * the association keywords use FOLLBY_STRING in the keyword
  506. * table, which tells the scanner to force the next token to be
  507. * a T_String, so it does not try to match a keyword but rather
  508. * expects a string when -4/-6 modifiers to server, peer, etc.
  509. * are encountered.
  510. * restrict -4 and restrict -6 parsing works correctly without
  511. * this hack, as restrict uses FOLLBY_TOKEN. [DH]
  512. */
  513. if ('-' == yytext[0]) {
  514. if ('4' == yytext[1]) {
  515. token = T_Ipv4_flag;
  516. goto normal_return;
  517. } else if ('6' == yytext[1]) {
  518. token = T_Ipv6_flag;
  519. goto normal_return;
  520. }
  521. }
  522. instring = 0;
  523. if (FOLLBY_STRING == followedby)
  524. followedby = FOLLBY_TOKEN;
  525. yylval_was_set = 1;
  526. token = create_string_token(yytext);
  527. normal_return:
  528. if (T_EOC == token)
  529. DPRINTF(4,("\t<end of command>\n"));
  530. else
  531. DPRINTF(4, ("yylex: lexeme '%s' -> %s\n", yytext,
  532. token_name(token)));
  533. if (!yylval_was_set)
  534. yylval.Integer = token;
  535. return token;
  536. lex_too_long:
  537. yytext[min(sizeof(yytext) - 1, 50)] = 0;
  538. msyslog(LOG_ERR,
  539. "configuration item on line %d longer than limit of %lu, began with '%s'",
  540. ip_file->line_no, (u_long)(sizeof(yytext) - 1), yytext);
  541. /*
  542. * If we hit the length limit reading the startup configuration
  543. * file, abort.
  544. */
  545. if (input_from_file)
  546. exit(sizeof(yytext) - 1);
  547. /*
  548. * If it's runtime configuration via ntpq :config treat it as
  549. * if the configuration text ended before the too-long lexeme,
  550. * hostname, or string.
  551. */
  552. yylval.Integer = 0;
  553. return 0;
  554. }