scandir.c 39 KB

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