aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2010-08-30 22:34:52 +0200
committerStefan Monnier2010-08-30 22:34:52 +0200
commite17b68ed837cc835c46b4e518d05810a73997ac5 (patch)
tree72a9fb443a06e264878b9955b529317e1efee50c
parent42b6a73bc70429b7997fa18986a72341f98840f8 (diff)
downloademacs-e17b68ed837cc835c46b4e518d05810a73997ac5.tar.gz
emacs-e17b68ed837cc835c46b4e518d05810a73997ac5.zip
Use SMIE for octave-mode.
* test/indent/octave.m: New file. * lisp/progmodes/octave-mod.el (octave-font-lock-keywords): Use regexp-opt. (octave-mode-map): Remove special bindings for forward/backward-block and octave-backward-up-block. Use smie-close-block. (octave-continuation-marker-regexp): New var. (octave-continuation-regexp): Use it. (octave-operator-table, octave-smie-op-levels) (octave-operator-regexp, octave-smie-indent-rules): New vars. (octave-smie-backward-token, octave-smie-forward-token): New funs. (octave-mode): Use SMIE. (octave-close-block): Delete.
-rw-r--r--lisp/ChangeLog13
-rw-r--r--lisp/progmodes/octave-mod.el193
-rw-r--r--test/ChangeLog8
-rw-r--r--test/indent/Makefile6
-rw-r--r--test/indent/octave.m2318
5 files changed, 2497 insertions, 41 deletions
diff --git a/lisp/ChangeLog b/lisp/ChangeLog
index 65ec210f8ef..8e9c9a7b251 100644
--- a/lisp/ChangeLog
+++ b/lisp/ChangeLog
@@ -1,3 +1,16 @@
12010-08-30 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * progmodes/octave-mod.el (octave-font-lock-keywords): Use regexp-opt.
4 (octave-mode-map): Remove special bindings for forward/backward-block
5 and octave-backward-up-block. Use smie-close-block.
6 (octave-continuation-marker-regexp): New var.
7 (octave-continuation-regexp): Use it.
8 (octave-operator-table, octave-smie-op-levels)
9 (octave-operator-regexp, octave-smie-indent-rules): New vars.
10 (octave-smie-backward-token, octave-smie-forward-token): New funs.
11 (octave-mode): Use SMIE.
12 (octave-close-block): Delete.
13
12010-08-30 Eli Zaretskii <eliz@gnu.org> 142010-08-30 Eli Zaretskii <eliz@gnu.org>
2 15
3 * menu-bar.el (menu-bar-edit-menu) <"Paste">: Check selection in 16 * menu-bar.el (menu-bar-edit-menu) <"Paste">: Check selection in
diff --git a/lisp/progmodes/octave-mod.el b/lisp/progmodes/octave-mod.el
index 11d86ecbde4..bc007180678 100644
--- a/lisp/progmodes/octave-mod.el
+++ b/lisp/progmodes/octave-mod.el
@@ -161,8 +161,8 @@ parenthetical grouping.")
161 (list 161 (list
162 ;; Fontify all builtin keywords. 162 ;; Fontify all builtin keywords.
163 (cons (concat "\\<\\(" 163 (cons (concat "\\<\\("
164 (mapconcat 'identity octave-reserved-words "\\|") 164 (regexp-opt (append octave-reserved-words
165 (mapconcat 'identity octave-text-functions "\\|") 165 octave-text-functions))
166 "\\)\\>") 166 "\\)\\>")
167 'font-lock-keyword-face) 167 'font-lock-keyword-face)
168 ;; Fontify all builtin operators. 168 ;; Fontify all builtin operators.
@@ -223,13 +223,10 @@ parenthetical grouping.")
223 (define-key map "\C-c\C-n" 'octave-next-code-line) 223 (define-key map "\C-c\C-n" 'octave-next-code-line)
224 (define-key map "\C-c\C-a" 'octave-beginning-of-line) 224 (define-key map "\C-c\C-a" 'octave-beginning-of-line)
225 (define-key map "\C-c\C-e" 'octave-end-of-line) 225 (define-key map "\C-c\C-e" 'octave-end-of-line)
226 (define-key map "\C-c\M-\C-n" 'octave-forward-block)
227 (define-key map "\C-c\M-\C-p" 'octave-backward-block)
228 (define-key map "\C-c\M-\C-u" 'octave-backward-up-block)
229 (define-key map "\C-c\M-\C-d" 'octave-down-block) 226 (define-key map "\C-c\M-\C-d" 'octave-down-block)
230 (define-key map "\C-c\M-\C-h" 'octave-mark-block) 227 (define-key map "\C-c\M-\C-h" 'octave-mark-block)
231 (define-key map "\C-c]" 'octave-close-block) 228 (define-key map "\C-c]" 'smie-close-block)
232 (define-key map "\C-c/" 'octave-close-block) 229 (define-key map "\C-c/" 'smie-close-block)
233 (define-key map "\C-c\C-f" 'octave-insert-defun) 230 (define-key map "\C-c\C-f" 'octave-insert-defun)
234 (define-key map "\C-c\C-h" 'octave-help) 231 (define-key map "\C-c\C-h" 'octave-help)
235 (define-key map "\C-c\C-il" 'octave-send-line) 232 (define-key map "\C-c\C-il" 'octave-send-line)
@@ -261,12 +258,9 @@ parenthetical grouping.")
261 ["End of Continuation" octave-end-of-line t] 258 ["End of Continuation" octave-end-of-line t]
262 ["Split Line at Point" octave-indent-new-comment-line t]) 259 ["Split Line at Point" octave-indent-new-comment-line t])
263 ("Blocks" 260 ("Blocks"
264 ["Next Block" octave-forward-block t]
265 ["Previous Block" octave-backward-block t]
266 ["Down Block" octave-down-block t] 261 ["Down Block" octave-down-block t]
267 ["Up Block" octave-backward-up-block t]
268 ["Mark Block" octave-mark-block t] 262 ["Mark Block" octave-mark-block t]
269 ["Close Block" octave-close-block t]) 263 ["Close Block" smie-close-block t])
270 ("Functions" 264 ("Functions"
271 ["Indent Function" octave-indent-defun t] 265 ["Indent Function" octave-indent-defun t]
272 ["Insert Function" octave-insert-defun t]) 266 ["Insert Function" octave-insert-defun t])
@@ -386,8 +380,11 @@ end keywords as associated values.")
386 "Extra indentation applied to Octave continuation lines." 380 "Extra indentation applied to Octave continuation lines."
387 :type 'integer 381 :type 'integer
388 :group 'octave) 382 :group 'octave)
383(eval-and-compile
384 (defconst octave-continuation-marker-regexp "\\\\\\|\\.\\.\\."))
389(defvar octave-continuation-regexp 385(defvar octave-continuation-regexp
390 "[^#%\n]*\\(\\\\\\|\\.\\.\\.\\)\\s-*\\(\\s<.*\\)?$") 386 (concat "[^#%\n]*\\(" octave-continuation-marker-regexp
387 "\\)\\s-*\\(\\s<.*\\)?$"))
391(defcustom octave-continuation-string "\\" 388(defcustom octave-continuation-string "\\"
392 "Character string used for Octave continuation lines. Normally \\." 389 "Character string used for Octave continuation lines. Normally \\."
393 :type 'string 390 :type 'string
@@ -425,6 +422,143 @@ Non-nil means always go to the next Octave code line after sending."
425 :group 'octave) 422 :group 'octave)
426 423
427 424
425;;; SMIE indentation
426
427(require 'smie)
428
429(defconst octave-operator-table
430 '((assoc ";" "\n") (assoc ",") ; The doc claims they have equal precedence!?
431 (right "=" "+=" "-=" "*=" "/=")
432 (assoc "&&") (assoc "||") ; The doc claims they have equal precedence!?
433 (assoc "&") (assoc "|") ; The doc claims they have equal precedence!?
434 (nonassoc "<" "<=" "==" ">=" ">" "!=" "~=")
435 (nonassoc ":") ;No idea what this is.
436 (assoc "+" "-")
437 (assoc "*" "/" "\\" ".\\" ".*" "./")
438 (nonassoc "'" ".'")
439 (nonassoc "++" "--" "!" "~") ;And unary "+" and "-".
440 (right "^" "**" ".^" ".**")
441 ;; It's not really an operator, but for indentation purposes it
442 ;; could be convenient to treat it as one.
443 (assoc "...")))
444
445(defconst octave-smie-op-levels
446 (smie-prec2-levels
447 (smie-merge-prec2s
448 (smie-bnf-precedence-table
449 '((atom)
450 ;; We can't distinguish the first element in a sequence with
451 ;; precedence grammars, so we can't distinguish the condition
452 ;; if the `if' from the subsequent body, for example.
453 ;; This has to be done later in the indentation rules.
454 (exp (exp "\n" exp)
455 ;; We need to mention at least one of the operators in this part
456 ;; of the grammar: if the BNF and the operator table have
457 ;; no overlap, SMIE can't know how they relate.
458 (exp ";" exp)
459 ("try" exp "catch" exp "end_try_catch")
460 ("try" exp "catch" exp "end")
461 ("unwind_protect" exp
462 "unwind_protect_cleanup" exp "end_unwind_protect")
463 ("unwind_protect" exp "unwind_protect_cleanup" exp "end")
464 ("for" exp "endfor")
465 ("for" exp "end")
466 ("do" exp "until" atom)
467 ("while" exp "endwhile")
468 ("while" exp "end")
469 ("if" exp "endif")
470 ("if" exp "else" exp "endif")
471 ("if" exp "elseif" exp "else" exp "endif")
472 ("if" exp "elseif" exp "elseif" exp "else" exp "endif")
473 ("if" exp "elseif" exp "elseif" exp "else" exp "end")
474 ("switch" exp "case" exp "endswitch")
475 ("switch" exp "case" exp "otherwise" exp "endswitch")
476 ("switch" exp "case" exp "case" exp "otherwise" exp "endswitch")
477 ("switch" exp "case" exp "case" exp "otherwise" exp "end")
478 ("function" exp "endfunction")
479 ("function" exp "end"))
480 ;; (fundesc (atom "=" atom))
481 )
482 '((assoc "\n" ";")))
483
484 (smie-precs-precedence-table
485 (append octave-operator-table
486 '((nonassoc " -dummy- "))) ;Bogus anchor at the end.
487 ))))
488
489;; Tokenizing needs to be refined so that ";;" is treated as two
490;; tokens and also so as to recognize the \n separator (and
491;; corresponding continuation lines).
492
493(defconst octave-operator-regexp
494 (regexp-opt (apply 'append (mapcar 'cdr octave-operator-table))))
495
496(defun octave-smie-backward-token ()
497 (let ((pos (point)))
498 (forward-comment (- (point)))
499 (cond
500 ((and (not (eq (char-before) ?\;)) ;Coalesce ";" and "\n".
501 (> pos (line-end-position))
502 (if (looking-back octave-continuation-marker-regexp (- (point) 3))
503 (progn
504 (goto-char (match-beginning 0))
505 (forward-comment (- (point)))
506 nil)
507 t)
508 ;; Ignore it if it's within parentheses.
509 (let ((ppss (syntax-ppss)))
510 (not (and (nth 1 ppss)
511 (eq ?\( (char-after (nth 1 ppss)))))))
512 (skip-chars-forward " \t")
513 ;; Why bother distinguishing \n and ;?
514 ";") ;;"\n"
515 ((and (looking-back octave-operator-regexp (- (point) 3) 'greedy)
516 ;; Don't mistake a string quote for a transpose.
517 (not (looking-back "\\s\"" (1- (point)))))
518 (goto-char (match-beginning 0))
519 (match-string-no-properties 0))
520 (t
521 (smie-default-backward-token)))))
522
523(defun octave-smie-forward-token ()
524 (skip-chars-forward " \t")
525 (when (looking-at (eval-when-compile
526 (concat "\\(" octave-continuation-marker-regexp
527 "\\)[ \t]*\\($\\|[%#]\\)")))
528 (goto-char (match-end 1))
529 (forward-comment 1))
530 (cond
531 ((and (looking-at "$\\|[%#]")
532 ;; Ignore it if it's within parentheses.
533 (prog1 (let ((ppss (syntax-ppss)))
534 (not (and (nth 1 ppss)
535 (eq ?\( (char-after (nth 1 ppss))))))
536 (forward-comment (point-max))))
537 ;; Why bother distinguishing \n and ;?
538 ";") ;;"\n"
539 ((looking-at ";[ \t]*\\($\\|[%#]\\)")
540 ;; Combine the ; with the subsequent \n.
541 (goto-char (match-beginning 1))
542 (forward-comment 1)
543 ";")
544 ((and (looking-at octave-operator-regexp)
545 ;; Don't mistake a string quote for a transpose.
546 (not (looking-at "\\s\"")))
547 (goto-char (match-end 0))
548 (match-string-no-properties 0))
549 (t
550 (smie-default-forward-token))))
551
552(defconst octave-smie-indent-rules
553 '((";"
554 (:parent ("function" "if" "while" "else" "elseif" "for" "otherwise"
555 "case" "try" "catch" "unwind_protect" "unwind_protect_cleanup")
556 ;; FIXME: don't hardcode 2.
557 (+ parent octave-block-offset))
558 ;; (:parent "switch" 4) ;For (invalid) code between switch and case.
559 0)
560 ((:before . "case") octave-block-offset)))
561
428;;;###autoload 562;;;###autoload
429(define-derived-mode octave-mode prog-mode "Octave" 563(define-derived-mode octave-mode prog-mode "Octave"
430 "Major mode for editing Octave code. 564 "Major mode for editing Octave code.
@@ -511,7 +645,17 @@ already added. You just need to add a description of the problem,
511including a reproducible test case and send the message." 645including a reproducible test case and send the message."
512 (setq local-abbrev-table octave-abbrev-table) 646 (setq local-abbrev-table octave-abbrev-table)
513 647
514 (set (make-local-variable 'indent-line-function) 'octave-indent-line) 648 (smie-setup octave-smie-op-levels octave-smie-indent-rules)
649 (set (make-local-variable 'smie-indent-basic) 'octave-block-offset)
650 (set (make-local-variable 'smie-backward-token-function)
651 'octave-smie-backward-token)
652 (set (make-local-variable 'smie-forward-token-function)
653 'octave-smie-forward-token)
654 (set (make-local-variable 'forward-sexp-function)
655 'smie-forward-sexp-command)
656 (set (make-local-variable 'smie-closer-alist)
657 (mapcar (lambda (elem) (cons (car elem) (car (last elem))))
658 octave-block-match-alist))
515 659
516 (set (make-local-variable 'comment-start) octave-comment-start) 660 (set (make-local-variable 'comment-start) octave-comment-start)
517 (set (make-local-variable 'comment-end) "") 661 (set (make-local-variable 'comment-end) "")
@@ -924,29 +1068,6 @@ The block marked is the one that contains point or follows point."
924 (goto-char pos) 1068 (goto-char pos)
925 (message "No block to mark found")))) 1069 (message "No block to mark found"))))
926 1070
927(defun octave-close-block ()
928 "Close the current Octave block on a separate line.
929An error is signaled if no block to close is found."
930 (interactive)
931 (let (bb-keyword)
932 (condition-case nil
933 (progn
934 (save-excursion
935 (octave-backward-up-block 1)
936 (setq bb-keyword (buffer-substring-no-properties
937 (match-beginning 1) (match-end 1))))
938 (if (save-excursion
939 (beginning-of-line)
940 (looking-at "^\\s-*$"))
941 (indent-according-to-mode)
942 (octave-reindent-then-newline-and-indent))
943 (insert (car (reverse
944 (assoc bb-keyword
945 octave-block-match-alist))))
946 (octave-reindent-then-newline-and-indent)
947 t)
948 (error (message "No block to close found")))))
949
950(defun octave-blink-matching-block-open () 1071(defun octave-blink-matching-block-open ()
951 "Blink the matching Octave begin block keyword. 1072 "Blink the matching Octave begin block keyword.
952If point is right after an Octave else or end type block keyword, move 1073If point is right after an Octave else or end type block keyword, move
diff --git a/test/ChangeLog b/test/ChangeLog
index a226e88d883..cf709f01eec 100644
--- a/test/ChangeLog
+++ b/test/ChangeLog
@@ -1,6 +1,10 @@
12010-08-30 Stefan Monnier <monnier@iro.umontreal.ca>
2
3 * indent/octave.m: New file.
4
12010-08-08 Ulf Jasper <ulf.jasper@web.de> 52010-08-08 Ulf Jasper <ulf.jasper@web.de>
2 6
3 * icalendar-testsuite.el (icalendar-testsuite-run): Added internal tests. 7 * icalendar-testsuite.el (icalendar-testsuite-run): Add internal tests.
4 (icalendar-testsuite--trim, icalendar-testsuite--compare-strings) 8 (icalendar-testsuite--trim, icalendar-testsuite--compare-strings)
5 (icalendar-testsuite--run-internal-tests): New. 9 (icalendar-testsuite--run-internal-tests): New.
6 (icalendar-testsuite--test-convert-ordinary-to-ical) 10 (icalendar-testsuite--test-convert-ordinary-to-ical)
@@ -13,7 +17,7 @@
13 (icalendar-testsuite--do-test-cycle): Use icalendar-testsuite--compare-strings 17 (icalendar-testsuite--do-test-cycle): Use icalendar-testsuite--compare-strings
14 (icalendar-testsuite--run-import-tests): Comment added. 18 (icalendar-testsuite--run-import-tests): Comment added.
15 (icalendar-testsuite--run-import-tests) 19 (icalendar-testsuite--run-import-tests)
16 (icalendar-testsuite--run-real-world-tests): Fixed expected results. 20 (icalendar-testsuite--run-real-world-tests): Fix expected results.
17 21
182010-06-25 Chong Yidong <cyd@stupidchicken.com> 222010-06-25 Chong Yidong <cyd@stupidchicken.com>
19 23
diff --git a/test/indent/Makefile b/test/indent/Makefile
index 2a5b7f4c352..9e75f3dad57 100644
--- a/test/indent/Makefile
+++ b/test/indent/Makefile
@@ -8,8 +8,8 @@ clean:
8# - mark the places where the indentation is known to be incorrect, 8# - mark the places where the indentation is known to be incorrect,
9# and allow either ignoring those errors or not. 9# and allow either ignoring those errors or not.
10%.test: % 10%.test: %
11 -$(RM) $<.test 11 -$(RM) $<.new
12 $(EMACS) --batch $< \ 12 $(EMACS) --batch $< \
13 --eval '(indent-region (point-min) (point-max) nil)' \ 13 --eval '(indent-region (point-min) (point-max) nil)' \
14 --eval '(write-region (point-min) (point-max) "$<.test")' 14 --eval '(write-region (point-min) (point-max) "$<.new")'
15 diff -u -B $< $<.test 15 diff -u -B $< $<.new
diff --git a/test/indent/octave.m b/test/indent/octave.m
new file mode 100644
index 00000000000..830af96ed8e
--- /dev/null
+++ b/test/indent/octave.m
@@ -0,0 +1,2318 @@
1## -*- octave -*-
2
3function res = tcomp (fn)
4 %% res = tcomp (fn)
5 %% imports components and rearranges them.
6
7 if nargin ~= 1
8 print_usage()
9 endif
10
11 data = dlmread(fn, 3, 0);
12
13 x = data(:,2:end);
14 y = 'hello';
15 z = y';
16
17 cnty = repmat(x(:,1)(:), 10, 1);
18
19 pop = x(:,1:10)(:);
20 bir = x(:,11:20)(:);
21 dth = x(:,21:30)(:);
22 imig = x(:,31:40)(:);
23 dmig = x(:,41:50)(:);
24 gq = x(:,51:60)(:);
25
26 yrs = repmat(2000:2009, 39, 1)(:);
27
28 res = [yrs, cnty, pop, bir, dth, imig, dmig, gq];
29
30endfunction
31
32## Copyright (C) 2005, 2006, 2007, 2008, 2009 S�ren Hauberg
33##
34## This file is part of Octave.
35##
36## Octave is free software; you can redistribute it and/or modify it
37## under the terms of the GNU General Public License as published by
38## the Free Software Foundation; either version 3 of the License, or (at
39## your option) any later version.
40##
41## Octave is distributed in the hope that it will be useful, but
42## WITHOUT ANY WARRANTY; without even the implied warranty of
43## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
44## General Public License for more details.
45##
46## You should have received a copy of the GNU General Public License
47## along with Octave; see the file COPYING. If not, see
48## <http://www.gnu.org/licenses/>.
49
50## -*- texinfo -*-
51## @deftypefn {Command} pkg @var{command} @var{pkg_name}
52## @deftypefnx {Command} pkg @var{command} @var{option} @var{pkg_name}
53## This command interacts with the package manager. Different actions will
54## be taken depending on the value of @var{command}.
55##
56## @table @samp
57## @item install
58## Install named packages. For example,
59## @example
60## pkg install image-1.0.0.tar.gz
61## @end example
62## @noindent
63## installs the package found in the file @file{image-1.0.0.tar.gz}.
64##
65## The @var{option} variable can contain options that affect the manner
66## in which a package is installed. These options can be one or more of
67##
68## @table @code
69## @item -nodeps
70## The package manager will disable the dependency checking. That way it
71## is possible to install a package even if it depends on another package
72## that's not installed on the system. @strong{Use this option with care.}
73##
74## @item -noauto
75## The package manager will not automatically load the installed package
76## when starting Octave, even if the package requests that it is.
77##
78## @item -auto
79## The package manager will automatically load the installed package when
80## starting Octave, even if the package requests that it isn't.
81##
82## @item -local
83## A local installation is forced, even if the user has system privileges.
84##
85## @item -global
86## A global installation is forced, even if the user doesn't normally have
87## system privileges
88##
89## @item -verbose
90## The package manager will print the output of all of the commands that are
91## performed.
92## @end table
93##
94## @item uninstall
95## Uninstall named packages. For example,
96## @example
97## pkg uninstall image
98## @end example
99## @noindent
100## removes the @code{image} package from the system. If another installed
101## package depends on the @code{image} package an error will be issued.
102## The package can be uninstalled anyway by using the @code{-nodeps} option.
103## @item load
104## Add named packages to the path. After loading a package it is
105## possible to use the functions provided by the package. For example,
106## @example
107## pkg load image
108## @end example
109## @noindent
110## adds the @code{image} package to the path. It is possible to load all
111## installed packages at once with the command
112## @example
113## pkg load all
114## @end example
115## @item unload
116## Removes named packages from the path. After unloading a package it is
117## no longer possible to use the functions provided by the package.
118## This command behaves like the @code{load} command.
119## @item list
120## Show a list of the currently installed packages. By requesting one or two
121## output argument it is possible to get a list of the currently installed
122## packages. For example,
123## @example
124## installed_packages = pkg list;
125## @end example
126## @noindent
127## returns a cell array containing a structure for each installed package.
128## The command
129## @example
130## [@var{user_packages}, @var{system_packages}] = pkg list
131## @end example
132## @noindent
133## splits the list of installed packages into those who are installed by
134## the current user, and those installed by the system administrator.
135## @item describe
136## Show a short description of the named installed packages, with the option
137## '-verbose' also list functions provided by the package, e.g.:
138## @example
139## pkg describe -verbose all
140## @end example
141## @noindent
142## will describe all installed packages and the functions they provide.
143## If one output is requested a cell of structure containing the
144## description and list of functions of each package is returned as
145## output rather than printed on screen:
146## @example
147## desc = pkg ("describe", "secs1d", "image")
148## @end example
149## @noindent
150## If any of the requested packages is not installed, pkg returns an
151## error, unless a second output is requested:
152## @example
153## [ desc, flag] = pkg ("describe", "secs1d", "image")
154## @end example
155## @noindent
156## @var{flag} will take one of the values "Not installed", "Loaded" or
157## "Not loaded" for each of the named packages.
158## @item prefix
159## Set the installation prefix directory. For example,
160## @example
161## pkg prefix ~/my_octave_packages
162## @end example
163## @noindent
164## sets the installation prefix to @file{~/my_octave_packages}.
165## Packages will be installed in this directory.
166##
167## It is possible to get the current installation prefix by requesting an
168## output argument. For example,
169## @example
170## p = pkg prefix
171## @end example
172##
173## The location in which to install the architecture dependent files can be
174## independent specified with an addition argument. For example
175##
176## @example
177## pkg prefix ~/my_octave_packages ~/my_arch_dep_pkgs
178## @end example
179## @item local_list
180## Set the file in which to look for information on the locally
181## installed packages. Locally installed packages are those that are
182## typically available only to the current user. For example
183## @example
184## pkg local_list ~/.octave_packages
185## @end example
186## It is possible to get the current value of local_list with the following
187## @example
188## pkg local_list
189## @end example
190## @item global_list
191## Set the file in which to look for, for information on the globally
192## installed packages. Globally installed packages are those that are
193## typically available to all users. For example
194## @example
195## pkg global_list /usr/share/octave/octave_packages
196## @end example
197## It is possible to get the current value of global_list with the following
198## @example
199## pkg global_list
200## @end example
201## @item rebuild
202## Rebuilds the package database from the installed directories. This can
203## be used in cases where for some reason the package database is corrupted.
204## It can also take the @code{-auto} and @code{-noauto} options to allow the
205## autoloading state of a package to be changed. For example
206##
207## @example
208## pkg rebuild -noauto image
209## @end example
210##
211## will remove the autoloading status of the image package.
212## @item build
213## Builds a binary form of a package or packages. The binary file produced
214## will itself be an Octave package that can be installed normally with
215## @code{pkg}. The form of the command to build a binary package is
216##
217## @example
218## pkg build builddir image-1.0.0.tar.gz @dots{}
219## @end example
220##
221## @noindent
222## where @code{builddir} is the name of a directory where the temporary
223## installation will be produced and the binary packages will be found.
224## The options @code{-verbose} and @code{-nodeps} are respected, while
225## the other options are ignored.
226## @end table
227## @end deftypefn
228
229function [local_packages, global_packages] = pkg (varargin)
230 ## Installation prefix (FIXME: what should these be on windows?)
231 persistent user_prefix = false;
232 persistent prefix = -1;
233 persistent archprefix = -1;
234 persistent local_list = tilde_expand (fullfile ("~", ".octave_packages"));
235 persistent global_list = fullfile (OCTAVE_HOME (), "share", "octave",
236 "octave_packages");
237 mlock ();
238
239 global_install = issuperuser ();
240
241 if (prefix == -1)
242 if (global_install)
243 prefix = fullfile (OCTAVE_HOME (), "share", "octave", "packages");
244 archprefix = fullfile (octave_config_info ("libexecdir"),
245 "octave", "packages");
246 else
247 prefix = fullfile ("~", "octave");
248 archprefix = prefix;
249 endif
250 prefix = tilde_expand (prefix);
251 archprefix = tilde_expand (archprefix);
252 endif
253
254 available_actions = {"list", "install", "uninstall", "load", ...
255 "unload", "prefix", "local_list", ...
256 "global_list", "rebuild", "build","describe"};
257 ## Handle input
258 if (length (varargin) == 0 || ! iscellstr (varargin))
259 print_usage ();
260 endif
261 files = {};
262 deps = true;
263 auto = 0;
264 action = "none";
265 verbose = false;
266 for i = 1:length (varargin)
267 switch (varargin{i})
268 case "-nodeps"
269 deps = false;
270 case "-noauto"
271 auto = -1;
272 case "-auto"
273 auto = 1;
274 case "-verbose"
275 verbose = true;
276 case "-local"
277 global_install = false;
278 if (! user_prefix)
279 prefix = tilde_expand (fullfile ("~", "octave"));
280 archprefix = prefix;
281 endif
282 case "-global"
283 global_install = true;
284 if (! user_prefix)
285 prefix = fullfile (OCTAVE_HOME (), "share", "octave", "packages");
286 archprefix = fullfile (octave_config_info ("libexecdir"),
287 "octave", "packages");
288 endif
289 case available_actions
290 if (strcmp (action, "none"))
291 action = varargin{i};
292 else
293 error ("more than one action specified");
294 endif
295 otherwise
296 files{end+1} = varargin{i};
297 endswitch
298 endfor
299
300 ## Take action
301 switch (action)
302 case "list"
303 if (nargout == 0)
304 installed_packages (local_list, global_list);
305 elseif (nargout == 1)
306 local_packages = installed_packages (local_list, global_list);
307 elseif (nargout == 2)
308 [local_packages, global_packages] = installed_packages (local_list,
309 global_list);
310 else
311 error ("too many output arguments requested");
312 endif
313
314 case "install"
315 if (length (files) == 0)
316 error ("you must specify at least one filename when calling 'pkg install'");
317 endif
318 install (files, deps, auto, prefix, archprefix, verbose, local_list,
319 global_list, global_install);
320
321 case "uninstall"
322 if (length (files) == 0)
323 error ("you must specify at least one package when calling 'pkg uninstall'");
324 endif
325 uninstall (files, deps, verbose, local_list,
326 global_list, global_install);
327
328 case "load"
329 if (length (files) == 0)
330 error ("you must specify at least one package, 'all' or 'auto' when calling 'pkg load'");
331 endif
332 load_packages (files, deps, local_list, global_list);
333
334 case "unload"
335 if (length (files) == 0)
336 error ("you must specify at least one package or 'all' when calling 'pkg unload'");
337 endif
338 unload_packages (files, deps, local_list, global_list);
339
340 case "prefix"
341 if (length (files) == 0 && nargout == 0)
342 printf ("Installation prefix: %s\n", prefix);
343 printf ("Architecture dependent prefix: %s\n", archprefix);
344 elseif (length (files) == 0 && nargout >= 1)
345 local_packages = prefix;
346 global_packages = archprefix;
347 elseif (length (files) >= 1 && nargout <= 2 && ischar (files{1}))
348 prefix = files{1};
349 prefix = absolute_pathname (prefix);
350 local_packages = prefix;
351 user_prefix = true;
352 if (length (files) >= 2 && ischar (files{2}))
353 archprefix = files{2};
354 try
355 archprefix = absolute_pathname (archprefix);
356 catch
357 mkdir (archprefix);
358 warning ("creating the directory %s\n", archprefix);
359 archprefix = absolute_pathname (archprefix);
360 end_try_catch
361 global_packages = archprefix;
362 endif
363 else
364 error ("you must specify a prefix directory, or request an output argument");
365 endif
366
367 case "local_list"
368 if (length (files) == 0 && nargout == 0)
369 disp (local_list);
370 elseif (length (files) == 0 && nargout == 1)
371 local_packages = local_list;
372 elseif (length (files) == 1 && nargout == 0 && ischar (files{1}))
373 try
374 local_list = absolute_pathname (files{1});
375 catch
376 ## Force file to be created
377 fclose (fopen (files{1}, "wt"));
378 local_list = absolute_pathname (files{1});
379 end_try_catch
380 else
381 error ("you must specify a local_list file, or request an output argument");
382 endif
383
384 case "global_list"
385 if (length (files) == 0 && nargout == 0)
386 disp(global_list);
387 elseif (length (files) == 0 && nargout == 1)
388 local_packages = global_list;
389 elseif (length (files) == 1 && nargout == 0 && ischar (files{1}))
390 try
391 global_list = absolute_pathname (files{1});
392 catch
393 ## Force file to be created
394 fclose (fopen (files{1}, "wt"));
395 global_list = absolute_pathname (files{1});
396 end_try_catch
397 else
398 error ("you must specify a global_list file, or request an output argument");
399 endif
400
401 case "rebuild"
402 if (global_install)
403 global_packages = rebuild (prefix, archprefix, global_list, files,
404 auto, verbose);
405 global_packages = save_order (global_packages);
406 save (global_list, "global_packages");
407 if (nargout > 0)
408 local_packages = global_packages;
409 endif
410 else
411 local_packages = rebuild (prefix, archprefix, local_list, files, auto,
412 verbose);
413 local_packages = save_order (local_packages);
414 save (local_list, "local_packages");
415 if (nargout == 0)
416 clear ("local_packages");
417 endif
418 endif
419
420 case "build"
421 if (length (files) < 2)
422 error ("you must specify at least the build directory and one filename\nwhen calling 'pkg build'");
423 endif
424 build (files, deps, auto, verbose);
425
426 case "describe"
427 if (length (files) == 0)
428 error ("you must specify at least one package or 'all' when calling 'pkg describe'");
429 endif
430 ## FIXME: the name of the output variables is inconsistent
431 ## with their content
432 switch (nargout)
433 case 0
434 describe (files, verbose, local_list, global_list);
435 case 1
436 pkg_desc_list = describe (files, verbose, local_list, ...
437 global_list);
438 local_packages = pkg_desc_list;
439 case 2
440 [pkg_desc_list, flag] = describe (files, verbose, local_list, ...
441 global_list);
442 local_packages = pkg_desc_list;
443 global_packages = flag;
444 otherwise
445 error ("you can request at most two outputs when calling 'pkg describe'");
446 endswitch
447
448 otherwise
449 error ("you must specify a valid action for 'pkg'. See 'help pkg' for details");
450 endswitch
451endfunction
452
453function descriptions = rebuild (prefix, archprefix, list, files, auto, verbose)
454 if (isempty (files))
455 [dirlist, err, msg] = readdir (prefix);
456 if (err)
457 error ("couldn't read directory %s: %s", prefix, msg);
458 endif
459 ## the two first entries of dirlist are "." and ".."
460 dirlist([1,2]) = [];
461 else
462 old_descriptions = installed_packages (list, list);
463 wd = pwd ();
464 unwind_protect
465 cd (prefix);
466 dirlist = glob (cellfun(@(x) cstrcat(x, '-*'), files, 'UniformOutput', 0));
467 unwind_protect_cleanup
468 cd (wd);
469 end_unwind_protect
470 endif
471 descriptions = {};
472 for k = 1:length (dirlist)
473 descfile = fullfile (prefix, dirlist{k}, "packinfo", "DESCRIPTION");
474 if (verbose)
475 printf ("recreating package description from %s\n", dirlist{k});
476 endif
477 if (exist (descfile, "file"))
478 desc = get_description (descfile);
479 desc.dir = fullfile (prefix, dirlist{k});
480 desc.archprefix = fullfile (archprefix, cstrcat (desc.name, "-",
481 desc.version));
482 if (auto != 0)
483 if (exist (fullfile (desc.dir, "packinfo", ".autoload"), "file"))
484 unlink (fullfile (desc.dir, "packinfo", ".autoload"));
485 endif
486 if (auto < 0)
487 desc.autoload = 0;
488 elseif (auto > 0)
489 desc.autoload = 1;
490 fclose (fopen (fullfile (desc.dir, "packinfo", ".autoload"), "wt"));
491 endif
492 else
493 if (exist (fullfile (desc.dir, "packinfo", ".autoload"), "file"))
494 desc.autoload = 1;
495 else
496 desc.autoload = 0;
497 endif
498 endif
499 descriptions{end + 1} = desc;
500 elseif (verbose)
501 warning ("directory %s is not a valid package", dirlist{k});
502 endif
503 endfor
504
505 if (! isempty (files))
506 ## We are rebuilding for a particular package(s) so we should take
507 ## care to keep the other untouched packages in the descriptions
508 descriptions = {descriptions{:}, old_descriptions{:}};
509
510 dup = [];
511 for i = 1:length (descriptions)
512 if (find (dup, i))
513 continue;
514 endif
515 for j = (i+1):length (descriptions)
516 if (find (dup, j))
517 continue;
518 endif
519 if (strcmp (descriptions{i}.name, descriptions{j}.name))
520 dup = [dup, j];
521 endif
522 endfor
523 endfor
524 if (! isempty (dup))
525 descriptions (dup) = [];
526 endif
527 endif
528endfunction
529
530function build (files, handle_deps, autoload, verbose)
531 if (length (files) < 1)
532 error ("insufficient number of files");
533 endif
534 builddir = files{1};
535 if (! exist (builddir, "dir"))
536 warning ("creating build directory %s", builddir);
537 [status, msg] = mkdir (builddir);
538 if (status != 1)
539 error ("could not create installation directory: %s", msg);
540 endif
541 endif
542 builddir = absolute_pathname (builddir);
543 installdir = fullfile (builddir, "install");
544 if (! exist (installdir, "dir"))
545 [status, msg] = mkdir (installdir);
546 if (status != 1)
547 error ("could not create installation directory: %s", msg);
548 endif
549 endif
550 files(1) = [];
551 buildlist = fullfile (builddir, "octave_packages");
552 install (files, handle_deps, autoload, installdir, installdir, verbose,
553 buildlist, "", false);
554 unwind_protect
555 repackage (builddir, buildlist);
556 unwind_protect_cleanup
557 unload_packages ({"all"}, handle_deps, buildlist, "");
558 if (exist (installdir, "dir"))
559 rm_rf (installdir);
560 endif
561 if (exist (buildlist, "file"))
562 unlink (buildlist);
563 endif
564 end_unwind_protect
565endfunction
566
567function install (files, handle_deps, autoload, prefix, archprefix, verbose,
568 local_list, global_list, global_install)
569
570 ## Check that the directory in prefix exist. If it doesn't: create it!
571 if (! exist (prefix, "dir"))
572 warning ("creating installation directory %s", prefix);
573 [status, msg] = mkdir (prefix);
574 if (status != 1)
575 error ("could not create installation directory: %s", msg);
576 endif
577 endif
578
579 ## Get the list of installed packages.
580 [local_packages, global_packages] = installed_packages (local_list,
581 global_list);
582
583 installed_pkgs_lst = {local_packages{:}, global_packages{:}};
584
585 if (global_install)
586 packages = global_packages;
587 else
588 packages = local_packages;
589 endif
590
591 ## Uncompress the packages and read the DESCRIPTION files.
592 tmpdirs = packdirs = descriptions = {};
593 try
594 ## Warn about non existent files.
595 for i = 1:length (files)
596 if (isempty (glob(files{i})))
597 warning ("file %s does not exist", files{i});
598 endif
599 endfor
600
601 ## Unpack the package files and read the DESCRIPTION files.
602 files = glob (files);
603 packages_to_uninstall = [];
604 for i = 1:length (files)
605 tgz = files{i};
606
607 if (exist (tgz, "file"))
608 ## Create a temporary directory.
609 tmpdir = tmpnam ();
610 tmpdirs{end+1} = tmpdir;
611 if (verbose)
612 printf ("mkdir (%s)\n", tmpdir);
613 endif
614 [status, msg] = mkdir (tmpdir);
615 if (status != 1)
616 error ("couldn't create temporary directory: %s", msg);
617 endif
618
619 ## Uncompress the package.
620 if (verbose)
621 printf ("untar (%s, %s)\n", tgz, tmpdir);
622 endif
623 untar (tgz, tmpdir);
624
625 ## Get the name of the directories produced by tar.
626 [dirlist, err, msg] = readdir (tmpdir);
627 if (err)
628 error ("couldn't read directory produced by tar: %s", msg);
629 endif
630
631 if (length (dirlist) > 3)
632 error ("bundles of packages are not allowed")
633 endif
634 endif
635
636 ## The filename pointed to an uncompressed package to begin with.
637 if (exist (tgz, "dir"))
638 dirlist = {".", "..", tgz};
639 endif
640
641 if (exist (tgz, "file") || exist (tgz, "dir"))
642 ## The two first entries of dirlist are "." and "..".
643 if (exist (tgz, "file"))
644 packdir = fullfile (tmpdir, dirlist{3});
645 else
646 packdir = fullfile (pwd(), dirlist{3});
647 endif
648 packdirs{end+1} = packdir;
649
650 ## Make sure the package contains necessary files.
651 verify_directory (packdir);
652
653 ## Read the DESCRIPTION file.
654 filename = fullfile (packdir, "DESCRIPTION");
655 desc = get_description (filename);
656
657 ## Verify that package name corresponds with filename.
658 [dummy, nm] = fileparts (tgz);
659 if ((length (nm) >= length (desc.name))
660 && ! strcmp (desc.name, nm(1:length(desc.name))))
661 error ("package name '%s' doesn't correspond to its filename '%s'",
662 desc.name, nm);
663 endif
664
665 ## Set default installation directory.
666 desc.dir = fullfile (prefix, cstrcat (desc.name, "-", desc.version));
667
668 ## Set default architectire dependent installation directory.
669 desc.archprefix = fullfile (archprefix, cstrcat (desc.name, "-",
670 desc.version));
671
672 ## Save desc.
673 descriptions{end+1} = desc;
674
675 ## Are any of the new packages already installed?
676 ## If so we'll remove the old version.
677 for j = 1:length (packages)
678 if (strcmp (packages{j}.name, desc.name))
679 packages_to_uninstall(end+1) = j;
680 endif
681 endfor
682 endif
683 endfor
684 catch
685 ## Something went wrong, delete tmpdirs.
686 for i = 1:length (tmpdirs)
687 rm_rf (tmpdirs{i});
688 endfor
689 rethrow (lasterror ());
690 end_try_catch
691
692 ## Check dependencies.
693 if (handle_deps)
694 ok = true;
695 error_text = "";
696 for i = 1:length (descriptions)
697 desc = descriptions{i};
698 idx2 = complement (i, 1:length(descriptions));
699 if (global_install)
700 ## Global installation is not allowed to have dependencies on locally
701 ## installed packages.
702 idx1 = complement (packages_to_uninstall,
703 1:length(global_packages));
704 pseudo_installed_packages = {global_packages{idx1}, ...
705 descriptions{idx2}};
706 else
707 idx1 = complement (packages_to_uninstall,
708 1:length(local_packages));
709 pseudo_installed_packages = {local_packages{idx1}, ...
710 global_packages{:}, ...
711 descriptions{idx2}};
712 endif
713 bad_deps = get_unsatisfied_deps (desc, pseudo_installed_packages);
714 ## Are there any unsatisfied dependencies?
715 if (! isempty (bad_deps))
716 ok = false;
717 for i = 1:length (bad_deps)
718 dep = bad_deps{i};
719 error_text = cstrcat (error_text, " ", desc.name, " needs ",
720 dep.package, " ", dep.operator, " ",
721 dep.version, "\n");
722 endfor
723 endif
724 endfor
725
726 ## Did we find any unsatisfied dependencies?
727 if (! ok)
728 error ("the following dependencies where unsatisfied:\n %s", error_text);
729 endif
730 endif
731
732 ## Prepare each package for installation.
733 try
734 for i = 1:length (descriptions)
735 desc = descriptions{i};
736 pdir = packdirs{i};
737 prepare_installation (desc, pdir);
738 configure_make (desc, pdir, verbose);
739 endfor
740 catch
741 ## Something went wrong, delete tmpdirs.
742 for i = 1:length (tmpdirs)
743 rm_rf (tmpdirs{i});
744 endfor
745 rethrow (lasterror ());
746 end_try_catch
747
748 ## Uninstall the packages that will be replaced.
749 try
750 for i = packages_to_uninstall
751 if (global_install)
752 uninstall ({global_packages{i}.name}, false, verbose, local_list,
753 global_list, global_install);
754 else
755 uninstall ({local_packages{i}.name}, false, verbose, local_list,
756 global_list, global_install);
757 endif
758 endfor
759 catch
760 ## Something went wrong, delete tmpdirs.
761 for i = 1:length (tmpdirs)
762 rm_rf (tmpdirs{i});
763 endfor
764 rethrow (lasterror ());
765 end_try_catch
766
767 ## Install each package.
768 try
769 for i = 1:length (descriptions)
770 desc = descriptions{i};
771 pdir = packdirs{i};
772 copy_files (desc, pdir, global_install);
773 create_pkgadddel (desc, pdir, "PKG_ADD", global_install);
774 create_pkgadddel (desc, pdir, "PKG_DEL", global_install);
775 finish_installation (desc, pdir, global_install);
776 generate_lookfor_cache (desc);
777 endfor
778 catch
779 ## Something went wrong, delete tmpdirs.
780 for i = 1:length (tmpdirs)
781 rm_rf (tmpdirs{i});
782 endfor
783 for i = 1:length (descriptions)
784 rm_rf (descriptions{i}.dir);
785 rm_rf (getarchdir (descriptions{i}));
786 endfor
787 rethrow (lasterror ());
788 end_try_catch
789
790 ## Check if the installed directory is empty. If it is remove it
791 ## from the list.
792 for i = length (descriptions):-1:1
793 if (dirempty (descriptions{i}.dir, {"packinfo", "doc"}) &&
794 dirempty (getarchdir (descriptions{i})))
795 warning ("package %s is empty\n", descriptions{i}.name);
796 rm_rf (descriptions{i}.dir);
797 rm_rf (getarchdir (descriptions{i}));
798 descriptions(i) = [];
799 endif
800 endfor
801
802 ## If the package requested that it is autoloaded, or the installer
803 ## requested that it is, then mark the package as autoloaded.
804 for i = length (descriptions):-1:1
805 if (autoload > 0 || (autoload == 0 && isautoload (descriptions(i))))
806 fclose (fopen (fullfile (descriptions{i}.dir, "packinfo",
807 ".autoload"), "wt"));
808 descriptions{i}.autoload = 1;
809 endif
810 endfor
811
812 ## Add the packages to the package list.
813 try
814 if (global_install)
815 idx = complement (packages_to_uninstall, 1:length(global_packages));
816 global_packages = save_order ({global_packages{idx}, descriptions{:}});
817 save (global_list, "global_packages");
818 installed_pkgs_lst = {local_packages{:}, global_packages{:}};
819 else
820 idx = complement (packages_to_uninstall, 1:length(local_packages));
821 local_packages = save_order ({local_packages{idx}, descriptions{:}});
822 save (local_list, "local_packages");
823 installed_pkgs_lst = {local_packages{:}, global_packages{:}};
824 endif
825 catch
826 ## Something went wrong, delete tmpdirs.
827 for i = 1:length (tmpdirs)
828 rm_rf (tmpdirs{i});
829 endfor
830 for i = 1:length (descriptions)
831 rm_rf (descriptions{i}.dir);
832 endfor
833 if (global_install)
834 printf ("error: couldn't append to %s\n", global_list);
835 else
836 printf ("error: couldn't append to %s\n", local_list);
837 endif
838 rethrow (lasterror ());
839 end_try_catch
840
841 ## All is well, let's clean up.
842 for i = 1:length (tmpdirs)
843 [status, msg] = rm_rf (tmpdirs{i});
844 if (status != 1)
845 warning ("couldn't clean up after my self: %s\n", msg);
846 endif
847 endfor
848
849 ## Add the newly installed packages to the path, so the user
850 ## can begin using them. Only load them if they are marked autoload.
851 if (length (descriptions) > 0)
852 idx = [];
853 for i = 1:length (descriptions)
854 if (isautoload (descriptions(i)))
855 nm = descriptions{i}.name;
856 for j = 1:length (installed_pkgs_lst)
857 if (strcmp (nm, installed_pkgs_lst{j}.name))
858 idx (end + 1) = j;
859 break;
860 endif
861 endfor
862 endif
863 endfor
864 load_packages_and_dependencies (idx, handle_deps, installed_pkgs_lst,
865 global_install);
866 endif
867endfunction
868
869function uninstall (pkgnames, handle_deps, verbose, local_list,
870 global_list, global_install)
871 ## Get the list of installed packages.
872 [local_packages, global_packages] = installed_packages(local_list,
873 global_list);
874 if (global_install)
875 installed_pkgs_lst = {local_packages{:}, global_packages{:}};
876 else
877 installed_pkgs_lst = local_packages;
878 endif
879
880 num_packages = length (installed_pkgs_lst);
881 delete_idx = [];
882 for i = 1:num_packages
883 cur_name = installed_pkgs_lst{i}.name;
884 if (any (strcmp (cur_name, pkgnames)))
885 delete_idx(end+1) = i;
886 endif
887 endfor
888
889 ## Are all the packages that should be uninstalled already installed?
890 if (length (delete_idx) != length (pkgnames))
891 if (global_install)
892 ## Try again for a locally installed package.
893 installed_pkgs_lst = local_packages;
894
895 num_packages = length (installed_pkgs_lst);
896 delete_idx = [];
897 for i = 1:num_packages
898 cur_name = installed_pkgs_lst{i}.name;
899 if (any (strcmp (cur_name, pkgnames)))
900 delete_idx(end+1) = i;
901 endif
902 endfor
903 if (length (delete_idx) != length (pkgnames))
904 ## FIXME: We should have a better error message.
905 warning ("some of the packages you want to uninstall are not installed");
906 endif
907 else
908 ## FIXME: We should have a better error message.
909 warning ("some of the packages you want to uninstall are not installed");
910 endif
911 endif
912
913 ## Compute the packages that will remain installed.
914 idx = complement (delete_idx, 1:num_packages);
915 remaining_packages = {installed_pkgs_lst{idx}};
916
917 ## Check dependencies.
918 if (handle_deps)
919 error_text = "";
920 for i = 1:length (remaining_packages)
921 desc = remaining_packages{i};
922 bad_deps = get_unsatisfied_deps (desc, remaining_packages);
923
924 ## Will the uninstallation break any dependencies?
925 if (! isempty (bad_deps))
926 for i = 1:length (bad_deps)
927 dep = bad_deps{i};
928 error_text = cstrcat (error_text, " ", desc.name, " needs ",
929 dep.package, " ", dep.operator, " ",
930 dep.version, "\n");
931 endfor
932 endif
933 endfor
934
935 if (! isempty (error_text))
936 error ("the following dependencies where unsatisfied:\n %s", error_text);
937 endif
938 endif
939
940 ## Delete the directories containing the packages.
941 for i = delete_idx
942 desc = installed_pkgs_lst{i};
943 ## If an 'on_uninstall.m' exist, call it!
944 if (exist (fullfile (desc.dir, "packinfo", "on_uninstall.m"), "file"))
945 wd = pwd ();
946 cd (fullfile (desc.dir, "packinfo"));
947 on_uninstall (desc);
948 cd (wd);
949 endif
950 ## Do the actual deletion.
951 if (desc.loaded)
952 rmpath (desc.dir);
953 if (exist (getarchdir (desc)))
954 rmpath (getarchdir (desc));
955 endif
956 endif
957 if (exist (desc.dir, "dir"))
958 [status, msg] = rm_rf (desc.dir);
959 if (status != 1)
960 error ("couldn't delete directory %s: %s", desc.dir, msg);
961 endif
962 [status, msg] = rm_rf (getarchdir (desc));
963 if (status != 1)
964 error ("couldn't delete directory %s: %s", getarchdir (desc), msg);
965 endif
966 if (dirempty (desc.archprefix))
967 rm_rf (desc.archprefix);
968 endif
969 else
970 warning ("directory %s previously lost", desc.dir);
971 endif
972 endfor
973
974 ## Write a new ~/.octave_packages.
975 if (global_install)
976 if (length (remaining_packages) == 0)
977 unlink (global_list);
978 else
979 global_packages = save_order (remaining_packages);
980 save (global_list, "global_packages");
981 endif
982 else
983 if (length (remaining_packages) == 0)
984 unlink (local_list);
985 else
986 local_packages = save_order (remaining_packages);
987 save (local_list, "local_packages");
988 endif
989 endif
990
991endfunction
992
993function [pkg_desc_list, flag] = describe (pkgnames, verbose,
994 local_list, global_list)
995
996 ## Get the list of installed packages.
997 installed_pkgs_lst = installed_packages(local_list, global_list);
998 num_packages = length (installed_pkgs_lst);
999
1000
1001 describe_all = false;
1002 if (any (strcmp ("all", pkgnames)))
1003 describe_all = true;
1004 flag(1:num_packages) = {"Not Loaded"};
1005 num_pkgnames = num_packages;
1006 else
1007 num_pkgnames = length (pkgnames);
1008 flag(1:num_pkgnames) = {"Not installed"};
1009 endif
1010
1011 for i = 1:num_packages
1012 curr_name = installed_pkgs_lst{i}.name;
1013 if (describe_all)
1014 name_pos = i;
1015 else
1016 name_pos = find(strcmp (curr_name, pkgnames));
1017 endif
1018
1019 if (! isempty (name_pos))
1020 if (installed_pkgs_lst{i}.loaded)
1021 flag{name_pos} = "Loaded";
1022 else
1023 flag{name_pos} = "Not loaded";
1024 endif
1025
1026 pkg_desc_list{name_pos}.name = installed_pkgs_lst{i}.name;
1027 pkg_desc_list{name_pos}.version = installed_pkgs_lst{i}.version;
1028 pkg_desc_list{name_pos}.description = installed_pkgs_lst{i}.description;
1029 pkg_desc_list{name_pos}.provides = parse_pkg_idx (installed_pkgs_lst{i}.dir);
1030
1031 endif
1032 endfor
1033
1034 non_inst = find (strcmp (flag, "Not installed"));
1035 if (! isempty (non_inst))
1036 if (nargout < 2)
1037 non_inst_str = sprintf (" %s ", pkgnames{non_inst});
1038 error ("some packages are not installed: %s", non_inst_str);
1039 else
1040 pkg_desc_list{non_inst} = struct ("name", {}, "description",
1041 {}, "provides", {});
1042 endif
1043 endif
1044
1045 if (nargout == 0)
1046 for i = 1:num_pkgnames
1047 print_package_description (pkg_desc_list{i}.name,
1048 pkg_desc_list{i}.version,
1049 pkg_desc_list{i}.provides,
1050 pkg_desc_list{i}.description,
1051 flag{i}, verbose);
1052 endfor
1053 endif
1054
1055endfunction
1056
1057## AUXILIARY FUNCTIONS
1058
1059## Read an INDEX file.
1060function [pkg_idx_struct] = parse_pkg_idx (packdir)
1061
1062 index_file = fullfile (packdir, "packinfo", "INDEX");
1063
1064 if (! exist (index_file, "file"))
1065 error ("could not find any INDEX file in directory %s, try 'pkg rebuild all' to generate missing INDEX files", packdir);
1066 endif
1067
1068
1069 [fid, msg] = fopen (index_file, "r");
1070 if (fid == -1)
1071 error ("the INDEX file %s could not be read: %s",
1072 index_file, msg);
1073 endif
1074
1075 cat_num = 1;
1076 pkg_idx_struct{1}.category = "Uncategorized";
1077 pkg_idx_struct{1}.functions = {};
1078
1079 line = fgetl (fid);
1080 while (isempty (strfind (line, ">>")) && ! feof (fid))
1081 line = fgetl (fid);
1082 endwhile
1083
1084 while (! feof (fid) || line != -1)
1085 if (! any (! isspace (line)) || line(1) == "#" || any (line == "="))
1086 ## Comments, blank lines or comments about unimplemented
1087 ## functions: do nothing
1088 ## FIXME: probably comments and pointers to external functions
1089 ## could be treated better when printing to screen?
1090 elseif (! isempty (strfind (line, ">>")))
1091 ## Skip package name and description as they are in DESCRIPTION
1092 ## already.
1093 elseif (! isspace (line(1)))
1094 ## Category.
1095 if (! isempty (pkg_idx_struct{cat_num}.functions))
1096 pkg_idx_struct{++cat_num}.functions = {};
1097 endif
1098 pkg_idx_struct{cat_num}.category = deblank (line);
1099 else
1100 ## Function names.
1101 while (any (! isspace (line)))
1102 [fun_name, line] = strtok (line);
1103 pkg_idx_struct{cat_num}.functions{end+1} = deblank (fun_name);
1104 endwhile
1105 endif
1106 line = fgetl (fid);
1107 endwhile
1108 fclose (fid);
1109endfunction
1110
1111function print_package_description (pkg_name, pkg_ver, pkg_idx_struct,
1112 pkg_desc, status, verbose)
1113
1114 printf ("---\nPackage name:\n\t%s\n", pkg_name);
1115 printf ("Version:\n\t%s\n", pkg_ver);
1116 printf ("Short description:\n\t%s\n", pkg_desc);
1117 printf ("Status:\n\t%s\n", status);
1118 if (verbose)
1119 printf ("---\nProvides:\n");
1120 for i = 1:length(pkg_idx_struct)
1121 if (! isempty (pkg_idx_struct{i}.functions))
1122 printf ("%s\n", pkg_idx_struct{i}.category);
1123 for j = 1:length(pkg_idx_struct{i}.functions)
1124 printf ("\t%s\n", pkg_idx_struct{i}.functions{j});
1125 endfor
1126 endif
1127 endfor
1128 endif
1129
1130endfunction
1131
1132
1133function pth = absolute_pathname (pth)
1134 [status, msg, msgid] = fileattrib (pth);
1135 if (status != 1)
1136 error ("could not find the file or path %s", pth);
1137 else
1138 pth = msg.Name;
1139 endif
1140endfunction
1141
1142function repackage (builddir, buildlist)
1143 packages = installed_packages (buildlist, buildlist);
1144
1145 wd = pwd();
1146 for i = 1 : length(packages)
1147 pack = packages{i};
1148 unwind_protect
1149 cd (builddir);
1150 mkdir (pack.name);
1151 mkdir (fullfile (pack.name, "inst"));
1152 copyfile (fullfile (pack.dir, "*"), fullfile (pack.name, "inst"));
1153 movefile (fullfile (pack.name, "inst","packinfo", "*"), pack.name);
1154 if (exist (fullfile (pack.name, "inst","packinfo", ".autoload"), "file"))
1155 unlink (fullfile (pack.name, "inst","packinfo", ".autoload"));
1156 endif
1157 rmdir (fullfile (pack.name, "inst", "packinfo"));
1158 if (exist (fullfile (pack.name, "inst", "doc"), "dir"))
1159 movefile (fullfile (pack.name, "inst", "doc"), pack.name);
1160 endif
1161 if (exist (fullfile (pack.name, "inst", "bin"), "dir"))
1162 movefile (fullfile (pack.name, "inst", "bin"), pack.name);
1163 endif
1164 archdir = fullfile (pack.archprefix, cstrcat (pack.name, "-",
1165 pack.version), getarch ());
1166 if (exist (archdir, "dir"))
1167 if (exist (fullfile (pack.name, "inst", "PKG_ADD"), "file"))
1168 unlink (fullfile (pack.name, "inst", "PKG_ADD"));
1169 endif
1170 if (exist (fullfile (pack.name, "inst", "PKG_DEL"), "file"))
1171 unlink (fullfile (pack.name, "inst", "PKG_DEL"));
1172 endif
1173 if (exist (fullfile (archdir, "PKG_ADD"), "file"))
1174 movefile (fullfile (archdir, "PKG_ADD"),
1175 fullfile (pack.name, "PKG_ADD"));
1176 endif
1177 if (exist (fullfile (archdir, "PKG_DEL"), "file"))
1178 movefile (fullfile (archdir, "PKG_DEL"),
1179 fullfile (pack.name, "PKG_DEL"));
1180 endif
1181 else
1182 if (exist (fullfile (pack.name, "inst", "PKG_ADD"), "file"))
1183 movefile (fullfile (pack.name, "inst", "PKG_ADD"),
1184 fullfile (pack.name, "PKG_ADD"));
1185 endif
1186 if (exist (fullfile (pack.name, "inst", "PKG_DEL"), "file"))
1187 movefile (fullfile (pack.name, "inst", "PKG_DEL"),
1188 fullfile (pack.name, "PKG_DEL"));
1189 endif
1190 endif
1191 tfile = cstrcat (pack.name, "-", pack.version, ".tar");
1192 tar (tfile, pack.name);
1193 try
1194 gzip (tfile);
1195 unlink (tfile);
1196 catch
1197 warning ("failed to compress %s", tfile);
1198 end_try_catch
1199 unwind_protect_cleanup
1200 if (exist (pack.name, "dir"))
1201 rm_rf (pack.name);
1202 endif
1203 cd (wd);
1204 end_unwind_protect
1205 endfor
1206endfunction
1207
1208function auto = isautoload (desc)
1209 auto = false;
1210 if (isfield (desc{1}, "autoload"))
1211 a = desc{1}.autoload;
1212 if ((isnumeric (a) && a > 0)
1213 || (ischar (a) && (strcmpi (a, "true")
1214 || strcmpi (a, "on")
1215 || strcmpi (a, "yes")
1216 || strcmpi (a, "1"))))
1217 auto = true;
1218 endif
1219 endif
1220endfunction
1221
1222function prepare_installation (desc, packdir)
1223 ## Is there a pre_install to call?
1224 if (exist (fullfile (packdir, "pre_install.m"), "file"))
1225 wd = pwd ();
1226 try
1227 cd (packdir);
1228 pre_install (desc);
1229 cd (wd);
1230 catch
1231 cd (wd);
1232 rethrow (lasterror ());
1233 end_try_catch
1234 endif
1235
1236 ## If the directory "inst" doesn't exist, we create it.
1237 inst_dir = fullfile (packdir, "inst");
1238 if (! exist (inst_dir, "dir"))
1239 [status, msg] = mkdir (inst_dir);
1240 if (status != 1)
1241 rm_rf (desc.dir);
1242 error ("the 'inst' directory did not exist and could not be created: %s",
1243 msg);
1244 endif
1245 endif
1246endfunction
1247
1248function configure_make (desc, packdir, verbose)
1249 ## Perform ./configure, make, make install in "src".
1250 if (exist (fullfile (packdir, "src"), "dir"))
1251 src = fullfile (packdir, "src");
1252 ## Configure.
1253 if (exist (fullfile (src, "configure"), "file"))
1254 flags = "";
1255 if (isempty (getenv ("CC")))
1256 flags = cstrcat (flags, " CC=\"", octave_config_info ("CC"), "\"");
1257 endif
1258 if (isempty (getenv ("CXX")))
1259 flags = cstrcat (flags, " CXX=\"", octave_config_info ("CXX"), "\"");
1260 endif
1261 if (isempty (getenv ("AR")))
1262 flags = cstrcat (flags, " AR=\"", octave_config_info ("AR"), "\"");
1263 endif
1264 if (isempty (getenv ("RANLIB")))
1265 flags = cstrcat (flags, " RANLIB=\"", octave_config_info ("RANLIB"), "\"");
1266 endif
1267 [status, output] = shell (strcat ("cd '", src, "'; ./configure --prefix=\"",
1268 desc.dir, "\"", flags));
1269 if (status != 0)
1270 rm_rf (desc.dir);
1271 error ("the configure script returned the following error: %s", output);
1272 elseif (verbose)
1273 printf("%s", output);
1274 endif
1275
1276 endif
1277
1278 ## Make.
1279 if (exist (fullfile (src, "Makefile"), "file"))
1280 [status, output] = shell (cstrcat ("export INSTALLDIR=\"", desc.dir,
1281 "\"; make -C '", src, "'"));
1282 if (status != 0)
1283 rm_rf (desc.dir);
1284 error ("'make' returned the following error: %s", output);
1285 elseif (verbose)
1286 printf("%s", output);
1287 endif
1288 endif
1289
1290 ## Copy files to "inst" and "inst/arch" (this is instead of 'make
1291 ## install').
1292 files = fullfile (src, "FILES");
1293 instdir = fullfile (packdir, "inst");
1294 archdir = fullfile (packdir, "inst", getarch ());
1295
1296 ## Get file names.
1297 if (exist (files, "file"))
1298 [fid, msg] = fopen (files, "r");
1299 if (fid < 0)
1300 error ("couldn't open %s: %s", files, msg);
1301 endif
1302 filenames = char (fread (fid))';
1303 fclose (fid);
1304 if (filenames(end) == "\n")
1305 filenames(end) = [];
1306 endif
1307 filenames = split_by (filenames, "\n");
1308 delete_idx = [];
1309 for i = 1:length (filenames)
1310 if (! all (isspace (filenames{i})))
1311 filenames{i} = fullfile (src, filenames{i});
1312 else
1313 delete_idx(end+1) = i;
1314 endif
1315 endfor
1316 filenames(delete_idx) = [];
1317 else
1318 m = dir (fullfile (src, "*.m"));
1319 oct = dir (fullfile (src, "*.oct"));
1320 mex = dir (fullfile (src, "*.mex"));
1321
1322 filenames = cellfun (@(x) fullfile (src, x),
1323 {m.name, oct.name, mex.name},
1324 "UniformOutput", false);
1325 endif
1326
1327 ## Split into architecture dependent and independent files.
1328 if (isempty (filenames))
1329 idx = [];
1330 else
1331 idx = cellfun (@is_architecture_dependent, filenames);
1332 endif
1333 archdependent = filenames (idx);
1334 archindependent = filenames (!idx);
1335
1336 ## Copy the files.
1337 if (! all (isspace ([filenames{:}])))
1338 if (! exist (instdir, "dir")) # fixindent
1339 mkdir (instdir);
1340 endif
1341 if (! all (isspace ([archindependent{:}])))
1342 if (verbose)
1343 printf ("copyfile");
1344 printf (" %s", archindependent{:});
1345 printf ("%s\n", instdir);
1346 endif
1347 [status, output] = copyfile (archindependent, instdir);
1348 if (status != 1)
1349 rm_rf (desc.dir);
1350 error ("Couldn't copy files from 'src' to 'inst': %s", output);
1351 endif
1352 endif
1353 if (! all (isspace ([archdependent{:}])))
1354 if (verbose)
1355 printf ("copyfile");
1356 printf (" %s", archdependent{:});
1357 printf (" %s\n", archdir);
1358 endif
1359 if (! exist (archdir, "dir"))
1360 mkdir (archdir);
1361 endif
1362 [status, output] = copyfile (archdependent, archdir);
1363 if (status != 1)
1364 rm_rf (desc.dir);
1365 error ("Couldn't copy files from 'src' to 'inst': %s", output);
1366 endif
1367 endif
1368 endif
1369 endif
1370endfunction
1371
1372function pkg = extract_pkg (nm, pat)
1373 fid = fopen (nm, "rt");
1374 pkg = "";
1375 if (fid >= 0)
1376 while (! feof (fid))
1377 ln = fgetl (fid);
1378 if (ln > 0)
1379 t = regexp (ln, pat, "tokens");
1380 if (! isempty (t))
1381 pkg = cstrcat (pkg, "\n", t{1}{1});
1382 endif
1383 endif
1384 endwhile
1385 if (! isempty (pkg))
1386 pkg = cstrcat (pkg, "\n");
1387 endif
1388 fclose (fid);
1389 endif
1390endfunction
1391
1392function create_pkgadddel (desc, packdir, nm, global_install)
1393 instpkg = fullfile (desc.dir, nm);
1394 instfid = fopen (instpkg, "wt");
1395 ## If it is exists, most of the PKG_* file should go into the
1396 ## architecture dependent directory so that the autoload/mfilename
1397 ## commands work as expected. The only part that doesn't is the
1398 ## part in the main directory.
1399 archdir = fullfile (getarchprefix (desc), cstrcat (desc.name, "-",
1400 desc.version), getarch ());
1401 if (exist (getarchdir (desc, global_install), "dir"))
1402 archpkg = fullfile (getarchdir (desc, global_install), nm);
1403 archfid = fopen (archpkg, "at");
1404 else
1405 archpkg = instpkg;
1406 archfid = instfid;
1407 endif
1408
1409 if (archfid >= 0 && instfid >= 0)
1410 ## Search all dot-m files for PKG commands.
1411 lst = dir (fullfile (packdir, "inst", "*.m"));
1412 for i = 1:length (lst)
1413 nam = fullfile (packdir, "inst", lst(i).name);
1414 fwrite (instfid, extract_pkg (nam, ['^[#%][#%]* *' nm ': *(.*)$']));
1415 endfor # fixindent
1416
1417 ## Search all C++ source files for PKG commands.
1418 lst = dir (fullfile (packdir, "src", "*.cc")); # fixindent
1419 for i = 1:length (lst)
1420 nam = fullfile (packdir, "src", lst(i).name);
1421 fwrite (archfid, extract_pkg (nam, ['^//* *' nm ': *(.*)$']));
1422 fwrite (archfid, extract_pkg (nam, ['^/\** *' nm ': *(.*) *\*/$']));
1423 endfor
1424
1425 ## Add developer included PKG commands.
1426 packdirnm = fullfile (packdir, nm);
1427 if (exist (packdirnm, "file"))
1428 fid = fopen (packdirnm, "rt");
1429 if (fid >= 0)
1430 while (! feof (fid))
1431 ln = fgets (fid);
1432 if (ln > 0)
1433 fwrite (archfid, ln);
1434 endif
1435 endwhile
1436 fclose (fid);
1437 endif
1438 endif
1439
1440 ## If the files is empty remove it.
1441 fclose (instfid);
1442 t = dir (instpkg);
1443 if (t.bytes <= 0)
1444 unlink (instpkg);
1445 endif
1446
1447 if (instfid != archfid)
1448 fclose (archfid);
1449 t = dir (archpkg);
1450 if (t.bytes <= 0)
1451 unlink (archpkg);
1452 endif
1453 endif
1454 endif # fixindent
1455endfunction # fixindent
1456
1457function copy_files (desc, packdir, global_install) # fixindent
1458 ## Create the installation directory.
1459 if (! exist (desc.dir, "dir"))
1460 [status, output] = mkdir (desc.dir);
1461 if (status != 1)
1462 error ("couldn't create installation directory %s : %s",
1463 desc.dir, output);
1464 endif
1465 endif
1466
1467 octfiledir = getarchdir (desc);
1468
1469 ## Copy the files from "inst" to installdir.
1470 instdir = fullfile (packdir, "inst");
1471 if (! dirempty (instdir))
1472 [status, output] = copyfile (fullfile (instdir, "*"), desc.dir);
1473 if (status != 1)
1474 rm_rf (desc.dir);
1475 error ("couldn't copy files to the installation directory");
1476 endif
1477 if (exist (fullfile (desc.dir, getarch ()), "dir") &&
1478 ! strcmp (fullfile (desc.dir, getarch ()), octfiledir))
1479 if (! exist (octfiledir, "dir"))
1480 ## Can be required to create upto three levels of dirs.
1481 octm1 = fileparts (octfiledir);
1482 if (! exist (octm1, "dir"))
1483 octm2 = fileparts (octm1);
1484 if (! exist (octm2, "dir"))
1485 octm3 = fileparts (octm2);
1486 if (! exist (octm3, "dir"))
1487 [status, output] = mkdir (octm3);
1488 if (status != 1)
1489 rm_rf (desc.dir);
1490 error ("couldn't create installation directory %s : %s",
1491 octm3, output);
1492 endif
1493 endif
1494 [status, output] = mkdir (octm2);
1495 if (status != 1)
1496 rm_rf (desc.dir);
1497 error ("couldn't create installation directory %s : %s",
1498 octm2, output);
1499 endif
1500 endif
1501 [status, output] = mkdir (octm1);
1502 if (status != 1)
1503 rm_rf (desc.dir);
1504 error ("couldn't create installation directory %s : %s",
1505 octm1, output);
1506 endif
1507 endif
1508 [status, output] = mkdir (octfiledir);
1509 if (status != 1)
1510 rm_rf (desc.dir);
1511 error ("couldn't create installation directory %s : %s",
1512 octfiledir, output);
1513 endif
1514 endif
1515 [status, output] = movefile (fullfile (desc.dir, getarch (), "*"),
1516 octfiledir);
1517 rm_rf (fullfile (desc.dir, getarch ()));
1518
1519 if (status != 1)
1520 rm_rf (desc.dir);
1521 rm_rf (octfiledir);
1522 error ("couldn't copy files to the installation directory");
1523 endif
1524 endif
1525
1526 endif
1527
1528 ## Create the "packinfo" directory.
1529 packinfo = fullfile (desc.dir, "packinfo");
1530 [status, msg] = mkdir (packinfo);
1531 if (status != 1)
1532 rm_rf (desc.dir);
1533 rm_rf (octfiledir);
1534 error ("couldn't create packinfo directory: %s", msg);
1535 endif
1536
1537 ## Copy DESCRIPTION.
1538 [status, output] = copyfile (fullfile (packdir, "DESCRIPTION"), packinfo);
1539 if (status != 1)
1540 rm_rf (desc.dir);
1541 rm_rf (octfiledir);
1542 error ("couldn't copy DESCRIPTION: %s", output);
1543 endif
1544
1545 ## Copy COPYING.
1546 [status, output] = copyfile (fullfile (packdir, "COPYING"), packinfo);
1547 if (status != 1)
1548 rm_rf (desc.dir);
1549 rm_rf (octfiledir);
1550 error ("couldn't copy COPYING: %s", output);
1551 endif
1552
1553 ## If the file ChangeLog exists, copy it.
1554 changelog_file = fullfile (packdir, "ChangeLog");
1555 if (exist (changelog_file, "file"))
1556 [status, output] = copyfile (changelog_file, packinfo);
1557 if (status != 1)
1558 rm_rf (desc.dir);
1559 rm_rf (octfiledir);
1560 error ("couldn't copy ChangeLog file: %s", output);
1561 endif
1562 endif
1563
1564 ## Is there an INDEX file to copy or should we generate one?
1565 index_file = fullfile (packdir, "INDEX");
1566 if (exist(index_file, "file"))
1567 [status, output] = copyfile (index_file, packinfo);
1568 if (status != 1)
1569 rm_rf (desc.dir);
1570 rm_rf (octfiledir);
1571 error ("couldn't copy INDEX file: %s", output);
1572 endif
1573 else
1574 try
1575 write_index (desc, fullfile (packdir, "inst"),
1576 fullfile (packinfo, "INDEX"), global_install);
1577 catch
1578 rm_rf (desc.dir);
1579 rm_rf (octfiledir);
1580 rethrow (lasterror ());
1581 end_try_catch
1582 endif
1583
1584 ## Is there an 'on_uninstall.m' to install?
1585 fon_uninstall = fullfile (packdir, "on_uninstall.m");
1586 if (exist (fon_uninstall, "file"))
1587 [status, output] = copyfile (fon_uninstall, packinfo);
1588 if (status != 1)
1589 rm_rf (desc.dir);
1590 rm_rf (octfiledir);
1591 error ("couldn't copy on_uninstall.m: %s", output);
1592 endif
1593 endif
1594
1595 ## Is there a doc/ directory that needs to be installed?
1596 docdir = fullfile (packdir, "doc");
1597 if (exist (docdir, "dir") && ! dirempty (docdir))
1598 [status, output] = copyfile (docdir, desc.dir);
1599 endif
1600
1601 ## Is there a bin/ directory that needs to be installed?
1602 ## FIXME: Need to treat architecture dependent files in bin/
1603 bindir = fullfile (packdir, "bin");
1604 if (exist (bindir, "dir") && ! dirempty (bindir))
1605 [status, output] = copyfile (bindir, desc.dir);
1606 endif
1607endfunction
1608
1609function finish_installation (desc, packdir, global_install)
1610 ## Is there a post-install to call?
1611 if (exist (fullfile (packdir, "post_install.m"), "file"))
1612 wd = pwd ();
1613 try
1614 cd (packdir);
1615 post_install (desc);
1616 cd (wd);
1617 catch
1618 cd (wd);
1619 rm_rf (desc.dir);
1620 rm_rf (getarchdir (desc), global_install);
1621 rethrow (lasterror ());
1622 end_try_catch
1623 endif
1624endfunction
1625
1626function generate_lookfor_cache (desc)
1627 dirs = split_by (genpath (desc.dir), pathsep ());
1628 for i = 1 : length (dirs)
1629 gen_doc_cache (fullfile (dirs{i}, "doc-cache"), dirs{i});
1630 endfor
1631endfunction
1632
1633## Make sure the package contains the essential files.
1634function verify_directory (dir)
1635 needed_files = {"COPYING", "DESCRIPTION"};
1636 for f = needed_files
1637 if (! exist (fullfile (dir, f{1}), "file"))
1638 error ("package is missing file: %s", f{1});
1639 endif
1640 endfor
1641endfunction
1642
1643## Parse the DESCRIPTION file.
1644function desc = get_description (filename)
1645 [fid, msg] = fopen (filename, "r");
1646 if (fid == -1)
1647 error ("the DESCRIPTION file %s could not be read: %s", filename, msg);
1648 endif
1649
1650 desc = struct ();
1651
1652 line = fgetl (fid);
1653 while (line != -1)
1654 if (line(1) == "#")
1655 ## Comments, do nothing.
1656 elseif (isspace(line(1)))
1657 ## Continuation lines
1658 if (exist ("keyword", "var") && isfield (desc, keyword))
1659 desc.(keyword) = cstrcat (desc.(keyword), " ", rstrip(line));
1660 endif
1661 else
1662 ## Keyword/value pair
1663 colon = find (line == ":");
1664 if (length (colon) == 0)
1665 disp ("skipping line");
1666 else
1667 colon = colon(1);
1668 keyword = tolower (strip (line(1:colon-1)));
1669 value = strip (line (colon+1:end));
1670 if (length (value) == 0)
1671 fclose (fid);
1672 error ("the keyword %s has an empty value", desc.keywords{end});
1673 endif
1674 desc.(keyword) = value;
1675 endif
1676 endif
1677 line = fgetl (fid);
1678 endwhile
1679 fclose (fid);
1680
1681 ## Make sure all is okay.
1682 needed_fields = {"name", "version", "date", "title", ...
1683 "author", "maintainer", "description"};
1684 for f = needed_fields
1685 if (! isfield (desc, f{1}))
1686 error ("description is missing needed field %s", f{1});
1687 endif
1688 endfor
1689 desc.version = fix_version (desc.version);
1690 if (isfield (desc, "depends"))
1691 desc.depends = fix_depends (desc.depends);
1692 else
1693 desc.depends = "";
1694 endif
1695 desc.name = tolower (desc.name);
1696endfunction
1697
1698## Make sure the version string v is a valid x.y.z version string
1699## Examples: "0.1" => "0.1.0", "monkey" => error(...).
1700function out = fix_version (v)
1701 dots = find (v == ".");
1702 if (length (dots) == 1)
1703 major = str2num (v(1:dots-1));
1704 minor = str2num (v(dots+1:end));
1705 if (length (major) != 0 && length (minor) != 0)
1706 out = sprintf ("%d.%d.0", major, minor);
1707 return;
1708 endif
1709 elseif (length (dots) == 2)
1710 major = str2num (v(1:dots(1)-1));
1711 minor = str2num (v(dots(1)+1:dots(2)-1));
1712 rev = str2num (v(dots(2)+1:end));
1713 if (length (major) != 0 && length (minor) != 0 && length (rev) != 0)
1714 out = sprintf ("%d.%d.%d", major, minor, rev);
1715 return;
1716 endif
1717 endif
1718 error ("bad version string: %s", v);
1719endfunction
1720
1721## Make sure the depends field is of the right format.
1722## This function returns a cell of structures with the following fields:
1723## package, version, operator
1724function deps_cell = fix_depends (depends)
1725 deps = split_by (tolower (depends), ",");
1726 deps_cell = cell (1, length (deps));
1727
1728 ## For each dependency.
1729 for i = 1:length (deps)
1730 dep = deps{i};
1731 lpar = find (dep == "(");
1732 rpar = find (dep == ")");
1733 ## Does the dependency specify a version
1734 ## Example: package(>= version).
1735 if (length (lpar) == 1 && length (rpar) == 1)
1736 package = tolower (strip (dep(1:lpar-1)));
1737 sub = dep(lpar(1)+1:rpar(1)-1);
1738 parts = strsplit (sub, " ", true);
1739 if (length (parts) != 2)
1740 error ("incorrect syntax for dependency `%s' in the DESCRIPTION file\n",
1741 dep);
1742 endif
1743 operator = parts{1};
1744 if (! any (strcmp (operator, {">", ">=", "<=", "<", "=="})))
1745 error ("unsupported operator: %s", operator);
1746 endif
1747 version = fix_version (parts{2});
1748
1749 ## If no version is specified for the dependency
1750 ## we say that the version should be greater than
1751 ## or equal to "0.0.0".
1752 else
1753 package = tolower (strip (dep));
1754 operator = ">=";
1755 version = "0.0.0";
1756 endif
1757 deps_cell{i} = struct ("package", package, "operator", operator,
1758 "version", version);
1759 endfor
1760endfunction
1761
1762## Strip the text of spaces from the right
1763## Example: " hello world " => " hello world"
1764## FIXME -- is this the same as deblank?
1765function text = rstrip (text)
1766 chars = find (! isspace (text));
1767 if (length (chars) > 0)
1768 ## FIXME: shouldn't it be text = text(1:chars(end));
1769 text = text (chars(1):end);
1770 else
1771 text = "";
1772 endif
1773endfunction
1774
1775## Strip the text of spaces from the left and the right.
1776## Example: " hello world " => "hello world"
1777function text = strip (text)
1778 chars = find (! isspace (text));
1779 if (length (chars) > 0)
1780 text = text(chars(1):chars(end));
1781 else
1782 text = "";
1783 endif
1784endfunction
1785
1786## Split the text into a cell array of strings by sep.
1787## Example: "A, B" => {"A", "B"} (with sep = ",")
1788function out = split_by (text, sep)
1789 out = strtrim (strsplit (text, sep));
1790endfunction
1791
1792## Create an INDEX file for a package that doesn't provide one.
1793## 'desc' describes the package.
1794## 'dir' is the 'inst' directory in temporary directory.
1795## 'index_file' is the name (including path) of resulting INDEX file.
1796function write_index (desc, dir, index_file, global_install)
1797 ## Get names of functions in dir
1798 [files, err, msg] = readdir (dir);
1799 if (err)
1800 error ("couldn't read directory %s: %s", dir, msg);
1801 endif
1802
1803 ## Check for architecture dependent files.
1804 tmpdir = getarchdir (desc);
1805 if (exist (tmpdir, "dir"))
1806 [files2, err, msg] = readdir (tmpdir);
1807 if (err)
1808 error ("couldn't read directory %s: %s", tmpdir, msg);
1809 endif
1810 files = [files; files2];
1811 endif
1812
1813 functions = {};
1814 for i = 1:length (files)
1815 file = files{i};
1816 lf = length (file);
1817 if (lf > 2 && strcmp (file(end-1:end), ".m"))
1818 functions{end+1} = file(1:end-2);
1819 elseif (lf > 4 && strcmp (file(end-3:end), ".oct"))
1820 functions{end+1} = file(1:end-4);
1821 endif
1822 endfor
1823
1824 ## Does desc have a categories field?
1825 if (! isfield (desc, "categories"))
1826 error ("the DESCRIPTION file must have a Categories field, when no INDEX file is given");
1827 endif
1828 categories = split_by (desc.categories, ",");
1829 if (length (categories) < 1)
1830 error ("the Category field is empty");
1831 endif
1832
1833 ## Write INDEX.
1834 fid = fopen (index_file, "w");
1835 if (fid == -1)
1836 error ("couldn't open %s for writing.", index_file);
1837 endif
1838 fprintf (fid, "%s >> %s\n", desc.name, desc.title);
1839 fprintf (fid, "%s\n", categories{1});
1840 fprintf (fid, " %s\n", functions{:});
1841 fclose (fid);
1842endfunction
1843
1844function bad_deps = get_unsatisfied_deps (desc, installed_pkgs_lst)
1845 bad_deps = {};
1846
1847 ## For each dependency.
1848 for i = 1:length (desc.depends)
1849 dep = desc.depends{i};
1850
1851 ## Is the current dependency Octave?
1852 if (strcmp (dep.package, "octave"))
1853 if (! compare_versions (OCTAVE_VERSION, dep.version, dep.operator))
1854 bad_deps{end+1} = dep;
1855 endif
1856 ## Is the current dependency not Octave?
1857 else
1858 ok = false;
1859 for i = 1:length (installed_pkgs_lst)
1860 cur_name = installed_pkgs_lst{i}.name;
1861 cur_version = installed_pkgs_lst{i}.version;
1862 if (strcmp (dep.package, cur_name)
1863 && compare_versions (cur_version, dep.version, dep.operator))
1864 ok = true;
1865 break;
1866 endif
1867 endfor
1868 if (! ok)
1869 bad_deps{end+1} = dep;
1870 endif
1871 endif
1872 endfor
1873endfunction
1874
1875function [out1, out2] = installed_packages (local_list, global_list)
1876 ## Get the list of installed packages.
1877 try
1878 local_packages = load (local_list).local_packages;
1879 catch
1880 local_packages = {};
1881 end_try_catch
1882 try
1883 global_packages = load (global_list).global_packages;
1884 catch
1885 global_packages = {};
1886 end_try_catch
1887 installed_pkgs_lst = {local_packages{:}, global_packages{:}};
1888
1889 ## Eliminate duplicates in the installed package list.
1890 ## Locally installed packages take precedence.
1891 dup = [];
1892 for i = 1:length (installed_pkgs_lst)
1893 if (find (dup, i))
1894 continue;
1895 endif
1896 for j = (i+1):length (installed_pkgs_lst)
1897 if (find (dup, j))
1898 continue;
1899 endif
1900 if (strcmp (installed_pkgs_lst{i}.name, installed_pkgs_lst{j}.name))
1901 dup = [dup, j];
1902 endif
1903 endfor
1904 endfor
1905 if (! isempty(dup))
1906 installed_pkgs_lst(dup) = [];
1907 endif
1908
1909 ## Now check if the package is loaded.
1910 tmppath = strrep (path(), "\\", "/");
1911 for i = 1:length (installed_pkgs_lst)
1912 if (findstr (tmppath, strrep (installed_pkgs_lst{i}.dir, "\\", "/")))
1913 installed_pkgs_lst{i}.loaded = true;
1914 else
1915 installed_pkgs_lst{i}.loaded = false;
1916 endif
1917 endfor
1918 for i = 1:length (local_packages)
1919 if (findstr (tmppath, strrep (local_packages{i}.dir, "\\", "/")))
1920 local_packages{i}.loaded = true;
1921 else
1922 local_packages{i}.loaded = false;
1923 endif
1924 endfor
1925 for i = 1:length (global_packages)
1926 if (findstr (tmppath, strrep (global_packages{i}.dir, "\\", "/")))
1927 global_packages{i}.loaded = true;
1928 else
1929 global_packages{i}.loaded = false;
1930 endif
1931 endfor
1932
1933 ## Should we return something?
1934 if (nargout == 2)
1935 out1 = local_packages;
1936 out2 = global_packages;
1937 return;
1938 elseif (nargout == 1)
1939 out1 = installed_pkgs_lst;
1940 return;
1941 endif
1942
1943 ## We shouldn't return something, so we'll print something.
1944 num_packages = length (installed_pkgs_lst);
1945 if (num_packages == 0)
1946 printf ("no packages installed.\n");
1947 return;
1948 endif
1949
1950 ## Compute the maximal lengths of name, version, and dir.
1951 h1 = "Package Name";
1952 h2 = "Version";
1953 h3 = "Installation directory";
1954 max_name_length = length (h1);
1955 max_version_length = length (h2);
1956 names = cell (num_packages, 1);
1957 for i = 1:num_packages
1958 max_name_length = max (max_name_length,
1959 length (installed_pkgs_lst{i}.name));
1960 max_version_length = max (max_version_length,
1961 length (installed_pkgs_lst{i}.version));
1962 names{i} = installed_pkgs_lst{i}.name;
1963 endfor
1964 max_dir_length = terminal_size()(2) - max_name_length - ...
1965 max_version_length - 7;
1966 if (max_dir_length < 20)
1967 max_dir_length = Inf;
1968 endif
1969
1970 h1 = postpad (h1, max_name_length + 1, " ");
1971 h2 = postpad (h2, max_version_length, " ");;
1972
1973 ## Print a header.
1974 header = sprintf("%s | %s | %s\n", h1, h2, h3);
1975 printf (header);
1976 tmp = sprintf (repmat ("-", 1, length(header)-1));
1977 tmp(length(h1)+2) = "+";
1978 tmp(length(h1)+length(h2)+5) = "+";
1979 printf ("%s\n", tmp);
1980
1981 ## Print the packages.
1982 format = sprintf ("%%%ds %%1s| %%%ds | %%s\n", max_name_length,
1983 max_version_length);
1984 [dummy, idx] = sort (names);
1985 for i = 1:num_packages
1986 cur_name = installed_pkgs_lst{idx(i)}.name;
1987 cur_version = installed_pkgs_lst{idx(i)}.version;
1988 cur_dir = installed_pkgs_lst{idx(i)}.dir;
1989 if (length (cur_dir) > max_dir_length)
1990 first_char = length (cur_dir) - max_dir_length + 4;
1991 first_filesep = strfind (cur_dir(first_char:end), filesep());
1992 if (! isempty (first_filesep))
1993 cur_dir = cstrcat ("...",
1994 cur_dir((first_char + first_filesep(1) - 1):end));
1995 else
1996 cur_dir = cstrcat ("...", cur_dir(first_char:end));
1997 endif
1998 endif
1999 if (installed_pkgs_lst{idx(i)}.loaded)
2000 cur_loaded = "*";
2001 else
2002 cur_loaded = " ";
2003 endif
2004 printf (format, cur_name, cur_loaded, cur_version, cur_dir);
2005 endfor
2006endfunction
2007
2008function load_packages (files, handle_deps, local_list, global_list)
2009 installed_pkgs_lst = installed_packages (local_list, global_list);
2010 num_packages = length (installed_pkgs_lst);
2011
2012 ## Read package names and installdirs into a more convenient format.
2013 pnames = pdirs = cell (1, num_packages);
2014 for i = 1:num_packages
2015 pnames{i} = installed_pkgs_lst{i}.name;
2016 pdirs{i} = installed_pkgs_lst{i}.dir;
2017 endfor
2018
2019 ## Load all.
2020 if (length (files) == 1 && strcmp (files{1}, "all"))
2021 idx = [1:length(installed_pkgs_lst)];
2022 ## Load auto.
2023 elseif (length (files) == 1 && strcmp (files{1}, "auto"))
2024 idx = [];
2025 for i = 1:length (installed_pkgs_lst)
2026 if (exist (fullfile (pdirs{i}, "packinfo", ".autoload"), "file"))
2027 idx (end + 1) = i;
2028 endif
2029 endfor
2030 ## Load package_name1 ...
2031 else
2032 idx = [];
2033 for i = 1:length (files)
2034 idx2 = find (strcmp (pnames, files{i}));
2035 if (! any (idx2))
2036 error ("package %s is not installed", files{i});
2037 endif
2038 idx (end + 1) = idx2;
2039 endfor
2040 endif
2041
2042 ## Load the packages, but take care of the ordering of dependencies.
2043 load_packages_and_dependencies (idx, handle_deps, installed_pkgs_lst, true);
2044endfunction
2045
2046function unload_packages (files, handle_deps, local_list, global_list)
2047 installed_pkgs_lst = installed_packages (local_list, global_list);
2048 num_packages = length (installed_pkgs_lst);
2049
2050 ## Read package names and installdirs into a more convenient format.
2051 pnames = pdirs = cell (1, num_packages);
2052 for i = 1:num_packages
2053 pnames{i} = installed_pkgs_lst{i}.name;
2054 pdirs{i} = installed_pkgs_lst{i}.dir;
2055 pdeps{i} = installed_pkgs_lst{i}.depends;
2056 endfor
2057
2058 ## Get the current octave path.
2059 p = split_by (path(), pathsep ());
2060
2061 if (length (files) == 1 && strcmp (files{1}, "all"))
2062 ## Unload all.
2063 dirs = pdirs;
2064 desc = installed_pkgs_lst;
2065 else
2066 ## Unload package_name1 ...
2067 dirs = {};
2068 desc = {};
2069 for i = 1:length (files)
2070 idx = strcmp (pnames, files{i});
2071 if (! any (idx))
2072 error ("package %s is not installed", files{i});
2073 endif
2074 dirs{end+1} = pdirs{idx};
2075 desc{end+1} = installed_pkgs_lst{idx};
2076 endfor
2077 endif
2078
2079 ## Check for architecture dependent directories.
2080 archdirs = {};
2081 for i = 1:length (dirs)
2082 tmpdir = getarchdir (desc{i});
2083 if (exist (tmpdir, "dir"))
2084 archdirs{end+1} = dirs{i};
2085 archdirs{end+1} = tmpdir;
2086 else
2087 archdirs{end+1} = dirs{i};
2088 endif
2089 endfor
2090
2091 ## Unload the packages.
2092 for i = 1:length (archdirs)
2093 d = archdirs{i};
2094 idx = strcmp (p, d);
2095 if (any (idx))
2096 rmpath (d);
2097 ## FIXME: We should also check if we need to remove items from
2098 ## EXEC_PATH.
2099 endif
2100 endfor
2101endfunction
2102
2103function [status_out, msg_out] = rm_rf (dir)
2104 if (exist (dir))
2105 crr = confirm_recursive_rmdir ();
2106 unwind_protect
2107 confirm_recursive_rmdir (false);
2108 [status, msg] = rmdir (dir, "s");
2109 unwind_protect_cleanup
2110 confirm_recursive_rmdir (crr);
2111 end_unwind_protect
2112 else
2113 status = 1;
2114 msg = "";
2115 endif
2116 if (nargout > 0)
2117 status_out = status;
2118 endif
2119 if (nargout > 1)
2120 msg_out = msg;
2121 endif
2122endfunction
2123
2124function emp = dirempty (nm, ign)
2125 if (exist (nm, "dir"))
2126 if (nargin < 2)
2127 ign = {".", ".."};
2128 else
2129 ign = [{".", ".."}, ign];
2130 endif
2131 l = dir (nm);
2132 for i = 1:length (l)
2133 found = false;
2134 for j = 1:length (ign)
2135 if (strcmp (l(i).name, ign{j}))
2136 found = true;
2137 break;
2138 endif
2139 endfor
2140 if (! found)
2141 emp = false;
2142 return
2143 endif
2144 endfor
2145 emp = true;
2146 else
2147 emp = true;
2148 endif
2149endfunction
2150
2151function arch = getarch ()
2152 persistent _arch = cstrcat (octave_config_info("canonical_host_type"), ...
2153 "-", octave_config_info("api_version"));
2154 arch = _arch;
2155endfunction
2156
2157function archprefix = getarchprefix (desc, global_install)
2158 if ((nargin == 2 && global_install) || (nargin < 2 && issuperuser ()))
2159 archprefix = fullfile (octave_config_info ("libexecdir"), "octave",
2160 "packages", cstrcat(desc.name, "-", desc.version));
2161 else
2162 archprefix = desc.dir;
2163 endif
2164endfunction
2165
2166function archdir = getarchdir (desc)
2167 archdir = fullfile (desc.archprefix, getarch());
2168endfunction
2169
2170function s = issuperuser ()
2171 if ((ispc () && ! isunix ()) || (geteuid() == 0))
2172 s = true;
2173 else
2174 s = false;
2175 endif
2176endfunction
2177
2178function [status, output] = shell (cmd)
2179 persistent have_sh;
2180
2181 cmd = strrep (cmd, "\\", "/");
2182 if (ispc () && ! isunix ())
2183 if (isempty(have_sh))
2184 if (system("sh.exe -c \"exit\""))
2185 have_sh = false;
2186 else
2187 have_sh = true;
2188 endif
2189 endif
2190 if (have_sh)
2191 [status, output] = system (cstrcat ("sh.exe -c \"", cmd, "\""));
2192 else
2193 error ("Can not find the command shell")
2194 endif
2195 else
2196 [status, output] = system (cmd);
2197 endif
2198endfunction
2199
2200function newdesc = save_order (desc)
2201 newdesc = {};
2202 for i = 1 : length(desc)
2203 deps = desc{i}.depends;
2204 if (isempty (deps) || (length (deps) == 1 &&
2205 strcmp(deps{1}.package, "octave")))
2206 newdesc {end + 1} = desc{i};
2207 else
2208 tmpdesc = {};
2209 for k = 1 : length (deps)
2210 for j = 1 : length (desc)
2211 if (strcmp (desc{j}.name, deps{k}.package))
2212 tmpdesc{end+1} = desc{j};
2213 break;
2214 endif
2215 endfor
2216 endfor
2217 if (! isempty (tmpdesc))
2218 newdesc = {newdesc{:}, save_order(tmpdesc){:}, desc{i}};
2219 else
2220 newdesc{end+1} = desc{i};
2221 endif
2222 endif
2223 endfor
2224 ## Eliminate the duplicates.
2225 idx = [];
2226 for i = 1 : length (newdesc)
2227 for j = (i + 1) : length (newdesc)
2228 if (strcmp (newdesc{i}.name, newdesc{j}.name))
2229 idx (end + 1) = j;
2230 endif
2231 endfor
2232 endfor
2233 newdesc(idx) = [];
2234endfunction
2235
2236function load_packages_and_dependencies (idx, handle_deps, installed_pkgs_lst,
2237 global_install)
2238 idx = load_package_dirs (idx, [], handle_deps, installed_pkgs_lst);
2239 dirs = {};
2240 execpath = EXEC_PATH ();
2241 for i = idx;
2242 ndir = installed_pkgs_lst{i}.dir;
2243 dirs{end+1} = ndir;
2244 if (exist (fullfile (dirs{end}, "bin"), "dir"))
2245 execpath = cstrcat (fullfile (dirs{end}, "bin"), ":", execpath);
2246 endif
2247 tmpdir = getarchdir (installed_pkgs_lst{i});
2248 if (exist (tmpdir, "dir"))
2249 dirs{end + 1} = tmpdir;
2250 if (exist (fullfile (dirs{end}, "bin"), "dir"))
2251 execpath = cstrcat (fullfile (dirs{end}, "bin"), ":", execpath);
2252 endif
2253 endif
2254 endfor
2255
2256 ## Load the packages.
2257 if (length (dirs) > 0)
2258 addpath (dirs{:});
2259 endif
2260
2261 ## Add the binaries to exec_path.
2262 if (! strcmp (EXEC_PATH, execpath))
2263 EXEC_PATH (execpath);
2264 endif
2265endfunction
2266
2267function idx = load_package_dirs (lidx, idx, handle_deps, installed_pkgs_lst)
2268 for i = lidx
2269 if (isfield (installed_pkgs_lst{i}, "loaded") &&
2270 installed_pkgs_lst{i}.loaded)
2271 continue;
2272 else
2273 if (handle_deps)
2274 deps = installed_pkgs_lst{i}.depends;
2275 if ((length (deps) > 1) || (length (deps) == 1 &&
2276 ! strcmp(deps{1}.package, "octave")))
2277 tmplidx = [];
2278 for k = 1 : length (deps)
2279 for j = 1 : length (installed_pkgs_lst)
2280 if (strcmp (installed_pkgs_lst{j}.name, deps{k}.package))
2281 tmplidx (end + 1) = j;
2282 break;
2283 endif
2284 endfor
2285 endfor
2286 idx = load_package_dirs (tmplidx, idx, handle_deps,
2287 installed_pkgs_lst);
2288 endif
2289 endif
2290 if (isempty (find(idx == i)))
2291 idx (end + 1) = i;
2292 endif
2293 endif
2294 endfor
2295endfunction
2296
2297function dep = is_architecture_dependent (nm)
2298 persistent archdepsuffix = {".oct",".mex",".a",".lib",".so",".so.*",".dll","dylib"};
2299
2300 dep = false;
2301 for i = 1 : length (archdepsuffix)
2302 ext = archdepsuffix{i};
2303 if (ext(end) == "*")
2304 isglob = true;
2305 ext(end) = [];
2306 else
2307 isglob = false;
2308 endif
2309 pos = findstr (nm, ext);
2310 if (pos)
2311 if (! isglob && (length(nm) - pos(end) != length(ext) - 1))
2312 continue;
2313 endif
2314 dep = true;
2315 break;
2316 endif
2317 endfor
2318endfunction