From 8283ca3bfc81ad5566041e4b47e8899bd6e5f9ba Mon Sep 17 00:00:00 2001 From: dockes Date: Mon, 9 Oct 2006 16:37:08 +0000 Subject: [PATCH] aspell checkpoint --- src/VERSION | 2 +- src/aspell/rclaspell.cpp | 140 +++++++++++++++++++++------------------ src/aspell/rclaspell.h | 25 +++++-- src/index/Makefile | 6 +- src/lib/Makefile | 10 ++- src/lib/mkMake | 2 +- src/makesrcdist.sh | 4 +- src/rcldb/rcldb.cpp | 58 +++++++++++++++- src/rcldb/rcldb.h | 16 ++++- src/rcldb/stemdb.cpp | 5 +- src/utils/execmd.cpp | 49 +++++++++----- src/utils/execmd.h | 66 ++++++++++++------ 12 files changed, 264 insertions(+), 119 deletions(-) diff --git a/src/VERSION b/src/VERSION index 94fe62c2..dc1e644a 100644 --- a/src/VERSION +++ b/src/VERSION @@ -1 +1 @@ -1.5.4 +1.6.0 diff --git a/src/aspell/rclaspell.cpp b/src/aspell/rclaspell.cpp index cad5c160..8bd54197 100644 --- a/src/aspell/rclaspell.cpp +++ b/src/aspell/rclaspell.cpp @@ -1,6 +1,6 @@ #ifndef TEST_RCLASPELL #ifndef lint -static char rcsid[] = "@(#$Id: rclaspell.cpp,v 1.1 2006-10-09 14:05:35 dockes Exp $ (C) 2006 J.F.Dockes"; +static char rcsid[] = "@(#$Id: rclaspell.cpp,v 1.2 2006-10-09 16:37:08 dockes Exp $ (C) 2006 J.F.Dockes"; #endif #include #include @@ -173,7 +173,7 @@ bool Aspell::buildDict(Rcl::Db &db, string &reason) bool Aspell::suggest(Rcl::Db &db, - string &term, listsuggestions, string &reason) + string &term, list &suggestions, string &reason) { AspellCanHaveError *ret; AspellSpeller *speller; @@ -184,6 +184,8 @@ bool Aspell::suggest(Rcl::Db &db, aapi.aspell_config_replace(config, "lang", m_lang.c_str()); aapi.aspell_config_replace(config, "encoding", "utf-8"); aapi.aspell_config_replace(config, "master", dicPath().c_str()); + aapi.aspell_config_replace(config, "sug-mode", "fast"); + // aapi.aspell_config_replace(config, "sug-edit-dist", "2"); ret = aapi.new_aspell_speller(config); aapi.delete_aspell_config(config); @@ -203,7 +205,15 @@ bool Aspell::suggest(Rcl::Db &db, AspellStringEnumeration *els = aapi.aspell_word_list_elements(wl); const char *word; while ((word = aapi.aspell_string_enumeration_next(els)) != 0) { - suggestions.push_back(word); + // stemDiffers checks that the word exists (we don't want + // aspell computed stuff, only exact terms from the dictionary), + // and that it stems differently to the base word (else it's not + // useful to expand the search). Or is it ? + // ******** This should depend if + // stemming is turned on or not for querying ******* + string sw(word); + if (db.termExists(sw) && db.stemDiffers("english", sw, term)) + suggestions.push_back(word); } aapi.delete_aspell_string_enumeration(els); aapi.delete_aspell_speller(speller); @@ -233,7 +243,9 @@ RclConfig *rclconfig; Rcl::Db rcldb; static char usage [] = -" \n\n" +" -b : build dictionary\n" +" -s : suggestions for term\n" +"\n\n" ; static void Usage(void) @@ -249,75 +261,75 @@ static int op_flags; int main(int argc, char **argv) { - int count = 10; + string word; - thisprog = argv[0]; - argc--; argv++; + thisprog = argv[0]; + argc--; argv++; - while (argc > 0 && **argv == '-') { - (*argv)++; - if (!(**argv)) - /* Cas du "adb - core" */ - Usage(); - while (**argv) - switch (*(*argv)++) { - case 's': op_flags |= OPT_s; break; - case 'b': op_flags |= OPT_b; if (argc < 2) Usage(); - if ((sscanf(*(++argv), "%d", &count)) != 1) - Usage(); - argc--; - goto b1; - default: Usage(); break; - } - b1: argc--; argv++; - } - - if (argc != 0) - Usage(); - - string reason; - rclconfig = recollinit(0, 0, reason); - if (!rclconfig || !rclconfig->ok()) { - fprintf(stderr, "Configuration problem: %s\n", reason.c_str()); - exit(1); + while (argc > 0 && **argv == '-') { + (*argv)++; + if (!(**argv)) + /* Cas du "adb - core" */ + Usage(); + while (**argv) + switch (*(*argv)++) { + case 'b': op_flags |= OPT_b; break; + case 's': op_flags |= OPT_s; if (argc < 2) Usage(); + word = *(++argv); + argc--; + goto b1; + default: Usage(); break; + } + b1: argc--; argv++; } - string dbdir = rclconfig->getDbDir(); - if (dbdir.empty()) { - fprintf(stderr, "No db directory in configuration"); - exit(1); - } + if (argc != 0 || op_flags == 0) + Usage(); - if (!rcldb.open(dbdir, Rcl::Db::DbRO, 0)) { - fprintf(stderr, "Could not open database in %s\n", dbdir.c_str()); - exit(1); - } + string reason; + rclconfig = recollinit(0, 0, reason); + if (!rclconfig || !rclconfig->ok()) { + fprintf(stderr, "Configuration problem: %s\n", reason.c_str()); + exit(1); + } - string lang = "en"; + string dbdir = rclconfig->getDbDir(); + if (dbdir.empty()) { + fprintf(stderr, "No db directory in configuration"); + exit(1); + } - Aspell aspell(rclconfig, lang); + if (!rcldb.open(dbdir, Rcl::Db::DbRO, 0)) { + fprintf(stderr, "Could not open database in %s\n", dbdir.c_str()); + exit(1); + } - if (!aspell.init("/usr/local", reason)) { - cerr << "Init failed: " << reason << endl; - exit(1); - } -#if 0 - if (!aspell.buildDict(rcldb, reason)) { - cerr << "buildDict failed: " << reason << endl; - exit(1); - } -#endif - list sug; - string word = "practice"; - if (!aspell.suggest(rcldb, word, sug, reason)) { - cerr << "suggest failed: " << reason << endl; - exit(1); - } - for (list::iterator it = sug.begin(); it != sug.end(); it++) { - cout << *it << endl; - } + string lang = "en"; - exit(0); + Aspell aspell(rclconfig, lang); + + if (!aspell.init("/usr/local", reason)) { + cerr << "Init failed: " << reason << endl; + exit(1); + } + if (op_flags & OPT_b) { + if (!aspell.buildDict(rcldb, reason)) { + cerr << "buildDict failed: " << reason << endl; + exit(1); + } + } else { + list suggs; + if (!aspell.suggest(rcldb, word, suggs, reason)) { + cerr << "suggest failed: " << reason << endl; + exit(1); + } + cout << "Suggestions for " << word << ":" << endl; + for (list::iterator it = suggs.begin(); + it != suggs.end(); it++) { + cout << *it << endl; + } + } + exit(0); } #endif // TEST_RCLASPELL test driver diff --git a/src/aspell/rclaspell.h b/src/aspell/rclaspell.h index 8631a019..5a454cac 100644 --- a/src/aspell/rclaspell.h +++ b/src/aspell/rclaspell.h @@ -1,8 +1,17 @@ #ifndef _RCLASPELL_H_INCLUDED_ #define _RCLASPELL_H_INCLUDED_ -/* @(#$Id: rclaspell.h,v 1.1 2006-10-09 14:05:35 dockes Exp $ (C) 2006 J.F.Dockes */ +/* @(#$Id: rclaspell.h,v 1.2 2006-10-09 16:37:08 dockes Exp $ (C) 2006 J.F.Dockes */ -/// Class to interface an aspell speller. +/** + * Aspell speller interface class. + * + * Aspell is used to let the user find about spelling variations that may + * exist in the document set for a given word. + * A specific aspell dictionary is created out of all the terms in the + * xapian index, and we then use it to expand a term to spelling neighbours. + * We use the aspell C api for term expansion, but have + * to execute the program to create dictionaries. + */ #include #include @@ -22,19 +31,23 @@ class Aspell { Aspell(RclConfig *cnf, const string &lang) : m_conf(cnf), m_lang(lang), m_data(0) {}; ~Aspell(); + /** Check health */ bool ok(); - /** Get hold of the aspell command and shared library */ + + /** Find the aspell command and shared library, init function pointers */ bool init(const string &basedir, string &reason); + /** Build dictionary out of index term list. This is done at the end * of an indexing pass. */ bool buildDict(Rcl::Db &db, string &reason); - /** Return a list of possible expansions for user term */ - bool suggest(Rcl::Db &db, string &term, listsuggestions, + + /** Return a list of possible expansions for a given word */ + bool suggest(Rcl::Db &db, string &term, list &suggestions, string &reason); - string dicPath(); private: + string dicPath(); RclConfig *m_conf; string m_lang; AspellData *m_data; diff --git a/src/index/Makefile b/src/index/Makefile index aa178cd5..e46d182c 100644 --- a/src/index/Makefile +++ b/src/index/Makefile @@ -4,7 +4,7 @@ include $(depth)/mk/sysconf PROGS = recollindex csguess mimetype SRCS = recollindex.cpp -all: depend $(PROGS) +all: depend $(PROGS) $(BIGLIB) RECOLLINDEX_OBJS= recollindex.o $(BIGLIB) $(MIMELIB) recollindex : $(RECOLLINDEX_OBJS) @@ -29,8 +29,10 @@ trmimetype.o : mimetype.cpp $(CXX) $(ALL_CXXFLAGS) -DTEST_MIMETYPE -c -o trmimetype.o \ mimetype.cpp -$(BIGLIB): +$(BIGLIB): force cd $(depth)/lib;$(MAKE) +force: + $(MIMELIB): cd $(depth)/bincimapmime;$(MAKE) diff --git a/src/lib/Makefile b/src/lib/Makefile index dd445c28..f2586b51 100644 --- a/src/lib/Makefile +++ b/src/lib/Makefile @@ -8,8 +8,8 @@ LIBS = librcl.a all: $(LIBS) -OBJS = conftree.o csguess.o debuglog.o execmd.o idfile.o md5.o wipedir.o fstreewalk.o mh_html.o mh_mail.o mh_exec.o mh_text.o htmlparse.o indexer.o internfile.o mimehandler.o mimeparse.o mimetype.o myhtmlparse.o pathhash.o pathut.o rclconfig.o rcldb.o rclinit.o stemdb.o base64.o readfile.o smallut.o textsplit.o transcode.o unacpp.o history.o docseq.o sortseq.o copyfile.o -DEPS = conftree.dep.stamp csguess.dep.stamp debuglog.dep.stamp execmd.dep.stamp idfile.dep.stamp md5.dep.stamp wipedir.dep.stamp fstreewalk.dep.stamp mh_html.dep.stamp mh_mail.dep.stamp mh_exec.dep.stamp mh_text.dep.stamp htmlparse.dep.stamp indexer.dep.stamp internfile.dep.stamp mimehandler.dep.stamp mimeparse.dep.stamp mimetype.dep.stamp myhtmlparse.dep.stamp pathhash.dep.stamp pathut.dep.stamp rclconfig.dep.stamp rcldb.dep.stamp rclinit.dep.stamp stemdb.dep.stamp base64.dep.stamp readfile.dep.stamp smallut.dep.stamp textsplit.dep.stamp transcode.dep.stamp unacpp.dep.stamp history.dep.stamp docseq.dep.stamp sortseq.dep.stamp copyfile.dep.stamp +OBJS = conftree.o csguess.o debuglog.o execmd.o idfile.o md5.o wipedir.o fstreewalk.o mh_html.o mh_mail.o mh_exec.o mh_text.o htmlparse.o indexer.o internfile.o mimehandler.o mimeparse.o mimetype.o myhtmlparse.o pathhash.o pathut.o rclconfig.o rcldb.o rclinit.o stemdb.o base64.o readfile.o smallut.o textsplit.o transcode.o unacpp.o history.o docseq.o sortseq.o copyfile.o rclaspell.o +DEPS = conftree.dep.stamp csguess.dep.stamp debuglog.dep.stamp execmd.dep.stamp idfile.dep.stamp md5.dep.stamp wipedir.dep.stamp fstreewalk.dep.stamp mh_html.dep.stamp mh_mail.dep.stamp mh_exec.dep.stamp mh_text.dep.stamp htmlparse.dep.stamp indexer.dep.stamp internfile.dep.stamp mimehandler.dep.stamp mimeparse.dep.stamp mimetype.dep.stamp myhtmlparse.dep.stamp pathhash.dep.stamp pathut.dep.stamp rclconfig.dep.stamp rcldb.dep.stamp rclinit.dep.stamp stemdb.dep.stamp base64.dep.stamp readfile.dep.stamp smallut.dep.stamp textsplit.dep.stamp transcode.dep.stamp unacpp.dep.stamp history.dep.stamp docseq.dep.stamp sortseq.dep.stamp copyfile.dep.stamp rclaspell.dep.stamp librcl.a : $(DEPS) $(OBJS) unac.o ar ru librcl.a $(OBJS) unac.o @@ -87,6 +87,8 @@ sortseq.o : ../query/sortseq.cpp $(CXX) $(ALL_CXXFLAGS) -c ../query/sortseq.cpp copyfile.o : ../utils/copyfile.cpp $(CXX) $(ALL_CXXFLAGS) -c ../utils/copyfile.cpp +rclaspell.o : ../aspell/rclaspell.cpp + $(CXX) $(ALL_CXXFLAGS) -c ../aspell/rclaspell.cpp depend: $(DEPS) clean: rm -f $(OBJS) $(LIBS) $(DEPS) unac.o @@ -198,6 +200,9 @@ sortseq.dep.stamp : ../query/sortseq.cpp copyfile.dep.stamp : ../utils/copyfile.cpp $(CXX) -M $(ALL_CXXFLAGS) ../utils/copyfile.cpp > copyfile.dep touch copyfile.dep.stamp +rclaspell.dep.stamp : ../aspell/rclaspell.cpp + $(CXX) -M $(ALL_CXXFLAGS) ../aspell/rclaspell.cpp > rclaspell.dep + touch rclaspell.dep.stamp include conftree.dep include csguess.dep include debuglog.dep @@ -233,3 +238,4 @@ include history.dep include docseq.dep include sortseq.dep include copyfile.dep +include rclaspell.dep diff --git a/src/lib/mkMake b/src/lib/mkMake index 1a4bc9f8..97b388c4 100755 --- a/src/lib/mkMake +++ b/src/lib/mkMake @@ -21,7 +21,7 @@ SRCS="${depth}/utils/conftree.cpp ${depth}/index/csguess.cpp \ ${depth}/utils/transcode.cpp ${depth}/common/unacpp.cpp \ ${depth}/query/history.cpp \ ${depth}/query/docseq.cpp ${depth}/query/sortseq.cpp \ - ${depth}/utils/copyfile.cpp" + ${depth}/utils/copyfile.cpp ${depth}/aspell/rclaspell.cpp" for c in $SRCS;do diff --git a/src/makesrcdist.sh b/src/makesrcdist.sh index edb78d80..1d65bb34 100644 --- a/src/makesrcdist.sh +++ b/src/makesrcdist.sh @@ -1,5 +1,5 @@ #!/bin/sh -# @(#$Id: makesrcdist.sh,v 1.10 2006-09-23 13:13:49 dockes Exp $ (C) 2005 J.F.Dockes +# @(#$Id: makesrcdist.sh,v 1.11 2006-10-09 16:37:08 dockes Exp $ (C) 2005 J.F.Dockes # A shell-script to make a recoll source distribution #set -x @@ -71,7 +71,7 @@ diff $topdir/doc/user/u1.html $topdir/doc/user/usermanual.html mv -f $topdir/doc/user/u1.html $topdir/doc/user/usermanual.html # We tag .. as there is the 'packaging/' directory in there -CVSTAG="RECOLL-$versionforcvs" +CVSTAG="RECOLL_$versionforcvs" [ $dotag = "yes" ] && (cd ..;cvs tag -F $CVSTAG .) out=recoll-$version.tar.gz diff --git a/src/rcldb/rcldb.cpp b/src/rcldb/rcldb.cpp index 91618c3a..37f98f87 100644 --- a/src/rcldb/rcldb.cpp +++ b/src/rcldb/rcldb.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: rcldb.cpp,v 1.79 2006-09-29 08:26:02 dockes Exp $ (C) 2004 J.F.Dockes"; +static char rcsid[] = "@(#$Id: rcldb.cpp,v 1.80 2006-10-09 16:37:08 dockes Exp $ (C) 2004 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -890,7 +890,7 @@ static void stringToXapianQueries(const string &iq, dumb_string(term, term1); // Possibly perform stem compression/expansion if (!nostemexp && (opts & Db::QO_STEM)) { - exp = m_ndb->stemExpand(stemlang,term1); + exp = m_ndb->stemExpand(stemlang, term1); } else { exp.push_back(term1); } @@ -1117,6 +1117,60 @@ list Db::completions(const string &root, const string &lang, int max) return res; } +/** Term list walking. */ +class TermIter { +public: + Xapian::TermIterator it; + Xapian::Database db; +}; +TermIter *Db::termWalkOpen() +{ + if (!m_ndb || !m_ndb->m_isopen) + return 0; + TermIter *tit = new TermIter; + if (tit) { + tit->db = m_ndb->m_iswritable ? m_ndb->wdb: m_ndb->db; + tit->it = tit->db.allterms_begin(); + } + return tit; +} +bool Db::termWalkNext(TermIter *tit, string &term) +{ + + if (tit && tit->it != tit->db.allterms_end()) { + term = *(tit->it)++; + return true; + } + return false; +} +void Db::termWalkClose(TermIter *tit) +{ + delete tit; +} + + +bool Db::termExists(const string& word) +{ + if (!m_ndb || !m_ndb->m_isopen) + return 0; + Xapian::Database db = m_ndb->m_iswritable ? m_ndb->wdb: m_ndb->db; + if (!db.term_exists(word)) + return false; + return true; +} + +bool Db::stemDiffers(const string& lang, const string& word, + const string& base) +{ + Xapian::Stem stemmer(lang); + if (!stemmer.stem_word(word).compare(stemmer.stem_word(base))) { + LOGDEB2(("Rcl::Db::stemDiffers: same for %s and %s\n", + word.c_str(), base.c_str())); + return false; + } + return true; +} + bool Db::getQueryTerms(list& terms) { if (!m_ndb) diff --git a/src/rcldb/rcldb.h b/src/rcldb/rcldb.h index b10b8198..40bcaede 100644 --- a/src/rcldb/rcldb.h +++ b/src/rcldb/rcldb.h @@ -16,7 +16,7 @@ */ #ifndef _DB_H_INCLUDED_ #define _DB_H_INCLUDED_ -/* @(#$Id: rcldb.h,v 1.36 2006-09-13 13:53:35 dockes Exp $ (C) 2004 J.F.Dockes */ +/* @(#$Id: rcldb.h,v 1.37 2006-10-09 16:37:08 dockes Exp $ (C) 2004 J.F.Dockes */ #include #include @@ -105,6 +105,7 @@ class Doc { class AdvSearchData; class Native; +class TermIter; /** * Wrapper class for the native database. @@ -170,9 +171,22 @@ class Db { /** Get a list of existing stemming databases */ std::list getStemLangs(); + /** Retrieve main database directory */ string getDbDir(); + + /** Set parameters for synthetic abstract generation */ void setAbstractParams(int idxTrunc, int synthLen, int syntCtxLen); + /** Whole term list walking. */ + TermIter *termWalkOpen(); + bool termWalkNext(TermIter *, string &term); + void termWalkClose(TermIter *); + /** Test term existence */ + bool termExists(const string& term); + /** Test if terms stem to different roots. */ + bool stemDiffers(const string& lang, const string& term, + const string& base); + private: string m_filterTopDir; // Current query filter on subtree top directory diff --git a/src/rcldb/stemdb.cpp b/src/rcldb/stemdb.cpp index e9447038..02496a5a 100644 --- a/src/rcldb/stemdb.cpp +++ b/src/rcldb/stemdb.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: stemdb.cpp,v 1.4 2006-09-20 06:21:43 dockes Exp $ (C) 2005 J.F.Dockes"; +static char rcsid[] = "@(#$Id: stemdb.cpp,v 1.5 2006-10-09 16:37:08 dockes Exp $ (C) 2005 J.F.Dockes"; #endif /** @@ -254,7 +254,8 @@ list stemExpand(const string& dbdir, const string& lang, LOGDEB(("stemExpand: %s -> %s\n", stem.c_str(), stringlistdisp(explist).c_str())); } catch (...) { - LOGERR(("stemExpand: error accessing stem db\n")); + LOGERR(("stemExpand: error accessing stem db. dbdir [%s] lang [%s]\n", + dbdir.c_str(), lang.c_str())); explist.push_back(term); return explist; } diff --git a/src/utils/execmd.cpp b/src/utils/execmd.cpp index 80202c0f..bb57e53d 100644 --- a/src/utils/execmd.cpp +++ b/src/utils/execmd.cpp @@ -1,5 +1,5 @@ #ifndef lint -static char rcsid[] = "@(#$Id: execmd.cpp,v 1.17 2006-04-03 09:42:47 dockes Exp $ (C) 2004 J.F.Dockes"; +static char rcsid[] = "@(#$Id: execmd.cpp,v 1.18 2006-10-09 16:37:08 dockes Exp $ (C) 2004 J.F.Dockes"; #endif /* * This program is free software; you can redistribute it and/or modify @@ -53,7 +53,9 @@ void ExecCmd::putenv(const string &ea) * raised in the callback */ class ExecCmdRsrc { public: + // Pipe for data going to the command int pipein[2]; + // Pipe for data coming out int pipeout[2]; pid_t pid; ExecCmdRsrc() { @@ -124,26 +126,25 @@ int ExecCmd::doexec(const string &cmd, const list& args, } if (e.pid) { + // Father process if (input) { close(e.pipein[0]); e.pipein[0] = -1; + fcntl(e.pipein[1], F_SETFL, O_NONBLOCK); } if (output) { close(e.pipeout[1]); e.pipeout[1] = -1; + fcntl(e.pipeout[0], F_SETFL, O_NONBLOCK); } - fd_set readfds, writefds; - struct timeval tv; - tv.tv_sec = 1; - tv.tv_usec = 0; if (input || output) { - if (input) - fcntl(e.pipein[1], F_SETFL, O_NONBLOCK); - if (output) - fcntl(e.pipeout[0], F_SETFL, O_NONBLOCK); unsigned int nwritten = 0; int nfds = MAX(e.pipein[1], e.pipeout[0]) + 1; + fd_set readfds, writefds; + struct timeval tv; + tv.tv_sec = m_timeoutMs / 1000; + tv.tv_usec = 1000 * (m_timeoutMs % 1000); for(; nfds > 0;) { if (m_cancelRequest) break; @@ -240,7 +241,22 @@ int ExecCmd::doexec(const string &cmd, const list& args, e.pipeout[1] = -1; } } - + // Do we need to redirect stderr ? + if (!m_stderrFile.empty()) { + int fd = open(m_stderrFile.c_str(), O_WRONLY|O_CREAT +#ifdef O_APPEND + |O_APPEND +#endif + , 0600); + if (fd < 0) { + close(2); + } else { + if (fd != 2) { + dup2(fd, 2); + } + lseek(2, 0, 2); + } + } e.reset(); // Allocate arg vector (2 more for arg0 + final 0) @@ -300,10 +316,10 @@ const char *data = "Une ligne de donnees\n"; class MEAdv : public ExecCmdAdvise { public: ExecCmd *cmd; - void newData(int) { - cerr << "New Data!" << endl; - CancelCheck::instance().setCancel(); - CancelCheck::instance().checkCancel(); + void newData(int cnt) { + cerr << "newData(" << cnt << ")" << endl; + // CancelCheck::instance().setCancel(); + // CancelCheck::instance().checkCancel(); // cmd->setCancel(); } }; @@ -325,10 +341,13 @@ int main(int argc, const char **argv) MEAdv adv; adv.cmd = &mexec; mexec.setAdvise(&adv); + mexec.setTimeout(500); + mexec.setStderr("/tmp/trexecStderr"); + string input, output; input = data; string *ip = 0; - //ip = &input; + ip = &input; mexec.putenv("TESTVARIABLE1=TESTVALUE1"); mexec.putenv("TESTVARIABLE2=TESTVALUE2"); mexec.putenv("TESTVARIABLE3=TESTVALUE3"); diff --git a/src/utils/execmd.h b/src/utils/execmd.h index 6f408be2..383134f2 100644 --- a/src/utils/execmd.h +++ b/src/utils/execmd.h @@ -16,10 +16,14 @@ */ #ifndef _EXECMD_H_INCLUDED_ #define _EXECMD_H_INCLUDED_ -/* @(#$Id: execmd.h,v 1.8 2006-01-30 11:15:28 dockes Exp $ (C) 2004 J.F.Dockes */ +/* @(#$Id: execmd.h,v 1.9 2006-10-09 16:37:08 dockes Exp $ (C) 2004 J.F.Dockes */ #include #include +#ifndef NO_NAMESPACES +using std::list; +using std::string; +#endif /** Callback function object to advise of new data arrival (or just heartbeat) * if cnt is 0 */ class ExecCmdAdvise { @@ -37,46 +41,66 @@ class ExecCmd { /** * Execute command. * - * Both input and output can be specified, and - * asynchronous io is used to prevent blocking. This wont work if - * input and output need to be synchronized (ie: Q/A), but ok for - * filtering. + * Both input and output can be specified, and asynchronous + * io (select-based) is used to prevent blocking. This will not + * work if input and output need to be synchronized (ie: Q/A), but + * works ok for filtering. * The function is exception-safe. In case an exception occurs in the * advise callback, fds and pids will be cleaned-up properly. * * @param cmd the program to execute. This must be an absolute file name * or exist in the PATH. * @param args the argument list (NOT including argv[0]). - * @param input Input to send to the command. - * @param output Output from the command. + * @param input Input to send TO the command. + * @param output Output FROM the command. * @return the exec ouput status (0 if ok). */ - int doexec(const std::string &cmd, const std::list& args, - const std::string *input = 0, - std::string *output = 0); + int doexec(const string &cmd, const list& args, + const string *input = 0, + string *output = 0); /** - * Add/replace environment variable before executing command. This should - * be called before doexec of course (possibly multiple times for several - * variables). + * Add/replace environment variable before executing command. This must + * be called before doexec to have an effect (possibly multiple + * times for several variables). * @param envassign an environment assignment string (name=value) */ - void putenv(const std::string &envassign); + void putenv(const string &envassign); - /** Set function object to call whenever new data is available */ + /** + * Set function object to call whenever new data is available or on + * select timeout. + */ void setAdvise(ExecCmdAdvise *adv) {m_advise = adv;} - /** Cancel exec. This can be called from another thread or from the - * advise callback, which could also raise an exception to accomplish - * the same thing + /** + * Set select timeout in milliseconds. The default is 1 S. + */ + void setTimeout(int mS) {if (mS > 30) m_timeoutMs = mS;} + + /** + * Set destination for stderr data. The default is to let it alone (will + * usually go to the terminal or to wherever the desktop messages go). + * There is currently no option to put stderr data into a program variable + * If the parameter can't be opened for writing, the command's + * stderr will be closed. + */ + void setStderr(const string &stderrFile) {m_stderrFile = stderrFile;} + + /** + * Cancel/kill command. This can be called from another thread or + * from the advise callback, which could also raise an exception to + * accomplish the same thing */ void setCancel() {m_cancelRequest = true;} - ExecCmd() : m_advise(0), m_cancelRequest(false) {} + ExecCmd() : m_advise(0), m_cancelRequest(false), m_timeoutMs(1000) {} private: - std::list m_env; + list m_env; ExecCmdAdvise *m_advise; - bool m_cancelRequest; + bool m_cancelRequest; + int m_timeoutMs; + string m_stderrFile; };