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