;;; package.lisp
;;;
;;; Copyright (C) 2008 Erik Huelsmann
;;; $Id$
;;;
;;; This program 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
;;; of the License, or (at your option) any later version.
;;;
;;; This program 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 this program; if not, write to the Free Software
;;; Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
;;;
;;; As a special exception, the copyright holders of this library give you
;;; permission to link this library with independent modules to produce an
;;; executable, regardless of the license terms of these independent
;;; modules, and to copy and distribute the resulting executable under
;;; terms of your choice, provided that you also meet, for each linked
;;; independent module, the terms and conditions of the license of that
;;; module.  An independent module is a module which is not derived from
;;; or based on this library.  If you modify this library, you may extend
;;; this exception to your version of the library, but you are not
;;; obligated to do so.  If you do not wish to do so, delete this
;;; exception statement from your version.

(in-package "SYSTEM")

;; Redefines make-package from boot.lisp

(defun make-package (name &key nicknames use)
  (restart-case
      (progn
        (when (find-package name)
          (error 'simple-error "Package ~A already exists." name))
        (dolist (nick nicknames)
          (when (find-package nick)
            (error 'package-error :package nick)))
        (%make-package name nicknames use))
    (use-existing-package ()
      :report "Use existing package"
      (return-from make-package (find-package name)))))

;; Redefines function from defpackage.lisp, because there it's lacking restart-case

(defun ensure-available-symbols (imports)
  (remove nil
          (mapcar #'(lambda (package-and-symbols)
                      (let* ((package (find-package (designated-package-name (car package-and-symbols))))
                             (new-symbols
                              (remove nil
                                      (mapcar #'(lambda (sym)
                                                  (restart-case
                                                      (progn
                                                        (unless (nth-value 1 (find-symbol sym package))
                                                          (error 'package-error
                                                                 "The symbol ~A is not present in package ~A." sym (package-name package)))
                                                        sym)
                                                    (skip ()
                                                      :report "Skip this symbol."
                                                      nil)))
                                              (cdr package-and-symbols)))))
                        (when new-symbols
                          (cons package new-symbols))))
                  imports)))




(defun import (symbols &optional (package *package* package-supplied-p))
  (dolist (symbol (if (listp symbols) symbols (list symbols)))
    (let* ((sym-name (string symbol))
           (local-sym (find-symbol sym-name package)))
      (restart-case
          (progn
            (when (and local-sym (not (eql symbol local-sym)))
              (error 'package-error
                     "Different symbol (~A) with the same name already accessible in package ~A."
                     local-sym (package-name package)))
            (if package-supplied-p
                (%import (list symbol) package) ;; in order to pass NIL, wrap in a list
                (%import (list symbol))))
        (unintern-existing ()
          :report (lambda (s) (format s "Unintern ~S and continue" local-sym))
          (unintern local-sym)
          (%import symbol))
        (skip ()
          :report "Skip symbol"))))
  T)

(defun delete-package (package)
  (with-simple-restart (continue "Ignore missing package.")
    (sys::%delete-package package)))

(defun add-package-local-nickname (local-nickname actual-package
                                   &optional (package-designator *package*))
  (let* ((local-nickname (string local-nickname))
         (package-designator (or (find-package package-designator)
                                 (error "Package ~A not found" package-designator)))
         (actual-package (or (find-package actual-package)
                             (error "Package ~A not found" actual-package))))
    (when (member local-nickname '("CL" "COMMON-LISP" "KEYWORD")
                  :test #'string=)
      (cerror "Continue anyway"
              "Trying to define a local nickname called ~A" local-nickname))
    (when (member local-nickname (list* (package-name package-designator)
                                        (package-nicknames package-designator))
                  :test #'string=)
      (cerror "Continue anyway"
              "Trying to override the name or nickname ~A  for package ~A ~
               with a local nickname for another package ~A"
              local-nickname package-designator actual-package))
    (sys::%add-package-local-nickname local-nickname actual-package
                                      package-designator)))
