Modernize carray(1) and fix missing allocation checks.

Add short options for most long options. Rename the -i option to -H, but
support -i for compatibility until the next release cycle, where -i will
become the short option of --identifier. Rename --include to --headers
and support --include until the next release cycle.

Add carray(1) manual page which makes --help unnecessary, and remove
--version as it surely matches your local Sortix version.
This commit is contained in:
Jonas 'Sortie' Termansen 2016-08-12 18:34:19 +02:00
parent 848eaaf593
commit a97e1ef16c
4 changed files with 354 additions and 150 deletions

View File

@ -6,7 +6,6 @@ include ../build-aux/dirs.mak
OPTLEVEL?=$(DEFAULT_OPTLEVEL)
CFLAGS?=$(OPTLEVEL)
CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
CFLAGS:=$(CFLAGS) -Wall -Wextra
ifeq ($(HOST_IS_SORTIX),0)
@ -22,6 +21,8 @@ all: $(BINARY)
install: all
mkdir -p $(DESTDIR)$(BINDIR)
install $(BINARY) $(DESTDIR)$(BINDIR)
mkdir -p $(DESTDIR)$(MANDIR)/man1
cp carray.1 $(DESTDIR)$(MANDIR)/man1/carray.1
%: %.c
$(CC) -std=gnu11 $(CFLAGS) $(CPPFLAGS) $< -o $@

237
carray/carray.1 Normal file
View File

@ -0,0 +1,237 @@
.Dd $Mdocdate: August 12 2016 $
.Dt CARRAY 1
.Os
.Sh NAME
.Nm carray
.Nd convert binary file to C array
.Sh SYNOPSIS
.Nm carray
.\" After releasing Sortix 1.1, make this change to match carray.c:
.\".Op Fl ceEgHrsv
.\" Compatibility:
.Op Fl ceEgHirsv
.\" (End)
.Op Fl G Ar guard
.\" After releasing Sortix 1.1, make this change to match carray.c:
.\".Op Fl i Ar identifier
.\" Compatibility:
.Op Fl \-identifier Ns "=" Ns Ar identifier
.\" (End)
.Op Fl \-includes Ns "=" Ns Ar include-statements
.Op Fl o Ar output
.Op Fl t Ar type
.Op Ar
.Sh DESCRIPTION
.Nm
encodes its input as the source code for a C array of hexadecimal constants.
The input is the specified
.Ar file
operands concatenated, or the standard input if no input files are specified.
.Nm
writes the source code for a C array to the standard output, or
.Ar output
if the
.Fl o
option is given.
The default type is an array of
.Sy unsigned char .
The array contents are indented with tabs
and the lines don't exceed 80 columns (tabs have the width of 8 spaces).
.Pp
The default array name is the output path if set, or the path of the first
input file (if any), or otherwise
.Sy carray .
The default array name has all file extensions removed (but a leading period
in the file name is kept). The default array name is converted to the
characters
a-z, A-Z,
.Sq _ ,
and 0-9. 0-9 cannot be the first character of an identifier.
.Sq +
will be replaced by
.Sq x .
Any other characters are replaced by
.Sq _
unless it is the first character, in which case it is replaced by
.Sq x .
.Pp
A guard macro is optionally used by the
.Fl g
and
.Fl G
options. The default guard macro is the output path if set, or the path of the
first input file (if any) followed by
.Sy _H ,
or otherwise
.Sy CARRAY_H .
The default guard macro is converted to the characters A-Z,
.Sq _ ,
and 0-9. 0-9 cannot be the first character of an identifier. The lower-case
a-z is converted to the upper-case A-Z.
.Sq +
is replaced by
.Sq X .
Any other characters are replaced by
.Sq _ .
.Pp
Parts of the output are optional, but the output will be in this order: The
opening guard macro check, the guard macro definition, the inclusion of
prerequisite headers, the opening
.Sy extern """C"""
block, the array declaration and initialization, the closing
.Sy extern """C"""
block, and the closing guard macro check.
.Pp
The options are as follows:
.Bl -tag -width "12345678"
.It Fl c , Fl \-const
Declare the array with the
.Sy const
keyword.
.It Fl e , Fl \-extern
Declare the array with the
.Sy extern
keyword. This option is mutually incompatible with the
.Fl s
option.
.It Fl E , Fl \-extern-c
Wrap the array in
.Sy extern """C""" { }
subject to a preprocessor check for C++.
.It Fl f , Fl \-forward
Forward declare the array only, do not initialize it with contents. The input
files are not opened and the standard input is unused. This option is mutually
incompatible with the
.Fl r
option.
.It Fl g , Fl \-use-guard
Wrap the output in a preprocessor conditional checking the guard macro is not
set, and declare the macro if it is not set. This conditional is a classic C
include guard that ensures only the first inclusion of a header has any effect.
.It Fl G , Fl \-guard Ar guard
Sets the guard macro to
.Ar guard
and enables the guard macro check as if the
.Fl g
option was set as well. The guard macro is unrestricted and untransformed and
will be be output verbatim.
.It Fl H , Fl \-headers
Output inclusions of all the prerequisite headers. By default, there are no
prerequisite headers, unless the array type is one of the
.In stdint.h
types, in which case
.In stdint.h
is the only prerequisite header.
.\" After releasing Sortix 1.1, make this change to match carray.c:
.\".It Fl i , Fl \-identifier Ar identifier
.\" Compatibility:
.It Fl \-identifier Ar identifier
.\" (End)
Sets the name of the array to
.Ar identifier .
The identifier is unrestricted and will be be output verbatim.
.It Fl \-includes Ar include-statements
Sets the C preprocessor statements
.Ar include-statements
as how to include all the prerequisite headers and enables inclusion of the
prerequisite headers as if the
.Fl H
option was set. The preprocessor statements are unrestricted and untransformed
and will be be output verbatim.
.It Fl o , Fl output Ar output
Write the output to the
.Ar output
path rather than the standard output.
.It Fl r , Fl \-raw
Output only the hexadecimally encoded array contents, and the guard macro check
if set by the
.Fl g
option. This option is mutually incompatible with the
.Fl f
option.
.It Fl s , Fl \-static
Declare the array with the
.Sy static
keyword.
.It Fl t , Fl \-type Ar type
Declare the array as an array of the specified
.Ar type .
The type is unrestricted and will be be output verbatim.
.It Fl v , Fl \-volatile
Declare the array with the
.Sy volatile
keyword.
.El
.Pp
In addition to the
.Fl t
option, the array type can be set by the following options:
.Bl -tag -width "--unsigned-char"
.It Fl \-char
Declare as array of
.Sy char .
.It Fl \-signed-char
Declare as array of
.Sy signed char .
.It Fl \-unsigned-char
Declare as array of
.Sy unsigned char .
.It Fl \-int8_t
Declare as array of
.Sy int8_t .
.It Fl \-uint8_t
Declare as array of
.Sy uint8_t .
.El
.Sh EXIT STATUS
.Nm
will exit 0 on success and non-zero otherwise.
.Sh EXAMPLES
.Bd -literal
$ echo foo | carray
unsigned char carray[] = {
0x66, 0x6F, 0x6F, 0x0A,
};
.Ed
.Bd -literal
$ echo foo | carray -cs -o foo.inc
$ cat foo.inc
static const unsigned char foo[] = {
0x66, 0x6F, 0x6F, 0x0A,
};
.Ed
.Bd -literal
$ echo foo | carray -ceEfgH -t uint8_t -o foo.h
$ cat foo.h
#ifndef FOO_H
#define FOO_H
#include <stdint.h>
#if defined(__cplusplus)
extern "C" {
#endif
extern const uint8_t foo[];
#if defined(__cplusplus)
} /* extern "C" */
#endif
#endif
.Ed
.Bd -literal
$ echo foo | carray -cH -t uint8_t -o foo.c
$ cat foo.c
#include <stdint.h>
const uint8_t foo[] = {
0x66, 0x6F, 0x6F, 0x0A,
};
.Ed
.Bd -literal
$ echo foo | carray -r
0x66, 0x6F, 0x6F, 0x0A,
.Ed
.Sh SEE ALSO
.Xr gcc 1

View File

@ -19,7 +19,6 @@
#include <err.h>
#include <errno.h>
#include <locale.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
@ -38,112 +37,64 @@ static void compact_arguments(int* argc, char*** argv)
}
}
bool get_option_variable(const char* option, char** varptr,
const char* arg, int argc, char** argv, int* ip,
const char* argv0)
bool get_option_variable(const char* option, const char** varptr,
const char* arg, int argc, char** argv, int* ip)
{
size_t option_len = strlen(option);
if ( strncmp(option, arg, option_len) != 0 )
return false;
if ( arg[option_len] == '=' )
{
*varptr = strdup(arg + option_len + 1);
*varptr = arg + option_len + 1;
return true;
}
if ( arg[option_len] != '\0' )
return false;
if ( *ip + 1 == argc )
{
fprintf(stderr, "%s: expected operand after `%s'\n", argv0, option);
exit(1);
}
*varptr = strdup(argv[++*ip]), argv[*ip] = NULL;
errx(1, "expected operand after `%s'\n", option);
*varptr = argv[++*ip];
argv[*ip] = NULL;
return true;
}
#define GET_OPTION_VARIABLE(str, varptr) \
get_option_variable(str, varptr, arg, argc, argv, &i, argv0)
get_option_variable(str, varptr, arg, argc, argv, &i)
void get_short_option_variable(char c, char** varptr,
const char* arg, int argc, char** argv, int* ip,
const char* argv0)
void get_short_option_variable(char c, const char** varptr,
const char* arg, int argc, char** argv, int* ip)
{
free(*varptr);
if ( *(arg+1) )
{
*varptr = strdup(arg + 1);
}
*varptr = arg + 1;
else
{
if ( *ip + 1 == argc )
{
warnx("option requires an argument -- '%c'", c);
fprintf(stderr, "Try `%s --help' for more information.\n", argv0);
exit(1);
}
*varptr = strdup(argv[*ip + 1]);
errx(1, "option requires an argument -- '%c'", c);
*varptr = argv[*ip + 1];
argv[++(*ip)] = NULL;
}
}
#define GET_SHORT_OPTION_VARIABLE(c, varptr) \
get_short_option_variable(c, varptr, arg, argc, argv, &i, argv0)
static void help(FILE* fp, const char* argv0)
{
fprintf(fp, "Usage: %s [OPTION]... [INPUT...] -o OUTPUT\n", argv0);
fprintf(fp, "Convert a binary file to a C array.\n");
fprintf(fp, "\n");
fprintf(fp, "Mandatory arguments to long options are mandatory for short options too.\n");
fprintf(fp, " -c, --const add const keyword\n");
fprintf(fp, " -e, --extern add extern keyword\n");
fprintf(fp, " --extern-c use C linkage\n");
fprintf(fp, " -f, --forward forward declare rather than define\n");
fprintf(fp, " --guard=MACRO use include guard macro MACRO\n");
fprintf(fp, " -g, --use-guard add include guard\n");
fprintf(fp, " -i, --include include prerequisite headers\n");
fprintf(fp, " --identifier=NAME use identifier NAME\n");
fprintf(fp, " --includes=INCLUDES emit raw preprocessor INCLUDES\n");
fprintf(fp, " -o, --output=FILE write result to FILE\n");
fprintf(fp, " -r, --raw emit only raw \n");
fprintf(fp, " -s, --static add static keyword\n");
fprintf(fp, " --type=TYPE use type TYPE [unsigned char]\n");
fprintf(fp, " -v, --volatile add volatile keyword\n");
fprintf(fp, " --char use type char\n");
fprintf(fp, " --signed-char use type signed char\n");
fprintf(fp, " --unsigned-char use type unsigned char\n");
fprintf(fp, " --int8_t use type int8_t\n");
fprintf(fp, " --uint8_t use type uint8_t\n");
fprintf(fp, " --help display this help and exit\n");
fprintf(fp, " --version output version information and exit\n");
}
static void version(FILE* fp, const char* argv0)
{
fprintf(fp, "%s (Sortix) %s\n", argv0, VERSIONSTR);
}
get_short_option_variable(c, varptr, arg, argc, argv, &i)
int main(int argc, char* argv[])
{
setlocale(LC_ALL, "");
bool flag_const = false;
bool flag_extern_c = false;
bool flag_extern = false;
bool flag_forward = false;
bool flag_guard = false;
bool flag_include = false;
bool flag_headers = false;
bool flag_raw = false;
bool flag_static = false;
bool flag_volatile = false;
char* arg_guard = NULL;
char* arg_identifier = NULL;
char* arg_includes = NULL;
char* arg_output = NULL;
char* arg_type = NULL;
const char* guard = NULL;
const char* identifier = NULL;
const char* includes = NULL;
const char* output = NULL;
const char* type = NULL;
const char* argv0 = argv[0];
for ( int i = 1; i < argc; i++ )
{
const char* arg = argv[i];
@ -159,33 +110,44 @@ int main(int argc, char* argv[])
{
case 'c': flag_const = true; break;
case 'e': flag_extern = true; break;
case 'E': flag_extern_c = true; break;
case 'f': flag_forward = true; break;
case 'g': flag_guard = true; break;
case 'i': flag_include = true; break;
case 'o': GET_SHORT_OPTION_VARIABLE('o', &arg_output); arg = "o"; break;
case 'G': GET_SHORT_OPTION_VARIABLE('G', &guard); arg = "G"; flag_guard = true; break;
case 'H': flag_headers = true; break;
// TODO: After releasing Sortix 1.1, change -i to --identifier
// rather than -H (--headers).
#if 0 // Future behavior:
case 'i': GET_SHORT_OPTION_VARIABLE('i', &identifier); arg = "i"; break;
#else // Compatibility:
case 'i': flag_headers = true; break;
#endif
case 'o': GET_SHORT_OPTION_VARIABLE('o', &output); arg = "o"; break;
case 'r': flag_raw = true; break;
case 's': flag_static = true; break;
case 't': GET_SHORT_OPTION_VARIABLE('t', &type); arg = "t"; break;
case 'v': flag_volatile = true; break;
default:
fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
help(stderr, argv0);
exit(1);
errx(1, "unknown option -- '%c'\n", c);
}
}
else if ( !strcmp(arg, "--help") )
help(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--version") )
version(stdout, argv0), exit(0);
else if ( !strcmp(arg, "--const") )
flag_const = true;
else if ( !strcmp(arg, "--extern") )
flag_extern = true;
else if ( !strcmp(arg, "--extern-c") )
flag_extern_c = true;
else if ( !strcmp(arg, "--forward") )
flag_forward = true;
else if ( !strcmp(arg, "--use-guard") )
flag_guard = true;
else if ( !strcmp(arg, "--headers") )
flag_headers = true;
// TODO: After releasing Sortix 1.1, remove --include.
#if 1 // Compatibility:
else if ( !strcmp(arg, "--include") )
flag_include = true;
flag_headers = true;
#endif
else if ( !strcmp(arg, "--raw") )
flag_raw = true;
else if ( !strcmp(arg, "--static") )
@ -193,109 +155,112 @@ int main(int argc, char* argv[])
else if ( !strcmp(arg, "--volatile") )
flag_volatile = true;
else if ( !strcmp(arg, "--char") )
free(arg_type), arg_type = strdup("char");
type = "char";
else if ( !strcmp(arg, "--signed-char") )
free(arg_type), arg_type = strdup("signed char");
type = "signed char";
else if ( !strcmp(arg, "--unsigned-char") )
free(arg_type), arg_type = strdup("unsigned char");
type = "unsigned char";
else if ( !strcmp(arg, "--int8_t") )
free(arg_type), arg_type = strdup("int8_t");
type = "int8_t";
else if ( !strcmp(arg, "--uint8_t") )
free(arg_type), arg_type = strdup("uint8_t");
else if ( GET_OPTION_VARIABLE("--guard", &arg_guard) )
type = "uint8_t";
else if ( GET_OPTION_VARIABLE("--guard", &guard) )
flag_guard = true;
else if ( GET_OPTION_VARIABLE("--identifier", &arg_identifier) ) { }
else if ( GET_OPTION_VARIABLE("--includes", &arg_includes) )
flag_include = true;
else if ( GET_OPTION_VARIABLE("--output", &arg_output) ) { }
else if ( GET_OPTION_VARIABLE("--type", &arg_type) ) { }
else if ( !strcmp(arg, "--extern-c") )
flag_extern_c = true;
else if ( GET_OPTION_VARIABLE("--identifier", &identifier) ) { }
else if ( GET_OPTION_VARIABLE("--includes", &includes) )
flag_headers = true;
else if ( GET_OPTION_VARIABLE("--output", &output) ) { }
else if ( GET_OPTION_VARIABLE("--type", &type) ) { }
else
{
fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
help(stderr, argv0);
exit(1);
}
errx(1, "unknown option: %s\n", arg);
}
compact_arguments(&argc, &argv);
const char* output_path = arg_output;
if ( flag_extern && flag_static )
errx(1, "the --extern and --static are mutually incompatible");
errx(1, "the --extern and --static options are mutually incompatible");
if ( flag_forward && flag_raw )
errx(1, "the --forward and --raw are mutually incompatible");
errx(1, "the --forward and --raw options are mutually incompatible");
if ( !arg_type )
arg_type = strdup("unsigned char");
if ( !type )
type = "unsigned char";
char* guard = arg_guard;
if ( !guard )
{
if ( output_path )
guard = strdup(output_path);
char* new_guard;
if ( output )
new_guard = strdup(output);
else if ( 2 <= argc && strcmp(argv[1], "-") != 0 )
asprintf(&guard, "%s_H", argv[1]);
else
guard = strdup("CARRAY_H");
for ( size_t i = 0; guard[i]; i++ )
{
if ( 'A' <= guard[i] && guard[i] <= 'Z' )
continue;
else if ( 'a' <= guard[i] && guard[i] <= 'z' )
guard[i] = 'A' + guard[i] - 'a';
else if ( i != 0 && '0' <= guard[i] && guard[i] <= '9' )
continue;
else if ( guard[i] == '+' )
guard[i] = 'X';
else if ( i == 0 )
guard[i] = 'X';
else
guard[i] = '_';
if ( asprintf(&new_guard, "%s_H", argv[1]) < 0 )
err(1, "asprintf");
}
else
new_guard = strdup("CARRAY_H");
if ( !new_guard )
err(1, "strdup");
for ( size_t i = 0; new_guard[i]; i++ )
{
if ( 'A' <= new_guard[i] && new_guard[i] <= 'Z' )
continue;
else if ( 'a' <= new_guard[i] && new_guard[i] <= 'z' )
new_guard[i] = 'A' + new_guard[i] - 'a';
else if ( i != 0 && '0' <= new_guard[i] && new_guard[i] <= '9' )
continue;
else if ( new_guard[i] == '+' )
new_guard[i] = 'X';
else if ( i == 0 )
new_guard[i] = 'X';
else
new_guard[i] = '_';
}
guard = new_guard;
}
if ( flag_include && !arg_includes )
if ( flag_headers && !includes )
{
if ( !strcmp(arg_type, "int8_t") ||
!strcmp(arg_type, "uint8_t") )
arg_includes = strdup("#include <stdint.h>");
if ( !strcmp(type, "int8_t") ||
!strcmp(type, "uint8_t") )
includes = "#include <stdint.h>";
}
char* identifier = arg_identifier;
if ( !identifier )
{
if ( output_path )
identifier = strdup(output_path);
char* new_identifier;
if ( output )
new_identifier = strdup(output);
else if ( 2 <= argc && strcmp(argv[1], "-") != 0 )
identifier = strdup(argv[1]);
new_identifier = strdup(argv[1]);
else
identifier = strdup("carray");
new_identifier = strdup("carray");
if ( !new_identifier )
err(1, "strdup");
for ( size_t i = 0; identifier[i]; i++ )
for ( size_t i = 0; new_identifier[i]; i++ )
{
if ( i && identifier[i] == '.' && !strchr(identifier + i, '/') )
identifier[i] = '\0';
else if ( 'a' <= identifier[i] && identifier[i] <= 'z' )
if ( i && new_identifier[i] == '.' && !strchr(new_identifier + i, '/') )
new_identifier[i] = '\0';
else if ( 'a' <= new_identifier[i] && new_identifier[i] <= 'z' )
continue;
else if ( 'A' <= identifier[i] && identifier[i] <= 'Z' )
identifier[i] = 'a' + identifier[i] - 'A';
else if ( i != 0 && '0' <= identifier[i] && identifier[i] <= '9' )
else if ( 'A' <= new_identifier[i] && new_identifier[i] <= 'Z' )
new_identifier[i] = 'a' + new_identifier[i] - 'A';
else if ( i != 0 && '0' <= new_identifier[i] && new_identifier[i] <= '9' )
continue;
else if ( guard[i] == '+' )
identifier[i] = 'x';
new_identifier[i] = 'x';
else if ( i == 0 )
identifier[i] = 'x';
new_identifier[i] = 'x';
else
identifier[i] = '_';
new_identifier[i] = '_';
}
identifier = new_identifier;
}
if ( output_path && !freopen(output_path, "w", stdout) )
err(1, "%s", output_path);
if ( output && !freopen(output, "w", stdout) )
err(1, "%s", output);
if ( flag_guard && guard )
{
@ -304,9 +269,9 @@ int main(int argc, char* argv[])
printf("\n");
}
if ( flag_include && arg_includes )
if ( flag_headers && includes )
{
printf("%s\n", arg_includes);
printf("%s\n", includes);
printf("\n");
}
@ -327,7 +292,7 @@ int main(int argc, char* argv[])
printf("const ");
if ( flag_volatile )
printf("volatile ");
printf("%s %s[]", arg_type, identifier);
printf("%s %s[]", type, identifier);
if ( flag_forward )
printf(";\n");
else
@ -399,7 +364,7 @@ int main(int argc, char* argv[])
}
if ( ferror(stdout) || fflush(stdout) == EOF )
err(1, "%s", output_path ? output_path : "stdout");
err(1, "%s", output ? output : "stdout");
return 0;
}

View File

@ -199,11 +199,12 @@ endif
%: %.kblayout
kblayout-compiler --format=sortix-kblayout-1 --compression=none $< -o $@
# TODO: After releasing Sortix 1.1, change --identifier to -i.
kb/default-kblayout.h: kb/default-kblayout
carray -cgis --uint8_t --guard=SORTIX_KB_DEFAULT_KBLAYOUT_H --identifier=default_kblayout $< -o $@
carray -cgHs -t uint8_t -G SORTIX_KB_DEFAULT_KBLAYOUT_H --identifier=default_kblayout $< -o $@
vgafont.h: vgafont.f16
carray -gis --uint8_t --guard=VGAFONT_H --identifier=vgafont $< -o $@
carray -gHs -t uint8_t -G VGAFONT_H --identifier=vgafont $< -o $@
*.cpp */*.cpp */*/*.cpp: | kb/default-kblayout.h vgafont.h