home *** CD-ROM | disk | FTP | other *** search
/ Tools / WinSN5.0Ver.iso / NETSCAP.50 / WIN1998.ZIP / ns / cmd / macfe / utility / MacBinSupport.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-04-08  |  14.6 KB  |  462 lines

  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
  2.  *
  3.  * The contents of this file are subject to the Netscape Public License
  4.  * Version 1.0 (the "NPL"); you may not use this file except in
  5.  * compliance with the NPL.  You may obtain a copy of the NPL at
  6.  * http://www.mozilla.org/NPL/
  7.  *
  8.  * Software distributed under the NPL is distributed on an "AS IS" basis,
  9.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
  10.  * for the specific language governing rights and limitations under the
  11.  * NPL.
  12.  *
  13.  * The Initial Developer of this code under the NPL is Netscape
  14.  * Communications Corporation.  Portions created by Netscape are
  15.  * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
  16.  * Reserved.
  17.  */
  18.  
  19. /* MacBinary support.c
  20.  
  21. This file implements MacBinary (actually MacBinary II) support for the following:
  22.  
  23.     -Determine the size of a file when encoded in MacBinary
  24.  
  25.     -Inline encoding of a file in MacBinary format
  26.  
  27. The MacBinary II format consists of a 128-byte header containing all the
  28. information necessary to reproduce the document's directory entry on the
  29. receiving Macintosh; followed by the document's Data Fork (if it has one),
  30. padded with nulls to a multiple of 128 bytes (if necessary); followed by the
  31. document's Resource Fork (again, padded if necessary). The lengths of these
  32. forks (either or both of which may be zero) are contained in the header. 
  33.  
  34. The format of the header for MacBinary II is as follows:
  35.  
  36.   Offset 000    Byte        old version number, must be kept at zero for compatibility
  37.   Offset 001    Byte        Length of filename (must be in the range 1-63)
  38.   Offset 002    1 to 63 chars, filename (only "length" bytes are significant).
  39.   Offset 065    Long    file type (normally expressed as four characters)
  40.   Offset 069    Long    file creator (normally expressed as four characters)
  41.   Offset 073    Byte        original Finder flags
  42.                                  Bit 7 - Locked.
  43.                                  Bit 6 - Invisible.
  44.                                  Bit 5 - Bundle.
  45.                                  Bit 4 - System.
  46.                                  Bit 3 - Bozo.
  47.                                  Bit 2 - Busy.
  48.                                  Bit 1 - Changed.
  49.                                  Bit 0 - Inited.
  50.   Offset 074    Byte    zero fill, must be zero for compatibility
  51.   Offset 075    Short    file's vertical position within its window.
  52.   Offset 077    Short    file's horizontal position within its window.
  53.   Offset 079    Short    file's window or folder ID.
  54.   Offset 081    Byte    "Protected" flag (in low order bit).
  55.   Offset 082    Byte    zero fill, must be zero for compatibility
  56.   Offset 083    Long    Data Fork length (bytes, zero if no Data Fork).
  57.   Offset 087    Long    Resource Fork length (bytes, zero if no R.F.).
  58.   Offset 091    Long    File's creation date
  59.   Offset 095    Long    File's "last modified" date.
  60.   Offset 099    Short    zero fill (was file comment length which is not supported)
  61.   Offset 101    Byte    Finder Flags, bits 0-7. (Bits 8-15 are already in byte 73)
  62.   Offset 116    Long    Length of total files when packed files are unpacked.
  63.                          This is only used by programs that pack and unpack on the fly,
  64.                          mimicing a standalone utility such as PackIt.  A program that is
  65.                          uploading a single file must zero this location when sending a
  66.                          file.  Programs that do not unpack/uncompress files when
  67.                          downloading may ignore this value.
  68.   Offset 120    Short    zero fill (was length of a secondary header which is not supported)
  69.   Offset 122    Byte    Version number of Macbinary II that the uploading program
  70.                              is written for (the version begins at 129)
  71.   Offset 123    Byte    Minimum MacBinary II version needed to read this file
  72.                              (start this value at 129)
  73.   Offset 124    Short    CRC of previous 124 bytes
  74.  
  75. All values are stored in normal 68000 order, with Most Significant Byte
  76. appearing first then the file.  Any bytes in the header not defined above
  77. should be set to zero.
  78.  
  79.  
  80. MacBinary header creation and CRC calculation based on Erny Tontlinger's free 'Terminal' source
  81.  
  82. */
  83.  
  84. #include "xp.h"
  85.  
  86. #include "MacBinSupport.h"
  87.  
  88. #include "MoreFilesExtras.h"
  89.  
  90. enum
  91. {
  92.     kMB_SendingHeader,            /* Data from read is MB header */
  93.     kMB_SetupDataFork,            /* Finished sending MB header so prepare data fork */
  94.     kMB_SendingDataFork,        /* Data from read is file's data fork */
  95.     kMB_SetupResFork,            /* Finished sending data fork so prepare res fork */
  96.     kMB_SendingResFork,            /* Data from read is file's data fork */
  97.     kMB_FinishedFile            /* Nothing left to send - file finished */
  98.     
  99. };
  100.  
  101. static Byte fillerBuf[kMBHeaderLength];
  102.  
  103. static unsigned short CalcMacBinaryCRC(Byte *ptr, unsigned long count)
  104. {
  105.     unsigned short crc;
  106.     unsigned short i;
  107.  
  108.     crc = 0;
  109.     while (count-- > 0) {
  110.         crc = crc ^ (unsigned short)*ptr++ << 8;
  111.         for (i = 0; i < 8; ++i)
  112.             if (crc & 0x8000)
  113.                 crc = crc << 1 ^ 0x1021;
  114.             else
  115.                 crc = crc << 1;
  116.     }
  117.     return crc;
  118. }
  119.  
  120. static OSErr InitMacBinaryHeader(MB_FileSpec *mbFileSpec)
  121. {
  122. #define    MB_VersionNumber    129
  123. #define MBH_name            1
  124. #define MBH_info1            65
  125. #define MBH_protected        81
  126. #define MBH_dLength            83
  127. #define MBH_rLength            87
  128. #define MBH_creation        91
  129. #define MBH_modification    95
  130. #define MBH_getInfoLength    99
  131. #define MBH_info2            101
  132. #define MBH_filesLength        116
  133. #define MBH_sHeaderLength    120
  134. #define MBH_newVersion        122
  135. #define MBH_minimumVersion    123
  136. #define MBH_crc                124
  137.  
  138.     OSErr             theErr;
  139.     unsigned short    crc;
  140.     HParamBlockRec    param;
  141.  
  142.     XP_MEMSET(¶m, 0, sizeof(param));
  143.     param.fileParam.ioNamePtr = (StringPtr)mbFileSpec->theFileSpec.name;
  144.     param.fileParam.ioVRefNum = mbFileSpec->theFileSpec.vRefNum;
  145.     param.fileParam.ioDirID = mbFileSpec->theFileSpec.parID;
  146.     theErr = PBHGetFInfoSync(¶m);
  147.     if (theErr != noErr)
  148.         return (theErr);
  149.  
  150.     XP_MEMSET(mbFileSpec->mbHeader, 0, kMBHeaderLength);
  151.  
  152.     XP_MEMCPY(
  153.         &mbFileSpec->mbHeader[MBH_name],
  154.         mbFileSpec->theFileSpec.name, mbFileSpec->theFileSpec.name[0] + 1);
  155.     mbFileSpec->mbHeader[MBH_info2] = param.fileParam.ioFlFndrInfo.fdFlags & 0x00FF;
  156.     param.fileParam.ioFlFndrInfo.fdFlags &= 0xFF00;
  157.     *(long *)¶m.fileParam.ioFlFndrInfo.fdLocation = 0;
  158.     param.fileParam.ioFlFndrInfo.fdFldr = 0;
  159.     XP_MEMCPY(&mbFileSpec->mbHeader[MBH_info1], (void *)¶m.fileParam.ioFlFndrInfo, 16);
  160.     mbFileSpec->mbHeader[MBH_protected] = (Byte)(param.fileParam.ioFlAttrib) & 0x01;
  161.     XP_MEMCPY(&mbFileSpec->mbHeader[MBH_dLength], (void *)¶m.fileParam.ioFlLgLen, 4);
  162.     XP_MEMCPY(&mbFileSpec->mbHeader[MBH_rLength], (void *)¶m.fileParam.ioFlRLgLen, 4);
  163.     XP_MEMCPY(&mbFileSpec->mbHeader[MBH_creation], (void *)¶m.fileParam.ioFlCrDat, 4);
  164.     XP_MEMCPY(&mbFileSpec->mbHeader[MBH_modification], (void *)¶m.fileParam.ioFlMdDat, 4);
  165.     mbFileSpec->mbHeader[MBH_newVersion] = MB_VersionNumber;
  166.     mbFileSpec->mbHeader[MBH_minimumVersion] = MB_VersionNumber;
  167.     crc = CalcMacBinaryCRC(mbFileSpec->mbHeader, 124);
  168.     XP_MEMCPY(&mbFileSpec->mbHeader[MBH_crc], (void *)&crc, 2);
  169.     
  170.     mbFileSpec->dataForkLength = param.fileParam.ioFlLgLen;
  171.     mbFileSpec->resForkLength = param.fileParam.ioFlRLgLen;
  172.     
  173.     return (noErr);
  174. }
  175.  
  176. static long PadBufferToMacBinBlock(char *buf, long bytesInBuf)
  177. {
  178.     long    paddingNeeded = 0;
  179.     
  180.     if (bytesInBuf % kMBHeaderLength)
  181.     {
  182.         paddingNeeded = kMBHeaderLength - (bytesInBuf % kMBHeaderLength);
  183.         XP_MEMCPY(&buf[bytesInBuf], fillerBuf, paddingNeeded);
  184.     }
  185.     
  186.     return (bytesInBuf + paddingNeeded);
  187. }
  188.  
  189. /* MB_Stat
  190.  
  191.     Returns the size of the file when encoded in MacBinary format
  192.     
  193.     This is computed as 128 bytes for the MacBinary header + the size of the data fork
  194.     rounded up to a multiple of 128 bytes + the size of the resource fork rounded up to
  195.     a multiple of 128 bytes.
  196. */
  197.  
  198. int MB_Stat( const char* name, XP_StatStruct * outStat,  XP_FileType type )
  199. {
  200.     int        result = -1;
  201.     long    totalFileSize = 0;
  202.     char    *newName = WH_FileName( name, type );
  203.     FSSpec    fileSpec = {0, 0, "\p"};
  204.     OSErr    theErr;
  205.     long    dataSize;
  206.     long    rsrcSize;
  207.     long    sizeRemainder;
  208.     
  209.     /* See if we managed to copy the name */
  210.     if (!newName)
  211.         return (result);
  212.     
  213.     /* Now see if we can find out something about the file */
  214.     theErr = FSSpecFromPathname_CWrapper(newName, &fileSpec);
  215.     if (theErr == noErr)
  216.     {
  217.         theErr = FSpGetFileSize(&fileSpec, &dataSize, &rsrcSize);
  218.         if (theErr == noErr)
  219.         {
  220.             result = 0;                /* Set the result code to success */
  221.             
  222.             totalFileSize = kMBHeaderLength;    /* 128 bytes for the MacBin header */
  223.             
  224.             /* Make sure data and rsrc sizes are multiples of 128 bytes
  225.                 before adding them to totalFileSize */
  226.             sizeRemainder = dataSize % kMBHeaderLength;
  227.             if (sizeRemainder)
  228.             {
  229.                 dataSize += (kMBHeaderLength - sizeRemainder);
  230.             }
  231.             totalFileSize += dataSize;
  232.             
  233.             sizeRemainder = rsrcSize % kMBHeaderLength;
  234.             if (sizeRemainder)
  235.             {
  236.                 rsrcSize += (kMBHeaderLength - sizeRemainder);
  237.             }
  238.             totalFileSize += rsrcSize;
  239.             
  240.             /* Stuff the calculated file size into the appropriate stat field */
  241.             outStat->st_size = totalFileSize;
  242.         }
  243.     }
  244.     
  245.     XP_FREE(newName);
  246.     
  247.     return (result);
  248. }
  249.  
  250. /* MB_Open
  251.  
  252.     Prepares a file for inline encoding in the MacBinary format.  It builds the
  253.     MacBinary header and initializes the state machine for the encoding process.
  254.     
  255.     
  256.     Get file info
  257.     Build MacBinary header
  258.     Set state machine to kMB_SendingHeader
  259.  
  260. */
  261.  
  262. OSErr MB_Open(const char *name, MB_FileSpec *mbFileSpec)
  263. {
  264.     char    *newName = WH_FileName( name, xpFileToPost );
  265.     OSErr    theErr = noErr;
  266.  
  267.     /* See if we managed to copy the name */
  268.     if (!newName)
  269.         return (fnfErr);
  270.     
  271.     /* Now see if we can find make an FSSpec for the file */
  272.     theErr = FSSpecFromPathname_CWrapper(newName, &mbFileSpec->theFileSpec);
  273.     if (theErr == noErr)
  274.     {
  275.         theErr = InitMacBinaryHeader(mbFileSpec);
  276.         if (theErr == noErr)
  277.         {
  278.             // Set up the rest of the MB_FileSpec info
  279.             mbFileSpec->fileState = kMB_SendingHeader;
  280.             mbFileSpec->fileRefNum = -1;
  281.             mbFileSpec->dataBytesRead = 0;
  282.             mbFileSpec->resBytesRead = 0;
  283.         }
  284.     }
  285.     
  286.     /* Make sure our filler buffer is cleared */
  287.     XP_MEMSET(fillerBuf, 0, kMBHeaderLength);
  288.  
  289.     return (theErr);
  290. }
  291.  
  292. /* MB_Read
  293.  
  294.     Provides a MacBinary encoded version of a file.  All reads are done to the supplied
  295.     buffer.  The MB_Read function just returns how many bytes have been placed in the
  296.     buffer.
  297.     
  298.     Switch on state
  299.         kMB_SendingHeader
  300.             set state to kMB_SetupDataFork
  301.             Return the # of bytes in an MB header
  302.         kMB_SetupDataFork
  303.             If hasDataFork
  304.                 Open the data fork of the file
  305.                 Set the state to kMB_SendingDataFork
  306.                 GOTO kMB_SendingDataFork
  307.             else
  308.                 GOTO kMB_SetupResFork
  309.         kMB_SendingDataFork
  310.             Read a buffer's worth of data
  311.                 Make sure buffer is padded to MB block boundary
  312.                 If EOF set state to kMB_SetupResFork
  313.             Return the # of bytes in buffer
  314.         kMB_SetupResFork
  315.             Close data fork if open
  316.             If hasResFork
  317.                 Open the resource fork of the file
  318.                 Set the state to kMB_SendingResFork
  319.                 GOTO kMB_SendingResFork
  320.             else
  321.                 GOTO kMB_FinishedFile
  322.         kMB_SendingResFork
  323.             Read a buffer's worth of data
  324.                 Make sure buffer is padded to MB block boundary
  325.                 If EOF set state to kMB_FinishedFile
  326.             Return the # of bytes in buffer
  327.         kMB_FinishedFile
  328.             Close resource fork if open
  329.             Set the state to kMB_FinishedFile
  330.         
  331. */
  332. int32    MB_Read(char *buf, int bufSize, MB_FileSpec *mbFileSpec)
  333. {
  334.     long    bytesInBuf = 0;
  335.     OSErr    theErr = noErr;
  336.     long    count = bufSize;
  337.     
  338.     switch (mbFileSpec->fileState)
  339.     {
  340.         case kMB_SendingHeader:
  341.             XP_MEMCPY(buf, mbFileSpec->mbHeader, kMBHeaderLength);
  342.             bytesInBuf = kMBHeaderLength;
  343.             mbFileSpec->fileState = kMB_SetupDataFork;
  344.             break;
  345.         
  346.         case kMB_SetupDataFork:
  347.             if (mbFileSpec->dataForkLength)
  348.             {
  349.                 theErr = FSpOpenDF(&mbFileSpec->theFileSpec, fsRdPerm, &mbFileSpec->fileRefNum);
  350.                 if (theErr == noErr)
  351.                 {
  352.                     mbFileSpec->fileState = kMB_SendingDataFork;
  353.                     
  354.                     /* Now that the data fork is open jump into the read state */
  355.                     goto SendingDataFork;
  356.                 }
  357.                 else
  358.                 {    /* Couldn't open the data fork so exit the state machine */
  359.                     goto FinishedFile;
  360.                 }
  361.             }
  362.             else
  363.             {    /* Apparently no data fork so go ahead and try the res fork */
  364.                 goto SetupResFork;
  365.             }
  366.             break;
  367.  
  368.         case kMB_SendingDataFork:
  369. SendingDataFork:
  370.             theErr = FSRead(mbFileSpec->fileRefNum, &count, buf);
  371.             if (theErr == noErr || theErr == eofErr)
  372.             {
  373.                 bytesInBuf = PadBufferToMacBinBlock(buf, count);
  374.                 mbFileSpec->dataBytesRead += count;
  375.                 
  376.                 /* See if we've reached EOF for the data */
  377.                 if (mbFileSpec->dataBytesRead == mbFileSpec->dataForkLength)
  378.                     mbFileSpec->fileState = kMB_SetupResFork;
  379.             }
  380.             else
  381.             {    /* Got some sort of error reading the data fork so exit the state machine */
  382.                 goto FinishedFile;
  383.             }
  384.             break;
  385.         
  386.         case kMB_SetupResFork:
  387. SetupResFork:
  388.             /* Close the data fork */
  389.             if (mbFileSpec->fileRefNum != -1)
  390.             {
  391.                 FSClose(mbFileSpec->fileRefNum);
  392.                 mbFileSpec->fileRefNum = -1;
  393.             }
  394.             
  395.             /* See if we have a res fork to send */
  396.             if (mbFileSpec->resForkLength)
  397.             {
  398.                 theErr = FSpOpenRF(&mbFileSpec->theFileSpec, fsRdPerm, &mbFileSpec->fileRefNum);
  399.                 if (theErr == noErr)
  400.                 {
  401.                     mbFileSpec->fileState = kMB_SendingResFork;
  402.                     /* Now that the res fork is open jump into the read state */
  403.                     goto SendingResFork;
  404.                 }
  405.                 else
  406.                 {    /* Couldn't open the res fork so exit the state machine */
  407.                     goto FinishedFile;
  408.                 }
  409.             }
  410.             else
  411.             {    /* Apparently no res fork so go ahead and exite the state machine */
  412.                 goto FinishedFile;
  413.             }
  414.  
  415.         case kMB_SendingResFork:
  416. SendingResFork:
  417.             theErr = FSRead(mbFileSpec->fileRefNum, &count, buf);
  418.             if (theErr == noErr || theErr == eofErr)
  419.             {
  420.                 bytesInBuf = PadBufferToMacBinBlock(buf, count);
  421.                 mbFileSpec->resBytesRead += count;
  422.                 
  423.                 /* See if we've reached EOF for the res fork */
  424.                 if (mbFileSpec->resBytesRead == mbFileSpec->resForkLength)
  425.                     mbFileSpec->fileState = kMB_FinishedFile;
  426.             }
  427.             else
  428.             {    /* Got some sort of error reading the res fork so exit the state machine */
  429.                 goto FinishedFile;
  430.             }
  431.             break;
  432.         
  433.         case kMB_FinishedFile:
  434. FinishedFile:
  435.             /* If we're here then we're done with the file */
  436.             mbFileSpec->fileState = kMB_FinishedFile;
  437.             
  438.             /* If we have a fork open, close it */
  439.             if (mbFileSpec->fileRefNum != -1)
  440.             {
  441.                 FSClose(mbFileSpec->fileRefNum);
  442.                 mbFileSpec->fileRefNum = -1;
  443.             }
  444.             break;
  445.     }
  446.     
  447.     return ((int32)bytesInBuf);
  448. }
  449.  
  450. /* MB_Close
  451.  
  452.     Nothing really to do since the file forks are actually closed in the MB_Read
  453.     routine after they have been sent.
  454. */
  455. void MB_Close(MB_FileSpec *mbFileSpec)
  456. {
  457.     if (mbFileSpec->fileRefNum != -1)
  458.         FSClose(mbFileSpec->fileRefNum);
  459.     
  460.     mbFileSpec->fileState = kMB_FinishedFile;
  461. }
  462.