libmobi
C library for handling MOBI format ebook documents
mobimeta.c

Program for testing libmobi library

Copyright (c) 2016 Bartek Fabiszewski http://www.fabiszewski.net

Licensed under LGPL, either version 3, or any later. See http://www.gnu.org/licenses/

#ifdef _WIN32
# include "win32/getopt.h"
#else
# include <unistd.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <mobi.h>
#include "common.h"
#ifdef HAVE_CONFIG_H
# include "../config.h"
#endif
/* encryption */
#ifdef USE_ENCRYPTION
# define PRINT_ENC_USG " [-p pid] [-P serial]"
# define PRINT_ENC_ARG "p:P:"
#else
# define PRINT_ENC_USG ""
# define PRINT_ENC_ARG ""
#endif
/* command line options */
#ifdef USE_ENCRYPTION
int setpid_opt = 0;
int setserial_opt = 0;
#endif
/* options values */
#ifdef USE_ENCRYPTION
char *pid = NULL;
char *serial = NULL;
#endif
#define META_SIZE ARRAYSIZE(meta_functions)
#define ACTIONS_SIZE ARRAYSIZE(actions)
typedef MOBI_RET (*MetaFunAdd)(MOBIData *m, const char *string);
typedef MOBI_RET (*MetaFunDel)(MOBIData *m);
typedef struct {
const char *name;
MetaFunAdd function_add;
MetaFunAdd function_set;
MetaFunDel function_del;
} CB;
const CB meta_functions[] = {
};
void exit_with_usage(const char *progname) {
char *p = strrchr(progname, separator);
if (p) { progname = ++p; }
printf("usage: %s [-a | -s meta=value[,meta=value,...]] [-d meta[,meta,...]]" PRINT_ENC_USG " [-v] filein [fileout]\n", progname);
printf(" without arguments prints document metadata and exits\n");
printf(" -a ? list valid meta named keys\n");
printf(" -a meta=value add metadata\n");
printf(" -d meta delete metadata\n");
printf(" -s meta=value set metadata\n");
#ifdef USE_ENCRYPTION
printf(" -p pid set pid for decryption\n");
printf(" -P serial set device serial for decryption\n");
#endif
printf(" -v show version and exit\n");
exit(ERROR);
}
bool isinteger(const char *string) {
if (*string == '\0') { return false; }
while (*string) {
if (!isdigit(*string++)) { return false; }
}
return true;
}
bool parsesubopt(char **subopts, char **token, char **value) {
if (!**subopts) { return -1; }
*token = NULL;
*value = NULL;
char *p = NULL;
while ((p = (*subopts)++) && *p) {
if (*p == ',') {
*p = '\0';
return true;
}
if (!*token) { *token = p; }
if (*p == '=') {
*p = '\0';
*value = ++p;
}
}
return false;
}
int get_meta(const char *token) {
for (int i = 0; i < (int) META_SIZE; i++) {
if (strcmp(token, meta_functions[i].name) == 0) {
return i;
}
}
return -1;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
exit_with_usage(argv[0]);
}
typedef struct {
int command;
int meta;
char *value;
} Action;
Action actions[100];
size_t cmd_count = 0;
char *token = NULL;
char *value = NULL;
char *subopts;
int opt;
int subopt;
bool parse;
while ((opt = getopt(argc, argv, "a:d:s:" PRINT_ENC_ARG "v")) != -1) {
switch (opt) {
case 'a':
case 'd':
case 's':
subopts = optarg;
parse = true;
while (parse) {
parse = parsesubopt(&subopts, &token, &value);
if ((subopt = get_meta(token)) >= 0) {
if ((value == NULL || strlen(value) == 0) && opt != 'd') {
printf("Missing value for suboption '%s'\n\n", meta_functions[subopt].name);
exit_with_usage(argv[0]);
}
if (cmd_count < (ACTIONS_SIZE)) {
actions[cmd_count++] = (Action) { opt, subopt, value };
}
} else if (isinteger(token)) {
if ((value == NULL || strlen(value) == 0) && opt != 'd') {
printf("Missing value for suboption '%s'\n\n", token);
exit_with_usage(argv[0]);
}
if (cmd_count < (ACTIONS_SIZE)) {
/* mark numeric meta with upper opt */
actions[cmd_count++] = (Action) { toupper(opt), atoi(token), value };
}
} else {
if (token[0] != '?') {
printf("Unknown meta: %s\n", token);
}
printf("Valid named meta keys:\n");
for (size_t i = 0; i < META_SIZE; i++) {
printf(" %s\n", meta_functions[i].name);
}
printf("\n");
if (token[0] != '?') {
exit_with_usage(argv[0]);
}
return SUCCESS;
}
}
break;
#ifdef USE_ENCRYPTION
case 'p':
setpid_opt = 1;
pid = optarg;
break;
case 'P':
setserial_opt = 1;
serial = optarg;
break;
#endif
case 'v':
printf("mobimeta build: " __DATE__ " " __TIME__ " (" COMPILER ")\n");
printf("libmobi: %s\n", mobi_version());
return 0;
case '?':
#ifdef USE_ENCRYPTION
if (optopt == 'p') {
printf("Option -%c requires an argument.\n", optopt);
}
else
#endif
if (isprint(optopt)) {
printf("Unknown option `-%c'\n", optopt);
}
else {
printf("Unknown option character `\\x%x'\n", optopt);
}
exit_with_usage(argv[0]);
break;
default:
exit_with_usage(argv[0]);
}
}
int file_args = argc - optind;
if (file_args <= 0) {
printf("Missing filename\n");
exit_with_usage(argv[0]);
}
char infile[FILENAME_MAX];
strncpy(infile, argv[optind], FILENAME_MAX - 1);
infile[FILENAME_MAX - 1] = '\0';
if (file_args >= 2) { optind++; }
char outfile[FILENAME_MAX];
strncpy(outfile, argv[optind], FILENAME_MAX - 1);
outfile[FILENAME_MAX - 1] = '\0';
/* Initialize MOBIData structure */
if (m == NULL) {
printf("Libmobi initialization failed\n");
return ERROR;
}
/* read */
FILE *file_in = fopen(infile, "rb");
if (file_in == NULL) {
int errsv = errno;
printf("Error opening file: %s (%s)\n", infile, strerror(errsv));
return ERROR;
}
MOBI_RET mobi_ret = mobi_load_file(m, file_in);
fclose(file_in);
if (mobi_ret != MOBI_SUCCESS) {
printf("Error loading file (%s)\n", libmobi_msg(mobi_ret));
return ERROR;
}
#ifdef USE_ENCRYPTION
if (setpid_opt || setserial_opt) {
int ret = set_decryption_key(m, serial, pid);
if (ret != SUCCESS) {
return ret;
};
}
#endif
if (cmd_count == 0) {
return SUCCESS;
}
for (size_t i = 0; i < cmd_count; i++) {
Action action = actions[i];
mobi_ret = MOBI_SUCCESS;
switch (action.command) {
/* named meta */
case 'a':
mobi_ret = meta_functions[action.meta].function_add(m, action.value);
break;
case 'd':
mobi_ret = meta_functions[action.meta].function_del(m);
break;
case 's':
mobi_ret = meta_functions[action.meta].function_set(m, action.value);
break;
/* numeric exth */
case 'A':
if (tag.name && tag.type == EXTH_NUMERIC && isinteger(action.value)) {
uint32_t numeric = (uint32_t) atoi(action.value);
mobi_ret = mobi_add_exthrecord(m, (MOBIExthTag) action.meta, sizeof(uint32_t), &numeric);
} else {
mobi_ret = mobi_add_exthrecord(m, (MOBIExthTag) action.meta, (uint32_t) strlen(action.value), action.value);
}
break;
case 'D':
mobi_ret = mobi_delete_exthrecord_by_tag(m, (MOBIExthTag) action.meta);
break;
case 'S':
mobi_ret = mobi_delete_exthrecord_by_tag(m, (MOBIExthTag) action.meta);
if (mobi_ret != MOBI_SUCCESS) { break; }
if (tag.name && tag.type == EXTH_NUMERIC && isinteger(action.value)) {
uint32_t numeric = (uint32_t) atoi(action.value);
mobi_ret = mobi_add_exthrecord(m, (MOBIExthTag) action.meta, sizeof(uint32_t), &numeric);
} else {
mobi_ret = mobi_add_exthrecord(m, (MOBIExthTag) action.meta, (uint32_t) strlen(action.value), action.value);
}
break;
}
if (mobi_ret != MOBI_SUCCESS) {
printf("Metadata modification failed (%s)\n", libmobi_msg(mobi_ret));
return ERROR;
}
}
/* write */
printf("Saving %s...\n", outfile);
FILE *file_out = fopen(outfile, "wb");
if (file_out == NULL) {
int errsv = errno;
printf("Error opening file: %s (%s)\n", outfile, strerror(errsv));
return ERROR;
}
mobi_ret = mobi_write_file(file_out, m);
fclose(file_out);
if (mobi_ret != MOBI_SUCCESS) {
printf("Error writing file (%s)\n", libmobi_msg(mobi_ret));
return ERROR;
}
/* Free MOBIData structure */
return SUCCESS;
}