1 /*
2  *  DIRUTE.C
3  *
4  *  Written on 30-Jul-90 by jim nutt and released to the public domain.
5  *
6  */
7 
8 
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <errno.h>
13 #include <assert.h>
14 #include <ctype.h>
15 
16 #include <huskylib/huskylib.h>
17 
18 #define __DIRUTE_C
19 
20 #if defined(__WATCOMC__) && defined(WINNT)
21 #include <wtypes.h>
22 #endif
23 
24 #include "dirute.h"
25 #include "strextra.h"
26 #include "memextra.h"
27 
28 
29 #if defined(OS216)
30 #include <time.h>
31 #include <dos.h>
32 #include <io.h>
33 #include <fcntl.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 
37 #define INCL_DOSPROCESS
38 #include <os2.h>
39 
40 union
41 {
42     FDATE a;
43     unsigned int b;
44 }
45 dc;
46 
47 union
48 {
49     FTIME a;
50     unsigned int b;
51 }
52 tc;
53 
54 static struct _FILEFINDBUF InfoBuf;
55 
56 HDIR hDir;
57 USHORT cSearch;
58 USHORT usAttrib;
59 
60 #define FILENAMELEN 255
61 
dir_findfirst(char * filename,int attribute,struct _dta * dta)62 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
63 {
64     hDir = 0x0001;
65     usAttrib = attribute;
66     cSearch = 1;
67 
68     if (DosFindFirst2(filename, &hDir, usAttrib, &InfoBuf, (USHORT) (sizeof(InfoBuf) * cSearch), &cSearch, FIL_STANDARD, (ULONG) NULL) != 0)
69     {
70         DosFindClose(hDir);
71         errno = ENOENT;
72         return -1;
73     }
74     else
75     {
76         dta->attrib = (char)InfoBuf.attrFile;
77         dta->size = InfoBuf.cbFile;
78         dc.a = InfoBuf.fdateLastWrite;
79         tc.a = InfoBuf.ftimeLastWrite;
80         dta->wr_time = tc.b;
81         dta->wr_date = dc.b;
82         strcpy(dta->name, InfoBuf.achName);
83         errno = 0;
84         return 0;
85     }
86 }
87 
dir_findnext(struct _dta * dta)88 int dir_findnext(struct _dta *dta)
89 {
90     if (DosFindNext(hDir, &InfoBuf, (USHORT) (FILENAMELEN + 23), &cSearch) || cSearch != 1)
91     {
92         DosFindClose(hDir);
93         errno = ENOENT;
94         return -1;
95     }
96     else
97     {
98         dta->attrib = (char)InfoBuf.attrFile;
99         dta->size = InfoBuf.cbFile;
100         dc.a = InfoBuf.fdateLastWrite;
101         tc.a = InfoBuf.ftimeLastWrite;
102         dta->wr_time = tc.b;
103         dta->wr_date = dc.b;
104         strcpy(dta->name, InfoBuf.achName);
105         errno = 0;
106         return 0;
107     }
108 }
109 
110 #error The drive letter routines are not yet implemented for this platform.
111 #error You might want to copypaste them from the 32-bit OS/2 section.
112 
113 #elif defined(OS2)
114 #define INCL_DOSPROCESS
115 #include <os2.h>
116 
117 union
118 {
119     FDATE a;
120     unsigned int b;
121 }
122 dc;
123 
124 union
125 {
126     FTIME a;
127     unsigned int b;
128 }
129 tc;
130 
131 static FILEFINDBUF3 InfoBuf;
132 
133 HDIR hDir;
134 ULONG cSearch;
135 ULONG usAttrib;
136 
137 #define FILENAMELEN 255
138 
dir_findfirst(char * filename,int attribute,struct _dta * dta)139 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
140 {
141     hDir = 0x0001;
142     usAttrib = attribute;
143     cSearch = 1;
144 
145     if (DosFindFirst((unsigned char *)filename, &hDir, usAttrib, &InfoBuf,
146                      sizeof InfoBuf, &cSearch, FIL_STANDARD) != 0)
147     {
148         DosFindClose(hDir);
149         errno = DIRUTE_NONE;
150         return -1;
151     }
152     else
153     {
154         dta->attrib = (char)InfoBuf.attrFile;
155         dta->size = InfoBuf.cbFile;
156         dc.a = InfoBuf.fdateLastWrite;
157         tc.a = InfoBuf.ftimeLastWrite;
158         dta->wr_time = tc.b;
159         dta->wr_date = dc.b;
160         strcpy(dta->name, InfoBuf.achName);
161         errno = 0;
162         return 0;
163     }
164 }
165 
dir_findnext(struct _dta * dta)166 int dir_findnext(struct _dta *dta)
167 {
168     if (DosFindNext(hDir, &InfoBuf, (USHORT) (FILENAMELEN + 23), &cSearch) || cSearch != 1)
169     {
170         DosFindClose(hDir);
171         errno = DIRUTE_NONE;
172         return -1;
173     }
174     else
175     {
176         dta->attrib = (char)InfoBuf.attrFile;
177         dta->size = InfoBuf.cbFile;
178         dc.a = InfoBuf.fdateLastWrite;
179         tc.a = InfoBuf.ftimeLastWrite;
180         dta->wr_time = tc.b;
181         dta->wr_date = dc.b;
182         strcpy(dta->name, InfoBuf.achName);
183         errno = 0;
184         return 0;
185     }
186 }
187 
188 /* drive letter routines for OS/2 32 Bit */
189 
190 const int drive_letters = 1;
191 
dir_setdrive(int d)192 void dir_setdrive(int d)
193 {
194     DosSetDefaultDisk((ULONG)d + 1UL);
195 }
dir_getdrive(void)196 int dir_getdrive(void)
197 {
198     unsigned long drivenr;
199     unsigned long logmap;
200 
201     DosQueryCurrentDisk((PULONG)(&drivenr), (PULONG)(&logmap));
202 
203     return drivenr - 1;
204 }
dir_getdrivelist(void)205 char *dir_getdrivelist(void)
206 {
207     unsigned long drivenr;
208     unsigned long logmap;
209     char *str = xmalloc(27);
210     int i , j;
211     unsigned long comp = 1;
212 
213     DosQueryCurrentDisk((PULONG)(&drivenr), (PULONG)(&logmap));
214 
215     for (i = 0, j = 0; i < 26; i++, comp = comp * 2UL)
216     {
217         if (logmap & comp)
218         {
219             str[j++] = (char)('A' + i);
220         }
221     }
222     str[j] = '\0';
223     return str;
224 }
225 
226 
227 
228 #elif defined(__RSXNT__) || defined(__CYGWIN__) || defined (__MINGW32__) || (defined(_MSC_VER) && (_MSC_VER >= 1200)) || (defined (__WATCOMC__) && defined(__NT__))
229 #define WIN32_LEAN_AND_MEAN
230 #define NOGDI
231 #define NOUSER
232 #include <windows.h>
233 
234 static HANDLE           hDirA = INVALID_HANDLE_VALUE;
235 static WIN32_FIND_DATA  InfoBuf;
236 static DWORD            dwFileAttributes;
237 
238 static int dir_find_common(struct _dta *dta);
239 
dir_findfirst(char * filename,int attribute,struct _dta * dta)240 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
241 {
242     if (hDirA != INVALID_HANDLE_VALUE)
243     {
244         FindClose(hDirA);
245     }
246 
247     dwFileAttributes = attribute;
248 
249     hDirA = FindFirstFile(filename, &InfoBuf);
250     return dir_find_common(dta);
251 }
252 
dir_findnext(struct _dta * dta)253 int dir_findnext(struct _dta *dta)
254 {
255     if (!FindNextFile(hDirA, &InfoBuf))
256     {
257         if (hDirA != INVALID_HANDLE_VALUE)
258         {
259             FindClose(hDirA);
260             hDirA = INVALID_HANDLE_VALUE;
261         }
262     }
263     return dir_find_common(dta);
264 }
265 
dir_find_common(struct _dta * dta)266 static int dir_find_common(struct _dta *dta)
267 {
268     while (hDirA != INVALID_HANDLE_VALUE)
269     {
270         if (!(InfoBuf.dwFileAttributes &
271               (~(dwFileAttributes | DIR_ARCHVD))))
272         {
273             if (strlen(InfoBuf.cFileName) < sizeof(dta->name))
274             {
275                 break;
276             }
277         }
278         if (!FindNextFile(hDirA, &InfoBuf))
279         {
280             if (hDirA != INVALID_HANDLE_VALUE)
281             {
282                 FindClose(hDirA);
283                 hDirA = INVALID_HANDLE_VALUE;
284             }
285         }
286     }
287     if (hDirA != INVALID_HANDLE_VALUE)
288     {
289         dta->attrib = (unsigned char)(InfoBuf.dwFileAttributes);
290         dta->size = (long)(InfoBuf.nFileSizeLow);
291         strcpy(dta->name, InfoBuf.cFileName);
292         errno = 0;
293         return 0;
294     }
295     else
296     {
297         errno = DIRUTE_NONE;
298         return -1;
299     }
300 }
301 
302 /* drive letter routines for RSX compiler on NT 32 Bit */
303 
304 const int drive_letters = 1;
305 
dir_setdrive(int d)306 void dir_setdrive(int d)
307 {
308     char buf[3];
309     buf[0] = d + 'A';
310     buf[1] = ':';
311     buf[2] = '\0';
312     SetCurrentDirectory(buf);
313 }
dir_getdrive(void)314 int dir_getdrive(void)
315 {
316     char buf[FILENAME_MAX + 1];
317 
318 #if defined (__WATCOMC__) || defined(__CYGWIN__) || defined (__MINGW32__) || (defined(_MSC_VER) && (_MSC_VER >= 1200))
319     GetCurrentDirectory(FILENAME_MAX, buf);
320 #else
321     GetCurrentDirectory(buf, FILENAME_MAX);
322 #endif
323     return (toupper(buf[0]) - 'A');
324 }
dir_getdrivelist(void)325 char *dir_getdrivelist(void)
326 {
327     unsigned long logmap;
328     char *str = xmalloc(27);
329     int i , j;
330     unsigned long comp = 1;
331 
332     logmap = GetLogicalDrives();
333 
334     for (i = 0, j = 0; i < 26; i++, comp = comp * 2UL)
335     {
336         if (logmap & comp)
337         {
338             str[j++] = 'A' + i;
339         }
340     }
341     str[j] = '\0';
342     return str;
343 }
344 
345 
346 #elif defined(__TURBOC__)
347 
348 #include <dos.h>
349 #include <dir.h>
350 
351 static struct ffblk ffblk;
352 
dir_findfirst(char * filename,int attribute,struct _dta * dta)353 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
354 {
355     int done;
356 
357     done = findfirst(filename, &ffblk, attribute);
358 
359     if (!done)
360     {
361         strcpy(dta->name, ffblk.ff_name);
362         dta->wr_time = ffblk.ff_fdate;
363         dta->wr_date = ffblk.ff_ftime;
364         dta->size = ffblk.ff_fsize;
365         dta->attrib = ffblk.ff_attrib;
366         return 0;
367     }
368     return -1;
369 }
370 
dir_findnext(struct _dta * dta)371 int dir_findnext(struct _dta *dta)
372 {
373     if (!findnext(&ffblk))
374     {
375         strcpy(dta->name, ffblk.ff_name);
376         dta->wr_time = ffblk.ff_fdate;
377         dta->wr_date = ffblk.ff_ftime;
378         dta->size = ffblk.ff_fsize;
379         dta->attrib = ffblk.ff_attrib;
380         return 0;
381     }
382     return -1;
383 }
384 
385 /* drive letter routines for Borland C */
386 
387 const int drive_letters = 1;
388 
dir_setdrive(int d)389 void dir_setdrive(int d)
390 {
391     setdisk(d);
392 }
dir_getdrive(void)393 int dir_getdrive(void)
394 {
395     return getdisk();
396 }
397 #if defined(WINNT) || defined(__NT__)
398 #define NOUSER
399 #include <windows.h>
dir_getdrivelist(void)400 char *dir_getdrivelist(void)
401 {
402     unsigned long logmap;
403     char *str = xmalloc(27);
404     int i , j;
405     unsigned long comp = 1;
406 
407     logmap = GetLogicalDrives();
408 
409     for (i = 0, j = 0; i < 26; i++, comp = comp * 2UL)
410     {
411         if (logmap & comp)
412         {
413             str[j++] = 'A' + i;
414         }
415     }
416     str[j] = '\0';
417     return str;
418 }
419 #else
dir_getdrivelist(void)420 char *dir_getdrivelist(void)
421 {
422     int ct = 0, curd, i;
423     char *buf = xmalloc(27);
424 
425     curd = getdisk();
426 
427     for (i = 0; i < 26; i++)
428     {
429         setdisk(i);
430         if (i == getdisk())
431         {
432             buf[ct++] = i + 'A';
433         }
434     }
435     buf[ct] = '\0';
436     setdisk(curd);
437 
438     return buf;
439 }
440 #endif
441 
442 #elif defined(PACIFIC)
443 
444 #include "rfind1st.h"
445 
446 static struct DSTRUCT dstr;
447 
dir_findfirst(char * filename,int attribute,struct _dta * dta)448 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
449 {
450     if (rfind_1st(filename, attribute, &dstr) != NULL)
451     {
452         strcpy(dta->name, dstr.NAME);
453         dta->wr_time = dstr.DATE;
454         dta->wr_date = dstr.TIME;
455         dta->size = dstr.FSIZE;
456         dta->attrib = dstr.ATTRIBUTE;
457         return 0;
458     }
459     return -1;
460 }
461 
dir_findnext(struct _dta * dta)462 int dir_findnext(struct _dta *dta)
463 {
464     if (rfind_nxt(&dstr) != NULL)
465     {
466         strcpy(dta->name, dstr.NAME);
467         dta->wr_time = dstr.DATE;
468         dta->wr_date = dstr.TIME;
469         dta->size = dstr.FSIZE;
470         dta->attrib = dstr.ATTRIBUTE;
471         return 0;
472     }
473     return -1;
474 }
475 
476 #error You must implement the drive letter routines for this platform first!
477 
478 #elif defined(SASC)
479 #include <dos.h>
480 
481 extern int __msflag;
482 static struct FileInfoBlock info;
483 static int error;
484 static int oldmsf;
485 
dir_findfirst(char * filename,int attribute,struct _dta * dta)486 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
487 {
488     oldmsf = __msflag;
489     __msflag = 1;
490     error = dfind(&info, filename, 0);
491     __msflag = oldmsf;
492     while (error == 0)
493     {
494         strcpy(dta->name, info.fib_FileName);
495         return 0;
496     }
497     return -1;
498 }
499 
dir_findnext(struct _dta * dta)500 int dir_findnext(struct _dta *dta)
501 {
502     oldmsf = __msflag;
503     __msflag = 1;
504     error = dnext(&info);
505     __msflag = oldmsf;
506     if (error == 0)
507     {
508         strcpy(dta->name, info.fib_FileName);
509         return 0;
510     }
511     return -1;
512 }
513 
514 #error You must implement the drive letter routines for this platform first!
515 
516 #elif defined(UNIX)
517 
518 /*
519  *  Support for UNIX goes here - use the POSIX.1 routines:
520  *    opendir, scandir, readdir, etc.
521  */
522 
523 #include <unistd.h>
524 #include <dirent.h>
525 #include <sys/types.h>
526 #include <sys/stat.h>
527 #include <ctype.h>
528 
529 #if (defined(__unix__) || defined(unix)) && !defined(USG)
530 #include <sys/param.h>          /* used to differentiate BSD from SYSV  */
531 #endif
532 
533 
534 static DIR *dir;
535 static struct dirent *de;
536 static int  ff_attribute;
537 static char firstbit[FILENAME_MAX + 1];
538 static char lastbit[FILENAME_MAX + 1];
539 static char fullname[FILENAME_MAX + 1];
540 
match(const char * name,const char * pattern,int attribute,int mode,int rdonly)541 static int match(const char *name, const char *pattern, int attribute,
542                  int mode, int rdonly)
543 {
544     char *matpattern;
545     char *matname;
546     char *cp;
547     int rc;
548     if (!(attribute & DIR_NO_WILDCARDS))
549     {
550         matpattern = strdup(pattern);
551         matname = strdup(name);
552 
553         if (matpattern == NULL || matname == NULL)
554         {
555             return 0;
556         }
557 
558         if (attribute & DIR_ICASE)
559         {
560             for (cp=matpattern; *cp; cp++)
561             {
562                 *cp = toupper(*cp);
563             }
564             for (cp=matname; *cp; cp++)
565             {
566                 *cp = toupper(*cp);
567             }
568         }
569 
570         rc = patmat(matname, matpattern);
571 
572         free(matname);
573         free(matpattern);
574     }
575     else
576     {
577         rc = !stricmp(pattern, name);
578     }
579 
580     if (rc)
581     {
582         /* the name matches, now check the other criteria */
583 
584         if (!(attribute & DIR_READON)) /* read only files not allowed */
585         {
586             if ((!S_ISDIR(mode)) && (rdonly))
587             {
588                 return 0;
589             }
590         }
591 
592         if (!(attribute & DIR_DIRECT))   /* directories not allowed */
593         {
594             if (S_ISDIR(mode))
595             {
596                 return 0;
597             }
598         }
599 
600         return 1;
601     }
602 
603     return 0;
604 
605 }
606 
607 
dir_findfirst(char * filename,int attribute,struct _dta * dta)608 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
609 {
610     char *p;
611     int fin = 0;
612     struct stat sb;
613 
614     p = strrchr(filename, '/');
615     if (p == NULL)
616     {
617         strcpy(firstbit, ".");
618         strcpy(lastbit, filename);
619     }
620     else
621     {
622         memcpy(firstbit, filename, p - filename);
623         firstbit[p - filename] = '\0';
624         strcpy(lastbit, p + 1);
625     }
626     if (!*firstbit)
627     {
628         dir = opendir("/");
629     }
630     else
631     {
632         dir = opendir(firstbit);
633     }
634     if (dir == NULL)
635     {
636         return -1;
637     }
638     while (!fin)
639     {
640         de = readdir(dir);
641         if (de == NULL)
642         {
643             closedir(dir);
644             dir = NULL;
645             return -1;
646         }
647 
648         strcpy(fullname, firstbit);
649         strcat(fullname, "/");
650         strcat(fullname, de->d_name);
651 
652         if (stat(fullname, &sb))
653         {
654             closedir(dir);
655             dir = NULL;
656             return -1;
657         }
658 
659         if (match(de->d_name, lastbit,
660                   attribute, sb.st_mode, access(fullname, W_OK)))
661         {
662             fin = 1;
663         }
664     }
665     strcpy(dta->name, de->d_name);
666     if (S_ISDIR(sb.st_mode))
667     {
668         dta->attrib = DIR_DIRECT;
669     }
670     else if (access(fullname, W_OK))
671     {
672         dta->attrib = DIR_READON;
673     }
674     else
675     {
676         dta->attrib = DIR_NORMAL;
677     }
678     dta->size = sb.st_size;
679     ff_attribute = attribute;
680     return 0;
681 }
682 
dir_findnext(struct _dta * dta)683 int dir_findnext(struct _dta *dta)
684 {
685     int fin = 0;
686     struct stat sb;
687 
688     while (!fin)
689     {
690         de = readdir(dir);
691         if (de == NULL)
692         {
693             closedir(dir);
694             dir = NULL;
695             return -1;
696         }
697         strcpy(fullname, firstbit);
698         strcat(fullname, "/");
699         strcat(fullname, de->d_name);
700 
701         if (stat(fullname, &sb))
702         {
703             closedir(dir);
704             dir = NULL;
705             return -1;
706         }
707 
708         if (match(de->d_name, lastbit, ff_attribute,
709                   sb.st_mode, access(fullname, W_OK)))
710         {
711             fin = 1;
712         }
713     }
714     strcpy(dta->name, de->d_name);
715     if (S_ISDIR(sb.st_mode))
716     {
717         dta->attrib = DIR_DIRECT;
718     }
719     else if (access(fullname, W_OK))
720     {
721         dta->attrib = DIR_READON;
722     }
723     else
724     {
725         dta->attrib = DIR_NORMAL;
726     }
727     dta->size = sb.st_size;
728     return 0;
729 }
730 
dir_findclose(struct _dta * dta)731 void dir_findclose(struct _dta *dta)
732 {
733     if (dir != NULL)
734     {
735         closedir(dir);
736         dir = NULL;
737     }
738 }
739 
740 /* UNIX does not have drive letters */
741 
742 const int drive_letters = 0;
743 
dir_setdrive(int d)744 void dir_setdrive(int d)
745 {
746 }
dir_getdrive(void)747 int dir_getdrive(void)
748 {
749     return 2;
750 }
dir_getdrivelist(void)751 char *dir_getdrivelist(void)
752 {
753     return xstrdup("C");
754 }
755 
756 
757 
758 #else  /* MSC version of these routines */
759 
760 #include <dos.h>
761 
762 struct find_t ffblk;
763 
dir_findfirst(char * filename,int attribute,struct _dta * dta)764 int dir_findfirst(char *filename, int attribute, struct _dta *dta)
765 {
766     int done;
767 
768     done = _dos_findfirst(filename, attribute, &ffblk);
769 
770     if (!done)
771     {
772         strcpy(dta->name, ffblk.name);
773         dta->wr_time = ffblk.wr_date;
774         dta->wr_date = ffblk.wr_time;
775         dta->size = ffblk.size;
776         dta->attrib = ffblk.attrib;
777         return 0;
778     }
779     return -1;
780 }
781 
dir_findnext(struct _dta * dta)782 int dir_findnext(struct _dta *dta)
783 {
784     if (!_dos_findnext(&ffblk))
785     {
786         strcpy(dta->name, ffblk.name);
787         dta->wr_time = ffblk.wr_date;
788         dta->wr_date = ffblk.wr_time;
789         dta->size = ffblk.size;
790         dta->attrib = ffblk.attrib;
791         return 0;
792     }
793     return -1;
794 }
795 
796 /* Drive letter routines for Microsoft C */
797 /* I have no idea if these ones work. I copied them from Borland. */
798 
799 const int drive_letters = 1;
800 
dir_setdrive(int d)801 void dir_setdrive(int d)
802 {
803     int e = d;
804     _dos_setdrive(d, &e);
805 }
dir_getdrive(void)806 int dir_getdrive(void)
807 {
808     int d;
809     _dos_getdrive(&d);
810     return d;
811 }
812 #if defined(WINNT) || defined(__NT__)
813 #define NOUSER
814 #include <windows.h>
dir_getdrivelist(void)815 char *dir_getdrivelist(void)
816 {
817     ungsigned long logmap;
818     char *str = xmalloc(27);
819     int i , j;
820     unsigned long comp = 1;
821 
822     logmap = GetLogicalDrives();
823 
824     for (i = 0, j = 0; i < 26; i++, comp = comp * 2UL)
825     {
826         if (logmap & comp)
827         {
828             str[j++] = 'A' + i;
829         }
830     }
831     str[j] = '\0';
832     return str;
833 }
834 #else
dir_getdrivelist(void)835 char *dir_getdrivelist(void)
836 {
837     int ct = 0, curd, i;
838     char *buf = xmalloc(27);
839 
840     curd = dir_getdrive();
841 
842     for (i = 0; i < 26; i++)
843     {
844         dir_setdrive(i);
845         if (i == dir_getdrive())
846         {
847             buf[ct++] = i + 'A';
848         }
849     }
850     buf[ct] = '\0';
851     dir_setdrive(curd);
852 
853     return buf;
854 }
855 #endif
856 
857 #endif
858 
859 
860 
861 #ifdef UNIX
862 
863 /* The adaptcase routine behaves as follows: It assumes that pathname
864    is a path name which may contain multiple dashes, and it assumes
865    that you run on a case sensitive file system but want / must to
866    match the path name insensitively. adaptcase takes every path
867    element out of pathname and uses findfirst to check if it
868    exists. If it exists, the path element is replaced by the exact
869    spelling as used by the file system. If it does not exist, it is
870    converted to lowercase.  This allows you to make you program deal
871    with things like mounts of DOS file systems under unix
872 
873    Return value is 1 if the file exists and 0 if not.
874 
875    Attention: Do not ever try to understand this code. I had to do
876    heavy caching and other optimizations in this routine in order to
877    reduce that startup time of msged to a reasonable value. (The
878    problem is that opendir / readdir is very slow ...). If you ever
879    have to fix something in this routine, you'd better rewrite it from
880    scratch.
881 */
882 
883 /* the cache will take  about 30 * 4192 + 30 * 512 * 4 bytes in this
884    configuration, i.e. 180K */
885 
886 #define adaptcase_cachesize 30
887 #define rawcache_stepsize 4192
888 #define cacheindex_stepsize 512
889 
890 struct adaptcase_cache_entry
891 {
892     char *query;
893     char *result;
894     char *raw_cache;
895     size_t *cache_index;
896     size_t n;
897 };
898 static int adaptcase_cache_position = -1;
899 static struct adaptcase_cache_entry adaptcase_cache[adaptcase_cachesize];
900 
901 static char *current_cache;
cache_sort_cmp(const void * a,const void * b)902 static int cache_sort_cmp(const void *a, const void *b)
903 {
904     return stricmp(current_cache+(*((const size_t *)a)),
905                    current_cache+(*((const size_t *)b)));
906 }
907 
cache_find_cmp(const void * a,const void * b)908 static int cache_find_cmp(const void *a, const void *b)
909 {
910     return stricmp((const char *)a, current_cache+(*((const size_t *)b)));
911 }
912 
913 /* #define TRACECACHE */
914 
915 #ifdef BSD
916 #define DIRENTLEN(x) ((x)->d_namlen)
917 #else
918 #define DIRENTLEN(x) (strlen((x)->d_name))
919 #endif
920 
adaptcase(char * pathname)921 void adaptcase(char *pathname)
922 {
923     int i,j,k,l,n,nmax, found=1, addresult=0;
924     size_t *m; size_t raw_high, rawmax;
925     char buf[FILENAME_MAX + 1];
926     DIR *dirp = NULL;
927     struct dirent *dp;
928     char c;
929 
930 #ifdef TRACECACHE
931     FILE *ftrc;
932 #endif
933 
934     if (!*pathname)
935         return;
936 #ifdef TRACECACHE
937     ftrc = fopen ("trace.log", "a");
938     fprintf(ftrc, "--Query: %s\n", pathname);
939 #endif
940 
941     if (adaptcase_cache_position == -1)
942     {
943         /* initialise the cache */
944         memset(adaptcase_cache, 0, adaptcase_cachesize *
945                sizeof(struct adaptcase_cache_entry));
946         adaptcase_cache_position = 0;
947     }
948 
949     k = strlen(pathname);
950     if (k > 2)
951     {
952         for (k = k - 2; k>0 && pathname[k] != '/'; k--);
953     }
954     else
955     {
956         k = 0;
957     }
958 
959     j = 0; i = 0;
960 
961 
962 start_over:
963 
964     if (k != 0)
965     {
966         l = adaptcase_cache_position;
967         do
968         {
969             if (adaptcase_cache[l].query != NULL)
970             {
971                 if ((!memcmp(adaptcase_cache[l].query,pathname,k)) &&
972                     (adaptcase_cache[l].query[k] == '\0'))
973                 {
974                     /* cache hit for the directory */
975 #ifdef TRACECACHE
976                     fprintf (ftrc, "Cache hit for Dir: %s\n",
977                              adaptcase_cache[l].result);
978 #endif
979                     memcpy(buf, adaptcase_cache[l].result, k);
980                     buf[k] = '/';
981                     current_cache=adaptcase_cache[l].raw_cache;
982                     m = bsearch(pathname + k + 1,
983                                 adaptcase_cache[l].cache_index,
984                                 adaptcase_cache[l].n,
985                                 sizeof(size_t),
986                                 cache_find_cmp);
987                     if (m == 0)
988                     {
989 #ifdef TRACECACHE
990                         fprintf (ftrc, "Cache miss for file.\n");
991 #endif
992 
993                         /* file does not exist - convert to lower c. */
994                         for (n = k + 1; pathname[n-1]; n++)
995                         {
996                             buf[n] = tolower(pathname[n]);
997                         }
998                         memcpy(pathname, buf, n-1);
999 #ifdef TRACECACHE
1000                         fprintf(ftrc, "Return: %s\n", pathname);
1001                         fclose(ftrc);
1002 #endif
1003                         return;
1004                     }
1005                     else
1006                     {
1007 #ifdef TRACECACHE
1008                         fprintf (ftrc, "Cache hit for file: %s\n",
1009                                  adaptcase_cache[l].raw_cache+(*m));
1010 #endif
1011 
1012                         /* file does exist = cache hit for the file */
1013                         for (n = k + 1; pathname[n-1]; n++)
1014                         {
1015                             buf[n] =
1016                                 adaptcase_cache[l].raw_cache[(*m) + n - k - 1];
1017                         }
1018                         assert(buf[n-1] == '\0');
1019                         memcpy(pathname, buf, n-1);
1020 #ifdef TRACECACHE
1021                         fprintf(ftrc, "Return: %s\n", pathname);
1022                         fclose(ftrc);
1023 #endif
1024                         return;
1025                     }
1026                 }
1027             }
1028             l = (l == 0) ? adaptcase_cachesize - 1 : l - 1;
1029         } while (l != adaptcase_cache_position);
1030 
1031 #ifdef TRACECACHE
1032         fprintf (ftrc, "Cache miss for directory.\n");
1033 #endif
1034 
1035 
1036         /* no hit for the directory */
1037         addresult = 1;
1038     }
1039 
1040 
1041     while (pathname[i])
1042     {
1043         if (pathname[i] == '/')
1044         {
1045             buf[i] = pathname[i];
1046             if (addresult && i == k)
1047             {
1048                 goto add_to_cache;
1049             }
1050 cache_failure:
1051             i++;
1052             buf[i]='\0';
1053             dirp = opendir(buf);
1054 #ifdef TRACECACHE
1055             if (dirp == NULL)
1056             {
1057                 fprintf (ftrc, "Error opening directory %s\n", buf);
1058             }
1059 #endif
1060         }
1061         else
1062         {
1063             assert(i==0);
1064             dirp = opendir("./");
1065 #ifdef TRACECACHE
1066             if (dirp == NULL)
1067             {
1068                 fprintf (ftrc, "Error opening directory ./\n");
1069             }
1070 #endif
1071         }
1072 
1073         j = i;
1074         for (; pathname[i] && pathname[i]!='/'; i++)
1075             buf[i] = pathname[i];
1076         buf[i] = '\0';
1077         found = 0;
1078 
1079         if (dirp != NULL)
1080         {
1081             while ((dp = readdir(dirp)) != NULL)
1082             {
1083                 if (!strcasecmp(dp->d_name, buf + j))
1084                 {
1085                     /* file exists, take over it's name */
1086 
1087                     assert(i - j == DIRENTLEN(dp));
1088                     memcpy(buf + j, dp->d_name, DIRENTLEN(dp) + 1);
1089                     closedir(dirp);
1090                     dirp = NULL;
1091                     found = 1;
1092                     break;
1093                 }
1094             }
1095         }
1096         if (!found)
1097         {
1098             /* file does not exist - so the rest is brand new and
1099                should be converted to lower case */
1100 
1101             for (i = j; pathname[i]; i++)
1102                 buf[i] = tolower(pathname[i]);
1103             buf[i] = '\0';
1104             if (dirp != NULL)
1105             {
1106                 closedir(dirp);
1107             }
1108             dirp = NULL;
1109             break;
1110         }
1111     }
1112     assert(strlen(pathname) == strlen(buf));
1113 
1114 add_to_cache:
1115     while (addresult)
1116     {
1117         l = adaptcase_cache_position;
1118         l = (l == adaptcase_cachesize - 1) ? 0 : l + 1;
1119 
1120         if (adaptcase_cache[l].query != NULL)
1121         {
1122             free(adaptcase_cache[l].query);
1123             adaptcase_cache[l].query = NULL;
1124         }
1125         if (adaptcase_cache[l].result != NULL)
1126         {
1127             free(adaptcase_cache[l].result);
1128             adaptcase_cache[l].result = NULL;
1129         }
1130         if (adaptcase_cache[l].raw_cache != NULL)
1131         {
1132             free(adaptcase_cache[l].raw_cache);
1133             adaptcase_cache[l].raw_cache = NULL;
1134         }
1135         if ( (adaptcase_cache[l].query = malloc(k + 1)) == NULL ||
1136              (adaptcase_cache[l].result = malloc(k + 1)) == NULL ||
1137              (adaptcase_cache[l].raw_cache =  malloc(rawmax = rawcache_stepsize)) == NULL ||
1138              (adaptcase_cache[l].cache_index = malloc((nmax = cacheindex_stepsize) * sizeof(size_t))) == NULL )
1139         {
1140             goto cache_error;
1141         }
1142 
1143         adaptcase_cache[l].n = 0;
1144         raw_high = 0;
1145 
1146         c = buf[k]; buf[k] = '\0';
1147         if ((dirp = opendir(buf)) == NULL)
1148         {
1149             buf[k] = c;
1150             goto cache_error;
1151         }
1152         buf[k] = c;
1153 
1154         while ((dp = readdir(dirp)) != NULL)
1155         {
1156             if (raw_high + DIRENTLEN(dp) + 1 > rawmax)
1157             {
1158                 if ((adaptcase_cache[l].raw_cache =
1159                      realloc(adaptcase_cache[l].raw_cache,
1160                              rawmax+=rawcache_stepsize)) == NULL)
1161                 {
1162                     goto cache_error;
1163                 }
1164             }
1165 
1166             if (adaptcase_cache[l].n == nmax - 1)
1167             {
1168                 if ((adaptcase_cache[l].cache_index =
1169                      realloc(adaptcase_cache[l].cache_index,
1170                              (nmax+=cacheindex_stepsize) *
1171                              sizeof(size_t))) == NULL)
1172                 {
1173                     goto cache_error;
1174                 }
1175             }
1176 
1177             memcpy (adaptcase_cache[l].raw_cache + raw_high,
1178                     dp->d_name, DIRENTLEN(dp) + 1);
1179             adaptcase_cache[l].cache_index[adaptcase_cache[l].n++] = raw_high;
1180             raw_high += DIRENTLEN(dp) + 1;
1181         }
1182         closedir(dirp);
1183         current_cache=adaptcase_cache[l].raw_cache;
1184         qsort(adaptcase_cache[l].cache_index, adaptcase_cache[l].n,
1185               sizeof(size_t), cache_sort_cmp);
1186 
1187         memcpy(adaptcase_cache[l].query, pathname, k);
1188         adaptcase_cache[l].query[k] = '\0';
1189         memcpy(adaptcase_cache[l].result, buf, k);
1190         adaptcase_cache[l].result[k] = '\0';
1191 
1192         adaptcase_cache_position = l;
1193 
1194 #ifdef TRACECACHE
1195         fprintf  (ftrc, "Sucessfully added cache entry.\n");
1196 #endif
1197         goto start_over;
1198 
1199     cache_error:
1200         if (adaptcase_cache[l].query != NULL)
1201         {
1202             free(adaptcase_cache[l].query);
1203             adaptcase_cache[l].query = NULL;
1204         }
1205         if (adaptcase_cache[l].result != NULL)
1206         {
1207             free(adaptcase_cache[l].result);
1208             adaptcase_cache[l].result = NULL;
1209         }
1210         if (adaptcase_cache[l].raw_cache != NULL)
1211         {
1212             free(adaptcase_cache[l].raw_cache);
1213             adaptcase_cache[l].raw_cache = NULL;
1214         }
1215         if (adaptcase_cache[l].cache_index != NULL)
1216         {
1217             free(adaptcase_cache[l].cache_index);
1218             adaptcase_cache[l].cache_index = NULL;
1219         }
1220         if (dirp != NULL)
1221         {
1222             closedir(dirp);
1223         }
1224 #ifdef TRACECACHE
1225         fprintf  (ftrc, "Error in building cache entry.\n");
1226 #endif
1227         addresult = 0;
1228         goto cache_failure;
1229     }
1230 
1231 #ifdef TRACECACHE
1232     fprintf(ftrc, "Return: %s\n", pathname);
1233     fclose(ftrc);
1234 #endif
1235     strcpy(pathname, buf);
1236     return;
1237 }
1238 #endif
1239 
1240 #if defined (TEST)
main(int argc,char ** argv)1241 int main(int argc, char **argv)
1242 {
1243         if (argc < 2)
1244                 return 0;
1245 
1246         adaptcase(argv[1]);
1247         printf ("%s\n", argv[1]);
1248 
1249         return 0;
1250 }
1251 
1252 #endif
1253 
1254 
1255