/* :ts=8 bk=0 * * eless.c: An attempt at a reasonable-speed 'ls' program by fiddling * with the DOS. * * Compiles under Manx 3.20 and patched 3.40. * cc eless.c * ln eless.c -lc -o eless * * Leo L. Schwab 8704.26 (415)-456-6565 */ #include #include #include #define BLOCKSIZE 128 #define STARTTAB 6 #define ENDTAB (BLOCKSIZE-51) #define TABSIZ (BLOCKSIZE-56) #define FILENAME (BLOCKSIZE-20) #define HASHCHAIN (BLOCKSIZE-4) #define SECONDARY (BLOCKSIZE-1) #define ST_DIR 2 #define ST_FILE -3 /* * Note: I usually declare Amiga functions this way to disable the compiler's * type checking. This allows me to assign values to variables without * explicitly casting them to the appropriate type. In reality, these * functions return things entirely different from the way I've declared * them. For example, CreatePort() should really be declared as: * * struct MsgPort *CreatePort(); * * Caveat Programmer. */ extern void *AllocMem(), *CreatePort(), *RemTail(), *FindTask(); extern long Lock(), Examine(); long dopacket(); int longcmp(), namecmp(); struct StandardPacket *packet; struct FileInfoBlock *fib; struct FileLock *lock; struct InfoData *id; struct MsgPort *dosreply; BPTR lok; long *buf, hashtab[TABSIZ]; main (ac, av) char **av; { int flag; openstuff (); if (ac < 2) /* No arguments; do current directory */ printdir (((struct Process *) FindTask (0L)) -> pr_CurrentDir); else { /* Arguments; treat as directories */ flag = (ac > 2); while (++av, --ac) { if (!(lok = Lock (*av, ACCESS_READ))) { printf ("%s: Not found.\n", *av); if (ac > 1) putchar ('\n'); continue; } if (!Examine (lok, fib)) die ("Examine failed."); if (fib -> fib_DirEntryType < 0) { printf ("%s: Not a directory.\n", *av); if (ac > 1) putchar ('\n'); continue; } if (flag) printf ("%s:\n", *av); printdir (lok); if (ac > 1) putchar ('\n'); UnLock (lok); lok = NULL; } } closestuff (); } /* * This function prints the directory in a nice, pretty columnar format. */ printdir (dirlock) BPTR dirlock; { struct List namelist; register struct Node *name, **namearray; long args[2]; register int i, n; int len = 0, ncols, nlines, nfiles = 0; char fmt1[16], fmt2[16]; char *fn = (char *) (&buf[FILENAME]) + 1; lock = (struct FileLock *) (dirlock << 2); args[0] = lock -> fl_Key; /* Block number */ args[1] = (long) buf >> 2; /* BPTR to buffer */ /* Attempt to get raw directory block. */ if (!dopacket (lock -> fl_Task, ACTION_GET_BLOCK, args, 2)) { if (packet -> sp_Pkt.dp_Res2 == ERROR_ACTION_NOT_KNOWN) printf ("Not a block device.\n"); else printf ("Error %ld while getting dirblock.\n", packet -> sp_Pkt.dp_Res2); return; } /* Copy DOS's hash table into our array. */ for (i=0; i fl_Task, ACTION_GET_BLOCK, args, 2); if (!(name = AllocMem (sizeof (*name) + *(fn-1) + 1L, MEMF_CLEAR))) { puts ("Node memory allocation failure."); goto bombout; } name -> ln_Name = (char *) name + sizeof (*name); name -> ln_Type = (buf[SECONDARY] == ST_DIR); fn[*(fn-1)] = '\0'; /* Force null-termination */ strcpy (name -> ln_Name, fn); AddTail (&namelist, name); nfiles++; /* Number of files found */ hashtab[i] = buf[HASHCHAIN]; } } if (!nfiles) /* No files */ return; /* Create array that qsort can deal with. */ if (!(namearray = AllocMem ((long) nfiles * sizeof (char *), MEMF_CLEAR))) { puts ("Name array allocation failure."); goto bombout; } /* Load up the array. */ for (name = namelist.lh_Head, i=0; name -> ln_Succ; name = name -> ln_Succ, i++) namearray[i] = name; /* Alphabetize filenames. */ qsort (namearray, nfiles, sizeof (struct Node *), namecmp); /* Find longest string so we can format intelligently. */ for (i=0; i ln_Name)) > len) len = n; len += 2; /* Inter-name spacing */ /* Print them suckers out */ ncols = 77 / len; /* Assume CON: is 77 columns */ nlines = nfiles/ncols + (nfiles%ncols != 0); sprintf (fmt1, "%%-%ds", len); /* Normal format string */ sprintf (fmt2, "\033[33m%%-%ds\033[m", len); /* For directories */ for (i=0; i ln_Type ? fmt2 : fmt1, namearray[n] -> ln_Name); putchar ('\n'); } /* Phew! Now free all the stuff we allocated. */ bombout: freenamelist (&namelist); if (namearray) FreeMem (namearray, (long) nfiles * sizeof (struct Node *)); } freenamelist (list) struct List *list; { register struct Node *now; while (now = RemTail (list)) FreeMem (now, sizeof (*now) + strlen (now -> ln_Name) + 1L); } /* * This function assumes the existence of a properly initialized * standardpacket structure called "packet" and a reply port called * "dosreply". A more general function would not do this. * * This function is synchronous i.e. it doesn't return until the specified * action has been performed. */ long dopacket (proc, action, args, nargs) struct MsgPort *proc; long action; register long *args; register int nargs; { register long *argp = &packet -> sp_Pkt.dp_Arg1; for (; nargs>0; nargs--) *argp++ = *args++; /* * AmigaDOS scribbles over the reply port, so we have to initialize * it every time we send a packet. */ packet -> sp_Pkt.dp_Port = dosreply; packet -> sp_Pkt.dp_Type = action; PutMsg (proc, packet); WaitPort (dosreply); GetMsg (dosreply); return (packet -> sp_Pkt.dp_Res1); } /* * These functions are called by qsort(). The sense of camparisons is * reversed in longcmp to get a reverse sort (largest elements first). */ longcmp (foo, bar) long *foo, *bar; { /* * We have to do it this way because qsort() expects an int to be * returned. Subtracting longs directly might overflow a 16-bit int. */ return ((*foo < *bar) - (*foo > *bar)); } namecmp (foo, bar) struct Node **foo, **bar; { return (strcmp ((*foo) -> ln_Name, (*bar) -> ln_Name)); } /* * Resource allocation/deallocation routines. */ openstuff () { if (!(packet = AllocMem ((long) sizeof (*packet), MEMF_CLEAR))) die ("Can't allocate packet space."); if (!(dosreply = CreatePort (NULL, NULL))) die ("Dos reply port create failed."); packet -> sp_Msg.mn_Node.ln_Name = (char *) &packet -> sp_Pkt; packet -> sp_Pkt.dp_Link = &packet -> sp_Msg; if (!(fib = AllocMem ((long) sizeof (*fib), MEMF_CLEAR))) die ("Can't allocate FileInfoBlock."); /* * Note: This allocation may not work with DMA hard disks. */ if (!(buf = AllocMem (BLOCKSIZE * 4L, MEMF_CHIP | MEMF_PUBLIC))) die ("Couldn't allocate sector buffer."); } closestuff () { if (lok) UnLock (lok); if (buf) FreeMem (buf, BLOCKSIZE * 4L); if (fib) FreeMem (fib, (long) sizeof (*fib)); if (dosreply) DeletePort (dosreply); if (packet) FreeMem (packet, (long) sizeof (*packet)); } die (str) char *str; { puts (str); closestuff (); exit (1); }