home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 3 Comm / 03-Comm.zip / TE2DS121.ZIP / TE2DSORT.C next >
C/C++ Source or Header  |  1992-07-26  |  26KB  |  770 lines

  1. // --------------------------------------------------------------------------
  2. //
  3. //  Te2DSort.C -- TE/2 Dialing Directory Sort Program
  4. //
  5. //    CopyRight 1990-1992 by Oberon Software, Mankato, MN
  6. //    All Rights Reserved
  7. //
  8. // --------------------------------------------------------------------------
  9.  
  10. #include <stdio.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include <conio.h>
  14.  
  15. #define INCL_DOS
  16. #include <os2.h>
  17.  
  18. // --------------------------------------------------------------------------
  19. // ---- Constants
  20.  
  21.   // ---- Parity designation codes for directory entries
  22. #define NO_PARITY         0       // No Parity
  23. #define ODD_PARITY        1       // Odd Parity
  24. #define EVEN_PARITY       2       // Even Parity
  25. #define MARK_PARITY       3       // Mark Parity
  26. #define SPACE_PARITY      4       // Space Parity
  27.  
  28.   // ---- StopBit designation codes for directory entries
  29. #define STOPBITS_1        0       // 1 Stopbit
  30. #define STOPBITS_15       1       // 1.5 Stopbits
  31. #define STOPBITS_2        2       // 2 Stopbits
  32.  
  33.   // ---- Protocol designation codes for directory entries
  34. #define PROTOCOL_ASCII    0       // Ascii protocol (upload only)
  35. #define PROTOCOL_XMODEM   1       // XModem (CRC)
  36. #define PROTOCOL_XMODEM1K 2       // XModem-1K
  37. #define PROTOCOL_YMODEM   3       // YModem
  38. #define PROTOCOL_ZMODEM   4       // ZModem
  39. #define PROTOCOL_YMODEMG  5       // YModem-G (not used before 1.00c)
  40. #define PROTOCOL_CISQB    6       // CIS BPlus (for future use)
  41.  
  42.   // ---- Emulation designation codes for directory entries
  43. #define EMULATE_TTY       0       // Teletype emulation
  44. #define EMULATE_ANSI      1       // ANSI-BBS
  45. #define EMULATE_XANSI     2       // ANSI-TE/2
  46. #define EMULATE_VT100     3       // VT100
  47.  
  48.   // ---- Other important constants
  49. #define ORIG_MAXDENTRY    50      // # of entries in pre-1.00c files
  50. #define MAXDENTRY         200     // # of entries in files since 1.00c
  51. #define TAGLEN            5       // Length of TAG field
  52. #define NAMELEN           25      // Length of NAME field
  53. #define NUMLEN            29      // Length of NUMBER field
  54. #define SCRIPTLEN         63      // Length of SCRIPT field
  55. #define ACCCODES          10      // Number of access code entries/file
  56. #define ACCCLEN           NUMLEN  // Length of access code entry
  57. #define RDF_BUFSIZ        80      // Length of a utility buffer
  58.  
  59.  
  60. // --------------------------------------------------------------------------
  61. // ---- Typedefs
  62.  
  63.   // ---- Directory entry structure
  64. typedef struct _DDENTRY
  65. {
  66.   char     tag[TAGLEN+1];       // May be any asciiz data
  67.   char     name[NAMELEN+1];     // May be any asciiz data
  68.   char     number[NUMLEN+1];    // May be any asciiz data
  69.   USHORT   baud;                // 110, 150, 300, 600, 1200, 2400, 4800, 9600,
  70.                                 //   19200, 38400, or 57600
  71.   USHORT   parity;              // NO_PARITY, ODD_PARITY, EVEN_PARITY,
  72.                                 //   MARK_PARITY, or SPACE_PARITY
  73.   USHORT   wordlen;             // 7 or 8
  74.   USHORT   stopbits;            // STOPBITS_1, STOPBITS_15, or STOPBITS_2
  75.   USHORT   duplex;              // TRUE == Half Duplex, FALSE == Full Duplex
  76.   USHORT   protocol;            // PROTOCOL_ASCII, PROTOCOL_XMODEM,
  77.                                 //   PROTOCOL_XMODEM1K, PROTOCOL_YMODEM,
  78.                                 //   PROTOCOL_ZMODEM, PROTOCOL_YMODEMG, or
  79.                                 //   PROTOCOL_CISQB
  80.   USHORT   emulate;             // EMULATE_TTY, EMULATE_ANSI, EMULATE_XANSI,
  81.                                 //   or EMULATE_VT100
  82.   char     script[SCRIPTLEN+1]; // May be any asciiz data
  83.   USHORT   connected;           // Count of times connected
  84.   DATETIME lastconnect;         // Date/Time of last connect
  85.   char     password[NAMELEN+1]; // May be any asciiz data
  86. } DDENTRY;
  87.  
  88.  
  89. // --------------------------------------------------------------------------
  90. // ---- Local data
  91.  
  92.   // ---- Array of pointers to directory entries, will be allocated and
  93.   //      filled by ReadDirectoryFile()
  94. static DDENTRY *directory[MAXDENTRY];
  95.  
  96.   // ---- Array of string buffers to hold access codes associated with
  97.   //      a dialing directory file.
  98. static char accCodes[ACCCODES][ACCCLEN+1];
  99.  
  100.   // ---- Signiture strings found in the header record of a directory
  101.   //      file from various versions of TE/2
  102. static char *dirFileSig_v0    = "TE/2 Directory v1.0";
  103. static char *dirFileSig_v100F = "TE/2 Directory v1.00α1";
  104. static char *dirFileSig_v110D = "TE/2 Directory v1.00α2";
  105. static char *dirFileSig       = "TE/2 Directory v1.10.E";
  106.  
  107.   // ---- An empty DATETIME structure used for default value of "lastconnect"
  108.   //      in older directory files
  109. static DATETIME nulDate   = { '\0', '\0', '\0', '\0', '\0', '\0', 0, 0, '\0' };
  110.  
  111.   // ---- Valid sort options
  112. char options[]  = "0TNPD#";
  113.  
  114. // --------------------------------------------------------------------------
  115.  
  116.   // ---- Function prototypes
  117.  
  118.     // ---- Stuff directly from TE2DDIR.C
  119. static int freadStrg(char *buf, FILE *fp);  // Read asciiz from file
  120. static int fwriteStrg(char *buf, FILE *fp); // Write asciiz to file
  121.  
  122. static int ReadV0Directory(FILE *fp);       // Read a very old dir file
  123. static int ReadV100FDirectory(FILE *fp);    // Read an old dir file
  124. static int ReadCurrentDirectory(FILE *fp, BOOL f);  // Read a current dir file
  125.  
  126. int  ReadDirectoryFile(char *fname);        // Read any vsn dir file
  127. int  WriteDirectoryFile(char *fname);       // Write a current vsn dir file
  128.  
  129. void EmptyDirectory(void);                  // free all allocated memory
  130.  
  131.     // ---- Added functions for this TE2DSORT.C
  132. void Sort(int opt, BOOL fInvert);
  133. int  main(int argc, char **argv);
  134.  
  135.  
  136. // --------------------------------------------------------------------------
  137. // ---- Functions
  138.  
  139.  
  140.   // -----------------------------------------------------------------------
  141.   // Read an ASCIIZ string from a file, expects 'buf' to be large enough
  142.   // to hold any conceivable number of characters (i.e., there is no
  143.   // error checking).
  144.  
  145. static int freadStrg(char *buf, FILE *fp)
  146. {
  147.   int  i = 0, err = FALSE;
  148.   char c;
  149.  
  150.   do
  151.   {
  152.     if (fread(&c, sizeof(char), 1, fp) != 1)
  153.     {
  154.       err = TRUE;
  155.       break;
  156.     }
  157.     buf[i++] = c;
  158.   }
  159.   while (c);
  160.  
  161.   return err;
  162. }
  163.  
  164.  
  165.  
  166.   // -----------------------------------------------------------------------
  167.   // Write an ASCIIZ string to a file
  168.  
  169. static int fwriteStrg(char *buf, FILE *fp)
  170. {
  171.   int  i = 0, err = FALSE;
  172.   char c;
  173.  
  174.   do
  175.   {
  176.     c = buf[i++];
  177.     if (fwrite(&c, sizeof(char), 1, fp) != 1)
  178.     {
  179.       err = TRUE;
  180.       break;
  181.     }
  182.   }
  183.   while (c);
  184.  
  185.   return err;
  186. }
  187.  
  188.  
  189. // --------------------------------------------------------------------------
  190.  
  191.  
  192.   // -----------------------------------------------------------------------
  193.   // General Notes about the Read*Directory() functions:
  194.   //
  195.   //   The "directory" array is an array of pointers to directory entry
  196.   //   structures, it contains all NULL pointers by default.  Each
  197.   //   function will allocate entries as needed as it encounters valid
  198.   //   entries in the directory file.  Empty entries in the file consist
  199.   //   of a single, zero byte.  When an empty entry is encountered, it's
  200.   //   pointer in the array is left NULL.
  201.   //
  202.   //   These functions may be called more than once to reread the same
  203.   //   or other directory files.  Space for entries will be reused,
  204.   //   allocated, and/or freed as required.
  205.   //
  206.   //   Each function returns TRUE if the file was read with no errors
  207.   //   detected, FALSE if any error occured.  If FALSE is returned, the
  208.   //   directory may be partially read and available for examination.
  209.   //
  210.   //   After the caller is done with the "directory" array, the
  211.   //   EmptyDirectory() function should be called to make sure that all
  212.   //   allocated memory has been freed.
  213.   // -----------------------------------------------------------------------
  214.  
  215.  
  216.   // -----------------------------------------------------------------------
  217.   // Read a "version 0" directory file into memory.  This is the file format
  218.   // created by TE/2 Version 1.00b.  Expects the current position in the
  219.   // file to be pointing past the header information at the first real entry.
  220.  
  221. static int ReadV0Directory(FILE *fp)
  222. {
  223.   int  i, j, err;
  224.   char buf = 0;
  225.  
  226.   for (i = 0; i < ORIG_MAXDENTRY; i++)
  227.   {
  228.     if (fread(&buf, sizeof(char), 1, fp) == 1)
  229.     {
  230.       if (buf)  // If there is an entry at this position
  231.       {
  232.         // Either create an entry structure if this is a first read or
  233.         //   clean up an existing one for reuse.
  234.         if (!directory[i])
  235.           directory[i] = calloc(1, sizeof(DDENTRY));
  236.         else
  237.           memset(directory[i], 0, sizeof(DDENTRY));
  238.  
  239.         // Read the data that's there
  240.         err  =  freadStrg(directory[i]->tag, fp);
  241.         err |=  freadStrg(directory[i]->name, fp);
  242.         err |=  freadStrg(directory[i]->number, fp);
  243.         err |= (fread(&(directory[i]->baud),     sizeof(USHORT), 1, fp) != 1);
  244.         err |= (fread(&(directory[i]->parity),   sizeof(USHORT), 1, fp) != 1);
  245.         err |= (fread(&(directory[i]->wordlen),  sizeof(USHORT), 1, fp) != 1);
  246.         err |= (fread(&(directory[i]->stopbits), sizeof(USHORT), 1, fp) != 1);
  247.         err |= (fread(&(directory[i]->duplex),   sizeof(USHORT), 1, fp) != 1);
  248.         err |= (fread(&(directory[i]->protocol), sizeof(USHORT), 1, fp) != 1);
  249.         err |=  freadStrg(directory[i]->script, fp);
  250.         if (err) break;
  251.  
  252.         // Insert default values, this version of the file didn't contain
  253.         //   these fields.
  254.         directory[i]->emulate = EMULATE_XANSI;
  255.         directory[i]->connected = 0;
  256.         directory[i]->lastconnect = nulDate;
  257.       }
  258.       else if (directory[i])
  259.       {
  260.         // No entry at this position in this file.  If there was already
  261.         // space allocated, free it.
  262.         free(directory[i]);
  263.         directory[i] = NULL;
  264.       }
  265.     }
  266.     else
  267.       break;
  268.   }
  269.  
  270.   if (!err)
  271.   {
  272.     // This version of the file did not contain as many records as later
  273.     // ones.  Make up the difference here with NULL entries.
  274.     for (j = i; j < MAXDENTRY; j++)
  275.       if (directory[j])
  276.       {
  277.         free(directory[j]);
  278.         directory[j] = NULL;
  279.       }
  280.  
  281.     // This version of the file didn't contain any Access Codes.  Fill in
  282.     // with NULL entries.
  283.     for (j = 0; j < ACCCODES; j++)
  284.       memset(accCodes[j], 0, ACCCLEN+1);
  285.   }
  286.  
  287.   // Return TRUE on a successful read, FALSE if an error occured
  288.   return (i == ORIG_MAXDENTRY);
  289. }
  290.  
  291.  
  292.  
  293.  
  294.   // -----------------------------------------------------------------------
  295.   // Read a "version 100F" directory file into memory.  This is the file
  296.   // format created by TE/2 starting with Version 1.00c and continued
  297.   // through version 1.00f. Expects the current position in the file to
  298.   // be pointing past the header information at the first real entry.
  299.  
  300. static int ReadV100FDirectory(FILE *fp)
  301. {
  302.   int  i, j, err;
  303.   char buf = 0;
  304.  
  305.   for (i = 0; i < MAXDENTRY; i++)
  306.   {
  307.     if (fread(&buf, sizeof(char), 1, fp) == 1)
  308.     {
  309.       if (buf)
  310.       {
  311.         // Either create an entry structure if this is a first read or
  312.         //   clean up an existing one for reuse.
  313.         if (!directory[i])
  314.           directory[i] = calloc(1, sizeof(DDENTRY));
  315.         else
  316.           memset(directory[i], 0, sizeof(DDENTRY));
  317.  
  318.         // Read the data that's there
  319.         err  =  freadStrg(directory[i]->tag, fp);
  320.         err |=  freadStrg(directory[i]->name, fp);
  321.         err |=  freadStrg(directory[i]->number, fp);
  322.         err |= (fread(&(directory[i]->baud),     sizeof(USHORT), 1, fp) != 1);
  323.         err |= (fread(&(directory[i]->parity),   sizeof(USHORT), 1, fp) != 1);
  324.         err |= (fread(&(directory[i]->wordlen),  sizeof(USHORT), 1, fp) != 1);
  325.         err |= (fread(&(directory[i]->stopbits), sizeof(USHORT), 1, fp) != 1);
  326.         err |= (fread(&(directory[i]->duplex),   sizeof(USHORT), 1, fp) != 1);
  327.         err |= (fread(&(directory[i]->protocol), sizeof(USHORT), 1, fp) != 1);
  328.         err |= (fread(&(directory[i]->emulate),  sizeof(USHORT), 1, fp) != 1);
  329.         err |=  freadStrg(directory[i]->script, fp);
  330.         if (err) break;
  331.  
  332.         // Insert default values, this version of the file didn't contain
  333.         //   these fields.
  334.         directory[i]->connected = 0;
  335.         directory[i]->lastconnect = nulDate;
  336.       }
  337.       else if (directory[i])
  338.       {
  339.         // No entry at this position in this file.  If there was already
  340.         // space allocated, free it.
  341.         free(directory[i]);
  342.         directory[i] = NULL;
  343.       }
  344.     }
  345.     else
  346.       break;
  347.   }
  348.  
  349.   if (!err)
  350.   {
  351.     // This version of the file didn't contain any Access Codes.  Fill in
  352.     // with NULL entries.
  353.     for (j = 0; j < ACCCODES; j++)
  354.       memset(accCodes[j], 0, ACCCLEN+1);
  355.   }
  356.  
  357.   // Return TRUE on a successful read, FALSE if an error occured
  358.   return (i == MAXDENTRY);
  359. }
  360.  
  361.  
  362.  
  363.  
  364.   // -----------------------------------------------------------------------
  365.   // Read a "current version" directory file into memory.  This is the
  366.   // file format created by TE/2 starting with Version 1.10a. Expects
  367.   // the current position in the file to be pointing past the header
  368.   // information at the first real entry.
  369.   // -----------------------------------------------------------------------
  370.   // Note regarding the f110EFlag: The only change in the 1.10e file format
  371.   // from the previous version is the including of the password field at the
  372.   // end of the record.  We can use the same routine to ready both
  373.   // versions.
  374.  
  375. static int ReadCurrentDirectory(FILE *fp, BOOL f110EFlag)
  376. {
  377.   int  i, j, err;
  378.   char buf = 0;
  379.  
  380.   for (i = 0; i < MAXDENTRY; i++)
  381.   {
  382.     if (fread(&buf, sizeof(char), 1, fp) == 1)
  383.     {
  384.       if (buf)
  385.       {
  386.         // Either create an entry structure if this is a first read or
  387.         //   clean up an existing one for reuse.
  388.         if (!directory[i])
  389.           directory[i] = calloc(1, sizeof(DDENTRY));
  390.         else
  391.           memset(directory[i], 0, sizeof(DDENTRY));
  392.  
  393.         // Read the data
  394.         err  =  freadStrg(directory[i]->tag, fp);
  395.         err |=  freadStrg(directory[i]->name, fp);
  396.         err |=  freadStrg(directory[i]->number, fp);
  397.         err |= (fread(&(directory[i]->baud),        sizeof(USHORT),   1, fp) != 1);
  398.         err |= (fread(&(directory[i]->parity),      sizeof(USHORT),   1, fp) != 1);
  399.         err |= (fread(&(directory[i]->wordlen),     sizeof(USHORT),   1, fp) != 1);
  400.         err |= (fread(&(directory[i]->stopbits),    sizeof(USHORT),   1, fp) != 1);
  401.         err |= (fread(&(directory[i]->duplex),      sizeof(USHORT),   1, fp) != 1);
  402.         err |= (fread(&(directory[i]->protocol),    sizeof(USHORT),   1, fp) != 1);
  403.         err |= (fread(&(directory[i]->emulate),     sizeof(USHORT),   1, fp) != 1);
  404.         err |=  freadStrg(directory[i]->script, fp);
  405.         err |= (fread(&(directory[i]->connected),   sizeof(USHORT),   1, fp) != 1);
  406.         err |= (fread(&(directory[i]->lastconnect), sizeof(DATETIME), 1, fp) != 1);
  407.         if (f110EFlag) err |=  freadStrg(directory[i]->password, fp);
  408.         if (err) break;
  409.       }
  410.       else if (directory[i])
  411.       {
  412.         // No entry at this position in this file.  If there was already
  413.         // space allocated, free it.
  414.         free(directory[i]);
  415.         directory[i] = NULL;
  416.       }
  417.     }
  418.     else
  419.       break;
  420.   }
  421.  
  422.   if (!err && !feof(fp))
  423.   {
  424.     // Read the Access Codes associated with this directory
  425.     for (j = 0; j < ACCCODES && !err; j++)
  426.       err |= freadStrg(accCodes[j], fp);
  427.   }
  428.  
  429.   // Return TRUE on a successful read, FALSE if an error occured
  430.   return ((i == MAXDENTRY) && !err);
  431. }
  432.  
  433.  
  434.  
  435.  
  436.   // -----------------------------------------------------------------------
  437.   // Read any format directory file into memory.
  438.  
  439. int ReadDirectoryFile(char *fname)
  440. {
  441.   FILE *fp;
  442.   int  i, rVal = FALSE;
  443.   char buf[RDF_BUFSIZ];
  444.  
  445.   if (fp = fopen(fname, "rb"))
  446.   {
  447.     // "dirFileSig", the signiture on a current version directory file is
  448.     // the longest signiture in any version.  We'll read enough of the
  449.     // header to get the whole string; we may need to seek back in the
  450.     // file if it's an older version.
  451.  
  452.     i = strlen(dirFileSig);
  453.     if ((int)fread(buf, sizeof(char), i, fp) == i)
  454.     {
  455.       if (strncmp(buf, dirFileSig, i) == 0)
  456.       {
  457.         // Current style directory, Version 1.10.e on
  458.         rVal = ReadCurrentDirectory(fp, TRUE);
  459.       }
  460.       else if (strncmp(buf, dirFileSig_v110D, strlen(dirFileSig_v110D)) == 0)
  461.       {
  462.         // Version 1.10.a through 1.10.d style directory
  463.         rVal = ReadCurrentDirectory(fp, FALSE);
  464.       }
  465.       else if (strncmp(buf, dirFileSig_v100F, strlen(dirFileSig_v100F)) == 0)
  466.       {
  467.         // Version 1.00.c through 1.00.f style directory
  468.         fseek(fp, (long)(strlen(dirFileSig_v100F)), SEEK_SET);
  469.         rVal = ReadV100FDirectory(fp);
  470.       }
  471.       else if (strncmp(buf, dirFileSig_v0, strlen(dirFileSig_v0)) == 0)
  472.       {
  473.         // Earliest style, first alpha
  474.         fseek(fp, (long)(strlen(dirFileSig_v0)), SEEK_SET);
  475.         rVal = ReadV0Directory(fp);
  476.       }
  477.     }
  478.     fclose(fp);
  479.   }
  480.  
  481.   return rVal;
  482. }
  483.  
  484.  
  485.  
  486.   // -----------------------------------------------------------------------
  487.   // Write a directory file of the current format.
  488.  
  489. int WriteDirectoryFile(char *fname)
  490. {
  491.   FILE *fp;
  492.   int  i, j, err, rVal = FALSE;
  493.   char c;
  494.  
  495.   if (fp = fopen(fname, "wb"))
  496.   {
  497.     i = strlen(dirFileSig);
  498.     if ((int)fwrite(dirFileSig, sizeof(char), i, fp) == i)
  499.     {
  500.       for (i = 0; i < MAXDENTRY; i++)
  501.       {
  502.         if (directory[i])
  503.         {
  504.           c = 1;
  505.           if (fwrite(&c, sizeof(char), 1, fp) == 1)
  506.           {
  507.             err  =  fwriteStrg(directory[i]->tag, fp);
  508.             err |=  fwriteStrg(directory[i]->name, fp);
  509.             err |=  fwriteStrg(directory[i]->number, fp);
  510.             err |= (fwrite(&(directory[i]->baud),        sizeof(USHORT),   1, fp) != 1);
  511.             err |= (fwrite(&(directory[i]->parity),      sizeof(USHORT),   1, fp) != 1);
  512.             err |= (fwrite(&(directory[i]->wordlen),     sizeof(USHORT),   1, fp) != 1);
  513.             err |= (fwrite(&(directory[i]->stopbits),    sizeof(USHORT),   1, fp) != 1);
  514.             err |= (fwrite(&(directory[i]->duplex),      sizeof(USHORT),   1, fp) != 1);
  515.             err |= (fwrite(&(directory[i]->protocol),    sizeof(USHORT),   1, fp) != 1);
  516.             err |= (fwrite(&(directory[i]->emulate),     sizeof(USHORT),   1, fp) != 1);
  517.             err |=  fwriteStrg(directory[i]->script, fp);
  518.             err |= (fwrite(&(directory[i]->connected),   sizeof(USHORT),   1, fp) != 1);
  519.             err |= (fwrite(&(directory[i]->lastconnect), sizeof(DATETIME), 1, fp) != 1);
  520.             err |=  fwriteStrg(directory[i]->password, fp);
  521.             if (err) break;
  522.           }
  523.           else
  524.             break;
  525.         }
  526.         else
  527.         {
  528.           c = 0;
  529.           if (fwrite(&c, sizeof(char), 1, fp) != 1)
  530.             break;
  531.         }
  532.       }
  533.       rVal = (i == MAXDENTRY);
  534.  
  535.       if (!err && !feof(fp))
  536.       {
  537.         for (j = 0; j < ACCCODES; j++)
  538.           err |= fwriteStrg(accCodes[j], fp);
  539.       }
  540.  
  541.     }
  542.     fclose(fp);
  543.   }
  544.  
  545.   return rVal;
  546. }
  547.  
  548.  
  549.  
  550.   // -----------------------------------------------------------------------
  551.   // Free all allocated memory associated with the directory
  552.  
  553.  
  554. void EmptyDirectory()
  555. {
  556.   int i;
  557.  
  558.   for (i = 0; i < MAXDENTRY; i++)
  559.   {
  560.     if (directory[i])
  561.     {
  562.       free(directory[i]);
  563.       directory[i] = NULL;
  564.     }
  565.   }
  566. }
  567.  
  568.  
  569.  
  570. // --------------------------------------------------------------------------
  571.  
  572.  
  573. void Sort(int opt, BOOL fInvert)
  574. {
  575.   // Bubble Sort.  It should be quick enough on 200 items and it has the
  576.   // added feature of being a stable sort so that subsequent resorts will
  577.   // behave as advertised.
  578.  
  579.   BOOL    fDone = FALSE;
  580.   BOOL    fSwap;
  581.   DDENTRY *p;
  582.   char    buf1[16], buf2[16];
  583.   USHORT  i;
  584.  
  585.   while (!fDone)
  586.   {
  587.     fDone = TRUE;
  588.     for (i = 1; i < MAXDENTRY; i++)
  589.     {
  590.       // NULL entries are 'heavier' than non-NULL
  591.       fSwap = (directory[i] && !directory[i-1]);
  592.  
  593.       // If this entry is NULL it cannot be 'lighter' than the previous,
  594.       // at best it's the same so we won't force a swap.  Likewise, if
  595.       // we've already decided to swap because the previous entry was NULL,
  596.       // there's no reason to check further.
  597.  
  598.       if (directory[i] && !fSwap)
  599.       {
  600.         switch (opt)
  601.         {
  602.           case 'T':
  603.             if (fInvert)
  604.               fSwap = (strcmp(directory[i-1]->tag, directory[i]->tag) < 0);
  605.             else
  606.               fSwap = (strcmp(directory[i-1]->tag, directory[i]->tag) > 0);
  607.             break;
  608.  
  609.           case 'N':
  610.             if (fInvert)
  611.               fSwap = (strcmp(directory[i-1]->name, directory[i]->name) < 0);
  612.             else
  613.               fSwap = (strcmp(directory[i-1]->name, directory[i]->name) > 0);
  614.             break;
  615.  
  616.           case 'P':
  617.             if (fInvert)
  618.               fSwap = (strcmp(directory[i-1]->number, directory[i]->number) < 0);
  619.             else
  620.               fSwap = (strcmp(directory[i-1]->number, directory[i]->number) > 0);
  621.             break;
  622.  
  623.           case 'D':
  624.             sprintf(buf1, "%04d%02d%02d%02d%02d%02d",
  625.                           directory[i-1]->lastconnect.year,
  626.                           directory[i-1]->lastconnect.month,
  627.                           directory[i-1]->lastconnect.day,
  628.                           directory[i-1]->lastconnect.hours,
  629.                           directory[i-1]->lastconnect.minutes,
  630.                           directory[i-1]->lastconnect.seconds);
  631.             sprintf(buf2, "%04d%02d%02d%02d%02d%02d",
  632.                           directory[i]->lastconnect.year,
  633.                           directory[i]->lastconnect.month,
  634.                           directory[i]->lastconnect.day,
  635.                           directory[i]->lastconnect.hours,
  636.                           directory[i]->lastconnect.minutes,
  637.                           directory[i]->lastconnect.seconds);
  638.             if (fInvert)
  639.               fSwap = (strcmp(buf1, buf2) < 0);
  640.             else
  641.               fSwap = (strcmp(buf1, buf2) > 0);
  642.             break;
  643.  
  644.           case '#':
  645.             if (fInvert)
  646.               fSwap = (directory[i]->connected > directory[i-1]->connected);
  647.             else
  648.               fSwap = (directory[i]->connected < directory[i-1]->connected);
  649.             break;
  650.         }
  651.       }
  652.       if (fSwap)
  653.       {
  654.         p              = directory[i];
  655.         directory[i]   = directory[i-1];
  656.         directory[i-1] = p;
  657.         fDone = FALSE;
  658.       }
  659.     }
  660.   }
  661. }
  662.  
  663.  
  664.  
  665.  
  666. int main(int argc, char **argv)
  667. {
  668.   BOOL fInvert, fParmError = FALSE;
  669.   FILE *fp;
  670.   char *cp, c;
  671.  
  672.   printf("TE2DSort - CopyRight 1990-1992, Oberon Software\n\n");
  673.  
  674.   if (argc < 4)
  675.     fParmError = TRUE;
  676.   else
  677.   {
  678.     // Make sure that all option characters are valid
  679.     strupr(argv[3]);
  680.     for (cp = argv[3]; *cp; cp++)
  681.     {
  682.       if ((strchr(options, *cp) == NULL) && (*cp != '-'))
  683.       {
  684.         fParmError = TRUE;
  685.         break;
  686.       }
  687.     }
  688.  
  689.     // Make sure that the last option character ISN'T a hyphen
  690.     if (!fParmError && (argv[3][strlen(argv[3])-1] == '-'))
  691.       fParmError = TRUE;
  692.   }
  693.  
  694.   if (fParmError)
  695.   {
  696.     fprintf(stderr, "usage: te2dsort dirfilename outfilename sortoption\n");
  697.     fprintf(stderr, " where sortoption is one or more of:\n");
  698.     fprintf(stderr, "       0 - No sort but close up NULL entries\n");
  699.     fprintf(stderr, "       T - Sort by Tag\n");
  700.     fprintf(stderr, "       N - Sort by Name\n");
  701.     fprintf(stderr, "       P - Sort by Phonenumber\n");
  702.     fprintf(stderr, "       D - Sort by Date of last connection\n");
  703.     fprintf(stderr, "       # - Sort by # of times connected\n");
  704.     fprintf(stderr, " any option character may be preceded with an hyphen to\n");
  705.     fprintf(stderr, "   reverse the sort order on that option only.\n\n");
  706.     fprintf(stderr, " examples:\n");
  707.     fprintf(stderr, "   te2dsort te2.dir te2dir.new T\n");
  708.     fprintf(stderr, "     will sort te2.dir by Tag and write output to te2dir.new\n");
  709.     fprintf(stderr, "   te2dsort te2.dir te2dir.new -D\n");
  710.     fprintf(stderr, "     will sort te2.dir by date, newest to oldest\n");
  711.     fprintf(stderr, "   te2dsort te2.dir te2dir.new PN\n");
  712.     fprintf(stderr, "     will sort first by Phonenumber and then by Name before\n");
  713.     fprintf(stderr, "     writing the output to te2dir.new.  The result will be\n");
  714.     fprintf(stderr, "     a file sorted primarily by Name and subsorted by Phonenumber.\n\n");
  715.     exit(1);
  716.   }
  717.  
  718.   if (!ReadDirectoryFile(argv[1]))
  719.   {
  720.     fprintf(stderr, "Error reading file \"%s\"\n", argv[1]);
  721.     exit(1);
  722.   }
  723.  
  724.   fInvert = FALSE;
  725.   for (cp = argv[3]; *cp; cp++)
  726.   {
  727.     if (*cp == '-')
  728.       fInvert = TRUE;
  729.     else
  730.     {
  731.       printf("Sorting using option %c%s ...", *cp, fInvert ? " (inverted)" : "");
  732.       Sort(*cp, fInvert);
  733.       fInvert = FALSE;
  734.       printf(" Done.\n");
  735.     }
  736.   }
  737.  
  738.   if (fp = fopen(argv[2], "r"))
  739.   {
  740.     fclose(fp);
  741.     fprintf(stderr, "File: \"%s\" already exists! Overwrite? [y/N] ", argv[2]);
  742.     while (TRUE)
  743.     {
  744.       c = (char)getch();
  745.       if (cp = strchr("yYnN\x1b\n", c))
  746.       {
  747.         if ((*cp == 'y') || (*cp == 'Y'))
  748.         {
  749.           fprintf(stderr, "Yes\n");
  750.           break;
  751.         }
  752.         else
  753.         {
  754.           fprintf(stderr, "No\nAborted\n");
  755.           exit(1);
  756.         }
  757.       }
  758.     }
  759.   }
  760.  
  761.   if (!WriteDirectoryFile(argv[2]))
  762.   {
  763.     fprintf(stderr, "Error writing file \"%s\"\n", argv[2]);
  764.     exit(1);
  765.   }
  766.  
  767.   return 0;
  768. }
  769.  
  770.