diff --git a/ChangeLog b/ChangeLog index 31d20cdf..3a3c4642 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,12 @@ * src/project.c: Made all project dialogs modal. + * src/main.c, src/symbols.c, src/symbols.h, tagmanager/tm_workspace.c: + Add option --generate-tags (-g) to generate a global tags file from + a list of source files. Currently this is only likely to work + correctly for C-like source files. Run 'geany -g' for syntax info. + Remove short option for hidden option --generate-data-files. + Update tm_workspace_create_global_tags() from Anjuta 1.2.4a. 2007-03-20 Enrico Tröger diff --git a/src/main.c b/src/main.c index ccb4e71f..d7dea70e 100644 --- a/src/main.c +++ b/src/main.c @@ -87,13 +87,15 @@ static gchar *lib_vte = NULL; static gboolean ignore_socket = FALSE; #endif static gboolean generate_datafiles = FALSE; +static gboolean generate_tags = FALSE; static GOptionEntry entries[] = { { "column", 0, 0, G_OPTION_ARG_INT, &cl_options.goto_column, N_("set initial column number for the first opened file (useful in conjunction with --line)"), NULL }, { "config", 'c', 0, G_OPTION_ARG_FILENAME, &alternate_config, N_("use an alternate configuration directory"), NULL }, { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug_mode, N_("runs in debug mode (means being verbose)"), NULL }, - { "generate-data-files", 'g', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &generate_datafiles, "", NULL }, + { "generate-tags", 'g', 0, G_OPTION_ARG_NONE, &generate_tags, N_("generate global tags file (see documentation)"), NULL }, + { "generate-data-files", 0, G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, &generate_datafiles, "", NULL }, #ifdef HAVE_SOCKET { "new-instance", 'i', 0, G_OPTION_ARG_NONE, &ignore_socket, N_("don't open files in a running instance, force opening a new instance"), NULL }, #endif @@ -433,6 +435,12 @@ static void parse_command_line_options(gint *argc, gchar ***argv) exit(0); } + if (generate_tags) + { + gboolean ret = symbols_generate_global_tags(*argc, *argv); + + exit(ret); + } app->debug_mode = debug_mode; #ifdef GEANY_DEBUG diff --git a/src/symbols.c b/src/symbols.c index 4b8ce5b1..cbcb5ce8 100644 --- a/src/symbols.c +++ b/src/symbols.c @@ -32,6 +32,7 @@ #include #include +#include #include "symbols.h" #include "utils.h" @@ -688,3 +689,41 @@ gboolean symbols_recreate_tag_list(gint idx) } +/* Adapted from anjuta-2.0.2/global-tags/tm_global_tags.c, thanks. + * Needs full path and \" quoting characters around filenames. + * Example: + * geany -g tagsfile \"/home/nmt/svn/geany/src/d*.h\" */ +int symbols_generate_global_tags(int argc, char **argv) +{ + /* -E pre-process, -dD output user macros, -p prof info (?), + * -undef remove builtin macros (seems to be needed with FC5 gcc 4.1.1 */ + const char pre_process[] = "gcc -E -dD -p -undef"; + + if (argc > 2) + { + /* Create global taglist */ + int status; + char *command; + command = g_strdup_printf("%s %s", pre_process, + NVL(getenv("CFLAGS"), "")); + //printf(">%s<\n", command); + status = tm_workspace_create_global_tags(command, + (const char **) (argv + 2), + argc - 2, argv[1]); + g_free(command); + if (!status) + return 1; + } + else + { + fprintf(stderr, "Usage: %s -g \n\n", argv[0]); + fprintf(stderr, "Each file in must be enclosed in double quotes.\n"); + fprintf(stderr, "Example:\n" + "CFLAGS=`pkg-config gtk+-2.0 --cflags` %s -g gtk2.c.tags" + " \\\"/usr/include/gtk-2.0/gtk/gtk.h\\\"\n", argv[0]); + return 1; + } + return 0; +} + + diff --git a/src/symbols.h b/src/symbols.h index 9282f668..7951daf6 100644 --- a/src/symbols.h +++ b/src/symbols.h @@ -48,4 +48,6 @@ void symbols_finalize(); gboolean symbols_recreate_tag_list(gint idx); +int symbols_generate_global_tags(int argc, char **argv); + #endif diff --git a/tagmanager/tm_workspace.c b/tagmanager/tm_workspace.c index 68fc5628..2ee417b3 100644 --- a/tagmanager/tm_workspace.c +++ b/tagmanager/tm_workspace.c @@ -33,6 +33,7 @@ #include "tm_workspace.h" #include "tm_project.h" + static TMWorkspace *theWorkspace = NULL; guint workspace_class_id = 0; @@ -156,6 +157,31 @@ gboolean tm_workspace_load_global_tags(const char *tags_file, gint mode) return TRUE; } +static guint tm_file_inode_hash(gconstpointer key) +{ + struct stat file_stat; + const char *filename = (const char*)key; + if (stat(filename, &file_stat) == 0) + { +#ifdef TM_DEBUG + g_message ("Hash for '%s' is '%d'\n", filename, file_stat.st_ino); +#endif + return g_direct_hash (GUINT_TO_POINTER (file_stat.st_ino)); + } else { + return 0; + } +} + +static void tm_move_entries_to_g_list(gpointer key, gpointer value, gpointer user_data) +{ + GList **pp_list = (GList**)user_data; + + if (user_data == NULL) + return; + + *pp_list = g_list_prepend(*pp_list, value); +} + gboolean tm_workspace_create_global_tags(const char *pre_process, const char **includes , int includes_count, const char *tags_file) { @@ -169,11 +195,9 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i FILE *fp; TMWorkObject *source_file; GPtrArray *tags_array; + GHashTable *includes_files_hash; GList *includes_files = NULL; - guint list_len; - guint idx_main; - guint idx_sub; - int remove_count = 0; + GList *node; #ifdef G_OS_WIN32 char *temp_file = g_strdup_printf("%s_%d_%ld_1.cpp", P_tmpdir, getpid(), time(NULL)); char *temp_file2 = g_strdup_printf("%s_%d_%ld_2.cpp", P_tmpdir, getpid(), time(NULL)); @@ -181,11 +205,18 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i char *temp_file = g_strdup_printf("%s/%d_%ld_1.cpp", P_tmpdir, getpid(), time(NULL)); char *temp_file2 = g_strdup_printf("%s/%d_%ld_2.cpp", P_tmpdir, getpid(), time(NULL)); #endif - TMTagAttrType sort_attrs[] = { tm_tag_attr_name_t, tm_tag_attr_scope_t - , tm_tag_attr_type_t, 0}; + TMTagAttrType sort_attrs[] = { + tm_tag_attr_name_t, tm_tag_attr_scope_t, + tm_tag_attr_type_t, 0 + }; + if (NULL == (fp = fopen(temp_file, "w"))) return FALSE; + includes_files_hash = g_hash_table_new_full (tm_file_inode_hash, + g_direct_equal, + NULL, g_free); + #ifdef HAVE_GLOB_H globbuf.gl_offs = 0; for(idx_inc = 0; idx_inc < includes_count; idx_inc++) @@ -195,91 +226,87 @@ gboolean tm_workspace_create_global_tags(const char *pre_process, const char **i strncpy(clean_path, includes[idx_inc] + 1, dirty_len - 1); clean_path[dirty_len - 2] = 0; - //printf("[o][%s]\n", clean_path); +#ifdef TM_DEBUG + g_message ("[o][%s]\n", clean_path); +#endif glob(clean_path, 0, NULL, &globbuf); - //printf("matches: %d\n", globbuf.gl_pathc); + +#ifdef TM_DEBUG + g_message ("matches: %d\n", globbuf.gl_pathc); +#endif + for(idx_glob = 0; idx_glob < globbuf.gl_pathc; idx_glob++) { - includes_files = g_list_append(includes_files, strdup(globbuf.gl_pathv[idx_glob])); - //printf(">>> %s\n", globbuf.gl_pathv[idx_glob]); +#ifdef TM_DEBUG + g_message (">>> %s\n", globbuf.gl_pathv[idx_glob]); +#endif + if (!g_hash_table_lookup(includes_files_hash, + globbuf.gl_pathv[idx_glob])) + { + char* file_name_copy = strdup(globbuf.gl_pathv[idx_glob]); + g_hash_table_insert(includes_files_hash, file_name_copy, + file_name_copy); +#ifdef TM_DEBUG + g_message ("Added ...\n"); +#endif + } } globfree(&globbuf); free(clean_path); } -#else +#else // no glob support for(idx_inc = 0; idx_inc < includes_count; idx_inc++) { - includes_files = g_list_append(includes_files, strdup(includes[idx_inc])); + if (!g_hash_table_lookup(includes_files_hash, + includes[idx_inc])) + { + char* file_name_copy = strdup(includes[idx_inc]); + g_hash_table_insert(includes_files_hash, file_name_copy, + file_name_copy); + } } #endif /* Checks for duplicate file entries which would case trouble */ + g_hash_table_foreach(includes_files_hash, tm_move_entries_to_g_list, + &includes_files); + + includes_files = g_list_reverse (includes_files); + +#ifdef TM_DEBUG + g_message ("writing out files to %s\n", temp_file); +#endif + node = includes_files; + while (node) { - struct stat main_stat; - struct stat sub_stat; - - remove_count = 0; - - list_len = g_list_length(includes_files); - - /* We look for files with the same inode */ - for(idx_main = 0; idx_main < list_len; idx_main++) - { -// printf("%d / %d\n", idx_main, list_len - 1); - stat(g_list_nth_data(includes_files, idx_main), &main_stat); - for(idx_sub = idx_main + 1; idx_sub < list_len; idx_sub++) - { - GList *element = NULL; - - stat(g_list_nth_data(includes_files, idx_sub), &sub_stat); - - - if(main_stat.st_ino != sub_stat.st_ino) - continue; - - /* Inodes match */ - - element = g_list_nth(includes_files, idx_sub); - -/* printf("%s == %s\n", g_list_nth_data(includes_files, idx_main), - g_list_nth_data(includes_files, idx_sub)); */ - - /* We delete the duplicate entry from the list */ - includes_files = g_list_remove_link(includes_files, element); - remove_count++; - - /* Don't forget to free the mallocs (we duplicated every string earlier!) */ - free(element->data); - - idx_sub--; /* Cause the inner loop not to move; good since we removed - an element at the current position; we don't have to worry - about the outer loop because the inner loop always starts - after the outer loop's index */ - - list_len = g_list_length(includes_files); - } - } - } - - - printf("writing out files to %s\n", temp_file); - for(idx_main = 0; idx_main < g_list_length(includes_files); idx_main++) - { - char *str = g_strdup_printf("#include \"%s\"\n", - (char*)g_list_nth_data(includes_files, - idx_main)); + char *str = g_strdup_printf("#include \"%s\"\n", (char*)node->data); int str_len = strlen(str); fwrite(str, str_len, 1, fp); - free(str); - free(g_list_nth(includes_files, idx_main) -> data); + node = g_list_next (node); } + g_list_free (includes_files); + g_hash_table_destroy(includes_files_hash); + includes_files_hash = NULL; + includes_files = NULL; fclose(fp); - command = g_strdup_printf("%s %s >%s", pre_process, temp_file, temp_file2); + /* FIXME: The following grep command it be remove the lines + * G_BEGIN_DECLS and G_END_DECLS from the header files. The reason is + * that in tagmanager, the files are not correctly parsed and the typedefs + * following these lines are incorrectly parsed. The real fix should, + * of course be in tagmanager (c) parser. This is just a temporary fix. + */ + command = g_strdup_printf("%s %s | grep -v -E '^\\s*(G_BEGIN_DECLS|G_END_DECLS)\\s*$' > %s", + pre_process, temp_file, temp_file2); + +#ifdef TM_DEBUG + g_message("Executing: %s", command); +#endif + system(command); g_free(command); unlink(temp_file);