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