b300eee9d2af0ee2ae0f64ef895b79f1439bcc83
[quine] / qqlib.c
1 /* -*-c-*-
2 *
3 * $Id: qqlib.c,v 1.2 2000/08/01 17:41:39 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.2 2000/08/01 17:41:39 mdw
38 * Remove unnecessary <ctype.h> header.
39 *
40 * Revision 1.1.1.1 1999/04/28 19:58:07 mdw
41 * Initial revision
42 *
43 */
44
45 /*----- Header files ------------------------------------------------------*/
46
47 /* --- ANSI headers --- */
48
49 #include <errno.h>
50 #include <stdio.h>
51 #include <stdlib.h>
52 #include <string.h>
53
54 /* --- POSIX headers --- */
55
56 #ifndef QUINE_PORTABLE
57 # include <sys/types.h>
58 # include <sys/fcntl.h>
59 # include <sys/stat.h>
60 # include <unistd.h>
61 #endif
62
63 /* --- Local headers --- */
64
65 #include "quine.h"
66
67 /*----- Macro hacking -----------------------------------------------------*/
68
69 #ifdef QUINE_PORTABLE
70 # define PORT(x) x
71 # define NPORT(x)
72 #else
73 # define PORT(x)
74 # define NPORT(x) x
75 #endif
76
77 /*----- Main code ---------------------------------------------------------*/
78
79 /* --- @qq_xlate@ --- *
80 *
81 * Arguments: @FILE *fp@ = output file handle
82 * @const char *p@ = pointer to encoded text
83 *
84 * Returns: ---
85 *
86 * Use: Writes the decoded string to the given file handle.
87 */
88
89 void qq_xlate(FILE *fp, const char *p)
90 {
91 while (*p) {
92 if (*p == '%') {
93 p++;
94 switch (*p) {
95 case 'q': fputc('\"', fp); break;
96 case 'b': fputc('\\', fp); break;
97 case 't': fputc('\t', fp); break;
98 case 'n': fputc('\n', fp); break;
99 case 'a': fputc('\a', fp); break;
100 case 'r': fputc('\r', fp); break;
101 case '0': fputc('\0', fp); break;
102 case '%': fputc('%', fp); break;
103 case 'x': {
104 unsigned char ch = 0;
105 p++;
106 while (*p != '%') {
107 unsigned int x = *p++;
108 x -= '0';
109 if (x >= 10) x -= 'a' - '0' - 10;
110 if (x >= 16) x -= 'A' - 'a';
111 ch = (ch << 4) + x;
112 }
113 fputc(ch, fp);
114 } break;
115 default:
116 p--;
117 break;
118 }
119 } else
120 fputc(*p, fp);
121 p++;
122 }
123 }
124
125 /* --- @qq_file@ --- *
126 *
127 * Arguments: @FILE *fp@ = output file handle
128 * @const char **p@ = pointer to the output array
129 *
130 * Returns: ---
131 *
132 * Use: Writes the contents of a file to the output.
133 */
134
135 void qq_file(FILE *fp, const char **p)
136 {
137 while (*p)
138 qq_xlate(fp, *p++);
139 }
140
141 /* --- @qq_head@ --- *
142 *
143 * Arguments: @FILE *fp@ = output file handle
144 *
145 * Returns: ---
146 *
147 * Use: Writes the head of a `qqout.c' file.
148 */
149
150 void qq_head(FILE *fp)
151 {
152 fputs("/* quine output file */\n\n", fp);
153 }
154
155 /* --- @qq_body@ --- *
156 *
157 * Arguments: @FILE *fp@ = output file handle
158 * @const char ***p@ = pointer to main table
159 *
160 * Returns: ---
161 *
162 * Use: Writes the body table of a `qqout.c' file.
163 */
164
165 void qq_body(FILE *fp, const char ***p)
166 {
167 size_t fno = 0;
168 const char **q;
169
170 fputs("/* --- file contents tables --- */\n\n", fp);
171 while (*p) {
172 q = *p;
173 fprintf(fp, "static const char *qq__c_%lx[] = {\n", (unsigned long)fno);
174 fprintf(fp, "/* Filename: */ \"%s\",\n", *q++);
175 if (strncmp(*q, "%!", 2) == 0)
176 fprintf(fp, "/* Mode: */ \"%s\",\n", *q++);
177 while (*q)
178 fprintf(fp, "\"%s\",\n", *q++);
179 fputs("0\n};\n\n", fp);
180 p++;
181 fno++;
182 }
183 }
184
185 /* --- @qq_tail@ --- *
186 *
187 * Arguments: @FILE *fp@ = output file handle
188 * @const char **qql@ = pointer to qqlib file array
189 * @size_t fno@ = number of files written
190 * @const char *fn@ = name of `qqout.c' file
191 *
192 * Returns: ---
193 *
194 * Use: Writes the head of a `qqout.c' file.
195 */
196
197 void qq_tail(FILE *fp, const char **qql, size_t fno, const char *fn)
198 {
199 const char **p;
200 size_t i;
201
202 /* --- Write out the qqlib array --- */
203
204 fputs("/* --- qqlib code --- */\n\n"
205 "const char *qq_qqlib[] = {\n", fp);
206 for (p = qql; *p; p++)
207 fprintf(fp, "\"%s\",\n", *p);
208 fputs("0\n};\n\n", fp);
209
210 /* --- Build the main array --- */
211
212 fputs("/* --- table of files to output --- */\n\n"
213 "const char **qq_table[] = {\n", fp);
214 for (i = 0; i < fno; i++)
215 fprintf(fp, " qq__c_%lx,\n", (unsigned long)i);
216 fputs(" 0\n};\n\n", fp);
217
218 /* --- Now output the qqlib code --- */
219
220 fputs("#define QQ_OUT\n\n", fp);
221 qq_file(fp, qql);
222
223 /* --- Finally write the code to write everything out --- */
224
225 fprintf(fp,
226 "/* --- quine output routines --- */\n\n"
227 "void qq_dump(FILE *fp) {\n"
228 " qq__dump(fp, qq_table, qq_qqlib, \"%s\", %lu);\n"
229 "}\n"
230 "int qq_create(void) {\n"
231 " return (qq__create(qq_table, qq_qqlib, \"%s\", %lu));\n"
232 "}\n\n",
233 fn, (unsigned long)fno, fn, (unsigned long)fno);
234 }
235
236 /* --- @qq_mkfile@ --- *
237 *
238 * Arguments: @const char *fn@ = pointer to a filename
239 * @int mode@ = file mode to use (only in Unix-specific version)
240 *
241 * Returns: A handle for the created file.
242 *
243 * Use: Creates a file, and leading directories and stuff.
244 */
245
246 #ifdef QUINE_PORTABLE
247 FILE *qq_mkfile(const char *fn)
248 {
249 return (fopen(fn, "w"));
250 }
251 #else
252 FILE *qq_mkfile(const char *fn, int mode)
253 {
254 char buf[FILENAME_MAX];
255 char *p;
256 int fd;
257
258 strcpy(buf, fn);
259 p = strchr(buf, '/');
260 for (;;) {
261 if (!p)
262 break;
263 *p = 0;
264 mkdir(buf, 0777);
265 *p = '/';
266 p = strchr(p + 1, '/');
267 }
268
269 fd = open(fn, O_WRONLY|O_CREAT|O_TRUNC, mode);
270 return (fd >=0 ? fdopen(fd, "w") : 0);
271 }
272 #endif
273
274 #ifdef QQ_OUT
275
276 /* --- @qq__dump@ --- *
277 *
278 * Arguments: @FILE *fp@ = file to dump on
279 * @const char ***p@ = pointer to main table
280 * @const char **qql@ = pointer to qqlib code
281 * @const char *fn@ = name of `qqout.c' file
282 * @size_t fno@ = number of files to be output
283 *
284 * Returns: ---
285 *
286 * Use: Outputs the entire source code to a given file.
287 */
288
289 static void qq__dump(FILE *fp, const char ***p, const char **qql,
290 const char *fn, size_t fno)
291 {
292 const char ***q = p;
293
294 while (*q) {
295 fprintf(fp, "******** %s\n\n", **q);
296 qq_file(fp, *q + 1);
297 q++;
298 }
299 fprintf(fp, "******** %s\n\n", fn);
300 qq_head(fp);
301 qq_body(fp, p);
302 qq_tail(fp, qql, fno, fn);
303 }
304
305 /* --- @qq__create@ --- *
306 *
307 * Arguments: @const char ***p@ = pointer to main table
308 * @const char **qql@ = pointer to qqlib code
309 * @const char *fn@ = name of `qqout.c' file
310 * @size_t fno@ = number of files to be output
311 *
312 * Returns: Zero if all went well, else nonzero.
313 *
314 * Use: Rebuilds the original source distribution.
315 */
316
317 static int qq__create(const char ***p, const char **qql,
318 const char *fn, size_t fno)
319 {
320 FILE *fp;
321 const char ***q = p;
322
323 while (*q) {
324 const char **qq = *q + 1;
325
326 #ifdef QUINE_PORTABLE
327 if (strncmp(*qq, "%!", 2) == 0)
328 qq++;
329 fp = qq_mkfile(*qq);
330 #else
331 if (strncmp(*qq, "%!", 2) == 0) {
332 fp = qq_mkfile(**q, (int)strtol(*qq + 2, 0, 8));
333 qq++;
334 } else
335 fp = qq_mkfile(**q, 0666);
336 #endif
337
338 if (!fp)
339 return (-1);
340 qq_file(fp, qq);
341 if (fclose(fp))
342 return (-1);
343 q++;
344 }
345 fp = qq_mkfile(fn, 0666);
346 if (!fp)
347 return (-1);
348 qq_head(fp);
349 qq_body(fp, p);
350 qq_tail(fp, qql, fno, fn);
351 if (fclose(fp))
352 return (-1);
353 return (0);
354 }
355
356 #endif
357
358 /*----- That's all, folks -------------------------------------------------*/