/* main.c * * Copyright (C) 2005-2006 by Andy Goth . * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the License, or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. */ #include #include #include #include #include "qbism.h" /* Allocate memory or die. */ void* xmalloc(size_t bytes) { void* result = malloc(bytes); if (result == NULL) { perror("malloc"); exit(EXIT_FAILURE); } return result; } /* Reallocate memory or die. */ void* xrealloc(void* buf, size_t bytes) { void* result = realloc(buf, bytes); if (result == NULL) { perror("realloc"); exit(EXIT_FAILURE); } return result; } /* Numeric list of options. */ typedef enum { OPT_INQBE, OPT_INQBM, OPT_OUTQBE, OPT_OUTQBM, OPT_OUTIMG, OPT_IMGFMT, OPT_OVERSAMP, OPT_SIZE, OPT_STEPS, OPT_REGS, OPT_RENDER, OPT_COUNT } opt_t; /* Textual list of options. */ static char* opt_names[] = { "-inqbe", "-inqbm", "-outqbe", "-outqbm", "-outimg", "-imgfmt", "-oversamp", "-size", "-steps", "-regs", "-render" }; /* Open a file. */ static FILE* my_open(char* name, char* mode) { FILE* chan; if (strcmp(name, "-") == 0) { if (strcmp(mode, "r") == 0) { chan = stdin; } else { chan = stdout; } } else { chan = fopen(name, mode); if (chan == NULL) { perror(name); exit(EXIT_FAILURE); } } return chan; } /* Close an opened file. */ static void my_close(FILE* chan) { if (chan != stdin && chan != stdout) { if (fclose(chan) == EOF) { perror("fclose"); exit(EXIT_FAILURE); } } } /* Repeal gravity. */ int main(int argc, char** argv) { FILE* chan; int i, j; algo_t algo = {NULL, 36, 12}; image_t img; char* opts[OPT_COUNT] = {NULL}; /* Defaults. */ opts[OPT_OVERSAMP] = "1"; opts[OPT_SIZE] = "640x480"; opts[OPT_IMGFMT] = "miff8"; opts[OPT_REGS] = "6"; opts[OPT_STEPS] = "36"; opts[OPT_RENDER] = "yes"; /* Check if help is requested. */ if (argc > 1 && ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "-help") == 0) || (strcmp(argv[1], "--help") == 0))) { printf("Usage: qbism [OPTION]...\n" " -inqbe FILE run the algorithm in the QBE FILE\n" " -inqbm FILE run the algorithm in the QBM FILE\n" " -outqbe FILE save the algorithm in QBE format to FILE\n" " -outqbm FILE save the algorithm in QBM format to FILE\n" " -outimg FILE render the image in MIFF format to FILE\n" " -imgfmt FORMAT_NAME can be 'miff8', 'miff16', or 'bmp'\n" " -oversamp NUM sample NUMxNUM points per pixel\n" " -size WIDTHxHEIGHT render at WIDTH by HEIGHT pixels\n" " -regs NUM for random algorithms, use NUM registers\n" " -steps NUM for random algorithms, use NUM steps\n" " -render 'yes'/'no' if 'yes', render image\n" " -h, -help display this message\n" " -v, -version print the version information\n" "If neither -inqbe nor -inqbm are specified, a random algorithm is\n" "generated and used to render the image. Otherwise the algorithm\n" "stored in the QBE or QBM file is used.\n" "\n" "Specify - as FILE to use stdin or stdout. By default, the output\n" "image is written to stdout; pipe through display or convert (from\n" "ImageMagick) for the desired effect.\n" "\n" "Report features to Andy Goth .\n"); return EXIT_SUCCESS; } /* Check if the version number is requested. */ if (argc > 1 && ((strcmp(argv[1], "-v") == 0) || (strcmp(argv[1], "-version") == 0) || (strcmp(argv[1], "--version") == 0))) { printf("qbism 0.4\n"); return EXIT_SUCCESS; } /* Check that every option has a parameter. */ if ((argc - 1) % 2 != 0) { fprintf(stderr, "Option \"%s\" has no parameter\n", argv[argc - 1]); return EXIT_FAILURE; } /* Get all parameters. */ for (i = 1; i < argc; i += 2) { for (j = 0; j < OPT_COUNT; ++j) { if (strcmp(argv[i], opt_names[j]) == 0) { opts[j] = argv[i + 1]; break; } } if (j == OPT_COUNT) { fprintf(stderr, "Unknown option \"%s\"\n", argv[i]); return EXIT_FAILURE; } } /* Check for disallowed combinations. */ if (opts[OPT_INQBE] != NULL && opts[OPT_INQBM] != NULL) { fprintf(stderr, "Cannot specify both -inqbe and -inqbm\n"); return EXIT_FAILURE; } /* Interpret options. */ if (strcmp(opts[OPT_IMGFMT], "miff8") == 0) { img.format = IF_MIFF8; } else if (strcmp(opts[OPT_IMGFMT], "miff16") == 0) { img.format = IF_MIFF16; } else if (strcmp(opts[OPT_IMGFMT], "bmp") == 0) { img.format = IF_BMP; } else { fprintf(stderr, "Unsupported image format \"%s\"\n", opts[OPT_IMGFMT]); return EXIT_FAILURE; } if (sscanf(opts[OPT_OVERSAMP], "%d", &img.oversample) != 1 || img.oversample <= 0) { fprintf(stderr, "Bad oversample value \"%s\"\n", opts[OPT_OVERSAMP]); return EXIT_FAILURE; } if (sscanf(opts[OPT_SIZE], "%dx%d", &img.width, &img.height) != 2 || img.width <= 0 || img.height <= 0) { fprintf(stderr, "Bad size \"%s\"\n", opts[OPT_SIZE]); return EXIT_FAILURE; } if (sscanf(opts[OPT_STEPS], "%d", &algo.seq_len) != 1 || algo.seq_len < 0) { fprintf(stderr, "Bad step count \"%s\"\n", opts[OPT_STEPS]); return EXIT_FAILURE; } if (sscanf(opts[OPT_REGS], "%d", &algo.num_regs) != 1 || algo.num_regs < 1) { fprintf(stderr, "Bad register count \"%s\"\n", opts[OPT_REGS]); return EXIT_FAILURE; } if (strcmp(opts[OPT_RENDER], "yes") != 0 && strcmp(opts[OPT_RENDER], "no") != 0 && strcmp(opts[OPT_RENDER], "force") != 0) { fprintf(stderr, "Bad render flag \"%s\"\n", opts[OPT_RENDER]); return EXIT_FAILURE; } /* Read/generate an algorithm. */ if (opts[OPT_INQBE] != NULL) { chan = my_open(opts[OPT_INQBE], "r"); qbe_read(&algo, chan); my_close(chan); } else if (opts[OPT_INQBM] != NULL) { chan = my_open(opts[OPT_INQBM], "r"); qbm_read(&algo, chan); my_close(chan); } else { /* Generate a random transformation sequence. */ srandom(time(NULL)); algo.seq = xmalloc(sizeof(*algo.seq) * algo.seq_len); for (i = 0; i < algo.seq_len; ++i) { algo.seq[i].opcode = random() % XOP_COUNT; algo.seq[i].source = random() % algo.num_regs; algo.seq[i].control = random() % algo.num_regs; algo.seq[i].dest = random() % algo.num_regs; } } /* If so requested, write out the algorithm. */ if (opts[OPT_OUTQBE] != NULL) { chan = my_open(opts[OPT_OUTQBE], "w"); /* Don't write binary QBE data to a terminal. */ if (ttyname(fileno(chan)) != NULL) { fprintf(stderr, "Cowardly refusing to write QBE to a terminal.\n"); my_close(chan); return EXIT_FAILURE; } else { qbe_write(&algo, chan); my_close(chan); } } if (opts[OPT_OUTQBM] != NULL) { chan = my_open(opts[OPT_OUTQBM], "w"); qbm_write(&algo, chan); my_close(chan); } /* If writing QBE/QBM files to stdout and -outimg is left at the default, * then don't render an image. */ if (opts[OPT_OUTIMG] == NULL && ((opts[OPT_OUTQBE] != NULL && strcmp(opts[OPT_OUTQBE], "-") == 0) || (opts[OPT_OUTQBM] != NULL && strcmp(opts[OPT_OUTQBM], "-") == 0))) { opts[OPT_RENDER] = "no"; } /* If -outimg wasn't given, default to writing to stdout. */ if (opts[OPT_OUTIMG] == NULL) { opts[OPT_OUTIMG] = "-"; } /* Render! */ if (strcmp(opts[OPT_RENDER], "no") != 0) { chan = my_open(opts[OPT_OUTIMG], "w"); /* Don't write binary image data to a terminal. */ if (strcmp(opts[OPT_RENDER], "yes") == 0 && ttyname(fileno(chan)) != NULL) { /* Only warn if not also writing QBE/QBM files. */ if (opts[OPT_OUTQBE] == NULL && opts[OPT_OUTQBM] == NULL) { fprintf(stderr, "Cowardly refusing to render to a terminal; " "use \"-render force\" to override.\n"); my_close(chan); return EXIT_FAILURE; } } else { /* Do it. */ img.algo = &algo; img.target = chan; qbism_render_image(&img); my_close(chan); } } return EXIT_SUCCESS; } /* vim: set ts=4 sts=4 sw=4 tw=80 et: */