Function: vc-cvs-parse-root

vc-cvs-parse-root is a byte-compiled function defined in vc-cvs.el.gz.

Signature

(vc-cvs-parse-root ROOT)

Documentation

Split CVS Root specification string into a list of fields.

Convert a CVS Root specification of the form

  [:METHOD:][[[USER][:PASSWORD]@]HOSTNAME][:[PORT]]/path/to/repository

to a normalized record with the following structure:

  (METHOD USER HOSTNAME FILENAME).

The default METHOD for a CVS root of the form /path/to/repository is "local". The default METHOD for a CVS root of the
form [USER@]HOSTNAME:/path/to/repository is "ext".

If METHOD is explicitly "local" or "fork", then the repository's file name starts immediately after the [:METHOD:] part. This must be used on MS-Windows platforms where absolute file names start with a drive letter.

Note that, except for METHOD, which is defaulted if not present, other optional parts will default to nil if not syntactically present, or to an empty string if present and delimited, but empty.

Return nil in case of an unparsable CVS Root (including the empty string), and issue a warning in that case.

This function doesn't check that an explicit method is valid, or that some fields which should not be empty for a given method, are empty or nil.

Source Code

;; Defined in /usr/src/emacs/lisp/vc/vc-cvs.el.gz
(cl-defun vc-cvs-parse-root (root)
  "Split CVS Root specification string into a list of fields.

Convert a CVS Root specification of the form

  [:METHOD:][[[USER][:PASSWORD]@]HOSTNAME][:[PORT]]/path/to/repository

to a normalized record with the following structure:

  \(METHOD USER HOSTNAME FILENAME).

The default METHOD for a CVS root of the form /path/to/repository
is \"local\".  The default METHOD for a CVS root of the
form  [USER@]HOSTNAME:/path/to/repository is \"ext\".

If METHOD is explicitly \"local\" or \"fork\", then the repository's
file name starts immediately after the [:METHOD:] part.  This must be
used on MS-Windows platforms where absolute file names start with a
drive letter.

Note that, except for METHOD, which is defaulted if not present,
other optional parts will default to nil if not syntactically
present, or to an empty string if present and delimited, but empty.

Return nil in case of an unparsable CVS Root (including the
empty string), and issue a warning in that case.

This function doesn't check that an explicit method is valid, or
that some fields which should not be empty for a given method,
are empty or nil."
  (let (method user password hostname port filename
               ;; IDX set by `next-delim' as a side-effect
               idx)
    (cl-labels
        ((invalid (reason &rest args)
           (apply #'lwarn '(vc-cvs) :warning
                  (concat "vc-cvs-parse-root: Can't parse '%s': " reason)
                  root args)
           (cl-return-from vc-cvs-parse-root))
         (no-filename ()
           (invalid "No repository file name"))
         (next-delim (start)
           ;; Search for a :, @ or /.  If none is found, there can be
           ;; no file name at the end, which is an error.
           (setq idx (string-match-p "[:@/]" root start))
           (if idx (aref root idx) (no-filename)))
         (grab-user (start end)
           (setq user (substring root start end)))
         (at-hostname-block (start)
           (let ((cand (next-delim start)))
             (cl-ecase cand
               (?:
                ;; Could be : before PORT and /path/to/repository, or
                ;; before PASSWORD.  We search for a @ to disambiguate.
                (let ((colon-idx idx)
                      (cand (next-delim (1+ idx))))
                  (cl-ecase cand
                    (?:
                     (invalid
                      (eval-when-compile
                        (concat "Hostname block: Superfluous : at %s "
                                "or missing @ before"))
                      idx))
                    (?@
                     ;; USER:PASSWORD case
                     (grab-user start colon-idx)
                     (delimited-password (1+ colon-idx) idx))
                    (?/
                     ;; HOSTNAME[:[PORT]] case
                     (grab-hostname start colon-idx)
                     (delimited-port (1+ colon-idx) idx)))))
               (?@
                (grab-user start idx)
                (at-hostname (1+ idx)))
               (?/
                (if (/= idx start)
                    (grab-hostname start idx))
                (at-filename idx)))))
         (delimited-password (start end)
           (setq password (substring root start end))
           (at-hostname (1+ end)))
         (grab-hostname (start end)
           (setq hostname (substring root start end)))
         (at-hostname (start)
           (let ((cand (next-delim start)))
             (cl-ecase cand
               (?:
                (grab-hostname start idx)
                (at-port (1+ idx)))
               (?@
                (invalid "Hostname: Unexpected @ after index %s" start))
               (?/
                (grab-hostname start idx)
                (at-filename idx)))))
         (delimited-port (start end)
           (setq port (substring root start end))
           (at-filename end))
         (at-port (start)
           (let ((end (string-match-p "/" root start)))
             (if end (delimited-port start end) (no-filename))))
         (at-filename (start)
           (setq filename (substring root start))))
      (when (string= root "")
        (invalid "Empty Root string"))
      ;; Check for a starting ":"
      (if (= (aref root 0) ?:)
          ;; 3 possible cases:
          ;; - :METHOD: at start.  METHOD doesn't have any @.
          ;; - :PASSWORD@ at start.  Must be followed by HOSTNAME.
          ;; - :[PORT] at start.  Must be followed immediately by a "/".
          ;; So, find the next character equal to ":", "@" or "/".
          (let ((cand (next-delim 1)))
            (cl-ecase cand
              (?:
               ;; :METHOD: case
               (setq method (substring root 1 idx))
               ;; Continue
               (if (member method '("local" "fork"))
                   (at-filename (1+ idx))
                 (at-hostname-block (1+ idx))))
              (?@
               ;; :PASSWORD@HOSTNAME case
               (delimited-password 1 idx))
              (?/
               ;; :[PORT] case.
               (at-port 1 idx))))
        ;; No starting ":", there can't be any METHOD.
        (at-hostname-block 0)))
    (unless method
      ;; Default the method if not specified
      (setq method
            (if (or user password hostname port) "ext" "local")))
    (list method user hostname filename)))