Function: command-line

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

Signature

(command-line)

Documentation

A subroutine of normal-top-level.

Amongst another things, it parses the command-line arguments.

Probably introduced at or before Emacs version 19.20.

Source Code

;; Defined in /usr/src/emacs/lisp/startup.el.gz
(defun command-line ()
  "A subroutine of `normal-top-level'.
Amongst another things, it parses the command-line arguments."
 (let (xdg-dir startup-init-directory)
  (setq before-init-time (current-time)
	after-init-time nil
        command-line-default-directory default-directory)

  ;; Force recomputation, in case it was computed during the dump.
  (setq abbreviated-home-dir nil)

  ;; See if we should import version-control from the environment variable.
  (let ((vc (getenv "VERSION_CONTROL")))
    (cond ((eq vc nil))			;don't do anything if not set
	  ((member vc '("t" "numbered"))
	   (setq version-control t))
	  ((member vc '("nil" "existing"))
	   (setq version-control nil))
	  ((member vc '("never" "simple"))
	   (setq version-control 'never))))

  ;;! This has been commented out; I currently find the behavior when
  ;;! split-window-keep-point is nil disturbing, but if I can get used
  ;;! to it, then it would be better to eliminate the option.
  ;;! ;; Choose a good default value for split-window-keep-point.
  ;;! (setq split-window-keep-point (> baud-rate 2400))

  ;; Convert preloaded file names in load-history to absolute.
  (let ((simple-file-name
	 ;; Look for simple.el or simple.elc and use their directory
	 ;; as the place where all Lisp files live.
	 (locate-file "simple" load-path (get-load-suffixes)))
	lisp-dir)
    ;; Don't abort if simple.el cannot be found, but print a warning.
    ;; Although in most usage we are going to cryptically abort a moment
    ;; later anyway, due to missing required bidi data files (eg bug#13430).
    (if (null simple-file-name)
	(let ((standard-output 'external-debugging-output)
	      (lispdir (expand-file-name "../lisp" data-directory)))
	  (princ "Warning: Could not find simple.el or simple.elc")
	  (terpri)
	  (when (getenv "EMACSLOADPATH")
	    (princ "The EMACSLOADPATH environment variable is set, \
please check its value")
	    (terpri))
	  (unless (file-readable-p lispdir)
	    (princ (format "Lisp directory %s not readable?" lispdir))
	    (terpri)))
      (setq lisp-dir (file-truename (file-name-directory simple-file-name)))
      (setq load-history
	    (mapcar (lambda (elt)
		      (if (and (stringp (car elt))
			       (not (file-name-absolute-p (car elt))))
			  (cons (concat lisp-dir
					(car elt))
				(cdr elt))
			elt))
		    load-history))))

  ;; Convert the arguments to Emacs internal representation.
  (let ((args command-line-args))
    (while args
      (setcar args
	      (decode-coding-string (car args) locale-coding-system t))
      (pop args)))

  (let ((done nil)
	(args (cdr command-line-args))
	display-arg)

    ;; Figure out which user's init file to load,
    ;; either from the environment or from the options.
    (setq init-file-user (if noninteractive nil (user-login-name)))
    ;; If user has not done su, use current $HOME to find .emacs.
    (and init-file-user
         (equal init-file-user (user-real-login-name))
	 (setq init-file-user ""))

    ;; Process the command-line args, and delete the arguments
    ;; processed.  This is consistent with the way main in emacs.c
    ;; does things.
    (while (and (not done) args)
      (let* ((longopts '(("--no-init-file") ("--no-site-file")
                         ("--no-x-resources") ("--debug-init")
                         ("--user") ("--iconic") ("--icon-type") ("--quick")
			 ("--no-blinking-cursor") ("--basic-display")
                         ("--dump-file") ("--temacs") ("--seccomp")))
             (argi (pop args))
             (orig-argi argi)
             argval)
	;; Handle --OPTION=VALUE format.
	(when (string-match "\\`\\(--[^=]*\\)=" argi)
	  (setq argval (substring argi (match-end 0))
                argi (match-string 1 argi)))
	(when (string-match "\\`--." orig-argi)
	  (let ((completion (try-completion argi longopts)))
	    (cond ((eq completion t)
		   (setq argi (substring argi 1)))
		  ((stringp completion)
		   (let ((elt (assoc completion longopts)))
		     (unless elt
		       (error "Option `%s' is ambiguous" argi))
		     (setq argi (substring (car elt) 1))))
		  (t
		   (setq argval nil
			 argi orig-argi)))))
	(cond
	 ;; The --display arg is handled partly in C, partly in Lisp.
	 ;; When it shows up here, we just put it back to be handled
	 ;; by `command-line-1'.
	 ((member argi '("-d" "-display"))
	  (setq display-arg (list argi (pop args))))
	 ((member argi '("-Q" "-quick"))
	  (setq init-file-user nil
		site-run-file nil
		inhibit-x-resources t)
	  ;; Stop it showing up in emacs -Q's customize-rogue.
	  (put 'site-run-file 'standard-value '(nil)))
         ((member argi '("-no-x-resources"))
          (setq inhibit-x-resources t))
	 ((member argi '("-D" "-basic-display"))
	  (setq no-blinking-cursor t
		emacs-basic-display t)
	  (push '(vertical-scroll-bars . nil) initial-frame-alist))
	 ((member argi '("-q" "-no-init-file"))
	  (setq init-file-user nil))
	 ((member argi '("-u" "-user"))
	  (setq init-file-user (or argval (pop args))
		argval nil))
	 ((equal argi "-no-site-file")
	  (setq site-run-file nil)
	  (put 'site-run-file 'standard-value '(nil)))
	 ((equal argi "-debug-init")
	  (setq init-file-debug t))
	 ((equal argi "-iconic")
	  (push '(visibility . icon) initial-frame-alist))
	 ((member argi '("-nbc" "-no-blinking-cursor"))
	  (setq no-blinking-cursor t))
         ((member argi '("-dump-file" "-temacs" "-seccomp"))
          ;; Handled in C
          (or argval (pop args))
          (setq argval nil))
	 ;; Push the popped arg back on the list of arguments.
	 (t
          (push argi args)
          (setq done t)))
	;; Was argval set but not used?
	(and argval
	     (error "Option `%s' doesn't allow an argument" argi))))

    ;; Re-attach the --display arg.
    (and display-arg (setq args (append display-arg args)))

    ;; Re-attach the program name to the front of the arg list.
    (and command-line-args
         (setcdr command-line-args args)))

  ;; Re-evaluate predefined variables whose initial value depends on
  ;; the runtime context.
  (when (listp custom-delayed-init-variables)
    (mapc #'custom-reevaluate-setting
          ;; Initialize them in the same order they were loaded, in
          ;; case there are dependencies between them.
          (reverse custom-delayed-init-variables)))
  (setq custom-delayed-init-variables t)

  ;; Warn for invalid user name.
  (when init-file-user
    (if (string-match "[~/:\n]" init-file-user)
        (display-warning 'initialization
                         (format "Invalid user name %s"
                                 init-file-user)
                         :error)
      (if (file-directory-p (expand-file-name
                             ;; We don't support ~USER on MS-Windows
                             ;; and MS-DOS except for the current
                             ;; user, and always load .emacs from
                             ;; the current user's home directory
                             ;; (see below).  So always check "~",
                             ;; even if invoked with "-u USER", or
                             ;; if $USER or $LOGNAME are set to
                             ;; something different.
                             (if (memq system-type '(windows-nt ms-dos))
                                 "~"
                               (concat "~" init-file-user))))
          nil
        (display-warning 'initialization
                         (format "User %s has no home directory"
                                 (if (equal init-file-user "")
                                     (user-real-login-name)
                                   init-file-user))
                         :error))))

  ;; Calculate the name of the Emacs init directory.
  ;; This is typically ~INIT-FILE-USER/.config/emacs unless the user
  ;; is following the ~INIT-FILE-USER/.emacs.d convention.
  (setq xdg-dir startup--xdg-config-home-emacs)
  (setq startup-init-directory
	(if (or (zerop (length init-file-user))
		(and (eq xdg-dir user-emacs-directory)
		     (not (eq xdg-dir startup--xdg-config-default))))
	    user-emacs-directory
	  ;; The name is not obvious, so access more directories to calculate it.
	  (setq xdg-dir (concat "~" init-file-user "/.config/emacs/"))
	  (startup--xdg-or-homedot xdg-dir init-file-user)))

  ;; Load the early init file, if found.
  (startup--load-user-init-file
   (lambda ()
     (expand-file-name
      ;; We use an explicit .el extension here to force
      ;; startup--load-user-init-file to set user-init-file to "early-init.el",
      ;; with the .el extension, if the file doesn't exist, not just
      ;; "early-init" without an extension, as it does for ".emacs".
      "early-init.el"
      startup-init-directory)))
  (setq early-init-file user-init-file)

  ;; If any package directory exists, initialize the package system.
  (and user-init-file
       package-enable-at-startup
       (not (bound-and-true-p package--activated))
       (catch 'package-dir-found
	 (let ((dirs (cons package-user-dir package-directory-list)))
	   (dolist (dir dirs)
	     (when (file-directory-p dir)
	       (dolist (subdir (directory-files dir))
		 (when (let ((subdir (expand-file-name subdir dir)))
                         (and (file-directory-p subdir)
                              (file-exists-p
                               (expand-file-name
                                (package--description-file subdir)
                                subdir))))
		   (throw 'package-dir-found t)))))))
       (package-activate-all))

  ;; Make sure window system's init file was loaded in loadup.el if
  ;; using a window system.
  ;; Initialize the window-system only after processing the command-line
  ;; args so that -Q can influence this initialization.
  (condition-case error
    (unless noninteractive
      (if (and initial-window-system
	       (not (featurep
		     (intern
		      (concat (symbol-name initial-window-system) "-win")))))
	  (error "Unsupported window system `%s'" initial-window-system))
      ;; Process window-system specific command line parameters.
      (setq command-line-args
            (let ((window-system initial-window-system)) ;Hack attack!
              (handle-args-function command-line-args)))
      ;; Initialize the window system. (Open connection, etc.)
      (let ((window-system initial-window-system)) ;Hack attack!
        (window-system-initialization))
      (put initial-window-system 'window-system-initialized t))
    ;; If there was an error, print the error message and exit.
    (error
     (princ
      (if (eq (car error) 'error)
	  (apply 'concat (cdr error))
	(if (memq 'file-error (get (car error) 'error-conditions))
	    (format "%s: %s"
                    (nth 1 error)
                    (mapconcat (lambda (obj) (prin1-to-string obj t))
                               (cdr (cdr error)) ", "))
	  (format "%s: %s"
                  (get (car error) 'error-message)
                  (mapconcat (lambda (obj) (prin1-to-string obj t))
                             (cdr error) ", "))))
      'external-debugging-output)
     (terpri 'external-debugging-output)
     (setq initial-window-system nil)
     (kill-emacs)))

  (run-hooks 'before-init-hook)

  ;; Under X, create the X frame and delete the terminal frame.
  (unless (daemonp)
    (if (or noninteractive emacs-basic-display)
	(setq menu-bar-mode nil
	      tab-bar-mode nil
	      tool-bar-mode nil))
    (frame-initialize))

  (when (fboundp 'x-create-frame)
    ;; Set up the tool-bar (even in tty frames, since Emacs might open a
    ;; graphical frame later).
    (unless noninteractive
      (tool-bar-setup)))

  (unless noninteractive
    (startup--setup-quote-display)
    (setq internal--text-quoting-flag t))

  (normal-erase-is-backspace-setup-frame)

  ;; Register default TTY colors for the case the terminal hasn't a
  ;; terminal init file.  We do this regardless of whether the terminal
  ;; supports colors or not and regardless the current display type,
  ;; since users can connect to color-capable terminals and also
  ;; switch color support on or off in mid-session by setting the
  ;; tty-color-mode frame parameter.
  ;; Exception: the `pc' ``window system'' has only 16 fixed colors,
  ;; and they are already set at this point by a suitable method of
  ;; window-system-initialization.
  (or (eq initial-window-system 'pc)
      (tty-register-default-colors))

  (let ((old-scalable-fonts-allowed scalable-fonts-allowed)
	(old-face-ignored-fonts face-ignored-fonts))

    ;; Run the site-start library if it exists.  The point of this file is
    ;; that it is run before .emacs.  There is no point in doing this after
    ;; .emacs; that is useless.
    ;; Note that user-init-file is nil at this point.  Code that might
    ;; be loaded from site-run-file and wants to test if -q was given
    ;; should check init-file-user instead, since that is already set.
    ;; See cus-edit.el for an example.
    (if site-run-file
        ;; Sites should not disable the startup screen.
        ;; Only individuals should disable the startup screen.
        (let ((inhibit-startup-screen inhibit-startup-screen))
	  (load site-run-file t t)))

    ;; Load that user's init file, or the default one, or none.
    (startup--load-user-init-file
     (lambda ()
       (cond
	((eq startup-init-directory xdg-dir) nil)
        ((eq system-type 'ms-dos)
         (concat "~" init-file-user "/_emacs"))
        ((not (eq system-type 'windows-nt))
         (concat "~" init-file-user "/.emacs"))
        ;; Else deal with the Windows situation.
        ((directory-files "~" nil "\\`\\.emacs\\(\\.elc?\\)?\\'")
         ;; Prefer .emacs on Windows.
         "~/.emacs")
        ((directory-files "~" nil "\\`_emacs\\(\\.elc?\\)?\\'")
         ;; Also support _emacs for compatibility, but warn about it.
         (push `(initialization
                 ,(format-message
                   "`_emacs' init file is deprecated, please use `.emacs'"))
               delayed-warnings-list)
         "~/_emacs")
        (t ;; But default to .emacs if _emacs does not exist.
         "~/.emacs")))
     (lambda ()
       (expand-file-name
        "init.el"
        startup-init-directory))
     t)

    (when (and deactivate-mark transient-mark-mode)
      (with-current-buffer (window-buffer)
        (deactivate-mark)))

    ;; If the user has a file of abbrevs, read it (unless -batch).
    (when (and (not noninteractive)
               (file-exists-p abbrev-file-name)
               (file-readable-p abbrev-file-name))
      (quietly-read-abbrev-file abbrev-file-name))

    ;; If the abbrevs came entirely from the init file or the
    ;; abbrevs file, they do not need saving.
    (setq abbrevs-changed nil)

    ;; Do this here in case the init file sets mail-host-address.
    (and mail-host-address
	 ;; Check that user-mail-address has not been set by hand.
	 ;; Yes, this is ugly, but slightly less so than leaving
	 ;; user-mail-address uninitialized during init file processing.
	 ;; Perhaps we should make :set-after do something like this?
	 ;; Ie, extend it to also mean (re)initialize-after.  See etc/TODO.
	 (equal user-mail-address
		(let (mail-host-address)
		  (ignore-errors
		    (custom--standard-value 'user-mail-address))))
	 (custom-reevaluate-setting 'user-mail-address))

    ;; If parameter have been changed in the init file which influence
    ;; face realization, clear the face cache so that new faces will
    ;; be realized.
    (unless (and (eq scalable-fonts-allowed old-scalable-fonts-allowed)
		 (eq face-ignored-fonts old-face-ignored-fonts))
      (clear-face-cache)))

  (setq after-init-time (current-time))
  ;; Display any accumulated warnings after all functions in
  ;; `after-init-hook' like `desktop-read' have finalized possible
  ;; changes in the window configuration.
  (run-hooks 'after-init-hook 'delayed-warnings-hook)

  ;; If *scratch* exists and init file didn't change its mode, initialize it.
  (if (get-buffer "*scratch*")
      (with-current-buffer "*scratch*"
	(if (eq major-mode 'fundamental-mode)
	    (funcall initial-major-mode))))

  ;; Load library for our terminal type.
  ;; User init file can set term-file-prefix to nil to prevent this.
  (unless (or noninteractive
              initial-window-system
              (daemonp))
    (tty-run-terminal-initialization (selected-frame) nil t))

  ;; Update the out-of-memory error message based on user's key bindings
  ;; for save-some-buffers.
  (setq memory-signal-data
	(list 'error
	      (substitute-command-keys "Memory exhausted--use \\[save-some-buffers] then exit and restart Emacs")))

  ;; Process the remaining args.
  (command-line-1 (cdr command-line-args))

  ;; This is a problem because, e.g. if emacs.d/gnus.el exists,
  ;; trying to load gnus could load the wrong file.
  ;; OK, it would not matter if .emacs.d were at the end of load-path.
  ;; but for the sake of simplicity, we discourage it full-stop.
  ;; Ref eg https://lists.gnu.org/r/emacs-devel/2012-03/msg00056.html
  ;;
  ;; A bad element could come from user-emacs-file, the command line,
  ;; or EMACSLOADPATH, so we basically always have to check.
  (let (warned)
    (dolist (dir load-path)
      (and (not warned)
	   (stringp dir)
	   (string-equal (file-name-as-directory (expand-file-name dir))
			 (expand-file-name user-emacs-directory))
	   (setq warned t)
	   (display-warning 'initialization
			    (format-message "\
Your `load-path' seems to contain\n\
your `.emacs.d' directory: %s\n\
This is likely to cause problems...\n\
Consider using a subdirectory instead, e.g.: %s"
                                    dir (expand-file-name
                                         "lisp" user-emacs-directory))
                            :warning))))

  ;; If -batch, terminate after processing the command options.
  (if noninteractive (kill-emacs t))

  ;; In daemon mode, start the server to allow clients to connect.
  ;; This is done after loading the user's init file and after
  ;; processing all command line arguments to allow e.g. `server-name'
  ;; to be changed before the server starts.
  (let ((dn (daemonp)))
    (when dn
      (when (stringp dn) (setq server-name dn))
      (server-start)
      (if server-process
	  (daemon-initialized)
	(if (stringp dn)
	    (message
	     "Unable to start daemon: Emacs server named %S already running"
	     server-name)
	  (message "Unable to start the daemon.\nAnother instance of Emacs is running the server, either as daemon or interactively.\nYou can use emacsclient to connect to that Emacs process."))
	(kill-emacs 1))))

  ;; Run emacs-session-restore (session management) if started by
  ;; the session manager and we have a session manager connection.
  (if (and (boundp 'x-session-previous-id)
           (stringp x-session-previous-id))
      (with-no-warnings
	(emacs-session-restore x-session-previous-id)))))