http://www.kyz.uklinux.net/downloads/cabextract-0.6.tar.gz
[captive.git] / src / install / acquire / cabextract / cabinfo.c
diff --git a/src/install/acquire/cabextract/cabinfo.c b/src/install/acquire/cabextract/cabinfo.c
new file mode 100644 (file)
index 0000000..dd48cd7
--- /dev/null
@@ -0,0 +1,497 @@
+/* cabinfo -- dumps useful information from cabinets
+ * (C) 2000 Stuart Caie <kyzer@4u.net>
+ *
+ * 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* structure offsets */
+#define cfhead_Signature         (0x00)
+#define cfhead_CabinetSize       (0x08)
+#define cfhead_FileOffset        (0x10)
+#define cfhead_MinorVersion      (0x18)
+#define cfhead_MajorVersion      (0x19)
+#define cfhead_NumFolders        (0x1A)
+#define cfhead_NumFiles          (0x1C)
+#define cfhead_Flags             (0x1E)
+#define cfhead_SetID             (0x20)
+#define cfhead_CabinetIndex      (0x22)
+#define cfhead_SIZEOF            (0x24)
+#define cfheadext_HeaderReserved (0x00)
+#define cfheadext_FolderReserved (0x02)
+#define cfheadext_DataReserved   (0x03)
+#define cfheadext_SIZEOF         (0x04)
+#define cffold_DataOffset        (0x00)
+#define cffold_NumBlocks         (0x04)
+#define cffold_CompType          (0x06)
+#define cffold_SIZEOF            (0x08)
+#define cffile_UncompressedSize  (0x00)
+#define cffile_FolderOffset      (0x04)
+#define cffile_FolderIndex       (0x08)
+#define cffile_Date              (0x0A)
+#define cffile_Time              (0x0C)
+#define cffile_Attribs           (0x0E)
+#define cffile_SIZEOF            (0x10)
+#define cfdata_CheckSum          (0x00)
+#define cfdata_CompressedSize    (0x04)
+#define cfdata_UncompressedSize  (0x06)
+#define cfdata_SIZEOF            (0x08)
+
+/* flags */
+#define cffoldCOMPTYPE_MASK            (0x000f)
+#define cffoldCOMPTYPE_NONE            (0x0000)
+#define cffoldCOMPTYPE_MSZIP           (0x0001)
+#define cffoldCOMPTYPE_QUANTUM         (0x0002)
+#define cffoldCOMPTYPE_LZX             (0x0003)
+#define cfheadPREV_CABINET             (0x0001)
+#define cfheadNEXT_CABINET             (0x0002)
+#define cfheadRESERVE_PRESENT          (0x0004)
+#define cffileCONTINUED_FROM_PREV      (0xFFFD)
+#define cffileCONTINUED_TO_NEXT        (0xFFFE)
+#define cffileCONTINUED_PREV_AND_NEXT  (0xFFFF)
+#define cffile_A_RDONLY                (0x01)
+#define cffile_A_HIDDEN                (0x02)
+#define cffile_A_SYSTEM                (0x04)
+#define cffile_A_ARCH                  (0x20)
+#define cffile_A_EXEC                  (0x40)
+#define cffile_A_NAME_IS_UTF           (0x80)
+
+
+FILE *fh;
+char *filename;
+unsigned long filelength;
+void search();
+void getinfo();
+
+#define EndGetI32(a)  ((((a)[3])<<24)|(((a)[2])<<16)|(((a)[1])<<8)|((a)[0]))
+#define EndGetI16(a)  ((((a)[1])<<8)|((a)[0]))
+#define GETLONG(n) EndGetI32(&buf[n])
+#define GETWORD(n) EndGetI16(&buf[n])
+#define GETBYTE(n) ((int)buf[n])
+
+#define GETOFFSET      (ftell(fh))
+#define READ(buf,len)  if (myread((void *)(buf),(len))) return
+#define SKIP(offset)   if (myseek((offset),SEEK_CUR)) return
+#define SEEK(offset)   if (myseek((offset),SEEK_SET)) return
+
+
+
+int myread(void *buf, int length) {
+  int remain = filelength - GETOFFSET;
+  if (length > remain) length = remain;
+  if (fread(buf, 1, length, fh) != length) {
+    perror(filename);
+    return 1;
+  }
+  return 0;
+}
+
+int myseek(unsigned long offset, int mode) {
+  if (fseek(fh, offset, mode) != 0) {
+    perror(filename);
+    return 1;
+  }
+  return 0;
+}
+
+int main(int argc, char *argv[]) {
+  printf("Cabinet information dumper by Stuart Caie <kyzer@4u.net>\n");
+
+  if (argc <= 1) {
+    printf("Usage: %s <file.cab>\n", argv[0]);
+    return 1;
+  }
+
+  if (!(fh = fopen((filename = argv[1]), "rb"))) {
+    perror(filename);
+    return 1;
+  }
+
+  if (fseek(fh, 0, SEEK_END) != 0) {
+    perror(filename);
+    fclose(fh);
+    return 1;
+  }
+
+  filelength = (unsigned long) ftell(fh);
+
+  if (fseek(fh, 0, SEEK_SET) != 0) {
+    perror(filename);
+    fclose(fh);
+    return 1;
+  }
+
+  printf("Examining file \"%s\" (%u bytes)...\n", filename, filelength);
+  search();
+  fclose(fh);
+  return 0;
+}
+
+
+#define SEARCH_SIZE (32*1024)
+unsigned char search_buf[SEARCH_SIZE];
+
+void search() {
+  unsigned char *pstart = &search_buf[0], *pend, *p;
+  unsigned long offset, caboff, cablen, foffset;
+  size_t length;
+  int state = 0;
+
+  for (offset = 0; offset < filelength; offset += length) {
+    /* search length is either the full length of the search buffer,
+     * or the amount of data remaining to the end of the file,
+     * whichever is less.
+     */
+    length = filelength - offset;
+    if (length > SEARCH_SIZE) length = SEARCH_SIZE;
+
+    /* fill the search buffer with data from disk */
+    SEEK(offset);
+    READ(&search_buf[0], length);
+    /* read through the entire buffer. */
+    p = pstart;
+    pend = &search_buf[length];
+    while (p < pend) {
+      switch (state) {
+       /* starting state */
+      case 0:
+       /* we spend most of our time in this while loop, looking for
+        * a leading 'M' of the 'MSCF' signature
+        */
+       while (*p++ != 0x4D && p < pend);
+       if (p < pend) state = 1; /* if we found tht 'M', advance state */
+       break;
+       
+       /* verify that the next 3 bytes are 'S', 'C' and 'F' */
+      case 1: state = (*p++ == 0x53) ? 2 : 0; break;
+      case 2: state = (*p++ == 0x43) ? 3 : 0; break;
+      case 3: state = (*p++ == 0x46) ? 4 : 0; break;
+       
+       /* we don't care about bytes 4-7 */
+       /* bytes 8-11 are the overall length of the cabinet */
+      case 8:  cablen  = *p++;       state++; break;
+      case 9:  cablen |= *p++ << 8;  state++; break;
+      case 10: cablen |= *p++ << 16; state++; break;
+      case 11: cablen |= *p++ << 24; state++; break;
+       
+       /* we don't care about bytes 12-15 */
+       /* bytes 16-19 are the offset within the cabinet of the filedata */
+      case 16: foffset  = *p++;       state++; break;
+      case 17: foffset |= *p++ << 8;  state++; break;
+      case 18: foffset |= *p++ << 16; state++; break;
+      case 19: foffset |= *p++ << 24;
+       /* now we have recieved 20 bytes of potential cab header. */
+       /* work out the offset in the file of this potential cabinet */
+       caboff = offset + (p-pstart) - 20;
+       /* check that the files offset is less than the alleged length
+        * of the cabinet, and that the offset + the alleged length are
+        * 'roughly' within the end of overall file length
+        */
+       if ((foffset < cablen) &&
+           ((caboff + foffset) < (filelength + 32)) &&
+           ((caboff + cablen) < (filelength + 32)) )
+       {
+         /* found a potential result - try loading it */
+         printf("Found cabinet header at offset %u\n", caboff);
+         SEEK(caboff);
+         getinfo();
+         offset = caboff + cablen;
+         length = 0;
+         p = pend;
+       }
+       state = 0;
+       break;
+
+      default:
+       p++, state++; break;
+      } /* switch state */
+    } /* while p < pend */
+  } /* while offset < filelength */
+}
+
+
+
+
+
+#define CAB_NAMEMAX (1024)
+
+/* UTF translates two-byte unicode characters into 1, 2 or 3 bytes.
+ * %000000000xxxxxxx -> %0xxxxxxx
+ * %00000xxxxxyyyyyy -> %110xxxxx %10yyyyyy
+ * %xxxxyyyyyyzzzzzz -> %1110xxxx %10yyyyyy %10zzzzzz
+ *
+ * Therefore, the inverse is as follows:
+ * First char:
+ *  0x00 - 0x7F = one byte char
+ *  0x80 - 0xBF = invalid
+ *  0xC0 - 0xDF = 2 byte char (next char only 0x80-0xBF is valid)
+ *  0xE0 - 0xEF = 3 byte char (next 2 chars only 0x80-0xBF is valid)
+ *  0xF0 - 0xFF = invalid
+ */
+
+/* translate UTF -> ASCII */
+int convertUTF(unsigned char *in) {
+  unsigned char c, *out = in, *end = in + strlen(in) + 1;
+  unsigned int x;
+
+  do {
+    /* read unicode character */
+    if ((c = *in++) < 0x80) x = c;
+    else {
+      if (c < 0xC0) return 0;
+      else if (c < 0xE0) {
+        x = (c & 0x1F) << 6;
+        if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F);
+      }
+      else if (c < 0xF0) {
+        x = (c & 0xF) << 12;
+        if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F)<<6;
+        if ((c = *in++) < 0x80 || c > 0xBF) return 0; else x |= (c & 0x3F);
+      }
+      else return 0;
+    }
+
+    /* terrible unicode -> ASCII conversion */
+    if (x > 127) x = '_';
+
+    if (in > end) return 0; /* just in case */
+  } while ((*out++ = (unsigned char) x));
+  return 1;
+}
+
+
+
+void getinfo() {
+  unsigned char buf[64];
+  unsigned char namebuf[CAB_NAMEMAX];
+  char *name;
+
+  int num_folders, num_files, num_blocks = 0;
+  int header_res = 0, folder_res = 0, data_res = 0;
+  int i, x, offset, base_offset, files_offset, base;
+
+  base_offset = GETOFFSET;
+
+  READ(&buf, cfhead_SIZEOF);
+
+  x = GETWORD(cfhead_Flags);
+
+  printf(
+    "\n*** HEADER SECTION ***\n\n"
+    "Cabinet signature      = '%4.4s'\n"
+    "Cabinet size           = %u bytes\n"
+    "Offset of files        = %u\n"
+    "Cabinet format version = %d.%d\n"
+    "Number of folders      = %u\n"
+    "Number of files        = %u\n"
+    "Header flags           = 0x%04x%s%s%s\n"
+    "Set ID                 = %u\n"
+    "Cabinet set index      = %u\n",
+
+    buf,
+    GETLONG(cfhead_CabinetSize),
+    files_offset = (GETLONG(cfhead_FileOffset) + base_offset),
+    GETBYTE(cfhead_MajorVersion),
+    GETBYTE(cfhead_MinorVersion),
+    num_folders = GETWORD(cfhead_NumFolders),
+    num_files = GETWORD(cfhead_NumFiles),
+    x,
+    ((x & cfheadPREV_CABINET)    ? " PREV_CABINET"    : ""),
+    ((x & cfheadNEXT_CABINET)    ? " NEXT_CABINET"    : ""),
+    ((x & cfheadRESERVE_PRESENT) ? " RESERVE_PRESENT" : ""),
+    GETWORD(cfhead_SetID),
+    GETWORD(cfhead_CabinetIndex)
+  );
+
+  if (num_folders == 0) { printf("ERROR: no folders\n"); return; }
+  if (num_files == 0) { printf("ERROR: no files\n"); return; }
+
+  if (buf[0]!='M' || buf[1]!='S' || buf[2]!='C' || buf[3]!='F')
+    printf("WARNING: cabinet doesn't start with MSCF signature\n");
+
+  if (GETBYTE(cfhead_MajorVersion) > 1
+  || GETBYTE(cfhead_MinorVersion) > 3)
+    printf("WARNING: format version > 1.3\n");
+
+
+
+  if (x & cfheadRESERVE_PRESENT) {
+    READ(&buf, cfheadext_SIZEOF);
+    header_res = GETWORD(cfheadext_HeaderReserved);
+    folder_res = GETBYTE(cfheadext_FolderReserved);
+    data_res   = GETBYTE(cfheadext_DataReserved);
+  }
+
+  printf("Reserved header space  = %u\n", header_res);
+  printf("Reserved folder space  = %u\n", folder_res);
+  printf("Reserved datablk space = %u\n", data_res);
+
+  if (header_res > 60000)
+    printf("WARNING: header reserved space > 60000\n");
+
+  if (header_res) {
+    printf("[Reserved header: offset %lu, size %u]\n", GETOFFSET, header_res);
+    SKIP(header_res);
+  }
+
+  if (x & cfheadPREV_CABINET) {
+    base = GETOFFSET;
+    READ(&namebuf, CAB_NAMEMAX);
+    SEEK(base + strlen(namebuf) + 1);
+    printf("Previous cabinet file  = %s\n", namebuf);
+    if (strlen(namebuf) > 256) printf("WARNING: name length > 256\n");
+
+    base = GETOFFSET;
+    READ(&namebuf, CAB_NAMEMAX);
+    SEEK(base + strlen(namebuf) + 1);
+    printf("Previous disk name     = %s\n", namebuf);
+    if (strlen(namebuf) > 256) printf("WARNING: name length > 256\n");
+  }
+
+  if (x & cfheadNEXT_CABINET) {
+    base = GETOFFSET;
+    READ(&namebuf, CAB_NAMEMAX);
+    SEEK(base + strlen(namebuf) + 1);
+    printf("Next cabinet file      = %s\n", namebuf);
+    if (strlen(namebuf) > 256) printf("WARNING: name length > 256\n");
+
+    base = GETOFFSET;
+    READ(&namebuf, CAB_NAMEMAX);
+    SEEK(base + strlen(namebuf) + 1);
+    printf("Next disk name         = %s\n", namebuf);
+    if (strlen(namebuf) > 256) printf("WARNING: name length > 256\n");
+  }
+
+  printf("\n*** FOLDERS SECTION ***\n");
+
+  for (i = 0; i < num_folders; i++) {
+    offset = GETOFFSET;
+    READ(&buf, cffold_SIZEOF);
+
+    switch(GETWORD(cffold_CompType) & cffoldCOMPTYPE_MASK) {
+    case cffoldCOMPTYPE_NONE:    name = "stored";  break;
+    case cffoldCOMPTYPE_MSZIP:   name = "MSZIP";   break;
+    case cffoldCOMPTYPE_QUANTUM: name = "Quantum"; break;
+    case cffoldCOMPTYPE_LZX:     name = "LZX";     break;
+    default:                     name = "unknown"; break;
+    }
+
+    printf(
+      "\n[New folder at offset %u]\n"
+      "Offset of folder       = %u\n"
+      "Num. blocks in folder  = %u\n"
+      "Compression type       = 0x%04x [%s]\n",
+
+      offset,
+      base_offset + GETLONG(cffold_DataOffset),
+      GETWORD(cffold_NumBlocks),
+      GETWORD(cffold_CompType),
+      name
+    );
+
+    num_blocks += GETWORD(cffold_NumBlocks);
+
+    if (folder_res) {
+      printf("[Reserved folder: offset %lu, size %u]\n", GETOFFSET, folder_res);
+      SKIP(folder_res);
+    }
+  }
+
+  printf("\n*** FILES SECTION ***\n");
+
+  if (GETOFFSET != files_offset) {
+    printf("WARNING: weird file offset in header\n");
+    SEEK(files_offset);
+  }
+
+
+  for (i = 0; i < num_files; i++) {
+    offset = GETOFFSET;
+    READ(&buf, cffile_SIZEOF);
+
+    switch (GETWORD(cffile_FolderIndex)) {
+    case cffileCONTINUED_PREV_AND_NEXT:
+      name = "continued from previous and to next cabinet";
+      break;
+    case cffileCONTINUED_FROM_PREV:
+      name = "continued from previous cabinet";
+      break;
+    case cffileCONTINUED_TO_NEXT:
+      name = "continued to next cabinet";
+      break;
+    default:
+      name = "normal folder";
+      break;
+    }
+    
+    x = GETWORD(cffile_Attribs);
+    
+    base = GETOFFSET;
+    READ(&namebuf, CAB_NAMEMAX);
+    SEEK(base + strlen(namebuf) + 1);
+    if (strlen(namebuf) > 256) printf("WARNING: name length > 256\n");
+
+    /* convert filename */
+    if (x & cffile_A_NAME_IS_UTF) {
+      if (!convertUTF(namebuf)) printf("WARNING: invalid UTF filename");
+    }
+
+    printf(
+      "\n[New file at offset %u]\n"
+      "File name              = %s\n"
+      "File size              = %u bytes\n"
+      "Offset within folder   = %u\n"
+      "Folder index           = 0x%04x [%s]\n"
+      "Date / time            = %02d/%02d/%4d %02d:%02d:%02d\n"
+      "File attributes        = 0x%02x %s%s%s%s%s\n",
+      offset,
+      namebuf,
+      GETLONG(cffile_UncompressedSize),
+      GETLONG(cffile_FolderOffset),
+      GETWORD(cffile_FolderIndex),
+      name,
+      GETWORD(cffile_Date) & 0x1f,
+      (GETWORD(cffile_Date)>>5) & 0xf,
+      (GETWORD(cffile_Date)>>9) + 1980,
+      GETWORD(cffile_Time) >> 11,
+      (GETWORD(cffile_Time)>>5) & 0x3f,
+      (GETWORD(cffile_Time) << 1) & 0x3e,
+      x,
+      (x & cffile_A_RDONLY) ? "RDONLY " : "",
+      (x & cffile_A_HIDDEN) ? "HIDDEN " : "",
+      (x & cffile_A_SYSTEM) ? "SYSTEM " : "",
+      (x & cffile_A_ARCH)   ? "ARCH "   : "",
+      (x & cffile_A_EXEC)   ? "EXEC "   : ""
+    );
+  }
+
+  printf("\n*** DATABLOCKS SECTION ***\n");
+  printf("*** Note: offset is BLOCK offset. Add 8 for DATA offset! ***\n\n");
+
+  for (i = 0; i < num_blocks; i++) {
+    offset = GETOFFSET;
+    READ(&buf, cfdata_SIZEOF);
+    printf("Block %5d: offset %10d / csum %08x / c=%5d / u=%5d\n",
+      i, offset, GETLONG(cfdata_CheckSum),
+      x = GETWORD(cfdata_CompressedSize),
+      GETWORD(cfdata_UncompressedSize)
+    );
+    SKIP(x);
+  }
+
+}