Main Page | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

cbmcopy/main.c

00001 /*
00002  *  This program is free software; you can redistribute it and/or
00003  *  modify it under the terms of the GNU General Public License
00004  *  as published by the Free Software Foundation; either version
00005  *  2 of the License, or (at your option) any later version.
00006  *
00007  *  Copyright 2001-2004 Michael Klein <michael(dot)klein(at)puffin(dot)lb(dot)shuttle(dot)de>
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 /* global, because of signal handler */
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     // if(!no_progress) printf("."); fflush(stdout);
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,   /* must be first, as it is default */
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'; /* read */
00231     }
00232     else if(strcmp(tail, "cbmwrite") == 0)
00233     {
00234         mode = 'w'; /* write */
00235     }
00236     else
00237     {
00238         mode = EOF; /* mode must be given later */
00239     }
00240 
00241     settings = cbmcopy_get_default_settings();
00242 
00243     /* loop over cmd line opts */
00244     while((c=getopt_long(argc, argv, shortopts, longopts, NULL)) != -1)
00245     {
00246         switch(c)
00247         {
00248             case 'h': /* --help */
00249                 help(argv[0]);
00250                 return 0;
00251             case 'V': /* --version */
00252                 printf("cbmcopy %s\n", OPENCBM_VERSION);
00253                 return 0;
00254             case 'q': /* -- quiet */
00255                 if(verbosity > sev_fatal)
00256                     verbosity--;
00257                 break;
00258             case 'v': /* --verbose */
00259                 if(verbosity < sev_debug)
00260                     verbosity++;
00261                 break;
00262             case 'n': /* --no-progress */
00263                 no_progress = 1;
00264                 break;
00265 
00266             case 'r': /* --read */
00267             case 'w': /* --write */
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': /* --transfer */
00278                 char_star_opt_once(&tm, "--transfer", argv);
00279                 break;
00280             case 'd': /* --drive-type */
00281                 char_star_opt_once(&dt, "--drive-type", argv);
00282                 break;
00283             case 'o': /* --output */
00284                 char_star_opt_once(&output_name, "--output", argv);
00285                 break;
00286             case 'f': /* --file-type */
00287                 output_type = (char) toupper(*optarg);
00288                 break;
00289             case 'R': /* --raw */
00290                 force_raw = 1;
00291                 break;
00292             case 'a': /* override-address */
00293                 char_star_opt_once(&address_str, "--address", argv);
00294                 break;
00295             default : /* unknown */
00296                 hint(argv[0]);
00297                 return 1;
00298         }
00299     }
00300 
00301     /* check -r/-w */
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     /* check transfer mode */
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     /* check device type */
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             ; /* nothing */
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     /* check CBM file type */
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     /* check load address override */
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     /* first non-option is device number */
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     /* remaining args are file names */
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     /* more than one file name given, avoid -o option */
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          * If the user specified auto transfer mode, find out
00420          * which transfer mode to use.
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                         /* try to detect file format */
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; /* raw file */
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                                 /* no charset conversion */
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"; /* default */
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                     /* should not happen... */
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 }

Generated on Sun Apr 30 18:45:54 2006 for opencbm by  doxygen 1.4.2