home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / NEXTSTEP / Utilities / FileSpy-1.1-MIHS / SpyTextFieldCell.m < prev    next >
Encoding:
Text File  |  1996-09-22  |  27.5 KB  |  1,036 lines

  1. /*
  2.  *   This sourcecode is part of FileSpy, a logfile observing utility.
  3.  *   Copyright (C) 1996  Felix Rauch
  4.  *
  5.  *   This program is free software; you can redistribute it and/or modify
  6.  *   it under the terms of the GNU General Public License as published by
  7.  *   the Free Software Foundation; either version 2 of the License, or
  8.  *   (at your option) any later version.
  9.  *   
  10.  *   Notice that this program may not be possessed, used, copied,
  11.  *   distributed or modified by people having to do with nuclear
  12.  *   weapons. See the file CONDITIONS for details.
  13.  *
  14.  *   This program is distributed in the hope that it will be useful,
  15.  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  *   GNU General Public License for more details.
  18.  *
  19.  *   You should have received a copy of the GNU General Public License
  20.  *   along with this program; if not, write to the Free Software
  21.  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  22.  *
  23.  *   To contact the original author, try:
  24.  *   e-mail: Felix.Rauch@nice.ch
  25.  *   Traditional mail:    Felix Rauch
  26.  *            Sempacherstrasse 33
  27.  *            8032 Zurich
  28.  *            Switzerland
  29.  */
  30.  
  31. #import <appkit/Text.h>
  32. #import <appkit/ScrollView.h>
  33. #import <appkit/graphics.h>
  34. #import "SpyTextFieldCell.h"
  35.  
  36. #define MAXSIZE 1.0e38    // Maximum size of a text object
  37. #define MAXSLOWDOWN 32    // maximum of file updates to ignore in case of nfs-errors (could be a default-value). Must be power of 2!
  38.  
  39. @implementation SpyTextFieldCell
  40.  
  41. static List *tmpList = nil;        // list for selected cells in exMatrix
  42.  
  43. + (void)setSharedTmpList:newList
  44. {
  45.     tmpList = newList;
  46. }
  47.  
  48. + sharedTmpList
  49. {
  50.     return tmpList;
  51. }
  52.  
  53. - init
  54. {
  55.  
  56.     [super init];
  57.     fileLenght = lastFileLenght = 0;
  58.     fileex = oldfileex = NO;
  59.     textloaded = NO;
  60.     dirloaded = NO;
  61.     firstTime = 2;
  62.     permissionOK = oldpermissionOK = YES;
  63.     maxLines = 0;
  64.     zeroFlag = NO;
  65.     nrEINVAL = 0;
  66.  
  67. // init font object
  68.     myFont = [Font newFont:"Ohlfs"
  69.             size:10
  70.             style:0
  71.             matrix:NX_FLIPPEDMATRIX];
  72.     [Text setDefaultFont:myFont];
  73.     myDoc = [Document new:self];
  74.     myWindow = [[myDoc scrollView] window];
  75.     myScrollView = [myDoc scrollView];
  76.     myText = [[myDoc scrollView] docView];
  77.     [myWindow setFreeWhenClosed:NO];
  78.     color = NX_COLORBLACK;
  79.  
  80.     return self;
  81. }
  82.  
  83. - delete
  84. {
  85.     if(exCell) {
  86.     int row, col;
  87.     
  88.     [exMatrix getRow:&row andCol:&col ofCell:exCell];
  89.     [exMatrix removeRowAt:row andFree:YES];
  90.     }
  91.     [myDoc free];
  92.     [myWindow setDelegate:nil];
  93.     [myWindow setFreeWhenClosed:YES];
  94.     [myWindow close];
  95.     return self;
  96. }
  97.  
  98. - free
  99. {
  100.     return [super free];
  101. }
  102.  
  103. - (long int)lastFileLenght
  104. {
  105.     return lastFileLenght;
  106. }
  107.  
  108. - setLastFileLenght:(long int)lenght
  109. {
  110.     lastFileLenght = lenght;
  111.     return self;
  112. }
  113.  
  114. - (BOOL)fileExists
  115. {
  116.     return fileex;
  117. }
  118.  
  119. - updateFile
  120. // This is important. It checks the file and looks whether something has changed with it
  121. {
  122.     if(nrEINVAL > 0) {        // if we had nfs errors recently then wait for next check with
  123.                     // exponentially increasing time between two checks (proposed by mwa)
  124.     unsigned int i = MAXSLOWDOWN;
  125.     
  126.     while((i > 0) && (i >= nrEINVAL)) {
  127.         if(nrEINVAL % i == 0)
  128.         break;
  129.         i /= 2;
  130.     }
  131.     if(nrEINVAL % i != 0) {
  132.         nrEINVAL++;
  133.         return self;
  134.     }
  135.     }
  136.     oldfileex = fileex;
  137.     oldpermissionOK = permissionOK;
  138.     if(firstTime > 0) {            // if we're here for the first or second time
  139.                         // firstTime has the following meanings: 2: first time here
  140.                     //                     1: second time here
  141.                     //                     0: nth time here, no more special cases
  142.     struct stat st;
  143.     
  144.     if(stat([self stringValue], &st) < 0) {
  145.         switch(errno) {
  146.         case ENOENT:        // file doesn't exist
  147.         case ENOTDIR:        // part of path-prefix is not a directory, so file doesn't exist
  148.             fileType = FILETYPE_NOTHING;
  149.             break;
  150.         case EACCES:
  151.             fileType = FILETYPE_NOTHING;
  152.             if((firstTime == 2) && !(spytype & SPY_EXISTENCE))
  153.             printf("filespy: no permission for %s\n", [self stringValue]);
  154.             permissionOK = NO;
  155.             break;
  156.         case EINVAL:            // in case of 'nfs-server blafasel not responding still trying'
  157.             if(nrEINVAL++ == 0) {
  158.             perror("filespy: stat");
  159.             }
  160.             break;
  161.         default:
  162.             perror("filespy: stat");
  163.             fileex = NO;
  164.         }
  165.         firstTime = 1;        // next time we will be here for the second time
  166.     } else {            // stat() was ok
  167.         fileex = YES;        // yes, the file exists
  168.         permissionOK = YES;        // yes, we can read the file
  169.         nrEINVAL = 0;        // no more nfs errors
  170.         if(st.st_mode & S_IFDIR) {    // the file is a deriectory
  171.         fileType = FILETYPE_DIR;
  172.         lastmtime = (time_t)0;    // as we're here for the first or second time, we haven't checked the file before
  173.         if((firstTime == 1) && !(spytype & SPY_EXISTENCE)) {    // if dir was created and we're not spying for ex.
  174.             printf("filespy: created directory %s\n", [self stringValue]);    // (if we were spying for existence,
  175.                     // then this would be noted in the existence-log, so we wouldn't need a printf)
  176.         }
  177.         } else if(st.st_mode & S_IFREG) {    // the file is a regular file
  178.         fileType = FILETYPE_FILE;
  179.         fileLenght = st.st_size;    // remember its current size
  180.         if(fileMode == RADIO_ENTIRE)    // if we will have to read the whole content, 
  181.             lastFileLenght = 0;        // just pretend that the file grew from 0 length.
  182.         else if(fileMode == RADIO_NEW)    // otherwise, this is the initial file length
  183.             lastFileLenght = fileLenght;
  184.         if((firstTime == 1) && (fileLenght == 0) && !(spytype & SPY_EXISTENCE)) {
  185.             // if we're here for the second time (i.e. the file didn't exist before) and the new file's length
  186.             // is 0 bytes and we're not spying for existence, we have to make the user aware of the new file.
  187.             printf("filespy: created empty file %s\n", [self stringValue]);
  188.         }
  189.         } else {
  190.         printf("filespy: not supported filetype in file %s\n", [self stringValue]);    // what should I do with other filetypes?
  191.         }
  192.         firstTime = 0;        // now we can handle this file in the regular way
  193.     }
  194.     } else if(fileType == FILETYPE_FILE) {    // this is a regular file
  195.     struct stat st;
  196.  
  197.     if(stat([self stringValue], &st) < 0) {
  198.         switch (errno) {
  199.         case ENOENT:     // no such file or directory
  200.         case ENOTDIR:    // some part of the path changed from directory to file -> file can't exist anymore
  201.             [self fileRemoved];
  202.             lastFileLenght = fileLenght;
  203.             fileLenght = 0;
  204.             firstTime = 2;    // well, the file doesn't exist anymore, so it's a special case again
  205.             if(!permissionOK) {        // if we didn't have permission before...
  206.             if(!(spytype & SPY_EXISTENCE)) {
  207.                 printf("filespy: regained permission for not existing file %s\n", [self stringValue]);
  208.                     // I'm sure whether this message makes much sense...
  209.             }
  210.             permissionOK = YES;    // we have permissions (well, sort of..)
  211.             }
  212.             break;
  213.         case EACCES:        // access denied
  214.             if(permissionOK) {
  215.             if(!(spytype & SPY_EXISTENCE))
  216.                 printf("filespy: lost permission for file %s\n", [self stringValue]);
  217.             permissionOK = NO;
  218.             }
  219.             break;
  220.         case EINVAL:        // nfs server not responding
  221.             if(nrEINVAL++ == 0) {    // do not report nfs-error each time the file is checked
  222.             perror("filespy: stat");
  223.             }
  224.             break;
  225.         default:
  226.             perror("filespy: stat");
  227.         }
  228.     } else {            // stat() was ok
  229.         nrEINVAL = 0;        // no more nfs errors
  230.         if(!permissionOK) {        // if we didn't have permission before
  231.         if(!(spytype & SPY_EXISTENCE))
  232.             printf("filespy: regained permission for file %s\n", [self stringValue]);
  233.         permissionOK = YES;
  234.         }
  235.         if(!(st.st_mode & S_IFREG)) {    // the file is no longer a regular file
  236.         [self fileRemoved];
  237.         lastFileLenght = fileLenght;
  238.         fileLenght = 0;
  239.         firstTime = 2;
  240.         } else {
  241.         lastFileLenght = fileLenght;
  242.         fileLenght = st.st_size;
  243.         fileex = YES;        // yes, the file exists
  244.         }
  245.     }    
  246.     } else if(fileType == FILETYPE_DIR) {    // this file is a directory
  247.     struct stat st;
  248.     
  249.     if(stat([self stringValue], &st) < 0) {
  250.         switch(errno) {
  251.             case ENOENT:
  252.             case ENOTDIR:
  253.             [self fileRemoved];
  254.             lastmtime = mtime;
  255.             firstTime = 2;
  256.             if(!permissionOK) {
  257.                 if(!(spytype & SPY_EXISTENCE))
  258.                 printf("filespy: regained permission for not existing directory %s\n", [self stringValue]);
  259.                 permissionOK = YES;
  260.             }
  261.             break;
  262.             case EACCES:
  263.             if(permissionOK) {
  264.                 if(!(spytype & SPY_EXISTENCE))
  265.                 printf("filespy: lost permission for directory %s\n", [self stringValue]);
  266.                 permissionOK = NO;
  267.             }
  268.             break;
  269.             case EINVAL:        // nfs server not responding
  270.             if(nrEINVAL++ == 0) {
  271.                 perror("filespy: stat");
  272.             }
  273.             break;
  274.             default:
  275.             perror("filespy: stat");
  276.         }
  277.     } else {
  278.         nrEINVAL = 0;        // no more nfs errors
  279.         if(!permissionOK) {
  280.         if(!(spytype & SPY_EXISTENCE))
  281.             printf("filespy: regained permission for directory %s\n", [self stringValue]);
  282.         permissionOK = YES;
  283.         }
  284.         if(!(st.st_mode & S_IFDIR)) {    // this file is no longer a directory
  285.         [self fileRemoved];
  286.         lastmtime = mtime;
  287.         firstTime = 2;
  288.         } else {
  289.         oldfileex = fileex;
  290.         lastmtime = mtime;
  291.         mtime = st.st_mtime;    // when was the directory last modified?
  292.         if(!dirStore) {        // if we don't have the directory's contents yet, then read them
  293.             DIR *dirp;
  294.             struct direct *dp;
  295.                     // read all filenames and put them in dirStore
  296.             dirp = opendir([self stringValue]);
  297.             if(dirp != NULL) {
  298.             dirStore = [[Storage alloc]
  299.                     initCount:0 elementSize:MAXFILENAMELENGHT
  300.                     description:"[MAXFILENAMELENGHT c]"];
  301.             for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
  302.                 [dirStore addElement:dp->d_name];
  303.             closedir(dirp);
  304.             }
  305.         }
  306.         }
  307.     }
  308.     }
  309.     if((spytype & SPY_EXISTENCE) && fileex) {
  310.     time_t myTime = time(NULL);
  311.     [exCell changeTime:myTime];
  312.     }
  313.     return self;
  314. }
  315.  
  316. - updateText
  317. // This one checks whether it is necessary to read the contents of the file or update the existencewindow
  318. {
  319.     if((fileType == FILETYPE_FILE) && (spytype & SPY_CONTENTS))
  320.     [self readText];
  321.     else if((fileType == FILETYPE_DIR) && (spytype & SPY_CONTENTS))
  322.     [self readDir];
  323.     if(spytype & SPY_EXISTENCE)    
  324.     [self updateExistence];
  325.     return self;
  326. }
  327.  
  328. - readText
  329. // read contents of changed regular file
  330. {
  331.     if(fileLenght < lastFileLenght) {        // if the file shrunk
  332.     if((fileLenght > 0) || zeroFlag) {    // if there's still something in it or it had 0 length last time we checked...
  333.             // (we do not report that a file has shrunken to 0 bytes the first time we notice it. there are
  334.             // commands in /usr/adm/daily which produce length-0-files for a very short time (e.g. with 'tail'))
  335.         printf("filespy: file %s reduced its size from %ld to %ld bytes\n", [self stringValue], lastFileLenght, fileLenght);
  336.         switch(fileMode) {
  337.         case RADIO_NEW: lastFileLenght = fileLenght; break;
  338.         case RADIO_ENTIRE: lastFileLenght = 0; break;
  339.         }
  340.         zeroFlag = NO;
  341.     } else {
  342.         zeroFlag = YES;            // first check that this file has length 0 for a longer time
  343.         fileLenght = lastFileLenght;    // ignore new filelength it until next check
  344.     }
  345.     [self contentChanged];            // however, something did change
  346.     }
  347.     if(fileLenght > lastFileLenght) {        // if the file grew then get the new text and send it to addText
  348.     int diff = fileLenght - lastFileLenght;
  349.     char *newString = malloc(diff + 2);
  350.     FILE *fp;
  351.  
  352.     if((fp = fopen([self stringValue], "r")) == (FILE *)NULL) {
  353.         printf("filespy: couldn't open file %s\n", [self stringValue]);
  354.     } else {
  355.         if(fseek(fp, lastFileLenght, SEEK_SET) == 0) {
  356.         if(fread(newString, sizeof(char), diff, fp) != 0) {
  357.             newString[diff] = '\000';
  358.             newString[diff+1] = '\000';
  359.             [self addText:newString];
  360.         } else {
  361.             perror("filespy: fread");
  362.         }
  363.         } else {
  364.         perror("filespy: fseek");
  365.         }
  366.         fclose(fp);
  367.     }
  368.     free(newString);
  369.     } else if(fileLenght == lastFileLenght)
  370.     textloaded = YES;
  371.     return self;
  372. }
  373.  
  374. - readDir
  375. // the contents of a directory and remember it
  376. {
  377.     DIR *dirp;
  378.     struct direct *dp;
  379.     unsigned int pos = 0, count = [dirStore count];
  380.     node root, actnode, tmpnode;    // pointers to nodedescs
  381.     
  382.     if((dirp = opendir([self stringValue])) == NULL) {
  383.     if(errno == EACCES)
  384.         printf("filespy: no permission to read directory %s\n", [self stringValue]);
  385.     else
  386.         perror("filespy: opendir");
  387.     return self;
  388.     }
  389.     actnode = root = NULL;
  390.     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
  391.     if((pos >= count) || (strcmp(dp->d_name, [dirStore elementAt:pos]) != 0)) {
  392.         unsigned int i = pos;
  393.         while(i < count && (strcmp(dp->d_name, (char *)[dirStore elementAt:i]) != 0))
  394.         i++;
  395.         if(i >= count) {        // not found, so it's a new file
  396.                         // remember all new files in a linked list so that we can print them later
  397.         char str[MAXFILENAMELENGHT + 10] = "new file: ";
  398.  
  399.         [dirStore insertElement:dp->d_name at:pos];    // remember new file
  400.         count++;
  401.         strcat(str, dp->d_name);
  402.         strcat(str, "\n");
  403.         tmpnode = (node)malloc(sizeof(nodedesc));
  404.         if(actnode != NULL) {
  405.             actnode->next = tmpnode;
  406.         } else {
  407.             root = tmpnode;
  408.         }
  409.         tmpnode->string = (char *)malloc(strlen(str)+1);
  410.         strcpy(tmpnode->string, str);
  411.         tmpnode->next = NULL;
  412.         actnode = tmpnode;
  413.         } else {            // found, so files between pos and i are deleted
  414.                         // remember the deleted files so that we can print them later
  415.         unsigned int j;
  416.         for(j = pos; j < i; j++) {
  417.             char str[MAXFILENAMELENGHT + 14] = "removed file: ";
  418.             strcat(str, (char *)[dirStore elementAt:j]);
  419.             strcat(str, "\n");
  420.             tmpnode = (node)malloc(sizeof(nodedesc));
  421.             if(actnode != NULL) {
  422.             actnode->next = tmpnode;
  423.             } else {
  424.             root = tmpnode;
  425.             }
  426.             tmpnode->string = (char *)malloc(strlen(str)+1);
  427.             strcpy(tmpnode->string, str);
  428.             tmpnode->next = NULL;
  429.             actnode = tmpnode;
  430.             [dirStore removeElementAt:j];
  431.             count--;
  432.         }
  433.         }
  434.     }
  435.     pos++;
  436.     }
  437.     closedir(dirp);
  438.     if(pos < count) {        // rest of the files in dirStore have been deleted
  439.     unsigned int i, max = [dirStore count];
  440.     for(i = pos; i < max; i++) {
  441.         char str[MAXFILENAMELENGHT + 14] = "removed file: ";
  442.         strcat(str, (char *)[dirStore elementAt:pos]);
  443.         strcat(str, "\n");
  444.         tmpnode = (node)malloc(sizeof(nodedesc));
  445.         if(actnode != NULL) {
  446.         actnode->next = tmpnode;
  447.         } else {
  448.         root = tmpnode;
  449.         }
  450.         tmpnode->string = (char *)malloc(strlen(str)+1);
  451.         strcpy(tmpnode->string, str);
  452.         tmpnode->next = NULL;
  453.         actnode = tmpnode;
  454.         [dirStore removeElementAt:pos];
  455.     }
  456.     }
  457.     if(root != NULL) {
  458.                 // contents of the directory changed (linked list exists), so put all changes together in
  459.             // a single string and pass it to addText
  460.     unsigned int len = 0;
  461.     char *totstr, *actstr;
  462.  
  463.     actnode = root;
  464.     while(actnode != NULL) {
  465.         len += strlen(actnode->string);
  466.         actnode = actnode->next;
  467.     }
  468.     actstr = totstr = (char *)malloc(len+1);
  469.     actnode = root;
  470.     while(actnode != NULL) {
  471.         strcpy(actstr, actnode->string);
  472.         actstr = actstr + strlen(actnode->string);
  473.         actnode = actnode->next;
  474.     }
  475.     [self addText:totstr];
  476.     free(totstr);
  477.     actnode = root;            // remove the linked list
  478.     while(actnode != NULL) {
  479.         tmpnode = actnode;
  480.         actnode = actnode->next;
  481.         free(tmpnode->string);
  482.         free(tmpnode);
  483.     }
  484.     }
  485.     return self;
  486. }
  487.  
  488. - addText:(char *)givenString
  489. // output the text givenString depending on the settings and preferences of the user
  490. {
  491.     BOOL changed = YES, filterMode, mode = NO;
  492. // Bugreport Nr. 5 and 6 by Karsten Heinze: malloc()ed memory was too small (I forgot the terminating 0-Byte)
  493.     char *newString = (char *)malloc(strlen(givenString)+1);
  494.     int textlenght;
  495.  
  496.     if(maxLines > 0)        // if a maximum number of lines is set, then cut the old parts of the existing text off
  497.     [self shortText];
  498.     filterMode = [myDelegate filterMode];
  499.     strcpy(newString, givenString);
  500.     textlenght = [myText textLength];
  501.     if(useFilter) {        // if the filter is active, the filter the text first
  502.     switch(filterMode) {
  503.         case FILTER_DONTCOPY: mode = YES; break;
  504.         case FILTER_DONTBEEP: mode = NO; break;
  505.     }
  506.     changed = [myDelegate filterString:newString andRemove:mode who:self];
  507.     if(timeStamp && (changed || (filterMode == FILTER_DONTBEEP))) {        // insert timestamp in front of each line
  508.         [self addTimeStamp:&newString];
  509.     }
  510.     if(changed || (filterMode == FILTER_DONTBEEP)) {
  511.         [self appendText:newString unfilteredString:givenString didChange:changed];
  512.     }                                
  513.     } else {        // without filter, just add the timeStamp if that feature is active and append the text
  514.     if(timeStamp) {
  515.         [self addTimeStamp:&newString];
  516.     }
  517.     [self appendText:newString unfilteredString:givenString didChange:changed];
  518.     }
  519.     if(fileType == FILETYPE_FILE)
  520.     textloaded = YES;
  521.     else if(fileType == FILETYPE_DIR)
  522.     dirloaded = YES;
  523.     free(newString);
  524.     return self;
  525. }
  526.  
  527. - appendText:(char *)string unfilteredString:(char *)ufString didChange:(BOOL)changed
  528. // append string to the text-object in myWindow. changed is YES if something passed the filter (or if the filter is not active)
  529. {
  530.     int textlenght;
  531.     
  532.     textlenght = [myText textLength];
  533.     [myText setSel :textlenght :textlenght];
  534.     [myText replaceSel:string];
  535.     [myText setSel :textlenght :[myText textLength]];
  536.     [[[myText sizeToFit] scrollSelToVisible] display];
  537.  
  538.     if(!useFilter || changed) {
  539.     if(autoPopUp && !logToSuperlog)
  540.         [self displayWindow];
  541.     if(beepOnChange && !logToSuperlog)
  542.         [myDelegate nxbeep];
  543.     }
  544.     [self contentChanged];
  545.     if(logToSuperlog && (fileex || !dontCopySuperlog)) {
  546.         if([myDelegate superUsesFilter]) {
  547.         [myDelegate updateSuperlog:self :string :changed :beepOnChange :autoPopUp];
  548.     } else {        // if the superlog doesn't use the filter, then send the unchanged string
  549.         // changed is YES here, because nothing could have been filtered out if the multilog doesn't filter
  550.         [myDelegate updateSuperlog:self :ufString :YES :beepOnChange :autoPopUp];
  551.     }
  552.     }
  553.     return self;
  554. }
  555.  
  556. - (BOOL)changedState
  557. // checks whether anything changed with this file
  558. {
  559.     int ret = 0;
  560.     
  561.     if((spytype & SPY_CONTENTS) != 0) {
  562.     if((fileType == FILETYPE_FILE) && (fileLenght != lastFileLenght)) {
  563.         ret++;
  564.     } else if((fileType == FILETYPE_DIR) && (mtime != lastmtime)) {
  565.         ret++;
  566.     }
  567.     }
  568.     if((spytype & SPY_EXISTENCE) != 0) {
  569.     if((fileex != oldfileex) || (permissionOK != oldpermissionOK)) {
  570.         ret++;
  571.     }
  572.     }
  573.     return ret > 0;
  574. }
  575.  
  576. - displayWindow
  577. {
  578.     if(![myWindow isVisible]) {
  579.     [myWindow orderFrontRegardless];
  580.     [myDelegate addWindow:self];
  581.     }
  582.     if([myDelegate becomeKey]) {
  583.     [myWindow makeKeyWindow];
  584.     [myDelegate unhideApp:self];
  585.     }
  586.     return self;
  587. }
  588.  
  589. - moveWindowTo:(NXCoord)x:(NXCoord)y
  590. {
  591.     [myDoc moveWindowTo:x:y];
  592.     return self;
  593. }
  594.  
  595. - setFileMode:(int)mode
  596. {
  597.     fileMode = mode;
  598.     return self;
  599. }
  600.  
  601. - (int)fileMode
  602. {
  603.     return fileMode;
  604. }
  605.  
  606. - setAutoPopUp:(BOOL)state
  607. {
  608.     autoPopUp = state;
  609.     return self;
  610. }
  611.  
  612. - (BOOL)autoPopUp
  613. {
  614.     return autoPopUp;
  615. }
  616.  
  617. - window
  618. {
  619.     return myWindow;
  620. }
  621.  
  622. - setMyDelegate:sender
  623. {
  624.     myDelegate = sender;
  625.     exMatrix = [myDelegate existenceMatrix];
  626.     return self;
  627. }
  628.  
  629. - myDelegate
  630. {
  631.     return myDelegate;
  632. }
  633.  
  634. - setBeepOnChange:(BOOL)state
  635. {
  636.     beepOnChange = state;
  637.     return self;
  638. }
  639.  
  640. - (BOOL)beepOnChange
  641. {
  642.     return beepOnChange;
  643. }
  644.  
  645. - setLogToSuperlog:(BOOL)state;
  646. {
  647.     logToSuperlog = state;
  648.     return self;
  649. }
  650.  
  651. - (BOOL)logToSuperlog
  652. {
  653.     return logToSuperlog;
  654. }
  655.  
  656. - setUseFilter:(BOOL)state
  657. {
  658.     useFilter = state;
  659.     return self;
  660. }
  661.  
  662. - (BOOL)useFilter
  663. {
  664.     return useFilter;
  665. }
  666.  
  667. - setDontCopy:(BOOL)state
  668. {
  669.     dontCopySuperlog = state;
  670.     return self;
  671. }
  672.  
  673. - (BOOL)dontCopy
  674. {
  675.     return dontCopySuperlog;
  676. }
  677.  
  678. - setTimeStamp:(BOOL)state
  679. {
  680.     timeStamp = state;
  681.     return self;
  682. }
  683.  
  684. - (BOOL)timeStamp;
  685. {
  686.     return timeStamp;
  687. }
  688.  
  689. - setSpytype:(unsigned short int)type
  690. {
  691.     int rowCount, colCount;
  692.     if((type & SPY_EXISTENCE) != (spytype & SPY_EXISTENCE)) {        // if the type changed in existence
  693.     if(!exCell) {                        // we haven't spyed for existence yet
  694.             // so insert new exCell in existence-matrix
  695.         [exMatrix addRow];
  696.         [exMatrix getNumRows:&rowCount numCols:&colCount];
  697.         exCell = [exMatrix cellAt:rowCount-1 :0];
  698.         [exCell setStringValue:[self stringValue]];
  699.         [exCell setMyDelegate:self];
  700.         [exCell setTime:time(NULL)];
  701.         [exMatrix sizeToCells];
  702.         [exMatrix scrollCellToVisible:rowCount-1 :0];
  703.         [exMatrix display];
  704.     } else {
  705.             // we were spying for this file, so we aren't any longer from now on -> remove the exCell
  706.         [exMatrix getRow:&rowCount andCol:&colCount ofCell:exCell];
  707.         [exMatrix removeRowAt:rowCount andFree:YES];
  708.         [exMatrix display];
  709.         exCell = nil;
  710.     }
  711.     }
  712.     spytype = type;
  713.     return self;
  714. }
  715.  
  716. - (unsigned short int)spytype
  717. {
  718.     return spytype;
  719. }
  720.  
  721. - updateExistence
  722. {
  723.     time_t myTime = time(NULL);
  724.     List *tmpList = [SpyTextFieldCell sharedTmpList];
  725.  
  726.     if((fileex != oldfileex) || (permissionOK != oldpermissionOK)) {        // existence or permission changed
  727.     [tmpList addObject:exCell];        // remember cell to update later in controller
  728.     if(fileex)
  729.         [exCell setTime:myTime];
  730.     else
  731.         [exCell changeTime:myTime];
  732.     if(beepOnChange)
  733.         [myDelegate nxbeep];
  734.     if(autoPopUp)
  735.         [myDelegate showExistencelogRegardless];
  736.     }
  737.     return self;
  738. }
  739.  
  740. - setSuperCopyFilename:(unsigned int)state
  741. {
  742.     superCopyFilename = state;
  743.     return self;
  744. }
  745.  
  746. - (unsigned int)superCopyFilename;
  747. {
  748.     return superCopyFilename;
  749. }
  750.  
  751. - setStringValue:(const char *)str
  752. {
  753.     [myWindow setTitleAsFilename:str];
  754.     return [super setStringValue:str];
  755. }
  756.  
  757. - clearBuffer:sender
  758. {
  759.     [myText setSel :0 :[myText textLength]];
  760.     [myText replaceSel:""];
  761.     [[myText sizeToFit] scrollSelToVisible];
  762.     [self contentChanged];
  763.     return self;
  764. }
  765.  
  766. - contentChanged
  767. {
  768.     if(![myWindow isKeyWindow] && ![myWindow isDocEdited])
  769.     [myWindow setDocEdited:YES];
  770.     return self;
  771. }
  772.  
  773. - fileRemoved
  774. // prepares string that says which file/directories have been removed
  775. {
  776.     char str[MAXFILENAMELENGHT + 14] = "removed ";
  777.  
  778.     if(fileType == FILETYPE_FILE) {    // this file has been removed
  779.     strcat(str, "file: ");
  780.     } else if(fileType == FILETYPE_DIR) { // the whole directory has been removed, so output the names of all the files in it
  781.     if([dirStore count] > 2) {
  782.         unsigned int i, max = [dirStore count];
  783.         node root, actnode, tmpnode;
  784.     
  785.         actnode = root = NULL;
  786.         for(i = 2; i < max; i++) {
  787.         char str[MAXFILENAMELENGHT + 14] = "removed file: ";
  788.         strcat(str, (char *)[dirStore elementAt:2]);
  789.         strcat(str, "\n");
  790.         tmpnode = (node)malloc(sizeof(nodedesc));
  791.         if(actnode != NULL) {
  792.             actnode->next = tmpnode;
  793.         } else {
  794.             root = tmpnode;
  795.         }
  796.         tmpnode->string = (char *)malloc(strlen(str)+1);
  797.         strcpy(tmpnode->string, str);
  798.         tmpnode->next = NULL;
  799.         actnode = tmpnode;
  800.         [dirStore removeElementAt:2];
  801.         }
  802.         if(root != NULL) {
  803.         unsigned int len = 0;
  804.         char *totstr, *actstr;
  805.  
  806.         actnode = root;
  807.         while(actnode != NULL) {
  808.             len += strlen(actnode->string);
  809.             actnode = actnode->next;
  810.         }
  811.         actstr = totstr = (char *)malloc(len+1);
  812.         actnode = root;
  813.         while(actnode != NULL) {
  814.             strcpy(actstr, actnode->string);
  815.             actstr = actstr + strlen(actnode->string);
  816.             actnode = actnode->next;
  817.         }
  818.         [self addText:totstr];
  819.         free(totstr);
  820.         actnode = root;
  821.         while(actnode != NULL) {
  822.             tmpnode = actnode;
  823.             actnode = actnode->next;
  824.             free(tmpnode->string);
  825.             free(tmpnode);
  826.         }
  827.         }
  828.     }
  829.     strcat(str, "dir: ");
  830.     }
  831.     strcat(str, [self stringValue]);
  832.     if(!(spytype & SPY_EXISTENCE))        // only print message if we're not spying for existence
  833.     puts(str);                // otherwise we have to update the existence-log
  834.     fileType = FILETYPE_NOTHING;
  835.     fileex = NO;
  836.     return self;
  837. }
  838.  
  839. - windowDidBecomeKey:sender
  840. {
  841.     if([sender isDocEdited])
  842.     [sender setDocEdited:NO];
  843.     [myDelegate windowDidBecomeKey:sender];
  844.     return self;
  845. }
  846.  
  847. - windowWillClose:sender
  848. {
  849.     [myDelegate windowWillClose:sender];
  850.     return self;
  851. }
  852.  
  853. - text
  854. {
  855.     return myText;
  856. }
  857.  
  858. - (NXColor)color
  859. {
  860.     return color;
  861. }
  862.  
  863. - setColor:(NXColor)newColor
  864. {
  865.     color = newColor;
  866.     [myText setTextColor:color];
  867.     [myText display];
  868.     return self;
  869. }
  870.  
  871. - setSpyFont:newFont
  872. {
  873.     myFont = newFont;
  874.     [myText setFont:myFont];
  875.     [myText scrollSelToVisible];
  876.     return self;
  877. }
  878.  
  879. - addTimeStamp:(char **)string
  880. // add current time in front of each line in string
  881. {
  882. #define TIMELEN 19        // length of timestring
  883.     time_t mytime = time((time_t *)NULL);
  884.     struct tm *tp = localtime(&mytime);
  885.     char tstr[TIMELEN], *tmpText, *p, *cp;
  886.     unsigned int n = 0, tlen;
  887.     BOOL copied;
  888.     
  889.     strftime(tstr, (size_t)TIMELEN, "{%b %d %H:%M:%S} ", tp);
  890.     tlen = strlen(tstr);
  891.     p = *string;
  892.     while(*p != '\000') {
  893.     if(*p++ == '\n')
  894.         n++;    // count number of lines
  895.     }
  896.     tmpText = (char *)malloc(strlen(*string) + n*TIMELEN + 1);
  897.     tmpText[0] = '\000';
  898.     p = *string;
  899.     cp = tmpText;
  900.     copied = NO;
  901.     while(*p != '\000') {
  902.     if(!copied) {
  903.         strcpy(cp, tstr);
  904.         cp += tlen;
  905.         copied = YES;
  906.     }
  907.     *cp++ = *p;
  908.     if(*p++ == '\n')
  909.         copied = NO;
  910.     }
  911.     *cp = '\000';
  912.     free(*string);
  913.     *string = tmpText;
  914.     return self;
  915. }
  916.  
  917. - spyFont
  918. {
  919.     return myFont;
  920. }
  921.  
  922. - setMaxLines:(int)lines
  923. {
  924.     maxLines = lines;
  925.     return self;
  926. }
  927.  
  928. - shortText
  929. // remove lines from the beginning of the text so that the text has not more than maxLines lines
  930. {
  931.     int textLen = [myText textLength];
  932.     int textLines = [myText lineFromPosition:textLen];
  933.  
  934.     if(textLines > maxLines) {
  935.     [[myText setSel:0 :[myText positionFromLine:(textLines - maxLines)]] replaceSel:""];
  936.     }
  937.     return self;
  938. }
  939.  
  940. - (BOOL)permissionOk
  941. {
  942.     return permissionOK;
  943. }
  944.  
  945. - setShowTime:(BOOL)state
  946. {
  947. //    [exCell setTime:time(NULL)];
  948.     showTime = state;
  949.     return self;
  950. }
  951.  
  952. - (BOOL)showTime
  953. {
  954.     return showTime;
  955. }
  956.  
  957. - (BOOL)askUpdateTime
  958. {
  959.     return (spytype & SPY_EXISTENCE) && fileex;    // need to update time in existenceTextFieldCell
  960. }
  961.  
  962. - drawInside:(const NXRect *)cellFrame inView:controlView    // taken from 'ScrollDoodScroll' and modified
  963. {
  964. #define IMAGEMARGIN 4.0
  965.     /* every CustomCell needs these two */
  966.     static id sharedTextCell = nil;
  967.     NXRect rect = *cellFrame;
  968.     NXRect boxRect;
  969.     NXCoord size;
  970.     NXSize ESize;
  971.     NXPoint imageOrigin;
  972.  
  973.   /* erase the cell */
  974.     PSsetgray((cFlags1.state || cFlags1.highlighted) ? NX_WHITE : NX_LTGRAY);
  975.     NXRectFill(cellFrame);
  976.     size = NX_HEIGHT(cellFrame) - 2*IMAGEMARGIN;
  977.     if(spytype & SPY_CONTENTS) {    // If we're only spying for existence, we don't need a colorbox
  978.     NXSetColor(color);
  979.     NXSetRect(&boxRect, NX_X(cellFrame) + IMAGEMARGIN, NX_Y(cellFrame) + IMAGEMARGIN,
  980.         size, size);
  981.     NXRectFill(&boxRect);
  982.     } else {
  983.     if(!EImage) {
  984.         EImage = [NXImage findImageNamed:"E"];
  985.     }
  986.     [EImage getSize:&ESize];
  987.     imageOrigin.x = NX_X(cellFrame) + IMAGEMARGIN;
  988.     imageOrigin.y = NX_Y(cellFrame) + NX_HEIGHT(cellFrame) -
  989.             (NX_HEIGHT(cellFrame) - ESize.height) / 2.0;
  990.     
  991.     [EImage composite:NX_SOVER toPoint:&imageOrigin];
  992.     }
  993.     NX_WIDTH(&rect) -= (size + IMAGEMARGIN * 2.0 - NX_X(&rect));
  994.     NX_X(&rect) += size + IMAGEMARGIN * 2.0;
  995.  
  996.     if (!sharedTextCell) {
  997.         sharedTextCell = [[Cell alloc] init];
  998.     [sharedTextCell setWrap:NO];
  999.     }
  1000.     [sharedTextCell setFont:[self font]];
  1001.     [sharedTextCell setStringValue:[self stringValue]];
  1002.     [sharedTextCell drawInside:&rect inView:controlView];
  1003.     
  1004.   /* all drawing from now on will be in dark gray */
  1005.     PSsetgray(NX_DKGRAY);
  1006.     
  1007.   /* draw the two dark gray lines above and below the cell */
  1008.     if (cFlags1.state || cFlags1.highlighted) {
  1009.         NXRect rectArray[2];
  1010.       /*
  1011.        * draw 1-pixel tall rectangles instead of lines (this is faster than
  1012.        * PSmoveto(); PSlineto()).
  1013.        */
  1014.     NXSetRect(&(rectArray[0]), NX_X(cellFrame), NX_Y(cellFrame),
  1015.         NX_WIDTH(cellFrame), 1.0);
  1016.     NXSetRect(&(rectArray[1]), NX_X(cellFrame), NX_MAXY(cellFrame) - 1.0,
  1017.         NX_WIDTH(cellFrame), 1.0);
  1018.  
  1019.       /* using NXRectFillList is faster than separate calls to NXRectFill */
  1020.     NXRectFillList(rectArray, 2);
  1021.     }
  1022.  
  1023.     return self;
  1024. }
  1025.  
  1026. - highlight:(const NXRect *)cellFrame inView:controlView lit:(BOOL)flag
  1027. {
  1028.     if (cFlags1.highlighted != flag) {
  1029.     cFlags1.highlighted = flag;
  1030.     [self drawInside:cellFrame inView:controlView];
  1031.     }
  1032.     return self;
  1033. }
  1034.  
  1035. @end
  1036.