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

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