3 * Copyright 2002 the LyX Team
4 * Read the file COPYING
7 * \author John Levon <levon@movementarian.org>
13 #pragma implementation
16 #include <sys/types.h>
22 // FIXME: do we need any of this horrible gook ?
23 #if TIME_WITH_SYS_TIME
24 # include <sys/time.h>
28 # include <sys/time.h>
34 #ifdef HAVE_SYS_SELECT_H
35 # ifdef HAVE_STRINGS_H
36 // <strings.h> is needed at least on AIX because FD_ZERO uses bzero().
37 // BUT we cannot include both string.h and strings.h on Irix 6.5 :(
42 #include <sys/select.h>
46 #include "support/lstrings.h"
53 #ifndef CXX_GLOBAL_CSTD
63 /// pid for the `ispell' process.
68 ISpell::ISpell(BufferParams const & params, string const & lang)
71 static char o_buf[BUFSIZ]; // jc: it could be smaller
79 if (pipe(pipein) == -1 || pipe(pipeout) == -1) {
80 lyxerr << "LyX: Can't create pipe for spellchecker!" << endl;
84 if ((out = fdopen(pipein[1], "w")) == 0) {
85 lyxerr << "LyX: Can't create stream for pipe for spellchecker!"
90 if ((in = fdopen(pipeout[0], "r")) == 0) {
91 lyxerr <<"LyX: Can't create stream for pipe for spellchecker!"
96 setvbuf(out, o_buf, _IOLBF, BUFSIZ);
103 lyxerr << "LyX: Can't create child process for spellchecker!"
110 dup2(pipein[0], STDIN_FILENO);
111 dup2(pipeout[1], STDOUT_FILENO);
118 char * tmp = new char[lyxrc.isp_command.length() + 1];
119 lyxrc.isp_command.copy(tmp, lyxrc.isp_command.length());
120 tmp[lyxrc.isp_command.length()] = '\0';
123 string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode
126 if (lang != "default") {
128 string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file
130 tmp = new char[lang.length() + 1];
131 lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0';
135 if (lyxrc.isp_accept_compound) {
136 // Consider run-together words as legal compounds
138 string("-C").copy(tmp, 2); tmp[2] = '\0';
141 // Report run-together words with
142 // missing blanks as errors
144 string("-B").copy(tmp, 2); tmp[2] = '\0';
147 if (lyxrc.isp_use_esc_chars) {
148 // Specify additional characters that
149 // can be part of a word
151 string("-w").copy(tmp, 2); tmp[2] = '\0';
153 // Put the escape chars in ""s
154 string tms = "\"" + lyxrc.isp_esc_chars + "\"";
155 tmp = new char[tms.length() + 1];
156 tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0';
159 if (lyxrc.isp_use_pers_dict) {
160 // Specify an alternate personal dictionary
162 string("-p").copy(tmp, 2);
165 tmp = new char[lyxrc.isp_pers_dict.length() + 1];
166 lyxrc.isp_pers_dict.copy(tmp, lyxrc.isp_pers_dict.length());
167 tmp[lyxrc.isp_pers_dict.length()] = '\0';
170 if (lyxrc.isp_use_input_encoding &&
171 params.inputenc != "default") {
172 string enc = (params.inputenc == "auto")
173 ? params.language->encoding()->LatexName()
175 string::size_type n = enc.length();
177 string("-T").copy(tmp, 2);
179 argv[argc++] = tmp; // Input encoding
180 tmp = new char[n + 1];
188 execvp(argv[0], const_cast<char * const *>(argv));
190 // free the memory used by string::copy in the
192 for (int i = 0; i < argc - 1; ++i)
195 lyxerr << "LyX: Failed to start ispell!" << endl;
199 /* Parent process: Read ispells identification message */
200 // Hmm...what are we using this id msg for? Nothing? (Lgb)
201 // Actually I used it to tell if it's truly Ispell or if it's
202 // aspell -- (kevinatk@home.com)
203 // But no code actually used the results for anything useful
204 // so I removed it again. Perhaps we can remove this code too.
211 FD_SET(pipeout[0], &infds);
212 tv.tv_sec = 15; // fifteen second timeout. Probably too much,
213 // but it can't really hurt.
216 // Configure provides us with macros which are supposed to do
217 // the right typecast.
218 retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1),
219 SELECT_TYPE_ARG234 (&infds),
222 SELECT_TYPE_ARG5 (&tv));
225 // Ok, do the reading. We don't have to FD_ISSET since
226 // there is only one fd in infds.
227 fgets(buf, 2048, in);
229 fputs("!\n", out); // Set terse mode (silently accept correct words)
231 } else if (retval == 0) {
232 // timeout. Give nice message to user.
233 lyxerr << "Ispell read timed out, what now?" << endl;
234 // This probably works but could need some thought
242 // Select returned error
243 lyxerr << "Select on ispell returned error, what now?" << endl;
250 "The spellcheck-process has died for some reason.\n"
251 "*One* possible reason could be that you do not have\n"
252 "a dictionary file for the language of this document\n"
254 "Check your spellchecker or set another dictionary\n"
255 "in the Spellchecker Options menu.\n\n";
268 /* FIXME: this is a minimalist solution until the above
269 * code is able to work with forkedcall.h. We only need
270 * to reap the zombies here.
272 void reapSpellchecker(void)
277 waitpid(isp_pid, 0, WNOHANG);
281 string const ISpell::nextMiss()
283 if (str == 0 || *(e+1) == '\0')
286 e = strpbrk(b, ",\n");
296 return isp_pid != -1;
300 void ISpell::cleanUp()
306 enum ISpell::Result ISpell::check(WordLangTuple const & word)
308 // FIXME Please rewrite to use string.
312 ::fputs(word.word().c_str(), out);
316 ::fgets(buf, 1024, in);
318 // I think we have to check if ispell is still alive here because
319 // the signal-handler could have disabled blocking on the fd
336 case '#': // Not found, no near misses and guesses
339 case '?': // Not found, and no near misses, but guesses (guesses are ignored)
340 case '&': // Not found, but we have near misses
343 char * p = strpbrk(buf, ":");
344 str = new char[strlen(p) + 1];
349 default: // This shouldn't happen, but you know Murphy
355 /* wait for ispell to finish */
365 // Note: If you decide to optimize this out when it is not
366 // needed please note that when Aspell is used this command
367 // is also needed to save the replacement dictionary.
368 // -- Kevin Atkinson (kevinatk@home.com)
370 fputs("#\n", out); // Save personal dictionary
377 void ISpell::insert(WordLangTuple const & word)
379 ::fputc('*', out); // Insert word in personal dictionary
380 ::fputs(word.word().c_str(), out);
385 void ISpell::accept(WordLangTuple const & word)
387 ::fputc('@', out); // Accept in this session
388 ::fputs(word.word().c_str(), out);
393 string const ISpell::error()