PLearn 0.1
fileutils.cc
Go to the documentation of this file.
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 :
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines