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