From 094ca96844842928810f14844413109fc6cdd890 Mon Sep 17 00:00:00 2001 From: Eduardo Chappa Date: Sun, 3 Feb 2013 00:59:38 -0700 Subject: Initial Alpine Version --- pico/osdep/altedit.c | 712 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 712 insertions(+) create mode 100644 pico/osdep/altedit.c (limited to 'pico/osdep/altedit.c') diff --git a/pico/osdep/altedit.c b/pico/osdep/altedit.c new file mode 100644 index 00000000..934ed016 --- /dev/null +++ b/pico/osdep/altedit.c @@ -0,0 +1,712 @@ +#if !defined(lint) && !defined(DOS) +static char rcsid[] = "$Id: altedit.c 854 2007-12-07 17:44:43Z hubert@u.washington.edu $"; +#endif + +/* + * ======================================================================== + * Copyright 2006-2007 University of Washington + * Copyright 2013 Eduardo Chappa + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * ======================================================================== + */ + + +#include +#include + +#include "../../pith/osdep/pipe.h" +#include "../../pith/osdep/forkwait.h" +#include "../../pith/charconv/filesys.h" + +#include "../estruct.h" +#include "../mode.h" +#include "../pico.h" +#include "../edef.h" +#include "../efunc.h" +#include "../keydefs.h" +#include "../utf8stub.h" +#include "tty.h" +#include "filesys.h" + +#ifdef _WINDOWS +#include "mswin.h" +#endif + +#include "altedit.h" +#ifndef _WINDOWS +#include "signals.h" + +#ifdef SIGCHLD +static jmp_buf pico_child_state; +static short pico_child_jmp_ok, pico_child_done; +#endif /* SIGCHLD */ + +#endif /* !_WINDOWS */ + + +/* internal prototypes */ +#ifndef _WINDOWS +#ifdef SIGCHLD +RETSIGTYPE child_handler(int); +#endif /* SIGCHLD */ +#else /* _WINDOWS */ +int alt_editor_valid(char *, char *, size_t); +int alt_editor_valid_fp(char *); +#endif /* _WINDOWS */ + + +/* + * alt_editor - fork off an alternate editor for mail message composition + * if one is configured and passed from pine. If not, only + * ask for the editor if advanced user flag is set, and + * suggest environment's EDITOR value as default. + */ +int +alt_editor(int f, int n) +{ +#ifndef _WINDOWS + char eb[NLINE]; /* buf holding edit command */ + char *fn; /* tmp holder for file name */ + char result[128]; /* result string */ + char prmpt[128]; + int i, done = 0, ret = 0, rv, trv; + pid_t child, pid; + RETSIGTYPE (*ohup)(int); + RETSIGTYPE (*oint)(int); + RETSIGTYPE (*osize)(int); + int status; + EML eml; + + eml.s = f ? "speller" : "editor"; + + if(gmode&MDSCUR){ + emlwrite("Alternate %s not available in restricted mode", &eml); + return(-1); + } + + strncpy(result, "Alternate %s complete.", sizeof(result)); + result[sizeof(result)-1] = '\0'; + + if(f){ + if(alt_speller){ + strncpy(eb, alt_speller, sizeof(eb)); + eb[sizeof(eb)-1] = '\0'; + } + else + return(-1); + } + else if(Pmaster == NULL){ + return(-1); + } + else{ + eb[0] = '\0'; + + if(Pmaster->alt_ed){ + char **lp, *wsp, *path, fname[MAXPATH+1]; + int c; + + for(lp = Pmaster->alt_ed; *lp && **lp; lp++){ + if((wsp = strpbrk(*lp, " \t")) != NULL){ + c = *wsp; + *wsp = '\0'; + } + + if(strchr(*lp, '/')){ + rv = fexist(*lp, "x", (off_t *)NULL); + } + else{ + if(!(path = getenv("PATH"))) + path = ":/bin:/usr/bin"; + + rv = ~FIOSUC; + while(rv != FIOSUC && *path && pathcat(fname, &path, *lp)) + rv = fexist(fname, "x", (off_t *)NULL); + } + + if(wsp) + *wsp = c; + + if(rv == FIOSUC){ + strncpy(eb, *lp, sizeof(eb)); + eb[sizeof(eb)-1] = '\0'; + break; + } + } + } + + if(!eb[0]){ + if(!(gmode&MDADVN)){ + unknown_command(0); + return(-1); + } + + if(getenv("EDITOR")){ + strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb)); + eb[sizeof(eb)-1] = '\0'; + } + else + *eb = '\0'; + + while(!done){ + pid = mlreplyd_utf8("Which alternate editor ? ", eb, + sizeof(eb), QDEFLT, NULL); + switch(pid){ + case ABORT: + curwp->w_flag |= WFMODE; + return(-1); + case HELPCH: + emlwrite("no alternate editor help yet", NULL); + +/* take sleep and break out after there's help */ + sleep(3); + break; + case (CTRL|'L'): + sgarbf = TRUE; + update(); + break; + case TRUE: + case FALSE: /* does editor exist ? */ + if(*eb == '\0'){ /* leave silently? */ + mlerase(); + curwp->w_flag |= WFMODE; + return(-1); + } + + done++; + break; + default: + break; + } + } + } + } + + if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */ + emlwrite("Problem writing temp file for alt editor", NULL); + return(-1); + } + + strncat(eb, " ", sizeof(eb)-strlen(eb)-1); + eb[sizeof(eb)-1] = '\0'; + strncat(eb, fn, sizeof(eb)-strlen(eb)-1); + eb[sizeof(eb)-1] = '\0'; + + + for(i = 0; i <= ((Pmaster) ? term.t_nrow : term.t_nrow - 1); i++){ + movecursor(i, 0); + if(!i){ + fputs("Invoking alternate ", stdout); + fputs(f ? "speller..." : "editor...", stdout); + } + + peeol(); + } + + (*term.t_flush)(); + if(Pmaster) + (*Pmaster->tty_fix)(0); + else + vttidy(); + +#ifdef SIGCHLD + if(Pmaster){ + /* + * The idea here is to keep any mail connections our caller + * may have open while our child's out running around... + */ + pico_child_done = pico_child_jmp_ok = 0; + (void) signal(SIGCHLD, child_handler); + } +#endif + + if((child = fork()) > 0){ /* wait for the child to finish */ + ohup = signal(SIGHUP, SIG_IGN); /* ignore signals for now */ + oint = signal(SIGINT, SIG_IGN); +#if defined(SIGWINCH) && defined(TIOCGWINSZ) + osize = signal(SIGWINCH, SIG_IGN); +#endif + +#ifdef SIGCHLD + if(Pmaster){ + while(!pico_child_done){ + (*Pmaster->newmail)(0, 0); + if(!pico_child_done){ + if(setjmp(pico_child_state) == 0){ + pico_child_jmp_ok = 1; + sleep(600); + } + else + our_sigunblock(SIGCHLD); + } + + pico_child_jmp_ok = 0; + } + } +#endif + + trv = process_reap(child, &status, PR_NONE); + + signal(SIGHUP, ohup); /* restore signals */ + signal(SIGINT, oint); +#if defined(SIGWINCH) && defined(TIOCGWINSZ) + signal(SIGWINCH, osize); +#endif + + /* + * Report child's unnatural or unhappy exit... + */ + if(trv > 0 && status == 0){ + strncpy(result, "Alternate %s done", sizeof(result)); + result[sizeof(result)-1] = '\0'; + } + else { + snprintf(result, sizeof(result), "Alternate %%s terminated abnormally (%d)", + (trv == 0) ? status : -1); + if(f) + ret = -1; + else{ + snprintf(prmpt, sizeof(prmpt), "Alt editor failed, use file %.20s of size %%ld chars anyway", fn); + ret = -2; + } + } + } + else if(child == 0){ /* spawn editor */ + signal(SIGHUP, SIG_DFL); /* let editor handle signals */ + signal(SIGINT, SIG_DFL); +#if defined(SIGWINCH) && defined(TIOCGWINSZ) + signal(SIGWINCH, SIG_DFL); +#endif +#ifdef SIGCHLD + (void) signal(SIGCHLD, SIG_DFL); +#endif + if(execl("/bin/sh", "sh", "-c", fname_to_locale(eb), (char *) NULL) < 0) + exit(-1); + } + else { /* error! */ + snprintf(result, sizeof(result), "\007Can't fork %%s: %s", errstr(errno)); + ret = -1; + } + +#ifdef SIGCHLD + (void) signal(SIGCHLD, SIG_DFL); +#endif + + if(Pmaster) + (*Pmaster->tty_fix)(1); + + /* + * replace edited text with new text + */ + curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */ + + if(ret == -2){ + off_t filesize; + char prompt[128]; + + rv = fexist(fn, "r", &filesize); + if(rv == FIOSUC && filesize > 0){ + snprintf(prompt, sizeof(prompt), prmpt, (long) filesize); + /* clear bottom 3 rows */ + pclear(term.t_nrow-2, term.t_nrow); + i = mlyesno_utf8(prompt, FALSE); + if(i == TRUE){ + ret = 0; + strncpy(result, "OK, alternate %s done", sizeof(result)); + result[sizeof(result)-1] = '\0'; + } + else + ret = -1; + } + else + ret = -1; + } + + if(ret == 0) + readin(fn, 0, 0); /* read new text overwriting old */ + + our_unlink(fn); /* blast temp file */ + curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */ + ttopen(); /* reset the signals */ + pico_refresh(0, 1); /* redraw */ + update(); + emlwrite(result, &eml); + return(ret); +#else /* _WINDOWS */ + char eb[2 * PATH_MAX]; /* buf holding edit command */ + char *fn; /* tmp holder for file name */ + char errbuf[128]; + char **lp; + int status; + int done; + int rc; + + + if(Pmaster == NULL) + return(-1); + + if(gmode&MDSCUR){ + emlwrite("Alternate editor not available in restricted mode", NULL); + return(-1); + } + + eb[0] = '\0'; + + if(Pmaster->alt_ed){ + char *p; + + for(lp = Pmaster->alt_ed; *lp && **lp; lp++) + if(*lp[0] == '\"'){ + if(p = strchr(*lp + 1, '\"')){ + *p = '\0'; + rc = alt_editor_valid((*lp) + 1, eb, sizeof(eb)); + *p = '\"'; + if(rc){ + strncat(eb, p + 1, sizeof(eb)-strlen(eb)-1); + eb[sizeof(eb)-1] = '\0'; + break; + } + } + } + else if(p = strchr(*lp, ' ')){ + for(rc = 0; !rc; ){ + if(p) + *p = '\0'; + + rc = alt_editor_valid(*lp, eb, sizeof(eb)); + + if(p) + *p = ' '; + + if(rc){ + if(p){ + strncat(eb, p, sizeof(eb)-strlen(eb)-1); + eb[sizeof(eb)-1] = '\0'; + } + + break; + } + else if(p) + p = strchr(p + 1, ' '); + else + break; + } + + if(rc) + break; + } + else if(alt_editor_valid(*lp, eb, sizeof(eb))) + break; + } + + if(!eb[0]){ + if(!(gmode&MDADVN)){ + unknown_command(0); + return(-1); + } + + /* Guess which editor they want. */ + if(getenv("EDITOR")){ + strncpy(eb, (char *)getenv("EDITOR"), sizeof(eb)); + eb[sizeof(eb)-1] = '\0'; + } + else + *eb = '\0'; + + done = FALSE; + while(!done){ + rc = mlreplyd_utf8("Which alternate editor ? ", eb, + sizeof(eb),QDEFLT,NULL); + + switch(rc){ + case ABORT: + curwp->w_flag |= WFMODE; + return(-1); + case HELPCH: + emlwrite("no alternate editor help yet", NULL); + +/* take sleep and break out after there's help */ + sleep(3); + break; + case (CTRL|'L'): + sgarbf = TRUE; + update(); + break; + case TRUE: + case FALSE: /* does editor exist ? */ + if(*eb == '\0'){ /* leave silently? */ + mlerase(); + curwp->w_flag |= WFMODE; + return(-1); + } + + done++; + break; + default: + break; + } + } + } + + if((fn=writetmp(1, NULL)) == NULL){ /* get temp file */ + emlwrite("Problem writing temp file for alt editor", NULL); + return(-1); + } + + emlwrite("Waiting for alternate editor to finish...", NULL); + +#ifdef ALTED_DOT + if(eb[0] == '.' && eb[1] == '\0') { + status = mswin_exec_and_wait ("alternate editor", eb, fn, fn, + NULL, 0); + } + else +#endif /* ALTED_DOT */ + { /* just here in case ALTED_DOT is defined */ + strncat(eb, " ", sizeof(eb)-strlen(eb)-1); + eb[sizeof(eb)-1] = '\0'; + strncat(eb, fn, sizeof(eb)-strlen(eb)-1); + eb[sizeof(eb)-1] = '\0'; + + status = mswin_exec_and_wait ("alternate editor", eb, NULL, NULL, + NULL, 0); + } + + switch (status) { + + case 0: + /* + * Success: replace edited text with new text + */ + curbp->b_flag &= ~BFCHG; /* make sure old text gets blasted */ + readin(fn, 0, 0); /* read new text overwriting old */ + our_unlink(fn); /* blast temp file */ + curbp->b_flag |= BFCHG; /* mark dirty for packbuf() */ + ttopen(); /* reset the signals */ + pico_refresh(0, 1); /* redraw */ + return(0); + + + /* + * Possible errors. + */ + case -1: + /* Failed to map return from WinExec to a HTASK. */ + emlwrite("Problem finding alternate editor task handle.", NULL); + return (-1); + + case -2: + /* User decided to abandon the alternate editor.*/ + emlwrite("Alternate editor abandoned.", NULL); + return (-1); + + default: + mswin_exec_err_msg ("alternate editor", status, errbuf, sizeof(errbuf)); + emlwrite (errbuf, NULL); + return (-1); + } + return (-1); +#endif /*_WINDOWS */ +} + + + +#ifndef _WINDOWS +int +pathcat(char *buf, char **path, char *file) +{ + register int n = 0; + + while(**path && **path != ':'){ + if(n++ > MAXPATH) + return(FALSE); + + *buf++ = *(*path)++; + } + + if(n){ + if(n++ > MAXPATH) + return(FALSE); + + *buf++ = '/'; + } + + while((*buf = *file++) != '\0'){ + if(n++ > MAXPATH) + return(FALSE); + + buf++; + } + + if(**path == ':') + (*path)++; + + return(TRUE); +} + + +#ifdef SIGCHLD +/* + * child_handler - handle child status change signal + */ +RETSIGTYPE +child_handler(int sig) +{ + pico_child_done = 1; + if(pico_child_jmp_ok){ +#ifdef sco + /* + * Villy Kruse says: + * This probably only affects older unix systems with a "broken" sleep + * function such as AT&T System V rel 3.2 and systems derived from + * that version. The problem is that the sleep function on these + * versions of unix will set up a signal handler for SIGALRM which + * will longjmp into the sleep function when the alarm goes off. + * This gives problems if another signal handler longjmps out of the + * sleep function, thus making the stack frame for the signal function + * invalid, and when the ALARM handler later longjmps back into the + * sleep function it does no longer have a valid stack frame. + * My sugested fix is to cancel the pending alarm in the SIGCHLD + * handler before longjmp'ing. This shouldn't hurt as there + * shouldn't be any ALARM pending at this point except possibly from + * the sleep call. + * + * + * [Even though it shouldn't hurt, we'll make it sco-specific for now.] + * + * + * The sleep call might have set up a signal handler which would + * longjmp back into the sleep code, and that would cause a crash. + */ + signal(SIGALRM, SIG_IGN); /* Cancel signal handeler */ + alarm(0); /* might longjmp back into sleep */ +#endif + longjmp(pico_child_state, 1); + } +} +#endif /* SIGCHLD */ + +#else /* _WINDOWS */ +/* + * alt_editor_valid -- test the given exe name exists, and if so + * return full name in "cmdbuf". + */ +int +alt_editor_valid(char *exe, char *cmdbuf, size_t ncmdbuf) +{ + +/**** + **** This isn't the right way to do this. I believe we + **** should be doing all of this in TCHARs starting in + **** the alt_editor function instead of trying to convert + **** to char * here. + ****/ + + if(!exe) + return(FALSE); + +#ifdef ALTED_DOT + if(exe[0] == '.' && exe[1] == '\0') { + cmdbuf[0] = '.'; + cmdbuf[1] = '\0'; + return(TRUE); + } + else +#endif /* ALTED_DOT */ + + if((isalpha((unsigned char) *exe) && *exe == ':') || strchr(exe, '\\')){ + char *path; + + if(alt_editor_valid_fp(path = exe)){ + if(strchr(path, ' ')) + snprintf(cmdbuf, ncmdbuf, "\"%s\"", path); + else{ + strncpy(cmdbuf, path, ncmdbuf); + cmdbuf[ncmdbuf-1] = '\0'; + } + + return(TRUE); + } + } + else{ + TCHAR pathbuflpt[PATH_MAX+1]; + LPTSTR name, exelpt; + LPTSTR cmdbuflpt; + char *utf8; + + cmdbuflpt = (LPTSTR) MemAlloc(ncmdbuf * sizeof(TCHAR)); + cmdbuflpt[0] = L'0'; + + exelpt = utf8_to_lptstr(exe); + + if(SearchPath(NULL, exelpt, NULL, PATH_MAX+1, pathbuflpt, &name)){ + + if(_tcschr(pathbuflpt, L' ')) + _stprintf_s(cmdbuflpt, ncmdbuf, TEXT("\"%s\""), pathbuflpt); + else + _stprintf_s(cmdbuflpt, ncmdbuf, TEXT("%s"), pathbuflpt); + + if(exelpt) + fs_give((void **) &exelpt); + + utf8 = lptstr_to_utf8(cmdbuflpt); + if(utf8){ + strncpy(cmdbuf, utf8, ncmdbuf); + cmdbuf[ncmdbuf-1] = '\0'; + fs_give((void **) &utf8); + } + + if(cmdbuflpt) + MemFree((void *) cmdbuflpt); + + return(TRUE); + } + + if(exelpt) + fs_give((void **) &exelpt); + + if(cmdbuflpt) + MemFree((void *) cmdbuflpt); + } + + return(FALSE); +} + + +/* + * alt_editor_valid_fp -- test the given full path name exists + */ +int +alt_editor_valid_fp(path) + char *path; +{ + static char *exts[] = {".exe", ".com", ".bat", NULL}; + char pathcopy[PATH_MAX + 1], *dot = NULL; + int i, j; + + for(i = 0; pathcopy[i] = path[i]; i++) + if(path[i] == '.') + dot = &path[i]; + + if(dot && (!strucmp(dot, ".exe") + || !strucmp(dot, ".com") || !strucmp(dot, ".bat"))){ + if(fexist(path, "x", (off_t *) NULL) == FIOSUC) + return(TRUE); + } + else{ + for(j = 0; exts[j]; j++){ + strncpy(&pathcopy[i], exts[j], sizeof(pathcopy)-i); + pathcopy[sizeof(pathcopy)-1] = '\0'; + if(fexist(pathcopy, "x", (off_t *) NULL) == FIOSUC) + return(TRUE); + } + } + + return(FALSE); +} +#endif -- cgit v1.2.3-54-g00ecf