chiark / gitweb /
f2bf0ebd7d7a8ed58d8fb87f6e1ed06e7c1e3559
[quine] / qqlib.c
1 /* -*-c-*-
2  *
3  * $Id$
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 -------------------------------------------------*/