Fix setinfo.
[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         char pattern[4096];
168         WIN32_FIND_DATA n_file;
169         HANDLE hFile;
170         strlcpy (pattern, basepath, sizeof(pattern));
171         strlcat (pattern, path, sizeof (pattern));
172         strlcat (pattern, "*", sizeof (pattern));
173         // ask for the directory listing handle
174         hFile = FindFirstFile(pattern, &n_file);
175         if(hFile == INVALID_HANDLE_VALUE)
176                 return;
177         do {
178                 adddirentry(list, path, n_file.cFileName);
179         } while (FindNextFile(hFile, &n_file) != 0);
180         FindClose(hFile);
181 }
182 #else
183 void listdirectory(stringlist_t *list, const char *basepath, const char *path)
184 {
185         char fullpath[MAX_OSPATH];
186         DIR *dir;
187         struct dirent *ent;
188         dpsnprintf(fullpath, sizeof(fullpath), "%s%s", basepath, path);
189 #ifdef __ANDROID__
190         // SDL currently does not support listing assets, so we have to emulate
191         // it. We're using relative paths for assets, so that will do.
192         if (basepath[0] != '/')
193         {
194                 char listpath[MAX_OSPATH];
195                 qfile_t *listfile;
196                 dpsnprintf(listpath, sizeof(listpath), "%sls.txt", fullpath);
197                 char *buf = (char *) FS_SysLoadFile(listpath, tempmempool, true, NULL);
198                 if (!buf)
199                         return;
200                 char *p = buf;
201                 for (;;)
202                 {
203                         char *q = strchr(p, '\n');
204                         if (q == NULL)
205                                 break;
206                         *q = 0;
207                         adddirentry(list, path, p);
208                         p = q + 1;
209                 }
210                 Mem_Free(buf);
211                 return;
212         }
213 #endif
214         dir = opendir(fullpath);
215         if (!dir)
216                 return;
217
218         while ((ent = readdir(dir)))
219                 adddirentry(list, path, ent->d_name);
220         closedir(dir);
221 }
222 #endif
223