
* admin/grammars/c.by (expr-binop): Add MOD. (variablearg): Add 'opt-assign'. (variablearg, varnamelist): Add default values so that it can be later expanded into the tag. (opt-stuff-after-symbol): Rename to 'brackets-after-symbol' and remove empty match. (multi-stage-dereference): Adapt to above rename. (unaryexpression): Use 'symbol' instead of 'namespace-symbol', since the latter also leads to an empty match at the end which would make this too greedy. (variablearg-opt-name): Support parsing of function pointers inside an argument list. * semantic/analyze.el (semantic-analyze-find-tag-sequence-default): Always add scope to the local miniscope for each type. Otherwise, structure tags are not analyzed correctly. Also, always search the extended miniscope even when not dealing with types. * semantic/ctxt.el (semantic-get-local-variables-default): Also try to parse local variables for buffers which are currently marked as unparseable. Otherwise, it is often impossible to complete local variables. * semantic/scope.el (semantic-analyze-scoped-types-default): If we cannot find a type in the typecache, also look into the the types we already found. This is necessary since in C++, a 'using namespace' can be dependend on a previous one. (semantic-completable-tags-from-type): When creating the list of completable types, pull in types which are referenced through 'using' statements, and also preserve their filenames. * semanitc/bovine/c.el (semantic/analyze/refs): Require. (semantic-analyze-tag-references): New override. Mainly copied from the default implementation, but if nothing could be found (or just the tag itself), drop all namespaces from the scope and search again. This is necessary for implementations which are defined outside of the namespace and only pull those in through 'using' statements. (semantic-ctxt-scoped-types): Go through all tags around point and search them for using statements. In the case for using statements outside of function scope, append them in the correct order instead of using 'cons'. This is important since using statements may depend on previous ones. (semantic-expand-c-tag-namelist): Do not try to parse struct definitions as default values. The grammar parser seems to return the point positions slightly differently (as a cons instead of a list). Also, set parent for typedefs to 'nil'. It does not really make sense to set a parent class for typedefs, and it can also lead to endless loops when calculating scope. (semantic-c-reconstitute-token): Change handling of function pointers; instead of seeing them as variables, handle them as functions with a 'function-pointer' attribute. Also, correctly deal with function pointers as function arguments. (semantic-c-reconstitute-function-arglist): New function to parse function pointers inside an argument list. (semantic-format-tag-name): Use 'function-pointer' attribute instead of the old 'functionpointer-flag'. (semantic-cpp-lexer): Use new `semantic-lex-spp-paren-or-list'. * semantic/bovine/gcc.el (semantic-gcc-setup): Add 'features.h' to the list of files whose preprocessor symbols are included. This pulls in things like __USE_POSIX and similar. * semantic/format.el (semantic-format-tag-prototype-default): Display default values if available. * semantic/analyze/refs.el (semantic-analyze-refs-impl) (semantic-analyze-refs-proto): Add 'default-value' as ignorable in call to `semantic-tag-similar-p'. * semantic/db-mode.el (semanticdb-semantic-init-hook-fcn): Always set buffer for `semanticdb-current-table'. * semantic/db.el (semanticdb-table::semanticdb-refresh-table): The previous change turned up a bug in this method. Since the current table now correctly has a buffer set, the first clause in the `cond' would be taken, but there was a `save-excursion' missing. * semantic/lex-spp.el (semantic-c-end-of-macro): Declare. (semantic-lex-spp-token-macro-to-macro-stream): Deal with macros which open/close a scope. For this, leave an overlay if we encounter a single open paren and return a semantic-list in the lexer. When this list gets expanded, retrieve the old position from the overlay. See the comments in the function for further details. (semantic-lex-spp-find-closing-macro): New function to find the next macro which closes scope (i.e., has a closing paren). (semantic-lex-spp-replace-or-symbol-or-keyword): Go to end of closing macro if necessary. (semantic-lex-spp-paren-or-list): New lexer to specially deal with parens in macro definitions. * semantic/decorate/mode.el (semantic-decoration-mode): Do not decorate available tags immediately but in an idle timer, since EDE will usually not be activated yet, which will make it impossible to find project includes. * semantic/decorate/include.el (semantic-decoration-on-includes-highlight-default): Remove 'unloaded' from throttle when decorating includes, otherwise all would be loaded. Rename 'table' to 'currenttable' to make things clearer. * ede/linux.el (cl): Require during compile. * ede/linux.el (project-linux-build-directory-default) (project-linux-architecture-default): Add customizable variables. (ede-linux-project): Add additional slots to track Linux-specific information (out-of-tree build directory and selected architecture). (ede-linux--get-build-directory, ede-linux--get-archs) (ede-linux--detect-architecture, ede-linux--get-architecture) (ede-linux--include-path): Added function to detect Linux-specific information. (ede-linux-load): Set new Linux-specific information when creating a project. (ede-expand-filename-impl): Use new and more accurate include information. * semantic/scope.el (semantic-calculate-scope): Return a clone of the scopecache, so that everyone is working with its own (shallow) copy. Otherwise, if one caller is resetting the scope, it would be reset for all others working with the scope cache as well.
491 lines
16 KiB
EmacsLisp
491 lines
16 KiB
EmacsLisp
;;; ede/generic.el --- Base Support for generic build systems
|
||
|
||
;; Copyright (C) 2010-2013 Free Software Foundation, Inc.
|
||
|
||
;; Author: Eric M. Ludlam <eric@siege-engine.com>
|
||
|
||
;; This file is part of GNU Emacs.
|
||
|
||
;; GNU Emacs 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 3 of the License, or
|
||
;; (at your option) any later version.
|
||
|
||
;; GNU Emacs 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 GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
|
||
|
||
;;; Commentary:
|
||
;;
|
||
;; There are a lot of build systems out there, and EDE can't support
|
||
;; them all fully. The ede/generic.el system is the base for
|
||
;; supporting alternate build systems in a simple way, automatically.
|
||
;;
|
||
;; The structure is for the ede-generic baseclass, which is augmented
|
||
;; by simple sub-classes that can be created by users on an as needed
|
||
;; basis. The generic system will have targets for many language
|
||
;; types, and create the targets on an as needed basis. All
|
||
;; sub-project types will recycle the same generic target types.
|
||
;;
|
||
;; The generic target types will only be implemented for languages
|
||
;; where having EDE support actually matters, with a single MISC to
|
||
;; represent anything else.
|
||
;;
|
||
;; TOO MANY PROJECTS DETECTED:
|
||
;;
|
||
;; If enabling ede-generic support starts identifying too many
|
||
;; projects, drop a file called `.ede-ignore' into any directory where
|
||
;; you do not want a project to be.
|
||
;;
|
||
;; Customization:
|
||
;;
|
||
;; Since these projects are all so incredibly generic, a user will
|
||
;; need to configure some aspects of the project by hand. In order to
|
||
;; enable this without configuring the project objects directly (which
|
||
;; are auto-generated) a special ede-generic-config object is defined to
|
||
;; hold the basics. Generic projects will identify and use these
|
||
;; config files.
|
||
;;
|
||
;; Adding support for new projects:
|
||
;;
|
||
;; To add support to EDE Generic for new project types is very quick.
|
||
;; See the end of this file for examples such as CMake and SCons.
|
||
;;
|
||
;; Support consists of one class for your project, specifying the file
|
||
;; name used by the project system you want to support. It also
|
||
;; should implement th method `ede-generic-setup-configuration' to
|
||
;; prepopulate the configurable portion of the generic project with
|
||
;; build details.
|
||
;;
|
||
;; Lastly, call `ede-generic-new-autoloader' to setup your project so
|
||
;; EDE can use it.
|
||
;;
|
||
;; Adding support for new types of source code:
|
||
;;
|
||
;; Sources of different types are supported with a simple class which
|
||
;; subclasses `ede-generic-target'. The slots `shortname' and
|
||
;; `extension' should be given new initial values.
|
||
;;
|
||
;; Optionally, any target method used by EDE can then be overridden.
|
||
;; The ede-generic-target-c-cpp has some example methods setting up
|
||
;; the pre-processor map and system include path.
|
||
;;
|
||
;; NOTE: It is not necessary to modify ede-generic.el to add any of
|
||
;; the above described support features.
|
||
|
||
(require 'eieio-opt)
|
||
(require 'ede)
|
||
(require 'ede/shell)
|
||
(require 'semantic/db)
|
||
|
||
;;; Code:
|
||
;;
|
||
;; Start with the configuration system
|
||
(defclass ede-generic-config (eieio-persistent)
|
||
((extension :initform ".ede")
|
||
(file-header-line :initform ";; EDE Generic Project Configuration")
|
||
(project :initform nil
|
||
:documentation
|
||
"The project this config is bound to.")
|
||
;; Generic customizations
|
||
(build-command :initarg :build-command
|
||
:initform "make -k"
|
||
:type string
|
||
:custom string
|
||
:group (default build)
|
||
:documentation
|
||
"Command used for building this project.")
|
||
(debug-command :initarg :debug-command
|
||
:initform "gdb "
|
||
:type string
|
||
:custom string
|
||
:group (default build)
|
||
:documentation
|
||
"Command used for debugging this project.")
|
||
(run-command :initarg :run-command
|
||
:initform nil
|
||
:type (or null string)
|
||
:custom string
|
||
:group (default build)
|
||
:documentation
|
||
"Command used to run something related to this project.")
|
||
;; C target customizations
|
||
(c-include-path :initarg :c-include-path
|
||
:initform nil
|
||
:type list
|
||
:custom (repeat (string :tag "Path"))
|
||
:group c
|
||
:documentation
|
||
"The include path used by C/C++ projects.")
|
||
(c-preprocessor-table :initarg :c-preprocessor-table
|
||
:initform nil
|
||
:type list
|
||
:custom (repeat (cons (string :tag "Macro")
|
||
(string :tag "Value")))
|
||
:group c
|
||
:documentation
|
||
"Preprocessor Symbols for this project.")
|
||
(c-preprocessor-files :initarg :c-preprocessor-files
|
||
:initform nil
|
||
:type list
|
||
:custom (repeat (string :tag "Include File")))
|
||
)
|
||
"User Configuration object for a generic project.")
|
||
|
||
(defun ede-generic-load (dir &optional rootproj)
|
||
"Return a Generic Project object if there is a match.
|
||
Return nil if there isn't one.
|
||
Argument DIR is the directory it is created for.
|
||
ROOTPROJ is nil, since there is only one project."
|
||
;; Doesn't already exist, so let's make one.
|
||
(let* ((alobj ede-constructing)
|
||
(this nil))
|
||
(when (not alobj) (error "Cannot load generic project without the autoload instance"))
|
||
|
||
(setq this
|
||
(funcall (oref alobj class-sym)
|
||
(symbol-name (oref alobj class-sym))
|
||
:name (file-name-nondirectory
|
||
(directory-file-name dir))
|
||
:version "1.0"
|
||
:directory (file-name-as-directory dir)
|
||
:file (expand-file-name (oref alobj :proj-file)) ))
|
||
(ede-add-project-to-global-list this)
|
||
))
|
||
|
||
;;; Base Classes for the system
|
||
(defclass ede-generic-target (ede-target)
|
||
((shortname :initform ""
|
||
:type string
|
||
:allocation :class
|
||
:documentation
|
||
"Something prepended to the target name.")
|
||
(extension :initform ""
|
||
:type string
|
||
:allocation :class
|
||
:documentation
|
||
"Regular expression representing the extension used for this target.
|
||
subclasses of this base target will override the default value.")
|
||
)
|
||
"Baseclass for all targets belonging to the generic ede system."
|
||
:abstract t)
|
||
|
||
(defclass ede-generic-project (ede-project)
|
||
((buildfile :initform ""
|
||
:type string
|
||
:allocation :class
|
||
:documentation "The file name that identifies a project of this type.
|
||
The class allocated value is replace by different sub classes.")
|
||
(config :initform nil
|
||
:type (or null ede-generic-config)
|
||
:documentation
|
||
"The configuration object for this project.")
|
||
)
|
||
"The baseclass for all generic EDE project types."
|
||
:abstract t)
|
||
|
||
(defmethod initialize-instance ((this ede-generic-project)
|
||
&rest fields)
|
||
"Make sure the targets slot is bound."
|
||
(call-next-method)
|
||
(unless (slot-boundp this 'targets)
|
||
(oset this :targets nil))
|
||
)
|
||
|
||
(defmethod ede-generic-get-configuration ((proj ede-generic-project))
|
||
"Return the configuration for the project PROJ."
|
||
(let ((config (oref proj config)))
|
||
(when (not config)
|
||
(let ((fname (expand-file-name "EDEConfig.el"
|
||
(oref proj :directory))))
|
||
(if (file-exists-p fname)
|
||
;; Load in the configuration
|
||
(setq config (eieio-persistent-read fname 'ede-generic-config))
|
||
;; Create a new one.
|
||
(setq config (ede-generic-config
|
||
"Configuration"
|
||
:file fname))
|
||
;; Set initial values based on project.
|
||
(ede-generic-setup-configuration proj config))
|
||
;; Link things together.
|
||
(oset proj config config)
|
||
(oset config project proj)))
|
||
config))
|
||
|
||
(defmethod ede-generic-setup-configuration ((proj ede-generic-project) config)
|
||
"Default configuration setup method."
|
||
nil)
|
||
|
||
(defmethod ede-commit-project ((proj ede-generic-project))
|
||
"Commit any change to PROJ to its file."
|
||
(let ((config (ede-generic-get-configuration proj)))
|
||
(ede-commit config)))
|
||
|
||
;;; A list of different targets
|
||
(defclass ede-generic-target-c-cpp (ede-generic-target)
|
||
((shortname :initform "C/C++")
|
||
(extension :initform "\\([ch]\\(pp\\|xx\\|\\+\\+\\)?\\|cc\\|hh\\|CC?\\)"))
|
||
"EDE Generic Project target for C and C++ code.
|
||
All directories need at least one target.")
|
||
|
||
(defclass ede-generic-target-el (ede-generic-target)
|
||
((shortname :initform "ELisp")
|
||
(extension :initform "el"))
|
||
"EDE Generic Project target for Emacs Lisp code.
|
||
All directories need at least one target.")
|
||
|
||
(defclass ede-generic-target-fortran (ede-generic-target)
|
||
((shortname :initform "Fortran")
|
||
(extension :initform "[fF]9[05]\\|[fF]\\|for"))
|
||
"EDE Generic Project target for Fortran code.
|
||
All directories need at least one target.")
|
||
|
||
(defclass ede-generic-target-texi (ede-generic-target)
|
||
((shortname :initform "Texinfo")
|
||
(extension :initform "texi"))
|
||
"EDE Generic Project target for texinfo code.
|
||
All directories need at least one target.")
|
||
|
||
;; MISC must always be last since it will always match the file.
|
||
(defclass ede-generic-target-misc (ede-generic-target)
|
||
((shortname :initform "Misc")
|
||
(extension :initform ""))
|
||
"EDE Generic Project target for Misc files.
|
||
All directories need at least one target.")
|
||
|
||
;;; Automatic target acquisition.
|
||
(defun ede-generic-find-matching-target (class dir targets)
|
||
"Find a target that is a CLASS and is in DIR in the list of TARGETS."
|
||
(let ((match nil))
|
||
(dolist (T targets)
|
||
(when (and (object-of-class-p T class)
|
||
(string= (oref T :path) dir))
|
||
(setq match T)
|
||
))
|
||
match))
|
||
|
||
(defmethod ede-find-target ((proj ede-generic-project) buffer)
|
||
"Find an EDE target in PROJ for BUFFER.
|
||
If one doesn't exist, create a new one for this directory."
|
||
(let* ((ext (file-name-extension (buffer-file-name buffer)))
|
||
(classes (eieio-build-class-alist 'ede-generic-target t))
|
||
(cls nil)
|
||
(targets (oref proj targets))
|
||
(dir default-directory)
|
||
(ans nil)
|
||
)
|
||
;; Pick a matching class type.
|
||
(when ext
|
||
(dolist (C classes)
|
||
(let* ((classsym (intern (car C)))
|
||
(extreg (oref classsym extension)))
|
||
(when (and (not (string= extreg ""))
|
||
(string-match (concat "^" extreg "$") ext))
|
||
(setq cls classsym)))))
|
||
(when (not cls) (setq cls 'ede-generic-target-misc))
|
||
;; find a pre-existing matching target
|
||
(setq ans (ede-generic-find-matching-target cls dir targets))
|
||
;; Create a new instance if there wasn't one
|
||
(when (not ans)
|
||
(setq ans (make-instance
|
||
cls
|
||
:name (oref cls shortname)
|
||
:path dir
|
||
:source nil))
|
||
(object-add-to-list proj :targets ans)
|
||
)
|
||
ans))
|
||
|
||
;;; C/C++ support
|
||
(defmethod ede-preprocessor-map ((this ede-generic-target-c-cpp))
|
||
"Get the pre-processor map for some generic C code."
|
||
(let* ((proj (ede-target-parent this))
|
||
(root (ede-project-root proj))
|
||
(config (ede-generic-get-configuration proj))
|
||
filemap
|
||
)
|
||
;; Preprocessor files
|
||
(dolist (G (oref config :c-preprocessor-files))
|
||
(let ((table (semanticdb-file-table-object
|
||
(ede-expand-filename root G))))
|
||
(when table
|
||
(when (semanticdb-needs-refresh-p table)
|
||
(semanticdb-refresh-table table))
|
||
(setq filemap (append filemap (oref table lexical-table)))
|
||
)))
|
||
;; The core table
|
||
(setq filemap (append filemap (oref config :c-preprocessor-table)))
|
||
|
||
filemap
|
||
))
|
||
|
||
(defmethod ede-system-include-path ((this ede-generic-target-c-cpp))
|
||
"Get the system include path used by project THIS."
|
||
(let* ((proj (ede-target-parent this))
|
||
(config (ede-generic-get-configuration proj)))
|
||
(oref config c-include-path)))
|
||
|
||
;;; Commands
|
||
;;
|
||
(defmethod project-compile-project ((proj ede-generic-project) &optional command)
|
||
"Compile the entire current project PROJ.
|
||
Argument COMMAND is the command to use when compiling."
|
||
(let* ((config (ede-generic-get-configuration proj))
|
||
(comp (oref config :build-command)))
|
||
(compile comp)))
|
||
|
||
(defmethod project-compile-target ((obj ede-generic-target) &optional command)
|
||
"Compile the current target OBJ.
|
||
Argument COMMAND is the command to use for compiling the target."
|
||
(project-compile-project (ede-current-project) command))
|
||
|
||
(defmethod project-debug-target ((target ede-generic-target))
|
||
"Run the current project derived from TARGET in a debugger."
|
||
(let* ((proj (ede-target-parent target))
|
||
(config (ede-generic-get-configuration proj))
|
||
(debug (oref config :debug-command))
|
||
(cmd (read-from-minibuffer
|
||
"Debug Command: "
|
||
debug))
|
||
(cmdsplit (split-string cmd " " t))
|
||
;; @TODO - this depends on the user always typing in something good
|
||
;; like "gdb" or "dbx" which also exists as a useful Emacs command.
|
||
;; Is there a better way?
|
||
(cmdsym (intern-soft (car cmdsplit))))
|
||
(call-interactively cmdsym t)))
|
||
|
||
(defmethod project-run-target ((target ede-generic-target))
|
||
"Run the current project derived from TARGET."
|
||
(let* ((proj (ede-target-parent target))
|
||
(config (ede-generic-get-configuration proj))
|
||
(run (concat "./" (oref config :run-command)))
|
||
(cmd (read-from-minibuffer "Run (like this): " run)))
|
||
(ede-shell-run-something target cmd)))
|
||
|
||
;;; Customization
|
||
;;
|
||
(defmethod ede-customize ((proj ede-generic-project))
|
||
"Customize the EDE project PROJ."
|
||
(let ((config (ede-generic-get-configuration proj)))
|
||
(eieio-customize-object config)))
|
||
|
||
(defmethod ede-customize ((target ede-generic-target))
|
||
"Customize the EDE TARGET."
|
||
;; Nothing unique for the targets, use the project.
|
||
(ede-customize-project))
|
||
|
||
(defmethod eieio-done-customizing ((config ede-generic-config))
|
||
"Called when EIEIO is done customizing the configuration object.
|
||
We need to go back through the old buffers, and update them with
|
||
the new configuration."
|
||
(ede-commit config)
|
||
;; Loop over all the open buffers, and re-apply.
|
||
(ede-map-targets
|
||
(oref config project)
|
||
(lambda (target)
|
||
(ede-map-target-buffers
|
||
target
|
||
(lambda (b)
|
||
(with-current-buffer b
|
||
(ede-apply-target-options)))))))
|
||
|
||
(defmethod ede-commit ((config ede-generic-config))
|
||
"Commit all changes to the configuration to disk."
|
||
(eieio-persistent-save config))
|
||
|
||
;;; Creating Derived Projects:
|
||
;;
|
||
;; Derived projects need an autoloader so that EDE can find the
|
||
;; different projects on disk.
|
||
(defun ede-generic-new-autoloader (internal-name external-name
|
||
projectfile class)
|
||
"Add a new EDE Autoload instance for identifying a generic project.
|
||
INTERNAL-NAME is a long name that identifies this project type.
|
||
EXTERNAL-NAME is a shorter human readable name to describe the project.
|
||
PROJECTFILE is a file name that identifies a project of this type to EDE, such as
|
||
a Makefile, or SConstruct file.
|
||
CLASS is the EIEIO class that is used to track this project. It should subclass
|
||
the class `ede-generic-project' project."
|
||
(ede-add-project-autoload
|
||
(ede-project-autoload internal-name
|
||
:name external-name
|
||
:file 'ede/generic
|
||
:proj-file projectfile
|
||
:load-type 'ede-generic-load
|
||
:class-sym class
|
||
:new-p nil
|
||
:safe-p nil) ; @todo - could be
|
||
; safe if we do something
|
||
; about the loading of the
|
||
; generic config file.
|
||
;; Generics must go at the end, since more specific types
|
||
;; can create Makefiles also.
|
||
'generic))
|
||
|
||
;;;###autoload
|
||
(defun ede-enable-generic-projects ()
|
||
"Enable generic project loaders."
|
||
(interactive)
|
||
(ede-generic-new-autoloader "generic-makefile" "Make"
|
||
"Makefile" 'ede-generic-makefile-project)
|
||
(ede-generic-new-autoloader "generic-scons" "SCons"
|
||
"SConstruct" 'ede-generic-scons-project)
|
||
(ede-generic-new-autoloader "generic-cmake" "CMake"
|
||
"CMakeLists" 'ede-generic-cmake-project)
|
||
)
|
||
|
||
|
||
;;; SPECIFIC TYPES OF GENERIC BUILDS
|
||
;;
|
||
|
||
;;; MAKEFILE
|
||
|
||
(defclass ede-generic-makefile-project (ede-generic-project)
|
||
((buildfile :initform "Makefile")
|
||
)
|
||
"Generic Project for makefiles.")
|
||
|
||
(defmethod ede-generic-setup-configuration ((proj ede-generic-makefile-project) config)
|
||
"Setup a configuration for Make."
|
||
(oset config build-command "make -k")
|
||
(oset config debug-command "gdb ")
|
||
)
|
||
|
||
|
||
;;; SCONS
|
||
(defclass ede-generic-scons-project (ede-generic-project)
|
||
((buildfile :initform "SConstruct")
|
||
)
|
||
"Generic Project for scons.")
|
||
|
||
(defmethod ede-generic-setup-configuration ((proj ede-generic-scons-project) config)
|
||
"Setup a configuration for SCONS."
|
||
(oset config build-command "scons")
|
||
(oset config debug-command "gdb ")
|
||
)
|
||
|
||
|
||
;;; CMAKE
|
||
(defclass ede-generic-cmake-project (ede-generic-project)
|
||
((buildfile :initform "CMakeLists")
|
||
)
|
||
"Generic Project for cmake.")
|
||
|
||
(defmethod ede-generic-setup-configuration ((proj ede-generic-cmake-project) config)
|
||
"Setup a configuration for CMake."
|
||
(oset config build-command "cmake")
|
||
(oset config debug-command "gdb ")
|
||
)
|
||
|
||
(provide 'ede/generic)
|
||
|
||
;; Local variables:
|
||
;; generated-autoload-file: "loaddefs.el"
|
||
;; generated-autoload-load-name: "ede/generic"
|
||
;; End:
|
||
|
||
;;; ede/generic.el ends here
|