FileSpecFind.cpp
Go to the documentation of this file.
1 //==============================================================================
2 //
3 // This file is part of GNSSTk, the ARL:UT GNSS Toolkit.
4 //
5 // The GNSSTk is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU Lesser General Public License as published
7 // by the Free Software Foundation; either version 3.0 of the License, or
8 // any later version.
9 //
10 // The GNSSTk is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with GNSSTk; if not, write to the Free Software Foundation,
17 // Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 // This software was developed by Applied Research Laboratories at the
20 // University of Texas at Austin.
21 // Copyright 2004-2022, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 // This software was developed by Applied Research Laboratories at the
28 // University of Texas at Austin, under contract to an agency or agencies
29 // within the U.S. Department of Defense. The U.S. Government retains all
30 // rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 // Pursuant to DoD Directive 523024
33 //
34 // DISTRIBUTION STATEMENT A: This software has been approved for public
35 // release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 #include "TimeString.hpp"
40 #include "FileSpecFind.hpp"
41 #ifndef WIN32
42 #include <glob.h>
43 #define PATH_SEP_STRING "/"
44 #else
45 #include "build_config.h" // for getFileSep
46 #define PATH_SEP_STRING "/\\"
47 #include <windows.h>
48 #include <shlwapi.h>
50 typedef struct
51 {
52  size_t gl_pathc;
53  char **gl_pathv;
54  size_t gl_offs;
55 } glob_t;
56 // These are defined to minimize changes, the code doesn't actually
57 // implement different behavior that is implied by these flags.
58 #define GLOB_ERR 0x0001
59 #define GLOB_NOSORT 0x0002
60 #define GLOB_TILDE 0x0000
61 #define GLOB_NOSPACE 1
62 #define GLOB_ABORTED 2
63 #define GLOB_NOMATCH 3
64 #endif
65 
66 using namespace std;
67 
68 
69 #ifdef WIN32
70 
76 static bool winMatchRE(const std::string& pattern, const std::string& test)
77 {
78  // change wildcards to regular expressions, making sure to match
79  // the whole string.
80  std::string patternRE = "^" + gnsstk::StringUtils::change(pattern,".","\\.") +
81  "$";
82  patternRE = gnsstk::StringUtils::change(patternRE,"*",".*");
83  patternRE = gnsstk::StringUtils::change(patternRE,"?",".");
84  std::regex re(patternRE, std::regex::basic);
85  std::smatch m;
86  std::regex_search(test, m, re);
87  return !m.empty();
88 }
89 
90 
100 static void winGlob(const char *pattern, std::list<std::string>& results)
101 {
102  WIN32_FIND_DATA findFileData;
103  HANDLE hFindFile;
104  std::string patternStr(pattern);
105  if (patternStr.empty())
106  return;
107  std::string::size_type pos = patternStr.find_first_of("*?["), pos2;
113  pos = patternStr.find_last_of(PATH_SEP_STRING, pos);
114  if (pos != std::string::npos)
115  {
116  pos++; // skip over the file separator.
117  pos2 = patternStr.find(PATH_SEP_STRING, pos);
118  std::string pathSearch = patternStr.substr(0,pos2);
119  // turn POSIX glob pattern into windows pattern before trying to find
120  pathSearch = gnsstk::StringUtils::change(pathSearch, "[0-9]", "?");
121  std::string matchPattern = patternStr.substr(pos,pos2-pos);
122  hFindFile = FindFirstFile(pathSearch.c_str(), &findFileData);
123  if (hFindFile != INVALID_HANDLE_VALUE)
124  {
125  do
126  {
127  // Check to make sure the results match the globbing
128  // pattern, since windows globbing is much less
129  // sophisticated than POSIX.
130  if (winMatchRE(matchPattern, findFileData.cFileName))
131  {
132  // Keep the path that matches at the current level
133  std::string match(
134  patternStr.substr(0,pos) + findFileData.cFileName);
135  // Add the remaining pattern, if any, for recursive search
136  std::string newPattern(
137  match + gnsstk::getFileSep() + patternStr.substr(pos2+1));
138  // check if we have more directory layers to go in
139  // the pattern and the match is a directory.
140  if ((pos2 != std::string::npos) &&
141  (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
142  {
143  // recurse into the matching directory
144  winGlob(newPattern.c_str(), results);
145  }
146  else if (pos2 == std::string::npos)
147  {
148  // add the match to our results
149  results.push_back(match);
150  }
151  }
152  } while(FindNextFile(hFindFile, &findFileData));
153  FindClose(hFindFile);
154  }
155  }
156  else
157  {
158  // If we're here, then we're looking for a file with no path
159  // and no wildcards, so just try to match the pattern
160  // directly.
161  hFindFile = FindFirstFile(pattern, &findFileData);
162  if (hFindFile != INVALID_HANDLE_VALUE)
163  {
164  results.push_back(pattern);
165  }
166  }
167 }
168 
169 
178 static int glob(const char *pattern, int flags,
179  int (*errfunc) (const char *epath, int eerrno),
180  glob_t *pglob)
181 {
182  pglob->gl_pathc = 0;
183  pglob->gl_pathv = nullptr;
184  std::list<std::string> results;
185  winGlob(pattern, results);
186  if (results.empty())
187  return GLOB_NOMATCH;
188  pglob->gl_pathc = results.size();
189  pglob->gl_pathv = (char**)malloc(pglob->gl_pathc * sizeof(char*));
190  if (pglob->gl_pathv == nullptr)
191  return GLOB_NOSPACE;
192  unsigned j = 0;
193  for (const auto& i : results)
194  {
195  pglob->gl_pathv[j] = (char*)malloc(i.length() + 1);
196  if (pglob->gl_pathv[j] == nullptr)
197  return GLOB_NOSPACE;
198  strcpy(pglob->gl_pathv[j], i.c_str());
199  j++;
200  }
201  return 0;
202 }
203 
204 
210 static void globfree(glob_t *pglob)
211 {
212  for (unsigned i = 0; i < pglob->gl_pathc; i++)
213  {
214  free(pglob->gl_pathv[i]);
215  }
216  free(pglob->gl_pathv);
217  pglob->gl_pathv = nullptr;
218  pglob->gl_pathc = 0;
219 }
220 #endif
221 
222 namespace gnsstk
223 {
224  list<string> FileSpecFind ::
225  find(const std::string& fileSpecString,
226  const gnsstk::CommonTime& start,
227  const gnsstk::CommonTime& end,
228  const gnsstk::FileSpec::FSTStringMap& fsts)
229  {
230  // This first pile of code replaces text tokens of frequently
231  // unknown size with fixed sizes. If you use FileSpec to
232  // create a filename that has a token %x or %4x and you don't
233  // have the "text" value set in fsts, FileSpec will not fill
234  // in any default. It will for most other tokens, however.
236  fsType = gnsstk::FileSpec::text,
238  gnsstk::FileSpec::FSTStringMap dummyFSTS(fsts);
239  string spec(fileSpecString);
240  unsigned textLen = dummyFSTS[fsType].length();
241  if (textLen == 0)
242  {
243  // If we don't have a text value set in fsts, make one up.
244  // It likely won't match anyway.
245  dummyFSTS[fsType] = "Z";
246  textLen = 1;
247  }
248  string textTok = gnsstk::FileSpec::convertFileSpecType(fsType);
249  string textLenS = gnsstk::StringUtils::asString(textLen);
250  unsigned selLen = dummyFSTS[fsSel].length();
251  if (selLen == 0)
252  {
253  // If we don't have a selection value set in fsts, make
254  // one up. It likely won't match anyway.
255  dummyFSTS[fsSel] = "Z"; // whatever, no meaning behind it
256  selLen = 1;
257  }
258  string selTok = gnsstk::FileSpec::convertFileSpecType(fsSel);
259  string selLenS = gnsstk::StringUtils::asString(textLen);
260  // Just change the arbitrary text token to the value we have,
261  // since there's at most one value to match.
262  gnsstk::StringUtils::change(spec, "%"+textTok, dummyFSTS[fsType]);
263  gnsstk::StringUtils::change(spec, "%"+selTok, "%"+selLenS+selTok);
264  // Fill in other defaults, which will get set to a fixed
265  // width by FileSpec.
266  for (unsigned i = gnsstk::FileSpec::unknown;
268  {
270  if ((fst == FileSpec::fixed) || (fst == FileSpec::unknown))
271  continue;
272  if (dummyFSTS.find(fst) == dummyFSTS.end())
273  dummyFSTS[fst] = "";
274  }
275 
276  Filter filter;
277 
278  return findGlob(start, end, spec, dummyFSTS, filter);
279  }
280 
281 
282  std::list<std::string> FileSpecFind ::
283  find(const std::string& fileSpec,
284  const CommonTime& start,
285  const CommonTime& end,
286  const Filter& filter)
287  {
288  // The filter can contain multiple values.
289  // "Cowardly refusing to implement variable width FileSpec"
290  FileSpec::FSTStringMap dummyFSTS;
291  // still gotta fill it, though, because even fixed width
292  // tokens won't get translated if there's no value, e.g. %5n
293  // will stay %5n after converting to string if there's no
294  // station value in dummyFSTS
295  for (unsigned i = gnsstk::FileSpec::unknown;
297  {
299  if ((fst == FileSpec::fixed) || (fst == FileSpec::unknown))
300  continue;
301  dummyFSTS[fst] = "";
302  }
303  return findGlob(start, end, fileSpec, dummyFSTS, filter);
304  }
305 
306 
307  string FileSpecFind ::
308  transToken(const string& token)
309  {
310  // chew through a FileSpec string changing tokens to glob patterns
311  string rv;
312  string::size_type spos = 0;
313  string textTok = gnsstk::FileSpec::convertFileSpecType(
319  while (true)
320  {
321  string::size_type ppos = token.find('%', spos);
322  if (ppos == string::npos)
323  {
324  rv += token.substr(spos, ppos);
325  break;
326  }
327  rv += token.substr(spos, ppos-spos);
328  string::size_type fpos = token.find_first_not_of("0123456789", ppos+1);
329  unsigned long len = 0;
330  char *end;
331  if ((fpos - ppos) > 0)
332  {
333  len = std::strtoul(&token[ppos+1], &end, 10);
334  }
335  if (len == 0)
336  {
337  rv += "*";
338  }
339  else
340  {
341  if ((token[fpos] == textTok[0]) || // arbitrary text
342  (token[fpos] == selTok[0]) || // selected
343  (token[fpos] == stnTok[0])) // station ID
344  {
345  // text values, just use single character matches for
346  // the appropriate length
347  rv += string(len, '?');
348  }
349  else
350  {
351  // is there a more efficient way to make multiple
352  // copies of a string?
353  for (unsigned long c = 0; c < len; c++)
354  {
355  // numeric value, match the range for however many
356  // characters the token width specifies.
357  rv += "[0-9]";
358  }
359  }
360  }
361  spos = fpos+1;
362  }
363  return rv;
364  }
365 
366 
367  /* This function recurses into a path, each level of recursion
368  * mapping to a level of the path hierarchy in which FileSpec
369  * tokens exist.
370  * e.g.
371  * 1 2 3
372  * 0123456789012345678901234567890123456789
373  * spec: /data/%04Y/%05n/%03j/...
374  * Level 0 ^^^^^^^^^^^^^^^^^^^^
375  * Level 1 ^^^^
376  * Level 2 ^^^^
377  *
378  * The function works by picking out the path in spec up to and
379  * including the first token after pos. This segment of the
380  * path (thisSpec in the code) is then used in two ways. First,
381  * the spec is used to create a pair of times that are fromTime
382  * and toTime reduced to the granularity available at that level
383  * (fromTimeMatch and toTimeMatch). At Level 0 in the above
384  * example, that would reduce the fromTime and toTime values to
385  * just the year.
386  *
387  * Second, the path segment is converted into a globbing pattern
388  * which is then used to find all files/directories on the file
389  * system that could *potentially* match the spec. A couple of
390  * potential match examples for Level 0 above:
391  * /data/9999
392  * /data/2018
393  * NOT a match:
394  * /data/NOPE
395  * /data/something
396  * /data/9123s
397  *
398  * Results from the glob function are then processed using
399  * FileSpec to get the time using thisSpec which is then
400  * compared with the fromTimeMatch and toTimeMatch computed
401  * above to make sure that the path falls within the requested
402  * time span at least to the time granularity available at that
403  * level (again, the granularity at level 0 would be year).
404  *
405  * Results that pass the time span test are then rolled back
406  * into a recursive call to findGlob, and this is where
407  * "matched" and "pos" come into play.
408  *
409  * Recursive calls use the results from glob() that pass the
410  * time test as the "matched" data, and the length of thisSpec
411  * as pos so that the called findGlob will replace the current
412  * path segment with the current match. All of this is done so
413  * that when recursing into subdirectories, ONLY those
414  * subdirectories that match the desired range are explored
415  * further.
416  *
417  * Example:
418  * .....Level 0..........................
419  * spec : /data/%04Y/%05n/%03j/nsh-FOO-%5n-%1r-%04Y-%03j-%02H%02M%02S.xml
420  * matched :
421  * pos : 0
422  * thisSpec : /data/%04Y
423  * pattern : /data/[0-9][0-9][0-9][0-9]
424  * fromTimeMatch: 2018 1 0.000000
425  * toTimeMatch : 2018 1 0.000000
426  * glob matches:
427  * /data/2017
428  * 2017 1 0.000000 not a time range match
429  * /data/2018
430  * 2018 1 0.000000 TIME RANGE MATCHED
431  *
432  *
433  * .....Level 1..........................
434  * spec : /data/%04Y/%05n/%03j/nsh-FOO-%5n-%1r-%04Y-%03j-%02H%02M%02S.xml
435  * matched : /data/2018
436  * pos : 38
437  * thisSpec : /data/%04Y/%05n
438  * pattern : /data/2018/[0-9][0-9][0-9][0-9][0-9]
439  * fromTimeMatch: 2018 1 0.000000
440  * toTimeMatch : 2018 1 0.000000
441  * glob matches:
442  * /data/2018/85789
443  * 2018 1 0.000000 TIME RANGE MATCHED
444  *
445  *
446  * .....Level 2..........................
447  * spec : /data/%04Y/%05n/%03j/nsh-FOO-%5n-%1r-%04Y-%03j-%02H%02M%02S.xml
448  * matched : /data/2018/85789
449  * pos : 43
450  * thisSpec : /data/%04Y/%05n/%03j
451  * pattern : /data/2018/85789/[0-9][0-9][0-9]
452  * fromTimeMatch: 2018 211 0.000000
453  * toTimeMatch : 2018 212 0.000000
454  * glob matches:
455  * /data/2018/85789/218
456  * 2018 218 0.000000 not a time range match
457  * /data/2018/85789/211
458  * 2018 211 0.000000 TIME RANGE MATCHED
459  *
460  *
461  * .....Level 3..........................
462  * spec : /data/%04Y/%05n/%03j/nsh-FOO-%5n-%1r-%04Y-%03j-%02H%02M%02S.xml
463  * matched : /data/2018/85789/211
464  * pos : 48
465  * thisSpec : /data/%04Y/%05n/%03j/nsh-FOO-%5n-%1r-%04Y-%03j-%02H%02M%02S.xml
466  * pattern : /data/2018/85789/211/nsh-FOO-[0-9][0-9][0-9][0-9][0-9]-[0-9]-[0-9][0-9][0-9][0-9]-[0-9][0-9][0-9]-[0-9][0-9][0-9][0-9][0-9][0-9].xml
467  * fromTimeMatch: 2018 211 0.000000
468  * toTimeMatch : 2018 212 0.000000
469  * glob matches:
470  * /data/2018/85789/211/nsh-FOO-85789-1-2018-211-184500.xml
471  * 2018 211 67500.000000 TIME RANGE MATCHED
472  */
473  list<string> FileSpecFind ::
474  findGlob(const gnsstk::CommonTime& fromTime,
475  const gnsstk::CommonTime& toTime,
476  const string& spec,
477  const gnsstk::FileSpec::FSTStringMap& dummyFSTS,
478  const Filter& filter,
479  const string& matched,
480  string::size_type pos)
481  {
482  list<string> rv;
483  // level 0:
484  // /data/%04Y/%05n/%03j/nsh-FOO-%5n-%1r-%04Y-%03j-%02H%02M%02S.xml
485  // 12 3
486  // 1 = stoppos
487  // 2 = stokpos
488  // 3 = srest
489  //
490  // level 3:
491  // /data/%04Y/%05n/%03j/nsh-FOO-%5n-%1r-%04Y-%03j-%02H%02M%02S.xml
492  // 1 2 3
493  // 1 = stoppos
494  // 2 = stokpos
495  // 3 = srest
496 
497  // find the first part of the path that contains a FileSpec token
498  string::size_type stokpos = spec.find('%', pos);
499  // find the beginning of the remaining path (subdirectory or
500  // file within the directory starting at stokpos)
501  string::size_type stoppos = min(
502  stokpos, spec.find_last_of(PATH_SEP_STRING, stokpos));
503  // srest is the first character of the "rest" of the path
504  // i.e. lower depths in the tree.
505  string::size_type srest =
506  (stokpos == string::npos
507  ? string::npos
508  : spec.find_first_of(PATH_SEP_STRING, stokpos+1));
509  // thisSpec is JUST the part of the path that we've already searched
510  string thisSpec(spec.substr(0, srest));
511  // patternSpec takes the parts of the original spec that have
512  // already been matched and replaces that with the "matched"
513  // string which fixes us into the specific directory tree
514  // we're searching with glob
515  string patternSpec(thisSpec);
516  patternSpec.replace(0, pos, matched);
517  // pattern is the same as patternSpec but with all the
518  // FileSpec tokens replaced with glob patterns.
519  string pattern = transToken(patternSpec);
520  // currentSpec and currentSpecScanner contain the file spec
521  // for just the currently searched directory. We do this to
522  // minimize the amount of effort done in matching, because
523  // we're doing recursive searches there's zero reason to
524  // check upper level directories against the filter, or time
525  // range if there are no time-related spec types at this
526  // level, as they will have already been checked.
527  string currentSpec = thisSpec.substr(stoppos+1);
528  FileSpec currentSpecScanner(currentSpec);
529  bool checkTime = currentSpecScanner.hasTimeField();
530 
531  // Use the current spec to turn our time range into something
532  // that will be useable to match at this level in the
533  // heirarchy.
534  gnsstk::CommonTime fromTimeMatch, toTimeMatch;
535  gnsstk::FileSpec specScanner(thisSpec);
536  if (checkTime)
537  {
538  string fromString = specScanner.toString(fromTime, dummyFSTS);
539  string toString = specScanner.toString(toTime, dummyFSTS);
540  fromTimeMatch = specScanner.extractCommonTime(fromString);
541  toTimeMatch = specScanner.extractCommonTime(toString);
542  // Make sure our conditions can be met, i.e. from <= t < to
543  // by adding 1/10th of a second to "to" if from and to are the same
544  if (toTimeMatch == fromTimeMatch)
545  toTimeMatch += 0.1;
546  }
547 
548  // Find all the files that match the pattern at this level.
549  glob_t globbuf;
550  int g = glob(pattern.c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_TILDE, nullptr,
551  &globbuf);
552  for (size_t i = 0; i < globbuf.gl_pathc; i++)
553  {
554  bool timeMatched = true;
555  if (checkTime)
556  {
557  // check if the matched files/directories are within
558  // the search time
559  gnsstk::CommonTime fileTime =
560  specScanner.extractCommonTime(globbuf.gl_pathv[i]);
561  timeMatched = ((fromTimeMatch <= fileTime) &&
562  (fileTime < toTimeMatch));
563  }
564  if (timeMatched)
565  {
566  // File's/directory's time is within the desired span.
567  // Allow optional filter to determine whether to recurse further
568  bool matchedFilter = true;
569  if (!filter.empty())
570  {
571  // Iterate through the filter data, attempting to
572  // match the filter values with the spec. Do NOT
573  // use the increment operator on the iterator as
574  // part of a for loop as that will cause the loop to
575  // get stuck at the end of the multimap.
576  Filter::const_iterator fi = filter.begin();
577  // Keep the value of the most recently extracted
578  // field so we don't spend time extracting it over
579  // and over if multiple values for a given
580  // FileSpecType are specified.
581  std::string fieldVal;
582  // Keep track of the FileSpecType associated with
583  // the value in fieldVal
584  FileSpec::FileSpecType lastFST = FileSpec::unknown;
585  while (fi != filter.end())
586  {
587  // Check to see if the current directory level of
588  // the file spec contains the FileSpecType
589  // pointed to by the filter iterator. We compare
590  // the FST against lastFST first because it's
591  // really fast compared to hasField().
592  if ((fi->first == lastFST) ||
593  currentSpecScanner.hasField(fi->first))
594  {
595  // thisSpec, which only matches the current
596  // directory level, contains this particular
597  // FileSpecType, so check to see if the value
598  // matches.
599  if (fi->first != lastFST)
600  {
601  // extract the field value from the path
602  // which we haven't done yet.
603  fieldVal = specScanner.extractField(globbuf.gl_pathv[i],
604  fi->first);
605  lastFST = fi->first;
606  }
607  if (fi->second == fieldVal)
608  {
609  // field value in the path matches, so move
610  // on to the next FileSpecType
611  fi = filter.upper_bound(fi->first);
612  }
613  else
614  {
615  // try the next filter value
616  fi++;
617  // but check to make sure we haven't
618  // exhausted all possible matches for a
619  // Filtered field.
620  if ((fi == filter.end()) ||
621  (fi->first != lastFST))
622  {
623  matchedFilter = false;
624  break;
625  }
626  }
627  }
628  else
629  {
630  // currentSpec, which only matches the current
631  // directory level, doesn't contain this
632  // particular FileSpecType, so skip to the
633  // next FileSpecType.
634  fi = filter.upper_bound(fi->first);
635  }
636  }
637  } // if (!filter.empty())
638 
639  if (matchedFilter)
640  {
641  // If srest is npos, that means there is no more path
642  // depth and no more recursion to process.
643  if (srest == string::npos)
644  {
645  rv.push_back(globbuf.gl_pathv[i]);
646  }
647  else
648  {
649  // Still more path depth to go, recurse.
650  list<string> toAdd =
651  findGlob(fromTime, toTime, spec, dummyFSTS, filter,
652  globbuf.gl_pathv[i], thisSpec.length());
653  rv.splice(rv.end(), toAdd);
654  }
655  } // if (matchedFilter)
656  } // if ((fromTimeMatch <= fileTime) && (fileTime < toTimeMatch))
657  } // for (size_t i = 0; i < globbuf.gl_pathc; i++)
658  globfree(&globbuf);
659  return rv;
660  }
661 }
662 
gnsstk::FileSpec::FSTStringMap
std::map< FileSpecType, std::string > FSTStringMap
Definition: FileSpec.hpp:136
gnsstk::FileSpecFind::Filter
std::multimap< FileSpec::FileSpecType, std::string > Filter
Data type for storing desired FileSpec values.
Definition: FileSpecFind.hpp:87
gnsstk::FileSpec::hasTimeField
bool hasTimeField() const
Return true if this FileSpec has any time fields.
Definition: FileSpec.hpp:194
gnsstk::FileSpec::selected
@ selected
't' A field for selected/unselected receiver
Definition: FileSpec.hpp:99
gnsstk::StringUtils::asString
std::string asString(IonexStoreStrategy e)
Convert a IonexStoreStrategy to a whitespace-free string name.
Definition: IonexStoreStrategy.cpp:46
gnsstk
For Sinex::InputHistory.
Definition: BasicFramework.cpp:50
gnsstk::FileSpec::unknown
@ unknown
Unknown type.
Definition: FileSpec.hpp:95
gnsstk::FileSpec
Definition: FileSpec.hpp:80
gnsstk::FileSpec::firstTime
@ firstTime
First enumeration value pertaining to time.
Definition: FileSpec.hpp:111
gnsstk::FileSpec::extractCommonTime
virtual gnsstk::CommonTime extractCommonTime(const std::string &filename) const
Definition: FileSpec.cpp:247
gnsstk::FileSpec::extractField
virtual std::string extractField(const std::string &filename, const FileSpecType) const
Definition: FileSpec.cpp:216
gnsstk::CommonTime
Definition: CommonTime.hpp:84
gnsstk::min
T min(const SparseMatrix< T > &SM)
Maximum element - return 0 if empty.
Definition: SparseMatrix.hpp:858
gnsstk::FileSpec::hasField
virtual bool hasField(FileSpecType fst) const
Definition: FileSpec.hpp:190
example4.pos
pos
Definition: example4.py:125
gnsstk::FileSpec::station
@ station
'n' A field for station numbers
Definition: FileSpec.hpp:96
gnsstk::FileSpec::text
@ text
Definition: FileSpec.hpp:105
gnsstk::FileSpec::FileSpecType
FileSpecType
Definition: FileSpec.hpp:93
gnsstk::FileSpec::toString
virtual std::string toString(const gnsstk::CommonTime &dt, const FSTStringMap &fstsMap=FSTStringMap()) const
Definition: FileSpec.cpp:278
std
Definition: Angle.hpp:142
PATH_SEP_STRING
#define PATH_SEP_STRING
Definition: FileSpecFind.cpp:43
gnsstk::StringUtils::change
std::string change(const std::string &aString, const std::string &inputString, const std::string &outputString, std::string::size_type startPos=0, unsigned numChanges=(std::numeric_limits< unsigned >().max()))
Definition: StringUtils.hpp:1521
FileSpecFind.hpp
TimeString.hpp
gnsstk::FileSpec::convertFileSpecType
static std::string convertFileSpecType(const FileSpecType)
Definition: FileSpec.cpp:106


gnsstk
Author(s):
autogenerated on Wed Oct 25 2023 02:40:39