search.c 8.35 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

#include "config.h"

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

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

28
static BOOL symbol_from_prototype (parsed_symbol *sym, const char *prototype);
29 30 31 32 33 34 35 36 37
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)
 */
38
BOOL symbol_search (parsed_symbol *sym)
39 40 41 42 43 44 45 46 47 48
{
  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))
49
    return FALSE;
50 51

  if (!grep_buff)
52
    grep_buff = xmalloc (MAX_RESULT_LEN);
53 54

  if (!fgrep_buff)
55
    fgrep_buff = xmalloc (MAX_RESULT_LEN);
56 57 58 59 60 61 62 63 64

  /* 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;
65 66
    char *cmd = strmake( "grep -d recurse -l \"%s%s\" %s", sym->symbol,
                         !attempt ? "[:blank:]*(" : "", globals.directory);
67 68 69 70 71 72 73 74 75 76 77 78 79

    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;
80 81 82 83 84
      const char *extension = grep_buff;
      for (i = 0; grep_buff[i] && grep_buff[i] != '\n' ; i++) {
	if (grep_buff[i] == '.')
	  extension = &grep_buff[i];
      }
85 86
      grep_buff[i] = '\0';

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

94 95 96
      if (VERBOSE)
        puts (grep_buff);

97
      cmd = strmake( "function_grep.pl %s \"%s\"", sym->symbol, grep_buff );
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

      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)))
        {
124
          if (iter > grep_buff && (iter[-1] == ' ' || iter[-1] == '*') &&
125
             (iter[strlen (sym->symbol)] == ' ' ||
126
              iter[strlen (sym->symbol)] == '('))
127 128
          {
            if (VERBOSE)
129
              printf ("Prototype '%s' looks OK, processing\n", grep_buff);
130

131
            if (symbol_from_prototype (sym, grep_buff))
132 133 134
            {
              pclose (f_grep);
              pclose (grep);
135
              return TRUE;  /* OK */
136 137 138 139 140 141 142 143 144 145 146 147 148 149
            }
            if (VERBOSE)
              puts ("Failed, trying next");
          }
          else
            iter += strlen (sym->symbol);
        }
      }
      pclose (f_grep);
    }
    pclose (grep);
    attempt++;
  }

150
  return FALSE; /* Not found */
151 152 153 154 155 156 157 158
}


/*******************************************************************
 *         symbol_from_prototype
 *
 * Convert a C prototype into a symbol
 */
159
static BOOL symbol_from_prototype (parsed_symbol *sym, const char *proto)
160
{
161
  const char *iter;
162
  BOOL found;
163 164 165

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

168
  iter = str_match (proto, sym->symbol, &found);
169 170 171

  if (!found)
  {
172
    char *call;
173 174 175
    /* Calling Convention */
    iter = strchr (iter, ' ');
    if (!iter)
176
      return FALSE;
177

178
    call = str_substring (proto, iter);
179

180 181 182 183 184
    if (!strcasecmp (call, "cdecl") || !strcasecmp (call, "__cdecl"))
      sym->flags |= SYM_CDECL;
    else
      sym->flags |= SYM_STDCALL;
    free (call);
185
    iter = str_match (iter, sym->symbol, &found);
186 187

    if (!found)
188
      return FALSE;
189 190 191 192

    if (VERBOSE)
      printf ("Using %s calling convention\n",
              sym->flags & SYM_CDECL ? "cdecl" : "stdcall");
193 194
  }
  else
195
    sym->flags = CALLING_CONVENTION;
196

197
  sym->function_name = xstrdup (sym->symbol);
198 199 200 201
  proto = iter;

  /* Now should be the arguments */
  if (*proto++ != '(')
202
    return FALSE;
203 204 205 206

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

  if (!strncmp (proto, "void", 4))
207
    return TRUE;
208 209 210 211 212 213

  do
  {
    /* Process next argument */
    str_match (proto, "...", &sym->varargs);
    if (sym->varargs)
214
      return TRUE;
215 216

    if (!(proto = get_type (sym, proto, sym->argc)))
217
      return FALSE;
218 219 220 221 222 223

    sym->argc++;

    if (*proto == ',')
      proto++;
    else if (*proto != ')')
224
      return FALSE;
225 226 227

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

228
  return TRUE;
229 230 231 232 233 234 235 236 237 238
}


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

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


249 250 251
  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);
252
  if (!is_struct)
253
    proto_str = str_match (proto_str, "union", &is_struct);
254

255
  catch_unsigned = proto_str;
256

257 258
  proto_str = str_match (proto_str, "unsigned", &is_unsigned);
  proto_str = str_match (proto_str, "signed", &is_signed);
259 260 261

  /* Can have 'unsigned const' or 'const unsigned' etc */
  if (!is_const)
262
    proto_str = str_match (proto_str, "const", &is_const);
263
  if (!is_volatile)
264
    proto_str = str_match (proto_str, "volatile", &is_volatile);
265

266 267
  base_type = proto_str;
  iter = str_find_set (proto_str, " ,*)");
268 269 270 271 272 273 274 275 276
  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))
    {
277
      iter = proto_str;
278
      base_type = catch_unsigned;
279 280
    } else
      catch_unsigned = NULL;
281 282 283 284 285
  }
  else
    catch_unsigned = NULL;

  /* FIXME: skip const/volatile here too */
286 287
  for (proto_str = iter; *proto_str; proto_str++)
    if (*proto_str == '*')
288
      ptrs++;
289
    else if (*proto_str != ' ')
290 291
      break;

292
  if (!*proto_str)
293 294
    return NULL;

295
  type_str = str_substring (proto, proto_str);
296 297 298
  if (iter == base_type || catch_unsigned)
  {
    /* 'unsigned' with no type */
299
    type_str = strmake( "%s int", type_str );
300 301 302 303 304 305 306
  }
  symbol_clean_string (type_str);

  dest_type = symbol_get_type (type_str);

  if (arg < 0)
  {
307
    sym->return_text = type_str;
308 309 310 311 312 313 314
    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;

315
    if (*proto_str == ',' || *proto_str == ')')
316
      sym->arg_name [arg] = strmake( "arg%u", arg );
317 318
    else
    {
319
      iter = str_find_set (proto_str, " ,)");
320 321
      if (!iter)
      {
322
        free (type_str);
323 324
        return NULL;
      }
325 326
      sym->arg_name [arg] = str_substring (proto_str, iter);
      proto_str = iter;
327
    }
328
    sym->arg_text [arg] = type_str;
329 330

  }
331
  return proto_str;
332
}