pkiutilities/PKCS12/CrBer/Src/crdata.cpp
changeset 0 164170e6151a
child 5 3b17fc5c9564
equal deleted inserted replaced
-1:000000000000 0:164170e6151a
       
     1 /*
       
     2 * Copyright (c) 2000, 2004 Nokia Corporation and/or its subsidiary(-ies). 
       
     3 * All rights reserved.
       
     4 * This component and the accompanying materials are made available
       
     5 * under the terms of "Eclipse Public License v1.0"
       
     6 * which accompanies this distribution, and is available
       
     7 * at the URL "http://www.eclipse.org/legal/epl-v10.html".
       
     8 *
       
     9 * Initial Contributors:
       
    10 * Nokia Corporation - initial contribution.
       
    11 *
       
    12 * Contributors:
       
    13 *
       
    14 * Description:   This module contains the implementation of CCrTemplate class. 
       
    15 *
       
    16 */
       
    17 
       
    18 
       
    19 
       
    20 //  INCLUDE FILES
       
    21 #include "crdata.h"
       
    22 
       
    23 
       
    24 
       
    25 // -----------------------------------------------------------------------------
       
    26 // CCrData
       
    27 // Constructors
       
    28 // These functions construct CCrData object.
       
    29 // -----------------------------------------------------------------------------
       
    30 CCrData::CCrData()  : iIsFile(ETrue), iPointer(0), iBuffer(NULL)
       
    31     { 
       
    32     }
       
    33 
       
    34 CCrData::CCrData(TBool aIsFile) : iIsFile(aIsFile), iPointer(0), iBuffer(NULL), iPath(NULL)
       
    35     {
       
    36     }
       
    37 
       
    38 // -----------------------------------------------------------------------------
       
    39 // CCrData
       
    40 // Destructor
       
    41 // This function destructs CCrData object.
       
    42 // -----------------------------------------------------------------------------
       
    43 CCrData::~CCrData()
       
    44     {
       
    45     if (iBuffer != NULL)
       
    46         {
       
    47         delete iBuffer;
       
    48         }
       
    49     if (iPath != NULL)
       
    50         {
       
    51         delete iPath;
       
    52         }
       
    53     }
       
    54 
       
    55 // -----------------------------------------------------------------------------
       
    56 // CCrData::ConstructL
       
    57 // This function initializes CCrData object's member objects.
       
    58 // -----------------------------------------------------------------------------
       
    59 TAny CCrData::ConstructL()
       
    60     {
       
    61     if (iIsFile == EFalse)
       
    62         {
       
    63         iBuffer = HBufC8::NewL(0);
       
    64         iPath = HBufC::NewL(0);
       
    65         }
       
    66     }
       
    67 
       
    68 // -----------------------------------------------------------------------------
       
    69 // CCrData::NewLC
       
    70 // -----------------------------------------------------------------------------
       
    71 EXPORT_C CCrData* CCrData::NewLC(TBool aIsFile) // default value ETrue
       
    72     {
       
    73     CCrData* self = new CCrData(aIsFile);
       
    74     CleanupStack::PushL(self);
       
    75 
       
    76     self->ConstructL();
       
    77 
       
    78     return self; 
       
    79     }
       
    80 
       
    81 // -----------------------------------------------------------------------------
       
    82 // CCrData::NewL
       
    83 // -----------------------------------------------------------------------------
       
    84 EXPORT_C CCrData* CCrData::NewL(TBool aIsFile) // default value ETrue
       
    85     {
       
    86     CCrData* self = NewLC(aIsFile);
       
    87     CleanupStack::Pop();
       
    88 
       
    89     return self; 
       
    90     }
       
    91 
       
    92 // -----------------------------------------------------------------------------
       
    93 // CCrData::Open
       
    94 // Opens an existing file for reading or writing. If the file 
       
    95 // does not already exist, an error is returned.
       
    96 // Parameters:    aFs The file server session
       
    97 //                aName The name of the file. Any path 
       
    98 //                      components which are not specified here will be 
       
    99 //                      taken from the session path.
       
   100 //                aFileMode The mode in which the file is opened. 
       
   101 //                          For more information see EFileShareExclusive and 
       
   102 //                          other file sharing and access modes.
       
   103 // Return Values:  TInt KErrNone if successful, otherwise
       
   104 //                 another of the system-wide error codes.
       
   105 // -----------------------------------------------------------------------------
       
   106 EXPORT_C TInt CCrData::Open(RFs& aFs,const TDesC& aName,TUint aFileMode)
       
   107     {
       
   108     if (iIsFile) // file
       
   109         {
       
   110         TInt result = iFile.Open(aFs, aName, aFileMode);
       
   111         SetPathL(aName);
       
   112         return result;
       
   113         }
       
   114     else // Buffer
       
   115         {
       
   116         return KErrNotSupported; 
       
   117         }
       
   118     
       
   119     }
       
   120 
       
   121 EXPORT_C TInt CCrData::Open(RFs& aFs,TUint aFileMode)
       
   122     {
       
   123     TInt result = KErrNone;
       
   124     if (iIsFile) // file
       
   125         {
       
   126         if (iPath != NULL)
       
   127             {
       
   128             result = iFile.Open(aFs, *iPath, aFileMode);
       
   129             }
       
   130         else
       
   131             {
       
   132             result = KErrPathNotFound;
       
   133             }
       
   134         }
       
   135     else // Buffer
       
   136         {
       
   137         result = KErrNotSupported; 
       
   138         }
       
   139     return result;
       
   140     }
       
   141 
       
   142 
       
   143 // -----------------------------------------------------------------------------
       
   144 // CCrData::Replace
       
   145 // Replaces a file. If there is an existing file with the same
       
   146 // name, this function will overwrite it. If the file does not 
       
   147 // already exist, this function creates it.
       
   148 // Parameters:    aFs The file server session
       
   149 //                aName The name of the file. 
       
   150 //                      Any path components which are not specified 
       
   151 //                      here will be taken from the session path.
       
   152 //                 aFileMode The mode in which the file  is opened. 
       
   153 //                           For more information see EFileShareExclusive and 
       
   154 //                           other file sharing and access modes. Note that the 
       
   155 //                           access mode is set to 
       
   156 //                           EFileWrite whatever the caller specifies.
       
   157 // Return Values:  KErrNone if successful, otherwise
       
   158 //                 another of the system-wide error codes.
       
   159 // -----------------------------------------------------------------------------
       
   160 EXPORT_C TInt CCrData::Replace(RFs& aFs, const TDesC& aName, TUint aFileMode)
       
   161     {
       
   162     if (iIsFile) // File
       
   163         {
       
   164         TInt result = iFile.Replace(aFs, aName, aFileMode);
       
   165         return result;
       
   166         }
       
   167     else // Buffer
       
   168         {
       
   169         return KErrNotSupported; 
       
   170         }  
       
   171     }
       
   172 
       
   173 // -----------------------------------------------------------------------------
       
   174 // CCrData::Close
       
   175 // Closes the file. Any open files are closed when the file server 
       
   176 // session is closed. Close() is guaranteed to return, and provides 
       
   177 // no indication of whether it completed successfully or not. When 
       
   178 // closing a file that you have written to, you should ensure that
       
   179 // data is committed to the file by invoking RFile::Flush() before 
       
   180 // closing. If Flush() completes successfully, Close() is essentially 
       
   181 // a no-operation.
       
   182 // -----------------------------------------------------------------------------
       
   183 EXPORT_C void CCrData::Close()
       
   184     {
       
   185     if (iIsFile) // File
       
   186         {
       
   187         iFile.Close();
       
   188         return; 
       
   189         }
       
   190     else // Buffer
       
   191         {
       
   192         return; 
       
   193         }      
       
   194     }
       
   195 
       
   196 // -----------------------------------------------------------------------------
       
   197 // CCrData::Read
       
   198 // Reading transfers data from a file (or a buffer) to a descriptor.
       
   199 // If CCrData object is created to be a file function only call 
       
   200 // RFile::Read(.), but if CCrData is buffer, function makes same things
       
   201 // to buffer as RFile::Read make to file. (See EPOC documentation)
       
   202 // -----------------------------------------------------------------------------
       
   203 EXPORT_C TInt CCrData::Read(TDes8& aDes)
       
   204     {
       
   205     TInt err = KErrNone;
       
   206 
       
   207     if (!iIsFile)                            // CCrData is buf 
       
   208         {       
       
   209         if (aDes.MaxLength() < iBuffer->Length())
       
   210             {
       
   211             err = KErrOverflow;
       
   212             }
       
   213         else 
       
   214             {
       
   215             aDes.Copy(*iBuffer);            // Copy iBuffer to aDes
       
   216             iPointer = aDes.Length();       // Set pointer to end of buf
       
   217             }           
       
   218         }
       
   219     else                                    // else it is file
       
   220         {  
       
   221         err = iFile.Read(aDes); 
       
   222         }
       
   223     return err;                             // error code
       
   224     }
       
   225 
       
   226 
       
   227 EXPORT_C TInt CCrData::Read(TDes8& aDes,TInt aLength)
       
   228     {
       
   229     TInt err = KErrNone;
       
   230     TInt i;
       
   231 
       
   232     if (!iIsFile)                           // CCrData is buffer
       
   233         {       
       
   234         if ((aLength < NULL) || (aLength > aDes.MaxLength())) 
       
   235             {
       
   236             err = KErrGeneral;
       
   237             }
       
   238         else 
       
   239             {
       
   240             aDes.Zero();
       
   241             for (i = iPointer; (i < (aLength + iPointer)) &&
       
   242                 (i < iBuffer->Length()); i++)
       
   243                 {
       
   244                 aDes.Append((*iBuffer)[i]);        // append data to aDes
       
   245                 }
       
   246             iPointer = i;                   // set pointer to next byte
       
   247             }
       
   248         }
       
   249     else                                    // else CCrData is file
       
   250         {
       
   251         err = iFile.Read(aDes, aLength);
       
   252         }
       
   253 
       
   254     return err;
       
   255     }
       
   256 
       
   257 
       
   258 EXPORT_C TInt CCrData::Read(TInt aPos,TDes8& aDes,TInt aLength)
       
   259     {
       
   260     TInt i;
       
   261     TInt err = KErrNone;
       
   262 
       
   263     if (!iIsFile)                           // CCrDAta is buffer 
       
   264         {       
       
   265         if (aLength < NULL)                 // aLength must be positive 
       
   266             {   
       
   267             err = KErrArgument;
       
   268             }
       
   269         else if (aLength > aDes.MaxLength()) // if buffer is too small 
       
   270             {
       
   271             err = KErrOverflow;            
       
   272             }
       
   273         else 
       
   274             {
       
   275             aDes.Delete(0, aDes.Length());  // delete old string
       
   276             TPtr8 ptr = iBuffer->Des();         
       
   277         
       
   278             for (i = aPos; (i < (aLength + aPos)) &&
       
   279                 (i < iBuffer->Length()); i++)
       
   280                 {
       
   281                 aDes.Append(ptr[i]);
       
   282                 }
       
   283             iPointer  = i;                  // set pointer to end of read       
       
   284             }
       
   285         }
       
   286     else                                    // CCrData is file
       
   287         { 
       
   288         err = iFile.Read(aPos, aDes, aLength);
       
   289         }
       
   290 
       
   291     return err;
       
   292     }
       
   293 
       
   294 // -----------------------------------------------------------------------------
       
   295 // CCrData::Write
       
   296 // Writing transfers data from a descriptor to a buffer or file.
       
   297 // If CCrData object is created to be a file function only call 
       
   298 // RFile::Write(.), but if CCrData is buffer, function makes same things
       
   299 // to buffer as RFile::Write make to file. After Write(..) iPointer is moved
       
   300 // to end of the buffer. 
       
   301 // -----------------------------------------------------------------------------
       
   302 EXPORT_C TInt CCrData::Write(const TDesC8& aDes)
       
   303     { 
       
   304     TInt err = KErrNone;
       
   305     if (!iIsFile)                           // buf
       
   306         {
       
   307         err = Write(aDes, aDes.Length()); 
       
   308         }
       
   309 
       
   310      else                                    // file
       
   311         {
       
   312         err = iFile.Write(aDes);
       
   313         }
       
   314 
       
   315     return err;
       
   316     }
       
   317 
       
   318 EXPORT_C TInt CCrData::Write(const TDesC8& aDes, TInt aLength)
       
   319     { 
       
   320     TInt err = KErrNone;
       
   321     if (aLength < NULL)                     // aLen must be positive 
       
   322         {                                   
       
   323         err = KErrArgument;           
       
   324         }
       
   325      
       
   326     if (!iIsFile)                           // buf
       
   327         { 
       
   328         TInt bufferLength = iBuffer->Length();
       
   329         TInt neededBufferLength = iPointer + aLength;
       
   330         if (neededBufferLength > bufferLength) // Reallocation is needed
       
   331             {
       
   332             TRAP(err, iBuffer = iBuffer->ReAllocL(neededBufferLength)); 
       
   333             if (err != KErrNone)
       
   334                     {
       
   335                     return KErrNoMemory;
       
   336                     }
       
   337             }
       
   338             
       
   339         TPtr8 ptr = iBuffer->Des();             
       
   340         // append new data, ONLY the amount of aLength
       
   341         if (aLength == aDes.Length())
       
   342             {
       
   343             ptr.Append(aDes); 
       
   344             }
       
   345         else // aLength < a.Des.Length()
       
   346             {
       
   347             ptr.Append(aDes.Left(aLength));
       
   348             }
       
   349         // iPointer is moved to the end of the buffer.        
       
   350         iPointer = iBuffer->Length();
       
   351         }
       
   352     else                                    // file
       
   353         {
       
   354         err = iFile.Write(aDes);
       
   355         }
       
   356 
       
   357     return err;
       
   358     }
       
   359 
       
   360 // -----------------------------------------------------------------------------
       
   361 // CCrData::Zero
       
   362 // -----------------------------------------------------------------------------
       
   363 EXPORT_C TInt CCrData::Zero(const TInt aPos, const TInt aLength)
       
   364     {
       
   365 	TPtr8 ptr = iBuffer->Des();
       
   366 	if (ptr.Length() < aPos + aLength)
       
   367 	    {
       
   368 		// Out of bounds
       
   369 		return KErrArgument;
       
   370 	    }
       
   371 	for (TInt i = 0; i < aLength; i++)
       
   372 	    {
       
   373 		ptr[aPos+i] = 0;
       
   374 	    }
       
   375 	return KErrNone;
       
   376     }
       
   377 
       
   378 // -----------------------------------------------------------------------------
       
   379 // CCrData::Seek
       
   380 // If CCrData object is created to be a file function only call 
       
   381 // RFile::Seek(.), but if CCrData is buffer, function makes same things
       
   382 // to buffer as RFile::Seek make to file. (See EPOC documentation)
       
   383 // -----------------------------------------------------------------------------
       
   384 EXPORT_C TInt CCrData::Seek(TSeek aMode,TInt& aPos)
       
   385     {
       
   386     TInt err = KErrNone;
       
   387 
       
   388     if (aPos < NULL)                        // aPos must be positive
       
   389         {
       
   390         err = KErrArgument;
       
   391         }
       
   392 
       
   393     else if (!iIsFile)                      // CCrData is buffer
       
   394         {
       
   395         switch (aMode)
       
   396             {
       
   397             case (ESeekCurrent):            // this place
       
   398                 {
       
   399                 iPointer += aPos;              
       
   400                 break;
       
   401                 }
       
   402             case (ESeekStart):              // from start of buf
       
   403                 {                
       
   404                 iPointer = aPos;   
       
   405                 break;
       
   406                 }
       
   407             case (ESeekEnd):                // from end of buf 
       
   408                 {
       
   409                 iPointer = iBuffer->Length() - aPos;
       
   410                 break;
       
   411                 }
       
   412             default:
       
   413                 {
       
   414                 break;                      // ESeekAddress
       
   415                 }
       
   416             }
       
   417     
       
   418             if (iPointer < NULL)            // iPointer must be positive
       
   419                 {
       
   420                 iPointer = NULL;            // set pointer to start of buf
       
   421                 }
       
   422 
       
   423             else if (iPointer > iBuffer->Length())
       
   424                 {
       
   425                 iPointer = iBuffer->Length();   // set pointer to end of buf
       
   426                 }
       
   427           
       
   428             aPos = iPointer;
       
   429             
       
   430         }
       
   431 
       
   432     else                                    // CCrData is file
       
   433         {       
       
   434         err = iFile.Seek(aMode, aPos);
       
   435         }
       
   436     
       
   437     return err;
       
   438     }
       
   439 
       
   440 // -----------------------------------------------------------------------------
       
   441 // CCrData::Size
       
   442 // Reading the size of data.
       
   443 // If CCrData is a buffer, returns length of the buffer. Otherwise 
       
   444 // calls RFile::Size( TInt ).
       
   445 // -----------------------------------------------------------------------------
       
   446 EXPORT_C TInt CCrData::Size(TInt& aSize)
       
   447     {
       
   448     TInt err = KErrNone;
       
   449 
       
   450     if (!iIsFile)                            // CCrData is buf 
       
   451         {       
       
   452         aSize = iBuffer->Length();
       
   453         }
       
   454     else                                    // else it is file
       
   455         {  
       
   456         err = iFile.Size(aSize); 
       
   457         }
       
   458     return err;                             // error code
       
   459     }
       
   460 
       
   461 
       
   462 // -----------------------------------------------------------------------------
       
   463 // CCrData::LineL
       
   464 // LineL sets data from the current place, where iPointer is, till next new
       
   465 // line character to aLine. 
       
   466 // iPointer moves to the following item from the found new line character.
       
   467 // If no new line character is found, aLine is the rest of the data and 
       
   468 // iPointer is moved to the end of the iBuffer or the iFile.
       
   469 // Return Values:  KErrNone if everything succeeds
       
   470 //                 KErrNoMemory if aLine is not length enough
       
   471 //                 KErrEof if iPointer is at the end of file.
       
   472 // -----------------------------------------------------------------------------
       
   473 EXPORT_C TInt CCrData::LineL(TDes8& aLine)
       
   474     {
       
   475     TBufC8<2> newLine(KNewLine);  // New line
       
   476 
       
   477     // Area to find the new line
       
   478     TInt size = 0;
       
   479     Size(size);
       
   480     TInt currentPlace = iPointer;
       
   481     TInt neededLengthToFind = size - currentPlace;
       
   482 
       
   483     if(neededLengthToFind == 0) // If the area is empty
       
   484         {
       
   485         return KErrEof;
       
   486         }
       
   487     TInt newLinePlace = 0;
       
   488 
       
   489     if (!iIsFile) // CCrData is a buffer.
       
   490         {
       
   491         TPtrC8 temp = iBuffer->Mid(currentPlace, neededLengthToFind);
       
   492 
       
   493         // Find next new line.
       
   494         newLinePlace = temp.Find(newLine);
       
   495         // If not found, return the whole area.
       
   496         if (newLinePlace == KErrNotFound)
       
   497             {
       
   498             newLinePlace = neededLengthToFind;
       
   499             }
       
   500         else
       
   501             {
       
   502             newLinePlace = newLinePlace + newLine.Length();
       
   503             }
       
   504 
       
   505         
       
   506         if (aLine.MaxLength() < newLinePlace)
       
   507             {
       
   508             return KErrNoMemory;
       
   509             }
       
   510 
       
   511         // Put the line to aLine
       
   512         aLine = temp.Left(newLinePlace);
       
   513         }
       
   514 
       
   515     else // CCrData is a file.
       
   516         {
       
   517 
       
   518         HBufC8* tempBuf = HBufC8::NewL(neededLengthToFind);
       
   519         TDes8 tempDes = tempBuf->Des();
       
   520 
       
   521         // Read data to a buffer from iPointer to end of file
       
   522         Read(currentPlace, tempDes, neededLengthToFind);
       
   523 
       
   524         // Find the next new line character
       
   525         newLinePlace = tempDes.Find(newLine);
       
   526         // If not found, return the whole area.
       
   527         if (newLinePlace == KErrNotFound)
       
   528             {
       
   529             newLinePlace = neededLengthToFind;
       
   530             }
       
   531         else
       
   532             {
       
   533             newLinePlace = newLinePlace + newLine.Length();
       
   534             }
       
   535 
       
   536         // Is there enough memory
       
   537         if (aLine.MaxLength() < newLinePlace)
       
   538             {
       
   539             delete tempBuf;
       
   540             tempBuf = NULL;
       
   541             return KErrNoMemory;
       
   542             }
       
   543 
       
   544         // Put data to aLine
       
   545         aLine = tempBuf->Left(newLinePlace);
       
   546         
       
   547         delete tempBuf;
       
   548         tempBuf = NULL;
       
   549         }
       
   550 
       
   551     // Move iPointer to next place from newLine.
       
   552     iPointer = currentPlace + newLinePlace;
       
   553 
       
   554     return KErrNone;
       
   555             
       
   556     }
       
   557 
       
   558 // -----------------------------------------------------------------------------
       
   559 // CCrData::Path
       
   560 // -----------------------------------------------------------------------------
       
   561 EXPORT_C HBufC* CCrData::Path()
       
   562     {
       
   563     return iPath;
       
   564     }
       
   565 
       
   566 // -----------------------------------------------------------------------------
       
   567 // CCrData::SetPathL
       
   568 // -----------------------------------------------------------------------------
       
   569 EXPORT_C TAny CCrData::SetPathL(const TDesC& aPath )
       
   570     {
       
   571     delete iPath;
       
   572     iPath = NULL;
       
   573     iPath = HBufC::NewL( aPath.Length() );
       
   574     TPtr pointer = iPath->Des();
       
   575     pointer.Copy( aPath );
       
   576     }
       
   577 
       
   578 // -----------------------------------------------------------------------------
       
   579 // CCrData::IsFile
       
   580 // -----------------------------------------------------------------------------
       
   581 EXPORT_C TBool CCrData::IsFile()
       
   582     {
       
   583     return iIsFile;
       
   584     }
       
   585 
       
   586 // -----------------------------------------------------------------------------
       
   587 // CCrData::Delete
       
   588 // -----------------------------------------------------------------------------
       
   589 EXPORT_C TInt CCrData::Delete( TInt aPos, TInt aLength )
       
   590     {
       
   591     if (!iIsFile)                            // CCrData is buf 
       
   592         {       
       
   593         if(aPos >= 0)
       
   594             {
       
   595             TPtr8 pointer = iBuffer->Des();
       
   596             pointer.Delete(aPos, aLength);
       
   597             }
       
   598         else
       
   599             {
       
   600             return KErrArgument;
       
   601             }
       
   602         }
       
   603     else                                    // else it is file
       
   604         {  
       
   605         return KErrNotSupported;
       
   606         }
       
   607     return KErrNone;
       
   608     }
       
   609 
       
   610 // -----------------------------------------------------------------------------
       
   611 // CCrDataSet
       
   612 // -----------------------------------------------------------------------------
       
   613 
       
   614 
       
   615 // -----------------------------------------------------------------------------
       
   616 // CCrDataSet
       
   617 // Constructor.
       
   618 // This function constructs CCrDataSet object.
       
   619 // -----------------------------------------------------------------------------
       
   620 CCrDataSet::CCrDataSet(TInt aGranularity)
       
   621     : CArrayPtrSeg<CCrData>(aGranularity)
       
   622     {
       
   623     }
       
   624 
       
   625 // -----------------------------------------------------------------------------
       
   626 // CCrDataSet
       
   627 // Destructor.
       
   628 // This function destructs CCrDataSet object.
       
   629 // -----------------------------------------------------------------------------
       
   630 CCrDataSet::~CCrDataSet()
       
   631     {
       
   632     ResetAndDestroy();
       
   633     }
       
   634 
       
   635 // -----------------------------------------------------------------------------
       
   636 // CCrDataSet::ConstructL
       
   637 // This function initializes CCrDataSet object's member objects.
       
   638 // -----------------------------------------------------------------------------
       
   639 TAny CCrDataSet::ConstructL()
       
   640     {
       
   641     }
       
   642 
       
   643 // -----------------------------------------------------------------------------
       
   644 // CCrDataSet::NewLC
       
   645 // -----------------------------------------------------------------------------
       
   646 EXPORT_C CCrDataSet* CCrDataSet::NewLC(TInt aGranularity)
       
   647     {
       
   648     CCrDataSet* self = new CCrDataSet(aGranularity);
       
   649     CleanupStack::PushL(self);
       
   650 
       
   651     self->ConstructL();
       
   652 
       
   653     return self; 
       
   654     }
       
   655 
       
   656 // -----------------------------------------------------------------------------
       
   657 // CCrDataSet::NewL
       
   658 // -----------------------------------------------------------------------------
       
   659 EXPORT_C CCrDataSet* CCrDataSet::NewL(TInt aGranularity)
       
   660     {
       
   661     CCrDataSet* self = NewLC(aGranularity);
       
   662     CleanupStack::Pop();
       
   663 
       
   664     return self; 
       
   665     }
       
   666 
       
   667 // End of file
       
   668