Remove the forced lower-casing in listdirectory on Windows, as it breaks
[xonotic/darkplaces.git] / filematch.c
1
2 #ifdef WIN32
3 #include <windows.h>
4 #else
5 #include <dirent.h>
6 #endif
7
8 #include "quakedef.h"
9
10 // LordHavoc: some portable directory listing code I wrote for lmp2pcx, now used in darkplaces to load id1/*.pak and such...
11
12 int matchpattern(const char *in, const char *pattern, int caseinsensitive)
13 {
14         return matchpattern_with_separator(in, pattern, caseinsensitive, "/\\:", false);
15 }
16
17 // wildcard_least_one: if true * matches 1 or more characters
18 //                     if false * matches 0 or more characters
19 int matchpattern_with_separator(const char *in, const char *pattern, int caseinsensitive, const char *separators, qboolean wildcard_least_one)
20 {
21         int c1, c2;
22         while (*pattern)
23         {
24                 switch (*pattern)
25                 {
26                 case 0:
27                         return 1; // end of pattern
28                 case '?': // match any single character
29                         if (*in == 0 || strchr(separators, *in))
30                                 return 0; // no match
31                         in++;
32                         pattern++;
33                         break;
34                 case '*': // match anything until following string
35                         if(wildcard_least_one)
36                         {
37                                 if (*in == 0 || strchr(separators, *in))
38                                         return 0; // no match
39                                 in++;
40                         }
41                         pattern++;
42                         while (*in)
43                         {
44                                 if (strchr(separators, *in))
45                                         break;
46                                 // see if pattern matches at this offset
47                                 if (matchpattern_with_separator(in, pattern, caseinsensitive, separators, wildcard_least_one))
48                                         return 1;
49                                 // nope, advance to next offset
50                                 in++;
51                         }
52                         break;
53                 default:
54                         if (*in != *pattern)
55                         {
56                                 if (!caseinsensitive)
57                                         return 0; // no match
58                                 c1 = *in;
59                                 if (c1 >= 'A' && c1 <= 'Z')
60                                         c1 += 'a' - 'A';
61                                 c2 = *pattern;
62                                 if (c2 >= 'A' && c2 <= 'Z')
63                                         c2 += 'a' - 'A';
64                                 if (c1 != c2)
65                                         return 0; // no match
66                         }
67                         in++;
68                         pattern++;
69                         break;
70                 }
71         }
72         if (*in)
73                 return 0; // reached end of pattern but not end of input
74         return 1; // success
75 }
76
77 // a little strings system
78 void stringlistinit(stringlist_t *list)
79 {
80         memset(list, 0, sizeof(*list));
81 }
82
83 void stringlistfreecontents(stringlist_t *list)
84 {
85         int i;
86         for (i = 0;i < list->numstrings;i++)
87         {
88                 if (list->strings[i])
89                         Z_Free(list->strings[i]);
90                 list->strings[i] = NULL;
91         }
92         list->numstrings = 0;
93         list->maxstrings = 0;
94         if (list->strings)
95                 Z_Free(list->strings);
96         list->strings = NULL;
97 }
98
99 void stringlistappend(stringlist_t *list, const char *text)
100 {
101         size_t textlen;
102         char **oldstrings;
103
104         if (list->numstrings >= list->maxstrings)
105         {
106                 oldstrings = list->strings;
107                 list->maxstrings += 4096;
108                 list->strings = (char **) Z_Malloc(list->maxstrings * sizeof(*list->strings));
109                 if (list->numstrings)
110                         memcpy(list->strings, oldstrings, list->numstrings * sizeof(*list->strings));
111                 if (oldstrings)
112                         Z_Free(oldstrings);
113         }
114         textlen = strlen(text) + 1;
115         list->strings[list->numstrings] = (char *) Z_Malloc(textlen);
116         memcpy(list->strings[list->numstrings], text, textlen);
117         list->numstrings++;
118 }
119
120 static int stringlistsort_cmp(const void *a, const void *b)
121 {
122         return strcasecmp(*(const char **)a, *(const char **)b);
123 }
124
125 void stringlistsort(stringlist_t *list, qboolean uniq)
126 {
127         int i, j;
128         if(list->numstrings < 1)
129                 return;
130         qsort(&list->strings[0], list->numstrings, sizeof(list->strings[0]), stringlistsort_cmp);
131         if(uniq)
132         {
133                 // i: the item to read
134                 // j: the item last written
135                 for (i = 1, j = 0; i < list->numstrings; ++i)
136                 {
137                         char *save;
138                         if(!strcasecmp(list->strings[i], list->strings[j]))
139                                 continue;
140                         ++j;
141                         save = list->strings[j];
142                         list->strings[j] = list->strings[i];
143                         list->strings[i] = save;
144                 }
145                 for(i = j+1; i < list->numstrings; ++i)
146                 {
147                         if (list->strings[i])
148                                 Z_Free(list->strings[i]);
149                 }
150                 list->numstrings = j+1;
151         }
152 }
153
154 // operating system specific code
155 static void adddirentry(stringlist_t *list, const char *path, const char *name)
156 {
157         if (strcmp(name, ".") && strcmp(name, ".."))
158         {
159                 char temp[MAX_OSPATH];
160                 dpsnprintf( temp, sizeof( temp ), "%s%s", path, name );
161                 stringlistappend(list, temp);
162         }
163 }
164 #ifdef WIN32
165 void listdirectory(stringlist_t *list, const char *basepath, const char *path)
166 {
167         int i;
168         char pattern[4096], *c;
169         WIN32_FIND_DATA n_file;
170         HANDLE hFile;
171         strlcpy (pattern, basepath, sizeof(pattern));
172         strlcat (pattern, path, sizeof (pattern));
173         strlcat (pattern, "*", sizeof (pattern));
174         // ask for the directory listing handle
175         hFile = FindFirstFile(pattern, &n_file);
176         if(hFile == INVALID_HANDLE_VALUE)
177                 return;
178         do {
179                 adddirentry(list, path, n_file.cFileName);
180         } while (FindNextFile(hFile, &n_file) != 0);
181         FindClose(hFile);
182 }
183 #else
184 void listdirectory(stringlist_t *list, const char *basepath, const char *path)
185 {
186         char fullpath[MAX_OSPATH];
187         DIR *dir;
188         struct dirent *ent;
189         dpsnprintf(fullpath, sizeof(fullpath), "%s%s", basepath, path);
190 #ifdef __ANDROID__
191         // SDL currently does not support listing assets, so we have to emulate
192         // it. We're using relative paths for assets, so that will do.
193         if (basepath[0] != '/')
194         {
195                 char listpath[MAX_OSPATH];
196                 qfile_t *listfile;
197                 dpsnprintf(listpath, sizeof(listpath), "%sls.txt", fullpath);
198                 char *buf = (char *) FS_SysLoadFile(listpath, tempmempool, true, NULL);
199                 if (!buf)
200                         return;
201                 char *p = buf;
202                 for (;;)
203                 {
204                         char *q = strchr(p, '\n');
205                         if (q == NULL)
206                                 break;
207                         *q = 0;
208                         adddirentry(list, path, p);
209                         p = q + 1;
210                 }
211                 Mem_Free(buf);
212                 return;
213         }
214 #endif
215         dir = opendir(fullpath);
216         if (!dir)
217                 return;
218
219         while ((ent = readdir(dir)))
220                 adddirentry(list, path, ent->d_name);
221         closedir(dir);
222 }
223 #endif
224