All file compared and corrected with /doc/mc.1.in.
[midnight-commander.git] / edit / editcmd.c
blob98160053b3edbcff56b98665bf9c12a5acec3087
1 /* editor high level editing commands.
3 Copyright (C) 1996, 1997 the Free Software Foundation
5 Authors: 1996, 1997 Paul Sheer
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307, USA.
24 /* #define PIPE_BLOCKS_SO_READ_BYTE_BY_BYTE */
26 #include <config.h>
27 #include <ctype.h>
29 #include "edit.h"
30 #include "editcmddef.h"
32 #include "src/color.h" /* dialog_colors */
33 #include "src/tty.h" /* LINES */
34 #include "src/widget.h" /* listbox_new() */
35 #include "src/main.h" /* mc_home */
36 #include "src/help.h" /* interactive_display() */
37 #include "src/key.h" /* XCTRL */
38 #include "src/wtools.h" /* message() */
39 #include "src/charsets.h"
41 /* globals: */
43 /* search and replace: */
44 int replace_scanf = 0;
45 int replace_regexp = 0;
46 int replace_all = 0;
47 int replace_prompt = 1;
48 int replace_whole = 0;
49 int replace_case = 0;
50 int replace_backwards = 0;
51 int search_create_bookmark = 0;
53 /* queries on a save */
54 int edit_confirm_save = 1;
56 #define NUM_REPL_ARGS 64
57 #define MAX_REPL_LEN 1024
59 static inline int my_lower_case (int c)
61 return tolower(c & 0xFF);
64 char *strcasechr (const unsigned char *s, int c)
66 for (c = my_lower_case (c); my_lower_case ((int) *s) != c; ++s)
67 if (*s == '\0')
68 return 0;
69 return (char *) s;
72 #ifndef HAVE_MEMMOVE
73 /* for Christophe */
74 static void *memmove (void *dest, const void *src, size_t n)
76 char *t, *s;
78 if (dest <= src) {
79 t = (char *) dest;
80 s = (char *) src;
81 while (n--)
82 *t++ = *s++;
83 } else {
84 t = (char *) dest + n;
85 s = (char *) src + n;
86 while (n--)
87 *--t = *--s;
89 return dest;
91 #endif /* !HAVE_MEMMOVE */
93 /* #define itoa MY_itoa <---- this line is now in edit.h */
94 static char *
95 MY_itoa (int i)
97 static char t[14];
98 char *s = t + 13;
99 int j = i;
100 *s-- = 0;
101 do {
102 *s-- = i % 10 + '0';
103 } while ((i = i / 10));
104 if (j < 0)
105 *s-- = '-';
106 return ++s;
109 /* Temporary strings */
110 static char *stacked[16];
113 This joins strings end on end and allocates memory for the result.
114 The result is later automatically free'd and must not be free'd
115 by the caller.
117 char *catstrs (const char *first,...)
119 static int i = 0;
120 va_list ap;
121 int len;
122 char *data;
124 if (!first)
125 return 0;
127 len = strlen (first);
128 va_start (ap, first);
130 while ((data = va_arg (ap, char *)) != 0)
131 len += strlen (data);
133 len++;
135 i = (i + 1) % 16;
136 g_free (stacked[i]);
138 stacked[i] = g_malloc (len);
139 va_end (ap);
140 va_start (ap, first);
141 strcpy (stacked[i], first);
142 while ((data = va_arg (ap, char *)) != 0)
143 strcat (stacked[i], data);
144 va_end (ap);
146 return stacked[i];
149 /* Free temporary strings */
150 void freestrs(void)
152 int i;
154 for (i = 0; i < sizeof(stacked) / sizeof(stacked[0]); i++) {
155 g_free (stacked[i]);
156 stacked[i] = NULL;
160 void edit_help_cmd (WEdit * edit)
162 interactive_display (NULL, "[Internal File Editor]");
163 edit->force |= REDRAW_COMPLETELY;
166 void edit_refresh_cmd (WEdit * edit)
168 #ifndef HAVE_SLANG
169 clr_scr();
170 do_refresh();
171 #else
173 int color;
174 edit_get_syntax_color (edit, -1, &color);
176 touchwin(stdscr);
177 #endif /* !HAVE_SLANG */
178 mc_refresh();
179 doupdate();
182 /* "Oleg Yu. Repin" <repin@ssd.sscc.ru> added backup filenames
183 ...thanks -paul */
185 /* If 0 (quick save) then a) create/truncate <filename> file,
186 b) save to <filename>;
187 if 1 (safe save) then a) save to <tempnam>,
188 b) rename <tempnam> to <filename>;
189 if 2 (do backups) then a) save to <tempnam>,
190 b) rename <filename> to <filename.backup_ext>,
191 c) rename <tempnam> to <filename>. */
193 /* returns 0 on error */
194 int edit_save_file (WEdit * edit, const char *filename)
196 char *p;
197 long filelen = 0;
198 char *savename = 0;
199 int this_save_mode, fd;
201 if (!filename)
202 return 0;
203 if (!*filename)
204 return 0;
206 if ((fd = mc_open (filename, O_WRONLY | O_BINARY)) == -1) {
208 * The file does not exists yet, so no safe save or
209 * backup are necessary.
211 this_save_mode = 0;
212 } else {
213 mc_close (fd);
214 this_save_mode = option_save_mode;
217 if (this_save_mode > 0) {
218 char *savedir, *slashpos, *saveprefix;
219 slashpos = strrchr (filename, PATH_SEP);
220 if (slashpos) {
221 savedir = (char *) strdup (filename);
222 savedir[slashpos - filename + 1] = '\0';
223 } else
224 savedir = (char *) strdup (".");
225 saveprefix = concat_dir_and_file (savedir, "cooledit");
226 free (savedir);
227 fd = mc_mkstemps(&savename, saveprefix, NULL);
228 g_free (saveprefix);
229 if (!savename)
230 return 0;
231 /* FIXME:
232 * Close for now because mc_mkstemps use pure open system call
233 * to create temporary file and it needs to be reopened by
234 * VFS-aware mc_open().
236 close (fd);
237 } else
238 savename = g_strdup (filename);
240 mc_chown (savename, edit->stat1.st_uid, edit->stat1.st_gid);
241 mc_chmod (savename, edit->stat1.st_mode);
243 if ((fd = mc_open (savename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY ,
244 edit->stat1.st_mode)) == -1)
245 goto error_save;
247 /* pipe save */
248 if ((p = (char *) edit_get_write_filter (savename, filename))) {
249 FILE *file;
251 mc_close (fd);
252 file = (FILE *) popen (p, "w");
254 if (file) {
255 filelen = edit_write_stream (edit, file);
256 #if 1
257 pclose (file);
258 #else
259 if (pclose (file) != 0) {
260 edit_error_dialog (_ ("Error"), catstrs (_ (" Error writing to pipe: "), p, " ", 0));
261 free (p);
262 goto error_save;
264 #endif
265 } else {
266 edit_error_dialog (_ ("Error"), get_sys_error (catstrs (_ (" Failed trying to open pipe for writing: "), p, " ", 0)));
267 free (p);
268 goto error_save;
270 free (p);
271 } else {
272 long buf;
273 buf = 0;
274 filelen = edit->last_byte;
275 while (buf <= (edit->curs1 >> S_EDIT_BUF_SIZE) - 1) {
276 if (mc_write (fd, (char *) edit->buffers1[buf], EDIT_BUF_SIZE) != EDIT_BUF_SIZE) {
277 mc_close (fd);
278 goto error_save;
280 buf++;
282 if (mc_write (fd, (char *) edit->buffers1[buf], edit->curs1 & M_EDIT_BUF_SIZE) != (edit->curs1 & M_EDIT_BUF_SIZE)) {
283 filelen = -1;
284 } else if (edit->curs2) {
285 edit->curs2--;
286 buf = (edit->curs2 >> S_EDIT_BUF_SIZE);
287 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)) {
288 filelen = -1;
289 } else {
290 while (--buf >= 0) {
291 if (mc_write (fd, (char *) edit->buffers2[buf], EDIT_BUF_SIZE) != EDIT_BUF_SIZE) {
292 filelen = -1;
293 break;
297 edit->curs2++;
299 if (mc_close (fd))
300 goto error_save;
303 if (filelen != edit->last_byte)
304 goto error_save;
305 if (this_save_mode == 2)
306 if (mc_rename (filename, catstrs (filename, option_backup_ext, 0)) == -1)
307 goto error_save;
308 if (this_save_mode > 0)
309 if (mc_rename (savename, filename) == -1)
310 goto error_save;
311 if (savename)
312 g_free (savename);
313 return 1;
314 error_save:
315 if (savename)
316 g_free (savename);
317 return 0;
321 I changed this from Oleg's original routine so
322 that option_backup_ext works with coolwidgets as well. This
323 does mean there is a memory leak - paul.
325 void menu_save_mode_cmd (void)
327 #define DLG_X 38
328 #define DLG_Y 10
329 static char *str_result;
330 static int save_mode_new;
331 static char *str[] =
333 N_("Quick save "),
334 N_("Safe save "),
335 N_("Do backups -->")};
336 static QuickWidget widgets[] =
338 {quick_button, 18, DLG_X, 7, DLG_Y, N_("&Cancel"), 0,
339 B_CANCEL, 0, 0, "c"},
340 {quick_button, 6, DLG_X, 7, DLG_Y, N_("&OK"), 0,
341 B_ENTER, 0, 0, "o"},
342 {quick_input, 23, DLG_X, 5, DLG_Y, 0, 9,
343 0, 0, &str_result, "edit-backup-ext"},
344 {quick_label, 22, DLG_X, 4, DLG_Y, N_("Extension:"), 0,
345 0, 0, 0, "savemext"},
346 {quick_radio, 4, DLG_X, 3, DLG_Y, "", 3,
347 0, &save_mode_new, str, "t"},
348 {0}};
349 static QuickDialog dialog =
350 {DLG_X, DLG_Y, -1, -1, N_(" Edit Save Mode "), "[Edit Save Mode]",
351 widgets};
352 static int i18n_flag = 0;
354 if (!i18n_flag) {
355 int i;
356 int maxlen = 0;
357 int dlg_x;
358 int l1;
360 /* Ok/Cancel buttons */
361 l1 = strlen (_(widgets[0].text)) + strlen (_(widgets[1].text)) + 5;
362 maxlen = max (maxlen, l1);
364 for (i = 0; i < 3; i++ ) {
365 str[i] = _(str[i]);
366 maxlen = max (maxlen, strlen (str[i]) + 7);
368 i18n_flag = 1;
370 dlg_x = maxlen + strlen (_(widgets[3].text)) + 5 + 1;
371 widgets[2].hotkey_pos = strlen (_(widgets[3].text)); /* input field length */
372 dlg_x = min (COLS, dlg_x);
373 dialog.xlen = dlg_x;
375 i = (dlg_x - l1)/3;
376 widgets[1].relative_x = i;
377 widgets[0].relative_x = i + strlen (_(widgets[1].text)) + i + 4;
379 widgets[2].relative_x = widgets[3].relative_x = maxlen + 2;
381 for (i = 0; i < sizeof (widgets)/sizeof (widgets[0]); i++)
382 widgets[i].x_divisions = dlg_x;
385 widgets[2].text = option_backup_ext;
386 widgets[4].value = option_save_mode;
387 if (quick_dialog (&dialog) != B_ENTER)
388 return;
389 option_save_mode = save_mode_new;
390 option_backup_ext = str_result; /* this is a memory leak */
391 option_backup_ext_int = 0;
392 str_result[min (strlen (str_result), sizeof (int))] = '\0';
393 memcpy ((char *) &option_backup_ext_int, str_result, strlen (option_backup_ext));
396 void edit_split_filename (WEdit * edit, const char *f)
398 if (edit->filename)
399 free (edit->filename);
400 edit->filename = (char *) strdup (f);
401 if (edit->dir)
402 free (edit->dir);
403 edit->dir = (char *) strdup ("");
406 /* Here we want to warn the users of overwriting an existing file,
407 but only if they have made a change to the filename */
408 /* returns 1 on success */
409 int edit_save_as_cmd (WEdit * edit)
411 /* This heads the 'Save As' dialog box */
412 char *exp = 0;
413 int different_filename = 0;
415 exp = edit_get_save_file (edit->dir, edit->filename, _(" Save As "));
416 edit_push_action (edit, KEY_PRESS + edit->start_display);
418 if (exp) {
419 if (!*exp) {
420 g_free (exp);
421 edit->force |= REDRAW_COMPLETELY;
422 return 0;
423 } else {
424 if (strcmp(catstrs (edit->dir, edit->filename, 0), exp)) {
425 int file;
426 different_filename = 1;
427 if ((file = mc_open (exp, O_RDONLY | O_BINARY)) != -1) { /* the file exists */
428 mc_close (file);
429 if (edit_query_dialog2 (_("Warning"),
430 _(" A file already exists with this name. "),
431 /* Push buttons to over-write the current file, or cancel the operation */
432 _("Overwrite"), _("Cancel"))) {
433 edit->force |= REDRAW_COMPLETELY;
434 g_free (exp);
435 return 0;
439 if (edit_save_file (edit, exp)) {
440 edit_split_filename (edit, exp);
441 g_free (exp);
442 edit->modified = 0;
443 edit->delete_file = 0;
444 if (different_filename && !edit->explicit_syntax)
445 edit_load_syntax (edit, 0, 0);
446 edit->force |= REDRAW_COMPLETELY;
447 return 1;
448 } else {
449 g_free (exp);
450 edit_error_dialog (_(" Save As "), get_sys_error (_(" Error trying to save file. ")));
451 edit->force |= REDRAW_COMPLETELY;
452 return 0;
456 edit->force |= REDRAW_COMPLETELY;
457 return 0;
460 /* {{{ Macro stuff starts here */
462 static int
463 raw_callback (struct Dlg_head *h, int key, int Msg)
465 switch (Msg) {
466 case DLG_KEY:
467 h->running = 0;
468 h->ret_value = key;
469 return MSG_HANDLED;
471 return default_dlg_callback (h, key, Msg);;
474 /* gets a raw key from the keyboard. Passing cancel = 1 draws
475 a cancel button thus allowing c-c etc. Alternatively, cancel = 0
476 will return the next key pressed. ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
477 and Esc are cannot returned */
479 edit_raw_key_query (char *heading, char *query, int cancel)
481 int w = strlen (query) + 7;
482 struct Dlg_head *raw_dlg =
483 create_dlg (0, 0, 7, w, dialog_colors, raw_callback,
484 NULL, heading,
485 DLG_CENTER | DLG_TRYUP | DLG_WANT_TAB);
486 if (cancel)
487 add_widget (raw_dlg,
488 button_new (4, w / 2 - 5, B_CANCEL, NORMAL_BUTTON,
489 _("Cancel"), 0, 0, 0));
490 add_widget (raw_dlg, label_new (3 - cancel, 2, query, 0));
491 add_widget (raw_dlg,
492 input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0));
493 run_dlg (raw_dlg);
494 w = raw_dlg->ret_value;
495 destroy_dlg (raw_dlg);
496 if (cancel) {
497 if (w == XCTRL ('g') || w == XCTRL ('c') || w == ESC_CHAR
498 || w == B_CANCEL)
499 return 0;
502 return w;
505 /* creates a macro file if it doesn't exist */
506 static FILE *edit_open_macro_file (const char *r)
508 char *filename;
509 int file;
510 filename = catstrs (home_dir, MACRO_FILE, 0);
511 if ((file = open (filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
512 return 0;
513 close (file);
514 return fopen (filename, r);
517 #define MAX_MACROS 1024
518 static int saved_macro[MAX_MACROS + 1];
519 static int saved_macros_loaded = 0;
522 This is just to stop the macro file be loaded over and over for keys
523 that aren't defined to anything. On slow systems this could be annoying.
525 static int
526 macro_exists (int k)
528 int i;
529 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++)
530 if (saved_macro[i] == k)
531 return i;
532 return -1;
535 /* returns 1 on error */
536 static int
537 edit_delete_macro (WEdit * edit, int k)
539 struct macro macro[MAX_MACRO_LENGTH];
540 FILE *f, *g;
541 int s, i, n, j = 0;
543 if (saved_macros_loaded)
544 if ((j = macro_exists (k)) < 0)
545 return 0;
546 g = fopen (catstrs (home_dir, TEMP_FILE, 0), "w");
547 if (!g) {
548 /* This heads the delete macro error dialog box */
549 edit_error_dialog (_(" Delete macro "),
550 /* 'Open' = load temp file */
551 get_sys_error (_(" Error trying to open temp file ")));
552 return 1;
554 f = edit_open_macro_file ("r");
555 if (!f) {
556 /* This heads the delete macro error dialog box */
557 edit_error_dialog (_(" Delete macro "),
558 /* 'Open' = load temp file */
559 get_sys_error (_(" Error trying to open macro file ")));
560 fclose (g);
561 return 1;
563 for (;;) {
564 n = fscanf (f, ("key '%d 0': "), &s);
565 if (!n || n == EOF)
566 break;
567 n = 0;
568 while (fscanf (f, "%hd %hd, ", &macro[n].command, &macro[n].ch))
569 n++;
570 fscanf (f, ";\n");
571 if (s != k) {
572 fprintf (g, ("key '%d 0': "), s);
573 for (i = 0; i < n; i++)
574 fprintf (g, "%hd %hd, ", macro[i].command, macro[i].ch);
575 fprintf (g, ";\n");
578 fclose (f);
579 fclose (g);
580 if (rename (catstrs (home_dir, TEMP_FILE, 0), catstrs (home_dir, MACRO_FILE, 0)) == -1) {
581 /* This heads the delete macro error dialog box */
582 edit_error_dialog (_(" Delete macro "),
583 get_sys_error (_(" Error trying to overwrite macro file ")));
584 return 1;
586 if (saved_macros_loaded)
587 memmove (saved_macro + j, saved_macro + j + 1, sizeof (int) * (MAX_MACROS - j - 1));
588 return 0;
591 /* returns 0 on error */
592 int edit_save_macro_cmd (WEdit * edit, struct macro macro[], int n)
594 FILE *f;
595 int s, i;
597 edit_push_action (edit, KEY_PRESS + edit->start_display);
598 /* This heads the 'Macro' dialog box */
599 s = edit_raw_key_query (_(" Save macro "),
600 /* Input line for a single key press follows the ':' */
601 _(" Press the macro's new hotkey: "), 1);
602 edit->force |= REDRAW_COMPLETELY;
603 if (s) {
604 if (edit_delete_macro (edit, s))
605 return 0;
606 f = edit_open_macro_file ("a+");
607 if (f) {
608 fprintf (f, ("key '%d 0': "), s);
609 for (i = 0; i < n; i++)
610 fprintf (f, "%hd %hd, ", macro[i].command, macro[i].ch);
611 fprintf (f, ";\n");
612 fclose (f);
613 if (saved_macros_loaded) {
614 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++);
615 saved_macro[i] = s;
617 return 1;
618 } else
619 /* This heads the 'Save Macro' dialog box */
620 edit_error_dialog (_(" Save macro "), get_sys_error (_(" Error trying to open macro file ")));
622 return 0;
625 void edit_delete_macro_cmd (WEdit * edit)
627 int command;
629 command = edit_raw_key_query (_ (" Delete macro "),
630 _ (" Press macro hotkey: "), 1);
632 if (!command)
633 return;
635 edit_delete_macro (edit, command);
638 /* return 0 on error */
639 int edit_load_macro_cmd (WEdit * edit, struct macro macro[], int *n, int k)
641 FILE *f;
642 int s, i = 0, found = 0;
644 if (saved_macros_loaded)
645 if (macro_exists (k) < 0)
646 return 0;
648 if ((f = edit_open_macro_file ("r"))) {
649 struct macro dummy;
650 do {
651 int u;
652 u = fscanf (f, ("key '%d 0': "), &s);
653 if (!u || u == EOF)
654 break;
655 if (!saved_macros_loaded)
656 saved_macro[i++] = s;
657 if (!found) {
658 *n = 0;
659 while (*n < MAX_MACRO_LENGTH && 2 == fscanf (f, "%hd %hd, ", &macro[*n].command, &macro[*n].ch))
660 (*n)++;
661 } else {
662 while (2 == fscanf (f, "%hd %hd, ", &dummy.command, &dummy.ch));
664 fscanf (f, ";\n");
665 if (s == k)
666 found = 1;
667 } while (!found || !saved_macros_loaded);
668 if (!saved_macros_loaded) {
669 saved_macro[i] = 0;
670 saved_macros_loaded = 1;
672 fclose (f);
673 return found;
674 } else
675 /* This heads the 'Load Macro' dialog box */
676 edit_error_dialog (_(" Load macro "),
677 get_sys_error (_(" Error trying to open macro file ")));
678 return 0;
681 /* }}} Macro stuff starts here */
683 /* returns 1 on success */
684 int edit_save_confirm_cmd (WEdit * edit)
686 char *f;
688 if (edit_confirm_save) {
689 f = catstrs (_(" Confirm save file? : "), edit->filename, " ", 0);
690 /* Buttons to 'Confirm save file' query */
691 if (edit_query_dialog2 (_(" Save file "), f, _("Save"), _("Cancel")))
692 return 0;
694 return edit_save_cmd (edit);
698 /* returns 1 on success */
699 int edit_save_cmd (WEdit * edit)
701 if (!edit_save_file (edit, catstrs (edit->dir, edit->filename, 0)))
702 return edit_save_as_cmd (edit);
703 edit->force |= REDRAW_COMPLETELY;
704 edit->modified = 0;
705 edit->delete_file = 0;
707 return 1;
711 /* returns 1 on success */
712 int edit_new_cmd (WEdit * edit)
714 if (edit->modified) {
715 if (edit_query_dialog2 (_ ("Warning"), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
716 edit->force |= REDRAW_COMPLETELY;
717 return 0;
720 edit->force |= REDRAW_COMPLETELY;
721 edit->modified = 0;
722 return edit_renew (edit); /* if this gives an error, something has really screwed up */
725 /* returns 1 on error */
726 static int
727 edit_load_file_from_filename (WEdit * edit, char *exp)
729 if (!edit_reload (edit, exp, 0, "", 0))
730 return 1;
731 edit_split_filename (edit, exp);
732 edit->modified = 0;
733 return 0;
736 int edit_load_cmd (WEdit * edit)
738 char *exp;
740 if (edit->modified) {
741 if (edit_query_dialog2 (_ ("Warning"), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
742 edit->force |= REDRAW_COMPLETELY;
743 return 0;
747 exp = edit_get_load_file (edit->dir, edit->filename, _ (" Load "));
749 if (exp) {
750 if (*exp)
751 edit_load_file_from_filename (edit, exp);
752 g_free (exp);
754 edit->force |= REDRAW_COMPLETELY;
755 return 0;
759 if mark2 is -1 then marking is from mark1 to the cursor.
760 Otherwise its between the markers. This handles this.
761 Returns 1 if no text is marked.
763 int eval_marks (WEdit * edit, long *start_mark, long *end_mark)
765 if (edit->mark1 != edit->mark2) {
766 if (edit->mark2 >= 0) {
767 *start_mark = min (edit->mark1, edit->mark2);
768 *end_mark = max (edit->mark1, edit->mark2);
769 } else {
770 *start_mark = min (edit->mark1, edit->curs1);
771 *end_mark = max (edit->mark1, edit->curs1);
772 edit->column2 = edit->curs_col;
774 return 0;
775 } else {
776 *start_mark = *end_mark = 0;
777 edit->column2 = edit->column1 = 0;
778 return 1;
782 #define space_width 1
784 static void
785 edit_insert_column_of_text (WEdit * edit, unsigned char *data, int size, int width)
787 long cursor;
788 int i, col;
789 cursor = edit->curs1;
790 col = edit_get_col (edit);
791 for (i = 0; i < size; i++) {
792 if (data[i] == '\n') { /* fill in and move to next line */
793 int l;
794 long p;
795 if (edit_get_byte (edit, edit->curs1) != '\n') {
796 l = width - (edit_get_col (edit) - col);
797 while (l > 0) {
798 edit_insert (edit, ' ');
799 l -= space_width;
802 for (p = edit->curs1;; p++) {
803 if (p == edit->last_byte) {
804 edit_cursor_move (edit, edit->last_byte - edit->curs1);
805 edit_insert_ahead (edit, '\n');
806 p++;
807 break;
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 static void
943 edit_delete_column_of_text (WEdit * edit)
945 long p, q, r, m1, m2;
946 int b, c, d;
947 int n;
949 eval_marks (edit, &m1, &m2);
950 n = edit_move_forward (edit, m1, 0, m2) + 1;
951 c = edit_move_forward3 (edit, edit_bol (edit, m1), 0, m1);
952 d = edit_move_forward3 (edit, edit_bol (edit, m2), 0, m2);
954 b = min (c, d);
955 c = max (c, d);
957 while (n--) {
958 r = edit_bol (edit, edit->curs1);
959 p = edit_move_forward3 (edit, r, b, 0);
960 q = edit_move_forward3 (edit, r, c, 0);
961 if (p < m1)
962 p = m1;
963 if (q > m2)
964 q = m2;
965 edit_cursor_move (edit, p - edit->curs1);
966 while (q > p) { /* delete line between margins */
967 if (edit_get_byte (edit, edit->curs1) != '\n')
968 edit_delete (edit);
969 q--;
971 if (n) /* move to next line except on the last delete */
972 edit_cursor_move (edit, edit_move_forward (edit, edit->curs1, 1, 0) - edit->curs1);
976 /* if success return 0 */
978 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
989 (_("Warning"),
991 (" Block is large, you may not be able to undo this action. "),
992 _("Continue"), _("Cancel"))) {
993 return 1;
996 edit_push_markers (edit);
997 edit_cursor_move (edit, start_mark - edit->curs1);
998 edit_scroll_screen_over_cursor (edit);
999 count = start_mark;
1000 if (start_mark < end_mark) {
1001 if (column_highlighting) {
1002 if (edit->mark2 < 0)
1003 edit_mark_cmd (edit, 0);
1004 edit_delete_column_of_text (edit);
1005 } else {
1006 while (count < end_mark) {
1007 edit_delete (edit);
1008 count++;
1012 edit_set_markers (edit, 0, 0, 0, 0);
1013 edit->force |= REDRAW_PAGE;
1014 return 0;
1017 /* returns 1 if canceelled by user */
1018 int edit_block_delete_cmd (WEdit * edit)
1020 long start_mark, end_mark;
1021 if (eval_marks (edit, &start_mark, &end_mark)) {
1022 edit_delete_line (edit);
1023 return 0;
1025 return edit_block_delete (edit);
1029 #define INPUT_INDEX 9
1030 #define SEARCH_DLG_WIDTH 58
1031 #define SEARCH_DLG_HEIGHT 10
1032 #define REPLACE_DLG_WIDTH 58
1033 #define REPLACE_DLG_HEIGHT 15
1034 #define CONFIRM_DLG_WIDTH 79
1035 #define CONFIRM_DLG_HEIGTH 6
1036 #define B_REPLACE_ALL B_USER+1
1037 #define B_REPLACE_ONE B_USER+2
1038 #define B_SKIP_REPLACE B_USER+3
1040 static int
1041 edit_replace_prompt (WEdit * edit, char *replace_text, int xpos, int ypos)
1043 QuickWidget quick_widgets[] =
1045 {quick_button, 63, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Cancel"),
1046 0, B_CANCEL, 0, 0, NULL},
1047 {quick_button, 50, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("O&ne"),
1048 0, B_REPLACE_ONE, 0, 0, NULL},
1049 {quick_button, 37, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("A&ll"),
1050 0, B_REPLACE_ALL, 0, 0, NULL},
1051 {quick_button, 21, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Skip"),
1052 0, B_SKIP_REPLACE, 0, 0, NULL},
1053 {quick_button, 4, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Replace"),
1054 0, B_ENTER, 0, 0, NULL},
1055 {quick_label, 2, CONFIRM_DLG_WIDTH, 2, CONFIRM_DLG_HEIGTH, 0,
1056 0, 0, 0, 0, 0},
1057 {0}};
1059 #ifdef HAVE_CHARSET
1060 char *msg = _(" Replace with: ");
1062 quick_widgets[5].text = catstrs (msg, replace_text, 0);
1064 if (*replace_text)
1065 convert_to_display (quick_widgets[5].text + strlen (msg));
1066 #else
1067 quick_widgets[5].text = catstrs (_ (" Replace with: "), replace_text, 0);
1068 #endif /* !HAVE_CHARSET */
1071 QuickDialog Quick_input =
1072 {CONFIRM_DLG_WIDTH, CONFIRM_DLG_HEIGTH, 0, 0, N_ (" Confirm replace "),
1073 "[Input Line Keys]", 0 /*quick_widgets */ };
1075 Quick_input.widgets = quick_widgets;
1077 Quick_input.xpos = xpos;
1079 /* Sometimes menu can hide replaced text. I don't like it */
1081 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + CONFIRM_DLG_HEIGTH - 1))
1082 ypos -= CONFIRM_DLG_HEIGTH;
1084 Quick_input.ypos = ypos;
1085 return quick_dialog (&Quick_input);
1089 static void
1090 edit_replace_dialog (WEdit * edit, char **search_text, char **replace_text, char **arg_order)
1092 int treplace_scanf = replace_scanf;
1093 int treplace_regexp = replace_regexp;
1094 int treplace_all = replace_all;
1095 int treplace_prompt = replace_prompt;
1096 int treplace_backwards = replace_backwards;
1097 int treplace_whole = replace_whole;
1098 int treplace_case = replace_case;
1100 QuickWidget quick_widgets[] =
1102 {quick_button, 6, 10, 12, REPLACE_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1103 0, NULL},
1104 {quick_button, 2, 10, 12, REPLACE_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1105 0, NULL},
1106 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1107 0, 0, NULL},
1108 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("replace &All"), 0, 0,
1109 0, 0, NULL},
1110 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("pr&Ompt on replace"), 0, 0,
1111 0, 0, NULL},
1112 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1113 0, 0, NULL},
1114 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1115 0, 0, NULL},
1116 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1117 0, 0, NULL},
1118 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 8, REPLACE_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1119 0, 0, NULL},
1120 {quick_input, 3, REPLACE_DLG_WIDTH, 7, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1121 0, "edit-argord"},
1122 {quick_label, 2, REPLACE_DLG_WIDTH, 6, REPLACE_DLG_HEIGHT, N_(" Enter replacement argument order eg. 3,2,1,4 "), 0, 0,
1123 0, 0, 0},
1124 {quick_input, 3, REPLACE_DLG_WIDTH, 5, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1125 0, "edit-replace"},
1126 {quick_label, 2, REPLACE_DLG_WIDTH, 4, REPLACE_DLG_HEIGHT, N_(" Enter replacement string:"), 0, 0, 0,
1127 0, 0},
1128 {quick_input, 3, REPLACE_DLG_WIDTH, 3, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1129 0, "edit-search"},
1130 {quick_label, 2, REPLACE_DLG_WIDTH, 2, REPLACE_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1131 0, 0},
1132 {0}};
1134 quick_widgets[2].result = &treplace_scanf;
1135 quick_widgets[3].result = &treplace_all;
1136 quick_widgets[4].result = &treplace_prompt;
1137 quick_widgets[5].result = &treplace_backwards;
1138 quick_widgets[6].result = &treplace_regexp;
1139 quick_widgets[7].result = &treplace_whole;
1140 quick_widgets[8].result = &treplace_case;
1141 quick_widgets[9].str_result = arg_order;
1142 quick_widgets[9].text = *arg_order;
1143 quick_widgets[11].str_result = replace_text;
1144 quick_widgets[11].text = *replace_text;
1145 quick_widgets[13].str_result = search_text;
1146 quick_widgets[13].text = *search_text;
1148 QuickDialog Quick_input =
1149 {REPLACE_DLG_WIDTH, REPLACE_DLG_HEIGHT, -1, 0, N_(" Replace "),
1150 "[Input Line Keys]", 0 /*quick_widgets */ };
1152 Quick_input.widgets = quick_widgets;
1154 if (quick_dialog (&Quick_input) != B_CANCEL) {
1155 replace_scanf = treplace_scanf;
1156 replace_backwards = treplace_backwards;
1157 replace_regexp = treplace_regexp;
1158 replace_all = treplace_all;
1159 replace_prompt = treplace_prompt;
1160 replace_whole = treplace_whole;
1161 replace_case = treplace_case;
1162 return;
1163 } else {
1164 *arg_order = NULL;
1165 *replace_text = NULL;
1166 *search_text = NULL;
1167 return;
1173 static void
1174 edit_search_dialog (WEdit * edit, char **search_text)
1176 int treplace_scanf = replace_scanf;
1177 int treplace_regexp = replace_regexp;
1178 int treplace_whole = replace_whole;
1179 int treplace_case = replace_case;
1180 int treplace_backwards = replace_backwards;
1182 QuickWidget quick_widgets[] =
1184 {quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1185 0, NULL},
1186 {quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1187 0, NULL},
1188 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1189 0, 0, NULL },
1190 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1191 0, 0, NULL},
1192 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1193 0, 0, NULL},
1194 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1195 0, 0, NULL},
1196 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1197 0, 0, NULL},
1198 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, 0,
1199 0, "edit-search"},
1200 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1201 0, 0},
1202 {0}};
1204 quick_widgets[2].result = &treplace_scanf;
1205 quick_widgets[3].result = &treplace_backwards;
1206 quick_widgets[4].result = &treplace_regexp;
1207 quick_widgets[5].result = &treplace_whole;
1208 quick_widgets[6].result = &treplace_case;
1209 quick_widgets[7].str_result = search_text;
1210 quick_widgets[7].text = *search_text;
1213 QuickDialog Quick_input =
1214 {SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
1215 "[Input Line Keys]", 0 /*quick_widgets */ };
1217 Quick_input.widgets = quick_widgets;
1219 if (quick_dialog (&Quick_input) != B_CANCEL) {
1220 replace_scanf = treplace_scanf;
1221 replace_backwards = treplace_backwards;
1222 replace_regexp = treplace_regexp;
1223 replace_whole = treplace_whole;
1224 replace_case = treplace_case;
1225 } else {
1226 *search_text = NULL;
1232 static long sargs[NUM_REPL_ARGS][256 / sizeof (long)];
1234 #define SCANF_ARGS sargs[0], sargs[1], sargs[2], sargs[3], \
1235 sargs[4], sargs[5], sargs[6], sargs[7], \
1236 sargs[8], sargs[9], sargs[10], sargs[11], \
1237 sargs[12], sargs[13], sargs[14], sargs[15]
1239 #define PRINTF_ARGS sargs[argord[0]], sargs[argord[1]], sargs[argord[2]], sargs[argord[3]], \
1240 sargs[argord[4]], sargs[argord[5]], sargs[argord[6]], sargs[argord[7]], \
1241 sargs[argord[8]], sargs[argord[9]], sargs[argord[10]], sargs[argord[11]], \
1242 sargs[argord[12]], sargs[argord[13]], sargs[argord[14]], sargs[argord[15]]
1245 /* This function is a modification of mc-3.2.10/src/view.c:regexp_view_search() */
1246 /* returns -3 on error in pattern, -1 on not found, found_len = 0 if either */
1247 static int
1248 string_regexp_search (char *pattern, char *string, int len, int match_type, int match_bol, int icase, int *found_len, void *d)
1250 static regex_t r;
1251 static char *old_pattern = NULL;
1252 static int old_type, old_icase;
1253 regmatch_t *pmatch;
1254 static regmatch_t s[1];
1256 pmatch = (regmatch_t *) d;
1257 if (!pmatch)
1258 pmatch = s;
1260 if (!old_pattern || strcmp (old_pattern, pattern) || old_type != match_type || old_icase != icase) {
1261 if (old_pattern) {
1262 regfree (&r);
1263 free (old_pattern);
1264 old_pattern = 0;
1266 if (regcomp (&r, pattern, REG_EXTENDED | (icase ? REG_ICASE : 0))) {
1267 *found_len = 0;
1268 return -3;
1270 old_pattern = (char *) strdup (pattern);
1271 old_type = match_type;
1272 old_icase = icase;
1274 if (regexec (&r, string, d ? NUM_REPL_ARGS : 1, pmatch, ((match_bol || match_type != match_normal) ? 0 : REG_NOTBOL)) != 0) {
1275 *found_len = 0;
1276 return -1;
1278 *found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1279 return (pmatch[0].rm_so);
1282 /* thanks to Liviu Daia <daia@stoilow.imar.ro> for getting this
1283 (and the above) routines to work properly - paul */
1285 static long
1286 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)
1288 long p, q = 0;
1289 long l = strlen ((char *) exp), f = 0;
1290 int n = 0;
1292 for (p = 0; p < l; p++) /* count conversions... */
1293 if (exp[p] == '%')
1294 if (exp[++p] != '%') /* ...except for "%%" */
1295 n++;
1297 if (replace_scanf || replace_regexp) {
1298 int c;
1299 unsigned char *buf;
1300 unsigned char mbuf[MAX_REPL_LEN * 2 + 3];
1302 replace_scanf = (!replace_regexp); /* can't have both */
1304 buf = mbuf;
1306 if (replace_scanf) {
1307 unsigned char e[MAX_REPL_LEN];
1308 if (n >= NUM_REPL_ARGS)
1309 return -3;
1311 if (replace_case) {
1312 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++)
1313 buf[p - start] = (*get_byte) (data, p);
1314 } else {
1315 for (p = 0; exp[p] != 0; p++)
1316 exp[p] = my_lower_case (exp[p]);
1317 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++) {
1318 c = (*get_byte) (data, p);
1319 buf[p - start] = my_lower_case (c);
1323 buf[(q = p - start)] = 0;
1324 strcpy ((char *) e, (char *) exp);
1325 strcat ((char *) e, "%n");
1326 exp = e;
1328 while (q) {
1329 *((int *) sargs[n]) = 0; /* --> here was the problem - now fixed: good */
1330 if (n == sscanf ((char *) buf, (char *) exp, SCANF_ARGS)) {
1331 if (*((int *) sargs[n])) {
1332 *len = *((int *) sargs[n]);
1333 return start;
1336 if (once_only)
1337 return -2;
1338 if (q + start < last_byte) {
1339 if (replace_case) {
1340 buf[q] = (*get_byte) (data, q + start);
1341 } else {
1342 c = (*get_byte) (data, q + start);
1343 buf[q] = my_lower_case (c);
1345 q++;
1347 buf[q] = 0;
1348 start++;
1349 buf++; /* move the window along */
1350 if (buf == mbuf + MAX_REPL_LEN) { /* the window is about to go past the end of array, so... */
1351 memmove (mbuf, buf, strlen ((char *) buf) + 1); /* reset it */
1352 buf = mbuf;
1354 q--;
1356 } else { /* regexp matching */
1357 long offset = 0;
1358 int found_start, match_bol, move_win = 0;
1360 while (start + offset < last_byte) {
1361 match_bol = (offset == 0 || (*get_byte) (data, start + offset - 1) == '\n');
1362 if (!move_win) {
1363 p = start + offset;
1364 q = 0;
1366 for (; p < last_byte && q < MAX_REPL_LEN; p++, q++) {
1367 mbuf[q] = (*get_byte) (data, p);
1368 if (mbuf[q] == '\n')
1369 break;
1371 q++;
1372 offset += q;
1373 mbuf[q] = 0;
1375 buf = mbuf;
1376 while (q) {
1377 found_start = string_regexp_search ((char *) exp, (char *) buf, q, match_normal, match_bol, !replace_case, len, d);
1379 if (found_start <= -2) { /* regcomp/regexec error */
1380 *len = 0;
1381 return -3;
1383 else if (found_start == -1) /* not found: try next line */
1384 break;
1385 else if (*len == 0) { /* null pattern: try again at next character */
1386 q--;
1387 buf++;
1388 match_bol = 0;
1389 continue;
1391 else /* found */
1392 return (start + offset - q + found_start);
1394 if (once_only)
1395 return -2;
1397 if (buf[q - 1] != '\n') { /* incomplete line: try to recover */
1398 buf = mbuf + MAX_REPL_LEN / 2;
1399 q = strlen ((char *) buf);
1400 memmove (mbuf, buf, q);
1401 p = start + q;
1402 move_win = 1;
1404 else
1405 move_win = 0;
1408 } else {
1409 *len = strlen ((char *) exp);
1410 if (replace_case) {
1411 for (p = start; p <= last_byte - l; p++) {
1412 if ((*get_byte) (data, p) == (unsigned char)exp[0]) { /* check if first char matches */
1413 for (f = 0, q = 0; q < l && f < 1; q++)
1414 if ((*get_byte) (data, q + p) != (unsigned char)exp[q])
1415 f = 1;
1416 if (f == 0)
1417 return p;
1419 if (once_only)
1420 return -2;
1422 } else {
1423 for (p = 0; exp[p] != 0; p++)
1424 exp[p] = my_lower_case (exp[p]);
1426 for (p = start; p <= last_byte - l; p++) {
1427 if (my_lower_case ((*get_byte) (data, p)) == (unsigned char)exp[0]) {
1428 for (f = 0, q = 0; q < l && f < 1; q++)
1429 if (my_lower_case ((*get_byte) (data, q + p)) != (unsigned char)exp[q])
1430 f = 1;
1431 if (f == 0)
1432 return p;
1434 if (once_only)
1435 return -2;
1439 return -2;
1443 static long
1444 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)
1445 { /*front end to find_string to check for
1446 whole words */
1447 long p;
1448 p = search_start;
1450 while ((p = edit_find_string (p, exp, len, last_byte, get_byte, data, once_only, d)) >= 0) {
1451 if (replace_whole) {
1452 /*If the bordering chars are not in option_whole_chars_search then word is whole */
1453 if (!strcasechr (option_whole_chars_search, (*get_byte) (data, p - 1))
1454 && !strcasechr (option_whole_chars_search, (*get_byte) (data, p + *len)))
1455 return p;
1456 if (once_only)
1457 return -2;
1458 } else
1459 return p;
1460 if (once_only)
1461 break;
1462 p++; /*not a whole word so continue search. */
1464 return p;
1467 static long
1468 edit_find (long search_start, unsigned char *exp, int *len, long last_byte, int (*get_byte) (void *, long), void *data, void *d)
1470 long p;
1471 if (replace_backwards) {
1472 while (search_start >= 0) {
1473 p = edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 1, d);
1474 if (p == search_start)
1475 return p;
1476 search_start--;
1478 } else {
1479 return edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 0, d);
1481 return -2;
1484 #define is_digit(x) ((x) >= '0' && (x) <= '9')
1486 #define snprintf(v) { \
1487 *p1++ = *p++; \
1488 *p1++ = '%'; \
1489 *p1++ = 'n'; \
1490 *p1 = '\0'; \
1491 sprintf(s,q1,v,&n); \
1492 s += n; \
1495 /* this function uses the sprintf command to do a vprintf */
1496 /* it takes pointers to arguments instead of the arguments themselves */
1497 static int sprintf_p (char *str, const char *fmt,...)
1498 __attribute__ ((format (printf, 2, 3)));
1500 static int sprintf_p (char *str, const char *fmt,...)
1502 va_list ap;
1503 int n;
1504 char *q, *p, *s = str;
1505 char q1[32];
1506 char *p1;
1508 va_start (ap, fmt);
1509 p = q = (char *) fmt;
1511 while ((p = strchr (p, '%'))) {
1512 n = p - q;
1513 strncpy (s, q, n); /* copy stuff between format specifiers */
1514 s += n;
1515 *s = 0;
1516 q = p;
1517 p1 = q1;
1518 *p1++ = *p++;
1519 if (*p == '%') {
1520 p++;
1521 *s++ = '%';
1522 q = p;
1523 continue;
1525 if (*p == 'n') {
1526 p++;
1527 /* do nothing */
1528 q = p;
1529 continue;
1531 if (*p == '#')
1532 *p1++ = *p++;
1533 if (*p == '0')
1534 *p1++ = *p++;
1535 if (*p == '-')
1536 *p1++ = *p++;
1537 if (*p == '+')
1538 *p1++ = *p++;
1539 if (*p == '*') {
1540 p++;
1541 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace field width with a number */
1542 p1 += strlen (p1);
1543 } else {
1544 while (is_digit (*p))
1545 *p1++ = *p++;
1547 if (*p == '.')
1548 *p1++ = *p++;
1549 if (*p == '*') {
1550 p++;
1551 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace precision with a number */
1552 p1 += strlen (p1);
1553 } else {
1554 while (is_digit (*p))
1555 *p1++ = *p++;
1557 /* flags done, now get argument */
1558 if (*p == 's') {
1559 snprintf (va_arg (ap, char *));
1560 } else if (*p == 'h') {
1561 if (strchr ("diouxX", *p))
1562 snprintf (*va_arg (ap, short *));
1563 } else if (*p == 'l') {
1564 *p1++ = *p++;
1565 if (strchr ("diouxX", *p))
1566 snprintf (*va_arg (ap, long *));
1567 } else if (strchr ("cdiouxX", *p)) {
1568 snprintf (*va_arg (ap, int *));
1569 } else if (*p == 'L') {
1570 *p1++ = *p++;
1571 if (strchr ("EefgG", *p))
1572 snprintf (*va_arg (ap, double *)); /* should be long double */
1573 } else if (strchr ("EefgG", *p)) {
1574 snprintf (*va_arg (ap, double *));
1575 } else if (strchr ("DOU", *p)) {
1576 snprintf (*va_arg (ap, long *));
1577 } else if (*p == 'p') {
1578 snprintf (*va_arg (ap, void **));
1580 q = p;
1582 va_end (ap);
1583 sprintf (s, q); /* print trailing leftover */
1584 return s - str + strlen (s);
1587 static void regexp_error (WEdit *edit)
1589 /* "Error: Syntax error in regular expression, or scanf expression contained too many %'s */
1590 edit_error_dialog (_("Error"), _(" Invalid regular expression, or scanf expression with to many conversions "));
1593 /* call with edit = 0 before shutdown to close memory leaks */
1594 void
1595 edit_replace_cmd (WEdit *edit, int again)
1597 static regmatch_t pmatch[NUM_REPL_ARGS];
1598 static char *old1 = NULL;
1599 static char *old2 = NULL;
1600 static char *old3 = NULL;
1601 char *exp1 = "";
1602 char *exp2 = "";
1603 char *exp3 = "";
1604 int replace_yes;
1605 int replace_continue;
1606 int treplace_prompt = 0;
1607 int i = 0;
1608 long times_replaced = 0, last_search;
1609 int argord[NUM_REPL_ARGS];
1611 if (!edit) {
1612 if (old1) {
1613 g_free (old1);
1614 old1 = 0;
1616 if (old2) {
1617 g_free (old2);
1618 old2 = 0;
1620 if (old3) {
1621 g_free (old3);
1622 old3 = 0;
1624 return;
1626 last_search = edit->last_byte;
1628 edit->force |= REDRAW_COMPLETELY;
1630 exp1 = old1 ? old1 : exp1;
1631 exp2 = old2 ? old2 : exp2;
1632 exp3 = old3 ? old3 : exp3;
1634 if (again) {
1635 if (!old1 || !old2)
1636 return;
1637 exp1 = g_strdup (old1);
1638 exp2 = g_strdup (old2);
1639 exp3 = g_strdup (old3);
1640 } else {
1641 edit_push_action (edit, KEY_PRESS + edit->start_display);
1643 convert_to_display (exp1);
1644 convert_to_display (exp2);
1646 edit_replace_dialog (edit, &exp1, &exp2, &exp3);
1648 convert_from_input (exp1);
1649 convert_from_input (exp2);
1651 treplace_prompt = replace_prompt;
1654 if (!exp1 || !*exp1) {
1655 edit->force = REDRAW_COMPLETELY;
1656 g_free (exp1);
1657 g_free (exp2);
1658 g_free (exp3);
1659 return;
1661 g_free (old1);
1662 g_free (old2);
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
1690 && replace_backwards)
1691 edit->search_start--;
1693 if (edit->found_len && edit->search_start == edit->found_start - 1
1694 && !replace_backwards)
1695 edit->search_start++;
1697 do {
1698 int len = 0;
1699 long new_start;
1700 new_start =
1701 edit_find (edit->search_start, (unsigned char *) exp1, &len,
1702 last_search, (int (*)(void *, long)) edit_get_byte,
1703 (void *) edit, pmatch);
1704 if (new_start == -3) {
1705 regexp_error (edit);
1706 break;
1708 edit->search_start = new_start;
1709 /*returns negative on not found or error in pattern */
1711 if (edit->search_start >= 0) {
1712 edit->found_start = edit->search_start;
1713 i = edit->found_len = len;
1715 edit_cursor_move (edit, edit->search_start - edit->curs1);
1716 edit_scroll_screen_over_cursor (edit);
1718 replace_yes = 1;
1720 if (treplace_prompt) {
1721 int l;
1722 l = edit->curs_row - edit->num_widget_lines / 3;
1723 if (l > 0)
1724 edit_scroll_downward (edit, l);
1725 if (l < 0)
1726 edit_scroll_upward (edit, -l);
1728 edit_scroll_screen_over_cursor (edit);
1729 edit->force |= REDRAW_PAGE;
1730 edit_render_keypress (edit);
1732 /*so that undo stops at each query */
1733 edit_push_key_press (edit);
1735 switch (edit_replace_prompt (edit, exp2, /* and prompt 2/3 down */
1736 (edit->num_widget_columns -
1737 CONFIRM_DLG_WIDTH) / 2,
1738 edit->num_widget_lines * 2 /
1739 3)) {
1740 case B_ENTER:
1741 break;
1742 case B_SKIP_REPLACE:
1743 replace_yes = 0;
1744 break;
1745 case B_REPLACE_ALL:
1746 treplace_prompt = 0;
1747 replace_continue = 1;
1748 break;
1749 case B_REPLACE_ONE:
1750 replace_continue = 0;
1751 break;
1752 case B_CANCEL:
1753 replace_yes = 0;
1754 replace_continue = 0;
1755 break;
1758 if (replace_yes) { /* delete then insert new */
1759 if (replace_scanf || replace_regexp) {
1760 char repl_str[MAX_REPL_LEN + 2];
1762 /* we need to fill in sargs just like with scanf */
1763 if (replace_regexp) {
1764 int k, j;
1765 for (k = 1;
1766 k < NUM_REPL_ARGS && pmatch[k].rm_eo >= 0;
1767 k++) {
1768 unsigned char *t;
1769 t = (unsigned char *) &sargs[k - 1][0];
1770 for (j = 0;
1771 j < pmatch[k].rm_eo - pmatch[k].rm_so
1772 && j < 255; j++, t++)
1773 *t = (unsigned char) edit_get_byte (edit,
1774 edit->
1775 search_start
1777 pmatch
1778 [0].
1779 rm_so +
1780 pmatch
1781 [k].
1782 rm_so +
1784 *t = '\0';
1786 for (; k <= NUM_REPL_ARGS; k++)
1787 sargs[k - 1][0] = 0;
1789 if (sprintf_p (repl_str, exp2, PRINTF_ARGS) >= 0) {
1790 times_replaced++;
1791 while (i--)
1792 edit_delete (edit);
1793 while (repl_str[++i])
1794 edit_insert (edit, repl_str[i]);
1795 } else {
1796 edit_error_dialog (_(" Replace "),
1798 (" Error in replacement format string. "));
1799 replace_continue = 0;
1801 } else {
1802 times_replaced++;
1803 while (i--)
1804 edit_delete (edit);
1805 while (exp2[++i])
1806 edit_insert (edit, exp2[i]);
1808 edit->found_len = i;
1810 /* so that we don't find the same string again */
1811 if (replace_backwards) {
1812 last_search = edit->search_start;
1813 edit->search_start--;
1814 } else {
1815 edit->search_start += i;
1816 last_search = edit->last_byte;
1818 edit_scroll_screen_over_cursor (edit);
1819 } else {
1820 char *msg = _(" Replace ");
1821 /* try and find from right here for next search */
1822 edit->search_start = edit->curs1;
1823 edit_update_curs_col (edit);
1825 edit->force |= REDRAW_PAGE;
1826 edit_render_keypress (edit);
1827 if (times_replaced) {
1828 message (0, msg, _(" %ld replacements made. "),
1829 times_replaced);
1830 } else
1831 edit_message_dialog (msg, _(" Search string not found "));
1832 replace_continue = 0;
1834 } while (replace_continue);
1836 g_free (exp1);
1837 g_free (exp2);
1838 g_free (exp3);
1839 edit->force = REDRAW_COMPLETELY;
1840 edit_scroll_screen_over_cursor (edit);
1846 void edit_search_cmd (WEdit * edit, int again)
1848 static char *old = NULL;
1849 char *exp = "";
1851 if (!edit) {
1852 if (old) {
1853 g_free (old);
1854 old = 0;
1856 return;
1858 exp = old ? old : exp;
1859 if (again) { /*ctrl-hotkey for search again. */
1860 if (!old)
1861 return;
1862 exp = g_strdup (old);
1863 } else {
1865 #ifdef HAVE_CHARSET
1866 if (exp && *exp)
1867 convert_to_display (exp);
1868 #endif /* HAVE_CHARSET */
1870 edit_search_dialog (edit, &exp);
1872 #ifdef HAVE_CHARSET
1873 if (exp && *exp)
1874 convert_from_input (exp);
1875 #endif /* HAVE_CHARSET */
1877 edit_push_action (edit, KEY_PRESS + edit->start_display);
1880 if (exp) {
1881 if (*exp) {
1882 int len = 0;
1883 if (old)
1884 g_free (old);
1885 old = g_strdup (exp);
1887 if (search_create_bookmark) {
1888 int found = 0, books = 0;
1889 int l = 0, l_last = -1;
1890 long p, q = 0;
1891 for (;;) {
1892 p = edit_find (q, (unsigned char *) exp, &len, edit->last_byte,
1893 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
1894 if (p < 0)
1895 break;
1896 found++;
1897 l += edit_count_lines (edit, q, p);
1898 if (l != l_last) {
1899 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
1900 books++;
1902 l_last = l;
1903 q = p + 1;
1905 if (found) {
1906 /* in response to number of bookmarks added because of string being found %d times */
1907 message (0, _("Search"), _(" %d finds made, %d bookmarks added "), found, books);
1908 } else {
1909 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
1911 } else {
1913 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
1914 edit->search_start--;
1916 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
1917 edit->search_start++;
1919 edit->search_start = edit_find (edit->search_start, (unsigned char *) exp, &len, edit->last_byte,
1920 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
1922 if (edit->search_start >= 0) {
1923 edit->found_start = edit->search_start;
1924 edit->found_len = len;
1926 edit_cursor_move (edit, edit->search_start - edit->curs1);
1927 edit_scroll_screen_over_cursor (edit);
1928 if (replace_backwards)
1929 edit->search_start--;
1930 else
1931 edit->search_start++;
1932 } else if (edit->search_start == -3) {
1933 edit->search_start = edit->curs1;
1934 regexp_error (edit);
1935 } else {
1936 edit->search_start = edit->curs1;
1937 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
1941 g_free (exp);
1943 edit->force |= REDRAW_COMPLETELY;
1944 edit_scroll_screen_over_cursor (edit);
1948 /* Real edit only */
1949 void edit_quit_cmd (WEdit * edit)
1951 edit_push_action (edit, KEY_PRESS + edit->start_display);
1953 edit->force |= REDRAW_COMPLETELY;
1954 if (edit->modified) {
1955 switch (edit_query_dialog3 (_ ("Quit"), _ (" File was modified, Save with exit? "), _ ("Cancel quit"), _ ("&Yes"), _ ("&No"))) {
1956 case 1:
1957 edit_push_markers (edit);
1958 edit_set_markers (edit, 0, 0, 0, 0);
1959 if (!edit_save_cmd (edit))
1960 return;
1961 break;
1962 case 2:
1963 if (edit->delete_file)
1964 unlink (catstrs (edit->dir, edit->filename, 0));
1965 break;
1966 case 0:
1967 case -1:
1968 return;
1971 else if (edit->delete_file)
1972 unlink (catstrs (edit->dir, edit->filename, 0));
1973 dlg_stop (edit->widget.parent);
1976 #define TEMP_BUF_LEN 1024
1978 /* returns a null terminated length of text. Result must be free'd */
1979 unsigned char *edit_get_block (WEdit * edit, long start, long finish, int *l)
1981 unsigned char *s, *r;
1982 r = s = malloc (finish - start + 1);
1983 if (column_highlighting) {
1984 *l = 0;
1985 while (start < finish) { /* copy from buffer, excluding chars that are out of the column 'margins' */
1986 int c, x;
1987 x = edit_move_forward3 (edit, edit_bol (edit, start), 0, start);
1988 c = edit_get_byte (edit, start);
1989 if ((x >= edit->column1 && x < edit->column2)
1990 || (x >= edit->column2 && x < edit->column1) || c == '\n') {
1991 *s++ = c;
1992 (*l)++;
1994 start++;
1996 } else {
1997 *l = finish - start;
1998 while (start < finish)
1999 *s++ = edit_get_byte (edit, start++);
2001 *s = 0;
2002 return r;
2005 /* save block, returns 1 on success */
2007 edit_save_block (WEdit * edit, const char *filename, long start,
2008 long finish)
2010 int len, file;
2012 if ((file =
2013 mc_open (filename, O_CREAT | O_WRONLY | O_TRUNC,
2014 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY)) == -1)
2015 return 0;
2017 if (column_highlighting) {
2018 unsigned char *block, *p;
2019 int r;
2020 p = block = edit_get_block (edit, start, finish, &len);
2021 while (len) {
2022 r = mc_write (file, p, len);
2023 if (r < 0)
2024 break;
2025 p += r;
2026 len -= r;
2028 free (block);
2029 } else {
2030 unsigned char *buf;
2031 int i = start, end;
2032 len = finish - start;
2033 buf = malloc (TEMP_BUF_LEN);
2034 while (start != finish) {
2035 end = min (finish, start + TEMP_BUF_LEN);
2036 for (; i < end; i++)
2037 buf[i - start] = edit_get_byte (edit, i);
2038 len -= mc_write (file, (char *) buf, end - start);
2039 start = end;
2041 free (buf);
2043 mc_close (file);
2044 if (len)
2045 return 0;
2046 return 1;
2049 /* copies a block to clipboard file */
2050 static int edit_save_block_to_clip_file (WEdit * edit, long start, long finish)
2052 return edit_save_block (edit, catstrs (home_dir, CLIP_FILE, 0), start, finish);
2056 void edit_paste_from_history (WEdit *edit)
2060 int edit_copy_to_X_buf_cmd (WEdit * edit)
2062 long start_mark, end_mark;
2063 if (eval_marks (edit, &start_mark, &end_mark))
2064 return 0;
2065 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2066 edit_error_dialog (_(" Copy to clipboard "), get_sys_error (_(" Unable to save to file. ")));
2067 return 1;
2069 edit_mark_cmd (edit, 1);
2070 return 0;
2073 int edit_cut_to_X_buf_cmd (WEdit * edit)
2075 long start_mark, end_mark;
2076 if (eval_marks (edit, &start_mark, &end_mark))
2077 return 0;
2078 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2079 edit_error_dialog (_(" Cut to clipboard "), _(" Unable to save to file. "));
2080 return 1;
2082 edit_block_delete_cmd (edit);
2083 edit_mark_cmd (edit, 1);
2084 return 0;
2087 void edit_paste_from_X_buf_cmd (WEdit * edit)
2089 edit_insert_file (edit, catstrs (home_dir, CLIP_FILE, 0));
2093 void edit_goto_cmd (WEdit *edit)
2095 char *f;
2096 static int l = 0;
2097 char s[12];
2098 sprintf (s, "%d", l);
2099 f = input_dialog (_(" Goto line "), _(" Enter line: "), l ? s : "");
2100 if (f) {
2101 if (*f) {
2102 l = atoi (f);
2103 edit_move_display (edit, l - edit->num_widget_lines / 2 - 1);
2104 edit_move_to_line (edit, l - 1);
2105 edit->force |= REDRAW_COMPLETELY;
2107 g_free (f);
2111 /*returns 1 on success */
2112 int edit_save_block_cmd (WEdit * edit)
2114 long start_mark, end_mark;
2115 char *exp;
2116 if (eval_marks (edit, &start_mark, &end_mark))
2117 return 1;
2118 exp = edit_get_save_file (edit->dir, catstrs (home_dir, CLIP_FILE, 0), _ (" Save Block "));
2119 edit_push_action (edit, KEY_PRESS + edit->start_display);
2120 if (exp) {
2121 if (!*exp) {
2122 g_free (exp);
2123 return 0;
2124 } else {
2125 if (edit_save_block (edit, exp, start_mark, end_mark)) {
2126 g_free (exp);
2127 edit->force |= REDRAW_COMPLETELY;
2128 return 1;
2129 } else {
2130 g_free (exp);
2131 edit_error_dialog (_ (" Save Block "), get_sys_error (_ (" Error trying to save file. ")));
2135 edit->force |= REDRAW_COMPLETELY;
2136 return 0;
2140 /* returns 1 on success */
2142 edit_insert_file_cmd (WEdit *edit)
2144 char *exp =
2145 edit_get_load_file (edit->dir, catstrs (home_dir, CLIP_FILE, 0),
2146 _(" Insert File "));
2147 edit_push_action (edit, KEY_PRESS + edit->start_display);
2148 if (exp) {
2149 if (!*exp) {
2150 g_free (exp);
2151 return 0;
2152 } else {
2153 if (edit_insert_file (edit, exp)) {
2154 g_free (exp);
2155 edit->force |= REDRAW_COMPLETELY;
2156 return 1;
2157 } else {
2158 g_free (exp);
2159 edit_error_dialog (_(" Insert File "),
2160 get_sys_error (_
2161 (" Error trying to insert file. ")));
2165 edit->force |= REDRAW_COMPLETELY;
2166 return 0;
2169 /* sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
2170 int edit_sort_cmd (WEdit * edit)
2172 static char *old = 0;
2173 char *exp;
2174 long start_mark, end_mark;
2175 int e;
2177 if (eval_marks (edit, &start_mark, &end_mark)) {
2178 edit_error_dialog (_(" Sort block "), _(" You must first highlight a block of text. "));
2179 return 0;
2181 edit_save_block (edit, catstrs (home_dir, BLOCK_FILE, 0), start_mark, end_mark);
2183 exp = old ? old : "";
2185 exp = input_dialog (_(" Run Sort "),
2186 _(" Enter sort options (see manpage) separated by whitespace: "), exp);
2188 if (!exp)
2189 return 1;
2190 if (old)
2191 g_free (old);
2192 old = exp;
2194 e = system (catstrs (" sort ", exp, " ", home_dir, BLOCK_FILE, " > ", home_dir, TEMP_FILE, 0));
2195 if (e) {
2196 if (e == -1 || e == 127) {
2197 edit_error_dialog (_(" Sort "),
2198 get_sys_error (_(" Error trying to execute sort command ")));
2199 } else {
2200 char q[8];
2201 sprintf (q, "%d ", e);
2202 edit_error_dialog (_(" Sort "),
2203 catstrs (_(" Sort returned non-zero: "), q, 0));
2205 return -1;
2208 edit->force |= REDRAW_COMPLETELY;
2210 if (edit_block_delete_cmd (edit))
2211 return 1;
2212 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, 0));
2213 return 0;
2216 /* if block is 1, a block must be highlighted and the shell command
2217 processes it. If block is 0 the shell command is a straight system
2218 command, that just produces some output which is to be inserted */
2219 void
2220 edit_block_process_cmd (WEdit * edit, const char *shell_cmd, int block)
2222 long start_mark, end_mark;
2223 char buf[BUFSIZ];
2224 FILE *script_home = NULL;
2225 FILE *script_src = NULL;
2226 FILE *block_file = NULL;
2227 char *o = NULL;
2228 char *h = NULL;
2229 char *b = NULL;
2231 o = catstrs (mc_home, shell_cmd, 0); /* original source script */
2232 h = catstrs (home_dir, EDIT_DIR, shell_cmd, 0); /* home script */
2233 b = catstrs (home_dir, BLOCK_FILE, 0); /* block file */
2235 if (!(script_home = fopen (h, "r"))) {
2236 if (!(script_home = fopen (h, "w"))) {
2237 edit_error_dialog ("", get_sys_error (catstrs
2239 ("Error creating script:"),
2240 h, 0)));
2241 return;
2243 if (!(script_src = fopen (o, "r"))) {
2244 fclose (script_home);
2245 unlink (h);
2246 edit_error_dialog ("", get_sys_error (catstrs
2247 (_("Error reading script:"),
2248 o, 0)));
2249 return;
2251 while (fgets (buf, sizeof (buf), script_src))
2252 fputs (buf, script_home);
2253 if (fclose (script_home)) {
2254 edit_error_dialog ("", get_sys_error (catstrs
2256 ("Error closing script:"),
2257 h, 0)));
2258 return;
2260 chmod (h, 0700);
2261 edit_error_dialog ("", get_sys_error (catstrs
2262 (_("Script created:"), h,
2263 0)));
2266 open_error_pipe ();
2268 if (block) { /* for marked block run indent formatter */
2269 if (eval_marks (edit, &start_mark, &end_mark)) {
2270 edit_error_dialog (_("Process block"),
2272 (" You must first highlight a block of text. "));
2273 return;
2275 edit_save_block (edit, b, start_mark, end_mark);
2278 * Run script.
2279 * Initial space is to avoid polluting bash history.
2280 * Arguments:
2281 * $1 - name of the edited file (to check its extention etc).
2282 * $2 - file containing the current block.
2283 * $3 - file where error messages should be put
2284 * (for compatibility with old scripts).
2286 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2287 edit->filename, " ", home_dir, BLOCK_FILE,
2288 " /dev/null", NULL));
2290 } else {
2292 * No block selected, just execute the command for the file.
2293 * Arguments:
2294 * $1 - name of the edited file.
2296 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2297 edit->filename, NULL));
2300 close_error_pipe (0, 0);
2302 edit_refresh_cmd (edit);
2303 edit->force |= REDRAW_COMPLETELY;
2305 /* insert result block */
2306 if (block) {
2307 if (edit_block_delete_cmd (edit))
2308 return;
2309 edit_insert_file (edit, b);
2310 if ((block_file = fopen (b, "w")))
2311 fclose (block_file);
2312 return;
2315 return;
2318 /* prints at the cursor */
2319 /* returns the number of chars printed */
2320 int edit_print_string (WEdit * e, const char *s)
2322 int i = 0;
2323 while (s[i])
2324 edit_execute_cmd (e, -1, (unsigned char) s[i++]);
2325 e->force |= REDRAW_COMPLETELY;
2326 edit_update_screen (e);
2327 return i;
2330 int edit_printf (WEdit * e, const char *fmt, ...)
2332 int i;
2333 va_list pa;
2334 char s[1024];
2335 va_start (pa, fmt);
2336 g_vsnprintf (s, sizeof (s), fmt, pa);
2337 i = edit_print_string (e, s);
2338 va_end (pa);
2339 return i;
2342 /* FIXME: does this function break NT_OS2 ? */
2344 static void pipe_mail (WEdit *edit, char *to, char *subject, char *cc)
2346 FILE *p = 0;
2347 char *s;
2349 s = g_strdup_printf ("mail -s \"%s\" -c \"%s\" \"%s\"", subject, cc, to);
2351 if (s) {
2352 p = popen (s, "w");
2353 g_free (s);
2356 if (p) {
2357 long i;
2358 for (i = 0; i < edit->last_byte; i++)
2359 fputc (edit_get_byte (edit, i), p);
2360 pclose (p);
2364 #define MAIL_DLG_HEIGHT 12
2366 void edit_mail_dialog (WEdit * edit)
2368 char *tmail_to;
2369 char *tmail_subject;
2370 char *tmail_cc;
2372 static char *mail_cc_last = 0;
2373 static char *mail_subject_last = 0;
2374 static char *mail_to_last = 0;
2376 QuickDialog Quick_input =
2377 {50, MAIL_DLG_HEIGHT, -1, 0, N_(" Mail "),
2378 "[Input Line Keys]", 0};
2380 QuickWidget quick_widgets[] =
2382 {quick_button, 6, 10, 9, MAIL_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
2383 0, NULL},
2384 {quick_button, 2, 10, 9, MAIL_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
2385 0, NULL},
2386 {quick_input, 3, 50, 8, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2387 0, "mail-dlg-input"},
2388 {quick_label, 2, 50, 7, MAIL_DLG_HEIGHT, N_(" Copies to"), 0, 0, 0,
2389 0, 0},
2390 {quick_input, 3, 50, 6, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2391 0, "mail-dlg-input-2"},
2392 {quick_label, 2, 50, 5, MAIL_DLG_HEIGHT, N_(" Subject"), 0, 0, 0,
2393 0, 0},
2394 {quick_input, 3, 50, 4, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2395 0, "mail-dlg-input-3"},
2396 {quick_label, 2, 50, 3, MAIL_DLG_HEIGHT, N_(" To"), 0, 0, 0,
2397 0, 0},
2398 {quick_label, 2, 50, 2, MAIL_DLG_HEIGHT, N_(" mail -s <subject> -c <cc> <to>"), 0, 0, 0,
2399 0, 0},
2400 {0}};
2402 quick_widgets[2].str_result = &tmail_cc;
2403 quick_widgets[2].text = mail_cc_last ? mail_cc_last : "";
2404 quick_widgets[4].str_result = &tmail_subject;
2405 quick_widgets[4].text = mail_subject_last ? mail_subject_last : "";
2406 quick_widgets[6].str_result = &tmail_to;
2407 quick_widgets[6].text = mail_to_last ? mail_to_last : "";
2409 Quick_input.widgets = quick_widgets;
2411 if (quick_dialog (&Quick_input) != B_CANCEL) {
2412 if (mail_cc_last)
2413 g_free (mail_cc_last);
2414 if (mail_subject_last)
2415 g_free (mail_subject_last);
2416 if (mail_to_last)
2417 g_free (mail_to_last);
2418 mail_cc_last = *(quick_widgets[2].str_result);
2419 mail_subject_last = *(quick_widgets[4].str_result);
2420 mail_to_last = *(quick_widgets[6].str_result);
2421 pipe_mail (edit, mail_to_last, mail_subject_last, mail_cc_last);
2426 /*******************/
2427 /* Word Completion */
2428 /*******************/
2431 /* find first character of current word */
2432 static int edit_find_word_start (WEdit *edit, long *word_start, int *word_len)
2434 int i, c, last;
2436 /* return if at begin of file */
2437 if (edit->curs1 <= 0)
2438 return 0;
2440 c = (unsigned char) edit_get_byte (edit, edit->curs1 - 1);
2441 /* return if not at end or in word */
2442 if (isspace (c) || !(isalnum (c) || c == '_'))
2443 return 0;
2445 /* search start of word to be completed */
2446 for (i = 2;; i++) {
2447 /* return if at begin of file */
2448 if (edit->curs1 - i < 0)
2449 return 0;
2451 last = c;
2452 c = (unsigned char) edit_get_byte (edit, edit->curs1 - i);
2454 if (!(isalnum (c) || c == '_')) {
2455 /* return if word starts with digit */
2456 if (isdigit (last))
2457 return 0;
2459 *word_start = edit->curs1 - (i - 1); /* start found */
2460 *word_len = i - 1;
2461 break;
2464 /* success */
2465 return 1;
2469 /* (re)set search parameters to the given values */
2470 static void edit_set_search_parameters (int rs, int rb, int rr, int rw, int rc)
2472 replace_scanf = rs;
2473 replace_backwards = rb;
2474 replace_regexp = rr;
2475 replace_whole = rw;
2476 replace_case = rc;
2480 #define MAX_WORD_COMPLETIONS 100 /* in listbox */
2482 /* collect the possible completions */
2483 static int edit_collect_completions (WEdit *edit, long start,
2484 int word_len, char *match_expr, struct selection *compl, int *num)
2486 int len, max_len = 0, i, skip;
2487 char *bufpos;
2489 /* collect max MAX_WORD_COMPLETIONS completions */
2490 while (*num < MAX_WORD_COMPLETIONS) {
2491 /* get next match */
2492 start = edit_find (start - 1, (unsigned char *) match_expr, &len,
2493 edit->last_byte, (int (*)(void *, long)) edit_get_byte,
2494 (void *) edit, 0);
2496 /* not matched */
2497 if (start < 0)
2498 break;
2500 /* add matched completion if not yet added */
2501 bufpos = &edit->buffers1[start >> S_EDIT_BUF_SIZE][start & M_EDIT_BUF_SIZE];
2502 skip = 0;
2503 for (i = 0; i < *num; i++) {
2504 if (strncmp (&compl[i].text[word_len], &bufpos[word_len],
2505 max (len, compl[i].len) - word_len) == 0) {
2506 skip = 1;
2507 break; /* skip it, already added */
2510 if (skip)
2511 continue;
2513 compl[*num].text = CMalloc (len + 1);
2514 compl[*num].len = len;
2515 for (i = 0; i < len; i++)
2516 compl[*num].text[i] = *(bufpos + i);
2517 compl[*num].text[i] = '\0';
2518 (*num)++;
2520 /* note the maximal length needed for the completion dialog */
2521 if (len > max_len)
2522 max_len = len;
2524 return max_len;
2528 static int compllist_callback (void *data)
2530 return 0;
2534 /* let the user select its preferred completion */
2535 static void
2536 edit_completion_dialog (WEdit *edit, int max_len, int word_len,
2537 struct selection *compl, int num_compl)
2539 int start_x, start_y, offset, i;
2540 char *curr = NULL;
2541 Dlg_head *compl_dlg;
2542 WListbox *compl_list;
2543 unsigned int compl_dlg_h; /* completion dialog height */
2544 unsigned int compl_dlg_w; /* completion dialog width */
2546 /* calculate the dialog metrics */
2547 compl_dlg_h = num_compl + 2;
2548 compl_dlg_w = max_len + 4;
2549 start_x = edit->curs_col + edit->start_col - (compl_dlg_w / 2);
2550 start_y = edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + 1;
2552 if (start_x < 0)
2553 start_x = 0;
2554 if (compl_dlg_w > COLS)
2555 compl_dlg_w = COLS;
2556 if (compl_dlg_h > LINES - 2)
2557 compl_dlg_h = LINES - 2;
2559 offset = start_x + compl_dlg_w - COLS;
2560 if (offset > 0)
2561 start_x -= offset;
2562 offset = start_y + compl_dlg_h - LINES;
2563 if (offset > 0)
2564 start_y -= (offset + 1);
2566 /* create the dialog */
2567 compl_dlg = create_dlg (start_y, start_x, compl_dlg_h, compl_dlg_w,
2568 dialog_colors, NULL, "[Completion]", NULL,
2569 DLG_COMPACT);
2571 /* create the listbox */
2572 compl_list = listbox_new (1, 1, compl_dlg_w - 2, compl_dlg_h - 2, 0,
2573 compllist_callback, NULL);
2575 /* add the dialog */
2576 add_widget (compl_dlg, compl_list);
2578 /* fill the listbox with the completions */
2579 for (i = 0; i < num_compl; i++)
2580 listbox_add_item (compl_list, 0, 0, compl[i].text, NULL);
2582 /* pop up the dialog */
2583 run_dlg (compl_dlg);
2585 /* apply the choosen completion */
2586 if (compl_dlg->ret_value == B_ENTER) {
2587 listbox_get_current (compl_list, &curr, NULL);
2588 if (curr)
2589 for (curr += word_len; *curr; curr++)
2590 edit_insert (edit, *curr);
2593 /* destroy dialog before return */
2594 destroy_dlg (compl_dlg);
2598 /* complete current word using regular expression search */
2599 /* backwards beginning at current cursor position */
2600 void edit_complete_word_cmd (WEdit *edit)
2602 int word_len = 0, i, num_compl = 0, max_len;
2603 long word_start = 0;
2604 char *bufpos;
2605 char match_expr[MAX_REPL_LEN];
2606 struct selection compl[MAX_WORD_COMPLETIONS]; /* completions */
2608 /* don't want to disturb another search */
2609 int old_rs = replace_scanf;
2610 int old_rb = replace_backwards;
2611 int old_rr = replace_regexp;
2612 int old_rw = replace_whole;
2613 int old_rc = replace_case;
2615 /* search start of word to be completed */
2616 if (!edit_find_word_start (edit, &word_start, &word_len))
2617 return;
2619 /* prepare match expression */
2620 bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE]
2621 [word_start & M_EDIT_BUF_SIZE];
2622 strncpy (match_expr, bufpos, word_len);
2623 match_expr[word_len] = '\0';
2624 strcat (match_expr, "[a-zA-Z_0-9]+");
2626 /* init search: backward, regexp, whole word, case sensitive */
2627 edit_set_search_parameters (0, 1, 1, 1, 1);
2629 /* collect the possible completions */
2630 /* start search from curs1 down to begin of file */
2631 max_len = edit_collect_completions (edit, word_start, word_len,
2632 match_expr, (struct selection *) &compl, &num_compl);
2634 if (num_compl > 0) {
2635 /* insert completed word if there is only one match */
2636 if (num_compl == 1) {
2637 for (i = word_len; i < compl[0].len; i++)
2638 edit_insert (edit, *(compl[0].text + i));
2640 /* more than one possible completion => ask the user */
2641 else {
2642 /* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
2643 /* !!! pressed again the selection dialog pops up, but that !!! */
2644 /* !!! seems to require a further internal state !!! */
2645 /*beep ();*/
2647 /* let the user select the preferred completion */
2648 edit_completion_dialog (edit, max_len, word_len,
2649 (struct selection *) &compl, num_compl);
2653 /* release memory before return */
2654 for (i = 0; i < num_compl; i++)
2655 free (compl[i].text);
2657 /* restore search parameters */
2658 edit_set_search_parameters (old_rs, old_rb, old_rr, old_rw, old_rc);