[cc65] Goodbye _curunit, Hello chdir()

From: Oliver Schmidt <ol.sc1web.de>
Date: 2012-10-18 14:27:28

As discussed in the thread "Uniform mass storage device discovery and
file locations" and as coordinated with Uz I've implemented some new
stuff. Here's a brief overview:

unsigned char getfirstdevice (void);
unsigned char __fastcall__ getnextdevice (unsigned char device);

Enumerate the the mass storage devices present and returns their
numbers. The numbers should be meaningful to users i.e. for choosing
in a list built from the result. On the CBMs the numbers are 8, 9, ...
The numbers can be used as parameter to dio_open(). Disk drives are
not turned on.

char* __fastcall__ getdevicedir (unsigned char device, char* buf, size_t size);

Given a device number provides a directory name that can be used as
parameter to chdir() in order to access the root directory of the
device. The name should be meaningful to users i.e. for choosing in a
list built from the result. On the CBMs the names are "8", "9", ...
Disk drives are checked for a formatted disk. If there's none the
return value is NULL instead of a pointer to the directory name.

int __fastcall__ chdir (const char* name);

Makes the directory provided the current one. The only directories
supported on CBMs are the root directories of the devices named "8",
"9", ... Disk drives are checked for a formatted disk. If there's none
the return value is -1.

char* __fastcall__ getcwd (char* buf, size_t size);

Provides the name of the current directory. On CBMs this is just the
name given to chdir() (or the default - usually "8").

Important note: Because getcwd() just returns a string cached inside
the C library all changes to the current directory have to be made
through C library functions which are aware of that cache and update
it. Usually the only one doing so is chdir(). Obviously setting the
current device by changing the value of _curunit is incompatible with
this approach. Therefore _curunit had to be removed so coding like

  _curunit = 9;

has to be replaced with


Sorry for the inconvenience.

With this new stuff it is now possible to enumerate (and access) all
devices, directories and files in a target-independent way - which was
my original goal in thread mentioned above :-) So it's time to say
thanks to all who contributed there!

I've written a new samples program called 'enumdevdir' that works on
(most) CBMs and on the Apple II without any #ifdef. On the latter it
processes subdirectories too. Please find its source code below this

Another more simple target-independent scenario is to ask the user not
only for a "File Name" but also for a "File Location" and doing a
chdir() to that location before accessing the file.

Final remark: I'm very optimistic that the new functions described
above can be implemented in a reasonable way on other targets too. The
obvious candidate here is the Atari. I'm looking forward to see
enumdevdir running there too...


 * Enumerate devices, directories and files.
 * 2012-10-15, Oliver Schmidt (ol.sc@web.de)

#include <stdio.h>
#include <conio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
#include <device.h>
#include <dirent.h>

void printdir (char *newdir)
    char olddir[FILENAME_MAX];
    char curdir[FILENAME_MAX];
    DIR *dir;
    struct dirent *ent;
    char *subdirs = NULL;
    unsigned dirnum = 0;
    unsigned num;

    getcwd (olddir, sizeof (olddir));
    if (chdir (newdir)) {

        /* If chdir() fails we just print the
         * directory name - as done for files.
        printf ("  Dir  %s\n", newdir);

    /* We call getcwd() in order to print the
     * absolute pathname for a subdirectory.
    getcwd (curdir, sizeof (curdir));
    printf (" Dir %s:\n", curdir);

    /* Calling opendir() always with "." avoids
     * fiddling around with pathname separators.
    dir = opendir (".");
    while (ent = readdir (dir)) {

        if (_DE_ISREG (ent->d_type)) {
            printf ("  File %s\n", ent->d_name);

        /* We defer handling of subdirectories until we're done with the
         * current one as several targets don't support other disk i/o
         * while reading a directory (see cc65 readdir() doc for more).
        if (_DE_ISDIR (ent->d_type)) {
            subdirs = realloc (subdirs, FILENAME_MAX * (dirnum + 1));
            strcpy (subdirs + FILENAME_MAX * dirnum++, ent->d_name);
    closedir (dir);

    for (num = 0; num < dirnum; ++num) {
        printdir (subdirs + FILENAME_MAX * num);
    free (subdirs);

    chdir (olddir);

void main (void)
    unsigned char device;
    char devicedir[FILENAME_MAX];

    /* Calling getfirstdevice()/getnextdevice() does _not_ turn on the motor
     * of a drive-type device and does _not_ check for a disk in the drive.
    device = getfirstdevice ();
    while (device != INVALID_DEVICE) {
        printf ("Device %d:\n", device);

        /* Calling getdevicedir() _does_ check for a (formatted) disk in a
         * floppy-disk-type device and returns NULL if that check fails.
        if (getdevicedir (device, devicedir, sizeof (devicedir))) {
            printdir (devicedir);
        } else {
            printf (" N/A\n");

        device = getnextdevice (device);

    cgetc ();
To unsubscribe from the list send mail to majordomo@musoftware.de with
the string "unsubscribe cc65" in the body(!) of the mail.
Received on Thu Oct 18 14:28:33 2012

This archive was generated by hypermail 2.1.8 : 2012-10-18 14:28:36 CEST