diff options
| author | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
| commit | 4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch) | |
| tree | c296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /tools/mkbuiltin.c | |
| download | relic-master.tar.gz | |
Diffstat (limited to 'tools/mkbuiltin.c')
| -rw-r--r-- | tools/mkbuiltin.c | 415 |
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; +} |
