/* * Copyright (C) 2005 Network Applied Communication Laboratory Co., Ltd. * * This file is part of Rast. * See the file COPYING for redistribution information. * */ #include #include #include #include #include #include "rast/config.h" #include "rast/db.h" #include "rast/filter.h" #ifdef HAVE_XMLRPC #include "rast/xmlrpc_client.h" #endif static int memory_error(int retcode); static void print_error(rast_error_t *error); #define OPTION_CREATE_ENCODING 256 #define OPTION_CREATE_BYTE_ORDER 257 #define OPTION_CREATE_PRESERVE_TEXT 258 #define OPTION_CREATE_PROPERTY 259 #define OPTION_REGISTER_MIME_TYPE 260 #define OPTION_REGISTER_VERBOSE 261 static int verbose = 0; static char * format_option(const apr_getopt_option_t *option, apr_pool_t *pool) { char *s; if (option == NULL) { return "?"; } if (option->optch <= 0xff) { s = apr_psprintf(pool, "-%c [--%s]", option->optch, option->name); } else { s = apr_psprintf(pool, "--%s", option->name); } if (option->has_arg) { s = apr_pstrcat(pool, s, " arg", NULL); } if (option->description != NULL) { s = apr_psprintf(pool, "%-24s : %s", s, option->description); } return s; } static char * create_usage(apr_getopt_option_t *opts, apr_pool_t *pool) { char *usage = ""; int i; for (i = 0; opts[i].optch != 0; i++) { char *line = format_option(&opts[i], pool); usage = apr_pstrcat(pool, usage, " ", line, "\n", NULL); } return usage; } static rast_error_t * parse_property_type(const char *s, rast_type_e *result) { if (strcmp(s, "string") == 0) { *result = RAST_TYPE_STRING; return RAST_OK; } if (strcmp(s, "date") == 0) { *result = RAST_TYPE_DATE; return RAST_OK; } if (strcmp(s, "datetime") == 0) { *result = RAST_TYPE_DATETIME; return RAST_OK; } if (strcmp(s, "uint") == 0) { *result = RAST_TYPE_UINT; return RAST_OK; } return rast_error(RAST_ERROR_INVALID_ARGUMENT, "unknown property type: %s", s); } static rast_error_t * parse_property_flag_name(const char *arg, int arg_len, rast_uint_t *result, apr_pool_t *pool) { if (strncmp(arg, "search", arg_len) == 0) { *result |= RAST_PROPERTY_FLAG_SEARCH; } else if (strncmp(arg, "text-search", arg_len) == 0) { *result |= RAST_PROPERTY_FLAG_TEXT_SEARCH; } else if (strncmp(arg, "full-text-search", arg_len) == 0) { *result |= RAST_PROPERTY_FLAG_FULL_TEXT_SEARCH; } else { return rast_error(RAST_ERROR_INVALID_ARGUMENT, "unknown property flag: %s", apr_pstrndup(pool, arg, arg_len)); } return RAST_OK; } static rast_error_t * parse_property_flags(const char *arg, rast_uint_t *result, apr_pool_t *pool) { const char *p, *q; rast_error_t *error; q = arg; while ((p = strchr(q, ',')) != NULL) { error = parse_property_flag_name(q, p - q, result, pool); if (error != RAST_OK) { return error; } q = p + 1; } error = parse_property_flag_name(q, p - q, result, pool); return error; } static rast_error_t * parse_property(const char *arg, rast_property_t *result, apr_pool_t *pool) { const char *p, *q; rast_error_t *error; result->flags = 0; q = arg; p = strchr(q, ':'); if (p == NULL) { result->name = (char *) arg; result->type = RAST_TYPE_STRING; return RAST_OK; } result->name = apr_pstrndup(pool, q, p - q); q = p + 1; p = strchr(q, ':'); if (p == NULL) { return parse_property_type(q, &result->type); } error = parse_property_type(apr_pstrndup(pool, q, p - q), &result->type); if (error != RAST_OK) { return error; } q = p + 1; return parse_property_flags(q, &result->flags, pool); } static rast_error_t * check_property_value(rast_property_t *property) { /* todo: must be refactored */ if (strcmp(property->name, "uri") == 0) { if ((property->flags & RAST_PROPERTY_FLAG_SEARCH) == 0) { return rast_error(RAST_ERROR_INVALID_ARGUMENT, "the property (uri) must be set search flag"); } if (property->type != RAST_TYPE_STRING) { return rast_error(RAST_ERROR_INVALID_ARGUMENT, "the property (uri) must be string property"); } } return RAST_OK; } static rast_error_t * create_main(int argc, const char * const *argv, apr_pool_t *pool) { struct { rast_property_t setting; int have_user_property; } default_properties[] = { { .setting = { .name = "uri", .type = RAST_TYPE_STRING, .flags = RAST_PROPERTY_FLAG_SEARCH, }, .have_user_property = 0, }, { .setting = { .name = "last_modified", .type = RAST_TYPE_DATETIME, .flags = RAST_PROPERTY_FLAG_SEARCH, }, .have_user_property = 0, }, { .setting = {0}, } }; apr_getopt_option_t opts[] = { { .name = "byte-order", .optch = OPTION_CREATE_BYTE_ORDER, .has_arg = 1, .description = "[byte-order]", }, { .name = "encoding", .optch = OPTION_CREATE_ENCODING, .has_arg = 1, .description = "[encoding]", }, { .name = "preserve-text", .optch = OPTION_CREATE_PRESERVE_TEXT, .has_arg = 0, .description = "[preserve-text]", }, { .name = "property", .optch = OPTION_CREATE_PROPERTY, .has_arg = 1, .description = "[property]", }, {0}, }; apr_getopt_t *os; const char *db_name; rast_db_create_option_t *options; int count, i, j, num_undef_properties = 0; apr_pool_t *option_pool; options = rast_db_create_option_create(pool); options->properties = (rast_property_t *) apr_palloc(pool, sizeof(rast_property_t) * (argc + 2)); options->num_properties = 2; apr_pool_create(&option_pool, pool); apr_getopt_init(&os, option_pool, argc, argv); for (count = 1; ; count++) { int option_char; const char *option_arg; rast_property_t *property; apr_status_t status; status = apr_getopt_long(os, opts, &option_char, &option_arg); if (status == APR_EOF) { break; } if (status != APR_SUCCESS) { return apr_status_to_rast_error(status); } if (option_arg != NULL && strchr(argv[count], '=') == NULL) { count++; } switch (option_char) { case OPTION_CREATE_BYTE_ORDER: switch (*option_arg) { case 'l': options->byte_order = RAST_LITTLE_ENDIAN; break; case 'b': options->byte_order = RAST_BIG_ENDIAN; break; case 'n': options->byte_order = RAST_NATIVE_ENDIAN; break; default: options->byte_order = RAST_NATIVE_ENDIAN; } break; case OPTION_CREATE_ENCODING: options->encoding = option_arg; break; case OPTION_CREATE_PRESERVE_TEXT: options->preserve_text = 1; break; case OPTION_CREATE_PROPERTY: property = &options->properties[options->num_properties]; parse_property(option_arg, property, pool); for (i = 0; default_properties[i].setting.name != NULL; i++) { rast_error_t *error; if (strcmp(property->name, default_properties[i].setting.name) == 0) { error = check_property_value(property); if (error != RAST_OK) { return error; } default_properties[i].have_user_property = 1; num_undef_properties++; } } options->num_properties++; break; default: fprintf(stderr, "%s\n", create_usage(opts, option_pool)); apr_pool_destroy(option_pool); return rast_error(RAST_ERROR_INVALID_ARGUMENT, ""); } } apr_pool_destroy(option_pool); for (i = 0, j = num_undef_properties; default_properties[i].setting.name != NULL; i++) { if (!default_properties[i].have_user_property) { options->properties[j] = default_properties[i].setting; j++; } } options->num_properties -= num_undef_properties; options->properties += num_undef_properties; if (argc - count < 1) { fprintf(stderr, "%s\n", create_usage(opts, option_pool)); return rast_error(RAST_ERROR_INVALID_ARGUMENT, "too few arguments"); } db_name = argv[count]; return rast_db_create(db_name, options, pool); } static rast_error_t * get_registered_info(rast_db_t *db, const char *uri, rast_result_t **result, apr_pool_t *pool) { const char *properties[] = { "last_modified", }; rast_search_option_t *options; const char *query; options = rast_search_option_create(pool); options->properties = properties; options->num_properties = 1; query = apr_pstrcat(pool, "uri = ", uri, NULL); return rast_db_search(db, query, options, result, pool); } static rast_error_t * register_regular_file(rast_db_t *db, const char *filename, const char *mime_type, apr_finfo_t *finfo, apr_pool_t *pool) { apr_file_t *file; rast_document_t *doc; rast_doc_id_t doc_id; rast_filter_chain_t *chain; apr_bucket_brigade *brigade; apr_bucket *bucket; apr_bucket_alloc_t *bucket_alloc; rast_result_t *result; char *uri, *last_modified_str; apr_time_exp_t last_modified; rast_value_t property; apr_status_t status; rast_error_t *error; uri = apr_pstrcat(pool, "file://", filename, NULL); apr_time_exp_lt(&last_modified, finfo->mtime); last_modified_str = apr_psprintf(pool, "%04d-%02d-%02dT%02d:%02d:%02d", last_modified.tm_year + 1900, last_modified.tm_mon + 1, last_modified.tm_mday, last_modified.tm_hour, last_modified.tm_min, last_modified.tm_sec); error = get_registered_info(db, uri, &result, pool); if (error != RAST_OK) { return error; } if (result->hit_count > 0) { if (strcmp(rast_value_datetime(&result->items[0]->properties[0]), last_modified_str) >= 0) { return RAST_OK; } error = rast_db_delete(db, result->items[0]->doc_id); if (error != RAST_OK) { return error; } } status = apr_file_open(&file, filename, APR_READ, APR_OS_DEFAULT, pool); if (status != APR_SUCCESS) { return apr_status_to_rast_error(status); } error = rast_db_create_document(db, &doc); if (error != RAST_OK) { return error; } bucket_alloc = apr_bucket_alloc_create(pool); error = rast_filter_chain_create(&chain, doc, NULL, 0, pool); if (error != RAST_OK) { return error; } brigade = apr_brigade_create(pool, bucket_alloc); bucket = apr_bucket_file_create(file, 0, finfo->size, pool, bucket_alloc); APR_BRIGADE_INSERT_TAIL(brigade, bucket); bucket = apr_bucket_eos_create(bucket_alloc); APR_BRIGADE_INSERT_TAIL(brigade, bucket); error = rast_filter_chain_invoke(chain, brigade, mime_type, filename); apr_file_close(file); if (error != RAST_OK) { return error; } rast_value_set_string(&property, uri); rast_value_set_type(&property, RAST_TYPE_STRING); error = rast_document_set_property(doc, "uri", &property); if (error != RAST_OK) { return error; } rast_value_set_datetime(&property, last_modified_str); rast_value_set_type(&property, RAST_TYPE_DATETIME); error = rast_document_set_property(doc, "last_modified", &property); if (error != RAST_OK) { return error; } if (verbose) { error = rast_document_get_doc_id(doc, &doc_id); if (error != RAST_OK) { return error; } } error = rast_document_commit(doc); if (error == RAST_OK && verbose) { printf("doc_id : %d\n", doc_id); printf("uri : %s\n", uri); } return error; } static rast_error_t *process_file_recursive(rast_db_t *db, const char *filename, const char *mime_type, rast_error_t *(*process_file_func)(rast_db_t *, const char *, const char *, apr_finfo_t *, apr_pool_t *), apr_pool_t *pool); static rast_error_t * process_directory_file(rast_db_t *db, const char *dirname, const char *mime_type, rast_error_t *(*process_file_func)(rast_db_t *db, const char *filename, const char *, apr_finfo_t *finfo, apr_pool_t *pool), apr_pool_t *pool) { apr_dir_t *dir; apr_finfo_t finfo; const char *path; apr_status_t status; rast_error_t *error; status = apr_dir_open(&dir, dirname, pool); if (status != APR_SUCCESS) { return apr_status_to_rast_error(status); } while (1) { status = apr_dir_read(&finfo, APR_FINFO_NAME, dir); if (status == APR_ENOENT) { break; } if (status != APR_SUCCESS) { return apr_status_to_rast_error(status); } if (strcmp(finfo.name, ".") == 0 || strcmp(finfo.name, "..") == 0) { continue; } path = apr_pstrcat(pool, dirname, "/", finfo.name, NULL); error = process_file_recursive(db, path, mime_type, process_file_func, pool); if (error != RAST_OK) { (void) apr_dir_close(dir); return error; } } return apr_status_to_rast_error(apr_dir_close(dir)); } static rast_error_t * process_file_recursive(rast_db_t *db, const char *filename, const char *mime_type, rast_error_t *(*process_file_func)(rast_db_t *db, const char *filename, const char *, apr_finfo_t *finfo, apr_pool_t *pool), apr_pool_t *pool) { apr_finfo_t finfo; apr_pool_t *sub_pool; apr_status_t status; rast_error_t *error; status = apr_stat(&finfo, filename, APR_FINFO_SIZE | APR_FINFO_MTIME | APR_FINFO_TYPE, pool); if (status != APR_SUCCESS) { return apr_status_to_rast_error(status); } switch (finfo.filetype) { case APR_REG: apr_pool_create(&sub_pool, pool); error = process_file_func(db, filename, mime_type, &finfo, pool); apr_pool_destroy(sub_pool); return error; case APR_DIR: return process_directory_file(db, filename, mime_type, process_file_func, pool); default: return RAST_OK; } } static rast_error_t * process_command(int argc, const char * const *argv, const char *mime_type, rast_error_t *(*process_command_func)(rast_db_t *db, const char *filename, const char *, apr_finfo_t *finfo, apr_pool_t *pool), apr_pool_t *pool) { const char *db_name; rast_db_t *db; int i, num_files; const char * const *files; apr_status_t status; rast_error_t *error; db_name = argv[0]; error = rast_db_open(&db, db_name, RAST_DB_RDWR, NULL, pool); if (error != RAST_OK) { return error; } num_files = argc - 1; files = argv + 1; for (i = 0; i < num_files; i++) { char *name; status = apr_filepath_merge(&name, NULL, files[i], APR_FILEPATH_NATIVE, pool); if (status != APR_SUCCESS) { rast_db_close(db); return apr_status_to_rast_error(status); } error = process_file_recursive(db, name, mime_type, process_command_func, pool); if (error != RAST_OK) { rast_db_close(db); return error; } } rast_db_close(db); return RAST_OK; } static rast_error_t * register_main(int argc, const char * const *argv, apr_pool_t *pool) { apr_getopt_option_t opts[] = { { .name = "mime-type", .optch = OPTION_REGISTER_MIME_TYPE, .has_arg = 1, .description = "[mime-type]", }, { .name = "verbose", .optch = OPTION_REGISTER_VERBOSE, .has_arg = 0, .description = "[verbose]", }, {0}, }; apr_getopt_t *os; const char *mime_type = NULL; int count; apr_pool_t *option_pool; apr_pool_create(&option_pool, pool); apr_getopt_init(&os, option_pool, argc, argv); for (count = 1; ; count++) { int option_char; const char *option_arg; apr_status_t status; status = apr_getopt_long(os, opts, &option_char, &option_arg); if (status == APR_EOF) { break; } if (status != APR_SUCCESS) { return apr_status_to_rast_error(status); } if (option_arg != NULL && strchr(argv[count], '=') == NULL) { count++; } switch (option_char) { case OPTION_REGISTER_MIME_TYPE: mime_type = option_arg; break; case OPTION_REGISTER_VERBOSE: verbose = 1; break; default: fprintf(stderr, "%s\n", create_usage(opts, option_pool)); apr_pool_destroy(option_pool); return rast_error(RAST_ERROR_INVALID_ARGUMENT, ""); } } apr_pool_destroy(option_pool); if (argc - count < 2) { fprintf(stderr, "usage: %s [option] db [file or directory]...\n", argv[0]); return rast_error(RAST_ERROR_INVALID_ARGUMENT, "too few arguments"); } return process_command(argc - count, argv + count, mime_type, register_regular_file, pool); } static rast_error_t * optimize_main(int argc, const char * const *argv, apr_pool_t *pool) { const char *db_name; rast_db_optimize_option_t *options; rast_error_t *error; if (argc < 2) { fprintf(stderr, "usage: %s db\n", argv[0]); return rast_error(RAST_ERROR_INVALID_ARGUMENT, ""); } db_name = argv[1]; options = rast_db_optimize_option_create(pool); error = rast_db_optimize(db_name, options, pool); return error; } static rast_error_t * delete_regular_file(rast_db_t *db, const char *filename, const char *mime_type, apr_finfo_t *finfo, apr_pool_t *pool) { rast_result_t *result; char *uri; rast_error_t *error; int i; uri = apr_pstrcat(pool, "file://", filename, NULL); error = get_registered_info(db, uri, &result, pool); if (error != RAST_OK) { return error; } for (i = 0; i < result->num_items; i++) { error = rast_db_delete(db, result->items[i]->doc_id); if (error != RAST_OK) { return error; } } return RAST_OK; } static rast_error_t * delete_main(int argc, const char * const *argv, apr_pool_t *pool) { if (argc < 3) { fprintf(stderr, "usage: %s db [file or directory]\n", argv[0]); return rast_error(RAST_ERROR_INVALID_ARGUMENT, ""); } return process_command(argc - 1, argv + 1, NULL, delete_regular_file, pool); } int main(int argc, const char * const *argv) { struct { const char *command_name; rast_error_t *(*main_func)(int argc, const char * const *argv, apr_pool_t *pool); } cmds[] = { {"rast-create", create_main}, {"rast-optimize", optimize_main}, {"rast-register", register_main}, {"rast-delete", delete_main}, {NULL, NULL} }; int i; const char *cmd; apr_pool_t *pool; rast_error_t *error; apr_initialize(); atexit(apr_terminate); #ifdef HAVE_XMLRPC error = rast_xmlrpc_client_initialize(); if (error != RAST_OK) { return 1; } atexit(rast_xmlrpc_client_finalize); #else error = rast_initialize(); if (error != RAST_OK) { print_error(error); return 1; } atexit(rast_finalize); #endif apr_pool_create_ex(&pool, NULL, memory_error, NULL); cmd = strrchr(argv[0], '/'); if (cmd == NULL) { cmd = argv[0]; } else { cmd++; } if (strncmp(cmd, "lt-", 3) == 0) { cmd += 3; } for (i = 0; cmds[i].command_name != NULL; i++) { if (strcmp(cmd, cmds[i].command_name) == 0) { error = cmds[i].main_func(argc, argv, pool); if (error != RAST_OK) { print_error(error); return 1; } return 0; } } fprintf(stderr, "%s:%s is not valid command name\n", cmd, argv[0]); return 1; } static int memory_error(int retcode) { abort(); return -1; /* prevent compiler warnings */ } static void print_error(rast_error_t *error) { #ifdef RAST_DEBUG fprintf(stderr, "%s:%d: %s\n", error->file, error->line, error->message); #else fprintf(stderr, "error: %s\n", error->message); #endif rast_error_destroy(error); } /* vim: set filetype=c sw=4 expandtab : */