Add filetype extraction by regex
[geany-mirror.git] / src / filetypes.c
blobc4b1b2a5529151ba1ec8ec08dad87a5bc666177d
1 /*
2 * filetypes.c - this file is part of Geany, a fast and lightweight IDE
4 * Copyright 2005-2011 Enrico Tröger <enrico(dot)troeger(at)uvena(dot)de>
5 * Copyright 2006-2011 Nick Treleaven <nick(dot)treleaven(at)btinternet(dot)com>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 /**
23 * @file filetypes.h
24 * Filetype detection, file extensions and filetype menu items.
27 /* Note: we use filetype_id for some function arguments, but GeanyFiletype is better; we should
28 * only use GeanyFiletype for API functions. */
30 #include <string.h>
31 #include <glib/gstdio.h>
33 #include "geany.h"
34 #include "filetypes.h"
35 #include "filetypesprivate.h"
36 #include "highlighting.h"
37 #include "support.h"
38 #include "templates.h"
39 #include "document.h"
40 #include "editor.h"
41 #include "msgwindow.h"
42 #include "utils.h"
43 #include "sciwrappers.h"
44 #include "ui_utils.h"
45 #include "symbols.h"
47 #include <stdlib.h>
49 #define GEANY_FILETYPE_SEARCH_LINES 2 /* lines of file to search for filetype */
51 GPtrArray *filetypes_array = NULL; /* Dynamic array of filetype pointers */
53 static GHashTable *filetypes_hash = NULL; /* Hash of filetype pointers based on name keys */
55 /** List of filetype pointers sorted by name, but with @c filetypes_index(GEANY_FILETYPES_NONE)
56 * first, as this is usually treated specially.
57 * The list does not change (after filetypes have been initialized), so you can use
58 * @code g_slist_nth_data(filetypes_by_title, n) @endcode and expect the same result at different times.
59 * @see filetypes_get_sorted_by_name(). */
60 GSList *filetypes_by_title = NULL;
63 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype);
65 static gchar *filetypes_get_conf_extension(const GeanyFiletype *ft);
66 static void read_filetype_config(void);
69 enum TitleType
71 TITLE_SOURCE_FILE,
72 TITLE_FILE
75 /* Save adding many translation strings if the filetype name doesn't need translating */
76 static void filetype_make_title(GeanyFiletype *ft, enum TitleType type)
78 const gchar *fmt = NULL;
80 switch (type)
82 default:
83 case TITLE_SOURCE_FILE: fmt = _("%s source file"); break;
84 case TITLE_FILE: fmt = _("%s file"); break;
86 g_assert(!ft->title);
87 g_assert(ft->name);
88 ft->title = g_strdup_printf(fmt, ft->name);
92 /* Note: remember to update HACKING if this function is renamed. */
93 static void init_builtin_filetypes(void)
95 GeanyFiletype *ft;
97 #define NONE /* these macros are only to ease navigation */
98 ft = filetypes[GEANY_FILETYPES_NONE];
99 /* ft->name must not be translated as it is used for filetype lookup.
100 * Use filetypes_get_display_name() instead. */
101 ft->name = g_strdup("None");
102 ft->title = g_strdup(_("None"));
103 ft->group = GEANY_FILETYPE_GROUP_NONE;
105 #define C
106 ft = filetypes[GEANY_FILETYPES_C];
107 ft->lang = 0;
108 ft->name = g_strdup("C");
109 filetype_make_title(ft, TITLE_SOURCE_FILE);
110 ft->mime_type = g_strdup("text/x-csrc");
111 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
113 #define CPP
114 ft = filetypes[GEANY_FILETYPES_CPP];
115 ft->lang = 1;
116 ft->name = g_strdup("C++");
117 filetype_make_title(ft, TITLE_SOURCE_FILE);
118 ft->mime_type = g_strdup("text/x-c++src");
119 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
121 #define CS
122 ft = filetypes[GEANY_FILETYPES_CS];
123 ft->lang = 25;
124 ft->name = g_strdup("C#");
125 filetype_make_title(ft, TITLE_SOURCE_FILE);
126 ft->mime_type = g_strdup("text/x-csharp");
127 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
129 #define VALA
130 ft = filetypes[GEANY_FILETYPES_VALA];
131 ft->lang = 33;
132 ft->name = g_strdup("Vala");
133 filetype_make_title(ft, TITLE_SOURCE_FILE);
134 ft->mime_type = g_strdup("text/x-vala");
135 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
137 #define D
138 ft = filetypes[GEANY_FILETYPES_D];
139 ft->lang = 17;
140 ft->name = g_strdup("D");
141 filetype_make_title(ft, TITLE_SOURCE_FILE);
142 ft->mime_type = g_strdup("text/x-dsrc");
143 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
145 #define JAVA
146 ft = filetypes[GEANY_FILETYPES_JAVA];
147 ft->lang = 2;
148 ft->name = g_strdup("Java");
149 filetype_make_title(ft, TITLE_SOURCE_FILE);
150 ft->mime_type = g_strdup("text/x-java");
151 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
153 #define PAS /* to avoid warnings when building under Windows, the symbol PASCAL is there defined */
154 ft = filetypes[GEANY_FILETYPES_PASCAL];
155 ft->lang = 4;
156 ft->name = g_strdup("Pascal");
157 filetype_make_title(ft, TITLE_SOURCE_FILE);
158 ft->mime_type = g_strdup("text/x-pascal");
159 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
161 #define ASM
162 ft = filetypes[GEANY_FILETYPES_ASM];
163 ft->lang = 9;
164 ft->name = g_strdup("ASM");
165 ft->title = g_strdup_printf(_("%s source file"), "Assembler");
166 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
168 #define BASIC
169 ft = filetypes[GEANY_FILETYPES_BASIC];
170 ft->lang = 26;
171 ft->name = g_strdup("FreeBasic");
172 filetype_make_title(ft, TITLE_SOURCE_FILE);
173 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
175 #define FORTRAN
176 ft = filetypes[GEANY_FILETYPES_FORTRAN];
177 ft->lang = 18;
178 ft->name = g_strdup("Fortran");
179 ft->title = g_strdup_printf(_("%s source file"), "Fortran (F90)");
180 ft->mime_type = g_strdup("text/x-fortran");
181 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
183 #define F77
184 ft = filetypes[GEANY_FILETYPES_F77];
185 ft->lang = 30;
186 ft->name = g_strdup("F77");
187 ft->title = g_strdup_printf(_("%s source file"), "Fortran (F77)");
188 ft->mime_type = g_strdup("text/x-fortran");
189 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
191 #define GLSL
192 ft = filetypes[GEANY_FILETYPES_GLSL];
193 ft->lang = 31;
194 ft->name = g_strdup("GLSL");
195 filetype_make_title(ft, TITLE_SOURCE_FILE);
196 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
198 #define CAML
199 ft = filetypes[GEANY_FILETYPES_CAML];
200 ft->name = g_strdup("CAML");
201 ft->title = g_strdup_printf(_("%s source file"), "(O)Caml");
202 ft->mime_type = g_strdup("text/x-ocaml");
203 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
205 #define PERL
206 ft = filetypes[GEANY_FILETYPES_PERL];
207 ft->lang = 5;
208 ft->name = g_strdup("Perl");
209 filetype_make_title(ft, TITLE_SOURCE_FILE);
210 ft->mime_type = g_strdup("application/x-perl");
211 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
213 #define PHP
214 ft = filetypes[GEANY_FILETYPES_PHP];
215 ft->lang = 6;
216 ft->name = g_strdup("PHP");
217 filetype_make_title(ft, TITLE_SOURCE_FILE);
218 ft->mime_type = g_strdup("application/x-php");
219 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
221 #define JAVASCRIPT
222 ft = filetypes[GEANY_FILETYPES_JS];
223 ft->lang = 23;
224 ft->name = g_strdup("Javascript");
225 filetype_make_title(ft, TITLE_SOURCE_FILE);
226 ft->mime_type = g_strdup("application/javascript");
227 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
229 #define PYTHON
230 ft = filetypes[GEANY_FILETYPES_PYTHON];
231 ft->lang = 7;
232 ft->name = g_strdup("Python");
233 filetype_make_title(ft, TITLE_SOURCE_FILE);
234 ft->mime_type = g_strdup("text/x-python");
235 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
237 #define RUBY
238 ft = filetypes[GEANY_FILETYPES_RUBY];
239 ft->lang = 14;
240 ft->name = g_strdup("Ruby");
241 filetype_make_title(ft, TITLE_SOURCE_FILE);
242 ft->mime_type = g_strdup("application/x-ruby");
243 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
245 #define TCL
246 ft = filetypes[GEANY_FILETYPES_TCL];
247 ft->lang = 15;
248 ft->name = g_strdup("Tcl");
249 filetype_make_title(ft, TITLE_SOURCE_FILE);
250 ft->mime_type = g_strdup("text/x-tcl");
251 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
253 #define LUA
254 ft = filetypes[GEANY_FILETYPES_LUA];
255 ft->lang = 22;
256 ft->name = g_strdup("Lua");
257 filetype_make_title(ft, TITLE_SOURCE_FILE);
258 ft->mime_type = g_strdup("text/x-lua");
259 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
261 #define FERITE
262 ft = filetypes[GEANY_FILETYPES_FERITE];
263 ft->lang = 19;
264 ft->name = g_strdup("Ferite");
265 filetype_make_title(ft, TITLE_SOURCE_FILE);
266 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
268 #define HASKELL
269 ft = filetypes[GEANY_FILETYPES_HASKELL];
270 ft->lang = 24;
271 ft->name = g_strdup("Haskell");
272 filetype_make_title(ft, TITLE_SOURCE_FILE);
273 ft->mime_type = g_strdup("text/x-haskell");
274 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
276 #define MARKDOWN
277 ft = filetypes[GEANY_FILETYPES_MARKDOWN];
278 ft->lang = 36;
279 ft->name = g_strdup("Markdown");
280 filetype_make_title(ft, TITLE_SOURCE_FILE);
281 ft->mime_type = g_strdup("text/x-markdown");
282 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
284 #define TXT2TAGS
285 ft = filetypes[GEANY_FILETYPES_TXT2TAGS];
286 ft->lang = 37;
287 ft->name = g_strdup("Txt2tags");
288 filetype_make_title(ft, TITLE_SOURCE_FILE);
289 ft->mime_type = g_strdup("text/x-txt2tags");
290 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
292 #define ABC
293 ft = filetypes[GEANY_FILETYPES_ABC];
294 ft->lang = 38;
295 ft->name = g_strdup("Abc");
296 filetype_make_title(ft, TITLE_FILE);
297 ft->group = GEANY_FILETYPE_GROUP_MISC;
299 #define SH
300 ft = filetypes[GEANY_FILETYPES_SH];
301 ft->lang = 16;
302 ft->name = g_strdup("Sh");
303 ft->title = g_strdup(_("Shell script"));
304 ft->mime_type = g_strdup("application/x-shellscript");
305 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
307 #define MAKE
308 ft = filetypes[GEANY_FILETYPES_MAKE];
309 ft->lang = 3;
310 ft->name = g_strdup("Make");
311 ft->title = g_strdup(_("Makefile"));
312 ft->mime_type = g_strdup("text/x-makefile");
313 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
315 #define XML
316 ft = filetypes[GEANY_FILETYPES_XML];
317 ft->name = g_strdup("XML");
318 ft->title = g_strdup(_("XML document"));
319 ft->mime_type = g_strdup("application/xml");
320 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
322 #define DOCBOOK
323 ft = filetypes[GEANY_FILETYPES_DOCBOOK];
324 ft->lang = 12;
325 ft->name = g_strdup("Docbook");
326 filetype_make_title(ft, TITLE_SOURCE_FILE);
327 ft->mime_type = g_strdup("application/docbook+xml");
328 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
330 #define HTML
331 ft = filetypes[GEANY_FILETYPES_HTML];
332 ft->lang = 29;
333 ft->name = g_strdup("HTML");
334 filetype_make_title(ft, TITLE_SOURCE_FILE);
335 ft->mime_type = g_strdup("text/html");
336 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
338 #define CSS
339 ft = filetypes[GEANY_FILETYPES_CSS];
340 ft->lang = 13;
341 ft->name = g_strdup("CSS");
342 ft->title = g_strdup(_("Cascading StyleSheet"));
343 ft->mime_type = g_strdup("text/css");
344 ft->group = GEANY_FILETYPE_GROUP_MARKUP; /* not really markup but fit quite well to HTML */
346 #define SQL
347 ft = filetypes[GEANY_FILETYPES_SQL];
348 ft->lang = 11;
349 ft->name = g_strdup("SQL");
350 filetype_make_title(ft, TITLE_FILE);
351 ft->mime_type = g_strdup("text/x-sql");
352 ft->group = GEANY_FILETYPE_GROUP_MISC;
354 #define COBOL
355 ft = filetypes[GEANY_FILETYPES_COBOL];
356 ft->lang = 41;
357 ft->name = g_strdup("COBOL");
358 filetype_make_title(ft, TITLE_SOURCE_FILE);
359 ft->mime_type = g_strdup("text/x-cobol");
360 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
362 #define LATEX
363 ft = filetypes[GEANY_FILETYPES_LATEX];
364 ft->lang = 8;
365 ft->name = g_strdup("LaTeX");
366 filetype_make_title(ft, TITLE_SOURCE_FILE);
367 ft->mime_type = g_strdup("text/x-tex");
368 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
370 #define VHDL
371 ft = filetypes[GEANY_FILETYPES_VHDL];
372 ft->lang = 21;
373 ft->name = g_strdup("VHDL");
374 filetype_make_title(ft, TITLE_SOURCE_FILE);
375 ft->mime_type = g_strdup("text/x-vhdl");
376 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
378 #define VERILOG
379 ft = filetypes[GEANY_FILETYPES_VERILOG];
380 ft->lang = 39;
381 ft->name = g_strdup("Verilog");
382 filetype_make_title(ft, TITLE_SOURCE_FILE);
383 ft->mime_type = g_strdup("text/x-verilog");
384 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
386 #define DIFF
387 ft = filetypes[GEANY_FILETYPES_DIFF];
388 ft->lang = 20;
389 ft->name = g_strdup("Diff");
390 filetype_make_title(ft, TITLE_FILE);
391 ft->mime_type = g_strdup("text/x-patch");
392 ft->group = GEANY_FILETYPE_GROUP_MISC;
394 #define LISP
395 ft = filetypes[GEANY_FILETYPES_LISP];
396 ft->name = g_strdup("Lisp");
397 filetype_make_title(ft, TITLE_SOURCE_FILE);
398 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
400 #define ERLANG
401 ft = filetypes[GEANY_FILETYPES_ERLANG];
402 ft->name = g_strdup("Erlang");
403 filetype_make_title(ft, TITLE_SOURCE_FILE);
404 ft->mime_type = g_strdup("text/x-erlang");
405 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
407 #define CONF
408 ft = filetypes[GEANY_FILETYPES_CONF];
409 ft->lang = 10;
410 ft->name = g_strdup("Conf");
411 ft->title = g_strdup(_("Config file"));
412 ft->group = GEANY_FILETYPE_GROUP_MISC;
414 #define PO
415 ft = filetypes[GEANY_FILETYPES_PO];
416 ft->name = g_strdup("Po");
417 ft->title = g_strdup(_("Gettext translation file"));
418 ft->mime_type = g_strdup("text/x-gettext-translation");
419 ft->group = GEANY_FILETYPE_GROUP_MISC;
421 #define HAXE
422 ft = filetypes[GEANY_FILETYPES_HAXE];
423 ft->lang = 27;
424 ft->name = g_strdup("Haxe");
425 filetype_make_title(ft, TITLE_SOURCE_FILE);
426 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
428 #define ACTIONSCRIPT
429 ft = filetypes[GEANY_FILETYPES_AS];
430 ft->lang = 34;
431 ft->name = g_strdup("ActionScript");
432 filetype_make_title(ft, TITLE_SOURCE_FILE);
433 ft->mime_type = g_strdup("application/ecmascript");
434 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
436 #define R
437 ft = filetypes[GEANY_FILETYPES_R];
438 ft->lang = 40;
439 ft->name = g_strdup("R");
440 filetype_make_title(ft, TITLE_SOURCE_FILE);
441 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
443 #define REST
444 ft = filetypes[GEANY_FILETYPES_REST];
445 ft->lang = 28;
446 ft->name = g_strdup("reStructuredText");
447 filetype_make_title(ft, TITLE_SOURCE_FILE);
448 ft->group = GEANY_FILETYPE_GROUP_MARKUP;
450 #define MATLAB
451 ft = filetypes[GEANY_FILETYPES_MATLAB];
452 ft->lang = 32;
453 ft->name = g_strdup("Matlab/Octave");
454 filetype_make_title(ft, TITLE_SOURCE_FILE);
455 ft->mime_type = g_strdup("text/x-matlab");
456 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
458 #define YAML
459 ft = filetypes[GEANY_FILETYPES_YAML];
460 ft->name = g_strdup("YAML");
461 filetype_make_title(ft, TITLE_FILE);
462 ft->mime_type = g_strdup("application/x-yaml");
463 ft->group = GEANY_FILETYPE_GROUP_MISC;
465 #define CMAKE
466 ft = filetypes[GEANY_FILETYPES_CMAKE];
467 ft->name = g_strdup("CMake");
468 filetype_make_title(ft, TITLE_SOURCE_FILE);
469 ft->mime_type = g_strdup("text/x-cmake");
470 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
472 #define NSIS
473 ft = filetypes[GEANY_FILETYPES_NSIS];
474 ft->lang = 35;
475 ft->name = g_strdup("NSIS");
476 filetype_make_title(ft, TITLE_SOURCE_FILE);
477 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
479 #define ADA
480 ft = filetypes[GEANY_FILETYPES_ADA];
481 ft->name = g_strdup("Ada");
482 filetype_make_title(ft, TITLE_SOURCE_FILE);
483 ft->mime_type = g_strdup("text/x-adasrc");
484 ft->group = GEANY_FILETYPE_GROUP_COMPILED;
486 #define FORTH
487 ft = filetypes[GEANY_FILETYPES_FORTH];
488 ft->name = g_strdup("Forth");
489 filetype_make_title(ft, TITLE_SOURCE_FILE);
490 ft->group = GEANY_FILETYPE_GROUP_SCRIPT;
494 /* initialize fields. */
495 static GeanyFiletype *filetype_new(void)
497 GeanyFiletype *ft = g_new0(GeanyFiletype, 1);
499 ft->group = GEANY_FILETYPE_GROUP_NONE;
500 ft->lang = -2; /* assume no tagmanager parser */
501 /* pattern must not be null */
502 ft->pattern = g_new0(gchar*, 1);
503 ft->project_list_entry = -1; /* no entry */
504 ft->indent_width = -1;
505 ft->indent_type = -1;
507 ft->priv = g_new0(GeanyFiletypePrivate, 1);
508 return ft;
512 static gint cmp_filetype(gconstpointer pft1, gconstpointer pft2, gpointer data)
514 gboolean by_name = GPOINTER_TO_INT(data);
515 const GeanyFiletype *ft1 = pft1, *ft2 = pft2;
517 if (G_UNLIKELY(ft1->id == GEANY_FILETYPES_NONE))
518 return -1;
519 if (G_UNLIKELY(ft2->id == GEANY_FILETYPES_NONE))
520 return 1;
522 return by_name ?
523 utils_str_casecmp(ft1->name, ft2->name) :
524 utils_str_casecmp(ft1->title, ft2->title);
528 /** Gets a list of filetype pointers sorted by name.
529 * The list does not change on subsequent calls.
530 * @return The list - do not free.
531 * @see filetypes_by_title. */
532 const GSList *filetypes_get_sorted_by_name(void)
534 static GSList *list = NULL;
536 g_return_val_if_fail(filetypes_by_title, NULL);
538 if (!list)
540 list = g_slist_copy(filetypes_by_title);
541 list = g_slist_sort_with_data(list, cmp_filetype, GINT_TO_POINTER(TRUE));
543 return list;
547 /* Add a filetype pointer to the lists of available filetypes,
548 * and set the filetype::id field. */
549 static void filetype_add(GeanyFiletype *ft)
551 g_return_if_fail(ft);
552 g_return_if_fail(ft->name);
554 ft->id = filetypes_array->len; /* len will be the index for filetype_array */
555 g_ptr_array_add(filetypes_array, ft);
556 g_hash_table_insert(filetypes_hash, ft->name, ft);
558 /* list will be sorted later */
559 filetypes_by_title = g_slist_prepend(filetypes_by_title, ft);
561 if (!ft->mime_type)
562 ft->mime_type = g_strdup("text/plain");
566 static void add_custom_filetype(const gchar *filename)
568 gchar *fn = utils_strdupa(strstr(filename, ".") + 1);
569 gchar *dot = g_strrstr(fn, ".conf");
570 GeanyFiletype *ft;
572 g_return_if_fail(dot);
574 *dot = 0x0;
576 if (g_hash_table_lookup(filetypes_hash, fn))
577 return;
579 ft = filetype_new();
580 ft->name = g_strdup(fn);
581 filetype_make_title(ft, TITLE_FILE);
582 ft->priv->custom = TRUE;
583 filetype_add(ft);
584 geany_debug("Added filetype %s (%d).", ft->name, ft->id);
588 static void init_custom_filetypes(const gchar *path)
590 GDir *dir;
591 const gchar *filename;
593 g_return_if_fail(path);
595 dir = g_dir_open(path, 0, NULL);
596 if (dir == NULL)
597 return;
599 foreach_dir(filename, dir)
601 const gchar prefix[] = "filetypes.";
603 if (g_str_has_prefix(filename, prefix) &&
604 g_str_has_suffix(filename + strlen(prefix), ".conf"))
606 add_custom_filetype(filename);
609 g_dir_close(dir);
613 /* Create the filetypes array and fill it with the known filetypes.
614 * Warning: GTK isn't necessarily initialized yet. */
615 void filetypes_init_types()
617 filetype_id ft_id;
618 gchar *f;
620 g_return_if_fail(filetypes_array == NULL);
621 g_return_if_fail(filetypes_hash == NULL);
623 filetypes_array = g_ptr_array_sized_new(GEANY_MAX_BUILT_IN_FILETYPES);
624 filetypes_hash = g_hash_table_new(g_str_hash, g_str_equal);
626 /* Create built-in filetypes */
627 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
629 filetypes[ft_id] = filetype_new();
631 init_builtin_filetypes();
633 /* Add built-in filetypes to the hash now the name fields are set */
634 for (ft_id = 0; ft_id < GEANY_MAX_BUILT_IN_FILETYPES; ft_id++)
636 filetype_add(filetypes[ft_id]);
638 init_custom_filetypes(app->datadir);
639 f = utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, NULL);
640 init_custom_filetypes(f);
641 g_free(f);
643 /* sort last instead of on insertion to prevent exponential time */
644 filetypes_by_title = g_slist_sort_with_data(filetypes_by_title,
645 cmp_filetype, GINT_TO_POINTER(FALSE));
647 read_filetype_config();
651 static void on_document_save(G_GNUC_UNUSED GObject *object, GeanyDocument *doc)
653 gchar *f;
655 g_return_if_fail(NZV(doc->real_path));
657 f = utils_build_path(app->configdir, "filetype_extensions.conf", NULL);
658 if (utils_str_equal(doc->real_path, f))
659 filetypes_reload_extensions();
661 g_free(f);
662 f = utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL);
663 if (utils_str_equal(doc->real_path, f))
665 guint i;
667 /* Note: we don't reload other filetypes, even though the named styles may have changed.
668 * The user can do this manually with 'Tools->Reload Configuration' */
669 filetypes_load_config(GEANY_FILETYPES_NONE, TRUE);
671 foreach_document(i)
672 document_reload_config(documents[i]);
674 g_free(f);
678 static void setup_config_file_menus(void)
680 gchar *f;
682 f = utils_build_path(app->configdir, "filetype_extensions.conf", NULL);
683 ui_add_config_file_menu_item(f, NULL, NULL);
684 setptr(f, utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.common", NULL));
685 ui_add_config_file_menu_item(f, NULL, NULL);
686 g_free(f);
688 g_signal_connect(geany_object, "document-save", G_CALLBACK(on_document_save), NULL);
692 static GtkWidget *group_menus[GEANY_FILETYPE_GROUP_COUNT] = {NULL};
694 static void create_sub_menu(GtkWidget *parent, gsize group_id, const gchar *title)
696 GtkWidget *menu, *item;
698 menu = gtk_menu_new();
699 item = gtk_menu_item_new_with_mnemonic((title));
700 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), menu);
701 gtk_container_add(GTK_CONTAINER(parent), item);
702 gtk_widget_show(item);
703 group_menus[group_id] = menu;
707 static void create_set_filetype_menu(void)
709 GSList *node;
710 GtkWidget *filetype_menu = ui_lookup_widget(main_widgets.window, "set_filetype1_menu");
712 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_COMPILED, _("_Programming Languages"));
713 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_SCRIPT, _("_Scripting Languages"));
714 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MARKUP, _("_Markup Languages"));
715 create_sub_menu(filetype_menu, GEANY_FILETYPE_GROUP_MISC, _("M_iscellaneous"));
717 /* Append all filetypes to the filetype menu */
718 foreach_slist(node, filetypes_by_title)
720 GeanyFiletype *ft = node->data;
722 if (ft->group != GEANY_FILETYPE_GROUP_NONE)
723 create_radio_menu_item(group_menus[ft->group], ft);
724 else
725 create_radio_menu_item(filetype_menu, ft);
730 void filetypes_init()
732 GSList *node;
734 filetypes_init_types();
736 /* this has to be here as GTK isn't initialized in filetypes_init_types(). */
737 foreach_slist(node, filetypes_by_title)
739 GeanyFiletype *ft = node->data;
740 ft->icon = ui_get_mime_icon(ft->mime_type, GTK_ICON_SIZE_MENU);
742 create_set_filetype_menu();
743 setup_config_file_menus();
747 /* Find a filetype that predicate returns TRUE for, otherwise return NULL. */
748 GeanyFiletype *filetypes_find(GCompareFunc predicate, gpointer user_data)
750 guint i;
752 for (i = 0; i < filetypes_array->len; i++)
754 GeanyFiletype *ft = filetypes[i];
756 if (predicate(ft, user_data))
757 return ft;
759 return NULL;
763 static gboolean match_basename(gconstpointer pft, gconstpointer user_data)
765 const GeanyFiletype *ft = pft;
766 const gchar *base_filename = user_data;
767 gint j;
768 gboolean ret = FALSE;
770 if (G_UNLIKELY(ft->id == GEANY_FILETYPES_NONE))
771 return FALSE;
773 for (j = 0; ft->pattern[j] != NULL; j++)
775 GPatternSpec *pattern = g_pattern_spec_new(ft->pattern[j]);
777 if (g_pattern_match_string(pattern, base_filename))
779 ret = TRUE;
780 g_pattern_spec_free(pattern);
781 break;
783 g_pattern_spec_free(pattern);
785 return ret;
789 static GeanyFiletype *check_builtin_filenames(const gchar *utf8_filename)
791 gchar *lfn = NULL;
792 gchar *path;
793 gboolean found = FALSE;
795 #ifdef G_OS_WIN32
796 /* use lower case basename */
797 lfn = g_utf8_strdown(utf8_filename, -1);
798 #else
799 lfn = g_strdup(utf8_filename);
800 #endif
801 setptr(lfn, utils_get_locale_from_utf8(lfn));
803 path = utils_build_path(app->configdir, GEANY_FILEDEFS_SUBDIR, "filetypes.", NULL);
804 if (g_str_has_prefix(lfn, path))
805 found = TRUE;
807 setptr(path, utils_build_path(app->datadir, "filetypes.", NULL));
808 if (g_str_has_prefix(lfn, path))
809 found = TRUE;
811 g_free(path);
812 g_free(lfn);
813 return found ? filetypes[GEANY_FILETYPES_CONF] : NULL;
817 /* Detect filetype only based on the filename extension.
818 * utf8_filename can include the full path. */
819 GeanyFiletype *filetypes_detect_from_extension(const gchar *utf8_filename)
821 gchar *base_filename;
822 GeanyFiletype *ft;
824 ft = check_builtin_filenames(utf8_filename);
825 if (ft)
826 return ft;
828 /* to match against the basename of the file (because of Makefile*) */
829 base_filename = g_path_get_basename(utf8_filename);
830 #ifdef G_OS_WIN32
831 /* use lower case basename */
832 setptr(base_filename, g_utf8_strdown(base_filename, -1));
833 #endif
835 ft = filetypes_find(match_basename, base_filename);
836 if (ft == NULL)
837 ft = filetypes[GEANY_FILETYPES_NONE];
839 g_free(base_filename);
840 return ft;
844 /* This detects the filetype of the file pointed by 'utf8_filename' and a list of filetype id's,
845 * terminated by -1.
846 * The detected filetype of the file is checked against every id in the passed list and if
847 * there is a match, TRUE is returned. */
848 static gboolean shebang_find_and_match_filetype(const gchar *utf8_filename, gint first, ...)
850 GeanyFiletype *ft = NULL;
851 gint test;
852 gboolean result = FALSE;
853 va_list args;
855 ft = filetypes_detect_from_extension(utf8_filename);
856 if (ft == NULL || ft->id >= filetypes_array->len)
857 return FALSE;
859 va_start(args, first);
860 test = first;
861 while (1)
863 if (test == -1)
864 break;
866 if (ft->id == (guint) test)
868 result = TRUE;
869 break;
871 test = va_arg(args, gint);
873 va_end(args);
875 return result;
879 static GeanyFiletype *find_shebang(const gchar *utf8_filename, const gchar *line)
881 GeanyFiletype *ft = NULL;
883 if (strlen(line) > 2 && line[0] == '#' && line[1] == '!')
885 gchar *tmp = g_path_get_basename(line + 2);
886 gchar *basename_interpreter = tmp;
888 if (strncmp(tmp, "env ", 4) == 0 && strlen(tmp) > 4)
889 { /* skip "env" and read the following interpreter */
890 basename_interpreter += 4;
893 if (strncmp(basename_interpreter, "sh", 2) == 0)
894 ft = filetypes[GEANY_FILETYPES_SH];
895 else if (strncmp(basename_interpreter, "bash", 4) == 0)
896 ft = filetypes[GEANY_FILETYPES_SH];
897 else if (strncmp(basename_interpreter, "perl", 4) == 0)
898 ft = filetypes[GEANY_FILETYPES_PERL];
899 else if (strncmp(basename_interpreter, "python", 6) == 0)
900 ft = filetypes[GEANY_FILETYPES_PYTHON];
901 else if (strncmp(basename_interpreter, "php", 3) == 0)
902 ft = filetypes[GEANY_FILETYPES_PHP];
903 else if (strncmp(basename_interpreter, "ruby", 4) == 0)
904 ft = filetypes[GEANY_FILETYPES_RUBY];
905 else if (strncmp(basename_interpreter, "tcl", 3) == 0)
906 ft = filetypes[GEANY_FILETYPES_TCL];
907 else if (strncmp(basename_interpreter, "make", 4) == 0)
908 ft = filetypes[GEANY_FILETYPES_MAKE];
909 else if (strncmp(basename_interpreter, "zsh", 3) == 0)
910 ft = filetypes[GEANY_FILETYPES_SH];
911 else if (strncmp(basename_interpreter, "ksh", 3) == 0)
912 ft = filetypes[GEANY_FILETYPES_SH];
913 else if (strncmp(basename_interpreter, "csh", 3) == 0)
914 ft = filetypes[GEANY_FILETYPES_SH];
915 else if (strncmp(basename_interpreter, "ash", 3) == 0)
916 ft = filetypes[GEANY_FILETYPES_SH];
917 else if (strncmp(basename_interpreter, "dmd", 3) == 0)
918 ft = filetypes[GEANY_FILETYPES_D];
919 else if (strncmp(basename_interpreter, "wish", 4) == 0)
920 ft = filetypes[GEANY_FILETYPES_TCL];
922 g_free(tmp);
924 /* detect HTML files */
925 if (strncmp(line, "<!DOCTYPE html", 14) == 0 || strncmp(line, "<html", 5) == 0)
927 /* PHP, Perl and Python files might also start with <html, so detect them based on filename
928 * extension and use the detected filetype, else assume HTML */
929 if (! shebang_find_and_match_filetype(utf8_filename,
930 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
932 ft = filetypes[GEANY_FILETYPES_HTML];
935 /* detect XML files */
936 else if (utf8_filename && strncmp(line, "<?xml", 5) == 0)
938 /* HTML and DocBook files might also start with <?xml, so detect them based on filename
939 * extension and use the detected filetype, else assume XML */
940 if (! shebang_find_and_match_filetype(utf8_filename,
941 GEANY_FILETYPES_HTML, GEANY_FILETYPES_DOCBOOK,
942 /* Perl, Python and PHP only to be safe */
943 GEANY_FILETYPES_PERL, GEANY_FILETYPES_PHP, GEANY_FILETYPES_PYTHON, -1))
945 ft = filetypes[GEANY_FILETYPES_XML];
948 else if (strncmp(line, "<?php", 5) == 0)
950 ft = filetypes[GEANY_FILETYPES_PHP];
953 return ft;
957 /* Detect the filetype checking for a shebang, then filename extension. */
958 static GeanyFiletype *filetypes_detect_from_file_internal(const gchar *utf8_filename,
959 gchar **lines)
961 GeanyFiletype *ft;
962 gint i;
963 GRegex *ft_regex;
964 GMatchInfo *match;
965 GError *regerr = NULL;
967 /* try to find a shebang and if found use it prior to the filename extension
968 * also checks for <?xml */
969 ft = find_shebang(utf8_filename, lines[0]);
970 if (ft != NULL)
971 return ft;
973 /* try to extract the filetype using a regex capture */
974 ft_regex = g_regex_new( file_prefs.extract_filetype_regex,
975 G_REGEX_RAW | G_REGEX_MULTILINE,
977 &regerr);
978 if (regerr == NULL)
980 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; i++)
982 if (g_regex_match(ft_regex, lines[i], 0, &match))
984 gchar *capture = g_match_info_fetch(match, 1);
985 if (capture != NULL)
987 ft = filetypes_lookup_by_name(capture);
988 g_free(capture);
991 g_match_info_free(match);
992 if (ft != NULL)
993 break;
996 else
998 g_error_free(regerr);
1000 g_regex_unref(ft_regex);
1001 if (ft != NULL)
1002 return ft;
1004 if (utf8_filename == NULL)
1005 return filetypes[GEANY_FILETYPES_NONE];
1007 return filetypes_detect_from_extension(utf8_filename);
1011 /* Detect the filetype for the document, checking for a shebang, then filename extension. */
1012 GeanyFiletype *filetypes_detect_from_document(GeanyDocument *doc)
1014 GeanyFiletype *ft;
1015 gchar *lines[GEANY_FILETYPE_SEARCH_LINES];
1016 gint i;
1018 if (doc == NULL)
1019 return filetypes[GEANY_FILETYPES_NONE];
1021 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
1023 lines[i] = sci_get_line(doc->editor->sci, i);
1025 ft = filetypes_detect_from_file_internal(doc->file_name, lines);
1026 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
1028 g_free(lines[i]);
1030 return ft;
1034 #ifdef HAVE_PLUGINS
1035 /* Currently only used by external plugins (e.g. geanyprj). */
1037 * Detects filetype based on a shebang line in the file or the filename extension.
1039 * @param utf8_filename The filename in UTF-8 encoding.
1041 * @return The detected filetype for @a utf8_filename or @c filetypes[GEANY_FILETYPES_NONE]
1042 * if it could not be detected.
1044 GeanyFiletype *filetypes_detect_from_file(const gchar *utf8_filename)
1046 gchar line[1024];
1047 gchar *lines[GEANY_FILETYPE_SEARCH_LINES];
1048 FILE *f;
1049 gchar *locale_name = utils_get_locale_from_utf8(utf8_filename);
1050 gint i;
1052 f = g_fopen(locale_name, "r");
1053 g_free(locale_name);
1054 if (f != NULL)
1056 if (fgets(line, sizeof(line), f) != NULL)
1058 fclose(f);
1059 for (i = 0; i < GEANY_FILETYPE_SEARCH_LINES; ++i)
1061 lines[i] = NULL;
1063 lines[0] = line;
1064 return filetypes_detect_from_file_internal(utf8_filename, lines);
1066 fclose(f);
1068 return filetypes_detect_from_extension(utf8_filename);
1070 #endif
1073 void filetypes_select_radio_item(const GeanyFiletype *ft)
1075 /* ignore_callback has to be set by the caller */
1076 g_return_if_fail(ignore_callback);
1078 if (ft == NULL)
1079 ft = filetypes[GEANY_FILETYPES_NONE];
1081 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(ft->priv->menu_item), TRUE);
1085 static void
1086 on_filetype_change(GtkCheckMenuItem *menuitem, gpointer user_data)
1088 GeanyDocument *doc = document_get_current();
1089 if (ignore_callback || doc == NULL || ! gtk_check_menu_item_get_active(menuitem))
1090 return;
1092 document_set_filetype(doc, (GeanyFiletype*)user_data);
1096 static void create_radio_menu_item(GtkWidget *menu, GeanyFiletype *ftype)
1098 static GSList *group = NULL;
1099 GtkWidget *tmp;
1101 tmp = gtk_radio_menu_item_new_with_label(group, ftype->title);
1102 group = gtk_radio_menu_item_get_group(GTK_RADIO_MENU_ITEM(tmp));
1103 ftype->priv->menu_item = tmp;
1104 gtk_widget_show(tmp);
1105 gtk_container_add(GTK_CONTAINER(menu), tmp);
1106 g_signal_connect(tmp, "activate", G_CALLBACK(on_filetype_change), (gpointer) ftype);
1110 static void set_error_regex(GeanyFiletype *ft, gchar *string)
1112 setptr(ft->error_regex_string, string);
1114 if (ft->priv->error_regex_compiled)
1115 regfree(&ft->priv->error_regex);
1117 ft->priv->error_regex_compiled = FALSE;
1118 /* regex will be compiled when needed */
1122 static void filetype_free(gpointer data, G_GNUC_UNUSED gpointer user_data)
1124 GeanyFiletype *ft = data;
1126 g_return_if_fail(ft != NULL);
1128 g_free(ft->name);
1129 g_free(ft->title);
1130 g_free(ft->extension);
1131 g_free(ft->mime_type);
1132 g_free(ft->comment_open);
1133 g_free(ft->comment_close);
1134 g_free(ft->comment_single);
1135 g_free(ft->context_action_cmd);
1136 g_free(ft->filecmds);
1137 g_free(ft->ftdefcmds);
1138 g_free(ft->execcmds);
1139 set_error_regex(ft, NULL);
1140 if (ft->icon)
1141 g_object_unref(ft->icon);
1143 g_strfreev(ft->pattern);
1144 g_free(ft->priv);
1145 g_free(ft);
1149 /* frees the array and all related pointers */
1150 void filetypes_free_types(void)
1152 g_return_if_fail(filetypes_array != NULL);
1153 g_return_if_fail(filetypes_hash != NULL);
1155 g_ptr_array_foreach(filetypes_array, filetype_free, NULL);
1156 g_ptr_array_free(filetypes_array, TRUE);
1157 g_hash_table_destroy(filetypes_hash);
1161 static void load_indent_settings(GeanyFiletype *ft, GKeyFile *config, GKeyFile *configh)
1163 ft->indent_width = utils_get_setting(integer, configh, config, "indentation", "width", -1);
1164 ft->indent_type = utils_get_setting(integer, configh, config, "indentation", "type", -1);
1165 /* check whether the indent type is OK */
1166 switch (ft->indent_type)
1168 case GEANY_INDENT_TYPE_TABS:
1169 case GEANY_INDENT_TYPE_SPACES:
1170 case GEANY_INDENT_TYPE_BOTH:
1171 case -1:
1172 break;
1174 default:
1175 g_warning("Invalid indent type %d in file type %s", ft->indent_type, ft->name);
1176 ft->indent_type = -1;
1177 break;
1182 static void load_settings(gint ft_id, GKeyFile *config, GKeyFile *configh)
1184 GeanyFiletype *ft = filetypes[ft_id];
1185 gchar *result;
1187 /* default extension */
1188 result = utils_get_setting(string, configh, config, "settings", "extension", NULL);
1189 if (result != NULL)
1191 setptr(filetypes[ft_id]->extension, result);
1194 /* read comment notes */
1195 result = utils_get_setting(string, configh, config, "settings", "comment_open", NULL);
1196 if (result != NULL)
1198 setptr(filetypes[ft_id]->comment_open, result);
1201 result = utils_get_setting(string, configh, config, "settings", "comment_close", NULL);
1202 if (result != NULL)
1204 setptr(filetypes[ft_id]->comment_close, result);
1207 result = utils_get_setting(string, configh, config, "settings", "comment_single", NULL);
1208 if (result != NULL)
1210 setptr(filetypes[ft_id]->comment_single, result);
1212 /* import correctly filetypes that use old-style single comments */
1213 else if (! NZV(filetypes[ft_id]->comment_close))
1215 setptr(filetypes[ft_id]->comment_single, filetypes[ft_id]->comment_open);
1216 filetypes[ft_id]->comment_open = NULL;
1219 filetypes[ft_id]->comment_use_indent = utils_get_setting(boolean, configh, config,
1220 "settings", "comment_use_indent", FALSE);
1222 /* read context action */
1223 result = utils_get_setting(string, configh, config, "settings", "context_action_cmd", NULL);
1224 if (result != NULL)
1226 setptr(filetypes[ft_id]->context_action_cmd, result);
1229 result = utils_get_setting(string, configh, config, "settings", "tag_parser", NULL);
1230 if (result != NULL)
1232 ft->lang = tm_source_file_get_named_lang(result);
1233 if (ft->lang < 0)
1234 geany_debug("Cannot find tag parser '%s' for custom filetype '%s'.", result, ft->name);
1235 g_free(result);
1238 result = utils_get_setting(string, configh, config, "settings", "lexer_filetype", NULL);
1239 if (result != NULL)
1241 ft->lexer_filetype = filetypes_lookup_by_name(result);
1242 if (!ft->lexer_filetype)
1243 geany_debug("Cannot find lexer filetype '%s' for custom filetype '%s'.", result, ft->name);
1244 g_free(result);
1247 ft->priv->symbol_list_sort_mode = utils_get_setting(integer, configh, config, "settings",
1248 "symbol_list_sort_mode", SYMBOLS_SORT_BY_NAME);
1249 ft->priv->xml_indent_tags = utils_get_setting(boolean, configh, config, "settings",
1250 "xml_indent_tags", FALSE);
1252 /* read indent settings */
1253 load_indent_settings(ft, config, configh);
1255 /* read build settings */
1256 build_load_menu(config, GEANY_BCS_FT, (gpointer)ft);
1257 build_load_menu(configh, GEANY_BCS_HOME_FT, (gpointer)ft);
1261 static void add_keys(GKeyFile *dest, const gchar *group, GKeyFile *src)
1263 gchar **keys = g_key_file_get_keys(src, group, NULL, NULL);
1264 gchar **ptr;
1266 foreach_strv(ptr, keys)
1268 gchar *key = *ptr;
1269 gchar *value = g_key_file_get_value(src, group, key, NULL);
1271 g_key_file_set_value(dest, group, key, value);
1272 g_free(value);
1274 g_strfreev(keys);
1278 static gchar *filetypes_get_filename(GeanyFiletype *ft, gboolean user)
1280 gchar *ext = filetypes_get_conf_extension(ft);
1281 gchar *f;
1283 if (user)
1284 f = utils_make_filename(app->configdir,
1285 GEANY_FILEDEFS_SUBDIR G_DIR_SEPARATOR_S, "filetypes.", ext, NULL);
1286 else
1287 f = utils_make_filename(app->datadir, "filetypes.", ext, NULL);
1289 g_free(ext);
1290 return f;
1294 static void add_group_keys(GKeyFile *kf, const gchar *group, GeanyFiletype *ft)
1296 gchar *files[2];
1297 gboolean loaded = FALSE;
1298 guint i;
1300 files[0] = filetypes_get_filename(ft, FALSE);
1301 files[1] = filetypes_get_filename(ft, TRUE);
1303 for (i = 0; i < G_N_ELEMENTS(files); i++)
1305 GKeyFile *src = g_key_file_new();
1307 if (g_key_file_load_from_file(src, files[i], G_KEY_FILE_NONE, NULL))
1309 add_keys(kf, group, src);
1310 loaded = TRUE;
1312 g_key_file_free(src);
1315 if (!loaded)
1316 geany_debug("Could not read config file %s for [%s=%s]!", files[0], group, ft->name);
1318 g_free(files[0]);
1319 g_free(files[1]);
1323 static void copy_ft_groups(GKeyFile *kf)
1325 gchar **groups = g_key_file_get_groups(kf, NULL);
1326 gchar **ptr;
1328 foreach_strv(ptr, groups)
1330 gchar *group = *ptr;
1331 gchar *name = strstr(*ptr, "=");
1332 GeanyFiletype *ft;
1334 if (!name)
1335 continue;
1337 /* terminate group at '=' */
1338 *name = 0;
1339 name++;
1340 if (!name[0])
1341 continue;
1343 ft = filetypes_lookup_by_name(name);
1344 if (ft)
1345 add_group_keys(kf, group, ft);
1347 g_strfreev(groups);
1351 /* simple wrapper function to print file errors in DEBUG mode */
1352 static void load_system_keyfile(GKeyFile *key_file, const gchar *file, GKeyFileFlags flags,
1353 GeanyFiletype *ft)
1355 GError *error = NULL;
1356 gboolean done = g_key_file_load_from_file(key_file, file, flags, &error);
1358 if (error != NULL)
1360 if (!done && !ft->priv->custom)
1361 geany_debug("Failed to open %s (%s)", file, error->message);
1363 g_error_free(error);
1364 error = NULL;
1369 /* Load the configuration file for the associated filetype id.
1370 * This should only be called when the filetype is needed, to save loading
1371 * 20+ configuration files all at once. */
1372 void filetypes_load_config(gint ft_id, gboolean reload)
1374 GKeyFile *config, *config_home;
1375 GeanyFiletypePrivate *pft;
1376 GeanyFiletype *ft;
1378 g_return_if_fail(ft_id >= 0 && ft_id < (gint) filetypes_array->len);
1380 ft = filetypes[ft_id];
1381 pft = ft->priv;
1383 /* when reloading, proceed only if the settings were already loaded */
1384 if (G_UNLIKELY(reload && ! pft->keyfile_loaded))
1385 return;
1387 /* when not reloading, load the settings only once */
1388 if (G_LIKELY(! reload && pft->keyfile_loaded))
1389 return;
1390 pft->keyfile_loaded = TRUE;
1392 config = g_key_file_new();
1393 config_home = g_key_file_new();
1395 /* highlighting uses GEANY_FILETYPES_NONE for common settings */
1396 gchar *f;
1398 f = filetypes_get_filename(ft, FALSE);
1399 load_system_keyfile(config, f, G_KEY_FILE_KEEP_COMMENTS, ft);
1401 setptr(f, filetypes_get_filename(ft, TRUE));
1402 g_key_file_load_from_file(config_home, f, G_KEY_FILE_KEEP_COMMENTS, NULL);
1403 g_free(f);
1405 /* Copy keys for any groups with [group=C] from system keyfile */
1406 copy_ft_groups(config);
1407 copy_ft_groups(config_home);
1409 load_settings(ft_id, config, config_home);
1410 highlighting_init_styles(ft_id, config, config_home);
1412 g_key_file_free(config);
1413 g_key_file_free(config_home);
1417 static gchar *filetypes_get_conf_extension(const GeanyFiletype *ft)
1419 gchar *result;
1421 if (ft->priv->custom)
1422 return g_strconcat(ft->name, ".conf", NULL);
1424 /* Handle any special extensions different from lowercase filetype->name */
1425 switch (ft->id)
1427 case GEANY_FILETYPES_CPP: result = g_strdup("cpp"); break;
1428 case GEANY_FILETYPES_CS: result = g_strdup("cs"); break;
1429 case GEANY_FILETYPES_MAKE: result = g_strdup("makefile"); break;
1430 case GEANY_FILETYPES_NONE: result = g_strdup("common"); break;
1431 /* name is Matlab/Octave */
1432 case GEANY_FILETYPES_MATLAB: result = g_strdup("matlab"); break;
1433 default:
1434 result = g_ascii_strdown(ft->name, -1);
1435 break;
1437 return result;
1441 void filetypes_save_commands(GeanyFiletype *ft)
1443 GKeyFile *config_home;
1444 gchar *fname, *data;
1446 fname = filetypes_get_filename(ft, TRUE);
1447 config_home = g_key_file_new();
1448 g_key_file_load_from_file(config_home, fname, G_KEY_FILE_KEEP_COMMENTS, NULL);
1449 build_save_menu(config_home, ft, GEANY_BCS_HOME_FT);
1450 data = g_key_file_to_data(config_home, NULL, NULL);
1451 utils_write_file(fname, data);
1452 g_free(data);
1453 g_key_file_free(config_home);
1454 g_free(fname);
1458 /* create one file filter which has each file pattern of each filetype */
1459 GtkFileFilter *filetypes_create_file_filter_all_source(void)
1461 GtkFileFilter *new_filter;
1462 guint i, j;
1464 new_filter = gtk_file_filter_new();
1465 gtk_file_filter_set_name(new_filter, _("All Source"));
1467 for (i = 0; i < filetypes_array->len; i++)
1469 if (G_UNLIKELY(i == GEANY_FILETYPES_NONE))
1470 continue;
1472 for (j = 0; filetypes[i]->pattern[j]; j++)
1474 gtk_file_filter_add_pattern(new_filter, filetypes[i]->pattern[j]);
1477 return new_filter;
1481 GtkFileFilter *filetypes_create_file_filter(const GeanyFiletype *ft)
1483 GtkFileFilter *new_filter;
1484 gint i;
1485 const gchar *title;
1487 g_return_val_if_fail(ft != NULL, NULL);
1489 new_filter = gtk_file_filter_new();
1490 title = ft->id == GEANY_FILETYPES_NONE ? _("All files") : ft->title;
1491 gtk_file_filter_set_name(new_filter, title);
1493 for (i = 0; ft->pattern[i]; i++)
1495 gtk_file_filter_add_pattern(new_filter, ft->pattern[i]);
1498 return new_filter;
1502 /* Indicates whether there is a tag parser for the filetype or not.
1503 * Only works for custom filetypes if the filetype settings have been loaded. */
1504 gboolean filetype_has_tags(GeanyFiletype *ft)
1506 g_return_val_if_fail(ft != NULL, FALSE);
1508 return ft->lang >= 0;
1512 /** Finds a filetype pointer from its @a name field.
1513 * @param name Filetype name.
1514 * @return The filetype found, or @c NULL.
1516 * @since 0.15
1518 GeanyFiletype *filetypes_lookup_by_name(const gchar *name)
1520 GeanyFiletype *ft;
1522 g_return_val_if_fail(NZV(name), NULL);
1524 ft = g_hash_table_lookup(filetypes_hash, name);
1525 if (G_UNLIKELY(ft == NULL))
1526 geany_debug("Could not find filetype '%s'.", name);
1527 return ft;
1531 static gchar *get_regex_match_string(const gchar *message, regmatch_t *pmatch, gint match_idx)
1533 return g_strndup(&message[pmatch[match_idx].rm_so],
1534 pmatch[match_idx].rm_eo - pmatch[match_idx].rm_so);
1538 static void compile_regex(GeanyFiletype *ft, regex_t *regex, gchar *regstr)
1540 gint retval = regcomp(regex, regstr, REG_EXTENDED);
1542 ft->priv->error_regex_compiled = (retval == 0); /* prevent recompilation */
1544 if (G_UNLIKELY(retval != 0))
1546 gchar buf[256];
1547 regerror(retval, regex, buf, sizeof buf);
1548 ui_set_statusbar(TRUE, _("Bad regex for filetype %s: %s"),
1549 filetypes_get_display_name(ft), buf);
1551 /* regex will be freed in set_error_regex(). */
1555 gboolean filetypes_parse_error_message(GeanyFiletype *ft, const gchar *message,
1556 gchar **filename, gint *line)
1558 gchar *regstr;
1559 gchar **tmp;
1560 GeanyDocument *doc;
1561 regex_t *regex;
1562 regmatch_t pmatch[3];
1564 if (ft == NULL)
1566 doc = document_get_current();
1567 if (doc != NULL)
1568 ft = doc->file_type;
1570 tmp = build_get_regex(build_info.grp, ft, NULL);
1571 if (tmp == NULL)
1572 return FALSE;
1573 regstr = *tmp;
1574 regex = &ft->priv->error_regex;
1576 *filename = NULL;
1577 *line = -1;
1579 if (G_UNLIKELY(! NZV(regstr)))
1580 return FALSE;
1582 if (!ft->priv->error_regex_compiled || regstr != ft->priv->last_string)
1584 compile_regex(ft, regex, regstr);
1585 ft->priv->last_string = regstr;
1587 if (!ft->priv->error_regex_compiled) /* regex error */
1588 return FALSE;
1590 if (regexec(regex, message, G_N_ELEMENTS(pmatch), pmatch, 0) != 0)
1591 return FALSE;
1593 if (pmatch[0].rm_so != -1 && pmatch[1].rm_so != -1 && pmatch[2].rm_so != -1)
1595 gchar *first, *second, *end;
1596 glong l;
1598 first = get_regex_match_string(message, pmatch, 1);
1599 second = get_regex_match_string(message, pmatch, 2);
1600 l = strtol(first, &end, 10);
1601 if (*end == '\0') /* first is purely decimals */
1603 *line = l;
1604 g_free(first);
1605 *filename = second;
1607 else
1609 l = strtol(second, &end, 10);
1610 if (*end == '\0')
1612 *line = l;
1613 g_free(second);
1614 *filename = first;
1616 else
1618 g_free(first);
1619 g_free(second);
1623 return *filename != NULL;
1627 #ifdef G_OS_WIN32
1628 static void convert_filetype_extensions_to_lower_case(gchar **patterns, gsize len)
1630 guint i;
1631 for (i = 0; i < len; i++)
1633 setptr(patterns[i], g_ascii_strdown(patterns[i], -1));
1636 #endif
1639 static void read_extensions(GKeyFile *sysconfig, GKeyFile *userconfig)
1641 guint i;
1642 gsize len = 0;
1644 /* read the keys */
1645 for (i = 0; i < filetypes_array->len; i++)
1647 gboolean userset =
1648 g_key_file_has_key(userconfig, "Extensions", filetypes[i]->name, NULL);
1649 gchar **list = g_key_file_get_string_list(
1650 (userset) ? userconfig : sysconfig, "Extensions", filetypes[i]->name, &len, NULL);
1652 g_strfreev(filetypes[i]->pattern);
1653 /* Note: we allow 'Foo=' to remove all patterns */
1654 if (!list)
1655 list = g_new0(gchar*, 1);
1656 filetypes[i]->pattern = list;
1658 #ifdef G_OS_WIN32
1659 convert_filetype_extensions_to_lower_case(filetypes[i]->pattern, len);
1660 #endif
1665 static void read_group(GKeyFile *config, const gchar *group_name, gint group_id)
1667 gchar **names = g_key_file_get_string_list(config, "Groups", group_name, NULL, NULL);
1668 gchar **name;
1670 foreach_strv(name, names)
1672 GeanyFiletype *ft = filetypes_lookup_by_name(*name);
1674 if (ft)
1676 ft->group = group_id;
1677 if (ft->priv->custom &&
1678 (group_id == GEANY_FILETYPE_GROUP_COMPILED || group_id == GEANY_FILETYPE_GROUP_SCRIPT))
1680 setptr(ft->title, NULL);
1681 filetype_make_title(ft, TITLE_SOURCE_FILE);
1684 else
1685 geany_debug("Filetype '%s' not found for group '%s'!", *name, group_name);
1687 g_strfreev(names);
1691 static void read_groups(GKeyFile *config)
1693 read_group(config, "Programming", GEANY_FILETYPE_GROUP_COMPILED);
1694 read_group(config, "Script", GEANY_FILETYPE_GROUP_SCRIPT);
1695 read_group(config, "Markup", GEANY_FILETYPE_GROUP_MARKUP);
1696 read_group(config, "Misc", GEANY_FILETYPE_GROUP_MISC);
1697 read_group(config, "None", GEANY_FILETYPE_GROUP_NONE);
1701 static void read_filetype_config(void)
1703 gchar *sysconfigfile = g_strconcat(app->datadir, G_DIR_SEPARATOR_S,
1704 "filetype_extensions.conf", NULL);
1705 gchar *userconfigfile = g_strconcat(app->configdir, G_DIR_SEPARATOR_S,
1706 "filetype_extensions.conf", NULL);
1707 GKeyFile *sysconfig = g_key_file_new();
1708 GKeyFile *userconfig = g_key_file_new();
1710 g_key_file_load_from_file(sysconfig, sysconfigfile, G_KEY_FILE_NONE, NULL);
1711 g_key_file_load_from_file(userconfig, userconfigfile, G_KEY_FILE_NONE, NULL);
1713 read_extensions(sysconfig, userconfig);
1714 read_groups(sysconfig);
1715 read_groups(userconfig);
1717 g_free(sysconfigfile);
1718 g_free(userconfigfile);
1719 g_key_file_free(sysconfig);
1720 g_key_file_free(userconfig);
1724 void filetypes_reload_extensions(void)
1726 guint i;
1728 read_filetype_config();
1730 /* Redetect filetype of any documents with none set */
1731 foreach_document(i)
1733 GeanyDocument *doc = documents[i];
1734 if (doc->file_type->id != GEANY_FILETYPES_NONE)
1735 continue;
1736 document_set_filetype(doc, filetypes_detect_from_document(doc));
1741 /** Accessor function for @ref GeanyData::filetypes_array items.
1742 * Example: @code ft = filetypes_index(GEANY_FILETYPES_C); @endcode
1743 * @param idx @c filetypes_array index.
1744 * @return The filetype, or @c NULL if @a idx is out of range.
1746 * @since 0.16
1748 GeanyFiletype *filetypes_index(gint idx)
1750 return (idx >= 0 && idx < (gint) filetypes_array->len) ? filetypes[idx] : NULL;
1754 void filetypes_reload(void)
1756 guint i;
1757 GeanyDocument *current_doc;
1759 /* reload filetype configs */
1760 for (i = 0; i < filetypes_array->len; i++)
1762 /* filetypes_load_config() will skip not loaded filetypes */
1763 filetypes_load_config(i, TRUE);
1766 current_doc = document_get_current();
1767 if (!current_doc)
1768 return;
1770 /* update document styling */
1771 foreach_document(i)
1773 if (current_doc != documents[i])
1774 document_reload_config(documents[i]);
1776 /* process the current document at last */
1777 document_reload_config(current_doc);
1781 /** Gets @c ft->name or a translation for filetype None.
1782 * @param ft .
1783 * @return .
1784 * @since Geany 0.20 */
1785 const gchar *filetypes_get_display_name(GeanyFiletype *ft)
1787 return ft->id == GEANY_FILETYPES_NONE ? _("None") : ft->name;