1 /* XVTDOS.C: Operating specific I/O and Spawning functions
2 under the XVT portability system
3 for MicroEMACS 4.00
4 (C)Copyright 1995 by Daniel M. Lawrence
5 */
6
7 #include <stdio.h>
8 #include "estruct.h"
9 #include "eproto.h"
10
11 #ifdef XVT
12 #include "edef.h"
13 #include "elang.h"
14
15 /* The Mouse driver only works with typeahead defined */
16 #if MOUSE
17 #undef TYPEAH
18 #define TYPEAH 1
19 #endif
20
21 #if TURBO
22 #include <conio.h>
23 #include <dir.h>
24 #include <dos.h>
25 #include <bios.h>
26
27 struct ffblk fileblock; /* structure for directory searches */
28 #endif
29 #if MSC | ZTC
30 #include <dos.h>
31
32 struct find_t fileblock; /* structure for directory searches */
33 #endif
34
35 #if LATTICE | MSC | TURBO | IC | MWC | ZTC
36 union REGS rg; /* cpu register for use of DOS calls */
37 struct SREGS segreg; /* cpu segment registers */
38 int nxtchar = -1; /* character held from type ahead */
39 #endif
40
41 #if MSC | TURBO
42 #include <process.h>
43 #endif
44
45 #if IC
46 #include <time.h>
47 #endif
48
49 /* Some global variable */
50 #define INBUFSIZ 40
51 static int mexist; /* is the mouse driver installed? */
52 static int nbuttons; /* number of buttons on the mouse */
53 static int oldbut; /* Previous state of mouse buttons */
54 static int oldcol; /* previous x position of mouse */
55 static int oldrow; /* previous y position of mouse */
56
57 int PASCAL NEAR execprog(char *cmd);
58
59 /* input buffers and pointers */
60
61 #define IBUFSIZE 64 /* this must be a power of 2 */
62
63 unsigned char in_buf[IBUFSIZE]; /* input character buffer */
64 int in_next = 0; /* pos to retrieve next input character */
65 int in_last = 0; /* pos to place most recent input character */
66
in_init()67 in_init() /* initialize the input buffer */
68
69 {
70 in_next = in_last = 0;
71 }
72
in_check()73 in_check() /* is the input buffer non-empty? */
74
75 {
76 if (in_next == in_last)
77 return(FALSE);
78 else
79 return(TRUE);
80 }
81
in_put(event)82 in_put(event)
83
84 int event; /* event to enter into the input buffer */
85
86 {
87 in_buf[in_last++] = event;
88 in_last &= (IBUFSIZE - 1);
89 }
90
in_get()91 int in_get() /* get an event from the input buffer */
92
93 {
94 register int event; /* event to return */
95
96 event = in_buf[in_next++];
97 in_next &= (IBUFSIZE - 1);
98 return(event);
99 }
100
101 /*
102 * This function is called once to set up the terminal device streams.
103 */
104
ttopen()105 int PASCAL NEAR ttopen()
106
107 {
108
109 /* on all screens we are not sure of the initial position
110 of the cursor */
111 ttrow = 999;
112 ttcol = 999;
113 strcpy(os, "XVT");
114
115 mexist = TRUE;
116 nbuttons = 3;
117
118 /* initialize our character input queue */
119 in_init();
120
121 }
122
123 /*
124 * This function gets called just before we go back home to the command
125 * interpreter. On VMS it puts the terminal back in a reasonable state.
126 * Another no-operation on CPM.
127 */
ttclose()128 int PASCAL NEAR ttclose()
129 {
130 /* nothing here! */
131 }
132
133 /*
134 * Flush terminal buffer. Does real work where the terminal output is buffered
135 * up. A no-operation on systems where byte at a time terminal I/O is done.
136 */
ttflush()137 int PASCAL NEAR ttflush()
138 {
139 }
140
141 /* typahead: Check to see if any characters are already in the
142 keyboard buffer
143 */
144
typahead()145 int PASCAL NEAR typahead()
146
147 {
148 return(in_check());
149 }
150
151 /*
152 * Create a subjob with a copy of the command intrepreter in it. When the
153 * command interpreter exits, mark the screen as garbage so that you do a full
154 * repaint. Bound to "^X C".
155 */
156
spawncli(f,n)157 int PASCAL NEAR spawncli(f, n)
158
159 int f, n;
160
161 {
162 /* don't allow this command if restricted */
163 if (restflag)
164 return(resterr());
165
166 movecursor(term.t_nrow, 0); /* Seek to last line. */
167 TTflush();
168 TTkclose();
169 shellprog("");
170 #if WINDOW_TEXT
171 refresh_screen(first_screen);
172 #endif
173 TTkopen();
174 sgarbf = TRUE;
175 return(TRUE);
176 }
177
178 /*
179 * Run a one-liner in a subjob. When the command returns, wait for a single
180 * character to be typed, then mark the screen as garbage so a full repaint is
181 * done. Bound to "C-X !".
182 */
spawn(f,n)183 int PASCAL NEAR spawn(f, n)
184
185 int f, n;
186
187 {
188 register int s;
189 char line[NLINE];
190
191 /* don't allow this command if restricted */
192 if (restflag)
193 return(resterr());
194
195 if ((s=mlreply("!", line, NLINE)) != TRUE)
196 return(s);
197 movecursor(term.t_nrow - 1, 0);
198 TTkclose();
199 shellprog(line);
200 TTkopen();
201
202 /* if we are interactive, pause here */
203 if (clexec == FALSE) {
204 #if XVT == 0
205 printf(TEXT227);
206 /* "\n--- Press any key to Continue ---" */
207 #endif
208 tgetc();
209 }
210 #if WINDOW_TEXT
211 refresh_screen(first_screen);
212 #endif
213 sgarbf = TRUE;
214 return (TRUE);
215 }
216
217 /*
218 * Run an external program with arguments. When it returns, wait for a single
219 * character to be typed, then mark the screen as garbage so a full repaint is
220 * done. Bound to "C-X $".
221 */
222
execprg(f,n)223 int PASCAL NEAR execprg(f, n)
224
225 {
226 register int s;
227 char line[NLINE];
228
229 /* don't allow this command if restricted */
230 if (restflag)
231 return(resterr());
232
233 if ((s=mlreply("$", line, NLINE)) != TRUE)
234 return(s);
235 movecursor(term.t_nrow - 1, 0);
236 TTkclose();
237 execprog(line);
238 #if WINDOW_TEXT
239 refresh_screen(first_screen);
240 #endif
241 TTkopen();
242 /* if we are interactive, pause here */
243 if (clexec == FALSE) {
244 mlputs(TEXT6);
245 /* "\r\n\n[End]" */
246 tgetc();
247 }
248 sgarbf = TRUE;
249 return (TRUE);
250 }
251
252 /*
253 * Pipe a one line command into a window
254 * Bound to ^X @
255 */
pipecmd(f,n)256 int PASCAL NEAR pipecmd(f, n)
257
258 int f, n;
259
260 {
261 register EWINDOW *wp; /* pointer to new window */
262 register BUFFER *bp; /* pointer to buffer to zot */
263 register char *tmp; /* ptr to TMP DOS environment variable */
264 FILE *fp;
265 char line[NLINE]; /* command line send to shell */
266 static char bname[] = "command";
267 static char filnam[NSTRING] = "command";
268
269 /* don't allow this command if restricted */
270 if (restflag)
271 return(resterr());
272
273 if ((tmp = getenv("TMP")) == NULL)
274 filnam[0] = 0;
275 else {
276 strcpy(filnam, tmp);
277 if (filnam[strlen(filnam) - 1] != '\\')
278 strcat(filnam, "\\");
279 }
280 strcat(filnam,"command");
281
282 /* get the command to pipe in */
283 if (mlreply("@", line, NLINE) != TRUE)
284 return(FALSE);
285
286 /* get rid of the command output buffer if it exists */
287 if ((bp=bfind(bname, FALSE, 0)) != FALSE) {
288 /* try to make sure we are off screen */
289 wp = wheadp;
290 while (wp != NULL) {
291 if (wp->w_bufp == bp) {
292 onlywind(FALSE, 1);
293 break;
294 }
295 wp = wp->w_wndp;
296 }
297 /* get rid of the existing command buffer */
298 if (zotbuf(bp) != TRUE)
299 return(FALSE);
300 }
301
302 /* redirect the command output to the output file */
303 strcat(line, " >>");
304 strcat(line, filnam);
305 movecursor(term.t_nrow - 1, 0);
306
307 /* execute the command */
308 TTkclose();
309 shellprog(line);
310 #if WINDOW_TEXT
311 refresh_screen(first_screen);
312 #endif
313 TTkopen();
314 sgarbf = TRUE;
315
316 /* did the output file get generated? */
317 if ((fp = fopen(filnam, "r")) == NULL)
318 return(FALSE);
319 fclose(fp);
320
321 /* split the current window to make room for the command output */
322 if (splitwind(FALSE, 1) == FALSE)
323 return(FALSE);
324
325 /* and read the stuff in */
326 if (getfile(filnam, FALSE) == FALSE)
327 return(FALSE);
328
329 /* make this window in VIEW mode, update all mode lines */
330 curwp->w_bufp->b_mode |= MDVIEW;
331 wp = wheadp;
332 while (wp != NULL) {
333 wp->w_flag |= WFMODE;
334 wp = wp->w_wndp;
335 }
336
337 /* and get rid of the temporary file */
338 unlink(filnam);
339 return(TRUE);
340 }
341
342 /*
343 * filter a buffer through an external DOS program
344 * Bound to ^X #
345 */
filter(f,n)346 int PASCAL NEAR filter(f, n)
347
348 int f, n;
349
350 {
351 register int s; /* return status from CLI */
352 register BUFFER *bp; /* pointer to buffer to zot */
353 char line[NLINE]; /* command line send to shell */
354 char tmpnam[NFILEN]; /* place to store real file name */
355 static char bname1[] = "fltinp";
356
357 static char filnam1[] = "fltinp";
358 static char filnam2[] = "fltout";
359
360 /* don't allow this command if restricted */
361 if (restflag)
362 return(resterr());
363
364 if (curbp->b_mode&MDVIEW) /* don't allow this command if */
365 return(rdonly()); /* we are in read only mode */
366
367 /* get the filter name and its args */
368 if ((s=mlreply("#", line, NLINE)) != TRUE)
369 return(s);
370
371 /* setup the proper file names */
372 bp = curbp;
373 strcpy(tmpnam, bp->b_fname); /* save the original name */
374 strcpy(bp->b_fname, bname1); /* set it to our new one */
375
376 /* write it out, checking for errors */
377 if (writeout(filnam1, "w") != TRUE) {
378 mlwrite(TEXT2);
379 /* "[Cannot write filter file]" */
380 strcpy(bp->b_fname, tmpnam);
381 return(FALSE);
382 }
383
384 strcat(line," <fltinp >fltout");
385 movecursor(term.t_nrow - 1, 0);
386 TTkclose();
387 shellprog(line);
388 #if WINDOW_TEXT
389 refresh_screen(first_screen);
390 #endif
391 TTkopen();
392 sgarbf = TRUE;
393 s = TRUE;
394
395 /* on failure, escape gracefully */
396 if (s != TRUE || (readin(filnam2,FALSE) == FALSE)) {
397 mlwrite(TEXT3);
398 /* "[Execution failed]" */
399 strcpy(bp->b_fname, tmpnam);
400 unlink(filnam1);
401 unlink(filnam2);
402 return(s);
403 }
404
405 /* reset file name */
406 strcpy(bp->b_fname, tmpnam); /* restore name */
407 bp->b_flag |= BFCHG; /* flag it as changed */
408
409 /* and get rid of the temporary file */
410 unlink(filnam1);
411 unlink(filnam2);
412 return(TRUE);
413 }
414
415 #if LATTICE
416 extern int _oserr;
417 #endif
418
419 #if MWC
420 extern int errno;
421 #endif
422
423 #if MSC
424 extern int _doserrno;
425 #endif
426
427 /* SHELLPROG: Execute a command in a subshell */
428
shellprog(cmd)429 int PASCAL NEAR shellprog(cmd)
430
431 char *cmd; /* Incoming command line to execute */
432
433 {
434 char *shell; /* Name of system command processor */
435 char swchar; /* switch character to use */
436 union REGS regs; /* parameters for dos call */
437 char comline[NSTRING]; /* constructed command line */
438
439 /* detect current switch character and set us up to use it */
440 regs.h.ah = 0x37; /* get setting data */
441 regs.h.al = 0x00; /* get switch character */
442 intdos(®s, ®s);
443 swchar = (char)regs.h.dl;
444
445 /* get name of system shell */
446 if ((shell = getenv("COMSPEC")) == NULL) {
447 return(FALSE); /* No shell located */
448 }
449
450 /* trim leading whitespace off the command */
451 while (*cmd == ' ' || *cmd == '\t') /* find out if null command */
452 cmd++;
453
454 /** If the command line is not empty, bring up the shell **/
455 /** and execute the command. Otherwise, bring up the **/
456 /** shell in interactive mode. **/
457
458 if (*cmd) {
459 strcpy(comline, shell);
460 strcat(comline, " ");
461 comline[strlen(comline) + 1] = 0;
462 comline[strlen(comline)] = swchar;
463 strcat(comline, "c ");
464 strcat(comline, cmd);
465 return(execprog(comline));
466 } else
467 return(execprog(shell));
468 }
469
470 /* EXECPROG: A function to execute a named program
471 with arguments
472 */
473
474 #if LATTICE | MWC
475 #define CFLAG 1
476 #endif
477
execprog(cmd)478 int PASCAL NEAR execprog(cmd)
479
480 char *cmd; /* Incoming command line to execute */
481
482 {
483 char *sp; /* temporary string pointer */
484 int rv; /* numeric return value from subprocess */
485 char f1[38]; /* FCB1 area (not initialized */
486 char f2[38]; /* FCB2 area (not initialized */
487 char prog[NSTRING]; /* program filespec */
488 char tail[NSTRING]; /* command tail with length byte */
489 union REGS regs; /* parameters for dos call */
490 #if MWC == 0
491 struct SREGS segreg; /* segment registers for dis call */
492 #endif
493 struct Pblock { /* EXEC parameter block */
494 short envptr; /* 2 byte pointer to environment string */
495 char *cline; /* 4 byte pointer to command line */
496 char *fcb1; /* 4 byte pointer to FCB at PSP+5Ch */
497 char *fcb2; /* 4 byte pointer to FCB at PSP+6Ch */
498 } pblock;
499
500 /* parse the command name from the command line */
501 sp = prog;
502 while (*cmd && (*cmd != ' ') && (*cmd != '\t'))
503 *sp++ = *cmd++;
504 *sp = 0;
505
506 /* and parse out the command tail */
507 while (*cmd && ((*cmd == ' ') || (*cmd == '\t')))
508 ++cmd;
509 *tail = (char)(strlen(cmd)); /* record the byte length */
510 strcpy(&tail[1], cmd);
511 strcat(&tail[1], "\r");
512
513 /* look up the program on the path trying various extentions */
514 if ((sp = flook(prog, TRUE)) == NULL)
515 if ((sp = flook(strcat(prog, ".exe"), TRUE)) == NULL) {
516 strcpy(&prog[strlen(prog)-4], ".com");
517 if ((sp = flook(prog, TRUE)) == NULL)
518 return(FALSE);
519 }
520 strcpy(prog, sp);
521
522 #if MWC == 0
523 /* get a pointer to this PSPs environment segment number */
524 #if IC
525 _segread(&segreg);
526 #else /* IC */
527 segread(&segreg);
528 #endif /* IC */
529 #endif /* MWC == 0 */
530
531 /* set up the EXEC parameter block */
532 pblock.envptr = 0; /* make the child inherit the parents env */
533 pblock.fcb1 = f1; /* point to a blank FCB */
534 pblock.fcb2 = f2; /* point to a blank FCB */
535 pblock.cline = tail; /* parameter line pointer */
536
537 /* and make the call */
538 regs.h.ah = 0x4b; /* EXEC Load or Execute a Program */
539 regs.h.al = 0x00; /* load end execute function subcode */
540 #if MWC
541 regs.x.ds = ((unsigned long)(prog) >> 16); /* program name ptr */
542 regs.x.dx = (unsigned int)(prog);
543 regs.x.es = regs.x.ds;
544 /*regs.x.es = ((unsigned long)(&pblock) >> 16); * set up param block ptr */
545 regs.x.bx = (unsigned int)(&pblock);
546 #endif
547 #if LATTICE | MSC | TURBO | IC | ZTC
548 segreg.ds = ((unsigned long)(prog) >> 16); /* program name ptr */
549 regs.x.dx = (unsigned int)(prog);
550 segreg.es = ((unsigned long)(&pblock) >> 16); /* set up param block ptr */
551 regs.x.bx = (unsigned int)(&pblock);
552 #endif
553 #if LATTICE
554 if ((intdosx(®s, ®s, &segreg) & CFLAG) == 0) {
555 regs.h.ah = 0x4d; /* get child process return code */
556 intdos(®s, ®s); /* go do it */
557 rv = regs.x.ax; /* save child's return code */
558 } else
559 rv = -_oserr; /* failed child call */
560 #endif
561 #if MWC
562 intcall(®s, ®s, DOSINT);
563 if ((regs.x.flags & CFLAG) == 0) {
564 regs.h.ah = 0x4d; /* get child process return code */
565 intcall(®s, ®s, DOSINT); /* go do it */
566 rv = regs.x.ax; /* save child's return code */
567 } else
568 rv = -errno; /* failed child call */
569 #endif
570 #if TURBO | IC | MSC | ZTC
571 intdosx(®s, ®s, &segreg);
572 if (regs.x.cflag == 0) {
573 regs.h.ah = 0x4d; /* get child process return code */
574 intdos(®s, ®s); /* go do it */
575 rv = regs.x.ax; /* save child's return code */
576 } else
577 #if IC
578 rv = -1;
579 #else /* IC */
580 rv = -_doserrno; /* failed child call */
581 #endif /* IC */
582 #endif
583 strcpy(rval, int_asc(rv));
584 return((rval < 0) ? FALSE : TRUE);
585 }
586
587 /* return a system dependant string with the current time */
588
timeset()589 char *PASCAL NEAR timeset()
590
591 {
592 #if MWC | TURBO | IC | MSC | ZTC
593 register char *sp; /* temp string pointer */
594 char buf[16]; /* time data buffer */
595 extern char *ctime();
596
597 #if IC
598 time((time_t *)buf);
599 sp = ctime((time_t *)buf);
600 #else
601 time(buf);
602 sp = ctime(buf);
603 #endif
604 sp[strlen(sp)-1] = 0;
605 return(sp);
606 #else
607 return(errorm);
608 #endif
609 }
610
611 #if TURBO
612 /* FILE Directory routines */
613
614 char path[NFILEN]; /* path of file to find */
615 char rbuf[NFILEN]; /* return file buffer */
616
617 /* do a wild card directory search (for file name completion) */
618
getffile(fspec)619 char *PASCAL NEAR getffile(fspec)
620
621 char *fspec; /* pattern to match */
622
623 {
624 register int index; /* index into various strings */
625 register int point; /* index into other strings */
626 register int extflag; /* does the file have an extention? */
627 char fname[NFILEN]; /* file/path for DOS call */
628
629 /* first parse the file path off the file spec */
630 strcpy(path, fspec);
631 index = strlen(path) - 1;
632 while (index >= 0 && (path[index] != '/' &&
633 path[index] != '\\' && path[index] != ':'))
634 --index;
635 path[index+1] = 0;
636
637 /* check for an extension */
638 point = strlen(fspec) - 1;
639 extflag = FALSE;
640 while (point > index) {
641 if (fspec[point] == '.') {
642 extflag = TRUE;
643 break;
644 }
645 point--;
646 }
647
648 /* construct the composite wild card spec */
649 strcpy(fname, path);
650 strcat(fname, &fspec[index+1]);
651 strcat(fname, "*");
652 if (extflag == FALSE)
653 strcat(fname, ".*");
654
655 /* and call for the first file */
656 if (findfirst(fname, &fileblock, FA_DIREC) == -1)
657 return(NULL);
658
659 /* return the first file name! */
660 strcpy(rbuf, path);
661 strcat(rbuf, fileblock.ff_name);
662 mklower(rbuf);
663 if (fileblock.ff_attrib == 16)
664 strcat(rbuf, DIRSEPSTR);
665 return(rbuf);
666 }
667
getnfile()668 char *PASCAL NEAR getnfile()
669
670 {
671 register int index; /* index into various strings */
672 register int point; /* index into other strings */
673 register int extflag; /* does the file have an extention? */
674 char fname[NFILEN]; /* file/path for DOS call */
675
676 /* and call for the first file */
677 if (findnext(&fileblock) == -1)
678 return(NULL);
679
680 /* return the first file name! */
681 strcpy(rbuf, path);
682 strcat(rbuf, fileblock.ff_name);
683 mklower(rbuf);
684 if (fileblock.ff_attrib == 16)
685 strcat(rbuf, DIRSEPSTR);
686 return(rbuf);
687 }
688 #else
689 #if MSC | ZTC
690 /* FILE Directory routines */
691
692 char path[NFILEN]; /* path of file to find */
693 char rbuf[NFILEN]; /* return file buffer */
694
695 /* do a wild card directory search (for file name completion) */
696
getffile(fspec)697 char *PASCAL NEAR getffile(fspec)
698
699 char *fspec; /* pattern to match */
700
701 {
702 register int index; /* index into various strings */
703 register int point; /* index into other strings */
704 register int extflag; /* does the file have an extention? */
705 char fname[NFILEN]; /* file/path for DOS call */
706
707 /* first parse the file path off the file spec */
708 strcpy(path, fspec);
709 index = strlen(path) - 1;
710 while (index >= 0 && (path[index] != '/' &&
711 path[index] != '\\' && path[index] != ':'))
712 --index;
713 path[index+1] = 0;
714
715 /* check for an extension */
716 point = strlen(fspec) - 1;
717 extflag = FALSE;
718 while (point > index) {
719 if (fspec[point] == '.') {
720 extflag = TRUE;
721 break;
722 }
723 point--;
724 }
725
726 /* construct the composite wild card spec */
727 strcpy(fname, path);
728 strcat(fname, &fspec[index+1]);
729 strcat(fname, "*");
730 if (extflag == FALSE)
731 strcat(fname, ".*");
732
733 /* and call for the first file */
734 if (_dos_findfirst(fname, _A_NORMAL|_A_SUBDIR, &fileblock) != 0)
735 return(NULL);
736
737 /* return the first file name! */
738 strcpy(rbuf, path);
739 strcat(rbuf, fileblock.name);
740 mklower(rbuf);
741 if (fileblock.attrib == 16)
742 strcat(rbuf, DIRSEPSTR);
743 return(rbuf);
744 }
745
getnfile()746 char *PASCAL NEAR getnfile()
747
748 {
749 register int index; /* index into various strings */
750 register int point; /* index into other strings */
751 register int extflag; /* does the file have an extention? */
752 char fname[NFILEN]; /* file/path for DOS call */
753
754 /* and call for the first file */
755 if (_dos_findnext(&fileblock) != 0)
756 return(NULL);
757
758 /* return the first file name! */
759 strcpy(rbuf, path);
760 strcat(rbuf, fileblock.name);
761 mklower(rbuf);
762 if (fileblock.attrib == 16)
763 strcat(rbuf, DIRSEPSTR);
764 return(rbuf);
765 }
766 #else
getffile(fspec)767 char *PASCAL NEAR getffile(fspec)
768
769 char *fspec; /* file to match */
770
771 {
772 return(NULL);
773 }
774
getnfile()775 char *PASCAL NEAR getnfile()
776
777 {
778 return(NULL);
779 }
780 #endif
781 #endif
782 #endif
783