Logo Search packages:      
Sourcecode: cbmconvert version File versions  Download package

enum RdStatus ReadLynx ( FILE *  file,
const char *  filename,
write_file_t  writeCallback,
log_t  log 
)

Read and convert a Lynx archive

Parameters:
file the file input stream
filename host system name of the file
writeCallback function for writing the contained files
log Call-back function for diagnostic output
Returns:
status of the operation

Definition at line 43 of file lynx.c.

References DEL, Errors, MAXBASICLENGTH, Filename::name, PRG, RdFail, RdNoSpace, RdOK, Filename::recordLength, REL, SEQ, Filename::type, USR, Warnings, WrFail, WrNoSpace, and WrOK.

{
  struct Filename name;
  unsigned f, fcount;

  /* File positions */
  long headerPos; /* current header position */
  long headerEnd; /* end of header (start of archive) */
  long archivePos; /* current archive position */

  bool errNoLength = false; /* set if the file length is unknown */

  {
    byte_t* buf;
    int i;
    long length;

    if (!(buf = malloc (MAXBASICLENGTH))) {
    memError:
      (*log) (Errors, 0, "Out of memory.");
      return RdFail;
    }

    length = fread (buf, 1, MAXBASICLENGTH, file);

    if (fseek (file, 0, SEEK_SET)) {
    seekError:
      free (buf);
      (*log) (Errors, 0, "fseek: %s", strerror(errno));
      return RdFail;
    }

    /* skip the BASIC header, if any */
    for (i = 4; i < MAXBASICLENGTH && i < length; i++)
      if (!(memcmp (&buf[i - 4], "\0\0\0\15", 4))) {
      /* skip the BASIC header */
      if (fseek (file, i, SEEK_SET))
        goto seekError;
      break;
      }

    free (buf);
  }

  /* Determine number of blocks and files */
  {
    char lynxhdr[25];
    unsigned blkcount;

    if (3 != fscanf (file, " %u  %24c\15 %u%*2[ \15]",
                 &blkcount, lynxhdr, &fcount) ||
      !blkcount ||
      !strstr (lynxhdr, "LYNX") ||
      !fcount) {
      (*log) (Errors, 0, "Not a Lynx archive.");
      return RdFail;
    }

    /* Set the file pointers. */
    headerPos = ftell (file);
    headerEnd = archivePos = 254 * blkcount;
  }

  /* start extracting files */

  for (f = 0; f++ < fcount;) {
    unsigned length, blocks;

    if (headerPos >= headerEnd) {
    hdrError:
      (*log) (Errors, 0, "Lynx header error.");
      return RdFail;
    }

    if (fseek (file, headerPos, 0)) {
      (*log) (Errors, 0, "fseek: %s", strerror(errno));
      return RdFail;
    }

    /* read the file header information */
    {
      unsigned i;
      int j;

      /* read the file name */
      for (i = 0; i < 17; i++) {
      j = fgetc(file);

      switch (j) {
      case EOF:
        goto hdrError;

      case 13: /* file name terminator */
        break;

      default: /* file name character */
        if (i > 15) {
          (*log) (Errors, 0, "Too long file name");
          return RdFail;
        }

        name.name[i] = j;
      }

      if (j == 13) break;
      }

      if (!i) {
      (*log) (Warnings, 0, "blank file name");
      }

      /* pad the rest of the file name with shifted spaces */
      for (; i < 16; i++)
      name.name[i] = 0xA0;
    }

    {
      char filetype;
      unsigned len;
      bool notLastFile = f < fcount;

      /* set the file type */
      if (2 != fscanf (file, " %u \015%c\015", &blocks, &filetype))
      goto hdrError;

      if (!fscanf (file, " %u%*2[ \015]", &len)) {
      /* Unspecified file length */
      if (filetype == 'R' || !notLastFile)
        /* The length must be known for relative files */
        /* and for all but the last file. */
        goto hdrError;

      errNoLength = true;
      len = 255;
      }

      length = len;

      name.recordLength = 0;

      switch (filetype) {
      int sidesectors;

      default:
      name.type = 0;
      (*log) (Errors, &name, "Unknown type, defaulting to DEL");
      /* fall through */
      case 'D':
      name.type = DEL;
      break;
      case 'S':
      name.type = SEQ;
      break;
      case 'P':
      name.type = PRG;
      break;
      case 'U':
      name.type = USR;
      break;
      case 'R':
      name.type = REL;
      name.recordLength = length;

      /* Thanks to Peter Schepers <schepers@ist.uwaterloo.ca>
         for pointing out the error in the original formula. */
      sidesectors = (blocks + 119) / 121;

      if (!sidesectors ||
          blocks < 121 * sidesectors - 119 ||
          blocks > 121 * sidesectors)
        goto hdrError; /* negative length file */

      blocks -= sidesectors;
      /* Lynx is stupid enough to store the side sectors in the file. */
      archivePos += 254 * sidesectors;

      if (!(fscanf (file, " %u \015", &length))) {
        if (notLastFile)
          goto hdrError;

        errNoLength = true;
        length = 255;
      }

      if (!name.recordLength)
        (*log) (Warnings, &name, "zero record length");

      break;
      }

      if ((blocks && length < 2) || (length == 1) || (!blocks && length)) {
      (*log) (Errors, &name, "illegal length, skipping file");
      (*log) (Errors, &name,
            "FATAL: the archive may be corrupted from this point on!");
      continue;
      }

      if (blocks)
      length += (unsigned)blocks * 254 - 255;
      else
      length = 0;

      if (name.type == REL && name.recordLength && length % name.recordLength)
      (*log) (Warnings, &name, "non-integer record count");
    }

    headerPos = ftell (file);

    /* Extract the file */

    {
      byte_t* buf;
      size_t readlength;

      if (fseek (file, archivePos, SEEK_SET)) {
      (*log) (Errors, &name, "fseek: %s", strerror(errno));
      return RdFail;
      }

      if (!(buf = malloc (length)))
      goto memError;

      if (length != (readlength = fread (buf, 1, length, file))) {
      if (feof (file)) {
        (*log) (Warnings, &name, "Truncated file, proceeding anyway");
      }
      if (ferror (file)) {
        free (buf);
        (*log) (Errors, &name, "fread: %s", strerror(errno));
        return RdFail;
      }
      }

      archivePos += 254 * blocks;

      switch ((*writeCallback) (&name, buf, readlength)) {
      case WrOK:
      break;
      case WrNoSpace:
      free (buf);
      return RdNoSpace;
      case WrFail:
      default:
      free (buf);
      return RdFail;
      }

      free (buf);
    }
  }

  if (errNoLength)
    (*log) (Warnings, 0, "The last file may be too long.");

  return RdOK;
}


Generated by  Doxygen 1.6.0   Back to index