Allow commands to ignore output if they want.
[anjuta-git-plugin.git] / plugins / git / git-command.c
blob1782944b3ee7558149f1eb813492340357fe5971
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
2 /*
3 * anjuta
4 * Copyright (C) James Liggett 2008 <jrliggett@cox.net>
5 *
6 * anjuta is free software.
7 *
8 * You may redistribute it and/or modify it under the terms of the
9 * GNU General Public License, as published by the Free Software
10 * Foundation; either version 2 of the License, or (at your option)
11 * any later version.
13 * anjuta is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16 * See the GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with anjuta. If not, write to:
20 * The Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor
22 * Boston, MA 02110-1301, USA.
25 #include "git-command.h"
27 enum
29 PROP_0,
31 PROP_WORKING_DIRECTORY,
32 PROP_SINGLE_LINE_OUTPUT
35 struct _GitCommandPriv
37 AnjutaLauncher *launcher;
38 GList *args;
39 size_t num_args;
40 gchar *working_directory;
41 GRegex *error_regex;
42 GString *error_string;
43 GQueue *info_queue;
44 gboolean single_line_output;
47 G_DEFINE_TYPE (GitCommand, git_command, ANJUTA_TYPE_SYNC_COMMAND);
49 static void
50 git_command_multi_line_output_arrived (AnjutaLauncher *launcher,
51 AnjutaLauncherOutputType output_type,
52 const gchar *chars, GitCommand *self)
54 GitCommandClass *klass;
56 klass = GIT_COMMAND_GET_CLASS (self);
58 switch (output_type)
60 case ANJUTA_LAUNCHER_OUTPUT_STDOUT:
61 if (klass->output_handler)
62 GIT_COMMAND_GET_CLASS (self)->output_handler (self, chars);
63 break;
64 case ANJUTA_LAUNCHER_OUTPUT_STDERR:
65 GIT_COMMAND_GET_CLASS (self)->error_handler (self, chars);
66 break;
67 default:
68 break;
72 /* Split the string up line by line. Works almost like g_strsplit, execpt the
73 * newlines are preserved. */
74 static gchar **
75 split_lines (const gchar *string)
77 GList *string_list;
78 gchar *string_pos;
79 const gchar *remainder;
80 guint n;
81 gchar **lines;
82 GList *current_line;
84 string_list = NULL;
85 string_pos = strchr (string, '\n');
86 remainder = string;
87 n = 0;
89 if (string_pos)
91 while (string_pos)
93 /* Increment string_pos to preserve the newline. */
94 string_pos++;
96 string_list = g_list_prepend (string_list, g_strndup (remainder,
97 (string_pos - remainder)));
98 n++;
100 remainder = string_pos;
101 string_pos = strchr (remainder, '\n');
105 else
107 /* If there are no newlines in the string, just return a vector with
108 * one line in it. */
109 string_list = g_list_prepend (string_list, g_strdup (string));
110 n++;
113 lines = g_new (gchar *, n + 1);
114 lines[n--] = NULL;
116 for (current_line = string_list;
117 current_line;
118 current_line = g_list_next (current_line))
120 lines[n--] = current_line->data;
123 g_list_free (string_list);
125 return lines;
129 static void
130 git_command_single_line_output_arrived (AnjutaLauncher *launcher,
131 AnjutaLauncherOutputType output_type,
132 const gchar *chars, GitCommand *self)
134 void (*output_handler) (GitCommand *git_command, const gchar *output);
135 gchar **lines;
136 gchar **current_line;
138 switch (output_type)
140 case ANJUTA_LAUNCHER_OUTPUT_STDOUT:
141 output_handler = GIT_COMMAND_GET_CLASS (self)->output_handler;
142 break;
143 case ANJUTA_LAUNCHER_OUTPUT_STDERR:
144 output_handler = GIT_COMMAND_GET_CLASS (self)->error_handler;
145 break;
146 default:
147 output_handler = NULL;
148 break;
151 if (output_handler)
153 lines = split_lines (chars);
155 for (current_line = lines; *current_line; current_line++)
156 output_handler (self, *current_line);
158 g_strfreev (lines);
162 static void
163 git_command_launch (GitCommand *self)
165 gchar **args;
166 GList *current_arg;
167 gint i;
168 AnjutaLauncherOutputCallback callback;
170 args = g_new0 (gchar *, self->priv->num_args + 2);
171 current_arg = self->priv->args;
172 i = 1;
174 args[0] = "git";
176 while (current_arg)
178 args[i] = current_arg->data;
179 current_arg = g_list_next (current_arg);
180 i++;
183 if (self->priv->single_line_output)
184 callback = (AnjutaLauncherOutputCallback) git_command_single_line_output_arrived;
185 else
186 callback = (AnjutaLauncherOutputCallback) git_command_multi_line_output_arrived;
188 if (!anjuta_launcher_execute_v (self->priv->launcher,
189 args,
190 callback,
191 self))
193 git_command_append_error (self, "Command execution failed.");
194 anjuta_command_notify_complete (ANJUTA_COMMAND (self), 1);
197 /* Strings aren't copied; don't free them, just the vector */
198 g_free (args);
201 static void
202 git_command_start (AnjutaCommand *command)
204 /* We consider the command to be complete when the launcher notifies us of
205 * the child git process's completion, instead of when ::run returns. In
206 * this case, execute the command if ::run retruns 0. */
207 if (ANJUTA_COMMAND_GET_CLASS (command)->run (command) == 0)
208 git_command_launch (GIT_COMMAND (command));
211 static void
212 git_command_error_handler (GitCommand *self, const gchar *output)
214 GMatchInfo *match_info;
215 gchar *error;
217 if (g_regex_match (self->priv->error_regex, output, 0, &match_info))
219 error = g_match_info_fetch (match_info, 1);
220 g_match_info_free (match_info);
222 g_string_append (self->priv->error_string, error);
223 g_free (error);
227 static void
228 git_command_child_exited (AnjutaLauncher *launcher, gint child_pid, gint status,
229 gulong time, GitCommand *self)
231 if (strlen (self->priv->error_string->str) > 0)
233 anjuta_command_set_error_message (ANJUTA_COMMAND (self),
234 self->priv->error_string->str);
237 anjuta_command_notify_complete (ANJUTA_COMMAND (self),
238 (guint) WEXITSTATUS (status));
241 static void
242 git_command_init (GitCommand *self)
244 self->priv = g_new0 (GitCommandPriv, 1);
245 self->priv->launcher = anjuta_launcher_new ();
247 g_signal_connect (G_OBJECT (self->priv->launcher), "child-exited",
248 G_CALLBACK (git_command_child_exited),
249 self);
251 self->priv->error_regex = g_regex_new ("^(?:warning|fatal): (.*)", 0, 0,
252 NULL);
253 self->priv->error_string = g_string_new ("");
254 self->priv->info_queue = g_queue_new ();
257 static void
258 git_command_finalize (GObject *object)
260 GitCommand *self;
261 GList *current_arg;
262 GList *current_info;
264 self = GIT_COMMAND (object);
266 current_arg = self->priv->args;
268 while (current_arg)
270 g_free (current_arg->data);
271 current_arg = g_list_next (current_arg);
274 current_info = self->priv->info_queue->head;
276 while (current_info)
278 g_free (current_info->data);
279 current_info = g_list_next (current_info);
282 g_object_unref (self->priv->launcher);
283 g_regex_unref (self->priv->error_regex);
284 g_string_free (self->priv->error_string, TRUE);
285 g_queue_free (self->priv->info_queue);
286 g_free (self->priv->working_directory);
287 g_free (self->priv);
289 G_OBJECT_CLASS (git_command_parent_class)->finalize (object);
292 static void
293 git_command_set_property (GObject *object, guint prop_id, const GValue *value,
294 GParamSpec *pspec)
296 GitCommand *self;
298 self = GIT_COMMAND (object);
300 switch (prop_id)
302 case PROP_WORKING_DIRECTORY:
303 g_free (self->priv->working_directory);
304 self->priv->working_directory = g_value_dup_string (value);
305 chdir (self->priv->working_directory);
306 break;
307 case PROP_SINGLE_LINE_OUTPUT:
308 self->priv->single_line_output = g_value_get_boolean (value);
309 break;
310 default:
311 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
312 break;
316 static void
317 git_command_get_property (GObject *object, guint prop_id, GValue *value,
318 GParamSpec *pspec)
320 GitCommand *self;
322 self = GIT_COMMAND (object);
324 switch (prop_id)
326 case PROP_WORKING_DIRECTORY:
327 g_value_set_string (value, self->priv->working_directory);
328 break;
329 default:
330 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
331 break;
335 static void
336 git_command_class_init (GitCommandClass *klass)
338 GObjectClass* object_class = G_OBJECT_CLASS (klass);
339 AnjutaCommandClass* command_class = ANJUTA_COMMAND_CLASS (klass);
341 object_class->finalize = git_command_finalize;
342 object_class->set_property = git_command_set_property;
343 object_class->get_property = git_command_get_property;
344 command_class->start = git_command_start;
345 klass->output_handler = NULL;
346 klass->error_handler = git_command_error_handler;
348 g_object_class_install_property (object_class, PROP_WORKING_DIRECTORY,
349 g_param_spec_string ("working-directory",
351 "Directory to run git in.",
353 G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
355 g_object_class_install_property (object_class, PROP_SINGLE_LINE_OUTPUT,
356 g_param_spec_boolean ("single-line-output",
358 "If TRUE, output "
359 "handlers are given "
360 "output one line at "
361 "a time.",
362 FALSE,
363 G_PARAM_CONSTRUCT_ONLY | G_PARAM_WRITABLE));
367 void
368 git_command_add_arg (GitCommand *self, const gchar *arg)
370 self->priv->args = g_list_append (self->priv->args, g_strdup (arg));
371 self->priv->num_args++;
374 void
375 git_command_add_list_to_args (GitCommand *self, GList *list)
377 GList *current_arg;
379 current_arg = list;
381 while (current_arg)
383 self->priv->args = g_list_append (self->priv->args,
384 g_strdup (current_arg->data));
385 self->priv->num_args++;
387 current_arg = g_list_next (current_arg);
391 void
392 git_command_append_error (GitCommand *self, const gchar *error_line)
394 if (strlen (self->priv->error_string->str) > 0)
395 g_string_append_printf (self->priv->error_string, "\n%s", error_line);
396 else
397 g_string_append (self->priv->error_string, error_line);
400 void
401 git_command_push_info (GitCommand *self, const gchar *info)
403 g_queue_push_tail (self->priv->info_queue, g_strdup (info));
404 anjuta_command_notify_data_arrived (ANJUTA_COMMAND (self));
407 GQueue *
408 git_command_get_info_queue (GitCommand *self)
410 return self->priv->info_queue;
413 void
414 git_command_send_output_to_info (GitCommand *git_command, const gchar *output)
416 gchar *newline;
417 gchar *info_string;
419 /* Strip off the newline before sending it to the queue */
420 newline = strchr (output, '\n');
422 if (newline)
423 info_string = g_strndup (output, (newline - output));
424 else
425 info_string = g_strdup (output);
427 git_command_push_info (git_command, info_string);
430 GList *
431 git_command_copy_path_list (GList *list)
433 GList *current_path;
434 GList *new_list;
436 new_list = NULL;
437 current_path = list;
439 while (current_path)
441 new_list = g_list_append (new_list, g_strdup (current_path->data));
442 current_path = g_list_next (current_path);
445 return new_list;
448 void
449 git_command_free_path_list (GList *list)
451 GList *current_path;
453 current_path = list;
455 while (current_path)
457 g_free (current_path->data);
458 current_path = g_list_next (current_path);
461 g_list_free (list);