1/*
2 * Ficl softcore generator.
3 * Generates both uncompressed and Lempel-Ziv compressed versions.
4 * Strips blank lines, strips full-line comments, collapses whitespace.
5 * Chops, blends, dices, makes julienne fries.
6 *
7 * Contributed by Larry Hastings, larry@hastings.org
8 */
9#include <ctype.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <time.h>
13
14#include "ficl.h"
15
16#ifndef SOFTCORE_OUT
17#define	SOFTCORE_OUT	"softcore.c"
18#endif
19
20extern size_t
21lz4_compress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n);
22
23void
24fprintDataAsHex(FILE *f, unsigned char *data, int length)
25{
26	int i;
27	while (length) {
28		fprintf(f, "\t");
29		for (i = 0; (i < 8) && length; i++) {
30			char buf[16];
31			/*
32			 * if you don't do this little stuff, you get ugly
33			 * sign-extended 0xFFFFFF6b crap.
34			 */
35			sprintf(buf, "%08x", (unsigned int)*data++);
36			fprintf(f, "0x%s, ", buf + 6);
37			length--;
38		}
39		fprintf(f, "\n");
40	}
41}
42
43void
44fprintDataAsQuotedString(FILE *f, char *data)
45{
46	int lineIsBlank = 1; /* true */
47
48	while (*data) {
49		if (*data == '\n') {
50			if (!lineIsBlank)
51				fprintf(f, "\\n\"\n");
52			lineIsBlank = 1; /* true */
53		} else {
54			if (lineIsBlank) {
55				fputc('\t', f);
56				fputc('"', f);
57				lineIsBlank = 0; /* false */
58			}
59
60			if (*data == '"')
61				fprintf(f, "\\\"");
62			else if (*data == '\\')
63				fprintf(f, "\\\\");
64			else
65				fputc(*data, f);
66		}
67		data++;
68	}
69	if (!lineIsBlank)
70		fprintf(f, "\"");
71}
72
73int
74main(int argc, char *argv[])
75{
76	char *uncompressed = (char *)malloc(128 * 1024);
77	unsigned char *compressed = malloc(128 * 1024);
78	char *trace = uncompressed;
79	int i;
80	size_t compressedSize = 128 * 1024;
81	size_t uncompressedSize;
82	char *src, *dst;
83	FILE *f;
84	time_t currentTimeT;
85	struct tm *currentTime;
86	char cleverTime[32];
87
88	time(&currentTimeT);
89	currentTime = localtime(&currentTimeT);
90	strftime(cleverTime, sizeof (cleverTime),
91	    "%Y/%m/%d %H:%M:%S", currentTime);
92
93	*trace++ = ' ';
94
95	for (i = 1; i < argc; i++) {
96		int size;
97		/*
98		 * This ensures there's always whitespace space between files.
99		 * It *also* ensures that src[-1] is always safe in comment
100		 * detection code below.  (Any leading whitespace will be
101		 * thrown away in a later pass.)
102		 * --lch
103		 */
104		*trace++ = ' ';
105
106		f = fopen(argv[i], "rb");
107		fseek(f, 0, SEEK_END);
108		size = ftell(f);
109		fseek(f, 0, SEEK_SET);
110		fread(trace, 1, size, f);
111		fclose(f);
112		trace += size;
113	}
114	*trace = 0;
115
116#define	IS_EOL(x)	((*x == '\n') || (*x == '\r'))
117#define	IS_EOL_COMMENT(x)	\
118	(((x[0] == '\\') && isspace(x[1])) || \
119	((x[0] == '/') && (x[1] == '/') && isspace(x[2])))
120#define	IS_BLOCK_COMMENT(x)	\
121	((x[0] == '(') && isspace(x[1]) && isspace(x[-1]))
122
123	src = dst = uncompressed;
124	while (*src) {
125		/* ignore leading whitespace, or entirely blank lines */
126		while (isspace(*src))
127			src++;
128		/* if the line is commented out */
129		if (IS_EOL_COMMENT(src)) {
130			/* throw away this entire line */
131			while (*src && !IS_EOL(src))
132				src++;
133			continue;
134		}
135		/*
136		 * This is where we'd throw away mid-line comments, but
137		 * that's simply unsafe.  Things like
138		 *	start-prefixes
139		 *	: \ postpone \ ;
140		 *	: ( postpone ( ;
141		 * get broken that way.
142		 * --lch
143		 */
144		while (*src && !IS_EOL(src)) {
145			*dst++ = *src++;
146		}
147
148		/* strip trailing whitespace */
149		dst--;
150		while (isspace(*dst))
151			dst--;
152		dst++;
153
154		/* and end the line */
155		*dst++ = '\n';
156	}
157
158	*dst = 0;
159
160	/*
161	 * now make a second pass to collapse all contiguous whitespace
162	 * to a single space.
163	 */
164	src = dst = uncompressed;
165	while (*src) {
166		*dst++ = *src;
167		if (!isspace(*src))
168			src++;
169		else {
170			while (isspace(*src))
171				src++;
172		}
173	}
174	*dst = 0;
175
176	f = fopen(SOFTCORE_OUT, "wt");
177	if (f == NULL) {
178		printf("couldn't open " SOFTCORE_OUT
179		    " for writing!  giving up.\n");
180		exit(-1);
181	}
182
183	fprintf(f,
184"/*\n"
185"** Ficl softcore\n"
186"** both uncompressed and LZ4 compressed versions.\n"
187"**\n"
188"** Generated %s\n"
189"**/\n"
190"\n"
191"#include \"ficl.h\"\n"
192"\n"
193"\n", cleverTime);
194
195	uncompressedSize = dst - uncompressed;
196	compressedSize = lz4_compress(uncompressed, compressed,
197	    uncompressedSize, compressedSize, 0);
198
199	fprintf(f, "static size_t ficlSoftcoreUncompressedSize = %d; "
200	    "/* not including trailing null */\n", uncompressedSize);
201	fprintf(f, "\n");
202	fprintf(f, "#if !FICL_WANT_LZ4_SOFTCORE\n");
203	fprintf(f, "\n");
204	fprintf(f, "static char ficlSoftcoreUncompressed[] =\n");
205	fprintDataAsQuotedString(f, uncompressed);
206	fprintf(f, ";\n");
207	fprintf(f, "\n");
208	fprintf(f, "#else /* !FICL_WANT_LZ4_SOFTCORE */\n");
209	fprintf(f, "\n");
210	fprintf(f, "extern int lz4_decompress(void *, void *, size_t, "
211	    "size_t, int);\n\n");
212	fprintf(f, "static unsigned char ficlSoftcoreCompressed[%d] = "
213	    "{\n", compressedSize);
214	fprintDataAsHex(f, compressed, compressedSize);
215	fprintf(f, "\t};\n");
216	fprintf(f, "\n");
217	fprintf(f, "#endif /* !FICL_WANT_LZ4_SOFTCORE */\n");
218	fprintf(f,
219"\n"
220"\n"
221"void ficlSystemCompileSoftCore(ficlSystem *system)\n"
222"{\n"
223"    ficlVm *vm = system->vmList;\n"
224"    int returnValue;\n"
225"    ficlCell oldSourceID = vm->sourceId;\n"
226"    ficlString s;\n"
227"#if FICL_WANT_LZ4_SOFTCORE\n"
228"    char *ficlSoftcoreUncompressed = malloc(ficlSoftcoreUncompressedSize+1);\n"
229"    returnValue = lz4_decompress(ficlSoftcoreCompressed, "
230"ficlSoftcoreUncompressed, sizeof(ficlSoftcoreCompressed), "
231"ficlSoftcoreUncompressedSize+1, 0);\n"
232"    FICL_VM_ASSERT(vm, returnValue == 0);\n"
233"#endif /* FICL_WANT_LZ4_SOFTCORE */\n"
234"    vm->sourceId.i = -1;\n"
235"    FICL_STRING_SET_POINTER(s, (char *)(ficlSoftcoreUncompressed));\n"
236"    FICL_STRING_SET_LENGTH(s, ficlSoftcoreUncompressedSize);\n"
237"    returnValue = ficlVmExecuteString(vm, s);\n"
238"    vm->sourceId = oldSourceID;\n"
239"#if FICL_WANT_LZ4_SOFTCORE\n"
240"    free(ficlSoftcoreUncompressed);\n"
241"#endif /* FICL_WANT_LZ4_SOFTCORE */\n"
242"    FICL_VM_ASSERT(vm, returnValue != FICL_VM_STATUS_ERROR_EXIT);\n"
243"    return;\n"
244"}\n\n"
245"/* end-of-file */\n");
246	free(uncompressed);
247	free(compressed);
248	return (0);
249}
250