2 * templates.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2010 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2010 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25 * Templates to insert into the current document, or filetype templates to create a new
34 #include "templates.h"
39 #include "filetypes.h"
42 #include "geanymenubuttonaction.h"
46 GeanyTemplatePrefs template_prefs
;
48 static GtkWidget
*new_with_template_menu
= NULL
; /* submenu used for both file menu and toolbar */
50 /* TODO: implement custom insertion templates instead? */
51 static gchar
*templates
[GEANY_MAX_TEMPLATES
];
53 /* We should probably remove filetype templates support soon - users can use custom
54 * file templates instead. */
55 static gchar
*ft_templates
[GEANY_MAX_BUILT_IN_FILETYPES
] = {NULL
};
58 static void replace_static_values(GString
*text
);
59 static gchar
*get_template_fileheader(GeanyFiletype
*ft
);
61 /* called by templates_replace_common */
62 static void templates_replace_default_dates(GString
*text
);
63 static void templates_replace_command(GString
*text
, const gchar
*file_name
,
64 const gchar
*file_type
, const gchar
*func_name
);
67 /* some simple macros to reduce code size and make the code readable */
68 #define TEMPLATES_GET_FILENAME(shortname) \
69 g_strconcat(app->configdir, \
70 G_DIR_SEPARATOR_S GEANY_TEMPLATES_SUBDIR G_DIR_SEPARATOR_S, shortname, NULL)
72 #define TEMPLATES_READ_FILE(fname, contents_ptr) \
73 g_file_get_contents(fname, contents_ptr, NULL, NULL);
76 static void read_template(const gchar
*name
, gint id
)
78 gchar
*fname
= TEMPLATES_GET_FILENAME(name
);
80 /* try system if user template doesn't exist */
81 if (!g_file_test(fname
, G_FILE_TEST_EXISTS
))
82 setptr(fname
, g_strconcat(app
->datadir
,
83 G_DIR_SEPARATOR_S GEANY_TEMPLATES_SUBDIR G_DIR_SEPARATOR_S
, name
, NULL
));
85 TEMPLATES_READ_FILE(fname
, &templates
[id
]);
87 /* FIXME: we should replace the line ends on insertion with doc pref, not on loading */
89 GString
*tmp
= g_string_new(templates
[id
]);
90 const gchar
*eol_str
= (file_prefs
.default_eol_character
== SC_EOL_CR
) ? "\r" : "\r\n";
92 /* first convert data to LF only */
93 utils_string_replace_all(tmp
, "\r\n", "\n");
94 utils_string_replace_all(tmp
, "\r", "\n");
95 /* now convert to desired line endings */
96 utils_string_replace_all(tmp
, "\n", eol_str
);
97 setptr(templates
[id
], tmp
->str
);
98 g_string_free(tmp
, FALSE
);
104 /* FIXME the callers should use GStrings instead of char arrays */
105 static gchar
*replace_all(gchar
*text
, const gchar
*year
, const gchar
*date
, const gchar
*datetime
)
112 str
= g_string_new(text
);
115 templates_replace_valist(str
,
118 "{datetime}", datetime
,
121 return g_string_free(str
, FALSE
);
125 static void convert_eol_characters(GString
*template, GeanyDocument
*doc
)
130 doc
= document_get_current();
132 g_return_if_fail(doc
!= NULL
);
134 doc_eol_mode
= editor_get_eol_char_mode(doc
->editor
);
135 utils_ensure_same_eol_characters(template, doc_eol_mode
);
139 static void init_general_templates(const gchar
*year
, const gchar
*date
, const gchar
*datetime
)
143 /* read the contents */
144 read_template("fileheader", GEANY_TEMPLATE_FILEHEADER
);
145 read_template("gpl", GEANY_TEMPLATE_GPL
);
146 read_template("bsd", GEANY_TEMPLATE_BSD
);
147 read_template("function", GEANY_TEMPLATE_FUNCTION
);
148 read_template("changelog", GEANY_TEMPLATE_CHANGELOG
);
150 /* FIXME: we should replace the dates on insertion, not on loading */
151 for (id
= 0; id
< GEANY_MAX_TEMPLATES
; id
++)
152 templates
[id
] = replace_all(templates
[id
], year
, date
, datetime
);
156 static void init_ft_templates(const gchar
*year
, const gchar
*date
, const gchar
*datetime
)
160 for (ft_id
= 0; ft_id
< GEANY_MAX_BUILT_IN_FILETYPES
; ft_id
++)
162 gchar
*ext
= (ft_id
!= GEANY_FILETYPES_NONE
) ?
163 filetypes_get_conf_extension(ft_id
) : g_strdup("none");
164 gchar
*shortname
= g_strconcat("filetype.", ext
, NULL
);
165 gchar
*fname
= TEMPLATES_GET_FILENAME(shortname
);
167 TEMPLATES_READ_FILE(fname
, &ft_templates
[ft_id
]);
168 ft_templates
[ft_id
] = replace_all(ft_templates
[ft_id
], year
, date
, datetime
);
178 on_new_with_filetype_template(GtkMenuItem
*menuitem
, gpointer user_data
)
180 GeanyFiletype
*ft
= user_data
;
181 gchar
*template = templates_get_template_new_file(ft
);
183 document_new_file(NULL
, ft
, template);
188 /* TODO: remove filetype template support after 0.19 */
189 static gboolean
create_new_filetype_items(void)
192 gboolean ret
= FALSE
;
193 GtkWidget
*menu
= NULL
;
195 foreach_slist(node
, filetypes_by_title
)
197 GeanyFiletype
*ft
= node
->data
;
200 if (ft
->id
>= GEANY_MAX_BUILT_IN_FILETYPES
|| ft_templates
[ft
->id
] == NULL
)
205 item
= gtk_menu_item_new_with_label(_("Old"));
206 menu
= gtk_menu_new();
207 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), menu
);
208 gtk_widget_show_all(item
);
209 gtk_container_add(GTK_CONTAINER(new_with_template_menu
), item
);
211 item
= gtk_menu_item_new_with_label(ft
->title
);
212 gtk_widget_show(item
);
213 gtk_container_add(GTK_CONTAINER(menu
), item
);
214 g_signal_connect(item
, "activate", G_CALLBACK(on_new_with_filetype_template
), ft
);
221 void templates_replace_common(GString
*template, const gchar
*fname
,
222 GeanyFiletype
*ft
, const gchar
*func_name
)
229 shortname
= g_strdup(GEANY_STRING_UNTITLED
);
231 shortname
= g_strconcat(GEANY_STRING_UNTITLED
, ".", ft
->extension
, NULL
);
234 shortname
= g_path_get_basename(fname
);
236 templates_replace_valist(template,
237 "{filename}", shortname
,
238 "{project}", app
->project
? app
->project
->name
: "",
239 "{description}", app
->project
? app
->project
->description
: "",
243 templates_replace_default_dates(template);
244 templates_replace_command(template, fname
, ft
->name
, func_name
);
245 /* Bug: command results could have {ob} {cb} strings in! */
246 /* replace braces last */
247 templates_replace_valist(template,
254 static gchar
*get_template_from_file(const gchar
*locale_fname
, const gchar
*doc_filename
,
258 GString
*template = NULL
;
260 g_file_get_contents(locale_fname
, &content
, NULL
, NULL
);
266 template = g_string_new(content
);
268 file_header
= get_template_fileheader(ft
);
269 templates_replace_valist(template,
270 "{fileheader}", file_header
,
272 templates_replace_common(template, doc_filename
, ft
, NULL
);
274 utils_free_pointers(2, file_header
, content
, NULL
);
275 return g_string_free(template, FALSE
);
282 on_new_with_file_template(GtkMenuItem
*menuitem
, G_GNUC_UNUSED gpointer user_data
)
284 gchar
*fname
= ui_menu_item_get_text(menuitem
);
287 const gchar
*extension
= strrchr(fname
, '.'); /* easy way to get the file extension */
288 gchar
*new_filename
= g_strconcat(GEANY_STRING_UNTITLED
, extension
, NULL
);
291 ft
= filetypes_detect_from_extension(fname
);
292 setptr(fname
, utils_get_locale_from_utf8(fname
));
294 /* fname is just the basename from the menu item, so prepend the custom files path */
295 path
= g_build_path(G_DIR_SEPARATOR_S
, app
->configdir
, GEANY_TEMPLATES_SUBDIR
,
296 "files", fname
, NULL
);
297 template = get_template_from_file(path
, new_filename
, ft
);
300 /* try the system path */
302 path
= g_build_path(G_DIR_SEPARATOR_S
, app
->datadir
, GEANY_TEMPLATES_SUBDIR
,
303 "files", fname
, NULL
);
304 template = get_template_from_file(path
, new_filename
, ft
);
307 document_new_file(new_filename
, ft
, template);
310 setptr(fname
, utils_get_utf8_from_locale(fname
));
311 ui_set_statusbar(TRUE
, _("Could not find file '%s'."), fname
);
315 g_free(new_filename
);
320 static void add_file_item(const gchar
*fname
, GtkWidget
*menu
)
322 GtkWidget
*tmp_button
;
325 g_return_if_fail(fname
);
326 g_return_if_fail(menu
);
328 label
= utils_get_utf8_from_locale(fname
);
330 tmp_button
= gtk_menu_item_new_with_label(label
);
331 gtk_widget_show(tmp_button
);
332 gtk_container_add(GTK_CONTAINER(menu
), tmp_button
);
333 g_signal_connect(tmp_button
, "activate", G_CALLBACK(on_new_with_file_template
), NULL
);
339 static gboolean
add_custom_template_items(void)
341 GSList
*list
= utils_get_config_files(GEANY_TEMPLATES_SUBDIR G_DIR_SEPARATOR_S
"files");
344 foreach_slist(node
, list
)
346 gchar
*fname
= node
->data
;
348 add_file_item(fname
, new_with_template_menu
);
356 static void create_file_template_menu(void)
358 GtkWidget
*sep
= NULL
;
360 new_with_template_menu
= gtk_menu_new();
362 if (add_custom_template_items())
364 sep
= gtk_separator_menu_item_new();
365 gtk_container_add(GTK_CONTAINER(new_with_template_menu
), sep
);
367 if (create_new_filetype_items() && sep
)
369 gtk_widget_show(sep
);
371 /* unless the file menu is showing, menu should be in the toolbar widget */
372 geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(
373 toolbar_get_action_by_name("New")), new_with_template_menu
);
377 static void on_file_menu_show(GtkWidget
*item
)
379 geany_menu_button_action_set_menu(
380 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), NULL
);
381 item
= ui_lookup_widget(main_widgets
.window
, "menu_new_with_template1");
382 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), new_with_template_menu
);
386 static void on_file_menu_hide(GtkWidget
*item
)
388 item
= ui_lookup_widget(main_widgets
.window
, "menu_new_with_template1");
389 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item
), NULL
);
390 geany_menu_button_action_set_menu(
391 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), new_with_template_menu
);
395 /* reload templates if any file in the templates path is saved */
396 static void on_document_save(G_GNUC_UNUSED GObject
*object
, GeanyDocument
*doc
)
398 const gchar
*path
= utils_build_path(app
->configdir
, GEANY_TEMPLATES_SUBDIR
, NULL
);
400 g_return_if_fail(NZV(doc
->real_path
));
402 if (strncmp(doc
->real_path
, path
, strlen(path
)) == 0)
404 /* reload templates */
405 templates_free_templates();
411 /* warning: also called when reloading template settings */
412 void templates_init(void)
414 gchar
*year
= utils_get_date_time(template_prefs
.year_format
, NULL
);
415 gchar
*date
= utils_get_date_time(template_prefs
.date_format
, NULL
);
416 gchar
*datetime
= utils_get_date_time(template_prefs
.datetime_format
, NULL
);
417 static gboolean init_done
= FALSE
;
419 init_general_templates(year
, date
, datetime
);
420 init_ft_templates(year
, date
, datetime
);
426 create_file_template_menu();
427 /* we hold our own ref for the menu as it has no parent whilst being moved */
428 g_object_ref(new_with_template_menu
);
430 /* only connect signals to persistent objects once */
434 /* reparent the template menu as needed */
435 item
= ui_lookup_widget(main_widgets
.window
, "file1");
436 item
= gtk_menu_item_get_submenu(GTK_MENU_ITEM(item
));
437 g_signal_connect(item
, "show", G_CALLBACK(on_file_menu_show
), NULL
);
438 g_signal_connect(item
, "hide", G_CALLBACK(on_file_menu_hide
), NULL
);
440 g_signal_connect(geany_object
, "document-save", G_CALLBACK(on_document_save
), NULL
);
446 /* indent is used to make some whitespace between comment char and real start of the line
447 * e.g. indent = 8 prints " * here comes the text of the line"
448 * indent is meant to be the whole amount of characters before the real line content follows, i.e.
449 * 6 characters are filled with whitespace when the comment characters include " *" */
450 static void make_comment_block(GString
*comment_text
, gint filetype_idx
, guint indent
)
452 gchar
*frame_start
; /* to add before comment_text */
453 gchar
*frame_end
; /* to add after comment_text */
454 const gchar
*line_prefix
; /* to add before every line in comment_text */
459 gint template_eol_mode
;
460 const gchar
*template_eol_char
;
461 GeanyFiletype
*ft
= filetypes_index(filetype_idx
);
463 g_return_if_fail(comment_text
!= NULL
);
464 g_return_if_fail(ft
!= NULL
);
466 template_eol_mode
= utils_get_line_endings(comment_text
->str
, comment_text
->len
);
467 template_eol_char
= utils_get_eol_char(template_eol_mode
);
469 if (NZV(ft
->comment_open
))
471 if (NZV(ft
->comment_close
))
473 frame_start
= g_strconcat(ft
->comment_open
, template_eol_char
, NULL
);
474 frame_end
= g_strconcat(ft
->comment_close
, template_eol_char
, NULL
);
481 line_prefix
= ft
->comment_open
;
485 { /* use C-like multi-line comments as fallback */
486 frame_start
= g_strconcat("/*", template_eol_char
, NULL
);
487 frame_end
= g_strconcat("*/", template_eol_char
, NULL
);
491 /* do some magic to nicely format C-like multi-line comments */
492 if (NZV(frame_start
) && frame_start
[1] == '*')
494 /* prefix the string with a space */
495 setptr(frame_end
, g_strconcat(" ", frame_end
, NULL
));
499 /* construct the real prefix with given amount of whitespace */
500 i
= (indent
> strlen(line_prefix
)) ? (indent
- strlen(line_prefix
)) : strlen(line_prefix
);
501 tmp
= g_strnfill(i
, ' ');
502 prefix
= g_strconcat(line_prefix
, tmp
, NULL
);
505 /* add line_prefix to every line of comment_text */
506 lines
= g_strsplit(comment_text
->str
, template_eol_char
, -1);
507 len
= g_strv_length(lines
) - 1;
508 for (i
= 0; i
< len
; i
++)
511 lines
[i
] = g_strconcat(prefix
, tmp
, NULL
);
514 tmp
= g_strjoinv(template_eol_char
, lines
);
516 /* clear old contents */
517 g_string_erase(comment_text
, 0, -1);
520 if (frame_start
!= NULL
)
521 g_string_append(comment_text
, frame_start
);
522 /* add the new main content */
523 g_string_append(comment_text
, tmp
);
524 /* add frame_start */
525 if (frame_end
!= NULL
)
526 g_string_append(comment_text
, frame_end
);
528 utils_free_pointers(4, prefix
, tmp
, frame_start
, frame_end
, NULL
);
533 gchar
*templates_get_template_licence(GeanyDocument
*doc
, gint licence_type
)
537 g_return_val_if_fail(doc
!= NULL
, NULL
);
538 g_return_val_if_fail(licence_type
== GEANY_TEMPLATE_GPL
|| licence_type
== GEANY_TEMPLATE_BSD
, NULL
);
540 template = g_string_new(templates
[licence_type
]);
541 replace_static_values(template);
542 templates_replace_default_dates(template);
543 templates_replace_command(template, DOC_FILENAME(doc
), doc
->file_type
->name
, NULL
);
545 make_comment_block(template, doc
->file_type
->id
, 8);
546 convert_eol_characters(template, doc
);
548 return g_string_free(template, FALSE
);
552 static gchar
*get_template_fileheader(GeanyFiletype
*ft
)
554 GString
*template = g_string_new(templates
[GEANY_TEMPLATE_FILEHEADER
]);
556 filetypes_load_config(ft
->id
, FALSE
); /* load any user extension setting */
558 templates_replace_valist(template,
559 "{gpl}", templates
[GEANY_TEMPLATE_GPL
],
560 "{bsd}", templates
[GEANY_TEMPLATE_BSD
],
563 /* we don't replace other wildcards here otherwise they would get done twice for files */
564 make_comment_block(template, ft
->id
, 8);
565 return g_string_free(template, FALSE
);
569 /* TODO change the signature to take a GeanyDocument? this would break plugin API/ABI */
570 gchar
*templates_get_template_fileheader(gint filetype_idx
, const gchar
*fname
)
572 GeanyFiletype
*ft
= filetypes
[filetype_idx
];
573 gchar
*str
= get_template_fileheader(ft
);
574 GString
*template = g_string_new(str
);
577 templates_replace_common(template, fname
, ft
, NULL
);
578 convert_eol_characters(template, NULL
);
579 return g_string_free(template, FALSE
);
583 gchar
*templates_get_template_new_file(GeanyFiletype
*ft
)
585 GString
*ft_template
;
586 gchar
*file_header
= NULL
;
588 g_return_val_if_fail(ft
!= NULL
, NULL
);
589 g_return_val_if_fail(ft
->id
< GEANY_MAX_BUILT_IN_FILETYPES
, NULL
);
591 ft_template
= g_string_new(ft_templates
[ft
->id
]);
592 if (FILETYPE_ID(ft
) == GEANY_FILETYPES_NONE
)
594 replace_static_values(ft_template
);
597 { /* file template only used for new files */
598 file_header
= get_template_fileheader(ft
);
599 templates_replace_valist(ft_template
, "{fileheader}", file_header
, NULL
);
601 templates_replace_common(ft_template
, NULL
, ft
, NULL
);
602 convert_eol_characters(ft_template
, NULL
);
605 return g_string_free(ft_template
, FALSE
);
609 gchar
*templates_get_template_function(GeanyDocument
*doc
, const gchar
*func_name
)
613 func_name
= (func_name
!= NULL
) ? func_name
: "";
614 text
= g_string_new(templates
[GEANY_TEMPLATE_FUNCTION
]);
616 templates_replace_valist(text
, "{functionname}", func_name
, NULL
);
617 templates_replace_default_dates(text
);
618 templates_replace_command(text
, DOC_FILENAME(doc
), doc
->file_type
->name
, func_name
);
620 make_comment_block(text
, doc
->file_type
->id
, 3);
621 convert_eol_characters(text
, doc
);
623 return g_string_free(text
, FALSE
);
627 gchar
*templates_get_template_changelog(GeanyDocument
*doc
)
629 GString
*result
= g_string_new(templates
[GEANY_TEMPLATE_CHANGELOG
]);
630 const gchar
*file_type_name
= (doc
!= NULL
) ? doc
->file_type
->name
: "";
632 replace_static_values(result
);
633 templates_replace_default_dates(result
);
634 templates_replace_command(result
, DOC_FILENAME(doc
), file_type_name
, NULL
);
635 convert_eol_characters(result
, doc
);
637 return g_string_free(result
, FALSE
);
641 void templates_free_templates(void)
644 GList
*children
, *item
;
646 /* disconnect the menu from the action widget, so destroying the items below doesn't
647 * trigger rebuilding of the menu on each item destroy */
648 geany_menu_button_action_set_menu(
649 GEANY_MENU_BUTTON_ACTION(toolbar_get_action_by_name("New")), NULL
);
651 for (i
= 0; i
< GEANY_MAX_TEMPLATES
; i
++)
653 g_free(templates
[i
]);
655 for (i
= 0; i
< GEANY_MAX_BUILT_IN_FILETYPES
; i
++)
657 g_free(ft_templates
[i
]);
659 /* destroy "New with template" sub menu items (in case we want to reload the templates) */
660 children
= gtk_container_get_children(GTK_CONTAINER(new_with_template_menu
));
661 foreach_list(item
, children
)
663 gtk_widget_destroy(GTK_WIDGET(item
->data
));
665 g_list_free(children
);
667 g_object_unref(new_with_template_menu
);
668 new_with_template_menu
= NULL
;
672 static void replace_static_values(GString
*text
)
674 utils_string_replace_all(text
, "{version}", template_prefs
.version
);
675 utils_string_replace_all(text
, "{initial}", template_prefs
.initials
);
676 utils_string_replace_all(text
, "{developer}", template_prefs
.developer
);
677 utils_string_replace_all(text
, "{mail}", template_prefs
.mail
);
678 utils_string_replace_all(text
, "{company}", template_prefs
.company
);
679 utils_string_replace_all(text
, "{untitled}", GEANY_STRING_UNTITLED
);
680 utils_string_replace_all(text
, "{geanyversion}", "Geany " VERSION
);
684 /* Replaces all static template wildcards (version, mail, company, name, ...)
685 * plus those wildcard, value pairs which are passed, e.g.
687 * templates_replace_valist(text, "{some_wildcard}", "some value",
688 * "{another_wildcard}", "another value", NULL);
690 * The argument list must be terminated with NULL. */
691 void templates_replace_valist(GString
*text
, const gchar
*first_wildcard
, ...)
694 const gchar
*key
, *value
;
696 g_return_if_fail(text
!= NULL
);
698 va_start(args
, first_wildcard
);
700 key
= first_wildcard
;
701 value
= va_arg(args
, gchar
*);
705 utils_string_replace_all(text
, key
, value
);
707 key
= va_arg(args
, gchar
*);
708 if (key
== NULL
|| text
== NULL
)
710 value
= va_arg(args
, gchar
*);
714 replace_static_values(text
);
718 static void templates_replace_default_dates(GString
*text
)
720 gchar
*year
= utils_get_date_time(template_prefs
.year_format
, NULL
);
721 gchar
*date
= utils_get_date_time(template_prefs
.date_format
, NULL
);
722 gchar
*datetime
= utils_get_date_time(template_prefs
.datetime_format
, NULL
);
724 g_return_if_fail(text
!= NULL
);
726 templates_replace_valist(text
,
729 "{datetime}", datetime
,
732 utils_free_pointers(3, year
, date
, datetime
, NULL
);
736 static gchar
*run_command(const gchar
*command
, const gchar
*file_name
,
737 const gchar
*file_type
, const gchar
*func_name
)
739 gchar
*result
= NULL
;
742 if (g_shell_parse_argv(command
, NULL
, &argv
, NULL
))
744 GError
*error
= NULL
;
747 file_name
= (file_name
!= NULL
) ? file_name
: "";
748 file_type
= (file_type
!= NULL
) ? file_type
: "";
749 func_name
= (func_name
!= NULL
) ? func_name
: "";
751 env
= utils_copy_environment(NULL
,
752 "GEANY_FILENAME", file_name
,
753 "GEANY_FILETYPE", file_type
,
754 "GEANY_FUNCNAME", func_name
,
756 if (! utils_spawn_sync(NULL
, argv
, env
, G_SPAWN_SEARCH_PATH
,
757 NULL
, NULL
, &result
, NULL
, NULL
, &error
))
759 g_warning("templates_replace_command: %s", error
->message
);
770 static void templates_replace_command(GString
*text
, const gchar
*file_name
,
771 const gchar
*file_type
, const gchar
*func_name
)
774 gchar
*wildcard
= NULL
;
778 g_return_if_fail(text
!= NULL
);
780 while ((match
= strstr(text
->str
, "{command:")) != NULL
)
783 while (*match
!= '}' && *match
!= '\0')
786 wildcard
= g_strndup(cmd
, match
- cmd
+ 1);
787 cmd
= g_strndup(wildcard
+ 9, strlen(wildcard
) - 10);
789 result
= run_command(cmd
, file_name
, file_type
, func_name
);
792 result
= g_strstrip(result
);
793 utils_string_replace_first(text
, wildcard
, result
);
797 utils_string_replace_first(text
, wildcard
, "");