X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2Fispell.C;h=9ac2625b503967d702dbc6c2c73f9a1018ee8cbc;hb=8a5d04fd84e344a1ba1cfd3f5cce67d41874cfa3;hp=c8e8721ee063c1e814f279288932d21c78ba6ddb;hpb=6b5c9696b6669b48062bec96707c78d605e1af58;p=lyx.git diff --git a/src/ispell.C b/src/ispell.C index c8e8721ee0..9ac2625b50 100644 --- a/src/ispell.C +++ b/src/ispell.C @@ -19,7 +19,7 @@ #include #include -// FIXME: do we need any of this horrible gook ? +// FIXME: do we need any of this horrible gook ? #if TIME_WITH_SYS_TIME # include # include @@ -43,12 +43,15 @@ #endif #include "LString.h" -#include "support/lstrings.h" #include "lyxrc.h" #include "language.h" #include "debug.h" #include "encoding.h" #include "ispell.h" +#include "WordLangTuple.h" + +#include "support/forkedcall.h" +#include "support/lstrings.h" #ifndef CXX_GLOBAL_CSTD using std::strcpy; @@ -60,190 +63,246 @@ using std::strstr; using std::endl; namespace { - /// pid for the `ispell' process. - pid_t isp_pid = -1; + +/// pid for the `ispell' process. +pid_t isp_pid = -1; + +class LaunchIspell : public ForkedProcess { +public: + /// + LaunchIspell(BufferParams const & p, string const & l, + int * in, int * out) + : params(p), lang(l), pipein(in), pipeout(out) {} + /// + virtual ForkedProcess * clone() const { + return new LaunchIspell(*this); + } + /// + int start(); +private: + /// + virtual int generateChild(); + + /// + BufferParams const & params; + string const & lang; + int * const pipein; + int * const pipeout; +}; + + +int LaunchIspell::start() +{ + command_ = "ispell"; + return runNonBlocking(); +} + + +int LaunchIspell::generateChild() +{ + isp_pid = fork(); + + if (isp_pid != 0) { + // failed (-1) or parent process (>0) + return isp_pid; + } + + // child process + dup2(pipein[0], STDIN_FILENO); + dup2(pipeout[1], STDOUT_FILENO); + ::close(pipein[0]); + ::close(pipein[1]); + ::close(pipeout[0]); + ::close(pipeout[1]); + + char * argv[14]; + int argc = 0; + + char * tmp = new char[lyxrc.isp_command.length() + 1]; + lyxrc.isp_command.copy(tmp, lyxrc.isp_command.length()); + tmp[lyxrc.isp_command.length()] = '\0'; + argv[argc++] = tmp; + tmp = new char[3]; + string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode + argv[argc++] = tmp; + + if (lang != "default") { + tmp = new char[3]; + string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file + argv[argc++] = tmp; + tmp = new char[lang.length() + 1]; + lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0'; + argv[argc++] = tmp; + } + + if (lyxrc.isp_accept_compound) { + // Consider run-together words as legal compounds + tmp = new char[3]; + string("-C").copy(tmp, 2); tmp[2] = '\0'; + argv[argc++] = tmp; + } else { + // Report run-together words with + // missing blanks as errors + tmp = new char[3]; + string("-B").copy(tmp, 2); tmp[2] = '\0'; + argv[argc++] = tmp; + } + if (lyxrc.isp_use_esc_chars) { + // Specify additional characters that + // can be part of a word + tmp = new char[3]; + string("-w").copy(tmp, 2); tmp[2] = '\0'; + argv[argc++] = tmp; + // Put the escape chars in ""s + string tms = '"' + lyxrc.isp_esc_chars + '"'; + tmp = new char[tms.length() + 1]; + tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0'; + argv[argc++] = tmp; + } + if (lyxrc.isp_use_pers_dict) { + // Specify an alternate personal dictionary + tmp = new char[3]; + string("-p").copy(tmp, 2); + tmp[2]= '\0'; + argv[argc++] = tmp; + tmp = new char[lyxrc.isp_pers_dict.length() + 1]; + lyxrc.isp_pers_dict.copy(tmp, lyxrc.isp_pers_dict.length()); + tmp[lyxrc.isp_pers_dict.length()] = '\0'; + argv[argc++] = tmp; + } + if (lyxrc.isp_use_input_encoding && + params.inputenc != "default") { + string enc = (params.inputenc == "auto") + ? params.language->encoding()->LatexName() + : params.inputenc; + string::size_type n = enc.length(); + tmp = new char[3]; + string("-T").copy(tmp, 2); + tmp[2] = '\0'; + argv[argc++] = tmp; // Input encoding + tmp = new char[n + 1]; + enc.copy(tmp, n); + tmp[n] = '\0'; + argv[argc++] = tmp; + } + + argv[argc++] = 0; + + execvp(argv[0], const_cast(argv)); + + // free the memory used by string::copy in the + // setup of argv + for (int i = 0; i < argc - 1; ++i) + delete[] argv[i]; + + lyxerr << "LyX: Failed to start ispell!" << endl; + _exit(0); } +} // namespace anon + + ISpell::ISpell(BufferParams const & params, string const & lang) : str(0) { static char o_buf[BUFSIZ]; // jc: it could be smaller int pipein[2]; int pipeout[2]; - char * argv[14]; - int argc; isp_pid = -1; if (pipe(pipein) == -1 || pipe(pipeout) == -1) { lyxerr << "LyX: Can't create pipe for spellchecker!" << endl; - goto END; + setError(); + return; } if ((out = fdopen(pipein[1], "w")) == 0) { lyxerr << "LyX: Can't create stream for pipe for spellchecker!" << endl; - goto END; + setError(); + return; } if ((in = fdopen(pipeout[0], "r")) == 0) { lyxerr <<"LyX: Can't create stream for pipe for spellchecker!" << endl; - goto END; + setError(); + return; } setvbuf(out, o_buf, _IOLBF, BUFSIZ); isp_fd = pipeout[0]; - isp_pid = fork(); - + LaunchIspell childprocess(params, lang, pipein, pipeout); + isp_pid = childprocess.start(); if (isp_pid == -1) { lyxerr << "LyX: Can't create child process for spellchecker!" << endl; - goto END; + setError(); + return; } - if (isp_pid == 0) { - /* child process */ - dup2(pipein[0], STDIN_FILENO); - dup2(pipeout[1], STDOUT_FILENO); - ::close(pipein[0]); - ::close(pipein[1]); + setError(); + /* Parent process: Read ispells identification message */ + // Hmm...what are we using this id msg for? Nothing? (Lgb) + // Actually I used it to tell if it's truly Ispell or if it's + // aspell -- (kevinatk@home.com) + // But no code actually used the results for anything useful + // so I removed it again. Perhaps we can remove this code too. + // - jbl + char buf[2048]; + fd_set infds; + struct timeval tv; + int retval = 0; + FD_ZERO(&infds); + FD_SET(pipeout[0], &infds); + tv.tv_sec = 15; // fifteen second timeout. Probably too much, + // but it can't really hurt. + tv.tv_usec = 0; + + // Configure provides us with macros which are supposed to do + // the right typecast. + retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1), + SELECT_TYPE_ARG234 (&infds), + 0, + 0, + SELECT_TYPE_ARG5 (&tv)); + + if (retval > 0) { + // Ok, do the reading. We don't have to FD_ISSET since + // there is only one fd in infds. + fgets(buf, 2048, in); + + fputs("!\n", out); // Set terse mode (silently accept correct words) + + } else if (retval == 0) { + // timeout. Give nice message to user. + lyxerr << "Ispell read timed out, what now?" << endl; + // This probably works but could need some thought + isp_pid = -1; ::close(pipeout[0]); ::close(pipeout[1]); + ::close(pipein[0]); + ::close(pipein[1]); + isp_fd = -1; + } else { + // Select returned error + lyxerr << "Select on ispell returned error, what now?" << endl; + } +} - argc = 0; - char * tmp = new char[lyxrc.isp_command.length() + 1]; - lyxrc.isp_command.copy(tmp, lyxrc.isp_command.length()); - tmp[lyxrc.isp_command.length()] = '\0'; - argv[argc++] = tmp; - tmp = new char[3]; - string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode - argv[argc++] = tmp; - if (lang != "default") { - tmp = new char[3]; - string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file - argv[argc++] = tmp; - tmp = new char[lang.length() + 1]; - lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0'; - argv[argc++] = tmp; - } - - if (lyxrc.isp_accept_compound) { - // Consider run-together words as legal compounds - tmp = new char[3]; - string("-C").copy(tmp, 2); tmp[2] = '\0'; - argv[argc++] = tmp; - } else { - // Report run-together words with - // missing blanks as errors - tmp = new char[3]; - string("-B").copy(tmp, 2); tmp[2] = '\0'; - argv[argc++] = tmp; - } - if (lyxrc.isp_use_esc_chars) { - // Specify additional characters that - // can be part of a word - tmp = new char[3]; - string("-w").copy(tmp, 2); tmp[2] = '\0'; - argv[argc++] = tmp; - // Put the escape chars in ""s - string tms = "\"" + lyxrc.isp_esc_chars + "\""; - tmp = new char[tms.length() + 1]; - tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0'; - argv[argc++] = tmp; - } - if (lyxrc.isp_use_pers_dict) { - // Specify an alternate personal dictionary - tmp = new char[3]; - string("-p").copy(tmp, 2); - tmp[2]= '\0'; - argv[argc++] = tmp; - tmp = new char[lyxrc.isp_pers_dict.length() + 1]; - lyxrc.isp_pers_dict.copy(tmp, lyxrc.isp_pers_dict.length()); - tmp[lyxrc.isp_pers_dict.length()] = '\0'; - argv[argc++] = tmp; - } - if (lyxrc.isp_use_input_encoding && - params.inputenc != "default") { - string enc = (params.inputenc == "auto") - ? params.language->encoding()->LatexName() - : params.inputenc; - string::size_type n = enc.length(); - tmp = new char[3]; - string("-T").copy(tmp, 2); - tmp[2] = '\0'; - argv[argc++] = tmp; // Input encoding - tmp = new char[n + 1]; - enc.copy(tmp, n); - tmp[n] = '\0'; - argv[argc++] = tmp; - } - - argv[argc++] = 0; - - execvp(argv[0], const_cast(argv)); - - // free the memory used by string::copy in the - // setup of argv - for (int i = 0; i < argc - 1; ++i) - delete[] argv[i]; - - lyxerr << "LyX: Failed to start ispell!" << endl; - _exit(0); - } - { - /* Parent process: Read ispells identification message */ - // Hmm...what are we using this id msg for? Nothing? (Lgb) - // Actually I used it to tell if it's truly Ispell or if it's - // aspell -- (kevinatk@home.com) - // But no code actually used the results for anything useful - // so I removed it again. Perhaps we can remove this code too. - // - jbl - char buf[2048]; - fd_set infds; - struct timeval tv; - int retval = 0; - FD_ZERO(&infds); - FD_SET(pipeout[0], &infds); - tv.tv_sec = 15; // fifteen second timeout. Probably too much, - // but it can't really hurt. - tv.tv_usec = 0; - - // Configure provides us with macros which are supposed to do - // the right typecast. - retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1), - SELECT_TYPE_ARG234 (&infds), - 0, - 0, - SELECT_TYPE_ARG5 (&tv)); - - if (retval > 0) { - // Ok, do the reading. We don't have to FD_ISSET since - // there is only one fd in infds. - fgets(buf, 2048, in); - - fputs("!\n", out); // Set terse mode (silently accept correct words) - - } else if (retval == 0) { - // timeout. Give nice message to user. - lyxerr << "Ispell read timed out, what now?" << endl; - // This probably works but could need some thought - isp_pid = -1; - ::close(pipeout[0]); - ::close(pipeout[1]); - ::close(pipein[0]); - ::close(pipein[1]); - isp_fd = -1; - } else { - // Select returned error - lyxerr << "Select on ispell returned error, what now?" << endl; - } - } - END: +ISpell::~ISpell() +{ + delete[] str; +} + + +void ISpell::setError() +{ if (isp_pid == -1) { error_ = "\n\n" @@ -259,25 +318,6 @@ ISpell::ISpell(BufferParams const & params, string const & lang) } -ISpell::~ISpell() -{ - delete[] str; -} - - -/* FIXME: this is a minimalist solution until the above - * code is able to work with forkedcall.h. We only need - * to reap the zombies here. - */ -void reapSpellchecker(void) -{ - if (isp_pid == -1) - return; - - waitpid(isp_pid, 0, WNOHANG); -} - - string const ISpell::nextMiss() { if (str == 0 || *(e+1) == '\0') @@ -308,7 +348,7 @@ enum ISpell::Result ISpell::check(WordLangTuple const & word) // FIXME Please rewrite to use string. Result res; - + ::fputs(word.word().c_str(), out); ::fputc('\n', out);