From 7c1306ff7b6adc12db9989c5f7873afd8085f4ad Mon Sep 17 00:00:00 2001 From: Jonas 'Sortie' Termansen Date: Mon, 21 Apr 2014 22:37:44 +0200 Subject: [PATCH] Add carray(1). --- Makefile | 3 + carray/.gitignore | 1 + carray/Makefile | 25 +++ carray/carray.c++ | 417 ++++++++++++++++++++++++++++++++++++++++++++++ doc/user-guide | 9 + system/Makefile | 2 + 6 files changed, 457 insertions(+) create mode 100644 carray/.gitignore create mode 100644 carray/Makefile create mode 100644 carray/carray.c++ 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