;; INSPEC to BibTeX bibliography conversion routine.
;; Copyright 1990 Ralph P. Sobek
;; inspect2bibtex.el  version 1.2
;;
;;; 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 1, 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.
;;;
;;; A copy of the GNU General Public License can be obtained from this
;;; program's author (send electronic mail to [email protected]) or from
;;; the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA
;;; 02139, USA.
;;;
;;; Send bug reports, suggestions, or improvements to [email protected].

(autoload 'bibtex-move-outside-of-entry "bibtex-mode")
(provide 'inspec2)

(defvar *INSPEC-msg-buffer* (get-buffer-create "*INSPEC Output*"))

(defvar *INSPEC-msg-flag* nil)

(defvar *INSPEC-buffer* nil)

(defvar *BibTeX-buffer* nil)

(defvar *INSPEC-last-id* "")

;; INSPEC REGEXPs

(defconst INSPEC-header
 "^Quest Accession Number : \\([0-9]+\\)\n  \\(.*\\)  INSPEC  \\(.*\\)  Issue.*$")

(defconst INSPEC-beginning "^  ")
(defconst INSPEC-end    "\\(^  \\|\\'\\)")

(defconst INSPEC-blank  "[ \n] *")
(defconst INSPEC-sep
 (concat "\n?,"
         INSPEC-blank))

(defconst INSPEC-tail-sep
 (concat "\n?,\\("
         INSPEC-blank
         "\\|\\'\\)"))

(defconst INSPEC-tail   "Treatment: \\(.+\n\\)+\n")

(defconst INSPEC-year   "\\([0-9][0-9][0-9][0-9]\\)")

(defconst INSPEC-refs
 (concat "\\([0-9]+"
         INSPEC-blank
         "Refs\\)"))

(defconst INSPEC-ref-refs
 (concat INSPEC-refs
         INSPEC-tail-sep))

(defconst INSPEC-day    "[0-9][0-9]?")

(defconst INSPEC-month
 "\\(Jan\\.\\|Feb\\.\\|March\\|April\\|May\\|June\\|July\\|Aug\\.\\|Sept\\.\\|Oct\\.\\|Nov\\.\\|Dec\\.\\)")

(defconst INSPEC-dates
 (concat "\\(\\(.*\\)"
         INSPEC-blank
         "\\)?"
         INSPEC-year))

(defconst INSPEC-ref-dates
 (concat INSPEC-dates
         INSPEC-tail-sep))

(defconst INSPEC-d-month-flag 1)
(defconst INSPEC-d-month-index 2)
(defconst INSPEC-d-year 3)

(defconst INSPEC-volume "vol\\.\\([^ ,]+\\)")

(defconst INSPEC-ref-volume
 (concat INSPEC-volume
         INSPEC-tail-sep))

(defconst INSPEC-pages      "p\\.\n?\\([---0-9+]+\\)")

;; INSPEC Conference Paper REGEXPs

(defconst INSPEC-ref-pages
 (concat INSPEC-pages
         "\\("
         INSPEC-blank
         "\\(pp\\|supl\\)\\.\\)?\\( "
         INSPEC-volume
         "\\)?"
         INSPEC-tail-sep
         ))

(defconst INSPEC-ref-pages-index 1)
(defconst INSPEC-ref-pp-index 2)
(defconst INSPEC-ref-volume-index 5)

(defconst INSPEC-country
 (concat "\\("
         INSPEC-sep
         ".*\\)*Country"
         INSPEC-blank
         "of"
         INSPEC-blank
         "Publ\\.:"
         INSPEC-blank
         "\\(.*\\)$"))

(defconst INSPEC-conference-address "\\(\\([^,]+, \\)?.*\\)  ")

(defconst INSPEC-conference-address&date
 (concat INSPEC-conference-address
         "\\("
         INSPEC-dates
         "\\)$"))

(defconst INSPEC-c-address-index 1)
(defconst INSPEC-c-dates-index 3)
(defconst INSPEC-c-month-flag 4)
(defconst INSPEC-c-month-index 5)

;; INSPEC Journal Paper REGEXPs

(defconst INSPEC-journal-abbrev "\\(.+\\) (\\(.+\\))$")

(defconst INSPEC-journal-name
 (concat "\\(\\<[^.]+\\>\\)"
         INSPEC-blank))

(defconst INSPEC-journal-number "no\\.\\([^ ,]+\\)")

(defconst INSPEC-ref-number
 (concat INSPEC-journal-number
         INSPEC-tail-sep))

(defconst INSPEC-journal-reference-header
 (concat "\\("
         INSPEC-journal-name
         "\\)?"
         INSPEC-volume))

(defconst INSPEC-j-name-flag 1)
(defconst INSPEC-j-name-index 2)

;; INSPEC Report REGEXPs

(defconst INSPEC-report "Rep No\\. \\([0-9A-Za-z---#.]+\\)")

(defconst INSPEC-report-info
 (concat INSPEC-report
         "  \\([A-Z0-9][^,]*\\)"
         INSPEC-sep
         "\\(\\(.+\n\\)+\\)"
         INSPEC-end))

;; INSPEC Match Functions

(defun match-quest ()
 "Function to match the next INSPEC quest."
 (interactive)
 (let (num type start end (last (point)) begin)
   (cond ((re-search-forward INSPEC-header nil 'skip 1)
          (setq num (buffer-substring (match-beginning 2) (match-end 2)))
          (setq type (buffer-substring (match-beginning 3) (match-end 3)))
          (setq start (match-end 0))
          (INSPEC-dump-junk last (match-beginning 0))
          (re-search-forward paragraph-start)
          (setq end (match-beginning 0))

          (save-excursion
            (save-restriction
              (re-search-backward "^  Treatment:" start t 1)
              (narrow-to-region start (match-beginning 0))
              (goto-char start)
              (cond ((string= "Conference Paper" type)
                     (match-conference-entry))
                    ((string= "Journal Paper" type)
                     (match-journal-entry))
                    ((string= "Report Section" type)
                     (match-in-collection-entry))
                    ((string= "Report" type)
                     (match-report-entry))
                    ((string= "Conference Proceedings" type)
                     (match-proceedings-entry))
                    ((string= "Book Chapter" type)
                     (match-in-book-entry))
                    (t (error "Unknown INSPEC type encountered: %s" type))))))
         (t (INSPEC-dump-junk last (point-max))))
))

(defun match-conference-entry ()
 "Function to match INSPEC Conference Paper."
 (let ((title (c2string(match-field)))
       (authors (match-field))
       (authors-address (match-field))
       conference
       conference-ref publisher editors organization address dates volume
       annote journal-name number month year pages note
       assoc-value)


   (setq authors (squeeze (convert-names (car authors) (car (cdr authors)))))

   (re-search-forward INSPEC-end nil t 1)

   (cond ((match-conf-address&dates (point) (match-field-end))
          (setq conference (c2string authors-address))
          (setq authors-address nil)
          (re-search-forward INSPEC-end nil t 1))
         (t (setq conference (buffer-substring (point) (match-field-end)))
            (re-search-forward INSPEC-end nil t 1)

            (if (match-conf-address&dates (point) (match-field-end))
                (re-search-forward INSPEC-end nil t 1)
              (INSPEC-msg num
                          "Missing proceedings address or dates in \"%s\"."
                          (buffer-substring (point) (point-max))))))


   (cond ((looking-at INSPEC-journal-abbrev)
          (setq journal-abbrev (list (match-beginning 1) (match-end 1)))
          (re-search-forward INSPEC-end nil t 1)

          (if (match-INSPEC-reference-field (point) (match-field-end))
              (re-search-forward INSPEC-end nil t 1)
            (INSPEC-msg num "Missing journal citation in \"%s\"."
                        (buffer-substring (point) (point-max))))

          (setq note (concat "In "
                             (squeeze conference)
                             ".  "
                             (squeeze dates)
                             ", "
                             (squeeze address)))

          (match-language-field)
          (match-sponsor-field)

          (add-annotation num)

          (add-entry "annote"  (squeeze annote))
          (add-entry "note"    note)
          (add-entry "organization"    (squeeze organization))
          (add-entry "pages"   (squeeze pages))
          (add-entry "number"  (squeeze number))
          (add-entry "volume"  (squeeze volume))
          (add-entry "month"   (squeeze month))
          (add-entry "year"    year)
          (add-entry "journal" (squeeze (cond ((and (stringp journal-name)
                                                    (not (string= journal-name
                                                                  "")))
                                               journal-name)
                                              (t (c2string journal-abbrev)))))
          (add-entry "title"   (squeeze title))
          (add-entry "author"  authors)
          (cons "Article" (cons (make-key authors) assoc-value)))

         ((match-INSPEC-reference-field (point) (match-field-end))

          (re-search-forward INSPEC-end nil t 1)

          (match-language-field)
          (match-publisher-field t)
          (match-pages-field)
          (match-isbn-field)
          (match-editors-field)
          (match-sponsor-field)

          (add-annotation num)

          (add-entry "annote"  (squeeze annote))
          (add-entry "note"    note)
          (add-entry "volume"  (squeeze volume))
          (add-entry "address" (squeeze address))
          (add-entry "publisher"       (squeeze publisher))
          (add-entry "organization"    (squeeze organization))
          (add-entry "pages"   (squeeze pages))
          (add-entry "editor"  (squeeze editors))
          (add-entry "month"   (squeeze month))
          (add-entry "year"    year)
          (add-entry "booktitle"       (squeeze conference))
          (add-entry "title"   (squeeze title))
          (add-entry "author"  authors)
          (cons "InProceedings" (cons (make-key authors) assoc-value)))
         (t (INSPEC-msg num "Missing conference citation in \"%s\"."
                        (buffer-substring (point) (point-max))))))
 )

(defun match-journal-entry ()
 "Function to match INSPEC Journal Paper."
 (let ((title (c2string(match-field)))
       (authors (match-field))
       authors-address journal-abbrev journal-name volume number
       month year pages note annote assoc-value)

   (re-search-forward INSPEC-end nil t 1)
   (cond ((not (looking-at INSPEC-journal-abbrev))
          (setq authors-address (list (point) (match-field-end)))
          (re-search-forward INSPEC-end nil t 1)
          (cond ((not (looking-at INSPEC-journal-abbrev))
                 (INSPEC-msg num "Missing journal abbrev. in \"%s\"."
                             (buffer-substring (point) (point-max))))
                (t
                 (setq journal-abbrev (list (match-beginning 1)
                                            (match-end 1)))
                 (re-search-forward INSPEC-end nil t 1))))

         (t (setq journal-abbrev (list (match-beginning 1) (match-end 1)))
          (re-search-forward INSPEC-end nil t 1)))

   (cond ((match-INSPEC-reference-field (point) (match-field-end))
          (re-search-forward INSPEC-end nil t 1))
         (t (INSPEC-msg num "Missing journal citation in \"%s\"."
                        (buffer-substring (point) (point-max)))))

   (match-language-field)

   (add-annotation num)

   (setq authors (squeeze (convert-names (car authors) (car (cdr authors)))))

   (add-entry "annote" (squeeze annote))
   (add-entry "note"   note)
   (add-entry "pages"  (squeeze pages))
   (add-entry "number" (squeeze number))
   (add-entry "volume" (squeeze volume))
   (add-entry "month"  (squeeze month))
   (add-entry "year"   (squeeze year))
   (add-entry "journal"        (squeeze (cond ((and (stringp journal-name)
                                                    (not (string= journal-name
                                                                  "")))
                                               journal-name)
                                              (t (c2string journal-abbrev)))))
   (add-entry "title"  (squeeze title))
   (add-entry "author" authors)
   (cons "Article" (cons (make-key authors) assoc-value))))

(defun match-proceedings-entry ()
 "Function to match INSPEC Conference Proceedings."
 (let ((title (c2string(match-field)))
       booktitle year volume number date publisher organization annote
       journal-name month pages note assoc-value)

   (re-search-forward INSPEC-end nil t 1)
   (if (match-conf-address&dates (point) (match-field-end))
       (re-search-forward INSPEC-end nil t 1)
     (INSPEC-msg num
                 "Missing proceedings address or dates in \"%s\"."
                 (buffer-substring (point) (point-max))))


   (cond ((looking-at INSPEC-journal-abbrev)
          (setq booktitle (buffer-substring (match-beginning 1) (match-end 1)))
          (re-search-forward INSPEC-end nil t 1)))

   (cond ((match-INSPEC-reference-field (point) (match-field-end))
          (re-search-forward INSPEC-end nil t 1))
         (t (INSPEC-msg num "Missing proceedings citation in \"%s\"."
                        (buffer-substring (point) (+ (point) 30)))))

   (match-language-field)
   (match-publisher-field)
   (match-pages-field)
   (match-sponsor-field)

   (add-annotation num)

   (add-entry "annote" (squeeze annote))
   ;;  (add-entry "note"       note)
   (add-entry "address"        (squeeze address))
   (add-entry "organization"   (squeeze organization))
   (add-entry "publisher"      (squeeze publisher))
   ;;  (add-entry "editor"     (squeeze editors))
   ;;  (add-entry "author"     (squeeze authors))
   (add-entry "month"  (squeeze month))
   (add-entry "year"   (squeeze year))
   (add-entry "booktitle"      (squeeze booktitle))
   (add-entry "title"  (squeeze title))
   (cons "Proceedings" (cons
                        (make-key (or organization publisher "Unknown"))
                        assoc-value)))
 )

(defun match-report-entry ()
 "Function to match INSPEC Report."
 (let ((title (c2string(match-field)))
       (authors (match-field))
       number institution address year month annote note journal-name volume
       assoc-value)

   (re-search-forward INSPEC-end nil t 1)

   (if (match-INSPEC-reference-field (point) (match-field-end))
       (re-search-forward INSPEC-end nil t 1)
     (INSPEC-msg num "Missing report date in \"%s\"."
                            (buffer-substring (point) (point-max))))

   (match-language-field)
   (match-pages-field)
   (cond ((looking-at INSPEC-report-info)
          (setq number (buffer-substring (match-beginning 1) (match-end 1)))
          (setq institution (buffer-substring (match-beginning 2)
                                              (match-end 2)))
          (setq address (buffer-substring (match-beginning 3) (match-end 3)))
          (re-search-forward INSPEC-end nil t 1))
         (t (INSPEC-msg num "Missing Report ref. in \"%s\"."
                        (buffer-substring (point) (point-max)))))


   (add-annotation num)

   (setq authors (squeeze (convert-names (car authors) (car (cdr authors)))))

   (add-entry "annote" (squeeze annote))
   (add-entry "note"   note)
   (add-entry "month"  (squeeze month))
   (add-entry "address"        (squeeze address))
   (add-entry "number" (squeeze number))
   (add-entry "year"   year)
   (add-entry "institution"    (squeeze institution))
   (add-entry "title"  (squeeze title))
   (add-entry "author" authors)
   (cons "TechReport" (cons (make-key authors) assoc-value)))
 )

(defun match-in-collection-entry ()
 "Function to match INSPEC Report Section."
 (let ((title (c2string(match-field)))
       (authors (match-field))
       (authors-address (match-field))
       booktitle note volume number date pages month year annote
       journal-name assoc-value)

   (re-search-forward INSPEC-end nil t 1)
   (cond ((not (match-INSPEC-reference-field (point) (match-field-end)))
          (setq booktitle (buffer-substring (point) (match-field-end)))
          (re-search-forward INSPEC-end nil t 1)

          (if (match-INSPEC-reference-field (point) (match-field-end))
              (re-search-forward INSPEC-end nil t 1)
            (INSPEC-msg num "Missing in-collection citation in \"%s\"."
                        (buffer-substring (point) (point-max)))))

         (t (setq booktitle (c2string authors-address))
            (setq authors-address nil)
            (re-search-forward INSPEC-end nil t 1)))

   (add-annotation num)

   (setq authors (squeeze (convert-names (car authors) (car (cdr authors)))))

   (match-language-field)

   (add-entry "note"   note)
   (add-entry "annote" (squeeze annote))
   ;;  (add-entry "publisher"  (squeeze publisher))
   ;;  (add-entry "editor"     (squeeze editor))
   (add-entry "year"   (squeeze year))
   (add-entry "booktitle"      (squeeze booktitle))
   (add-entry "pages"  (squeeze month))
   (add-entry "title"  (squeeze title))
   (add-entry "author" authors)
   (cons "InCollection" (cons (make-key authors) assoc-value))))

(defun match-in-book-entry ()
 "Function to match INSPEC Book Chapter."
 (let ((title (c2string(match-field)))
       (authors (match-field))
       (authors-address (match-field))
       booktitle publisher editors year month pages note annote
       journal-name volume number assoc-value)

   (re-search-forward INSPEC-end nil t 1)
   (cond ((not (match-INSPEC-reference-field (point) (match-field-end)))
          (setq booktitle (buffer-substring (point) (match-field-end)))
          (re-search-forward INSPEC-end nil t 1)

          (if (match-INSPEC-reference-field (point) (match-field-end))
              (re-search-forward INSPEC-end nil t 1)
            (INSPEC-msg num "Missing in-book citation in \"%s\"."
                        (buffer-substring (point) (point-max)))))

         (t (setq booktitle (c2string authors-address))
            (setq authors-address nil)
            (re-search-forward INSPEC-end nil t 1)))

   (match-language-field)
   (match-publisher-field t)
   (match-pages-field)
   (match-isbn-field)
   (match-editors-field)

   (add-annotation num)

   (setq authors (squeeze (convert-names (car authors) (car (cdr authors)))))

   (add-entry "annote" (squeeze annote))
   (add-entry "note"   note)
   (add-entry "pages"  (squeeze pages))
   (add-entry "editor" (squeeze editors))
   (add-entry "month"  (squeeze month))
   (add-entry "year"   year)
   (add-entry "publisher"      (squeeze publisher))
   (add-entry "booktitle"      (squeeze booktitle))
   (add-entry "title"  (squeeze title))
   (add-entry "author" authors)
   (cons "InBook" (cons (make-key authors) assoc-value))))

(defun match-INSPEC-reference-field (start end)
 "Function to match the INSPEC reference field in all INSPEC documents."
 (interactive "r")
 (let (flag journal-flag)
   (save-excursion
     (save-restriction
       (narrow-to-region start end)
       (goto-char start)
       (cond ((looking-at INSPEC-journal-reference-header)

              ;; Journal Papers, Conference Papers, and Conference
              ;; Proceedings may fall through this branch.
;;(/= start (match-beginning 0))
              (cond (
                     (match-beginning INSPEC-j-name-flag)
                     (setq journal-name (buffer-substring
                            (match-beginning INSPEC-j-name-index)
                            (match-end INSPEC-j-name-index)))
;;                          (buffer-substring start (1- (match-beginning 0)))
                     (goto-char (match-end INSPEC-j-name-flag))))

              (cond ((looking-at INSPEC-ref-volume)
                     (setq volume (buffer-substring
                                   (match-beginning 1) (match-end 1)))
                     (goto-char (match-end 0))))

              (cond ((looking-at INSPEC-ref-number)
                     (setq number (buffer-substring (match-beginning 1)
                                                    (match-end 1)))
                     (goto-char (match-end 0))))
              (setq flag t journal-flag t)))

       (cond ((looking-at INSPEC-ref-dates)

              ;; Conference Papers, Conference Proceedings, Reports,
              ;; Report Sections, Book Chapters, and also Journal
              ;; Papers may fall through this branch.

              (if (and (or (not month) journal-flag)
                       (match-beginning INSPEC-d-month-flag)
                       (/= (match-beginning INSPEC-d-month-index)
                           (match-end INSPEC-d-month-index)))
                  (setq month (buffer-substring
                               (match-beginning INSPEC-d-month-index)
                               (match-end INSPEC-d-month-index))))
              (setq year (buffer-substring
                          (match-beginning INSPEC-d-year)
                          (match-end INSPEC-d-year)))
              (goto-char (match-end 0))
              (setq flag t)))

       (cond ((looking-at INSPEC-ref-pages)
              (setq pages (buffer-substring
                           (match-beginning INSPEC-ref-pages-index)
                           (if (match-beginning INSPEC-ref-pp-index)
                               (match-end INSPEC-ref-pp-index)
                             (match-end INSPEC-ref-pages-index))))
              (if (match-beginning INSPEC-ref-volume-index)
                  (setq volume (buffer-substring
                                (match-beginning INSPEC-ref-volume-index)
                                (match-end INSPEC-ref-volume-index))))
              (goto-char (match-end 0))
              (setq flag t)))

       (cond ((looking-at INSPEC-ref-refs)
              ;; flag is just a temporary variable here
              (setq flag (buffer-substring (match-beginning 1)
                                                (match-end 1)))
              (if (/= 0 (string-to-int flag)) (add-annotation flag))
              (setq flag t)))))
   (if flag (goto-char (1+ end)))
   flag))

(defun match-language-field ()
 (cond ((looking-at "\\(In .*\\)$")
        (add-note (buffer-substring (match-beginning 1) (match-end 1)))
        (re-search-forward INSPEC-end nil t 1))))

(defun match-publisher-field (&optional errorflag)
 (cond ((looking-at "Publisher: \\(.+\\)\\.")
        (setq publisher (buffer-substring (match-beginning 1)
                                          (match-end 1)))
        (re-search-forward INSPEC-end nil t 1))
       (errorflag (INSPEC-msg num "Missing \"Publisher:\" field at \"%s\"."
                              (buffer-substring (point)
                                                (min (+(point) 20)
                                                     (point-max)))))))

(defun match-pages-field ()
 (cond ((looking-at "Pages: ")
        (re-search-forward INSPEC-end nil t 1))))

(defun match-isbn-field ()
 (cond ((looking-at "ISBN: ")
        (re-search-forward INSPEC-end nil t 1))))

(defun match-editors-field ()
 (cond ((looking-at "\\(.*\\) (Editors)$")
        (setq editors (convert-names (match-beginning 1)
                                     (match-end 1)))
        (re-search-forward INSPEC-end nil t 1))))

(defun match-sponsor-field ()
 (let (start)
   (cond ((looking-at "Sponsor: ")
          (setq start (match-end 0))
          (re-search-forward INSPEC-end nil t 1)
          (setq organization (buffer-substring start
                                               (1- (match-beginning 0))))))))

(defun match-conf-address&dates (start end)
   (save-excursion
     (save-restriction
       (narrow-to-region start end)
       (goto-char start)
       (cond ((looking-at INSPEC-conference-address&date)
               (setq address (buffer-substring
                              (match-beginning INSPEC-c-address-index)
                              (match-end INSPEC-c-address-index)))
               (setq dates (buffer-substring
                            (match-beginning INSPEC-c-dates-index)
                            (match-end INSPEC-c-dates-index)))

               (if (and (match-beginning INSPEC-c-month-flag)
                        (/= (match-beginning INSPEC-c-month-index)
                            (match-end INSPEC-c-month-index)))

                   (setq month (buffer-substring
                                (match-beginning INSPEC-c-month-index)
                                          (match-end INSPEC-c-month-index))))
               't)))))


(defun match-field-end ()
 (save-excursion (re-search-forward INSPEC-end nil t 1)
                 (1- (match-beginning 0))))

(defun match-field ()
 (re-search-forward INSPEC-beginning)
 (list (point)
       (match-field-end))
)

(defun convert-names (start end)
 "Function to convert list of names in INSPEC format (separated by
 `;'s) to BibTeX format (separated by ` and ').  The INSPEC names are
  in the buffer between START and END."
 (save-excursion
   (save-restriction
     (let (names)
       (narrow-to-region start end)
       (goto-char start)
       (while (and (/= (point) end)
                   (re-search-forward "\\([^;]*\\)\\(;\\|\\'\\)" end t 1))
         (setq names (nconc names (list(list (match-beginning 1)
                                             (match-end 1))))))
       (mapconcat (function(lambda (l) (buffer-substring (car l)
                                                         (car (cdr l)))))
                  names
                  " and "))))
)

(defun INSPEC-msg (num string &rest args)
 "Function to send an error message to the `*INSPEC-msg-buffer*'.
 The message consits of the INSPEC identity NUM, the formatting
 STRING and the ARGS to be formatted."
 (save-excursion
  (set-buffer *INSPEC-msg-buffer*)
  (if (string= num *INSPEC-last-id*)
      (insert-char ?  9)
    (setq *INSPEC-last-id* num)
    (insert (format "
In Ref. %s " num)))
  (insert (format string args) ?\n)
  (goto-char (point-max))))

(defun INSPEC-dump-junk (start end)
 "Function to check for extraneous junk and dump it to the
`*INSPEC-msg-buffer*'."
 (save-excursion
   (save-restriction
     (goto-char end)
     ;; There's an extra line before the INSPEC header
     (forward-line -1)
     (skip-chars-backward " \t\n" start)
     (if (/= start (point))
         (progn
           (setq end (point))
           (goto-char start)
           (skip-chars-forward " \t\n" end)
           (beginning-of-line)
           (if (/= end (point))
               (let ((flag *INSPEC-msg-flag*))
                 (narrow-to-region (point) end)
                 (set-buffer *INSPEC-msg-buffer*)
                 (or flag (progn
                            (insert
                             (format "%s:\n\n"
                                     (buffer-file-name *INSPEC-buffer*)))
                            (goto-char (point-max))))
                 (insert-buffer *INSPEC-buffer*)
                 (goto-char (point-max))
                 (insert-char ?\n 2))))))))

(defun c2string (lst)
 "Function to convert a list of 2 buffer indices into a string."
 (buffer-substring (car lst) (car (cdr lst))))

(defun squeeze (string)
 "Function to squeeze all white-space in a string to individual
 spaces.  White-space at the beginnig or the end of the string is
 eliminated."
 (and string
      (let (start (oldpos 0) newstring (len (length string)))
        (save-excursion
          (while (setq start (string-match "[ \n]+" string start))
            (cond ((= start 0))
                  ((= len (match-end 0))
                   (setq newstring (concat newstring
                                           (substring string oldpos start))))
                  (t (setq newstring (concat newstring
                                             (substring string oldpos start)
                                             " "))))
            (setq oldpos (setq start (match-end 0)))))
        (if (/= oldpos len) (concat newstring (substring string oldpos))
          newstring))))

(defun add-annotation (string)
 (setq annote (if (stringp annote)
                  (concat annote ", " string)
                string)))

(defun add-note (string)
 (setq note (if (stringp note)
                  (concat note ", " string)
                string)))

(defun add-entry (type entry)
 (if (and (stringp entry)
          (/= 0 (length entry)))
     (setq assoc-value (cons (cons type entry) assoc-value))))

;; BibTex conversion functions

(defun make-key (string)
 "Function to create a BibTeX key based upon STRING and dynamic
 `year' of the form <string>-nn."
 (string-match "\\<\\([^ ,]+\\)\\>[, ]?" string)
 (concat (substring string (match-beginning 1) (match-end 1))
         "-"
         (if (and year (stringp year) (= (length year) 4))
             (substring year 2)
           "??")))

(defun make-bibtex (entry-type key assoc-list)
 "Function to create a filled BibTeX entry from the ENTRY-TYPE,
 author KEY, and association list, ASSOC-LIST, of BibTeX field keys
 and their associated values."
 (bibtex-move-outside-of-entry)
 (insert (concat "@" entry-type "{" key ",\n\n}\n\n"))
 (previous-line 3)
 (insert (mapconcat (function make-bibtex-entry) assoc-list ",\n"))
 (re-search-forward "}\n\n" nil t 1))

(defun make-bibtex-entry (assoc)
 "Function to build BibTeX entry field from ASSOC's field key and
 value."
 (concat "     " (car assoc) " =       \"" (cdr assoc) "\""))

(defun convert-entry ()
 "Function to convert an INSPEC entry to its corresponding BibTeX
 entry."
 (let (quest)
   (setq quest (match-quest))
   (if (consp quest)
       (save-excursion
         (set-buffer *BibTeX-buffer*)
         (make-bibtex (car quest) (car (cdr quest)) (cdr (cdr quest)))))))

(defun inspec2bibtex (f1 f2)
 "Interactive function to convert an INSPEC file, F1, to the
 corresponding BibTeX file, F2."
 (interactive "fINSPEC file:\nFBibTeX file:")
 (find-file-read-only f1)
 (setq *INSPEC-buffer* (current-buffer))
 (setq *BibTeX-buffer* (find-file-noselect f2))
 (inspec2bibtex-region-1 (point-min) (point-max)))

(defun inspec2bibtex-region (start end f2)
 "Function to convert the region from START to END in the `*INSPEC
 buffer*' to BibTeX file F2."
 (interactive "r\nFBibTeX file:")
 (setq *INSPEC-buffer* (current-buffer))
 (setq *BibTeX-buffer* (find-file-noselect f2))
 (set-buffer *BibTeX-buffer*)
 (goto-char (point-max))
 (set-buffer *INSPEC-buffer*)
 (inspec2bibtex-region-1 start end))


(defun inspec2bibtex-region-1 (start end)
 (let ((max-msg (save-excursion (set-buffer *INSPEC-msg-buffer*)
                                (point-max))))
   (make-local-variable 'paragraph-start)
   (make-local-variable '*INSPEC-msg-flag*)
   (setq paragraph-start "^[ \f\n\t]*$")

   (save-excursion
     (save-restriction
       (narrow-to-region start end)
       (goto-char start)
       (while (< (point) end)
         (convert-entry))))

   (if (/= max-msg (save-excursion (set-buffer *INSPEC-msg-buffer*)
                                (point-max)))
       (pop-to-buffer *INSPEC-msg-buffer*))))