Fossil SCM

fossil-scm / tools / mkbuiltin.c
Blame History Raw 416 lines
1
/*
2
** Copyright (c) 2014 D. Richard Hipp
3
**
4
** This program is free software; you can redistribute it and/or
5
** modify it under the terms of the Simplified BSD License (also
6
** known as the "2-Clause License" or "FreeBSD License".)
7
**
8
** This program is distributed in the hope that it will be useful,
9
** but without any warranty; without even the implied warranty of
10
** merchantability or fitness for a particular purpose.
11
**
12
** Author contact information:
13
** [email protected]
14
** http://www.hwaci.com/drh/
15
**
16
*******************************************************************************
17
**
18
** This is a stand-alone utility program that is part of the Fossil build
19
** process. This program reads files named on the command line and converts
20
** them into ANSI-C static char array variables. Output is written onto
21
** standard output.
22
**
23
** Additionally, the input files may be listed in a separate list file (one
24
** resource name per line, optionally enclosed in double quotes). Pass the list
25
** via '--reslist <the-list-file>' option. Both lists, from the command line and
26
** the list file, are merged; duplicate file names skipped from processing.
27
** This option is useful to get around the command line length limitations
28
** under some OS, like Windows.
29
**
30
** The makefiles use this utility to package various resources (large scripts,
31
** GIF images, etc) that are separate files in the source code as byte
32
** arrays in the resulting executable.
33
*/
34
#include <stdio.h>
35
#include <stdlib.h>
36
#include <string.h>
37
#include <ctype.h>
38
39
/*
40
** Read the entire content of the file named zFilename into memory obtained
41
** from malloc() and return a pointer to that memory. Write the size of the
42
** file into *pnByte.
43
*/
44
static unsigned char *read_file(const char *zFilename, int *pnByte){
45
FILE *in;
46
unsigned char *z;
47
int nByte;
48
int got;
49
in = fopen(zFilename, "rb");
50
if( in==0 ){
51
return 0;
52
}
53
fseek(in, 0, SEEK_END);
54
*pnByte = nByte = ftell(in);
55
fseek(in, 0, SEEK_SET);
56
z = malloc( nByte+1 );
57
if( z==0 ){
58
fprintf(stderr, "failed to allocate %d bytes\n", nByte+1);
59
exit(1);
60
}
61
got = fread(z, 1, nByte, in);
62
fclose(in);
63
z[got] = 0;
64
return z;
65
}
66
67
#ifndef FOSSIL_DEBUG
68
/*
69
** Try to compress a javascript file by removing unnecessary whitespace.
70
**
71
** Warning: This compression routine does not necessarily work for any
72
** arbitrary Javascript source file. But it should work ok for the
73
** well-behaved source files in this project.
74
*/
75
static void compressJavascript(unsigned char *z, int *pn){
76
int n = *pn;
77
int i, j, k;
78
for(i=j=0; i<n; i++){
79
unsigned char c = z[i];
80
if( c=='/' && (i==0 || z[i-1]!=':')){
81
if( z[i+1]=='*' ){
82
while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; }
83
for(k=i+3; k<n && (z[k]!='/' || z[k-1]!='*'); k++){}
84
i = k;
85
continue;
86
}else if( z[i+1]=='/' ){
87
while( j>0 && (z[j-1]==' ' || z[j-1]=='\t') ){ j--; }
88
for(k=i+2; k<n && z[k]!='\n'; k++){}
89
i = k-1;
90
continue;
91
}
92
}
93
if( c=='\n' ){
94
if( j==0 ) continue;
95
while( j>0 && isspace(z[j-1]) ) j--;
96
z[j++] = '\n';
97
while( i+1<n && isspace(z[i+1]) ) i++;
98
continue;
99
}
100
z[j++] = c;
101
}
102
z[j] = 0;
103
*pn = j;
104
}
105
#endif /* FOSSIL_DEBUG */
106
107
/*
108
** There is an instance of the following for each file translated.
109
*/
110
typedef struct Resource Resource;
111
struct Resource {
112
char *zName;
113
int nByte;
114
int idx;
115
};
116
117
typedef struct ResourceList ResourceList;
118
struct ResourceList {
119
Resource *aRes;
120
int nRes;
121
char *buf;
122
long bufsize;
123
};
124
125
126
Resource *read_reslist(char *name, ResourceList *list){
127
#define RESLIST_BUF_MAXBYTES (1L<<20) /* 1 MB of text */
128
FILE *in;
129
long filesize = 0L;
130
long linecount = 0L;
131
char *p = 0;
132
char *pb = 0;
133
134
memset(list, 0, sizeof(*list));
135
136
if( (in = fopen(name, "rb"))==0 ){
137
return list->aRes;
138
}
139
fseek(in, 0L, SEEK_END);
140
filesize = ftell(in);
141
rewind(in);
142
143
if( filesize > RESLIST_BUF_MAXBYTES ){
144
fprintf(stderr, "List file [%s] must be smaller than %ld bytes\n", name,
145
RESLIST_BUF_MAXBYTES);
146
return list->aRes;
147
}
148
list->bufsize = filesize;
149
list->buf = (char *)calloc((list->bufsize + 2), sizeof(list->buf[0]));
150
if( list->buf==0 ){
151
fprintf(stderr, "failed to allocated %ld bytes\n", list->bufsize + 1);
152
list->bufsize = 0L;
153
return list->aRes;
154
}
155
filesize = fread(list->buf, sizeof(list->buf[0]),list->bufsize, in);
156
if ( filesize!=list->bufsize ){
157
fprintf(stderr, "failed to read [%s]\n", name);
158
return list->aRes;
159
}
160
fclose(in);
161
162
/*
163
** append an extra newline (if missing) for a correct line count
164
*/
165
if( list->buf[list->bufsize-1]!='\n' ) list->buf[list->bufsize]='\n';
166
167
linecount = 0L;
168
for( p = strchr(list->buf, '\n');
169
p && p <= &list->buf[list->bufsize-1];
170
p = strchr(++p, '\n') ){
171
++linecount;
172
}
173
174
list->aRes = (Resource *)calloc(linecount+1, sizeof(list->aRes[0]));
175
for( pb = list->buf, p = strchr(pb, '\n');
176
p && p <= &list->buf[list->bufsize-1];
177
pb = ++p, p = strchr(pb, '\n') ){
178
179
char *path = pb;
180
char *pe = p - 1;
181
182
/* strip leading and trailing whitespace */
183
while( path < p && isspace(*path) ) ++path;
184
while( pe > path && isspace(*pe) ){
185
*pe = '\0';
186
--pe;
187
}
188
189
/* strip outer quotes */
190
while( path < p && *path=='\"') ++path;
191
while( pe > path && *pe=='\"' ){
192
*pe = '\0';
193
--pe;
194
}
195
*p = '\0';
196
197
/* skip empty path */
198
if( *path ){
199
list->aRes[list->nRes].zName = path;
200
++(list->nRes);
201
}
202
}
203
return list->aRes;
204
}
205
206
void free_reslist(ResourceList *list){
207
if( list ){
208
if( list->buf ) free(list->buf);
209
if( list->aRes) free(list->aRes);
210
memset(list, 0, sizeof(*list));
211
}
212
}
213
214
/*
215
** Compare two Resource objects for sorting purposes. They sort
216
** in zName order so that Fossil can search for resources using
217
** a binary search.
218
*/
219
typedef int (*QsortCompareFunc)(const void *, const void*);
220
221
static int compareResource(const Resource *a, const Resource *b){
222
return strcmp(a->zName, b->zName);
223
}
224
225
int remove_duplicates(ResourceList *list){
226
char dupNameAsc[64] = "\255";
227
char dupNameDesc[64] = "";
228
Resource dupResAsc;
229
Resource dupResDesc;
230
Resource *pDupRes;
231
int dupcount = 0;
232
int i;
233
234
if( list->nRes==0 ){
235
return list->nRes;
236
}
237
238
/*
239
** scan for duplicates and assign their names to a string that would sort to
240
** the bottom, then re-sort and truncate the duplicates
241
*/
242
memset(dupNameAsc, dupNameAsc[0], sizeof(dupNameAsc)-2);
243
memset(dupNameDesc, dupNameDesc[0], sizeof(dupNameDesc)-2);
244
memset(&dupResAsc, 0, sizeof(dupResAsc));
245
dupResAsc.zName = dupNameAsc;
246
memset(&dupResDesc, 0, sizeof(dupResDesc));
247
dupResDesc.zName = dupNameDesc;
248
pDupRes = (compareResource(&dupResAsc, &dupResDesc) > 0
249
? &dupResAsc : &dupResDesc);
250
251
qsort(list->aRes, list->nRes, sizeof(list->aRes[0]),
252
(QsortCompareFunc)compareResource);
253
for( i=0; i<list->nRes-1 ; ++i){
254
Resource *res = &list->aRes[i];
255
256
while( i<list->nRes-1
257
&& compareResource(res, &list->aRes[i+1])==0 ){
258
fprintf(stderr, "Skipped a duplicate file [%s]\n", list->aRes[i+1].zName);
259
memcpy(&list->aRes[i+1], pDupRes, sizeof(list->aRes[0]));
260
++dupcount;
261
262
++i;
263
}
264
}
265
if( dupcount == 0){
266
return list->nRes;
267
}
268
qsort(list->aRes, list->nRes, sizeof(list->aRes[0]),
269
(QsortCompareFunc)compareResource);
270
list->nRes -= dupcount;
271
memset(&list->aRes[list->nRes], 0, sizeof(list->aRes[0]));
272
273
return list->nRes;
274
}
275
276
int main(int argc, char **argv){
277
int i, sz;
278
int j, n;
279
ResourceList resList;
280
Resource *aRes;
281
int nRes;
282
unsigned char *pData;
283
int nErr = 0;
284
int nSkip;
285
int nPrefix = 0;
286
#ifndef FOSSIL_DEBUG
287
int nName;
288
#endif
289
290
if( argc==1 ){
291
fprintf(stderr, "usage\t:%s "
292
"[--prefix path] [--reslist file] [resource-file1 ...]\n",
293
argv[0]
294
);
295
return 1;
296
}
297
if( argc>3 && strcmp(argv[1],"--prefix")==0 ){
298
nPrefix = (int)strlen(argv[2]);
299
argc -= 2;
300
argv += 2;
301
}
302
303
memset(&resList, 0, sizeof(resList));
304
if( argc>2 && strcmp(argv[1],"--reslist")==0 ){
305
if( read_reslist(argv[2], &resList)==0 ){
306
fprintf(stderr, "Failed to load resource list from [%s]", argv[2]);
307
free_reslist(&resList);
308
return 1;
309
}
310
argc -= 2;
311
argv += 2;
312
}
313
314
if( argc>1 ){
315
aRes = realloc(resList.aRes, (resList.nRes+argc-1)*sizeof(resList.aRes[0]));
316
if( aRes==0 || aRes==resList.aRes ){
317
fprintf(stderr, "realloc failed\n");
318
free_reslist(&resList);
319
return 1;
320
}
321
resList.aRes = aRes;
322
323
for(i=0; i<argc-1; i++){
324
resList.aRes[resList.nRes].zName = argv[i+1];
325
++resList.nRes;
326
}
327
}
328
329
if( resList.nRes==0 ){
330
fprintf(stderr,"No resource files to process\n");
331
free_reslist(&resList);
332
return 1;
333
}
334
remove_duplicates(&resList);
335
336
nRes = resList.nRes;
337
aRes = resList.aRes;
338
qsort(aRes, nRes, sizeof(aRes[0]), (QsortCompareFunc)compareResource);
339
340
printf("/* Automatically generated code: Do not edit.\n**\n"
341
"** Rerun the \"mkbuiltin.c\" program or rerun the Fossil\n"
342
"** makefile to update this source file.\n"
343
"*/\n");
344
for(i=0; i<nRes; i++){
345
pData = read_file(aRes[i].zName, &sz);
346
if( pData==0 ){
347
fprintf(stderr, "Cannot open file [%s]\n", aRes[i].zName);
348
nErr++;
349
continue;
350
}
351
352
/* Skip initial lines beginning with # */
353
nSkip = 0;
354
while( pData[nSkip]=='#' ){
355
while( pData[nSkip]!=0 && pData[nSkip]!='\n' ){ nSkip++; }
356
if( pData[nSkip]=='\n' ) nSkip++;
357
}
358
359
#ifndef FOSSIL_DEBUG
360
/* Compress javascript source files */
361
nName = (int)strlen(aRes[i].zName);
362
if( (nName>3 && strcmp(&aRes[i].zName[nName-3],".js")==0)
363
|| (nName>7 && strcmp(&aRes[i].zName[nName-7], "/js.txt")==0)
364
){
365
int x = sz-nSkip;
366
compressJavascript(pData+nSkip, &x);
367
sz = x + nSkip;
368
}
369
#endif
370
371
aRes[i].nByte = sz - nSkip;
372
aRes[i].idx = i;
373
printf("/* Content of file %s */\n", aRes[i].zName);
374
printf("static const unsigned char bidata%d[%d] = {\n ",
375
i, sz+1-nSkip);
376
for(j=nSkip, n=0; j<=sz; j++){
377
printf("%3d", pData[j]);
378
if( j==sz ){
379
printf(" };\n");
380
}else if( n==14 ){
381
printf(",\n ");
382
n = 0;
383
}else{
384
printf(", ");
385
n++;
386
}
387
}
388
free(pData);
389
}
390
printf("typedef struct BuiltinFileTable BuiltinFileTable;\n");
391
printf("struct BuiltinFileTable {\n");
392
printf(" const char *zName;\n");
393
printf(" const unsigned char *pData;\n");
394
printf(" int nByte;\n");
395
printf("};\n");
396
printf("static const BuiltinFileTable aBuiltinFiles[] = {\n");
397
for(i=0; i<nRes; i++){
398
char *z = aRes[i].zName;
399
if( strlen(z)>=nPrefix ) z += nPrefix;
400
while( z[0]=='.' || z[0]=='/' || z[0]=='\\' ){ z++; }
401
aRes[i].zName = z;
402
while( z[0] ){
403
if( z[0]=='\\' ) z[0] = '/';
404
z++;
405
}
406
}
407
qsort(aRes, nRes, sizeof(aRes[0]), (QsortCompareFunc)compareResource);
408
for(i=0; i<nRes; i++){
409
printf(" { \"%s\", bidata%d, %d },\n",
410
aRes[i].zName, aRes[i].idx, aRes[i].nByte);
411
}
412
printf("};\n");
413
free_reslist(&resList);
414
return nErr;
415
}
416

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button