#include #include #include #include #include #include #include #include #include #include #include #include struct config { int version; enum qr_ec_level ec; enum qr_data_type dtype; int ansi; const char * file; const char * input; }; struct qr_code * create(int version, enum qr_ec_level ec, enum qr_data_type dtype, const char * input, size_t len) { struct qr_data * data; struct qr_code * code; data = qr_data_create(version, ec, dtype, input, len); if (!data) { /* BUG: this could also indicate OOM or * some other error. */ fprintf(stderr, "Invalid data\n"); exit(1); } code = qr_code_create(data); qr_data_destroy(data); if (!code) { perror("Failed to create code"); exit(2); } return code; } void output_pbm(const struct qr_bitmap * bmp, const char * comment) { unsigned char * row; int x, y; puts("P1"); if (comment) printf("# %s\n", comment); printf("%u %u\n", (unsigned)bmp->width + 8, (unsigned)bmp->height + 8); row = bmp->bits; for (y = -4; y < (int)bmp->height + 4; ++y) { if (y < 0 || y >= (int)bmp->height) { for (x = 0; x < (int)bmp->width + 8; ++x) printf("0 "); putchar('\n'); continue; } printf("0 0 0 0 "); for (x = 0; x < (int)bmp->width; ++x) { int mask = 1 << x % CHAR_BIT; int byte = row[x / CHAR_BIT]; printf("%c ", (byte & mask) ? '1' : '0'); } puts("0 0 0 0"); row += bmp->stride; } } void output_ansi(const struct qr_bitmap * bmp) { const char * out[2] = { " ", "\033[7m \033[0m", }; unsigned char * line; size_t x, y; line = bmp->bits; for (y = 0; y < bmp->height; ++y) { for (x = 0; x < bmp->width; ++x) { int mask = 1 << (x % CHAR_BIT); int byte = line[x / CHAR_BIT]; printf("%s", out[!!(byte & mask)]); } putchar('\n'); line += bmp->stride; } } void show_help() { fprintf(stderr, "Usage:\n\t%s [options] \n\n" "\t-h Display this help message\n" "\t-f File containing data to encode (- for stdin)\n" "\t-v Specify QR version (size) 1 <= n <= 40\n" "\t-e Specify EC type: L, M, Q, H\n" "\t-a Output as ANSI graphics (default)\n" "\t-p Output as PBM\n\n", "qrgen"); } void set_default_config(struct config * conf) { conf->version = 0; conf->ec = QR_EC_LEVEL_M; conf->dtype = QR_DATA_8BIT; conf->ansi = 1; conf->file = NULL; conf->input = NULL; } void parse_options(int argc, char ** argv, struct config * conf) { int c; for (;;) { c = getopt(argc, argv, ":hf:v:e:t:ap"); if (c == -1) /* no more options */ break; switch (c) { case 'h': /* help */ show_help(); exit(0); break; case 'f': /* file */ conf->file = optarg; break; case 'v': /* version */ conf->version = atoi(optarg); if (conf->version < 1 || conf->version > 40) { fprintf(stderr, "Version must be between 1 and 40\n"); exit(1); } break; case 'e': /* ec */ switch (tolower(optarg[0])) { case 'l': conf->ec = QR_EC_LEVEL_L; break; case 'm': conf->ec = QR_EC_LEVEL_M; break; case 'q': conf->ec = QR_EC_LEVEL_Q; break; case 'h': conf->ec = QR_EC_LEVEL_H; break; default: fprintf(stderr, "Invalid EC type (%c). Choose from" " L, M, Q or H.\n", optarg[0]); } break; case 't': /* type */ fprintf(stderr, "XXX: ignored \"type\"\n"); break; case 'a': /* ansi */ conf->ansi = 1; break; case 'p': /* pnm */ conf->ansi = 0; break; case ':': fprintf(stderr, "Argument \"%s\" missing parameter\n", argv[optind-1]); exit(1); break; case '?': default: fprintf(stderr, "Invalid argument: \"%s\"\n" "Try -h for help\n", argv[optind-1]); exit(1); break; } } if (optind < argc) conf->input = argv[optind++]; if (!conf->file && !conf->input) { fprintf(stderr, "No data (try -h for help)\n"); exit(1); } } void slurp_file(const char * path, char ** data, size_t * len) { const size_t chunk_size = 65536; FILE * file; char * tmpbuf; size_t count; if (strcmp(path, "-") == 0) file = stdin; else file = fopen(path, "rb"); if (!file) { fprintf(stderr, "Failed to open %s\n", path); exit(2); } *data = NULL; *len = 0; do { tmpbuf = realloc(*data, *len + chunk_size); if (!tmpbuf) { perror("realloc"); exit(2); } *data = tmpbuf; count = fread(*data + *len, 1, chunk_size, file); if (count == 0 && !feof(file)) { perror("fread"); exit(2); } *len += count; } while (*len == chunk_size); fclose(file); } int main(int argc, char ** argv) { struct config conf; struct qr_code * code; char * file_data; size_t len; set_default_config(&conf); parse_options(argc, argv, &conf); if (conf.file) slurp_file(conf.file, &file_data, &len); else len = strlen(conf.input); code = create(conf.version, conf.ec, conf.dtype, conf.file ? file_data : conf.input, len); if (conf.file) free(file_data); if (conf.ansi) output_ansi(code->modules); else output_pbm(code->modules, "libqr v" QR_VERSION); qr_code_destroy(code); return 0; }