summaryrefslogtreecommitdiffstats
path: root/tools/slang-embed/slang-embed.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/slang-embed/slang-embed.cpp')
-rw-r--r--tools/slang-embed/slang-embed.cpp228
1 files changed, 228 insertions, 0 deletions
diff --git a/tools/slang-embed/slang-embed.cpp b/tools/slang-embed/slang-embed.cpp
new file mode 100644
index 000000000..5dee2a609
--- /dev/null
+++ b/tools/slang-embed/slang-embed.cpp
@@ -0,0 +1,228 @@
+// slang-embed.cpp
+
+// This file implements a simple utility for taking an input file
+// and embedding into a C++ source file as a `static const` array.
+
+// For now this utility uses plain C stdlib functionality rather
+// than depending on any of the utiltiies from the Slang project
+// libraries.
+//
+#ifdef _MSC_VER
+#pragma warning(disable: 4996)
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+// Utility to free pointers on scope exit
+struct ScopedMemory
+{
+ ScopedMemory(void* ptr)
+ : ptr(ptr)
+ {}
+
+ ~ScopedMemory()
+ {
+ if(ptr) free(ptr);
+ }
+
+ void* ptr;
+};
+
+// Utility to close file on scope exit
+struct ScopedFile
+{
+ ScopedFile(FILE* file)
+ : file(file)
+ {}
+
+ ~ScopedFile()
+ {
+ if(file) fclose(file);
+ }
+
+ FILE* file;
+};
+
+// The utility is implemented as a single `struct` type
+// that provides a context for the code. We do this as
+// an alternative to using global variables for passing
+// around options easily.
+//
+struct App
+{
+ char const* appName = "slang-embed";
+ char const* inputPath = nullptr;
+
+ void parseOptions(int argc, char** argv)
+ {
+ // Options are currently all specified by position,
+ // so the parsing logic is simplistic.
+
+ if( argc > 0 )
+ {
+ appName = *argv++;
+ argc--;
+ }
+
+ if( argc > 0 )
+ {
+ inputPath = *argv++;
+ argc--;
+ }
+
+ if( !inputPath || (argc != 0) )
+ {
+ fprintf(stderr, "usage: %s <inputPath>\n", appName);
+ exit(1);
+ }
+ }
+
+ void processInputFile()
+ {
+ // Note: Eventually we might support multiple input files in a
+ // single invocation of the tool, but for now we only have
+ // a single file to process.
+
+ // We open the input file in text mode because we are currently
+ // embedding textual source files. If/when this utility gets
+ // used for binary files another mode could be called for.
+ //
+ // (Alternatively, we might always use binary mode, but this
+ // could lead to a difference in the embedded bytes based on
+ // the line ending convention of the host platform)
+ //
+ FILE* inputFile = fopen(inputPath, "r");
+ ScopedFile inputFileCleanup(inputFile);
+ if( !inputFile )
+ {
+ fprintf(stderr, "%s: error: failed to open '%s' for reading\n", appName, inputPath);
+ exit(1);
+ }
+
+ // We derive an output path simply by appending `.cpp` to the input path.
+ //
+ // TODO: If we start adding more complicated options, a `-o` option
+ // to specify a desired output path would be an obvious choice.
+ //
+ char* outputPath = (char*) malloc(strlen(inputPath) + strlen(".cpp") + 1);
+ ScopedMemory outputPathCleanup(outputPath);
+ strcpy(outputPath, inputPath);
+ strcat(outputPath, ".cpp");
+
+ FILE* outputFile = fopen(outputPath, "w");
+ ScopedFile outputFileCleanup(outputFile);
+ if( !outputPath )
+ {
+ fprintf(stderr, "%s: error: failed to open '%s' for reading\n", appName, outputPath);
+ exit(1);
+ }
+
+ // We want to derive a variable name based on the name of
+ // the input file we are mbedded. Toward this end, we
+ // start by trying to strip off any leading directories
+ // in the path. This logic is ad hoc but should suffice,
+ // given that we don't plan to give the files we embed
+ // unconventional names.
+ //
+ char const* fileName = inputPath;
+ if(auto pos = strrchr(fileName, '\\'))
+ fileName = pos+1;
+ if(auto pos = strrchr(fileName, '/'))
+ fileName = pos+1;
+
+ // The variable name will start as a copy of the file
+ // name, although we will immediately drop any extension
+ // that comes after a `.` to trim the name further.
+ //
+ char* variableName = (char*) malloc(strlen(fileName)+1);
+ ScopedMemory variableNameCleanup(variableName);
+ strcpy(variableName, fileName);
+ if(auto pos = strchr(variableName, '.'))
+ *pos = 0;
+
+ // We will also replace any `-` in the file name with
+ // a `_` in the generate variable name, so that the
+ // tool will be compatible with our current naming
+ // convention of using `-` as the separator in file names.
+ //
+ for( auto cursor = variableName; *cursor; ++cursor)
+ {
+ switch( *cursor )
+ {
+ default:
+ break;
+ case '-': *cursor = '_';
+ }
+ }
+
+ // With all the preliminaries out of the way, the actual
+ // task of outputting the generated source file is simple.
+ //
+ fprintf(outputFile, "// generated code; do not edit\n");
+ fprintf(outputFile, "const char* %s =\n", variableName);
+
+ // Note: For now we are embedding the file as a string
+ // literal, with full knowledge that this strategy
+ // will run into limitations in certain compilers
+ // (e.g., some versions of the Visual C++ compiler
+ // don't handle string literals larger than 64KB).
+ //
+ // TODO: Eventually we should replace this logic with
+ // code to emit a plain array of `unsigned char` with
+ // an array initializer list `{ ... }`. While some
+ // compilers have limitations or performance issues
+ // with large array literals, the practical limits
+ // appear to be higher than they are for string literals.
+
+ fprintf(outputFile, "\"");
+ for( ;;)
+ {
+ int c = fgetc(inputFile);
+ if( c == EOF )
+ break;
+
+ // Based on the byte that we are trying to emit,
+ // we may need to emit an escape sequence.
+ //
+ switch( c )
+ {
+ // The common C escape sequencs are handled directly.
+ //
+ case '"': fprintf(outputFile, "\\\""); break;
+ case '\n': fprintf(outputFile, "\\n\"\n\""); break;
+ case '\t': fprintf(outputFile, "\\t"); break;
+
+ default:
+ // For all other cases, we detect if the byte
+ // is in the printable ASCII range, and emit
+ // it directly if sco.
+ //
+ if( c >= 32 && c <= 126 )
+ {
+ fputc(c, outputFile);
+ }
+ else
+ {
+ // Otherwise, we emit the byte as an octal
+ // escape sequence, being sure to emit a
+ // full three digits to avoid errorneous
+ // encoding if the following byte might
+ // represent a digit.
+ //
+ fprintf(outputFile, "\\%03o", c);
+ }
+ break;
+ }
+ }
+ fprintf(outputFile, "\";\n");
+ }
+};
+
+int main(int argc, char** argv)
+{
+ App app;
+ app.parseOptions(argc, argv);
+ app.processInputFile();
+ return 0;
+}