"Fossies" - the Fresh Open Source Software Archive

Member "squidview-0.86/squidview.cpp" (1 Feb 2017, 121667 Bytes) of package /linux/privat/squidview-0.86.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "squidview.cpp": 0.85_vs_0.86.

    1 /*
    2  *  This program is free software; you can redistribute it and/or modify
    3  *  it under the terms of the GNU General Public License as published by
    4  *  the Free Software Foundation; either version 2 of the License, or
    5  *  (at your option) any later version.
    6  *
    7  *  This program is distributed in the hope that it will be useful,
    8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
    9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   10  *  GNU Library General Public License for more details.
   11  *
   12  *  You should have received a copy of the GNU General Public License
   13  *  along with this program; if not, write to the Free Software
   14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
   15 
   16  squidview 0.8x
   17 
   18  A program to nicely browse your squid log
   19   
   20  (c) 2001 - 2017 Graeme Sheppard
   21 
   22  This program is not how it would be if I had to rewrite it from scratch.
   23  It started off very simple (quick and nasty) and needed to be fast because
   24  the computer was only a 486. So it was done in C. Then it would encounter
   25  a long request line, buffer overflow and seg fault. So I doubled the
   26  buffer size. OK for a while then same thing. Should've been C++ from the
   27  start because I kept doubling.
   28 
   29  A report generator a mile long, and a mile deep. Global variables like crazy.
   30  A half-baked keyboard input system. A half-baked console out display. The
   31  last one has a view good things about it.
   32  
   33  The program should have been split into several files at least. The code
   34  logic is followable which is something.
   35  
   36  Warning: those who complain about the source code might be requested to
   37  be the one who rewrites it :)
   38   
   39  */
   40 
   41 #ifdef HAVE_CONFIG_H
   42 #include <config.h>
   43 #else
   44 #  define SHAREDIR "/usr/local/share/squidview"
   45 #  define PACKAGE "squidview"
   46 #  define VERSION "0.7"
   47 #endif
   48 
   49 #include <string>
   50 #include <sstream>
   51 #include <vector>
   52 #include <algorithm>
   53 
   54 #include <curses.h>
   55 #include <signal.h>
   56 #include <sys/time.h>
   57 #include <sys/types.h>
   58 #include <sys/stat.h>
   59 #include <unistd.h>
   60 #include <fcntl.h>
   61 #include <time.h>
   62 #include <stdlib.h>
   63 #include <stdio.h>
   64 #include <math.h>
   65 #include <ctype.h>
   66 #include <string.h>
   67 
   68 #include "squidview.h"
   69 
   70 using namespace std;
   71 
   72 
   73 ct con; // screen output
   74 cUsersMode aTally; // an instance of users' tally
   75 
   76 void ShowHighLightMessage (const string& sText);
   77 
   78 
   79 void print (const string& sText)
   80 {
   81   printf ("%s\n", sText.c_str());
   82 }
   83 
   84 
   85 // is the text null?
   86 // this function fixed by Mike Reid
   87 bool NullText (const char* szText)
   88 {
   89   if (szText != 0)
   90   {
   91     const int length = strlen(szText);
   92     for (int index = 0; index < length; ++index)
   93     {
   94       if (!isspace(szText[index]))
   95         return false;
   96     }
   97   }
   98 
   99   // If got here then string is null.
  100   return true;
  101 }
  102 
  103 
  104 // string to lower case
  105 // this function fixed by Mike Reid
  106 void StrLwr (string& sString)
  107 {
  108   const int length = sString.length();
  109   for (int index = 0; index < length; ++index)
  110     sString[index] = tolower(sString[index]);
  111 }
  112 
  113 
  114 // support the next function
  115 inline bool CompactThis (string& sString, const string& sSubString)
  116 {
  117   int iLen = sString.length();
  118   int iSubLen = sSubString.length();
  119   string sTemp;
  120 
  121   if (iSubLen > iLen)
  122     return false;
  123   sTemp = sString.substr (iLen - iSubLen, iSubLen);
  124   if (sSubString == sTemp)
  125   {
  126     sString.replace (iLen - iSubLen, iSubLen, "");
  127     return true;
  128   }
  129   else
  130     return false;
  131 }
  132 
  133 
  134 // Remove trailing spaces: " "s or "%20"s
  135 void Compact (string& sString)
  136 {
  137   bool bDone;
  138 
  139   for (;;)
  140   {
  141     bDone = true;
  142     if (CompactThis (sString, " "))
  143       bDone = false;
  144     if (CompactThis (sString, "%20"))
  145       bDone = false;
  146     if (bDone)
  147       return;
  148   }
  149 }
  150 
  151 
  152 // remove leading " "s
  153 void NoLeadingSpaces (string& sText)
  154 {
  155   while ((sText != "") && (sText [0] == ' '))
  156     sText.replace (0, 1, "");
  157 }
  158 
  159 
  160 // int to string conversion
  161 string ItoS (tByteTotal iNumber)
  162 {
  163   stringstream ssTemp;
  164 
  165   ssTemp << iNumber;
  166   return (ssTemp.str());
  167 }
  168 
  169 
  170 // 12345 to "12,345"
  171 string ItoCommas (tByteTotal iNumber)
  172 {
  173   string sReturn;
  174   int iIndex, iCount;
  175 
  176   sReturn = ItoS (iNumber);
  177   
  178   iIndex = sReturn.length();
  179   if (iIndex == 0)
  180     return sReturn;
  181   iIndex--;
  182   iCount = 4;
  183   while (iIndex >= 0)
  184   {
  185     if (--iCount == 0)
  186     {
  187       sReturn.insert (iIndex + 1, ",");
  188       iCount = 3;
  189     }
  190     iIndex--;
  191   }
  192   return sReturn;
  193 }
  194 
  195 
  196 // open a file, with large file support >4G
  197 int OpenLargeFile (const char* szFilename)
  198 {
  199 #ifdef LARGE_FILE
  200   return open (szFilename, O_RDONLY | O_LARGEFILE);
  201 #else
  202   return open (szFilename, O_RDONLY);
  203 #endif
  204 }
  205 
  206 
  207 // return the size of a file
  208 tFilePos GetFileSize (const string& sFileName)
  209 {
  210   int iFD;
  211   tFilePos iPos;
  212   
  213   iFD = OpenLargeFile (sFileName.c_str());
  214   if (iFD < 0)
  215   {
  216     return 0;
  217   }
  218   iPos = lseek (iFD, 0, SEEK_END);
  219   close (iFD);
  220   return iPos;
  221 }
  222 
  223 
  224 // clears the screen
  225 void MyCls()
  226 {
  227   attroff (A_REVERSE);
  228   clear();
  229   refresh();
  230   move (0, 0);
  231 }
  232 
  233 
  234 bool RunProgramQuietly (const string& sCommand)
  235 {
  236   if (system ((sCommand + " 2>/dev/null").c_str()) == 0)
  237     return true;
  238   sStatusMessage = string ("Error running command: " + sCommand);
  239   return false;
  240 }
  241 
  242 
  243 // execute a shell command
  244 void RunProgramInConsole (const string& sCommand)
  245 {
  246   MyCls();
  247   curs_set (1);
  248   echo();
  249   nocbreak();
  250   endwin();   // suggested by endwin(3)
  251   if (system (sCommand.c_str()))
  252     sStatusMessage = string ("Error running command: " + sCommand);
  253   keypad (stdscr, TRUE);
  254   curs_set (0);
  255   noecho();
  256   cbreak();
  257   MyCls();
  258 }
  259 
  260 
  261 // allows check of keyboard buffer to see if keypress ready
  262 void GoForNoDelay()
  263 {
  264   if (wTemp)
  265     delwin (wTemp);
  266   wTemp = newwin (1, 1, LINES - 1, COLS - 1);
  267   wrefresh (wTemp);
  268   nodelay (wTemp, true);
  269 }
  270 
  271 
  272 // return xxx.yy% of first number to second number
  273 void CalcPercentage (tFilePos iCurrent, tFilePos iEnd,
  274                      string& sOutput)
  275 {
  276   float fPC;
  277   int iNum;
  278   char szTemp [20] = "";
  279 
  280   if ((iCurrent == 0) || (iEnd == 0))
  281     sOutput = "  0.00%";
  282   else
  283   if (iCurrent >= iEnd)
  284     sOutput = "100.00%";
  285   else
  286   {
  287     iNum = int (10000.0 * iCurrent / iEnd);
  288     fPC = float (iNum) / 100.0;
  289     if (snprintf (szTemp, sizeof (szTemp), "%6.2f%%", fPC) < 0)
  290       sOutput = "  -.--%";
  291     else
  292       sOutput = szTemp;
  293   }
  294 }
  295 
  296 
  297 // parse "50000 250000 1000000" for size catches
  298 // given a pointer to it, return the number and the new pointer
  299 int NextCatchSize (const char** ppcText)
  300 {
  301   const char* pcLetter;
  302   char cLetter;
  303   int iNum;
  304   
  305   iNum = 0;
  306   pcLetter = *ppcText;
  307   for (;;)
  308   {
  309     cLetter = *pcLetter;
  310     if (cLetter == '\0')
  311     {
  312       *ppcText = pcLetter;
  313       return iNum;
  314     }
  315     pcLetter++;
  316     if (cLetter == ' ')
  317     {
  318       *ppcText = pcLetter;
  319       return iNum;
  320     }
  321     if ((cLetter < '0') || (cLetter > '9'))
  322       return -1;
  323     iNum = iNum * 10 + cLetter - '0';
  324   }
  325 }
  326 
  327 
  328 string RightJustify (const string& sText, int iCols)
  329 {
  330   string sTemp;
  331 
  332   if (int (sText.length()) >= iCols)
  333     return sText;
  334   sTemp.assign (iCols - int (sText.length()), ' ');
  335   sTemp += sText;
  336   return sTemp;
  337 }
  338 
  339 
  340 ct& ct::operator << (const char* szText)
  341 {
  342   addstr (const_cast<char*> (szText));
  343   return *this;
  344 }
  345 
  346 
  347 ct& ct::operator << (const string& sText)
  348 {
  349   addstr (const_cast<char*> (sText.c_str()));
  350   return *this;
  351 }
  352 
  353 
  354 ct& ct::operator << (char cLetter)
  355 {
  356   addch (cLetter);
  357   return *this;
  358 }
  359 
  360 
  361 ct& ct::operator << (bool bYes)
  362 {
  363   if (bYes)
  364     addstr ("yes");
  365   else
  366     addstr ("no");
  367   return *this;
  368 }
  369 
  370 
  371 ct& ct::operator << (int iNumber)
  372 {
  373   string sTemp;
  374 
  375   sTemp = ItoS (iNumber);
  376   addstr (const_cast<char*> (sTemp.c_str()));
  377   return *this;
  378 }
  379 
  380 
  381 bool CheckInterrupt()
  382 {
  383   for (;;)
  384   {
  385     switch (wgetch (wTemp))
  386     {
  387       case 's':
  388         return true;
  389       case ERR:
  390         return false;
  391     }
  392   }
  393 }
  394 
  395 
  396 bool CheckTicker()
  397 {
  398   static time_t iTicker = 0;
  399   time_t iCurrentime_t;
  400 
  401   iCurrentime_t = time (0);
  402   if (iCurrentime_t == iTicker)
  403     return false;
  404   
  405   iTicker = iCurrentime_t;
  406   return true;
  407 }
  408 
  409 
  410 void UpdateTicker (const char* szText, tFilePos iPos, tFilePos iMax)
  411 {
  412   string sTemp;
  413 
  414   CalcPercentage (iPos, iMax, sTemp);
  415   sTemp += string (" ") + szText + " | <s> to stop";
  416   ShowHighLightMessage (sTemp);
  417 }
  418 
  419 
  420 // check if need to open new file, return false on error
  421 inline bool ReadingFile()
  422 {
  423   if ((fReadingFile < 0) || (pszReadingFile != pszCurrentLog))
  424   {
  425     if (fReadingFile > 0)
  426       close (fReadingFile);
  427     fReadingFile = OpenLargeFile (pszCurrentLog);
  428     if (fReadingFile > 0)
  429     {
  430       pszReadingFile = pszCurrentLog;
  431       return true;
  432     }
  433     else
  434     {
  435       pszReadingFile = 0;
  436       return false;
  437     }
  438   }
  439   return true;
  440 }
  441 
  442 
  443 // goto log at a certain position, read a line into buffer
  444 // return start of next line
  445 // incorporates modified patches by Vaclav Haisman: 2x speed-up
  446 tFilePos GetLine (tFilePos iOffset)
  447 {
  448   tFilePos iBlock, iAvailable, iReturn;
  449   int iCount;
  450   char *pcBuffer;
  451 
  452   pcReqBuff = pcEmpty;
  453   
  454   iBlock = iLogFileSize - iOffset;
  455   if (iBlock <= 0)
  456     return 0;
  457   if (iBlock > nReadBuffer)
  458     iBlock = nReadBuffer;
  459 
  460   if (!ReadingFile())
  461     return 0;
  462 
  463   iReturn = 0;
  464 
  465   if (lseek (fReadingFile, iOffset, SEEK_SET) >= 0)
  466   {
  467     iAvailable = read (fReadingFile, &cDiskBuffer, iBlock);
  468     if (iAvailable > 0)
  469     {
  470       pcBuffer = cDiskBuffer;
  471       for (iCount = 0; iCount < iAvailable; iCount++)
  472       {
  473         if (*pcBuffer == cEOL)
  474           break;
  475         pcBuffer++;
  476       }
  477       if (iCount >= iAvailable)
  478         iCount = iAvailable - 1;
  479       iReturn = iOffset + iCount + 1;
  480       cDiskBuffer [iCount] = '\0';
  481       pcReqBuff = cDiskBuffer;
  482     }
  483   }
  484 
  485   return iReturn;
  486 }
  487 
  488 
  489 // return the line before, eg for going backwards
  490 // incorporates modified patches by Vaclav Haisman: 2x speed-up
  491 tFilePos GetPrevLine (tFilePos iOffset)
  492 {
  493   tFilePos iBlock, iBase, iAvailable, iReturn;
  494   int iIndex;
  495   char* pcBuffer;
  496 
  497   pcReqBuff = pcEmpty;
  498 
  499   if (iOffset <= 0)
  500     return 0;
  501 
  502   iBlock = iOffset;
  503   if (iBlock > nReadBuffer)
  504     iBlock = nReadBuffer;
  505 
  506   if (!ReadingFile())
  507     return iOffset;
  508 
  509   iReturn = iOffset;
  510 
  511   iBase = iOffset - iBlock;
  512   if (lseek (fReadingFile, iBase, SEEK_SET) >= 0)
  513   {
  514     iAvailable = read (fReadingFile, &cDiskBuffer, iBlock);
  515     if (iAvailable > 3)
  516     {
  517       if (cDiskBuffer [iAvailable - 2] == cEOL)
  518       {
  519         iReturn = iBase + iAvailable - 2;
  520       }
  521       else
  522       {
  523         iIndex = iAvailable - 3;
  524         pcBuffer = cDiskBuffer + iIndex;
  525         while (iIndex > 0)
  526         {
  527           if (*pcBuffer == cEOL)
  528           {
  529             iReturn = iBase + iIndex + 1;
  530             pcReqBuff = cDiskBuffer + iIndex + 1;
  531             cDiskBuffer [iAvailable] = '\0';
  532             break;
  533           }
  534           iIndex--;
  535           pcBuffer--;
  536         }
  537         if (iIndex == 0)
  538           iReturn = iBase;
  539       }
  540     }
  541   }
  542 
  543   return iReturn;
  544 }
  545 
  546 
  547 // rationalise log file statistics
  548 void CalcLastPage()
  549 {
  550   tFilePos iThis, iNext;
  551   int iCount;
  552 
  553   iLogFileSize = GetFileSize (pszCurrentLog);
  554   if (iLogFileSize == 0)
  555   {
  556     iMaxLinesDown = 0;
  557     iLastPage = 0;
  558     iLastLinePos = 0;
  559     iLinesDown = 0;
  560     return;
  561   }
  562 
  563   iLastLinePos = GetPrevLine (iLogFileSize - 1);
  564 
  565   iThis = GetLine (0);
  566   for (iCount = 0; iCount < iMainLines; iCount++)
  567   {
  568     iNext = GetLine (iThis);
  569     if ((iNext == iThis) || (iNext == 0))
  570     {
  571       iMaxLinesDown = iCount;
  572       iLastPage = 0;
  573       if (iLinesDown > iMaxLinesDown)
  574         iLinesDown = iMaxLinesDown;
  575       return;
  576     }
  577     iThis = iNext;
  578   }
  579   iMaxLinesDown = iMainLines - 1;
  580   if (iLinesDown > iMaxLinesDown)
  581     iLinesDown = iMaxLinesDown;
  582 
  583   iThis = iLogFileSize;
  584   for (iCount = 0; iCount < iMainLines; iCount++)
  585     iThis = GetPrevLine (iThis);
  586   iLastPage = iThis;
  587 }
  588 
  589 
  590 // goto some point in log, place in middle of screen and
  591 // highlight it
  592 void CentreScreen (tFilePos iGoto)
  593 {
  594   tFilePos iTempPos;
  595   int iCount, iHalf;
  596 
  597   if (iGoto >= iLastPage)
  598   {
  599     iLinesDown = 0;
  600     iPagePos = iTempPos = iLastPage;
  601     for (iCount = 0; iCount < iMaxLinesDown; iCount++)
  602     {
  603       if (iTempPos == iGoto)
  604         return;
  605       iTempPos = GetLine (iTempPos);
  606       iLinesDown++;
  607     }
  608     iLinesDown = iMaxLinesDown;
  609     return;
  610   }
  611 
  612   iHalf = iMaxLinesDown / 2;
  613   iLinesDown = 0;
  614   iTempPos = iGoto;
  615   for (iCount = 0; iCount < iHalf; iCount++)
  616   {
  617     if (iTempPos == 0)
  618     {
  619       iPagePos = 0;
  620       return;
  621     }
  622     iTempPos = GetPrevLine (iTempPos);
  623     iLinesDown++;
  624   }
  625   iPagePos = iTempPos;
  626 }
  627 
  628 
  629 // return space seperated column number iNum
  630 // first = 1
  631 void GetColumn (const char* pcText0, int iNum, string& sResult)
  632 {
  633   int iColumn, iIndex, iStart;
  634   bool bCapture, bSkipSpace;
  635   char cLetter;
  636   const char* pcText;
  637   
  638   bCapture = iNum == 1 ? true : false;
  639   bSkipSpace = false;
  640   iStart = iIndex = 0;
  641   iColumn = 1;
  642   pcText = pcText0;
  643 
  644   for (;;)
  645   {
  646     cLetter = *pcText;
  647     if ((cLetter == 0) || (cLetter == cEOL) || (cLetter == cCR))
  648       break;
  649     if (cLetter == ' ')
  650     {
  651       if (bCapture)
  652         break;
  653       bSkipSpace = true;
  654     }
  655     else
  656     if (bSkipSpace)
  657     {
  658       if (++iColumn == iNum)
  659       {
  660         bCapture = true;
  661         iStart = iIndex;
  662       }
  663       bSkipSpace = false;
  664     }
  665     pcText++;
  666     iIndex++;
  667   }
  668   sResult = "";
  669   if (bCapture)
  670     sResult.append (pcText0 + iStart, iIndex - iStart);
  671 }
  672 
  673 
  674 // do we really need to see heaps of "http://" and "ftp://"?
  675 void RemoveSlashes (string& sText)
  676 {
  677   char szTemp [20];
  678 
  679   sText.copy (szTemp, 15);
  680 
  681   szTemp [7] = 0;
  682   if (strcmp (szTemp, "http://") == 0)
  683   {
  684     sText.replace (0, 7, "");
  685     return;
  686   }
  687 
  688   szTemp [6] = 0;
  689   if (strcmp (szTemp, "ftp://") == 0)
  690   {
  691     sText.replace (0, 6, "");
  692     return;
  693   }
  694 }
  695 
  696 
  697 void GetDomainName (const char* szRequest, string& sDomain)
  698 {
  699   int iCount, iSize;
  700   
  701   GetColumn (szRequest, iTargetCol, sDomain);
  702   RemoveSlashes (sDomain);
  703   iSize = sDomain.length();
  704   for (iCount = 0; iCount < iSize; iCount++)
  705     if (sDomain [iCount] == '/')
  706     {
  707       sDomain.replace (iCount, iSize - iCount, "");
  708       return;
  709     }
  710 }
  711 
  712 
  713 // pad a string with spaces to the edge specified, eg screen
  714 void Columnize (string& sText, int iWidth)
  715 {
  716   int iLength, iSpaces;
  717   string sTemp;
  718 
  719   iLength = sText.length();
  720   if (iLength == iWidth)
  721     return;
  722   if (iLength > iWidth)
  723     sText.replace (iWidth, iLength - iWidth, "");
  724   else
  725   {
  726     iSpaces = iWidth - iLength;
  727     sTemp.assign (iSpaces, ' ');
  728     sText += sTemp;
  729   }
  730 }
  731 
  732 
  733 time_t GetTimeNumber (const char* szRequest)
  734 {
  735   string sTemp;
  736   int iPos, iLength;
  737   char cLetter;
  738   time_t iTime;
  739 
  740   GetColumn (szRequest, iTimeCol, sTemp);
  741   if (sTemp == "")
  742     return 0;
  743     
  744   iTime = 0;
  745   iPos = 0;
  746   iLength = sTemp.length();
  747   for (;;)
  748   {
  749     if (iPos >= iLength)
  750       return iTime;
  751     cLetter = sTemp [iPos];
  752     if ((cLetter == '.') || (cLetter == 0))
  753         return iTime;
  754     if ((cLetter >= '0') && (cLetter <= '9'))
  755       iTime = (iTime * 10) + cLetter - '0';
  756     else
  757       return 0;
  758     iPos++;
  759   }
  760 }
  761 
  762 
  763 void CalcTime (time_t iTime, string& sDate)
  764 {
  765   int iCount; 
  766   char szTemp [80]; 
  767 
  768   strncpy (szTemp, ctime (&iTime), sizeof (szTemp) - 1); 
  769 // Ensure the time string is null-terminated. 
  770   szTemp [sizeof (szTemp) - 1] = '\0';
  771   iCount = strlen (szTemp); 
  772   if (iCount > 0) 
  773     if (szTemp [iCount - 1] == '\n') 
  774       szTemp [iCount - 1] = 0; 
  775   sDate = szTemp; 
  776 }
  777 
  778 
  779 void EraseStatusLine()
  780 {
  781   move (iMainLines, 0);
  782   attroff (A_REVERSE);
  783   clrtoeol();
  784 }
  785 
  786 
  787 void ShowHighLightMessage (const string& sText)
  788 {
  789   EraseStatusLine();
  790   move (iMainLines, 0);
  791   attron (A_REVERSE);
  792   con << sText;
  793   refresh();
  794 }
  795 
  796 
  797 void PromptForText (const string& sQuestion, string& sResult)
  798 {
  799   char szTemp [nInputLen];
  800 
  801   EraseStatusLine();
  802   move (iMainLines, 0);
  803   attroff (A_REVERSE);
  804   con << sQuestion;
  805   curs_set (1);
  806   echo();
  807   getstr (szTemp);
  808   curs_set (0);
  809   noecho();
  810   EraseStatusLine();
  811   sResult = szTemp;
  812 }
  813 
  814 
  815 char PromptForKey (const string& sQuestion)
  816 {
  817   char cKey;
  818 
  819   EraseStatusLine();
  820   move (iMainLines, 0);
  821   attroff (A_REVERSE);
  822   con << sQuestion;
  823   cKey = getch();
  824   EraseStatusLine();
  825   return cKey;
  826 }
  827 
  828 
  829 bool CharGood (char cLetter)
  830 {
  831   if ((cLetter >= '0') && (cLetter <= '9'))
  832     return true;
  833   if ((cLetter >= 'A') && (cLetter <= 'Z'))
  834     return true;
  835   if ((cLetter >= 'a') && (cLetter <= 'z'))
  836     return true;
  837   switch (cLetter)
  838   {
  839     case '-':
  840     case '_':
  841       return true;
  842   }
  843   return false;
  844 }
  845 
  846 
  847 string CheckFileName (const string& sName)
  848 {
  849   int iCount;
  850   char cLetter;
  851   string sReturn;
  852 
  853   for (iCount = 0; iCount < int (sName.length()); iCount++)
  854   {
  855     cLetter = sName [iCount];
  856     if (CharGood (cLetter) == false)
  857     {
  858       if ((cLetter < 32) || (cLetter > 126))
  859         return "Illegal character.";
  860       sReturn = string ("Illegal character: '") + cLetter + "'.";
  861       return sReturn;
  862     }
  863   }
  864   return "";
  865 }
  866 
  867 
  868 string CheckEmail (const string& sName)
  869 {
  870   int iCount;
  871   char cLetter;
  872   string sReturn;
  873 
  874   for (iCount = 0; iCount < int (sName.length()); iCount++)
  875   {
  876     cLetter = sName [iCount];
  877     if ((cLetter != '@') && (cLetter != '.') && (CharGood (cLetter) == false))
  878     {
  879       if ((cLetter < 32) || (cLetter > 126))
  880         return "Illegal character.";
  881       sReturn = string ("Illegal character: '") + cLetter + "'.";
  882       return sReturn;
  883     }
  884   }
  885   return "";
  886 }
  887 
  888 
  889 void GetText (string& sText)
  890 {
  891   char szTemp [nInputLen];
  892 
  893   curs_set (1);
  894   echo();
  895   getstr (szTemp);
  896   curs_set (0);
  897   noecho();
  898   sText = szTemp;
  899 }
  900 
  901 
  902 void GetFileName (string& sResult, bool* pbGood = 0)
  903 {
  904   string sCheck;
  905   
  906   if (pbGood)
  907     *pbGood = true;
  908   GetText (sResult);
  909   if (sResult == "")
  910     return;
  911 
  912   sCheck = CheckFileName (sResult);
  913   if (sCheck == "")
  914   {
  915     sResult += sReportExt;
  916     return;
  917   }
  918 
  919   con << "Problem: " << sCheck << " Press any key.";
  920   getch();
  921   con << "\n";
  922   sResult = "";
  923   if (pbGood)
  924     *pbGood = false;
  925 }
  926 
  927 
  928 /* unused
  929 void RemoveTrailingSpaces (string& sText)
  930 {
  931   int iIndex, iKeep;
  932   bool bStrip;
  933 
  934   iIndex = iKeep = sText.length();
  935   if (iIndex == 0)
  936     return;
  937   iIndex--;
  938   bStrip = false;
  939   for (;;)
  940   {
  941     if (sText [iIndex] != ' ')
  942       break;
  943     if (--iIndex < 0)
  944     {
  945       sText = "";
  946       return;
  947     }
  948     bStrip = true;
  949     iKeep--;
  950   }
  951   if (bStrip)
  952     sText.resize (iKeep);
  953 }
  954 */
  955 
  956 
  957 // load the search phrases
  958 int LoadWords()
  959 {
  960   int iFile, iRead, iLetter, iLength;
  961 
  962   sWords = "";
  963   iLength = 0;
  964   iFile = open (sWordFile.c_str(), O_RDONLY);
  965   if (iFile <= 0)
  966     return 0;
  967 
  968   iRead = 1;
  969   while (iRead == 1)
  970   {
  971     iLetter = 0;
  972     iRead = read (iFile, &iLetter, 1);
  973     if (iRead == 1)
  974     {
  975       if (iLetter == cEOL)
  976       {
  977         if (iLength > 0)
  978           sWords += cSeperator;
  979         iLength = 0;
  980       }
  981       else
  982       if (iLetter >= ' ')
  983       {
  984         sWords += iLetter;
  985         iLength++;
  986       }
  987     }
  988   }
  989   close (iFile);
  990 
  991   if ((sWords != "") && (sWords [sWords.length() - 1] != cSeperator))
  992     sWords += cSeperator;
  993   return 1;
  994 }
  995 
  996 
  997 // save the search phrases
  998 int SaveWords()
  999 {
 1000   int iIndex, iWrite, iReturn;
 1001   char cLetter, cWrite;
 1002   FILE* fOutput;
 1003 
 1004   fOutput = fopen (sWordFile.c_str(), "w");
 1005   if (fOutput == NULL)
 1006     return 0;
 1007 
 1008   iIndex = 0;
 1009   iReturn = -1;
 1010   while (iReturn == -1)
 1011   {
 1012     cLetter = sWords [iIndex++];
 1013     cWrite = 0;
 1014     switch (cLetter)
 1015     {
 1016       case 0:
 1017         iReturn = 1;
 1018         break;
 1019 
 1020       case cSeperator:
 1021         cWrite = '\n';
 1022         break;
 1023 
 1024       default:
 1025         cWrite = cLetter;
 1026     }
 1027     if (cWrite)
 1028     {
 1029       iWrite = fwrite (&cWrite, 1, 1, fOutput);
 1030       if (iWrite != 1)
 1031         iReturn = 0;
 1032     }
 1033   }
 1034   fclose (fOutput);
 1035   return iReturn;
 1036 }
 1037 
 1038 
 1039 tIPnum GetIP (const string& sText)
 1040 {
 1041   tIPnum iReturn = 0;
 1042   int iLength = sText.length();
 1043   int iIndex = 0;
 1044   int iByte = 0;
 1045   char cLetter = 0, cLast = 0;
 1046   
 1047   while (iIndex < iLength)
 1048   {
 1049     cLetter = sText [iIndex++];
 1050     if (cLetter == '.')
 1051     {
 1052       if (cLast == '.')
 1053         return 0;
 1054       iReturn = (iReturn << 8) + iByte;
 1055       iByte = 0;
 1056     }
 1057     else
 1058     if ((cLetter >= '0') && (cLetter <= '9'))
 1059     {
 1060       iByte = (iByte * 10) + (cLetter - '0');
 1061       if (iByte > 255)
 1062         return 0;
 1063     }
 1064     else
 1065       return 0;
 1066     cLast = cLetter;
 1067   }
 1068   
 1069   if (cLetter == '.')
 1070     return 0;
 1071     
 1072   iReturn = (iReturn << 8) + iByte;
 1073   return iReturn;  
 1074 }
 1075 
 1076 
 1077 // load aliases
 1078 void LoadAliases()
 1079 {
 1080   int iFile, iRead, iLetter, iNames, iLine, iCount;
 1081   string sLine, sColumn;
 1082   tIPnum iIP;
 1083   rAliasRecord rEntry;
 1084 
 1085   while (!vAliasList.empty())
 1086     vAliasList.pop_back();
 1087   while (!vAliasUserName.empty())
 1088     vAliasUserName.pop_back();
 1089 
 1090   iNames = 0;
 1091   iLine = 1;
 1092   
 1093   iFile = open (sAliasesFile.c_str(), O_RDONLY);
 1094   if (iFile <= 0)
 1095     return;
 1096 
 1097   iRead = 1;
 1098   sLine = "";
 1099   while (iRead == 1)
 1100   {
 1101     iLetter = 0;
 1102     iRead = read (iFile, &iLetter, 1);
 1103     if (iRead == 1)
 1104     {
 1105       if (iLetter == cEOL)
 1106       {
 1107         GetColumn (sLine.c_str(), 1, sColumn);
 1108         if (sColumn != "")
 1109         {
 1110           vAliasUserName.push_back (sColumn);
 1111           iCount = 2;
 1112           for (;;)
 1113           {
 1114             GetColumn (sLine.c_str(), iCount++, sColumn);
 1115             if (sColumn == "")
 1116               break;
 1117             iIP = GetIP (sColumn);
 1118             if (iIP <= 0)
 1119             {
 1120               sStatusMessage = string ("Error in aliases file at line ") +
 1121                 ItoS (iLine);
 1122               close (iFile);
 1123               return;
 1124             }
 1125             rEntry.iIPnum = iIP;
 1126             rEntry.iUserNum = iNames;
 1127             vAliasList.push_back (rEntry);
 1128           }
 1129           iNames++;
 1130         }
 1131         sLine = "";
 1132         iLine++;
 1133       }
 1134       else
 1135         sLine += char (iLetter);
 1136     }
 1137   }
 1138   close (iFile);
 1139 }
 1140 
 1141 
 1142 // does szText contain any of the search phrases?
 1143 // skip lines if they match with a ! (skip) search word
 1144 eSearch SearchString (const char* szText)
 1145 {
 1146   const char *pcWords, *pcWords2;
 1147   const char *pcText0, *pcText, *pcText2;
 1148   char cLetter, cFirst;
 1149   bool bFound;
 1150   eSearch iReturn;
 1151 
 1152   pcWords = sWords.c_str();
 1153   pcText0 = szText;
 1154   while (*pcWords)
 1155   {
 1156     iReturn = hit;
 1157     if (*pcWords == '!')
 1158     {
 1159       iReturn = veto;
 1160       pcWords++;
 1161     }
 1162     cFirst = *pcWords;
 1163     pcText = pcText0;
 1164     while ((cLetter = *pcText) != 0)
 1165     {
 1166       if (cLetter == cFirst)
 1167       {
 1168         bFound = true;
 1169         pcWords2 = pcWords + 1;
 1170         pcText2 = pcText + 1;
 1171         while (*pcWords2 != cSeperator)
 1172         {
 1173           if (*pcText2 != *pcWords2)
 1174           {
 1175             bFound = false;
 1176             break;
 1177           }
 1178           pcWords2++;
 1179           pcText2++;
 1180         }
 1181         if (bFound)
 1182           return iReturn;
 1183       }
 1184       pcText++;
 1185     }
 1186     while (*pcWords != cSeperator)
 1187       pcWords++;
 1188     pcWords++;
 1189   }
 1190   return miss;
 1191 }
 1192 
 1193 
 1194 // find full name of user
 1195 string GetLongUserName (const string& sLoginName)
 1196 {
 1197   string sUserLine, sUserName, sTemp, sReturn;
 1198   int iFile, iRead;
 1199   char cLetter;
 1200 
 1201   iFile = open (sUsersFile.c_str(), O_RDONLY);
 1202   if (iFile <= 0)
 1203     return "";
 1204 
 1205   sUserName = sLoginName;
 1206   StrLwr (sUserName);
 1207   sUserLine = sReturn = "";
 1208 
 1209   for (;;)
 1210   {
 1211     iRead = read (iFile, &cLetter, 1);
 1212     if (iRead < 1)
 1213       break;
 1214     if (cLetter == cEOL)
 1215     {
 1216       GetColumn (sUserLine.c_str(), 1, sTemp);
 1217       if (sTemp == sUserName)
 1218       {
 1219         sReturn = sUserLine;
 1220         sReturn.replace (0, sUserName.length() + 1, "");
 1221         break;
 1222       }
 1223       sUserLine = "";
 1224     }
 1225     else
 1226     if (cLetter != cCR)
 1227       sUserLine += cLetter;
 1228   }
 1229   close (iFile);
 1230   return sReturn;
 1231 }
 1232 
 1233 
 1234 string LookupIP (const string& sIPtext)
 1235 {
 1236   tIPnum iIP;
 1237   int iCount, iTotal;
 1238   
 1239   iIP = GetIP (sIPtext);
 1240   if (iIP <= 0)
 1241     return "";
 1242   iTotal = vAliasList.size();
 1243   
 1244   for (iCount = 0; iCount < iTotal; iCount++)
 1245     if (vAliasList [iCount].iIPnum == iIP)
 1246       return vAliasUserName [vAliasList [iCount].iUserNum];
 1247   
 1248   return "";
 1249 }
 1250 
 1251 
 1252 string ChopOffNetwork (const string& sSourceIP, int iLength)
 1253 {
 1254   string sTemp = sSourceIP;
 1255   int iLen, iIndex;
 1256   
 1257   for (;;)
 1258   {
 1259     if ((iLen = sTemp.length()) <= iLength)
 1260       return sTemp;
 1261     iIndex = 0;
 1262     for (;;)
 1263     {
 1264       if (iIndex >= iLen)
 1265         return sTemp;
 1266       if (sTemp [iIndex] == '.')
 1267       {
 1268         sTemp.replace (0, iIndex + 1, "");
 1269         break;
 1270       }
 1271       iIndex++;
 1272     }
 1273   }
 1274 }
 1275 
 1276 
 1277 // get username from column in request line, lower case it, remove "%20"s
 1278 // if dashed shorten the returned user by default
 1279 string GetUserName (const char* szRequest, bool bTruncate)
 1280 {
 1281   string sTemp, sIP;
 1282   
 1283   GetColumn (szRequest, iUserCol, sTemp);
 1284 
 1285   if (bAliases  && (sTemp == "-"))
 1286   {
 1287     GetColumn (szRequest, iIPCol, sIP);
 1288     sIP = LookupIP (sIP);
 1289     if (sIP != "")
 1290       return sIP;
 1291   }
 1292   
 1293   if (bLookupIP && (sTemp == "-"))
 1294   {
 1295     GetColumn (szRequest, iIPCol, sTemp);
 1296     if (!bTruncate)
 1297       return sTemp;
 1298     else
 1299       return ChopOffNetwork (sTemp, iUserNameLen);
 1300   }
 1301 
 1302   Compact (sTemp);
 1303   StrLwr (sTemp);
 1304   return sTemp;
 1305 }
 1306 
 1307 
 1308 // return the number of bytes in the request
 1309 int GetRequestSize (const char* szRequest)
 1310 {
 1311   string sTemp;
 1312   tFilePos iSize;
 1313 
 1314   GetColumn (szRequest, iSizeCol, sTemp);
 1315   if (sTemp == "")
 1316     return 0;
 1317   iSize = atoi (sTemp.c_str());
 1318   return iSize;
 1319 }
 1320 
 1321 
 1322 void GetRequestCacheHit (const char* szRequest, char& cHit, int& iHitNumber)
 1323 {
 1324   string sBoth, sLeft, sRight;
 1325   int iCount, iLength, iSlash = -1;
 1326   
 1327   cHit = ' ';
 1328   iHitNumber = 0; 
 1329   GetColumn (szRequest, iCacheHitCol, sBoth);
 1330   iLength = sBoth.length();
 1331   for (iCount = 0; iCount < iLength; iCount++)
 1332     if (sBoth [iCount] == '/')
 1333     {
 1334       iSlash = iCount;
 1335       break;
 1336     }
 1337   if (iSlash == -1)
 1338     return;
 1339   
 1340   sLeft = sRight = "";
 1341   if (iSlash >= 1)
 1342     sLeft = sBoth.substr (0, iSlash);
 1343   if (iSlash < iLength - 1)
 1344     sRight = sBoth.substr (iSlash + 1, iLength - iSlash - 1);
 1345   
 1346   // should be in table form
 1347   // the cache hits, the cache misses and other
 1348 
 1349   if (sLeft == "TCP_HIT")                 cHit = 'H'; else
 1350   if (sLeft == "TCP_IMS_HIT")             cHit = 'I'; else
 1351   if (sLeft == "TCP_REFRESH_HIT")         cHit = 'R'; else
 1352   if (sLeft == "TCP_REFRESH_UNMODIFIED")  cHit = 'R'; else
 1353   if (sLeft == "TCP_NEGATIVE_HIT")        cHit = 'N'; else
 1354   if (sLeft == "TCP_MEM_HIT")             cHit = 'M'; else
 1355   if (sLeft == "TCP_HIT_ABORTED")         cHit = 'A'; else
 1356   if (sLeft == "TCP_REFRESH_FAIL_OLD")    cHit = 'F'; else
 1357   if (sLeft == "TCP_INM_HIT")             cHit = 'U'; else
 1358 
 1359   if (sLeft == "TCP_MISS")                cHit = ' '; else
 1360   if (sLeft == "TCP_REFRESH_MISS")        cHit = 'm'; else
 1361   if (sLeft == "TCP_REFRESH_MODIFIED")    cHit = 'm'; else
 1362   if (sLeft == "TCP_CLIENT_REFRESH")      cHit = 'r'; else
 1363   if (sLeft == "TCP_CLIENT_REFRESH_MISS") cHit = 'c'; else
 1364   if (sLeft == "TCP_MISS_ABORTED")        cHit = 'a'; else
 1365   if (sLeft == "TCP_SWAPFAIL_MISS")       cHit = 'f'; else
 1366   if (sLeft == "TCP_TUNNEL")              cHit = 't'; else
 1367   if (sLeft == "TCP_REDIRECT")            cHit = 'o'; else
 1368 
 1369   if (sLeft == "TCP_DENIED")              cHit = 'd'; else
 1370   if (sLeft == "TAG_NONE")                cHit = '0'; else
 1371   if (sLeft == "NONE")                    cHit = '0'; else
 1372     cHit = '?';
 1373   
 1374   iHitNumber = atoi (sRight.c_str());
 1375 }
 1376 
 1377 
 1378 // return Request Flags
 1379 void GetRequestFlags (const char* szRequest, string& sFlags)
 1380 {
 1381   tFilePos iSize, iTemp;
 1382   int iCount;
 1383   const char* pcLetter;
 1384   char cLetter;
 1385   string sTemp;
 1386   
 1387   switch (SearchString (szRequest))
 1388   {
 1389     case hit:
 1390       sFlags = "w";
 1391       break;
 1392 
 1393     case miss:
 1394       sFlags = " ";
 1395       break;
 1396 
 1397     case veto:
 1398       sFlags = "-";
 1399       break;
 1400   }
 1401 
 1402   if (bAliases && (sFlags == " "))
 1403   {
 1404     sTemp = GetUserName (szRequest, false);
 1405     switch (SearchString (sTemp.c_str()))
 1406     {
 1407       case hit:
 1408         sFlags = "w";
 1409         break;
 1410 
 1411       case veto:
 1412         sFlags = "-";
 1413         break;
 1414 
 1415       case miss:
 1416         break;
 1417     }
 1418   }
 1419   
 1420   iSize = GetRequestSize (szRequest);
 1421   pcLetter = sSizeHits.c_str();
 1422   iCount = 0;
 1423   for (;;)
 1424   {
 1425     iTemp = NextCatchSize (&pcLetter);
 1426     if (iTemp <= 0)
 1427       break;
 1428     if (iTemp >= iSize)
 1429       break;
 1430     iCount++;
 1431   }
 1432   if (iCount == 0)
 1433     sFlags += ' ';
 1434   else
 1435     sFlags += char (iCount + '0');
 1436   
 1437   GetRequestCacheHit (szRequest, cLetter, iCount);
 1438   sFlags += cLetter;
 1439 
 1440   cLetter = ' ';
 1441   if (iRepFocus == user)
 1442   {
 1443     sTemp = GetUserName (szRequest, false);
 1444     if (sTemp == sRepFocus)
 1445       cLetter = 'f';
 1446   }
 1447   sFlags += cLetter;
 1448 }
 1449 
 1450 
 1451 inline bool SearchHit (const string& sFlags)
 1452 {
 1453   if ((iRepFocus == user) && (sFlags [3] != 'f'))
 1454     return false;
 1455   if (sFlags [0] == 'w')
 1456     return true;
 1457   if (sFlags [0] == '-')
 1458     return false;
 1459   if (iSizeHitGrade == 0)
 1460     return false;
 1461   if (sFlags [1] >= char (iSizeHitGrade + '0'))
 1462     return true;
 1463   return false;
 1464 }
 1465 
 1466 
 1467 void GetPaddedUserName (const char* szRequest, string& sUser)
 1468 {
 1469   sUser = GetUserName (szRequest, true);
 1470   Columnize (sUser, iUserNameLen);
 1471 }
 1472 
 1473 
 1474 // search the log file in up (-1) or down (+1) direction
 1475 void SearchWords (int iDir)
 1476 {
 1477   tFilePos iCurrent, iTempPos;
 1478   string sFlags, sDispLine;
 1479 
 1480   if ((iDir < 0) && (iPagePos == 0) && (iLinesDown == 0))
 1481     return;
 1482   if ((iDir > 0) && (iPagePos >= iLastPage) && (iLinesDown == iMaxLinesDown))
 1483     return;
 1484 
 1485   iCurrent = iLinePos;
 1486   if (iDir > 0)
 1487     iCurrent = GetLine (iCurrent);
 1488 
 1489   for (;;)
 1490   {
 1491     if (iDir > 0)
 1492       iTempPos = GetLine (iCurrent);
 1493     else
 1494       iTempPos = GetPrevLine (iCurrent);
 1495       
 1496     GetRequestFlags (pcReqBuff, sFlags);
 1497     if (SearchHit (sFlags))
 1498     {
 1499       if (iDir > 0)
 1500         CentreScreen (iCurrent);
 1501       else
 1502         CentreScreen (iTempPos);
 1503       return;
 1504     }
 1505 
 1506     if (iDir < 0)
 1507     {
 1508       iCurrent = iTempPos;
 1509       if (iCurrent <= 0)
 1510       {
 1511         iPagePos = 0;
 1512         iLinesDown = 0;
 1513         return;
 1514       }
 1515     }
 1516     else
 1517     {
 1518       if ((iTempPos == 0) || (iTempPos == iCurrent))
 1519       {
 1520         iPagePos = iLastPage;
 1521         iLinesDown = iMaxLinesDown;
 1522         return;
 1523       }
 1524       iCurrent = iTempPos;
 1525     }
 1526 
 1527     if (CheckInterrupt())
 1528       return;
 1529     if (CheckTicker())
 1530       UpdateTicker ("Searching...", iCurrent, iLastPage);
 1531   }
 1532 }
 1533 
 1534 
 1535 // remove seconds from time string
 1536 inline void RemoveSeconds (string& sTime)
 1537 {
 1538   if (sTime.length() > 21)
 1539     sTime.replace (16, 3, "");
 1540 }
 1541 
 1542 
 1543 // get the request's time out of it
 1544 string RequestToTime (const char* szRequest)
 1545 {
 1546   string sTime;
 1547   time_t iTime;
 1548   
 1549   if (iLogFileSize == 0) 
 1550     return "";
 1551     
 1552   if (NullText (szRequest))
 1553     return "";
 1554   
 1555   iTime = GetTimeNumber (szRequest);
 1556   CalcTime (iTime, sTime);
 1557   RemoveSeconds (sTime);
 1558   return sTime;
 1559 }
 1560 
 1561 
 1562 // get the time from an arbitary point in log file
 1563 string FilePosToTime (tFilePos iCurrent)
 1564 {
 1565   if (iLogFileSize == 0)
 1566     return "";
 1567 
 1568   GetLine (iCurrent);
 1569   return RequestToTime (pcReqBuff);
 1570 }
 1571 
 1572 
 1573 bool ReportOptions()
 1574 {
 1575   string sTemp, sTemp1, sTemp2, sWordMode, sBUTmode, sBDTmode, sBDImode;
 1576   int iTemp, iKey;
 1577   bool bGood;
 1578   
 1579   for (;;)
 1580   {
 1581     MyCls();
 1582     
 1583     if (iRepStart == 0)
 1584       sTemp1 = "begining of log";
 1585     else
 1586       sTemp1 = FilePosToTime (iRepStart);
 1587 
 1588     if (iRepEnd == 0)
 1589       sTemp2 = "end of log";
 1590     else
 1591       sTemp2 = FilePosToTime (iRepEnd);
 1592 
 1593     switch (iRepWordHits)
 1594     {
 1595       case no_word:
 1596         sWordMode = "none";
 1597         break;
 1598 
 1599       case text:
 1600         sWordMode = "normal text";
 1601         break;
 1602 
 1603       case CSV:
 1604         sWordMode = "CSV";
 1605         break;
 1606 
 1607       default:
 1608         sWordMode = "<error>";
 1609         break;
 1610     }
 1611     
 1612     switch (iRepBUT)
 1613     {
 1614       case nBUT_none:
 1615         sBUTmode = "none";
 1616         break;
 1617 
 1618       case nBUT_notveto:
 1619         sBUTmode = "domains not skipped";
 1620         break;
 1621 
 1622       case nBUT_all:
 1623         sBUTmode = "all domains";
 1624         break;
 1625 
 1626       default:
 1627         sBUTmode = "<error>";
 1628         break;
 1629     }
 1630 
 1631     switch (iRepBDT)
 1632     {
 1633       case nBDT_none:
 1634         sBDTmode = "none";
 1635         break;
 1636 
 1637       case nBDT_all:
 1638         sBDTmode = "all users";
 1639         break;
 1640 
 1641       case nBDT_user:
 1642         sBDTmode = "only user " + sRepBDTuser;
 1643         break;
 1644 
 1645       default:
 1646         sBDTmode = "<error>";
 1647         break;
 1648     }
 1649 
 1650     switch (iRepBDI)
 1651     {
 1652       case nBDI_notveto:
 1653         sBDImode = "domains not skipped";
 1654         break;
 1655 
 1656       case nBDI_all:
 1657         sBDImode = "all domains";
 1658         break;
 1659 
 1660       default:
 1661         sBDImode = "<error>";
 1662         break;
 1663     }
 1664 
 1665     con << "Log A Report Options\n"
 1666         << "\na. start from " << sTemp1
 1667         << "\nb. finish at " << sTemp2
 1668         << "\nc. report file name: " << sRepFileName
 1669         << "\nd. title of report: " << sRepTitle
 1670         << "\ne. cache hit report: " << bRepCacheReport
 1671         << "\nf. user bandwidth totals: " << sBUTmode
 1672         << "\ng. domain bandwidth totals: " << sBDTmode
 1673         << "\nh. domain bandwidth includes: " << sBDImode
 1674         << "\ni. word hit action: " << sWordMode
 1675         << "\nj. normal text: total columns " << iRepColumns 
 1676         << "\nk. normal text: split long lines: " << bRepSplit
 1677         << "\nl. normal text: show request size: " << bRepShowSize
 1678         << "\nm. CSV: field seperator: \"" << sRepSeperator << "\""
 1679         << "\nn. CSV: target columns " << iRepCSVcols
 1680         << "\n\nPress <enter> to go or q to quit:";
 1681 
 1682     iKey = getch();
 1683     con << "\n\n";
 1684     switch (iKey)
 1685     {
 1686       case 'a':
 1687         iRepStart = iRepStart == 0 ? iLinePos : 0;
 1688         break;
 1689         
 1690       case 'b':
 1691         iRepEnd = iRepEnd == 0 ? iLinePos : 0;
 1692         break;
 1693 
 1694       case 'c':
 1695         con << "New file name : ";
 1696         GetFileName (sTemp, &bGood);
 1697         if (bGood)
 1698           sRepFileName = sTemp;
 1699         break;
 1700 
 1701       case 'd':
 1702         con << "New title : ";
 1703         GetText (sRepTitle);
 1704         break;
 1705 
 1706       case 'e':
 1707         bRepCacheReport = !bRepCacheReport;
 1708         break;
 1709 
 1710       case 'f':
 1711         if (++iRepBUT >= 3)
 1712           iRepBUT = 0;
 1713         break;
 1714 
 1715       case 'g':
 1716         MyCls();
 1717         if (iRepFocus == user)
 1718           sTemp1 = sRepFocus;
 1719         else
 1720           sTemp1 = "";
 1721         sTemp2 = "";
 1722         GetLine (iLinePos);
 1723         if (NullText (pcReqBuff))
 1724           sTemp2 = "";
 1725         else
 1726           sTemp2 = GetUserName (pcReqBuff, false);
 1727         con << "Bandwidth By Domain Option:-\n\n"
 1728                "1.  no domain totals\n"
 1729                "2.  all users contribute to domain totals\n"
 1730                "3.  only focus user " << sTemp1 << "\n"
 1731                "4.  only current user " << sTemp2 << "\n"
 1732                "5.  specify a different user\n\n"
 1733                "Your choice: ";
 1734         iKey = getch();
 1735         con << "\n\n";
 1736         sTemp = "NON3";
 1737         switch (iKey)
 1738         {
 1739           case '1':
 1740             iRepBDT = nBDT_none;
 1741             break;
 1742 
 1743           case '2':
 1744             iRepBDT = nBDT_all;
 1745             break;
 1746 
 1747           case '3':
 1748             sTemp = sTemp1;
 1749             break;
 1750 
 1751           case '4':
 1752             sTemp = sTemp2;
 1753             break;
 1754 
 1755           case '5':
 1756             con << "Enter user name: ";
 1757             GetText (sTemp);
 1758             con << "\n";
 1759             StrLwr (sTemp);
 1760             break;
 1761         }
 1762         if (sTemp != "NON3")
 1763         {
 1764           if (sTemp == "")
 1765           {
 1766             con << "No user there. Press any key. ";
 1767             getch();
 1768           }
 1769           else
 1770           {
 1771             sRepBDTuser = sTemp;
 1772             iRepBDT = nBDT_user;
 1773           }
 1774         }
 1775         break;
 1776 
 1777       case 'h':
 1778         if (++iRepBDI >= 2)
 1779           iRepBDI = 0;
 1780         break;
 1781 
 1782       case 'i':
 1783         if (++iRepWordHits >= 3)
 1784           iRepWordHits = 0;
 1785         break;
 1786 
 1787       case 'j':
 1788         con << "New normal columns: ";
 1789         GetText (sTemp);
 1790         iTemp = atoi (sTemp.c_str());
 1791         if ((iTemp >= 45) && (iTemp <= 256))
 1792           iRepColumns = iTemp;
 1793         break;
 1794         
 1795       case 'k':
 1796         bRepSplit = !bRepSplit;
 1797         break;
 1798         
 1799       case 'l':
 1800         bRepShowSize = !bRepShowSize;
 1801         break;
 1802         
 1803       case 'm':
 1804         con << "New CSV seperator: ";
 1805         GetText (sRepSeperator);
 1806         break;
 1807 
 1808       case 'n':
 1809         con << "New CSV target max size: ";
 1810         GetText (sTemp);
 1811         iTemp = atoi (sTemp.c_str());
 1812         if ((iTemp > 10) && (iTemp < 600))
 1813           iRepCSVcols = iTemp;
 1814         break;
 1815 
 1816       case cEOL:
 1817       case cCR:
 1818         return true;
 1819 
 1820       case 'q':
 1821         return false;
 1822     }
 1823   }
 1824 }
 1825 
 1826 
 1827 void KeepExtension (string& sTarget, int iColumns)
 1828 {
 1829   int iLen = sTarget.length();
 1830   int iIndex, iRemove, iDomainStart, iPathStart;
 1831   
 1832   if ((iRemove = iLen - iColumns) < 0)
 1833     return;
 1834     
 1835   if (!bRepKeepExt)
 1836   {
 1837     sTarget.replace (iColumns, iRemove, "");
 1838     return;
 1839   }
 1840   
 1841   iIndex = iLen - 1;
 1842   for (;;)
 1843   {
 1844     if (iIndex < 0)
 1845     {
 1846       iDomainStart = 0;
 1847       break;
 1848     }
 1849     if (sTarget [iIndex] == ' ')
 1850     {
 1851       iDomainStart = iIndex + 1;
 1852       break;
 1853     }
 1854     iIndex--;
 1855   }
 1856 
 1857   iIndex = iDomainStart;
 1858   for (;;)
 1859   {
 1860     if (iIndex >= iLen)
 1861     {
 1862       iPathStart = iLen;
 1863       break;
 1864     }
 1865     if (sTarget [iIndex] == '/')
 1866     {
 1867       iPathStart = iIndex + 1;
 1868       break;
 1869     }
 1870     iIndex++;
 1871   }
 1872 
 1873   if (iLen - iPathStart >= iRemove)
 1874   {
 1875     sTarget.replace (iPathStart, iRemove, "");
 1876     return;
 1877   }
 1878   
 1879   sTarget.replace (iColumns, iRemove, "");
 1880 }
 1881 
 1882 
 1883 inline sRecordPointer sRecordPointer::operator= (const sRecordPointer& sFrom)
 1884 {
 1885   iBytes = sFrom.iBytes;
 1886   iHits = sFrom.iHits;
 1887   iRecordNumber = sFrom.iRecordNumber;
 1888   return *this;
 1889 }
 1890 
 1891 
 1892 void sListRecords::Empty()
 1893 {
 1894   while (!vList.empty())
 1895     vList.pop_back();
 1896 }
 1897 
 1898 
 1899 bool CompareBytes (const sRecordPointer& rLeft, const sRecordPointer& rRight)
 1900 {
 1901   if (rLeft.iBytes < rRight.iBytes)
 1902     return false;
 1903   else
 1904     return true;
 1905 }
 1906 
 1907 
 1908 bool CompareHits (const sRecordPointer& rLeft, const sRecordPointer& rRight)
 1909 {
 1910   if (rLeft.iHits > rRight.iHits)
 1911     return false;
 1912   else
 1913     return true;
 1914 }
 1915 
 1916 
 1917 void sListRecords::Sort (eSortType nSelect)
 1918 {
 1919   bool bDone, bSwitch;
 1920   int iIndex;
 1921   sRecordPointer rTemp;
 1922   
 1923   if (vList.size() < 2)
 1924     return;
 1925     
 1926   if (!bRepMySort)
 1927   {
 1928     switch (nSelect)
 1929     {
 1930       case nSortBytes:
 1931         iSortFlag = 1014;
 1932         sort (vList.begin(), vList.end(), CompareBytes);
 1933         iSortFlag = 0;
 1934         return;
 1935 
 1936       case nSortHits:
 1937         iSortFlag = 1015;
 1938         sort (vList.begin(), vList.end(), CompareHits);
 1939         iSortFlag = 0;
 1940         return;
 1941     }
 1942   }
 1943   
 1944   iSortFlag = 824;
 1945   
 1946   for (;;)
 1947   {
 1948     bDone = true;
 1949     for (iIndex = vList.size() - 2; iIndex >= 0; iIndex--)
 1950     {
 1951       bSwitch = false;
 1952       switch (nSelect)
 1953       {
 1954         case nSortHits:
 1955           if (vList [iIndex].iHits > vList [iIndex + 1].iHits)
 1956             bSwitch = true;
 1957           break;
 1958 
 1959         case nSortBytes:
 1960           if (vList [iIndex].iBytes < vList [iIndex + 1].iBytes)
 1961             bSwitch = true;
 1962           break;
 1963       }
 1964       if (bSwitch)
 1965       {
 1966         bDone = false;
 1967         rTemp = vList [iIndex];
 1968         vList [iIndex] = vList [iIndex + 1];
 1969         vList [iIndex + 1] = rTemp;
 1970       }
 1971     }
 1972     if (bDone)
 1973     {
 1974       iSortFlag = 0;
 1975       return;
 1976     }
 1977   }
 1978 }
 1979 
 1980 
 1981 class GenerateReport
 1982 {
 1983   public:
 1984     void Run();
 1985 
 1986     enum eDone {done, ioerror, keybreak};
 1987 
 1988   private:
 1989     void WriteReportLine (const string& sText);
 1990     void HandleCSV();
 1991     void HandleNormal();
 1992     void ZeroCounters();
 1993 
 1994     bool bLogOutput;
 1995     bool bMadeOutput;
 1996     FILE* fOutput;
 1997     string sBytePad, sFlags;
 1998     time_t iLastime_t;
 1999     tByteTotal iRequestSize;
 2000     string sStatusLine;
 2001     bool bDisplayOutput;
 2002     
 2003     vector <sUserRecord> vUserDetails;
 2004     vector <sDomainRecord> vDomainDetails;
 2005     sListRecords rUserPointers;
 2006     sListRecords rDomainPointers;
 2007 };
 2008 
 2009 
 2010 // to compile on Slackware and other oldies don't
 2011 // use clear() because of naming problems: ncurses
 2012 // has a macro "clear" and it takes over.
 2013 inline void GenerateReport::ZeroCounters()
 2014 {
 2015   while (!vUserDetails.empty())
 2016     vUserDetails.pop_back();
 2017   while (!vDomainDetails.empty())
 2018     vDomainDetails.pop_back();
 2019   rUserPointers.Empty();
 2020   rDomainPointers.Empty();
 2021 }
 2022 
 2023 
 2024 inline void GenerateReport::WriteReportLine (const string& sText)
 2025 {
 2026   if (bLogOutput == false)
 2027     return;
 2028 
 2029   if (bDisplayOutput)
 2030   {
 2031     EraseStatusLine();
 2032     refresh();
 2033     printf ("%s\r", sText.c_str());
 2034     ShowHighLightMessage (sStatusLine); /// don't like this bit
 2035   }
 2036     
 2037   if (fwrite (sText.c_str(), 1, sText.length(), fOutput) != sText.length())
 2038     throw ioerror;
 2039 }
 2040 
 2041 
 2042 inline void GenerateReport::HandleCSV()
 2043 {
 2044   static string sTemp;
 2045   static string sDispLine;
 2046   static time_t iTime;
 2047 
 2048   GetColumn (pcReqBuff, iSizeCol, sTemp);
 2049   sDispLine = sTemp + sRepSeperator;
 2050   sTemp = GetUserName (pcReqBuff, true);
 2051   sDispLine += sTemp + sRepSeperator;
 2052   sDispLine += sFlags + sRepSeperator;
 2053   GetColumn (pcReqBuff, iTimeCol, sTemp);
 2054   sDispLine += sTemp + sRepSeperator;
 2055   iTime = GetTimeNumber (pcReqBuff);
 2056   CalcTime (iTime, sTemp);
 2057   RemoveSeconds (sTemp);
 2058   sDispLine += sTemp + sRepSeperator;
 2059   GetColumn (pcReqBuff, iTargetCol, sTemp);
 2060   KeepExtension (sTemp, iRepCSVcols);
 2061   sDispLine += sTemp + "\n";
 2062   WriteReportLine (sDispLine);
 2063 }
 2064 
 2065 
 2066 inline void GenerateReport::HandleNormal()
 2067 {
 2068   static time_t iTime;
 2069   const time_t iTimeMod = 60;
 2070   static string sTemp1;
 2071   static string sTemp2;
 2072   static string sTarget;
 2073   static string sTime;
 2074   int iColsNotCSV, iCount, iDLfrom, iDLsize;
 2075   
 2076   iTime = GetTimeNumber (pcReqBuff);
 2077   iTime = iTime - (iTime % iTimeMod);
 2078   if (iTime != iLastime_t)
 2079   {
 2080     iLastime_t = iTime;
 2081     CalcTime (iTime, sTime);
 2082     RemoveSeconds (sTime);
 2083   }
 2084   else
 2085     sTime = "";
 2086 
 2087   if (bRepShowSize)
 2088     sTemp1 = RightJustify (ItoCommas (iRequestSize), nSizeCols) + " ";
 2089   else
 2090     sTemp1 = "";
 2091 
 2092   GetPaddedUserName (pcReqBuff, sTemp2);
 2093   sTemp1 += sTemp2 + " " + sFlags + " ";
 2094   if (sBytePad == "")
 2095     sBytePad.assign (sTemp1.length(), ' ');
 2096 
 2097   if (sTime != "")
 2098     WriteReportLine (sBytePad + sTime + "\n");
 2099 
 2100   GetColumn (pcReqBuff, iTargetCol, sTarget);
 2101   RemoveSlashes (sTarget);
 2102 
 2103   if (bRepSplit == false)
 2104   {
 2105      sTemp1 += sTarget;
 2106      KeepExtension (sTemp1, iRepColumns);
 2107      sTemp1 += "\n";
 2108      WriteReportLine (sTemp1);
 2109   }
 2110   else
 2111   {
 2112     iColsNotCSV = iRepColumns - sBytePad.length();
 2113     if (iRepColumns < 5)
 2114       iRepColumns = 5;
 2115     iDLfrom = 0;
 2116     iCount = sTarget.length();
 2117     iDLsize = iCount;
 2118     if (iDLsize > iColsNotCSV)
 2119       iDLsize = iColsNotCSV;
 2120     for (;;)
 2121     {
 2122       WriteReportLine (sTemp1 + sTarget.substr (iDLfrom, iDLsize) + "\n");
 2123       iDLfrom += iDLsize;
 2124       if (iDLfrom >= iCount)
 2125         break;
 2126       iDLsize = iCount - iDLfrom;
 2127       if (iDLsize > iColsNotCSV)
 2128         iDLsize = iColsNotCSV;
 2129       sTemp1 = sBytePad;
 2130     }
 2131   }
 2132 }
 2133 
 2134 
 2135 // make the report
 2136 void GenerateReport::Run()
 2137 {
 2138   tFilePos iCurrent, iTempPos, iStopPos, iScanArea;
 2139   eDone iResult;
 2140   int iHits, iVectorCount, iNumRecords, iThisEntry, iPointer;
 2141   tByteTotal iByteTotal, iSearchHitTotal, iFocusTotal, iDashTotal;
 2142   tByteTotal iUserBandWidth, iDomainBandWidth;
 2143   tByteTotal iCacheHitBytes;
 2144   int iCacheHitRequests, iRequests;
 2145   int iFile, iRead;
 2146   string sDispLine, sTemp, sTemp2, sUserName;
 2147   sRecordPointer rPointer;
 2148   sUserRecord rUserRecord;
 2149   sDomainRecord rDomainRecord;
 2150   bool bHandled = false, bTemp;
 2151   char cLetter;
 2152   time_t iTime;
 2153   int iLine;
 2154     
 2155   bLogOutput = sRepFileName == "" ? false : true;
 2156   iCurrent = iRepStart;
 2157   iStopPos = iRepEnd == 0 ? -1 : iRepEnd;
 2158   iScanArea = (iRepEnd == 0 ? iLastPage : iRepEnd) - iRepStart;
 2159   sBytePad = "";
 2160   iByteTotal = iSearchHitTotal = iFocusTotal = iDashTotal = 0;
 2161   iUserBandWidth = iDomainBandWidth = 0;
 2162   iCacheHitBytes = iCacheHitRequests = iRequests = 0;
 2163   iHits = 0;
 2164   iLine = 1;
 2165   iLastime_t = -1;
 2166    
 2167   if (bLogOutput)
 2168   {
 2169     fOutput = fopen (string (sPathToFiles + "/" + sRepFileName)
 2170                      .c_str(), "w");
 2171     if (fOutput == NULL)
 2172     {
 2173       sStatusMessage = "Could not write to " + sRepFileName;
 2174       return;
 2175     }
 2176   }
 2177 
 2178   ZeroCounters();
 2179   GoForNoDelay();
 2180   bMadeOutput = false;
 2181   iTime = time (0);
 2182 
 2183   sStatusLine = "";
 2184   bDisplayOutput = true;
 2185   
 2186   try
 2187   {
 2188     if (sRepTitle != "")
 2189       WriteReportLine (sRepTitle + "\n\n");
 2190 
 2191     sTemp = FilePosToTime (iCurrent);
 2192     if (iRepEnd == 0)
 2193       sTemp2 = FilePosToTime (iLastPage);
 2194     else
 2195       sTemp2 = FilePosToTime (iRepEnd);
 2196 
 2197     if (sTemp2 == sTemp)
 2198       WriteReportLine ("For " + sTemp + "\n\n");
 2199     else
 2200       WriteReportLine ("For " + sTemp + " to " + sTemp2 + "\n\n");
 2201     
 2202     for (;;)
 2203     {
 2204       if ((iStopPos > 0) && (iCurrent >= iStopPos))
 2205         throw done;
 2206       iTempPos = GetLine (iCurrent);
 2207       if ((iTempPos == 0) || (iTempPos == iCurrent))
 2208         throw done;
 2209       bTemp = false;
 2210       if (!NullText (pcReqBuff))
 2211       {
 2212         GetRequestFlags (pcReqBuff, sFlags);
 2213         if (sFlags.find ("d") == string::npos) // check for squid denied
 2214           bTemp = true;
 2215       }
 2216       if (bTemp)
 2217       {
 2218         iRequestSize = GetRequestSize (pcReqBuff);
 2219         iByteTotal += iRequestSize;
 2220         iRequests++;
 2221         GetColumn (pcReqBuff, iUserCol, sTemp);
 2222         if (sTemp == "-")
 2223           iDashTotal += iRequestSize;
 2224           
 2225         bHandled = SearchHit (sFlags);
 2226         if (bHandled)
 2227         {
 2228           iHits++;
 2229           iSearchHitTotal += iRequestSize;
 2230           if (bLogOutput)
 2231           {
 2232             switch (iRepWordHits)
 2233             {
 2234               case text:
 2235                 HandleNormal();
 2236                 bMadeOutput = true;
 2237                 break;
 2238                 
 2239               case CSV:
 2240                 HandleCSV();
 2241                 bMadeOutput = true;
 2242                 break;
 2243             }
 2244           }
 2245         }
 2246 
 2247         bTemp = false;
 2248         if (iRepBUT == nBUT_all)
 2249           bTemp = true;
 2250         if ((iRepBUT == nBUT_notveto) && (sFlags [0] != '-'))
 2251           bTemp = true;
 2252         if (iRequestSize <= 0)
 2253           bTemp = false;
 2254 
 2255         if (bTemp)
 2256         {
 2257           iUserBandWidth += iRequestSize;
 2258           sUserName = GetUserName (pcReqBuff, false);
 2259           if (sUserName != "")
 2260           {
 2261             iNumRecords = rUserPointers.vList.size();
 2262             iThisEntry = -1;
 2263             for (iVectorCount = iNumRecords - 1;
 2264                  iVectorCount >= 0;
 2265                  iVectorCount--)
 2266             {
 2267               iPointer = rUserPointers.vList [iVectorCount].iRecordNumber;
 2268               if (sUserName == vUserDetails [iPointer].sLoginName)
 2269               {
 2270                 iThisEntry = iVectorCount;
 2271                 rUserPointers.vList [iVectorCount].iBytes += iRequestSize;
 2272                 rUserPointers.vList [iVectorCount].iHits++;
 2273                 break;
 2274               }
 2275             }
 2276             if (iThisEntry == -1)
 2277             {
 2278               rPointer.iBytes = iRequestSize;
 2279               rPointer.iHits = 1;
 2280               rPointer.iRecordNumber = iNumRecords;
 2281               rUserPointers.vList.push_back (rPointer);
 2282               
 2283               rUserRecord.sLoginName = sUserName;
 2284               rUserRecord.sFullName = "";
 2285               vUserDetails.push_back (rUserRecord);
 2286             }
 2287           }
 2288         }
 2289 
 2290         bTemp = false;
 2291         if (iRepBDT == nBDT_all)
 2292           bTemp = true;
 2293         if (iRepBDT >= nBDT_user)
 2294         {
 2295           sUserName = GetUserName (pcReqBuff, false);
 2296           if (sUserName == sRepBDTuser)
 2297             bTemp = true;
 2298         }
 2299         if ((iRepBDI == nBDI_notveto) && (sFlags [0] == '-'))
 2300           bTemp = false;
 2301         if (iRequestSize <= 0)
 2302           bTemp = false;
 2303 
 2304         if (bTemp)
 2305         {
 2306           iDomainBandWidth += iRequestSize;
 2307           GetDomainName (pcReqBuff, sTemp);
 2308           iNumRecords = rDomainPointers.vList.size();
 2309           iThisEntry = -1;
 2310           for (iVectorCount = iNumRecords - 1;
 2311                iVectorCount >= 0;
 2312                iVectorCount--)
 2313           {
 2314             iPointer = rDomainPointers.vList [iVectorCount].iRecordNumber;
 2315             if (sTemp == vDomainDetails [iPointer].sDomainName)
 2316             {
 2317               iThisEntry = iVectorCount;
 2318               rDomainPointers.vList [iVectorCount].iBytes += iRequestSize;
 2319               rDomainPointers.vList [iVectorCount].iHits++;
 2320               break;
 2321             }
 2322           }
 2323           if (iThisEntry == -1)
 2324           {
 2325             rPointer.iBytes = iRequestSize;
 2326             rPointer.iHits = 1;
 2327             rPointer.iRecordNumber = iNumRecords;
 2328             rDomainPointers.vList.push_back (rPointer);
 2329               
 2330             rDomainRecord.sDomainName = sTemp;
 2331             vDomainDetails.push_back (rDomainRecord);
 2332           }
 2333         }
 2334 
 2335         if ((sFlags [2] >= 'A') && (sFlags [2] <= 'Z'))
 2336         {
 2337           iCacheHitBytes += iRequestSize;
 2338           iCacheHitRequests++;
 2339         }
 2340       }
 2341 
 2342       if ((iStopPos > 0) && (iTempPos >= iStopPos))
 2343         throw done;
 2344       iCurrent = iTempPos;
 2345 
 2346       if ((iRepFast) && (iLine % iRepFast == 0))
 2347       {
 2348         rUserPointers.Sort (sListRecords::nSortHits);
 2349         rDomainPointers.Sort (sListRecords::nSortHits);
 2350       }
 2351       iLine++;
 2352 
 2353       if (CheckInterrupt())
 2354         throw keybreak;
 2355 
 2356       if (bHandled || CheckTicker())
 2357       {      
 2358         sTemp = string ("complete: ") +
 2359                 ItoCommas (iByteTotal) + " bytes, " +
 2360                 ItoCommas (iHits) + " hits";
 2361         UpdateTicker (sTemp.c_str(), iCurrent - iRepStart, iScanArea);
 2362       }
 2363     }
 2364   }
 2365   catch (eDone iTemp)
 2366   {
 2367     iResult = iTemp;
 2368   }
 2369 
 2370   bDisplayOutput = false;
 2371   MyCls();
 2372 
 2373   if (iResult == done)
 2374   {
 2375     try
 2376     {
 2377       WriteReportLine ("\n" +
 2378         RightJustify (ItoCommas (iByteTotal), nSizeCols) +
 2379         " scanned bytes, " + ItoCommas (iHits) + " hits total " + 
 2380         ItoCommas (iSearchHitTotal) + " bytes\n");
 2381 
 2382       WriteReportLine ("\nBytes attributed to \"-\" user: " +
 2383         ItoCommas (iDashTotal) + "\n");
 2384 
 2385       if (bRepCacheReport)
 2386       {
 2387         bMadeOutput = true;
 2388         WriteReportLine ("\nCache hit statistics:\n");
 2389         CalcPercentage (iCacheHitRequests, iRequests, sTemp);
 2390         WriteReportLine (ItoCommas (iCacheHitRequests) + " requests out of "
 2391           + ItoCommas (iRequests) + ": " + sTemp + "\n");
 2392         CalcPercentage (iCacheHitBytes, iByteTotal, sTemp);
 2393         WriteReportLine (ItoCommas (iCacheHitBytes) + " bytes out of "
 2394           + ItoCommas (iByteTotal) + ": " + sTemp + "\n");
 2395       }
 2396             
 2397       if (iRepBUT != nBUT_none)
 2398       {
 2399         ShowHighLightMessage ("Sorting users' totals...");
 2400         rUserPointers.Sort (sListRecords::nSortBytes);
 2401         ShowHighLightMessage ("Finding full user names...");
 2402 
 2403         iFile = open (sUsersFile.c_str(), O_RDONLY);
 2404         if (iFile > 0)
 2405         {
 2406           sDispLine = "";
 2407           iNumRecords = vUserDetails.size();
 2408 
 2409           for (;;)
 2410           {
 2411             iRead = read (iFile, &cLetter, 1);
 2412             if (iRead < 1)
 2413               break;
 2414             if ((cLetter == cEOL) || (cLetter == cCR))
 2415             {
 2416               if (sDispLine != "")
 2417               {
 2418                 GetColumn (sDispLine.c_str(), 1, sTemp);
 2419                 for (iVectorCount = 0; iVectorCount < iNumRecords;
 2420                      iVectorCount++)
 2421                 {
 2422                   if (sTemp == vUserDetails [iVectorCount].sLoginName)
 2423                   {
 2424                     sDispLine.replace (0, sTemp.length() + 1, "");
 2425                     vUserDetails [iVectorCount].sFullName = sDispLine;
 2426                     break;
 2427                   }
 2428                 }
 2429                 sDispLine = "";
 2430               }
 2431             }
 2432             else
 2433               sDispLine += cLetter;
 2434           }
 2435           close (iFile);
 2436         }
 2437 
 2438         ShowHighLightMessage ("Done finding full user names.");
 2439         
 2440         sTemp = "\n" + RightJustify (ItoCommas (iUserBandWidth), nSizeCols);
 2441         if (iRepBUT == nBUT_notveto)
 2442           sTemp += " bytes not skipped domains";
 2443         else
 2444         if (iRepBUT == nBUT_all)
 2445           sTemp += " bytes all domains";
 2446         WriteReportLine (sTemp + "\n");
 2447 
 2448         iNumRecords = rUserPointers.vList.size();
 2449         for (iVectorCount = 0; iVectorCount < iNumRecords; iVectorCount++)
 2450         {
 2451           bMadeOutput = true;
 2452           sTemp = RightJustify (
 2453                     ItoCommas (
 2454                       rUserPointers.vList [iVectorCount].iBytes), nSizeCols);
 2455           if (iRepWordHits == CSV)
 2456             sTemp += sRepSeperator;
 2457           else
 2458             sTemp += ' ';
 2459           iPointer = rUserPointers.vList [iVectorCount].iRecordNumber;
 2460           sTemp += vUserDetails [iPointer].sLoginName;
 2461           if (vUserDetails [iPointer].sFullName != "")  
 2462             sTemp += " : " + vUserDetails [iPointer].sFullName;
 2463           WriteReportLine (sTemp + "\n");
 2464         }
 2465       }
 2466       
 2467       if (iRepBDT != nBDT_none)
 2468       {
 2469         ShowHighLightMessage ("Sorting destination domain totals...");
 2470         rDomainPointers.Sort (sListRecords::nSortBytes);
 2471         ShowHighLightMessage ("Done sorting domain totals.");
 2472 
 2473         WriteReportLine ("\n");
 2474         if (iRepBDT >= nBDT_user)
 2475         {
 2476           sTemp = GetLongUserName (sRepBDTuser);
 2477           if (sTemp != "")
 2478             WriteReportLine (sRepBDTuser + " is " + sTemp + "\n");
 2479         }
 2480 
 2481         sTemp = RightJustify (ItoCommas (iDomainBandWidth), nSizeCols);
 2482         if (iRepBDT >= nBDT_user)
 2483           sTemp += " bytes user " + sRepBDTuser + " bandwidth: ";
 2484         else
 2485           sTemp += " bytes all users bandwidth: ";
 2486 
 2487         if (iRepBDI == nBDI_notveto)
 2488           sTemp += "domains not skipped";
 2489         if (iRepBDI == nBDI_all)
 2490           sTemp += "all domains";
 2491         WriteReportLine (sTemp + "\n");
 2492 
 2493         iNumRecords = rDomainPointers.vList.size();
 2494         for (iVectorCount = 0; iVectorCount < iNumRecords; iVectorCount++)
 2495         {
 2496           bMadeOutput = true;
 2497           sTemp = RightJustify (
 2498                    ItoCommas (
 2499                     rDomainPointers.vList [iVectorCount].iBytes), nSizeCols);
 2500           if (iRepWordHits == CSV)
 2501             sTemp += sRepSeperator;
 2502           else
 2503             sTemp += ' ';
 2504           iPointer = rDomainPointers.vList [iVectorCount].iRecordNumber;
 2505           sTemp += vDomainDetails [iPointer].sDomainName;
 2506           WriteReportLine (sTemp + "\n");
 2507         }
 2508       }
 2509     }
 2510     catch (eDone iTemp)
 2511     {
 2512       iResult = iTemp;
 2513     }
 2514   }
 2515 
 2516   ZeroCounters();
 2517   if (bLogOutput)
 2518     fclose (fOutput);
 2519     
 2520   iTime = time (0) - iTime;
 2521 
 2522   if (wTemp)
 2523     nodelay (wTemp, false);
 2524 
 2525   if (iResult == ioerror)
 2526   {
 2527     sStatusMessage = "Could not append to " + sRepFileName;
 2528     return;
 2529   }
 2530 
 2531   if (iResult == keybreak)
 2532   {
 2533     sStatusMessage = "Report canceled.";
 2534     return;
 2535   }
 2536 
 2537   sTemp = ItoCommas (iByteTotal) + " scanned bytes, " +
 2538           ItoCommas (iHits) + " hits - " + ItoS (iTime) + " seconds";
 2539 
 2540   if (bLogOutput)    
 2541   {
 2542     if (!bMadeOutput)
 2543     {
 2544       sStatusMessage = "No report: " + sTemp + ".";
 2545       return;
 2546     }
 2547     sCurrentReport = sRepFileName;
 2548     sDispLine = sRepFileName + " made: " + sTemp + "; View (y/n)? ";
 2549     if (PromptForKey (sDispLine) == 'y')
 2550     {
 2551       sTemp = sViewer + " " +sPathToFiles + "/" + sRepFileName;
 2552       RunProgramInConsole (sTemp);
 2553     }
 2554     sStatusMessage = "Press 'n' for misc. operations you can do on "
 2555                      + sRepFileName;
 2556   }
 2557   else
 2558   {
 2559     sStatusMessage = "Summary: " + sTemp + ".";
 2560   }
 2561 }
 2562 
 2563 
 2564 // prepend a word to the search list
 2565 void PrependFindText()
 2566 {
 2567   string sText;
 2568 
 2569   PromptForText ("find text: ", sText);
 2570   if (sText != "")
 2571   {
 2572     sText += cSeperator;
 2573     sWords = sText + sWords;
 2574     sStatusMessage = "Now press <- or ->";
 2575   }
 2576 }
 2577 
 2578 
 2579 void AppendFindText()
 2580 {
 2581   string sText;
 2582 
 2583   PromptForText ("Find text: ", sText);
 2584   if (sText != "")
 2585   {
 2586     sWords += sText;
 2587     sWords += cSeperator;
 2588     sStatusMessage = "Now press <- or ->";
 2589   }
 2590 }
 2591 
 2592 
 2593 // size hit sub menu
 2594 void SizeHitOptions()
 2595 {
 2596   int iCount, iSize, iTemp;
 2597   const char *pcLetter;
 2598   string sTemp;
 2599 
 2600   for (;;)
 2601   {
 2602     MyCls();
 2603     con << "Hit sizes are:-\n\n0: <off>\n";
 2604     pcLetter = sSizeHits.c_str();
 2605     iCount = 1;
 2606     for (;;)
 2607     {
 2608       iSize = NextCatchSize (&pcLetter);
 2609       if (iSize <= 0)
 2610         break;
 2611       con << ItoS (iCount++) << ": " << ItoCommas (iSize) << "\n";
 2612     }
 2613 
 2614     if (iSizeHitGrade == 0)
 2615       con << "\nNo hit size set.\n";
 2616     else
 2617       con << "\nCurrent hit grade is " << iSizeHitGrade << ": " +
 2618              ItoCommas (iSizeHitBytes) + " bytes\n";
 2619     
 2620     con << "\n-1 to -9 deletes entry, 0 to 9 sets grade.\n"
 2621            "A large number inserts a new grade.\n"
 2622            "Enter nothing to dismiss.\n\nEnter choice: ";
 2623     GetText (sTemp);
 2624     if (sTemp == "")
 2625       return;
 2626     iTemp = atoi (sTemp.c_str());
 2627 
 2628     if (iTemp >= 10)
 2629     {
 2630       if (iCount >= 10)
 2631       {
 2632         con << "\nNot enough room!\n\nPress a key: ";
 2633         getch();
 2634       }
 2635       else
 2636       {
 2637         pcLetter = sSizeHits.c_str();
 2638         sTemp = "";
 2639         iCount = 1;
 2640         for (;;)
 2641         {
 2642           iSize = NextCatchSize (&pcLetter);
 2643           if (iTemp == iSize)
 2644             iTemp = -1;
 2645           if (iSize == 0)
 2646           {
 2647             if (iTemp >= 10)
 2648               sTemp += " " + ItoS (iTemp);
 2649             break;
 2650           }
 2651           if ((iTemp >= 10) && (iTemp < iSize))
 2652           {
 2653             sTemp += " " + ItoS (iTemp);
 2654             iSizeHitGrade = iCount;
 2655             iSizeHitBytes = iTemp;
 2656             iTemp = -1;
 2657           }
 2658           sTemp += " " + ItoS (iSize);
 2659           iCount++;
 2660         }
 2661         if ((sTemp != "") && (sTemp [0] == ' '))
 2662           sTemp.replace (0, 1, "");
 2663         sSizeHits = sTemp;
 2664       }   
 2665     }
 2666     else
 2667     if (iTemp < 0)
 2668     {
 2669       pcLetter = sSizeHits.c_str();
 2670       iCount = -1;
 2671       sTemp = "";
 2672       for (;;)
 2673       {
 2674         iSize = NextCatchSize (&pcLetter);
 2675         if (iSize <= 0)
 2676           break;
 2677         if (iTemp != iCount--)
 2678           sTemp += " " + ItoS (iSize);
 2679       }
 2680       if ((sTemp != "") && (sTemp [0] == ' '))
 2681         sTemp.replace (0, 1, "");
 2682       sSizeHits = sTemp;
 2683 
 2684       iSizeHitGrade = 0;
 2685       pcLetter = sSizeHits.c_str();
 2686       iCount = 1;
 2687       for (;;)
 2688       {
 2689         iSize = NextCatchSize (&pcLetter);
 2690         if (iSize <= 0)
 2691           break;
 2692         if (iSize == iSizeHitBytes)
 2693         {
 2694           iSizeHitGrade = iCount;
 2695           break;
 2696         }
 2697         iCount++;
 2698       }
 2699       if (iSizeHitGrade == 0)
 2700         iSizeHitBytes = 0;
 2701     }
 2702     else
 2703     if (sTemp == "0")
 2704     {
 2705       iSizeHitGrade = 0;
 2706       iSizeHitBytes = 0;
 2707     }
 2708     else
 2709     if ((iTemp >= 1) && (iTemp <= 9))
 2710     {
 2711       pcLetter = sSizeHits.c_str();
 2712       iCount = 1;
 2713       for (;;)
 2714       {
 2715         iSize = NextCatchSize (&pcLetter);
 2716         if (iSize == 0)
 2717           break;
 2718         if (iTemp == iCount)
 2719         {
 2720           iSizeHitGrade = iCount;
 2721           iSizeHitBytes = iSize;
 2722           break;
 2723         }
 2724         iCount++;
 2725       }
 2726     }
 2727   }
 2728 }
 2729 
 2730 
 2731 int CountLeadingSpaces (const string& sText)
 2732 {
 2733   int iIndex = 0;
 2734   int iLen = sText.length();
 2735   
 2736   for (iIndex = 0; iIndex < iLen; iIndex++)
 2737     if (sText [iIndex] != ' ')
 2738       return iIndex;
 2739   return iLen;
 2740 }
 2741 
 2742 
 2743 bool IsDateTime (const string& sText)
 2744 {
 2745   string sTemp;
 2746   
 2747   GetColumn (sText.c_str(), 2, sTemp);
 2748   if (sTemp == "Sun") return true;
 2749   if (sTemp == "Mon") return true;
 2750   if (sTemp == "Tue") return true;
 2751   if (sTemp == "Wed") return true;
 2752   if (sTemp == "Thu") return true;
 2753   if (sTemp == "Fri") return true;
 2754   if (sTemp == "Sat") return true;
 2755   return false;
 2756 }
 2757 
 2758 
 2759 bool IsTimeSpan (const string& sText)
 2760 {
 2761   string sTemp;
 2762   
 2763   GetColumn (sText.c_str(), 1, sTemp);
 2764   if (sTemp != "For")
 2765     return false;
 2766   return IsDateTime (sText);
 2767 }
 2768 
 2769 
 2770 bool IsGrandTotal (const string& sText)
 2771 {
 2772   string sTemp;
 2773   
 2774   GetColumn (sText.c_str(), 2, sTemp);
 2775   if (sTemp != "scanned")
 2776     return false;
 2777   GetColumn (sText.c_str(), 3, sTemp);
 2778   if (sTemp != "bytes,")
 2779     return false;
 2780 
 2781   return true;
 2782 }
 2783 
 2784 
 2785 bool IsUsersRequest (const string& sText, const string& sUser)
 2786 {
 2787   string sTemp;
 2788   int iCount;
 2789   
 2790   for (iCount = 1; iCount <= 3; iCount++)
 2791   {
 2792     GetColumn (sText.c_str(), iCount, sTemp);
 2793     if (sTemp == sUser)
 2794       return true;
 2795   }
 2796   return false;
 2797 }
 2798 
 2799 
 2800 bool FilterReport (const string& sSrc, const string& sDest,
 2801                    const string& sUser)
 2802 {
 2803   int iIn, iOut, iLetter, iLen, iSpaces;
 2804   string sLine, sTime, sLastTime, sTemp;
 2805   bool bWrite, bRet, bSplitting, bTimed, bGrand;
 2806 
 2807   iSpaces = 0;
 2808   sTime = "";
 2809   bSplitting = bTimed = bGrand = bRet = false;
 2810   
 2811   iIn = open (string (sPathToFiles + "/" + sSrc).c_str(), O_RDONLY);
 2812   if (iIn <= 0)
 2813     sStatusMessage = "Can't open " + sSrc + " for reading.";
 2814   else
 2815   {
 2816     iOut = open (string (sPathToFiles + "/" + sDest).c_str(),
 2817                  O_WRONLY | O_CREAT | O_TRUNC, S_IREAD | S_IWRITE);
 2818     if (iOut <= 0)
 2819       sStatusMessage = "Can't open " + sDest + " for writing.";
 2820     else
 2821     {
 2822       sLine = "Web usage report on ";
 2823       sTemp = GetLongUserName (sUser);
 2824       if (sTemp == "")
 2825         sLine += sUser + "\n\n"; 
 2826       else
 2827         sLine += "\"" + sUser + "\" who is " + sTemp + "\n\n"; 
 2828       if (write (iOut, sLine.c_str(), sLine.length()) < 0)
 2829         sStatusMessage = "Can't write to  " + sDest;
 2830 
 2831       sLine = "";
 2832       for (;;)
 2833       {
 2834         iLetter = 0;
 2835         iLen = read (iIn, &iLetter, 1);
 2836         if ((iLen < 1) || (iLetter == cEOL))
 2837         {
 2838           bWrite = false;
 2839           sLastTime = sTime;
 2840           
 2841           if (!bWrite)
 2842           {
 2843             if (IsTimeSpan (sLine))
 2844             {
 2845               sLine += "\n";
 2846               bWrite = true;
 2847             }
 2848           }
 2849 
 2850           if (!bWrite)
 2851           {
 2852             if (IsDateTime (sLine))
 2853             {
 2854               sTime = sLine;
 2855               iSpaces = CountLeadingSpaces (sLine);
 2856             }
 2857           }
 2858 
 2859           if (!bWrite)
 2860           {
 2861             if (IsUsersRequest (sLine, sUser))
 2862             {
 2863               bRet = true;
 2864               if (bGrand)
 2865               {
 2866                 GetColumn (sLine.c_str(), 1, sTemp);
 2867                 if (sTemp == "")
 2868                   GetColumn (sLine.c_str(), 2, sTemp);
 2869                 if (sTemp == "")
 2870                   sTemp = "unkown";
 2871                 sLine = "Total bytes including small requests: " + sTemp
 2872                         + "\n";
 2873                 bWrite = true;
 2874                 iLen = 0;
 2875               }
 2876               else
 2877               {
 2878                 sTemp = "";
 2879                 if (!bTimed)
 2880                 {
 2881                   sTemp = sTime + "\n";
 2882                   sLastTime = sTime;
 2883                 }
 2884                 sLine = sTemp + sLine;
 2885                 bWrite = true;
 2886                 bSplitting = true;
 2887               }
 2888             }
 2889             else
 2890             {
 2891               if ((IsDateTime (sLine)) ||
 2892                   (CountLeadingSpaces (sLine) != iSpaces))
 2893                 bSplitting = false;
 2894             }
 2895           }
 2896 
 2897           if (!bWrite)
 2898           {
 2899             if (IsGrandTotal (sLine))
 2900             {
 2901               sLine = "";
 2902               bWrite = bGrand = true;
 2903             }
 2904           }
 2905           
 2906           if (bWrite || bSplitting)
 2907           {
 2908             sLine += "\n";
 2909             if (write (iOut, sLine.c_str(), sLine.length()) < 0)
 2910               sStatusMessage = "Can't write to  " + sDest;
 2911           }
 2912           sLine = "";
 2913         }
 2914         else
 2915           sLine += char (iLetter);
 2916         if (iLen < 1)
 2917           break;
 2918       }
 2919       close (iOut);
 2920     }
 2921     close (iIn);
 2922   }
 2923   return bRet;
 2924 }
 2925 
 2926 
 2927 // menu
 2928 void SearchOptions()
 2929 {
 2930   int iKey = 0, iPause;
 2931   const char* pcLetter;
 2932   char cLetter;
 2933   string sCatch, sTemp, sFocus;
 2934   
 2935   for (;;)
 2936   {
 2937     MyCls();
 2938 
 2939     con << "Search Options: current words are:-\n\n";
 2940     pcLetter = sWords.c_str();
 2941     while ((cLetter = *pcLetter++) != 0)
 2942     {
 2943       if (cLetter == cSeperator)
 2944         cLetter = ' ';
 2945       addch (cLetter);
 2946     }
 2947 
 2948     if (iSizeHitGrade == 0)
 2949       sCatch = "<off>";
 2950     else
 2951       sCatch = ItoS (iSizeHitGrade) + " (" +
 2952                ItoCommas (iSizeHitBytes) + ")";
 2953 
 2954     switch (iRepFocus)
 2955     {
 2956       case no_focus:
 2957         sFocus = "no focus";
 2958         break;
 2959 
 2960       case user:
 2961         sFocus = "focus on user " + sRepFocus;
 2962         break;
 2963 
 2964       default:
 2965         sFocus = "<error>";
 2966         break;
 2967     }
 2968      
 2969     con << "\n\nIn the case of skips (\"!\"s) first words "
 2970            "have higher preference.\n\n"
 2971            "Commands:\n\n"
 2972            "n.  null words\n"
 2973            "r.  reload words\n"
 2974            "s.  save words\n"
 2975            "c.  change request hit size from " << sCatch << "\n"
 2976            "f.  focus: " << sFocus << "\n"
 2977            "q.  quit this screen\n\n"
 2978            "Your choice: ";
 2979     refresh();
 2980 
 2981     iKey = getch();
 2982     iPause = 0;
 2983     switch (iKey)
 2984     {
 2985       case 'n':
 2986         sWords = "";
 2987         con << "\n\nAll words gone.\n";
 2988         iPause = 1;
 2989         break;
 2990 
 2991       case 'r':
 2992         if (LoadWords())
 2993           con << "\n\nWords reset.\n";
 2994         else
 2995           con << "\n\nOpps, problem loading words.\n";
 2996         iPause = 1;
 2997         break;
 2998 
 2999       case 's':
 3000         if (SaveWords())
 3001           con << "\n\nWords saved.\n";
 3002         else
 3003           con << "\n\nOpps, problem saving words.\n";
 3004         iPause = 1;
 3005         break;
 3006 
 3007       case 'c':
 3008         SizeHitOptions();
 3009         break;
 3010 
 3011       case 'f':
 3012         MyCls();
 3013         con << "Focus options:\n\n"
 3014                "1.  no focus\n"
 3015                "2.  a user\n";
 3016         sFocus = "";
 3017         GetLine (iLinePos);
 3018         if (NullText (pcReqBuff))
 3019           sTemp = "";
 3020         else
 3021         {
 3022           sFocus = GetUserName (pcReqBuff, false);
 3023           if (sFocus != "")
 3024             con << "3.  focus on user " << sFocus << "\n";
 3025         }
 3026         con << "\nYour choice: ";
 3027         switch (getch())
 3028         {
 3029           case '1':
 3030             iRepFocus = no_focus;
 3031             break;
 3032 
 3033           case '2':
 3034             con << "\n\nEnter user name: ";
 3035             GetText (sTemp);
 3036             if (sTemp == "")
 3037               iRepFocus = no_focus;
 3038             else
 3039             {
 3040               StrLwr (sTemp);
 3041               sRepFocus = sTemp;
 3042               iRepFocus = user;
 3043             }
 3044             break;
 3045 
 3046           case '3':
 3047             if (sFocus != "")
 3048             {
 3049               sRepFocus = sFocus;
 3050               iRepFocus = user;
 3051             }
 3052             break;
 3053         }
 3054         break;
 3055 
 3056       case 'q':
 3057         return;
 3058     }
 3059     if (iPause)
 3060     {
 3061       con << "\nPress any key.";
 3062       getch();
 3063     }
 3064   }
 3065 }
 3066 
 3067 
 3068 void CommonOptions()
 3069 {
 3070   int iKey, iTemp;
 3071   string sTemp;
 3072   
 3073   for (;;)
 3074   {
 3075     MyCls();
 3076     con << "Common Options\n\n"
 3077            "These affect both the browsing window and reports.\n"
 3078            "\ne.  keep target/extension/file type: " << bRepKeepExt <<
 3079            "\nn.  IP number instead of null user: " << bLookupIP <<
 3080            "\na.  use alias file to map IPs to names: " << bAliases <<
 3081            "\nw.  width of user name field: eg 8 (for names) "
 3082            "or 15 (for IPs): " << iUserNameLen <<
 3083            "\nq.  quit this menu\n\n"
 3084            "Your choice: ";
 3085     iKey = getch();
 3086     con << "\n\n";
 3087     switch (iKey)
 3088     {
 3089       case 'q':
 3090         return;
 3091 
 3092       case 'e':
 3093         bRepKeepExt = !bRepKeepExt;
 3094         break;
 3095 
 3096       case 'n':
 3097         bLookupIP = !bLookupIP;
 3098         break;
 3099 
 3100       case 'a':
 3101         bAliases = !bAliases;
 3102         if (bAliases && vAliasList.empty())
 3103         {
 3104           con << "Get a working aliases file first.\nPress a key. ";
 3105           getch();
 3106           bAliases = false;
 3107         }
 3108         break;
 3109 
 3110       case 'w':
 3111         con << "New width: ";
 3112         GetText (sTemp);
 3113         iTemp = atoi (sTemp.c_str());
 3114         if ((iTemp >= 1) && (iTemp <= 64))
 3115           iUserNameLen = iTemp;
 3116         break;
 3117 
 3118       default:
 3119         con << "Bad option. Press a key. ";
 3120         getch();
 3121     }
 3122   }
 3123 }
 3124 
 3125 
 3126 void MiscLogOptions()
 3127 {
 3128   int iKey = 0, iPause;
 3129   string sTemp, sFile, sFilter, sCheck, sTemp1, sTemp2, sUser, sDest;
 3130   bool bGood;
 3131   
 3132   for (;;)
 3133   {
 3134     MyCls();
 3135     con << "Misc Log Options\n\n"
 3136            "Current report is: " << sCurrentReport << "\n"
 3137            "Current filtered report is: " << sRepFilter << "\n"
 3138            "Current email is: " << sCurrentEmail << "\n\n"
 3139            "Commands:\n\n"
 3140            "c.  change current report\n"
 3141            "C.  change filter report\n"
 3142            "l.  list reports\n"
 3143            "d.  delete the report\n"
 3144            "v.  view current report with " << sViewer << "\n"
 3145            "V.  view filtered report\n"
 3146            "f.  filter the report for just one user\n"
 3147            "e.  change current email\n"
 3148            "m.  mail current report to current email with Pine\n"
 3149            "s.  show built in log filenames\n"
 3150            "q.  quit this menu\n\n"
 3151            "Your choice: ";
 3152     iKey = getch();
 3153     con << "\n\n";
 3154     iPause = 0;
 3155     sFile = sPathToFiles + "/" + sCurrentReport;
 3156     sFilter = sPathToFiles + "/" + sRepFilter;
 3157     switch (iKey)
 3158     {
 3159       case 'c':
 3160         con << "Enter new current report: ";
 3161         GetFileName (sTemp);
 3162         if (sTemp != "")
 3163           sCurrentReport = sTemp;
 3164         break;
 3165 
 3166       case 'C':
 3167         con << "Enter filtered report name: ";
 3168         GetFileName (sTemp);
 3169         if (sTemp != "")
 3170           sRepFilter = sTemp;
 3171         break;
 3172 
 3173       case 'l':
 3174         sTemp = "( cd " + sPathToFiles + " ; " +
 3175                 "echo -e \"Current reports:\n\" ; " +
 3176                 "ls -l *" + sReportExt + " ) | " + sViewer;
 3177         RunProgramQuietly (sTemp);
 3178         break;
 3179 
 3180       case 'd':
 3181         if (sCurrentReport == "")
 3182         {
 3183           con << "No current report to delete.\n";
 3184           iPause = 1;
 3185           break;
 3186         }
 3187         sTemp = "rm " + sFile;
 3188         RunProgramQuietly (sTemp);
 3189         sCurrentReport = "";
 3190         break;
 3191 
 3192       case 'v':
 3193         if (GetFileSize (sFile.c_str()) == 0)
 3194         {
 3195           con << "Empty file :(\n";
 3196           iPause = 1;
 3197         }
 3198         else
 3199         {
 3200           sTemp = sViewer + " " + sFile;
 3201           RunProgramInConsole (sTemp);
 3202         }
 3203         break;
 3204 
 3205       case 'V':
 3206         if (GetFileSize (sFilter.c_str()) == 0)
 3207         {
 3208           con << "Empty file :(\n";
 3209           iPause = 1;
 3210         }
 3211         else
 3212         {
 3213           sTemp = sViewer + " " + sFilter;
 3214           RunProgramInConsole (sTemp);
 3215         }
 3216         break;
 3217 
 3218       case 'f':
 3219         if (GetFileSize (sFile.c_str()) == 0)
 3220         {
 3221           con << "No or empty file :(\n";
 3222           iPause = 1;
 3223           break;
 3224         }
 3225 
 3226         MyCls();
 3227         con << "Filter a report leaving one user.\n\n"
 3228             << "Enter new report name: ";
 3229         GetFileName (sDest, &bGood);
 3230         if (!bGood)
 3231           break;
 3232         con << "\n";
 3233         
 3234         if (sDest == sCurrentReport)
 3235         {
 3236           con << "Destination report must be different to source.\n\n";
 3237           iPause = 1;
 3238           break;
 3239         }
 3240 
 3241         if (iRepFocus == user)
 3242           sTemp1 = sRepFocus;
 3243         else
 3244           sTemp1 = "";
 3245         GetLine (iLinePos);
 3246         if (NullText (pcReqBuff))
 3247           sTemp2 = "";
 3248         else
 3249           sTemp2 = GetUserName (pcReqBuff, false);
 3250                 
 3251         con << "Filter report for:-\n\n"
 3252                "1.  focus user " << sTemp1 << "\n"
 3253                "2.  current user " << sTemp2 << "\n"
 3254                "3.  specify a different user\n\n"
 3255                "Your choice: ";
 3256         iKey = getch();
 3257         con << "\n\n";
 3258         sUser = "";
 3259         switch (iKey)
 3260         {
 3261           case '1':
 3262             sUser = sTemp1;
 3263             break;
 3264 
 3265           case '2':
 3266             sUser = sTemp2;
 3267             break;
 3268 
 3269           case '3':
 3270             con << "Enter user name: ";
 3271             GetText (sUser);
 3272             con << "\n";
 3273             StrLwr (sUser);
 3274             break;
 3275         }
 3276         if (sUser != "")
 3277         {
 3278           con << "Working... ";
 3279           sStatusMessage = "";
 3280           bGood = FilterReport (sCurrentReport, sDest, sUser);
 3281           if (bGood)
 3282           {
 3283             sRepFilter = sDest;
 3284             con << "\n\nReport " + sDest + " made: view (y/n)?";
 3285             if (getch() == 'y')
 3286             {
 3287               sTemp = sViewer + " " + sPathToFiles + "/" + sDest;
 3288               RunProgramInConsole (sTemp);
 3289             }            
 3290           }
 3291           else
 3292           {
 3293             con << "\n\nEmpty report " << sDest << ":\n"
 3294                 << sStatusMessage << "\n";
 3295             iPause = 1;
 3296           }
 3297           sStatusMessage = "";
 3298         }
 3299         break;
 3300 
 3301       case 'e':
 3302         con << "Enter new email address: ";
 3303         GetText (sTemp);
 3304         if (sTemp != "")
 3305         {
 3306           sCheck = CheckEmail (sTemp);
 3307           if (sCheck != "")
 3308           {
 3309             con << "\nSorry, no illegal characters.\n";
 3310             iPause = 1;
 3311           }
 3312           else
 3313             sCurrentEmail = sTemp;
 3314         }
 3315         break;
 3316 
 3317       case 'm':
 3318         if (GetFileSize (sFile) == 0)
 3319         {
 3320           con << "Empty file :(\n";
 3321           iPause = 1;
 3322           break;
 3323         }
 3324         if (sCurrentEmail == "")
 3325         {
 3326           con << "No email address.\n";
 3327           iPause = 1;
 3328           break;
 3329         }
 3330        con << "You are about to send the report using the\n"
 3331               "email program Pine. Press <ctrl>-x in Pine to\n"
 3332               "send it.\n"
 3333               "\n"
 3334               "Press any key to begin";
 3335         getch();
 3336         sTemp = "pine -attach " + sFile + " " + sCurrentEmail;
 3337         RunProgramInConsole (sTemp);
 3338         break;
 3339 
 3340       case 's':
 3341         MyCls();
 3342         con << "Log files:\n\n"
 3343             << szLabel1 << " -> " << sLogFile1 << "\n"
 3344             << szLabel2 << " -> " << sLogFile2 << "\n"
 3345             << szLabel3 << " -> " << sLogFile3 << "\n"
 3346             << "\nTo adjust these do this:\n\n"
 3347             << "cd ~/.squidview\n"
 3348             << "rm log1\n"
 3349             << "ln -s /var/whatever/access.log log1\n";
 3350         iPause = 1;
 3351         break;
 3352 
 3353       case 'q':
 3354         return;
 3355     }
 3356     if (iPause)
 3357     {
 3358       con << "\nPress any key.";
 3359       getch();
 3360       RunProgramInConsole ("clear");
 3361     }
 3362   }
 3363 }
 3364 
 3365 
 3366 // put some text at the end of a string (overwrite)
 3367 void ShowMsg (string& sLine, const string& sMsg)
 3368 {
 3369   int iLLen, iMLen;
 3370 
 3371   iLLen = sLine.length();
 3372   iMLen = sMsg.length();
 3373   if (iMLen > iLLen)
 3374     return;
 3375   sLine.replace (iLLen - iMLen, iMLen, "");
 3376   sLine += sMsg;
 3377 }
 3378 
 3379 
 3380 // go to a certain point in the log file
 3381 void GotoPercentage()
 3382 {
 3383   string sText;
 3384   double dPercent;
 3385 
 3386   PromptForText ("Goto percentage: ", sText);
 3387   if (sText != "")
 3388   {
 3389     dPercent = strtod (sText.c_str(), 0);
 3390     if ((dPercent >= 0) && (dPercent <= 100))
 3391       CentreScreen (GetLine (tFilePos (iLastLinePos * dPercent / 100)));
 3392   }
 3393 }
 3394 
 3395 
 3396 // next position ahead in log
 3397 inline tFilePos ForwardNextLeap (tFilePos iStart, tFilePos iInc)
 3398 {
 3399   tFilePos iNext, iMiddle;
 3400   
 3401   if (iStart >= iLastLinePos)
 3402     return -1;
 3403     
 3404   iNext = iStart + iInc;
 3405   if (iNext >= iLastLinePos)
 3406     return iLastLinePos;
 3407 
 3408   iMiddle = GetPrevLine (iNext);
 3409   if (iMiddle <= iStart)
 3410   {
 3411     iMiddle = GetLine (iStart);
 3412     if ((iMiddle == 0) || (iMiddle >= iLastLinePos))
 3413       return -1;
 3414   }
 3415   return iMiddle;
 3416 }
 3417 
 3418 
 3419 // next position backwards in log
 3420 inline tFilePos BackwardNextLeap (tFilePos iStart, tFilePos iInc)
 3421 {
 3422   tFilePos iNext, iMiddle;
 3423   
 3424   if (iStart <= 0)
 3425     return -1;
 3426     
 3427   iNext = iStart - iInc;
 3428   if (iNext <= 0)
 3429     return 0;
 3430 
 3431   iMiddle = GetPrevLine (iNext);
 3432   if (iMiddle <= 0)
 3433     iMiddle = 0;
 3434   
 3435   return iMiddle;
 3436 }
 3437 
 3438 
 3439 // seconds of highlighted line
 3440 time_t CalcCurrentSecs()
 3441 {
 3442   string sBuff;
 3443   time_t tSecs;
 3444   
 3445   GetLine (iLinePos);
 3446   if (NullText (pcReqBuff))
 3447     return 0;
 3448 
 3449   tSecs = GetTimeNumber (pcReqBuff);
 3450   return tSecs;
 3451 }
 3452 
 3453 
 3454 // this is necessary to get around daylight savings issues
 3455 time_t Get4am (time_t tSecs)
 3456 {
 3457   struct tm* tmTable;
 3458   
 3459   tmTable = localtime (&tSecs);
 3460   tmTable->tm_sec = 0;
 3461   tmTable->tm_min = 0;
 3462   tmTable->tm_hour = 4;
 3463   return mktime (tmTable);
 3464 }
 3465 
 3466 
 3467 // nuke minutes and hours of a day
 3468 time_t Get12am (time_t tSecs)
 3469 {
 3470   struct tm* tmTable;
 3471   
 3472   tmTable = localtime (&tSecs);
 3473   tmTable->tm_sec = 0;
 3474   tmTable->tm_min = 0;
 3475   tmTable->tm_hour = 0;
 3476   return mktime (tmTable);
 3477 }
 3478 
 3479 
 3480 // return day of month
 3481 inline int CalcDayFromSecs (time_t tSecs)
 3482 {
 3483   struct tm* tmTable;
 3484 
 3485   if (tSecs == 0)
 3486     return 0;
 3487     
 3488   tmTable = localtime (&tSecs);
 3489   return tmTable->tm_mday;
 3490 }
 3491 
 3492 
 3493 // having the above and lower bounds in log, zoom in
 3494 void SequenceJump (tFilePos iBelow, tFilePos iAbove, time_t iSecs)
 3495 {
 3496   tFilePos iNext;
 3497 
 3498   ShowHighLightMessage ("Sequencing...");
 3499   
 3500   for (;;)
 3501   {
 3502     iNext = GetLine (iBelow);
 3503     if (GetTimeNumber (pcReqBuff) >= iSecs)
 3504     {
 3505       CentreScreen (iBelow);
 3506       return;
 3507     }
 3508     if (iNext >= iAbove)
 3509     {
 3510       CentreScreen (iAbove);
 3511       return;
 3512     }
 3513     iBelow = iNext;
 3514   }
 3515 }
 3516 
 3517 
 3518 // split in two until time in range, then zoom
 3519 void SplitJump (time_t iSecs)
 3520 {
 3521   tFilePos iBelow, iAbove, iMiddle;
 3522   time_t iTry;
 3523   
 3524   iBelow = 0;
 3525   iAbove = iLastLinePos;
 3526 
 3527   ShowHighLightMessage ("Splitting pointer...");
 3528   
 3529   for (;;)
 3530   {
 3531     if (iAbove <= iBelow)
 3532     {
 3533       CentreScreen (iBelow);
 3534       return;
 3535     }
 3536     if (iAbove - iBelow < 100000)
 3537     {
 3538       SequenceJump (iBelow, iAbove, iSecs);
 3539       return;
 3540     }
 3541 
 3542     iMiddle = (iBelow + iAbove) >> 1;
 3543     iMiddle = GetLine (iMiddle);
 3544     for (;;)
 3545     {
 3546       iMiddle = GetLine (iMiddle);
 3547       iTry = GetTimeNumber (pcReqBuff);
 3548       if (iTry != 0)
 3549         break;
 3550       if (iMiddle >= iAbove)
 3551       {
 3552         sStatusMessage = "Problem in jump :(";
 3553         return;
 3554       }
 3555     }
 3556     if (iTry < iSecs)
 3557       iBelow = iMiddle;
 3558     else
 3559       iAbove = iMiddle;
 3560   }
 3561 }
 3562 
 3563 
 3564 // move along to a certain day
 3565 void JumpToDayForward()
 3566 {
 3567   int iDay, iCount;
 3568   time_t tCReq;
 3569   string sBuffer;
 3570 
 3571   if (iLogFileSize == 0)
 3572     return;
 3573 
 3574   tCReq = CalcCurrentSecs();
 3575   if (tCReq == 0)
 3576   {
 3577     sStatusMessage = "Bad day to start with.";
 3578     return;
 3579   }
 3580 
 3581   PromptForText ("Jump forward to day of month: ", sBuffer);
 3582   if (sBuffer == "")
 3583     return;
 3584   iDay = atoi (sBuffer.c_str());
 3585   if ((iDay < 1) || (iDay > 31))
 3586     return;
 3587 
 3588   ShowHighLightMessage ("Finding day...");
 3589 
 3590   tCReq = Get4am (tCReq);
 3591   if (iDay == CalcDayFromSecs (tCReq))
 3592     tCReq += nSecsInDay;
 3593 
 3594   iCount = 0;
 3595   for (;;)
 3596   {
 3597     if (CalcDayFromSecs (tCReq) == iDay)
 3598       break;
 3599     if (++iCount > 100)
 3600     {
 3601       sStatusMessage = "Couldn't find day";
 3602       return;
 3603     }
 3604     tCReq += nSecsInDay;
 3605   }
 3606 
 3607   tCReq = Get12am (tCReq);
 3608 
 3609   SplitJump (tCReq);
 3610   return;
 3611 }
 3612 
 3613 
 3614 // move backward to a certain day
 3615 void JumpToDayBackward()
 3616 {
 3617   string sBuffer;
 3618   int iDay, iCount;
 3619   time_t tCReq;
 3620 
 3621   if (iLogFileSize == 0)
 3622     return;
 3623 
 3624   tCReq = CalcCurrentSecs();
 3625   if (tCReq == 0)
 3626   {
 3627     sStatusMessage = "Bad day to start with.";
 3628     return;
 3629   }
 3630 
 3631   PromptForText ("Jump backward to day of month: ", sBuffer);
 3632   if (sBuffer == "")
 3633     return;
 3634   iDay = atoi (sBuffer.c_str());
 3635   if ((iDay < 1) || (iDay > 31))
 3636     return;
 3637 
 3638   ShowHighLightMessage ("Finding day...");
 3639 
 3640   tCReq = Get4am (tCReq);
 3641   if (iDay == CalcDayFromSecs (tCReq))
 3642     tCReq -= nSecsInDay;
 3643 
 3644   iCount = 0;
 3645   for (;;)
 3646   {
 3647     if (CalcDayFromSecs (tCReq) == iDay)
 3648       break;
 3649     if (++iCount > 100)
 3650     {
 3651       sStatusMessage = "Couldn't find day";
 3652       return;
 3653     }
 3654     tCReq -= nSecsInDay;
 3655   }
 3656 
 3657   tCReq = Get12am (tCReq);
 3658 
 3659   SplitJump (tCReq);
 3660   return;
 3661 }
 3662 
 3663 
 3664 void ChangeLog (int iNewLog)
 3665 {
 3666   string sTemp;
 3667   int iLen;
 3668   int iBase;
 3669   
 3670   switch (iNewLog)
 3671   {
 3672     case 1:
 3673       pszCurrentLog = sLogFile1.c_str();
 3674       pszCurrentName = szLabel1;
 3675       iCurrentLog = iNewLog;
 3676       break;
 3677 
 3678     case 2:
 3679       pszCurrentLog = sLogFile2.c_str();
 3680       pszCurrentName = szLabel2;
 3681       iCurrentLog = iNewLog;
 3682       break;
 3683 
 3684     case 3:
 3685       pszCurrentLog = sLogFile3.c_str();
 3686       pszCurrentName = szLabel3;
 3687       iCurrentLog = iNewLog;
 3688       break;
 3689   }
 3690 
 3691   if (iNewLog >= 1)
 3692     aTally.ZeroAll();
 3693 
 3694   CalcLastPage();
 3695   iRepStart = 0;
 3696   iRepEnd = 0;
 3697   if (iMonitorMode)
 3698   {
 3699     iPagePos = iLastPage;
 3700     iLinesDown = iMaxLinesDown;
 3701   }
 3702   else
 3703   {
 3704     iPagePos = 0;
 3705     iLinesDown = 0;
 3706   }
 3707 
 3708   GetLine (0);
 3709   iBase = 0;
 3710   
 3711   GetColumn (pcReqBuff, 5, sTemp);
 3712   iLen = sTemp.length();
 3713   if ((iLen > 2) && (sTemp [iLen - 1] == ':'))
 3714     iBase = 5;
 3715 
 3716   iTimeCol = iBase + 1;
 3717   iIPCol = iBase + 3;
 3718   iCacheHitCol = iBase + 4;
 3719   iSizeCol = iBase + 5;
 3720   iTargetCol = iBase + 7;
 3721 
 3722   GetColumn (pcReqBuff, iBase + 3, sTemp);
 3723   if ((sTemp == "") || (sTemp [0] < 'A'))
 3724     iUserCol = iBase + 8;
 3725   else
 3726     iUserCol = iBase + 3;
 3727 }
 3728 
 3729 
 3730 void ViewSelectedLine (tFilePos iLogPos)
 3731 {
 3732   string sUserName, sLongName;
 3733   int iKey = 0;
 3734   bool bLegal;
 3735 
 3736   MyCls();
 3737   GetLine (iLogPos);
 3738   if (NullText (pcReqBuff))
 3739   {
 3740     con << "Line not available.\n";
 3741     bLegal = false;
 3742   }
 3743   else
 3744     bLegal = true;
 3745 
 3746   if (bLegal)
 3747   {
 3748     con << pcReqBuff << "\n\n"
 3749         << "Request size = " << ItoCommas (GetRequestSize (pcReqBuff))
 3750         << "\n\nLooking for user...";
 3751     sUserName = GetUserName (pcReqBuff, false);
 3752     sLongName = GetLongUserName (sUserName);
 3753     if (sLongName == "")
 3754       con << "not found.\n";
 3755     else
 3756       con << "\n\n" << sUserName << " is " << sLongName << "\n";
 3757   }
 3758 
 3759   con << "\nPress q:";
 3760   refresh();
 3761 
 3762   while (iKey != 'q')
 3763     iKey = getch();
 3764 
 3765   MyCls();
 3766 }
 3767 
 3768 
 3769 int CheckUpdate()
 3770 {
 3771   static int iScreenVal = -1;
 3772   int iTemp;
 3773   int ReCalc;
 3774 
 3775   ReCalc = 0;
 3776   iTemp = (COLS * 65536) + LINES;
 3777   if (iScreenVal != iTemp)
 3778   {
 3779     erase();
 3780     iScreenVal = iTemp;
 3781     iMainLines = LINES - 1;
 3782     ReCalc = 2;
 3783   }
 3784   if (GetFileSize (pszCurrentLog) != iLogFileSize)
 3785     ReCalc++;
 3786   if (ReCalc)
 3787   {
 3788     CalcLastPage();
 3789     if (ReCalc >= 2)
 3790     {
 3791       ChangeLog (-1);
 3792       return 1;
 3793     }
 3794   }
 3795   return 0;
 3796 }
 3797 
 3798 
 3799 class SettingsIO
 3800 {
 3801   public:
 3802     SettingsIO();
 3803     void Save();
 3804     void Load();
 3805 
 3806   private:
 3807     FILE* fIO;
 3808     bool bSave;
 3809     string sLoadName, sLoadValue;
 3810     bool bError;
 3811 
 3812     void Write (string sText);
 3813     void IOsetting (string sName, string sValue);
 3814     void IObool (const char*, bool&);
 3815     void IOint (const char*, int&);
 3816     void IOfloat (const char*, float&);
 3817     void IOstring (const char*, string&);
 3818     void EachOne();
 3819 };
 3820 
 3821 
 3822 SettingsIO::SettingsIO()
 3823 {
 3824   sSetFileName = "squidview.conf";
 3825 }
 3826 
 3827 
 3828 void SettingsIO::Save()
 3829 {
 3830   bSave = true;
 3831   bError = false;
 3832   fIO = fopen (sSetFileName.c_str(), "w");
 3833   if (fIO == NULL)
 3834   {
 3835     sStatusMessage = string ("Could not write to ") + sSetFileName;
 3836     return;
 3837   }
 3838   Write ("# this complete file is reset on every clean exit\n");
 3839   EachOne();
 3840   fclose (fIO);
 3841 }
 3842 
 3843 
 3844 void SettingsIO::Load()
 3845 {
 3846   int iFile, iRead, iLetter, iCount, iLength;
 3847   char cLetter;
 3848   bool bGotEquals;
 3849   string sLine;
 3850 
 3851   bSave = false;
 3852   bError = false;
 3853 
 3854   iFile = open (sSetFileName.c_str(), O_RDONLY);
 3855   if (iFile <= 0)
 3856   {
 3857     sStatusMessage = string ("Could not read ") + sSetFileName;
 3858     return;
 3859   }
 3860 
 3861   sLine = "";
 3862   iRead = 1;
 3863   while (iRead == 1)
 3864   {
 3865     iLetter = 0;
 3866     iRead = read (iFile, &iLetter, 1);
 3867     if (iRead == 1)
 3868     {
 3869       if ((iLetter == cEOL) || (iLetter == cCR))
 3870       {
 3871         iLength = sLine.length();
 3872         if ((iLength > 0) && (sLine [0] != '#'))
 3873         {
 3874           sLoadName = "";
 3875           sLoadValue = "";
 3876           bGotEquals = false;
 3877           for (iCount = 0; iCount < iLength; iCount++)
 3878           {
 3879             cLetter = sLine [iCount];
 3880             if (bGotEquals)
 3881               sLoadValue += cLetter;
 3882             else
 3883             if (cLetter == '=')
 3884               bGotEquals = true;
 3885             else
 3886               sLoadName += cLetter;
 3887           }
 3888           EachOne();
 3889         }
 3890         sLine = "";
 3891       }
 3892       else
 3893         sLine += char (iLetter);
 3894     }
 3895   }
 3896   close (iFile);
 3897 }
 3898 
 3899 
 3900 void SettingsIO::Write (string sText)
 3901 {
 3902   int iTemp;
 3903   
 3904   if (bError)
 3905     return;
 3906       
 3907   iTemp = sText.length();
 3908   if (int (fwrite (sText.c_str(), 1, iTemp, fIO)) == iTemp)
 3909     return;
 3910   sStatusMessage = string ("Could not append to ") + sSetFileName;
 3911   bError = true;
 3912 }
 3913 
 3914 
 3915 
 3916 void SettingsIO::IOsetting (string sName, string sValue)
 3917 {
 3918   if (bError)
 3919     return;
 3920       
 3921   if (bSave)
 3922     Write (sName + "=" + sValue + "\n");
 3923 }
 3924 
 3925 
 3926 void SettingsIO::IObool (const char* szText, bool& bVariable)
 3927 {
 3928   string sTemp;
 3929   
 3930   if (bSave)
 3931   {
 3932     sTemp = bVariable == true ? "yes" : "no";
 3933     IOsetting (szText, sTemp);
 3934   }
 3935   else
 3936   if (sLoadName == szText)
 3937     bVariable = sLoadValue == "yes" ? true: false;
 3938 }
 3939 
 3940 
 3941 void SettingsIO::IOint (const char* szText, int& iVariable)
 3942 {
 3943   if (bSave)
 3944     IOsetting (szText, ItoS (iVariable));
 3945   else
 3946   if (sLoadName == szText)
 3947     iVariable = atoi (sLoadValue.c_str());
 3948 }
 3949 
 3950 
 3951 void SettingsIO::IOfloat (const char* szText, float& fVariable)
 3952 {
 3953   char szTemp [32];
 3954   
 3955   if (bSave)
 3956   {
 3957     snprintf (szTemp, sizeof (szTemp), "%f", fVariable);
 3958     IOsetting (szText, szTemp);
 3959   }
 3960   else
 3961   if (sLoadName == szText)
 3962     fVariable = atof (sLoadValue.c_str());
 3963 }
 3964 
 3965 
 3966 void SettingsIO::IOstring (const char* szText, string& sVariable)
 3967 {
 3968   if (bSave)
 3969     IOsetting (szText, sVariable);
 3970   else
 3971   if (sLoadName == szText)
 3972     sVariable = sLoadValue;
 3973 }
 3974 
 3975 
 3976 // store these variables in config file
 3977 void SettingsIO::EachOne()
 3978 {
 3979   IOstring ("sRepFileName", sRepFileName);
 3980   IOstring ("sRepFilter", sRepFilter);
 3981   IOstring ("sRepTitle", sRepTitle);
 3982   IObool ("bRepKeepExt", bRepKeepExt);
 3983   IOint ("iRepColumns", iRepColumns);
 3984   IObool ("bRepSplit", bRepSplit);
 3985   IObool ("bRepShowSize", bRepShowSize);
 3986   IObool ("bRepCacheReport", bRepCacheReport);
 3987   IOstring ("sRepSeperator", sRepSeperator);
 3988   IOint ("iRepCSVcols", iRepCSVcols);
 3989   IOint ("iRepWordHits", iRepWordHits);
 3990   IOint ("iRepBUT", iRepBUT);   
 3991   IOint ("iRepBDT", iRepBDT);   
 3992   IOint ("iRepBDI", iRepBDI);   
 3993   IOint ("iRepFocus", iRepFocus);   
 3994   IOstring ("sRepFocus", sRepFocus);
 3995   IOstring ("sRepBDTuser", sRepBDTuser);
 3996   IOstring ("sViewer", sViewer);
 3997   IOstring ("sCurrentReport", sCurrentReport);
 3998   IOstring ("sCurrentEmail", sCurrentEmail);
 3999   IOint ("iSizeHitGrade", iSizeHitGrade);
 4000   IOint ("iSizeHitBytes", iSizeHitBytes);
 4001   IOstring ("sSizeHits", sSizeHits);
 4002   IOint ("iCurrentLog", iCurrentLog);
 4003   IOint ("iMonitorMode", iMonitorMode);
 4004   IObool ("bLookupIP", bLookupIP);
 4005   IObool ("bAliases", bAliases);
 4006   IOint ("iUserNameLen", iUserNameLen);
 4007   IOint ("iRepFast", iRepFast);
 4008   IObool ("bRepMySort", bRepMySort);
 4009 
 4010   IOint ("iTallyHP", aTally.iHistoryPeriod);
 4011   IOfloat ("iTallyFade", aTally.fFadeFactor);
 4012   IOfloat ("iTallyEntry", aTally.fEntryFactor);
 4013   IObool ("iTallyMonitor", aTally.bUsersMonitorMode);
 4014   IObool ("iTallyDenies", aTally.bAcceptDenies);
 4015 }
 4016 
 4017 
 4018 void ViewHowTo()
 4019 {
 4020   if (GetFileSize (sHowToLink) <= 0)
 4021   {
 4022     MyCls();
 4023     con << "I can't find the howto for this program.\n\n"
 4024         << "There should be this link:\n\n"
 4025         << sHowToLink << "\n\n"
 4026         << "pointing to:\n\n"
 4027         << SHAREDIR << "/" << szHowToFile << "\n\n"
 4028         << "You can set the correct link manually.\n"
 4029         << "Press q:";
 4030     for (;;)
 4031     {
 4032       if (getch() == 'q')
 4033         return;
 4034     }
 4035   }
 4036   else
 4037     RunProgramInConsole (sViewer + " " + sHowToLink);
 4038 }
 4039 
 4040 
 4041 int WaitKeyOrLog (tFilePos iLastSize)
 4042 {
 4043   fd_set fdsInput;
 4044   struct timeval tvTimeOut;
 4045   int iTemp;
 4046 
 4047   for (;;)
 4048   {
 4049     FD_ZERO (&fdsInput);
 4050     FD_SET (0, &fdsInput);
 4051     tvTimeOut.tv_sec = 3;
 4052     tvTimeOut.tv_usec = 0;
 4053     iTemp = select (1, &fdsInput, 0, 0, &tvTimeOut);
 4054     if (iTemp)
 4055       return getch();
 4056     else
 4057       if (GetFileSize (pszCurrentLog) != iLastSize)
 4058         return 0;
 4059   }
 4060 }
 4061 
 4062 
 4063 string CalcIdle (time_t iLastTime)
 4064 {
 4065   int iTime;
 4066   
 4067   iTime = time (0) - iLastTime;
 4068   if (iTime < 0)
 4069     return "!";
 4070   if (iTime < 60)
 4071     return ItoS (iTime) + "s";
 4072     
 4073   iTime = iTime / 60;
 4074   if (iTime < 60)
 4075     return ItoS (iTime) + "m";
 4076 
 4077   iTime = iTime / 60;
 4078   if (iTime < 60)
 4079     return ItoS (iTime) + "h";
 4080     
 4081   iTime = iTime / 24;
 4082   return ItoS (iTime) + "d";
 4083 }
 4084 
 4085 
 4086 time_t TimeLapse (time_t iInstant = 0)
 4087 {
 4088   if (iInstant == 0)
 4089     return time (0);
 4090   else
 4091     return time (0) - iInstant;
 4092 }
 4093 
 4094 
 4095 bool EnterUserHistory (const string& sLoginName)
 4096 {
 4097   cUserHistory aUH;
 4098   
 4099   return aUH.Run (sLoginName);
 4100 }
 4101 
 4102 
 4103 bool cUserHistory::Run (const string& sLoginName)
 4104 {
 4105   sUser = sLoginName;
 4106   iScreenStart = iSelectedRow = 0;
 4107   MyCls();
 4108   CalcScreen();
 4109   iNorthMark = -1;
 4110   iSouthMark = iSouthMarkEnd = iOneUserFileSize;
 4111   AddHistory();
 4112   return Browse();
 4113 }
 4114 
 4115   
 4116 void cUserHistory::CalcScreen()
 4117 {
 4118   tFilePos iCurrent;
 4119   
 4120   iCurrent = GetFileSize (pszCurrentLog);
 4121   if (iCurrent < iOneUserFileSize)
 4122     ZeroAll();
 4123   else
 4124     iLogFileSize = iOneUserFileSize = iCurrent;
 4125   iScreenHeight = LINES - 2;
 4126   iTotalRows = vRequests.size();
 4127   iSelectedAddress = iScreenStart + iSelectedRow;
 4128   iLastScreenRow = iTotalRows - iScreenHeight;
 4129   if (iLastScreenRow < 0)
 4130     iLastScreenRow = 0;
 4131 }
 4132 
 4133 
 4134 void cUserHistory::ZeroAll()
 4135 {
 4136   while (!vRequests.empty())
 4137     vRequests.pop_back();
 4138   iScreenStart = iSelectedRow = 0;
 4139   iNorthMark = 0;
 4140   iSouthMark = iSouthMarkEnd = 0;
 4141   iLogFileSize = iOneUserFileSize = 0;
 4142 }
 4143 
 4144 
 4145 bool cUserHistory::Browse()
 4146 {
 4147   int iKey;
 4148   string sTemp;
 4149   
 4150   for (;;)
 4151   {
 4152     CalcScreen();
 4153     DisplayHistory();
 4154     refresh();
 4155     iKey = WaitKeyOrLog (iOneUserFileSize);
 4156     CalcScreen();
 4157      
 4158     switch (iKey)
 4159     {
 4160       case KEY_UP:
 4161       case '8':
 4162         if (iSelectedRow > 0)
 4163           iSelectedRow--;
 4164         else
 4165         {
 4166           if (iScreenStart > 0)
 4167             iScreenStart--;
 4168           else
 4169             AddHistoryNorth();
 4170         }
 4171         break;
 4172 
 4173       case KEY_DOWN:
 4174       case '2':
 4175         if (iSelectedRow < iScreenHeight - 1)
 4176         {
 4177           if (iSelectedAddress < iTotalRows - 1)
 4178             iSelectedRow++;
 4179           else
 4180           {
 4181             if (AddHistorySouth() > 0)
 4182             {
 4183               if (vRequests.size() == 1)
 4184                 iSelectedRow = 0;
 4185               else
 4186                 iSelectedRow++;
 4187             }
 4188           }
 4189         }
 4190         else
 4191         {
 4192           if (iScreenStart < iLastScreenRow)
 4193             iScreenStart++;
 4194           else
 4195           {
 4196             if (AddHistorySouth() > 0)
 4197               iScreenStart++;
 4198           }
 4199         }
 4200         break;
 4201 
 4202       case KEY_PPAGE:
 4203       case '9':
 4204         if (iScreenStart == 0)
 4205           iSelectedRow = 0;
 4206         else
 4207         {
 4208           iScreenStart = iScreenStart - iScreenHeight;
 4209           if (iScreenStart < 0)
 4210             iScreenStart = 0;
 4211         }
 4212         break;
 4213 
 4214       case KEY_NPAGE:
 4215       case '3':
 4216         if (iScreenStart >= iLastScreenRow)
 4217         {
 4218           iScreenStart = iLastScreenRow;
 4219           iSelectedRow = iScreenHeight - 1;
 4220           if (iSelectedRow > iTotalRows - 1)
 4221             iSelectedRow = iTotalRows - 1;
 4222         }
 4223         else
 4224         {
 4225           iScreenStart += iScreenHeight;
 4226           if (iScreenStart > iLastScreenRow)
 4227             iScreenStart = iLastScreenRow;
 4228         }
 4229         break;
 4230       
 4231       case KEY_HOME:
 4232       case '7':
 4233         iScreenStart = iSelectedRow = 0;
 4234         break;
 4235       
 4236       case KEY_END:
 4237       case '1':
 4238         iScreenStart = iLastScreenRow;
 4239         if (iTotalRows < iScreenHeight)
 4240           iSelectedRow = iTotalRows - 1;
 4241         else
 4242           iSelectedRow = iScreenHeight - 1;
 4243         if (iSelectedRow < 0)
 4244           iSelectedRow = 0;
 4245         break;
 4246 
 4247       case 'v':
 4248         if (iSelectedAddress < iTotalRows)
 4249           ViewSelectedLine (vRequests [iSelectedAddress]);
 4250         break;
 4251 
 4252       case 'u':
 4253         PromptForText ("Switch to user: ", sTemp);
 4254         if (sTemp != "")
 4255         {
 4256           MyCls();
 4257           sUser = sTemp;
 4258           ZeroAll();
 4259         }
 4260         break;
 4261       
 4262       case 'Z':
 4263         ZeroAll();
 4264         break;
 4265 
 4266       case 'h':
 4267         DisplayHelp();
 4268         break;
 4269 
 4270       case 'r':
 4271         ViewHowTo();
 4272         break;
 4273 
 4274       case 'q':
 4275         return false;
 4276 
 4277       case 24:   // ctrl-x
 4278         return true;
 4279     }
 4280   }
 4281 }
 4282 
 4283 
 4284 void cUserHistory::AddHistory()
 4285 {
 4286   int iCount;
 4287   time_t iInstant;
 4288   
 4289   CalcScreen();
 4290   iCount = 0;
 4291   iInstant = TimeLapse (0);
 4292   
 4293   for (;;)
 4294   {
 4295     if (AddHistoryNorth() == 0)
 4296       break;
 4297     if (++iCount >= iScreenHeight)
 4298       break;
 4299     if (TimeLapse (iInstant) >= 2)
 4300       break;
 4301   }
 4302 
 4303   CalcScreen();
 4304 }
 4305 
 4306 
 4307 int cUserHistory::AddHistoryNorth()
 4308 {
 4309   tFilePos iCurrentPos;
 4310   string sComment;
 4311   int iCount, iReturn = 0, iSize;
 4312   bool bFound;
 4313 
 4314   if (iNorthMark == 0)
 4315     return 0;
 4316   
 4317   if (iNorthMark > 0)
 4318     iCurrentPos = iNorthMark;
 4319   else
 4320   {
 4321     if (vRequests.size() == 0)
 4322       iCurrentPos = iOneUserFileSize;
 4323     else
 4324       iCurrentPos = vRequests [0];
 4325   }
 4326 
 4327   sComment = "History of " + sUser;
 4328   iCount = 0;
 4329   GoForNoDelay();
 4330   bFound = false;
 4331       
 4332   for (;;)
 4333   {
 4334     iNorthMark = GetPrevLine (iCurrentPos);
 4335     if (iNorthMark == 0)
 4336     {
 4337       iReturn = 0;
 4338       break;
 4339     }
 4340 
 4341     if (!NullText (pcReqBuff))
 4342       if (GetUserName (pcReqBuff, false) == sUser)
 4343       {
 4344         bFound = true;
 4345         break;
 4346       }
 4347 
 4348     if (++iCount > nMaxNorth)
 4349     {
 4350       iReturn = -1;
 4351       break;
 4352     }
 4353       
 4354     if (CheckInterrupt())
 4355     {
 4356 //      bUsersMonitorMode = false;
 4357       iReturn = -2;
 4358       break;
 4359     }
 4360     if (CheckTicker())
 4361     {
 4362       UpdateTicker (sComment.c_str(), iNorthMark, iOneUserFileSize);
 4363     }
 4364     
 4365     iCurrentPos = iNorthMark;
 4366   }
 4367 
 4368   if (wTemp)
 4369     nodelay (wTemp, false);
 4370 
 4371   if (!bFound)
 4372     return iReturn;
 4373 
 4374   iSize = int (vRequests.size());
 4375   if (iSize < nMaxRows)
 4376     vRequests.push_back (iNorthMark);
 4377   else
 4378   {
 4379     iSouthMark = vRequests [iSize - 2];
 4380     iSouthMarkEnd = vRequests [iSize - 1];
 4381   }
 4382   for (iCount = vRequests.size() - 1; iCount > 0; iCount--)
 4383     vRequests [iCount] = vRequests [iCount - 1];
 4384   vRequests [0] = iNorthMark;
 4385   return 1;
 4386 }
 4387 
 4388 
 4389 int cUserHistory::AddHistorySouth()
 4390 {
 4391   tFilePos iCurrentPos, iNextPos;
 4392   string sComment;
 4393   int iCount, iReturn = 0, iSize;
 4394   bool bFound;
 4395 
 4396   if (iSouthMarkEnd >= iOneUserFileSize)
 4397     return 0;
 4398   
 4399   iCurrentPos = iSouthMarkEnd;
 4400 
 4401   sComment = "History of " + sUser;
 4402   iCount = 0;
 4403   GoForNoDelay();
 4404   bFound = false;
 4405       
 4406   for (;;)
 4407   {
 4408     iNextPos = GetLine (iCurrentPos);
 4409     if (iNextPos <= iCurrentPos)
 4410     {
 4411       iReturn = 0;
 4412       iNextPos = iCurrentPos;
 4413       break;
 4414     }
 4415 
 4416     if (!NullText (pcReqBuff))
 4417       if (GetUserName (pcReqBuff, false) == sUser)
 4418       {
 4419         bFound = true;
 4420         break;
 4421       }
 4422 
 4423     if (++iCount > nMaxSouth)
 4424     {
 4425       iReturn = -1;
 4426       break;
 4427     }
 4428       
 4429     if (CheckInterrupt())
 4430     {
 4431 //      bUsersMonitorMode = false;
 4432       iReturn = -2;
 4433       break;
 4434     }
 4435     if (CheckTicker())
 4436     {
 4437       UpdateTicker (sComment.c_str(), iNextPos, iOneUserFileSize);
 4438     }
 4439     
 4440     iCurrentPos = iNextPos;
 4441   }
 4442 
 4443   if (wTemp)
 4444     nodelay (wTemp, false);
 4445 
 4446   iSouthMarkEnd = iNextPos;
 4447 
 4448   if (!bFound)
 4449     return iReturn;
 4450   
 4451   iSouthMark = iCurrentPos;
 4452   iSize = int (vRequests.size());
 4453   if (iSize < nMaxRows)
 4454   {
 4455     vRequests.push_back (iSouthMark);
 4456     return 1;
 4457   }
 4458   else
 4459   {
 4460     for (iCount = 0; iCount < iSize - 1; iCount++)
 4461       vRequests [iCount] = vRequests [iCount + 1];
 4462     vRequests [iSize - 1] = iSouthMark;
 4463     iNorthMark = vRequests [0];
 4464     return 0;
 4465   }
 4466 }
 4467 
 4468 
 4469 void cUserHistory::DisplayHistory()
 4470 {
 4471   int iScreenY, iCount, iRow, iColumns;
 4472   string sLine, sFlags, sTarget, sTemp;
 4473   bool bInverse;
 4474   time_t iTime;
 4475 
 4476   iColumns = COLS - 1;
 4477   sLine = "One user history: " + sUser;
 4478   Columnize (sLine, iColumns);
 4479   move (0, 0);
 4480   attron (A_REVERSE);
 4481   con << sLine;
 4482   
 4483   iRow = iScreenStart;
 4484   iScreenY = 1;
 4485   iTime = 0;
 4486   
 4487   for (iCount = 0; iCount < iScreenHeight; iCount++)
 4488   {
 4489     sLine = "";
 4490     bInverse = false;
 4491     if (iRow < iTotalRows)
 4492     {
 4493       if (GetLine (vRequests [iRow]) > vRequests [iRow])
 4494       {
 4495         GetRequestFlags (pcReqBuff, sFlags);
 4496         GetColumn (pcReqBuff, iTargetCol, sTarget);
 4497         RemoveSlashes (sTarget);
 4498         sLine = sFlags + " " + sTarget;
 4499         KeepExtension (sLine, iColumns);
 4500         if (iRow == iSelectedAddress)
 4501         {
 4502           iTime = GetTimeNumber (pcReqBuff);
 4503           bInverse = true;
 4504         }
 4505       }
 4506     }
 4507     Columnize (sLine, iColumns);
 4508     move (iScreenY, 0);
 4509     if (bInverse)
 4510         attron (A_REVERSE);
 4511     else
 4512         attroff (A_REVERSE);
 4513     con << sLine;
 4514     iScreenY++;
 4515     iRow++;
 4516   }
 4517 
 4518   CalcPercentage (iNorthMark, iOneUserFileSize, sTemp);
 4519   NoLeadingSpaces (sTemp);
 4520   sLine = "(" + sTemp;
 4521   CalcPercentage (iSouthMarkEnd, iOneUserFileSize, sTemp);
 4522   NoLeadingSpaces (sTemp);
 4523   sLine += " to " + sTemp;
 4524   CalcPercentage (iSelectedAddress,
 4525                   iTotalRows == 0 ? 0 : iTotalRows - 1, sTemp);
 4526   NoLeadingSpaces (sTemp);
 4527   sLine += ") " + sTemp;
 4528   
 4529   if (iTime > 0)
 4530   {
 4531     CalcTime (iTime, sTemp);
 4532     RemoveSeconds (sTemp);
 4533     sLine += " " + sTemp;
 4534   }
 4535   
 4536   Columnize (sLine, iColumns);
 4537   ShowMsg (sLine, " | h = help");
 4538   move (iScreenY, 0);
 4539   attron (A_REVERSE);
 4540   con << sLine;
 4541 }
 4542 
 4543 
 4544 
 4545 void cUserHistory::DisplayHelp()
 4546 {
 4547   erase();
 4548   move (0, 0);
 4549   attroff (A_REVERSE);
 4550   con << "One User's History help\n\n"
 4551          "v.        view line\n"
 4552          "u.        zero and switch to new user\n"
 4553          "Z.        zero (wipe) history\n"
 4554          "r.        read howto\n"
 4555          "q.        quit this mode\n"
 4556          "ctrl-x:   quit squidview\n"
 4557          "\nPress any key or r: ";
 4558   if (getch() == 'r')
 4559     ViewHowTo();
 4560 }
 4561 
 4562 
 4563 cUsersMode::cUsersMode()
 4564 {
 4565   iSortMode = nSortN;
 4566   iLastAdjustTime = iNextAdjustTime = 0;
 4567   iHistoryPeriod = 3600;
 4568   fFadeFactor = 0.75;
 4569   fEntryFactor = 1.0;
 4570   iScreenMode = nPoints;
 4571   iScreenStart = iSelectedRow = 0;
 4572   iCurrentLogPos = 0;
 4573   bUsersMonitorMode = true;
 4574   bAcceptDenies = true;
 4575 }
 4576 
 4577 
 4578 bool cUsersMode::Enter()
 4579 {
 4580   string sTemp;
 4581   int iKey;
 4582     
 4583   MyCls();
 4584   if (iCurrentLogPos == 0)
 4585   {
 4586     CalcPercentage (iLinePos, iLastLinePos, sTemp);
 4587     NoLeadingSpaces (sTemp);
 4588     con << "All Users' Tally Mode\n\nCurrent position is "
 4589         << FilePosToTime (iRepStart)
 4590         << " (" << sTemp << ")\n\nPress enter to begin from here.\n\n"
 4591         << "Note this might take a while.";
 4592     iKey = getch();
 4593     if ((iKey == cEOL) || (iKey == cCR))
 4594       AddData (iLinePos, iLastLinePos + 1);
 4595     else
 4596       return false;
 4597     MyCls();
 4598   }
 4599   return Browse();
 4600 }
 4601 
 4602   
 4603 bool cUsersMode::Browse()
 4604 {
 4605   int iKey, iMode;
 4606   string sLine, sTemp;
 4607 
 4608   for (;;)
 4609   {
 4610     CalcScreen();
 4611      
 4612     switch (iScreenMode)
 4613     {
 4614       case nPoints:
 4615         DisplayPoints();
 4616         break;
 4617 
 4618       case nFullNames:
 4619         DisplayFullNames();
 4620         break;
 4621 
 4622       case nGreatest:
 4623         DisplayGreatestURLs();
 4624         break;
 4625 
 4626       case nLast:
 4627         DisplayLastURLs();
 4628         break;
 4629       
 4630       default:
 4631         break;
 4632     }
 4633     
 4634     CalcPercentage (iSelectedAddress, iTotalRows - 1, sLine);
 4635     sLine += string (" ") + SortMode();
 4636     Columnize (sLine, COLS - 1);
 4637     sTemp = bAcceptDenies == false ? "-d " : "";
 4638     sTemp += bUsersMonitorMode ? "mon ": "";
 4639     sTemp += "| h = help";
 4640     ShowMsg (sLine, sTemp);
 4641     ShowHighLightMessage (sLine);
 4642 
 4643     if (bUsersMonitorMode)
 4644       iKey = WaitKeyOrLog (iUsersFileSize);
 4645     else
 4646       while ((iKey = getch()) == 0) {}
 4647     
 4648     CalcScreen();
 4649     rUserHistory& rUH = vHistory [iSelectedAddress];
 4650      
 4651     switch (iKey)
 4652     {
 4653       case 0:
 4654         AddData (iCurrentLogPos, iUsersFileSize);
 4655         break;
 4656          
 4657       case KEY_UP:
 4658       case '8':
 4659         if (iSelectedRow > 0)
 4660           iSelectedRow--;
 4661         else
 4662           if (iScreenStart > 0)
 4663             iScreenStart--;
 4664         break;
 4665 
 4666       case KEY_DOWN:
 4667       case '2':
 4668         if (iSelectedRow < iScreenHeight - 1)
 4669         {
 4670           if (iSelectedAddress < iTotalRows - 1)
 4671             iSelectedRow++;
 4672         }
 4673         else
 4674           if (iScreenStart < iLastScreenRow)
 4675             iScreenStart++;
 4676         break;
 4677 
 4678       case KEY_PPAGE:
 4679       case '9':
 4680         if (iScreenStart == 0)
 4681           iSelectedRow = 0;
 4682         else
 4683         {
 4684           iScreenStart = iScreenStart - iScreenHeight;
 4685           if (iScreenStart < 0)
 4686             iScreenStart = 0;
 4687         }
 4688         break;
 4689 
 4690       case KEY_NPAGE:
 4691       case '3':
 4692         if (iScreenStart >= iLastScreenRow)
 4693         {
 4694           iScreenStart = iLastScreenRow;
 4695           iSelectedRow = iScreenHeight - 1;
 4696         }
 4697         else
 4698         {
 4699           iScreenStart += iScreenHeight;
 4700           if (iScreenStart > iLastScreenRow)
 4701             iScreenStart = iLastScreenRow;
 4702         }
 4703         break;
 4704       
 4705       case KEY_HOME:
 4706       case '7':
 4707         iScreenStart = iSelectedRow = 0;
 4708         break;
 4709       
 4710       case KEY_END:
 4711       case '1':
 4712         iScreenStart = iLastScreenRow;
 4713         iSelectedRow = iScreenHeight - 1;
 4714         break;
 4715       
 4716       case 'v':
 4717         iMode = int (iScreenMode);
 4718         if (++iMode >= int (nSMend))
 4719           iMode = 0;
 4720         iScreenMode = eSM (iMode);
 4721         break;
 4722         
 4723       case 's':
 4724         SortMenu();
 4725         break;
 4726 
 4727       case 'O':
 4728         sTemp = vHistory [iSelectedAddress].sUser;
 4729         if (EnterUserHistory (sTemp))
 4730           return true;
 4731         break;
 4732 
 4733       case 'h':
 4734         DisplayHelp();
 4735         break;
 4736         
 4737       case 'r':
 4738         ViewHowTo();
 4739         break;
 4740         
 4741       case 'm':
 4742         bUsersMonitorMode = !bUsersMonitorMode;
 4743         break;
 4744 
 4745       case 'd':
 4746         bAcceptDenies = !bAcceptDenies;
 4747         break;
 4748 
 4749       case 'z':
 4750         rUH.iPoints = rUH.iBytes = rUH.iRequests = rUH.iURLsize = 0;
 4751         // leave rUH.iLastTime alone
 4752         rUH.iGreatestURL = rUH.iLastURL = -1;
 4753         break;
 4754         
 4755       case 'Z':
 4756         ZeroAll();
 4757         break;
 4758         
 4759       case 'q':
 4760         return false;
 4761         
 4762       case 24:   // ctrl-x
 4763         return true;
 4764     }
 4765   }
 4766 }
 4767 
 4768 
 4769 void cUsersMode::DisplayPoints()
 4770 {
 4771   string sTemp;
 4772   int iIndex, iLine, iCount;
 4773   
 4774   erase();
 4775   move (0, 0);
 4776   attron (A_REVERSE);
 4777   sTemp = "User";
 4778   Columnize (sTemp, iUserNameLen);
 4779   con << sTemp << " ";
 4780 
 4781   DisplayHeading ("Points");
 4782   DisplayHeading ("Bytes");
 4783   DisplayHeading ("Requests");
 4784   DisplayHeading ("URL size");
 4785   DisplayHeading ("Idle");
 4786   
 4787   iLine = 1;
 4788   iIndex = iScreenStart;
 4789   iCount = 0;
 4790   
 4791   for (;;)
 4792   {
 4793     if ((iIndex >= iTotalRows) || (iCount >= iScreenHeight))
 4794       break;
 4795     move (iLine, 0);
 4796     if (iCount == iSelectedRow)
 4797       attron (A_REVERSE);
 4798     else
 4799       attroff (A_REVERSE);
 4800     rUserHistory& rEntry = vHistory [iIndex];
 4801     con << LookupUser (rEntry) << " ";
 4802     DisplayData (rEntry.iPoints);
 4803     DisplayData (rEntry.iBytes);
 4804     DisplayData (rEntry.iRequests);
 4805     DisplayData (rEntry.iURLsize);
 4806     DisplayHeading (CalcIdle (rEntry.iLastTime));
 4807     iIndex++;
 4808     iLine++;
 4809     iCount++;
 4810   }
 4811 }
 4812 
 4813 
 4814 void cUsersMode::DisplayFullNames()
 4815 {
 4816   string sLine;
 4817   int iIndex, iLine, iCount, iNameLength;
 4818   
 4819   erase();
 4820   move (0, 0);
 4821   attron (A_REVERSE);
 4822   con << "Full Names";
 4823      
 4824   iIndex = iScreenStart;
 4825   iLine = 1;
 4826   iCount = 0;
 4827   iNameLength = iUserNameLen;
 4828   if (iNameLength < 15)
 4829     iNameLength = 15;
 4830   
 4831   for (;;)
 4832   {
 4833     if ((iIndex >= iTotalRows) || (iCount >= iScreenHeight))
 4834       break;
 4835     rUserHistory& rEntry = vHistory [iIndex];
 4836     sLine = rEntry.sUser;
 4837     Columnize (sLine, iNameLength);
 4838     sLine = sLine + " " + rEntry.sFullName;
 4839     Columnize (sLine, COLS - 1);
 4840     move (iLine, 0);
 4841     if (iCount == iSelectedRow)
 4842       attron (A_REVERSE);
 4843     else
 4844       attroff (A_REVERSE);
 4845     con << sLine;
 4846     iIndex++;
 4847     iLine++;
 4848     iCount++;
 4849   }
 4850 }
 4851 
 4852 
 4853 void cUsersMode::DisplayGreatestURLs()
 4854 {
 4855   string sLine, sSize, sTemp;
 4856   int iColumns, iIndex, iLine, iCount;
 4857   
 4858   erase();
 4859   move (0, 0);
 4860   attron (A_REVERSE);
 4861   con << "Greatest Hits";
 4862 
 4863   iColumns = COLS;
 4864   iIndex = iScreenStart;
 4865   iLine = 1;
 4866   iCount = 0;
 4867   
 4868   for (;;)
 4869   {
 4870     if ((iIndex >= iTotalRows) || (iCount >= iScreenHeight))
 4871       break;
 4872     rUserHistory& rEntry = vHistory [iIndex];
 4873     sLine = LookupUser (rEntry);
 4874     sSize = ItoCommas (rEntry.iURLsize);
 4875     sTemp = "";
 4876     Columnize (sTemp, nSizeCols);
 4877     ShowMsg (sTemp, sSize);
 4878     sLine = sLine + " " + sTemp;
 4879     if (GetLine (rEntry.iGreatestURL) > rEntry.iGreatestURL)
 4880     {
 4881       GetColumn (pcReqBuff, iTargetCol, sTemp);
 4882       RemoveSlashes (sTemp);
 4883       sLine += " " + sTemp;
 4884     }
 4885     KeepExtension (sLine, iColumns - 1);
 4886     Columnize (sLine, iColumns - 1);
 4887     move (iLine, 0);
 4888     if (iCount == iSelectedRow)
 4889       attron (A_REVERSE);
 4890     else
 4891       attroff (A_REVERSE);
 4892     con << sLine;
 4893     iIndex++;
 4894     iLine++;
 4895     iCount++;
 4896   }
 4897 }
 4898 
 4899 
 4900 void cUsersMode::DisplayLastURLs()
 4901 {
 4902   string sLine, sSize, sTemp;
 4903   int iColumns, iIndex, iLine, iCount;
 4904   
 4905   erase();
 4906   move (0, 0);
 4907   attron (A_REVERSE);
 4908   con << "Last visited sites";
 4909 
 4910   iColumns = COLS;
 4911   iIndex = iScreenStart;
 4912   iLine = 1;
 4913   iCount = 0;
 4914   
 4915   for (;;)
 4916   {
 4917     if ((iIndex >= iTotalRows) || (iCount >= iScreenHeight))
 4918       break;
 4919     rUserHistory& rEntry = vHistory [iIndex];
 4920     sLine = LookupUser (rEntry);
 4921     if (GetLine (rEntry.iLastURL) > rEntry.iLastURL)
 4922     {
 4923       GetColumn (pcReqBuff, iTargetCol, sTemp);
 4924       RemoveSlashes (sTemp);
 4925       sLine += " " + sTemp;
 4926     }
 4927     KeepExtension (sLine, iColumns - 1);
 4928     Columnize (sLine, iColumns - 1);
 4929     move (iLine, 0);
 4930     if (iCount == iSelectedRow)
 4931       attron (A_REVERSE);
 4932     else
 4933       attroff (A_REVERSE);
 4934     con << sLine;
 4935     iIndex++;
 4936     iLine++;
 4937     iCount++;
 4938   }
 4939 }
 4940 
 4941 
 4942 void cUsersMode::DisplayHeading (const string& sHeading)
 4943 {
 4944   string sTemp = "";
 4945   
 4946   Columnize (sTemp, nSizeCols);
 4947   ShowMsg (sTemp, sHeading);
 4948   con << sTemp << " ";
 4949 } 
 4950 
 4951 
 4952 void cUsersMode::DisplayData (int iData)
 4953 {
 4954   string sTemp, sData;
 4955   
 4956   sTemp = "";
 4957   sData = ItoCommas (iData);
 4958   Columnize (sTemp, nSizeCols);
 4959   ShowMsg (sTemp, sData);
 4960   con << sTemp << " ";
 4961 } 
 4962 
 4963 
 4964 void cUsersMode::DisplayHelp()
 4965 {
 4966   erase();
 4967   move (0, 0);
 4968   attroff (A_REVERSE);
 4969   con << "All Users' Tally help\n\n"
 4970          "v.        toggle {requests,user name,greatest hits,last site}"
 4971          " view mode\n"
 4972          "m.        toggle monitor mode\n"
 4973          "d.        toggle accept denies\n"
 4974          "s.        sort menu\n"
 4975          "O.        one user mode\n"
 4976          "z.        zero this user\'s stats\n"
 4977          "Z.        zero (wipe) tally\n"
 4978          "r.        read howto\n"
 4979          "q.        quit users mode\n"
 4980          "ctrl-x:   quit squidview\n"
 4981          "\nPress any key or r: ";
 4982   if (getch() == 'r')
 4983     ViewHowTo();
 4984 }
 4985 
 4986 
 4987 void cUsersMode::CalcScreen()
 4988 {
 4989   tFilePos iCurrent;
 4990   
 4991   iCurrent = GetFileSize (pszCurrentLog);
 4992   if (iCurrent < iUsersFileSize)
 4993     ZeroAll();
 4994   iUsersFileSize = iCurrent;
 4995   iSelectedAddress = iScreenStart + iSelectedRow;
 4996   iScreenHeight = LINES - 2;
 4997   if (iScreenHeight > iTotalRows)
 4998     iScreenHeight = iTotalRows;
 4999   iLastScreenRow = iTotalRows - iScreenHeight;
 5000   if (iLastScreenRow < 0)
 5001     iLastScreenRow = 0;
 5002   FindFullNames();
 5003 }
 5004 
 5005 
 5006 string cUsersMode::LookupUser (const rUserHistory& aUser)
 5007 {
 5008   string sTemp;
 5009   
 5010   sTemp = aUser.sUser;
 5011   if (int (sTemp.length()) > iUserNameLen)
 5012     sTemp = ChopOffNetwork (sTemp, iUserNameLen);
 5013   Columnize (sTemp, iUserNameLen);
 5014   return sTemp;
 5015 }
 5016 
 5017 
 5018 void cUsersMode::AddData (tFilePos iFrom, tFilePos iTo)
 5019 {
 5020   tFilePos iCurrent, iTempPos;
 5021   int iCount, iRequestSize, iThisEntry, iTemp;
 5022   time_t iTime;
 5023   string sUserName;
 5024   rUserHistory aEntry;
 5025   char cRequestFlag;
 5026   eSort iOriginalSort;
 5027   
 5028   iCurrent = iFrom;
 5029   iOriginalSort = iSortMode;
 5030   if (iOriginalSort == nSortN)
 5031     iOriginalSort = nSortP;
 5032   iSortMode = nSortR;
 5033 
 5034   CheckUpdate();
 5035   GoForNoDelay();
 5036   
 5037   for (;;)
 5038   {
 5039     if (iCurrent >= iTo)
 5040       break;
 5041     iTempPos = GetLine (iCurrent);
 5042     if ((iTempPos == 0) || (iTempPos == iCurrent))
 5043       break;
 5044 
 5045     if (!NullText (pcReqBuff))
 5046     {
 5047       iRequestSize = GetRequestSize (pcReqBuff);
 5048       sUserName = GetUserName (pcReqBuff, false);
 5049 
 5050       if (bAcceptDenies)
 5051         cRequestFlag = ' ';
 5052       else
 5053         GetRequestCacheHit (pcReqBuff, cRequestFlag, iTemp);
 5054 
 5055       if ((sUserName != "") && (cRequestFlag != 'd'))
 5056       {
 5057         iThisEntry = -1;
 5058         for (iCount = vHistory.size() - 1; iCount >= 0; iCount--)
 5059         {
 5060           if (sUserName == vHistory [iCount].sUser)
 5061           {
 5062             iThisEntry = iCount;
 5063             break;
 5064           }
 5065         }
 5066         if (iThisEntry == -1)
 5067         {
 5068           aEntry.Zero();
 5069           aEntry.sUser = sUserName;
 5070           vHistory.push_back (aEntry);
 5071           iThisEntry = vHistory.size() - 1;
 5072         }
 5073         
 5074         iTime = GetTimeNumber (pcReqBuff);
 5075         if (iTime >= iNextAdjustTime)
 5076         {
 5077           if (iNextAdjustTime == 0)
 5078           {
 5079             iLastAdjustTime = CalcAeon (iTime);
 5080             iNextAdjustTime = iLastAdjustTime + iHistoryPeriod;
 5081           }
 5082           else
 5083             AgeData (iTime);
 5084         }
 5085         
 5086         rUserHistory& aIndex = vHistory [iThisEntry];
 5087         aIndex.iBytes += iRequestSize;
 5088         aIndex.iRequests++;
 5089         if (iRequestSize > aIndex.iURLsize)
 5090         {
 5091           aIndex.iURLsize = iRequestSize;
 5092           aIndex.iGreatestURL = iCurrent;
 5093         }
 5094         aIndex.iPoints += int (iRequestSize * fEntryFactor);
 5095         aIndex.iLastTime = iTime;
 5096         aIndex.iLastURL = iCurrent;
 5097       }
 5098     }
 5099 
 5100     iCurrentLogPos = iTempPos;
 5101     
 5102     if (CheckInterrupt())
 5103     {
 5104       bUsersMonitorMode = false;
 5105       break;
 5106     }
 5107     if (CheckTicker())
 5108     {
 5109       SortData();
 5110       UpdateTicker ("Users mode gathering", iCurrent - iFrom, iTo - iFrom);
 5111     }
 5112     iCurrent = iTempPos;
 5113   }
 5114 
 5115   if (wTemp)
 5116     nodelay (wTemp, false);
 5117 
 5118   iTotalRows = vHistory.size();
 5119   iSortMode = iOriginalSort;
 5120   SortData();
 5121 }
 5122 
 5123 
 5124 void cUsersMode::SortData()
 5125 {
 5126   bool bDone, bSwitch;
 5127   int iIndex;
 5128   rUserHistory rUH;
 5129 
 5130   iSortFlag = 312;
 5131   
 5132   for (;;)
 5133   {
 5134     bDone = true;
 5135     for (iIndex = vHistory.size() - 2; iIndex >= 0; iIndex--)
 5136     {
 5137       bSwitch = false;
 5138       rUserHistory& rUHleft = vHistory [iIndex];
 5139       rUserHistory& rUHright = vHistory [iIndex + 1];
 5140       switch (iSortMode)
 5141       {
 5142         case nSortU:
 5143           if (rUHleft.sUser > rUHright.sUser)
 5144             bSwitch = true;
 5145           break;
 5146 
 5147         case nSortP:
 5148           if (rUHleft.iPoints < rUHright.iPoints)
 5149             bSwitch = true;
 5150           break;
 5151 
 5152         case nSortB:
 5153           if (rUHleft.iBytes < rUHright.iBytes)
 5154             bSwitch = true;
 5155           break;
 5156           
 5157         case nSortR:
 5158           if (rUHleft.iRequests < rUHright.iRequests)
 5159             bSwitch = true;
 5160           break;
 5161 
 5162         case nSortURL:
 5163           if (rUHleft.iURLsize < rUHright.iURLsize)
 5164             bSwitch = true;
 5165           break;
 5166 
 5167         case nSortI:
 5168           if (rUHleft.iLastTime < rUHright.iLastTime)
 5169             bSwitch = true;
 5170           break;
 5171           
 5172         case nSortN:
 5173           break;
 5174       }
 5175       if (bSwitch)
 5176       {
 5177         bDone = false;
 5178         rUH = rUHleft;
 5179         rUHleft = rUHright;
 5180         rUHright = rUH;
 5181       }
 5182     }
 5183     if (bDone)
 5184     {
 5185       iSortFlag = 0;
 5186       return;
 5187     }
 5188   }
 5189 }
 5190 
 5191 
 5192 const char* cUsersMode::SortMode()
 5193 {
 5194   switch (iSortMode)
 5195   {
 5196     case nSortU:
 5197       return "user name";
 5198       
 5199     case nSortP:
 5200       return "points";
 5201       
 5202     case nSortB:
 5203       return "bytes";
 5204       
 5205     case nSortR:
 5206       return "requests";
 5207       
 5208     case nSortURL:
 5209       return "URL size";
 5210 
 5211     case nSortI:
 5212       return "idle";
 5213 
 5214     case nSortN:
 5215     default:
 5216       return "unsorted";      
 5217   }
 5218 }
 5219 
 5220 
 5221 void cUsersMode::FindFullNames()
 5222 {
 5223   int iFile, iIndex, iRead;
 5224   string sLine, sTemp;
 5225   char cLetter;
 5226   
 5227   static int iLastTotal = -1;
 5228   
 5229   if (iTotalRows == iLastTotal)
 5230     return;
 5231   iLastTotal = iTotalRows;
 5232   
 5233   iFile = open (sUsersFile.c_str(), O_RDONLY);
 5234   if (iFile > 0)
 5235   {
 5236     sLine = "";
 5237     for (;;)
 5238     {
 5239       iRead = read (iFile, &cLetter, 1);
 5240       if (iRead < 1)
 5241         break;
 5242       if ((cLetter == cEOL) || (cLetter == cCR))
 5243       {
 5244         if (sLine != "")
 5245         {
 5246           GetColumn (sLine.c_str(), 1, sTemp);
 5247           for (iIndex = 0; iIndex < iTotalRows; iIndex++)
 5248           {
 5249             rUserHistory& rEntry = vHistory [iIndex];
 5250             if (sTemp == rEntry.sUser)
 5251             {
 5252               sLine.replace (0, sTemp.length() + 1, "");
 5253               rEntry.sFullName = sLine;
 5254               break;
 5255             }
 5256           }
 5257           sLine = "";
 5258         }
 5259       }
 5260       else
 5261         sLine += cLetter;
 5262     }
 5263     close (iFile);
 5264   }
 5265 }
 5266 
 5267 
 5268 void cUsersMode::SortMenu()
 5269 {
 5270   int iKey;
 5271   
 5272   erase();
 5273   move (0, 0);
 5274   attroff (A_REVERSE);
 5275   con << "Current sort method is: " << SortMode()
 5276       << "\n\nChange to:\n\n"
 5277          "1.  user name\n"
 5278          "2.  points\n"
 5279          "3.  bytes\n"
 5280          "4.  requests\n"
 5281          "5.  URL size\n"
 5282          "6.  idle time\n"
 5283          "\nAny other key to dismiss";
 5284   iKey = getch();
 5285   if ((iKey >= '1') && (iKey <= '6'))
 5286   {
 5287     con << "\n\nSorting...";
 5288     refresh();
 5289     iSortMode = cUsersMode::eSort ((iKey - '1') + nSortU);
 5290     SortData();
 5291     iScreenStart = iSelectedRow = 0;
 5292   }
 5293 }
 5294 
 5295 
 5296 void cUsersMode::AgeData (int iCurrentTime)
 5297 {
 5298   int iAge, iIndex, iTotal;
 5299   float fFactor;
 5300   
 5301   iAge = (CalcAeon (iCurrentTime) - iLastAdjustTime) / iHistoryPeriod;
 5302   fFactor = pow (fFadeFactor, iAge);
 5303   iTotal = vHistory.size();
 5304   for (iIndex = 0; iIndex < iTotal; iIndex++)
 5305     vHistory [iIndex].iPoints = int (vHistory [iIndex].iPoints * fFactor);
 5306   iLastAdjustTime = CalcAeon (iCurrentTime);
 5307   iNextAdjustTime = iLastAdjustTime + iHistoryPeriod;
 5308 }
 5309 
 5310 
 5311 void cUsersMode::ZeroAll()
 5312 {
 5313   while (!vHistory.empty())
 5314     vHistory.pop_back();
 5315   iTotalRows = 0;
 5316   iScreenStart = iSelectedRow = 0;
 5317   iCurrentLogPos = 0;
 5318 }
 5319 
 5320 
 5321 // list of keystrokes
 5322 void Help()
 5323 {
 5324   MyCls();
 5325   con << PACKAGE << " " << VERSION << ": (c) " << "2001-2017"
 5326       << " Graeme Sheppard - GPL software\n"
 5327       << "www.rillion.net/squidview\n\n" <<
 5328     "Keystrokes:-\n\n"
 5329     "Up/Down/PgUp/PgDn:   browse\n"
 5330     "Home/End/g/j/J:      goto certain location in log\n"
 5331     "Left/Right:          search up/down\n"
 5332     "1-9:                 numlock and keypad strokes if necessary\n"
 5333     "f/F:                 find text\n"
 5334     "o:                   find options\n"
 5335     "v:                   verbose listing of selected line\n"
 5336     "p/s/t:               primary / secondary / tertiary log file\n"
 5337     "m:                   toggle monitor mode\n"
 5338     "l:                   log a report\n"
 5339     "n:                   misc. log functions\n"
 5340     "c:                   common options\n"
 5341     "T:                   all users' tally mode\n"
 5342     "O:                   one user history mode\n"
 5343     "r:                   read the squidview howto with " << sViewer << "\n"
 5344     "ctrl-x:              quit\n"
 5345     "\nPress q to quit this screen, r to read the howto.";
 5346   refresh();
 5347 
 5348   for (;;)
 5349   {
 5350     switch (getch())
 5351     {
 5352       case 'r':
 5353         ViewHowTo();
 5354       case 'q':
 5355         return;
 5356     }
 5357   }
 5358 }
 5359 
 5360 
 5361 // the routine to draw the screen
 5362 void DrawMain()
 5363 {
 5364   string sLine, sFlags, sTarget, sSelectedTime, sTemp;
 5365   int iCount;
 5366   tFilePos iThis, iNext;
 5367   time_t iTime;
 5368 
 5369   iLinePos = 0;
 5370   iThis = iPagePos;
 5371   sSelectedTime = "";
 5372 
 5373   for (iCount = 0; iCount < iMainLines; iCount++)
 5374   {
 5375     move (iCount, 0);
 5376     iNext = GetLine (iThis);
 5377     if (iNext > iThis)
 5378     {
 5379       GetPaddedUserName (pcReqBuff, sLine);
 5380       GetRequestFlags (pcReqBuff, sFlags);
 5381       GetColumn (pcReqBuff, iTargetCol, sTarget);
 5382       RemoveSlashes (sTarget);
 5383       sLine += " " + sFlags + " " + sTarget;
 5384       KeepExtension (sLine, COLS - 1);
 5385       Columnize (sLine, COLS - 1);
 5386       if (iCount == iLinesDown)
 5387       {
 5388         attron (A_REVERSE);
 5389         iLinePos = iThis;
 5390         iTime = GetTimeNumber (pcReqBuff);
 5391         CalcTime (iTime, sSelectedTime);
 5392         RemoveSeconds (sSelectedTime);
 5393       }
 5394       else
 5395         attroff (A_REVERSE);
 5396       con << sLine;
 5397       iThis = iNext;
 5398     }
 5399     else
 5400     {
 5401       sLine = "";
 5402       Columnize (sLine, COLS - 1);
 5403       attroff (A_REVERSE);
 5404       con << sLine;
 5405     }
 5406   }
 5407 
 5408   CalcPercentage (iLinePos, iLastLinePos, sLine);
 5409   sLine += " " + sSelectedTime;
 5410   Columnize (sLine, COLS - 1);
 5411   if (iMonitorMode)
 5412     sTemp = "Mon ";
 5413   else
 5414     sTemp = " ";
 5415   sTemp += string (pszCurrentName) + " | h = help";
 5416   ShowMsg (sLine, sTemp);
 5417 
 5418   if (sStatusMessage != "")
 5419   {
 5420     ShowHighLightMessage (sStatusMessage);
 5421     sStatusMessage = "";
 5422   }
 5423   else
 5424     ShowHighLightMessage (sLine);
 5425 
 5426   refresh();
 5427 }
 5428 
 5429 
 5430 // finish up
 5431 static void FinishSig (int iSig)
 5432 {
 5433   endwin();
 5434   if (fReadingFile > 0)
 5435     close (fReadingFile);  
 5436   exit (iSig);
 5437 }
 5438 
 5439 
 5440 // darn STL?
 5441 static void SegFaultSig (int iSig)
 5442 {
 5443   endwin();
 5444   RunProgramInConsole ("reset -Q ; clear");
 5445   if (iSortFlag >= 1000)
 5446     printf ("Squidview: fault during STL sort()\n"
 5447             "code = %d\n"
 5448             "Try:\n"
 5449             " 1. increasing iRepFast or making it 0\n"
 5450             " 2. using my sort routine\n"
 5451             "Refer to the BUGS file.\n"
 5452             "Sorry.\n",
 5453             iSortFlag);
 5454   else
 5455     printf ("Squidview: general fault\n"
 5456             "code = %d\n"
 5457             "Refer to the BUGS file.\n"
 5458             "Sorry.\n",
 5459             iSortFlag);
 5460   exit (iSig);
 5461 }
 5462 
 5463 
 5464 bool FileExists (const string &file)
 5465 {
 5466   return (access (file.c_str(), F_OK) == 0);
 5467 }
 5468 
 5469 
 5470 bool FileReadable (const string &file)
 5471 {
 5472   return (access (file.c_str(), R_OK) == 0);
 5473 }
 5474 
 5475 
 5476 // is this the first run?
 5477 // if so create .squidview directory and make links
 5478 void CreateLinks()
 5479 {
 5480   char* szTemp;
 5481   int iTemp;
 5482   string sLog1, sLog2, sLog3, sHowToFile, sLogLocation;
 5483 
 5484   szTemp = getenv ("HOME");
 5485   if (szTemp == 0)
 5486   {
 5487     print ("squidview: can't get your home directory, exiting.");
 5488     exit (1);
 5489   }
 5490 
 5491   sHomeDir = szTemp;
 5492   sPathToFiles = sHomeDir + "/" + szSquidHome;
 5493 
 5494   sLogFile1 = sPathToFiles + "/" + szLog1;
 5495   sLogFile2 = sPathToFiles + "/" + szLog2;
 5496   sLogFile3 = sPathToFiles + "/" + szLog3;
 5497   sWordFile = sPathToFiles + "/" + szWordFile;
 5498   sUsersFile = sPathToFiles + "/" + szUsersFile;
 5499   sAliasesFile = sPathToFiles + "/" + szAliasesFile;
 5500   sSetFileName = sPathToFiles + "/squidview.conf";
 5501   sHowToLink = sPathToFiles + "/" + szHowToFile;
 5502   sHowToFile = string (SHAREDIR) + "/" + szHowToFile;
 5503 
 5504   if (FileExists (sPathToFiles))
 5505   {
 5506     if (!FileReadable (sLogFile1))
 5507     {
 5508       print ("The squid log file cannot be read.");
 5509       exit (1);
 5510     }
 5511     return; // all is well hopefully
 5512   }
 5513 
 5514   print ("Making .squidview directory and links...");
 5515 
 5516 // bug fix contributed by Willi Mann: check for file existance, not size
 5517   sLogLocation = "";
 5518   bool LogFileFound = false;
 5519   for (iTemp = 0; sLogLocations [iTemp] != ""; iTemp++)
 5520     if (FileExists (sLogLocations [iTemp]))
 5521     {
 5522       sLogLocation = sLogLocations [iTemp];
 5523       LogFileFound = true;
 5524       break;
 5525     }
 5526 
 5527   if(!LogFileFound)
 5528     sLogLocation = sLogLocations[0];
 5529 
 5530   iTemp = 0;
 5531   for (;;)
 5532   {
 5533     if (mkdir (sPathToFiles.c_str(), 0755)) break;
 5534     if (symlink (sLogLocation.c_str(), sLogFile1.c_str())) break;
 5535     if (symlink (szLog1, sLogFile2.c_str())) break;
 5536     if (symlink (szLog1, sLogFile3.c_str())) break;
 5537     if (symlink (sHowToFile.c_str(), sHowToLink.c_str())) break;
 5538     iTemp = 1;
 5539     break;
 5540   }
 5541   if (iTemp == 0)
 5542   {
 5543     print ("~/.squidview/ setup errors. Exiting.");
 5544     exit (1);
 5545   }
 5546 
 5547   if (!FileReadable (sLogFile1))
 5548   {
 5549     print ("The squid log file cannot be accessed at pre-programmed locations.");
 5550     perror("The error was");
 5551     print ("You may need to manually adjust the symlinks in ~/.squidview/");
 5552     print ("You may need to give user permissions to log files, or add to a group (logout.)");
 5553 
 5554 #ifdef DEBIANIZED
 5555     print ("You may want to add your username to the group proxy.\n");
 5556 #endif
 5557 
 5558     print ("Or, after admin adjustments delete ~/.squidview/ and re-run squidview.");
 5559     exit (1);
 5560   }
 5561 }
 5562 
 5563 
 5564 // main is also the main loop
 5565 int main (int, char*[])
 5566 {
 5567   int iKey, iCount;
 5568   SettingsIO cSettings;
 5569   GenerateReport cReport;
 5570   string sTemp;
 5571   bool bExit;
 5572 
 5573   CreateLinks();    // better put this here
 5574 
 5575   signal (SIGINT, FinishSig);
 5576   signal (SIGSEGV, SegFaultSig);
 5577   initscr();
 5578   keypad (stdscr, TRUE);
 5579   nonl();
 5580   cbreak();
 5581   noecho();
 5582   curs_set (0);
 5583 
 5584   iLogFileSize = 0;
 5585   cSettings.Load();
 5586   ChangeLog (iCurrentLog);
 5587   sStatusMessage = "";
 5588   LoadWords();
 5589   LoadAliases();
 5590   
 5591   CheckUpdate();
 5592 
 5593   for (;;)
 5594   {
 5595     if (wTemp)
 5596       nodelay (wTemp, false);
 5597     CheckUpdate();
 5598     DrawMain();
 5599 
 5600     if (iMonitorMode == 0)
 5601     {
 5602       while ((iKey = getch()) == 0) {}
 5603     }
 5604     else
 5605     {
 5606       iKey = WaitKeyOrLog (iLogFileSize);
 5607       if (iKey == 0)
 5608         iKey = KEY_END;
 5609     }
 5610 
 5611     CheckUpdate();
 5612     bExit = false;
 5613 
 5614     switch (iKey)
 5615     {
 5616       case KEY_UP:
 5617       case '8':
 5618         if (iLinesDown > 0)
 5619           iLinesDown--;
 5620         else
 5621           iPagePos = GetPrevLine (iPagePos);
 5622         break;
 5623 
 5624       case KEY_DOWN:
 5625       case '2':
 5626         if (iLinesDown < iMaxLinesDown)
 5627           iLinesDown++;
 5628         else
 5629         {
 5630           if (iPagePos < iLastPage)
 5631             iPagePos = GetLine (iPagePos);
 5632         }
 5633         break;
 5634 
 5635       case KEY_HOME:
 5636       case '7':
 5637         iPagePos = 0;
 5638         iLinesDown = 0;
 5639         break;
 5640 
 5641       case KEY_END:
 5642       case '1':
 5643         iPagePos = iLastPage;
 5644         iLinesDown = iMaxLinesDown;
 5645         break;
 5646 
 5647       case KEY_PPAGE:
 5648       case '9':
 5649         for (iCount = 0; iCount < iMainLines; iCount++)
 5650           iPagePos = GetPrevLine (iPagePos);
 5651         if (iPagePos == 0)
 5652           iLinesDown = 0;
 5653         break;
 5654 
 5655       case KEY_NPAGE:
 5656       case '3':
 5657         for (iCount = 0; iCount < iMainLines; iCount++)
 5658           iPagePos = GetLine (iPagePos);
 5659         if (iPagePos >= iLastPage)
 5660         {
 5661           iPagePos = iLastPage;
 5662           iLinesDown = iMaxLinesDown;
 5663         }
 5664         break;
 5665 
 5666       case KEY_LEFT:
 5667       case '4':
 5668         GoForNoDelay();
 5669         SearchWords (-1);
 5670         break;
 5671 
 5672       case KEY_RIGHT:
 5673       case '6':
 5674         GoForNoDelay();
 5675         SearchWords (1);
 5676         break;
 5677 
 5678       case 'g':
 5679         GotoPercentage();
 5680         break;
 5681 
 5682       case 'j':
 5683         JumpToDayForward();
 5684         break;
 5685 
 5686       case 'J':
 5687         JumpToDayBackward();
 5688         break;
 5689 
 5690       case 'f':
 5691         PrependFindText();
 5692         break;
 5693 
 5694       case 'F':
 5695         AppendFindText();
 5696         break;
 5697 
 5698       case 'o':
 5699         SearchOptions();
 5700         break;
 5701 
 5702       case 'c':
 5703         CommonOptions();
 5704         break;
 5705 
 5706       case 'n':
 5707         MiscLogOptions();
 5708         break;
 5709 
 5710       case 'm':
 5711         if (iMonitorMode == 0)
 5712           iMonitorMode = 1;
 5713         else
 5714           iMonitorMode = 0;
 5715         break;
 5716 
 5717       case 'l':
 5718         if (ReportOptions())
 5719           cReport.Run();
 5720         break;
 5721 
 5722       case 'p':
 5723         ChangeLog (1);
 5724         break;
 5725 
 5726       case 's':
 5727         ChangeLog (2);
 5728         break;
 5729 
 5730       case 't':
 5731         ChangeLog (3);
 5732         break;
 5733 
 5734       case 'v':
 5735         ViewSelectedLine (iLinePos);
 5736         break;
 5737 
 5738       case 'h':
 5739         Help();
 5740         break;
 5741 
 5742       case 'r':
 5743         ViewHowTo();
 5744         break;
 5745 
 5746       case ' ':
 5747         MyCls();
 5748         DrawMain();
 5749         break;
 5750 
 5751       case 'd':
 5752         MyCls();
 5753         con << sDebugText;
 5754         getch();
 5755         break;
 5756 
 5757       case 'O':
 5758         GetLine (iLinePos);
 5759         bExit = EnterUserHistory (GetUserName (pcReqBuff, false));
 5760         break;
 5761 
 5762       case 'T':
 5763         bExit = aTally.Enter();
 5764         break;
 5765 
 5766       case 24:   // ctrl-x
 5767         bExit = true;
 5768         break;
 5769     }
 5770 
 5771     if (bExit)
 5772     {
 5773       cSettings.Save();
 5774       FinishSig (0);
 5775     }
 5776   }
 5777 }