corrected some file operations dialog msgs
[midnight-commander.git] / edit / editcmd.c
blobf3c10dc4dc08d7c6eee4ecf7ac10285ceeea7dfc
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 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 */
467 raw_callback (struct Dlg_head *h, int key, int Msg)
469 switch (Msg) {
470 case DLG_KEY:
471 h->running = 0;
472 h->ret_value = key;
473 return MSG_HANDLED;
475 return default_dlg_callback (h, key, Msg);;
478 /* gets a raw key from the keyboard. Passing cancel = 1 draws
479 a cancel button thus allowing c-c etc. Alternatively, cancel = 0
480 will return the next key pressed. ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
481 and Esc are cannot returned */
483 edit_raw_key_query (char *heading, char *query, int cancel)
485 int w = strlen (query) + 7;
486 struct Dlg_head *raw_dlg =
487 create_dlg (0, 0, 7, w, dialog_colors, raw_callback,
488 NULL, heading,
489 DLG_CENTER | DLG_TRYUP | DLG_WANT_TAB);
490 if (cancel)
491 add_widget (raw_dlg,
492 button_new (4, w / 2 - 5, B_CANCEL, NORMAL_BUTTON,
493 _("Cancel"), 0, 0, 0));
494 add_widget (raw_dlg, label_new (3 - cancel, 2, query, 0));
495 add_widget (raw_dlg,
496 input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0));
497 run_dlg (raw_dlg);
498 w = raw_dlg->ret_value;
499 destroy_dlg (raw_dlg);
500 if (cancel) {
501 if (w == XCTRL ('g') || w == XCTRL ('c') || w == ESC_CHAR
502 || w == B_CANCEL)
503 return 0;
506 return w;
509 /* creates a macro file if it doesn't exist */
510 static FILE *edit_open_macro_file (const char *r)
512 char *filename;
513 int file;
514 filename = catstrs (home_dir, MACRO_FILE, 0);
515 if ((file = open (filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
516 return 0;
517 close (file);
518 return fopen (filename, r);
521 #define MAX_MACROS 1024
522 static int saved_macro[MAX_MACROS + 1];
523 static int saved_macros_loaded = 0;
526 This is just to stop the macro file be loaded over and over for keys
527 that aren't defined to anything. On slow systems this could be annoying.
529 int macro_exists (int k)
531 int i;
532 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++)
533 if (saved_macro[i] == k)
534 return i;
535 return -1;
538 /* returns 1 on error */
539 int edit_delete_macro (WEdit * edit, int k)
541 struct macro macro[MAX_MACRO_LENGTH];
542 FILE *f, *g;
543 int s, i, n, j = 0;
545 if (saved_macros_loaded)
546 if ((j = macro_exists (k)) < 0)
547 return 0;
548 g = fopen (catstrs (home_dir, TEMP_FILE, 0), "w");
549 if (!g) {
550 /* This heads the delete macro error dialog box */
551 edit_error_dialog (_(" Delete macro "),
552 /* 'Open' = load temp file */
553 get_sys_error (_(" Error trying to open temp file ")));
554 return 1;
556 f = edit_open_macro_file ("r");
557 if (!f) {
558 /* This heads the delete macro error dialog box */
559 edit_error_dialog (_(" Delete macro "),
560 /* 'Open' = load temp file */
561 get_sys_error (_(" Error trying to open macro file ")));
562 fclose (g);
563 return 1;
565 for (;;) {
566 n = fscanf (f, ("key '%d 0': "), &s);
567 if (!n || n == EOF)
568 break;
569 n = 0;
570 while (fscanf (f, "%hd %hd, ", &macro[n].command, &macro[n].ch))
571 n++;
572 fscanf (f, ";\n");
573 if (s != k) {
574 fprintf (g, ("key '%d 0': "), s);
575 for (i = 0; i < n; i++)
576 fprintf (g, "%hd %hd, ", macro[i].command, macro[i].ch);
577 fprintf (g, ";\n");
580 fclose (f);
581 fclose (g);
582 if (rename (catstrs (home_dir, TEMP_FILE, 0), catstrs (home_dir, MACRO_FILE, 0)) == -1) {
583 /* This heads the delete macro error dialog box */
584 edit_error_dialog (_(" Delete macro "),
585 get_sys_error (_(" Error trying to overwrite macro file ")));
586 return 1;
588 if (saved_macros_loaded)
589 memmove (saved_macro + j, saved_macro + j + 1, sizeof (int) * (MAX_MACROS - j - 1));
590 return 0;
593 /* returns 0 on error */
594 int edit_save_macro_cmd (WEdit * edit, struct macro macro[], int n)
596 FILE *f;
597 int s, i;
599 edit_push_action (edit, KEY_PRESS + edit->start_display);
600 /* This heads the 'Macro' dialog box */
601 s = edit_raw_key_query (_(" Macro "),
602 /* Input line for a single key press follows the ':' */
603 _(" Press the macro's new hotkey: "), 1);
604 edit->force |= REDRAW_COMPLETELY;
605 if (s) {
606 if (edit_delete_macro (edit, s))
607 return 0;
608 f = edit_open_macro_file ("a+");
609 if (f) {
610 fprintf (f, ("key '%d 0': "), s);
611 for (i = 0; i < n; i++)
612 fprintf (f, "%hd %hd, ", macro[i].command, macro[i].ch);
613 fprintf (f, ";\n");
614 fclose (f);
615 if (saved_macros_loaded) {
616 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++);
617 saved_macro[i] = s;
619 return 1;
620 } else
621 /* This heads the 'Save Macro' dialog box */
622 edit_error_dialog (_(" Save macro "), get_sys_error (_(" Error trying to open macro file ")));
624 return 0;
627 void edit_delete_macro_cmd (WEdit * edit)
629 int command;
631 command = edit_raw_key_query (_ (" Delete Macro "),
632 _ (" Press macro hotkey: "), 1);
634 if (!command)
635 return;
637 edit_delete_macro (edit, command);
640 /* return 0 on error */
641 int edit_load_macro_cmd (WEdit * edit, struct macro macro[], int *n, int k)
643 FILE *f;
644 int s, i = 0, found = 0;
646 if (saved_macros_loaded)
647 if (macro_exists (k) < 0)
648 return 0;
650 if ((f = edit_open_macro_file ("r"))) {
651 struct macro dummy;
652 do {
653 int u;
654 u = fscanf (f, ("key '%d 0': "), &s);
655 if (!u || u == EOF)
656 break;
657 if (!saved_macros_loaded)
658 saved_macro[i++] = s;
659 if (!found) {
660 *n = 0;
661 while (*n < MAX_MACRO_LENGTH && 2 == fscanf (f, "%hd %hd, ", &macro[*n].command, &macro[*n].ch))
662 (*n)++;
663 } else {
664 while (2 == fscanf (f, "%hd %hd, ", &dummy.command, &dummy.ch));
666 fscanf (f, ";\n");
667 if (s == k)
668 found = 1;
669 } while (!found || !saved_macros_loaded);
670 if (!saved_macros_loaded) {
671 saved_macro[i] = 0;
672 saved_macros_loaded = 1;
674 fclose (f);
675 return found;
676 } else
677 /* This heads the 'Load Macro' dialog box */
678 edit_error_dialog (_(" Load macro "),
679 get_sys_error (_(" Error trying to open macro file ")));
680 return 0;
683 /* }}} Macro stuff starts here */
685 /* returns 1 on success */
686 int edit_save_confirm_cmd (WEdit * edit)
688 char *f;
690 if (edit_confirm_save) {
691 f = catstrs (_(" Confirm save file? : "), edit->filename, " ", 0);
692 /* Buttons to 'Confirm save file' query */
693 if (edit_query_dialog2 (_(" Save file "), f, _("Save"), _("Cancel")))
694 return 0;
696 return edit_save_cmd (edit);
700 /* returns 1 on success */
701 int edit_save_cmd (WEdit * edit)
703 if (!edit_save_file (edit, catstrs (edit->dir, edit->filename, 0)))
704 return edit_save_as_cmd (edit);
705 edit->force |= REDRAW_COMPLETELY;
706 edit->modified = 0;
707 edit->delete_file = 0;
709 return 1;
713 /* returns 1 on success */
714 int edit_new_cmd (WEdit * edit)
716 if (edit->modified) {
717 if (edit_query_dialog2 (_ (" Warning "), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
718 edit->force |= REDRAW_COMPLETELY;
719 return 0;
722 edit->force |= REDRAW_COMPLETELY;
723 edit->modified = 0;
724 return edit_renew (edit); /* if this gives an error, something has really screwed up */
727 /* returns 1 on error */
728 int edit_load_file_from_filename (WEdit * edit, char *exp)
730 if (!edit_reload (edit, exp, 0, "", 0))
731 return 1;
732 edit_split_filename (edit, exp);
733 edit->modified = 0;
734 return 0;
737 int edit_load_cmd (WEdit * edit)
739 char *exp;
741 if (edit->modified) {
742 if (edit_query_dialog2 (_ (" Warning "), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
743 edit->force |= REDRAW_COMPLETELY;
744 return 0;
748 exp = edit_get_load_file (edit->dir, edit->filename, _ (" Load "));
750 if (exp) {
751 if (*exp)
752 edit_load_file_from_filename (edit, exp);
753 g_free (exp);
755 edit->force |= REDRAW_COMPLETELY;
756 return 0;
760 if mark2 is -1 then marking is from mark1 to the cursor.
761 Otherwise its between the markers. This handles this.
762 Returns 1 if no text is marked.
764 int eval_marks (WEdit * edit, long *start_mark, long *end_mark)
766 if (edit->mark1 != edit->mark2) {
767 if (edit->mark2 >= 0) {
768 *start_mark = min (edit->mark1, edit->mark2);
769 *end_mark = max (edit->mark1, edit->mark2);
770 } else {
771 *start_mark = min (edit->mark1, edit->curs1);
772 *end_mark = max (edit->mark1, edit->curs1);
773 edit->column2 = edit->curs_col;
775 return 0;
776 } else {
777 *start_mark = *end_mark = 0;
778 edit->column2 = edit->column1 = 0;
779 return 1;
783 #define space_width 1
785 void 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_insert_ahead (edit, '\n');
805 if (edit_get_byte (edit, p) == '\n') {
806 p++;
807 break;
810 edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->curs1);
811 l = col - edit_get_col (edit);
812 while (l >= space_width) {
813 edit_insert (edit, ' ');
814 l -= space_width;
816 continue;
818 edit_insert (edit, data[i]);
820 edit_cursor_move (edit, cursor - edit->curs1);
824 void edit_block_copy_cmd (WEdit * edit)
826 long start_mark, end_mark, current = edit->curs1;
827 int size, x;
828 unsigned char *copy_buf;
830 edit_update_curs_col (edit);
831 x = edit->curs_col;
832 if (eval_marks (edit, &start_mark, &end_mark))
833 return;
834 if (column_highlighting)
835 if ((x >= edit->column1 && x < edit->column2) || (x > edit->column2 && x <= edit->column1))
836 return;
838 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
840 /* all that gets pushed are deletes hence little space is used on the stack */
842 edit_push_markers (edit);
844 if (column_highlighting) {
845 edit_insert_column_of_text (edit, copy_buf, size, abs (edit->column2 - edit->column1));
846 } else {
847 while (size--)
848 edit_insert_ahead (edit, copy_buf[size]);
851 free (copy_buf);
852 edit_scroll_screen_over_cursor (edit);
854 if (column_highlighting) {
855 edit_set_markers (edit, 0, 0, 0, 0);
856 edit_push_action (edit, COLUMN_ON);
857 column_highlighting = 0;
858 } else if (start_mark < current && end_mark > current)
859 edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0);
861 edit->force |= REDRAW_PAGE;
865 void edit_block_move_cmd (WEdit * edit)
867 long count;
868 long current;
869 unsigned char *copy_buf;
870 long start_mark, end_mark;
871 int deleted = 0;
872 int x = 0;
874 if (eval_marks (edit, &start_mark, &end_mark))
875 return;
876 if (column_highlighting) {
877 edit_update_curs_col (edit);
878 x = edit->curs_col;
879 if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
880 if ((x > edit->column1 && x < edit->column2) || (x > edit->column2 && x < edit->column1))
881 return;
882 } else if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
883 return;
885 if ((end_mark - start_mark) > option_max_undo / 2)
886 if (edit_query_dialog2 (_ (" Warning "), _ (" Block is large, you may not be able to undo this action. "), _ ("Continue"), _ ("Cancel")))
887 return;
889 edit_push_markers (edit);
890 current = edit->curs1;
891 if (column_highlighting) {
892 int size, c1, c2, line;
893 line = edit->curs_line;
894 if (edit->mark2 < 0)
895 edit_mark_cmd (edit, 0);
896 c1 = min (edit->column1, edit->column2);
897 c2 = max (edit->column1, edit->column2);
898 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
899 if (x < c2) {
900 edit_block_delete_cmd (edit);
901 deleted = 1;
903 edit_move_to_line (edit, line);
904 edit_cursor_move (edit, edit_move_forward3 (edit, edit_bol (edit, edit->curs1), x, 0) - edit->curs1);
905 edit_insert_column_of_text (edit, copy_buf, size, c2 - c1);
906 if (!deleted) {
907 line = edit->curs_line;
908 edit_update_curs_col (edit);
909 x = edit->curs_col;
910 edit_block_delete_cmd (edit);
911 edit_move_to_line (edit, line);
912 edit_cursor_move (edit, edit_move_forward3 (edit, edit_bol (edit, edit->curs1), x, 0) - edit->curs1);
914 edit_set_markers (edit, 0, 0, 0, 0);
915 edit_push_action (edit, COLUMN_ON);
916 column_highlighting = 0;
917 } else {
918 copy_buf = malloc (end_mark - start_mark);
919 edit_cursor_move (edit, start_mark - edit->curs1);
920 edit_scroll_screen_over_cursor (edit);
921 count = start_mark;
922 while (count < end_mark) {
923 copy_buf[end_mark - count - 1] = edit_delete (edit);
924 count++;
926 edit_scroll_screen_over_cursor (edit);
927 edit_cursor_move (edit, current - edit->curs1 - (((current - edit->curs1) > 0) ? end_mark - start_mark : 0));
928 edit_scroll_screen_over_cursor (edit);
929 while (count-- > start_mark)
930 edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
931 edit_set_markers (edit, edit->curs1, edit->curs1 + end_mark - start_mark, 0, 0);
933 edit_scroll_screen_over_cursor (edit);
934 free (copy_buf);
935 edit->force |= REDRAW_PAGE;
938 void edit_cursor_to_bol (WEdit * edit);
940 void edit_delete_column_of_text (WEdit * edit)
942 long p, q, r, m1, m2;
943 int b, c, d;
944 int n;
946 eval_marks (edit, &m1, &m2);
947 n = edit_move_forward (edit, m1, 0, m2) + 1;
948 c = edit_move_forward3 (edit, edit_bol (edit, m1), 0, m1);
949 d = edit_move_forward3 (edit, edit_bol (edit, m2), 0, m2);
951 b = min (c, d);
952 c = max (c, d);
954 while (n--) {
955 r = edit_bol (edit, edit->curs1);
956 p = edit_move_forward3 (edit, r, b, 0);
957 q = edit_move_forward3 (edit, r, c, 0);
958 if (p < m1)
959 p = m1;
960 if (q > m2)
961 q = m2;
962 edit_cursor_move (edit, p - edit->curs1);
963 while (q > p) { /* delete line between margins */
964 if (edit_get_byte (edit, edit->curs1) != '\n')
965 edit_delete (edit);
966 q--;
968 if (n) /* move to next line except on the last delete */
969 edit_cursor_move (edit, edit_move_forward (edit, edit->curs1, 1, 0) - edit->curs1);
973 /* if success return 0 */
974 int edit_block_delete (WEdit * edit)
976 long count;
977 long start_mark, end_mark;
978 if (eval_marks (edit, &start_mark, &end_mark))
979 return 0;
980 if (column_highlighting && edit->mark2 < 0)
981 edit_mark_cmd (edit, 0);
982 if ((end_mark - start_mark) > option_max_undo / 2)
983 /* Warning message with a query to continue or cancel the operation */
984 if (edit_query_dialog2 (_ (" Warning "), _ (" Block is large, you may not be able to undo this action. "), _ (" Continue "), _ (" Cancel ")))
985 return 1;
986 edit_push_markers (edit);
987 edit_cursor_move (edit, start_mark - edit->curs1);
988 edit_scroll_screen_over_cursor (edit);
989 count = start_mark;
990 if (start_mark < end_mark) {
991 if (column_highlighting) {
992 if (edit->mark2 < 0)
993 edit_mark_cmd (edit, 0);
994 edit_delete_column_of_text (edit);
995 } else {
996 while (count < end_mark) {
997 edit_delete (edit);
998 count++;
1002 edit_set_markers (edit, 0, 0, 0, 0);
1003 edit->force |= REDRAW_PAGE;
1004 return 0;
1007 /* returns 1 if canceelled by user */
1008 int edit_block_delete_cmd (WEdit * edit)
1010 long start_mark, end_mark;
1011 if (eval_marks (edit, &start_mark, &end_mark)) {
1012 edit_delete_line (edit);
1013 return 0;
1015 return edit_block_delete (edit);
1019 #define INPUT_INDEX 9
1020 #define SEARCH_DLG_WIDTH 58
1021 #define SEARCH_DLG_HEIGHT 10
1022 #define REPLACE_DLG_WIDTH 58
1023 #define REPLACE_DLG_HEIGHT 15
1024 #define CONFIRM_DLG_WIDTH 79
1025 #define CONFIRM_DLG_HEIGTH 6
1026 #define B_REPLACE_ALL B_USER+1
1027 #define B_REPLACE_ONE B_USER+2
1028 #define B_SKIP_REPLACE B_USER+3
1030 int edit_replace_prompt (WEdit * edit, char *replace_text, int xpos, int ypos)
1032 QuickWidget quick_widgets[] =
1034 {quick_button, 63, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Cancel"),
1035 0, B_CANCEL, 0, 0, NULL},
1036 {quick_button, 50, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("o&Ne"),
1037 0, B_REPLACE_ONE, 0, 0, NULL},
1038 {quick_button, 37, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("al&L"),
1039 0, B_REPLACE_ALL, 0, 0, NULL},
1040 {quick_button, 21, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Skip"),
1041 0, B_SKIP_REPLACE, 0, 0, NULL},
1042 {quick_button, 4, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Replace"),
1043 0, B_ENTER, 0, 0, NULL},
1044 {quick_label, 2, CONFIRM_DLG_WIDTH, 2, CONFIRM_DLG_HEIGTH, 0,
1045 0, 0, 0, 0, 0},
1046 {0}};
1048 #ifdef HAVE_CHARSET
1049 char *msg = _(" Replace with: ");
1051 quick_widgets[5].text = catstrs (msg, replace_text, 0);
1053 if (*replace_text)
1054 convert_to_display (quick_widgets[5].text + strlen (msg));
1055 #else
1056 quick_widgets[5].text = catstrs (_ (" Replace with: "), replace_text, 0);
1057 #endif /* !HAVE_CHARSET */
1060 QuickDialog Quick_input =
1061 {CONFIRM_DLG_WIDTH, CONFIRM_DLG_HEIGTH, 0, 0, N_ (" Confirm replace "),
1062 "[Input Line Keys]", 0 /*quick_widgets */ };
1064 Quick_input.widgets = quick_widgets;
1066 Quick_input.xpos = xpos;
1068 /* Sometimes menu can hide replaced text. I don't like it */
1070 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + CONFIRM_DLG_HEIGTH - 1))
1071 ypos -= CONFIRM_DLG_HEIGTH;
1073 Quick_input.ypos = ypos;
1074 return quick_dialog (&Quick_input);
1078 void edit_replace_dialog (WEdit * edit, char **search_text, char **replace_text, char **arg_order)
1080 int treplace_scanf = replace_scanf;
1081 int treplace_regexp = replace_regexp;
1082 int treplace_all = replace_all;
1083 int treplace_prompt = replace_prompt;
1084 int treplace_backwards = replace_backwards;
1085 int treplace_whole = replace_whole;
1086 int treplace_case = replace_case;
1088 QuickWidget quick_widgets[] =
1090 {quick_button, 6, 10, 12, REPLACE_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1091 0, NULL},
1092 {quick_button, 2, 10, 12, REPLACE_DLG_HEIGHT, N_("&Ok"), 0, B_ENTER, 0,
1093 0, NULL},
1094 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1095 0, 0, NULL},
1096 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("replace &All"), 0, 0,
1097 0, 0, NULL},
1098 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("pr&Ompt on replace"), 0, 0,
1099 0, 0, NULL},
1100 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1101 0, 0, NULL},
1102 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1103 0, 0, NULL},
1104 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1105 0, 0, NULL},
1106 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 8, REPLACE_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1107 0, 0, NULL},
1108 {quick_input, 3, REPLACE_DLG_WIDTH, 7, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1109 0, "edit-argord"},
1110 {quick_label, 2, REPLACE_DLG_WIDTH, 6, REPLACE_DLG_HEIGHT, N_(" Enter replacement argument order eg. 3,2,1,4 "), 0, 0,
1111 0, 0, 0},
1112 {quick_input, 3, REPLACE_DLG_WIDTH, 5, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1113 0, "edit-replace"},
1114 {quick_label, 2, REPLACE_DLG_WIDTH, 4, REPLACE_DLG_HEIGHT, N_(" Enter replacement string:"), 0, 0, 0,
1115 0, 0},
1116 {quick_input, 3, REPLACE_DLG_WIDTH, 3, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1117 0, "edit-search"},
1118 {quick_label, 2, REPLACE_DLG_WIDTH, 2, REPLACE_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1119 0, 0},
1120 {0}};
1122 quick_widgets[2].result = &treplace_scanf;
1123 quick_widgets[3].result = &treplace_all;
1124 quick_widgets[4].result = &treplace_prompt;
1125 quick_widgets[5].result = &treplace_backwards;
1126 quick_widgets[6].result = &treplace_regexp;
1127 quick_widgets[7].result = &treplace_whole;
1128 quick_widgets[8].result = &treplace_case;
1129 quick_widgets[9].str_result = arg_order;
1130 quick_widgets[9].text = *arg_order;
1131 quick_widgets[11].str_result = replace_text;
1132 quick_widgets[11].text = *replace_text;
1133 quick_widgets[13].str_result = search_text;
1134 quick_widgets[13].text = *search_text;
1136 QuickDialog Quick_input =
1137 {REPLACE_DLG_WIDTH, REPLACE_DLG_HEIGHT, -1, 0, N_(" Replace "),
1138 "[Input Line Keys]", 0 /*quick_widgets */ };
1140 Quick_input.widgets = quick_widgets;
1142 if (quick_dialog (&Quick_input) != B_CANCEL) {
1143 replace_scanf = treplace_scanf;
1144 replace_backwards = treplace_backwards;
1145 replace_regexp = treplace_regexp;
1146 replace_all = treplace_all;
1147 replace_prompt = treplace_prompt;
1148 replace_whole = treplace_whole;
1149 replace_case = treplace_case;
1150 return;
1151 } else {
1152 *arg_order = NULL;
1153 *replace_text = NULL;
1154 *search_text = NULL;
1155 return;
1161 void edit_search_dialog (WEdit * edit, char **search_text)
1163 int treplace_scanf = replace_scanf;
1164 int treplace_regexp = replace_regexp;
1165 int treplace_whole = replace_whole;
1166 int treplace_case = replace_case;
1167 int treplace_backwards = replace_backwards;
1169 QuickWidget quick_widgets[] =
1171 {quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1172 0, NULL},
1173 {quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&Ok"), 0, B_ENTER, 0,
1174 0, NULL},
1175 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1176 0, 0, NULL },
1177 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1178 0, 0, NULL},
1179 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1180 0, 0, NULL},
1181 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1182 0, 0, NULL},
1183 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1184 0, 0, NULL},
1185 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, 0,
1186 0, "edit-search"},
1187 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1188 0, 0},
1189 {0}};
1191 quick_widgets[2].result = &treplace_scanf;
1192 quick_widgets[3].result = &treplace_backwards;
1193 quick_widgets[4].result = &treplace_regexp;
1194 quick_widgets[5].result = &treplace_whole;
1195 quick_widgets[6].result = &treplace_case;
1196 quick_widgets[7].str_result = search_text;
1197 quick_widgets[7].text = *search_text;
1200 QuickDialog Quick_input =
1201 {SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_(" Search "),
1202 "[Input Line Keys]", 0 /*quick_widgets */ };
1204 Quick_input.widgets = quick_widgets;
1206 if (quick_dialog (&Quick_input) != B_CANCEL) {
1207 replace_scanf = treplace_scanf;
1208 replace_backwards = treplace_backwards;
1209 replace_regexp = treplace_regexp;
1210 replace_whole = treplace_whole;
1211 replace_case = treplace_case;
1212 } else {
1213 *search_text = NULL;
1219 static long sargs[NUM_REPL_ARGS][256 / sizeof (long)];
1221 #define SCANF_ARGS sargs[0], sargs[1], sargs[2], sargs[3], \
1222 sargs[4], sargs[5], sargs[6], sargs[7], \
1223 sargs[8], sargs[9], sargs[10], sargs[11], \
1224 sargs[12], sargs[13], sargs[14], sargs[15]
1226 #define PRINTF_ARGS sargs[argord[0]], sargs[argord[1]], sargs[argord[2]], sargs[argord[3]], \
1227 sargs[argord[4]], sargs[argord[5]], sargs[argord[6]], sargs[argord[7]], \
1228 sargs[argord[8]], sargs[argord[9]], sargs[argord[10]], sargs[argord[11]], \
1229 sargs[argord[12]], sargs[argord[13]], sargs[argord[14]], sargs[argord[15]]
1232 /* This function is a modification of mc-3.2.10/src/view.c:regexp_view_search() */
1233 /* returns -3 on error in pattern, -1 on not found, found_len = 0 if either */
1234 int string_regexp_search (char *pattern, char *string, int len, int match_type, int match_bol, int icase, int *found_len, void *d)
1236 static regex_t r;
1237 static char *old_pattern = NULL;
1238 static int old_type, old_icase;
1239 regmatch_t *pmatch;
1240 static regmatch_t s[1];
1242 pmatch = (regmatch_t *) d;
1243 if (!pmatch)
1244 pmatch = s;
1246 if (!old_pattern || strcmp (old_pattern, pattern) || old_type != match_type || old_icase != icase) {
1247 if (old_pattern) {
1248 regfree (&r);
1249 free (old_pattern);
1250 old_pattern = 0;
1252 if (regcomp (&r, pattern, REG_EXTENDED | (icase ? REG_ICASE : 0))) {
1253 *found_len = 0;
1254 return -3;
1256 old_pattern = (char *) strdup (pattern);
1257 old_type = match_type;
1258 old_icase = icase;
1260 if (regexec (&r, string, d ? NUM_REPL_ARGS : 1, pmatch, ((match_bol || match_type != match_normal) ? 0 : REG_NOTBOL)) != 0) {
1261 *found_len = 0;
1262 return -1;
1264 *found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1265 return (pmatch[0].rm_so);
1268 /* thanks to Liviu Daia <daia@stoilow.imar.ro> for getting this
1269 (and the above) routines to work properly - paul */
1271 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)
1273 long p, q = 0;
1274 long l = strlen ((char *) exp), f = 0;
1275 int n = 0;
1277 for (p = 0; p < l; p++) /* count conversions... */
1278 if (exp[p] == '%')
1279 if (exp[++p] != '%') /* ...except for "%%" */
1280 n++;
1282 if (replace_scanf || replace_regexp) {
1283 int c;
1284 unsigned char *buf;
1285 unsigned char mbuf[MAX_REPL_LEN * 2 + 3];
1287 replace_scanf = (!replace_regexp); /* can't have both */
1289 buf = mbuf;
1291 if (replace_scanf) {
1292 unsigned char e[MAX_REPL_LEN];
1293 if (n >= NUM_REPL_ARGS)
1294 return -3;
1296 if (replace_case) {
1297 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++)
1298 buf[p - start] = (*get_byte) (data, p);
1299 } else {
1300 for (p = 0; exp[p] != 0; p++)
1301 exp[p] = my_lower_case (exp[p]);
1302 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++) {
1303 c = (*get_byte) (data, p);
1304 buf[p - start] = my_lower_case (c);
1308 buf[(q = p - start)] = 0;
1309 strcpy ((char *) e, (char *) exp);
1310 strcat ((char *) e, "%n");
1311 exp = e;
1313 while (q) {
1314 *((int *) sargs[n]) = 0; /* --> here was the problem - now fixed: good */
1315 if (n == sscanf ((char *) buf, (char *) exp, SCANF_ARGS)) {
1316 if (*((int *) sargs[n])) {
1317 *len = *((int *) sargs[n]);
1318 return start;
1321 if (once_only)
1322 return -2;
1323 if (q + start < last_byte) {
1324 if (replace_case) {
1325 buf[q] = (*get_byte) (data, q + start);
1326 } else {
1327 c = (*get_byte) (data, q + start);
1328 buf[q] = my_lower_case (c);
1330 q++;
1332 buf[q] = 0;
1333 start++;
1334 buf++; /* move the window along */
1335 if (buf == mbuf + MAX_REPL_LEN) { /* the window is about to go past the end of array, so... */
1336 memmove (mbuf, buf, strlen ((char *) buf) + 1); /* reset it */
1337 buf = mbuf;
1339 q--;
1341 } else { /* regexp matching */
1342 long offset = 0;
1343 int found_start, match_bol, move_win = 0;
1345 while (start + offset < last_byte) {
1346 match_bol = (offset == 0 || (*get_byte) (data, start + offset - 1) == '\n');
1347 if (!move_win) {
1348 p = start + offset;
1349 q = 0;
1351 for (; p < last_byte && q < MAX_REPL_LEN; p++, q++) {
1352 mbuf[q] = (*get_byte) (data, p);
1353 if (mbuf[q] == '\n')
1354 break;
1356 q++;
1357 offset += q;
1358 mbuf[q] = 0;
1360 buf = mbuf;
1361 while (q) {
1362 found_start = string_regexp_search ((char *) exp, (char *) buf, q, match_normal, match_bol, !replace_case, len, d);
1364 if (found_start <= -2) { /* regcomp/regexec error */
1365 *len = 0;
1366 return -3;
1368 else if (found_start == -1) /* not found: try next line */
1369 break;
1370 else if (*len == 0) { /* null pattern: try again at next character */
1371 q--;
1372 buf++;
1373 match_bol = 0;
1374 continue;
1376 else /* found */
1377 return (start + offset - q + found_start);
1379 if (once_only)
1380 return -2;
1382 if (buf[q - 1] != '\n') { /* incomplete line: try to recover */
1383 buf = mbuf + MAX_REPL_LEN / 2;
1384 q = strlen ((char *) buf);
1385 memmove (mbuf, buf, q);
1386 p = start + q;
1387 move_win = 1;
1389 else
1390 move_win = 0;
1393 } else {
1394 *len = strlen ((char *) exp);
1395 if (replace_case) {
1396 for (p = start; p <= last_byte - l; p++) {
1397 if ((*get_byte) (data, p) == (unsigned char)exp[0]) { /* check if first char matches */
1398 for (f = 0, q = 0; q < l && f < 1; q++)
1399 if ((*get_byte) (data, q + p) != (unsigned char)exp[q])
1400 f = 1;
1401 if (f == 0)
1402 return p;
1404 if (once_only)
1405 return -2;
1407 } else {
1408 for (p = 0; exp[p] != 0; p++)
1409 exp[p] = my_lower_case (exp[p]);
1411 for (p = start; p <= last_byte - l; p++) {
1412 if (my_lower_case ((*get_byte) (data, p)) == (unsigned char)exp[0]) {
1413 for (f = 0, q = 0; q < l && f < 1; q++)
1414 if (my_lower_case ((*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;
1424 return -2;
1428 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)
1429 { /*front end to find_string to check for
1430 whole words */
1431 long p;
1432 p = search_start;
1434 while ((p = edit_find_string (p, exp, len, last_byte, get_byte, data, once_only, d)) >= 0) {
1435 if (replace_whole) {
1436 /*If the bordering chars are not in option_whole_chars_search then word is whole */
1437 if (!strcasechr (option_whole_chars_search, (*get_byte) (data, p - 1))
1438 && !strcasechr (option_whole_chars_search, (*get_byte) (data, p + *len)))
1439 return p;
1440 if (once_only)
1441 return -2;
1442 } else
1443 return p;
1444 if (once_only)
1445 break;
1446 p++; /*not a whole word so continue search. */
1448 return p;
1451 long edit_find (long search_start, unsigned char *exp, int *len, long last_byte, int (*get_byte) (void *, long), void *data, void *d)
1453 long p;
1454 if (replace_backwards) {
1455 while (search_start >= 0) {
1456 p = edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 1, d);
1457 if (p == search_start)
1458 return p;
1459 search_start--;
1461 } else {
1462 return edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 0, d);
1464 return -2;
1467 #define is_digit(x) ((x) >= '0' && (x) <= '9')
1469 #define snprintf(v) { \
1470 *p1++ = *p++; \
1471 *p1++ = '%'; \
1472 *p1++ = 'n'; \
1473 *p1 = '\0'; \
1474 sprintf(s,q1,v,&n); \
1475 s += n; \
1478 /* this function uses the sprintf command to do a vprintf */
1479 /* it takes pointers to arguments instead of the arguments themselves */
1480 static int sprintf_p (char *str, const char *fmt,...)
1481 __attribute__ ((format (printf, 2, 3)));
1483 static int sprintf_p (char *str, const char *fmt,...)
1485 va_list ap;
1486 int n;
1487 char *q, *p, *s = str;
1488 char q1[32];
1489 char *p1;
1491 va_start (ap, fmt);
1492 p = q = (char *) fmt;
1494 while ((p = strchr (p, '%'))) {
1495 n = p - q;
1496 strncpy (s, q, n); /* copy stuff between format specifiers */
1497 s += n;
1498 *s = 0;
1499 q = p;
1500 p1 = q1;
1501 *p1++ = *p++;
1502 if (*p == '%') {
1503 p++;
1504 *s++ = '%';
1505 q = p;
1506 continue;
1508 if (*p == 'n') {
1509 p++;
1510 /* do nothing */
1511 q = p;
1512 continue;
1514 if (*p == '#')
1515 *p1++ = *p++;
1516 if (*p == '0')
1517 *p1++ = *p++;
1518 if (*p == '-')
1519 *p1++ = *p++;
1520 if (*p == '+')
1521 *p1++ = *p++;
1522 if (*p == '*') {
1523 p++;
1524 strcpy (p1, itoa (*va_arg (ap, int *))); /* replace field width with a number */
1525 p1 += strlen (p1);
1526 } else {
1527 while (is_digit (*p))
1528 *p1++ = *p++;
1530 if (*p == '.')
1531 *p1++ = *p++;
1532 if (*p == '*') {
1533 p++;
1534 strcpy (p1, itoa (*va_arg (ap, int *))); /* replace precision with a number */
1535 p1 += strlen (p1);
1536 } else {
1537 while (is_digit (*p))
1538 *p1++ = *p++;
1540 /* flags done, now get argument */
1541 if (*p == 's') {
1542 snprintf (va_arg (ap, char *));
1543 } else if (*p == 'h') {
1544 if (strchr ("diouxX", *p))
1545 snprintf (*va_arg (ap, short *));
1546 } else if (*p == 'l') {
1547 *p1++ = *p++;
1548 if (strchr ("diouxX", *p))
1549 snprintf (*va_arg (ap, long *));
1550 } else if (strchr ("cdiouxX", *p)) {
1551 snprintf (*va_arg (ap, int *));
1552 } else if (*p == 'L') {
1553 *p1++ = *p++;
1554 if (strchr ("EefgG", *p))
1555 snprintf (*va_arg (ap, double *)); /* should be long double */
1556 } else if (strchr ("EefgG", *p)) {
1557 snprintf (*va_arg (ap, double *));
1558 } else if (strchr ("DOU", *p)) {
1559 snprintf (*va_arg (ap, long *));
1560 } else if (*p == 'p') {
1561 snprintf (*va_arg (ap, void **));
1563 q = p;
1565 va_end (ap);
1566 sprintf (s, q); /* print trailing leftover */
1567 return s - str + strlen (s);
1570 static void regexp_error (WEdit *edit)
1572 /* "Error: Syntax error in regular expression, or scanf expression contained too many %'s */
1573 edit_error_dialog (_(" Error "), _(" Invalid regular expression, or scanf expression with to many conversions "));
1576 /* call with edit = 0 before shutdown to close memory leaks */
1577 void edit_replace_cmd (WEdit * edit, int again)
1579 static regmatch_t pmatch[NUM_REPL_ARGS];
1580 static char *old1 = NULL;
1581 static char *old2 = NULL;
1582 static char *old3 = NULL;
1583 char *exp1 = "";
1584 char *exp2 = "";
1585 char *exp3 = "";
1586 int replace_yes;
1587 int replace_continue;
1588 int treplace_prompt = 0;
1589 int i = 0;
1590 long times_replaced = 0, last_search;
1591 int argord[NUM_REPL_ARGS];
1593 if (!edit) {
1594 if (old1) {
1595 g_free (old1);
1596 old1 = 0;
1598 if (old2) {
1599 g_free (old2);
1600 old2 = 0;
1602 if (old3) {
1603 g_free (old3);
1604 old3 = 0;
1606 return;
1608 last_search = edit->last_byte;
1610 edit->force |= REDRAW_COMPLETELY;
1612 exp1 = old1 ? old1 : exp1;
1613 exp2 = old2 ? old2 : exp2;
1614 exp3 = old3 ? old3 : exp3;
1616 if (again) {
1617 if (!old1 || !old2)
1618 return;
1619 exp1 = g_strdup (old1);
1620 exp2 = g_strdup (old2);
1621 exp3 = g_strdup (old3);
1622 } else {
1623 edit_push_action (edit, KEY_PRESS + edit->start_display);
1625 #ifdef HAVE_CHARSET
1626 if (exp1 && *exp1)
1627 convert_to_display (exp1);
1628 if (exp2 && *exp2)
1629 convert_to_display (exp2);
1630 #endif /* HAVE_CHARSET */
1632 edit_replace_dialog (edit, &exp1, &exp2, &exp3);
1634 #ifdef HAVE_CHARSET
1635 if (exp1 && *exp1)
1636 convert_from_input (exp1);
1637 if (exp2 && *exp2)
1638 convert_from_input (exp2);
1639 #endif /* HAVE_CHARSET */
1641 treplace_prompt = replace_prompt;
1644 if (!exp1 || !*exp1) {
1645 edit->force = REDRAW_COMPLETELY;
1646 if (exp1)
1647 g_free (exp1);
1648 if (exp2)
1649 g_free (exp2);
1650 if (exp3)
1651 g_free (exp3);
1652 return;
1654 if (old1)
1655 g_free (old1);
1656 if (old2)
1657 g_free (old2);
1658 if (old3)
1659 g_free (old3);
1660 old1 = g_strdup (exp1);
1661 old2 = g_strdup (exp2);
1662 old3 = g_strdup (exp3);
1665 char *s;
1666 int ord;
1667 while ((s = strchr (exp3, ' ')))
1668 memmove (s, s + 1, strlen (s));
1669 s = exp3;
1670 for (i = 0; i < NUM_REPL_ARGS; i++) {
1671 if (s != (char *)1 && *s) {
1672 ord = atoi (s);
1673 if ((ord > 0) && (ord < NUM_REPL_ARGS))
1674 argord[i] = ord - 1;
1675 else
1676 argord[i] = i;
1677 s = strchr (s, ',') + 1;
1678 } else
1679 argord[i] = i;
1683 replace_continue = replace_all;
1685 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
1686 edit->search_start--;
1688 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
1689 edit->search_start++;
1691 do {
1692 int len = 0;
1693 long new_start;
1694 new_start = edit_find (edit->search_start, (unsigned char *) exp1, &len, last_search,
1695 (int (*)(void *, long)) edit_get_byte, (void *) edit, pmatch);
1696 if (new_start == -3) {
1697 regexp_error (edit);
1698 break;
1700 edit->search_start = new_start;
1701 /*returns negative on not found or error in pattern */
1703 if (edit->search_start >= 0) {
1704 edit->found_start = edit->search_start;
1705 i = edit->found_len = len;
1707 edit_cursor_move (edit, edit->search_start - edit->curs1);
1708 edit_scroll_screen_over_cursor (edit);
1710 replace_yes = 1;
1712 if (treplace_prompt) {
1713 int l;
1714 l = edit->curs_row - edit->num_widget_lines / 3;
1715 if (l > 0)
1716 edit_scroll_downward (edit, l);
1717 if (l < 0)
1718 edit_scroll_upward (edit, -l);
1720 edit_scroll_screen_over_cursor (edit);
1721 edit->force |= REDRAW_PAGE;
1722 edit_render_keypress (edit);
1724 /*so that undo stops at each query */
1725 edit_push_key_press (edit);
1727 switch (edit_replace_prompt (edit, exp2, /* and prompt 2/3 down */
1728 (edit->num_widget_columns - CONFIRM_DLG_WIDTH)/2,
1729 edit->num_widget_lines * 2 / 3)) {
1730 case B_ENTER:
1731 break;
1732 case B_SKIP_REPLACE:
1733 replace_yes = 0;
1734 break;
1735 case B_REPLACE_ALL:
1736 treplace_prompt = 0;
1737 replace_continue = 1;
1738 break;
1739 case B_REPLACE_ONE:
1740 replace_continue = 0;
1741 break;
1742 case B_CANCEL:
1743 replace_yes = 0;
1744 replace_continue = 0;
1745 break;
1748 if (replace_yes) { /* delete then insert new */
1749 if (replace_scanf || replace_regexp) {
1750 char repl_str[MAX_REPL_LEN + 2];
1751 if (replace_regexp) { /* we need to fill in sargs just like with scanf */
1752 int k, j;
1753 for (k = 1; k < NUM_REPL_ARGS && pmatch[k].rm_eo >= 0; k++) {
1754 unsigned char *t;
1755 t = (unsigned char *) &sargs[k - 1][0];
1756 for (j = 0; j < pmatch[k].rm_eo - pmatch[k].rm_so && j < 255; j++, t++)
1757 *t = (unsigned char) edit_get_byte (edit, edit->search_start - pmatch[0].rm_so + pmatch[k].rm_so + j);
1758 *t = '\0';
1760 for (; k <= NUM_REPL_ARGS; k++)
1761 sargs[k - 1][0] = 0;
1763 if (sprintf_p (repl_str, exp2, PRINTF_ARGS) >= 0) {
1764 times_replaced++;
1765 while (i--)
1766 edit_delete (edit);
1767 while (repl_str[++i])
1768 edit_insert (edit, repl_str[i]);
1769 } else {
1770 edit_error_dialog (_ (" Replace "),
1771 /* "Invalid regexp string or scanf string" */
1772 _ (" Error in replacement format string. "));
1773 replace_continue = 0;
1775 } else {
1776 times_replaced++;
1777 while (i--)
1778 edit_delete (edit);
1779 while (exp2[++i])
1780 edit_insert (edit, exp2[i]);
1782 edit->found_len = i;
1784 /* so that we don't find the same string again */
1785 if (replace_backwards) {
1786 last_search = edit->search_start;
1787 edit->search_start--;
1788 } else {
1789 edit->search_start += i;
1790 last_search = edit->last_byte;
1792 edit_scroll_screen_over_cursor (edit);
1793 } else {
1794 edit->search_start = edit->curs1; /* try and find from right here for next search */
1795 edit_update_curs_col (edit);
1797 edit->force |= REDRAW_PAGE;
1798 edit_render_keypress (edit);
1799 if (times_replaced) {
1800 message (0, _(" Replace "), _(" %ld replacements made. "), times_replaced);
1801 } else
1802 edit_message_dialog (_ (" Replace "), _ (" Search string not found "));
1803 replace_continue = 0;
1805 } while (replace_continue);
1807 g_free (exp1);
1808 g_free (exp2);
1809 g_free (exp3);
1810 edit->force = REDRAW_COMPLETELY;
1811 edit_scroll_screen_over_cursor (edit);
1817 void edit_search_cmd (WEdit * edit, int again)
1819 static char *old = NULL;
1820 char *exp = "";
1822 if (!edit) {
1823 if (old) {
1824 g_free (old);
1825 old = 0;
1827 return;
1829 exp = old ? old : exp;
1830 if (again) { /*ctrl-hotkey for search again. */
1831 if (!old)
1832 return;
1833 exp = (char *) g_strdup (old);
1834 } else {
1836 #ifdef HAVE_CHARSET
1837 if (exp && *exp)
1838 convert_to_display (exp);
1839 #endif /* HAVE_CHARSET */
1841 edit_search_dialog (edit, &exp);
1843 #ifdef HAVE_CHARSET
1844 if (exp && *exp)
1845 convert_from_input (exp);
1846 #endif /* HAVE_CHARSET */
1848 edit_push_action (edit, KEY_PRESS + edit->start_display);
1851 if (exp) {
1852 if (*exp) {
1853 int len = 0;
1854 if (old)
1855 g_free (old);
1856 old = (char *) g_strdup (exp);
1858 if (search_create_bookmark) {
1859 int found = 0, books = 0;
1860 int l = 0, l_last = -1;
1861 long p, q = 0;
1862 for (;;) {
1863 p = edit_find (q, (unsigned char *) exp, &len, edit->last_byte,
1864 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
1865 if (p < 0)
1866 break;
1867 found++;
1868 l += edit_count_lines (edit, q, p);
1869 if (l != l_last) {
1870 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
1871 books++;
1873 l_last = l;
1874 q = p + 1;
1876 if (found) {
1877 /* in response to number of bookmarks added because of string being found %d times */
1878 message (0, _(" Search "), _(" %d finds made, %d bookmarks added "), found, books);
1879 } else {
1880 edit_error_dialog (_ (" Search "), _ (" Search string not found "));
1882 } else {
1884 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
1885 edit->search_start--;
1887 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
1888 edit->search_start++;
1890 edit->search_start = edit_find (edit->search_start, (unsigned char *) exp, &len, edit->last_byte,
1891 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
1893 if (edit->search_start >= 0) {
1894 edit->found_start = edit->search_start;
1895 edit->found_len = len;
1897 edit_cursor_move (edit, edit->search_start - edit->curs1);
1898 edit_scroll_screen_over_cursor (edit);
1899 if (replace_backwards)
1900 edit->search_start--;
1901 else
1902 edit->search_start++;
1903 } else if (edit->search_start == -3) {
1904 edit->search_start = edit->curs1;
1905 regexp_error (edit);
1906 } else {
1907 edit->search_start = edit->curs1;
1908 edit_error_dialog (_ (" Search "), _ (" Search string not found "));
1912 g_free (exp);
1914 edit->force |= REDRAW_COMPLETELY;
1915 edit_scroll_screen_over_cursor (edit);
1919 /* Real edit only */
1920 void edit_quit_cmd (WEdit * edit)
1922 edit_push_action (edit, KEY_PRESS + edit->start_display);
1924 edit->force |= REDRAW_COMPLETELY;
1925 if (edit->modified) {
1926 switch (edit_query_dialog3 (_ (" Quit "), _ (" File was modified, Save with exit? "), _ ("Cancel quit"), _ ("&Yes"), _ ("&No"))) {
1927 case 1:
1928 edit_push_markers (edit);
1929 edit_set_markers (edit, 0, 0, 0, 0);
1930 if (!edit_save_cmd (edit))
1931 return;
1932 break;
1933 case 2:
1934 if (edit->delete_file)
1935 unlink (catstrs (edit->dir, edit->filename, 0));
1936 break;
1937 case 0:
1938 case -1:
1939 return;
1942 else if (edit->delete_file)
1943 unlink (catstrs (edit->dir, edit->filename, 0));
1944 dlg_stop (edit->widget.parent);
1947 #define TEMP_BUF_LEN 1024
1949 /* returns a null terminated length of text. Result must be free'd */
1950 unsigned char *edit_get_block (WEdit * edit, long start, long finish, int *l)
1952 unsigned char *s, *r;
1953 r = s = malloc (finish - start + 1);
1954 if (column_highlighting) {
1955 *l = 0;
1956 while (start < finish) { /* copy from buffer, excluding chars that are out of the column 'margins' */
1957 int c, x;
1958 x = edit_move_forward3 (edit, edit_bol (edit, start), 0, start);
1959 c = edit_get_byte (edit, start);
1960 if ((x >= edit->column1 && x < edit->column2)
1961 || (x >= edit->column2 && x < edit->column1) || c == '\n') {
1962 *s++ = c;
1963 (*l)++;
1965 start++;
1967 } else {
1968 *l = finish - start;
1969 while (start < finish)
1970 *s++ = edit_get_byte (edit, start++);
1972 *s = 0;
1973 return r;
1976 /* save block, returns 1 on success */
1977 int edit_save_block (WEdit * edit, const char *filename, long start, long finish)
1979 int len, file;
1981 if ((file = mc_open ((char *) filename, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
1982 return 0;
1984 if (column_highlighting) {
1985 unsigned char *block, *p;
1986 int r;
1987 p = block = edit_get_block (edit, start, finish, &len);
1988 while (len) {
1989 r = mc_write (file, p, len);
1990 if (r < 0)
1991 break;
1992 p += r;
1993 len -= r;
1995 free (block);
1996 } else {
1997 unsigned char *buf;
1998 int i = start, end;
1999 len = finish - start;
2000 buf = malloc (TEMP_BUF_LEN);
2001 while (start != finish) {
2002 end = min (finish, start + TEMP_BUF_LEN);
2003 for (; i < end; i++)
2004 buf[i - start] = edit_get_byte (edit, i);
2005 len -= mc_write (file, (char *) buf, end - start);
2006 start = end;
2008 free (buf);
2010 mc_close (file);
2011 if (len)
2012 return 0;
2013 return 1;
2016 /* copies a block to clipboard file */
2017 static int edit_save_block_to_clip_file (WEdit * edit, long start, long finish)
2019 return edit_save_block (edit, catstrs (home_dir, CLIP_FILE, 0), start, finish);
2023 void edit_paste_from_history (WEdit *edit)
2027 int edit_copy_to_X_buf_cmd (WEdit * edit)
2029 long start_mark, end_mark;
2030 if (eval_marks (edit, &start_mark, &end_mark))
2031 return 0;
2032 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2033 edit_error_dialog (_(" Copy to clipboard "), get_sys_error (_(" Unable to save to file. ")));
2034 return 1;
2036 edit_mark_cmd (edit, 1);
2037 return 0;
2040 int edit_cut_to_X_buf_cmd (WEdit * edit)
2042 long start_mark, end_mark;
2043 if (eval_marks (edit, &start_mark, &end_mark))
2044 return 0;
2045 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2046 edit_error_dialog (_(" Cut to clipboard "), _(" Unable to save to file. "));
2047 return 1;
2049 edit_block_delete_cmd (edit);
2050 edit_mark_cmd (edit, 1);
2051 return 0;
2054 void edit_paste_from_X_buf_cmd (WEdit * edit)
2056 edit_insert_file (edit, catstrs (home_dir, CLIP_FILE, 0));
2060 void edit_goto_cmd (WEdit *edit)
2062 char *f;
2063 static int l = 0;
2064 char s[12];
2065 sprintf (s, "%d", l);
2066 f = input_dialog (_(" Goto line "), _(" Enter line: "), l ? s : "");
2067 if (f) {
2068 if (*f) {
2069 l = atoi (f);
2070 edit_move_display (edit, l - edit->num_widget_lines / 2 - 1);
2071 edit_move_to_line (edit, l - 1);
2072 edit->force |= REDRAW_COMPLETELY;
2074 g_free (f);
2078 /*returns 1 on success */
2079 int edit_save_block_cmd (WEdit * edit)
2081 long start_mark, end_mark;
2082 char *exp;
2083 if (eval_marks (edit, &start_mark, &end_mark))
2084 return 1;
2085 exp = edit_get_save_file (edit->dir, catstrs (home_dir, CLIP_FILE, 0), _ (" Save Block "));
2086 edit_push_action (edit, KEY_PRESS + edit->start_display);
2087 if (exp) {
2088 if (!*exp) {
2089 g_free (exp);
2090 return 0;
2091 } else {
2092 if (edit_save_block (edit, exp, start_mark, end_mark)) {
2093 g_free (exp);
2094 edit->force |= REDRAW_COMPLETELY;
2095 return 1;
2096 } else {
2097 g_free (exp);
2098 edit_error_dialog (_ (" Save Block "), get_sys_error (_ (" Error trying to save file. ")));
2102 edit->force |= REDRAW_COMPLETELY;
2103 return 0;
2107 /* returns 1 on success */
2108 int edit_insert_file_cmd (WEdit * edit)
2110 char *exp = edit_get_load_file (edit->dir, catstrs (home_dir, CLIP_FILE, 0), _ (" Insert File "));
2111 edit_push_action (edit, KEY_PRESS + edit->start_display);
2112 if (exp) {
2113 if (!*exp) {
2114 g_free (exp);
2115 return 0;
2116 } else {
2117 if (edit_insert_file (edit, exp)) {
2118 g_free (exp);
2119 edit->force |= REDRAW_COMPLETELY;
2120 return 1;
2121 } else {
2122 g_free (exp);
2123 edit_error_dialog (_ (" Insert file "), get_sys_error (_ (" Error trying to insert file. ")));
2127 edit->force |= REDRAW_COMPLETELY;
2128 return 0;
2131 /* sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
2132 int edit_sort_cmd (WEdit * edit)
2134 static char *old = 0;
2135 char *exp;
2136 long start_mark, end_mark;
2137 int e;
2139 if (eval_marks (edit, &start_mark, &end_mark)) {
2140 edit_error_dialog (_(" Sort block "), _(" You must first highlight a block of text. "));
2141 return 0;
2143 edit_save_block (edit, catstrs (home_dir, BLOCK_FILE, 0), start_mark, end_mark);
2145 exp = old ? old : "";
2147 exp = input_dialog (_(" Run Sort "),
2148 _(" Enter sort options (see manpage) separated by whitespace: "), exp);
2150 if (!exp)
2151 return 1;
2152 if (old)
2153 g_free (old);
2154 old = exp;
2156 e = system (catstrs (" sort ", exp, " ", home_dir, BLOCK_FILE, " > ", home_dir, TEMP_FILE, 0));
2157 if (e) {
2158 if (e == -1 || e == 127) {
2159 edit_error_dialog (_(" Sort "),
2160 get_sys_error (_(" Error trying to execute sort command ")));
2161 } else {
2162 char q[8];
2163 sprintf (q, "%d ", e);
2164 edit_error_dialog (_(" Sort "),
2165 catstrs (_(" Sort returned non-zero: "), q, 0));
2167 return -1;
2170 edit->force |= REDRAW_COMPLETELY;
2172 if (edit_block_delete_cmd (edit))
2173 return 1;
2174 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, 0));
2175 return 0;
2178 /* if block is 1, a block must be highlighted and the shell command
2179 processes it. If block is 0 the shell command is a straight system
2180 command, that just produces some output which is to be inserted */
2181 void
2182 edit_block_process_cmd (WEdit * edit, const char *shell_cmd, int block)
2184 long start_mark, end_mark;
2185 char buf[BUFSIZ];
2186 FILE *script_home = NULL;
2187 FILE *script_src = NULL;
2188 FILE *block_file = NULL;
2189 char *o = NULL;
2190 char *h = NULL;
2191 char *b = NULL;
2193 o = catstrs (mc_home, shell_cmd, 0); /* original source script */
2194 h = catstrs (home_dir, EDIT_DIR, shell_cmd, 0); /* home script */
2195 b = catstrs (home_dir, BLOCK_FILE, 0); /* block file */
2197 if (!(script_home = fopen (h, "r"))) {
2198 if (!(script_home = fopen (h, "w"))) {
2199 edit_error_dialog ("", get_sys_error (catstrs
2201 ("Error creating script:"),
2202 h, 0)));
2203 return;
2205 if (!(script_src = fopen (o, "r"))) {
2206 fclose (script_home);
2207 unlink (h);
2208 edit_error_dialog ("", get_sys_error (catstrs
2209 (_("Error reading script:"),
2210 o, 0)));
2211 return;
2213 while (fgets (buf, sizeof (buf), script_src))
2214 fputs (buf, script_home);
2215 if (fclose (script_home)) {
2216 edit_error_dialog ("", get_sys_error (catstrs
2218 ("Error closing script:"),
2219 h, 0)));
2220 return;
2222 chmod (h, 0700);
2223 edit_error_dialog ("", get_sys_error (catstrs
2224 (_("Script created:"), h,
2225 0)));
2228 open_error_pipe ();
2230 if (block) { /* for marked block run indent formatter */
2231 if (eval_marks (edit, &start_mark, &end_mark)) {
2232 edit_error_dialog (_("Process block"),
2234 (" You must first highlight a block of text. "));
2235 return;
2237 edit_save_block (edit, b, start_mark, end_mark);
2240 * Run script.
2241 * Initial space is to avoid polluting bash history.
2242 * Arguments:
2243 * $1 - name of the edited file (to check its extention etc).
2244 * $2 - file containing the current block.
2245 * $3 - file where error messages should be put
2246 * (for compatibility with old scripts).
2248 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2249 edit->filename, " ", home_dir, BLOCK_FILE,
2250 " /dev/null", NULL));
2252 } else {
2254 * No block selected, just execute the command for the file.
2255 * Arguments:
2256 * $1 - name of the edited file.
2258 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2259 edit->filename, NULL));
2262 close_error_pipe (0, 0);
2264 edit_refresh_cmd (edit);
2265 edit->force |= REDRAW_COMPLETELY;
2267 /* insert result block */
2268 if (block) {
2269 if (edit_block_delete_cmd (edit))
2270 return;
2271 edit_insert_file (edit, b);
2272 if ((block_file = fopen (b, "w")))
2273 fclose (block_file);
2274 return;
2277 return;
2280 /* prints at the cursor */
2281 /* returns the number of chars printed */
2282 int edit_print_string (WEdit * e, const char *s)
2284 int i = 0;
2285 while (s[i])
2286 edit_execute_cmd (e, -1, (unsigned char) s[i++]);
2287 e->force |= REDRAW_COMPLETELY;
2288 edit_update_screen (e);
2289 return i;
2292 int edit_printf (WEdit * e, const char *fmt, ...)
2294 int i;
2295 va_list pa;
2296 char s[1024];
2297 va_start (pa, fmt);
2298 g_vsnprintf (s, sizeof (s), fmt, pa);
2299 i = edit_print_string (e, s);
2300 va_end (pa);
2301 return i;
2304 /* FIXME: does this function break NT_OS2 ? */
2306 static void pipe_mail (WEdit *edit, char *to, char *subject, char *cc)
2308 FILE *p = 0;
2309 char *s;
2311 s = g_strdup_printf ("mail -s \"%s\" -c \"%s\" \"%s\"", subject, cc, to);
2313 if (s) {
2314 p = popen (s, "w");
2315 g_free (s);
2318 if (p) {
2319 long i;
2320 for (i = 0; i < edit->last_byte; i++)
2321 fputc (edit_get_byte (edit, i), p);
2322 pclose (p);
2326 #define MAIL_DLG_HEIGHT 12
2328 void edit_mail_dialog (WEdit * edit)
2330 char *tmail_to;
2331 char *tmail_subject;
2332 char *tmail_cc;
2334 static char *mail_cc_last = 0;
2335 static char *mail_subject_last = 0;
2336 static char *mail_to_last = 0;
2338 QuickDialog Quick_input =
2339 {50, MAIL_DLG_HEIGHT, -1, 0, N_(" Mail "),
2340 "[Input Line Keys]", 0};
2342 QuickWidget quick_widgets[] =
2344 {quick_button, 6, 10, 9, MAIL_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
2345 0, NULL},
2346 {quick_button, 2, 10, 9, MAIL_DLG_HEIGHT, N_("&Ok"), 0, B_ENTER, 0,
2347 0, NULL},
2348 {quick_input, 3, 50, 8, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2349 0, "mail-dlg-input"},
2350 {quick_label, 2, 50, 7, MAIL_DLG_HEIGHT, N_(" Copies to"), 0, 0, 0,
2351 0, 0},
2352 {quick_input, 3, 50, 6, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2353 0, "mail-dlg-input-2"},
2354 {quick_label, 2, 50, 5, MAIL_DLG_HEIGHT, N_(" Subject"), 0, 0, 0,
2355 0, 0},
2356 {quick_input, 3, 50, 4, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2357 0, "mail-dlg-input-3"},
2358 {quick_label, 2, 50, 3, MAIL_DLG_HEIGHT, N_(" To"), 0, 0, 0,
2359 0, 0},
2360 {quick_label, 2, 50, 2, MAIL_DLG_HEIGHT, N_(" mail -s <subject> -c <cc> <to>"), 0, 0, 0,
2361 0, 0},
2362 {0}};
2364 quick_widgets[2].str_result = &tmail_cc;
2365 quick_widgets[2].text = mail_cc_last ? mail_cc_last : "";
2366 quick_widgets[4].str_result = &tmail_subject;
2367 quick_widgets[4].text = mail_subject_last ? mail_subject_last : "";
2368 quick_widgets[6].str_result = &tmail_to;
2369 quick_widgets[6].text = mail_to_last ? mail_to_last : "";
2371 Quick_input.widgets = quick_widgets;
2373 if (quick_dialog (&Quick_input) != B_CANCEL) {
2374 if (mail_cc_last)
2375 g_free (mail_cc_last);
2376 if (mail_subject_last)
2377 g_free (mail_subject_last);
2378 if (mail_to_last)
2379 g_free (mail_to_last);
2380 mail_cc_last = *(quick_widgets[2].str_result);
2381 mail_subject_last = *(quick_widgets[4].str_result);
2382 mail_to_last = *(quick_widgets[6].str_result);
2383 pipe_mail (edit, mail_to_last, mail_subject_last, mail_cc_last);
2388 /*******************/
2389 /* Word Completion */
2390 /*******************/
2393 /* find first character of current word */
2394 static int edit_find_word_start (WEdit *edit, long *word_start, int *word_len)
2396 int i, c, last;
2398 /* return if at begin of file */
2399 if (edit->curs1 <= 0)
2400 return 0;
2402 c = (unsigned char) edit_get_byte (edit, edit->curs1 - 1);
2403 /* return if not at end or in word */
2404 if (isspace (c) || !(isalnum (c) || c == '_'))
2405 return 0;
2407 /* search start of word to be completed */
2408 for (i = 2;; i++) {
2409 /* return if at begin of file */
2410 if (edit->curs1 - i < 0)
2411 return 0;
2413 last = c;
2414 c = (unsigned char) edit_get_byte (edit, edit->curs1 - i);
2416 if (!(isalnum (c) || c == '_')) {
2417 /* return if word starts with digit */
2418 if (isdigit (last))
2419 return 0;
2421 *word_start = edit->curs1 - (i - 1); /* start found */
2422 *word_len = i - 1;
2423 break;
2426 /* success */
2427 return 1;
2431 /* (re)set search parameters to the given values */
2432 static void edit_set_search_parameters (int rs, int rb, int rr, int rw, int rc)
2434 replace_scanf = rs;
2435 replace_backwards = rb;
2436 replace_regexp = rr;
2437 replace_whole = rw;
2438 replace_case = rc;
2442 const static int MAX_WORD_COMPLETIONS = 100; /* in listbox */
2445 /* collect the possible completions */
2446 static int edit_collect_completions (WEdit *edit, long start,
2447 int word_len, char *match_expr, struct selection *compl, int *num)
2449 int len, max_len = 0, i, skip;
2450 char *bufpos;
2452 /* collect max MAX_WORD_COMPLETIONS completions */
2453 while (*num < MAX_WORD_COMPLETIONS) {
2454 /* get next match */
2455 start = edit_find (start - 1, (unsigned char *) match_expr, &len,
2456 edit->last_byte, (int (*)(void *, long)) edit_get_byte,
2457 (void *) edit, 0);
2459 /* not matched */
2460 if (start < 0)
2461 break;
2463 /* add matched completion if not yet added */
2464 bufpos = &edit->buffers1[start >> S_EDIT_BUF_SIZE][start & M_EDIT_BUF_SIZE];
2465 skip = 0;
2466 for (i = 0; i < *num; i++) {
2467 if (strncmp (&compl[i].text[word_len], &bufpos[word_len],
2468 max (len, compl[i].len) - word_len) == 0) {
2469 skip = 1;
2470 break; /* skip it, already added */
2473 if (skip)
2474 continue;
2476 compl[*num].text = CMalloc (len + 1);
2477 compl[*num].len = len;
2478 for (i = 0; i < len; i++)
2479 compl[*num].text[i] = *(bufpos + i);
2480 compl[*num].text[i] = '\0';
2481 (*num)++;
2483 /* note the maximal length needed for the completion dialog */
2484 if (len > max_len)
2485 max_len = len;
2487 return max_len;
2491 static int compllist_callback (void *data)
2493 return 0;
2497 /* let the user select its preferred completion */
2498 void edit_completion_dialog (WEdit *edit, int max_len, int word_len,
2499 struct selection *compl, int num_compl)
2501 int start_x, start_y, offset, i;
2502 char *curr = NULL;
2503 Dlg_head *compl_dlg;
2504 WListbox *compl_list;
2505 unsigned int compl_dlg_h; /* completion dialog height */
2506 unsigned int compl_dlg_w; /* completion dialog width */
2508 /* calculate the dialog metrics */
2509 compl_dlg_h = num_compl + 2;
2510 compl_dlg_w = max_len + 4;
2511 start_x = edit->curs_col + edit->start_col - (compl_dlg_w / 2);
2512 start_y = edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + 1;
2514 if (start_x < 0)
2515 start_x = 0;
2516 if (compl_dlg_w > COLS)
2517 compl_dlg_w = COLS;
2518 if (compl_dlg_h > LINES - 2)
2519 compl_dlg_h = LINES - 2;
2521 offset = start_x + compl_dlg_w - COLS;
2522 if (offset > 0)
2523 start_x -= offset;
2524 offset = start_y + compl_dlg_h - LINES;
2525 if (offset > 0)
2526 start_y -= (offset + 1);
2528 /* create the dialog */
2529 compl_dlg = create_dlg (start_y, start_x, compl_dlg_h, compl_dlg_w,
2530 dialog_colors, NULL, "[Completion]", NULL,
2531 DLG_COMPACT);
2533 /* create the listbox */
2534 compl_list = listbox_new (1, 1, compl_dlg_w - 2, compl_dlg_h - 2, 0,
2535 compllist_callback, NULL);
2537 /* add the dialog */
2538 add_widget (compl_dlg, compl_list);
2540 /* fill the listbox with the completions */
2541 for (i = 0; i < num_compl; i++)
2542 listbox_add_item (compl_list, 0, 0, compl[i].text, NULL);
2544 /* pop up the dialog */
2545 run_dlg (compl_dlg);
2547 /* apply the choosen completion */
2548 if (compl_dlg->ret_value == B_ENTER) {
2549 listbox_get_current (compl_list, &curr, NULL);
2550 if (curr)
2551 for (curr += word_len; *curr; curr++)
2552 edit_insert (edit, *curr);
2555 /* destroy dialog before return */
2556 destroy_dlg (compl_dlg);
2560 /* complete current word using regular expression search */
2561 /* backwards beginning at current cursor position */
2562 void edit_complete_word_cmd (WEdit *edit)
2564 int word_len = 0, i, num_compl = 0, max_len;
2565 long word_start = 0;
2566 char *bufpos;
2567 char match_expr[MAX_REPL_LEN];
2568 struct selection compl[MAX_WORD_COMPLETIONS]; /* completions */
2570 /* don't want to disturb another search */
2571 int old_rs = replace_scanf;
2572 int old_rb = replace_backwards;
2573 int old_rr = replace_regexp;
2574 int old_rw = replace_whole;
2575 int old_rc = replace_case;
2577 /* search start of word to be completed */
2578 if (!edit_find_word_start (edit, &word_start, &word_len))
2579 return;
2581 /* prepare match expression */
2582 bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE]
2583 [word_start & M_EDIT_BUF_SIZE];
2584 strncpy (match_expr, bufpos, word_len);
2585 match_expr[word_len] = '\0';
2586 strcat (match_expr, "[a-zA-Z_0-9]+");
2588 /* init search: backward, regexp, whole word, case sensitive */
2589 edit_set_search_parameters (0, 1, 1, 1, 1);
2591 /* collect the possible completions */
2592 /* start search from curs1 down to begin of file */
2593 max_len = edit_collect_completions (edit, word_start, word_len,
2594 match_expr, (struct selection *) &compl, &num_compl);
2596 if (num_compl > 0) {
2597 /* insert completed word if there is only one match */
2598 if (num_compl == 1) {
2599 for (i = word_len; i < compl[0].len; i++)
2600 edit_insert (edit, *(compl[0].text + i));
2602 /* more than one possible completion => ask the user */
2603 else {
2604 /* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
2605 /* !!! pressed again the selection dialog pops up, but that !!! */
2606 /* !!! seems to require a further internal state !!! */
2607 /*beep ();*/
2609 /* let the user select the preferred completion */
2610 edit_completion_dialog (edit, max_len, word_len,
2611 (struct selection *) &compl, num_compl);
2615 /* release memory before return */
2616 for (i = 0; i < num_compl; i++)
2617 free (compl[i].text);
2619 /* restore search parameters */
2620 edit_set_search_parameters (old_rs, old_rb, old_rr, old_rw, old_rc);