hti/PC_Tools/DataGateway/SRC/util.cpp
branchRCL_3
changeset 59 8ad140f3dd41
parent 0 a03f92240627
equal deleted inserted replaced
49:7fdc9a71d314 59:8ad140f3dd41
       
     1 /*
       
     2 * Copyright (c) 2009 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:
       
    15 *   This module contains the implementation of Util class member functions. 
       
    16 *   Util class provides static utility functions for logging and string
       
    17 *	manipulation.
       
    18 */
       
    19 
       
    20 // INCLUDE FILES
       
    21 #include "util.h"
       
    22 
       
    23 // command line parameters
       
    24 extern map<string, string> g_parameters;
       
    25 
       
    26 //**********************************************************************************
       
    27 // Class Util
       
    28 //
       
    29 // Provides static utility functions for logging and string handling.
       
    30 //**********************************************************************************
       
    31 
       
    32 // The default verbose level for Util output is error
       
    33 Util::VerboseLevel Util::iVerbose(Util::error);
       
    34 
       
    35 // The default output is without timestamp
       
    36 int Util::iTimestamp(0);
       
    37 
       
    38 #ifdef ENABLE_LOG_SYNC 
       
    39 	Mutex Util::iCritical;
       
    40 #endif
       
    41 
       
    42 /*-------------------------------------------------------------------------------
       
    43 
       
    44     Class: Util
       
    45 
       
    46     Method: SetVerboseLevel
       
    47 
       
    48     Description: Sets new verbose level by string:
       
    49 	           
       
    50 				 "none"
       
    51 				 "result"
       
    52 				 "error"
       
    53 				 "info"
       
    54 				 "debug"
       
    55     
       
    56     Parameters: const string& aVerbose : in: Verbose level as string
       
    57 
       
    58     Return Values: None
       
    59 
       
    60     Errors/Exceptions: None
       
    61 
       
    62 -------------------------------------------------------------------------------*/
       
    63 void Util::SetVerboseLevel(const string& aVerbose)
       
    64 {
       
    65 	string verbose(aVerbose);
       
    66 	ToLower(verbose);
       
    67 	if (verbose.compare("result") == 0)
       
    68 	{
       
    69 		iVerbose = result;
       
    70 	}
       
    71 	else if (verbose.compare("error") == 0)
       
    72 	{
       
    73 		iVerbose = error;
       
    74 	}
       
    75 	else if (verbose.compare("info") == 0)
       
    76 	{
       
    77 		iVerbose = info;
       
    78 	}
       
    79 	else if (verbose.compare("debug") == 0)
       
    80 	{
       
    81 		iVerbose = debug;
       
    82 	}
       
    83 	else
       
    84 	{
       
    85 	 	iVerbose = none;
       
    86 	}
       
    87 }
       
    88 
       
    89 /*-------------------------------------------------------------------------------
       
    90 
       
    91     Class: Util
       
    92 
       
    93     Method: SetVerboseLevel
       
    94 
       
    95     Description: Sets new verbose level by enumeration.
       
    96     
       
    97     Parameters: const VerboseLevel aVerbose : in: Verbose level
       
    98 
       
    99     Return Values: None
       
   100 
       
   101     Errors/Exceptions: None
       
   102 
       
   103 -------------------------------------------------------------------------------*/
       
   104 void Util::SetVerboseLevel(const VerboseLevel aVerbose)
       
   105 {
       
   106 	iVerbose = aVerbose;
       
   107 }
       
   108 
       
   109 /*-------------------------------------------------------------------------------
       
   110 
       
   111     Class: Util
       
   112 
       
   113     Method: GetVerboseLevel
       
   114 
       
   115     Description: Returns current verbose level.
       
   116     
       
   117     Parameters: None
       
   118 
       
   119     Return Values: VerboseLevel : Current verbose level
       
   120 
       
   121     Errors/Exceptions: None
       
   122 
       
   123 -------------------------------------------------------------------------------*/
       
   124 Util::VerboseLevel Util::GetVerboseLevel()
       
   125 {
       
   126 	return iVerbose;
       
   127 }
       
   128 
       
   129 /*-------------------------------------------------------------------------------
       
   130 
       
   131     Class: Util
       
   132 
       
   133     Method: SetTimestamp
       
   134 
       
   135     Description: Enables/disables timestamp from output by string "yes" or "no".
       
   136     
       
   137     Parameters: const string& aTimestamp : in : "yes" to enable timestamp, "no" to
       
   138 	                                            disable.
       
   139 
       
   140     Return Values: None
       
   141 
       
   142     Errors/Exceptions: None
       
   143 
       
   144 -------------------------------------------------------------------------------*/
       
   145 void Util::SetTimestamp(const string& aTimestamp)
       
   146 {
       
   147 	string timestamp(aTimestamp);
       
   148 	ToLower(timestamp);
       
   149 	if (timestamp.compare("true") == 0)
       
   150 	{
       
   151 		SetTimestamp(1);
       
   152 	}
       
   153 	else
       
   154 	{
       
   155 		SetTimestamp(0);
       
   156 	}
       
   157 }
       
   158 
       
   159 /*-------------------------------------------------------------------------------
       
   160 
       
   161     Class: Util
       
   162 
       
   163     Method: SetTimestamp
       
   164 
       
   165     Description: Enables/disables timestamp from output. 0 is disabled.
       
   166     
       
   167     Parameters: const int aTimestamp : in : Enable/disable time stamp
       
   168 
       
   169     Return Values: None
       
   170 
       
   171     Errors/Exceptions: None
       
   172 
       
   173 -------------------------------------------------------------------------------*/
       
   174 void Util::SetTimestamp(const int aTimestamp)
       
   175 {
       
   176 	iTimestamp = aTimestamp;
       
   177 }
       
   178 
       
   179 /*-------------------------------------------------------------------------------
       
   180 
       
   181     Class: Util
       
   182 
       
   183     Method: ParseCmdLine
       
   184 
       
   185     Description: Parses command line parameters to map. The map which is passed
       
   186 	             as argument must contain all valid keys (command line swicthes).
       
   187 				 Function adds values to map to all keys found from command line.
       
   188 				 The command line switch is form <key>=<value>. Default values for
       
   189 				 keys can be specified.
       
   190     
       
   191     Parameters: const int aArgc           : in     : The number of arguments
       
   192 	            char **aArgs              : in     : Pointer array to arguments
       
   193 				map<string, string>& aMap : in/out : Map containing possible command
       
   194 				                                     line switches (keys). Values of
       
   195 													 switches are returned as values.
       
   196 
       
   197     Return Values: None
       
   198 
       
   199     Errors/Exceptions: UtilError
       
   200 
       
   201 -------------------------------------------------------------------------------*/
       
   202 void Util::ParseCmdLine(const int aArgc,
       
   203 						char **aArgs,
       
   204 						map<string, string>& aMap) throw (UtilError)
       
   205 {
       
   206 	for (int i = 1; i < aArgc; i++)
       
   207 	{
       
   208 		string p(aArgs[i]);
       
   209 		string v;
       
   210 		int idx = p.find_first_of("=");
       
   211 		if (idx != string::npos)
       
   212 		{
       
   213 			if ((idx + 1) == p.length())
       
   214 			{
       
   215 				string err("Parameter '" + p.substr(0, idx) + "' without value");
       
   216 				throw UtilError(err, ERR_UTIL_NO_PARAM_VALUE);
       
   217 			}
       
   218 			v = &aArgs[i][idx + 1];
       
   219 			p = p.substr(0, idx);
       
   220 		}
       
   221 		else
       
   222 		{
       
   223 			v = "";
       
   224 		}
       
   225 		aMap[p] = v;
       
   226 	}
       
   227 }
       
   228 
       
   229 /*-------------------------------------------------------------------------------
       
   230 
       
   231     Class: Util
       
   232 
       
   233     Method: GetValue
       
   234 
       
   235     Description: Returns value by key from a map.
       
   236     
       
   237     Parameters: const string& aKey        : in  : Key to value 
       
   238 				map<string, string>& aMap : in  : Map containing valid keys
       
   239 				string& aValue            : out : Value of key
       
   240 
       
   241     Return Values: None
       
   242 
       
   243     Errors/Exceptions: UtilError
       
   244 
       
   245 -------------------------------------------------------------------------------*/
       
   246 void Util::GetValue(const string& aKey,
       
   247 					const map<string,
       
   248 					string>& aMap, string& aValue) throw (UtilError)
       
   249 {
       
   250 	map<string, string>::const_iterator i = aMap.find(aKey);
       
   251 	if (i != aMap.end())
       
   252 	{
       
   253 		aValue = i->second;
       
   254 	}
       
   255 	else
       
   256 	{
       
   257 		string err("Unknown parameter '" + aKey + "'");
       
   258 		throw UtilError(err, ERR_UTIL_UNKNOWN_PARAM);
       
   259 	}
       
   260 }
       
   261 
       
   262 /*-------------------------------------------------------------------------------
       
   263 
       
   264     Class: Util
       
   265 
       
   266     Method: ReadProperties
       
   267 
       
   268     Description: Reads properties from a file to a map. Line beginning with
       
   269 	             hash character ('#') are considered as comments. The form
       
   270 				 of prooerties in file is <property> = <value>.
       
   271     
       
   272     Parameters: const string& aFilename   : in  : Filename where properties are read
       
   273 	            map<string, string>& aMap : out : Map where property values are put
       
   274 
       
   275     Return Values: None
       
   276 
       
   277     Errors/Exceptions: UtilError
       
   278 
       
   279 -------------------------------------------------------------------------------*/
       
   280 void Util::ReadProperties(const string& aFilename,
       
   281 						  map<string, string>& aMap) throw (UtilError)
       
   282 {
       
   283 	ifstream in(aFilename.c_str());
       
   284 	if (!in)
       
   285 	{
       
   286 		string err("Cannot open properties file '");
       
   287 		err += aFilename + "'.";
       
   288 		throw UtilError(err, ERR_UTIL_PROPERTIES_NOT_FOUND);
       
   289 	}
       
   290 	while (in)
       
   291 	{
       
   292 		char tmp[256];
       
   293 		in.getline(tmp, 256);
       
   294 		string line(tmp);
       
   295 		if (line.length() == 0 || line.find_first_of("#") == 0)
       
   296 		{
       
   297 			continue;
       
   298 		}
       
   299 		int idx = line.find_first_of('=');
       
   300 		string v, p;
       
   301 		if (idx != string::npos)
       
   302 		{
       
   303 			p = line.substr(0, idx);
       
   304 			int r = p.find(" ");
       
   305 			while (r != string::npos)
       
   306 			{
       
   307 				p.replace(r, 1, "");
       
   308 				r = p.find(" ");
       
   309 			}
       
   310 			v = line.substr(idx + 1);
       
   311 			string::iterator i = v.begin();
       
   312 			int spaces = 0;
       
   313 			while (i != v.end() && *i == ' ')
       
   314 			{
       
   315 				++spaces;
       
   316 				++i;
       
   317 			}
       
   318 			v.erase(0, spaces);
       
   319 			if (v.length() == 0)
       
   320 			{
       
   321 				string err = "Value not specified for parameter '";
       
   322 				err += line.substr(0, idx) + "'.";
       
   323 				throw UtilError(err, ERR_UTIL_NO_PROPERTY_VALUE);			
       
   324 			}
       
   325 		}
       
   326 		else 
       
   327 		{
       
   328 			p = line;
       
   329 			v = "";
       
   330 		}
       
   331 		aMap[p] = v;
       
   332 	}
       
   333 }
       
   334 
       
   335 /*-------------------------------------------------------------------------------
       
   336 
       
   337     Class: Util
       
   338 
       
   339     Method: Error
       
   340 
       
   341     Description: Prints message to output in error level.
       
   342     
       
   343     Parameters: const string& aMsg : in : Error message to output
       
   344 
       
   345     Return Values: None
       
   346 
       
   347     Errors/Exceptions: None
       
   348 
       
   349 -------------------------------------------------------------------------------*/
       
   350 void Util::Error(const string& aMsg)
       
   351 {
       
   352 	Print(cerr, aMsg, error);
       
   353 }
       
   354 
       
   355 /*-------------------------------------------------------------------------------
       
   356 
       
   357     Class: Util
       
   358 
       
   359     Method: Error
       
   360 
       
   361     Description: Prints message to output in error level with error code number.
       
   362     
       
   363     Parameters: const string& aMsg : in : Error message to output
       
   364 				const long aCode   : in : Error code to output
       
   365 
       
   366     Return Values: None
       
   367 
       
   368     Errors/Exceptions: None
       
   369 
       
   370 -------------------------------------------------------------------------------*/
       
   371 void Util::Error(const string& aMsg, const long aCode)
       
   372 {
       
   373 	Print(cerr, aMsg, error, aCode);
       
   374 }
       
   375 
       
   376 /*-------------------------------------------------------------------------------
       
   377 
       
   378     Class: Util
       
   379 
       
   380     Method: Log
       
   381 
       
   382     Description: Prints message to output in result level.
       
   383     
       
   384     Parameters: const string& aMsg : in : Result message to output
       
   385 
       
   386     Return Values: None
       
   387 
       
   388     Errors/Exceptions: None
       
   389 
       
   390 -------------------------------------------------------------------------------*/
       
   391 void Util::Log(const string& aMsg)
       
   392 {
       
   393 	Print(cout, aMsg, result);
       
   394 }
       
   395 
       
   396 /*-------------------------------------------------------------------------------
       
   397 
       
   398     Class: Util
       
   399 
       
   400     Method: Info
       
   401 
       
   402     Description: Prints message to output in info level.
       
   403     
       
   404     Parameters: const string& aMsg : in : Info message to output
       
   405 
       
   406     Return Values: None
       
   407 
       
   408     Errors/Exceptions: None
       
   409 
       
   410 -------------------------------------------------------------------------------*/
       
   411 void Util::Info(const string& aMsg)
       
   412 {
       
   413 	Print(cout, aMsg, info);
       
   414 }
       
   415 
       
   416 /*-------------------------------------------------------------------------------
       
   417 
       
   418     Class: Util
       
   419 
       
   420     Method: Debug
       
   421 
       
   422     Description: Prints message to output in debug level.
       
   423     
       
   424     Parameters: const string& aMsg : in : Debug message to output
       
   425 
       
   426     Return Values: None
       
   427 
       
   428     Errors/Exceptions: None
       
   429 
       
   430 -------------------------------------------------------------------------------*/
       
   431 void Util::Debug(const string& aMsg)
       
   432 {
       
   433 	Print(cout, aMsg, debug);
       
   434 }
       
   435 
       
   436 /*-------------------------------------------------------------------------------
       
   437 
       
   438     Class: Util
       
   439 
       
   440     Method: Print
       
   441 
       
   442     Description: Prints output to a stream.
       
   443     
       
   444     Parameters: ostream& out              : in : Stream where to write
       
   445                 const string& aMsg        : in : Message to output
       
   446                 const VerboseLevel aLevel : in : Verbose level of message
       
   447 				const long aCode          : in : Possible error code if != 0
       
   448 
       
   449     Return Values: None
       
   450 
       
   451     Errors/Exceptions: None
       
   452 
       
   453 -------------------------------------------------------------------------------*/
       
   454 void Util::Print(ostream& out,
       
   455 				 const string& aMsg,
       
   456 				 const VerboseLevel aLevel,
       
   457 				 const long aCode)
       
   458 {
       
   459 #ifdef ENABLE_LOG_SYNC 
       
   460 	iCritical.Lock();
       
   461 #endif
       
   462 
       
   463 	if (aLevel <= iVerbose)
       
   464 	{
       
   465 		if (iTimestamp)
       
   466 		{
       
   467 			char timestamp[128];
       
   468 			_strdate(timestamp);
       
   469 			char tmp[16];
       
   470 			_strtime(tmp);
       
   471 			strcat(timestamp, " ");
       
   472 			strcat(timestamp, tmp);
       
   473 			struct _timeb tb;
       
   474 			_ftime(&tb);
       
   475 			sprintf(tmp, ".%03d", tb.millitm);
       
   476 			strcat(timestamp, tmp);
       
   477 			out << "[" << timestamp << "] ";
       
   478 		}
       
   479 
       
   480 		out << aMsg;
       
   481 		//if (aLevel == debug)
       
   482 		{
       
   483 			OutputDebugString(aMsg.c_str());
       
   484 			OutputDebugString("\r\n");
       
   485 		}
       
   486 		if (aCode != 0)
       
   487 		{
       
   488 			out.setf(ios_base::hex, ios_base::basefield);
       
   489 			out << " (error: 0x" << aCode << ")";
       
   490 		}
       
   491 		out << endl;
       
   492 	}
       
   493 #ifdef ENABLE_LOG_SYNC 
       
   494 	iCritical.Unlock();
       
   495 #endif
       
   496 }
       
   497 
       
   498 /*-------------------------------------------------------------------------------
       
   499 
       
   500     Class: Util
       
   501 
       
   502     Method: ToLower
       
   503 
       
   504     Description: Converts string to lower case.
       
   505     
       
   506     Parameters: string& aString : in/out : String which is converted to lower case
       
   507 
       
   508     Return Values: string& : Reference to converted string.
       
   509 
       
   510     Errors/Exceptions: None
       
   511 
       
   512 -------------------------------------------------------------------------------*/
       
   513 string& Util::ToLower(string& aString)
       
   514 {
       
   515 	string::iterator p = aString.begin();
       
   516 	while (p != aString.end())
       
   517 	{
       
   518 		*p = tolower(*p);
       
   519 		++p;
       
   520 	}
       
   521 	return aString;
       
   522 }
       
   523 
       
   524 /*-------------------------------------------------------------------------------
       
   525 
       
   526     Class: Util
       
   527 
       
   528     Method: ToUpper
       
   529 
       
   530     Description: Converts string to upper case.
       
   531     
       
   532     Parameters: string& aString : in/out : String which is converted to upper case
       
   533 
       
   534     Return Values: string& : Reference to converted string.
       
   535 
       
   536     Errors/Exceptions: None
       
   537 
       
   538 -------------------------------------------------------------------------------*/
       
   539 string& Util::ToUpper(string& aString)
       
   540 {
       
   541 	string::iterator p = aString.begin();
       
   542 	while (p != aString.end())
       
   543 	{
       
   544 		*p = toupper(*p);
       
   545 		++p;
       
   546 	}
       
   547 	return aString;
       
   548 }
       
   549 
       
   550 /*-------------------------------------------------------------------------------
       
   551 
       
   552     Class: Util
       
   553 
       
   554     Method: Hex
       
   555 
       
   556     Description: Prints hex dump of char table to output.
       
   557     
       
   558     Parameters: const char aData[] : in : Data to be output as hex dump
       
   559 
       
   560     Return Values: None
       
   561 
       
   562     Errors/Exceptions: None
       
   563 
       
   564 -------------------------------------------------------------------------------*/
       
   565 void Util::Hex(const char aData[])
       
   566 {
       
   567 	string s(aData);
       
   568 	Hex(s);
       
   569 }
       
   570 
       
   571 /*-------------------------------------------------------------------------------
       
   572 
       
   573     Class: Util
       
   574 
       
   575     Method: Hex
       
   576 
       
   577     Description: Prints hex dump of string to output.
       
   578     
       
   579     Parameters: const string& aMsg : in : Data to be output as hex dump
       
   580 
       
   581     Return Values: None
       
   582 
       
   583     Errors/Exceptions: None
       
   584 
       
   585 -------------------------------------------------------------------------------*/
       
   586 void Util::Hex(const string& aMsg)
       
   587 {
       
   588 	Hex((const unsigned char *)aMsg.c_str(), aMsg.length());
       
   589 }
       
   590 
       
   591 /*-------------------------------------------------------------------------------
       
   592 
       
   593     Class: Util
       
   594 
       
   595     Method: Hex
       
   596 
       
   597     Description: Prints hex dump of data to output.
       
   598     
       
   599     Parameters: const unsigned char* aData : in : Pointer to data to be output
       
   600 	                                              as hex dump
       
   601                 const int aLength          : in : The lenght of data
       
   602 
       
   603     Return Values: None
       
   604 
       
   605     Errors/Exceptions: None
       
   606 
       
   607 -------------------------------------------------------------------------------*/
       
   608 void Util::Hex(const unsigned char* aData, const int aLength)
       
   609 {
       
   610 	Hex((void *)aData, aLength);
       
   611 }
       
   612 
       
   613 /*-------------------------------------------------------------------------------
       
   614 
       
   615     Class: Util
       
   616 
       
   617     Method: Hex
       
   618 
       
   619     Description: Prints hex dump of data to output.
       
   620     
       
   621     Parameters: void *aData       : in : Pointer to data to be output as hex dump
       
   622                 const int aLength : in : The lenght of data
       
   623 
       
   624     Return Values: None
       
   625 
       
   626     Errors/Exceptions: None
       
   627 
       
   628 -------------------------------------------------------------------------------*/
       
   629 void Util::Hex(void *aData, const int aLength)
       
   630 {
       
   631 	Hex(aData, aLength, NULL);
       
   632 }
       
   633 
       
   634 void Util::Hex(void* aData, const int aLength, string* aHexDump)
       
   635 {
       
   636 #ifdef ENABLE_LOG_SYNC 
       
   637 	iCritical.Lock();
       
   638 #endif
       
   639 
       
   640 	char line[16];
       
   641 	int line_len = 16;
       
   642 	int bytes_to_copy = 0;
       
   643 	int printed = 0;
       
   644 	bool print_out = (aHexDump == NULL ? true : false);
       
   645 	unsigned char *data = (unsigned char *)aData;
       
   646 
       
   647 	if (print_out)
       
   648 	{
       
   649 		printf("\n");
       
   650 	}
       
   651 	else
       
   652 	{
       
   653 		aHexDump->append("\r\n");
       
   654 	}
       
   655 	while (printed < aLength)
       
   656 	{
       
   657 		bytes_to_copy = ((aLength - printed >= line_len ? line_len : (aLength - printed)));
       
   658 		memset(line, 0, sizeof(line));
       
   659 		memcpy(line, &data[printed], bytes_to_copy);
       
   660 		for (int j = 0; j < line_len; j++)
       
   661 		{
       
   662 			char hex[4];
       
   663 			sprintf(hex, "%02X ", (unsigned char)line[j]);
       
   664 			if (print_out)
       
   665 			{
       
   666 				printf("%s", hex);
       
   667 			}
       
   668 			else
       
   669 			{
       
   670 				string s(hex);
       
   671 				aHexDump->append(s);
       
   672 			}
       
   673 		}
       
   674 		if (print_out) printf(" | ");
       
   675 		for (int j = 0; j < line_len; j++)
       
   676 		{
       
   677 			char c = line[j];
       
   678 			if (c >= ' ' && c <= 'z')
       
   679 			{
       
   680 				if (print_out)
       
   681 				{
       
   682 					printf("%c", c);
       
   683 				}
       
   684 				else
       
   685 				{
       
   686 					char tmp[2];
       
   687 					sprintf(tmp, "%c", c);
       
   688 					string s(tmp);
       
   689 					aHexDump->append(s);
       
   690 				}
       
   691 			} 
       
   692 			else
       
   693 			{
       
   694 				if (print_out)
       
   695 				{
       
   696 					printf(".");
       
   697 				}
       
   698 				else
       
   699 				{
       
   700 					aHexDump->append(".");
       
   701 				}
       
   702 			}
       
   703 		}
       
   704 		if (print_out)
       
   705 		{
       
   706 			printf("\n");
       
   707 		}
       
   708 		else
       
   709 		{
       
   710 			aHexDump->append("\r\n");
       
   711 		}
       
   712 		if ((printed - line_len) < aLength)
       
   713 		{
       
   714 			printed += 16;
       
   715 		}
       
   716 		else
       
   717 		{
       
   718 			printed = aLength - printed;
       
   719 		}
       
   720 	}
       
   721 	if (print_out) printf("\n");
       
   722 
       
   723 #ifdef ENABLE_LOG_SYNC 
       
   724 	iCritical.Unlock();
       
   725 #endif
       
   726 }
       
   727 
       
   728 /*
       
   729  * Sets paramvalue if command line parameter is found
       
   730  */
       
   731 void Util::CheckCommandlineParam( const string& paramname, string& paramvalue )
       
   732 {
       
   733 	// Command line parameter overrides ini-file value
       
   734 	if ( !g_parameters[paramname].empty() )
       
   735 		paramvalue = g_parameters[paramname];
       
   736 }
       
   737 
       
   738 // End of the file