home *** CD-ROM | disk | FTP | other *** search
/ Hall of Fame / HallofFameCDROM.cdr / arc / pakutl12.lzh / PAKSORT.C < prev    next >
Text File  |  1988-09-10  |  15KB  |  610 lines

  1. /*
  2.     PAKSORT.C    Sort contents of .PAK archive.
  3.  
  4.     Copyright 1988, Michael J. Housky
  5.     Released to the Public Domain ... *no* rights reserved.
  6. */
  7.  
  8. /*
  9.     Update log:
  10. ******************************************************************************
  11.     Version 1.2, 10 September 1988: Released to public domain.
  12.     Also relocated stderr fprintf's to stdout so error messages
  13.     can be captured with command-line redirection.
  14.     P.S. I only observe about 2% improvement with read/write over
  15.     fread/fwrite, but it *is* smaller. (mjh)
  16. ******************************************************************************
  17.     Version 1.1, 9 September 1988: used read, write, tell, lseek, open,
  18.     close instead of fread, fwrite, ftell, etc. This caused a 12% or so
  19.     speedup in the operation. jjn
  20. ******************************************************************************
  21.     Version 1.0, 25 August 1988: First version.
  22. ******************************************************************************
  23. */
  24.  
  25. #include <string.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <fcntl.h>
  29. #include <sys\types.h>
  30. #include <sys\stat.h>
  31. #include <io.h>
  32.  
  33.  
  34. #include "dirfn.h"
  35. #include "pakdefn.h"
  36.  
  37. #define BUF_SIZE    4096    /* buffer size for sort/copy step */
  38.  
  39. typedef struct            /* stored directory format: */
  40. {
  41.     long    floc;        /* byte location of header in file */
  42.     long    slen;        /* stored (packed) length of file */
  43.     long    olen;        /* original (unpacked) length of file */
  44.     unsigned    ddate;        /* DOS date of update */
  45.     unsigned    dtime;        /* DOS time of update */
  46.     char    fname[13];    /* filename */
  47. } pak_dir;
  48.  
  49. /* ------------    Global data: */
  50.  
  51.  
  52. FILE_TYPE    *file_list = NULL;    /* list of files (malloc'ed) */
  53. unsigned char    pak_buf[BUF_SIZE];    /* buffer for reading pakfile data */
  54. pak_hdr        fhdr;            /* buffer for reading pakfile header */
  55. long        pakloc;            /* location in input file */
  56. long        pakloc,paklen;        /* length of input file */
  57. char        order[33];        /* sort order from argument */
  58. char        fpath[128];        /* full path\name of file */
  59. char        *fnptr;            /* location of filename in fpath */
  60.  
  61. char        pak_mark[2] = {PAK_MARK,0};
  62.  
  63. /*
  64.     read_pakhdr:    Read header of .PAK member file
  65.  
  66. Reads next header sequentially from file.
  67.  
  68. Returns 0 if good header, and global fhdr has header.
  69. Returns +1 if end of file marker found.
  70. Returns -1 if bad header or read error.
  71.  
  72. */
  73.  
  74. int read_pakhdr( int );
  75.  
  76. int read_pakhdr( fp )
  77.     int        fp;
  78. {
  79.     int i;
  80.     register int j;
  81.  
  82.     i = 0;
  83.     j = read(fp, (char *)&i, 1);    /* get file marker */
  84.     if ( i != PAK_MARK || j == -1)    /* error if not correct */
  85.     return -1;
  86.  
  87.     j = read(fp, (char*)&fhdr, PAK_HDRLEN);
  88.                     /* read header, follows mark */
  89.  
  90.     if ( j>=1 && fhdr.type==0 )        /* type 0 header is an EOF mark */
  91.     return 1;            /* end of file mark seen */
  92.  
  93.     if ( j!=PAK_HDRLEN || fhdr.slen<0L || fhdr.olen<fhdr.slen )
  94.     return -1;            /* invalid header seen */
  95.  
  96.     pakloc = tell(fp);            /* note start of data and test */
  97.     if ( pakloc+fhdr.slen > paklen )    /*  for all data present */
  98.     return -1;            /* no: presume truncated file */
  99.     return 0;
  100.  
  101. } /* read_pakhdr */
  102.  
  103.  
  104. /*
  105.     pak_getdir:    Build directory of .PAK file headers.
  106.  
  107. Sequentially read and validate all headers within pakfile. Build malloc-ed
  108. table of headers found.
  109.  
  110. Returns:
  111.      0 = success, *pdirp = pointer to malloc-ed directory.
  112.      1 = out of memory;
  113.     -1 = read/seek error or bad header.
  114. */
  115.  
  116. int pak_getdir( int, pak_dir** );
  117.  
  118. int pak_getdir( fp, pdirp )
  119.     int        fp;
  120.     pak_dir    **pdirp;
  121. {
  122.     register    int i;
  123.     int        n,k;
  124.     long    l,hdrloc;
  125.     pak_dir    *pd;
  126.  
  127. /* Read the first header: */
  128.  
  129.     hdrloc = tell(fp);
  130.  
  131.     i = read_pakhdr(fp);
  132.     if ( i )
  133.     {
  134.     printf("Invalid header seen.\n");
  135.     return -1;
  136.     }
  137.  
  138.     pd = (pak_dir*) malloc( (k=32)*sizeof(pak_dir) );
  139.     if ( pd==NULL )
  140.     {
  141.     printf("Out of memory.\n");
  142.     return -1;            /* out of memory error */
  143.     }
  144.     n = 0;
  145.     
  146.     do            /* loop once for each new entry found, with */
  147.     {            /* n=#headers found, k=directory size */
  148.  
  149.     if ( n >= k )            /* test for current table full */
  150.     {
  151.         /* add 10 more entries to directory: */
  152.         pd = (pak_dir*) realloc( (char*)pd, (k+=16)*sizeof(pak_dir) );
  153.         if ( pd==NULL )
  154.         {
  155.         printf("Out of memory.\n");
  156.         return -1;        /* out of memory error */
  157.         }
  158.     }
  159.  
  160.     pd[n].floc  = hdrloc;        /* store header location */
  161.     pd[n].slen  = fhdr.slen;    /* copy stored length */
  162.     pd[n].olen  = fhdr.olen;    /* copy original length */
  163.     pd[n].ddate = fhdr.ddate;    /* copy DOS date */
  164.     pd[n].dtime = fhdr.dtime;    /* copy DOS time */
  165.     strcpy(pd[n].fname,fhdr.fname);    /* copy file name */
  166.     ++n;                /* and increment file count */
  167.  
  168.     hdrloc += fhdr.slen+PAK_HDRLEN+1;
  169.     l = lseek( fp, hdrloc, SEEK_SET );
  170.     if ( l == -1L )
  171.     {
  172.         printf("Seek error.\n");
  173.         free( (char*)pd );
  174.         return -1;
  175.     }
  176.  
  177.     i = read_pakhdr(fp);
  178.  
  179.     } while ( i==0 );
  180.  
  181.     if ( i<0 )                /* test for error */
  182.     {
  183.     printf("Invalid header seen.\n");
  184.     free( (char*)pd );
  185.     return -1;
  186.     }
  187.     *pdirp = pd;
  188.     return n;
  189.  
  190. } /* pak_getdir */
  191.  
  192.  
  193. /*
  194.     dir_cmp:    Directory entry compare function for sort.
  195.  
  196. Input:
  197.     lp:    pointer to "left" entry
  198.     rp:    pointer to "right" entry
  199.     order:    sort order
  200. Return:
  201.     0 if entries are equal
  202.     positive if left "greater than" right
  203.     negative if left "less than" right
  204. */
  205.  
  206. int dir_cmp( pak_dir*, pak_dir*, char* );
  207.  
  208. int dir_cmp( lp, rp, order )
  209.     pak_dir    *lp,*rp;
  210.     char    *order;
  211. {
  212.     register int i,j;
  213.     int        lc,rc;
  214.     char    *lcp,*rcp;
  215.  
  216.     for ( i=j=0; j==0; i+=2 )
  217.     {
  218.     switch( order[i] )
  219.     {
  220.         case 'F':        /* compare full fileid */
  221.         j = strcmp( lp->fname, rp->fname );
  222.         break;
  223.  
  224.         case 'N':        /* compare name only */
  225.         lc = *(lcp = lp->fname);
  226.         rc = *(rcp = rp->fname);
  227.             while ( (j=lc-rc)==0 && lc!=0 )
  228.         {
  229.             lc = *++lcp;
  230.             if ( lc=='.' )
  231.             lc = 0;
  232.             rc = *++rcp;
  233.             if ( rc=='.' )
  234.             rc = 0;
  235.         }
  236.         break;
  237.  
  238.         case 'X':        /* compare .ext only */
  239.         /* find start of left extension: */
  240.         for ( lc=*(lcp = lp->fname); lc; )
  241.         {
  242.             ++lcp;
  243.             lc = ( lc=='.' ) ? 0 : *lcp;
  244.         }
  245.         /* find start of right extension: */
  246.         for ( rc=*(rcp = rp->fname); rc; )
  247.         {
  248.             ++rcp;
  249.             rc = ( rc=='.' ) ? 0 : *rcp;
  250.         }
  251.         /* return regular string compare of remainder of names: */
  252.         j = strcmp( lcp, rcp );
  253.         break;
  254.  
  255.         case 'D':        /* compare unsigned date: */
  256.         j = (lp->ddate > rp->ddate) - (lp->ddate < rp->ddate);
  257.         break;
  258.  
  259.         case 'T':        /* compare unsigned time: */
  260.         j = (lp->dtime > rp->dtime) - (lp->dtime < rp->dtime);
  261.         break;
  262.  
  263.         case 'S':        /* compare (long) size: */
  264.         j = (lp->olen > rp->olen) - (lp->olen < rp->olen);
  265.         break;
  266.  
  267.         case '\0':
  268.         return 0;
  269.     }
  270.     if ( order[i+1] == '-' )
  271.         j = -j;
  272.     }
  273.     return j;
  274.  
  275. } /* dir_cmp */
  276.  
  277.  
  278. /*
  279.     pak_sort:    Sort one .PAK file.
  280.  
  281. A simple (slow, but small and sure) routine is used here to
  282. sort the directory without the overhead of qsort and recursion.
  283.  
  284. If this sort is replaced with an unstable sort (like quicksort) then
  285. the dir_cmp function should be changed to return compare of original
  286. file location if all fields in order[] compare equal. This will preserve
  287. sort stability ... i.e. input order is preserved on equal entries.
  288.  
  289. Return value is 0 if all files were originally in order,
  290.  or 1 if output order is different from input order.
  291.  
  292. */
  293.  
  294. int pak_sort( pak_dir*, int, char* );
  295.  
  296. int pak_sort( pdir, n, order )
  297.     pak_dir    *pdir;
  298.     int        n;
  299.     char    *order;
  300. {
  301.     register    int i,j;
  302.     int        i0,j0,k,r;
  303.     pak_dir    tdir;
  304.  
  305.     r = 0;
  306.     for ( j0=1; j0<n; ++j0 )
  307.     {
  308.     i = i0 = j0-1;            /* starting entry # to compare */
  309.         for ( j=j0; j<n; ++j )
  310.         {
  311.         k = dir_cmp( pdir+i, pdir+j, order );
  312.         if ( k>0 )
  313.         {                /* out of order */
  314.         i = j;            /* will swap at end */
  315.         }
  316.         }
  317.     if ( i != i0 )            /* if out of order */
  318.     {
  319.         tdir = pdir[i];        /* swap smallest entry */
  320.         pdir[i] = pdir[i0];        /* (at most n-1 times) */
  321.         pdir[i0] = tdir;
  322.         r = 1;            /* tell caller that input was */
  323.                         /* not in order */
  324.     }
  325.     }
  326.     return r;
  327.  
  328. } /* pak_sort */
  329.  
  330.  
  331. /*
  332.     pak_write:    Copy packed source files to dest in sorted order.
  333. */
  334.  
  335. int pak_write( int, int, pak_dir*, int );
  336.  
  337. int pak_write( src, dst, dirp, n )
  338.     int        src;        /* opened source file */
  339.     int        dst;        /* opened dest file */
  340.     pak_dir    *dirp;        /* sorted directory of source */
  341.     int        n;        /* number of entries in source directory */
  342. {
  343.     register int i,j;
  344.     int        k;
  345.     long    len,start,end;
  346.     long    l;
  347.  
  348.     for ( i=0; i<n; ++i )
  349.     {                /* once for each member file */
  350.  
  351.     printf("  Copying: %-13s - loc=%-6ld  len=%-6ld\n",
  352.         dirp[i].fname,
  353.         dirp[i].floc,
  354.         dirp[i].slen+1+PAK_HDRLEN );
  355.  
  356.     /* point to start of member, test for error: */
  357.     l = lseek(src, dirp[i].floc, SEEK_SET);
  358.     if ( l == -1L )
  359.     {
  360.         printf("Seek error.\n");
  361.         return -1;
  362.     }
  363.  
  364.     /* compute length and copy member to destination: */
  365.     len = dirp[i].slen+1+PAK_HDRLEN;
  366.     for ( end=0; end<len; )
  367.     {
  368.          start = end;
  369.         end = start+(k=BUF_SIZE);
  370.         if (end>len)
  371.         k = (int)((end=len)-start);
  372.         j = read(src,pak_buf,k);
  373.         if ( j != k )
  374.         {
  375.         printf("Read error.\n");
  376.         return -1;
  377.         }
  378.         j = write(dst,pak_buf,k);
  379.         if ( j != k )
  380.         {
  381.         printf("Write error.\n");
  382.         return -1;
  383.         }
  384.     }
  385.     }
  386.  
  387.     /* Add EOF mark to (end of) destination: */
  388.     j = write(dst, pak_mark, sizeof(pak_mark));
  389.     if ( j != sizeof(pak_mark) )
  390.     {
  391.     printf("Write error.\n");
  392.     return -1;
  393.     }
  394.  
  395.     return 0;
  396.  
  397. } /* pak_write */
  398.  
  399.  
  400.  
  401. /*
  402.     pak_dosort:    Read/sort/rewrite one .PAK file.
  403.  
  404. Returns 0 if successful, or errorlevel for DOS on error:
  405.    1 = Can't open/read source. (missing or invalid pakfile)
  406.    2 = Can't create/write dest (maybe disk or directory full)
  407.    3 = Can't delete old source file. (read-only)
  408.    4 = Can't rename temp file to source name. (???)
  409. */
  410.  
  411. int pak_dosort( char* );
  412.  
  413. int pak_dosort( fn )
  414.     char    *fn;
  415. {
  416.     register int i,j;
  417.     int        n;
  418.     pak_dir    *pdir;        /* pointer to (malloced) directory */
  419.                 /*  built by pak_getdir */
  420.     int        src;        /* source file */
  421.     int        dst;        /* dest file */
  422.     char    srcname[81];    /* save source filename */
  423.  
  424.     strcpy( fnptr, fn );        /* make full path to file */
  425.     printf("\nSearching:  %s\n",fpath);    /* and identify */
  426.     src = open( fpath, O_RDONLY | O_BINARY );    /* and open file */
  427.     if ( src == -1 )
  428.     {
  429.     printf("Can't open.\n", fpath);
  430.     return 1;
  431.     }
  432.     paklen = filelength(src);        /* get file length in global */
  433.  
  434.     pdir = NULL;            /* clear directory pointer */
  435.     n = pak_getdir( src, &pdir );    /* get directory */
  436.     if ( n<1 )
  437.     {
  438.     printf("Invalid file format, not changed.\n");
  439.     close(src);
  440.     return 1;
  441.     }
  442.  
  443.     i = pak_sort( pdir, n, order );
  444.     if ( i==0 )
  445.     {
  446.     printf("Already in order, not changed.\n");
  447.     free( (char*)pdir );
  448.     return 0;
  449.     }
  450.  
  451. /* pakfile is not in order, create sorted copy & rename: */
  452.  
  453.     strcpy( srcname, fpath );
  454.     strcpy( fnptr, "PAKSRTMP.$$$" );
  455.     dst = open( fpath,
  456.         O_CREAT | O_TRUNC | O_WRONLY | O_BINARY,
  457.         S_IREAD | S_IWRITE);
  458.     if ( dst==-1 )
  459.     {
  460.     printf("Can't create: %s\n", fpath );
  461.     free( (char*)pdir );
  462.     return 1;
  463.     }
  464.  
  465.     j = pak_write( src, dst, pdir, n );    /* copy source to dest tempfile */
  466.     close(src);                /* unconditionally close source */
  467.     free( (char*)pdir );        /*  and release directory mem */
  468.     i = close(dst);            /* close dest file */
  469.  
  470.     if ( j || (i == -1) )        /* if error on write or close */
  471.     {
  472.         if ( !j )            /* identify close error */
  473.         printf("Can't close: %s\n",fpath);
  474.         unlink(fpath);
  475.         return 2;
  476.     }
  477.  
  478.     j = unlink(srcname);        /* delete source */
  479.     if ( j )
  480.     {                    /* can't delete, read-only? */
  481.     printf( "Can't delete original.\n");
  482.     unlink(fpath);            /* delete sorted temp copy */
  483.     return 3;            /* return error */
  484.     }
  485.     j = rename( fpath, srcname );
  486.     if ( j )
  487.     {
  488.     printf("Rename error: %d, original unsorted data lost!\n");
  489.     printf("Sorted data retained in temp output file for recovery:\n");
  490.     printf("%s\n",fpath);
  491.     return 4;
  492.     }
  493.  
  494.     return 0;
  495.  
  496. } /* pak_dosort */
  497.  
  498.  
  499. /*
  500.     PKASORT.C main program
  501. */
  502.  
  503.  
  504. int main(int,char **);
  505. int main( argc, argv )
  506.     int        argc;
  507.     char    **argv;
  508. {
  509.     register int
  510.         i,j;
  511.     int        k;
  512.     long    len;
  513.     int        addext;
  514.     int        num_files;
  515.  
  516. /* First, identify self and examine paramter: */
  517.  
  518.     printf("\nPAKSORT v1.2\n");
  519.     if ( argc<2 )
  520.     {
  521.     printf("Usage: paksort <fname> [<order>]\n");
  522.     printf("<order> is one or more of: \n");
  523.     printf("  F+ or F-: sort by full filename.ext,\n");
  524.     printf("  N+ or N-: sort by filename,\n");
  525.     printf("  X+ or X-: sort by .ext,\n");
  526.     printf("  D+ or D-: sort by date,\n");
  527.     printf("  T+ or T-: sort by time,\n");
  528.     printf("  S+ or S-: sort by (unpacked) file size,\n");
  529.     printf("  [Note: + gives ascending order, - gives descending order.]\n");
  530.     printf("The default order is D+T+F+\n");
  531.  
  532.     return 0;
  533.     }
  534.  
  535. /* Parameter valid, find path and decide about default extension */
  536.  
  537.     strcpy( order, "D+T+F+" );        /* set default order */
  538.     if ( argc > 2 )
  539.     {
  540.     k = strlen(argv[2]);
  541.     if ( (k&1) || (k>=sizeof(order)) )
  542.     {
  543.         printf( "Invalid sort order\n" );
  544.         return 1;
  545.     }
  546.     strcpy( order, argv[2] );
  547.     strupr( order );
  548.     for ( i=0; i<k; i+=2 )
  549.     {
  550.         switch( order[i] )
  551.         {
  552.         default:
  553.             printf("Invalid sort order\n");
  554.             return 1;
  555.         case 'N': case 'F': case 'X':
  556.         case 'D': case 'T': case 'S':
  557.             /* break */;
  558.         }
  559.         switch( order[i+1] )
  560.         {
  561.         default:
  562.             printf("Invalid sort order\n");
  563.             return 1;
  564.         case '+': case '-':
  565.             /* break */;
  566.         }
  567.  
  568.     }
  569.     }
  570.     strcpy( fpath, argv[1] );        /* extract path, find end */
  571.     i = strlen(fpath);            /* index of last character */
  572.     addext = 1;                /* presume no extension */
  573.     while ( --i >= 0 )            /* search backward thru argument */
  574.     {
  575.     j = fpath[i];
  576.     if ( j=='\\' || j==':' )
  577.         break;            /* colon or backslash ends search */
  578.     if ( j=='.' )
  579.         addext = 0;            /* period found, no default */
  580.     }
  581.     fnptr = fpath+i+1;
  582.     strupr(fpath);            /* uppercase for display */
  583.     if ( addext )
  584.     strcat(fpath,".PAK");        /* add default extension */
  585.  
  586.     num_files = get_files(fpath,0,&file_list);
  587.     if (num_files < 0)
  588.     {
  589.     printf("Too many files: out of memory.\n");
  590.     return(1);
  591.     }
  592.  
  593.     if (num_files < 1)
  594.     {
  595.     printf("Cannot find file(s): %s\n",argv[1]);
  596.     return(1);
  597.     }
  598.  
  599. /* Now, list contents of each file: */
  600.  
  601.     for (i = 0; i < num_files; ++i)
  602.     {
  603.     j = pak_dosort( file_list[i].filename ); 
  604.     if ( j )
  605.         return j;
  606.     }
  607.     return 0;
  608.  
  609. } /* main */
  610.