find.c 55 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772
  1. /*
  2. Find file command for the Midnight Commander
  3. Copyright (C) 1995, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
  4. 2006, 2007, 2011
  5. The Free Software Foundation, Inc.
  6. Written by:
  7. Miguel de Icaza, 1995
  8. This file is part of the Midnight Commander.
  9. The Midnight Commander is free software: you can redistribute it
  10. and/or modify it under the terms of the GNU General Public License as
  11. published by the Free Software Foundation, either version 3 of the License,
  12. or (at your option) any later version.
  13. The Midnight Commander is distributed in the hope that it will be useful,
  14. but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. 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, see <http://www.gnu.org/licenses/>.
  19. */
  20. /** \file find.c
  21. * \brief Source: Find file command
  22. */
  23. #include <config.h>
  24. #include <ctype.h>
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <string.h>
  28. #include <fcntl.h>
  29. #include <sys/stat.h>
  30. #include "lib/global.h"
  31. #include "lib/tty/tty.h"
  32. #include "lib/tty/key.h"
  33. #include "lib/skin.h"
  34. #include "lib/search.h"
  35. #include "lib/mcconfig.h"
  36. #include "lib/vfs/vfs.h"
  37. #include "lib/strutil.h"
  38. #include "lib/widget.h"
  39. #include "lib/util.h" /* canonicalize_pathname() */
  40. #include "src/setup.h" /* verbose */
  41. #include "src/history.h" /* MC_HISTORY_SHARED_SEARCH */
  42. #include "src/main.h" /* do_cd */
  43. #include "dir.h"
  44. #include "cmd.h" /* view_file_at_line */
  45. #include "midnight.h" /* current_panel */
  46. #include "boxes.h"
  47. #include "panelize.h"
  48. #include "find.h"
  49. /*** global variables ****************************************************************************/
  50. /*** file scope macro definitions ****************************************************************/
  51. /* Size of the find window */
  52. #define FIND2_Y (LINES - 4)
  53. #define FIND2_X_USE (FIND2_X - 20)
  54. /*** file scope type declarations ****************************************************************/
  55. /* A couple of extra messages we need */
  56. enum
  57. {
  58. B_STOP = B_USER + 1,
  59. B_AGAIN,
  60. B_PANELIZE,
  61. B_TREE,
  62. B_VIEW
  63. };
  64. typedef enum
  65. {
  66. FIND_CONT = 0,
  67. FIND_SUSPEND,
  68. FIND_ABORT
  69. } FindProgressStatus;
  70. /* find file options */
  71. typedef struct
  72. {
  73. /* file name options */
  74. gboolean file_case_sens;
  75. gboolean file_pattern;
  76. gboolean find_recurs;
  77. gboolean skip_hidden;
  78. gboolean file_all_charsets;
  79. /* file content options */
  80. gboolean content_use;
  81. gboolean content_case_sens;
  82. gboolean content_regexp;
  83. gboolean content_first_hit;
  84. gboolean content_whole_words;
  85. gboolean content_all_charsets;
  86. /* whether use ignore dirs or not */
  87. gboolean ignore_dirs_enable;
  88. /* list of directories to be ignored, separated by ':' */
  89. char *ignore_dirs;
  90. } find_file_options_t;
  91. /*** file scope variables ************************************************************************/
  92. /* Parsed ignore dirs */
  93. static char **find_ignore_dirs = NULL;
  94. /* Size of the find parameters window */
  95. #if HAVE_CHARSET
  96. static int FIND_Y = 19;
  97. #else
  98. static int FIND_Y = 18;
  99. #endif
  100. static int FIND_X = 68;
  101. static int FIND2_X = 64;
  102. /* static variables to remember find parameters */
  103. static WInput *in_start; /* Start path */
  104. static WInput *in_name; /* Filename */
  105. static WInput *in_with; /* Text */
  106. static WInput *in_ignore;
  107. static WLabel *content_label; /* 'Content:' label */
  108. static WCheck *file_case_sens_cbox; /* "case sensitive" checkbox */
  109. static WCheck *file_pattern_cbox; /* File name is glob or regexp */
  110. static WCheck *recursively_cbox;
  111. static WCheck *skip_hidden_cbox;
  112. static WCheck *content_use_cbox; /* Take into account the Content field */
  113. static WCheck *content_case_sens_cbox; /* "case sensitive" checkbox */
  114. static WCheck *content_regexp_cbox; /* "find regular expression" checkbox */
  115. static WCheck *content_first_hit_cbox; /* "First hit" checkbox" */
  116. static WCheck *content_whole_words_cbox; /* "whole words" checkbox */
  117. #ifdef HAVE_CHARSET
  118. static WCheck *file_all_charsets_cbox;
  119. static WCheck *content_all_charsets_cbox;
  120. #endif
  121. static WCheck *ignore_dirs_cbox;
  122. static gboolean running = FALSE; /* nice flag */
  123. static char *find_pattern = NULL; /* Pattern to search */
  124. static char *content_pattern = NULL; /* pattern to search inside files; if
  125. content_regexp_flag is true, it contains the
  126. regex pattern, else the search string. */
  127. static unsigned long matches; /* Number of matches */
  128. static gboolean is_start = FALSE; /* Status of the start/stop toggle button */
  129. static char *old_dir = NULL;
  130. /* Where did we stop */
  131. static int resuming;
  132. static int last_line;
  133. static int last_pos;
  134. static size_t ignore_count = 0;
  135. static Dlg_head *find_dlg; /* The dialog */
  136. static WButton *stop_button; /* pointer to the stop button */
  137. static WLabel *status_label; /* Finished, Searching etc. */
  138. static WLabel *found_num_label; /* Number of found items */
  139. static WListbox *find_list; /* Listbox with the file list */
  140. /* This keeps track of the directory stack */
  141. #if GLIB_CHECK_VERSION (2, 14, 0)
  142. static GQueue dir_queue = G_QUEUE_INIT;
  143. #else
  144. typedef struct dir_stack
  145. {
  146. char *name;
  147. struct dir_stack *prev;
  148. } dir_stack;
  149. static dir_stack *dir_stack_base = 0;
  150. #endif /* GLIB_CHECK_VERSION */
  151. /* *INDENT-OFF* */
  152. static struct
  153. {
  154. const char *text;
  155. int len; /* length including space and brackets */
  156. int x;
  157. } fbuts[] =
  158. {
  159. {N_("&Suspend"), 11, 29},
  160. {N_("Con&tinue"), 12, 29},
  161. {N_("&Chdir"), 11, 3},
  162. {N_("&Again"), 9, 17},
  163. {N_("&Quit"), 8, 43},
  164. {N_("Pane&lize"), 12, 3},
  165. {N_("&View - F3"), 13, 20},
  166. {N_("&Edit - F4"), 13, 38}
  167. };
  168. /* *INDENT-ON* */
  169. static find_file_options_t options = {
  170. TRUE, TRUE, TRUE, FALSE, FALSE,
  171. FALSE, TRUE, FALSE, FALSE, FALSE, FALSE
  172. };
  173. static char *in_start_dir = INPUT_LAST_TEXT;
  174. static mc_search_t *search_file_handle = NULL;
  175. static mc_search_t *search_content_handle = NULL;
  176. /*** file scope functions ************************************************************************/
  177. static void
  178. parse_ignore_dirs (const char *ignore_dirs)
  179. {
  180. size_t r = 0, w = 0; /* read and write iterators */
  181. if (!options.ignore_dirs_enable || ignore_dirs == NULL || ignore_dirs[0] == '\0')
  182. return;
  183. find_ignore_dirs = g_strsplit (ignore_dirs, ":", -1);
  184. /* Values like '/foo::/bar: produce holes in list.
  185. * Find and remove them */
  186. for (; find_ignore_dirs[r] != NULL; r++)
  187. {
  188. if (find_ignore_dirs[r][0] == '\0')
  189. {
  190. /* empty entry -- skip it */
  191. g_free (find_ignore_dirs[r]);
  192. find_ignore_dirs[r] = NULL;
  193. continue;
  194. }
  195. if (r != w)
  196. {
  197. /* copy entry to the previous free array cell */
  198. find_ignore_dirs[w] = find_ignore_dirs[r];
  199. find_ignore_dirs[r] = NULL;
  200. }
  201. canonicalize_pathname (find_ignore_dirs[w]);
  202. if (find_ignore_dirs[w][0] != '\0')
  203. w++;
  204. else
  205. {
  206. g_free (find_ignore_dirs[w]);
  207. find_ignore_dirs[w] = NULL;
  208. }
  209. }
  210. if (find_ignore_dirs[0] == NULL)
  211. {
  212. g_strfreev (find_ignore_dirs);
  213. find_ignore_dirs = NULL;
  214. }
  215. }
  216. /* --------------------------------------------------------------------------------------------- */
  217. static void
  218. find_load_options (void)
  219. {
  220. static gboolean loaded = FALSE;
  221. char *ignore_dirs;
  222. if (loaded)
  223. return;
  224. loaded = TRUE;
  225. options.file_case_sens =
  226. mc_config_get_bool (mc_main_config, "FindFile", "file_case_sens", TRUE);
  227. options.file_pattern =
  228. mc_config_get_bool (mc_main_config, "FindFile", "file_shell_pattern", TRUE);
  229. options.find_recurs = mc_config_get_bool (mc_main_config, "FindFile", "file_find_recurs", TRUE);
  230. options.skip_hidden =
  231. mc_config_get_bool (mc_main_config, "FindFile", "file_skip_hidden", FALSE);
  232. options.file_all_charsets =
  233. mc_config_get_bool (mc_main_config, "FindFile", "file_all_charsets", FALSE);
  234. options.content_use = mc_config_get_bool (mc_main_config, "FindFile", "content_use", TRUE);
  235. options.content_case_sens =
  236. mc_config_get_bool (mc_main_config, "FindFile", "content_case_sens", TRUE);
  237. options.content_regexp =
  238. mc_config_get_bool (mc_main_config, "FindFile", "content_regexp", FALSE);
  239. options.content_first_hit =
  240. mc_config_get_bool (mc_main_config, "FindFile", "content_first_hit", FALSE);
  241. options.content_whole_words =
  242. mc_config_get_bool (mc_main_config, "FindFile", "content_whole_words", FALSE);
  243. options.content_all_charsets =
  244. mc_config_get_bool (mc_main_config, "FindFile", "content_all_charsets", FALSE);
  245. options.ignore_dirs_enable =
  246. mc_config_get_bool (mc_main_config, "FindFile", "ignore_dirs_enable", TRUE);
  247. /* Back compatibility: try load old parameter at first */
  248. ignore_dirs = mc_config_get_string (mc_main_config, "Misc", "find_ignore_dirs", "");
  249. mc_config_del_key (mc_main_config, "Misc", "find_ignore_dirs");
  250. /* Then load new parameters */
  251. options.ignore_dirs = mc_config_get_string (mc_main_config, "FindFile", "ignore_dirs", "");
  252. if (options.ignore_dirs[0] != '\0')
  253. g_free (ignore_dirs);
  254. else
  255. {
  256. g_free (options.ignore_dirs);
  257. options.ignore_dirs = ignore_dirs;
  258. }
  259. if (options.ignore_dirs[0] == '\0')
  260. {
  261. g_free (options.ignore_dirs);
  262. options.ignore_dirs = NULL;
  263. }
  264. }
  265. /* --------------------------------------------------------------------------------------------- */
  266. static void
  267. find_save_options (void)
  268. {
  269. mc_config_set_bool (mc_main_config, "FindFile", "file_case_sens", options.file_case_sens);
  270. mc_config_set_bool (mc_main_config, "FindFile", "file_shell_pattern", options.file_pattern);
  271. mc_config_set_bool (mc_main_config, "FindFile", "file_find_recurs", options.find_recurs);
  272. mc_config_set_bool (mc_main_config, "FindFile", "file_skip_hidden", options.skip_hidden);
  273. mc_config_set_bool (mc_main_config, "FindFile", "file_all_charsets", options.file_all_charsets);
  274. mc_config_set_bool (mc_main_config, "FindFile", "content_use", options.content_use);
  275. mc_config_set_bool (mc_main_config, "FindFile", "content_case_sens", options.content_case_sens);
  276. mc_config_set_bool (mc_main_config, "FindFile", "content_regexp", options.content_regexp);
  277. mc_config_set_bool (mc_main_config, "FindFile", "content_first_hit", options.content_first_hit);
  278. mc_config_set_bool (mc_main_config, "FindFile", "content_whole_words",
  279. options.content_whole_words);
  280. mc_config_set_bool (mc_main_config, "FindFile", "content_all_charsets",
  281. options.content_all_charsets);
  282. mc_config_set_bool (mc_main_config, "FindFile", "ignore_dirs_enable",
  283. options.ignore_dirs_enable);
  284. mc_config_set_string (mc_main_config, "FindFile", "ignore_dirs", options.ignore_dirs);
  285. }
  286. /* --------------------------------------------------------------------------------------------- */
  287. static inline char *
  288. add_to_list (const char *text, void *data)
  289. {
  290. return listbox_add_item (find_list, LISTBOX_APPEND_AT_END, 0, text, data);
  291. }
  292. /* --------------------------------------------------------------------------------------------- */
  293. static inline void
  294. stop_idle (void *data)
  295. {
  296. set_idle_proc (data, 0);
  297. }
  298. /* --------------------------------------------------------------------------------------------- */
  299. static inline void
  300. status_update (const char *text)
  301. {
  302. label_set_text (status_label, text);
  303. }
  304. /* --------------------------------------------------------------------------------------------- */
  305. static void
  306. found_num_update (void)
  307. {
  308. char buffer[BUF_TINY];
  309. g_snprintf (buffer, sizeof (buffer), _("Found: %ld"), matches);
  310. label_set_text (found_num_label, buffer);
  311. }
  312. /* --------------------------------------------------------------------------------------------- */
  313. static void
  314. get_list_info (char **file, char **dir)
  315. {
  316. listbox_get_current (find_list, file, (void **) dir);
  317. }
  318. /* --------------------------------------------------------------------------------------------- */
  319. /** check regular expression */
  320. static gboolean
  321. find_check_regexp (const char *r)
  322. {
  323. mc_search_t *search;
  324. gboolean regexp_ok = FALSE;
  325. search = mc_search_new (r, -1);
  326. if (search != NULL)
  327. {
  328. search->search_type = MC_SEARCH_T_REGEX;
  329. regexp_ok = mc_search_prepare (search);
  330. mc_search_free (search);
  331. }
  332. return regexp_ok;
  333. }
  334. /* --------------------------------------------------------------------------------------------- */
  335. /**
  336. * Callback for the parameter dialog.
  337. * Validate regex, prevent closing the dialog if it's invalid.
  338. */
  339. static cb_ret_t
  340. find_parm_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
  341. {
  342. switch (msg)
  343. {
  344. case DLG_ACTION:
  345. if (sender == (Widget *) content_use_cbox)
  346. {
  347. gboolean disable = !(content_use_cbox->state & C_BOOL);
  348. widget_disable (content_label->widget, disable);
  349. send_message ((Widget *) content_label, WIDGET_DRAW, 0);
  350. widget_disable (in_with->widget, disable);
  351. send_message ((Widget *) in_with, WIDGET_DRAW, 0);
  352. widget_disable (content_first_hit_cbox->widget, disable);
  353. send_message ((Widget *) content_first_hit_cbox, WIDGET_DRAW, 0);
  354. widget_disable (content_regexp_cbox->widget, disable);
  355. send_message ((Widget *) content_regexp_cbox, WIDGET_DRAW, 0);
  356. widget_disable (content_case_sens_cbox->widget, disable);
  357. send_message ((Widget *) content_case_sens_cbox, WIDGET_DRAW, 0);
  358. #ifdef HAVE_CHARSET
  359. widget_disable (content_all_charsets_cbox->widget, disable);
  360. send_message ((Widget *) content_all_charsets_cbox, WIDGET_DRAW, 0);
  361. #endif
  362. widget_disable (content_whole_words_cbox->widget, disable);
  363. send_message ((Widget *) content_whole_words_cbox, WIDGET_DRAW, 0);
  364. return MSG_HANDLED;
  365. }
  366. if (sender == (Widget *) ignore_dirs_cbox)
  367. {
  368. gboolean disable = !(ignore_dirs_cbox->state & C_BOOL);
  369. widget_disable (in_ignore->widget, disable);
  370. send_message ((Widget *) in_ignore, WIDGET_DRAW, 0);
  371. return MSG_HANDLED;
  372. }
  373. return MSG_NOT_HANDLED;
  374. case DLG_VALIDATE:
  375. if (h->ret_value != B_ENTER)
  376. return MSG_HANDLED;
  377. /* check filename regexp */
  378. if (!(file_pattern_cbox->state & C_BOOL)
  379. && (in_name->buffer[0] != '\0') && !find_check_regexp (in_name->buffer))
  380. {
  381. h->state = DLG_ACTIVE; /* Don't stop the dialog */
  382. message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
  383. dlg_select_widget (in_name);
  384. return MSG_HANDLED;
  385. }
  386. /* check content regexp */
  387. if ((content_use_cbox->state & C_BOOL) && (content_regexp_cbox->state & C_BOOL)
  388. && (in_with->buffer[0] != '\0') && !find_check_regexp (in_with->buffer))
  389. {
  390. h->state = DLG_ACTIVE; /* Don't stop the dialog */
  391. message (D_ERROR, MSG_ERROR, _("Malformed regular expression"));
  392. dlg_select_widget (in_with);
  393. return MSG_HANDLED;
  394. }
  395. return MSG_HANDLED;
  396. default:
  397. return default_dlg_callback (h, sender, msg, parm, data);
  398. }
  399. }
  400. /* --------------------------------------------------------------------------------------------- */
  401. /**
  402. * find_parameters: gets information from the user
  403. *
  404. * If the return value is TRUE, then the following holds:
  405. *
  406. * start_dir, ignore_dirs, pattern and content contain the information provided by the user.
  407. * They are newly allocated strings and must be freed when uneeded.
  408. *
  409. * start_dir_len is -1 when user entered an absolute path, otherwise it is a length
  410. * of start_dir (which is absolute). It is used to get a relative pats of find results.
  411. */
  412. static gboolean
  413. find_parameters (char **start_dir, ssize_t * start_dir_len,
  414. char **ignore_dirs, char **pattern, char **content)
  415. {
  416. gboolean return_value;
  417. /* file name */
  418. const char *file_case_label = N_("Cas&e sensitive");
  419. const char *file_pattern_label = N_("&Using shell patterns");
  420. const char *file_recurs_label = N_("&Find recursively");
  421. const char *file_skip_hidden_label = N_("S&kip hidden");
  422. #ifdef HAVE_CHARSET
  423. const char *file_all_charsets_label = N_("&All charsets");
  424. #endif
  425. /* file content */
  426. const char *content_use_label = N_("Sea&rch for content");
  427. const char *content_case_label = N_("Case sens&itive");
  428. const char *content_regexp_label = N_("Re&gular expression");
  429. const char *content_first_hit_label = N_("Fir&st hit");
  430. const char *content_whole_words_label = N_("&Whole words");
  431. #ifdef HAVE_CHARSET
  432. const char *content_all_charsets_label = N_("A&ll charsets");
  433. #endif
  434. const char *buts[] = { N_("&OK"), N_("&Cancel"), N_("&Tree") };
  435. int b0, b1, b2;
  436. int cbox_position;
  437. gboolean disable;
  438. #ifdef ENABLE_NLS
  439. {
  440. int i = sizeof (buts) / sizeof (buts[0]);
  441. while (i-- != 0)
  442. buts[i] = _(buts[i]);
  443. file_case_label = _(file_case_label);
  444. file_pattern_label = _(file_pattern_label);
  445. file_recurs_label = _(file_recurs_label);
  446. file_skip_hidden_label = _(file_skip_hidden_label);
  447. #ifdef HAVE_CHARSET
  448. file_all_charsets_label = _(file_all_charsets_label);
  449. content_all_charsets_label = _(content_all_charsets_label);
  450. #endif
  451. content_use_label = _(content_use_label);
  452. content_case_label = _(content_case_label);
  453. content_regexp_label = _(content_regexp_label);
  454. content_first_hit_label = _(content_first_hit_label);
  455. content_whole_words_label = _(content_whole_words_label);
  456. }
  457. #endif /* ENABLE_NLS */
  458. b0 = str_term_width1 (buts[0]) + 6; /* default button */
  459. b1 = str_term_width1 (buts[1]) + 4;
  460. b2 = str_term_width1 (buts[2]) + 4;
  461. find_load_options ();
  462. if (in_start_dir == NULL)
  463. in_start_dir = g_strdup (".");
  464. disable = !options.content_use;
  465. find_dlg =
  466. create_dlg (TRUE, 0, 0, FIND_Y, FIND_X, dialog_colors,
  467. find_parm_callback, "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
  468. add_widget (find_dlg,
  469. button_new (FIND_Y - 3, FIND_X * 3 / 4 - b1 / 2, B_CANCEL, NORMAL_BUTTON, buts[1],
  470. 0));
  471. add_widget (find_dlg,
  472. button_new (FIND_Y - 3, FIND_X / 4 - b0 / 2, B_ENTER, DEFPUSH_BUTTON, buts[0], 0));
  473. cbox_position = FIND_Y - 5;
  474. content_first_hit_cbox =
  475. check_new (cbox_position--, FIND_X / 2 + 1, options.content_first_hit,
  476. content_first_hit_label);
  477. widget_disable (content_first_hit_cbox->widget, disable);
  478. add_widget (find_dlg, content_first_hit_cbox);
  479. content_whole_words_cbox =
  480. check_new (cbox_position--, FIND_X / 2 + 1, options.content_whole_words,
  481. content_whole_words_label);
  482. widget_disable (content_whole_words_cbox->widget, disable);
  483. add_widget (find_dlg, content_whole_words_cbox);
  484. #ifdef HAVE_CHARSET
  485. content_all_charsets_cbox = check_new (cbox_position--, FIND_X / 2 + 1,
  486. options.content_all_charsets,
  487. content_all_charsets_label);
  488. widget_disable (content_all_charsets_cbox->widget, disable);
  489. add_widget (find_dlg, content_all_charsets_cbox);
  490. #endif
  491. content_case_sens_cbox =
  492. check_new (cbox_position--, FIND_X / 2 + 1, options.content_case_sens, content_case_label);
  493. widget_disable (content_case_sens_cbox->widget, disable);
  494. add_widget (find_dlg, content_case_sens_cbox);
  495. content_regexp_cbox =
  496. check_new (cbox_position--, FIND_X / 2 + 1, options.content_regexp, content_regexp_label);
  497. widget_disable (content_regexp_cbox->widget, disable);
  498. add_widget (find_dlg, content_regexp_cbox);
  499. cbox_position = FIND_Y - 6;
  500. skip_hidden_cbox = check_new (cbox_position--, 3, options.skip_hidden, file_skip_hidden_label);
  501. add_widget (find_dlg, skip_hidden_cbox);
  502. #ifdef HAVE_CHARSET
  503. file_all_charsets_cbox =
  504. check_new (cbox_position--, 3, options.file_all_charsets, file_all_charsets_label);
  505. add_widget (find_dlg, file_all_charsets_cbox);
  506. #endif
  507. file_case_sens_cbox = check_new (cbox_position--, 3, options.file_case_sens, file_case_label);
  508. add_widget (find_dlg, file_case_sens_cbox);
  509. file_pattern_cbox = check_new (cbox_position--, 3, options.file_pattern, file_pattern_label);
  510. add_widget (find_dlg, file_pattern_cbox);
  511. recursively_cbox = check_new (cbox_position, 3, options.find_recurs, file_recurs_label);
  512. add_widget (find_dlg, recursively_cbox);
  513. /* This checkbox is located in the second column */
  514. content_use_cbox =
  515. check_new (cbox_position, FIND_X / 2 + 1, options.content_use, content_use_label);
  516. add_widget (find_dlg, content_use_cbox);
  517. in_with =
  518. input_new (8, FIND_X / 2 + 1, input_get_default_colors (), FIND_X / 2 - 4, INPUT_LAST_TEXT,
  519. MC_HISTORY_SHARED_SEARCH, INPUT_COMPLETE_DEFAULT);
  520. widget_disable (in_with->widget, disable);
  521. add_widget (find_dlg, in_with);
  522. content_label = label_new (7, FIND_X / 2 + 1, _("Content:"));
  523. widget_disable (content_label->widget, disable);
  524. add_widget (find_dlg, content_label);
  525. in_name = input_new (8, 3, input_get_default_colors (),
  526. FIND_X / 2 - 4, INPUT_LAST_TEXT, "name", INPUT_COMPLETE_DEFAULT);
  527. add_widget (find_dlg, in_name);
  528. add_widget (find_dlg, label_new (7, 3, _("File name:")));
  529. in_ignore = input_new (5, 3, input_get_default_colors (), FIND_X - 6,
  530. options.ignore_dirs != NULL ? options.ignore_dirs : "",
  531. "ignoredirs", INPUT_COMPLETE_DEFAULT);
  532. widget_disable (in_ignore->widget, !options.ignore_dirs_enable);
  533. add_widget (find_dlg, in_ignore);
  534. ignore_dirs_cbox =
  535. check_new (4, 3, options.ignore_dirs_enable, _("Ena&ble ignore directories:"));
  536. add_widget (find_dlg, ignore_dirs_cbox);
  537. add_widget (find_dlg, button_new (3, FIND_X - b2 - 2, B_TREE, NORMAL_BUTTON, buts[2], 0));
  538. in_start = input_new (3, 3, input_get_default_colors (),
  539. FIND_X - b2 - 6, in_start_dir, "start", INPUT_COMPLETE_DEFAULT);
  540. add_widget (find_dlg, in_start);
  541. add_widget (find_dlg, label_new (2, 3, _("Start at:")));
  542. find_par_start:
  543. dlg_select_widget (in_name);
  544. switch (run_dlg (find_dlg))
  545. {
  546. case B_CANCEL:
  547. return_value = FALSE;
  548. break;
  549. case B_TREE:
  550. {
  551. const char *temp_dir = in_start->buffer;
  552. if ((temp_dir[0] == '\0') || ((temp_dir[0] == '.') && (temp_dir[1] == '\0')))
  553. temp_dir = current_panel->cwd;
  554. if (in_start_dir != INPUT_LAST_TEXT)
  555. g_free (in_start_dir);
  556. in_start_dir = tree_box (temp_dir);
  557. if (in_start_dir == NULL)
  558. in_start_dir = g_strdup (temp_dir);
  559. input_assign_text (in_start, in_start_dir);
  560. /* Warning: Dreadful goto */
  561. goto find_par_start;
  562. }
  563. default:
  564. {
  565. char *s;
  566. #ifdef HAVE_CHARSET
  567. options.file_all_charsets = file_all_charsets_cbox->state & C_BOOL;
  568. options.content_all_charsets = content_all_charsets_cbox->state & C_BOOL;
  569. #endif
  570. options.content_use = content_use_cbox->state & C_BOOL;
  571. options.content_case_sens = content_case_sens_cbox->state & C_BOOL;
  572. options.content_regexp = content_regexp_cbox->state & C_BOOL;
  573. options.content_first_hit = content_first_hit_cbox->state & C_BOOL;
  574. options.content_whole_words = content_whole_words_cbox->state & C_BOOL;
  575. options.find_recurs = recursively_cbox->state & C_BOOL;
  576. options.file_pattern = file_pattern_cbox->state & C_BOOL;
  577. options.file_case_sens = file_case_sens_cbox->state & C_BOOL;
  578. options.skip_hidden = skip_hidden_cbox->state & C_BOOL;
  579. options.ignore_dirs_enable = ignore_dirs_cbox->state & C_BOOL;
  580. g_free (options.ignore_dirs);
  581. options.ignore_dirs = g_strdup (in_ignore->buffer);
  582. *content = (options.content_use && in_with->buffer[0] != '\0')
  583. ? g_strdup (in_with->buffer) : NULL;
  584. *start_dir = in_start->buffer[0] != '\0' ? in_start->buffer : (char *) ".";
  585. *pattern = g_strdup (in_name->buffer);
  586. if (in_start_dir != INPUT_LAST_TEXT)
  587. g_free (in_start_dir);
  588. in_start_dir = g_strdup (*start_dir);
  589. s = tilde_expand (*start_dir);
  590. canonicalize_pathname (s);
  591. if (s[0] == '.' && s[1] == '\0')
  592. {
  593. *start_dir = g_strdup (current_panel->cwd);
  594. /* FIXME: is current_panel->cwd canonicalized? */
  595. /* relative paths will be used in panelization */
  596. *start_dir_len = (ssize_t) strlen (current_panel->cwd);
  597. g_free (s);
  598. }
  599. else if (g_path_is_absolute (s))
  600. {
  601. *start_dir = s;
  602. *start_dir_len = -1;
  603. }
  604. else
  605. {
  606. /* relative paths will be used in panelization */
  607. *start_dir = mc_build_filename (current_panel->cwd, s, (char *) NULL);
  608. *start_dir_len = (ssize_t) strlen (current_panel->cwd);
  609. g_free (s);
  610. }
  611. if (!options.ignore_dirs_enable || in_ignore->buffer[0] == '\0'
  612. || (in_ignore->buffer[0] == '.' && in_ignore->buffer[1] == '\0'))
  613. *ignore_dirs = NULL;
  614. else
  615. *ignore_dirs = g_strdup (in_ignore->buffer);
  616. find_save_options ();
  617. return_value = TRUE;
  618. }
  619. }
  620. destroy_dlg (find_dlg);
  621. return return_value;
  622. }
  623. /* --------------------------------------------------------------------------------------------- */
  624. #if GLIB_CHECK_VERSION (2, 14, 0)
  625. static inline void
  626. push_directory (const char *dir)
  627. {
  628. g_queue_push_head (&dir_queue, (void *) dir);
  629. }
  630. /* --------------------------------------------------------------------------------------------- */
  631. static inline char *
  632. pop_directory (void)
  633. {
  634. return (char *) g_queue_pop_tail (&dir_queue);
  635. }
  636. /* --------------------------------------------------------------------------------------------- */
  637. /** Remove all the items from the stack */
  638. static void
  639. clear_stack (void)
  640. {
  641. g_queue_foreach (&dir_queue, (GFunc) g_free, NULL);
  642. g_queue_clear (&dir_queue);
  643. }
  644. /* --------------------------------------------------------------------------------------------- */
  645. #else /* GLIB_CHECK_VERSION */
  646. static void
  647. push_directory (const char *dir)
  648. {
  649. dir_stack *new;
  650. new = g_new (dir_stack, 1);
  651. new->name = (char *) dir;
  652. new->prev = dir_stack_base;
  653. dir_stack_base = new;
  654. }
  655. /* --------------------------------------------------------------------------------------------- */
  656. static char *
  657. pop_directory (void)
  658. {
  659. char *name = NULL;
  660. if (dir_stack_base != NULL)
  661. {
  662. dir_stack *next;
  663. name = dir_stack_base->name;
  664. next = dir_stack_base->prev;
  665. g_free (dir_stack_base);
  666. dir_stack_base = next;
  667. }
  668. return name;
  669. }
  670. /* --------------------------------------------------------------------------------------------- */
  671. /** Remove all the items from the stack */
  672. static void
  673. clear_stack (void)
  674. {
  675. char *dir = NULL;
  676. while ((dir = pop_directory ()) != NULL)
  677. g_free (dir);
  678. }
  679. #endif /* GLIB_CHECK_VERSION */
  680. /* --------------------------------------------------------------------------------------------- */
  681. static void
  682. insert_file (const char *dir, const char *file)
  683. {
  684. char *tmp_name = NULL;
  685. static char *dirname = NULL;
  686. while (dir[0] == PATH_SEP && dir[1] == PATH_SEP)
  687. dir++;
  688. if (old_dir)
  689. {
  690. if (strcmp (old_dir, dir))
  691. {
  692. g_free (old_dir);
  693. old_dir = g_strdup (dir);
  694. dirname = add_to_list (dir, NULL);
  695. }
  696. }
  697. else
  698. {
  699. old_dir = g_strdup (dir);
  700. dirname = add_to_list (dir, NULL);
  701. }
  702. tmp_name = g_strdup_printf (" %s", file);
  703. add_to_list (tmp_name, dirname);
  704. g_free (tmp_name);
  705. }
  706. /* --------------------------------------------------------------------------------------------- */
  707. static void
  708. find_add_match (const char *dir, const char *file)
  709. {
  710. insert_file (dir, file);
  711. /* Don't scroll */
  712. if (matches == 0)
  713. listbox_select_first (find_list);
  714. send_message (&find_list->widget, WIDGET_DRAW, 0);
  715. matches++;
  716. found_num_update ();
  717. }
  718. /* --------------------------------------------------------------------------------------------- */
  719. /**
  720. * get_line_at:
  721. *
  722. * Returns malloced null-terminated line from file file_fd.
  723. * Input is buffered in buf_size long buffer.
  724. * Current pos in buf is stored in pos.
  725. * n_read - number of read chars.
  726. * has_newline - is there newline ?
  727. */
  728. static char *
  729. get_line_at (int file_fd, char *buf, int buf_size, int *pos, int *n_read, gboolean * has_newline)
  730. {
  731. char *buffer = NULL;
  732. int buffer_size = 0;
  733. char ch = 0;
  734. int i = 0;
  735. for (;;)
  736. {
  737. if (*pos >= *n_read)
  738. {
  739. *pos = 0;
  740. *n_read = mc_read (file_fd, buf, buf_size);
  741. if (*n_read <= 0)
  742. break;
  743. }
  744. ch = buf[(*pos)++];
  745. if (ch == '\0')
  746. {
  747. /* skip possible leading zero(s) */
  748. if (i == 0)
  749. continue;
  750. break;
  751. }
  752. if (i >= buffer_size - 1)
  753. buffer = g_realloc (buffer, buffer_size += 80);
  754. /* Strip newline */
  755. if (ch == '\n')
  756. break;
  757. buffer[i++] = ch;
  758. }
  759. *has_newline = (ch != '\0');
  760. if (buffer != NULL)
  761. buffer[i] = '\0';
  762. return buffer;
  763. }
  764. /* --------------------------------------------------------------------------------------------- */
  765. static FindProgressStatus
  766. check_find_events (Dlg_head * h)
  767. {
  768. Gpm_Event event;
  769. int c;
  770. event.x = -1;
  771. c = tty_get_event (&event, h->mouse_status == MOU_REPEAT, FALSE);
  772. if (c != EV_NONE)
  773. {
  774. dlg_process_event (h, c, &event);
  775. if (h->ret_value == B_ENTER
  776. || h->ret_value == B_CANCEL || h->ret_value == B_AGAIN || h->ret_value == B_PANELIZE)
  777. {
  778. /* dialog terminated */
  779. return FIND_ABORT;
  780. }
  781. if (!(h->flags & DLG_WANT_IDLE))
  782. {
  783. /* searching suspended */
  784. return FIND_SUSPEND;
  785. }
  786. }
  787. return FIND_CONT;
  788. }
  789. /* --------------------------------------------------------------------------------------------- */
  790. /**
  791. * search_content:
  792. *
  793. * Search the content_pattern string in the DIRECTORY/FILE.
  794. * It will add the found entries to the find listbox.
  795. *
  796. * returns FALSE if do_search should look for another file
  797. * TRUE if do_search should exit and proceed to the event handler
  798. */
  799. static gboolean
  800. search_content (Dlg_head * h, const char *directory, const char *filename)
  801. {
  802. struct stat s;
  803. char buffer[BUF_4K];
  804. char *fname = NULL;
  805. int file_fd;
  806. gboolean ret_val = FALSE;
  807. vfs_path_t *vpath;
  808. fname = mc_build_filename (directory, filename, (char *) NULL);
  809. vpath = vfs_path_from_str (fname);
  810. if (mc_stat (vpath, &s) != 0 || !S_ISREG (s.st_mode))
  811. {
  812. g_free (fname);
  813. vfs_path_free (vpath);
  814. return FALSE;
  815. }
  816. file_fd = mc_open (fname, O_RDONLY);
  817. g_free (fname);
  818. vfs_path_free (vpath);
  819. if (file_fd == -1)
  820. return FALSE;
  821. g_snprintf (buffer, sizeof (buffer), _("Grepping in %s"), str_trunc (filename, FIND2_X_USE));
  822. status_update (buffer);
  823. mc_refresh ();
  824. tty_enable_interrupt_key ();
  825. tty_got_interrupt ();
  826. {
  827. int line = 1;
  828. int pos = 0;
  829. int n_read = 0;
  830. gboolean has_newline;
  831. char *p = NULL;
  832. gboolean found = FALSE;
  833. gsize found_len;
  834. char result[BUF_MEDIUM];
  835. if (resuming)
  836. {
  837. /* We've been previously suspended, start from the previous position */
  838. resuming = 0;
  839. line = last_line;
  840. pos = last_pos;
  841. }
  842. while (!ret_val
  843. && (p = get_line_at (file_fd, buffer, sizeof (buffer),
  844. &pos, &n_read, &has_newline)) != NULL)
  845. {
  846. if (!found /* Search in binary line once */
  847. && mc_search_run (search_content_handle,
  848. (const void *) p, 0, strlen (p), &found_len))
  849. {
  850. g_snprintf (result, sizeof (result), "%d:%s", line, filename);
  851. find_add_match (directory, result);
  852. found = TRUE;
  853. }
  854. g_free (p);
  855. if (found && options.content_first_hit)
  856. break;
  857. if (has_newline)
  858. {
  859. found = FALSE;
  860. line++;
  861. }
  862. if ((line & 0xff) == 0)
  863. {
  864. FindProgressStatus res;
  865. res = check_find_events (h);
  866. switch (res)
  867. {
  868. case FIND_ABORT:
  869. stop_idle (h);
  870. ret_val = TRUE;
  871. break;
  872. case FIND_SUSPEND:
  873. resuming = 1;
  874. last_line = line;
  875. last_pos = pos;
  876. ret_val = TRUE;
  877. break;
  878. default:
  879. break;
  880. }
  881. }
  882. }
  883. }
  884. tty_disable_interrupt_key ();
  885. mc_close (file_fd);
  886. return ret_val;
  887. }
  888. /* --------------------------------------------------------------------------------------------- */
  889. /**
  890. If dir is absolute, this means we're within dir and searching file here.
  891. If dir is relative, this means we're going to add dir to the directory stack.
  892. **/
  893. static gboolean
  894. find_ignore_dir_search (const char *dir)
  895. {
  896. if (find_ignore_dirs != NULL)
  897. {
  898. const size_t dlen = strlen (dir);
  899. const unsigned char dabs = g_path_is_absolute (dir) ? 1 : 0;
  900. char **ignore_dir;
  901. for (ignore_dir = find_ignore_dirs; *ignore_dir != NULL; ignore_dir++)
  902. {
  903. const size_t ilen = strlen (*ignore_dir);
  904. const unsigned char iabs = g_path_is_absolute (*ignore_dir) ? 2 : 0;
  905. /* ignore dir is too long -- skip it */
  906. if (dlen < ilen)
  907. continue;
  908. /* handle absolute and relative paths */
  909. switch (iabs | dabs)
  910. {
  911. case 0: /* both paths are relative */
  912. case 3: /* both paths are abolute */
  913. /* if ignore dir is not a path of dir -- skip it */
  914. if (strncmp (dir, *ignore_dir, ilen) == 0)
  915. {
  916. /* be sure that ignore dir is not a part of dir like:
  917. ignore dir is "h", dir is "home" */
  918. if (dir[ilen] == '\0' || dir[ilen] == PATH_SEP)
  919. return TRUE;
  920. }
  921. break;
  922. case 1: /* dir is absolute, ignore_dir is relative */
  923. {
  924. char *d;
  925. d = strstr (dir, *ignore_dir);
  926. if (d != NULL && d[-1] == PATH_SEP && (d[ilen] == '\0' || d[ilen] == PATH_SEP))
  927. return TRUE;
  928. }
  929. break;
  930. case 2: /* dir is relative, ignore_dir is absolute */
  931. /* FIXME: skip this case */
  932. break;
  933. default: /* this cannot occurs */
  934. return FALSE;
  935. }
  936. }
  937. }
  938. return FALSE;
  939. }
  940. /* --------------------------------------------------------------------------------------------- */
  941. static void
  942. find_rotate_dash (const Dlg_head * h, gboolean finish)
  943. {
  944. static const char rotating_dash[] = "|/-\\";
  945. static unsigned int pos = 0;
  946. if (verbose)
  947. {
  948. pos = (pos + 1) % 4;
  949. tty_setcolor (h->color[DLG_COLOR_NORMAL]);
  950. dlg_move (h, FIND2_Y - 7, FIND2_X - 4);
  951. tty_print_char (finish ? ' ' : rotating_dash[pos]);
  952. mc_refresh ();
  953. }
  954. }
  955. /* --------------------------------------------------------------------------------------------- */
  956. static int
  957. do_search (Dlg_head * h)
  958. {
  959. static struct dirent *dp = NULL;
  960. static DIR *dirp = NULL;
  961. static char *directory = NULL;
  962. struct stat tmp_stat;
  963. static int subdirs_left = 0;
  964. gsize bytes_found;
  965. unsigned short count;
  966. if (h == NULL)
  967. { /* someone forces me to close dirp */
  968. if (dirp != NULL)
  969. {
  970. mc_closedir (dirp);
  971. dirp = NULL;
  972. }
  973. g_free (directory);
  974. directory = NULL;
  975. dp = NULL;
  976. return 1;
  977. }
  978. for (count = 0; count < 32; count++)
  979. {
  980. while (dp == NULL)
  981. {
  982. if (dirp != NULL)
  983. {
  984. mc_closedir (dirp);
  985. dirp = NULL;
  986. }
  987. while (dirp == NULL)
  988. {
  989. char *tmp = NULL;
  990. vfs_path_t *tmp_vpath;
  991. tty_setcolor (REVERSE_COLOR);
  992. while (TRUE)
  993. {
  994. tmp = pop_directory ();
  995. if (tmp == NULL)
  996. {
  997. running = FALSE;
  998. if (ignore_count == 0)
  999. status_update (_("Finished"));
  1000. else
  1001. {
  1002. char msg[BUF_SMALL];
  1003. g_snprintf (msg, sizeof (msg),
  1004. ngettext ("Finished (ignored %zd directory)",
  1005. "Finished (ignored %zd directories)",
  1006. ignore_count), ignore_count);
  1007. status_update (msg);
  1008. }
  1009. find_rotate_dash (h, TRUE);
  1010. stop_idle (h);
  1011. return 0;
  1012. }
  1013. /* handle absolute ignore dirs here */
  1014. if (!find_ignore_dir_search (tmp))
  1015. break;
  1016. g_free (tmp);
  1017. ignore_count++;
  1018. }
  1019. g_free (directory);
  1020. directory = tmp;
  1021. tmp_vpath = vfs_path_from_str (directory);
  1022. if (verbose)
  1023. {
  1024. char buffer[BUF_SMALL];
  1025. g_snprintf (buffer, sizeof (buffer), _("Searching %s"),
  1026. str_trunc (directory, FIND2_X_USE));
  1027. status_update (buffer);
  1028. }
  1029. /* mc_stat should not be called after mc_opendir
  1030. because vfs_s_opendir modifies the st_nlink
  1031. */
  1032. if (mc_stat (tmp_vpath, &tmp_stat) == 0)
  1033. subdirs_left = tmp_stat.st_nlink - 2;
  1034. else
  1035. subdirs_left = 0;
  1036. dirp = mc_opendir (tmp_vpath);
  1037. vfs_path_free (tmp_vpath);
  1038. } /* while (!dirp) */
  1039. /* skip invalid filenames */
  1040. while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
  1041. ;
  1042. } /* while (!dp) */
  1043. if (strcmp (dp->d_name, ".") == 0 || strcmp (dp->d_name, "..") == 0)
  1044. {
  1045. /* skip invalid filenames */
  1046. while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
  1047. ;
  1048. return 1;
  1049. }
  1050. if (!(options.skip_hidden && (dp->d_name[0] == '.')))
  1051. {
  1052. gboolean search_ok;
  1053. if ((subdirs_left != 0) && options.find_recurs && (directory != NULL))
  1054. { /* Can directory be NULL ? */
  1055. /* handle relative ignore dirs here */
  1056. if (options.ignore_dirs_enable && find_ignore_dir_search (dp->d_name))
  1057. ignore_count++;
  1058. else
  1059. {
  1060. vfs_path_t *tmp_vpath;
  1061. tmp_vpath = vfs_path_build_filename (directory, dp->d_name, (char *) NULL);
  1062. if (mc_lstat (tmp_vpath, &tmp_stat) == 0 && S_ISDIR (tmp_stat.st_mode))
  1063. {
  1064. push_directory (vfs_path_to_str (tmp_vpath));
  1065. vfs_path_free (tmp_vpath);
  1066. subdirs_left--;
  1067. }
  1068. else
  1069. vfs_path_free (tmp_vpath);
  1070. }
  1071. }
  1072. search_ok = mc_search_run (search_file_handle, dp->d_name,
  1073. 0, strlen (dp->d_name), &bytes_found);
  1074. if (search_ok)
  1075. {
  1076. if (content_pattern == NULL)
  1077. find_add_match (directory, dp->d_name);
  1078. else if (search_content (h, directory, dp->d_name))
  1079. return 1;
  1080. }
  1081. }
  1082. /* skip invalid filenames */
  1083. while ((dp = mc_readdir (dirp)) != NULL && !str_is_valid_string (dp->d_name))
  1084. ;
  1085. } /* for */
  1086. find_rotate_dash (h, FALSE);
  1087. return 1;
  1088. }
  1089. /* --------------------------------------------------------------------------------------------- */
  1090. static void
  1091. init_find_vars (void)
  1092. {
  1093. g_free (old_dir);
  1094. old_dir = NULL;
  1095. matches = 0;
  1096. ignore_count = 0;
  1097. /* Remove all the items from the stack */
  1098. clear_stack ();
  1099. g_strfreev (find_ignore_dirs);
  1100. find_ignore_dirs = NULL;
  1101. }
  1102. /* --------------------------------------------------------------------------------------------- */
  1103. static void
  1104. find_do_view_edit (int unparsed_view, int edit, char *dir, char *file)
  1105. {
  1106. char *fullname = NULL;
  1107. const char *filename = NULL;
  1108. int line;
  1109. if (content_pattern != NULL)
  1110. {
  1111. filename = strchr (file + 4, ':') + 1;
  1112. line = atoi (file + 4);
  1113. }
  1114. else
  1115. {
  1116. filename = file + 4;
  1117. line = 0;
  1118. }
  1119. fullname = mc_build_filename (dir, filename, (char *) NULL);
  1120. if (edit)
  1121. do_edit_at_line (fullname, use_internal_edit, line);
  1122. else
  1123. view_file_at_line (fullname, unparsed_view, use_internal_view, line);
  1124. g_free (fullname);
  1125. }
  1126. /* --------------------------------------------------------------------------------------------- */
  1127. static cb_ret_t
  1128. view_edit_currently_selected_file (int unparsed_view, int edit)
  1129. {
  1130. char *dir = NULL;
  1131. char *text = NULL;
  1132. listbox_get_current (find_list, &text, (void **) &dir);
  1133. if ((text == NULL) || (dir == NULL))
  1134. return MSG_NOT_HANDLED;
  1135. find_do_view_edit (unparsed_view, edit, dir, text);
  1136. return MSG_HANDLED;
  1137. }
  1138. /* --------------------------------------------------------------------------------------------- */
  1139. static cb_ret_t
  1140. find_callback (Dlg_head * h, Widget * sender, dlg_msg_t msg, int parm, void *data)
  1141. {
  1142. switch (msg)
  1143. {
  1144. case DLG_KEY:
  1145. if (parm == KEY_F (3) || parm == KEY_F (13))
  1146. {
  1147. int unparsed_view = (parm == KEY_F (13));
  1148. return view_edit_currently_selected_file (unparsed_view, 0);
  1149. }
  1150. if (parm == KEY_F (4))
  1151. {
  1152. return view_edit_currently_selected_file (0, 1);
  1153. }
  1154. return MSG_NOT_HANDLED;
  1155. case DLG_IDLE:
  1156. do_search (h);
  1157. return MSG_HANDLED;
  1158. default:
  1159. return default_dlg_callback (h, sender, msg, parm, data);
  1160. }
  1161. }
  1162. /* --------------------------------------------------------------------------------------------- */
  1163. /** Handles the Stop/Start button in the find window */
  1164. static int
  1165. start_stop (WButton * button, int action)
  1166. {
  1167. (void) button;
  1168. (void) action;
  1169. running = is_start;
  1170. set_idle_proc (find_dlg, running);
  1171. is_start = !is_start;
  1172. status_update (is_start ? _("Stopped") : _("Searching"));
  1173. button_set_text (stop_button, fbuts[is_start ? 1 : 0].text);
  1174. return 0;
  1175. }
  1176. /* --------------------------------------------------------------------------------------------- */
  1177. /** Handle view command, when invoked as a button */
  1178. static int
  1179. find_do_view_file (WButton * button, int action)
  1180. {
  1181. (void) button;
  1182. (void) action;
  1183. view_edit_currently_selected_file (0, 0);
  1184. return 0;
  1185. }
  1186. /* --------------------------------------------------------------------------------------------- */
  1187. /** Handle edit command, when invoked as a button */
  1188. static int
  1189. find_do_edit_file (WButton * button, int action)
  1190. {
  1191. (void) button;
  1192. (void) action;
  1193. view_edit_currently_selected_file (0, 1);
  1194. return 0;
  1195. }
  1196. /* --------------------------------------------------------------------------------------------- */
  1197. static void
  1198. setup_gui (void)
  1199. {
  1200. #ifdef ENABLE_NLS
  1201. static gboolean i18n_flag = FALSE;
  1202. if (!i18n_flag)
  1203. {
  1204. int i = sizeof (fbuts) / sizeof (fbuts[0]);
  1205. while (i-- != 0)
  1206. {
  1207. fbuts[i].text = _(fbuts[i].text);
  1208. fbuts[i].len = str_term_width1 (fbuts[i].text) + 3;
  1209. }
  1210. fbuts[2].len += 2; /* DEFPUSH_BUTTON */
  1211. i18n_flag = TRUE;
  1212. }
  1213. #endif /* ENABLE_NLS */
  1214. /*
  1215. * Dynamically place buttons centered within current window size
  1216. */
  1217. {
  1218. int l0 = max (fbuts[0].len, fbuts[1].len);
  1219. int l1 = fbuts[2].len + fbuts[3].len + l0 + fbuts[4].len;
  1220. int l2 = fbuts[5].len + fbuts[6].len + fbuts[7].len;
  1221. int r1, r2;
  1222. /* Check, if both button rows fit within FIND2_X */
  1223. FIND2_X = max (l1 + 9, COLS - 16);
  1224. FIND2_X = max (l2 + 8, FIND2_X);
  1225. /* compute amount of space between buttons for each row */
  1226. r1 = (FIND2_X - 4 - l1) % 5;
  1227. l1 = (FIND2_X - 4 - l1) / 5;
  1228. r2 = (FIND2_X - 4 - l2) % 4;
  1229. l2 = (FIND2_X - 4 - l2) / 4;
  1230. /* ...and finally, place buttons */
  1231. fbuts[2].x = 2 + r1 / 2 + l1;
  1232. fbuts[3].x = fbuts[2].x + fbuts[2].len + l1;
  1233. fbuts[0].x = fbuts[3].x + fbuts[3].len + l1;
  1234. fbuts[4].x = fbuts[0].x + l0 + l1;
  1235. fbuts[5].x = 2 + r2 / 2 + l2;
  1236. fbuts[6].x = fbuts[5].x + fbuts[5].len + l2;
  1237. fbuts[7].x = fbuts[6].x + fbuts[6].len + l2;
  1238. }
  1239. find_dlg =
  1240. create_dlg (TRUE, 0, 0, FIND2_Y, FIND2_X, dialog_colors, find_callback,
  1241. "[Find File]", _("Find File"), DLG_CENTER | DLG_REVERSE);
  1242. add_widget (find_dlg,
  1243. button_new (FIND2_Y - 3, fbuts[7].x, B_VIEW, NORMAL_BUTTON,
  1244. fbuts[7].text, find_do_edit_file));
  1245. add_widget (find_dlg,
  1246. button_new (FIND2_Y - 3, fbuts[6].x, B_VIEW, NORMAL_BUTTON,
  1247. fbuts[6].text, find_do_view_file));
  1248. add_widget (find_dlg,
  1249. button_new (FIND2_Y - 3, fbuts[5].x, B_PANELIZE, NORMAL_BUTTON, fbuts[5].text,
  1250. NULL));
  1251. add_widget (find_dlg,
  1252. button_new (FIND2_Y - 4, fbuts[4].x, B_CANCEL, NORMAL_BUTTON, fbuts[4].text, NULL));
  1253. stop_button =
  1254. button_new (FIND2_Y - 4, fbuts[0].x, B_STOP, NORMAL_BUTTON, fbuts[0].text, start_stop);
  1255. add_widget (find_dlg, stop_button);
  1256. add_widget (find_dlg,
  1257. button_new (FIND2_Y - 4, fbuts[3].x, B_AGAIN, NORMAL_BUTTON, fbuts[3].text, NULL));
  1258. add_widget (find_dlg,
  1259. button_new (FIND2_Y - 4, fbuts[2].x, B_ENTER, DEFPUSH_BUTTON, fbuts[2].text, NULL));
  1260. status_label = label_new (FIND2_Y - 7, 4, _("Searching"));
  1261. add_widget (find_dlg, status_label);
  1262. found_num_label = label_new (FIND2_Y - 6, 4, "");
  1263. add_widget (find_dlg, found_num_label);
  1264. find_list = listbox_new (2, 2, FIND2_Y - 10, FIND2_X - 4, FALSE, NULL);
  1265. add_widget (find_dlg, find_list);
  1266. }
  1267. /* --------------------------------------------------------------------------------------------- */
  1268. static int
  1269. run_process (void)
  1270. {
  1271. int ret;
  1272. search_content_handle = mc_search_new (content_pattern, -1);
  1273. if (search_content_handle)
  1274. {
  1275. search_content_handle->search_type =
  1276. options.content_regexp ? MC_SEARCH_T_REGEX : MC_SEARCH_T_NORMAL;
  1277. search_content_handle->is_case_sensitive = options.content_case_sens;
  1278. search_content_handle->whole_words = options.content_whole_words;
  1279. search_content_handle->is_all_charsets = options.content_all_charsets;
  1280. }
  1281. search_file_handle = mc_search_new (find_pattern, -1);
  1282. search_file_handle->search_type = options.file_pattern ? MC_SEARCH_T_GLOB : MC_SEARCH_T_REGEX;
  1283. search_file_handle->is_case_sensitive = options.file_case_sens;
  1284. search_file_handle->is_all_charsets = options.file_all_charsets;
  1285. search_file_handle->is_entire_line = options.file_pattern;
  1286. resuming = 0;
  1287. set_idle_proc (find_dlg, 1);
  1288. ret = run_dlg (find_dlg);
  1289. mc_search_free (search_file_handle);
  1290. search_file_handle = NULL;
  1291. mc_search_free (search_content_handle);
  1292. search_content_handle = NULL;
  1293. return ret;
  1294. }
  1295. /* --------------------------------------------------------------------------------------------- */
  1296. static void
  1297. kill_gui (void)
  1298. {
  1299. set_idle_proc (find_dlg, 0);
  1300. destroy_dlg (find_dlg);
  1301. }
  1302. /* --------------------------------------------------------------------------------------------- */
  1303. static int
  1304. do_find (const char *start_dir, ssize_t start_dir_len, const char *ignore_dirs,
  1305. const char *pattern, const char *content, char **dirname, char **filename)
  1306. {
  1307. int return_value = 0;
  1308. char *dir_tmp = NULL, *file_tmp = NULL;
  1309. setup_gui ();
  1310. /* FIXME: Need to cleanup this, this ought to be passed non-globaly */
  1311. find_pattern = (char *) pattern;
  1312. content_pattern = NULL;
  1313. if (options.content_use && content != NULL && str_is_valid_string (content))
  1314. content_pattern = g_strdup (content);
  1315. init_find_vars ();
  1316. parse_ignore_dirs (ignore_dirs);
  1317. push_directory (start_dir);
  1318. return_value = run_process ();
  1319. /* Clear variables */
  1320. init_find_vars ();
  1321. get_list_info (&file_tmp, &dir_tmp);
  1322. if (dir_tmp)
  1323. *dirname = g_strdup (dir_tmp);
  1324. if (file_tmp)
  1325. *filename = g_strdup (file_tmp);
  1326. if (return_value == B_PANELIZE && *filename)
  1327. {
  1328. int status, link_to_dir, stale_link;
  1329. int next_free = 0;
  1330. int i;
  1331. struct stat st;
  1332. GList *entry;
  1333. dir_list *list = &current_panel->dir;
  1334. char *name = NULL;
  1335. if (set_zero_dir (list))
  1336. next_free++;
  1337. for (i = 0, entry = find_list->list; entry != NULL; i++, entry = g_list_next (entry))
  1338. {
  1339. const char *lc_filename = NULL;
  1340. WLEntry *le = (WLEntry *) entry->data;
  1341. char *p;
  1342. if ((le->text == NULL) || (le->data == NULL))
  1343. continue;
  1344. if (content_pattern != NULL)
  1345. lc_filename = strchr (le->text + 4, ':') + 1;
  1346. else
  1347. lc_filename = le->text + 4;
  1348. name = mc_build_filename (le->data, lc_filename, (char *) NULL);
  1349. /* skip initial start dir */
  1350. if (start_dir_len < 0)
  1351. p = name;
  1352. else
  1353. {
  1354. p = name + (size_t) start_dir_len;
  1355. if (*p == PATH_SEP)
  1356. p++;
  1357. }
  1358. status = handle_path (list, p, &st, next_free, &link_to_dir, &stale_link);
  1359. if (status == 0)
  1360. {
  1361. g_free (name);
  1362. continue;
  1363. }
  1364. if (status == -1)
  1365. {
  1366. g_free (name);
  1367. break;
  1368. }
  1369. /* don't add files more than once to the panel */
  1370. if (content_pattern != NULL && next_free > 0
  1371. && strcmp (list->list[next_free - 1].fname, p) == 0)
  1372. {
  1373. g_free (name);
  1374. continue;
  1375. }
  1376. if (next_free == 0) /* first turn i.e clean old list */
  1377. panel_clean_dir (current_panel);
  1378. list->list[next_free].fnamelen = strlen (p);
  1379. list->list[next_free].fname = g_strndup (p, list->list[next_free].fnamelen);
  1380. list->list[next_free].f.marked = 0;
  1381. list->list[next_free].f.link_to_dir = link_to_dir;
  1382. list->list[next_free].f.stale_link = stale_link;
  1383. list->list[next_free].f.dir_size_computed = 0;
  1384. list->list[next_free].st = st;
  1385. list->list[next_free].sort_key = NULL;
  1386. list->list[next_free].second_sort_key = NULL;
  1387. next_free++;
  1388. g_free (name);
  1389. if (!(next_free & 15))
  1390. rotate_dash ();
  1391. }
  1392. if (next_free)
  1393. {
  1394. current_panel->count = next_free;
  1395. current_panel->is_panelized = TRUE;
  1396. /* absolute path */
  1397. if (start_dir_len < 0)
  1398. {
  1399. int ret;
  1400. strcpy (current_panel->cwd, PATH_SEP_STR);
  1401. ret = chdir (PATH_SEP_STR);
  1402. }
  1403. panelize_save_panel (current_panel);
  1404. }
  1405. }
  1406. g_free (content_pattern);
  1407. kill_gui ();
  1408. do_search (NULL); /* force do_search to release resources */
  1409. g_free (old_dir);
  1410. old_dir = NULL;
  1411. return return_value;
  1412. }
  1413. /* --------------------------------------------------------------------------------------------- */
  1414. /*** public functions ****************************************************************************/
  1415. /* --------------------------------------------------------------------------------------------- */
  1416. void
  1417. find_file (void)
  1418. {
  1419. char *start_dir = NULL, *pattern = NULL, *content = NULL, *ignore_dirs = NULL;
  1420. ssize_t start_dir_len;
  1421. char *filename = NULL, *dirname = NULL;
  1422. int v;
  1423. while (find_parameters (&start_dir, &start_dir_len, &ignore_dirs, &pattern, &content))
  1424. {
  1425. if (pattern[0] == '\0')
  1426. break; /* nothing search */
  1427. dirname = filename = NULL;
  1428. is_start = FALSE;
  1429. v = do_find (start_dir, start_dir_len, ignore_dirs, pattern, content, &dirname, &filename);
  1430. g_free (ignore_dirs);
  1431. g_free (pattern);
  1432. if (v == B_ENTER)
  1433. {
  1434. if (dirname != NULL)
  1435. {
  1436. do_cd (dirname, cd_exact);
  1437. if (filename != NULL)
  1438. try_to_select (current_panel,
  1439. filename + (content != NULL
  1440. ? strchr (filename + 4, ':') - filename + 1 : 4));
  1441. }
  1442. else if (filename != NULL)
  1443. do_cd (filename, cd_exact);
  1444. g_free (dirname);
  1445. g_free (filename);
  1446. break;
  1447. }
  1448. g_free (content);
  1449. g_free (dirname);
  1450. g_free (filename);
  1451. if (v == B_CANCEL)
  1452. break;
  1453. if (v == B_PANELIZE)
  1454. {
  1455. panel_re_sort (current_panel);
  1456. try_to_select (current_panel, NULL);
  1457. break;
  1458. }
  1459. }
  1460. }
  1461. /* --------------------------------------------------------------------------------------------- */