..
/
download
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include <stdbool.h>
#include <string.h>
#include <libxml/tree.h>
#include <libxml/xpath.h>
#include <libxslt/transform.h>
#include "xslt.h"
#include "s1kd_tools.h"
#define PROG_NAME "s1kd-index"
#define VERSION "1.10.0"
/* Path to text nodes where indexFlags may occur */
#define ELEMENTS_XPATH BAD_CAST "//para/text()"
#define PRE_TERM_DELIM BAD_CAST " "
#define POST_TERM_DELIM BAD_CAST " .,"
#define ERR_PREFIX PROG_NAME ": ERROR: "
#define INF_PREFIX PROG_NAME ": INFO: "
#define E_NO_LIST ERR_PREFIX "Could not read index flags from %s\n"
#define E_BAD_LIST ERR_PREFIX "Could not read list: %s\n"
#define E_NO_FILE ERR_PREFIX "Could not read file: %s\n"
#define I_MARKUP INF_PREFIX "Adding index flags to %s...\n"
#define I_DELETE INF_PREFIX "Deleting index flags from %s...\n"
#define EXIT_NO_LIST 1
static enum verbosity { QUIET, NORMAL, VERBOSE } verbosity = NORMAL;
/* Help/usage message */
static void show_help(void)
{
puts("Usage:");
puts(" " PROG_NAME " -h?");
puts(" " PROG_NAME " [-I <index>] [-filqv] [<module>...]");
puts(" " PROG_NAME " -D [-filqv] [<module>...]");
puts("");
puts("Options:");
puts(" -D, --delete Delete current index flags.");
puts(" -f, --overwrite Overwrite input module(s).");
puts(" -h, -?, --help Show help/usage message.");
puts(" -I, --indexflags <index> Specify a custom .indexflags file");
puts(" -i, --ignore-case Ignore case when flagging terms.");
puts(" -l, --list Input is a list of file names.");
puts(" -q, --quiet Quiet mode.");
puts(" -v, --verbose Verbose output.");
puts(" --version Show version information.");
LIBXML2_PARSE_LONGOPT_HELP
}
static void show_version(void)
{
printf("%s (s1kd-tools) %s\n", PROG_NAME, VERSION);
printf("Using libxml %s and libxslt %s\n", xmlParserVersion, xsltEngineVersion);
}
/* Return the lowest level in an indexFlag. This is matched against the text
* to determine where to insert the flag.
*/
static xmlChar *last_level(xmlNodePtr flag)
{
xmlChar *lvl;
if ((lvl = xmlGetProp(flag, BAD_CAST "indexLevelFour"))) {
return lvl;
} else if ((lvl = xmlGetProp(flag, BAD_CAST "indexLevelThree"))) {
return lvl;
} else if ((lvl = xmlGetProp(flag, BAD_CAST "indexLevelTwo"))) {
return lvl;
} else if ((lvl = xmlGetProp(flag, BAD_CAST "indexLevelOne"))) {
return lvl;
}
return NULL;
}
static bool is_term(xmlChar *content, int content_len, int i, xmlChar *term, int term_len, bool ignorecase)
{
bool is;
xmlChar s, e;
s = i == 0 ? ' ' : content[i - 1];
e = i + term_len >= content_len - 1 ? ' ' : content[i + term_len];
is = xmlStrchr(PRE_TERM_DELIM, s) &&
(ignorecase ?
xmlStrncasecmp(content + i, term, term_len) :
xmlStrncmp(content + i, term, term_len)) == 0 &&
xmlStrchr(POST_TERM_DELIM, e);
return is;
}
/* Insert indexFlag elements after matched terms. */
static void gen_index_node(xmlNodePtr node, xmlNodePtr flag, bool ignorecase)
{
xmlChar *content;
xmlChar *term;
int term_len, content_len;
int i;
content = xmlNodeGetContent(node);
content_len = xmlStrlen(content);
term = last_level(flag);
term_len = xmlStrlen(term);
i = 0;
while (i + term_len <= content_len) {
if (is_term(content, content_len, i, term, term_len, ignorecase)) {
xmlChar *s1 = xmlStrndup(content, i + term_len);
xmlChar *s2 = xmlStrsub(content, i + term_len, content_len - (i + term_len));
xmlNodePtr acr;
xmlFree(content);
xmlNodeSetContent(node, s1);
xmlFree(s1);
acr = xmlAddNextSibling(node, xmlCopyNode(flag, 1));
node = xmlAddNextSibling(acr, xmlNewText(s2));
content = s2;
content_len = xmlStrlen(s2);
i = 0;
} else {
++i;
}
}
xmlFree(term);
xmlFree(content);
}
/* Flag an individual term in all applicable elements in a module. */
static void gen_index_flag(xmlNodePtr flag, xmlXPathContextPtr ctx, bool ignorecase)
{
xmlXPathObjectPtr obj;
obj = xmlXPathEvalExpression(ELEMENTS_XPATH, ctx);
if (!xmlXPathNodeSetIsEmpty(obj->nodesetval)) {
int i;
for (i = 0; i < obj->nodesetval->nodeNr; ++i) {
gen_index_node(obj->nodesetval->nodeTab[i], flag, ignorecase);
}
}
xmlXPathFreeObject(obj);
}
/* Insert indexFlags for each term included in the specified index file. */
static void gen_index_flags(xmlNodeSetPtr flags, xmlXPathContextPtr ctx, bool ignorecase)
{
int i;
for (i = 0; i < flags->nodeNr; ++i) {
gen_index_flag(flags->nodeTab[i], ctx, ignorecase);
}
}
/* Apply a built-in XSLT transform to a doc in place. */
static void transform_doc(xmlDocPtr doc, unsigned char *xsl, unsigned int len)
{
xmlDocPtr styledoc, src, res;
xsltStylesheetPtr style;
xmlNodePtr old;
src = xmlCopyDoc(doc, 1);
styledoc = read_xml_mem((const char *) xsl, len);
style = xsltParseStylesheetDoc(styledoc);
res = xsltApplyStylesheet(style, src, NULL);
old = xmlDocSetRootElement(doc, xmlCopyNode(xmlDocGetRootElement(res), 1));
xmlFreeNode(old);
xmlFreeDoc(src);
xmlFreeDoc(res);
xsltFreeStylesheet(style);
}
/* Convert index flags for older issues. */
static void convert_to_iss_30(xmlDocPtr doc)
{
transform_doc(doc, iss30_xsl, iss30_xsl_len);
}
static void delete_index_flags(const char *path, bool overwrite)
{
xmlDocPtr doc;
if (verbosity >= VERBOSE) {
fprintf(stderr, I_DELETE, path);
}
doc = read_xml_doc(path);
transform_doc(doc, delete_xsl, delete_xsl_len);
if (overwrite) {
save_xml_doc(doc, path);
} else {
save_xml_doc(doc, "-");
}
}
/* Insert indexFlag elements after matched terms in a document. */
static void gen_index(const char *path, xmlDocPtr index_doc, bool overwrite, bool ignorecase)
{
xmlDocPtr doc;
xmlXPathContextPtr doc_ctx, index_ctx;
xmlXPathObjectPtr index_obj;
xmlNodeSetPtr flags;
if (verbosity >= VERBOSE) {
fprintf(stderr, I_MARKUP, path);
}
if (!(doc = read_xml_doc(path))) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_NO_FILE, path);
}
return;
}
index_ctx = xmlXPathNewContext(index_doc);
index_obj = xmlXPathEvalExpression(BAD_CAST "//indexFlag", index_ctx);
flags = index_obj->nodesetval;
doc_ctx = xmlXPathNewContext(doc);
if (!xmlXPathNodeSetIsEmpty(flags)) {
gen_index_flags(flags, doc_ctx, ignorecase);
}
xmlXPathFreeContext(doc_ctx);
xmlXPathFreeObject(index_obj);
xmlXPathFreeContext(index_ctx);
if (xmlStrcmp(xmlFirstElementChild(xmlDocGetRootElement(doc))->name, BAD_CAST "idstatus") == 0) {
convert_to_iss_30(doc);
}
if (overwrite) {
save_xml_doc(doc, path);
} else {
save_xml_doc(doc, "-");
}
xmlFreeDoc(doc);
}
static xmlDocPtr read_index_flags(const char *fname)
{
xmlDocPtr index_doc;
if (!(index_doc = read_xml_doc(fname))) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_NO_LIST, fname);
}
exit(EXIT_NO_LIST);
}
return index_doc;
}
static void handle_list(const char *path, bool delflags, xmlDocPtr index_doc, bool overwrite, bool ignorecase)
{
FILE *f;
char line[PATH_MAX];
if (path) {
f = fopen(path, "r");
} else {
f = stdin;
}
if (!f) {
if (verbosity >= NORMAL) {
fprintf(stderr, E_BAD_LIST, path);
}
return;
}
while (fgets(line, PATH_MAX, f)) {
strtok(line, "\t\r\n");
if (delflags) {
delete_index_flags(line, overwrite);
} else {
gen_index(line, index_doc, overwrite, ignorecase);
}
}
fclose(f);
}
int main(int argc, char **argv)
{
int i;
bool overwrite = false;
bool ignorecase = false;
bool delflags = false;
bool list = false;
xmlDocPtr index_doc = NULL;
const char *sopts = "DfI:liqvh?";
struct option lopts[] = {
{"version" , no_argument , 0, 0},
{"help" , no_argument , 0, 'h'},
{"delete" , no_argument , 0, 'D'},
{"overwrite" , no_argument , 0, 'f'},
{"indexflags" , required_argument, 0, 'I'},
{"ignore-case", no_argument , 0, 'i'},
{"list" , no_argument , 0, 'l'},
{"quiet" , no_argument , 0, 'q'},
{"verbose" , no_argument , 0, 'v'},
LIBXML2_PARSE_LONGOPT_DEFS
{0, 0, 0, 0}
};
int loptind = 0;
while ((i = getopt_long(argc, argv, sopts, lopts, &loptind)) != -1) {
switch (i) {
case 0:
if (strcmp(lopts[loptind].name, "version") == 0) {
show_version();
return 0;
}
LIBXML2_PARSE_LONGOPT_HANDLE(lopts, loptind, optarg)
break;
case 'D':
delflags = true;
break;
case 'f':
overwrite = true;
break;
case 'I':
if (!index_doc) {
index_doc = read_index_flags(optarg);
}
break;
case 'i':
ignorecase = true;
break;
case 'l':
list = true;
break;
case 'q':
--verbosity;
break;
case 'v':
++verbosity;
break;
case 'h':
case '?':
show_help();
return 0;
}
}
if (!index_doc && !delflags) {
char fname[PATH_MAX];
find_config(fname, DEFAULT_INDEXFLAGS_FNAME);
index_doc = read_index_flags(fname);
}
if (optind < argc) {
for (i = optind; i < argc; ++i) {
if (list) {
handle_list(argv[i], delflags, index_doc, overwrite, ignorecase);
} else if (delflags) {
delete_index_flags(argv[i], overwrite);
} else {
gen_index(argv[i], index_doc, overwrite, ignorecase);
}
}
} else if (list) {
handle_list(NULL, delflags, index_doc, overwrite, ignorecase);
} else if (delflags) {
delete_index_flags("-", false);
} else {
gen_index("-", index_doc, false, ignorecase);
}
xmlFreeDoc(index_doc);
xsltCleanupGlobals();
xmlCleanupParser();
return 0;
}
gopher://khzae.net/0/s1000d/s1kd-tools/src/tools/s1kd-index/s1kd-index.c