* editcmd.c: Clean up global variables in the completion code.
[midnight-commander.git] / edit / editcmd.c
blob139401a6103ee740046bcde15c9603f59b9875de
1 /* editor high level editing commands.
3 Copyright (C) 1996, 1997 the Free Software Foundation
5 Authors: 1996, 1997 Paul Sheer
7 $Id$
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
22 02111-1307, USA.
26 /* #define PIPE_BLOCKS_SO_READ_BYTE_BY_BYTE */
28 #include <config.h>
29 #include <ctype.h>
31 #include "edit.h"
32 #include "editcmddef.h"
34 #ifdef HAVE_CHARSET
35 #include "src/charsets.h"
36 #endif
38 /* globals: */
40 /* search and replace: */
41 int replace_scanf = 0;
42 int replace_regexp = 0;
43 int replace_all = 0;
44 int replace_prompt = 1;
45 int replace_whole = 0;
46 int replace_case = 0;
47 int replace_backwards = 0;
48 int search_create_bookmark = 0;
50 /* queries on a save */
51 int edit_confirm_save = 1;
53 #define NUM_REPL_ARGS 64
54 #define MAX_REPL_LEN 1024
56 static inline int my_lower_case (int c)
58 return tolower(c & 0xFF);
61 char *strcasechr (const unsigned char *s, int c)
63 for (c = my_lower_case (c); my_lower_case ((int) *s) != c; ++s)
64 if (*s == '\0')
65 return 0;
66 return (char *) s;
69 #ifndef HAVE_MEMMOVE
70 /* for Christophe */
71 static void *memmove (void *dest, const void *src, size_t n)
73 char *t, *s;
75 if (dest <= src) {
76 t = (char *) dest;
77 s = (char *) src;
78 while (n--)
79 *t++ = *s++;
80 } else {
81 t = (char *) dest + n;
82 s = (char *) src + n;
83 while (n--)
84 *--t = *--s;
86 return dest;
88 #endif /* !HAVE_MEMMOVE */
90 /* #define itoa MY_itoa <---- this line is now in edit.h */
91 char *itoa (int i)
93 static char t[14];
94 char *s = t + 13;
95 int j = i;
96 *s-- = 0;
97 do {
98 *s-- = i % 10 + '0';
99 } while ((i = i / 10));
100 if (j < 0)
101 *s-- = '-';
102 return ++s;
105 /* Temporary strings */
106 static char *stacked[16] =
107 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
110 This joins strings end on end and allocates memory for the result.
111 The result is later automatically free'd and must not be free'd
112 by the caller.
114 char *catstrs (const char *first,...)
116 static int i = 0;
117 va_list ap;
118 int len;
119 char *data;
121 if (!first)
122 return 0;
124 len = strlen (first);
125 va_start (ap, first);
127 while ((data = va_arg (ap, char *)) != 0)
128 len += strlen (data);
130 len++;
132 i = (i + 1) % 16;
133 g_free (stacked[i]);
135 stacked[i] = g_malloc (len);
136 va_end (ap);
137 va_start (ap, first);
138 strcpy (stacked[i], first);
139 while ((data = va_arg (ap, char *)) != 0)
140 strcat (stacked[i], data);
141 va_end (ap);
143 return stacked[i];
146 /* Free temporary strings */
147 void freestrs(void)
149 int i;
151 for (i = 0; i < sizeof(stacked) / sizeof(stacked[0]); i++) {
152 g_free (stacked[i]);
153 stacked[i] = NULL;
157 void edit_help_cmd (WEdit * edit)
159 interactive_display (NULL, "[Internal File Editor]");
160 edit->force |= REDRAW_COMPLETELY;
163 void edit_refresh_cmd (WEdit * edit)
165 #ifndef HAVE_SLANG
166 clr_scr();
167 do_refresh();
168 #else
170 int color;
171 edit_get_syntax_color (edit, -1, &color);
173 touchwin(stdscr);
174 #endif /* !HAVE_SLANG */
175 mc_refresh();
176 doupdate();
179 /* "Oleg Yu. Repin" <repin@ssd.sscc.ru> added backup filenames
180 ...thanks -paul */
182 /* If 0 (quick save) then a) create/truncate <filename> file,
183 b) save to <filename>;
184 if 1 (safe save) then a) save to <tempnam>,
185 b) rename <tempnam> to <filename>;
186 if 2 (do backups) then a) save to <tempnam>,
187 b) rename <filename> to <filename.backup_ext>,
188 c) rename <tempnam> to <filename>. */
190 /* returns 0 on error */
191 int edit_save_file (WEdit * edit, const char *filename)
193 char *p;
194 long filelen = 0;
195 char *savename = 0;
196 int this_save_mode, fd;
198 if (!filename)
199 return 0;
200 if (!*filename)
201 return 0;
203 if ((fd = mc_open (filename, O_WRONLY)) == -1) {
205 * The file does not exists yet, so no safe save or
206 * backup are necessary.
208 this_save_mode = 0;
209 } else {
210 mc_close (fd);
211 this_save_mode = option_save_mode;
214 if (this_save_mode > 0) {
215 char *savedir, *slashpos, *saveprefix;
216 slashpos = strrchr (filename, PATH_SEP);
217 if (slashpos) {
218 savedir = (char *) strdup (filename);
219 savedir[slashpos - filename + 1] = '\0';
220 } else
221 savedir = (char *) strdup (".");
222 saveprefix = concat_dir_and_file (savedir, "cooledit");
223 free (savedir);
224 fd = mc_mkstemps(&savename, saveprefix, NULL);
225 g_free (saveprefix);
226 if (!savename)
227 return 0;
228 /* FIXME:
229 * Close for now because mc_mkstemps use pure open system call
230 * to create temporary file and it needs to be reopened by
231 * VFS-aware mc_open() and MY_O_TEXT should be used.
233 close (fd);
234 } else
235 savename = g_strdup (filename);
237 mc_chmod (savename, edit->stat1.st_mode);
238 mc_chown (savename, edit->stat1.st_uid, edit->stat1.st_gid);
240 if ((fd = mc_open (savename, O_CREAT | O_WRONLY | O_TRUNC | MY_O_TEXT,
241 edit->stat1.st_mode)) == -1)
242 goto error_save;
244 /* pipe save */
245 if ((p = (char *) edit_get_write_filter (savename, filename))) {
246 FILE *file;
248 mc_close (fd);
249 file = (FILE *) popen (p, "w");
251 if (file) {
252 filelen = edit_write_stream (edit, file);
253 #if 1
254 pclose (file);
255 #else
256 if (pclose (file) != 0) {
257 edit_error_dialog (_ (" Error "), catstrs (_ (" Error writing to pipe: "), p, " ", 0));
258 free (p);
259 goto error_save;
261 #endif
262 } else {
263 edit_error_dialog (_ (" Error "), get_sys_error (catstrs (_ (" Failed trying to open pipe for writing: "), p, " ", 0)));
264 free (p);
265 goto error_save;
267 free (p);
268 #ifdef CR_LF_TRANSLATION
269 } else { /* optimised save */
270 filelen = edit_write_stream (edit, f);
271 if (fclose (file))
272 filelen = -1;
273 #else
274 } else {
275 long buf;
276 buf = 0;
277 filelen = edit->last_byte;
278 while (buf <= (edit->curs1 >> S_EDIT_BUF_SIZE) - 1) {
279 if (mc_write (fd, (char *) edit->buffers1[buf], EDIT_BUF_SIZE) != EDIT_BUF_SIZE) {
280 mc_close (fd);
281 goto error_save;
283 buf++;
285 if (mc_write (fd, (char *) edit->buffers1[buf], edit->curs1 & M_EDIT_BUF_SIZE) != (edit->curs1 & M_EDIT_BUF_SIZE)) {
286 filelen = -1;
287 } else if (edit->curs2) {
288 edit->curs2--;
289 buf = (edit->curs2 >> S_EDIT_BUF_SIZE);
290 if (mc_write (fd, (char *) edit->buffers2[buf] + EDIT_BUF_SIZE - (edit->curs2 & M_EDIT_BUF_SIZE) - 1, 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) != 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) {
291 filelen = -1;
292 } else {
293 while (--buf >= 0) {
294 if (mc_write (fd, (char *) edit->buffers2[buf], EDIT_BUF_SIZE) != EDIT_BUF_SIZE) {
295 filelen = -1;
296 break;
300 edit->curs2++;
302 if (mc_close (fd))
303 goto error_save;
304 #endif /* !CR_LF_TRANSLATION */
307 if (filelen != edit->last_byte)
308 goto error_save;
309 if (this_save_mode == 2)
310 if (mc_rename (filename, catstrs (filename, option_backup_ext, 0)) == -1)
311 goto error_save;
312 if (this_save_mode > 0)
313 if (mc_rename (savename, filename) == -1)
314 goto error_save;
315 if (savename)
316 g_free (savename);
317 return 1;
318 error_save:
319 if (savename)
320 g_free (savename);
321 return 0;
325 I changed this from Oleg's original routine so
326 that option_backup_ext works with coolwidgets as well. This
327 does mean there is a memory leak - paul.
329 void menu_save_mode_cmd (void)
331 #define DLG_X 38
332 #define DLG_Y 10
333 static char *str_result;
334 static int save_mode_new;
335 static char *str[] =
337 N_("Quick save "),
338 N_("Safe save "),
339 N_("Do backups -->")};
340 static QuickWidget widgets[] =
342 {quick_button, 18, DLG_X, 7, DLG_Y, N_("&Cancel"), 0,
343 B_CANCEL, 0, 0, "c"},
344 {quick_button, 6, DLG_X, 7, DLG_Y, N_("&Ok"), 0,
345 B_ENTER, 0, 0, "o"},
346 {quick_input, 23, DLG_X, 5, DLG_Y, 0, 9,
347 0, 0, &str_result, "edit-backup-ext"},
348 {quick_label, 22, DLG_X, 4, DLG_Y, N_("Extension:"), 0,
349 0, 0, 0, "savemext"},
350 {quick_radio, 4, DLG_X, 3, DLG_Y, "", 3,
351 0, &save_mode_new, str, "t"},
352 {0}};
353 static QuickDialog dialog =
354 {DLG_X, DLG_Y, -1, -1, N_(" Edit Save Mode "), "[Edit Save Mode]",
355 "esm", widgets};
356 static int i18n_flag = 0;
358 if (!i18n_flag) {
359 int i;
360 int maxlen = 0;
361 int dlg_x;
362 int l1;
364 /* Ok/Cancel buttons */
365 l1 = strlen (_(widgets[0].text)) + strlen (_(widgets[1].text)) + 5;
366 maxlen = max (maxlen, l1);
368 for (i = 0; i < 3; i++ ) {
369 str[i] = _(str[i]);
370 maxlen = max (maxlen, strlen (str[i]) + 7);
372 i18n_flag = 1;
374 dlg_x = maxlen + strlen (_(widgets[3].text)) + 5 + 1;
375 widgets[2].hotkey_pos = strlen (_(widgets[3].text)); /* input field length */
376 dlg_x = min (COLS, dlg_x);
377 dialog.xlen = dlg_x;
379 i = (dlg_x - l1)/3;
380 widgets[1].relative_x = i;
381 widgets[0].relative_x = i + strlen (_(widgets[1].text)) + i + 4;
383 widgets[2].relative_x = widgets[3].relative_x = maxlen + 2;
385 for (i = 0; i < sizeof (widgets)/sizeof (widgets[0]); i++)
386 widgets[i].x_divisions = dlg_x;
389 widgets[2].text = option_backup_ext;
390 widgets[4].value = option_save_mode;
391 if (quick_dialog (&dialog) != B_ENTER)
392 return;
393 option_save_mode = save_mode_new;
394 option_backup_ext = str_result; /* this is a memory leak */
395 option_backup_ext_int = 0;
396 str_result[min (strlen (str_result), sizeof (int))] = '\0';
397 memcpy ((char *) &option_backup_ext_int, str_result, strlen (option_backup_ext));
400 void edit_split_filename (WEdit * edit, const char *f)
402 if (edit->filename)
403 free (edit->filename);
404 edit->filename = (char *) strdup (f);
405 if (edit->dir)
406 free (edit->dir);
407 edit->dir = (char *) strdup ("");
410 /* Here we want to warn the users of overwriting an existing file,
411 but only if they have made a change to the filename */
412 /* returns 1 on success */
413 int edit_save_as_cmd (WEdit * edit)
415 /* This heads the 'Save As' dialog box */
416 char *exp = 0;
417 int different_filename = 0;
419 exp = edit_get_save_file (edit->dir, edit->filename, _(" Save As "));
420 edit_push_action (edit, KEY_PRESS + edit->start_display);
422 if (exp) {
423 if (!*exp) {
424 g_free (exp);
425 edit->force |= REDRAW_COMPLETELY;
426 return 0;
427 } else {
428 if (strcmp(catstrs (edit->dir, edit->filename, 0), exp)) {
429 int file;
430 different_filename = 1;
431 if ((file = mc_open ((char *) exp, O_RDONLY)) != -1) { /* the file exists */
432 mc_close (file);
433 if (edit_query_dialog2 (_(" Warning "),
434 _(" A file already exists with this name. "),
435 /* Push buttons to over-write the current file, or cancel the operation */
436 _("Overwrite"), _("Cancel"))) {
437 edit->force |= REDRAW_COMPLETELY;
438 g_free (exp);
439 return 0;
443 if (edit_save_file (edit, exp)) {
444 edit_split_filename (edit, exp);
445 g_free (exp);
446 edit->modified = 0;
447 edit->delete_file = 0;
448 if (different_filename && !edit->explicit_syntax)
449 edit_load_syntax (edit, 0, 0);
450 edit->force |= REDRAW_COMPLETELY;
451 return 1;
452 } else {
453 g_free (exp);
454 edit_error_dialog (_(" Save as "), get_sys_error (_(" Error trying to save file. ")));
455 edit->force |= REDRAW_COMPLETELY;
456 return 0;
460 edit->force |= REDRAW_COMPLETELY;
461 return 0;
464 /* {{{ Macro stuff starts here */
466 int raw_callback (struct Dlg_head *h, int key, int Msg)
468 switch (Msg) {
469 case DLG_DRAW:
470 common_dialog_repaint (h);
471 break;
473 case DLG_KEY:
474 h->running = 0;
475 h->ret_value = key;
476 return 1;
478 return 0;
481 /* gets a raw key from the keyboard. Passing cancel = 1 draws
482 a cancel button thus allowing c-c etc.. Alternatively, cancel = 0
483 will return the next key pressed */
485 edit_raw_key_query (char *heading, char *query, int cancel)
487 int w = strlen (query) + 7;
488 struct Dlg_head *raw_dlg = create_dlg (0, 0, 7, w, dialog_colors,
489 /* NLS ? */
490 raw_callback, "[Raw Key Query]",
491 "raw_key_input",
492 DLG_CENTER | DLG_TRYUP |
493 DLG_WANT_TAB);
494 x_set_dialog_title (raw_dlg, heading);
495 if (cancel)
496 add_widget (raw_dlg,
497 button_new (4, w / 2 - 5, B_CANCEL, NORMAL_BUTTON,
498 _("Cancel"), 0, 0, 0));
499 add_widget (raw_dlg, label_new (3 - cancel, 2, query, 0));
500 add_widget (raw_dlg,
501 input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0));
502 run_dlg (raw_dlg);
503 w = raw_dlg->ret_value;
504 destroy_dlg (raw_dlg);
505 if (cancel)
506 if (w == XCTRL ('g') || w == XCTRL ('c') || w == ESC_CHAR
507 || w == B_CANCEL)
508 return 0;
509 /* hence ctrl-a (=B_CANCEL), ctrl-g, ctrl-c, and Esc are cannot returned */
510 return w;
513 /* creates a macro file if it doesn't exist */
514 static FILE *edit_open_macro_file (const char *r)
516 char *filename;
517 int file;
518 filename = catstrs (home_dir, MACRO_FILE, 0);
519 if ((file = open (filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
520 return 0;
521 close (file);
522 return fopen (filename, r);
525 #define MAX_MACROS 1024
526 static int saved_macro[MAX_MACROS + 1];
527 static int saved_macros_loaded = 0;
530 This is just to stop the macro file be loaded over and over for keys
531 that aren't defined to anything. On slow systems this could be annoying.
533 int macro_exists (int k)
535 int i;
536 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++)
537 if (saved_macro[i] == k)
538 return i;
539 return -1;
542 /* returns 1 on error */
543 int edit_delete_macro (WEdit * edit, int k)
545 struct macro macro[MAX_MACRO_LENGTH];
546 FILE *f, *g;
547 int s, i, n, j = 0;
549 if (saved_macros_loaded)
550 if ((j = macro_exists (k)) < 0)
551 return 0;
552 g = fopen (catstrs (home_dir, TEMP_FILE, 0), "w");
553 if (!g) {
554 /* This heads the delete macro error dialog box */
555 edit_error_dialog (_(" Delete macro "),
556 /* 'Open' = load temp file */
557 get_sys_error (_(" Error trying to open temp file ")));
558 return 1;
560 f = edit_open_macro_file ("r");
561 if (!f) {
562 /* This heads the delete macro error dialog box */
563 edit_error_dialog (_(" Delete macro "),
564 /* 'Open' = load temp file */
565 get_sys_error (_(" Error trying to open macro file ")));
566 fclose (g);
567 return 1;
569 for (;;) {
570 n = fscanf (f, ("key '%d 0': "), &s);
571 if (!n || n == EOF)
572 break;
573 n = 0;
574 while (fscanf (f, "%hd %hd, ", &macro[n].command, &macro[n].ch))
575 n++;
576 fscanf (f, ";\n");
577 if (s != k) {
578 fprintf (g, ("key '%d 0': "), s);
579 for (i = 0; i < n; i++)
580 fprintf (g, "%hd %hd, ", macro[i].command, macro[i].ch);
581 fprintf (g, ";\n");
584 fclose (f);
585 fclose (g);
586 if (rename (catstrs (home_dir, TEMP_FILE, 0), catstrs (home_dir, MACRO_FILE, 0)) == -1) {
587 /* This heads the delete macro error dialog box */
588 edit_error_dialog (_(" Delete macro "),
589 get_sys_error (_(" Error trying to overwrite macro file ")));
590 return 1;
592 if (saved_macros_loaded)
593 memmove (saved_macro + j, saved_macro + j + 1, sizeof (int) * (MAX_MACROS - j - 1));
594 return 0;
597 /* returns 0 on error */
598 int edit_save_macro_cmd (WEdit * edit, struct macro macro[], int n)
600 FILE *f;
601 int s, i;
603 edit_push_action (edit, KEY_PRESS + edit->start_display);
604 /* This heads the 'Macro' dialog box */
605 s = edit_raw_key_query (_(" Macro "),
606 /* Input line for a single key press follows the ':' */
607 _(" Press the macro's new hotkey: "), 1);
608 edit->force |= REDRAW_COMPLETELY;
609 if (s) {
610 if (edit_delete_macro (edit, s))
611 return 0;
612 f = edit_open_macro_file ("a+");
613 if (f) {
614 fprintf (f, ("key '%d 0': "), s);
615 for (i = 0; i < n; i++)
616 fprintf (f, "%hd %hd, ", macro[i].command, macro[i].ch);
617 fprintf (f, ";\n");
618 fclose (f);
619 if (saved_macros_loaded) {
620 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++);
621 saved_macro[i] = s;
623 return 1;
624 } else
625 /* This heads the 'Save Macro' dialog box */
626 edit_error_dialog (_(" Save macro "), get_sys_error (_(" Error trying to open macro file ")));
628 return 0;
631 void edit_delete_macro_cmd (WEdit * edit)
633 int command;
635 command = edit_raw_key_query (_ (" Delete Macro "),
636 _ (" Press macro hotkey: "), 1);
638 if (!command)
639 return;
641 edit_delete_macro (edit, command);
644 /* return 0 on error */
645 int edit_load_macro_cmd (WEdit * edit, struct macro macro[], int *n, int k)
647 FILE *f;
648 int s, i = 0, found = 0;
650 if (saved_macros_loaded)
651 if (macro_exists (k) < 0)
652 return 0;
654 if ((f = edit_open_macro_file ("r"))) {
655 struct macro dummy;
656 do {
657 int u;
658 u = fscanf (f, ("key '%d 0': "), &s);
659 if (!u || u == EOF)
660 break;
661 if (!saved_macros_loaded)
662 saved_macro[i++] = s;
663 if (!found) {
664 *n = 0;
665 while (*n < MAX_MACRO_LENGTH && 2 == fscanf (f, "%hd %hd, ", &macro[*n].command, &macro[*n].ch))
666 (*n)++;
667 } else {
668 while (2 == fscanf (f, "%hd %hd, ", &dummy.command, &dummy.ch));
670 fscanf (f, ";\n");
671 if (s == k)
672 found = 1;
673 } while (!found || !saved_macros_loaded);
674 if (!saved_macros_loaded) {
675 saved_macro[i] = 0;
676 saved_macros_loaded = 1;
678 fclose (f);
679 return found;
680 } else
681 /* This heads the 'Load Macro' dialog box */
682 edit_error_dialog (_(" Load macro "),
683 get_sys_error (_(" Error trying to open macro file ")));
684 return 0;
687 /* }}} Macro stuff starts here */
689 /* returns 1 on success */
690 int edit_save_confirm_cmd (WEdit * edit)
692 char *f;
694 if (edit_confirm_save) {
695 f = catstrs (_(" Confirm save file? : "), edit->filename, " ", 0);
696 /* Buttons to 'Confirm save file' query */
697 if (edit_query_dialog2 (_(" Save file "), f, _("Save"), _("Cancel")))
698 return 0;
700 return edit_save_cmd (edit);
704 /* returns 1 on success */
705 int edit_save_cmd (WEdit * edit)
707 if (!edit_save_file (edit, catstrs (edit->dir, edit->filename, 0)))
708 return edit_save_as_cmd (edit);
709 edit->force |= REDRAW_COMPLETELY;
710 edit->modified = 0;
711 edit->delete_file = 0;
713 return 1;
717 /* returns 1 on success */
718 int edit_new_cmd (WEdit * edit)
720 if (edit->modified) {
721 if (edit_query_dialog2 (_ (" Warning "), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
722 edit->force |= REDRAW_COMPLETELY;
723 return 0;
726 edit->force |= REDRAW_COMPLETELY;
727 edit->modified = 0;
728 return edit_renew (edit); /* if this gives an error, something has really screwed up */
731 /* returns 1 on error */
732 int edit_load_file_from_filename (WEdit * edit, char *exp)
734 if (!edit_reload (edit, exp, 0, "", 0))
735 return 1;
736 edit_split_filename (edit, exp);
737 edit->modified = 0;
738 return 0;
741 int edit_load_cmd (WEdit * edit)
743 char *exp;
745 if (edit->modified) {
746 if (edit_query_dialog2 (_ (" Warning "), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
747 edit->force |= REDRAW_COMPLETELY;
748 return 0;
752 exp = edit_get_load_file (edit->dir, edit->filename, _ (" Load "));
754 if (exp) {
755 if (*exp)
756 edit_load_file_from_filename (edit, exp);
757 g_free (exp);
759 edit->force |= REDRAW_COMPLETELY;
760 return 0;
764 if mark2 is -1 then marking is from mark1 to the cursor.
765 Otherwise its between the markers. This handles this.
766 Returns 1 if no text is marked.
768 int eval_marks (WEdit * edit, long *start_mark, long *end_mark)
770 if (edit->mark1 != edit->mark2) {
771 if (edit->mark2 >= 0) {
772 *start_mark = min (edit->mark1, edit->mark2);
773 *end_mark = max (edit->mark1, edit->mark2);
774 } else {
775 *start_mark = min (edit->mark1, edit->curs1);
776 *end_mark = max (edit->mark1, edit->curs1);
777 edit->column2 = edit->curs_col;
779 return 0;
780 } else {
781 *start_mark = *end_mark = 0;
782 edit->column2 = edit->column1 = 0;
783 return 1;
787 #define space_width 1
789 void edit_insert_column_of_text (WEdit * edit, unsigned char *data, int size, int width)
791 long cursor;
792 int i, col;
793 cursor = edit->curs1;
794 col = edit_get_col (edit);
795 for (i = 0; i < size; i++) {
796 if (data[i] == '\n') { /* fill in and move to next line */
797 int l;
798 long p;
799 if (edit_get_byte (edit, edit->curs1) != '\n') {
800 l = width - (edit_get_col (edit) - col);
801 while (l > 0) {
802 edit_insert (edit, ' ');
803 l -= space_width;
806 for (p = edit->curs1;; p++) {
807 if (p == edit->last_byte)
808 edit_insert_ahead (edit, '\n');
809 if (edit_get_byte (edit, p) == '\n') {
810 p++;
811 break;
814 edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->curs1);
815 l = col - edit_get_col (edit);
816 while (l >= space_width) {
817 edit_insert (edit, ' ');
818 l -= space_width;
820 continue;
822 edit_insert (edit, data[i]);
824 edit_cursor_move (edit, cursor - edit->curs1);
828 void edit_block_copy_cmd (WEdit * edit)
830 long start_mark, end_mark, current = edit->curs1;
831 int size, x;
832 unsigned char *copy_buf;
834 edit_update_curs_col (edit);
835 x = edit->curs_col;
836 if (eval_marks (edit, &start_mark, &end_mark))
837 return;
838 if (column_highlighting)
839 if ((x >= edit->column1 && x < edit->column2) || (x > edit->column2 && x <= edit->column1))
840 return;
842 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
844 /* all that gets pushed are deletes hence little space is used on the stack */
846 edit_push_markers (edit);
848 if (column_highlighting) {
849 edit_insert_column_of_text (edit, copy_buf, size, abs (edit->column2 - edit->column1));
850 } else {
851 while (size--)
852 edit_insert_ahead (edit, copy_buf[size]);
855 free (copy_buf);
856 edit_scroll_screen_over_cursor (edit);
858 if (column_highlighting) {
859 edit_set_markers (edit, 0, 0, 0, 0);
860 edit_push_action (edit, COLUMN_ON);
861 column_highlighting = 0;
862 } else if (start_mark < current && end_mark > current)
863 edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0);
865 edit->force |= REDRAW_PAGE;
869 void edit_block_move_cmd (WEdit * edit)
871 long count;
872 long current;
873 unsigned char *copy_buf;
874 long start_mark, end_mark;
875 int deleted = 0;
876 int x = 0;
878 if (eval_marks (edit, &start_mark, &end_mark))
879 return;
880 if (column_highlighting) {
881 edit_update_curs_col (edit);
882 x = edit->curs_col;
883 if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
884 if ((x > edit->column1 && x < edit->column2) || (x > edit->column2 && x < edit->column1))
885 return;
886 } else if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
887 return;
889 if ((end_mark - start_mark) > option_max_undo / 2)
890 if (edit_query_dialog2 (_ (" Warning "), _ (" Block is large, you may not be able to undo this action. "), _ ("Continue"), _ ("Cancel")))
891 return;
893 edit_push_markers (edit);
894 current = edit->curs1;
895 if (column_highlighting) {
896 int size, c1, c2, line;
897 line = edit->curs_line;
898 if (edit->mark2 < 0)
899 edit_mark_cmd (edit, 0);
900 c1 = min (edit->column1, edit->column2);
901 c2 = max (edit->column1, edit->column2);
902 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
903 if (x < c2) {
904 edit_block_delete_cmd (edit);
905 deleted = 1;
907 edit_move_to_line (edit, line);
908 edit_cursor_move (edit, edit_move_forward3 (edit, edit_bol (edit, edit->curs1), x, 0) - edit->curs1);
909 edit_insert_column_of_text (edit, copy_buf, size, c2 - c1);
910 if (!deleted) {
911 line = edit->curs_line;
912 edit_update_curs_col (edit);
913 x = edit->curs_col;
914 edit_block_delete_cmd (edit);
915 edit_move_to_line (edit, line);
916 edit_cursor_move (edit, edit_move_forward3 (edit, edit_bol (edit, edit->curs1), x, 0) - edit->curs1);
918 edit_set_markers (edit, 0, 0, 0, 0);
919 edit_push_action (edit, COLUMN_ON);
920 column_highlighting = 0;
921 } else {
922 copy_buf = malloc (end_mark - start_mark);
923 edit_cursor_move (edit, start_mark - edit->curs1);
924 edit_scroll_screen_over_cursor (edit);
925 count = start_mark;
926 while (count < end_mark) {
927 copy_buf[end_mark - count - 1] = edit_delete (edit);
928 count++;
930 edit_scroll_screen_over_cursor (edit);
931 edit_cursor_move (edit, current - edit->curs1 - (((current - edit->curs1) > 0) ? end_mark - start_mark : 0));
932 edit_scroll_screen_over_cursor (edit);
933 while (count-- > start_mark)
934 edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
935 edit_set_markers (edit, edit->curs1, edit->curs1 + end_mark - start_mark, 0, 0);
937 edit_scroll_screen_over_cursor (edit);
938 free (copy_buf);
939 edit->force |= REDRAW_PAGE;
942 void edit_cursor_to_bol (WEdit * edit);
944 void edit_delete_column_of_text (WEdit * edit)
946 long p, q, r, m1, m2;
947 int b, c, d;
948 int n;
950 eval_marks (edit, &m1, &m2);
951 n = edit_move_forward (edit, m1, 0, m2) + 1;
952 c = edit_move_forward3 (edit, edit_bol (edit, m1), 0, m1);
953 d = edit_move_forward3 (edit, edit_bol (edit, m2), 0, m2);
955 b = min (c, d);
956 c = max (c, d);
958 while (n--) {
959 r = edit_bol (edit, edit->curs1);
960 p = edit_move_forward3 (edit, r, b, 0);
961 q = edit_move_forward3 (edit, r, c, 0);
962 if (p < m1)
963 p = m1;
964 if (q > m2)
965 q = m2;
966 edit_cursor_move (edit, p - edit->curs1);
967 while (q > p) { /* delete line between margins */
968 if (edit_get_byte (edit, edit->curs1) != '\n')
969 edit_delete (edit);
970 q--;
972 if (n) /* move to next line except on the last delete */
973 edit_cursor_move (edit, edit_move_forward (edit, edit->curs1, 1, 0) - edit->curs1);
977 /* if success return 0 */
978 int edit_block_delete (WEdit * edit)
980 long count;
981 long start_mark, end_mark;
982 if (eval_marks (edit, &start_mark, &end_mark))
983 return 0;
984 if (column_highlighting && edit->mark2 < 0)
985 edit_mark_cmd (edit, 0);
986 if ((end_mark - start_mark) > option_max_undo / 2)
987 /* Warning message with a query to continue or cancel the operation */
988 if (edit_query_dialog2 (_ (" Warning "), _ (" Block is large, you may not be able to undo this action. "), _ (" Continue "), _ (" Cancel ")))
989 return 1;
990 edit_push_markers (edit);
991 edit_cursor_move (edit, start_mark - edit->curs1);
992 edit_scroll_screen_over_cursor (edit);
993 count = start_mark;
994 if (start_mark < end_mark) {
995 if (column_highlighting) {
996 if (edit->mark2 < 0)
997 edit_mark_cmd (edit, 0);
998 edit_delete_column_of_text (edit);
999 } else {
1000 while (count < end_mark) {
1001 edit_delete (edit);
1002 count++;
1006 edit_set_markers (edit, 0, 0, 0, 0);
1007 edit->force |= REDRAW_PAGE;
1008 return 0;
1011 /* returns 1 if canceelled by user */
1012 int edit_block_delete_cmd (WEdit * edit)
1014 long start_mark, end_mark;
1015 if (eval_marks (edit, &start_mark, &end_mark)) {
1016 edit_delete_line (edit);
1017 return 0;
1019 return edit_block_delete (edit);
1023 #define INPUT_INDEX 9
1024 #define SEARCH_DLG_WIDTH 58
1025 #define SEARCH_DLG_HEIGHT 10
1026 #define REPLACE_DLG_WIDTH 58
1027 #define REPLACE_DLG_HEIGHT 15
1028 #define CONFIRM_DLG_WIDTH 79
1029 #define CONFIRM_DLG_HEIGTH 6
1030 #define B_REPLACE_ALL B_USER+1
1031 #define B_REPLACE_ONE B_USER+2
1032 #define B_SKIP_REPLACE B_USER+3
1034 int edit_replace_prompt (WEdit * edit, char *replace_text, int xpos, int ypos)
1036 QuickWidget quick_widgets[] =
1038 {quick_button, 63, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Cancel"),
1039 0, B_CANCEL, 0, 0, NULL},
1040 {quick_button, 50, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("o&Ne"),
1041 0, B_REPLACE_ONE, 0, 0, NULL},
1042 {quick_button, 37, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("al&L"),
1043 0, B_REPLACE_ALL, 0, 0, NULL},
1044 {quick_button, 21, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Skip"),
1045 0, B_SKIP_REPLACE, 0, 0, NULL},
1046 {quick_button, 4, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Replace"),
1047 0, B_ENTER, 0, 0, NULL},
1048 {quick_label, 2, CONFIRM_DLG_WIDTH, 2, CONFIRM_DLG_HEIGTH, 0,
1049 0, 0, 0, 0, 0},
1050 {0}};
1052 #ifdef HAVE_CHARSET
1053 char *msg = _(" Replace with: ");
1055 quick_widgets[5].text = catstrs (msg, replace_text, 0);
1057 if (*replace_text)
1058 convert_to_display (quick_widgets[5].text + strlen (msg));
1059 #else
1060 quick_widgets[5].text = catstrs (_ (" Replace with: "), replace_text, 0);
1061 #endif /* !HAVE_CHARSET */
1064 QuickDialog Quick_input =
1065 {CONFIRM_DLG_WIDTH, CONFIRM_DLG_HEIGTH, 0, 0, N_ (" Confirm replace "),
1066 "[Input Line Keys]", "quick_input", 0 /*quick_widgets */ };
1068 Quick_input.widgets = quick_widgets;
1070 Quick_input.xpos = xpos;
1072 /* Sometimes menu can hide replaced text. I don't like it */
1074 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + CONFIRM_DLG_HEIGTH - 1))
1075 ypos -= CONFIRM_DLG_HEIGTH;
1077 Quick_input.ypos = ypos;
1078 return quick_dialog (&Quick_input);
1082 void edit_replace_dialog (WEdit * edit, char **search_text, char **replace_text, char **arg_order)
1084 int treplace_scanf = replace_scanf;
1085 int treplace_regexp = replace_regexp;
1086 int treplace_all = replace_all;
1087 int treplace_prompt = replace_prompt;
1088 int treplace_backwards = replace_backwards;
1089 int treplace_whole = replace_whole;
1090 int treplace_case = replace_case;
1092 QuickWidget quick_widgets[] =
1094 {quick_button, 6, 10, 12, REPLACE_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1095 0, NULL},
1096 {quick_button, 2, 10, 12, REPLACE_DLG_HEIGHT, N_("&Ok"), 0, B_ENTER, 0,
1097 0, NULL},
1098 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1099 0, 0, NULL},
1100 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("replace &All"), 0, 0,
1101 0, 0, NULL},
1102 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("pr&Ompt on replace"), 0, 0,
1103 0, 0, NULL},
1104 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1105 0, 0, NULL},
1106 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1107 0, 0, NULL},
1108 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1109 0, 0, NULL},
1110 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 8, REPLACE_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1111 0, 0, NULL},
1112 {quick_input, 3, REPLACE_DLG_WIDTH, 7, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1113 0, "edit-argord"},
1114 {quick_label, 2, REPLACE_DLG_WIDTH, 6, REPLACE_DLG_HEIGHT, N_(" Enter replacement argument order eg. 3,2,1,4 "), 0, 0,
1115 0, 0, 0},
1116 {quick_input, 3, REPLACE_DLG_WIDTH, 5, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1117 0, "edit-replace"},
1118 {quick_label, 2, REPLACE_DLG_WIDTH, 4, REPLACE_DLG_HEIGHT, N_(" Enter replacement string:"), 0, 0, 0,
1119 0, 0},
1120 {quick_input, 3, REPLACE_DLG_WIDTH, 3, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1121 0, "edit-search"},
1122 {quick_label, 2, REPLACE_DLG_WIDTH, 2, REPLACE_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1123 0, 0},
1124 {0}};
1126 quick_widgets[2].result = &treplace_scanf;
1127 quick_widgets[3].result = &treplace_all;
1128 quick_widgets[4].result = &treplace_prompt;
1129 quick_widgets[5].result = &treplace_backwards;
1130 quick_widgets[6].result = &treplace_regexp;
1131 quick_widgets[7].result = &treplace_whole;
1132 quick_widgets[8].result = &treplace_case;
1133 quick_widgets[9].str_result = arg_order;
1134 quick_widgets[9].text = *arg_order;
1135 quick_widgets[11].str_result = replace_text;
1136 quick_widgets[11].text = *replace_text;
1137 quick_widgets[13].str_result = search_text;
1138 quick_widgets[13].text = *search_text;
1140 QuickDialog Quick_input =
1141 {REPLACE_DLG_WIDTH, REPLACE_DLG_HEIGHT, -1, 0, N_(" Replace "),
1142 "[Input Line Keys]", "quick_input", 0 /*quick_widgets */ };
1144 Quick_input.widgets = quick_widgets;
1146 if (quick_dialog (&Quick_input) != B_CANCEL) {
1147 replace_scanf = treplace_scanf;
1148 replace_backwards = treplace_backwards;
1149 replace_regexp = treplace_regexp;
1150 replace_all = treplace_all;
1151 replace_prompt = treplace_prompt;
1152 replace_whole = treplace_whole;
1153 replace_case = treplace_case;
1154 return;
1155 } else {
1156 *arg_order = NULL;
1157 *replace_text = NULL;
1158 *search_text = NULL;
1159 return;
1165 void edit_search_dialog (WEdit * edit, char **search_text)
1167 int treplace_scanf = replace_scanf;
1168 int treplace_regexp = replace_regexp;
1169 int treplace_whole = replace_whole;
1170 int treplace_case = replace_case;
1171 int treplace_backwards = replace_backwards;
1173 QuickWidget quick_widgets[] =
1175 {quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1176 0, NULL},
1177 {quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&Ok"), 0, B_ENTER, 0,
1178 0, NULL},
1179 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1180 0, 0, NULL },
1181 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1182 0, 0, NULL},
1183 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1184 0, 0, NULL},
1185 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1186 0, 0, NULL},
1187 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1188 0, 0, NULL},
1189 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, 0,
1190 0, "edit-search"},
1191 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1192 0, 0},
1193 {0}};
1195 quick_widgets[2].result = &treplace_scanf;
1196 quick_widgets[3].result = &treplace_backwards;
1197 quick_widgets[4].result = &treplace_regexp;
1198 quick_widgets[5].result = &treplace_whole;
1199 quick_widgets[6].result = &treplace_case;
1200 quick_widgets[7].str_result = search_text;
1201 quick_widgets[7].text = *search_text;
1204 QuickDialog Quick_input =
1205 {SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_(" Search "),
1206 "[Input Line Keys]", "quick_input", 0 /*quick_widgets */ };
1208 Quick_input.widgets = quick_widgets;
1210 if (quick_dialog (&Quick_input) != B_CANCEL) {
1211 replace_scanf = treplace_scanf;
1212 replace_backwards = treplace_backwards;
1213 replace_regexp = treplace_regexp;
1214 replace_whole = treplace_whole;
1215 replace_case = treplace_case;
1216 } else {
1217 *search_text = NULL;
1223 static long sargs[NUM_REPL_ARGS][256 / sizeof (long)];
1225 #define SCANF_ARGS sargs[0], sargs[1], sargs[2], sargs[3], \
1226 sargs[4], sargs[5], sargs[6], sargs[7], \
1227 sargs[8], sargs[9], sargs[10], sargs[11], \
1228 sargs[12], sargs[13], sargs[14], sargs[15]
1230 #define PRINTF_ARGS sargs[argord[0]], sargs[argord[1]], sargs[argord[2]], sargs[argord[3]], \
1231 sargs[argord[4]], sargs[argord[5]], sargs[argord[6]], sargs[argord[7]], \
1232 sargs[argord[8]], sargs[argord[9]], sargs[argord[10]], sargs[argord[11]], \
1233 sargs[argord[12]], sargs[argord[13]], sargs[argord[14]], sargs[argord[15]]
1236 /* This function is a modification of mc-3.2.10/src/view.c:regexp_view_search() */
1237 /* returns -3 on error in pattern, -1 on not found, found_len = 0 if either */
1238 int string_regexp_search (char *pattern, char *string, int len, int match_type, int match_bol, int icase, int *found_len, void *d)
1240 static regex_t r;
1241 static char *old_pattern = NULL;
1242 static int old_type, old_icase;
1243 regmatch_t *pmatch;
1244 static regmatch_t s[1];
1246 pmatch = (regmatch_t *) d;
1247 if (!pmatch)
1248 pmatch = s;
1250 if (!old_pattern || strcmp (old_pattern, pattern) || old_type != match_type || old_icase != icase) {
1251 if (old_pattern) {
1252 regfree (&r);
1253 free (old_pattern);
1254 old_pattern = 0;
1256 if (regcomp (&r, pattern, REG_EXTENDED | (icase ? REG_ICASE : 0))) {
1257 *found_len = 0;
1258 return -3;
1260 old_pattern = (char *) strdup (pattern);
1261 old_type = match_type;
1262 old_icase = icase;
1264 if (regexec (&r, string, d ? NUM_REPL_ARGS : 1, pmatch, ((match_bol || match_type != match_normal) ? 0 : REG_NOTBOL)) != 0) {
1265 *found_len = 0;
1266 return -1;
1268 *found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1269 return (pmatch[0].rm_so);
1272 /* thanks to Liviu Daia <daia@stoilow.imar.ro> for getting this
1273 (and the above) routines to work properly - paul */
1275 long edit_find_string (long start, unsigned char *exp, int *len, long last_byte, int (*get_byte) (void *, long), void *data, int once_only, void *d)
1277 long p, q = 0;
1278 long l = strlen ((char *) exp), f = 0;
1279 int n = 0;
1281 for (p = 0; p < l; p++) /* count conversions... */
1282 if (exp[p] == '%')
1283 if (exp[++p] != '%') /* ...except for "%%" */
1284 n++;
1286 if (replace_scanf || replace_regexp) {
1287 int c;
1288 unsigned char *buf;
1289 unsigned char mbuf[MAX_REPL_LEN * 2 + 3];
1291 replace_scanf = (!replace_regexp); /* can't have both */
1293 buf = mbuf;
1295 if (replace_scanf) {
1296 unsigned char e[MAX_REPL_LEN];
1297 if (n >= NUM_REPL_ARGS)
1298 return -3;
1300 if (replace_case) {
1301 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++)
1302 buf[p - start] = (*get_byte) (data, p);
1303 } else {
1304 for (p = 0; exp[p] != 0; p++)
1305 exp[p] = my_lower_case (exp[p]);
1306 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++) {
1307 c = (*get_byte) (data, p);
1308 buf[p - start] = my_lower_case (c);
1312 buf[(q = p - start)] = 0;
1313 strcpy ((char *) e, (char *) exp);
1314 strcat ((char *) e, "%n");
1315 exp = e;
1317 while (q) {
1318 *((int *) sargs[n]) = 0; /* --> here was the problem - now fixed: good */
1319 if (n == sscanf ((char *) buf, (char *) exp, SCANF_ARGS)) {
1320 if (*((int *) sargs[n])) {
1321 *len = *((int *) sargs[n]);
1322 return start;
1325 if (once_only)
1326 return -2;
1327 if (q + start < last_byte) {
1328 if (replace_case) {
1329 buf[q] = (*get_byte) (data, q + start);
1330 } else {
1331 c = (*get_byte) (data, q + start);
1332 buf[q] = my_lower_case (c);
1334 q++;
1336 buf[q] = 0;
1337 start++;
1338 buf++; /* move the window along */
1339 if (buf == mbuf + MAX_REPL_LEN) { /* the window is about to go past the end of array, so... */
1340 memmove (mbuf, buf, strlen ((char *) buf) + 1); /* reset it */
1341 buf = mbuf;
1343 q--;
1345 } else { /* regexp matching */
1346 long offset = 0;
1347 int found_start, match_bol, move_win = 0;
1349 while (start + offset < last_byte) {
1350 match_bol = (offset == 0 || (*get_byte) (data, start + offset - 1) == '\n');
1351 if (!move_win) {
1352 p = start + offset;
1353 q = 0;
1355 for (; p < last_byte && q < MAX_REPL_LEN; p++, q++) {
1356 mbuf[q] = (*get_byte) (data, p);
1357 if (mbuf[q] == '\n')
1358 break;
1360 q++;
1361 offset += q;
1362 mbuf[q] = 0;
1364 buf = mbuf;
1365 while (q) {
1366 found_start = string_regexp_search ((char *) exp, (char *) buf, q, match_normal, match_bol, !replace_case, len, d);
1368 if (found_start <= -2) { /* regcomp/regexec error */
1369 *len = 0;
1370 return -3;
1372 else if (found_start == -1) /* not found: try next line */
1373 break;
1374 else if (*len == 0) { /* null pattern: try again at next character */
1375 q--;
1376 buf++;
1377 match_bol = 0;
1378 continue;
1380 else /* found */
1381 return (start + offset - q + found_start);
1383 if (once_only)
1384 return -2;
1386 if (buf[q - 1] != '\n') { /* incomplete line: try to recover */
1387 buf = mbuf + MAX_REPL_LEN / 2;
1388 q = strlen ((char *) buf);
1389 memmove (mbuf, buf, q);
1390 p = start + q;
1391 move_win = 1;
1393 else
1394 move_win = 0;
1397 } else {
1398 *len = strlen ((char *) exp);
1399 if (replace_case) {
1400 for (p = start; p <= last_byte - l; p++) {
1401 if ((*get_byte) (data, p) == (unsigned char)exp[0]) { /* check if first char matches */
1402 for (f = 0, q = 0; q < l && f < 1; q++)
1403 if ((*get_byte) (data, q + p) != (unsigned char)exp[q])
1404 f = 1;
1405 if (f == 0)
1406 return p;
1408 if (once_only)
1409 return -2;
1411 } else {
1412 for (p = 0; exp[p] != 0; p++)
1413 exp[p] = my_lower_case (exp[p]);
1415 for (p = start; p <= last_byte - l; p++) {
1416 if (my_lower_case ((*get_byte) (data, p)) == (unsigned char)exp[0]) {
1417 for (f = 0, q = 0; q < l && f < 1; q++)
1418 if (my_lower_case ((*get_byte) (data, q + p)) != (unsigned char)exp[q])
1419 f = 1;
1420 if (f == 0)
1421 return p;
1423 if (once_only)
1424 return -2;
1428 return -2;
1432 long edit_find_forwards (long search_start, unsigned char *exp, int *len, long last_byte, int (*get_byte) (void *, long), void *data, int once_only, void *d)
1433 { /*front end to find_string to check for
1434 whole words */
1435 long p;
1436 p = search_start;
1438 while ((p = edit_find_string (p, exp, len, last_byte, get_byte, data, once_only, d)) >= 0) {
1439 if (replace_whole) {
1440 /*If the bordering chars are not in option_whole_chars_search then word is whole */
1441 if (!strcasechr (option_whole_chars_search, (*get_byte) (data, p - 1))
1442 && !strcasechr (option_whole_chars_search, (*get_byte) (data, p + *len)))
1443 return p;
1444 if (once_only)
1445 return -2;
1446 } else
1447 return p;
1448 if (once_only)
1449 break;
1450 p++; /*not a whole word so continue search. */
1452 return p;
1455 long edit_find (long search_start, unsigned char *exp, int *len, long last_byte, int (*get_byte) (void *, long), void *data, void *d)
1457 long p;
1458 if (replace_backwards) {
1459 while (search_start >= 0) {
1460 p = edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 1, d);
1461 if (p == search_start)
1462 return p;
1463 search_start--;
1465 } else {
1466 return edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 0, d);
1468 return -2;
1471 #define is_digit(x) ((x) >= '0' && (x) <= '9')
1473 #define snprintf(v) { \
1474 *p1++ = *p++; \
1475 *p1++ = '%'; \
1476 *p1++ = 'n'; \
1477 *p1 = '\0'; \
1478 sprintf(s,q1,v,&n); \
1479 s += n; \
1482 /* this function uses the sprintf command to do a vprintf */
1483 /* it takes pointers to arguments instead of the arguments themselves */
1484 static int sprintf_p (char *str, const char *fmt,...)
1485 __attribute__ ((format (printf, 2, 3)));
1487 static int sprintf_p (char *str, const char *fmt,...)
1489 va_list ap;
1490 int n;
1491 char *q, *p, *s = str;
1492 char q1[32];
1493 char *p1;
1495 va_start (ap, fmt);
1496 p = q = (char *) fmt;
1498 while ((p = strchr (p, '%'))) {
1499 n = p - q;
1500 strncpy (s, q, n); /* copy stuff between format specifiers */
1501 s += n;
1502 *s = 0;
1503 q = p;
1504 p1 = q1;
1505 *p1++ = *p++;
1506 if (*p == '%') {
1507 p++;
1508 *s++ = '%';
1509 q = p;
1510 continue;
1512 if (*p == 'n') {
1513 p++;
1514 /* do nothing */
1515 q = p;
1516 continue;
1518 if (*p == '#')
1519 *p1++ = *p++;
1520 if (*p == '0')
1521 *p1++ = *p++;
1522 if (*p == '-')
1523 *p1++ = *p++;
1524 if (*p == '+')
1525 *p1++ = *p++;
1526 if (*p == '*') {
1527 p++;
1528 strcpy (p1, itoa (*va_arg (ap, int *))); /* replace field width with a number */
1529 p1 += strlen (p1);
1530 } else {
1531 while (is_digit (*p))
1532 *p1++ = *p++;
1534 if (*p == '.')
1535 *p1++ = *p++;
1536 if (*p == '*') {
1537 p++;
1538 strcpy (p1, itoa (*va_arg (ap, int *))); /* replace precision with a number */
1539 p1 += strlen (p1);
1540 } else {
1541 while (is_digit (*p))
1542 *p1++ = *p++;
1544 /* flags done, now get argument */
1545 if (*p == 's') {
1546 snprintf (va_arg (ap, char *));
1547 } else if (*p == 'h') {
1548 if (strchr ("diouxX", *p))
1549 snprintf (*va_arg (ap, short *));
1550 } else if (*p == 'l') {
1551 *p1++ = *p++;
1552 if (strchr ("diouxX", *p))
1553 snprintf (*va_arg (ap, long *));
1554 } else if (strchr ("cdiouxX", *p)) {
1555 snprintf (*va_arg (ap, int *));
1556 } else if (*p == 'L') {
1557 *p1++ = *p++;
1558 if (strchr ("EefgG", *p))
1559 snprintf (*va_arg (ap, double *)); /* should be long double */
1560 } else if (strchr ("EefgG", *p)) {
1561 snprintf (*va_arg (ap, double *));
1562 } else if (strchr ("DOU", *p)) {
1563 snprintf (*va_arg (ap, long *));
1564 } else if (*p == 'p') {
1565 snprintf (*va_arg (ap, void **));
1567 q = p;
1569 va_end (ap);
1570 sprintf (s, q); /* print trailing leftover */
1571 return s - str + strlen (s);
1574 static void regexp_error (WEdit *edit)
1576 /* "Error: Syntax error in regular expression, or scanf expression contained too many %'s */
1577 edit_error_dialog (_(" Error "), _(" Invalid regular expression, or scanf expression with to many conversions "));
1580 /* call with edit = 0 before shutdown to close memory leaks */
1581 void edit_replace_cmd (WEdit * edit, int again)
1583 static regmatch_t pmatch[NUM_REPL_ARGS];
1584 static char *old1 = NULL;
1585 static char *old2 = NULL;
1586 static char *old3 = NULL;
1587 char *exp1 = "";
1588 char *exp2 = "";
1589 char *exp3 = "";
1590 int replace_yes;
1591 int replace_continue;
1592 int treplace_prompt = 0;
1593 int i = 0;
1594 long times_replaced = 0, last_search;
1595 int argord[NUM_REPL_ARGS];
1597 if (!edit) {
1598 if (old1) {
1599 g_free (old1);
1600 old1 = 0;
1602 if (old2) {
1603 g_free (old2);
1604 old2 = 0;
1606 if (old3) {
1607 g_free (old3);
1608 old3 = 0;
1610 return;
1612 last_search = edit->last_byte;
1614 edit->force |= REDRAW_COMPLETELY;
1616 exp1 = old1 ? old1 : exp1;
1617 exp2 = old2 ? old2 : exp2;
1618 exp3 = old3 ? old3 : exp3;
1620 if (again) {
1621 if (!old1 || !old2)
1622 return;
1623 exp1 = g_strdup (old1);
1624 exp2 = g_strdup (old2);
1625 exp3 = g_strdup (old3);
1626 } else {
1627 edit_push_action (edit, KEY_PRESS + edit->start_display);
1629 #ifdef HAVE_CHARSET
1630 if (exp1 && *exp1)
1631 convert_to_display (exp1);
1632 if (exp2 && *exp2)
1633 convert_to_display (exp2);
1634 #endif /* HAVE_CHARSET */
1636 edit_replace_dialog (edit, &exp1, &exp2, &exp3);
1638 #ifdef HAVE_CHARSET
1639 if (exp1 && *exp1)
1640 convert_from_input (exp1);
1641 if (exp2 && *exp2)
1642 convert_from_input (exp2);
1643 #endif /* HAVE_CHARSET */
1645 treplace_prompt = replace_prompt;
1648 if (!exp1 || !*exp1) {
1649 edit->force = REDRAW_COMPLETELY;
1650 if (exp1)
1651 g_free (exp1);
1652 if (exp2)
1653 g_free (exp2);
1654 if (exp3)
1655 g_free (exp3);
1656 return;
1658 if (old1)
1659 g_free (old1);
1660 if (old2)
1661 g_free (old2);
1662 if (old3)
1663 g_free (old3);
1664 old1 = g_strdup (exp1);
1665 old2 = g_strdup (exp2);
1666 old3 = g_strdup (exp3);
1669 char *s;
1670 int ord;
1671 while ((s = strchr (exp3, ' ')))
1672 memmove (s, s + 1, strlen (s));
1673 s = exp3;
1674 for (i = 0; i < NUM_REPL_ARGS; i++) {
1675 if (s != (char *)1 && *s) {
1676 ord = atoi (s);
1677 if ((ord > 0) && (ord < NUM_REPL_ARGS))
1678 argord[i] = ord - 1;
1679 else
1680 argord[i] = i;
1681 s = strchr (s, ',') + 1;
1682 } else
1683 argord[i] = i;
1687 replace_continue = replace_all;
1689 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
1690 edit->search_start--;
1692 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
1693 edit->search_start++;
1695 do {
1696 int len = 0;
1697 long new_start;
1698 new_start = edit_find (edit->search_start, (unsigned char *) exp1, &len, last_search,
1699 (int (*)(void *, long)) edit_get_byte, (void *) edit, pmatch);
1700 if (new_start == -3) {
1701 regexp_error (edit);
1702 break;
1704 edit->search_start = new_start;
1705 /*returns negative on not found or error in pattern */
1707 if (edit->search_start >= 0) {
1708 edit->found_start = edit->search_start;
1709 i = edit->found_len = len;
1711 edit_cursor_move (edit, edit->search_start - edit->curs1);
1712 edit_scroll_screen_over_cursor (edit);
1714 replace_yes = 1;
1716 if (treplace_prompt) {
1717 int l;
1718 l = edit->curs_row - edit->num_widget_lines / 3;
1719 if (l > 0)
1720 edit_scroll_downward (edit, l);
1721 if (l < 0)
1722 edit_scroll_upward (edit, -l);
1724 edit_scroll_screen_over_cursor (edit);
1725 edit->force |= REDRAW_PAGE;
1726 edit_render_keypress (edit);
1728 /*so that undo stops at each query */
1729 edit_push_key_press (edit);
1731 switch (edit_replace_prompt (edit, exp2, /* and prompt 2/3 down */
1732 (edit->num_widget_columns - CONFIRM_DLG_WIDTH)/2,
1733 edit->num_widget_lines * 2 / 3)) {
1734 case B_ENTER:
1735 break;
1736 case B_SKIP_REPLACE:
1737 replace_yes = 0;
1738 break;
1739 case B_REPLACE_ALL:
1740 treplace_prompt = 0;
1741 replace_continue = 1;
1742 break;
1743 case B_REPLACE_ONE:
1744 replace_continue = 0;
1745 break;
1746 case B_CANCEL:
1747 replace_yes = 0;
1748 replace_continue = 0;
1749 break;
1752 if (replace_yes) { /* delete then insert new */
1753 if (replace_scanf || replace_regexp) {
1754 char repl_str[MAX_REPL_LEN + 2];
1755 if (replace_regexp) { /* we need to fill in sargs just like with scanf */
1756 int k, j;
1757 for (k = 1; k < NUM_REPL_ARGS && pmatch[k].rm_eo >= 0; k++) {
1758 unsigned char *t;
1759 t = (unsigned char *) &sargs[k - 1][0];
1760 for (j = 0; j < pmatch[k].rm_eo - pmatch[k].rm_so && j < 255; j++, t++)
1761 *t = (unsigned char) edit_get_byte (edit, edit->search_start - pmatch[0].rm_so + pmatch[k].rm_so + j);
1762 *t = '\0';
1764 for (; k <= NUM_REPL_ARGS; k++)
1765 sargs[k - 1][0] = 0;
1767 if (sprintf_p (repl_str, exp2, PRINTF_ARGS) >= 0) {
1768 times_replaced++;
1769 while (i--)
1770 edit_delete (edit);
1771 while (repl_str[++i])
1772 edit_insert (edit, repl_str[i]);
1773 } else {
1774 edit_error_dialog (_ (" Replace "),
1775 /* "Invalid regexp string or scanf string" */
1776 _ (" Error in replacement format string. "));
1777 replace_continue = 0;
1779 } else {
1780 times_replaced++;
1781 while (i--)
1782 edit_delete (edit);
1783 while (exp2[++i])
1784 edit_insert (edit, exp2[i]);
1786 edit->found_len = i;
1788 /* so that we don't find the same string again */
1789 if (replace_backwards) {
1790 last_search = edit->search_start;
1791 edit->search_start--;
1792 } else {
1793 edit->search_start += i;
1794 last_search = edit->last_byte;
1796 edit_scroll_screen_over_cursor (edit);
1797 } else {
1798 edit->search_start = edit->curs1; /* try and find from right here for next search */
1799 edit_update_curs_col (edit);
1801 edit->force |= REDRAW_PAGE;
1802 edit_render_keypress (edit);
1803 if (times_replaced) {
1804 message (0, _(" Replace "), _(" %ld replacements made. "), times_replaced);
1805 } else
1806 edit_message_dialog (_ (" Replace "), _ (" Search string not found. "));
1807 replace_continue = 0;
1809 } while (replace_continue);
1811 g_free (exp1);
1812 g_free (exp2);
1813 g_free (exp3);
1814 edit->force = REDRAW_COMPLETELY;
1815 edit_scroll_screen_over_cursor (edit);
1821 void edit_search_cmd (WEdit * edit, int again)
1823 static char *old = NULL;
1824 char *exp = "";
1826 if (!edit) {
1827 if (old) {
1828 g_free (old);
1829 old = 0;
1831 return;
1833 exp = old ? old : exp;
1834 if (again) { /*ctrl-hotkey for search again. */
1835 if (!old)
1836 return;
1837 exp = (char *) g_strdup (old);
1838 } else {
1840 #ifdef HAVE_CHARSET
1841 if (exp && *exp)
1842 convert_to_display (exp);
1843 #endif /* HAVE_CHARSET */
1845 edit_search_dialog (edit, &exp);
1847 #ifdef HAVE_CHARSET
1848 if (exp && *exp)
1849 convert_from_input (exp);
1850 #endif /* HAVE_CHARSET */
1852 edit_push_action (edit, KEY_PRESS + edit->start_display);
1855 if (exp) {
1856 if (*exp) {
1857 int len = 0;
1858 if (old)
1859 g_free (old);
1860 old = (char *) g_strdup (exp);
1862 if (search_create_bookmark) {
1863 int found = 0, books = 0;
1864 int l = 0, l_last = -1;
1865 long p, q = 0;
1866 for (;;) {
1867 p = edit_find (q, (unsigned char *) exp, &len, edit->last_byte,
1868 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
1869 if (p < 0)
1870 break;
1871 found++;
1872 l += edit_count_lines (edit, q, p);
1873 if (l != l_last) {
1874 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
1875 books++;
1877 l_last = l;
1878 q = p + 1;
1880 if (found) {
1881 /* in response to number of bookmarks added because of string being found %d times */
1882 message (0, _(" Search "), _(" %d finds made, %d bookmarks added "), found, books);
1883 } else {
1884 edit_error_dialog (_ (" Search "), _ (" Search string not found. "));
1886 } else {
1888 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
1889 edit->search_start--;
1891 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
1892 edit->search_start++;
1894 edit->search_start = edit_find (edit->search_start, (unsigned char *) exp, &len, edit->last_byte,
1895 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
1897 if (edit->search_start >= 0) {
1898 edit->found_start = edit->search_start;
1899 edit->found_len = len;
1901 edit_cursor_move (edit, edit->search_start - edit->curs1);
1902 edit_scroll_screen_over_cursor (edit);
1903 if (replace_backwards)
1904 edit->search_start--;
1905 else
1906 edit->search_start++;
1907 } else if (edit->search_start == -3) {
1908 edit->search_start = edit->curs1;
1909 regexp_error (edit);
1910 } else {
1911 edit->search_start = edit->curs1;
1912 edit_error_dialog (_ (" Search "), _ (" Search string not found. "));
1916 g_free (exp);
1918 edit->force |= REDRAW_COMPLETELY;
1919 edit_scroll_screen_over_cursor (edit);
1923 /* Real edit only */
1924 void edit_quit_cmd (WEdit * edit)
1926 edit_push_action (edit, KEY_PRESS + edit->start_display);
1928 edit->force |= REDRAW_COMPLETELY;
1929 if (edit->modified) {
1930 switch (edit_query_dialog3 (_ (" Quit "), _ (" File was modified, Save with exit? "), _ ("Cancel quit"), _ ("&Yes"), _ ("&No"))) {
1931 case 1:
1932 edit_push_markers (edit);
1933 edit_set_markers (edit, 0, 0, 0, 0);
1934 if (!edit_save_cmd (edit))
1935 return;
1936 break;
1937 case 2:
1938 if (edit->delete_file)
1939 unlink (catstrs (edit->dir, edit->filename, 0));
1940 break;
1941 case 0:
1942 case -1:
1943 return;
1946 else if (edit->delete_file)
1947 unlink (catstrs (edit->dir, edit->filename, 0));
1948 dlg_stop (edit->widget.parent);
1951 #define TEMP_BUF_LEN 1024
1953 /* returns a null terminated length of text. Result must be free'd */
1954 unsigned char *edit_get_block (WEdit * edit, long start, long finish, int *l)
1956 unsigned char *s, *r;
1957 r = s = malloc (finish - start + 1);
1958 if (column_highlighting) {
1959 *l = 0;
1960 while (start < finish) { /* copy from buffer, excluding chars that are out of the column 'margins' */
1961 int c, x;
1962 x = edit_move_forward3 (edit, edit_bol (edit, start), 0, start);
1963 c = edit_get_byte (edit, start);
1964 if ((x >= edit->column1 && x < edit->column2)
1965 || (x >= edit->column2 && x < edit->column1) || c == '\n') {
1966 *s++ = c;
1967 (*l)++;
1969 start++;
1971 } else {
1972 *l = finish - start;
1973 while (start < finish)
1974 *s++ = edit_get_byte (edit, start++);
1976 *s = 0;
1977 return r;
1980 /* save block, returns 1 on success */
1981 int edit_save_block (WEdit * edit, const char *filename, long start, long finish)
1983 int len, file;
1985 if ((file = mc_open ((char *) filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
1986 return 0;
1988 if (column_highlighting) {
1989 unsigned char *block, *p;
1990 int r;
1991 p = block = edit_get_block (edit, start, finish, &len);
1992 while (len) {
1993 r = mc_write (file, p, len);
1994 if (r < 0)
1995 break;
1996 p += r;
1997 len -= r;
1999 free (block);
2000 } else {
2001 unsigned char *buf;
2002 int i = start, end;
2003 len = finish - start;
2004 buf = malloc (TEMP_BUF_LEN);
2005 while (start != finish) {
2006 end = min (finish, start + TEMP_BUF_LEN);
2007 for (; i < end; i++)
2008 buf[i - start] = edit_get_byte (edit, i);
2009 len -= mc_write (file, (char *) buf, end - start);
2010 start = end;
2012 free (buf);
2014 mc_close (file);
2015 if (len)
2016 return 0;
2017 return 1;
2020 /* copies a block to clipboard file */
2021 static int edit_save_block_to_clip_file (WEdit * edit, long start, long finish)
2023 return edit_save_block (edit, catstrs (home_dir, CLIP_FILE, 0), start, finish);
2027 void edit_paste_from_history (WEdit *edit)
2031 int edit_copy_to_X_buf_cmd (WEdit * edit)
2033 long start_mark, end_mark;
2034 if (eval_marks (edit, &start_mark, &end_mark))
2035 return 0;
2036 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2037 edit_error_dialog (_(" Copy to clipboard "), get_sys_error (_(" Unable to save to file. ")));
2038 return 1;
2040 edit_mark_cmd (edit, 1);
2041 return 0;
2044 int edit_cut_to_X_buf_cmd (WEdit * edit)
2046 long start_mark, end_mark;
2047 if (eval_marks (edit, &start_mark, &end_mark))
2048 return 0;
2049 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2050 edit_error_dialog (_(" Cut to clipboard "), _(" Unable to save to file. "));
2051 return 1;
2053 edit_block_delete_cmd (edit);
2054 edit_mark_cmd (edit, 1);
2055 return 0;
2058 void edit_paste_from_X_buf_cmd (WEdit * edit)
2060 edit_insert_file (edit, catstrs (home_dir, CLIP_FILE, 0));
2064 void edit_goto_cmd (WEdit *edit)
2066 char *f;
2067 static int l = 0;
2068 char s[12];
2069 sprintf (s, "%d", l);
2070 f = input_dialog (_(" Goto line "), _(" Enter line: "), l ? s : "");
2071 if (f) {
2072 if (*f) {
2073 l = atoi (f);
2074 edit_move_display (edit, l - edit->num_widget_lines / 2 - 1);
2075 edit_move_to_line (edit, l - 1);
2076 edit->force |= REDRAW_COMPLETELY;
2078 g_free (f);
2082 /*returns 1 on success */
2083 int edit_save_block_cmd (WEdit * edit)
2085 long start_mark, end_mark;
2086 char *exp;
2087 if (eval_marks (edit, &start_mark, &end_mark))
2088 return 1;
2089 exp = edit_get_save_file (edit->dir, catstrs (home_dir, CLIP_FILE, 0), _ (" Save Block "));
2090 edit_push_action (edit, KEY_PRESS + edit->start_display);
2091 if (exp) {
2092 if (!*exp) {
2093 g_free (exp);
2094 return 0;
2095 } else {
2096 if (edit_save_block (edit, exp, start_mark, end_mark)) {
2097 g_free (exp);
2098 edit->force |= REDRAW_COMPLETELY;
2099 return 1;
2100 } else {
2101 g_free (exp);
2102 edit_error_dialog (_ (" Save Block "), get_sys_error (_ (" Error trying to save file. ")));
2106 edit->force |= REDRAW_COMPLETELY;
2107 return 0;
2111 /* returns 1 on success */
2112 int edit_insert_file_cmd (WEdit * edit)
2114 char *exp = edit_get_load_file (edit->dir, catstrs (home_dir, CLIP_FILE, 0), _ (" Insert File "));
2115 edit_push_action (edit, KEY_PRESS + edit->start_display);
2116 if (exp) {
2117 if (!*exp) {
2118 g_free (exp);
2119 return 0;
2120 } else {
2121 if (edit_insert_file (edit, exp)) {
2122 g_free (exp);
2123 edit->force |= REDRAW_COMPLETELY;
2124 return 1;
2125 } else {
2126 g_free (exp);
2127 edit_error_dialog (_ (" Insert file "), get_sys_error (_ (" Error trying to insert file. ")));
2131 edit->force |= REDRAW_COMPLETELY;
2132 return 0;
2135 /* sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
2136 int edit_sort_cmd (WEdit * edit)
2138 static char *old = 0;
2139 char *exp;
2140 long start_mark, end_mark;
2141 int e;
2143 if (eval_marks (edit, &start_mark, &end_mark)) {
2144 edit_error_dialog (_(" Sort block "), _(" You must first highlight a block of text. "));
2145 return 0;
2147 edit_save_block (edit, catstrs (home_dir, BLOCK_FILE, 0), start_mark, end_mark);
2149 exp = old ? old : "";
2151 exp = input_dialog (_(" Run Sort "),
2152 _(" Enter sort options (see manpage) separated by whitespace: "), exp);
2154 if (!exp)
2155 return 1;
2156 if (old)
2157 g_free (old);
2158 old = exp;
2160 e = system (catstrs (" sort ", exp, " ", home_dir, BLOCK_FILE, " > ", home_dir, TEMP_FILE, 0));
2161 if (e) {
2162 if (e == -1 || e == 127) {
2163 edit_error_dialog (_(" Sort "),
2164 get_sys_error (_(" Error trying to execute sort command ")));
2165 } else {
2166 char q[8];
2167 sprintf (q, "%d ", e);
2168 edit_error_dialog (_(" Sort "),
2169 catstrs (_(" Sort returned non-zero: "), q, 0));
2171 return -1;
2174 edit->force |= REDRAW_COMPLETELY;
2176 if (edit_block_delete_cmd (edit))
2177 return 1;
2178 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, 0));
2179 return 0;
2182 /* if block is 1, a block must be highlighted and the shell command
2183 processes it. If block is 0 the shell command is a straight system
2184 command, that just produces some output which is to be inserted */
2185 void
2186 edit_block_process_cmd (WEdit * edit, const char *shell_cmd, int block)
2188 long start_mark, end_mark;
2189 struct stat s;
2190 char buf[BUFSIZ];
2191 FILE *script_home = NULL;
2192 FILE *script_src = NULL;
2193 FILE *block_file = NULL;
2194 char *o = NULL;
2195 char *h = NULL;
2196 char *b = NULL;
2197 char *e = NULL;
2199 o = g_strconcat (mc_home, shell_cmd, 0); /* original source script */
2200 h = g_strconcat (home_dir, EDIT_DIR, shell_cmd, 0); /* home script */
2201 b = g_strconcat (home_dir, BLOCK_FILE, 0); /* block file */
2202 e = g_strconcat (home_dir, ERROR_FILE, 0); /* error file */
2204 if (!(script_home = fopen (h, "r"))) {
2205 if (!(script_home = fopen (h, "w"))) {
2206 edit_error_dialog ("", get_sys_error (g_strconcat
2208 ("Error create script:"),
2209 h, 0)));
2210 goto err;
2212 if (!(script_src = fopen (o, "r"))) {
2213 fclose (script_home);
2214 unlink (h);
2215 edit_error_dialog ("", get_sys_error (g_strconcat
2216 (_("Error read script:"),
2217 o, 0)));
2218 goto err;
2220 while (fgets (buf, sizeof (buf), script_src))
2221 fputs (buf, script_home);
2222 if (fclose (script_home)) {
2223 edit_error_dialog ("", get_sys_error (g_strconcat
2225 ("Error close script:"),
2226 h, 0)));
2227 goto err;
2229 chmod (h, 0700);
2230 edit_error_dialog ("", get_sys_error (g_strconcat
2231 (_("Script created:"), h,
2232 0)));
2234 if (block) { /* for marked block run indent formatter */
2235 if (eval_marks (edit, &start_mark, &end_mark)) {
2236 edit_error_dialog (_("Process block"),
2238 (" You must first highlight a block of text. "));
2239 goto err;
2241 edit_save_block (edit, b, start_mark, end_mark);
2244 * Run script.
2245 * Initial space is to avoid polluting bash history.
2246 * Arguments:
2247 * $1 - name of the edited file (to check its extention etc).
2248 * $2 - file containing the current block.
2249 * $3 - file where error messages should be put.
2251 execute (g_strconcat (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2252 edit->filename, " ", home_dir, BLOCK_FILE,
2253 " ", home_dir, ERROR_FILE, NULL));
2255 } else {
2257 * No block selected, just execute the command for the file.
2258 * Arguments:
2259 * $1 - name of the edited file.
2261 execute (g_strconcat (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2262 edit->filename, NULL));
2265 edit_refresh_cmd (edit);
2266 edit->force |= REDRAW_COMPLETELY;
2268 /* insert result block */
2269 if (block) {
2270 if (mc_stat (e, &s) == 0) {
2271 if (!s.st_size) { /* no error messages */
2272 if (edit_block_delete_cmd (edit))
2273 goto err;
2274 edit_insert_file (edit, b);
2275 } else {
2276 edit_insert_file (edit, e);
2278 } else {
2279 edit_error_dialog ("",
2280 get_sys_error (g_strconcat
2282 ("Error trying to stat file:"),
2283 e, 0)));
2284 edit->force |= REDRAW_COMPLETELY;
2286 if ((block_file = fopen (b, "w")))
2287 fclose (block_file);
2288 goto err;
2291 err:
2292 g_free (o);
2293 g_free (h);
2294 g_free (b);
2295 g_free (e);
2297 return;
2300 /* prints at the cursor */
2301 /* returns the number of chars printed */
2302 int edit_print_string (WEdit * e, const char *s)
2304 int i = 0;
2305 while (s[i])
2306 edit_execute_cmd (e, -1, (unsigned char) s[i++]);
2307 e->force |= REDRAW_COMPLETELY;
2308 edit_update_screen (e);
2309 return i;
2312 int edit_printf (WEdit * e, const char *fmt,...)
2314 int i;
2315 va_list pa;
2316 char s[1024];
2317 va_start (pa, fmt);
2318 sprintf (s, fmt, pa);
2319 i = edit_print_string (e, s);
2320 va_end (pa);
2321 return i;
2324 /* FIXME: does this function break NT_OS2 ? */
2326 static void pipe_mail (WEdit *edit, char *to, char *subject, char *cc)
2328 FILE *p = 0;
2329 char *s;
2331 s = g_strdup_printf ("mail -s \"%s\" -c \"%s\" \"%s\"", subject, cc, to);
2333 if (s) {
2334 p = popen (s, "w");
2335 g_free (s);
2338 if (p) {
2339 long i;
2340 for (i = 0; i < edit->last_byte; i++)
2341 fputc (edit_get_byte (edit, i), p);
2342 pclose (p);
2346 #define MAIL_DLG_HEIGHT 12
2348 void edit_mail_dialog (WEdit * edit)
2350 char *tmail_to;
2351 char *tmail_subject;
2352 char *tmail_cc;
2354 static char *mail_cc_last = 0;
2355 static char *mail_subject_last = 0;
2356 static char *mail_to_last = 0;
2358 QuickDialog Quick_input =
2359 {50, MAIL_DLG_HEIGHT, -1, 0, N_(" Mail "),
2360 "[Input Line Keys]", "quick_input", 0};
2362 QuickWidget quick_widgets[] =
2364 {quick_button, 6, 10, 9, MAIL_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
2365 0, NULL},
2366 {quick_button, 2, 10, 9, MAIL_DLG_HEIGHT, N_("&Ok"), 0, B_ENTER, 0,
2367 0, NULL},
2368 {quick_input, 3, 50, 8, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2369 0, "mail-dlg-input"},
2370 {quick_label, 2, 50, 7, MAIL_DLG_HEIGHT, N_(" Copies to"), 0, 0, 0,
2371 0, 0},
2372 {quick_input, 3, 50, 6, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2373 0, "mail-dlg-input-2"},
2374 {quick_label, 2, 50, 5, MAIL_DLG_HEIGHT, N_(" Subject"), 0, 0, 0,
2375 0, 0},
2376 {quick_input, 3, 50, 4, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2377 0, "mail-dlg-input-3"},
2378 {quick_label, 2, 50, 3, MAIL_DLG_HEIGHT, N_(" To"), 0, 0, 0,
2379 0, 0},
2380 {quick_label, 2, 50, 2, MAIL_DLG_HEIGHT, N_(" mail -s <subject> -c <cc> <to>"), 0, 0, 0,
2381 0, 0},
2382 {0}};
2384 quick_widgets[2].str_result = &tmail_cc;
2385 quick_widgets[2].text = mail_cc_last ? mail_cc_last : "";
2386 quick_widgets[4].str_result = &tmail_subject;
2387 quick_widgets[4].text = mail_subject_last ? mail_subject_last : "";
2388 quick_widgets[6].str_result = &tmail_to;
2389 quick_widgets[6].text = mail_to_last ? mail_to_last : "";
2391 Quick_input.widgets = quick_widgets;
2393 if (quick_dialog (&Quick_input) != B_CANCEL) {
2394 if (mail_cc_last)
2395 g_free (mail_cc_last);
2396 if (mail_subject_last)
2397 g_free (mail_subject_last);
2398 if (mail_to_last)
2399 g_free (mail_to_last);
2400 mail_cc_last = *(quick_widgets[2].str_result);
2401 mail_subject_last = *(quick_widgets[4].str_result);
2402 mail_to_last = *(quick_widgets[6].str_result);
2403 pipe_mail (edit, mail_to_last, mail_subject_last, mail_cc_last);
2408 /*******************/
2409 /* Word Completion */
2410 /*******************/
2413 /* find first character of current word */
2414 static int edit_find_word_start (WEdit *edit, long *word_start, int *word_len)
2416 int i, c, last;
2418 /* return if at begin of file */
2419 if (edit->curs1 <= 0)
2420 return 0;
2422 c = (unsigned char) edit_get_byte (edit, edit->curs1 - 1);
2423 /* return if not at end or in word */
2424 if (isspace (c) || !(isalnum (c) || c == '_'))
2425 return 0;
2427 /* search start of word to be completed */
2428 for (i = 2;; i++) {
2429 /* return if at begin of file */
2430 if (edit->curs1 - i < 0)
2431 return 0;
2433 last = c;
2434 c = (unsigned char) edit_get_byte (edit, edit->curs1 - i);
2436 if (!(isalnum (c) || c == '_')) {
2437 /* return if word starts with digit */
2438 if (isdigit (last))
2439 return 0;
2441 *word_start = edit->curs1 - (i - 1); /* start found */
2442 *word_len = i - 1;
2443 break;
2446 /* success */
2447 return 1;
2451 /* (re)set search parameters to the given values */
2452 static void edit_set_search_parameters (int rs, int rb, int rr, int rw, int rc)
2454 replace_scanf = rs;
2455 replace_backwards = rb;
2456 replace_regexp = rr;
2457 replace_whole = rw;
2458 replace_case = rc;
2462 const static int MAX_WORD_COMPLETIONS = 100; /* in listbox */
2465 /* collect the possible completions */
2466 static int edit_collect_completions (WEdit *edit, long start,
2467 int word_len, char *match_expr, struct selection *compl, int *num)
2469 int len, max_len = 0, i, skip;
2470 char *bufpos;
2472 /* collect max MAX_WORD_COMPLETIONS completions */
2473 while (*num < MAX_WORD_COMPLETIONS) {
2474 /* get next match */
2475 start = edit_find (start - 1, (unsigned char *) match_expr, &len,
2476 edit->last_byte, (int (*)(void *, long)) edit_get_byte,
2477 (void *) edit, 0);
2479 /* not matched */
2480 if (start < 0)
2481 break;
2483 /* add matched completion if not yet added */
2484 bufpos = &edit->buffers1[start >> S_EDIT_BUF_SIZE][start & M_EDIT_BUF_SIZE];
2485 skip = 0;
2486 for (i = 0; i < *num; i++) {
2487 if (strncmp (&compl[i].text[word_len], &bufpos[word_len],
2488 max (len, compl[i].len) - word_len) == 0) {
2489 skip = 1;
2490 break; /* skip it, already added */
2493 if (skip)
2494 continue;
2496 compl[*num].text = CMalloc (len + 1);
2497 compl[*num].len = len;
2498 for (i = 0; i < len; i++)
2499 compl[*num].text[i] = *(bufpos + i);
2500 compl[*num].text[i] = '\0';
2501 (*num)++;
2503 /* note the maximal length needed for the completion dialog */
2504 if (len > max_len)
2505 max_len = len;
2507 return max_len;
2511 /* completion dialog callback */
2512 static int compl_callback (Dlg_head *h, int key, int Msg)
2514 switch (Msg) {
2515 case DLG_DRAW:
2516 common_dialog_repaint (h);
2517 break;
2519 return 0;
2523 static int compllist_callback (void *data)
2525 return 0;
2529 /* let the user select its preferred completion */
2530 void edit_completion_dialog (WEdit *edit, int max_len, int word_len,
2531 struct selection *compl, int num_compl)
2533 int start_x, start_y, offset, i;
2534 char *curr = NULL;
2535 Dlg_head *compl_dlg;
2536 WListbox *compl_list;
2537 unsigned int compl_dlg_h; /* completion dialog height */
2538 unsigned int compl_dlg_w; /* completion dialog width */
2540 /* calculate the dialog metrics */
2541 compl_dlg_h = num_compl + 2;
2542 compl_dlg_w = max_len + 4;
2543 start_x = edit->curs_col + edit->start_col - (compl_dlg_w / 2);
2544 start_y = edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + 1;
2546 if (start_x < 0)
2547 start_x = 0;
2548 if (compl_dlg_w > COLS)
2549 compl_dlg_w = COLS;
2550 if (compl_dlg_h > LINES - 2)
2551 compl_dlg_h = LINES - 2;
2553 offset = start_x + compl_dlg_w - COLS;
2554 if (offset > 0)
2555 start_x -= offset;
2556 offset = start_y + compl_dlg_h - LINES;
2557 if (offset > 0)
2558 start_y -= (offset + 1);
2560 /* create the dialog */
2561 compl_dlg = create_dlg (start_y, start_x, compl_dlg_h, compl_dlg_w,
2562 dialog_colors, compl_callback, "[Word Completion]", "complete_word",
2563 DLG_COMPACT);
2565 /* create the listbox */
2566 compl_list = listbox_new (1, 1, compl_dlg_w - 2, compl_dlg_h - 2, 0,
2567 compllist_callback, NULL);
2569 /* add the dialog */
2570 add_widget (compl_dlg, compl_list);
2572 /* fill the listbox with the completions */
2573 for (i = 0; i < num_compl; i++)
2574 listbox_add_item (compl_list, 0, 0, compl[i].text, NULL);
2576 /* pop up the dialog */
2577 run_dlg (compl_dlg);
2579 /* apply the choosen completion */
2580 if (compl_dlg->ret_value == B_ENTER) {
2581 listbox_get_current (compl_list, &curr, NULL);
2582 if (curr)
2583 for (curr += word_len; *curr; curr++)
2584 edit_insert (edit, *curr);
2587 /* destroy dialog before return */
2588 destroy_dlg (compl_dlg);
2592 /* complete current word using regular expression search */
2593 /* backwards beginning at current cursor position */
2594 void edit_complete_word_cmd (WEdit *edit)
2596 int word_len = 0, i, num_compl = 0, max_len;
2597 long word_start = 0;
2598 char *bufpos;
2599 char match_expr[MAX_REPL_LEN];
2600 struct selection compl[MAX_WORD_COMPLETIONS]; /* completions */
2602 /* don't want to disturb another search */
2603 int old_rs = replace_scanf;
2604 int old_rb = replace_backwards;
2605 int old_rr = replace_regexp;
2606 int old_rw = replace_whole;
2607 int old_rc = replace_case;
2609 /* search start of word to be completed */
2610 if (!edit_find_word_start (edit, &word_start, &word_len))
2611 return;
2613 /* prepare match expression */
2614 bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE]
2615 [word_start & M_EDIT_BUF_SIZE];
2616 strncpy (match_expr, bufpos, word_len);
2617 match_expr[word_len] = '\0';
2618 strcat (match_expr, "[a-zA-Z_0-9]+");
2620 /* init search: backward, regexp, whole word, case sensitive */
2621 edit_set_search_parameters (0, 1, 1, 1, 1);
2623 /* collect the possible completions */
2624 /* start search from curs1 down to begin of file */
2625 max_len = edit_collect_completions (edit, word_start, word_len,
2626 match_expr, (struct selection *) &compl, &num_compl);
2628 if (num_compl > 0) {
2629 /* insert completed word if there is only one match */
2630 if (num_compl == 1) {
2631 for (i = word_len; i < compl[0].len; i++)
2632 edit_insert (edit, *(compl[0].text + i));
2634 /* more than one possible completion => ask the user */
2635 else {
2636 /* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
2637 /* !!! pressed again the selection dialog pops up, but that !!! */
2638 /* !!! seems to require a further internal state !!! */
2639 /*beep ();*/
2641 /* let the user select the preferred completion */
2642 edit_completion_dialog (edit, max_len, word_len,
2643 (struct selection *) &compl, num_compl);
2647 /* release memory before return */
2648 for (i = 0; i < num_compl; i++)
2649 free (compl[i].text);
2651 /* restore search parameters */
2652 edit_set_search_parameters (old_rs, old_rb, old_rr, old_rw, old_rc);