shlwapi: Lowercase schemes in UrlCombine function.
[wine] / tools / winedump / search.c
1 /*
2  *  Prototype search and parsing functions
3  *
4  *  Copyright 2000 Jon Griffiths
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #include "config.h"
22 #include "wine/port.h"
23
24 #include "winedump.h"
25
26 static char *grep_buff = NULL;
27 static char *fgrep_buff = NULL;
28
29 static int symbol_from_prototype (parsed_symbol *sym, const char *prototype);
30 static const char *get_type (parsed_symbol *sym, const char *proto, int arg);
31
32
33 /*******************************************************************
34  *         symbol_search
35  *
36  * Call Patrik Stridvall's 'function_grep.pl' script to retrieve a
37  * function prototype from include file(s)
38  */
39 int symbol_search (parsed_symbol *sym)
40 {
41   static const size_t MAX_RESULT_LEN = 1024;
42   FILE *grep;
43   int attempt = 0;
44
45   assert (globals.do_code);
46   assert (globals.directory);
47   assert (sym && sym->symbol);
48
49   if (!symbol_is_valid_c (sym))
50     return - 1;
51
52   if (!grep_buff)
53     grep_buff = malloc (MAX_RESULT_LEN);
54
55   if (!fgrep_buff)
56     fgrep_buff = malloc (MAX_RESULT_LEN);
57
58   if (!grep_buff || !fgrep_buff)
59     fatal ("Out of Memory");
60
61   /* Use 'grep' to tell us which possible files the function is in,
62    * then use 'function_grep.pl' to get the prototype. If this fails the
63    * first time then give grep a more general query (that doesn't
64    * require an opening argument brace on the line with the function name).
65    */
66   while (attempt < 2)
67   {
68     FILE *f_grep;
69     char *cmd = str_create (4, "grep -d recurse -l \"", sym->symbol,
70         !attempt ? "[:blank:]*(\" " : "\" ", globals.directory);
71
72     if (VERBOSE)
73       puts (cmd);
74
75     fflush (NULL); /* See 'man popen' */
76
77     if (!(grep = popen (cmd, "r")))
78       fatal ("Cannot execute grep -l");
79     free (cmd);
80
81     while (fgets (grep_buff, MAX_RESULT_LEN, grep))
82     {
83       int i;
84       const char *extension = grep_buff;
85       for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++) {
86         if (grep_buff[i] == '.')
87           extension = &grep_buff[i];
88       }
89       grep_buff[i] = '\0';
90
91       /* Definitely not in these: */
92       if (strcmp(extension,".dll") == 0 ||
93           strcmp(extension,".lib") == 0 ||
94           strcmp(extension,".so")  == 0 ||
95           strcmp(extension,".o")   == 0)
96         continue;
97
98       if (VERBOSE)
99         puts (grep_buff);
100
101       cmd = str_create (5, "function_grep.pl ", sym->symbol,
102                         " \"", grep_buff, "\"");
103
104       if (VERBOSE)
105         puts (cmd);
106
107       fflush (NULL); /* See 'man popen' */
108
109       if (!(f_grep = popen (cmd, "r")))
110         fatal ("Cannot execute function_grep.pl");
111       free (cmd);
112
113       while (fgets (grep_buff, MAX_RESULT_LEN, f_grep))
114       {
115         char *iter = grep_buff;
116
117         /* Keep only the first line */
118         symbol_clean_string(grep_buff);
119
120         for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
121           ;
122         grep_buff[i] = '\0';
123
124         if (VERBOSE)
125           puts (grep_buff);
126
127         while ((iter = strstr (iter, sym->symbol)))
128         {
129           if (iter > grep_buff && (iter[-1] == ' ' || iter[-1] == '*') &&
130              (iter[strlen (sym->symbol)] == ' ' ||
131               iter[strlen (sym->symbol)] == '('))
132           {
133             if (VERBOSE)
134               printf ("Prototype '%s' looks OK, processing\n", grep_buff);
135
136             if (!symbol_from_prototype (sym, grep_buff))
137             {
138               pclose (f_grep);
139               pclose (grep);
140               return 0;  /* OK */
141             }
142             if (VERBOSE)
143               puts ("Failed, trying next");
144           }
145           else
146             iter += strlen (sym->symbol);
147         }
148       }
149       pclose (f_grep);
150     }
151     pclose (grep);
152     attempt++;
153   }
154
155   return -1; /* Not found */
156 }
157
158
159 /*******************************************************************
160  *         symbol_from_prototype
161  *
162  * Convert a C prototype into a symbol
163  */
164 static int symbol_from_prototype (parsed_symbol *sym, const char *proto)
165 {
166   const char *iter;
167   int found;
168
169   proto = get_type (sym, proto, -1); /* Get return type */
170   if (!proto)
171     return -1;
172
173   iter = str_match (proto, sym->symbol, &found);
174
175   if (!found)
176   {
177     char *call;
178     /* Calling Convention */
179     iter = strchr (iter, ' ');
180     if (!iter)
181       return -1;
182
183     call = str_substring (proto, iter);
184
185     if (!strcasecmp (call, "cdecl") || !strcasecmp (call, "__cdecl"))
186       sym->flags |= SYM_CDECL;
187     else
188       sym->flags |= SYM_STDCALL;
189     free (call);
190     iter = str_match (iter, sym->symbol, &found);
191
192     if (!found)
193       return -1;
194
195     if (VERBOSE)
196       printf ("Using %s calling convention\n",
197               sym->flags & SYM_CDECL ? "cdecl" : "stdcall");
198   }
199   else
200     sym->flags = CALLING_CONVENTION;
201
202   sym->function_name = strdup (sym->symbol);
203   proto = iter;
204
205   /* Now should be the arguments */
206   if (*proto++ != '(')
207     return -1;
208
209   for (; *proto == ' '; proto++);
210
211   if (!strncmp (proto, "void", 4))
212     return 0;
213
214   do
215   {
216     /* Process next argument */
217     str_match (proto, "...", &sym->varargs);
218     if (sym->varargs)
219       return 0;
220
221     if (!(proto = get_type (sym, proto, sym->argc)))
222       return -1;
223
224     sym->argc++;
225
226     if (*proto == ',')
227       proto++;
228     else if (*proto != ')')
229       return -1;
230
231   } while (*proto != ')');
232
233   return 0;
234 }
235
236
237 /*******************************************************************
238  *         get_type
239  *
240  * Read a type from a prototype
241  */
242 static const char *get_type (parsed_symbol *sym, const char *proto, int arg)
243 {
244   int is_const, is_volatile, is_struct, is_signed, is_unsigned, ptrs = 0;
245   const char *iter, *base_type, *catch_unsigned, *proto_str;
246   char dest_type, *type_str;
247
248   assert (sym && sym->symbol);
249   assert (proto && *proto);
250   assert (arg < 0 || (unsigned)arg == sym->argc);
251
252
253   proto_str = str_match (proto, "const", &is_const);
254   proto_str = str_match (proto_str, "volatile", &is_volatile);
255   proto_str = str_match (proto_str, "struct", &is_struct);
256   if (!is_struct)
257     proto_str = str_match (proto_str, "union", &is_struct);
258
259   catch_unsigned = proto_str;
260
261   proto_str = str_match (proto_str, "unsigned", &is_unsigned);
262   proto_str = str_match (proto_str, "signed", &is_signed);
263
264   /* Can have 'unsigned const' or 'const unsigned' etc */
265   if (!is_const)
266     proto_str = str_match (proto_str, "const", &is_const);
267   if (!is_volatile)
268     proto_str = str_match (proto_str, "volatile", &is_volatile);
269
270   base_type = proto_str;
271   iter = str_find_set (proto_str, " ,*)");
272   if (!iter)
273     return NULL;
274
275   if (arg < 0 && (is_signed || is_unsigned))
276   {
277     /* Prevent calling convention from being swallowed by 'un/signed' alone */
278     if (strncmp (base_type, "int", 3) && strncmp (base_type, "long", 4) &&
279         strncmp (base_type, "short", 5) && strncmp (base_type, "char", 4))
280     {
281       iter = proto_str;
282       base_type = catch_unsigned;
283     } else
284       catch_unsigned = NULL;
285   }
286   else
287     catch_unsigned = NULL;
288
289   /* FIXME: skip const/volatile here too */
290   for (proto_str = iter; *proto_str; proto_str++)
291     if (*proto_str == '*')
292       ptrs++;
293     else if (*proto_str != ' ')
294       break;
295
296   if (!*proto_str)
297     return NULL;
298
299   type_str = str_substring (proto, proto_str);
300   if (iter == base_type || catch_unsigned)
301   {
302     /* 'unsigned' with no type */
303     char *tmp = str_create (2, type_str, " int");
304     free (type_str);
305     type_str = tmp;
306   }
307   symbol_clean_string (type_str);
308
309   dest_type = symbol_get_type (type_str);
310
311   if (arg < 0)
312   {
313     sym->return_text = (char*)type_str;
314     sym->return_type = dest_type;
315   }
316   else
317   {
318     sym->arg_type [arg] = dest_type;
319     sym->arg_flag [arg] = is_const ? CT_CONST : is_volatile ? CT_VOLATILE : 0;
320
321     if (*proto_str == ',' || *proto_str == ')')
322       sym->arg_name [arg] = str_create_num (1, arg, "arg");
323     else
324     {
325       iter = str_find_set (proto_str, " ,)");
326       if (!iter)
327       {
328         free (type_str);
329         return NULL;
330       }
331       sym->arg_name [arg] = str_substring (proto_str, iter);
332       proto_str = iter;
333     }
334     sym->arg_text [arg] = (char*)type_str;
335
336   }
337   return proto_str;
338 }
339
340
341 #ifdef __GNUC__
342 /*******************************************************************
343  *         search_cleanup
344  *
345  * Free memory used while searching (a niceity)
346  */
347 void search_cleanup (void) __attribute__ ((destructor));
348 void search_cleanup (void)
349 {
350   free (grep_buff);
351   free (fgrep_buff);
352 }
353 #endif