* Fix an error in compilation when Alpine is not built with S/MIME
[alpine.git] / pico / osdep / altedit.c
blobba7ed729b2bde3f1f4503e6ca46bee724e4bbe3e
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: altedit.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $";
3 #endif
5 /*
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 * ========================================================================
20 #include <system.h>
21 #include <general.h>
23 #include "../../pith/osdep/pipe.h"
24 #include "../../pith/osdep/forkwait.h"
25 #include "../../pith/charconv/filesys.h"
27 #include "../estruct.h"
28 #include "../mode.h"
29 #include "../pico.h"
30 #include "../edef.h"
31 #include "../efunc.h"
32 #include "../keydefs.h"
33 #include "../utf8stub.h"
34 #include "tty.h"
35 #include "filesys.h"
37 #ifdef _WINDOWS
38 #include "mswin.h"
39 #endif
41 #include "altedit.h"
42 #ifndef _WINDOWS
43 #include "signals.h"
45 #ifdef SIGCHLD
46 static jmp_buf pico_child_state;
47 static short pico_child_jmp_ok, pico_child_done;
48 #endif /* SIGCHLD */
50 #endif /* !_WINDOWS */
53 /* internal prototypes */
54 #ifndef _WINDOWS
55 #ifdef SIGCHLD
56 RETSIGTYPE child_handler(int);
57 #endif /* SIGCHLD */
58 #else /* _WINDOWS */
59 int alt_editor_valid(char *, char *, size_t);
60 int alt_editor_valid_fp(char *);
61 #endif /* _WINDOWS */
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.
69 int
70 alt_editor(int f, int n)
72 #ifndef _WINDOWS
73 char eb[NLINE]; /* buf holding edit command */
74 char *fn; /* tmp holder for file name */
75 char result[128]; /* result string */
76 char prmpt[128];
77 int i, done = 0, ret = 0, rv, trv;
78 pid_t child, pid;
79 RETSIGTYPE (*ohup)(int);
80 RETSIGTYPE (*oint)(int);
81 RETSIGTYPE (*osize)(int);
82 int status;
83 EML eml;
85 eml.s = f ? "speller" : "editor";
87 if(gmode&MDSCUR){
88 emlwrite("Alternate %s not available in restricted mode", &eml);
89 return(-1);
92 strncpy(result, "Alternate %s complete.", sizeof(result));
93 result[sizeof(result)-1] = '\0';
95 if(f){
96 if(alt_speller){
97 strncpy(eb, alt_speller, sizeof(eb));
98 eb[sizeof(eb)-1] = '\0';
100 else
101 return(-1);
103 else if(Pmaster == NULL){
104 return(-1);
106 else{
107 eb[0] = '\0';
109 if(Pmaster->alt_ed){
110 char **lp, *wsp, *path, fname[MAXPATH+1];
111 int c;
113 for(lp = Pmaster->alt_ed; *lp && **lp; lp++){
114 if((wsp = strpbrk(*lp, " \t")) != NULL){
115 c = *wsp;
116 *wsp = '\0';
119 if(strchr(*lp, '/')){
120 rv = fexist(*lp, "x", (off_t *)NULL);
122 else{
123 if(!(path = getenv("PATH")))
124 path = ":/bin:/usr/bin";
126 rv = ~FIOSUC;
127 while(rv != FIOSUC && *path && pathcat(fname, &path, *lp))
128 rv = fexist(fname, "x", (off_t *)NULL);
131 if(wsp)
132 *wsp = c;
134 if(rv == FIOSUC){
135 strncpy(eb, *lp, sizeof(eb));
136 eb[sizeof(eb)-1] = '\0';
137 break;
142 if(!eb[0]){
143 if(!(gmode&MDADVN)){
144 unknown_command(0);
145 return(-1);
148 if(getenv("EDITOR")){
149 strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb));
150 eb[sizeof(eb)-1] = '\0';
152 else
153 *eb = '\0';
155 while(!done){
156 pid = mlreplyd_utf8("Which alternate editor ? ", eb,
157 sizeof(eb), QDEFLT, NULL);
158 switch(pid){
159 case ABORT:
160 curwp->w_flag |= WFMODE;
161 return(-1);
162 case HELPCH:
163 emlwrite("no alternate editor help yet", NULL);
165 /* take sleep and break out after there's help */
166 sleep(3);
167 break;
168 case (CTRL|'L'):
169 sgarbf = TRUE;
170 update();
171 break;
172 case TRUE:
173 case FALSE: /* does editor exist ? */
174 if(*eb == '\0'){ /* leave silently? */
175 mlerase();
176 curwp->w_flag |= WFMODE;
177 return(-1);
180 done++;
181 break;
182 default:
183 break;
189 if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */
190 emlwrite("Problem writing temp file for alt editor", NULL);
191 return(-1);
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++){
201 movecursor(i, 0);
202 if(!i){
203 fputs("Invoking alternate ", stdout);
204 fputs(f ? "speller..." : "editor...", stdout);
207 peeol();
210 (*term.t_flush)();
211 if(Pmaster)
212 (*Pmaster->tty_fix)(0);
213 else
214 vttidy();
216 #ifdef SIGCHLD
217 if(Pmaster){
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);
225 #endif
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);
232 #endif
234 #ifdef SIGCHLD
235 if(Pmaster){
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;
241 sleep(600);
243 else
244 our_sigunblock(SIGCHLD);
247 pico_child_jmp_ok = 0;
250 #endif
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);
258 #endif
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';
267 else {
268 snprintf(result, sizeof(result), "Alternate %%s terminated abnormally (%d)",
269 (trv == 0) ? status : -1);
270 if(f)
271 ret = -1;
272 else{
273 snprintf(prmpt, sizeof(prmpt), "Alt editor failed, use file %.20s of size %%ld chars anyway", fn);
274 ret = -2;
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);
283 #endif
284 #ifdef SIGCHLD
285 (void) signal(SIGCHLD, SIG_DFL);
286 #endif
287 if(execl("/bin/sh", "sh", "-c", fname_to_locale(eb), (char *) NULL) < 0)
288 exit(-1);
290 else { /* error! */
291 snprintf(result, sizeof(result), "\007Can't fork %%s: %s", errstr(errno));
292 ret = -1;
295 #ifdef SIGCHLD
296 (void) signal(SIGCHLD, SIG_DFL);
297 #endif
299 if(Pmaster)
300 (*Pmaster->tty_fix)(1);
303 * replace edited text with new text
305 curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */
307 if(ret == -2){
308 off_t filesize;
309 char prompt[128];
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);
317 if(i == TRUE){
318 ret = 0;
319 strncpy(result, "OK, alternate %s done", sizeof(result));
320 result[sizeof(result)-1] = '\0';
322 else
323 ret = -1;
325 else
326 ret = -1;
329 if(ret == 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 */
336 update();
337 emlwrite(result, &eml);
338 return(ret);
339 #else /* _WINDOWS */
340 char eb[2 * PATH_MAX]; /* buf holding edit command */
341 char *fn; /* tmp holder for file name */
342 char errbuf[128];
343 char **lp;
344 int status;
345 int done;
346 int rc;
349 if(Pmaster == NULL)
350 return(-1);
352 if(gmode&MDSCUR){
353 emlwrite("Alternate editor not available in restricted mode", NULL);
354 return(-1);
357 eb[0] = '\0';
359 if(Pmaster->alt_ed){
360 char *p;
362 for(lp = Pmaster->alt_ed; *lp && **lp; lp++)
363 if(*lp[0] == '\"'){
364 if(p = strchr(*lp + 1, '\"')){
365 *p = '\0';
366 rc = alt_editor_valid((*lp) + 1, eb, sizeof(eb));
367 *p = '\"';
368 if(rc){
369 strncat(eb, p + 1, sizeof(eb)-strlen(eb)-1);
370 eb[sizeof(eb)-1] = '\0';
371 break;
375 else if(p = strchr(*lp, ' ')){
376 for(rc = 0; !rc; ){
377 if(p)
378 *p = '\0';
380 rc = alt_editor_valid(*lp, eb, sizeof(eb));
382 if(p)
383 *p = ' ';
385 if(rc){
386 if(p){
387 strncat(eb, p, sizeof(eb)-strlen(eb)-1);
388 eb[sizeof(eb)-1] = '\0';
391 break;
393 else if(p)
394 p = strchr(p + 1, ' ');
395 else
396 break;
399 if(rc)
400 break;
402 else if(alt_editor_valid(*lp, eb, sizeof(eb)))
403 break;
406 if(!eb[0]){
407 if(!(gmode&MDADVN)){
408 unknown_command(0);
409 return(-1);
412 /* Guess which editor they want. */
413 if(getenv("EDITOR")){
414 strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb));
415 eb[sizeof(eb)-1] = '\0';
417 else
418 *eb = '\0';
420 done = FALSE;
421 while(!done){
422 rc = mlreplyd_utf8("Which alternate editor ? ", eb,
423 sizeof(eb),QDEFLT,NULL);
425 switch(rc){
426 case ABORT:
427 curwp->w_flag |= WFMODE;
428 return(-1);
429 case HELPCH:
430 emlwrite("no alternate editor help yet", NULL);
432 /* take sleep and break out after there's help */
433 sleep(3);
434 break;
435 case (CTRL|'L'):
436 sgarbf = TRUE;
437 update();
438 break;
439 case TRUE:
440 case FALSE: /* does editor exist ? */
441 if(*eb == '\0'){ /* leave silently? */
442 mlerase();
443 curwp->w_flag |= WFMODE;
444 return(-1);
447 done++;
448 break;
449 default:
450 break;
455 if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */
456 emlwrite("Problem writing temp file for alt editor", NULL);
457 return(-1);
460 emlwrite("Waiting for alternate editor to finish...", NULL);
462 #ifdef ALTED_DOT
463 if(eb[0] == '.' && eb[1] == '\0') {
464 status = mswin_exec_and_wait ("alternate editor", eb, fn, fn,
465 NULL, 0);
467 else
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,
476 NULL, 0);
479 switch (status) {
481 case 0:
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 */
491 return(0);
495 * Possible errors.
497 case -1:
498 /* Failed to map return from WinExec to a HTASK. */
499 emlwrite("Problem finding alternate editor task handle.", NULL);
500 return (-1);
502 case -2:
503 /* User decided to abandon the alternate editor.*/
504 emlwrite("Alternate editor abandoned.", NULL);
505 return (-1);
507 default:
508 mswin_exec_err_msg ("alternate editor", status, errbuf, sizeof(errbuf));
509 emlwrite (errbuf, NULL);
510 return (-1);
512 return (-1);
513 #endif /*_WINDOWS */
518 #ifndef _WINDOWS
520 pathcat(char *buf, char **path, char *file)
522 register int n = 0;
524 while(**path && **path != ':'){
525 if(n++ > MAXPATH)
526 return(FALSE);
528 *buf++ = *(*path)++;
531 if(n){
532 if(n++ > MAXPATH)
533 return(FALSE);
535 *buf++ = '/';
538 while((*buf = *file++) != '\0'){
539 if(n++ > MAXPATH)
540 return(FALSE);
542 buf++;
545 if(**path == ':')
546 (*path)++;
548 return(TRUE);
551 #ifdef SIGCHLD
553 * child_handler - handle child status change signal
555 RETSIGTYPE
556 child_handler(int sig)
558 pico_child_done = 1;
559 if(pico_child_jmp_ok){
560 #ifdef sco
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
575 * the sleep call.
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 */
586 #endif
587 longjmp(pico_child_state, 1);
590 #endif /* SIGCHLD */
592 #else /* _WINDOWS */
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)
601 /****
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
605 **** to char * here.
606 ****/
608 if(!exe)
609 return(FALSE);
611 #ifdef ALTED_DOT
612 if(exe[0] == '.' && exe[1] == '\0') {
613 cmdbuf[0] = '.';
614 cmdbuf[1] = '\0';
615 return(TRUE);
617 else
618 #endif /* ALTED_DOT */
620 if((isalpha((unsigned char) *exe) && *exe == ':') || strchr(exe, '\\')){
621 char *path;
623 if(alt_editor_valid_fp(path = exe)){
624 if(strchr(path, ' '))
625 snprintf(cmdbuf, ncmdbuf, "\"%s\"", path);
626 else{
627 strncpy(cmdbuf, path, ncmdbuf);
628 cmdbuf[ncmdbuf-1] = '\0';
631 return(TRUE);
634 else{
635 TCHAR pathbuflpt[PATH_MAX+1];
636 LPTSTR name, exelpt;
637 LPTSTR cmdbuflpt;
638 char *utf8;
640 cmdbuflpt = (LPTSTR) MemAlloc(ncmdbuf * sizeof(TCHAR));
641 cmdbuflpt[0] = L'0';
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);
649 else
650 _stprintf_s(cmdbuflpt, ncmdbuf, TEXT("%s"), pathbuflpt);
652 if(exelpt)
653 fs_give((void **) &exelpt);
655 utf8 = lptstr_to_utf8(cmdbuflpt);
656 if(utf8){
657 strncpy(cmdbuf, utf8, ncmdbuf);
658 cmdbuf[ncmdbuf-1] = '\0';
659 fs_give((void **) &utf8);
662 if(cmdbuflpt)
663 MemFree((void *) cmdbuflpt);
665 return(TRUE);
668 if(exelpt)
669 fs_give((void **) &exelpt);
671 if(cmdbuflpt)
672 MemFree((void *) cmdbuflpt);
675 return(FALSE);
680 * alt_editor_valid_fp -- test the given full path name exists
683 alt_editor_valid_fp(path)
684 char *path;
686 static char *exts[] = {".exe", ".com", ".bat", NULL};
687 char pathcopy[PATH_MAX + 1], *dot = NULL;
688 int i, j;
690 for(i = 0; pathcopy[i] = path[i]; i++)
691 if(path[i] == '.')
692 dot = &path[i];
694 if(dot && (!strucmp(dot, ".exe")
695 || !strucmp(dot, ".com") || !strucmp(dot, ".bat"))){
696 if(fexist(path, "x", (off_t *) NULL) == FIOSUC)
697 return(TRUE);
699 else{
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)
704 return(TRUE);
708 return(FALSE);
710 #endif