source: thomson/tools/sap/libsap.c@ 134f1c4

main
Last change on this file since 134f1c4 was 134f1c4, checked in by Adrien Destugues <pulkomandy@…>, 12 years ago

Add sapfs (Vital Motion modified code...)

git-svn-id: svn://localhost/thomson@21 85ae3b6b-dc8f-4344-a89d-598714f2e4e5

  • Property mode set to 100644
File size: 43.6 KB
Line 
1/* LibSAP
2 * Version 0.9.4
3 * Copyright (C) 2000-2003 Eric Botcazou
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20
21#include <ctype.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <time.h>
26#include "libsap.h"
27
28
29int sap_errno;
30
31
32#define FILENAME_LENGTH 128
33#define MAX_ARCHIVE 8
34
35#define SAP_MAGIC_NUM 0xB3
36#define SAP_HEADER_SIZE 66
37static const char sap_header[]="\0SYSTEME D'ARCHIVAGE PUKALL S.A.P. "
38 "(c) Alexandre PUKALL Avril 1998";
39
40
41static const int sap_format_table[][3] = {
42 { SAP_NTRACKS1, SAP_SECTSIZE1, SAP_TRACKSIZE1 },
43 { SAP_NTRACKS2, SAP_SECTSIZE2, SAP_TRACKSIZE2 }
44};
45
46#define SAP_NTRACKS(f) sap_format_table[f-SAP_FORMAT1][0]
47#define SAP_SECTSIZE(f) sap_format_table[f-SAP_FORMAT1][1]
48#define SAP_TRACKSIZE(f) sap_format_table[f-SAP_FORMAT1][2]
49
50#define SAP_EXTSECTSIZE(f) (SAP_SECTSIZE(f) + sizeof(sapsector_t) - SAP_SECTSIZE1)
51
52
53static short int crcpuk_temp;
54
55static short int puktable[]={
56 0x0000, 0x1081, 0x2102, 0x3183,
57 0x4204, 0x5285, 0x6306, 0x7387,
58 0x8408, 0x9489, 0xa50a, 0xb58b,
59 0xc60c, 0xd68d, 0xe70e, 0xf78f
60};
61
62
63#define NO_ARCHIVE 0
64#define EMPTY_ARCHIVE 1
65#define FILLED_ARCHIVE 2
66#define FULL_ARCHIVE 3
67
68static struct {
69 int format;
70 int state;
71 int ntracks;
72 FILE *file;
73} archive[MAX_ARCHIVE+1];
74
75#define ID_FORMAT(id) archive[id].format
76#define ID_STATE(id) archive[id].state
77#define ID_NTRACKS(id) archive[id].ntracks
78#define ID_FILE(id) archive[id].file
79
80
81/* constants for the TO logical disk format */
82#define TO_NSECTS 16
83#define TO_SECTOR_PER_BLOCK 8
84
85#define TO_SECTSIZE1 255 /* not 256 */
86#define TO_FAT_START1 257
87#define TO_NBLOCKS1 160
88#define TO_BLOCKSIZE1 (TO_SECTOR_PER_BLOCK*TO_SECTSIZE1)
89#define TO_FILESIZE_MAX1 (TO_NBLOCKS1-2)
90#define TO_DIR_START1 512
91
92#define TO_SECTSIZE2 127 /* not 128 */
93#define TO_FAT_START2 129
94#define TO_NBLOCKS2 80
95#define TO_BLOCKSIZE2 (TO_SECTOR_PER_BLOCK*TO_SECTSIZE2)
96#define TO_FILESIZE_MAX2 (TO_NBLOCKS2-2)
97#define TO_DIR_START2 256
98
99#define TO_FILLER_BYTE 0xE5
100#define TO_TAG_RESERVED 0xFE
101#define TO_TAG_FREE 0xFF
102#define TO_END_BLOCK_OFFSET 0xC0
103
104#define TO_DIRENTRY_LENGTH 32
105#define TO_NAME 0
106#define TO_NAME_LENGTH 8
107#define TO_EXT 8
108#define TO_EXT_LENGTH 3
109#define TO_FILE_TYPE 11
110#define TO_DATA_TYPE 12
111#define TO_FIRST_BLOCK 13
112#define TO_END_SIZE 14
113#define TO_COMMENT 16
114#define TO_COMMENT_LENGTH 8
115#define TO_DATE_DAY 24
116#define TO_DATE_MONTH 25
117#define TO_DATE_YEAR 26
118#define TO_CHG_MODE 30
119#define TO_CHG_CHECKSUM 31
120
121#define TO_DIRENTRY_PER_SECTOR1 8
122#define TO_NDIRENTRIES1 (TO_NSECTS-2)*TO_DIRENTRY_PER_SECTOR1
123
124#define TO_DIRENTRY_PER_SECTOR2 4
125#define TO_NDIRENTRIES2 (TO_NSECTS-2)*TO_DIRENTRY_PER_SECTOR2
126
127static const int to_format_table[][8] = {
128 { TO_SECTSIZE1, TO_FAT_START1, TO_NBLOCKS1, TO_BLOCKSIZE1,
129 TO_FILESIZE_MAX1, TO_DIR_START1, TO_DIRENTRY_PER_SECTOR1, TO_NDIRENTRIES1 },
130 { TO_SECTSIZE2, TO_FAT_START2, TO_NBLOCKS2, TO_BLOCKSIZE2,
131 TO_FILESIZE_MAX2, TO_DIR_START2, TO_DIRENTRY_PER_SECTOR2, TO_NDIRENTRIES2 }
132};
133
134#define TO_SECTSIZE(f) to_format_table[f-SAP_FORMAT1][0]
135#define TO_FAT_START(f) to_format_table[f-SAP_FORMAT1][1]
136#define TO_NBLOCKS(f) to_format_table[f-SAP_FORMAT1][2]
137#define TO_BLOCKSIZE(f) to_format_table[f-SAP_FORMAT1][3]
138#define TO_FILESIZE_MAX(f) to_format_table[f-SAP_FORMAT1][4]
139#define TO_DIR_START(f) to_format_table[f-SAP_FORMAT1][5]
140#define TO_DIRENTRY_PER_SECTOR(f) to_format_table[f-SAP_FORMAT1][6]
141#define TO_NDIRENTRIES(f) to_format_table[f-SAP_FORMAT1][7]
142
143
144/* constants for the TO memory layout */
145#define TO_BANKSIZE 16384
146
147
148/* file types */
149enum {
150 FTYPE_BASIC,
151 FTYPE_DATA,
152 FTYPE_MACHINE,
153 FTYPE_ASM,
154 FTYPE_OTHERS,
155 FTYPE_PARAGRAPHE = 0xA
156};
157
158static char ftype_sym[5] = { 'B', 'D', 'M', 'A', '*' };
159
160enum {
161 DTYPE_BINARY,
162 DTYPE_ASCII = 0xFF,
163 DTYPE_OTHERS = 2
164};
165
166static char dtype_sym[3] = { 'B', 'A', '*' };
167
168struct extension_t {
169 char tag[TO_EXT_LENGTH+1];
170 unsigned char ftype;
171 unsigned char dtype;
172};
173
174enum {
175 EXT_TYPE_BAS,
176 EXT_TYPE_DAT,
177 EXT_TYPE_ASC,
178 EXT_TYPE_ASM,
179 EXT_TYPE_BIN,
180 EXT_TYPE_MAP,
181 EXT_TYPE_CHG,
182 EXT_TYPE_CFG,
183 EXT_TYPE_BAT,
184 EXT_TYPE_CAR,
185 EXT_TYPE_PAR,
186 EXT_TYPE_UNKNOWN,
187 EXT_TYPE_MAX
188};
189
190static struct extension_t ext_type[EXT_TYPE_MAX] = {
191 { "BAS", FTYPE_BASIC, DTYPE_BINARY },
192 { "DAT", FTYPE_DATA, DTYPE_ASCII },
193 { "ASC", FTYPE_DATA, DTYPE_ASCII },
194 { "ASM", FTYPE_ASM, DTYPE_ASCII },
195 { "BIN", FTYPE_MACHINE, DTYPE_BINARY },
196 { "MAP", FTYPE_MACHINE, DTYPE_BINARY },
197 { "CHG", FTYPE_DATA, DTYPE_ASCII },
198 { "CFG", FTYPE_MACHINE, DTYPE_BINARY },
199 { "BAT", FTYPE_BASIC, DTYPE_BINARY },
200 { "CAR", FTYPE_DATA, DTYPE_ASCII },
201 { "PAR", FTYPE_PARAGRAPHE, DTYPE_BINARY },
202 { "???", FTYPE_MACHINE, DTYPE_BINARY }
203};
204
205
206#define MIN(x,y) (((x) < (y)) ? (x) : (y))
207
208
209
210/************************************************/
211/*** private functions: helper routines ***/
212/************************************************/
213
214
215/* get_id:
216 * Finds the first empty id, returns 0 if has failed.
217 */
218static int get_id(void)
219{
220 int i;
221
222 for (i=MAX_ARCHIVE; i>0; i--) {
223 if (archive[i].state == NO_ARCHIVE)
224 break;
225 }
226
227 return i;
228}
229
230
231
232/* clean_string:
233 * Helper function to clean up character strings.
234 */
235static inline void clean_string(unsigned char *str)
236{
237 while (*str) {
238 if ((*str<32) || (*str>127))
239 *str = '#';
240
241 str++;
242 }
243}
244
245
246
247/************************************************/
248/*** private functions: SAP format support ***/
249/************************************************/
250
251
252/* crc_pukall:
253 * Computes the new CRC from the c data.
254 */
255static void crc_pukall(short int c)
256{
257 short int index;
258
259 index = (crcpuk_temp ^ c) & 0xf;
260 crcpuk_temp = ((crcpuk_temp>>4) & 0xfff) ^ puktable[index];
261
262 c >>= 4;
263
264 index = (crcpuk_temp ^ c) & 0xf;
265 crcpuk_temp = ((crcpuk_temp>>4) & 0xfff) ^ puktable[index];
266}
267
268
269
270/* do_crc:
271 * Computes the CRC for the specified SAP sector.
272 */
273static void do_crc(sapsector_t *sapsector, int format)
274{
275 int i;
276
277 crcpuk_temp = 0xffff;
278
279 crc_pukall(sapsector->format);
280 crc_pukall(sapsector->protection);
281 crc_pukall(sapsector->track);
282 crc_pukall(sapsector->sector);
283
284 for (i=0; i<SAP_SECTSIZE(format); i++)
285 crc_pukall(sapsector->data[i]);
286}
287
288
289
290/* do_read_sector:
291 * Performs the low-level read operation for the specified sector.
292 */
293static void do_read_sector(sapID id, sapsector_t *sapsector)
294{
295 unsigned char buffer[sizeof(sapsector_t)];
296 int format = ID_FORMAT(id);
297 int i;
298
299 i=fread(buffer, sizeof(char), SAP_EXTSECTSIZE(format), ID_FILE(id));
300
301 sapsector->format = buffer[0];
302 sapsector->protection = buffer[1];
303 sapsector->track = buffer[2];
304 sapsector->sector = buffer[3];
305
306 for (i=0; i<SAP_SECTSIZE(format); i++)
307 sapsector->data[i] = buffer[4+i]^SAP_MAGIC_NUM;
308
309 sapsector->data[SAP_SECTSIZE(format)] = buffer[4+i];
310 sapsector->data[SAP_SECTSIZE(format)+1] = buffer[4+i+1];
311}
312
313
314
315/* do_write_sector:
316 * Performs the low-level write operation for the specified sector.
317 */
318static void do_write_sector(sapID id, sapsector_t *sapsector)
319{
320 unsigned char buffer[sizeof(sapsector_t)];
321 int format = ID_FORMAT(id);
322 int i;
323
324 /* fill in the write buffer */
325 buffer[0] = sapsector->format;
326 buffer[1] = sapsector->protection;
327 buffer[2] = sapsector->track;
328 buffer[3] = sapsector->sector;
329
330 for (i=0; i<SAP_SECTSIZE(format); i++)
331 buffer[4+i] = sapsector->data[i]^SAP_MAGIC_NUM;
332
333 /* compute the CRC */
334 do_crc(sapsector, format);
335 buffer[4+i] = sapsector->data[SAP_SECTSIZE(format)] = (crcpuk_temp>>8)&0xff;
336 buffer[4+i+1] = sapsector->data[SAP_SECTSIZE(format)+1] = crcpuk_temp&0xff;
337
338 fwrite(buffer, sizeof(char), SAP_EXTSECTSIZE(format), ID_FILE(id));
339}
340
341
342
343/* seek_pos:
344 * Seeks the specified position in the SAP archive.
345 */
346static void seek_pos(sapID id, int track, int sect)
347{
348 int pos;
349
350 pos = SAP_HEADER_SIZE + (track*SAP_NSECTS + sect-1) * SAP_EXTSECTSIZE(ID_FORMAT(id));
351
352 fseek(ID_FILE(id), pos, SEEK_SET);
353}
354
355
356
357/************************************************/
358/*** private functions: Thomson DOS support ***/
359/************************************************/
360
361
362
363/* get_file_size:
364 * Returns the size in blocks of the specified file on disk.
365 */
366static int get_file_size(int format, int n, const unsigned char trk20_data[])
367{
368 const unsigned char *fat_data, *entry_data;
369 int block, size = 0;
370
371 fat_data = trk20_data + TO_FAT_START(format);
372 entry_data = trk20_data + TO_DIR_START(format) + n*TO_DIRENTRY_LENGTH;
373
374 block = entry_data[TO_FIRST_BLOCK];
375
376 while ((block<TO_NBLOCKS(format)) && (size<TO_FILESIZE_MAX(format))) {
377 size++;
378 block = fat_data[block];
379 }
380
381 return size;
382}
383
384
385
386/* get_dskf:
387 * Returns the amount of free space in blocks on disk.
388 */
389static int get_dskf(int format, const unsigned char trk20_data[])
390{
391 const unsigned char *fat_data;
392 int i, dskf = 0;
393
394 fat_data = trk20_data + TO_FAT_START(format);
395
396 for (i=0; i<TO_NBLOCKS(format); i++) {
397 if (fat_data[i] == TO_TAG_FREE)
398 dskf++;
399 }
400
401 return dskf;
402}
403
404
405
406/* extract_dir_entry:
407 * Extracts one dir entry and returns the number of characters.
408 */
409static int extract_dir_entry(int format, char buffer[], int buffer_size, int n, const unsigned char trk20_data[])
410{
411 const unsigned char *entry_data;
412 char name[TO_NAME_LENGTH+1], ext[TO_EXT_LENGTH+1], date[9], *comment;
413 unsigned char ftype, dtype;
414 int i, len, size;
415
416 entry_data = trk20_data + TO_DIR_START(format) + n*TO_DIRENTRY_LENGTH;
417
418 comment = malloc(TO_COMMENT_LENGTH+1);
419
420 if ((entry_data[TO_NAME] == 0) || (entry_data[TO_NAME] == TO_TAG_FREE))
421 return 0;
422
423 if (entry_data[TO_NAME] == 1) {
424 /* display only comment (if any) */
425 if (entry_data[TO_COMMENT]) {
426 comment[0] = '\0';
427 strncat(comment,(char*)(entry_data+TO_COMMENT), TO_COMMENT_LENGTH);
428
429 for (i=1; i<TO_COMMENT_LENGTH; i++) {
430 if (comment[i] == 18) { /* DC2 (repetition) ? */
431 len = strlen(comment);
432 comment = realloc(comment, len + comment[i+1]);
433 memmove(comment+i+comment[i+1], comment+i+2, len - (i+2) + 1);
434 memset(comment+i, comment[i-1], comment[i+1]);
435 }
436 }
437
438 return sprintf(buffer, "%30s%s\n", "", comment);
439 }
440 else {
441 return 0;
442 }
443 }
444
445 /* name */
446 name[0] = 0;
447 strncat(name,(char*)(entry_data+TO_NAME), TO_NAME_LENGTH);
448 clean_string((unsigned char*)name);
449
450 /* extension */
451 ext[0] = 0;
452 strncat(ext,(char*)(entry_data+TO_EXT), TO_EXT_LENGTH);
453 clean_string((unsigned char *)ext);
454
455 /* file type */
456 ftype = entry_data[TO_FILE_TYPE];
457 ftype = (ftype > 3 ? ftype_sym[4] : ftype_sym[ftype]);
458
459 /* data type */
460 dtype = entry_data[TO_DATA_TYPE];
461 dtype = (dtype == 0 ? dtype_sym[0] : (dtype == 0xFF ? dtype_sym[1] : dtype_sym[2]));
462
463 /* size */
464 size = get_file_size(format, n, trk20_data) * ((TO_BLOCKSIZE(format) + 1024) / 1024);
465
466 /* date */
467 if ((entry_data[TO_DATE_DAY] >= 1) && (entry_data[TO_DATE_DAY] <= 31) &&
468 (entry_data[TO_DATE_MONTH] >= 1) && (entry_data[TO_DATE_MONTH] <= 12) &&
469 (entry_data[TO_DATE_YEAR] <= 99))
470 sprintf(date, "%2d-%2d-%2d", entry_data[TO_DATE_DAY], entry_data[TO_DATE_MONTH], entry_data[TO_DATE_YEAR]);
471 else
472 date[0] = 0;
473
474 /* comment */
475 comment[0] = 0;
476 if (entry_data[TO_COMMENT])
477 strncat(comment,(char*)(entry_data+TO_COMMENT), TO_COMMENT_LENGTH);
478
479 /* display the entry */
480 i = sprintf(buffer, "%-8s %-3s %c %c %-3d %-8s %s\n", name, ext, ftype, dtype, size, date, comment);
481 free(comment);
482
483 return i;
484
485 (void) buffer_size; /* DJGPP 2.03 lacks snprintf() */
486}
487
488
489
490/* get_filename:
491 * Detects whether the file entry is valid and returns the filename if so.
492 */
493static int get_filename(char filename[], const unsigned char entry_data[])
494{
495 int i, j;
496
497 if ((entry_data[TO_NAME] == 0) || (entry_data[TO_NAME] == TO_TAG_FREE))
498 return -1;
499
500 /* name */
501 strncpy(filename,(char*)(entry_data+TO_NAME), TO_NAME_LENGTH);
502
503 for (i=0; i<TO_NAME_LENGTH; i++) {
504 if (filename[i] == 32)
505 break;
506 }
507
508 filename[i++] = '.';
509 filename[i] = 0;
510
511 /* extension */
512 strncat(filename,(char*)(entry_data+TO_EXT), TO_EXT_LENGTH);
513
514 for (j=0; j<TO_EXT_LENGTH; j++) {
515 if (filename[i+j] == 32)
516 break;
517 }
518
519 if (filename[i+j-1] == '.')
520 filename[i+j-1] = 0;
521 else
522 filename[i+j] = 0;
523
524 return 0;
525}
526
527
528
529/* decode_filename:
530 * Decodes the specified raw filename into a valid dir entry.
531 */
532static void decode_filename(unsigned char entry_data[], const char filename[], int file_size)
533{
534 const char *p;
535 char shortname[128];
536 char ext[TO_EXT_LENGTH+1];
537 int i, type, len, ext_len = 0;
538 FILE *file;
539
540 /* zero the entry data */
541 memset(entry_data + TO_NAME, 32, TO_NAME_LENGTH + TO_EXT_LENGTH);
542 entry_data[TO_COMMENT] = 0;
543 memset(entry_data + TO_COMMENT+1, 32, TO_COMMENT_LENGTH-1);
544 memset(entry_data + TO_DATE_DAY, 0, TO_DIRENTRY_LENGTH - TO_DATE_DAY);
545
546 /* find short name */
547 len = strlen(filename);
548 p = filename + len - 1;
549
550 while ((p>=filename) && (*p != '/'))
551 p--;
552
553 shortname[0] = 0;
554 strncat(shortname, p+1, sizeof(shortname) - 1);
555
556 /* find extension */
557 len = strlen(shortname);
558 p = shortname + len - 1;
559
560 while ((p>=shortname) && (*p != '.'))
561 p--;
562
563 if (p < shortname) { /* no extension? */
564 memcpy(entry_data+TO_NAME, shortname, MIN(TO_NAME_LENGTH, len));
565 type = EXT_TYPE_MAX-1;
566 }
567 else {
568 /* name */
569 memcpy(entry_data+TO_NAME, shortname, MIN(TO_NAME_LENGTH, p - shortname));
570
571 /* extension */
572 ext_len = MIN(TO_EXT_LENGTH, shortname + len - 1 - p);
573 memcpy(entry_data+TO_EXT, p+1, ext_len);
574
575 /* build upper case extension */
576 ext[0] = 0;
577 strncat(ext, p+1, ext_len);
578 for (i=0; i<ext_len; i++)
579 ext[i] = toupper(ext[i]);
580
581 /* search for a standard extension */
582 for (type=0; type<EXT_TYPE_MAX-1; type++) {
583 if (strcmp(ext_type[type].tag, ext) == 0)
584 break;
585 }
586 }
587
588 /* set file and data types */
589 entry_data[TO_FILE_TYPE] = ext_type[type].ftype;
590 entry_data[TO_DATA_TYPE] = ext_type[type].dtype;
591
592 /* special treatment */
593 switch (type) {
594
595 case EXT_TYPE_BAS:
596 /* differentiate ASCII from BINARY data */
597 file = fopen(filename, "rb");
598 if (file) {
599 if ((fgetc(file) == '\r') && (fgetc(file) == '\n'))
600 entry_data[TO_DATA_TYPE] = DTYPE_ASCII;
601 fclose(file);
602 }
603 break;
604
605 case EXT_TYPE_CHG:
606 /* automatic launch + number of requested banks */
607 entry_data[TO_CHG_MODE] = 0x80 | ((file_size/TO_BANKSIZE)&0x3F);
608
609 /* 8-bit checksum */
610 for (i=0; i<TO_NAME_LENGTH; i++)
611 entry_data[TO_CHG_CHECKSUM] += entry_data[i];
612 break;
613 }
614}
615
616
617
618/* seek_file:
619 * Seeks a file in the specified directory and returns its index number if found.
620 */
621static int seek_file(int format, const char filename[], const unsigned char dir_data[])
622{
623 char entry_name[TO_NAME_LENGTH + 1 + TO_EXT_LENGTH + 1];
624 int i;
625
626 for (i=0; i<TO_NDIRENTRIES(format); i++) {
627 if ((get_filename(entry_name, dir_data+i*TO_DIRENTRY_LENGTH) == 0) && (strcmp(entry_name, filename) == 0))
628 return i;
629 }
630
631 return -1;
632}
633
634
635
636/* wildcard handling code by Michael Bulkin (M.A.Bukin@inp.nsk.su) */
637#define WCD_MATCH_TRY 0
638#define WCD_MATCH_ONE 1
639#define WCD_MATCH_ANY 2
640
641
642struct WCD_MATCH_DATA
643{
644 int type;
645 const char *s1;
646 const char *s2;
647};
648
649
650
651/* wcdcmp:
652 * Compares two strings ('*' matches any number of characters,
653 * '?' matches any character).
654 */
655static int wcdcmp(const char *s1, const char *s2)
656{
657 static unsigned int size = 0;
658 static struct WCD_MATCH_DATA *data = NULL;
659 const char *s1end;
660 int index, c1, c2;
661
662 /* handle NULL arguments */
663 if ((!s1) && (!s2)) {
664 if (data) {
665 free(data);
666 data = NULL;
667 }
668
669 return 1;
670 }
671
672 s1end = s1 + strlen(s1);
673
674 /* allocate larger working area if necessary */
675 if (data && (size < strlen(s2))) {
676 free(data);
677 data = NULL;
678 }
679
680 if (!data) {
681 size = strlen(s2);
682 data = malloc(sizeof(struct WCD_MATCH_DATA) * size * 2 + 1);
683 if (!data)
684 return 1;
685 }
686
687 index = 0;
688 data[0].s1 = s1;
689 data[0].s2 = s2;
690 data[0].type = WCD_MATCH_TRY;
691
692 while (index >= 0) {
693 s1 = data[index].s1;
694 s2 = data[index].s2;
695 c1 = *s1;
696 c2 = *s2;
697
698 switch (data[index].type) {
699
700 case WCD_MATCH_TRY:
701 if (c2 == 0) {
702 /* pattern exhausted */
703 if (c1 == 0)
704 return 0;
705 else
706 index--;
707 }
708 else if (c1 == 0) {
709 /* string exhausted */
710 while (*s2 == '*')
711 s2++;
712 if (*s2 == 0)
713 return 0;
714 else
715 index--;
716 }
717 else if (c2 == '*') {
718 /* try to match the rest of pattern with empty string */
719 data[index++].type = WCD_MATCH_ANY;
720 data[index].s1 = s1end;
721 data[index].s2 = s2 + 1;
722 data[index].type = WCD_MATCH_TRY;
723 }
724 else if ((c2 == '?') || (c1 == c2)) {
725 /* try to match the rest */
726 data[index++].type = WCD_MATCH_ONE;
727 data[index].s1 = s1 + 1;
728 data[index].s2 = s2 + 1;
729 data[index].type = WCD_MATCH_TRY;
730 }
731 else
732 index--;
733 break;
734
735 case WCD_MATCH_ONE:
736 /* the rest of string did not match, try earlier */
737 index--;
738 break;
739
740 case WCD_MATCH_ANY:
741 /* rest of string did not match, try add more chars to string tail */
742 if (--data[index + 1].s1 >= s1) {
743 data[index + 1].type = WCD_MATCH_TRY;
744 index++;
745 }
746 else
747 index--;
748 break;
749
750 default:
751 /* this is a bird? This is a plane? No it's a bug!!! */
752 return 1;
753 }
754 }
755
756 return 1;
757}
758
759
760
761/* seek_wilcard:
762 * Seeks a wildcard in the specified directory and returns its index number if found.
763 */
764static int seek_wildcard(int format, char filename[], const char pattern[], const unsigned char dir_data[])
765{
766 char entry_name[TO_NAME_LENGTH + 1 + TO_EXT_LENGTH + 1];
767 static int i = 0;
768 int j;
769
770 while (i<TO_NDIRENTRIES(format)) {
771 j = i++;
772
773 if ((get_filename(entry_name, dir_data+j*TO_DIRENTRY_LENGTH) == 0) && (wcdcmp(entry_name, pattern) == 0)) {
774 strcpy(filename, entry_name);
775 return j;
776 }
777 }
778
779 i = 0;
780
781 /* to avoid leaking memory */
782 wcdcmp(NULL, NULL);
783
784 return -1;
785}
786
787
788
789/* find_free_block_sym:
790 * Finds a free block using a symmetrical search.
791 */
792static int find_free_block_sym(int format, unsigned char fat_data[])
793{
794 int block;
795
796 block = TO_NBLOCKS(format)/2 - 1;
797
798 while (block < TO_NBLOCKS(format)) {
799 if (fat_data[block] == TO_TAG_FREE)
800 return block;
801
802 if (fat_data[TO_NBLOCKS(format) - block - 2] == TO_TAG_FREE) /* fat_data[-1] is always 0 */
803 return TO_NBLOCKS(format) - block - 2;
804
805 block++;
806 }
807
808 return -1;
809}
810
811
812
813/* find_free_block_rel:
814 * Finds a free block using a relative search.
815 */
816static int find_free_block_rel(int format, int block, unsigned char fat_data[])
817{
818 if (block < TO_NBLOCKS(format)/2) {
819 while (block >= 0) {
820 if (fat_data[block] == TO_TAG_FREE)
821 break;
822
823 block--;
824 }
825 }
826 else {
827 while (block < TO_NBLOCKS(format)) {
828 if (fat_data[block] == TO_TAG_FREE)
829 break;
830
831 block++;
832 }
833 }
834
835 if ((block<0) || (block>=TO_NBLOCKS(format)))
836 block = find_free_block_sym(format, fat_data);
837
838 return block;
839}
840
841
842
843/* do_add_file:
844 * Performs the low-level add operation for the specified file entry.
845 */
846static void do_add_file(sapID id, FILE *file, int file_size, unsigned char entry_data[], int n, unsigned char trk20_data[])
847{
848 sapsector_t sapsector;
849 unsigned char *fat_data;
850 unsigned int block, next_block;
851 int sect;
852 int dummy;
853
854 /* find the first block */
855 fat_data = trk20_data + TO_FAT_START(ID_FORMAT(id));
856
857 block = find_free_block_sym(ID_FORMAT(id), fat_data);
858 entry_data[TO_FIRST_BLOCK] = block;
859
860 sapsector.format = 0;
861 sapsector.protection = 0;
862
863 /* write full blocks */
864 while (file_size > TO_BLOCKSIZE(ID_FORMAT(id))) {
865 sapsector.track = block/2;
866 sapsector.sector = 1 + (block%2 ? TO_SECTOR_PER_BLOCK : 0);
867 seek_pos(id, sapsector.track, sapsector.sector);
868
869 for (sect=0; sect<TO_SECTOR_PER_BLOCK; sect++) {
870 dummy=fread(sapsector.data, sizeof(char), TO_SECTSIZE(ID_FORMAT(id)), file);
871 do_write_sector(id, &sapsector);
872 sapsector.sector++;
873 }
874
875 fat_data[block] = TO_TAG_RESERVED; /* temporarily reserved */
876 next_block = find_free_block_rel(ID_FORMAT(id), block, fat_data);
877
878 fat_data[block] = next_block;
879 block = next_block;
880
881 file_size -= TO_BLOCKSIZE(ID_FORMAT(id));
882 }
883
884 /* write remaining full sectors */
885 sapsector.track = block/2;
886 sapsector.sector = 1 + (block%2 ? TO_SECTOR_PER_BLOCK : 0);
887 seek_pos(id, sapsector.track, sapsector.sector);
888
889 fat_data[block] = TO_END_BLOCK_OFFSET + 1;
890
891 while (file_size > TO_SECTSIZE(ID_FORMAT(id))) {
892 dummy=fread(sapsector.data, sizeof(char), TO_SECTSIZE(ID_FORMAT(id)), file);
893 do_write_sector(id, &sapsector);
894 sapsector.sector++;
895
896 fat_data[block]++;
897 file_size -= TO_SECTSIZE(ID_FORMAT(id));
898 }
899
900 /* write remaining individual bytes */
901 dummy=fread(sapsector.data, sizeof(char), TO_SECTSIZE(ID_FORMAT(id)), file);
902 do_write_sector(id, &sapsector);
903
904 entry_data[TO_END_SIZE] = file_size>>8;
905 entry_data[TO_END_SIZE+1] = file_size;
906
907 /* write the new entry into the directory */
908 memcpy(trk20_data + TO_DIR_START(ID_FORMAT(id)) + n*TO_DIRENTRY_LENGTH, entry_data, TO_DIRENTRY_LENGTH);
909}
910
911
912
913/* do_delete_file:
914 * Performs the low-level delete operation for the specified file entry and
915 * returns the size of file.
916 */
917static int do_delete_file(sapID id, const char filename[], int n, unsigned char trk20_data[])
918{
919 unsigned char *fat_data, *entry_data;
920 int block, old_block, size = 0;
921
922 (void) filename;
923
924 fat_data = trk20_data + TO_FAT_START(ID_FORMAT(id));
925 entry_data = trk20_data + TO_DIR_START(ID_FORMAT(id)) + n*TO_DIRENTRY_LENGTH;
926
927 /* delete the file entry */
928 entry_data[TO_NAME] = 0;
929
930 /* read start block */
931 block = entry_data[TO_FIRST_BLOCK];
932
933 /* mark FAT entries with the TO_TAG_FREE tag */
934 while ((block<TO_NBLOCKS(ID_FORMAT(id))) && (size<TO_FILESIZE_MAX(ID_FORMAT(id)))) {
935 old_block = block;
936 block = fat_data[block];
937 fat_data[old_block] = TO_TAG_FREE;
938 size++;
939 }
940
941 size *= TO_BLOCKSIZE(ID_FORMAT(id));
942
943 if (block > TO_END_BLOCK_OFFSET+1)
944 size += (block - TO_END_BLOCK_OFFSET - 1) * TO_SECTSIZE(ID_FORMAT(id));
945
946 size += (entry_data[TO_END_SIZE]<<8) + entry_data[TO_END_SIZE+1];
947
948 return size;
949}
950
951
952
953/* do_extract_file:
954 * Performs the low-level extract operation for the specified file entry and
955 * returns the size of file.
956 */
957static int do_extract_file(sapID id, const char filename[], int n, unsigned char trk20_data[])
958{
959 sapsector_t sapsector;
960 unsigned char *fat_data, *entry_data;
961 int sect, end_size, size = 0;
962 unsigned int block;
963 FILE *file;
964
965 fat_data = trk20_data + TO_FAT_START(ID_FORMAT(id));
966 entry_data = trk20_data + TO_DIR_START(ID_FORMAT(id)) + n*TO_DIRENTRY_LENGTH;
967
968 /* read start block */
969 block = entry_data[TO_FIRST_BLOCK];
970
971 if ((file=fopen(filename, "wb")) == NULL) {
972 sap_errno = SAP_EPERM;
973 return 0;
974 }
975
976 /* extract full blocks */
977 while ((fat_data[block]<TO_NBLOCKS(ID_FORMAT(id))) && (size<TO_FILESIZE_MAX(ID_FORMAT(id)))) {
978 seek_pos(id, block/2, 1 + (block%2 ? TO_SECTOR_PER_BLOCK : 0));
979
980 for (sect=0; sect<TO_SECTOR_PER_BLOCK; sect++) {
981 do_read_sector(id, &sapsector);
982 fwrite(sapsector.data, sizeof(char), TO_SECTSIZE(ID_FORMAT(id)), file);
983 }
984
985 block = fat_data[block];
986 size++;
987 }
988
989 size *= TO_BLOCKSIZE(ID_FORMAT(id));
990
991 /* extract remaining full sectors */
992 seek_pos(id, block/2, 1 + (block%2 ? TO_SECTOR_PER_BLOCK : 0));
993
994 for (sect=0; sect<(fat_data[block] - TO_END_BLOCK_OFFSET - 1); sect++) {
995 do_read_sector(id, &sapsector);
996 fwrite(sapsector.data, sizeof(char), TO_SECTSIZE(ID_FORMAT(id)), file);
997 size += TO_SECTSIZE(ID_FORMAT(id));
998 }
999
1000 /* extract remaining individual bytes */
1001 do_read_sector(id, &sapsector);
1002 end_size = (entry_data[TO_END_SIZE]<<8) + entry_data[TO_END_SIZE+1];
1003 fwrite(sapsector.data, sizeof(char), end_size, file);
1004 size += end_size;
1005
1006 fclose(file);
1007
1008 return size;
1009}
1010
1011
1012
1013/************************************************/
1014/*** public low-level functions ***/
1015/************************************************/
1016
1017
1018/* _ExtractDir:
1019 * Extract the directory from track 20 and returns the number of lines.
1020 */
1021int _ExtractDir(char buffer[], int buffer_size, int drive, int density, const unsigned char trk20_data[])
1022{
1023 unsigned char disk_name[TO_NAME_LENGTH+1];
1024 int dskf, i, len, pos, lines = 0;
1025 int format = (density == 1 ? SAP_FORMAT2 : SAP_FORMAT1);
1026
1027 /* name of the volume */
1028 strncpy((char*)disk_name, (char*)trk20_data, TO_NAME_LENGTH);
1029
1030 if ((disk_name[0] == 0) || (disk_name[0] == TO_TAG_FREE)) {
1031 strcpy((char*)disk_name, "No Name ");
1032 }
1033 else {
1034 disk_name[TO_NAME_LENGTH] = 0;
1035 clean_string(disk_name);
1036 }
1037
1038 /* dskf */
1039 dskf = get_dskf(format, trk20_data) * ((TO_BLOCKSIZE(format) + 1024) / 1024);
1040
1041 /* header */
1042 pos = sprintf(buffer, "%s density %d:%s DSKF = %d\n", (density == 1 ? "Single" : "Double"), drive, disk_name, dskf);
1043 lines++;
1044
1045 /* directory entries */
1046 for (i=0; i<TO_NDIRENTRIES(format); i++) {
1047 len = extract_dir_entry(format, buffer+pos, buffer_size-pos, i, trk20_data);
1048
1049 if (len>0) {
1050 pos += len;
1051 lines++;
1052 }
1053 }
1054
1055 return lines;
1056}
1057
1058
1059
1060/* _ForEachFile:
1061 * Executes the specified callback funtion on each file matching the pattern.
1062 */
1063int _ForEachFile(sapID id, const char pattern[], sapfilecb_t callback, int save_back)
1064{
1065 char filename[TO_NAME_LENGTH + 1 + TO_EXT_LENGTH + 1];
1066 unsigned char trk20_data[SAP_TRACKSIZE1], *dir_data;
1067 int n, ret = 0;
1068
1069 switch (ID_STATE(id)) {
1070
1071 case NO_ARCHIVE:
1072 sap_errno = SAP_EINVAL;
1073 return 0;
1074
1075 case EMPTY_ARCHIVE:
1076 sap_errno = SAP_EBUSY;
1077 return 0;
1078
1079 case FILLED_ARCHIVE:
1080 sap_errno = SAP_EBUSY;
1081 return 0;
1082
1083 case FULL_ARCHIVE:
1084 break;
1085 }
1086
1087 /* read track 20 */
1088 sap_ReadSectorEx(id, 20, 1, SAP_NSECTS, trk20_data);
1089
1090 dir_data = trk20_data + TO_DIR_START(ID_FORMAT(id));
1091
1092 if (strpbrk(pattern, "?*")) { /* wildcards? */
1093 while ((n=seek_wildcard(ID_FORMAT(id), filename, pattern, dir_data)) >= 0)
1094 ret += callback(id, filename, n, trk20_data);
1095 }
1096 else {
1097 n = seek_file(ID_FORMAT(id), pattern, dir_data);
1098
1099 if (n<0)
1100 sap_errno = SAP_ENOENT;
1101 else
1102 ret = callback(id, pattern, n, trk20_data);
1103 }
1104
1105 if (save_back) {
1106 /* save track 20 */
1107 sap_WriteSectorEx(id, 20, 1, SAP_NSECTS, trk20_data);
1108 }
1109
1110 return ret;
1111}
1112
1113
1114
1115/************************************************/
1116/*** API functions: physical format support ***/
1117/************************************************/
1118
1119
1120/* OpenArchive:
1121 * Opens an existing archive,
1122 * returns the sapID on success or SAP_ERROR on error.
1123 */
1124sapID sap_OpenArchive(const char filename[], int *format)
1125{
1126 char header[SAP_HEADER_SIZE];
1127 sapID id;
1128 int dummy;
1129
1130 /* seek for an empty id */
1131 if (!(id=get_id())) {
1132 sap_errno = SAP_ETOOMANY;
1133 return SAP_ERROR;
1134 }
1135
1136 if ((ID_FILE(id)=fopen(filename, "rb+")) == NULL) {
1137 sap_errno = SAP_ENOENT;
1138 return SAP_ERROR;
1139 }
1140
1141 /* read the header */
1142 dummy=fread(header, sizeof(char), SAP_HEADER_SIZE, ID_FILE(id));
1143
1144 /* find the format */
1145 if ((header[0] != SAP_FORMAT1) && (header[0] != SAP_FORMAT2)) {
1146 fclose(ID_FILE(id));
1147 sap_errno = SAP_EBADF;
1148 return SAP_ERROR;
1149 }
1150
1151 *format = header[0];
1152
1153 /* check the header */
1154 header[0] = 0;
1155
1156 if (strncmp(header, sap_header, SAP_HEADER_SIZE) != 0) {
1157 fclose(ID_FILE(id));
1158 sap_errno = SAP_EBADF;
1159 return SAP_ERROR;
1160 }
1161
1162 ID_FORMAT(id) = *format;
1163 ID_STATE(id) = FULL_ARCHIVE;
1164 ID_NTRACKS(id) = SAP_NTRACKS(*format);
1165
1166 return id;
1167}
1168
1169
1170
1171/* CreateArchive:
1172 * Creates an archive skeleton and writes the header,
1173 * returns the sapID on success or SAP_ERROR on error.
1174 */
1175sapID sap_CreateArchive(const char filename[], int format)
1176{
1177 char header[SAP_HEADER_SIZE];
1178 sapID id;
1179
1180 if ((format != SAP_FORMAT1) && (format != SAP_FORMAT2)) {
1181 sap_errno = SAP_EINVAL;
1182 return SAP_ERROR;
1183 }
1184
1185 /* seek for an empty id */
1186 if (!(id=get_id())) {
1187 sap_errno = SAP_ETOOMANY;
1188 return SAP_ERROR;
1189 }
1190
1191 if ((ID_FILE(id)=fopen(filename, "wb")) == NULL) {
1192 sap_errno = SAP_EPERM;
1193 return SAP_ERROR;
1194 }
1195
1196 /* write the header */
1197 memcpy(header, sap_header, SAP_HEADER_SIZE);
1198 header[0] = format;
1199
1200 fwrite(header, sizeof(char), SAP_HEADER_SIZE, ID_FILE(id));
1201
1202 ID_FILE(id) = freopen(filename, "rb+", ID_FILE(id));
1203 seek_pos(id, 0 ,1);
1204
1205 ID_FORMAT(id) = format;
1206 ID_STATE(id) = EMPTY_ARCHIVE;
1207 ID_NTRACKS(id) = 0;
1208
1209 return id;
1210}
1211
1212
1213
1214/* CloseArchive:
1215 * Closes the archive, filling it up with empty tracks if needed,
1216 * returns SAP_OK on success or SAP_ERROR on error.
1217 */
1218int sap_CloseArchive(sapID id)
1219{
1220 sapsector_t sapsector;
1221 int format = ID_FORMAT(id);
1222 int track, sect;
1223
1224 switch (ID_STATE(id)) {
1225
1226 case NO_ARCHIVE:
1227 sap_errno = SAP_EINVAL;
1228 return SAP_ERROR;
1229
1230 case EMPTY_ARCHIVE:
1231 case FILLED_ARCHIVE:
1232 sapsector.format = 0;
1233 sapsector.protection = 0;
1234 memset(sapsector.data, TO_FILLER_BYTE, SAP_SECTSIZE(format));
1235
1236 for (track=ID_NTRACKS(id); track<SAP_NTRACKS(format); track++) {
1237 for (sect=1; sect<=SAP_NSECTS; sect++) {
1238 sapsector.track = track;
1239 sapsector.sector = sect;
1240 do_write_sector(id, &sapsector);
1241 }
1242 }
1243 break;
1244
1245 case FULL_ARCHIVE:
1246 break;
1247 }
1248
1249 fclose(ID_FILE(id));
1250 ID_STATE(id) = NO_ARCHIVE;
1251
1252 return SAP_OK;
1253}
1254
1255
1256
1257/* FillArchive:
1258 * Fills an empty archive sector by sector, it's up to
1259 * the user to call it the right number of times,
1260 * returns SAP_OK on success or SAP_ERROR on error.
1261 */
1262int sap_FillArchive(sapID id, sapsector_t *sapsector)
1263{
1264 static int sect;
1265
1266 switch (ID_STATE(id)) {
1267
1268 case NO_ARCHIVE:
1269 sap_errno = SAP_EINVAL;
1270 return SAP_ERROR;
1271
1272 case EMPTY_ARCHIVE:
1273 ID_STATE(id) = FILLED_ARCHIVE;
1274 sect = 1;
1275 /* no break */
1276
1277 case FILLED_ARCHIVE:
1278 do_write_sector(id, sapsector);
1279
1280 if (++sect == SAP_NSECTS+1) {
1281 sect = 1;
1282
1283 if (++ID_NTRACKS(id) == SAP_NTRACKS(ID_FORMAT(id)))
1284 ID_STATE(id) = FULL_ARCHIVE;
1285 }
1286 break;
1287
1288 case FULL_ARCHIVE:
1289 sap_errno = SAP_ENOSPC;
1290 return SAP_ERROR;
1291 } /* end of switch */
1292
1293 return SAP_OK;
1294}
1295
1296
1297
1298/* ReadSector:
1299 * Reads the specified sector and returns SAP_OK or a flagged code:
1300 * SAP_NO_STD_FMT: non standard format
1301 * SAP_PROTECTED : protected sector
1302 * SAP_BAD_SECTOR: bad sector identifiers
1303 * SAP_CRC_ERROR : CRC error
1304 */
1305int sap_ReadSector(sapID id, int track, int sect, sapsector_t *sapsector)
1306{
1307 int flag = SAP_OK;
1308 int format = ID_FORMAT(id);
1309
1310 switch (ID_STATE(id)) {
1311
1312 case NO_ARCHIVE:
1313 sap_errno = SAP_EINVAL;
1314 return SAP_ERROR;
1315
1316 case EMPTY_ARCHIVE:
1317 sap_errno = SAP_EEMPTY;
1318 return SAP_ERROR;
1319
1320 case FILLED_ARCHIVE:
1321 sap_errno = SAP_EBUSY;
1322 return SAP_ERROR;
1323
1324 case FULL_ARCHIVE:
1325 break;
1326 }
1327
1328 seek_pos(id, track, sect);
1329 do_read_sector(id, sapsector);
1330
1331 if (sapsector->format != 0)
1332 flag |= SAP_NO_STD_FMT;
1333
1334 if (sapsector->protection != 0)
1335 flag |= SAP_PROTECTED;
1336
1337 if ((sapsector->track != track) || (sapsector->sector != sect))
1338 flag |= SAP_BAD_SECTOR;
1339
1340 do_crc(sapsector, format);
1341
1342 if ((sapsector->data[SAP_SECTSIZE(format)] != ((crcpuk_temp>>8)&0xff))
1343 || (sapsector->data[SAP_SECTSIZE(format)] != (crcpuk_temp&0xff)))
1344 flag |= SAP_CRC_ERROR;
1345
1346 return flag;
1347}
1348
1349
1350
1351/* ReadSectorEx:
1352 * Reads one or more sectors from the same track,
1353 * returns SAP_OK on success or SAP_ERROR on error.
1354 */
1355int sap_ReadSectorEx(sapID id, int track, int sect, int nsects, unsigned char data[])
1356{
1357 sapsector_t sapsector;
1358 int format = ID_FORMAT(id);
1359 int i;
1360
1361 switch (ID_STATE(id)) {
1362
1363 case NO_ARCHIVE:
1364 sap_errno = SAP_EINVAL;
1365 return SAP_ERROR;
1366
1367 case EMPTY_ARCHIVE:
1368 sap_errno = SAP_EEMPTY;
1369 return SAP_ERROR;
1370
1371 case FILLED_ARCHIVE:
1372 sap_errno = SAP_EBUSY;
1373 return SAP_ERROR;
1374
1375 case FULL_ARCHIVE:
1376 break;
1377 }
1378
1379 seek_pos(id, track, sect);
1380
1381 for (i=0; i<nsects; i++) {
1382 do_read_sector(id, &sapsector);
1383 memcpy(data + i*SAP_SECTSIZE(format), sapsector.data, SAP_SECTSIZE(format));
1384 }
1385
1386 return SAP_OK;
1387}
1388
1389
1390
1391/* WriteSector:
1392 * Writes the specified sector and
1393 * returns SAP_OK on success or SAP_ERROR on error.
1394 */
1395int sap_WriteSector(sapID id, int track, int sect, sapsector_t *sapsector)
1396{
1397 switch (ID_STATE(id)) {
1398
1399 case NO_ARCHIVE:
1400 sap_errno = SAP_EINVAL;
1401 return SAP_ERROR;
1402
1403 case EMPTY_ARCHIVE:
1404 sap_errno = SAP_EEMPTY;
1405 return SAP_ERROR;
1406
1407 case FILLED_ARCHIVE:
1408 sap_errno = SAP_EBUSY;
1409 return SAP_ERROR;
1410
1411 case FULL_ARCHIVE:
1412 break;
1413 }
1414
1415 seek_pos(id, track, sect);
1416 do_write_sector(id, sapsector);
1417
1418 return SAP_OK;
1419}
1420
1421
1422
1423/* WriteSectorEx:
1424 * Writes one or more sectors into the same track,
1425 * returns SAP_OK on success or SAP_ERROR on error.
1426 */
1427int sap_WriteSectorEx(sapID id, int track, int sect, int nsects, const unsigned char data[])
1428{
1429 sapsector_t sapsector;
1430 int format = ID_FORMAT(id);
1431 int i;
1432
1433 switch (ID_STATE(id)) {
1434
1435 case NO_ARCHIVE:
1436 sap_errno = SAP_EINVAL;
1437 return SAP_ERROR;
1438
1439 case EMPTY_ARCHIVE:
1440 sap_errno = SAP_EEMPTY;
1441 return SAP_ERROR;
1442
1443 case FILLED_ARCHIVE:
1444 sap_errno = SAP_EBUSY;
1445 return SAP_ERROR;
1446
1447 case FULL_ARCHIVE:
1448 break;
1449 }
1450
1451 seek_pos(id, track, sect);
1452
1453 sapsector.format = 0;
1454 sapsector.protection = 0;
1455 sapsector.track = track;
1456
1457 for (i=0; i<nsects; i++) {
1458 memcpy(sapsector.data, data + i*SAP_SECTSIZE(format), SAP_SECTSIZE(format));
1459 sapsector.sector = sect + i;
1460 do_write_sector(id, &sapsector);
1461 }
1462
1463 return SAP_OK;
1464}
1465
1466
1467
1468/************************************************/
1469/*** API functions: logical format support ***/
1470/************************************************/
1471
1472
1473/* FormatArchive:
1474 * Formats an archive using the Thomson BASIC DOS format,
1475 * returns SAP_OK on success or SAP_ERROR on error.
1476 */
1477int sap_FormatArchive(sapID id, int capacity)
1478{
1479 int format = ID_FORMAT(id);
1480 int track, sect;
1481
1482 sapsector_t sapsector;
1483
1484 switch (ID_STATE(id)) {
1485
1486 case NO_ARCHIVE:
1487 sap_errno = SAP_EINVAL;
1488 return SAP_ERROR;
1489
1490 case EMPTY_ARCHIVE:
1491 break;
1492
1493 case FILLED_ARCHIVE:
1494 sap_errno = SAP_EBUSY;
1495 return SAP_ERROR;
1496
1497 case FULL_ARCHIVE:
1498 seek_pos(id, 0, 1);
1499 break;
1500 }
1501
1502 if (!(capacity == SAP_TRK40) && !(capacity == SAP_TRK80)) {
1503 sap_errno = SAP_EINVAL;
1504 return SAP_ERROR;
1505 }
1506
1507 sapsector.format = 0;
1508 sapsector.protection = 0;
1509 memset(sapsector.data, TO_FILLER_BYTE, SAP_SECTSIZE(format));
1510
1511 for (track=0; track<SAP_NTRACKS(format); track++) {
1512 for (sect=1; sect<SAP_NSECTS+1; sect++) {
1513 sapsector.track = track;
1514 sapsector.sector = sect;
1515 do_write_sector(id, &sapsector);
1516 }
1517 }
1518
1519 /* write track 20 */
1520 sapsector.track = 20;
1521 memset(sapsector.data, TO_TAG_FREE, SAP_SECTSIZE(format));
1522 seek_pos(id, 20, 1);
1523
1524 for (sect=1; sect<SAP_NSECTS+1; sect++) {
1525 sapsector.sector = sect;
1526 do_write_sector(id, &sapsector);
1527 }
1528
1529 /* FAT */
1530 sapsector.track = 20;
1531 sapsector.sector = 2;
1532 memset(sapsector.data, TO_TAG_RESERVED, SAP_SECTSIZE(format));
1533
1534 /* first byte */
1535 sapsector.data[0] = 0;
1536
1537 /* lower zone */
1538 memset(sapsector.data + 1, TO_TAG_FREE, 40);
1539
1540 /* upper zone */
1541 if (capacity == SAP_TRK80) /* not (format == SAP_FORMAT1) */
1542 memset(sapsector.data + 43, TO_TAG_FREE, TO_NBLOCKS1 - 43 + 1);
1543 else
1544 memset(sapsector.data + 43, TO_TAG_FREE, TO_NBLOCKS2 - 43 + 1);
1545
1546 seek_pos(id, 20, 2);
1547 do_write_sector(id, &sapsector);
1548
1549 ID_STATE(id) = FULL_ARCHIVE;
1550 ID_NTRACKS(id) = SAP_NTRACKS(format);
1551
1552 return SAP_OK;
1553}
1554
1555
1556
1557/* ListArchive:
1558 * Builds a list of files contained in the archive,
1559 * returns the number of lines on success or 0 on error.
1560 */
1561int sap_ListArchive(sapID id, char buffer[], int buffer_size)
1562{
1563 unsigned char trk20_data[SAP_TRACKSIZE1];
1564
1565 switch (ID_STATE(id)) {
1566
1567 case NO_ARCHIVE:
1568 sap_errno = SAP_EINVAL;
1569 return 0;
1570
1571 case EMPTY_ARCHIVE:
1572 sap_errno = SAP_EEMPTY;
1573 return 0;
1574
1575 case FILLED_ARCHIVE:
1576 sap_errno = SAP_EBUSY;
1577 return 0;
1578
1579 case FULL_ARCHIVE:
1580 break;
1581 }
1582
1583 /* read track 20 */
1584 sap_ReadSectorEx(id, 20, 1, SAP_NSECTS, trk20_data);
1585
1586 return _ExtractDir(buffer, buffer_size, 0, ID_FORMAT(id) == SAP_FORMAT1 ? 2 : 1, trk20_data);
1587}
1588
1589
1590
1591/* AddFile:
1592 * Adds the specified file to the archive,
1593 * returns the size of the file in bytes on success or 0 on error.
1594 */
1595int sap_AddFile(sapID id, const char filename[])
1596{
1597 unsigned char entry_data[TO_DIRENTRY_LENGTH];
1598 unsigned char trk20_data[SAP_TRACKSIZE1], *fat_data, *dir_data;
1599 int free_n=-1, prev_n=-1;
1600 int i, dskf, file_size = 0;
1601 FILE *file;
1602
1603 switch (ID_STATE(id)) {
1604
1605 case NO_ARCHIVE:
1606 sap_errno = SAP_EINVAL;
1607 return 0;
1608
1609 case EMPTY_ARCHIVE:
1610 sap_errno = SAP_EBUSY;
1611 return 0;
1612
1613 case FILLED_ARCHIVE:
1614 sap_errno = SAP_EBUSY;
1615 return 0;
1616
1617 case FULL_ARCHIVE:
1618 break;
1619 }
1620
1621 /* open the file */
1622 if ((file=fopen(filename, "rb")) == NULL) {
1623 sap_errno = SAP_ENOENT;
1624 return 0;
1625 }
1626
1627 /* find size of the file */
1628 while (fgetc(file) != EOF)
1629 file_size++;
1630
1631 if (file_size == 0) {
1632 fclose(file);
1633 sap_errno = SAP_ENFILE;
1634 return 0;
1635 }
1636
1637 fseek(file, 0, SEEK_SET);
1638
1639 /* decode the filename */
1640 decode_filename(entry_data, filename, file_size);
1641
1642 /* read track 20 */
1643 sap_ReadSectorEx(id, 20, 1, SAP_NSECTS, trk20_data);
1644
1645 fat_data = trk20_data + TO_FAT_START(ID_FORMAT(id));
1646 dir_data = trk20_data + TO_DIR_START(ID_FORMAT(id));
1647
1648 /* simultaneously seek already free entry and previous entry */
1649 for (i=0; i<TO_NDIRENTRIES(ID_FORMAT(id)); i++) {
1650 if ((dir_data[i*TO_DIRENTRY_LENGTH] == 0) || (dir_data[i*TO_DIRENTRY_LENGTH] == TO_TAG_FREE)) {
1651 if (free_n<0)
1652 free_n = i;
1653 }
1654 else {
1655 if (strncmp((char*)(dir_data+i*TO_DIRENTRY_LENGTH),(char*) entry_data, TO_NAME_LENGTH + TO_EXT_LENGTH) == 0) {
1656 prev_n = i;
1657 break;
1658 }
1659 }
1660 }
1661
1662 if ((free_n<0) && (prev_n<0)) {
1663 fclose(file);
1664 sap_errno = SAP_ENOSPC;
1665 return 0;
1666 }
1667
1668 /* test for enough free disk space */
1669 dskf = get_dskf(ID_FORMAT(id), trk20_data);
1670
1671 if (prev_n >=0)
1672 dskf += get_file_size(ID_FORMAT(id), prev_n, trk20_data);
1673
1674 if ((dskf*TO_BLOCKSIZE(ID_FORMAT(id))) < file_size) {
1675 sap_errno = SAP_EFBIG;
1676 fclose(file);
1677 return 0;
1678 }
1679
1680 /* delete previous entry */
1681 if (prev_n >= 0) {
1682 do_delete_file(id, filename, prev_n, trk20_data);
1683 free_n = prev_n;
1684 }
1685
1686 /* phew! we can finally add the file... */
1687 do_add_file(id, file, file_size, entry_data, free_n, trk20_data);
1688
1689 /* update directory and FAT */
1690 sap_WriteSectorEx(id, 20, 1, SAP_NSECTS, trk20_data);
1691
1692 fclose(file);
1693
1694 return file_size;
1695}
1696
1697
1698
1699/* DeleteFile:
1700 * Deletes the specified files from the archive,
1701 * returns the total size of the files in bytes on success or 0 on error.
1702 */
1703int sap_DeleteFile(sapID id, const char pattern[])
1704{
1705 return _ForEachFile(id, pattern, do_delete_file, 1);
1706}
1707
1708
1709
1710/* ExtractFile:
1711 * Extracts the specified files from the archive,
1712 * returns the total size of the files in bytes on success or 0 on error.
1713 */
1714int sap_ExtractFile(sapID id, const char pattern[])
1715{
1716 return _ForEachFile(id, pattern, do_extract_file, 0);
1717}
1718
1719
1720
1721/* GetFileInfo:
1722 * Fills in a structure with the info for the specified file
1723 * returns SAP_OK on success or SAP_ERROR on error.
1724 */
1725int sap_GetFileInfo(sapID id, const char filename[], sapfileinfo_t *info)
1726{
1727 unsigned char trk20_data[SAP_TRACKSIZE1], *dir_data, *fat_data, *entry_data;
1728 struct tm tim;
1729 int n, block, i=0;
1730
1731 switch (ID_STATE(id)) {
1732
1733 case NO_ARCHIVE:
1734 sap_errno = SAP_EINVAL;
1735 return 0;
1736
1737 case EMPTY_ARCHIVE:
1738 sap_errno = SAP_EEMPTY;
1739 return 0;
1740
1741 case FILLED_ARCHIVE:
1742 sap_errno = SAP_EBUSY;
1743 return 0;
1744
1745 case FULL_ARCHIVE:
1746 break;
1747 }
1748
1749 /* read track 20 */
1750 sap_ReadSectorEx(id, 20, 1, SAP_NSECTS, trk20_data);
1751
1752 dir_data = trk20_data + TO_DIR_START(ID_FORMAT(id));
1753
1754 n = seek_file(ID_FORMAT(id), filename, dir_data);
1755
1756 if (n<0) {
1757 sap_errno = SAP_ENOENT;
1758 return SAP_ERROR;
1759 }
1760
1761 fat_data = trk20_data + TO_FAT_START(ID_FORMAT(id));
1762 entry_data = trk20_data + TO_DIR_START(ID_FORMAT(id)) + n*TO_DIRENTRY_LENGTH;
1763
1764 /* block chain */
1765 info->nblocks = get_file_size(ID_FORMAT(id), n, trk20_data);
1766 info->block = malloc(info->nblocks*sizeof(int));
1767
1768 block = entry_data[TO_FIRST_BLOCK];
1769
1770 while ((block<TO_NBLOCKS(ID_FORMAT(id))) && (i<TO_FILESIZE_MAX(ID_FORMAT(id)))) {
1771 info->block[i++] = block;
1772 block = fat_data[block];
1773 }
1774
1775 /* common attributes */
1776 info->size = info->nblocks*TO_BLOCKSIZE(ID_FORMAT(id)) + entry_data[TO_END_SIZE];
1777 info->file_type = entry_data[TO_FILE_TYPE];
1778 info->data_type = entry_data[TO_DATA_TYPE];
1779
1780 /* date */
1781 if ((entry_data[TO_DATE_DAY] >= 1) && (entry_data[TO_DATE_DAY] <= 31) &&
1782 (entry_data[TO_DATE_MONTH] >= 1) && (entry_data[TO_DATE_MONTH] <= 12) &&
1783 (entry_data[TO_DATE_YEAR] <= 99)) {
1784 memset(&tim, 0, sizeof(struct tm));
1785 tim.tm_mday = entry_data[TO_DATE_DAY];
1786 tim.tm_mon = entry_data[TO_DATE_MONTH];
1787 tim.tm_year = entry_data[TO_DATE_YEAR];
1788 info->date = mktime(&tim);
1789 }
1790 else {
1791 info->date = 0;
1792 }
1793
1794 /* comment */
1795 info->comment[0] = '\0';
1796 if (entry_data[TO_COMMENT])
1797 strncat(info->comment,(char*)( entry_data+TO_COMMENT), TO_COMMENT_LENGTH);
1798
1799 return SAP_OK;
1800}
1801
Note: See TracBrowser for help on using the repository browser.