libs/EDoc/utils.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 
00003    Copyright (C) 2007 by Brendon Costa
00004 
00005    This library is free software; you can redistribute it and/or modify 
00006    it under the terms of the "LGPL Like" License defined in the file COPYING 
00007    that should have been distributed along with this source.
00008 
00009    This library is distributed in the hope that it will be useful, 
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of 
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  
00012 
00013    You should have received a copy of the "LGPL Like" License 
00014    along with this library; see the file COPYING. if not, it can be 
00015    obtained from the EDoc++ website: 
00016    http://edoc.sourceforge.net/license.html
00017 
00018 *******************************************************************************/
00019 
00020 //==============================================================================
00021 /** \mainpage EDoc++ library documentation.
00022  * 
00023  * \section introduction Introduction
00024  * 
00025  * The EDoc++ library provides much of the code used to process EDoc++ data
00026  * files and to gather information about exception propogation from those data
00027  * files. The edoc and edc-arch applications both make use of this library. The
00028  * main purpose is to be able to load *.edc files, and then calculate the
00029  * complete list of propogating exceptions from the information provided
00030  * therein.
00031  *
00032  * \subsection introduction_main_objects MainObjects
00033  *
00034  * Following is a list of types that correspond directly to some logical
00035  * structure in the parsed source code:
00036  *
00037  *    - EDoc::CatchBlock
00038  *    - EDoc::CodeBlock
00039  *    - EDoc::Exception
00040  *    - EDoc::File
00041  *    - EDoc::Function
00042  *    - EDoc::FunctionType
00043  *    - EDoc::Location (Identifies a location in the source code file/line)
00044  *    - EDoc::PropogatingException
00045  *    - EDoc::TranslationUnit
00046  *    - EDoc::TryBlock
00047  *    - EDoc::Type
00048  *    .
00049  *
00050  *
00051  * \section overview Overview
00052  *
00053  * Following is the general procedure followed by users of this library:
00054  *
00055  * -# For each .edc file, create a EDoc::PersistenceIFace instance usually by 
00056  *    using EDoc::Persistence::ReadOpen().
00057  * -# For each .edc file, load its contents into a EDoc::Dictionary instance,
00058  *    using EDoc::Dictionary::Read().
00059  * -# Merge all the EDoc::Dictionary instances into a single EDoc::Dictionary
00060  *    instance. This is equivilant of linking multiple translation units into a
00061  *    single binary.
00062  * -# Calculate the complete list of exceptions for all functions. This is
00063  *    usually done by calling:
00064  *    EDoc::Dictionary::CalculatePropogatingExceptions().
00065  * .
00066  *
00067  * Finally after all this, a user is free to analyse the objects as they 
00068  * see fit. The edoc application performs any number of
00069  * checks over the resulting data to check for invalid usage of exceptions, and
00070  * also uses the data to produce doxygen documentation.
00071  *
00072  *
00073  * \todo @@@Brendon Describe the memory management structure in the library and
00074  * particular ownership of memory by Dictionary objects. Including String
00075  * identified objects, Dictionary specific objects, and the validation routines
00076  * and configure option.
00077  *
00078  * \todo @@@Brendon Describe the notification framework for indication of
00079  * warnings/errors while processing data.
00080  *
00081  * \todo @@@Brendon Describe the format of the data files and loadin/saving
00082  * procedures. Including Managed/Unmanaged files, the different formats and 
00083  * the use of the IndexedDictionary for multi-stage loading/saving.
00084  *
00085  * \todo @@@Brendon Describe the logging framework.
00086  *
00087  * \todo @@@Brendon Describe the format of the data files and loadin/saving
00088  * procedures.
00089  *
00090  * \todo @@@Brendon Describe the error handling strategy. Also when implement
00091  * new feature for safety guarentee, implement EDoc++ using this to demonstrate
00092  * and test it.
00093  */
00094 
00095 #include "config.h"
00096 
00097 #include "EDoc/utils.h"
00098 #include "EDoc/exceptions.h"
00099 
00100 #include <string>
00101 #include <vector>
00102 #include <iostream>
00103 
00104 
00105 #ifdef HAVE_STDLIB_H
00106    #include <stdlib.h>
00107 #endif
00108 
00109 #ifdef HAVE_STRING_H
00110    #include <string.h>
00111 #endif
00112 
00113 #ifdef HAVE_SYS_SELECT_H
00114    #include <sys/select.h>
00115 #endif
00116 
00117 #ifdef HAVE_SYS_WAIT_H
00118    #include <sys/wait.h>
00119 #endif
00120 
00121 #ifdef HAVE_SYS_STAT_H
00122    #include <sys/stat.h>
00123 #endif
00124 
00125 #ifdef HAVE_UNISTD_H
00126    #include <unistd.h>
00127 #endif
00128 
00129 #ifdef HAVE_ERRNO_H
00130    #include <errno.h>
00131 #endif
00132    
00133 // mkstemp
00134 //#include <stdlib.h>
00135 //#include <stdio.h>
00136 
00137 namespace EDoc
00138 {
00139    //===========================================================================
00140    std::list<std::string> open_temp_files;
00141    //===========================================================================
00142    bool PushBack(const std::string& name, 
00143       std::vector<std::string>& list)
00144    {
00145       bool already_contains = false;
00146       for (size_t j = 0; j < list.size(); j++)
00147       {
00148          if (list[j] == name)
00149          {
00150             already_contains = true;
00151          }
00152       }
00153 
00154       if (!already_contains)
00155       {
00156          list.push_back(name);
00157       }
00158 
00159       return !already_contains;
00160    }
00161    //===========================================================================
00162    std::string GetFileExtension(std::string name)
00163    {
00164       size_t pos = name.rfind('.');
00165       if (pos == std::string::npos)
00166       {
00167          return "";
00168       }
00169 
00170       return name.substr(pos + 1);
00171    }
00172    //===========================================================================
00173    std::pair<std::string, std::string> SplitFileExtension(std::string name)
00174    {
00175       size_t pos = name.rfind('.');
00176       if (pos == std::string::npos)
00177       {
00178          return std::pair<std::string, std::string>(name, "");
00179       }
00180 
00181       return std::pair<std::string, std::string>(name.substr(0, pos), 
00182          name.substr(pos + 1));
00183    }
00184    //===========================================================================
00185    std::string CreateTempFilename(std::string base)
00186    {
00187       const char* path = "/tmp/";
00188 
00189       char* b = new char[strlen(path) + base.size() + 6 + 1];
00190       strcpy(b, path);
00191       strcat(b, base.c_str());
00192       strcat(b, "XXXXXX");
00193 
00194       int fd = mkstemp(b);
00195       if (fd == -1)
00196       {
00197          delete [] b;
00198          EDOC_THROW_EXCEPTION(BugException, "", "");
00199       }
00200       close(fd);
00201 
00202       std::string f(b);
00203       delete [] b;
00204       b = NULL;
00205       return f;
00206    }
00207    //===========================================================================
00208    std::vector<std::string> Split(std::string str, char sep)
00209    {
00210       std::vector<std::string> ret;
00211 
00212       size_t last = 0;
00213       while (last != std::string::npos)
00214       {
00215          size_t index = str.find_first_of(sep, last);
00216          if (index != std::string::npos)
00217          {
00218             ret.push_back(str.substr(last, index - last));
00219             last = index + 1;
00220          }
00221          else
00222          {
00223             ret.push_back(str.substr(last));
00224             last = index;
00225          }
00226       }
00227 
00228       return ret;
00229    }
00230    //===========================================================================
00231    int RunProgram(std::string command_in)
00232    {
00233       // @@@Brendon Update to throw proper exceptions for errors.
00234       
00235       int pid = fork();
00236       switch (pid)
00237       {
00238          case -1:
00239          {
00240             std::cerr << "ERROR failed fork." << std::endl;
00241             EDOC_THROW_EXCEPTION(BugException, "", "");
00242             break;
00243          }
00244          case 0:
00245          {
00246             // Child.
00247             std::vector<std::string> sep = Split(command_in, ' ');
00248             size_t len = sep.size();
00249             char** argv = new char*[len + 1];
00250             for (size_t i = 0; i < len; i++)
00251             {
00252                argv[i] = new char[sep[i].size() + 1];
00253                strcpy(argv[i], sep[i].c_str());
00254                EDOC_Debug("argv: " << i << ", value: " << argv[i]);
00255                //argv[i] = (char*)sep[i].c_str();
00256             }
00257             argv[len] = NULL;
00258 
00259             EDOC_Debug("Executing command: " << command_in);
00260             EDOC_Debug("argv0: \"" << argv[0] << "\"");
00261 
00262             //if (execvp(command_in.c_str(), argv) == -1)
00263             if (execvp(argv[0], argv) == -1)
00264             {
00265                std::cerr << "ERROR: Failed exec. Reason: " << strerror(errno) 
00266                          << std::endl;
00267                exit(1);
00268             }
00269          }
00270          default:
00271          {
00272             // Parent.
00273             int status = 0;
00274             if (waitpid(pid, &status, 0) == -1)
00275             {
00276                EDOC_THROW_EXCEPTION(BugException, "", "");
00277             }
00278             return status;
00279          }
00280       }
00281       return 0;
00282    }
00283    //===========================================================================
00284    int RunProgramRedirect(std::string& out, std::string& err, std::string command_in)
00285    {
00286       // @@@Brendon Update to throw proper exceptions for errors.
00287       
00288       // First create the redirection pipes.
00289       static const size_t READ = 0;
00290       static const size_t WRITE = 1;
00291       
00292       int out_pipe[2];
00293       int err_pipe[2];
00294       int result = 0;
00295       
00296       out = "";
00297       err = "";
00298       
00299       result = pipe(out_pipe);
00300       if(result == -1)
00301       {
00302          std::cerr << "ERROR failed to create pipe. Reason: " << strerror(errno) << std::endl;
00303          EDOC_THROW_EXCEPTION(BugException, "", "");
00304       }
00305 
00306       result = pipe(err_pipe);
00307       if(result == -1)
00308       {
00309          // @@@Brendon Cleanup the out_pipe descriptors.
00310          std::cerr << "ERROR failed to create pipe. Reason: " << strerror(errno) << std::endl;
00311          EDOC_THROW_EXCEPTION(BugException, "", "");
00312       }
00313       
00314       int pid = fork();
00315       switch (pid)
00316       {
00317          case -1:
00318          {
00319             // @@@Brendon Cleanup the pipe descriptors.
00320             std::cerr << "ERROR failed fork. Reason: " << strerror(errno) << std::endl;
00321             EDOC_THROW_EXCEPTION(BugException, "", "");
00322             break;
00323          }
00324          case 0:
00325          {
00326             // Child.
00327             // Determine the command that we should run.
00328             std::vector<std::string> sep = Split(command_in, ' ');
00329             size_t len = sep.size();
00330             char** argv = new char*[len + 1];
00331             for (size_t i = 0; i < len; i++)
00332             {
00333                argv[i] = new char[sep[i].size() + 1];
00334                strcpy(argv[i], sep[i].c_str());
00335                EDOC_Debug("argv: " << i << ", value: " << argv[i]);
00336             }
00337             argv[len] = NULL;
00338 
00339             EDOC_Debug("Executing command: " << command_in);
00340             EDOC_Debug("argv0: \"" << argv[0] << "\"");
00341             
00342             // Close the un-needed side of the descriptors.
00343             close(out_pipe[READ]);
00344             close(err_pipe[READ]);
00345 
00346             // Duplicate the descriptors to the stdout stderr.
00347             result = dup2(out_pipe[WRITE], 1);
00348             if (result == -1)
00349             {
00350                std::cerr << "ERROR: Failed dup. Reason: " << strerror(errno) 
00351                          << std::endl;
00352                exit(1);
00353             }
00354 
00355             result = dup2(err_pipe[WRITE], 2);
00356             if (result == -1)
00357             {
00358                std::cerr << "ERROR: Failed dup. Reason: " << strerror(errno) 
00359                          << std::endl;
00360                exit(1);
00361             }
00362             
00363             // Close the now duplicated pipe.
00364             close(out_pipe[WRITE]);
00365             close(err_pipe[WRITE]);
00366             
00367             // Finally do a execvp to run the command replacing the current 
00368             // process.
00369             if (execvp(argv[0], argv) == -1)
00370             {
00371                std::cerr << "ERROR: Failed exec. Reason: " << strerror(errno) 
00372                          << std::endl;
00373                exit(1);
00374             }
00375          }
00376          default:
00377          {
00378             // Parent.
00379             
00380             // Close the un-used side of the pipe.
00381             close(out_pipe[WRITE]);
00382             close(err_pipe[WRITE]);
00383             
00384             // Go into a loop reading standard out and std err into the 
00385             // provided strings.
00386             bool out_eof = false;
00387             bool err_eof = false;
00388             static const int BUFSIZE = 100;
00389             char buf[BUFSIZE];
00390             while (!out_eof || !err_eof)
00391             {
00392                int max_descriptor = 0;
00393                fd_set descriptors;
00394                FD_ZERO(&descriptors);
00395                
00396                if (out_pipe[READ] > max_descriptor)
00397                {
00398                   max_descriptor = out_pipe[READ];
00399                }
00400                FD_SET(out_pipe[READ], &descriptors);
00401                
00402                if (err_pipe[READ] > max_descriptor)
00403                {
00404                   max_descriptor = err_pipe[READ];
00405                }
00406                FD_SET(err_pipe[READ], &descriptors);
00407 
00408                int result = select(max_descriptor + 1, 
00409                   &descriptors, NULL, NULL, NULL);
00410                if(result ==  -1)
00411                {
00412                   // @@@Brendon Cleanup the pipe descriptors.
00413                   std::cerr << "ERROR failed select. Reason: " << strerror(errno) << std::endl;
00414                   EDOC_THROW_EXCEPTION(BugException, "", "");
00415                }
00416                
00417                if (FD_ISSET(out_pipe[READ], &descriptors))
00418                {
00419                   result = read(out_pipe[READ], buf, BUFSIZE);
00420                   if (result == -1)
00421                   {
00422                      // @@@Brendon Cleanup the pipe descriptors.
00423                      std::cerr << "ERROR failed read. Reason: " << strerror(errno) << std::endl;
00424                      EDOC_THROW_EXCEPTION(BugException, "", "");
00425                   }
00426                   else if (result == 0)
00427                   {
00428                      out_eof = true;
00429                   }
00430                   out += std::string(buf, result);
00431                }
00432 
00433                if (FD_ISSET(err_pipe[READ], &descriptors))
00434                {
00435                   result = read(err_pipe[READ], buf, BUFSIZE);
00436                   if (result == -1)
00437                   {
00438                      // @@@Brendon Cleanup the pipe descriptors.
00439                      std::cerr << "ERROR failed read. Reason: " << strerror(errno) << std::endl;
00440                      EDOC_THROW_EXCEPTION(BugException, "", "");
00441                   }
00442                   else if (result == 0)
00443                   {
00444                      err_eof = true;
00445                   }
00446                   err += std::string(buf, result);
00447                }
00448             }
00449 
00450             close(out_pipe[READ]);
00451             close(err_pipe[READ]);
00452 
00453             int status = 0;
00454             if (waitpid(pid, &status, 0) == -1)
00455             {
00456                EDOC_THROW_EXCEPTION(BugException, "", "");
00457             }
00458             return status;
00459          }
00460       }
00461       return 0;
00462    }
00463    //===========================================================================
00464    static int mkdirs(char *path, mode_t mode)
00465    {
00466       size_t i;
00467       int retval = 0;
00468       
00469       if (!path[0])
00470       {
00471          std::cerr << "Path provided was empty." << std::endl;
00472          return 0;
00473       }
00474 
00475       for (i = 1; path[i] != 0; i++)
00476       {
00477          if (path[i] == '/')
00478          {
00479             path[i] = 0;
00480             EDOC_Debug("Making dir: "<< path);
00481             retval = mkdir(path, mode);
00482             if ((retval != 0) && (errno != EEXIST))
00483             {
00484                std::cerr << "Failed to create dir: " << path << std::endl;
00485                path[i] = '/';
00486                return retval;
00487             }
00488             path[i] = '/';
00489          }
00490       }
00491       
00492       retval = mkdir(path, mode);
00493       if ((retval != 0) && (errno != EEXIST))
00494       {
00495          std::cerr << "Failed to create dir: " << path << std::endl;
00496          return retval;
00497       }
00498 
00499       return 0;
00500    }
00501    //===========================================================================
00502    void MkDirs(std::string dir)
00503    {
00504       if (mkdirs(const_cast<char*>(dir.c_str()), 
00505                               S_IRUSR | S_IWUSR | S_IXUSR |
00506                               S_IRGRP | S_IWGRP | S_IXGRP |
00507                               S_IROTH | S_IWOTH | S_IXOTH) == -1)
00508       {
00509          EDOC_THROW_EXCEPTION(FileIOException, strerror(errno), 
00510             "Failed to create directory: " << dir);
00511       }
00512    }
00513    //===========================================================================
00514    void CopyFile(std::string source, std::string destination)
00515    {
00516       // @@@Brendon Should implement this properly at some point.
00517 
00518       // @@@Brendon Using system is the simplest way to copy this file. Should i
00519       // somehow sanitize this string to ensure nothing malicious is being done?
00520       std::ostringstream command;
00521       
00522       command << "cp " << source  << " " << destination;
00523       if (RunProgram(command.str()) != 0)
00524       {
00525          EDOC_THROW_EXCEPTION(FileIOException, "Failed to copy file.", "");
00526       }
00527    }
00528    //===========================================================================
00529    bool FileExists(std::string filename)
00530    {
00531       struct stat buffer;
00532       int result = stat(filename.c_str(), &buffer);
00533       if (result == -1)
00534       {
00535          return false;
00536       }
00537       return buffer.st_mode & S_IFREG;
00538    }
00539    //===========================================================================
00540    static const std::string white_space(" \t\r\n");
00541    std::string TrimWhitespace(const std::string& str)
00542    {
00543       std::string result = str;
00544       std::string::size_type pos;
00545 
00546       if ((pos = result.find_first_not_of(white_space)) == std::string::npos)
00547       {
00548          return std::string("");
00549       }
00550       else
00551       {
00552          result.erase(0, pos);
00553       }
00554       
00555       if ((pos = result.find_last_not_of(white_space)) != std::string::npos)
00556       {
00557          result.erase(pos+1);
00558       }
00559       
00560       return result;
00561    }
00562    //========================================================================
00563    std::string MakeTemporaryFile(std::string prefix, 
00564       std::string EDOC_UNUSED(suffix), std::string directory)
00565    {
00566       std::string ft = directory + "/" + prefix + "XXXXXX";// + suffix;
00567       
00568       // directory / prefix XXXXXX
00569       char* filename_template = new char[ft.size() + 1];
00570       strcpy(filename_template, ft.c_str());
00571 
00572       // Get the OS to create the temporary file for us.
00573       int result = mkstemp(filename_template);
00574       if (result == -1)
00575       {
00576          delete [] filename_template;
00577          filename_template = NULL;
00578 
00579          EDOC_THROW_EXCEPTION(FileIOException, strerror(errno), "Unable to create temporary file: " + ft);
00580       }
00581 
00582       // Cleanup and close the file descriptor.
00583       prefix = filename_template;
00584       delete [] filename_template;
00585       filename_template = NULL;
00586       result = close(result);
00587       if (result == -1)
00588       {
00589          EDOC_THROW_EXCEPTION(FileIOException, strerror(errno), "Failed closing descriptor for temporary file: " + ft);
00590       }
00591 
00592       return prefix;
00593    }
00594    //========================================================================
00595 }

Generated on Tue Jan 20 18:26:07 2009 for EDoc-0.2.1 by  doxygen 1.5.1