Check that there are some methods to iterate through before iterating
[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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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 = (char *) malloc (MAX_RESULT_LEN);
54
55   if (!fgrep_buff)
56     fgrep_buff = (char *) 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, *type_str, *base_type, *catch_unsigned;
246   char dest_type;
247
248   assert (sym && sym->symbol);
249   assert (proto && *proto);
250   assert (arg < 0 || (unsigned)arg == sym->argc);
251
252   type_str = proto;
253
254   proto = str_match (proto, "const", &is_const);
255   proto = str_match (proto, "volatile", &is_volatile);
256   proto = str_match (proto, "struct", &is_struct);
257   if (!is_struct)
258     proto = str_match (proto, "union", &is_struct);
259
260   catch_unsigned = proto;
261
262   proto = str_match (proto, "unsigned", &is_unsigned);
263   proto = str_match (proto, "signed", &is_signed);
264
265   /* Can have 'unsigned const' or 'const unsigned' etc */
266   if (!is_const)
267     proto = str_match (proto, "const", &is_const);
268   if (!is_volatile)
269     proto = str_match (proto, "volatile", &is_volatile);
270
271   base_type = proto;
272   iter = str_find_set (proto, " ,*)");
273   if (!iter)
274     return NULL;
275
276   if (arg < 0 && (is_signed || is_unsigned))
277   {
278     /* Prevent calling convention from being swallowed by 'un/signed' alone */
279     if (strncmp (base_type, "int", 3) && strncmp (base_type, "long", 4) &&
280         strncmp (base_type, "short", 5) && strncmp (base_type, "char", 4))
281     {
282       iter = proto;
283       base_type = catch_unsigned;
284     } else
285       catch_unsigned = NULL;
286   }
287   else
288     catch_unsigned = NULL;
289
290   /* FIXME: skip const/volatile here too */
291   for (proto = iter; *proto; proto++)
292     if (*proto == '*')
293       ptrs++;
294     else if (*proto != ' ')
295       break;
296
297   if (!*proto)
298     return NULL;
299
300   type_str = str_substring (type_str, proto);
301   if (iter == base_type || catch_unsigned)
302   {
303     /* 'unsigned' with no type */
304     char *tmp = str_create (2, type_str, " int");
305     free ((char*)type_str);
306     type_str = tmp;
307   }
308   symbol_clean_string (type_str);
309
310   dest_type = symbol_get_type (type_str);
311
312   if (arg < 0)
313   {
314     sym->return_text = (char*)type_str;
315     sym->return_type = dest_type;
316   }
317   else
318   {
319     sym->arg_type [arg] = dest_type;
320     sym->arg_flag [arg] = is_const ? CT_CONST : is_volatile ? CT_VOLATILE : 0;
321
322     if (*proto == ',' || *proto == ')')
323       sym->arg_name [arg] = str_create_num (1, arg, "arg");
324     else
325     {
326       iter = str_find_set (proto, " ,)");
327       if (!iter)
328       {
329         free ((char*)type_str);
330         return NULL;
331       }
332       sym->arg_name [arg] = str_substring (proto, iter);
333       proto = iter;
334     }
335     sym->arg_text [arg] = (char*)type_str;
336
337   }
338   return proto;
339 }
340
341
342 #ifdef __GNUC__
343 /*******************************************************************
344  *         search_cleanup
345  *
346  * Free memory used while searching (a niceity)
347  */
348 void search_cleanup (void) __attribute__ ((destructor));
349 void search_cleanup (void)
350 {
351   if (grep_buff)
352     free (grep_buff);
353
354   if (fgrep_buff)
355     free (fgrep_buff);
356 }
357 #endif