Track gratuitous change to mdwopt interface.
[quine] / qqlib.c
1 /* -*-c-*-
2 *
3 * $Id: qqlib.c,v 1.1 1999/04/28 19:58:07 mdw Exp $
4 *
5 * Useful library of tools for quines
6 *
7 * (c) 1999 Mark Wooding
8 */
9
10 /*----- Licensing notice --------------------------------------------------*
11 *
12 * This file is part of Quine
13 *
14 * Quine is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * Quine is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with Quine; if not, write to the Free Software Foundation,
26 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 *
28 * However, when this source text is embedded in a file by the Quine
29 * program, you may do anything you like with the resulting text;
30 * distribution and modification are not limited by the General Public
31 * License.
32 */
33
34 /*----- Revision history --------------------------------------------------*
35 *
36 * $Log: qqlib.c,v $
37 * Revision 1.1 1999/04/28 19:58:07 mdw
38 * Initial revision
39 *
40 */
41
42 /*----- Header files ------------------------------------------------------*/
43
44 /* --- ANSI headers --- */
45
46 #include <ctype.h>
47 #include <errno.h>
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <string.h>
51
52 /* --- POSIX headers --- */
53
54 #ifndef QUINE_PORTABLE
55 # include <sys/types.h>
56 # include <sys/fcntl.h>
57 # include <sys/stat.h>
58 # include <unistd.h>
59 #endif
60
61 /* --- Local headers --- */
62
63 #include "quine.h"
64
65 /*----- Macro hacking -----------------------------------------------------*/
66
67 #ifdef QUINE_PORTABLE
68 # define PORT(x) x
69 # define NPORT(x)
70 #else
71 # define PORT(x)
72 # define NPORT(x) x
73 #endif
74
75 /*----- Main code ---------------------------------------------------------*/
76
77 /* --- @qq_xlate@ --- *
78 *
79 * Arguments: @FILE *fp@ = output file handle
80 * @const char *p@ = pointer to encoded text
81 *
82 * Returns: ---
83 *
84 * Use: Writes the decoded string to the given file handle.
85 */
86
87 void qq_xlate(FILE *fp, const char *p)
88 {
89 while (*p) {
90 if (*p == '%') {
91 p++;
92 switch (*p) {
93 case 'q': fputc('\"', fp); break;
94 case 'b': fputc('\\', fp); break;
95 case 't': fputc('\t', fp); break;
96 case 'n': fputc('\n', fp); break;
97 case 'a': fputc('\a', fp); break;
98 case 'r': fputc('\r', fp); break;
99 case '0': fputc('\0', fp); break;
100 case '%': fputc('%', fp); break;
101 case 'x': {
102 unsigned char ch = 0;
103 p++;
104 while (*p != '%') {
105 unsigned int x = *p++;
106 x -= '0';
107 if (x >= 10) x -= 'a' - '0' - 10;
108 if (x >= 16) x -= 'A' - 'a';
109 ch = (ch << 4) + x;
110 }
111 fputc(ch, fp);
112 } break;
113 default:
114 p--;
115 break;
116 }
117 } else
118 fputc(*p, fp);
119 p++;
120 }
121 }
122
123 /* --- @qq_file@ --- *
124 *
125 * Arguments: @FILE *fp@ = output file handle
126 * @const char **p@ = pointer to the output array
127 *
128 * Returns: ---
129 *
130 * Use: Writes the contents of a file to the output.
131 */
132
133 void qq_file(FILE *fp, const char **p)
134 {
135 while (*p)
136 qq_xlate(fp, *p++);
137 }
138
139 /* --- @qq_head@ --- *
140 *
141 * Arguments: @FILE *fp@ = output file handle
142 *
143 * Returns: ---
144 *
145 * Use: Writes the head of a `qqout.c' file.
146 */
147
148 void qq_head(FILE *fp)
149 {
150 fputs("/* quine output file */\n\n", fp);
151 }
152
153 /* --- @qq_body@ --- *
154 *
155 * Arguments: @FILE *fp@ = output file handle
156 * @const char ***p@ = pointer to main table
157 *
158 * Returns: ---
159 *
160 * Use: Writes the body table of a `qqout.c' file.
161 */
162
163 void qq_body(FILE *fp, const char ***p)
164 {
165 size_t fno = 0;
166 const char **q;
167
168 fputs("/* --- file contents tables --- */\n\n", fp);
169 while (*p) {
170 q = *p;
171 fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno);
172 fprintf(fp, "/* Filename: */ \"%s\",\n", *q++);
173 if (strncmp(*q, "%!", 2) == 0)
174 fprintf(fp, "/* Mode: */ \"%s\",\n", *q++);
175 while (*q)
176 fprintf(fp, "\"%s\",\n", *q++);
177 fputs("0\n};\n\n", fp);
178 p++;
179 fno++;
180 }
181 }
182
183 /* --- @qq_tail@ --- *
184 *
185 * Arguments: @FILE *fp@ = output file handle
186 * @const char **qql@ = pointer to qqlib file array
187 * @size_t fno@ = number of files written
188 * @const char *fn@ = name of `qqout.c' file
189 *
190 * Returns: ---
191 *
192 * Use: Writes the head of a `qqout.c' file.
193 */
194
195 void qq_tail(FILE *fp, const char **qql, size_t fno, const char *fn)
196 {
197 const char **p;
198 size_t i;
199
200 /* --- Write out the qqlib array --- */
201
202 fputs("/* --- qqlib code --- */\n\n"
203 "const char *qq_qqlib[] = {\n", fp);
204 for (p = qql; *p; p++)
205 fprintf(fp, "\"%s\",\n", *p);
206 fputs("0\n};\n\n", fp);
207
208 /* --- Build the main array --- */
209
210 fputs("/* --- table of files to output --- */\n\n"
211 "const char **qq_table[] = {\n", fp);
212 for (i = 0; i < fno; i++)
213 fprintf(fp, " qq__c_%lx,\n", (unsigned long)i);
214 fputs(" 0\n};\n\n", fp);
215
216 /* --- Now output the qqlib code --- */
217
218 fputs("#define QQ_OUT\n\n", fp);
219 qq_file(fp, qql);
220
221 /* --- Finally write the code to write everything out --- */
222
223 fprintf(fp,
224 "/* --- quine output routines --- */\n\n"
225 "void qq_dump(FILE *fp) {\n"
226 " qq__dump(fp, qq_table, qq_qqlib, \"%s\", %lu);\n"
227 "}\n"
228 "int qq_create(void) {\n"
229 " return (qq__create(qq_table, qq_qqlib, \"%s\", %lu));\n"
230 "}\n\n",
231 fn, (unsigned long)fno, fn, (unsigned long)fno);
232 }
233
234 /* --- @qq_mkfile@ --- *
235 *
236 * Arguments: @const char *fn@ = pointer to a filename
237 * @int mode@ = file mode to use (only in Unix-specific version)
238 *
239 * Returns: A handle for the created file.
240 *
241 * Use: Creates a file, and leading directories and stuff.
242 */
243
244 #ifdef QUINE_PORTABLE
245 FILE *qq_mkfile(const char *fn)
246 {
247 return (fopen(fn, "w"));
248 }
249 #else
250 FILE *qq_mkfile(const char *fn, int mode)
251 {
252 char buf[FILENAME_MAX];
253 char *p;
254 int fd;
255
256 strcpy(buf, fn);
257 p = strchr(buf, '/');
258 for (;;) {
259 if (!p)
260 break;
261 *p = 0;
262 mkdir(buf, 0777);
263 *p = '/';
264 p = strchr(p + 1, '/');
265 }
266
267 fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode);
268 return (fd >=0 ? fdopen(fd, "w") : 0);
269 }
270 #endif
271
272 #ifdef QQ_OUT
273
274 /* --- @qq__dump@ --- *
275 *
276 * Arguments: @FILE *fp@ = file to dump on
277 * @const char ***p@ = pointer to main table
278 * @const char **qql@ = pointer to qqlib code
279 * @const char *fn@ = name of `qqout.c' file
280 * @size_t fno@ = number of files to be output
281 *
282 * Returns: ---
283 *
284 * Use: Outputs the entire source code to a given file.
285 */
286
287 static void qq__dump(FILE *fp, const char ***p, const char **qql,
288 const char *fn, size_t fno)
289 {
290 const char ***q = p;
291
292 while (*q) {
293 fprintf(fp, "******** %s\n\n", **q);
294 qq_file(fp, *q + 1);
295 q++;
296 }
297 fprintf(fp, "******** %s\n\n", fn);
298 qq_head(fp);
299 qq_body(fp, p);
300 qq_tail(fp, qql, fno, fn);
301 }
302
303 /* --- @qq__create@ --- *
304 *
305 * Arguments: @const char ***p@ = pointer to main table
306 * @const char **qql@ = pointer to qqlib code
307 * @const char *fn@ = name of `qqout.c' file
308 * @size_t fno@ = number of files to be output
309 *
310 * Returns: Zero if all went well, else nonzero.
311 *
312 * Use: Rebuilds the original source distribution.
313 */
314
315 static int qq__create(const char ***p, const char **qql,
316 const char *fn, size_t fno)
317 {
318 FILE *fp;
319 const char ***q = p;
320
321 while (*q) {
322 const char **qq = *q + 1;
323
324 #ifdef QUINE_PORTABLE
325 if (strncmp(*qq, "%!", 2) == 0)
326 qq++;
327 fp = qq_mkfile(*qq);
328 #else
329 if (strncmp(*qq, "%!", 2) == 0) {
330 fp = qq_mkfile(**q, (int)strtol(*qq + 2, 0, 8));
331 qq++;
332 } else
333 fp = qq_mkfile(**q, 0666);
334 #endif
335
336 if (!fp)
337 return (-1);
338 qq_file(fp, qq);
339 if (fclose(fp))
340 return (-1);
341 q++;
342 }
343 fp = qq_mkfile(fn, 0666);
344 if (!fp)
345 return (-1);
346 qq_head(fp);
347 qq_body(fp, p);
348 qq_tail(fp, qql, fno, fn);
349 if (fclose(fp))
350 return (-1);
351 return (0);
352 }
353
354 #endif
355
356 /*----- That's all, folks -------------------------------------------------*/