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