| /* liblouis Braille Translation and Back-Translation Library |
| |
| Copyright (C) 2015, 2016 Christian Egli, Swiss Library for the Blind, Visually Impaired |
| and Print Disabled |
| Copyright (C) 2017 Bert Frees |
| |
| 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, either version 3 of the License, or |
| (at your option) any later version. |
| |
| This program is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with this program. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include <config.h> |
| #include <assert.h> |
| #include <getopt.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include "internal.h" |
| #include "error.h" |
| #include "errno.h" |
| #include "progname.h" |
| #include "version-etc.h" |
| #include "brl_checks.h" |
| |
| static const struct option longopts[] = { |
| { "help", no_argument, NULL, 'h' }, { "version", no_argument, NULL, 'v' }, |
| { NULL, 0, NULL, 0 }, |
| }; |
| |
| const char version_etc_copyright[] = |
| "Copyright %s %d Swiss Library for the Blind, Visually Impaired and Print " |
| "Disabled"; |
| |
| #define AUTHORS "Christian Egli" |
| |
| static void |
| print_help(void) { |
| printf("\ |
| Usage: %s YAML_TEST_FILE\n", |
| program_name); |
| |
| fputs("\ |
| Run the tests defined in the YAML_TEST_FILE. Return 0 if all tests pass\n\ |
| or 1 if any of the tests fail. The details of failing tests are printed\n\ |
| to stderr.\n\n", |
| stdout); |
| |
| fputs("\ |
| -h, --help display this help and exit\n\ |
| -v, --version display version information and exit\n", |
| stdout); |
| |
| printf("\n"); |
| printf("Report bugs to %s.\n", PACKAGE_BUGREPORT); |
| |
| #ifdef PACKAGE_PACKAGER_BUG_REPORTS |
| printf("Report %s bugs to: %s\n", PACKAGE_PACKAGER, PACKAGE_PACKAGER_BUG_REPORTS); |
| #endif |
| #ifdef PACKAGE_URL |
| printf("%s home page: <%s>\n", PACKAGE_NAME, PACKAGE_URL); |
| #endif |
| } |
| |
| #define EXIT_SKIPPED 77 |
| |
| #ifdef HAVE_LIBYAML |
| #include <yaml.h> |
| |
| const char *event_names[] = { "YAML_NO_EVENT", "YAML_STREAM_START_EVENT", |
| "YAML_STREAM_END_EVENT", "YAML_DOCUMENT_START_EVENT", "YAML_DOCUMENT_END_EVENT", |
| "YAML_ALIAS_EVENT", "YAML_SCALAR_EVENT", "YAML_SEQUENCE_START_EVENT", |
| "YAML_SEQUENCE_END_EVENT", "YAML_MAPPING_START_EVENT", "YAML_MAPPING_END_EVENT" }; |
| const char *encoding_names[] = { "YAML_ANY_ENCODING", "YAML_UTF8_ENCODING", |
| "YAML_UTF16LE_ENCODING", "YAML_UTF16BE_ENCODING" }; |
| |
| const char *inline_table_prefix = "checkyaml_inline_"; |
| |
| yaml_parser_t parser; |
| yaml_event_t event; |
| |
| char *file_name; |
| |
| int errors = 0; |
| int count = 0; |
| |
| static char const **emph_classes = NULL; |
| |
| void |
| simple_error(const char *msg, yaml_parser_t *parser, yaml_event_t *event) { |
| error_at_line(EXIT_FAILURE, 0, file_name, |
| event->start_mark.line ? event->start_mark.line + 1 |
| : parser->problem_mark.line + 1, |
| "%s", msg); |
| } |
| |
| void |
| yaml_parse_error(yaml_parser_t *parser) { |
| error_at_line(EXIT_FAILURE, 0, file_name, parser->problem_mark.line + 1, "%s", |
| parser->problem); |
| } |
| |
| void |
| yaml_error(yaml_event_type_t expected, yaml_event_t *event) { |
| error_at_line(EXIT_FAILURE, 0, file_name, event->start_mark.line + 1, |
| "Expected %s (actual %s)", event_names[expected], event_names[event->type]); |
| } |
| |
| char * |
| read_table(yaml_event_t *start_event, yaml_parser_t *parser) { |
| char *table = NULL; |
| if (start_event->type != YAML_SCALAR_EVENT || |
| strcmp((const char *)start_event->data.scalar.value, "table")) |
| return 0; |
| |
| table = malloc(sizeof(char) * MAXSTRING); |
| table[0] = '\0'; |
| yaml_event_t event; |
| if (!yaml_parser_parse(parser, &event) || |
| !(event.type == YAML_SEQUENCE_START_EVENT || event.type == YAML_SCALAR_EVENT)) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Expected %s or %s (actual %s)", event_names[YAML_SEQUENCE_START_EVENT], |
| event_names[YAML_SCALAR_EVENT], event_names[event.type]); |
| |
| if (event.type == YAML_SEQUENCE_START_EVENT) { |
| yaml_event_delete(&event); |
| int done = 0; |
| char *p = table; |
| while (!done) { |
| if (!yaml_parser_parse(parser, &event)) { |
| yaml_parse_error(parser); |
| } |
| if (event.type == YAML_SEQUENCE_END_EVENT) { |
| done = 1; |
| } else if (event.type == YAML_SCALAR_EVENT) { |
| if (table != p) strcat(p++, ","); |
| strcat(p, (const char *)event.data.scalar.value); |
| p += event.data.scalar.length; |
| } |
| yaml_event_delete(&event); |
| } |
| if (!lou_getTable(table)) |
| error_at_line(EXIT_FAILURE, 0, file_name, start_event->start_mark.line + 1, |
| "Table %s not valid", table); |
| } else { // YAML_SCALAR_EVENT |
| yaml_char_t *p = event.data.scalar.value; |
| if (*p) |
| while (p[1]) p++; |
| if (*p == 10 || *p == 13) { |
| // If the scalar ends with a newline, assume it is a block |
| // scalar, so treat as an inline table. (Is there a proper way |
| // to check for a block scalar?) |
| sprintf(table, "%s%d", inline_table_prefix, rand()); |
| p = event.data.scalar.value; |
| yaml_char_t *line_start = p; |
| int line_len = 0; |
| while (*p) { |
| if (*p == 10 || *p == 13) { |
| char *line = strndup((const char *)line_start, line_len); |
| lou_compileString(table, line); |
| free(line); |
| line_start = p + 1; |
| line_len = 0; |
| } else { |
| line_len++; |
| } |
| p++; |
| } |
| } else { |
| strcat(table, (const char *)event.data.scalar.value); |
| } |
| yaml_event_delete(&event); |
| } |
| emph_classes = lou_getEmphClasses(table); // get declared emphasis classes |
| return table; |
| } |
| |
| void |
| read_flags(yaml_parser_t *parser, int *direction, int *hyphenation) { |
| yaml_event_t event; |
| int parse_error = 1; |
| |
| *direction = 0; |
| *hyphenation = 0; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_MAPPING_START_EVENT)) |
| yaml_error(YAML_MAPPING_START_EVENT, &event); |
| |
| yaml_event_delete(&event); |
| |
| while ((parse_error = yaml_parser_parse(parser, &event)) && |
| (event.type == YAML_SCALAR_EVENT)) { |
| if (!strcmp((const char *)event.data.scalar.value, "testmode")) { |
| yaml_event_delete(&event); |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SCALAR_EVENT)) |
| yaml_error(YAML_SCALAR_EVENT, &event); |
| if (!strcmp((const char *)event.data.scalar.value, "forward")) { |
| *direction = 0; |
| } else if (!strcmp((const char *)event.data.scalar.value, "backward")) { |
| *direction = 1; |
| } else if (!strcmp((const char *)event.data.scalar.value, "hyphenate")) { |
| *hyphenation = 1; |
| } else { |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Testmode '%s' not supported\n", event.data.scalar.value); |
| } |
| } else { |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Flag '%s' not supported\n", event.data.scalar.value); |
| } |
| } |
| if (!parse_error) yaml_parse_error(parser); |
| if (event.type != YAML_MAPPING_END_EVENT) yaml_error(YAML_MAPPING_END_EVENT, &event); |
| yaml_event_delete(&event); |
| } |
| |
| int |
| read_xfail(yaml_parser_t *parser) { |
| yaml_event_t event; |
| /* assume xfail true if there is an xfail key */ |
| int xfail = 1; |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SCALAR_EVENT)) |
| yaml_error(YAML_SCALAR_EVENT, &event); |
| if (!strcmp((const char *)event.data.scalar.value, "false") || |
| !strcmp((const char *)event.data.scalar.value, "off")) |
| xfail = 0; |
| yaml_event_delete(&event); |
| return xfail; |
| } |
| |
| translationModes |
| read_mode(yaml_parser_t *parser) { |
| yaml_event_t event; |
| translationModes mode = 0; |
| int parse_error = 1; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SEQUENCE_START_EVENT)) |
| yaml_error(YAML_SEQUENCE_START_EVENT, &event); |
| yaml_event_delete(&event); |
| |
| while ((parse_error = yaml_parser_parse(parser, &event)) && |
| (event.type == YAML_SCALAR_EVENT)) { |
| if (!strcmp((const char *)event.data.scalar.value, "noContractions")) { |
| mode |= noContractions; |
| } else if (!strcmp((const char *)event.data.scalar.value, "compbrlAtCursor")) { |
| mode |= compbrlAtCursor; |
| } else if (!strcmp((const char *)event.data.scalar.value, "dotsIO")) { |
| mode |= dotsIO; |
| } else if (!strcmp((const char *)event.data.scalar.value, "comp8Dots")) { |
| mode |= comp8Dots; |
| } else if (!strcmp((const char *)event.data.scalar.value, "pass1Only")) { |
| mode |= pass1Only; |
| } else if (!strcmp((const char *)event.data.scalar.value, "compbrlLeftCursor")) { |
| mode |= compbrlLeftCursor; |
| } else if (!strcmp((const char *)event.data.scalar.value, "ucBrl")) { |
| mode |= ucBrl; |
| } else if (!strcmp((const char *)event.data.scalar.value, "noUndefinedDots")) { |
| mode |= noUndefinedDots; |
| } else if (!strcmp((const char *)event.data.scalar.value, "partialTrans")) { |
| mode |= partialTrans; |
| } else { |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Mode '%s' not supported\n", event.data.scalar.value); |
| } |
| yaml_event_delete(&event); |
| } |
| if (!parse_error) yaml_parse_error(parser); |
| if (event.type != YAML_SEQUENCE_END_EVENT) |
| yaml_error(YAML_SEQUENCE_END_EVENT, &event); |
| yaml_event_delete(&event); |
| return mode; |
| } |
| |
| int * |
| read_inPos(yaml_parser_t *parser, int wrdlen, int translen) { |
| int *pos = malloc(sizeof(int) * translen); |
| int i = 0; |
| yaml_event_t event; |
| int parse_error = 1; |
| char *tail; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SEQUENCE_START_EVENT)) |
| yaml_error(YAML_SEQUENCE_START_EVENT, &event); |
| yaml_event_delete(&event); |
| |
| while ((parse_error = yaml_parser_parse(parser, &event)) && |
| (event.type == YAML_SCALAR_EVENT)) { |
| if (i >= translen) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Too many input positions for translation of length %d.", translen); |
| errno = 0; |
| int val = strtol((const char *)event.data.scalar.value, &tail, 0); |
| if (errno != 0) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Not a valid input position '%s'. Must be a number\n", |
| event.data.scalar.value); |
| if ((const char *)event.data.scalar.value == tail) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "No digits found in input position '%s'. Must be a number\n", |
| event.data.scalar.value); |
| pos[i++] = val; |
| yaml_event_delete(&event); |
| } |
| if (i < translen) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Too few input positions (%i) for translation of length %i\n", i, |
| translen); |
| if (!parse_error) yaml_parse_error(parser); |
| if (event.type != YAML_SEQUENCE_END_EVENT) |
| yaml_error(YAML_SEQUENCE_END_EVENT, &event); |
| yaml_event_delete(&event); |
| return pos; |
| } |
| |
| int * |
| read_outPos(yaml_parser_t *parser, int wrdlen, int translen) { |
| int *pos = malloc(sizeof(int) * wrdlen); |
| int i = 0; |
| yaml_event_t event; |
| int parse_error = 1; |
| char *tail; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SEQUENCE_START_EVENT)) |
| yaml_error(YAML_SEQUENCE_START_EVENT, &event); |
| yaml_event_delete(&event); |
| |
| while ((parse_error = yaml_parser_parse(parser, &event)) && |
| (event.type == YAML_SCALAR_EVENT)) { |
| if (i >= wrdlen) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Too many output positions for input string of length %d.", translen); |
| errno = 0; |
| int val = strtol((const char *)event.data.scalar.value, &tail, 0); |
| if (errno != 0) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Not a valid output position '%s'. Must be a number\n", |
| event.data.scalar.value); |
| if ((const char *)event.data.scalar.value == tail) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "No digits found in output position '%s'. Must be a number\n", |
| event.data.scalar.value); |
| pos[i++] = val; |
| yaml_event_delete(&event); |
| } |
| if (i < wrdlen) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Too few output positions (%i) for input string of length %i\n", i, |
| wrdlen); |
| if (!parse_error) yaml_parse_error(parser); |
| if (event.type != YAML_SEQUENCE_END_EVENT) |
| yaml_error(YAML_SEQUENCE_END_EVENT, &event); |
| yaml_event_delete(&event); |
| return pos; |
| } |
| |
| int * |
| read_cursorPos(yaml_parser_t *parser, int wrdlen, int translen) { |
| int *pos = malloc(sizeof(int) * wrdlen); |
| int i = 0; |
| yaml_event_t event; |
| int parse_error = 1; |
| char *tail; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SEQUENCE_START_EVENT)) |
| yaml_error(YAML_SEQUENCE_START_EVENT, &event); |
| yaml_event_delete(&event); |
| |
| while ((parse_error = yaml_parser_parse(parser, &event)) && |
| (event.type == YAML_SCALAR_EVENT)) { |
| if (i >= wrdlen) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Too many cursor positions for input string of length %d.", wrdlen); |
| errno = 0; |
| int val = strtol((const char *)event.data.scalar.value, &tail, 0); |
| if (errno != 0) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Not a valid cursor position '%s'. Must be a number\n", |
| event.data.scalar.value); |
| if ((const char *)event.data.scalar.value == tail) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "No digits found in cursor position '%s'. Must be a number\n", |
| event.data.scalar.value); |
| pos[i++] = val; |
| yaml_event_delete(&event); |
| } |
| if (i < wrdlen) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Too few cursor positions (%i) for input string of length %i\n", i, |
| wrdlen); |
| if (!parse_error) yaml_parse_error(parser); |
| if (event.type != YAML_SEQUENCE_END_EVENT) |
| yaml_error(YAML_SEQUENCE_END_EVENT, &event); |
| yaml_event_delete(&event); |
| return pos; |
| } |
| |
| void |
| read_typeform_string(yaml_parser_t *parser, formtype *typeform, typeforms kind, int len) { |
| yaml_event_t event; |
| int typeform_len; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SCALAR_EVENT)) |
| yaml_error(YAML_SCALAR_EVENT, &event); |
| typeform_len = strlen((const char *)event.data.scalar.value); |
| if (typeform_len != len) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Too many or too typeforms (%i) for word of length %i\n", typeform_len, |
| len); |
| update_typeform((const char *)event.data.scalar.value, typeform, kind); |
| yaml_event_delete(&event); |
| } |
| |
| formtype * |
| read_typeforms(yaml_parser_t *parser, int len) { |
| yaml_event_t event; |
| formtype *typeform = calloc(len, sizeof(formtype)); |
| int parse_error = 1; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_MAPPING_START_EVENT)) |
| yaml_error(YAML_MAPPING_START_EVENT, &event); |
| yaml_event_delete(&event); |
| |
| while ((parse_error = yaml_parser_parse(parser, &event)) && |
| (event.type == YAML_SCALAR_EVENT)) { |
| if (strcmp((const char *)event.data.scalar.value, "computer_braille") == 0) { |
| yaml_event_delete(&event); |
| read_typeform_string(parser, typeform, computer_braille, len); |
| } else if (strcmp((const char *)event.data.scalar.value, "no_translate") == 0) { |
| yaml_event_delete(&event); |
| read_typeform_string(parser, typeform, no_translate, len); |
| } else if (strcmp((const char *)event.data.scalar.value, "no_contract") == 0) { |
| yaml_event_delete(&event); |
| read_typeform_string(parser, typeform, no_contract, len); |
| } else { |
| int i; |
| typeforms kind = plain_text; |
| for (i = 0; emph_classes[i]; i++) { |
| if (strcmp((const char *)event.data.scalar.value, emph_classes[i]) == 0) { |
| yaml_event_delete(&event); |
| kind = italic << i; |
| if (kind > emph_10) |
| error_at_line(EXIT_FAILURE, 0, file_name, |
| event.start_mark.line + 1, |
| "Typeform '%s' was not declared\n", |
| event.data.scalar.value); |
| read_typeform_string(parser, typeform, kind, len); |
| break; |
| } |
| } |
| } |
| } |
| if (!parse_error) yaml_parse_error(parser); |
| |
| if (event.type != YAML_MAPPING_END_EVENT) yaml_error(YAML_MAPPING_END_EVENT, &event); |
| yaml_event_delete(&event); |
| return typeform; |
| } |
| |
| void |
| read_options(yaml_parser_t *parser, int wordLen, int translationLen, int *xfail, |
| translationModes *mode, formtype **typeform, int **inPos, int **outPos, |
| int **cursorPos) { |
| yaml_event_t event; |
| char *option_name; |
| int parse_error = 1; |
| |
| *mode = 0; |
| *xfail = 0; |
| *typeform = NULL; |
| *inPos = NULL; |
| *outPos = NULL; |
| *cursorPos = NULL; |
| |
| while ((parse_error = yaml_parser_parse(parser, &event)) && |
| (event.type == YAML_SCALAR_EVENT)) { |
| option_name = |
| strndup((const char *)event.data.scalar.value, event.data.scalar.length); |
| |
| if (!strcmp(option_name, "xfail")) { |
| yaml_event_delete(&event); |
| *xfail = read_xfail(parser); |
| } else if (!strcmp(option_name, "mode")) { |
| yaml_event_delete(&event); |
| *mode = read_mode(parser); |
| } else if (!strcmp(option_name, "typeform")) { |
| yaml_event_delete(&event); |
| *typeform = read_typeforms(parser, wordLen); |
| } else if (!strcmp(option_name, "inputPos")) { |
| yaml_event_delete(&event); |
| *inPos = read_inPos(parser, wordLen, translationLen); |
| } else if (!strcmp(option_name, "outputPos")) { |
| yaml_event_delete(&event); |
| *outPos = read_outPos(parser, wordLen, translationLen); |
| } else if (!strcmp(option_name, "cursorPos")) { |
| yaml_event_delete(&event); |
| *cursorPos = read_cursorPos(parser, wordLen, translationLen); |
| } else { |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Unsupported option %s", option_name); |
| } |
| free(option_name); |
| } |
| if (!parse_error) yaml_parse_error(parser); |
| if (event.type != YAML_MAPPING_END_EVENT) yaml_error(YAML_MAPPING_END_EVENT, &event); |
| yaml_event_delete(&event); |
| } |
| |
| /* see http://stackoverflow.com/questions/5117393/utf-8-strings-length-in-linux-c */ |
| int |
| my_strlen_utf8_c(char *s) { |
| int i = 0, j = 0; |
| while (s[i]) { |
| if ((s[i] & 0xc0) != 0x80) j++; |
| i++; |
| } |
| return j; |
| } |
| |
| void |
| read_test(yaml_parser_t *parser, char **tables, int direction, int hyphenation) { |
| yaml_event_t event; |
| char *description = NULL; |
| char *word; |
| char *translation; |
| int xfail = 0; |
| translationModes mode = 0; |
| formtype *typeform = NULL; |
| int *inPos = NULL; |
| int *outPos = NULL; |
| int *cursorPos = NULL; |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SCALAR_EVENT)) |
| simple_error("Word expected", parser, &event); |
| |
| word = strndup((const char *)event.data.scalar.value, event.data.scalar.length); |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SCALAR_EVENT)) |
| simple_error("Translation expected", parser, &event); |
| |
| translation = |
| strndup((const char *)event.data.scalar.value, event.data.scalar.length); |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(parser, &event)) yaml_parse_error(parser); |
| |
| /* Handle an optional description */ |
| if (event.type == YAML_SCALAR_EVENT) { |
| description = word; |
| word = translation; |
| translation = |
| strndup((const char *)event.data.scalar.value, event.data.scalar.length); |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(parser, &event)) yaml_parse_error(parser); |
| } |
| |
| if (event.type == YAML_MAPPING_START_EVENT) { |
| yaml_event_delete(&event); |
| read_options(parser, my_strlen_utf8_c(word), my_strlen_utf8_c(translation), |
| &xfail, &mode, &typeform, &inPos, &outPos, &cursorPos); |
| |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SEQUENCE_END_EVENT)) |
| yaml_error(YAML_SEQUENCE_END_EVENT, &event); |
| } else if (event.type != YAML_SEQUENCE_END_EVENT) { |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Expected %s or %s (actual %s)", event_names[YAML_MAPPING_START_EVENT], |
| event_names[YAML_SEQUENCE_END_EVENT], event_names[event.type]); |
| } |
| |
| int result = 0; |
| char **table = tables; |
| while (*table) { |
| if (inPos || outPos || cursorPos) { |
| result |= check_full( |
| *table, word, typeform, translation, mode, NULL, direction, !xfail); |
| if (inPos) result |= check_inpos(*table, word, inPos); |
| if (outPos) result |= check_outpos(*table, word, outPos); |
| if (cursorPos) result |= check_cursor_pos(*table, word, cursorPos); |
| } else if (hyphenation) { |
| result |= check_hyphenation(*table, word, translation); |
| } else { |
| // FIXME: Note that the typeform array was constructed using the |
| // emphasis classes mapping of the last compiled table. This |
| // means that if we are testing multiple tables at the same time |
| // they must have the same mapping (i.e. the emphasis classes |
| // must be defined in the same order). |
| result |= check_full( |
| *table, word, typeform, translation, mode, NULL, direction, !xfail); |
| } |
| table++; |
| } |
| if (xfail != result) { |
| if (description) fprintf(stderr, "%s\n", description); |
| error_at_line(0, 0, file_name, event.start_mark.line + 1, |
| (xfail ? "Unexpected Pass" : "Failure")); |
| errors++; |
| } |
| yaml_event_delete(&event); |
| count++; |
| |
| free(description); |
| free(word); |
| free(translation); |
| free(typeform); |
| free(inPos); |
| free(outPos); |
| free(cursorPos); |
| } |
| |
| void |
| read_tests(yaml_parser_t *parser, char **tables, int direction, int hyphenation) { |
| yaml_event_t event; |
| if (!yaml_parser_parse(parser, &event) || (event.type != YAML_SEQUENCE_START_EVENT)) |
| yaml_error(YAML_SEQUENCE_START_EVENT, &event); |
| |
| yaml_event_delete(&event); |
| |
| int done = 0; |
| while (!done) { |
| if (!yaml_parser_parse(parser, &event)) { |
| yaml_parse_error(parser); |
| } |
| if (event.type == YAML_SEQUENCE_END_EVENT) { |
| done = 1; |
| yaml_event_delete(&event); |
| } else if (event.type == YAML_SEQUENCE_START_EVENT) { |
| yaml_event_delete(&event); |
| read_test(parser, tables, direction, hyphenation); |
| } else { |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Expected %s or %s (actual %s)", event_names[YAML_SEQUENCE_END_EVENT], |
| event_names[YAML_SEQUENCE_START_EVENT], event_names[event.type]); |
| } |
| } |
| } |
| |
| /* |
| * This custom table resolver handles magic table names that represent |
| * inline tables. |
| */ |
| static char ** |
| customTableResolver(const char *tableList, const char *base) { |
| static char *dummy_table[1]; |
| if (strncmp(tableList, inline_table_prefix, strlen(inline_table_prefix)) == 0) |
| return dummy_table; |
| return _lou_defaultTableResolver(tableList, base); |
| } |
| |
| #endif // HAVE_LIBYAML |
| |
| int |
| main(int argc, char *argv[]) { |
| int optc; |
| |
| set_program_name(argv[0]); |
| |
| while ((optc = getopt_long(argc, argv, "hv", longopts, NULL)) != -1) switch (optc) { |
| /* --help and --version exit immediately, per GNU coding standards. */ |
| case 'v': |
| version_etc( |
| stdout, program_name, PACKAGE_NAME, VERSION, AUTHORS, (char *)NULL); |
| exit(EXIT_SUCCESS); |
| break; |
| case 'h': |
| print_help(); |
| exit(EXIT_SUCCESS); |
| break; |
| default: |
| fprintf(stderr, "Try `%s --help' for more information.\n", program_name); |
| exit(EXIT_FAILURE); |
| break; |
| } |
| |
| if (optind != argc - 1) { |
| /* Print error message and exit. */ |
| if (optind < argc - 1) |
| fprintf(stderr, "%s: extra operand: %s\n", program_name, argv[optind + 1]); |
| else |
| fprintf(stderr, "%s: no YAML test file specified\n", program_name); |
| |
| fprintf(stderr, "Try `%s --help' for more information.\n", program_name); |
| exit(EXIT_FAILURE); |
| } |
| |
| #ifdef WITHOUT_YAML |
| fprintf(stderr, |
| "Skipping tests for %s as yaml was disabled in configure with " |
| "--without-yaml\n", |
| argv[1]); |
| return EXIT_SKIPPED; |
| #else |
| #ifndef HAVE_LIBYAML |
| fprintf(stderr, "Skipping tests for %s as libyaml was not found\n", argv[1]); |
| return EXIT_SKIPPED; |
| #endif // not HAVE_LIBYAML |
| #endif // WITHOUT_YAML |
| |
| #ifndef WITHOUT_YAML |
| #ifdef HAVE_LIBYAML |
| |
| FILE *file; |
| yaml_parser_t parser; |
| yaml_event_t event; |
| |
| file_name = argv[1]; |
| file = fopen(file_name, "rb"); |
| if (!file) { |
| fprintf(stderr, "%s: file not found: %s\n", program_name, file_name); |
| exit(3); |
| } |
| |
| char *dir_name = strdup(file_name); |
| int i = strlen(dir_name); |
| while (i > 0) { |
| if (dir_name[i - 1] == '/' || dir_name[i - 1] == '\\') { |
| i--; |
| break; |
| } |
| i--; |
| } |
| dir_name[i] = '\0'; |
| // FIXME: problem with this is that |
| // LOUIS_TABLEPATH=$(top_srcdir)/tables,... does not work anymore because |
| // $(top_srcdir) == .. (not an absolute path) |
| chdir(dir_name); |
| |
| // register custom table resolver |
| lou_registerTableResolver(&customTableResolver); |
| |
| assert(yaml_parser_initialize(&parser)); |
| |
| yaml_parser_set_input_file(&parser, file); |
| |
| if (!yaml_parser_parse(&parser, &event) || (event.type != YAML_STREAM_START_EVENT)) { |
| yaml_error(YAML_STREAM_START_EVENT, &event); |
| } |
| |
| if (event.data.stream_start.encoding != YAML_UTF8_ENCODING) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "UTF-8 encoding expected (actual %s)", |
| encoding_names[event.data.stream_start.encoding]); |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(&parser, &event) || |
| (event.type != YAML_DOCUMENT_START_EVENT)) { |
| yaml_error(YAML_DOCUMENT_START_EVENT, &event); |
| } |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(&parser, &event) || (event.type != YAML_MAPPING_START_EVENT)) { |
| yaml_error(YAML_MAPPING_START_EVENT, &event); |
| } |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(&parser, &event)) |
| simple_error("table expected", &parser, &event); |
| |
| int MAXTABLES = 10; |
| char *tables[MAXTABLES + 1]; |
| while ((tables[0] = read_table(&event, &parser))) { |
| yaml_event_delete(&event); |
| int k = 1; |
| while (1) { |
| if (!yaml_parser_parse(&parser, &event)) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Expected table or %s (actual %s)", |
| event_names[YAML_SCALAR_EVENT], event_names[event.type]); |
| if ((tables[k++] = read_table(&event, &parser))) { |
| if (k == MAXTABLES) exit(EXIT_FAILURE); |
| yaml_event_delete(&event); |
| } else |
| break; |
| } |
| |
| if (event.type != YAML_SCALAR_EVENT) yaml_error(YAML_SCALAR_EVENT, &event); |
| |
| int direction = 0; |
| int hyphenation = 0; |
| if (!strcmp((const char *)event.data.scalar.value, "flags")) { |
| yaml_event_delete(&event); |
| read_flags(&parser, &direction, &hyphenation); |
| |
| if (!yaml_parser_parse(&parser, &event) || |
| (event.type != YAML_SCALAR_EVENT) || |
| strcmp((const char *)event.data.scalar.value, "tests")) { |
| simple_error("tests expected", &parser, &event); |
| } |
| yaml_event_delete(&event); |
| read_tests(&parser, tables, direction, hyphenation); |
| |
| } else if (!strcmp((const char *)event.data.scalar.value, "tests")) { |
| yaml_event_delete(&event); |
| read_tests(&parser, tables, direction, hyphenation); |
| } else { |
| simple_error("flags or tests expected", &parser, &event); |
| } |
| |
| char **p = tables; |
| while (*p) free(*(p++)); |
| |
| if (!yaml_parser_parse(&parser, &event)) |
| error_at_line(EXIT_FAILURE, 0, file_name, event.start_mark.line + 1, |
| "Expected table or %s (actual %s)", |
| event_names[YAML_MAPPING_END_EVENT], event_names[event.type]); |
| } |
| if (event.type != YAML_MAPPING_END_EVENT) yaml_error(YAML_MAPPING_END_EVENT, &event); |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(&parser, &event) || (event.type != YAML_DOCUMENT_END_EVENT)) { |
| yaml_error(YAML_DOCUMENT_END_EVENT, &event); |
| } |
| yaml_event_delete(&event); |
| |
| if (!yaml_parser_parse(&parser, &event) || (event.type != YAML_STREAM_END_EVENT)) { |
| yaml_error(YAML_STREAM_END_EVENT, &event); |
| } |
| yaml_event_delete(&event); |
| |
| yaml_parser_delete(&parser); |
| |
| free(emph_classes); |
| lou_free(); |
| |
| assert(!fclose(file)); |
| |
| printf("%s (%d tests, %d failure%s)\n", (errors ? "FAILURE" : "SUCCESS"), count, |
| errors, ((errors != 1) ? "s" : "")); |
| |
| return errors ? 1 : 0; |
| |
| #endif // HAVE_LIBYAML |
| #endif // not WITHOUT_YAML |
| } |