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

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

unix.superglobalmegacorp.com

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