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