/* c.fart * * Disktree copier. Better than far. * Written for Graham Toal by Tiggr. * Not optimizing space. I'm lazy. * (C) 1990 WayForward Technologies */ #include <kernel.h> #include <ctype.h> #include <stdio.h> #include <stdlib.h> #include <string.h> typedef unsigned int uint; typedef int boolean; #undef TRUE #define TRUE (0==0) #undef FALSE #define FALSE (0!=0) typedef struct dirsize { /* all sizes are rounded up to 1024 bytes (E-type sector size) */ uint size; /* size taken by files in this directory */ uint rsize; /* recusive size, including this and other directories */ } *dirsize_t; typedef struct dirinfo { char *fullname; struct entry *first; struct dirsize size; boolean part_done; } *dirinfo_t; #define DIR_SIZE (0x800) #define FLOPPY_SIZE (0xc7000) typedef struct entry { char *name; /* leaf name */ dirinfo_t dir; uint size; boolean done; struct entry *next; } *entry_t; struct dirinfo root; typedef struct file { char *name; struct file *next; } *file_t; file_t already_done = NULL; char *logfn; FILE *logfile; long pos = 0; boolean doing_recover = FALSE; /******************************** hmpf */ static void hmpf (int i) { fprintf (stderr, "Fatal error occured. Goodbye...\n"); exit (i); } /* hmpf */ /******************************** hmpf_system */ static int hmpf_system (char *s) { printf ("%s\n", s); fflush (stdout); return (_kernel_system (s, 0)); } /* hmpf_system */ /******************************** usage */ static void usage (char *progname) { printf ("This is FART version 1.10 (23-Jan-1990), written by Tiggr.\n\ This utility was written for a specific purpose: doing what `far' does\n\ not do very well: copying a large directory tree from an adfs harddisc\n\ onto floppies. Don't complain if it does not match other expectations.\n\ \n\ Usage: %s <source root> <logfile> [-f <filename>]\n\ \n\ The <source root> of the directory tree to be copied should be given as a\n\ leaf name relative to the current directory (@). If the -f option is not\n\ specified <logfile> is used to write the filename of each file as it is\n\ copied to a floppy. If -f option is specified, the <filename> given is\n\ searched for in <logfile> (which must already exist). All files which appear\n\ in <logfile> before and including the given <filename> are not copied.\n\ Copying then starts after <filename>.\n\ \n\ FART cannot cope with files which don't fit on a floppy. FART will exit on\n\ any error condition occuring (but will try to close the logfile properly).\n\ \n\ BUGS\n\ If FART recovers from a logfile and it crashes before having written so\n\ much to the logfile that the file's length would extend, the logfile will\n\ contain erroneous information.\n\ \n\ Share and Enjoy! (but without warranty)\n", progname); } /* usage */ /******************************** xswi performs the swi call, exiting the program upon error */ static void xswi (uint swine, _kernel_swi_regs *r) { _kernel_oserror *e = _kernel_swi (swine, r, r); if (!e) return; fprintf (stderr, "Fatal error: 0x%x:\n", e->errnum); fprintf (stderr, "%s\n", e->errmess); exit (1); } /* xswi */ /******************************** free_space returns free space on floppy adfs::0 */ static uint free_space () { _kernel_swi_regs r; r.r[0] = (int) ":0"; xswi (0x40243, &r); /* ADFS_FreeSpace */ return (r.r[0]); } /* free_space */ /******************************** logit writes an entry into the logfile */ static void logit (char *name) { logfile = fopen (logfn, "r+"); fseek (logfile, pos, SEEK_SET); fprintf (logfile, "%s\n", name); pos = ftell (logfile); fclose (logfile); } /* logit */ /******************************** findname returns TRUE iff the name can be found in the already_done list */ static boolean findname (char *name) { file_t f = already_done; char *s, *t; while (f) { s = name; t = f->name; while (*s && (*s == *t || isalpha (*s) && isalpha (*t) && toupper (*s) == toupper (*t))) { s++; t++; } if (!*s && !*t) { printf ("found: '%s'\n", name); return (TRUE); } f = f->next; } return (FALSE); } /* findname */ /******************************** build_list builds a list containing information on the objects. */ static void build_list (dirinfo_t dir) { _kernel_swi_regs r; entry_t cur = NULL, next; union { char c[256]; uint i[64]; } buf; char *name = &buf.c[20]; uint size = 0, rsize = 0; r.r[0] = 10; r.r[1] = (int) dir->fullname; r.r[2] = (int) &buf.i[0]; r.r[4] = 0; r.r[5] = 256; r.r[6] = 0; while (r.r[4] != -1) { r.r[3] = 1; xswi (0x0c, &r); /* OS_GBPB */ if (r.r[3] != 1) continue; if (buf.i[4] < 1 || buf.i[4] > 2) { printf ("Unrecognised object type %d for file `%s.%s'\n", buf.i[4], dir->fullname, name); exit (3); } next = calloc (1, sizeof (struct entry)); if (cur) cur->next = next; else dir->first = next; cur = next; cur->name = malloc (1 + strlen (name)); strcpy (cur->name, name); cur->size = buf.i[2]; if (buf.i[4] == 2) { /* directory */ cur->dir = calloc (1, sizeof (struct dirinfo)); cur->dir->fullname = malloc (2 + strlen (name) + strlen (dir->fullname)); sprintf (cur->dir->fullname, "%s.%s", dir->fullname, name); build_list (cur->dir); rsize += cur->dir->size.rsize; } else size += (buf.i[2] + 1023) & ~1023; } dir->size.size = size; dir->size.rsize = rsize + size + DIR_SIZE; } /* build_list */ /******************************** do_one returns TRUE if it could copy the whole directory */ static boolean do_one (dirinfo_t dir) { entry_t cur; boolean ret = FALSE, do_recursive = TRUE, b, have_skipped = FALSE, dolog = FALSE; char src[1024], cmd[3072]; int left; if (findname (dir->fullname)) return (TRUE); sprintf (cmd, "cdir adfs::0.%s", dir->fullname); if (hmpf_system (cmd)) hmpf (1); for (cur = dir->first; cur; cur = cur->next) { if (cur->done) continue; left = free_space (); ret = TRUE; if (cur->dir) { /* directory */ if (!cur->dir->part_done && cur->dir->size.rsize <= left && !doing_recover) { /* lucky; recursize directory fits! */ /* printf ("dir fits: sizeof (\"%s\") = %d\n", cur->dir->fullname, cur->dir->size.rsize); */ cur->done = TRUE; if (findname (cur->dir->fullname)) continue; sprintf (cmd, "copy %s adfs::0.%s rf~cvq", cur->dir->fullname, cur->dir->fullname); if (hmpf_system (cmd)) hmpf (1); logit (cur->dir->fullname); dolog = TRUE; } else if (DIR_SIZE < left) { if (findname (cur->dir->fullname)) { cur->done = TRUE; continue; } b = do_one (cur->dir); cur->dir->part_done = TRUE; cur->done = b; if (b) { logit (cur->dir->fullname); cur->done = TRUE; dolog = TRUE; } do_recursive = do_recursive && b; } else { if (findname (cur->dir->fullname)) { cur->done = TRUE; continue; } have_skipped = TRUE; } } else { if (cur->size <= left) { /* printf ("file fits: sizeof (\"%s.%s\") = %d\n", dir->fullname, cur->name, cur->size); */ cur->done = TRUE; sprintf (src, "%s.%s", dir->fullname, cur->name); if (findname (src)) { continue; } logit (src); sprintf (cmd, "copy %s adfs::0.%s f~cvq", src, src); if (hmpf_system (cmd)) hmpf (1); } else { sprintf (cmd, "%s.%s", dir->fullname, cur->name); if (findname (cmd)) { cur->done = TRUE; continue; } have_skipped = TRUE; } } } if (do_recursive && !have_skipped && dolog) logit (dir->fullname); return (do_recursive && !have_skipped); } /* do_one */ /******************************** cleanup */ void cleanup (void) { fflush (logfile); fclose (logfile); return; } /* cleanup */ /******************************** main */ int main (int argc, char **argv) { struct dirinfo root; char *s, *resfn = NULL; file_t cf, *nf; char fname[1024]; int c, i, j; if (argc != 3 && argc != 5 || (argc == 5 && strcmp (argv[3], "-f"))) { usage (argv[0]); exit (2); } for (s = argv[1]; *s; s++) if (strchr ("@^%&:#-", *s)) { fprintf (stderr, "Please specify directory to be copied relative to @.\n"); exit (2); } root.fullname = argv[1]; logfn = argv[2]; if (argc == 5) resfn = argv[4]; logfile = fopen (logfn, resfn ? "r+" : "w"); if (logfile == NULL) { fprintf (stderr, "failed to open logfile '%s'\n", logfn); exit (1); } if (atexit (cleanup)) { fprintf (stderr, "failed to register exit handler\n"); exit (1); } if (resfn) { /* Build list of already-done filenames. */ doing_recover = TRUE; nf = &already_done; while (! feof (logfile)) { i = 0; while ((c = fgetc (logfile)) != '\n' && c != EOF) fname[i++] = c; fname[i] = 0; if (i) { for (j = 0; j < i; j++) if (fname[j] != resfn[j] && (isalpha (fname[j]) && isalpha (resfn[j]) && toupper (fname[j]) != toupper (resfn[j]))) break; *nf = cf = malloc (sizeof (struct file)); nf = &cf->next; cf->name = malloc (i + 1); strcpy (cf->name, fname); if (j == i) { fseek (logfile, ftell (logfile), SEEK_SET); break; } } } *nf = NULL; } pos = ftell (logfile); fclose (logfile); build_list (&root); for (;;) { printf ("Please insert fresh floppy and press return"); fflush (stdout); while ((c = fgetc (stdin)) != '\n') if (c == EOF) exit (1); printf ("\n"); if (hmpf_system ("-adfs-mount :0")) hmpf (1); if (hmpf_system ("dir \\")) hmpf (1); if (do_one (&root)) break; if (hmpf_system ("-adfs-dismount :0")) hmpf (1); } fclose (logfile); return (0); } /* main */ /* EOF fart.c */