/* OS2.C: Operating specific I/O and Spawning functions for the OS/2 operating system for MicroEMACS 3.9 (C)opyright 1988 by Daniel M. Lawrence Modifications needed: check that we don't construct command lines and temporary filenames which are too large for their buffers. */ #include #include "estruct.h" #include "etype.h" #if OS2 #include "elang.h" #include "edef.h" /* * Create a subjob with a copy of the command intrepreter in it. When the * command interpreter exits, mark the screen as garbage so that you do a full * repaint. Bound to "^X C". The message at the start in VMS puts out a newline. * Under some (unknown) condition, you don't get one free when DCL starts up. */ PASCAL NEAR spawncli(f, n) { /* don't allow this command if restricted */ if (restflag) return(resterr()); movecursor(term.t_nrow, 0); /* Seek to last line. */ TTflush(); TTkclose(); shell(); TTkopen(); sgarbf = TRUE; return(TRUE); } /* * Run a one-liner in a subjob. When the command returns, wait for a single * character to be typed, then mark the screen as garbage so a full repaint is * done. Bound to "C-X !". */ PASCAL NEAR spawn(f, n) { register int s; char line[NLINE]; /* don't allow this command if restricted */ if (restflag) return(resterr()); if ((s=mlreply("!", line, NLINE)) != TRUE) return(s); movecursor(term.t_nrow - 1, 0); TTkclose(); system(line); TTkopen(); /* if we are interactive, pause here */ if (clexec == FALSE) { puts(TEXT6); /* "\r\n\n[End]" */ tgetc(); } sgarbf = TRUE; return (TRUE); } /* * Run an external program with arguments. When it returns, wait for a single * character to be typed, then mark the screen as garbage so a full repaint is * done. Bound to "C-X $". */ PASCAL NEAR execprg(f, n) { register int s; char line[NLINE]; /* don't allow this command if restricted */ if (restflag) return(resterr()); if ((s=mlreply("$", line, NLINE)) != TRUE) return(s); movecursor(term.t_nrow - 1, 0); TTkclose(); execprog(line); TTkopen(); /* if we are interactive, pause here */ if (clexec == FALSE) { puts(TEXT6); /* "\r\n\n[End]" */ tgetc(); } sgarbf = TRUE; return (TRUE); } /* * Pipe a one line command into a window * Bound to ^X @ * We use a unique temporary file name so that multiple instances of * MicroEMACS don't try to use the same file. */ PASCAL NEAR pipecmd(f, n) { register WINDOW *wp; /* pointer to new window */ register BUFFER *bp; /* pointer to buffer to zot */ register char *tmp; /* ptr to TMP DOS environment variable */ char line[NLINE]; /* command line send to shell */ static char bname[] = "command"; static char filnam[NSTRING]; char *getenv(); /* don't allow this command if restricted */ if (restflag) return(resterr()); /* get rid of the command output buffer if it exists */ if ((bp=bfind(bname, FALSE, 0)) != FALSE) { /* try to make sure we are off screen */ wp = wheadp; while (wp != NULL) { if (wp->w_bufp == bp) { onlywind(FALSE, 1); break; } wp = wp->w_wndp; } /* get rid of the existing command buffer */ if (zotbuf(bp) != TRUE) return(FALSE); } /* get the command to pipe in */ if (mlreply("@", line, NLINE) != TRUE) return(FALSE); /* Call mktemp() to get a unique filename in the tmp directory. */ if ((tmp = getenv("TMP")) == NULL) filnam[0] = 0; else { strcpy(filnam, tmp); if (filnam[strlen(filnam) - 1] != '\\') strcat(filnam, "\\"); } strcat(filnam,"eXXXXXX"); mktemp(filnam); /* redirect the command output to the output file */ strcat(line, " >>"); strcat(line, filnam); movecursor(term.t_nrow - 1, 0); /* execute the command */ TTkclose(); system(line); TTkopen(); sgarbf = TRUE; /* did the output file get generated? */ if (access( filnam, 0) != 0) return(FALSE); /* split the current window to make room for the command output */ if (splitwind(FALSE, 1) == FALSE) return(FALSE); /* and read the stuff in */ if (getfile(filnam, FALSE) == FALSE) return(FALSE); /* rename the buffer */ strcpy( curwp->w_bufp->b_bname, "command"); /* make this window in VIEW mode, update all mode lines */ curwp->w_bufp->b_mode |= MDVIEW; wp = wheadp; while (wp != NULL) { wp->w_flag |= WFMODE; wp = wp->w_wndp; } /* and get rid of the temporary file */ unlink(filnam); return(TRUE); } /* * filter a buffer through an external DOS program * Bound to ^X # * We use unique temporary file names so that multiple instances of * MicroEMACS don't try to use the same file. */ PASCAL NEAR filter(f, n) { register int s; /* return status from CLI */ register BUFFER *bp; /* pointer to buffer to zot */ char line[NLINE]; /* command line send to shell */ char tmpnam[NFILEN]; /* place to store real file name */ char *tmp; /* ptr to TMP DOS environment variable */ static char filnam1[NSTRING]; static char filnam2[NSTRING]; /* don't allow this command if restricted */ if (restflag) return(resterr()); if (curbp->b_mode&MDVIEW) /* don't allow this command if */ return(rdonly()); /* we are in read only mode */ /* get the filter name and its args */ if ((s=mlreply("#", line, NLINE)) != TRUE) return(s); /* Call mktemp() to get unique filenames in the tmp directory. */ if ((tmp = getenv("TMP")) == NULL) filnam1[0] = filnam2[0] = 0; else { strcpy(filnam1, tmp); strcpy(filnam2, tmp); if (filnam1[strlen(filnam1) - 1] != '\\') { strcat(filnam1, "\\"); strcat(filnam2, "\\"); } } strcat(filnam1,"eXXXXXX"); strcat(filnam2,"eXXXXXX"); mktemp(filnam1); mktemp(filnam2); /* setup the proper file names */ bp = curbp; strcpy(tmpnam, bp->b_fname); /* save the original name */ strcpy(bp->b_fname, filnam1); /* set it to our new one */ /* write it out, checking for errors */ if (writeout(filnam1) != TRUE) { mlwrite(TEXT2); /* "[Cannot write filter file]" */ strcpy(bp->b_fname, tmpnam); return(FALSE); } strcat(line, " <"); /* construct the command line */ strcat(line, filnam1); strcat(line, " >"); strcat(line, filnam2); movecursor(term.t_nrow - 1, 0); TTkclose(); system(line); TTkopen(); sgarbf = TRUE; s = TRUE; /* on failure, escape gracefully */ if (s != TRUE || (readin(filnam2,FALSE) == FALSE)) { mlwrite(TEXT3); /* "[Execution failed]" */ strcpy(bp->b_fname, tmpnam); unlink(filnam1); unlink(filnam2); return(s); } /* reset file name */ strcpy(bp->b_fname, tmpnam); /* restore name */ bp->b_flag |= BFCHG; /* flag it as changed */ /* and get rid of the temporary file */ unlink(filnam1); unlink(filnam2); return(TRUE); } /* SHELL: Bring up a shell. */ shell(void) { char *shell; /* Name of system command processor */ /* get name of system shell */ if ((shell = getenv("COMSPEC")) == NULL) { return(FALSE); /* No shell located */ } /* * We are actually setting up a shell inside a shell here. * Is there a better way? */ return(system(shell)); } PASCAL NEAR execprog( char *cmd) { char args[NSTRING]; /* args passed to program */ char *sp; char failName[1]; char prog[NSTRING]; /* name of program */ USHORT i; RESULTCODES results; char envBlk[2]; envBlk[0] = envBlk[1] = '\0'; /* * Parse the command name from the command line and copy it * into the prog and args arrays. */ i = 0; while (cmd[i] && (cmd[i] != ' ') && (cmd[i] != '\t')) { prog[i] = args[i] = cmd[i]; i++; } prog[i] = args[i] = 0; /* terminate with a null */ /* skip whitespace and copy the args */ while (cmd[i] && ((cmd[i] == ' ') || (cmd[i] == '\t'))) i++; while (cmd[i]) { args[i] = cmd[i]; i++; } args[i] = args[i + 1] = 0; /* terminate with 2 nulls */ /* look up the program on the path, trying various extentions */ if ((sp = flook(prog, TRUE)) == NULL) if ((sp = flook(strcat(prog, ".exe"), TRUE)) == NULL) { strcpy(&prog[strlen(prog)-4], ".com"); if ((sp = flook(prog, TRUE)) == NULL) return(FALSE); } strcpy(prog, sp); /* * Execute the program synchronously. We wait for child * to return. */ return (0 == DosExecPgm( failName, 1, EXEC_SYNC, args, envBlk, &results, prog)); } /* return a system dependant string with the current time */ char * PASCAL NEAR timeset() { register char *sp; /* temp string pointer */ char buf[16]; /* time data buffer */ extern char *ctime(); time(buf); sp = ctime(buf); sp[strlen(sp)-1] = 0; return(sp); } /* extcode: resolve OS/2 extended character codes encoding the proper sequences into emacs printable character specifications */ int extcode(c) unsigned c; /* byte following a zero extended char byte */ /* High order normally contains 0x00 but may contain 0xE0 if an AT-style keyboard is attached */ { /* function keys 1 through 9. High-order will be 0x00 */ if (c >= 0x3B && c < 0x44) return(SPEC | c - 0x3A + '0'); /* function key 10 */ if (c == 0x44) return(SPEC | '0'); /* shifted function keys */ if (c >= 0x54 && c < 0x5D) return(SPEC | SHFT | c - 0x53 + '0'); if (c == 0x5D) return(SPEC | SHFT | '0'); /* control function keys */ if (c >= 0x5E && c < 0x67) return(SPEC | CTRL | c - 0x5D + '0'); if (c == 0x67) return(SPEC | CTRL | '0'); /* ALTed function keys */ if (c >= 0x68 && c < 0x71) return(SPEC | ALTD | c - 0x67 + '0'); if (c == 0x71) return(SPEC | ALTD | '0'); /* ALTed number keys */ /* This doesn't work for OS/2. Using ALT in conjunction with the number keys lets you enter any (decimal) character value you want. ----- if (c >= 0x78 && c < 0x81) return(ALTD | c - 0x77 + '0'); if (c == 0x82) return(ALTD | '0'); ----- */ /* some others as well */ switch (c) { case 3: return(0); /* null */ case 0x0F: return(SHFT | CTRL | 'I'); /* backtab */ case 0x10: return(ALTD | 'Q'); case 0x11: return(ALTD | 'W'); case 0x12: return(ALTD | 'E'); case 0x13: return(ALTD | 'R'); case 0x14: return(ALTD | 'T'); case 0x15: return(ALTD | 'Y'); case 0x16: return(ALTD | 'U'); case 0x17: return(ALTD | 'I'); case 0x18: return(ALTD | 'O'); case 0x19: return(ALTD | 'P'); case 0x1E: return(ALTD | 'A'); case 0x1F: return(ALTD | 'S'); case 0x20: return(ALTD | 'D'); case 0x21: return(ALTD | 'F'); case 0x22: return(ALTD | 'G'); case 0x23: return(ALTD | 'H'); case 0x24: return(ALTD | 'J'); case 0x25: return(ALTD | 'K'); case 0x26: return(ALTD | 'L'); case 0x2C: return(ALTD | 'Z'); case 0x2D: return(ALTD | 'X'); case 0x2E: return(ALTD | 'C'); case 0x2F: return(ALTD | 'V'); case 0x30: return(ALTD | 'B'); case 0x31: return(ALTD | 'N'); case 0x32: return(ALTD | 'M'); case 0x47: return(CTRL | 'A'); /* home */ case 0x48: return(SPEC | 'P'); /* cursor up */ case 0x49: return(SPEC | 'Z'); /* page up */ case 0x4B: return(SPEC | 'B'); /* cursor left */ case 0x4C: return(SPEC | 'L'); /* NP 5 */ case 0x4D: return(SPEC | 'F'); /* cursor right */ case 0x4F: return(CTRL | 'E'); /* end */ case 0x50: return(SPEC | 'N'); /* cursor down */ case 0x51: return(SPEC | 'V'); /* page down */ case 0x52: return(SPEC | 'C'); /* insert */ case 0x53: return(SPEC | 'D'); /* delete */ case 0x73: return(SPEC | CTRL | 'B'); /* control left */ case 0x8F: return(SPEC | CTRL | 'L'); /* control NP5 */ case 0x74: return(SPEC | CTRL | 'F'); /* control right */ case 0x75: return(SPEC | CTRL | '>'); /* control end */ case 0x76: return(SPEC | CTRL | 'V'); /* control page down */ case 0x77: return(SPEC | CTRL | '<'); /* control home */ case 0x84: return(SPEC | CTRL | 'Z'); /* control page up */ case 0x8D: return(SPEC | CTRL | 'P'); /* control up */ case 0x91: return(SPEC | CTRL | 'N'); /* control down */ case 0x85: return(SPEC | CTRL | SHFT | '1'); /* F11 */ case 0x86: return(SPEC | CTRL | SHFT | '2'); /* F12 */ case 0x87: return(SPEC | CTRL | SHFT | '3'); /* shift F11 */ case 0x88: return(SPEC | CTRL | SHFT | '4'); /* shift F12 */ case 0x89: return(SPEC | CTRL | SHFT | '5'); /* control F11 */ case 0x8A: return(SPEC | CTRL | SHFT | '6'); /* control F12 */ case 0x8B: return(SPEC | CTRL | SHFT | '7'); /* alt F11 */ case 0x8C: return(SPEC | CTRL | SHFT | '8'); /* alt F12 */ case 0xE047: return(SPEC | '<'); /* grey home */ case 0xE048: return(SPEC | 'P'); /* grey cursor up */ case 0xE049: return(SPEC | 'Z'); /* grey page up */ case 0xE04B: return(SPEC | 'B'); /* grey cursor left */ case 0xE04D: return(SPEC | 'F'); /* grey cursor right */ case 0xE04F: return(SPEC | '>'); /* grey end */ case 0xE050: return(SPEC | 'N'); /* grey cursor down */ case 0xE051: return(SPEC | 'V'); /* grey page down */ case 0xE052: return(SPEC | 'C'); /* grey insert */ case 0xE053: return(SPEC | 'D'); /* grey delete */ case 0xE073: return(SPEC | CTRL | 'B'); /* control grey left */ case 0xE074: return(SPEC | CTRL | 'F'); /* control grey right */ case 0xE075: return(SPEC | CTRL | '>'); /* control grey end */ case 0xE076: return(SPEC | CTRL | 'V'); /* control grey page down */ case 0xE077: return(SPEC | CTRL | '<'); /* control grey home */ case 0xE084: return(SPEC | CTRL | 'Z'); /* control grey page up */ case 0xE08D: return(SPEC | CTRL | 'P'); /* control grey up */ case 0xE091: return(SPEC | CTRL | 'P'); /* control grey down */ case 0xE097: return(SPEC | '<'); /* alt grey home */ case 0xE098: return(SPEC | 'P'); /* alt grey cursor up */ case 0xE099: return(SPEC | 'Z'); /* alt grey page up */ case 0xE09B: return(SPEC | 'B'); /* alt grey cursor left */ case 0xE09D: return(SPEC | 'F'); /* alt grey cursor right */ case 0xE09F: return(SPEC | '>'); /* alt grey end */ case 0xE0A0: return(SPEC | 'N'); /* alt grey cursor down */ case 0xE0A1: return(SPEC | 'V'); /* alt grey page down */ case 0xE0A2: return(SPEC | 'C'); /* alt grey insert */ case 0xE0A3: return(SPEC | 'D'); /* alt grey delete */ } return(ALTD | c); } #if COMPLET /* FILE Directory routines */ char path[NFILEN]; /* path of file to find */ char rbuf[NFILEN]; /* return file buffer */ FILEFINDBUF fileblock; HDIR dHand = 1; USHORT entries; /* do a wild card directory search (for file name completion) */ char * PASCAL NEAR getffile(fspec) char *fspec; /* pattern to match */ { register int index; /* index into various strings */ register int point; /* index into other strings */ register int extflag; /* does the file have an extention? */ char fname[NFILEN]; /* file/path for DOS call */ entries = 1; /* first parse the file path off the file spec */ strcpy(path, fspec); index = strlen(path) - 1; while (index >= 0 && (path[index] != '/' && path[index] != '\\' && path[index] != ':')) --index; path[index+1] = 0; /* check for an extension */ point = strlen(fspec) - 1; extflag = FALSE; while (point >= 0) { if (fspec[point] == '.') { extflag = TRUE; break; } point--; } /* construct the composite wild card spec */ strcpy(fname, path); strcat(fname, &fspec[index+1]); strcat(fname, "*"); if (extflag == FALSE) strcat(fname, ".*"); /* and call for the first file */ DosFindFirst(fname,&dHand,0,&fileblock,sizeof fileblock,&entries,0L); if (entries <= 0) return(NULL); /* return the first file name! */ strcpy(rbuf, path); strcat(rbuf, fileblock.achName); mklower(rbuf); return(rbuf); } char * PASCAL NEAR getnfile() { register int index; /* index into various strings */ register int point; /* index into other strings */ register int extflag; /* does the file have an extention? */ char fname[NFILEN]; /* file/path for DOS call */ /* and call for the first file */ DosFindNext(dHand,&fileblock,sizeof fileblock,&entries); if (entries <= 0) return(NULL); /* return the first file name! */ strcpy(rbuf, path); strcat(rbuf, fileblock.achName); mklower(rbuf); return(rbuf); } #else char * PASCAL NEAR getffile(fspec) char *fspec; /* file to match */ { return(NULL); } char * PASCAL NEAR getnfile() { return(NULL); } #endif #endif