mcedit: fixed syntax highlighting bug in .c and .cxx syntax scripts
[midnight-commander.git] / lib / search / search.c
blobfaedffdd673cfc315d3776d1d329c73a7fc4884f
1 /*
2 Search text engine.
3 Interface functions
5 Copyright (C) 2009-2019
6 Free Software Foundation, Inc.
8 Written by:
9 Slava Zanko <slavazanko@gmail.com>, 2009
10 Andrew Borodin <aborodin@vmail.ru>, 2013
12 This file is part of the Midnight Commander.
14 The Midnight Commander is free software: you can redistribute it
15 and/or modify it under the terms of the GNU General Public License as
16 published by the Free Software Foundation, either version 3 of the License,
17 or (at your option) any later version.
19 The Midnight Commander is distributed in the hope that it will be useful,
20 but WITHOUT ANY WARRANTY; without even the implied warranty of
21 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 GNU General Public License for more details.
24 You should have received a copy of the GNU General Public License
25 along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <config.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <sys/types.h>
34 #include "lib/global.h"
35 #include "lib/strutil.h"
36 #include "lib/search.h"
37 #include "lib/util.h"
38 #ifdef HAVE_CHARSET
39 #include "lib/charsets.h"
40 #endif
42 #include "internal.h"
44 /*** global variables ****************************************************************************/
46 /*** file scope macro definitions ****************************************************************/
48 /*** file scope type declarations ****************************************************************/
50 /*** file scope variables ************************************************************************/
52 static const mc_search_type_str_t mc_search__list_types[] = {
53 {N_("No&rmal"), MC_SEARCH_T_NORMAL},
54 {N_("Re&gular expression"), MC_SEARCH_T_REGEX},
55 {N_("He&xadecimal"), MC_SEARCH_T_HEX},
56 {N_("Wil&dcard search"), MC_SEARCH_T_GLOB},
57 {NULL, MC_SEARCH_T_INVALID}
60 /*** file scope functions ************************************************************************/
62 static mc_search_cond_t *
63 mc_search__cond_struct_new (mc_search_t * lc_mc_search, const char *str,
64 gsize str_len, const char *charset)
66 mc_search_cond_t *mc_search_cond;
68 mc_search_cond = g_malloc0 (sizeof (mc_search_cond_t));
69 mc_search_cond->str = g_string_new_len (str, str_len);
70 mc_search_cond->charset = g_strdup (charset);
72 switch (lc_mc_search->search_type)
74 case MC_SEARCH_T_GLOB:
75 mc_search__cond_struct_new_init_glob (charset, lc_mc_search, mc_search_cond);
76 break;
77 case MC_SEARCH_T_NORMAL:
78 mc_search__cond_struct_new_init_normal (charset, lc_mc_search, mc_search_cond);
79 break;
80 case MC_SEARCH_T_REGEX:
81 mc_search__cond_struct_new_init_regex (charset, lc_mc_search, mc_search_cond);
82 break;
83 case MC_SEARCH_T_HEX:
84 mc_search__cond_struct_new_init_hex (charset, lc_mc_search, mc_search_cond);
85 break;
86 default:
87 break;
89 return mc_search_cond;
92 /* --------------------------------------------------------------------------------------------- */
94 static void
95 mc_search__cond_struct_free (mc_search_cond_t * mc_search_cond)
97 if (mc_search_cond->upper)
98 g_string_free (mc_search_cond->upper, TRUE);
100 if (mc_search_cond->lower)
101 g_string_free (mc_search_cond->lower, TRUE);
103 g_string_free (mc_search_cond->str, TRUE);
104 g_free (mc_search_cond->charset);
106 #ifdef SEARCH_TYPE_GLIB
107 if (mc_search_cond->regex_handle)
108 g_regex_unref (mc_search_cond->regex_handle);
109 #else /* SEARCH_TYPE_GLIB */
110 g_free (mc_search_cond->regex_handle);
111 #endif /* SEARCH_TYPE_GLIB */
113 g_free (mc_search_cond);
116 /* --------------------------------------------------------------------------------------------- */
118 static void
119 mc_search__conditions_free (GPtrArray * array)
121 g_ptr_array_foreach (array, (GFunc) mc_search__cond_struct_free, NULL);
122 g_ptr_array_free (array, TRUE);
125 /* --------------------------------------------------------------------------------------------- */
126 /*** public functions ****************************************************************************/
127 /* --------------------------------------------------------------------------------------------- */
128 /* Init search descriptor.
130 * @param original pattern to search
131 * @param original_charset charset of #original. If NULL then cp_display will be used
133 * @return new mc_search_t object. Use #mc_search_free() to free it.
136 mc_search_t *
137 mc_search_new (const gchar * original, const gchar * original_charset)
139 if (original == NULL)
140 return NULL;
142 return mc_search_new_len (original, strlen (original), original_charset);
145 /* --------------------------------------------------------------------------------------------- */
146 /* Init search descriptor.
148 * @param original pattern to search
149 * @param original_len length of #original or -1 if #original is NULL-terminated
150 * @param original_charset charset of #original. If NULL then cp_display will be used
152 * @return new mc_search_t object. Use #mc_search_free() to free it.
155 mc_search_t *
156 mc_search_new_len (const gchar * original, gsize original_len, const gchar * original_charset)
158 mc_search_t *lc_mc_search;
160 if (original == NULL || original_len == 0)
161 return NULL;
163 lc_mc_search = g_new0 (mc_search_t, 1);
164 lc_mc_search->original = g_strndup (original, original_len);
165 lc_mc_search->original_len = original_len;
166 #ifdef HAVE_CHARSET
167 lc_mc_search->original_charset =
168 g_strdup (original_charset != NULL
169 && *original_charset != '\0' ? original_charset : cp_display);
170 #else
171 (void) original_charset;
172 #endif
174 return lc_mc_search;
177 /* --------------------------------------------------------------------------------------------- */
179 void
180 mc_search_free (mc_search_t * lc_mc_search)
182 if (lc_mc_search == NULL)
183 return;
185 g_free (lc_mc_search->original);
186 #ifdef HAVE_CHARSET
187 g_free (lc_mc_search->original_charset);
188 #endif
189 g_free (lc_mc_search->error_str);
191 if (lc_mc_search->conditions != NULL)
192 mc_search__conditions_free (lc_mc_search->conditions);
194 #ifdef SEARCH_TYPE_GLIB
195 if (lc_mc_search->regex_match_info != NULL)
196 g_match_info_free (lc_mc_search->regex_match_info);
197 #else /* SEARCH_TYPE_GLIB */
198 g_free (lc_mc_search->regex_match_info);
199 #endif /* SEARCH_TYPE_GLIB */
201 if (lc_mc_search->regex_buffer != NULL)
202 g_string_free (lc_mc_search->regex_buffer, TRUE);
204 g_free (lc_mc_search);
207 /* --------------------------------------------------------------------------------------------- */
209 gboolean
210 mc_search_prepare (mc_search_t * lc_mc_search)
212 GPtrArray *ret;
214 ret = g_ptr_array_new ();
215 #ifdef HAVE_CHARSET
216 if (lc_mc_search->is_all_charsets)
218 gsize loop1;
220 for (loop1 = 0; loop1 < codepages->len; loop1++)
222 const char *id;
223 gsize recoded_str_len;
224 gchar *buffer;
226 id = ((codepage_desc *) g_ptr_array_index (codepages, loop1))->id;
227 if (g_ascii_strcasecmp (id, lc_mc_search->original_charset) == 0)
229 g_ptr_array_add (ret,
230 mc_search__cond_struct_new (lc_mc_search, lc_mc_search->original,
231 lc_mc_search->original_len,
232 lc_mc_search->original_charset));
233 continue;
236 buffer =
237 mc_search__recode_str (lc_mc_search->original, lc_mc_search->original_len,
238 lc_mc_search->original_charset, id, &recoded_str_len);
240 g_ptr_array_add (ret,
241 mc_search__cond_struct_new (lc_mc_search, buffer,
242 recoded_str_len, id));
243 g_free (buffer);
246 else
248 g_ptr_array_add (ret,
249 mc_search__cond_struct_new (lc_mc_search, lc_mc_search->original,
250 lc_mc_search->original_len,
251 lc_mc_search->original_charset));
253 #else
254 g_ptr_array_add (ret,
255 mc_search__cond_struct_new (lc_mc_search, lc_mc_search->original,
256 lc_mc_search->original_len,
257 str_detect_termencoding ()));
258 #endif
259 lc_mc_search->conditions = ret;
261 return (lc_mc_search->error == MC_SEARCH_E_OK);
264 /* --------------------------------------------------------------------------------------------- */
267 * Carries out the search.
269 * Returns TRUE if found.
271 * Returns FALSE if not found. In this case, lc_mc_search->error reveals
272 * the reason:
274 * - MC_SEARCH_E_NOTFOUND: the pattern isn't in the subject string.
275 * - MC_SEARCH_E_ABORT: the user aborted the search.
276 * - For any other reason (but not for the above two!): the description
277 * is in lc_mc_search->error_str.
279 gboolean
280 mc_search_run (mc_search_t * lc_mc_search, const void *user_data,
281 gsize start_search, gsize end_search, gsize * found_len)
283 gboolean ret = FALSE;
285 if (lc_mc_search == NULL || user_data == NULL)
286 return FALSE;
287 if (!mc_search_is_type_avail (lc_mc_search->search_type))
289 mc_search_set_error (lc_mc_search, MC_SEARCH_E_INPUT, "%s", _(STR_E_UNKNOWN_TYPE));
290 return FALSE;
292 #ifdef SEARCH_TYPE_GLIB
293 if (lc_mc_search->regex_match_info != NULL)
295 g_match_info_free (lc_mc_search->regex_match_info);
296 lc_mc_search->regex_match_info = NULL;
298 #endif /* SEARCH_TYPE_GLIB */
300 mc_search_set_error (lc_mc_search, MC_SEARCH_E_OK, NULL);
302 if ((lc_mc_search->conditions == NULL) && !mc_search_prepare (lc_mc_search))
303 return FALSE;
305 switch (lc_mc_search->search_type)
307 case MC_SEARCH_T_NORMAL:
308 ret = mc_search__run_normal (lc_mc_search, user_data, start_search, end_search, found_len);
309 break;
310 case MC_SEARCH_T_REGEX:
311 ret = mc_search__run_regex (lc_mc_search, user_data, start_search, end_search, found_len);
312 break;
313 case MC_SEARCH_T_GLOB:
314 ret = mc_search__run_glob (lc_mc_search, user_data, start_search, end_search, found_len);
315 break;
316 case MC_SEARCH_T_HEX:
317 ret = mc_search__run_hex (lc_mc_search, user_data, start_search, end_search, found_len);
318 break;
319 default:
320 break;
322 return ret;
325 /* --------------------------------------------------------------------------------------------- */
327 gboolean
328 mc_search_is_type_avail (mc_search_type_t search_type)
330 switch (search_type)
332 case MC_SEARCH_T_GLOB:
333 case MC_SEARCH_T_NORMAL:
334 case MC_SEARCH_T_REGEX:
335 case MC_SEARCH_T_HEX:
336 return TRUE;
337 default:
338 break;
340 return FALSE;
343 /* --------------------------------------------------------------------------------------------- */
345 const mc_search_type_str_t *
346 mc_search_types_list_get (size_t * num)
348 /* don't count last NULL item */
349 if (num != NULL)
350 *num = G_N_ELEMENTS (mc_search__list_types) - 1;
352 return mc_search__list_types;
355 /* --------------------------------------------------------------------------------------------- */
357 GString *
358 mc_search_prepare_replace_str (mc_search_t * lc_mc_search, GString * replace_str)
360 GString *ret;
362 if (replace_str == NULL || replace_str->len == 0)
363 return g_string_new ("");
365 if (lc_mc_search == NULL)
366 return g_string_new_len (replace_str->str, replace_str->len);
368 switch (lc_mc_search->search_type)
370 case MC_SEARCH_T_REGEX:
371 ret = mc_search_regex_prepare_replace_str (lc_mc_search, replace_str);
372 break;
373 case MC_SEARCH_T_GLOB:
374 ret = mc_search_glob_prepare_replace_str (lc_mc_search, replace_str);
375 break;
376 case MC_SEARCH_T_NORMAL:
377 ret = mc_search_normal_prepare_replace_str (lc_mc_search, replace_str);
378 break;
379 case MC_SEARCH_T_HEX:
380 ret = mc_search_hex_prepare_replace_str (lc_mc_search, replace_str);
381 break;
382 default:
383 ret = g_string_new_len (replace_str->str, replace_str->len);
384 break;
386 return ret;
389 /* --------------------------------------------------------------------------------------------- */
391 char *
392 mc_search_prepare_replace_str2 (mc_search_t * lc_mc_search, const char *replace_str)
394 GString *ret;
395 GString *replace_str2;
397 replace_str2 = g_string_new (replace_str);
398 ret = mc_search_prepare_replace_str (lc_mc_search, replace_str2);
399 g_string_free (replace_str2, TRUE);
400 return (ret != NULL) ? g_string_free (ret, FALSE) : NULL;
403 /* --------------------------------------------------------------------------------------------- */
405 gboolean
406 mc_search_is_fixed_search_str (mc_search_t * lc_mc_search)
408 if (lc_mc_search == NULL)
409 return FALSE;
410 switch (lc_mc_search->search_type)
412 case MC_SEARCH_T_REGEX:
413 case MC_SEARCH_T_GLOB:
414 return FALSE;
415 default:
416 return TRUE;
420 /* --------------------------------------------------------------------------------------------- */
421 /* Search specified pattern in specified string.
423 * @param pattern string to search
424 * @param pattern_charset charset of #pattern. If NULL then cp_display will be used
425 * @param str string where search #pattern
426 * @param search type (normal, regex, hex or glob)
428 * @return TRUE if found is successful, FALSE otherwise.
431 gboolean
432 mc_search (const gchar * pattern, const gchar * pattern_charset, const gchar * str,
433 mc_search_type_t type)
435 gboolean ret;
436 mc_search_t *search;
438 if (str == NULL)
439 return FALSE;
441 search = mc_search_new (pattern, pattern_charset);
442 if (search == NULL)
443 return FALSE;
445 search->search_type = type;
446 search->is_case_sensitive = TRUE;
448 if (type == MC_SEARCH_T_GLOB)
449 search->is_entire_line = TRUE;
451 ret = mc_search_run (search, str, 0, strlen (str), NULL);
452 mc_search_free (search);
453 return ret;
456 /* --------------------------------------------------------------------------------------------- */
459 mc_search_getstart_result_by_num (mc_search_t * lc_mc_search, int lc_index)
461 if (lc_mc_search == NULL)
462 return 0;
463 if (lc_mc_search->search_type == MC_SEARCH_T_NORMAL)
464 return 0;
465 #ifdef SEARCH_TYPE_GLIB
467 gint start_pos;
468 gint end_pos;
470 g_match_info_fetch_pos (lc_mc_search->regex_match_info, lc_index, &start_pos, &end_pos);
471 return (int) start_pos;
473 #else /* SEARCH_TYPE_GLIB */
474 return lc_mc_search->iovector[lc_index * 2];
475 #endif /* SEARCH_TYPE_GLIB */
478 /* --------------------------------------------------------------------------------------------- */
481 mc_search_getend_result_by_num (mc_search_t * lc_mc_search, int lc_index)
483 if (lc_mc_search == NULL)
484 return 0;
485 if (lc_mc_search->search_type == MC_SEARCH_T_NORMAL)
486 return 0;
487 #ifdef SEARCH_TYPE_GLIB
489 gint start_pos;
490 gint end_pos;
492 g_match_info_fetch_pos (lc_mc_search->regex_match_info, lc_index, &start_pos, &end_pos);
493 return (int) end_pos;
495 #else /* SEARCH_TYPE_GLIB */
496 return lc_mc_search->iovector[lc_index * 2 + 1];
497 #endif /* SEARCH_TYPE_GLIB */
500 /* --------------------------------------------------------------------------------------------- */
502 * Replace an old error code and message of an mc_search_t object.
504 * @param mc_search mc_search_t object
505 * @param code error code, one of mc_search_error_t values
506 * @param format format of error message. If NULL, the old error string is free'd and become NULL
509 void
510 mc_search_set_error (mc_search_t * lc_mc_search, mc_search_error_t code, const gchar * format, ...)
512 lc_mc_search->error = code;
514 MC_PTR_FREE (lc_mc_search->error_str);
516 if (format != NULL)
518 va_list args;
520 va_start (args, format);
521 lc_mc_search->error_str = g_strdup_vprintf (format, args);
522 va_end (args);
526 /* --------------------------------------------------------------------------------------------- */