home *** CD-ROM | disk | FTP | other *** search
/ Amiga ISO Collection / AmigaUtilCD2.iso / Programming / C / SASC6571.LZX / extras / smfind / SMFind.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-12-24  |  11.8 KB  |  506 lines

  1. /*-------------------------------------------------------------------*/
  2. /* Copyright (c) 1993 SAS Institute, Inc.  All Rights Reserved.      */
  3. /* Author: Doug Walker                                               */
  4. /* Support: walker                                                   */
  5. /*-------------------------------------------------------------------*/
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9. #include <dos/dos.h>
  10. #include <exec/memory.h>
  11. #include <exec/execbase.h>
  12. #include <workbench/startup.h>
  13.  
  14. #include <proto/exec.h>
  15. #include <proto/dos.h>
  16. #include <proto/wb.h>
  17. #include <proto/intuition.h>
  18.  
  19. #include "smcomm.h"
  20.  
  21. #include "toolversion.h"
  22.  
  23. static const char ver[] = "$VER: SAS/C_SMFind " TOOLVER TOOLDATE;
  24.  
  25. int ParseFile(char *file, char *pattern);
  26. int GetLine(BPTR fh, char *buf, int buflen);
  27.  
  28. #define BSIZ 512
  29.  
  30. char buf[BSIZ];
  31.  
  32. #define V37 (SysBase->LibNode.lib_Version >= 36) 
  33. extern struct ExecBase *SysBase;
  34.  
  35. struct MYALLOC
  36. {
  37.    long len;
  38.    struct MYALLOC *next;
  39. };
  40.  
  41. static struct MYALLOC *first;
  42.  
  43. void __stdargs _MemCleanup(void){}
  44.  
  45. void *myalloc(long len)
  46. {
  47.    struct MYALLOC *new;
  48.    if(new = AllocMem(len+sizeof(struct MYALLOC), 0))
  49.    {
  50.       new->len = len;
  51.       new->next = first;
  52.       first = new;
  53.    }
  54.    return(new+1);
  55. }
  56.  
  57. static BPTR oldcurdir;
  58. static int dirchanged;
  59.  
  60. void _STDmyfree(void)
  61. {
  62.    struct MYALLOC *tmp;
  63.    struct MYALLOC *next;
  64.    for(tmp=first; tmp; tmp=next)
  65.    {
  66.       next = tmp->next;
  67.       FreeMem(tmp, tmp->len+sizeof(struct MYALLOC));
  68.    }
  69.    if(dirchanged)
  70.    {
  71.       CurrentDir(oldcurdir);
  72.       dirchanged = 0;
  73.    }
  74. }
  75.  
  76. // NAME:    msg
  77. // PURPOSE: Communicate a message to the user
  78. // NOTES:   Uses Output() if invoked from the CLI
  79. //          Uses an AutoRequest if invoked from WorkBench
  80.  
  81. void msg(char *m)
  82. {
  83.    struct Library *IntuitionBase;
  84.    struct IntuiText itext;
  85.    static const struct IntuiText ntext =
  86.    {
  87.       (unsigned char)-1, (unsigned char)-1, 
  88.       0, 0, 0, NULL, "Cancel", NULL
  89.    };
  90.  
  91.    if(Output())
  92.    {
  93.       Write(Output(), m, strlen(m));
  94.    }
  95.    else
  96.    {
  97.       if(IntuitionBase = OpenLibrary("intuition.library", 0))
  98.       {
  99.          memcpy(&itext, &ntext, sizeof(itext));
  100.          itext.LeftEdge = 14;
  101.          itext.TopEdge = 14;
  102.          itext.IText = m;
  103.  
  104.          AutoRequest(NULL, &itext, NULL, (struct IntuiText *)&ntext, 
  105.                      0L, 0L, 400L, 60L);
  106.  
  107.          CloseLibrary(IntuitionBase);
  108.       }
  109.  
  110.    }
  111. }
  112.  
  113. // NAME:    FullName
  114. // PURPOSE: get the full pathname of a file.
  115. // NOTES:   Under AmigaDOS 2.0, use NameFromLock.
  116. //          Under 1.3, we should do this ourselves, but instead we punt and 
  117. //             just copy the incoming name over.
  118.  
  119. void FullName(BPTR parent, char *name, char *buf, int len)
  120. {
  121.    int i;
  122.    if(V37)
  123.    {
  124.       if(NameFromLock(parent, buf, len-1))
  125.       {
  126.          i = strlen(buf);
  127.          if(buf[i-1] != ':' && buf[i-1] != '/')
  128.          {
  129.             buf[i++] = '/';
  130.             buf[i] = 0;
  131.          }
  132.       }
  133.    }
  134.    else
  135.       i = 0;
  136.  
  137.    if(i < len-1) strncpy(buf+i, name, len-i-1);
  138.  
  139.    return;
  140. }
  141.  
  142. #define READ(x,l) Read(Input(), x, l)
  143.  
  144. #define SEPS " \t\n="
  145.  
  146. struct ARGPARSE
  147. {
  148.    int argc;
  149.    char **argv;
  150.    char *cmdbuf;
  151.    int cmdlen;
  152. };
  153.  
  154. // NAME:    NextArg
  155. // PURPOSE: Return the next command-line or WorkBench argument
  156. // NOTES:   If invoked from the CLI, this just parses the incoming command-line
  157. //             into tokens.
  158. //          If invoked from WorkBench, this walks the WBArg list.
  159. //          This function also implements the ? command-line option.
  160.  
  161. extern struct WBStartup *_WBenchMsg;
  162.  
  163. char *NextArg(struct ARGPARSE *ap)
  164. {
  165.    static struct WBStartup WBM;
  166.    static char buf[256];
  167.    static struct WBArg *wba;
  168.    char *name;
  169.  
  170.    if(ap->argc == 0)
  171.    {
  172.       /* Workbench startup */
  173.       if(_WBenchMsg == NULL) return(NULL);  // No WB arguments
  174.  
  175.       if(wba == NULL) 
  176.       {
  177.          /* This is the first time we have been called.    */
  178.          /* Make a copy of the WBenchMsg and set up wba so */
  179.          /* we can use it to traverse all the arguments.   */
  180.          WBM = *_WBenchMsg;
  181.          WBM.sm_NumArgs--;
  182.          wba = WBM.sm_ArgList;
  183.          dirchanged = 1;
  184.          oldcurdir = CurrentDir(wba->wa_Lock);
  185.          if(!stricmp(wba->wa_Name, "Find"))
  186.          {
  187.             /* They clicked on a "Find" icon in a directory */
  188.             /* Pretend that's the location of the actual program */
  189.             wba++;
  190.             WBM.sm_NumArgs--;
  191.          }
  192.       }
  193.  
  194.       if(WBM.sm_NumArgs <= 0) return(NULL);  // No WB arguments
  195.  
  196.       wba++;                   // Advance to the next argument
  197.       WBM.sm_NumArgs--;  // Reduce the argument count
  198.  
  199.       if(!strcmp(wba->wa_Name, "Find"))
  200.       {
  201.          /* This is the WB icon for "Find", so ignore it */
  202.          if(!dirchanged)
  203.          {
  204.             dirchanged = 1;
  205.             oldcurdir = CurrentDir(wba->wa_Lock);
  206.          }
  207.          else
  208.             CurrentDir(wba->wa_Lock);
  209.          wba++;
  210.          WBM.sm_NumArgs--;
  211.          if(WBM.sm_NumArgs <= 0) return(NULL);
  212.       }
  213.  
  214.       /* Get the full pathname of the argument into 'buf' */
  215.       FullName(wba->wa_Lock, wba->wa_Name, buf, sizeof(buf));
  216.  
  217.       /* Allocate memory to store the argument name */
  218.       name = myalloc(strlen(buf)+1);
  219.       strcpy(name, buf);
  220.  
  221.       return(name);
  222.    }
  223.    else if(ap->argc == -1)
  224.    {
  225.       /* We previously did a ? operation and the user entered data */
  226.       /* This data is now all set up to use as our "command line"  */
  227.       /* so just call strtok() to get the next token.              */
  228.       return(strtok(NULL, SEPS));
  229.    }
  230.    else if(ap->argc < 2)
  231.    {
  232.       /* Some kind of weird error.  This should never happen. */
  233.       return(NULL);
  234.    }
  235.    else
  236.    {
  237.       /* Normal, everyday CLI command-line argument. */
  238.       /* Use argc and argv to get it.                */
  239.       ap->argc--;
  240.       ap->argv++;
  241.       if(ap->argc == 1 && !strcmp("?", ap->argv[0]))
  242.       {
  243.          // They want help; give it to them.
  244.          msg("PATTERN/K/A,FILES/M: ");
  245.          if(READ(ap->cmdbuf, ap->cmdlen) <= 0) return((char *)-1);
  246.  
  247.          ap->argc = -1;  // Indicates that from now on, we are to use strtok
  248.                          // to get arguments instead of argc, argv
  249.  
  250.          return(strtok(ap->cmdbuf, SEPS));
  251.       }
  252.       else
  253.       {
  254.          // No help requested, just return the current argument
  255.          return(ap->argv[0]);
  256.       }
  257.    }
  258.    // No return necessary since all cases above have a return
  259. }
  260.  
  261. // NAME:    query
  262. // PURPOSE: query the user for input
  263. // NOTES:   Opens a CON: window the first time it is called.
  264. //          Does not close the CON: window until it is called
  265. //             with a NULL prompt argument.  This prevents annoying
  266. //             multiple popup CON: windows.
  267.  
  268. char *query(char *prompt)
  269. {
  270.    static BPTR qwin;
  271.    int i;
  272.    char tmpbuf[256];
  273.    char *reslt = NULL;
  274.  
  275.    if(!prompt)
  276.    {
  277.       // Null prompt string means close the window.
  278.       if(qwin) Close(qwin);
  279.       qwin = NULL;
  280.       return(NULL);
  281.    }
  282.  
  283.    // Open the window if necessary
  284.    if(qwin || (qwin=Open("CON:0/0/450/100/SMFind", MODE_NEWFILE)))
  285.    {
  286.       Write(qwin, prompt, strlen(prompt));
  287.       i = Read(qwin, tmpbuf, sizeof(tmpbuf));
  288.       if(i > 1)
  289.       {
  290.          tmpbuf[i-1] = 0;
  291.          reslt = myalloc(strlen(tmpbuf)+1);
  292.          strcpy(reslt, tmpbuf);
  293.       }
  294.    }
  295.  
  296.    return(reslt);
  297. }
  298.  
  299. // Copy the "from" string to the "to" string
  300. // Return the address of the first unused byte in the "to" string
  301. static char *updatestr(char *to, char *from)
  302. {
  303.    int i;
  304.    i = strlen(from);
  305.    memcpy(to, from, i);
  306.    return(to+i);
  307. }
  308.  
  309. // Create a temporary filename
  310. char *tmpnam(char *s)
  311. {
  312.    int i;
  313.    char *tmpptr;
  314.    BPTR lock;
  315.  
  316.    strcpy(s, "T:TMPsmfind0");
  317.    tmpptr = s + 11;
  318.    for(i=0; i<10; i++)
  319.    {
  320.       *tmpptr = '0' + i;
  321.       if(lock=Lock(s, SHARED_LOCK))
  322.          UnLock(lock);
  323.       else if(IoErr() == ERROR_OBJECT_NOT_FOUND)
  324.          return(s);
  325.    }
  326.    return(NULL);
  327. }
  328.  
  329. int main(int argc, char *argv[])
  330. {
  331.    char *pattern;
  332.    static char tmpfile[BSIZ];
  333.    char cmdbuf[256];
  334.    BPTR fh1, fh2;
  335.    struct ARGPARSE ap;
  336.    char *arg;
  337.    char *tmpptr;
  338.  
  339.    // Set up the argument parsing structure
  340.    ap.argc = argc;
  341.    ap.argv = argv;
  342.    ap.cmdbuf = cmdbuf;
  343.    ap.cmdlen = sizeof(cmdbuf);
  344.  
  345.    if(argc == 0)
  346.    {
  347.       /* Invoked from WorkBench - query user for pattern */
  348.       pattern = query("Enter search string: ");
  349.    }
  350.    else
  351.    {
  352.       pattern = NextArg(&ap);
  353.       if(pattern && !stricmp(pattern, "pattern"))
  354.          pattern = NextArg(&ap);
  355.    }
  356.    if(!pattern)
  357.    {
  358.       // No pattern specified - error exit
  359.       query(NULL);  // Close query window if open
  360.  
  361.       usage:
  362.       if(Output())
  363.          msg("USAGE: smfind <pattern> <file> [<file>...]\n");
  364.       return(20);
  365.    }
  366.  
  367.    if(!(arg=NextArg(&ap)) && argc == 0)
  368.    {
  369.       arg = query("Enter file or file pattern: ");
  370.    }
  371.  
  372.    query(NULL);  // Close query window if open
  373.  
  374.    // If no files specified, it's an error
  375.    if(!arg) goto usage;
  376.  
  377.    // Clear the results of the last SMFIND run from SCMSG
  378.    ClearSCMSG("smfind");
  379.  
  380.    while(arg)
  381.    {
  382.       // Create a new temporary filename
  383.       if(!tmpnam(tmpfile))
  384.       {
  385.          if(Output())
  386.             msg("ERROR: Can't create temporary file\n");
  387.          break;
  388.       }
  389.  
  390.       // Execute the sc:c/grep command, redirecting its output to
  391.       // our temporary file
  392.       tmpptr = updatestr(buf, "sc:c/grep >");
  393.       tmpptr = updatestr(tmpptr, tmpfile);
  394.       *(tmpptr++) = ' ';
  395.       tmpptr = updatestr(tmpptr, pattern);
  396.       *(tmpptr++) = ' ';
  397.       tmpptr = updatestr(tmpptr, arg);
  398.       *tmpptr = 0;
  399.  
  400.       fh1 = Open("nil:", MODE_NEWFILE);
  401.       fh2 = Open("nil:", MODE_NEWFILE);
  402.       Execute(buf, fh1, fh2);
  403.       Close(fh1);
  404.       Close(fh2);
  405.  
  406.       // Parse the results of the grep command, looking for
  407.       // filenames and line numbers
  408.       ParseFile(tmpfile, arg);
  409.  
  410.       // Get rid of the temporary file
  411.       DeleteFile(tmpfile);
  412.  
  413.       // Move on to the next file
  414.       arg = NextArg(&ap);
  415.    }
  416.    return(0);
  417. }
  418.  
  419. // NAME:    ParseFile
  420. // PURPOSE: Read grep output and generate SCMSG entries for the lines found
  421. // NOTES:   grep output contains a filename followed by all the entries for
  422. //             that file.  The file's entries are each preceded by a line number.
  423.  
  424. int ParseFile(char *file, char *pattern)
  425. {
  426.    BPTR fh;
  427.    int curline;
  428.    int i, j;
  429.    static char curfile[BSIZ];
  430.  
  431.    if(!(fh=Open(file, MODE_OLDFILE)))
  432.       return(1);
  433.  
  434.    strcpy(curfile, pattern);
  435.    while(GetLine(fh, buf, sizeof(buf)))
  436.    {
  437.       // Skip leading blanks
  438.       for(i=0; buf[i] == ' '; i++);
  439.  
  440.       if(i == 0)
  441.       {
  442.          // No leading blanks indicates that this is a new filename
  443.          for(j=0; i<sizeof(buf) && buf[i] != '\n' && buf[i]; i++, j++)
  444.             curfile[j] = buf[i];
  445.          curfile[j-1] = 0;  // Chop off the trailing ':'
  446.       }
  447.       else
  448.       {
  449.          // Not a filename, so it's a line number and text
  450.  
  451.          i += stcd_i(buf+i, &curline) + 1;  // Read the line number, skip the ':'
  452.  
  453.          // Copy the line text
  454.          for(j=i; buf[j] && buf[j] != '\n'; j++)
  455.             if(buf[j] == '\t') buf[j] = ' ';
  456.  
  457.          buf[j] = 0;  // Null terminate it
  458.  
  459.          // Install the new occurrence in SCMSG
  460.          AddSCMSG("smfind", curfile, curline, buf+i);
  461.       }
  462.    }
  463.  
  464.    Close(fh);
  465.  
  466.    return(0);
  467. }
  468.  
  469. /* Equivalent of fgets() for AmigaDOS file handles */
  470. /* Works under 1.3 and 2.0 */
  471. int GetLine(BPTR fh, char *buf, int buflen)
  472. {
  473.    static char iobuf[256];
  474.    static int iocur, iomax;
  475.    int maxcopy;
  476.  
  477.    buflen--;
  478.    while(buflen)
  479.    {
  480.       if(iocur >= iomax)
  481.       {
  482.          iomax = Read(fh, iobuf, sizeof(iobuf));
  483.          if(iomax <= 0)
  484.             return(0);
  485.          iocur = 0;
  486.       }
  487.  
  488.       if(iomax-iocur > buflen)
  489.          maxcopy = iocur+buflen;
  490.       else
  491.          maxcopy = iomax;
  492.  
  493.       for(; iocur<maxcopy; iocur++, buflen--, buf++)
  494.       {
  495.          if((*buf = iobuf[iocur]) == '\n')
  496.          {
  497.             iocur++;
  498.             buf[1] = 0;
  499.             return(1);
  500.          }
  501.       }
  502.    }
  503.    *buf = 0;
  504.    return(1);
  505. }
  506.