Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New command line option --commands= for multiple commands #98

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 58 additions & 8 deletions man/lxterminal.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,7 @@
</refsynopsisdiv>
<refsect1> <title>DESCRIPTION</title>

<para>This manual page documents briefly the
<command>lxterminal</command> command.</para>
<para>This manual page documents the <command>lxterminal</command> command.</para>

<para><command>lxterminal</command> is a program that provides a terminal
emulator
Expand All @@ -45,34 +44,85 @@
<option>--command=<replaceable>STRING</replaceable></option>
<option>--command <replaceable>STRING</replaceable></option>

<option>--commands=<replaceable>[STRING[,STRING[...]]]</replaceable></option>
</term>
<listitem> <para>This option specifies the program (and its command line arguments) to be run in the terminal.
Except in the <option>--command=</option> form, this must be the last option on the command line.</para>
<listitem> <para>This option specifies the program (and its command line arguments) to be run in the terminal. Except in the <option>--command=</option>, or the <option>commands=</option>, forms, this must be the last option on the command line. Comma separated multiple commands create multiple tabs, each running a different comand, only with the <option>--commands=</option> form. New tabs will be created as necessary after all the explictly requested tabs will be exhausted.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>--geometry=<replaceable>CHARACTERS</replaceable>x<replaceable>LINES</replaceable></option>
</term>
<listitem> <para>Set the terminal's size in characters and lines.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>-h</option>
<option>--help</option>
</term>
<listitem> <para>Prints a short help message, and exit.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>-l</option>
<option>--loginshell</option>
</term>
<listitem> <para>Executes login shell.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>-t <replaceable>NAME[,NAME[,NAME[...]]]</replaceable></option>
<option>--title=<replaceable>NAME[,NAME[,NAME[...]]]</replaceable></option>
<option>--tabs=<replaceable>NAME[,NAME[,NAME[...]]]</replaceable></option>
<varlistentry> <term> <option>-T <replaceable>NAME</replaceable></option>
<option>-t <replaceable>NAME</replaceable></option>
<option>--title=<replaceable>NAME</replaceable></option>
<option>--tabs=<replaceable>[NAME[,NAME[...]]]</replaceable></option>
</term>
<listitem> <para>Set the terminal's title. Use comma for multiple tabs.</para>
<listitem> <para>Set the terminal's title. Comma separated multiple titles create multiple tabs only with the <option>--tabs=</option> form.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>--working-directory=<replaceable>DIRECTORY</replaceable></option>
</term>
<listitem> <para>Set the terminal's working directory.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>-v</option>
<option>--version</option>
</term>
<listitem> <para>Prints the program version, and exit.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

<refsect1> <title>EXAMPLES, AND MORE DETAILS</title>

<variablelist> <varlistentry> <term> <option>The hidden tab</option>
</term>
<listitem> <para>A hidden tab is created when <option>--tabs</option>, or any <option>--command</option> variant, is requested, with no more than one command, or no more than one tab, is specified. For example,</para>
<para><command>lxterminal</command> --tabs= --no-remote</para>
<para>This hidden tab can be revealed when the <command>lxterminal</command> is running. For example, when requesting a new tab with the Shift+Ctrl+T combination keys.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>The command examples</option> </term><listitem>
<para>For display puposes, the command examples might be broken into more than one line. When actually using the examples, commands should be entered in a single line. Or have line breaks according to the usual shell conventions.</para>
<para>All command examples here use <option>--no-remote</option> to avoid interaction with possibly running <command>lxterminal</command>s.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>A short lived process is requested in the command line</option> </term>
<listitem> <para>Consider</para>
<para><command>lxterminal</command> --no-remote --command=&apos;/bin/bash -c &quot;echo This window will be closed after you will press enter. ; read -p \&quot;Do press the &lt;enter&gt; key.\&quot; &quot; &apos;</para>
<para>This example emphasizes a limitaion of running commands from the <command>lxterminal</command> command line. As a result, the feature to run a command from the <command>lxterminal</command> command line is much more useful for processes that exit by the user explicit request. Such as an interactive shell. A short lived process does work. Only that it is fast enough to make the window, or the tab, closed before the user can pay attention. For a short lived process, such as</para>
<para><command>lxterminal</command> --no-remote --command=ls</para><para>
, the user must have indirect means to inspect the outcome. Such as redirecting the output to a file.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>Multiple occurences of the same option</option> </term>
<listitem>
<para><command>lxterminal</command> --no-remote --tabs=&apos;1st tab&apos;,&quot;2nd tab&quot; --tabs= --no-remote</para>
<para>In contrast to many other applications, repeating a command line <option>option</option> is not an error. With some <option>option</option>s, the last repetition override previous ocuurences. But there are exceptions. A <option>--command=</option> will always be executed in the 1st tab. Before, and in addition to, any <option>--commands=</option>. Just try out if it is a concern for you. The behaviour is deterministic.</para>
</listitem>
</varlistentry>
<varlistentry> <term> <option>Comma is a separator for --tabs= and --commands=</option> </term>
<listitem>
<para>Comma characters, &apos;,&apos;, are used as separators for the <option>--commands=</option>, and <option>--tabs=</option> options.</para>
<para><command>lxterminal</command> --tabs=&quot;midnight commander,2nd tab&quot; --commands=&apos;mc, &apos; --no-remote</para>
<para>There is no way to insert a comma character in the titles to be displayed by <option>--tabs=</option>. Or in the programs, and their arguments, to be run by <option>--commands=</option>. However, the single <option>--command=</option>, and its other variants, can accept a comma character as part of the program, and its arguments, to be executed.</para>
</listitem>
</varlistentry>
</variablelist>
</refsect1>

Expand Down
143 changes: 92 additions & 51 deletions src/lxterminal.c
Original file line number Diff line number Diff line change
Expand Up @@ -97,13 +97,14 @@ static gboolean terminal_vte_button_press_event(VteTerminal * vte, GdkEventButto
static void terminal_settings_apply_to_term(LXTerminal * terminal, Term * term);
static Term * terminal_new(LXTerminal * terminal, const gchar * label, const gchar * pwd, gchar * * env, gchar * * exec);
static void terminal_set_geometry_hints(Term * term, GdkGeometry * geometry);
static void terminal_new_tab(LXTerminal * terminal, const gchar * label);
static void terminal_new_tab(LXTerminal * terminal, const gchar * label, const gchar * cmd);
static void terminal_free(Term * term);
static void terminal_menubar_initialize(LXTerminal * terminal);
static void terminal_menu_accelerator_update(LXTerminal * terminal);
static void terminal_settings_apply(LXTerminal * terminal);
static void terminal_update_menu_shortcuts(Setting * setting);
static void terminal_initialize_menu_shortcuts(Setting * setting);
static void terminal_process_requested_command(gchar * * * const command, const gint cmd_len, const gboolean login_shell);

/* Menu accelerator saved when the user disables it. */
static char * saved_menu_accelerator = NULL;
Expand Down Expand Up @@ -293,7 +294,8 @@ static void terminal_initialize_switch_tab_accelerator(Term * term)
{
/* Formulate the accelerator name. */
char switch_tab_accel[1 + 3 + 1 + 1 + 1]; /* "<ALT>n" */
sprintf(switch_tab_accel, "<ALT>%d", term->index + 1);
/* Casting to unsigned, and %10, to shut off a compilation warning. */
sprintf(switch_tab_accel, "<ALT>%d", (unsigned)(term->index + 1)%10);

/* Parse the accelerator name. */
guint key;
Expand Down Expand Up @@ -373,7 +375,7 @@ static void terminal_new_window_activate_event(GtkAction * action, LXTerminal *
* Open a new tab. */
static void terminal_new_tab_activate_event(GtkAction * action, LXTerminal * terminal)
{
terminal_new_tab(terminal, NULL);
terminal_new_tab(terminal, NULL, NULL);
}

static void terminal_set_geometry_hints(Term *term, GdkGeometry *geometry)
Expand Down Expand Up @@ -410,7 +412,7 @@ static void terminal_save_size(LXTerminal * terminal)
terminal->row = vte_terminal_get_row_count(VTE_TERMINAL(term->vte));
}

static void terminal_new_tab(LXTerminal * terminal, const gchar * label)
static void terminal_new_tab(LXTerminal * terminal, const gchar * label, const gchar * cmd)
{
Term * term;
gchar * proc_cwd = terminal_get_current_dir(terminal);
Expand All @@ -419,21 +421,12 @@ static void terminal_new_tab(LXTerminal * terminal, const gchar * label)
* If the working directory was determined above, use it; otherwise default to the working directory of the process.
* Create the new terminal. */

if (terminal->login_shell)
{
/* Create a login shell, this should be cleaner. */
gchar * * exec = g_malloc(3 * sizeof(gchar *));
exec[0] = g_strdup(terminal_get_preferred_shell());
char * shellname = g_path_get_basename(exec[0]);
exec[1] = g_strdup_printf("-%s", shellname);
g_free(shellname);
exec[2] = NULL;
term = terminal_new(terminal, label, proc_cwd, NULL, exec);
}
else
{
term = terminal_new(terminal, label, proc_cwd, NULL, NULL);
}
gint cmd_len = 0;
gchar * * exec = NULL;
if (cmd != NULL)
g_shell_parse_argv(cmd, &cmd_len, &exec, NULL);
terminal_process_requested_command(&exec, cmd_len, terminal->login_shell);
term = terminal_new(terminal, label, proc_cwd, NULL, exec);
g_free(proc_cwd);

/* Add a tab to the notebook and the "terms" array. */
Expand Down Expand Up @@ -1315,6 +1308,7 @@ static Term * terminal_new(LXTerminal * terminal, const gchar * label, const gch
exec[1] = g_path_get_basename(exec[0]);
exec[2] = NULL;
}
term->command = exec;

#if VTE_CHECK_VERSION (0, 38, 0)
vte_terminal_spawn_sync(
Expand Down Expand Up @@ -1342,7 +1336,6 @@ static Term * terminal_new(LXTerminal * terminal, const gchar * label, const gch
&term->pid,
NULL);
#endif
g_strfreev(exec);

/* Connect signals. */
g_signal_connect(G_OBJECT(term->tab), "button-press-event", G_CALLBACK(terminal_tab_button_press_event), term);
Expand All @@ -1365,6 +1358,7 @@ static Term * terminal_new(LXTerminal * terminal, const gchar * label, const gch
/* Deallocate a Term structure. */
static void terminal_free(Term * term)
{
g_strfreev(term->command);
g_free(term->matched_url);
if ((GTK_IS_ACCEL_GROUP(term->parent->accel_group)) && (term->closure != NULL))
{
Expand Down Expand Up @@ -1439,8 +1433,14 @@ gboolean lxterminal_process_arguments(gint argc, gchar * * argv, CommandArgument
argv_cursor ++;
char * argument = *argv_cursor;

/* --commands=<commands> */
if (strncmp(argument, "--commands=", 11) == 0)
{
arguments->commands = &argument[11];
}

/* --command=<command> */
if (strncmp(argument, "--command=", 10) == 0)
else if (strncmp(argument, "--command=", 10) == 0)
{
g_strfreev(arguments->command);
g_shell_parse_argv(&argument[10], &cmd_len, &arguments->command, NULL);
Expand All @@ -1450,7 +1450,7 @@ gboolean lxterminal_process_arguments(gint argc, gchar * * argv, CommandArgument
* The <rest of arguments> behavior is demanded by distros who insist on this xterm feature. */
else if ((strcmp(argument, "--command") == 0) || (strcmp(argument, "-e") == 0))
{
if(arguments->command != NULL) g_strfreev(arguments->command);
g_strfreev(arguments->command);
cmd_len = 0;
arguments->command = g_malloc(argc * sizeof(gchar *));

Expand Down Expand Up @@ -1521,7 +1521,7 @@ gboolean lxterminal_process_arguments(gint argc, gchar * * argv, CommandArgument
arguments->working_directory = &argument[20];
}

/* --no-remote: Do not accept or send remote commands */
/* --no-remote: Do not accept or send remote commands */
else if (strcmp(argument, "--no-remote") == 0) {
arguments->no_remote = TRUE;
}
Expand All @@ -1536,55 +1536,62 @@ gboolean lxterminal_process_arguments(gint argc, gchar * * argv, CommandArgument
else {
printf("%s\n", usage_display);
return FALSE;
}
}
}
terminal_process_requested_command(&arguments->command, cmd_len, arguments->login_shell);
return TRUE;
}

/* Furthere process a command from the argument vector before executing it. */
static void terminal_process_requested_command(gchar * * * const command, const gint cmd_len, const gboolean login_shell)
{
gboolean force_login_shell = FALSE;
/* Handle --loginshell. */
if (arguments->command != NULL && cmd_len <= 2) {
if (*command != NULL && cmd_len <= 2) {
/* Force using login shell if it has only 1 command, and command is not
* in PATH. */
gchar * program_path = g_find_program_in_path(arguments->command[0]);
gchar * program_path = g_find_program_in_path((*command)[0]);
if (program_path == NULL) {
arguments->login_shell = TRUE;
force_login_shell = TRUE;
}
g_free(program_path);
}
if (arguments->login_shell == TRUE)
if (login_shell == TRUE || force_login_shell == TRUE)
{
const gchar * shell = terminal_get_preferred_shell();
gchar * shellname = g_path_get_basename(shell);
if (arguments->command == NULL)
if (*command == NULL)
{
arguments->command = g_malloc(3 * sizeof(gchar *));
arguments->command[0] = g_strdup(shell);
arguments->command[1] = g_strdup_printf("-%s", shellname);
arguments->command[2] = NULL;
*command = g_malloc(3 * sizeof(gchar *));
(*command)[0] = g_strdup(shell);
(*command)[1] = g_strdup_printf("-%s", shellname);
(*command)[2] = NULL;
}
else
{
gchar * * tmp = g_malloc((cmd_len + 4) * sizeof(gchar *));
tmp[0] = g_strdup(shell);
tmp[1] = g_strdup_printf("-%s", shellname);
tmp[2] = g_strdup("-c");
memcpy((tmp + 3), arguments->command, cmd_len * sizeof(gchar *));
memcpy((tmp + 3), *command, cmd_len * sizeof(gchar *));
tmp[cmd_len + 3] = NULL;
g_free(arguments->command);
arguments->command = tmp;
g_free(*command);
*command = tmp;
}
g_free(shellname);
}
else
{
if(arguments->command != NULL)
if (*command != NULL)
{
gchar * * tmp = g_malloc((cmd_len + 2) * sizeof(gchar *));
tmp[0] = g_strdup(arguments->command[0]);
memcpy((tmp + 1), arguments->command, cmd_len * sizeof(gchar *));
tmp[0] = g_strdup((*command)[0]);
memcpy((tmp + 1), *command, cmd_len * sizeof(gchar *));
tmp[cmd_len + 1] = NULL;
g_free(arguments->command);
arguments->command = tmp;
g_free(*command);
*command = tmp;
}
}
return TRUE;
}

/* Initialize a new LXTerminal.
Expand Down Expand Up @@ -1670,12 +1677,28 @@ LXTerminal * lxterminal_initialize(LXTermWindow * lxtermwin, CommandArguments *
{
local_working_directory = g_get_current_dir();
}
gchar * first_comma = NULL;;
gboolean used_prefix_of_commands = FALSE;
if (arguments->command == NULL && arguments->commands != NULL)
{
used_prefix_of_commands = TRUE;
first_comma = strchr(arguments->commands, ',');
if (first_comma != NULL)
{
*first_comma = '\0';
}
gint cmd_len = 0;
g_shell_parse_argv(arguments->commands, &cmd_len, &arguments->command, NULL);
terminal_process_requested_command(&arguments->command, cmd_len, arguments->login_shell);
}
Term * term = terminal_new(
terminal,
((arguments->title != NULL) ? arguments->title : NULL),
((arguments->working_directory != NULL) ? arguments->working_directory : local_working_directory),
NULL,
arguments->command);
if (first_comma != NULL)
*first_comma = ',';
g_free(local_working_directory);

/* Set window title. */
Expand Down Expand Up @@ -1761,18 +1784,36 @@ LXTerminal * lxterminal_initialize(LXTermWindow * lxtermwin, CommandArguments *
/* Update terminal settings. */
terminal_settings_apply(terminal);

if (arguments->tabs != NULL && arguments->tabs[0] != '\0')
if (arguments->tabs != NULL && arguments->tabs[0] != '\0' ||
arguments->commands != NULL && arguments->commands[0] != '\0')
{
/* use token to destructively slice tabs to different tab names */
char * token = strtok(arguments->tabs, ",");
term->user_specified_label = TRUE;
gtk_label_set_text(GTK_LABEL(term->label), token);
token = strtok(NULL, ",");
gchar * cmd = NULL, * cmd_saveptr, * tab = NULL, * tab_saveptr;
if (arguments->commands != NULL && arguments->commands[0] != '\0')
{
/* use cmd to destructively slice commands tab names */
cmd = strtok_r(arguments->commands, ",", &cmd_saveptr);
if (used_prefix_of_commands == TRUE)
/* Initially, single hidden tab is running a command from --commands */
cmd = strtok_r(NULL, ",", &cmd_saveptr);
}
if (arguments->tabs != NULL && arguments->tabs[0] != '\0')
{
/* use tab to destructively slice tabs to different tab names */
tab = strtok_r(arguments->tabs, ",", &tab_saveptr);
term->user_specified_label = TRUE;
gtk_label_set_text(GTK_LABEL(term->label), tab);
/* Initially, a single tab is hidden */
tab = strtok_r(NULL, ",", &tab_saveptr);
}

while (token != NULL && token[0] != '\0')
while (cmd != NULL && cmd[0] != '\0' ||
tab != NULL && tab[0] != '\0')
{
terminal_new_tab(terminal, token);
token = strtok(NULL, ",");
terminal_new_tab(terminal, tab, cmd);
if (cmd != NULL && cmd[0] != '\0')
cmd = strtok_r(NULL, ",", &cmd_saveptr);
if (tab != NULL && tab[0] != '\0')
tab = strtok_r(NULL, ",", &tab_saveptr);
}
}

Expand Down
Loading