-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathsortdir.c
2731 lines (2525 loc) · 68 KB
/
sortdir.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* SORTDIR - for Apple II (ProDOS)
*
* Bobbi January-June 2020
*
* TODO: Find out why free(usedlist) at end -> crash. Memory corruption?
* TODO: EOF validation / fix:
* 1) Check this in readir() taking account of sparse files
* 2) When trimming a directory, need to update EOF for parent entry
* TODO: Print indication when a file is sparse - blocks in inverse video?
* TODO: Get both ProDOS-8 and GNO versions to build from this source
*
* Revision History
* v0.50 Initial alpha release on GitHub. Ported from GNO/ME version.
* v0.51 Made buf[] and buf2[] dynamic.
* v0.52 Support for aux memory.
* v0.53 Auto-sizing of filelist[] to fit available memory.
* v0.54 Make command line argument handling a compile time option.
* v0.55 Can use *all* of largest heap block for filelist[].
* v0.56 Minor improvements to conditional compilation.
* v0.57 Fixed bugs in aux memory allocation, memory zeroing bug.
* v0.58 Fixed more bugs. Now working properly using aux memory.
* v0.59 Moved creation of filelist[] into buildsorttable(). More bugfix.
* v0.60 Modified fileent to be a union. Build it for each subsort. Saves RAM.
* v0.61 Squeezed fileent to be a few bytes smaller. Fixed folder sort.
* v0.62 Modified buildsorttable() to update existing filelist[].
* v0.63 Made code work properly with #undef CHECK.
* v0.64 Fixed overflow in file count (entries). Added check to auxalloc().
* v0.65 Fixed length passed to AUXMOVE in copyaux().
* v0.66 Modified to build sorted blocks on the fly rather than in aux memory.
* v0.67 Fixed bug in v0.66 where garbage was written to end of directory.
* v0.68 Cleaned up error msgs.
* v0.69 Fixed support for drive number >2. (cc65 needs to be fixed too!)
* v0.70 Changed sort options to support mtime & ctime. Improved UI a bit.
* v0.71 Added support for allocating aux LC memory.
* v0.72 Initial support for freelist and usedlist in aux mem. (Slow!)
* v0.73 Speedup to checkfreeandused();
* v0.74 Eliminate no-op sort.
* v0.75 Fix bug - crash when too many files to sort.
* v0.76 Fix bug - checkfreeandused() not traversing all freelist.
* v0.77 Implemented zeroblock() for ProDOS-8.
* v0.78 Improved error handling when too many files to sort.
* v0.79 Trim unused directory blocks after sorting. Write freelist to disk.
* v0.80 Reinstated no-op sort (useful for compacting dir without reordering).
* v0.81 Do not trim volume directory to <4 blocks.
* v0.82 Minor fix to TRIMDIR conditional compilation.
* v0.83 Print additional info on each file.
* v0.84 Minor fixup for builds without CHECK and FREELIST defined.
* v0.85 Only write free list if it has been changed.
* v0.86 Show 'invisible' access bit.
* v0.87 Change the fix options so '-' is ask, 'y'/'n' are always/never.
* v0.88 Show ProDOS 2.5 dates in inverse video (saves two columns!)
* v0.89 Commented out free(usedlist) which was crashing for some reason.
* v0.90 Fixed parsing of dateopts[], caseopts[], fixopts[]
* v0.91 Added disconnect_ramdisk()
* v0.92 Copied RAMdisk disconnection/reconnection code from EDIT.SYSTEM
*/
//#pragma debug 9
//#pragma lint -1
//#pragma stacksize 16384
//#pragma memorymodel 0
//#pragma optimize -1 /* Disable stack repair code */
#include <apple2enh.h>
#include <conio.h>
#include <ctype.h>
#include <dio.h>
#include <fcntl.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
//#include <sys/stat.h>
//#include <orca.h>
//#include <gsos.h>
//#include <prodos.h>
#define CHECK /* Perform additional integrity checking */
#define SORT /* Enable sorting code */
#define FREELIST /* Checking of free list */
#define AUXMEM /* Auxiliary memory support on //e and up */
#undef CMDLINE /* Command line option parsing */
#undef TRIMDIR /* Enable trimming of directory blocks */
#define NLEVELS 4 /* Number of nested sorts permitted */
typedef unsigned char uchar;
typedef unsigned int uint;
typedef unsigned long ulong;
#define NMLEN 15 /* Length of filename */
/*
* ProDOS directory header
* See ProDOS-8 Tech Ref pp. 152
*/
struct pd_dirhdr {
uchar typ_len;
char name[NMLEN];
char reserved[8];
uchar ctime[4];
uchar vers;
uchar minvers;
uchar access;
uchar entlen;
uchar entperblk;
uchar filecnt[2];
uchar parptr[2]; /* Bitmap pointer in volume dir */
uchar parentry; /* Total blocks LSB in volume dir */
uchar parentlen; /* Total blocks MSB in volume dir */
};
/*
* ProDOS file entry
* See ProDOS-8 Tech Ref pp. 155
*/
struct pd_dirent {
uchar typ_len;
char name[NMLEN];
uchar type;
uchar keyptr[2];
uchar blksused[2];
uchar eof[3];
uchar ctime[4];
uchar vers;
uchar minvers;
uchar access;
uchar auxtype[2];
uchar mtime[4];
uchar hdrptr[2];
};
#define BLKSZ 512 /* 512 byte blocks */
#define PTRSZ 4 /* 4 bytes of pointers at beginning of each blk */
#define ENTSZ 0x27 /* Normal ProDOS directory entry size */
#define ENTPERBLK 0x0d /* Normal ProDOS dirents per block */
#define FLSZ 8192 /* Bytes required for 64K block free-list */
/* Exit codes */
#define EXIT_SUCCESS 0
#define EXIT_BAD_ARG 1
#define EXIT_ALLOC_ERR 2
#define EXIT_FATAL_ERR 3
/*
* Linked list of directory blocks read from disk
* Directory block is stored in data[]
*/
struct block {
#ifdef AUXMEM
char *data; /* Contents of block (pointer to auxmem) */
#else
char data[BLKSZ]; /* Contents of block */
#endif
uint blocknum; /* Block number on disk */
struct block *next;
};
/*
* Entry for array of filenames used by qsort()
*/
struct fileent {
uchar blockidx; /* Index of dir block (1,2,3 ...) */
uchar entrynum; /* Entry within the block */
union {
char name[NMLEN-2]; /* Name converted to upper/lower case */
char datetime[12+1];/* Date/time as a yyyymmddhhmm string */
uchar type; /* ProDOS file type */
uint blocks; /* Size in blocks */
ulong eof; /* EOF position in bytes */
};
/* NOTE: Because name is unique we do not need the order field to make
* the sort stable, so we can let the name buffer overflow by 2 bytes
*/
uint order; /* Hack to make qsort() stable */
};
/*
* Entry for list of directory keyblocks to check
*/
struct dirblk {
uint blocknum;
struct dirblk *next;
};
/*
* Represents a date and time
*/
struct datetime {
uint year;
uchar month;
uchar day;
uchar hour;
uchar minute;
uchar ispd25format;
uchar nodatetime;
};
/*
* Globals
*/
#ifdef AUXMEM
#define STARTAUX1 0x0800 // 46K main aux block
#define ENDAUX1 0xbfff
#define STARTAUX2 0xd000 // 12K block in aux LC
#define ENDAUX2 0xffff
static char *auxp = (char*)STARTAUX1; /* For allocating aux main */
static char *auxp2 = (char*)STARTAUX2; /* For allocating aux LC */
static char *auxlockp = (char*)STARTAUX1; /* Aux mem protection */
#endif
#ifdef FREELIST
static uint totblks; /* Total # blocks on volume */
static uchar *freelist; /* Free-list bitmap */
static uchar *usedlist; /* Bit map of used blocks */
static uchar flloaded = 0; /* 1 if free-list has been loaded */
static uchar flchanged = 0; /* 1 if free-list has been changed */
static uint flsize; /* Size of free-list in blocks */
static uint flblk; /* Block num for start of freelist */
#endif
static char currdir[NMLEN+1]; /* Name of current directory */
static struct block *blocks = NULL; /* List of directory disk blocks */
static struct dirblk *dirs = NULL; /* List of key blocks of subdirs */
static uint numfiles; /* Number of files in current dir */
static uint maxfiles; /* Size of filelist[] */
static uchar entsz; /* Bytes per file entry */
static uchar entperblk; /* Number of entries per block */
static uint errcount = 0; /* Error counter */
static dhandle_t dio_hdl; /* cc64 direct I/O handle */
static uchar dowholedisk = 0; /* -D whole-disk option */
static uchar dorecurse = 0; /* -r recurse option */
static uchar dowrite = 0; /* -w write option */
static uchar doverbose = 0; /* -v verbose option */
static uchar dodebug = 0; /* -V very verbose option */
#ifdef FREELIST
static uchar dozero = 0; /* -z zero free blocks option */
#endif
static char sortopts[NLEVELS+1] = ""; /* -s:abc list of sort options */
static char caseopts[2] = ""; /* -c:x case conversion option */
static char fixopts[2] = ""; /* -f:x fix mode option */
static char dateopts[2] = ""; /* -d:x date conversion option */
// Allocated dynamically in main()
static char *buf; /* General purpose scratch buffer */
static char *buf2; /* General purpose scratch buffer */
static char *dirblkbuf; /* Used for reading directory blocks */
static struct fileent *filelist; /* Used for qsort() */
/* Error messages */
static const char err_nomem[] = "No memory!";
static const char err_noaux[] = "No aux mem!";
static const char err_rdblk1[] = "Can't read blk %u";
static const char err_rdblk2[] = "Can't read blk %u ($%2x)";
static const char err_wtblk1[] = "Can't write blk %u";
static const char err_wtblk2[] = "Can't write blk %u ($%2x)";
#ifdef CHECK
static const char err_stype2[] = "Bad storage type $%2x for %s";
#endif
static const char err_odir1[] = "Can't open dir %s";
static const char err_rddir1[] = "Can't read dir %s";
static const char err_rdpar[] = "Can't read parent dir";
#ifdef CHECK
static const char err_sdname[] = "Bad subdir name";
static const char err_entsz2[] = "Bad entry size %u, should be %u";
static const char err_entblk2[] = "Bad entries/blk %u, should be %u";
static const char err_parblk3[] = "Bad parent %s %u, should be %u";
static const char err_hdrblk2[] = "Bad hdr blk %u, should be %u";
static const char err_access[] = "Bad access";
static const char err_forksz3[] = "%s fork size %u is wrong, should be %u";
static const char err_used2[] = "Blks used %u is wrong, should be %u";
#endif
static const char err_many[] = "Too many files to sort";
static const char err_count2[] = "Filecount %u wrong, should be %u";
static const char err_nosort[] = "Not sorting due to errors";
#ifdef FREELIST
static const char err_rdfl[] = "Can't read free list";
static const char err_blfree1[] = "In use blk %u is marked free";
static const char err_blfree2[] = "%s blk %u marked free";
static const char err_blused1[] = "Unused blk %u not marked free";
static const char err_blused2[] = "%s blk %u used elsewhere";
#endif
static const char err_updsdir1[] = "Can't update subdir entry (%s)";
static const char err_invopt[] = "Invalid %s option";
#ifdef CMDLINE
static const char err_usage[] = "Usage error";
#endif
static const char err_80col[] = "Need 80 cols";
static const char err_128K[] = "Need 128K";
// The following are used for reconnecting /RAM and /RAM3 on exit
uint16_t s3d1vec;
uint16_t s3d2vec;
uint8_t s3d1dev;
uint8_t s3d2dev;
/* Prototypes */
#ifdef AUXMEM
void copyaux(char *src, char *dst, uint len, uchar dir);
char *auxalloc(uint bytes);
char *auxalloc2(uint bytes);
void lockaux(void);
void freeallaux(void);
#endif
void hline(void);
void hlinechar(char c);
void confirm(void);
void err(enum errtype severity, const char *fmt, ...);
void flushall(void);
int readdiskblock(uchar device, uint blocknum, char *buf);
int writediskblock(uchar device, uint blocknum, char *buf);
void fixcase(char *in, char *out, uchar vers, uchar minvers, uchar len);
void lowercase(char *p, uchar len, uchar *vers, uchar *minvers);
void uppercase(char *p, uchar len, uchar *vers, uchar *minvers);
void initialcase(uchar mode, char *p, uchar len, uchar *vers, uchar *minvers);
void firstblk(char *dirname, uchar *device, uint *block);
void readdatetime(uchar time[4], struct datetime *dt);
void writedatetime(struct datetime *dt, uchar time[4]);
void printdatetime(struct datetime *dt);
uint askfix(void);
#ifdef FREELIST
int readfreelist(uchar device);
int isfree(uint blk);
int isused(uint blk);
void markused(uint blk);
void trimdirblock(uint blk);
void checkblock(uint blk, char *msg);
#endif
#ifdef CHECK
int seedlingblocks(uchar device, uint keyblk, uint *blkcnt);
int saplingblocks(uchar device, uint keyblk, uint *blkcnt);
int treeblocks(uchar device, uint keyblk, uint *blkcnt);
int forkblocks(uchar device, uint keyblk, uint *blkcnt);
int subdirblocks(uchar device, uint keyblk, struct pd_dirent *ent,
uint blocknum, uint blkentries, uint *blkcnt);
#endif
void enqueuesubdir(uint blocknum, uint subdiridx);
int readdir(uint device, uint blocknum);
#ifdef SORT
uchar buildsorttable(char s, uchar callidx);
int cmp_name_asc(const void *a, const void *b);
int cmp_name_desc(const void *a, const void *b);
int cmp_name_asc_ci(const void *a, const void *b);
int cmp_name_desc_ci(const void *a, const void *b);
int cmp_datetime_asc(const void *a, const void *b);
int cmp_datetime_desc(const void *a, const void *b);
int cmp_type_asc(const void *a, const void *b);
int cmp_type_desc(const void *a, const void *b);
int cmp_dir_beg(const void *a, const void *b);
int cmp_dir_end(const void *a, const void *b);
int cmp_blocks_asc(const void *a, const void *b);
int cmp_blocks_desc(const void *a, const void *b);
int cmp_eof_asc(const void *a, const void *b);
int cmp_eof_desc(const void *a, const void *b);
int cmp_noop(const void *a, const void *b);
void sortlist(char s);
#endif
void printlist(void);
uint blockidxtoblocknum(uint idx);
void copydirblkptrs(uint blkidx);
void copydirent(uint srcblk, uint srcent, uint dstblk, uint dstent, uint device);
uchar sortblock(uint device, uint dstblk);
uchar writedir(uchar device);
uchar writefreelist(uchar device);
void freeblocks(void);
void subtitle(char *s);
void interactive(void);
void processdir(uint device, uint blocknum);
#ifdef FREELIST
void checkfreeandused(uchar device);
void zeroblock(uchar device, uint blocknum);
void zerofreeblocks(uchar device, uint freeblks);
#endif
#ifdef CMDLINE
void usage(void);
void parseargs(void);
#endif
enum errtype {WARN, NONFATAL, FATAL, FATALALLOC, FATALBADARG, FINISHED};
#ifdef AUXMEM
/* Aux memory copy routine */
#define FROMAUX 0
#define TOAUX 1
void copyaux(char *src, char *dst, uint len, uchar dir) {
char **a1 = (char**)0x3c;
char **a2 = (char**)0x3e;
char **a4 = (char**)0x42;
*a1 = src;
*a2 = src + len - 1; // AUXMOVE moves length+1 bytes!!
*a4 = dst;
if (dir == TOAUX) {
__asm__("sec"); // Copy main->aux
__asm__("jsr $c311"); // AUXMOVE
} else {
__asm__("clc"); // Copy aux->main
__asm__("jsr $c311"); // AUXMOVE
}
}
/* Extremely simple aux memory allocator */
char *auxalloc(uint bytes) {
char *p = auxp;
auxp += bytes;
if (auxp > (char*)ENDAUX1)
return auxalloc2(bytes);
return p;
}
/* Extremely simple aux memory allocator */
char *auxalloc2(uint bytes) {
char *p = auxp2;
auxp2 += bytes;
if (auxp2 < p) // ie: wrap around $ffff
err(FATAL, err_noaux);
return p;
}
/* Lock aux memory below address provided
* Must be in main bank
*/
void lockaux(void) {
auxlockp = auxp;
}
/* Free all aux memory above lock address */
void freeallaux() {
auxp = (char*)auxlockp;
auxp2 = (char*)STARTAUX2;
}
#endif
/* Horizontal line */
void hline(void) {
hlinechar('-');
}
void hlinechar(char c) {
uint i;
for (i = 0; i < 80; ++i)
putchar(c);
}
void confirm() {
puts("[Press Any Key]");
getchar();
}
/****************************************************************************/
/* LANGUAGE CARD BANK 2 0xd400-x0dfff 3KB */
/****************************************************************************/
#pragma code-name (push, "LC")
/*
* Display error message
*/
void err(enum errtype severity, const char *fmt, ...) {
va_list v;
uint rv = 0;
putchar('\n');
if (severity == FINISHED) {
hline();
if (errcount == 0)
printf("DONE - no errors found.\n");
else
printf("DONE - %u errors\n", errcount);
hline();
confirm();
exit(EXIT_SUCCESS);
}
++errcount;
switch (severity) {
case FATAL:
rv = EXIT_FATAL_ERR;
case FATALALLOC:
rv = EXIT_ALLOC_ERR;
case FATALBADARG:
rv = EXIT_BAD_ARG;
}
fputs(((rv > 0) ? " ** " : " "), stdout);
va_start(v, fmt);
vprintf(fmt, v);
va_end(v);
if (rv > 0) {
printf("\nStopping after %u errors\n", errcount);
confirm();
exit(rv);
}
}
/*
* Disable GSOS block cache and flush any unwritten changes
*/
void flushall(void) {
// short ff[2];
// ResetCacheGS(0); /* Disable block caching */
// ff[0] = 1;
// ff[1] = 0;
// FlushGS(ff);
}
/*
* Read block from disk using ProDOS call
* buf must point to buffer with at least 512 bytes
*/
int readdiskblock(uchar device, uint blocknum, char *buf) {
int rc;
#ifdef CHECK
#ifdef FREELIST
if (flloaded)
if (isfree(blocknum))
err(NONFATAL, err_blfree1, blocknum);
#endif
#endif
// BlockRec br;
// br.blockDevNum = device;
// br.blockDataBuffer = buf;
// br.blockNum = blocknum;
// READ_BLOCK(&br);
// int rc = toolerror();
// if (rc) {
// err(FATAL, "Blk read failed, err=%x", rc);
// return -1;
// }
rc = dio_read(dio_hdl, blocknum, buf);
if (rc)
err(FATAL, err_rdblk2, blocknum, rc);
return 0;
}
/*
* Write block from disk using ProDOS call
* buf must point to buffer with at least 512 bytes
*/
int writediskblock(uchar device, uint blocknum, char *buf) {
int rc;
if ((strcmp(currdir, "LIB") == 0) ||
(strcmp(currdir, "LIBRARIES") == 0)) {
printf("Not writing lib dir %s\n", currdir);
return 0;
}
flushall();
// DIORecGS dr;
// dr.pCount = 6;
// dr.devNum = device;
// dr.buffer = buf;
// dr.requestCount = BLKSZ;
// dr.startingBlock = blocknum;
// dr.blockSize = BLKSZ;
// DWriteGS(&dr);
// if (dr.transferCount != BLKSZ) {
// err(FATAL, "Blk write failed");
// return -1;
// }
rc = dio_write(dio_hdl, blocknum, buf);
if (rc)
err(FATAL, err_wtblk2, blocknum, rc);
return 0;
}
/*
* Uses the vers and minvers fields of the directory entry
* as a bitmap representing which characters are upper and which are
* lowercase
*/
void fixcase(char *in, char *out, uchar vers, uchar minvers, uchar len) {
uint i;
uchar idx = 0;
if (!(minvers & 0x80)) {
for (idx = 0; idx < NMLEN; ++idx)
out[idx] = in[idx];
out[len] = '\0';
return;
}
minvers <<= 1;
for (i = 0; i < 7; ++i) {
out[idx] = ((minvers & 0x80) ? tolower(in[idx]) : in[idx]);
++idx;
minvers <<= 1;
}
for (i = 0; i < 8; ++i) {
out[idx] = ((vers & 0x80) ? tolower(in[idx]) : in[idx]);
++idx;
vers <<= 1;
}
out[len] = '\0';
}
/*
* Convert filename pointed to by p into lower case (which is recorded
* as a bitmap in the vers and minvers fields.
*/
void lowercase(char *p, uchar len, uchar *vers, uchar *minvers) {
uint i;
uchar idx = 0;
*vers = 0x00;
*minvers = 0x01;
for (i = 0; i < 7; ++i) {
*minvers <<= 1;
if ((idx < len) && isalpha(p[idx++]))
*minvers |= 0x01;
}
for (i = 0; i < 8; ++i) {
*vers <<= 1;
if ((idx < len) && isalpha(p[idx++]))
*vers |= 0x01;
}
}
/*
* Convert filename pointed to by p into upper case (which is recorded
* as a bitmap in the vers and minvers fields.
*/
void uppercase(char*, uchar, uchar *vers, uchar *minvers) {
*vers = 0x00;
*minvers = 0x00;
}
/*
* Convert filename pointed to by p into to have first letter capitalized
* (which is recorded as a bitmap in the vers and minvers fields.
* If mode = 0 then just uppercase the initial char ("Read.me")
* otherwise camel-case the name ("Read.Me")
*/
void initialcase(uchar mode, char *p, uchar len, uchar *vers, uchar *minvers) {
uint i;
uchar idx = 0;
uchar capsflag = 1;
*vers = 0x00;
*minvers = 0x01;
for (i = 0; i < 7; ++i) {
*minvers <<= 1;
if ((idx < len) && isalpha(p[idx++]))
if (!capsflag)
*minvers |= 0x01;
if ((mode == 1) && !isalpha(p[idx-1]))
capsflag = 1;
else
capsflag = 0;
}
for (i = 0; i < 8; ++i) {
*vers <<= 1;
if ((idx < len) && isalpha(p[idx++]))
if (!capsflag)
*vers |= 0x01;
if ((mode == 1) && !isalpha(p[idx-1]))
capsflag = 1;
else
capsflag = 0;
}
}
//segment "extra";
/*
* Read the first block of a directory and deduce the device ID and block
* number of the first block of the directory.
*/
void firstblk(char *dirname, uchar *device, uint *block) {
struct pd_dirhdr *hdr;
struct pd_dirent *ent;
int fp;
uint len;
uint parentblk, parententry, parententlen;
uchar slot, drive;
uchar *lastdev = (uchar*)0xbf30; /* Last device accessed by ProDOS */
fp = open(dirname, O_RDONLY);
if (!fp) {
err(FATAL, err_odir1, dirname);
goto ret;
}
len = read(fp, buf, BLKSZ);
if (len != BLKSZ) {
err(FATAL, err_rddir1, dirname);
goto ret;
}
// struct stat st;
// if (stat(dirname, &st) == -1)
// err(FATAL, "Can't stat %s", dirname);
//
// if (!S_ISDIR(st.st_mode))
// err(FATAL, "%s is not a directory", dirname);
//
// *device = st.st_dev;
/*
* lastdev is in the following format:
* ProDOS 2.5+ DSSS00DD (supports drives 1-8 for each slot)
* ProDOS 2.x DSSS0000 (supports drives 1-2 for each slot)
*/
*device = *lastdev;
slot = (*lastdev & 0x70) >> 4;
drive = ((*lastdev & 0x80) >> 7) + ((*lastdev & 0x03) << 1) + 1;
clrscr();
printf("[Slot %u, Drive %u]\n", slot, drive);
*device = slot + (drive - 1) * 8;
dio_hdl = dio_open(*device);
hdr = (struct pd_dirhdr*)(buf + PTRSZ);
/* Detect & handle volume directory */
if ((hdr->typ_len & 0xf0) == 0xf0) {
*block = 2;
goto ret;
}
#ifdef CHECK
if ((hdr->typ_len & 0xf0) != 0xe0) {
err(NONFATAL, err_stype2, hdr->typ_len & 0xf0, "dir");
goto ret;
}
#endif
/* Handle subdirectory */
parentblk = hdr->parptr[0] + 256U * hdr->parptr[1];
parententry = hdr->parentry;
parententlen = hdr->parentlen;
/* Read parent directory block */
if (readdiskblock(*device, parentblk, buf) == -1)
err(FATAL, err_rdpar);
ent = (struct pd_dirent *)(buf + PTRSZ + (parententry-1) * parententlen);
*block = ent->keyptr[0] + 256U * ent->keyptr[1];
ret:
if (fp)
close(fp);
}
/****************************************************************************/
/* END OF LANGUAGE CARD BANK 2 0xd400-x0dfff 3KB SEGMENT */
/****************************************************************************/
#pragma code-name (pop)
/*
* Parse mtime or ctime fields and populate the fields of the datetime struct
* Supports the legacy ProDOS date/time format as used by ProDOS 1.0->2.4.0
* and also the new format introduced with ProDOS 2.5.
*/
void readdatetime(uchar time[4], struct datetime *dt) {
uint d = time[0] + 256U * time[1];
uint t = time[2] + 256U * time[3];
if ((d == 0) && (t == 0)) {
dt->nodatetime = 1;
return;
}
dt->nodatetime = 0;
if (!(t & 0xe000)) {
/* ProDOS 1.0 to 2.4.2 date format */
dt->year = (d & 0xfe00) >> 9;
dt->month = (d & 0x01e0) >> 5;
dt->day = d & 0x001f;
dt->hour = (t & 0x1f00) >> 8;
dt->minute = t & 0x003f;
dt->ispd25format = 0;
if (dt->year < 40) /* See ProDOS-8 Tech Note 48 */
dt->year += 2000;
else
dt->year += 1900;
} else {
/* ProDOS 2.5.0+ */
dt->year = t & 0x0fff;
dt->month = ((t & 0xf000) >> 12) - 1;
dt->day = (d & 0xf800) >> 11;
dt->hour = (d & 0x07c0) >> 6;
dt->minute = d & 0x003f;
dt->ispd25format = 1;
}
}
/*
* Write the date and time stored in struct datetime in ProDOS on disk format,
* storing the bytes in array time[]. Supports both legacy format
* (ProDOS 1.0-2.4.2) and the new date and time format introduced
* with ProDOS 2.5
*/
void writedatetime(struct datetime *dt, uchar time[4]) {
uint d, t;
if (dt->nodatetime == 1) {
time[0] = time[1] = time[2] = time[3] = 0;
return;
}
if (dt->ispd25format == 0) {
/* ProDOS 1.0 to 2.4.2 date format */
uint year = dt->year;
if (year > 2039) /* 2039 is last year */
year = 2039;
if (year < 1940) /* 1940 is first year */
year = 1940;
if (year >= 2000)
year -= 2000;
if (year >= 1900)
year -= 1900;
d = (year << 9) | (dt->month << 5) | dt->day;
t = (dt->hour << 8) | dt->minute;
} else {
/* ProDOS 2.5.0+ */
t = ((dt->month + 1) << 12) | dt->year;
d = (dt->day << 11) | (dt->hour << 6) | dt->minute;
}
time[0] = d & 0xff;
time[1] = (d >> 8) & 0xff;
time[2] = t & 0xff;
time[3] = (t >> 8) & 0xff;
}
/*
* Print date/time value for directory listing
*/
void printdatetime(struct datetime *dt) {
if (dt->nodatetime)
fputs("-------- --:--", stderr);
else {
if (dt->ispd25format)
revers(1);
printf("%02d%02d%02d %02d:%02d",
dt->year, dt->month, dt->day, dt->hour, dt->minute);
revers(0);
}
}
/*
* Determine whether or not to perform a fix
* Return 0 not to perform fix, 1 to perform fix
*/
uint askfix(void) {
if (strlen(fixopts) == 0)
return 0;
fputs(": Fix (y/n)? ", stdout);
switch (fixopts[0]) {
case '-':
if (tolower(getchar()) == 'y')
return 1;
return 0;
case 'y':
fputs("y", stdout);
return 1;
default:
fputs("n", stdout);
return 0;
}
}
#ifdef FREELIST
/*
* Read the free list
*/
int readfreelist(uchar device) {
uint i, f;
char *p;
#ifdef AUXMEM
bzero(buf, BLKSZ);
for (i = 0; i < 16; ++i) {
copyaux(buf, freelist + i * BLKSZ, BLKSZ, TOAUX);
copyaux(buf, usedlist + i * BLKSZ, BLKSZ, TOAUX);
}
#else
bzero(freelist, FLSZ);
bzero(usedlist, FLSZ);
#endif
markused(0); /* Boot block */
markused(1); /* SOS boot block */
if (readdiskblock(device, 2, buf) == -1) {
err(NONFATAL, err_rdblk1, 2);
return -1;
}
flblk = f = buf[0x27] + 256U * buf[0x28];
totblks = buf[0x29] + 256U * buf[0x2a];
if (doverbose)
printf("Volume has %u blocks\n", totblks);
flsize = totblks / 4096U;
if ((totblks % 4096) > 0)
++flsize;
p = (char*)freelist;
for (i = 0; i < flsize; ++i) {
markused(f);
#ifdef AUXMEM
if (readdiskblock(device, f++, buf) == -1) {
#else
if (readdiskblock(device, f++, p) == -1) {
#endif
err(NONFATAL, err_rdfl);
return -1;
}
#ifdef AUXMEM
copyaux(buf, p, BLKSZ, TOAUX);
#endif
p += BLKSZ;
}
flloaded = 1;
return 0;
}
/*
* Determine if block blk is free or not
*/
int isfree(uint blk) {
uchar temp;
uint idx = blk / 8;
uint bit = blk % 8;
#ifdef AUXMEM
copyaux(freelist + idx, &temp, 1, FROMAUX);
return (temp << bit) & 0x80 ? 1 : 0;
#else
return (freelist[idx] << bit) & 0x80 ? 1 : 0;
#endif
}
/*
* Determine if block blk is used or not
*/
int isused(uint blk) {
uchar temp;
uint idx = blk / 8;
uint bit = blk % 8;
#ifdef AUXMEM
copyaux(usedlist + idx, &temp, 1, FROMAUX);
return (temp << bit) & 0x80 ? 1 : 0;
#else
return (usedlist[idx] << bit) & 0x80 ? 1 : 0;
#endif
}
/*
* Mark a block as used
*/
void markused(uint blk) {
uchar temp;
uint idx = blk / 8;
uint bit = blk % 8;
#ifdef AUXMEM
copyaux(usedlist + idx, &temp, 1, FROMAUX);
temp |= (0x80 >> bit);
copyaux(&temp, usedlist + idx, 1, TOAUX);
#else
usedlist[idx] |= (0x80 >> bit);
#endif
}
/*
* Mark a block as not used and add it to freelist
*/
void trimdirblock(uint blk) {
uchar temp;
uint idx = blk / 8;
uint bit = blk % 8;
#ifdef AUXMEM
copyaux(usedlist + idx, &temp, 1, FROMAUX);
temp &= ~(0x80 >> bit);
copyaux(&temp, usedlist + idx, 1, TOAUX);
copyaux(freelist + idx, &temp, 1, FROMAUX);
temp |= (0x80 >> bit);
copyaux(&temp, freelist + idx, 1, TOAUX);
#else
usedlist[idx] &= ~(0x80 >> bit);
freelist[idx] |= (0x80 >> bit);
#endif
flchanged = 1;
}
/*
* Perform all the operations to check a block which is used by
* a directory or file. Complains if the block is on the free-list
* and also if we have encountered this block in a previous file or dir.
*/
void checkblock(uint blk, char *msg) {
if (isfree(blk))
err(WARN, err_blfree2, msg, blk);
if (isused(blk))
err(WARN, err_blused2, msg, blk);
markused(blk);
}
#endif
#ifdef CHECK
/*
* Count the blocks in a seedling file
*/
#ifdef FREELIST
int seedlingblocks(uchar, uint keyblk, uint *blkcnt) {
checkblock(keyblk, "Data");
#else
int seedlingblocks(uchar, uint, uint *blkcnt) {
#endif
*blkcnt = 1;