home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Toolbox / Visual Basic Toolbox (P.I.E.)(1996).ISO / disk_utl / statemch / filesrch.frm (.txt) next >
Encoding:
Visual Basic Form  |  1995-04-26  |  25.6 KB  |  637 lines

  1. VERSION 2.00
  2. Begin Form frmSearch 
  3.    BackColor       =   &H00C0C0C0&
  4.    BorderStyle     =   1  'Fixed Single
  5.    Caption         =   "File Search"
  6.    ClientHeight    =   4116
  7.    ClientLeft      =   1092
  8.    ClientTop       =   1488
  9.    ClientWidth     =   8520
  10.    Height          =   4536
  11.    Icon            =   FILESRCH.FRX:0000
  12.    Left            =   1044
  13.    LinkTopic       =   "Form1"
  14.    MaxButton       =   0   'False
  15.    ScaleHeight     =   4116
  16.    ScaleWidth      =   8520
  17.    Top             =   1116
  18.    Width           =   8616
  19.    Begin SSPanel pnContents 
  20.       Alignment       =   0  'Left Justify - TOP
  21.       BevelInner      =   1  'Inset
  22.       Caption         =   "File Contents"
  23.       Height          =   4095
  24.       Left            =   4260
  25.       TabIndex        =   9
  26.       Top             =   0
  27.       Width           =   4215
  28.       Begin CommandButton cmdExit 
  29.          Caption         =   "E&xit"
  30.          Height          =   315
  31.          Left            =   2640
  32.          TabIndex        =   13
  33.          Top             =   3600
  34.          Width           =   1335
  35.       End
  36.       Begin TextBox enViewText 
  37.          FontBold        =   0   'False
  38.          FontItalic      =   0   'False
  39.          FontName        =   "MS Sans Serif"
  40.          FontSize        =   7.8
  41.          FontStrikethru  =   0   'False
  42.          FontUnderline   =   0   'False
  43.          Height          =   2775
  44.          Left            =   240
  45.          MultiLine       =   -1  'True
  46.          ScrollBars      =   3  'Both
  47.          TabIndex        =   11
  48.          Top             =   720
  49.          Width           =   3795
  50.       End
  51.       Begin CommandButton cmdEdit 
  52.          Caption         =   "&Edit"
  53.          Enabled         =   0   'False
  54.          Height          =   315
  55.          Left            =   240
  56.          TabIndex        =   12
  57.          Top             =   3600
  58.          Width           =   1335
  59.       End
  60.       Begin Label lbPathName 
  61.          BackColor       =   &H00FFFFFF&
  62.          BorderStyle     =   1  'Fixed Single
  63.          Caption         =   "c:/nick/class/statmach/srcfiles.exe"
  64.          FontBold        =   0   'False
  65.          FontItalic      =   0   'False
  66.          FontName        =   "MS Sans Serif"
  67.          FontSize        =   7.8
  68.          FontStrikethru  =   0   'False
  69.          FontUnderline   =   0   'False
  70.          Height          =   255
  71.          Left            =   240
  72.          TabIndex        =   10
  73.          Top             =   360
  74.          Width           =   3795
  75.       End
  76.    End
  77.    Begin SSPanel pnSearch 
  78.       Alignment       =   0  'Left Justify - TOP
  79.       BevelInner      =   1  'Inset
  80.       Caption         =   "Search Parameters"
  81.       Height          =   4095
  82.       Left            =   0
  83.       TabIndex        =   0
  84.       Top             =   0
  85.       Width           =   4215
  86.       Begin SSPanel pnCurdir 
  87.          Alignment       =   0  'Left Justify - TOP
  88.          BevelInner      =   1  'Inset
  89.          BorderWidth     =   1
  90.          Height          =   315
  91.          Left            =   120
  92.          TabIndex        =   8
  93.          Top             =   3660
  94.          Width           =   3975
  95.       End
  96.       Begin Timer tmrFSA 
  97.          Enabled         =   0   'False
  98.          Interval        =   1
  99.          Left            =   180
  100.          Top             =   3060
  101.       End
  102.       Begin CommandButton cmdStop 
  103.          Cancel          =   -1  'True
  104.          Caption         =   "&Stop"
  105.          Enabled         =   0   'False
  106.          Height          =   315
  107.          Left            =   2640
  108.          TabIndex        =   6
  109.          Top             =   1140
  110.          Width           =   1275
  111.       End
  112.       Begin CommandButton cmdBegin 
  113.          Caption         =   "&Begin"
  114.          Default         =   -1  'True
  115.          Height          =   315
  116.          Left            =   420
  117.          TabIndex        =   5
  118.          Top             =   1140
  119.          Width           =   1275
  120.       End
  121.       Begin TextBox enRootDir 
  122.          Height          =   315
  123.          Left            =   2040
  124.          TabIndex        =   4
  125.          Text            =   "enRootDir"
  126.          Top             =   720
  127.          Width           =   1875
  128.       End
  129.       Begin ListBox lsMatched 
  130.          Height          =   1944
  131.          Left            =   420
  132.          TabIndex        =   7
  133.          Top             =   1500
  134.          Width           =   3552
  135.       End
  136.       Begin TextBox enPattern 
  137.          Height          =   285
  138.          Left            =   2040
  139.          TabIndex        =   2
  140.          Text            =   "enPattern"
  141.          Top             =   360
  142.          Width           =   1875
  143.       End
  144.       Begin Label lbRootDir 
  145.          BackStyle       =   0  'Transparent
  146.          Caption         =   "Starting at:"
  147.          Height          =   195
  148.          Left            =   780
  149.          TabIndex        =   3
  150.          Top             =   720
  151.          Width           =   1140
  152.       End
  153.       Begin Label lbPattern 
  154.          BackStyle       =   0  'Transparent
  155.          Caption         =   "File Pattern:"
  156.          Height          =   195
  157.          Left            =   720
  158.          TabIndex        =   1
  159.          Top             =   420
  160.          Width           =   1350
  161.       End
  162.    End
  163. Option Explicit
  164. DefInt A-Z
  165. ' ----------------------------------------------------------------
  166. ' State Machine Example: File Search Utility
  167. ' Created by A. Nicklas Malik
  168. ' License:  You may use this utility, and any and all accompanying code
  169. '     in the creation of any software product, for resale or otherwise,
  170. '     as you see fit.  You may distribute this program and its accompanying
  171. '     source code on any media, under one condition: you may not charge any
  172. '     amount of money exceeding the duplication costs.  This code
  173. '     is free to be used on an AS IS basis.  Testing for program errors is
  174. '     your responsibility.  There is NO WARRANTY on this code WHATSOEVER.
  175. ' ----------------------------------------------------------------
  176. '  PURPOSE
  177. ' This program implements an event-driven Finite State Automaton.  The issue
  178. ' has been extensively explored in recent articles by Daniel Appleman.  The
  179. ' author of this code agrees with Mr. Appleman that state machines, as these
  180. ' programs are called, can be an extremely useful technique when attempting to
  181. ' handle long, involved calculations or manipulations in event-driven systems
  182. ' like Windows.
  183. ' The purpose of this program is to educate and enlighten.  If you get a useful
  184. ' utility in the bargain, then consider yourself lucky.
  185. ' ----------------------------------------------------------------
  186. '  FUNCTIONAL DESCRIPTION
  187. '  File Search utility: given a pattern to match against, this utility will scan
  188. '  the user's hard drive looking for files that match the pattern.  The matching
  189. '  will begin in the directory specified and will proceed to include all
  190. '  subdirectories under the specified directory.
  191. '  At any time during the search, the user can:
  192. '     1) restart the search with a new criteria
  193. '     2) exit the app
  194. '     3) select one of the files found so far
  195. '     4) abort the search without losing any information
  196. '  Note: if the user selects a file (action #3), the first page or so of text will
  197. '  be displayed in the text box (unless the file is binary).  The user can click the
  198. '  edit button to bring the current file into the Notepad editor.
  199. '  None of this functionality is earth-shattering.  The unique thing is that all
  200. '  of these actions can take place before the search is completed.
  201. ' ----------------------------------------------------------------
  202. '  INNER WORKINGS
  203. '  To date, there is no accompanying article to explain the workings of this
  204. '  program.  Unfortunately, this topic appears too arcane, and technical, for
  205. '  the average magazine reader.  Instead, I will attempt to explain, in the
  206. '  next few paragraphs, where you can look in this code for clues to its
  207. '  operation.  I hope that this information is enough to get you started in
  208. '  exploring this useful technique.
  209. '  Normally, when a VB program begins an long operation, it "freezes" up, refusing
  210. '  to even repaint it's windows.  On Windows 3.1 or WFWG 3.11, this actually can
  211. '  prevent other apps from running as well, since these systems require cooperation
  212. '  to do their multitasking, and VB apps have to reach the end of an event
  213. '  procedure (or a DoEvents call) before they are cooperating with Windows.
  214. '  Instead of using DoEvents, which is a common technique that begs the issue, this
  215. '  app will demonstrate a technique called "Finite State Automata", a.k.a State
  216. '  Machines.  An application designed as a state machine has the following
  217. '  advantages over a traditional "one-thing-at-a-time" app.  State machine apps can
  218. '     1) restart long calculations in the middle without using recovery code
  219. '     2) allow other Windows apps to operate uninterrupted
  220. '     3) can perform other tasks for the user while the calculations are being done
  221. '     4) can exit in the middle of an operation
  222. '     5) can provide a faster response to the user, increasing their productivity
  223. '     6) can better support DDE and OLE messaging schemes
  224. '  In general, an application that makes good use of state machine architecture
  225. '  can provide a cleaner, more appealing, and more productive interface to the
  226. '  user than traditional applications.
  227. '  The key to understanding this app is to recognize that only one portion of the
  228. '  program is involved in the state machine: the process of searching for matching
  229. '  file names.  The rest of the app: displaying the contents of the file or bringing
  230. '  up notepad to edit the file, is regular VB code.
  231. '  Therefore, only the single, long-duration process is involved in the state
  232. '  machine itself.
  233. '  In a state machine, the program stores "state" information in persistent
  234. '  memory.  In VB, this means using module-level or global variables. These values
  235. '  maintain information about "where we left off" so that, on each event, the
  236. '  state machine can "pick up" the work, do a SMALL amount, store a little state
  237. '  information, and exit.
  238. '  Obviously, if a state machine only does a small amount of work, and then exits,
  239. '  there needs to be some mechanism for restarting it.  An accepted technique in
  240. '  VB is to use a timer control for this purpose.  However, if you want a more
  241. '  "textbook" state machine, the event mechanism should be done by posting
  242. '  messages in the message queue, so that each time one round completes, it posts
  243. '  a message that will eventually trigger the next round.  Since "vanilla" VB has no
  244. '  way to collect these messages, timers are the next best thing.
  245. '  This program uses the timer 'tmrFSA' to trigger the state machine.  In fact, the
  246. '  bulk of the state machine code is located in the tmrFSA_Timer event.
  247. '  This routine works by placing the name of the starting directory in a "stack".
  248. '  The state machine will pop the directory from the stack.  It will scan every file
  249. '  in the directory.  If the file name matches the pattern provided, the name is
  250. '  added to the list box.  If the file name is the name of a subdirectory, it is
  251. '  pushed onto the stack.
  252. '  Every iteration of the state machine will work on a single directory.  The
  253. '  contents of the stack, and the state identifier, comprise the entire amount of
  254. '  "global" information needed by the state machine.
  255. '   Here is a call chain:
  256. '   tmrFSA_Timer         ' state machine triggers
  257. '      Dir() function    ' begins searching a directory
  258. '      search_for_files  ' continues the search
  259. '         examine_attributes    ' examines the current file's attributes
  260. '  Note that the stack in an odd creature.  It is represented as an array.
  261. '  Each element of the array is a list of directory names, seperated by a space.
  262. '  As a new directory is encountered, the examine_attributes routine will append
  263. '  the name of the directory to the current stack element's list of names.
  264. '  I make no claim that this technique is the most efficient way to handle
  265. '  variable-dimensioned arrays, but it works for the sake of this example...
  266. '  and that's an accomplishment!
  267. '  I hope that this introduction has been enough to get you started in
  268. '  understanding this code.  If you have questions, or problems with this
  269. '  code or any other VB issue, feel free to drop me a note at: 76055,2722
  270. '  on Compuserve.  (the internet address is '76055.2722@compuserve.com' )
  271. '  --- Nick Malik
  272. '  Lecturer, Author, Consultant, and all-around nice guy
  273. ' -----------------------------------------------------------------
  274. ' ---------------
  275. ' the following declaration is used to send a message to the text box
  276. ' to set it to be a read-only text box.  See the routine set_read_only()
  277. ' for an example.
  278. Declare Function SendMessage Lib "User" (ByVal hWnd As Integer, ByVal wMsg As Integer, ByVal wParam As Integer, lParam As Any) As Long
  279. ' ---------------
  280. ' our entire cadre of global variables!
  281. ' most of these vars are initialized in cmdBegin_Click()
  282. ' and used in tmrFSA and it's children.
  283. Dim curstate As Integer    ' current state  see ST_* below
  284. Dim pathstack(50) As String    ' stack of directories yet to search
  285. Dim stackpt As Integer         ' index of next free spot on the stack
  286. Dim pattern As String      ' pattern to match against
  287. Dim start_dir As String    ' where to come back to when we finish
  288. Dim nfound     As Long     ' number of matches found
  289. Dim dirscount As Long      ' number of directories found
  290. 'Current states in this state machine
  291. Const ST_IDLE = 0        ' do nothing
  292. Const ST_READ_DIR = 1    ' get a subdirectory from the current top of stack
  293. Const ST_SCAN = 2        ' process current Directory, up to 100 items
  294. ' The state machine in this system is very simple
  295. ' State 0 - idle - if we hit this, turn off the timer and do nothing
  296. ' State 1 - DIR - give a search argument to the DIR function to begin
  297. '                 searching for subdirectories and matching files.
  298. ' State 2 - DIR - no argument on DIR function, still in the process of
  299. '                 searching for files.
  300. ' File Attribute constants.
  301. ' These are DOS values, returned by the GetAttr function
  302. Const ATTR_NORMAL = 0       'Normal file
  303. Const ATTR_READONLY = 1     'Read-only file
  304. Const ATTR_HIDDEN = 2       'Hidden file
  305. Const ATTR_SYSTEM = 4       'System file
  306. Const ATTR_VOLUME = 8       'Volume label
  307. Const ATTR_DIRECTORY = 16   'MS-DOS directory
  308. Const ATTR_ARCHIVE = 32     'File has changed since last back-up
  309. Sub cmdBegin_Click ()
  310.     Dim fname$, pathdir$, patt$
  311.     tmrFSA.Enabled = False
  312.     cmdBegin.Enabled = False
  313.     start_dir = CurDir$  ' save this for later
  314.     fname$ = Trim$(enPattern.Text)
  315.     If Len(fname$) = 0 Then
  316.     pattern$ = "*"
  317.     pathdir$ = ""
  318.     Else
  319.     parse_filename fname$, patt$, pathdir$
  320.     If Len(patt$) = 0 Then
  321.         pattern$ = "*"
  322.     Else
  323.         pattern$ = UCase$(patt$)
  324.     End If
  325.     End If
  326.     If pathdir$ <> "" Then
  327.     If Len(Trim(enRootDir.Text)) > 0 Then
  328.         MsgBox "you cannot give a path in both the pattern and starting at boxes"
  329.         Exit Sub
  330.     End If
  331.     Else
  332.     pathdir$ = Trim$(enRootDir.Text)
  333.     If Len(pathdir$) = 0 Then
  334.         pathdir$ = Left$(CurDir$, 3)
  335.     End If
  336.     End If
  337.     ' DOS complains when you try to change to a directory and you use a trailing slash,
  338.     ' unless, of course, it is the root directory.
  339.     If Len(pathdir$) > 3 And Right$(pathdir$, 1) = "\" Then
  340.     pathdir$ = Left$(pathdir$, Len(pathdir) - 1)
  341.     End If
  342.     pathstack(0) = pathdir$
  343.     stackpt = 0
  344.     curstate = ST_READ_DIR
  345.     cmdStop.Enabled = True
  346.     tmrFSA.Enabled = True
  347.     nfound = 0
  348.     dirscount = 1
  349.     lsMatched.Clear
  350. End Sub
  351. Sub cmdEdit_Click ()
  352.     Dim rc%
  353.     rc% = Shell("Notepad " & lbPathname.Caption)
  354. End Sub
  355. Sub cmdExit_Click ()
  356.     Unload Me
  357.     End
  358. End Sub
  359. Sub cmdStop_Click ()
  360.     curstate = ST_IDLE
  361. End Sub
  362. '  given the name of a file, read the first 12000 bytes
  363. '  from the file and display it in the text box
  364. '  'enViewText'.  If the file is binary, display
  365. '  a message to that effect.
  366. Sub display_contents (filename As String)
  367.     Dim fnum%
  368.     Dim buffr$
  369.     Dim trunc_string$
  370.     Dim flen As Long
  371.     On Error Resume Next
  372.     fnum% = FreeFile
  373.     Open filename For Binary As fnum%
  374.     If Err <> 0 Then
  375.     enViewText.Text = "Error opening file: " & filename & "   error is: " & Error$
  376.     Exit Sub
  377.     End If
  378.     On Error GoTo dc_error
  379.     enViewText.Text = ""
  380.     flen = LOF(fnum%)   ' get the length of the file, in bytes
  381.     If flen > 12000 Then
  382.     flen = 12000
  383.     trunc_string$ = Chr$(13) & Chr$(10) & " <<<<<< FILE DISPLAY TRUNCATED >>>>>>"
  384.     Else
  385.     trunc_string$ = ""
  386.     End If
  387.     buffr$ = Space$(flen)
  388.     Get fnum%, , buffr$     ' get first 12000 characters of the file
  389.     If isbinary(buffr$) Then
  390.     enViewText.Text = "File is not in ASCII format, cannot be displayed"
  391.     cmdEdit.Enabled = False
  392.     Else
  393.     enViewText.Text = buffr$ & trunc_string$
  394.     cmdEdit.Enabled = True
  395.     End If
  396.     lbPathname.Caption = filename
  397. dc_at_end:
  398.     Close fnum%
  399.     Exit Sub
  400. dc_error:
  401.     enViewText.Text = enViewText.Text & " <<  Error " & Error$ & " >> during read of file " & filename
  402.     Resume dc_at_end
  403. End Sub
  404. '  This routine is called for each filename found.  The attributes of the
  405. '  file are inspected to determine if the name is a file or a directory.
  406. '  If it is a directory, it is added to the stack.  If it is a file, it
  407. '  is compared against the pattern.  Matching files are added to the list box.
  408. '  This routine is called by search_for_files()
  409. Sub examine_attributes (filename$)
  410.     Dim filepart$, pathpart$
  411.     Dim attrib%
  412.     attrib% = GetAttr(filename$)
  413.     If (attrib% And ATTR_DIRECTORY) > 0 Then   ' got a directory
  414.     If filename$ <> "." And filename$ <> ".." Then
  415.         pathstack(stackpt) = pathstack(stackpt) & " " & filename$
  416.         dirscount = dirscount + 1
  417.     End If
  418.     Else
  419.     'parse_filename filename$, filepart$, pathpart$
  420.     If filename$ Like pattern Then ' compare with global pattern
  421.         lsMatched.AddItem list_name(filename$)
  422.         nfound = nfound + 1
  423.     End If
  424.     End If
  425. End Sub
  426. '  start up with some nice defaults.
  427. Sub Form_Load ()
  428.     On Error Resume Next
  429.     set_read_only enViewText
  430.     enRootDir.Text = CurDir$
  431.     enPattern.Text = "*.txt"
  432.     lbPathname = ""
  433. End Sub
  434. '  find out if the file is binary...
  435. '  read the first 1000 bytes of the buffer looking for binary characters
  436. '  if you find too many, then the file is binary, so quit.
  437. Function isbinary (buffer As String) As Integer
  438.     Dim charix%, binct%, limit%
  439.     Dim cval%
  440.     On Error Resume Next
  441.     limit% = Len(buffer)
  442.     If limit% > 1024 Then limit% = 1024   ' only check the first 1K
  443.     binct% = 0
  444.     ' begin looking for binary characters
  445.     For charix% = 1 To limit%
  446.     cval% = Asc(Mid$(buffer, charix%, 1))
  447.     ' don't complain about tabs and carriage returns, etc
  448.     If (cval% < 8) Or ((cval% > 13) And (cval% < 32)) Then
  449.         binct% = binct% + 1
  450.         If binct% > 25 Then Exit For
  451.     End If
  452.     Next charix%
  453.     ' if too many, or if more than 1/3 of all chars are binary, then it is!
  454.     isbinary = (binct% > (limit% / 3)) Or (binct% > 25)
  455. End Function
  456. '  create a full path name from the parameter, which is assumed to be the
  457. '  name of a file in the current directory.
  458. Function list_name (fname$) As String
  459.     Dim cdir$
  460.     If Mid$(fname$, 2, 1) = ":" Then
  461.     list_name = fname$
  462.     Else
  463.     cdir$ = CurDir$
  464.     If Right$(cdir$, 1) <> "\" Then cdir$ = cdir$ & "\"
  465.     list_name = LCase$(cdir$ & fname$)
  466.     End If
  467. End Function
  468. Sub lsMatched_Click ()
  469.     On Error Resume Next
  470.     Dim fname$
  471.     fname$ = lsMatched.List(lsMatched.ListIndex)
  472.     display_contents fname$
  473. End Sub
  474. '  when passed a DOS filename in the first parameter, this routine
  475. '  will return values in the second and third parameters.  The
  476. '  second parameter will contain the name of the file itself, and
  477. '  the third param will contain the directory tree it is under.
  478. '   Note: the only time that 'pathonly$' does not end in a trailing
  479. '  backslash is when the 'longname$' parameter did not contain one.
  480. Sub parse_filename (longname$, filename$, pathonly$)
  481.     Dim slashloc%
  482.     Dim prevslash%
  483.     prevslash% = InStr(longname$, "\")
  484.     slashloc% = InStr(prevslash% + 1, longname$, "\")
  485.     Do While slashloc% > 0
  486.     prevslash% = slashloc%
  487.     slashloc% = InStr(prevslash% + 1, longname$, "\")
  488.     Loop
  489.     filename$ = Mid$(longname$, prevslash% + 1)
  490.     pathonly$ = Left$(longname$, prevslash%)
  491. End Sub
  492. '  given a list of directory names, seperated by spaces,
  493. '  pull the first one from the list and trim the list.
  494. Function pop_directory (dirnlist$) As String
  495.     Dim newlist$        ' working value for the list param
  496.     Dim firstdir$       ' value pulled from list
  497.     Dim spacloc%        ' location of the first blank
  498.     On Error Resume Next
  499.     newlist$ = Trim$(dirnlist$)
  500.     If Len(newlist$) = 0 Then
  501.     dirnlist$ = ""
  502.     pop_directory = ""
  503.     Exit Function
  504.     End If
  505.     spacloc% = InStr(newlist$, " ")
  506.     If spacloc% = 0 Then    ' no blanks found, list must contain only one item
  507.     dirnlist$ = ""
  508.     pop_directory = newlist$
  509.     Else
  510.     dirnlist$ = Trim$(Mid$(newlist$, spacloc% + 1))
  511.     pop_directory = Left$(newlist$, spacloc% - 1)
  512.     End If
  513. End Function
  514. '  Note: before this routine was called, the DIR() function must have
  515. '  been called with a parameter.  This is needed to initialize this routine.
  516. '  This routine will search for the next 100 files, or will stop at the end
  517. '  of the directory, whichever comes first.
  518. '  If it hits the end of subdir, this routine will return True, else it will
  519. '     return False.
  520. '  No state machine manipulations will happen in here... this is a work-horse
  521. '     routine.
  522. Function search_for_files () As Integer
  523.     Dim fcount%
  524.     Dim fname$
  525.     Dim fattr%
  526.     On Error Resume Next   ' ignore all errors
  527.     For fcount% = 1 To 100
  528.     fname$ = Dir
  529.     If Len(fname$) = 0 Then
  530.         search_for_files = True
  531.         Exit Function
  532.     End If
  533.     ' the next routine will decide if the file is a directory or a matching text file
  534.     examine_attributes fname$
  535.     Next fcount%
  536.     search_for_files = False
  537. End Function
  538. '  Flags a text box as read only, allowing the user to use the scroll bars,
  539. '  and to copy to the clipboard, but not to edit the contents.
  540. Sub set_read_only (txctrl As Control)
  541.     Dim dis As Long
  542.     Const EM_SETREADONLY = &H400 + 31
  543.     dis = SendMessage(txctrl.hWnd, EM_SETREADONLY, 1, ByVal 0&)
  544. End Sub
  545. '  the entire state machine lives in here.
  546. '  it is initialized in cmdBegin_Click()
  547. '  this routine is called repeatedly, as long as the timer
  548. '  is enabled.  As this routine comes in, it will check the
  549. '  value of the state variable to determine which state to
  550. '  process.
  551. '     In each state, the search_for_files routine will look
  552. '  through 100 files in a directory, or until the directory
  553. '  runs out, whichever comes first.
  554. '  The stack consists of an array of strings, where each
  555. '  string represents the list of subdirectories at that level
  556. '  that have yet to be searched.
  557. '  (this is a generic DOS Directory search algorithm, only
  558. '  using a stack array instead of recursion).
  559. '  I suppose the stack could be more efficient, but I wasn't
  560. '  going for string efficiency... I wanted to demonstrate a
  561. '  pratical use for State Machines.
  562. '  --- Nick Malik
  563. Sub tmrFSA_Timer ()
  564.     On Error Resume Next
  565.     Dim srcdir$, fname$
  566.     Select Case curstate
  567.     Case ST_IDLE
  568.     tmrFSA.Enabled = False
  569.     cmdBegin.Enabled = True
  570.     cmdStop.Enabled = False
  571.     stackpt = 0
  572.     ChDir start_dir
  573.     pnCurDir.Caption = "Complete!  " & Format$(nfound) & " matches found in " & Format$(dirscount) & " dirs"
  574.     Beep: Beep
  575.     ' when we enter here, there will be a list of directory names (possibly
  576.     ' including the drive letter) on the current position of the stack.
  577.     ' pull one directory name from the stack, increment the stack pointer, and
  578.     ' start looking
  579.     Case ST_READ_DIR
  580.     srcdir$ = pop_directory(pathstack(stackpt))
  581.     If srcdir$ = "" Then   ' no elements, back up
  582.         If stackpt <= 0 Then
  583.         curstate = ST_IDLE ' we are done!
  584.         Exit Sub
  585.         End If
  586.         stackpt = stackpt - 1
  587.         ChDir ".."
  588.         Exit Sub
  589.     End If
  590.     ChDir list_name(srcdir$)
  591.     If Err <> 0 Then
  592.         MsgBox "error : " & Error$
  593.     End If
  594.     pnCurDir.Caption = trimmed_dir(CurDir$)
  595.     stackpt = stackpt + 1
  596.     pathstack(stackpt) = ""
  597.     fname$ = Dir("*.*", ATTR_DIRECTORY)
  598.     examine_attributes fname$
  599.     If search_for_files() Then
  600.         ' to get here means we have exhausted the files in the current directory
  601.         ' if the next stack position has any text in it, we should progress
  602.         If Len(pathstack(stackpt)) > 0 Then Exit Sub
  603.         ' to be here means we found no subdirectories... back up and try again
  604.         ChDir ".."
  605.         stackpt = stackpt - 1
  606.     Else
  607.         curstate = ST_SCAN
  608.     End If
  609.     ' when we enter here, we are in the middle of processing the list of files
  610.     Case ST_SCAN
  611.     ' the IF stmt below is functionally identical to the one above
  612.     If search_for_files() Then
  613.         curstate = ST_READ_DIR
  614.         If Len(pathstack(stackpt)) > 0 Then
  615.         Exit Sub
  616.         End If
  617.         ChDir ".."
  618.         stackpt = stackpt - 1
  619.     End If
  620.     End Select
  621. End Sub
  622. '  given a possibly long path in 'indir', return a path that can be displayed in a caption
  623. ' of a 3-d panel
  624. Function trimmed_dir (indir$) As String
  625.     Dim predir$
  626.     Dim temp$
  627.     Dim slashloc%
  628.     If Len(indir$) < 25 Then
  629.     trimmed_dir = indir$
  630.     Else
  631.     predir$ = Left$(indir$, 3) ' get drive letter
  632.     temp$ = Right$(indir$, 18)
  633.     slashloc% = InStr(temp$, "\")
  634.     trimmed_dir$ = predir$ & "..." & Mid$(temp$, slashloc%)
  635.     End If
  636. End Function
  637.