Branch: refs/heads/master
Author: Enrico Tröger <enrico.troeger(a)uvena.de>
Committer: Enrico Tröger <enrico.troeger(a)uvena.de>
Date: Sun, 19 Oct 2014 13:54:39 UTC
Commit: 41a83df7f55e723d68abb3576f1b09ee3bf96282
https://github.com/geany/geany-plugins/commit/41a83df7f55e723d68abb3576f1b0…
Log Message:
-----------
After configure, list also plugins skipped due to missing dependencies
Modified Paths:
--------------
wscript
Modified: wscript
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -126,9 +126,12 @@ def configure(conf):
if revision is not None:
conf.msg('Compiling Git revision', revision)
conf.msg('Plugins to compile', ' '.join(enabled_plugins))
+ plugins_with_missing_dependencies =conf.env['plugins_with_missing_dependencies']
+ conf.msg('Plugins to skip due to missing dependencies', ' '.join(plugins_with_missing_dependencies))
def configure_plugins(conf, enabled_plugins):
+ conf.env['plugins_with_missing_dependencies'] = []
# we need to iterate over the plugin directories ourselves to be able
# to catch plugin ConfigurationError's and remove the plugin in this case
plugins = list(enabled_plugins)
@@ -139,6 +142,7 @@ def configure_plugins(conf, enabled_plugins):
conf.recurse(plugin, mandatory=False)
except ConfigurationError:
enabled_plugins.remove(plugin)
+ conf.env['plugins_with_missing_dependencies'].append(plugin)
def setup_configuration_env(conf):
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Frank Lanitz <frank(a)frank.uvena.de>
Committer: Frank Lanitz <frank(a)frank.uvena.de>
Date: Sun, 19 Oct 2014 08:10:37 UTC
Commit: 1a2f8c4d658e2471d02983295861f2824eb4ae1c
https://github.com/geany/geany-plugins/commit/1a2f8c4d658e2471d02983295861f…
Log Message:
-----------
Merge pull request #161 from techee/geanyctags
Add the GeanyCtags plugin
Modified Paths:
--------------
MAINTAINERS
Makefile.am
build/geanyctags.m4
configure.ac
geanyctags/AUTHORS
geanyctags/COPYING
geanyctags/ChangeLog
geanyctags/Makefile.am
geanyctags/NEWS
geanyctags/README
geanyctags/src/Makefile.am
geanyctags/src/geanyctags.c
geanyctags/src/readtags.c
geanyctags/src/readtags.h
geanyctags/wscript_build
geanyctags/wscript_configure
po/POTFILES.in
Modified: MAINTAINERS
6 lines changed, 6 insertions(+), 0 deletions(-)
===================================================================
@@ -60,6 +60,12 @@ M: Matthew Brush <matt(a)geany.org>
W: http://plugins.geany.org/devhelp.html
S: Maintained
+geanyctags
+P: Jiří Techet <techet(a)gmail.com>
+M: Jiří Techet <techet(a)gmail.com>
+W: http://plugins.geany.org/geanyctags.html
+S: Maintained
+
geanydoc
P: Yura Siamashka <yurand2(a)gmail.com>
M: Yura Siamashka <yurand2(a)gmail.com>
Modified: Makefile.am
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -35,6 +35,10 @@ if ENABLE_DEVHELP
SUBDIRS += devhelp
endif
+if ENABLE_GEANYCTAGS
+SUBDIRS += geanyctags
+endif
+
if ENABLE_GEANYDOC
SUBDIRS += geanydoc
endif
Modified: build/geanyctags.m4
9 lines changed, 9 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,9 @@
+AC_DEFUN([GP_CHECK_GEANYCTAGS],
+[
+ GP_ARG_DISABLE([GeanyCtags], [auto])
+ GP_COMMIT_PLUGIN_STATUS([GeanyCtags])
+ AC_CONFIG_FILES([
+ geanyctags/Makefile
+ geanyctags/src/Makefile
+ ])
+])
Modified: configure.ac
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -34,6 +34,7 @@ GP_CHECK_COMMANDER
GP_CHECK_DEBUGGER
GP_CHECK_DEFINEFORMAT
GP_CHECK_DEVHELP
+GP_CHECK_GEANYCTAGS
GP_CHECK_GEANYDOC
GP_CHECK_GEANYEXTRASEL
GP_CHECK_GEANYGENDOC
Modified: geanyctags/AUTHORS
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1 @@
+Jiří Techet <techet(a)gmail.com>
Modified: geanyctags/COPYING
340 lines changed, 340 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program 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 General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
Modified: geanyctags/ChangeLog
0 lines changed, 0 insertions(+), 0 deletions(-)
===================================================================
No diff available, check online
Modified: geanyctags/Makefile.am
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,4 @@
+include $(top_srcdir)/build/vars.auxfiles.mk
+
+SUBDIRS = src
+plugin = geanyctags
Modified: geanyctags/NEWS
0 lines changed, 0 insertions(+), 0 deletions(-)
===================================================================
No diff available, check online
Modified: geanyctags/README
115 lines changed, 115 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,115 @@
+==========
+GeanyCtags
+==========
+
+.. contents::
+
+About
+=====
+
+GeanyCtags adds a simple support for generating and querying ctags files for a Geany
+project. It requires that the ctags command is installed in a system path. On
+unix systems, distributions usually provide the ctags package; on Windows, the
+ctags binary can be found in the zip Windows distribution from the ctags home
+page (http://ctags.sourceforge.net).
+
+Even though Geany supports symbol definition searching by itself within the open files
+(and with a plugin support within the whole project), tag regeneration can become
+too slow for really big projects. This is why this plugin was created. It makes
+it possible to generate the tag file only once and just query it when searching
+for a particular symbol definition/declaration. This approach is fine for big projects
+where most of the codebase remains unchanged and the tag positions remain more
+or less static.
+
+Usage
+=====
+
+GeanyCtags only works for Geany projects so in order to use it, a Geany project
+has to be open. The "File patterns" entry under Project->Properties determines
+for which files the tag list will be generated. All files within the project base
+path satisfying the file patterns will be included for the tag generation.
+
+Tag Generation
+--------------
+
+To generate the list of tags, make sure that
+
+* a project is open, and
+* "File patterns" are specified correctly under Project->Properties
+
+Then, select Project->Generate tags. After this, a file whose name corresponds to
+the project name with the suffix ".tags" is created in the base path of the project.
+
+Tag Querying
+------------
+
+There are two ways to find a symbol's definition/declaration:
+
+* using the context menu by placing a cursor at the searched symbol, right-clicking
+ and selecting one of the two top items "Find Tag Definition" or "Find Tag Declaration".
+* using Project->Find tag
+
+If the symbol is found, the editor is opened at the tag line. In addition, all the
+found tags are written into the Messages window. This is useful when there are more
+tags of the same name - by clicking a line in the Messages window it is possible
+to jump to any of the found tags.
+
+When using the Project->Find tag search, it is possible to select from several
+search types:
+
+* prefix (default) - finds all tags with the specified prefix
+* full - finds all tags matching the name exactly
+* pattern - finds all tags matching the provided glob pattern
+
+Note that the pattern option is the slowest of the three possibilities because it
+has to go through the tag list sequentially instead of using binary search used
+by the other two methods. By default, tag definitions are searched; to search tag
+declarations, select the Declaration option.
+
+Known issues
+============
+
+* Right now it's not possible to just right-click the searched symbol in the editor
+ and search for definition/declaration - first, a cursor has to be placed at
+ the symbol. This is because at the moment the necessary functionality to do this
+ is private in Geany and not exported to plugins so it cannot be implemented.
+ I will try to get the necessary functions public in Geany to make it possible
+ in the future.
+* Under Windows, GeanyCtags generates tags for all the files within the project base
+ path instead of just the files defined by the patterns. This happens because
+ of the missing (or at least not normally installed) find command used under
+ unix systems to restrict the file list to those matching the patterns. It
+ would be possible to perform the filtering manually but frankly I'm rather lazy
+ to do so (and don't have Windows installed to test). Patches are welcome.
+
+License
+=======
+
+GeanyCtags is distributed under the terms of the GNU General Public License
+as published by the Free Software Foundation; either version 2 of the
+License, or (at your option) any later version. A copy of this license
+can be found in the file COPYING included with the source code of this
+program.
+
+Downloads
+=========
+
+GeanyCtags is part of the combined Geany Plugins release.
+For more information and downloads, please visit
+http://plugins.geany.org/geany-plugins/
+
+Development Code
+================
+
+Get the code from::
+
+ git clone https://github.com/geany/geany-plugins.git
+
+Ideas, questions, patches and bug reports
+=========================================
+
+If you add something, or fix a bug, please send a patch (in 'diff -u'
+format) to the geany mailing list or to one of the authors listed bellow.
+
+2010-2014 by Jiří Techet
+techet(at)gmail(dot)com
Modified: geanyctags/src/Makefile.am
12 lines changed, 12 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,12 @@
+include $(top_srcdir)/build/vars.build.mk
+
+geanyplugins_LTLIBRARIES = geanyctags.la
+
+geanyctags_la_SOURCES = \
+ geanyctags.c \
+ readtags.h \
+ readtags.c
+
+geanyctags_la_LIBADD = $(COMMONLIBS)
+
+include $(top_srcdir)/build/cppcheck.mk
Modified: geanyctags/src/geanyctags.c
642 lines changed, 642 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,642 @@
+/*
+ * Copyright 2010-2014 Jiri Techet <techet(a)gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <sys/time.h>
+#include <string.h>
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <geanyplugin.h>
+
+#include "readtags.h"
+
+#include <errno.h>
+#include <glib/gstdio.h>
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+
+PLUGIN_VERSION_CHECK(211);
+PLUGIN_SET_INFO("GeanyCtags",
+ _("Ctags generation and search plugin for geany projects"),
+ VERSION,
+ "Jiri Techet <techet(a)gmail.com>");
+
+GeanyPlugin *geany_plugin;
+GeanyData *geany_data;
+GeanyFunctions *geany_functions;
+
+
+static GtkWidget *s_context_fdec_item, *s_context_fdef_item, *s_context_sep_item,
+ *s_gt_item, *s_sep_item, *s_ft_item;
+
+static struct
+{
+ GtkWidget *widget;
+
+ GtkWidget *combo;
+ GtkWidget *combo_match;
+ GtkWidget *case_sensitive;
+ GtkWidget *declaration;
+} s_ft_dialog = {NULL, NULL, NULL, NULL};
+
+
+enum
+{
+ KB_FIND_TAG,
+ KB_GENERATE_TAGS,
+ KB_COUNT
+};
+
+
+
+void plugin_init(G_GNUC_UNUSED GeanyData * data);
+void plugin_cleanup(void);
+
+
+static void set_widgets_sensitive(gboolean sensitive)
+{
+ gtk_widget_set_sensitive(GTK_WIDGET(s_gt_item), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(s_context_fdec_item), sensitive);
+ gtk_widget_set_sensitive(GTK_WIDGET(s_context_fdef_item), sensitive);
+}
+
+static void on_project_open(G_GNUC_UNUSED GObject * obj, GKeyFile * config, G_GNUC_UNUSED gpointer user_data)
+{
+ set_widgets_sensitive(TRUE);
+}
+
+static void on_project_close(G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED gpointer user_data)
+{
+ set_widgets_sensitive(FALSE);
+}
+
+PluginCallback plugin_callbacks[] = {
+ {"project-open", (GCallback) & on_project_open, TRUE, NULL},
+ {"project-close", (GCallback) & on_project_close, TRUE, NULL},
+ {NULL, NULL, FALSE, NULL}
+};
+
+static void spawn_cmd(const gchar *cmd, const gchar *dir)
+{
+ GError *error = NULL;
+ gchar **argv;
+ gchar *working_dir;
+ gchar *utf8_working_dir;
+ gchar *utf8_cmd_string;
+ gchar *err;
+ gint exitcode;
+
+#ifndef G_OS_WIN32
+ /* run within shell so we can use pipes */
+ argv = g_new0(gchar *, 4);
+ argv[0] = g_strdup("/bin/sh");
+ argv[1] = g_strdup("-c");
+ argv[2] = g_strdup(cmd);
+ argv[3] = NULL;
+#else
+ /* no shell on windows */
+ argv = g_new0(gchar *, 2);
+ argv[0] = g_strdup(cmd);
+ argv[1] = NULL;
+#endif
+
+ utf8_cmd_string = utils_get_utf8_from_locale(cmd);
+ utf8_working_dir = g_strdup(dir);
+ working_dir = utils_get_locale_from_utf8(utf8_working_dir);
+
+ msgwin_clear_tab(MSG_MESSAGE);
+ msgwin_switch_tab(MSG_MESSAGE, TRUE);
+ msgwin_msg_add(COLOR_BLUE, -1, NULL, _("%s (in directory: %s)"), utf8_cmd_string, utf8_working_dir);
+ g_free(utf8_working_dir);
+ g_free(utf8_cmd_string);
+
+ if (!utils_spawn_sync(working_dir, argv, NULL, G_SPAWN_SEARCH_PATH,
+ NULL, NULL, NULL, &err, &exitcode, &error) || exitcode != 0)
+ {
+ if (error != NULL)
+ {
+ msgwin_msg_add(COLOR_RED, -1, NULL, _("Process execution failed (%s)"), error->message);
+ g_error_free(error);
+ }
+ msgwin_msg_add(COLOR_RED, -1, NULL, "%s", err);
+ }
+ else
+ {
+ msgwin_msg_add(COLOR_BLACK, -1, NULL, "%s", err);
+ }
+
+ g_strfreev(argv);
+ g_free(working_dir);
+ g_free(err);
+}
+
+static gchar *get_tags_filename()
+{
+ gchar *ret = NULL;
+
+ if (geany_data->app->project)
+ {
+ ret = utils_remove_ext_from_filename(geany_data->app->project->file_name);
+ SETPTR(ret, g_strconcat(ret, ".tags", NULL));
+ }
+ return ret;
+}
+
+static gchar *generate_find_string(GeanyProject *prj)
+{
+ gchar *ret;
+
+ ret = g_strdup("find");
+
+ if (!EMPTY(prj->file_patterns))
+ {
+ guint i;
+
+ SETPTR(ret, g_strconcat(ret, " \\( -name \"", prj->file_patterns[0], "\"", NULL));
+ for (i = 1; prj->file_patterns[i]; i++)
+ SETPTR(ret, g_strconcat(ret, " -o -name \"", prj->file_patterns[i], "\"", NULL));
+ SETPTR(ret, g_strconcat(ret, " \\)", NULL));
+ }
+ return ret;
+}
+
+
+static void
+on_generate_tags(GtkMenuItem *menuitem, gpointer user_data)
+{
+ GeanyProject *prj;
+
+ prj = geany_data->app->project;
+ if (prj)
+ {
+ gchar *cmd;
+ gchar *tag_filename;
+
+ tag_filename = get_tags_filename();
+
+#ifndef G_OS_WIN32
+ gchar *find_string = generate_find_string(prj);
+ cmd = g_strconcat(find_string,
+ " | ctags --totals --fields=fKsSt --extra=-fq --c-kinds=+p --sort=foldcase --excmd=number -L - -f ",
+ tag_filename, NULL);
+#else
+ /* We don't have find and | on windows, generate tags for all files in the project (-R recursively) */
+
+ /* Unfortunately, there's a bug in ctags - when run with -R, the first line is
+ * empty, ctags doesn't recognize the tags file as a valid ctags file and
+ * refuses to overwrite it. Therefore, we need to delete the tags file manually. */
+ g_unlink(tag_filename);
+
+ cmd = g_strconcat("ctags.exe -R --totals --fields=fKsSt --extra=-fq --c-kinds=+p --sort=foldcase --excmd=number -f ",
+ tag_filename, NULL);
+#endif
+
+ spawn_cmd(cmd, prj->base_path);
+
+ g_free(cmd);
+ g_free(find_string);
+ g_free(tag_filename);
+ }
+}
+
+static void show_entry(tagEntry *entry)
+{
+ const gchar *kind;
+ const gchar *signature;
+ const gchar *scope;
+ const gchar *file;
+ const gchar *name;
+ gchar *scope_str;
+ gchar *kind_str;
+
+ file = entry->file;
+ if (!file)
+ file = "";
+
+ name = entry->name;
+ if (!name)
+ name = "";
+
+ signature = tagsField(entry, "signature");
+ if (!signature)
+ signature = "";
+
+ scope = tagsField(entry, "class");
+ if (!scope)
+ scope = tagsField(entry, "struct");
+ if (!scope)
+ scope = tagsField(entry, "union");
+ if (!scope)
+ scope = tagsField(entry, "enum");
+
+ if (scope)
+ scope_str = g_strconcat(scope, "::", NULL);
+ else
+ scope_str = g_strdup("");
+
+ kind = entry->kind;
+ if (kind)
+ {
+ kind_str = g_strconcat(kind, ": ", NULL);
+ SETPTR(kind_str, g_strdup_printf("%-14s", kind_str));
+ }
+ else
+ kind_str = g_strdup("");
+
+ msgwin_msg_add(COLOR_BLACK, -1, NULL, "%s:%lu:\n %s%s%s%s", file,
+ entry->address.lineNumber, kind_str, scope_str, name, signature);
+
+ g_free(scope_str);
+ g_free(kind_str);
+}
+
+
+static gchar *get_selection()
+{
+ gchar *ret = NULL;
+ GeanyDocument *doc = document_get_current();
+ GeanyEditor *editor;
+
+ if (!doc)
+ return NULL;
+
+ editor = doc->editor;
+
+ if (sci_has_selection(editor->sci))
+ ret = sci_get_selection_contents(editor->sci);
+ else
+ ret = editor_get_word_at_pos(editor, -1, GEANY_WORDCHARS);
+
+ return ret;
+}
+
+/* TODO: Not possible to do it the way below because some of the API is private
+ * in Geany. This means the cursor has to be placed at the symbol first and
+ * then right-clicked (right-clicking without having the cursor at the symbol
+ * doesn't work) */
+
+/*
+static gchar *get_selection()
+{
+ gchar *ret = NULL;
+ GeanyDocument *doc = document_get_current();
+
+ if (!doc)
+ return NULL;
+
+ if (!sci_has_selection(doc->editor->sci))
+ sci_set_current_position(doc->editor->sci, editor_info.click_pos, FALSE);
+
+ if (sci_has_selection(doc->editor->sci))
+ return sci_get_selection_contents(doc->editor->sci);
+
+ gint len = sci_get_selected_text_length(doc->editor->sci);
+
+ ret = g_malloc(len + 1);
+ sci_get_selected_text(doc->editor->sci, ret);
+
+ editor_find_current_word(doc->editor, -1,
+ editor_info.current_word, GEANY_MAX_WORD_LENGTH, NULL);
+
+ return editor_info.current_word != 0 ? g_strdup(editor_info.current_word) : NULL;
+}
+*/
+
+typedef enum
+{
+ MATCH_FULL,
+ MATCH_PREFIX,
+ MATCH_PATTERN
+} MatchType;
+
+static gboolean find_first(tagFile *tf, tagEntry *entry, const gchar *name, MatchType match_type)
+{
+ gboolean ret;
+
+ if (match_type == MATCH_PATTERN)
+ ret = tagsFirst(tf, entry) == TagSuccess;
+ else
+ {
+ int options = TAG_IGNORECASE;
+
+ options |= match_type == MATCH_PREFIX ? TAG_PARTIALMATCH : TAG_FULLMATCH;
+ ret = tagsFind(tf, entry, name, options) == TagSuccess;
+ }
+ return ret;
+}
+
+static gboolean find_next(tagFile *tf, tagEntry *entry, MatchType match_type)
+{
+ gboolean ret;
+
+ if (match_type == MATCH_PATTERN)
+ ret = tagsNext(tf, entry) == TagSuccess;
+ else
+ ret = tagsFindNext(tf, entry) == TagSuccess;
+ return ret;
+}
+
+static gboolean filter_tag(tagEntry *entry, GPatternSpec *name, gboolean declaration, gboolean case_sensitive)
+{
+ gboolean filter = TRUE;
+ gchar *entry_name;
+
+ if (!EMPTY(entry->kind))
+ {
+ gboolean is_prototype;
+
+ is_prototype = g_strcmp0(entry->kind, "prototype") == 0;
+ filter = (declaration && !is_prototype) || (!declaration && is_prototype);
+ if (filter)
+ return TRUE;
+ }
+
+ if (case_sensitive)
+ entry_name = g_strdup(entry->name);
+ else
+ entry_name = g_utf8_strdown(entry->name, -1);
+
+ filter = !g_pattern_match_string(name, entry_name);
+
+ g_free(entry_name);
+
+ return filter;
+}
+
+static void find_tags(const gchar *name, gboolean declaration, gboolean case_sensitive, MatchType match_type)
+{
+ tagFile *tf;
+ GeanyProject *prj;
+ gchar *tag_filename = NULL;
+ tagEntry entry;
+ tagFileInfo info;
+
+ prj = geany_data->app->project;
+ if (!prj)
+ return;
+
+ msgwin_clear_tab(MSG_MESSAGE);
+ msgwin_set_messages_dir(prj->base_path);
+
+ tag_filename = get_tags_filename();
+ tf = tagsOpen(tag_filename, &info);
+
+ if (tf)
+ {
+ if (find_first(tf, &entry, name, match_type))
+ {
+ GPatternSpec *name_pat;
+ gchar *name_case;
+
+ if (case_sensitive)
+ name_case = g_strdup(name);
+ else
+ name_case = g_utf8_strdown(name, -1);
+
+ SETPTR(name_case, g_strconcat("*", name_case, "*", NULL));
+ name_pat = g_pattern_spec_new(name_case);
+
+ if (!filter_tag(&entry, name_pat, declaration, case_sensitive))
+ {
+ gchar *path = g_build_filename(prj->base_path, entry.file, NULL);
+ GeanyDocument *doc = document_open_file(path, FALSE, NULL, NULL);
+ if (doc != NULL)
+ {
+ navqueue_goto_line(document_get_current(), doc, entry.address.lineNumber);
+ gtk_widget_grab_focus(GTK_WIDGET(doc->editor->sci));
+ }
+ show_entry(&entry);
+ g_free(path);
+ }
+
+ while (find_next(tf, &entry, match_type))
+ {
+ if (!filter_tag(&entry, name_pat, declaration, case_sensitive))
+ show_entry(&entry);
+ }
+
+ g_pattern_spec_free(name_pat);
+ g_free(name_case);
+ }
+
+ tagsClose(tf);
+ }
+
+ msgwin_switch_tab(MSG_MESSAGE, TRUE);
+
+ g_free(tag_filename);
+}
+
+static void on_find_declaration(GtkMenuItem *menuitem, gpointer user_data)
+{
+ gchar *name;
+
+ name = get_selection();
+ if (name)
+ find_tags(name, TRUE, TRUE, MATCH_FULL);
+ g_free(name);
+}
+
+static void on_find_definition(GtkMenuItem *menuitem, gpointer user_data)
+{
+ gchar *name;
+
+ name = get_selection();
+ if (name)
+ find_tags(name, FALSE, TRUE, MATCH_FULL);
+ g_free(name);
+}
+
+static void create_dialog_find_file()
+{
+ GtkWidget *label, *vbox, *ebox, *entry;
+ GtkSizeGroup *size_group;
+
+ if (s_ft_dialog.widget)
+ return;
+
+ s_ft_dialog.widget = gtk_dialog_new_with_buttons(
+ _("Find Tag"), GTK_WINDOW(geany->main_widgets->window),
+ GTK_DIALOG_DESTROY_WITH_PARENT,
+ GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, NULL);
+ gtk_dialog_add_button(GTK_DIALOG(s_ft_dialog.widget), "gtk-find", GTK_RESPONSE_ACCEPT);
+ gtk_dialog_set_default_response(GTK_DIALOG(s_ft_dialog.widget), GTK_RESPONSE_ACCEPT);
+
+ vbox = ui_dialog_vbox_new(GTK_DIALOG(s_ft_dialog.widget));
+ gtk_box_set_spacing(GTK_BOX(vbox), 9);
+
+ size_group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL);
+
+ label = gtk_label_new(_("Search for:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_size_group_add_widget(size_group, label);
+
+ s_ft_dialog.combo = gtk_combo_box_text_new_with_entry();
+ entry = gtk_bin_get_child(GTK_BIN(s_ft_dialog.combo));
+
+ ui_entry_add_clear_icon(GTK_ENTRY(entry));
+ gtk_entry_set_width_chars(GTK_ENTRY(entry), 40);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), entry);
+ ui_entry_add_clear_icon(GTK_ENTRY(entry));
+ gtk_entry_set_activates_default(GTK_ENTRY(entry), TRUE);
+
+ ebox = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(ebox), s_ft_dialog.combo, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
+
+ label = gtk_label_new(_("Match type:"));
+ gtk_misc_set_alignment(GTK_MISC(label), 0, 0.5);
+ gtk_size_group_add_widget(size_group, label);
+
+ s_ft_dialog.combo_match = gtk_combo_box_new_text();
+ gtk_combo_box_append_text(GTK_COMBO_BOX(s_ft_dialog.combo_match), "full");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(s_ft_dialog.combo_match), "prefix");
+ gtk_combo_box_append_text(GTK_COMBO_BOX(s_ft_dialog.combo_match), "pattern");
+ gtk_combo_box_set_active(GTK_COMBO_BOX(s_ft_dialog.combo_match), 1);
+ gtk_label_set_mnemonic_widget(GTK_LABEL(label), s_ft_dialog.combo_match);
+
+ ebox = gtk_hbox_new(FALSE, 6);
+ gtk_box_pack_start(GTK_BOX(ebox), label, FALSE, FALSE, 0);
+ gtk_box_pack_start(GTK_BOX(ebox), s_ft_dialog.combo_match, TRUE, TRUE, 0);
+ gtk_box_pack_start(GTK_BOX(vbox), ebox, TRUE, FALSE, 0);
+
+ s_ft_dialog.case_sensitive = gtk_check_button_new_with_mnemonic(_("C_ase sensitive"));
+ gtk_button_set_focus_on_click(GTK_BUTTON(s_ft_dialog.case_sensitive), FALSE);
+
+ s_ft_dialog.declaration = gtk_check_button_new_with_mnemonic(_("_Declaration"));
+ gtk_button_set_focus_on_click(GTK_BUTTON(s_ft_dialog.declaration), FALSE);
+
+ g_object_unref(G_OBJECT(size_group)); /* auto destroy the size group */
+
+ gtk_container_add(GTK_CONTAINER(vbox), s_ft_dialog.case_sensitive);
+ gtk_container_add(GTK_CONTAINER(vbox), s_ft_dialog.declaration);
+ gtk_widget_show_all(vbox);
+}
+
+static void on_find_tag(GtkMenuItem *menuitem, gpointer user_data)
+{
+ gchar *selection;
+ GtkWidget *entry;
+
+ create_dialog_find_file();
+
+ entry = gtk_bin_get_child(GTK_BIN(s_ft_dialog.combo));
+
+ selection = get_selection();
+ if (selection)
+ gtk_entry_set_text(GTK_ENTRY(entry), selection);
+ g_free(selection);
+
+ gtk_widget_grab_focus(entry);
+
+ if (gtk_dialog_run(GTK_DIALOG(s_ft_dialog.widget)) == GTK_RESPONSE_ACCEPT)
+ {
+ const gchar *name;
+ gboolean case_sensitive, declaration;
+ MatchType match_type;
+
+ name = gtk_entry_get_text(GTK_ENTRY(entry));
+ case_sensitive = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_ft_dialog.case_sensitive));
+ declaration = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(s_ft_dialog.declaration));
+ match_type = gtk_combo_box_get_active(GTK_COMBO_BOX(s_ft_dialog.combo_match));
+
+ ui_combo_box_add_to_history(GTK_COMBO_BOX_TEXT(s_ft_dialog.combo), name, 0);
+
+ find_tags(name, declaration, case_sensitive, match_type);
+ }
+
+ gtk_widget_hide(s_ft_dialog.widget);
+}
+
+static gboolean kb_callback(guint key_id)
+{
+ switch (key_id)
+ {
+ case KB_FIND_TAG:
+ on_find_tag(NULL, NULL);
+ return TRUE;
+ case KB_GENERATE_TAGS:
+ on_generate_tags(NULL, NULL);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+void plugin_init(G_GNUC_UNUSED GeanyData * data)
+{
+ GeanyKeyGroup *key_group = plugin_set_key_group(geany_plugin, "GeanyCtags", KB_COUNT, kb_callback);
+
+ s_context_sep_item = gtk_separator_menu_item_new();
+ gtk_widget_show(s_context_sep_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_sep_item);
+
+ s_context_fdec_item = gtk_menu_item_new_with_mnemonic(_("Find Tag Declaration (geanyctags)"));
+ gtk_widget_show(s_context_fdec_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_fdec_item);
+ g_signal_connect((gpointer) s_context_fdec_item, "activate", G_CALLBACK(on_find_declaration), NULL);
+
+ s_context_fdef_item = gtk_menu_item_new_with_mnemonic(_("Find Tag Definition (geanyctags)"));
+ gtk_widget_show(s_context_fdef_item);
+ gtk_menu_shell_prepend(GTK_MENU_SHELL(geany->main_widgets->editor_menu), s_context_fdef_item);
+ g_signal_connect((gpointer) s_context_fdef_item, "activate", G_CALLBACK(on_find_definition), NULL);
+
+ s_sep_item = gtk_separator_menu_item_new();
+ gtk_widget_show(s_sep_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_sep_item);
+
+ s_gt_item = gtk_menu_item_new_with_mnemonic(_("Generate tags"));
+ gtk_widget_show(s_gt_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_gt_item);
+ g_signal_connect((gpointer) s_gt_item, "activate", G_CALLBACK(on_generate_tags), NULL);
+ keybindings_set_item(key_group, KB_GENERATE_TAGS, NULL,
+ 0, 0, "generate_tags", _("Generate tags"), s_gt_item);
+
+ s_ft_item = gtk_menu_item_new_with_mnemonic(_("Find tag"));
+ gtk_widget_show(s_ft_item);
+ gtk_container_add(GTK_CONTAINER(geany->main_widgets->project_menu), s_ft_item);
+ g_signal_connect((gpointer) s_ft_item, "activate", G_CALLBACK(on_find_tag), NULL);
+ keybindings_set_item(key_group, KB_FIND_TAG, NULL,
+ 0, 0, "find_tag", _("Find tag"), s_ft_item);
+
+ set_widgets_sensitive(FALSE);
+}
+
+void plugin_cleanup(void)
+{
+ gtk_widget_destroy(s_context_fdec_item);
+ gtk_widget_destroy(s_context_fdef_item);
+ gtk_widget_destroy(s_context_sep_item);
+
+ gtk_widget_destroy(s_ft_item);
+ gtk_widget_destroy(s_gt_item);
+ gtk_widget_destroy(s_sep_item);
+
+ if (s_ft_dialog.widget)
+ gtk_widget_destroy(s_ft_dialog.widget);
+ s_ft_dialog.widget = NULL;
+}
Modified: geanyctags/src/readtags.c
959 lines changed, 959 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,959 @@
+/*
+* $Id: readtags.c 592 2007-07-31 03:30:41Z dhiebert $
+*
+* Copyright (c) 1996-2003, Darren Hiebert
+*
+* This source code is released into the public domain.
+*
+* This module contains functions for reading tag files.
+*/
+
+/*
+* INCLUDE FILES
+*/
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h> /* to declare off_t */
+
+#include "readtags.h"
+
+/*
+* MACROS
+*/
+#define TAB '\t'
+
+
+/*
+* DATA DECLARATIONS
+*/
+typedef struct {
+ size_t size;
+ char *buffer;
+} vstring;
+
+/* Information about current tag file */
+struct sTagFile {
+ /* has the file been opened and this structure initialized? */
+ short initialized;
+ /* format of tag file */
+ short format;
+ /* how is the tag file sorted? */
+ sortType sortMethod;
+ /* pointer to file structure */
+ FILE* fp;
+ /* file position of first character of `line' */
+ off_t pos;
+ /* size of tag file in seekable positions */
+ off_t size;
+ /* last line read */
+ vstring line;
+ /* name of tag in last line read */
+ vstring name;
+ /* defines tag search state */
+ struct {
+ /* file position of last match for tag */
+ off_t pos;
+ /* name of tag last searched for */
+ char *name;
+ /* length of name for partial matches */
+ size_t nameLength;
+ /* peforming partial match */
+ short partial;
+ /* ignoring case */
+ short ignorecase;
+ } search;
+ /* miscellaneous extension fields */
+ struct {
+ /* number of entries in `list' */
+ unsigned short max;
+ /* list of key value pairs */
+ tagExtensionField *list;
+ } fields;
+ /* buffers to be freed at close */
+ struct {
+ /* name of program author */
+ char *author;
+ /* name of program */
+ char *name;
+ /* URL of distribution */
+ char *url;
+ /* program version */
+ char *version;
+ } program;
+};
+
+/*
+* DATA DEFINITIONS
+*/
+const char *const EmptyString = "";
+const char *const PseudoTagPrefix = "!_";
+
+/*
+* FUNCTION DEFINITIONS
+*/
+
+/*
+ * Compare two strings, ignoring case.
+ * Return 0 for match, < 0 for smaller, > 0 for bigger
+ * Make sure case is folded to uppercase in comparison (like for 'sort -f')
+ * This makes a difference when one of the chars lies between upper and lower
+ * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
+ */
+static int struppercmp (const char *s1, const char *s2)
+{
+ int result;
+ do
+ {
+ result = toupper ((int) *s1) - toupper ((int) *s2);
+ } while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
+ return result;
+}
+
+static int strnuppercmp (const char *s1, const char *s2, size_t n)
+{
+ int result;
+ do
+ {
+ result = toupper ((int) *s1) - toupper ((int) *s2);
+ } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
+ return result;
+}
+
+static int growString (vstring *s)
+{
+ int result = 0;
+ size_t newLength;
+ char *newLine;
+ if (s->size == 0)
+ {
+ newLength = 128;
+ newLine = (char*) malloc (newLength);
+ *newLine = '\0';
+ }
+ else
+ {
+ newLength = 2 * s->size;
+ newLine = (char*) realloc (s->buffer, newLength);
+ }
+ if (newLine == NULL)
+ perror ("string too large");
+ else
+ {
+ s->buffer = newLine;
+ s->size = newLength;
+ result = 1;
+ }
+ return result;
+}
+
+/* Copy name of tag out of tag line */
+static void copyName (tagFile *const file)
+{
+ size_t length;
+ const char *end = strchr (file->line.buffer, '\t');
+ if (end == NULL)
+ {
+ end = strchr (file->line.buffer, '\n');
+ if (end == NULL)
+ end = strchr (file->line.buffer, '\r');
+ }
+ if (end != NULL)
+ length = end - file->line.buffer;
+ else
+ length = strlen (file->line.buffer);
+ while (length >= file->name.size)
+ growString (&file->name);
+ strncpy (file->name.buffer, file->line.buffer, length);
+ file->name.buffer [length] = '\0';
+}
+
+static int readTagLineRaw (tagFile *const file)
+{
+ int result = 1;
+ int reReadLine;
+
+ /* If reading the line places any character other than a null or a
+ * newline at the last character position in the buffer (one less than
+ * the buffer size), then we must resize the buffer and reattempt to read
+ * the line.
+ */
+ do
+ {
+ char *const pLastChar = file->line.buffer + file->line.size - 2;
+ char *line;
+
+ file->pos = ftell (file->fp);
+ reReadLine = 0;
+ *pLastChar = '\0';
+ line = fgets (file->line.buffer, (int) file->line.size, file->fp);
+ if (line == NULL)
+ {
+ /* read error */
+ if (! feof (file->fp))
+ perror ("readTagLine");
+ result = 0;
+ }
+ else if (*pLastChar != '\0' &&
+ *pLastChar != '\n' && *pLastChar != '\r')
+ {
+ /* buffer overflow */
+ growString (&file->line);
+ fseek (file->fp, file->pos, SEEK_SET);
+ reReadLine = 1;
+ }
+ else
+ {
+ size_t i = strlen (file->line.buffer);
+ while (i > 0 &&
+ (file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r'))
+ {
+ file->line.buffer [i - 1] = '\0';
+ --i;
+ }
+ }
+ } while (reReadLine && result);
+ if (result)
+ copyName (file);
+ return result;
+}
+
+static int readTagLine (tagFile *const file)
+{
+ int result;
+ do
+ {
+ result = readTagLineRaw (file);
+ } while (result && *file->name.buffer == '\0');
+ return result;
+}
+
+static tagResult growFields (tagFile *const file)
+{
+ tagResult result = TagFailure;
+ unsigned short newCount = (unsigned short) 2 * file->fields.max;
+ tagExtensionField *newFields = (tagExtensionField*)
+ realloc (file->fields.list, newCount * sizeof (tagExtensionField));
+ if (newFields == NULL)
+ perror ("too many extension fields");
+ else
+ {
+ file->fields.list = newFields;
+ file->fields.max = newCount;
+ result = TagSuccess;
+ }
+ return result;
+}
+
+static void parseExtensionFields (tagFile *const file, tagEntry *const entry,
+ char *const string)
+{
+ char *p = string;
+ while (p != NULL && *p != '\0')
+ {
+ while (*p == TAB)
+ *p++ = '\0';
+ if (*p != '\0')
+ {
+ char *colon;
+ char *field = p;
+ p = strchr (p, TAB);
+ if (p != NULL)
+ *p++ = '\0';
+ colon = strchr (field, ':');
+ if (colon == NULL)
+ entry->kind = field;
+ else
+ {
+ const char *key = field;
+ const char *value = colon + 1;
+ *colon = '\0';
+ if (strcmp (key, "kind") == 0)
+ entry->kind = value;
+ else if (strcmp (key, "file") == 0)
+ entry->fileScope = 1;
+ else if (strcmp (key, "line") == 0)
+ entry->address.lineNumber = atol (value);
+ else
+ {
+ if (entry->fields.count == file->fields.max)
+ growFields (file);
+ file->fields.list [entry->fields.count].key = key;
+ file->fields.list [entry->fields.count].value = value;
+ ++entry->fields.count;
+ }
+ }
+ }
+ }
+}
+
+static void parseTagLine (tagFile *file, tagEntry *const entry)
+{
+ int i;
+ char *p = file->line.buffer;
+ char *tab = strchr (p, TAB);
+
+ entry->fields.list = NULL;
+ entry->fields.count = 0;
+ entry->kind = NULL;
+ entry->fileScope = 0;
+
+ entry->name = p;
+ if (tab != NULL)
+ {
+ *tab = '\0';
+ p = tab + 1;
+ entry->file = p;
+ tab = strchr (p, TAB);
+ if (tab != NULL)
+ {
+ int fieldsPresent;
+ *tab = '\0';
+ p = tab + 1;
+ if (*p == '/' || *p == '?')
+ {
+ /* parse pattern */
+ int delimiter = *(unsigned char*) p;
+ entry->address.lineNumber = 0;
+ entry->address.pattern = p;
+ do
+ {
+ p = strchr (p + 1, delimiter);
+ } while (p != NULL && *(p - 1) == '\\');
+ if (p == NULL)
+ {
+ /* invalid pattern */
+ }
+ else
+ ++p;
+ }
+ else if (isdigit ((int) *(unsigned char*) p))
+ {
+ /* parse line number */
+ entry->address.pattern = p;
+ entry->address.lineNumber = atol (p);
+ while (isdigit ((int) *(unsigned char*) p))
+ ++p;
+ }
+ else
+ {
+ /* invalid pattern */
+ }
+ fieldsPresent = (strncmp (p, ";\"", 2) == 0);
+ *p = '\0';
+ if (fieldsPresent)
+ parseExtensionFields (file, entry, p + 2);
+ }
+ }
+ if (entry->fields.count > 0)
+ entry->fields.list = file->fields.list;
+ for (i = entry->fields.count ; i < file->fields.max ; ++i)
+ {
+ file->fields.list [i].key = NULL;
+ file->fields.list [i].value = NULL;
+ }
+}
+
+static char *duplicate (const char *str)
+{
+ char *result = NULL;
+ if (str != NULL)
+ {
+ result = strdup (str);
+ if (result == NULL)
+ perror (NULL);
+ }
+ return result;
+}
+
+static void readPseudoTags (tagFile *const file, tagFileInfo *const info)
+{
+ fpos_t startOfLine;
+ const size_t prefixLength = strlen (PseudoTagPrefix);
+ if (info != NULL)
+ {
+ info->file.format = 1;
+ info->file.sort = TAG_UNSORTED;
+ info->program.author = NULL;
+ info->program.name = NULL;
+ info->program.url = NULL;
+ info->program.version = NULL;
+ }
+ while (1)
+ {
+ fgetpos (file->fp, &startOfLine);
+ if (! readTagLine (file))
+ break;
+ if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
+ break;
+ else
+ {
+ tagEntry entry;
+ const char *key, *value;
+ parseTagLine (file, &entry);
+ key = entry.name + prefixLength;
+ value = entry.file;
+ if (strcmp (key, "TAG_FILE_SORTED") == 0)
+ file->sortMethod = (sortType) atoi (value);
+ else if (strcmp (key, "TAG_FILE_FORMAT") == 0)
+ file->format = (short) atoi (value);
+ else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0)
+ file->program.author = duplicate (value);
+ else if (strcmp (key, "TAG_PROGRAM_NAME") == 0)
+ file->program.name = duplicate (value);
+ else if (strcmp (key, "TAG_PROGRAM_URL") == 0)
+ file->program.url = duplicate (value);
+ else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0)
+ file->program.version = duplicate (value);
+ if (info != NULL)
+ {
+ info->file.format = file->format;
+ info->file.sort = file->sortMethod;
+ info->program.author = file->program.author;
+ info->program.name = file->program.name;
+ info->program.url = file->program.url;
+ info->program.version = file->program.version;
+ }
+ }
+ }
+ fsetpos (file->fp, &startOfLine);
+}
+
+static void gotoFirstLogicalTag (tagFile *const file)
+{
+ fpos_t startOfLine;
+ const size_t prefixLength = strlen (PseudoTagPrefix);
+ rewind (file->fp);
+ while (1)
+ {
+ fgetpos (file->fp, &startOfLine);
+ if (! readTagLine (file))
+ break;
+ if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
+ break;
+ }
+ fsetpos (file->fp, &startOfLine);
+}
+
+static tagFile *initialize (const char *const filePath, tagFileInfo *const info)
+{
+ tagFile *result = (tagFile*) calloc ((size_t) 1, sizeof (tagFile));
+ if (result != NULL)
+ {
+ growString (&result->line);
+ growString (&result->name);
+ result->fields.max = 20;
+ result->fields.list = (tagExtensionField*) calloc (
+ result->fields.max, sizeof (tagExtensionField));
+ result->fp = fopen (filePath, "r");
+ if (result->fp == NULL)
+ {
+ free (result);
+ result = NULL;
+ info->status.error_number = errno;
+ }
+ else
+ {
+ fseek (result->fp, 0, SEEK_END);
+ result->size = ftell (result->fp);
+ rewind (result->fp);
+ readPseudoTags (result, info);
+ info->status.opened = 1;
+ result->initialized = 1;
+ }
+ }
+ return result;
+}
+
+static void terminate (tagFile *const file)
+{
+ fclose (file->fp);
+
+ free (file->line.buffer);
+ free (file->name.buffer);
+ free (file->fields.list);
+
+ if (file->program.author != NULL)
+ free (file->program.author);
+ if (file->program.name != NULL)
+ free (file->program.name);
+ if (file->program.url != NULL)
+ free (file->program.url);
+ if (file->program.version != NULL)
+ free (file->program.version);
+ if (file->search.name != NULL)
+ free (file->search.name);
+
+ memset (file, 0, sizeof (tagFile));
+
+ free (file);
+}
+
+static tagResult readNext (tagFile *const file, tagEntry *const entry)
+{
+ tagResult result;
+ if (file == NULL || ! file->initialized)
+ result = TagFailure;
+ else if (! readTagLine (file))
+ result = TagFailure;
+ else
+ {
+ if (entry != NULL)
+ parseTagLine (file, entry);
+ result = TagSuccess;
+ }
+ return result;
+}
+
+static const char *readFieldValue (
+ const tagEntry *const entry, const char *const key)
+{
+ const char *result = NULL;
+ int i;
+ if (strcmp (key, "kind") == 0)
+ result = entry->kind;
+ else if (strcmp (key, "file") == 0)
+ result = EmptyString;
+ else for (i = 0 ; i < entry->fields.count && result == NULL ; ++i)
+ if (strcmp (entry->fields.list [i].key, key) == 0)
+ result = entry->fields.list [i].value;
+ return result;
+}
+
+static int readTagLineSeek (tagFile *const file, const off_t pos)
+{
+ int result = 0;
+ if (fseek (file->fp, pos, SEEK_SET) == 0)
+ {
+ result = readTagLine (file); /* read probable partial line */
+ if (pos > 0 && result)
+ result = readTagLine (file); /* read complete line */
+ }
+ return result;
+}
+
+static int nameComparison (tagFile *const file)
+{
+ int result;
+ if (file->search.ignorecase)
+ {
+ if (file->search.partial)
+ result = strnuppercmp (file->search.name, file->name.buffer,
+ file->search.nameLength);
+ else
+ result = struppercmp (file->search.name, file->name.buffer);
+ }
+ else
+ {
+ if (file->search.partial)
+ result = strncmp (file->search.name, file->name.buffer,
+ file->search.nameLength);
+ else
+ result = strcmp (file->search.name, file->name.buffer);
+ }
+ return result;
+}
+
+static void findFirstNonMatchBefore (tagFile *const file)
+{
+#define JUMP_BACK 512
+ int more_lines;
+ int comp;
+ off_t start = file->pos;
+ off_t pos = start;
+ do
+ {
+ if (pos < (off_t) JUMP_BACK)
+ pos = 0;
+ else
+ pos = pos - JUMP_BACK;
+ more_lines = readTagLineSeek (file, pos);
+ comp = nameComparison (file);
+ } while (more_lines && comp == 0 && pos > 0 && pos < start);
+}
+
+static tagResult findFirstMatchBefore (tagFile *const file)
+{
+ tagResult result = TagFailure;
+ int more_lines;
+ off_t start = file->pos;
+ findFirstNonMatchBefore (file);
+ do
+ {
+ more_lines = readTagLine (file);
+ if (nameComparison (file) == 0)
+ result = TagSuccess;
+ } while (more_lines && result != TagSuccess && file->pos < start);
+ return result;
+}
+
+static tagResult findBinary (tagFile *const file)
+{
+ tagResult result = TagFailure;
+ off_t lower_limit = 0;
+ off_t upper_limit = file->size;
+ off_t last_pos = 0;
+ off_t pos = upper_limit / 2;
+ while (result != TagSuccess)
+ {
+ if (! readTagLineSeek (file, pos))
+ {
+ /* in case we fell off end of file */
+ result = findFirstMatchBefore (file);
+ break;
+ }
+ else if (pos == last_pos)
+ {
+ /* prevent infinite loop if we backed up to beginning of file */
+ break;
+ }
+ else
+ {
+ const int comp = nameComparison (file);
+ last_pos = pos;
+ if (comp < 0)
+ {
+ upper_limit = pos;
+ pos = lower_limit + ((upper_limit - lower_limit) / 2);
+ }
+ else if (comp > 0)
+ {
+ lower_limit = pos;
+ pos = lower_limit + ((upper_limit - lower_limit) / 2);
+ }
+ else if (pos == 0)
+ result = TagSuccess;
+ else
+ result = findFirstMatchBefore (file);
+ }
+ }
+ return result;
+}
+
+static tagResult findSequential (tagFile *const file)
+{
+ tagResult result = TagFailure;
+ if (file->initialized)
+ {
+ while (result == TagFailure && readTagLine (file))
+ {
+ if (nameComparison (file) == 0)
+ result = TagSuccess;
+ }
+ }
+ return result;
+}
+
+static tagResult find (tagFile *const file, tagEntry *const entry,
+ const char *const name, const int options)
+{
+ tagResult result;
+ if (file->search.name != NULL)
+ free (file->search.name);
+ file->search.name = duplicate (name);
+ file->search.nameLength = strlen (name);
+ file->search.partial = (options & TAG_PARTIALMATCH) != 0;
+ file->search.ignorecase = (options & TAG_IGNORECASE) != 0;
+ fseek (file->fp, 0, SEEK_END);
+ file->size = ftell (file->fp);
+ rewind (file->fp);
+ if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
+ (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
+ {
+#ifdef DEBUG
+ printf ("<performing binary search>\n");
+#endif
+ result = findBinary (file);
+ }
+ else
+ {
+#ifdef DEBUG
+ printf ("<performing sequential search>\n");
+#endif
+ result = findSequential (file);
+ }
+
+ if (result != TagSuccess)
+ file->search.pos = file->size;
+ else
+ {
+ file->search.pos = file->pos;
+ if (entry != NULL)
+ parseTagLine (file, entry);
+ }
+ return result;
+}
+
+static tagResult findNext (tagFile *const file, tagEntry *const entry)
+{
+ tagResult result;
+ if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
+ (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
+ {
+ result = tagsNext (file, entry);
+ if (result == TagSuccess && nameComparison (file) != 0)
+ result = TagFailure;
+ }
+ else
+ {
+ result = findSequential (file);
+ if (result == TagSuccess && entry != NULL)
+ parseTagLine (file, entry);
+ }
+ return result;
+}
+
+/*
+* EXTERNAL INTERFACE
+*/
+
+extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info)
+{
+ return initialize (filePath, info);
+}
+
+extern tagResult tagsSetSortType (tagFile *const file, const sortType type)
+{
+ tagResult result = TagFailure;
+ if (file != NULL && file->initialized)
+ {
+ file->sortMethod = type;
+ result = TagSuccess;
+ }
+ return result;
+}
+
+extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry)
+{
+ tagResult result = TagFailure;
+ if (file != NULL && file->initialized)
+ {
+ gotoFirstLogicalTag (file);
+ result = readNext (file, entry);
+ }
+ return result;
+}
+
+extern tagResult tagsNext (tagFile *const file, tagEntry *const entry)
+{
+ tagResult result = TagFailure;
+ if (file != NULL && file->initialized)
+ result = readNext (file, entry);
+ return result;
+}
+
+extern const char *tagsField (const tagEntry *const entry, const char *const key)
+{
+ const char *result = NULL;
+ if (entry != NULL)
+ result = readFieldValue (entry, key);
+ return result;
+}
+
+extern tagResult tagsFind (tagFile *const file, tagEntry *const entry,
+ const char *const name, const int options)
+{
+ tagResult result = TagFailure;
+ if (file != NULL && file->initialized)
+ result = find (file, entry, name, options);
+ return result;
+}
+
+extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry)
+{
+ tagResult result = TagFailure;
+ if (file != NULL && file->initialized)
+ result = findNext (file, entry);
+ return result;
+}
+
+extern tagResult tagsClose (tagFile *const file)
+{
+ tagResult result = TagFailure;
+ if (file != NULL && file->initialized)
+ {
+ terminate (file);
+ result = TagSuccess;
+ }
+ return result;
+}
+
+/*
+* TEST FRAMEWORK
+*/
+
+#ifdef READTAGS_MAIN
+
+static const char *TagFileName = "tags";
+static const char *ProgramName;
+static int extensionFields;
+static int SortOverride;
+static sortType SortMethod;
+
+static void printTag (const tagEntry *entry)
+{
+ int i;
+ int first = 1;
+ const char* separator = ";\"";
+ const char* const empty = "";
+/* "sep" returns a value only the first time it is evaluated */
+#define sep (first ? (first = 0, separator) : empty)
+ printf ("%s\t%s\t%s",
+ entry->name, entry->file, entry->address.pattern);
+ if (extensionFields)
+ {
+ if (entry->kind != NULL && entry->kind [0] != '\0')
+ printf ("%s\tkind:%s", sep, entry->kind);
+ if (entry->fileScope)
+ printf ("%s\tfile:", sep);
+#if 0
+ if (entry->address.lineNumber > 0)
+ printf ("%s\tline:%lu", sep, entry->address.lineNumber);
+#endif
+ for (i = 0 ; i < entry->fields.count ; ++i)
+ printf ("%s\t%s:%s", sep, entry->fields.list [i].key,
+ entry->fields.list [i].value);
+ }
+ putchar ('\n');
+#undef sep
+}
+
+static void findTag (const char *const name, const int options)
+{
+ tagFileInfo info;
+ tagEntry entry;
+ tagFile *const file = tagsOpen (TagFileName, &info);
+ if (file == NULL)
+ {
+ fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
+ ProgramName, strerror (info.status.error_number), name);
+ exit (1);
+ }
+ else
+ {
+ if (SortOverride)
+ tagsSetSortType (file, SortMethod);
+ if (tagsFind (file, &entry, name, options) == TagSuccess)
+ {
+ do
+ {
+ printTag (&entry);
+ } while (tagsFindNext (file, &entry) == TagSuccess);
+ }
+ tagsClose (file);
+ }
+}
+
+static void listTags (void)
+{
+ tagFileInfo info;
+ tagEntry entry;
+ tagFile *const file = tagsOpen (TagFileName, &info);
+ if (file == NULL)
+ {
+ fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
+ ProgramName, strerror (info.status.error_number), TagFileName);
+ exit (1);
+ }
+ else
+ {
+ while (tagsNext (file, &entry) == TagSuccess)
+ printTag (&entry);
+ tagsClose (file);
+ }
+}
+
+const char *const Usage =
+ "Find tag file entries matching specified names.\n\n"
+ "Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n"
+ "Options:\n"
+ " -e Include extension fields in output.\n"
+ " -i Perform case-insensitive matching.\n"
+ " -l List all tags.\n"
+ " -p Perform partial matching.\n"
+ " -s[0|1|2] Override sort detection of tag file.\n"
+ " -t file Use specified tag file (default: \"tags\").\n"
+ "Note that options are acted upon as encountered, so order is significant.\n";
+
+extern int main (int argc, char **argv)
+{
+ int options = 0;
+ int actionSupplied = 0;
+ int i;
+ ProgramName = argv [0];
+ if (argc == 1)
+ {
+ fprintf (stderr, Usage, ProgramName);
+ exit (1);
+ }
+ for (i = 1 ; i < argc ; ++i)
+ {
+ const char *const arg = argv [i];
+ if (arg [0] != '-')
+ {
+ findTag (arg, options);
+ actionSupplied = 1;
+ }
+ else
+ {
+ size_t j;
+ for (j = 1 ; arg [j] != '\0' ; ++j)
+ {
+ switch (arg [j])
+ {
+ case 'e': extensionFields = 1; break;
+ case 'i': options |= TAG_IGNORECASE; break;
+ case 'p': options |= TAG_PARTIALMATCH; break;
+ case 'l': listTags (); actionSupplied = 1; break;
+
+ case 't':
+ if (arg [j+1] != '\0')
+ {
+ TagFileName = arg + j + 1;
+ j += strlen (TagFileName);
+ }
+ else if (i + 1 < argc)
+ TagFileName = argv [++i];
+ else
+ {
+ fprintf (stderr, Usage, ProgramName);
+ exit (1);
+ }
+ break;
+ case 's':
+ SortOverride = 1;
+ ++j;
+ if (arg [j] == '\0')
+ SortMethod = TAG_SORTED;
+ else if (strchr ("012", arg[j]) != NULL)
+ SortMethod = (sortType) (arg[j] - '0');
+ else
+ {
+ fprintf (stderr, Usage, ProgramName);
+ exit (1);
+ }
+ break;
+ default:
+ fprintf (stderr, "%s: unknown option: %c\n",
+ ProgramName, arg[j]);
+ exit (1);
+ break;
+ }
+ }
+ }
+ }
+ if (! actionSupplied)
+ {
+ fprintf (stderr,
+ "%s: no action specified: specify tag name(s) or -l option\n",
+ ProgramName);
+ exit (1);
+ }
+ return 0;
+}
+
+#endif
+
+/* vi:set tabstop=4 shiftwidth=4: */
Modified: geanyctags/src/readtags.h
252 lines changed, 252 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,252 @@
+/*
+* $Id: readtags.h 443 2006-05-30 04:37:13Z darren $
+*
+* Copyright (c) 1996-2003, Darren Hiebert
+*
+* This source code is released for the public domain.
+*
+* This file defines the public interface for looking up tag entries in tag
+* files.
+*
+* The functions defined in this interface are intended to provide tag file
+* support to a software tool. The tag lookups provided are sufficiently fast
+* enough to permit opening a sorted tag file, searching for a matching tag,
+* then closing the tag file each time a tag is looked up (search times are
+* on the order of hundreths of a second, even for huge tag files). This is
+* the recommended use of this library for most tool applications. Adhering
+* to this approach permits a user to regenerate a tag file at will without
+* the tool needing to detect and resynchronize with changes to the tag file.
+* Even for an unsorted 24MB tag file, tag searches take about one second.
+*/
+#ifndef READTAGS_H
+#define READTAGS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+* MACROS
+*/
+
+/* Options for tagsSetSortType() */
+typedef enum {
+ TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED
+} sortType ;
+
+/* Options for tagsFind() */
+#define TAG_FULLMATCH 0x0
+#define TAG_PARTIALMATCH 0x1
+
+#define TAG_OBSERVECASE 0x0
+#define TAG_IGNORECASE 0x2
+
+/*
+* DATA DECLARATIONS
+*/
+
+typedef enum { TagFailure = 0, TagSuccess = 1 } tagResult;
+
+struct sTagFile;
+
+typedef struct sTagFile tagFile;
+
+/* This structure contains information about the tag file. */
+typedef struct {
+
+ struct {
+ /* was the tag file successfully opened? */
+ int opened;
+
+ /* errno value when 'opened' is false */
+ int error_number;
+ } status;
+
+ /* information about the structure of the tag file */
+ struct {
+ /* format of tag file (1 = original, 2 = extended) */
+ short format;
+
+ /* how is the tag file sorted? */
+ sortType sort;
+ } file;
+
+
+ /* information about the program which created this tag file */
+ struct {
+ /* name of author of generating program (may be null) */
+ const char *author;
+
+ /* name of program (may be null) */
+ const char *name;
+
+ /* URL of distribution (may be null) */
+ const char *url;
+
+ /* program version (may be null) */
+ const char *version;
+ } program;
+
+} tagFileInfo;
+
+/* This structure contains information about an extension field for a tag.
+ * These exist at the end of the tag in the form "key:value").
+ */
+typedef struct {
+
+ /* the key of the extension field */
+ const char *key;
+
+ /* the value of the extension field (may be an empty string) */
+ const char *value;
+
+} tagExtensionField;
+
+/* This structure contains information about a specific tag. */
+typedef struct {
+
+ /* name of tag */
+ const char *name;
+
+ /* path of source file containing definition of tag */
+ const char *file;
+
+ /* address for locating tag in source file */
+ struct {
+ /* pattern for locating source line
+ * (may be NULL if not present) */
+ const char *pattern;
+
+ /* line number in source file of tag definition
+ * (may be zero if not known) */
+ unsigned long lineNumber;
+ } address;
+
+ /* kind of tag (may by name, character, or NULL if not known) */
+ const char *kind;
+
+ /* is tag of file-limited scope? */
+ short fileScope;
+
+ /* miscellaneous extension fields */
+ struct {
+ /* number of entries in `list' */
+ unsigned short count;
+
+ /* list of key value pairs */
+ tagExtensionField *list;
+ } fields;
+
+} tagEntry;
+
+
+/*
+* FUNCTION PROTOTYPES
+*/
+
+/*
+* This function must be called before calling other functions in this
+* library. It is passed the path to the tag file to read and a (possibly
+* null) pointer to a structure which, if not null, will be populated with
+* information about the tag file. If successful, the function will return a
+* handle which must be supplied to other calls to read information from the
+* tag file, and info.status.opened will be set to true. If unsuccessful,
+* info.status.opened will be set to false and info.status.error_number will
+* be set to the errno value representing the system error preventing the tag
+* file from being successfully opened.
+*/
+extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info);
+
+/*
+* This function allows the client to override the normal automatic detection
+* of how a tag file is sorted. Permissible values for `type' are
+* TAG_UNSORTED, TAG_SORTED, TAG_FOLDSORTED. Tag files in the new extended
+* format contain a key indicating whether or not they are sorted. However,
+* tag files in the original format do not contain such a key even when
+* sorted, preventing this library from taking advantage of fast binary
+* lookups. If the client knows that such an unmarked tag file is indeed
+* sorted (or not), it can override the automatic detection. Note that
+* incorrect lookup results will result if a tag file is marked as sorted when
+* it actually is not. The function will return TagSuccess if called on an
+* open tag file or TagFailure if not.
+*/
+extern tagResult tagsSetSortType (tagFile *const file, const sortType type);
+
+/*
+* Reads the first tag in the file, if any. It is passed the handle to an
+* opened tag file and a (possibly null) pointer to a structure which, if not
+* null, will be populated with information about the first tag file entry.
+* The function will return TagSuccess another tag entry is found, or
+* TagFailure if not (i.e. it reached end of file).
+*/
+extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry);
+
+/*
+* Step to the next tag in the file, if any. It is passed the handle to an
+* opened tag file and a (possibly null) pointer to a structure which, if not
+* null, will be populated with information about the next tag file entry. The
+* function will return TagSuccess another tag entry is found, or TagFailure
+* if not (i.e. it reached end of file). It will always read the first tag in
+* the file immediately after calling tagsOpen().
+*/
+extern tagResult tagsNext (tagFile *const file, tagEntry *const entry);
+
+/*
+* Retrieve the value associated with the extension field for a specified key.
+* It is passed a pointer to a structure already populated with values by a
+* previous call to tagsNext(), tagsFind(), or tagsFindNext(), and a string
+* containing the key of the desired extension field. If no such field of the
+* specified key exists, the function will return null.
+*/
+extern const char *tagsField (const tagEntry *const entry, const char *const key);
+
+/*
+* Find the first tag matching `name'. The structure pointed to by `entry'
+* will be populated with information about the tag file entry. If a tag file
+* is sorted using the C locale, a binary search algorithm is used to search
+* the tag file, resulting in very fast tag lookups, even in huge tag files.
+* Various options controlling the matches can be combined by bit-wise or-ing
+* certain values together. The available values are:
+*
+* TAG_PARTIALMATCH
+* Tags whose leading characters match `name' will qualify.
+*
+* TAG_FULLMATCH
+* Only tags whose full lengths match `name' will qualify.
+*
+* TAG_IGNORECASE
+* Matching will be performed in a case-insenstive manner. Note that
+* this disables binary searches of the tag file.
+*
+* TAG_OBSERVECASE
+* Matching will be performed in a case-senstive manner. Note that
+* this enables binary searches of the tag file.
+*
+* The function will return TagSuccess if a tag matching the name is found, or
+* TagFailure if not.
+*/
+extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, const char *const name, const int options);
+
+/*
+* Find the next tag matching the name and options supplied to the most recent
+* call to tagsFind() for the same tag file. The structure pointed to by
+* `entry' will be populated with information about the tag file entry. The
+* function will return TagSuccess if another tag matching the name is found,
+* or TagFailure if not.
+*/
+extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry);
+
+/*
+* Call tagsTerminate() at completion of reading the tag file, which will
+* close the file and free any internal memory allocated. The function will
+* return TagFailure is no file is currently open, TagSuccess otherwise.
+*/
+extern tagResult tagsClose (tagFile *const file);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
+
+/* vi:set tabstop=4 shiftwidth=4: */
Modified: geanyctags/wscript_build
29 lines changed, 29 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,29 @@
+# -*- coding: utf-8 -*-
+#
+# WAF build script for geany-plugins - GProject
+#
+# Copyright 2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+# $Id$
+
+from build.wafutils import build_plugin
+
+
+name = 'GeanyCtags'
+includes = ['geanyctags/src']
+
+build_plugin(bld, name, includes=includes)
Modified: geanyctags/wscript_configure
0 lines changed, 0 insertions(+), 0 deletions(-)
===================================================================
No diff available, check online
Modified: po/POTFILES.in
3 lines changed, 3 insertions(+), 0 deletions(-)
===================================================================
@@ -104,6 +104,9 @@ devhelp/devhelp/ige-conf-gconf.c
devhelp/devhelp/ige-conf.h
devhelp/devhelp/ige-conf-private.h
+# geanyctags
+geanyctags/src/geanyctags.c
+
# geanydoc
geanydoc/tests/unittests.c
geanydoc/src/config.c
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Colomban Wendling <ban(a)herbesfolles.org>
Committer: Colomban Wendling <ban(a)herbesfolles.org>
Date: Wed, 15 Oct 2014 20:30:05 UTC
Commit: c2d4a39f10dedc9be852719341331e8bc72317dd
https://github.com/geany/geany-plugins/commit/c2d4a39f10dedc9be852719341331…
Log Message:
-----------
Revert "autotools: Enable subdir-objects as many plugins use sub-directory objects"
This reverts commit 8873b9082c1c98b4f3e97062f0c6f26c9d980e95.
Unfortunately Automake has a bug when using `subdir-objects` and a
object is in a sub-directory that contains rules for creating other
objects. See http://debbugs.gnu.org/cgi/bugreport.cgi?bug=17354
We use it in GeanyPrj's and GeanyVC's unit tests.
There are other possible fixes while still using `subdir-objects`:
* include the C file directly from the unit test main source file,
like #include "../src/utils.c";
* switch these plugins to non-recursive Automake setup, so all rules
are in the same Makefile.am.
Modified Paths:
--------------
configure.ac
Modified: configure.ac
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -1,7 +1,7 @@
AC_PREREQ([2.61])
dnl Don't forget when updating version number to do GP_CHECK_GEANY also
AC_INIT([geany-plugins], [1.25])
-AM_INIT_AUTOMAKE([1.8 foreign dist-bzip2 tar-ustar subdir-objects])
+AM_INIT_AUTOMAKE([1.8 foreign dist-bzip2 tar-ustar])
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).
Branch: refs/heads/master
Author: Colomban Wendling <ban(a)herbesfolles.org>
Committer: Colomban Wendling <ban(a)herbesfolles.org>
Date: Wed, 15 Oct 2014 15:38:43 UTC
Commit: 2b2b4e8afdbd92600f7518313bb8ebea75539bf0
https://github.com/geany/geany-plugins/commit/2b2b4e8afdbd92600f7518313bb8e…
Log Message:
-----------
multiterm: Fix vapidir under Automake >= 1.11.0a with VPATH builds
Automake 1.11.0a introduced an incompatible change, that is it now
changes directory to `$(srcdir)` before running `valac`.
This means `$(top_srcdir)`-based paths are most likely to be incorrect
when building out-of-tree (VPATH) if the user used relative path as
srcdir (e.g. invoked configure with something like `../configure`).
To fix that, use the absolute version of `$(top_srcdir)`.
Modified Paths:
--------------
multiterm/src/Makefile.am
Modified: multiterm/src/Makefile.am
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -6,7 +6,7 @@ plugin = multiterm
geanyplugins_LTLIBRARIES = multiterm.la
multiterm_la_VALAFLAGS = \
- --vapidir $(top_srcdir)/multiterm/src/vapi \
+ --vapidir $(abs_top_srcdir)/multiterm/src/vapi \
--pkg gtk+-2.0 \
--pkg geany \
--pkg vte \
--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).