1 /** \file fsdevice-open.c
2 * \brief File system device
3 *
4 * \author Andreas Boose <viceteam@t-online.de>
5 * \author Teemu Rantanen <tvr@cs.hut.fi>
6 * \author Jarkko Sonninen <sonninen@lut.fi>
7 * \author Jouko Valta <jopi@stekt.oulu.fi>
8 * \author Olaf Seibert <rhialto@mbfys.kun.nl>
9 * \author Andre Fachat <a.fachat@physik.tu-chemnitz.de>
10 * \author Ettore Perazzoli <ettore@comm2000.it>
11 * \author pottendo <pottendo@gmx.net>
12 */
13
14 /*
15 * This file is part of VICE, the Versatile Commodore Emulator.
16 * See README for copyright notice.
17 *
18 * This program is free software; you can redistribute it and/or modify
19 * it under the terms of the GNU General Public License as published by
20 * the Free Software Foundation; either version 2 of the License, or
21 * (at your option) any later version.
22 *
23 * This program is distributed in the hope that it will be useful,
24 * but WITHOUT ANY WARRANTY; without even the implied warranty of
25 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
26 * GNU General Public License for more details.
27 *
28 * You should have received a copy of the GNU General Public License
29 * along with this program; if not, write to the Free Software
30 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
31 * 02111-1307 USA.
32 *
33 */
34
35 #include "vice.h"
36
37 /* #define DEBUG_DRIVEOPEN */
38
39 #include <ctype.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43
44 #include "archdep.h"
45 #include "cbmdos.h"
46 #include "charset.h"
47 #include "fileio.h"
48 #include "fsdevice-filename.h"
49 #include "fsdevice-open.h"
50 #include "fsdevice-read.h"
51 #include "fsdevice-resources.h"
52 #include "fsdevice-write.h"
53 #include "fsdevicetypes.h"
54 #include "ioutil.h"
55 #include "lib.h"
56 #include "log.h"
57 #include "resources.h"
58 #include "tape.h"
59 #include "vdrive-command.h"
60 #include "vdrive.h"
61 #include "util.h"
62
63 #ifdef DEBUG_DRIVEOPEN
64 #define DBG(x) printf x
65 #else
66 #define DBG(x)
67 #endif
68
69 /* shorten the path shown in the disk header to 16 characters */
makeshortheader(uint8_t * p)70 static uint8_t *makeshortheader(uint8_t *p)
71 {
72 int longnames = 0;
73 size_t n;
74 uint8_t *d;
75
76 if (resources_get_int("FSDeviceLongNames", &longnames) < 0) {
77 return p;
78 }
79 n = strlen((char*)p);
80 if (!longnames && (n > 16)) {
81 d = p + (n - 1);
82 /* scan backwards until path seperator */
83 while (d != p) {
84 if (*d == '/') { /* FIXME: use macro */
85 d++; n = 0;
86 /* copy last part to the beginning */
87 while (d) {
88 p[n] = *d;
89 n++;
90 if (n == 16) {
91 break;
92 }
93 d++;
94 }
95 p[n] = 0;
96 return p;
97 }
98 d--;
99 }
100 }
101 return p;
102 }
103
fsdevice_open_directory(vdrive_t * vdrive,unsigned int secondary,bufinfo_t * bufinfo,cbmdos_cmd_parse_t * cmd_parse,char * rname)104 static int fsdevice_open_directory(vdrive_t *vdrive, unsigned int secondary,
105 bufinfo_t *bufinfo,
106 cbmdos_cmd_parse_t *cmd_parse, char *rname)
107 {
108 struct ioutil_dir_s *ioutil_dir;
109 char *mask;
110 uint8_t *p;
111 int i;
112
113 if ((secondary != 0) || (bufinfo[secondary].mode != Read)) {
114 fsdevice_error(vdrive, CBMDOS_IPE_NOT_WRITE);
115 return FLOPPY_ERROR;
116 }
117
118 if (!(mask = strrchr(rname, '/'))) {
119 mask = rname;
120 }
121
122 /* Test on wildcards. */
123 if (cbmdos_parse_wildcard_check(mask, (unsigned int)strlen(mask))) {
124 if (*mask == '/') {
125 strcpy(bufinfo[secondary].dirmask, mask + 1);
126 *mask++ = 0;
127 } else {
128 strcpy(bufinfo[secondary].dirmask, mask);
129 lib_free(cmd_parse->parsecmd);
130 cmd_parse->parsecmd = lib_strdup(fsdevice_get_path(vdrive->unit));
131 }
132 } else {
133 bufinfo[secondary].dirmask[0] = '\0';
134 if (!*(cmd_parse->parsecmd)) {
135 lib_free(cmd_parse->parsecmd);
136 cmd_parse->parsecmd = lib_strdup(fsdevice_get_path(vdrive->unit));
137 }
138 }
139
140 /* trying to open */
141 ioutil_dir = ioutil_opendir((char *)(cmd_parse->parsecmd), IOUTIL_OPENDIR_ALL_FILES);
142 if (ioutil_dir == NULL) {
143 for (p = (uint8_t *)(cmd_parse->parsecmd); *p; p++) {
144 if (isupper((int)*p)) {
145 *p = tolower((int)*p);
146 }
147 }
148 ioutil_dir = ioutil_opendir((char *)(cmd_parse->parsecmd), IOUTIL_OPENDIR_ALL_FILES);
149 if (ioutil_dir == NULL) {
150 fsdevice_error(vdrive, CBMDOS_IPE_NOT_FOUND);
151 return FLOPPY_ERROR;
152 }
153 }
154 strcpy(bufinfo[secondary].dir, cmd_parse->parsecmd);
155 /*
156 * Start Address, Line Link and Line number 0
157 */
158
159 p = bufinfo[secondary].name;
160
161 *p++ = 1;
162 *p++ = 4;
163
164 *p++ = 1;
165 *p++ = 1;
166
167 *p++ = 0;
168 *p++ = 0;
169
170 *p++ = (uint8_t)0x12; /* Reverse on */
171
172 *p++ = '"';
173 strcpy((char *)p, bufinfo[secondary].dir); /* Dir name */
174 charset_petconvstring((uint8_t *)p, 0); /* ASCII name to PETSCII */
175 i = 0;
176
177 makeshortheader(p);
178
179 while (*p) {
180 ++p;
181 i++;
182 }
183 while (i < 16) {
184 *p++ = ' ';
185 i++;
186 }
187
188 /* put the drive-unit and drive number into the "format id" */
189 *p++ = '"';
190 *p++ = ' ';
191 if (vdrive->drive < 10) {
192 *p++ = ' ';
193 *p++ = '#';
194 *p++ = '0' + vdrive->unit;
195 } else {
196 *p++ = '#';
197 *p++ = '1';
198 *p++ = '0' + (vdrive->unit - 10);
199 }
200 *p++ = ':';
201 *p++ = '0' + vdrive->drive;
202 *p++ = 0;
203
204 bufinfo[secondary].buflen = (int)(p - bufinfo[secondary].name);
205 bufinfo[secondary].bufp = bufinfo[secondary].name;
206 bufinfo[secondary].mode = Directory;
207 bufinfo[secondary].ioutil_dir = ioutil_dir;
208 bufinfo[secondary].eof = 0;
209
210 return FLOPPY_COMMAND_OK;
211 }
212
fsdevice_open_file(vdrive_t * vdrive,unsigned int secondary,bufinfo_t * bufinfo,cbmdos_cmd_parse_t * cmd_parse,char * rname)213 static int fsdevice_open_file(vdrive_t *vdrive, unsigned int secondary,
214 bufinfo_t *bufinfo,
215 cbmdos_cmd_parse_t *cmd_parse, char *rname)
216 {
217 char *comma;
218 char *newrname;
219 tape_image_t *tape;
220 unsigned int format = 0;
221 fileio_info_t *finfo;
222 int fileio_command;
223
224 if (fsdevice_convert_p00_enabled[(vdrive->unit) - 8]) {
225 format |= FILEIO_FORMAT_P00;
226 }
227 if (!fsdevice_hide_cbm_files_enabled[vdrive->unit - 8]) {
228 format |= FILEIO_FORMAT_RAW;
229 }
230
231 /* Remove comma. */
232 if ((cmd_parse->parsecmd)[0] == ',') {
233 (cmd_parse->parsecmd)[1] = '\0';
234 } else {
235 comma = strchr(cmd_parse->parsecmd, ',');
236 if (comma != NULL) {
237 *comma = '\0';
238 }
239 }
240
241 /* Test on wildcards. */
242 if (cbmdos_parse_wildcard_check(cmd_parse->parsecmd,
243 (unsigned int)strlen(cmd_parse->parsecmd)) > 0) {
244 if (bufinfo[secondary].mode == Write
245 || bufinfo[secondary].mode == Append) {
246 fsdevice_error(vdrive, CBMDOS_IPE_BAD_NAME);
247 return FLOPPY_ERROR;
248 }
249 }
250
251 /* Open file for write mode access. */
252 if (bufinfo[secondary].mode == Write) {
253
254 if (fsdevice_save_p00_enabled[vdrive->unit - 8]) {
255 format = FILEIO_FORMAT_P00;
256 } else {
257 format = FILEIO_FORMAT_RAW;
258 }
259
260 DBG(("fsdevice_open_file write '%s'\n", rname));
261 fsdevice_limit_createnamelength(vdrive, rname);
262 DBG(("fsdevice_open_file write limited: '%s'\n", rname));
263
264 if (cmd_parse->atsign) {
265 /* TODO: maybe rename to a backup name */
266 DBG(("fsdevice_open_file overwrite @'%s'\n", rname));
267 fileio_command = FILEIO_COMMAND_OVERWRITE;
268 } else if (fsdevice_overwrite_existing_files) {
269 fileio_command = FILEIO_COMMAND_OVERWRITE;
270 } else {
271 fileio_command = FILEIO_COMMAND_WRITE;
272 }
273
274 finfo = fileio_open(rname, fsdevice_get_path(vdrive->unit), format,
275 fileio_command, bufinfo[secondary].type,
276 &bufinfo[secondary].reclen);
277
278 if (finfo != NULL) {
279 bufinfo[secondary].fileio_info = finfo;
280 fsdevice_error(vdrive, CBMDOS_IPE_OK);
281 return FLOPPY_COMMAND_OK;
282 } else {
283 fsdevice_error(vdrive, CBMDOS_IPE_FILE_EXISTS);
284 return FLOPPY_ERROR;
285 }
286 }
287
288 if (bufinfo[secondary].mode == Append) {
289 /* Open file for append mode access. */
290 DBG(("fsdevice_open_file append '%s'\n", rname));
291 newrname = fsdevice_expand_shortname(vdrive, rname);
292 DBG(("fsdevice_open_file append expanded '%s'\n", newrname));
293 finfo = fileio_open(newrname, fsdevice_get_path(vdrive->unit), format,
294 FILEIO_COMMAND_APPEND_READ,
295 bufinfo[secondary].type,
296 &bufinfo[secondary].reclen);
297 lib_free(newrname);
298
299 if (finfo != NULL) {
300 bufinfo[secondary].fileio_info = finfo;
301 fsdevice_error(vdrive, CBMDOS_IPE_OK);
302 return FLOPPY_COMMAND_OK;
303 } else {
304 fsdevice_error(vdrive, CBMDOS_IPE_NOT_FOUND);
305 return FLOPPY_ERROR;
306 }
307 }
308
309 /* Open file for read or relative mode access. */
310 tape = bufinfo[secondary].tape;
311 tape->name = util_concat(fsdevice_get_path(vdrive->unit),
312 FSDEV_DIR_SEP_STR, rname, NULL);
313 charset_petconvstring((uint8_t *)(tape->name) +
314 strlen(fsdevice_get_path(vdrive->unit)) +
315 strlen(FSDEV_DIR_SEP_STR), 1);
316 tape->read_only = 1;
317 /* Prepare for buffered reads */
318 bufinfo[secondary].isbuffered = 0;
319 bufinfo[secondary].iseof = 0;
320 if (tape_image_open(tape) < 0) {
321 lib_free(tape->name);
322 tape->name = NULL;
323 } else {
324 tape_file_record_t *r;
325 static uint8_t startaddr[2];
326 tape_seek_start(tape);
327 tape_seek_to_file(tape, 0);
328 r = tape_get_current_file_record(tape);
329 if ((r->type == 1) || (r->type == 3)) {
330 startaddr[0] = r->start_addr & 255;
331 startaddr[1] = r->start_addr >> 8;
332 bufinfo[secondary].bufp = startaddr;
333 bufinfo[secondary].buflen = 2;
334 } else {
335 bufinfo[secondary].buflen = 0;
336 }
337
338 return FLOPPY_COMMAND_OK;
339 }
340
341
342 DBG(("fsdevice_open_file read '%s'\n", rname));
343 newrname = fsdevice_expand_shortname(vdrive, rname);
344 DBG(("fsdevice_open_file read expanded '%s'\n", newrname));
345
346 fileio_command =
347 bufinfo[secondary].mode == Relative ? FILEIO_COMMAND_READ_WRITE
348 : FILEIO_COMMAND_READ;
349
350 finfo = fileio_open(newrname, fsdevice_get_path(vdrive->unit), format,
351 fileio_command, bufinfo[secondary].type,
352 &bufinfo[secondary].reclen);
353
354 lib_free(newrname);
355
356 if (finfo != NULL) {
357 bufinfo[secondary].fileio_info = finfo;
358 fsdevice_error(vdrive, CBMDOS_IPE_OK);
359
360 if (bufinfo[secondary].mode == Relative) {
361 fsdevice_relative_switch_record(vdrive, &bufinfo[secondary], 0, 0);
362 }
363
364 return FLOPPY_COMMAND_OK;
365 }
366
367 fsdevice_error(vdrive, CBMDOS_IPE_NOT_FOUND);
368 return FLOPPY_ERROR;
369 }
370
fsdevice_open_buffer(vdrive_t * vdrive,unsigned int secondary,bufinfo_t * bufinfo,cbmdos_cmd_parse_t * cmd_parse,char * rname)371 static int fsdevice_open_buffer(vdrive_t *vdrive, unsigned int secondary,
372 bufinfo_t *bufinfo,
373 cbmdos_cmd_parse_t *cmd_parse, char *rname)
374 {
375 log_message(LOG_DEFAULT, "Fsdevice: Warning - open channel '%s'. (block access needs disk image)", rname);
376 fsdevice_error(vdrive, CBMDOS_IPE_OK);
377 return FLOPPY_COMMAND_OK;
378 }
379
fsdevice_open(vdrive_t * vdrive,const uint8_t * name,unsigned int length,unsigned int secondary,cbmdos_cmd_parse_t * cmd_parse_ext)380 int fsdevice_open(vdrive_t *vdrive, const uint8_t *name, unsigned int length,
381 unsigned int secondary, cbmdos_cmd_parse_t *cmd_parse_ext)
382 {
383 char *rname;
384 int status = 0, rc;
385 unsigned int i;
386 cbmdos_cmd_parse_t cmd_parse;
387 bufinfo_t *bufinfo;
388
389 DBG(("fsdevice_open name:'%s' (secondary:%u)\n", name, secondary));
390
391 bufinfo = fsdevice_dev[vdrive->unit - 8].bufinfo;
392
393 if (bufinfo[secondary].fileio_info != NULL) {
394 return FLOPPY_ERROR;
395 }
396
397 if (secondary == 15) {
398 for (i = 0; i < length; i++) {
399 status = fsdevice_write(vdrive, name[i], 15);
400 }
401 return status;
402 }
403 cmd_parse.cmd = name;
404 cmd_parse.cmdlength = length;
405 cmd_parse.secondary = secondary;
406
407 DBG(("fsdevice_open cmd_parse '%s'\n", name));
408
409 rc = cbmdos_command_parse(&cmd_parse);
410 if (rc != SERIAL_OK) {
411 status = SERIAL_ERROR;
412 goto out;
413 }
414
415 /*
416 Check for '@0:filename' or '@:filename'. Must include a ':'.
417 (original filename starts with '@', but parsed version doesn't)
418 '@filename' will open a file starting with '@'!
419 */
420 if (length > 0 && name[0] == '@' &&
421 !(cmd_parse.parselength > 0 && cmd_parse.parsecmd[0] == '@')) {
422 cmd_parse.atsign = 1;
423 }
424
425 bufinfo[secondary].type = cmd_parse.filetype;
426 bufinfo[secondary].reclen = cmd_parse.recordlength;
427 bufinfo[secondary].num_records = -1;
428
429 rname = lib_malloc(ioutil_maxpathlen());
430
431 cmd_parse.parsecmd[cmd_parse.parselength] = 0;
432 strncpy(rname, cmd_parse.parsecmd, cmd_parse.parselength + 1);
433
434 /* CBM name to FSname */
435 charset_petconvstring((uint8_t *)(cmd_parse.parsecmd), 1);
436 DBG(("fsdevice_open rname: %s\n", rname));
437
438 if (cmd_parse.filetype == CBMDOS_FT_REL) {
439 /* REL files override whatever rwmode has been inferred by
440 * parsecmd() */
441 bufinfo[secondary].mode = Relative;
442 } else {
443 switch (cmd_parse.readmode) {
444 case CBMDOS_FAM_WRITE:
445 bufinfo[secondary].mode = Write;
446 break;
447 case CBMDOS_FAM_READ:
448 bufinfo[secondary].mode = Read;
449 break;
450 case CBMDOS_FAM_APPEND:
451 bufinfo[secondary].mode = Append;
452 break;
453 }
454 }
455
456 /*
457 check wether the length of the name string does not match the
458 passed length. this might happen if a) the cbm filename in the
459 running program is zero terminated and b) a wrong namelen is
460 passed to SETNAM. this would almost certainly result in a "file
461 not found" - so it is the best we can do in that case, too.
462 */
463 if (strlen((const char*)name) != length) {
464 log_message(LOG_DEFAULT,
465 "Fsdevice: Warning - filename '%s' with bogus length '%u'.",
466 cmd_parse.parsecmd, length);
467 status = CBMDOS_IPE_NOT_FOUND;
468 goto out;
469 }
470
471 if (*name == '$') {
472 status = fsdevice_open_directory(vdrive, secondary, bufinfo, &cmd_parse, rname);
473 } else if (*name == '#') {
474 status = fsdevice_open_buffer(vdrive, secondary, bufinfo, &cmd_parse, rname);
475 } else {
476 status = fsdevice_open_file(vdrive, secondary, bufinfo, &cmd_parse, rname);
477 }
478
479 lib_free(rname);
480
481 if (status != FLOPPY_COMMAND_OK) {
482 goto out;
483 }
484
485 fsdevice_error(vdrive, CBMDOS_IPE_OK);
486
487 out:
488 lib_free(cmd_parse.parsecmd);
489
490 return status;
491 }
492