IniFile.cpp
Go to the documentation of this file.
00001 /****************************************************************
00002  *
00003  * Copyright (c) 2010
00004  *
00005  * Fraunhofer Institute for Manufacturing Engineering   
00006  * and Automation (IPA)
00007  *
00008  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00009  *
00010  * Project name: care-o-bot
00011  * ROS stack name: cob3_driver
00012  * ROS package name: sickS300
00013  * Description:
00014  *                                                              
00015  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00016  *                      
00017  * Author: Christian Connette, email:christian.connette@ipa.fhg.de
00018  * Supervised by: Christian Connette, email:christian.connette@ipa.fhg.de
00019  *
00020  * Date of creation: Jan 2009
00021  * ToDo: Remove it!
00022  *
00023  * +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
00024  *
00025  * Redistribution and use in source and binary forms, with or without
00026  * modification, are permitted provided that the following conditions are met:
00027  *
00028  *     * Redistributions of source code must retain the above copyright
00029  *       notice, this list of conditions and the following disclaimer.
00030  *     * Redistributions in binary form must reproduce the above copyright
00031  *       notice, this list of conditions and the following disclaimer in the
00032  *       documentation and/or other materials provided with the distribution.
00033  *     * Neither the name of the Fraunhofer Institute for Manufacturing 
00034  *       Engineering and Automation (IPA) nor the names of its
00035  *       contributors may be used to endorse or promote products derived from
00036  *       this software without specific prior written permission.
00037  *
00038  * This program is free software: you can redistribute it and/or modify
00039  * it under the terms of the GNU Lesser General Public License LGPL as 
00040  * published by the Free Software Foundation, either version 3 of the 
00041  * License, or (at your option) any later version.
00042  * 
00043  * This program is distributed in the hope that it will be useful,
00044  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00045  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00046  * GNU Lesser General Public License LGPL for more details.
00047  * 
00048  * You should have received a copy of the GNU Lesser General Public 
00049  * License LGPL along with this program. 
00050  * If not, see <http://www.gnu.org/licenses/>.
00051  *
00052  ****************************************************************/
00053 
00054 #include <cob_utilities/IniFile.h>
00055 
00056 #include <math.h>
00057 #include <string.h>
00058 #include <stdio.h>
00059 #include <stdlib.h>
00060 
00061 using namespace std;
00062 
00063 //-------------------------------------------------------------------
00064 IniFile::IniFile(): m_vectorSize(500), m_CurCharInd(0)
00065 {
00066         m_bFileOK=false;
00067         m_CurLine.resize(m_vectorSize);
00068 }
00069 //--------------------------------------------------------------------------------
00070 
00071 IniFile::IniFile(std::string fileName): m_vectorSize(500), m_CurCharInd(0)
00072 {
00073         m_bFileOK=false;
00074         m_CurLine.resize(m_vectorSize);
00075         if(fileName != "") 
00076                 SetFileName(fileName);
00077 }
00078 //--------------------------------------------------------------------------------
00079 IniFile::~IniFile()
00080 {
00081 }
00082 //--------------------------------------------------------------------------------
00083 int IniFile::SetFileName(std::string fileName, std::string strIniFileUsedBy, bool bCreate)
00084 {
00085         m_fileName = fileName;
00086         m_strIniFileUsedBy = strIniFileUsedBy;
00087 
00088         if ((f = fopen(m_fileName.c_str(),"r")) == NULL)
00089         {       
00090                 if (bCreate == true)
00091                 {
00092                         f = fopen(m_fileName.c_str(),"w");      // create new file
00093                         std::cout << "Creating new INI-File " << m_fileName.c_str() << std::endl;
00094                         fclose(f);
00095                 }
00096                 else
00097                 {
00098                         std::cout << "INI-File not found " << m_fileName.c_str() << std::endl;
00099                         return -1;
00100                 }
00101         }
00102         else fclose(f);
00103         m_bFileOK = true;
00104         return 0;
00105 }
00106 //--------------------------------------------------------------------------------
00107 int IniFile::WriteKeyString(const char* pSect, const char* pKey, const std::string* pStrToWrite, bool bWarnIfNotfound)
00108 {       
00109         std::string StrWithDelimeters = '"' + *pStrToWrite + '"'; 
00110         return WriteKeyValue(pSect, pKey, StrWithDelimeters.c_str(), bWarnIfNotfound);
00111 }
00112 //--------------------------------------------------------------------------------
00113 int IniFile::WriteKeyValue(const char* szSect,const char* szKey,const char* szValue, bool bWarnIfNotfound)
00114 {
00115         if (!m_bFileOK) return -1;
00116         
00117         FILE* ftemp;
00118         int   lS,lK,i,bEoff;
00119         int   bFoundSect,bFoundKey;
00120         char  c;
00121         long fpos;
00122 
00123 
00124 /*------------------------verifications*/
00125 
00126         bFoundSect = 1 /* true*/;
00127         bFoundKey = 0 /* */;
00128         bEoff = 0;
00129         lS = strlen(szSect);
00130         lK = strlen(szKey);
00131         if ((lS * lK) == 0) return -1;
00132 
00133 /*--------------------- file opening */
00134 
00135         f = fopen(m_fileName.c_str(),"r");
00136         if (f == NULL)
00137         {
00138                 std::cout << "INI-File not found " << m_fileName.c_str() << std::endl;
00139                 return -1;
00140         }
00141         if ((ftemp = tmpfile()) == NULL)
00142         {
00143                 std::cout << "tmpfile() did not work!" << std::endl;
00144                 return -1;
00145         }
00146 
00147 
00148 /* ---------------------- search section and key */
00149         if (FindSection(szSect, bWarnIfNotfound) != 0)
00150         {       
00151                 bFoundSect = 0;
00152         }
00153         fpos = ftell(f);
00154         if (bFoundSect)
00155         {
00156                 if (!FindKey(szKey, false))     bFoundKey = 1;
00157                 fpos = ftell(f);                        
00158         }
00159         if (feof(f)) bEoff = 1;
00160 
00161 /* --------------------- updating the file */
00162         fseek(f,0,SEEK_SET);
00163         for (i=0;i<fpos;i++)
00164         { 
00165                 fscanf(f,"%c",&c);
00166                 fprintf(ftemp,"%c",c);
00167                 // MMB/23.02.2006: The counter i must not be incremented here on Linux machines!
00168 #ifdef WIN32
00169                 if (c=='\n') 
00170                         i++;
00171 #endif
00172         };
00173 
00174         if (!bFoundSect) {
00175                 fprintf(ftemp,"\n\n[%s]\n",szSect);
00176         }
00177         if (bFoundSect && (!bFoundKey) && (bEoff)) 
00178         {
00179                 fprintf(ftemp,"\n");
00180         }
00181                 
00182         
00183         if (!bFoundKey) 
00184         {
00185                 fprintf(ftemp,"%s=",szKey);
00186         }
00187         
00188         fprintf(ftemp,"%s",szValue);
00189 
00190         if (bFoundKey) FindNextLine(m_CurLine, m_CurCharInd);
00191         if (!(bEoff || feof(f))) 
00192         {       fprintf(ftemp,"\n");
00193                 while (!feof(f))
00194                 { 
00195                         fscanf(f,"%c",&c);
00196                         if (!feof(f)) 
00197                         {
00198                                 fprintf(ftemp,"%c",c);
00199                         }
00200                 }
00201         }
00202         fpos = ftell(ftemp);
00203         fclose(f);
00204 
00205         if ((f = fopen(m_fileName.c_str(),"w")) == NULL)
00206         {
00207                 if ((f = fopen(m_fileName.c_str(),"r")) != NULL)
00208                 {
00209                         fclose(f);
00210                         std::cout << "INI-File is write protected " << m_fileName.c_str() << std::endl;
00211                         return -1;
00212                 }
00213 
00214                 std::cout << "INI-File not found " << m_fileName.c_str() << std::endl;
00215                 return -1;
00216         }
00217         fseek(ftemp,0,SEEK_SET);
00218         for (i=0;i<fpos;i++)
00219         { 
00220                 fscanf(ftemp,"%c",&c);
00221                 fprintf(f,"%c",c);
00222         };
00223         fclose(f);
00224         fclose(ftemp);
00225         return 0;
00226 
00227 }
00228 //--------------------------------------------------------------------------------
00229 int IniFile::WriteKeyBool(const char* pSect, const char* pKey, bool bValue, bool bWarnIfNotfound)
00230 {       if(bValue)
00231                 return WriteKeyValue(pSect, pKey, "true", bWarnIfNotfound);
00232         else
00233                 return WriteKeyValue(pSect, pKey, "false", bWarnIfNotfound);
00234 }
00235 //--------------------------------------------------------------------------------
00236 int IniFile::WriteKeyInt(const char* szSect,const char* szKey,int nValue, bool bWarnIfNotfound)
00237 {
00238         char buff[20];
00239         snprintf(buff, 10, "%d", nValue);
00240         return WriteKeyValue(szSect,szKey,buff, bWarnIfNotfound);
00241 }
00242 //--------------------------------------------------------------------------------
00243 int IniFile::WriteKeyDouble(const char* szSect,const char* szKey,double Value,
00244                                                    int Length/*=12*/,int decimals/*=5*/, bool bWarnIfNotfound)
00245 {
00246         char buff[100];
00247         sprintf(buff, "%g", Value);
00248         return WriteKeyValue(szSect,szKey, buff, bWarnIfNotfound);
00249 }
00250 //--------------------------------------------------------------------------------
00251 int IniFile::GetKeyBool(const char* pSect, const char* pKey, bool* pValue, 
00252                                                         bool bWarnIfNotfound)
00253 {
00254         std::string strRead;
00255         char pBuf[20];
00256         *pValue = false;
00257         if (GetKeyValue(pSect, pKey, pBuf, 20, bWarnIfNotfound) == -1)
00258                 return -1;
00259 
00260         char* pChar = pBuf;
00261         while( *pChar == ' ' )  pChar++;                // skip spaces
00262 
00263         if( strncmp(pChar, "true", 4) == 0 )
00264         {
00265                 *pValue = true;
00266                 return 0;
00267         }
00268         if( strncmp(pChar, "false", 5) == 0 )
00269         {
00270                 *pValue = false;
00271                 return 0;
00272         }
00273 
00274         return -1;
00275 }
00276 //--------------------------------------------------------------------------------
00277 int IniFile::GetKeyInt(const char* szSect,const char* szKey,int* pValue, 
00278                                                         bool bWarnIfNotfound)
00279 {
00280         char buf[9];
00281         if (GetKeyValue(szSect,szKey,buf,9, bWarnIfNotfound)!=-1)
00282         {
00283                 //first get rid of spaces
00284                 if (buf[0] == ' ')
00285                 {
00286                         for (int i=0; i<=6; i++)
00287                                 buf[i] = buf[i+1];
00288                         buf[7]='\0';
00289                 }
00290         
00291                 if ((buf[0]=='0') && (buf[1] == 'x'))
00292                 {
00293                         int iNumLength = 0;
00294                         //check how long the hex-number is
00295                         for (int z=0; z<=7; z++)
00296                         {
00297                                 //if its not a number..
00298                                 if ((buf[z+2]<0x30) || (buf[z+2]>0x3A))
00299                                 {       
00300                                         iNumLength = z-1;
00301                                         break;
00302                                 }
00303                         }
00304                         *pValue = 0;
00305                         for (int i=0; i<=(iNumLength); i++)
00306                         {       
00307                                 //convert hex-string from character into int
00308                                 *pValue += (buf[i+2]-0x30) * (int)pow((double)16, (iNumLength-i));
00309                         }
00310                 }
00311                 else
00312                 {
00313                         *pValue = atoi(buf);
00314                 }
00315                 return 0;
00316 
00317         }
00318         else return -1;
00319 }
00320 //--------------------------------------------------------------------------------
00321 int IniFile::GetKeyLong(const char* szSect,const char* szKey,long* pValue, 
00322                                                         bool bWarnIfNotfound)
00323 {
00324         char buf[9];
00325         if (GetKeyValue(szSect,szKey,buf,9, bWarnIfNotfound)!=-1)
00326         {
00327                 *pValue = atol(buf);
00328                 return 0;
00329         }
00330         else return -1;
00331 }
00332 //--------------------------------------------------------------------------------
00333 int IniFile::GetKeyDouble(const char* szSect,const char* szKey,double* pValue, 
00334                                                         bool bWarnIfNotfound)
00335 {
00336         char buf[50];
00337         if (GetKeyValue(szSect, szKey, buf, 50, bWarnIfNotfound) == -1)
00338         {
00339                 if( bWarnIfNotfound )
00340                         std::cout << "Setting parameter " << szKey <<" = " << *pValue << " of section '" << szSect << 
00341                                                                                         "' in File '" << m_fileName.c_str() << std::endl;
00342                 return -1;
00343         }
00344 
00345         *pValue = atof(buf);
00346         return 0;
00347 } 
00348 //-----------------------------------------------
00349 int IniFile::GetKeyDouble(const char* pSect,const char* pKey,double* pValue,
00350                                                   double dDefault, bool bWarnIfNotfound)
00351 {
00352         (*pValue) = dDefault;
00353         return GetKeyDouble(pSect, pKey, pValue, bWarnIfNotfound);
00354 }
00355 //--------------------------------------------------------------------------------
00356 int IniFile::GetKeyValue(const char* szSect,const char* szKey,char* szBuf,
00357                                                 int lenBuf,     bool bWarnIfNotfound)
00358 {
00359         if (!m_bFileOK) return -1;
00360 
00361         int   lS,lK;
00362 
00363         lS = strlen(szSect);
00364         lK = strlen(szKey);
00365         if ((lS * lK) == 0) return -1;
00366         if ((f = fopen(m_fileName.c_str(),"r")) == NULL)
00367         {
00368                 std::cout << "INI-File not found " << m_fileName.c_str() << std::endl;
00369                 return -1;
00370         }
00371         if ( FindSection(szSect, bWarnIfNotfound) )
00372         {       fclose(f); 
00373                 return -1;
00374         }
00375         if ( FindKey(szKey, bWarnIfNotfound) )
00376         {       fclose(f); 
00377                 return -1;
00378         }
00379         
00380         if (feof(f))
00381         {
00382                 fclose(f);
00383                 return -1;
00384         }
00385 
00386         //----------- read szBuf bytes from file into szKey
00387         int BytesRead = fread( szBuf, 1, lenBuf, f );
00388 
00389         // terminate string
00390         int StrLen;
00391         if(BytesRead < lenBuf)
00392         {
00393                 if( BytesRead == 0 && (!feof(f)) )
00394                 {
00395                         std::cout << "file read" << std::endl;
00396                 }
00397                 StrLen = BytesRead;
00398         }
00399         else
00400         {
00401                 StrLen = lenBuf-1;
00402         }
00403         szBuf[StrLen] = '\0';   
00404         
00405         fclose(f);
00406         return StrLen;
00407 }
00408 //--------------------------------------------------------------------------------
00409 int IniFile::GetKeyString(const char* szSect,const char* szKey, std::string* pStrToRead, 
00410                                                         bool bWarnIfNotfound)
00411 {
00412         if (!m_bFileOK) return -1;
00413 
00414         int   lS,lK;
00415 
00416         lS = strlen(szSect);
00417         lK = strlen(szKey);
00418         if ((lS * lK) == 0) return -1;
00419         if ((f = fopen(m_fileName.c_str(),"r")) == NULL)
00420         {
00421                 std::cout << "INI-File not found " << m_fileName.c_str() << std::endl;
00422                 return -1;
00423         }
00424         if ( FindSection(szSect, bWarnIfNotfound) )
00425         {       fclose(f); 
00426                 return -1;
00427         }
00428         if ( FindKey(szKey, bWarnIfNotfound) )
00429         {       fclose(f); 
00430                 return -1;
00431         }
00432         
00433         if (feof(f))
00434         {
00435                 fclose(f);
00436                 return -1;
00437         }
00438 
00439         //----------- read szBuf bytes from file into szKey
00440         int res = SkipLineUntil(f, '"'); // find begin of string
00441         if(res == -1)
00442         {       if(bWarnIfNotfound)
00443                 {
00444                         std::cout << "GetKeyString section " << szSect << " key " << szKey << " first \" not found" << std::endl;
00445                 }
00446                 fclose(f);
00447                 return -1;
00448         }
00449 
00450         std::string strRead;
00451         res = ReadLineUntil(f, '"', strRead); // read string
00452         if(res == -1)
00453         {       
00454                 if(bWarnIfNotfound)
00455                 {       
00456                         std::cout << "GetKeyString section " << szSect << " key " << szKey << " string not found" << std::endl;
00457                 }
00458                 fclose(f);
00459                 return -1;
00460         }
00461 
00462         // success
00463         *pStrToRead = strRead;
00464         fclose(f);
00465         return 0;
00466 }
00467 //--------------------------------------------------------------------------------
00468 int IniFile::SkipLineUntil(FILE* pFile, const char EndChar)
00469 {
00470         int CharsRead = 0;                      
00471         while (1)
00472         {
00473                 int Char = fgetc(pFile);
00474                 
00475                 if (Char == EndChar)                    // end found?
00476                         return CharsRead;                       // read finished
00477         
00478                 if (Char == EOF || Char == '\n')
00479                         return -1;                              // end not found
00480 
00481                 CharsRead++;
00482         }
00483 }
00484 //--------------------------------------------------------------------------------
00485 int IniFile::ReadLineUntil(FILE* pFile, const char EndChar, std::string& ReadIntoStr)
00486 {
00487         int CharsRead = 0;                      
00488         while (1)
00489         {
00490                 int Char = fgetc(pFile);
00491                 
00492                 if (Char == EndChar)                    // end found?
00493                         return CharsRead;                       // read finished
00494         
00495                 if (Char == EOF || Char == '\n')
00496                         return -1;                              // end not found
00497 
00498                 ReadIntoStr.append(1, char(Char));
00499 
00500                 CharsRead++;
00501         }
00502 }
00503 //--------------------------------------------------------------------------------
00504 int IniFile::FindNextLine(std::vector<char>& NewLine, int& CharInd)
00505 {
00506         if (!feof(f))
00507         {
00508                 fgets(&NewLine[0], NewLine.size(), f); // store next line in NewLine
00509                 CharInd=0;        // makes CharInd reference the first char of the line
00510                 return 0;
00511         }
00512         return -1;
00513 }
00514 //--------------------------------------------------------------------------------
00515 int IniFile::FindNextSection(std::string* pSect, std::string prevSect, bool bWarnIfNotfound)
00516 {
00517         std::vector<char> line;
00518         //int charInd = 0;
00519         //int res = -1;
00520 
00521         if (!m_bFileOK) return -1;
00522 
00523         // Make sure that there is no old data.
00524         pSect->erase();
00525 
00526 /*--------------------- file opening */
00527         f = fopen(m_fileName.c_str(),"r");
00528         if (f == NULL)
00529         {
00530                 std::cout << "INI-File not found " << m_fileName.c_str() << std::endl;
00531                 return -1;
00532         }
00533         if (feof(f)) return -1;
00534 
00535 /*--------------------- search the section */
00536         if( prevSect != "" ) {
00537                 FindSection( prevSect.c_str(), bWarnIfNotfound );
00538         } else {
00539                 fseek(f,0,SEEK_SET);
00540         }
00541 
00542         FindNextLine(m_CurLine, m_CurCharInd); //read first line of file           
00543         do
00544         {
00545                 if (m_CurLine[0] == '[')
00546                 {
00547                         while( m_CurCharInd < (int)m_CurLine.size() ) {
00548                                 m_CurCharInd++;
00549                                 if (m_CurLine[m_CurCharInd] == ']') // if found section name equals searched one
00550                                 {
00551                                         for( int i=1; i<m_CurCharInd; ++i )
00552                                                 pSect->append(1, char(m_CurLine[i]));
00553                                         return 0;
00554                                 }       
00555                         }
00556                 }
00557                 else 
00558                 {
00559                         FindNextLine(m_CurLine, m_CurCharInd);
00560                 }
00561         }while (!feof(f));
00562 
00563 /*--------------------- file closing */
00564         fclose(f);
00565 
00566         return 0;
00567 }
00568 //--------------------------------------------------------------------------------
00569 int IniFile::FindSection(const char* sect, 
00570                                                         bool bWarnIfNotfound)
00571 {
00572         int   lS;
00573         lS = strlen(sect);
00574         if (feof(f)) return -1;
00575   
00576         FindNextLine(m_CurLine, m_CurCharInd); //read first line of file           
00577         do
00578         {
00579                 if (m_CurLine[0] == '[')
00580                 {
00581                         m_CurCharInd++;
00582                         if ((strncmp(&m_CurLine[m_CurCharInd], sect, lS) == 0) && (m_CurLine[m_CurCharInd+lS] == ']')) // if found section name equals searched one
00583                 {                     
00584                                 return 0;
00585                         }       
00586                         else 
00587                         {
00588                                 FindNextLine(m_CurLine, m_CurCharInd);
00589                         }
00590                 }
00591                 else if (m_CurLine[m_CurCharInd] == ' ') // if a blank is found
00592                 {
00593                         m_CurCharInd++;
00594                 }
00595                 else 
00596                 {
00597                         FindNextLine(m_CurLine, m_CurCharInd);
00598                 }
00599         }while (!feof(f));
00600 
00601         // not found
00602         if(bWarnIfNotfound)
00603         {
00604                 std::cout << "Section [" << sect << "] in IniFile " << m_fileName.c_str() << " used by "
00605                         << m_strIniFileUsedBy << " not found" << std::endl;
00606         }
00607         
00608         return -1;
00609 }
00610 //--------------------------------------------------------------------------------
00611 int IniFile::FindKey(const char* skey, 
00612                                                         bool bWarnIfNotfound)
00613 {
00614         int   lS;
00615         long  fpos = 0l;
00616         lS = strlen(skey);
00617         if (feof(f)) return -1;
00618         
00619         do
00620         {
00621                 fpos=ftell(f);// pointer to the begin of the last read line
00622                 FindNextLine(m_CurLine, m_CurCharInd);
00623                 
00624                 while ( m_CurLine[m_CurCharInd] == ' ' ) // skip blanks
00625                         {
00626                                 m_CurCharInd++;
00627                                 fpos++;
00628                         }
00629                 
00630                 if (m_CurLine[m_CurCharInd] == '[') // next section?
00631                         break; // not found
00632 
00633                 if (strncmp(&m_CurLine[m_CurCharInd], skey, lS) == 0) //Found
00634                 {   
00635                         m_CurCharInd+=lS;
00636                         fpos+=lS; // set file pointer to end of found key
00637             while ( m_CurLine[m_CurCharInd] == ' ' ) // skip blanks
00638                         {
00639                                 m_CurCharInd++;
00640                                 fpos++;
00641                         }
00642                         if ( m_CurLine[m_CurCharInd] == '=' )
00643                         {
00644                                 m_CurCharInd++; // set index to first char after the =
00645                                 fpos++;         
00646                                 fseek(f,fpos,SEEK_SET);// set file pointer to first char after the =
00647                                 return 0;
00648                         }
00649                         
00650                 }
00651                 
00652         }while (!feof(f));
00653 
00654         if(bWarnIfNotfound)
00655         {
00656                 std::cout << "Key " << skey << " in IniFile '" << m_fileName.c_str() << "' used by "
00657                         << m_strIniFileUsedBy << " not found" << std::endl;
00658         }
00659         return -1;
00660 }
00661 //-----------------------------------------------
00662 int IniFile::GetKey(const char* pSect,const char* pKey, std::string* pStrToRead, bool bWarnIfNotfound)
00663 {
00664         return GetKeyString(pSect,pKey,pStrToRead,bWarnIfNotfound);
00665 }
00666 //-----------------------------------------------
00667 int IniFile::GetKey(const char* pSect,const char* pKey,int* pValue, bool bWarnIfNotfound)
00668 {
00669         return GetKeyInt(pSect,pKey,pValue,bWarnIfNotfound);
00670 }
00671 //-----------------------------------------------
00672 
00673 int IniFile::GetKey(const char* pSect, const char* pKey, bool* pValue, bool bWarnIfNotfound)
00674 {
00675         return GetKeyBool(pSect,pKey,pValue,bWarnIfNotfound);
00676 }
00677 //-----------------------------------------------
00678 int IniFile::GetKey(const char* pSect,const char* pKey,double* pValue, bool bWarnIfNotfound)
00679 {
00680         return GetKeyDouble(pSect,pKey,pValue,bWarnIfNotfound);
00681 }
00682 


cob_utilities
Author(s): Christian Connette
autogenerated on Thu Aug 27 2015 12:45:31