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-2018 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 *);
64 * alt_editor - fork off an alternate editor for mail message composition
65 * if one is configured and passed from pine. If not, only
66 * ask for the editor if advanced user flag is set, and
67 * suggest environment's EDITOR value as default.
70 alt_editor(int f
, int n
)
73 char eb
[NLINE
]; /* buf holding edit command */
74 char *fn
; /* tmp holder for file name */
75 char result
[128]; /* result string */
77 int i
, done
= 0, ret
= 0, rv
, trv
, beep
;
79 RETSIGTYPE (*ohup
)(int);
80 RETSIGTYPE (*oint
)(int);
81 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
), _("Can't fork %%s: %s"), errstr(errno
));
298 (void) signal(SIGCHLD
, SIG_DFL
);
302 (*Pmaster
->tty_fix
)(1);
305 * replace edited text with new text
307 curbp
->b_flag
&= ~BFCHG
; /* make sure old text gets blasted */
313 rv
= fexist(fn
, "r", &filesize
);
314 if(rv
== FIOSUC
&& filesize
> 0){
315 snprintf(prompt
, sizeof(prompt
), prmpt
, (long) filesize
);
316 /* clear bottom 3 rows */
317 pclear(term
.t_nrow
-2, term
.t_nrow
);
318 i
= mlyesno_utf8(prompt
, FALSE
);
321 strncpy(result
, "OK, alternate %s done", sizeof(result
));
322 result
[sizeof(result
)-1] = '\0';
332 readin(fn
, 0, 0); /* read new text overwriting old */
334 our_unlink(fn
); /* blast temp file */
335 curbp
->b_flag
|= BFCHG
; /* mark dirty for packbuf() */
336 ttopen(); /* reset the signals */
337 pico_refresh(0, 1); /* redraw */
340 emlwwrite(result
, &eml
);
342 emlwrite(result
, &eml
);
345 char eb
[2 * PATH_MAX
]; /* buf holding edit command */
346 char *fn
; /* tmp holder for file name */
358 emlwrite("Alternate editor not available in restricted mode", NULL
);
367 for(lp
= Pmaster
->alt_ed
; *lp
&& **lp
; lp
++)
369 if(p
= strchr(*lp
+ 1, '\"')){
371 rc
= alt_editor_valid((*lp
) + 1, eb
, sizeof(eb
));
374 strncat(eb
, p
+ 1, sizeof(eb
)-strlen(eb
)-1);
375 eb
[sizeof(eb
)-1] = '\0';
380 else if(p
= strchr(*lp
, ' ')){
385 rc
= alt_editor_valid(*lp
, eb
, sizeof(eb
));
392 strncat(eb
, p
, sizeof(eb
)-strlen(eb
)-1);
393 eb
[sizeof(eb
)-1] = '\0';
399 p
= strchr(p
+ 1, ' ');
407 else if(alt_editor_valid(*lp
, eb
, sizeof(eb
)))
417 /* Guess which editor they want. */
418 if(getenv("EDITOR")){
419 strncpy(eb
, (char *)getenv("EDITOR"), sizeof(eb
));
420 eb
[sizeof(eb
)-1] = '\0';
427 rc
= mlreplyd_utf8("Which alternate editor ? ", eb
,
428 sizeof(eb
),QDEFLT
,NULL
);
432 curwp
->w_flag
|= WFMODE
;
435 emlwrite("no alternate editor help yet", NULL
);
437 /* take sleep and break out after there's help */
445 case FALSE
: /* does editor exist ? */
446 if(*eb
== '\0'){ /* leave silently? */
448 curwp
->w_flag
|= WFMODE
;
460 if((fn
=writetmp(1, NULL
)) == NULL
){ /* get temp file */
461 emlwrite("Problem writing temp file for alt editor", NULL
);
465 emlwrite("Waiting for alternate editor to finish...", NULL
);
468 if(eb
[0] == '.' && eb
[1] == '\0') {
469 status
= mswin_exec_and_wait ("alternate editor", eb
, fn
, fn
,
473 #endif /* ALTED_DOT */
474 { /* just here in case ALTED_DOT is defined */
475 strncat(eb
, " ", sizeof(eb
)-strlen(eb
)-1);
476 eb
[sizeof(eb
)-1] = '\0';
477 strncat(eb
, fn
, sizeof(eb
)-strlen(eb
)-1);
478 eb
[sizeof(eb
)-1] = '\0';
480 status
= mswin_exec_and_wait ("alternate editor", eb
, NULL
, NULL
,
488 * Success: replace edited text with new text
490 curbp
->b_flag
&= ~BFCHG
; /* make sure old text gets blasted */
491 readin(fn
, 0, 0); /* read new text overwriting old */
492 our_unlink(fn
); /* blast temp file */
493 curbp
->b_flag
|= BFCHG
; /* mark dirty for packbuf() */
494 ttopen(); /* reset the signals */
495 pico_refresh(0, 1); /* redraw */
503 /* Failed to map return from WinExec to a HTASK. */
504 emlwrite("Problem finding alternate editor task handle.", NULL
);
508 /* User decided to abandon the alternate editor.*/
509 emlwrite("Alternate editor abandoned.", NULL
);
513 mswin_exec_err_msg ("alternate editor", status
, errbuf
, sizeof(errbuf
));
514 emlwrite (errbuf
, NULL
);
525 pathcat(char *buf
, char **path
, char *file
)
529 while(**path
&& **path
!= ':'){
543 while((*buf
= *file
++) != '\0'){
558 * child_handler - handle child status change signal
561 child_handler(int sig
)
564 if(pico_child_jmp_ok
){
567 * Villy Kruse <vek@twincom.nl> says:
568 * This probably only affects older unix systems with a "broken" sleep
569 * function such as AT&T System V rel 3.2 and systems derived from
570 * that version. The problem is that the sleep function on these
571 * versions of unix will set up a signal handler for SIGALRM which
572 * will longjmp into the sleep function when the alarm goes off.
573 * This gives problems if another signal handler longjmps out of the
574 * sleep function, thus making the stack frame for the signal function
575 * invalid, and when the ALARM handler later longjmps back into the
576 * sleep function it does no longer have a valid stack frame.
577 * My sugested fix is to cancel the pending alarm in the SIGCHLD
578 * handler before longjmp'ing. This shouldn't hurt as there
579 * shouldn't be any ALARM pending at this point except possibly from
583 * [Even though it shouldn't hurt, we'll make it sco-specific for now.]
586 * The sleep call might have set up a signal handler which would
587 * longjmp back into the sleep code, and that would cause a crash.
589 signal(SIGALRM
, SIG_IGN
); /* Cancel signal handeler */
590 alarm(0); /* might longjmp back into sleep */
592 longjmp(pico_child_state
, 1);
599 * alt_editor_valid -- test the given exe name exists, and if so
600 * return full name in "cmdbuf".
603 alt_editor_valid(char *exe
, char *cmdbuf
, size_t ncmdbuf
)
607 **** This isn't the right way to do this. I believe we
608 **** should be doing all of this in TCHARs starting in
609 **** the alt_editor function instead of trying to convert
617 if(exe
[0] == '.' && exe
[1] == '\0') {
623 #endif /* ALTED_DOT */
625 if((isalpha((unsigned char) *exe
) && *exe
== ':') || strchr(exe
, '\\')){
628 if(alt_editor_valid_fp(path
= exe
)){
629 if(strchr(path
, ' '))
630 snprintf(cmdbuf
, ncmdbuf
, "\"%s\"", path
);
632 strncpy(cmdbuf
, path
, ncmdbuf
);
633 cmdbuf
[ncmdbuf
-1] = '\0';
640 TCHAR pathbuflpt
[PATH_MAX
+1];
645 cmdbuflpt
= (LPTSTR
) MemAlloc(ncmdbuf
* sizeof(TCHAR
));
648 exelpt
= utf8_to_lptstr(exe
);
650 if(SearchPath(NULL
, exelpt
, NULL
, PATH_MAX
+1, pathbuflpt
, &name
)){
652 if(_tcschr(pathbuflpt
, L
' '))
653 _stprintf_s(cmdbuflpt
, ncmdbuf
, TEXT("\"%s\""), pathbuflpt
);
655 _stprintf_s(cmdbuflpt
, ncmdbuf
, TEXT("%s"), pathbuflpt
);
658 fs_give((void **) &exelpt
);
660 utf8
= lptstr_to_utf8(cmdbuflpt
);
662 strncpy(cmdbuf
, utf8
, ncmdbuf
);
663 cmdbuf
[ncmdbuf
-1] = '\0';
664 fs_give((void **) &utf8
);
668 MemFree((void *) cmdbuflpt
);
674 fs_give((void **) &exelpt
);
677 MemFree((void *) cmdbuflpt
);
685 * alt_editor_valid_fp -- test the given full path name exists
688 alt_editor_valid_fp(path
)
691 static char *exts
[] = {".exe", ".com", ".bat", NULL
};
692 char pathcopy
[PATH_MAX
+ 1], *dot
= NULL
;
695 for(i
= 0; pathcopy
[i
] = path
[i
]; i
++)
699 if(dot
&& (!strucmp(dot
, ".exe")
700 || !strucmp(dot
, ".com") || !strucmp(dot
, ".bat"))){
701 if(fexist(path
, "x", (off_t
*) NULL
) == FIOSUC
)
705 for(j
= 0; exts
[j
]; j
++){
706 strncpy(&pathcopy
[i
], exts
[j
], sizeof(pathcopy
)-i
);
707 pathcopy
[sizeof(pathcopy
)-1] = '\0';
708 if(fexist(pathcopy
, "x", (off_t
*) NULL
) == FIOSUC
)