1 /* -------------------------------------------------------------------------- *
2  *                              OpenSim:  IO.cpp                              *
3  * -------------------------------------------------------------------------- *
4  * The OpenSim API is a toolkit for musculoskeletal modeling and simulation.  *
5  * See http://opensim.stanford.edu and the NOTICE file for more information.  *
6  * OpenSim is developed at Stanford University and supported by the US        *
7  * National Institutes of Health (U54 GM072970, R24 HD065690) and by DARPA    *
8  * through the Warrior Web program.                                           *
9  *                                                                            *
10  * Copyright (c) 2005-2017 Stanford University and the Authors                *
11  * Author(s): Frank C. Anderson                                               *
12  *                                                                            *
13  * Licensed under the Apache License, Version 2.0 (the "License"); you may    *
14  * not use this file except in compliance with the License. You may obtain a  *
15  * copy of the License at http://www.apache.org/licenses/LICENSE-2.0.         *
16  *                                                                            *
17  * Unless required by applicable law or agreed to in writing, software        *
18  * distributed under the License is distributed on an "AS IS" BASIS,          *
19  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.   *
20  * See the License for the specific language governing permissions and        *
21  * limitations under the License.                                             *
22  * -------------------------------------------------------------------------- */
23 
24 /* Note: This code was originally developed by Realistic Dynamics Inc.
25  * Author: Frank C. Anderson
26  */
27 
28 
29 // INCLUDES
30 #include <time.h>
31 #include <math.h>
32 #include <string>
33 #include <climits>
34 
35 #include "IO.h"
36 #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__)
37     #include <sys/stat.h>
38     #include <sys/types.h>
39 #elif defined(_MSC_VER)
40     #include <direct.h>
41 #else
42     #include <unistd.h>
43 #endif
44 
45 // PATH stuff from Kenny
46 #ifdef _MSC_VER
47     #include <direct.h>
48     #define PATH_MAX _MAX_PATH
49 #else
50     #include <unistd.h>
51 #endif
52 
53 // CONSTANTS
54 
55 
56 using namespace OpenSim;
57 using namespace std;
58 
59 // STATICS
60 bool IO::_Scientific = false;
61 bool IO::_GFormatForDoubleOutput = false;
62 int IO::_Pad = 8;
63 int IO::_Precision = 8;
64 char IO::_DoubleFormat[] = "%16.8lf";
65 bool IO::_PrintOfflineDocuments = true;
66 
67 
68 //=============================================================================
69 // FILE NAME UTILITIES
70 //=============================================================================
71 //-----------------------------------------------------------------------------
72 // TIME STAMP
73 //-----------------------------------------------------------------------------
74 //_____________________________________________________________________________
75 /**
76  * Construct a date and time stamp with the format YYYYMMDD_HHMMSS.
77  *
78  * @return Null terminated character array of the stamp.  The caller must
79  * delete this memory.
80  */
81 char* IO::
ConstructDateAndTimeStamp()82 ConstructDateAndTimeStamp()
83 {
84     // GET DATE AND TIME
85     time_t timeInSeconds;
86     struct tm *timeStruct;
87     time(&timeInSeconds);
88     timeStruct = localtime(&timeInSeconds);
89 
90     // CONSTRUCT STAMP
91     char *stamp = new char[64];
92     sprintf(stamp,"%d%02d%02d_%02d%02d%02d",
93         timeStruct->tm_year+1900,timeStruct->tm_mon+1,timeStruct->tm_mday,
94         timeStruct->tm_hour,timeStruct->tm_min,timeStruct->tm_sec);
95 
96     return(stamp);
97 }
98 //-----------------------------------------------------------------------------
99 // FIXING SLASHES IN PATH
100 //-----------------------------------------------------------------------------
101 //_____________________________________________________________________________
102 /**
103  * Ensures slashes are back slashes ('\') in Windows and forward slashes ('/') in Linux.
104  */
105 std::string IO::
FixSlashesInFilePath(const std::string & path)106 FixSlashesInFilePath(const std::string &path)
107 {
108     std::string fixedPath = path;
109     for(unsigned int i=0;i<fixedPath.length();i++) {
110 #ifdef _WIN32
111         if(fixedPath[i] == '/') fixedPath[i] = '\\';
112 #else
113         if(fixedPath[i] == '\\') fixedPath[i] = '/';
114 #endif
115     }
116     return fixedPath;
117 }
118 
119 //=============================================================================
120 // NUMBERED OUTPUT
121 //=============================================================================
122 //-----------------------------------------------------------------------------
123 // SCIENTIFIC
124 //-----------------------------------------------------------------------------
125 //_____________________________________________________________________________
126 /**
127  * Set whether or not output of numbers should be in scientific or float
128  * format.
129  *
130  * @param aTrueFalse Scientific notation if true, and float if false.
131  */
132 void IO::
SetScientific(bool aTrueFalse)133 SetScientific(bool aTrueFalse)
134 {
135     _Scientific = aTrueFalse;
136     ConstructDoubleOutputFormat();
137 }
138 
139 //_____________________________________________________________________________
140 /**
141  * Set whether or not output of numbers should be in scientific or float
142  * format.
143  *
144  * @return True if scientific notation, false if float.
145  */
146 bool IO::
GetScientific()147 GetScientific()
148 {
149     return(_Scientific);
150 }
151 
152 //-----------------------------------------------------------------------------
153 // %g formatting for doubles
154 //-----------------------------------------------------------------------------
155 //_____________________________________________________________________________
156 /**
157  * Set whether or not output of numbers should be printed using %g.
158  */
159 void IO::
SetGFormatForDoubleOutput(bool aTrueFalse)160 SetGFormatForDoubleOutput(bool aTrueFalse)
161 {
162     _GFormatForDoubleOutput = aTrueFalse;
163     ConstructDoubleOutputFormat();
164 }
165 
166 //_____________________________________________________________________________
167 /**
168  */
169 bool IO::
GetGFormatForDoubleOutput()170 GetGFormatForDoubleOutput()
171 {
172     return(_GFormatForDoubleOutput);
173 }
174 
175 //-----------------------------------------------------------------------------
176 // PAD
177 //-----------------------------------------------------------------------------
178 //_____________________________________________________________________________
179 /**
180  * Set the number of digits with which to pad output of numbers.
181  * The pad number is used to set the minimum field width by the following
182  * formula:
183  *      width = pad + precision
184  *
185  * The formated output string, for example, would have the form
186  *      %width.precisionlf.
187  *
188  * @param aPad Number of digits with which output of numbers is padded.
189  */
190 void IO::
SetDigitsPad(int aPad)191 SetDigitsPad(int aPad)
192 {
193     if(aPad<0) aPad = -1;
194     _Pad = aPad;
195     ConstructDoubleOutputFormat();
196 }
197 //_____________________________________________________________________________
198 /**
199  * Get the number of digits with which output of numbers is padded.
200  * The pad number is used to set the minimum field width by the following
201  * formula:
202  *      width = pad + precision
203  *
204  * The formated output string, for example, would have the form
205  *      %width.precisionlf.
206  *
207  * @return aPad Number of digits with which output of number is padded.
208  */
209 int IO::
GetDigitsPad()210 GetDigitsPad()
211 {
212     return(_Pad);
213 }
214 
215 //-----------------------------------------------------------------------------
216 // PRECISION
217 //-----------------------------------------------------------------------------
218 //_____________________________________________________________________________
219 /**
220  * Set the precision with which numbers are output.
221  * The precision is usually simply the number of decimal places.
222  *
223  * @param aPrecision Precision.
224  */
225 void IO::
SetPrecision(int aPrecision)226 SetPrecision(int aPrecision)
227 {
228     if(aPrecision<0) aPrecision = 0;
229     _Precision = aPrecision;
230     ConstructDoubleOutputFormat();
231 }
232 //_____________________________________________________________________________
233 /**
234  * Get the precision with which numbers are output.
235  * The precision is usually simply the number of decimal places.
236  *
237  * @return Precision (often number of decimal places).
238  */
239 int IO::
GetPrecision()240 GetPrecision()
241 {
242     return(_Precision);
243 }
244 
245 //-----------------------------------------------------------------------------
246 // OUTPUT FORMAT
247 //-----------------------------------------------------------------------------
248 //_____________________________________________________________________________
249 /**
250  * Get the current output format for numbers of type double.
251  *
252  * The format is maintained as a static variable in class IO, so any
253  * changes made to the output parameters in class IO will be seen globally
254  * by all classes using this method.
255  *
256  * The returned output format will be of the form
257  *
258  *      "% width.precision lf" (if not scientific)  or
259  *      "% width.precision le" (if scientific),
260  *
261  * where the spaces have been removed and width = pad + precision.
262  *
263  * @return Format string for output of doubles.
264  * @see SetScientific(), SetDigitsPad, SetPrecision.
265  */
266 const char* IO::
GetDoubleOutputFormat()267 GetDoubleOutputFormat()
268 {
269     return(_DoubleFormat);
270 }
271 
272 //_____________________________________________________________________________
273 /**
274  * Construct a valid output format for numbers of type double.
275  * This method is called each time a change is made to any of the output
276  * format parameters.
277  *
278  * @see SetScientific(), SetDigitsPad, SetPrecision.
279  */
280 void IO::
ConstructDoubleOutputFormat()281 ConstructDoubleOutputFormat()
282 {
283     if(_GFormatForDoubleOutput) {
284         sprintf(_DoubleFormat,"%%g");
285     } else if(_Scientific) {
286         if(_Pad<0) {
287             sprintf(_DoubleFormat,"%%.%dle",_Precision);
288         } else {
289             sprintf(_DoubleFormat,"%%%d.%dle",_Pad+_Precision,_Precision);
290         }
291     } else {
292         if(_Pad<0) {
293             sprintf(_DoubleFormat,"%%.%dlf",_Precision);
294         } else {
295             sprintf(_DoubleFormat,"%%%d.%dlf",_Pad+_Precision,_Precision);
296         }
297     }
298 }
299 
300 //=============================================================================
301 // Object printing
302 //=============================================================================
303 //_____________________________________________________________________________
304 /**
305  * If Object::print is called and an xml node has a file attribute pointing
306  * to an external file, then the _PrintOfflineDocuments determines whether
307  * that file's contents will be printed to that file or not.
308  * It's "offline" because it represents non-inlined xml code.
309  */
310 void IO::
SetPrintOfflineDocuments(bool aTrueFalse)311 SetPrintOfflineDocuments(bool aTrueFalse)
312 {
313     _PrintOfflineDocuments = aTrueFalse;
314 }
315 //_____________________________________________________________________________
316 /**
317  */
318 bool IO::
GetPrintOfflineDocuments()319 GetPrintOfflineDocuments()
320 {
321     return _PrintOfflineDocuments;
322 }
323 //=============================================================================
324 // READ
325 //=============================================================================
326 //_____________________________________________________________________________
327 /**
328  * Read lines from the file until a line consisting entirely of a
329  * token is read or until the end of the file.  The file pointer is
330  * advanced to the first character after the token.
331  *
332  * Memory is allocated for the description, so the caller is responsible
333  * for deleting the returned description.
334  *
335  * @param aIS input stream.
336  * @param aToken Token to which to read.  The token should not contain
337  * a carriage return.
338  * @return String up to a token line.  The ending carriage
339  * return is included in the returned string.
340  */
341 string IO::
ReadToTokenLine(istream & aIS,const string & aToken)342 ReadToTokenLine(istream &aIS,const string &aToken)
343 {
344     string text;
345     while(aIS) {
346         string line = IO::ReadLine(aIS);
347         if(line == aToken) break;
348         text+=line+"\n";
349     }
350     return text;
351 }
352 
353 //_____________________________________________________________________________
354 /**
355  * Read a line of text from file.
356  *
357  * The terminating carriage return is not included in the returned string.
358  *
359  * This handles both DOS- and Unix-style line endings.
360  *
361  * @param aIS Input stream.
362  * @return Line of text not including the terminating carriage return.
363  */
364 string IO::
ReadLine(istream & aIS)365 ReadLine(istream &aIS)
366 {
367     std::string line;
368     getline(aIS, line);
369     int len=(int)line.length();
370     // deal with reading a DOS-format file in Linux
371     if(len>0 && line[len-1]=='\r') line=line.substr(0,len-1);
372     return line;
373 }
374 //_____________________________________________________________________________
375 /**
376  * Compute the number of steps of a given size to get from some initial time to
377  * some final time.
378  *
379  * If (aTF-aTI)/aDT has a remainder that is greater than 1% of the step size,
380  * an additional step is assumed to be needed to step completely to the end
381  * of the interval.
382  *
383  * @param aTI Initial time.
384  * @param aTF Final time.
385  * @param aDT Step size.  Should be greater than 0.0.
386  */
387 int IO::
ComputeNumberOfSteps(double aTI,double aTF,double aDT)388 ComputeNumberOfSteps(double aTI,double aTF,double aDT)
389 {
390     if(aDT<=0) return(0);
391 
392     double duration = aTF-aTI;
393     int ns = (int)floor(duration/aDT);
394     if((ns*aDT)<(duration-1.0e-2*aDT)) {
395         ns += 2;
396     } else {
397         ns += 1;
398     }
399 
400     return(ns);
401 }
402 //_____________________________________________________________________________
403 /**
404  * Read a specified number of characters from file.
405  *
406  * @param aIS Input stream.
407  * @param aNChar Number of characters in the description.
408  * @return read string.
409  */
410 string IO::
ReadCharacters(istream & aIS,int aNChar)411 ReadCharacters(istream &aIS,int aNChar)
412 {
413     char *buffer=new char[aNChar+1];
414     aIS.read(buffer,aNChar);
415     buffer[aIS.gcount()] = '\0';
416     string str = buffer;
417     delete[] buffer;
418     return str;
419 }
420 
FileExists(const std::string & filePath)421 bool IO::FileExists(const std::string& filePath) {
422     return std::ifstream(filePath).good();
423 }
424 
425 //_____________________________________________________________________________
426 /**
427  * Open a file.
428  */
429 FILE* IO::
OpenFile(const string & aFileName,const string & aMode)430 OpenFile(const string &aFileName,const string &aMode)
431 {
432     FILE *fp = NULL;
433 
434     // OPEN THE FILE
435     fp = fopen(aFileName.c_str(),aMode.c_str());
436     if(fp==NULL) {
437         printf("IO.OpenFile(const string&,const string&): failed to open %s\n",
438          aFileName.c_str());
439         return(NULL);
440     }
441 
442     return(fp);
443 }
444 //_____________________________________________________________________________
445 /**
446  * Open a file.
447  */
448 ifstream *IO::
OpenInputFile(const string & aFileName,ios_base::openmode mode)449 OpenInputFile(const string &aFileName,ios_base::openmode mode)
450 {
451     ifstream *fs = new ifstream(aFileName.c_str(), ios_base::in | mode);
452     if(!fs || !(*fs)) {
453         printf("IO.OpenInputFile(const string&,openmode mode): failed to open %s\n", aFileName.c_str());
454         return(NULL);
455     }
456 
457     return(fs);
458 }
459 ofstream *IO::
OpenOutputFile(const string & aFileName,ios_base::openmode mode)460 OpenOutputFile(const string &aFileName,ios_base::openmode mode)
461 {
462     ofstream *fs = new ofstream(aFileName.c_str(), ios_base::out | mode);
463     if(!fs || !(*fs)) {
464         printf("IO.OpenOutputFile(const string&,openmode mode): failed to open %s\n", aFileName.c_str());
465         return(NULL);
466     }
467 
468     return(fs);
469 }
470 //_____________________________________________________________________________
471 /**
472  * Create a directory. Potentially platform dependent.
473   * @return int 0 on success, EEXIST or other error condition
474 */
475 int IO::
makeDir(const string & aDirName)476 makeDir(const string &aDirName)
477 {
478 
479 #if defined __linux__ || defined __APPLE__ || defined __FreeBSD__ || defined(__DragonFly__)
480     return mkdir(aDirName.c_str(),S_IRWXU);
481 #else
482     return _mkdir(aDirName.c_str());
483 #endif
484 }
485 //_____________________________________________________________________________
486 /**
487  * Change working directory. Potentially platform dependent.
488   * @return int 0 on success, error condition otherwise
489 */
490 int IO::
chDir(const string & aDirName)491 chDir(const string &aDirName)
492 {
493 
494 #if defined __linux__ || defined __APPLE__ || defined __FreeBSD__ || defined(__DragonFly__)
495     return chdir(aDirName.c_str());
496 #else
497     return _chdir(aDirName.c_str());
498 #endif
499 
500 }
501 //_____________________________________________________________________________
502 /**
503  * Get current working directory. Potentially platform dependent.
504   * @return working directory on success, NULL on error
505 */
506 string IO::
getCwd()507 getCwd()
508 {
509     char buffer[PATH_MAX];
510 #if defined __linux__ || defined __APPLE__ || defined __FreeBSD__ || defined(__DragonFly__)
511     auto ptr = getcwd(buffer, PATH_MAX); (void)ptr;
512 #else
513     _getcwd(buffer, PATH_MAX);
514 #endif
515     return string(buffer);
516 }
517 
518 //_____________________________________________________________________________
519 /**
520  * Get parent directory of the passed in fileName.
521  *
522 */
523 string IO::
getParentDirectory(const string & fileName)524 getParentDirectory(const string& fileName)
525 {
526     string  result="";
527 
528     string::size_type dirSep = fileName.rfind('/'); // Unix/Mac dir separator
529 
530     if (dirSep == string::npos)
531         dirSep = fileName.rfind('\\'); // DOS dir separator
532 
533     if (dirSep != string::npos) // if '_fileName' contains path information...
534         result = fileName.substr(0,dirSep+1); // include trailing slashes
535 
536     return result;
537 }
538 
539 //_____________________________________________________________________________
540 /**
541  * Get filename part of a passed in URI (also works if a DOS/Unix path is passed in)
542  *
543 */
544 string IO::
GetFileNameFromURI(const string & aURI)545 GetFileNameFromURI(const string& aURI)
546 {
547     string  result=aURI;
548 
549     string::size_type dirSep = aURI.rfind('/'); // Unix/Mac dir separator
550 
551     if (dirSep == string::npos)
552         dirSep = aURI.rfind('\\'); // DOS dir separator
553 
554     if (dirSep != string::npos) // if aURI contains path information...
555         result = aURI.substr(dirSep+1);
556 
557     return result;
558 }
559 
560 // TODO: account for '\n' inside comments
561 string IO::
formatText(const string & aComment,const string & leadingWhitespace,int width,const string & endlineTokenToInsert)562 formatText(const string& aComment,const string& leadingWhitespace,int width,const string& endlineTokenToInsert)
563 {
564     string formatted;
565     int count = 0;
566     string::size_type i = 0;
567     for(;;) {
568         string::size_type pos = aComment.find_first_not_of(" \t\n", i);
569         if(pos==string::npos) break;
570         string whitespace = aComment.substr(i, pos-i);
571         int newlineCount = 0;
572         for(string::size_type j=0;j<whitespace.size();j++) {
573             if (whitespace[j]=='\t') whitespace[j]=' ';
574             else if (whitespace[j]=='\n') newlineCount++;
575         }
576         string::size_type pos2 = aComment.find_first_of(" \t\n", pos);
577         string word;
578         if(pos2==string::npos) word = aComment.substr(pos);
579         else word = aComment.substr(pos, pos2-pos);
580         i = pos2;
581         if(!newlineCount && count+whitespace.size()+word.size()<=(string::size_type)width) {
582             formatted += whitespace+word;
583             count += (int)(whitespace.size()+word.size());
584         } else {
585             if(!formatted.empty()) {
586                 if(newlineCount) for(int j=0;j<newlineCount-1;j++) formatted += endlineTokenToInsert;
587                 formatted += endlineTokenToInsert + leadingWhitespace;
588             }
589             formatted += word;
590             count = (int)word.size();
591         }
592     }
593     return formatted;
594 }
595 
596 string IO::
GetSuffix(const std::string & aStr,int aLen)597 GetSuffix(const std::string &aStr, int aLen)
598 {
599     const int sz = (int)aStr.size();
600     return aStr.substr((sz>=aLen ? sz-aLen : 0));
601 }
602 
603 void IO::
RemoveSuffix(std::string & rStr,int aLen)604 RemoveSuffix(std::string &rStr, int aLen)
605 {
606     const int sz = (int)rStr.size();
607     rStr.erase((sz>=aLen ? sz-aLen : 0));
608 }
609 
610 string IO::
replaceSubstring(const std::string & aStr,const std::string & aFrom,const std::string & aTo)611 replaceSubstring(const std::string &aStr, const std::string &aFrom, const std::string &aTo)
612 {
613     string result = aStr;
614     for(string::size_type i = string::npos;;) {
615         i = result.rfind(aFrom, i);
616         if(i==string::npos) break;
617         result.replace(i, aFrom.size(), aTo);
618         if(i==0) break; else i--;
619     }
620     return result;
621 }
622 
623 void IO::
TrimLeadingWhitespace(std::string & rStr)624 TrimLeadingWhitespace(std::string &rStr)
625 {
626     string::size_type front = rStr.find_first_not_of(" \t\r\n");
627     rStr.erase(0, front);
628 }
629 
630 void IO::
TrimTrailingWhitespace(std::string & rStr)631 TrimTrailingWhitespace(std::string &rStr)
632 {
633     string::size_type back = rStr.find_last_not_of(" \t\r\n");
634     if(back < rStr.size()-1) rStr.erase(back+1);
635 }
636 
637 std::string IO::
Lowercase(const std::string & aStr)638 Lowercase(const std::string &aStr)
639 {
640     std::string result = aStr;
641     for(unsigned int i=0; i<aStr.size(); i++) result[i] = tolower(result[i]);
642     return result;
643 }
644 
645 std::string IO::
Uppercase(const std::string & aStr)646 Uppercase(const std::string &aStr)
647 {
648     std::string result = aStr;
649     for(unsigned int i=0; i<aStr.size(); i++) result[i] = toupper(result[i]);
650     return result;
651 }
652 
eraseEmptyElements(std::vector<std::string> & list)653 void IO::eraseEmptyElements(std::vector<std::string>& list)
654 {
655     std::vector<std::string>::iterator it = list.begin();
656     while (it != list.end()) {
657         if (it->empty())
658             it = list.erase(it);
659         else
660             ++it;
661     }
662 }
663