Initial Commit
[packages] / xemacs-packages / prog-modes / rpm-spec-mode.el
1 ;;; rpm-spec-mode.el --- RPM spec file editing commands for Emacs/XEmacs
2
3 ;; Copyright (C) 1997-2002 Stig Bjørlykke, <stigb@tihlde.org>
4
5 ;; Author:   Stig Bjørlykke, <stigb@tihlde.org>
6 ;; Keywords: unix, languages, rpm
7 ;; Version:  0.12x
8
9 ;; This file is part of XEmacs.
10
11 ;; XEmacs is free software; you can redistribute it and/or modify
12 ;; it under the terms of the GNU General Public License as published by
13 ;; the Free Software Foundation; either version 2, or (at your option)
14 ;; any later version.
15
16 ;; XEmacs is distributed in the hope that it will be useful,
17 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19 ;; General Public License for more details.
20
21 ;; You should have received a copy of the GNU General Public License
22 ;; along with XEmacs; see the file COPYING.  If not, write to the
23 ;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
24 ;; Boston, MA 02110-1301 USA.
25
26 ;;; Synched up with: not in GNU Emacs.
27
28 ;;; Thanx to:
29
30 ;;     Tore Olsen <toreo@tihlde.org> for some general fixes.
31 ;;     Steve Sanbeg <sanbeg@dset.com> for navigation functions and
32 ;;          some Emacs fixes.
33 ;;     Tim Powers <timp@redhat.com> and Trond Eivind Glomsrød
34 ;;          <teg@redhat.com> for Red Hat adaptions and some fixes.
35 ;;     Chmouel Boudjnah <chmouel@mandrakesoft.com> for Mandrake fixes.
36 ;;     Ville Skyttä  <scop@xemacs.org> for some fixes.
37
38 ;;; ToDo:
39
40 ;; - rewrite function names.
41 ;; - autofill changelog entries.
42 ;; - customize rpm-tags-list, rpm-obsolete-tags-list and rpm-group-tags-list.
43 ;; - get values from `rpm --showrc'.
44 ;; - ssh/rsh for compile.
45 ;; - finish integrating the new navigation functions in with existing stuff.
46 ;; - use a single prefix consistently (internal)
47
48 ;;; Commentary:
49
50 ;; This mode is used for editing spec files used for building RPM packages.
51 ;;
52 ;; Most recent version is available from:
53 ;;  <URL:http://www.tihlde.org/~stigb/rpm-spec-mode.el>
54 ;;
55 ;; Put this in your .emacs file to enable autoloading of rpm-spec-mode,
56 ;; and auto-recognition of ".spec" files:
57 ;;
58 ;;  (autoload 'rpm-spec-mode "rpm-spec-mode.el" "RPM spec mode." t)
59 ;;  (setq auto-mode-alist (append '(("\\.spec" . rpm-spec-mode))
60 ;;                                auto-mode-alist))
61 ;;------------------------------------------------------------
62 ;;
63
64 ;;; Code:
65
66 (defconst rpm-spec-mode-version "0.12.3x" "Version of `rpm-spec-mode'.")
67
68 (defgroup rpm-spec nil
69   "RPM spec mode with Emacs/XEmacs enhancements."
70   :prefix "rpm-spec-"
71   :group 'languages)
72
73 (defcustom rpm-spec-build-command "rpmbuild"
74   "Command for building an RPM package."
75   :type 'string
76   :group 'rpm-spec)
77
78 (defcustom rpm-spec-add-attr nil
79   "Add \"%attr\" entry for file listings or not."
80   :type 'boolean
81   :group 'rpm-spec)
82
83 (defcustom rpm-spec-short-circuit nil
84   "Skip straight to specified stage.
85 (ie, skip all stages leading up to the specified stage).  Only valid
86 in \"%build\" and \"%install\" stage."
87   :type 'boolean
88   :group 'rpm-spec)
89
90 (defcustom rpm-spec-timecheck "0"
91   "Set the \"timecheck\" age (0 to disable).
92 The timecheck value expresses, in seconds, the maximum age of a file
93 being packaged.  Warnings will be printed for all files beyond the
94 timecheck age."
95   :type 'integer
96   :group 'rpm-spec)
97
98 (defcustom rpm-spec-buildroot ""
99   "When building, override the BuildRoot tag with directory <dir>."
100   :type 'string
101   :group 'rpm-spec)
102
103 (defcustom rpm-spec-target ""
104   "Interpret given string as `arch-vendor-os'.
105 Set the macros _target, _target_arch and _target_os accordingly"
106   :type 'string
107   :group 'rpm-spec)
108
109 (define-obsolete-variable-alias
110   'rpm-completion-ignore-case 'rpm-spec-completion-ignore-case)
111
112 (defcustom rpm-spec-completion-ignore-case t
113   "*Non-nil means that case differences are ignored during completion.
114 A value of nil means that case is significant.
115 This is used during Tempo template completion."
116   :type 'boolean
117   :group 'rpm-spec)
118
119 (defcustom rpm-spec-clean nil
120   "Remove the build tree after the packages are made."
121   :type 'boolean
122   :group 'rpm-spec)
123
124 (defcustom rpm-spec-rmsource nil
125   "Remove the source and spec file after the packages are made."
126   :type 'boolean
127   :group 'rpm-spec)
128
129 (define-obsolete-variable-alias
130   'rpm-spec-test 'rpm-spec-nobuild)
131
132 (defcustom rpm-spec-nobuild nil
133   "Do not execute any build stages.  Useful for testing out spec files."
134   :type 'boolean
135   :group 'rpm-spec)
136
137 (defcustom rpm-spec-quiet nil
138   "Print as little as possible.
139 Normally only error messages will be displayed."
140   :type 'boolean
141   :group 'rpm-spec)
142
143 (defcustom rpm-spec-sign-gpg nil
144   "Embed a GPG signature in the package.
145 This signature can be used to verify the integrity and the origin of
146 the package."
147   :type 'boolean
148   :group 'rpm-spec)
149
150 (defcustom rpm-spec-nodeps nil
151   "Do not verify build dependencies."
152   :type 'boolean
153   :group 'rpm-spec)
154
155 (define-obsolete-variable-alias
156   'rpm-initialize-sections 'rpm-spec-initialize-sections)
157
158 (defcustom rpm-spec-initialize-sections t
159   "Automatically add empty section headings to new spec files."
160   :type 'boolean
161   :group 'rpm-spec)
162
163 (define-obsolete-variable-alias
164   'rpm-insert-version 'rpm-spec-insert-changelog-version)
165
166 (defcustom rpm-spec-insert-changelog-version t
167   "Automatically add version in a new change log entry."
168   :type 'boolean
169   :group 'rpm-spec)
170
171 (defcustom rpm-spec-user-full-name nil
172   "*Full name of the user.
173 This is used in the change log and the Packager tag.  It defaults to the
174 value returned by function `user-full-name'."
175   :type '(choice (const :tag "Use `user-full-name'" nil)
176                  string)
177   :group 'rpm-spec)
178
179 (defcustom rpm-spec-user-mail-address nil
180   "*Email address of the user.
181 This is used in the change log and the Packager tag.  It defaults to the
182 value returned by function `user-mail-address'."
183   :type '(choice (const :tag "Use `user-mail-address'" nil)
184                  string)
185   :group 'rpm-spec)
186
187 (defcustom rpm-spec-indent-heading-values nil
188   "*Indent values for all tags in the \"heading\" of the spec file."
189   :type 'boolean
190   :group 'rpm-spec)
191
192 (defcustom rpm-spec-use-compilation-mode t
193   "*If non-nil, build in `compilation-mode' if it's available."
194   :type 'boolean
195   :group 'rpm-spec)
196
197 (defcustom rpm-spec-default-release "1"
198   "*Default value for the Release tag in new spec files."
199   :type 'string
200   :group 'rpm-spec)
201
202 (defcustom rpm-spec-default-epoch nil
203   "*If non-nil, default value for the Epoch tag in new spec files."
204   :type '(choice (const :tag "No Epoch" nil) integer)
205   :group 'rpm-spec)
206
207 (defcustom rpm-spec-default-buildroot
208   "%{_tmppath}/%{name}-%{version}-%{release}-root"
209   "*Default value for the BuildRoot tag in new spec files."
210   :type 'integer
211   :group 'rpm-spec)
212
213 (defcustom rpm-spec-default-build-section ""
214   "*Default %build section in new spec files."
215   :type 'string
216   :group 'rpm-spec)
217
218 (defcustom rpm-spec-default-install-section "rm -rf $RPM_BUILD_ROOT\n"
219   "*Default %install section in new spec files."
220   :type 'string
221   :group 'rpm-spec)
222
223 (defcustom rpm-spec-default-clean-section "rm -rf $RPM_BUILD_ROOT\n"
224   "*Default %clean section in new spec files."
225   :type 'string
226   :group 'rpm-spec)
227
228 (defgroup rpm-spec-faces nil
229   "Font lock faces for `rpm-spec-mode'."
230   :prefix "rpm-spec-"
231   :group 'rpm-spec
232   :group 'faces)
233
234 ;;------------------------------------------------------------
235 ;; variables used by navigation functions.
236
237 (defconst rpm-sections
238   '("preamble" "description" "prep" "setup" "build" "install" "check" "clean"
239     "changelog" "files")
240   "Partial list of section names.")
241 (defvar rpm-section-list
242   '(("preamble") ("description") ("prep") ("setup") ("build") ("install")
243     ("check") ("clean") ("changelog") ("files"))
244   "Partial list of section names.")
245 (defconst rpm-scripts
246   '("pre" "post" "preun" "postun"
247     "trigger" "triggerin" "triggerprein" "triggerun" "triggerpostun"
248     "pretrans" "posttrans")
249   "List of rpm scripts.")
250 (defconst rpm-section-seperate "^%\\(\\w+\\)\\s-")
251 (defconst rpm-section-regexp
252   (eval-when-compile
253     (concat "^%"
254             (regexp-opt
255              ;; From RPM 4.6.0 sources, file build/parseSpec.c: partList[].
256              '("build" "changelog" "check" "clean" "description" "files"
257                "install" "package" "post" "postun" "pretrans" "posttrans"
258                "pre" "prep" "preun" "trigger" "triggerin" "triggerpostun"
259                "triggerprein" "triggerun" "verifyscript") t)
260             "\\b"))
261   "Regular expression to match beginning of a section.")
262
263 ;;------------------------------------------------------------
264
265 (defface rpm-spec-tag-face
266   '(( ((class color) (background light)) (:foreground "blue3") )
267     ( ((class color) (background dark)) (:foreground "blue") ))
268   "*Face for tags."
269   :group 'rpm-spec-faces)
270
271 (defface rpm-spec-obsolete-tag-face
272   '(( ((class color)) (:foreground "white" :background "red") ))
273   "*Face for obsolete tags."
274   :group 'rpm-spec-faces)
275
276 (defface rpm-spec-macro-face
277   '(( ((class color) (background light)) (:foreground "purple") )
278     ( ((class color) (background dark)) (:foreground "yellow") ))
279   "*Face for RPM macros and variables."
280   :group 'rpm-spec-faces)
281
282 (defface rpm-spec-var-face
283   '(( ((class color) (background light)) (:foreground "maroon") )
284     ( ((class color) (background dark)) (:foreground "maroon") ))
285   "*Face for environment variables."
286   :group 'rpm-spec-faces)
287
288 (defface rpm-spec-doc-face
289   '(( ((class color) (background light)) (:foreground "magenta3") )
290     ( ((class color) (background dark)) (:foreground "magenta") ))
291   "*Face for %doc entries in %files."
292   :group 'rpm-spec-faces)
293
294 (defface rpm-spec-dir-face
295   '(( ((class color) (background light)) (:foreground "green4") )
296     ( ((class color) (background dark)) (:foreground "green") ))
297   "*Face for %dir entries in %files."
298   :group 'rpm-spec-faces)
299
300 (defface rpm-spec-package-face
301   '(( ((class color) (background light)) (:foreground "red3") )
302     ( ((class color) (background dark)) (:foreground "red") ))
303   "*Face for package tag."
304   :group 'rpm-spec-faces)
305
306 (defface rpm-spec-ghost-face
307   '(( ((class color) (background light)) (:foreground "gray50") )
308     ( ((class color) (background dark)) (:foreground "red") ))
309   "*Face for %ghost and %config entries in %files."
310   :group 'rpm-spec-faces)
311
312 (defface rpm-spec-section-face
313   '(( ((class color) (background light)) (:foreground "purple" :underline t) )
314     ( ((class color) (background dark)) (:foreground "yellow" :underline t) ))
315   "*Face for section markers."
316   :group 'rpm-spec-faces)
317
318 ;;; GNU emacs font-lock needs these...
319 (defvar rpm-spec-macro-face
320   'rpm-spec-macro-face "*Face for RPM macros and variables.")
321 (defvar rpm-spec-var-face
322   'rpm-spec-var-face "*Face for environment variables.")
323 (defvar rpm-spec-tag-face
324   'rpm-spec-tag-face "*Face for tags.")
325 (defvar rpm-spec-obsolete-tag-face
326   'rpm-spec-tag-face "*Face for obsolete tags.")
327 (defvar rpm-spec-package-face
328   'rpm-spec-package-face "*Face for package tag.")
329 (defvar rpm-spec-dir-face
330   'rpm-spec-dir-face "*Face for %dir entries in %files.")
331 (defvar rpm-spec-doc-face
332   'rpm-spec-doc-face "*Face for %doc entries in %files.")
333 (defvar rpm-spec-ghost-face
334   'rpm-spec-ghost-face "*Face for %ghost and %config entries in %files.")
335 (defvar rpm-spec-section-face
336   'rpm-spec-section-face "*Face for section markers.")
337
338 (defvar rpm-default-umask "-"
339   "*Default umask for files, specified with \"%attr\".")
340 (defvar rpm-default-owner "root"
341   "*Default owner for files, specified with \"%attr\".")
342 (defvar rpm-default-group "root"
343   "*Default group for files, specified with \"%attr\".")
344
345 ;;------------------------------------------------------------
346
347 (defvar rpm-no-gpg nil "Tell rpm not to sign package.")
348 (defvar rpm-spec-nobuild-option "--nobuild" "Option for no build.")
349
350 (defvar rpm-tags-list
351   ;; From RPM 4.4.9 sources, file build/parsePreamble.c: preambleList[], and
352   ;; a few macros that aren't tags, but useful here.
353   '(("AutoProv")
354     ("AutoReq")
355     ("AutoReqProv")
356     ("BuildArch")
357     ("BuildArchitectures")
358     ("BuildConflicts")
359     ("BuildEnhances")
360     ("BuildPlatforms")
361     ("BuildPreReq")
362     ("BuildRequires")
363     ("BuildRoot")
364     ("BuildSuggests")
365     ("Conflicts")
366     ("CVSId")
367     ("%description")
368     ("Distribution")
369     ("DistTag")
370     ("DistURL")
371     ("DocDir")
372     ("Enhances")
373     ("Epoch")
374     ("ExcludeArch")
375     ("ExcludeOS")
376     ("ExclusiveArch")
377     ("ExclusiveOS")
378     ("%files")
379     ("Group")
380     ("Icon")
381     ("%ifarch")
382     ("Keyword")
383     ("Keywords")
384     ("License")
385     ("Name")
386     ("NoPatch")
387     ("NoSource")
388     ("Obsoletes")
389     ("%package")
390     ("Packager")
391     ("Patch")
392     ("Prefix")
393     ("Prefixes")
394     ("PreReq")
395     ("Provides")
396     ("Release")
397     ("Requires")
398     ("RepoTag")
399     ("Source")
400     ("Suggests")
401     ("Summary")
402     ("SVNId")
403     ("URL")
404     ("Variant")
405     ("Variants")
406     ("Vendor")
407     ("Version")
408     ("XMajor")
409     ("XMinor")
410     )
411   "List of elements that are valid tags.")
412
413 (defvar rpm-tags-regexp
414   (concat "\\(\\<" (regexp-opt (mapcar 'car rpm-tags-list))
415           "\\|\\(Patch\\|Source\\)[0-9]+\\>\\)")
416   "Regular expression for matching valid tags.")
417
418 (defvar rpm-obsolete-tags-list
419   ;; From RPM sources, file build/parsePreamble.c: preambleList[].
420   '(("Copyright")    ;; 4.4.2
421     ("RHNPlatform")  ;; 4.4.2, 4.4.9
422     ("Serial")       ;; 4.4.2, 4.4.9
423     )
424   "List of elements that are obsolete tags in some versions of rpm.")
425
426 (defvar rpm-obsolete-tags-regexp
427   (regexp-opt (mapcar 'car rpm-obsolete-tags-list) 'words)
428   "Regular expression for matching obsolete tags.")
429
430 (defvar rpm-group-tags-list
431   ;; From RPM 4.4.9 sources, file GROUPS.
432   '(("Amusements/Games")
433     ("Amusements/Graphics")
434     ("Applications/Archiving")
435     ("Applications/Communications")
436     ("Applications/Databases")
437     ("Applications/Editors")
438     ("Applications/Emulators")
439     ("Applications/Engineering")
440     ("Applications/File")
441     ("Applications/Internet")
442     ("Applications/Multimedia")
443     ("Applications/Productivity")
444     ("Applications/Publishing")
445     ("Applications/System")
446     ("Applications/Text")
447     ("Development/Debuggers")
448     ("Development/Languages")
449     ("Development/Libraries")
450     ("Development/System")
451     ("Development/Tools")
452     ("Documentation")
453     ("System Environment/Base")
454     ("System Environment/Daemons")
455     ("System Environment/Kernel")
456     ("System Environment/Libraries")
457     ("System Environment/Shells")
458     ("User Interface/Desktops")
459     ("User Interface/X")
460     ("User Interface/X Hardware Support")
461     )
462   "List of elements that are valid group tags.")
463
464 (defvar rpm-spec-mode-syntax-table nil
465   "Syntax table in use in `rpm-spec-mode' buffers.")
466 (unless rpm-spec-mode-syntax-table
467   (setq rpm-spec-mode-syntax-table (make-syntax-table))
468   (modify-syntax-entry ?\\ "\\" rpm-spec-mode-syntax-table)
469   (modify-syntax-entry ?\n ">   " rpm-spec-mode-syntax-table)
470   (modify-syntax-entry ?\f ">   " rpm-spec-mode-syntax-table)
471   (modify-syntax-entry ?\# "<   " rpm-spec-mode-syntax-table)
472   (modify-syntax-entry ?/ "." rpm-spec-mode-syntax-table)
473   (modify-syntax-entry ?* "." rpm-spec-mode-syntax-table)
474   (modify-syntax-entry ?+ "." rpm-spec-mode-syntax-table)
475   (modify-syntax-entry ?- "." rpm-spec-mode-syntax-table)
476   (modify-syntax-entry ?= "." rpm-spec-mode-syntax-table)
477   (modify-syntax-entry ?% "_" rpm-spec-mode-syntax-table)
478   (modify-syntax-entry ?< "." rpm-spec-mode-syntax-table)
479   (modify-syntax-entry ?> "." rpm-spec-mode-syntax-table)
480   (modify-syntax-entry ?& "." rpm-spec-mode-syntax-table)
481   (modify-syntax-entry ?| "." rpm-spec-mode-syntax-table)
482   (modify-syntax-entry ?\' "." rpm-spec-mode-syntax-table))
483
484 (defvar rpm-spec-mode-map nil
485   "Keymap used in `rpm-spec-mode'.")
486 (unless rpm-spec-mode-map
487   (setq rpm-spec-mode-map (make-sparse-keymap))
488   (and (functionp 'set-keymap-name)
489        (set-keymap-name rpm-spec-mode-map 'rpm-spec-mode-map))
490   (define-key rpm-spec-mode-map "\C-c\C-c"  'rpm-change-tag)
491   (define-key rpm-spec-mode-map "\C-c\C-e"  'rpm-add-change-log-entry)
492   (define-key rpm-spec-mode-map "\C-c\C-i"  'rpm-insert-tag)
493   (define-key rpm-spec-mode-map "\C-c\C-n"  'rpm-forward-section)
494   (define-key rpm-spec-mode-map "\C-c\C-o"  'rpm-goto-section)
495   (define-key rpm-spec-mode-map "\C-c\C-p"  'rpm-backward-section)
496   (define-key rpm-spec-mode-map "\C-c\C-r"  'rpm-increase-release-tag)
497   (define-key rpm-spec-mode-map "\C-c\C-u"  'rpm-insert-true-prefix)
498   (define-key rpm-spec-mode-map "\C-c\C-ba" 'rpm-build-all)
499   (define-key rpm-spec-mode-map "\C-c\C-bb" 'rpm-build-binary)
500   (define-key rpm-spec-mode-map "\C-c\C-bc" 'rpm-build-compile)
501   (define-key rpm-spec-mode-map "\C-c\C-bi" 'rpm-build-install)
502   (define-key rpm-spec-mode-map "\C-c\C-bl" 'rpm-list-check)
503   (define-key rpm-spec-mode-map "\C-c\C-bp" 'rpm-build-prepare)
504   (define-key rpm-spec-mode-map "\C-c\C-bs" 'rpm-build-source)
505   (define-key rpm-spec-mode-map "\C-c\C-dd" 'rpm-insert-dir)
506   (define-key rpm-spec-mode-map "\C-c\C-do" 'rpm-insert-docdir)
507   (define-key rpm-spec-mode-map "\C-c\C-fc" 'rpm-insert-config)
508   (define-key rpm-spec-mode-map "\C-c\C-fd" 'rpm-insert-doc)
509   (define-key rpm-spec-mode-map "\C-c\C-ff" 'rpm-insert-file)
510   (define-key rpm-spec-mode-map "\C-c\C-fg" 'rpm-insert-ghost)
511   (define-key rpm-spec-mode-map "\C-c\C-xa" 'rpm-toggle-add-attr)
512   (define-key rpm-spec-mode-map "\C-c\C-xb" 'rpm-change-buildroot-option)
513   (define-key rpm-spec-mode-map "\C-c\C-xc" 'rpm-toggle-clean)
514   (define-key rpm-spec-mode-map "\C-c\C-xd" 'rpm-toggle-nodeps)
515   (define-key rpm-spec-mode-map "\C-c\C-xf" 'rpm-files-group)
516   (define-key rpm-spec-mode-map "\C-c\C-xg" 'rpm-toggle-sign-gpg)
517   (define-key rpm-spec-mode-map "\C-c\C-xi" 'rpm-change-timecheck-option)
518   (define-key rpm-spec-mode-map "\C-c\C-xn" 'rpm-toggle-nobuild)
519   (define-key rpm-spec-mode-map "\C-c\C-xo" 'rpm-files-owner)
520   (define-key rpm-spec-mode-map "\C-c\C-xr" 'rpm-toggle-rmsource)
521   (define-key rpm-spec-mode-map "\C-c\C-xq" 'rpm-toggle-quiet)
522   (define-key rpm-spec-mode-map "\C-c\C-xs" 'rpm-toggle-short-circuit)
523   (define-key rpm-spec-mode-map "\C-c\C-xt" 'rpm-change-target-option)
524   (define-key rpm-spec-mode-map "\C-c\C-xu" 'rpm-files-umask)
525   ;;(define-key rpm-spec-mode-map "\C-q" 'indent-spec-exp)
526   ;;(define-key rpm-spec-mode-map "\t" 'sh-indent-line)
527   )
528
529 (defconst rpm-spec-mode-menu
530   (purecopy '("RPM spec"
531               ["Insert Tag..."           rpm-insert-tag                t]
532               ["Change Tag..."           rpm-change-tag                t]
533               "---"
534               ["Go to section..."        rpm-mouse-goto-section  :keys "C-c C-o"]
535               ["Forward section"         rpm-forward-section           t]
536               ["Backward section"        rpm-backward-section          t]
537               "---"
538               ["Add change log entry..." rpm-add-change-log-entry      t]
539               ["Increase release tag"    rpm-increase-release-tag      t]
540               "---"
541               ("Add file entry"
542                ["Regular file..."        rpm-insert-file               t]
543                ["Config file..."         rpm-insert-config             t]
544                ["Document file..."       rpm-insert-doc                t]
545                ["Ghost file..."          rpm-insert-ghost              t]
546                "---"
547                ["Directory..."           rpm-insert-dir                t]
548                ["Document directory..."  rpm-insert-docdir             t]
549                "---"
550                ["Insert %{prefix}"       rpm-insert-true-prefix        t]
551                "---"
552                ["Default add \"%attr\" entry" rpm-toggle-add-attr
553                 :style toggle :selected rpm-spec-add-attr]
554                ["Change default umask for files..."  rpm-files-umask   t]
555                ["Change default owner for files..."  rpm-files-owner   t]
556                ["Change default group for files..."  rpm-files-group   t])
557               ("Build Options"
558                ["Short circuit" rpm-toggle-short-circuit
559                 :style toggle :selected rpm-spec-short-circuit]
560                ["Remove source" rpm-toggle-rmsource
561                 :style toggle :selected rpm-spec-rmsource]
562                ["Clean"         rpm-toggle-clean
563                 :style toggle :selected rpm-spec-clean]
564                ["No build"      rpm-toggle-nobuild
565                 :style toggle :selected rpm-spec-nobuild]
566                ["Quiet"         rpm-toggle-quiet
567                 :style toggle :selected rpm-spec-quiet]
568                ["GPG sign"      rpm-toggle-sign-gpg
569                 :style toggle :selected rpm-spec-sign-gpg]
570                ["Ignore dependencies" rpm-toggle-nodeps
571                 :style toggle :selected rpm-spec-nodeps]
572                "---"
573                ["Change timecheck value..."  rpm-change-timecheck-option   t]
574                ["Change buildroot value..."  rpm-change-buildroot-option   t]
575                ["Change target value..."     rpm-change-target-option      t])
576               ("RPM Build"
577                ["Execute \"%prep\" stage"    rpm-build-prepare             t]
578                ["Do a \"list check\""        rpm-list-check                t]
579                ["Do the \"%build\" stage"    rpm-build-compile             t]
580                ["Do the \"%install\" stage"  rpm-build-install             t]
581                "---"
582                ["Build binary package"       rpm-build-binary              t]
583                ["Build source package"       rpm-build-source              t]
584                ["Build binary and source"    rpm-build-all                 t])
585               "---"
586               ["About rpm-spec-mode"         rpm-about-rpm-spec-mode       t]
587               )))
588
589 (defvar rpm-spec-font-lock-keywords
590   (list
591    (cons rpm-section-regexp rpm-spec-section-face)
592    '("%[a-zA-Z0-9_]+" 0 rpm-spec-macro-face)
593    (cons (concat "^" rpm-obsolete-tags-regexp "\\(\([a-zA-Z0-9,_]+\)\\)[ \t]*:")
594          '((1 'rpm-spec-obsolete-tag-face)
595            (2 'rpm-spec-ghost-face)))
596    (cons (concat "^" rpm-tags-regexp "\\(\([a-zA-Z0-9,_]+\)\\)[ \t]*:")
597          '((1 'rpm-spec-tag-face)
598            (3 'rpm-spec-ghost-face)))
599    (cons (concat "^" rpm-obsolete-tags-regexp "[ \t]*:")
600          '(1 'rpm-spec-obsolete-tag-face))
601    (cons (concat "^" rpm-tags-regexp "[ \t]*:")
602          '(1 'rpm-spec-tag-face))
603    '("%\\(de\\(fine\\|scription\\)\\|files\\|global\\|package\\)[ \t]+\\([^-][^ \t\n]*\\)"
604      (3 rpm-spec-package-face))
605    '("%p\\(ost\\|re\\)\\(un\\|trans\\)?[ \t]+\\([^-][^ \t\n]*\\)"
606      (3 rpm-spec-package-face))
607    '("%configure " 0 rpm-spec-macro-face)
608    '("%dir[ \t]+\\([^ \t\n]+\\)[ \t]*" 1 rpm-spec-dir-face)
609    '("%doc\\(dir\\)?[ \t]+\\(.*\\)\n" 2 rpm-spec-doc-face)
610    '("%\\(ghost\\|config\\([ \t]*(.*)\\)?\\)[ \t]+\\(.*\\)\n"
611      3 rpm-spec-ghost-face)
612    '("^%.+-[a-zA-Z][ \t]+\\([a-zA-Z0-9\.-]+\\)" 1 rpm-spec-doc-face)
613    '("^\\(.+\\)(\\([a-zA-Z]\\{2,2\\}\\)):"
614      (1 rpm-spec-tag-face)
615      (2 rpm-spec-doc-face))
616    '("^\\*\\(.*[0-9] \\)\\(.*\\)<\\(.*\\)>\\(.*\\)\n"
617      (1 rpm-spec-dir-face)
618      (2 rpm-spec-package-face)
619      (3 rpm-spec-tag-face)
620      (4 rpm-spec-ghost-face))
621    '("%{[^{}]*}" 0 rpm-spec-macro-face)
622    '("$[a-zA-Z0-9_]+" 0 rpm-spec-var-face)
623    '("${[a-zA-Z0-9_]+}" 0 rpm-spec-var-face)
624    )
625   "Additional expressions to highlight in `rpm-spec-mode'.")
626
627 ;;Initialize font lock for xemacs
628 (put 'rpm-spec-mode 'font-lock-defaults '(rpm-spec-font-lock-keywords))
629
630 (defvar rpm-spec-mode-abbrev-table nil
631   "Abbrev table in use in `rpm-spec-mode' buffers.")
632 (define-abbrev-table 'rpm-spec-mode-abbrev-table ())
633
634 ;;------------------------------------------------------------
635
636 (add-hook 'rpm-spec-mode-new-file-hook 'rpm-spec-initialize)
637
638 ;;;###autoload
639 (defun rpm-spec-mode ()
640   "Major mode for editing RPM spec files.
641 This is much like C mode except for the syntax of comments.  It uses
642 the same keymap as C mode and has the same variables for customizing
643 indentation.  It has its own abbrev table and its own syntax table.
644
645 Turning on RPM spec mode calls the value of the variable `rpm-spec-mode-hook'
646 with no args, if that value is non-nil."
647   (interactive)
648   (kill-all-local-variables)
649   (condition-case nil
650       (require 'shindent)
651     (error
652      (require 'sh-script)))
653   (require 'cc-mode)
654   (use-local-map rpm-spec-mode-map)
655   (setq major-mode 'rpm-spec-mode)
656   (rpm-update-mode-name)
657   (setq local-abbrev-table rpm-spec-mode-abbrev-table)
658   (set-syntax-table rpm-spec-mode-syntax-table)
659
660   (require 'easymenu)
661   (easy-menu-define rpm-spec-call-menu rpm-spec-mode-map
662                     "Post menu for `rpm-spec-mode'." rpm-spec-mode-menu)
663   (easy-menu-add rpm-spec-mode-menu)
664
665   (if (and (= (buffer-size) 0) rpm-spec-initialize-sections)
666       (run-hooks 'rpm-spec-mode-new-file-hook))
667
668   (if (not (executable-find "rpmbuild"))
669       (progn
670         (setq rpm-spec-build-command "rpm")
671         (setq rpm-spec-nobuild-option "--test")))
672   
673   (make-local-variable 'paragraph-start)
674   (setq paragraph-start (concat "$\\|" page-delimiter))
675   (make-local-variable 'paragraph-separate)
676   (setq paragraph-separate paragraph-start)
677   (make-local-variable 'paragraph-ignore-fill-prefix)
678   (setq paragraph-ignore-fill-prefix t)
679 ;  (make-local-variable 'indent-line-function)
680 ;  (setq indent-line-function 'c-indent-line)
681   (make-local-variable 'require-final-newline)
682   (setq require-final-newline t)
683   (make-local-variable 'comment-start)
684   (setq comment-start "# ")
685   (make-local-variable 'comment-end)
686   (setq comment-end "")
687   (make-local-variable 'comment-column)
688   (setq comment-column 32)
689   (make-local-variable 'comment-start-skip)
690   (setq comment-start-skip "#+ *")
691 ;  (make-local-variable 'comment-indent-function)
692 ;  (setq comment-indent-function 'c-comment-indent)
693   ;;Initialize font lock for GNU emacs.
694   (make-local-variable 'font-lock-defaults)
695   (setq font-lock-defaults '(rpm-spec-font-lock-keywords nil t))
696   (run-hooks 'rpm-spec-mode-hook))
697
698 (defun rpm-command-filter (process string)
699   "Filter to process normal output."
700   (save-excursion
701     (set-buffer (process-buffer process))
702     (save-excursion
703       (goto-char (process-mark process))
704       (insert-before-markers string)
705       (set-marker (process-mark process) (point)))))
706
707 ;;------------------------------------------------------------
708
709 (defun rpm-add-change-log-entry (&optional change-log-entry)
710   "Find change log and add an entry for today."
711   (interactive "sChange log entry: ")
712   (save-excursion
713     (rpm-goto-section "changelog")
714     (let* ((address (rpm-spec-user-mail-address))
715            (fullname (or rpm-spec-user-full-name (user-full-name)))
716            (string (concat "* " (substring (current-time-string) 0 11)
717                            (substring (current-time-string) -4) " "
718                            fullname " <" address ">"
719                            (and rpm-spec-insert-changelog-version
720                                 (concat " - " (rpm-find-spec-version t))))))
721       (if (not (search-forward string nil t))
722           (insert "\n" string "\n")
723         (forward-line 2))
724       (insert "- " change-log-entry "\n"))))
725
726 ;;------------------------------------------------------------
727
728 (defun rpm-insert-f (&optional filetype filename)
729   "Insert new \"%files\" entry."
730   (save-excursion
731     (and (rpm-goto-section "files") (rpm-end-of-section))
732     (if (or (eq filename 1) (not filename))
733         (insert (read-file-name
734                  (concat filetype "filename: ") "" "" nil) "\n")
735       (insert filename "\n"))
736     (forward-line -1)
737     (if rpm-spec-add-attr
738         (let ((rpm-default-mode rpm-default-umask))
739           (insert "%attr(" rpm-default-mode ", " rpm-default-owner ", "
740                   rpm-default-group ") ")))
741     (insert filetype)))
742
743 (defun rpm-insert-file (&optional filename)
744   "Insert regular file."
745   (interactive "p")
746   (rpm-insert-f "" filename))
747
748 (defun rpm-insert-config (&optional filename)
749   "Insert config file."
750   (interactive "p")
751   (rpm-insert-f "%config " filename))
752
753 (defun rpm-insert-doc (&optional filename)
754   "Insert doc file."
755   (interactive "p")
756   (rpm-insert-f "%doc " filename))
757
758 (defun rpm-insert-ghost (&optional filename)
759   "Insert ghost file."
760   (interactive "p")
761   (rpm-insert-f "%ghost " filename))
762
763 (defun rpm-insert-dir (&optional dirname)
764   "Insert directory."
765   (interactive "p")
766   (rpm-insert-f "%dir " dirname))
767
768 (defun rpm-insert-docdir (&optional dirname)
769   "Insert doc directory."
770   (interactive "p")
771   (rpm-insert-f "%docdir " dirname))
772
773 ;;------------------------------------------------------------
774 (defun rpm-completing-read (prompt table &optional pred require init hist)
775   "Read from the minibuffer, with completion.
776 Like `completing-read', but the variable `rpm-spec-completion-ignore-case'
777 controls whether case is significant."
778   (let ((completion-ignore-case rpm-spec-completion-ignore-case))
779     (completing-read prompt table pred require init hist)))
780
781 (defun rpm-insert (&optional what file-completion)
782   "Insert given tag.  Use file-completion if argument is t."
783   (beginning-of-line)
784   (if (not what)
785       (setq what (rpm-completing-read "Tag: " rpm-tags-list)))
786   (if (string-match "^%" what)
787       (setq read-text (concat "Packagename for " what ": ")
788             insert-text (concat what " "))
789     (setq read-text (concat what ": ")
790           insert-text (concat what ": ")))
791   (cond
792    ((string-equal what "Group")
793     (rpm-insert-group))
794    ((string-equal what "Source")
795     (rpm-insert-n "Source"))
796    ((string-equal what "Patch")
797     (rpm-insert-n "Patch"))
798    (t
799     (if file-completion
800         (insert insert-text (read-file-name (concat read-text) "" "" nil) "\n")
801       (insert insert-text (read-from-minibuffer (concat read-text)) "\n")))))
802
803 (defun rpm-topdir ()
804   (or
805    (getenv "RPM")
806    (getenv "rpm")
807    (if (file-directory-p "~/rpm") "~/rpm/")
808    (if (file-directory-p "~/RPM") "~/RPM/")
809    (if (file-directory-p "/usr/src/redhat/") "/usr/src/redhat/")
810    "/usr/src/RPM"))
811
812 (defun rpm-insert-n (what &optional arg)
813   "Insert given tag with possible number."
814   (save-excursion
815     (goto-char (point-max))
816     (if (search-backward-regexp (concat "^" what "\\([0-9]*\\):") nil t)
817         (let ((release (1+ (string-to-int (match-string 1)))))
818           (forward-line 1)
819           (let ((default-directory (concat (rpm-topdir) "/SOURCES/")))
820             (insert what (int-to-string release) ": "
821                     (read-file-name (concat what "file: ") "" "" nil) "\n")))
822       (goto-char (point-min))
823       (rpm-end-of-section)
824       (insert what ": " (read-from-minibuffer (concat what "file: ")) "\n"))))
825
826 (defun rpm-change (&optional what arg)
827   "Update given tag."
828   (save-excursion
829     (if (not what)
830         (setq what (rpm-completing-read "Tag: " rpm-tags-list)))
831     (cond
832      ((string-equal what "Group")
833       (rpm-change-group))
834      ((string-equal what "Source")
835       (rpm-change-n "Source"))
836      ((string-equal what "Patch")
837       (rpm-change-n "Patch"))
838      (t
839       (goto-char (point-min))
840       (if (search-forward-regexp (concat "^" what ":\\s-*\\(.*\\)$") nil t)
841           (replace-match
842            (concat what ": " (read-from-minibuffer
843                               (concat "New " what ": ") (match-string 1))))
844         (message "%s tag not found..." what))))))
845
846 (defun rpm-change-n (what &optional arg)
847   "Change given tag with possible number."
848   (save-excursion
849     (goto-char (point-min))
850     (let ((number (read-from-minibuffer (concat what " number: "))))
851       (if (search-forward-regexp
852            (concat "^" what number ":\\s-*\\(.*\\)") nil t)
853           (let ((default-directory (concat (rpm-topdir) "/SOURCES/")))
854             (replace-match
855              (concat what number ": "
856                      (read-file-name (concat "New " what number " file: ")
857                                      "" "" nil (match-string 1)))))
858         (message "%s number \"%s\" not found..." what number)))))
859
860 (defun rpm-insert-group (group)
861   "Insert Group tag."
862   (interactive (list (rpm-completing-read "Group: " rpm-group-tags-list)))
863   (beginning-of-line)
864   (insert "Group: " group "\n"))
865
866 (defun rpm-change-group (&optional arg)
867   "Update Group tag."
868   (interactive "p")
869   (save-excursion
870     (goto-char (point-min))
871     (if (search-forward-regexp "^Group: \\(.*\\)$" nil t)
872         (replace-match
873          (concat "Group: "
874                  (insert (rpm-completing-read "Group: " rpm-group-tags-list
875                                               nil nil (match-string 1)))))
876       (message "Group tag not found..."))))
877
878 (defun rpm-insert-tag (&optional arg)
879   "Insert or change a tag."
880   (interactive "p")
881   (if current-prefix-arg
882       (rpm-change)
883     (rpm-insert)))
884
885 (defun rpm-change-tag (&optional arg)
886   "Change a tag."
887   (interactive "p")
888   (rpm-change))
889
890 (defun rpm-insert-packager (&optional arg)
891   "Insert Packager tag."
892   (interactive "p")
893   (beginning-of-line)
894   (insert "Packager: " (or rpm-spec-user-full-name (user-full-name))
895           " <" (rpm-spec-user-mail-address) ">\n"))
896
897 (defun rpm-change-packager (&optional arg)
898   "Update Packager tag."
899   (interactive "p")
900   (rpm-change "Packager"))
901
902 ;;------------------------------------------------------------
903
904 (defun rpm-current-section nil
905   (interactive)
906   (save-excursion
907     (rpm-forward-section)
908     (rpm-backward-section)
909     (if (bobp) "preamble"
910       (buffer-substring (match-beginning 1) (match-end 1)))))
911
912 (defun rpm-backward-section nil
913   "Move backward to the beginning of the previous section.
914 Go to beginning of previous section."
915   (interactive)
916   (or (re-search-backward rpm-section-regexp nil t)
917       (goto-char (point-min))))
918
919 (defun rpm-beginning-of-section nil
920   "Move backward to the beginning of the current section.
921 Go to beginning of current section."
922   (interactive)
923   (or (and (looking-at rpm-section-regexp) (point))
924       (re-search-backward rpm-section-regexp nil t)
925       (goto-char (point-min))))
926
927 (defun rpm-forward-section nil
928   "Move forward to the beginning of the next section."
929   (interactive)
930   (forward-char)
931   (if (re-search-forward rpm-section-regexp nil t)
932       (progn (forward-line 0) (point))
933     (goto-char (point-max))))
934
935 (defun rpm-end-of-section nil
936   "Move forward to the end of this section."
937   (interactive)
938   (forward-char)
939   (if (re-search-forward rpm-section-regexp nil t)
940       (forward-line -1)
941     (goto-char (point-max)))
942 ;;  (while (or (looking-at paragraph-separate) (looking-at "^\\s-*#"))
943   (while (looking-at "^\\s-*\\($\\|#\\)")
944     (forward-line -1))
945   (forward-line 1)
946   (point))
947
948 (defun rpm-goto-section (section)
949   "Move point to the beginning of the specified section;
950 leave point at previous location."
951   (interactive (list (rpm-completing-read "Section: " rpm-section-list)))
952   (push-mark)
953   (goto-char (point-min))
954   (or
955    (equal section "preamble")
956    (re-search-forward (concat "^%" section "\\b") nil t)
957    (let ((s (cdr rpm-sections)))
958      (while (not (equal section (car s)))
959        (re-search-forward (concat "^%" (car s) "\\b") nil t)
960        (setq s (cdr s)))
961      (if (re-search-forward rpm-section-regexp nil t)
962          (forward-line -1) (goto-char (point-max)))
963      (insert "\n%" section "\n"))))
964
965 (defun rpm-mouse-goto-section (&optional section)
966   (interactive
967    (x-popup-menu
968     nil
969     (list "sections"
970           (cons "Sections" (mapcar (lambda (e) (list e e)) rpm-sections))
971           (cons "Scripts" (mapcar (lambda (e) (list e e)) rpm-scripts))
972           )))
973   ;; If user doesn't pick a section, exit quietly.
974   (and section
975        (if (member section rpm-sections)
976            (rpm-goto-section section)
977          (goto-char (point-min))
978          (or (re-search-forward (concat "^%" section "\\b") nil t)
979              (and (re-search-forward "^%files\\b" nil t) (forward-line -1))
980              (goto-char (point-max))))))
981
982 (defun rpm-insert-true-prefix ()
983   (interactive)
984   (insert "%{prefix}"))
985
986 ;;------------------------------------------------------------
987
988 (defun rpm-build (buildoptions)
989   "Build this RPM package."
990   (if (and (buffer-modified-p)
991            (y-or-n-p (format "Buffer %s modified, save it? " (buffer-name))))
992       (save-buffer))
993   (setq rpm-buffer-name
994         (concat "*" rpm-spec-build-command " " buildoptions " "
995                 (file-name-nondirectory buffer-file-name) "*"))
996   (rpm-process-check rpm-buffer-name)
997   (if (get-buffer rpm-buffer-name)
998       (kill-buffer rpm-buffer-name))
999   (create-file-buffer rpm-buffer-name)
1000   (display-buffer rpm-buffer-name)
1001   (setq buildoptions (list buildoptions buffer-file-name))
1002   (if (or rpm-spec-short-circuit rpm-spec-nobuild)
1003       (setq rpm-no-gpg t))
1004   (if rpm-spec-rmsource
1005       (setq buildoptions (cons "--rmsource" buildoptions)))
1006   (if rpm-spec-clean
1007       (setq buildoptions (cons "--clean" buildoptions)))
1008   (if rpm-spec-short-circuit
1009       (setq buildoptions (cons "--short-circuit" buildoptions)))
1010   (if (and (not (equal rpm-spec-timecheck "0"))
1011            (not (equal rpm-spec-timecheck "")))
1012       (setq buildoptions (cons "--timecheck" (cons rpm-spec-timecheck
1013                                                    buildoptions))))
1014   (if (not (equal rpm-spec-buildroot ""))
1015       (setq buildoptions (cons "--buildroot" (cons rpm-spec-buildroot
1016                                                    buildoptions))))
1017   (if (not (equal rpm-spec-target ""))
1018       (setq buildoptions (cons "--target" (cons rpm-spec-target
1019                                                 buildoptions))))
1020   (if rpm-spec-nobuild
1021       (setq buildoptions (cons rpm-spec-nobuild-option buildoptions)))
1022   (if rpm-spec-quiet
1023       (setq buildoptions (cons "--quiet" buildoptions)))
1024   (if rpm-spec-nodeps
1025       (setq buildoptions (cons "--nodeps" buildoptions)))
1026   (if (and rpm-spec-sign-gpg (not rpm-no-gpg))
1027       (setq buildoptions (cons "--sign" buildoptions)))
1028   (save-excursion
1029     (set-buffer (get-buffer rpm-buffer-name))
1030     (and rpm-spec-use-compilation-mode
1031          (fboundp 'compilation-mode)
1032          (compilation-mode))
1033     (goto-char (point-max)))
1034   (let* ((process-environment (cons "EMACS=t" process-environment))
1035          (process
1036           (apply 'start-process rpm-spec-build-command rpm-buffer-name
1037                  rpm-spec-build-command buildoptions)))
1038     (if (and rpm-spec-sign-gpg (not rpm-no-gpg))
1039         (let ((rpm-passwd-cache (read-passwd "GPG passphrase: ")))
1040           (process-send-string process (concat rpm-passwd-cache "\n"))))
1041     (set-process-filter process 'rpm-command-filter)))
1042
1043 (defun rpm-build-prepare (&optional arg)
1044   "Run a `rpmbuild -bp'."
1045   (interactive "p")
1046   (if rpm-spec-short-circuit
1047       (message "Cannot run `%s -bp' with --short-circuit"
1048                rpm-spec-build-command)
1049     (setq rpm-no-gpg t)
1050     (rpm-build "-bp")))
1051
1052 (defun rpm-list-check (&optional arg)
1053   "Run a `rpmbuild -bl'."
1054   (interactive "p")
1055   (if rpm-spec-short-circuit
1056       (message "Cannot run `%s -bl' with --short-circuit"
1057                rpm-spec-build-command)
1058     (setq rpm-no-gpg t)
1059     (rpm-build "-bl")))
1060
1061 (defun rpm-build-compile (&optional arg)
1062   "Run a `rpmbuild -bc'."
1063   (interactive "p")
1064   (setq rpm-no-gpg t)
1065   (rpm-build "-bc"))
1066
1067 (defun rpm-build-install (&optional arg)
1068   "Run a `rpmbuild -bi'."
1069   (interactive "p")
1070   (setq rpm-no-gpg t)
1071   (rpm-build "-bi"))
1072
1073 (defun rpm-build-binary (&optional arg)
1074   "Run a `rpmbuild -bb'."
1075   (interactive "p")
1076   (if rpm-spec-short-circuit
1077       (message "Cannot run `%s -bb' with --short-circuit"
1078                rpm-spec-build-command)
1079     (setq rpm-no-gpg nil)
1080     (rpm-build "-bb")))
1081
1082 (defun rpm-build-source (&optional arg)
1083   "Run a `rpmbuild -bs'."
1084   (interactive "p")
1085   (if rpm-spec-short-circuit
1086       (message "Cannot run `%s -bs' with --short-circuit"
1087                rpm-spec-build-command)
1088     (setq rpm-no-gpg nil)
1089     (rpm-build "-bs")))
1090
1091 (defun rpm-build-all (&optional arg)
1092   "Run a `rpmbuild -ba'."
1093   (interactive "p")
1094   (if rpm-spec-short-circuit
1095       (message "Cannot run `%s -ba' with --short-circuit"
1096                rpm-spec-build-command)
1097     (setq rpm-no-gpg nil)
1098     (rpm-build "-ba")))
1099
1100 (defun rpm-process-check (buffer)
1101   "Check if BUFFER has a running process.
1102 If so, give the user the choice of aborting the process or the current
1103 command."
1104   (let ((process (get-buffer-process (get-buffer buffer))))
1105     (if (and process (eq (process-status process) 'run))
1106         (if (yes-or-no-p (concat "Process `" (process-name process)
1107                                  "' running.  Kill it? "))
1108             (delete-process process)
1109           (error "Cannot run two simultaneous processes ...")))))
1110
1111 ;;------------------------------------------------------------
1112
1113 (defun rpm-toggle-short-circuit (&optional arg)
1114   "Toggle `rpm-spec-short-circuit'."
1115   (interactive "p")
1116   (setq rpm-spec-short-circuit (not rpm-spec-short-circuit))
1117   (rpm-update-mode-name)
1118   (message (concat "Turned `--short-circuit' "
1119                    (if rpm-spec-short-circuit "on" "off") ".")))
1120
1121 (defun rpm-toggle-rmsource (&optional arg)
1122   "Toggle `rpm-spec-rmsource'."
1123   (interactive "p")
1124   (setq rpm-spec-rmsource (not rpm-spec-rmsource))
1125   (rpm-update-mode-name)
1126   (message (concat "Turned `--rmsource' "
1127                    (if rpm-spec-rmsource "on" "off") ".")))
1128
1129 (defun rpm-toggle-clean (&optional arg)
1130   "Toggle `rpm-spec-clean'."
1131   (interactive "p")
1132   (setq rpm-spec-clean (not rpm-spec-clean))
1133   (rpm-update-mode-name)
1134   (message (concat "Turned `--clean' "
1135                    (if rpm-spec-clean "on" "off") ".")))
1136
1137 (defun rpm-toggle-nobuild (&optional arg)
1138   "Toggle `rpm-spec-nobuild'."
1139   (interactive "p")
1140   (setq rpm-spec-nobuild (not rpm-spec-nobuild))
1141   (rpm-update-mode-name)
1142   (message (concat "Turned `" rpm-spec-nobuild-option "' "
1143                    (if rpm-spec-nobuild "on" "off") ".")))
1144
1145 (defun rpm-toggle-quiet (&optional arg)
1146   "Toggle `rpm-spec-quiet'."
1147   (interactive "p")
1148   (setq rpm-spec-quiet (not rpm-spec-quiet))
1149   (rpm-update-mode-name)
1150   (message (concat "Turned `--quiet' "
1151                    (if rpm-spec-quiet "on" "off") ".")))
1152
1153 (defun rpm-toggle-sign-gpg (&optional arg)
1154   "Toggle `rpm-spec-sign-gpg'."
1155   (interactive "p")
1156   (setq rpm-spec-sign-gpg (not rpm-spec-sign-gpg))
1157   (rpm-update-mode-name)
1158   (message (concat "Turned `--sign' "
1159                    (if rpm-spec-sign-gpg "on" "off") ".")))
1160
1161 (defun rpm-toggle-add-attr (&optional arg)
1162   "Toggle `rpm-spec-add-attr'."
1163   (interactive "p")
1164   (setq rpm-spec-add-attr (not rpm-spec-add-attr))
1165   (rpm-update-mode-name)
1166   (message (concat "Default add \"attr\" entry turned "
1167                    (if rpm-spec-add-attr "on" "off") ".")))
1168
1169 (defun rpm-toggle-nodeps (&optional arg)
1170   "Toggle `rpm-spec-nodeps'."
1171   (interactive "p")
1172   (setq rpm-spec-nodeps (not rpm-spec-nodeps))
1173   (rpm-update-mode-name)
1174   (message (concat "Turned `--nodeps' "
1175                    (if rpm-spec-nodeps "on" "off") ".")))
1176
1177 (defun rpm-update-mode-name ()
1178   "Update `mode-name' according to values set."
1179   (setq mode-name "RPM-SPEC")
1180   (setq modes (concat (if rpm-spec-add-attr      "A")
1181                       (if rpm-spec-clean         "C")
1182                       (if rpm-spec-nodeps        "D")
1183                       (if rpm-spec-sign-gpg      "G")
1184                       (if rpm-spec-nobuild       "N")
1185                       (if rpm-spec-rmsource      "R")
1186                       (if rpm-spec-short-circuit "S")
1187                       (if rpm-spec-quiet         "Q")
1188                       ))
1189   (if (not (equal modes ""))
1190       (setq mode-name (concat mode-name ":" modes))))
1191
1192 ;;------------------------------------------------------------
1193
1194 (defun rpm-change-timecheck-option (&optional arg)
1195   "Change the value for timecheck."
1196   (interactive "p")
1197   (setq rpm-spec-timecheck
1198         (read-from-minibuffer "New timecheck: " rpm-spec-timecheck)))
1199
1200 (defun rpm-change-buildroot-option (&optional arg)
1201   "Change the value for buildroot."
1202   (interactive "p")
1203   (setq rpm-spec-buildroot
1204         (read-from-minibuffer "New buildroot: " rpm-spec-buildroot)))
1205
1206 (defun rpm-change-target-option (&optional arg)
1207   "Change the value for target."
1208   (interactive "p")
1209   (setq rpm-spec-target
1210         (read-from-minibuffer "New target: " rpm-spec-target)))
1211
1212 (defun rpm-files-umask (&optional arg)
1213   "Change the default umask for files."
1214   (interactive "p")
1215   (setq rpm-default-umask
1216         (read-from-minibuffer "Default file umask: " rpm-default-umask)))
1217
1218 (defun rpm-files-owner (&optional arg)
1219   "Change the default owner for files."
1220   (interactive "p")
1221   (setq rpm-default-owner
1222         (read-from-minibuffer "Default file owner: " rpm-default-owner)))
1223
1224 (defun rpm-files-group (&optional arg)
1225   "Change the source directory."
1226   (interactive "p")
1227   (setq rpm-default-group
1228         (read-from-minibuffer "Default file group: " rpm-default-group)))
1229
1230 (defun rpm-increase-release-tag (&optional arg)
1231   "Increase the release tag by 1."
1232   (interactive "p")
1233   (save-excursion
1234     (goto-char (point-min))
1235     (if (search-forward-regexp
1236          ;; Try to find the last digit-only group of a dot-separated release string
1237          (concat "^\\(Release[ \t]*:[ \t]*\\)"
1238                  "\\(.*[ \t\\.}]\\)\\([0-9]+\\)\\([ \t\\.%].*\\|$\\)") nil t)
1239         (let ((release (1+ (string-to-int (match-string 3)))))
1240           (setq release
1241                 (concat (match-string 2) (int-to-string release) (match-string 4)))
1242           (replace-match (concat (match-string 1) release))
1243           (message "Release tag changed to %s." release))
1244       (if (search-forward-regexp "^Release[ \t]*:[ \t]*%{?\\([^}]*\\)}?$" nil t)
1245           (rpm-increase-release-with-macros)
1246         (message "No Release tag to increase found...")))))
1247
1248 ;;------------------------------------------------------------
1249
1250 (defun rpm-spec-field-value (field max)
1251   "Get the value of FIELD, searching up to buffer position MAX.
1252 See `search-forward-regexp'."
1253   (save-excursion
1254     (condition-case nil
1255       (let ((str
1256              (progn
1257                (goto-char (point-min))
1258                (search-forward-regexp
1259                 (concat "^" field ":[ \t]*\\(.*?\\)[ \t]*$") max)
1260                (match-string 1))))
1261         ;; Try to expand macros
1262         (if (string-match "\\(%{?\\(\\?\\)?\\)\\([a-zA-Z0-9_]*\\)\\(}?\\)" str)
1263             (let ((start-string (substring str 0 (match-beginning 1)))
1264                   (end-string (substring str (match-end 4))))
1265               (if (progn
1266                     (goto-char (point-min))
1267                     (search-forward-regexp
1268                      (concat "%\\(define\\|global\\)[ \t]+"
1269                              (match-string 3 str)
1270                              "[ \t]+\\(.*\\)") nil t))
1271                   ;; Got it - replace.
1272                   (concat start-string (match-string 2) end-string)
1273                 (if (match-string 2 str)
1274                     ;; Conditionally evaluated macro - remove it.
1275                     (concat start-string end-string)
1276                   ;; Leave as is.
1277                   str)))
1278           str))
1279       (error nil))))
1280
1281 (defun rpm-find-spec-version (&optional with-epoch)
1282   "Get the version string.
1283 If WITH-EPOCH is non-nil, the string contains the Epoch/Serial value,
1284 if one is present in the file."
1285   (save-excursion
1286     (goto-char (point-min))
1287     (let* ((max (search-forward-regexp rpm-section-regexp))
1288            (version (rpm-spec-field-value "Version" max))
1289            (release (rpm-spec-field-value "Release" max))
1290            (epoch   (rpm-spec-field-value "Epoch"   max)) )
1291       (when (and version (< 0 (length version)))
1292         (unless epoch (setq epoch (rpm-spec-field-value "Serial" max)))
1293         (concat (and with-epoch epoch (concat epoch ":"))
1294                 version
1295                 (and release (concat "-" release)))))))
1296
1297 (defun rpm-increase-release-with-macros ()
1298   (save-excursion
1299     (let ((str
1300            (progn
1301              (goto-char (point-min))
1302              (search-forward-regexp "^Release[ \t]*:[ \t]*\\(.+\\).*$" nil)
1303              (match-string 1))))
1304       (let ((inrel
1305              (if (string-match "%{?\\([^}]*\\)}?$" str)
1306                  (progn
1307                    (goto-char (point-min))
1308                    (setq macros (substring str (match-beginning 1)
1309                                            (match-end 1)))
1310                    (search-forward-regexp
1311                     (concat "%define[ \t]+" macros
1312                             "[ \t]+\\(\\([0-9]\\|\\.\\)+\\)\\(.*\\)"))
1313                    (concat macros " " (int-to-string (1+ (string-to-int
1314                                                           (match-string 1))))
1315                            (match-string 3)))
1316                str)))
1317         (setq dinrel inrel)
1318         (replace-match (concat "%define " dinrel))
1319         (message "Release tag changed to %s." dinrel)))))
1320
1321 ;;------------------------------------------------------------
1322
1323 (defun rpm-spec-initialize ()
1324   "Create a default spec file if one does not exist or is empty."
1325   (let (file name version (release rpm-spec-default-release))
1326     (setq file (if (buffer-file-name)
1327                    (file-name-nondirectory (buffer-file-name))
1328                  (buffer-name)))
1329     (cond
1330      ((eq (string-match "\\(.*\\)-\\([^-]*\\)-\\([^-]*\\).spec" file) 0)
1331       (setq name (match-string 1 file))
1332       (setq version (match-string 2 file))
1333       (setq release (match-string 3 file)))
1334      ((eq (string-match "\\(.*\\)-\\([^-]*\\).spec" file) 0)
1335       (setq name (match-string 1 file))
1336       (setq version (match-string 2 file)))
1337      ((eq (string-match "\\(.*\\).spec" file) 0)
1338       (setq name (match-string 1 file))))
1339
1340     (if rpm-spec-indent-heading-values
1341         (insert
1342          "Summary:        "
1343          "\nName:           " (or name "")
1344          "\nVersion:        " (or version "")
1345          "\nRelease:        " (or release "")
1346          (if rpm-spec-default-epoch
1347              (concat "\nEpoch:          "
1348                      (int-to-string rpm-spec-default-epoch))
1349            "")
1350          "\nLicense:        "
1351          "\nGroup:          "
1352          "\nURL:            "
1353          "\nSource0:        %{name}-%{version}.tar.gz"
1354          "\nBuildRoot:      " rpm-spec-default-buildroot)
1355       (insert
1356        "Summary: "
1357        "\nName: " (or name "")
1358        "\nVersion: " (or version "")
1359        "\nRelease: " (or release "")
1360        (if rpm-spec-default-epoch
1361            (concat "\nEpoch: " (int-to-string rpm-spec-default-epoch))
1362          "")
1363        "\nLicense: "
1364        "\nGroup: "
1365        "\nURL: "
1366        "\nSource0: %{name}-%{version}.tar.gz"
1367        "\nBuildRoot: " rpm-spec-default-buildroot))
1368
1369     (insert
1370      "\n\n%description\n"
1371      "\n%prep"
1372      "\n%setup -q"
1373      "\n\n%build\n"
1374      (or rpm-spec-default-build-section "")
1375      "\n%install\n"
1376      (or rpm-spec-default-install-section "")
1377      "\n%clean\n"
1378      (or rpm-spec-default-clean-section "")
1379      "\n\n%files"
1380      "\n%defattr(-,root,root,-)"
1381      "\n%doc\n"
1382      "\n\n%changelog\n")
1383
1384     (end-of-line 1)
1385     (rpm-add-change-log-entry "Initial build.")))
1386
1387 ;;------------------------------------------------------------
1388
1389 (defun rpm-spec-user-mail-address ()
1390   "User mail address helper."
1391   (cond
1392    (rpm-spec-user-mail-address
1393     rpm-spec-user-mail-address)
1394    ((fboundp 'user-mail-address)
1395     (user-mail-address))
1396    (t
1397     user-mail-address)))
1398
1399 ;;------------------------------------------------------------
1400
1401 (defun rpm-about-rpm-spec-mode (&optional arg)
1402   "About `rpm-spec-mode'."
1403   (interactive "p")
1404   (message
1405    (concat "rpm-spec-mode version "
1406            rpm-spec-mode-version
1407            " by Stig Bjørlykke, <stigb@tihlde.org>")))
1408
1409 ;;;###autoload(add-to-list 'auto-mode-alist '("\\.spec\\(\\.in\\)?$" . rpm-spec-mode))
1410
1411 (provide 'rpm-spec-mode)
1412
1413 ;;; rpm-spec-mode.el ends here