Commits

Anonymous committed 9b24eb4

- added support for different coding systems in dictionaries
(suggested by Eugene Morozov)
- added lpath.el for creating xemacs package with Makefile.upstream

  • Participants
  • Parent commits e9b29cb

Comments (0)

Files changed (5)

+2001-12-09  Torsten Hilbrich <dictionary@myrkr.in-berlin.de>
+
+	* Added HTTP-proxy support (using the HTTP CONNECT)
+	* Added support for dictionaries with encodings other
+	  than utf-8
+
 2001-09-08  Steve Youngs  <youngs@xemacs.org>
 
 	* Initial XEmacs Package version.
 # the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 # Boston, MA 02111-1307, USA.
 
-VERSION = 1.01
-AUTHOR_VERSION = 1.5.1
+VERSION = 1.00
+AUTHOR_VERSION = 1.6
 MAINTAINER = Torsten Hilbrich <dictionary@myrkr.in-berlin.de>
 PACKAGE = dictionary
 PKG_TYPE = regular

File Makefile.upstream

 EMACS=emacs
 
-VERSION=1.5
+VERSION=1.7
 PACKAGE=dictionary
 TYPE=comm
 XEMACS-PACKAGE=$(PACKAGE)-$(VERSION)-pkg.tar.gz

File dictionary.el

-; dictionary.el -- an interface to RFC 2229 dictionary server
+ ;; dictionary.el -- an interface to RFC 2229 dictionary server
 
-;; Author: Torsten Hilbrich <dictionary@myrkr.in-berlin.de>
-;; Keywords: interface, dictionary
-;; $Id$
+ ;; Author: Torsten Hilbrich <dictionary@myrkr.in-berlin.de>
+ ;; Keywords: interface, dictionary
+ ;; $Id$
 
-;; This file is free software; you can redistribute it and/or modify
-;; it under the terms of the GNU General Public License as published by
-;; the Free Software Foundation; either version 2, or (at your option)
-;; any later version.
+ ;; This file is free software; you can redistribute it and/or modify
+ ;; it under the terms of the GNU General Public License as published by
+ ;; the Free Software Foundation; either version 2, or (at your option)
+ ;; any later version.
 
-;; This file is distributed in the hope that it will be useful,
-;; but WITHOUT ANY WARRANTY; without even the implied warranty of
-;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-;; GNU General Public License for more details.
+ ;; This file is distributed in the hope that it will be useful,
+ ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ ;; GNU General Public License for more details.
 
-;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to
-;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-;; Boston, MA 02111-1307, USA.
+ ;; You should have received a copy of the GNU General Public License
+ ;; along with GNU Emacs; see the file COPYING.  If not, write to
+ ;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ ;; Boston, MA 02111-1307, USA.
 
-(eval-when-compile
-  (require 'cl))
+ (eval-when-compile
+   (require 'cl))
 
-(require 'easymenu)
-(require 'custom)
-(require 'connection)
-(require 'link)
+ (require 'easymenu)
+ (require 'custom)
+ (require 'connection)
+ (require 'link)
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Stuff for customizing.
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Stuff for customizing.
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-(eval-when-compile
-  (unless (fboundp 'defface)
-	  (message "Please update your custom.el file: %s"
-		   "http://www.dina.kvl.dk/~abraham/custom/"))
-  
-  (unless (fboundp 'defgroup)
-	  (defmacro defgroup (&rest ignored))
-	  (defmacro defcustom (var value doc &rest ignored)
-	    (list 'defvar var value doc))))
+ (eval-when-compile
+   (unless (fboundp 'defface)
+	   (message "Please update your custom.el file: %s"
+		    "http://www.dina.kvl.dk/~abraham/custom/"))
 
-(defgroup dictionary nil
-  "Client for accessing the dictd server based dictionaries"
-  :group 'help
-  :group 'hypermedia)
+   (unless (fboundp 'defgroup)
+	   (defmacro defgroup (&rest ignored))
+	   (defmacro defcustom (var value doc &rest ignored)
+	     (list 'defvar var value doc))))
 
-(defcustom dictionary-server
-  "dict.org"
-  "This server is contacted for searching the dictionary"
-  :group 'dictionary
-  :type 'string)
+ (defgroup dictionary nil
+   "Client for accessing the dictd server based dictionaries"
+   :group 'hypermedia)
 
-(defcustom dictionary-port
-  2628
-  "The port of the dictionary server.
-This port is propably always 2628 so there should be no need to modify it."
-  :group 'dictionary
-  :type 'number)
+ (defgroup dictionary-proxy nil
+   "Proxy configuration options for the dictionary client"
+   :group 'dictionary)
 
-(defcustom dictionary-identification
-  "dictionary.el emacs lisp dictionary client"
-  "This is the identification string that will be sent to the server."
-  :group 'dictionary
-  :type 'string)
+ (defcustom dictionary-server
+   "dict.org"
+   "This server is contacted for searching the dictionary"
+   :group 'dictionary
+   :type 'string)
 
-(defcustom dictionary-default-dictionary
-  "*"
-  "The dictionary which is used for searching definitions and matching.
-* and ! have a special meaning, * search all dictionaries, ! search until
-one dictionary yields matches."
-  :group 'dictionary
-  :type 'string)
+ (defcustom dictionary-port
+   2628
+   "The port of the dictionary server.
+ This port is propably always 2628 so there should be no need to modify it."
+   :group 'dictionary
+   :type 'number)
 
-(defcustom dictionary-default-strategy
-  "."
-  "The default strategy for listing matching words."
-  :group 'dictionary
-  :type 'string)
+ (defcustom dictionary-identification
+   "dictionary.el emacs lisp dictionary client"
+   "This is the identification string that will be sent to the server."
+   :group 'dictionary
+   :type 'string)
 
-(defcustom dictionary-create-buttons
-  t
-  "Create some clickable buttons on top of the window if non-nil"
-  :group 'dictionary
-  :type 'boolean)
+ (defcustom dictionary-default-dictionary
+   "*"
+   "The dictionary which is used for searching definitions and matching.
+ * and ! have a special meaning, * search all dictionaries, ! search until
+ one dictionary yields matches."
+   :group 'dictionary
+   :type 'string)
 
-(defcustom dictionary-mode-hook
-  nil
-  "Hook run in dictionary mode buffers."
-  :group 'dictionary
-  :type 'hook)
+ (defcustom dictionary-default-strategy
+   "."
+   "The default strategy for listing matching words."
+   :group 'dictionary
+   :type 'string)
 
-(if (fboundp 'defface)
-    (progn
+ (defcustom dictionary-create-buttons
+   t
+   "Create some clickable buttons on top of the window if non-nil."
+   :group 'dictionary
+   :type 'boolean)
 
-(defface dictionary-word-entry-face
-  '((((type x))
-     (:italic t))
-    (((type tty) (class color))
-     (:foreground "green"))
-    (t
-     (:inverse t)))
-  "The face that is used for displaying the initial word entry line."
-  :group 'dictionary)
+ (defcustom dictionary-mode-hook
+   nil
+   "Hook run in dictionary mode buffers."
+   :group 'dictionary
+   :type 'hook)
 
-(defface dictionary-button-face
-  '((t
-     (:bold t)))
-  "The face that is used for displaying buttons."
-  :group 'dictionary)
+ (defcustom dictionary-use-http-proxy
+   nil
+   "Connects via a HTTP proxy using the CONNECT command when not nil."
+   :group 'dictionary-proxy
+   :type 'boolean)
 
-(defface dictionary-reference-face
-  '((((type x)
-      (class color)
-      (background dark))
-     (:foreground "yellow"))
-    (((type tty)
-      (class color)
-      (background dark))
-     (:foreground "cyan"))
-    (((class color)
-      (background light))
-     (:foreground "blue"))
-    (t
-     (:underline t)))
-  
-  "The face that is used for displaying a reference word."
-  :group 'dictionary)
+ (defcustom dictionary-proxy-server
+   "proxy"
+   "The name of the HTTP proxy to use when dictionary-use-http-proxy is set."
+   :group 'dictionary-proxy
+   :type 'string)
+
+ (defcustom dictionary-proxy-port
+   3128
+   "The port of the proxy server, used only when dictionary-use-http-proxy is set."
+   :group 'dictionary-proxy
+   :type 'number)
+
+;; Define only when coding-system-list is available
+(when (fboundp 'coding-system-list)
+  (defcustom dictionary-coding-systems-for-dictionaries
+    '( ("mueller" . koi8-r))
+    "Mapping of dictionaries to coding systems.
+ Each entry in this list defines the coding system to be used for that
+ dictionary.  The default coding system for all other dictionaries
+ is utf-8"
+    :group 'dictionary
+    :type `(repeat (cons :tag "Association" 
+			 (string :tag "Dictionary name") 
+			 (choice :tag "Coding system"
+				 ,@(mapcar (lambda (x) (list 'const x))
+					   (coding-system-list))
+				 ))))
 
 )
 
-;; else
-(copy-face 'italic 'dictionary-word-entry-face)
-(copy-face 'bold 'dictionary-button-face)
-(copy-face 'default 'dictionary-reference-face)
-(set-face-foreground 'dictionary-reference-face "blue"))
+ (if (fboundp 'defface)
+     (progn
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Buffer local variables for storing the current state
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (defface dictionary-word-entry-face
+   '((((type x))
+      (:italic t))
+     (((type tty) (class color))
+      (:foreground "green"))
+     (t
+      (:inverse t)))
+   "The face that is used for displaying the initial word entry line."
+   :group 'dictionary)
 
-(defvar dictionary-window-configuration
-  nil
-  "The window configuration to be restored upon closing the buffer")
+ (defface dictionary-button-face
+   '((t
+      (:bold t)))
+   "The face that is used for displaying buttons."
+   :group 'dictionary)
 
-(defvar dictionary-selected-window
-  nil
-  "The currently selected window")
+ (defface dictionary-reference-face
+   '((((type x)
+       (class color)
+       (background dark))
+      (:foreground "yellow"))
+     (((type tty)
+       (class color)
+       (background dark))
+      (:foreground "cyan"))
+     (((class color)
+       (background light))
+      (:foreground "blue"))
+     (t
+      (:underline t)))
 
-(defvar dictionary-position-stack
-  nil
-  "The history buffer for point and window position")
+   "The face that is used for displaying a reference word."
+   :group 'dictionary)
 
-(defvar dictionary-data-stack
-  nil
-  "The history buffer for functions and arguments")
+ )
 
-(defvar dictionary-positions
-  nil
-  "The current positions")
+ ;; else
+ (copy-face 'italic 'dictionary-word-entry-face)
+ (copy-face 'bold 'dictionary-button-face)
+ (copy-face 'default 'dictionary-reference-face)
+ (set-face-foreground 'dictionary-reference-face "blue"))
 
-(defvar dictionary-current-data
-  nil
-  "The item that will be placed on stack next time")
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Buffer local variables for storing the current state
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Global variables
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defvar dictionary-mode-map
-  nil
-  "Keymap for dictionary mode")
+ (defvar dictionary-window-configuration
+   nil
+   "The window configuration to be restored upon closing the buffer")
 
-(defvar dictionary-connection 
-  nil
-  "The current network connection")
+ (defvar dictionary-selected-window
+   nil
+   "The currently selected window")
 
-(defvar dictionary-instances
-  0
-  "The number of open dictionary buffers")
+ (defvar dictionary-position-stack
+   nil
+   "The history buffer for point and window position")
 
-(defvar dictionary-marker 
-  nil
-  "Stores the point position while buffer display.")
+ (defvar dictionary-data-stack
+   nil
+   "The history buffer for functions and arguments")
 
-(defvar dictionary-color-support 
-  (condition-case nil
-      (x-display-color-p)
-    (error nil))
-  "Stores the point position while buffer display.")
+ (defvar dictionary-positions
+   nil
+   "The current positions")
 
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;; Basic function providing startup actions
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (defvar dictionary-current-data
+   nil
+   "The item that will be placed on stack next time")
 
-;;;###autoload
-(defun dictionary-mode ()
-  "This is a mode for searching a dictionary server implementing
-the protocol defined in RFC 2229.
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Global variables
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ (defvar dictionary-mode-map
+   nil
+   "Keymap for dictionary mode")
 
-This is a quick reference to this mode describing the default key bindings:
+ (defvar dictionary-connection 
+   nil
+   "The current network connection")
 
-* q close the dictionary buffer
-* h display this help information
-* s ask for a new word to search
-* d search the word at point
-* n or Tab place point to the next link
-* p or S-Tab place point to the prev link
+ (defvar dictionary-instances
+   0
+   "The number of open dictionary buffers")
 
-* m ask for a pattern and list all matching words.
-* D select the default dictionary
-* M select the default search strategy
+ (defvar dictionary-marker 
+   nil
+   "Stores the point position while buffer display.")
 
-* Return or Button2 visit that link
-* M-Return or M-Button2 search the word beneath link in all dictionaries
-"
+ (defvar dictionary-color-support 
+   (condition-case nil
+       (x-display-color-p)
+     (error nil))
+   "Stores the point position while buffer display.")
 
-  (unless (eq major-mode 'dictionary-mode)
-    (incf dictionary-instances))
-  
-  (kill-all-local-variables)
-  (buffer-disable-undo)
-  (use-local-map dictionary-mode-map)
-  (setq major-mode 'dictionary-mode)
-  (setq mode-name "Dictionary")
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+ ;; Basic function providing startup actions
+ ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
-  (make-local-variable 'dictionary-data-stack)
-  (setq dictionary-data-stack nil)
-  (make-local-variable 'dictionary-position-stack)
-  (setq dictionary-position-stack nil)
+ ;;;###autoload
+ (defun dictionary-mode ()
+   "This is a mode for searching a dictionary server implementing
+ the protocol defined in RFC 2229.
 
-  (make-local-variable 'dictionary-current-data)
-  (make-local-variable 'dictionary-positions)
+ This is a quick reference to this mode describing the default key bindings:
 
-  (make-local-variable 'dictionary-default-dictionary)
-  (make-local-variable 'dictionary-default-strategy)
+ * q close the dictionary buffer
+ * h display this help information
+ * s ask for a new word to search
+ * d search the word at point
+ * n or Tab place point to the next link
+ * p or S-Tab place point to the prev link
 
-  (make-local-hook 'kill-buffer-hook)
-  (add-hook 'kill-buffer-hook 'dictionary-close t t)
-  (run-hooks 'dictionary-mode-hook))
+ * m ask for a pattern and list all matching words.
+ * D select the default dictionary
+ * M select the default search strategy
 
-;;;###autoload
-(defun dictionary ()
-  "Create a new dictonary buffer and install dictionary-mode"
-  (interactive)
-  (let ((coding-system nil))
-    (if (and (functionp 'coding-system-list)
-	     (member 'utf-8 (coding-system-list)))
- 	(setq coding-system 'utf-8))
-    (let ((coding-system-for-read coding-system)
-          (coding-system-for-write coding-system))
-      (let ((buffer (generate-new-buffer "*Dictionary buffer*"))
-            (window-configuration (current-window-configuration))
-	    (selected-window (frame-selected-window)))
+ * Return or Button2 visit that link
+ * M-Return or M-Button2 search the word beneath link in all dictionaries
+ "
+
+   (unless (eq major-mode 'dictionary-mode)
+     (incf dictionary-instances))
+
+   (kill-all-local-variables)
+   (buffer-disable-undo)
+   (use-local-map dictionary-mode-map)
+   (setq major-mode 'dictionary-mode)
+   (setq mode-name "Dictionary")
+
+   (make-local-variable 'dictionary-data-stack)
+   (setq dictionary-data-stack nil)
+   (make-local-variable 'dictionary-position-stack)
+   (setq dictionary-position-stack nil)
+
+   (make-local-variable 'dictionary-current-data)
+   (make-local-variable 'dictionary-positions)
+
+   (make-local-variable 'dictionary-default-dictionary)
+   (make-local-variable 'dictionary-default-strategy)
+
+   (make-local-hook 'kill-buffer-hook)
+   (add-hook 'kill-buffer-hook 'dictionary-close t t)
+   (run-hooks 'dictionary-mode-hook))
+
+ ;;;###autoload
+ (defun dictionary ()
+   "Create a new dictonary buffer and install dictionary-mode"
+;;    (interactive)
+;;    (let ((coding-system nil))
+;;      (if (and (fboundp 'coding-system-list)
+;; 	      (member 'utf-8 (coding-system-list)))
+;; 	 (setq coding-system 'utf-8))
+;;     (let ((coding-system-for-read coding-system)
+;;           (coding-system-for-write coding-system))
+       (let ((buffer (generate-new-buffer "*Dictionary buffer*"))
+             (window-configuration (current-window-configuration))
+ 	    (selected-window (frame-selected-window)))
 	
         (switch-to-buffer-other-window buffer)
         (dictionary-mode)
         (dictionary-check-connection)
 	(dictionary-new-buffer)
 	(dictionary-store-positions)
-	(dictionary-store-state 'dictionary-new-buffer nil)))))
+	(dictionary-store-state 'dictionary-new-buffer nil)));))
 
 (defun dictionary-new-buffer (&rest ignore)
   "Create a new and clean buffer"
   (if (not (and dictionary-connection
 		(eq (connection-status dictionary-connection) 'up)))
       (let ((coding-system nil))
-	(if (and (functionp 'coding-system-list)
+	(if (and (fboundp 'coding-system-list)
 		 (member 'utf-8 (coding-system-list)))
 	    (setq coding-system 'utf-8))
 	(let ((coding-system-for-read coding-system)
 		   dictionary-port)
 	  (connection-close dictionary-connection)
 	  (setq dictionary-connection
-		(connection-open dictionary-server dictionary-port))
+		(if dictionary-use-http-proxy
+		    (connection-open dictionary-proxy-server 
+				     dictionary-proxy-port)
+		  (connection-open dictionary-server dictionary-port)))
+
+	  (when dictionary-use-http-proxy
+	    (message "Proxy CONNECT to %s:%d" 
+		     dictionary-proxy-server
+		     dictionary-proxy-port)
+	    (dictionary-send-command (format "CONNECT %s:%d HTTP/1.1"
+					     dictionary-server
+					     dictionary-port))
+	    ;; just a \r\n combination
+	    (dictionary-send-command "")
+
+	    ;; read first line of reply
+	    (let* ((reply (dictionary-read-reply))
+		   (reply-list (dictionary-split-string reply)))
+	      ;; first item is protocol, second item is code
+	      (unless (= (string-to-number (cadr reply-list)) 200)
+		(error "Bad reply from proxy server %s" reply))
+	      
+	      ;; skip the following header lines until empty found
+	      (while (not (equal reply ""))
+		(setq reply (dictionary-read-reply)))))
+
 	  (dictionary-check-initial-reply)
 	  (dictionary-send-command (concat "client " dictionary-identification))
 	  (let ((reply (dictionary-read-reply-and-split)))
 (defun dictionary-read-reply ()
   "Read the reply line from the server"
   (let ((answer (connection-read-crlf dictionary-connection)))
-    (if (string-match "\r" answer)
+    (if (string-match "\r?\n" answer)
 	(substring answer 0 (match-beginning 0))
       answer)))
 
     (and (numberp number)
 	 (= number code))))
     
+(defun dictionary-decode-charset (text dictionary)
+  "Convert the text from the charset defined by the dictionary given."
+  (let ((coding-system 
+	(if (boundp 'dictionary-coding-systems-for-dictionaries)
+	    (cdr (assoc dictionary 
+			dictionary-coding-systems-for-dictionaries)))))
+    (if coding-system
+	(let ((new-text (decode-coding-string text coding-system)))
+	  ;; the following is a work-around for emacs-20.7.2
+	  (while (string-match "\201" new-text)
+	    (setq new-text (replace-match "" t t new-text)))
+	  new-text)
+      text)))
+
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;; Communication functions
 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
   (message "Searching for %s in %s" word dictionary)
   (dictionary-send-command (concat "define \"" dictionary "\" \""
-				   word "\""))
+				   word
+				   "\""))
   (message nil)
   (let ((reply (dictionary-read-reply-and-split)))
     (if (dictionary-check-reply reply 552)
 (defun dictionary-post-buffer ()
   "These commands are executed at the end of a new buffer"
   (goto-char dictionary-marker)
+  
   (set-buffer-modified-p nil)
   (toggle-read-only 1))
 
 (defun dictionary-display-word-definition (reply word dictionary)
   "Insert the definition for the current word"
   (let ((start (point)))
-    (insert reply)
+    (insert (dictionary-decode-charset reply dictionary))
     (insert "\n\n")
     (let ((regexp "\\({+\\)\\([^ '\"][^}]*\\)\\(}+\\)"))
       (goto-char start)
     (let ((result (mapcar (lambda (item)
 			    (let* ((list (dictionary-split-string item))
 				   (dictionary (car list))
-				   (word (cadr list)))
+				   (word (dictionary-decode-charset 
+					  (cadr list) dictionary)))
 			      (if (equal word "")
 				  [ "-" nil nil]
 				(vector (concat "[" dictionary "] " word)
+;; Shut up.
+
+(setq load-path (cons "." load-path))
+