00001
00002 #include "idep_aliasdep.h"
00003 #include "idep_namearray.h"
00004 #include "idep_nameindexmap.h"
00005 #include "idep_filedepiter.h"
00006 #include "idep_tokeniter.h"
00007 #include "idep_aliastable.h"
00008 #include "idep_aliasutil.h"
00009
00010 #include <ctype.h>
00011 #include <cstring>
00012 #include <fstream>
00013 #include <memory>
00014 #include <iostream>
00015 #include <cassert>
00016
00017
00018
00019 static std::ostream& warn(std::ostream& ing)
00020 {
00021 return ing << "Warning: ";
00022 }
00023
00024 static std::ostream& err(std::ostream& orr)
00025 {
00026 return orr << "Error: ";
00027 }
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041 static const char *stripDir(const char *s)
00042 {
00043 if (s) {
00044 const char *slash = strrchr(s, '/');
00045 return slash ? slash + 1 : s;
00046 }
00047 return s;
00048 }
00049
00050 static int isAsciiFile(const char *fileName)
00051 {
00052 enum { NO = 0, YES = 1 };
00053 std::ifstream in(fileName);
00054 if (!in) {
00055 return NO;
00056 }
00057
00058
00059 char c;
00060 while (in && !in.get(c).eof()) {
00061 if (!isascii(c)) {
00062 return NO;
00063 }
00064 }
00065 return YES;
00066 }
00067
00068 typedef void (idep_AliasDep::*Func)(const char *);
00069 static void loadFromStream(std::istream& in, idep_AliasDep *dep, Func add)
00070 {
00071 assert(in);
00072 for (idep_TokenIter it(in); it; ++it) {
00073 if ('#' == *it()) {
00074 while (it && '\n' != *it()) {
00075 ++it;
00076 }
00077 continue;
00078 }
00079 if ('\n' != *it()) {
00080 (dep->*add)(it());
00081 }
00082 }
00083 }
00084
00085 static int loadFromFile(const char *file, idep_AliasDep *dep, Func add)
00086 {
00087 enum { BAD = -1, GOOD = 0 };
00088 if (!isAsciiFile(file)) {
00089 return BAD;
00090 }
00091 std::ifstream in(file);
00092 assert(in);
00093 loadFromStream(in, dep, add);
00094 return GOOD;
00095 }
00096
00097 static char *newStrCpy(const char *name)
00098 {
00099 int size = strlen(name) + 1;
00100 char *buf = new char[size];
00101 memcpy(buf, name, size);
00102 return buf;
00103 }
00104
00105 static char *removeSuffix(char *dirPath)
00106 {
00107 char *dot = strrchr(dirPath, '.');
00108 if (dot && !strchr(dot, '/')) {
00109 dot[0] = '\0';
00110 }
00111 return dirPath;
00112 }
00113
00114
00115
00116 class idep_AliasDepIntArray {
00117 int *d_array_p;
00118 int d_length;
00119 idep_AliasDepIntArray(const idep_AliasDepIntArray&);
00120 idep_AliasDepIntArray& operator=(const idep_AliasDepIntArray&);
00121 public:
00122 idep_AliasDepIntArray(int length) :
00123 d_array_p(new int[length]), d_length(length) {}
00124 ~idep_AliasDepIntArray() { delete [] d_array_p; }
00125 int& operator[](int i) { return d_array_p[i]; }
00126 int length() const { return d_length; }
00127 };
00128
00129 static void zero(idep_AliasDepIntArray *a)
00130 {
00131 for (int i = 0; i < a->length(); ++i) {
00132 (*a)[i] = 0;
00133 }
00134 }
00135
00136
00137
00138 class idep_AliasDepString {
00139 char *d_string_p;
00140 idep_AliasDepString(const idep_AliasDepString&);
00141 idep_AliasDepString& operator=(const idep_AliasDepString&);
00142 public:
00143 idep_AliasDepString(const char *s) : d_string_p(newStrCpy(s)){}
00144 ~idep_AliasDepString() { delete [] d_string_p; }
00145 operator char *() { return d_string_p; }
00146 };
00147
00148
00149
00150 struct idep_AliasDep_i {
00151 idep_NameIndexMap d_ignoreNames;
00152 idep_AliasTable d_aliases;
00153 idep_NameIndexMap d_fileNames;
00154 };
00155
00156
00157
00158 idep_AliasDep::idep_AliasDep()
00159 : d_this(new idep_AliasDep_i)
00160 {
00161 }
00162
00163 idep_AliasDep::~idep_AliasDep()
00164 {
00165 delete d_this;
00166 }
00167
00168 void idep_AliasDep::addIgnoreName(const char *fileName)
00169 {
00170 d_this->d_ignoreNames.add(fileName);
00171 }
00172
00173 int idep_AliasDep::readIgnoreNames(const char *file)
00174 {
00175 return loadFromFile(file, this, &idep_AliasDep::addIgnoreName);
00176 }
00177
00178 const char *idep_AliasDep::addAlias(const char *alias, const char *component)
00179 {
00180 return d_this->d_aliases.add(alias, component) < 0 ?
00181 d_this->d_aliases.lookup(alias) : 0;
00182 }
00183
00184 int idep_AliasDep::readAliases(std::ostream& orr, const char *file)
00185 {
00186 return idep_AliasUtil::readAliases(&d_this->d_aliases, orr, file);
00187 }
00188
00189 void idep_AliasDep::addFileName(const char *fileName)
00190 {
00191 d_this->d_fileNames.add(fileName);
00192 }
00193
00194 int idep_AliasDep::readFileNames(const char *file)
00195 {
00196 return loadFromFile(file, this, &idep_AliasDep::addFileName);
00197 }
00198
00199 void idep_AliasDep::inputFileNames()
00200 {
00201 if (std::cin) {
00202 loadFromStream(std::cin, this, &idep_AliasDep::addFileName);
00203 std::cin.clear(std::ios::iostate(0));
00204 }
00205 }
00206
00207 int idep_AliasDep::unpaired(std::ostream& out,
00208 std::ostream& ing,
00209 int suffixFlag) const
00210 {
00211 int maxLength = d_this->d_fileNames.length();
00212 idep_AliasDepIntArray hits(maxLength);
00213 idep_AliasDepIntArray cmap(maxLength);
00214 zero(&hits);
00215 idep_NameIndexMap components;
00216 int numComponents = 0;
00217
00218 idep_NameIndexMap printNames;
00219
00220
00221 int i;
00222 for (i = 0; i < maxLength; ++i) {
00223 idep_AliasDepString s(d_this->d_fileNames[i]);
00224
00225 if (d_this->d_ignoreNames.lookup(s) >= 0) {
00226 continue;
00227 }
00228 removeSuffix(s);
00229
00230 const char *componentName = d_this->d_aliases.lookup(s);
00231 if (!componentName) {
00232 componentName = s;
00233 }
00234
00235 int componentIndex = components.entry(componentName);
00236 if (components.length() > numComponents) {
00237 ++numComponents;
00238
00239 }
00240
00241 assert(components.length() == numComponents);
00242
00243 ++hits[componentIndex];
00244 cmap[componentIndex] = i;
00245 }
00246
00247 for (i = 0; i < numComponents; ++i) {
00248 assert(hits[i] > 0);
00249 if (1 == hits[i]) {
00250 printNames.add(suffixFlag ? d_this->d_fileNames[cmap[i]]
00251 : components[i]);
00252 }
00253 if (hits[i] > 2) {
00254 warn(ing) << "component \"" << components[i]
00255 << "\" consists of " << hits[i] << " files."
00256 << std::endl;
00257 }
00258 }
00259
00260
00261
00262
00263
00264
00265
00266
00267
00268 int numUnpaired = printNames.length();
00269 idep_AliasDepIntArray smap(numUnpaired);
00270 for (i = 0; i < numUnpaired; ++i) {
00271 smap[i] = i;
00272 }
00273
00274 for (i = 1; i < numUnpaired; ++i) {
00275 for (int j = 0; j < numUnpaired; ++j) {
00276 int swap;
00277 if (suffixFlag) {
00278 swap = strcmp(printNames[smap[i]], printNames[smap[j]]) < 0;
00279 }
00280 else {
00281 int li = strlen(printNames[smap[i]]);
00282 int lj = strlen(printNames[smap[j]]);
00283 int len = li < lj ? li : lj;
00284 int cmp = strncmp(printNames[smap[i]],
00285 printNames[smap[j]], len);
00286 swap = cmp < 0 || 0 == cmp && li > lj;
00287 }
00288 if (swap) {
00289 int tmp = smap[i];
00290 smap[i] = smap[j];
00291 smap[j] = tmp;
00292 }
00293 }
00294 }
00295
00296
00297
00298 for (i = 0; i < numUnpaired; ++i) {
00299 out << printNames[smap[i]] << std::endl;
00300 }
00301
00302 return printNames.length();
00303 }
00304
00305 static const char *th(int n)
00306 {
00307 return 1 == n ? "st" : 2 == n ? "nd" : 3 == n ? "rd" : "th";
00308 }
00309
00310 int idep_AliasDep::verify(std::ostream& orr) const
00311 {
00312 enum { IOERROR = -1, GOOD = 0 } status = GOOD;
00313 int errorCount = 0;
00314
00315 int length = d_this->d_fileNames.length();
00316 for (int i = 0; i < length; ++i) {
00317 const char *path = d_this->d_fileNames[i];
00318 idep_AliasDepString c(path);
00319
00320 if (d_this->d_ignoreNames.lookup(c) >= 0) {
00321 continue;
00322 }
00323
00324
00325 removeSuffix(c);
00326 const char *actualComponent = stripDir(c);
00327 const char *compAlias = d_this->d_aliases.lookup(actualComponent);
00328 const char *component = compAlias ? compAlias : actualComponent;
00329
00330 int directiveIndex = 0;
00331 idep_FileDepIter it(path);
00332 for (; it; ++it) {
00333
00334 ++directiveIndex;
00335
00336
00337 idep_AliasDepString h(it());
00338 removeSuffix(h);
00339 const char *actualHeader = stripDir(h);
00340 const char *headerAlias = d_this->d_aliases.lookup(actualHeader);
00341 const char *header = headerAlias ? headerAlias : actualHeader;
00342
00343 if (0 == strcmp(component, header)) {
00344 break;
00345 }
00346 }
00347
00348 if (!it.isValidFile()) {
00349 err(orr) << "unable to open file \""
00350 << path << "\" for read access." << std::endl;
00351 status = IOERROR;
00352 }
00353 else if (!it) {
00354 err(orr) << "corresponding include directive for \"" << path
00355 << "\" not found."
00356 << std::endl;
00357 ++errorCount;
00358 }
00359 else if (1 != directiveIndex) {
00360 err(orr) << '"' << path
00361 << "\" contains corresponding include as "
00362 << directiveIndex << th(directiveIndex)
00363 << " directive." << std::endl;
00364 ++errorCount;
00365 }
00366
00367
00368 }
00369
00370 return status == GOOD ? errorCount : status;
00371 }
00372
00373
00374 int idep_AliasDep::extract(std::ostream& out, std::ostream& orr) const
00375 {
00376 enum { IOERROR = -1, GOOD = 0 } status = GOOD;
00377 enum { INVALID_INDEX = -1 };
00378 int errorCount = 0;
00379
00380 idep_NameIndexMap uniqueHeaders;
00381 int length = d_this->d_fileNames.length();
00382 idep_AliasDepIntArray hits(length);
00383 zero(&hits);
00384 idep_AliasDepIntArray hmap(length);
00385 idep_AliasDepIntArray verified(length);
00386 zero(&verified);
00387
00388 int i;
00389 for (i = 0; i < length; ++i) {
00390 hmap[i] = INVALID_INDEX;
00391
00392 const char *path = d_this->d_fileNames[i];
00393 idep_AliasDepString c(path);
00394
00395 if (d_this->d_ignoreNames.lookup(c) >= 0) {
00396 continue;
00397 }
00398
00399
00400 removeSuffix(c);
00401 const char *actualComponent = stripDir(c);
00402 const char *compAlias = d_this->d_aliases.lookup(actualComponent);
00403 const char *component = compAlias ? compAlias : actualComponent;
00404
00405 idep_FileDepIter it(path);
00406
00407 if (!it.isValidFile()) {
00408 err(orr) << "unable to open file \""
00409 << path << "\" for read access." << std::endl;
00410 status = IOERROR;
00411 continue;
00412 }
00413
00414 if (!it) {
00415 err(orr) << '"' << path
00416 << "\" contains no include directives." << std::endl;
00417 ++errorCount;
00418 continue;
00419 }
00420
00421
00422 idep_AliasDepString h(it());
00423 removeSuffix(h);
00424 const char *actualHeader = stripDir(h);
00425 const char *headerAlias = d_this->d_aliases.lookup(actualHeader);
00426 const char *header = headerAlias ? headerAlias : actualHeader;
00427
00428 if (0 == strcmp(component, header)) {
00429
00430
00431
00432
00433
00434
00435 verified[i] = 1;
00436 }
00437 else {
00438
00439
00440
00441
00442
00443
00444 if (compAlias) {
00445 continue;
00446 }
00447 }
00448
00449
00450
00451
00452 int hIndex = uniqueHeaders.entry(header);
00453 ++hits[hIndex];
00454 hmap[i] = hIndex;
00455 }
00456
00457 const int FW = 25;
00458 const char *const ARROW = ">- probably correct -> ";
00459
00460
00461
00462
00463 for (i = 0; i < uniqueHeaders.length(); ++i) {
00464 if (hits[i] > 1) {
00465 warn(orr) << hits[i] << " files specify \"" << uniqueHeaders[i]
00466 << "\" as their first include directive:" << std::endl;
00467 for (int j = 0; j < length; ++j) {
00468 if (i == hmap[j]) {
00469 orr.width(FW);
00470 orr << (verified[j] ? ARROW : "");
00471 orr << '"' << stripDir(d_this->d_fileNames[j])
00472 << '"' << std::endl;
00473 }
00474 }
00475 orr << std::endl;
00476 }
00477 }
00478
00479
00480
00481
00482 for (i = 0; i < length; ++i) {
00483 if (hmap[i] >= 0 && !verified[i]) {
00484
00485 idep_AliasDepString c(d_this->d_fileNames[i]);
00486 removeSuffix(c);
00487 out << uniqueHeaders[hmap[i]] << ' ' << c << std::endl;
00488 }
00489 }
00490
00491 return status == GOOD ? errorCount : status;
00492 }
00493