Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * Copyright 2006 Rob Kendrick <rjek@rjek.com>
  3.  *
  4.  * This file is part of NetSurf, http://www.netsurf-browser.org/
  5.  *
  6.  * NetSurf is free software; you can redistribute it and/or modify
  7.  * it under the terms of the GNU General Public License as published by
  8.  * the Free Software Foundation; version 2 of the License.
  9.  *
  10.  * NetSurf is distributed in the hope that it will be useful,
  11.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13.  * GNU General Public License for more details.
  14.  *
  15.  * You should have received a copy of the GNU General Public License
  16.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  17.  */
  18.  
  19. /* To build a stand-alone command-line utility to create and dismantal
  20.  * these theme files, build this thusly:
  21.  *
  22.  * gcc -I../ -DNSTHEME -o themetool container.c
  23.  *
  24.  * [needs a c99 compiler]
  25.  *
  26.  * then for instance to create a theme file called mythemefilename
  27.  * ./themetool --verbose --create -n"My theme name" mythemefilename\
  28.  * --author "Myname" /path/to/directory/containing/theme/files/
  29.  */
  30.  
  31. /** \file
  32.  * Container format handling for themes etc. */
  33.  
  34. #include <limits.h>
  35. #include <stdio.h>
  36. #include <stdlib.h>
  37. #include <string.h>
  38. #include <stdbool.h>
  39. #include <sys/stat.h>
  40. #include <arpa/inet.h>
  41. #include "utils/config.h"
  42. #include "utils/container.h"
  43. #include "utils/log.h"
  44. #include "utils/messages.h"
  45. #include "utils/utils.h"
  46.  
  47. #ifdef WITH_MMAP
  48. #include <sys/mman.h>
  49. #endif
  50.  
  51. #ifdef NSTHEME
  52. bool verbose_log = true;
  53. #endif
  54.  
  55. struct container_dirent {
  56.         unsigned char   filename[64];
  57.         u_int32_t       startoffset;
  58.         u_int32_t       len;
  59.         u_int32_t       flags1;
  60.         u_int32_t       flags2;
  61. };
  62.  
  63. struct container_header {
  64.         u_int32_t       magic;  /* 0x4d54534e little endian */
  65.         u_int32_t       parser;
  66.         unsigned char   name[32];
  67.         unsigned char   author[64];
  68.         u_int32_t       diroffset;
  69. };
  70.  
  71. struct container_ctx {
  72.         FILE            *fh;
  73.         bool            creating;
  74.         bool            processed;
  75.         struct container_header header;
  76.         unsigned int    entries;
  77.         unsigned char   *data;
  78.         struct container_dirent *directory;
  79. };
  80.  
  81. inline static size_t container_filelen(FILE *fd)
  82. {
  83.         long o = ftell(fd);
  84.         long a;
  85.  
  86.         fseek(fd, 0, SEEK_END);
  87.         a = ftell(fd);
  88.         fseek(fd, o, SEEK_SET);
  89.         if (a == -1) {
  90.                 LOG(("could not ascertain size of file in theme container; omitting"));
  91.                 return 0;
  92.         }
  93.         if (((unsigned long) a) > SIZE_MAX) {
  94.                 LOG(("overlarge file in theme container; possible truncation"));
  95.                 return SIZE_MAX;
  96.         }
  97.         return (size_t) a;
  98. }
  99.  
  100. static void container_add_to_dir(struct container_ctx *ctx,
  101.                                         const unsigned char *entryname,
  102.                                         const u_int32_t offset,
  103.                                         const u_int32_t length)
  104. {
  105.         struct container_dirent *temp;
  106.         temp = realloc(ctx->directory, ctx->entries *
  107.                         sizeof(struct container_dirent));
  108.         if (temp == NULL) {
  109.                 printf("error adding entry for %s to theme container\n", entryname);
  110.                 return;
  111.         }
  112.         ctx->entries += 1;
  113.         ctx->directory = temp;
  114.  
  115.         strncpy((char *)ctx->directory[ctx->entries - 1].filename,
  116.                                 (char *)entryname, sizeof(ctx->directory[
  117.                                 ctx->entries - 1].filename));
  118.         ctx->directory[ctx->entries - 1].startoffset = offset;
  119.         ctx->directory[ctx->entries - 1].len = length;
  120.         ctx->directory[ctx->entries - 1].flags1 = 0;
  121.         ctx->directory[ctx->entries - 1].flags2 = 0;
  122. }
  123.  
  124. struct container_ctx *container_open(const char *filename)
  125. {
  126.         size_t val;
  127.         struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1);
  128.  
  129.         ctx->fh = fopen(filename, "rb");
  130.  
  131.         if (ctx->fh == NULL) {
  132.                 free(ctx);
  133.                 return NULL;
  134.         }
  135.  
  136.         /* we don't actually load any of the data (including directory)
  137.          * until we need to, such that _get_name and _get_author are as quick
  138.          * as possible.  When we have, this gets set to true.
  139.          */
  140.         ctx->processed = false;
  141.  
  142.         val = fread(&ctx->header.magic, 4, 1, ctx->fh);
  143.         if (val == 0)
  144.                 LOG(("empty read magic"));
  145.         ctx->header.magic = ntohl(ctx->header.magic);
  146.  
  147.         val = fread(&ctx->header.parser, 4, 1, ctx->fh);
  148.         if (val == 0)
  149.                 LOG(("empty read parser"));    
  150.         ctx->header.parser = ntohl(ctx->header.parser);
  151.  
  152.         val = fread(ctx->header.name, 32, 1, ctx->fh);
  153.         if (val == 0)
  154.                 LOG(("empty read name"));
  155.         val = fread(ctx->header.author, 64, 1, ctx->fh);
  156.         if (val == 0)
  157.                 LOG(("empty read author"));
  158.  
  159.         val = fread(&ctx->header.diroffset, 4, 1, ctx->fh);
  160.         if (val == 0)
  161.                 LOG(("empty read diroffset"));
  162.         ctx->header.diroffset = ntohl(ctx->header.diroffset);
  163.  
  164.         if (ctx->header.magic != 0x4e53544d || ctx->header.parser != 3) {
  165.                 fclose(ctx->fh);
  166.                 free(ctx);
  167.                 return NULL;
  168.         }
  169.  
  170.         return ctx;
  171. }
  172.  
  173. static void container_process(struct container_ctx *ctx)
  174. {
  175.         size_t val;
  176.         unsigned char filename[64];
  177.         u_int32_t start, len, flags1, flags2;
  178.  
  179. #ifdef WITH_MMAP
  180.         ctx->data = mmap(NULL, ctx->header.diroffset, PROT_READ, MAP_PRIVATE,
  181.                                 fileno(ctx->fh), 0);
  182. #else
  183.         ctx->data = malloc(ctx->header.diroffset);
  184.         fseek(ctx->fh, 0, SEEK_SET);
  185.         val = fread(ctx->data, ctx->header.diroffset, 1, ctx->fh);
  186.         if (val == 0)
  187.                 LOG(("empty read diroffset"));
  188. #endif
  189.         fseek(ctx->fh, ctx->header.diroffset, SEEK_SET);
  190.         /* now work through the directory structure taking it apart into
  191.          * our structure */
  192. #define BEREAD(x) do { val = fread(&(x), 4, 1, ctx->fh); if (val == 0)\
  193.                 LOG(("empty read"));(x) = ntohl((x)); } while (0)
  194.         do {
  195.                 val = fread(filename, 64, 1, ctx->fh);
  196.                 if (val == 0)
  197.                         LOG(("empty read filename"));
  198.                 BEREAD(start);
  199.                 BEREAD(len);
  200.                 BEREAD(flags1);
  201.                 BEREAD(flags2);
  202.                 if (filename[0] != '\0')
  203.                         container_add_to_dir(ctx, filename, start, len);
  204.         } while (filename[0] != '\0');
  205. #undef BEREAD
  206.         ctx->processed = true;
  207. }
  208.  
  209. static const struct container_dirent *container_lookup(
  210.                                         struct container_ctx *ctx,
  211.                                         const unsigned char *entryname)
  212. {
  213.         unsigned int i;
  214.  
  215.         for (i = 1; i <= ctx->entries; i++) {
  216.                 struct container_dirent *e = ctx->directory + i - 1;
  217.                 if (strcmp((char *)e->filename, (char *)entryname) == 0)
  218.                         return e;
  219.         }
  220.  
  221.         return NULL;
  222. }
  223.  
  224. const unsigned char *container_get(struct container_ctx *ctx,
  225.                                         const unsigned char *entryname,
  226.                                         u_int32_t *size)
  227. {
  228.         const struct container_dirent *e;
  229.  
  230.         if (ctx->processed == false)
  231.                 container_process(ctx);
  232.  
  233.         e = container_lookup(ctx, entryname);
  234.  
  235.         if (e == NULL)
  236.                 return NULL;
  237.  
  238.         *size = e->len;
  239.  
  240.         return &ctx->data[e->startoffset];
  241. }
  242.  
  243. const unsigned char *container_iterate(struct container_ctx *ctx, int *state)
  244. {
  245.         struct container_dirent *e;
  246.         unsigned char *r;
  247.  
  248.         if (ctx->processed == false)
  249.                 container_process(ctx);
  250.  
  251.         e = ctx->directory + *state;
  252.  
  253.         r = e->filename;
  254.  
  255.         if (r == NULL || r[0] == '\0')
  256.                 r = NULL;
  257.  
  258.         *state += 1;
  259.  
  260.         return r;
  261. }
  262.  
  263. const unsigned char *container_get_name(struct container_ctx *ctx)
  264. {
  265.         return ctx->header.name;
  266. }
  267.  
  268. const unsigned char *container_get_author(struct container_ctx *ctx)
  269. {
  270.         return ctx->header.author;
  271. }
  272.  
  273.  
  274. static void container_write_dir(struct container_ctx *ctx)
  275. {
  276.         size_t val;
  277.         unsigned int i;
  278.         u_int32_t tmp;
  279. #define BEWRITE(x) do {tmp = htonl((x)); val = fwrite(&tmp, 4, 1, ctx->fh);\
  280.                 if (val == 0) LOG(("empty write")); } while(0)
  281.         for (i = 1; i <= ctx->entries; i++) {
  282.                 struct container_dirent *e = ctx->directory + i - 1;
  283.                 val = fwrite(e->filename, 64, 1, ctx->fh);
  284.                 if (val == 0)
  285.                         LOG(("empty write filename"));
  286.                 BEWRITE(e->startoffset);
  287.                 BEWRITE(e->len);
  288.                 BEWRITE(e->flags1);
  289.                 BEWRITE(e->flags2);
  290.         }
  291. #undef BEWRITE
  292.         /* empty entry signifies end of directory */
  293.         tmp = 0;
  294.         val = fwrite(&tmp, 4, 8, ctx->fh);
  295.         if (val == 0)
  296.                 LOG(("empty write end"));
  297. }
  298.  
  299. struct container_ctx *container_create(const char *filename,
  300.                                         const unsigned char *name,
  301.                                         const unsigned char *author)
  302. {
  303.         size_t val;
  304.         struct container_ctx *ctx = calloc(sizeof(struct container_ctx), 1);
  305.  
  306.         ctx->fh = fopen(filename, "wb");
  307.  
  308.         if (ctx->fh == NULL) {
  309.                 free(ctx);
  310.                 return NULL;
  311.         }
  312.  
  313.         ctx->creating = true;
  314.         ctx->entries = 0;
  315.         ctx->directory = NULL;
  316.         ctx->header.parser = htonl(3);
  317.         strncpy((char *)ctx->header.name, (char *)name, 32);
  318.         strncpy((char *)ctx->header.author, (char *)author, 64);
  319.  
  320.         val = fwrite("NSTM", 4, 1, ctx->fh);
  321.         if (val == 0)
  322.                 LOG(("empty write NSTM"));
  323.         val = fwrite(&ctx->header.parser, 4, 1, ctx->fh);
  324.         if (val == 0)
  325.                 LOG(("empty write parser"));
  326.         val = fwrite(ctx->header.name, 32, 1, ctx->fh);
  327.         if (val == 0)
  328.                 LOG(("empty write name"));
  329.         val = fwrite(ctx->header.author, 64, 1, ctx->fh);
  330.         if (val == 0)
  331.                 LOG(("empty write author"));
  332.  
  333.         ctx->header.diroffset = 108;
  334.  
  335.         /* skip over the directory offset for now, and fill it in later.
  336.          * we don't know where it'll be yet!
  337.          */
  338.  
  339.         fseek(ctx->fh, 108, SEEK_SET);
  340.  
  341.         return ctx;
  342. }
  343.  
  344. void container_add(struct container_ctx *ctx, const unsigned char *entryname,
  345.                                         const unsigned char *data,
  346.                                         const u_int32_t datalen)
  347. {
  348.         size_t val;
  349.         container_add_to_dir(ctx, entryname, ftell(ctx->fh), datalen);
  350.         val = fwrite(data, datalen, 1, ctx->fh);
  351.         if (val == 0)
  352.                 LOG(("empty write add file"));
  353. }
  354.  
  355. void container_close(struct container_ctx *ctx)
  356. {
  357.         if (ctx->creating == true) {
  358.                 size_t flen, nflen, val;
  359.  
  360.                 /* discover where the directory's going to go. */
  361.                 flen = container_filelen(ctx->fh);
  362.                 flen = (flen + 3) & (~3); /* round up to nearest 4 bytes */
  363.  
  364.                 /* write this location to the header */
  365.                 fseek(ctx->fh, 104, SEEK_SET);
  366.                 nflen = htonl(flen);
  367.                 val = fwrite(&nflen, 4, 1, ctx->fh);
  368.                 if (val == 0)
  369.                         LOG(("empty write directory location"));
  370.  
  371.                 /* seek to where the directory will be, and write it */
  372.                 fseek(ctx->fh, flen, SEEK_SET);
  373.                 container_write_dir(ctx);
  374.  
  375.         } else if (ctx->processed) {
  376. #ifdef WITH_MMAP
  377.                 munmap(ctx->data, ctx->header.diroffset);
  378. #else
  379.                 free(ctx->data);
  380. #endif
  381.         }
  382.  
  383.         fclose(ctx->fh);
  384.         free(ctx);
  385. }
  386.  
  387. #ifdef WITH_THEME_INSTALL
  388.  
  389. /**
  390.  * install theme from container
  391.  * \param themefile a file containing the containerized theme
  392.  * \param dirbasename a directory basename including trailing path sep; the
  393.  * full path of the theme is then a subdirectory of that
  394.  * caller owns reference to returned string, NULL for error
  395.  */
  396.  
  397. char *container_extract_theme(const char *themefile, const char *dirbasename)
  398. {
  399.         struct stat statbuf;
  400.         struct container_ctx *cctx;
  401.         FILE *fh;
  402.         size_t val;
  403.         const unsigned char *e, *d;
  404.         char *themename, *dirname;
  405.         char path[PATH_MAX];
  406.         int state = 0;
  407.         unsigned int i;
  408.         u_int32_t flen;
  409.  
  410.         cctx = container_open(themefile);
  411.         if (cctx == NULL) {
  412.                 warn_user("FileOpenError", themefile);
  413.                 return NULL;
  414.         }
  415.         themename = strdup((const char *)container_get_name(cctx));
  416.         if (themename == NULL) {
  417.                 warn_user("NoMemory", 0);
  418.                 container_close(cctx);
  419.                 return NULL;
  420.         }
  421.         LOG(("theme name: %s", themename));
  422.         LOG(("theme author: %s", container_get_author(cctx)));
  423.        
  424.         dirname = malloc(strlen(dirbasename) + strlen(themename) + 2);
  425.         if (dirname == NULL) {
  426.                 warn_user(messages_get("NoMemory"), 0);
  427.                 free(themename);
  428.                 container_close(cctx);
  429.                 return NULL;
  430.         }
  431.         strcpy(dirname, dirbasename);
  432.         strcat(dirname, themename);
  433.         if (stat(dirname, &statbuf) != -1) {
  434.                 warn_user("DirectoryError", dirname);
  435.                 container_close(cctx);
  436.                 free(dirname);
  437.                 free(themename);
  438.                 return NULL;
  439.         }
  440.         mkdir(dirname, S_IRWXU);
  441.  
  442.         for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries;
  443.                         e = container_iterate(cctx, &state), i++) {
  444.                 LOG(("extracting %s", e));
  445.                 snprintf(path, PATH_MAX, "%s/%s", dirname, e);
  446.                 fh = fopen(path, "wb");
  447.                 if (fh == NULL) {
  448.                         warn_user("FileOpenError", (char *)e);
  449.                 } else {
  450.                         d = container_get(cctx, e, &flen);
  451.                         val = fwrite(d, flen, 1, fh);
  452.                         if (val == 0)
  453.                                 LOG(("empty write"));
  454.                         fclose(fh);
  455.                 }
  456.         }
  457.         LOG(("theme container unpacked"));
  458.         container_close(cctx);
  459.         free(dirname);
  460.         return themename;
  461.  
  462. }
  463.  
  464. #endif
  465.  
  466. #ifdef TEST_RIG
  467. int main(int argc, char *argv[])
  468. {
  469.         struct container_ctx *ctx = container_create("test.theme", "Test theme",
  470.                                 "Rob Kendrick");
  471.         u_int32_t size;
  472.         int state = 0;
  473.         char *n;
  474.  
  475.         container_add(ctx, "CHEESE", "This is a test of some cheese.", sizeof("This is a test of some cheese."));
  476.         container_add(ctx, "FOO", "This is a test of some cheese.", sizeof("This is a test of some cheese."));
  477.  
  478.         container_close(ctx);
  479.  
  480.         ctx = container_open("test.theme");
  481.  
  482.         printf("Theme name: %s\n", container_get_name(ctx));
  483.         printf("Theme author: %s\n", container_get_author(ctx));
  484.  
  485.         printf("Test string: %s\n", container_get(ctx, "CHEESE", &size));
  486.         printf("Length of text: %d\n", size);
  487.  
  488.         while ( (n = container_iterate(ctx, &state)) ) {
  489.                 printf("%s\n", n);
  490.         }
  491.  
  492.         container_close(ctx);
  493.  
  494.         exit(0);
  495. }
  496. #endif
  497.  
  498. #ifdef NSTHEME
  499.         /* code to implement a simple container creator/extractor */
  500. #include <getopt.h>
  501. #include <dirent.h>
  502. #include <errno.h>
  503. #include <unistd.h>
  504.  
  505. static bool verbose = false;
  506.  
  507. static void show_usage(const char *argv0)
  508. {
  509.         fprintf(stderr, "%s [options] <theme file> <directory>\n", argv0);
  510.         fprintf(stderr, " --help       This text\n");
  511.         fprintf(stderr, " --create     Create theme file from directory\n");
  512.         fprintf(stderr, " --extract    Extract theme file into directory\n");
  513.         fprintf(stderr, " --name x     Set theme's name when creating\n");
  514.         fprintf(stderr, " --author x   Set theme's author when creating\n");
  515.         fprintf(stderr, " --verbose    Print progress information\n");
  516.         fprintf(stderr, "\nOne and only one of --create or --extract must be specified.\n");
  517. }
  518.  
  519. static void extract_theme(const char *themefile, const char *dirname)
  520. {
  521.         struct stat statbuf;
  522.         struct container_ctx *cctx;
  523.         FILE *fh;
  524.         const unsigned char *e, *d;
  525.         char path[PATH_MAX];
  526.         int i, state = 0;
  527.         u_int32_t flen;
  528.  
  529.  
  530.         if (stat(dirname, &statbuf) != -1) {
  531.                 fprintf(stderr, "error: directory '%s' already exists.\n",
  532.                         dirname);
  533.                 exit(1);
  534.         }
  535.  
  536.         mkdir(dirname, S_IRWXU);
  537.  
  538.         cctx = container_open(themefile);
  539.         if (cctx == NULL) {
  540.                 fprintf(stderr, "error: unable to open theme file '%s'\n",
  541.                         themefile);
  542.                 exit(1);
  543.         }
  544.  
  545.         if (verbose == true) {
  546.                 printf("theme name: %s\n", container_get_name(cctx));
  547.                 printf("theme author: %s\n", container_get_author(cctx));
  548.         }
  549.  
  550.         for (e = container_iterate(cctx, &state), i = 0; i < cctx->entries;
  551.                         e = container_iterate(cctx, &state), i++) {
  552.                 if (verbose == true)
  553.                         printf("extracting %s\n", e);
  554.                 snprintf(path, PATH_MAX, "%s/%s", dirname, e);
  555.                 fh = fopen(path, "wb");
  556.                 if (fh == NULL) {
  557.                         perror("warning: unable to open file for output");
  558.                 } else {
  559.                         d = container_get(cctx, e, &flen);
  560.                         fwrite(d, flen, 1, fh);
  561.                         fclose(fh);
  562.                 }
  563.         }
  564.  
  565.         container_close(cctx);
  566.  
  567. }
  568.  
  569. static void create_theme(const char *themefile, const char *dirname,
  570.                                 const unsigned char *name,
  571.                                 const unsigned char *author)
  572. {
  573.         DIR *dir = opendir(dirname);
  574.         FILE *fh;
  575.         struct dirent *e;
  576.         struct stat statbuf;
  577.         struct container_ctx *cctx;
  578.         unsigned char *data;
  579.         char path[PATH_MAX];
  580.         size_t flen;
  581.         int t;
  582.  
  583.         if (dir == NULL) {
  584.                 perror("error: unable to open directory");
  585.                 exit(1);
  586.         }
  587.  
  588.         cctx = container_create(themefile, name, author);
  589.  
  590.         errno = 0;      /* to distinguish between end of dir and err */
  591.  
  592.         while ((e = readdir(dir)) != NULL) {
  593.                 if (strcmp(e->d_name, ".") != 0 &&
  594.                         strcmp(e->d_name, "..") != 0) {
  595.                         /* not the metadirs, so we want to process this. */
  596.                         if (verbose == true)
  597.                                 printf("adding %s\n", e->d_name);
  598.                         if (strlen(e->d_name) > 63) {
  599.                                 fprintf(stderr,
  600.                         "warning: name truncated to length 63.\n");
  601.                         }
  602.  
  603.                         snprintf(path, PATH_MAX, "%s/%s", dirname, e->d_name);
  604.  
  605.                         stat(path, &statbuf);
  606.                         if (S_ISDIR(statbuf.st_mode)) {
  607.                                 fprintf(stderr,
  608.                                         "warning: skipping directory '%s'\n",
  609.                                         e->d_name);
  610.                                 continue;
  611.                         }
  612.  
  613.                         fh = fopen(path, "rb");
  614.                         if (fh == NULL) {
  615.                                 fprintf(stderr,
  616.                                         "warning: unable to open, skipping.");
  617.                         } else {
  618.                                 flen = statbuf.st_size;
  619.                                 data = malloc(flen);
  620.                                 t = fread(data, flen, 1, fh);
  621.                                 fclose(fh);
  622.                                 container_add(cctx, (unsigned char *)e->d_name,
  623.                                                 data, flen);
  624.                                 free(data);
  625.                         }
  626.                 }
  627.                 errno = 0;
  628.         }
  629.  
  630.         if (errno != 0) {
  631.                 perror("error: couldn't enumerate directory");
  632.                 closedir(dir);
  633.                 container_close(cctx);
  634.                 exit(1);
  635.         }
  636.  
  637.         closedir(dir);
  638.         container_close(cctx);
  639. }
  640.  
  641. int main(int argc, char *argv[])
  642. {
  643.         static struct option l_opts[] = {
  644.                 { "help", 0, 0, 'h' },
  645.                 { "create", 0, 0, 'c' },
  646.                 { "extract", 0, 0, 'x' },
  647.                 { "name", 1, 0, 'n' },
  648.                 { "author", 1, 0, 'a' },
  649.                 { "verbose", 0, 0, 'v' },
  650.  
  651.                 { NULL, 0, 0, 0 }
  652.         };
  653.  
  654.         static char *s_opts = "hcxn:a:v";
  655.         int optch, optidx;
  656.         bool creating = false, extracting = false;
  657.         unsigned char name[32] = { '\0' }, author[64] = { '\0' };
  658.         char *themefile, *dirname;
  659.  
  660.         while ((optch = getopt_long(argc, argv, s_opts, l_opts, &optidx)) != -1)
  661.                 switch (optch) {
  662.                 case 'h':
  663.                         show_usage(argv[0]);
  664.                         exit(0);
  665.                         break;
  666.                 case 'c':
  667.                         creating = true;
  668.                         break;
  669.                 case 'x':
  670.                         extracting = true;
  671.                         break;
  672.                 case 'n':
  673.                         strncpy((char *)name, optarg, 31);
  674.                         if (strlen(optarg) > 32)
  675.                                 fprintf(stderr, "warning: theme name truncated to 32 characters.\n");
  676.                         break;
  677.                 case 'a':
  678.                         strncpy((char *)author, optarg, 63);
  679.                         if (strlen(optarg) > 64)
  680.                                 fprintf(stderr, "warning: theme author truncated to 64 characters.\n");
  681.                         break;
  682.                 case 'v':
  683.                         verbose = true;
  684.                         break;
  685.                 default:
  686.                         show_usage(argv[0]);
  687.                         exit(1);
  688.                         break;
  689.                 }
  690.  
  691.         if (creating == extracting) {
  692.                 show_usage(argv[0]);
  693.                 exit(1);
  694.         }
  695.  
  696.         if ((argc - optind) < 2) {
  697.                 show_usage(argv[0]);
  698.                 exit(1);
  699.         }
  700.  
  701.         if (creating == true &&
  702.                 (strlen((char *)name) == 0 || strlen((char *)author) == 0)) {
  703.                 fprintf(stderr, "No theme name and/or author specified.\n");
  704.                 show_usage(argv[0]);
  705.                 exit(1);
  706.         }
  707.  
  708.         themefile = strdup(argv[optind]);
  709.         dirname = strdup(argv[optind + 1]);
  710.  
  711.         if (verbose == true)
  712.                 printf("%s '%s' %s directory '%s'\n",
  713.                         creating ? "creating" : "extracting", themefile,
  714.                         creating ? "from" : "to", dirname);
  715.  
  716.         if (creating) {
  717.                 if (verbose == true)
  718.                         printf("name = %s, author = %s\n", name, author);
  719.                 create_theme(themefile, dirname, name, author);
  720.         } else {
  721.                 extract_theme(themefile, dirname);
  722.         }
  723.  
  724.         return 0;
  725. }
  726. #endif
  727.