"Fossies" - the Fresh Open Source Software Archive

Member "geany-1.36/src/build.c" (28 Sep 2019, 87170 Bytes) of package /linux/misc/geany-1.36.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "build.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.35_vs_1.36.

    1 /*
    2  *      build.c - this file is part of Geany, a fast and lightweight IDE
    3  *
    4  *      Copyright 2005 The Geany contributors
    5  *
    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.
   10  *
   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.
   15  *
   16  *      You should have received a copy of the GNU General Public License along
   17  *      with this program; if not, write to the Free Software Foundation, Inc.,
   18  *      51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   19  */
   20 
   21 /*
   22  * Build commands and menu items.
   23  */
   24 /* TODO: tidy code:
   25  * Use intermediate pointers for common subexpressions.
   26  * Replace defines with enums.
   27  * Other TODOs in code. */
   28 
   29 #ifdef HAVE_CONFIG_H
   30 # include "config.h"
   31 #endif
   32 
   33 #include "app.h"
   34 #include "build.h"
   35 #include "dialogs.h"
   36 #include "document.h"
   37 #include "filetypesprivate.h"
   38 #include "geanymenubuttonaction.h"
   39 #include "geanyobject.h"
   40 #include "keybindingsprivate.h"
   41 #include "msgwindow.h"
   42 #include "prefs.h"
   43 #include "projectprivate.h"
   44 #include "sciwrappers.h"
   45 #include "spawn.h"
   46 #include "support.h"
   47 #include "toolbar.h"
   48 #include "ui_utils.h"
   49 #include "utils.h"
   50 #include "vte.h"
   51 #include "win32.h"
   52 
   53 #include "gtkcompat.h"
   54 
   55 #include <stdlib.h>
   56 #include <string.h>
   57 #include <sys/stat.h>
   58 #include <unistd.h>
   59 #include <errno.h>
   60 #include <glib/gstdio.h>
   61 
   62 
   63 
   64 /* Number of editor indicators to draw - limited as this can affect performance */
   65 #define GEANY_BUILD_ERR_HIGHLIGHT_MAX 50
   66 
   67 
   68 GeanyBuildInfo build_info = {GEANY_GBG_FT, 0, 0, NULL, GEANY_FILETYPES_NONE, NULL, 0};
   69 
   70 static gchar *current_dir_entered = NULL;
   71 
   72 typedef struct RunInfo
   73 {
   74     GPid pid;
   75     gint file_type_id;
   76 } RunInfo;
   77 
   78 static RunInfo *run_info;
   79 
   80 #ifndef G_OS_WIN32
   81 static const gchar RUN_SCRIPT_CMD[] = "geany_run_script_XXXXXX.sh";
   82 #endif
   83 
   84 /* Order is important (see GBO_TO_GBG, GBO_TO_CMD below) */
   85 /* * Geany Known Build Commands.
   86  * These commands are named after their default use.
   87  * Only these commands can currently have keybindings.
   88  **/
   89 typedef enum
   90 {
   91     GEANY_GBO_COMPILE,      /* *< default compile file */
   92     GEANY_GBO_BUILD,        /* *< default build file */
   93     GEANY_GBO_MAKE_ALL,     /* *< default make */
   94     GEANY_GBO_CUSTOM,       /* *< default make user specified target */
   95     GEANY_GBO_MAKE_OBJECT,  /* *< default make object, make %e.o */
   96     GEANY_GBO_EXEC,         /* *< default execute ./%e */
   97     GEANY_GBO_COUNT         /* *< count of how many */
   98 } GeanyBuildType;
   99 
  100 /* * Convert @c GeanyBuildType to @c GeanyBuildGroup.
  101  *
  102  * This macro converts @c GeanyBuildType enum values (the "known" commands)
  103  * to the group they are part of.
  104  *
  105  * @param gbo the @c GeanyBuildType value.
  106  *
  107  * @return the @c GeanyBuildGroup group that @a gbo is in.
  108  *
  109  * Note this is a macro so that it can be used in static initialisers.
  110  **/
  111 #define GBO_TO_GBG(gbo) \
  112     ((gbo) > GEANY_GBO_EXEC ? GEANY_GBG_COUNT : \
  113         ((gbo) >= GEANY_GBO_EXEC ? GEANY_GBG_EXEC : \
  114              ((gbo) >= GEANY_GBO_MAKE_ALL ? GEANY_GBG_NON_FT : GEANY_GBG_FT)))
  115 
  116 /* * Convert @c GeanyBuildType to command index.
  117  *
  118  * This macro converts @c GeanyBuildType enum values (the "known" commands)
  119  * to the index within the group.
  120  *
  121  * @param gbo the @c GeanyBuildType value.
  122  *
  123  * @return the index of the @a gbo command in its group.
  124  *
  125  * Note this is a macro so that it can be used in static initialisers.
  126  **/
  127 #define GBO_TO_CMD(gbo) \
  128     ((gbo) >= GEANY_GBO_COUNT ? (gbo) - GEANY_GBO_COUNT : \
  129         ((gbo) >= GEANY_GBO_EXEC ? (gbo) - GEANY_GBO_EXEC : \
  130              ((gbo) >= GEANY_GBO_MAKE_ALL ? \
  131                 (gbo) - GEANY_GBO_MAKE_ALL : (gbo))))
  132 
  133 /* pack group (<8) and command (<32) into a user_data pointer */
  134 #define GRP_CMD_TO_POINTER(grp, cmd) GUINT_TO_POINTER((((grp)&7) << 5) | ((cmd)&0x1f))
  135 #define GBO_TO_POINTER(gbo) (GRP_CMD_TO_POINTER(GBO_TO_GBG(gbo), GBO_TO_CMD(gbo)))
  136 #define GPOINTER_TO_CMD(gptr) (GPOINTER_TO_UINT(gptr)&0x1f)
  137 #define GPOINTER_TO_GRP(gptr) ((GPOINTER_TO_UINT(gptr)&0xe0) >> 5)
  138 
  139 static gpointer last_toolbutton_action = GBO_TO_POINTER(GEANY_GBO_BUILD);
  140 
  141 static BuildMenuItems menu_items = {NULL, {NULL, NULL, NULL, NULL}};
  142 
  143 static struct
  144 {
  145     GtkAction   *run_action;
  146     GtkAction   *compile_action;
  147     GtkAction   *build_action;
  148     GtkWidget   *toolmenu;
  149 
  150     GtkWidget   *toolitem_build;
  151     GtkWidget   *toolitem_make_all;
  152     GtkWidget   *toolitem_make_custom;
  153     GtkWidget   *toolitem_make_object;
  154     GtkWidget   *toolitem_set_args;
  155 }
  156 widgets;
  157 
  158 static guint build_groups_count[GEANY_GBG_COUNT] = { 3, 4, 2 };
  159 static guint build_items_count = 9;
  160 
  161 static void build_exit_cb(GPid pid, gint status, gpointer user_data);
  162 static void build_iofunc(GString *string, GIOCondition condition, gpointer data);
  163 #ifndef G_OS_WIN32
  164 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error);
  165 #endif
  166 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir);
  167 static void set_stop_button(gboolean stop);
  168 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data);
  169 static void on_set_build_commands_activate(GtkWidget *w, gpointer u);
  170 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data);
  171 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data);
  172 static void kill_process(GPid *pid);
  173 static void show_build_result_message(gboolean failure);
  174 static void process_build_output_line(gchar *msg, gint color);
  175 static void show_build_commands_dialog(void);
  176 static void on_build_menu_item(GtkWidget *w, gpointer user_data);
  177 
  178 void build_finalize(void)
  179 {
  180     g_free(build_info.dir);
  181     g_free(build_info.custom_target);
  182 
  183     if (menu_items.menu != NULL && GTK_IS_WIDGET(menu_items.menu))
  184         gtk_widget_destroy(menu_items.menu);
  185 }
  186 
  187 
  188 /* note: copied from keybindings.c, may be able to go away */
  189 static void add_menu_accel(GeanyKeyGroup *group, guint kb_id,
  190     GtkAccelGroup *accel_group, GtkWidget *menuitem)
  191 {
  192     GeanyKeyBinding *kb = keybindings_get_item(group, kb_id);
  193 
  194     if (kb->key != 0)
  195         gtk_widget_add_accelerator(menuitem, "activate", accel_group,
  196             kb->key, kb->mods, GTK_ACCEL_VISIBLE);
  197 }
  198 
  199 
  200 /* convenience routines to access parts of GeanyBuildCommand */
  201 static gchar *id_to_str(GeanyBuildCommand *bc, gint id)
  202 {
  203     switch (id)
  204     {
  205         case GEANY_BC_LABEL:
  206             return bc->label;
  207         case GEANY_BC_COMMAND:
  208             return bc->command;
  209         case GEANY_BC_WORKING_DIR:
  210             return bc->working_dir;
  211     }
  212     g_assert(0);
  213     return NULL;
  214 }
  215 
  216 
  217 static void set_command(GeanyBuildCommand *bc, gint id, gchar *str)
  218 {
  219     switch (id)
  220     {
  221         case GEANY_BC_LABEL:
  222             SETPTR(bc->label, str);
  223             break;
  224         case GEANY_BC_COMMAND:
  225             SETPTR(bc->command, str);
  226             break;
  227         case GEANY_BC_WORKING_DIR:
  228             SETPTR(bc->working_dir, str);
  229             break;
  230         default:
  231             g_assert(0);
  232     }
  233 }
  234 
  235 
  236 static const gchar *config_keys[GEANY_BC_CMDENTRIES_COUNT] = {
  237     "LB", /* label */
  238     "CM", /* command */
  239     "WD"  /* working directory */
  240 };
  241 
  242 /*-----------------------------------------------------
  243  *
  244  * Execute commands and handle results
  245  *
  246  *-----------------------------------------------------*/
  247 
  248 /* the various groups of commands not in the filetype struct */
  249 static GeanyBuildCommand *ft_def = NULL;
  250 static GeanyBuildCommand *non_ft_proj = NULL;
  251 static GeanyBuildCommand *non_ft_pref = NULL;
  252 static GeanyBuildCommand *non_ft_def = NULL;
  253 static GeanyBuildCommand *exec_proj = NULL;
  254 static GeanyBuildCommand *exec_pref = NULL;
  255 static GeanyBuildCommand *exec_def = NULL;
  256 /* and the regexen not in the filetype structure */
  257 static gchar *regex_pref = NULL;
  258 /* project non-fileregex string */
  259 static gchar *regex_proj = NULL;
  260 
  261 /* control if build commands are printed by get_build_cmd, for debug purposes only*/
  262 #ifndef PRINTBUILDCMDS
  263 #define PRINTBUILDCMDS FALSE
  264 #endif
  265 static gboolean printbuildcmds = PRINTBUILDCMDS;
  266 
  267 
  268 /* for debug only, print the commands structures in priority order */
  269 static void printfcmds(void)
  270 {
  271 #if 0
  272     GeanyBuildCommand **cl[GEANY_GBG_COUNT][GEANY_BCS_COUNT] = {
  273         /* GEANY_BCS_DEF, GEANY_BCS_FT, GEANY_BCS_HOME_FT, GEANY_BCS_PREF,
  274          * GEANY_BCS_FT_PROJ, GEANY_BCS_PROJ */
  275         { &ft_def, NULL, NULL, NULL, NULL, NULL },
  276         { &non_ft_def, NULL, NULL, &non_ft_pref, NULL, &non_ft_proj },
  277         { &exec_def, NULL, NULL, &exec_pref, NULL, &exec_proj }
  278     };
  279     GeanyFiletype *ft = NULL;
  280     GeanyDocument *doc;
  281     gint i, j, k, l, m;
  282     enum GeanyBuildCmdEntries n;
  283     gint cc[GEANY_BCS_COUNT];
  284     gchar c;
  285 
  286     doc = document_get_current();
  287     if (doc != NULL)
  288         ft = doc->file_type;
  289     if (ft != NULL)
  290     {
  291         printf("filetype %s\n",ft->name);
  292         cl[GEANY_GBG_FT][GEANY_BCS_FT] = &(ft->priv->filecmds);
  293         cl[GEANY_GBG_FT][GEANY_BCS_HOME_FT] = &(ft->priv->homefilecmds);
  294         cl[GEANY_GBG_FT][GEANY_BCS_PROJ] = &(ft->priv->projfilecmds);
  295         cl[GEANY_GBG_NON_FT][GEANY_BCS_FT] = &(ft->priv->ftdefcmds);
  296         cl[GEANY_GBG_EXEC][GEANY_BCS_FT] = &(ft->priv->execcmds);
  297         cl[GEANY_GBG_EXEC][GEANY_BCS_HOME_FT] = &(ft->priv->homeexeccmds);
  298         cl[GEANY_GBG_EXEC][GEANY_BCS_PROJ_FT] = &(ft->priv->projexeccmds);
  299     }
  300     for (i = 0; i < GEANY_BCS_COUNT; ++i)
  301     {
  302         m = 1;
  303         for (j = 0; j < GEANY_GBG_COUNT; ++j)
  304         {
  305             for (k = 0; k < build_groups_count[j]; ++k)
  306                 if (cl[j][i] != NULL && *(cl[j][i]) != NULL && (*(cl[j][i]))[k].exists)
  307                 {
  308                     for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
  309                     {
  310                         if ((*(cl[j][i]))[k].entries[n] != NULL &&
  311                             (l = strlen((*(cl[j][i]))[k].entries[n])) > m)
  312                         {
  313                             m = l;
  314                         }
  315                     }
  316                 }
  317         }
  318         cc[i] = m;
  319     }
  320     for (i = 0; i < GEANY_GBG_COUNT; ++i)
  321     {
  322         for (k = 0; k < build_groups_count[i]; ++k)
  323         {
  324             for (l = 0; l < 2; ++l)
  325             {
  326                 c = ' ';
  327                 for (j = 0; j < GEANY_BCS_COUNT; ++j)
  328                 {
  329                     if (cl[i][j] != NULL && *(cl[i][j]) != NULL && (*(cl[i][j]))[k].exists)
  330                     {
  331                         for (n = 0; n < GEANY_BC_CMDENTRIES_COUNT; n++)
  332                         {
  333                             if ((*(cl[i][j]))[k].entries[i] != NULL)
  334                                 printf("%c %*.*s",c,cc[j],cc[j],(*(cl[i][j]))[k].entries[i]);
  335                             else
  336                                 printf("%c %*.*s",c,cc[j],cc[j]," ");
  337                         }
  338                     }
  339                     else
  340                         printf("%c %*.*s",c,cc[j],cc[j]," ");
  341                     c = ',';
  342                 }
  343                 printf("\n");
  344             }
  345         }
  346         printf("\n");
  347     }
  348 #endif
  349 }
  350 
  351 
  352 /* macros to save typing and make the logic visible */
  353 #define return_cmd_if(src, cmds)\
  354     if (cmds != NULL && cmds[cmdindex].exists && below>src)\
  355     { \
  356         *fr=src; \
  357         if (printbuildcmds) \
  358             printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
  359         return &(cmds[cmdindex]); \
  360     }
  361 
  362 #define return_ft_cmd_if(src, cmds)\
  363     if (ft != NULL && ft->priv->cmds != NULL \
  364         && ft->priv->cmds[cmdindex].exists && below>src)\
  365         { \
  366             *fr=src; \
  367             if (printbuildcmds) \
  368                 printf("cmd[%u,%u]=%u\n",cmdgrp,cmdindex,src); \
  369             return &(ft->priv->cmds[cmdindex]); \
  370         }
  371 
  372 
  373 /* get the next lowest command taking priority into account */
  374 static GeanyBuildCommand *get_next_build_cmd(GeanyDocument *doc, guint cmdgrp, guint cmdindex,
  375                                              guint below, guint *from)
  376 {
  377     /* Note: parameter below used in macros above */
  378     GeanyFiletype *ft = NULL;
  379     guint sink, *fr = &sink;
  380 
  381     g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
  382 
  383     if (printbuildcmds)
  384         printfcmds();
  385     if (cmdgrp >= GEANY_GBG_COUNT)
  386         return NULL;
  387     if (from != NULL)
  388         fr = from;
  389     if (doc == NULL)
  390         doc = document_get_current();
  391     if (doc != NULL)
  392         ft = doc->file_type;
  393 
  394     switch (cmdgrp)
  395     {
  396         case GEANY_GBG_FT: /* order proj ft, home ft, ft, defft */
  397             if (ft != NULL)
  398             {
  399                 return_ft_cmd_if(GEANY_BCS_PROJ, projfilecmds);
  400                 return_ft_cmd_if(GEANY_BCS_PREF, homefilecmds);
  401                 return_ft_cmd_if(GEANY_BCS_FT, filecmds);
  402             }
  403             return_cmd_if(GEANY_BCS_DEF, ft_def);
  404             break;
  405         case GEANY_GBG_NON_FT: /* order proj, pref, def */
  406             return_cmd_if(GEANY_BCS_PROJ, non_ft_proj);
  407             return_cmd_if(GEANY_BCS_PREF, non_ft_pref);
  408             return_ft_cmd_if(GEANY_BCS_FT, ftdefcmds);
  409             return_cmd_if(GEANY_BCS_DEF, non_ft_def);
  410             break;
  411         case GEANY_GBG_EXEC: /* order proj, proj ft, pref, home ft, ft, def */
  412             return_cmd_if(GEANY_BCS_PROJ, exec_proj);
  413             return_ft_cmd_if(GEANY_BCS_PROJ_FT, projexeccmds);
  414             return_cmd_if(GEANY_BCS_PREF, exec_pref);
  415             return_ft_cmd_if(GEANY_BCS_FT, homeexeccmds);
  416             return_ft_cmd_if(GEANY_BCS_FT, execcmds);
  417             return_cmd_if(GEANY_BCS_DEF, exec_def);
  418             break;
  419         default:
  420             break;
  421     }
  422     return NULL;
  423 }
  424 
  425 
  426 /* shortcut to start looking at the top */
  427 static GeanyBuildCommand *get_build_cmd(GeanyDocument *doc, guint grp, guint cmdindex, guint *from)
  428 {
  429     return get_next_build_cmd(doc, grp, cmdindex, GEANY_BCS_COUNT, from);
  430 }
  431 
  432 
  433 #define return_nonblank_regex(src, ptr)\
  434     if (!EMPTY(ptr)) \
  435         { *fr = (src); return &(ptr); }
  436 
  437 
  438 /* like get_build_cmd, but for regexen, used by filetypes */
  439 gchar **build_get_regex(GeanyBuildGroup grp, GeanyFiletype *ft, guint *from)
  440 {
  441     guint sink, *fr = &sink;
  442 
  443     if (from != NULL)
  444         fr = from;
  445     if (grp == GEANY_GBG_FT)
  446     {
  447         if (ft == NULL)
  448         {
  449             GeanyDocument *doc = document_get_current();
  450             if (doc != NULL)
  451                 ft = doc->file_type;
  452         }
  453         if (ft == NULL)
  454             return NULL;
  455         return_nonblank_regex(GEANY_BCS_PROJ, ft->priv->projerror_regex_string);
  456         return_nonblank_regex(GEANY_BCS_HOME_FT, ft->priv->homeerror_regex_string);
  457         return_nonblank_regex(GEANY_BCS_FT, ft->error_regex_string);
  458     }
  459     else if (grp == GEANY_GBG_NON_FT)
  460     {
  461         return_nonblank_regex(GEANY_BCS_PROJ, regex_proj);
  462         return_nonblank_regex(GEANY_BCS_PREF, regex_pref);
  463     }
  464     return NULL;
  465 }
  466 
  467 
  468 static GeanyBuildCommand **get_build_group_pointer(const GeanyBuildSource src, const GeanyBuildGroup grp)
  469 {
  470     GeanyDocument *doc;
  471     GeanyFiletype *ft = NULL;
  472 
  473     switch (grp)
  474     {
  475         case GEANY_GBG_FT:
  476             if ((doc = document_get_current()) == NULL)
  477                 return NULL;
  478             if ((ft = doc->file_type) == NULL)
  479                 return NULL;
  480             switch (src)
  481             {
  482                 case GEANY_BCS_DEF:  return &(ft->priv->ftdefcmds);
  483                 case GEANY_BCS_FT:    return &(ft->priv->filecmds);
  484                 case GEANY_BCS_HOME_FT: return &(ft->priv->homefilecmds);
  485                 case GEANY_BCS_PREF:    return &(ft->priv->homefilecmds);
  486                 case GEANY_BCS_PROJ:    return &(ft->priv->projfilecmds);
  487                 default: return NULL;
  488             }
  489             break;
  490         case GEANY_GBG_NON_FT:
  491             switch (src)
  492             {
  493                 case GEANY_BCS_DEF:  return &(non_ft_def);
  494                 case GEANY_BCS_PREF:    return &(non_ft_pref);
  495                 case GEANY_BCS_PROJ:    return &(non_ft_proj);
  496                 default: return NULL;
  497             }
  498             break;
  499         case GEANY_GBG_EXEC:
  500             if ((doc = document_get_current()) != NULL)
  501                 ft = doc->file_type;
  502             switch (src)
  503             {
  504                 case GEANY_BCS_DEF:  return &(exec_def);
  505                 case GEANY_BCS_FT:    return ft ? &(ft->priv->execcmds): NULL;
  506                 case GEANY_BCS_HOME_FT: return ft ? &(ft->priv->homeexeccmds): NULL;
  507                 case GEANY_BCS_PROJ_FT: return ft ? &(ft->priv->projexeccmds): NULL;
  508                 case GEANY_BCS_PREF:    return &(exec_pref);
  509                 case GEANY_BCS_PROJ:    return &(exec_proj);
  510                 default: return NULL;
  511             }
  512             break;
  513         default:
  514             return NULL;
  515     }
  516 }
  517 
  518 
  519 /* get pointer to the command group array */
  520 static GeanyBuildCommand *get_build_group(const GeanyBuildSource src, const GeanyBuildGroup grp)
  521 {
  522     GeanyBuildCommand **g = get_build_group_pointer(src, grp);
  523     if (g == NULL) return NULL;
  524     return *g;
  525 }
  526 
  527 
  528 /** Remove the specified Build menu item.
  529  *
  530  * Makes the specified menu item configuration no longer exist. This
  531  * is different to setting fields to blank because the menu item
  532  * will be deleted from the configuration file on saving
  533  * (except the system filetypes settings @see Build Menu Configuration
  534  * section of the Manual).
  535  *
  536  * @param src the source of the menu item to remove.
  537  * @param grp the group of the command to remove.
  538  * @param cmd the index (from 0) of the command within the group. A negative
  539  *        value will remove the whole group.
  540  *
  541  * If any parameter is out of range does nothing.
  542  *
  543  * Updates the menu.
  544  *
  545  **/
  546 GEANY_API_SYMBOL
  547 void build_remove_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp, const gint cmd)
  548 {
  549     GeanyBuildCommand *bc;
  550     guint i;
  551 
  552     bc = get_build_group(src, grp);
  553     if (bc == NULL)
  554         return;
  555     if (cmd < 0)
  556     {
  557         for (i = 0; i < build_groups_count[grp]; ++i)
  558             bc[i].exists = FALSE;
  559     }
  560     else if ((guint) cmd < build_groups_count[grp])
  561         bc[cmd].exists = FALSE;
  562 }
  563 
  564 
  565 /* * Get the @a GeanyBuildCommand structure for the specified Build menu item.
  566  *
  567  * Get the command for any menu item specified by @a src, @a grp and @a cmd even if it is
  568  * hidden by higher priority commands.
  569  *
  570  * @param src the source of the specified menu item.
  571  * @param grp the group of the specified menu item.
  572  * @param cmd the index of the command within the group.
  573  *
  574  * @return a pointer to the @a GeanyBuildCommand structure or @c NULL if it doesn't exist.
  575  *         This is a pointer to an internal structure and must not be freed.
  576  *
  577  * @see build_menu_update
  578  **/
  579 GeanyBuildCommand *build_get_menu_item(GeanyBuildSource src, GeanyBuildGroup grp, guint cmd)
  580 {
  581     GeanyBuildCommand *bc;
  582 
  583     g_return_val_if_fail(src < GEANY_BCS_COUNT, NULL);
  584     g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
  585     g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
  586 
  587     bc = get_build_group(src, grp);
  588     if (bc == NULL)
  589         return NULL;
  590     return &(bc[cmd]);
  591 }
  592 
  593 
  594 /** Get the string for the menu item field.
  595  *
  596  * Get the current highest priority command specified by @a grp and @a cmd.  This is the one
  597  * that the menu item will use if activated.
  598  *
  599  * @param grp the group of the specified menu item.
  600  * @param cmd the index of the command within the group.
  601  * @param fld the field to return
  602  *
  603  * @return @nullable a pointer to the constant string or @c NULL if it doesn't exist.
  604  *         This is a pointer to an internal structure and must not be freed.
  605  *
  606  **/
  607 GEANY_API_SYMBOL
  608 const gchar *build_get_current_menu_item(const GeanyBuildGroup grp, const guint cmd,
  609                                          const GeanyBuildCmdEntries fld)
  610 {
  611     GeanyBuildCommand *c;
  612     gchar *str = NULL;
  613 
  614     g_return_val_if_fail(grp < GEANY_GBG_COUNT, NULL);
  615     g_return_val_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT, NULL);
  616     g_return_val_if_fail(cmd < build_groups_count[grp], NULL);
  617 
  618     c = get_build_cmd(NULL, grp, cmd, NULL);
  619     if (c == NULL) return NULL;
  620     switch (fld)
  621     {
  622         case GEANY_BC_COMMAND:
  623             str = c->command;
  624             break;
  625         case GEANY_BC_LABEL:
  626             str = c->label;
  627             break;
  628         case GEANY_BC_WORKING_DIR:
  629             str = c->working_dir;
  630             break;
  631         default:
  632             break;
  633     }
  634     return str;
  635 }
  636 
  637 /** Set the string for the menu item field.
  638  *
  639  * Set the specified field of the command specified by @a src, @a grp and @a cmd.
  640  *
  641  * @param src the source of the menu item
  642  * @param grp the group of the specified menu item.
  643  * @param cmd the index of the menu item within the group.
  644  * @param fld the field in the menu item command to set
  645  * @param val the value to set the field to, is copied
  646  *
  647  **/
  648 GEANY_API_SYMBOL
  649 void build_set_menu_item(const GeanyBuildSource src, const GeanyBuildGroup grp,
  650                          const guint cmd, const GeanyBuildCmdEntries fld, const gchar *val)
  651 {
  652     GeanyBuildCommand **g;
  653 
  654     g_return_if_fail(src < GEANY_BCS_COUNT);
  655     g_return_if_fail(grp < GEANY_GBG_COUNT);
  656     g_return_if_fail(fld < GEANY_BC_CMDENTRIES_COUNT);
  657     g_return_if_fail(cmd < build_groups_count[grp]);
  658 
  659     g = get_build_group_pointer(src, grp);
  660     if (g == NULL) return;
  661     if (*g == NULL )
  662     {
  663         *g = g_new0(GeanyBuildCommand, build_groups_count[grp]);
  664     }
  665     switch (fld)
  666     {
  667         case GEANY_BC_COMMAND:
  668             SETPTR((*g)[cmd].command, g_strdup(val));
  669             (*g)[cmd].exists = TRUE;
  670             break;
  671         case GEANY_BC_LABEL:
  672             SETPTR((*g)[cmd].label, g_strdup(val));
  673             (*g)[cmd].exists = TRUE;
  674             break;
  675         case GEANY_BC_WORKING_DIR:
  676             SETPTR((*g)[cmd].working_dir, g_strdup(val));
  677             (*g)[cmd].exists = TRUE;
  678             break;
  679         default:
  680             break;
  681     }
  682     build_menu_update(NULL);
  683 }
  684 
  685 /** Activate the menu item.
  686  *
  687  * Activate the menu item specified by @a grp and @a cmd.
  688  *
  689  * @param grp the group of the specified menu item.
  690  * @param cmd the index of the command within the group.
  691  *
  692  **/
  693 GEANY_API_SYMBOL
  694 void build_activate_menu_item(const GeanyBuildGroup grp, const guint cmd)
  695 {
  696     on_build_menu_item(NULL, GRP_CMD_TO_POINTER(grp, cmd));
  697 }
  698 
  699 
  700 /* Clear all error indicators in all documents. */
  701 static void clear_all_errors(void)
  702 {
  703     guint i;
  704 
  705     foreach_document(i)
  706     {
  707         editor_indicator_clear_errors(documents[i]->editor);
  708     }
  709 }
  710 
  711 
  712 /* Replaces occurrences of %e and %p with the appropriate filenames and
  713  * %l with current line number. %d and %p replacements should be in UTF8 */
  714 static gchar *build_replace_placeholder(const GeanyDocument *doc, const gchar *src)
  715 {
  716     GString *stack;
  717     gchar *replacement;
  718     gchar *executable = NULL;
  719     gint line_num;
  720 
  721     g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
  722 
  723     stack = g_string_new(src);
  724     if (doc != NULL && doc->file_name != NULL)
  725     {
  726         /* replace %f with the filename (including extension) */
  727         replacement = g_path_get_basename(doc->file_name);
  728         utils_string_replace_all(stack, "%f", replacement);
  729         g_free(replacement);
  730 
  731         /* replace %d with the absolute path of the dir of the current file */
  732         replacement = g_path_get_dirname(doc->file_name);
  733         utils_string_replace_all(stack, "%d", replacement);
  734         g_free(replacement);
  735 
  736         /* replace %e with the filename (excluding extension) */
  737         executable = utils_remove_ext_from_filename(doc->file_name);
  738         replacement = g_path_get_basename(executable);
  739         utils_string_replace_all(stack, "%e", replacement);
  740         g_free(replacement);
  741 
  742         /* replace %l with the current 1-based line number */
  743         line_num = sci_get_current_line(doc->editor->sci) + 1;
  744         replacement = g_strdup_printf("%i", line_num);
  745         utils_string_replace_all(stack, "%l", replacement);
  746         g_free(replacement);
  747     }
  748 
  749     /* replace %p with the current project's (absolute) base directory */
  750     replacement = NULL; /* prevent double free if no replacement found */
  751     if (app->project)
  752     {
  753         replacement = project_get_base_path();
  754     }
  755     else if (strstr(stack->str, "%p"))
  756     {   /* fall back to %d */
  757         ui_set_statusbar(FALSE, _("failed to substitute %%p, no project active"));
  758         if (doc != NULL && doc->file_name != NULL)
  759             replacement = g_path_get_dirname(doc->file_name);
  760     }
  761 
  762     utils_string_replace_all(stack, "%p", replacement);
  763     g_free(replacement);
  764     g_free(executable);
  765 
  766     return g_string_free(stack, FALSE); /* don't forget to free src also if needed */
  767 }
  768 
  769 
  770 /* dir is the UTF-8 working directory to run cmd in. It can be NULL to use the
  771  * idx document directory */
  772 static void build_spawn_cmd(GeanyDocument *doc, const gchar *cmd, const gchar *dir)
  773 {
  774     GError *error = NULL;
  775     gchar *argv[] = { "/bin/sh", "-c", NULL, NULL };
  776     gchar *working_dir;
  777     gchar *utf8_working_dir;
  778     gchar *cmd_string;
  779 
  780     g_return_if_fail(doc == NULL || doc->is_valid);
  781 
  782     if ((doc == NULL || EMPTY(doc->file_name)) && EMPTY(dir))
  783     {
  784         geany_debug("Failed to run command with no working directory");
  785         ui_set_statusbar(TRUE, _("Process failed, no working directory"));
  786         return;
  787     }
  788 
  789     clear_all_errors();
  790     SETPTR(current_dir_entered, NULL);
  791 
  792     utf8_working_dir = !EMPTY(dir) ? g_strdup(dir) : g_path_get_dirname(doc->file_name);
  793     working_dir = utils_get_locale_from_utf8(utf8_working_dir);
  794 
  795     gtk_list_store_clear(msgwindow.store_compiler);
  796     gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
  797     msgwin_compiler_add(COLOR_BLUE, _("%s (in directory: %s)"), cmd, utf8_working_dir);
  798     g_free(utf8_working_dir);
  799 
  800 #ifdef G_OS_UNIX
  801     cmd_string = utils_get_locale_from_utf8(cmd);
  802     argv[2] = cmd_string;
  803     cmd = NULL;  /* under Unix, use argv to start cmd via sh for compatibility */
  804 #else
  805     /* Expand environment variables like %blah%. */
  806     cmd_string = win32_expand_environment_variables(cmd);
  807     argv[0] = NULL;  /* under Windows, run cmd directly */
  808     cmd = cmd_string;
  809 #endif
  810 
  811     /* set the build info for the message window */
  812     g_free(build_info.dir);
  813     build_info.dir = g_strdup(working_dir);
  814     build_info.file_type_id = (doc == NULL) ? GEANY_FILETYPES_NONE : doc->file_type->id;
  815     build_info.message_count = 0;
  816 
  817     if (!spawn_with_callbacks(working_dir, cmd, argv, NULL, 0, NULL, NULL, build_iofunc,
  818         GINT_TO_POINTER(0), 0, build_iofunc, GINT_TO_POINTER(1), 0, build_exit_cb, NULL,
  819         &build_info.pid, &error))
  820     {
  821         geany_debug("build command spawning failed: %s", error->message);
  822         ui_set_statusbar(TRUE, _("Process failed (%s)"), error->message);
  823         g_error_free(error);
  824     }
  825 
  826     g_free(working_dir);
  827     g_free(cmd_string);
  828 }
  829 
  830 
  831 /* Returns: NULL if there was an error, or the command to be executed. If Geany is
  832  * set to use a run script, the returned value is a path to the script that runs
  833  * the command; otherwise the command itself is returned. working_dir is a pointer
  834  * to the working directory from which the command is executed. Both strings are
  835  * in the locale encoding. */
  836 static gchar *prepare_run_cmd(GeanyDocument *doc, gchar **working_dir, guint cmdindex)
  837 {
  838     GeanyBuildCommand *cmd = NULL;
  839     const gchar *cmd_working_dir;
  840     gboolean autoclose = FALSE;
  841     gchar *cmd_string_utf8, *working_dir_utf8, *run_cmd, *cmd_string;
  842     GError *error = NULL;
  843 
  844     cmd = get_build_cmd(doc, GEANY_GBG_EXEC, cmdindex, NULL);
  845 
  846     cmd_string_utf8 = build_replace_placeholder(doc, cmd->command);
  847     cmd_working_dir =  cmd->working_dir;
  848     if (EMPTY(cmd_working_dir))
  849         cmd_working_dir = "%d";
  850     working_dir_utf8 = build_replace_placeholder(doc, cmd_working_dir);
  851     *working_dir = utils_get_locale_from_utf8(working_dir_utf8);
  852 
  853     if (EMPTY(*working_dir) || ! g_file_test(*working_dir, G_FILE_TEST_EXISTS) ||
  854         ! g_file_test(*working_dir, G_FILE_TEST_IS_DIR))
  855     {
  856         ui_set_statusbar(TRUE, _("Invalid working directory \"%s\""),
  857                 !EMPTY(working_dir_utf8) ? working_dir_utf8 : "<NULL>" );
  858         utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, *working_dir, NULL);
  859         return NULL;
  860     }
  861 
  862     cmd_string = utils_get_locale_from_utf8(cmd_string_utf8);
  863 
  864 #ifdef HAVE_VTE
  865     if (vte_info.have_vte && vc->run_in_vte)
  866     {
  867         if (vc->skip_run_script)
  868         {
  869             utils_free_pointers(2, cmd_string_utf8, working_dir_utf8, NULL);
  870             return cmd_string;
  871         }
  872         else
  873             /* don't wait for user input at the end of script when we are running in VTE */
  874             autoclose = TRUE;
  875     }
  876 #endif
  877 
  878 #ifdef G_OS_WIN32
  879     /* Expand environment variables like %blah%. */
  880     SETPTR(cmd_string, win32_expand_environment_variables(cmd_string));
  881 
  882     gchar *helper = g_build_filename(utils_resource_dir(RESOURCE_DIR_LIBEXEC), "geany-run-helper", NULL);
  883     /* escape helper appropriately */
  884     /* FIXME: check the Windows rules, but it should not matter too much here as \es and "es are not
  885      * allowed in paths anyway */
  886     run_cmd = g_strdup_printf("\"%s\" \"%s\" %d %s", helper, *working_dir, autoclose ? 1 : 0, cmd_string);
  887     g_free(helper);
  888 #else
  889     run_cmd = build_create_shellscript(*working_dir, cmd_string, autoclose, &error);
  890     if (!run_cmd)
  891     {
  892         ui_set_statusbar(TRUE, _("Failed to execute \"%s\" (start-script could not be created: %s)"),
  893             !EMPTY(cmd_string_utf8) ? cmd_string_utf8 : NULL, error->message);
  894         g_error_free(error);
  895         g_free(*working_dir);
  896     }
  897 #endif
  898     utils_free_pointers(3, cmd_string_utf8, working_dir_utf8, cmd_string, NULL);
  899     return run_cmd;
  900 }
  901 
  902 
  903 static void build_run_cmd(GeanyDocument *doc, guint cmdindex)
  904 {
  905     gchar *working_dir;
  906     gchar *run_cmd = NULL;
  907 
  908     if (! DOC_VALID(doc) || doc->file_name == NULL)
  909         return;
  910 
  911     run_cmd = prepare_run_cmd(doc, &working_dir, cmdindex);
  912     if (run_cmd == NULL)
  913         return;
  914 
  915     run_info[cmdindex].file_type_id = doc->file_type->id;
  916 
  917 #ifdef HAVE_VTE
  918     if (vte_info.have_vte && vc->run_in_vte)
  919     {
  920         gchar *vte_cmd;
  921 
  922         /* VTE expects commands in UTF-8 */
  923         SETPTR(run_cmd, utils_get_utf8_from_locale(run_cmd));
  924         SETPTR(working_dir, utils_get_utf8_from_locale(working_dir));
  925 
  926         if (vc->skip_run_script)
  927             vte_cmd = g_strconcat(run_cmd, "\n", NULL);
  928         else
  929             vte_cmd = g_strconcat("\n/bin/sh ", run_cmd, "\n", NULL);
  930 
  931         vte_cwd(working_dir, TRUE);
  932         if (! vte_send_cmd(vte_cmd))
  933         {
  934             const gchar *msg = _("File not executed because the terminal may contain some input (press Ctrl+C or Enter to clear it).");
  935             ui_set_statusbar(FALSE, "%s", msg);
  936             geany_debug("%s", msg);
  937             if (!vc->skip_run_script)
  938                 g_unlink(run_cmd);
  939         }
  940 
  941         /* show the VTE */
  942         gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_VTE);
  943         gtk_widget_grab_focus(vc->vte);
  944         msgwin_show_hide(TRUE);
  945 
  946         run_info[cmdindex].pid = 1;
  947         g_free(vte_cmd);
  948     }
  949     else
  950 #endif
  951     {
  952         gchar *locale_term_cmd = utils_get_locale_from_utf8(tool_prefs.term_cmd);
  953         GError *error = NULL;
  954 
  955 #ifdef G_OS_WIN32
  956         if (g_regex_match_simple("^[ \"]*cmd([.]exe)?[\" ]", locale_term_cmd, 0, 0))
  957         {
  958             /* if passing an argument to cmd.exe, respect its quoting rules */
  959             GString *escaped_run_cmd = g_string_new(NULL);
  960             for (gchar *p = run_cmd; *p; p++)
  961             {
  962                 if (strchr("()%!^\"<>&| ", *p)) // cmd.exe metacharacters
  963                     g_string_append_c(escaped_run_cmd, '^');
  964                 g_string_append_c(escaped_run_cmd, *p);
  965             }
  966             SETPTR(run_cmd, g_string_free(escaped_run_cmd, FALSE));
  967         }
  968 #endif
  969 
  970         utils_str_replace_all(&locale_term_cmd, "%c", run_cmd);
  971 
  972         if (spawn_async(working_dir, locale_term_cmd, NULL, NULL, &(run_info[cmdindex].pid),
  973             &error))
  974         {
  975             g_child_watch_add(run_info[cmdindex].pid, (GChildWatchFunc) run_exit_cb,
  976                                 (gpointer) &(run_info[cmdindex]));
  977             build_menu_update(doc);
  978         }
  979         else
  980         {
  981             gchar *utf8_term_cmd = utils_get_utf8_from_locale(locale_term_cmd);
  982             ui_set_statusbar(TRUE, _("Cannot execute build command \"%s\": %s. "
  983                 "Check the Terminal setting in Preferences"), utf8_term_cmd, error->message);
  984             g_free(utf8_term_cmd);
  985             g_error_free(error);
  986 #ifndef G_OS_WIN32
  987             g_unlink(run_cmd);
  988 #endif
  989             run_info[cmdindex].pid = (GPid) 0;
  990         }
  991     }
  992 
  993     g_free(working_dir);
  994     g_free(run_cmd);
  995 }
  996 
  997 
  998 static void process_build_output_line(gchar *msg, gint color)
  999 {
 1000     gchar *tmp;
 1001     gchar *filename;
 1002     gint line;
 1003 
 1004     g_strchomp(msg);
 1005 
 1006     if (EMPTY(msg))
 1007         return;
 1008 
 1009     if (build_parse_make_dir(msg, &tmp))
 1010     {
 1011         SETPTR(current_dir_entered, tmp);
 1012     }
 1013     msgwin_parse_compiler_error_line(msg, current_dir_entered, &filename, &line);
 1014 
 1015     if (line != -1 && filename != NULL)
 1016     {
 1017         GeanyDocument *doc = document_find_by_filename(filename);
 1018 
 1019         /* limit number of indicators */
 1020         if (doc && editor_prefs.use_indicators &&
 1021             build_info.message_count < GEANY_BUILD_ERR_HIGHLIGHT_MAX)
 1022         {
 1023             if (line > 0) /* some compilers, like pdflatex report errors on line 0 */
 1024                 line--;   /* so only adjust the line number if it is greater than 0 */
 1025             editor_indicator_set_on_line(doc->editor, GEANY_INDICATOR_ERROR, line);
 1026         }
 1027         build_info.message_count++;
 1028         color = COLOR_RED;  /* error message parsed on the line */
 1029     }
 1030     g_free(filename);
 1031 
 1032     msgwin_compiler_add_string(color, msg);
 1033 }
 1034 
 1035 
 1036 static void build_iofunc(GString *string, GIOCondition condition, gpointer data)
 1037 {
 1038     if (condition & (G_IO_IN | G_IO_PRI))
 1039     {
 1040         process_build_output_line(string->str,
 1041             (GPOINTER_TO_INT(data)) ? COLOR_DARK_RED : COLOR_BLACK);
 1042     }
 1043 }
 1044 
 1045 
 1046 gboolean build_parse_make_dir(const gchar *string, gchar **prefix)
 1047 {
 1048     const gchar *pos;
 1049 
 1050     *prefix = NULL;
 1051 
 1052     if (string == NULL)
 1053         return FALSE;
 1054 
 1055     if ((pos = strstr(string, "Entering directory")) != NULL)
 1056     {
 1057         gsize len;
 1058         gchar *input;
 1059 
 1060         /* get the start of the path */
 1061         pos = strstr(string, "/");
 1062 
 1063         if (pos == NULL)
 1064             return FALSE;
 1065 
 1066         input = g_strdup(pos);
 1067 
 1068         /* kill the ' at the end of the path */
 1069         len = strlen(input);
 1070         input[len - 1] = '\0';
 1071         input = g_realloc(input, len);  /* shorten by 1 */
 1072         *prefix = input;
 1073 
 1074         return TRUE;
 1075     }
 1076 
 1077     if (strstr(string, "Leaving directory") != NULL)
 1078     {
 1079         *prefix = NULL;
 1080         return TRUE;
 1081     }
 1082 
 1083     return FALSE;
 1084 }
 1085 
 1086 
 1087 static void show_build_result_message(gboolean failure)
 1088 {
 1089     gchar *msg;
 1090 
 1091     if (failure)
 1092     {
 1093         msg = _("Compilation failed.");
 1094         msgwin_compiler_add_string(COLOR_BLUE, msg);
 1095         /* If msgwindow is hidden, user will want to display it to see the error */
 1096         if (! ui_prefs.msgwindow_visible)
 1097         {
 1098             gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
 1099             msgwin_show_hide(TRUE);
 1100         }
 1101         else
 1102         if (gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
 1103             ui_set_statusbar(FALSE, "%s", msg);
 1104     }
 1105     else
 1106     {
 1107         msg = _("Compilation finished successfully.");
 1108         msgwin_compiler_add_string(COLOR_BLUE, msg);
 1109         if (! ui_prefs.msgwindow_visible ||
 1110             gtk_notebook_get_current_page(GTK_NOTEBOOK(msgwindow.notebook)) != MSG_COMPILER)
 1111                 ui_set_statusbar(FALSE, "%s", msg);
 1112     }
 1113 }
 1114 
 1115 
 1116 static void build_exit_cb(GPid child_pid, gint status, gpointer user_data)
 1117 {
 1118     show_build_result_message(!SPAWN_WIFEXITED(status) || SPAWN_WEXITSTATUS(status) != EXIT_SUCCESS);
 1119     utils_beep();
 1120 
 1121     build_info.pid = 0;
 1122     /* enable build items again */
 1123     build_menu_update(NULL);
 1124     ui_progress_bar_stop();
 1125 }
 1126 
 1127 
 1128 static void run_exit_cb(GPid child_pid, gint status, gpointer user_data)
 1129 {
 1130     RunInfo *run_info_data = user_data;
 1131 
 1132     g_spawn_close_pid(child_pid);
 1133 
 1134     run_info_data->pid = 0;
 1135     /* reset the stop button and menu item to the original meaning */
 1136     build_menu_update(NULL);
 1137 }
 1138 
 1139 
 1140 /* write a little shellscript to call the executable (similar to anjuta_launcher but "internal")
 1141  * working_dir and cmd are both in the locale encoding
 1142  * it returns the full file name (including path) of the created script in the locale encoding */
 1143 #ifndef G_OS_WIN32
 1144 static gchar *build_create_shellscript(const gchar *working_dir, const gchar *cmd, gboolean autoclose, GError **error)
 1145 {
 1146     gint fd;
 1147     gchar *str, *fname;
 1148     gboolean success = TRUE;
 1149     gchar *escaped_dir;
 1150     fd = g_file_open_tmp (RUN_SCRIPT_CMD, &fname, error);
 1151     if (fd < 0)
 1152         return NULL;
 1153     close(fd);
 1154 
 1155     escaped_dir = g_shell_quote(working_dir);
 1156     str = g_strdup_printf(
 1157         "#!/bin/sh\n\nrm $0\n\ncd %s\n\n%s\n\necho \"\n\n------------------\n(program exited with code: $?)\" \
 1158         \n\n%s\n", escaped_dir, cmd, (autoclose) ? "" :
 1159         "\necho \"Press return to continue\"\n#to be more compatible with shells like "
 1160             "dash\ndummy_var=\"\"\nread dummy_var");
 1161     g_free(escaped_dir);
 1162 
 1163     if (!g_file_set_contents(fname, str, -1, error))
 1164         success = FALSE;
 1165     g_free(str);
 1166 #ifdef __APPLE__
 1167     if (success && g_chmod(fname, 0777) != 0)
 1168     {
 1169         if (error)
 1170         {
 1171             gint errsv = errno;
 1172 
 1173             g_set_error(error, G_FILE_ERROR, g_file_error_from_errno(errsv),
 1174                     "Failed to make file executable: %s", g_strerror(errsv));
 1175         }
 1176         success = FALSE;
 1177     }
 1178 #endif
 1179 
 1180     if (!success)
 1181     {
 1182         g_unlink(fname);
 1183         g_free(fname);
 1184         fname = NULL;
 1185     }
 1186 
 1187     return fname;
 1188 }
 1189 #endif
 1190 
 1191 
 1192 typedef void Callback(GtkWidget *w, gpointer u);
 1193 
 1194 /* run the command catenating cmd_cat if present */
 1195 static void build_command(GeanyDocument *doc, GeanyBuildGroup grp, guint cmd, gchar *cmd_cat)
 1196 {
 1197     gchar *dir;
 1198     gchar *full_command, *subs_command;
 1199     GeanyBuildCommand *buildcmd = get_build_cmd(doc, grp, cmd, NULL);
 1200     gchar *cmdstr;
 1201 
 1202     if (buildcmd == NULL)
 1203         return;
 1204 
 1205     cmdstr = buildcmd->command;
 1206 
 1207     if (cmd_cat != NULL)
 1208     {
 1209         if (cmdstr != NULL)
 1210             full_command = g_strconcat(cmdstr, cmd_cat, NULL);
 1211         else
 1212             full_command = g_strdup(cmd_cat);
 1213     }
 1214     else
 1215         full_command = cmdstr;
 1216 
 1217     dir = build_replace_placeholder(doc, buildcmd->working_dir);
 1218     subs_command = build_replace_placeholder(doc, full_command);
 1219     build_info.grp = grp;
 1220     build_info.cmd = cmd;
 1221     build_spawn_cmd(doc, subs_command, dir);
 1222     g_free(subs_command);
 1223     g_free(dir);
 1224     if (cmd_cat != NULL)
 1225         g_free(full_command);
 1226     build_menu_update(doc);
 1227     if (build_info.pid)
 1228         ui_progress_bar_start(NULL);
 1229 }
 1230 
 1231 
 1232 /*----------------------------------------------------------------
 1233  *
 1234  * Create build menu and handle callbacks (&toolbar callbacks)
 1235  *
 1236  *----------------------------------------------------------------*/
 1237 static void on_make_custom_input_response(const gchar *input, gpointer data)
 1238 {
 1239     GeanyDocument *doc = document_get_current();
 1240 
 1241     SETPTR(build_info.custom_target, g_strdup(input));
 1242     build_command(doc, GBO_TO_GBG(GEANY_GBO_CUSTOM), GBO_TO_CMD(GEANY_GBO_CUSTOM),
 1243                     build_info.custom_target);
 1244 }
 1245 
 1246 
 1247 static void on_build_menu_item(GtkWidget *w, gpointer user_data)
 1248 {
 1249     GeanyDocument *doc = document_get_current();
 1250     GeanyBuildCommand *bc;
 1251     guint grp = GPOINTER_TO_GRP(user_data);
 1252     guint cmd = GPOINTER_TO_CMD(user_data);
 1253 
 1254     if (doc && doc->changed)
 1255     {
 1256         if (!document_save_file(doc, FALSE))
 1257             return;
 1258     }
 1259     g_signal_emit_by_name(geany_object, "build-start");
 1260 
 1261     if (grp == GEANY_GBG_NON_FT && cmd == GBO_TO_CMD(GEANY_GBO_CUSTOM))
 1262     {
 1263         static GtkWidget *dialog = NULL; /* keep dialog for combo history */
 1264 
 1265         if (! dialog)
 1266         {
 1267             dialog = dialogs_show_input_persistent(_("Custom Text"), GTK_WINDOW(main_widgets.window),
 1268                 _("Enter custom text here, all entered text is appended to the command."),
 1269                 build_info.custom_target, &on_make_custom_input_response, NULL);
 1270         }
 1271         else
 1272         {
 1273             gtk_widget_show(dialog);
 1274         }
 1275         return;
 1276     }
 1277     else if (grp == GEANY_GBG_EXEC)
 1278     {
 1279         if (run_info[cmd].pid > (GPid) 1)
 1280         {
 1281             kill_process(&run_info[cmd].pid);
 1282             return;
 1283         }
 1284         bc = get_build_cmd(doc, grp, cmd, NULL);
 1285         if (bc != NULL && strcmp(bc->command, "builtin") == 0)
 1286         {
 1287             const gchar *uri_file_prefix;
 1288             gchar *uri;
 1289             if (doc == NULL)
 1290                 return;
 1291             uri_file_prefix = utils_get_uri_file_prefix();
 1292             uri = g_strconcat(uri_file_prefix, doc->file_name, NULL);
 1293             utils_open_browser(uri);
 1294             g_free(uri);
 1295         }
 1296         else
 1297             build_run_cmd(doc, cmd);
 1298     }
 1299     else
 1300         build_command(doc, grp, cmd, NULL);
 1301 }
 1302 
 1303 
 1304 /* group codes for menu items other than the known commands
 1305  * value order is important, see the following table for use */
 1306 
 1307 /* the rest in each group */
 1308 #define MENU_FT_REST     (GEANY_GBG_COUNT + GEANY_GBG_FT)
 1309 #define MENU_NON_FT_REST (GEANY_GBG_COUNT + GEANY_GBG_NON_FT)
 1310 #define MENU_EXEC_REST   (GEANY_GBG_COUNT + GEANY_GBG_EXEC)
 1311 /* the separator */
 1312 #define MENU_SEPARATOR   (2*GEANY_GBG_COUNT)
 1313 /* the fixed items */
 1314 #define MENU_NEXT_ERROR  (MENU_SEPARATOR + 1)
 1315 #define MENU_PREV_ERROR  (MENU_NEXT_ERROR + 1)
 1316 #define MENU_COMMANDS   (MENU_PREV_ERROR + 1)
 1317 #define MENU_DONE       (MENU_COMMANDS + 1)
 1318 
 1319 
 1320 static struct BuildMenuItemSpec {
 1321     const gchar *stock_id;
 1322     const gint   key_binding;
 1323     const guint  build_grp;
 1324     const guint  build_cmd;
 1325     const gchar *fix_label;
 1326     Callback *cb;
 1327 } build_menu_specs[] = {
 1328     {GTK_STOCK_CONVERT, GEANY_KEYS_BUILD_COMPILE, GBO_TO_GBG(GEANY_GBO_COMPILE),
 1329         GBO_TO_CMD(GEANY_GBO_COMPILE), NULL, on_build_menu_item},
 1330     {GEANY_STOCK_BUILD, GEANY_KEYS_BUILD_LINK, GBO_TO_GBG(GEANY_GBO_BUILD),
 1331         GBO_TO_CMD(GEANY_GBO_BUILD), NULL, on_build_menu_item},
 1332     {NULL, -1, MENU_FT_REST,
 1333         GBO_TO_CMD(GEANY_GBO_BUILD) + 1, NULL, on_build_menu_item},
 1334     {NULL, -1, MENU_SEPARATOR,
 1335         GBF_SEP_1, NULL, NULL},
 1336     {NULL, GEANY_KEYS_BUILD_MAKE, GBO_TO_GBG(GEANY_GBO_MAKE_ALL),
 1337         GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL, on_build_menu_item},
 1338     {NULL, GEANY_KEYS_BUILD_MAKEOWNTARGET, GBO_TO_GBG(GEANY_GBO_CUSTOM),
 1339         GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL, on_build_menu_item},
 1340     {NULL, GEANY_KEYS_BUILD_MAKEOBJECT, GBO_TO_GBG(GEANY_GBO_MAKE_OBJECT),
 1341         GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL, on_build_menu_item},
 1342     {NULL, -1, MENU_NON_FT_REST,
 1343         GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT) + 1, NULL, on_build_menu_item},
 1344     {NULL, -1, MENU_SEPARATOR,
 1345         GBF_SEP_2, NULL, NULL},
 1346     {GTK_STOCK_GO_DOWN, GEANY_KEYS_BUILD_NEXTERROR, MENU_NEXT_ERROR,
 1347         GBF_NEXT_ERROR, N_("_Next Error"), on_build_next_error},
 1348     {GTK_STOCK_GO_UP, GEANY_KEYS_BUILD_PREVIOUSERROR, MENU_PREV_ERROR,
 1349         GBF_PREV_ERROR, N_("_Previous Error"), on_build_previous_error},
 1350     {NULL, -1, MENU_SEPARATOR,
 1351         GBF_SEP_3, NULL, NULL},
 1352     {GTK_STOCK_EXECUTE, GEANY_KEYS_BUILD_RUN, GBO_TO_GBG(GEANY_GBO_EXEC),
 1353         GBO_TO_CMD(GEANY_GBO_EXEC), NULL, on_build_menu_item},
 1354     {NULL, -1, MENU_EXEC_REST,
 1355         GBO_TO_CMD(GEANY_GBO_EXEC) + 1, NULL, on_build_menu_item},
 1356     {NULL, -1, MENU_SEPARATOR,
 1357         GBF_SEP_4, NULL, NULL},
 1358     {GTK_STOCK_PREFERENCES, GEANY_KEYS_BUILD_OPTIONS, MENU_COMMANDS,
 1359         GBF_COMMANDS, N_("_Set Build Commands"), on_set_build_commands_activate},
 1360     {NULL, -1, MENU_DONE,
 1361         0, NULL, NULL}
 1362 };
 1363 
 1364 
 1365 static void create_build_menu_item(GtkWidget *menu, GeanyKeyGroup *group, GtkAccelGroup *ag,
 1366                             struct BuildMenuItemSpec *bs, const gchar *lbl, guint grp, guint cmd)
 1367 {
 1368     GtkWidget *item = gtk_image_menu_item_new_with_mnemonic(lbl);
 1369 
 1370     if (bs->stock_id != NULL)
 1371     {
 1372         GtkWidget *image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
 1373         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), image);
 1374     }
 1375     gtk_widget_show(item);
 1376     if (bs->key_binding >= 0)
 1377         add_menu_accel(group, (guint) bs->key_binding, ag, item);
 1378     gtk_container_add(GTK_CONTAINER(menu), item);
 1379     if (bs->cb != NULL)
 1380     {
 1381         g_signal_connect(item, "activate", G_CALLBACK(bs->cb), GRP_CMD_TO_POINTER(grp,cmd));
 1382     }
 1383     menu_items.menu_item[grp][cmd] = item;
 1384 }
 1385 
 1386 
 1387 static void create_build_menu(BuildMenuItems *build_menu_items)
 1388 {
 1389     GtkWidget *menu;
 1390     GtkAccelGroup *accel_group = gtk_accel_group_new();
 1391     GeanyKeyGroup *keygroup = keybindings_get_core_group(GEANY_KEY_GROUP_BUILD);
 1392     guint i, j;
 1393 
 1394     menu = gtk_menu_new();
 1395     build_menu_items->menu_item[GEANY_GBG_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_FT]);
 1396     build_menu_items->menu_item[GEANY_GBG_NON_FT] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_NON_FT]);
 1397     build_menu_items->menu_item[GEANY_GBG_EXEC] = g_new0(GtkWidget*, build_groups_count[GEANY_GBG_EXEC]);
 1398     build_menu_items->menu_item[GBG_FIXED] = g_new0(GtkWidget*, GBF_COUNT);
 1399 
 1400     for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
 1401     {
 1402         struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
 1403         if (bs->build_grp == MENU_SEPARATOR)
 1404         {
 1405             GtkWidget *item = gtk_separator_menu_item_new();
 1406             gtk_widget_show(item);
 1407             gtk_container_add(GTK_CONTAINER(menu), item);
 1408             build_menu_items->menu_item[GBG_FIXED][bs->build_cmd] = item;
 1409         }
 1410         else if (bs->fix_label != NULL)
 1411         {
 1412             create_build_menu_item(menu, keygroup, accel_group, bs, _(bs->fix_label),
 1413                                     GBG_FIXED, bs->build_cmd);
 1414         }
 1415         else if (bs->build_grp >= MENU_FT_REST && bs->build_grp <= MENU_SEPARATOR)
 1416         {
 1417             guint grp = bs->build_grp - GEANY_GBG_COUNT;
 1418             for (j = bs->build_cmd; j < build_groups_count[grp]; ++j)
 1419             {
 1420                 GeanyBuildCommand *bc = get_build_cmd(NULL, grp, j, NULL);
 1421                 const gchar *lbl = (bc == NULL) ? "" : bc->label;
 1422                 create_build_menu_item(menu, keygroup, accel_group, bs, lbl, grp, j);
 1423             }
 1424         }
 1425         else
 1426         {
 1427             GeanyBuildCommand *bc = get_build_cmd(NULL, bs->build_grp, bs->build_cmd, NULL);
 1428             const gchar *lbl = (bc == NULL) ? "" : bc->label;
 1429             create_build_menu_item(menu, keygroup, accel_group, bs, lbl, bs->build_grp, bs->build_cmd);
 1430         }
 1431     }
 1432     build_menu_items->menu = menu;
 1433     gtk_widget_show(menu);
 1434     gtk_menu_item_set_submenu(GTK_MENU_ITEM(ui_lookup_widget(main_widgets.window, "menu_build1")), menu);
 1435 }
 1436 
 1437 
 1438 /* * Update the build menu to reflect changes in configuration or status.
 1439  *
 1440  * Sets the labels and number of visible items to match the highest
 1441  * priority configured commands.  Also sets sensitivity if build commands are
 1442  * running and switches executes to stop when commands are running.
 1443  *
 1444  * @param doc The current document, if available, to save looking it up.
 1445  *        If @c NULL it will be looked up.
 1446  *
 1447  * Call this after modifying any fields of a GeanyBuildCommand structure.
 1448  *
 1449  * @see Build Menu Configuration section of the Manual.
 1450  *
 1451  **/
 1452 void build_menu_update(GeanyDocument *doc)
 1453 {
 1454     guint i, cmdcount, cmd, grp;
 1455     gboolean vis = FALSE;
 1456     gboolean have_path, build_running, exec_running, have_errors, cmd_sensitivity;
 1457     gboolean can_compile, can_build, can_make, run_sensitivity = FALSE, run_running = FALSE;
 1458     GeanyBuildCommand *bc;
 1459 
 1460     g_return_if_fail(doc == NULL || doc->is_valid);
 1461 
 1462     if (menu_items.menu == NULL)
 1463         create_build_menu(&menu_items);
 1464     if (doc == NULL)
 1465         doc = document_get_current();
 1466     have_path = doc != NULL && doc->file_name != NULL;
 1467     build_running =  build_info.pid > (GPid) 1;
 1468     have_errors = gtk_tree_model_iter_n_children(GTK_TREE_MODEL(msgwindow.store_compiler), NULL) > 0;
 1469     for (i = 0; build_menu_specs[i].build_grp != MENU_DONE; ++i)
 1470     {
 1471         struct BuildMenuItemSpec *bs = &(build_menu_specs[i]);
 1472         switch (bs->build_grp)
 1473         {
 1474             case MENU_SEPARATOR:
 1475                 if (vis == TRUE)
 1476                 {
 1477                     gtk_widget_show_all(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
 1478                     vis = FALSE;
 1479                 }
 1480                 else
 1481                     gtk_widget_hide(menu_items.menu_item[GBG_FIXED][bs->build_cmd]);
 1482                 break;
 1483             case MENU_NEXT_ERROR:
 1484             case MENU_PREV_ERROR:
 1485                 gtk_widget_set_sensitive(menu_items.menu_item[GBG_FIXED][bs->build_cmd], have_errors);
 1486                 vis |= TRUE;
 1487                 break;
 1488             case MENU_COMMANDS:
 1489                 vis |= TRUE;
 1490                 break;
 1491             default: /* all configurable commands */
 1492                 if (bs->build_grp >= GEANY_GBG_COUNT)
 1493                 {
 1494                     grp = bs->build_grp - GEANY_GBG_COUNT;
 1495                     cmdcount = build_groups_count[grp];
 1496                 }
 1497                 else
 1498                 {
 1499                     grp = bs->build_grp;
 1500                     cmdcount = bs->build_cmd + 1;
 1501                 }
 1502                 for (cmd = bs->build_cmd; cmd < cmdcount; ++cmd)
 1503                 {
 1504                     GtkWidget *menu_item = menu_items.menu_item[grp][cmd];
 1505                     const gchar *label;
 1506                     bc = get_build_cmd(doc, grp, cmd, NULL);
 1507                     if (bc)
 1508                         label = bc->label;
 1509                     else
 1510                         label = NULL;
 1511 
 1512                     if (grp < GEANY_GBG_EXEC)
 1513                     {
 1514                         cmd_sensitivity =
 1515                             (grp == GEANY_GBG_FT && bc != NULL && have_path && ! build_running) ||
 1516                             (grp == GEANY_GBG_NON_FT && bc != NULL && ! build_running);
 1517                         gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
 1518                         if (bc != NULL && !EMPTY(label))
 1519                         {
 1520                             gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
 1521                             gtk_widget_show_all(menu_item);
 1522                             vis |= TRUE;
 1523                         }
 1524                         else
 1525                             gtk_widget_hide(menu_item);
 1526                     }
 1527                     else
 1528                     {
 1529                         GtkWidget *image;
 1530                         exec_running = run_info[cmd].pid > (GPid) 1;
 1531                         cmd_sensitivity = (bc != NULL) || exec_running;
 1532                         gtk_widget_set_sensitive(menu_item, cmd_sensitivity);
 1533                         if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
 1534                             run_sensitivity = cmd_sensitivity;
 1535                         if (! exec_running)
 1536                         {
 1537                             image = gtk_image_new_from_stock(bs->stock_id, GTK_ICON_SIZE_MENU);
 1538                         }
 1539                         else
 1540                         {
 1541                             image = gtk_image_new_from_stock(GTK_STOCK_STOP, GTK_ICON_SIZE_MENU);
 1542                         }
 1543                         if (cmd == GBO_TO_CMD(GEANY_GBO_EXEC))
 1544                             run_running = exec_running;
 1545                         gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image);
 1546                         if (bc != NULL && !EMPTY(label))
 1547                         {
 1548                             gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), label);
 1549                             gtk_widget_show_all(menu_item);
 1550                             vis |= TRUE;
 1551                         }
 1552                         else
 1553                             gtk_widget_hide(menu_item);
 1554                     }
 1555                 }
 1556         }
 1557     }
 1558 
 1559     run_sensitivity &= (doc != NULL);
 1560     can_build = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_BUILD), NULL) != NULL
 1561                     && have_path && ! build_running;
 1562     if (widgets.toolitem_build != NULL)
 1563         gtk_widget_set_sensitive(widgets.toolitem_build, can_build);
 1564     can_make = FALSE;
 1565     if (widgets.toolitem_make_all != NULL)
 1566         gtk_widget_set_sensitive(widgets.toolitem_make_all,
 1567             (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_ALL), NULL) != NULL
 1568                             && ! build_running));
 1569     if (widgets.toolitem_make_custom != NULL)
 1570         gtk_widget_set_sensitive(widgets.toolitem_make_custom,
 1571             (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_CUSTOM), NULL) != NULL
 1572                             && ! build_running));
 1573     if (widgets.toolitem_make_object != NULL)
 1574         gtk_widget_set_sensitive(widgets.toolitem_make_object,
 1575             (can_make |= get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT), NULL) != NULL
 1576                             && ! build_running));
 1577     if (widgets.toolitem_set_args != NULL)
 1578         gtk_widget_set_sensitive(widgets.toolitem_set_args, TRUE);
 1579 
 1580     can_compile = get_build_cmd(doc, GEANY_GBG_FT, GBO_TO_CMD(GEANY_GBO_COMPILE), NULL) != NULL
 1581                     && have_path && ! build_running;
 1582     gtk_action_set_sensitive(widgets.compile_action, can_compile);
 1583     gtk_action_set_sensitive(widgets.build_action, can_make);
 1584     gtk_action_set_sensitive(widgets.run_action, run_sensitivity);
 1585 
 1586     /* show the stop command if a program is running from execute 0 , otherwise show run command */
 1587     set_stop_button(run_running);
 1588 
 1589 }
 1590 
 1591 
 1592 /* Call build_menu_update() instead of calling this directly. */
 1593 static void set_stop_button(gboolean stop)
 1594 {
 1595     const gchar *button_stock_id = NULL;
 1596     GtkToolButton *run_button;
 1597 
 1598     run_button = GTK_TOOL_BUTTON(toolbar_get_widget_by_name("Run"));
 1599     if (run_button != NULL)
 1600         button_stock_id = gtk_tool_button_get_stock_id(run_button);
 1601 
 1602     if (stop && utils_str_equal(button_stock_id, GTK_STOCK_STOP))
 1603         return;
 1604     if (! stop && utils_str_equal(button_stock_id, GTK_STOCK_EXECUTE))
 1605         return;
 1606 
 1607      /* use the run button also as stop button  */
 1608     if (stop)
 1609     {
 1610         if (run_button != NULL)
 1611             gtk_tool_button_set_stock_id(run_button, GTK_STOCK_STOP);
 1612     }
 1613     else
 1614     {
 1615         if (run_button != NULL)
 1616             gtk_tool_button_set_stock_id(run_button, GTK_STOCK_EXECUTE);
 1617     }
 1618 }
 1619 
 1620 
 1621 static void on_set_build_commands_activate(GtkWidget *w, gpointer u)
 1622 {
 1623     /* For now, just show the project dialog */
 1624     if (app->project)
 1625         project_build_properties();
 1626     else
 1627         show_build_commands_dialog();
 1628 }
 1629 
 1630 
 1631 static void on_toolbutton_build_activate(GtkWidget *menuitem, gpointer user_data)
 1632 {
 1633     last_toolbutton_action = user_data;
 1634     g_object_set(widgets.build_action, "tooltip", _("Build the current file"), NULL);
 1635     on_build_menu_item(menuitem, user_data);
 1636 }
 1637 
 1638 
 1639 static void on_toolbutton_make_activate(GtkWidget *menuitem, gpointer user_data)
 1640 {
 1641     gchar *msg;
 1642 
 1643     last_toolbutton_action = user_data;
 1644     if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_ALL))
 1645             msg = _("Build the current file with Make and the default target");
 1646     else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_CUSTOM))
 1647             msg = _("Build the current file with Make and the specified target");
 1648     else if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT))
 1649             msg = _("Compile the current file with Make");
 1650     else
 1651             msg = NULL;
 1652     g_object_set(widgets.build_action, "tooltip", msg, NULL);
 1653     on_build_menu_item(menuitem, user_data);
 1654 }
 1655 
 1656 
 1657 static void kill_process(GPid *pid)
 1658 {
 1659     GError *error = NULL;
 1660 
 1661     if (spawn_kill_process(*pid, &error))
 1662     {
 1663         *pid = 0;
 1664         build_menu_update(NULL);
 1665     }
 1666     else
 1667     {
 1668         ui_set_statusbar(TRUE, _("Process could not be stopped (%s)."), error->message);
 1669         g_error_free(error);
 1670     }
 1671 }
 1672 
 1673 
 1674 static void on_build_next_error(GtkWidget *menuitem, gpointer user_data)
 1675 {
 1676     if (ui_tree_view_find_next(GTK_TREE_VIEW(msgwindow.tree_compiler),
 1677         msgwin_goto_compiler_file_line))
 1678     {
 1679         gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
 1680     }
 1681     else
 1682         ui_set_statusbar(FALSE, _("No more build errors."));
 1683 }
 1684 
 1685 
 1686 static void on_build_previous_error(GtkWidget *menuitem, gpointer user_data)
 1687 {
 1688     if (ui_tree_view_find_previous(GTK_TREE_VIEW(msgwindow.tree_compiler),
 1689         msgwin_goto_compiler_file_line))
 1690     {
 1691         gtk_notebook_set_current_page(GTK_NOTEBOOK(msgwindow.notebook), MSG_COMPILER);
 1692     }
 1693     else
 1694         ui_set_statusbar(FALSE, _("No more build errors."));
 1695 }
 1696 
 1697 
 1698 void build_toolbutton_build_clicked(GtkAction *action, gpointer unused)
 1699 {
 1700     if (last_toolbutton_action == GBO_TO_POINTER(GEANY_GBO_BUILD))
 1701     {
 1702         on_build_menu_item(NULL, GBO_TO_POINTER(GEANY_GBO_BUILD));
 1703     }
 1704     else
 1705     {
 1706         on_build_menu_item(NULL, last_toolbutton_action);
 1707     }
 1708 }
 1709 
 1710 
 1711 /*------------------------------------------------------
 1712  *
 1713  * Create and handle the build menu configuration dialog
 1714  *
 1715  *-------------------------------------------------------*/
 1716 typedef struct RowWidgets
 1717 {
 1718     GtkWidget *entries[GEANY_BC_CMDENTRIES_COUNT];
 1719     GeanyBuildSource src;
 1720     GeanyBuildSource dst;
 1721     GeanyBuildCommand *cmdsrc;
 1722     guint grp;
 1723     guint cmd;
 1724     gboolean cleared;
 1725     gboolean used_dst;
 1726 } RowWidgets;
 1727 
 1728 #if GTK_CHECK_VERSION(3,0,0)
 1729 typedef GdkRGBA InsensitiveColor;
 1730 #else
 1731 typedef GdkColor InsensitiveColor;
 1732 #endif
 1733 static InsensitiveColor insensitive_color;
 1734 
 1735 static void set_row_color(RowWidgets *r, InsensitiveColor *color)
 1736 {
 1737     enum GeanyBuildCmdEntries i;
 1738 
 1739     for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 1740     {
 1741         if (i == GEANY_BC_LABEL)
 1742             continue;
 1743 
 1744 #if GTK_CHECK_VERSION(3,0,0)
 1745         gtk_widget_override_color(r->entries[i], GTK_STATE_FLAG_NORMAL, color);
 1746 #else
 1747         gtk_widget_modify_text(r->entries[i], GTK_STATE_NORMAL, color);
 1748 #endif
 1749     }
 1750 }
 1751 
 1752 
 1753 static void set_build_command_entry_text(GtkWidget *wid, const gchar *text)
 1754 {
 1755     if (GTK_IS_BUTTON(wid))
 1756         gtk_button_set_label(GTK_BUTTON(wid), text);
 1757     else
 1758         gtk_entry_set_text(GTK_ENTRY(wid), text);
 1759 }
 1760 
 1761 
 1762 static void on_clear_dialog_row(GtkWidget *unused, gpointer user_data)
 1763 {
 1764     RowWidgets *r = user_data;
 1765     guint src;
 1766     enum GeanyBuildCmdEntries i;
 1767     GeanyBuildCommand *bc = get_next_build_cmd(NULL, r->grp, r->cmd, r->dst, &src);
 1768 
 1769     if (bc != NULL)
 1770     {
 1771         r->cmdsrc = bc;
 1772         r->src = src;
 1773         for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 1774         {
 1775             set_build_command_entry_text(r->entries[i],
 1776                 id_to_str(bc,i) != NULL ? id_to_str(bc,i) : "");
 1777         }
 1778     }
 1779     else
 1780     {
 1781         r->cmdsrc = NULL;
 1782         for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 1783         {
 1784             set_build_command_entry_text(r->entries[i], "");
 1785         }
 1786     }
 1787     r->used_dst = FALSE;
 1788     set_row_color(r, &insensitive_color);
 1789     r->cleared = TRUE;
 1790 }
 1791 
 1792 
 1793 static void on_clear_dialog_regex_row(GtkEntry *regex, gpointer unused)
 1794 {
 1795     gtk_entry_set_text(regex,"");
 1796 }
 1797 
 1798 
 1799 static void on_label_button_clicked(GtkWidget *wid, gpointer user_data)
 1800 {
 1801     RowWidgets *r = user_data;
 1802     GtkWidget *top_level = gtk_widget_get_toplevel(wid);
 1803     const gchar *old = gtk_button_get_label(GTK_BUTTON(wid));
 1804     gchar *str;
 1805 
 1806     if (gtk_widget_is_toplevel(top_level) && GTK_IS_WINDOW(top_level))
 1807         str = dialogs_show_input(_("Set menu item label"), GTK_WINDOW(top_level), NULL, old);
 1808     else
 1809         str = dialogs_show_input(_("Set menu item label"), NULL, NULL, old);
 1810 
 1811     if (!str)
 1812         return;
 1813 
 1814     gtk_button_set_label(GTK_BUTTON(wid), str);
 1815     g_free(str);
 1816     r->used_dst = TRUE;
 1817     set_row_color(r, NULL);
 1818 }
 1819 
 1820 
 1821 static void on_entry_focus(GtkWidget *wid, GdkEventFocus *unused, gpointer user_data)
 1822 {
 1823     RowWidgets *r = user_data;
 1824 
 1825     r->used_dst = TRUE;
 1826     set_row_color(r, NULL);
 1827 }
 1828 
 1829 
 1830 /* Column headings, array NULL-terminated */
 1831 static const gchar *colheads[] =
 1832 {
 1833     "#",
 1834     N_("Label"),
 1835     N_("Command"),
 1836     N_("Working directory"),
 1837     N_("Reset"),
 1838     NULL
 1839 };
 1840 
 1841 /* column names */
 1842 #define DC_ITEM 0
 1843 #define DC_ENTRIES 1
 1844 #define DC_CLEAR 4
 1845 #define DC_N_COL 5
 1846 
 1847 static const guint entry_x_padding = 3;
 1848 static const guint entry_y_padding = 0;
 1849 
 1850 
 1851 static RowWidgets *build_add_dialog_row(GeanyDocument *doc, GtkTable *table, guint row,
 1852                 GeanyBuildSource dst, guint grp, guint cmd, gboolean dir)
 1853 {
 1854     GtkWidget *label, *clear, *clearicon;
 1855     RowWidgets *roww;
 1856     GeanyBuildCommand *bc;
 1857     guint src;
 1858     enum GeanyBuildCmdEntries i;
 1859     guint column = 0;
 1860     gchar *text;
 1861 
 1862     g_return_val_if_fail(doc == NULL || doc->is_valid, NULL);
 1863 
 1864     text = g_strdup_printf("%d.", cmd + 1);
 1865     label = gtk_label_new(text);
 1866     g_free(text);
 1867 #if GTK_CHECK_VERSION(3,0,0)
 1868 {
 1869     GtkStyleContext *ctx = gtk_widget_get_style_context(label);
 1870 
 1871     gtk_style_context_save(ctx);
 1872     gtk_style_context_get_color(ctx, GTK_STATE_FLAG_INSENSITIVE, &insensitive_color);
 1873     gtk_style_context_restore(ctx);
 1874 }
 1875 #else
 1876     insensitive_color = gtk_widget_get_style(label)->text[GTK_STATE_INSENSITIVE];
 1877 #endif
 1878     gtk_table_attach(table, label, column, column + 1, row, row + 1, GTK_FILL,
 1879         GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 1880     roww = g_new0(RowWidgets, 1);
 1881     roww->src = GEANY_BCS_COUNT;
 1882     roww->grp = grp;
 1883     roww->cmd = cmd;
 1884     roww->dst = dst;
 1885     for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 1886     {
 1887         gint xflags = (i == GEANY_BC_COMMAND) ? GTK_FILL | GTK_EXPAND : GTK_FILL;
 1888 
 1889         column += 1;
 1890         if (i == GEANY_BC_LABEL)
 1891         {
 1892             GtkWidget *wid = roww->entries[i] = gtk_button_new();
 1893             gtk_button_set_use_underline(GTK_BUTTON(wid), TRUE);
 1894             gtk_widget_set_tooltip_text(wid, _("Click to set menu item label"));
 1895             g_signal_connect(wid, "clicked", G_CALLBACK(on_label_button_clicked), roww);
 1896         }
 1897         else
 1898         {
 1899             roww->entries[i] = gtk_entry_new();
 1900             g_signal_connect(roww->entries[i], "focus-in-event", G_CALLBACK(on_entry_focus), roww);
 1901         }
 1902         gtk_table_attach(table, roww->entries[i], column, column + 1, row, row + 1, xflags,
 1903             GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 1904     }
 1905     column++;
 1906     clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
 1907     clear = gtk_button_new();
 1908     gtk_button_set_image(GTK_BUTTON(clear), clearicon);
 1909     g_signal_connect(clear, "clicked", G_CALLBACK(on_clear_dialog_row), roww);
 1910     gtk_table_attach(table, clear, column, column + 1, row, row + 1, GTK_FILL,
 1911         GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 1912     roww->cmdsrc = bc = get_build_cmd(doc, grp, cmd, &src);
 1913     if (bc != NULL)
 1914         roww->src = src;
 1915 
 1916     for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 1917     {
 1918         const gchar *str = "";
 1919 
 1920         if (bc != NULL )
 1921         {
 1922             if ((str = id_to_str(bc, i)) == NULL)
 1923                 str = "";
 1924             else if (dst == src)
 1925                 roww->used_dst = TRUE;
 1926         }
 1927         set_build_command_entry_text(roww->entries[i], str);
 1928     }
 1929     if (bc != NULL && (dst > src))
 1930         set_row_color(roww, &insensitive_color);
 1931     if (bc != NULL && (src > dst || (grp == GEANY_GBG_FT && (doc == NULL || doc->file_type == NULL))))
 1932     {
 1933         for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 1934             gtk_widget_set_sensitive(roww->entries[i], FALSE);
 1935         gtk_widget_set_sensitive(clear, FALSE);
 1936     }
 1937     return roww;
 1938 }
 1939 
 1940 
 1941 typedef struct BuildTableFields
 1942 {
 1943     RowWidgets  **rows;
 1944     GtkWidget    *fileregex;
 1945     GtkWidget    *nonfileregex;
 1946     gchar       **fileregexstring;
 1947     gchar       **nonfileregexstring;
 1948 } BuildTableFields;
 1949 
 1950 
 1951 GtkWidget *build_commands_table(GeanyDocument *doc, GeanyBuildSource dst, BuildTableData *table_data,
 1952                                 GeanyFiletype *ft)
 1953 {
 1954     GtkWidget *label, *sep, *clearicon, *clear;
 1955     BuildTableFields *fields;
 1956     GtkTable *table;
 1957     const gchar **ch;
 1958     gchar *txt;
 1959     guint col, row, cmdindex;
 1960     guint cmd;
 1961     guint src;
 1962     gboolean sensitivity;
 1963     guint sep_padding = entry_y_padding + 3;
 1964 
 1965     table = GTK_TABLE(gtk_table_new(build_items_count + 12, 5, FALSE));
 1966     fields = g_new0(BuildTableFields, 1);
 1967     fields->rows = g_new0(RowWidgets*, build_items_count);
 1968     for (ch = colheads, col = 0; *ch != NULL; ch++, col++)
 1969     {
 1970         label = gtk_label_new(_(*ch));
 1971         gtk_table_attach(table, label, col, col + 1, 0, 1,
 1972             GTK_FILL, GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 1973     }
 1974     sep = gtk_hseparator_new();
 1975     gtk_table_attach(table, sep, 0, DC_N_COL, 1, 2, GTK_FILL, GTK_FILL | GTK_EXPAND,
 1976         entry_x_padding, sep_padding);
 1977     if (ft != NULL && ft->id != GEANY_FILETYPES_NONE)
 1978         txt = g_strdup_printf(_("%s commands"), ft->name);
 1979     else
 1980         txt = g_strdup_printf(_("%s commands"), _("No filetype"));
 1981 
 1982     label = ui_label_new_bold(txt);
 1983     g_free(txt);
 1984     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 1985     gtk_table_attach(table, label, 0, DC_N_COL, 2, 3, GTK_FILL, GTK_FILL | GTK_EXPAND,
 1986         entry_x_padding, entry_y_padding);
 1987     for (row = 3, cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++row, ++cmdindex, ++cmd)
 1988         fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_FT, cmd, FALSE);
 1989     label = gtk_label_new(_("Error regular expression:"));
 1990     gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 1991         entry_x_padding, entry_y_padding);
 1992     fields->fileregex = gtk_entry_new();
 1993     fields->fileregexstring = build_get_regex(GEANY_GBG_FT, NULL, &src);
 1994     sensitivity = (ft == NULL) ? FALSE : TRUE;
 1995     if (fields->fileregexstring != NULL && *(fields->fileregexstring) != NULL)
 1996     {
 1997         gtk_entry_set_text(GTK_ENTRY(fields->fileregex), *(fields->fileregexstring));
 1998         if (src > dst)
 1999             sensitivity = FALSE;
 2000     }
 2001     gtk_table_attach(table, fields->fileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
 2002         GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 2003     clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
 2004     clear = gtk_button_new();
 2005     gtk_button_set_image(GTK_BUTTON(clear), clearicon);
 2006     g_signal_connect_swapped(clear, "clicked",
 2007         G_CALLBACK(on_clear_dialog_regex_row), (fields->fileregex));
 2008     gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
 2009         GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 2010     gtk_widget_set_sensitive(fields->fileregex, sensitivity);
 2011     gtk_widget_set_sensitive(clear, sensitivity);
 2012     ++row;
 2013     sep = gtk_hseparator_new();
 2014     gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 2015         entry_x_padding, sep_padding);
 2016     ++row;
 2017     label = ui_label_new_bold(_("Independent commands"));
 2018     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 2019     gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 2020         entry_x_padding, entry_y_padding);
 2021     for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++row, ++cmdindex, ++cmd)
 2022         fields->rows[cmdindex] = build_add_dialog_row(
 2023             doc, table, row, dst, GEANY_GBG_NON_FT, cmd, TRUE);
 2024     label = gtk_label_new(_("Error regular expression:"));
 2025     gtk_table_attach(table, label, 0, DC_ENTRIES + 1, row, row + 1, GTK_FILL,
 2026         GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 2027     fields->nonfileregex = gtk_entry_new();
 2028     fields->nonfileregexstring = build_get_regex(GEANY_GBG_NON_FT, NULL, &src);
 2029     sensitivity = TRUE;
 2030     if (fields->nonfileregexstring != NULL && *(fields->nonfileregexstring) != NULL)
 2031     {
 2032         gtk_entry_set_text(GTK_ENTRY(fields->nonfileregex), *(fields->nonfileregexstring));
 2033         sensitivity = src > dst ? FALSE : TRUE;
 2034     }
 2035     gtk_table_attach(table, fields->nonfileregex, DC_ENTRIES + 1, DC_CLEAR, row, row + 1, GTK_FILL,
 2036         GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 2037     clearicon = gtk_image_new_from_stock(GTK_STOCK_CLEAR, GTK_ICON_SIZE_MENU);
 2038     clear = gtk_button_new();
 2039     gtk_button_set_image(GTK_BUTTON(clear), clearicon);
 2040     g_signal_connect_swapped(clear, "clicked",
 2041         G_CALLBACK(on_clear_dialog_regex_row), (fields->nonfileregex));
 2042     gtk_table_attach(table, clear, DC_CLEAR, DC_CLEAR + 1, row, row + 1, GTK_FILL,
 2043         GTK_FILL | GTK_EXPAND, entry_x_padding, entry_y_padding);
 2044     gtk_widget_set_sensitive(fields->nonfileregex, sensitivity);
 2045     gtk_widget_set_sensitive(clear, sensitivity);
 2046     ++row;
 2047     label = gtk_label_new(NULL);
 2048     ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
 2049         _("Note: Item 2 opens a dialog and appends the response to the command."));
 2050     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 2051     gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 2052         entry_x_padding, entry_y_padding);
 2053     ++row;
 2054     sep = gtk_hseparator_new();
 2055     gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 2056         entry_x_padding, sep_padding);
 2057     ++row;
 2058     label = ui_label_new_bold(_("Execute commands"));
 2059     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 2060     gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 2061         entry_x_padding, entry_y_padding);
 2062     for (++row, cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++row, ++cmdindex, ++cmd)
 2063         fields->rows[cmdindex] = build_add_dialog_row(doc, table, row, dst, GEANY_GBG_EXEC, cmd, TRUE);
 2064     sep = gtk_hseparator_new();
 2065     gtk_table_attach(table, sep, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 2066         entry_x_padding, sep_padding);
 2067     ++row;
 2068     label = gtk_label_new(NULL);
 2069     ui_label_set_markup(GTK_LABEL(label), "<i>%s</i>",
 2070         _("%d, %e, %f, %p, %l are substituted in command and directory fields, see manual for details."));
 2071     gtk_misc_set_alignment(GTK_MISC(label), 0.0, 0.5);
 2072     gtk_table_attach(table, label, 0, DC_N_COL, row, row + 1, GTK_FILL, GTK_FILL | GTK_EXPAND,
 2073         entry_x_padding, entry_y_padding);
 2074     /*printf("%d extra rows in dialog\n", row-build_items_count);*/
 2075     ++row;
 2076     *table_data = fields;
 2077     return GTK_WIDGET(table);
 2078 }
 2079 
 2080 
 2081 void build_free_fields(BuildTableData table_data)
 2082 {
 2083     guint cmdindex;
 2084 
 2085     for (cmdindex = 0; cmdindex < build_items_count; ++cmdindex)
 2086         g_free(table_data->rows[cmdindex]);
 2087     g_free(table_data->rows);
 2088     g_free(table_data);
 2089 }
 2090 
 2091 
 2092 /* string compare where null pointers match null or 0 length strings */
 2093 #if 0
 2094 static gint stcmp(const gchar *a, const gchar *b)
 2095 {
 2096     if (a == NULL && b == NULL)
 2097         return 0;
 2098     if (a == NULL && b != NULL)
 2099         return strlen(b);
 2100     if (a != NULL && b == NULL)
 2101         return strlen(a);
 2102     return strcmp(a, b);
 2103 }
 2104 #endif
 2105 
 2106 
 2107 static const gchar *get_build_command_entry_text(GtkWidget *wid)
 2108 {
 2109     if (GTK_IS_BUTTON(wid))
 2110         return gtk_button_get_label(GTK_BUTTON(wid));
 2111     else
 2112         return gtk_entry_get_text(GTK_ENTRY(wid));
 2113 }
 2114 
 2115 
 2116 static gboolean read_row(BuildDestination *dst, BuildTableData table_data, guint drow, guint grp, guint cmd)
 2117 {
 2118     gchar *entries[GEANY_BC_CMDENTRIES_COUNT];
 2119     gboolean changed = FALSE;
 2120     enum GeanyBuildCmdEntries i;
 2121 
 2122     for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 2123     {
 2124         entries[i] = g_strdup(get_build_command_entry_text(table_data->rows[drow]->entries[i]));
 2125     }
 2126     if (table_data->rows[drow]->cleared)
 2127     {
 2128         if (dst->dst[grp] != NULL)
 2129         {
 2130             if (*(dst->dst[grp]) == NULL)
 2131                 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
 2132             (*(dst->dst[grp]))[cmd].exists = FALSE;
 2133             (*(dst->dst[grp]))[cmd].changed = TRUE;
 2134             changed = TRUE;
 2135         }
 2136     }
 2137     if (table_data->rows[drow]->used_dst == TRUE)
 2138     {
 2139         if (dst->dst[grp] != NULL)
 2140         {
 2141             if (*(dst->dst[grp]) == NULL)
 2142                 *(dst->dst[grp]) = g_new0(GeanyBuildCommand, build_groups_count[grp]);
 2143             for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 2144                 set_command(&(*(dst->dst[grp]))[cmd], i, entries[i]);
 2145             (*(dst->dst[grp]))[cmd].exists = TRUE;
 2146             (*(dst->dst[grp]))[cmd].changed = TRUE;
 2147             changed = TRUE;
 2148         }
 2149     }
 2150     else
 2151     {
 2152         for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 2153             g_free(entries[i]);
 2154     }
 2155     return changed;
 2156 }
 2157 
 2158 
 2159 static gboolean read_regex(GtkWidget *regexentry, gchar **src, gchar **dst)
 2160 {
 2161     gboolean changed = FALSE;
 2162     const gchar *reg = gtk_entry_get_text(GTK_ENTRY(regexentry));
 2163 
 2164     if (((src == NULL           /* originally there was no regex */
 2165         || *src == NULL)        /* or it was NULL*/
 2166         && !EMPTY(reg))         /* and something was typed */
 2167         || (src != NULL         /* originally there was a regex*/
 2168         && (*src == NULL        /* and either it was NULL */
 2169         || strcmp(*src, reg) != 0)))    /* or it has been changed */
 2170     {
 2171         if (dst != NULL)
 2172         {
 2173             SETPTR(*dst, g_strdup(reg));
 2174             changed = TRUE;
 2175         }
 2176     }
 2177     return changed;
 2178 }
 2179 
 2180 
 2181 static gboolean build_read_commands(BuildDestination *dst, BuildTableData table_data, gint response)
 2182 {
 2183     guint cmdindex, cmd;
 2184     gboolean changed = FALSE;
 2185 
 2186     if (response == GTK_RESPONSE_ACCEPT)
 2187     {
 2188         for (cmdindex = 0, cmd = 0; cmd < build_groups_count[GEANY_GBG_FT]; ++cmdindex, ++cmd)
 2189             changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_FT, cmd);
 2190         for (cmd = 0; cmd < build_groups_count[GEANY_GBG_NON_FT]; ++cmdindex, ++cmd)
 2191             changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_NON_FT, cmd);
 2192         for (cmd = 0; cmd < build_groups_count[GEANY_GBG_EXEC]; ++cmdindex, ++cmd)
 2193             changed |= read_row(dst, table_data, cmdindex, GEANY_GBG_EXEC, cmd);
 2194         changed |= read_regex(table_data->fileregex, table_data->fileregexstring, dst->fileregexstr);
 2195         changed |= read_regex(table_data->nonfileregex, table_data->nonfileregexstring, dst->nonfileregexstr);
 2196     }
 2197     return changed;
 2198 }
 2199 
 2200 
 2201 void build_read_project(GeanyFiletype *ft, BuildTableData build_properties)
 2202 {
 2203     BuildDestination menu_dst;
 2204 
 2205     if (ft != NULL)
 2206     {
 2207         menu_dst.dst[GEANY_GBG_FT] = &(ft->priv->projfilecmds);
 2208         menu_dst.fileregexstr = &(ft->priv->projerror_regex_string);
 2209     }
 2210     else
 2211     {
 2212         menu_dst.dst[GEANY_GBG_FT] = NULL;
 2213         menu_dst.fileregexstr = NULL;
 2214     }
 2215     menu_dst.dst[GEANY_GBG_NON_FT] = &non_ft_proj;
 2216     menu_dst.dst[GEANY_GBG_EXEC] = &exec_proj;
 2217     menu_dst.nonfileregexstr = &regex_proj;
 2218 
 2219     build_read_commands(&menu_dst, build_properties, GTK_RESPONSE_ACCEPT);
 2220 }
 2221 
 2222 
 2223 static void show_build_commands_dialog(void)
 2224 {
 2225     GtkWidget *dialog, *table, *vbox;
 2226     GeanyDocument *doc = document_get_current();
 2227     GeanyFiletype *ft = NULL;
 2228     const gchar *title = _("Set Build Commands");
 2229     gint response;
 2230     BuildTableData table_data;
 2231     BuildDestination prefdsts;
 2232 
 2233     if (doc != NULL)
 2234         ft = doc->file_type;
 2235     dialog = gtk_dialog_new_with_buttons(title, GTK_WINDOW(main_widgets.window),
 2236                                         GTK_DIALOG_DESTROY_WITH_PARENT,
 2237                                         GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
 2238                                         GTK_STOCK_OK, GTK_RESPONSE_ACCEPT, NULL);
 2239     table = build_commands_table(doc, GEANY_BCS_PREF, &table_data, ft);
 2240     vbox = ui_dialog_vbox_new(GTK_DIALOG(dialog));
 2241     gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 0);
 2242     gtk_widget_show_all(dialog);
 2243     /* run modally to prevent user changing idx filetype */
 2244     response = gtk_dialog_run(GTK_DIALOG(dialog));
 2245 
 2246     prefdsts.dst[GEANY_GBG_NON_FT] = &non_ft_pref;
 2247     if (ft != NULL)
 2248     {
 2249         prefdsts.dst[GEANY_GBG_FT] = &(ft->priv->homefilecmds);
 2250         prefdsts.fileregexstr = &(ft->priv->homeerror_regex_string);
 2251         prefdsts.dst[GEANY_GBG_EXEC] = &(ft->priv->homeexeccmds);
 2252     }
 2253     else
 2254     {
 2255         prefdsts.dst[GEANY_GBG_FT] = NULL;
 2256         prefdsts.fileregexstr = NULL;
 2257         prefdsts.dst[GEANY_GBG_EXEC] = NULL;
 2258     }
 2259     prefdsts.nonfileregexstr = &regex_pref;
 2260     if (build_read_commands(&prefdsts, table_data, response) && ft != NULL)
 2261         filetypes_save_commands(ft);
 2262     build_free_fields(table_data);
 2263 
 2264     build_menu_update(doc);
 2265     gtk_widget_destroy(dialog);
 2266 }
 2267 
 2268 
 2269 /* Creates the relevant build menu if necessary. */
 2270 BuildMenuItems *build_get_menu_items(gint filetype_idx)
 2271 {
 2272     BuildMenuItems *items;
 2273 
 2274     items = &menu_items;
 2275     if (items->menu == NULL)
 2276         create_build_menu(items);
 2277     return items;
 2278 }
 2279 
 2280 
 2281 /*----------------------------------------------------------
 2282  *
 2283  * Load and store configuration
 2284  *
 2285  * ---------------------------------------------------------*/
 2286 static const gchar *build_grp_name = "build-menu";
 2287 
 2288 /* config format for build-menu group is prefix_gg_nn_xx=value
 2289  * where gg = FT, NF, EX for the command group
 2290  *       nn = 2 digit command number
 2291  *       xx = LB for label, CM for command and WD for working dir */
 2292 static const gchar *groups[GEANY_GBG_COUNT] = { "FT", "NF", "EX" };
 2293 static const gchar *fixedkey="xx_xx_xx";
 2294 
 2295 #define set_key_grp(key, grp) (key[prefixlen + 0] = grp[0], key[prefixlen + 1] = grp[1])
 2296 #define set_key_cmd(key, cmd) (key[prefixlen + 3] = cmd[0], key[prefixlen + 4] = cmd[1])
 2297 #define set_key_fld(key, fld) (key[prefixlen + 6] = fld[0], key[prefixlen + 7] = fld[1])
 2298 
 2299 static void build_load_menu_grp(GKeyFile *config, GeanyBuildCommand **dst, gint grp,
 2300                                 gchar *prefix, gboolean loc)
 2301 {
 2302     guint cmd;
 2303     gsize prefixlen; /* NOTE prefixlen used in macros above */
 2304     GeanyBuildCommand *dstcmd;
 2305     gchar *key;
 2306     static gchar cmdbuf[4] = "  ";
 2307 
 2308     if (*dst == NULL)
 2309         *dst = g_new0(GeanyBuildCommand, build_groups_count[grp]);
 2310     dstcmd = *dst;
 2311     prefixlen = prefix == NULL ? 0 : strlen(prefix);
 2312     key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
 2313     for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
 2314     {
 2315         gchar *label;
 2316         if (cmd >= 100)
 2317             break; /* ensure no buffer overflow */
 2318         sprintf(cmdbuf, "%02u", cmd);
 2319         set_key_grp(key, groups[grp]);
 2320         set_key_cmd(key, cmdbuf);
 2321         set_key_fld(key, "LB");
 2322         if (loc)
 2323             label = g_key_file_get_locale_string(config, build_grp_name, key, NULL, NULL);
 2324         else
 2325             label = g_key_file_get_string(config, build_grp_name, key, NULL);
 2326         if (label != NULL)
 2327         {
 2328             dstcmd[cmd].exists = TRUE;
 2329             SETPTR(dstcmd[cmd].label, label);
 2330             set_key_fld(key,"CM");
 2331             SETPTR(dstcmd[cmd].command,
 2332                     g_key_file_get_string(config, build_grp_name, key, NULL));
 2333             set_key_fld(key,"WD");
 2334             SETPTR(dstcmd[cmd].working_dir,
 2335                     g_key_file_get_string(config, build_grp_name, key, NULL));
 2336         }
 2337         else dstcmd[cmd].exists = FALSE;
 2338     }
 2339     g_free(key);
 2340 }
 2341 
 2342 
 2343 /* set GeanyBuildCommand if it doesn't already exist and there is a command */
 2344 static void assign_cmd(GeanyBuildCommand *type, guint id,
 2345         const gchar *label, gchar *value)
 2346 {
 2347     if (!EMPTY(value) && ! type[GBO_TO_CMD(id)].exists)
 2348     {
 2349         type[GBO_TO_CMD(id)].exists = TRUE;
 2350         SETPTR(type[GBO_TO_CMD(id)].label, g_strdup(label));
 2351         SETPTR(type[GBO_TO_CMD(id)].command, value);
 2352         SETPTR(type[GBO_TO_CMD(id)].working_dir, NULL);
 2353         type[GBO_TO_CMD(id)].old = TRUE;
 2354     }
 2355     else
 2356         g_free(value);
 2357 }
 2358 
 2359 /* for the specified source load new format build menu items or try to make some sense of
 2360  * old format setings, not done perfectly but better than ignoring them */
 2361 void build_load_menu(GKeyFile *config, GeanyBuildSource src, gpointer p)
 2362 {
 2363     GeanyFiletype *ft;
 2364     GeanyProject *pj;
 2365     gchar **ftlist;
 2366     gchar *value, *basedir, *makebasedir;
 2367     gboolean bvalue = FALSE;
 2368 
 2369     if (g_key_file_has_group(config, build_grp_name))
 2370     {
 2371         switch (src)
 2372         {
 2373             case GEANY_BCS_FT:
 2374                 ft = (GeanyFiletype*)p;
 2375                 if (ft == NULL)
 2376                     return;
 2377                 build_load_menu_grp(config, &(ft->priv->filecmds), GEANY_GBG_FT, NULL, TRUE);
 2378                 build_load_menu_grp(config, &(ft->priv->ftdefcmds), GEANY_GBG_NON_FT, NULL, TRUE);
 2379                 build_load_menu_grp(config, &(ft->priv->execcmds), GEANY_GBG_EXEC, NULL, TRUE);
 2380                 SETPTR(ft->error_regex_string,
 2381                         g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
 2382                 break;
 2383             case GEANY_BCS_HOME_FT:
 2384                 ft = (GeanyFiletype*)p;
 2385                 if (ft == NULL)
 2386                     return;
 2387                 build_load_menu_grp(config, &(ft->priv->homefilecmds), GEANY_GBG_FT, NULL, FALSE);
 2388                 build_load_menu_grp(config, &(ft->priv->homeexeccmds), GEANY_GBG_EXEC, NULL, FALSE);
 2389                 SETPTR(ft->priv->homeerror_regex_string,
 2390                         g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
 2391                 break;
 2392             case GEANY_BCS_PREF:
 2393                 build_load_menu_grp(config, &non_ft_pref, GEANY_GBG_NON_FT, NULL, FALSE);
 2394                 build_load_menu_grp(config, &exec_pref, GEANY_GBG_EXEC, NULL, FALSE);
 2395                 SETPTR(regex_pref, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
 2396                 break;
 2397             case GEANY_BCS_PROJ:
 2398                 build_load_menu_grp(config, &non_ft_proj, GEANY_GBG_NON_FT, NULL, FALSE);
 2399                 build_load_menu_grp(config, &exec_proj, GEANY_GBG_EXEC, NULL, FALSE);
 2400                 SETPTR(regex_proj, g_key_file_get_string(config, build_grp_name, "error_regex", NULL));
 2401                 pj = (GeanyProject*)p;
 2402                 if (p == NULL)
 2403                     return;
 2404                 ftlist = g_key_file_get_string_list(config, build_grp_name, "filetypes", NULL, NULL);
 2405                 if (ftlist != NULL)
 2406                 {
 2407                     gchar **ftname;
 2408                     if (pj->priv->build_filetypes_list == NULL)
 2409                         pj->priv->build_filetypes_list = g_ptr_array_new();
 2410                     g_ptr_array_set_size(pj->priv->build_filetypes_list, 0);
 2411                     for (ftname = ftlist; *ftname != NULL; ++ftname)
 2412                     {
 2413                         ft = filetypes_lookup_by_name(*ftname);
 2414                         if (ft != NULL)
 2415                         {
 2416                             gchar *regkey = g_strdup_printf("%serror_regex", *ftname);
 2417                             g_ptr_array_add(pj->priv->build_filetypes_list, ft);
 2418                             SETPTR(ft->priv->projerror_regex_string,
 2419                                     g_key_file_get_string(config, build_grp_name, regkey, NULL));
 2420                             g_free(regkey);
 2421                             build_load_menu_grp(config, &(ft->priv->projfilecmds), GEANY_GBG_FT, *ftname, FALSE);
 2422                             build_load_menu_grp(config, &(ft->priv->projexeccmds), GEANY_GBG_EXEC, *ftname, FALSE);
 2423                         }
 2424                     }
 2425                     g_free(ftlist);
 2426                 }
 2427                 break;
 2428             default: /* defaults don't load from config, see build_init */
 2429                 break;
 2430         }
 2431     }
 2432 
 2433     /* load old [build_settings] values if there is no value defined by [build-menu] */
 2434 
 2435     switch (src)
 2436     {
 2437         case GEANY_BCS_FT:
 2438             ft = (GeanyFiletype*)p;
 2439             value = g_key_file_get_string(config, "build_settings", "compiler", NULL);
 2440             if (value != NULL)
 2441             {
 2442                 if (ft->priv->filecmds == NULL)
 2443                     ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
 2444                 assign_cmd(ft->priv->filecmds, GEANY_GBO_COMPILE, _("_Compile"), value);
 2445             }
 2446             value = g_key_file_get_string(config, "build_settings", "linker", NULL);
 2447             if (value != NULL)
 2448             {
 2449                 if (ft->priv->filecmds == NULL)
 2450                     ft->priv->filecmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
 2451                 assign_cmd(ft->priv->filecmds, GEANY_GBO_BUILD, _("_Build"), value);
 2452             }
 2453             value = g_key_file_get_string(config, "build_settings", "run_cmd", NULL);
 2454             if (value != NULL)
 2455             {
 2456                 if (ft->priv->execcmds == NULL)
 2457                     ft->priv->execcmds = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
 2458                 assign_cmd(ft->priv->execcmds, GEANY_GBO_EXEC, _("_Execute"), value);
 2459             }
 2460             if (ft->error_regex_string == NULL)
 2461                 ft->error_regex_string = g_key_file_get_string(config, "build_settings", "error_regex", NULL);
 2462             break;
 2463         case GEANY_BCS_PROJ:
 2464             if (non_ft_pref == NULL)
 2465                 non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
 2466             basedir = project_get_base_path();
 2467             if (basedir == NULL)
 2468                 basedir = g_strdup("%d");
 2469             bvalue = g_key_file_get_boolean(config, "project", "make_in_base_path", NULL);
 2470             if (bvalue)
 2471                 makebasedir = g_strdup(basedir);
 2472             else
 2473                 makebasedir = g_strdup("%d");
 2474             if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].old)
 2475                 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_ALL)].working_dir, g_strdup(makebasedir));
 2476             if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].old)
 2477                 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_CUSTOM)].working_dir, g_strdup(makebasedir));
 2478             if (non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].old)
 2479                 SETPTR(non_ft_pref[GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)].working_dir, g_strdup("%d"));
 2480             value = g_key_file_get_string(config, "project", "run_cmd", NULL);
 2481             if (!EMPTY(value))
 2482             {
 2483                 if (exec_proj == NULL)
 2484                     exec_proj = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
 2485                 if (! exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists)
 2486                 {
 2487                     exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].exists = TRUE;
 2488                     SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].label, g_strdup(_("_Execute")));
 2489                     SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].command, value);
 2490                     SETPTR(exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].working_dir, g_strdup(basedir));
 2491                     exec_proj[GBO_TO_CMD(GEANY_GBO_EXEC)].old = TRUE;
 2492                 }
 2493             }
 2494             g_free(makebasedir);
 2495             g_free(basedir);
 2496             break;
 2497         case GEANY_BCS_PREF:
 2498             value = g_key_file_get_string(config, "tools", "make_cmd", NULL);
 2499             if (value != NULL)
 2500             {
 2501                 if (non_ft_pref == NULL)
 2502                     non_ft_pref = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
 2503                 assign_cmd(non_ft_pref, GEANY_GBO_CUSTOM, _("Make Custom _Target..."),
 2504                         g_strdup_printf("%s ", value));
 2505                 assign_cmd(non_ft_pref, GEANY_GBO_MAKE_OBJECT, _("Make _Object"),
 2506                         g_strdup_printf("%s %%e.o",value));
 2507                 assign_cmd(non_ft_pref, GEANY_GBO_MAKE_ALL, _("_Make"), value);
 2508             }
 2509             break;
 2510         default:
 2511             break;
 2512     }
 2513 }
 2514 
 2515 
 2516 static guint build_save_menu_grp(GKeyFile *config, GeanyBuildCommand *src, gint grp, gchar *prefix)
 2517 {
 2518     guint cmd;
 2519     gsize prefixlen; /* NOTE prefixlen used in macros above */
 2520     gchar *key;
 2521     guint count = 0;
 2522     enum GeanyBuildCmdEntries i;
 2523 
 2524     if (src == NULL)
 2525         return 0;
 2526     prefixlen = prefix == NULL ? 0 : strlen(prefix);
 2527     key = g_strconcat(prefix == NULL ? "" : prefix, fixedkey, NULL);
 2528     for (cmd = 0; cmd < build_groups_count[grp]; ++cmd)
 2529     {
 2530         if (src[cmd].exists) ++count;
 2531         if (src[cmd].changed)
 2532         {
 2533             static gchar cmdbuf[4] = "   ";
 2534             if (cmd >= 100)
 2535                 break; /* ensure no buffer overflow */
 2536             sprintf(cmdbuf, "%02u", cmd);
 2537             set_key_grp(key, groups[grp]);
 2538             set_key_cmd(key, cmdbuf);
 2539             if (src[cmd].exists)
 2540             {
 2541                 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 2542                 {
 2543                     set_key_fld(key, config_keys[i]);
 2544                     g_key_file_set_string(config, build_grp_name, key, id_to_str(&src[cmd], i));
 2545                 }
 2546             }
 2547             else
 2548             {
 2549                 for (i = 0; i < GEANY_BC_CMDENTRIES_COUNT; i++)
 2550                 {
 2551                     set_key_fld(key, config_keys[i]);
 2552                     g_key_file_remove_key(config, build_grp_name, key, NULL);
 2553                 }
 2554             }
 2555         }
 2556     }
 2557     g_free(key);
 2558     return count;
 2559 }
 2560 
 2561 
 2562 static gboolean save_project_filetype(GeanyFiletype *ft, GKeyFile *config)
 2563 {
 2564     guint i = 0;
 2565     gchar *regkey = g_strdup_printf("%serror_regex", ft->name);
 2566 
 2567     i += build_save_menu_grp(config, ft->priv->projfilecmds, GEANY_GBG_FT, ft->name);
 2568     i += build_save_menu_grp(config, ft->priv->projexeccmds, GEANY_GBG_EXEC, ft->name);
 2569     if (!EMPTY(ft->priv->projerror_regex_string))
 2570     {
 2571         g_key_file_set_string(config, build_grp_name, regkey, ft->priv->projerror_regex_string);
 2572         i++;
 2573     }
 2574     else
 2575         g_key_file_remove_key(config, build_grp_name, regkey, NULL);
 2576     g_free(regkey);
 2577     return (i > 0);
 2578 }
 2579 
 2580 /* TODO: untyped ptr is too ugly (also for build_load_menu) */
 2581 void build_save_menu(GKeyFile *config, gpointer ptr, GeanyBuildSource src)
 2582 {
 2583     GeanyFiletype *ft;
 2584     GeanyProject *pj;
 2585 
 2586     switch (src)
 2587     {
 2588         case GEANY_BCS_HOME_FT:
 2589             ft = (GeanyFiletype*)ptr;
 2590             if (ft == NULL)
 2591                 return;
 2592             build_save_menu_grp(config, ft->priv->homefilecmds, GEANY_GBG_FT, NULL);
 2593             build_save_menu_grp(config, ft->priv->homeexeccmds, GEANY_GBG_EXEC, NULL);
 2594             if (!EMPTY(ft->priv->homeerror_regex_string))
 2595                 g_key_file_set_string(config, build_grp_name, "error_regex", ft->priv->homeerror_regex_string);
 2596             else
 2597                 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
 2598             break;
 2599         case GEANY_BCS_PREF:
 2600             build_save_menu_grp(config, non_ft_pref, GEANY_GBG_NON_FT, NULL);
 2601             build_save_menu_grp(config, exec_pref, GEANY_GBG_EXEC, NULL);
 2602             if (!EMPTY(regex_pref))
 2603                 g_key_file_set_string(config, build_grp_name, "error_regex", regex_pref);
 2604             else
 2605                 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
 2606             break;
 2607         case GEANY_BCS_PROJ:
 2608             pj = (GeanyProject*)ptr;
 2609             build_save_menu_grp(config, non_ft_proj, GEANY_GBG_NON_FT, NULL);
 2610             build_save_menu_grp(config, exec_proj, GEANY_GBG_EXEC, NULL);
 2611             if (!EMPTY(regex_proj))
 2612                 g_key_file_set_string(config, build_grp_name, "error_regex", regex_proj);
 2613             else
 2614                 g_key_file_remove_key(config, build_grp_name, "error_regex", NULL);
 2615             if (pj->priv->build_filetypes_list != NULL)
 2616             {
 2617                 GPtrArray *ft_names = g_ptr_array_new();
 2618                 const GPtrArray *build_fts = pj->priv->build_filetypes_list;
 2619                 
 2620                 for (guint i = 0; i < build_fts->len; i++)
 2621                 {
 2622                     ft = build_fts->pdata[i];
 2623                     if (save_project_filetype(ft, config))
 2624                         g_ptr_array_add(ft_names, ft->name);
 2625                 }
 2626                 if (ft_names->pdata != NULL)
 2627                     g_key_file_set_string_list(config, build_grp_name, "filetypes",
 2628                         (const gchar**)ft_names->pdata, ft_names->len);
 2629                 else
 2630                     g_key_file_remove_key(config, build_grp_name, "filetypes", NULL);
 2631                 g_ptr_array_free(ft_names, TRUE);
 2632             }
 2633             break;
 2634         default: /* defaults and GEANY_BCS_FT can't save */
 2635             break;
 2636     }
 2637 }
 2638 
 2639 
 2640 /* FIXME: count is int only because calling code doesn't handle checking its value itself */
 2641 void build_set_group_count(GeanyBuildGroup grp, gint count)
 2642 {
 2643     guint i, sum;
 2644 
 2645     g_return_if_fail(count >= 0);
 2646 
 2647     if ((guint) count > build_groups_count[grp])
 2648         build_groups_count[grp] = (guint) count;
 2649     for (i = 0, sum = 0; i < GEANY_GBG_COUNT; ++i)
 2650         sum += build_groups_count[i];
 2651     build_items_count = sum;
 2652 }
 2653 
 2654 
 2655 /** Get the count of commands for the group
 2656  *
 2657  * Get the number of commands in the group specified by @a grp.
 2658  *
 2659  * @param grp the group of the specified menu item.
 2660  *
 2661  * @return a count of the number of commands in the group
 2662  *
 2663  **/
 2664 GEANY_API_SYMBOL
 2665 guint build_get_group_count(const GeanyBuildGroup grp)
 2666 {
 2667     g_return_val_if_fail(grp < GEANY_GBG_COUNT, 0);
 2668     return build_groups_count[grp];
 2669 }
 2670 
 2671 
 2672 static void on_project_close(void)
 2673 {
 2674     /* remove project regexen */
 2675     SETPTR(regex_proj, NULL);
 2676 }
 2677 
 2678 
 2679 static struct
 2680 {
 2681     const gchar *label;
 2682     const gchar *command;
 2683     const gchar *working_dir;
 2684     GeanyBuildCommand **ptr;
 2685     gint index;
 2686 } default_cmds[] = {
 2687     { N_("_Make"), "make", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_ALL)},
 2688     { N_("Make Custom _Target..."), "make ", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_CUSTOM)},
 2689     { N_("Make _Object"), "make %e.o", NULL, &non_ft_def, GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)},
 2690     { N_("_Execute"), "./%e", NULL, &exec_def, GBO_TO_CMD(GEANY_GBO_EXEC)},
 2691     { NULL, NULL, NULL, NULL, 0 }
 2692 };
 2693 
 2694 
 2695 void build_init(void)
 2696 {
 2697     GtkWidget *item;
 2698     GtkWidget *toolmenu;
 2699     gint cmdindex;
 2700 
 2701     g_signal_connect(geany_object, "project-close", on_project_close, NULL);
 2702 
 2703     ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_FT]);
 2704     non_ft_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_NON_FT]);
 2705     exec_def = g_new0(GeanyBuildCommand, build_groups_count[GEANY_GBG_EXEC]);
 2706     run_info = g_new0(RunInfo, build_groups_count[GEANY_GBG_EXEC]);
 2707 
 2708     for (cmdindex = 0; default_cmds[cmdindex].command != NULL; ++cmdindex)
 2709     {
 2710         GeanyBuildCommand *cmd = &((*(default_cmds[cmdindex].ptr))[ default_cmds[cmdindex].index ]);
 2711         cmd->exists = TRUE;
 2712         cmd->label = g_strdup(_(default_cmds[cmdindex].label));
 2713         cmd->command = g_strdup(default_cmds[cmdindex].command);
 2714         cmd->working_dir = g_strdup(default_cmds[cmdindex].working_dir);
 2715     }
 2716 
 2717     /* create the toolbar Build item sub menu */
 2718     toolmenu = gtk_menu_new();
 2719     g_object_ref(toolmenu);
 2720 
 2721     /* build the code */
 2722     item = ui_image_menu_item_new(GEANY_STOCK_BUILD, _("_Build"));
 2723     gtk_widget_show(item);
 2724     gtk_container_add(GTK_CONTAINER(toolmenu), item);
 2725     g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_build_activate),
 2726         GBO_TO_POINTER(GEANY_GBO_BUILD));
 2727     widgets.toolitem_build = item;
 2728 
 2729     item = gtk_separator_menu_item_new();
 2730     gtk_widget_show(item);
 2731     gtk_container_add(GTK_CONTAINER(toolmenu), item);
 2732 
 2733     /* build the code with make all */
 2734     item = gtk_image_menu_item_new_with_mnemonic(_("_Make All"));
 2735     gtk_widget_show(item);
 2736     gtk_container_add(GTK_CONTAINER(toolmenu), item);
 2737     g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
 2738         GBO_TO_POINTER(GEANY_GBO_MAKE_ALL));
 2739     widgets.toolitem_make_all = item;
 2740 
 2741     /* build the code with make custom */
 2742     item = gtk_image_menu_item_new_with_mnemonic(_("Make Custom _Target..."));
 2743     gtk_widget_show(item);
 2744     gtk_container_add(GTK_CONTAINER(toolmenu), item);
 2745     g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
 2746         GBO_TO_POINTER(GEANY_GBO_CUSTOM));
 2747     widgets.toolitem_make_custom = item;
 2748 
 2749     /* build the code with make object */
 2750     item = gtk_image_menu_item_new_with_mnemonic(_("Make _Object"));
 2751     gtk_widget_show(item);
 2752     gtk_container_add(GTK_CONTAINER(toolmenu), item);
 2753     g_signal_connect(item, "activate", G_CALLBACK(on_toolbutton_make_activate),
 2754         GBO_TO_POINTER(GEANY_GBO_MAKE_OBJECT));
 2755     widgets.toolitem_make_object = item;
 2756 
 2757     item = gtk_separator_menu_item_new();
 2758     gtk_widget_show(item);
 2759     gtk_container_add(GTK_CONTAINER(toolmenu), item);
 2760 
 2761     /* arguments */
 2762     item = ui_image_menu_item_new(GTK_STOCK_PREFERENCES, _("_Set Build Commands"));
 2763     gtk_widget_show(item);
 2764     gtk_container_add(GTK_CONTAINER(toolmenu), item);
 2765     g_signal_connect(item, "activate", G_CALLBACK(on_set_build_commands_activate), NULL);
 2766     widgets.toolitem_set_args = item;
 2767 
 2768     /* get toolbar action pointers */
 2769     widgets.build_action = toolbar_get_action_by_name("Build");
 2770     widgets.compile_action = toolbar_get_action_by_name("Compile");
 2771     widgets.run_action = toolbar_get_action_by_name("Run");
 2772     widgets.toolmenu = toolmenu;
 2773     /* set the submenu to the toolbar item */
 2774     geany_menu_button_action_set_menu(GEANY_MENU_BUTTON_ACTION(widgets.build_action), toolmenu);
 2775 }
 2776 
 2777 
 2778 gboolean build_keybinding(guint key_id)
 2779 {
 2780     GtkWidget *item;
 2781     BuildMenuItems *menu_items;
 2782     GeanyDocument *doc = document_get_current();
 2783 
 2784     if (doc == NULL)
 2785         return TRUE;
 2786 
 2787     if (!gtk_widget_is_sensitive(ui_lookup_widget(main_widgets.window, "menu_build1")))
 2788         return TRUE;
 2789 
 2790     menu_items = build_get_menu_items(doc->file_type->id);
 2791     /* TODO make it a table??*/
 2792     switch (key_id)
 2793     {
 2794         case GEANY_KEYS_BUILD_COMPILE:
 2795             item = menu_items->menu_item[GEANY_GBG_FT][GBO_TO_CMD(GEANY_GBO_COMPILE)];
 2796             break;
 2797         case GEANY_KEYS_BUILD_LINK:
 2798             item = menu_items->menu_item[GEANY_GBG_FT][GBO_TO_CMD(GEANY_GBO_BUILD)];
 2799             break;
 2800         case GEANY_KEYS_BUILD_MAKE:
 2801             item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_MAKE_ALL)];
 2802             break;
 2803         case GEANY_KEYS_BUILD_MAKEOWNTARGET:
 2804             item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_CUSTOM)];
 2805             break;
 2806         case GEANY_KEYS_BUILD_MAKEOBJECT:
 2807             item = menu_items->menu_item[GEANY_GBG_NON_FT][GBO_TO_CMD(GEANY_GBO_MAKE_OBJECT)];
 2808             break;
 2809         case GEANY_KEYS_BUILD_NEXTERROR:
 2810             item = menu_items->menu_item[GBG_FIXED][GBF_NEXT_ERROR];
 2811             break;
 2812         case GEANY_KEYS_BUILD_PREVIOUSERROR:
 2813             item = menu_items->menu_item[GBG_FIXED][GBF_PREV_ERROR];
 2814             break;
 2815         case GEANY_KEYS_BUILD_RUN:
 2816             item = menu_items->menu_item[GEANY_GBG_EXEC][GBO_TO_CMD(GEANY_GBO_EXEC)];
 2817             break;
 2818         case GEANY_KEYS_BUILD_OPTIONS:
 2819             item = menu_items->menu_item[GBG_FIXED][GBF_COMMANDS];
 2820             break;
 2821         default:
 2822             item = NULL;
 2823     }
 2824     /* Note: For Build menu items it's OK (at the moment) to assume they are in the correct
 2825      * sensitive state, but some other menus don't update the sensitive status until
 2826      * they are redrawn. */
 2827     if (item && gtk_widget_is_sensitive(item))
 2828         gtk_menu_item_activate(GTK_MENU_ITEM(item));
 2829     return TRUE;
 2830 }
 2831