1 ;;; rpm-spec-mode.el --- RPM spec file editing commands for Emacs/XEmacs
3 ;; Copyright (C) 1997-2002 Stig Bjørlykke, <stigb@tihlde.org>
5 ;; Author: Stig Bjørlykke, <stigb@tihlde.org>
6 ;; Keywords: unix, languages
9 ;; This file is part of XEmacs.
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)
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.
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.
26 ;;; Synched up with: not in GNU Emacs.
30 ;; Tore Olsen <toreo@tihlde.org> for some general fixes.
31 ;; Steve Sanbeg <sanbeg@dset.com> for navigation functions and
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.
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)
49 ;; This mode is used for editing spec files used for building RPM packages.
51 ;; Most recent version is available from:
52 ;; <URL:http://www.tihlde.org/~stigb/rpm-spec-mode.el>
54 ;; Put this in your .emacs file to enable autoloading of rpm-spec-mode,
55 ;; and auto-recognition of ".spec" files:
57 ;; (autoload 'rpm-spec-mode "rpm-spec-mode.el" "RPM spec mode." t)
58 ;; (setq auto-mode-alist (append '(("\\.spec" . rpm-spec-mode))
60 ;;------------------------------------------------------------
65 (defconst rpm-spec-mode-version "0.12" "Version of `rpm-spec-mode'.")
67 (defgroup rpm-spec nil
68 "RPM spec mode with Emacs/XEmacs enhancements."
72 (defcustom rpm-spec-build-command "rpmbuild"
73 "Command for building a RPM package."
77 (defcustom rpm-spec-add-attr nil
78 "Add \"%attr\" entry for file listings or not."
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."
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
97 (defcustom rpm-spec-buildroot ""
98 "Override the BuildRoot tag with directory <dir>."
102 (defcustom rpm-spec-target ""
103 "Interpret given string as `arch-vendor-os'.
104 Set the macros _target, _target_arch and _target_os accordingly"
108 (define-obsolete-variable-alias
109 'rpm-completion-ignore-case 'rpm-spec-completion-ignore-case)
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."
118 (defcustom rpm-spec-clean nil
119 "Remove the build tree after the packages are made."
123 (defcustom rpm-spec-rmsource nil
124 "Remove the source and spec file after the packages are made."
128 (defcustom rpm-spec-nobuild nil
129 "Do not execute any build stages. Useful for testing out spec files."
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
140 (defcustom rpm-spec-nodeps nil
141 "Do not verify build dependencies."
145 (defcustom rpm-spec-old-rpm nil
146 "Set if using `rpm' as command for building packages."
150 (define-obsolete-variable-alias
151 'rpm-initialize-sections 'rpm-spec-initialize-sections)
153 (defcustom rpm-spec-initialize-sections t
154 "Automatically add empty section headings to new spec files."
158 (define-obsolete-variable-alias
159 'rpm-insert-version 'rpm-spec-insert-changelog-version)
161 (defcustom rpm-spec-insert-changelog-version t
162 "Automatically add version in a new change log entry."
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)
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)
182 (defgroup rpm-spec-faces nil
183 "Font lock faces for `rpm-spec-mode'."
187 ;;------------------------------------------------------------
188 ;; variables used by navigation functions.
190 (defconst rpm-sections
191 '("preamble" "description" "prep" "setup" "build" "install" "clean"
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
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)
212 "Regular expression to match beginning of a section.")
214 ;;------------------------------------------------------------
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)
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)
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)
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)
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)
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)
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)
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.")
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\".")
281 ;;------------------------------------------------------------
283 (defvar rpm-no-gpg nil "Tell rpm not to sign package.")
285 (defvar rpm-tags-list
286 ;; From RPM 4.1 sources, file build/parsePreamble.c: preambleList[].")
291 ("BuildArchitectures")
332 "List of elements that are valid tags.")
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")
357 ("System Environment/Base")
358 ("System Environment/Daemons")
359 ("System Environment/Kernel")
360 ("System Environment/Libraries")
361 ("System Environment/Shells")
362 ("User Interface/Desktops")
364 ("User Interface/X Hardware Support")
366 "List of elements that are valid group tags.")
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))
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)
432 (defconst rpm-spec-mode-menu
433 (purecopy '("RPM spec"
434 ["Insert Tag..." rpm-insert-tag t]
435 ["Change Tag..." rpm-change-tag t]
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]
441 ["Add change log entry..." rpm-add-change-log-entry t]
442 ["Increase release tag" rpm-increase-release-tag t]
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]
450 ["Directory..." rpm-insert-dir t]
451 ["Document directory..." rpm-insert-docdir t]
453 ["Insert %{prefix}" rpm-insert-true-prefix t]
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])
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]
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])
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]
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])
487 ["About rpm-spec-mode" rpm-about-rpm-spec-mode t]
490 (defvar rpm-spec-font-lock-keywords
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)
518 "Additional expressions to highlight in `rpm-spec-mode'.")
520 ;;Initialize font lock for xemacs
521 (put 'rpm-spec-mode 'font-lock-defaults '(rpm-spec-font-lock-keywords))
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 ())
527 ;;------------------------------------------------------------
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.
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."
539 (kill-all-local-variables)
543 (require 'sh-script)))
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)
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)
556 (if (= (buffer-size) 0)
557 (rpm-spec-initialize))
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"))
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))
589 (defun rpm-command-filter (process string)
590 "Filter to process normal output."
592 (set-buffer (process-buffer process))
594 (goto-char (process-mark process))
595 (insert-before-markers string)
596 (set-marker (process-mark process) (point)))))
598 ;;------------------------------------------------------------
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: ")
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")
615 (insert "- " change-log-entry "\n"))))
617 ;;------------------------------------------------------------
619 (defun rpm-insert-f (&optional filetype filename)
620 "Insert new \"%files\" entry."
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"))
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 ") ")))
634 (defun rpm-insert-file (&optional filename)
635 "Insert regular file."
637 (rpm-insert-f "" filename))
639 (defun rpm-insert-config (&optional filename)
640 "Insert config file."
642 (rpm-insert-f "%config " filename))
644 (defun rpm-insert-doc (&optional filename)
647 (rpm-insert-f "%doc " filename))
649 (defun rpm-insert-ghost (&optional filename)
652 (rpm-insert-f "%ghost " filename))
654 (defun rpm-insert-dir (&optional dirname)
657 (rpm-insert-f "%dir " dirname))
659 (defun rpm-insert-docdir (&optional dirname)
660 "Insert doc directory."
662 (rpm-insert-f "%docdir " dirname))
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)))
672 (defun rpm-insert (&optional what file-completion)
673 "Insert given tag. Use file-completion if argument is t."
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 ": ")))
683 ((string-equal what "Group")
685 ((string-equal what "Source")
686 (rpm-insert-n "Source"))
687 ((string-equal what "Patch")
688 (rpm-insert-n "Patch"))
691 (insert insert-text (read-file-name (concat read-text) "" "" nil) "\n")
692 (insert insert-text (read-from-minibuffer (concat read-text)) "\n")))))
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/")
703 (defun rpm-insert-n (what &optional arg)
704 "Insert given tag with possible number."
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)))))
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))
715 (insert what ": " (read-from-minibuffer (concat what "file: ")) "\n"))))
717 (defun rpm-change (&optional what arg)
721 (setq what (rpm-completing-read "Tag: " rpm-tags-list)))
723 ((string-equal what "Group")
725 ((string-equal what "Source")
726 (rpm-change-n "Source"))
727 ((string-equal what "Patch")
728 (rpm-change-n "Patch"))
730 (goto-char (point-min))
731 (if (search-forward-regexp (concat "^" what ":\\s-*\\(.*\\)$") nil t)
733 (concat what ": " (read-from-minibuffer
734 (concat "New " what ": ") (match-string 1))))
735 (message (concat what " tag not found...")))))))
737 (defun rpm-change-n (what &optional arg)
738 "Change given tag with possible number."
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/")))
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..."))))))
751 (defun rpm-insert-group (group)
753 (interactive (list (rpm-completing-read "Group: " rpm-group-tags-list)))
755 (insert "Group: " group "\n"))
757 (defun rpm-change-group (&optional arg)
761 (goto-char (point-min))
762 (if (search-forward-regexp "^Group: \\(.*\\)$" nil t)
765 (insert (rpm-completing-read "Group: " rpm-group-tags-list
766 nil nil (match-string 1)))))
767 (message "Group tag not found..."))))
769 (defun rpm-insert-tag (&optional arg)
770 "Insert or change a tag."
772 (if current-prefix-arg
776 (defun rpm-change-tag (&optional arg)
781 (defun rpm-insert-packager (&optional arg)
782 "Insert Packager tag."
785 (insert "Packager: " (or rpm-spec-user-full-name (user-full-name))
786 " <" (or rpm-spec-user-mail-address (user-mail-address)) ">\n"))
788 (defun rpm-change-packager (&optional arg)
789 "Update Packager tag."
791 (rpm-change "Packager"))
793 ;;------------------------------------------------------------
795 (defun rpm-current-section nil
798 (rpm-forward-section)
799 (rpm-backward-section)
800 (if (bobp) "preamble"
801 (buffer-substring (match-beginning 1) (match-end 1)))))
803 (defun rpm-backward-section nil
804 "Move backward to the beginning of the previous section.
805 Go to beginning of previous section."
807 (or (re-search-backward rpm-section-regexp nil t)
808 (goto-char (point-min))))
810 (defun rpm-beginning-of-section nil
811 "Move backward to the beginning of the current section.
812 Go to beginning of current section."
814 (or (and (looking-at rpm-section-regexp) (point))
815 (re-search-backward rpm-section-regexp nil t)
816 (goto-char (point-min))))
818 (defun rpm-forward-section nil
819 "Move forward to the beginning of the next section."
822 (if (re-search-forward rpm-section-regexp nil t)
823 (progn (forward-line 0) (point))
824 (goto-char (point-max))))
826 (defun rpm-end-of-section nil
827 "Move forward to the end of this section."
830 (if (re-search-forward rpm-section-regexp nil t)
832 (goto-char (point-max)))
833 ;; (while (or (looking-at paragraph-separate) (looking-at "^\\s-*#"))
834 (while (looking-at "^\\s-*\\($\\|#\\)")
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)))
844 (goto-char (point-min))
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)
852 (if (re-search-forward rpm-section-regexp nil t)
853 (forward-line -1) (goto-char (point-max)))
854 (insert "\n%" section "\n"))))
856 (defun rpm-mouse-goto-section (&optional section)
861 (cons "Sections" (mapcar (lambda (e) (list e e)) rpm-sections))
862 (cons "Scripts" (mapcar (lambda (e) (list e e)) rpm-scripts))
864 ;; If user doesn't pick a section, exit quietly.
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))))))
873 (defun rpm-insert-true-prefix ()
875 (insert "%{prefix}"))
877 ;;------------------------------------------------------------
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)
892 (if rpm-spec-rmsource
893 (setq buildoptions (cons "--rmsource" buildoptions)))
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
902 (if (not (equal rpm-spec-buildroot ""))
903 (setq buildoptions (cons "--buildroot" (cons rpm-spec-buildroot
905 (if (not (equal rpm-spec-target ""))
906 (setq buildoptions (cons "--target" (cons rpm-spec-target
909 (setq buildoptions (cons (if rpm-spec-old-rpm "--test" "--nobuild")
912 (setq buildoptions (cons "--nodeps" buildoptions)))
913 (if (and rpm-spec-sign-gpg (not rpm-no-gpg))
914 (setq buildoptions (cons "--sign" buildoptions)))
916 (set-buffer (get-buffer rpm-buffer-name))
917 (goto-char (point-max)))
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)))
926 (defun rpm-build-prepare (&optional arg)
927 "Run a `rpmbuild -bp'."
929 (if rpm-spec-short-circuit
930 (message (concat "Cannot run `" rpm-spec-build-command
931 " -bp' with --short-circuit"))
935 (defun rpm-list-check (&optional arg)
936 "Run a `rpmbuild -bl'."
938 (if rpm-spec-short-circuit
939 (message (concat "Cannot run `" rpm-spec-build-command
940 " -bl' with --short-circuit"))
944 (defun rpm-build-compile (&optional arg)
945 "Run a `rpmbuild -bc'."
950 (defun rpm-build-install (&optional arg)
951 "Run a `rpmbuild -bi'."
956 (defun rpm-build-binary (&optional arg)
957 "Run a `rpmbuild -bb'."
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)
965 (defun rpm-build-source (&optional arg)
966 "Run a `rpmbuild -bs'."
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)
974 (defun rpm-build-all (&optional arg)
975 "Run a `rpmbuild -ba'."
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)
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
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 ...")))))
994 ;;------------------------------------------------------------
996 (defun rpm-toggle-short-circuit (&optional arg)
997 "Toggle `rpm-spec-short-circuit'."
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") ".")))
1004 (defun rpm-toggle-rmsource (&optional arg)
1005 "Toggle `rpm-spec-rmsource'."
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") ".")))
1012 (defun rpm-toggle-clean (&optional arg)
1013 "Toggle `rpm-spec-clean'."
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") ".")))
1020 (defun rpm-toggle-nobuild (&optional arg)
1021 "Toggle `rpm-spec-nobuild'."
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") ".")))
1028 (defun rpm-toggle-sign-gpg (&optional arg)
1029 "Toggle `rpm-spec-sign-gpg'."
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") ".")))
1036 (defun rpm-toggle-add-attr (&optional arg)
1037 "Toggle `rpm-spec-add-attr'."
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") ".")))
1044 (defun rpm-toggle-nodeps (&optional arg)
1045 "Toggle `rpm-spec-nodeps'."
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") ".")))
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")
1063 (if (not (equal modes ""))
1064 (setq mode-name (concat mode-name ":" modes))))
1066 ;;------------------------------------------------------------
1068 (defun rpm-change-timecheck-option (&optional arg)
1069 "Change the value for timecheck."
1071 (setq rpm-spec-timecheck
1072 (read-from-minibuffer "New timecheck: " rpm-spec-timecheck)))
1074 (defun rpm-change-buildroot-option (&optional arg)
1075 "Change the value for buildroot."
1077 (setq rpm-spec-buildroot
1078 (read-from-minibuffer "New buildroot: " rpm-spec-buildroot)))
1080 (defun rpm-change-target-option (&optional arg)
1081 "Change the value for target."
1083 (setq rpm-spec-target
1084 (read-from-minibuffer "New target: " rpm-spec-target)))
1086 (defun rpm-files-umask (&optional arg)
1087 "Change the default umask for files."
1089 (setq rpm-default-umask
1090 (read-from-minibuffer "Default file umask: " rpm-default-umask)))
1092 (defun rpm-files-owner (&optional arg)
1093 "Change the default owner for files."
1095 (setq rpm-default-owner
1096 (read-from-minibuffer "Default file owner: " rpm-default-owner)))
1098 (defun rpm-files-group (&optional arg)
1099 "Change the source directory."
1101 (setq rpm-default-group
1102 (read-from-minibuffer "Default file group: " rpm-default-group)))
1104 (defun rpm-increase-release-tag (&optional arg)
1105 "Increase the release tag by 1."
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...")))))
1118 ;;------------------------------------------------------------
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'."
1127 (goto-char (point-min))
1128 (search-forward-regexp (concat
1129 field ":[ \t]*\\(.*?\\)[ \t]*$") max)
1131 (if (string-match "%{?\\([^}]*\\)}?$" str)
1133 (goto-char (point-min))
1134 (search-forward-regexp
1135 (concat "%define[ \t]+" (substring str (match-beginning 1)
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."
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 ":"))
1155 (and release (concat "-" release)))))))
1157 (defun rpm-increase-release-with-macros ()
1161 (goto-char (point-min))
1162 (search-forward-regexp (concat "Release:[ \t]*\\(.+\\).*$") nil)
1165 (if (string-match "%{?\\([^}]*\\)}?$" str)
1167 (goto-char (point-min))
1168 (setq macros (substring str (match-beginning 1)
1170 (search-forward-regexp
1171 (concat "%define[ \t]+" macros
1172 "[ \t]+\\(\\([0-9]\\|\\.\\)+\\)\\(.*\\)"))
1173 (concat macros " " (int-to-string (1+ (string-to-int
1178 (replace-match (concat "%define " dinrel))
1179 (message (concat "Release tag changed to " dinrel "."))))))
1181 ;;------------------------------------------------------------
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))
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))))
1202 "\nName: " (or name "")
1203 "\nVersion: " (or version "")
1204 "\nRelease: " (or release "")
1208 "\nSource0: %{name}-%{version}.tar.gz"
1209 "\nBuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-buildroot"
1210 "\n\n%description\n"
1215 "\nrm -rf $RPM_BUILD_ROOT"
1217 "\nrm -rf $RPM_BUILD_ROOT"
1219 "\n%defattr(-,root,root,-)"
1223 (rpm-add-change-log-entry "Initial build.\n")))
1225 ;;------------------------------------------------------------
1227 (defun rpm-about-rpm-spec-mode (&optional arg)
1228 "About `rpm-spec-mode'."
1231 (concat "rpm-spec-mode version "
1232 rpm-spec-mode-version
1233 " by Stig Bjørlykke, <stigb@tihlde.org>")))
1236 (add-to-list 'auto-mode-alist '("\\.spec$" . rpm-spec-mode))
1238 (provide 'rpm-spec-mode)
1240 ;;; rpm-spec-mode.el ends here