1 /*
2  * SimRsim.c -
3  *
4  *	This file provides routines for Magic to communicate with Rsim/Irsim.
5  *	Communications takes place using two pipes, one for Magic to
6  *	send a command to the simulator, the other to get back the reply.
7  *
8  *     *********************************************************************
9  *     * Copyright (C) 1985, 1990 Regents of the University of California. *
10  *     * Permission to use, copy, modify, and distribute this              *
11  *     * software and its documentation for any purpose and without        *
12  *     * fee is hereby granted, provided that the above copyright          *
13  *     * notice appear in all copies.  The University of California        *
14  *     * makes no representations about the suitability of this            *
15  *     * software for any purpose.  It is provided "as is" without         *
16  *     * express or implied warranty.  Export of this software outside     *
17  *     * of the United States of America may require an export license.    *
18  *     *********************************************************************
19  * of California.  All rights reserved.
20  *
21  */
22 
23 #ifdef RSIM_MODULE
24 
25 #include <stdio.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include <signal.h>
29 #include <unistd.h>
30 
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/wait.h>
34 #include <sys/file.h>
35 
36 #include "utils/magic.h"
37 #include "utils/stack.h"
38 #include "utils/geometry.h"
39 #include "utils/utils.h"
40 #include "tiles/tile.h"
41 #include "utils/hash.h"
42 #include "database/database.h"
43 #include "utils/signals.h"
44 #include "utils/styles.h"
45 #include "windows/windows.h"
46 #include "dbwind/dbwind.h"
47 #include "sim/sim.h"
48 #include <errno.h>
49 
50 static bool InitRsim();
51 
52 #define BUF_SIZE	1024
53 #define	LINEBUF_SIZE	256
54 #define	READBUF_SIZE	4096 + LINEBUF_SIZE
55 
56 #define E_RUNNING	"Simulator already running.\n"
57 #define E_PIPE1OP	"Could not create Magic to Rsim pipe.\n"
58 #define E_PIPE2OP	"Could not create Rsim to Magic pipe.\n"
59 #define E_NOFORK	"Could not fork process.\n"
60 #define E_NOSTART	"Rsim not started.\n"
61 #define E_PIPERD	"Error reading pipe from Rsim.\n"
62 #define E_PIPEWR	"Could not write on pipe to Rsim.\n"
63 
64 #define P_READ		0
65 #define P_WRITE		1
66 
67 static int 	status;
68 static int	pipeIn;					/* Rsim --> Magic */
69 static int	pipeOut;	 			/* Magic --> Rsim */
70 static char 	keyBoardBuf[BUF_SIZE];
71 static bool	RsimJustStarted = TRUE;
72 static char	rsim_prompt[20];
73 static int	prompt_len;
74 static int      rsim_pid;
75 
76 bool	SimRsimRunning = FALSE;
77 bool	SimHasCoords = FALSE;
78 bool	SimGetReplyLine();
79 
80 /* Forward declaration */
81 void SimStopRsim();
82 
83 /*
84  *-----------------------------------------------------------------------
85  * SimGetNodeCommand
86  *
87  *	This function returns the "full" name of the rsim command if 'cmd'
88  *	should be applied to the selected node(s) or NULL if the command
89  *	should be shipped without any node names.
90  *
91  * Results:
92  *	A ptr to the command name or NULL.
93  *
94  * Side effects:
95  *	None.
96  *-----------------------------------------------------------------------
97  */
98 
99 char *
SimGetNodeCommand(cmd)100 SimGetNodeCommand(cmd)
101     char *cmd;
102 {
103     /* This table is used to define which Rsim commands are applied to
104      * each node in the selection.  Depending on the command, you
105      * woudn't want to send a command to rsim for each node.  For example
106      * given the "s" command (to step the clock), you wouldn't want to
107      * step the clock once for every node in the selection.
108      */
109 
110     static char *RsimNodeCommands[] =
111       {
112 	"?",
113 	"!",
114 	"analyzer",
115 	"d",
116 	"h",
117 	"l",
118 	"path",
119 	"t",
120 	"u",
121 	"w",
122 	"x",
123 	NULL
124       };
125     int  cmdNum;
126 
127     cmdNum = Lookup( cmd, RsimNodeCommands );
128     cmd = (cmdNum >= 0) ? RsimNodeCommands[cmdNum] : (char *) NULL;
129 
130     return( cmd );
131 }
132 
133 
134 /*
135  *-----------------------------------------------------------------------
136  * SimStartRsim
137  *
138  *	This procedure is used to fork the rsim process.  It takes a list of
139  *	arguements to pass to rsim when initiating the fork.  The
140  * 	environment variable RSIM is first checked to find the pathname for
141  *	rsim/irsim.  If this variable does not exist, then BIN_DIR/irsim
142  *	is then used.
143  *
144  * Results:
145  *	TRUE if the fork was successful.
146  *
147  * Side effects:
148  *	None.
149  *-----------------------------------------------------------------------
150  */
151 
152 bool
SimStartRsim(argv)153 SimStartRsim(argv)
154     char *argv[];		/* list of rsim args for the fork */
155 {
156 
157     int child;
158     int magToRsimPipe[2];
159     int rsimToMagPipe[2];
160     char *getenv();
161     char rsimfile[256];
162     char *cad = BIN_DIR;
163     char *src, *dst;
164 
165     /* don't start another rsim if one is already running */
166 
167     if (SimRsimRunning) {
168 	TxPrintf(E_RUNNING);
169 	return(FALSE);
170     }
171 
172     /* Create the pipes.  One is for Magic sending to rsim, the other
173      * is for rsim sending to Magic.
174      */
175 
176     if (pipe(magToRsimPipe) < 0) {
177 	TxPrintf(E_PIPE1OP);
178 	return(FALSE);
179     }
180 
181     if (pipe(rsimToMagPipe) < 0) {
182 	TxPrintf(E_PIPE2OP);
183 	return(FALSE);
184     }
185 
186     /* Look for rsim; check for environ var first; if none, then
187      * try to open the one located in BIN_DIR.
188      */
189 
190     src = getenv("RSIM");
191     if( src != NULL )
192 	strcpy(rsimfile, src);
193     else {
194 	src = cad;
195 	dst = rsimfile;
196 	if (PaExpand(&src, &dst, 100) == -1) {
197 	    TxError ("Could not find " BIN_DIR "\n");
198 	    return(FALSE);
199 	}
200 	strcat(rsimfile, "/irsim");
201     }
202 #ifndef NO_ACCESS_CALL
203     if( access( rsimfile, 1 ) != 0 )
204     {
205 	TxPrintf("can not execute '%s'\n", rsimfile );
206 	return(FALSE);
207     }
208 #endif
209 
210 
211     FORK(child);
212 /*
213 #ifdef SYSV
214     child = fork();
215 #else
216     child = vfork();
217 #endif
218 */
219     if (child == -1) {
220 	close(magToRsimPipe[P_READ]);
221 	close(magToRsimPipe[P_WRITE]);
222 	close(rsimToMagPipe[P_READ]);
223 	close(rsimToMagPipe[P_WRITE]);
224 
225 	TxPrintf(E_NOFORK);
226 	return(FALSE);
227     }
228 
229     if (child > 0) {
230 
231 	/* This is the parent */
232 
233 	SimRsimRunning = TRUE;
234 	close(magToRsimPipe[P_READ]);
235 	close(rsimToMagPipe[P_WRITE]);
236 	pipeIn = rsimToMagPipe[P_READ];
237 	pipeOut = magToRsimPipe[P_WRITE];
238 	rsim_pid = child;
239     }
240     else {
241 
242 	int  i;
243 
244 	/* This is the child */
245 
246 	close(magToRsimPipe[P_WRITE]);
247 	close(rsimToMagPipe[P_READ]);
248 
249 	dup2(magToRsimPipe[P_READ], fileno(stdin));
250 	dup2(rsimToMagPipe[P_WRITE], fileno(stderr));
251 	dup2(rsimToMagPipe[P_WRITE], fileno(stdout));
252 
253 	for( i = 3; i < 15; i++ )
254 	    close( i );
255 
256 	/* try our best, folks..... */
257 
258 	execvp(rsimfile, argv);
259 	_exit(5);			/* pick a number, any number */
260 
261     }
262     return(FALSE);			/* to keep lint happy */
263 }
264 
265 
266 /*
267  *-----------------------------------------------------------------------
268  * SimConnectRsim
269  *
270  *	This procedure is called when Magic is sending commands to Rsim and
271  *	awaiting a reply.  The reply is retrieved by successive calls
272  *	to SimGetReplyLine() which returns one line of the reply at a time.
273  *
274  *	Magic sends a command to Rsim through one pipe, and
275  *	Rsim's reply comes back from the other pipe.
276  *
277  * Results:
278  *	None.
279  *
280  * Side effects:
281  *	The reply generated by Rsim is printed.
282  *	The reply buffer from SimGetReplyLine is statically allocated.
283  *	Subsequent calls will change the contents of this buffer.
284  *
285  *-----------------------------------------------------------------------
286  */
287 
288 void
SimConnectRsim(escRsim)289 SimConnectRsim(escRsim)
290     bool escRsim;			/* TRUE if we should escape back to Magic */
291 {
292     static char HELLO_MSG[] =
293 	"Type \"q\" to quit simulator or \".\" to escape back to Magic.\n";
294 
295     char *replyLine;		/* used to hold one line of the Rsim reply */
296 
297     if (!SimRsimRunning) {
298 	TxPrintf(E_NOSTART);
299 	return;
300     }
301 
302     /* read the header of the rsim reply and determine the prompt */
303 
304     if( RsimJustStarted ) {
305 	if( ! InitRsim( escRsim ? NULL : HELLO_MSG ) )
306 	    return;
307     }
308 
309     if (escRsim) {
310 	RsimJustStarted = FALSE;
311 	return;
312     }
313 
314     if (!RsimJustStarted) {
315 	TxPrintf("%s", HELLO_MSG);
316     }
317 
318 
319     while (TRUE) {
320 
321 	/* exceptions can toggle this flag. */
322 
323 	if (!SimRsimRunning) {
324 	    return;
325 	}
326 	TxPrintf("%s", rsim_prompt);
327 
328 	/* Read the user's command for Rsim */
329 
330 	if (TxGetLine(keyBoardBuf, BUF_SIZE) == 0) keyBoardBuf[0] = 0;
331 
332 	/* prepare the Rsim command string */
333 
334 	strcat(keyBoardBuf, "\n");
335 
336 	/* check to see if we quit Rsim or escape back to Magic */
337 
338 	if ((keyBoardBuf[0] == '.') && (keyBoardBuf[1] == '\n')) {
339 	    RsimJustStarted = FALSE;
340 	    return;
341 	}
342 	if ((keyBoardBuf[0] == 'q') && (keyBoardBuf[1] == '\n')) {
343 	    SimStopRsim();
344 	    return;
345 	}
346 
347 	/* Send the command to Rsim and get the reply. */
348 
349 	if (write(pipeOut, keyBoardBuf, strlen(keyBoardBuf)) < 0) {
350 	    TxPrintf(E_PIPEWR);
351 	    SimStopRsim();
352 	    return;
353 	}
354 	if (!SimGetReplyLine(&replyLine)) {
355 	    return;
356 	}
357 	while (replyLine != NULL) {
358 	    TxPrintf("%s\n",replyLine);
359 	    if (!SimGetReplyLine(&replyLine)) {
360 		return;
361 	    }
362 	}
363     }
364 }
365 
366 
367 /*
368  *-----------------------------------------------------------------------
369  * InitRsim
370  *	Read the initial header from rsim and determine the prompt.
371  *	The prompt is found by searching for the string enclosed
372  *	the last "\n" and "> ".
373  *
374  * Results:
375  *	Returns TRUE if rsim started correctly, else FALSE.
376  *
377  * Side effects:
378  *	Sets the rsim prompt and length.
379  *-----------------------------------------------------------------------
380  */
381 
382 bool
InitRsim(hello_msg)383 InitRsim(hello_msg)
384     char  *hello_msg;
385 {
386     char	buff[READBUF_SIZE];
387     char	*last;
388     int		nchars;
389     bool	first_time = TRUE;
390 
391     prompt_len = 0;
392     do
393     {
394 	nchars = 0;
395 	last = buff;
396 	if( SimFillBuffer( buff, &last, &nchars ) <= 0 )
397 	{
398 	    SimStopRsim();			/* rsim must have died */
399 	    TxPrintf( "<Simulator is dead>\n" );
400 	    return( FALSE );
401 	}
402 	buff[nchars] = '\0';
403 
404 	if( last[-1] == '>' && *last == ' ' )
405 	{
406 	    for( last--; last > buff && last[-1] != '\n'; last-- );
407 	    strcpy( rsim_prompt, last );
408 	    prompt_len = strlen( rsim_prompt );
409 	    *last = '\0';
410 	}
411 
412 	if( first_time )
413 	{
414 	    TxPrintf("Be sure your sim file matches the root cell of this window.\n");
415 	    if( hello_msg )
416 		TxPrintf( "%s", hello_msg );
417 	    first_time = FALSE;
418 	}
419 	if( *buff )
420 	    TxPrintf( "%s", buff );
421 
422     } while( prompt_len == 0 );
423 
424     if( write( pipeOut, "has_coords\n", 11 ) < 0 )
425     {
426 	TxPrintf(E_PIPEWR);
427 	SimStopRsim();
428 	return(FALSE);
429     }
430 
431     SimHasCoords = FALSE;
432     do
433     {
434 	if( ! SimGetReplyLine( &last ) )
435 	    return( FALSE );
436 
437 	if( last != NULL && strncmp( last, "YES", 3 ) == 0 )
438 	    SimHasCoords = TRUE;
439     }
440     while( last );
441 
442     return( TRUE );
443 }
444 
445 
446 /*
447  *-----------------------------------------------------------------------
448  * SimStopRsim
449  *
450  *	This procedure is called to kill off the Rsim process.  The
451  *	exit condition is checked and a message is printed if necessary.
452  *
453  * Results:
454  *	None.
455  *
456  * Side effects:
457  *	The child Rsim process is killed.
458  *
459  *-----------------------------------------------------------------------
460  */
461 
462 void
SimStopRsim()463 SimStopRsim()
464 {
465     int  pid;
466 
467     if (SimRsimRunning) {
468 
469 	/* closing the pipes to Rsim have the effect of killing it */
470 
471 	close(pipeOut);
472 	close(pipeIn);
473 
474 	/* set the Rsim state flags */
475 
476 	RsimJustStarted = TRUE;
477 	SimRsimRunning = FALSE;
478 
479 	kill(rsim_pid, SIGHUP);		/* just in case rsim hangs */
480 
481 	if (WaitPid (rsim_pid, &status) == -1)
482 	  return;
483 	pid = rsim_pid;
484 
485 	switch (status & 0xFF) {
486 	    case 0 :
487 		break;
488 	    case 2 :
489 		TxPrintf("Simulator interrupted.\n");
490 		break;
491 	    default :
492 		TxPrintf("Simulator terminated abnormally.\n");
493 		break;
494 	}
495     }
496 }
497 
498 
499 /*
500  *-----------------------------------------------------------------------
501  * RsimErrorMsg
502  *
503  *	This procedure prints out an error message.
504  *
505  * Results:
506  *	None.
507  *
508  * Side effects:
509  *	None.
510  *
511  *-----------------------------------------------------------------------
512  */
513 
514 void
RsimErrorMsg()515 RsimErrorMsg()
516 {
517     static char msg[] = "The simulator must be running before this command "
518 		"can be executed.  To do\n"
519 		"this enter the command \"rsim <options> <filename>\".  "
520 		"To escape back to\n"
521 		"Magic enter \".\" in response to the simulator prompt.\n";
522 
523     TxPrintf("%s", msg);
524 }
525 
526 
527 /*
528  *-----------------------------------------------------------------------
529  * SimRsimIt
530  *
531  *	This procedure takes an Rsim command and a node name to apply
532  *	the command to, constructs a complete Rsim command and sends it
533  *	to Rsim to process.
534  *
535  * Results:
536  *	None.
537  *
538  * Side effects:
539  *	Everything is set up so that GetReplyLine() should be called
540  *	after this routine to read the Rsim output for each node.
541  *	The reply buffer is statically allocated.  Subsequent calls will
542  *	change the contents of this buffer.
543  *
544  *-----------------------------------------------------------------------
545  */
546 
547 void
SimRsimIt(cmd,nodeName)548 SimRsimIt(cmd, nodeName)
549     char *cmd;
550     char *nodeName;
551 {
552 
553     static char cmdStr[256];
554     static char cleanName[256];
555     char *strptr;
556 
557     cmdStr[0] = 0;
558 
559     if (!SimRsimRunning) {
560 	RsimErrorMsg();
561 	return;
562     }
563 
564     /* change the node name to a form Rsim will accept.  That is:
565      * if CHANGE_SQBRACKET is defined (it really should not) then
566      * "[" and "]" in the path name must be changed to a "." Also, the
567      * trailing "#" of Magic generated node names must also be removed.
568      */
569 
570     strcpy(cleanName, nodeName);
571     strptr = cleanName;
572     while (*strptr != 0) {
573 #ifdef CHANGE_SQBRACKET
574         if ((*strptr == '[') || (*strptr == ']')) *strptr = '.';
575 #endif
576 	strptr++;
577     }
578     if (*--strptr == '#') {
579 	*strptr = 0;
580     }
581     sprintf(cmdStr, "%s %s\n", cmd, cleanName);
582 
583     /* send the command to Rsim */
584 
585     if (write(pipeOut, cmdStr, strlen(cmdStr)) < 0) {
586 	TxPrintf(E_PIPEWR);
587 	SimStopRsim();
588     }
589 }
590 
591 
592 /*
593  *-----------------------------------------------------------------------
594  * SimFillBuffer
595  *
596  *	This procedure reads characters from Rsim via a pipe and
597  *	places the characters into a buffer pointed by pLastChar.
598  *	It is assumed the buffer is at least READBUF_SIZE charcters
599  *	large, and that charCount contains the number of charcters
600  *	which remain unprocessed in the buffer.
601  *	If an interrupt is received while waiting for input from
602  *	rsim, then the signal is propagated to rsim and we try the
603  *	read again; this should get the simulator to its top level
604  *	command parser (or kill it depending on the simulator).
605  *
606  * Results:
607  *	Number of characters read into the buffer.
608  *
609  * Side effects:
610  *	pLastChar is updated to point to the last valid character
611  *	in the buffer.  charCount is also updated to contain the
612  *	total number of unprocessed characters in the buffer.
613  *	Some i/o may take place.
614  *
615  *-----------------------------------------------------------------------
616  */
617 
618 int
SimFillBuffer(buffHead,pLastChar,charCount)619 SimFillBuffer(buffHead, pLastChar, charCount)
620     char *buffHead;			/* ptr to start of buffer */
621     char **pLastChar;			/* used to return ptr to last char
622 					 * in the buffer.
623 					 */
624     int *charCount;			/* number of chars in the buffer */
625 {
626     int 	charsRead = 0;
627     char 	*temp;
628     int		n, nfd;
629 #if defined(SYSV) || defined(CYGWIN) || defined(__FreeBSD__) || defined(__APPLE__)
630     fd_set readfds, writefds, exceptfds;
631 #else
632     int		nr, nex;
633 #endif  /* SYSV */
634 
635     struct timeval timeout;
636 
637     /* Set the timeout to 5 seconds so we don't block indefinitely if	*/
638     /* something goes wrong with the pipe.				*/
639 
640     timeout.tv_sec = 5;
641     timeout.tv_usec = 0;
642 
643     /* read reply from Rsim */
644 
645 #if defined(SYSV) || defined(CYGWIN) || defined(__FreeBSD__) || defined(__APPLE__)
646     FD_ZERO(&readfds);
647     FD_ZERO(&exceptfds);
648 #endif  /* SYSV */
649 
650     nfd = pipeIn + 1;
651 
652 try_again:
653 
654 #if defined(SYSV) || defined(CYGWIN) || defined(__FreeBSD__) || defined(__APPLE__)
655     FD_SET(pipeIn, &readfds);
656     FD_ZERO(&writefds);
657     FD_SET(pipeIn, &exceptfds);
658     n = select(nfd, &readfds, &writefds, &exceptfds, &timeout);
659 
660 #else /* !SYSV */
661     nr = nex = 1 << pipeIn;
662     n = select(nfd, &nr, (int *) NULL, &nex, &timeout);
663 
664 #endif
665 
666     if (n == 0)
667 	return 0;	/* select() timed out */
668 
669     else if (n < 0)
670     {
671 	if (errno == EINTR)
672 	{
673 	    if (SigInterruptPending)
674 	    {
675 		kill(rsim_pid, SIGINT);
676 		SigInterruptPending = FALSE;
677 	    }
678 	    goto try_again;
679 	}
680     }
681 
682     temp = *pLastChar;
683     charsRead = read(pipeIn, temp, (READBUF_SIZE - 1 - *charCount));
684 
685     if (charsRead > 0) {
686 	temp += charsRead;
687 	if (*charCount == 0) {
688 	    temp--;
689 	}
690 	*pLastChar = temp;
691 	*charCount += charsRead;
692     }
693     ASSERT(((buffHead + READBUF_SIZE) > *pLastChar), "SimFillBuffer");
694     return(charsRead);
695 }
696 
697 /*
698  *-----------------------------------------------------------------------
699  * SimShiftChars
700  *
701  *	This procedure shifts unprocessed charcters in a buffer
702  *	to the beginning of the buffer and updates the head and
703  *	tail pointers to the valid buffer characters.
704  *
705  * Results:
706  *	None.
707  *
708  * Side effects:
709  *	Valid data in the buffer is shifted to the beginning of
710  *	the buffer.
711  *
712  *-----------------------------------------------------------------------
713  */
714 
715 void
SimShiftChars(buffStart,lineStart,lastChar)716 SimShiftChars(buffStart, lineStart, lastChar)
717     char *buffStart;		/* beginning of buffer */
718     char **lineStart;		/* ptr to first valid char in buffer */
719     char **lastChar;		/* ptr to last valid char in buffer */
720 {
721     char *temp;
722     char *temp1;
723 
724     if (buffStart == *lineStart) {
725 	return;
726     }
727 
728     for (temp = buffStart, temp1 = *lineStart; temp1 <= *lastChar;) {
729 	*temp++ = *temp1++;
730     }
731     temp--;
732     *lineStart = buffStart;
733     *lastChar = temp;
734 }
735 
736 /*
737  *-----------------------------------------------------------------------
738  * SimFindNewLine
739  *
740  *	This procedure searches for a '\n' in the char buffer delimited by
741  *	buffStart and buffEnd.
742  *
743  * Results:
744  *	returns a ptr to the '\n' in the buffer.  If no '\n' is found,
745  *	NULL is returned.
746  *
747  * Side effects:
748  *	None.
749  *
750  *-----------------------------------------------------------------------
751  */
752 
753 char *
SimFindNewLine(buffStart,buffEnd)754 SimFindNewLine(buffStart, buffEnd)
755     char 	*buffStart;		/* first char in buffer */
756     char	*buffEnd;		/* last char in buffer */
757 {
758     char *sp;
759 
760     for (sp = buffStart; sp <= buffEnd; sp++) {
761 	if (*sp == '\n') {
762 	    return(sp);
763 	}
764     }
765     return(NULL);
766 }
767 
768 /*
769  *-----------------------------------------------------------------------
770  * SimGetReplyLine
771  *
772  *	This procedure returns the next line of an Rsim reply.  It does this
773  *	by maintaining a character buffer to read the Rsim reply.   This
774  *	buffer is scanned for '\n' which delimit lines, and lines are
775  *	returned from this buffer.  This routine automatically refills
776  *	the reply buffer if no '\n' charcters are found.  Head and tail
777  *	pointers to the "unprocessed" charcters in the buffer (those
778  *	characters beloning to a reply line in the buffer which has not
779  *	yet been returned) are maintained.
780  *
781  *	We assume that the longest line Rsim will return is less than
782  *	256 characters, and that at most 4K characters can be read
783  *	from a pipe in one read call.
784  *
785  * Results:
786  *	SimGetReplyLine returns TRUE if replyLine is a valid value,
787  *	otherwise it is FALSE.
788  *	If we find a line containing only the rsim prompt, replyLine
789  *	points to a NULL pointer and TRUE is returned.
790  *
791  * Side effects:
792  *	replyLine contains a pointer to the next Rsim reply line.
793  *	The buffer returned by replyLine is statically allcoated, so
794  *	subsequent calls to this routine will change this buffer.
795  *
796  *-----------------------------------------------------------------------
797  */
798 
799 bool
SimGetReplyLine(replyLine)800 SimGetReplyLine(replyLine)
801     char **replyLine;
802 {
803     static char	simReadBuff[READBUF_SIZE];	/* buffer in which to read the
804 						 * rsim reply before processing
805 						 * it.  This is big enough for
806 						 * one incomplete Rsim line
807 						 * (256 chars) and for the
808 						 * additional read from the
809 						 * pipe to complete the line
810 						 * (4K chars).
811 						 */
812     static char *lineStart = simReadBuff;	/* points to the first character
813 						 * of the next reply line.
814 						 */
815     static char *lastChar = simReadBuff;	/* points the the last valid
816 						 * char in the input buffer.
817 						 */
818     static int	charsInBuff = 0;		/* number of characters left
819 						 * in input buffer to process.
820 						 */
821     char *strptr;
822     char *strptr1;
823 
824     /* keep reading characters until we have at least enough for a prompt */
825 
826     while (charsInBuff < prompt_len) {
827 	SimShiftChars(simReadBuff, &lineStart, &lastChar);
828 	status = SimFillBuffer(simReadBuff, &lastChar, &charsInBuff);
829 	if (status == 0) {
830 	    /* no more characters to read; stop Rsim */
831 	    SimStopRsim();
832 	    *replyLine = NULL;
833 	    return(FALSE);
834 	}
835 	if (status < 0) {
836 	    TxPrintf(E_PIPERD);
837 	    *replyLine = NULL;
838 	    return(FALSE);
839 	}
840     }
841 
842     /* check for the prompt at the end of the buffer, if found then
843      * reset buffer pointers and character count.
844      */
845 
846     if (!(strncmp(rsim_prompt, lineStart, prompt_len)))  {
847 	lineStart = lastChar = simReadBuff;
848 	charsInBuff = 0;
849 	*replyLine = NULL;
850 	return(TRUE);
851     }
852 
853     /* Now try to extract a line out of the buffer. */
854 
855     strptr = SimFindNewLine(lineStart, lastChar);
856 
857     if (!strptr) {
858 
859 	/* haven't found a '\n' in the buffer yet */
860 
861 	SimShiftChars(simReadBuff, &lineStart, &lastChar);
862 	strptr1 = lastChar;
863 
864 	/* keep trying to read characters until we find a '\n'; we
865 	 * are assuming rsim reply lines are no more than LINEBUF_SIZE
866 	 * characters in length.
867 	 */
868 
869 	while (TRUE) {
870 	    strptr = SimFindNewLine(lineStart, lastChar);
871 	    if ((charsInBuff > LINEBUF_SIZE) || (strptr)) {
872 		break;
873 	    }
874 	    strptr1 = lastChar;
875 	    status = SimFillBuffer(simReadBuff, &lastChar, &charsInBuff);
876 	    if (status == 0) {
877 		/* no more characters to read; stop Rsim */
878 		SimStopRsim();
879 		*replyLine = NULL;
880 		return(FALSE);
881 	    }
882 	    if (status < 0) {
883 		TxPrintf(E_PIPERD);
884 		*replyLine = NULL;
885 		return(FALSE);
886 	    }
887 	}
888     }
889 
890     if (!strptr) {
891 	TxPrintf("Error in SimGetReplyLine  -- Rsim line longer than 256 chars\n");
892 	*replyLine = NULL;
893 	return(FALSE);
894     }
895 
896     *strptr = 0;				/* change the '\n' to a NULL */
897     strptr1 = lineStart;			/* string to return */
898     lineStart = strptr + 1;			/* start of next line */
899     charsInBuff -= (strlen(strptr1) + 1); 	/* + 1 because of the '\n' */
900     if (charsInBuff == 0) {			/* reset buffer pointers */
901 	lineStart = lastChar = simReadBuff;
902     }
903     *replyLine = strptr1;
904     return(TRUE);
905 }
906 
907 /* ----------------------------------------------------------------------------
908  *
909  * SimInit --
910  *
911  *	Initialize this module.
912  *
913  * Results:
914  *	None.
915  *
916  * Side Effects:
917  *	Just initialization.
918  *
919  * ----------------------------------------------------------------------------
920  */
921 
922 void
SimInit()923 SimInit()
924 {
925     static char *rsimdoc =
926 "You are currently using the \"rsim\" tool.  The button actions are:\n\
927    left    - move the box so its lower-left corner is at cursor position\n\
928    right   - resize box by moving upper-right corner to cursor position\n\
929    middle  - display the Rsim node values of the selected paint\n\
930 You can move or resize the box by different corners by pressing left\n\
931     or right, holding it down, moving the cursor near a different corner\n\
932     and clicking the other (left or right) button down then up without\n\
933     releasing the initial button.  Rsim must already have been started\n\
934     to display the node values on the circuit.\n";
935 
936     DBWAddButtonHandler("rsim", SimRsimHandler, STYLE_CURS_RSIM, rsimdoc);
937 }
938 
939 #endif	/* RSIM_MODULE */
940