Última actividad 1 month ago

basic.c Sin formato
1
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <ctype.h>
6#include <math.h>
7#include <time.h>
8
9/* ================================================================
10 MS BASIC-80 Full C Port
11 Ported from Z80 assembly disassembly (FNCTAB/WORDS/EVAL/NEWSTT)
12 ================================================================ */
13
14#define MAXLINES 2000
15#define MAXBUF 512
16#define MAXVARS 512
17#define MAXARR 64
18#define MAXARRLEN 1024
19#define MAXSTACK 256
20#define MAXDATA 2048
21#define MAXSTR 256
22#define MAXFILES 8
23
24/* ---- Program storage ---- */
25typedef struct { int num; char text[MAXBUF]; } Line;
26Line program[MAXLINES];
27int prog_cnt = 0;
28
29/* ---- Variable types ---- */
30#define TYPE_INT 2
31#define TYPE_SNG 4
32#define TYPE_STR 3
33
34typedef struct {
35 char name[MAXSTR];
36 int type;
37 double num;
38 char str[MAXSTR];
39} Var;
40
41Var variables[MAXVARS];
42int var_cnt = 0;
43
44/* ---- Arrays ---- */
45typedef struct {
46 char name[MAXSTR];
47 int type;
48 int dims[4];
49 int ndims;
50 int base; /* OPTION BASE */
51 double *nums;
52 char (*strs)[MAXSTR];
53} Arr;
54
55Arr arrays[MAXARR];
56int arr_cnt = 0;
57int option_base = 0;
58
59/* ---- DATA pool (FDTLP) ---- */
60char data_pool[MAXDATA][MAXSTR];
61int data_count = 0;
62int data_pos = 0;
63
64/* ---- GOSUB stack (BAKSTK) ---- */
65int call_stack[MAXSTACK];
66int call_sp = 0;
67
68/* ---- FOR/NEXT stack ---- */
69typedef struct {
70 int var_idx;
71 double limit;
72 double step;
73 int line_idx;
74} ForEntry;
75ForEntry for_stack[MAXSTACK];
76int for_sp = 0;
77
78/* ---- WHILE/WEND stack ---- */
79int while_stack[MAXSTACK];
80int while_sp = 0;
81
82/* ---- ON ERROR ---- */
83int on_error_line = -1;
84int err_number = 0;
85int err_line = 0;
86
87/* ---- Globals ---- */
88double fac;
89int cur_line_idx;
90int running;
91int tron_flag = 0;
92int null_count = 0; /* NULL() */
93char last_inkey = 0;
94int cont_idx = -1; /* for CONT */
95
96/* ---- File handles (OPEN/CLOSE) ---- */
97FILE *file_handles[MAXFILES];
98char file_modes[MAXFILES]; /* 'I','O','A','R' */
99
100/* ============================================================
101 UTILS
102 ============================================================ */
103static void skip_ws(char **p) { while (**p == ' ' || **p == '\t') (*p)++; }
104
105static char *strupr_local(char *s) {
106 for (char *p = s; *p; p++) *p = toupper(*p);
107 return s;
108}
109
110/* ============================================================
111 LINE MANAGEMENT (SRCHLN / INSERT)
112 ============================================================ */
113int find_line(int num) {
114 for (int i = 0; i < prog_cnt; i++)
115 if (program[i].num == num) return i;
116 return -1;
117}
118
119void insert_line(int num, char *text) {
120 for (int i = 0; i < prog_cnt; i++) {
121 if (program[i].num == num) {
122 if (!text || !*text) {
123 for (int j = i; j < prog_cnt-1; j++) program[j] = program[j+1];
124 prog_cnt--; return;
125 }
126 strncpy(program[i].text, text, MAXBUF-1);
127 return;
128 }
129 }
130 if (!text || !*text) return;
131 program[prog_cnt].num = num;
132 strncpy(program[prog_cnt].text, text, MAXBUF-1);
133 prog_cnt++;
134 for (int i = prog_cnt-1; i > 0 && program[i].num < program[i-1].num; i--) {
135 Line tmp = program[i]; program[i] = program[i-1]; program[i-1] = tmp;
136 }
137}
138
139/* ============================================================
140 VARIABLE MANAGEMENT (PTRGET / GETVAR)
141 ============================================================ */
142int ptrget(const char *name) {
143 for (int i = 0; i < var_cnt; i++)
144 if (strcasecmp(name, variables[i].name) == 0) return i;
145 strncpy(variables[var_cnt].name, name, MAXSTR-1);
146 variables[var_cnt].type = TYPE_SNG;
147 variables[var_cnt].num = 0;
148 variables[var_cnt].str[0] = 0;
149 return var_cnt++;
150}
151
152int is_str_var(const char *name) {
153 return name[strlen(name)-1] == '$';
154}
155
156/* ============================================================
157 ARRAY MANAGEMENT (DIM / SBSCPT)
158 ============================================================ */
159Arr *find_arr(const char *name) {
160 for (int i = 0; i < arr_cnt; i++)
161 if (strcasecmp(name, arrays[i].name) == 0) return &arrays[i];
162 return NULL;
163}
164
165Arr *make_arr(const char *name, int *dims, int ndims) {
166 Arr *a = &arrays[arr_cnt++];
167 strncpy(a->name, name, MAXSTR-1);
168 a->ndims = ndims; a->base = option_base;
169 int total = 1;
170 for (int i = 0; i < ndims; i++) { a->dims[i] = dims[i]+1; total *= (dims[i]+1); }
171 a->type = is_str_var(name) ? TYPE_STR : TYPE_SNG;
172 if (a->type == TYPE_STR) a->strs = calloc(total, sizeof(*a->strs));
173 else a->nums = calloc(total, sizeof(double));
174 return a;
175}
176
177/* ============================================================
178 EXPRESSION PARSER (EVAL / OPRND / PRITAB)
179 Full: + - * / ^ unary- relational AND OR NOT
180 Functions: ABS SQR RND SIN COS TAN ATN LOG EXP INT FIX SGN
181 LEN VAL ASC CHR$ STR$ LEFT$ RIGHT$ MID$ SPACE$
182 HEX$ OCT$ CINT CSNG CDBL INSTR STRING$ FRE POS
183 ============================================================ */
184double expr(char **p);
185void run_from(int idx);
186void execute_stmt(char *p);
187void basic_error(int code, char *msg);
188
189static char str_result[MAXSTR]; /* scratch for string functions */
190
191static double parse_string_fn(char **p) {
192 /* Returns 0, result in str_result */
193 str_result[0] = 0;
194 return 0;
195}
196
197/* Parse a string literal or string expression, result -> str_result */
198static void parse_str_expr(char **p, char *out) {
199 skip_ws(p);
200 if (**p == '"') {
201 (*p)++; int i=0;
202 while (**p && **p != '"') out[i++] = *(*p)++;
203 out[i]=0; if(**p=='"')(*p)++;
204 } else if (isalpha(**p)) {
205 char name[MAXSTR]={0}; char *q=*p;
206 int i=0;
207 while (isalnum(**p) || **p=='$' || **p=='_') name[i++]=*(*p)++;
208 name[i]=0;
209 if (is_str_var(name)) {
210 strcpy(out, variables[ptrget(name)].str);
211 } else { *p=q; sprintf(out,"%g",expr(p)); }
212 } else { sprintf(out,"%g",expr(p)); }
213}
214
215static double primary(char **p) {
216 skip_ws(p);
217 double x = 0;
218 if (**p == '-') { (*p)++; return -primary(p); }
219 if (**p == '+') { (*p)++; return primary(p); }
220 if (**p == '(') { (*p)++; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return x; }
221
222 /* Functions */
223 #define MATHFN1(nm,fn) if(strncasecmp(*p,nm"(",strlen(nm)+1)==0){*p+=strlen(nm)+1;x=fn(expr(p));skip_ws(p);if(**p==')')(*p)++;return x;}
224 MATHFN1("ABS",fabs) MATHFN1("SQR",sqrt) MATHFN1("SIN",sin)
225 MATHFN1("COS",cos) MATHFN1("TAN",tan) MATHFN1("ATN",atan)
226 MATHFN1("LOG",log) MATHFN1("EXP",exp) MATHFN1("INT",floor)
227 MATHFN1("FIX",trunc)
228 #undef MATHFN1
229
230 if (strncasecmp(*p,"SGN(",4)==0){ *p+=4; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return (x>0)-(x<0); }
231 if (strncasecmp(*p,"RND(",4)==0){ *p+=4; double a=expr(p); skip_ws(p); if(**p==')')(*p)++; (void)a; return (double)rand()/RAND_MAX; }
232 if (strncasecmp(*p,"CINT(",5)==0){ *p+=5; x=round(expr(p)); skip_ws(p); if(**p==')')(*p)++; return (int)x; }
233 if (strncasecmp(*p,"CSNG(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return (float)x; }
234 if (strncasecmp(*p,"CDBL(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return x; }
235 if (strncasecmp(*p,"FRE(",4)==0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return 60000; }
236 if (strncasecmp(*p,"POS(",4)==0) { *p+=4; expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; }
237 if (strncasecmp(*p,"LEN(",4)==0) { *p+=4; char tmp[MAXSTR]; parse_str_expr(p,tmp); skip_ws(p); if(**p==')')(*p)++; return strlen(tmp); }
238 if (strncasecmp(*p,"ASC(",4)==0) { *p+=4; char tmp[MAXSTR]; parse_str_expr(p,tmp); skip_ws(p); if(**p==')')(*p)++; return tmp[0]; }
239 if (strncasecmp(*p,"VAL(",4)==0) { *p+=4; char tmp[MAXSTR]; parse_str_expr(p,tmp); skip_ws(p); if(**p==')')(*p)++; return atof(tmp); }
240 if (strncasecmp(*p,"PEEK(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } /* stub */
241 if (strncasecmp(*p,"INP(",4)==0) { *p+=4; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; } /* stub */
242 if (strncasecmp(*p,"LPOS(",5)==0){ *p+=5; x=expr(p); skip_ws(p); if(**p==')')(*p)++; return 0; }
243
244 if (strncasecmp(*p,"INSTR(",6)==0) {
245 *p+=6; double start=1; char h[MAXSTR],n[MAXSTR];
246 /* INSTR([start,] hay, needle) */
247 char *save=*p; double t=expr(p); skip_ws(p);
248 if (**p==',') { start=t; (*p)++; parse_str_expr(p,h); }
249 else { *p=save; parse_str_expr(p,h); }
250 skip_ws(p); if(**p==',')(*p)++;
251 parse_str_expr(p,n);
252 skip_ws(p); if(**p==')')(*p)++;
253 char *found=strstr(h+(int)start-1,n);
254 return found ? (found-h+1) : 0;
255 }
256
257 /* NOT */
258 if (strncasecmp(*p,"NOT",3)==0 && !isalnum((*p)[3])) { *p+=3; return ~(int)primary(p); }
259
260 /* Number literal */
261 if (isdigit(**p) || **p=='.') {
262 char *end; x=strtod(*p,&end); *p=end; return x;
263 }
264
265 /* Hex &H / Octal &O */
266 if (**p=='&') {
267 (*p)++;
268 if (toupper(**p)=='H') { (*p)++; x=(double)strtol(*p,p,16); return x; }
269 if (toupper(**p)=='O') { (*p)++; x=(double)strtol(*p,p,8); return x; }
270 x=(double)strtol(*p,p,8); return x;
271 }
272
273 /* Variable or array element */
274 if (isalpha(**p)) {
275 char name[MAXSTR]={0}; int i=0;
276 while (isalnum(**p)||**p=='$'||**p=='_') name[i++]=*(*p)++;
277 name[i]=0;
278 skip_ws(p);
279 /* Array element */
280 if (**p=='(') {
281 (*p)++;
282 int idx[4]={0}; int nd=0;
283 while (**p && **p!=')') {
284 idx[nd++]=(int)expr(p); skip_ws(p);
285 if(**p==',')(*p)++;
286 }
287 if(**p==')')(*p)++;
288 Arr *a = find_arr(name);
289 if (!a) { /* auto-dim 0..10 */ int d=10; a=make_arr(name,&d,1); }
290 int flat=0;
291 if (nd>=1) flat=idx[0]-option_base;
292 if (nd>=2) flat=flat*(a->dims[1])+idx[1]-option_base;
293 if (flat<0) flat=0;
294 if (a->type==TYPE_STR) { /* str arrays not returned as number */ return 0; }
295 else return a->nums[flat];
296 }
297 return variables[ptrget(name)].num;
298 }
299 return 0;
300}
301
302static double power_e(char **p) {
303 double x=primary(p); skip_ws(p);
304 if (**p=='^') { (*p)++; x=pow(x,primary(p)); }
305 return x;
306}
307static double term(char **p) {
308 double x=power_e(p); skip_ws(p);
309 while(**p=='*'||**p=='/') { char op=*(*p)++; double y=power_e(p); x=(op=='*')?x*y:(y?x/y:(basic_error(11,"Division by zero"),1.0)); skip_ws(p); }
310 return x;
311}
312static double add_sub(char **p) {
313 double x=term(p); skip_ws(p);
314 while(**p=='+'||**p=='-') { char op=*(*p)++; double y=term(p); x=(op=='+')?x+y:x-y; skip_ws(p); }
315 return x;
316}
317static double relational(char **p) {
318 double x=add_sub(p); skip_ws(p);
319 if (strncmp(*p,"<>",2)==0){*p+=2;return x!=add_sub(p);}
320 if (strncmp(*p,"<=",2)==0){*p+=2;return x<=add_sub(p);}
321 if (strncmp(*p,">=",2)==0){*p+=2;return x>=add_sub(p);}
322 if (**p=='<'){(*p)++;return x<add_sub(p);}
323 if (**p=='>'){(*p)++;return x>add_sub(p);}
324 if (**p=='='){(*p)++;return x==add_sub(p);}
325 return x;
326}
327static double and_or(char **p) {
328 double x=relational(p); skip_ws(p);
329 while(1){
330 if (strncasecmp(*p,"AND",3)==0&&!isalnum((*p)[3])){ *p+=3; x=(int)x&(int)relational(p); }
331 else if(strncasecmp(*p,"OR",2)==0&&!isalnum((*p)[2])){ *p+=2; x=(int)x|(int)relational(p); }
332 else if(strncasecmp(*p,"XOR",3)==0&&!isalnum((*p)[3])){ *p+=3; x=(int)x^(int)relational(p); }
333 else break;
334 skip_ws(p);
335 }
336 return x;
337}
338double expr(char **p) { return fac=and_or(p); }
339
340/* String expression: result in out */
341static void str_expr(char **p, char *out) {
342 skip_ws(p);
343 /* CHR$ */
344 if (strncasecmp(*p,"CHR$(",5)==0){ *p+=5; int c=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; out[0]=c; out[1]=0; return; }
345 /* STR$ */
346 if (strncasecmp(*p,"STR$(",5)==0){ *p+=5; double v=expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%g",v); return; }
347 /* LEFT$ */
348 if (strncasecmp(*p,"LEFT$(",6)==0){ *p+=6; char s[MAXSTR]; parse_str_expr(p,s); skip_ws(p); if(**p==',')(*p)++; int n=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; strncpy(out,s,n); out[n]=0; return; }
349 /* RIGHT$ */
350 if (strncasecmp(*p,"RIGHT$(",7)==0){ *p+=7; char s[MAXSTR]; parse_str_expr(p,s); skip_ws(p); if(**p==',')(*p)++; int n=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; int l=strlen(s); int st=l-n; if(st<0)st=0; strcpy(out,s+st); return; }
351 /* MID$ */
352 if (strncasecmp(*p,"MID$(",5)==0){ *p+=5; char s[MAXSTR]; parse_str_expr(p,s); skip_ws(p); if(**p==',')(*p)++; int st=(int)expr(p)-1; skip_ws(p); int n=strlen(s); if(**p==','){ (*p)++; n=(int)expr(p); } skip_ws(p); if(**p==')')(*p)++; if(st<0)st=0; strncpy(out,s+st,n); out[n]=0; return; }
353 /* SPACE$ */
354 if (strncasecmp(*p,"SPACE$(",7)==0){ *p+=7; int n=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; memset(out,' ',n); out[n]=0; return; }
355 /* STRING$ */
356 if (strncasecmp(*p,"STRING$(",8)==0){ *p+=8; int n=(int)expr(p); skip_ws(p); if(**p==',')(*p)++; char tmp[MAXSTR]; if(**p=='"'){ parse_str_expr(p,tmp); memset(out,tmp[0],n); } else { int c=(int)expr(p); memset(out,c,n); } out[n]=0; skip_ws(p); if(**p==')')(*p)++; return; }
357 /* HEX$ */
358 if (strncasecmp(*p,"HEX$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%X",v); return; }
359 /* OCT$ */
360 if (strncasecmp(*p,"OCT$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; sprintf(out,"%o",v); return; }
361 /* MKI$ MKS$ MKD$ */
362 if (strncasecmp(*p,"MKI$(",5)==0){ *p+=5; int v=(int)expr(p); skip_ws(p); if(**p==')')(*p)++; memcpy(out,&v,2); out[2]=0; return; }
363 if (strncasecmp(*p,"MKS$(",5)==0){ *p+=5; float v=(float)expr(p); skip_ws(p); if(**p==')')(*p)++; memcpy(out,&v,4); out[4]=0; return; }
364 if (strncasecmp(*p,"MKD$(",5)==0){ *p+=5; double v=expr(p); skip_ws(p); if(**p==')')(*p)++; memcpy(out,&v,8); out[8]=0; return; }
365 /* INKEY$ */
366 if (strncasecmp(*p,"INKEY$",6)==0){ *p+=6; out[0]=last_inkey; out[1]=0; last_inkey=0; return; }
367 /* Fallthrough: parse as string var or literal */
368 parse_str_expr(p, out);
369}
370
371/* ============================================================
372 COLLECT DATA (FDTLP)
373 ============================================================ */
374void collect_data() {
375 data_count=0; data_pos=0;
376 for (int i=0;i<prog_cnt;i++) {
377 char *p=program[i].text;
378 while (*p) {
379 if (strncasecmp(p,"DATA",4)==0) {
380 p+=4;
381 while (*p && *p!='\n') {
382 skip_ws(&p);
383 if (*p==',') { p++; continue; }
384 if (!*p) break;
385 char *dst=data_pool[data_count];
386 if (*p=='"') { p++; int i2=0; while(*p&&*p!='"') dst[i2++]=*p++; dst[i2]=0; if(*p=='"')p++; }
387 else { int i2=0; while(*p&&*p!=','&&*p!='\n') dst[i2++]=*p++; dst[i2]=0; }
388 data_count++;
389 skip_ws(&p);
390 }
391 break;
392 }
393 p++;
394 }
395 }
396}
397
398/* ============================================================
399 ERROR HANDLING (ERRORREPORT)
400 ============================================================ */
401void basic_error(int code, char *msg) {
402 fprintf(stderr,"?%s error in %d\n", msg, (prog_cnt>0 && cur_line_idx<prog_cnt)?program[cur_line_idx].num:0);
403 err_number=code;
404 if (on_error_line>=0) { cur_line_idx=find_line(on_error_line); running=2; return; }
405 running=0;
406}
407
408/* ============================================================
409 PRINT USING (USING / PUFOUT) - basic implementation
410 ============================================================ */
411static void print_using(char *fmt, double val) {
412 /* Handle # . , + - $ simple format chars */
413 char buf[MAXBUF]; int w=0,d=0;
414 for (char *f=fmt;*f;f++) if(*f=='#') w++; else if(*f=='.') { for(char *g=f+1;*g=='#';g++) d++; break; }
415 if (d>0) printf("%*.*f",w,d,val);
416 else printf("%*g",w,val);
417}
418
419/* ============================================================
420 WRITE statement (csv output)
421 ============================================================ */
422static void do_write(char **p, FILE *fp) {
423 while (**p) {
424 skip_ws(p);
425 if (**p=='"' || (isalpha(**p) && is_str_var(*p))) {
426 char s[MAXSTR]; str_expr(p,s);
427 fprintf(fp,"\"%s\"",s);
428 } else {
429 fprintf(fp,"%g",expr(p));
430 }
431 skip_ws(p);
432 if (**p==',') { fprintf(fp,","); (*p)++; }
433 else break;
434 }
435 fprintf(fp,"\n");
436}
437
438/* ============================================================
439 EXECUTE ONE STATEMENT (NEWSTT / EXEC / FNCTAB)
440 ============================================================ */
441void execute_stmt(char *p) {
442 skip_ws(&p);
443 if (!*p) return;
444
445 /* REM / ' */
446 if (strncasecmp(p,"REM",3)==0||*p=='\'') return;
447
448 /* PRINT / LPRINT */
449 if (strncasecmp(p,"LPRINT",6)==0||strncasecmp(p,"PRINT",5)==0) {
450 FILE *fp=stdout; int is_lp=(strncasecmp(p,"LPRINT",6)==0);
451 p += is_lp?6:5;
452 /* PRINT USING */
453 if (strncasecmp(p," USING",6)==0||strncasecmp(p,"USING",5)==0) {
454 if(*p==' ')p++; p+=5; skip_ws(&p);
455 char fmt[MAXBUF]; str_expr(&p,fmt);
456 skip_ws(&p); if(*p==';')p++;
457 double v=expr(&p);
458 print_using(fmt,v);
459 fprintf(fp,"\n"); return;
460 }
461 int newline=1;
462 while (*p) {
463 skip_ws(&p);
464 if (!*p) break;
465 if (*p==';') { p++; newline=0; continue; }
466 if (*p==',') { fprintf(fp,"\t"); p++; newline=0; continue; }
467 /* TAB( */
468 if (strncasecmp(p,"TAB(",4)==0) { p+=4; int n=(int)expr(&p); skip_ws(&p); if(*p==')')p++; for(int i=0;i<n;i++) fputc(' ',fp); newline=0; continue; }
469 /* SPC( */
470 if (strncasecmp(p,"SPC(",4)==0) { p+=4; int n=(int)expr(&p); skip_ws(&p); if(*p==')')p++; for(int i=0;i<n;i++) fputc(' ',fp); newline=0; continue; }
471 newline=1;
472 /* Detect string-producing tokens (CHR$, STR$, MID$ etc.) before numeric eval */
473 int is_str_tok = (*p=='"') ||
474 (isalpha(*p) && is_str_var(p)) ||
475 strncasecmp(p,"CHR$(",5)==0 ||
476 strncasecmp(p,"STR$(",5)==0 ||
477 strncasecmp(p,"LEFT$(",6)==0 ||
478 strncasecmp(p,"RIGHT$(",7)==0 ||
479 strncasecmp(p,"MID$(",5)==0 ||
480 strncasecmp(p,"SPACE$(",7)==0 ||
481 strncasecmp(p,"STRING$(",8)==0 ||
482 strncasecmp(p,"HEX$(",5)==0 ||
483 strncasecmp(p,"OCT$(",5)==0 ||
484 strncasecmp(p,"MKI$(",5)==0 ||
485 strncasecmp(p,"MKS$(",5)==0 ||
486 strncasecmp(p,"MKD$(",5)==0 ||
487 strncasecmp(p,"INKEY$",6)==0;
488 if (is_str_tok) {
489 char s[MAXSTR]; str_expr(&p,s); fprintf(fp,"%s",s);
490 } else {
491 double v=expr(&p);
492 if (v==(long)v) fprintf(fp,"%.0f",v); else fprintf(fp,"%g",v);
493 fputc(' ',fp);
494 }
495 skip_ws(&p);
496 }
497 if (newline) fprintf(fp,"\n");
498 return;
499 }
500
501 /* INPUT / LINE INPUT */
502 if (strncasecmp(p,"LINE INPUT",10)==0) {
503 p+=10; skip_ws(&p); if(*p=='#'){p++; expr(&p); skip_ws(&p); if(*p==',')p++;} /* file# stub */
504 char name[MAXSTR]={0}; int i=0; skip_ws(&p);
505 while(isalnum(*p)||*p=='$') name[i++]=*p++;
506 name[i]=0;
507 char line[MAXBUF]; if(!fgets(line,MAXBUF,stdin)) line[0]=0;
508 line[strcspn(line,"\n")]=0;
509 int vi=ptrget(name); strncpy(variables[vi].str,line,MAXSTR-1); variables[vi].type=TYPE_STR;
510 return;
511 }
512 if (strncasecmp(p,"INPUT",5)==0) {
513 p+=5; skip_ws(&p);
514 int file_no=-1;
515 if (*p=='#') { p++; file_no=(int)expr(&p); skip_ws(&p); if(*p==',')p++; }
516 char prompt[MAXBUF]="? ";
517 if (*p=='"') { p++; int i=0; while(*p&&*p!='"') prompt[i++]=*p++; prompt[i]=0; if(*p=='"')p++; if(*p==';'||*p==',')p++; strcat(prompt,"? "); }
518 while (*p) {
519 skip_ws(&p);
520 if (*p==',') { p++; continue; }
521 char name[MAXSTR]={0}; int i=0;
522 while(isalnum(*p)||*p=='$') name[i++]=*p++;
523 name[i]=0;
524 if (!name[0]) break;
525 printf("%s",prompt); prompt[0]=0; strcpy(prompt,"");
526 char ibuf[MAXBUF];
527 if (file_no>=0 && file_handles[file_no]) { fgets(ibuf,MAXBUF,file_handles[file_no]); }
528 else { fgets(ibuf,MAXBUF,stdin); }
529 ibuf[strcspn(ibuf,"\n")]=0;
530 int vi=ptrget(name);
531 if (is_str_var(name)) { strncpy(variables[vi].str,ibuf,MAXSTR-1); variables[vi].type=TYPE_STR; }
532 else { variables[vi].num=atof(ibuf); variables[vi].type=TYPE_SNG; }
533 skip_ws(&p);
534 }
535 return;
536 }
537
538 /* LET / assignment */
539 if (strncasecmp(p,"LET",3)==0&&!isalnum(p[3])) p+=3;
540 if (isalpha(*p)) {
541 char name[MAXSTR]={0}; char *save=p; int i=0;
542 while(isalnum(*p)||*p=='$') name[i++]=*p++;
543 name[i]=0;
544 skip_ws(&p);
545 /* Array assignment */
546 if (*p=='(') {
547 (*p==0); p++;
548 int idx[4]={0}; int nd=0;
549 while(*p&&*p!=')') { idx[nd++]=(int)expr(&p); skip_ws(&p); if(*p==',')p++; }
550 if(*p==')')p++;
551 skip_ws(&p); if(*p=='=')p++;
552 Arr *a=find_arr(name);
553 if(!a){int d=10;a=make_arr(name,&d,1);}
554 int flat=idx[0]-option_base;
555 if (nd>=2) flat=flat*(a->dims[1])+idx[1]-option_base;
556 if (flat<0) flat=0;
557 if (a->type==TYPE_STR){char s[MAXSTR];str_expr(&p,s);strncpy(a->strs[flat],s,MAXSTR-1);}
558 else a->nums[flat]=expr(&p);
559 return;
560 }
561 if (*p=='=') {
562 p++;
563 int vi=ptrget(name);
564 if (is_str_var(name)) {
565 char s[MAXSTR]; str_expr(&p,s);
566 strncpy(variables[vi].str,s,MAXSTR-1);
567 variables[vi].type=TYPE_STR;
568 } else {
569 variables[vi].num=expr(&p);
570 variables[vi].type=TYPE_SNG;
571 }
572 return;
573 }
574 p=save;
575 }
576
577 /* IF / THEN / ELSE */
578 if (strncasecmp(p,"IF",2)==0&&!isalnum(p[2])) {
579 p+=2; skip_ws(&p);
580 double cond=expr(&p);
581 skip_ws(&p);
582 if (strncasecmp(p,"THEN",4)==0&&!isalnum(p[4])) p+=4;
583 skip_ws(&p);
584 /* THEN line_number */
585 if (isdigit(*p)) {
586 int tgt=(int)strtol(p,&p,10);
587 if (cond) { int idx=find_line(tgt); if(idx>=0){cur_line_idx=idx;running=2;} }
588 else {
589 /* find ELSE */
590 skip_ws(&p);
591 if (strncasecmp(p,"ELSE",4)==0) { p+=4; skip_ws(&p);
592 if(isdigit(*p)){ int t=(int)strtol(p,&p,10); int idx=find_line(t); if(idx>=0){cur_line_idx=idx;running=2;}}
593 else execute_stmt(p);
594 }
595 }
596 return;
597 }
598 char *else_p=NULL;
599 /* find ELSE in remainder */
600 char *scan=p; int depth=0;
601 while(*scan){
602 if(*scan=='(')depth++; else if(*scan==')')depth--;
603 if(depth==0&&strncasecmp(scan,"ELSE",4)==0&&!isalnum(scan[4])&&!isalnum(scan[-1])){ else_p=scan; break; }
604 scan++;
605 }
606 if (cond) { char tmp[MAXBUF]; if(else_p){int len=else_p-p;strncpy(tmp,p,len);tmp[len]=0;execute_stmt(tmp);}else execute_stmt(p); }
607 else if (else_p) { execute_stmt(else_p+4); }
608 return;
609 }
610
611 /* GOTO */
612 if (strncasecmp(p,"GOTO",4)==0&&!isalnum(p[4])) {
613 p+=4; int tgt=(int)expr(&p);
614 int idx=find_line(tgt);
615 if(idx<0){basic_error(8,"Undefined line");return;}
616 cur_line_idx=idx; running=2; return;
617 }
618
619 /* GOSUB */
620 if (strncasecmp(p,"GOSUB",5)==0&&!isalnum(p[5])) {
621 p+=5; int tgt=(int)expr(&p);
622 int idx=find_line(tgt);
623 if(idx<0){basic_error(8,"Undefined line");return;}
624 call_stack[call_sp++]=cur_line_idx+1;
625 cur_line_idx=idx; running=2; return;
626 }
627
628 /* RETURN */
629 if (strncasecmp(p,"RETURN",6)==0&&!isalnum(p[6])) {
630 if(call_sp<=0){basic_error(3,"RETURN without GOSUB");return;}
631 cur_line_idx=call_stack[--call_sp]; running=2; return;
632 }
633
634 /* ON GOTO / ON GOSUB / ON ERROR */
635 if (strncasecmp(p,"ON",2)==0&&!isalnum(p[2])) {
636 p+=2; skip_ws(&p);
637 if (strncasecmp(p,"ERROR",5)==0) {
638 p+=5; skip_ws(&p);
639 if (strncasecmp(p,"GOTO",4)==0){p+=4; on_error_line=(int)expr(&p);}
640 else if(strncasecmp(p,"RESUME",6)==0){p+=6;skip_ws(&p);if(strncasecmp(p,"NEXT",4)==0)p+=4;}
641 return;
642 }
643 double n=expr(&p); skip_ws(&p);
644 int is_sub=(strncasecmp(p,"GOSUB",5)==0);
645 p += is_sub?5:4;
646 int targets[64]; int tc=0;
647 while(*p){skip_ws(&p);if(!isdigit(*p))break;targets[tc++]=(int)strtol(p,&p,10);skip_ws(&p);if(*p==',')p++;}
648 int ni=(int)n-1;
649 if(ni>=0&&ni<tc){
650 int idx=find_line(targets[ni]);
651 if(idx>=0){
652 if(is_sub){call_stack[call_sp++]=cur_line_idx+1;}
653 cur_line_idx=idx; running=2;
654 }
655 }
656 return;
657 }
658
659 /* FOR */
660 if (strncasecmp(p,"FOR",3)==0&&!isalnum(p[3])) {
661 p+=3; skip_ws(&p);
662 char name[MAXSTR]={0}; int i=0;
663 while(isalnum(*p)||*p=='$') name[i++]=*p++;
664 name[i]=0;
665 skip_ws(&p); if(*p=='=')p++;
666 double start=expr(&p);
667 skip_ws(&p); if(strncasecmp(p,"TO",2)==0&&!isalnum(p[2]))p+=2;
668 double limit=expr(&p);
669 double step=1.0;
670 skip_ws(&p); if(strncasecmp(p,"STEP",4)==0&&!isalnum(p[4])){p+=4;step=expr(&p);}
671 int vi=ptrget(name); variables[vi].num=start; variables[vi].type=TYPE_SNG;
672 for_stack[for_sp].var_idx=vi;
673 for_stack[for_sp].limit=limit;
674 for_stack[for_sp].step=step;
675 for_stack[for_sp].line_idx=cur_line_idx;
676 for_sp++;
677 return;
678 }
679
680 /* NEXT */
681 if (strncasecmp(p,"NEXT",4)==0&&!isalnum(p[4])) {
682 if(for_sp<=0){basic_error(1,"NEXT without FOR");return;}
683 ForEntry *fe=&for_stack[for_sp-1];
684 variables[fe->var_idx].num+=fe->step;
685 if((fe->step>0&&variables[fe->var_idx].num<=fe->limit)||
686 (fe->step<0&&variables[fe->var_idx].num>=fe->limit)){
687 cur_line_idx=fe->line_idx; running=2;
688 } else for_sp--;
689 return;
690 }
691
692 /* WHILE */
693 if (strncasecmp(p,"WHILE",5)==0&&!isalnum(p[5])) {
694 p+=5;
695 if (!expr(&p)) {
696 /* skip to WEND */
697 int depth=1;
698 while(++cur_line_idx<prog_cnt&&depth>0){
699 if(strstr(program[cur_line_idx].text,"WHILE"))depth++;
700 if(strstr(program[cur_line_idx].text,"WEND"))depth--;
701 }
702 } else {
703 while_stack[while_sp++]=cur_line_idx;
704 }
705 return;
706 }
707
708 /* WEND */
709 if (strncasecmp(p,"WEND",4)==0&&!isalnum(p[4])) {
710 if(while_sp<=0){basic_error(23,"WEND without WHILE");return;}
711 cur_line_idx=while_stack[--while_sp]-1;
712 running=2; return;
713 }
714
715 /* DATA */
716 if (strncasecmp(p,"DATA",4)==0) return;
717
718 /* READ */
719 if (strncasecmp(p,"READ",4)==0&&!isalnum(p[4])) {
720 p+=4;
721 while(*p){
722 skip_ws(&p); if(*p==',')p++; skip_ws(&p); if(!*p)break;
723 char name[MAXSTR]={0}; int i=0;
724 while(isalnum(*p)||*p=='$') name[i++]=*p++;
725 name[i]=0; if(!name[0])break;
726 if(data_pos>=data_count){basic_error(4,"Out of DATA");return;}
727 int vi=ptrget(name);
728 if(is_str_var(name)){strncpy(variables[vi].str,data_pool[data_pos++],MAXSTR-1);variables[vi].type=TYPE_STR;}
729 else{variables[vi].num=atof(data_pool[data_pos++]);variables[vi].type=TYPE_SNG;}
730 }
731 return;
732 }
733
734 /* RESTORE */
735 if (strncasecmp(p,"RESTORE",7)==0) {
736 p+=7; skip_ws(&p);
737 if(isdigit(*p)){int tgt=(int)strtol(p,&p,10);(void)tgt;} /* stub: restore to line */
738 data_pos=0; return;
739 }
740
741 /* DIM */
742 if (strncasecmp(p,"DIM",3)==0&&!isalnum(p[3])) {
743 p+=3;
744 while(*p){
745 skip_ws(&p); if(*p==',')p++; skip_ws(&p); if(!*p)break;
746 char name[MAXSTR]={0}; int i=0;
747 while(isalnum(*p)||*p=='$') name[i++]=*p++;
748 name[i]=0;
749 skip_ws(&p);
750 int dims[4]={10,10,10,10}; int ndims=0;
751 if(*p=='('){
752 p++;
753 while(*p&&*p!=')'){skip_ws(&p);dims[ndims++]=(int)expr(&p);skip_ws(&p);if(*p==',')p++;}
754 if(*p==')')p++;
755 }
756 if(ndims==0)ndims=1;
757 make_arr(name,dims,ndims);
758 }
759 return;
760 }
761
762 /* SWAP */
763 if (strncasecmp(p,"SWAP",4)==0&&!isalnum(p[4])) {
764 p+=4; skip_ws(&p);
765 char n1[MAXSTR]={0}; int i=0; while(isalnum(*p)||*p=='$') n1[i++]=*p++; n1[i]=0;
766 skip_ws(&p); if(*p==',')p++; skip_ws(&p);
767 char n2[MAXSTR]={0}; i=0; while(isalnum(*p)||*p=='$') n2[i++]=*p++; n2[i]=0;
768 int v1=ptrget(n1),v2=ptrget(n2);
769 double tmp=variables[v1].num; variables[v1].num=variables[v2].num; variables[v2].num=tmp;
770 char ts[MAXSTR]; strcpy(ts,variables[v1].str); strcpy(variables[v1].str,variables[v2].str); strcpy(variables[v2].str,ts);
771 return;
772 }
773
774 /* POKE */
775 if (strncasecmp(p,"POKE",4)==0&&!isalnum(p[4])) {
776 p+=4; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); /* stub */
777 return;
778 }
779
780 /* WAIT */
781 if (strncasecmp(p,"WAIT",4)==0&&!isalnum(p[4])) { p+=4; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); return; }
782
783 /* OUT */
784 if (strncasecmp(p,"OUT",3)==0&&!isalnum(p[3])) { p+=3; expr(&p); skip_ws(&p); if(*p==',')p++; expr(&p); return; }
785
786 /* LSET / RSET */
787 if (strncasecmp(p,"LSET",4)==0&&!isalnum(p[4])) {
788 p+=4; skip_ws(&p); char name[MAXSTR]={0}; int i=0;
789 while(isalnum(*p)||*p=='$') name[i++]=*p++;
790 name[i]=0; skip_ws(&p); if(*p=='=')p++;
791 char s[MAXSTR]; str_expr(&p,s);
792 int vi=ptrget(name); strncpy(variables[vi].str,s,MAXSTR-1); variables[vi].type=TYPE_STR;
793 return;
794 }
795 if (strncasecmp(p,"RSET",4)==0&&!isalnum(p[4])) {
796 p+=4; skip_ws(&p); char name[MAXSTR]={0}; int i=0;
797 while(isalnum(*p)||*p=='$') name[i++]=*p++;
798 name[i]=0; skip_ws(&p); if(*p=='=')p++;
799 char s[MAXSTR]; str_expr(&p,s);
800 int vi=ptrget(name); strncpy(variables[vi].str,s,MAXSTR-1); variables[vi].type=TYPE_STR;
801 return;
802 }
803
804 /* WRITE */
805 if (strncasecmp(p,"WRITE",5)==0&&!isalnum(p[5])) {
806 p+=5; skip_ws(&p);
807 FILE *fp=stdout; if(*p=='#'){p++;int fn=(int)expr(&p);skip_ws(&p);if(*p==',')p++;if(fn>=0&&fn<MAXFILES&&file_handles[fn])fp=file_handles[fn];}
808 do_write(&p,fp); return;
809 }
810
811 /* OPEN */
812 if (strncasecmp(p,"OPEN",4)==0&&!isalnum(p[4])) {
813 p+=4; skip_ws(&p);
814 char fname[MAXBUF]; str_expr(&p,fname); skip_ws(&p);
815 char mode_str[8]="O";
816 if(strncasecmp(p,"FOR",3)==0&&!isalnum(p[3])){
817 p+=3; skip_ws(&p);
818 if(strncasecmp(p,"INPUT",5)==0){strcpy(mode_str,"r");p+=5;}
819 else if(strncasecmp(p,"OUTPUT",6)==0){strcpy(mode_str,"w");p+=6;}
820 else if(strncasecmp(p,"APPEND",6)==0){strcpy(mode_str,"a");p+=6;}
821 skip_ws(&p);
822 }
823 if(strncasecmp(p,"AS",2)==0&&!isalnum(p[2])){p+=2;skip_ws(&p);}
824 if(*p=='#')p++;
825 int fn=(int)expr(&p);
826 if(fn>=0&&fn<MAXFILES){ if(file_handles[fn])fclose(file_handles[fn]); file_handles[fn]=fopen(fname,mode_str); }
827 return;
828 }
829
830 /* CLOSE */
831 if (strncasecmp(p,"CLOSE",5)==0&&!isalnum(p[5])) {
832 p+=5; skip_ws(&p);
833 if(!*p){for(int i=0;i<MAXFILES;i++){if(file_handles[i])fclose(file_handles[i]);file_handles[i]=NULL;}return;}
834 while(*p){skip_ws(&p);if(*p=='#')p++;int fn=(int)expr(&p);if(fn>=0&&fn<MAXFILES&&file_handles[fn]){fclose(file_handles[fn]);file_handles[fn]=NULL;}skip_ws(&p);if(*p==',')p++;}
835 return;
836 }
837
838 /* LOAD */
839 if (strncasecmp(p,"LOAD",4)==0&&!isalnum(p[4])) {
840 p+=4; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
841 FILE *f=fopen(fname,"r"); if(!f){fprintf(stderr,"?File not found: %s\n",fname);return;}
842 char line[MAXBUF];
843 while(fgets(line,MAXBUF,f)){
844 line[strcspn(line,"\n")]=0;
845 char *q=line; skip_ws(&q);
846 if(isdigit(*q)){int num=(int)strtol(q,&q,10);skip_ws(&q);insert_line(num,q);}
847 }
848 fclose(f); collect_data(); return;
849 }
850
851 /* SAVE */
852 if (strncasecmp(p,"SAVE",4)==0&&!isalnum(p[4])) {
853 p+=4; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
854 FILE *f=fopen(fname,"w"); if(!f){fprintf(stderr,"?Cannot save\n");return;}
855 for(int i=0;i<prog_cnt;i++) fprintf(f,"%d %s\n",program[i].num,program[i].text);
856 fclose(f); return;
857 }
858
859 /* KILL */
860 if (strncasecmp(p,"KILL",4)==0&&!isalnum(p[4])) {
861 p+=4; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname); remove(fname); return;
862 }
863
864 /* NAME */
865 if (strncasecmp(p,"NAME",4)==0&&!isalnum(p[4])) {
866 p+=4; skip_ws(&p); char old[MAXBUF],new_[MAXBUF];
867 str_expr(&p,old); skip_ws(&p);
868 if(strncasecmp(p,"AS",2)==0)p+=2; skip_ws(&p);
869 str_expr(&p,new_); rename(old,new_); return;
870 }
871
872 /* FILES */
873 if (strncasecmp(p,"FILES",5)==0&&!isalnum(p[5])) {
874 system("ls"); return; /* stub */
875 }
876
877 /* WIDTH */
878 if (strncasecmp(p,"WIDTH",5)==0&&!isalnum(p[5])) {
879 p+=5; expr(&p); return; /* stub */
880 }
881
882 /* TRON / TROFF */
883 if (strncasecmp(p,"TRON",4)==0&&!isalnum(p[4])) { tron_flag=1; return; }
884 if (strncasecmp(p,"TROFF",5)==0&&!isalnum(p[5])) { tron_flag=0; return; }
885
886 /* ERASE */
887 if (strncasecmp(p,"ERASE",5)==0&&!isalnum(p[5])) {
888 p+=5; skip_ws(&p); char name[MAXSTR]={0}; int i=0;
889 while(isalnum(*p)||*p=='$') name[i++]=*p++;
890 name[i]=0;
891 for(int j=0;j<arr_cnt;j++) if(strcasecmp(arrays[j].name,name)==0){
892 if(arrays[j].nums)free(arrays[j].nums);
893 if(arrays[j].strs)free(arrays[j].strs);
894 for(int k=j;k<arr_cnt-1;k++) arrays[k]=arrays[k+1]; arr_cnt--; break;
895 }
896 return;
897 }
898
899 /* RANDOMIZE */
900 if (strncasecmp(p,"RANDOMIZE",9)==0&&!isalnum(p[9])) {
901 p+=9; skip_ws(&p);
902 if(*p) srand((unsigned)expr(&p)); else srand((unsigned)time(NULL));
903 return;
904 }
905
906 /* OPTION BASE */
907 if (strncasecmp(p,"OPTION",6)==0&&!isalnum(p[6])) {
908 p+=6; skip_ws(&p);
909 if(strncasecmp(p,"BASE",4)==0){p+=4;option_base=(int)expr(&p);}
910 return;
911 }
912
913 /* DEF FN */
914 if (strncasecmp(p,"DEF",3)==0&&!isalnum(p[3])) {
915 /* stub - user functions not yet supported */
916 return;
917 }
918
919 /* DEFSTR/DEFINT/DEFSNG/DEFDBL */
920 if (strncasecmp(p,"DEFSTR",6)==0||strncasecmp(p,"DEFINT",6)==0||
921 strncasecmp(p,"DEFSNG",6)==0||strncasecmp(p,"DEFDBL",6)==0) return; /* stub */
922
923 /* RESUME */
924 if (strncasecmp(p,"RESUME",6)==0&&!isalnum(p[6])) {
925 p+=6; skip_ws(&p);
926 if(strncasecmp(p,"NEXT",4)==0) cur_line_idx++;
927 err_number=0; on_error_line=-1; running=1; return;
928 }
929
930 /* ERROR */
931 if (strncasecmp(p,"ERROR",5)==0&&!isalnum(p[5])) {
932 p+=5; int code=(int)expr(&p); basic_error(code,"User"); return;
933 }
934
935 /* DELETE */
936 if (strncasecmp(p,"DELETE",6)==0&&!isalnum(p[6])) {
937 p+=6; skip_ws(&p); int n1=(int)strtol(p,&p,10); int n2=n1;
938 if(*p=='-'){p++;n2=(int)strtol(p,&p,10);}
939 for(int i=n1;i<=n2;i++) insert_line(i,"");
940 return;
941 }
942
943 /* RENUM */
944 if (strncasecmp(p,"RENUM",5)==0&&!isalnum(p[5])) {
945 p+=5; int start=10, step=10;
946 skip_ws(&p); if(isdigit(*p)){start=(int)strtol(p,&p,10);skip_ws(&p);if(*p==','){p++;step=(int)strtol(p,&p,10);}}
947 for(int i=0;i<prog_cnt;i++) program[i].num=start+i*step;
948 return;
949 }
950
951 /* AUTO */
952 if (strncasecmp(p,"AUTO",4)==0&&!isalnum(p[4])) { printf("[AUTO mode not supported interactively]\n"); return; }
953
954 /* EDIT */
955 if (strncasecmp(p,"EDIT",4)==0&&!isalnum(p[4])) {
956 p+=4; skip_ws(&p); int n=(int)strtol(p,&p,10);
957 int idx=find_line(n);
958 if(idx>=0) printf("%d %s\n",program[idx].num,program[idx].text);
959 return;
960 }
961
962 /* NULL */
963 if (strncasecmp(p,"NULL",4)==0&&!isalnum(p[4])) { p+=4; null_count=(int)expr(&p); return; }
964
965 /* CALL */
966 if (strncasecmp(p,"CALL",4)==0&&!isalnum(p[4])) { p+=4; expr(&p); return; /* stub */ }
967
968 /* CHAIN */
969 if (strncasecmp(p,"CHAIN",5)==0&&!isalnum(p[5])) {
970 p+=5; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
971 execute_stmt("NEW"); execute_stmt("LOAD");
972 return;
973 }
974
975 /* SYSTEM */
976 if (strncasecmp(p,"SYSTEM",6)==0&&!isalnum(p[6])) { printf("\n"); exit(0); }
977
978 /* END / STOP */
979 if (strncasecmp(p,"END",3)==0&&!isalnum(p[3])) { cont_idx=cur_line_idx; running=0; return; }
980 if (strncasecmp(p,"STOP",4)==0&&!isalnum(p[4])) { printf("Break in %d\n", program[cur_line_idx].num); cont_idx=cur_line_idx+1; running=0; return; }
981
982 /* CONT */
983 if (strncasecmp(p,"CONT",4)==0&&!isalnum(p[4])) {
984 if(cont_idx<0){printf("?Can't continue\n");return;}
985 run_from(cont_idx); return;
986 }
987
988 /* LIST [n[-m]] */
989 if (strncasecmp(p,"LIST",4)==0&&!isalnum(p[4])) {
990 p+=4; skip_ws(&p);
991 int n1=0,n2=999999;
992 if(isdigit(*p)){n1=(int)strtol(p,&p,10);n2=n1;skip_ws(&p);if(*p=='-'){p++;if(isdigit(*p))n2=(int)strtol(p,&p,10);else n2=999999;}}
993 for(int i=0;i<prog_cnt;i++) if(program[i].num>=n1&&program[i].num<=n2) printf("%d %s\n",program[i].num,program[i].text);
994 return;
995 }
996
997 /* RUN */
998 if (strncasecmp(p,"RUN",3)==0&&!isalnum(p[3])) {
999 p+=3; skip_ws(&p); int start=0;
1000 if(isdigit(*p)){int n=(int)strtol(p,&p,10);start=find_line(n);if(start<0)start=0;}
1001 collect_data(); data_pos=0; call_sp=0; for_sp=0; while_sp=0; var_cnt=0; arr_cnt=0;
1002 run_from(start); return;
1003 }
1004
1005 /* NEW */
1006 if (strncasecmp(p,"NEW",3)==0&&!isalnum(p[3])) {
1007 prog_cnt=0; var_cnt=0; arr_cnt=0; call_sp=0; for_sp=0; while_sp=0; data_count=0; on_error_line=-1;
1008 return;
1009 }
1010
1011 /* CLEAR */
1012 if (strncasecmp(p,"CLEAR",5)==0&&!isalnum(p[5])) {
1013 var_cnt=0; arr_cnt=0; call_sp=0; for_sp=0; while_sp=0; data_pos=0; return;
1014 }
1015
1016 /* MERGE */
1017 if (strncasecmp(p,"MERGE",5)==0&&!isalnum(p[5])) {
1018 p+=5; skip_ws(&p); char fname[MAXBUF]; str_expr(&p,fname);
1019 FILE *f=fopen(fname,"r"); if(!f)return;
1020 char line[MAXBUF]; while(fgets(line,MAXBUF,f)){
1021 line[strcspn(line,"\n")]=0; char *q=line; skip_ws(&q);
1022 if(isdigit(*q)){int num=(int)strtol(q,&q,10);skip_ws(&q);insert_line(num,q);}
1023 } fclose(f); return;
1024 }
1025
1026 /* GET / PUT (file) */
1027 if (strncasecmp(p,"GET",3)==0&&!isalnum(p[3])) { p+=3; /* stub */ return; }
1028 if (strncasecmp(p,"PUT",3)==0&&!isalnum(p[3])) { p+=3; /* stub */ return; }
1029
1030 /* FIELD */
1031 if (strncasecmp(p,"FIELD",5)==0&&!isalnum(p[5])) { return; /* stub */ }
1032
1033 /* RESET */
1034 if (strncasecmp(p,"RESET",5)==0&&!isalnum(p[5])) { return; /* stub */ }
1035
1036 printf("?Syntax error: %.40s\n",p);
1037}
1038
1039/* Execute : separated stmts on a line */
1040void execute_line(char *text) {
1041 char buf[MAXBUF]; strncpy(buf,text,MAXBUF-1); buf[MAXBUF-1]=0;
1042 char *p=buf;
1043 while(*p && running!=0) {
1044 char stmt[MAXBUF]; int i=0;
1045 int in_q=0;
1046 while(*p){
1047 if(*p=='"') in_q=!in_q;
1048 if(*p==':' && !in_q){ p++; break; }
1049 stmt[i++]=*p++;
1050 }
1051 stmt[i]=0;
1052 int prev_running=running;
1053 execute_stmt(stmt);
1054 if(running==2) return; /* jump happened */
1055 }
1056}
1057
1058void run_from(int idx) {
1059 running=1; cur_line_idx=idx;
1060 while(running==1 && cur_line_idx>=0 && cur_line_idx<prog_cnt){
1061 if(tron_flag) printf("[%d]",program[cur_line_idx].num);
1062 int save=cur_line_idx;
1063 execute_line(program[cur_line_idx].text);
1064 if(running==2){ running=1; }
1065 else if(running==1) cur_line_idx++;
1066 }
1067 running=0;
1068}
1069
1070/* ============================================================
1071 MAIN - READY loop (asm READY/PROMPT)
1072 ============================================================ */
1073int main() {
1074 srand((unsigned)time(NULL));
1075 memset(file_handles,0,sizeof(file_handles));
1076 on_error_line=-1;
1077 printf("MS BASIC-80 C Port\nOk\n");
1078 char input[MAXBUF];
1079 while(1){
1080 printf("> ");
1081 fflush(stdout);
1082 if(!fgets(input,MAXBUF,stdin)) break;
1083 input[strcspn(input,"\n")]=0;
1084 if(!*input) continue;
1085 char *p=input; skip_ws(&p);
1086 if(isdigit(*p)){
1087 int num=(int)strtol(p,&p,10); skip_ws(&p);
1088 insert_line(num,*p?p:"");
1089 } else {
1090 execute_stmt(p);
1091 }
1092 if(!running) printf("Ok\n");
1093 }
1094 return 0;
1095}
1096