996
|
1 /****************************************************************************
|
|
2
|
|
3 NAME
|
|
4 yuk.c -- C-INTERCAL debugger and profiler code, linked into programs.
|
|
5
|
|
6 DESCRIPTION
|
|
7 File linked into programs as a debugger or profiler.
|
|
8
|
|
9 LICENSE TERMS
|
|
10 Copyright (C) 2006 Alex Smith
|
|
11
|
|
12 This program is free software; you can redistribute it and/or modify
|
|
13 it under the terms of the GNU General Public License as published by
|
|
14 the Free Software Foundation; either version 2 of the License, or
|
|
15 (at your option) any later version.
|
|
16
|
|
17 This program is distributed in the hope that it will be useful,
|
|
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20 GNU General Public License for more details.
|
|
21
|
|
22 You should have received a copy of the GNU General Public License
|
|
23 along with this program; if not, write to the Free Software
|
|
24 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
25
|
|
26 ****************************************************************************/
|
|
27 #include "config.h" /* AIS: Generated by autoconf */
|
|
28 #include <stdlib.h>
|
|
29 #include <stdio.h>
|
|
30 #ifdef HAVE_SYS_TIME_H
|
|
31 # include <sys/time.h>
|
|
32 # ifdef TIME_WITH_SYS_TIME
|
|
33 # include <time.h>
|
|
34 # endif
|
|
35 #else
|
|
36 # include <time.h>
|
|
37 #endif
|
|
38 #include <signal.h>
|
|
39 #ifdef HAVE_UNISTD_H
|
|
40 # include <unistd.h>
|
|
41 #endif
|
|
42 #include <setjmp.h>
|
|
43 #include <string.h>
|
|
44
|
|
45 #define YUKDEBUG 1
|
|
46
|
|
47 #include "yuk.h"
|
|
48 #include "ick_lose.h"
|
|
49 #include "sizes.h"
|
|
50 #include "abcess.h"
|
|
51 #include "uncommon.h"
|
|
52
|
|
53 #if YPTIMERTYPE == 4
|
|
54 #include <sys/times.h>
|
|
55 #endif
|
|
56
|
|
57 extern signed char onewatch[];
|
|
58 extern signed char twowatch[];
|
|
59 extern ick_type16 oneold[];
|
|
60 extern ick_type32 twoold[];
|
|
61
|
|
62 char** globalargv;
|
|
63 int globalargc;
|
|
64 int yuklines = 0;
|
|
65 int yukloop = 0;
|
|
66 int yukcommands = 0; /* these 5 lines because externs must be defined
|
|
67 somewhere */
|
|
68 /* Global variable storage types:
|
|
69 static: limited to the file
|
|
70 unadorned: defining an extern
|
|
71 extern: defined elsewhere (unless initialised) */
|
|
72
|
|
73 static char buf[21];
|
|
74
|
|
75 static sig_atomic_t singlestep = 1; /* if 0, run until a breakpoint */
|
|
76 static sig_atomic_t writelines = 1; /* whether to display executed lines onscreen */
|
|
77 static int breakpoints[80]; /* initialised to all 0s. Breakpoint locations */
|
|
78 static int nbreakpoints = 1; /* how many breakpoints we have */
|
|
79 static int monitors[80]; /* monitors give a message when program flow passes them */
|
|
80 static int nmonitors = 0;
|
|
81 static int untilnext = -1; /* NEXTING level to break the program at */
|
|
82 static int firstrun = 1; /* ick_first time an interactive command point is reached */
|
|
83 static int yukerrcmdg = -1; /* the aboff that indicates an error in the 'g' command */
|
|
84
|
|
85 static yptimer tickcount; /* yptimer of last run */
|
|
86 static int lastaboff = 0; /* last value of aboff */
|
|
87
|
|
88 static void handlesigint(int i)
|
|
89 { /* this is a signal handler, so can't do much */
|
|
90 singlestep = 1;
|
|
91 writelines = 1;
|
|
92 /*@-noeffect@*/ (void) i; /*@=noeffect@*/
|
|
93 }
|
|
94
|
|
95 #if YPTIMERTYPE==1 || YPTIMERTYPE==2
|
|
96 static yptimer yukgettimeofday()
|
|
97 {
|
|
98 static struct timeval tp;
|
|
99 yptimer temp;
|
|
100 /* gettimeofday is POSIX; config.sh has checked that it's
|
|
101 available, so turn off the unrecog warning */
|
|
102 /*@-unrecog@*/
|
|
103 gettimeofday(&tp,0);
|
|
104 /*@=unrecog@*/
|
|
105 temp=(yptimer)tp.tv_usec +
|
|
106 (yptimer)tp.tv_sec * (yptimer)1000000LU;
|
|
107 /* here we make use of unsigned wraparound. In the case
|
|
108 YPTIMERTYPE == 1, it seems quite likely that we're going
|
|
109 to wraparound, but because everything is cast to the
|
|
110 unsigned integral type yptimer, we get a value that will
|
|
111 wraparound in such a way that - will give us the correct
|
|
112 time interval. */
|
|
113 return temp;
|
|
114 }
|
|
115 #elif YPTIMERTYPE == 4
|
|
116 yptimer yuktimes()
|
|
117 {
|
|
118 static struct tms tp;
|
|
119 times(&tp);
|
|
120 return tp.tms_utime + tp.tms_stime;
|
|
121 }
|
|
122 #elif YPTIMERTYPE == 5
|
|
123 static yptimer yukclock_gettime()
|
|
124 {
|
|
125 static struct timespec ts;
|
|
126 yptimer temp;
|
|
127 /* We've checked that this function is available; -lrt will be linked in. */
|
|
128 /*@-unrecog@*/
|
|
129 #if defined(_POSIX_CPUTIME) && _POSIX_CPUTIME > 0
|
|
130 clock_gettime(CLOCK_PROCESS_CPUTIME_ID,&ts);
|
|
131 #else
|
|
132 # if defined(_POSIX_THREAD_CPUTIME) && _POSIX_THREAD_CPUTIME > 0
|
|
133 clock_gettime(CLOCK_THREAD_CPUTIME_ID,&ts);
|
|
134 # else
|
|
135 # if defined(_POSIX_MONOTONIC_CLOCK) && _POSIX_MONOTONIC_CLOCK > 0
|
|
136 clock_gettime(CLOCK_MONOTONIC,&ts);
|
|
137 # else
|
|
138 # ifndef CLOCK_REALTIME
|
|
139 # error clock_gettime is defined, but no clocks seem to be; try changing YPTIMERTYPE in src/yuk.h
|
|
140 # endif
|
|
141 clock_gettime(CLOCK_REALTIME,&ts);
|
|
142 # endif
|
|
143 # endif
|
|
144 #endif
|
|
145 /*@=unrecog@*/
|
|
146 temp=(yptimer)ts.tv_nsec +
|
|
147 (yptimer)ts.tv_sec * (yptimer)1000000000LU;
|
|
148 /* using wraparound as with gettimeofday */
|
|
149 return temp;
|
|
150 }
|
|
151 #endif /* YPTIMERTYPE */
|
|
152
|
|
153 void yukterm(void)
|
|
154 {
|
|
155 int i,lastline,thisline,inrow=0;
|
|
156 yptimer avgtime,avgtime2;
|
|
157 if(yukopts==2) (void) puts("Program ended without error.");
|
|
158 if(!(yukopts&1)) return;
|
|
159 /* Print profiling information */
|
|
160 (void) puts("Profiling information saved to \"yuk.out\".");
|
|
161 (void) freopen("yuk.out","w",stdout);
|
|
162 i=-1;
|
|
163 lastline=-1;
|
|
164 while(++i<yukcommands)
|
|
165 {
|
|
166 thisline=lineofaboff[i];
|
|
167 if(thisline==lastline) inrow++; else
|
|
168 {
|
|
169 inrow=1;
|
|
170 printf("%5d:\t%s\n",thisline,textlines[thisline]);
|
|
171 }
|
|
172 lastline=thisline;
|
|
173 avgtime=ypexecount[i]+ypabscount[i];
|
|
174 if(avgtime) avgtime=ypexectime[i]/avgtime;
|
|
175 avgtime2=0;
|
|
176 if(ypexecount[i]) avgtime2=ypexectime[i]/ypexecount[i];
|
|
177 /*@-formatcode@*/ /* Splint doesn't understand string interpolation */
|
|
178 printf("C%d: Time%4" YPTIMERTFORMAT ".%0" YPTIMERTFORMATD
|
|
179 ", Avg%2" YPTIMERTFORMAT ".%0" YPTIMERTFORMATD ", "
|
|
180 "Avg Exec%2" YPTIMERTFORMAT ".%0" YPTIMERTFORMATD
|
|
181 ", Exec%8" YPCOUNTERTFORMAT ", Abs%8" YPCOUNTERTFORMAT "\n",
|
|
182 inrow,ypexectime[i]/YPTIMERSCALE, ypexectime[i]%YPTIMERSCALE,
|
|
183 avgtime/YPTIMERSCALE, avgtime%YPTIMERSCALE,
|
|
184 avgtime2/YPTIMERSCALE, avgtime2%YPTIMERSCALE,
|
|
185 ypexecount[i],ypabscount[i]);
|
|
186 /*@=formatcode@*/
|
|
187 }
|
|
188 }
|
|
189
|
|
190 void yukline(int aboff,int emitlineno)
|
|
191 { /* this runs every time a source line is encountered */
|
|
192 int i;
|
|
193 int broken; /* hit a breakpoint, monitorpoint, viewbreak, until */
|
|
194 int keeplooping;
|
|
195 int templine;
|
|
196 int tempcmd;
|
|
197 int temp;
|
|
198 yptimer temptick;
|
|
199 char* text=textlines[lineofaboff[aboff]];
|
|
200 char copyloc[BUFSIZ+9];
|
|
201 const char* tempcharp;
|
|
202 if(!yukopts) return;
|
|
203 if(globalargc!=3)
|
|
204 ick_lose(IE778,emitlineno,(const char*)NULL);
|
|
205 if(yukopts & 1)
|
|
206 { /* profile */
|
|
207 temptick=YPGETTIME;
|
|
208 if(lastaboff) ypexectime[lastaboff]+=(yptimer)(temptick-tickcount);
|
|
209 tickcount=temptick;
|
|
210 if(ick_abstained[aboff]) ypabscount[aboff]++;
|
|
211 else ypexecount[aboff]++;
|
|
212 lastaboff=aboff;
|
|
213 }
|
|
214 if(yukopts & 2)
|
|
215 { /* debug */
|
|
216 if(firstrun)
|
|
217 {
|
|
218 /* GNU GPL requires a copyright notice at this point */
|
|
219 (void) puts("yuk debugger Copyright (C) 2006 Alex Smith.");
|
|
220 (void) puts("The yuk debugger is covered by the GNU GPL and can");
|
|
221 (void) puts("be redistributed freely, but comes with ABSOLUTELY");
|
|
222 (void) puts("NO WARRANTY. For more information, type *<RET>.\n");
|
|
223 (void) puts("For help on yuk, type ?<RET>.\n");
|
|
224 }
|
|
225 i=nbreakpoints;
|
|
226 broken=0;
|
|
227 while(i--) broken|=breakpoints[i]==lineofaboff[aboff];
|
|
228 if(yukloop&&broken&&*breakpoints!=lineofaboff[aboff]) broken=0;
|
|
229 if(broken)
|
|
230 {
|
|
231 if(*breakpoints!=lineofaboff[aboff])
|
|
232 printf("Breakpoint hit at line %d:\n",lineofaboff[aboff]);
|
|
233 singlestep=1;
|
|
234 }
|
|
235 else
|
|
236 {
|
|
237 i=nmonitors;
|
|
238 while(i--) broken|=monitors[i]==lineofaboff[aboff];
|
|
239 if(yukloop) broken=0;
|
|
240 if(broken) printf("Command flowed past line %d:\n",lineofaboff[aboff]);
|
|
241 }
|
|
242 if(ick_nextindex <= untilnext)
|
|
243 {
|
|
244 broken = 1;
|
|
245 singlestep = 1;
|
|
246 }
|
|
247 if(!broken&&yukerrcmdg==aboff)
|
|
248 {
|
|
249 singlestep = 1;
|
|
250 (void) puts("There are no commands on that line.");
|
|
251 /* To the user, nothing will have happened but the error message! */
|
|
252 }
|
|
253 i=-1;
|
|
254 while(++i,1)
|
|
255 {
|
|
256 if(yukvars[i].vartype==YUKEND) break;
|
|
257 if(yukvars[i].vartype==ick_ONESPOT)
|
|
258 {
|
|
259 if(onewatch[yukvars[i].intername] != (char)0)
|
|
260 {
|
|
261 if(ick_onespots[yukvars[i].intername]!=oneold[yukvars[i].intername]&&
|
|
262 onewatch[yukvars[i].intername]>(char)1)
|
|
263 {
|
|
264 oneold[yukvars[i].intername]=ick_onespots[yukvars[i].intername];
|
|
265 if(onewatch[yukvars[i].intername]==(char)2||!ick_onespots[yukvars[i].intername])
|
|
266 {
|
|
267 /*@-formatconst@*/ /* it's safe, I checked it */
|
|
268 printf(onewatch[yukvars[i].intername]==(char)2?
|
|
269 "Variable .%d changed.\n":"Variable .%d became 0.\n",
|
|
270 yukvars[i].extername);
|
|
271 /*@=formatconst@*/
|
|
272 broken=1; singlestep=1;
|
|
273 }
|
|
274 }
|
|
275 if(writelines||broken)
|
|
276 {
|
|
277 printf(".%d is:\n",yukvars[i].extername);
|
|
278 ick_pout(ick_onespots[yukvars[i].intername]);
|
|
279 }
|
|
280 }
|
|
281 }
|
|
282 if(yukvars[i].vartype==ick_TWOSPOT)
|
|
283 {
|
|
284 if(twowatch[yukvars[i].intername] != (char)0)
|
|
285 {
|
|
286 if(ick_twospots[yukvars[i].intername]!=twoold[yukvars[i].intername]&&
|
|
287 twowatch[yukvars[i].intername] > (char)1)
|
|
288 {
|
|
289 twoold[yukvars[i].intername]=ick_twospots[yukvars[i].intername];
|
|
290 if(twowatch[yukvars[i].intername]==(char)2||!ick_twospots[yukvars[i].intername])
|
|
291 {
|
|
292 /*@-formatconst@*/ /* there's just the one %d each way round */
|
|
293 printf(twowatch[yukvars[i].intername]==(char)2?
|
|
294 "Variable :%d changed.\n":"Variable :%d became 0.\n",
|
|
295 yukvars[i].extername);
|
|
296 /*@=formatconst@*/
|
|
297 broken=1; singlestep=1;
|
|
298 }
|
|
299 }
|
|
300 if(writelines||broken)
|
|
301 {
|
|
302 printf(":%d is:\n",yukvars[i].extername);
|
|
303 ick_pout(ick_twospots[yukvars[i].intername]);
|
|
304 }
|
|
305 }
|
|
306 }
|
|
307 }
|
|
308 if(writelines||broken)
|
|
309 {
|
|
310 /* write line that we're on */
|
|
311 printf("%5d:\t%s\n",lineofaboff[aboff],text);
|
|
312 /* write command within line that we're on */
|
|
313 tempcmd=aboff;
|
|
314 while(tempcmd&&lineofaboff[--tempcmd]==lineofaboff[aboff]);
|
|
315 printf("On C%d: Abstained %d time%s\n",aboff?aboff-tempcmd:1,
|
|
316 ick_abstained[aboff]-yukloop,ick_abstained[aboff]-yukloop==1?".":"s.");
|
|
317 }
|
|
318 if(singlestep)
|
|
319 {
|
|
320 (void)signal(SIGINT,handlesigint); /* placing this line here means that a rapid
|
|
321 ^C^C will terminate the program, if it's
|
|
322 stuck in a loop somewhere */
|
|
323 keeplooping = 1;
|
|
324 breakpoints[0] = 0; /* breakpoints[0] goes whenever a breakpoint is hit */
|
|
325 untilnext = -1;
|
|
326 if(yukloop)
|
|
327 { /* reverse the abstentions that g caused */
|
|
328 i = -1;
|
|
329 while(++i<yukcommands) ick_abstained[i]--;
|
|
330 }
|
|
331 yukloop = 0;
|
|
332 yukerrcmdg = -1;
|
|
333 do
|
|
334 {
|
|
335 printf("yuk007 "); /* this is our prompt, a sort of reverse
|
|
336 INTERCAL version of % */
|
|
337 (void) fgets(buf,20,stdin);
|
|
338 if(!strchr(buf,'\n'))
|
|
339 {
|
|
340 ick_lose(IE810,emitlineno,(const char*)NULL);
|
|
341 }
|
|
342 templine=0;
|
|
343 switch(*buf)
|
|
344 {
|
|
345 case '?':
|
|
346 case '@':
|
|
347 (void) puts("a<line>\tabstain once from all non-ick_abstained commands on <line>");
|
|
348 (void) puts("b<line>\tset breakpoint at <line>");
|
|
349 (void) puts("c\tcontinue execution until a breakpoint is reached");
|
|
350 (void) puts("d<line>\tdelete breakpoint at <line>");
|
|
351 (void) puts("e<line>\texplain the ick_main expression on <line>");
|
|
352 (void) puts("f<line>\tstop producing messages when commands on <line> are run");
|
|
353 (void) puts("g<line>\tchange currently executing command to the ick_first command");
|
|
354 (void) puts("\ton <line> or the ick_next command if already on <line>");
|
|
355 (void) puts("h\tlist 10 lines either side of the current line");
|
|
356 (void) puts("i<var>\tignore a variable");
|
|
357 (void) puts("j<var>\tremember a variable");
|
|
358 (void) puts("k\tcontinue until we RESUME back to the current nexting level");
|
|
359 (void) puts("\t(that is, step unless we are on a NEXT, in which case execute");
|
|
360 (void) puts("\tuntil a RESUME or FORGET back to the same or smaller NEXT stack)");
|
|
361 (void) puts("l<line>\tlist 10 lines either side of <line>");
|
|
362 (void) puts("m<line>\tproduce a message every time a command on <line> is run");
|
|
363 (void) puts("n\tshow the NEXT stack");
|
|
364 (void) puts("o\tcontinue until we RESUME/FORGET below the current nexting level");
|
|
365 (void) puts("\tie until the NEXT stack becomes smaller than it is at present");
|
|
366 (void) puts("p\tdisplay the values of all onespot and twospot variables");
|
|
367 (void) puts("q\tabort execution");
|
|
368 (void) puts("r<line>\treinstate once all ick_abstained commands on <line>");
|
|
369 (void) puts("s\texecute one command");
|
|
370 (void) puts("t\tcontinue until a breakpoint, displaying all lines executed");
|
|
371 (void) puts("u<line>\texecute until just before <line> is reached");
|
|
372 (void) puts("v<var>\tshow value of variable every time a command is printed");
|
|
373 (void) puts("w\tshow the current line and current command");
|
|
374 (void) puts("x<var>\tremove a variable view, breakchange, or breakzero");
|
|
375 (void) puts("y<var>\tview variable every displayed line, and break on change");
|
|
376 (void) puts("z<var>\tview variable every displayed line, and break on zero");
|
|
377 (void) puts("<var>\tview the value of a onespot or twospot variable");
|
|
378 (void) puts("<<var>\tset the value of a onespot or twospot variable");
|
|
379 (void) puts("*\tview the GNU General Public License");
|
|
380 (void) puts("?\tview this help screen");
|
|
381 (void) puts("@\tview this help screen");
|
|
382 (void) puts("Line numbers refer to lines of source code, not line labels.");
|
|
383 (void) puts("Listings have (Axxxxxx) at the start of each line: this shows");
|
|
384 (void) puts("the abstention status of each command on that line.");
|
|
385 (void) puts("The values of variables must be input in proper INTERCAL");
|
|
386 (void) puts("notation (i.e. ONE TWO THREE), and are output as butchered");
|
|
387 (void) puts("Roman ick_numerals.");
|
|
388 #ifdef __DJGPP__
|
|
389 (void) puts("You can press <CTRL>-<BREAK> to interrupt an executing program.");
|
|
390 #else
|
|
391 (void) puts("You can press <CTRL>-C to interrupt an executing program.");
|
|
392 #endif
|
|
393 break;
|
|
394 case 'q':
|
|
395 exit(0);
|
|
396 /*@-unreachable@*/ break; /*@=unreachable@*/
|
|
397 case 'n':
|
|
398 i=ick_nextindex;
|
|
399 if(!i)
|
|
400 {
|
|
401 (void) puts("The NEXT stack is empty.");
|
|
402 }
|
|
403 else
|
|
404 {
|
|
405 (void) puts("Commands NEXTED from:");
|
|
406 while(i--)
|
|
407 {
|
|
408 /* write NEXT line */
|
|
409 printf("%5d:\t%s\n",lineofaboff[ick_next[i]-1],
|
|
410 textlines[lineofaboff[ick_next[i]-1]]);
|
|
411 /* write NEXT command within line */
|
|
412 tempcmd=(int)ick_next[i];
|
|
413 while(tempcmd&&lineofaboff[--tempcmd]==lineofaboff[ick_next[i]-1]);
|
|
414 printf("NEXTED from command C%u: Abstained %d time%s\n",ick_next[i]-1?
|
|
415 ick_next[i]-1-tempcmd:1,ick_abstained[ick_next[i]-1],
|
|
416 ick_abstained[ick_next[i]-1]==1?".":"s.");
|
|
417 }
|
|
418 }
|
|
419 break;
|
|
420 case 'w':
|
|
421 /* write line that we're on */
|
|
422 printf("%5d:\t%s\n",lineofaboff[aboff],text);
|
|
423 /* write command within line that we're on */
|
|
424 tempcmd=aboff;
|
|
425 while(tempcmd&&lineofaboff[--tempcmd]==lineofaboff[aboff]);
|
|
426 printf("On C%d: Abstained %d time%s\n",aboff?aboff-tempcmd:1,ick_abstained[aboff],
|
|
427 ick_abstained[aboff]==1?".":"s.");
|
|
428 break;
|
|
429 case 'p':
|
|
430 i=-1;
|
|
431 while(++i,1)
|
|
432 {
|
|
433 if(yukvars[i].vartype==YUKEND) break;
|
|
434 if(yukvars[i].vartype==ick_ONESPOT)
|
|
435 {
|
|
436 printf("Variable .%d is:\n",yukvars[i].extername);
|
|
437 ick_pout(ick_onespots[yukvars[i].intername]);
|
|
438 }
|
|
439 if(yukvars[i].vartype==ick_TWOSPOT)
|
|
440 {
|
|
441 printf("Variable :%d is:\n",yukvars[i].extername);
|
|
442 ick_pout(ick_twospots[yukvars[i].intername]);
|
|
443 }
|
|
444 }
|
|
445 break;
|
|
446 case '.':
|
|
447 case ':':
|
|
448 temp = sscanf(buf+1,"%d",&templine);
|
|
449 if(templine<1 || temp != 1)
|
|
450 {
|
|
451 (void) puts("Don't know which variable you mean.");
|
|
452 break;
|
|
453 }
|
|
454 i=-1;
|
|
455 temp=0;
|
|
456 while(++i,!temp)
|
|
457 {
|
|
458 if(yukvars[i].vartype==YUKEND) break;
|
|
459 if((*buf=='.'&&yukvars[i].vartype==ick_ONESPOT)||
|
|
460 (*buf==':'&&yukvars[i].vartype==ick_TWOSPOT))
|
|
461 {
|
|
462 if(yukvars[i].extername==templine)
|
|
463 {
|
|
464 temp=1;
|
|
465 if(yukvars[i].vartype==ick_ONESPOT)
|
|
466 {
|
|
467 ick_pout(ick_onespots[yukvars[i].intername]);
|
|
468 (void) puts(ick_oneforget[yukvars[i].intername]?
|
|
469 "This variable is currently ignored.":
|
|
470 "This variable is currently remembered.");
|
|
471 }
|
|
472 if(yukvars[i].vartype==ick_TWOSPOT)
|
|
473 {
|
|
474 ick_pout(ick_twospots[yukvars[i].intername]);
|
|
475 (void) puts(ick_twoforget[yukvars[i].intername]?
|
|
476 "This variable is currently ignored.":
|
|
477 "This variable is currently remembered.");
|
|
478 }
|
|
479 }
|
|
480 }
|
|
481 }
|
|
482 if(temp) break;
|
|
483 (void) puts("That variable is not in the program.");
|
|
484 break;
|
|
485 case 'v':
|
|
486 case 'x':
|
|
487 case 'y':
|
|
488 case 'z':
|
|
489 if(buf[1]!='.'&&buf[1]!=':')
|
|
490 {
|
|
491 (void) puts("This command only works on onespot and twospot variables.");
|
|
492 break;
|
|
493 }
|
|
494 temp = sscanf(buf+2,"%d",&templine);
|
|
495 if(templine<1 || temp != 1)
|
|
496 {
|
|
497 (void) puts("Don't know which variable you mean.");
|
|
498 break;
|
|
499 }
|
|
500 i=-1;
|
|
501 temp=0;
|
|
502 while(++i,!temp)
|
|
503 {
|
|
504 if(yukvars[i].vartype==YUKEND) break;
|
|
505 if(yukvars[i].extername==templine)
|
|
506 {
|
|
507 if(buf[1]=='.'&&yukvars[i].vartype==ick_ONESPOT)
|
|
508 {
|
|
509 if(*buf=='v') onewatch[yukvars[i].intername]=(char)1;
|
|
510 if(*buf=='x') onewatch[yukvars[i].intername]=(char)0;
|
|
511 if(*buf=='y') onewatch[yukvars[i].intername]=(char)2;
|
|
512 if(*buf=='z') onewatch[yukvars[i].intername]=(char)3;
|
|
513 oneold[yukvars[i].intername]=ick_onespots[yukvars[i].intername];
|
|
514 temp=1;
|
|
515 }
|
|
516 if(buf[1]==':'&&yukvars[i].vartype==ick_TWOSPOT)
|
|
517 {
|
|
518 if(*buf=='v') twowatch[yukvars[i].intername]=(char)1;
|
|
519 if(*buf=='x') twowatch[yukvars[i].intername]=(char)0;
|
|
520 if(*buf=='y') twowatch[yukvars[i].intername]=(char)2;
|
|
521 if(*buf=='z') twowatch[yukvars[i].intername]=(char)3;
|
|
522 twoold[yukvars[i].intername]=ick_twospots[yukvars[i].intername];
|
|
523 temp=1;
|
|
524 }
|
|
525 }
|
|
526 }
|
|
527 if(!temp)
|
|
528 {
|
|
529 (void) puts("That variable is not in the program.");
|
|
530 break;
|
|
531 }
|
|
532 if(*buf=='v') (void) puts("Set a normal variable view.");
|
|
533 if(*buf=='x') (void) puts("Removed all views from that variable.");
|
|
534 if(*buf=='y') (void) puts("Set a breakchange variable view.");
|
|
535 if(*buf=='z') (void) puts("Set a breakzero variable view.");
|
|
536 break;
|
|
537 case 'i':
|
|
538 case 'j':
|
|
539 if(buf[1]!='.'&&buf[1]!=':'&&buf[1]!=','&&buf[1]!=';')
|
|
540 {
|
|
541 (void) puts("That isn't a real sort of variable.");
|
|
542 break;
|
|
543 }
|
|
544 temp = sscanf(buf+2,"%d",&templine);
|
|
545 if(templine<1 || temp != 1)
|
|
546 {
|
|
547 (void) puts("Don't know which variable you mean.");
|
|
548 break;
|
|
549 }
|
|
550 i=-1;
|
|
551 temp=0;
|
|
552 while(++i,!temp)
|
|
553 {
|
|
554 if(yukvars[i].vartype==YUKEND) break;
|
|
555 if((buf[1]=='.'&&yukvars[i].vartype==ick_ONESPOT)||
|
|
556 (buf[1]==':'&&yukvars[i].vartype==ick_TWOSPOT)||
|
|
557 (buf[1]==','&&yukvars[i].vartype==ick_TAIL)||
|
|
558 (buf[1]==';'&&yukvars[i].vartype==ick_HYBRID))
|
|
559 {
|
|
560 if(yukvars[i].extername==templine)
|
|
561 {
|
|
562 temp=1;
|
|
563 if(yukvars[i].vartype==ick_ONESPOT)
|
|
564 ick_oneforget[yukvars[i].intername]=*buf=='i';
|
|
565 if(yukvars[i].vartype==ick_TWOSPOT)
|
|
566 ick_twoforget[yukvars[i].intername]=*buf=='i';
|
|
567 if(yukvars[i].vartype==ick_TAIL)
|
|
568 ick_tailforget[yukvars[i].intername]=*buf=='i';
|
|
569 if(yukvars[i].vartype==ick_HYBRID)
|
|
570 ick_hyforget[yukvars[i].intername]=*buf=='i';
|
|
571 }
|
|
572 }
|
|
573 }
|
|
574 if(temp)
|
|
575 {
|
|
576 if(*buf=='i')
|
|
577 (void) puts("Variable ignored.");
|
|
578 else
|
|
579 (void) puts("Variable remembered.");
|
|
580 break;
|
|
581 }
|
|
582 (void) puts("That variable is not in the program.");
|
|
583 break;
|
|
584 case '<':
|
|
585 if(buf[1]!='.'&&buf[1]!=':')
|
|
586 {
|
|
587 (void) puts("You cannot set that sort of variable (if it exists at all).");
|
|
588 break;
|
|
589 }
|
|
590 temp = sscanf(buf+2,"%d",&templine);
|
|
591 if(templine<1 || temp != 1)
|
|
592 {
|
|
593 (void) puts("Don't know which variable you mean.");
|
|
594 break;
|
|
595 }
|
|
596 i=-1;
|
|
597 temp=0;
|
|
598 while(++i,!temp)
|
|
599 {
|
|
600 if(yukvars[i].vartype==YUKEND) break;
|
|
601 if((buf[1]=='.'&&yukvars[i].vartype==ick_ONESPOT)||
|
|
602 (buf[1]==':'&&yukvars[i].vartype==ick_TWOSPOT))
|
|
603 {
|
|
604 if(yukvars[i].extername==templine)
|
|
605 {
|
|
606 temp=1;
|
|
607 if(yukvars[i].vartype==ick_ONESPOT)
|
|
608 ick_onespots[yukvars[i].intername]=(ick_type16)ick_pin();
|
|
609 if(yukvars[i].vartype==ick_TWOSPOT)
|
|
610 ick_twospots[yukvars[i].intername]=(ick_type32)ick_pin();
|
|
611 /* note that when debugging, you can set an
|
|
612 ignored variable */
|
|
613 }
|
|
614 }
|
|
615 }
|
|
616 if(temp) break;
|
|
617 (void) puts("That variable is not in the program.");
|
|
618 break;
|
|
619 case 'g':
|
|
620 temp = sscanf(buf+1,"%d",&templine);
|
|
621 if(!templine || temp != 1)
|
|
622 {
|
|
623 (void) puts("Don't know which line you mean.");
|
|
624 break;
|
|
625 }
|
|
626 breakpoints[0] = templine;
|
|
627 yukloop = 1;
|
|
628 /* This is implemented by incrementing all ABSTAIN counts,
|
|
629 even the normally immutable ones on GIVE UP lines, setting
|
|
630 a temporary breakpoint ([0]) on the line given, and running the
|
|
631 program. When the breakpoint is hit singlestep will see that
|
|
632 yukloop is set (its purpose is to cause the program to go back
|
|
633 to the start when it reaches the end) and decrement all ABSTAIN
|
|
634 counts, putting the commands back the way they were. We set an error
|
|
635 breakpoint on this line in case the user is trying to jump to a
|
|
636 line with no commands (although this debugger command is called
|
|
637 'g', would I dare to describe this as a GOTO?) */
|
|
638 i = -1;
|
|
639 while(++i<yukcommands) ick_abstained[i]++;
|
|
640 yukerrcmdg = aboff;
|
|
641 singlestep = 0;
|
|
642 writelines = 0;
|
|
643 keeplooping = 0; /* to break out of the loop in the debugger! */
|
|
644 break;
|
|
645 case 'a':
|
|
646 case 'r':
|
|
647 temp = sscanf(buf+1,"%d",&templine);
|
|
648 if(!templine || temp != 1)
|
|
649 {
|
|
650 (void) puts("Don't know which line you mean.");
|
|
651 break;
|
|
652 }
|
|
653 i=-1;
|
|
654 tempcmd=1;
|
|
655 while(++i<yukcommands)
|
|
656 {
|
|
657 if(lineofaboff[i]==templine)
|
|
658 {
|
|
659 temp=ick_abstained[i];
|
|
660 if(*buf=='a') if(!temp) ick_abstained[i]=1;
|
|
661 if(*buf=='r') if(temp) ick_abstained[i]--;
|
|
662 printf("Command %d on line %d was ick_abstained %d time%s, "
|
|
663 "now ick_abstained %d time%s.\n",tempcmd,templine,
|
|
664 temp,temp==1?"":"s",ick_abstained[i],
|
|
665 ick_abstained[i]==1?"":"s");
|
|
666 tempcmd++;
|
|
667 }
|
|
668 }
|
|
669 if(tempcmd==1) (void) puts("No commands start on this line.");
|
|
670 break;
|
|
671 case 'k':
|
|
672 case 'o':
|
|
673 untilnext=ick_nextindex-(*buf=='o');
|
|
674 /*@fallthrough@*/
|
|
675 case 'c':
|
|
676 singlestep=0;
|
|
677 writelines=0;
|
|
678 keeplooping=0;
|
|
679 break;
|
|
680 case 'b':
|
|
681 temp = sscanf(buf+1,"%d",&templine);
|
|
682 if(templine && temp == 1)
|
|
683 {
|
|
684 printf("Breakpoint set at line %d.\n",templine);
|
|
685 breakpoints[nbreakpoints++]=templine;
|
|
686 if(nbreakpoints>=80) ick_lose(IE811,emitlineno,(const char*)NULL);
|
|
687 }
|
|
688 else
|
|
689 (void) puts("Don't know which line you mean.");
|
|
690 break;
|
|
691 case 'd':
|
|
692 temp = sscanf(buf+1,"%d",&templine);
|
|
693 if(templine && temp == 1)
|
|
694 {
|
|
695 printf("All breakpoints removed from line %d.\n",templine);
|
|
696 i=nbreakpoints;
|
|
697 while(i--) if(templine==breakpoints[i])
|
|
698 {
|
|
699 memmove(breakpoints+i,breakpoints+i+1,sizeof(int)*(nbreakpoints-i));
|
|
700 nbreakpoints--;
|
|
701 }
|
|
702 }
|
|
703 else
|
|
704 (void) puts("Don't know which line you mean.");
|
|
705 break;
|
|
706 case 'm':
|
|
707 temp = sscanf(buf+1,"%d",&templine);
|
|
708 if(templine && temp == 1)
|
|
709 {
|
|
710 printf("Monitor set at line %d.\n",templine);
|
|
711 monitors[nmonitors++]=templine;
|
|
712 if(nmonitors>=80) ick_lose(IE811,emitlineno,(const char*)NULL);
|
|
713 }
|
|
714 else
|
|
715 (void) puts("Don't know which line you mean.");
|
|
716 break;
|
|
717 case 'f':
|
|
718 temp = sscanf(buf+1,"%d",&templine);
|
|
719 if(templine && temp == 1)
|
|
720 {
|
|
721 printf("All monitors removed from line %d.\n",templine);
|
|
722 i=nmonitors;
|
|
723 while(i--) if(templine==monitors[i])
|
|
724 {
|
|
725 memmove(monitors+i,monitors+i+1,sizeof(int)*(nmonitors-i));
|
|
726 nmonitors--;
|
|
727 }
|
|
728 }
|
|
729 else
|
|
730 (void) puts("Don't know which line you mean.");
|
|
731 break;
|
|
732 case 's':
|
|
733 singlestep=1;
|
|
734 writelines=1;
|
|
735 keeplooping=0;
|
|
736 break;
|
|
737 case 't':
|
|
738 singlestep=0;
|
|
739 writelines=1;
|
|
740 keeplooping=0;
|
|
741 break;
|
|
742 case 'u':
|
|
743 temp = sscanf(buf+1,"%d",&templine);
|
|
744 if(templine && temp == 1)
|
|
745 {
|
|
746 breakpoints[0]=templine;
|
|
747 singlestep=0;
|
|
748 writelines=0;
|
|
749 keeplooping=0;
|
|
750 }
|
|
751 else
|
|
752 (void) puts("Don't know which line you mean.");
|
|
753 break;
|
|
754 case 'e':
|
|
755 temp = sscanf(buf+1,"%d",&templine);
|
|
756 if(!templine || temp != 1)
|
|
757 {
|
|
758 (void) puts("Don't know which line you mean.");
|
|
759 break;
|
|
760 }
|
|
761 tempcmd=-1;
|
|
762 temp=0;
|
|
763 i=0;
|
|
764 while(++tempcmd<yukcommands)
|
|
765 {
|
|
766 if(lineofaboff[tempcmd]==templine)
|
|
767 {
|
|
768 if(!yukexplain[tempcmd]) ++i;
|
|
769 else
|
|
770 {
|
|
771 printf("C%d: Expression is %s\n",++i,yukexplain[tempcmd]);
|
|
772 temp++;
|
|
773 }
|
|
774 }
|
|
775 }
|
|
776 if(!temp) (void) puts("No expressions on that line.");
|
|
777 break;
|
|
778 case 'l':
|
|
779 temp = sscanf(buf+1,"%d",&templine);
|
|
780 if(!templine || temp != 1)
|
|
781 {
|
|
782 (void) puts("Don't know which line you mean.");
|
|
783 break;
|
|
784 }
|
|
785 /*@fallthrough@*/
|
|
786 case 'h':
|
|
787 if(!templine) templine=lineofaboff[aboff];
|
|
788 templine-=10;
|
|
789 if(templine+21>=yuklines) templine=yuklines-22;
|
|
790 if(templine<1) templine=1;
|
|
791 i=templine;
|
|
792 while(i<templine+21&&i<yuklines)
|
|
793 {
|
|
794 buf[6]='\0';
|
|
795 buf[0]=buf[1]=buf[2]=buf[3]=buf[4]=buf[5]=' ';
|
|
796 tempcmd=-1;
|
|
797 temp=0;
|
|
798 while(++tempcmd<yukcommands)
|
|
799 {
|
|
800 if(lineofaboff[tempcmd]==i)
|
|
801 {
|
|
802 if(ick_abstained[tempcmd]>9) buf[temp++]='!';
|
|
803 else buf[temp++]='0'+(char)ick_abstained[tempcmd];
|
|
804 if(temp==6) break;
|
|
805 }
|
|
806 }
|
|
807 printf("(A%s)%5d:\t%s\n",buf,i,textlines[i]);
|
|
808 i++;
|
|
809 }
|
|
810 break;
|
|
811 case '*':
|
|
812 tempcharp=ick_findandtestopen("COPYING.txt",globalargv[1],
|
|
813 "r",globalargv[2]);
|
|
814 if(tempcharp != NULL)
|
|
815 {
|
|
816 #ifndef HAVE_SNPRINTF
|
|
817 (void) sprintf(copyloc,"more < %s",tempcharp);
|
|
818 #else
|
|
819 (void) snprintf(copyloc,sizeof copyloc,"more < %s",tempcharp);
|
|
820 #endif
|
|
821 (void) system(copyloc); /* display the GNU GPL copyright */
|
|
822 }
|
|
823 else
|
|
824 (void) puts("Couldn't find license file. See the file COPYING.txt that\n"
|
|
825 "came with your C-INTERCAL distribution.");
|
|
826 break;
|
|
827 default:
|
|
828 (void) puts("Not sure what you mean. Try typing ?<RET>.");
|
|
829 break;
|
|
830 }
|
|
831 } while(keeplooping);
|
|
832 }
|
|
833 }
|
|
834 firstrun=0;
|
|
835 }
|