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