2 * Copyright 2012 Samsung Electronics Co., Ltd
4 * Licensed under the Flora License, Version 1.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://floralicense.org/license/
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
29 #include "mf-ug-main.h"
30 #include "mf-ug-search.h"
31 #include "mf-ug-search-internal.h"
32 #include "mf-ug-fs-util.h"
34 #define APPEND_SIZE 2 /* for null and slash */
35 #define MF_ERR_BUF 256
37 #define NORMALIZE_OPTION G_NORMALIZE_NFD
39 #ifdef CHECK_RESTRICTED_PATH
41 * This code should be revised.
42 * How to get restricted path information?
43 * I think this module should not depend on other lib(except glib and stdlib).
45 #define ROOT_UMS "/opt/usr/media"
46 #define ROOT_MMC "/opt/storage/sdcard"
47 #endif /* CHECK_RESTRICTED_PATH */
49 int flagSearchMsg = 1;
50 pthread_mutex_t gLockSearchMsg;
51 pthread_cond_t gCondSearchMsg;
53 static void __mf_ug_search_tx_wait();
54 static void __mf_ug_search_result_publish_msg(mf_search_pipe_msg_type type, void *result, void *user_data);
56 inline static void __mf_ug_search_cmd_lock(ms_handle_t *handle)
58 if (handle && handle->cmd_lock) {
59 g_mutex_lock(handle->cmd_lock);
64 inline static void __mf_ug_search_cmd_unlock(ms_handle_t *handle)
66 if (handle && handle->cmd_lock) {
67 g_mutex_unlock(handle->cmd_lock);
72 inline static void __mf_ug_search_thread_lock(ms_handle_t *handle)
74 if (handle && handle->thread_mutex) {
75 g_mutex_lock(handle->thread_mutex);
80 inline static void __mf_ug_search_thread_unlock(ms_handle_t *handle)
82 if (handle && handle->thread_mutex) {
83 g_mutex_unlock(handle->thread_mutex);
88 inline static void __mf_ug_search_args_free(ms_args_t *args)
91 if (args->root_path) {
92 g_list_foreach(args->root_path, (GFunc) g_free, NULL);
93 g_list_free(args->root_path);
94 args->root_path = NULL;
105 inline static void __mf_ug_search_result_free(mf_search_result_t *result)
108 if (result->current_dir) {
109 g_free(result->current_dir);
110 result->current_dir = NULL;
112 if (result->dir_list) {
113 g_list_foreach(result->dir_list, (GFunc) g_free, NULL);
114 g_list_free(result->dir_list);
115 result->dir_list = NULL;
117 if (result->file_list) {
118 g_list_foreach(result->file_list, (GFunc) g_free, NULL);
119 g_list_free(result->file_list);
120 result->file_list = NULL;
127 #ifdef CHECK_RESTRICTED_PATH
128 gboolean __mf_ug_search_check_licet_path(const char *path)
130 return (gboolean) (strstr(path, ROOT_UMS) || strstr(path, ROOT_MMC));
132 #endif /*CHECK_RESTRICTED_PATH*/
135 /*This function is for testing and should be revised for performance before applying*/
136 static inline gboolean __mf_ug_search_has_nonspacing_mark(const char *nstr)
139 const char *p_str = nstr;
140 while (p_str && *p_str) {
142 uc = g_utf8_get_char(p_str);
143 if (g_unichar_type(uc) == G_UNICODE_NON_SPACING_MARK) {
146 p_str = g_utf8_next_char(p_str);
153 static gboolean __mf_ug_search_NFD_ext(const char *str, const char *needle)
165 n_len = strlen(needle);
172 char *pdot = strrchr(str, '.');
176 } else if (pdot != str) {
178 ext = g_strdup(pdot+1);
179 if (g_strcmp0(ext, needle)== 0) {
194 static gboolean __mf_ug_search_NFD_strstr(const char *str, const char *needle)
207 n_len = strlen(needle);
216 if (__mf_ug_search_has_nonspacing_mark(str)) {
217 const char *p_str = str;
218 const char *end = p_str + s_len - n_len;
220 while (p_str && p_str <= end && *p_str) {
221 const char *s = p_str;
222 const char *n = needle;
226 sc = g_utf8_get_char(s);
227 nc = g_utf8_get_char(n);
228 if (g_unichar_type(sc) == G_UNICODE_NON_SPACING_MARK) {
229 if (g_unichar_type(nc) == G_UNICODE_NON_SPACING_MARK) {
233 s = g_utf8_next_char(s);
234 n = g_utf8_next_char(n);
237 s = g_utf8_next_char(s);
239 } else if (sc != nc) {
242 s = g_utf8_next_char(s);
243 n = g_utf8_next_char(n);
252 p_str = g_utf8_next_char(p_str);
255 return (gboolean) (!(!strstr(str, needle)));
260 static GList *__mf_ug_search_do_find(const char *root, const char *needle, mf_search_option option, ms_handle_t *handle)
262 DIR *directory = NULL;
263 GList *candidate = NULL;
265 char *up_needle = NULL;
266 char *up_name = NULL;
269 ms_error("handle is NULL");
273 if (!handle->result) {
274 ms_error("handle->result is NULL");
278 if (!root || !needle) {
279 ms_error("invaild args");
283 if (!g_file_test(root, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) {
284 ms_error("invaild root_path : %s", root);
288 directory = opendir(root);
290 mf_search_result_t *result = NULL;
291 struct dirent *entry = NULL;
293 result = handle->result;
294 __mf_ug_search_thread_lock(handle);
295 if (result->current_dir) {
296 g_free(result->current_dir);
298 result->current_dir = g_strdup(root);
299 __mf_ug_search_thread_unlock(handle);
300 while ((entry = readdir(directory)) != NULL) {
301 if (!(option & MF_SEARCH_OPT_HIDDEN) && (0 == strncmp(entry->d_name, ".", 1))) {
302 ms_debug("[%s] is hidden file. Skip it", entry->d_name);
306 if (handle->is_stop == TRUE) {
307 ms_debug("break from do find");
311 if (entry->d_type & DT_REG) {
312 if (option & MF_SEARCH_OPT_FILE) {
313 __mf_ug_search_thread_lock(handle);
314 result->total_count++;
315 __mf_ug_search_thread_unlock(handle);
317 up_name = g_utf8_strup(entry->d_name, strlen(entry->d_name));
318 up_needle = g_utf8_strup(needle, strlen(needle));
321 /* should we check the return value for further use? */
322 gchar *nor_str = g_utf8_normalize(up_name, -1, NORMALIZE_OPTION);
323 if (__mf_ug_search_NFD_strstr(nor_str, up_needle))
326 gssize len = strlen(root) + strlen(entry->d_name) + APPEND_SIZE; /* for null and slash*/
327 path = g_malloc(sizeof(gchar) * len);
329 g_snprintf(path, len, "%s/%s", root, entry->d_name);
331 __mf_ug_search_thread_lock(handle);
332 result->file_list = g_list_append(result->file_list, (gpointer) path);
333 result->is_end = FALSE;
334 __mf_ug_search_thread_unlock(handle);
336 __mf_ug_search_tx_wait();
337 __mf_ug_search_result_publish_msg(MF_SEARCH_PIPE_MSG_RESULT_REPORT, result,
338 handle->args->user_data);
340 /*1 TODO: how can i handle else case?*/
347 }else if (option & MF_SEARCH_OPT_EXT) {
348 __mf_ug_search_thread_lock(handle);
349 result->total_count++;
350 __mf_ug_search_thread_unlock(handle);
352 up_name = g_utf8_strup(entry->d_name, strlen(entry->d_name));
353 up_needle = g_utf8_strup(needle, strlen(needle));
356 /* should we check the return value for further use? */
357 gchar *nor_str = g_utf8_normalize(up_name, -1, NORMALIZE_OPTION);
358 if (__mf_ug_search_NFD_ext(nor_str, up_needle))
361 gssize len = strlen(root) + strlen(entry->d_name) + APPEND_SIZE; /* for null and slash*/
362 path = g_malloc(sizeof(gchar) * len);
364 g_snprintf(path, len, "%s/%s", root, entry->d_name);
366 __mf_ug_search_thread_lock(handle);
367 result->file_list = g_list_append(result->file_list, (gpointer) path);
368 result->is_end = FALSE;
369 __mf_ug_search_thread_unlock(handle);
371 __mf_ug_search_tx_wait();
372 __mf_ug_search_result_publish_msg(MF_SEARCH_PIPE_MSG_RESULT_REPORT, result,
373 handle->args->user_data);
375 /*1 TODO: how can i handle else case?*/
384 } else if (entry->d_type & DT_DIR) {
388 len = strlen(entry->d_name);
389 /*skip current and upper directory*/
390 if (0 == strncmp(entry->d_name, ".", strlen(".")) || 0 == strncmp(entry->d_name, "..", strlen(".."))) {
393 /* we are not going to search /opt/media/SLP_Debug folder */
394 if ((strlen(result->current_dir) == strlen(PHONE_FOLDER)) && (strcmp(result->current_dir, PHONE_FOLDER) == 0)
395 && (strlen(entry->d_name) == strlen(DEBUG_FOLDER)) && (strcmp(entry->d_name, DEBUG_FOLDER) == 0)) {
396 ms_debug("[%s] is hidden folder. Skip it", entry->d_name);
400 len = strlen(root) + strlen(entry->d_name) + APPEND_SIZE; /* for null and slash */
401 path = g_malloc(sizeof(gchar) * len);
403 g_snprintf(path, len, "%s/%s", root, entry->d_name);
404 candidate = g_list_append(candidate, (gpointer) path);
406 /*1 TODO: how can i handle else case?*/
407 if (option & MF_SEARCH_OPT_DIR) {
408 __mf_ug_search_thread_lock(handle);
409 result->total_count++;
410 __mf_ug_search_thread_unlock(handle);
412 up_name = g_utf8_strup(entry->d_name, strlen(entry->d_name));
413 if (needle && strlen(needle)) {
414 up_needle = g_utf8_strup(needle, strlen(needle));
415 gchar *nor_str = g_utf8_normalize(up_name, -1, NORMALIZE_OPTION);
416 if (__mf_ug_search_NFD_strstr(nor_str, up_needle))
418 __mf_ug_search_thread_lock(handle);
419 result->dir_list = g_list_append(result->dir_list, (gpointer) g_strdup(path));
420 result->is_end = FALSE;
421 __mf_ug_search_thread_unlock(handle);
422 __mf_ug_search_tx_wait();
423 __mf_ug_search_result_publish_msg(MF_SEARCH_PIPE_MSG_RESULT_REPORT, result, handle->args->user_data);
445 static gpointer __mf_ug_search_find_thread(gpointer data)
447 ms_handle_t *handle = (ms_handle_t *) data;
449 ms_args_t *args = NULL;
450 mf_search_result_t *result = NULL;
452 result = handle->result;
455 if (args && result) {
457 GList *candidate = NULL; /*use this list as stack*/
458 root = args->root_path;
460 char *path = (char *)root->data;
462 /*push root paths to stack*/
463 candidate = g_list_append(candidate, (gpointer) g_strdup(path));
465 root = g_list_next(root);
469 GList *new_list = NULL;
473 __mf_ug_search_thread_lock(handle);
474 if (handle->is_stop) {
475 __mf_ug_search_thread_unlock(handle);
476 result->is_end = TRUE;
477 goto MF_FIND_THREAD_EXIT;
479 __mf_ug_search_thread_unlock(handle);
481 list = g_list_first(candidate);
482 /*pop one path from stack*/
483 candidate = g_list_remove_link(candidate, list);
484 item = (gchar *) list->data;
486 ms_debug("current : %s", item);
488 __mf_ug_search_thread_lock(handle);
489 if (result->current_dir) {
490 g_free(result->current_dir);
492 result->current_dir = g_strdup(item);
493 __mf_ug_search_thread_unlock(handle);
495 /*publish root change message here*/
496 __mf_ug_search_tx_wait();
497 if (handle->is_stop) {
498 result->is_end = TRUE;
499 goto MF_FIND_THREAD_EXIT;
501 __mf_ug_search_result_publish_msg(MF_SEARCH_PIPE_MSG_ROOT_CHANGE, item, args->user_data);
502 new_list = __mf_ug_search_do_find(item, args->needle, args->option, handle);
508 /*push new paths to stack*/
509 candidate = g_list_concat(new_list, candidate);
512 __mf_ug_search_thread_lock(handle);
513 result->is_end = TRUE;
514 __mf_ug_search_thread_unlock(handle);
515 __mf_ug_search_tx_wait();
516 __mf_ug_search_result_publish_msg(MF_SEARCH_PIPE_MSG_FINISHED, handle->result, args->user_data);
519 g_list_foreach(candidate, (GFunc) g_free, NULL);
520 g_list_free(candidate);
524 ms_error("args : %p or result : %p is not allocated yet!!", handle->args, handle->result);
527 /*g_thread_exit(NULL);*/
531 int _mf_ug_search_init(ms_handle_t **handle)
534 ms_handle_t *ms_handle = NULL;
539 return MF_SEARCH_ERROR_INVAL_P;
542 ms_handle = g_malloc0(sizeof(ms_handle_t));
543 if (ms_handle == NULL) {
544 ms_error("Fail to allocate memory for handle ");
546 return MF_SEARCH_ERROR_ALLOC;
549 ms_handle->state = MF_SEARCH_STATE_INIT;
550 ms_handle->is_stop = FALSE;
552 lock = g_mutex_new();
554 ms_error("Fail to create cmd_lock");
556 return MF_SEARCH_ERROR_ALLOC;
558 ms_handle->cmd_lock = lock;
562 ms_info("Success to make search handle : %p", ms_handle);
563 return MF_SEARCH_ERROR_NONE;
566 int _mf_ug_search_start(ms_handle_t *handle, const char **root_path, unsigned int path_num, const char *needle, mf_search_option option, void *user_data)
568 ms_args_t *args = NULL;
569 mf_search_result_t *result = NULL;
570 mf_search_option l_opt = MF_SEARCH_OPT_NONE;
571 int ret = MF_SEARCH_ERROR_NONE;
575 ms_error("handle is NULL");
576 return MF_SEARCH_ERROR_INVAL_P;
579 if (handle->state != MF_SEARCH_STATE_INIT) {
580 ms_error("invaild state : %d", handle->state);
581 return MF_SEARCH_ERROR_INVAL_S;
584 if (!root_path || !needle || path_num < 1) {
585 ms_error("invaild arguments - root[%p], path_num[%d], needle[%p]", root_path, path_num, needle);
586 return MF_SEARCH_ERROR_INVAL_P;
589 __mf_ug_search_cmd_lock(handle);
592 __mf_ug_search_args_free(handle->args);
595 handle->args = args = g_malloc0(sizeof(ms_args_t));
597 ms_error("fail to alloc args");
598 ret = MF_SEARCH_ERROR_ALLOC;
602 if (option == MF_SEARCH_OPT_NONE) {
603 ms_warn("option is MF_SEARCH_OPT_NONE, set all option automatically ");
604 l_opt = MF_SEARCH_OPT_HIDDEN | MF_SEARCH_OPT_DIR | MF_SEARCH_OPT_FILE;
609 for (i = 0; i < path_num; i++) {
610 const char *path = root_path[i];
611 ms_debug("%d th root path is %s", i, path);
612 #ifdef CHECK_RESTRICTED_PATH
613 if (!__mf_ug_search_check_licet_path(path)) {
614 ms_error("%dth root path[%s] is invaild", i, path);
615 ret = MF_SEARCH_ERROR_INVAL_P;
618 #endif /*CHECK_RESTRICTED_PATH*/
619 if (g_file_test(path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)
620 && ((l_opt & MF_SEARCH_OPT_HIDDEN) || strncmp(path, ".", 1))
622 gchar *new_path = NULL;
623 gssize len = strlen(path);
625 if (path[len - 1] == '/') {
626 new_path = g_strndup(path, len - 1);
628 new_path = g_strndup(path, len);
630 args->root_path = g_list_append(args->root_path, (gpointer) new_path);
632 ms_error("Fail to test %dthe root path[%s]", i, path);
633 ret = MF_SEARCH_ERROR_INVAL_P;
637 args->user_data = user_data;
640 args->needle = g_utf8_normalize(needle, -1, NORMALIZE_OPTION);
642 ms_error("fail to alloc args->needle");
646 args->option = l_opt;
648 if (handle->result) {
649 __mf_ug_search_result_free(handle->result);
650 handle->result = NULL;
652 handle->result = result = g_malloc0(sizeof(ms_args_t));
654 ms_error("fail to alloc result");
655 ret = MF_SEARCH_ERROR_ALLOC;
659 handle->thread_mutex = g_mutex_new();
660 if (!handle->thread_mutex) {
661 ms_error("fail to alloc handle->thread_mutex");
662 ret = MF_SEARCH_ERROR_ALLOC;
666 handle->is_stop = FALSE;
667 handle->result->is_end = FALSE;
669 /*create thread for find item.*/
670 handle->thread_h = g_thread_create(__mf_ug_search_find_thread, handle, TRUE, NULL);
671 if (!handle->thread_h) {
672 ms_error("fail to create __mf_ug_search_find_thread");
673 ret = MF_SEARCH_ERROR_INTERNAL;
676 /*create idler for reporting find result.*/
677 handle->state = MF_SEARCH_STATE_SEARCH;
678 __mf_ug_search_cmd_unlock(handle);
679 return MF_SEARCH_ERROR_NONE;
683 __mf_ug_search_args_free(args);
688 __mf_ug_search_result_free(result);
689 handle->result = NULL;
692 if (handle->thread_mutex) {
693 g_mutex_free(handle->thread_mutex);
694 handle->thread_mutex = NULL;
697 if (handle->thread_h) {
698 __mf_ug_search_thread_lock(handle);
699 handle->is_stop = TRUE;
700 __mf_ug_search_thread_unlock(handle);
701 g_thread_join(handle->thread_h);
702 handle->thread_h = NULL;
704 __mf_ug_search_cmd_unlock(handle);
709 int _mf_ug_search_stop(ms_handle_t *handle)
714 ms_error("handle is NULL");
715 return MF_SEARCH_ERROR_INVAL_P;
718 if (handle->state != MF_SEARCH_STATE_SEARCH) {
719 ms_error("invaild state : %d", handle->state);
720 return MF_SEARCH_ERROR_INVAL_S;
723 __mf_ug_search_cmd_lock(handle);
725 __mf_ug_search_thread_lock(handle);
726 handle->is_stop = TRUE;
727 __mf_ug_search_thread_unlock(handle);
729 pthread_mutex_lock(&gLockSearchMsg);
730 if (flagSearchMsg == 0) {
732 pthread_cond_signal(&gCondSearchMsg);
734 pthread_mutex_unlock(&gLockSearchMsg);
736 if (handle->thread_h) {
737 g_thread_join(handle->thread_h);
738 handle->thread_h = NULL;
741 if (handle->thread_mutex) {
742 g_mutex_free(handle->thread_mutex);
743 handle->thread_mutex = NULL;
747 __mf_ug_search_args_free(handle->args);
750 if (handle->result) {
751 __mf_ug_search_result_free(handle->result);
752 handle->result = NULL;
755 handle->state = MF_SEARCH_STATE_INIT;
756 handle->is_stop = FALSE;
758 __mf_ug_search_cmd_unlock(handle);
760 return MF_SEARCH_ERROR_NONE;
763 void _mf_ug_search_finalize(ms_handle_t **handle)
765 ms_handle_t *ms_handle = *handle;
770 ms_warn("invaild handle");
774 if (ms_handle->state == MF_SEARCH_STATE_SEARCH) {
775 mf_ug_search_stop(ms_handle);
777 /* __mf_ug_search_cmd_lock(ms_handle); */
778 /* __mf_ug_search_cmd_unlock(ms_handle); */
780 if (ms_handle->cmd_lock) {
781 g_mutex_free(ms_handle->cmd_lock);
782 ms_handle->cmd_lock = NULL;
790 /*+++++++++++++++++++++++++ UTIL APIs ++++++++++++++++++++++++++++++ */
791 static void __mf_ug_search_tx_wait()
793 pthread_mutex_lock(&gLockSearchMsg);
794 while (flagSearchMsg == 0) {
795 pthread_cond_wait(&gCondSearchMsg, &gLockSearchMsg);
798 pthread_mutex_unlock(&gLockSearchMsg);
801 static void __mf_ug_search_result_publish_msg(mf_search_pipe_msg_type type, void *result, void *user_data)
803 ugData *ugd = (ugData *)user_data;
804 /*generate message block*/
805 mf_search_pipe_msg msg;
806 memset(&msg, 0, sizeof(mf_search_pipe_msg));
808 msg.mf_sp_msg_type = type;
809 if (msg.mf_sp_msg_type == MF_SEARCH_PIPE_MSG_RESULT_REPORT) {
810 msg.report_result = g_strdup((gchar *) result);
811 msg.current_path = NULL;
812 } else if (msg.mf_sp_msg_type == MF_SEARCH_PIPE_MSG_ROOT_CHANGE) {
813 msg.report_result = NULL;
814 msg.current_path = g_strdup((gchar *) result);
815 ms_debug("current path is %s", msg.current_path);
816 } else if (msg.mf_sp_msg_type == MF_SEARCH_PIPE_MSG_FINISHED) {
817 msg.report_result = result;
818 msg.current_path = NULL;
820 msg.report_result = NULL;
821 msg.current_path = NULL;
824 /*write message to pipe*/
825 ecore_pipe_write(ugd->ug_UiGadget.ug_pSyncPipe, &msg, sizeof(msg));
828 gchar *_mf_ug_search_result_dir_get(mf_search_result_t * result)
833 list = result->dir_list;
834 if (list && list->data) {
835 gchar *item = (gchar *) list->data;
836 result->dir_list = g_list_remove(list, item);
843 gchar *_mf_ug_search_result_file_get(mf_search_result_t * result)
848 list = result->file_list;
849 if (list && list->data) {
850 gchar *item = (gchar *) list->data;
851 result->file_list = g_list_remove(list, item);
858 gboolean _mf_ug_search_result_is_end(mf_search_result_t *result)
860 gboolean end = FALSE;
862 end = result->is_end;
867 guint _mf_ug_search_result_total_count_get(mf_search_result_t *result)
871 count = result->total_count;
876 gchar *_mf_ug_search_result_current_dir_get(mf_search_result_t * result)
880 if (result->current_dir) {
881 c_dir = result->current_dir;
882 result->current_dir = NULL;