summaryrefslogtreecommitdiff
path: root/tools/mkbuiltin.c
diff options
context:
space:
mode:
Diffstat (limited to 'tools/mkbuiltin.c')
-rw-r--r--tools/mkbuiltin.c415
1 files changed, 415 insertions, 0 deletions
diff --git a/tools/mkbuiltin.c b/tools/mkbuiltin.c
new file mode 100644
index 0000000..33ac0a4
--- /dev/null
+++ b/tools/mkbuiltin.c
@@ -0,0 +1,415 @@
+/*
+** Copyright (c) 2014 D. Richard Hipp
+**
+** This program is free software; you can redistribute it and/or
+** modify it under the terms of the Simplified BSD License (also
+** known as the "2-Clause License" or "FreeBSD License".)
+**
+** 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.
+**
+** Author contact information:
+** drh@hwaci.com
+** http://www.hwaci.com/drh/
+**
+*******************************************************************************
+**
+** This is a stand-alone utility program that is part of the Fossil build
+** process. This program reads files named on the command line and converts
+** them into ANSI-C static char array variables. Output is written onto
+** standard output.
+**
+** Additionally, the input files may be listed in a separate list file (one
+** resource name per line, optionally enclosed in double quotes). Pass the list
+** via '--reslist <the-list-file>' option. Both lists, from the command line and
+** the list file, are merged; duplicate file names skipped from processing.
+** This option is useful to get around the command line length limitations
+** under some OS, like Windows.
+**
+** The makefiles use this utility to package various resources (large scripts,
+** GIF images, etc) that are separate files in the source code as byte
+** arrays in the resulting executable.
+*/
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+/*
+** Read the entire content of the file named zFilename into memory obtained
+** from malloc() and return a pointer to that memory. Write the size of the
+** file into *pnByte.
+*/
+static unsigned char *read_file(const char *zFilename, int *pnByte){
+ FILE *in;
+ unsigned char *z;
+ int nByte;
+ int got;
+ in = fopen(zFilename, "rb");
+ if( in==0 ){
+ return 0;
+ }
+ fseek(in, 0, SEEK_END);
+ *pnByte = nByte = ftell(in);
+ fseek(in, 0, SEEK_SET);
+ z = malloc( nByte+1 );
+ if( z==0 ){
+ fprintf(stderr, "failed to allocate %d bytes\n", nByte+1);
+ exit(1);
+ }
+ got = fread(z, 1, nByte, in);
+ fclose(in);
+ z[got] = 0;
+ return z;
+}
+
+#ifndef FOSSIL_DEBUG
+/*
+** Try to compress a javascript file by removing unnecessary whitespace.
+**
+** Warning: This compression routine does not necessarily work for any
+** arbitrary Javascript source file. But it should work ok for the
+** well-behaved source files in this project.
+*/
+static void compressJavascript(unsigned char *z, int *pn){
+ int n = *pn;
+ int i, j, k;
+ for(i=j=0; i<n; i++){
+ unsigned char c = z[i];
+ if( c=='/' && (i==0 || z[i-1]!=':')){
+ if( z[i+1]=='*' ){
+ while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; }
+ for(k=i+3; k<n && (z[k]!='/' || z[k-1]!='*'); k++){}
+ i = k;
+ continue;
+ }else if( z[i+1]=='/' ){
+ while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; }
+ for(k=i+2; k<n && z[k]!='\n'; k++){}
+ i = k-1;
+ continue;
+ }
+ }
+ if( c=='\n' ){
+ if( j==0 ) continue;
+ while( j>0 && isspace(z[j-1]) ) j--;
+ z[j++] = '\n';
+ while( i+1<n && isspace(z[i+1]) ) i++;
+ continue;
+ }
+ z[j++] = c;
+ }
+ z[j] = 0;
+ *pn = j;
+}
+#endif /* FOSSIL_DEBUG */
+
+/*
+** There is an instance of the following for each file translated.
+*/
+typedef struct Resource Resource;
+struct Resource {
+ char *zName;
+ int nByte;
+ int idx;
+};
+
+typedef struct ResourceList ResourceList;
+struct ResourceList {
+ Resource *aRes;
+ int nRes;
+ char *buf;
+ long bufsize;
+};
+
+
+Resource *read_reslist(char *name, ResourceList *list){
+#define RESLIST_BUF_MAXBYTES (1L<<20) /* 1 MB of text */
+ FILE *in;
+ long filesize = 0L;
+ long linecount = 0L;
+ char *p = 0;
+ char *pb = 0;
+
+ memset(list, 0, sizeof(*list));
+
+ if( (in = fopen(name, "rb"))==0 ){
+ return list->aRes;
+ }
+ fseek(in, 0L, SEEK_END);
+ filesize = ftell(in);
+ rewind(in);
+
+ if( filesize > RESLIST_BUF_MAXBYTES ){
+ fprintf(stderr, "List file [%s] must be smaller than %ld bytes\n", name,
+ RESLIST_BUF_MAXBYTES);
+ return list->aRes;
+ }
+ list->bufsize = filesize;
+ list->buf = (char *)calloc((list->bufsize + 2), sizeof(list->buf[0]));
+ if( list->buf==0 ){
+ fprintf(stderr, "failed to allocated %ld bytes\n", list->bufsize + 1);
+ list->bufsize = 0L;
+ return list->aRes;
+ }
+ filesize = fread(list->buf, sizeof(list->buf[0]),list->bufsize, in);
+ if ( filesize!=list->bufsize ){
+ fprintf(stderr, "failed to read [%s]\n", name);
+ return list->aRes;
+ }
+ fclose(in);
+
+ /*
+ ** append an extra newline (if missing) for a correct line count
+ */
+ if( list->buf[list->bufsize-1]!='\n' ) list->buf[list->bufsize]='\n';
+
+ linecount = 0L;
+ for( p = strchr(list->buf, '\n');
+ p && p <= &list->buf[list->bufsize-1];
+ p = strchr(++p, '\n') ){
+ ++linecount;
+ }
+
+ list->aRes = (Resource *)calloc(linecount+1, sizeof(list->aRes[0]));
+ for( pb = list->buf, p = strchr(pb, '\n');
+ p && p <= &list->buf[list->bufsize-1];
+ pb = ++p, p = strchr(pb, '\n') ){
+
+ char *path = pb;
+ char *pe = p - 1;
+
+ /* strip leading and trailing whitespace */
+ while( path < p && isspace(*path) ) ++path;
+ while( pe > path && isspace(*pe) ){
+ *pe = '\0';
+ --pe;
+ }
+
+ /* strip outer quotes */
+ while( path < p && *path=='\"') ++path;
+ while( pe > path && *pe=='\"' ){
+ *pe = '\0';
+ --pe;
+ }
+ *p = '\0';
+
+ /* skip empty path */
+ if( *path ){
+ list->aRes[list->nRes].zName = path;
+ ++(list->nRes);
+ }
+ }
+ return list->aRes;
+}
+
+void free_reslist(ResourceList *list){
+ if( list ){
+ if( list->buf ) free(list->buf);
+ if( list->aRes) free(list->aRes);
+ memset(list, 0, sizeof(*list));
+ }
+}
+
+/*
+** Compare two Resource objects for sorting purposes. They sort
+** in zName order so that Fossil can search for resources using
+** a binary search.
+*/
+typedef int (*QsortCompareFunc)(const void *, const void*);
+
+static int compareResource(const Resource *a, const Resource *b){
+ return strcmp(a->zName, b->zName);
+}
+
+int remove_duplicates(ResourceList *list){
+ char dupNameAsc[64] = "\255";
+ char dupNameDesc[64] = "";
+ Resource dupResAsc;
+ Resource dupResDesc;
+ Resource *pDupRes;
+ int dupcount = 0;
+ int i;
+
+ if( list->nRes==0 ){
+ return list->nRes;
+ }
+
+ /*
+ ** scan for duplicates and assign their names to a string that would sort to
+ ** the bottom, then re-sort and truncate the duplicates
+ */
+ memset(dupNameAsc, dupNameAsc[0], sizeof(dupNameAsc)-2);
+ memset(dupNameDesc, dupNameDesc[0], sizeof(dupNameDesc)-2);
+ memset(&dupResAsc, 0, sizeof(dupResAsc));
+ dupResAsc.zName = dupNameAsc;
+ memset(&dupResDesc, 0, sizeof(dupResDesc));
+ dupResDesc.zName = dupNameDesc;
+ pDupRes = (compareResource(&dupResAsc, &dupResDesc) > 0
+ ? &dupResAsc : &dupResDesc);
+
+ qsort(list->aRes, list->nRes, sizeof(list->aRes[0]),
+ (QsortCompareFunc)compareResource);
+ for( i=0; i<list->nRes-1 ; ++i){
+ Resource *res = &list->aRes[i];
+
+ while( i<list->nRes-1
+ && compareResource(res, &list->aRes[i+1])==0 ){
+ fprintf(stderr, "Skipped a duplicate file [%s]\n", list->aRes[i+1].zName);
+ memcpy(&list->aRes[i+1], pDupRes, sizeof(list->aRes[0]));
+ ++dupcount;
+
+ ++i;
+ }
+ }
+ if( dupcount == 0){
+ return list->nRes;
+ }
+ qsort(list->aRes, list->nRes, sizeof(list->aRes[0]),
+ (QsortCompareFunc)compareResource);
+ list->nRes -= dupcount;
+ memset(&list->aRes[list->nRes], 0, sizeof(list->aRes[0]));
+
+ return list->nRes;
+}
+
+int main(int argc, char **argv){
+ int i, sz;
+ int j, n;
+ ResourceList resList;
+ Resource *aRes;
+ int nRes;
+ unsigned char *pData;
+ int nErr = 0;
+ int nSkip;
+ int nPrefix = 0;
+#ifndef FOSSIL_DEBUG
+ int nName;
+#endif
+
+ if( argc==1 ){
+ fprintf(stderr, "usage\t:%s "
+ "[--prefix path] [--reslist file] [resource-file1 ...]\n",
+ argv[0]
+ );
+ return 1;
+ }
+ if( argc>3 && strcmp(argv[1],"--prefix")==0 ){
+ nPrefix = (int)strlen(argv[2]);
+ argc -= 2;
+ argv += 2;
+ }
+
+ memset(&resList, 0, sizeof(resList));
+ if( argc>2 && strcmp(argv[1],"--reslist")==0 ){
+ if( read_reslist(argv[2], &resList)==0 ){
+ fprintf(stderr, "Failed to load resource list from [%s]", argv[2]);
+ free_reslist(&resList);
+ return 1;
+ }
+ argc -= 2;
+ argv += 2;
+ }
+
+ if( argc>1 ){
+ aRes = realloc(resList.aRes, (resList.nRes+argc-1)*sizeof(resList.aRes[0]));
+ if( aRes==0 || aRes==resList.aRes ){
+ fprintf(stderr, "realloc failed\n");
+ free_reslist(&resList);
+ return 1;
+ }
+ resList.aRes = aRes;
+
+ for(i=0; i<argc-1; i++){
+ resList.aRes[resList.nRes].zName = argv[i+1];
+ ++resList.nRes;
+ }
+ }
+
+ if( resList.nRes==0 ){
+ fprintf(stderr,"No resource files to process\n");
+ free_reslist(&resList);
+ return 1;
+ }
+ remove_duplicates(&resList);
+
+ nRes = resList.nRes;
+ aRes = resList.aRes;
+ qsort(aRes, nRes, sizeof(aRes[0]), (QsortCompareFunc)compareResource);
+
+ printf("/* Automatically generated code: Do not edit.\n**\n"
+ "** Rerun the \"mkbuiltin.c\" program or rerun the Fossil\n"
+ "** makefile to update this source file.\n"
+ "*/\n");
+ for(i=0; i<nRes; i++){
+ pData = read_file(aRes[i].zName, &sz);
+ if( pData==0 ){
+ fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName);
+ nErr++;
+ continue;
+ }
+
+ /* Skip initial lines beginning with # */
+ nSkip = 0;
+ while( pData[nSkip]=='#' ){
+ while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; }
+ if( pData[nSkip]=='\n' ) nSkip++;
+ }
+
+#ifndef FOSSIL_DEBUG
+ /* Compress javascript source files */
+ nName = (int)strlen(aRes[i].zName);
+ if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0)
+ || (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0)
+ ){
+ int x = sz-nSkip;
+ compressJavascript(pData+nSkip, &x);
+ sz = x + nSkip;
+ }
+#endif
+
+ aRes[i].nByte = sz - nSkip;
+ aRes[i].idx = i;
+ printf("/* Content of file %s */\n", aRes[i].zName);
+ printf("static const unsigned char bidata%d[%d] = {\n ",
+ i, sz+1-nSkip);
+ for(j=nSkip, n=0; j<=sz; j++){
+ printf("%3d", pData[j]);
+ if( j==sz ){
+ printf(" };\n");
+ }else if( n==14 ){
+ printf(",\n ");
+ n = 0;
+ }else{
+ printf(", ");
+ n++;
+ }
+ }
+ free(pData);
+ }
+ printf("typedef struct BuiltinFileTable BuiltinFileTable;\n");
+ printf("struct BuiltinFileTable {\n");
+ printf(" const char *zName;\n");
+ printf(" const unsigned char *pData;\n");
+ printf(" int nByte;\n");
+ printf("};\n");
+ printf("static const BuiltinFileTable aBuiltinFiles[] = {\n");
+ for(i=0; i<nRes; i++){
+ char *z = aRes[i].zName;
+ if( strlen(z)>=nPrefix ) z += nPrefix;
+ while( z[0]=='.' || z[0]=='/' || z[0]=='\\' ){ z++; }
+ aRes[i].zName = z;
+ while( z[0] ){
+ if( z[0]=='\\' ) z[0] = '/';
+ z++;
+ }
+ }
+ qsort(aRes, nRes, sizeof(aRes[0]), (QsortCompareFunc)compareResource);
+ for(i=0; i<nRes; i++){
+ printf(" { \"%s\", bidata%d, %d },\n",
+ aRes[i].zName, aRes[i].idx, aRes[i].nByte);
+ }
+ printf("};\n");
+ free_reslist(&resList);
+ return nErr;
+}