home *** CD-ROM | disk | FTP | other *** search
/ Amiga ACS 1998 #4 / amigaacscoverdisc1998-041998.iso / utilities / shareware / comm / qamitrack / source / qamitrack.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-02-19  |  73.7 KB  |  2,392 lines

  1. /* QAmiTrack.c -- a program to keep a running list of available Amigas. */
  2.  
  3. #include "QAmiTrackShared.h"
  4. #include "TrackRexx.h"
  5. #include "QAmitrack.h"
  6.  
  7. #include "include:signal.h"
  8.  
  9. /* our GUI gadget IDs */
  10. #define GAD_CLIENTLIST    10
  11. #define GAD_VARLIST       11
  12. #define GAD_COMMENTSTRING 12
  13. #define GAD_SERVERSTRING  13
  14. #define GAD_PORTSTRING    14
  15. #define GAD_ACTIONCYCLE   15
  16. #define GAD_LOGLIST       16
  17.  
  18. /* Menu item IDs */
  19. #define P_ABOUT         100
  20. #define P_HIDE          101
  21. #define P_QUIT          102
  22. #define O_ENABLED       200
  23. #define O_CONFIRM       201
  24. #define O_RAGGED        202
  25. #define O_BEEPONLOG     203
  26. #define O_LOG           204
  27. #define O_LOGNONE       205
  28. #define O_LOGTERSE      206
  29. #define O_LOGMODERATE   207
  30. #define O_LOGVERBOSE    208
  31. #define O_VIEWHOSTNAME  209
  32. #define O_VIEWUSERNAME  210
  33. #define O_VIEWREALNAME  211
  34. #define O_SORTBYAGE     212
  35. #define O_SORTBYNAME    213
  36. #define O_SORTBYCOMMENT 214
  37. #define O_SORTBYNONE    215
  38. #define O_LOGVIEWOFF    216
  39. #define O_LOGVIEW25     217
  40. #define O_LOGVIEW50     218
  41. #define O_LOGVIEW75     219
  42. #define O_LOGVIEWFULL   220
  43. #define O_REVERTCOMMENT 221
  44. #define O_CLEARLOGVIEW  222
  45.  
  46. /* Not in the menus, but... */
  47. #define O_COMMENT       999
  48.  
  49. #define Strncat(a,b,c) {strncat(a,b,c); a[c-1] = '\0';}
  50. #define Strncpy(a,b,c) {strncpy(a,b,c); a[c-1] = '\0';}
  51.  
  52. /* script callback keywords for rexx script tooltypes */
  53. const char * rexxScriptNames[] = {
  54.    "LOGONSCRIPT",
  55.    "LOGOFFSCRIPT",
  56.    "UPDATESCRIPT",
  57.    "CONNECTSCRIPT",
  58.    "DISCONNECTSCRIPT",
  59.    "STARTSCRIPT",
  60.    "ENDSCRIPT",
  61.    "SYSMESSAGESCRIPT",
  62.    "DEFAULTSCRIPT"
  63. };
  64.   
  65. /* Logging levels */
  66. #define OUTPUT_NONE     0
  67. #define OUTPUT_TERSE    1
  68. #define OUTPUT_MODERATE 2
  69. #define OUTPUT_VERBOSE  3
  70.  
  71. /* chords of what to transmit in SendState() */
  72. #define STATE_REALNAME 0x01
  73. #define STATE_USERNAME 0x02
  74. #define STATE_COMMENT  0x04
  75. #define STATE_WINOPEN  0x08
  76. #define STATE_ALL      0xFF
  77.  
  78. /* overridable, compile-time defaults */
  79. #define DEFAULT_AMITRACK_SERVER        "qamitrack.tibb.at"
  80. #define DEFAULT_AMITRACK_PORT          2957
  81. #define DEFAULT_AMITRACK_COMMENT       ""
  82. #define DEFAULT_AMITRACK_CXPOPUP       "YES"
  83. #define DEFAULT_AMITRACK_CXPRI         0
  84. #define DEFAULT_AMITRACK_POPKEY        "lcommand shift a"
  85. #define DEFAULT_AMITRACK_ENABLED       "YES"
  86. #define DEFAULT_AMITRACK_ALLOWMULTIPLE "NO"
  87. #define DEFAULT_AMITRACK_CONFIRMAPP    "YES"
  88. #define DEFAULT_AMITRACK_RAGGEDTEXT    "NO"
  89. #define DEFAULT_AMITRACK_BEEPONLOG     "NO"
  90. #define DEFAULT_AMITRACK_RECONNECTDELAY 2
  91. #define DEFAULT_AMITRACK_LOGFILE       ""
  92. #define DEFAULT_AMITRACK_LOGLEVEL      "moderate"
  93. #define DEFAULT_AMITRACK_USERNAME      "(anonymous)"
  94. #define DEFAULT_AMITRACK_VIEWBY        "hostname"
  95. #define DEFAULT_AMITRACK_SORTBY        "age"
  96. #define DEFAULT_AMITRACK_ACCESSTO      "#?"
  97. #define DEFAULT_AMITRACK_LOGVIEWLEVEL  "25%"
  98. #define DEFAULT_AMITRACK_LOGVIEWLENGTH 50
  99.  
  100. UBYTE ** ttypes = NULL;
  101. char * rexxScripts[NUM_LOG_ENUMS];
  102.  
  103. int currentAction = -1;
  104. BOOL BConfirmAppLaunch, BConnected = FALSE, BBeepOnLog = FALSE;
  105. BOOL BStartedFromWB = FALSE, BRaggedText;
  106. int clientListLength = 0, logListLen = 0, nLogLevel;
  107. int connectDelayLeft = 0, nReconnectDelay;  /* nReconnectDelay is in minutes */
  108. int nViewBy, nSortBy;
  109.  
  110. int nPort, nNumActions=0, nLastEntryClicked=-1, currentNameLen = -1;
  111. int timeUnit=60, nUpdateDelay=20;  /* Show in minutes, update display three times per minute */
  112. int nLogViewLevel, maxLogListLen;
  113. struct CxStuff * CX = NULL;
  114. struct Client * myInfo = NULL;
  115. char * szServerName = NULL, * szLogFileName = NULL, * szAccessTo, * origComment = NULL;
  116. struct QSession * session = NULL;
  117. char szVersionString[] = "$VER: QAmiTrack 1.92 (Compiled " __DATE__ ")", * pcDisplayVersionString;
  118. struct TimerStuff * TS = NULL;
  119. ULONG lastUpdateTime = 0L;
  120. struct Client * cLastEntryClicked = NULL;
  121. struct Task * mainTask = NULL;
  122.  
  123. void Cleanup(void);
  124. void UpdateWindowTimes(void);
  125. void InitActions(void);  /* NULLs all string pointers */
  126. void FreeActions(void);  /* Frees all string pointers */
  127.  
  128. int main(int argc, char **argv);
  129.  
  130. /* Data structures for Gadtools GUI */
  131. struct Library * WorkbenchBase = NULL;
  132. struct Library * IconBase      = NULL;
  133. struct Library * GraphicsBase  = NULL;
  134. struct Library * IntuitionBase = NULL;
  135. struct Library * GadToolsBase  = NULL;
  136. struct Library * DiskFontBase  = NULL;
  137. struct Library * CxBase        = NULL;
  138. struct Library * AMarqueeBase  = NULL;
  139.  
  140. struct RexxHost    * rexxHost    = NULL;
  141. struct WindowStuff * TrackWindow = NULL;
  142. struct Menu        * Menu        = NULL;
  143. struct MenuItem    * MenuItem    = NULL;
  144.  
  145. struct List clientList, logList;
  146.  
  147. char * ActionLabels[20], * Actions[20];
  148.  
  149. /* default GUI settings */
  150. int nWinLeft        = 50;
  151. int nWinTop            = 100;
  152. int nWinWidth        = 520;
  153. int nWinHeight        = 200;
  154. int nMinWinWidth    = 300;
  155. int nMinWinHeight    = 120;
  156.  
  157. long lLastChangeAt = 0L;
  158. BOOL BEnabled = TRUE, BAllowMultiple = FALSE;
  159.  
  160. /* menus */
  161. struct NewMenu nmMenus[] = {
  162.     NM_TITLE, "Project",      NULL,     0L,             NULL, NULL,
  163.     NM_ITEM,  "About",          "?",    0L,             NULL, (void *) P_ABOUT,
  164.     NM_ITEM,  "Hide",          "H",    0L,             NULL, (void *) P_HIDE,
  165.     NM_ITEM,  NM_BARLABEL,      NULL,     0L,             NULL, NULL,
  166.     NM_ITEM,  "Quit",            "Q",      0L,             NULL, (void *) P_QUIT,
  167.  
  168.     NM_TITLE, "Options",     NULL,  0L,          NULL, NULL,
  169.     NM_ITEM,  "Enabled",     "E",   CHECKIT,     NULL, (void *) O_ENABLED,
  170.     NM_ITEM,  "Confirm",     "C",   CHECKIT,     NULL, (void *) O_CONFIRM,
  171.     NM_ITEM,  "Ragged Text", "T",   CHECKIT,     NULL, (void *) O_RAGGED,
  172.     NM_ITEM,  "Beep On Log", "B",   CHECKIT,     NULL, (void *) O_BEEPONLOG,
  173.  
  174.     NM_ITEM,  "Log Level",   NULL,  NULL,        NULL, NULL,
  175.     NM_SUB,   "None",         "!",  CHECKIT,     NULL, (void *) O_LOGNONE,
  176.     NM_SUB,   "Terse",        "@",  CHECKIT,     NULL, (void *) O_LOGTERSE,
  177.     NM_SUB,   "Moderate",     "#",  CHECKIT,     NULL, (void *) O_LOGMODERATE,
  178.     NM_SUB,   "Verbose",      "$",  CHECKIT,     NULL, (void *) O_LOGVERBOSE,        
  179.  
  180.     NM_ITEM,  "View By",     NULL,  0L,          NULL, NULL,
  181.     NM_SUB,   "Hostname",     "N",  CHECKIT,     NULL, (void *) O_VIEWHOSTNAME,
  182.     NM_SUB,   "User Name",    "U",  CHECKIT,     NULL, (void *) O_VIEWUSERNAME,
  183.     NM_SUB,   "Real Name",    "R",  CHECKIT,     NULL, (void *) O_VIEWREALNAME,
  184.     
  185.     NM_ITEM,  "Sort By",     NULL,  0L,          NULL, NULL,
  186.     NM_SUB,   "Age",          "A",  CHECKIT,     NULL, (void *) O_SORTBYAGE,
  187.     NM_SUB,   "Name",         "M",  CHECKIT,     NULL, (void *) O_SORTBYNAME,
  188.     NM_SUB,   "Comment",      "L",  CHECKIT,     NULL, (void *) O_SORTBYCOMMENT,
  189.     NM_SUB,   "None",         "G",  CHECKIT,     NULL, (void *) O_SORTBYNONE,
  190.  
  191.     NM_ITEM,  "Log View",   NULL,  0L,          NULL, NULL,
  192.     NM_SUB,   "Off",          "1",  CHECKIT,     NULL, (void *) O_LOGVIEWOFF,
  193.     NM_SUB,   "25%",          "2",  CHECKIT,     NULL, (void *) O_LOGVIEW25,
  194.     NM_SUB,   "50%",          "3",  CHECKIT,     NULL, (void *) O_LOGVIEW50,
  195.     NM_SUB,   "75%",          "4",  CHECKIT,     NULL, (void *) O_LOGVIEW75,
  196.     NM_SUB,   "Full",         "5",  CHECKIT,     NULL, (void *) O_LOGVIEWFULL,
  197.  
  198.     NM_ITEM,  "Revert Comment",".", 0L,          NULL, (void *) O_REVERTCOMMENT,
  199.     NM_ITEM,  "Clear Log View","Z", 0L,          NULL, (void *) O_CLEARLOGVIEW,
  200.     
  201.     NM_END,   NULL,          NULL,     NULL,             NULL, NULL
  202. };
  203.  
  204. /* GUI functions */
  205. struct WindowStuff * SetupTrackWindow(struct WindowStuff *);
  206. void HandleIDCMP(struct WindowStuff *);
  207. BOOL UpdateWindow(struct WindowStuff * win, BOOL BFree);
  208. struct Node * AllocDisplayItem(char * szHostName, ULONG ulIPAddress, ULONG ulHostFieldLen, char * szComment);
  209. void AttachClientList(struct WindowStuff * win, BOOL BAttach);
  210. void AttachLogList(struct WindowStuff * win, BOOL BAttach);
  211. BOOL CreateTrackMenus(struct WindowStuff * win, BOOL BCreate);
  212.  
  213. /* ------------ Amiga GUI functions -------------------------- */
  214.  
  215. #define HSPACE               5
  216. #define VSPACE               5
  217. #define SERVER_STRING_TEXT   "Server:"
  218. #define PORT_STRING_TEXT     "Port:"
  219. #define COMMENT_STRING_TEXT  "Comment:"
  220.  
  221. void edebug(int i)
  222. {
  223.   int t = *((int *)i);
  224.   t++;
  225. }
  226.  
  227. struct String * NewString(char * string)
  228. {
  229.   struct String * ret;
  230.   ULONG len = strlen(string)+1;
  231.   
  232.   if (ret = AllocMem(sizeof(struct String) + len, MEMF_ANY))
  233.   {
  234.     ret->bufLen = len;
  235.     ret->buffer = &ret[1];
  236.     strcpy(ret->buffer, string);
  237.   }
  238.   return(ret);
  239. };
  240.  
  241. int GetLogViewLevelPercent(ULONG ulCode)
  242. {
  243.   switch(ulCode)
  244.   {
  245.     case O_LOGVIEWOFF:   return 0;
  246.     case O_LOGVIEW25:    return 25;
  247.     case O_LOGVIEW50:    return 50;
  248.     case O_LOGVIEW75:    return 75;
  249.     case O_LOGVIEWFULL:  return 100;
  250.   }
  251.   return -1;  /* error! */
  252. }
  253.  
  254. void FreeString(struct String * s)
  255. {
  256.   FreeMem(s, sizeof(struct String) + s->bufLen);
  257. }
  258.  
  259. /* Returns TRUE and sets s on success, FALSE on failure.
  260.    On failure, old string is still okay. */
  261. BOOL SetString(struct String ** s, char * newVal)
  262. {
  263.   struct String * newString;
  264.   
  265.   UNLESS((newVal)&&(newString = NewString(newVal))) return(FALSE);
  266.   FreeString(*s);
  267.   *s = newString;
  268.   return(TRUE);
  269. }
  270.  
  271. int GetFieldIDByName(char * name)
  272. {
  273.   UNLESS(strcmp(name,"comment"))  return(O_COMMENT);
  274.   UNLESS(strcmp(name,"hostname")) return(O_VIEWHOSTNAME);
  275.   UNLESS(strcmp(name,"username")) return(O_VIEWUSERNAME);
  276.   UNLESS(strcmp(name,"realname")) return(O_VIEWREALNAME);
  277.   return(0);
  278. }
  279.  
  280. struct String ** getField(struct Client * c, char * f)
  281. {
  282.   UNLESS(strcmp(f,"comment"))  return(&c->comment);
  283.   UNLESS(strcmp(f,"username")) return(&c->userName);
  284.   UNLESS(strcmp(f,"realname")) return(&c->realName);
  285.   UNLESS(strcmp(f,"winopen"))  return(&c->winOpen);
  286.   return(NULL);
  287. }
  288.  
  289.  
  290. void FreeClient(struct Client * c)
  291. {
  292.   if (c->hostName)   FreeString(c->hostName);
  293.   if (c->userName)   FreeString(c->userName);
  294.   if (c->realName)   FreeString(c->realName);
  295.   if (c->comment)    FreeString(c->comment);
  296.   if (c->listString) FreeString(c->listString);
  297.   if (c->winOpen)    FreeString(c->winOpen);
  298.   if (cLastEntryClicked == c) cLastEntryClicked = NULL;
  299.   FreeMem(c, sizeof(struct Client));
  300. }
  301.  
  302. struct Client * NewClient(char * hostName)
  303. {
  304.   struct Client * ret;
  305.   
  306.   if (ret = AllocMem(sizeof(struct Client), MEMF_CLEAR))
  307.   {
  308.     if ((ret->firstTime  = TRUE) &&
  309.         (ret->hostName   = NewString(hostName))&&
  310.         (ret->userName   = NewString("anon"))&&
  311.         (ret->realName   = NewString("Anonymous"))&& /*********************/
  312.         (ret->winOpen    = NewString(""))&&          /* Will be set later */
  313.         (ret->comment    = NewString(""))&&          /*********************/
  314.         (ret->listString = NewString("")))
  315.     {
  316.       ret->node.ln_Name = "";
  317.       ret->timeStamp  = time(NULL);
  318.       ret->viewOffset = 0;
  319.     }
  320.     else
  321.     {
  322.       /* Failure! */
  323.       FreeClient(ret);
  324.       ret = NULL;
  325.     }      
  326.   }
  327.   return(ret);
  328. }
  329.  
  330. /* To tear down, set BCreate==FALSE */
  331. BOOL CreateTrackMenus(struct WindowStuff * win, BOOL BCreate)
  332. {   
  333.     UNLESS((win)&&(win->vi)) return(FALSE);
  334.     
  335.     UNLESS(BCreate) 
  336.     {
  337.         if (Menu) 
  338.         {
  339.             ResetMenuStrip(win->win,Menu); 
  340.             FreeMenus(Menu); 
  341.             Menu = NULL;
  342.         }
  343.         return(FALSE);
  344.     }
  345.  
  346.     /* Create menus */
  347.     UNLESS((Menu = CreateMenus(nmMenus, TAG_DONE)) &&
  348.            (LayoutMenus(Menu, win->vi, TAG_DONE)))
  349.            return(CreateTrackMenus(win, FALSE));
  350.  
  351.     SetMenuStrip(win->win, Menu);
  352.     return(TRUE);
  353. }
  354.  
  355. struct String * getDisplayIndexString(struct Client * c)
  356. {
  357.   switch(nViewBy)
  358.   {
  359.     case O_VIEWHOSTNAME: return c->hostName; 
  360.     case O_VIEWUSERNAME: return c->userName; 
  361.     case O_VIEWREALNAME: return c->realName; 
  362.   }
  363.   printf("getDisplayIndexString:  warning, returning NULL!\n");
  364.   return(NULL);
  365. }
  366.  
  367. void dateStamp(FILE * f)
  368. {
  369.   static char temp[100], *t;
  370.   
  371.   sprintf(temp, "%s", ctime(NULL));
  372.   if (t = strchr(temp, '\n')) *t = '\0';
  373.   fprintf(f, "%s ",temp);
  374. }
  375.  
  376. void RunRexxScriptsForEvent(int opCode, struct Client * actOn, char * fieldChanged, char * aux1, char * aux2)
  377. {
  378.   int updateType = fieldChanged ? GetFieldIDByName(fieldChanged) : 0;
  379.   char * rexxScript = rexxScripts[opCode];
  380.  
  381.   if ((opCode == LOG_UPDATE)&&(updateType != O_COMMENT)) return;  /* Non-comment types are redundant */
  382.   if (*rexxScript == '\0') rexxScript = rexxScripts[LOG_DEFAULT];
  383.   if (*rexxScript)
  384.   {
  385.     static char temp[256];
  386.     char sep[] = "^";
  387.     
  388.     Strncpy(temp, rexxScript, sizeof(temp));
  389.     Strncat(temp, " ", sizeof(temp));
  390.     Strncat(temp, rexxScriptNames[opCode], sizeof(temp));
  391.     
  392.     switch(opCode)
  393.     {
  394.       case LOG_UPDATE:
  395.       case LOG_LOGON:
  396.       case LOG_LOGOFF:
  397.         Strncat(temp, sep, sizeof(temp));
  398.         Strncat(temp, actOn->hostName->buffer, sizeof(temp));
  399.         Strncat(temp, sep, sizeof(temp));
  400.         Strncat(temp, actOn->userName->buffer, sizeof(temp));
  401.         Strncat(temp, sep, sizeof(temp));
  402.         Strncat(temp, actOn->realName->buffer, sizeof(temp));
  403.         Strncat(temp, sep, sizeof(temp));
  404.         Strncat(temp, (*actOn->winOpen->buffer == 'Y') ? "Y" : "N", sizeof(temp));
  405.         Strncat(temp, sep, sizeof(temp));
  406.         Strncat(temp, actOn->comment->buffer, sizeof(temp));
  407.       break;
  408.  
  409.       case LOG_CONNECT:
  410.       case LOG_DISCONNECT:
  411.         if (szServerName) 
  412.         {
  413.           Strncat(temp, sep, sizeof(temp));
  414.           Strncat(temp, szServerName, sizeof(temp));
  415.         }
  416.       break;
  417.  
  418.       case LOG_SYSMESSAGE:
  419.         Strncat(temp, sep,  sizeof(temp));
  420.         Strncat(temp, aux1, sizeof(temp));
  421.         Strncat(temp, sep,  sizeof(temp));
  422.         Strncat(temp, aux2, sizeof(temp));
  423.       break;
  424.       
  425.       case LOG_START:
  426.       case LOG_END:
  427.         /* No info to append */
  428.     }
  429.  
  430.     if (rexxHost) SendRexxCommand(rexxHost, temp, 0L);
  431.   }
  432. }
  433.  
  434.  
  435. void LogEvent(int opCode, struct Client * actOn, char * fieldChanged, char * aux1, char * aux2)
  436. {
  437.   FILE * logfile;
  438.   char * id      = actOn ? getDisplayIndexString(actOn)->buffer : NULL;
  439.   char * comment = actOn ? actOn->comment->buffer       : NULL;
  440.   int updateType = fieldChanged ? GetFieldIDByName(fieldChanged) : 0;
  441.   
  442.   if ((id)&&(*id == '\0')) id = actOn->hostName->buffer;
  443.  
  444.   if (nLogLevel == OUTPUT_NONE) return;
  445.   UNLESS((szLogFileName)&&(*szLogFileName)&&(logfile = fopen(szLogFileName, "a"))) return;
  446.  
  447.   switch(nLogLevel)
  448.   {
  449.     case OUTPUT_VERBOSE:
  450.      if (opCode == LOG_START) {dateStamp(logfile); fprintf(logfile, "QAmiTrack begins execution.\n");}
  451.      if (opCode == LOG_END)   {dateStamp(logfile); fprintf(logfile, "QAmiTrack exits.\n");}
  452.      
  453.     case OUTPUT_MODERATE:
  454.      if ((opCode == LOG_UPDATE)&&(updateType == O_COMMENT)&&(*comment)) {dateStamp(logfile); fprintf(logfile, "[%s] says: [%s]\n", id, comment);}
  455.      if (opCode == LOG_CONNECT){dateStamp(logfile); fprintf(logfile, "QAmiTrack has connected to server [%s]\n", szServerName);}
  456.      
  457.     case OUTPUT_TERSE: 
  458.      if ((BConnected)&&(opCode == LOG_DISCONNECT)) 
  459.      {
  460.        dateStamp(logfile); 
  461.        fprintf(logfile,"The QAmiTrack connection was closed%s%s%s.\n",szServerName,aux1?" [":"",aux1?aux1:"",aux1?"]":"");
  462.      }  
  463.      if (opCode == LOG_LOGON)      
  464.      {
  465.        dateStamp(logfile); fprintf(logfile, "[%s] is now online.\n",id);
  466.      }
  467.      if (opCode == LOG_LOGOFF)     {dateStamp(logfile); fprintf(logfile, "[%s] is no longer on line.\n",id);}
  468.      if (opCode == LOG_SYSMESSAGE) {dateStamp(logfile); fprintf(logfile, "System Message from [%s]: [%s]\n", aux1, aux2);}
  469.  
  470.     case OUTPUT_NONE: 
  471.      /* Do nothing--actually this case is never executed anyway  */
  472.   }
  473.   fclose(logfile);
  474. }
  475.  
  476.  
  477. void LogDisplay(char * msg)
  478. {
  479.   struct Node * node = AllocMem(sizeof(struct Node)+strlen(msg)+1, MEMF_CLEAR);
  480.  
  481.   if (node)
  482.   {
  483.     node->ln_Name = ((char *)node)+sizeof(struct Node);
  484.     strcpy(node->ln_Name, msg);
  485.     AttachLogList(TrackWindow, FALSE);
  486.     AddTail(&logList, node); logListLen++;
  487.     if (logListLen > maxLogListLen)
  488.     {
  489.       struct Node * top = RemHead(&logList);
  490.       if (top) 
  491.       {
  492.         FreeMem(top,sizeof(struct Node)+strlen(top->ln_Name)+1);
  493.         logListLen--;
  494.       }
  495.     }
  496.     AttachLogList(TrackWindow, TRUE);
  497.     if ((TrackWindow)&&(TrackWindow->win)&&(TrackWindow->LogListGadget)) 
  498.        GT_SetGadgetAttrs(TrackWindow->LogListGadget, TrackWindow->win, NULL, GTLV_Top, logListLen+1, TAG_END);       
  499.        
  500.     if (BBeepOnLog) DisplayBeep(NULL);
  501.   }
  502.   else printf("Oops, no memory to allocate Log Display item!\n");
  503. }
  504.  
  505. void ClearLogView(void)
  506. {
  507.   struct Node * next;
  508.  
  509.   AttachLogList(TrackWindow, FALSE);
  510.   while(next = RemHead(&logList)) FreeMem(next,sizeof(struct Node)+strlen(next->ln_Name)+1);
  511.   logListLen = 0;
  512.   AttachLogList(TrackWindow, TRUE);
  513. }
  514.  
  515. void DisplayEvent(int opCode, struct Client * actOn, char * fieldChanged, char * aux1, char * aux2)
  516. {
  517.   char * id      = actOn ? getDisplayIndexString(actOn)->buffer : NULL;
  518.   char * comment = actOn ? actOn->comment->buffer       : NULL;
  519.   int updateType = fieldChanged ? GetFieldIDByName(fieldChanged) : 0;
  520.   static char temp[512];
  521.     
  522.   if ((id)&&(*id == '\0')) id = actOn->hostName->buffer;
  523.  
  524.   switch(nLogLevel)
  525.   {
  526.     case OUTPUT_VERBOSE:
  527.      if (opCode == LOG_START) LogDisplay("(QAmiTrack begins execution)");
  528.      if (opCode == LOG_END)   LogDisplay("(QAmiTrack exits)");
  529.      
  530.     case OUTPUT_MODERATE:
  531.      if (opCode == LOG_CONNECT)
  532.      {
  533.        Strncpy(temp, "QAmiTrack has connected to server [", sizeof(temp));
  534.        Strncat(temp, szServerName, sizeof(temp));
  535.        Strncat(temp, "]", sizeof(temp));
  536.        LogDisplay(temp);
  537.      }
  538.      if (opCode == LOG_LOGON)      
  539.      {
  540.        Strncpy(temp, "[", sizeof(temp));
  541.        Strncat(temp, id, sizeof(temp));
  542.        Strncat(temp, "] is now online.", sizeof(temp));
  543.        LogDisplay(temp);
  544.      }
  545.      if (opCode == LOG_LOGOFF)     
  546.      {
  547.        Strncpy(temp, "[", sizeof(temp));
  548.        Strncat(temp, id, sizeof(temp));
  549.        Strncat(temp, "] is no longer online.", sizeof(temp));
  550.        LogDisplay(temp);
  551.      }
  552.  
  553.     case OUTPUT_TERSE: 
  554.      if ((BConnected)&&(opCode == LOG_DISCONNECT)) 
  555.      {
  556.        Strncpy(temp, "The QAmiTrack connection was closed.", sizeof(temp));
  557.        if (aux1)
  558.        {
  559.          Strncat(temp, " [", sizeof(temp));
  560.          Strncat(temp, aux1, sizeof(temp));
  561.          Strncat(temp, "]", sizeof(temp));
  562.        }
  563.        LogDisplay(temp);
  564.      }  
  565.      if ((opCode == LOG_UPDATE)&&(updateType == O_COMMENT)&&(*comment))
  566.      {
  567.        Strncpy(temp, id, sizeof(temp));
  568.        Strncat(temp, ": ", sizeof(temp));
  569.        Strncat(temp, comment, sizeof(temp));
  570.        LogDisplay(temp);
  571.      }
  572.  
  573.  
  574.     case OUTPUT_NONE: 
  575.      if (opCode == LOG_SYSMESSAGE) 
  576.      {
  577.        Strncpy(temp, "SYSTEM MESSAGE from [", sizeof(temp));
  578.        Strncat(temp, aux1, sizeof(temp));
  579.        Strncat(temp, "]: ", sizeof(temp));
  580.        Strncat(temp, aux2, sizeof(temp));
  581.        LogDisplay(temp);
  582.      }
  583.   }
  584. }
  585.  
  586. void RecordEvent(int opCode, struct Client * actOn, char * fieldChanged, char * aux1, char * aux2)
  587. {
  588.   LogEvent(opCode, actOn, fieldChanged, aux1, aux2);
  589.   DisplayEvent(opCode, actOn, fieldChanged, aux1, aux2);
  590.   RunRexxScriptsForEvent(opCode, actOn, fieldChanged, aux1, aux2);
  591. }
  592.  
  593. void InitActions()
  594. {
  595.   int i;
  596.   
  597.   for (i=0; i<(sizeof(Actions)/sizeof(char *)); i++)
  598.     Actions[i] = ActionLabels[i] = NULL;
  599.   nNumActions = 0;
  600. }
  601.  
  602. void FreeActions()
  603. {
  604.   int i;
  605.   
  606.   for (i=0; i<(sizeof(Actions)/sizeof(char *)); i++)
  607.   {
  608.     if (Actions[i]) ReplaceAllocedString(&Actions[i], NULL);
  609.     if (ActionLabels[i]) ReplaceAllocedString(&ActionLabels[i], NULL);
  610.   }
  611.   nNumActions = 0;
  612. }
  613.  
  614.  
  615. struct Client * GetClientByIndex(int index)
  616. {
  617.   struct Client * next = (struct Client *) clientList.lh_Head;
  618.   
  619.   while(index--) UNLESS(next = next->node.ln_Succ) return(NULL);
  620.   return(next);
  621. }
  622.  
  623. void ListClicked(int index)
  624. {
  625.   static time_t tLastTimeClicked = (time_t) -1;
  626.   time_t tPreviousTimeClicked = tLastTimeClicked;
  627.     
  628.   tLastTimeClicked = time(NULL);
  629.  
  630.   /* Record which client it was */
  631.   nLastEntryClicked = index;  
  632.   cLastEntryClicked = GetClientByIndex(nLastEntryClicked);
  633.         
  634.   if ((index == nLastEntryClicked) &&
  635.      ((tLastTimeClicked-tPreviousTimeClicked) < 2)) (void)DoAction(index, currentAction, BConfirmAppLaunch);        
  636. }
  637.  
  638.  
  639. struct Client * GetHost(int index)
  640. {
  641.   struct Client * next = (struct Client *) clientList.lh_Head;
  642.   
  643.   while(index--) {UNLESS(next = next->node.ln_Succ) return(NULL);}
  644.   return(next);
  645. }
  646.  
  647. int GetTrackHostByName(char * name)
  648. {
  649.   struct Node * current;
  650.   int i=0;
  651.   
  652.   current = clientList.lh_Head;
  653.   while(current->ln_Succ)
  654.   {
  655.     struct Client * c = (struct Client *) current;
  656.     
  657.     if (strcmp(c->hostName->buffer, name) == 0) return(i);
  658.     current = current->ln_Succ;
  659.     i++;
  660.   }
  661.   return(-1);
  662. }
  663.  
  664. int GetTrackActionByName(char * name)
  665. {
  666.   int i;
  667.   
  668.   for (i=0;i<nNumActions;i++) if (strcmp(name, ActionLabels[i])==0) return(i);
  669.   return(-1);
  670. }
  671.  
  672. /* Allocates a newly malloc'd string with all the replaceMe's replaced with withMe's */
  673. /* free()'s oldString, or returns it on error or if no changes were made. */
  674. char * makeSubbedString(char * oldString, char * replaceMe, char * withMe)
  675. {
  676.   int len = strlen(oldString);
  677.   int oldStringLen = len;
  678.   int replaceMeLen = strlen(replaceMe);
  679.   int withMeLen    = strlen(withMe);
  680.   int diff = withMeLen-replaceMeLen;
  681.   int numChanges = 0;
  682.   char * newString, * temp = oldString;
  683.       
  684.   /* Figure out how much extra space we'll need */
  685.   while(temp = strstr(temp, replaceMe))
  686.   {
  687.     len += diff;
  688.     temp += replaceMeLen;
  689.     numChanges++;
  690.   }
  691.   if (numChanges == 0) return(oldString);
  692.   if (newString = malloc(len+1+1))  /* 1 for the NUL, 1 for the debug guard */
  693.   {
  694.     char * nextSub;
  695.  
  696.     temp = oldString;    
  697.     *newString = '\0';
  698.  
  699.     /* Place the debug guard here! */
  700.     newString[len+1] = '~';  /* After the NUL, we'll check later to see if this was overridden (which would indicate a BUG!) */
  701.     while(nextSub = strstr(temp, replaceMe))
  702.     {      
  703.       char c = *nextSub;
  704.       
  705.       *nextSub = '\0';
  706.       strcat(newString, temp);
  707.       strcat(newString, withMe);
  708.       temp = nextSub + replaceMeLen;
  709.       *nextSub = c;
  710.     }
  711.     strcat(newString, temp);  
  712.     
  713.     if (newString[len+1] != '~') (void)MakeReq(NULL, "There's a BUG in QAmiTrack's launching code!", "Better go email Jeremy!");
  714.     free(oldString);
  715.   }
  716.   return(newString ? newString : oldString);
  717. }
  718.  
  719. int DoAction(int index, int action, BOOL BConf)
  720. {
  721.   int result = -2;
  722.   struct Client * target;
  723.   char * s;
  724.   struct Task * thisTask = FindTask(NULL);
  725.   int stackSize = thisTask->tc_SPUpper - thisTask->tc_SPLower;
  726.   
  727. printf("stackSize = %i\n",stackSize);
  728.   UNLESS((action < nNumActions)&&(action >= 0)&&(target = GetHost(index))) return(-1);
  729.   
  730.   if (BConf)
  731.   {
  732.     /* Do user confirmation */
  733.     const char p1[] = "Okay to connect to ";
  734.     const char p2[] = " using ";
  735.     const char p3[] = "?";
  736.     char * reqText = NULL;
  737.     int reqTextLen = strlen(p1) + strlen(target->hostName->buffer) + strlen(p2) + strlen(ActionLabels[action]) + strlen(p3) + 1;
  738.     BOOL BOk;
  739.  
  740.     UNLESS(reqText = AllocMem(reqTextLen, MEMF_ANY)) return(-1);
  741.     sprintf(reqText, "%s%s%s%s%s",p1,target->hostName->buffer,p2,ActionLabels[action],p3);
  742.     BOk = MakeReq("QAmiTrack Launch Confirmation", reqText, "Connect|Cancel");
  743.     FreeMem(reqText, reqTextLen);
  744.     UNLESS(BOk) return(-2);
  745.   }
  746.     
  747.   if (s = strdup(Actions[action]))
  748.   {
  749.     char timeString1[35], timeString2[35];
  750.       
  751.     sprintf(timeString1,"%i",(time(NULL)-target->timeStamp)/timeUnit);
  752.     sprintf(timeString2,"%i",(time(NULL)-lastUpdateTime)/timeUnit);
  753.  
  754.     s = makeSubbedString(s, "%s", target->hostName->buffer);      
  755.     s = makeSubbedString(s, "%h", target->hostName->buffer);
  756.       
  757.     s = makeSubbedString(s, "%u", target->userName->buffer);
  758.     s = makeSubbedString(s, "%U", myInfo->userName->buffer);
  759.       
  760.     s = makeSubbedString(s, "%c", target->comment->buffer);
  761.     s = makeSubbedString(s, "%C", myInfo->comment->buffer);
  762.     
  763.     s = makeSubbedString(s, "%r", target->realName->buffer);
  764.     s = makeSubbedString(s, "%R", myInfo->realName->buffer);
  765.       
  766.     s = makeSubbedString(s, "%l", target->listString->buffer);
  767.       
  768.     s = makeSubbedString(s, "%t", timeString1);
  769.     s = makeSubbedString(s, "%T", timeString2);
  770.  
  771.     s = makeSubbedString(s, "%%", "%");
  772.     s = makeSubbedString(s, "%q", "\"");
  773.  
  774.     if ((result = SystemTags(s, 
  775.        SYS_Asynch, TRUE,
  776.        SYS_Input,  Open("NIL:",MODE_OLDFILE), 
  777.        SYS_Output, NULL,
  778.        NP_StackSize, stackSize, 
  779.        TAG_DONE)) == -1)
  780.          MakeReq(NULL,"Error launching program!", NULL);
  781.  
  782.     free(s);
  783.   }
  784.   return(result);
  785. }
  786.  
  787.  
  788. int getActionCycleWidth(struct WindowStuff * win)
  789. {    
  790.   int i,len = 0;
  791.   for (i=0; (ActionLabels[i] != NULL); i++)
  792.   {
  793.     int next = TextLength(&win->screen->RastPort, ActionLabels[i], strlen(ActionLabels[i]));
  794.     if (next > len) len = next; 
  795.   }
  796.   if (len > 0) len += HSPACE*6; /* For the arrow icon! */
  797.   if (len > (win->win->Width/2)) len = win->win->Width/2;  /* Not TOO wide! */
  798.   return(len);
  799. }
  800.     
  801.  
  802. /* Updates/reupdates the window on creation or after a size change */
  803. /* If BFree is TRUE, just deallocate anything that was allocated */
  804. /* Returns the allocated/deallocated state of the window gadgets */
  805. BOOL UpdateWindow(struct WindowStuff * win, BOOL BFree)
  806. {
  807.     struct NewGadget ng;
  808.     struct Gadget * gad;
  809.     int actionLabelLength;
  810.     int leftoverHeight, commentListViewHeight, logListViewHeight;
  811.  
  812.     UNLESS((win)&&(win->screen)) return(FALSE);
  813.     
  814.     /* First deallocate */
  815.     if ((win->win)&&(win->glist)) RemoveGList(win->win, win->glist, -1);
  816.     if (win->glist)    {FreeGadgets(win->glist); win->glist = NULL;}
  817.  
  818.     /* Mark all gadgets as free */
  819.     win->CommentListGadget = win->LogListGadget = 
  820.     win->ServerString = win->PortString = win->CommentString = NULL;
  821.  
  822.     if (Menu) CreateTrackMenus(win, FALSE);
  823.     if (win->vi) {FreeVisualInfo(win->vi); win->vi = NULL;}
  824.     if (BFree) return(FALSE);    /* If we're just freeing, that's all to do */
  825.  
  826.     if (win->win)
  827.     {
  828.         /* erase any gadget imagery */
  829.         EraseRect(win->win->RPort,win->win->BorderLeft, win->win->BorderTop,
  830.           win->win->Width  - win->win->BorderRight - 1,
  831.           win->win->Height - win->win->BorderBottom - 1);
  832.         RefreshWindowFrame(win->win);               
  833.     }
  834.  
  835.     /* Make sure datestamps are up to date and timer is ticking */
  836.     UpdateWindowTimes();
  837.  
  838.     /* Make everything NULL, etc. */
  839.     memset(&ng, 0, sizeof(ng));
  840.  
  841.     /* Now start allocating */
  842.     UNLESS(win->vi = GetVisualInfo(win->screen,TAG_END)) return(FALSE);
  843.     UNLESS(CreateTrackMenus(win, TRUE))                  return(FALSE);
  844.     UNLESS(gad     = CreateContext(&win->glist))         return(FALSE);
  845.  
  846.     /* Allocate Server gadget */
  847.     ng.ng_VisualInfo = win->vi;
  848.     ng.ng_TextAttr   = &win->font;
  849.     ng.ng_Height     = win->font.ta_YSize + VSPACE;
  850.     ng.ng_GadgetText = SERVER_STRING_TEXT;
  851.     ng.ng_LeftEdge   += win->screen->WBorLeft + TextLength(&win->screen->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText)) + (HSPACE*3);
  852.     ng.ng_TopEdge    = win->screen->WBorTop + win->screen->RastPort.TxHeight + VSPACE;
  853.     ng.ng_Width      = ((win->win->Width * 2)/3) - ng.ng_LeftEdge;
  854.     ng.ng_GadgetID   = GAD_SERVERSTRING;
  855.     ng.ng_Flags         = PLACETEXT_LEFT;
  856.     win->ServerString = gad = CreateGadget(STRING_KIND, gad, &ng, GTST_String, szServerName, GT_Underscore, '_', TAG_END);
  857.  
  858.     /* Allocate Port gadget */
  859.     ng.ng_GadgetText = PORT_STRING_TEXT;
  860.     ng.ng_LeftEdge   += ng.ng_Width + TextLength(&win->screen->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText)) + (HSPACE*3);
  861.     ng.ng_Width      = win->win->Width - ng.ng_LeftEdge - win->screen->WBorRight - HSPACE;
  862.     ng.ng_GadgetID   = GAD_PORTSTRING;
  863.     ng.ng_Flags         = PLACETEXT_LEFT;
  864.     win->PortString = gad = CreateGadget(INTEGER_KIND, gad, &ng, GTIN_Number, nPort, GT_Underscore, '_', TAG_END);
  865.  
  866.     /* Calculate the width of the widest action label */
  867.     actionLabelLength = getActionCycleWidth(win);
  868.     
  869.     /* Allocate Comment gadget */
  870.     ng.ng_GadgetText = COMMENT_STRING_TEXT;
  871.     ng.ng_LeftEdge   = win->screen->WBorLeft + TextLength(&win->screen->RastPort, ng.ng_GadgetText, strlen(ng.ng_GadgetText)) + (HSPACE*3);
  872.     ng.ng_TopEdge    += ng.ng_Height + VSPACE;
  873.     ng.ng_Width      = win->win->Width - ng.ng_LeftEdge - win->screen->WBorRight - HSPACE - actionLabelLength - (actionLabelLength ? 2*HSPACE : 0);
  874.     ng.ng_GadgetID   = GAD_COMMENTSTRING;
  875.     ng.ng_Flags         = PLACETEXT_LEFT;
  876.     win->CommentString = gad = CreateGadget(STRING_KIND, gad, &ng, GTST_String, myInfo->comment->buffer, GTST_MaxChars, 1023, GT_Underscore, '_', TAG_END);
  877.  
  878.     /* Allocate Actions cycle, if we have any actions! */
  879.     if (actionLabelLength > 0)
  880.     {
  881.       ng.ng_GadgetText = NULL;
  882.       ng.ng_LeftEdge   += ng.ng_Width + HSPACE;
  883.       ng.ng_Width      = actionLabelLength + HSPACE;
  884.       ng.ng_GadgetID   = GAD_ACTIONCYCLE;
  885.       win->ActionCycle = gad = CreateGadget(CYCLE_KIND, gad, &ng, GTCY_Labels, ActionLabels, GTCY_Active, currentAction, GT_Underscore, '_', TAG_END);
  886.     }
  887.     else win->ActionCycle = NULL;
  888.  
  889.     /* Allocate ListView */
  890.     ng.ng_GadgetText = NULL;
  891.     ng.ng_TextAttr   = &win->fixedfont;
  892.     ng.ng_LeftEdge   = win->screen->WBorLeft + HSPACE;
  893.     ng.ng_TopEdge   += ng.ng_Height + VSPACE;
  894.     ng.ng_Width         = win->win->Width - ng.ng_LeftEdge - win->screen->WBorRight - HSPACE;
  895.  
  896.     /* calculate heights of the two ListView's */
  897.     leftoverHeight = win->win->Height - ng.ng_TopEdge - win->screen->WBorBottom - (VSPACE*2);
  898.     switch(nLogViewLevel)
  899.     {
  900.        case O_LOGVIEWOFF:  commentListViewHeight = leftoverHeight;       logListViewHeight = 0;                    break;
  901.        case O_LOGVIEW25:   commentListViewHeight = (3*leftoverHeight)/4; logListViewHeight = leftoverHeight/4;     break;
  902.        case O_LOGVIEW50:   commentListViewHeight = leftoverHeight/2;     logListViewHeight = leftoverHeight/2;     break;
  903.        case O_LOGVIEW75:   commentListViewHeight = leftoverHeight/4;     logListViewHeight = (3*leftoverHeight)/4; break;
  904.        case O_LOGVIEWFULL: commentListViewHeight = 0;                    logListViewHeight = leftoverHeight;       break;      
  905.     }
  906.  
  907.     ng.ng_Height     = commentListViewHeight;
  908.     ng.ng_GadgetID   = GAD_CLIENTLIST;
  909.  
  910.     if (ng.ng_Height > 0)
  911.     {
  912.        struct Gadget * tempGad = win->CommentListGadget = CreateGadget(LISTVIEW_KIND, gad, &ng, GTLV_Labels, &clientList, GTLV_ReadOnly, FALSE, TAG_END);
  913.        if (tempGad) gad = tempGad;
  914.        ng.ng_TopEdge += ng.ng_Height;
  915.     }
  916.     
  917.     ng.ng_Height     = logListViewHeight;
  918.     ng.ng_GadgetID   = GAD_LOGLIST;
  919.     if (ng.ng_Height > 0)
  920.     {
  921.        struct Gadget * tempGad = win->LogListGadget = CreateGadget(LISTVIEW_KIND, gad, &ng, GTLV_Labels, &logList, GTLV_ReadOnly, TRUE, TAG_END);
  922.        if (tempGad) gad = tempGad;
  923.        ng.ng_TopEdge += ng.ng_Height;
  924.     }
  925.  
  926.     /* Attach gadgets to window */
  927.     AddGList(win->win, win->glist, -1, -1, NULL);
  928.     RefreshGList(win->glist, win->win, NULL, -1);
  929.     GT_RefreshWindow(win->win, NULL);
  930.  
  931.     if ((win)&&(win->win)&&(win->LogListGadget)) 
  932.        GT_SetGadgetAttrs(win->LogListGadget, win->win, NULL, GTLV_Top, logListLen+1, TAG_END);
  933.  
  934.     return(TRUE);
  935. }
  936.  
  937.  
  938. void AttachClientList(struct WindowStuff * win, BOOL BAttach)
  939. {
  940.   if ((win)&&(win->win)&&(win->CommentListGadget)) 
  941.   {
  942.     GT_SetGadgetAttrs(win->CommentListGadget, win->win, NULL, GTLV_Labels, (BAttach) ? (&clientList) : ((struct List *)(~0)), TAG_END);
  943.   }
  944. }
  945.  
  946. void AttachLogList(struct WindowStuff * win, BOOL BAttach)
  947. {
  948.   if ((win)&&(win->win)&&(win->LogListGadget)) 
  949.   {
  950.     GT_SetGadgetAttrs(win->LogListGadget, win->win, NULL, GTLV_Labels, (BAttach) ? (&logList) : ((struct List *)(~0)), TAG_END);
  951.   }
  952. }
  953.  
  954. int GetNumItems(struct List * list)
  955. {
  956.   int count = 0;
  957.   struct Node * current = list->lh_Head;
  958.  
  959.   while(current->ln_Succ)
  960.   {
  961.     count++;
  962.     current = current->ln_Succ;
  963.   }
  964.   return(count);
  965. }
  966.  
  967. BOOL SendState(ULONG which)
  968. {
  969.   if (session)
  970.   {    
  971.     lastUpdateTime = time(NULL);  /* Mark now as the last time we were changed */
  972.     
  973.     /* concatenate the data items together */
  974.     if (which & STATE_REALNAME) QSetOp(session, "realname", myInfo->realName->buffer, myInfo->realName->bufLen);
  975.     if (which & STATE_USERNAME) QSetOp(session, "username", myInfo->userName->buffer, myInfo->userName->bufLen);
  976.     if (which & STATE_WINOPEN)  QSetOp(session, "winopen",  myInfo->winOpen->buffer, myInfo->winOpen->bufLen);
  977.     if (which & STATE_COMMENT)  QSetOp(session, "comment",  myInfo->comment->buffer, myInfo->comment->bufLen);
  978.  
  979.     QGo(session, 0L);
  980.   }
  981.   return(session != NULL);
  982. }
  983.  
  984. void SetCommentString(struct WindowStuff * win, char * newVal, BOOL BFinal)
  985. {                              
  986.   /* Grab and sanitize the new comment */
  987.   SetString(&myInfo->comment, PastSpaces(RemoveUnprintableChars(newVal)));
  988.   if (win) GT_SetGadgetAttrs(win->CommentString, win->win, NULL, GTST_String, myInfo->comment->buffer, TAG_END);
  989.   if (BFinal) SendState(STATE_COMMENT);
  990. }
  991.  
  992. /* Frees a malloced() and strduped() Null-terminated string vector */
  993. void FreeStringArray(char ** array)
  994. {
  995.   char ** next = array, * This;
  996.   
  997.   while(This = *next) {free(This); next++;}
  998.   free(array);
  999. }
  1000.  
  1001. void SetServerString(struct WindowStuff * win, char * newVal)
  1002. {
  1003.   char * szTemp = NULL;
  1004.                               
  1005.   /* Grab and sanitize the new server string */
  1006.   ReplaceAllocedString(&szTemp, newVal);
  1007.   ReplaceAllocedString(&szServerName, ToLower(PastSpaces(RemoveUnprintableChars(szTemp))));
  1008.   ReplaceAllocedString(&szTemp, NULL);
  1009.   if (win) GT_SetGadgetAttrs(win->ServerString, win->win, NULL, GTST_String, szServerName, TAG_END);
  1010.   SetTrackFlag(CODE_RECONNECT);
  1011. }
  1012.  
  1013. void SetPortNumber(struct WindowStuff * win, int nP)
  1014. {
  1015.   if (nP <= 0) nP = DEFAULT_AMITRACK_PORT;
  1016.   if (win) GT_SetGadgetAttrs(win->PortString, win->win, NULL, GTIN_Number, nP, TAG_END);
  1017.   SetTrackFlag(CODE_RECONNECT);
  1018.   nPort = nP;
  1019. }
  1020.  
  1021.  
  1022. void SetCurrentAction(struct WindowStuff * win, char * name)
  1023. {
  1024.   int i;
  1025.   
  1026.   for (i=0; i<nNumActions; i++)
  1027.   {
  1028.     if (strcmp(name, ActionLabels[i]) == 0)
  1029.     {
  1030.       SetCurrentActionIndex(win, i);
  1031.       return;
  1032.     }
  1033.   }
  1034. }
  1035.  
  1036. void SetCurrentActionIndex(struct WindowStuff * win, int code)
  1037. {
  1038.   currentAction = (code >= nNumActions) ? nNumActions-1 : code; 
  1039.   if ((win)&&(currentAction >= 0)) 
  1040.     GT_SetGadgetAttrs(win->ActionCycle, win->win, NULL, 
  1041.        GTCY_Active, currentAction, 
  1042.        GTCY_Labels, ActionLabels,
  1043.        TAG_END);
  1044. }
  1045.  
  1046. struct Client * FindClientByName(char * hostName)
  1047. {
  1048.   struct Client * c = (struct Client *)clientList.lh_Head;
  1049.   
  1050.   while(c->node.ln_Succ)
  1051.   {
  1052.     UNLESS(strcmp(hostName, c->hostName->buffer)) return(c);
  1053.     c = (struct Client *)c->node.ln_Succ;
  1054.   }
  1055.   return(NULL);
  1056. }
  1057.  
  1058.  
  1059. BOOL OutOfOrder(struct Client * c1, struct Client * c2)
  1060. {
  1061.   switch(nSortBy)
  1062.   {
  1063.     case O_SORTBYAGE:     return(c1->timeStamp<c2->timeStamp); 
  1064.     case O_SORTBYNAME:    return(stricmp(getDisplayIndexString(c1)->buffer,getDisplayIndexString(c2)->buffer)>0); 
  1065.     case O_SORTBYCOMMENT: return(stricmp(c1->comment->buffer,c2->comment->buffer)>0); 
  1066.     default:              return(FALSE);
  1067.   }
  1068. }
  1069.  
  1070. void SortClientList(void)
  1071. {
  1072.   int nSwappedThisPass = 1;
  1073.   struct Node *current, *past;
  1074.  
  1075.   if ((clientListLength <= 1)||(nSortBy == O_SORTBYNONE)) return;
  1076.   
  1077.   while (nSwappedThisPass > 0)
  1078.   {
  1079.     /* start a pass */
  1080.     past = clientList.lh_Head;
  1081.     current = past ? past->ln_Succ : NULL;
  1082.     nSwappedThisPass = 0;        /* start with none recorded */
  1083.  
  1084.     while ((past)&&(current)&&(current->ln_Succ))
  1085.     {
  1086.       if (OutOfOrder((struct Client *)past, (struct Client *)current))
  1087.       {
  1088.         /* Swap the two nodes */
  1089.         Remove(past);
  1090.         Insert(&clientList, past, current);
  1091.         nSwappedThisPass++;
  1092.       }
  1093.  
  1094.       past = current;
  1095.       current = current->ln_Succ;
  1096.     }
  1097.   }
  1098. }
  1099.  
  1100. struct Client * AddClient(char * hostName)
  1101. {
  1102.   struct Client * newClient;
  1103.  
  1104.   if (newClient = NewClient(hostName))
  1105.   {
  1106.     /* Append now node to the clientList */
  1107.     struct Client * next = (struct Client *)clientList.lh_Head;
  1108.     
  1109.     AddTail(&clientList, (struct Node *)newClient);
  1110.     clientListLength++;
  1111.   }
  1112.   return(newClient);
  1113. }
  1114.  
  1115. struct String * MakeNewDisplayString(struct Client * c, int nameCol)
  1116. {
  1117.   const int dateLen = sizeof("00");
  1118.   int stringLen;
  1119.   struct String * s, * iString = getDisplayIndexString(c);
  1120.   
  1121.   if (BRaggedText) nameCol = iString->bufLen;
  1122.   
  1123.   nameCol += dateLen;  /* Prepending chars for the time elapsed! */
  1124.   stringLen = nameCol + c->comment->bufLen;
  1125.   
  1126.   if (s = AllocMem(sizeof(struct String)+stringLen, MEMF_ANY))
  1127.   {    
  1128.     if (c->viewOffset >= (c->comment->bufLen-1)) c->viewOffset = c->comment->bufLen-2;
  1129.     if (c->viewOffset < 0)                       c->viewOffset = 0;
  1130.  
  1131.     time_t timeDiff = time(NULL)-(c->timeStamp);
  1132.     s->bufLen = stringLen;
  1133.     s->buffer = (char *)(&s[1]);
  1134.     memset(s->buffer, ' ', stringLen);
  1135.     
  1136.     /* Only display time if it fits! */
  1137.     if ((timeDiff/timeUnit) < 100) sprintf(s->buffer,"%2i", timeDiff/timeUnit);
  1138.                               else sprintf(s->buffer,"  ");
  1139.     s->buffer[2] = (c->winOpen->buffer[0]=='Y') ? '+' : ' ';
  1140.     strcpy(s->buffer+dateLen,iString->buffer);
  1141.     *(s->buffer+dateLen+iString->bufLen-1)=' ';
  1142.     strcpy(s->buffer+nameCol,c->comment->buffer+c->viewOffset); 
  1143.   }
  1144.   return(s);
  1145. }
  1146.  
  1147. /* Go through displayed items, reprinting the current elapsed time in each.  Reset timer when done! */
  1148. void UpdateWindowTimes(void)
  1149. {
  1150.   struct Client * next;
  1151.   time_t currentTime = time(NULL);
  1152.   
  1153.   AttachClientList(TrackWindow, FALSE);
  1154.   
  1155.   next = (struct Client *)clientList.lh_Head;
  1156.   while(next->node.ln_Succ)
  1157.   {
  1158.     char * buf = next->listString->buffer;
  1159.     
  1160.     if ((buf)&&(buf[0])&&(buf[1])&&(buf[2])&&(buf[3]))
  1161.     {
  1162.       time_t timeDiff = currentTime - next->timeStamp;
  1163.       
  1164.       if ((timeDiff/timeUnit) < 100) sprintf(buf,"%2i", timeDiff/timeUnit);
  1165.                                 else sprintf(buf,"  ");
  1166.       buf[2] = (next->winOpen->buffer[0]=='Y') ? '+' : ' ';
  1167.     }
  1168.     next = (struct Client *)next->node.ln_Succ;
  1169.   }
  1170.   
  1171.   SortClientList();
  1172.   AttachClientList(TrackWindow, TRUE);
  1173. }
  1174.  
  1175. BOOL UpdateClientTime(char * path, ULONG secs)
  1176. {
  1177.   char * slash;
  1178.   
  1179.   path++; 
  1180.   if (slash = strchr(path, '/')) 
  1181.   {
  1182.     struct Client * client;
  1183.     
  1184.     *slash = '\0';
  1185.     if (client = FindClientByName(path))
  1186.     {
  1187.       client->timeStamp = time(NULL)-secs;
  1188.       return(TRUE);
  1189.     }
  1190.   }
  1191.   return(FALSE);
  1192. }
  1193.  
  1194. void UpdateClientField(struct Client * modme, char * fieldName, char * newVal)
  1195. {        
  1196.   struct String ** stringToChange;
  1197.   BOOL BLogIt = TRUE;
  1198.   
  1199.   if (stringToChange = getField(modme, fieldName))
  1200.   {
  1201.     /* If the new string is the same as the old, no need to update anything! */
  1202.     BLogIt = !((*stringToChange)&&((*stringToChange)->buffer)&&
  1203.                (newVal)&&(strcmp((*stringToChange)->buffer, newVal) == 0));
  1204.  
  1205.     SetString(stringToChange, newVal);
  1206.     SetString(&modme->listString, "");  /* force update */
  1207.   }
  1208.   modme->timeStamp  = time(NULL);
  1209.   modme->viewOffset = 0;
  1210.  
  1211.   if ((modme->firstTime)&&(GetFieldIDByName(fieldName)==O_COMMENT))
  1212.   {
  1213.     if (BLogIt) RecordEvent(LOG_LOGON, modme, fieldName, NULL, NULL);
  1214.     modme->firstTime=FALSE;
  1215.   }
  1216.   else if (BLogIt) RecordEvent(LOG_UPDATE, modme, fieldName, NULL, NULL);
  1217. }
  1218.  
  1219. void UpdateClientList(struct QMessage * qmsg)
  1220. {
  1221.   /* Parse path */
  1222.   if (qmsg)
  1223.   {
  1224.     char * path = qmsg->qm_Path;
  1225.     char * data = qmsg->qm_Data;
  1226.     char * slash1=NULL, * slash2=NULL;
  1227.     char * hostName, * field;
  1228.     struct Client * modme;
  1229.     
  1230.     UNLESS((path)&&(slash1 = strchr(path+1,'/'))&&(slash2 = strchr(slash1+1,'/'))) return;
  1231.     hostName = path+1; field = slash2+1;
  1232.     *slash1 = *slash2 = '\0';  /* terminate parsed fields */
  1233.  
  1234.     /* Prepare to modify the ListView */
  1235.     AttachClientList(TrackWindow, FALSE);
  1236.  
  1237.     if (data)
  1238.     {
  1239.       UNLESS(modme = FindClientByName(hostName))
  1240.       {
  1241.         ULONG secsSince = time(NULL)-lastUpdateTime;
  1242.  
  1243.         /* If he's new, send him a message so he knows how long since we've been updated */
  1244.         *slash1 = '/';  /* re-concatenate the /hostName/QAmiTrack part */
  1245.         (void)QMessageOp(session, path, &secsSince, sizeof(ULONG));
  1246.         (void)QGo(session, 0L);
  1247.         *slash1 = '\0';
  1248.  
  1249.         modme = AddClient(hostName);
  1250.       }
  1251.       if (modme) UpdateClientField(modme, slash2+1, data);
  1252.     }
  1253.     else 
  1254.     {
  1255.       /* NULL data means he's logging out! */
  1256.       if (modme = FindClientByName(hostName))
  1257.       {
  1258.         RecordEvent(LOG_LOGOFF, modme, NULL, NULL, NULL);
  1259.         Remove((struct Node *)modme);
  1260.         clientListLength--;
  1261.         FreeClient(modme);
  1262.       }
  1263.     }
  1264.   }
  1265.  
  1266.   {
  1267.     int maxNameLen=0;
  1268.     BOOL BReformat = FALSE;
  1269.     struct Client * next;    
  1270.  
  1271.     AttachClientList(TrackWindow, FALSE);
  1272.     
  1273.     /* Recalculate the display strings */
  1274.     next = (struct Client *)clientList.lh_Head;
  1275.     while(next->node.ln_Succ)
  1276.     {
  1277.       int len = getDisplayIndexString(next)->bufLen;
  1278.       if (maxNameLen < len) maxNameLen = len;
  1279.       next = next->node.ln_Succ;
  1280.     }
  1281.     BReformat = (maxNameLen != currentNameLen);
  1282.     currentNameLen = maxNameLen;
  1283.  
  1284.     /* Now regenerate all listStrings */
  1285.     next = (struct Client *)clientList.lh_Head;
  1286.     while(next->node.ln_Succ)
  1287.     {
  1288.       struct String * oldString = next->listString;
  1289.   
  1290.       if ((BReformat)||(*(oldString->buffer)=='\0'))
  1291.       {
  1292.         struct String * newString;
  1293.       
  1294.         if (newString = MakeNewDisplayString(next, maxNameLen))
  1295.         { 
  1296.           if (oldString) FreeString(oldString);
  1297.           next->listString = newString;
  1298.           next->node.ln_Name = next->listString->buffer;
  1299.         }
  1300.       }
  1301.       next = (struct Client *)next->node.ln_Succ;
  1302.     }
  1303.     
  1304.     SortClientList();
  1305.   }
  1306.   AttachClientList(TrackWindow, TRUE);
  1307. }
  1308.  
  1309.  
  1310. void ScrollClient(struct Client * c, int scrollBy)
  1311. {
  1312.   struct String * newString;
  1313.  
  1314.   if (newString = NewString(""))
  1315.   {
  1316.     c->viewOffset += scrollBy;
  1317.     if (c->viewOffset < 0) c->viewOffset = 0;
  1318.     
  1319.     AttachClientList(TrackWindow, FALSE);  /* avoid dangling pointer */
  1320.     
  1321.     /* Put the client in a state where it will be updated! */
  1322.     FreeString(c->listString);
  1323.     c->listString = newString;
  1324.  
  1325.     UpdateClientList(NULL);  /* list will be reattached to window here */
  1326.   }    
  1327. }
  1328.  
  1329.  
  1330. void SetViewBy(ULONG ulItemCode)
  1331. {                            
  1332.   if (nViewBy != ulItemCode) 
  1333.   {
  1334.     nViewBy = ulItemCode;
  1335.     currentNameLen = -1;  /* Force redraw of whole list */
  1336.     UpdateClientList(NULL); 
  1337.   }
  1338. }
  1339.  
  1340. void SetSortBy(ULONG ulItemCode)
  1341. {                            
  1342.   if (nSortBy != ulItemCode) 
  1343.   {
  1344.     nSortBy = ulItemCode;
  1345.     currentNameLen = -1;  /* Force redraw of whole list */
  1346.     UpdateClientList(NULL); 
  1347.   }
  1348. }
  1349.  
  1350. BOOL SetLogViewLevel(ULONG ulItemCode)
  1351. {                            
  1352.   if (nLogViewLevel != ulItemCode) 
  1353.   {
  1354.     nLogViewLevel = ulItemCode;
  1355.     return(TRUE);
  1356.   }
  1357.   return(FALSE);
  1358. }
  1359.  
  1360. /* Returns any additional action flags that we want set */
  1361. void HandleIDCMP(struct WindowStuff * win)
  1362. {
  1363.     struct IntuiMessage *message;
  1364.     ULONG class, code, qual, ulItemCode, id;
  1365.     struct Gadget * gad;
  1366.     struct MenuItem * mItem;
  1367.     
  1368.     if (win == NULL) SetTrackFlag(CODE_QUIT);
  1369.  
  1370.     /* Examine pending messages */    
  1371.     while (message = (struct IntuiMessage *)GT_GetIMsg(win->win->UserPort))
  1372.     {
  1373.         class = message->Class;        /* extract needed info from message */
  1374.         code  = message->Code;
  1375.         qual  = message->Qualifier;
  1376.         gad   = (struct Gadget *) (message->IAddress);
  1377.  
  1378.         /* tell Intuition we got the message */
  1379.         GT_ReplyIMsg(message);
  1380.  
  1381.         /* see what events occured, take correct action */
  1382.         switch(class)
  1383.         {        
  1384.             case IDCMP_CLOSEWINDOW: 
  1385.                 SetTrackFlag(CODE_HIDE);
  1386.                 break;
  1387.  
  1388.             case IDCMP_NEWSIZE:
  1389.                 {
  1390.                   char temp[50];
  1391.                   UpdateWindow(win,FALSE);
  1392.                   sprintf(temp, "Resize: X=%i, Y=%i, W=%i, H=%i",
  1393.                     win->win->LeftEdge,  win->win->TopEdge,
  1394.                     win->win->Width,     win->win->Height);
  1395.                   StatMessage(temp);
  1396.                 }
  1397.                 break;
  1398.           
  1399.             case IDCMP_INTUITICKS:
  1400.                 break;
  1401.                   
  1402.             case IDCMP_MOUSEBUTTONS:
  1403.                 break;
  1404.                       
  1405.             case IDCMP_VANILLAKEY: 
  1406.                 { 
  1407.                   char buf[2];
  1408.                   
  1409.                   buf[0] = code; buf[1] = '\0';
  1410.                   SetCommentString(win, buf, FALSE);
  1411.                   ((struct StringInfo*)win->CommentString->SpecialInfo)->BufferPos = 1;  /* hack the cursor over to the right, dammit! */
  1412.                   ActivateGadget(win->CommentString, win->win, NULL);
  1413.                 }
  1414.                 break;
  1415.             
  1416.             case IDCMP_RAWKEY:
  1417.                 if (cLastEntryClicked)
  1418.                 {
  1419.                   switch(code)
  1420.                   {
  1421.                     case 'L': ScrollClient(cLastEntryClicked, -5000); break;
  1422.                     case 'M': ScrollClient(cLastEntryClicked,  10); break;
  1423.                     case 'N': ScrollClient(cLastEntryClicked,  1); break;
  1424.                     case 'O': ScrollClient(cLastEntryClicked, -1); break;
  1425.                   }
  1426.                 }
  1427.                 break;
  1428.                 
  1429.             case IDCMP_GADGETUP:
  1430.                 switch(gad ? gad->GadgetID : -1)
  1431.                 {
  1432.                    case GAD_CLIENTLIST: ListClicked(code); break;
  1433.                    case GAD_COMMENTSTRING: SetCommentString(TrackWindow, ((struct StringInfo*)win->CommentString->SpecialInfo)->Buffer, TRUE); break;
  1434.                    case GAD_SERVERSTRING: SetServerString(TrackWindow, ((struct StringInfo*)win->ServerString->SpecialInfo)->Buffer); break;
  1435.                    case GAD_PORTSTRING: SetPortNumber(TrackWindow, ((struct StringInfo*)win->PortString->SpecialInfo)->LongInt); break;
  1436.                    case GAD_ACTIONCYCLE: SetCurrentActionIndex(TrackWindow,code); break;
  1437.                    default: printf("HandleICMP:  unknown gadget ID %i\n", id); break;
  1438.                 }
  1439.                 break;
  1440.  
  1441.             case IDCMP_MENUPICK:
  1442.                 {
  1443.                   BOOL BUpdateWin = FALSE;
  1444.                   
  1445.                   while(code != MENUNULL) 
  1446.                   {
  1447.                     char szMessage[150];
  1448.                     mItem = ItemAddress( Menu, code );
  1449.  
  1450.                     ulItemCode = (ULONG) GTMENUITEM_USERDATA(mItem);
  1451.                     switch(ulItemCode)
  1452.                     {
  1453.                         case P_HIDE:
  1454.                             SetTrackFlag(CODE_HIDE);
  1455.                             break;
  1456.                             
  1457.                         case P_ABOUT:        
  1458.                             sprintf(szMessage,"%s\nby Jeremy Friesner\njfriesne@ucsd.edu\nCompiled: %s",
  1459.                                 pcDisplayVersionString,__DATE__);
  1460.                             MakeReq(NULL,szMessage,"Not Bad");
  1461.                             break;
  1462.                             
  1463.                         case P_QUIT:
  1464.                             SetTrackFlag(CODE_QUIT);
  1465.                             break;
  1466.  
  1467.                         case O_CONFIRM:
  1468.                             BConfirmAppLaunch = BConfirmAppLaunch ? FALSE : TRUE;
  1469.                             break;
  1470.                             
  1471.                         case O_RAGGED:
  1472.                             BRaggedText = !BRaggedText;
  1473.                             currentNameLen = -1;  /* Force re-creation of display list */
  1474.                             UpdateClientList(NULL);
  1475.                             break;
  1476.  
  1477.                         case O_BEEPONLOG:
  1478.                             BBeepOnLog = !BBeepOnLog;
  1479.                             break;
  1480.                             
  1481.                         case O_ENABLED:
  1482.                             SetTrackFlag(BEnabled ? CODE_DISABLE : CODE_ENABLE);
  1483.                             break;
  1484.                             
  1485.                         case O_LOGNONE:     nLogLevel = OUTPUT_NONE;     break;
  1486.                         case O_LOGTERSE:    nLogLevel = OUTPUT_TERSE;    break;
  1487.                         case O_LOGMODERATE: nLogLevel = OUTPUT_MODERATE; break;
  1488.                         case O_LOGVERBOSE:  nLogLevel = OUTPUT_VERBOSE;  break;
  1489.                         
  1490.                         case O_VIEWUSERNAME: case O_VIEWHOSTNAME: case O_VIEWREALNAME:
  1491.                             SetViewBy(ulItemCode);
  1492.                             break;
  1493.                             
  1494.                         case O_SORTBYAGE: case O_SORTBYNAME: case O_SORTBYCOMMENT: case O_SORTBYNONE:
  1495.                             SetSortBy(ulItemCode);
  1496.                             break;
  1497.                             
  1498.                         case O_LOGVIEWOFF: case O_LOGVIEW25: case O_LOGVIEW50: case O_LOGVIEW75: case O_LOGVIEWFULL:
  1499.                             BUpdateWin = (ulItemCode != nLogViewLevel);
  1500.                             SetLogViewLevel(ulItemCode);
  1501.                             break;
  1502.                         
  1503.                         case O_CLEARLOGVIEW:
  1504.                             ClearLogView();
  1505.                             break;
  1506.  
  1507.                         case O_REVERTCOMMENT:
  1508.                             SetCommentString(TrackWindow, origComment, TRUE);
  1509.                             break;
  1510.                     }
  1511.                     code = mItem->NextSelect;
  1512.                   }
  1513.                   
  1514.                   if (BUpdateWin) UpdateWindow(win, FALSE);
  1515.                 }
  1516.                 break;
  1517.             
  1518.             case IDCMP_REFRESHWINDOW:
  1519.                 GT_BeginRefresh(win->win);
  1520.                 GT_EndRefresh(win->win, TRUE);
  1521.                 nWinWidth  = win->win->Width;
  1522.                 nWinHeight = win->win->Height;
  1523.                 nWinLeft   = win->win->LeftEdge;
  1524.                 nWinTop    = win->win->TopEdge;
  1525.                 break;
  1526.                        
  1527.             default:        
  1528.                 printf("handleIDCMP: bad class %lu\n",class);
  1529.                 break;
  1530.         }
  1531.     }
  1532. }
  1533.  
  1534. struct CxStuff * SetupCxStuff(struct CxStuff * cx, int pri, char * hotkey)
  1535. {
  1536.     if (cx)
  1537.     {
  1538.        if (cx->name) FreeMem(cx->name, strlen(cx->name)+1);
  1539.        if (cx->broker) DeleteCxObjAll(cx->broker);
  1540.        if (cx->port)
  1541.        {
  1542.          /* deallocate cx */
  1543.          CxMsg * msg;
  1544.        
  1545.          while(msg = (CxMsg *)GetMsg(cx->port)) ReplyMsg((struct Message *)msg);
  1546.          DeletePort(cx->port);
  1547.        }
  1548.        FreeMem(cx, sizeof(struct CxStuff));  
  1549.        return(NULL);
  1550.     }
  1551.     else
  1552.     {
  1553.        char name[200];
  1554.        CxObj * filter;
  1555.        
  1556.        /* allocate and return a new cx */
  1557.        UNLESS(cx = AllocMem(sizeof(struct CxStuff), MEMF_CLEAR)) return(NULL);
  1558.        UNLESS(cx->port = CreateMsgPort()) return(SetupCxStuff(cx,0,NULL));
  1559.  
  1560.        if (BAllowMultiple) sprintf(name,"QAmiTrack %lu",time(NULL));
  1561.                       else strcpy(name,"QAmiTrack");
  1562.                       
  1563.        UNLESS(cx->name = AllocMem(strlen(name)+1, MEMF_ANY)) return(SetupCxStuff(cx,0,NULL));
  1564.        strcpy(cx->name, name);
  1565.        
  1566.        cx->nb.nb_Version = NB_VERSION;
  1567.        cx->nb.nb_Name    = cx->name;
  1568.        cx->nb.nb_Title   = pcDisplayVersionString;
  1569.        cx->nb.nb_Descr   = "Lists other Amigas on the 'net";
  1570.        cx->nb.nb_Unique  = NBU_UNIQUE | NBU_NOTIFY;   
  1571.        cx->nb.nb_Flags   = COF_SHOW_HIDE;  /* We'll have a window available */
  1572.        cx->nb.nb_Pri     = pri;
  1573.        cx->nb.nb_Port    = cx->port;
  1574.        cx->nb.nb_ReservedChannel = 0;
  1575.     
  1576.        UNLESS(cx->broker = CxBroker(&cx->nb, NULL)) return(SetupCxStuff(cx,0,NULL));
  1577.        if ((hotkey)&&(filter=CxFilter(hotkey))) 
  1578.        {
  1579.          CxObj *sender;
  1580.          
  1581.          AttachCxObj(cx->broker, filter);
  1582.          if (sender = CxSender(cx->port, EVT_HOTKEY)) 
  1583.          {
  1584.            CxObj *translate;
  1585.            
  1586.            AttachCxObj(filter,sender);
  1587.            if (translate = CxTranslate(NULL)) AttachCxObj(filter, translate);
  1588.          }
  1589.        }
  1590.        
  1591.        ActivateCxObj(cx->broker, 1L); /* Makes us "active" -- receiving messages */
  1592.        if (BEnabled == FALSE) ActivateCxObj(cx->broker, 0L);  /* Now disable it */
  1593.        return(cx);    
  1594.     }
  1595. }
  1596.  
  1597. struct WindowStuff * SetupTrackWindow(struct WindowStuff * win)
  1598. {
  1599.  if (win)
  1600.  {
  1601.    /* Free the window */
  1602.    UpdateWindow(win,TRUE);    /* Free all the gadgets */
  1603.    if (win->fontdata)         CloseFont(win->fontdata);
  1604.    if (win->fixedfontdata)    CloseFont(win->fixedfontdata);
  1605.    if (win->win)            
  1606.    {
  1607.      nWinTop  = win->win->TopEdge;
  1608.      nWinLeft = win->win->LeftEdge;
  1609.      CloseWindow(win->win);
  1610.    }
  1611.    if (win->screen)         UnlockPubScreen(NULL,win->screen);
  1612.         
  1613.    FreeMem(win,sizeof(struct WindowStuff));
  1614.    return(NULL);
  1615.  }
  1616.  else
  1617.  {
  1618.    UNLESS(win = AllocMem(sizeof(struct WindowStuff), MEMF_CLEAR)) return(NULL);
  1619.         
  1620.    /* Find the default public screen */
  1621.    UNLESS(win->screen = LockPubScreen(NULL)) return(SetupTrackWindow(win));
  1622.  
  1623.    AskFont(&win->screen->RastPort, &win->font);
  1624.    UNLESS(win->fontdata = OpenDiskFont(&win->font)) return(SetupTrackWindow(win));
  1625.  
  1626.    nMinWinHeight = win->screen->WBorTop + win->screen->RastPort.TxHeight + VSPACE + 3*(win->font.ta_YSize + VSPACE + VSPACE) + win->screen->WBorBottom + VSPACE;
  1627.    nMinWinWidth  = win->screen->WBorLeft + (HSPACE * 25) + win->screen->WBorRight;
  1628.         
  1629.    if (nWinWidth  < nMinWinWidth)  nWinWidth = nMinWinWidth;
  1630.    if (nWinHeight < nMinWinHeight) nWinHeight= nMinWinHeight;
  1631.         
  1632.    /* Open the window */
  1633.    UNLESS(win->win = OpenWindowTags(NULL,
  1634.     WA_Left,        nWinLeft,
  1635.     WA_Top,            nWinTop,
  1636.     WA_Width,        nWinWidth,
  1637.     WA_Height,        nWinHeight,
  1638.     WA_MinWidth,    nMinWinWidth,
  1639.     WA_MinHeight,    nMinWinHeight,
  1640.     WA_PubScreen,    win->screen,
  1641.     WA_PubScreenFallBack, TRUE,
  1642.     WA_MaxWidth,    -1,
  1643.     WA_MaxHeight,    -1,
  1644.     WA_Title,        pcDisplayVersionString, 
  1645.     WA_CloseGadget, TRUE,
  1646.     WA_DepthGadget, TRUE,
  1647.     WA_SizeGadget,  TRUE,
  1648.     WA_Activate,    TRUE,
  1649.     WA_DragBar,        TRUE,
  1650.     WA_SizeBBottom, TRUE,
  1651.     WA_Flags,       WFLG_NEWLOOKMENUS,
  1652.     WA_IDCMP,       IDCMP_REFRESHWINDOW | IDCMP_CLOSEWINDOW | 
  1653.                     IDCMP_MENUPICK | IDCMP_NEWSIZE | IDCMP_VANILLAKEY | 
  1654.                      IDCMP_RAWKEY | BUTTONIDCMP | LISTVIEWIDCMP |
  1655.                      CYCLEIDCMP | STRINGIDCMP,
  1656.     TAG_DONE)) return(SetupTrackWindow(win));
  1657.  
  1658.    AskFont(win->win->RPort, &win->fixedfont);
  1659.    UNLESS(win->fixedfontdata = OpenDiskFont(&win->fixedfont)) return(SetupTrackWindow(win));
  1660.    UNLESS(UpdateWindow(win,FALSE)) return(SetupTrackWindow(win));
  1661.    return(win);
  1662.    }
  1663. }
  1664.  
  1665. void debug(int n)
  1666. {
  1667.     printf("enter debug %i ... ",n); fflush(stdout);
  1668.     Delay(50);
  1669.     printf("continuing.\n"); fflush(stdout);
  1670. }
  1671.  
  1672. VOID wbmain(struct WBStartup *wbargv)
  1673. {    
  1674.     BStartedFromWB = TRUE;
  1675.     main(0,wbargv);
  1676. }
  1677.  
  1678. /* return TRUE iff the string represents the boolean value true */
  1679. BOOL ParseBool(char * string)
  1680. {
  1681.   char temp[100];
  1682.   
  1683.   strncpy(temp,string,sizeof(temp));
  1684.   temp[99]=0;
  1685.   
  1686.   ToLower(temp);
  1687.   
  1688.   return(
  1689.     (*temp=='\0')              ||  /* then it's just KEYWORD, so yes, right? */
  1690.     (strcmp("true",temp) == 0) ||
  1691.     (strcmp("yes",temp)  == 0) ||
  1692.     (strcmp("1",temp)    == 0));   /* Anyone who specifies 1 for true is a weenie! */
  1693. }
  1694.  
  1695.  
  1696. int ParseOutputLevel(char * keyword)
  1697. {
  1698.   ToLower(keyword);
  1699.   UNLESS(strcmp(keyword, "terse"))    return OUTPUT_TERSE;
  1700.   UNLESS(strcmp(keyword, "moderate")) return OUTPUT_MODERATE;
  1701.   UNLESS(strcmp(keyword, "verbose"))  return OUTPUT_VERBOSE;  
  1702.   return OUTPUT_NONE;  /* default */
  1703. }
  1704.  
  1705. int ParseLogViewLevel(char * keyword)
  1706. {
  1707.   ToLower(keyword);
  1708.   UNLESS(strcmp(keyword,  "off"))     return O_LOGVIEWOFF;
  1709.   UNLESS(strncmp(keyword, "0",  1))   return O_LOGVIEWOFF;
  1710.   UNLESS(strncmp(keyword, "25", 2))   return O_LOGVIEW25;
  1711.   UNLESS(strncmp(keyword, "50", 2))   return O_LOGVIEW50;
  1712.   UNLESS(strncmp(keyword, "75", 2))   return O_LOGVIEW75;
  1713.   UNLESS(strncmp(keyword, "100",3))   return O_LOGVIEWFULL;
  1714.   UNLESS(strcmp(keyword, "full"))     return O_LOGVIEWFULL;  
  1715.   return O_LOGVIEWOFF;  /* default */
  1716. }
  1717.  
  1718. int ParseViewBy(char * keyword)
  1719. {
  1720.   ToLower(keyword);
  1721.  
  1722.   UNLESS(strcmp(keyword, "username")) return O_VIEWUSERNAME;  
  1723.   UNLESS(strcmp(keyword, "realname")) return O_VIEWREALNAME;
  1724.   return O_VIEWHOSTNAME;  /* default */
  1725. }
  1726.  
  1727. int ParseSortBy(char * keyword)
  1728. {
  1729.   ToLower(keyword);
  1730.  
  1731.   UNLESS(strcmp(keyword, "age"))     return O_SORTBYAGE;  
  1732.   UNLESS(strcmp(keyword, "name"))    return O_SORTBYNAME;
  1733.   UNLESS(strcmp(keyword, "comment")) return O_SORTBYCOMMENT;
  1734.   UNLESS(strcmp(keyword, "none"))    return O_SORTBYNONE;
  1735.   
  1736.   return O_SORTBYAGE;  /* default */
  1737. }
  1738.  
  1739. char * GetViewByName(int k)
  1740. {
  1741.   switch(k)
  1742.   {
  1743.     case O_VIEWUSERNAME: return("username");
  1744.     case O_VIEWHOSTNAME: return("hostname");
  1745.     case O_VIEWREALNAME: return("realname");
  1746.     default: return("error");
  1747.   }
  1748. }
  1749.  
  1750. char * GetSortByName(int k)
  1751. {
  1752.   switch(k)
  1753.   {
  1754.     case O_SORTBYAGE:     return("age");
  1755.     case O_SORTBYNAME:    return("name");
  1756.     case O_SORTBYCOMMENT: return("comment");
  1757.     case O_SORTBYNONE:    return("nothing");
  1758.     default: return("error");
  1759.   }
  1760. }
  1761.  
  1762. char * GetOutputLevelName(int id)
  1763. {
  1764.   switch(id)
  1765.   {
  1766.     case OUTPUT_NONE:     return("none");
  1767.     case OUTPUT_TERSE:    return("terse");
  1768.     case OUTPUT_MODERATE: return("moderate");
  1769.     case OUTPUT_VERBOSE:  return("verbose");
  1770.     default:              return("error");
  1771.   }
  1772. }
  1773.  
  1774. void SetLogViewLength(int numLines)
  1775. {
  1776.   if (numLines >= 0)
  1777.   {
  1778.     AttachLogList(TrackWindow, FALSE);
  1779.     while(logListLen > numLines)
  1780.     {
  1781.       struct Node * top = RemHead(&logList);
  1782.       if (top) 
  1783.       {
  1784.         FreeMem(top,sizeof(struct Node)+strlen(top->ln_Name)+1);
  1785.         logListLen--;
  1786.       }
  1787.     }
  1788.     AttachLogList(TrackWindow, TRUE);
  1789.     maxLogListLen = numLines;
  1790.   }
  1791. }
  1792.  
  1793. void SetRexxScript(int which, char * name)
  1794. {
  1795.   if ((which >= 0)&&(which < NUM_LOG_ENUMS))
  1796.   {
  1797.     char * Old = rexxScripts[which];
  1798.     char * New = strdup(name);
  1799.     
  1800.     if (New)
  1801.     {
  1802.       rexxScripts[which] = New;
  1803.       if (Old) free(Old);
  1804.     }
  1805.   }
  1806. }
  1807.  
  1808. void FreeClientList(void)
  1809. {
  1810.   struct Client * next;
  1811.   
  1812.   while(next = (struct Client *) RemHead(&clientList)) FreeClient(next);
  1813.   clientListLength = 0;
  1814. }
  1815.  
  1816. /* Always called when AmiTrack exits, does any necessary cleanup */
  1817. void Cleanup(void)
  1818. {
  1819.     int i;
  1820.     
  1821.     RecordEvent(LOG_END, NULL, NULL, NULL, NULL);
  1822.     
  1823.     ClearLogView();
  1824.     
  1825.     for (i=0; i<NUM_LOG_ENUMS; i++) if (rexxScripts[i]) free(rexxScripts[i]);
  1826.     
  1827.     if (szLogFileName) free(szLogFileName);
  1828.     if (TS)          SetupTimer(TS);
  1829.     if (rexxHost)    CloseDownARexxHost(rexxHost);
  1830.     if (CX)          SetupCxStuff(CX,0,NULL);
  1831.     if (ttypes)      ArgArrayDone();    
  1832.     if (TrackWindow) SetupTrackWindow(TrackWindow);
  1833.     if (session)     QFreeSession(session);
  1834.     if (myInfo)      FreeClient(myInfo);
  1835.     
  1836.     FreeActions();
  1837.     FreeClientList();
  1838.     ReplaceAllocedString(&szServerName, NULL);
  1839.  
  1840.     if (AMarqueeBase)  CloseLibrary(AMarqueeBase);
  1841.     if (DiskFontBase)  CloseLibrary(DiskFontBase);
  1842.     if (GadToolsBase)  CloseLibrary(GadToolsBase);
  1843.     if (IntuitionBase) CloseLibrary(IntuitionBase);
  1844.     if (GraphicsBase)  CloseLibrary(GraphicsBase);
  1845.     if (WorkbenchBase) CloseLibrary(WorkbenchBase);
  1846.     if (IconBase)      CloseLibrary(IconBase);
  1847.     if (CxBase)        CloseLibrary(CxBase);    
  1848. }
  1849.  
  1850. void Disconnect(char * optStat)
  1851. {
  1852.   if (session)
  1853.   {
  1854.     QFreeSession(session);
  1855.     session = NULL;
  1856.     RecordEvent(LOG_DISCONNECT, NULL, NULL, optStat, NULL);
  1857.     BConnected = FALSE;
  1858.   }
  1859.   if (optStat) StatMessage(optStat);
  1860.  
  1861.   /* Clear the client list! */
  1862.   AttachClientList(TrackWindow, FALSE);
  1863.   FreeClientList();
  1864.   AttachClientList(TrackWindow, TRUE);
  1865. }
  1866.  
  1867. void Reconnect(void)
  1868. {
  1869.   char szMessage[400];            
  1870.   char szInterest[] = "/#?/QAmiTrack/(comment|username|realname|winopen)";
  1871.   int maxFieldLen = 1024;
  1872.   
  1873.   UNLESS(BEnabled) return;
  1874.   
  1875.   Disconnect(NULL);
  1876.   UNLESS(*szServerName)
  1877.   {
  1878.     StatMessage("Can't connect, no server name given.");
  1879.     return;
  1880.   }
  1881.   
  1882.   if (session = QNewSessionAsync(szServerName, nPort, "QAmiTrack"))
  1883.   { 
  1884.     sprintf(szMessage,"Connecting to [%s:%i]",szServerName,nPort);
  1885.     StatMessage(szMessage);
  1886.     
  1887.     /* these won't get processed until the connection is ready! */
  1888.     sprintf(szMessage,"/%s/QAmiTrack", szAccessTo);
  1889.     UNLESS((QSetMessageAccessOp(session, "/#?/QAmiTrack", sizeof(ULONG))) &&
  1890.            (QSetAccessOp(session, szMessage)) &&
  1891.            (QRequestPrivilegesOp(session, QPRIV_GETSYSMESSAGES)) &&
  1892.            (QGetOp(session, szInterest, maxFieldLen)) &&
  1893.            (QSubscribeOp(session, szInterest, maxFieldLen)) &&
  1894.            (SendState(STATE_ALL)) &&
  1895.            (QGo(session, 0L)))
  1896.     {
  1897.       sprintf(szMessage, "Couldn't log in to server [%s:%i]",szServerName,nPort);
  1898.       Disconnect(szMessage);
  1899.     }
  1900.   }
  1901.   else
  1902.   {                
  1903.     if (szServerName)
  1904.     {
  1905.       sprintf(szMessage,"Couldn't connect to %s:%i",szServerName, nPort);
  1906.       StatMessage(szMessage);
  1907.     }
  1908.   }
  1909. }
  1910.  
  1911.  
  1912. void StatMessage(char * message)
  1913. {
  1914.     static char szMessage[100] = "AmiTrack ready.";
  1915.     
  1916.     if (message) strncpy(szMessage,message,sizeof(szMessage));
  1917.     szMessage[99] = '\0';
  1918.         
  1919.     if ((TrackWindow)&&(TrackWindow->win)) 
  1920.       SetWindowTitles(TrackWindow->win, szMessage, (char *)~0);
  1921. }
  1922.  
  1923. #define UPDATEMENUFLAG(item, variable, flag) {if (variable) item->Flags |= flag; else item->Flags &= ~(flag);}
  1924. void SetMenuValues(void)
  1925. {
  1926.  struct Menu *currentMenu = Menu;  /* Project Menu */
  1927.  struct MenuItem *currentItem, *currentSub;
  1928.  
  1929.  UNLESS(TrackWindow) return;
  1930.  ClearMenuStrip(TrackWindow->win);
  1931.  
  1932.  currentMenu = currentMenu->NextMenu;  /* Options menu */
  1933.  
  1934.  currentItem = currentMenu->FirstItem;    /* Enabled */
  1935.  UPDATEMENUFLAG(currentItem, BEnabled, CHECKED);
  1936.           
  1937.  currentItem = currentItem->NextItem;   /* Confirm */
  1938.  UPDATEMENUFLAG(currentItem, BConfirmAppLaunch, CHECKED);
  1939.  UPDATEMENUFLAG(currentItem, Actions[0],        ITEMENABLED);
  1940.                          
  1941.  currentItem = currentItem->NextItem;   /* Ragged Text */
  1942.  UPDATEMENUFLAG(currentItem, BRaggedText, CHECKED);
  1943.  
  1944.  currentItem = currentItem->NextItem;   /* Beep On Log */
  1945.  UPDATEMENUFLAG(currentItem, BBeepOnLog, CHECKED);
  1946.  
  1947.  currentItem = currentItem->NextItem;   /* Log Events */
  1948.  
  1949.    currentSub = currentItem->SubItem;  /* Log None  */
  1950.    UPDATEMENUFLAG(currentSub, nLogLevel==OUTPUT_NONE, CHECKED);
  1951.  
  1952.    currentSub = currentSub->NextItem;  /* Log Terse */
  1953.    UPDATEMENUFLAG(currentSub, nLogLevel==OUTPUT_TERSE, CHECKED);
  1954.  
  1955.    currentSub = currentSub->NextItem;  /* Log Moderate */
  1956.    UPDATEMENUFLAG(currentSub, nLogLevel==OUTPUT_MODERATE, CHECKED);
  1957.  
  1958.    currentSub = currentSub->NextItem;  /* Log Verbose */
  1959.    UPDATEMENUFLAG(currentSub, nLogLevel==OUTPUT_VERBOSE, CHECKED);
  1960.  
  1961.  currentItem = currentItem->NextItem;  /* View By... */
  1962.  
  1963.    currentSub  = currentItem->SubItem;   /* Host Name */
  1964.    UPDATEMENUFLAG(currentSub, (nViewBy==O_VIEWHOSTNAME), CHECKED);
  1965.  
  1966.    currentSub  = currentSub->NextItem;   /* User Name */
  1967.    UPDATEMENUFLAG(currentSub, (nViewBy==O_VIEWUSERNAME), CHECKED);
  1968.  
  1969.    currentSub  = currentSub->NextItem;   /* Real Name */
  1970.    UPDATEMENUFLAG(currentSub, (nViewBy==O_VIEWREALNAME), CHECKED);
  1971.  
  1972.  currentItem = currentItem->NextItem;   /* Sort By... */
  1973.  
  1974.    currentSub  = currentItem->SubItem;   /* Age */
  1975.    UPDATEMENUFLAG(currentSub, (nSortBy==O_SORTBYAGE), CHECKED);
  1976.    
  1977.    currentSub  = currentSub->NextItem;   /* Name */
  1978.    UPDATEMENUFLAG(currentSub, (nSortBy==O_SORTBYNAME), CHECKED);
  1979.    
  1980.    currentSub  = currentSub->NextItem;   /* Comment */
  1981.    UPDATEMENUFLAG(currentSub, (nSortBy==O_SORTBYCOMMENT), CHECKED);
  1982.    
  1983.    currentSub  = currentSub->NextItem;   /* None */
  1984.    UPDATEMENUFLAG(currentSub, (nSortBy==O_SORTBYNONE), CHECKED);
  1985.  
  1986.  currentItem = currentItem->NextItem;   /* Log View... */
  1987.  
  1988.    currentSub  = currentItem->SubItem;   /* Off */
  1989.    UPDATEMENUFLAG(currentSub, (nLogViewLevel==O_LOGVIEWOFF), CHECKED);
  1990.    
  1991.    currentSub  = currentSub->NextItem;   /* 25% */
  1992.    UPDATEMENUFLAG(currentSub, (nLogViewLevel==O_LOGVIEW25), CHECKED);
  1993.    
  1994.    currentSub  = currentSub->NextItem;   /* 50% */
  1995.    UPDATEMENUFLAG(currentSub, (nLogViewLevel==O_LOGVIEW50), CHECKED);
  1996.    
  1997.    currentSub  = currentSub->NextItem;   /* 75% */
  1998.    UPDATEMENUFLAG(currentSub, (nLogViewLevel==O_LOGVIEW75), CHECKED);
  1999.  
  2000.    currentSub  = currentSub->NextItem;   /* Full */
  2001.    UPDATEMENUFLAG(currentSub, (nLogViewLevel==O_LOGVIEWFULL), CHECKED);
  2002.    
  2003.  ResetMenuStrip(TrackWindow->win, Menu);                 
  2004.  return;    
  2005. }
  2006.  
  2007. /* accepts a command string of the following format:
  2008.  
  2009.     "NAME,COMMAND"
  2010.     
  2011.    To insert, update, or delete an action.  e.g.
  2012.    
  2013.     "AmiPhone,sys:utilities/AmiPhone CONNECT=%s"
  2014.     
  2015.    Would add a command named AmiPhone to the cycle gadget.
  2016.    
  2017.    To delete an action, specify the name without a command.  e.g.
  2018.    
  2019.     "AmiPhone"
  2020.     
  2021.    Returns the index of the new (or removed) command, or -1 if 
  2022.    there was an error (out of memory or all 20 spots used)
  2023.    
  2024. */ 
  2025. int UpdateActions(struct WindowStuff * win, char * string)
  2026. {
  2027.   int prevGadWidth;
  2028.   char name[75] = "", command[300] = "", *pcTemp;
  2029.   int index = -1, i;
  2030.   
  2031.   if (win) prevGadWidth = getActionCycleWidth(win);
  2032.   
  2033.   if (pcTemp = strchr(string, ',')) *pcTemp = '\0';
  2034.   strncpy(name, string, sizeof(name));
  2035.   if (pcTemp)
  2036.   {
  2037.     *pcTemp = ','; pcTemp++;
  2038.     strncpy(command, pcTemp, sizeof(command));
  2039.   }
  2040.   
  2041.   /* Check current index to see if the item is already present */
  2042.   for (i=0; ((ActionLabels[i])&&(i<20)); i++)
  2043.   {
  2044.     if (strcmp(name, ActionLabels[i]) == 0) 
  2045.     {
  2046.       index = i; 
  2047.       break;
  2048.     }
  2049.   }
  2050.   
  2051.   if (index >= 0)
  2052.   {
  2053.     /* Replace/Delete existing command */
  2054.     if (command[0]) ReplaceAllocedString(&Actions[index], command);
  2055.     else 
  2056.     {
  2057.       /* delete this entry and move the following entries back one! */
  2058.       ReplaceAllocedString(&ActionLabels[index], NULL);
  2059.       ReplaceAllocedString(&Actions[index], NULL);
  2060.       for (int i=index; i<19; i++)
  2061.       {
  2062.         ActionLabels[i] = ActionLabels[i+1];
  2063.         Actions[i]      = Actions[i+1];
  2064.       }
  2065.       Actions[19] = ActionLabels[19] = NULL;
  2066.       nNumActions--;
  2067.           
  2068.       /* Shrink cycle gadget if necessary */
  2069.       if ((win)&&(getActionCycleWidth(win) < prevGadWidth)) UpdateWindow(win, FALSE);
  2070.       SetCurrentActionIndex(win, currentAction);
  2071.       return(index);
  2072.     }
  2073.   }
  2074.   else
  2075.   {
  2076.     /* Add new command */
  2077.     if ((name[0])&&(command[0]))
  2078.     {
  2079.       for (i=0; i<20; i++)
  2080.       {
  2081.         if (ActionLabels[i] == NULL)
  2082.         {
  2083.           ReplaceAllocedString(&ActionLabels[i], name);
  2084.           ReplaceAllocedString(&Actions[i], command);
  2085.           nNumActions++;
  2086.           index = i;
  2087.           if (currentAction < 0) currentAction = 0;
  2088.           break;
  2089.         }
  2090.       }
  2091.     }
  2092.   }
  2093.   
  2094.   /* grow cycle gadget if necessary */
  2095.   if ((win)&&(getActionCycleWidth(win) > prevGadWidth)) UpdateWindow(win, FALSE);
  2096.   SetCurrentActionIndex(win, currentAction);
  2097.   return(index);
  2098. }
  2099.  
  2100. BOOL isDeviceAvailable(char * name)
  2101. {
  2102.   struct DosList * dlist;
  2103.   BOOL ret = FALSE;
  2104.   ULONG flags = LDF_ALL | LDF_WRITE;
  2105.   
  2106.   if (dlist = LockDosList(flags))
  2107.   {
  2108.     ret = (FindDosEntry(dlist, name, flags) != NULL);
  2109.     UnLockDosList(flags);
  2110.   }
  2111.   return(ret);
  2112. }
  2113.  
  2114.  
  2115. int main(int argc, char **argv)
  2116. {
  2117.   BOOL BOpenWin;
  2118.   char * hotkey, * action, * userName, * realName;
  2119.   long pri;
  2120.   char * fakeArgv[2];
  2121.   int i=0;
  2122.     
  2123.   NewList(&clientList);
  2124.   NewList(&logList);
  2125.   memset(rexxScripts, 0, sizeof(rexxScripts));
  2126.  
  2127.   /* Disable DICE's stupid CTRL-C handling--we really can't have ungraceful exits */
  2128.   signal(SIGINT, SIG_IGN);
  2129.     
  2130.   /* fix bug when called from CLI with no args */
  2131.   if ((BStartedFromWB == FALSE)&&(argc==1))
  2132.   {
  2133.     /* fake an arg */
  2134.     argc = 2;
  2135.     argv = fakeArgv;
  2136.       
  2137.     fakeArgv[0] = argv[0];
  2138.     fakeArgv[1] = "\0";
  2139.   }
  2140.   if ((BStartedFromWB == FALSE)&&(argc>=2)&&(*argv[1]=='?'))
  2141.   {
  2142.     printf("Usage: QAmiTrack SERVER/K,PORT/K/N,COMMENT/K,CX_POPUP/K,\n"
  2143.            "                 CX_PRIORITY/K/N,CX_POPKEY/K,ENABLE/K,\n"
  2144.            "                 ALLOWMULTIPLE/K,CONFIRMAPPLAUNCH/K,\n"
  2145.            "                 ACTION1/K,ACTION2/K,...,LEFT/K/N,TOP/K/N,\n"
  2146.            "                 WIDTH/K/N,HEIGHT/K/N,LOGFILE/K,LOGLEVEL/K,\n"
  2147.            "                 RECONNECTDELAY/K/N,RAGGEDTEXT/K,VIEWBY/K\n"
  2148.            "                 SORTBY/K,USERNAME/K,REALNAME/K,VISIBLETO/K\n"
  2149.            "                 LOGONSCRIPT/K,LOGOFFSCRIPT/K,UPDATESCRIPT/K\n"
  2150.            "                 CONNECTSCRIPT/K,ENDSCRIPT/K,DISCONNECTSCRIPT/K\n"
  2151.            "                 DISCONNECTSCRIPT/K,STARTSCRIPT/K,BEEPONLOG/K\n"
  2152.            "                 LOGVIEWLEVEL/K,LOGVIEWLENGTH/K/N\n");
  2153.     exit(5);
  2154.   }
  2155.    
  2156.   InitActions();
  2157.  
  2158.   atexit(Cleanup);
  2159.   
  2160.   UNLESS(IconBase = OpenLibrary("icon.library",33))
  2161.     TrackExit("Couldn't open icon.library 33+",RETURN_ERROR);
  2162.  
  2163.   UNLESS(CxBase = OpenLibrary("commodities.library", 37L))
  2164.     TrackExit("Couldn't open commodities.library v37+", RETURN_ERROR);
  2165.                         
  2166.   UNLESS(WorkbenchBase = OpenLibrary("workbench.library",37))
  2167.       TrackExit("Couldn't open icon.library 37+",RETURN_ERROR);
  2168.  
  2169.   UNLESS(GraphicsBase = OpenLibrary("graphics.library", 37))
  2170.     TrackExit("Couldn't open graphics.library 37+",RETURN_ERROR);
  2171.  
  2172.   UNLESS(IntuitionBase = OpenLibrary("intuition.library", 37))
  2173.     TrackExit("Couldn't open intuition.library 37+",RETURN_ERROR);
  2174.  
  2175.   UNLESS(AMarqueeBase = OpenLibrary("amarquee.library", 46L))
  2176.     TrackExit("Couldn't open amarquee.library v46+", RETURN_ERROR);
  2177.         
  2178.   UNLESS(GadToolsBase = OpenLibrary("gadtools.library", 37))
  2179.     TrackExit("Couldn't open gadtools.library 37+",RETURN_ERROR);
  2180.  
  2181.   UNLESS(DiskFontBase = OpenLibrary("diskfont.library", 37))
  2182.     TrackExit("Couldn't open diskfont.library 37+",RETURN_ERROR);
  2183.     
  2184.   /* Parse startup arguments */
  2185.   UNLESS(ttypes = ArgArrayInit(argc, argv))
  2186.       TrackExit("Couldn't read startup arguments", RETURN_ERROR);
  2187.  
  2188.   nWinLeft    = ArgInt(   ttypes, "LEFT",         nWinLeft);
  2189.   nWinTop      = ArgInt(   ttypes, "TOP",          nWinTop);
  2190.   nWinWidth   = ArgInt(   ttypes, "WIDTH",        nWinWidth);
  2191.   nWinHeight  = ArgInt(   ttypes, "HEIGHT",       nWinHeight);
  2192.   nPort       = ArgInt(   ttypes, "PORT",         DEFAULT_AMITRACK_PORT);
  2193.   origComment = PastSpaces(ArgString(ttypes, "COMMENT",      DEFAULT_AMITRACK_COMMENT));
  2194.   pri         = ArgInt(   ttypes, "CX_PRIORITY",  DEFAULT_AMITRACK_CXPRI);
  2195.   nReconnectDelay = ArgInt(ttypes, "RECONNECTDELAY", DEFAULT_AMITRACK_RECONNECTDELAY);
  2196.   hotkey      = ArgString(ttypes, "CX_POPKEY",    DEFAULT_AMITRACK_POPKEY);
  2197.   szAccessTo  = ArgString(ttypes, "VISIBLETO",    DEFAULT_AMITRACK_ACCESSTO);
  2198.   szLogFileName = strdup(ArgString(ttypes, "LOGFILE",      DEFAULT_AMITRACK_LOGFILE));  /* strdup() it because we may need to change it! */
  2199.   nViewBy     = ParseViewBy(ArgString(ttypes, "VIEWBY",      DEFAULT_AMITRACK_VIEWBY));
  2200.   nSortBy     = ParseSortBy(ArgString(ttypes, "SORTBY",      DEFAULT_AMITRACK_SORTBY));
  2201.   nLogLevel   = ParseOutputLevel(ArgString(ttypes, "LOGLEVEL",        DEFAULT_AMITRACK_LOGLEVEL));
  2202.   BOpenWin    = ParseBool(ArgString(ttypes, "CX_POPUP",     DEFAULT_AMITRACK_CXPOPUP));
  2203.   BAllowMultiple    = ParseBool(ArgString(ttypes, "ALLOWMULTIPLE",    DEFAULT_AMITRACK_ALLOWMULTIPLE));
  2204.   BEnabled          = ParseBool(ArgString(ttypes, "ENABLED",          DEFAULT_AMITRACK_ENABLED));
  2205.   BConfirmAppLaunch = ParseBool(ArgString(ttypes, "CONFIRMAPPLAUNCH", DEFAULT_AMITRACK_CONFIRMAPP));
  2206.   BRaggedText       = ParseBool(ArgString(ttypes, "RAGGEDTEXT",       DEFAULT_AMITRACK_RAGGEDTEXT));    
  2207.   BBeepOnLog        = ParseBool(ArgString(ttypes, "BEEPONLOG",        DEFAULT_AMITRACK_BEEPONLOG));    
  2208.   ReplaceAllocedString(&szServerName, ArgString(ttypes, "SERVER",       DEFAULT_AMITRACK_SERVER));
  2209.   nLogViewLevel    = ParseLogViewLevel(ArgString(ttypes, "LOGVIEWLEVEL", DEFAULT_AMITRACK_LOGVIEWLEVEL));
  2210.   maxLogListLen    = ArgInt(ttypes, "LOGVIEWLENGTH", DEFAULT_AMITRACK_LOGVIEWLENGTH);
  2211.  
  2212.   realName = ArgString(ttypes, "REALNAME", NULL);    
  2213.   userName = ArgString(ttypes, "USERNAME", NULL);    
  2214.   
  2215.   /* If not given, look in ENV: */
  2216.   UNLESS(userName) userName = getenv("USERNAME");
  2217.   UNLESS(userName) userName = getenv("USER");
  2218.   UNLESS(userName) userName = getenv("LOGNAME");
  2219.   UNLESS(userName) userName = "nobody";
  2220.   UNLESS(realName) realName = getenv("REALNAME");
  2221.   UNLESS(realName) realName = "anonymous";
  2222.  
  2223.   /* Record any ARexx scripts the user wants launched */
  2224.   for (i=0; i<NUM_LOG_ENUMS; i++) 
  2225.   {
  2226.     UNLESS(rexxScripts[i] = strdup(ArgString(ttypes, rexxScriptNames[i], "")))
  2227.       TrackExit("Couldn't allocate Rexx script name", RETURN_ERROR);
  2228.   }
  2229.   
  2230.   for (i=0; i<20; i++)
  2231.   {
  2232.     char temp[15];
  2233.     sprintf(temp,"ACTION%i",i);
  2234.       
  2235.     if (action = (ArgString(ttypes, temp, NULL)))
  2236.     {
  2237.       char * pcTemp = strchr(action,',');
  2238.       if (pcTemp)
  2239.       {
  2240.         *pcTemp = '\0';  /* temporarily separate the string */
  2241.         ReplaceAllocedString(&ActionLabels[nNumActions],action);
  2242.         ReplaceAllocedString(&Actions[nNumActions],pcTemp+1);
  2243.         *pcTemp = ',';
  2244.         nNumActions++;        
  2245.         currentAction = 0;
  2246.       }
  2247.     }
  2248.   }
  2249.     
  2250.   /* set globals */
  2251.   pcDisplayVersionString = &szVersionString[6];
  2252.  
  2253.   lLastChangeAt = time(NULL);
  2254.  
  2255.   /* Allocate our own copies of the given strings */
  2256.   UNLESS((myInfo = NewClient(""))&&
  2257.          (SetString(&myInfo->userName, userName))&&
  2258.          (SetString(&myInfo->realName, realName))&&
  2259.          (SetString(&myInfo->comment,  origComment)) &&
  2260.          (SetString(&myInfo->winOpen,  BOpenWin ? "Y" : "N")))
  2261.            TrackExit("Couldn't allocate local info", RETURN_ERROR);
  2262.  
  2263.   /* Force userName to be <= 8 chars */
  2264.   if (myInfo->userName->bufLen >= 9) myInfo->userName->buffer[8] = '\0';
  2265.     
  2266.   UNLESS(CX = SetupCxStuff(NULL, pri, hotkey))
  2267.     TrackExit("Couldn't setup Commodities Broker.  (AmiTrack already running?)",RETURN_OK);
  2268.  
  2269.   UNLESS(TS = SetupTimer(NULL)) TrackExit("Couldn't setup timer.",RETURN_ERROR);
  2270.         
  2271.   rexxHost = SetupARexxHost(CX->name, NULL);
  2272.     
  2273.   UNLESS((BOpenWin==FALSE)||(TrackWindow = SetupTrackWindow(NULL))) TrackExit("Couldn't open GUI", RETURN_ERROR);
  2274.  
  2275.   SetMenuValues();
  2276.     
  2277.   SetTrackFlag(CODE_RECONNECT);
  2278.   SetTimer(TS, nUpdateDelay, 0);
  2279.  
  2280.   RecordEvent(LOG_START, NULL, NULL, NULL, NULL);
  2281.   
  2282.   while(1)
  2283.   {
  2284.     if (TrackFlagsSet() == FALSE) TrackWait(TrackWindow, session, rexxHost, CX, TS);
  2285.  
  2286.     if (CheckTrackFlag(CODE_TIMER))
  2287.     {
  2288.       if (TrackWindow) UpdateWindowTimes();
  2289.       connectDelayLeft -= nUpdateDelay;
  2290.       if ((session == NULL)&&(connectDelayLeft < 0)) 
  2291.       {
  2292.         if (nReconnectDelay >= 0) SetTrackFlag(CODE_RECONNECT);
  2293.         connectDelayLeft = nReconnectDelay*60;
  2294.       }
  2295.       SetTimer(TS, nUpdateDelay, 0);
  2296.     }
  2297.     if (CheckTrackFlag(CODE_QUIT)) break;
  2298.     if (CheckTrackFlag(CODE_WINDOW_EVENT)) HandleIDCMP(TrackWindow);
  2299.     if (CheckTrackFlag(CODE_AREXX))        ARexxDispatch(rexxHost);
  2300.     if (CheckTrackFlag(CODE_ENABLE))
  2301.     {
  2302.       BEnabled = TRUE;
  2303.       ActivateCxObj(CX->broker, 1L);
  2304.       StatMessage("AmiTrack client is now enabled.");
  2305.       if (session == NULL) SetTrackFlag(CODE_RECONNECT);
  2306.     }
  2307.     if (CheckTrackFlag(CODE_DISABLE))
  2308.     {
  2309.       ActivateCxObj(CX->broker, 0L);
  2310.       BEnabled = FALSE;
  2311.       Disconnect("AmiTrack client is now disabled.");
  2312.     }
  2313.     if (CheckTrackFlag(CODE_RECONNECT)) Reconnect();
  2314.     if (CheckTrackFlag(CODE_QMESSAGE))
  2315.     {
  2316.       struct QMessage * msg;
  2317.  
  2318.       while(msg = GetMsg(session->qMsgPort))
  2319.       {
  2320.         if ((msg->qm_Status == QERROR_SYS_MESSAGE)&&(msg->qm_Path)&&(msg->qm_Data))
  2321.         {
  2322.            char * hostNameEnd = strchr(msg->qm_Path+1, '/');
  2323.            
  2324.            if (hostNameEnd)
  2325.            {
  2326.              *hostNameEnd = '\0';
  2327.              RecordEvent(LOG_SYSMESSAGE, NULL, NULL, msg->qm_Path+1, msg->qm_Data);          
  2328.              *hostNameEnd = '/';
  2329.            }
  2330.         }
  2331.         else if (msg->qm_Status == QERROR_UNPRIVILEGED)
  2332.         {
  2333.           /* Do nothing, it probably only means nobody heard our QMessageOp() */
  2334.         }
  2335.         else if (msg->qm_Status == QERROR_NO_ERROR)
  2336.         {
  2337.           if (msg->qm_ID > 0) UpdateClientList(msg);
  2338.           else if ((msg->qm_ID == 0)&&(msg->qm_Path)&&(msg->qm_Data))
  2339.           {
  2340.             if (BConnected == FALSE)
  2341.             {
  2342.               lastUpdateTime = time(NULL);
  2343.               StatMessage("Connected to server.");
  2344.               RecordEvent(LOG_CONNECT, NULL, NULL, NULL, NULL);
  2345.               BConnected = TRUE;  /* Note that our connection is "live" now! */
  2346.             }
  2347.             else
  2348.             {
  2349.               /* If ID is zero, then we know QMessage is a result
  2350.                  of someone doing a QMessageOp() at us! */
  2351.               UpdateClientTime(msg->qm_Path, *((ULONG *) msg->qm_Data));
  2352.               UpdateWindowTimes();
  2353.             }
  2354.           }
  2355.         }
  2356.         else /* error! */
  2357.         {
  2358.           char szMessage[250];
  2359.  
  2360.           sprintf(szMessage,"Disconnected: [%s] (line %i).", QErrorName(msg->qm_Status), msg->qm_ErrorLine);
  2361.           FreeQMessage(session, msg);  /* gotta free it now as were gonna break outta the loop! */
  2362.           Disconnect(BConnected ? szMessage : NULL);
  2363.           break;  /* Important to stop processing QMessages after the session has gone! */
  2364.         }
  2365.  
  2366.         /* free up message now that we're done with it! */ 
  2367.         FreeQMessage(session, msg);
  2368.       }
  2369.     }
  2370.     if ((CheckTrackFlag(CODE_HIDE))&&(TrackWindow)) 
  2371.     {
  2372.       TrackWindow = SetupTrackWindow(TrackWindow);
  2373.       SetString(&myInfo->winOpen,"N");
  2374.       SendState(STATE_WINOPEN);
  2375.     }
  2376.     if (CheckTrackFlag(CODE_SHOW))
  2377.     {
  2378.       if (TrackWindow) ActivateWindow(TrackWindow->win);
  2379.       else
  2380.       {
  2381.         TrackWindow = SetupTrackWindow(NULL);        
  2382.         StatMessage(NULL);  /* Show last message */ 
  2383.         SetString(&myInfo->winOpen,"Y");
  2384.         SendState(STATE_WINOPEN);
  2385.       }
  2386.     } 
  2387.     SetMenuValues();
  2388.   }
  2389.   
  2390.   /* Cleanup() called here! */
  2391. }
  2392.