root/libparser/parser.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. cmp
  2. load_notfunction
  3. isnotfunction
  4. load_plugin_parser
  5. unload_plugin_parser
  6. get_lang_entry
  7. parser_init
  8. parser_exit
  9. parse_file
  10. dbg_print

   1 /*
   2  * Copyright (c) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2010, 2011
   3  *      Tama Communications Corporation
   4  *
   5  * This file is part of GNU GLOBAL.
   6  *
   7  * This program is free software: you can redistribute it and/or modify
   8  * it under the terms of the GNU General Public License as published by
   9  * the Free Software Foundation, either version 3 of the License, or
  10  * (at your option) any later version.
  11  * 
  12  * This program is distributed in the hope that it will be useful,
  13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15  * GNU General Public License for more details.
  16  * 
  17  * You should have received a copy of the GNU General Public License
  18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  19  */
  20 
  21 #ifdef HAVE_CONFIG_H
  22 #include <config.h>
  23 #endif
  24 #include <stdio.h>
  25 #ifdef STDC_HEADERS
  26 #include <stdlib.h>
  27 #endif
  28 #ifdef HAVE_STRING_H
  29 #include <string.h>
  30 #else
  31 #include <strings.h>
  32 #endif
  33 #include <ltdl.h>
  34 
  35 #include "parser.h"
  36 #include "internal.h"
  37 #include "checkalloc.h"
  38 #include "die.h"
  39 #include "langmap.h"
  40 #include "locatestring.h"
  41 #include "queue.h"
  42 #include "strbuf.h"
  43 #include "test.h"
  44 
  45 #define NOTFUNCTION     ".notfunction"
  46 #ifdef __DJGPP__
  47 #define DOS_NOTFUNCTION "_notfunction"
  48 #endif
  49 
  50 struct words {
  51         const char *name;
  52 };
  53 static struct words *words;
  54 static int tablesize;
  55 
  56 static int
  57 cmp(const void *s1, const void *s2)
  58 {
  59         return strcmp(((struct words *)s1)->name, ((struct words *)s2)->name);
  60 }
  61 
  62 static void
  63 load_notfunction(const char *filename)
  64 {
  65         FILE *ip;
  66         STRBUF *sb = strbuf_open(0);
  67         STRBUF *ib = strbuf_open(0);
  68         char *p;
  69         int i;
  70 
  71         if ((ip = fopen(filename, "r")) == NULL)
  72                 die("'%s' cannot read.", filename);
  73         for (tablesize = 0; (p = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL; tablesize++)
  74                 strbuf_puts0(sb, p);
  75         fclose(ip);
  76         words = (struct words *)check_malloc(sizeof(struct words) * tablesize);
  77         /*
  78          * Don't free *p.
  79          */
  80         p = (char *)check_malloc(strbuf_getlen(sb) + 1);
  81         memcpy(p, strbuf_value(sb), strbuf_getlen(sb) + 1);
  82         for (i = 0; i < tablesize; i++) {
  83                 words[i].name = p;
  84                 p += strlen(p) + 1;
  85         }
  86         qsort(words, tablesize, sizeof(struct words), cmp);
  87         strbuf_close(sb);
  88         strbuf_close(ib);
  89 }
  90 
  91 static int
  92 isnotfunction(const char *name)
  93 {
  94         struct words tmp;
  95         struct words *result;
  96 
  97         if (words == NULL)
  98                 return 0;
  99         tmp.name = name;
 100         result = (struct words *)bsearch(&tmp, words, tablesize, sizeof(struct words), cmp);
 101         return (result != NULL) ? 1 : 0;
 102 }
 103 
 104 /*----------------------------------------------------------------------*/
 105 /* Parser switch                                                        */
 106 /*----------------------------------------------------------------------*/
 107 /**
 108  * This is the linkage section of each parsers.
 109  * If you want to support new language, you must define parser procedure
 110  * which requires file name as an argument.
 111  */
 112 struct lang_entry {
 113         const char *lang_name;
 114         void (*parser)(const struct parser_param *);    /**< parser procedure */
 115         const char *parser_name;
 116         const char *lt_dl_name;
 117 };
 118 
 119 struct plugin_entry {
 120         STAILQ_ENTRY(plugin_entry) next;
 121         lt_dlhandle handle;
 122         struct lang_entry entry;
 123 };
 124 
 125 #ifndef IS__DOXYGEN_
 126 static STAILQ_HEAD(plugin_list, plugin_entry)
 127         plugin_list = STAILQ_HEAD_INITIALIZER(plugin_list);
 128 
 129 #else
 130 static struct plugin_list {
 131         struct plugin_entry *stqh_first; /**< first element */
 132         struct plugin_entry **stqh_last; /**< addr of last next element */
 133 } plugin_list = { NULL, &(plugin_list).stqh_first };
 134 #endif
 135 static char *langmap_saved, *pluginspec_saved;
 136 
 137 /**
 138  * load_plugin_parser: Load plug-in parsers.
 139  *
 140  *      @param[in]      pluginspec      described below
 141  *
 142  * @par Syntax:
 143  * @code{.txt}
 144  *   <pluginspec> ::= <map> | <map>","<pluginspec>
 145  *   <map>        ::= <language name>":"<shared object path>
 146  *                  | <language name>":"<shared object path>":"<function name>
 147  * @endcode
 148  */
 149 static void
 150 load_plugin_parser(const char *pluginspec)
 151 {
 152         char *p, *q;
 153         const char *lt_dl_name, *parser_name;
 154         struct plugin_entry *pent;
 155 
 156         pluginspec_saved = check_strdup(pluginspec);
 157         if (lt_dlinit() != 0)
 158                 die("cannot initialize libltdl.");
 159         p = pluginspec_saved;
 160         while (*p != '\0') {
 161                 pent = check_malloc(sizeof(*pent));
 162                 pent->entry.lang_name = p;
 163                 p = strchr(p, ':');
 164                 if (p == NULL)
 165                         die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
 166                 *p++ = '\0';
 167                 if (*p == '\0')
 168                         die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
 169                 lt_dl_name = p;
 170                 p = strchr(p, ',');
 171                 if (p != NULL)
 172                         *p++ = '\0';
 173                 q = strchr(lt_dl_name, ':');
 174                 if (q == NULL) {
 175                         parser_name = "parser";
 176                 } else {
 177                         *q++ = '\0';
 178                         if (*q == '\0')
 179                                 die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
 180                         parser_name = q;
 181                 }
 182                 pent->handle = lt_dlopen(lt_dl_name);
 183                 if (pent->handle == NULL)
 184                         die_with_code(2, "cannot open shared object '%s'.", lt_dl_name);
 185                 pent->entry.lt_dl_name = lt_dl_name;
 186                 pent->entry.parser = lt_dlsym(pent->handle, parser_name);
 187                 if (pent->entry.parser == NULL)
 188                         die_with_code(2, "cannot find symbol '%s' in '%s'.", parser_name, lt_dl_name);
 189                 pent->entry.parser_name = parser_name;
 190                 STAILQ_INSERT_TAIL(&plugin_list, pent, next);
 191                 if (p == NULL)
 192                         break;
 193         }
 194 }
 195 
 196 /**
 197  * unload_plugin_parser: Unload plug-in parsers.
 198  */
 199 static void
 200 unload_plugin_parser(void)
 201 {
 202         struct plugin_entry *pent;
 203 
 204         if (pluginspec_saved == NULL)
 205                 return;
 206         while (!STAILQ_EMPTY(&plugin_list)) {
 207                 pent = STAILQ_FIRST(&plugin_list);
 208                 lt_dlclose(pent->handle);
 209                 STAILQ_REMOVE_HEAD(&plugin_list, next);
 210                 free(pent);
 211         }
 212         lt_dlexit();
 213         free(pluginspec_saved);
 214 }
 215 
 216 /**
 217  * The first entry is default language.
 218  */
 219 static const struct lang_entry lang_switch[] = {
 220         /* lang_name    parser_proc     parser_name     lt_dl_name,     */
 221         /*                              (for debug)     (for debug)     */
 222         {"c",           C,              "C",            "builtin"},     /* DEFAULT */
 223         {"yacc",        yacc,           "yacc",         "builtin"},
 224         {"cpp",         Cpp,            "Cpp",          "builtin"},
 225         {"java",        java,           "java",         "builtin"},
 226         {"php",         php,            "php",          "builtin"},
 227         {"asm",         assembly,       "assembly",     "builtin"}
 228 };
 229 #define DEFAULT_ENTRY &lang_switch[0]
 230 /**
 231  * get language entry.
 232  *
 233  *      @param[in]      lang    language name (@CODE{NULL} means 'not specified'.)
 234  *      @return              language entry
 235  */
 236 static const struct lang_entry *
 237 get_lang_entry(const char *lang)
 238 {
 239         int i, size = sizeof(lang_switch) / sizeof(struct lang_entry);
 240         struct plugin_entry *pent;
 241 
 242         if (lang == NULL)
 243                 die("get_lang_entry: something is wrong.");
 244         /*
 245          * Priority 1: locates in the plugin parser list.
 246          */
 247         STAILQ_FOREACH(pent, &plugin_list, next) {
 248                 if (strcmp(lang, pent->entry.lang_name) == 0)
 249                         return &pent->entry;
 250         }
 251         /*
 252          * Priority 2: locates in the built-in parser list.
 253          */
 254         for (i = 0; i < size; i++)
 255                 if (!strcmp(lang, lang_switch[i].lang_name))
 256                         return &lang_switch[i];
 257         /*
 258          * if specified language not found, it assumes default language, that is C.
 259          */
 260         return DEFAULT_ENTRY;
 261 }
 262 
 263 /**
 264  * @par Usage:
 265  * @code{.txt}
 266  * [gtags.conf]
 267  * +----------------------------
 268  * |...
 269  * |gtags_parser=<pluginspec>
 270  * |langmap=<langmap>
 271  * @endcode
 272  *
 273  * 1. Load langmap and pluginspec, and initialize parsers.
 274  *
 275  *      @par
 276  *      @CODE{parser_init(langmap, plugin_parser);}
 277  *
 278  * 2. Execute parsers
 279  *
 280  *      @par
 281  *      @CODE{#parse_file(...);}
 282  *
 283  * 3. Unload parsers.
 284  *
 285  *      @par
 286  *      @CODE{parser_exit();}
 287  */
 288 /**
 289  * parser_init: load langmap and shared libraries.
 290  *
 291  *      @param[in]      langmap         the value of @CODE{langmap=\<langmap\>}
 292  *      @param[in]      pluginspec      the value of @CODE{gtags_parser=\<pluginspec\>}
 293  */
 294 void
 295 parser_init(const char *langmap, const char *pluginspec)
 296 {
 297         /* setup language mapping. */
 298         if (langmap == NULL)
 299                 langmap = DEFAULTLANGMAP;
 300         setup_langmap(langmap);
 301         langmap_saved = check_strdup(langmap);
 302 
 303         /* load shared objects. */
 304         if (pluginspec != NULL)
 305                 load_plugin_parser(pluginspec);
 306 
 307         /*
 308          * This is a hack for FreeBSD.
 309          * In the near future, it will be removed.
 310          */
 311         if (test("r", NOTFUNCTION))
 312                 load_notfunction(NOTFUNCTION);
 313 #ifdef __DJGPP__
 314         else if (test("r", DOS_NOTFUNCTION))
 315                 load_notfunction(DOS_NOTFUNCTION);
 316 #endif
 317 }
 318 
 319 /**
 320  * parser_exit: unload shared libraries.
 321  */
 322 void
 323 parser_exit(void)
 324 {
 325         unload_plugin_parser();
 326         free(langmap_saved);
 327 }
 328 
 329 /**
 330  * parse_file: select and execute a parser.
 331  *
 332  *      @param[in]      path    path name
 333  *      @param[in]      flags   #PARSER_WARNING: print warning messages
 334  *      @param[in]      put     callback routine <br>
 335  *                      each parser use this routine for output
 336  *      @param[in]      arg     argument for callback routine
 337  */
 338 void
 339 parse_file(const char *path, int flags, PARSER_CALLBACK put, void *arg)
 340 {
 341         const char *lang, *suffix;
 342         const struct lang_entry *ent;
 343         struct parser_param param;
 344 
 345         /* get suffix of the path. */
 346         suffix = locatestring(path, ".", MATCH_LAST);
 347         if (suffix == NULL)
 348                 return;
 349         lang = decide_lang(suffix);
 350         if (lang == NULL)
 351                 return;
 352         /*
 353          * Select parser.
 354          * If lang == NULL then default parser is selected.
 355          */
 356         ent = get_lang_entry(lang);
 357         if (flags & PARSER_DEBUG) {
 358                 fprintf(stderr, "File '%s' is handled as follows:\n", path);
 359                 fprintf(stderr, "\tsuffix:   |%s|\n", suffix);
 360                 fprintf(stderr, "\tlanguage: |%s|\n", lang);
 361                 fprintf(stderr, "\tparser:   |%s|\n", ent->parser_name);
 362                 fprintf(stderr, "\tlibrary:  |%s|\n", ent->lt_dl_name ? ent->lt_dl_name : "builtin library");
 363         }
 364         /*
 365          * call language specific parser.
 366          */
 367         param.size = sizeof(param);
 368         param.flags = flags;
 369         param.file = path;
 370         param.put = put;
 371         param.arg = arg;
 372         param.isnotfunction = isnotfunction;
 373         param.langmap = langmap_saved;
 374         param.die = die;
 375         param.warning = warning;
 376         param.message = message;
 377         ent->parser(&param);
 378 }
 379 
 380 void
 381 dbg_print(int level, const char *s)
 382 {
 383         fprintf(stderr, "[%04d]", lineno);
 384         for (; level > 0; level--)
 385                 fprintf(stderr, "    ");
 386         fprintf(stderr, "%s\n", s);
 387 }

/* [previous][next][first][last][top][bottom][index][help] */