Subversion Repositories Programming Utils

Rev

Rev 48 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 rm5248 1
/* dirlist.cpp
2
 *
3
 * $Id: dirlist.cpp,v 1.1 2006/06/08 02:14:18 swm Exp $
4
 *
5
 * $Log: dirlist.cpp,v $
6
 * Revision 1.1  2006/06/08 02:14:18  swm
7
 * Initial revision
8
 *
9
 */
10
 
11
#include <ctype.h>
12
#include <dirent.h>
13
#include <iostream>
14
#include <fstream>
15
#include <sstream>
5 rm5248 16
#include <vector>
17
#include <sstream>
18
#include <algorithm>
19
#include <iterator>
20
#include <string.h>
1 rm5248 21
 
22
#include "dirlist.h"
23
 
24
using namespace std;
25
 
26
/* Constructor for Inserter object that just initializes fields
27
 * of struct
28
 */
29
FileListInserter::FileListInserter( const set<string> &f, const char *suf )
30
  : files(f), suffix(suf) {}
31
 
32
/* Insertion operator for the previous inserter object. Writes a
33
 * space-separated list of file names in f.
34
 */
35
ostream & operator<<( ostream &os, const FileListInserter &ins ) {
78 rm5248 36
  int current_line_len = 0;
37
 
1 rm5248 38
  for( set<string>::const_iterator it = ins.files.begin();
39
       it != ins.files.end();
40
       ++it ) {
41
    if( it != ins.files.begin() ) { // don't put space before first item
42
      os << " ";
43
    }
47 rm5248 44
 
1 rm5248 45
    if( ins.suffix ) {
78 rm5248 46
      if( ( DirList::basename( *it ).size() + current_line_len ) >= 60 ){
47
        //if this would put us over 60 characters on the line,
48
        //put a newline.  can't use tellp when redirecting to file
49
        os << "\\\n";
47 rm5248 50
      }
1 rm5248 51
      os << DirList::basename( *it ) << ins.suffix;
52
    } else {
78 rm5248 53
      if( ( ( *it ).size() + current_line_len ) >= 60 ){
54
        //if this would put us over 60 characters on the line,
47 rm5248 55
        //put a newline
78 rm5248 56
        os << "\\\n";
47 rm5248 57
      }
1 rm5248 58
      os << *it;
59
    }
78 rm5248 60
 
61
    current_line_len += ( *it ).size();
47 rm5248 62
 
1 rm5248 63
  }
64
  return os;
65
}
66
 
48 rm5248 67
/**
68
 * The default constructor goes through the default directory,
69
 * and all subdirectories to find files.
70
 */
71
DirList::DirList(){
72
  DIR* dir;
73
 
74
  dir = opendir( "." );
75
  if( dir != NULL ){
76
    scan_dirs( dir );
77
  }
78
  closedir( dir );
79
}
80
 
1 rm5248 81
/* DirList
82
 *
5 rm5248 83
 *
84
 */
85
DirList::DirList(map<string,string>& config){
86
  //
6 rm5248 87
  // Read the directory and store all the files we find there
5 rm5248 88
  //
89
  vector<string> tokens;
90
  char modified_string[ config["dirs"].size() + 1];
91
  memcpy( modified_string, config["dirs"].c_str(), config["dirs"].size() + 1 );
92
  char* ptr = strtok( modified_string, ":");
93
  while( ptr != NULL ){
94
    tokens.push_back( string( ptr ) );
95
    ptr = strtok( NULL, ":");
96
  }
97
 
98
  for( unsigned int x = 0; x < tokens.size(); x++ ){
48 rm5248 99
    DIR *dir( opendir( tokens[x].c_str() ) );
5 rm5248 100
 
48 rm5248 101
    if( dir != NULL ){
102
      scan_single_dir( dir );
103
      closedir( dir );
104
    } else {
105
      cerr << "Error opening directory " << tokens[x] << "\n";
5 rm5248 106
    }
107
  }
108
}
109
 
110
/* DirList
111
 *
1 rm5248 112
 * Read the current directory, and categorize the files found there
113
 * according to their suffix, saving them on the appropriate list.
114
 * If command line arguments were given, read that list to find
115
 * .C, .cpp, .c, and .s files, and ignore those found in the directory.
116
 * We always read .h files.
117
 */
78 rm5248 118
DirList::DirList( const char **av ){
1 rm5248 119
  //
120
  // Read the directory and store all the files we find there,
121
  // EXCEPT if arguments were given, those are the only .C, .c, .cpp, and .s
122
  // files we're interested in, so skip any found here.
123
  //
124
  DIR *dir( opendir( "." ) );
78 rm5248 125
  unsigned int ac = 0;
1 rm5248 126
 
127
  if( dir != 0 ){
48 rm5248 128
    scan_single_dir( dir, ac );
1 rm5248 129
  } else {
130
    cerr << "Error opening current directory\n";
78 rm5248 131
    return;
1 rm5248 132
  }
133
 
134
  //
135
  // Copy the names from the argument list into their vector.
136
  //
78 rm5248 137
    while( av[ ac ] != NULL ){
138
    string name( av[ ac++ ] );
1 rm5248 139
 
140
    switch( fileType( name ) ) {
141
    case CPP:
142
      insert_source( hasMainFunction, name, cpp, cpp_main, cpp_other );
143
      break;
144
 
145
    case C:
146
      insert_source( hasMainFunction, name, c, c_main, c_other );
147
      break;
148
 
149
    case ASSEMBLY:
150
      insert_source( hasMainLabel, name, ass, ass_main, ass_other );
151
      break;
152
 
153
    default:
154
      cerr << av[ -1 ] << ": ignored, not C/C++ or assembly" << endl;
155
    }
156
  }
157
}
158
 
159
/* insert_source
160
 *
161
 *  Insert a filename into the 'main' or 'other' list, depending on
162
 * whether it contains a main function.  Also insert it into the
163
 * 'both' list, too.
164
 */
165
void DirList::insert_source( bool (* mainfn)( const string &name ),
166
                             const string &name,
167
                             set<string> &both,
168
                             set<string> &main,
169
                             set<string> &other ) {
170
  both.insert( name );
171
  if( mainfn( name ) ) {
172
    main.insert( name );
173
  } else {
174
    other.insert( name );
175
  }
176
}
177
 
178
/* basename
179
 *
180
 * Return the basename (without a suffix) of name.
181
 */
182
const string DirList::basename( const string &name ) {
183
  size_t pos = name.rfind('.');
184
  if( pos == string::npos ) return name;
185
  return name.substr( 0, pos );
186
}
187
 
188
/* fileType
189
 *
190
 * Return a member of the fileType enumeration corresponding to
191
 * the suffix in name.
192
 */
193
DirList::FileType DirList::fileType( const string &name ) const {
194
  if( name[ 0 ] == '.' ) return OTHER;
195
  size_t pos = name.rfind('.');
196
  if( pos == string::npos ) return OTHER;
197
  string suffix( name.substr( pos+1, name.size() ) );
198
  FileType ft = OTHER;
6 rm5248 199
 
200
  if( suffix == "m4" ){
201
    string prefix( name.substr( 0, pos ) );
202
    size_t pos2 = prefix.rfind('.');
203
    if( pos2 == string::npos ) return OTHER;
204
    string real_suffix( name.substr( pos2, pos ) );
205
    if( real_suffix == "C" || real_suffix == "cpp" || real_suffix == "cc" ) ft = M4_CPP;
206
    if( real_suffix == "H" || real_suffix == "h" ) ft = M4_H;
207
    return ft;
208
  }
209
 
210
  if( suffix == "C" || suffix == "cpp" || suffix == "cc" ) ft = CPP;
1 rm5248 211
  if( suffix == "c" ) ft = C;
6 rm5248 212
  if( suffix == "S" ) ft = PREASSEMBLY;
1 rm5248 213
  if( suffix == "s" ) ft = ASSEMBLY;
214
  if( suffix == "H" || suffix == "h" ) ft =  H;
215
  if( suffix == "a" ) ft = ARCHIVE;
216
  if( ft == OTHER ) return OTHER;
217
  if( name.find('#') != string::npos ) {
218
    cerr << "Warning: ignoring file whose name contains '#': "
219
         << name << endl;
220
    return OTHER;
221
  }
222
  return ft;
223
}
224
 
225
/* matchString
226
 *
227
 * Matches a given string or stops on first character of mismatch. All
228
 * matched characters are passed in the stream. ch will be set to the
229
 * first unmatched character or the next character if the string is
230
 * completely matched.
231
 */
232
bool DirList::matchString( ifstream &in, char &ch, const string &s ) {
233
  int n = s.size();
234
  for( int i = 0; i < n && in; i++, in.get( ch ) ) {
235
    if( s[i] != ch ) {
236
      return false;
237
    }
238
  }
239
  return in; // true if stream still good else false for no match
240
}
241
 
242
/* skipComments
243
 *
244
 * Skips a possible comment.
245
 *
246
 * May absorb a '/' and return false. ch will be set to the character
247
 * following the comment or following a '/' that does not start a comment.
248
*/
249
bool DirList::skipComments( ifstream &in, char &ch ) {
250
  if( ch == '/' && in ) {
251
    if( in.get( ch ) && ch == '/' ) {
252
      while( in.get( ch ) && ch != '\n' ) {/* empty */}
253
      in.get( ch ); // pass end-of-line
254
      return true;
255
    } else if( ch == '*' ) {
256
      if( in.get( ch ) ) {
257
        do {
258
          while( ch != '*' && in.get( ch ) ) {/* empty */}
259
          while( ch == '*' && in.get( ch ) ) {/* empty */}
260
        } while( ch != '/' && in );
261
      }
262
      return true;
263
    } else {
264
      return false;
265
    }
266
  }
267
  return false;
268
}
269
 
270
/* skipWhitespace
271
 *
272
 * Skips possible whitespace. ch will be set to the first non-whitespace
273
 * character.
274
 */
275
bool DirList::skipWhitespace( ifstream &in, char &ch ) {
276
  bool skipped = false;
277
  while( isspace( ch ) && in ) {
278
    in.get( ch );
279
    skipped = true;
280
  }
281
  return skipped;
282
}
283
 
284
/* getString
285
 *
286
 * Returns quoted string.
287
 *
288
 * Assumes that ch contains the quote character. The next character read
289
 * from the stream is the first character of the quoted string. ch is set
290
 * after the closing quote character. An empty string is returned if a
291
 * newline or end-of-file is encountered.
292
 */
293
string DirList::getString( ifstream &in, char &ch ) {
294
  char quote = ch;
295
  ostringstream os;
296
  while( in.get( ch ) && ch != quote && ch != '\n' ) {
297
    os << ch;
298
  }
299
  if( ch == quote ) {
300
    in.get( ch ); // pass the closing quote character
301
    return os.str();
302
  } else {
303
    return string(); // return an empty string on failure
304
  }
305
}
306
 
307
/* hasMainFunction
308
 *
309
 * Scan a file to see if it contains a main function.
310
 * This is only a partial job, because to really do it right
311
 * takes about the same amount of work as compiling the file.
312
 *
313
 * 1) ignore C++ style comments,
314
 * 2) ignore C style comments (fails if the file has strings
315
 *    that contain / * or * /  ),
316
 * 3) then look for the pattern:
317
 *  (^|ws) "int" ws+ "main" ws* "("
318
 */
319
bool DirList::hasMainFunction( const string &name ) {
320
  ifstream in( name.c_str() );
321
  char ch;
322
 
323
  in.get( ch );
324
  while( in ) {
325
    while( skipWhitespace( in, ch ) || skipComments( in, ch ) ) {/* empty */}
326
    if ( matchString( in, ch, "int" ) ) {
327
      if( isspace( ch ) ) {
328
        while( skipWhitespace( in, ch ) ) {/* empty */}
329
        if( matchString( in, ch, "main" ) ) {
330
          while( skipWhitespace( in, ch ) ) {/* empty */}
331
          if( matchString( in, ch, "(" ) ) {
332
            return true;
333
          }
334
        }
335
      }
336
    }
337
    in.get( ch );
338
  }
339
  return false;
340
}
341
 
342
/* hasMainLabel
343
 *
344
 * Scan a file to see if it contains a main label.
345
 * This is only a partial job, because to really do it right
346
 * takes about the same amount of work as compiling the file.
347
 *
348
 * 1) ignore C++ style comments,
349
 * 2) ignore C style comments (fails if the file has strings
350
 *    that contain / * or * /  ),
351
 * 3) then look for the pattern:
352
 *  (^|ws) "main" ws* ":"
353
 */
354
bool DirList::hasMainLabel( const string &name ) {
355
  ifstream in( name.c_str() );
356
  char ch;
357
 
358
  in.get( ch );
359
  while( in ) {
360
    while( skipWhitespace( in, ch ) || skipComments( in, ch ) ) {/* empty */}
361
    if ( matchString( in, ch, "main" ) ) {
362
      while( skipWhitespace( in, ch ) ) {/* empty */}
363
      if( matchString( in, ch, ":" ) ) {
364
        return true;
365
      }
366
    } else {
367
      in.get( ch );
368
    }
369
  }
370
  return false;
371
}
372
 
373
/* getIncludeFiles
374
 *
375
 * Read the file, looking for this pattern:
376
 * ^(ws*)#(ws*)include(ws*)"name"
377
 * For each entry found, add the name to the filelist.
378
 */
379
set<string> DirList::getIncludeFiles( const string &name ) {
380
  ifstream in( name.c_str() );
381
  set<string> list;
382
  ostringstream os;
383
  char ch;
384
 
385
  in.get( ch );
386
  while( in ) { // at beginning of line
387
    skipWhitespace( in, ch );
388
    if( matchString( in, ch, "#" ) ) {
389
      skipWhitespace( in, ch );
390
      if( matchString( in, ch, "include" ) ) {
391
        skipWhitespace( in, ch );
392
        if( ch == '"' ) {
393
          //in.get( ch );
394
          string name = getString( in, ch );
395
          if( name.size() != 0 ) {
396
            list.insert( name );
397
          }
398
        }
399
      }
400
    }
401
    while( ch != '\n' && in.get( ch ) ) {/* empty */}
402
    in.get(ch);
403
  }
404
  return list;
405
}
48 rm5248 406
 
407
void DirList::scan_dirs( DIR* dir ){
408
  struct dirent* dirent;
409
  DIR* newDir;
410
 
411
  if( dir == NULL ) return;
412
 
413
  while( ( dirent = readdir( dir ) ) != NULL ){
414
    switch( dirent->d_type ){
415
      case DT_DIR:
416
        newDir = opendir( dirent->d_name );
417
        scan_dirs( newDir );
418
        closedir( newDir );
419
        break;
420
      case DT_REG:
421
        //These are the files that we care about
422
        scan_single_dir( dir );
423
    }
424
  }
425
 
426
}
427
 
428
void DirList::scan_single_dir( DIR* dir, int ac ){
429
    struct dirent *dirent;
430
 
431
    while( ( dirent = readdir( dir ) ) != 0 ) {
432
      string name( dirent->d_name );
433
 
434
      switch( fileType( name ) ) {
435
      case CPP:
436
        if( ac <= 0 ) {
437
          insert_source( hasMainFunction, name, cpp, cpp_main, cpp_other );
438
        }
439
        break;
440
 
441
      case C:
442
        if( ac <= 0 ) {
443
          insert_source( hasMainFunction, name, c, c_main, c_other );
444
        }
445
        break;
446
 
447
      case ASSEMBLY:
448
        if( ac <= 0 ) {
449
          insert_source( hasMainLabel, name, ass, ass_main, ass_other );
450
        }
451
        break;
452
 
453
      case PREASSEMBLY:
454
        if( ac <= 0 ){
455
          insert_source( hasMainLabel, name, preass, preass_main, preass_other );
456
        }
457
        break;
458
 
459
      case H:
460
        h.insert( name );
461
        break;
462
 
463
      case ARCHIVE:
464
        archive.insert( name );
465
        break;
466
 
467
      default:;
468
        // nothing to do
469
      }
470
 
471
    }
472
}