1 /**
2  * \file   os_match.c
3  * \brief  Match files and directories.
4  * \author Copyright (c) 2002-2014 Jason Perkins and the Premake project
5  */
6 
7 #include <stdlib.h>
8 #include <string.h>
9 #include "premake.h"
10 
11 
12 #if PLATFORM_WINDOWS
13 
14 typedef struct struct_MatchInfo
15 {
16 	HANDLE handle;
17 	int    is_first;
18 	WIN32_FIND_DATAW entry;
19 } MatchInfo;
20 
os_matchstart(lua_State * L)21 int os_matchstart(lua_State* L)
22 {
23 	const char* mask = luaL_checkstring(L, 1);
24 	MatchInfo* m;
25 
26 	wchar_t wide_mask[PATH_MAX];
27 	if (MultiByteToWideChar(CP_UTF8, 0, mask, -1, wide_mask, PATH_MAX) == 0)
28 	{
29 		lua_pushstring(L, "unable to encode mask");
30 		return lua_error(L);
31 	}
32 
33 	m = (MatchInfo*)malloc(sizeof(MatchInfo));
34 
35 	m->handle = FindFirstFileW(wide_mask, &m->entry);
36 	m->is_first = 1;
37 	lua_pushlightuserdata(L, m);
38 	return 1;
39 }
40 
os_matchdone(lua_State * L)41 int os_matchdone(lua_State* L)
42 {
43 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
44 	if (m->handle != INVALID_HANDLE_VALUE)
45 		FindClose(m->handle);
46 	free(m);
47 	return 0;
48 }
49 
os_matchname(lua_State * L)50 int os_matchname(lua_State* L)
51 {
52 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
53 
54 	char filename[PATH_MAX];
55 	if (WideCharToMultiByte(CP_UTF8, 0, m->entry.cFileName, -1, filename, PATH_MAX, NULL, NULL) == 0)
56 	{
57 		lua_pushstring(L, "unable to decode filename");
58 		return lua_error(L);
59 	}
60 
61 	lua_pushstring(L, filename);
62 	return 1;
63 }
64 
os_matchisfile(lua_State * L)65 int os_matchisfile(lua_State* L)
66 {
67 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
68 	lua_pushboolean(L, (m->entry.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == 0);
69 	return 1;
70 }
71 
os_matchnext(lua_State * L)72 int os_matchnext(lua_State* L)
73 {
74 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
75 	if (m->handle == INVALID_HANDLE_VALUE) {
76 		return 0;
77 	}
78 
79 	while (m)  /* loop forever */
80 	{
81 		if (m->is_first)
82 			m->is_first = 0;
83 		else
84 		{
85 			if (!FindNextFileW(m->handle, &m->entry))
86 				return 0;
87 		}
88 
89 		if (wcscmp(m->entry.cFileName, L".") != 0 && wcscmp(m->entry.cFileName, L"..") != 0)
90 		{
91 			lua_pushboolean(L, 1);
92 			return 1;
93 		}
94 	}
95 
96 	return 0;
97 }
98 
99 #else
100 
101 #include <dirent.h>
102 #include <fnmatch.h>
103 #include <sys/stat.h>
104 
105 typedef struct struct_MatchInfo
106 {
107 	DIR* handle;
108 	struct dirent* entry;
109 	char* path;
110 	char* mask;
111 } MatchInfo;
112 
os_matchstart(lua_State * L)113 int os_matchstart(lua_State* L)
114 {
115 	const char* split;
116 	const char* mask = luaL_checkstring(L, 1);
117 	MatchInfo* m = (MatchInfo*)malloc(sizeof(MatchInfo));
118 
119 	/* split the mask into path and filename components */
120 	split = strrchr(mask, '/');
121 	if (split)
122 	{
123 		m->path = (char*)malloc(split - mask + 1);
124 		strncpy(m->path, mask, split - mask);
125 		m->path[split - mask] = '\0';
126 		m->mask = (char*)malloc(mask + strlen(mask) - split);
127 		strcpy(m->mask, split + 1);
128 	}
129 	else
130 	{
131 		m->path = (char*)malloc(2);
132 		strcpy(m->path, ".");
133 		m->mask = (char*)malloc(strlen(mask)+1);
134 		strcpy(m->mask, mask);
135 	}
136 
137 	m->handle = opendir(m->path);
138 	lua_pushlightuserdata(L, m);
139 	return 1;
140 }
141 
os_matchdone(lua_State * L)142 int os_matchdone(lua_State* L)
143 {
144 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
145 	if (m->handle != NULL)
146 		closedir(m->handle);
147 	free(m->path);
148 	free(m->mask);
149 	free(m);
150 	return 0;
151 }
152 
os_matchname(lua_State * L)153 int os_matchname(lua_State* L)
154 {
155 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
156 	lua_pushstring(L, m->entry->d_name);
157 	return 1;
158 }
159 
os_matchisfile(lua_State * L)160 int os_matchisfile(lua_State* L)
161 {
162 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
163 #if defined(_DIRENT_HAVE_D_TYPE)
164 	// Dirent marks symlinks as DT_LNK, not (DT_LNK|DT_DIR). The fallback handles symlinks using stat.
165 	if (m->entry->d_type == DT_DIR)
166 	{
167 		lua_pushboolean(L, 0);
168 	}
169 	else
170 #endif
171 	{
172 		const char* fname;
173 		lua_pushfstring(L, "%s/%s", m->path, m->entry->d_name);
174 		fname = lua_tostring(L, -1);
175 		lua_pop(L, 1);
176 
177 		lua_pushboolean(L, do_isfile(L, fname));
178 	}
179 	return 1;
180 }
181 
os_matchnext(lua_State * L)182 int os_matchnext(lua_State* L)
183 {
184 	MatchInfo* m = (MatchInfo*)lua_touserdata(L, 1);
185 	if (m->handle == NULL) {
186 		return 0;
187 	}
188 
189 	m->entry = readdir(m->handle);
190 	while (m->entry != NULL)
191 	{
192 		const char* name = m->entry->d_name;
193 		if (strcmp(name, ".") != 0 && strcmp(name, "..") != 0)
194 		{
195 			if (fnmatch(m->mask, name, 0) == 0)
196 			{
197 				lua_pushboolean(L, 1);
198 				return 1;
199 			}
200 		}
201 		m->entry = readdir(m->handle);
202 	}
203 
204 	return 0;
205 }
206 
207 #endif
208