ding or following the quoted string is ignored."
  (save-excursion
    (goto-char start)
    (search-forward "\"")
    (setq start (point))
    (goto-char end)
    (search-backward "\"")
    (setq end (point)))
  (po-extract-part-unquoted buffer start end))

(defun po-extract-part-unquoted (buffer start end)
  "Extract and return the unquoted string in BUFFER going from START to END.
Surrounding quotes are already excluded by the position of START and END."
  (with-temp-buffer
   (insert-buffer-substring buffer start end)
   ;; Glue concatenated strings.
   (goto-char (point-min))
   (while (re-search-forward "\"[ \t]*\\\\?\n\\(#~\\)?[ \t]*\"" nil t)
     (replace-match "" t t))
   ;; Remove escaped newlines.
   (goto-char (point-min))
   (while (re-search-forward "\\\\[ \t]*\n" nil t)
     (replace-match "" t t))
   ;; Unquote individual characters.
   (goto-char (point-min))
   (while (re-search-forward "\\\\[\"abfnt\\0-7]" nil t)
     (cond ((eq (preceding-char) ?\") (replace-match "\"" t t))
           ((eq (preceding-char) ?a) (replace-match "\a" t t))
           ((eq (preceding-char) ?b) (replace-match "\b" t t))
           ((eq (preceding-char) ?f) (replace-match "\f" t t))
           ((eq (preceding-char) ?n) (replace-match "\n" t t))
           ((eq (preceding-char) ?t) (replace-match "\t" t t))
           ((eq (preceding-char) ?\\) (replace-match "\\" t t))
           (t (let ((value (- (preceding-char) ?0)))
                (replace-match "" t t)
                (while (looking-at "[0-7]")
                  (setq value (+ (* 8 value) (- (following-char) ?0)))
                  (replace-match "" t t))
                (insert value)))))
   (buffer-string)))

(defun po-eval-requoted (form prefix obsolete)
  "Eval FORM, which inserts a string, and return the string fully requoted.
If PREFIX, precede the result with its contents.  If OBSOLETE, comment all
generated lines in the returned string.  Evaluating FORM should insert the
wanted string in the buffer which is current at the time of evaluation.
If FORM is itself a string, then this string is used for insertion."
  (with-temp-buffer
    (if (stringp form)
        (insert form)
      (push-mark)
      (eval form))
    (goto-char (point-min))
    (let ((multi-line (re-search-forward "[^\n]\n+[^\n]" nil t)))
      (goto-char (point-min))
      (while (re-search-forward "[\"\a\b\f\n\r\t\\]" nil t)
        (cond ((eq (preceding-char) ?\") (replace-match "\\\"" t t))
              ((eq (preceding-char) ?\a) (replace-match "\\a" t t))
              ((eq (preceding-char) ?\b) (replace-match "\\b" t t))
              ((eq (preceding-char) ?\f) (replace-match "\\f" t t))
              ((eq (preceding-char) ?\n)
               (replace-match (if (or (not multi-line) (eobp))
                                  "\\n"
                                "\\n\"\n\"")
                              t t))
              ((eq (preceding-char) ?\r) (replace-match "\\r" t t))
              ((eq (preceding-char) ?\t) (replace-match "\\t" t t))
              ((eq (preceding-char) ?\\) (replace-match "\\\\" t t))))
      (goto-char (point-min))
      (if prefix (insert prefix " "))
      (insert (if multi-line "\"\"\n\"" "\""))
      (goto-char (point-max))
      (insert "\"")
      (if prefix (insert "\n"))
      (if obsolete
          (progn
            (goto-char (point-min))
            (while (not (eobp))
              (or (eq (following-char) ?\n) (insert "#~ "))
              (search-forward "\n"))))
      (buffer-string))))

(defun po-get-msgid ()
  "Extract and return the unquoted msgid string."
  (let ((string (po-extract-unquoted (current-buffer)
                                     po-start-of-msgid
                                     (or po-start-of-msgid_plural
                                         po-start-of-msgstr-block))))
    string))

(defun po-get-msgid_plural ()
  "Extract and return the unquoted msgid_plural string.
Return nil if it is not present."
  (if po-start-of-msgid_plural
      (let ((string (po-extract-unquoted (current-buffer)
                                         po-start-of-msgid_plural
                                         po-start-of-msgstr-block)))
        string)
    nil))

(defun po-get-msgstr-flavor ()
  "Helper function to detect msgstr and msgstr[] variants.
Returns one of \"msgstr\" or \"msgstr[i]\" for some i."
  (save-excursion
    (goto-char po-start-of-msgstr-form)
    (re-search-forward "^\\(#~[ \t]*\\)?\\(msgstr\\(\\[[0-9]\\]\\)?\\)")
    (match-string 2)))

(defun po-get-msgstr-form ()
  "Extract and return the unquoted msgstr string."
  (let ((string (po-extract-unquoted (current-buffer)
                                     po-start-of-msgstr-form
                                     po-end-of-msgstr-form)))
    string))

(defun po-set-msgid (form)
  "Replace the current msgid, using FORM to get a string.
Evaluating FORM should insert the wanted string in the current buffer.  If
FORM is itself a string, then this string is used for insertion.  The string
is properly requoted before the replacement occurs.

Returns 'nil' if the buffer has not been modified, for if the new msgid
described by FORM is merely identical to the msgid already in place."
  (let ((string (po-eval-requoted form "msgid" (eq po-entry-type 'obsolete))))
    (save-excursion
      (goto-char po-start-of-entry)
      (re-search-forward po-any-msgid-regexp po-start-of-msgstr-block)
      (and (not (string-equal (match-string-no-properties 0) string))
           (let ((buffer-read-only po-read-only))
             (replace-match string t t)
             (goto-char po-start-of-msgid)
             (po-find-span-of-entry)
             t)))))

(defun po-set-msgstr-form (form)
  "Replace the current msgstr or msgstr[], using FORM to get a string.
Evaluating FORM should insert the wanted string in the current buffer.  If
FORM is itself a string, then this string is used for insertion.  The string
is properly requoted before the replacement occurs.

Returns 'nil' if the buffer has not been modified, for if the new msgstr
described by FORM is merely identical to the msgstr already in place."
  (let ((string (po-eval-requoted form
                                  (po-get-msgstr-flavor)
                                  (eq po-entry-type 'obsolete))))
    (save-excursion
      (goto-char po-start-of-msgstr-form)
      (re-search-forward po-any-msgstr-form-regexp po-end-of-msgstr-form)
      (and (not (string-equal (match-string-no-properties 0) string))
           (let ((buffer-read-only po-read-only))
             (po-decrease-type-counter)
             (replace-match string t t)
             (goto-char po-start-of-msgid)
             (po-find-span-of-entry)
             (po-increase-type-counter)
             t)))))

(defun po-kill-ring-save-msgstr ()
  "Push the msgstr string from current entry on the kill ring."
  (interactive)
  (po-find-span-of-entry)
  (let ((string (po-get-msgstr-form)))
    (kill-new string)
    string))

(defun po-kill-msgstr ()
  "Empty the msgstr string from current entry, pushing it on the kill ring."
  (interactive)
  (po-kill-ring-save-msgstr)
  (if (po-set-msgstr-form "")
      (po-maybe-delete-previous-untranslated)))

(defun po-yank-msgstr ()
  "Replace the current msgstr string by the top of the kill ring."
  (interactive)
  (po-find-span-of-entry)
  (if (po-set-msgstr-form (if (eq last-command 'yank) '(yank-pop 1) '(yank)))
      (po-maybe-delete-previous-untranslated))
  (setq this-command 'yank))

(defun po-fade-out-entry ()
  "Mark an active entry as fuzzy; obsolete a fuzzy or untranslated entry;
or completely delete an obsolete entry, saving its msgstr on the kill ring."
  (interactive)
  (po-find-span-of-entry)

  (cond ((eq po-entry-type 'translated)
         (po-decrease-type-counter)
         (po-add-attribute "fuzzy")
         (po-current-entry)
         (po-increase-type-counter))

        ((or (eq po-entry-type 'fuzzy)
             (eq po-entry-type 'untranslated))
         (if (y-or-n-p (_"Should I really obsolete this entry? "))
             (progn
               (po-decrease-type-counter)
               (save-excursion
                 (save-restriction
                   (narrow-to-region po-start-of-entry po-end-of-entry)
                   (let ((buffer-read-only po-read-only))
                     (goto-char (point-min))
                     (skip-chars-forward "\n")
                     (while (not (eobp))
                       (insert "#~ ")
                       (search-forward "\n")))))
               (po-current-entry)
               (po-increase-type-counter)))
         (message ""))

        ((and (eq po-entry-type 'obsolete)
              (po-check-for-pending-edit po-start-of-msgid)
              (po-check-for-pending-edit po-start-of-msgstr-block))
         (po-decrease-type-counter)
         (po-update-mode-line-string)
         ;; TODO: Should save all msgstr forms here, not just one.
         (kill-new (po-get-msgstr-form))
         (let ((buffer-read-only po-read-only))
           (delete-region po-start-of-entry po-end-of-entry))
         (goto-char po-start-of-entry)
         (if (re-search-forward po-any-msgstr-block-regexp nil t)
             (goto-char (match-beginning 0))
           (re-search-backward po-any-msgstr-block-regexp nil t))
         (po-current-entry)
         (message ""))))
