| 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 -------------------------------------------------*/ |