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