Annotation of mstools/samples/pdc/pdc.c, revision 1.1.1.1

1.1       root        1: /*++
                      2: 
                      3: Copyright (c) 1992 Microsoft Corporation
                      4: 
                      5: Module Name:
                      6: 
                      7:     pdc.c
                      8: 
                      9: Abstract:
                     10: 
                     11:     This is the main source file for the Windows/NT PDC API demonstration
                     12:     program.  This program demonstrates how to use many of the advanced
                     13:     operating system features provided by the Win32 API set on Windows/NT.
                     14: 
                     15:     This file and its corresponding header file, pdc.h, can be found
                     16:     in the sample programs directory of the PDC CD-ROM.
                     17: 
                     18:     This program has a real purpose, although the implementation is
                     19:     somewhat contrived in order to demonstrate the various operating
                     20:     system features of Windows/NT.
                     21: 
                     22:     The features that this program demonstrate are:
                     23: 
                     24:         - Creating multiple threads, using critical sections
                     25:           and semaphores for synchronization.
                     26: 
                     27:         - Thread termination.
                     28: 
                     29:         - Virtual memory, commitment vs reservation.
                     30: 
                     31:         - Structured exception handling, including finally
                     32:           clauses and an exception filter procedure.
                     33: 
                     34:         - Enumeration of directory entries.
                     35: 
                     36:         - Mapped file I/O
                     37: 
                     38:         - Asynchronous file I/O via completion routine.
                     39: 
                     40:         - Synchronous file I/O
                     41: 
                     42:     PDC is a character mode program for searching the files in a
                     43:     directory tree for a match against a pattern.  It uses multiple
                     44:     threads to do it's work, with each thread processing a file at a
                     45:     time, accumulating it's matches and outputting them to standard
                     46:     output contiguously when it is done searching a file.
                     47: 
                     48:     The command line syntax is:
                     49: 
                     50:         Usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]
                     51: 
                     52:         where:
                     53: 
                     54:             -h - prints this message.
                     55: 
                     56:             -v - generates verbose output.
                     57: 
                     58:             -y - ignores case when doing comparisons.
                     59: 
                     60:             -a - specifies that the program should use asynchronous file
                     61:                  I/O to read the files being searched.
                     62: 
                     63:             -s - specifies that the program should use synchronous file
                     64:                  I/O to read the files being searched.
                     65: 
                     66:             -m - specifies that the program should use mapped file I/O
                     67:                  to read the files being searched.
                     68: 
                     69:             -t - specifies the number of threads to use when doing the
                     70:                  search.  Default is 4 * the number of processors.
                     71: 
                     72:             SearchString - specifies the text to search for.  Enclose in
                     73:                            quotes if it contains spaces or punctuation.
                     74: 
                     75:             DirectoryPath - specifies the root of the tree to begin the
                     76:                             search at.  Defaults to the current directory.
                     77: 
                     78: 
                     79: --*/
                     80: 
                     81: #include "pdc.h"
                     82: 
                     83: 
                     84: int
                     85: main(
                     86:     int argc,
                     87:     char *argv[]
                     88:     )
                     89: 
                     90: /*++
                     91: 
                     92: Routine Description:
                     93: 
                     94:     This is the main procedure for the PDC program, and is called by
                     95:     the C Runtime startup code when the program starts.
                     96: 
                     97: Arguments:
                     98: 
                     99:     argc - number of argumments in the argv array.
                    100: 
                    101:     argv - pointer to an array of null terminated string pointers.
                    102: 
                    103: Return Value:
                    104: 
                    105:     Process exit status.  The value returned by this function will
                    106:     be used as the exit code parameter passed to ExitProcess.
                    107: 
                    108: --*/
                    109: 
                    110: {
                    111:     SYSTEM_INFO SystemInformation;
                    112: 
                    113:     //
                    114:     // Query the number of processors from the system and
                    115:     // default the number of worker threads to 4 times that.
                    116:     //
                    117: 
                    118:     GetSystemInfo( &SystemInformation );
                    119:     NumberOfWorkerThreads = SystemInformation.dwNumberOfProcessors * 4;
                    120: 
                    121:     //
                    122:     // Process the arguments given on the command line.
                    123:     //
                    124: 
                    125:     if (!ProcessCommandLineArguments( argc, argv )) {
                    126:         exit( 1 );
                    127:         }
                    128: 
                    129:     //
                    130:     // Allocate a thread local storage slot for use by our worker
                    131:     // thread routine (ProcessRequest).  This call reserves a
                    132:     // 32-bit slot in the thread local storage array for every
                    133:     // thread in this process.  Remember the slot index in a global
                    134:     // variable for use by our worker thread routine.
                    135:     //
                    136: 
                    137:     TlsIndex = TlsAlloc();
                    138:     if (TlsIndex == 0xFFFFFFFF) {
                    139:         fprintf( stderr, "PDC: Unable to allocated thread local storage.\n" );
                    140:         exit( 1 );
                    141:         }
                    142: 
                    143: 
                    144:     //
                    145:     // Create a work queue, which will create the specified number of threads
                    146:     // to process.
                    147:     //
                    148: 
                    149:     WorkQueue = CreateWorkQueue( NumberOfWorkerThreads, ProcessRequest );
                    150:     if (WorkQueue == NULL) {
                    151:         fprintf( stderr, "PDC: Unable to create %u worker threads.\n", NumberOfWorkerThreads );
                    152:         exit( 1 );
                    153:         }
                    154: 
                    155:     //
                    156:     // If using asynchronous I/O, create an event that will be signalled
                    157:     // when there are no more outstanding I/O requests.  The event is
                    158:     // a manual reset event, that once signalled via SetEvent, will
                    159:     // remain signalled until ResetEvent is called.
                    160:     //
                    161: 
                    162:     if (ASyncIO) {
                    163:         IoCompletedEvent = CreateEvent( NULL,   // Not inherited
                    164:                                         TRUE,   // Manual reset
                    165:                                         FALSE,  // Initially reset
                    166:                                         NULL    // No name
                    167:                                       );
                    168:         }
                    169: 
                    170:     //
                    171:     // Now walk the directory tree, which will call our procedure
                    172:     // (QueueSearchFile) for each directory and file in the tree.
                    173:     //
                    174: 
                    175:     EnumerateDirectoryTree( DirectoryPath,
                    176:                             QueueSearchFile,
                    177:                             NULL
                    178:                           );
                    179: 
                    180:     //
                    181:     // Done walking the tree.  If using asynchronous I/O, wait for all of
                    182:     // the outstanding I/O requests to be completed.
                    183:     //
                    184: 
                    185:     if (ASyncIO) {
                    186:         //
                    187:         // We use an alertable wait in a loop, as I/O completion
                    188:         // will terminate the wait, even through the event we
                    189:         // are waiting on is not signalled.
                    190:         //
                    191: 
                    192:         while (WaitForSingleObjectEx( IoCompletedEvent,
                    193:                                       0xFFFFFFFF,
                    194:                                       TRUE
                    195:                                     ) == WAIT_IO_COMPLETION
                    196:               ) {
                    197:             ;
                    198:             }
                    199:         }
                    200: 
                    201:     //
                    202:     // All done, destroy the work queue.  This will wait for the work queues
                    203:     // to empty before terminating the worker threads and destroying the
                    204:     // queue.
                    205:     //
                    206: 
                    207:     DestroyWorkQueue( WorkQueue );
                    208: 
                    209:     if (Verbose && MatchedLineCount) {
                    210:         fprintf( stderr,
                    211:                  "Found %u lines with matches in %u files, out of %u files searched.\n",
                    212:                  MatchedLineCount,
                    213:                  MatchedFileCount,
                    214:                  SearchedFileCount
                    215:                );
                    216:         }
                    217: 
                    218:     return 0;
                    219: }
                    220: 
                    221: 
                    222: 
                    223: VOID
                    224: QueueSearchFile(
                    225:     LPSTR Path,
                    226:     PWIN32_FIND_DATA FindFileData,
                    227:     PVOID EnumerateParameter
                    228:     )
                    229: 
                    230: /*++
                    231: 
                    232: Routine Description:
                    233: 
                    234:     This is the directory enumeration function.  It is called by the
                    235:     EnumerateDirectoryTree function once for each file and directory
                    236:     in the tree.
                    237: 
                    238:     This function, if it decides it wants to search the file, will
                    239:     open the file and then, depending upon the I/O method selected via
                    240:     the command line, will:
                    241: 
                    242:         - map the file PAGE_READONLY for mapped file I/O
                    243: 
                    244:         - read the file into an allocated buffer for synchronous
                    245:           I/O.
                    246: 
                    247:         - will allocate the buffer and start a read operation
                    248:           to read the entire file into the buffer.  A completion
                    249:           routine will be invoked when the read completes, possibly
                    250:           in another thread context.
                    251: 
                    252:     Finally it will queue a search request to the work queue, with the
                    253:     relevant information contained in the request.  For asynchronous
                    254:     I/O, the search request is allocated and initialized here but is
                    255:     not actually queued to the work queue until the I/O completion
                    256:     routine has been called.
                    257: 
                    258: Arguments:
                    259: 
                    260:     Path - Supplies a pointer to a null terminated string that contains
                    261:         the fully qualified path of the file or directory.
                    262: 
                    263:     FindFileData - Supplies the directory information associated
                    264:         with the file or directory specified by the Path argument.
                    265: 
                    266:     EnumerateParameter - Uninterpreted 32-bit value.  Not used.
                    267: 
                    268: Return Value:
                    269: 
                    270:     None.
                    271: 
                    272: --*/
                    273: 
                    274: {
                    275:     PSEARCH_REQUEST SearchRequest;
                    276:     HANDLE File;
                    277:     HANDLE Mapping;
                    278:     LPVOID FileData;
                    279:     DWORD FileSize;
                    280: 
                    281:     //
                    282:     // Ignore directories or zero length files, as there
                    283:     // is nothing to search in these cases.
                    284:     //
                    285: 
                    286:     if (FindFileData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ||
                    287:         !(FileSize = FindFileData->nFileSizeLow)
                    288:        ) {
                    289:         return;
                    290:         }
                    291: 
                    292:     //
                    293:     // Open the file using the fully qualified path.  Specify the
                    294:     // sequential scan hint to the cache manager and if asynchronous
                    295:     // I/O will be used, specified the overlapped flag as well.
                    296:     //
                    297: 
                    298:     File = CreateFile( Path,
                    299:                        GENERIC_READ,
                    300:                        FILE_SHARE_READ,
                    301:                        NULL,
                    302:                        OPEN_EXISTING,
                    303:                        FILE_FLAG_SEQUENTIAL_SCAN |
                    304:                          (ASyncIO ? FILE_FLAG_OVERLAPPED : 0),
                    305:                        NULL
                    306:                      );
                    307: 
                    308:     //
                    309:     // Since NULL might be a valid file object handle, failure
                    310:     // is indicated by a special return value.
                    311:     //
                    312: 
                    313:     if (File == INVALID_HANDLE_VALUE) {
                    314:         fprintf( stderr, "%s(0) : error %u: Unable to open file.\n",
                    315:                  Path,
                    316:                  GetLastError()
                    317:                );
                    318: 
                    319:         return;
                    320:         }
                    321: 
                    322:     //
                    323:     // File successfully opened for read access.
                    324:     //
                    325:     if (MappedFileIO) {
                    326:         //
                    327:         // If mapped file I/O, create a file mapping object, backed by
                    328:         // the file we just opened.  Make the default page protection
                    329:         // for the mapping be readonly.
                    330:         //
                    331: 
                    332:         Mapping = CreateFileMapping( File,
                    333:                                      NULL,
                    334:                                      PAGE_READONLY,
                    335:                                      0,
                    336:                                      0,
                    337:                                      NULL
                    338:                                    );
                    339: 
                    340:         //
                    341:         // Okay to close the file handle now, since the mapping object
                    342:         // has a reference to the file that will cause the open file
                    343:         // object to remain until the reference is destroyed.  Note that
                    344:         // any sharing information is lost at this point, so somebody
                    345:         // could come in an open it for write access.
                    346:         //
                    347: 
                    348:         CloseHandle( File );
                    349: 
                    350:         //
                    351:         // Here, a null value indicates an error.
                    352:         //
                    353: 
                    354:         if (Mapping == NULL) {
                    355:             fprintf( stderr, "%s(0) : error %u: Unable to create mapping object.\n",
                    356:                      Path,
                    357:                      GetLastError()
                    358:                    );
                    359:             return;
                    360:             }
                    361: 
                    362:         //
                    363:         // Finally, map a view of the file, using the mapping object
                    364:         // just created, into the address space of this process.
                    365:         // The offset and size are zero, which means to map the
                    366:         // entire file.
                    367:         //
                    368: 
                    369:         FileData = MapViewOfFile( Mapping,
                    370:                                   FILE_MAP_READ,
                    371:                                   0,
                    372:                                   0,
                    373:                                   0
                    374:                                 );
                    375: 
                    376:         //
                    377:         // Okay to close the mapping object handle now, it will remained
                    378:         // referenced as long as the view remains mapped.
                    379:         //
                    380: 
                    381:         CloseHandle( Mapping );
                    382: 
                    383:         //
                    384:         // A null value indicates the map operation failed.
                    385:         //
                    386: 
                    387:         if (FileData == NULL) {
                    388:             fprintf( stderr, "%s(0) : error %u: Unable to map file.\n",
                    389:                      Path,
                    390:                      GetLastError()
                    391:                    );
                    392:             return;
                    393:             }
                    394: 
                    395:         //
                    396:         // All done mapping the file.  Set the File handle to NULL as
                    397:         // it has been closed already.  Both the file object and mapping
                    398:         // objects created above will be freed when the map view is
                    399:         // unmapped.
                    400:         //
                    401: 
                    402:         File = NULL;
                    403:         }
                    404:     else {
                    405:         //
                    406:         // Not using mapped I/O, so allocate a buffer big enough to
                    407:         // contain the entire file.
                    408:         //
                    409: 
                    410:         FileData = VirtualAlloc( NULL,
                    411:                                  FileSize,
                    412:                                  MEM_COMMIT,
                    413:                                  PAGE_READWRITE
                    414:                                );
                    415:         if (FileData == NULL) {
                    416:             fprintf( stderr, "%s(0) : error %u: Unable to allocate memory to contain file.\n",
                    417:                      Path,
                    418:                      GetLastError()
                    419:                    );
                    420:             CloseHandle( File );
                    421:             return;
                    422:             }
                    423:         }
                    424: 
                    425:     //
                    426:     // We have successfully opened the file and are ready to go.
                    427:     // Allocate space for a search request and fill it in
                    428:     // with the information needed by the ProcessSearchFile
                    429:     // function when it runs.
                    430:     //
                    431: 
                    432:     SearchRequest = LocalAlloc( LMEM_ZEROINIT,
                    433:                                 sizeof( *SearchRequest ) +
                    434:                                     strlen( Path ) + 1
                    435:                               );
                    436:     if (SearchRequest == NULL) {
                    437:         fprintf( stderr, "PDC: Out of memory\n" );
                    438:         exit( 1 );
                    439:         }
                    440: 
                    441:     SearchRequest->WorkItem.Reason = WORK_ITEM;
                    442:     SearchRequest->File = File;
                    443:     SearchRequest->FileSize = FileSize;
                    444:     SearchRequest->FileData = FileData;
                    445:     strcpy( SearchRequest->FullPathName, Path );
                    446: 
                    447:     if (!ASyncIO) {
                    448:         //
                    449:         // If not using asynchronous I/O, then queue the search
                    450:         // request to the work queue.
                    451:         //
                    452: 
                    453:         QueueWorkItem( WorkQueue, &SearchRequest->WorkItem );
                    454:         }
                    455:     else {
                    456:         //
                    457:         // Using asynchronous I/O, so queue the read operation.
                    458:         // The file handle must remain open while the read operation
                    459:         // is pending.
                    460:         //
                    461: 
                    462:         if (!ReadFileEx( File,
                    463:                          FileData,
                    464:                          FileSize,
                    465:                          &SearchRequest->OverlappedIO,
                    466:                          ProcessReadFileCompletion
                    467:                        )
                    468:            ) {
                    469:             fprintf( stderr, "%s(0) : error %u: Unable to queue read of file.\n",
                    470:                      Path,
                    471:                      GetLastError()
                    472:                    );
                    473: 
                    474:             VirtualFree( FileData, 0, MEM_RELEASE );
                    475:             CloseHandle( File );
                    476:             LocalFree( SearchRequest );
                    477:             return;
                    478:             }
                    479: 
                    480: 
                    481:         //
                    482:         // Successfully queued the read operation.  Keep a count
                    483:         // of outstanding read operations so we know when it is
                    484:         // okay to terminate.
                    485:         //
                    486: 
                    487:         OutstandingIOOperations += 1;
                    488:         }
                    489: 
                    490:     //
                    491:     // Return back to the EnumerateDirectoryTree function so that it
                    492:     // can call us with the next file or directrry.
                    493:     //
                    494: 
                    495:     return;
                    496: }
                    497: 
                    498: 
                    499: 
                    500: VOID
                    501: ProcessRequest(
                    502:     IN PWORK_QUEUE_ITEM WorkItem
                    503:     )
                    504: 
                    505: /*++
                    506: 
                    507: Routine Description:
                    508: 
                    509:     This function is called whenever a work item is removed from
                    510:     the work queue by one of the worker threads.  Which worker
                    511:     thread context this function is called in is arbitrary.
                    512: 
                    513:     This functions keeps a pointer to state information in
                    514:     thread local storage.
                    515: 
                    516:     This function is called once at the beginning with a
                    517:     special initialization call.  During this call, this
                    518:     function allocates space for state information and
                    519:     remembers the pointer to the state information in
                    520:     a Thread Local Storage (TLS) slot.
                    521: 
                    522:     This function is called once at the end with a special
                    523:     termination call.  During this call, this function
                    524:     frees the state information allocated during the
                    525:     initialization call.
                    526: 
                    527:     In between these two calls are zero or more calls to
                    528:     handle a work item.  The work item is a search request
                    529:     which is handled by the ProcessSearchFile function.
                    530: 
                    531: Arguments:
                    532: 
                    533:     WorkItem - Supplies a pointer to the work item just removed
                    534:         from the work queue.  It is the responsibility of this
                    535:         routine to free the memory used to hold the work item.
                    536: 
                    537: Return Value:
                    538: 
                    539:     None.
                    540: 
                    541: --*/
                    542: 
                    543: {
                    544:     DWORD BytesWritten;
                    545:     PSEARCH_REQUEST_STATE State;
                    546:     PSEARCH_REQUEST SearchRequest;
                    547: 
                    548:     if (WorkItem->Reason == WORK_INITIALIZE_ITEM) {
                    549:         //
                    550:         // First time initialization call.  Allocate space for
                    551:         // state information.
                    552:         //
                    553: 
                    554:         State = LocalAlloc( LMEM_ZEROINIT,
                    555:                             sizeof( *State )
                    556:                           );
                    557: 
                    558:         if (State != NULL) {
                    559:             //
                    560:             // Now create a virtual buffer, with an initial commitment
                    561:             // of zero and a maximum commitment of 128KB.  This buffer
                    562:             // will be used to accumulate the matched strings output
                    563:             // during the search of a single file.  This is so the
                    564:             // output can be written to standard output with a single
                    565:             // write call, thus insuring that it remains contiguous
                    566:             // in the output stream, and is not intermingled with the
                    567:             // output of the other worker threads.
                    568:             //
                    569: 
                    570:             if (CreateVirtualBuffer( &State->Buffer, 0, 2 * 64 * 1024 )) {
                    571:                 //
                    572:                 // The CurrentOutput field of the state block is
                    573:                 // a pointer to where the next output goes in the
                    574:                 // buffer.  It is initialized here and reset each
                    575:                 // time the buffer is flushed to standard output.
                    576:                 //
                    577: 
                    578:                 State->CurrentOutput = State->Buffer.Base;
                    579:                 }
                    580:             else {
                    581:                 LocalFree( State );
                    582:                 State = NULL;
                    583:                 }
                    584:             }
                    585: 
                    586:         //
                    587:         // Remember the pointer to the state informaiton
                    588:         // thread local storage.
                    589:         //
                    590: 
                    591:         TlsSetValue( TlsIndex, State );
                    592:         return;
                    593:         }
                    594: 
                    595:     //
                    596:     // Here to handle a work item or special terminate call.
                    597:     // Get the state pointer from thread local storage.
                    598:     //
                    599: 
                    600:     State = (PSEARCH_REQUEST_STATE)TlsGetValue( TlsIndex );
                    601:     if (State == NULL) {
                    602:         return;
                    603:         }
                    604: 
                    605:     //
                    606:     // If this is the special terminate work item, free the virtual
                    607:     // buffer and state block allocated above and set the thread
                    608:     // local storage value to NULL.  Return to caller.
                    609:     //
                    610: 
                    611:     if (WorkItem->Reason == WORK_TERMINATE_ITEM) {
                    612:         FreeVirtualBuffer( &State->Buffer );
                    613:         LocalFree( State );
                    614:         TlsSetValue( TlsIndex, NULL );
                    615:         return;
                    616:         }
                    617: 
                    618:     //
                    619:     // If not an initialize or terminate work item, then must be a
                    620:     // search request.  Calculate the address of the search request
                    621:     // block, based on the position of the WorkItem field in the
                    622:     // SEARCH_REQUEST structure.
                    623:     //
                    624: 
                    625:     SearchRequest = CONTAINING_RECORD( WorkItem, SEARCH_REQUEST, WorkItem );
                    626: 
                    627:     //
                    628:     // Actual search operation is protected by a try ... except
                    629:     // block so that any attempts to store into the virtual buffer
                    630:     // will be handled correctly by extending the virtual buffer.
                    631:     //
                    632: 
                    633:     try {
                    634:         //
                    635:         // Perform the search against this file.
                    636:         //
                    637: 
                    638:         ProcessSearchFile( SearchRequest, State );
                    639: 
                    640:         //
                    641:         // Done with this file.  If using asynchronous I/O, decrement the
                    642:         // count of outstanding I/O operations and it if goes to zero,
                    643:         // then signal the IoCompletedEvent as there are no more outstanding
                    644:         // I/O operations.
                    645:         //
                    646: 
                    647:         if (ASyncIO && InterlockedDecrement( &OutstandingIOOperations ) == 0) {
                    648:             SetEvent( IoCompletedEvent );
                    649:             }
                    650: 
                    651:         //
                    652:         // Free the storage used by the SearchRequest
                    653:         //
                    654: 
                    655:         LocalFree( SearchRequest );
                    656: 
                    657:         //
                    658:         // If any output was written to the virtual buffer,
                    659:         // flush the output to standard output.  Trim the
                    660:         // virtual buffer back to zero committed pages.
                    661:         //
                    662: 
                    663:         if (State->CurrentOutput > (LPSTR)State->Buffer.Base) {
                    664:             WriteFile( GetStdHandle( STD_OUTPUT_HANDLE ),
                    665:                        State->Buffer.Base,
                    666:                        State->CurrentOutput - (LPSTR)State->Buffer.Base,
                    667:                        &BytesWritten,
                    668:                        NULL
                    669:                      );
                    670: 
                    671:             TrimVirtualBuffer( &State->Buffer );
                    672:             State->CurrentOutput = (LPSTR)State->Buffer.Base;
                    673:             }
                    674:         }
                    675: 
                    676:     except( VirtualBufferExceptionFilter( GetExceptionCode(),
                    677:                                           GetExceptionInformation(),
                    678:                                           &State->Buffer
                    679:                                         )
                    680:           ) {
                    681: 
                    682:         //
                    683:         // We will get here if the exception filter was unable to
                    684:         // commit the memory.
                    685:         //
                    686: 
                    687:         fprintf( stderr, "PDC: out of memory.\n" );
                    688:         }
                    689: 
                    690:     //
                    691:     // All done with this request.  Return to the worker thread that
                    692:     // called us.
                    693:     //
                    694: 
                    695:     return;
                    696: }
                    697: 
                    698: 
                    699: 
                    700: VOID
                    701: ProcessReadFileCompletion(
                    702:     DWORD dwErrorCode,
                    703:     DWORD dwNumberOfBytesTransfered,
                    704:     LPOVERLAPPED lpOverlapped
                    705:     )
                    706: 
                    707: /*++
                    708: 
                    709: Routine Description:
                    710: 
                    711:     This function is called whenever an asynchronous I/O operation,
                    712:     queued by the previous function, completes.  This function
                    713:     calulates the address of the search request block and then
                    714:     queue the request to the work queue, now that the data is
                    715:     in memory.
                    716: 
                    717: Arguments:
                    718: 
                    719:     dwErrorCode - Supplies the error code that the I/O completed with.
                    720: 
                    721:     dwNumberOfBytesTransfered - Supplies the actual number of bytes
                    722:         transferred.
                    723: 
                    724:     lpOverlapped - Supplies a pointer to the structure given to
                    725:         ReadFileEx when the I/O operation was queued.
                    726: 
                    727: Return Value:
                    728: 
                    729:     None.
                    730: 
                    731: --*/
                    732: 
                    733: {
                    734:     PSEARCH_REQUEST SearchRequest;
                    735: 
                    736:     //
                    737:     // Since the data we need is now in memory, queue the search
                    738:     // request to the work queue.
                    739:     //
                    740: 
                    741:     SearchRequest = CONTAINING_RECORD( lpOverlapped, SEARCH_REQUEST, OverlappedIO );
                    742:     QueueWorkItem( WorkQueue, &SearchRequest->WorkItem );
                    743: }
                    744: 
                    745: 
                    746: 
                    747: VOID
                    748: ProcessSearchFile(
                    749:     IN PSEARCH_REQUEST SearchRequest,
                    750:     IN PSEARCH_REQUEST_STATE State
                    751:     )
                    752: 
                    753: /*++
                    754: 
                    755: Routine Description:
                    756: 
                    757:     This function performs the actual search of the contents of the
                    758:     passed file for the search string given on the command line.
                    759:     If we are using synchronous I/O, then do the read operation
                    760:     now.
                    761: 
                    762:     Search the contents of the file for any matches, and accumulate
                    763:     the match output in the virtual buffer using sprintf, which is
                    764:     multi-thread safe, even with the single threaded version of
                    765:     the libraries.
                    766: 
                    767: Arguments:
                    768: 
                    769:     SearchRequest - Supplies a pointer to the search request which
                    770:         contains the relevant information.
                    771: 
                    772:     State - Supplies a pointer to state information for the current
                    773:         thread.
                    774: 
                    775: Return Value:
                    776: 
                    777:     None.
                    778: 
                    779: --*/
                    780: 
                    781: {
                    782:     LPSTR FileData, s, s1, BegLine, EndLine, EndOfFile;
                    783:     DWORD LineNumber;
                    784:     DWORD MatchesFound;
                    785:     DWORD BytesRead;
                    786: 
                    787:     //
                    788:     // Get a pointer to the beginning of the file data in memory
                    789:     // and calculate the address of the end of file point in
                    790:     // memory.
                    791:     //
                    792: 
                    793:     FileData = SearchRequest->FileData;
                    794:     EndOfFile = FileData + SearchRequest->FileSize;
                    795: 
                    796:     //
                    797:     // If using synchronous I/O, then we have not read in the
                    798:     // file contents yet, so issue the synchronous read to get
                    799:     // the data into memory.
                    800:     //
                    801: 
                    802:     if (SyncIO) {
                    803:         if (!ReadFile( SearchRequest->File,
                    804:                        FileData,
                    805:                        SearchRequest->FileSize,
                    806:                        &BytesRead,
                    807:                        NULL
                    808:                      ) ||
                    809:             BytesRead != SearchRequest->FileSize
                    810:            ) {
                    811:             State->CurrentOutput += sprintf( State->CurrentOutput,
                    812:                                              "%s(0) : error %u: Unable to read file contents.\n",
                    813:                                              SearchRequest->FullPathName,
                    814:                                              GetLastError()
                    815:                                            );
                    816: 
                    817:             CloseHandle( SearchRequest->File );
                    818:             return;
                    819:             }
                    820:         }
                    821: 
                    822:     //
                    823:     // Close any open file handle associated with this request.
                    824:     //
                    825: 
                    826:     if (SearchRequest->File != NULL) {
                    827:         CloseHandle( SearchRequest->File );
                    828:         }
                    829: 
                    830:     //
                    831:     // Search the file contents, keeping track of line breaks
                    832:     // so we can tell the line number of each match.
                    833:     //
                    834: 
                    835:     s = FileData;
                    836:     LineNumber = 0;
                    837:     MatchesFound = 0;
                    838:     while (s < EndOfFile) {
                    839:         BegLine = s;
                    840:         while (s < EndOfFile && *s != '\n') {
                    841:             s++;
                    842:             }
                    843: 
                    844:         if (*s == '\n') {
                    845:             LineNumber += 1;
                    846:             EndLine = s - 1;
                    847:             if (EndLine > BegLine && EndLine[ -1 ] == '\r') {
                    848:                 EndLine -= 1;
                    849:                 }
                    850: 
                    851:             s1 = BegLine;
                    852:             while (s1 < (EndLine - SearchStringLength)) {
                    853:                 if (!(SearchFunction)( s1, SearchString, SearchStringLength )) {
                    854:                     //
                    855:                     // We have a match for this line.  Append the
                    856:                     // output to the virtual buffer and update the
                    857:                     // current output pointer.
                    858:                     //
                    859: 
                    860:                     State->CurrentOutput += sprintf( State->CurrentOutput,
                    861:                                                      "%s(%u) : %.*s\n",
                    862:                                                      SearchRequest->FullPathName,
                    863:                                                      LineNumber,
                    864:                                                      EndLine - BegLine,
                    865:                                                      BegLine
                    866:                                                    );
                    867:                     MatchesFound += 1;
                    868:                     break;
                    869:                     }
                    870: 
                    871:                 s1++;
                    872:                 }
                    873: 
                    874:             s++;
                    875:             }
                    876:         }
                    877: 
                    878:     if (MatchesFound) {
                    879:         MatchedLineCount += MatchesFound;
                    880:         MatchedFileCount += 1;
                    881:         }
                    882:     SearchedFileCount += 1;
                    883: 
                    884:     //
                    885:     // All done with the file contents.  Discard it either by
                    886:     // unmapping the view of the file in the case of mapped file
                    887:     // I/O or free the virtual memory for other types of I/O
                    888:     //
                    889: 
                    890:     if (MappedFileIO) {
                    891:         UnmapViewOfFile( FileData );
                    892:         }
                    893:     else {
                    894:         VirtualFree( FileData, 0, MEM_RELEASE );
                    895:         }
                    896: }
                    897: 
                    898: 
                    899: PWORK_QUEUE
                    900: CreateWorkQueue(
                    901:     IN DWORD NumberOfWorkerThreads,
                    902:     IN PWORKER_ROUTINE WorkerRoutine
                    903:     )
                    904: 
                    905: /*++
                    906: 
                    907: Routine Description:
                    908: 
                    909:     This function creates a work queue, with the specified number of
                    910:     threads to service work items placed in the queue.  Work items
                    911:     are removed from the queue in the same order that they are placed
                    912:     in the queue.
                    913: 
                    914: Arguments:
                    915: 
                    916:     NumberOfWorkerThreads - Specifies how many threads this function
                    917:         should create to process work items placed in the queue.
                    918:         Must be greater than 0 and less than 128.
                    919: 
                    920:     WorkerRoutine - Specifies the address of a routine to call
                    921:         for each work item as it is removed from the queue.  The
                    922:         thread context the routine is called in is undefined.
                    923: 
                    924: Return Value:
                    925: 
                    926:     A pointer to the work queue.  Returns NULL if unable to create
                    927:     the work queue and its worker threads.  Extended error information
                    928:     is available from GetLastError()
                    929: 
                    930: --*/
                    931: 
                    932: {
                    933:     PWORK_QUEUE WorkQueue;
                    934:     HANDLE Thread;
                    935:     DWORD ThreadId;
                    936:     DWORD i;
                    937: 
                    938:     //
                    939:     // Allocate space for the work queue, which includes an
                    940:     // array of thread handles.
                    941:     //
                    942: 
                    943:     WorkQueue = LocalAlloc( LMEM_ZEROINIT,
                    944:                             sizeof( *WorkQueue ) +
                    945:                                 (NumberOfWorkerThreads * sizeof( HANDLE ))
                    946:                           );
                    947:     if (WorkQueue == NULL) {
                    948:         return NULL;
                    949:         }
                    950: 
                    951:     //
                    952:     // The work queue is controlled by a counting semaphore that
                    953:     // is incremented each time a work item is placed in the queue
                    954:     // and decremented each time a worker thread wakes up to remove
                    955:     // an item from the queue.
                    956:     //
                    957: 
                    958:     if (WorkQueue->Semaphore = CreateSemaphore( NULL, 0, 1000, NULL )) {
                    959:         //
                    960:         // Mutual exclusion between the worker threads accessing
                    961:         // the work queue is done with a critical section.
                    962:         //
                    963: 
                    964:         InitializeCriticalSection( &WorkQueue->CriticalSection );
                    965: 
                    966:         //
                    967:         // The queue itself is just a doubly linked list, where
                    968:         // items are placed in the queue at the tail of the list
                    969:         // and removed from the queue from the head of the list.
                    970:         //
                    971: 
                    972:         InitializeListHead( &WorkQueue->Queue );
                    973: 
                    974:         //
                    975:         // Removed the address of the supplied worker function
                    976:         // in the work queue structure.
                    977:         //
                    978: 
                    979:         WorkQueue->WorkerRoutine = WorkerRoutine;
                    980: 
                    981:         //
                    982:         // Now create the requested number of worker threads.
                    983:         // The handle to each thread is remembered in an
                    984:         // array of thread handles in the work queue structure.
                    985:         //
                    986: 
                    987:         for (i=0; i<NumberOfWorkerThreads; i++) {
                    988:             Thread = CreateThread( NULL,
                    989:                                    0,
                    990:                                    WorkerThread,
                    991:                                    NULL,
                    992:                                    0,
                    993:                                    &ThreadId
                    994:                                  );
                    995:             if (Thread == NULL) {
                    996:                 break;
                    997:                 }
                    998:             else {
                    999:                 WorkQueue->NumberOfWorkerThreads++;
                   1000:                 WorkQueue->WorkerThreads[ i ] = Thread;
                   1001:                 }
                   1002:             }
                   1003: 
                   1004:         //
                   1005:         // If we successfully created all of the worker threads
                   1006:         // then return the address of the work queue structure
                   1007:         // to indicate success.
                   1008:         //
                   1009: 
                   1010:         if (i == NumberOfWorkerThreads) {
                   1011:             return WorkQueue;
                   1012:             }
                   1013:         }
                   1014: 
                   1015:     //
                   1016:     // Failed for some reason.  Destroy whatever we managed
                   1017:     // to create and return failure to the caller.
                   1018:     //
                   1019: 
                   1020:     DestroyWorkQueue( WorkQueue );
                   1021:     return NULL;
                   1022: }
                   1023: 
                   1024: 
                   1025: 
                   1026: VOID
                   1027: DestroyWorkQueue(
                   1028:     IN OUT PWORK_QUEUE WorkQueue
                   1029:     )
                   1030: 
                   1031: /*++
                   1032: 
                   1033: Routine Description:
                   1034: 
                   1035:     This function destroys a work queue created with the CreateWorkQueue
                   1036:     functions.  It attempts to shut down the worker threads cleanly
                   1037:     by queueing a terminate work item to each worker thread.  It then
                   1038:     waits for all the worker threads to terminate.  If the wait is
                   1039:     not satisfied within 30 seconds, then it goes ahead and terminates
                   1040:     all of the worker threads.
                   1041: 
                   1042: Arguments:
                   1043: 
                   1044:     WorkQueue - Supplies a pointer to the work queue to destroy.
                   1045: 
                   1046: Return Value:
                   1047: 
                   1048:     None.
                   1049: 
                   1050: --*/
                   1051: 
                   1052: {
                   1053:     DWORD i;
                   1054:     DWORD rc;
                   1055: 
                   1056:     //
                   1057:     // If the semaphore handle field is not NULL, then there
                   1058:     // may be threads to terminate.
                   1059:     //
                   1060: 
                   1061:     if (WorkQueue->Semaphore != NULL) {
                   1062:         //
                   1063:         // Set the termiating flag in the work queue and
                   1064:         // signal the counting semaphore by the number
                   1065:         // worker threads so they will all wake up and
                   1066:         // notice the terminating flag and exit.
                   1067:         //
                   1068: 
                   1069:         EnterCriticalSection( &WorkQueue->CriticalSection );
                   1070:         try {
                   1071:             WorkQueue->Terminating = TRUE;
                   1072:             ReleaseSemaphore( WorkQueue->Semaphore,
                   1073:                               WorkQueue->NumberOfWorkerThreads,
                   1074:                               NULL
                   1075:                             );
                   1076:             }
                   1077:         finally {
                   1078:             LeaveCriticalSection( &WorkQueue->CriticalSection );
                   1079:             }
                   1080: 
                   1081:         //
                   1082:         // Wait for all worker threads to wake up and see the
                   1083:         // terminate flag and then terminate themselves.  Timeout
                   1084:         // the wait after 30 seconds.
                   1085:         //
                   1086: 
                   1087:         while (TRUE) {
                   1088:             rc = WaitForMultipleObjectsEx( WorkQueue->NumberOfWorkerThreads,
                   1089:                                            WorkQueue->WorkerThreads,
                   1090:                                            TRUE,
                   1091:                                            30000,
                   1092:                                            TRUE
                   1093:                                          );
                   1094:             if (rc == WAIT_IO_COMPLETION) {
                   1095:                 //
                   1096:                 // If we came out of the wait because an I/O
                   1097:                 // completion routine was called, reissue the
                   1098:                 // wait.
                   1099:                 //
                   1100:                 continue;
                   1101:                 }
                   1102:             else {
                   1103:                 break;
                   1104:                 }
                   1105:             }
                   1106: 
                   1107:         //
                   1108:         // Now close our thread handles so they will actually
                   1109:         // evaporate.  If the wait above was unsuccessful,
                   1110:         // then first attempt to force the termination of
                   1111:         // each worker thread prior to closing the handle.
                   1112:         //
                   1113: 
                   1114:         for (i=0; i<WorkQueue->NumberOfWorkerThreads; i++) {
                   1115:             if (rc != NO_ERROR) {
                   1116:                 TerminateThread( WorkQueue->WorkerThreads[ i ], rc );
                   1117:                 }
                   1118: 
                   1119:             CloseHandle( WorkQueue->WorkerThreads[ i ] );
                   1120:             }
                   1121: 
                   1122:         //
                   1123:         // All threads stopped, all thread handles closed.  Now
                   1124:         // delete the critical section and close the semaphore
                   1125:         // handle.
                   1126:         //
                   1127: 
                   1128:         DeleteCriticalSection( &WorkQueue->CriticalSection );
                   1129:         CloseHandle( WorkQueue->Semaphore );
                   1130:         }
                   1131: 
                   1132:     //
                   1133:     // Everything done, now free the memory used by the work queue.
                   1134:     //
                   1135: 
                   1136:     LocalFree( WorkQueue );
                   1137:     return;
                   1138: }
                   1139: 
                   1140: 
                   1141: 
                   1142: BOOL
                   1143: QueueWorkItem(
                   1144:     IN OUT PWORK_QUEUE WorkQueue,
                   1145:     IN PWORK_QUEUE_ITEM WorkItem
                   1146:     )
                   1147: 
                   1148: /*++
                   1149: 
                   1150: Routine Description:
                   1151: 
                   1152:     This function queues a work item to the passed work queue that is
                   1153:     processed by one of the worker threads associated with the queue.
                   1154: 
                   1155: Arguments:
                   1156: 
                   1157:     WorkQueue - Supplies a pointer to the work queue that is to
                   1158:         receive the work item.
                   1159: 
                   1160:     WorkItem - Supplies a pointer to the work item to add the the queue.
                   1161:         The work item structure contains a doubly linked list entry, the
                   1162:         address of a routine to call and a parameter to pass to that
                   1163:         routine.  It is the routine's responsibility to reclaim the
                   1164:         storage occupied by the WorkItem structure.
                   1165: 
                   1166: Return Value:
                   1167: 
                   1168:     TRUE if operation was successful.  Otherwise returns FALSE and
                   1169:     extended error information is available from GetLastError()
                   1170: 
                   1171: --*/
                   1172: 
                   1173: {
                   1174:     BOOL Result;
                   1175: 
                   1176:     //
                   1177:     // Acquire the work queue critical section and insert the work item
                   1178:     // in the queue and release the semaphore if the work item is not
                   1179:     // already in the list.
                   1180:     //
                   1181: 
                   1182:     EnterCriticalSection( &WorkQueue->CriticalSection );
                   1183:     Result = TRUE;
                   1184:     try {
                   1185:         InsertTailList( &WorkQueue->Queue, &WorkItem->List );
                   1186:         Result = ReleaseSemaphore( WorkQueue->Semaphore, 1, NULL );
                   1187:         }
                   1188:     finally {
                   1189:         LeaveCriticalSection( &WorkQueue->CriticalSection );
                   1190:         }
                   1191: 
                   1192:     return Result;
                   1193: }
                   1194: 
                   1195: 
                   1196: 
                   1197: DWORD
                   1198: WorkerThread(
                   1199:     LPVOID lpThreadParameter
                   1200:     )
                   1201: {
                   1202:     DWORD rc;
                   1203:     WORK_QUEUE_ITEM InitWorkItem;
                   1204:     PWORK_QUEUE_ITEM WorkItem;
                   1205: 
                   1206:     //
                   1207:     // Call the worker routine with an initialize work item
                   1208:     // to give it a change to initialize some per thread
                   1209:     // state that will passed to it for each subsequent
                   1210:     // work item.
                   1211:     //
                   1212: 
                   1213:     InitWorkItem.Reason = WORK_INITIALIZE_ITEM;
                   1214:     (WorkQueue->WorkerRoutine)( &InitWorkItem );
                   1215:     while( TRUE ) {
                   1216: 
                   1217:         //
                   1218:         // Wait until something is put in the queue (semaphore is
                   1219:         // released), remove the item from the queue, mark it not
                   1220:         // inserted, and execute the specified routine.
                   1221:         //
                   1222: 
                   1223:         rc = WaitForSingleObjectEx( WorkQueue->Semaphore, 0xFFFFFFFF, TRUE );
                   1224:         if (rc == WAIT_IO_COMPLETION) {
                   1225:             continue;
                   1226:             }
                   1227: 
                   1228:         EnterCriticalSection( &WorkQueue->CriticalSection );
                   1229:         try {
                   1230:             if (WorkQueue->Terminating && IsListEmpty( &WorkQueue->Queue )) {
                   1231:                 break;
                   1232:                 }
                   1233: 
                   1234:             WorkItem = (PWORK_QUEUE_ITEM)RemoveHeadList( &WorkQueue->Queue );
                   1235:             }
                   1236:         finally {
                   1237:             LeaveCriticalSection( &WorkQueue->CriticalSection );
                   1238:             }
                   1239: 
                   1240:         //
                   1241:         // Execute the worker routine for this work item.
                   1242:         //
                   1243: 
                   1244:         try {
                   1245:             (WorkQueue->WorkerRoutine)( WorkItem );
                   1246:             }
                   1247: 
                   1248:         except( EXCEPTION_EXECUTE_HANDLER ) {
                   1249:             }
                   1250:         }
                   1251: 
                   1252:     InitWorkItem.Reason = WORK_TERMINATE_ITEM;
                   1253:     (WorkQueue->WorkerRoutine)( &InitWorkItem );
                   1254: 
                   1255:     ExitThread( 0 );
                   1256:     return 0;       // This will exit this thread
                   1257: }
                   1258: 
                   1259: 
                   1260: BOOL
                   1261: CreateVirtualBuffer(
                   1262:     OUT PVIRTUAL_BUFFER Buffer,
                   1263:     IN DWORD CommitSize,
                   1264:     IN DWORD ReserveSize OPTIONAL
                   1265:     )
                   1266: 
                   1267: /*++
                   1268: 
                   1269: Routine Description:
                   1270: 
                   1271:     This function is called to create a virtual buffer.  A virtual
                   1272:     buffer is a contiguous range of virtual memory, where some initial
                   1273:     prefix portion of the memory is committed and the remainder is only
                   1274:     reserved virtual address space.  A routine is provided to extend the
                   1275:     size of the committed region incrementally or to trim the size of
                   1276:     the committed region back to some specified amount.
                   1277: 
                   1278: Arguments:
                   1279: 
                   1280:     Buffer - Pointer to the virtual buffer control structure that is
                   1281:         filled in by this function.
                   1282: 
                   1283:     CommitSize - Size of the initial committed portion of the buffer.
                   1284:         May be zero.
                   1285: 
                   1286:     ReserveSize - Amount of virtual address space to reserve for the
                   1287:         buffer.  May be zero, in which case amount reserved is the
                   1288:         committed size plus one, rounded up to the next 64KB boundary.
                   1289: 
                   1290: Return Value:
                   1291: 
                   1292:     TRUE if operation was successful.  Otherwise returns FALSE and
                   1293:     extended error information is available from GetLastError()
                   1294: 
                   1295: --*/
                   1296: 
                   1297: {
                   1298:     SYSTEM_INFO SystemInformation;
                   1299: 
                   1300:     //
                   1301:     // Query the page size from the system for rounding
                   1302:     // our memory allocations.
                   1303:     //
                   1304: 
                   1305:     GetSystemInfo( &SystemInformation );
                   1306:     Buffer->PageSize = SystemInformation.dwPageSize;
                   1307: 
                   1308:     //
                   1309:     // If the reserve size was not specified, default it by
                   1310:     // rounding up the initial committed size to a 64KB
                   1311:     // boundary.  This is because the Win32 Virtual Memory
                   1312:     // API calls always allocate virtual address space on
                   1313:     // 64KB boundaries, so we might well have it available
                   1314:     // for commitment.
                   1315:     //
                   1316: 
                   1317:     if (!ARGUMENT_PRESENT( ReserveSize )) {
                   1318:         ReserveSize = ROUND_UP( CommitSize + 1, 0x10000 );
                   1319:         }
                   1320: 
                   1321:     //
                   1322:     // Attempt to reserve the address space.
                   1323:     //
                   1324: 
                   1325:     Buffer->Base = VirtualAlloc( NULL,
                   1326:                                  ReserveSize,
                   1327:                                  MEM_RESERVE,
                   1328:                                  PAGE_READWRITE
                   1329:                                );
                   1330:     if (Buffer->Base == NULL) {
                   1331:         //
                   1332:         // Unable to reserve address space, return failure.
                   1333:         //
                   1334: 
                   1335:         return FALSE;
                   1336:         }
                   1337: 
                   1338:     //
                   1339:     // Attempt to commit some initial portion of the reserved region.
                   1340:     //
                   1341:     //
                   1342: 
                   1343:     CommitSize = ROUND_UP( CommitSize, Buffer->PageSize );
                   1344:     if (CommitSize == 0 ||
                   1345:         VirtualAlloc( Buffer->Base,
                   1346:                       CommitSize,
                   1347:                       MEM_COMMIT,
                   1348:                       PAGE_READWRITE
                   1349:                     ) != NULL
                   1350:        ) {
                   1351:         //
                   1352:         // Either the size of the committed region was zero or the
                   1353:         // commitment succeeded.  In either case calculate the
                   1354:         // address of the first byte after the committed region
                   1355:         // and the address of the first byte after the reserved
                   1356:         // region and return successs.
                   1357:         //
                   1358: 
                   1359:         Buffer->CommitLimit = (LPVOID)
                   1360:             ((char *)Buffer->Base + CommitSize);
                   1361: 
                   1362:         Buffer->ReserveLimit = (LPVOID)
                   1363:             ((char *)Buffer->Base + ReserveSize);
                   1364: 
                   1365:         return TRUE;
                   1366:         }
                   1367: 
                   1368:     //
                   1369:     // If unable to commit the memory, release the virtual address
                   1370:     // range allocated above and return failure.
                   1371:     //
                   1372: 
                   1373:     VirtualFree( Buffer->Base, 0, MEM_RELEASE );
                   1374:     return FALSE;
                   1375: }
                   1376: 
                   1377: 
                   1378: 
                   1379: BOOL
                   1380: ExtendVirtualBuffer(
                   1381:     IN PVIRTUAL_BUFFER Buffer,
                   1382:     IN LPVOID Address
                   1383:     )
                   1384: 
                   1385: /*++
                   1386: 
                   1387: Routine Description:
                   1388: 
                   1389:     This function is called to extend the committed portion of a virtual
                   1390:     buffer.
                   1391: 
                   1392: Arguments:
                   1393: 
                   1394:     Buffer - Pointer to the virtual buffer control structure.
                   1395: 
                   1396:     Address - Byte at this address is committed, along with all memory
                   1397:         from the beginning of the buffer to this address.  If the
                   1398:         address is already within the committed portion of the virtual
                   1399:         buffer, then this routine does nothing.  If outside the reserved
                   1400:         portion of the virtual buffer, then this routine returns an
                   1401:         error.
                   1402: 
                   1403:         Otherwise enough pages are committed so that the memory from the
                   1404:         base of the buffer to the passed address is a contiguous region
                   1405:         of committed memory.
                   1406: 
                   1407: 
                   1408: Return Value:
                   1409: 
                   1410:     TRUE if operation was successful.  Otherwise returns FALSE and
                   1411:     extended error information is available from GetLastError()
                   1412: 
                   1413: --*/
                   1414: 
                   1415: {
                   1416:     DWORD NewCommitSize;
                   1417:     LPVOID NewCommitLimit;
                   1418: 
                   1419:     //
                   1420:     // See if address is within the buffer.
                   1421:     //
                   1422: 
                   1423:     if (Address >= Buffer->Base && Address < Buffer->ReserveLimit) {
                   1424:         //
                   1425:         // See if the address is within the committed portion of
                   1426:         // the buffer.  If so return success immediately.
                   1427:         //
                   1428: 
                   1429:         if (Address < Buffer->CommitLimit) {
                   1430:             return TRUE;
                   1431:             }
                   1432: 
                   1433:         //
                   1434:         // Address is within the reserved portion.  Determine how many
                   1435:         // bytes are between the address and the end of the committed
                   1436:         // portion of the buffer.  Round this size to a multiple of
                   1437:         // the page size and this is the amount we will attempt to
                   1438:         // commit.
                   1439:         //
                   1440: 
                   1441:         NewCommitSize =
                   1442:             ((DWORD)ROUND_UP( (DWORD)Address + 1, Buffer->PageSize ) -
                   1443:              (DWORD)Buffer->CommitLimit
                   1444:             );
                   1445: 
                   1446:         //
                   1447:         // Attempt to commit the memory.
                   1448:         //
                   1449: 
                   1450:         NewCommitLimit = VirtualAlloc( Buffer->CommitLimit,
                   1451:                                        NewCommitSize,
                   1452:                                        MEM_COMMIT,
                   1453:                                        PAGE_READWRITE
                   1454:                                      );
                   1455:         if (NewCommitLimit != NULL) {
                   1456:             //
                   1457:             // Successful, so update the upper limit of the committed
                   1458:             // region of the buffer and return success.
                   1459:             //
                   1460: 
                   1461:             Buffer->CommitLimit = (LPVOID)
                   1462:                 ((DWORD)NewCommitLimit + NewCommitSize);
                   1463: 
                   1464:             return TRUE;
                   1465:             }
                   1466:         }
                   1467: 
                   1468:     //
                   1469:     // Address is outside of the buffer, return failure.
                   1470:     //
                   1471: 
                   1472:     return FALSE;
                   1473: }
                   1474: 
                   1475: 
                   1476: BOOL
                   1477: TrimVirtualBuffer(
                   1478:     IN PVIRTUAL_BUFFER Buffer
                   1479:     )
                   1480: 
                   1481: /*++
                   1482: 
                   1483: Routine Description:
                   1484: 
                   1485:     This function is called to decommit any memory that has been
                   1486:     committed for this virtual buffer.
                   1487: 
                   1488: Arguments:
                   1489: 
                   1490:     Buffer - Pointer to the virtual buffer control structure.
                   1491: 
                   1492: Return Value:
                   1493: 
                   1494:     TRUE if operation was successful.  Otherwise returns FALSE and
                   1495:     extended error information is available from GetLastError()
                   1496: 
                   1497: --*/
                   1498: 
                   1499: {
                   1500:     Buffer->CommitLimit = Buffer->Base;
                   1501:     return VirtualFree( Buffer->Base, 0, MEM_DECOMMIT );
                   1502: }
                   1503: 
                   1504: 
                   1505: 
                   1506: BOOL
                   1507: FreeVirtualBuffer(
                   1508:     IN PVIRTUAL_BUFFER Buffer
                   1509:     )
                   1510: /*++
                   1511: 
                   1512: Routine Description:
                   1513: 
                   1514:     This function is called to free all the memory that is associated
                   1515:     with this virtual buffer.
                   1516: 
                   1517: Arguments:
                   1518: 
                   1519:     Buffer - Pointer to the virtual buffer control structure.
                   1520: 
                   1521: Return Value:
                   1522: 
                   1523:     TRUE if operation was successful.  Otherwise returns FALSE and
                   1524:     extended error information is available from GetLastError()
                   1525: 
                   1526: --*/
                   1527: 
                   1528: {
                   1529:     //
                   1530:     // Decommit and release all virtual memory associated with
                   1531:     // this virtual buffer.
                   1532:     //
                   1533: 
                   1534:     return VirtualFree( Buffer->Base, 0, MEM_RELEASE );
                   1535: }
                   1536: 
                   1537: 
                   1538: 
                   1539: int
                   1540: VirtualBufferExceptionFilter(
                   1541:     IN DWORD ExceptionCode,
                   1542:     IN PEXCEPTION_POINTERS ExceptionInfo,
                   1543:     IN OUT PVIRTUAL_BUFFER Buffer
                   1544:     )
                   1545: 
                   1546: /*++
                   1547: 
                   1548: Routine Description:
                   1549: 
                   1550:     This function is an exception filter that handles exceptions that
                   1551:     referenced uncommitted but reserved memory contained in the passed
                   1552:     virtual buffer.  It this filter routine is able to commit the
                   1553:     additional pages needed to allow the memory reference to succeed,
                   1554:     then it will re-execute the faulting instruction.  If it is unable
                   1555:     to commit the pages, it will execute the callers exception handler.
                   1556: 
                   1557:     If the exception is not an access violation or is an access
                   1558:     violation but does not reference memory contained in the reserved
                   1559:     portion of the virtual buffer, then this filter passes the exception
                   1560:     on up the exception chain.
                   1561: 
                   1562: Arguments:
                   1563: 
                   1564:     ExceptionCode - Reason for the exception.
                   1565: 
                   1566:     ExceptionInfo - Information about the exception and the context
                   1567:         that it occurred in.
                   1568: 
                   1569:     Buffer - Points to a virtual buffer control structure that defines
                   1570:         the reserved memory region that is to be committed whenever an
                   1571:         attempt is made to access it.
                   1572: 
                   1573: Return Value:
                   1574: 
                   1575:     Exception disposition code that tells the exception dispatcher what
                   1576:     to do with this exception.  One of three values is returned:
                   1577: 
                   1578:         EXCEPTION_EXECUTE_HANDLER - execute the exception handler
                   1579:             associated with the exception clause that called this filter
                   1580:             procedure.
                   1581: 
                   1582:         EXCEPTION_CONTINUE_SEARCH - Continue searching for an exception
                   1583:             handler to handle this exception.
                   1584: 
                   1585:         EXCEPTION_CONTINUE_EXECUTION - Dismiss this exception and return
                   1586:             control to the instruction that caused the exception.
                   1587: 
                   1588: 
                   1589: --*/
                   1590: 
                   1591: {
                   1592:     LPVOID FaultingAddress;
                   1593: 
                   1594:     //
                   1595:     // If this is an access violation touching memory within
                   1596:     // our reserved buffer, but outside of the committed portion
                   1597:     // of the buffer, then we are going to take this exception.
                   1598:     //
                   1599: 
                   1600:     if (ExceptionCode == STATUS_ACCESS_VIOLATION) {
                   1601:         //
                   1602:         // Get the virtual address that caused the access violation
                   1603:         // from the exception record.  Determine if the address
                   1604:         // references memory within the reserved but uncommitted
                   1605:         // portion of the virtual buffer.
                   1606:         //
                   1607: 
                   1608:         FaultingAddress = (LPVOID)ExceptionInfo->ExceptionRecord->ExceptionInformation[ 1 ];
                   1609:         if (FaultingAddress >= Buffer->CommitLimit &&
                   1610:             FaultingAddress <= Buffer->ReserveLimit
                   1611:            ) {
                   1612:             //
                   1613:             // This is our exception.  Try to extend the buffer
                   1614:             // to including the faulting address.
                   1615:             //
                   1616: 
                   1617:             if (ExtendVirtualBuffer( Buffer, FaultingAddress )) {
                   1618:                 //
                   1619:                 // Buffer successfully extended, so re-execute the
                   1620:                 // faulting instruction.
                   1621:                 //
                   1622: 
                   1623:                return EXCEPTION_CONTINUE_EXECUTION;
                   1624:                 }
                   1625:             else {
                   1626:                 //
                   1627:                 // Unable to extend the buffer.  Stop searching
                   1628:                 // for exception handlers and execute the caller's
                   1629:                 // handler.
                   1630:                 //
                   1631: 
                   1632:                 return EXCEPTION_EXECUTE_HANDLER;
                   1633:                 }
                   1634:             }
                   1635:         }
                   1636: 
                   1637:     //
                   1638:     // Not an exception we care about, so pass it up the chain.
                   1639:     //
                   1640: 
                   1641:     return EXCEPTION_CONTINUE_SEARCH;
                   1642: }
                   1643: 
                   1644: 
                   1645: BOOL
                   1646: EnumerateDirectoryTree(
                   1647:     LPSTR DirectoryPath,
                   1648:     PDIRECTORY_ENUMERATE_ROUTINE EnumerateRoutine,
                   1649:     PVOID EnumerateParameter
                   1650:     )
                   1651: 
                   1652: /*++
                   1653: 
                   1654: Routine Description:
                   1655: 
                   1656:     This function walks a directory tree, depth first, calling the
                   1657:     passed enumeration routine for each directory and file found
                   1658:     in the tree.  The enumeration routine is passed the full path
                   1659:     of the file, the directory information associated with the file
                   1660:     and an enumeration parameter that is uninterpreted by this
                   1661:     function.
                   1662: 
                   1663: Arguments:
                   1664: 
                   1665:     DirectoryPath - Absolute or relative path to the directory that
                   1666:         will is the root of the tree to enumerate.
                   1667: 
                   1668:     EnumerateRoutine - Pointer to an enumeration routine to call
                   1669:         for each file and directory found.
                   1670: 
                   1671:     EnumerateParameter - Uninterpreted 32-bit value that is passed
                   1672:         to the EnumerationRoutine each time it is called.
                   1673: 
                   1674: Return Value:
                   1675: 
                   1676:     TRUE if operation was successful.  Otherwise returns FALSE and
                   1677:     extended error information is available from GetLastError()
                   1678: 
                   1679: --*/
                   1680: 
                   1681: {
                   1682:     BOOL Result;
                   1683:     VIRTUAL_BUFFER Buffer;
                   1684:     PENUMERATE_DIRECTORY_STATE State;
                   1685:     PENUMERATE_DIRECTORY_STACK Stack;
                   1686:     WIN32_FIND_DATA FindFileData;
                   1687: 
                   1688:     //
                   1689:     // Create a virtual buffer with an initial committed size of
                   1690:     // our directory state buffer, and a maximum reserved size of
                   1691:     // the longest possible full path based on the maximum depth
                   1692:     // we handle and the maximum length of each path component.
                   1693:     //
                   1694: 
                   1695:     if (!CreateVirtualBuffer( &Buffer,
                   1696:                               sizeof( ENUMERATE_DIRECTORY_STATE ),
                   1697:                               sizeof( ENUMERATE_DIRECTORY_STATE ) +
                   1698:                                  MAX_DEPTH * MAX_PATH
                   1699:                             )
                   1700:        ) {
                   1701:         return FALSE;
                   1702:         }
                   1703: 
                   1704:     //
                   1705:     // This buffer will be used to maintain a stack of directory
                   1706:     // search handles, as well as accumulate the full path string
                   1707:     // as we descend the directory tree.
                   1708:     //
                   1709: 
                   1710:     State = (PENUMERATE_DIRECTORY_STATE)Buffer.Base;
                   1711:     State->Depth = 0;
                   1712:     Stack = &State->Stack[ 0 ];
                   1713: 
                   1714:     //
                   1715:     // Enter a try ... finally block so we can insure that we clean
                   1716:     // up after ourselves on exit.
                   1717:     //
                   1718: 
                   1719:     try {
                   1720:         //
                   1721:         // First translate the passed in DirectoryPath into a fully
                   1722:         // qualified path.  This path will be the initial value in
                   1723:         // our path buffer.  The initial allocation of the path buffer
                   1724:         // is big enough for this initial request, so does not need
                   1725:         // to be guarded by a try ... except clause.
                   1726:         //
                   1727: 
                   1728:         if (GetFullPathName( DirectoryPath, MAX_PATH, State->Path, &Stack->PathEnd )) {
                   1729:             //
                   1730:             // Now enter a try ... except block that will be used to
                   1731:             // manage the commitment of space in the path buffer as
                   1732:             // we append subdirectory names and file names to it.
                   1733:             // Using the virtual buffer allows us to handle full
                   1734:             // path names up to 16KB in length, with an initial
                   1735:             // allocation of 4KB.
                   1736:             //
                   1737: 
                   1738:             try {
                   1739:                 //
                   1740:                 // Walk the directory tree.  The outer loop is executed
                   1741:                 // once for each directory in the tree.
                   1742:                 //
                   1743: 
                   1744:                 while (TRUE) {
                   1745: startDirectorySearch:
                   1746:                     //
                   1747:                     // Find the end of the current path, and make sure
                   1748:                     // there is a trailing path separator.
                   1749:                     //
                   1750: 
                   1751:                     Stack->PathEnd = strchr( State->Path, '\0' );
                   1752:                     if (Stack->PathEnd > State->Path && Stack->PathEnd[ -1 ] != '\\') {
                   1753:                         *(Stack->PathEnd)++ = '\\';
                   1754:                         }
                   1755: 
                   1756:                     //
                   1757:                     // Now append the wild card specification that will
                   1758:                     // let us enumerate all the entries in this directory.
                   1759:                     // Call FindFirstFile to find the first entry in the
                   1760:                     // directory.
                   1761:                     //
                   1762: 
                   1763:                     strcpy( Stack->PathEnd, "*.*" );
                   1764:                     Stack->FindHandle = FindFirstFile( State->Path,
                   1765:                                                        &FindFileData
                   1766:                                                      );
                   1767:                     if (Stack->FindHandle != INVALID_HANDLE_VALUE) {
                   1768:                         //
                   1769:                         // Entry found.  Now loop through the entire
                   1770:                         // directory processing each entry found,
                   1771:                         // including the first one.
                   1772:                         //
                   1773:                         do {
                   1774:                             //
                   1775:                             // Ignore bogus pseudo-directories that are
                   1776:                             // returned by some file systems (e.g. FAT).
                   1777:                             //
                   1778: 
                   1779:                             if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
                   1780:                                 (!strcmp( FindFileData.cFileName, "." ) ||
                   1781:                                  !strcmp( FindFileData.cFileName, ".." )
                   1782:                                 )
                   1783:                                ) {
                   1784:                                 continue;
                   1785:                                 }
                   1786: 
                   1787:                             //
                   1788:                             // Copy the file name portion from the current
                   1789:                             // directory entry to the last component in the
                   1790:                             // path buffer.
                   1791:                             //
                   1792: 
                   1793:                             strcpy( Stack->PathEnd, FindFileData.cFileName );
                   1794: 
                   1795:                             //
                   1796:                             // Call the supplied enumeration routine with the
                   1797:                             // full path we have built up in the path buffer,
                   1798:                             // the directory information for this directory
                   1799:                             // entry and the supplied enumeration parameter.
                   1800:                             //
                   1801: 
                   1802:                             (*EnumerateRoutine)( State->Path, &FindFileData, EnumerateParameter );
                   1803: 
                   1804:                             //
                   1805:                             // If this is entry is a subdirectory, then it is
                   1806:                             // time to recurse.  Do this by incrementing the
                   1807:                             // stack pointer and depth and jumping to the top
                   1808:                             // of the outer loop to process current contents
                   1809:                             // of the path buffer as a fully qualified name of
                   1810:                             // a directory.
                   1811:                             //
                   1812: 
                   1813:                             if (FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
                   1814:                                 Stack++;
                   1815:                                 State->Depth++;
                   1816:                                 goto startDirectorySearch;
                   1817: restartDirectorySearch:         ;
                   1818:                                 }
                   1819: 
                   1820:                             //
                   1821:                             // Here to find the next entry in the current directory.
                   1822:                             //
                   1823:                             }
                   1824: 
                   1825:                         while ( FindNextFile( Stack->FindHandle, &FindFileData ) );
                   1826: 
                   1827:                         //
                   1828:                         // No more entries in the current directory, so close
                   1829:                         // the search handle and fall into the code that will
                   1830:                         // pop our stack of directory seacrh handles.
                   1831: 
                   1832:                         FindClose( Stack->FindHandle );
                   1833:                         }
                   1834: 
                   1835:                     //
                   1836:                     // Here when done with a directory.  See if we are pushed
                   1837:                     // inside another directory.  If not, then we are done
                   1838:                     // enumerating the whole tree, so break out of the loop.
                   1839:                     //
                   1840: 
                   1841:                     if (!State->Depth) {
                   1842:                         Result = TRUE;
                   1843:                         break;
                   1844:                         }
                   1845: 
                   1846:                     //
                   1847:                     // We were pushed within another directory search,
                   1848:                     // so pop the stack to restore its search handle
                   1849:                     // and path buffer position and resume the search
                   1850:                     // within that directory.
                   1851: 
                   1852:                     State->Depth--;
                   1853:                     --Stack;
                   1854:                     goto restartDirectorySearch;
                   1855:                     }
                   1856:                 }
                   1857: 
                   1858:             //
                   1859:             // Any of the code that appends to the path buffer within
                   1860:             // the above try ... except clause can cause an access
                   1861:             // violation if the path buffer becomes longer than its
                   1862:             // current committed size.  This exception filter
                   1863:             // will dynamically commit additional pages as needed
                   1864:             // and resume execution.
                   1865:             //
                   1866: 
                   1867:             except( VirtualBufferExceptionFilter( GetExceptionCode(),
                   1868:                                                   GetExceptionInformation(),
                   1869:                                                   &Buffer
                   1870:                                                 )
                   1871:                   ) {
                   1872:                 //
                   1873:                 // We will get here if the exception filter was unable to
                   1874:                 // commit the memory.
                   1875:                 //
                   1876: 
                   1877:                 Result = FALSE;
                   1878:                 }
                   1879:             }
                   1880:         else {
                   1881:             //
                   1882:             // Initial GetFullPathName failed, so return a failure.
                   1883:             //
                   1884: 
                   1885:             Result = FALSE;
                   1886:             }
                   1887:         }
                   1888:     finally {
                   1889:         //
                   1890:         // Here on our way out of the outer try ... finally block.
                   1891:         // Make sure all our search handles have been closed and then
                   1892:         // free the virtual buffer.  The only way this code is not
                   1893:         // executed is if code within the try ... finally block
                   1894:         // called ExitThread or ExitProcess, or an external thread
                   1895:         // or process terminated this thread or process.
                   1896:         //
                   1897:         // In the case of process death, this is not a problem, because
                   1898:         // process terminate closes all open handles attached to the process
                   1899:         // and frees all private virtual memory that is part of the address
                   1900:         // space of the process.
                   1901:         //
                   1902:         // In the case ot thread death, the code below is not executed if
                   1903:         // the thread terminates via ExitThread in the context of the
                   1904:         // try .. finally or if an external thread, either in this process
                   1905:         // or another process called TerminateThread on this thread.
                   1906:         //
                   1907: 
                   1908:         while (State->Depth--) {
                   1909:             --Stack;
                   1910:             FindClose( Stack->FindHandle );
                   1911:             }
                   1912: 
                   1913:         FreeVirtualBuffer( &Buffer );
                   1914:         }
                   1915: 
                   1916:     return Result;
                   1917: }
                   1918: 
                   1919: 
                   1920: BOOL
                   1921: ProcessCommandLineArguments(
                   1922:     int argc,
                   1923:     char *argv[]
                   1924:     )
                   1925: {
                   1926:     BOOL Result;
                   1927:     LPSTR s;
                   1928: 
                   1929:     Result = FALSE;
                   1930:     try {
                   1931:         if (argc < 1) {
                   1932:             return Result;
                   1933:             }
                   1934: 
                   1935:         while (--argc) {
                   1936:             s = *++argv;
                   1937:             if (*s == '-' || *s == '/') {
                   1938:                 while (*++s) {
                   1939:                     switch( tolower( *s ) ) {
                   1940:                         case 'm':
                   1941:                             MappedFileIO = TRUE;
                   1942:                             break;
                   1943: 
                   1944:                         case 'a':
                   1945:                             ASyncIO = TRUE;
                   1946:                             break;
                   1947: 
                   1948:                         case 's':
                   1949:                             SyncIO = TRUE;
                   1950:                             break;
                   1951: 
                   1952:                         case 'v':
                   1953:                             Verbose = TRUE;
                   1954:                             break;
                   1955: 
                   1956:                         case 'y':
                   1957:                             IgnoreCase = TRUE;
                   1958:                             break;
                   1959: 
                   1960:                         case 't':
                   1961:                             if (--argc) {
                   1962:                                 NumberOfWorkerThreads = atoi( *++argv );
                   1963:                                 if (NumberOfWorkerThreads > 0 && NumberOfWorkerThreads < 128) {
                   1964:                                     break;
                   1965:                                     }
                   1966:                                 }
                   1967: 
                   1968:                             // fall through if -t argument missing.
                   1969: 
                   1970:                         case '?':
                   1971:                         case 'h':
                   1972:                         default:
                   1973:                             return Result;
                   1974:                         }
                   1975:                     }
                   1976:                 }
                   1977:             else
                   1978:             if (SearchString == NULL) {
                   1979:                 SearchString = s;
                   1980:                 }
                   1981:             else
                   1982:             if (DirectoryPath == NULL) {
                   1983:                 DirectoryPath = s;
                   1984:                 }
                   1985:             else {
                   1986:                 return Result;
                   1987:                 }
                   1988:             }
                   1989: 
                   1990:         if (SearchString == NULL) {
                   1991:             return Result;
                   1992:             }
                   1993: 
                   1994:         SearchStringLength = strlen( SearchString );
                   1995:         if (SearchStringLength == 0) {
                   1996:             return Result;
                   1997:             }
                   1998: 
                   1999:         if (IgnoreCase) {
                   2000:             SearchFunction = _strnicmp;
                   2001:             }
                   2002:         else {
                   2003:             SearchFunction = strncmp;
                   2004:             }
                   2005: 
                   2006:         if (DirectoryPath == NULL) {
                   2007:             DirectoryPath = ".";
                   2008:             }
                   2009: 
                   2010:         if (!(MappedFileIO || ASyncIO || SyncIO)) {
                   2011:             MappedFileIO = TRUE;
                   2012:             }
                   2013: 
                   2014:         if (Verbose) {
                   2015:             fprintf( stderr, "Directory Tree: %s\n", DirectoryPath );
                   2016:             fprintf( stderr, "Search String: '%s'\n", SearchString );
                   2017:             fprintf( stderr, "Case %ssensitive\n", IgnoreCase ? "in" : "" );
                   2018:             fprintf( stderr, "Number of Worker Threads: %u\n", NumberOfWorkerThreads );
                   2019:             if (MappedFileIO) {
                   2020:                 fprintf( stderr, "Using Mapped File I/O\n" );
                   2021:                 }
                   2022:             else
                   2023:             if (ASyncIO) {
                   2024:                 fprintf( stderr, "Using ASynchronous File I/O\n" );
                   2025:                 }
                   2026:             else
                   2027:             if (MappedFileIO) {
                   2028:                 fprintf( stderr, "Using Synchronous File I/O\n" );
                   2029:                 }
                   2030:             }
                   2031: 
                   2032:         Result = TRUE;
                   2033:         return Result;
                   2034:         }
                   2035:     finally {
                   2036:         if (!Result) {
                   2037:             fprintf( stderr, "usage: PDC [-h] [-v] [-y] [-a | -s | -m] [-t n] SearchString [DirectoryPath]\n" );
                   2038:             fprintf( stderr, "Where...\n" );
                   2039:             fprintf( stderr, "    -h - prints this message\n" );
                   2040:             fprintf( stderr, "    -v - generates verbose output\n" );
                   2041:             fprintf( stderr, "    -y - ignores case when doing comparision\n" );
                   2042:             fprintf( stderr, "    -t - specifies the number of threads to use (defaults to 4 * number of processors)\n" );
                   2043:             fprintf( stderr, "    -a - uses asynchronous file I/O\n" );
                   2044:             fprintf( stderr, "    -s - uses synchronous file I/O\n" );
                   2045:             fprintf( stderr, "    -m - uses mapped file I/O (default)\n" );
                   2046:             fprintf( stderr, "    SearchString - specifies the text to search for\n" );
                   2047:             fprintf( stderr, "    DirectoryPath - specifies the directory to start from (defaults to .)\n" );
                   2048:             }
                   2049:         }
                   2050: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.