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