blob: 6887ef538dd523660629beb7764898babba2a980 [file] [log] [blame]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include "program.h"
#include "cmdline.h"
#include "log.h"
#include "cldr.h"
#include "datafile.h"
#include "utf8.h"
#define DEFAULT_OUTPUT_FORMAT "%s\\t%n\\n"
static char *opt_outputFormat;
BEGIN_OPTION_TABLE(programOptions)
{ .word = "output-format",
.letter = 'f',
.argument = strtext("string"),
.setting.string = &opt_outputFormat,
.internal.setting = DEFAULT_OUTPUT_FORMAT,
.description = strtext("The format of each output line.")
},
END_OPTION_TABLE(programOptions)
static
BEGIN_USAGE_NOTES(usageNotes)
"The output format is printf-like -",
"arbitrary text which may contain",
"field specifiers (introduced via a percent sign [%])",
"and/or special characters (introduced via a backslash [\\]).",
"The default format, excluding the quotes, is \"" DEFAULT_OUTPUT_FORMAT "\".",
"",
"These field specifiers are recognized:",
" %n the name of the character sequence",
" %s the character sequence itself",
" %x the character sequence in hexadecimal",
" %% a literal percent sign",
"",
"These special characters are recognized:",
" \\a alert (bell)",
" \\b backspace",
" \\e escape",
" \\f form feed",
" \\n new line",
" \\r carriage return",
" \\t horizontal tab",
" \\v vertical tab",
" \\\\ literal backslasha ",
END_USAGE_NOTES
static void
onFormatError (void) {
exit(PROG_EXIT_SYNTAX);
}
static void
onMissingCharacter (const char *type) {
logMessage(LOG_ERR, "missing %s character", type);
onFormatError();
}
static void
onUnrecognizedCharacter (const char *type, int byte) {
logMessage(LOG_ERR, "unrecognized %s character: %c", type, byte);
onFormatError();
}
static void
onOutputError (void) {
logMessage(LOG_ERR, "output error %d: %s", errno, strerror(errno));
exit(PROG_EXIT_FATAL);
}
static void
putByte (int byte) {
if (fputc(byte, stdout) == EOF) onOutputError();
}
static void
putString (const char *string) {
if (fputs(string, stdout) == EOF) onOutputError();
}
static void
putHexadecimal (const char *string) {
size_t size = strlen(string) + 1;
wchar_t characters[size];
const char *byte = string;
wchar_t *character = characters;
wchar_t *end = character;
convertUtf8ToWchars(&byte, &end, size);
while (character < end) {
if (writeHexadecimalCharacter(stdout, *character) == EOF) onOutputError();
character += 1;
}
}
static
CLDR_ANNOTATION_HANDLER(handleAnnotation) {
typedef enum {LITERAL, FORMAT, ESCAPE} State;
State state = LITERAL;
const char *format = opt_outputFormat;
while (*format) {
int byte = *format & 0XFF;
switch (state) {
case LITERAL: {
switch (byte) {
case '%':
state = FORMAT;
break;
case '\\':
state = ESCAPE;
break;
default:
putByte(byte);
break;
}
break;
}
case FORMAT: {
switch (byte) {
case 'n':
putString(parameters->name);
break;
case 's':
putString(parameters->sequence);
break;
case 'x':
putHexadecimal(parameters->sequence);
break;
case '%':
putByte(byte);
break;
default:
onUnrecognizedCharacter("format", byte);
return 0;
}
state = LITERAL;
break;
}
case ESCAPE: {
static const char escapes[] = {
['a'] = '\a',
['b'] = '\b',
['e'] = '\e',
['f'] = '\f',
['n'] = '\n',
['r'] = '\r',
['t'] = '\t',
['v'] = '\v',
['\\'] = '\\'
};
switch (byte) {
default: {
if (byte < ARRAY_COUNT(escapes)) {
char escape = escapes[byte];
if (escape) {
putByte(escape);
break;
}
}
onUnrecognizedCharacter("escape", byte);
return 0;
}
}
state = LITERAL;
break;
}
}
format += 1;
}
switch (state) {
case LITERAL:
return 1;
case FORMAT:
onMissingCharacter("format");
break;
case ESCAPE:
onMissingCharacter("escape");
break;
}
return 0;
}
int
main (int argc, char *argv[]) {
{
const CommandLineDescriptor descriptor = {
.options = &programOptions,
.applicationName = "brltty-cldr",
.usage = {
.purpose = strtext("List the characters defined within a CLDR (Common Locale Data Repository Project) annotations file."),
.parameters = "input-file",
.notes = USAGE_NOTES(usageNotes),
}
};
PROCESS_OPTIONS(descriptor, argc, argv);
}
if (argc < 1) {
logMessage(LOG_ERR, "missing annotations file name");
return PROG_EXIT_SYNTAX;
}
const char *inputFile = *argv++;
argc -= 1;
if (argc > 0) {
logMessage(LOG_ERR, "too many parameters");
return PROG_EXIT_SYNTAX;
}
return cldrParseFile(inputFile, handleAnnotation, NULL)?
PROG_EXIT_SUCCESS:
PROG_EXIT_FATAL;
}