scandir.c 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149
  1. /*
  2. * scandir.c Copyright (C) Codemist Ltd., 1994-98
  3. *
  4. *
  5. * Directory scanning code for use in CSL-related utilities.
  6. *
  7. * A C Norman
  8. */
  9. /* Signature: 29696d9f 15-Sep-1998 */
  10. #include "sys.h"
  11. /*
  12. * If I am to process directories I need a set of routines that will
  13. * scan sub-directories for me. This is (necessarily?) dependent on
  14. * the operating system I am running under, hence the conditional compilation
  15. * here. The specification I want is:
  16. * void scan_directory(char *dir,
  17. * void (*proc)(char *name, int why, long int size));
  18. *
  19. * This is called with a file- or directory-name as its first argument
  20. * and a function as its second.
  21. * It calls the function for every directory and every file that can be found
  22. * rooted from the given place. If the file to scan is specified as NULL
  23. * the current directory is processed. I also arrange that an input string
  24. * "." (on Windows, DOS and Unix) or "@" (Archimedes) is treated as a request
  25. * to scan the whole of the current directory.
  26. * When a simple file is found the procedure is called with the name of the
  27. * file, why=0, and the length (in bytes) of the file. For a directory
  28. * the function is called with why=1, then the contents of the directory are
  29. * processed. For directories the size information will be 0. There is no
  30. * guarantee of useful behaviour if some of the files to be scanned are
  31. * flagged as "invisible" or "not readable" or if they are otherwise special.
  32. *
  33. * I also provide a similar function scan_files() with the same arguments that
  34. * does just the same except that it does not recurse into sub-directories,
  35. * but if the name originally passed is that of a directory then all the
  36. * files in it will be scanned.
  37. */
  38. /*
  39. * When scan_directory calls the procedure it has been passed, it will have
  40. * set scan_leafstart to the offset in the passed filename where the
  41. * original directory ended and the new information starts.
  42. */
  43. int scan_leafstart = 0;
  44. /* #define SCAN_FILE 0 */ /* In scandir.h - listed here for emphasis */
  45. /* #define SCAN_STARTDIR 1 */
  46. /* #define SCAN_ENDDIR 2 */
  47. /*
  48. * I use a (static) flag to indicate how sub-directories should be
  49. * handled, and what to do about case. By default I fold to lower case
  50. * on windows. setting hostcase non-zero causes case to be preserved.
  51. */
  52. static int recursive_scan, hostcase = 0;
  53. /*
  54. * The following is an expression of despair! ANSI C (all the way back from
  55. * 1989, and based on good practise from before then, mandates that
  56. * realloc should behave as malloc if its first arg is NULL. But STILL there
  57. * are C libraries out there which do not honour this and which crash
  58. * in such cases. Thus this veneer ought to be unnecessary but in reality
  59. * is a useful safety net!
  60. */
  61. static void *my_realloc(void *p, int len)
  62. {
  63. if (p == NULL) return malloc(len);
  64. else return realloc(p, len);
  65. }
  66. void set_hostcase(int fg)
  67. {
  68. hostcase = fg;
  69. }
  70. #ifdef WINDOWS_NT
  71. /*
  72. * Note that I put the test for WINDOWS_NT before the test for MS_DOS
  73. * so that if it happens that the symbol MS_DOS is defined that fact will
  74. * not cause too much confusion.
  75. */
  76. static char filename[LONGEST_LEGAL_FILENAME];
  77. static WIN32_FIND_DATA *found_files = NULL;
  78. static int n_found_files = 0, max_found_files = 0;
  79. #define TABLE_INCREMENT 50
  80. static int more_files()
  81. {
  82. if (n_found_files > max_found_files - 5)
  83. { WIN32_FIND_DATA *fnew = (WIN32_FIND_DATA *)
  84. my_realloc((void *)found_files,
  85. sizeof(WIN32_FIND_DATA)*
  86. (max_found_files + TABLE_INCREMENT));
  87. if (fnew == NULL) return 1; /* failure flag */
  88. found_files = fnew;
  89. max_found_files += TABLE_INCREMENT;
  90. }
  91. return 0;
  92. }
  93. /*
  94. * Anybody compiling using Microsoft Visual C++ had better note that
  95. * the type declared in the Microsoft header files for qsort insists
  96. * on a __cdecl here. Ugh.
  97. */
  98. int MS_CDECL alphasort_files(const void *a, const void *b)
  99. {
  100. const WIN32_FIND_DATA *fa = (const WIN32_FIND_DATA *)a,
  101. *fb = (const WIN32_FIND_DATA *)b;
  102. return strncmp(fb->cFileName, fa->cFileName, sizeof(fa->cFileName));
  103. }
  104. static void exall(int namelength,
  105. void (*proc)(char *name, int why, long int size))
  106. /*
  107. * This procedure scans a directory-full of files, calling the given procedure
  108. * to process each one it finds.
  109. */
  110. {
  111. WIN32_FIND_DATA found;
  112. int rootlen = namelength, first = n_found_files;
  113. HANDLE hSearch = FindFirstFile(filename, &found);
  114. if (hSearch == INVALID_HANDLE_VALUE) return; /* No files found at all */
  115. for (;;)
  116. { if (more_files()) break;
  117. found_files[n_found_files++] = found;
  118. if (!FindNextFile(hSearch, &found)) break;
  119. }
  120. FindClose(hSearch);
  121. qsort((void *)&found_files[first],
  122. n_found_files-first,
  123. sizeof(WIN32_FIND_DATA),
  124. alphasort_files);
  125. while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
  126. while (n_found_files != first)
  127. { char *p = (char *)&found_files[--n_found_files].cFileName;
  128. int c;
  129. /*
  130. * Fill out filename with the actual name I grabbed, i.e. with
  131. * wild-cards expanded.
  132. */
  133. namelength = rootlen+1;
  134. /*
  135. * I fold DOS filenames into lower case because it does not matter much
  136. * to DOS and I think it looks better - furthermore it helps when I move
  137. * archives to other systems. So I do the same on NT.
  138. */
  139. while ((c = *p++) != 0)
  140. { if (!hostcase) if (isupper(c)) c = tolower(c);
  141. filename[namelength++] = c;
  142. }
  143. filename[namelength] = 0;
  144. if (found_files[n_found_files].dwFileAttributes &
  145. FILE_ATTRIBUTE_DIRECTORY)
  146. { if (found_files[n_found_files].cFileName[0] != '.')
  147. /*
  148. * I filter out directory names that start with '.'.
  149. * This is to avoid calamity with recursion though chains such as .\.\.\.....
  150. */
  151. { proc(filename, SCAN_STARTDIR, 0);
  152. if (!recursive_scan) continue;
  153. strcpy(&filename[namelength], "\\*.*");
  154. /*
  155. * Append "\*.*" to the directory-name and try again, thereby scanning
  156. * its contents.
  157. */
  158. exall(namelength+4, proc);
  159. filename[namelength] = 0;
  160. proc(filename, SCAN_ENDDIR, 0);
  161. }
  162. }
  163. else proc(filename, SCAN_FILE,
  164. found_files[n_found_files].nFileSizeLow);
  165. }
  166. return;
  167. }
  168. void scan_directory(char *dir,
  169. void (*proc)(char *name, int why, long int size))
  170. {
  171. recursive_scan = 1;
  172. if (dir==NULL || strcmp(dir,".")==0)
  173. { dir = "*.*";
  174. scan_leafstart = 0;
  175. }
  176. else scan_leafstart = strlen(dir)+1;
  177. strcpy(filename, dir);
  178. exall(strlen(filename), proc);
  179. }
  180. void scan_files(char *dir,
  181. void (*proc)(char *name, int why, long int size))
  182. {
  183. recursive_scan = 0;
  184. if (dir==NULL || strcmp(dir,".")==0)
  185. { strcpy(filename, "*.*");
  186. scan_leafstart = 0;
  187. }
  188. else
  189. { scan_leafstart = strlen(dir);
  190. strcpy(filename, dir);
  191. if (filename[scan_leafstart-1] == '\\')
  192. { /* Root directory */
  193. strcpy(filename+scan_leafstart, "*.*");
  194. --scan_leafstart;
  195. }
  196. else strcpy(filename+scan_leafstart, "\\*.*");
  197. scan_leafstart++;
  198. }
  199. exall(strlen(filename), proc);
  200. }
  201. #define SCANDIR_KNOWN 1
  202. #else /* WINDOWS_NT */
  203. #ifdef MS_DOS
  204. /*
  205. * This will be OK for both Zortech and Watcom. The entry for GCCWIN refers
  206. * to Cygnus cygwin32 "Windows GCC" together with the windows32api header
  207. * files and libraries. However note well that the cygwin32 version of this
  208. * code has not yet been got working and the status of various bits of
  209. * Windows code in that environment remains slightly delicate as of 1Q97.
  210. */
  211. #if defined __WATCOMC__ || defined _MSC_VER || defined GCCWIN
  212. static char filename[LONGEST_LEGAL_FILENAME];
  213. static WIN32_FIND_DATA *found_files = NULL;
  214. static int n_found_files = 0, max_found_files = 0;
  215. #define TABLE_INCREMENT 50
  216. static int more_files()
  217. {
  218. if (n_found_files > max_found_files - 5)
  219. { WIN32_FIND_DATA *fnew = (WIN32_FIND_DATA *)
  220. my_realloc((void *)found_files,
  221. sizeof(WIN32_FIND_DATA) *
  222. (max_found_files + TABLE_INCREMENT));
  223. if (fnew == NULL) return 1; /* failure flag */
  224. found_files = fnew;
  225. max_found_files += TABLE_INCREMENT;
  226. }
  227. return 0;
  228. }
  229. int MS_CDECL alphasort_files(const void *a, const void *b)
  230. {
  231. const WIN32_FIND_DATA *fa = (const WIN32_FIND_DATA *)a,
  232. *fb = (const WIN32_FIND_DATA *)b;
  233. return strncmp(fb->cFileName, fa->cFileName, sizeof(fa->cFileName));
  234. }
  235. static void exall(int namelength,
  236. void (*proc)(char *name, int why, long int size))
  237. /*
  238. * This procedure scans a directory-full of files, calling the given procedure
  239. * to process each one it finds.
  240. */
  241. {
  242. int rootlen = namelength;
  243. int code, first = n_found_files;
  244. HANDLE fh;
  245. WIN32_FIND_DATA found;
  246. fh = FindFirstFile(filename, &found);
  247. code = (fh != INVALID_HANDLE_VALUE);
  248. while (code)
  249. { if (more_files()) break;
  250. found_files[n_found_files++] = found;
  251. code = FindNextFile(fh, &found);
  252. }
  253. FindClose(fh);
  254. qsort((void *)&found_files[first],
  255. n_found_files-first,
  256. sizeof(WIN32_FIND_DATA),
  257. alphasort_files);
  258. while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
  259. while (n_found_files != first)
  260. { char *p = (char *)&found_files[--n_found_files].cFileName;
  261. int c;
  262. /*
  263. * Fill out filename with the actual name I grabbed, i.e. with
  264. * wild-cards expanded.
  265. */
  266. namelength = rootlen+1;
  267. /*
  268. * I fold DOS filenames into lower case because it does not matter much
  269. * to DOS and I think it looks better - furthermore it helps when I move
  270. * archives to other systems.
  271. */
  272. while ((c = *p++) != 0)
  273. { if (!hostcase) if (isupper(c)) c = tolower(c);
  274. filename[namelength++] = c;
  275. }
  276. filename[namelength] = 0;
  277. if (found_files[n_found_files].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  278. { if (found_files[n_found_files].cFileName[0] != '.')
  279. /*
  280. * I filter out directory names that start with '.'.
  281. * This is to avoid calamity with recursion though chains such as .\.\.\.....
  282. */
  283. { proc(filename, SCAN_STARTDIR, 0);
  284. if (!recursive_scan) continue;
  285. strcpy(&filename[namelength], "\\*.*");
  286. /*
  287. * Append "\*.*" to the directory-name and try again, thereby scanning
  288. * its contents.
  289. */
  290. exall(namelength+4, proc);
  291. filename[namelength] = 0;
  292. proc(filename, SCAN_ENDDIR, 0);
  293. }
  294. }
  295. else proc(filename, SCAN_FILE, found_files[n_found_files].nFileSizeLow);
  296. }
  297. return;
  298. }
  299. void scan_directory(char *dir,
  300. void (*proc)(char *name, int why, long int size))
  301. {
  302. recursive_scan = 1;
  303. if (dir==NULL || strcmp(dir,".")==0)
  304. { dir = "*.*";
  305. scan_leafstart = 0;
  306. }
  307. else scan_leafstart = strlen(dir)+1;
  308. strcpy(filename, dir);
  309. exall(strlen(filename), proc);
  310. }
  311. void scan_files(char *dir,
  312. void (*proc)(char *name, int why, long int size))
  313. {
  314. recursive_scan = 0;
  315. if (dir==NULL || strcmp(dir,".")==0)
  316. { strcpy(filename, "*.*");
  317. scan_leafstart = 0;
  318. }
  319. else
  320. { scan_leafstart = strlen(dir);
  321. strcpy(filename, dir);
  322. strcpy(filename+scan_leafstart, "\\*.*");
  323. scan_leafstart++;
  324. }
  325. exall(strlen(filename), proc);
  326. }
  327. #else
  328. #ifdef GCC386
  329. /*
  330. * BEWARE: this version will not recognise Windows 95 long file-names
  331. * and so there is very real possibility of muddle.
  332. */
  333. static char filename[LONGEST_LEGAL_FILENAME];
  334. static struct ffblk *found_files = NULL;
  335. static int n_found_files = 0, max_found_files = 0;
  336. #define TABLE_INCREMENT 50
  337. static int more_files()
  338. {
  339. if (n_found_files > max_found_files - 5)
  340. { struct ffblk *fnew = (struct ffblk *)
  341. my_realloc((void *)found_files,
  342. sizeof(struct ffblk) *
  343. (max_found_files + TABLE_INCREMENT));
  344. if (fnew == NULL) return 1; /* failure flag */
  345. found_files = fnew;
  346. max_found_files += TABLE_INCREMENT;
  347. }
  348. return 0;
  349. }
  350. int alphasort_files(const void *a, const void *b)
  351. {
  352. const struct ffblk *fa = (const struct ffblk *)a,
  353. *fb = (const struct ffblk *)b;
  354. return strncmp(fb->ff_name, fa->ff_name, sizeof(fa->ff_name));
  355. }
  356. static void exall(int namelength,
  357. void (*proc)(char *name, int why, long int size))
  358. /*
  359. * This procedure scans a directory-full of files, calling the given procedure
  360. * to process each one it finds.
  361. */
  362. {
  363. int rootlen = namelength;
  364. int code, first = n_found_files;
  365. struct ffblk found;
  366. code = findfirst(filename, &found, FA_DIREC);
  367. while (code == 0)
  368. { if (more_files()) break;
  369. found_files[n_found_files++] = found;
  370. code = findnext(&found);
  371. }
  372. qsort((void *)&found_files[first],
  373. n_found_files-first,
  374. sizeof(struct ffblk),
  375. alphasort_files);
  376. while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
  377. while (n_found_files != first)
  378. { char *p = (char *)&found_files[--n_found_files].ff_name;
  379. int c;
  380. /*
  381. * Fill out filename with the actual name I grabbed, i.e. with
  382. * wild-cards expanded.
  383. */
  384. namelength = rootlen+1;
  385. /*
  386. * I fold DOS filenames into lower case because it does not matter much
  387. * to DOS and I think it looks better - furthermore it helps when I move
  388. * archives to other systems.
  389. */
  390. while ((c = *p++) != 0)
  391. { if (!hostcase) if (isupper(c)) c = tolower(c);
  392. filename[namelength++] = c;
  393. }
  394. filename[namelength] = 0;
  395. if (found_files[n_found_files].ff_attrib & FA_DIREC)
  396. { if (found_files[n_found_files].ff_name[0] != '.')
  397. /*
  398. * I filter out directory names that start with '.'.
  399. * This is to avoid calamity with recursion though chains such as .\.\.\.....
  400. */
  401. { proc(filename, SCAN_STARTDIR, 0);
  402. if (!recursive_scan) continue;
  403. strcpy(&filename[namelength], "\\*.*");
  404. /*
  405. * Append "\*.*" to the directory-name and try again, thereby scanning
  406. * its contents.
  407. */
  408. exall(namelength+4, proc);
  409. filename[namelength] = 0;
  410. proc(filename, SCAN_ENDDIR, 0);
  411. }
  412. }
  413. else proc(filename, SCAN_FILE, found_files[n_found_files].ff_fsize);
  414. }
  415. return;
  416. }
  417. void scan_directory(char *dir,
  418. void (*proc)(char *name, int why, long int size))
  419. {
  420. recursive_scan = 1;
  421. if (dir==NULL || strcmp(dir,".")==0)
  422. { dir = "*.*";
  423. scan_leafstart = 0;
  424. }
  425. else scan_leafstart = strlen(dir)+1;
  426. strcpy(filename, dir);
  427. exall(strlen(filename), proc);
  428. }
  429. void scan_files(char *dir,
  430. void (*proc)(char *name, int why, long int size))
  431. {
  432. recursive_scan = 0;
  433. if (dir==NULL || strcmp(dir,".")==0)
  434. { strcpy(filename, "*.*");
  435. scan_leafstart = 0;
  436. }
  437. else
  438. { scan_leafstart = strlen(dir);
  439. strcpy(filename, dir);
  440. strcpy(filename+scan_leafstart, "\\*.*");
  441. scan_leafstart++;
  442. }
  443. exall(strlen(filename), proc);
  444. }
  445. #else /* __WATCOMC__ */
  446. static char filename[LONGEST_LEGAL_FILENAME];
  447. static struct find_t *found_files = NULL;
  448. static int n_found_files = 0, max_found_files = 0;
  449. #define TABLE_INCREMENT 50
  450. static int more_files()
  451. {
  452. if (n_found_files > max_found_files - 5)
  453. { struct find_t *fnew = (struct find_t *)
  454. my_realloc((void *)found_files,
  455. sizeof(struct find_t) *
  456. (max_found_files + TABLE_INCREMENT));
  457. if (fnew == NULL) return 1; /* failure flag */
  458. found_files = fnew;
  459. max_found_files += TABLE_INCREMENT;
  460. }
  461. return 0;
  462. }
  463. int alphasort_files(const void *a, const void *b)
  464. {
  465. const struct find_t *fa = (const struct find_t *)a,
  466. *fb = (const struct find_t *)b;
  467. return strncmp(fb->name, fa->name, sizeof(fa->name));
  468. }
  469. static void exall(int namelength,
  470. void (*proc)(char *name, int why, long int size))
  471. /*
  472. * This procedure scans a directory-full of files, calling the given procedure
  473. * to process each one it finds.
  474. */
  475. {
  476. int rootlen = namelength;
  477. int code, first = n_found_files;
  478. struct find_t found;
  479. #ifdef FA_DIREC
  480. code = _dos_findfirst(filename, FA_DIREC, &found);
  481. #else
  482. code = _dos_findfirst(filename, _A_SUBDIR, &found);
  483. #endif
  484. while (code == 0)
  485. { if (more_files()) break;
  486. found_files[n_found_files++] = found;
  487. code = _dos_findnext(&found);
  488. }
  489. qsort((void *)&found_files[first],
  490. n_found_files-first,
  491. sizeof(struct find_t),
  492. alphasort_files);
  493. while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
  494. while (n_found_files != first)
  495. { char *p = (char *)&found_files[--n_found_files].name;
  496. int c;
  497. /*
  498. * Fill out filename with the actual name I grabbed, i.e. with
  499. * wild-cards expanded.
  500. */
  501. namelength = rootlen+1;
  502. /*
  503. * I fold DOS filenames into lower case because it does not matter much
  504. * to DOS and I think it looks better - furthermore it helps when I move
  505. * archives to other systems.
  506. */
  507. while ((c = *p++) != 0)
  508. { if (!hostcase) if (isupper(c)) c = tolower(c);
  509. filename[namelength++] = c;
  510. }
  511. filename[namelength] = 0;
  512. #ifdef FA_DIREC
  513. if (found_files[n_found_files].attribute & FA_DIREC)
  514. #else
  515. if (found_files[n_found_files].attrib & _A_SUBDIR)
  516. #endif
  517. { if (found_files[n_found_files].name[0] != '.')
  518. /*
  519. * I filter out directory names that start with '.'.
  520. * This is to avoid calamity with recursion though chains such as .\.\.\.....
  521. */
  522. { proc(filename, SCAN_STARTDIR, 0);
  523. if (!recursive_scan) continue;
  524. strcpy(&filename[namelength], "\\*.*");
  525. /*
  526. * Append "\*.*" to the directory-name and try again, thereby scanning
  527. * its contents.
  528. */
  529. exall(namelength+4, proc);
  530. filename[namelength] = 0;
  531. proc(filename, SCAN_ENDDIR, 0);
  532. }
  533. }
  534. else proc(filename, SCAN_FILE, found_files[n_found_files].size);
  535. }
  536. return;
  537. }
  538. void scan_directory(char *dir,
  539. void (*proc)(char *name, int why, long int size))
  540. {
  541. recursive_scan = 1;
  542. if (dir==NULL || strcmp(dir,".")==0)
  543. { dir = "*.*";
  544. scan_leafstart = 0;
  545. }
  546. else scan_leafstart = strlen(dir)+1;
  547. strcpy(filename, dir);
  548. exall(strlen(filename), proc);
  549. }
  550. void scan_files(char *dir,
  551. void (*proc)(char *name, int why, long int size))
  552. {
  553. recursive_scan = 0;
  554. if (dir==NULL || strcmp(dir,".")==0)
  555. { strcpy(filename, "*.*");
  556. scan_leafstart = 0;
  557. }
  558. else
  559. { scan_leafstart = strlen(dir);
  560. strcpy(filename, dir);
  561. strcpy(filename+scan_leafstart, "\\*.*");
  562. scan_leafstart++;
  563. }
  564. exall(strlen(filename), proc);
  565. }
  566. #endif /* __WATCOMC__ */
  567. #endif /* GCC386 */
  568. #define SCANDIR_KNOWN 1
  569. #else /* MS_DOS */
  570. #ifdef UNIX
  571. static char filename[LONGEST_LEGAL_FILENAME];
  572. /*
  573. * The code here uses opendir, readdir and closedir and as such ought to
  574. * be Posix compatible. The macro USE_DIRECT_H can cause an older variant
  575. * on this idea to be used. BUt it may need adjustment for different
  576. * systems.
  577. */
  578. #ifdef USE_DIRECT_H
  579. #include <sys/types.h>
  580. #include <sys/dir.h>
  581. #else
  582. #include <dirent.h>
  583. #endif
  584. static char **found_files = NULL;
  585. int n_found_files = 0, max_found_files = 0;
  586. #define TABLE_INCREMENT 50
  587. static int more_files()
  588. {
  589. if (n_found_files > max_found_files - 5)
  590. { char **fnew = (char **)
  591. my_realloc((void *)found_files,
  592. sizeof(char *) *
  593. (max_found_files + TABLE_INCREMENT));
  594. if (fnew == NULL) return 1; /* failure flag */
  595. found_files = fnew;
  596. max_found_files += TABLE_INCREMENT;
  597. }
  598. return 0;
  599. }
  600. int alphasort_files(const void *a, const void *b)
  601. {
  602. const char *fa = *(const char **)a,
  603. *fb = *(const char **)b;
  604. return strcmp(fb, fa);
  605. }
  606. extern int stat(const char *, struct stat*);
  607. static void scan_file(int namelength,
  608. void (*proc)(char *name, int why, long int size));
  609. static void exall(int namelength,
  610. void (*proc)(char *name, int why, long int size))
  611. {
  612. DIR *d;
  613. #ifdef USE_DIRECT_H
  614. struct direct *dd;
  615. #else
  616. struct dirent *dd;
  617. #endif
  618. int i, j;
  619. int rootlen = namelength, first = n_found_files;
  620. proc(filename, SCAN_STARTDIR, 0);
  621. d = opendir(filename);
  622. if (d != NULL)
  623. { while ((dd = readdir(d)) != NULL)
  624. { char *leafname = dd->d_name;
  625. char *copyname;
  626. /*
  627. * readdir hands back both "." and ".." but I had better not recurse
  628. * into either!
  629. */
  630. if (strcmp(leafname, ".") == 0 ||
  631. strcmp(leafname, "..") == 0) continue;
  632. if (more_files()) break;
  633. copyname = (char *)malloc(1+strlen(leafname));
  634. if (copyname == NULL) break;
  635. strcpy(copyname, leafname);
  636. found_files[n_found_files++] = copyname;
  637. }
  638. closedir(d);
  639. }
  640. qsort((void *)&found_files[first],
  641. n_found_files-first,
  642. sizeof(char *),
  643. alphasort_files);
  644. filename[rootlen] = '/';
  645. while (n_found_files != first)
  646. { char *p = found_files[--n_found_files];
  647. int c;
  648. namelength = rootlen+1;
  649. while ((c = *p++) != 0) filename[namelength++] = c;
  650. free((void *)found_files[n_found_files]);
  651. filename[namelength] = 0;
  652. scan_file(namelength, proc);
  653. }
  654. filename[rootlen] = 0;
  655. proc(filename, SCAN_ENDDIR, 0);
  656. }
  657. #ifndef S_IFMT
  658. # ifdef __S_IFMT
  659. # define S_IFMT __S_IFMT
  660. # endif
  661. #endif
  662. #ifndef S_IFDIR
  663. # ifdef __S_IFDIR
  664. # define S_IFDIR __S_IFDIR
  665. # endif
  666. #endif
  667. #ifndef S_IFREG
  668. # ifdef __S_IFREG
  669. # define S_IFREG __S_IFREG
  670. # endif
  671. #endif
  672. static void scan_file(int namelength,
  673. void (*proc)(char *name, int why, long int size))
  674. {
  675. struct stat buf;
  676. stat(filename, &buf);
  677. if ((buf.st_mode & S_IFMT) == S_IFDIR)
  678. { if (!recursive_scan) proc(filename, SCAN_STARTDIR, 0);
  679. else exall(namelength, proc);
  680. }
  681. else if ((buf.st_mode & S_IFMT) == S_IFREG)
  682. proc(filename, SCAN_FILE, buf.st_size);
  683. /* else fprintf(stderr, "Mode of %s is %o\n", filename, buf.st_mode); */
  684. }
  685. void scan_directory(char *dir,
  686. void (*proc)(char *name, int why, long int size))
  687. {
  688. recursive_scan = 1;
  689. if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
  690. scan_leafstart = strlen(dir)+1;
  691. strcpy(filename, dir);
  692. scan_file(scan_leafstart-1, proc);
  693. }
  694. void scan_files(char *dir,
  695. void (*proc)(char *name, int why, long int size))
  696. {
  697. recursive_scan = 0;
  698. if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
  699. scan_leafstart = strlen(dir)+1;
  700. strcpy(filename, dir);
  701. exall(scan_leafstart-1, proc);
  702. }
  703. #define SCANDIR_KNOWN 1
  704. #else /* UNIX */
  705. #ifdef __arm
  706. /*
  707. * I want to be able to find the currently-selected directory - if
  708. * only so I can restore it at the end of processing. Yes I do know
  709. * that RISCOS is moving away from the concept of a current directory,
  710. * but this is a command-line utility at present, at least.
  711. */
  712. static void find_current_directory(char dir[])
  713. {
  714. os_gbpbstr block;
  715. char drivename[64];
  716. char name[32];
  717. /*
  718. * osgbpb(5) reads the name of the current disc - which I need as the first
  719. * part of a full-rooted file-path.
  720. */
  721. block.action = 5;
  722. block.file_handle = 0;
  723. block.data_addr = drivename;
  724. os_gbpb(&block);
  725. for (;;)
  726. { int len;
  727. /*
  728. * osgbpb(6) reads the name of the currently selected directory - but just
  729. * the trailing component.
  730. */
  731. block.action = 6;
  732. block.file_handle = 0;
  733. block.data_addr = name;
  734. os_gbpb(&block);
  735. len = name[1];
  736. /* Here I trim trailing blanks from the name */
  737. while (name[len + 1] == ' ' && len > 0) len--;
  738. if (dir[0] == 0)
  739. { memcpy(dir, &name[2], len);
  740. dir[len] = 0;
  741. }
  742. else
  743. { memmove(&dir[1 + len], dir, strlen(dir) + 1);
  744. memcpy(dir, &name[2], len);
  745. dir[len] = '.';
  746. }
  747. /*
  748. * When I find that the director name is '$' I conclude that I must be
  749. * at the top of the tree, and I exit.
  750. */
  751. if (len == 1 && name[2] == '$')
  752. { len = drivename[0];
  753. memmove(&dir[2 + len], dir, strlen(dir) + 1);
  754. memcpy(&dir[1], &drivename[1], len);
  755. dir[0] = ':';
  756. dir[len+1] = '.';
  757. return;
  758. }
  759. /*
  760. * Mostly I select the parent directory, and by reading names of
  761. * successive parents I build up a complete name.
  762. */
  763. system("dir ^");
  764. }
  765. }
  766. #define AT_ONCE 80
  767. #define NAME_LENGTH 12
  768. static char filename[LONGEST_LEGAL_FILENAME];
  769. static char dirsave[LONGEST_LEGAL_FILENAME+4];
  770. /* Original directory the user had selected */
  771. static void exall(int namelength,
  772. void (*proc)(char *name, int why, long int size))
  773. /*
  774. * This procedure scans a directory-full of files, calling the given procedure
  775. * to process each one it finds.
  776. */
  777. {
  778. os_filestr file_status;
  779. /*
  780. * osfile(5) reads information about a file - in particular it allows me to
  781. * tell if I have a simple file or a directory.
  782. */
  783. file_status.action = 5;
  784. file_status.name = filename;
  785. if (os_file(&file_status) != NULL) file_status.action = 99;
  786. switch (file_status.action)
  787. {
  788. default:
  789. /* fprintf(stderr, "\nBad return code from osfile\n"); */
  790. return;
  791. /* case 0: fprintf(stderr, "\nFile %s not found\n", filename); */
  792. /* exit(EXIT_FAILURE); */
  793. case 0xff: /* Protected file */
  794. case 1: proc(filename, SCAN_FILE, file_status.start); /* Simple file */
  795. return;
  796. case 2: /* Here we have a directory to scan */
  797. proc(filename, SCAN_STARTDIR, 0);
  798. if (!recursive_scan) return;
  799. { char workvec[NAME_LENGTH*AT_ONCE];
  800. os_gbpbstr block;
  801. block.seq_point = 0;
  802. for (;;)
  803. { char *p = workvec;
  804. block.action = 9;
  805. block.file_handle = (int)filename;/* Scan given directory */
  806. block.data_addr = workvec;
  807. block.number = AT_ONCE;
  808. block.buf_len = NAME_LENGTH*AT_ONCE;
  809. block.wild_fld = "*";
  810. filename[namelength] = 0;
  811. /*
  812. * osgbpb(9) reads (several) file-names from the specified directory.
  813. */
  814. os_gbpb(&block);
  815. if (block.number == 0) break; /* Nothing transfered */
  816. while (block.number != 0)
  817. { int len = strlen(p);
  818. filename[namelength] = '.';
  819. strcpy(&filename[namelength+1], p);
  820. p += len + 1;
  821. /*
  822. * I concatenate the name of the new directory on the end of my current path
  823. * and recurse into it.
  824. */
  825. exall(namelength + len + 1, proc);
  826. block.number--;
  827. }
  828. if (block.seq_point == -1) break;
  829. }
  830. filename[namelength] = 0;
  831. }
  832. proc(filename, SCAN_ENDDIR, 0);
  833. return;
  834. }
  835. }
  836. void scan_directory(char *dir,
  837. void (*proc)(char *name, int why, long int size))
  838. {
  839. recursive_scan = 1;
  840. strcpy(dirsave, "dir ");
  841. find_current_directory(dirsave+4); /* Find initial directory */
  842. system(dirsave); /* Restore initial directory */
  843. strcpy(filename, dir==NULL ? "@" : dir);
  844. scan_leafstart = strcmp(dir, "@")==0 ? 2 : 0;
  845. exall(strlen(filename), proc);
  846. system(dirsave); /* Restore initial directory */
  847. }
  848. void scan_files(char *dir,
  849. void (*proc)(char *name, int why, long int size))
  850. {
  851. recursive_scan = 0;
  852. strcpy(dirsave, "dir ");
  853. find_current_directory(dirsave+4); /* Find initial directory */
  854. system(dirsave); /* Restore initial directory */
  855. strcpy(filename, dir==NULL ? "@" : dir);
  856. scan_leafstart = strcmp(dir, "@")==0 ? 2 : 0;
  857. exall(strlen(filename), proc);
  858. system(dirsave); /* Restore initial directory */
  859. }
  860. #define SCANDIR_KNOWN 1
  861. #else /* __arm */
  862. #ifdef ATARI
  863. static char filename[LONGEST_LEGAL_FILENAME];
  864. static void exall(int32 namelength,
  865. void (*proc)(char *name, int why, long int size))
  866. /*
  867. * This procedure scans a directory-full of files, calling the given procedure
  868. * to process each one it finds.
  869. */
  870. {
  871. int32 rootlen = namelength;
  872. int code;
  873. struct FILEINFO found;
  874. code = dfind(&found, filename, FA_DIREC);
  875. while (rootlen>=0 && filename[rootlen]!='\\') rootlen--;
  876. /*
  877. * If I used _dos_findfirst I could allow recursion here,
  878. * and if I call findfirst(filename, FA_DIREC) then directories can
  879. * be found as well as just files.
  880. */
  881. while (code == 0)
  882. { char *p = (char *)&found.name;
  883. int c;
  884. /*
  885. * Fill out filename with the actual name I grabbed, i.e. with
  886. * wild-cards expanded.
  887. */
  888. namelength = rootlen+1;
  889. /*
  890. * I fold Atari filenames into lower case because it does not matter much
  891. * to Atari and I think it looks better - furthermore it helps when I move
  892. * archives to other systems.
  893. */
  894. while ((c = *p++) != 0)
  895. { if (!hostcase) c = tolower(c);
  896. filename[namelength++] = c;
  897. }
  898. filename[namelength] = 0;
  899. if (found.attr & FA_DIREC)
  900. { if (found.name[0]!='.')
  901. /*
  902. * I filter out directory names that start with '.'.
  903. * This is to avoid calamity with recursion though chains such as .\.\.\.....
  904. */
  905. { proc(filename, SCAN_STARTDIR, 0);
  906. if (!recursive_scan) continue;
  907. strcpy(&filename[namelength], "\\*.*");
  908. /*
  909. * Append "\*.*" to the directory-name and try again, thereby scanning
  910. * its contents.
  911. */
  912. exall(namelength+4, proc);
  913. filename[namelength] = 0;
  914. proc(filename, SCAN_ENDDIR, 0);
  915. }
  916. }
  917. else proc(filename, SCAN_FILE, found.size);
  918. code = dnext(&found);
  919. }
  920. return;
  921. }
  922. void scan_directory(char *dir,
  923. void (*proc)(char *name, int why, long int size))
  924. {
  925. recursive_scan = 1;
  926. if (dir==NULL || strcmp(dir,".")==0)
  927. { dir = "*.*";
  928. scan_leafstart = 0;
  929. }
  930. else scan_leafstart = strlen(dir)+1;
  931. strcpy(filename, dir);
  932. exall(strlen(filename), proc);
  933. }
  934. void scan_files(char *dir,
  935. void (*proc)(char *name, int why, long int size))
  936. {
  937. recursive_scan = 0;
  938. if (dir==NULL || strcmp(dir,".")==0)
  939. { dir = "*.*";
  940. scan_leafstart = 0;
  941. }
  942. else scan_leafstart = strlen(dir)+1;
  943. strcpy(filename, dir);
  944. exall(strlen(filename), proc);
  945. }
  946. #define SCANDIR_KNOWN 1
  947. #else /* ATARI */
  948. #ifdef macintosh
  949. /*
  950. * I have place-holder routines here, so that at some stage proper versions
  951. * can be written and installed. By leaving the Unix code in as comment I hope
  952. * to help whoever gets around to building the Macintosh-specific versions...
  953. */
  954. static char filename[LONGEST_LEGAL_FILENAME];
  955. #ifdef THIS_NEEDS_RE_WRITING_FOR_MACINTOSH
  956. static int remove_dot(struct direct *name)
  957. {
  958. if (strcmp(name->d_name, ".")==0 || strcmp(name->d_name, "..")==0) return 0;
  959. return 1;
  960. }
  961. #endif
  962. /* int alphasort(struct direct **, struct direct **); */
  963. /* extern int stat(const char *, struct stat*); */
  964. static void scan_file(int namelength,
  965. void (*proc)(char *name, int why, long int size));
  966. static void exall(int namelength,
  967. void (*proc)(char *name, int why, long int size))
  968. {
  969. #ifdef THIS_NEEDS_RE_WRITING_FOR_MACINTOSH
  970. struct direct **namelist;
  971. int i, j;
  972. proc(filename, SCAN_STARTDIR, 0);
  973. if (!recursive_scan) return;
  974. i = scandir(filename, &namelist, remove_dot, alphasort);
  975. for(j=0; j<i; j++)
  976. { char *leafname = namelist[j]->d_name;
  977. filename[namelength] = '/';
  978. strcpy(&filename[namelength+1], leafname);
  979. scan_file(namelength+1+strlen(leafname), proc);
  980. free(namelist[j]);
  981. }
  982. free(namelist);
  983. filename[namelength] = 0;
  984. proc(filename, SCAN_ENDDIR, 0);
  985. #endif
  986. }
  987. static void scan_file(int namelength,
  988. void (*proc)(char *name, int why, long int size))
  989. {
  990. #ifdef THIS_NEEDS_RE_WRITING_FOR_MACINTOSH
  991. struct stat buf;
  992. stat(filename, &buf);
  993. if ((buf.st_mode & S_IFMT) == S_IFDIR) exall(namelength, proc);
  994. else if ((buf.st_mode & S_IFMT) == S_IFREG)
  995. proc(filename, SCAN_FILE, buf.st_size);
  996. /* else fprintf(stderr, "Mode of %s is %o\n", filename, buf.st_mode); */
  997. #endif
  998. }
  999. void scan_directory(char *dir,
  1000. void (*proc)(char *name, int why, long int size))
  1001. {
  1002. recursive_scan = 1;
  1003. if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
  1004. scan_leafstart = strlen(dir)+1;
  1005. strcpy(filename, dir);
  1006. scan_file(scan_leafstart-1, proc);
  1007. }
  1008. void scan_files(char *dir,
  1009. void (*proc)(char *name, int why, long int size))
  1010. {
  1011. recursive_scan = 0;
  1012. if (dir==NULL || strcmp(dir, ".")==0) dir = ".";
  1013. scan_leafstart = strlen(dir)+1;
  1014. strcpy(filename, dir);
  1015. scan_file(scan_leafstart-1, proc);
  1016. }
  1017. #define SCANDIR_KNOWN 1
  1018. #endif /* macintosh */
  1019. #endif /* ATARI */
  1020. #endif /* __arm */
  1021. #endif /* UNIX */
  1022. #endif /* MS_DOS */
  1023. #endif /* WINDOWS_NT */
  1024. #ifndef SCANDIR_KNOWN
  1025. #error "Directory scanning machine-specific code not present"
  1026. #endif
  1027. /* end of scandir.c */