/alib/src/a_env.cpp

https://code.google.com/p/csvfix/ · C++ · 497 lines · 284 code · 93 blank · 120 comment · 85 complexity · 11d07b7e9ee96e8a09491091cc656581 MD5 · raw file

  1. //---------------------------------------------------------------------------
  2. // a_env.cpp
  3. //
  4. // environment & command line stuff for alib
  5. //
  6. // Copyright (C) 2008 Neil Butterworth
  7. //---------------------------------------------------------------------------
  8. #include <stdlib.h>
  9. #include "a_base.h"
  10. #include "a_assert.h"
  11. #include "a_dict.h"
  12. #include "a_except.h"
  13. #include "a_str.h"
  14. #include "a_env.h"
  15. #include <map>
  16. #include <cstdlib>
  17. // need win api if doing our own globbing on windows
  18. #include "a_win.h"
  19. #include "a_dir.h"
  20. using std::string;
  21. using std::vector;
  22. //---------------------------------------------------------------------------
  23. // Alib stuff starts here
  24. //---------------------------------------------------------------------------
  25. namespace ALib {
  26. //---------------------------------------------------------------------------
  27. // Single command line instance
  28. //---------------------------------------------------------------------------
  29. CommandLine * CommandLine::mInstance = 0;
  30. //---------------------------------------------------------------------------
  31. // Transform args into vector of strings and set instance pointer
  32. //---------------------------------------------------------------------------
  33. CommandLine :: CommandLine( int argc, char * argv[] )
  34. : mArgs( argv, argv + argc ) {
  35. if ( mInstance != 0 ) {
  36. ATHROW( "Only one CommandLine object allowed" );
  37. }
  38. mInstance = this;
  39. }
  40. //---------------------------------------------------------------------------
  41. // Reset instance pointer
  42. //---------------------------------------------------------------------------
  43. CommandLine :: ~CommandLine() {
  44. mInstance = 0;
  45. }
  46. //---------------------------------------------------------------------------
  47. // Get command line instance
  48. //---------------------------------------------------------------------------
  49. CommandLine * CommandLine :: Instance() {
  50. if ( mInstance == 0 ) {
  51. ATHROW( "No CommandLine object exists" );
  52. }
  53. return mInstance;
  54. }
  55. //---------------------------------------------------------------------------
  56. // How many args - same as arc in main()
  57. //---------------------------------------------------------------------------
  58. int CommandLine :: Argc() const {
  59. return mArgs.size();
  60. }
  61. //---------------------------------------------------------------------------
  62. // Get arg by integer index - asame as argv[] in main
  63. //---------------------------------------------------------------------------
  64. const string & CommandLine :: Argv( int i ) const {
  65. if ( i < 0 || i >= Argc() ) {
  66. ATHROW( "Invalid command line parameter index: " << i );
  67. }
  68. return mArgs[i];
  69. }
  70. //----------------------------------------------------------------------------
  71. // Clear all args and files
  72. //----------------------------------------------------------------------------
  73. void CommandLine :: Clear() {
  74. mArgs.clear();
  75. mFiles.clear();
  76. }
  77. //----------------------------------------------------------------------------
  78. // Add arg to args list
  79. //----------------------------------------------------------------------------
  80. void CommandLine :: Add( const std::string & arg ) {
  81. mArgs.push_back( arg );
  82. }
  83. //---------------------------------------------------------------------------
  84. // Get index of flag, returning -1 if does not exist
  85. //---------------------------------------------------------------------------
  86. int CommandLine :: FindFlag( const string & name ) const {
  87. for ( int i = 0; i < Argc(); i++ ) {
  88. if ( Argv(i) == name ) {
  89. return i;
  90. }
  91. }
  92. return -1;
  93. }
  94. //---------------------------------------------------------------------------
  95. // How many occurences of a flag are there?
  96. //---------------------------------------------------------------------------
  97. unsigned int CommandLine :: FlagCount( const string & name ) const {
  98. unsigned int count = 0;
  99. for ( int i = 0; i < Argc(); i++ ) {
  100. if ( Argv(i) == name ) {
  101. count++;
  102. }
  103. }
  104. return count;
  105. }
  106. //---------------------------------------------------------------------------
  107. // See if flag exists - this (and all other flag functions) is
  108. // case sensitive and requires the exact flag including leading
  109. // dashes or whatever.
  110. //---------------------------------------------------------------------------
  111. bool CommandLine :: HasFlag( const string & name ) const {
  112. return FindFlag( name ) >= 0;
  113. }
  114. //---------------------------------------------------------------------------
  115. // Get value for parameter of flag or default value if param does not exist
  116. //---------------------------------------------------------------------------
  117. string CommandLine :: GetValue( const string & name,
  118. const string & defval ) const {
  119. int i = FindFlag( name );
  120. if ( i < 0 || i == Argc() - 1 ) {
  121. return defval;
  122. }
  123. else {
  124. return Argv( i + 1 );
  125. }
  126. }
  127. //---------------------------------------------------------------------------
  128. // Get all values for a particular flag
  129. //---------------------------------------------------------------------------
  130. unsigned int CommandLine :: GetValues( const string & name,
  131. vector <string> & vals,
  132. const string & defval ) const {
  133. for ( int i = 1; i < Argc(); i++ ) {
  134. if ( Argv(i) == name ) {
  135. string val = i < Argc() - 1 ? Argv( i + 1 ) : defval;
  136. if ( val != "" && val[0] == '-' ) {
  137. val = defval;
  138. }
  139. vals.push_back( val );
  140. }
  141. }
  142. return vals.size();
  143. }
  144. //---------------------------------------------------------------------------
  145. // Get value for flag or throw if flag does not exist
  146. //---------------------------------------------------------------------------
  147. string CommandLine :: MustGetValue( const string & name ) const {
  148. int i = FindFlag( name );
  149. if ( i < 0 || i == Argc() - 1 ) {
  150. ATHROW( "Required value for option " << name << " not found" );
  151. }
  152. return Argv( i + 1 );
  153. }
  154. //---------------------------------------------------------------------------
  155. // Add a flag to list of flags that the command line knows about
  156. //---------------------------------------------------------------------------
  157. void CommandLine :: AddFlag( const CommandLineFlag & f ) {
  158. if ( mFlagDict.Contains( f.Name() ) ) {
  159. ATHROW( "Duplicate option: " << f.Name() );
  160. }
  161. mFlagDict.Add( f.Name(), f );
  162. }
  163. //---------------------------------------------------------------------------
  164. // User-callable function to check that the command line uses only the
  165. // specified flags. Also gets the list of filenames from the command line.
  166. // Throws exceptions if validation of flags fails.
  167. //---------------------------------------------------------------------------
  168. void CommandLine :: CheckFlags( unsigned int start ) {
  169. CheckRequiredFlags( start );
  170. CheckMultiFlags( start );
  171. int pos = start;
  172. while( pos < Argc() ) {
  173. string a = Argv( pos );
  174. if ( ! IsEmpty( a ) ) {
  175. if ( a == "-" || a[0] != '-' ) { // stdio dash or not flag
  176. CheckNoMoreFlags( pos );
  177. break;
  178. }
  179. else {
  180. const CommandLineFlag * f = mFlagDict.GetPtr( a );
  181. if ( f == 0 ) {
  182. ATHROW( "Invalid option: " << a );
  183. }
  184. else if ( f->ParamCount() ) {
  185. if ( pos + 1 == Argc() ) {
  186. ATHROW( "Option has no value: " << a );
  187. }
  188. string param = Argv( ++pos );
  189. if ( param.size() && param[0] == '-' ) {
  190. ATHROW( "Option has no value: " << a << " " << param );
  191. }
  192. }
  193. }
  194. }
  195. pos++;
  196. }
  197. BuildFileList( start );
  198. }
  199. //---------------------------------------------------------------------------
  200. // See if flag exists on command line starting at 'start' - this lets us
  201. // ignore things that aren't flags, such as sub-commands.
  202. //---------------------------------------------------------------------------
  203. bool CommandLine :: HasFlag( const string & name, unsigned int start ) {
  204. for ( int i = start; i < Argc(); i++ ) {
  205. if ( Argv( i ) == name ) {
  206. return true;
  207. }
  208. }
  209. return false;
  210. }
  211. //---------------------------------------------------------------------------
  212. // Check that any multiple uses of same flag are allowed
  213. //---------------------------------------------------------------------------
  214. void CommandLine :: CheckMultiFlags( unsigned int start ) {
  215. std::map <string, int> flagcount;
  216. for ( int i = start; i < Argc(); i++ ) {
  217. string f = Argv(i);
  218. if ( f.size() && f != "-" && f[0] == '-' ) {
  219. flagcount[f]++;
  220. }
  221. }
  222. std::map <string,int>::const_iterator it = flagcount.begin();
  223. while( it != flagcount.end() ) {
  224. if ( it->second > 1 ) {
  225. const CommandLineFlag * f = mFlagDict.GetPtr( it->first );
  226. if ( f && ! f->MultipleOK() ) {
  227. ATHROW( "Multiple " << f->Name() << " options not allowed" );
  228. }
  229. }
  230. ++it;
  231. }
  232. }
  233. //---------------------------------------------------------------------------
  234. // Check that flags that are required are present
  235. //---------------------------------------------------------------------------
  236. void CommandLine :: CheckRequiredFlags( unsigned int start ) {
  237. vector <string> names;
  238. mFlagDict.GetNames( names );
  239. for ( unsigned int i = 0; i < names.size(); i++ ) {
  240. const CommandLineFlag * f = mFlagDict.GetPtr( names[i] );
  241. if ( f->Required() && ! HasFlag( names[i], start ) ) {
  242. ATHROW( "Required option " << names[i] << " missing" ;)
  243. }
  244. }
  245. }
  246. //---------------------------------------------------------------------------
  247. // Check that there are no more flags on the command line after 'start'
  248. //---------------------------------------------------------------------------
  249. void CommandLine :: CheckNoMoreFlags( unsigned int start ) {
  250. int pos = start;
  251. while( pos < Argc() ) {
  252. string a = Argv( pos );
  253. if ( ! IsEmpty( a ) && a != "-" && a[0] == '-' ) {
  254. ATHROW( "Unexpected value " << SQuote( Argv( start ) ) );
  255. }
  256. pos++;
  257. }
  258. }
  259. //---------------------------------------------------------------------------
  260. // Construct list of files on command line. File names come after
  261. // any flags, so we work backwards until we get a flag.
  262. // On Windows, we need to do our own filename globbing, as we need
  263. // not to glob parameters for regex flags.
  264. //---------------------------------------------------------------------------
  265. unsigned int CommandLine :: BuildFileList( unsigned int start ) {
  266. mFiles.clear();
  267. // work backwards to find last flag
  268. unsigned int pos = Argc() - 1, lastflag = 0;
  269. while( pos >= start ) {
  270. string arg = Argv( pos );
  271. if ( arg.size() && arg != "-" && arg.at(0) == '-' ) {
  272. lastflag = pos;
  273. break;
  274. }
  275. pos--;
  276. }
  277. unsigned int filestart = 0;
  278. if ( lastflag ) {
  279. // if there was a flag then start is one past flag
  280. // plus any paramaeter count
  281. const CommandLineFlag * f = mFlagDict.GetPtr( Argv( lastflag ) );
  282. AASSERT( f != 0 ); // we already validated flags
  283. filestart = lastflag + 1 + f->ParamCount();
  284. }
  285. else {
  286. // otherwise its right at the start
  287. filestart = start;
  288. }
  289. // save all file names
  290. for ( int i = filestart; i < Argc(); i++ ) {
  291. #ifdef ALIB_WINAPI // do our own globbing for windows
  292. DirList dir( Argv( i ) );
  293. if ( dir.Count() == 0 ) {
  294. mFiles.push_back( Argv( i ) );
  295. }
  296. else {
  297. for( unsigned int i = 0; i < dir.Count(); i++ ) {
  298. mFiles.push_back( dir.At(i)->Name() );
  299. }
  300. }
  301. #else
  302. // on linux/unix we have already globbed
  303. mFiles.push_back( Argv( i ) );
  304. #endif
  305. }
  306. return mFiles.size();
  307. }
  308. //---------------------------------------------------------------------------
  309. // How many files?
  310. //---------------------------------------------------------------------------
  311. unsigned int CommandLine :: FileCount() const {
  312. return mFiles.size();
  313. }
  314. //---------------------------------------------------------------------------
  315. // Get a file.
  316. //---------------------------------------------------------------------------
  317. string CommandLine :: File( unsigned int i ) const {
  318. if ( i >= mFiles.size() ) {
  319. ATHROW( "Invalid CommandLine file index: " << i );
  320. }
  321. return mFiles[ i ];
  322. }
  323. //---------------------------------------------------------------------------
  324. // Command line flags have a name (e.g. -f), an indicator of whether they
  325. // are required or not, and the number of parameters they can have (this
  326. // can currently only be 1 or 0).
  327. //---------------------------------------------------------------------------
  328. CommandLineFlag :: CommandLineFlag( const string & name,
  329. bool reqd,
  330. unsigned int pcount,
  331. bool multi )
  332. : mName( name ), mRequired( reqd ),
  333. mParamCount( pcount ), mMulti( multi ) {
  334. if ( pcount > 1 ) {
  335. ATHROW( "Invalid CommandLine parameter count: " << pcount );
  336. }
  337. }
  338. //---------------------------------------------------------------------------
  339. // Accessors
  340. //---------------------------------------------------------------------------
  341. string CommandLineFlag :: Name() const {
  342. return mName;
  343. }
  344. bool CommandLineFlag :: Required() const {
  345. return mRequired;
  346. }
  347. unsigned int CommandLineFlag :: ParamCount() const {
  348. return mParamCount;
  349. }
  350. bool CommandLineFlag :: MultipleOK() const {
  351. return mMulti;
  352. }
  353. //---------------------------------------------------------------------------
  354. // Populate the dictionary from command line environment pointer
  355. //---------------------------------------------------------------------------
  356. Environment :: Environment( char * env[] ) {
  357. unsigned int i = 0;
  358. while( env[i] ) {
  359. mDict.AddNVP( env[i] );
  360. i++;
  361. }
  362. }
  363. //---------------------------------------------------------------------------
  364. // Get value by name, or empty string if doesn't exist
  365. //---------------------------------------------------------------------------
  366. string Environment :: Get( const string & name ) const {
  367. return mDict.Get( name, "" );
  368. }
  369. //---------------------------------------------------------------------------
  370. // See if we have env variable
  371. //---------------------------------------------------------------------------
  372. bool Environment :: Contains( const string & name ) const {
  373. return mDict.Contains( name );
  374. }
  375. //---------------------------------------------------------------------------
  376. // Dump environment for debug
  377. //---------------------------------------------------------------------------
  378. void Environment :: DumpOn( std::ostream & os ) const {
  379. mDict.DumpOn( os );
  380. }
  381. //----------------------------------------------------------------------------
  382. // Simple functions to get env values without using objects.
  383. //----------------------------------------------------------------------------
  384. string GetEnv( const string & ev ) {
  385. const char * val = std::getenv( ev.c_str() );
  386. return val == 0 ? "" : val;
  387. }
  388. bool EnvContains( const string & ev ) {
  389. return std::getenv( ev.c_str() ) != 0;
  390. }
  391. //---------------------------------------------------------------------------
  392. } // namespace
  393. //----------------------------------------------------------------------------
  394. #ifdef ALIB_TEST
  395. #include "a_myth.h"
  396. using namespace ALib;
  397. using namespace std;
  398. DEFSUITE( "a_env" );
  399. DEFTEST( EnvTest ) {
  400. FAILNE( EnvContains( "PATH" ), true );
  401. FAILNE( GetEnv( "PATH" ).size() > 0, true );
  402. }
  403. #endif
  404. // end