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-2016 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
;
79 RETSIGTYPE (*ohup
)(int);
80 RETSIGTYPE (*oint
)(int);
81 RETSIGTYPE (*osize
)(int);
85 eml
.s
= f
? "speller" : "editor";
88 emlwrite("Alternate %s not available in restricted mode", &eml
);
92 strncpy(result
, "Alternate %s complete.", sizeof(result
));
93 result
[sizeof(result
)-1] = '\0';
97 strncpy(eb
, alt_speller
, sizeof(eb
));
98 eb
[sizeof(eb
)-1] = '\0';
103 else if(Pmaster
== NULL
){
110 char **lp
, *wsp
, *path
, fname
[MAXPATH
+1];
113 for(lp
= Pmaster
->alt_ed
; *lp
&& **lp
; lp
++){
114 if((wsp
= strpbrk(*lp
, " \t")) != NULL
){
119 if(strchr(*lp
, '/')){
120 rv
= fexist(*lp
, "x", (off_t
*)NULL
);
123 if(!(path
= getenv("PATH")))
124 path
= ":/bin:/usr/bin";
127 while(rv
!= FIOSUC
&& *path
&& pathcat(fname
, &path
, *lp
))
128 rv
= fexist(fname
, "x", (off_t
*)NULL
);
135 strncpy(eb
, *lp
, sizeof(eb
));
136 eb
[sizeof(eb
)-1] = '\0';
148 if(getenv("EDITOR")){
149 strncpy(eb
, (char *)getenv("EDITOR"), sizeof(eb
));
150 eb
[sizeof(eb
)-1] = '\0';
156 pid
= mlreplyd_utf8("Which alternate editor ? ", eb
,
157 sizeof(eb
), QDEFLT
, NULL
);
160 curwp
->w_flag
|= WFMODE
;
163 emlwrite("no alternate editor help yet", NULL
);
165 /* take sleep and break out after there's help */
173 case FALSE
: /* does editor exist ? */
174 if(*eb
== '\0'){ /* leave silently? */
176 curwp
->w_flag
|= WFMODE
;
189 if((fn
=writetmp(1, NULL
)) == NULL
){ /* get temp file */
190 emlwrite("Problem writing temp file for alt editor", NULL
);
194 strncat(eb
, " ", sizeof(eb
)-strlen(eb
)-1);
195 eb
[sizeof(eb
)-1] = '\0';
196 strncat(eb
, fn
, sizeof(eb
)-strlen(eb
)-1);
197 eb
[sizeof(eb
)-1] = '\0';
200 for(i
= 0; i
<= ((Pmaster
) ? term
.t_nrow
: term
.t_nrow
- 1); i
++){
203 fputs("Invoking alternate ", stdout
);
204 fputs(f
? "speller..." : "editor...", stdout
);
212 (*Pmaster
->tty_fix
)(0);
219 * The idea here is to keep any mail connections our caller
220 * may have open while our child's out running around...
222 pico_child_done
= pico_child_jmp_ok
= 0;
223 (void) signal(SIGCHLD
, child_handler
);
227 if((child
= fork()) > 0){ /* wait for the child to finish */
228 ohup
= signal(SIGHUP
, SIG_IGN
); /* ignore signals for now */
229 oint
= signal(SIGINT
, SIG_IGN
);
230 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
231 osize
= signal(SIGWINCH
, SIG_IGN
);
236 while(!pico_child_done
){
237 (*Pmaster
->newmail
)(0, 0);
238 if(!pico_child_done
){
239 if(setjmp(pico_child_state
) == 0){
240 pico_child_jmp_ok
= 1;
244 our_sigunblock(SIGCHLD
);
247 pico_child_jmp_ok
= 0;
252 trv
= process_reap(child
, &status
, PR_NONE
);
254 signal(SIGHUP
, ohup
); /* restore signals */
255 signal(SIGINT
, oint
);
256 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
257 signal(SIGWINCH
, osize
);
261 * Report child's unnatural or unhappy exit...
263 if(trv
> 0 && status
== 0){
264 strncpy(result
, "Alternate %s done", sizeof(result
));
265 result
[sizeof(result
)-1] = '\0';
268 snprintf(result
, sizeof(result
), "Alternate %%s terminated abnormally (%d)",
269 (trv
== 0) ? status
: -1);
273 snprintf(prmpt
, sizeof(prmpt
), "Alt editor failed, use file %.20s of size %%ld chars anyway", fn
);
278 else if(child
== 0){ /* spawn editor */
279 signal(SIGHUP
, SIG_DFL
); /* let editor handle signals */
280 signal(SIGINT
, SIG_DFL
);
281 #if defined(SIGWINCH) && defined(TIOCGWINSZ)
282 signal(SIGWINCH
, SIG_DFL
);
285 (void) signal(SIGCHLD
, SIG_DFL
);
287 if(execl("/bin/sh", "sh", "-c", fname_to_locale(eb
), (char *) NULL
) < 0)
291 snprintf(result
, sizeof(result
), "\007Can't fork %%s: %s", errstr(errno
));
296 (void) signal(SIGCHLD
, SIG_DFL
);
300 (*Pmaster
->tty_fix
)(1);
303 * replace edited text with new text
305 curbp
->b_flag
&= ~BFCHG
; /* make sure old text gets blasted */
311 rv
= fexist(fn
, "r", &filesize
);
312 if(rv
== FIOSUC
&& filesize
> 0){
313 snprintf(prompt
, sizeof(prompt
), prmpt
, (long) filesize
);
314 /* clear bottom 3 rows */
315 pclear(term
.t_nrow
-2, term
.t_nrow
);
316 i
= mlyesno_utf8(prompt
, FALSE
);
319 strncpy(result
, "OK, alternate %s done", sizeof(result
));
320 result
[sizeof(result
)-1] = '\0';
330 readin(fn
, 0, 0); /* read new text overwriting old */
332 our_unlink(fn
); /* blast temp file */
333 curbp
->b_flag
|= BFCHG
; /* mark dirty for packbuf() */
334 ttopen(); /* reset the signals */
335 pico_refresh(0, 1); /* redraw */
337 emlwrite(result
, &eml
);
340 char eb
[2 * PATH_MAX
]; /* buf holding edit command */
341 char *fn
; /* tmp holder for file name */
353 emlwrite("Alternate editor not available in restricted mode", NULL
);
362 for(lp
= Pmaster
->alt_ed
; *lp
&& **lp
; lp
++)
364 if(p
= strchr(*lp
+ 1, '\"')){
366 rc
= alt_editor_valid((*lp
) + 1, eb
, sizeof(eb
));
369 strncat(eb
, p
+ 1, sizeof(eb
)-strlen(eb
)-1);
370 eb
[sizeof(eb
)-1] = '\0';
375 else if(p
= strchr(*lp
, ' ')){
380 rc
= alt_editor_valid(*lp
, eb
, sizeof(eb
));
387 strncat(eb
, p
, sizeof(eb
)-strlen(eb
)-1);
388 eb
[sizeof(eb
)-1] = '\0';
394 p
= strchr(p
+ 1, ' ');
402 else if(alt_editor_valid(*lp
, eb
, sizeof(eb
)))
412 /* Guess which editor they want. */
413 if(getenv("EDITOR")){
414 strncpy(eb
, (char *)getenv("EDITOR"), sizeof(eb
));
415 eb
[sizeof(eb
)-1] = '\0';
422 rc
= mlreplyd_utf8("Which alternate editor ? ", eb
,
423 sizeof(eb
),QDEFLT
,NULL
);
427 curwp
->w_flag
|= WFMODE
;
430 emlwrite("no alternate editor help yet", NULL
);
432 /* take sleep and break out after there's help */
440 case FALSE
: /* does editor exist ? */
441 if(*eb
== '\0'){ /* leave silently? */
443 curwp
->w_flag
|= WFMODE
;
455 if((fn
=writetmp(1, NULL
)) == NULL
){ /* get temp file */
456 emlwrite("Problem writing temp file for alt editor", NULL
);
460 emlwrite("Waiting for alternate editor to finish...", NULL
);
463 if(eb
[0] == '.' && eb
[1] == '\0') {
464 status
= mswin_exec_and_wait ("alternate editor", eb
, fn
, fn
,
468 #endif /* ALTED_DOT */
469 { /* just here in case ALTED_DOT is defined */
470 strncat(eb
, " ", sizeof(eb
)-strlen(eb
)-1);
471 eb
[sizeof(eb
)-1] = '\0';
472 strncat(eb
, fn
, sizeof(eb
)-strlen(eb
)-1);
473 eb
[sizeof(eb
)-1] = '\0';
475 status
= mswin_exec_and_wait ("alternate editor", eb
, NULL
, NULL
,
483 * Success: replace edited text with new text
485 curbp
->b_flag
&= ~BFCHG
; /* make sure old text gets blasted */
486 readin(fn
, 0, 0); /* read new text overwriting old */
487 our_unlink(fn
); /* blast temp file */
488 curbp
->b_flag
|= BFCHG
; /* mark dirty for packbuf() */
489 ttopen(); /* reset the signals */
490 pico_refresh(0, 1); /* redraw */
498 /* Failed to map return from WinExec to a HTASK. */
499 emlwrite("Problem finding alternate editor task handle.", NULL
);
503 /* User decided to abandon the alternate editor.*/
504 emlwrite("Alternate editor abandoned.", NULL
);
508 mswin_exec_err_msg ("alternate editor", status
, errbuf
, sizeof(errbuf
));
509 emlwrite (errbuf
, NULL
);
520 pathcat(char *buf
, char **path
, char *file
)
524 while(**path
&& **path
!= ':'){
538 while((*buf
= *file
++) != '\0'){
553 * child_handler - handle child status change signal
556 child_handler(int sig
)
559 if(pico_child_jmp_ok
){
562 * Villy Kruse <vek@twincom.nl> says:
563 * This probably only affects older unix systems with a "broken" sleep
564 * function such as AT&T System V rel 3.2 and systems derived from
565 * that version. The problem is that the sleep function on these
566 * versions of unix will set up a signal handler for SIGALRM which
567 * will longjmp into the sleep function when the alarm goes off.
568 * This gives problems if another signal handler longjmps out of the
569 * sleep function, thus making the stack frame for the signal function
570 * invalid, and when the ALARM handler later longjmps back into the
571 * sleep function it does no longer have a valid stack frame.
572 * My sugested fix is to cancel the pending alarm in the SIGCHLD
573 * handler before longjmp'ing. This shouldn't hurt as there
574 * shouldn't be any ALARM pending at this point except possibly from
578 * [Even though it shouldn't hurt, we'll make it sco-specific for now.]
581 * The sleep call might have set up a signal handler which would
582 * longjmp back into the sleep code, and that would cause a crash.
584 signal(SIGALRM
, SIG_IGN
); /* Cancel signal handeler */
585 alarm(0); /* might longjmp back into sleep */
587 longjmp(pico_child_state
, 1);
594 * alt_editor_valid -- test the given exe name exists, and if so
595 * return full name in "cmdbuf".
598 alt_editor_valid(char *exe
, char *cmdbuf
, size_t ncmdbuf
)
602 **** This isn't the right way to do this. I believe we
603 **** should be doing all of this in TCHARs starting in
604 **** the alt_editor function instead of trying to convert
612 if(exe
[0] == '.' && exe
[1] == '\0') {
618 #endif /* ALTED_DOT */
620 if((isalpha((unsigned char) *exe
) && *exe
== ':') || strchr(exe
, '\\')){
623 if(alt_editor_valid_fp(path
= exe
)){
624 if(strchr(path
, ' '))
625 snprintf(cmdbuf
, ncmdbuf
, "\"%s\"", path
);
627 strncpy(cmdbuf
, path
, ncmdbuf
);
628 cmdbuf
[ncmdbuf
-1] = '\0';
635 TCHAR pathbuflpt
[PATH_MAX
+1];
640 cmdbuflpt
= (LPTSTR
) MemAlloc(ncmdbuf
* sizeof(TCHAR
));
643 exelpt
= utf8_to_lptstr(exe
);
645 if(SearchPath(NULL
, exelpt
, NULL
, PATH_MAX
+1, pathbuflpt
, &name
)){
647 if(_tcschr(pathbuflpt
, L
' '))
648 _stprintf_s(cmdbuflpt
, ncmdbuf
, TEXT("\"%s\""), pathbuflpt
);
650 _stprintf_s(cmdbuflpt
, ncmdbuf
, TEXT("%s"), pathbuflpt
);
653 fs_give((void **) &exelpt
);
655 utf8
= lptstr_to_utf8(cmdbuflpt
);
657 strncpy(cmdbuf
, utf8
, ncmdbuf
);
658 cmdbuf
[ncmdbuf
-1] = '\0';
659 fs_give((void **) &utf8
);
663 MemFree((void *) cmdbuflpt
);
669 fs_give((void **) &exelpt
);
672 MemFree((void *) cmdbuflpt
);
680 * alt_editor_valid_fp -- test the given full path name exists
683 alt_editor_valid_fp(path
)
686 static char *exts
[] = {".exe", ".com", ".bat", NULL
};
687 char pathcopy
[PATH_MAX
+ 1], *dot
= NULL
;
690 for(i
= 0; pathcopy
[i
] = path
[i
]; i
++)
694 if(dot
&& (!strucmp(dot
, ".exe")
695 || !strucmp(dot
, ".com") || !strucmp(dot
, ".bat"))){
696 if(fexist(path
, "x", (off_t
*) NULL
) == FIOSUC
)
700 for(j
= 0; exts
[j
]; j
++){
701 strncpy(&pathcopy
[i
], exts
[j
], sizeof(pathcopy
)-i
);
702 pathcopy
[sizeof(pathcopy
)-1] = '\0';
703 if(fexist(pathcopy
, "x", (off_t
*) NULL
) == FIOSUC
)