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 
20 extern size_t
21 lz4_compress(void *s_start, void *d_start, size_t s_len, size_t d_len, int n);
22 
23 void
fprintDataAsHex(FILE * f,unsigned char * data,int length)24 fprintDataAsHex(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 
43 void
fprintDataAsQuotedString(FILE * f,char * data)44 fprintDataAsQuotedString(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 
73 int
main(int argc,char * argv[])74 main(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