search.c 8.81 KB
Newer Older
1 2 3 4
/*
 *  Prototype search and parsing functions
 *
 *  Copyright 2000 Jon Griffiths
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 22 23

#include "config.h"
#include "wine/port.h"

24
#include "winedump.h"
25 26 27 28

static char *grep_buff = NULL;
static char *fgrep_buff = NULL;

29
static BOOL symbol_from_prototype (parsed_symbol *sym, const char *prototype);
30 31 32 33 34 35 36 37 38
static const char *get_type (parsed_symbol *sym, const char *proto, int arg);


/*******************************************************************
 *         symbol_search
 *
 * Call Patrik Stridvall's 'function_grep.pl' script to retrieve a
 * function prototype from include file(s)
 */
39
BOOL symbol_search (parsed_symbol *sym)
40 41 42 43 44 45 46 47 48 49
{
  static const size_t MAX_RESULT_LEN = 1024;
  FILE *grep;
  int attempt = 0;

  assert (globals.do_code);
  assert (globals.directory);
  assert (sym && sym->symbol);

  if (!symbol_is_valid_c (sym))
50
    return FALSE;
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 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83

  if (!grep_buff || !fgrep_buff)
    fatal ("Out of Memory");

  /* Use 'grep' to tell us which possible files the function is in,
   * then use 'function_grep.pl' to get the prototype. If this fails the
   * first time then give grep a more general query (that doesn't
   * require an opening argument brace on the line with the function name).
   */
  while (attempt < 2)
  {
    FILE *f_grep;
    char *cmd = str_create (4, "grep -d recurse -l \"", sym->symbol,
        !attempt ? "[:blank:]*(\" " : "\" ", globals.directory);

    if (VERBOSE)
      puts (cmd);

    fflush (NULL); /* See 'man popen' */

    if (!(grep = popen (cmd, "r")))
      fatal ("Cannot execute grep -l");
    free (cmd);

    while (fgets (grep_buff, MAX_RESULT_LEN, grep))
    {
      int i;
84 85 86 87 88
      const char *extension = grep_buff;
      for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++) {
	if (grep_buff[i] == '.')
	  extension = &grep_buff[i];
      }
89 90
      grep_buff[i] = '\0';

Francois Gouget's avatar
Francois Gouget committed
91
      /* Definitely not in these: */
92 93 94 95 96 97
      if (strcmp(extension,".dll") == 0 ||
	  strcmp(extension,".lib") == 0 ||
	  strcmp(extension,".so")  == 0 ||
	  strcmp(extension,".o")   == 0)
	continue;

98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
      if (VERBOSE)
        puts (grep_buff);

      cmd = str_create (5, "function_grep.pl ", sym->symbol,
                        " \"", grep_buff, "\"");

      if (VERBOSE)
        puts (cmd);

      fflush (NULL); /* See 'man popen' */

      if (!(f_grep = popen (cmd, "r")))
        fatal ("Cannot execute function_grep.pl");
      free (cmd);

      while (fgets (grep_buff, MAX_RESULT_LEN, f_grep))
      {
        char *iter = grep_buff;

        /* Keep only the first line */
        symbol_clean_string(grep_buff);

        for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++)
          ;
        grep_buff[i] = '\0';

        if (VERBOSE)
          puts (grep_buff);

        while ((iter = strstr (iter, sym->symbol)))
        {
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 139
            {
              pclose (f_grep);
              pclose (grep);
140
              return TRUE;  /* OK */
141 142 143 144 145 146 147 148 149 150 151 152 153 154
            }
            if (VERBOSE)
              puts ("Failed, trying next");
          }
          else
            iter += strlen (sym->symbol);
        }
      }
      pclose (f_grep);
    }
    pclose (grep);
    attempt++;
  }

155
  return FALSE; /* Not found */
156 157 158 159 160 161 162 163
}


/*******************************************************************
 *         symbol_from_prototype
 *
 * Convert a C prototype into a symbol
 */
164
static BOOL symbol_from_prototype (parsed_symbol *sym, const char *proto)
165
{
166
  const char *iter;
167
  BOOL found;
168 169 170

  proto = get_type (sym, proto, -1); /* Get return type */
  if (!proto)
171
    return FALSE;
172

173
  iter = str_match (proto, sym->symbol, &found);
174 175 176

  if (!found)
  {
177
    char *call;
178 179 180
    /* Calling Convention */
    iter = strchr (iter, ' ');
    if (!iter)
181
      return FALSE;
182

183
    call = str_substring (proto, iter);
184

185 186 187 188 189
    if (!strcasecmp (call, "cdecl") || !strcasecmp (call, "__cdecl"))
      sym->flags |= SYM_CDECL;
    else
      sym->flags |= SYM_STDCALL;
    free (call);
190
    iter = str_match (iter, sym->symbol, &found);
191 192

    if (!found)
193
      return FALSE;
194 195 196 197

    if (VERBOSE)
      printf ("Using %s calling convention\n",
              sym->flags & SYM_CDECL ? "cdecl" : "stdcall");
198 199
  }
  else
200
    sym->flags = CALLING_CONVENTION;
201 202 203 204 205 206

  sym->function_name = strdup (sym->symbol);
  proto = iter;

  /* Now should be the arguments */
  if (*proto++ != '(')
207
    return FALSE;
208 209 210 211

  for (; *proto == ' '; proto++);

  if (!strncmp (proto, "void", 4))
212
    return TRUE;
213 214 215 216 217 218

  do
  {
    /* Process next argument */
    str_match (proto, "...", &sym->varargs);
    if (sym->varargs)
219
      return TRUE;
220 221

    if (!(proto = get_type (sym, proto, sym->argc)))
222
      return FALSE;
223 224 225 226 227 228

    sym->argc++;

    if (*proto == ',')
      proto++;
    else if (*proto != ')')
229
      return FALSE;
230 231 232

  } while (*proto != ')');

233
  return TRUE;
234 235 236 237 238 239 240 241 242 243
}


/*******************************************************************
 *         get_type
 *
 * Read a type from a prototype
 */
static const char *get_type (parsed_symbol *sym, const char *proto, int arg)
{
244 245
  BOOL is_const, is_volatile, is_struct, is_signed, is_unsigned;
  int ptrs = 0;
246 247
  const char *iter, *base_type, *catch_unsigned, *proto_str;
  char dest_type, *type_str;
248 249 250 251 252 253

  assert (sym && sym->symbol);
  assert (proto && *proto);
  assert (arg < 0 || (unsigned)arg == sym->argc);


254 255 256
  proto_str = str_match (proto, "const", &is_const);
  proto_str = str_match (proto_str, "volatile", &is_volatile);
  proto_str = str_match (proto_str, "struct", &is_struct);
257
  if (!is_struct)
258
    proto_str = str_match (proto_str, "union", &is_struct);
259

260
  catch_unsigned = proto_str;
261

262 263
  proto_str = str_match (proto_str, "unsigned", &is_unsigned);
  proto_str = str_match (proto_str, "signed", &is_signed);
264 265 266

  /* Can have 'unsigned const' or 'const unsigned' etc */
  if (!is_const)
267
    proto_str = str_match (proto_str, "const", &is_const);
268
  if (!is_volatile)
269
    proto_str = str_match (proto_str, "volatile", &is_volatile);
270

271 272
  base_type = proto_str;
  iter = str_find_set (proto_str, " ,*)");
273 274 275 276 277 278 279 280 281
  if (!iter)
    return NULL;

  if (arg < 0 && (is_signed || is_unsigned))
  {
    /* Prevent calling convention from being swallowed by 'un/signed' alone */
    if (strncmp (base_type, "int", 3) && strncmp (base_type, "long", 4) &&
        strncmp (base_type, "short", 5) && strncmp (base_type, "char", 4))
    {
282
      iter = proto_str;
283
      base_type = catch_unsigned;
284 285
    } else
      catch_unsigned = NULL;
286 287 288 289 290
  }
  else
    catch_unsigned = NULL;

  /* FIXME: skip const/volatile here too */
291 292
  for (proto_str = iter; *proto_str; proto_str++)
    if (*proto_str == '*')
293
      ptrs++;
294
    else if (*proto_str != ' ')
295 296
      break;

297
  if (!*proto_str)
298 299
    return NULL;

300
  type_str = str_substring (proto, proto_str);
301 302 303 304
  if (iter == base_type || catch_unsigned)
  {
    /* 'unsigned' with no type */
    char *tmp = str_create (2, type_str, " int");
305 306
    free (type_str);
    type_str = tmp;
307 308 309 310 311 312 313
  }
  symbol_clean_string (type_str);

  dest_type = symbol_get_type (type_str);

  if (arg < 0)
  {
314
    sym->return_text = (char*)type_str;
315 316 317 318 319 320 321
    sym->return_type = dest_type;
  }
  else
  {
    sym->arg_type [arg] = dest_type;
    sym->arg_flag [arg] = is_const ? CT_CONST : is_volatile ? CT_VOLATILE : 0;

322
    if (*proto_str == ',' || *proto_str == ')')
323 324 325
      sym->arg_name [arg] = str_create_num (1, arg, "arg");
    else
    {
326
      iter = str_find_set (proto_str, " ,)");
327 328
      if (!iter)
      {
329
        free (type_str);
330 331
        return NULL;
      }
332 333
      sym->arg_name [arg] = str_substring (proto_str, iter);
      proto_str = iter;
334
    }
335
    sym->arg_text [arg] = (char*)type_str;
336 337

  }
338
  return proto_str;
339 340 341 342 343 344 345 346 347 348 349 350
}


#ifdef __GNUC__
/*******************************************************************
 *         search_cleanup
 *
 * Free memory used while searching (a niceity)
 */
void search_cleanup (void) __attribute__ ((destructor));
void search_cleanup (void)
{
351 352
  free (grep_buff);
  free (fgrep_buff);
353 354
}
#endif