Function: command-line-1

command-line-1 is a byte-compiled function defined in startup.el.gz.

Signature

(command-line-1 ARGS-LEFT)

Documentation

A subroutine of command-line.

Source Code

;; Defined in /usr/src/emacs/lisp/startup.el.gz
(defun command-line-1 (args-left)
  "A subroutine of `command-line'."
  (display-startup-echo-area-message)

  ;; `displayable-buffers' is a list of buffers that may be displayed,
  ;; which includes files parsed from the command line arguments and
  ;; `initial-buffer-choice'.  All of the display logic happens at the
  ;; end of this `let'.  As files as processed from the command line
  ;; arguments, their buffers are prepended to `displayable-buffers'.
  ;; In order for options like "--eval" to work with the "--file" arg,
  ;; the file buffers are set as the current buffer as they are seen
  ;; on the command line (so "emacs --batch --file a --file b
  ;; --eval='(message "%s" (buffer-name))'" will print "b"), but this
  ;; does not affect the final displayed state of the buffers.
  (let ((displayable-buffers nil))
    ;; This `let' processes the command line arguments.
    (let ((command-line-args-left args-left))
      (when command-line-args-left
        ;; We have command args; process them.
        (let* ((dir command-line-default-directory)
               tem
               ;; This approach loses for "-batch -L DIR --eval "(require foo)",
               ;; if foo is intended to be found in DIR.
               ;;
               ;; The directories listed in --directory/-L options will *appear*
               ;; at the front of `load-path' in the order they appear on the
               ;; command-line.  We cannot do this by *placing* them at the front
               ;; in the order they appear, so we need this variable to hold them,
               ;; temporarily.
               ;;
               ;; To DTRT we keep track of the splice point and modify `load-path'
               ;; straight away upon any --directory/-L option.
               splice
               just-files ;; t if this follows the magic -- option.
               ;; This includes our standard options' long versions
               ;; and long versions of what's on command-switch-alist.
               (longopts
                (append '("--funcall" "--load" "--insert" "--kill"
                          "--dump-file" "--seccomp"
                          "--directory" "--eval" "--execute" "--no-splash"
                          "--find-file" "--visit" "--file" "--no-desktop")
                        (mapcar (lambda (elt) (concat "-" (car elt)))
                                command-switch-alist)))
               (line 0)
               (column 0)
               ;; `process-file-arg' opens a file buffer for `name',
               ;; sets that buffer as the current buffer without
               ;; displaying it, adds the buffer to
               ;; `displayable-buffers', and puts the point at
               ;; `line':`column'.  `line' and `column' are both reset
               ;; to zero when `process-file-arg' returns.
               (process-file-arg
                (lambda (name)
		  ;; This can only happen if PWD is deleted.
		  (if (not (or dir (file-name-absolute-p name)))
		      (message "Ignoring relative file name (%s) due to \
nil default-directory" name)
		    (let* ((file (expand-file-name
				  (command-line-normalize-file-name name)
				  dir))
			   (buf (find-file-noselect file)))
                      (file-name-history--add file)
		      (setq displayable-buffers (cons buf displayable-buffers))
                      ;; Set the file buffer to the current buffer so
                      ;; that it will be used with "--eval" and
                      ;; similar options.
                      (set-buffer buf)
                      ;; Put the point at `line':`column' in the file
                      ;; buffer, and reset `line' and `column' to 0.
                      (unless (zerop line)
                        (goto-char (point-min))
                        (forward-line (1- line)))
                      (setq line 0)
                      (unless (< column 1)
                        (move-to-column (1- column)))
                      (setq column 0))))))

          ;; Add the long X options to longopts.
          (dolist (tem command-line-x-option-alist)
            (if (string-match "^--" (car tem))
                (push (car tem) longopts)))

          ;; Add the long NS options to longopts.
          (dolist (tem command-line-ns-option-alist)
            (if (string-match "^--" (car tem))
                (push (list (car tem)) longopts)))

          ;; Loop, processing options.
          (while command-line-args-left
            (let* ((argi (car command-line-args-left))
                   (orig-argi argi)
                   argval completion)
              (setq command-line-args-left (cdr command-line-args-left))

              ;; Do preliminary decoding of the option.
              (if just-files
                  ;; After --, don't look for options; treat all args as files.
                  (setq argi "")
                ;; Convert long options to ordinary options
                ;; and separate out an attached option argument into argval.
                (when (string-match "\\`\\(--[^=]*\\)=" argi)
                  (setq argval (substring argi (match-end 0))
                        argi (match-string 1 argi)))
                (when (string-match "\\`--?[^-]" orig-argi)
                  (setq completion (try-completion argi longopts))
                  (if (eq completion t)
                      (setq argi (substring argi 1))
                    (if (stringp completion)
                        (let ((elt (member completion longopts)))
                          (or elt
                              (error "Option `%s' is ambiguous" argi))
                          (setq argi (substring (car elt) 1)))
                      (setq argval nil
                            argi orig-argi)))))

              ;; Execute the option.
              (cond ((setq tem (assoc argi command-switch-alist))
                     (if argval
                         (let ((command-line-args-left
                                (cons argval command-line-args-left)))
                           (funcall (cdr tem) argi))
                       (funcall (cdr tem) argi)))

                    ((equal argi "-no-splash")
                     (setq inhibit-startup-screen t))

                    ((member argi '("-f"	; what the manual claims
                                    "-funcall"
                                    "-e"))  ; what the source used to say
                     (setq inhibit-startup-screen t)
                     (setq tem (intern (or argval (pop command-line-args-left))))
                     (if (commandp tem)
                         (command-execute tem)
                       (funcall tem)))

                    ((member argi '("-eval" "-execute"))
                     (setq inhibit-startup-screen t)
                     (let* ((str-expr (or argval (pop command-line-args-left)))
                            (read-data (read-from-string str-expr))
                            (expr (car read-data))
                            (end (cdr read-data)))
                       ;; Allow same trailing chars as minibuf.c's
                       ;; `string_to_object'.
                       (unless (string-match-p "[\s\t\n]*\\'" str-expr end)
                         (error "Trailing garbage following expression: %s"
                                (substring str-expr end)))
                       (eval expr t)))

                    ((member argi '("-L" "-directory"))
                     ;; -L :/foo adds /foo to the _end_ of load-path.
                     (let (append)
                       (if (string-match-p
                            (format "\\`%s" path-separator)
                            (setq tem (or argval (pop command-line-args-left))))
                           (setq tem (substring tem 1)
                                 append t))
                       (setq tem (expand-file-name
                                  (command-line-normalize-file-name tem)))
                       (cond (append (setq load-path
                                           (append load-path (list tem)))
                                     (if splice (setq splice load-path)))
                             (splice (setcdr splice (cons tem (cdr splice)))
                                     (setq splice (cdr splice)))
                             (t (setq load-path (cons tem load-path)
                                      splice load-path)))))

                    ((member argi '("-l" "-load"))
                     (let* ((file (command-line-normalize-file-name
                                   (or argval (pop command-line-args-left))))
                            ;; Take file from default dir if it exists there;
                            ;; otherwise let `load' search for it.
                            (file-ex (file-truename (expand-file-name file))))
                       (when (file-regular-p file-ex)
                         (setq file file-ex))
                       (load file nil t)))

                    ;; This is used to handle -script.  It's not clear
                    ;; we need to document it (it is totally internal).
                    ((member argi '("-scriptload" "-scripteval"))
                     (let* ((file (command-line-normalize-file-name
                                   (or argval (pop command-line-args-left))))
                            ;; Take file from default dir.
                            (file-ex (expand-file-name file))
                            (truename (file-truename file-ex)))
                       ;; We want to use the truename here if we can,
                       ;; because that makes `eval-after-load' work
                       ;; more reliably.  But if the file is, for
                       ;; instance, /dev/stdin, the truename doesn't
                       ;; actually exist on some systems.
                       (when (file-exists-p truename)
                         (setq file-ex truename))
                       (if (equal argi "-scripteval")
                           ;; This will kill Emacs.
                           (command-line--eval-script file-ex)
                         (command-line--load-script file-ex))))

                    ((equal argi "-insert")
                     (setq inhibit-startup-screen t)
                     (setq tem (or argval (pop command-line-args-left)))
                     (or (stringp tem)
                         (error "File name omitted from `-insert' option"))
                     (insert-file-contents (command-line-normalize-file-name tem)))

                    ((or (equal argi "-dump-file")
                         (equal argi "-seccomp"))
                     ;; This was processed in C.
                     (or argval (pop command-line-args-left)))

                    ((equal argi "-kill")
                     (kill-emacs t))

                    ;; This is for when they use --no-desktop with -q, or
                    ;; don't load Desktop in their .emacs.  If desktop.el
                    ;; _is_ loaded, it will handle this switch, and we
                    ;; won't see it by the time we get here.
                    ((equal argi "-no-desktop")
                     (message "\"--no-desktop\" ignored because the Desktop package is not loaded"))

                    ((string-match "^\\+[0-9]+\\'" argi)
                     (setq line (string-to-number argi)))

                    ((string-match "^\\+\\([0-9]+\\):\\([0-9]+\\)\\'" argi)
                     (setq line (string-to-number (match-string 1 argi))
                           column (string-to-number (match-string 2 argi))))

                    ((setq tem (assoc orig-argi command-line-x-option-alist))
                     ;; Ignore X-windows options and their args if not using X.
                     (setq command-line-args-left
                           (nthcdr (nth 1 tem) command-line-args-left)))

                    ((setq tem (assoc orig-argi command-line-ns-option-alist))
                     ;; Ignore NS-windows options and their args if not using NS.
                     (setq command-line-args-left
                           (nthcdr (nth 1 tem) command-line-args-left)))

                    ((member argi '("-find-file" "-file" "-visit"))
                     (setq inhibit-startup-screen t)
                     ;; An explicit option to specify visiting a file.
                     (setq tem (or argval (pop command-line-args-left)))
                     (unless (stringp tem)
                       (error "File name omitted from `%s' option" argi))
                     (funcall process-file-arg tem))

                    ;; These command lines now have no effect.
                    ((string-match "\\`--?\\(no-\\)?\\(uni\\|multi\\)byte$" argi)
                     (display-warning 'initialization
                                      (format "Ignoring obsolete arg %s" argi)))

                    ((equal argi "--")
                     (setq just-files t))
                    (t
                     ;; We have almost exhausted our options. See if the
                     ;; user has made any other command-line options available
                     (let ((hooks command-line-functions)
                           (did-hook nil))
                       (while (and hooks
                                   (not (setq did-hook (funcall (car hooks)))))
                         (setq hooks (cdr hooks)))
                       (unless did-hook
                         ;; Presume that the argument is a file name.
                         (if (string-match "\\`-" argi)
                             (error "Unknown option `%s'" argi))
                         ;; FIXME: Why do we only inhibit the startup
                         ;; screen for -nw?
                         (unless initial-window-system
                           (setq inhibit-startup-screen t))
                         (funcall process-file-arg orig-argi)))))

              ;; In unusual circumstances, the execution of Lisp code due
              ;; to command-line options can cause the last visible frame
              ;; to be deleted.  In this case, kill emacs to avoid an
              ;; abort later.
              (unless (frame-live-p (selected-frame)) (kill-emacs nil)))))))

    (when (eq initial-buffer-choice t)
      ;; When `initial-buffer-choice' equals t make sure that *scratch*
      ;; exists.
      (get-scratch-buffer-create))

    ;; If *scratch* exists and is empty, insert initial-scratch-message.
    ;; Do this before switching to *scratch* below to handle bug#9605.
    (and initial-scratch-message
	 (get-buffer "*scratch*")
	 (with-current-buffer "*scratch*"
	   (when (zerop (buffer-size))
	     (insert (substitute-command-keys initial-scratch-message))
	     (set-buffer-modified-p nil))))

    ;; Prepend `initial-buffer-choice' to `displayable-buffers'. If
    ;; the buffer is already a member of that list then shift the
    ;; buffer to the head of the list. The shift behavior is intended
    ;; to prevent the same buffer being displayed in two windows when
    ;; an `initial-buffer-choice' function happens to return the head
    ;; of `displayable-buffers'.
    (when initial-buffer-choice
      (let ((buf
             (cond ((stringp initial-buffer-choice)
		    (find-file-noselect initial-buffer-choice))
		   ((functionp initial-buffer-choice)
		    (funcall initial-buffer-choice))
                   ((eq initial-buffer-choice t)
                    (get-scratch-buffer-create))
                   (t
                    (error "`initial-buffer-choice' must be a string, a function, or t")))))
        (unless (buffer-live-p buf)
          (error "Value returned by `initial-buffer-choice' is not a live buffer: %S" buf))
        (setq displayable-buffers (cons buf (delq buf displayable-buffers)))))

    ;; Display the first two buffers in `displayable-buffers'.  If
    ;; `initial-buffer-choice' is non-nil, its buffer will be the
    ;; first buffer in `displayable-buffers'.  The first buffer will
    ;; be focused.
    (let ((displayable-buffers-len (length displayable-buffers))
          ;; `nondisplayed-buffers-p' is true if there exist buffers
          ;; in `displayable-buffers' that were not displayed to the
          ;; user.
          (nondisplayed-buffers-p nil)
          (old-face-font-rescale-alist face-font-rescale-alist))
      (when (> displayable-buffers-len 0)
        (switch-to-buffer (car displayable-buffers)))
      (cond
       ;; Two buffers; display them both.
       ((= displayable-buffers-len 2)
        (switch-to-buffer-other-window (cadr displayable-buffers))
        ;; Focus on the first buffer.
        (other-window -1))
       ;; More than two buffers: Ensure that the buffer display order
       ;; reflects the order they were given on the command line.
       ;; (This will end up with a `next-buffer' order that's in
       ;; reverse order -- the final file is the focused one, and then
       ;; the rest are in `next-buffer' in descending order.
       ((> displayable-buffers-len 2)
        (let ((bufs (reverse (cdr displayable-buffers))))
          (switch-to-buffer-other-window (pop bufs))
          (dolist (buf bufs)
            (switch-to-buffer buf nil t))
          ;; Focus on the first buffer.
          (other-window -1))))
      (when (> displayable-buffers-len 2)
        (setq nondisplayed-buffers-p t))

      (if (or inhibit-startup-screen
              initial-buffer-choice
              noninteractive
              (daemonp)
              inhibit-x-resources)

          ;; Not displaying a startup screen.  Display *Buffer List* if
          ;; there exist buffers that were not displayed.
          (when (and nondisplayed-buffers-p
                     (not noninteractive)
                     (not inhibit-startup-buffer-menu))
            (list-buffers))

        ;; Display a startup screen, after some preparations.

        ;; If there are no switches to process, we might as well
        ;; run this hook now, and there may be some need to do it
        ;; before doing any output.
        (run-hooks 'emacs-startup-hook 'term-setup-hook)

        ;; See the commentary in `normal-top-level' for why we do
        ;; this.
	(when (and (display-multi-font-p)
                   (not (eq face-font-rescale-alist
                            old-face-font-rescale-alist))
                   (assoc (font-xlfd-name (face-attribute 'default :font))
                          face-font-rescale-alist #'string-match-p))
	  (set-face-attribute 'default nil :font (font-spec)))

        ;; It's important to notice the user settings before we
        ;; display the startup message; otherwise, the settings
        ;; won't take effect until the user gives the first
        ;; keystroke, and that's distracting.
        (when (fboundp 'frame-notice-user-settings)
          (frame-notice-user-settings))

        ;; If there are no switches to process, we might as well
        ;; run this hook now, and there may be some need to do it
        ;; before doing any output.
        (run-hooks 'window-setup-hook)

        (setq inhibit-startup-hooks t)

        ;; ;; Do this now to avoid an annoying delay if the user
        ;; ;; clicks the menu bar during the sit-for.
        ;; (when (display-popup-menus-p)
        ;;      (precompute-menubar-bindings))
        ;; (with-no-warnings
        ;;      (setq menubar-bindings-done t))

        (display-startup-screen (> displayable-buffers-len 0))))))