ftp.nice.ch/pub/next/tools/emulators/vice.0.15.0.NeXT.sd.tgz#/vice-0.15.0/src/fsdevice.c

This is fsdevice.c in view mode; [Download] [Up]

/*
 * fsdevice.c - File system device.
 *
 * Written by
 *  Teemu Rantanen      (tvr@cs.hut.fi)
 *  Jarkko Sonninen     (sonninen@lut.fi)
 *  Jouko Valta         (jopi@stekt.oulu.fi)
 *  Olaf Seibert        (rhialto@mbfys.kun.nl)
 *  André Fachat        (a.fachat@physik.tu-chemnitz.de)
 *  Ettore Perazzoli    (ettore@comm2000.it)
 *  Martin Pottendorfer (Martin.Pottendorfer@aut.alcatel.at)
 *  Andreas Boose       (boose@unixserv.rz.fh-hannover.de)
 *
 * Patches by
 *  Dan Miner           (dminer@nyx10.cs.du.edu)
 *  Germano Caronni     (caronni@tik.ethz.ch)
 *  Daniel Fandrich     (dan@fch.wimsey.bc.ca)  /DF/
 *
 * This file is part of VICE, the Versatile Commodore Emulator.
 * See README for copyright notice.
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 *  02111-1307  USA.
 *
 */

/*#define DEBUG_FS*/
/*#define DEBUG_FSDRIVE*/

#include "vice.h"

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <memory.h>
#include <assert.h>
#include <errno.h>

#include "resources.h"
#include "drive.h"
#include "file.h"
#include "charsets.h"
#include "tape.h"
#include "utils.h"
#include "cmdline.h"
#include "fsdevice.h"

enum fsmode {
    Write, Read, Append, Directory
};

struct fs_buffer_info {
    FILE *fd;
    DIR *dp;
    enum fsmode mode;
    char dir[MAXPATHLEN];
    BYTE name[MAXPATHLEN + 5];
    int buflen;
    BYTE *bufp;
    int eof;
    int dirmpos;
    int reclen;
    int type;
} fs_info[16];

/* this should somehow go into the fs_info struct... */

static char fs_errorl[MAXPATHLEN];
static unsigned int fs_elen, fs_eptr;
static char fs_cmdbuf[MAXPATHLEN];
static unsigned int fs_cptr = 0;

static char fs_dirmask[MAXPATHLEN];

static FILE *fs_find_pc64_name(void *flp, char *name, int length, char *pname);
static void fs_test_pc64_name(void *flp, char *rname, int secondary);
static int fsdevice_compare_wildcards(char *name, char *p00name);
static void fsdevice_compare_file_name(void *flp, char *fsname2, char *fsname,
                                       int secondary);
static int fsdevice_create_file_p00(void *flp, char *name, int length,
                                    char *fsname, int secondary);
static int fsdevice_reduce_filename_p00(char *filename, int len);
static int fsdevice_eliminate_char_p00(char *filename, int pos);
static int fsdevice_evaluate_name_p00(char *name, int length, char *filename);

/* FIXME: ugly.  */
extern errortext_t floppy_error_messages;

/* ------------------------------------------------------------------------- */

static int fsdevice_convert_p00_enabled[4];
static int fsdevice_save_p00_enabled[4];
static int fsdevice_hide_cbm_files_enabled[4];
static char *fsdevice_8_dir;
static char *fsdevice_9_dir;
static char *fsdevice_10_dir;
static char *fsdevice_11_dir;

static int set_fsdevice_8_convert_p00(resource_value_t v)
{
    fsdevice_convert_p00_enabled[0] = (int) v;
    return 0;
}

static int set_fsdevice_9_convert_p00(resource_value_t v)
{
    fsdevice_convert_p00_enabled[1] = (int) v;
    return 0;
}

static int set_fsdevice_10_convert_p00(resource_value_t v)
{
    fsdevice_convert_p00_enabled[2] = (int) v;
    return 0;
}

static int set_fsdevice_11_convert_p00(resource_value_t v)
{
    fsdevice_convert_p00_enabled[3] = (int) v;
    return 0;
}

static int set_fsdevice_8_dir(resource_value_t v)
{
    const char *name = (const char *) v;

    if (fsdevice_8_dir != NULL && name != NULL
        && strcmp(name, fsdevice_8_dir) == 0)
        return 0;

    string_set(&fsdevice_8_dir, name);
    return 0;
}

static int set_fsdevice_9_dir(resource_value_t v)
{
    const char *name = (const char *) v;

    if (fsdevice_9_dir != NULL && name != NULL
        && strcmp(name, fsdevice_9_dir) == 0)
        return 0;

    string_set(&fsdevice_9_dir, name);
    return 0;
}

static int set_fsdevice_10_dir(resource_value_t v)
{
    const char *name = (const char *) v;

    if (fsdevice_10_dir != NULL && name != NULL
        && strcmp(name, fsdevice_10_dir) == 0)
        return 0;

    string_set(&fsdevice_10_dir, name);
    return 0;
}

static int set_fsdevice_11_dir(resource_value_t v)
{
    const char *name = (const char *) v;

    if (fsdevice_11_dir != NULL && name != NULL
        && strcmp(name, fsdevice_11_dir) == 0)
        return 0;

    string_set(&fsdevice_11_dir, name);
    return 0;
}

static int set_fsdevice_8_save_p00(resource_value_t v)
{
    fsdevice_save_p00_enabled[0] = (int) v;
    return 0;
}

static int set_fsdevice_9_save_p00(resource_value_t v)
{
    fsdevice_save_p00_enabled[1] = (int) v;
    return 0;
}

static int set_fsdevice_10_save_p00(resource_value_t v)
{
    fsdevice_save_p00_enabled[2] = (int) v;
    return 0;
}

static int set_fsdevice_11_save_p00(resource_value_t v)
{
    fsdevice_save_p00_enabled[3] = (int) v;
    return 0;
}

static int set_fsdevice_8_hide_cbm_files(resource_value_t v)
{
    if (!fsdevice_convert_p00_enabled[0])
	return -1;
    fsdevice_hide_cbm_files_enabled[0] = (int) v;
    return 0;
}

static int set_fsdevice_9_hide_cbm_files(resource_value_t v)
{
    if (!fsdevice_convert_p00_enabled[1])
	return -1;
    fsdevice_hide_cbm_files_enabled[1] = (int) v;
    return 0;
}

static int set_fsdevice_10_hide_cbm_files(resource_value_t v)
{
    if (!fsdevice_convert_p00_enabled[2])
	return -1;
    fsdevice_hide_cbm_files_enabled[2] = (int) v;
    return 0;
}

static int set_fsdevice_11_hide_cbm_files(resource_value_t v)
{
    if (!fsdevice_convert_p00_enabled[3])
	return -1;
    fsdevice_hide_cbm_files_enabled[3] = (int) v;
    return 0;
}

static resource_t resources[] = {
    { "FSDevice8ConvertP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_convert_p00_enabled[0],
      set_fsdevice_8_convert_p00 },
    { "FSDevice9ConvertP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_convert_p00_enabled[1],
      set_fsdevice_9_convert_p00 },
    { "FSDevice10ConvertP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_convert_p00_enabled[2],
      set_fsdevice_10_convert_p00 },
    { "FSDevice11ConvertP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_convert_p00_enabled[3],
      set_fsdevice_11_convert_p00 },
    { "FSDevice8Dir", RES_STRING, (resource_value_t) ".",
      (resource_value_t *) &fsdevice_8_dir, set_fsdevice_8_dir },
    { "FSDevice9Dir", RES_STRING, (resource_value_t) ".",
      (resource_value_t *) &fsdevice_9_dir, set_fsdevice_9_dir },
    { "FSDevice10Dir", RES_STRING, (resource_value_t) ".",
      (resource_value_t *) &fsdevice_10_dir, set_fsdevice_10_dir },
    { "FSDevice11Dir", RES_STRING, (resource_value_t) ".",
      (resource_value_t *) &fsdevice_11_dir, set_fsdevice_11_dir },
    { "FSDevice8SaveP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_save_p00_enabled[0],
      set_fsdevice_8_save_p00 },
    { "FSDevice9SaveP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_save_p00_enabled[1],
      set_fsdevice_9_save_p00 },
    { "FSDevice10SaveP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_save_p00_enabled[2],
      set_fsdevice_10_save_p00 },
    { "FSDevice11SaveP00", RES_INTEGER, (resource_value_t) 1,
      (resource_value_t *) &fsdevice_save_p00_enabled[3],
      set_fsdevice_11_save_p00 },
    { "FSDevice8HideCBMFiles", RES_INTEGER, (resource_value_t) 0,
      (resource_value_t *) &fsdevice_hide_cbm_files_enabled[0],
      set_fsdevice_8_hide_cbm_files },
    { "FSDevice9HideCBMFiles", RES_INTEGER, (resource_value_t) 0,
      (resource_value_t *) &fsdevice_hide_cbm_files_enabled[1],
      set_fsdevice_9_hide_cbm_files },
    { "FSDevice10HideCBMFiles", RES_INTEGER, (resource_value_t) 0,
      (resource_value_t *) &fsdevice_hide_cbm_files_enabled[2],
      set_fsdevice_10_hide_cbm_files },
    { "FSDevice11HideCBMFiles", RES_INTEGER, (resource_value_t) 0,
      (resource_value_t *) &fsdevice_hide_cbm_files_enabled[3],
      set_fsdevice_11_hide_cbm_files },
    { NULL }
};

int fsdevice_init_resources(void)
{
    return resources_register(resources);
}

/* ------------------------------------------------------------------------- */

static int cmdline_fsdirectory(const char *param, void *extra_param)
{
    int unit = (int) extra_param;
    char directory[MAXPATHLEN];

    strcpy(directory, param);
    strcat(directory, "/");

    switch (unit) {
      case 8:
	set_fsdevice_8_dir((resource_value_t) directory);
	break;
      case 9:
	set_fsdevice_9_dir((resource_value_t) directory);
	break;
      case 10:
	set_fsdevice_10_dir((resource_value_t) directory);
	break;
      case 11:
	set_fsdevice_11_dir((resource_value_t) directory);
	break;
      default:
	fprintf(stderr, "cmdline_fsdirectory(): unexpected unit number %d?!\n",
		unit);
    }

    return 0;
}

static cmdline_option_t cmdline_options[] = {
    { "-fs8", CALL_FUNCTION, 1, cmdline_fsdirectory, (void *) 8, NULL, NULL,
      "<name>", "Use <name> as directory for file system device #8" },
    { "-fs9", CALL_FUNCTION, 1, cmdline_fsdirectory, (void *) 9, NULL, NULL,
      "<name>", "Use <name> as directory for file system device #9" },
    { "-fs10", CALL_FUNCTION, 1, cmdline_fsdirectory, (void *) 10, NULL, NULL,
      "<name>", "Use <name> as directory for file system device #10" },
    { "-fs11", CALL_FUNCTION, 1, cmdline_fsdirectory, (void *) 11, NULL, NULL,
      "<name>", "Use <name> as directory for file system device #11" },
    { NULL }
};

int fsdevice_init_cmdline_options(void)
{
    return cmdline_register_options(cmdline_options);
}

/* ------------------------------------------------------------------------- */

int attach_fsdevice(int device, char *var, char *name)
{
    if (serial_attach_device(device, (char *) var, (char *) name,
			     read_fs, write_fs, open_fs, close_fs, flush_fs))
	return 1;
    fs_error(IPE_DOS_VERSION);
    return 0;
}

void fsdevice_set_directory(char *filename, int unit)
{
#if 0
    char *p;

    /* FIXME: Remove this once the select directory dialog is available.  */
    p = strrchr(filename, '/');
    *(++p) = '\0';
#endif

    switch (unit) {
      case 8:
	set_fsdevice_8_dir((resource_value_t) filename);
	break;
      case 9:
	set_fsdevice_9_dir((resource_value_t) filename);
	break;
      case 10:
	set_fsdevice_10_dir((resource_value_t) filename);
	break;
      case 11:
	set_fsdevice_11_dir((resource_value_t) filename);
	break;
    }
    return;
}

static char *fsdevice_get_path(int unit)
{
    switch (unit) {
      case 8:
	return fsdevice_8_dir;
	break;
      case 9:
	return fsdevice_9_dir;
	break;
      case 10:
	return fsdevice_10_dir;
	break;
      case 11:
	return fsdevice_11_dir;
	break;
    }
    return NULL;
}

void fs_error(int code)
{
    static int last_code;
    char *message;

    /* Only set an error once per command */
    if (code != IPE_OK && last_code != IPE_OK && last_code != IPE_DOS_VERSION)
	return;

    last_code = code;

    if (code == IPE_DOS_VERSION) {
	message = "VICE FS DRIVER V2.0";
    } else {
	errortext_t *e;
	e = &floppy_error_messages;
	while (e->nr >= 0 && e->nr != code)
	    e++;
	if (e->nr >= 0)
	    message = e->text;
	else
	    message = "UNKNOWN ERROR NUMBER";
    }

    sprintf(fs_errorl, "%02d,%s,00,00\015", code, message);

    fs_elen = strlen(fs_errorl);
    fs_eptr = 0;

    if (code && code != IPE_DOS_VERSION)
	fprintf(stderr, "UnixFS: ERR = %02d, %s\n", code, message);
}

void flush_fs(void *flp, int secondary)
{
    DRIVE *floppy = (DRIVE *)flp;
    char *cmd, *realarg, *arg, *realarg2 = NULL, *arg2 = NULL;
    char cbmcmd[MAXPATHLEN], name1[MAXPATHLEN], name2[MAXPATHLEN];
    int er = IPE_SYNTAX;
    FILE *fd;

    if (secondary != 15 || !fs_cptr)
	return;

    /* remove trailing cr */
    while (fs_cptr && (fs_cmdbuf[fs_cptr - 1] == 13))
	fs_cptr--;
    fs_cmdbuf[fs_cptr] = 0;

    strcpy(cbmcmd, fs_cmdbuf);
    petconvstring(cbmcmd, 1);	/* CBM name to FSname */
    cmd = cbmcmd;
    while (*cmd == ' ')
	cmd++;

    arg = strchr(cbmcmd, ':');
    if (arg) {
	*arg++ = '\0';
    }
    realarg = strchr(fs_cmdbuf, ':');
    if (realarg) {
	*realarg++ = '\0';
    }
#ifdef DEBUG_FS
    printf("Flush_FS: command='%s', cmd='%s'\n", cmd, arg);
#endif
    if (!strcmp(cmd, "cd")) {
	er = IPE_OK;
	if (chdir(arg)) {
	    er = IPE_NOT_FOUND;
	    if (errno == EPERM)
		er = IPE_PERMISSION;
	}
    } else if (!strcmp(cmd, "md")) {
	er = IPE_OK;
	if (mkdir(arg, /*S_IFDIR | */ 0770)) {
	    er = IPE_INVAL;
	    if (errno == EEXIST)
		er = IPE_FILE_EXISTS;
	    if (errno == EACCES)
		er = IPE_PERMISSION;
	    if (errno == ENOENT)
		er = IPE_NOT_FOUND;
	}
    } else if (!strcmp(cmd, "rd")) {
	er = IPE_OK;
	if (rmdir(arg)) {
	    er = IPE_NOT_EMPTY;
	    if (errno == EPERM)
		er = IPE_PERMISSION;
	}
    } else if (*cmd == 's') {
	er = IPE_DELETED;
	fd = fs_find_pc64_name(flp, realarg, strlen(realarg), name1);
	    if (fd != NULL) {
		fclose(fd);
	    } else {
		if (fsdevice_hide_cbm_files_enabled[floppy->unit - 8]) {
		    fs_error(IPE_NOT_FOUND);
		    fs_cptr = 0;
		    return;
		}
		strcpy(name1, fsdevice_get_path(floppy->unit));
                strcat(name1, "/");
		strcat(name1, arg);
	    }
	if (unlink(name1)) {
	    er = IPE_NOT_FOUND;
	    if (errno == EPERM)
		er = IPE_PERMISSION;
	}
    } else if (*cmd == 'r') {
	if ((arg2 = strchr(arg, '='))) {
	    char name2long[MAXPATHLEN];
	    er = IPE_OK;
	    *arg2++ = 0;
	    realarg2 = strchr(realarg, '=');
	    *realarg2++ = 0;
	    fd = fs_find_pc64_name(flp, realarg2, strlen(realarg2), name2long);
	    if (fd != NULL) {
		/* Rename P00 file.  */
		int name1len;
		char *p, p00name[17], p00type, p00count[2];
		char name1p00[MAXPATHLEN], name2p00[MAXPATHLEN];
		fclose(fd);
		strcpy(name2p00, name2long);
		p = strrchr(name2long, '.');
		p00type = p[1];
		*p = '\0';
		p = strrchr(name2long, '/');
		strcpy(name2, ++p);
		name1len = fsdevice_evaluate_name_p00(realarg, strlen(realarg),
							name1);
		name1[name1len] = '\0';
		memset(p00name, 0, 17);
		strncpy(p00name, realarg, 16);
		fd = fopen(name2p00, "r+");
		if (fd) {
		    if ((fseek(fd, 8, SEEK_SET) != 0)
                        || (fwrite(p00name, 16, 1, fd) < 1))
			er = IPE_NOT_FOUND;
		    fclose(fd);
		} else {
		    er = IPE_NOT_FOUND;
		}
		if (er == IPE_OK && strcmp(name1, name2) != 0) {
		    int i;
		    for (i = 0; i < 100; i++) {
			memset(name1p00, 0, MAXPATHLEN);
			strcpy(name1p00, fsdevice_get_path(floppy->unit));
                        strcat(name1p00, "/");
			strcat(name1p00, name1);
			strcat(name1p00, ".");
			strncat(name1p00, &p00type, 1);
			sprintf(p00count, "%02i", i);
			strncat(name1p00, p00count, 2);
			fd = fopen(name1p00, READ);
			if (fd) {
			    fclose(fd);
			    continue;
			}
			if (rename(name2p00, name1p00) == 0)
			    break;
		    }
		}
	    } else {
		/* Rename CBM file.  */
		if (fsdevice_hide_cbm_files_enabled[floppy->unit - 8]) {
		    fs_error(IPE_NOT_FOUND);
		    fs_cptr = 0;
		    return;
		}
		strcpy(name1, fsdevice_get_path(floppy->unit));
                strcat(name1, "/");
		strcat(name1, arg);
		strcpy(name2, fsdevice_get_path(floppy->unit));
                strcat(name2, "/");
		strcat(name2, arg2);
		if (rename(name2, name1)) {
		    er = IPE_NOT_FOUND;
		    if (errno == EPERM)
			er = IPE_PERMISSION;
		}
	    }
	}
    }
    fs_error(er);
    fs_cptr = 0;
}

int write_fs(void *flp, BYTE data, int secondary)
{
    if (secondary == 15) {
#ifdef DEBUG_FS_
	printf("Write_FS(secadr=15, data=%02x, '%c')\n", data, data);
#endif
	if (fs_cptr < MAXPATHLEN - 1) {		/* keep place for nullbyte */
	    fs_cmdbuf[fs_cptr++] = data;
	    return SERIAL_OK;
	} else {
	    fs_error(IPE_LONG_LINE);
	    return SERIAL_ERROR;
	}
    }
    if (fs_info[secondary].mode != Write && fs_info[secondary].mode != Append)
	return FLOPPY_ERROR;

    if (fs_info[secondary].fd) {
	fputc(data, fs_info[secondary].fd);
	return FLOPPY_COMMAND_OK;
    };

    return FLOPPY_ERROR;
}


int read_fs(void *flp, BYTE * data, int secondary)
{
    DRIVE *floppy = (DRIVE *)flp;
    int i, l, f;
    unsigned short blocks;
    struct dirent *dirp;	/* defined in /usr/include/sys/dirent.h */
    struct stat statbuf;
    struct fs_buffer_info *info = &fs_info[secondary];
    char rname[256];

    if (secondary == 15) {
	if (!fs_elen)
	    fs_error(IPE_OK);
	if (fs_eptr < fs_elen) {
	    *data = fs_errorl[fs_eptr++];
#ifdef DEBUG_FS
	    printf("Read_FS(secadr=15) reads '%c'\n", *data);
#endif
	    return SERIAL_OK;
	} else {
	    fs_error(IPE_OK);
	    *data = 0xc7;
	    return SERIAL_EOF;
	}
    }
    switch (info->mode) {
      case Write:
      case Append:
	  return FLOPPY_ERROR;

      case Read:
	  if (info->fd) {
	      i = fgetc(info->fd);
	      if (ferror(info->fd))
		  return FLOPPY_ERROR;
	      if (feof(info->fd)) {
		  *data = 0xc7;
		  return SERIAL_EOF;
	      }
	      *data = i;
	      return SERIAL_OK;
	  }
	  break;

      case Directory:
	  if (info->dp) {
	      if (info->buflen <= 0) {
		  char buf[MAXPATHLEN];

#ifdef DEBUG_FSDRIVE
		  printf("reading\n");
#endif

		  info->bufp = info->name;

		  if (info->eof) {
		      *data = 0xc7;
		      return SERIAL_EOF;
		  }
		  /*
		   * Find the next directory entry and return it as a CBM
		   * directory line.
		   */

		  /* first test if dirmask is needed - maybe this should be
		     replaced by some regex functions... */
#ifdef DEBUG_FS
		  printf("FS_ReadDir: mask ='%s'\n", fs_dirmask);
#endif
		  f = 1;
		  do {
		      char *p;
		      dirp = readdir(info->dp);
		      if (!dirp)
			  break;
#ifdef DEBUG_FS
		      printf("FS_ReadDir: testing file '%s'\n", dirp->d_name);
#endif
		      fs_info[secondary].type = FT_PRG;
		      strcpy(rname, dirp->d_name);
		      if (fsdevice_convert_p00_enabled[(floppy->unit) - 8])
			  fs_test_pc64_name(flp, rname, secondary);
			  if (strcmp(rname, dirp->d_name) == 0
			  && fsdevice_hide_cbm_files_enabled[floppy->unit - 8])
			      continue;
		      if (!*fs_dirmask)
			  break;
		      l = strlen(fs_dirmask);
		      for (p = rname, i = 0; *p && fs_dirmask[i] && i < l; i++) {
			  if (fs_dirmask[i] == '?') {
			      p++;
			  } else if (fs_dirmask[i] == '*') {
			      if (!fs_dirmask[i + 1]) {
				  f = 0;
				  break;
			      }	/* end mask */
			      while (*p && (*p != fs_dirmask[i + 1]))
				  p++;
			  } else {
			      if (*p != fs_dirmask[i])
				  break;
			      p++;
			  }
			  if ((!*p) && (!fs_dirmask[i + 1])) {
			      f = 0;
			      break;
			  }
		      }
		  }
		  while (f);

#ifdef DEBUG_FS
		  printf("FS_ReadDir: printing file '%s'\n",
			 dirp ? rname : NULL);
#endif

		  if (dirp != NULL) {
		      BYTE *p = info->name;
		      char *tp;

		      strcpy(buf, info->dir);
		      strcat(buf, "/");
		      tp = buf + strlen(buf);
		      strcat(buf, dirp->d_name);

		      /* Line link, Length and spaces */

		      p += 2;	/* skip link addr, fill in later */

		      if (stat(buf, &statbuf) < 0)
			  blocks = 0;	/* this file can't be opened */
		      else
			  blocks = (unsigned short) ((statbuf.st_size + 253) / 254);

		      SET_LO_HI(p, blocks);

		      if (blocks < 10)
			  *p++ = ' ';
		      if (blocks < 100)
			  *p++ = ' ';
		      if (blocks < 1000)
			  *p++ = ' ';

		      *p++ = ' ';


		      /*
		       * Filename
		       */

		      *p++ = '"';

		      if (strcmp(rname, dirp->d_name)) {
			  for (i = 0; rname[i] && (*p = rname[i]); ++i, ++p);
		      } else {
			  for (i = 0; tp[i] /*i < dirp->d_namlen */ &&
			       (*p = p_topetcii(tp[i] /*dirp->d_name[i] */ )); ++i, ++p);
		      }

		      *p++ = '"';
		      for (; i < 17; i++)
			  *p++ = ' ';

		      if (S_ISDIR(statbuf.st_mode)) {
			  *p++ = 'D';
			  *p++ = 'I';
			  *p++ = 'R';
		      } else {
			  switch(fs_info[secondary].type) {
			    case FT_DEL:
			      *p++ = 'D';
			      *p++ = 'E';
			      *p++ = 'L';
			      break;
			    case FT_SEQ:
			      *p++ = 'S';
			      *p++ = 'E';
			      *p++ = 'Q';
			      break;
			    case FT_PRG:
			      *p++ = 'P';
			      *p++ = 'R';
			      *p++ = 'G';
			      break;
			    case FT_USR:
			      *p++ = 'U';
			      *p++ = 'S';
			      *p++ = 'R';
			      break;
			    case FT_REL:
			      *p++ = 'R';
			      *p++ = 'E';
			      *p++ = 'L';
			      break;
			  }
		      }

		      *p = '\0';	/* to allow strlen */

		      /* some (really very) old programs rely on the directory
		         entry to be 32 Bytes in total (incl. nullbyte) */
		      l = strlen((char *) (info->name + 4)) + 4;
		      while (l < 31) {
			  *p++ = ' ';
			  l++;
		      }

		      *p++ = '\0';

		      info->dirmpos += p - info->name;
		      *info->name = info->dirmpos & 0xff;
		      *(info->name + 1) = (info->dirmpos >> 8) & 0xff;

		      info->buflen = (int) (p - info->name);

#ifdef DEBUG_FSDRIVE
		      printf("found %4d>%s< (%d)  buf:>%s< (%d)\n",
			     blocks, dirp->d_name, i,
			     info->name + 4, info->buflen);
#endif
		  } else {

		      /* EOF => End file */

		      memset(info->name, 0, 2);
		      info->buflen = 2;
		      info->eof++;
		  }
	      }			/* info->buflen */
	      *data = *info->bufp++;
	      info->buflen--;
	      return SERIAL_OK;

	  }			/* info->dp */
	  break;
    }

    return FLOPPY_ERROR;
}


int open_fs(void *flp, char *name, int length, int secondary)
{
    DRIVE *floppy = (DRIVE *)flp;
    FILE *fd;
    DIR *dp;
    BYTE *p, *linkp;
    char fsname[MAXPATHLEN], fsname2[MAXPATHLEN], rname[MAXPATHLEN];
    char *mask, *comma;
    int status = 0, i, reallength, readmode, rl;

    if (fs_info[secondary].fd)
	return FLOPPY_ERROR;

    memcpy(fsname2, name, length);
    fsname2[length] = 0;

    if (secondary == 15) {
#ifdef DEBUG_FS
	printf("Open_FS(secadr=15, name='%s'\n", fsname2);
#endif
	for (i = 0; i < length; i++) {
	    status = write_fs(flp, name[i], 15);
	}
	return status;
    }

    if (secondary == 1)
	readmode = FAM_WRITE;
    else
	readmode = FAM_READ;

    rl = 0;

    if (floppy_parse_name(fsname2, length, fsname, &reallength, &readmode,
			  &fs_info[secondary].type, &rl) != SERIAL_OK)
	return SERIAL_ERROR;

    if (fs_info[secondary].type == FT_DEL)
	fs_info[secondary].type = (secondary < 2) ? FT_PRG : FT_SEQ;

    fsname[reallength] = 0;
    strncpy(rname, fsname, reallength);

    petconvstring(fsname, 1);	/* CBM name to FSname */

    switch (readmode) {
      case FAM_WRITE:
	fs_info[secondary].mode = Write;
	break;
      case FAM_READ:
	fs_info[secondary].mode = Read;
	break;
      case FAM_APPEND:
	fs_info[secondary].mode = Append;
	break;
    }

    if (*name == '$') {	/* Directory read */
	if ((secondary != 0) || (fs_info[secondary].mode != Read)) {
	    fs_error(IPE_NOT_WRITE);
	    return FLOPPY_ERROR;
	}
	/* Test on wildcards.  */
	if (!(mask = strrchr(fsname, '/')))
	    mask = fsname;
	if (strchr(mask, '*') || strchr(mask, '?')) {
	    if (*mask == '/') {
		strcpy(fs_dirmask, mask + 1);
		*mask++ = 0;
	    } else {
		strcpy(fs_dirmask, mask);
		strcpy(fsname, fsdevice_get_path(floppy->unit));
	    }
	} else {
	    *fs_dirmask = 0;
	    if (!*fsname)
		strcpy(fsname, fsdevice_get_path(floppy->unit));
	}
#ifdef DEBUG_FS
	printf("Opening Dir with dir='%s', mask='%s')\n", fsname, fs_dirmask);
#endif
	/* trying to open */
	if (!(dp = opendir((char *) fsname))) {
	    for (p = (BYTE *) fsname; *p; p++)
		if (isupper(*p))
		    *p = tolower(*p);
	    if (!(dp = opendir((char *) fsname))) {
		fs_error(IPE_NOT_FOUND);
		return FLOPPY_ERROR;
	    }
	}
	strcpy(fs_info[secondary].dir, fsname);

	/*
	 * Start Address, Line Link and Line number 0
	 */

	p = fs_info[secondary].name;

	*p++ = 1;
	*p++ = 4;

	linkp = p;
	p += 2;

	*p++ = 0;
	*p++ = 0;

	*p++ = (BYTE) 0x12;	/* Reverse on */

	*p++ = '"';
	strcpy((char *) p, fs_info[secondary].dir);	/* Dir name */
	petconvstring((char *) p, 0);	/* ASCII name to PETSCII */
	i = 0;
	while (*p) {
	    ++p;
	    i++;
	}
	while (i < 16) {
	    *p++ = ' ';
	    i++;
	}
	*p++ = '"';
	while (i < 22) {
	    *p++ = ' ';
	    i++;
	}
	*p++ = 0;

	i = 0x0401 + p - linkp;
	*linkp = i & 0xff;
	*(linkp + 1) = (i >> 8) & 0xff;

	fs_info[secondary].buflen = p - fs_info[secondary].name;
	fs_info[secondary].bufp = fs_info[secondary].name;
	fs_info[secondary].mode = Directory;
	fs_info[secondary].dp = dp;
	fs_info[secondary].eof = 0;
	fs_info[secondary].dirmpos = i;		/* start address of next line */

#ifdef DEBUG_FSDRIVE
	printf("opened directory\n");
#endif


    } else {			/* Normal file, not directory ("$") */

	/* Override access mode if secondary address is 0 or 1.  */
	if (secondary == 0)
	    fs_info[secondary].mode = Read;
	if (secondary == 1)
	    fs_info[secondary].mode = Write;

	/* Remove comma.  */
	if (fsname[0] == ',') {
	    fsname[1] = '\0';
	} else {
	    comma = strchr(fsname, ',');
	    if (comma != NULL)
		*comma = '\0';
	}
	if (name[0] == ',') {
	    name[1] = '\0';
	} else {
	    comma = memchr(name, ',', length);
	    if (comma != NULL) {
		*comma = '\0';
		length = strlen(name);
	    }
	}

	strcpy(fsname2, fsname);
	strcpy(fsname, fsdevice_get_path(floppy->unit));
        strcat(fsname, "/");
	strcat(fsname, fsname2);

#ifdef DEBUG_FSDRIVE
    printf("Open file name '%s' (before wildcard expansion).\n", fsname);
#endif

	/* Test on wildcards.  */
	if (strchr(fsname2, '*') || strchr(fsname2, '?')) {
	    if (fs_info[secondary].mode == Write
				|| fs_info[secondary].mode == Append) {
		fs_error(IPE_BAD_NAME);
		return FLOPPY_ERROR;
	    } else {
		fsdevice_compare_file_name(flp, fsname2, fsname, secondary);
	    }
	}

#ifdef DEBUG_FSDRIVE
    printf("Open file name '%s' (after wildcard expansion).\n", fsname);
#endif

	/* Open file for write mode access.  */
	if (fs_info[secondary].mode == Write) {
	    fd = fopen(fsname, READ);
	    if (fd > 0) {
		fclose(fd);
		fs_error(IPE_FILE_EXISTS);
		return FLOPPY_ERROR;
	    }
	    if (fsdevice_convert_p00_enabled[(floppy->unit) - 8]) {
		fd = fs_find_pc64_name(flp, rname, reallength, fsname2);
		if (fd > 0) {
		    fclose(fd);
		    fs_error(IPE_FILE_EXISTS);
		    return FLOPPY_ERROR;
		}
	    }
	    if (fsdevice_save_p00_enabled[(floppy->unit) - 8]) {
		if (fsdevice_create_file_p00(flp, rname, reallength, fsname,
							secondary) > 0) {
		    fs_error(IPE_FILE_EXISTS);
		    return FLOPPY_ERROR;
		} else {
		    fd = fopen(fsname, "a+");
		    fs_info[secondary].fd = fd;
		    fs_error(IPE_OK);
		    return FLOPPY_COMMAND_OK;
		}
	    } else {
		fd = fopen(fsname, WRITE);
		fs_info[secondary].fd = fd;
		fs_error(IPE_OK);
		return FLOPPY_COMMAND_OK;
	    }
	}

	/* Open file for append mode access.  */
	if (fs_info[secondary].mode == Append) {
	    fd = fopen(fsname, READ);
	    if (!fd) {
		if (!fsdevice_convert_p00_enabled[(floppy->unit) - 8]) {
		    fs_error(IPE_NOT_FOUND);
		    return FLOPPY_ERROR;
		}
		fd = fs_find_pc64_name(flp, rname, reallength, fsname2);
		if (!fd) {
		    fs_error(IPE_NOT_FOUND);
		    return FLOPPY_ERROR;
		}
		fclose(fd);
		fd = fopen(fsname2, "a+");
		if (!fd) {
		    fs_error(IPE_NOT_FOUND);
		    return FLOPPY_ERROR;
		}
		fs_info[secondary].fd = fd;
		fs_error(IPE_OK);
		return FLOPPY_COMMAND_OK;
	    } else {
		fclose(fd);
		fd = fopen(fsname, "a+");
		if (!fd) {
		    fs_error(IPE_NOT_FOUND);
		    return FLOPPY_ERROR;
		}
		fs_info[secondary].fd = fd;
		fs_error(IPE_OK);
		return FLOPPY_COMMAND_OK;
	    }
	}

	/* Open file for read mode access.  */
 	fd = fopen(fsname, READ);
	if (!fd) {
	    if (!fsdevice_convert_p00_enabled[(floppy->unit) - 8]) {
		fs_error(IPE_NOT_FOUND);
		return FLOPPY_ERROR;
	    }
	    fd = fs_find_pc64_name(flp, rname, reallength, fsname2);
	    if (!fd) {
		fs_error(IPE_NOT_FOUND);
		return FLOPPY_ERROR;
	    }
	    fs_info[secondary].fd = fd;
	    fs_error(IPE_OK);
	    return FLOPPY_COMMAND_OK;
	} else {
	    if (fsdevice_hide_cbm_files_enabled[floppy->unit - 8]) {
		fclose(fd);
		fs_error(IPE_NOT_FOUND);
		return FLOPPY_ERROR;
	    }
	    fs_info[secondary].fd = fd;
	    fs_error(IPE_OK);
	    return FLOPPY_COMMAND_OK;
	}
    }
    fs_error(IPE_OK);
    return FLOPPY_COMMAND_OK;
}


int close_fs(void *flp, int secondary)
{
    if (secondary == 15) {
#ifdef DEBUG_FS
	printf("Close_FS(secadr=15)\n");
#endif
	fs_error(IPE_OK);
	return FLOPPY_COMMAND_OK;
    }
    switch (fs_info[secondary].mode) {
      case Write:
      case Read:
      case Append:
	  if (!fs_info[secondary].fd)
	      return FLOPPY_ERROR;

	  fclose(fs_info[secondary].fd);
	  fs_info[secondary].fd = NULL;
	  break;

      case Directory:
	  if (!fs_info[secondary].dp)
	      return FLOPPY_ERROR;

	  closedir(fs_info[secondary].dp);
	  fs_info[secondary].dp = NULL;
	  break;
    }

    return FLOPPY_COMMAND_OK;
}

void fs_test_pc64_name(void *flp, char *rname, int secondary)
{
    DRIVE *floppy = (DRIVE *)flp;
    char p00id[8];
    char p00name[17];
    char pathname[MAXPATHLEN];
    FILE *fd;
    int tmptype;

    tmptype = is_pc64name(rname);
    if (tmptype >= 0) {
	strcpy(pathname, fsdevice_get_path(floppy->unit));
        strcat(pathname, "/");
	strcat(pathname, rname);
	fd = fopen(pathname, READ);
	if (!fd)
	    return;

	fread((char *) p00id, 8, 1, fd);
	if (ferror(fd)) {
	    fclose(fd);
	    return;
	}
	p00id[7] = '\0';
	if (!strncmp(p00id, "C64File", 7)) {
	    fread((char *) p00name, 16, 1, fd);
	    if (ferror(fd)) {
		fclose(fd);
		return;
	    }
	    fs_info[secondary].type = tmptype;
	    p00name[16] = '\0';
	    strcpy(rname, p00name);
	    fclose(fd);
	    return;
	}
	fclose(fd);
    }
}


FILE *fs_find_pc64_name(void *flp, char *name, int length, char *pname)
{
    DRIVE *floppy = (DRIVE *)flp;
    struct dirent *dirp;
    char *p;
    DIR *dp;
    char p00id[8], p00name[17], p00dummy[2];
    FILE *fd;

    name[length] = '\0';

    dp = opendir(fsdevice_get_path(floppy->unit));
    do {
	dirp = readdir(dp);
	if (dirp != NULL) {
	    strcpy(pname, fsdevice_get_path(floppy->unit));
            strcat(pname, "/");
	    strcat(pname, dirp->d_name);
	    p = pname;
	    if (is_pc64name(p) >= 0) {
		fd = fopen(p, READ);
		if (!fd)
		    continue;
		fread((char *) p00id, 8, 1, fd);
		if (ferror(fd)) {
		    fclose(fd);
		    continue;
		}
		p00id[7] = '\0';
		if (!strncmp(p00id, "C64File", 7)) {
		    fread((char *) p00name, 16, 1, fd);
		    if (ferror(fd)) {
			fclose(fd);
			continue;
		    }
		    p00name[16] = '\0';
		    if (fsdevice_compare_wildcards(name, p00name) > 0) {
			fread((char *) p00dummy, 2, 1, fd);
			if (ferror(fd)) {
			    fclose(fd);
			    continue;
			}
			return fd;
		    }
		}
		fclose(fd);
	    }
	}
    }
    while (dirp != NULL);
    closedir(dp);
    return NULL;
}

static int fsdevice_compare_wildcards(char *name, char *p00name)
{
    int i, len;

    len = strlen(name);
    if (len == 0)
	return 0;

    for (i = 0; i < len; i++) {
	if (name[i] == '*')
	    return 1;
	if (name[i] != '?' && name[i] != p00name[i])
	    return 0;
    }
    return 1;
}

static void fsdevice_compare_file_name(void *flp, char *fsname2, char *fsname,
                                       int secondary)
{
    DRIVE *floppy = (DRIVE *)flp;
    struct dirent *dirp;
    DIR *dp;
    char rname[MAXPATHLEN];

    dp = opendir(fsdevice_get_path(floppy->unit));
    do {
	dirp = readdir(dp);
	if (dirp != NULL) {
	    if (fsdevice_compare_wildcards(fsname2, dirp->d_name) > 0) {
		strcpy(rname, dirp->d_name);
		fs_test_pc64_name(flp, rname, secondary);
		if (strcmp(rname, dirp->d_name) == 0) {
		    strcpy(fsname, fsdevice_get_path(floppy->unit));
		    strcat(fsname, "/");
		    strcat(fsname, dirp->d_name);
		    closedir(dp);
		    return;
		}
	    }
	}
    }
    while (dirp != NULL);
    closedir(dp);
    return;
}

static int fsdevice_create_file_p00(void *flp, char *name, int length,
                                     char *fsname, int secondary)
{
    DRIVE *floppy = (DRIVE *)flp;
    char filename[17], realname[16];
    int i, len;
    FILE *fd;

    if (length > 16)
	length = 16;
    memset(realname, 0, 16);
    strncpy(realname, name, length);

    len = fsdevice_evaluate_name_p00(name, length, filename);

    strcpy(fsname, fsdevice_get_path(floppy->unit));
    strcat(fsname, "/");
    strncat(fsname, filename, len);
    switch (fs_info[secondary].type) {
      case FT_DEL:
	strcat(fsname, ".D");
	break;
      case FT_SEQ:
	strcat(fsname, ".S");
	break;
      case FT_PRG:
	strcat(fsname, ".P");
	break;
      case FT_USR:
	strcat(fsname, ".U");
	break;
      case FT_REL:
	strcat(fsname, ".R");
	break;
    }
    strcat(fsname, "00");

    for (i = 1; i < 100; i++) {
	fd = fopen(fsname, READ);
	if (!fd)
	    break;
	fclose(fd);
	sprintf(&fsname[strlen(fsname) - 2], "%02i", i);
    }

    if (i >= 100)
	return 1;

    fd = fopen(fsname, WRITE);
    if (!fd)
	return 1;

    if (fwrite("C64File", 8, 1, fd) < 1) {
	fclose(fd);
	return 1;
    }
    if (fwrite(realname, 16, 1, fd) < 1) {
	fclose(fd);
	return 1;
    }
    if (fwrite("\0\0", 2, 1, fd) < 1) {
	fclose(fd);
	return 1;
    }
    fclose(fd);
    return 0;
}

static int fsdevice_reduce_filename_p00(char *filename, int len)
{
    int i, j;

    for (i = len - 1; i >= 0; i--) {
	if (filename[i] == '_')
	    if (fsdevice_eliminate_char_p00(filename, i) <= 8)
		return 8;
	}

    for (i = 0; i < len; i++) {
	if (strchr("AEIOU", filename[i]) != NULL)
	    break;
    }

    for (j = len - 1; j >= i; j--) {
	if (strchr("AEIOU", filename[j]) != NULL)
	    if (fsdevice_eliminate_char_p00(filename, j) <= 8)
		return 8;
    }

    for (i = len - 1; i >= 0; i--) {
	if (isalpha(filename[i]))
	    if (fsdevice_eliminate_char_p00(filename, i) <= 8)
		return 8;
    }

    for (i = len - 1; i >= 0; i--)
	if (fsdevice_eliminate_char_p00(filename, i) <= 8)
	    return 8;

    return 1;
}

static int fsdevice_eliminate_char_p00(char *filename, int pos)
{
    memcpy(&filename[pos], &filename[pos+1], 16 - pos);
    return strlen(filename);
}

static int fsdevice_evaluate_name_p00(char *name, int length, char *filename)
{
    int i, j;

    memset(filename, 0, 17);

    for (i = 0, j = 0; i < length; i++) {
	switch (name[i]) {
	  case ' ':
	  case '-':
	    filename[j++] = '_';
	    break;
	  default:
	    if (islower(name[i])) {
		filename[j++] = toupper(name[i]);
		break;
	    }
	    if (isalnum(name[i])) {
		filename[j++] = name[i];
		break;
	    }
	}
    }
    if (j == 0) {
	strcpy(filename, "_");
	j++;
    }
    return ((j > 8) ? fsdevice_reduce_filename_p00(filename, j) : j);
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.