1 /* #includes */ /*{{{C}}}*//*{{{*/
2 #include "config.h"
3
4 #include <assert.h>
5 #include <ctype.h>
6 #include <errno.h>
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <string.h>
10 #include <time.h>
11
12 #include "cpmfs.h"
13 #include "getopt_.h"
14 #include "term.h"
15 /*}}}*/
16
17 extern char **environ;
18
19 static char *mapbuf;
20
cpmtime(char lday,char hday,char hour,char min)21 static struct tm *cpmtime(char lday, char hday, char hour, char min) /*{{{*/
22 {
23 static struct tm tm;
24 unsigned long days=(lday&0xff)|((hday&0xff)<<8);
25 int d;
26 unsigned int md[12]={31,0,31,30,31,30,31,31,30,31,30,31};
27
28 tm.tm_sec=0;
29 tm.tm_min=((min>>4)&0xf)*10+(min&0xf);
30 tm.tm_hour=((hour>>4)&0xf)*10+(hour&0xf);
31 tm.tm_mon=0;
32 tm.tm_year=1978;
33 tm.tm_isdst=-1;
34 if (days) --days;
35 while (days>=(d=(((tm.tm_year%400)==0 || ((tm.tm_year%4)==0 && (tm.tm_year%100))) ? 366 : 365)))
36 {
37 days-=d;
38 ++tm.tm_year;
39 }
40 md[1]=((tm.tm_year%400)==0 || ((tm.tm_year%4)==0 && (tm.tm_year%100))) ? 29 : 28;
41 while (days>=md[tm.tm_mon])
42 {
43 days-=md[tm.tm_mon];
44 ++tm.tm_mon;
45 }
46 tm.tm_mday=days+1;
47 tm.tm_year-=1900;
48 return &tm;
49 }
50 /*}}}*/
info(struct cpmSuperBlock * sb,const char * format,const char * image)51 static void info(struct cpmSuperBlock *sb, const char *format, const char *image) /*{{{*/
52 {
53 const char *msg;
54
55 term_clear();
56 msg="File system characteristics";
57 term_xy((term_cols()-strlen(msg))/2,0); term_printf(msg);
58 term_xy(0,2); term_printf(" Image: %s",image);
59 term_xy(0,3); term_printf(" Format: %s",format);
60 term_xy(0,4); term_printf(" File system: ");
61 switch (sb->type)
62 {
63 case CPMFS_DR22: term_printf("CP/M 2.2"); break;
64 case CPMFS_P2DOS: term_printf("P2DOS 2.3"); break;
65 case CPMFS_DR3: term_printf("CP/M Plus"); break;
66 }
67
68 term_xy(0,6); term_printf(" Sector length: %d",sb->secLength);
69 term_xy(0,7); term_printf(" Number of tracks: %d",sb->tracks);
70 term_xy(0,8); term_printf(" Sectors per track: %d",sb->sectrk);
71
72 term_xy(0,10);term_printf(" Block size: %d",sb->blksiz);
73 term_xy(0,11);term_printf("Number of directory entries: %d",sb->maxdir);
74 term_xy(0,12);term_printf(" Number of directory blocks: %d",sb->dirblks);
75 term_xy(0,13);term_printf(" Logical sector skew: %d",sb->skew);
76 term_xy(0,14);term_printf(" Number of system tracks: %d",sb->boottrk);
77 term_xy(0,15);term_printf(" Logical extents per extent: %d",sb->extents);
78 term_xy(0,16);term_printf(" Allocatable data blocks: %d",sb->size-(sb->maxdir*32+sb->blksiz-1)/sb->blksiz);
79
80 msg="Any key to continue";
81 term_xy((term_cols()-strlen(msg))/2,23); term_printf(msg);
82 term_getch();
83 }
84 /*}}}*/
map(struct cpmSuperBlock * sb)85 static void map(struct cpmSuperBlock *sb) /*{{{*/
86 {
87 const char *msg;
88 char bmap[18*80];
89 int secmap,sys,directory;
90 int pos;
91
92 term_clear();
93 msg="Data map";
94 term_xy((term_cols()-strlen(msg))/2,0); term_printf(msg);
95
96 secmap=(sb->tracks*sb->sectrk+80*18-1)/(80*18);
97 memset(bmap,' ',sizeof(bmap));
98 sys=sb->boottrk*sb->sectrk;
99 memset(bmap,'S',sys/secmap);
100 directory=(sb->maxdir*32+sb->secLength-1)/sb->secLength;
101 memset(bmap+sys/secmap,'D',directory/secmap);
102 memset(bmap+(sys+directory)/secmap,'.',sb->sectrk*sb->tracks/secmap);
103
104 for (pos=0; pos<(sb->maxdir*32+sb->secLength-1)/sb->secLength; ++pos)
105 {
106 int entry;
107
108 Device_readSector(&sb->dev,sb->boottrk+pos/(sb->sectrk*sb->secLength),pos/sb->secLength,mapbuf);
109 for (entry=0; entry<sb->secLength/32 && (pos*sb->secLength/32)+entry<sb->maxdir; ++entry)
110 {
111 int i;
112
113 if (mapbuf[entry*32]>=0 && mapbuf[entry*32]<=(sb->type==CPMFS_P2DOS ? 31 : 15))
114 {
115 for (i=0; i<16; ++i)
116 {
117 int sector;
118
119 sector=mapbuf[entry*32+16+i]&0xff;
120 if (sb->size>=256) sector|=(((mapbuf[entry*32+16+ ++i]&0xff)<<8));
121 if (sector>0 && sector<=sb->size)
122 {
123 /* not entirely correct without the last extent record count */
124 sector=sector*(sb->blksiz/sb->secLength)+sb->sectrk*sb->boottrk;
125 memset(bmap+sector/secmap,'#',sb->blksiz/(sb->secLength*secmap));
126 }
127 }
128 }
129 }
130 }
131
132 for (pos=0; pos<(int)sizeof(bmap); ++pos)
133 {
134 term_xy(pos/18,2+pos%18);
135 term_putch(bmap[pos]);
136 }
137 term_xy(0,21); term_printf("S=System area D=Directory area #=File data .=Free");
138 msg="Any key to continue";
139 term_xy((term_cols()-strlen(msg))/2,23); term_printf(msg);
140 term_getch();
141 }
142 /*}}}*/
data(struct cpmSuperBlock * sb,const char * buf,unsigned long int pos)143 static void data(struct cpmSuperBlock *sb, const char *buf, unsigned long int pos) /*{{{*/
144 {
145 int offset=(pos%sb->secLength)&~0x7f;
146 unsigned int i;
147
148 for (i=0; i<128; ++i)
149 {
150 term_xy((i&0x0f)*3+!!(i&0x8),4+(i>>4)); term_printf("%02x",buf[i+offset]&0xff);
151 if (pos%sb->secLength==i+offset) term_reverse(1);
152 term_xy(50+(i&0x0f),4+(i>>4)); term_printf("%c",isprint((unsigned char)buf[i+offset]) ? buf[i+offset] : '.');
153 term_reverse(0);
154 }
155 term_xy(((pos&0x7f)&0x0f)*3+!!((pos&0x7f)&0x8)+1,4+((pos&0x7f)>>4));
156 }
157 /*}}}*/
158
159 const char cmd[]="fsed.cpm";
160
main(int argc,char * argv[])161 int main(int argc, char *argv[]) /*{{{*/
162 {
163 /* variables */ /*{{{*/
164 const char *devopts=(const char*)0;
165 int uppercase=0;
166 char *image;
167 const char *err;
168 struct cpmSuperBlock drive;
169 struct cpmInode root;
170 const char *format;
171 int c,usage=0;
172 off_t pos;
173 int ch;
174 int reload;
175 char *buf;
176 /*}}}*/
177
178 /* parse options */ /*{{{*/
179 if (!(format=getenv("CPMTOOLSFMT"))) format=FORMAT;
180 while ((c=getopt(argc,argv,"T:f:uh?"))!=EOF) switch(c)
181 {
182 case 'f': format=optarg; break;
183 case 'T': devopts=optarg; break;
184 case 'u': uppercase=1; break;
185 case 'h':
186 case '?': usage=1; break;
187 }
188
189 if (optind!=(argc-1)) usage=1;
190 else image=argv[optind++];
191
192 if (usage)
193 {
194 fprintf(stderr,"Usage: fsed.cpm [-f format] image\n");
195 exit(1);
196 }
197 /*}}}*/
198 /* open image */ /*{{{*/
199 if ((err=Device_open(&drive.dev,image,O_RDONLY,devopts)))
200 {
201 fprintf(stderr,"%s: cannot open %s (%s)\n",cmd,image,err);
202 exit(1);
203 }
204 if (cpmReadSuper(&drive,&root,format,uppercase)==-1)
205 {
206 fprintf(stderr,"%s: cannot read superblock (%s)\n",cmd,boo);
207 exit(1);
208 }
209 /*}}}*/
210 /* alloc sector buffers */ /*{{{*/
211 if ((buf=malloc(drive.secLength))==(char*)0 || (mapbuf=malloc(drive.secLength))==(char*)0)
212 {
213 fprintf(stderr,"fsed.cpm: can not allocate sector buffer (%s).\n",strerror(errno));
214 exit(1);
215 }
216 /*}}}*/
217 term_init();
218
219 pos=0;
220 reload=1;
221 do
222 {
223 /* display position and load data */ /*{{{*/
224 term_clear();
225 term_xy(0,2); term_printf("Byte %8lu (0x%08lx) ",pos,pos);
226 if (pos<(drive.boottrk*drive.sectrk*drive.secLength))
227 {
228 term_printf("Physical sector %3lu ",((pos/drive.secLength)%drive.sectrk)+1);
229 }
230 else
231 {
232 term_printf("Sector %3lu ",((pos/drive.secLength)%drive.sectrk)+1);
233 term_printf("(physical %3d) ",drive.skewtab[(pos/drive.secLength)%drive.sectrk]+1);
234 }
235 term_printf("Offset %5lu ",pos%drive.secLength);
236 term_printf("Track %5lu",pos/(drive.secLength*drive.sectrk));
237 term_xy(0,term_lines()-3); term_printf("N)ext track P)revious track");
238 term_xy(0,term_lines()-2); term_printf("n)ext record p)revious record f)orward byte b)ackward byte");
239 term_xy(0,term_lines()-1); term_printf("i)nfo q)uit");
240 if (reload)
241 {
242 if (pos<(drive.boottrk*drive.sectrk*drive.secLength))
243 {
244 err=Device_readSector(&drive.dev,pos/(drive.secLength*drive.sectrk),(pos/drive.secLength)%drive.sectrk,buf);
245 }
246 else
247 {
248 err=Device_readSector(&drive.dev,pos/(drive.secLength*drive.sectrk),drive.skewtab[(pos/drive.secLength)%drive.sectrk],buf);
249 }
250 if (err)
251 {
252 term_xy(0,4); term_printf("Data can not be read: %s",err);
253 }
254 else reload=0;
255 }
256 /*}}}*/
257
258 if /* position before end of system area */ /*{{{*/
259 (pos<(drive.boottrk*drive.sectrk*drive.secLength))
260 {
261 const char *msg;
262
263 msg="System area"; term_xy((term_cols()-strlen(msg))/2,0); term_printf(msg);
264 term_xy(36,term_lines()-3); term_printf("F)orward 16 byte B)ackward 16 byte");
265 if (!reload) data(&drive,buf,pos);
266 switch (ch=term_getch())
267 {
268 case 'F': /* next 16 byte */ /*{{{*/
269 {
270 if (pos+16<(drive.sectrk*drive.tracks*(off_t)drive.secLength))
271 {
272 if (pos/drive.secLength!=(pos+16)/drive.secLength) reload=1;
273 pos+=16;
274 }
275 break;
276 }
277 /*}}}*/
278 case 'B': /* previous 16 byte */ /*{{{*/
279 {
280 if (pos>=16)
281 {
282 if (pos/drive.secLength!=(pos-16)/drive.secLength) reload=1;
283 pos-=16;
284 }
285 break;
286 }
287 /*}}}*/
288 }
289 }
290 /*}}}*/
291 else if /* position before end of directory area */ /*{{{*/
292 (pos<(drive.boottrk*drive.sectrk*drive.secLength+drive.maxdir*32))
293 {
294 const char *msg;
295 unsigned long entrystart=(pos&~0x1f)%drive.secLength;
296 int entry=(pos-(drive.boottrk*drive.sectrk*drive.secLength))>>5;
297 int offset=pos&0x1f;
298
299 msg="Directory area"; term_xy((term_cols()-strlen(msg))/2,0); term_printf(msg);
300 term_xy(36,term_lines()-3); term_printf("F)orward entry B)ackward entry");
301
302 term_xy(0,13); term_printf("Entry %3d: ",entry);
303 if /* free or used directory entry */ /*{{{*/
304 ((buf[entrystart]>=0 && buf[entrystart]<=(drive.type==CPMFS_P2DOS ? 31 : 15)) || buf[entrystart]==(char)0xe5)
305 {
306 int i;
307
308 if (buf[entrystart]==(char)0xe5)
309 {
310 if (offset==0) term_reverse(1);
311 term_printf("Free");
312 term_reverse(0);
313 }
314 else term_printf("Directory entry");
315 term_xy(0,15);
316 if (buf[entrystart]!=(char)0xe5)
317 {
318 term_printf("User: ");
319 if (offset==0) term_reverse(1);
320 term_printf("%2d",buf[entrystart]);
321 term_reverse(0);
322 term_printf(" ");
323 }
324 term_printf("Name: ");
325 for (i=0; i<8; ++i)
326 {
327 if (offset==1+i) term_reverse(1);
328 term_printf("%c",buf[entrystart+1+i]&0x7f);
329 term_reverse(0);
330 }
331 term_printf(" Extension: ");
332 for (i=0; i<3; ++i)
333 {
334 if (offset==9+i) term_reverse(1);
335 term_printf("%c",buf[entrystart+9+i]&0x7f);
336 term_reverse(0);
337 }
338 term_xy(0,16); term_printf("Extent: %3d",((buf[entrystart+12]&0xff)+((buf[entrystart+14]&0xff)<<5))/drive.extents);
339 term_printf(" (low: ");
340 if (offset==12) term_reverse(1);
341 term_printf("%2d",buf[entrystart+12]&0xff);
342 term_reverse(0);
343 term_printf(", high: ");
344 if (offset==14) term_reverse(1);
345 term_printf("%2d",buf[entrystart+14]&0xff);
346 term_reverse(0);
347 term_printf(")");
348 term_xy(0,17); term_printf("Last extent record count: ");
349 if (offset==15) term_reverse(1);
350 term_printf("%3d",buf[entrystart+15]&0xff);
351 term_reverse(0);
352 term_xy(0,18); term_printf("Last record byte count: ");
353 if (offset==13) term_reverse(1);
354 term_printf("%3d",buf[entrystart+13]&0xff);
355 term_reverse(0);
356 term_xy(0,19); term_printf("Data blocks:");
357 for (i=0; i<16; ++i)
358 {
359 unsigned int block=buf[entrystart+16+i]&0xff;
360 if (drive.size>=256)
361 {
362 term_printf(" ");
363 if (offset==16+i || offset==16+i+1) term_reverse(1);
364 term_printf("%5d",block|(((buf[entrystart+16+ ++i]&0xff)<<8)));
365 term_reverse(0);
366 }
367 else
368 {
369 term_printf(" ");
370 if (offset==16+i) term_reverse(1);
371 term_printf("%3d",block);
372 term_reverse(0);
373 }
374 }
375 }
376 /*}}}*/
377 else if /* disc label */ /*{{{*/
378 (buf[entrystart]==0x20 && drive.type==CPMFS_DR3)
379 {
380 int i;
381 const struct tm *tm;
382 char s[30];
383
384 if (offset==0) term_reverse(1);
385 term_printf("Disc label");
386 term_reverse(0);
387 term_xy(0,15);
388 term_printf("Label: ");
389 for (i=0; i<11; ++i)
390 {
391 if (i+1==offset) term_reverse(1);
392 term_printf("%c",buf[entrystart+1+i]&0x7f);
393 term_reverse(0);
394 }
395 term_xy(0,16);
396 term_printf("Bit 0,7: ");
397 if (offset==12) term_reverse(1);
398 term_printf("Label %s",buf[entrystart+12]&1 ? "set" : "not set");
399 term_printf(", password protection %s",buf[entrystart+12]&0x80 ? "set" : "not set");
400 term_reverse(0);
401 term_xy(0,17);
402 term_printf("Bit 4,5,6: ");
403 if (offset==12) term_reverse(1);
404 term_printf("Time stamp ");
405 if (buf[entrystart+12]&0x10) term_printf("on create, ");
406 else term_printf("not on create, ");
407 if (buf[entrystart+12]&0x20) term_printf("on modification, ");
408 else term_printf("not on modifiction, ");
409 if (buf[entrystart+12]&0x40) term_printf("on access");
410 else term_printf("not on access");
411 term_reverse(0);
412 term_xy(0,18);
413 term_printf("Password: ");
414 for (i=0; i<8; ++i)
415 {
416 char printable;
417
418 if (offset==16+(7-i)) term_reverse(1);
419 printable=(buf[entrystart+16+(7-i)]^buf[entrystart+13])&0x7f;
420 term_printf("%c",isprint(printable) ? printable : ' ');
421 term_reverse(0);
422 }
423 term_printf(" XOR value: ");
424 if (offset==13) term_reverse(1);
425 term_printf("0x%02x",buf[entrystart+13]&0xff);
426 term_reverse(0);
427 term_xy(0,19);
428 term_printf("Created: ");
429 tm=cpmtime(buf[entrystart+24],buf[entrystart+25],buf[entrystart+26],buf[entrystart+27]);
430 if (offset==24 || offset==25) term_reverse(1);
431 strftime(s,sizeof(s),"%x",tm);
432 term_printf("%s",s);
433 term_reverse(0);
434 term_printf(" ");
435 if (offset==26) term_reverse(1);
436 term_printf("%2d",tm->tm_hour);
437 term_reverse(0);
438 term_printf(":");
439 if (offset==27) term_reverse(1);
440 term_printf("%02d",tm->tm_min);
441 term_reverse(0);
442 term_printf(" Updated: ");
443 tm=cpmtime(buf[entrystart+28],buf[entrystart+29],buf[entrystart+30],buf[entrystart+31]);
444 if (offset==28 || offset==29) term_reverse(1);
445 strftime(s,sizeof(s),"%x",tm);
446 term_printf("%s",s);
447 term_reverse(0);
448 term_printf(" ");
449 if (offset==30) term_reverse(1);
450 term_printf("%2d",tm->tm_hour);
451 term_reverse(0);
452 term_printf(":");
453 if (offset==31) term_reverse(1);
454 term_printf("%02d",tm->tm_min);
455 term_reverse(0);
456 }
457 /*}}}*/
458 else if /* time stamp */ /*{{{*/
459 (buf[entrystart]==0x21 && (drive.type==CPMFS_P2DOS || drive.type==CPMFS_DR3))
460 {
461 const struct tm *tm;
462 char s[30];
463
464 if (offset==0) term_reverse(1);
465 term_printf("Time stamps");
466 term_reverse(0);
467 term_xy(0,15);
468 term_printf("3rd last extent: Created/Accessed ");
469 tm=cpmtime(buf[entrystart+1],buf[entrystart+2],buf[entrystart+3],buf[entrystart+4]);
470 if (offset==1 || offset==2) term_reverse(1);
471 strftime(s,sizeof(s),"%x",tm);
472 term_printf("%s",s);
473 term_reverse(0);
474 term_printf(" ");
475 if (offset==3) term_reverse(1);
476 term_printf("%2d",tm->tm_hour);
477 term_reverse(0);
478 term_printf(":");
479 if (offset==4) term_reverse(1);
480 term_printf("%02d",tm->tm_min);
481 term_reverse(0);
482 term_printf(" Modified ");
483 tm=cpmtime(buf[entrystart+5],buf[entrystart+6],buf[entrystart+7],buf[entrystart+8]);
484 if (offset==5 || offset==6) term_reverse(1);
485 strftime(s,sizeof(s),"%x",tm);
486 term_printf("%s",s);
487 term_reverse(0);
488 term_printf(" ");
489 if (offset==7) term_reverse(1);
490 term_printf("%2d",tm->tm_hour);
491 term_reverse(0);
492 term_printf(":");
493 if (offset==8) term_reverse(1);
494 term_printf("%02d",tm->tm_min);
495 term_reverse(0);
496
497 term_xy(0,16);
498 term_printf("2nd last extent: Created/Accessed ");
499 tm=cpmtime(buf[entrystart+11],buf[entrystart+12],buf[entrystart+13],buf[entrystart+14]);
500 if (offset==11 || offset==12) term_reverse(1);
501 strftime(s,sizeof(s),"%x",tm);
502 term_printf("%s",s);
503 term_reverse(0);
504 term_printf(" ");
505 if (offset==13) term_reverse(1);
506 term_printf("%2d",tm->tm_hour);
507 term_reverse(0);
508 term_printf(":");
509 if (offset==14) term_reverse(1);
510 term_printf("%02d",tm->tm_min);
511 term_reverse(0);
512 term_printf(" Modified ");
513 tm=cpmtime(buf[entrystart+15],buf[entrystart+16],buf[entrystart+17],buf[entrystart+18]);
514 if (offset==15 || offset==16) term_reverse(1);
515 strftime(s,sizeof(s),"%x",tm);
516 term_printf("%s",s);
517 term_reverse(0);
518 term_printf(" ");
519 if (offset==17) term_reverse(1);
520 term_printf("%2d",tm->tm_hour);
521 term_reverse(0);
522 term_printf(":");
523 if (offset==18) term_reverse(1);
524 term_printf("%02d",tm->tm_min);
525 term_reverse(0);
526
527 term_xy(0,17);
528 term_printf(" Last extent: Created/Accessed ");
529 tm=cpmtime(buf[entrystart+21],buf[entrystart+22],buf[entrystart+23],buf[entrystart+24]);
530 if (offset==21 || offset==22) term_reverse(1);
531 strftime(s,sizeof(s),"%x",tm);
532 term_printf("%s",s);
533 term_reverse(0);
534 term_printf(" ");
535 if (offset==23) term_reverse(1);
536 term_printf("%2d",tm->tm_hour);
537 term_reverse(0);
538 term_printf(":");
539 if (offset==24) term_reverse(1);
540 term_printf("%02d",tm->tm_min);
541 term_reverse(0);
542 term_printf(" Modified ");
543 tm=cpmtime(buf[entrystart+25],buf[entrystart+26],buf[entrystart+27],buf[entrystart+28]);
544 if (offset==25 || offset==26) term_reverse(1);
545 strftime(s,sizeof(s),"%x",tm);
546 term_printf("%s",s);
547 term_reverse(0);
548 term_printf(" ");
549 if (offset==27) term_reverse(1);
550 term_printf("%2d",tm->tm_hour);
551 term_reverse(0);
552 term_printf(":");
553 if (offset==28) term_reverse(1);
554 term_printf("%02d",tm->tm_min);
555 term_reverse(0);
556 }
557 /*}}}*/
558 else if /* password */ /*{{{*/
559 (buf[entrystart]>=16 && buf[entrystart]<=31 && drive.type==CPMFS_DR3)
560 {
561 int i;
562
563 if (offset==0) term_reverse(1);
564 term_printf("Password");
565 term_reverse(0);
566
567 term_xy(0,15);
568 term_printf("Name: ");
569 for (i=0; i<8; ++i)
570 {
571 if (offset==1+i) term_reverse(1);
572 term_printf("%c",buf[entrystart+1+i]&0x7f);
573 term_reverse(0);
574 }
575 term_printf(" Extension: ");
576 for (i=0; i<3; ++i)
577 {
578 if (offset==9+i) term_reverse(1);
579 term_printf("%c",buf[entrystart+9+i]&0x7f);
580 term_reverse(0);
581 }
582
583 term_xy(0,16);
584 term_printf("Password required for: ");
585 if (offset==12) term_reverse(1);
586 if (buf[entrystart+12]&0x80) term_printf("Reading ");
587 if (buf[entrystart+12]&0x40) term_printf("Writing ");
588 if (buf[entrystart+12]&0x20) term_printf("Deleting ");
589 term_reverse(0);
590
591 term_xy(0,17);
592 term_printf("Password: ");
593 for (i=0; i<8; ++i)
594 {
595 char printable;
596
597 if (offset==16+(7-i)) term_reverse(1);
598 printable=(buf[entrystart+16+(7-i)]^buf[entrystart+13])&0x7f;
599 term_printf("%c",isprint(printable) ? printable : ' ');
600 term_reverse(0);
601 }
602 term_printf(" XOR value: ");
603 if (offset==13) term_reverse(1);
604 term_printf("0x%02x",buf[entrystart+13]&0xff);
605 term_reverse(0);
606 }
607 /*}}}*/
608 else /* bad status */ /*{{{*/
609 {
610 term_printf("Bad status ");
611 if (offset==0) term_reverse(1);
612 term_printf("0x%02x",buf[entrystart]);
613 term_reverse(0);
614 }
615 /*}}}*/
616 if (!reload) data(&drive,buf,pos);
617 switch (ch=term_getch())
618 {
619 case 'F': /* next entry */ /*{{{*/
620 {
621 if (pos+32<(drive.sectrk*drive.tracks*(off_t)drive.secLength))
622 {
623 if (pos/drive.secLength!=(pos+32)/drive.secLength) reload=1;
624 pos+=32;
625 }
626 break;
627 }
628 /*}}}*/
629 case 'B': /* previous entry */ /*{{{*/
630 {
631 if (pos>=32)
632 {
633 if (pos/drive.secLength!=(pos-32)/drive.secLength) reload=1;
634 pos-=32;
635 }
636 break;
637 }
638 /*}}}*/
639 }
640 }
641 /*}}}*/
642 else /* data area */ /*{{{*/
643 {
644 const char *msg;
645
646 msg="Data area"; term_xy((term_cols()-strlen(msg))/2,0); term_printf(msg);
647 if (!reload) data(&drive,buf,pos);
648 ch=term_getch();
649 }
650 /*}}}*/
651
652 /* process common commands */ /*{{{*/
653 switch (ch)
654 {
655 case 'n': /* next record */ /*{{{*/
656 {
657 if (pos+128<(drive.sectrk*drive.tracks*(off_t)drive.secLength))
658 {
659 if (pos/drive.secLength!=(pos+128)/drive.secLength) reload=1;
660 pos+=128;
661 }
662 break;
663 }
664 /*}}}*/
665 case 'p': /* previous record */ /*{{{*/
666 {
667 if (pos>=128)
668 {
669 if (pos/drive.secLength!=(pos-128)/drive.secLength) reload=1;
670 pos-=128;
671 }
672 break;
673 }
674 /*}}}*/
675 case 'N': /* next track */ /*{{{*/
676 {
677 if ((pos+drive.sectrk*drive.secLength)<(drive.sectrk*drive.tracks*drive.secLength))
678 {
679 pos+=drive.sectrk*drive.secLength;
680 reload=1;
681 }
682 break;
683 }
684 /*}}}*/
685 case 'P': /* previous track */ /*{{{*/
686 {
687 if (pos>=drive.sectrk*drive.secLength)
688 {
689 pos-=drive.sectrk*drive.secLength;
690 reload=1;
691 }
692 break;
693 }
694 /*}}}*/
695 case 'b': /* byte back */ /*{{{*/
696 {
697 if (pos)
698 {
699 if (pos/drive.secLength!=(pos-1)/drive.secLength) reload=1;
700 --pos;
701 }
702 break;
703 }
704 /*}}}*/
705 case 'f': /* byte forward */ /*{{{*/
706 {
707 if (pos+1<drive.tracks*drive.sectrk*drive.secLength)
708 {
709 if (pos/drive.secLength!=(pos+1)/drive.secLength) reload=1;
710 ++pos;
711 }
712 break;
713 }
714 /*}}}*/
715 case 'i': info(&drive,format,image); break;
716 case 'm': map(&drive); break;
717 }
718 /*}}}*/
719 } while (ch!='q');
720
721 term_exit();
722 exit(0);
723 }
724 /*}}}*/
725