# System Interfaces

The `core.sys` package contains interfaces for interacting with the operating system. At this time, these interfaces are focused on launching other processes.

## Starting Processes

The following free functions are provided to ask the operating system to open files (and web addresses through `core.io.Url`s), as well as starting arbitrary programs.

- `void open(core.io.Url file)`

  Open an `Url` using the default program that is associated with the file in the operating system. Throws a `SystemError` on failure.

- `void execute(core.io.Url program, core.Array<core.Str> args)`

  Execute the program `program` with the parameters in `args`. If `program` is a relative url that consists of a single part, then the function searches for the program in the `PATH` environment variable.

  Equivalent to `Execute(program, params).detach()`.

The class `core.sys.Execute` provides more control over process execution. It has the following members:

- `core.sys.Execute workingDirectory(core.Maybe<core.io.Url> dir)`

  Set the working directory.

- `core.sys.Execute newTerminal()`

  Start the program in a new terminal window (implies a new process group).

  - On Windows: this is only done if the started program is a console application.
  - On Linux: this always launches a new terminal using `xdg-terminal-exec` if available.
- `core.sys.Execute newGroup()`

  Start the program in a new process group. Mostly relevant for terminal-based applications.

- `void detach()`

  Execute the program and detach it from the current process (i.e., we will not wait for it, nor interact with stdin/stdout).

Note that the members that set properties return the object itself. This is to make it possible to chain such calls into a single line. For example, to start a program in a new terminal window, one can write:

    Execute("emacs", ["~/.emacs"]).newTerminal().detach();

This design also allows extending the capabilities of the `core.sys.Execute` interface in the future without creating many combinations of the `execute` overload.

### Notes for Windows

On Windows, the `open` function calls the Win32 function `ShellExecute`, which takes file associations into account. This makes the `open` function roughly equivalent to typing the URL in the "Run" dialog.

The functionality provided by `Execute` is based around the Win32 function `CreateProcess`. While it is straight-forward to map most of the functionality to `CreateProcess`, there is one notable exception: namely that Windows treats all command-line arguments as a single string that is passed to the program. It is then up to each individual program to split the string into individual arguments. Storm constructs a string of the quoted arguments for you, using a format that corresponds to what the C runtime library understands. This should cover most use-cases. However, a notable exception from this convention is `cmd.exe`.

### Notes for Linux

The `open` function is implemented by calling the `xdg-open` program on Linux. This makes Storm respect the file associations that are set up by the desktop environment. However, `xdg-open` is not always installed, and it is not always possible to detect errors from `xdg-open` as it sometimes forks off the new process, and sometimes simply execs into it. Storm does its best to account for this and interprets the exit codes from `xdg-open`, but since it does not always terminate quickly Storm has to wait for a while before it is able to determine whether `xdg-open` succeeded or not.

The functionality provided by `Execute` generally maps well to `exec` and `fork` on Linux. One difficulty is that there is no well-established way to determine which terminal emulator to launch for terminal applications. The implementation currently has the following approach:

1. If the program `xdg-terminal-exec` is installed, it is used. While `xdg-terminal-exec` is new, it represents a standardized way in which programs can ask to start a new instance of the user's preferred terminal emulator.
2. Inspect the environment variable `STORM_TERM_COMMAND`. If it is set, Storm will append the program to start to the end of the command and execute it (after splitting `STORM_TERM_COMMAND` on spaces). For example, to launch `xterm`, one could set `export STORM_TERM_COMMAND="xterm -e"`.
3. Attempt to launch `xterm` using `xterm -e` followed by the command.
4. If everything else fails, throw an error.

Finally, it is worth noting that old versions of `xdg-terminal-exec` did not support any command line arguments. Since Storm uses `xdg-terminal-exec -- <command>` to avoid problems with commands that start with dashes, old versions of `xdg-terminal-exec` will launch a terminal that just complains that it is unable to launch the command `--`. To solve this problem, you need a newer version of `xdg-terminal-exec` (it is not possible to detect the old version programmatically as it does not accept any command line parameters such as `--version`).