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

Program for testing libmobi library

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

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

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include <string.h>
#include <mobi.h>
#include "common.h"
/* 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
bool setpid_opt = false;
bool setserial_opt = false;
#endif
/* options values */
#ifdef USE_ENCRYPTION
char *pid = NULL;
char *serial = NULL;
#endif
#define META_SIZE ARRAYSIZE(meta_functions)
#define ACTIONS_SIZE ARRAYSIZE(actions)
#if HAVE_ATTRIBUTE_NORETURN
static void exit_with_usage(const char *progname) __attribute__((noreturn));
#else
static void exit_with_usage(const char *progname);
#endif
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[] = {
};
static 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 " [-hv] 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(" -h show this usage summary and exit\n");
printf(" -v show version and exit\n");
exit(ERROR);
}
static bool isinteger(const char *string) {
if (*string == '\0') { return false; }
while (*string) {
if (!isdigit(*string++)) { return false; }
}
return true;
}
static bool parsesubopt(char **subopts, char **token, char **value) {
if (!**subopts) { return false; }
*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;
}
static 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:hs:" PRINT_ENC_ARG "v")) != -1) {
switch (opt) {
case 'a':
case 'd':
case 's':
if (strlen(optarg) == 2 && optarg[0] == '-') {
printf("Option -%c requires an argument.\n", opt);
return ERROR;
}
subopts = optarg;
parse = true;
while (parse) {
parse = parsesubopt(&subopts, &token, &value);
if (token && (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 (token && 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 == NULL || token[0] != '?') {
printf("Unknown meta: %s\n", token ? token : optarg);
}
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 == NULL || token[0] != '?') {
exit_with_usage(argv[0]);
}
return SUCCESS;
}
}
break;
#ifdef USE_ENCRYPTION
case 'p':
if (strlen(optarg) == 2 && optarg[0] == '-') {
printf("Option -%c requires an argument.\n", opt);
return ERROR;
}
setpid_opt = true;
pid = optarg;
break;
case 'P':
if (strlen(optarg) == 2 && optarg[0] == '-') {
printf("Option -%c requires an argument.\n", opt);
return ERROR;
}
setserial_opt = true;
serial = optarg;
break;
#endif
case 'v':
printf("mobimeta build: " __DATE__ " " __TIME__ " (" COMPILER ")\n");
printf("libmobi: %s\n", mobi_version());
return 0;
case '?':
if (isprint(optopt)) {
printf("Unknown option `-%c'\n", optopt);
}
else {
printf("Unknown option character `\\x%x'\n", optopt);
}
exit_with_usage(argv[0]);
case 'h':
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';
normalize_path(infile);
if (file_args >= 2) { optind++; }
char outfile[FILENAME_MAX];
strncpy(outfile, argv[optind], FILENAME_MAX - 1);
outfile[FILENAME_MAX - 1] = '\0';
normalize_path(outfile);
/* 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;
}
void print_exth(const MOBIData *m)
Print all loaded EXTH record tags.
Definition: common.c:433
int set_decryption_key(MOBIData *m, const char *serial, const char *pid)
Set key for decryption. Use user supplied pid or device serial number.
Definition: common.c:554
const char * libmobi_msg(const MOBI_RET ret)
Return message for given libmobi return code.
Definition: common.c:68
void print_summary(const MOBIData *m)
Print summary meta information.
Definition: common.c:244
void normalize_path(char *path)
Make sure we use consistent separators on Windows builds.
Definition: common.c:225
MOBI_RET
Error codes returned by functions.
Definition: mobi.h:59
MOBIExthTag
EXTH record tags.
Definition: mobi.h:91
@ MOBI_SUCCESS
Definition: mobi.h:60
MOBI_RET mobi_meta_add_description(MOBIData *m, const char *description)
Add document description metadata.
Definition: meta.c:399
void mobi_free(MOBIData *m)
Free MOBIData structure and all its children.
Definition: memory.c:177
MOBI_RET mobi_meta_add_language(MOBIData *m, const char *language)
Add document language code metadata.
Definition: meta.c:814
MOBI_EXPORT const char * mobi_version(void)
Get libmobi version.
Definition: util.c:76
MOBI_RET mobi_meta_set_subject(MOBIData *m, const char *subject)
Set document subject metadata.
Definition: meta.c:255
MOBI_RET mobi_meta_add_contributor(MOBIData *m, const char *contributor)
Add document contributor metadata.
Definition: meta.c:513
MOBI_RET mobi_meta_set_publishdate(MOBIData *m, const char *publishdate)
Set document publishdate metadata.
Definition: meta.c:369
MOBI_RET mobi_meta_add_publishdate(MOBIData *m, const char *publishdate)
Add document publishdate metadata.
Definition: meta.c:342
MOBI_RET mobi_meta_delete_author(MOBIData *m)
Delete all author metadata.
Definition: meta.c:185
MOBI_RET mobi_meta_set_review(MOBIData *m, const char *review)
Set document review metadata.
Definition: meta.c:597
MOBI_RET mobi_meta_delete_isbn(MOBIData *m)
Delete all isbn metadata.
Definition: meta.c:698
MOBI_RET mobi_meta_set_copyright(MOBIData *m, const char *copyright)
Set document copyright metadata.
Definition: meta.c:654
MOBI_EXPORT MOBI_RET mobi_load_file(MOBIData *m, FILE *file)
Read MOBI document from file into MOBIData structure.
Definition: read.c:846
MOBI_RET mobi_meta_set_author(MOBIData *m, const char *author)
Set document author metadata.
Definition: meta.c:198
MOBI_RET mobi_meta_set_language(MOBIData *m, const char *language)
Set document language code metadata.
Definition: meta.c:849
MOBI_RET mobi_meta_set_contributor(MOBIData *m, const char *contributor)
Set document contributor metadata.
Definition: meta.c:540
MOBI_RET mobi_meta_set_publisher(MOBIData *m, const char *publisher)
Set document publisher metadata.
Definition: meta.c:312
MOBI_RET mobi_meta_add_imprint(MOBIData *m, const char *imprint)
Add document imprint metadata.
Definition: meta.c:456
MOBI_RET mobi_meta_add_isbn(MOBIData *m, const char *isbn)
Add document isbn metadata.
Definition: meta.c:684
MOBI_RET mobi_meta_delete_title(MOBIData *m)
Delete all title metadata.
Definition: meta.c:111
MOBI_RET mobi_meta_add_author(MOBIData *m, const char *author)
Add document author metadata.
Definition: meta.c:171
MOBI_RET mobi_meta_add_publisher(MOBIData *m, const char *publisher)
Add document publisher metadata.
Definition: meta.c:285
MOBI_RET mobi_meta_set_description(MOBIData *m, const char *description)
Set document description metadata.
Definition: meta.c:426
MOBI_RET mobi_meta_delete_publishdate(MOBIData *m)
Delete all publishdate metadata.
Definition: meta.c:356
MOBI_RET mobi_meta_set_isbn(MOBIData *m, const char *isbn)
Set document isbn metadata.
Definition: meta.c:711
MOBI_RET mobi_meta_add_title(MOBIData *m, const char *title)
Add document title metadata.
Definition: meta.c:97
MOBI_RET mobi_meta_delete_copyright(MOBIData *m)
Delete all copyright metadata.
Definition: meta.c:641
MOBI_EXPORT MOBI_RET mobi_write_file(FILE *file, MOBIData *m)
Write mobi document to file.
Definition: write.c:511
MOBI_RET mobi_meta_delete_language(MOBIData *m)
Delete all language code metadata.
Definition: meta.c:828
MOBI_EXPORT MOBI_RET mobi_delete_exthrecord_by_tag(MOBIData *m, const MOBIExthTag tag)
Delete all EXTH records with given MOBIExthTag tag.
Definition: util.c:1426
MOBI_RET mobi_meta_add_subject(MOBIData *m, const char *subject)
Add document subject metadata.
Definition: meta.c:228
MOBI_RET mobi_meta_set_asin(MOBIData *m, const char *asin)
Set document asin metadata.
Definition: meta.c:768
MOBI_RET mobi_meta_delete_review(MOBIData *m)
Delete all review metadata.
Definition: meta.c:584
MOBI_RET mobi_meta_delete_publisher(MOBIData *m)
Delete all publisher metadata.
Definition: meta.c:299
MOBI_EXPORT MOBIExthMeta mobi_get_exthtagmeta_by_tag(const MOBIExthTag tag)
Get MOBIExthMeta tag structure by MOBIExthTag tag id.
Definition: util.c:1538
MOBI_RET mobi_meta_add_asin(MOBIData *m, const char *asin)
Add document asin metadata.
Definition: meta.c:741
MOBI_RET mobi_meta_delete_imprint(MOBIData *m)
Delete all imprint metadata.
Definition: meta.c:470
MOBI_RET mobi_meta_delete_subject(MOBIData *m)
Delete all subject metadata.
Definition: meta.c:242
MOBI_RET mobi_meta_add_review(MOBIData *m, const char *review)
Add document review metadata.
Definition: meta.c:570
MOBI_RET mobi_meta_delete_description(MOBIData *m)
Delete all description metadata.
Definition: meta.c:413
MOBI_EXPORT MOBI_RET mobi_add_exthrecord(MOBIData *m, const MOBIExthTag tag, const uint32_t size, const void *value)
Add new EXTH record with given tag and value.
Definition: util.c:1283
MOBI_RET mobi_meta_set_imprint(MOBIData *m, const char *imprint)
Set document imprint metadata.
Definition: meta.c:483
MOBI_RET mobi_meta_add_copyright(MOBIData *m, const char *copyright)
Add document copyright metadata.
Definition: meta.c:627
MOBI_RET mobi_meta_delete_asin(MOBIData *m)
Delete all asin metadata.
Definition: meta.c:755
MOBI_RET mobi_meta_delete_contributor(MOBIData *m)
Delete all contributor metadata.
Definition: meta.c:527
MOBI_RET mobi_meta_set_title(MOBIData *m, const char *title)
Set document title metadata.
Definition: meta.c:130
MOBIData * mobi_init(void)
Initializer for MOBIData structure.
Definition: memory.c:25
Libmobi main header file.
int main(int argc, char *argv[])
Main.
Definition: mobimeta.c:170
MOBI_RET(* MetaFunAdd)(MOBIData *m, const char *string)
Meta handling functions.
Definition: mobimeta.c:58
Meta functions structure.
Definition: mobimeta.c:64
Main structure holding all metadata and unparsed records data.
Definition: mobi.h:381
EXTH tag metadata.
Definition: mobi.h:273
MOBIExthType type
Definition: mobi.h:275
char * name
Definition: mobi.h:276