1 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  *  Copyright (C) 2007-2011  Kouhei Sutou <kou@clear-code.com>
4  *
5  *  This library is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Lesser General Public License as published by
7  *  the Free Software Foundation, either version 3 of the License, or
8  *  (at your option) any later version.
9  *
10  *  This library is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Lesser General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Lesser General Public License
16  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
17  *
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #  include <config.h>
22 #endif /* HAVE_CONFIG_H */
23 
24 #include <string.h>
25 #include <math.h>
26 #ifdef HAVE_UNISTD_H
27 #  include <unistd.h>
28 #endif
29 #include <glib.h>
30 #include <glib/gstdio.h>
31 #ifdef G_OS_WIN32
32 #  include <winsock2.h>
33 #  include <io.h>
34 #  define close _close
35 #endif
36 
37 #include <errno.h>
38 
39 #include "cut-utils.h"
40 #include "cut-sub-process.h"
41 #include "cut-sub-process-group.h"
42 #include "cut-readable-differ.h"
43 #include "cut-main.h"
44 #include "cut-backtrace-entry.h"
45 #include "../gcutter/gcut-public.h"
46 #include "../gcutter/gcut-error.h"
47 #include "../gcutter/gcut-assertions-helper.h"
48 
49 #ifndef CUT_DISABLE_SOCKET_SUPPORT
50 #  ifdef CUT_HAVE_WINSOCK2_H
51 #    include <winsock2.h>
52 #    include <ws2tcpip.h>
53 #  else
54 #    include <sys/types.h>
55 #    include <sys/socket.h>
56 #    include <netinet/in.h>
57 #    include <arpa/inet.h>
58 #    ifdef HAVE_SYS_UN_H
59 #      include <sys/un.h>
60 #    endif
61 #  endif
62 #endif
63 
64 gchar *
cut_utils_create_regex_pattern(const gchar * string)65 cut_utils_create_regex_pattern (const gchar *string)
66 {
67     gchar *pattern;
68 
69     if (!string) {
70         pattern = g_strdup(".*");
71     } else if (strlen(string) > 1 &&
72         g_str_has_prefix(string, "/") && g_str_has_suffix(string, "/")) {
73         pattern = g_strndup(string + 1, strlen(string) - 2);
74     } else {
75         gchar *escaped_string;
76         escaped_string = g_regex_escape_string(string, -1);
77         pattern = g_strdup_printf("^%s$", escaped_string);
78         g_free(escaped_string);
79     }
80 
81     return pattern;
82 }
83 
84 GList *
cut_utils_filter_to_regexs(const gchar ** filter)85 cut_utils_filter_to_regexs (const gchar **filter)
86 {
87     GList *regexs = NULL;
88 
89     for (; *filter; filter++) {
90         GRegex *regex;
91         gchar *pattern;
92         GError *error = NULL;
93 
94         if (*filter[0] == '\0')
95             continue;
96 
97         pattern = cut_utils_create_regex_pattern(*filter);
98         regex = g_regex_new(pattern, 0, 0, &error);
99         if (regex) {
100             regexs = g_list_prepend(regexs, regex);
101         } else {
102             cut_utils_report_error(error);
103         }
104         g_free(pattern);
105     }
106 
107     return regexs;
108 }
109 
110 gboolean
cut_utils_filter_match(GList * regexs,const gchar * name)111 cut_utils_filter_match (GList *regexs, const gchar *name)
112 {
113     GList *node;
114 
115     for (node = regexs; node; node = g_list_next(node)) {
116         GRegex *regex = node->data;
117 
118         if (g_regex_match(regex, name, 0, NULL))
119             return TRUE;
120     }
121 
122     return FALSE;
123 }
124 
125 gchar *
cut_utils_inspect_memory(const void * memory,size_t size)126 cut_utils_inspect_memory (const void *memory, size_t size)
127 {
128     const guchar *binary = memory;
129     GString *buffer;
130     size_t i, n_printable_characters;
131     size_t max_size = 1024;
132 
133     if (memory == NULL || size == 0)
134         return g_strdup("(null)");
135 
136     buffer = g_string_sized_new(size * strlen("0xXX") +
137                                 (size - 1) * strlen(" ") +
138                                 strlen(": ") +
139                                 size);
140     max_size = MIN(size, max_size);
141     n_printable_characters = 0;
142     for (i = 0; i < max_size; i++) {
143         g_string_append_printf(buffer, "0x%02x ", binary[i]);
144         if (g_ascii_isprint(binary[i]))
145             n_printable_characters++;
146     }
147     if (size > max_size)
148         g_string_append(buffer, "... ");
149 
150     if (n_printable_characters >= max_size * 0.3) {
151         g_string_overwrite(buffer, buffer->len - 1, ": ");
152         for (i = 0; i < max_size; i++) {
153             if (g_ascii_isprint(binary[i])) {
154                 g_string_append_c(buffer, binary[i]);
155             } else {
156                 g_string_append_c(buffer, '.');
157             }
158         }
159         if (size > max_size)
160             g_string_append(buffer, "...");
161     } else {
162         g_string_truncate(buffer, buffer->len - 1);
163     }
164 
165     return g_string_free(buffer, FALSE);
166 }
167 
168 gboolean
cut_utils_equal_string(const gchar * string1,const gchar * string2)169 cut_utils_equal_string (const gchar *string1, const gchar *string2)
170 {
171     if (string1 == string2)
172         return TRUE;
173 
174     if (string1 == NULL || string2 == NULL)
175         return FALSE;
176 
177     return g_str_equal(string1, string2);
178 }
179 
180 gboolean
cut_utils_equal_substring(const gchar * string1,const gchar * string2,size_t length)181 cut_utils_equal_substring (const gchar *string1, const gchar *string2,
182                            size_t length)
183 {
184     if (string1 == string2)
185         return TRUE;
186 
187     if (string1 == NULL || string2 == NULL)
188         return FALSE;
189 
190     return strncmp(string1, string2, length) == 0;
191 }
192 
193 gboolean
cut_utils_equal_double(gdouble double1,gdouble double2,gdouble error)194 cut_utils_equal_double (gdouble double1, gdouble double2, gdouble error)
195 {
196     return fabs(double1 - double2) <= error;
197 }
198 
199 gboolean
cut_utils_equal_string_array(gchar ** strings1,gchar ** strings2)200 cut_utils_equal_string_array (gchar **strings1, gchar **strings2)
201 {
202     gint i, length;
203 
204     if (!strings1 && !strings2)
205         return TRUE;
206 
207     if (!strings1 || !strings2)
208         return FALSE;
209 
210     length = g_strv_length(strings1);
211 
212     if (length != g_strv_length(strings2))
213         return FALSE;
214 
215     for (i = 0; i < length; i++) {
216         if (strcmp(strings1[i], strings2[i]))
217             return FALSE;
218     }
219 
220     return TRUE;
221 }
222 
223 static void
cut_utils_inspect_string_to_gstring(GString * inspected,const gchar * string)224 cut_utils_inspect_string_to_gstring (GString *inspected, const gchar *string)
225 {
226     if (!string) {
227         g_string_append(inspected, "(null)");
228         return;
229     }
230 
231     g_string_append_c(inspected, '"');
232     for (; string[0]; string++) {
233         switch (string[0]) {
234         case '"':
235             g_string_append(inspected, "\\\"");
236             break;
237         case '\\':
238             g_string_append(inspected, "\\\\");
239             break;
240         default:
241             g_string_append_c(inspected, string[0]);
242             break;
243         }
244     }
245     g_string_append_c(inspected, '"');
246 }
247 
248 gchar *
cut_utils_inspect_string_array(gchar ** strings)249 cut_utils_inspect_string_array (gchar **strings)
250 {
251     GString *inspected;
252     gchar **string, **next_string;
253 
254     if (!strings)
255         return g_strdup("(null)");
256 
257     inspected = g_string_new("[");
258     string = strings;
259     while (*string) {
260         cut_utils_inspect_string_to_gstring(inspected, *string);
261         next_string = string + 1;
262         if (*next_string)
263             g_string_append(inspected, ", ");
264         string = next_string;
265     }
266     g_string_append(inspected, "]");
267 
268     return g_string_free(inspected, FALSE);
269 }
270 
271 gchar *
cut_utils_inspect_string(const gchar * string)272 cut_utils_inspect_string (const gchar *string)
273 {
274     GString *inspected;
275 
276     inspected = g_string_new(NULL);
277     cut_utils_inspect_string_to_gstring(inspected, string);
278     return g_string_free(inspected, FALSE);
279 }
280 
281 #ifndef CUT_DISABLE_SOCKET_SUPPORT
282 gboolean
cut_utils_equal_sockaddr(const struct sockaddr * address1,const struct sockaddr * address2)283 cut_utils_equal_sockaddr (const struct sockaddr *address1,
284                           const struct sockaddr *address2)
285 {
286     if (address1 == address2)
287         return TRUE;
288     if (!address1 || !address2)
289         return FALSE;
290     if (address1->sa_family != address2->sa_family)
291         return FALSE;
292 
293     switch (address1->sa_family) {
294 #ifdef HAVE_SYS_UN_H
295     case AF_UNIX:
296     {
297         struct sockaddr_un *address_unix1 = (struct sockaddr_un *)address1;
298         struct sockaddr_un *address_unix2 = (struct sockaddr_un *)address2;
299 
300         return cut_utils_equal_string(address_unix1->sun_path,
301                                       address_unix2->sun_path);
302         break;
303     }
304 #endif
305     case AF_INET:
306     {
307         struct sockaddr_in *address_inet1 = (struct sockaddr_in *)address1;
308         struct sockaddr_in *address_inet2 = (struct sockaddr_in *)address2;
309 
310         if (address_inet1->sin_addr.s_addr != address_inet2->sin_addr.s_addr)
311             return FALSE;
312         if (address_inet1->sin_port != address_inet2->sin_port)
313             return FALSE;
314 
315         return TRUE;
316         break;
317     }
318     case AF_INET6:
319     {
320         struct sockaddr_in6 *address_inet6_1 = (struct sockaddr_in6 *)address1;
321         struct sockaddr_in6 *address_inet6_2 = (struct sockaddr_in6 *)address2;
322 
323         if (memcmp(address_inet6_1->sin6_addr.s6_addr,
324                    address_inet6_2->sin6_addr.s6_addr,
325                    sizeof(address_inet6_1->sin6_addr.s6_addr)) != 0)
326             return FALSE;
327         if (address_inet6_1->sin6_port != address_inet6_2->sin6_port)
328             return FALSE;
329 
330         return TRUE;
331         break;
332     }
333     case AF_UNSPEC:
334         return TRUE;
335         break;
336     default:
337         return FALSE;
338         break;
339     }
340 
341     return FALSE;
342 }
343 
344 #ifdef G_OS_WIN32
345 #  if !defined(NTDDI_VERSION) || (NTDDI_VERSION < NTDDI_LONGHORN)
346 static const char *
inet_ntop(int address_family,const void * source,char * destination,socklen_t destination_length)347 inet_ntop (int address_family, const void *source,
348            char *destination, socklen_t destination_length)
349 {
350     DWORD socket_address_length;
351     DWORD winsock_destination_length = destination_length;
352 
353     switch (address_family) {
354     case AF_INET:
355         socket_address_length = sizeof(struct sockaddr_in);
356         break;
357     case AF_INET6:
358         socket_address_length = sizeof(struct sockaddr_in6);
359         break;
360     default:
361         return NULL;
362         break;
363     }
364 
365     if (WSAAddressToString((LPSOCKADDR)source, socket_address_length, NULL,
366                            destination, &winsock_destination_length)) {
367         return NULL;
368     } else {
369         return destination;
370     }
371 }
372 #  endif
373 #endif
374 
375 gchar *
cut_utils_inspect_sockaddr(const struct sockaddr * address)376 cut_utils_inspect_sockaddr (const struct sockaddr *address)
377 {
378     gchar *spec = NULL;
379 
380     if (!address)
381         return g_strdup("(null)");
382 
383     switch (address->sa_family) {
384 #ifdef HAVE_SYS_UN_H
385     case AF_UNIX:
386     {
387         struct sockaddr_un *address_unix = (struct sockaddr_un *)address;
388         spec = g_strdup_printf("unix:%s", address_unix->sun_path);
389         break;
390     }
391 #endif
392     case AF_INET:
393     {
394         struct sockaddr_in *address_inet = (struct sockaddr_in *)address;
395         gchar ip_address_string[INET_ADDRSTRLEN];
396 
397         if (inet_ntop(AF_INET, &address_inet->sin_addr,
398                       (gchar *)ip_address_string, INET_ADDRSTRLEN)) {
399             spec = g_strdup_printf("inet:%s:%d",
400                                    ip_address_string,
401                                    g_ntohs(address_inet->sin_port));
402         }
403         break;
404     }
405     case AF_INET6:
406     {
407         struct sockaddr_in6 *address_inet6 = (struct sockaddr_in6 *)address;
408         gchar ip_address_string[INET6_ADDRSTRLEN];
409 
410         if (inet_ntop(AF_INET6, &address_inet6->sin6_addr,
411                       (gchar *)ip_address_string, INET6_ADDRSTRLEN)) {
412             spec = g_strdup_printf("inet6:[%s]:%d",
413                                    ip_address_string,
414                                    g_ntohs(address_inet6->sin6_port));
415         }
416         break;
417     }
418     case AF_UNSPEC:
419         spec = g_strdup("unknown");
420         break;
421     default:
422         spec = g_strdup_printf("unexpected:%d", address->sa_family);
423         break;
424     }
425 
426     return spec;
427 }
428 #endif
429 
430 gboolean
cut_utils_path_exist(const gchar * path)431 cut_utils_path_exist (const gchar *path)
432 {
433     return g_file_test(path, G_FILE_TEST_EXISTS);
434 }
435 
436 gboolean
cut_utils_regex_match(const gchar * pattern,const gchar * string)437 cut_utils_regex_match (const gchar *pattern, const gchar *string)
438 {
439     return g_regex_match_simple(pattern, string, G_REGEX_MULTILINE, 0);
440 }
441 
442 gchar *
cut_utils_regex_replace(const gchar * pattern,const gchar * string,const gchar * replacement,GError ** error)443 cut_utils_regex_replace (const gchar *pattern, const gchar *string,
444                          const gchar *replacement, GError **error)
445 {
446     GRegex *regex;
447     gchar *replaced;
448 
449     regex = g_regex_new(pattern, G_REGEX_MULTILINE, 0, error);
450     if (!regex)
451         return NULL;
452 
453     replaced = g_regex_replace(regex, string, -1, 0, replacement, 0, error);
454     g_regex_unref(regex);
455 
456     return replaced;
457 }
458 
459 const gchar *
cut_utils_get_fixture_data(CutTestContext * context,const gchar ** fixture_data_path,gsize * size,const char * path,...)460 cut_utils_get_fixture_data (CutTestContext *context,
461                             const gchar **fixture_data_path,
462                             gsize *size,
463                             const char *path,
464                             ...)
465 {
466     const gchar *data;
467     va_list args;
468 
469     va_start(args, path);
470     data = cut_utils_get_fixture_data_va_list(context, fixture_data_path, size,
471                                               path, args);
472     va_end(args);
473 
474     return data;
475 }
476 
477 const gchar *
cut_utils_get_fixture_data_va_list(CutTestContext * context,const gchar ** fixture_data_path,gsize * size,const gchar * path,va_list args)478 cut_utils_get_fixture_data_va_list (CutTestContext *context,
479                                     const gchar **fixture_data_path,
480                                     gsize *size,
481                                     const gchar *path,
482                                     va_list args)
483 {
484     GString *fixture_data;
485 
486     fixture_data = gcut_utils_get_fixture_data_va_list(context,
487                                                        fixture_data_path,
488                                                        path, args);
489     if (!fixture_data)
490         return NULL;
491 
492     if (size)
493         *size = fixture_data->len;
494 
495     return fixture_data->str;
496 }
497 
498 void
cut_utils_append_indent(GString * string,guint size)499 cut_utils_append_indent (GString *string, guint size)
500 {
501     guint i;
502 
503     for (i = 0; i < size; i++)
504         g_string_append_c(string, ' ');
505 }
506 
507 void
cut_utils_append_xml_element_with_value(GString * string,guint indent,const gchar * element_name,const gchar * value)508 cut_utils_append_xml_element_with_value (GString *string, guint indent,
509                                          const gchar *element_name,
510                                          const gchar *value)
511 {
512     gchar *escaped;
513 
514     cut_utils_append_indent(string, indent);
515     escaped = g_markup_printf_escaped("<%s>%s</%s>\n",
516                                       element_name,
517                                       value,
518                                       element_name);
519     g_string_append(string, escaped);
520     g_free(escaped);
521 }
522 
523 void
cut_utils_append_xml_element_with_boolean_value(GString * string,guint indent,const gchar * element_name,gboolean boolean)524 cut_utils_append_xml_element_with_boolean_value (GString *string, guint indent,
525                                                  const gchar *element_name,
526                                                  gboolean boolean)
527 {
528     cut_utils_append_xml_element_with_value(string, indent, element_name,
529                                             boolean ? "true" : "false");
530 }
531 
532 gchar **
cut_utils_strv_concat(const gchar ** string_array,...)533 cut_utils_strv_concat (const gchar **string_array, ...)
534 {
535     guint length, i;
536     guint args_length = 0;
537     va_list args;
538     gchar *string;
539     gchar **new_string_array;
540 
541     if (!string_array)
542         return NULL;
543 
544     length = g_strv_length((gchar **)string_array);
545     va_start(args, string_array);
546     string = va_arg(args, gchar*);
547     while (string) {
548         args_length++;
549         string = va_arg(args, gchar*);
550     }
551     va_end(args);
552 
553     new_string_array = g_new(gchar*, length + args_length + 1);
554 
555     for (i = 0; i < length; i++) {
556         new_string_array[i] = g_strdup(string_array[i]);
557     }
558 
559     va_start(args, string_array);
560     string = va_arg(args, gchar*);
561     while (string) {
562         new_string_array[i] = g_strdup(string);
563         i++;
564         string = va_arg(args, gchar*);
565     }
566     va_end(args);
567 
568     new_string_array[i] = NULL;
569 
570     return new_string_array;
571 }
572 
573 void
cut_utils_close_pipe(int * pipe,CutPipeMode mode)574 cut_utils_close_pipe (int *pipe, CutPipeMode mode)
575 {
576     if (pipe[mode] == -1)
577         return;
578     close(pipe[mode]);
579     pipe[mode] = -1;
580 }
581 
582 const gchar *
cut_utils_get_cutter_command_path(void)583 cut_utils_get_cutter_command_path (void)
584 {
585     const gchar *cutter_command;
586 
587     cutter_command = g_getenv("CUTTER");
588     if (cutter_command)
589         return cutter_command;
590 
591     cutter_command = cut_get_cutter_command_path();
592     if (cutter_command)
593         return cutter_command;
594 
595     return g_get_prgname();
596 }
597 
598 gchar *
cut_utils_build_path(const gchar * path,...)599 cut_utils_build_path (const gchar *path, ...)
600 {
601     char *built_path;
602     va_list args;
603 
604     va_start(args, path);
605     built_path = cut_utils_build_path_va_list(path, args);
606     va_end(args);
607 
608     return built_path;
609 }
610 
611 gchar *
cut_utils_build_path_va_list(const gchar * path,va_list args)612 cut_utils_build_path_va_list (const gchar *path, va_list args)
613 {
614     GArray *elements;
615     gchar *element, *concatenated_path;
616 
617     if (!path)
618         return NULL;
619 
620     elements = g_array_new(TRUE, FALSE, sizeof(char *));
621 
622     g_array_append_val(elements, path);
623     while ((element = va_arg(args, gchar *))) {
624         g_array_append_val(elements, element);
625     }
626     concatenated_path =
627         cut_utils_build_path_array((const gchar **)(elements->data));
628     g_array_free(elements, TRUE);
629 
630     return concatenated_path;
631 }
632 
633 gchar *
cut_utils_build_path_array(const gchar ** paths)634 cut_utils_build_path_array (const gchar **paths)
635 {
636     return g_build_filenamev((gchar **)paths);
637 }
638 
639 gchar *
cut_utils_expand_path(const gchar * path)640 cut_utils_expand_path (const gchar *path)
641 {
642     if (!path)
643         return NULL;
644 
645     if (g_path_is_absolute(path)) {
646         return g_strdup(path);
647     } else {
648         gchar *current_dir, *full_path;
649 
650         current_dir = g_get_current_dir();
651         full_path = g_build_filename(current_dir, path, NULL);
652         g_free(current_dir);
653 
654         return full_path;
655     }
656 }
657 
658 gchar *
cut_utils_expand_path_va_list(const gchar * path,va_list args)659 cut_utils_expand_path_va_list (const gchar *path, va_list args)
660 {
661     gchar *concatenated_path, *expanded_path;
662 
663     if (!path)
664         return NULL;
665 
666     concatenated_path = cut_utils_build_path_va_list(path, args);
667     expanded_path = cut_utils_expand_path(concatenated_path);
668     g_free(concatenated_path);
669     return expanded_path;
670 }
671 
672 gboolean
cut_utils_remove_path(const char * path,GError ** error)673 cut_utils_remove_path (const char *path, GError **error)
674 {
675     if (!g_file_test(path, G_FILE_TEST_EXISTS)) {
676         g_set_error(error, G_FILE_ERROR, G_FILE_ERROR_NOENT,
677                     "path doesn't exist: %s", path);
678         return FALSE;
679     }
680 
681     if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
682         if (g_rmdir(path) == -1) {
683             g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno),
684                         "can't remove directory: %s", path);
685             return FALSE;
686         }
687     } else {
688         if (g_unlink(path) == -1) {
689             g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errno),
690                         "can't remove path: %s", path);
691             return FALSE;
692         }
693     }
694 
695     return TRUE;
696 }
697 
698 gboolean
cut_utils_remove_path_recursive(const char * path,GError ** error)699 cut_utils_remove_path_recursive (const char *path, GError **error)
700 {
701     if (g_file_test(path, G_FILE_TEST_IS_DIR)) {
702         GDir *dir;
703         const gchar *name;
704         gboolean success = TRUE;
705 
706         dir = g_dir_open(path, 0, error);
707         if (!dir)
708             return FALSE;
709 
710         while ((name = g_dir_read_name(dir))) {
711             gchar *full_path;
712 
713             full_path = g_build_filename(path, name, NULL);
714             success = cut_utils_remove_path_recursive(full_path, error);
715             g_free(full_path);
716             if (!success)
717                 break;
718         }
719 
720         g_dir_close(dir);
721 
722         if (!success)
723             return FALSE;
724     }
725 
726     return cut_utils_remove_path(path, error);
727 }
728 
729 void
cut_utils_remove_path_recursive_force(const gchar * path)730 cut_utils_remove_path_recursive_force (const gchar *path)
731 {
732     cut_utils_remove_path_recursive(path, NULL);
733 }
734 
735 void
cut_utils_make_directory_recursive_force(const gchar * path)736 cut_utils_make_directory_recursive_force (const gchar *path)
737 {
738     g_mkdir_with_parents(path, 0700);
739 }
740 
741 gchar *
cut_utils_append_diff(const gchar * message,const gchar * from,const gchar * to)742 cut_utils_append_diff (const gchar *message, const gchar *from, const gchar *to)
743 {
744     gchar *diff, *result;
745 
746     diff = cut_diff_readable(from, to);
747     if (cut_diff_readable_is_interested(diff)) {
748         result = g_strdup_printf("%s\n"
749                                  "\n"
750                                  "diff:\n"
751                                  "%s",
752                                  message, diff);
753         if (cut_diff_readable_need_fold(diff)) {
754             gchar *folded_diff, *original_result;
755 
756             original_result = result;
757             folded_diff = cut_diff_readable_folded(from, to);
758             result = g_strdup_printf("%s\n"
759                                      "\n"
760                                      "folded diff:\n"
761                                      "%s",
762                                      original_result, folded_diff);
763             g_free(original_result);
764             g_free(folded_diff);
765         }
766     } else {
767         result = g_strdup(message);
768     }
769     g_free(diff);
770 
771     return result;
772 }
773 
774 gchar *
cut_utils_fold(const gchar * string)775 cut_utils_fold (const gchar *string)
776 {
777     GRegex *fold_re;
778     GArray *folded_lines;
779     gchar **lines, **line;
780     gchar *folded_string;
781     guint i;
782 
783     fold_re = g_regex_new("(.{78})", 0, 0, NULL);
784     folded_lines = g_array_new(TRUE, FALSE, sizeof(gchar *));
785 
786     lines = g_regex_split_simple("\r?\n", string, 0, 0);
787     for (line = lines; *line; line++) {
788         gchar *folded_line;
789 
790         folded_line = g_regex_replace(fold_re, *line, -1, 0, "\\1\n", 0, NULL);
791         g_array_append_val(folded_lines, folded_line);
792     }
793     g_strfreev(lines);
794 
795     folded_string = g_strjoinv("\n", (gchar **)(folded_lines->data));
796     for (i = 0; i < folded_lines->len; i++) {
797         gchar *folded_line;
798 
799         folded_line = g_array_index(folded_lines, gchar *, i);
800         g_free(folded_line);
801     }
802     g_array_free(folded_lines, TRUE);
803     g_regex_unref(fold_re);
804 
805     return folded_string;
806 }
807 
808 CutSubProcess *
cut_utils_take_new_sub_process(const char * test_directory,CutTestContext * test_context)809 cut_utils_take_new_sub_process (const char     *test_directory,
810                                 CutTestContext *test_context)
811 {
812     CutSubProcess *sub_process;
813 
814     sub_process = cut_sub_process_new(test_directory, test_context);
815     cut_test_context_take_g_object(test_context, G_OBJECT(sub_process));
816     return sub_process;
817 }
818 
819 CutSubProcessGroup *
cut_utils_take_new_sub_process_group(CutTestContext * test_context)820 cut_utils_take_new_sub_process_group (CutTestContext *test_context)
821 {
822     CutSubProcessGroup *group;
823 
824     group = cut_sub_process_group_new(test_context);
825     cut_test_context_take_g_object(test_context, G_OBJECT(group));
826     return group;
827 }
828 
829 typedef enum
830 {
831     GDB_BACKTRACE_START,
832     GDB_BACKTRACE_IN,
833     GDB_BACKTRACE_FUNCTION,
834     GDB_BACKTRACE_ARGUMENTS,
835     GDB_BACKTRACE_ARGUMENTS_END,
836     GDB_BACKTRACE_AT,
837     GDB_BACKTRACE_LINE
838 } GdbBacktraceState;
839 
840 GList *
cut_utils_parse_gdb_backtrace(const gchar * gdb_backtrace)841 cut_utils_parse_gdb_backtrace (const gchar *gdb_backtrace)
842 {
843     GList *backtraces = NULL;
844 
845     while (gdb_backtrace && gdb_backtrace[0]) {
846         CutBacktraceEntry *entry;
847         gchar *file = NULL;
848         guint line = 0;
849         gchar *function = NULL;
850         gchar *info = NULL;
851         const gchar *start_point = NULL;
852         GdbBacktraceState state = GDB_BACKTRACE_START;
853 
854         while (gdb_backtrace[0] && gdb_backtrace[0] != '\n') {
855             switch (state) {
856             case GDB_BACKTRACE_START:
857                 if (gdb_backtrace[0] == 'i' && gdb_backtrace[1] == 'n') {
858                     gdb_backtrace++;
859                     state = GDB_BACKTRACE_IN;
860                 }
861                 break;
862             case GDB_BACKTRACE_IN:
863                 if (start_point) {
864                     if (gdb_backtrace[0] == ' ') {
865                         function = g_strndup(start_point,
866                                              gdb_backtrace - start_point);
867                         state = GDB_BACKTRACE_ARGUMENTS;
868                         start_point = NULL;
869                     }
870                 } else {
871                     if (gdb_backtrace[0] != ' ')
872                         start_point = gdb_backtrace;
873                 }
874                 break;
875             case GDB_BACKTRACE_ARGUMENTS:
876                 if (gdb_backtrace[0] == ')')
877                     state = GDB_BACKTRACE_ARGUMENTS_END;
878                 break;
879             case GDB_BACKTRACE_ARGUMENTS_END:
880                 if (gdb_backtrace[0] == 'a' && gdb_backtrace[1] == 't') {
881                     gdb_backtrace ++;
882                     state = GDB_BACKTRACE_AT;
883                 }
884                 break;
885             case GDB_BACKTRACE_AT:
886                 if (start_point) {
887                     if (gdb_backtrace[0] == ':') {
888                         file = g_strndup(start_point,
889                                          gdb_backtrace - start_point);
890                         state = GDB_BACKTRACE_LINE;
891                         start_point = NULL;
892                     }
893                 } else {
894                     if (gdb_backtrace[0] != ' ')
895                         start_point = gdb_backtrace;
896                 }
897                 break;
898             case GDB_BACKTRACE_LINE:
899                 if (start_point) {
900                     if (gdb_backtrace[1] == '\n') {
901                         line = atoi(start_point);
902                         start_point = NULL;
903                     }
904                 } else {
905                     start_point = gdb_backtrace;
906                 }
907                 break;
908             default:
909                 break;
910             }
911             gdb_backtrace++;
912         }
913 
914         entry = cut_backtrace_entry_new(file ? file : "unknown",
915                                         line,
916                                         function,
917                                         info);
918         backtraces = g_list_append(backtraces, entry);
919         if (file)
920             g_free(file);
921         if (function)
922             g_free(function);
923         if (info)
924             g_free(info);
925 
926         gdb_backtrace++;
927     }
928 
929     return backtraces;
930 }
931 
932 gchar *
cut_utils_double_to_string(gdouble value)933 cut_utils_double_to_string (gdouble value)
934 {
935     gint i;
936     gchar *string;
937 
938     string = g_strdup_printf("%f", value);
939     for (i = 0; string[i]; i++) {
940         if (string[i] == ',')
941             string[i] = '.';
942     }
943 
944     return string;
945 }
946 
947 gint
cut_utils_compare_string(gconstpointer data1,gconstpointer data2)948 cut_utils_compare_string (gconstpointer data1, gconstpointer data2)
949 {
950     if (data1 == NULL && data2 == NULL)
951         return 0;
952 
953     if (data1 == NULL)
954         return -1;
955     if (data2 == NULL)
956         return 1;
957 
958     return strcmp(data1, data2);
959 }
960 
961 gint
cut_utils_compare_direct(gconstpointer data1,gconstpointer data2)962 cut_utils_compare_direct (gconstpointer data1, gconstpointer data2)
963 {
964     guint value1, value2;
965 
966     value1 = GPOINTER_TO_UINT(data1);
967     value2 = GPOINTER_TO_UINT(data2);
968     if (value1 == value2) {
969         return 0;
970     } else if (value1 < value2) {
971         return -1;
972     } else {
973         return 1;
974     }
975 }
976 
977 #ifdef G_OS_WIN32
978 static gchar *win32_base_path = NULL;
979 
980 const gchar *
cut_win32_base_path(void)981 cut_win32_base_path (void)
982 {
983     if (win32_base_path)
984         return win32_base_path;
985 
986     win32_base_path = g_win32_get_package_installation_directory_of_module(NULL);
987 
988     return win32_base_path;
989 }
990 
991 static gchar *win32_icons_dir = NULL;
992 
993 const gchar *
cut_win32_icons_dir(void)994 cut_win32_icons_dir (void)
995 {
996     if (win32_icons_dir)
997         return win32_icons_dir;
998 
999     win32_icons_dir = g_build_filename(cut_win32_base_path(), "share", PACKAGE,
1000                                        "icons", NULL);
1001     return win32_icons_dir;
1002 }
1003 
1004 static gchar *win32_ui_data_dir = NULL;
1005 
1006 const gchar *
cut_win32_ui_data_dir(void)1007 cut_win32_ui_data_dir (void)
1008 {
1009     if (win32_ui_data_dir)
1010         return win32_ui_data_dir;
1011 
1012     win32_ui_data_dir = g_build_filename(cut_win32_base_path(), "share", PACKAGE,
1013                                          "ui", NULL);
1014     return win32_ui_data_dir;
1015 }
1016 
1017 gchar *
cut_win32_build_module_dir_name(const gchar * type)1018 cut_win32_build_module_dir_name (const gchar *type)
1019 {
1020     return g_build_filename(cut_win32_base_path(), "lib", PACKAGE,
1021                             "module", type, NULL);
1022 }
1023 
1024 gchar *
cut_win32_build_factory_module_dir_name(const gchar * type)1025 cut_win32_build_factory_module_dir_name (const gchar *type)
1026 {
1027     gchar *module_dir, *factory_module_dir;
1028 
1029     module_dir = cut_win32_build_module_dir_name("factory");
1030     factory_module_dir = g_build_filename(module_dir, type, NULL);
1031     g_free(module_dir);
1032     return factory_module_dir;
1033 }
1034 
1035 gboolean
cut_win32_kill_process(GPid pid,guint exit_code)1036 cut_win32_kill_process (GPid pid, guint exit_code)
1037 {
1038     return TerminateProcess(pid, exit_code) ? TRUE : FALSE;
1039 }
1040 #endif
1041 
1042 
1043 /*
1044 vi:ts=4:nowrap:ai:expandtab:sw=4
1045 */
1046