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"
52 #include "WordLangTuple.h"
54 #ifndef CXX_GLOBAL_CSTD
64 /// pid for the `ispell' process.
69 ISpell::ISpell(BufferParams const & params, string const & lang)
72 static char o_buf[BUFSIZ]; // jc: it could be smaller
80 if (pipe(pipein) == -1 || pipe(pipeout) == -1) {
81 lyxerr << "LyX: Can't create pipe for spellchecker!" << endl;
85 if ((out = fdopen(pipein[1], "w")) == 0) {
86 lyxerr << "LyX: Can't create stream for pipe for spellchecker!"
91 if ((in = fdopen(pipeout[0], "r")) == 0) {
92 lyxerr <<"LyX: Can't create stream for pipe for spellchecker!"
97 setvbuf(out, o_buf, _IOLBF, BUFSIZ);
104 lyxerr << "LyX: Can't create child process for spellchecker!"
111 dup2(pipein[0], STDIN_FILENO);
112 dup2(pipeout[1], STDOUT_FILENO);
119 char * tmp = new char[lyxrc.isp_command.length() + 1];
120 lyxrc.isp_command.copy(tmp, lyxrc.isp_command.length());
121 tmp[lyxrc.isp_command.length()] = '\0';
124 string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode
127 if (lang != "default") {
129 string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file
131 tmp = new char[lang.length() + 1];
132 lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0';
136 if (lyxrc.isp_accept_compound) {
137 // Consider run-together words as legal compounds
139 string("-C").copy(tmp, 2); tmp[2] = '\0';
142 // Report run-together words with
143 // missing blanks as errors
145 string("-B").copy(tmp, 2); tmp[2] = '\0';
148 if (lyxrc.isp_use_esc_chars) {
149 // Specify additional characters that
150 // can be part of a word
152 string("-w").copy(tmp, 2); tmp[2] = '\0';
154 // Put the escape chars in ""s
155 string tms = "\"" + lyxrc.isp_esc_chars + "\"";
156 tmp = new char[tms.length() + 1];
157 tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0';
160 if (lyxrc.isp_use_pers_dict) {
161 // Specify an alternate personal dictionary
163 string("-p").copy(tmp, 2);
166 tmp = new char[lyxrc.isp_pers_dict.length() + 1];
167 lyxrc.isp_pers_dict.copy(tmp, lyxrc.isp_pers_dict.length());
168 tmp[lyxrc.isp_pers_dict.length()] = '\0';
171 if (lyxrc.isp_use_input_encoding &&
172 params.inputenc != "default") {
173 string enc = (params.inputenc == "auto")
174 ? params.language->encoding()->LatexName()
176 string::size_type n = enc.length();
178 string("-T").copy(tmp, 2);
180 argv[argc++] = tmp; // Input encoding
181 tmp = new char[n + 1];
189 execvp(argv[0], const_cast<char * const *>(argv));
191 // free the memory used by string::copy in the
193 for (int i = 0; i < argc - 1; ++i)
196 lyxerr << "LyX: Failed to start ispell!" << endl;
200 /* Parent process: Read ispells identification message */
201 // Hmm...what are we using this id msg for? Nothing? (Lgb)
202 // Actually I used it to tell if it's truly Ispell or if it's
203 // aspell -- (kevinatk@home.com)
204 // But no code actually used the results for anything useful
205 // so I removed it again. Perhaps we can remove this code too.
212 FD_SET(pipeout[0], &infds);
213 tv.tv_sec = 15; // fifteen second timeout. Probably too much,
214 // but it can't really hurt.
217 // Configure provides us with macros which are supposed to do
218 // the right typecast.
219 retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1),
220 SELECT_TYPE_ARG234 (&infds),
223 SELECT_TYPE_ARG5 (&tv));
226 // Ok, do the reading. We don't have to FD_ISSET since
227 // there is only one fd in infds.
228 fgets(buf, 2048, in);
230 fputs("!\n", out); // Set terse mode (silently accept correct words)
232 } else if (retval == 0) {
233 // timeout. Give nice message to user.
234 lyxerr << "Ispell read timed out, what now?" << endl;
235 // This probably works but could need some thought
243 // Select returned error
244 lyxerr << "Select on ispell returned error, what now?" << endl;
251 "The spellcheck-process has died for some reason.\n"
252 "*One* possible reason could be that you do not have\n"
253 "a dictionary file for the language of this document\n"
255 "Check your spellchecker or set another dictionary\n"
256 "in the Spellchecker Options menu.\n\n";
269 /* FIXME: this is a minimalist solution until the above
270 * code is able to work with forkedcall.h. We only need
271 * to reap the zombies here.
273 void reapSpellchecker(void)
278 waitpid(isp_pid, 0, WNOHANG);
282 string const ISpell::nextMiss()
284 if (str == 0 || *(e+1) == '\0')
287 e = strpbrk(b, ",\n");
297 return isp_pid != -1;
301 void ISpell::cleanUp()
307 enum ISpell::Result ISpell::check(WordLangTuple const & word)
309 // FIXME Please rewrite to use string.
313 ::fputs(word.word().c_str(), out);
317 ::fgets(buf, 1024, in);
319 // I think we have to check if ispell is still alive here because
320 // the signal-handler could have disabled blocking on the fd
337 case '#': // Not found, no near misses and guesses
340 case '?': // Not found, and no near misses, but guesses (guesses are ignored)
341 case '&': // Not found, but we have near misses
344 char * p = strpbrk(buf, ":");
345 str = new char[strlen(p) + 1];
350 default: // This shouldn't happen, but you know Murphy
356 /* wait for ispell to finish */
366 // Note: If you decide to optimize this out when it is not
367 // needed please note that when Aspell is used this command
368 // is also needed to save the replacement dictionary.
369 // -- Kevin Atkinson (kevinatk@home.com)
371 fputs("#\n", out); // Save personal dictionary
378 void ISpell::insert(WordLangTuple const & word)
380 ::fputc('*', out); // Insert word in personal dictionary
381 ::fputs(word.word().c_str(), out);
386 void ISpell::accept(WordLangTuple const & word)
388 ::fputc('@', out); // Accept in this session
389 ::fputs(word.word().c_str(), out);
394 string const ISpell::error()