Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

567 lines
13 KiB

  1. // fnreg.cpp
  2. //
  3. // filename regular expression routine for WIN32
  4. //
  5. // Copyright (C) 1994-1998 by Hirofumi Yamamoto. All rights reserved.
  6. //
  7. // Redistribution and use in source and binary forms are permitted
  8. // provided that
  9. // the above copyright notice and this paragraph are duplicated in all such
  10. // forms and that any documentation, advertising materials, and other
  11. // materials related to such distribution and use acknowledge that the
  12. // software was developed by Hirofumi Yamamoto may not be used to endorse or
  13. // promote products derived from this software without specific prior written
  14. // permission. THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  15. // IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
  16. // OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  17. //
  18. #include "precomp.h"
  19. #pragma hdrstop
  20. #include "fnreg.h"
  21. // Hide everything in a name space
  22. namespace fnreg_implement {
  23. #ifdef UNICODE
  24. #define MAX USHRT_MAX
  25. typedef TCHAR uchar;
  26. #define iskanji(x) false
  27. #else /* !UNICODE */
  28. #define MAX UCHAR_MAX
  29. typedef unsigned char uchar;
  30. #define iskanji(x) isleadbyte(x)
  31. #endif /* !UNICODE */
  32. #define PATHDLM _T("\\/")
  33. #define ANY _T('?')
  34. #define CH _T('.')
  35. #define WILD _T('*')
  36. #define EOR _T('\x01')
  37. #define WILDCARD _T("?*")
  38. static TCHAR* fnrecompWorker(TCHAR* s, TCHAR* re, int& min, int& max)
  39. {
  40. TCHAR* t;
  41. switch (*s) {
  42. case _T('\0'):
  43. *re = EOR;
  44. re[1] = _T('\0');
  45. return s;
  46. case ANY:
  47. *re++ = *s++;
  48. break;
  49. case WILD:
  50. *re++ = *s++;
  51. t = fnrecompWorker(s, re + 2, min, max);
  52. *re = min + 1;
  53. re[1] = max > MAX ? MAX : max + 1;
  54. max = MAX;
  55. return t;
  56. default:
  57. *re++ = CH;
  58. #ifdef UNICODE
  59. *re++ = _totlower(*s);
  60. ++s;
  61. #else
  62. #error MBCS handling needed here.
  63. #endif
  64. }
  65. t = fnrecompWorker(s, re, min, max);
  66. min++;
  67. max++;
  68. return t;
  69. }
  70. static BOOL fnrecomp(TCHAR* s, TCHAR* re)
  71. {
  72. int a = 0, b = 0;
  73. return fnrecompWorker(s, re, a, b) != NULL;
  74. }
  75. static BOOL match(TCHAR* re, TCHAR* s)
  76. {
  77. int min, max;
  78. int i;
  79. TCHAR* p;
  80. switch (*re) {
  81. case CH:
  82. return (re[1] == _totlower(*s)) && match(re + 2, s + 1);
  83. case ANY:
  84. return *s && match(re + 1, s + 1);
  85. case WILD:
  86. min = (uchar)re[1];
  87. max = (uchar)re[2];
  88. re += 3;
  89. i = 1;
  90. #if !defined(UNICODE)
  91. #error MBCS handling needed here.
  92. #endif
  93. for (p = s + _tcslen(s); p >= s && i <= max; --p, ++i) {
  94. if (i >= min && match(re, p))
  95. return TRUE;
  96. }
  97. return FALSE;
  98. case EOR:
  99. if (re[1] == _T('\0'))
  100. return *s == _T('\0');
  101. }
  102. return FALSE;
  103. }
  104. ///////////////////////////////////////////////////////////////////
  105. // FileString class
  106. ///////////////////////////////////////////////////////////////////
  107. class FileString {
  108. public:
  109. FileString(const TCHAR* p);
  110. ~FileString();
  111. operator const TCHAR*() const { return m_string; }
  112. int operator==(FileString& s) const
  113. {
  114. return !_tcscmp(m_string, s.m_string);
  115. }
  116. int operator-(const FileString& f)
  117. {
  118. return _tcscmp(m_string, f);
  119. }
  120. protected:
  121. TCHAR* m_string;
  122. void normalize();
  123. };
  124. void FileString::normalize()
  125. {
  126. for (TCHAR* p = m_string; *p; ++p) {
  127. if (iskanji(*p))
  128. ++p;
  129. else if (*p == '\\')
  130. *p = '/';
  131. }
  132. }
  133. FileString::FileString(const TCHAR* p)
  134. {
  135. m_string = new TCHAR[_tcslen(p) + 1];
  136. if (m_string == NULL) {
  137. fputs("FileString:: not enough mem\n", stderr);
  138. exit(1);
  139. }
  140. _tcscpy(m_string, p);
  141. normalize();
  142. }
  143. FileString::~FileString()
  144. {
  145. delete[] m_string;
  146. }
  147. ///////////////////////////////////////////////////////////////////
  148. // PtrArray class
  149. ///////////////////////////////////////////////////////////////////
  150. template <class T>
  151. class PtrArray {
  152. public:
  153. PtrArray(bool doDeleteContents = true, int defsize = DEFSIZE)
  154. : m_size(defsize), m_max(0), m_doDelete(doDeleteContents)
  155. {
  156. m_table = (T**)malloc(sizeof(T*) * m_size);
  157. if (m_table == NULL) {
  158. perror("PtrArray");
  159. exit(1);
  160. }
  161. }
  162. virtual ~PtrArray()
  163. {
  164. if (m_doDelete) {
  165. for (int i = 0; i < m_max; ++i) {
  166. delete m_table[i];
  167. }
  168. }
  169. if (m_table)
  170. free(m_table);
  171. }
  172. void add(T*);
  173. int howmany() { return m_max; }
  174. T* operator[](int n)
  175. {
  176. assert(n >= 0 && n < m_max);
  177. return m_table[n];
  178. }
  179. void sortIt();
  180. protected:
  181. int m_size;
  182. int m_max;
  183. bool m_doDelete; // whether to delete the contents
  184. T** m_table;
  185. enum { DEFSIZE = 128, INCR = 128 };
  186. static int __cdecl compare(const void*, const void*);
  187. };
  188. template <class T>
  189. int __cdecl PtrArray<T>::compare(const void* a, const void* b)
  190. {
  191. const T** ta = (const T**)a;
  192. const T** tb = (const T**)b;
  193. return int(**ta - **tb);
  194. }
  195. template <class T>
  196. void PtrArray<T>::sortIt()
  197. {
  198. qsort(m_table, m_max, sizeof(T*), compare);
  199. }
  200. template <class T>
  201. void PtrArray<T>::add(T* t)
  202. {
  203. if (m_max >= m_size) {
  204. m_table = (T**)realloc(m_table, (m_size += INCR) * sizeof(T*));
  205. if (m_table == NULL) {
  206. perror("PtrArray:add\n");
  207. exit(1);
  208. }
  209. }
  210. m_table[m_max++] = t;
  211. }
  212. ///////////////////////////////////////////////////////////////////
  213. // PtrArrayIterator class
  214. ///////////////////////////////////////////////////////////////////
  215. #if 1
  216. template <class T>
  217. class PtrArrayIterator {
  218. public:
  219. PtrArrayIterator(PtrArray<T>& s) : m_array(s), m_cur(0)
  220. {
  221. }
  222. public:
  223. T* operator++(int);
  224. void restart() { m_cur = 0; }
  225. protected:
  226. PtrArray<T>& m_array;
  227. int m_cur;
  228. };
  229. template <class T>
  230. T* PtrArrayIterator<T>::operator++(int)
  231. {
  232. T* t;
  233. if (m_cur < m_array.howmany()) {
  234. t = m_array[m_cur++];
  235. }
  236. else {
  237. t = NULL;
  238. }
  239. return t;
  240. }
  241. #else
  242. template <class T>
  243. class SimpleIterator {
  244. public:
  245. SimpleIterator(T& s) : m_array(s), m_cur(0)
  246. {
  247. }
  248. public:
  249. T* operator++(int);
  250. void restart() { m_cur = 0; }
  251. protected:
  252. T& m_array;
  253. int m_cur;
  254. };
  255. template <class T>
  256. T* PtrArrayIterator<T>::operator++(int)
  257. {
  258. T* t;
  259. if (m_cur < m_array.howmany()) {
  260. t = m_array[m_cur];
  261. ++m_cur;
  262. }
  263. else {
  264. t = NULL;
  265. }
  266. return t;
  267. }
  268. #endif
  269. ///////////////////////////////////////////////////////////////////
  270. // FilenameTable class
  271. ///////////////////////////////////////////////////////////////////
  272. class FilenameTable {
  273. public:
  274. FilenameTable(TCHAR* = NULL, int _searchDir = TRUE);
  275. ~FilenameTable();
  276. public:
  277. void search(TCHAR* p, int level = 0);
  278. int howmany() { return m_names.howmany(); }
  279. PtrArray<FileString>& getTable() { return m_names; }
  280. protected:
  281. int m_searchDir;
  282. PtrArray<FileString> m_names;
  283. };
  284. FilenameTable::FilenameTable(TCHAR* nm, int _searchDir /*=TRUE*/)
  285. : m_searchDir(_searchDir)
  286. {
  287. #if 0
  288. search(substHome(nm));
  289. #else
  290. if (nm)
  291. search(nm);
  292. #endif
  293. }
  294. FilenameTable::~FilenameTable()
  295. {
  296. }
  297. inline bool chkCurrentOrParentDir(const TCHAR* s)
  298. {
  299. return s[0] == _T('.') && (s[1] == _T('\0') || (s[1] == _T('.') && s[2] == _T('\0')));
  300. }
  301. void FilenameTable::search(TCHAR* p, int level)
  302. {
  303. TCHAR* wild = _tcspbrk(p, WILDCARD);
  304. if (wild) {
  305. // has wildcards
  306. TCHAR* const morepath = _tcspbrk(wild, PATHDLM); // more path ?
  307. TCHAR drive[_MAX_DRIVE], dir[_MAX_DIR], file[_MAX_FNAME], ext[_MAX_EXT];
  308. TCHAR re[(_MAX_FNAME + _MAX_EXT) * 2] = {0};
  309. // split the path
  310. {
  311. // *hack*
  312. // to avoid strcpy, we'll touch argument p directly
  313. TCHAR bc;
  314. if (morepath) {
  315. // truncate the path so that we will work with
  316. // the lookup directory which contains the wild cards
  317. bc = *morepath;
  318. *morepath = _T('\0');
  319. }
  320. _tsplitpath(p, drive, dir, file, ext);
  321. if (morepath) {
  322. *morepath = bc;
  323. }
  324. }
  325. // build file+ext which contains the wild cards
  326. TCHAR fnext[_MAX_FNAME + _MAX_EXT - 1];
  327. _tcscpy(fnext, file);
  328. _tcscat(fnext, ext);
  329. // compile the regular expression
  330. if (!fnrecomp(fnext, re)) {
  331. fputs("Illegal regular expression in ", stderr);
  332. _fputts(fnext, stderr);
  333. fputs("\n", stderr);
  334. exit(1);
  335. }
  336. // make search string
  337. TCHAR path[_MAX_PATH];
  338. _tmakepath(path, drive, dir, _T("*"), _T(".*"));
  339. // listup all the files and directories in the current lookup directory
  340. // and pickup matched ones
  341. _tfinddata_t findinfo;
  342. intptr_t hFind = _tfindfirst(path, &findinfo);
  343. if (hFind != -1) {
  344. do {
  345. if (!chkCurrentOrParentDir(findinfo.name)) {
  346. if (match(re, findinfo.name)) {
  347. // searched file or directory matches the pattern
  348. _tmakepath(path, drive, dir, findinfo.name, _T(""));
  349. if (morepath) {
  350. // there's more sub directories to search.
  351. if (findinfo.attrib & _A_SUBDIR) {
  352. // if directory, do recursive calls
  353. _tcscat(path, morepath); // morepath begins with '/'
  354. search(path, level + 1);
  355. }
  356. }
  357. else {
  358. // this directory is the last element
  359. if (m_searchDir || !(findinfo.attrib & _A_SUBDIR)) {
  360. FileString* name = new FileString(path);
  361. if (name == NULL) {
  362. fputs("FilenameTable::search(): not enough mem\n", stderr);
  363. exit(1);
  364. }
  365. m_names.add(name);
  366. }
  367. }
  368. }
  369. }
  370. } while (!_tfindnext(hFind, &findinfo));
  371. _findclose(hFind);
  372. }
  373. }
  374. if ((level == 0 && m_names.howmany() == 0) || (!wild && !_taccess(p, 0))) {
  375. FileString* name = new FileString(p);
  376. if (name == NULL) {
  377. fputs("FilenameTable::search() not enough mem\n", stderr);
  378. exit(1);
  379. }
  380. m_names.add(name);
  381. }
  382. if (level == 0 && m_names.howmany() > 0) {
  383. m_names.sortIt();
  384. }
  385. }
  386. }; // end of namespace
  387. using namespace ::fnreg_implement;
  388. ///////////////////////////////////////////////////////////////////
  389. // Global object of FilenameTable class
  390. ///////////////////////////////////////////////////////////////////
  391. static PtrArray<FilenameTable> fnarray;
  392. ///////////////////////////////////////////////////////////////////
  393. // Interface routine to the world
  394. ///////////////////////////////////////////////////////////////////
  395. extern "C"
  396. BOOL fnexpand(int* pargc, TCHAR*** pargv)
  397. {
  398. assert(pargc != NULL);
  399. assert(pargv != NULL);
  400. for (int i = 1; i < *pargc; ++i) {
  401. FilenameTable* fn = new FilenameTable((*pargv)[i]);
  402. fnarray.add(fn);
  403. }
  404. int cnt = 0;
  405. PtrArrayIterator<FilenameTable> fnItor(fnarray);
  406. // first count up the argc
  407. for (FilenameTable* ft; ft = fnItor++; ) {
  408. cnt += ft->howmany();
  409. }
  410. fnItor.restart();
  411. // setup argc and argv
  412. *pargc = cnt + 1;
  413. TCHAR** nargv = new TCHAR*[*pargc];
  414. if (!nargv)
  415. return FALSE;
  416. nargv[0] = (*pargv)[0];
  417. // set all arguments
  418. for (cnt = 1, i = 0; ft = fnItor++; ++i) {
  419. PtrArrayIterator<FileString> itor(ft->getTable());
  420. FileString* fs;
  421. for (; fs = itor++; ++cnt) {
  422. const TCHAR* p = *fs;
  423. nargv[cnt] = (TCHAR*)p;
  424. }
  425. }
  426. assert(*pargc == cnt);
  427. *pargv = nargv;
  428. return TRUE;
  429. }
  430. #if defined(TEST) || defined(TEST0)
  431. void print(TCHAR* p)
  432. {
  433. for (; *p; ++p) {
  434. _puttchar(_T('['));
  435. if (*p >= 0x20 && *p < 0x7f) {
  436. _puttchar(*p);
  437. }
  438. #if 1
  439. printf(":%d] ", *p);
  440. #else
  441. _puttchar(_T(':'));
  442. TCHAR buf[34];
  443. _ltot(*p, buf, 10);
  444. _fputts(buf, stdout);
  445. _fputts(_T("] "), stdout);
  446. #endif
  447. }
  448. _puttchar('\n');
  449. }
  450. #endif
  451. #ifdef TEST
  452. extern "C"
  453. int wmain(int argc, TCHAR** argv)
  454. {
  455. if (!fnexpand(argc, argv))
  456. return EXIT_FAILURE;
  457. while (--argc) {
  458. _putts(*++argv);
  459. }
  460. return EXIT_SUCCESS;
  461. }
  462. #endif
  463. #ifdef TEST0
  464. extern "C"
  465. int wmain(int argc, TCHAR** argv)
  466. {
  467. TCHAR re[256];
  468. if (!fnrecomp(argv[1], re)) {
  469. puts("error");
  470. return EXIT_FAILURE;
  471. }
  472. print(re);
  473. TCHAR buf[BUFSIZ];
  474. while (_getts(buf)) {
  475. if (match(re, buf))
  476. puts("match");
  477. else
  478. puts("does not match.");
  479. }
  480. return EXIT_SUCCESS;
  481. }
  482. #endif