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