1 /*-
2  * Copyright (c) 2004-2008 os-cillation e.K.
3  *
4  * Written by Benedikt Meurer <benny@xfce.org>.
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 2 of the License, or (at your option)
9  * any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 
24 #ifdef HAVE_MEMORY_H
25 #include <memory.h>
26 #endif
27 #ifdef HAVE_STRING_H
28 #include <string.h>
29 #endif
30 #ifdef HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif
33 
34 #include <libxfce4util/libxfce4util.h>
35 
36 #include <terminal/terminal-options.h>
37 #include <terminal/terminal-private.h>
38 
39 
40 
41 /**
42  * terminal_option_cmp:
43  * @long_name     : long option text or %NULL
44  * @short_name    : short option character or 0
45  * @argv          : pointer to the argument vector
46  * @argc          : length of the argument vector
47  * @argv_offset   : current offset in the argument vector
48  * @return_string : return location of a pointer to the option
49  *                  arguments, or %NULL to make this function
50  *                  behave for string comparison.
51  *
52  * Return value: %TRUE if @long_name or @short_name was found.
53  **/
54 static gboolean
terminal_option_cmp(const gchar * long_name,gchar short_name,gint argc,gchar ** argv,gint * argv_offset,gchar ** return_string)55 terminal_option_cmp (const gchar   *long_name,
56                      gchar          short_name,
57                      gint           argc,
58                      gchar        **argv,
59                      gint          *argv_offset,
60                      gchar        **return_string)
61 {
62   gint   len, offset;
63   gchar *arg = argv[*argv_offset];
64 
65   if (long_name != NULL && *(arg + 1) == '-')
66     {
67       /* a boolean compare, check if the remaining string matches */
68       if (return_string == NULL)
69         return (strcmp (arg + 2, long_name) == 0);
70 
71       len = strlen (long_name);
72       if (strncmp (arg + 2, long_name, len) != 0)
73         return FALSE;
74 
75       offset = 2 + len;
76     }
77   else if (short_name != 0 && *(arg + 1) == short_name)
78     {
79       if (return_string == NULL)
80         return (*(arg + 2) == '\0');
81 
82       offset = 2;
83     }
84   else
85     {
86       return FALSE;
87     }
88 
89   terminal_assert (return_string != NULL);
90   if (*(arg + offset) == '=')
91     *return_string = arg + (offset + 1);
92   else if (*argv_offset + 1 > argc)
93     *return_string = NULL;
94   else
95     *return_string = argv[++*argv_offset];
96 
97   return TRUE;
98 }
99 
100 
101 
102 static gboolean
terminal_option_show_hide_cmp(const gchar * long_name,gint argc,gchar ** argv,gint * argv_offset,TerminalVisibility * return_visibility)103 terminal_option_show_hide_cmp (const gchar         *long_name,
104                                gint                 argc,
105                                gchar              **argv,
106                                gint                *argv_offset,
107                                TerminalVisibility  *return_visibility)
108 {
109   gchar *arg = argv[*argv_offset];
110   const size_t pref_len = strlen ("--show-");
111 
112   terminal_return_val_if_fail (long_name != NULL, FALSE);
113   terminal_return_val_if_fail (return_visibility != NULL, FALSE);
114 
115   if ((strncmp (arg, "--show-", pref_len) == 0 || strncmp (arg, "--hide-", pref_len) == 0)
116       && strcmp (arg + pref_len, long_name) == 0)
117     {
118       if (*(arg + 2) == 's')
119         *return_visibility = TERMINAL_VISIBILITY_SHOW;
120       else
121         *return_visibility = TERMINAL_VISIBILITY_HIDE;
122 
123       return TRUE;
124     }
125 
126   return FALSE;
127 }
128 
129 
130 
131 void
terminal_options_parse(gint argc,gchar ** argv,TerminalOptions * options)132 terminal_options_parse (gint              argc,
133                         gchar           **argv,
134                         TerminalOptions  *options)
135 {
136   gint n;
137 
138   for (n = 1; n < argc; ++n)
139     {
140       /* all arguments should atleast start with a dash */
141       if (argv[n] == NULL || *argv[n] != '-')
142         continue;
143 
144       /* everything after execute belongs to the command */
145       if (terminal_option_cmp ("execute", 'x', argc, argv, &n, NULL))
146         break;
147 
148       if (terminal_option_cmp ("help", 'h', argc, argv, &n, NULL))
149         options->show_help = 1;
150       else if (terminal_option_cmp ("version", 'V', argc, argv, &n, NULL))
151         options->show_version = 1;
152       else if (terminal_option_cmp ("disable-server", 0, argc, argv, &n, NULL))
153         options->disable_server = 1;
154       else if (terminal_option_cmp ("color-table", 0, argc, argv, &n, NULL))
155         options->show_colors = 1;
156       else if (terminal_option_cmp ("preferences", 0, argc, argv, &n, NULL))
157         options->show_preferences = 1;
158     }
159 }
160 
161 
162 
163 /**
164  * terminal_window_attr_parse:
165  * @argc            :
166  * @argv            :
167  * @error           :
168  *
169  * Return value: %NULL on failure.
170  **/
171 GSList *
terminal_window_attr_parse(gint argc,gchar ** argv,gboolean can_reuse_tab,GError ** error)172 terminal_window_attr_parse (gint              argc,
173                             gchar           **argv,
174                             gboolean          can_reuse_tab,
175                             GError          **error)
176 {
177   TerminalWindowAttr *win_attr;
178   TerminalTabAttr    *tab_attr;
179   gchar              *default_directory = NULL;
180   gchar              *default_display = NULL;
181   gchar              *s;
182   GSList             *tp, *wp;
183   GSList             *attrs;
184   gint                n;
185   gchar              *end_ptr = NULL;
186   TerminalVisibility  visible;
187 
188   win_attr = terminal_window_attr_new ();
189   tab_attr = win_attr->tabs->data;
190   attrs = g_slist_prepend (NULL, win_attr);
191 
192   for (n = 1; n < argc; ++n)
193     {
194       /* all arguments should atleast start with a dash */
195       if (argv[n] == NULL || *argv[n] != '-')
196         goto unknown_option;
197 
198       if (terminal_option_cmp ("default-display", 0, argc, argv, &n, &s))
199         {
200           if (G_UNLIKELY (s == NULL))
201             {
202               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
203                            _("Option \"--default-display\" requires specifying "
204                              "the default X display as its parameter"));
205               goto failed;
206             }
207           else
208             {
209               g_free (default_display);
210               default_display = g_strdup (s);
211               continue;
212             }
213         }
214       else if (terminal_option_cmp ("default-working-directory", 0, argc, argv, &n, &s))
215         {
216           if (G_UNLIKELY (s == NULL))
217             {
218               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
219                            _("Option \"--default-working-directory\" requires "
220                              "specifying the default working directory as its "
221                              "parameter"));
222               goto failed;
223             }
224           else
225             {
226               g_free (default_directory);
227               default_directory = g_strdup (s);
228               continue;
229             }
230         }
231       else if (terminal_option_cmp ("execute", 'x', argc, argv, &n, NULL))
232         {
233           if (++n >= argc)
234             {
235               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
236                            _("Option \"--execute/-x\" requires specifying the command "
237                              "to run on the rest of the command line"));
238               goto failed;
239             }
240           else
241             {
242               g_strfreev (tab_attr->command);
243               tab_attr->command = g_strdupv (argv + n);
244             }
245 
246           break;
247         }
248       else if (terminal_option_cmp ("command", 'e', argc, argv, &n, &s))
249         {
250           if (G_UNLIKELY (s == NULL))
251             {
252               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
253                            _("Option \"--command/-e\" requires specifying "
254                              "the command to run as its parameter"));
255               goto failed;
256             }
257           else
258             {
259               g_strfreev (tab_attr->command);
260               tab_attr->command = NULL;
261               if (!g_shell_parse_argv (s, NULL, &tab_attr->command, error))
262                 goto failed;
263             }
264         }
265       else if (terminal_option_cmp ("working-directory", 0, argc, argv, &n, &s))
266         {
267           if (G_UNLIKELY (s == NULL))
268             {
269               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
270                            _("Option \"--working-directory\" requires specifying "
271                              "the working directory as its parameter"));
272               goto failed;
273             }
274           else
275             {
276               g_free (tab_attr->directory);
277               tab_attr->directory = g_strdup (s);
278             }
279         }
280       else if (terminal_option_cmp ("title", 'T', argc, argv, &n, &s))
281         {
282           if (G_UNLIKELY (s == NULL))
283             {
284               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
285                            _("Option \"--title/-T\" requires specifying "
286                              "the title as its parameter"));
287               goto failed;
288             }
289           else
290             {
291               g_free (tab_attr->title);
292               tab_attr->title = g_strdup (s);
293             }
294         }
295       else if (terminal_option_cmp ("dynamic-title-mode", 0, argc, argv, &n, &s))
296         {
297           if (G_UNLIKELY (s == NULL))
298             {
299               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
300                            _("Option \"--dynamic-title-mode\" requires specifying "
301                              "the dynamic title mode as its parameter"));
302               goto failed;
303             }
304           else if (g_ascii_strcasecmp (s, "replace") == 0)
305             tab_attr->dynamic_title_mode = TERMINAL_TITLE_REPLACE;
306           else if (g_ascii_strcasecmp (s, "before") == 0)
307             tab_attr->dynamic_title_mode = TERMINAL_TITLE_PREPEND;
308           else if (g_ascii_strcasecmp (s, "after") == 0)
309             tab_attr->dynamic_title_mode = TERMINAL_TITLE_APPEND;
310           else if (g_ascii_strcasecmp (s, "none") == 0)
311             tab_attr->dynamic_title_mode = TERMINAL_TITLE_HIDE;
312           else
313             {
314               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
315                            _("Invalid argument for option \"--dynamic-title-mode\": %s"),
316                            s);
317               goto failed;
318             }
319         }
320       else if (terminal_option_cmp ("initial-title", 0, argc, argv, &n, &s))
321         {
322           if (G_UNLIKELY (s == NULL))
323             {
324               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
325                            _("Option \"--initial-title\" requires specifying "
326                              "the initial title as its parameter"));
327               goto failed;
328             }
329           else
330             {
331               g_free (tab_attr->initial_title);
332               tab_attr->initial_title = g_strdup (s);
333             }
334         }
335       else if (terminal_option_cmp ("hold", 'H', argc, argv, &n, NULL))
336         {
337           tab_attr->hold = TRUE;
338         }
339       else if (terminal_option_cmp ("active-tab", 0, argc, argv, &n, NULL))
340         {
341           tab_attr->active = TRUE;
342         }
343       else if (terminal_option_cmp ("color-text", 0, argc, argv, &n, &s))
344         {
345           GdkRGBA color;
346           if (G_UNLIKELY (s == NULL))
347             {
348               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
349                            _("Option \"%s\" requires specifying "
350                              "the color as its parameter"), "--color-text");
351               goto failed;
352             }
353           else if (!gdk_rgba_parse (&color, s))
354             {
355               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
356                            _("Unable to parse color: %s"), s);
357               goto failed;
358             }
359           g_free (tab_attr->color_text);
360           tab_attr->color_text = g_strdup (s);
361         }
362       else if (terminal_option_cmp ("color-bg", 0, argc, argv, &n, &s))
363         {
364           GdkRGBA color;
365           if (G_UNLIKELY (s == NULL))
366             {
367               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
368                            _("Option \"%s\" requires specifying "
369                              "the color as its parameter"), "--color-bg");
370               goto failed;
371             }
372           else if (!gdk_rgba_parse (&color, s))
373             {
374               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
375                            _("Unable to parse color: %s"), s);
376               goto failed;
377             }
378           g_free (tab_attr->color_bg);
379           tab_attr->color_bg = g_strdup (s);
380         }
381       else if (terminal_option_cmp ("display", 0, argc, argv, &n, &s))
382         {
383           if (G_UNLIKELY (s == NULL))
384             {
385               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
386                            _("Option \"--display\" requires specifying "
387                              "the X display as its parameter"));
388               goto failed;
389             }
390           else
391             {
392               g_free (win_attr->display);
393               win_attr->display = g_strdup (s);
394             }
395         }
396       else if (terminal_option_cmp ("geometry", 0, argc, argv, &n, &s))
397         {
398           if (G_UNLIKELY (s == NULL))
399             {
400               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
401                            _("Option \"--geometry\" requires specifying "
402                              "the window geometry as its parameter"));
403               goto failed;
404             }
405           else
406             {
407               g_free (win_attr->geometry);
408               win_attr->geometry = g_strdup (s);
409             }
410         }
411       else if (terminal_option_cmp ("role", 0, argc, argv, &n, &s))
412         {
413           if (G_UNLIKELY (s == NULL))
414             {
415               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
416                            _("Option \"--role\" requires specifying "
417                              "the window role as its parameter"));
418               goto failed;
419             }
420           else
421             {
422               g_free (win_attr->role);
423               win_attr->role = g_strdup (s);
424             }
425         }
426       else if (terminal_option_cmp ("sm-client-id", 0, argc, argv, &n, &s))
427         {
428           if (G_UNLIKELY (s == NULL))
429             {
430               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
431                            _("Option \"--sm-client-id\" requires specifying "
432                              "the unique session id as its parameter"));
433               goto failed;
434             }
435           else
436             {
437               g_free (win_attr->sm_client_id);
438               win_attr->sm_client_id = g_strdup (s);
439             }
440         }
441       else if (terminal_option_cmp ("startup-id", 0, argc, argv, &n, &s))
442         {
443           if (G_UNLIKELY (s == NULL))
444             {
445               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
446                            _("Option \"--startup-id\" requires specifying "
447                              "the startup id as its parameter"));
448               goto failed;
449             }
450           else
451             {
452               g_free (win_attr->startup_id);
453               win_attr->startup_id = g_strdup (s);
454               continue;
455             }
456         }
457       else if (terminal_option_cmp ("icon", 'I', argc, argv, &n, &s))
458         {
459           if (G_UNLIKELY (s == NULL))
460             {
461               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
462                            _("Option \"--icon/-I\" requires specifying "
463                              "an icon name or filename as its parameter"));
464               goto failed;
465             }
466           else
467             {
468               g_free (win_attr->icon);
469               win_attr->icon = g_strdup (s);
470             }
471         }
472       else if (terminal_option_cmp ("drop-down", 0, argc, argv, &n, NULL))
473         {
474           win_attr->drop_down = TRUE;
475         }
476       else if (terminal_option_show_hide_cmp ("menubar", argc, argv, &n, &visible))
477         {
478           win_attr->menubar = visible;
479         }
480       else if (terminal_option_cmp ("fullscreen", 0, argc, argv, &n, NULL))
481         {
482           win_attr->fullscreen = TRUE;
483         }
484       else if (terminal_option_cmp ("maximize", 0, argc, argv, &n, NULL))
485         {
486           win_attr->maximize = TRUE;
487         }
488       else if (terminal_option_cmp ("minimize", 0, argc, argv, &n, NULL))
489         {
490           win_attr->minimize = TRUE;
491         }
492       else if (terminal_option_show_hide_cmp ("borders", argc, argv, &n, &visible))
493         {
494           win_attr->borders = visible;
495         }
496       else if (terminal_option_show_hide_cmp ("toolbar", argc, argv, &n, &visible))
497         {
498           win_attr->toolbar = visible;
499         }
500       else if (terminal_option_show_hide_cmp ("scrollbar", argc, argv, &n, &visible))
501         {
502           win_attr->scrollbar = visible;
503         }
504       else if (terminal_option_cmp ("tab", 0, argc, argv, &n, NULL))
505         {
506           if (can_reuse_tab)
507             {
508               /* tab is the first user option, reuse existing window */
509               win_attr->reuse_last_window = TRUE;
510             }
511           else
512             {
513               /* add new tab */
514               tab_attr = terminal_tab_attr_new ();
515               win_attr->tabs = g_slist_append (win_attr->tabs, tab_attr);
516             }
517         }
518       else if (terminal_option_cmp ("window", 0, argc, argv, &n, NULL))
519         {
520           /* multiple windows, don't reuse */
521           win_attr->reuse_last_window = FALSE;
522 
523           /* setup new window */
524           win_attr = terminal_window_attr_new ();
525           tab_attr = win_attr->tabs->data;
526           attrs = g_slist_append (attrs, win_attr);
527         }
528       else if (terminal_option_cmp ("font", 0, argc, argv, &n, &s))
529         {
530           if (G_UNLIKELY (s == NULL))
531             {
532               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
533                            _("Option \"--font\" requires specifying "
534                              "the font name as its parameter"));
535               goto failed;
536             }
537           else
538             {
539               g_free (win_attr->font);
540               win_attr->font = g_strdup (s);
541               continue;
542             }
543         }
544       else if (terminal_option_cmp ("zoom", 0, argc, argv, &n, &s))
545         {
546           if (G_UNLIKELY (s == NULL) ||
547               strtol (s, &end_ptr, 0) < TERMINAL_ZOOM_LEVEL_MINIMUM ||
548               strtol (s, &end_ptr, 0) > TERMINAL_ZOOM_LEVEL_MAXIMUM)
549             {
550               g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
551                            _("Option \"--zoom\" requires specifying "
552                              "the zoom (%d .. %d) as its parameter"),
553                            TERMINAL_ZOOM_LEVEL_MINIMUM, TERMINAL_ZOOM_LEVEL_MAXIMUM);
554               goto failed;
555             }
556           else
557             {
558               win_attr->zoom = strtol (s, &end_ptr, 0);
559               continue;
560             }
561         }
562       else if (terminal_option_cmp ("disable-server", 0, argc, argv, &n, NULL)
563                || terminal_option_cmp ("sync", 0, argc, argv, &n, NULL)
564                || terminal_option_cmp ("g-fatal-warnings", 0, argc, argv, &n, NULL))
565         {
566           /* options we can ignore */
567           continue;
568         }
569       else
570         {
571 unknown_option:
572           g_set_error (error, G_SHELL_ERROR, G_SHELL_ERROR_FAILED,
573                        _("Unknown option \"%s\""), argv[n]);
574           goto failed;
575         }
576 
577       /* not the first option anymore */
578       can_reuse_tab = FALSE;
579     }
580 
581   /* substitute default working directory and default display if any */
582   if (default_display != NULL || default_directory != NULL)
583     {
584       for (wp = attrs; wp != NULL; wp = wp->next)
585         {
586           win_attr = wp->data;
587           for (tp = win_attr->tabs; tp != NULL; tp = tp->next)
588             {
589               tab_attr = tp->data;
590               if (tab_attr->directory == NULL && default_directory != NULL)
591                 tab_attr->directory = g_strdup (default_directory);
592             }
593 
594           if (win_attr->display == NULL && default_display != NULL)
595             win_attr->display = g_strdup (default_display);
596         }
597     }
598 
599   g_free (default_directory);
600   g_free (default_display);
601 
602   return attrs;
603 
604 failed:
605 
606   for (wp = attrs; wp != NULL; wp = wp->next)
607     terminal_window_attr_free (wp->data);
608   g_slist_free (attrs);
609 
610   g_free (default_directory);
611   g_free (default_display);
612 
613   return NULL;
614 }
615 
616 
617 
618 /**
619  **/
620 TerminalWindowAttr*
terminal_window_attr_new(void)621 terminal_window_attr_new (void)
622 {
623   TerminalWindowAttr *win_attr = g_slice_new0 (TerminalWindowAttr);
624 
625   win_attr->fullscreen = FALSE;
626   win_attr->menubar = TERMINAL_VISIBILITY_DEFAULT;
627   win_attr->borders = TERMINAL_VISIBILITY_DEFAULT;
628   win_attr->toolbar = TERMINAL_VISIBILITY_DEFAULT;
629   win_attr->scrollbar = TERMINAL_VISIBILITY_DEFAULT;
630   win_attr->zoom = TERMINAL_ZOOM_LEVEL_DEFAULT;
631   win_attr->tabs = g_slist_prepend (NULL, terminal_tab_attr_new ());
632 
633   return win_attr;
634 }
635 
636 
637 
638 /**
639  **/
640 TerminalTabAttr*
terminal_tab_attr_new(void)641 terminal_tab_attr_new (void)
642 {
643   TerminalTabAttr *tab_attr = g_slice_new0 (TerminalTabAttr);
644 
645   tab_attr->dynamic_title_mode = TERMINAL_TITLE_DEFAULT;
646   tab_attr->position = -1;
647 
648   return tab_attr;
649 }
650 
651 
652 
653 /**
654  * terminal_tab_attr_free:
655  * @attr  : A #TerminalTabAttr.
656  **/
657 void
terminal_tab_attr_free(TerminalTabAttr * attr)658 terminal_tab_attr_free (TerminalTabAttr *attr)
659 {
660   terminal_return_if_fail (attr != NULL);
661 
662   g_strfreev (attr->command);
663   g_free (attr->directory);
664   g_free (attr->title);
665   g_free (attr->initial_title);
666   g_free (attr->color_text);
667   g_free (attr->color_bg);
668   g_free (attr->color_title);
669   g_slice_free (TerminalTabAttr, attr);
670 }
671 
672 
673 
674 /**
675  * terminal_window_attr_free:
676  * @attr  : A #TerminalWindowAttr.
677  **/
678 void
terminal_window_attr_free(TerminalWindowAttr * attr)679 terminal_window_attr_free (TerminalWindowAttr *attr)
680 {
681   terminal_return_if_fail (attr != NULL);
682 
683   g_slist_free_full (attr->tabs, (GDestroyNotify) terminal_tab_attr_free);
684   g_free (attr->startup_id);
685   g_free (attr->sm_client_id);
686   g_free (attr->geometry);
687   g_free (attr->display);
688   g_free (attr->role);
689   g_free (attr->icon);
690   g_free (attr->font);
691   g_slice_free (TerminalWindowAttr, attr);
692 }
693