blob: 41d940934628372a823ca32aeb8a579a35f3cc45 [file] [log] [blame]
/* 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
}