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

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

unix.superglobalmegacorp.com

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