1 /*	MSDOS.C:	Operating specific I/O and Spawning functions
2 			under the MS/PCDOS operating 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	MSDOS
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 #if	MOUSE
109 	long miaddr;	/* mouse interupt routine address */
110 #endif
111 
112 	/* on all screens we are not sure of the initial position
113 	   of the cursor					*/
114 	ttrow = 999;
115 	ttcol = 999;
116 	strcpy(os, "MSDOS");
117 
118 #if	MOUSE
119 	/* check if the mouse drive exists first */
120 	rg.x.ax = 0x3533;	/* look at the interrupt 33 address */
121 #if	MSC | TURBO | IC | LATTICE | MWC
122 	int86x(0x21, &rg, &rg, &segreg);
123 	miaddr = (((long)segreg.es) << 16) + (long)rg.x.bx;
124 	if (miaddr == 0 || *(char * far)miaddr == 0xcf) {
125 #endif
126 #if	ZTC
127 	int86x(0x21, &rg, &rg, &segreg);
128 	miaddr = (((long)segreg.es) << 16) + (long)rg.x.bx;
129 	if (miaddr == 0 || *(char far *)miaddr == 0xcf) {
130 #endif
131 		mexist = FALSE;
132 		return(TRUE);
133 	}
134 
135 	/* and then check for the mouse itself */
136 	rg.x.ax = 0;		/* mouse status flag */
137 	int86(0x33, &rg, &rg);	/* check for the mouse interupt */
138 	mexist = (rg.x.ax != 0);
139 	nbuttons = rg.x.bx;
140 
141 	/* initialize our character input queue */
142 	in_init();
143 	if (mexist == FALSE)
144 		return(TRUE);
145 
146 	/* if the mouse exists.. get it in the upper right corner */
147 	rg.x.ax = 4;		/* set mouse cursor position */
148 	oldcol = (term.t_ncol - 1);
149 	rg.x.cx = oldcol << 3;	/* last col of display */
150 	oldrow = 0;
151 	rg.x.dx = oldrow;	/* top row */
152 	oldbut = 0;
153 	int86(0x33, &rg, &rg);
154 
155 	/* and set its attributes */
156 	rg.x.ax = 10;		/* set text cursor */
157 	rg.x.bx = 0;		/* software text cursor please */
158 	rg.x.cx = 0x77ff;	/* screen mask */
159 	rg.x.dx = 0x7700;	/* cursor mask */
160 	int86(0x33, &rg, &rg);
161 #else	/* !MOUSE */
162 	mexist = 0;
163 #endif	/* !MOUSE */
164 }
165 
166 maxlines(lines)		/* set number of vertical rows for mouse */
167 
168 int lines;	/* # of vertical lines */
169 
170 {
171 #if	MOUSE
172 	if (mexist) {
173 		rg.x.ax = 8;		/* set min/max vertical cursor position */
174 		rg.x.cx = 0;		/* start at 0 */
175 		rg.x.dx = (lines - 1)<<3; /* end at the end */
176 		int86(0x33, &rg, &rg);
177 	}
178 #endif
179 }
180 
181 /*
182  * This function gets called just before we go back home to the command
183  * interpreter. On VMS it puts the terminal back in a reasonable state.
184  * Another no-operation on CPM.
185  */
186 int PASCAL NEAR ttclose()
187 {
188 	/* nothing here! */
189 }
190 
191 /*
192  * Write a character to the display. On VMS, terminal output is buffered, and
193  * we just put the characters in the big array, after checking for overflow.
194  * On CPM terminal I/O unbuffered, so we just write the byte out. Ditto on
195  * MS-DOS (use the very very raw console output routine).
196  */
197 
198 int PASCAL NEAR ttputc(c)
199 
200 int c;
201 
202 {
203 #if     MWC
204         putcnb(c);
205 #endif
206 
207 #if	(LATTICE | TURBO | IC | MSC | ZTC) & ~IBMPC
208 	bdos(6, c, 0);
209 #endif
210 }
211 
212 /*
213  * Flush terminal buffer. Does real work where the terminal output is buffered
214  * up. A no-operation on systems where byte at a time terminal I/O is done.
215  */
216 int PASCAL NEAR ttflush()
217 {
218 }
219 
220 int doschar()	/* call the dos to get a char */
221 
222 {
223 #if ATKBD	/* AT-style extended keyboard with grey keys */
224 
225 	rg.h.ah = 0x10;			/* Get Keyboard Input call */
226 	int86(0x16, &rg, &rg);
227 
228 	/* function key!! */
229 	if (rg.h.al == 0 || rg.h.al == 0xE0) {
230 
231 		rg.h.cl = rg.h.ah;	/* swap scan code and value */
232 		rg.h.ch = rg.h.al;
233 		rg.x.cx = extcode(rg.x.cx);
234 		in_put(rg.h.ch & 255);	/* prefix byte */
235 		in_put(rg.h.cl & 255);	/* event code byte */
236 		return(0);		/* extended escape sequence */
237 	}
238 	return(rg.h.al & 255);
239 #else
240 
241 #if	(TURBO | IC) && HP150 == 0
242 /*	Added 8/13/89 by DRK to get Turbo C version to use BIOS for
243 	keyboard input. Low byte of k has first byte of extended scan
244 	(ASCII value for reg. char, 0 for extended char), high byte has
245 	extended key scan code.
246 */
247 
248 	unsigned k = (unsigned)bioskey(0);
249 	unsigned c;		/* Extended character to return. */
250 
251 	if ((k & 0xFF) == 0) {	/* Check for extended key. */
252 				/* Convert extended key scan code to a
253 				   uEMACS internal form. */
254 		c = extcode(k >> 8);
255 		in_put(c >> 8);	/* Report prefix and event code bytes. */
256 		in_put(c & 0xFF);
257 		return(0);	/* Return extended escape sequence. */
258 	}
259 	return (k & 0xFF);	/* Return regular ASCII value. */
260 
261 #else
262 	register unsigned int c;	/* extended character to return */
263 
264 	rg.h.ah = 7;		/* dos Direct Console Input call */
265 	intdos(&rg, &rg);
266 #if	HP150 == 0		/* this translation level is deeper on the HP150 */
267 	if (rg.h.al == 0) {	/* function key!! */
268 		rg.h.ah = 7;	/* get the next character */
269 		intdos(&rg, &rg);
270 		c = extcode(rg.h.al);
271 		in_put(c >> 8);		/* prefix byte */
272 		in_put(c & 255);	/* event code byte */
273 		return(0);		/* extended escape sequence */
274 	}
275 #endif
276 	return(rg.h.al & 255);
277 #endif
278 #endif
279 }
280 
281 /*
282  * Read a character from the terminal, performing no editing and doing no echo
283  * at all. Also mouse events are forced into the input stream here.
284  */
285 int PASCAL NEAR ttgetc()
286 
287 {
288 	register int c;		/* character read */
289 
290 ttc:	/* return any keystrokes waiting in the
291 	   type ahead buffer */
292 	if (in_check())
293 		return(in_get());
294 
295 #if	TYPEAH
296 	if (typahead())
297 		return(doschar());
298 
299 	/* with no mouse, this is a simple get char routine */
300 	if (mexist == FALSE || mouseflag == FALSE)
301 		return(doschar());
302 
303 #if	MOUSE
304 	/* turn the mouse cursor on */
305 	rg.x.ax = 1;	/* Show Cursor */
306 	int86(0x33, &rg, &rg);
307 
308 	/* loop waiting for something to happen */
309 	while (TRUE) {
310 		if (typahead())
311 			break;
312 		if (checkmouse())
313 			break;
314 	}
315 
316 	/* turn the mouse cursor back off */
317 	rg.x.ax = 2;	/* Hide Cursor */
318 	int86(0x33, &rg, &rg);
319 
320 	goto ttc;
321 #endif	/* MOUSE */
322 #else	/* TYPEAH */
323 	return(doschar());
324 #endif	/* TYPEAH */
325 }
326 
327 #if	MOUSE
328 checkmouse()
329 
330 {
331 	register int k;		/* current bit/button of mouse */
332 	register int etype;	/* event type byte */
333 	register int event;	/* encoded mouse event */
334 	int mousecol;		/* current mouse column */
335 	int mouserow;		/* current mouse row */
336 	int sstate;		/* current shift key status */
337 	int newbut;		/* new state of the mouse buttons */
338 
339 	/* check to see if any mouse buttons are different */
340 	rg.x.ax = 3;	/* Get button status and mouse position */
341 	int86(0x33, &rg, &rg);
342 	newbut = rg.x.bx;
343 	if (strcmp(sres, "CGA40") == 0)
344 		mousecol = rg.x.cx >> 4;
345 	else /* if (term.t_ncol == 80) */
346 		mousecol = rg.x.cx >> 3;
347 	mouserow = (rg.x.dx >> 3);
348 
349 	/* only notice changes */
350 	if ((oldbut == newbut) && (mousecol == oldcol)
351 	    && (mouserow == oldrow))
352 		return(FALSE);
353 
354 	/* get the shift key status as well */
355 	etype = MOUS >> 8;
356 	sstate = 0;
357 	rg.h.ah = 2;	/* return current shift status */
358 	int86(0x16, &rg, &rg);
359 	sstate = rg.h.al;
360 	if (sstate & 3)		/* shifted? */
361 		etype |= (SHFT >> 8);
362 	if (sstate & 4)		/* controled? */
363 		etype |= (CTRL >> 8);
364 
365 	/* no buttons changes */
366 	if (oldbut == newbut) {
367 
368 		/* generate a mouse movement */
369 		if (((mouse_move == 1) && (mmove_flag == TRUE)) ||
370 		    (mouse_move == 2)) {
371 			in_put(0);
372 			in_put(etype);
373 			in_put(mousecol);
374 			in_put(mouserow);
375 			in_put('m');
376 		}
377 		oldcol = mousecol;
378 		oldrow = mouserow;
379 		return(TRUE);
380 	}
381 
382 	/* only on screen presses are legit! */
383 	if (mousecol < 0)
384 		mousecol = 0;
385 	if (mouserow < 0)
386 		mouserow = 0;
387 
388 	for (k=1; k != (1 << nbuttons); k = k<<1) {
389 		/* For each button on the mouse */
390 		if ((oldbut&k) != (newbut&k)) {
391 			/* This button changed, generate an event */
392 			in_put(0);
393 			in_put(etype);
394 			in_put(mousecol);
395 			in_put(mouserow);
396 
397 			event = ((newbut&k) ? 0 : 1);	/* up or down? */
398 			if (k == 2)			/* center button? */
399 				event += 4;
400 			if (k == 4)			/* right button? */
401 				event += 2;
402 			event += 'a';		/* plain */
403 			in_put(event);
404 			oldbut = newbut;
405 			oldcol = mousecol;
406 			oldrow = mouserow;
407 			return(TRUE);
408 		}
409 	}
410 	return(FALSE);
411 }
412 #endif
413 
414 #if	TYPEAH
415 /* typahead:	Check to see if any characters are already in the
416 		keyboard buffer
417 */
418 
419 int PASCAL NEAR typahead()
420 
421 {
422 	int flags;	/* cpu flags from dos call */
423 
424 #if	(TURBO | IC ) && HP150 == 0 && ATKBD == 0
425 	if (bioskey(1) == 0)
426 		return FALSE;
427 	else
428 		return TRUE;
429 #else
430 	rg.x.ax = 0x4406;	/* IOCTL input status */
431 	rg.x.bx = 0;		/* File handle = stdin */
432 #if	MSC
433 	int86(0x21,&rg,&rg);
434 	flags = rg.h.al;
435 #else
436 #if	LATTICE | TURBO | IC | ZTC
437 	flags = intdos(&rg, &rg);
438 #else
439 	intcall(&rg, &rg, 0x21);
440 	flags = rg.x.flags;
441 #endif
442 #endif
443 	if (flags & 1)		/* AL = 0xFF if ready */
444 		return(TRUE);
445 	else
446 		return(FALSE);
447 #endif
448 }
449 #endif
450 
451 /*
452  * Create a subjob with a copy of the command intrepreter in it. When the
453  * command interpreter exits, mark the screen as garbage so that you do a full
454  * repaint. Bound to "^X C".
455  */
456 
457 int PASCAL NEAR spawncli(f, n)
458 
459 int f, n;
460 
461 {
462 	/* don't allow this command if restricted */
463 	if (restflag)
464 		return(resterr());
465 
466         movecursor(term.t_nrow, 0);             /* Seek to last line.   */
467         TTflush();
468 	TTkclose();
469 	shellprog("");
470 #if	WINDOW_TEXT
471 	refresh_screen(first_screen);
472 #endif
473 	TTkopen();
474         sgarbf = TRUE;
475         return(TRUE);
476 }
477 
478 /*
479  * Run a one-liner in a subjob. When the command returns, wait for a single
480  * character to be typed, then mark the screen as garbage so a full repaint is
481  * done. Bound to "C-X !".
482  */
483 int PASCAL NEAR spawn(f, n)
484 
485 int f, n;
486 
487 {
488         register int s;
489         char line[NLINE];
490 
491 	/* don't allow this command if restricted */
492 	if (restflag)
493 		return(resterr());
494 
495         if ((s=mlreply("!", line, NLINE)) != TRUE)
496                 return(s);
497 	movecursor(term.t_nrow - 1, 0);
498 	TTkclose();
499         shellprog(line);
500 	TTkopen();
501 
502 	/* if we are interactive, pause here */
503 	if (clexec == FALSE) {
504 #if	XVT == 0
505 		printf(TEXT227);
506 /*			"\n--- Press any key to Continue ---" */
507 #endif
508 		tgetc();
509         }
510 #if	WINDOW_TEXT
511 	refresh_screen(first_screen);
512 #endif
513         sgarbf = TRUE;
514         return (TRUE);
515 }
516 
517 /*
518  * Run an external program with arguments. When it returns, wait for a single
519  * character to be typed, then mark the screen as garbage so a full repaint is
520  * done. Bound to "C-X $".
521  */
522 
523 int PASCAL NEAR execprg(f, n)
524 
525 {
526         register int s;
527         char line[NLINE];
528 
529 	/* don't allow this command if restricted */
530 	if (restflag)
531 		return(resterr());
532 
533         if ((s=mlreply("$", line, NLINE)) != TRUE)
534                 return(s);
535 	movecursor(term.t_nrow - 1, 0);
536 	TTkclose();
537         execprog(line);
538 #if	WINDOW_TEXT
539 	refresh_screen(first_screen);
540 #endif
541 	TTkopen();
542 	/* if we are interactive, pause here */
543 	if (clexec == FALSE) {
544 	        mlputs(TEXT6);
545 /*                     "\r\n\n[End]" */
546         	tgetc();
547         }
548         sgarbf = TRUE;
549         return (TRUE);
550 }
551 
552 /*
553  * Pipe a one line command into a window
554  * Bound to ^X @
555  */
556 int PASCAL NEAR pipecmd(f, n)
557 
558 int f, n;
559 
560 {
561 	register EWINDOW *wp;	/* pointer to new window */
562 	register BUFFER *bp;	/* pointer to buffer to zot */
563 	register char *tmp;	/* ptr to TMP DOS environment variable */
564 	FILE *fp;
565         char line[NLINE];	/* command line send to shell */
566 	static char bname[] = "command";
567 	static char filnam[NSTRING] = "command";
568 
569 	/* don't allow this command if restricted */
570 	if (restflag)
571 		return(resterr());
572 
573 	if ((tmp = getenv("TMP")) == NULL)
574 		filnam[0] = 0;
575 	else {
576 		strcpy(filnam, tmp);
577 		if (filnam[strlen(filnam) - 1] != '\\')
578 			strcat(filnam, "\\");
579         }
580 	strcat(filnam,"command");
581 
582 	/* get the command to pipe in */
583         if (mlreply("@", line, NLINE) != TRUE)
584                 return(FALSE);
585 
586 	/* get rid of the command output buffer if it exists */
587         if ((bp=bfind(bname, FALSE, 0)) != FALSE) {
588 		/* try to make sure we are off screen */
589 		wp = wheadp;
590 		while (wp != NULL) {
591 			if (wp->w_bufp == bp) {
592 				onlywind(FALSE, 1);
593 				break;
594 			}
595 			wp = wp->w_wndp;
596 		}
597 		/* get rid of the existing command buffer */
598 		if (zotbuf(bp) != TRUE)
599 			return(FALSE);
600 	}
601 
602 	/* redirect the command output to the output file */
603 	strcat(line, " >>");
604 	strcat(line, filnam);
605 	movecursor(term.t_nrow - 1, 0);
606 
607 	/* execute the command */
608 	TTkclose();
609         shellprog(line);
610 #if	WINDOW_TEXT
611 	refresh_screen(first_screen);
612 #endif
613 	TTkopen();
614         sgarbf = TRUE;
615 
616         /* did the output file get generated? */
617 	if ((fp = fopen(filnam, "r")) == NULL)
618 		return(FALSE);
619 	fclose(fp);
620 
621 	/* split the current window to make room for the command output */
622 	if (splitwind(FALSE, 1) == FALSE)
623 			return(FALSE);
624 
625 	/* and read the stuff in */
626 	if (getfile(filnam, FALSE) == FALSE)
627 		return(FALSE);
628 
629 	/* make this window in VIEW mode, update all mode lines */
630 	curwp->w_bufp->b_mode |= MDVIEW;
631 	wp = wheadp;
632 	while (wp != NULL) {
633 		wp->w_flag |= WFMODE;
634 		wp = wp->w_wndp;
635 	}
636 
637 	/* and get rid of the temporary file */
638 	unlink(filnam);
639 	return(TRUE);
640 }
641 
642 /*
643  * filter a buffer through an external DOS program
644  * Bound to ^X #
645  */
646 int PASCAL NEAR filter(f, n)
647 
648 int f, n;
649 
650 {
651         register int    s;	/* return status from CLI */
652 	register BUFFER *bp;	/* pointer to buffer to zot */
653         char line[NLINE];	/* command line send to shell */
654 	char tmpnam[NFILEN];	/* place to store real file name */
655 	static char bname1[] = "fltinp";
656 
657 	static char filnam1[] = "fltinp";
658 	static char filnam2[] = "fltout";
659 
660 	/* don't allow this command if restricted */
661 	if (restflag)
662 		return(resterr());
663 
664 	if (curbp->b_mode&MDVIEW)	/* don't allow this command if	*/
665 		return(rdonly());	/* we are in read only mode	*/
666 
667 	/* get the filter name and its args */
668         if ((s=mlreply("#", line, NLINE)) != TRUE)
669                 return(s);
670 
671 	/* setup the proper file names */
672 	bp = curbp;
673 	strcpy(tmpnam, bp->b_fname);	/* save the original name */
674 	strcpy(bp->b_fname, bname1);	/* set it to our new one */
675 
676 	/* write it out, checking for errors */
677 	if (writeout(filnam1, "w") != TRUE) {
678 		mlwrite(TEXT2);
679 /*                      "[Cannot write filter file]" */
680 		strcpy(bp->b_fname, tmpnam);
681 		return(FALSE);
682 	}
683 
684 	strcat(line," <fltinp >fltout");
685 	movecursor(term.t_nrow - 1, 0);
686 	TTkclose();
687         shellprog(line);
688 #if	WINDOW_TEXT
689 	refresh_screen(first_screen);
690 #endif
691 	TTkopen();
692         sgarbf = TRUE;
693 	s = TRUE;
694 
695 	/* on failure, escape gracefully */
696 	if (s != TRUE || (readin(filnam2,FALSE) == FALSE)) {
697 		mlwrite(TEXT3);
698 /*                      "[Execution failed]" */
699 		strcpy(bp->b_fname, tmpnam);
700 		unlink(filnam1);
701 		unlink(filnam2);
702 		return(s);
703 	}
704 
705 	/* reset file name */
706 	strcpy(bp->b_fname, tmpnam);	/* restore name */
707 	bp->b_flag |= BFCHG;		/* flag it as changed */
708 
709 	/* and get rid of the temporary file */
710 	unlink(filnam1);
711 	unlink(filnam2);
712 	return(TRUE);
713 }
714 
715 #if	LATTICE
716 extern int _oserr;
717 #endif
718 
719 #if	MWC
720 extern int errno;
721 #endif
722 
723 #if	MSC
724 extern int _doserrno;
725 #endif
726 
727 /*	SHELLPROG: Execute a command in a subshell		*/
728 
729 int PASCAL NEAR shellprog(cmd)
730 
731 char *cmd;	/*  Incoming command line to execute  */
732 
733 {
734 	char *shell;		/* Name of system command processor */
735 	char swchar;		/* switch character to use */
736 	union REGS regs;	/* parameters for dos call */
737 	char comline[NSTRING];	/* constructed command line */
738 
739 	/*  detect current switch character and set us up to use it */
740 	regs.h.ah = 0x37;	/*  get setting data  */
741 	regs.h.al = 0x00;	/*  get switch character  */
742 	intdos(&regs, &regs);
743 	swchar = (char)regs.h.dl;
744 
745 	/*  get name of system shell  */
746 	if ((shell = getenv("COMSPEC")) == NULL) {
747 		return(FALSE);		/*  No shell located  */
748 	}
749 
750 	/* trim leading whitespace off the command */
751 	while (*cmd == ' ' || *cmd == '\t')	/*  find out if null command */
752 		cmd++;
753 
754 	/**  If the command line is not empty, bring up the shell  **/
755 	/**  and execute the command.  Otherwise, bring up the     **/
756 	/**  shell in interactive mode.   **/
757 
758 	if (*cmd) {
759 		strcpy(comline, shell);
760 		strcat(comline, " ");
761 		comline[strlen(comline) + 1] = 0;
762 		comline[strlen(comline)] = swchar;
763 		strcat(comline, "c ");
764 		strcat(comline, cmd);
765 		return(execprog(comline));
766 	} else
767 		return(execprog(shell));
768 }
769 
770 /*	EXECPROG:	A function to execute a named program
771 			with arguments
772 */
773 
774 #if	LATTICE | MWC
775 #define	CFLAG	1
776 #endif
777 
778 int PASCAL NEAR execprog(cmd)
779 
780 char *cmd;	/*  Incoming command line to execute  */
781 
782 {
783 	char *sp;		/* temporary string pointer */
784 	int rv;			/* numeric return value from subprocess */
785 	char f1[38];		/* FCB1 area (not initialized */
786 	char f2[38];		/* FCB2 area (not initialized */
787 	char prog[NSTRING];	/* program filespec */
788 	char tail[NSTRING];	/* command tail with length byte */
789 	union REGS regs;	/* parameters for dos call  */
790 #if	MWC == 0
791 	struct SREGS segreg;	/* segment registers for dis call */
792 #endif
793 	struct Pblock {		/* EXEC parameter block */
794 		short envptr;	/* 2 byte pointer to environment string */
795 		char *cline;	/* 4 byte pointer to command line */
796 		char *fcb1;	/* 4 byte pointer to FCB at PSP+5Ch */
797 		char *fcb2;	/* 4 byte pointer to FCB at PSP+6Ch */
798 	} pblock;
799 
800 	/* parse the command name from the command line */
801 	sp = prog;
802 	while (*cmd && (*cmd != ' ') && (*cmd != '\t'))
803 		*sp++ = *cmd++;
804 	*sp = 0;
805 
806 	/* and parse out the command tail */
807 	while (*cmd && ((*cmd == ' ') || (*cmd == '\t')))
808 		++cmd;
809 	*tail = (char)(strlen(cmd)); /* record the byte length */
810 	strcpy(&tail[1], cmd);
811 	strcat(&tail[1], "\r");
812 
813 	/* look up the program on the path trying various extentions */
814 	if ((sp = flook(prog, TRUE)) == NULL)
815 		if ((sp = flook(strcat(prog, ".exe"), TRUE)) == NULL) {
816 			strcpy(&prog[strlen(prog)-4], ".com");
817 			if ((sp = flook(prog, TRUE)) == NULL)
818 				return(FALSE);
819 		}
820 	strcpy(prog, sp);
821 
822 #if	MWC == 0
823 	/* get a pointer to this PSPs environment segment number */
824 #if	IC
825 	_segread(&segreg);
826 #else /* IC */
827 	segread(&segreg);
828 #endif /* IC */
829 #endif /* MWC == 0 */
830 
831 	/* set up the EXEC parameter block */
832 	pblock.envptr = 0;	/* make the child inherit the parents env */
833 	pblock.fcb1 = f1;		/* point to a blank FCB */
834 	pblock.fcb2 = f2;		/* point to a blank FCB */
835         pblock.cline = tail;		/* parameter line pointer */
836 
837 	/* and make the call */
838 	regs.h.ah = 0x4b;	/* EXEC Load or Execute a Program */
839 	regs.h.al = 0x00;	/* load end execute function subcode */
840 #if	MWC
841 	regs.x.ds = ((unsigned long)(prog) >> 16);	/* program name ptr */
842 	regs.x.dx = (unsigned int)(prog);
843 	regs.x.es = regs.x.ds;
844 	/*regs.x.es = ((unsigned long)(&pblock) >> 16);	* set up param block ptr */
845 	regs.x.bx = (unsigned int)(&pblock);
846 #endif
847 #if	LATTICE | MSC | TURBO | IC | ZTC
848 	segreg.ds = ((unsigned long)(prog) >> 16);	/* program name ptr */
849 	regs.x.dx = (unsigned int)(prog);
850 	segreg.es = ((unsigned long)(&pblock) >> 16);	/* set up param block ptr */
851 	regs.x.bx = (unsigned int)(&pblock);
852 #endif
853 #if	LATTICE
854 	if ((intdosx(&regs, &regs, &segreg) & CFLAG) == 0) {
855 		regs.h.ah = 0x4d;	/* get child process return code */
856 		intdos(&regs, &regs);	/* go do it */
857 		rv = regs.x.ax;		/* save child's return code */
858 	} else
859 		rv = -_oserr;		/* failed child call */
860 #endif
861 #if	MWC
862 	intcall(&regs, &regs, DOSINT);
863 	if ((regs.x.flags & CFLAG) == 0) {
864 		regs.h.ah = 0x4d;	/* get child process return code */
865 		intcall(&regs, &regs, DOSINT);	/* go do it */
866 		rv = regs.x.ax;		/* save child's return code */
867 	} else
868 		rv = -errno;		/* failed child call */
869 #endif
870 #if	TURBO | IC | MSC | ZTC
871 	intdosx(&regs, &regs, &segreg);
872 	if (regs.x.cflag == 0) {
873 		regs.h.ah = 0x4d;	/* get child process return code */
874 		intdos(&regs, &regs);	/* go do it */
875 		rv = regs.x.ax;		/* save child's return code */
876 	} else
877 #if	IC
878 		rv = -1;
879 #else /* IC */
880 		rv = -_doserrno;	/* failed child call */
881 #endif /* IC */
882 #endif
883 	strcpy(rval, int_asc(rv));
884 	return((rval < 0) ? FALSE : TRUE);
885 }
886 
887 /* return a system dependant string with the current time */
888 
889 char *PASCAL NEAR timeset()
890 
891 {
892 #if	MWC | TURBO | IC | MSC | ZTC
893 	register char *sp;	/* temp string pointer */
894 	char buf[16];		/* time data buffer */
895 	extern char *ctime();
896 
897 #if	IC
898 	time((time_t *)buf);
899 	sp = ctime((time_t *)buf);
900 #else
901 	time(buf);
902 	sp = ctime(buf);
903 #endif
904 	sp[strlen(sp)-1] = 0;
905 	return(sp);
906 #else
907 	return(errorm);
908 #endif
909 }
910 
911 #if	TURBO
912 /*	FILE Directory routines		*/
913 
914 char path[NFILEN];	/* path of file to find */
915 char rbuf[NFILEN];	/* return file buffer */
916 
917 /*	do a wild card directory search (for file name completion) */
918 
919 char *PASCAL NEAR getffile(fspec)
920 
921 char *fspec;	/* pattern to match */
922 
923 {
924 	register int index;		/* index into various strings */
925 	register int point;		/* index into other strings */
926 	register int extflag;		/* does the file have an extention? */
927 	char fname[NFILEN];		/* file/path for DOS call */
928 
929 	/* first parse the file path off the file spec */
930 	strcpy(path, fspec);
931 	index = strlen(path) - 1;
932 	while (index >= 0 && (path[index] != '/' &&
933 				path[index] != '\\' && path[index] != ':'))
934 		--index;
935 	path[index+1] = 0;
936 
937 	/* check for an extension */
938 	point = strlen(fspec) - 1;
939 	extflag = FALSE;
940 	while (point > index) {
941 		if (fspec[point] == '.') {
942 			extflag = TRUE;
943 			break;
944 		}
945 		point--;
946 	}
947 
948 	/* construct the composite wild card spec */
949 	strcpy(fname, path);
950 	strcat(fname, &fspec[index+1]);
951 	strcat(fname, "*");
952 	if (extflag == FALSE)
953 		strcat(fname, ".*");
954 
955 	/* and call for the first file */
956 	if (findfirst(fname, &fileblock, FA_DIREC) == -1)
957 		return(NULL);
958 
959 	/* return the first file name! */
960 	strcpy(rbuf, path);
961 	strcat(rbuf, fileblock.ff_name);
962 	mklower(rbuf);
963 	if (fileblock.ff_attrib == 16)
964 		strcat(rbuf, DIRSEPSTR);
965 	return(rbuf);
966 }
967 
968 char *PASCAL NEAR getnfile()
969 
970 {
971 	register int index;		/* index into various strings */
972 	register int point;		/* index into other strings */
973 	register int extflag;		/* does the file have an extention? */
974 	char fname[NFILEN];		/* file/path for DOS call */
975 
976 	/* and call for the first file */
977 	if (findnext(&fileblock) == -1)
978 		return(NULL);
979 
980 	/* return the first file name! */
981 	strcpy(rbuf, path);
982 	strcat(rbuf, fileblock.ff_name);
983 	mklower(rbuf);
984 	if (fileblock.ff_attrib == 16)
985 		strcat(rbuf, DIRSEPSTR);
986 	return(rbuf);
987 }
988 #else
989 #if	MSC | ZTC
990 /*	FILE Directory routines		*/
991 
992 char path[NFILEN];	/* path of file to find */
993 char rbuf[NFILEN];	/* return file buffer */
994 
995 /*	do a wild card directory search (for file name completion) */
996 
997 char *PASCAL NEAR getffile(fspec)
998 
999 char *fspec;	/* pattern to match */
1000 
1001 {
1002 	register int index;		/* index into various strings */
1003 	register int point;		/* index into other strings */
1004 	register int extflag;		/* does the file have an extention? */
1005 	char fname[NFILEN];		/* file/path for DOS call */
1006 
1007 	/* first parse the file path off the file spec */
1008 	strcpy(path, fspec);
1009 	index = strlen(path) - 1;
1010 	while (index >= 0 && (path[index] != '/' &&
1011 				path[index] != '\\' && path[index] != ':'))
1012 		--index;
1013 	path[index+1] = 0;
1014 
1015 	/* check for an extension */
1016 	point = strlen(fspec) - 1;
1017 	extflag = FALSE;
1018 	while (point > index) {
1019 		if (fspec[point] == '.') {
1020 			extflag = TRUE;
1021 			break;
1022 		}
1023 		point--;
1024 	}
1025 
1026 	/* construct the composite wild card spec */
1027 	strcpy(fname, path);
1028 	strcat(fname, &fspec[index+1]);
1029 	strcat(fname, "*");
1030 	if (extflag == FALSE)
1031 		strcat(fname, ".*");
1032 
1033 	/* and call for the first file */
1034 	if (_dos_findfirst(fname, _A_NORMAL|_A_SUBDIR, &fileblock) != 0)
1035 		return(NULL);
1036 
1037 	/* return the first file name! */
1038 	strcpy(rbuf, path);
1039 	strcat(rbuf, fileblock.name);
1040 	mklower(rbuf);
1041 	if (fileblock.attrib == 16)
1042 		strcat(rbuf, DIRSEPSTR);
1043 	return(rbuf);
1044 }
1045 
1046 char *PASCAL NEAR getnfile()
1047 
1048 {
1049 	register int index;		/* index into various strings */
1050 	register int point;		/* index into other strings */
1051 	register int extflag;		/* does the file have an extention? */
1052 	char fname[NFILEN];		/* file/path for DOS call */
1053 
1054 	/* and call for the first file */
1055 	if (_dos_findnext(&fileblock) != 0)
1056 		return(NULL);
1057 
1058 	/* return the first file name! */
1059 	strcpy(rbuf, path);
1060 	strcat(rbuf, fileblock.name);
1061 	mklower(rbuf);
1062 	if (fileblock.attrib == 16)
1063 		strcat(rbuf, DIRSEPSTR);
1064 	return(rbuf);
1065 }
1066 #else
1067 char *PASCAL NEAR getffile(fspec)
1068 
1069 char *fspec;	/* file to match */
1070 
1071 {
1072 	return(NULL);
1073 }
1074 
1075 char *PASCAL NEAR getnfile()
1076 
1077 {
1078 	return(NULL);
1079 }
1080 #endif
1081 #endif
1082 #endif
1083