1 /*
   2  * Sparse Ctags
   3  *
   4  * Ctags generates tags from preprocessing results.
   5  *
   6  * Copyright (C) 2006 Christopher Li
   7  *
   8  * Permission is hereby granted, free of charge, to any person obtaining a copy
   9  * of this software and associated documentation files (the "Software"), to deal
  10  * in the Software without restriction, including without limitation the rights
  11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  12  * copies of the Software, and to permit persons to whom the Software is
  13  * furnished to do so, subject to the following conditions:
  14  *
  15  * The above copyright notice and this permission notice shall be included in
  16  * all copies or substantial portions of the Software.
  17  *
  18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  24  * THE SOFTWARE.
  25  */
  26 #include <stdlib.h>
  27 #include <stdio.h>
  28 #include <string.h>
  29 #include <unistd.h>
  30 #include <fcntl.h>
  31 
  32 #include "parse.h"
  33 #include "scope.h"
  34 
  35 static struct symbol_list *taglist = NULL;
  36 
  37 static void examine_symbol(struct symbol *sym);
  38 
  39 #define MAX(_x,_y) ((_x) > (_y) ? (_x) : (_y))
  40 
  41 static int cmp_sym(const void *m, const void *n)
  42 {
  43         const struct ident *a = ((const struct symbol *)m)->ident;
  44         const struct ident *b = ((const struct symbol *)n)->ident;
  45         int ret = strncmp(a->name, b->name, MAX(a->len, b->len));
  46         if (!ret) {
  47                 const struct position a_pos = ((const struct symbol *)m)->pos;
  48                 const struct position b_pos = ((const struct symbol *)n)->pos;
  49 
  50                 ret = strcmp(stream_name(a_pos.stream),
  51                              stream_name(b_pos.stream));
  52                 if (!ret)
  53                         return a_pos.line < b_pos.line;
  54         }
  55         return ret;
  56 }
  57 
  58 static void show_tag_header(FILE *fp)
  59 {
  60         fprintf(fp, "!_TAG_FILE_FORMAT\t2\t/extended format; --format=1 will not append ;\" to lines/\n");
  61         fprintf(fp, "!_TAG_FILE_SORTED\t0\t/0=unsorted, 1=sorted, 2=foldcase/\n");
  62         fprintf(fp, "!_TAG_PROGRAM_AUTHOR\tChristopher Li\t/sparse@chrisli.org/\n");
  63         fprintf(fp, "!_TAG_PROGRAM_NAME\tSparse Ctags\t//\n");
  64         fprintf(fp, "!_TAG_PROGRAM_URL\thttp://www.kernel.org/pub/software/devel/sparse/\t/official site/\n");
  65         fprintf(fp, "!_TAG_PROGRAM_VERSION\t0.01\t//\n");
  66 }
  67 
  68 static inline void show_symbol_tag(FILE *fp, struct symbol *sym)
  69 {
  70         fprintf(fp, "%s\t%s\t%d;\"\t%c\tfile:\n", show_ident(sym->ident),
  71                stream_name(sym->pos.stream), sym->pos.line, (int)sym->kind);
  72 }
  73 
  74 static void show_tags(struct symbol_list *list)
  75 {
  76         struct symbol *sym;
  77         struct ident *ident = NULL;
  78         struct position pos = {};
  79         static const char *filename;
  80         FILE *fp;
  81 
  82         if (!list)
  83                 return;
  84 
  85         fp = fopen("tags", "w");
  86         if (!fp) {
  87                 perror("open tags file");
  88                 return;
  89         }
  90         show_tag_header(fp);
  91         FOR_EACH_PTR(list, sym) {
  92                 if (ident == sym->ident && pos.line == sym->pos.line &&
  93                     !strcmp(filename, stream_name(sym->pos.stream)))
  94                         continue;
  95 
  96                 show_symbol_tag(fp, sym);
  97                 ident = sym->ident;
  98                 pos = sym->pos;
  99                 filename = stream_name(sym->pos.stream);
 100         } END_FOR_EACH_PTR(sym);
 101         fclose(fp);
 102 }
 103 
 104 static inline void add_tag(struct symbol *sym)
 105 {
 106         if (sym->ident && !sym->visited) {
 107                 sym->visited = 1;
 108                 add_symbol(&taglist, sym);
 109         }
 110 }
 111 
 112 static inline void examine_members(struct symbol_list *list)
 113 {
 114         struct symbol *sym;
 115 
 116         FOR_EACH_PTR(list, sym) {
 117                 sym->kind = 'm';
 118                 examine_symbol(sym);
 119         } END_FOR_EACH_PTR(sym);
 120 }
 121 
 122 static void examine_symbol(struct symbol *sym)
 123 {
 124         struct symbol *base = sym;
 125 
 126         if (!sym || sym->visited)
 127                 return;
 128         if (sym->ident && sym->ident->reserved)
 129                 return;
 130         if (sym->type == SYM_KEYWORD || sym->type == SYM_PREPROCESSOR)
 131                 return;
 132 
 133         add_tag(sym);
 134         base = sym->ctype.base_type;
 135 
 136         switch (sym->type) {
 137         case SYM_NODE:
 138                 if (base && base->type == SYM_FN)
 139                         sym->kind = 'f';
 140                 examine_symbol(base);
 141                 break;
 142         case SYM_STRUCT:
 143                 sym->kind = 's';
 144                 examine_members(sym->symbol_list);
 145                 break;
 146         case SYM_UNION:
 147                 sym->kind = 'u';
 148                 examine_members(sym->symbol_list);
 149                 break;
 150         case SYM_ENUM:
 151                 sym->kind = 'e';
 152         case SYM_PTR:
 153         case SYM_TYPEOF:
 154         case SYM_BITFIELD:
 155         case SYM_FN:
 156         case SYM_ARRAY:
 157                 examine_symbol(sym->ctype.base_type);
 158                 break;
 159         case SYM_BASETYPE:
 160                 break;
 161 
 162         default:
 163                 die("unknown symbol %s namespace:%d type:%d\n", show_ident(sym->ident),
 164                     sym->namespace, sym->type);
 165         }
 166         if (!sym->kind)
 167                 sym->kind = 'v';
 168         return;
 169 }
 170 
 171 static void examine_namespace(struct symbol *sym)
 172 {
 173         if (sym->visited)
 174                 return;
 175         if (sym->ident && sym->ident->reserved)
 176                 return;
 177 
 178         switch(sym->namespace) {
 179         case NS_KEYWORD:
 180         case NS_PREPROCESSOR:
 181                 return;
 182         case NS_LABEL:
 183                 sym->kind = 'l';
 184                 break;
 185         case NS_MACRO:
 186         case NS_UNDEF:
 187                 sym->kind = 'd';
 188                 break;
 189         case NS_TYPEDEF:
 190                 sym->kind = 't';
 191         case NS_SYMBOL:
 192         case NS_STRUCT:
 193                 examine_symbol(sym);
 194                 break;
 195         default:
 196                 die("unknown namespace %d symbol:%s type:%d\n", sym->namespace,
 197                     show_ident(sym->ident), sym->type);
 198         }
 199         add_tag(sym);
 200 }
 201 
 202 static inline void examine_symbol_list(struct symbol_list *list)
 203 {
 204         struct symbol *sym;
 205 
 206         if (!list)
 207                 return;
 208         FOR_EACH_PTR(list, sym) {
 209                 examine_namespace(sym);
 210         } END_FOR_EACH_PTR(sym);
 211 }
 212 
 213 int main(int argc, char **argv)
 214 {
 215         struct string_list *filelist = NULL;
 216         char *file;
 217 
 218         examine_symbol_list(sparse_initialize(argc, argv, &filelist));
 219         FOR_EACH_PTR(filelist, file) {
 220                 sparse(file);
 221                 examine_symbol_list(file_scope->symbols);
 222         } END_FOR_EACH_PTR(file);
 223         examine_symbol_list(global_scope->symbols);
 224         sort_list((struct ptr_list **)&taglist, cmp_sym);
 225         show_tags(taglist);
 226         return 0;
 227 }