-
Notifications
You must be signed in to change notification settings - Fork 1
/
install.el
605 lines (550 loc) · 23.8 KB
/
install.el
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
;;; install.el --- Package to ease installation of Elisp packages
;; Copyright (C) 2001, 2003, 2004, 2005, 2006 Stefan Monnier
;; Author: Stefan Monnier <[email protected]>
;; This file is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2, or (at your option)
;; any later version.
;; This file is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;; You should have received a copy of the GNU General Public License
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.
;;; Commentary:
;; This little package is meant to ease up the task of installing
;; third party ELisp packages. I.e. it takes care of placing it in
;; an appropriate location, finds out what code is necessary to get
;; the package activated, sets up your .emacs file to activate the
;; package, and byte-compiles the package.
;; It should work on both single-file packages and tarballs.
;; On tarball packages, it does a bit of guess work to figure out
;; where are which files and how to use them. This is bound to
;; fail sometimes.
;; The main entry points are the following:
;; M-x install-file RET
;; Takes a file as an argument (an elisp file or a tarball) and installs
;; and activates it. This includes copying/untarring into the
;; site-lisp area.
;;
;; M-x install-buffer RET
;; Same thing but with a buffer (elisp or tar mode).
;;
;; M-x install-directory-inplace RET
;; Takes a directory containing an elisp package (probably tarball you
;; just untarred) and sets it up so it can be used (byte-compiles files,
;; sets up autoloads), and activates it. All of this without copying it
;; into the site-lisp area.
;;
;; M-x install-list-packages RET
;; A placeholder for what should become a UI that will allow you to list
;; available/installed/activated packages, activate/deactivate them,
;; install/uninstall, ...
;; Tested on:
;; + ProofGeneral
;; + sml-mode
;; + AUCTeX (missed the info page)
;; + X-Symbol (as an XEmacs package)
;; + Gnus (but doesn't install the info doc :-( )
;; + BBDB (misses the main info page)
;; - WhizzyTeX (needs to hack the perl script and stuff :-( )
;; ? ECB
;; ? JDEE
;; ? preview-latex
;; ? VM
;; ? mmm-mode
;; ? Semantic
;; The above list should be taken with a heavy grain of salt: each bullet
;; was true at some point, but the code of install.el hs changed since and
;; the code of those packages as well.
;; The on-disk structure is as follows:
;; - there are two area: the `home' and the `site' each with their
;; respective directory (~/lib/emacs and /usr/share/emacs/site-lisp)
;; and file (.emacs and site-start).
;; - There is a distinction between installing and activating.
;; Installing only places the files on disk, whereas activating sets up
;; autoloads and friends.
;; - Activation is done on a directory by directory basis. Each directory
;; has an `autoloads' file. Loading it activates the package(s)
;; in directory.
;; - Single-file packages are placed together in the toplevel directory
;; whereas tarball-packages are placed in their own subdirectory (so they
;; can be activated independently).
;;; Todo:
;; - don't ask whether to activate site-wide packages installed in home.
;; - Create Info from Texinfo when needed.
;; - Try harder to find Info files such as doc/auctex.
;; - UI to (un)install and (de)activate packages, get a list, ...
;; - If a single-file package lacks ;;;###autoload, try to add them
;; based on the Commentary section or something.
;; - don't prompt for site-wide/home from within the command. Only do that
;; from the interactive spec, if at all.
;; - install-activate should be called install-save-activation.
;; Better separate activation and writing the activation command.
;; - don't use (load "/foo/bar/baz/autoloads" 'install) but rather something
;; like (install-activate "baz") together with a search path like
;; install-package-dirs (or maybe reuse load-path for that).
;; - maybe in install-activate remember load-file-name so as to know which
;; file to modify if the user wants to remove the activation command.
;;; Code:
(require 'em-glob)
(eval-when-compile (require 'cl))
(defgroup install nil
"Elisp package installation")
(defmacro install-filter (list exp)
(declare (debug t))
`(let ((res nil))
(dolist (x ,list (nreverse res))
(if ,exp (push x res)))))
(defcustom install-site-file
(or
(locate-file (or site-run-file "site-start") load-path load-suffixes)
(let ((lp (mapcar 'abbreviate-file-name load-path)))
;; Prefer non-user directories.
(setq lp (or (install-filter lp (not (string-match "\\`~/" x))) lp))
;; Prefer site-lisp directories.
(setq lp (or (install-filter lp (string-match "/site-lisp\\'" x)) lp))
;; Prefer shorter directory names (i.e. parents rather than subdirs).
(setq lp (sort lp (lambda (d1 d2) (< (length d1) (length d2)))))
;;
(expand-file-name (concat (or site-run-file "site-start") ".el") (car lp))))
"Customization file where we store site-wide activation settings."
:type 'file)
(defcustom install-site-dir (file-name-directory install-site-file)
"Directory where we install site-wide packages."
:type 'directory)
(defcustom install-home-file (or user-init-file
(convert-standard-filename "~/.emacs"))
"Customization file where we store user-specific activation settings."
:type 'file)
(defcustom install-home-dir
;; FIXME: We should be careful never to choose one of Emacs's own
;; directories, even if the user installed Emacs in his home dir.
(let ((lp (mapcar 'abbreviate-file-name load-path)))
;; Only consider writable directories.
(setq lp (install-filter lp (file-writable-p x)))
;; Only consider user directories.
(setq lp (install-filter lp (string-match "\\`~/" x)))
;; Prefer shorter directory names (i.e. parents rather than subdirs).
(setq lp (sort lp (lambda (d1 d2) (< (length d1) (length d2)))))
;; Default to ~/lib/emacs.
(if (or (null lp)
;; If it's a subdir of lib/emacs, use lib/emacs. This can happen
;; because Install does not automatically add lib/emacs to the
;; load-path if it only installs tar packages underneath.
(string-match "\\`~/lib/emacs/" (car lp)))
"~/lib/emacs/"
(car lp)))
"Directory into which we install user-specific elisp packages."
:type 'directory)
(defconst install-autoload-file "autoloads"
"Name of autoload files used by Install.")
(defcustom install-compress-source-files nil ;; ".gz"
"If non-nil, Install will try to compress file.")
(defcustom install-byte-compile t
"If non-nil, elisp files are byte-compiled during installation."
:type 'boolean)
;;
(defun install-get-dir ()
"Return the directory into which to install packages."
(or (and (file-writable-p install-site-dir)
(y-or-n-p "Install site-wide? ")
install-site-dir)
(progn
(unless (file-writable-p install-home-dir)
(setq install-home-dir
(let ((default-directory
(expand-file-name
(file-name-as-directory install-home-dir))))
(read-directory-name "Directory to install into: ")))
(unless (file-directory-p install-home-dir)
(make-directory install-home-dir t)))
install-home-dir)))
(defun install-get-file ()
"Return the file into which to activate packages."
(or (and (file-writable-p install-site-file)
(y-or-n-p "Activate site-wide? ")
install-site-file)
install-home-file))
(defmacro install-with-file (file &rest body)
(declare (debug t) (indent 1))
`(let ((install-with-existing-file (find-buffer-visiting ,file)))
(with-current-buffer (or install-with-existing-file
(find-file-noselect ,file))
(prog1 (save-current-buffer ,@body)
(unless install-with-existing-file
(kill-buffer (current-buffer)))))))
;;;###autoload
(defun install-file (file)
(interactive "fFile to install: ")
(with-current-buffer (find-file-noselect file)
(install-buffer)))
;;;###autoload
(defun install-buffer ()
"Install the current elisp buffer as a package.
The package is install in `install-home-dir', autoloads are added
to the `install-autoload-file' in that directory and the
`install-custom-file' is then updated to load these autoloads."
(interactive)
(cond
((derived-mode-p 'tar-mode) (install-tar-buffer))
((not (derived-mode-p 'emacs-lisp-mode))
(error "I only know how to install tar.gz and elisp files."))
(t
(install-elisp-buffer))))
(defun install-elisp-buffer ()
(let* ((install-dir (install-get-dir))
(package (file-name-nondirectory buffer-file-name))
(file (expand-file-name package install-dir))
(autoload (expand-file-name install-autoload-file install-dir)))
(when (and install-compress-source-files
(string-match "\\.el\\'" file))
(setq file (concat file (if (stringp install-compress-source-files)
install-compress-source-files ".gz"))))
;; Install the elisp file.
(write-region (point-min) (point-max) file)
;; Extract the autoloads into a separate file.
(install-update-autoloads autoload)
;; Activate.
(install-activate autoload)
;; Finally, byte compile. In the present case (a single-file package),
;; this could be done before activation.
(if install-byte-compile
(byte-compile-file file))))
(defun install-tar-buffer ()
"Like `install-buffer' but for a tar package rather than single file."
(let* ((name (file-name-nondirectory buffer-file-name))
;; Strip off ".tar.gz", ".tar", ".tgz", ".tar.Z", ...
(name (if (string-match "\\.[tT][^.]+\\(\\.[^.]+\\)?\\'" name)
(substring name 0 (match-beginning 0)) name))
(install-dir (install-get-dir))
(default-directory (expand-file-name (file-name-as-directory name)
install-dir)))
;; Install the files.
;; FIXME: check what `tar-untar-buffer' does with symlinks and stuff.
(if (not (file-directory-p default-directory))
(make-directory default-directory)
(unless (y-or-n-p (format "%s already exists. Overwite? "
default-directory))
(error "Abort")))
(tar-untar-buffer)
(let ((files (directory-files default-directory
nil "\\`\\([^.]\\|\\.[^.]\\|\\.\\..\\)" t)))
;; If the tar file already had everything under a single directory,
;; remove the redundant level of directory.
(when (and (= (length files) 1) (file-directory-p (car files)))
(let* ((f (car files))
;; Keep the longest name of the two, assuming that the
;; difference is that the longer one has a version number.
(final (if (> (length name) (length f)) name f))
(temp (if (= (length name) (length f)) (concat f ".tmp") f)))
;; FIXME: the dir might already exist.
(rename-file f (expand-file-name temp install-dir))
(setq default-directory
(expand-file-name (file-name-as-directory install-dir)))
(delete-directory name)
;; FIXME: the dir might already exist.
(unless (equal final temp) (rename-file temp final))
(setq name final)
(setq default-directory (expand-file-name final)))))
(install-directory-inplace default-directory)))
;;;###autoload
(defun install-directory-inplace (dir)
"Prepare and activate the current directory for use by Emacs.
Sets up the autoload files, activates them and byte-compiles if needed."
(interactive
(list (if (derived-mode-p 'dired-mode)
list-buffers-directory
(read-directory-name "Directory to install: " nil nil t))))
(let ((default-directory (expand-file-name (file-name-as-directory dir))))
;; Extract the autoloads.
(install-setup-tree)
;; Activate the package.
(install-activate (expand-file-name (install-get-activation-file)))
;; Finally, byte-compile the files.
(if install-byte-compile
(install-byte-compile-dir))))
(defun install-dirs-of-files (files)
"Return a list of subdirs containing elisp files."
(let ((dirs nil)
(ignore (regexp-opt
(cons
;; Ignore contrib directories because they tend to contain
;; either less-debugged code, or packages that might
;; already be installed and can thus interfere.
"contrib/"
(let ((exts nil))
(dolist (ext completion-ignored-extensions exts)
(if (eq (aref ext (1- (length ext))) ?/)
(push ext exts))))))))
;; Collect the dirs that hold elisp files.
(dolist (file files dirs)
(let ((dir (file-name-directory file)))
(unless (or (member dir dirs)
(and dir (string-match ignore dir)))
(push dir dirs))))))
(defun install-find-elisp-dirs ()
"Return a list of subdirs containing elisp files."
(install-dirs-of-files (install-glob "**/*.el")))
(defun install-byte-compile-dir ()
"Byte compile all elisp files under the current directory."
(let ((load-path (append (mapcar (lambda (dir)
(if dir
(expand-file-name dir)
default-directory))
(install-find-elisp-dirs))
load-path)))
(byte-recompile-directory default-directory 0)))
(defun install-glob (pattern)
(let ((res (eshell-extended-glob pattern)))
(if (listp res) res)))
(defun install-get-activation-file ()
"Return the file to load to activate the package.
This is usually \"./autoloads\", but it can also be \"lisp/foo-site.el\"."
(if (file-exists-p install-autoload-file)
install-autoload-file
(or (car (install-glob (concat "**/" install-autoload-file)))
(car (install-glob "**/auto-autoloads.el"))
(car (install-glob "**/*-site.el")))))
(defun install-setup-tree ()
(eshell-glob-initialize)
;; Look for elisp files.
(let ((dirs (install-find-elisp-dirs))
(autoload-files nil)
(toplevel nil))
;; Prepare each elisp subdir and collect info along the way.
;; PROBLEM: if a dir that contains elisp files contains no autoload, we
;; don't know whether to add it to load-path or not: if it's a library
;; it should be added, but it might be some unimportant `contrib' or
;; testing code that should be left alone.
(dolist (dir dirs)
(let ((default-directory (expand-file-name (or dir default-directory))))
;; Remove *.elc files, in case they were not compiled for our version.
(mapc 'delete-file (install-glob "*.elc"))
;; Extract autoloads.
(let ((sites (or (install-glob "auto-autoloads.el")
(install-glob "*-load.el")
(install-glob "*-site.el"))))
(if (= 1 (length sites))
;; Some packages come with a <pkg>-site.el file instead
;; of using autoloads. In that case, just load that file.
(progn
(install-sanitize-autoloads-file (car sites))
(push (concat dir (car sites)) autoload-files))
;; Otherwise. Make an autoloads file and load it.
;; FIXME: Don't make hundreds of autoload files.
(let ((exists (file-exists-p install-autoload-file)))
(if (not (install-update-autoloads install-autoload-file))
;; Don't stupidly add empty autoloads files.
;; PROBLEM: this removes DIR from load-path as well.
(unless exists (delete-file install-autoload-file))
(push (concat dir install-autoload-file) autoload-files)))))))
(unless autoload-files
;; We didn't find any autoload file. I.e. there's nothing to load.
;; Most likely this package should be used via `require', like elib.
(setq autoload-files
(mapcar (lambda (dir) (concat dir install-autoload-file)) dirs))
(mapc 'install-sanitize-autoloads-file autoload-files))
;; Setup the toplevel activation file.
(if (and (= 1 (length autoload-files))
(equal (car autoload-files) (install-get-activation-file)))
(setq toplevel (car autoload-files))
(setq toplevel install-autoload-file)
(dolist (file autoload-files)
(unless (equal file toplevel)
(install-activate
`(expand-file-name
,(file-relative-name
(expand-file-name file)
(file-name-directory (expand-file-name toplevel)))
(file-name-directory load-file-name))
toplevel))))
;; Make up an info/dir file if necessary and register the info dirs.
(let ((info-dirs (install-make-info)))
(when info-dirs
(install-with-file toplevel
(unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode))
(goto-char (point-min))
(unless (re-search-forward "(add-to-list[ \t\n]+'Info-default-directory-list" nil t)
(forward-comment (point-max))
(while (re-search-backward "^" nil t))
(unless (bolp) (newline))
(let ((top-dir (file-name-directory (expand-file-name toplevel))))
(dolist (dir info-dirs)
(setq dir (expand-file-name (or dir default-directory)))
(if (equal dir top-dir)
(insert "(add-to-list 'Info-default-directory-list (file-name-directory load-file-name))\n")
(let ((text (pp-to-string (file-relative-name dir top-dir))))
(if (string-match "\n\\'" text)
(setq text (substring text 0 -1)))
(insert "(add-to-list 'Info-default-directory-list\n"
" (expand-file-name " text
" (file-name-directory load-file-name)))\n")))))
(save-buffer 0)))))))
(defun install-sanitize-autoloads-file (file)
"Make sure that the autoload file FILE exists and behaves sanely."
(install-with-file file
(unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode))
;; Make file and buffer writable (for CVSREAD=on kind of situations).
(when buffer-read-only
(set-file-modes buffer-file-name
(logior ?\200 (file-modes buffer-file-name)))
(toggle-read-only))
(goto-char (point-min))
;; Insert a little boiler plate if there's nothing yet.
(when (eobp)
(insert ";;; " (file-name-nondirectory file)
" --- automatically extracted autoloads\n"
";;\n"
";;; Code:\n\n"
"\n;; Local Variables:\n"
";; version-control: never\n"
";; no-byte-compile: t\n"
";; no-update-autoloads: t\n"
";; End:\n"
";;; " (file-name-nondirectory file)
" ends here\n")
(goto-char (point-min)))
;; Make sure it will setup the load path properly.
(unless (re-search-forward "\\<load-file-name\\>" nil t)
(forward-comment (point-max))
(while (re-search-backward "^" nil t))
(unless (bolp) (newline))
(unless (eq (char-before (1- (point))) ?\n) (newline))
(insert ";; Tell Emacs to look for elisp files in this directory."
;; Add some sort of signature.
" -- Install\n")
(insert "(add-to-list 'load-path
(or (file-name-directory load-file-name) (car load-path)))\n\n")
(save-buffer 0)))
file)
(defvar generated-autoload-file)
(defun install-update-autoloads (autoload)
"Update file AUTOLOAD. This will create the file if necessary.
Returns non-nil if there is anything autoloaded into it."
(setq autoload (expand-file-name autoload))
(let ((bufp (find-buffer-visiting autoload)))
(let ((generated-autoload-file (install-sanitize-autoloads-file autoload)))
;; (update-file-autoloads file)
(update-directory-autoloads (file-name-directory autoload)))
;; Make sure the file sets up the load-path appropriately.
(with-current-buffer (find-file-noselect autoload)
(unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode))
(goto-char (point-min))
(re-search-forward "^" nil t) ;Find the first autoload entry.
(forward-comment (point-max))
(prog1 (not (eobp))
(unless bufp (kill-buffer (current-buffer)))))))
(defun install-activate (autoload &optional into)
"Update INTO to make sure it loads AUTOLOAD.
AUTOLOAD can be an expression.
If it is a string, this also loads it into the currently running Emacs.
If provided, INTO specifies the file which should load AUTOLOAD.
The default is to use `install-get-file'."
(when (stringp autoload)
(setq autoload (abbreviate-file-name autoload))
(load autoload))
(install-with-file (or into (install-get-file))
(unless (derived-mode-p 'emacs-lisp-mode) (emacs-lisp-mode))
(save-excursion
(let ((text (pp-to-string autoload)))
(if (string-match "\n\\'" text)
(setq text (substring text 0 -1)))
(goto-char (point-min))
(unless (re-search-forward (regexp-quote text) nil t)
(goto-char (point-min))
(forward-comment (point-max))
(while (re-search-backward "^" nil t))
(unless (bolp) (newline))
;; Pass `install' as argument to load: this both makes Emacs
;; ignore the load if the file is missing and is used as a marker
;; indicating that this load statement was introduced by us.
(insert "(load " text " 'install)\n")
(save-buffer))))))
;;;###autoload
(defun install-list-packages ()
"Show the installed packages."
(interactive)
(dired (install-get-dir)))
;; Info files and DIR files.
;; Some of this should probably be moved to info.el.
(defconst install-info-dir "-*- Text -*-\n\n\
File: dir Node: Top This is the top of the INFO tree\
\n\n* Menu:\n\n"
"Text content of a barebones empty `info/dir' file.")
(defun install-find-info-files ()
(let ((files (or (install-glob "**/*.info*")
(install-glob "**/info/*")))
(tmp nil))
(dolist (f files)
(unless (or (member f '("dir" "localdir"))
(and (string-match "-[0-9]+" f)
(member (replace-match "" t t f) files))
(not (string-match "\\.info\\>\\|\\(\\`\\|/\\)[^.]+\\(\\'\\|\\.\\(gz\\|Z\\)\\)" f)))
(push f tmp)))
tmp))
(defun install-make-info ()
"Make an info/dir file if necessary and return the info directories."
;; FIXME: This should create the info files from the Texinfo files
;; if necessary !!
;; Problems to do that:
;; - detect when necessary. E.g. BBDB comes with an info page for
;; the bbdb-filters stuff, but the main bbdb doc is in texinfo.
;; - figure out how to makeinfo the thing. E.g. AucTeX comes with
;; a whole bunch of Texinfo files and it's really not clear which
;; is the right one.
;; - The info file might be there, but not found. E.e. AucTeX has its
;; page in doc/auctex.
(let* ((files (install-find-info-files))
(dirs (install-dirs-of-files files))
(dir-files nil))
;; Remove files that were in ignored directories.
(dolist (file files)
(unless (member (file-name-directory file) dirs)
(setq files (delq file files))))
;; Check that there's something to do.
(when files
(assert dirs)
(dolist (dir dirs)
(if (file-exists-p (expand-file-name "dir" dir))
(push (expand-file-name "dir" dir) dir-files)))
(unless dir-files
;; Pick the dir closest to the toplevel to put the main dir file.
(setq dirs (sort dirs (lambda (s1 s2) (< (length s1) (length s2)))))
(install-with-file (expand-file-name "dir" (car dirs))
(assert (= (point-min) (point-max)))
(insert install-info-dir)
(narrow-to-region (point) (point-max))
(dolist (file files)
(let ((section "Miscellaneous")
(entry nil))
(install-with-file file
(goto-char (point-min))
(if (not (re-search-forward
(concat "^START-INFO-DIR-ENTRY\n"
"\\([* \t].*\n\\)+"
"END-INFO-DIR-ENTRY$") nil t))
;; No entry in the file, let's build a default one.
(let ((base (file-name-nondirectory
(file-name-sans-extension file))))
(setq entry (concat "* " (upcase base)
": (" base ").\n")))
(setq entry (match-string 1))
(goto-char (point-min))
(when (re-search-forward
"^INFO-DIR-SECTION[ \t]+\\(.*[^ \t\n]\\)" nil t)
(setq section (match-string 1)))))
(goto-char (point-min))
(unless (search-forward entry nil t)
(unless (re-search-forward (concat "^" (regexp-quote section) "[ \t]*\n") nil 'move)
(unless (bobp) (newline))
(insert section) (newline))
(insert entry))))
(save-buffer 0)
(kill-buffer (current-buffer))))
dirs)))
(provide 'install)
;; arch-tag: 7f3dd74d-b21f-462e-91bd-1e53fa19a2ee
;;; install.el ends here