Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
3584 sourcerer 1
/*
2
 * Copyright 2006 Rob Kendrick 
3
 *
4
 * This file is part of NetSurf, http://www.netsurf-browser.org/
5
 *
6
 * NetSurf is free software; you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation; version 2 of the License.
9
 *
10
 * NetSurf is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program.  If not, see .
17
 */
18
 
19
/* To build a stand-alone command-line utility to create and dismantal
20
 * these theme files, build this thusly:
21
 *
22
 * gcc -I../ -DNSTHEME -o themetool container.c
23
 *
24
 * [needs a c99 compiler]
25
 *
26
 * then for instance to create a theme file called mythemefilename
27
 * ./themetool --verbose --create -n"My theme name" mythemefilename\
28
 * --author "Myname" /path/to/directory/containing/theme/files/
29
 */
30
 
31
/** \file
32
 * Container format handling for themes etc. */
33
 
34
#include 
35
#include 
36
#include 
37
#include 
38
#include 
39
#include 
40
#include 
41
#include "utils/config.h"
42
#include "utils/container.h"
43
#include "utils/log.h"
44
#include "utils/messages.h"
45
#include "utils/utils.h"
46
 
47
#ifdef WITH_MMAP
48
#include 
49
#endif
50
 
51
#ifdef NSTHEME
52
bool verbose_log = true;
53
#endif
54
 
55
struct container_dirent {
56
	unsigned char	filename[64];
57
	u_int32_t	startoffset;
58
	u_int32_t	len;
59
	u_int32_t	flags1;
60
	u_int32_t	flags2;
61
};
62
 
63
struct container_header {
64
	u_int32_t	magic;	/* 0x4d54534e little endian */
65
	u_int32_t	parser;
66
	unsigned char	name[32];
67
	unsigned char	author[64];
68
	u_int32_t	diroffset;
69
};
70
 
71
struct container_ctx {
72
	FILE		*fh;
73
	bool		creating;
74
	bool		processed;
75
	struct container_header	header;
76
	unsigned int	entries;
77
	unsigned char 	*data;
78
	struct container_dirent *directory;
79
};
80
 
81
inline static size_t container_filelen(FILE *fd)
82
{
83
	long o = ftell(fd);
84
	long a;
85
 
86
	fseek(fd, 0, SEEK_END);
87
	a = ftell(fd);
88
	fseek(fd, o, SEEK_SET);
89
	if (a == -1) {
90
		LOG(("could not ascertain size of file in theme container; omitting"));
91
		return 0;
92
	}
93
	if (((unsigned long) a) > SIZE_MAX) {
94
		LOG(("overlarge file in theme container; possible truncation"));
95
		return SIZE_MAX;
96
	}
97
	return (size_t) a;
98
}
99
 
100
static void container_add_to_dir(struct container_ctx *ctx,
101
					const unsigned char *entryname,
102
					const u_int32_t offset,
103
					const u_int32_t length)
104
{
105
	struct container_dirent *temp;
106
	temp = realloc(ctx->directory, ctx->entries *
107
			sizeof(struct container_dirent));
108
	if (temp == NULL) {
109
		printf("error adding entry for %s to theme container\n", entryname);
110
		return;
111
	}
112
	ctx->entries += 1;
113
	ctx->directory = temp;
114
 
115
	strncpy((char *)ctx->directory[ctx->entries - 1].filename,
116
				(char *)entryname, sizeof(ctx->directory[
117
				ctx->entries - 1].filename));
118
	ctx->directory[ctx->entries - 1].startoffset = offset;
119
	ctx->directory[ctx->entries - 1].len = length;
120
	ctx->directory[ctx->entries - 1].flags1 = 0;
121
	ctx->directory[ctx->entries - 1].flags2 = 0;
122
}
123
 
124
struct container_ctx *container_open(const char *filename)
125
{
126
	size_t val;
127
	struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1);
128
 
129
	ctx->fh = fopen(filename, "rb");
130
 
131
	if (ctx->fh == NULL) {
132
		free(ctx);
133
		return NULL;
134
	}
135
 
136
	/* we don't actually load any of the data (including directory)
137
	 * until we need to, such that _get_name and _get_author are as quick
138
	 * as possible.  When we have, this gets set to true.
139
	 */
140
	ctx->processed = false;
141
 
142
	val = fread(&ctx->header.magic, 4, 1, ctx->fh);
143
	if (val == 0)
144
		LOG(("empty read magic"));
145
	ctx->header.magic = ntohl(ctx->header.magic);
146
 
147
	val = fread(&ctx->header.parser, 4, 1, ctx->fh);
148
	if (val == 0)
149
		LOG(("empty read parser"));
150
	ctx->header.parser = ntohl(ctx->header.parser);
151
 
152
	val = fread(ctx->header.name, 32, 1, ctx->fh);
153
	if (val == 0)
154
		LOG(("empty read name"));
155
	val = fread(ctx->header.author, 64, 1, ctx->fh);
156
	if (val == 0)
157
		LOG(("empty read author"));
158
 
159
	val = fread(&ctx->header.diroffset, 4, 1, ctx->fh);
160
	if (val == 0)
161
		LOG(("empty read diroffset"));
162
	ctx->header.diroffset = ntohl(ctx->header.diroffset);
163
 
164
	if (ctx->header.magic != 0x4e53544d || ctx->header.parser != 3) {
165
		fclose(ctx->fh);
166
		free(ctx);
167
		return NULL;
168
	}
169
 
170
	return ctx;
171
}
172
 
173
static void container_process(struct container_ctx *ctx)
174
{
175
	size_t val;
176
	unsigned char filename[64];
177
	u_int32_t start, len, flags1, flags2;
178
 
179
#ifdef WITH_MMAP
180
	ctx->data = mmap(NULL, ctx->header.diroffset, PROT_READ, MAP_PRIVATE,
181
				fileno(ctx->fh), 0);
182
#else
183
	ctx->data = malloc(ctx->header.diroffset);
184
	fseek(ctx->fh, 0, SEEK_SET);
185
	val = fread(ctx->data, ctx->header.diroffset, 1, ctx->fh);
186
	if (val == 0)
187
		LOG(("empty read diroffset"));
188
#endif
189
	fseek(ctx->fh, ctx->header.diroffset, SEEK_SET);
190
	/* now work through the directory structure taking it apart into
191
	 * our structure */
192
#define BEREAD(x) do { val = fread(&(x), 4, 1, ctx->fh); if (val == 0)\
193
		LOG(("empty read"));(x) = ntohl((x)); } while (0)
194
	do {
195
		val = fread(filename, 64, 1, ctx->fh);
196
		if (val == 0)
197
			LOG(("empty read filename"));
198
		BEREAD(start);
199
		BEREAD(len);
200
		BEREAD(flags1);
201
		BEREAD(flags2);
202
		if (filename[0] != '\0')
203
			container_add_to_dir(ctx, filename, start, len);
204
	} while (filename[0] != '\0');
205
#undef BEREAD
206
	ctx->processed = true;
207
}
208
 
209
static const struct container_dirent *container_lookup(
210
					struct container_ctx *ctx,
211
					const unsigned char *entryname)
212
{
213
	unsigned int i;
214
 
215
	for (i = 1; i <= ctx->entries; i++) {
216
		struct container_dirent *e = ctx->directory + i - 1;
217
		if (strcmp((char *)e->filename, (char *)entryname) == 0)
218
			return e;
219
	}
220
 
221
	return NULL;
222
}
223
 
224
const unsigned char *container_get(struct container_ctx *ctx,
225
					const unsigned char *entryname,
226
					u_int32_t *size)
227
{
228
	const struct container_dirent *e;
229
 
230
	if (ctx->processed == false)
231
		container_process(ctx);
232
 
233
	e = container_lookup(ctx, entryname);
234
 
235
	if (e == NULL)
236
		return NULL;
237
 
238
	*size = e->len;
239
 
240
	return &ctx->data[e->startoffset];
241
}
242
 
243
const unsigned char *container_iterate(struct container_ctx *ctx, int *state)
244
{
245
	struct container_dirent *e;
246
	unsigned char *r;
247
 
248
	if (ctx->processed == false)
249
		container_process(ctx);
250
 
251
	e = ctx->directory + *state;
252
 
253
	r = e->filename;
254
 
255
	if (r == NULL || r[0] == '\0')
256
		r = NULL;
257
 
258
	*state += 1;
259
 
260
	return r;
261
}
262
 
263
const unsigned char *container_get_name(struct container_ctx *ctx)
264
{
265
	return ctx->header.name;
266
}
267
 
268
const unsigned char *container_get_author(struct container_ctx *ctx)
269
{
270
	return ctx->header.author;
271
}
272
 
273
 
274
static void container_write_dir(struct container_ctx *ctx)
275
{
276
	size_t val;
277
	unsigned int i;
278
	u_int32_t tmp;
279
#define BEWRITE(x) do {tmp = htonl((x)); val = fwrite(&tmp, 4, 1, ctx->fh);\
280
		if (val == 0) LOG(("empty write")); } while(0)
281
	for (i = 1; i <= ctx->entries; i++) {
282
		struct container_dirent *e = ctx->directory + i - 1;
283
		val = fwrite(e->filename, 64, 1, ctx->fh);
284
		if (val == 0)
285
			LOG(("empty write filename"));
286
		BEWRITE(e->startoffset);
287
		BEWRITE(e->len);
288
		BEWRITE(e->flags1);
289
		BEWRITE(e->flags2);
290
	}
291
#undef BEWRITE
292
	/* empty entry signifies end of directory */
293
	tmp = 0;
294
	val = fwrite(&tmp, 4, 8, ctx->fh);
295
	if (val == 0)
296
		LOG(("empty write end"));
297
}
298
 
299
struct container_ctx *container_create(const char *filename,
300
					const unsigned char *name,
301
					const unsigned char *author)
302
{
303
	size_t val;
304
	struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1);
305
 
306
	ctx->fh = fopen(filename, "wb");
307
 
308
	if (ctx->fh == NULL) {
309
		free(ctx);
310
		return NULL;
311
	}
312
 
313
	ctx->creating = true;
314
	ctx->entries = 0;
315
	ctx->directory = NULL;
316
	ctx->header.parser = htonl(3);
317
	strncpy((char *)ctx->header.name, (char *)name, 32);
318
	strncpy((char *)ctx->header.author, (char *)author, 64);
319
 
320
	val = fwrite("NSTM", 4, 1, ctx->fh);
321
	if (val == 0)
322
		LOG(("empty write NSTM"));
323
	val = fwrite(&ctx->header.parser, 4, 1, ctx->fh);
324
	if (val == 0)
325
		LOG(("empty write parser"));
326
	val = fwrite(ctx->header.name, 32, 1, ctx->fh);
327
	if (val == 0)
328
		LOG(("empty write name"));
329
	val = fwrite(ctx->header.author, 64, 1, ctx->fh);
330
	if (val == 0)
331
		LOG(("empty write author"));
332
 
333
	ctx->header.diroffset = 108;
334
 
335
	/* skip over the directory offset for now, and fill it in later.
336
	 * we don't know where it'll be yet!
337
	 */
338
 
339
	fseek(ctx->fh, 108, SEEK_SET);
340
 
341
	return ctx;
342
}
343
 
344
void container_add(struct container_ctx *ctx, const unsigned char *entryname,
345
					const unsigned char *data,
346
					const u_int32_t datalen)
347
{
348
	size_t val;
349
	container_add_to_dir(ctx, entryname, ftell(ctx->fh), datalen);
350
	val = fwrite(data, datalen, 1, ctx->fh);
351
	if (val == 0)
352
		LOG(("empty write add file"));
353
}
354
 
355
void container_close(struct container_ctx *ctx)
356
{
357
	if (ctx->creating == true) {
358
		size_t flen, nflen, val;
359
 
360
		/* discover where the directory's going to go. */
361
		flen = container_filelen(ctx->fh);
362
		flen = (flen + 3) & (~3); /* round up to nearest 4 bytes */
363
 
364
		/* write this location to the header */
365
		fseek(ctx->fh, 104, SEEK_SET);
366
		nflen = htonl(flen);
367
		val = fwrite(&nflen, 4, 1, ctx->fh);
368
		if (val == 0)
369
			LOG(("empty write directory location"));
370
 
371
		/* seek to where the directory will be, and write it */
372
		fseek(ctx->fh, flen, SEEK_SET);
373
		container_write_dir(ctx);
374
 
375
	} else if (ctx->processed) {
376
#ifdef WITH_MMAP
377
		munmap(ctx->data, ctx->header.diroffset);
378
#else
379
		free(ctx->data);
380
#endif
381
	}
382
 
383
	fclose(ctx->fh);
384
	free(ctx);
385
}
386
 
387
#ifdef WITH_THEME_INSTALL
388
 
389
/**
390
 * install theme from container
391
 * \param themefile a file containing the containerized theme
392
 * \param dirbasename a directory basename including trailing path sep; the
393
 * full path of the theme is then a subdirectory of that
394
 * caller owns reference to returned string, NULL for error
395
 */
396
 
397
char *container_extract_theme(const char *themefile, const char *dirbasename)
398
{
399
	struct stat statbuf;
400
	struct container_ctx *cctx;
401
	FILE *fh;
402
	size_t val;
403
	const unsigned char *e, *d;
404
	char *themename, *dirname;
405
	char path[PATH_MAX];
406
	int state = 0;
407
	unsigned int i;
408
	u_int32_t flen;
409
 
410
	cctx = container_open(themefile);
411
	if (cctx == NULL) {
412
		warn_user("FileOpenError", themefile);
413
		return NULL;
414
	}
415
	themename = strdup((const char *)container_get_name(cctx));
416
	if (themename == NULL) {
417
		warn_user("NoMemory", 0);
418
		container_close(cctx);
419
		return NULL;
420
	}
421
	LOG(("theme name: %s", themename));
422
	LOG(("theme author: %s", container_get_author(cctx)));
423
 
424
	dirname = malloc(strlen(dirbasename) + strlen(themename) + 2);
425
	if (dirname == NULL) {
426
		warn_user(messages_get("NoMemory"), 0);
427
		free(themename);
428
		container_close(cctx);
429
		return NULL;
430
	}
431
	strcpy(dirname, dirbasename);
432
	strcat(dirname, themename);
433
	if (stat(dirname, &statbuf) != -1) {
434
		warn_user("DirectoryError", dirname);
435
		container_close(cctx);
436
		free(dirname);
437
		free(themename);
438
		return NULL;
439
	}
440
	mkdir(dirname, S_IRWXU);
441
 
442
	for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries;
443
			e = container_iterate(cctx, &state), i++) {
444
		LOG(("extracting %s", e));
445
		snprintf(path, PATH_MAX, "%s/%s", dirname, e);
446
		fh = fopen(path, "wb");
447
		if (fh == NULL) {
448
			warn_user("FileOpenError", (char *)e);
449
		} else {
450
			d = container_get(cctx, e, &flen);
451
			val = fwrite(d, flen, 1, fh);
452
			if (val == 0)
453
				LOG(("empty write"));
454
			fclose(fh);
455
		}
456
	}
457
	LOG(("theme container unpacked"));
458
	container_close(cctx);
459
	free(dirname);
460
	return themename;
461
 
462
}
463
 
464
#endif
465
 
466
#ifdef TEST_RIG
467
int main(int argc, char *argv[])
468
{
469
	struct container_ctx *ctx = container_create("test.theme", "Test theme",
470
				"Rob Kendrick");
471
	u_int32_t size;
472
	int state = 0;
473
	char *n;
474
 
475
	container_add(ctx, "CHEESE", "This is a test of some cheese.", sizeof("This is a test of some cheese."));
476
	container_add(ctx, "FOO", "This is a test of some cheese.", sizeof("This is a test of some cheese."));
477
 
478
	container_close(ctx);
479
 
480
	ctx = container_open("test.theme");
481
 
482
	printf("Theme name: %s\n", container_get_name(ctx));
483
	printf("Theme author: %s\n", container_get_author(ctx));
484
 
485
	printf("Test string: %s\n", container_get(ctx, "CHEESE", &size));
486
	printf("Length of text: %d\n", size);
487
 
488
	while ( (n = container_iterate(ctx, &state)) ) {
489
		printf("%s\n", n);
490
	}
491
 
492
	container_close(ctx);
493
 
494
	exit(0);
495
}
496
#endif
497
 
498
#ifdef NSTHEME
499
	/* code to implement a simple container creator/extractor */
500
#include 
501
#include 
502
#include 
503
#include 
504
 
505
static bool verbose = false;
506
 
507
static void show_usage(const char *argv0)
508
{
509
	fprintf(stderr, "%s [options]  \n", argv0);
510
	fprintf(stderr, " --help       This text\n");
511
	fprintf(stderr, " --create     Create theme file from directory\n");
512
	fprintf(stderr, " --extract    Extract theme file into directory\n");
513
	fprintf(stderr, " --name x     Set theme's name when creating\n");
514
	fprintf(stderr, " --author x   Set theme's author when creating\n");
515
	fprintf(stderr, " --verbose    Print progress information\n");
516
	fprintf(stderr, "\nOne and only one of --create or --extract must be specified.\n");
517
}
518
 
519
static void extract_theme(const char *themefile, const char *dirname)
520
{
521
	struct stat statbuf;
522
	struct container_ctx *cctx;
523
	FILE *fh;
524
	const unsigned char *e, *d;
525
	char path[PATH_MAX];
526
	int i, state = 0;
527
	u_int32_t flen;
528
 
529
 
530
	if (stat(dirname, &statbuf) != -1) {
531
		fprintf(stderr, "error: directory '%s' already exists.\n",
532
			dirname);
533
		exit(1);
534
	}
535
 
536
	mkdir(dirname, S_IRWXU);
537
 
538
	cctx = container_open(themefile);
539
	if (cctx == NULL) {
540
		fprintf(stderr, "error: unable to open theme file '%s'\n",
541
			themefile);
542
		exit(1);
543
	}
544
 
545
	if (verbose == true) {
546
		printf("theme name: %s\n", container_get_name(cctx));
547
		printf("theme author: %s\n", container_get_author(cctx));
548
	}
549
 
550
	for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries;
551
			e = container_iterate(cctx, &state), i++) {
552
		if (verbose == true)
553
			printf("extracting %s\n", e);
554
		snprintf(path, PATH_MAX, "%s/%s", dirname, e);
555
		fh = fopen(path, "wb");
556
		if (fh == NULL) {
557
			perror("warning: unable to open file for output");
558
		} else {
559
			d = container_get(cctx, e, &flen);
560
			fwrite(d, flen, 1, fh);
561
			fclose(fh);
562
		}
563
	}
564
 
565
	container_close(cctx);
566
 
567
}
568
 
569
static void create_theme(const char *themefile, const char *dirname,
570
				const unsigned char *name,
571
				const unsigned char *author)
572
{
573
	DIR *dir = opendir(dirname);
574
	FILE *fh;
575
	struct dirent *e;
576
	struct stat statbuf;
577
	struct container_ctx *cctx;
578
	unsigned char *data;
579
	char path[PATH_MAX];
580
	size_t flen;
581
	int t;
582
 
583
	if (dir == NULL) {
584
		perror("error: unable to open directory");
585
		exit(1);
586
	}
587
 
588
	cctx = container_create(themefile, name, author);
589
 
590
	errno = 0;	/* to distinguish between end of dir and err */
591
 
592
	while ((e = readdir(dir)) != NULL) {
593
		if (strcmp(e->d_name, ".") != 0 &&
594
			strcmp(e->d_name, "..") != 0) {
595
			/* not the metadirs, so we want to process this. */
596
			if (verbose == true)
597
				printf("adding %s\n", e->d_name);
598
			if (strlen(e->d_name) > 63) {
599
				fprintf(stderr,
600
			"warning: name truncated to length 63.\n");
601
			}
602
 
603
			snprintf(path, PATH_MAX, "%s/%s", dirname, e->d_name);
604
 
605
			stat(path, &statbuf);
606
			if (S_ISDIR(statbuf.st_mode)) {
607
				fprintf(stderr,
608
					"warning: skipping directory '%s'\n",
609
					e->d_name);
610
				continue;
611
			}
612
 
613
			fh = fopen(path, "rb");
614
			if (fh == NULL) {
615
				fprintf(stderr,
616
					"warning: unable to open, skipping.");
617
			} else {
618
				flen = statbuf.st_size;
619
				data = malloc(flen);
620
				t = fread(data, flen, 1, fh);
621
				fclose(fh);
622
				container_add(cctx, (unsigned char *)e->d_name,
623
						data, flen);
624
				free(data);
625
			}
626
		}
627
		errno = 0;
628
	}
629
 
630
	if (errno != 0) {
631
		perror("error: couldn't enumerate directory");
632
		closedir(dir);
633
		container_close(cctx);
634
		exit(1);
635
	}
636
 
637
	closedir(dir);
638
	container_close(cctx);
639
}
640
 
641
int main(int argc, char *argv[])
642
{
643
	static struct option l_opts[] = {
644
		{ "help", 0, 0, 'h' },
645
		{ "create", 0, 0, 'c' },
646
		{ "extract", 0, 0, 'x' },
647
		{ "name", 1, 0, 'n' },
648
		{ "author", 1, 0, 'a' },
649
		{ "verbose", 0, 0, 'v' },
650
 
651
		{ NULL, 0, 0, 0 }
652
	};
653
 
654
	static char *s_opts = "hcxn:a:v";
655
	int optch, optidx;
656
	bool creating = false, extracting = false;
657
	unsigned char name[32] = { '\0' }, author[64] = { '\0' };
658
	char *themefile, *dirname;
659
 
660
	while ((optch = getopt_long(argc, argv, s_opts, l_opts, &optidx)) != -1)
661
		switch (optch) {
662
		case 'h':
663
			show_usage(argv[0]);
664
			exit(0);
665
			break;
666
		case 'c':
667
			creating = true;
668
			break;
669
		case 'x':
670
			extracting = true;
671
			break;
672
		case 'n':
673
			strncpy((char *)name, optarg, 31);
674
			if (strlen(optarg) > 32)
675
				fprintf(stderr, "warning: theme name truncated to 32 characters.\n");
676
			break;
677
		case 'a':
678
			strncpy((char *)author, optarg, 63);
679
			if (strlen(optarg) > 64)
680
				fprintf(stderr, "warning: theme author truncated to 64 characters.\n");
681
			break;
682
		case 'v':
683
			verbose = true;
684
			break;
685
		default:
686
			show_usage(argv[0]);
687
			exit(1);
688
			break;
689
		}
690
 
691
	if (creating == extracting) {
692
		show_usage(argv[0]);
693
		exit(1);
694
	}
695
 
696
	if ((argc - optind) < 2) {
697
		show_usage(argv[0]);
698
		exit(1);
699
	}
700
 
701
	if (creating == true &&
702
		(strlen((char *)name) == 0 || strlen((char *)author) == 0)) {
703
		fprintf(stderr, "No theme name and/or author specified.\n");
704
		show_usage(argv[0]);
705
		exit(1);
706
	}
707
 
708
	themefile = strdup(argv[optind]);
709
	dirname = strdup(argv[optind + 1]);
710
 
711
	if (verbose == true)
712
		printf("%s '%s' %s directory '%s'\n",
713
			creating ? "creating" : "extracting", themefile,
714
			creating ? "from" : "to", dirname);
715
 
716
	if (creating) {
717
		if (verbose == true)
718
			printf("name = %s, author = %s\n", name, author);
719
		create_theme(themefile, dirname, name, author);
720
	} else {
721
		extract_theme(themefile, dirname);
722
	}
723
 
724
	return 0;
725
}
726
#endif