diff --git a/Makefile b/Makefile
index b52fbf48..e1537bf9 100644
--- a/Makefile
+++ b/Makefile
@@ -10,6 +10,7 @@ libm \
libpthread \
dispd \
bench \
+carray \
ext \
games \
mbr \
@@ -47,11 +48,13 @@ all: sysroot
.PHONY: build-tools
build-tools:
+ $(MAKE) -C carray
$(MAKE) -C mkinitrd
$(MAKE) -C tix
.PHONY: install-build-tools
install-build-tools:
+ $(MAKE) -C carray install
$(MAKE) -C mkinitrd install
$(MAKE) -C tix install
diff --git a/carray/.gitignore b/carray/.gitignore
new file mode 100644
index 00000000..cacb8c25
--- /dev/null
+++ b/carray/.gitignore
@@ -0,0 +1 @@
+carray
diff --git a/carray/Makefile b/carray/Makefile
new file mode 100644
index 00000000..9694370b
--- /dev/null
+++ b/carray/Makefile
@@ -0,0 +1,25 @@
+include ../compiler.mak
+include ../version.mak
+include ../dirs.mak
+
+OPTLEVEL?=$(DEFAULT_OPTLEVEL)
+CXXFLAGS?=$(OPTLEVEL)
+
+CPPFLAGS:=$(CPPFLAGS) -DVERSIONSTR=\"$(VERSION)\"
+CXXFLAGS:=$(CXXFLAGS) -Wall -Wextra -fno-exceptions -fno-rtti
+
+BINARY:=carray
+
+all: $(BINARY)
+
+.PHONY: all install uninstall clean
+
+install: all
+ mkdir -p $(DESTDIR)$(BINDIR)
+ install $(BINARY) $(DESTDIR)$(BINDIR)
+
+%: %.c++
+ $(CXX) -std=gnu++11 $(CPPFLAGS) $(CXXFLAGS) $< -o $@
+
+clean:
+ rm -f $(BINARY) *.o
diff --git a/carray/carray.c++ b/carray/carray.c++
new file mode 100644
index 00000000..df2349ee
--- /dev/null
+++ b/carray/carray.c++
@@ -0,0 +1,417 @@
+/*******************************************************************************
+
+ Copyright(C) Jonas 'Sortie' Termansen 2014.
+
+ 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 .
+
+ carray.c++
+ Convert a binary file to a C array.
+
+*******************************************************************************/
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#if !defined(VERSIONSTR)
+#define VERSIONSTR "unknown version"
+#endif
+
+static void compact_arguments(int* argc, char*** argv)
+{
+ for ( int i = 0; i < *argc; i++ )
+ {
+ while ( i < *argc && !(*argv)[i] )
+ {
+ for ( int n = i; n < *argc; n++ )
+ (*argv)[n] = (*argv)[n+1];
+ (*argc)--;
+ }
+ }
+}
+
+bool get_option_variable(const char* option, char** varptr,
+ const char* arg, int argc, char** argv, int* ip,
+ const char* argv0)
+{
+ 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);
+ 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;
+ return true;
+}
+
+#define GET_OPTION_VARIABLE(str, varptr) \
+ get_option_variable(str, varptr, arg, argc, argv, &i, argv0)
+
+void get_short_option_variable(char c, char** varptr,
+ const char* arg, int argc, char** argv, int* ip,
+ const char* argv0)
+{
+ free(*varptr);
+ if ( *(arg+1) )
+ {
+ *varptr = strdup(arg + 1);
+ }
+ else
+ {
+ if ( *ip + 1 == argc )
+ {
+ error(0, 0, "option requires an argument -- '%c'", c);
+ fprintf(stderr, "Try `%s --help' for more information.\n", argv0);
+ exit(1);
+ }
+ *varptr = strdup(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);
+ fprintf(fp, "License GPLv3+: GNU GPL version 3 or later .\n");
+ fprintf(fp, "This is free software: you are free to change and redistribute it.\n");
+ fprintf(fp, "There is NO WARRANTY, to the extent permitted by law.\n");
+}
+
+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_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* argv0 = argv[0];
+ for ( int i = 1; i < argc; i++ )
+ {
+ const char* arg = argv[i];
+ if ( arg[0] != '-' || !arg[1] )
+ continue;
+ argv[i] = NULL;
+ if ( !strcmp(arg, "--") )
+ break;
+ if ( arg[1] != '-' )
+ {
+ while ( char c = *++arg ) switch ( c )
+ {
+ case 'c': flag_const = true; break;
+ case 'e': flag_extern = 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 'r': flag_raw = true; break;
+ case 's': flag_static = true; break;
+ case 'v': flag_volatile = true; break;
+ default:
+ fprintf(stderr, "%s: unknown option -- '%c'\n", argv0, c);
+ help(stderr, argv0);
+ exit(1);
+ }
+ }
+ 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, "--forward") )
+ flag_forward = true;
+ else if ( !strcmp(arg, "--use-guard") )
+ flag_guard = true;
+ else if ( !strcmp(arg, "--include") )
+ flag_include = true;
+ else if ( !strcmp(arg, "--raw") )
+ flag_raw = true;
+ else if ( !strcmp(arg, "--static") )
+ flag_static = true;
+ else if ( !strcmp(arg, "--volatile") )
+ flag_volatile = true;
+ else if ( !strcmp(arg, "--char") )
+ free(arg_type), arg_type = strdup("char");
+ else if ( !strcmp(arg, "--signed-char") )
+ free(arg_type), arg_type = strdup("signed char");
+ else if ( !strcmp(arg, "--unsigned-char") )
+ free(arg_type), arg_type = strdup("unsigned char");
+ else if ( !strcmp(arg, "--int8_t") )
+ free(arg_type), arg_type = strdup("int8_t");
+ else if ( !strcmp(arg, "--uint8_t") )
+ free(arg_type), arg_type = strdup("uint8_t");
+ else if ( GET_OPTION_VARIABLE("--guard", &arg_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
+ {
+ fprintf(stderr, "%s: unknown option: %s\n", argv0, arg);
+ help(stderr, argv0);
+ exit(1);
+ }
+ }
+
+ compact_arguments(&argc, &argv);
+
+ const char* output_path = arg_output;
+
+ if ( flag_extern && flag_static )
+ error(1, 0, "the --extern and --static are mutually incompatible");
+ if ( flag_forward && flag_raw )
+ error(1, 0, "the --forward and --raw are mutually incompatible");
+
+ if ( !arg_type )
+ arg_type = strdup("unsigned char");
+
+ char* guard = arg_guard;
+ if ( !guard )
+ {
+ if ( output_path )
+ guard = strdup(output_path);
+ else if ( 2 <= argc && strcmp(argv[1], "-") != 0 )
+ {
+ guard = (char*) malloc(strlen(argv[1]) + 2 + 1);
+ strcpy(guard, argv[1]);
+ strcat(guard, "_H");
+ }
+ 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 ( flag_include && !arg_includes )
+ {
+ if ( !strcmp(arg_type, "int8_t") ||
+ !strcmp(arg_type, "uint8_t") )
+ arg_includes = strdup("#include ");
+ }
+
+ char* identifier = arg_identifier;
+ if ( !identifier )
+ {
+ if ( output_path )
+ identifier = strdup(output_path);
+ else if ( 2 <= argc && strcmp(argv[1], "-") != 0 )
+ identifier = strdup(argv[1]);
+ else
+ identifier = strdup("carray");
+
+ for ( size_t i = 0; identifier[i]; i++ )
+ {
+ if ( i && identifier[i] == '.' && !strchr(identifier + i, '/') )
+ identifier[i] = '\0';
+ else if ( 'a' <= identifier[i] && 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' )
+ continue;
+ else if ( guard[i] == '+' )
+ identifier[i] = 'x';
+ else if ( i == 0 )
+ identifier[i] = 'x';
+ else
+ identifier[i] = '_';
+ }
+ }
+
+ if ( output_path && !freopen(output_path, "w", stdout) )
+ error(1, errno, "%s", output_path);
+
+ if ( flag_guard && guard )
+ {
+ printf("#ifndef %s\n", guard);
+ printf("#define %s\n", guard);
+ printf("\n");
+ }
+
+ if ( flag_include && arg_includes )
+ {
+ printf("%s\n", arg_includes);
+ printf("\n");
+ }
+
+ if ( !flag_raw )
+ {
+ if ( flag_extern_c )
+ {
+ printf("#if defined(__cplusplus)\n");
+ printf("extern \"C\" {\n");
+ printf("#endif\n");
+ printf("\n");
+ }
+ if ( flag_extern )
+ printf("extern ");
+ if ( flag_static )
+ printf("static ");
+ if ( flag_const )
+ printf("const ");
+ if ( flag_volatile )
+ printf("volatile ");
+ printf("%s %s[]", arg_type, identifier);
+ if ( flag_forward )
+ printf(";\n");
+ else
+ printf(" = {\n");
+ }
+
+ if ( !flag_forward )
+ {
+ bool begun_row = false;
+ unsigned int position = 0;
+
+ for ( int i = 0; i < argc; i++ )
+ {
+ if ( i == 0 && 2 <= argc )
+ continue;
+ FILE* fp;
+ const char* arg;
+ if ( argc == 1 || !strcmp(argv[i], "-") )
+ {
+ arg = "";
+ fp = stdin;
+ }
+ else
+ {
+ arg = argv[i];
+ fp = fopen(arg, "r");
+ }
+ if ( !fp )
+ error(1, errno, "%s", arg);
+ int ic;
+ while ( (ic = fgetc(fp)) != EOF )
+ {
+ printf("%c0x%02X,", position++ ? ' ' : '\t', ic);
+ begun_row = true;
+ if ( position == (80 - 8) / 6 )
+ {
+ printf("\n");
+ position = 0;
+ begun_row = false;
+ }
+ }
+ if ( ferror(fp) )
+ error(1, errno, "fgetc: %s", arg);
+ if ( fp != stdin )
+ fclose(fp);
+ }
+
+ if ( begun_row )
+ printf("\n");
+ }
+
+ if ( !flag_raw )
+ {
+ if ( !flag_forward )
+ printf("};\n");
+ if ( flag_extern_c )
+ {
+ printf("\n");
+ printf("#if defined(__cplusplus)\n");
+ printf("} /* extern \"C\" */\n");
+ printf("#endif\n");
+ }
+ }
+
+ if ( flag_guard && guard )
+ {
+ printf("\n");
+ printf("#endif\n");
+ }
+
+ if ( ferror(stdout) || fflush(stdout) == EOF )
+ error(1, errno, "%s", output_path ? output_path : "stdout");
+
+ return 0;
+}
diff --git a/doc/user-guide b/doc/user-guide
index e31d7618..16dd23c6 100644
--- a/doc/user-guide
+++ b/doc/user-guide
@@ -145,6 +145,7 @@ Sortix comes with a number of home-made programs. Here is an overview:
* `benchctxswitch` - useless benchmark
* `benchsyscall` - slightly less useless benchmark
* `calc` - reverse polish calculator
+* `carray` - convert a binary file to a C array
* `cat` - display file on terminal
* `chmod` - change file mode bits
* `chroot` - change the root directory
@@ -550,6 +551,14 @@ driver program.
make
make install
+### Building carray ###
+
+This program converts a binary file to an C array.
+
+ cd /src/carray
+ make
+ make install
+
### Building the Sortix Kernel ###
The Sortix kernel is the core of the Sortix operating system. It provides all
diff --git a/system/Makefile b/system/Makefile
index 6581a19a..7d4b8e2d 100644
--- a/system/Makefile
+++ b/system/Makefile
@@ -22,6 +22,7 @@ clean:
$(MAKE) -B -C $(SRCDIR)/libpthread clean
$(MAKE) -B -C $(SRCDIR)/dispd clean
$(MAKE) -B -C $(SRCDIR)/bench clean
+ $(MAKE) -B -C $(SRCDIR)/carray clean
$(MAKE) -B -C $(SRCDIR)/games clean
$(MAKE) -B -C $(SRCDIR)/mkinitrd clean
$(MAKE) -B -C $(SRCDIR)/utils clean
@@ -71,6 +72,7 @@ system:
$(MAKE) -B -C $(SRCDIR)/libm install DESTDIR=$(ROOT) C_INCLUDE_PATH=$(ROOT)/include CPLUS_INCLUDE_PATH=$(ROOT)/include LIBRARY_PATH=$(ROOT)/$(cputype)/lib
$(MAKE) -B -C $(SRCDIR)/libpthread install DESTDIR=$(ROOT) C_INCLUDE_PATH=$(ROOT)/include CPLUS_INCLUDE_PATH=$(ROOT)/include LIBRARY_PATH=$(ROOT)/$(cputype)/lib
$(MAKE) -B -C $(SRCDIR)/dispd install DESTDIR=$(ROOT) C_INCLUDE_PATH=$(ROOT)/include CPLUS_INCLUDE_PATH=$(ROOT)/include LIBRARY_PATH=$(ROOT)/$(cputype)/lib
+ $(MAKE) -B -C $(SRCDIR)/bench install DESTDIR=$(ROOT) C_INCLUDE_PATH=$(ROOT)/include CPLUS_INCLUDE_PATH=$(ROOT)/include LIBRARY_PATH=$(ROOT)/$(cputype)/lib
$(MAKE) -B -C $(SRCDIR)/games install DESTDIR=$(ROOT) C_INCLUDE_PATH=$(ROOT)/include CPLUS_INCLUDE_PATH=$(ROOT)/include LIBRARY_PATH=$(ROOT)/$(cputype)/lib
$(MAKE) -B -C $(SRCDIR)/mkinitrd install DESTDIR=$(ROOT) C_INCLUDE_PATH=$(ROOT)/include CPLUS_INCLUDE_PATH=$(ROOT)/include LIBRARY_PATH=$(ROOT)/$(cputype)/lib
$(MAKE) -B -C $(SRCDIR)/utils install DESTDIR=$(ROOT) C_INCLUDE_PATH=$(ROOT)/include CPLUS_INCLUDE_PATH=$(ROOT)/include LIBRARY_PATH=$(ROOT)/$(cputype)/lib