Replaced tempnam by mkstemps.
[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       for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
85         ;
86       grep_buff[i] = '\0';
87
88       if (VERBOSE)
89         puts (grep_buff);
90
91       cmd = str_create (5, "function_grep.pl ", sym->symbol,
92                         " \"", grep_buff, "\"");
93
94       if (VERBOSE)
95         puts (cmd);
96
97       fflush (NULL); /* See 'man popen' */
98
99       if (!(f_grep = popen (cmd, "r")))
100         fatal ("Cannot execute function_grep.pl");
101       free (cmd);
102
103       while (fgets (grep_buff, MAX_RESULT_LEN, f_grep))
104       {
105         char *iter = grep_buff;
106
107         /* Keep only the first line */
108         symbol_clean_string(grep_buff);
109
110         for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
111           ;
112         grep_buff[i] = '\0';
113
114         if (VERBOSE)
115           puts (grep_buff);
116
117         while ((iter = strstr (iter, sym->symbol)))
118         {
119           if (iter > grep_buff && iter[-1] == ' ' &&
120              (iter[strlen (sym->symbol)] == ' ' ||
121              iter[strlen (sym->symbol)] == '('))
122           {
123             if (VERBOSE)
124               printf ("Prototype '%s' looks OK, processing\n", grep_buff);
125
126             if (!symbol_from_prototype (sym, grep_buff))
127             {
128               pclose (f_grep);
129               pclose (grep);
130               return 0;  /* OK */
131             }
132             if (VERBOSE)
133               puts ("Failed, trying next");
134           }
135           else
136             iter += strlen (sym->symbol);
137         }
138       }
139       pclose (f_grep);
140     }
141     pclose (grep);
142     attempt++;
143   }
144
145   return -1; /* Not found */
146 }
147
148
149 /*******************************************************************
150  *         symbol_from_prototype
151  *
152  * Convert a C prototype into a symbol
153  */
154 static int symbol_from_prototype (parsed_symbol *sym, const char *proto)
155 {
156   char *iter;
157   int found;
158
159   proto = get_type (sym, proto, -1); /* Get return type */
160   if (!proto)
161     return -1;
162
163   iter = (char *)str_match (proto, sym->symbol, &found);
164
165   if (!found)
166   {
167     char *call;
168     /* Calling Convention */
169     iter = strchr (iter, ' ');
170     if (!iter)
171       return -1;
172
173     call = str_substring (proto, iter);
174
175     if (!strcasecmp (call, "cdecl") || !strcasecmp (call, "__cdecl"))
176       sym->flags |= SYM_CDECL;
177     else
178       sym->flags |= SYM_STDCALL;
179     free (call);
180     iter = (char *)str_match (iter, sym->symbol, &found);
181
182     if (!found)
183       return -1;
184
185     if (VERBOSE)
186       printf ("Using %s calling convention\n",
187               sym->flags & SYM_CDECL ? "cdecl" : "stdcall");
188   }
189   else
190     sym->flags = CALLING_CONVENTION;
191
192   sym->function_name = strdup (sym->symbol);
193   proto = iter;
194
195   /* Now should be the arguments */
196   if (*proto++ != '(')
197     return -1;
198
199   for (; *proto == ' '; proto++);
200
201   if (!strncmp (proto, "void", 4))
202     return 0;
203
204   do
205   {
206     /* Process next argument */
207     str_match (proto, "...", &sym->varargs);
208     if (sym->varargs)
209       return 0;
210
211     if (!(proto = get_type (sym, proto, sym->argc)))
212       return -1;
213
214     sym->argc++;
215
216     if (*proto == ',')
217       proto++;
218     else if (*proto != ')')
219       return -1;
220
221   } while (*proto != ')');
222
223   return 0;
224 }
225
226
227 /*******************************************************************
228  *         get_type
229  *
230  * Read a type from a prototype
231  */
232 static const char *get_type (parsed_symbol *sym, const char *proto, int arg)
233 {
234   int is_const, is_volatile, is_struct, is_signed, is_unsigned, ptrs = 0;
235   char *iter, *type_str, *base_type, *catch_unsigned, dest_type;
236
237   assert (sym && sym->symbol);
238   assert (proto && *proto);
239   assert (arg < 0 || (unsigned)arg == sym->argc);
240
241   type_str = (char *)proto;
242
243   proto = str_match (proto, "const", &is_const);
244   proto = str_match (proto, "volatile", &is_volatile);
245   proto = str_match (proto, "struct", &is_struct);
246   if (!is_struct)
247     proto = str_match (proto, "union", &is_struct);
248
249   catch_unsigned = (char *)proto;
250
251   proto = str_match (proto, "unsigned", &is_unsigned);
252   proto = str_match (proto, "signed", &is_signed);
253
254   /* Can have 'unsigned const' or 'const unsigned' etc */
255   if (!is_const)
256     proto = str_match (proto, "const", &is_const);
257   if (!is_volatile)
258     proto = str_match (proto, "volatile", &is_volatile);
259
260   base_type = (char *)proto;
261   iter = (char *)str_find_set (proto, " ,*)");
262   if (!iter)
263     return NULL;
264
265   if (arg < 0 && (is_signed || is_unsigned))
266   {
267     /* Prevent calling convention from being swallowed by 'un/signed' alone */
268     if (strncmp (base_type, "int", 3) && strncmp (base_type, "long", 4) &&
269         strncmp (base_type, "short", 5) && strncmp (base_type, "char", 4))
270     {
271       iter = (char *)proto;
272       base_type = catch_unsigned;
273     }
274   }
275   else
276     catch_unsigned = NULL;
277
278   /* FIXME: skip const/volatile here too */
279   for (proto = iter; *proto; proto++)
280     if (*proto == '*')
281       ptrs++;
282     else if (*proto != ' ')
283       break;
284
285   if (!*proto)
286     return NULL;
287
288   type_str = str_substring (type_str, proto);
289   if (iter == base_type || catch_unsigned)
290   {
291     /* 'unsigned' with no type */
292     char *tmp = str_create (2, type_str, " int");
293     free (type_str);
294     type_str = tmp;
295   }
296   symbol_clean_string (type_str);
297
298   dest_type = symbol_get_type (type_str);
299
300   if (arg < 0)
301   {
302     sym->return_text = type_str;
303     sym->return_type = dest_type;
304   }
305   else
306   {
307     sym->arg_type [arg] = dest_type;
308     sym->arg_flag [arg] = is_const ? CT_CONST : is_volatile ? CT_VOLATILE : 0;
309
310     if (*proto == ',' || *proto == ')')
311       sym->arg_name [arg] = str_create_num (1, arg, "arg");
312     else
313     {
314       iter = (char *)str_find_set (proto, " ,)");
315       if (!iter)
316       {
317         free (type_str);
318         return NULL;
319       }
320       sym->arg_name [arg] = str_substring (proto, iter);
321       proto = iter;
322     }
323     sym->arg_text [arg] = type_str;
324
325   }
326   return proto;
327 }
328
329
330 #ifdef __GNUC__
331 /*******************************************************************
332  *         search_cleanup
333  *
334  * Free memory used while searching (a niceity)
335  */
336 void search_cleanup (void) __attribute__ ((destructor));
337 void search_cleanup (void)
338 {
339   if (grep_buff)
340     free (grep_buff);
341
342   if (fgrep_buff)
343     free (fgrep_buff);
344 }
345 #endif
346