1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: altedit.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2014 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
23 #include "../../pith/osdep/pipe.h"
24 #include "../../pith/osdep/forkwait.h"
25 #include "../../pith/charconv/filesys.h"
27 #include "../estruct.h"
32 #include "../keydefs.h"
33 #include "../utf8stub.h"
46 static jmp_buf pico_child_state
;
47 static short pico_child_jmp_ok
, pico_child_done
;
50 #endif /* !_WINDOWS */
53 /* internal prototypes */
56 RETSIGTYPE
child_handler(int);
59 int alt_editor_valid(char *, char *, size_t);
60 int alt_editor_valid_fp(char *);
65 * alt_editor - fork off an alternate editor for mail message composition
66 * if one is configured and passed from pine. If not, only
67 * ask for the editor if advanced user flag is set, and
68 * suggest environment's EDITOR value as default.
71 alt_editor(int f
, int n
)
74 char eb
[NLINE
]; /* buf holding edit command */
75 char *fn
; /* tmp holder for file name */
76 char result
[128]; /* result string */
78 int i
, done
= 0, ret
= 0, rv
, trv
;
80 RETSIGTYPE (*ohup
)(int);
81 RETSIGTYPE (*oint
)(int);
82 RETSIGTYPE (*osize
)(int);
86 eml
.s
= f
? "speller" : "editor";
89 emlwrite("Alternate %s not available in restricted mode", &eml
);
93 strncpy(result
, "Alternate %s complete.", sizeof(result
));
94 result
[sizeof(result
)-1] = '\0';
98 strncpy(eb
, alt_speller
, sizeof(eb
));
99 eb
[sizeof(eb
)-1] = '\0';
104 else if(Pmaster
== NULL
){
111 char **lp
, *wsp
, *path
, fname
[MAXPATH
+1];
114 for(lp
= Pmaster
->alt_ed
; *lp
&& **lp
; lp
++){
115 if((wsp
= strpbrk(*lp
, " \t")) != NULL
){
120 if(strchr(*lp
, '/')){
121 rv
= fexist(*lp
, "x", (off_t
*)NULL
);
124 if(!(path
= getenv("PATH")))
125 path
= ":/bin:/usr/bin";
128 while(rv
!= FIOSUC
&& *path
&& pathcat(fname
, &path
, *lp
))
129 rv
= fexist(fname
, "x", (off_t
*)NULL
);
136 strncpy(eb
, *lp
, sizeof(eb
));
137 eb
[sizeof(eb
)-1] = '\0';
149 if(getenv("EDITOR")){
150 strncpy(eb
, (char *)getenv("EDITOR"), sizeof(eb
));
151 eb
[sizeof(eb
)-1] = '\0';
157 pid
= mlreplyd_utf8("Which alternate editor ? ", eb
,
158 sizeof(eb
), QDEFLT
, NULL
);
161 curwp
->w_flag
|= WFMODE
;
164 emlwrite("no alternate editor help yet", NULL
);
166 /* take sleep and break out after there's help */
174 case FALSE
: /* does editor exist ? */
175 if(*eb
== '\0'){ /* leave silently? */
177 curwp
->w_flag
|= WFMODE
;
190 if((fn
=writetmp(1, NULL
)) == NULL
){ /* get temp file */
191 emlwrite("Problem writing temp file for alt editor", NULL
);
195 strncat(eb
, " ", sizeof(eb
)-strlen(eb
)-1);
196 eb
[sizeof(eb
)-1] = '\0';
197 strncat(eb
, fn
, sizeof(eb
)-strlen(eb
)-1);
198 eb
[sizeof(eb
)-1] = '\0';
201 for(i
= 0; i
<= ((Pmaster
) ? term
.t_nrow
: term
.t_nrow
- 1); i
++){
204 fputs("Invoking alternate ", stdout
);
205 fputs(f
? "speller..." : "editor...", stdout
);
213 (*Pmaster
->tty_fix
)(0);
220 * The idea here is to keep any mail connections our caller
221 * may have open while our child's out running around...
223 pico_child_done
= pico_child_jmp_ok
= 0;
224 (void) signal(SIGCHLD
, child_handler
);
228 if((child
= fork()) > 0){ /* wait for the child to finish */
229 ohup
= signal(SIGHUP
, SIG_IGN
); /* ignore signals for now */
230 oint
= signal(SIGINT
, SIG_IGN
);
231 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
232 osize
= signal(SIGWINCH
, SIG_IGN
);
237 while(!pico_child_done
){
238 (*Pmaster
->newmail
)(0, 0);
239 if(!pico_child_done
){
240 if(setjmp(pico_child_state
) == 0){
241 pico_child_jmp_ok
= 1;
245 our_sigunblock(SIGCHLD
);
248 pico_child_jmp_ok
= 0;
253 trv
= process_reap(child
, &status
, PR_NONE
);
255 signal(SIGHUP
, ohup
); /* restore signals */
256 signal(SIGINT
, oint
);
257 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
258 signal(SIGWINCH
, osize
);
262 * Report child's unnatural or unhappy exit...
264 if(trv
> 0 && status
== 0){
265 strncpy(result
, "Alternate %s done", sizeof(result
));
266 result
[sizeof(result
)-1] = '\0';
269 snprintf(result
, sizeof(result
), "Alternate %%s terminated abnormally (%d)",
270 (trv
== 0) ? status
: -1);
274 snprintf(prmpt
, sizeof(prmpt
), "Alt editor failed, use file %.20s of size %%ld chars anyway", fn
);
279 else if(child
== 0){ /* spawn editor */
280 signal(SIGHUP
, SIG_DFL
); /* let editor handle signals */
281 signal(SIGINT
, SIG_DFL
);
282 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
283 signal(SIGWINCH
, SIG_DFL
);
286 (void) signal(SIGCHLD
, SIG_DFL
);
288 if(execl("/bin/sh", "sh", "-c", fname_to_locale(eb
), (char *) NULL
) < 0)
292 snprintf(result
, sizeof(result
), "\007Can't fork %%s: %s", errstr(errno
));
297 (void) signal(SIGCHLD
, SIG_DFL
);
301 (*Pmaster
->tty_fix
)(1);
304 * replace edited text with new text
306 curbp
->b_flag
&= ~BFCHG
; /* make sure old text gets blasted */
312 rv
= fexist(fn
, "r", &filesize
);
313 if(rv
== FIOSUC
&& filesize
> 0){
314 snprintf(prompt
, sizeof(prompt
), prmpt
, (long) filesize
);
315 /* clear bottom 3 rows */
316 pclear(term
.t_nrow
-2, term
.t_nrow
);
317 i
= mlyesno_utf8(prompt
, FALSE
);
320 strncpy(result
, "OK, alternate %s done", sizeof(result
));
321 result
[sizeof(result
)-1] = '\0';
331 readin(fn
, 0, 0); /* read new text overwriting old */
333 our_unlink(fn
); /* blast temp file */
334 curbp
->b_flag
|= BFCHG
; /* mark dirty for packbuf() */
335 ttopen(); /* reset the signals */
336 pico_refresh(0, 1); /* redraw */
338 emlwrite(result
, &eml
);
341 char eb
[2 * PATH_MAX
]; /* buf holding edit command */
342 char *fn
; /* tmp holder for file name */
354 emlwrite("Alternate editor not available in restricted mode", NULL
);
363 for(lp
= Pmaster
->alt_ed
; *lp
&& **lp
; lp
++)
365 if(p
= strchr(*lp
+ 1, '\"')){
367 rc
= alt_editor_valid((*lp
) + 1, eb
, sizeof(eb
));
370 strncat(eb
, p
+ 1, sizeof(eb
)-strlen(eb
)-1);
371 eb
[sizeof(eb
)-1] = '\0';
376 else if(p
= strchr(*lp
, ' ')){
381 rc
= alt_editor_valid(*lp
, eb
, sizeof(eb
));
388 strncat(eb
, p
, sizeof(eb
)-strlen(eb
)-1);
389 eb
[sizeof(eb
)-1] = '\0';
395 p
= strchr(p
+ 1, ' ');
403 else if(alt_editor_valid(*lp
, eb
, sizeof(eb
)))
413 /* Guess which editor they want. */
414 if(getenv("EDITOR")){
415 strncpy(eb
, (char *)getenv("EDITOR"), sizeof(eb
));
416 eb
[sizeof(eb
)-1] = '\0';
423 rc
= mlreplyd_utf8("Which alternate editor ? ", eb
,
424 sizeof(eb
),QDEFLT
,NULL
);
428 curwp
->w_flag
|= WFMODE
;
431 emlwrite("no alternate editor help yet", NULL
);
433 /* take sleep and break out after there's help */
441 case FALSE
: /* does editor exist ? */
442 if(*eb
== '\0'){ /* leave silently? */
444 curwp
->w_flag
|= WFMODE
;
456 if((fn
=writetmp(1, NULL
)) == NULL
){ /* get temp file */
457 emlwrite("Problem writing temp file for alt editor", NULL
);
461 emlwrite("Waiting for alternate editor to finish...", NULL
);
464 if(eb
[0] == '.' && eb
[1] == '\0') {
465 status
= mswin_exec_and_wait ("alternate editor", eb
, fn
, fn
,
469 #endif /* ALTED_DOT */
470 { /* just here in case ALTED_DOT is defined */
471 strncat(eb
, " ", sizeof(eb
)-strlen(eb
)-1);
472 eb
[sizeof(eb
)-1] = '\0';
473 strncat(eb
, fn
, sizeof(eb
)-strlen(eb
)-1);
474 eb
[sizeof(eb
)-1] = '\0';
476 status
= mswin_exec_and_wait ("alternate editor", eb
, NULL
, NULL
,
484 * Success: replace edited text with new text
486 curbp
->b_flag
&= ~BFCHG
; /* make sure old text gets blasted */
487 readin(fn
, 0, 0); /* read new text overwriting old */
488 our_unlink(fn
); /* blast temp file */
489 curbp
->b_flag
|= BFCHG
; /* mark dirty for packbuf() */
490 ttopen(); /* reset the signals */
491 pico_refresh(0, 1); /* redraw */
499 /* Failed to map return from WinExec to a HTASK. */
500 emlwrite("Problem finding alternate editor task handle.", NULL
);
504 /* User decided to abandon the alternate editor.*/
505 emlwrite("Alternate editor abandoned.", NULL
);
509 mswin_exec_err_msg ("alternate editor", status
, errbuf
, sizeof(errbuf
));
510 emlwrite (errbuf
, NULL
);
521 pathcat(char *buf
, char **path
, char *file
)
525 while(**path
&& **path
!= ':'){
539 while((*buf
= *file
++) != '\0'){
555 * child_handler - handle child status change signal
558 child_handler(int sig
)
561 if(pico_child_jmp_ok
){
564 * Villy Kruse <vek@twincom.nl> says:
565 * This probably only affects older unix systems with a "broken" sleep
566 * function such as AT&T System V rel 3.2 and systems derived from
567 * that version. The problem is that the sleep function on these
568 * versions of unix will set up a signal handler for SIGALRM which
569 * will longjmp into the sleep function when the alarm goes off.
570 * This gives problems if another signal handler longjmps out of the
571 * sleep function, thus making the stack frame for the signal function
572 * invalid, and when the ALARM handler later longjmps back into the
573 * sleep function it does no longer have a valid stack frame.
574 * My sugested fix is to cancel the pending alarm in the SIGCHLD
575 * handler before longjmp'ing. This shouldn't hurt as there
576 * shouldn't be any ALARM pending at this point except possibly from
580 * [Even though it shouldn't hurt, we'll make it sco-specific for now.]
583 * The sleep call might have set up a signal handler which would
584 * longjmp back into the sleep code, and that would cause a crash.
586 signal(SIGALRM
, SIG_IGN
); /* Cancel signal handeler */
587 alarm(0); /* might longjmp back into sleep */
589 longjmp(pico_child_state
, 1);
596 * alt_editor_valid -- test the given exe name exists, and if so
597 * return full name in "cmdbuf".
600 alt_editor_valid(char *exe
, char *cmdbuf
, size_t ncmdbuf
)
604 **** This isn't the right way to do this. I believe we
605 **** should be doing all of this in TCHARs starting in
606 **** the alt_editor function instead of trying to convert
614 if(exe
[0] == '.' && exe
[1] == '\0') {
620 #endif /* ALTED_DOT */
622 if((isalpha((unsigned char) *exe
) && *exe
== ':') || strchr(exe
, '\\')){
625 if(alt_editor_valid_fp(path
= exe
)){
626 if(strchr(path
, ' '))
627 snprintf(cmdbuf
, ncmdbuf
, "\"%s\"", path
);
629 strncpy(cmdbuf
, path
, ncmdbuf
);
630 cmdbuf
[ncmdbuf
-1] = '\0';
637 TCHAR pathbuflpt
[PATH_MAX
+1];
642 cmdbuflpt
= (LPTSTR
) MemAlloc(ncmdbuf
* sizeof(TCHAR
));
645 exelpt
= utf8_to_lptstr(exe
);
647 if(SearchPath(NULL
, exelpt
, NULL
, PATH_MAX
+1, pathbuflpt
, &name
)){
649 if(_tcschr(pathbuflpt
, L
' '))
650 _stprintf_s(cmdbuflpt
, ncmdbuf
, TEXT("\"%s\""), pathbuflpt
);
652 _stprintf_s(cmdbuflpt
, ncmdbuf
, TEXT("%s"), pathbuflpt
);
655 fs_give((void **) &exelpt
);
657 utf8
= lptstr_to_utf8(cmdbuflpt
);
659 strncpy(cmdbuf
, utf8
, ncmdbuf
);
660 cmdbuf
[ncmdbuf
-1] = '\0';
661 fs_give((void **) &utf8
);
665 MemFree((void *) cmdbuflpt
);
671 fs_give((void **) &exelpt
);
674 MemFree((void *) cmdbuflpt
);
682 * alt_editor_valid_fp -- test the given full path name exists
685 alt_editor_valid_fp(path
)
688 static char *exts
[] = {".exe", ".com", ".bat", NULL
};
689 char pathcopy
[PATH_MAX
+ 1], *dot
= NULL
;
692 for(i
= 0; pathcopy
[i
] = path
[i
]; i
++)
696 if(dot
&& (!strucmp(dot
, ".exe")
697 || !strucmp(dot
, ".com") || !strucmp(dot
, ".bat"))){
698 if(fexist(path
, "x", (off_t
*) NULL
) == FIOSUC
)
702 for(j
= 0; exts
[j
]; j
++){
703 strncpy(&pathcopy
[i
], exts
[j
], sizeof(pathcopy
)-i
);
704 pathcopy
[sizeof(pathcopy
)-1] = '\0';
705 if(fexist(pathcopy
, "x", (off_t
*) NULL
) == FIOSUC
)