chiark / gitweb /
The Great Upheaval -- step 1.
[quine] / qqlib.c
CommitLineData
267c6003 1/* -*-c-*-
2 *
7f6303ef 3 * $Id$
267c6003 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 $
ab2f100c 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
267c6003 41 * Initial revision
42 *
43 */
44
45/*----- Header files ------------------------------------------------------*/
46
47/* --- ANSI headers --- */
48
267c6003 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
89void 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
135void 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
150void 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
165void 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
197void 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
247FILE *qq_mkfile(const char *fn)
248{
249 return (fopen(fn, "w"));
250}
251#else
252FILE *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
289static 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
317static 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 -------------------------------------------------*/