/* This file is part of Ellinika project. Copyright (C) 2004 Sergey Poznyakoff Ellinika is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Ellinika is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Ellinika; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "trans.h" #include #include char *sql_database; /* Database name */ char *sql_host; /* SQL server host name */ int sql_port = 3306; /* SQL port */ char *sql_user; /* SQL user name */ char *sql_password; /* Password for this user */ int compile_only; /* Compile input into the internal representation and exit. Do not write the database */ int preserve_flag; /* Preserve closed cross-reference links in pending_links table. */ int debug; /* Debug level */ static int error_count; /* Number of errors encountered during compilation */ static RAD_LIST *include_list; /* List of m4 include directories */ static char *m4_bin = "/usr/bin/m4"; /* Full file name to the m4 binary. FIXME: Should be autoconf'ed */ void * emalloc(size_t size) { void *ptr; ptr = malloc(size); if (!ptr) { fprintf(stderr, "not enough memory\n"); exit(1); } return ptr; } void efree(void *ptr) { free(ptr); } RAD_LIST *node_list; struct node * create_node(RAD_LIST *hdr, RAD_LIST *descr) { struct node *node = emalloc(sizeof(*node)); struct header *prev = NULL; int i; for (i = list_count(hdr)-1; i >= 0; i--) { struct header *dp = list_item(hdr, i); if (dp->pos < 0) { if (prev) dp->pos = prev->pos; else { fprintf(stderr, "%s:%d: no POS is set for \"%s\"\n", file_name, input_line, dp->key); error_count++; } } if (dp->pos >= 0) prev = dp; } node->header = hdr; node->descr = descr; return node; } static unsigned long dict_index; static unsigned long article_index; int _emit_headers(void *item, void *data) { struct header *hp = item; char *sound; if (debug) fprintf(stderr, "Emitting %s\n", hp->key); sound = greek_to_transcription(hp->key); if (hp->forms) sql_query("INSERT INTO dict VALUES(%lu,\"%s\",\"%s\",%d,\"%s\")", dict_index, hp->key, sound, hp->pos, hp->forms); else sql_query("INSERT INTO dict (ident,word,sound,pos) VALUES(%lu,\"%s\",\"%s\",%d)", dict_index, hp->key, sound, hp->pos); efree(sound); return 0; } static void set_link(char *type, unsigned long dict_index, u_char *value) { sql_query("INSERT INTO pending_links (type,originator,word) " "VALUES('%s',%lu,'%s')", type, dict_index, value); } int _emit_descr(void *item, void *data) { struct descr *descr = item; unsigned long index; switch (descr->type) { case descr_topic: if (sql_query_n(&index, "SELECT ident FROM topic WHERE title=\"%s\"", descr->value)) { sql_query("INSERT INTO topic (title) VALUES (\"%s\")", descr->value); sql_query_n(&index, "SELECT LAST_INSERT_ID()"); } sql_query("INSERT INTO topic_tab VALUES(%lu,%lu)", index, dict_index); break; case descr_meaning: sql_query("INSERT INTO articles VALUES (%lu, %lu, \"%s\")", dict_index, article_index++, descr->value); break; case descr_antonym: set_link("ANT", dict_index, descr->value); break; case descr_xref: set_link("XREF", dict_index, descr->value); break; } return 0; } int emit_node(void *item, void *data) { struct node *node = item; article_index = 0; dict_index++; list_iterate(node->header, _emit_headers, NULL); list_iterate(node->descr, _emit_descr, NULL); return 0; } void pending_fixup() { unsigned long val; sql_query("INSERT IGNORE INTO links " "SELECT p.type,p.originator,d.ident " "FROM dict d, pending_links p " "WHERE p.word = d.word AND p.type != 'CLOSED'"); sql_query("INSERT IGNORE INTO links " "SELECT p.type,d.ident,p.originator " "FROM dict d, pending_links p " "WHERE p.word = d.word AND p.type != 'CLOSED'"); sql_query("UPDATE pending_links p, dict d SET p.type='CLOSED' " "WHERE p.word = d.word"); /* SQL92: UPDATE pending_links SET type='CLOSED' WHERE originator in (SELECT d.ident from dict d, pending_links p WHERE p.word=d.word) */ if (!preserve_flag) sql_query("DELETE FROM pending_links WHERE type = 'CLOSED'"); if (sql_query("SELECT count(*) FROM pending_links " " WHERE type != 'CLOSED' " " GROUP BY word") == 0) fprintf (stderr, "%lu unresolved references\n", sql_num_tuples()); } void update_stat() { unsigned long count; sql_query_n(&count, "SELECT count(*) from dict"); sql_query("DELETE from stat"); sql_query("INSERT INTO stat (count,updated) VALUES(%lu,now())", count); } void cleanup_db() { sql_query("DELETE FROM links"); sql_query("DELETE FROM articles"); sql_query("DELETE FROM dict"); sql_query("DELETE FROM topic"); sql_query("DELETE FROM topic_tab"); sql_query("DELETE FROM pending_links"); } #define ARG_CLEANUP 256 #define ARG_HELP 257 #define ARG_VERSION 258 #define ARG_PRESERVE 259 struct option option[] = { { "check", no_argument, 0, 'c' }, { "cleanup", no_argument, 0, ARG_CLEANUP }, { "database", required_argument, 0, 'd' }, { "host", required_argument, 0, 'h' }, { "port", required_argument, 0, 'P' }, { "password", required_argument, 0, 'p' }, { "user", required_argument, 0, 'u' }, { "verbose", no_argument, 0, 'v' }, { "include-dir", required_argument, 0, 'I' }, { "m4-path", required_argument, 0, 'm' }, { "help", no_argument, 0, ARG_HELP }, { "version", no_argument, 0, ARG_VERSION }, { NULL } }; void usage() { printf("usage: trans [OPTIONS] [FILE...]\n\ \n\ Options are:\n\ Database location:\n\ -d, --database=NAME Set the database name\n\ -h, --host=NAME Set the host name or IP address\n\ -P, --port Set the port number to connect to\n\ -u, --user=NAME Set MySQL user name\n\ -p, --password=STRING Set user password\n\ \n\ Operation modifiers:\n\ \n\ -c, --check Check input file syntax, do not modify the database\n\ --cleanup Clean up the database before starting the operation\n\ -v, --verbose Produce verbose output. The more -v you give, the\n\ higher verbosity level is.\n\ --preserve-xref Preserve closed cross references (may be useful\n\ for debugging)\n\ -I, --include-dir=DIR Append DIR to m4 include path\n\ -m, --m4=FILE Set full file name of the m4 executable\n\ \n\ Informational options:\n\ \n\ --help Display this help message\n\ --version Display program version\n"); exit(0); } int main(int argc, char **argv) { int rc; int index; int cleanup_flag = 0; while ((rc = getopt_long(argc, argv, "cd:h:I:m:P:p:u:v", option, NULL)) != EOF) { switch (rc) { case 'c': compile_only = 1; break; case 'd': sql_database = optarg; break; case 'h': sql_host = optarg; break; case 'P': sql_port = atoi(optarg); break; case 'p': sql_password = optarg; break; case 'u': sql_user = optarg; break; case 'v': debug++; break; case 'I': if (!include_list) include_list = list_create(); list_append(include_list, optarg); break; case 'm': m4_bin = optarg; break; case ARG_CLEANUP: cleanup_flag = 1; break; case ARG_PRESERVE: preserve_flag = 1; break; case ARG_HELP: usage(); break; case ARG_VERSION: printf("trans (%s)\n", PACKAGE_STRING); exit(0); } } make_m4_args (m4_bin, include_list); sql_connect(); node_list = list_create(); argc -= optind; argv += optind; if (parse(argc, argv) || error_count) return 1; if (compile_only) return 0; if (cleanup_flag) cleanup_db(); sql_query_n(&dict_index, "SELECT ident FROM dict ORDER BY ident DESC LIMIT 1"); list_iterate(node_list, emit_node, NULL); pending_fixup(); update_stat(); sql_close(); return 0; }