1 /* gEDA - GPL Electronic Design Automation
2 * gschem - gEDA Schematic Capture
3 * Copyright (C) 1998-2010 Ales Hvezda
4 * Copyright (C) 1998-2020 gEDA Contributors (see ChangeLog for details)
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 /*! \file x_controlfd.c
22 * \brief Remote-control gschem via a file descriptor.
24 * This feature allows project managers like Igor2's "genxproj" to
25 * issue certain actions in gschem. It is implemented as a plain text
26 * protocol and can be enabled via the command-line options
27 * `--control-fd=stdin' or `--control-fd=FD'.
29 * See \ref help_string for a list of commands.
36 #include <glib-unix.h>
38 static const char *help_string
=
39 "The following commands are supported:\n"
46 " patch-filename FILE PATCHFILE\n"
47 " import-patch FILE [PATCHFILE]\n"
51 "File paths must be absolute, except for patch filenames which are resolved\n"
52 "relative to the main file. Arguments are separated by spaces; spaces and\n"
53 "backslashes inside arguments must be escaped with a backslash.\n";
55 static int control_fd
= -1;
57 static FILE *stream
= NULL
;
59 static gboolean
can_read (gint fd
, GIOCondition condition
, gpointer user_data
);
63 x_controlfd_parsearg (char *optarg
)
68 if (strcmp (optarg
, "stdin") == 0) {
69 control_fd
= STDIN_FILENO
;
74 val
= strtol (optarg
, &endptr
, 10);
76 if ((errno
!= 0 && val
== 0) ||
77 (errno
== ERANGE
&& (val
== LONG_MAX
|| val
== LONG_MIN
))) {
78 fprintf (stderr
, "%s: strtol: \"%s\": %s\n",
79 g_get_prgname (), optarg
, strerror (errno
));
83 if (endptr
== optarg
|| *endptr
!= '\0') {
84 fprintf (stderr
, "%s: \"%s\": Not a valid integer\n",
85 g_get_prgname (), optarg
);
89 if (val
< 0 || val
> INT_MAX
) {
90 fprintf (stderr
, "%s: \"%s\": File descriptor out of range\n",
91 g_get_prgname (), optarg
);
100 x_controlfd_init (void)
102 if (control_fd
!= -1) {
103 stream
= fdopen (control_fd
, "r");
105 g_warning (_("Can't open control fd %d: %s\n"),
106 control_fd
, strerror (errno
));
110 tag
= g_unix_fd_add (control_fd
, G_IO_IN
, can_read
, NULL
);
115 x_controlfd_free (void)
118 (void) g_source_remove (tag
);
124 /*! \brief Search for a page by filename, optionally opening it.
126 * Searches all open windows for a page with filename \a arg, and
127 * return the found window and page. The filename must be absolute
128 * and is normalized in the normal manner. If the page wasn't found
129 * and \a open_if_not_found is \c TRUE, open the file in the last
132 * \returns whether a page has been found (or opened)
135 find_page (GschemToplevel
**w_current_return
, PAGE
**page_return
,
136 const gchar
*arg
, gboolean open_if_not_found
)
141 GschemToplevel
*w_current
= NULL
;
144 if (!g_path_is_absolute (arg
)) {
145 fprintf (stderr
, "Path must be absolute\n");
149 fn
= f_normalize_filename (arg
, &err
);
151 fprintf (stderr
, "%s: %s\n", arg
, err
->message
);
155 for (const GList
*l
= global_window_list
; l
!= NULL
; l
= l
->next
) {
156 w_current
= GSCHEM_TOPLEVEL (l
->data
);
157 page
= s_page_search (gschem_toplevel_get_toplevel (w_current
), fn
);
158 if (page
== NULL
|| page
->is_untitled
)
161 *w_current_return
= w_current
;
167 if (!open_if_not_found
) {
168 fprintf (stderr
, "%s: File hasn't been opened\n", fn
);
173 /* arbitrarily use last window to open file */
174 g_return_val_if_fail (w_current
!= NULL
, FALSE
);
175 page
= x_highlevel_open_page (w_current
, fn
);
180 *w_current_return
= w_current
;
186 /*! \brief Split argument string into individual tokens.
188 * Modifies \a buf in place.
190 * On error, prints a message and doesn't change the return arguments.
193 split_args (gchar
***args_return
, gint
*count_return
, gchar
*buf
)
195 GSList
*tokens
= NULL
;
196 size_t len
= strlen (buf
);
200 size_t sp
= strchrnul (buf
+ skip
, ' ') - buf
;
201 size_t bs
= strchrnul (buf
+ skip
, '\\') - buf
;
205 tokens
= g_slist_prepend (tokens
, g_strndup (buf
, sp
));
209 } else if (bs
< sp
) {
210 if (buf
[bs
+ 1] != ' ' && buf
[bs
+ 1] != '\\') {
211 fprintf (stderr
, "Backslash may only be followed by space or another "
213 g_slist_free (tokens
);
216 memmove (buf
+ bs
, buf
+ bs
+ 1, len
- bs
); /* include trailing NUL */
220 tokens
= g_slist_prepend (tokens
, g_strdup (buf
));
225 gint count
= g_slist_length (tokens
);
226 gchar
**args
= g_new (char *, count
);
229 for (unsigned int i
= 0; i
< count
; i
++) {
230 args
[count
- i
- 1] = (char *) l
->data
;
234 g_slist_free (tokens
);
237 *count_return
= count
;
241 /*! \brief Parse and execute the command passed in \a buf.
243 * Modifies \a buf in place.
246 process_command (gchar
*buf
)
251 split_args (&args
, &count
, buf
);
259 GschemToplevel
*w_current
;
262 if (strcmp (args
[0], "visit") == 0) {
264 fprintf (stderr
, "Command usage: visit FILE\n");
265 else if (find_page (&w_current
, &page
, args
[1], TRUE
)) {
266 x_window_set_current_page (w_current
, page
);
267 x_window_present (w_current
);
271 else if (strcmp (args
[0], "save") == 0) {
273 fprintf (stderr
, "Command usage: save FILE\n");
274 else if (find_page (&w_current
, &page
, args
[1], FALSE
))
275 x_highlevel_save_page (w_current
, page
);
278 else if (strcmp (args
[0], "save-all") == 0) {
280 fprintf (stderr
, "Command usage: save-all\n");
282 for (const GList
*l
= global_window_list
; l
!= NULL
; l
= l
->next
)
283 x_highlevel_save_all (GSCHEM_TOPLEVEL (l
->data
));
286 else if (strcmp (args
[0], "revert") == 0) {
288 fprintf (stderr
, "Command usage: revert FILE\n");
289 else if (find_page (&w_current
, &page
, args
[1], FALSE
))
290 x_highlevel_revert_page (w_current
, page
);
293 else if (strcmp (args
[0], "close") == 0) {
295 fprintf (stderr
, "Command usage: close FILE\n");
296 else if (find_page (&w_current
, &page
, args
[1], FALSE
))
297 x_highlevel_close_page (w_current
, page
);
300 else if (strcmp (args
[0], "patch-filename") == 0) {
302 fprintf (stderr
, "Command usage: patch-filename FILE PATCHFILE\n");
303 else if (find_page (&w_current
, &page
, args
[1], FALSE
)) {
304 g_free (page
->patch_filename
);
305 if (g_path_is_absolute (args
[2]))
306 page
->patch_filename
= g_strdup (args
[2]);
308 gchar
*dir
= g_path_get_dirname (page
->page_filename
);
309 page
->patch_filename
= g_build_filename (dir
, args
[2], NULL
);
315 else if (strcmp (args
[0], "import-patch") == 0) {
316 if (count
!= 2 && count
!= 3)
317 fprintf (stderr
, "Command usage: import-patch FILE [PATCHFILE]\n");
318 else if (find_page (&w_current
, &page
, args
[1], FALSE
)) {
320 g_free (page
->patch_filename
);
321 if (g_path_is_absolute (args
[2]))
322 page
->patch_filename
= g_strdup (args
[2]);
324 gchar
*dir
= g_path_get_dirname (page
->page_filename
);
325 page
->patch_filename
= g_build_filename (dir
, args
[2], NULL
);
329 if (page
->patch_filename
== NULL
)
330 fprintf (stderr
, "No patch filename given\n");
332 x_patch_do_import (w_current
, page
);
336 else if (strcmp (args
[0], "quit") == 0) {
338 fprintf (stderr
, "Command usage: quit\n");
340 x_window_close_all (NULL
);
343 else if (strcmp (args
[0], "help") == 0 ||
344 strcmp (args
[0], "h") == 0 ||
345 strcmp (args
[0], "?") == 0) {
347 fprintf (stderr
, "Command usage: help\n");
349 fprintf (stderr
, "%s", help_string
);
353 fprintf (stderr
, "Unknown command \"%s\"\n", args
[0]);
355 for (unsigned int i
= 0; i
< count
; i
++)
361 /*! \brief Called when \c control_fd becomes ready to read.
363 * \returns whether the function expects to be called again
366 can_read (gint fd
, GIOCondition condition
, gpointer user_data
)
373 len
= getline (&buf
, &bufsize
, stream
);
376 g_warning (_("Can't read from control fd %d: %s\n"),
377 control_fd
, strerror (errno
));
382 g_warning (_("Received EOF on control fd %d\n"), control_fd
);
387 g_warning (_("Read zero bytes from control fd %d\n"), control_fd
);
393 process_command (buf
);