00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #ifdef SAVE_RCSID
00011 static char *rcsid =
00012 "@(#) $Id: main.c,v 1.21 2006/04/21 09:18:04 trikalio Exp $";
00013 #endif
00014
00015 #include <ctype.h>
00016 #include <getopt.h>
00017 #include <signal.h>
00018 #include <stdarg.h>
00019 #include <stdio.h>
00020 #include <stdlib.h>
00021 #include <string.h>
00022
00023 #include "arch.h"
00024
00025 #include "opencbm.h"
00026 #include "cbmcopy.h"
00027 #include "inputfiles.h"
00028
00029
00030 static CBM_FILE fd_cbm;
00031
00032 static cbmcopy_severity_e verbosity = sev_info;
00033 static int no_progress = 0;
00034
00035 static void my_message_cb(cbmcopy_severity_e severity,
00036 const char *format, ...)
00037 {
00038 va_list args;
00039
00040 static const char *severities[4] =
00041 {
00042 "Fatal",
00043 "Warning",
00044 "Info",
00045 "Debug"
00046 };
00047
00048 if(verbosity >= severity)
00049 {
00050 fprintf(stderr, "[%s] ", severities[severity]);
00051 va_start(args, format);
00052 vfprintf(stderr, format, args);
00053 va_end(args);
00054 fprintf(stderr, "\n");
00055 }
00056 }
00057
00058 static int my_status_cb(int blocks_written)
00059 {
00060 char *statstr;
00061
00062
00063 if(!no_progress)
00064 {
00065 switch (blocks_written%4)
00066 {
00067 case 3: statstr="\010-"; break;
00068 case 2: statstr="\010/"; break;
00069 case 1: statstr="\010|"; break;
00070 default: statstr="\010.\\";
00071 }
00072 printf("%s",statstr);
00073 fflush(stdout);
00074 }
00075 return 0;
00076 }
00077
00078
00079 static void help(const char *prog)
00080 {
00081 printf(
00082 "Usage: %s [OPTION]... [DRIVE] [FILE]...\n"
00083 "Copy files to a CBM-15[478]1 or compatible drive and vice versa\n"
00084 "\n"
00085 "Options:\n"
00086 " -r, --read transfer 15x1->PC\n"
00087 " (default when started as 'cbmread')\n"
00088 " -w, --write transfer PC->15x1\n"
00089 " (default when started as 'cbmwrite')\n"
00090 " -r and -w are mutually exclusive\n"
00091 "\n"
00092 " -h, --help display this help and exit\n"
00093 " -V, --version display version information and exit\n"
00094 " -q, --quiet quiet output\n"
00095 " -v, --verbose control verbosity (repeatedly, up to 3 times)\n"
00096 " -n, --no-progress do not display progress information\n"
00097 "\n"
00098 " -t, --transfer=TRANSFER set transfermode; valid modes:\n"
00099 " auto (default)\n"
00100 " serial1 or s1 (slowest)\n"
00101 " serial2 or s2\n"
00102 " parallel (fastest)\n"
00103 " (can be abbreviated, if unambiguous)\n"
00104 " `serial1' should work in any case;\n"
00105 " `serial2' won't work if more than one device is\n"
00106 " connected to the IEC bus;\n"
00107 " `parallel' needs a XP1541/XP1571 cable in addition\n"
00108 " to the serial one.\n"
00109 " `auto' tries to determine the best option.\n"
00110 " -d, --drive-type=TYPE specify drive type, one of:\n"
00111 " 1541, 1570, 1571, 1581\n"
00112 " -a, --address=ADDRESS override file start address\n"
00113 " -o, --output=NAME specifies target name (ASCII, even for writing).\n"
00114 "\n"
00115 "Options for writing:\n"
00116 " -f, --file-type specify CBM file type (D,P,S,U)\n"
00117 " -R, --raw skip test for PC64 (.p00) and T64 input file\n"
00118 "\n", prog);
00119 }
00120
00121 static void hint(char *prog)
00122 {
00123 printf("Try `%s' --help for more information.\n", prog);
00124 }
00125
00126 static void ARCH_SIGNALDECL reset(int dummy)
00127 {
00128 fprintf(stderr, "\nSIGINT caught X-( Resetting IEC bus...\n");
00129 #ifdef CBMCOPY_DEBUG
00130 printDebugCounters(my_message_cb);
00131 #endif
00132 arch_sleep(1);
00133 cbm_reset(fd_cbm);
00134 cbm_driver_close(fd_cbm);
00135 exit(1);
00136 }
00137
00138
00139 extern input_reader cbmwrite_raw;
00140 extern input_reader cbmwrite_pc64;
00141 extern input_reader cbmwrite_t64;
00142
00143
00144 static void char_star_opt_once(const char **arg,
00145 const char *long_name,
00146 char **argv)
00147 {
00148 if(*arg)
00149 {
00150 my_message_cb(sev_fatal, "%s given more than once.", long_name);
00151 hint(argv[0]);
00152 exit(1);
00153 }
00154 *arg = optarg;
00155 }
00156
00157
00158 int ARCH_MAINDECL main(int argc, char **argv)
00159 {
00160 CBM_FILE fd;
00161 FILE *file;
00162 char *fname;
00163
00164 int mode;
00165 int c;
00166 unsigned char *filedata;
00167 size_t filesize;
00168 char buf[48];
00169 int num_entries;
00170 int num_files;
00171 int rv;
00172 int i;
00173 int write;
00174 cbmcopy_settings *settings;
00175 char auto_name[17];
00176 char auto_type = '\0';
00177 char output_type = '\0';
00178 char *tail;
00179 char *ext;
00180
00181 unsigned char drive;
00182 const char *tm = NULL;
00183 const char *dt = NULL;
00184 int force_raw = 0;
00185 int address = -1;
00186 const char *output_name = NULL;
00187 const char *address_str = NULL;
00188 char *fs_name;
00189
00190 input_reader *readers[] =
00191 {
00192 &cbmwrite_raw,
00193 &cbmwrite_pc64,
00194 &cbmwrite_t64,
00195 NULL
00196 };
00197
00198 input_reader *rd;
00199
00200 struct option longopts[] =
00201 {
00202 { "help" , no_argument , NULL, 'h' },
00203 { "verbose" , no_argument , NULL, 'v' },
00204 { "quiet" , no_argument , NULL, 'q' },
00205 { "version" , no_argument , NULL, 'V' },
00206 { "no-progress" , no_argument , NULL, 'n' },
00207 { "read" , no_argument , NULL, 'r' },
00208 { "write" , no_argument , NULL, 'w' },
00209 { "transfer" , required_argument, NULL, 't' },
00210 { "drive-type" , required_argument, NULL, 'd' },
00211 { "file-type" , required_argument, NULL, 'f' },
00212 { "output" , required_argument, NULL, 'o' },
00213 { "raw" , no_argument , NULL, 'R' },
00214 { "address" , no_argument , NULL, 'a' },
00215 { NULL , 0 , NULL, 0 }
00216 };
00217
00218 const char shortopts[] ="hVqvrwnt:d:f:o:Ra:";
00219
00220 if(NULL == (tail = strrchr(argv[0], '/')))
00221 {
00222 tail = argv[0];
00223 }
00224 else
00225 {
00226 tail++;
00227 }
00228 if(strcmp(tail, "cbmread") == 0)
00229 {
00230 mode = 'r';
00231 }
00232 else if(strcmp(tail, "cbmwrite") == 0)
00233 {
00234 mode = 'w';
00235 }
00236 else
00237 {
00238 mode = EOF;
00239 }
00240
00241 settings = cbmcopy_get_default_settings();
00242
00243
00244 while((c=getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
00245 {
00246 switch(c)
00247 {
00248 case 'h':
00249 help(argv[0]);
00250 return 0;
00251 case 'V':
00252 printf("cbmcopy %s\n", OPENCBM_VERSION);
00253 return 0;
00254 case 'q':
00255 if(verbosity > sev_fatal)
00256 verbosity--;
00257 break;
00258 case 'v':
00259 if(verbosity < sev_debug)
00260 verbosity++;
00261 break;
00262 case 'n':
00263 no_progress = 1;
00264 break;
00265
00266 case 'r':
00267 case 'w':
00268 if(mode != EOF)
00269 {
00270 my_message_cb(sev_fatal, "-r/-w given more than once");
00271 hint(argv[0]);
00272 return 1;
00273 }
00274 mode = c;
00275 break;
00276
00277 case 't':
00278 char_star_opt_once(&tm, "--transfer", argv);
00279 break;
00280 case 'd':
00281 char_star_opt_once(&dt, "--drive-type", argv);
00282 break;
00283 case 'o':
00284 char_star_opt_once(&output_name, "--output", argv);
00285 break;
00286 case 'f':
00287 output_type = (char) toupper(*optarg);
00288 break;
00289 case 'R':
00290 force_raw = 1;
00291 break;
00292 case 'a':
00293 char_star_opt_once(&address_str, "--address", argv);
00294 break;
00295 default :
00296 hint(argv[0]);
00297 return 1;
00298 }
00299 }
00300
00301
00302 switch(mode)
00303 {
00304 case 'r' :
00305 write = 0;
00306 break;
00307 case 'w' :
00308 write = 1;
00309 break;
00310 default:
00311 my_message_cb(sev_fatal,
00312 "-r or -w must be given when started as `%s'",
00313 argv[0]);
00314 hint(argv[0]);
00315 return 1;
00316 }
00317
00318
00319 settings->transfer_mode = cbmcopy_get_transfer_mode_index(tm);
00320 if(settings->transfer_mode < 0)
00321 {
00322 my_message_cb(sev_fatal, "Unknown transfer mode: %s", tm);
00323 return 1;
00324 }
00325
00326
00327 if(dt)
00328 {
00329 const struct
00330 {
00331 const char *str;
00332 enum cbm_device_type_e type;
00333 }
00334 *p, types[] =
00335 {
00336 { "1541", cbm_dt_cbm1541 }, { "1571", cbm_dt_cbm1571 },
00337 { "1570", cbm_dt_cbm1570 }, { "1581", cbm_dt_cbm1581 },
00338 { NULL , cbm_dt_unknown }
00339 };
00340
00341 for(p = types; p->str && strcmp(dt, p->str); p++)
00342 ;
00343
00344 if(!p->str)
00345 {
00346 my_message_cb(sev_fatal, "Unknown drive type: %s", dt);
00347 return 1;
00348 }
00349 settings->drive_type = p->type;
00350 }
00351
00352
00353 if(output_type)
00354 {
00355 if(write)
00356 {
00357 if(strchr("DSPU", output_type) == NULL)
00358 {
00359 my_message_cb(sev_fatal, "Invalid file type : %c", output_type);
00360 }
00361 }
00362 else
00363 {
00364 my_message_cb(sev_warning, "--file-type ignored");
00365 }
00366 }
00367
00368
00369 if(address_str)
00370 {
00371 address = strtol(address_str, &tail, 0);
00372 if(*tail || address < 0 || address > 0xffff)
00373 {
00374 my_message_cb(sev_fatal, "--address invalid: %s", address_str);
00375 hint(argv[0]);
00376 return 1;
00377 }
00378 }
00379
00380
00381 if(optind == argc)
00382 {
00383 my_message_cb(sev_fatal, "%s: No drive number given", argv[0]);
00384 hint(argv[0]);
00385 return 1;
00386 }
00387
00388 drive = (unsigned char) strtol(argv[optind], &tail, 0);
00389 if(drive < 8 || drive > 11 || *tail)
00390 {
00391 my_message_cb(sev_fatal, "invalid drive: `%s'", argv[optind]);
00392 return 1;
00393 }
00394
00395
00396 num_files = argc - optind - 1;
00397
00398 if(num_files == 0)
00399 {
00400 my_message_cb(sev_fatal, "%s: No files?", argv[0]);
00401 hint(argv[0]);
00402 return 1;
00403 }
00404
00405
00406 if(num_files > 1 && output_name)
00407 {
00408 my_message_cb(sev_fatal, "--output requires exactly one file name");
00409 return 1;
00410 }
00411
00412 rv = cbm_driver_open( &fd, 0 );
00413
00414 if(0 == rv)
00415 {
00416 fd_cbm = fd;
00417
00418
00419
00420
00421
00422 settings->transfer_mode =
00423 cbmcopy_check_auto_transfer_mode(fd_cbm,
00424 settings->transfer_mode,
00425 drive);
00426
00427 signal( SIGINT, reset );
00428
00429 while(++optind < argc)
00430 {
00431 fname = argv[optind];
00432 if(write)
00433 {
00434 rd = readers[0];
00435
00436 file = fopen(fname, "rb");
00437 if(file)
00438 {
00439 num_entries = 0;
00440 if(!force_raw)
00441 {
00442
00443 for(i = 1; readers[i] && !num_entries; i++)
00444 {
00445 num_entries =
00446 readers[i]->probe( file, fname, my_message_cb );
00447 if(num_entries)
00448 rd = readers[i];
00449 }
00450 }
00451 if(!num_entries) num_entries = 1;
00452
00453 for(i = 0; i < num_entries; i++)
00454 {
00455 my_message_cb( sev_debug,
00456 "processing entry %d from %s",
00457 i, fname );
00458 if(rd->read(file, fname, i,
00459 auto_name, &auto_type,
00460 &filedata, &filesize, my_message_cb ) == 0)
00461 {
00462 buf[16] = '\0';
00463 if(output_name)
00464 {
00465 strncpy(buf, output_name, 16);
00466 cbm_ascii2petscii(buf);
00467 }
00468 else
00469 {
00470
00471 strncpy(buf, auto_name, 16);
00472 }
00473 strcat(buf, ",x");
00474 buf[strlen(buf)-1] =
00475 output_type ? output_type : auto_type;
00476 strcat(buf, ",W");
00477
00478 my_message_cb( sev_info,
00479 "writing %s -> %s", fname, buf );
00480
00481 if(address >= 0 && filesize > 1)
00482 {
00483 filedata[0] = address % 0x100;
00484 filedata[1] = address / 0x100;
00485
00486 my_message_cb( sev_debug,
00487 "override address: $%02x%02x",
00488 filedata[1], filedata[0] );
00489
00490 }
00491 if(cbmcopy_write_file(fd, settings, drive,
00492 buf, strlen(buf),
00493 filedata, filesize,
00494 my_message_cb,
00495 my_status_cb) == 0)
00496 {
00497 printf("\n");
00498 rv = cbm_device_status( fd, drive,
00499 buf, sizeof(buf) );
00500 my_message_cb( rv ? sev_warning : sev_info,
00501 "%s", buf );
00502 }
00503 else
00504 printf("\n");
00505
00506 if(filedata)
00507 {
00508 free(filedata);
00509 }
00510 }
00511 else
00512 {
00513 my_message_cb( sev_warning,
00514 "error processing entry %d from %s",
00515 i, fname );
00516 }
00517 }
00518 }
00519 else
00520 {
00521 my_message_cb( sev_warning,
00522 "warning could not read %s: %s",
00523 fname, arch_strerror(arch_get_errno()) );
00524 }
00525 }
00526 else
00527 {
00528 strncpy(buf, fname, 16);
00529 buf[16] = '\0';
00530 cbm_ascii2petscii(buf);
00531
00532 if(output_name)
00533 {
00534 fs_name = arch_strdup(output_name);
00535 }
00536 else
00537 {
00538 for(tail = fname; *tail && *tail != ','; tail++);
00539
00540 ext = "prg";
00541
00542 if(*tail)
00543 {
00544 tail++;
00545 switch(*tail)
00546 {
00547 case 'D': ext = "del"; break;
00548 case 'S': ext = "seq"; break;
00549 case 'U': ext = "usr"; break;
00550 }
00551 }
00552 fs_name = malloc(strlen(fname) + strlen(ext) + 2);
00553 if(fs_name) sprintf(fs_name, "%s.%s", fname, ext);
00554 }
00555
00556 if(fs_name)
00557 {
00558 for(tail = fs_name; *tail; tail++)
00559 {
00560 if(*tail == '/') *tail = '_';
00561 }
00562 }
00563 else
00564 {
00565
00566 cbm_driver_close( fd );
00567 my_message_cb(sev_fatal, "Out of memory");
00568 exit(1);
00569 }
00570
00571 my_message_cb( sev_info, "reading %s -> %s", buf, fs_name );
00572
00573 if(cbmcopy_read_file(fd, settings, drive, buf, strlen(buf),
00574 &filedata, &filesize,
00575 my_message_cb, my_status_cb) == 0)
00576 {
00577 rv = cbm_device_status( fd, drive, buf, sizeof(buf) );
00578 my_message_cb( rv ? sev_warning : sev_info, "%s", buf );
00579
00580 file = fopen(fs_name, "wb");
00581 if(file)
00582 {
00583 if(filedata)
00584 {
00585 if(address >= 0 && filesize > 1)
00586 {
00587 filedata[0] = address % 0x100;
00588 filedata[1] = address / 0x100;
00589
00590 my_message_cb( sev_debug,
00591 "override address: $%02x%02x",
00592 filedata[1], filedata[0] );
00593 }
00594 if(fwrite(filedata, filesize, 1, file) != 1)
00595 {
00596 my_message_cb(sev_warning,
00597 "could not write %s: %s",
00598 fs_name, arch_strerror(arch_get_errno()));
00599 }
00600 }
00601 fclose(file);
00602 }
00603 else
00604 {
00605 my_message_cb(sev_warning,
00606 "could not open %s: %s",
00607 fs_name, arch_strerror(arch_get_errno()));
00608 }
00609
00610 if(filedata)
00611 {
00612 free(filedata);
00613 }
00614 }
00615 else
00616 {
00617 my_message_cb(sev_warning, "error reading %s", buf);
00618 }
00619 if(fs_name)
00620 {
00621 free(fs_name);
00622 }
00623 }
00624 }
00625 cbm_driver_close( fd );
00626
00627 if(rv)
00628 {
00629 my_message_cb(sev_warning, "there was at least one error" );
00630 }
00631 }
00632
00633 return rv;
00634 }