PLearn 0.1
|
00001 // -*- C++ -*- 00002 00003 // PLearn (A C++ Machine Learning Library) 00004 // Copyright (C) 1998 Pascal Vincent 00005 // Copyright (C) 1999-2002 Pascal Vincent and Yoshua Bengio 00006 // Copyright (C) 1999-2005 University of Montreal 00007 // 00008 00009 // Redistribution and use in source and binary forms, with or without 00010 // modification, are permitted provided that the following conditions are met: 00011 // 00012 // 1. Redistributions of source code must retain the above copyright 00013 // notice, this list of conditions and the following disclaimer. 00014 // 00015 // 2. Redistributions in binary form must reproduce the above copyright 00016 // notice, this list of conditions and the following disclaimer in the 00017 // documentation and/or other materials provided with the distribution. 00018 // 00019 // 3. The name of the authors may not be used to endorse or promote 00020 // products derived from this software without specific prior written 00021 // permission. 00022 // 00023 // THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR 00024 // IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 00025 // OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN 00026 // NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 00027 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00028 // TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00029 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00030 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00031 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00032 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00033 // 00034 // This file is part of the PLearn library. For more information on the PLearn 00035 // library, go to the PLearn Web site at www.plearn.org 00036 00037 00038 /* ******************************************************* 00039 * $Id: fileutils.cc 10121 2009-04-15 14:27:39Z nouiz $ 00040 * AUTHORS: Pascal Vincent 00041 * This file is part of the PLearn library. 00042 ******************************************************* */ 00043 00044 // Win32 specific declarations. 00045 #if defined(WIN32) && !defined(__CYGWIN__) && !defined(_MINGW_) 00046 #include <direct.h> // Needed for declaration of _chdir. 00047 #define SYSTEM_CHDIR _chdir 00048 #else 00049 #define SYSTEM_CHDIR chdir 00050 #include <unistd.h> 00051 #endif // WIN32 00052 00053 #include "fileutils.h" 00054 #include "openFile.h" 00055 #include "openString.h" 00056 #include "PStream.h" 00057 #include "PPath.h" 00058 #include <plearn/base/tostring.h> 00059 #include <plearn/base/stringutils.h> 00060 #include <plearn/base/plerror.h> 00061 #include <plearn/math/pl_math.h> 00062 00063 #include <plearn/base/PrUtils.h> 00064 #include <nspr/prio.h> 00065 #include <nspr/prtime.h> 00066 #include <nspr/prerror.h> 00067 #include <nspr/prlong.h> 00068 #include <nspr/prenv.h> 00069 00070 namespace PLearn { 00071 using namespace std; 00072 00073 // TODO Use NSPR everywhere !? 00074 // TODO-PPath: there are a few things to fix to make it fully PPath-compliant. 00075 // TODO-PStream: this file is PStream-compliant 00076 00077 00088 static PRStatus PR_GetFileInfo64_NoWildcards(const char *fn, 00089 PRFileInfo64 *info) 00090 { 00091 PRFileDesc* f = PR_Open(fn, PR_RDONLY, 0); 00092 if (!f) 00093 return PR_FAILURE; 00094 00095 PRStatus status = PR_GetOpenFileInfo64(f, info); 00096 PR_Close(f); 00097 return status; 00098 } 00099 00101 // chdir // 00103 int chdir(const PPath& path) 00104 { 00105 int status = ::SYSTEM_CHDIR(path.absolute().c_str()); 00106 if (status!=0) 00107 PLERROR("Could not chdir to %s.",path.absolute().c_str()); 00108 return status; 00109 } 00110 00112 // pathexists // 00114 bool pathexists(const PPath& path) 00115 { 00116 PRFileInfo64 fi; 00117 00118 if (PR_GetFileInfo64(path.absolute().c_str(), &fi) != PR_SUCCESS) 00119 return false; 00120 else 00121 return fi.type == PR_FILE_FILE || fi.type == PR_FILE_DIRECTORY; 00122 } 00123 00125 // isdir // 00127 bool isdir(const PPath& path) 00128 { 00129 PRFileInfo64 fi; 00130 00131 if (PR_GetFileInfo64(path.absolute().c_str(), &fi) != PR_SUCCESS) 00132 return false; 00133 else 00134 return fi.type == PR_FILE_DIRECTORY; 00135 } 00136 00138 // isfile // 00140 bool isfile(const PPath& path) 00141 { 00142 PRFileInfo64 fi; 00143 00144 if (PR_GetFileInfo64(path.absolute().c_str(), &fi) != PR_SUCCESS) 00145 return false; 00146 else 00147 return fi.type == PR_FILE_FILE; 00148 } 00149 00151 // isfile // 00153 bool isemptyFile(const PPath& path) 00154 { 00155 PRFileInfo64 fi; 00156 00157 if (PR_GetFileInfo64(path.absolute().c_str(), &fi) != PR_SUCCESS) 00158 return false; 00159 else 00160 return (fi.type == PR_FILE_FILE) && (fi.size == 0); 00161 } 00162 00164 // mtime // 00166 time_t mtime(const PPath& path) 00167 { 00168 PRFileInfo64 fi; 00169 00170 if (PR_GetFileInfo64_NoWildcards(path.absolute().c_str(), &fi) != PR_SUCCESS) 00171 return 0; 00172 else { 00173 // The NSPR PRTime is number of microseconds since the epoch, while 00174 // time_t is the number of seconds since the (same) epoch. 00175 // Translate from the former to the later by dividing by 1e6, using 00176 // NSPR long long manipulation macros to be extra safe. 00177 PRInt64 time_t_compatible_value; 00178 PRInt64 one_million = LL_INIT(0, 1000000); 00179 LL_DIV(time_t_compatible_value, fi.modifyTime, one_million); 00180 return (time_t)time_t_compatible_value; 00181 } 00182 } 00183 00185 // lsdir // 00187 vector<string> lsdir(const PPath& dirpath) 00188 { 00189 vector<string> list; 00190 00191 // Since NSPR functions do not reset the current error id when nothing goes 00192 // wrong, we do it manually by setting it to the 'PR_MAX_ERROR' value, 00193 // which is a placeholder for the last available error in NSPR (thus it is 00194 // not a true error code by itself). 00195 // This will avoid a crash triggered by an earlier error. 00196 PR_SetError(PR_MAX_ERROR, 0); 00197 00198 PRDir* d = PR_OpenDir(dirpath.absolute().c_str()); 00199 if (!d) 00200 PLERROR("In lsdir: could not open directory %s",dirpath.absolute().c_str()); 00201 00202 PRDirEntry* dirent = PR_ReadDir(d, PR_SKIP_BOTH); 00203 while (dirent) { 00204 list.push_back(dirent->name); 00205 dirent = PR_ReadDir(d, PR_SKIP_BOTH); 00206 } 00207 00208 PRErrorCode e = PR_GetError(); 00209 // The error code 'PR_NO_MORE_FILES_ERROR' can be found due to the call to 00210 // 'PR_ReadDir', that sets this error when reaching the end of the 00211 // directory. 00212 if (e != PR_MAX_ERROR && e != PR_NO_MORE_FILES_ERROR) 00213 PLERROR("In lsdir: error while listing directory: %s.", 00214 getPrErrorString().c_str()); 00215 00216 if (PR_CloseDir(d) != PR_SUCCESS) 00217 PLERROR("In lsdir: error while closing directory: %s.", 00218 getPrErrorString().c_str()); 00219 00220 return list; 00221 } 00222 00224 // lsdir_fullpath // 00226 vector<PPath> lsdir_fullpath(const PPath& dirpath) 00227 { 00228 // TODO Somewhat a copy of addprefix, not really elegant. Do better ? 00229 vector<string> without_path = lsdir(dirpath); 00230 vector<PPath> with_path(without_path.size()); 00231 PPath prefix = dirpath; 00232 vector<string>::const_iterator it = without_path.begin(); 00233 vector<PPath>::iterator newit = with_path.begin(); 00234 while (it != without_path.end()) { 00235 *newit = prefix / *it; 00236 ++it; 00237 ++newit; 00238 } 00239 return with_path; 00240 } 00241 00242 00243 bool mkdir_lowlevel(const PPath& dirname) 00244 { 00245 return PR_MkDir(dirname.c_str(), 0777) == PR_SUCCESS; 00246 } 00247 00248 00250 // force_mkdir // 00252 // If you can't spot a race condition in the previous version of this function 00253 // (look in the version control history), please don't change the logic used 00254 // here. 00255 bool force_mkdir(const PPath& dirname) 00256 { 00257 if (dirname.isEmpty()) 00258 PLERROR("In force_mkdir - Parameter 'dirname' is empty"); 00259 00260 vector<PPath> paths; 00261 PPath path = dirname.absolute(); 00262 while (!path.isRoot()) { 00263 paths.push_back(path); 00264 path = path.up(); 00265 } 00266 00267 for (int i = int(paths.size()) - 1; i >= 0; i--) 00268 mkdir_lowlevel(paths[i].absolute()); 00269 00270 return isdir(dirname); 00271 } 00272 00274 // force_mkdir_for_file // 00276 void force_mkdir_for_file(const PPath& filepath) 00277 { 00278 PPath dirpath = filepath.dirname(); 00279 if (!force_mkdir(dirpath)) 00280 PLERROR("force_mkdir(%s) failed",dirpath.absolute().c_str()); 00281 } 00282 00284 // force_rmdir // 00286 bool force_rmdir(const PPath& dirname) 00287 { 00288 if (!isdir(dirname)) 00289 return false; 00290 00291 const vector<PPath> entries = lsdir_fullpath(dirname); 00292 for (vector<PPath>::const_iterator it = entries.begin(); 00293 it != entries.end(); ++it) { 00294 if (isdir(*it)) { 00295 if (!force_rmdir(*it)) 00296 return false; 00297 } 00298 else { 00299 if (PR_Delete(it->absolute().c_str()) != PR_SUCCESS) 00300 return false; 00301 } 00302 } 00303 00304 return PR_RmDir(dirname.absolute().c_str()) == PR_SUCCESS; 00305 } 00306 00308 // filesize // 00310 PRUint64 filesize64(const PPath& filename) 00311 { 00312 PRFileInfo64 inf; 00313 if (PR_GetFileInfo64_NoWildcards(filename.absolute().c_str(), &inf) != PR_SUCCESS) 00314 PLERROR("In filesize: error getting file info for %s: %s.", 00315 filename.absolute().c_str(), getPrErrorString().c_str()); 00316 return inf.size; 00317 } 00318 00319 00321 // loadFileAsString // 00323 string loadFileAsString(const PPath& filepath) 00324 { 00325 PStream in = openFile(filepath, PStream::raw_ascii, "r"); 00326 long n = filesize(filepath); 00327 00328 string result; 00329 in.read(result, streamsize(n)); 00330 return result; 00331 } 00332 00334 // saveStringInFile // 00336 void saveStringInFile(const PPath& filepath, const string& text) 00337 { 00338 force_mkdir_for_file(filepath); 00339 PStream out = openFile(filepath, PStream::raw_ascii, "w"); 00340 out << text; 00341 } 00342 00344 // cp // 00346 void cp(const PPath& srcpath, const PPath& destpath) 00347 { 00348 // TODO Cross-platform version ? 00349 string command = "\\cp -R " + srcpath.absolute() + " " + destpath.absolute(); 00350 system(command.c_str()); 00351 } 00352 00354 // rm // 00356 bool rm(const PPath& file, bool fail_on_error_if_exist) 00357 { 00358 // New cross-platform version. 00359 PRStatus ret = PR_Delete(file.absolute().c_str()); 00360 if(fail_on_error_if_exist && ret != PR_SUCCESS && pathexists(file)) 00361 PLERROR("Can't delete file %s",file.c_str()); 00362 return ret == PR_SUCCESS; 00363 /* 00364 // TODO Better cross-platform version ? 00365 #ifdef WIN32 00366 // For the moment works ONLY with files!!! 00367 if ( !DeleteFile(file.absolute().c_str()) ) 00368 { 00369 DWORD errorCode = GetLastError(); 00370 LPVOID lpMsgBuf; 00371 FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | 00372 FORMAT_MESSAGE_FROM_SYSTEM, 00373 NULL, errorCode, 00374 MAKELANGID(LANG_NEUTRAL, 00375 SUBLANG_DEFAULT), 00376 (LPTSTR) &lpMsgBuf, 0, 00377 NULL ); 00378 00379 // Comment because it works only with files.. 00380 //PLERROR("Cannot delete file %s. %s", file.c_str(), lpMsgBuf); 00381 LocalFree( lpMsgBuf ); 00382 } 00383 #else 00384 string command = "\\rm -rf " + file.absolute(); 00385 system(command.c_str()); 00386 #endif 00387 */ 00388 } 00389 00391 // mv // 00393 PRStatus mv(const PPath& source, const PPath& destination, bool fail_on_error) 00394 { 00395 PRStatus ret=PR_Rename(source.absolute().c_str(),destination.absolute().c_str()); 00396 if(ret!=PR_SUCCESS && fail_on_error) 00397 PLERROR("In mv(%s,%s) - the move failed!",source.c_str(),destination.c_str()); 00398 00399 return ret; 00400 } 00401 00403 // mvforce // 00405 PRStatus mvforce(const PPath& source, const PPath& destination, bool fail_on_error) 00406 { 00407 if(PR_Access(destination.c_str(), PR_ACCESS_EXISTS)==PR_SUCCESS){ 00408 if(PR_Delete(destination.c_str())!=PR_SUCCESS){ 00409 if(fail_on_error) 00410 PLERROR("In mvforce(%s,%s) - we failed to delete the destination!",source.c_str(),destination.c_str()); 00411 else 00412 return PR_FAILURE; 00413 } 00414 } 00415 return mv(source,destination); 00416 } 00417 00418 00420 // readWhileMatches // 00422 void readWhileMatches(PStream& in, const string& s){ 00423 string::size_type i = 0; 00424 int c; 00425 c = in.get(); 00426 string::size_type n = s.length(); 00427 while(c!=EOF) 00428 { 00429 if(s[i]!=c) 00430 { 00431 in.unget(); // Match failed, unget that last character. 00432 PLERROR("In readWhileMatches. Failure while matching %s: " 00433 "at position %ld expected a '%c', but read a '%c'",s.c_str(),long(i),s[i],c); 00434 } 00435 ++i; 00436 if(i==n) // passed through the whole string 00437 return; 00438 c = in.get(); 00439 } 00440 PLERROR("In readWhileMatches, met EOF while matching %s", s.c_str()); 00441 } 00442 00444 // skipRestOfLine // 00446 void skipRestOfLine(PStream& in) 00447 { 00448 int c = in.get(); 00449 while (c!='\n' && c!=EOF) 00450 c = in.get(); 00451 } 00452 00454 // skipBlanksAndComments // 00456 void skipBlanksAndComments(PStream& in) 00457 { 00458 int c = in.get(); 00459 while(c!=EOF) 00460 { 00461 if(!isspace(c)) 00462 { 00463 if(c=='#') 00464 skipRestOfLine(in); 00465 else 00466 break; 00467 } 00468 c = in.get(); 00469 } 00470 in.unget(); 00471 } 00472 00474 // getNextNonBlankLine // 00476 void getNextNonBlankLine(PStream& in, string& line) 00477 { 00478 while (in.good()) { 00479 in.getline(line); 00480 size_t l = line.size(); 00481 bool ok = false; 00482 size_t i = 0; 00483 while (i < l) { 00484 char& c = line[i]; 00485 if (!isspace(c)) { 00486 if (c == '#') { 00487 if (!ok) 00488 // The first non-blank character is a comment. 00489 break; 00490 else { 00491 // We get rid of the comments. 00492 line.resize(i); 00493 return; 00494 } 00495 } else { 00496 // We got a non-blank, non-comment character. 00497 ok = true; 00498 i++; 00499 } 00500 } else 00501 // Read a blank character. 00502 i++; 00503 } 00504 if (ok) 00505 // We read a non-blank line with no comment. 00506 return; 00507 } 00508 // Could not find a non-blank line. 00509 line = ""; 00510 } 00511 00513 // countNonBlankLinesOfFile // 00515 int countNonBlankLinesOfFile(const PPath& filename) 00516 { 00517 PStream in = openFile(filename, PStream::raw_ascii, "r"); 00518 int count = 0; 00519 int c = in.get(); 00520 while(c!=EOF) 00521 { 00522 while(c=='\n' || c==' ' || c=='\t' || c=='\r') 00523 c = in.get(); 00524 if(c!='\n' && c!='#' && c!=EOF) // We've found a non-blank, non-comment char. 00525 ++count; 00526 while(c!='\n' && c!=EOF) // Read until end of line. 00527 c = in.get(); 00528 c = in.get(); 00529 } 00530 return count; 00531 } 00532 00534 // newFilename // 00536 PPath newFilename(const PPath& directory, const string& prefix, bool is_directory) 00537 { 00538 #if defined(_MINGW_) || (defined(WIN32) && !defined(__CYGWIN__)) 00539 //PLERROR("This call is not yet implemented for this platform"); 00540 char* tmpfilename = tempnam(directory.absolute().c_str(), prefix.c_str()); 00541 #else 00542 // TODO Could probably make a better implementation. 00543 const string tmpdirname = remove_trailing_slash(directory.absolute()); 00544 const int length = int(tmpdirname.length() + 1 + prefix.length() + 6 + 1); 00545 char* tmpfilename = new char[length]; 00546 if (tmpdirname=="") 00547 sprintf(tmpfilename,"%sXXXXXX",prefix.c_str()); 00548 else 00549 sprintf(tmpfilename,"%s/%sXXXXXX",tmpdirname.c_str(),prefix.c_str()); 00550 int fd = mkstemp(tmpfilename); 00551 if (fd == -1) 00552 PLERROR("In newFilename - Could not create temporary file"); 00553 // Close the file descriptor, since we are not using it. 00554 close(fd); 00555 #endif 00556 if(!tmpfilename) 00557 PLERROR("In newFilename - Could not obtain temporary file name"); 00558 if (is_directory) { 00559 // Defeats the purpose of creating a temporary file, but who cares? 00560 PLearn::rm(tmpfilename); 00561 PR_MkDir(tmpfilename, 0777); 00562 } 00563 return tmpfilename; 00564 } 00565 00566 00568 // makeFileNameValid // 00570 PPath makeFileNameValid(const PPath& path) 00571 { 00572 PPath dirname = path.dirname(); 00573 PPath filename_full = path.basename(); 00574 PPath filename = filename_full.no_extension(); 00575 string ext = filename_full.extension(true); 00576 PPath ret = path; 00577 if(filename.length() + ext.length() > 256) 00578 { 00579 // We make a shorter name by encoding the rest into a few numbers. 00580 int j = 0; 00581 string rest = filename.substr(256-ext.length()-12); 00582 do 00583 { 00584 unsigned int n = j++; 00585 for(size_t i = 0; i < rest.length(); ++i) 00586 { 00587 int m=0; 00588 switch(i%4) 00589 { 00590 case 3: m= 1; break; 00591 case 2: m= 256; break; 00592 case 1: m= 65536; break; 00593 case 0: m= 256*65536; break; 00594 } 00595 n+= m*(unsigned char)rest[i]; 00596 } 00597 filename.resize(256-ext.length()-12); 00598 filename+= "-" + tostring(n); 00599 } while(pathexists(dirname / (filename + ext))); 00600 PLWARNING("makeFileNameValid: Filename '%s' changed to '%s'.", 00601 path.absolute().c_str(), (dirname / (filename + ext)).c_str()); 00602 ret = (dirname / (filename + ext)); 00603 } 00604 00605 // Replace illegal characters. 00606 string illegal = "*?'\"${}[]@ ,()"; 00607 for(size_t i=0;i<ret.size();i++) 00608 if (illegal.find(ret[i]) != string::npos) 00609 ret[i]='_'; 00610 return ret; 00611 } 00612 00614 // touch // 00616 void touch(const PPath& file) 00617 { 00618 string command = "touch "+ file.absolute(); 00619 system(command.c_str()); 00620 } 00621 00623 // addFileAndDateVariables // 00625 void addFileAndDateVariables(const PPath& filepath, map<string, string>& variables, const time_t& latest) 00626 { 00627 // Define new local variables 00628 variables["HOME"] = PPath::getenv("HOME"); 00629 00630 const PPath fpath = filepath.absolute(); 00631 variables["FILEPATH"] = fpath; 00632 variables["DIRPATH"] = fpath.dirname(); 00633 00634 const PPath basename = fpath.basename(); 00635 variables["FILENAME"] = basename; 00636 variables["FILEBASE"] = basename.no_extension(); 00637 variables["FILEEXT"] = fpath.extension(); 00638 00639 // Compute DATE, TIME, and DATETIME variables 00640 time_t curtime = time(NULL); 00641 struct tm *broken_down_time = localtime(&curtime); 00642 const int SIZE = 100; 00643 char time_buffer[SIZE]; 00644 strftime(time_buffer,SIZE,"%Y%m%d:%H%M%S",broken_down_time); 00645 variables["DATETIME"] = time_buffer; 00646 strftime(time_buffer,SIZE,"%Y%m%d",broken_down_time); 00647 variables["DATE"] = time_buffer; 00648 strftime(time_buffer,SIZE,"%H%M%S",broken_down_time); 00649 variables["TIME"] = time_buffer; 00650 variables["MTIME"] = tostring(latest); 00651 } 00652 00654 // readFileAndMacroProcess // 00656 string readFileAndMacroProcess(const PPath& filepath, map<string, string>& variables, 00657 time_t& latest, bool change_dir) 00658 { 00659 // pout << "Processing file: " << filepath.absolute() << endl; 00660 // Save old variables (to allow recursive calls) 00661 const char* OldVariables[] = { 00662 "FILEPATH", "DIRPATH", "FILENAME", "FILEBASE", "FILEEXT", "DATE", "TIME", "DATETIME" 00663 }; 00664 const int num_old = sizeof(OldVariables) / sizeof(OldVariables[0]); 00665 map<string,string> old_vars; 00666 for (int i=0; i<num_old; ++i) 00667 old_vars[OldVariables[i]] = variables[OldVariables[i]]; 00668 PPath file(filepath); // Default: file = filepath. 00669 00670 map<string, string>* added = 0; 00671 map<string, string>* backup = 0; 00672 if (!isfile(file)) { 00673 // Parse 'file' for potential additional arguments. 00674 added = new map<string, string>(); 00675 backup = new map<string, string>(); 00676 parseBaseAndParameters(file.absolute(), file, variables, added, backup); 00677 } 00678 00679 // Possibly change directory. 00680 PPath old_dir; 00681 if (change_dir) { 00682 old_dir = PPath::getcwd(); 00683 chdir(file.dirname()); 00684 file = file.basename(); 00685 } 00686 00687 latest=max(latest,mtime(file.absolute())); 00688 00689 // Add the new file and date variables 00690 addFileAndDateVariables(file, variables, latest); 00691 00692 // Perform actual parsing and macro processing... 00693 PStream in = openFile(file, PStream::plearn_ascii, "r"); 00694 string text; 00695 try 00696 { 00697 text = readAndMacroProcess(in, variables, latest); 00698 } 00699 catch(const PLearnError& e) 00700 { 00701 PLERROR("while parsing file %s we got an error: \n%s", 00702 filepath.c_str(),e.message().c_str()); 00703 } 00704 00705 // Restore previous variables 00706 if (added) 00707 for (map<string, string>::const_iterator it = added->begin(); 00708 it != added->end(); it++) 00709 variables.erase(it->first); 00710 if (backup) 00711 for (map<string, string>::const_iterator it = backup->begin(); 00712 it != backup->end(); it++) 00713 variables[it->first] = it->second; 00714 for (int i=0; i<num_old; ++i) 00715 variables[OldVariables[i]] = old_vars[OldVariables[i]]; 00716 00717 // Restore previous directory. 00718 if (change_dir) 00719 chdir(old_dir); 00720 00721 // Free memory. 00722 if (added) delete added; 00723 if (backup) delete backup; 00724 00725 return text; 00726 } 00727 00729 // readAndMacroProcess // 00731 string readAndMacroProcess(PStream& in, map<string, string>& variables, 00732 time_t& latest, bool skip_comments) 00733 { 00734 string text; // the processed text to return 00735 bool inside_a_quoted_string=false; // inside a quoted string we don't skip characters following a # 00736 int c=EOF, last_c=EOF; 00737 while(in.good()) 00738 { 00739 last_c = c; 00740 c = in.get(); 00741 if (last_c!='\\' && c=='"') // we find either the beginning or end of a quoted string 00742 inside_a_quoted_string = !inside_a_quoted_string; // flip status 00743 00744 if(!inside_a_quoted_string && c=='#' && skip_comments) 00745 // It's a comment: skip rest of line 00746 while(c!=EOF && c!='\n' && c!='\r') 00747 c = in.get(); 00748 00749 if(c==EOF) 00750 break; 00751 else if(c!='$') 00752 text += c; 00753 else // We have a $ macro command 00754 { 00755 c = in.peek(); 00756 switch(c) 00757 { 00758 case '{': // expand a defined variable ${varname} 00759 { 00760 string varname; // name of a variable 00761 in.get(); // skip '{' 00762 in.smartReadUntilNext("}", varname, true); 00763 // Maybe there are macros to process to obtain the real name of the variable. 00764 PStream varname_stream = openString(varname, PStream::raw_ascii); 00765 varname = readAndMacroProcess(varname_stream, variables, latest); 00766 varname = removeblanks(varname); 00767 map<string, string>::const_iterator it = variables.find(varname); 00768 if(it==variables.end()) 00769 PLERROR("Macro variable ${%s} undefined", varname.c_str()); 00770 PStream varin = openString(it->second, PStream::raw_ascii); 00771 text += readAndMacroProcess(varin, variables, latest); 00772 } 00773 break; 00774 00775 case 'C': // it's a CHAR{expression} 00776 { 00777 string expr; 00778 readWhileMatches(in, "CHAR"); 00779 bool syntax_ok = true; 00780 c = in.get(); 00781 if(c == '{') 00782 in.smartReadUntilNext("}", expr, true); 00783 else 00784 syntax_ok = false; 00785 if (!syntax_ok) 00786 PLERROR("$CHAR syntax is: $CHAR{expr}"); 00787 PStream expr_stream = openString(expr, PStream::raw_ascii); 00788 char ch = (char) toint(readAndMacroProcess(expr_stream, variables, latest)); 00789 text += ch; 00790 } 00791 break; 00792 00793 case 'D': 00794 { 00795 int next = in.get(); 00796 next = in.peek(); // Next character. 00797 switch(next) { 00798 00799 case 'E': // it's a DEFINE{varname}{expr} 00800 { 00801 string varname; // name of a variable 00802 string vardef; // definition of a variable 00803 readWhileMatches(in, "EFINE{"); 00804 in.getline(varname, '}'); 00805 varname = removeblanks(varname); 00806 skipBlanksAndComments(in); 00807 if(in.get()!='{') 00808 PLERROR("Bad syntax in .plearn DEFINE macro: correct syntax is $DEFINE{name}{definition}"); 00809 in.smartReadUntilNext("}", vardef, true); 00810 map<string, string>::const_iterator it = variables.find(varname); 00811 if (it == variables.end()) 00812 variables[varname] = vardef; 00813 else 00814 PLERROR("Variable %s is already defined, you need to first $UNDEFINE it " 00815 "if you want to assign it a new value", varname.c_str()); 00816 } 00817 break; 00818 00819 case 'I': // it's a DIVIDE{expr1}{expr2} 00820 { 00821 string expr1, expr2; 00822 readWhileMatches(in, "IVIDE"); 00823 bool syntax_ok = true; 00824 c = in.get(); 00825 if (syntax_ok) { 00826 if(c == '{') 00827 in.smartReadUntilNext("}", expr1, true); 00828 else 00829 syntax_ok = false; 00830 } 00831 if (syntax_ok) { 00832 c = in.get(); 00833 if(c == '{') 00834 in.smartReadUntilNext("}", expr2, true); 00835 else 00836 syntax_ok = false; 00837 } 00838 if (!syntax_ok) 00839 PLERROR("$DIVIDE syntax is: $DIVIDE{expr1}{expr2}"); 00840 PStream expr1_stream = openString(expr1, PStream::raw_ascii); 00841 PStream expr2_stream = openString(expr2, PStream::raw_ascii); 00842 string expr1_eval = readAndMacroProcess(expr1_stream, variables, latest); 00843 string expr2_eval = readAndMacroProcess(expr2_stream, variables, latest); 00844 real e1, e2; 00845 if (!pl_isnumber(expr1_eval, &e1) || !pl_isnumber(expr2_eval, &e2)) { 00846 PLERROR("In $DIVIDE{expr1}{expr2}, either 'expr1' or 'expr2' is not a number"); 00847 } 00848 text += tostring(e1 / e2); 00849 } 00850 break; 00851 00852 } 00853 break; 00854 } 00855 00856 case 'E': 00857 { 00858 int next = in.get(); 00859 next = in.peek(); // Next character. 00860 switch(next) { 00861 00862 case 'C': // it's an ECHO{expr} 00863 { 00864 string expr; 00865 readWhileMatches(in, "CHO"); 00866 bool syntax_ok = true; 00867 c = in.get(); 00868 if(c == '{') 00869 in.smartReadUntilNext("}", expr, true); 00870 else 00871 syntax_ok = false; 00872 if (!syntax_ok) 00873 PLERROR("$ECHO syntax is: $ECHO{expr}"); 00874 PStream expr_stream = openString(expr, PStream::raw_ascii); 00875 pout << readAndMacroProcess(expr_stream, variables, latest) << endl; 00876 } 00877 break; 00878 00879 case 'V': // it's an EVALUATE{varname} 00880 { 00881 string expr; 00882 readWhileMatches(in, "VALUATE"); 00883 bool syntax_ok = true; 00884 c = in.get(); 00885 if(c == '{') 00886 in.smartReadUntilNext("}", expr, true); 00887 else 00888 syntax_ok = false; 00889 if (!syntax_ok) 00890 PLERROR("$EVALUATE syntax is: $EVALUATE{varname}"); 00891 PStream expr_stream = openString(expr, PStream::raw_ascii); 00892 string varname = readAndMacroProcess(expr_stream, variables, latest); 00893 string to_evaluate = variables[varname]; 00894 PStream to_evaluate_stream = openString(to_evaluate, PStream::raw_ascii); 00895 string evaluated = readAndMacroProcess(to_evaluate_stream, variables, latest); 00896 variables[varname] = evaluated; 00897 } 00898 break; 00899 } 00900 break; 00901 } 00902 00903 case 'G': // it's a GETENV{expression} 00904 { 00905 string expr; 00906 readWhileMatches(in, "GETENV"); 00907 bool syntax_ok = true; 00908 c = in.get(); 00909 if(c == '{') 00910 in.smartReadUntilNext("}", expr, true); 00911 else 00912 syntax_ok = false; 00913 if (!syntax_ok) 00914 PLERROR("$GETENV syntax is: $GETENV{expr}"); 00915 PStream expr_stream = openString(expr, PStream::raw_ascii); 00916 string var_name = readAndMacroProcess(expr_stream, variables, latest); 00917 const char* var = PR_GetEnv(var_name.c_str()); 00918 00919 if (!var) 00920 PLERROR("In readAndMacroProcess - The environment variable %s is not defined", var_name.c_str()); 00921 text += string(var); 00922 } 00923 break; 00924 00925 case 'I': 00926 { 00927 int next = in.get(); 00928 next = in.peek(); // Next character. 00929 switch(next) { 00930 00931 case 'F': // it's an IF{cond}{expr_cond_true}{expr_cond_false} 00932 { 00933 string cond, expr_cond_true, expr_cond_false, expr_evaluated; 00934 readWhileMatches(in, "F"); 00935 bool syntax_ok = true; 00936 c = in.get(); 00937 if(c == '{') 00938 in.smartReadUntilNext("}", cond, true); 00939 else 00940 syntax_ok = false; 00941 if (syntax_ok) { 00942 c = in.get(); 00943 if(c == '{') 00944 in.smartReadUntilNext("}", expr_cond_true, true); 00945 else 00946 syntax_ok = false; 00947 } 00948 if (syntax_ok) { 00949 c = in.get(); 00950 if(c == '{') 00951 in.smartReadUntilNext("}", expr_cond_false, true); 00952 else 00953 syntax_ok = false; 00954 } 00955 if (!syntax_ok) 00956 PLERROR("$IF syntax is: $IF{cond}{expr_cond_true}{expr_cond_false}"); 00957 00958 PStream cond_stream = openString(cond, PStream::raw_ascii); 00959 string evaluate_cond = readAndMacroProcess(cond_stream, variables, latest); 00960 if (evaluate_cond == "1" ) { 00961 expr_evaluated = expr_cond_true; 00962 } else if (evaluate_cond == "0") { 00963 expr_evaluated = expr_cond_false; 00964 } else { 00965 PLERROR("$IF condition should be 0 or 1, but is %s", evaluate_cond.c_str()); 00966 } 00967 PStream expr_stream = openString(expr_evaluated, PStream::raw_ascii); 00968 text += readAndMacroProcess(expr_stream, variables, latest); 00969 } 00970 break; 00971 00972 case 'N': 00973 { 00974 next = in.get(); 00975 next = in.peek(); // Next character. 00976 switch(next) { 00977 00978 case 'C': // it's an INCLUDE{filepath} 00979 { 00980 string raw_includefilepath; // The raw path read from the script. 00981 readWhileMatches(in, "CLUDE"); 00982 c = in.get(); 00983 if(c=='<') 00984 in.smartReadUntilNext(">", raw_includefilepath, true); 00985 else if(c=='{') 00986 in.smartReadUntilNext("}", raw_includefilepath, true); 00987 else 00988 PLERROR("$INCLUDE must be followed immediately by a { or <"); 00989 PStream pathin = openString(raw_includefilepath, PStream::raw_ascii); 00990 raw_includefilepath = readAndMacroProcess(pathin, variables, latest); 00991 raw_includefilepath = removeblanks(raw_includefilepath); 00992 PPath p = PPath(raw_includefilepath); 00993 // Read file with appropriate variable definitions. 00994 time_t new_latest = 0; 00995 text += readFileAndMacroProcess 00996 (p, variables, new_latest); 00997 latest=max(latest,new_latest); 00998 string s=tostring(latest); 00999 variables["MTIME"]=s; 01000 } 01001 break; 01002 01003 case 'T': // it's an INT{val} 01004 { 01005 string expr; 01006 readWhileMatches(in, "T"); 01007 bool syntax_ok = true; 01008 c = in.get(); 01009 if(c == '{') 01010 in.smartReadUntilNext("}", expr, true); 01011 else 01012 syntax_ok = false; 01013 if (!syntax_ok) 01014 PLERROR("$INT syntax is: $INT{expr}"); 01015 PStream expr_stream = openString(expr, PStream::raw_ascii); 01016 string expr_eval = readAndMacroProcess(expr_stream, variables, latest); 01017 real e; 01018 if (!pl_isnumber(expr_eval, &e)) { 01019 PLERROR("In $INT{expr}, 'expr' is not a number"); 01020 } 01021 text += tostring(int(e)); 01022 } 01023 } 01024 } 01025 break; 01026 01027 case 'S': 01028 { 01029 01030 next = in.get(); 01031 next = in.peek(); // Next character. 01032 switch(next) { 01033 01034 case 'D': // it's an ISDEFINED{expr} 01035 { 01036 string expr; 01037 readWhileMatches(in, "DEFINED"); 01038 bool syntax_ok = true; 01039 c = in.get(); 01040 if(c == '{') 01041 in.smartReadUntilNext("}", expr, true); 01042 else 01043 syntax_ok = false; 01044 if (!syntax_ok) 01045 PLERROR("$ISDEFINED syntax is: $ISDEFINED{expr}"); 01046 PStream expr_stream = openString(expr, PStream::raw_ascii); 01047 string expr_eval = readAndMacroProcess(expr_stream, variables, latest); 01048 map<string, string>::const_iterator it = variables.find(expr_eval); 01049 if(it==variables.end()) { 01050 // The variable is not defined. 01051 text += "0"; 01052 } else { 01053 text += "1"; 01054 } 01055 } 01056 break; 01057 01058 case 'E': // it's an ISEQUAL{expr1}{expr2} 01059 { 01060 string expr1, expr2; 01061 readWhileMatches(in, "EQUAL"); 01062 bool syntax_ok = true; 01063 c = in.get(); 01064 if(c == '{') 01065 in.smartReadUntilNext( "}", expr1, true); 01066 else 01067 syntax_ok = false; 01068 if (syntax_ok) { 01069 c = in.get(); 01070 if(c == '{') 01071 in.smartReadUntilNext("}", expr2, true); 01072 else 01073 syntax_ok = false; 01074 } 01075 if (!syntax_ok) 01076 PLERROR("$ISEQUAL syntax is: $ISEQUAL{expr1}{expr2}"); 01077 PStream expr1_stream = openString(expr1, PStream::raw_ascii); 01078 PStream expr2_stream = openString(expr2, PStream::raw_ascii); 01079 string expr1_eval = readAndMacroProcess(expr1_stream, variables, latest); 01080 string expr2_eval = readAndMacroProcess(expr2_stream, variables, latest); 01081 if (expr1_eval == expr2_eval) { 01082 text += "1"; 01083 } else { 01084 text += "0"; 01085 } 01086 } 01087 break; 01088 01089 case 'H': // it's an ISHIGHER{expr1}{expr2} 01090 { 01091 string expr1, expr2; 01092 readWhileMatches(in, "HIGHER"); 01093 bool syntax_ok = true; 01094 c = in.get(); 01095 if(c == '{') 01096 in.smartReadUntilNext("}", expr1, true); 01097 else 01098 syntax_ok = false; 01099 if (syntax_ok) { 01100 c = in.get(); 01101 if(c == '{') 01102 in.smartReadUntilNext("}", expr2, true); 01103 else 01104 syntax_ok = false; 01105 } 01106 if (!syntax_ok) 01107 PLERROR("$ISHIGHER syntax is: $ISHIGHER{expr1}{expr2}"); 01108 PStream expr1_stream = openString(expr1, PStream::raw_ascii); 01109 PStream expr2_stream = openString(expr2, PStream::raw_ascii); 01110 string expr1_eval = readAndMacroProcess(expr1_stream, variables, latest); 01111 string expr2_eval = readAndMacroProcess(expr2_stream, variables, latest); 01112 real e1, e2; 01113 if (!pl_isnumber(expr1_eval, &e1) || !pl_isnumber(expr2_eval, &e2)) { 01114 PLERROR("In $ISHIGHER{expr1}{expr2}, either 'expr1' or 'expr2' is not a number"); 01115 } 01116 if (e1 > e2) { 01117 text += "1"; 01118 } else { 01119 text += "0"; 01120 } 01121 } 01122 break; 01123 } 01124 } 01125 break; 01126 } 01127 } 01128 break; 01129 01130 case 'M': // it's a MINUS{expr1}{expr2} 01131 { 01132 string expr1, expr2; 01133 readWhileMatches(in, "MINUS"); 01134 bool syntax_ok = true; 01135 c = in.get(); 01136 if (syntax_ok) { 01137 if(c == '{') 01138 in.smartReadUntilNext("}", expr1,true); 01139 else 01140 syntax_ok = false; 01141 } 01142 if (syntax_ok) { 01143 c = in.get(); 01144 if(c == '{') 01145 in.smartReadUntilNext("}", expr2,true); 01146 else 01147 syntax_ok = false; 01148 } 01149 if (!syntax_ok) 01150 PLERROR("$MINUS syntax is: $MINUS{expr1}{expr2}"); 01151 PStream expr1_stream = openString(expr1, PStream::raw_ascii); 01152 PStream expr2_stream = openString(expr2, PStream::raw_ascii); 01153 string expr1_eval = readAndMacroProcess(expr1_stream, variables, latest); 01154 string expr2_eval = readAndMacroProcess(expr2_stream, variables, latest); 01155 real e1, e2; 01156 if (!pl_isnumber(expr1_eval, &e1) || !pl_isnumber(expr2_eval, &e2)) { 01157 PLERROR("In $MINUS{expr1}{expr2}, either 'expr1' or 'expr2' is not a number"); 01158 } 01159 text += tostring(e1 - e2); 01160 } 01161 break; 01162 01163 case 'O': // it's an OR{expr1}{expr2} 01164 { 01165 string expr1, expr2; 01166 readWhileMatches(in, "OR"); 01167 bool syntax_ok = true; 01168 c = in.get(); 01169 if (syntax_ok) { 01170 if(c == '{') 01171 in.smartReadUntilNext("}", expr1,true); 01172 else 01173 syntax_ok = false; 01174 } 01175 if (syntax_ok) { 01176 c = in.get(); 01177 if(c == '{') 01178 in.smartReadUntilNext("}", expr2,true); 01179 else 01180 syntax_ok = false; 01181 } 01182 if (!syntax_ok) 01183 PLERROR("$OR syntax is: $OR{expr1}{expr2}"); 01184 PStream expr1_stream = openString(expr1, PStream::raw_ascii); 01185 PStream expr2_stream = openString(expr2, PStream::raw_ascii); 01186 string expr1_eval = readAndMacroProcess(expr1_stream, variables, latest); 01187 string expr2_eval = readAndMacroProcess(expr2_stream, variables, latest); 01188 real e1, e2; 01189 if (!pl_isnumber(expr1_eval, &e1) || !pl_isnumber(expr2_eval, &e2)) { 01190 PLERROR("In $OR{expr1}{expr2}, either 'expr1' or 'expr2' is not a number"); 01191 } 01192 int i1 = toint(expr1_eval); 01193 int i2 = toint(expr2_eval); 01194 bool is_true = i1 || i2; 01195 text += tostring(is_true); 01196 } 01197 break; 01198 01199 case 'P': // it's a PLUS{expr1}{expr2} 01200 { 01201 string expr1, expr2; 01202 readWhileMatches(in, "PLUS"); 01203 bool syntax_ok = true; 01204 c = in.get(); 01205 if (syntax_ok) { 01206 if(c == '{') 01207 in.smartReadUntilNext("}", expr1,true); 01208 else 01209 syntax_ok = false; 01210 } 01211 if (syntax_ok) { 01212 c = in.get(); 01213 if(c == '{') 01214 in.smartReadUntilNext("}", expr2,true); 01215 else 01216 syntax_ok = false; 01217 } 01218 if (!syntax_ok) 01219 PLERROR("$PLUS syntax is: $PLUS{expr1}{expr2}"); 01220 PStream expr1_stream = openString(expr1, PStream::raw_ascii); 01221 PStream expr2_stream = openString(expr2, PStream::raw_ascii); 01222 string expr1_eval = readAndMacroProcess(expr1_stream, variables, latest); 01223 string expr2_eval = readAndMacroProcess(expr2_stream, variables, latest); 01224 real e1, e2; 01225 if (!pl_isnumber(expr1_eval, &e1) || !pl_isnumber(expr2_eval, &e2)) { 01226 PLERROR("In $PLUS{expr1}{expr2}, either 'expr1' or 'expr2' is not a number"); 01227 } 01228 text += tostring(e1 + e2); 01229 } 01230 break; 01231 01232 case 'S': // it's a SWITCH{expr}{cond1}{val1}{cond2}{val2}...{valdef} 01233 { 01234 string expr, valdef; 01235 vector<string> comp; 01236 vector<string> val; 01237 readWhileMatches(in, "SWITCH"); 01238 bool syntax_ok = true; 01239 // First read 'expr'. 01240 c = in.get(); 01241 if (syntax_ok) { 01242 if(c == '{') 01243 in.smartReadUntilNext("}", expr, true); 01244 else 01245 syntax_ok = false; 01246 } 01247 // Read the pairs {compx}{valx}, then {valdef} 01248 bool done_parsing = false; 01249 while (syntax_ok && !done_parsing) { 01250 c = getAfterSkipBlanksAndComments(in); 01251 string tmp_comp, tmp_val; 01252 if(c == '{') 01253 in.smartReadUntilNext("}", tmp_comp, true); 01254 else 01255 syntax_ok = false; 01256 if (syntax_ok) { 01257 c = peekAfterSkipBlanksAndComments(in); 01258 if(c == '{') { 01259 c = getAfterSkipBlanksAndComments(in); 01260 in.smartReadUntilNext("}", tmp_val, true); 01261 } 01262 else { 01263 // We must have read 'valdef' just before. 01264 valdef = tmp_comp; 01265 done_parsing = true; 01266 } 01267 } 01268 if (!done_parsing) { 01269 comp.push_back(tmp_comp); 01270 val.push_back(tmp_val); 01271 } 01272 } 01273 if (!syntax_ok) 01274 PLERROR("$SWITCH syntax is: $SWITCH{expr}{comp1}{val1}{comp2}{val2}...{valdef}"); 01275 PStream expr_stream = openString(expr, PStream::raw_ascii); 01276 string expr_eval = readAndMacroProcess(expr_stream, variables, latest); 01277 bool not_done = true; 01278 for (size_t i = 0; i < comp.size() && not_done; i++) { 01279 PStream comp_stream = openString(comp[i], PStream::raw_ascii); 01280 string comp_eval = readAndMacroProcess(comp_stream, variables, latest); 01281 if (expr_eval == comp_eval) { 01282 not_done = false; 01283 PStream val_stream = openString(val[i], PStream::raw_ascii); 01284 text += readAndMacroProcess(val_stream, variables, latest); 01285 } 01286 } 01287 if (not_done) { 01288 // Default value needed. 01289 PStream val_stream = openString(valdef, PStream::raw_ascii); 01290 text += readAndMacroProcess(val_stream, variables, latest); 01291 } 01292 } 01293 break; 01294 01295 case 'T': // it's a TIMES{expr1}{expr2} 01296 { 01297 string expr1, expr2; 01298 readWhileMatches(in, "TIMES"); 01299 bool syntax_ok = true; 01300 c = in.get(); 01301 if (syntax_ok) { 01302 if(c == '{') 01303 in.smartReadUntilNext("}", expr1, true); 01304 else 01305 syntax_ok = false; 01306 } 01307 if (syntax_ok) { 01308 c = in.get(); 01309 if(c == '{') 01310 in.smartReadUntilNext("}", expr2, true); 01311 else 01312 syntax_ok = false; 01313 } 01314 if (!syntax_ok) 01315 PLERROR("$TIMES syntax is: $TIMES{expr1}{expr2}"); 01316 PStream expr1_stream = openString(expr1, PStream::raw_ascii); 01317 PStream expr2_stream = openString(expr2, PStream::raw_ascii); 01318 string expr1_eval = readAndMacroProcess(expr1_stream, variables, latest); 01319 string expr2_eval = readAndMacroProcess(expr2_stream, variables, latest); 01320 real e1, e2; 01321 if (!pl_isnumber(expr1_eval, &e1) || !pl_isnumber(expr2_eval, &e2)) { 01322 PLERROR("In $TIMES{expr1}{expr2}, either 'expr1' or 'expr2' is not a number"); 01323 } 01324 text += tostring(e1 * e2); 01325 } 01326 break; 01327 01328 case 'U': // it's an UNDEFINE{varname} 01329 { 01330 string expr; 01331 readWhileMatches(in, "UNDEFINE"); 01332 bool syntax_ok = true; 01333 c = in.get(); 01334 if(c == '{') 01335 in.smartReadUntilNext("}", expr, true); 01336 else 01337 syntax_ok = false; 01338 if (!syntax_ok) 01339 PLERROR("$UNDEFINE syntax is: $UNDEFINE{expr}"); 01340 PStream expr_stream = openString(expr, PStream::raw_ascii); 01341 string varname = readAndMacroProcess(expr_stream, variables, latest); 01342 while (variables.count(varname) > 0) { 01343 // This loop is probably not necessary, but just in case... 01344 variables.erase(varname); 01345 } 01346 } 01347 break; 01348 01349 default: 01350 PLERROR("In readAndMacroProcess: only supported macro commands are \n" 01351 "${varname}, $CHAR, $DEFINE, $DIVIDE, $ECHO, $EVALUATE, $GETENV, $IF, $INCLUDE, $INT, $ISDEFINED, $ISEQUAL, $ISHIGHER, $MINUS, $PLUS, $OR, $SWITCH, $TIMES, $UNDEFINE." 01352 "But I read $%c !!",c); 01353 } 01354 c = ' '; // Make sure we do not believe it is a quoted string. 01355 } 01356 } 01357 01358 return text; 01359 } 01360 01361 static map<string, unsigned int> count_refs_to_file; 01362 01364 // addReferenceToFile // 01366 void addReferenceToFile(const PPath& file) 01367 { 01368 if (file.isEmpty()) 01369 return; 01370 string s = file.canonical(); 01371 if (count_refs_to_file.find(s) != count_refs_to_file.end()) 01372 count_refs_to_file[s]++; 01373 else 01374 count_refs_to_file[s] = 1; 01375 } 01376 01378 // noReferenceToFile // 01380 bool noReferenceToFile(const PPath& file) 01381 { 01382 return nReferencesToFile(file) == 0; 01383 } 01384 01386 // nReferencesToFile // 01388 unsigned int nReferencesToFile(const PPath& file) 01389 { 01390 if (file.isEmpty()) 01391 return 0; 01392 string s = file.canonical(); 01393 if (count_refs_to_file.find(s) != count_refs_to_file.end()) 01394 return count_refs_to_file[s]; 01395 else 01396 return 0; 01397 } 01398 01400 // removeReferenceToFile // 01402 void removeReferenceToFile(const PPath& file) 01403 { 01404 if (file.isEmpty()) 01405 return; 01406 string s = file.canonical(); 01407 if (count_refs_to_file.find(s) != count_refs_to_file.end()) 01408 if (count_refs_to_file[s] == 0) 01409 PLERROR("In removeReferenceToFile - Trying to decrease the counter" 01410 " of references to file '%s', but it is already zero", 01411 file.absolute().c_str()); 01412 else 01413 count_refs_to_file[s]--; 01414 else 01415 PLERROR("In removeReferenceToFile - Trying to decrease the counter of " 01416 "references to file '%s', but it is not in the counter map", 01417 file.absolute().c_str()); 01418 } 01419 01420 } // end of namespace PLearn 01421 01422 01423 /* 01424 Local Variables: 01425 mode:c++ 01426 c-basic-offset:4 01427 c-file-style:"stroustrup" 01428 c-file-offsets:((innamespace . 0)(inline-open . 0)) 01429 indent-tabs-mode:nil 01430 fill-column:79 01431 End: 01432 */ 01433 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=79 :