]> git.lyx.org Git - lyx.git/blob - src/spellchecker.C
white-space changes, removed definitions.h several enum changes because of this,...
[lyx.git] / src / spellchecker.C
1 /*
2  *This file is part of
3  * ====================================================== 
4  * 
5  *           LyX, The Document Processor
6  *       
7  *          Copyright 1995 Matthias Ettrich
8  *          Copyright 1995-1998 The LyX Team
9  *
10  * ====================================================== 
11  */
12
13 #include <config.h>
14
15 #include <unistd.h>
16 #include <fcntl.h>
17 #include <cstdlib>
18 #include <cstring>
19 #include <csignal>
20 #include <sys/wait.h>
21 #include <sys/types.h>
22 #include <cctype>
23 #include FORMS_H_LOCATION
24
25 #if TIME_WITH_SYS_TIME
26 # include <sys/time.h>
27 # include <ctime>
28 #else
29 # if HAVE_SYS_TIME_H
30 #  include <sys/time.h>
31 # else
32 #  include <ctime>
33 # endif
34 #endif
35
36 #ifdef HAVE_SYS_SELECT_H
37 #include <sys/select.h>
38 #endif
39
40 #include "LString.h"
41 #include "sp_form.h"
42 #include "spellchecker.h"
43 #include "lyx_main.h"
44 #include "buffer.h"
45 #include "lyxrc.h"
46 #include "BufferView.h"
47 #include "gettext.h"
48 #include "lyx_gui_misc.h"
49 #include "debug.h"
50 #include "support/lstrings.h"
51
52 extern LyXRC *lyxrc;
53 extern BufferView *current_view;
54
55 // Spellchecker status
56 enum {
57         ISP_OK = 1,
58         ISP_ROOT,
59         ISP_COMPOUNDWORD,
60         ISP_UNKNOWN,
61         ISP_MISSED,
62         ISP_IGNORE
63 };
64
65 static bool RunSpellChecker(string const &);
66
67 static FILE *in, *out;  /* streams to communicate with ispell */
68 pid_t isp_pid = -1; // pid for the `ispell' process. Also used (RO) in
69                     // lyx_cb.C
70
71 // the true spell checker program being used
72 enum ActualSpellChecker {ASC_ISPELL, ASC_ASPELL};
73 static ActualSpellChecker actual_spell_checker;
74
75 static int isp_fd;
76
77 static FD_form_spell_options *fd_form_spell_options = 0;
78 FD_form_spell_check *fd_form_spell_check = 0;
79
80 //void sigchldhandler(int sig);
81 void sigchldhandler(pid_t pid, int *status);
82
83 //extern void sigchldchecker(int sig);
84 extern void sigchldchecker(pid_t pid, int *status);
85
86 struct isp_result {
87         int flag;
88         int count;
89         string str;
90         char **misses;
91         isp_result() {
92                 flag = ISP_UNKNOWN;
93                 count = 0;
94                 misses = (char**)0;
95         }
96         ~isp_result() {
97                 if (misses) delete[] misses;
98         }
99 };
100
101
102 /***** Spellchecker options *****/
103
104 // Rewritten to use ordinary LyX xforms loop and OK, Apply and Cancel set,
105 // now also string. Amazing, eh? (Asger)
106
107 // Set (sane) values in form to current spellchecker options
108 void SpellOptionsUpdate() 
109 {
110         // Alternate language
111         if (lyxrc->isp_alt_lang.empty()) {
112                 lyxrc->isp_use_alt_lang = false;
113         } else {
114                 fl_set_input(fd_form_spell_options->altlang_input,
115                              lyxrc->isp_alt_lang.c_str());
116         }
117         if (lyxrc->isp_use_alt_lang) {
118                 fl_set_button(fd_form_spell_options->buflang, 0);
119                 fl_set_button(fd_form_spell_options->altlang, 1);
120         } else {
121                 fl_set_button(fd_form_spell_options->buflang, 1);
122                 fl_set_button(fd_form_spell_options->altlang, 0);
123         }  
124
125         // Personal dictionary
126         if (lyxrc->isp_pers_dict.empty()) {
127                 lyxrc->isp_use_pers_dict = false;
128         } else {
129                 fl_set_input(fd_form_spell_options->perdict_input,
130                              lyxrc->isp_pers_dict.c_str());
131         }
132         fl_set_button(fd_form_spell_options->perdict,
133                       lyxrc->isp_use_pers_dict ? 1:0);
134
135         // Escape chars
136         if (lyxrc->isp_esc_chars.empty()) {
137                 lyxrc->isp_use_esc_chars = false;
138         } else {
139                 fl_set_input(fd_form_spell_options->esc_chars_input,
140                              lyxrc->isp_esc_chars.c_str());
141         }
142         fl_set_button(fd_form_spell_options->esc_chars,
143                       lyxrc->isp_use_esc_chars ? 1:0);
144
145         // Options
146         fl_set_button(fd_form_spell_options->compounds,
147                       lyxrc->isp_accept_compound ? 1 : 0);
148         fl_set_button(fd_form_spell_options->inpenc,
149                       lyxrc->isp_use_input_encoding ? 1 : 0);
150 }
151
152 // Update spellchecker options
153 void SpellOptionsApplyCB(FL_OBJECT *, long)
154 {
155         // Build new status from form data
156         lyxrc->isp_use_alt_lang = 
157                 fl_get_button(fd_form_spell_options->altlang);
158         lyxrc->isp_use_pers_dict = 
159                 fl_get_button(fd_form_spell_options->perdict);
160         lyxrc->isp_accept_compound = 
161                 fl_get_button(fd_form_spell_options->compounds);
162         lyxrc->isp_use_esc_chars = 
163                 fl_get_button(fd_form_spell_options->esc_chars);
164         lyxrc->isp_use_input_encoding = 
165                 fl_get_button(fd_form_spell_options->inpenc);
166
167         // Update strings with data from input fields
168         lyxrc->isp_alt_lang = 
169                 fl_get_input(fd_form_spell_options->altlang_input);
170         lyxrc->isp_pers_dict = 
171                 fl_get_input(fd_form_spell_options->perdict_input);
172         lyxrc->isp_esc_chars = 
173                 fl_get_input(fd_form_spell_options->esc_chars_input);
174
175         // Update form
176         SpellOptionsUpdate();
177 }
178
179
180 void SpellOptionsCancelCB(FL_OBJECT *, long)
181 {
182         fl_hide_form(fd_form_spell_options->form_spell_options);
183 }
184
185
186 void SpellOptionsOKCB(FL_OBJECT * ob, long data)
187 {
188         SpellOptionsApplyCB(ob, data);
189         SpellOptionsCancelCB(ob, data);
190 }
191
192
193 // Show spellchecker options form
194 void SpellCheckerOptions()
195 {
196         // Create form if nescessary
197         if (fd_form_spell_options ==  0) {
198                 fd_form_spell_options = create_form_form_spell_options();
199                 // Make sure pressing the close box does not kill LyX. (RvdK)
200                 fl_set_form_atclose(fd_form_spell_options->form_spell_options, 
201                                     CancelCloseBoxCB, 0);
202         }
203
204         // Update form to current options
205         SpellOptionsUpdate();
206
207         // Focus in alternate language field
208         fl_set_focus_object(fd_form_spell_options->form_spell_options,
209                             fd_form_spell_options->altlang_input);
210
211         // Show form
212         if (fd_form_spell_options->form_spell_options->visible) {
213                 fl_raise_form(fd_form_spell_options->form_spell_options);
214         } else {
215                 fl_show_form(fd_form_spell_options->form_spell_options,
216                              FL_PLACE_MOUSE, FL_FULLBORDER,
217                              _("Spellchecker Options"));
218         }
219 }
220
221
222 /***** Spellchecker *****/
223
224 // Could also use a clean up. (Asger Alstrup)
225
226 static
227 void create_ispell_pipe(string const & lang)
228 {
229         static char o_buf[BUFSIZ];  // jc: it could be smaller
230         int pipein[2], pipeout[2];
231         char * argv[14];
232         int argc;
233
234         isp_pid = -1;
235
236         if(pipe(pipein) == -1 || pipe(pipeout) == -1) {
237                 lyxerr << "LyX: Can't create pipe for spellchecker!" << endl;
238                 return;
239         }
240
241         if ((out = fdopen(pipein[1], "w")) == 0) {
242                 lyxerr << "LyX: Can't create stream for pipe for spellchecker!"
243                        << endl;
244                 return;
245         }
246
247         if ((in = fdopen(pipeout[0], "r")) == 0) {
248                 lyxerr <<"LyX: Can't create stream for pipe for spellchecker!"
249                        << endl;
250                 return;
251         }
252
253         setvbuf(out, o_buf, _IOLBF, BUFSIZ);
254
255         isp_fd = pipeout[0];
256
257         isp_pid = fork();
258
259         if(isp_pid == -1) {
260                 lyxerr << "LyX: Can't create child process for spellchecker!"
261                        << endl;
262                 return;
263         }
264
265         if(isp_pid == 0) {        
266                 /* child process */
267                 dup2(pipein[0], STDIN_FILENO);
268                 dup2(pipeout[1], STDOUT_FILENO);
269                 close(pipein[0]);
270                 close(pipein[1]);
271                 close(pipeout[0]);
272                 close(pipeout[1]);
273
274                 argc = 0;
275                 char * tmp = new char[lyxrc->isp_command.length() + 1];
276                 lyxrc->isp_command.copy(tmp, lyxrc->isp_command.length());
277                 tmp[lyxrc->isp_command.length()] = '\0';
278                 argv[argc++] = tmp;
279                 tmp = new char[3];
280                 string("-a").copy(tmp, 2); tmp[2] = '\0'; // pipe mode
281                 argv[argc++] = tmp;
282
283                 if (lang != "default") {
284                         tmp = new char[3];
285                         string("-d").copy(tmp, 2); tmp[2] = '\0'; // Dictionary file
286                         argv[argc++] = tmp;
287                         tmp = new char[lang.length() + 1];
288                         lang.copy(tmp, lang.length()); tmp[lang.length()] = '\0';
289                         argv[argc++] = tmp;
290                 }
291
292                 if (lyxrc->isp_accept_compound) {
293                         // Consider run-together words as legal compounds
294                         tmp = new char[3];
295                         string("-C").copy(tmp, 2); tmp[2] = '\0';
296                         argv[argc++] = tmp;
297                 } else {
298                         // Report run-together words with
299                         // missing blanks as errors
300                         tmp = new char[3]; 
301                         string("-B").copy(tmp, 2); tmp[2] = '\0';
302                         argv[argc++] = tmp; 
303                 }
304                 if (lyxrc->isp_use_esc_chars) {
305                         // Specify additional characters that
306                         // can be part of a word
307                         tmp = new char[3];
308                         string("-w").copy(tmp, 2); tmp[2] = '\0';
309                         argv[argc++] = tmp; 
310                         // Put the escape chars in ""s
311                         string tms = "\"" + lyxrc->isp_esc_chars + "\"";
312                         tmp = new char[tms.length() + 1];
313                         tms.copy(tmp, tms.length()); tmp[tms.length()] = '\0';
314                         argv[argc++] = tmp;
315                 }
316                 if (lyxrc->isp_use_pers_dict) {
317                         // Specify an alternate personal dictionary
318                         tmp = new char[3];
319                         string("-p").copy(tmp, 2);
320                         tmp[2]= '\0';
321                         argv[argc++] = tmp;
322                         tmp = new char[lyxrc->isp_pers_dict.length() + 1];
323                         lyxrc->isp_pers_dict.copy(tmp, lyxrc->isp_pers_dict.length());
324                         tmp[lyxrc->isp_pers_dict.length()] = '\0';
325                         argv[argc++] = tmp;
326                 }
327                 if (lyxrc->isp_use_input_encoding &&
328                     current_view->buffer()->params.inputenc != "default") {
329                         tmp = new char[3];
330                         string("-T").copy(tmp, 2); tmp[2] = '\0';
331                         argv[argc++] = tmp; // Input encoding
332                         tmp = new char[current_view->buffer()->params.inputenc.length() + 1];
333                         current_view->buffer()->params.inputenc.copy(tmp, current_view->buffer()->params.inputenc.length());
334                         tmp[current_view->buffer()->params.inputenc.length()] = '\0';
335                         argv[argc++] = tmp;
336                 }
337
338                 argv[argc++] = 0;
339
340                 execvp("ispell", (char * const *) argv);
341
342                 // free the memory used by string::copy in the
343                 // setup of argv
344                 for (int i= 0; i < argc -1; i++)
345                         delete[] argv[i];
346                 
347                 lyxerr << "LyX: Failed to start ispell!" << endl;
348                 _exit(0);
349         }
350
351         /* Parent process: Read ispells identification message */
352         // Hmm...what are we using this id msg for? Nothing? (Lgb)
353         // Actually I used it to tell if it's truly Ispell or if it's
354         // aspell -- (kevinatk@home.com)
355         char buf[2048];
356         fd_set infds;
357         struct timeval tv;
358         int retval = 0;
359         FD_ZERO(&infds);
360         FD_SET(pipeout[0], &infds);
361         tv.tv_sec = 15; // fifteen second timeout. Probably too much,
362                         // but it can't really hurt.
363         tv.tv_usec = 0;
364
365         // Configure provides us with macros which are supposed to do
366         // the right typecast.
367         retval = select(SELECT_TYPE_ARG1 (pipeout[0]+1), 
368                         SELECT_TYPE_ARG234 (&infds), 
369                         0, 
370                         0, 
371                         SELECT_TYPE_ARG5 (&tv));
372
373         if (retval > 0) {
374                 // Ok, do the reading. We don't have to FD_ISSET since
375                 // there is only one fd in infds.
376                 fgets(buf, 2048, in);
377                 
378                 // determine if the spell checker is really Aspell
379                 if (strstr(buf, "Aspell"))
380                   actual_spell_checker = ASC_ASPELL;
381                 else
382                   actual_spell_checker = ASC_ISPELL;
383                 
384         } else if (retval == 0) {
385                 // timeout. Give nice message to user.
386                 lyxerr << "Ispell read timed out, what now?" << endl;
387                 // This probably works but could need some thought
388                 isp_pid = -1;
389                 close(pipeout[0]); close(pipeout[1]);
390                 close(pipein[0]); close(pipein[1]);
391                 isp_fd = -1;
392         } else {
393                 // Select returned error
394                 lyxerr << "Select on ispell returned error, what now?" << endl;
395         }
396 }
397
398
399 // Send word to ispell and get reply
400 static
401 isp_result *ispell_check_word(char *word)
402 {
403         //Please rewrite to use string.
404         isp_result *result;
405         char buf[1024], *p;
406         int count, i;
407
408         fputs(word, out); 
409         fputc('\n', out);
410   
411         fgets(buf, 1024, in); 
412   
413         /* I think we have to check if ispell is still alive here because
414            the signal-handler could have disabled blocking on the fd */
415         if (isp_pid == -1) return (isp_result *) 0;
416
417         result = new isp_result;
418   
419         switch (*buf) {
420         case '*': // Word found
421                 result->flag = ISP_OK;
422                 break;
423         case '+': // Word found through affix removal
424                 result->flag = ISP_ROOT;
425                 break;
426         case '-': // Word found through compound formation
427                 result->flag = ISP_COMPOUNDWORD;
428                 break;
429         case '\n': // Number or when in terse mode: no problems
430                 result->flag = ISP_IGNORE;
431                 break;
432         case '#': // Not found, no near misses and guesses
433                 result->flag = ISP_UNKNOWN;
434                 break;
435         case '?': // Not found, and no near misses, but guesses (guesses are ignored)
436         case '&': // Not found, but we have near misses
437         {
438                 result->flag = ISP_MISSED;
439                 result->str = buf;
440                 char * nb = new char[result->str.length() + 1];
441                 result->str.copy(nb, result->str.length());
442                 nb[result->str.length()]= '\0';
443                 p = strpbrk(nb+2, " ");
444                 sscanf(p, "%d", &count); // Get near misses count
445                 result->count = count;
446                 if (count) result->misses = new char*[count];
447                 p = strpbrk(nb, ":");
448                 p += 2;
449                 for (i = 0; i<count; i++) {
450                         result->misses[i] = p;
451                         p = strpbrk(p, ",\n");
452                         *p = 0;
453                         p+= 2;
454                 }
455                 break;
456         }
457         default: // This shouldn't happend, but you know Murphy
458                 result->flag = ISP_UNKNOWN;
459         }
460
461         *buf = 0;
462         if (result->flag!= ISP_IGNORE) {
463                 while (*buf!= '\n') fgets(buf, 255, in); /* wait for ispell to finish */
464         }
465         return result;
466 }
467
468
469 static
470 inline void ispell_terminate()
471 {
472         // Note: If you decide to optimize this out when it is not 
473         // needed please note that when Aspell is used this command 
474         // is also needed to save the replacement dictionary.
475         // -- Kevin Atkinson (kevinatk@home.com)
476
477         fputs("#\n", out); // Save personal dictionary
478
479         fflush(out);
480         fclose(out);
481 }
482
483
484 static
485 inline void ispell_terse_mode()
486 {
487         fputs("!\n", out); // Set terse mode (silently accept correct words)
488 }
489
490
491 static
492 inline void ispell_insert_word(char const *word)
493 {
494         fputc('*', out); // Insert word in personal dictionary
495         fputs(word, out);
496         fputc('\n', out);
497 }
498
499
500 static
501 inline void ispell_accept_word(char const *word) 
502 {
503         fputc('@', out); // Accept in this session
504         fputs(word, out);
505         fputc('\n', out);
506 }
507
508 static
509 inline void ispell_store_replacement(char const *mis, string const & cor) {
510         if(actual_spell_checker == ASC_ASPELL) {
511                 fputs("$$ra ", out);
512                 fputs(mis, out);
513                 fputc(',', out);
514                 fputs(cor.c_str(), out);
515                 fputc('\n', out);
516         }
517 }
518
519
520 void ShowSpellChecker()
521 {
522         FL_OBJECT *obj;
523         int ret;
524
525         // Exit if we don't have a document open
526         if (!current_view->getScreen())
527                 return;
528
529         if (fd_form_spell_check == 0) {
530                 fd_form_spell_check = create_form_form_spell_check();
531                 // Make sure pressing the close box does not kill LyX. (RvdK)
532                 fl_set_form_atclose(fd_form_spell_check->form_spell_check, IgnoreCloseBoxCB, 0);
533         }
534
535         // Clear form
536         fl_set_slider_value(fd_form_spell_check->slider, 0);
537         fl_set_slider_bounds(fd_form_spell_check->slider, 0, 100);
538         fl_set_object_label(fd_form_spell_check->text, "");
539         fl_set_input(fd_form_spell_check->input, "");
540         fl_clear_browser(fd_form_spell_check->browser);
541
542         // Show form
543         if (fd_form_spell_check->form_spell_check->visible) {
544                 fl_raise_form(fd_form_spell_check->form_spell_check);
545         } else {
546                 fl_show_form(fd_form_spell_check->form_spell_check,
547                              FL_PLACE_MOUSE, FL_FULLBORDER,
548                              _("Spellchecker"));
549         }
550         fl_deactivate_object(fd_form_spell_check->slider); 
551
552         // deactivate insert, accept, replace, and stop
553         fl_deactivate_object(fd_form_spell_check->insert);
554         fl_deactivate_object(fd_form_spell_check->accept);
555         fl_deactivate_object(fd_form_spell_check->ignore);
556         fl_deactivate_object(fd_form_spell_check->replace);
557         fl_deactivate_object(fd_form_spell_check->stop);
558         fl_deactivate_object(fd_form_spell_check->input);
559         fl_deactivate_object(fd_form_spell_check->browser);
560         fl_set_object_lcol(fd_form_spell_check->insert, FL_INACTIVE);
561         fl_set_object_lcol(fd_form_spell_check->accept, FL_INACTIVE);
562         fl_set_object_lcol(fd_form_spell_check->ignore, FL_INACTIVE);
563         fl_set_object_lcol(fd_form_spell_check->replace, FL_INACTIVE);
564         fl_set_object_lcol(fd_form_spell_check->stop, FL_INACTIVE);
565         fl_set_object_lcol(fd_form_spell_check->input, FL_INACTIVE);
566         fl_set_object_lcol(fd_form_spell_check->browser, FL_INACTIVE);
567
568         while (true){
569                 obj = fl_do_forms();
570                 if (obj == fd_form_spell_check->options){
571                         SpellCheckerOptions();
572                 }
573                 if (obj == fd_form_spell_check->start){
574                         // activate insert, accept, and stop
575                         fl_activate_object(fd_form_spell_check->insert);
576                         fl_activate_object(fd_form_spell_check->accept);
577                         fl_activate_object(fd_form_spell_check->ignore);
578                         fl_activate_object(fd_form_spell_check->stop);
579                         fl_activate_object(fd_form_spell_check->input);
580                         fl_activate_object(fd_form_spell_check->browser);
581                         fl_set_object_lcol(fd_form_spell_check->insert, FL_BLACK);
582                         fl_set_object_lcol(fd_form_spell_check->accept, FL_BLACK);
583                         fl_set_object_lcol(fd_form_spell_check->ignore, FL_BLACK);
584                         fl_set_object_lcol(fd_form_spell_check->stop, FL_BLACK);
585                         fl_set_object_lcol(fd_form_spell_check->input, FL_BLACK);
586                         fl_set_object_lcol(fd_form_spell_check->browser, FL_BLACK);
587                         // activate replace only if the file is not read-only
588                         if (!current_view->buffer()->isReadonly()) { 
589                           fl_activate_object(fd_form_spell_check->replace);
590                           fl_set_object_lcol(fd_form_spell_check->replace, FL_BLACK);
591                         }
592
593                         // deactivate options and start
594                         fl_deactivate_object(fd_form_spell_check->options);
595                         fl_deactivate_object(fd_form_spell_check->start);
596                         fl_set_object_lcol(fd_form_spell_check->options, FL_INACTIVE);
597                         fl_set_object_lcol(fd_form_spell_check->start, FL_INACTIVE);
598
599                         ret = RunSpellChecker(current_view->buffer()->GetLanguage());
600
601                         // deactivate insert, accept, replace, and stop
602                         fl_deactivate_object(fd_form_spell_check->insert);
603                         fl_deactivate_object(fd_form_spell_check->accept);
604                         fl_deactivate_object(fd_form_spell_check->ignore);
605                         fl_deactivate_object(fd_form_spell_check->replace);
606                         fl_deactivate_object(fd_form_spell_check->stop);
607                         fl_deactivate_object(fd_form_spell_check->input);
608                         fl_deactivate_object(fd_form_spell_check->browser);
609                         fl_set_object_lcol(fd_form_spell_check->insert, FL_INACTIVE);
610                         fl_set_object_lcol(fd_form_spell_check->accept, FL_INACTIVE);
611                         fl_set_object_lcol(fd_form_spell_check->ignore, FL_INACTIVE);
612                         fl_set_object_lcol(fd_form_spell_check->replace, FL_INACTIVE);
613                         fl_set_object_lcol(fd_form_spell_check->stop, FL_INACTIVE);
614                         fl_set_object_lcol(fd_form_spell_check->input, FL_INACTIVE);
615                         fl_set_object_lcol(fd_form_spell_check->browser, FL_INACTIVE);
616
617                         // activate options and start
618                         fl_activate_object(fd_form_spell_check->options);
619                         fl_activate_object(fd_form_spell_check->start);
620                         fl_set_object_lcol(fd_form_spell_check->options, FL_BLACK);
621                         fl_set_object_lcol(fd_form_spell_check->start, FL_BLACK);
622
623                         // if RunSpellChecker returns false quit spellchecker
624                         if (!ret) break;
625                 }
626                 if (obj == fd_form_spell_check->done) break;
627         }
628         fl_hide_form(fd_form_spell_check->form_spell_check);
629         EndOfSpellCheck();
630         return;
631 }
632
633
634 // Perform an ispell session
635 static
636 bool RunSpellChecker(string const & lang)
637 {
638         isp_result *result;
639         char *word;
640         int i, oldval, clickline, newvalue;
641         float newval;
642         FL_OBJECT *obj;
643         unsigned int word_count = 0;
644
645         string tmp = (lyxrc->isp_use_alt_lang) ? lyxrc->isp_alt_lang:lang;
646
647         oldval = 0;  /* used for updating slider only when needed */
648         newval = 0.0;
649    
650         /* create ispell process */
651         create_ispell_pipe(tmp);
652
653         if (isp_pid == -1) {
654                 fl_show_message(
655                         _("\n\n"
656                           "The ispell-process has died for some reason. *One* possible reason\n"
657                           "could be that you do not have a dictionary file\n"
658                           "for the language of this document installed.\n"
659                           "Check /usr/lib/ispell or set another\n"
660                           "dictionary in the Spellchecker Options menu."), "", "");
661                 fclose(out);
662                 return true;
663         }
664
665         // Put ispell in terse mode to improve speed
666         ispell_terse_mode();
667
668         while (true) {
669                 word = NextWord(newval);
670                 if (word == 0) break;
671                 word_count++;
672                 
673                 // Update slider if and only if value has changed
674                 newvalue = int(100.0*newval);
675                 if(newvalue!= oldval) {
676                         oldval = newvalue;
677                         fl_set_slider_value(fd_form_spell_check->slider, oldval);
678                 }
679
680                 result = ispell_check_word(word);
681                 if (isp_pid == -1) {
682                         delete[] word;
683                         break;
684                 }
685
686                 obj =  fl_check_forms();
687                 if (obj == fd_form_spell_check->stop) {
688                         delete result;
689                         delete[] word;
690                         ispell_terminate();
691                         return true;
692                 }
693                 if (obj == fd_form_spell_check->done) {
694                         delete result;
695                         delete[] word;
696                         ispell_terminate(); 
697                         return false;
698                 }
699     
700                 switch (result->flag) {
701                 case ISP_UNKNOWN:
702                 case ISP_MISSED:
703                         SelectLastWord();
704                         fl_set_object_label(fd_form_spell_check->text, word);
705                         fl_set_input(fd_form_spell_check->input, word);
706                         fl_clear_browser(fd_form_spell_check->browser);
707                         for (i= 0; i<result->count; i++) {
708                                 fl_add_browser_line(fd_form_spell_check->browser, result->misses[i]);
709                         }
710
711                         clickline = -1;
712                         while (true) {
713                                 obj = fl_do_forms();
714                                 if (obj == fd_form_spell_check->insert) {
715                                         ispell_insert_word(word);
716                                         break;
717                                 }
718                                 if (obj == fd_form_spell_check->accept) {
719                                         ispell_accept_word(word);
720                                         break;
721                                 }
722                                 if (obj == fd_form_spell_check->ignore) {
723                                         break;
724                                 }
725                                 if (obj == fd_form_spell_check->replace || 
726                                     obj == fd_form_spell_check->input) {
727                                         ispell_store_replacement(word, fl_get_input(fd_form_spell_check->input));
728                                         ReplaceWord(fl_get_input(fd_form_spell_check->input));
729                                         break;
730                                 }
731                                 if (obj == fd_form_spell_check->browser) {
732                                         // implements double click in the browser window.
733                                         // sent to lyx@via by Mark Burton <mark@cbl.leeds.ac.uk>
734                                         if (clickline == 
735                                             fl_get_browser(fd_form_spell_check->browser)) {
736                                                 ispell_store_replacement(word, fl_get_input(fd_form_spell_check->input));
737                                                 ReplaceWord(fl_get_input(fd_form_spell_check->input));
738                                                 break;
739                                         }
740                                         clickline = fl_get_browser(fd_form_spell_check->browser);
741                                         fl_set_input(fd_form_spell_check->input, 
742                                                      fl_get_browser_line(fd_form_spell_check->browser,
743                                                                          fl_get_browser(fd_form_spell_check->browser)));
744                                                      
745                                 }
746                                 if (obj == fd_form_spell_check->stop) {
747                                         delete result;
748                                         delete[] word;
749                                         ispell_terminate();
750                                         return true;
751                                 }
752             
753                                 if (obj == fd_form_spell_check->done) {
754                                         delete result;
755                                         delete[] word;
756                                         ispell_terminate();
757                                         return false;
758                                 }
759                         }
760                 default:
761                         delete result;
762                         delete[] word;
763                 }
764         }
765    
766         if(isp_pid!= -1) {
767                 ispell_terminate();
768                 string word_msg;
769                 word_msg += tostr(word_count);
770                 if (word_count != 1) {
771                         word_msg += _(" words checked.");
772                 } else {
773                         word_msg += _(" word checked.");
774                 }
775                 fl_show_message("", _("Spellchecking completed!"),
776                                 word_msg.c_str());
777                 return false;
778         } else {
779                 fl_show_message(_("The ispell-process has died for some reason.\n"
780                                 "Maybe it has been killed."), "", "");
781                 fclose(out);
782                 return true;
783         }
784 }
785
786
787 //void sigchldhandler(int sig)
788 void sigchldhandler(pid_t pid, int *status)
789
790         //int status ;
791
792         if (isp_pid>0)
793                 //if (waitpid(isp_pid, &status, WNOHANG) == isp_pid) {
794                 if (pid == isp_pid) {
795                         isp_pid= -1;
796                         fcntl(isp_fd, F_SETFL, O_NONBLOCK); /* set the file descriptor
797                                                                to nonblocking so we can 
798                                                                continue */
799                 }
800         //sigchldchecker(sig);
801         sigchldchecker(pid, status);
802 }