Small fix in manual translation.
[midnight-commander.git] / edit / editcmd.c
blobebd7a70cfe7577d2934ef02b469aea5f0cbf0ccc
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 "editlock.h"
31 #include "editcmddef.h"
32 #include "edit-widget.h"
34 #include "src/color.h" /* dialog_colors */
35 #include "src/tty.h" /* LINES */
36 #include "src/widget.h" /* listbox_new() */
37 #include "src/main.h" /* mc_home */
38 #include "src/help.h" /* interactive_display() */
39 #include "src/key.h" /* XCTRL */
40 #include "src/dialog.h" /* do_refresh() */
41 #include "src/wtools.h" /* message() */
42 #include "src/charsets.h"
44 #define edit_get_load_file(f,h) input_dialog (h, _(" Enter file name: "), f)
45 #define edit_get_save_file(f,h) input_dialog (h, _(" Enter file name: "), f)
47 /* globals: */
49 /* search and replace: */
50 int replace_scanf = 0;
51 int replace_regexp = 0;
52 int replace_all = 0;
53 int replace_prompt = 1;
54 int replace_whole = 0;
55 int replace_case = 0;
56 int replace_backwards = 0;
57 int search_create_bookmark = 0;
59 /* queries on a save */
60 int edit_confirm_save = 1;
62 #define NUM_REPL_ARGS 64
63 #define MAX_REPL_LEN 1024
65 static inline int my_lower_case (int c)
67 return tolower(c & 0xFF);
70 char *strcasechr (const unsigned char *s, int c)
72 for (c = my_lower_case (c); my_lower_case ((int) *s) != c; ++s)
73 if (*s == '\0')
74 return 0;
75 return (char *) s;
78 #ifndef HAVE_MEMMOVE
79 /* for Christophe */
80 static void *memmove (void *dest, const void *src, size_t n)
82 char *t, *s;
84 if (dest <= src) {
85 t = (char *) dest;
86 s = (char *) src;
87 while (n--)
88 *t++ = *s++;
89 } else {
90 t = (char *) dest + n;
91 s = (char *) src + n;
92 while (n--)
93 *--t = *--s;
95 return dest;
97 #endif /* !HAVE_MEMMOVE */
99 /* #define itoa MY_itoa <---- this line is now in edit.h */
100 static char *
101 MY_itoa (int i)
103 static char t[14];
104 char *s = t + 13;
105 int j = i;
106 *s-- = 0;
107 do {
108 *s-- = i % 10 + '0';
109 } while ((i = i / 10));
110 if (j < 0)
111 *s-- = '-';
112 return ++s;
115 /* Temporary strings */
116 static char *stacked[16];
119 This joins strings end on end and allocates memory for the result.
120 The result is later automatically free'd and must not be free'd
121 by the caller.
123 char *catstrs (const char *first,...)
125 static int i = 0;
126 va_list ap;
127 int len;
128 char *data;
130 if (!first)
131 return 0;
133 len = strlen (first);
134 va_start (ap, first);
136 while ((data = va_arg (ap, char *)) != 0)
137 len += strlen (data);
139 len++;
141 i = (i + 1) % 16;
142 g_free (stacked[i]);
144 stacked[i] = g_malloc (len);
145 va_end (ap);
146 va_start (ap, first);
147 strcpy (stacked[i], first);
148 while ((data = va_arg (ap, char *)) != 0)
149 strcat (stacked[i], data);
150 va_end (ap);
152 return stacked[i];
155 /* Free temporary strings */
156 void freestrs(void)
158 int i;
160 for (i = 0; i < sizeof(stacked) / sizeof(stacked[0]); i++) {
161 g_free (stacked[i]);
162 stacked[i] = NULL;
166 void edit_help_cmd (WEdit * edit)
168 interactive_display (NULL, "[Internal File Editor]");
169 edit->force |= REDRAW_COMPLETELY;
172 void edit_refresh_cmd (WEdit * edit)
174 #ifndef HAVE_SLANG
175 clr_scr();
176 do_refresh();
177 #else
179 int color;
180 edit_get_syntax_color (edit, -1, &color);
182 touchwin(stdscr);
183 #endif /* !HAVE_SLANG */
184 mc_refresh();
185 doupdate();
188 /* If 0 (quick save) then a) create/truncate <filename> file,
189 b) save to <filename>;
190 if 1 (safe save) then a) save to <tempnam>,
191 b) rename <tempnam> to <filename>;
192 if 2 (do backups) then a) save to <tempnam>,
193 b) rename <filename> to <filename.backup_ext>,
194 c) rename <tempnam> to <filename>. */
196 /* returns 0 on error */
198 edit_save_file (WEdit *edit, const char *filename)
200 char *p;
201 long filelen = 0;
202 char *savename = 0;
203 int this_save_mode, fd;
205 if (!filename)
206 return 0;
207 if (!*filename)
208 return 0;
210 if ((fd = mc_open (filename, O_WRONLY | O_BINARY)) == -1) {
212 * The file does not exists yet, so no safe save or
213 * backup are necessary.
215 this_save_mode = EDIT_QUICK_SAVE;
216 } else {
217 mc_close (fd);
218 this_save_mode = option_save_mode;
221 if (this_save_mode != EDIT_QUICK_SAVE) {
222 char *savedir, *slashpos, *saveprefix;
223 slashpos = strrchr (filename, PATH_SEP);
224 if (slashpos) {
225 savedir = g_strdup (filename);
226 savedir[slashpos - filename + 1] = '\0';
227 } else
228 savedir = g_strdup (".");
229 saveprefix = concat_dir_and_file (savedir, "cooledit");
230 g_free (savedir);
231 fd = mc_mkstemps (&savename, saveprefix, NULL);
232 g_free (saveprefix);
233 if (!savename)
234 return 0;
235 /* FIXME:
236 * Close for now because mc_mkstemps use pure open system call
237 * to create temporary file and it needs to be reopened by
238 * VFS-aware mc_open().
240 close (fd);
241 } else
242 savename = g_strdup (filename);
244 mc_chown (savename, edit->stat1.st_uid, edit->stat1.st_gid);
245 mc_chmod (savename, edit->stat1.st_mode);
247 if ((fd =
248 mc_open (savename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
249 edit->stat1.st_mode)) == -1)
250 goto error_save;
252 /* pipe save */
253 if ((p = edit_get_write_filter (savename, filename))) {
254 FILE *file;
256 mc_close (fd);
257 file = (FILE *) popen (p, "w");
259 if (file) {
260 filelen = edit_write_stream (edit, file);
261 #if 1
262 pclose (file);
263 #else
264 if (pclose (file) != 0) {
265 edit_error_dialog (_("Error"),
266 catstrs (_(" Error writing to pipe: "),
267 p, " ", 0));
268 g_free (p);
269 goto error_save;
271 #endif
272 } else {
273 edit_error_dialog (_("Error"),
274 get_sys_error (catstrs
276 (" Cannot open pipe for writing: "),
277 p, " ", 0)));
278 g_free (p);
279 goto error_save;
281 g_free (p);
282 } else {
283 long buf;
284 buf = 0;
285 filelen = edit->last_byte;
286 while (buf <= (edit->curs1 >> S_EDIT_BUF_SIZE) - 1) {
287 if (mc_write (fd, (char *) edit->buffers1[buf], EDIT_BUF_SIZE)
288 != EDIT_BUF_SIZE) {
289 mc_close (fd);
290 goto error_save;
292 buf++;
294 if (mc_write
295 (fd, (char *) edit->buffers1[buf],
296 edit->curs1 & M_EDIT_BUF_SIZE) !=
297 (edit->curs1 & M_EDIT_BUF_SIZE)) {
298 filelen = -1;
299 } else if (edit->curs2) {
300 edit->curs2--;
301 buf = (edit->curs2 >> S_EDIT_BUF_SIZE);
302 if (mc_write
303 (fd,
304 (char *) edit->buffers2[buf] + EDIT_BUF_SIZE -
305 (edit->curs2 & M_EDIT_BUF_SIZE) - 1,
306 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) !=
307 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) {
308 filelen = -1;
309 } else {
310 while (--buf >= 0) {
311 if (mc_write
312 (fd, (char *) edit->buffers2[buf],
313 EDIT_BUF_SIZE) != EDIT_BUF_SIZE) {
314 filelen = -1;
315 break;
319 edit->curs2++;
321 if (mc_close (fd))
322 goto error_save;
325 if (filelen != edit->last_byte)
326 goto error_save;
327 if (this_save_mode == EDIT_DO_BACKUP)
328 if (mc_rename (filename, catstrs (filename, option_backup_ext, 0))
329 == -1)
330 goto error_save;
331 if (this_save_mode != EDIT_QUICK_SAVE)
332 if (mc_rename (savename, filename) == -1)
333 goto error_save;
334 g_free (savename);
335 return 1;
336 error_save:
337 /* FIXME: Is this safe ?
338 * if (this_save_mode != EDIT_QUICK_SAVE)
339 * mc_unlink (savename);
341 g_free (savename);
342 return 0;
346 I changed this from Oleg's original routine so
347 that option_backup_ext works with coolwidgets as well. This
348 does mean there is a memory leak - paul.
350 void menu_save_mode_cmd (void)
352 #define DLG_X 38
353 #define DLG_Y 10
354 static char *str_result;
355 static int save_mode_new;
356 static char *str[] =
358 N_("Quick save "),
359 N_("Safe save "),
360 N_("Do backups -->")};
361 static QuickWidget widgets[] =
363 {quick_button, 18, DLG_X, 7, DLG_Y, N_("&Cancel"), 0,
364 B_CANCEL, 0, 0, "c"},
365 {quick_button, 6, DLG_X, 7, DLG_Y, N_("&OK"), 0,
366 B_ENTER, 0, 0, "o"},
367 {quick_input, 23, DLG_X, 5, DLG_Y, 0, 9,
368 0, 0, &str_result, "edit-backup-ext"},
369 {quick_label, 22, DLG_X, 4, DLG_Y, N_("Extension:"), 0,
370 0, 0, 0, "savemext"},
371 {quick_radio, 4, DLG_X, 3, DLG_Y, "", 3,
372 0, &save_mode_new, str, "t"},
373 {0}};
374 static QuickDialog dialog =
375 {DLG_X, DLG_Y, -1, -1, N_(" Edit Save Mode "), "[Edit Save Mode]",
376 widgets};
377 static int i18n_flag = 0;
379 if (!i18n_flag) {
380 int i;
381 int maxlen = 0;
382 int dlg_x;
383 int l1;
385 /* OK/Cancel buttons */
386 l1 = strlen (_(widgets[0].text)) + strlen (_(widgets[1].text)) + 5;
387 maxlen = max (maxlen, l1);
389 for (i = 0; i < 3; i++ ) {
390 str[i] = _(str[i]);
391 maxlen = max (maxlen, strlen (str[i]) + 7);
393 i18n_flag = 1;
395 dlg_x = maxlen + strlen (_(widgets[3].text)) + 5 + 1;
396 widgets[2].hotkey_pos = strlen (_(widgets[3].text)); /* input field length */
397 dlg_x = min (COLS, dlg_x);
398 dialog.xlen = dlg_x;
400 i = (dlg_x - l1)/3;
401 widgets[1].relative_x = i;
402 widgets[0].relative_x = i + strlen (_(widgets[1].text)) + i + 4;
404 widgets[2].relative_x = widgets[3].relative_x = maxlen + 2;
406 for (i = 0; i < sizeof (widgets)/sizeof (widgets[0]); i++)
407 widgets[i].x_divisions = dlg_x;
410 widgets[2].text = option_backup_ext;
411 widgets[4].value = option_save_mode;
412 if (quick_dialog (&dialog) != B_ENTER)
413 return;
414 option_save_mode = save_mode_new;
415 option_backup_ext = str_result; /* this is a memory leak */
416 option_backup_ext_int = 0;
417 str_result[min (strlen (str_result), sizeof (int))] = '\0';
418 memcpy ((char *) &option_backup_ext_int, str_result, strlen (option_backup_ext));
421 void
422 edit_set_filename (WEdit *edit, const char *f)
424 if (edit->filename)
425 g_free (edit->filename);
426 if (!f)
427 f = "";
428 edit->filename = g_strdup (f);
431 /* Here we want to warn the users of overwriting an existing file,
432 but only if they have made a change to the filename */
433 /* returns 1 on success */
435 edit_save_as_cmd (WEdit *edit)
437 /* This heads the 'Save As' dialog box */
438 char *exp = 0;
439 int save_lock = 0;
440 int different_filename = 0;
442 exp = edit_get_save_file (edit->filename, _(" Save As "));
443 edit_push_action (edit, KEY_PRESS + edit->start_display);
445 if (exp) {
446 if (!*exp) {
447 g_free (exp);
448 edit->force |= REDRAW_COMPLETELY;
449 return 0;
450 } else {
451 if (strcmp (edit->filename, exp)) {
452 int file;
453 different_filename = 1;
454 if ((file = mc_open (exp, O_RDONLY | O_BINARY)) != -1) {
455 /* the file exists */
456 mc_close (file);
457 /* Overwrite the current file or cancel the operation */
458 if (edit_query_dialog2
459 (_("Warning"),
460 _(" A file already exists with this name. "),
461 _("Overwrite"), _("Cancel"))) {
462 edit->force |= REDRAW_COMPLETELY;
463 g_free (exp);
464 return 0;
467 save_lock = edit_lock_file (exp);
468 } else {
469 /* filenames equal, check if already locked */
470 if (!edit->locked && !edit->delete_file)
471 save_lock = edit_lock_file (exp);
474 if (edit_save_file (edit, exp)) {
475 /* Succesful, so unlock both files */
476 if (strcmp (edit->filename, exp)) {
477 if (save_lock)
478 edit_unlock_file (exp);
479 if (edit->locked)
480 edit->locked = edit_unlock_file (edit->filename);
481 } else {
482 if (edit->locked || save_lock)
483 edit->locked = edit_unlock_file (edit->filename);
486 edit_set_filename (edit, exp);
487 g_free (exp);
488 edit->modified = 0;
489 edit->delete_file = 0;
490 if (different_filename && !edit->explicit_syntax)
491 edit_load_syntax (edit, 0, 0);
492 edit->force |= REDRAW_COMPLETELY;
493 return 1;
494 } else {
495 /* Failed, so maintain modify (not save) lock */
496 if (strcmp (edit->filename, exp) && save_lock)
497 edit_unlock_file (exp);
498 if (save_lock)
499 edit->locked = edit_unlock_file (edit->filename);
500 g_free (exp);
501 edit_error_dialog (_(" Save As "),
502 get_sys_error (_
503 (" Cannot save file. ")));
504 edit->force |= REDRAW_COMPLETELY;
505 return 0;
509 edit->force |= REDRAW_COMPLETELY;
510 return 0;
513 /* {{{ Macro stuff starts here */
515 static int
516 raw_callback (struct Dlg_head *h, int key, int Msg)
518 switch (Msg) {
519 case DLG_KEY:
520 h->running = 0;
521 h->ret_value = key;
522 return MSG_HANDLED;
524 return default_dlg_callback (h, key, Msg);;
527 /* gets a raw key from the keyboard. Passing cancel = 1 draws
528 a cancel button thus allowing c-c etc. Alternatively, cancel = 0
529 will return the next key pressed. ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
530 and Esc are cannot returned */
532 edit_raw_key_query (char *heading, char *query, int cancel)
534 int w = strlen (query) + 7;
535 struct Dlg_head *raw_dlg =
536 create_dlg (0, 0, 7, w, dialog_colors, raw_callback,
537 NULL, heading,
538 DLG_CENTER | DLG_TRYUP | DLG_WANT_TAB);
539 if (cancel)
540 add_widget (raw_dlg,
541 button_new (4, w / 2 - 5, B_CANCEL, NORMAL_BUTTON,
542 _("Cancel"), 0, 0, 0));
543 add_widget (raw_dlg, label_new (3 - cancel, 2, query, 0));
544 add_widget (raw_dlg,
545 input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0));
546 run_dlg (raw_dlg);
547 w = raw_dlg->ret_value;
548 destroy_dlg (raw_dlg);
549 if (cancel) {
550 if (w == XCTRL ('g') || w == XCTRL ('c') || w == ESC_CHAR
551 || w == B_CANCEL)
552 return 0;
555 return w;
558 /* creates a macro file if it doesn't exist */
559 static FILE *edit_open_macro_file (const char *r)
561 char *filename;
562 int file;
563 filename = catstrs (home_dir, MACRO_FILE, 0);
564 if ((file = open (filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
565 return 0;
566 close (file);
567 return fopen (filename, r);
570 #define MAX_MACROS 1024
571 static int saved_macro[MAX_MACROS + 1];
572 static int saved_macros_loaded = 0;
575 This is just to stop the macro file be loaded over and over for keys
576 that aren't defined to anything. On slow systems this could be annoying.
578 static int
579 macro_exists (int k)
581 int i;
582 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++)
583 if (saved_macro[i] == k)
584 return i;
585 return -1;
588 /* returns 1 on error */
589 static int
590 edit_delete_macro (WEdit * edit, int k)
592 struct macro macro[MAX_MACRO_LENGTH];
593 FILE *f, *g;
594 int s, i, n, j = 0;
596 if (saved_macros_loaded)
597 if ((j = macro_exists (k)) < 0)
598 return 0;
599 g = fopen (catstrs (home_dir, TEMP_FILE, 0), "w");
600 if (!g) {
601 /* This heads the delete macro error dialog box */
602 edit_error_dialog (_(" Delete macro "),
603 /* 'Open' = load temp file */
604 get_sys_error (_(" Cannot open temp file ")));
605 return 1;
607 f = edit_open_macro_file ("r");
608 if (!f) {
609 /* This heads the delete macro error dialog box */
610 edit_error_dialog (_(" Delete macro "),
611 /* 'Open' = load temp file */
612 get_sys_error (_(" Cannot open macro file ")));
613 fclose (g);
614 return 1;
616 for (;;) {
617 n = fscanf (f, ("key '%d 0': "), &s);
618 if (!n || n == EOF)
619 break;
620 n = 0;
621 while (fscanf (f, "%hd %hd, ", &macro[n].command, &macro[n].ch))
622 n++;
623 fscanf (f, ";\n");
624 if (s != k) {
625 fprintf (g, ("key '%d 0': "), s);
626 for (i = 0; i < n; i++)
627 fprintf (g, "%hd %hd, ", macro[i].command, macro[i].ch);
628 fprintf (g, ";\n");
631 fclose (f);
632 fclose (g);
633 if (rename (catstrs (home_dir, TEMP_FILE, 0), catstrs (home_dir, MACRO_FILE, 0)) == -1) {
634 /* This heads the delete macro error dialog box */
635 edit_error_dialog (_(" Delete macro "),
636 get_sys_error (_(" Cannot overwrite macro file ")));
637 return 1;
639 if (saved_macros_loaded)
640 memmove (saved_macro + j, saved_macro + j + 1, sizeof (int) * (MAX_MACROS - j - 1));
641 return 0;
644 /* returns 0 on error */
645 int edit_save_macro_cmd (WEdit * edit, struct macro macro[], int n)
647 FILE *f;
648 int s, i;
650 edit_push_action (edit, KEY_PRESS + edit->start_display);
651 /* This heads the 'Macro' dialog box */
652 s = edit_raw_key_query (_(" Save macro "),
653 /* Input line for a single key press follows the ':' */
654 _(" Press the macro's new hotkey: "), 1);
655 edit->force |= REDRAW_COMPLETELY;
656 if (s) {
657 if (edit_delete_macro (edit, s))
658 return 0;
659 f = edit_open_macro_file ("a+");
660 if (f) {
661 fprintf (f, ("key '%d 0': "), s);
662 for (i = 0; i < n; i++)
663 fprintf (f, "%hd %hd, ", macro[i].command, macro[i].ch);
664 fprintf (f, ";\n");
665 fclose (f);
666 if (saved_macros_loaded) {
667 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++);
668 saved_macro[i] = s;
670 return 1;
671 } else
672 /* This heads the 'Save Macro' dialog box */
673 edit_error_dialog (_(" Save macro "), get_sys_error (_(" Cannot open macro file ")));
675 return 0;
678 void edit_delete_macro_cmd (WEdit * edit)
680 int command;
682 command = edit_raw_key_query (_ (" Delete macro "),
683 _ (" Press macro hotkey: "), 1);
685 if (!command)
686 return;
688 edit_delete_macro (edit, command);
691 /* return 0 on error */
692 int edit_load_macro_cmd (WEdit * edit, struct macro macro[], int *n, int k)
694 FILE *f;
695 int s, i = 0, found = 0;
697 if (saved_macros_loaded)
698 if (macro_exists (k) < 0)
699 return 0;
701 if ((f = edit_open_macro_file ("r"))) {
702 struct macro dummy;
703 do {
704 int u;
705 u = fscanf (f, ("key '%d 0': "), &s);
706 if (!u || u == EOF)
707 break;
708 if (!saved_macros_loaded)
709 saved_macro[i++] = s;
710 if (!found) {
711 *n = 0;
712 while (*n < MAX_MACRO_LENGTH && 2 == fscanf (f, "%hd %hd, ", &macro[*n].command, &macro[*n].ch))
713 (*n)++;
714 } else {
715 while (2 == fscanf (f, "%hd %hd, ", &dummy.command, &dummy.ch));
717 fscanf (f, ";\n");
718 if (s == k)
719 found = 1;
720 } while (!found || !saved_macros_loaded);
721 if (!saved_macros_loaded) {
722 saved_macro[i] = 0;
723 saved_macros_loaded = 1;
725 fclose (f);
726 return found;
727 } else
728 /* This heads the 'Load Macro' dialog box */
729 edit_error_dialog (_(" Load macro "),
730 get_sys_error (_(" Cannot open macro file ")));
731 return 0;
734 /* }}} Macro stuff starts here */
736 /* returns 1 on success */
737 int edit_save_confirm_cmd (WEdit * edit)
739 char *f;
741 if (edit_confirm_save) {
742 f = catstrs (_(" Confirm save file? : "), edit->filename, " ", 0);
743 /* Buttons to 'Confirm save file' query */
744 if (edit_query_dialog2 (_(" Save file "), f, _("Save"), _("Cancel")))
745 return 0;
747 return edit_save_cmd (edit);
751 /* returns 1 on success */
752 int edit_save_cmd (WEdit * edit)
754 int res, save_lock = 0;
756 if (!edit->locked && !edit->delete_file)
757 save_lock = edit_lock_file (edit->filename);
758 res = edit_save_file (edit, edit->filename);
760 /* Maintain modify (not save) lock on failure */
761 if ((res && edit->locked) || save_lock)
762 edit->locked = edit_unlock_file (edit->filename);
764 /* On failure try 'save as', it does locking on its own */
765 if (!res)
766 return edit_save_as_cmd (edit);
767 edit->force |= REDRAW_COMPLETELY;
768 edit->delete_file = 0;
769 edit->modified = 0;
771 return 1;
775 /* returns 1 on success */
776 int edit_new_cmd (WEdit * edit)
778 if (edit->modified) {
779 if (edit_query_dialog2 (_ ("Warning"), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
780 edit->force |= REDRAW_COMPLETELY;
781 return 0;
784 edit->force |= REDRAW_COMPLETELY;
786 if (edit->locked)
787 edit->locked = edit_unlock_file (edit->filename);
788 return edit_renew (edit); /* if this gives an error, something has really screwed up */
791 /* returns 1 on error */
792 static int
793 edit_load_file_from_filename (WEdit * edit, char *exp)
795 int prev_locked = edit->locked;
796 char *prev_filename = g_strdup (edit->filename);
798 if (!edit_reload (edit, exp)) {
799 g_free (prev_filename);
800 return 1;
803 if (prev_locked)
804 edit_unlock_file (prev_filename);
805 g_free (prev_filename);
806 return 0;
810 edit_load_cmd (WEdit *edit)
812 char *exp;
814 if (edit->modified) {
815 if (edit_query_dialog2
816 (_("Warning"),
817 _(" Current text was modified without a file save. \n"
818 " Continue discards these changes. "), _("Continue"),
819 _("Cancel"))) {
820 edit->force |= REDRAW_COMPLETELY;
821 return 0;
825 exp = edit_get_load_file (edit->filename, _(" Load "));
827 if (exp) {
828 if (*exp)
829 edit_load_file_from_filename (edit, exp);
830 g_free (exp);
832 edit->force |= REDRAW_COMPLETELY;
833 return 0;
837 if mark2 is -1 then marking is from mark1 to the cursor.
838 Otherwise its between the markers. This handles this.
839 Returns 1 if no text is marked.
841 int eval_marks (WEdit * edit, long *start_mark, long *end_mark)
843 if (edit->mark1 != edit->mark2) {
844 if (edit->mark2 >= 0) {
845 *start_mark = min (edit->mark1, edit->mark2);
846 *end_mark = max (edit->mark1, edit->mark2);
847 } else {
848 *start_mark = min (edit->mark1, edit->curs1);
849 *end_mark = max (edit->mark1, edit->curs1);
850 edit->column2 = edit->curs_col;
852 return 0;
853 } else {
854 *start_mark = *end_mark = 0;
855 edit->column2 = edit->column1 = 0;
856 return 1;
860 #define space_width 1
862 static void
863 edit_insert_column_of_text (WEdit * edit, unsigned char *data, int size, int width)
865 long cursor;
866 int i, col;
867 cursor = edit->curs1;
868 col = edit_get_col (edit);
869 for (i = 0; i < size; i++) {
870 if (data[i] == '\n') { /* fill in and move to next line */
871 int l;
872 long p;
873 if (edit_get_byte (edit, edit->curs1) != '\n') {
874 l = width - (edit_get_col (edit) - col);
875 while (l > 0) {
876 edit_insert (edit, ' ');
877 l -= space_width;
880 for (p = edit->curs1;; p++) {
881 if (p == edit->last_byte) {
882 edit_cursor_move (edit, edit->last_byte - edit->curs1);
883 edit_insert_ahead (edit, '\n');
884 p++;
885 break;
887 if (edit_get_byte (edit, p) == '\n') {
888 p++;
889 break;
892 edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->curs1);
893 l = col - edit_get_col (edit);
894 while (l >= space_width) {
895 edit_insert (edit, ' ');
896 l -= space_width;
898 continue;
900 edit_insert (edit, data[i]);
902 edit_cursor_move (edit, cursor - edit->curs1);
906 void
907 edit_block_copy_cmd (WEdit *edit)
909 long start_mark, end_mark, current = edit->curs1;
910 int size, x;
911 unsigned char *copy_buf;
913 edit_update_curs_col (edit);
914 x = edit->curs_col;
915 if (eval_marks (edit, &start_mark, &end_mark))
916 return;
917 if (column_highlighting)
918 if ((x >= edit->column1 && x < edit->column2)
919 || (x > edit->column2 && x <= edit->column1))
920 return;
922 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
924 /* all that gets pushed are deletes hence little space is used on the stack */
926 edit_push_markers (edit);
928 if (column_highlighting) {
929 edit_insert_column_of_text (edit, copy_buf, size,
930 abs (edit->column2 - edit->column1));
931 } else {
932 while (size--)
933 edit_insert_ahead (edit, copy_buf[size]);
936 g_free (copy_buf);
937 edit_scroll_screen_over_cursor (edit);
939 if (column_highlighting) {
940 edit_set_markers (edit, 0, 0, 0, 0);
941 edit_push_action (edit, COLUMN_ON);
942 column_highlighting = 0;
943 } else if (start_mark < current && end_mark > current)
944 edit_set_markers (edit, start_mark,
945 end_mark + end_mark - start_mark, 0, 0);
947 edit->force |= REDRAW_PAGE;
951 void
952 edit_block_move_cmd (WEdit *edit)
954 long count;
955 long current;
956 unsigned char *copy_buf;
957 long start_mark, end_mark;
958 int deleted = 0;
959 int x = 0;
961 if (eval_marks (edit, &start_mark, &end_mark))
962 return;
963 if (column_highlighting) {
964 edit_update_curs_col (edit);
965 x = edit->curs_col;
966 if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
967 if ((x > edit->column1 && x < edit->column2)
968 || (x > edit->column2 && x < edit->column1))
969 return;
970 } else if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
971 return;
973 if ((end_mark - start_mark) > option_max_undo / 2)
974 if (edit_query_dialog2
975 (_("Warning"),
977 (" Block is large, you may not be able to undo this action. "),
978 _("Continue"), _("Cancel")))
979 return;
981 edit_push_markers (edit);
982 current = edit->curs1;
983 if (column_highlighting) {
984 int size, c1, c2, line;
985 line = edit->curs_line;
986 if (edit->mark2 < 0)
987 edit_mark_cmd (edit, 0);
988 c1 = min (edit->column1, edit->column2);
989 c2 = max (edit->column1, edit->column2);
990 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
991 if (x < c2) {
992 edit_block_delete_cmd (edit);
993 deleted = 1;
995 edit_move_to_line (edit, line);
996 edit_cursor_move (edit,
997 edit_move_forward3 (edit,
998 edit_bol (edit, edit->curs1),
999 x, 0) - edit->curs1);
1000 edit_insert_column_of_text (edit, copy_buf, size, c2 - c1);
1001 if (!deleted) {
1002 line = edit->curs_line;
1003 edit_update_curs_col (edit);
1004 x = edit->curs_col;
1005 edit_block_delete_cmd (edit);
1006 edit_move_to_line (edit, line);
1007 edit_cursor_move (edit,
1008 edit_move_forward3 (edit,
1009 edit_bol (edit,
1010 edit->curs1),
1011 x, 0) - edit->curs1);
1013 edit_set_markers (edit, 0, 0, 0, 0);
1014 edit_push_action (edit, COLUMN_ON);
1015 column_highlighting = 0;
1016 } else {
1017 copy_buf = g_malloc (end_mark - start_mark);
1018 edit_cursor_move (edit, start_mark - edit->curs1);
1019 edit_scroll_screen_over_cursor (edit);
1020 count = start_mark;
1021 while (count < end_mark) {
1022 copy_buf[end_mark - count - 1] = edit_delete (edit);
1023 count++;
1025 edit_scroll_screen_over_cursor (edit);
1026 edit_cursor_move (edit,
1027 current - edit->curs1 -
1028 (((current - edit->curs1) >
1029 0) ? end_mark - start_mark : 0));
1030 edit_scroll_screen_over_cursor (edit);
1031 while (count-- > start_mark)
1032 edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
1033 edit_set_markers (edit, edit->curs1,
1034 edit->curs1 + end_mark - start_mark, 0, 0);
1036 edit_scroll_screen_over_cursor (edit);
1037 g_free (copy_buf);
1038 edit->force |= REDRAW_PAGE;
1041 static void
1042 edit_delete_column_of_text (WEdit * edit)
1044 long p, q, r, m1, m2;
1045 int b, c, d;
1046 int n;
1048 eval_marks (edit, &m1, &m2);
1049 n = edit_move_forward (edit, m1, 0, m2) + 1;
1050 c = edit_move_forward3 (edit, edit_bol (edit, m1), 0, m1);
1051 d = edit_move_forward3 (edit, edit_bol (edit, m2), 0, m2);
1053 b = min (c, d);
1054 c = max (c, d);
1056 while (n--) {
1057 r = edit_bol (edit, edit->curs1);
1058 p = edit_move_forward3 (edit, r, b, 0);
1059 q = edit_move_forward3 (edit, r, c, 0);
1060 if (p < m1)
1061 p = m1;
1062 if (q > m2)
1063 q = m2;
1064 edit_cursor_move (edit, p - edit->curs1);
1065 while (q > p) { /* delete line between margins */
1066 if (edit_get_byte (edit, edit->curs1) != '\n')
1067 edit_delete (edit);
1068 q--;
1070 if (n) /* move to next line except on the last delete */
1071 edit_cursor_move (edit, edit_move_forward (edit, edit->curs1, 1, 0) - edit->curs1);
1075 /* if success return 0 */
1077 edit_block_delete (WEdit *edit)
1079 long count;
1080 long start_mark, end_mark;
1081 if (eval_marks (edit, &start_mark, &end_mark))
1082 return 0;
1083 if (column_highlighting && edit->mark2 < 0)
1084 edit_mark_cmd (edit, 0);
1085 if ((end_mark - start_mark) > option_max_undo / 2) {
1086 /* Warning message with a query to continue or cancel the operation */
1087 if (edit_query_dialog2
1088 (_("Warning"),
1090 (" Block is large, you may not be able to undo this action. "),
1091 _("Continue"), _("Cancel"))) {
1092 return 1;
1095 edit_push_markers (edit);
1096 edit_cursor_move (edit, start_mark - edit->curs1);
1097 edit_scroll_screen_over_cursor (edit);
1098 count = start_mark;
1099 if (start_mark < end_mark) {
1100 if (column_highlighting) {
1101 if (edit->mark2 < 0)
1102 edit_mark_cmd (edit, 0);
1103 edit_delete_column_of_text (edit);
1104 } else {
1105 while (count < end_mark) {
1106 edit_delete (edit);
1107 count++;
1111 edit_set_markers (edit, 0, 0, 0, 0);
1112 edit->force |= REDRAW_PAGE;
1113 return 0;
1116 /* returns 1 if canceelled by user */
1117 int edit_block_delete_cmd (WEdit * edit)
1119 long start_mark, end_mark;
1120 if (eval_marks (edit, &start_mark, &end_mark)) {
1121 edit_delete_line (edit);
1122 return 0;
1124 return edit_block_delete (edit);
1128 #define INPUT_INDEX 9
1129 #define SEARCH_DLG_WIDTH 58
1130 #define SEARCH_DLG_HEIGHT 10
1131 #define REPLACE_DLG_WIDTH 58
1132 #define REPLACE_DLG_HEIGHT 15
1133 #define CONFIRM_DLG_WIDTH 79
1134 #define CONFIRM_DLG_HEIGTH 6
1135 #define B_REPLACE_ALL B_USER+1
1136 #define B_REPLACE_ONE B_USER+2
1137 #define B_SKIP_REPLACE B_USER+3
1139 static int
1140 edit_replace_prompt (WEdit * edit, char *replace_text, int xpos, int ypos)
1142 QuickWidget quick_widgets[] =
1144 {quick_button, 63, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Cancel"),
1145 0, B_CANCEL, 0, 0, NULL},
1146 {quick_button, 50, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("O&ne"),
1147 0, B_REPLACE_ONE, 0, 0, NULL},
1148 {quick_button, 37, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("A&ll"),
1149 0, B_REPLACE_ALL, 0, 0, NULL},
1150 {quick_button, 21, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Skip"),
1151 0, B_SKIP_REPLACE, 0, 0, NULL},
1152 {quick_button, 4, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Replace"),
1153 0, B_ENTER, 0, 0, NULL},
1154 {quick_label, 2, CONFIRM_DLG_WIDTH, 2, CONFIRM_DLG_HEIGTH, 0,
1155 0, 0, 0, 0, 0},
1156 {0}};
1158 #ifdef HAVE_CHARSET
1159 char *msg = _(" Replace with: ");
1161 quick_widgets[5].text = catstrs (msg, replace_text, 0);
1163 if (*replace_text)
1164 convert_to_display (quick_widgets[5].text + strlen (msg));
1165 #else
1166 quick_widgets[5].text = catstrs (_ (" Replace with: "), replace_text, 0);
1167 #endif /* !HAVE_CHARSET */
1170 QuickDialog Quick_input =
1171 {CONFIRM_DLG_WIDTH, CONFIRM_DLG_HEIGTH, 0, 0, N_ (" Confirm replace "),
1172 "[Input Line Keys]", 0 /*quick_widgets */ };
1174 Quick_input.widgets = quick_widgets;
1176 Quick_input.xpos = xpos;
1178 /* Sometimes menu can hide replaced text. I don't like it */
1180 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + CONFIRM_DLG_HEIGTH - 1))
1181 ypos -= CONFIRM_DLG_HEIGTH;
1183 Quick_input.ypos = ypos;
1184 return quick_dialog (&Quick_input);
1188 static void
1189 edit_replace_dialog (WEdit * edit, char **search_text, char **replace_text, char **arg_order)
1191 int treplace_scanf = replace_scanf;
1192 int treplace_regexp = replace_regexp;
1193 int treplace_all = replace_all;
1194 int treplace_prompt = replace_prompt;
1195 int treplace_backwards = replace_backwards;
1196 int treplace_whole = replace_whole;
1197 int treplace_case = replace_case;
1199 QuickWidget quick_widgets[] =
1201 {quick_button, 6, 10, 12, REPLACE_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1202 0, NULL},
1203 {quick_button, 2, 10, 12, REPLACE_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1204 0, NULL},
1205 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1206 0, 0, NULL},
1207 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("replace &All"), 0, 0,
1208 0, 0, NULL},
1209 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("pr&Ompt on replace"), 0, 0,
1210 0, 0, NULL},
1211 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1212 0, 0, NULL},
1213 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1214 0, 0, NULL},
1215 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1216 0, 0, NULL},
1217 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 8, REPLACE_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1218 0, 0, NULL},
1219 {quick_input, 3, REPLACE_DLG_WIDTH, 7, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1220 0, "edit-argord"},
1221 {quick_label, 2, REPLACE_DLG_WIDTH, 6, REPLACE_DLG_HEIGHT, N_(" Enter replacement argument order eg. 3,2,1,4 "), 0, 0,
1222 0, 0, 0},
1223 {quick_input, 3, REPLACE_DLG_WIDTH, 5, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1224 0, "edit-replace"},
1225 {quick_label, 2, REPLACE_DLG_WIDTH, 4, REPLACE_DLG_HEIGHT, N_(" Enter replacement string:"), 0, 0, 0,
1226 0, 0},
1227 {quick_input, 3, REPLACE_DLG_WIDTH, 3, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1228 0, "edit-search"},
1229 {quick_label, 2, REPLACE_DLG_WIDTH, 2, REPLACE_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1230 0, 0},
1231 {0}};
1233 quick_widgets[2].result = &treplace_scanf;
1234 quick_widgets[3].result = &treplace_all;
1235 quick_widgets[4].result = &treplace_prompt;
1236 quick_widgets[5].result = &treplace_backwards;
1237 quick_widgets[6].result = &treplace_regexp;
1238 quick_widgets[7].result = &treplace_whole;
1239 quick_widgets[8].result = &treplace_case;
1240 quick_widgets[9].str_result = arg_order;
1241 quick_widgets[9].text = *arg_order;
1242 quick_widgets[11].str_result = replace_text;
1243 quick_widgets[11].text = *replace_text;
1244 quick_widgets[13].str_result = search_text;
1245 quick_widgets[13].text = *search_text;
1247 QuickDialog Quick_input =
1248 {REPLACE_DLG_WIDTH, REPLACE_DLG_HEIGHT, -1, 0, N_(" Replace "),
1249 "[Input Line Keys]", 0 /*quick_widgets */ };
1251 Quick_input.widgets = quick_widgets;
1253 if (quick_dialog (&Quick_input) != B_CANCEL) {
1254 replace_scanf = treplace_scanf;
1255 replace_backwards = treplace_backwards;
1256 replace_regexp = treplace_regexp;
1257 replace_all = treplace_all;
1258 replace_prompt = treplace_prompt;
1259 replace_whole = treplace_whole;
1260 replace_case = treplace_case;
1261 return;
1262 } else {
1263 *arg_order = NULL;
1264 *replace_text = NULL;
1265 *search_text = NULL;
1266 return;
1272 static void
1273 edit_search_dialog (WEdit * edit, char **search_text)
1275 int treplace_scanf = replace_scanf;
1276 int treplace_regexp = replace_regexp;
1277 int treplace_whole = replace_whole;
1278 int treplace_case = replace_case;
1279 int treplace_backwards = replace_backwards;
1281 QuickWidget quick_widgets[] =
1283 {quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1284 0, NULL},
1285 {quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1286 0, NULL},
1287 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1288 0, 0, NULL },
1289 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1290 0, 0, NULL},
1291 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1292 0, 0, NULL},
1293 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1294 0, 0, NULL},
1295 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1296 0, 0, NULL},
1297 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, 0,
1298 0, "edit-search"},
1299 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1300 0, 0},
1301 {0}};
1303 quick_widgets[2].result = &treplace_scanf;
1304 quick_widgets[3].result = &treplace_backwards;
1305 quick_widgets[4].result = &treplace_regexp;
1306 quick_widgets[5].result = &treplace_whole;
1307 quick_widgets[6].result = &treplace_case;
1308 quick_widgets[7].str_result = search_text;
1309 quick_widgets[7].text = *search_text;
1312 QuickDialog Quick_input =
1313 {SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
1314 "[Input Line Keys]", 0 /*quick_widgets */ };
1316 Quick_input.widgets = quick_widgets;
1318 if (quick_dialog (&Quick_input) != B_CANCEL) {
1319 replace_scanf = treplace_scanf;
1320 replace_backwards = treplace_backwards;
1321 replace_regexp = treplace_regexp;
1322 replace_whole = treplace_whole;
1323 replace_case = treplace_case;
1324 } else {
1325 *search_text = NULL;
1331 static long sargs[NUM_REPL_ARGS][256 / sizeof (long)];
1333 #define SCANF_ARGS sargs[0], sargs[1], sargs[2], sargs[3], \
1334 sargs[4], sargs[5], sargs[6], sargs[7], \
1335 sargs[8], sargs[9], sargs[10], sargs[11], \
1336 sargs[12], sargs[13], sargs[14], sargs[15]
1338 #define PRINTF_ARGS sargs[argord[0]], sargs[argord[1]], sargs[argord[2]], sargs[argord[3]], \
1339 sargs[argord[4]], sargs[argord[5]], sargs[argord[6]], sargs[argord[7]], \
1340 sargs[argord[8]], sargs[argord[9]], sargs[argord[10]], sargs[argord[11]], \
1341 sargs[argord[12]], sargs[argord[13]], sargs[argord[14]], sargs[argord[15]]
1344 /* This function is a modification of mc-3.2.10/src/view.c:regexp_view_search() */
1345 /* returns -3 on error in pattern, -1 on not found, found_len = 0 if either */
1346 static int
1347 string_regexp_search (char *pattern, char *string, int len, int match_type,
1348 int match_bol, int icase, int *found_len, void *d)
1350 static regex_t r;
1351 static char *old_pattern = NULL;
1352 static int old_type, old_icase;
1353 regmatch_t *pmatch;
1354 static regmatch_t s[1];
1356 pmatch = (regmatch_t *) d;
1357 if (!pmatch)
1358 pmatch = s;
1360 if (!old_pattern || strcmp (old_pattern, pattern)
1361 || old_type != match_type || old_icase != icase) {
1362 if (old_pattern) {
1363 regfree (&r);
1364 g_free (old_pattern);
1365 old_pattern = 0;
1367 if (regcomp (&r, pattern, REG_EXTENDED | (icase ? REG_ICASE : 0))) {
1368 *found_len = 0;
1369 return -3;
1371 old_pattern = g_strdup (pattern);
1372 old_type = match_type;
1373 old_icase = icase;
1375 if (regexec
1376 (&r, string, d ? NUM_REPL_ARGS : 1, pmatch,
1377 ((match_bol
1378 || match_type != match_normal) ? 0 : REG_NOTBOL)) != 0) {
1379 *found_len = 0;
1380 return -1;
1382 *found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1383 return (pmatch[0].rm_so);
1386 /* thanks to Liviu Daia <daia@stoilow.imar.ro> for getting this
1387 (and the above) routines to work properly - paul */
1389 static long
1390 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)
1392 long p, q = 0;
1393 long l = strlen ((char *) exp), f = 0;
1394 int n = 0;
1396 for (p = 0; p < l; p++) /* count conversions... */
1397 if (exp[p] == '%')
1398 if (exp[++p] != '%') /* ...except for "%%" */
1399 n++;
1401 if (replace_scanf || replace_regexp) {
1402 int c;
1403 unsigned char *buf;
1404 unsigned char mbuf[MAX_REPL_LEN * 2 + 3];
1406 replace_scanf = (!replace_regexp); /* can't have both */
1408 buf = mbuf;
1410 if (replace_scanf) {
1411 unsigned char e[MAX_REPL_LEN];
1412 if (n >= NUM_REPL_ARGS)
1413 return -3;
1415 if (replace_case) {
1416 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++)
1417 buf[p - start] = (*get_byte) (data, p);
1418 } else {
1419 for (p = 0; exp[p] != 0; p++)
1420 exp[p] = my_lower_case (exp[p]);
1421 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++) {
1422 c = (*get_byte) (data, p);
1423 buf[p - start] = my_lower_case (c);
1427 buf[(q = p - start)] = 0;
1428 strcpy ((char *) e, (char *) exp);
1429 strcat ((char *) e, "%n");
1430 exp = e;
1432 while (q) {
1433 *((int *) sargs[n]) = 0; /* --> here was the problem - now fixed: good */
1434 if (n == sscanf ((char *) buf, (char *) exp, SCANF_ARGS)) {
1435 if (*((int *) sargs[n])) {
1436 *len = *((int *) sargs[n]);
1437 return start;
1440 if (once_only)
1441 return -2;
1442 if (q + start < last_byte) {
1443 if (replace_case) {
1444 buf[q] = (*get_byte) (data, q + start);
1445 } else {
1446 c = (*get_byte) (data, q + start);
1447 buf[q] = my_lower_case (c);
1449 q++;
1451 buf[q] = 0;
1452 start++;
1453 buf++; /* move the window along */
1454 if (buf == mbuf + MAX_REPL_LEN) { /* the window is about to go past the end of array, so... */
1455 memmove (mbuf, buf, strlen ((char *) buf) + 1); /* reset it */
1456 buf = mbuf;
1458 q--;
1460 } else { /* regexp matching */
1461 long offset = 0;
1462 int found_start, match_bol, move_win = 0;
1464 while (start + offset < last_byte) {
1465 match_bol = (offset == 0 || (*get_byte) (data, start + offset - 1) == '\n');
1466 if (!move_win) {
1467 p = start + offset;
1468 q = 0;
1470 for (; p < last_byte && q < MAX_REPL_LEN; p++, q++) {
1471 mbuf[q] = (*get_byte) (data, p);
1472 if (mbuf[q] == '\n')
1473 break;
1475 q++;
1476 offset += q;
1477 mbuf[q] = 0;
1479 buf = mbuf;
1480 while (q) {
1481 found_start = string_regexp_search ((char *) exp, (char *) buf, q, match_normal, match_bol, !replace_case, len, d);
1483 if (found_start <= -2) { /* regcomp/regexec error */
1484 *len = 0;
1485 return -3;
1487 else if (found_start == -1) /* not found: try next line */
1488 break;
1489 else if (*len == 0) { /* null pattern: try again at next character */
1490 q--;
1491 buf++;
1492 match_bol = 0;
1493 continue;
1495 else /* found */
1496 return (start + offset - q + found_start);
1498 if (once_only)
1499 return -2;
1501 if (buf[q - 1] != '\n') { /* incomplete line: try to recover */
1502 buf = mbuf + MAX_REPL_LEN / 2;
1503 q = strlen ((char *) buf);
1504 memmove (mbuf, buf, q);
1505 p = start + q;
1506 move_win = 1;
1508 else
1509 move_win = 0;
1512 } else {
1513 *len = strlen ((char *) exp);
1514 if (replace_case) {
1515 for (p = start; p <= last_byte - l; p++) {
1516 if ((*get_byte) (data, p) == (unsigned char)exp[0]) { /* check if first char matches */
1517 for (f = 0, q = 0; q < l && f < 1; q++)
1518 if ((*get_byte) (data, q + p) != (unsigned char)exp[q])
1519 f = 1;
1520 if (f == 0)
1521 return p;
1523 if (once_only)
1524 return -2;
1526 } else {
1527 for (p = 0; exp[p] != 0; p++)
1528 exp[p] = my_lower_case (exp[p]);
1530 for (p = start; p <= last_byte - l; p++) {
1531 if (my_lower_case ((*get_byte) (data, p)) == (unsigned char)exp[0]) {
1532 for (f = 0, q = 0; q < l && f < 1; q++)
1533 if (my_lower_case ((*get_byte) (data, q + p)) != (unsigned char)exp[q])
1534 f = 1;
1535 if (f == 0)
1536 return p;
1538 if (once_only)
1539 return -2;
1543 return -2;
1547 static long
1548 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)
1549 { /*front end to find_string to check for
1550 whole words */
1551 long p;
1552 p = search_start;
1554 while ((p = edit_find_string (p, exp, len, last_byte, get_byte, data, once_only, d)) >= 0) {
1555 if (replace_whole) {
1556 /*If the bordering chars are not in option_whole_chars_search then word is whole */
1557 if (!strcasechr (option_whole_chars_search, (*get_byte) (data, p - 1))
1558 && !strcasechr (option_whole_chars_search, (*get_byte) (data, p + *len)))
1559 return p;
1560 if (once_only)
1561 return -2;
1562 } else
1563 return p;
1564 if (once_only)
1565 break;
1566 p++; /*not a whole word so continue search. */
1568 return p;
1571 static long
1572 edit_find (long search_start, unsigned char *exp, int *len, long last_byte, int (*get_byte) (void *, long), void *data, void *d)
1574 long p;
1575 if (replace_backwards) {
1576 while (search_start >= 0) {
1577 p = edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 1, d);
1578 if (p == search_start)
1579 return p;
1580 search_start--;
1582 } else {
1583 return edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 0, d);
1585 return -2;
1588 #define is_digit(x) ((x) >= '0' && (x) <= '9')
1590 #define snprintf(v) { \
1591 *p1++ = *p++; \
1592 *p1++ = '%'; \
1593 *p1++ = 'n'; \
1594 *p1 = '\0'; \
1595 sprintf(s,q1,v,&n); \
1596 s += n; \
1599 /* this function uses the sprintf command to do a vprintf */
1600 /* it takes pointers to arguments instead of the arguments themselves */
1601 static int sprintf_p (char *str, const char *fmt,...)
1602 __attribute__ ((format (printf, 2, 3)));
1604 static int sprintf_p (char *str, const char *fmt,...)
1606 va_list ap;
1607 int n;
1608 char *q, *p, *s = str;
1609 char q1[32];
1610 char *p1;
1612 va_start (ap, fmt);
1613 p = q = (char *) fmt;
1615 while ((p = strchr (p, '%'))) {
1616 n = p - q;
1617 strncpy (s, q, n); /* copy stuff between format specifiers */
1618 s += n;
1619 *s = 0;
1620 q = p;
1621 p1 = q1;
1622 *p1++ = *p++;
1623 if (*p == '%') {
1624 p++;
1625 *s++ = '%';
1626 q = p;
1627 continue;
1629 if (*p == 'n') {
1630 p++;
1631 /* do nothing */
1632 q = p;
1633 continue;
1635 if (*p == '#')
1636 *p1++ = *p++;
1637 if (*p == '0')
1638 *p1++ = *p++;
1639 if (*p == '-')
1640 *p1++ = *p++;
1641 if (*p == '+')
1642 *p1++ = *p++;
1643 if (*p == '*') {
1644 p++;
1645 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace field width with a number */
1646 p1 += strlen (p1);
1647 } else {
1648 while (is_digit (*p))
1649 *p1++ = *p++;
1651 if (*p == '.')
1652 *p1++ = *p++;
1653 if (*p == '*') {
1654 p++;
1655 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace precision with a number */
1656 p1 += strlen (p1);
1657 } else {
1658 while (is_digit (*p))
1659 *p1++ = *p++;
1661 /* flags done, now get argument */
1662 if (*p == 's') {
1663 snprintf (va_arg (ap, char *));
1664 } else if (*p == 'h') {
1665 if (strchr ("diouxX", *p))
1666 snprintf (*va_arg (ap, short *));
1667 } else if (*p == 'l') {
1668 *p1++ = *p++;
1669 if (strchr ("diouxX", *p))
1670 snprintf (*va_arg (ap, long *));
1671 } else if (strchr ("cdiouxX", *p)) {
1672 snprintf (*va_arg (ap, int *));
1673 } else if (*p == 'L') {
1674 *p1++ = *p++;
1675 if (strchr ("EefgG", *p))
1676 snprintf (*va_arg (ap, double *)); /* should be long double */
1677 } else if (strchr ("EefgG", *p)) {
1678 snprintf (*va_arg (ap, double *));
1679 } else if (strchr ("DOU", *p)) {
1680 snprintf (*va_arg (ap, long *));
1681 } else if (*p == 'p') {
1682 snprintf (*va_arg (ap, void **));
1684 q = p;
1686 va_end (ap);
1687 sprintf (s, q); /* print trailing leftover */
1688 return s - str + strlen (s);
1691 static void regexp_error (WEdit *edit)
1693 /* "Error: Syntax error in regular expression, or scanf expression contained too many %'s */
1694 edit_error_dialog (_("Error"), _(" Invalid regular expression, or scanf expression with to many conversions "));
1697 /* call with edit = 0 before shutdown to close memory leaks */
1698 void
1699 edit_replace_cmd (WEdit *edit, int again)
1701 static regmatch_t pmatch[NUM_REPL_ARGS];
1702 static char *old1 = NULL;
1703 static char *old2 = NULL;
1704 static char *old3 = NULL;
1705 char *exp1 = "";
1706 char *exp2 = "";
1707 char *exp3 = "";
1708 int replace_yes;
1709 int replace_continue;
1710 int treplace_prompt = 0;
1711 int i = 0;
1712 long times_replaced = 0, last_search;
1713 int argord[NUM_REPL_ARGS];
1715 if (!edit) {
1716 if (old1) {
1717 g_free (old1);
1718 old1 = 0;
1720 if (old2) {
1721 g_free (old2);
1722 old2 = 0;
1724 if (old3) {
1725 g_free (old3);
1726 old3 = 0;
1728 return;
1730 last_search = edit->last_byte;
1732 edit->force |= REDRAW_COMPLETELY;
1734 exp1 = old1 ? old1 : exp1;
1735 exp2 = old2 ? old2 : exp2;
1736 exp3 = old3 ? old3 : exp3;
1738 if (again) {
1739 if (!old1 || !old2)
1740 return;
1741 exp1 = g_strdup (old1);
1742 exp2 = g_strdup (old2);
1743 exp3 = g_strdup (old3);
1744 } else {
1745 edit_push_action (edit, KEY_PRESS + edit->start_display);
1747 convert_to_display (exp1);
1748 convert_to_display (exp2);
1750 edit_replace_dialog (edit, &exp1, &exp2, &exp3);
1752 convert_from_input (exp1);
1753 convert_from_input (exp2);
1755 treplace_prompt = replace_prompt;
1758 if (!exp1 || !*exp1) {
1759 edit->force = REDRAW_COMPLETELY;
1760 g_free (exp1);
1761 g_free (exp2);
1762 g_free (exp3);
1763 return;
1765 g_free (old1);
1766 g_free (old2);
1767 g_free (old3);
1768 old1 = g_strdup (exp1);
1769 old2 = g_strdup (exp2);
1770 old3 = g_strdup (exp3);
1773 char *s;
1774 int ord;
1775 while ((s = strchr (exp3, ' ')))
1776 memmove (s, s + 1, strlen (s));
1777 s = exp3;
1778 for (i = 0; i < NUM_REPL_ARGS; i++) {
1779 if (s != (char *) 1 && *s) {
1780 ord = atoi (s);
1781 if ((ord > 0) && (ord < NUM_REPL_ARGS))
1782 argord[i] = ord - 1;
1783 else
1784 argord[i] = i;
1785 s = strchr (s, ',') + 1;
1786 } else
1787 argord[i] = i;
1791 replace_continue = replace_all;
1793 if (edit->found_len && edit->search_start == edit->found_start + 1
1794 && replace_backwards)
1795 edit->search_start--;
1797 if (edit->found_len && edit->search_start == edit->found_start - 1
1798 && !replace_backwards)
1799 edit->search_start++;
1801 do {
1802 int len = 0;
1803 long new_start;
1804 new_start =
1805 edit_find (edit->search_start, (unsigned char *) exp1, &len,
1806 last_search, (int (*)(void *, long)) edit_get_byte,
1807 (void *) edit, pmatch);
1808 if (new_start == -3) {
1809 regexp_error (edit);
1810 break;
1812 edit->search_start = new_start;
1813 /*returns negative on not found or error in pattern */
1815 if (edit->search_start >= 0) {
1816 edit->found_start = edit->search_start;
1817 i = edit->found_len = len;
1819 edit_cursor_move (edit, edit->search_start - edit->curs1);
1820 edit_scroll_screen_over_cursor (edit);
1822 replace_yes = 1;
1824 if (treplace_prompt) {
1825 int l;
1826 l = edit->curs_row - edit->num_widget_lines / 3;
1827 if (l > 0)
1828 edit_scroll_downward (edit, l);
1829 if (l < 0)
1830 edit_scroll_upward (edit, -l);
1832 edit_scroll_screen_over_cursor (edit);
1833 edit->force |= REDRAW_PAGE;
1834 edit_render_keypress (edit);
1836 /*so that undo stops at each query */
1837 edit_push_key_press (edit);
1839 switch (edit_replace_prompt (edit, exp2, /* and prompt 2/3 down */
1840 (edit->num_widget_columns -
1841 CONFIRM_DLG_WIDTH) / 2,
1842 edit->num_widget_lines * 2 /
1843 3)) {
1844 case B_ENTER:
1845 break;
1846 case B_SKIP_REPLACE:
1847 replace_yes = 0;
1848 break;
1849 case B_REPLACE_ALL:
1850 treplace_prompt = 0;
1851 replace_continue = 1;
1852 break;
1853 case B_REPLACE_ONE:
1854 replace_continue = 0;
1855 break;
1856 case B_CANCEL:
1857 replace_yes = 0;
1858 replace_continue = 0;
1859 break;
1862 if (replace_yes) { /* delete then insert new */
1863 if (replace_scanf || replace_regexp) {
1864 char repl_str[MAX_REPL_LEN + 2];
1866 /* we need to fill in sargs just like with scanf */
1867 if (replace_regexp) {
1868 int k, j;
1869 for (k = 1;
1870 k < NUM_REPL_ARGS && pmatch[k].rm_eo >= 0;
1871 k++) {
1872 unsigned char *t;
1873 t = (unsigned char *) &sargs[k - 1][0];
1874 for (j = 0;
1875 j < pmatch[k].rm_eo - pmatch[k].rm_so
1876 && j < 255; j++, t++)
1877 *t = (unsigned char) edit_get_byte (edit,
1878 edit->
1879 search_start
1881 pmatch
1882 [0].
1883 rm_so +
1884 pmatch
1885 [k].
1886 rm_so +
1888 *t = '\0';
1890 for (; k <= NUM_REPL_ARGS; k++)
1891 sargs[k - 1][0] = 0;
1893 if (sprintf_p (repl_str, exp2, PRINTF_ARGS) >= 0) {
1894 times_replaced++;
1895 while (i--)
1896 edit_delete (edit);
1897 while (repl_str[++i])
1898 edit_insert (edit, repl_str[i]);
1899 } else {
1900 edit_error_dialog (_(" Replace "),
1902 (" Error in replacement format string. "));
1903 replace_continue = 0;
1905 } else {
1906 times_replaced++;
1907 while (i--)
1908 edit_delete (edit);
1909 while (exp2[++i])
1910 edit_insert (edit, exp2[i]);
1912 edit->found_len = i;
1914 /* so that we don't find the same string again */
1915 if (replace_backwards) {
1916 last_search = edit->search_start;
1917 edit->search_start--;
1918 } else {
1919 edit->search_start += i;
1920 last_search = edit->last_byte;
1922 edit_scroll_screen_over_cursor (edit);
1923 } else {
1924 char *msg = _(" Replace ");
1925 /* try and find from right here for next search */
1926 edit->search_start = edit->curs1;
1927 edit_update_curs_col (edit);
1929 edit->force |= REDRAW_PAGE;
1930 edit_render_keypress (edit);
1931 if (times_replaced) {
1932 message (0, msg, _(" %ld replacements made. "),
1933 times_replaced);
1934 } else
1935 edit_message_dialog (msg, _(" Search string not found "));
1936 replace_continue = 0;
1938 } while (replace_continue);
1940 g_free (exp1);
1941 g_free (exp2);
1942 g_free (exp3);
1943 edit->force = REDRAW_COMPLETELY;
1944 edit_scroll_screen_over_cursor (edit);
1950 void edit_search_cmd (WEdit * edit, int again)
1952 static char *old = NULL;
1953 char *exp = "";
1955 if (!edit) {
1956 if (old) {
1957 g_free (old);
1958 old = 0;
1960 return;
1962 exp = old ? old : exp;
1963 if (again) { /*ctrl-hotkey for search again. */
1964 if (!old)
1965 return;
1966 exp = g_strdup (old);
1967 } else {
1969 #ifdef HAVE_CHARSET
1970 if (exp && *exp)
1971 convert_to_display (exp);
1972 #endif /* HAVE_CHARSET */
1974 edit_search_dialog (edit, &exp);
1976 #ifdef HAVE_CHARSET
1977 if (exp && *exp)
1978 convert_from_input (exp);
1979 #endif /* HAVE_CHARSET */
1981 edit_push_action (edit, KEY_PRESS + edit->start_display);
1984 if (exp) {
1985 if (*exp) {
1986 int len = 0;
1987 if (old)
1988 g_free (old);
1989 old = g_strdup (exp);
1991 if (search_create_bookmark) {
1992 int found = 0, books = 0;
1993 int l = 0, l_last = -1;
1994 long p, q = 0;
1995 for (;;) {
1996 p = edit_find (q, (unsigned char *) exp, &len, edit->last_byte,
1997 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
1998 if (p < 0)
1999 break;
2000 found++;
2001 l += edit_count_lines (edit, q, p);
2002 if (l != l_last) {
2003 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
2004 books++;
2006 l_last = l;
2007 q = p + 1;
2009 if (found) {
2010 /* in response to number of bookmarks added because of string being found %d times */
2011 message (0, _("Search"), _(" %d finds made, %d bookmarks added "), found, books);
2012 } else {
2013 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
2015 } else {
2017 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
2018 edit->search_start--;
2020 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
2021 edit->search_start++;
2023 edit->search_start = edit_find (edit->search_start, (unsigned char *) exp, &len, edit->last_byte,
2024 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
2026 if (edit->search_start >= 0) {
2027 edit->found_start = edit->search_start;
2028 edit->found_len = len;
2030 edit_cursor_move (edit, edit->search_start - edit->curs1);
2031 edit_scroll_screen_over_cursor (edit);
2032 if (replace_backwards)
2033 edit->search_start--;
2034 else
2035 edit->search_start++;
2036 } else if (edit->search_start == -3) {
2037 edit->search_start = edit->curs1;
2038 regexp_error (edit);
2039 } else {
2040 edit->search_start = edit->curs1;
2041 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
2045 g_free (exp);
2047 edit->force |= REDRAW_COMPLETELY;
2048 edit_scroll_screen_over_cursor (edit);
2052 /* Real edit only */
2053 void edit_quit_cmd (WEdit * edit)
2055 edit_push_action (edit, KEY_PRESS + edit->start_display);
2057 edit->force |= REDRAW_COMPLETELY;
2058 if (edit->modified) {
2059 switch (edit_query_dialog3 (_ ("Quit"), _ (" File was modified, Save with exit? "), _ ("Cancel quit"), _ ("&Yes"), _ ("&No"))) {
2060 case 1:
2061 edit_push_markers (edit);
2062 edit_set_markers (edit, 0, 0, 0, 0);
2063 if (!edit_save_cmd (edit))
2064 return;
2065 break;
2066 case 2:
2067 if (edit->locked)
2068 edit->locked = edit_unlock_file (edit->filename);
2069 if (edit->delete_file)
2070 unlink (edit->filename);
2071 break;
2072 case 0:
2073 case -1:
2074 return;
2077 else if (edit->delete_file)
2078 unlink (edit->filename);
2079 dlg_stop (edit->widget.parent);
2082 #define TEMP_BUF_LEN 1024
2084 /* Return a null terminated length of text. Result must be g_free'd */
2085 unsigned char *
2086 edit_get_block (WEdit *edit, long start, long finish, int *l)
2088 unsigned char *s, *r;
2089 r = s = g_malloc (finish - start + 1);
2090 if (column_highlighting) {
2091 *l = 0;
2092 /* copy from buffer, excluding chars that are out of the column 'margins' */
2093 while (start < finish) {
2094 int c, x;
2095 x = edit_move_forward3 (edit, edit_bol (edit, start), 0,
2096 start);
2097 c = edit_get_byte (edit, start);
2098 if ((x >= edit->column1 && x < edit->column2)
2099 || (x >= edit->column2 && x < edit->column1) || c == '\n') {
2100 *s++ = c;
2101 (*l)++;
2103 start++;
2105 } else {
2106 *l = finish - start;
2107 while (start < finish)
2108 *s++ = edit_get_byte (edit, start++);
2110 *s = 0;
2111 return r;
2114 /* save block, returns 1 on success */
2116 edit_save_block (WEdit * edit, const char *filename, long start,
2117 long finish)
2119 int len, file;
2121 if ((file =
2122 mc_open (filename, O_CREAT | O_WRONLY | O_TRUNC,
2123 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY)) == -1)
2124 return 0;
2126 if (column_highlighting) {
2127 unsigned char *block, *p;
2128 int r;
2129 p = block = edit_get_block (edit, start, finish, &len);
2130 while (len) {
2131 r = mc_write (file, p, len);
2132 if (r < 0)
2133 break;
2134 p += r;
2135 len -= r;
2137 g_free (block);
2138 } else {
2139 unsigned char *buf;
2140 int i = start, end;
2141 len = finish - start;
2142 buf = g_malloc (TEMP_BUF_LEN);
2143 while (start != finish) {
2144 end = min (finish, start + TEMP_BUF_LEN);
2145 for (; i < end; i++)
2146 buf[i - start] = edit_get_byte (edit, i);
2147 len -= mc_write (file, (char *) buf, end - start);
2148 start = end;
2150 g_free (buf);
2152 mc_close (file);
2153 if (len)
2154 return 0;
2155 return 1;
2158 /* copies a block to clipboard file */
2159 static int edit_save_block_to_clip_file (WEdit * edit, long start, long finish)
2161 return edit_save_block (edit, catstrs (home_dir, CLIP_FILE, 0), start, finish);
2165 void edit_paste_from_history (WEdit *edit)
2169 int edit_copy_to_X_buf_cmd (WEdit * edit)
2171 long start_mark, end_mark;
2172 if (eval_marks (edit, &start_mark, &end_mark))
2173 return 0;
2174 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2175 edit_error_dialog (_(" Copy to clipboard "), get_sys_error (_(" Unable to save to file. ")));
2176 return 1;
2178 edit_mark_cmd (edit, 1);
2179 return 0;
2182 int edit_cut_to_X_buf_cmd (WEdit * edit)
2184 long start_mark, end_mark;
2185 if (eval_marks (edit, &start_mark, &end_mark))
2186 return 0;
2187 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2188 edit_error_dialog (_(" Cut to clipboard "), _(" Unable to save to file. "));
2189 return 1;
2191 edit_block_delete_cmd (edit);
2192 edit_mark_cmd (edit, 1);
2193 return 0;
2196 void edit_paste_from_X_buf_cmd (WEdit * edit)
2198 edit_insert_file (edit, catstrs (home_dir, CLIP_FILE, 0));
2203 * Ask user for the line and go to that line.
2204 * Negative numbers mean line from the end (i.e. -1 is the last line).
2206 void
2207 edit_goto_cmd (WEdit *edit)
2209 char *f;
2210 static long line = 0; /* line as typed, saved as default */
2211 long l;
2212 char *error;
2213 char s[32];
2215 g_snprintf (s, sizeof (s), "%ld", line);
2216 f = input_dialog (_(" Goto line "), _(" Enter line: "), line ? s : "");
2217 if (!f)
2218 return;
2220 if (!*f) {
2221 g_free (f);
2222 return;
2225 l = strtol (f, &error, 0);
2226 if (*error) {
2227 g_free (f);
2228 return;
2231 line = l;
2232 if (l < 0)
2233 l = edit->total_lines + l + 2;
2234 edit_move_display (edit, l - edit->num_widget_lines / 2 - 1);
2235 edit_move_to_line (edit, l - 1);
2236 edit->force |= REDRAW_COMPLETELY;
2237 g_free (f);
2241 /* Return 1 on success */
2243 edit_save_block_cmd (WEdit *edit)
2245 long start_mark, end_mark;
2246 char *exp;
2247 if (eval_marks (edit, &start_mark, &end_mark))
2248 return 1;
2249 exp =
2250 edit_get_save_file (catstrs (home_dir, CLIP_FILE, 0),
2251 _(" Save Block "));
2252 edit_push_action (edit, KEY_PRESS + edit->start_display);
2253 if (exp) {
2254 if (!*exp) {
2255 g_free (exp);
2256 return 0;
2257 } else {
2258 if (edit_save_block (edit, exp, start_mark, end_mark)) {
2259 g_free (exp);
2260 edit->force |= REDRAW_COMPLETELY;
2261 return 1;
2262 } else {
2263 g_free (exp);
2264 edit_error_dialog (_(" Save Block "),
2265 get_sys_error (_
2266 (" Cannot save file. ")));
2270 edit->force |= REDRAW_COMPLETELY;
2271 return 0;
2275 /* returns 1 on success */
2277 edit_insert_file_cmd (WEdit *edit)
2279 char *exp = edit_get_load_file (catstrs (home_dir, CLIP_FILE, 0),
2280 _(" Insert File "));
2281 edit_push_action (edit, KEY_PRESS + edit->start_display);
2282 if (exp) {
2283 if (!*exp) {
2284 g_free (exp);
2285 return 0;
2286 } else {
2287 if (edit_insert_file (edit, exp)) {
2288 g_free (exp);
2289 edit->force |= REDRAW_COMPLETELY;
2290 return 1;
2291 } else {
2292 g_free (exp);
2293 edit_error_dialog (_(" Insert File "),
2294 get_sys_error (_
2295 (" Cannot insert file. ")));
2299 edit->force |= REDRAW_COMPLETELY;
2300 return 0;
2303 /* sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
2304 int edit_sort_cmd (WEdit * edit)
2306 static char *old = 0;
2307 char *exp;
2308 long start_mark, end_mark;
2309 int e;
2311 if (eval_marks (edit, &start_mark, &end_mark)) {
2312 edit_error_dialog (_(" Sort block "), _(" You must first highlight a block of text. "));
2313 return 0;
2315 edit_save_block (edit, catstrs (home_dir, BLOCK_FILE, 0), start_mark, end_mark);
2317 exp = old ? old : "";
2319 exp = input_dialog (_(" Run Sort "),
2320 _(" Enter sort options (see manpage) separated by whitespace: "), exp);
2322 if (!exp)
2323 return 1;
2324 if (old)
2325 g_free (old);
2326 old = exp;
2328 e = system (catstrs (" sort ", exp, " ", home_dir, BLOCK_FILE, " > ", home_dir, TEMP_FILE, 0));
2329 if (e) {
2330 if (e == -1 || e == 127) {
2331 edit_error_dialog (_(" Sort "),
2332 get_sys_error (_(" Cannot execute sort command ")));
2333 } else {
2334 char q[8];
2335 sprintf (q, "%d ", e);
2336 edit_error_dialog (_(" Sort "),
2337 catstrs (_(" Sort returned non-zero: "), q, 0));
2339 return -1;
2342 edit->force |= REDRAW_COMPLETELY;
2344 if (edit_block_delete_cmd (edit))
2345 return 1;
2346 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, 0));
2347 return 0;
2351 * Ask user for a command, execute it and paste its output back to the
2352 * editor.
2355 edit_ext_cmd (WEdit *edit)
2357 char *exp;
2358 int e;
2360 exp =
2361 input_dialog (_("Paste output of external command"),
2362 _("Enter shell command(s):"), NULL);
2364 if (!exp)
2365 return 1;
2367 e = system (catstrs (exp, " > ", home_dir, TEMP_FILE, 0));
2368 g_free (exp);
2370 if (e) {
2371 edit_error_dialog (_("External command"),
2372 get_sys_error (_("Cannot execute command")));
2373 return -1;
2376 edit->force |= REDRAW_COMPLETELY;
2378 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, 0));
2379 return 0;
2382 /* if block is 1, a block must be highlighted and the shell command
2383 processes it. If block is 0 the shell command is a straight system
2384 command, that just produces some output which is to be inserted */
2385 void
2386 edit_block_process_cmd (WEdit *edit, const char *shell_cmd, int block)
2388 long start_mark, end_mark;
2389 char buf[BUFSIZ];
2390 FILE *script_home = NULL;
2391 FILE *script_src = NULL;
2392 FILE *block_file = NULL;
2393 char *o = NULL;
2394 char *h = NULL;
2395 char *b = NULL;
2396 char *quoted_name = NULL;
2398 o = catstrs (mc_home, shell_cmd, 0); /* original source script */
2399 h = catstrs (home_dir, EDIT_DIR, shell_cmd, 0); /* home script */
2400 b = catstrs (home_dir, BLOCK_FILE, 0); /* block file */
2402 if (!(script_home = fopen (h, "r"))) {
2403 if (!(script_home = fopen (h, "w"))) {
2404 edit_error_dialog ("", get_sys_error (catstrs
2406 ("Error creating script:"),
2407 h, 0)));
2408 return;
2410 if (!(script_src = fopen (o, "r"))) {
2411 fclose (script_home);
2412 unlink (h);
2413 edit_error_dialog ("", get_sys_error (catstrs
2414 (_("Error reading script:"),
2415 o, 0)));
2416 return;
2418 while (fgets (buf, sizeof (buf), script_src))
2419 fputs (buf, script_home);
2420 if (fclose (script_home)) {
2421 edit_error_dialog ("", get_sys_error (catstrs
2423 ("Error closing script:"),
2424 h, 0)));
2425 return;
2427 chmod (h, 0700);
2428 edit_error_dialog ("", get_sys_error (catstrs
2429 (_("Script created:"), h, 0)));
2432 open_error_pipe ();
2434 if (block) { /* for marked block run indent formatter */
2435 if (eval_marks (edit, &start_mark, &end_mark)) {
2436 edit_error_dialog (_("Process block"),
2438 (" You must first highlight a block of text. "));
2439 return;
2441 edit_save_block (edit, b, start_mark, end_mark);
2442 quoted_name = name_quote (edit->filename, 0);
2444 * Run script.
2445 * Initial space is to avoid polluting bash history.
2446 * Arguments:
2447 * $1 - name of the edited file (to check its extension etc).
2448 * $2 - file containing the current block.
2449 * $3 - file where error messages should be put
2450 * (for compatibility with old scripts).
2452 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ", quoted_name,
2453 " ", home_dir, BLOCK_FILE " /dev/null", NULL));
2455 } else {
2457 * No block selected, just execute the command for the file.
2458 * Arguments:
2459 * $1 - name of the edited file.
2461 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2462 quoted_name, NULL));
2464 g_free (quoted_name);
2465 close_error_pipe (0, 0);
2467 edit_refresh_cmd (edit);
2468 edit->force |= REDRAW_COMPLETELY;
2470 /* insert result block */
2471 if (block) {
2472 if (edit_block_delete_cmd (edit))
2473 return;
2474 edit_insert_file (edit, b);
2475 if ((block_file = fopen (b, "w")))
2476 fclose (block_file);
2477 return;
2480 return;
2483 /* prints at the cursor */
2484 /* returns the number of chars printed */
2485 int edit_print_string (WEdit * e, const char *s)
2487 int i = 0;
2488 while (s[i])
2489 edit_execute_cmd (e, -1, (unsigned char) s[i++]);
2490 e->force |= REDRAW_COMPLETELY;
2491 edit_update_screen (e);
2492 return i;
2495 int edit_printf (WEdit * e, const char *fmt, ...)
2497 int i;
2498 va_list pa;
2499 char s[1024];
2500 va_start (pa, fmt);
2501 g_vsnprintf (s, sizeof (s), fmt, pa);
2502 i = edit_print_string (e, s);
2503 va_end (pa);
2504 return i;
2507 /* FIXME: does this function break NT_OS2 ? */
2509 static void pipe_mail (WEdit *edit, char *to, char *subject, char *cc)
2511 FILE *p = 0;
2512 char *s;
2514 to = name_quote (to, 0);
2515 subject = name_quote (subject, 0);
2516 cc = name_quote (cc, 0);
2517 s = g_strdup_printf ("mail -s %s -c %s %s", subject, cc, to);
2518 g_free (to);
2519 g_free (subject);
2520 g_free (cc);
2522 if (s) {
2523 p = popen (s, "w");
2524 g_free (s);
2527 if (p) {
2528 long i;
2529 for (i = 0; i < edit->last_byte; i++)
2530 fputc (edit_get_byte (edit, i), p);
2531 pclose (p);
2535 #define MAIL_DLG_HEIGHT 12
2537 void edit_mail_dialog (WEdit * edit)
2539 char *tmail_to;
2540 char *tmail_subject;
2541 char *tmail_cc;
2543 static char *mail_cc_last = 0;
2544 static char *mail_subject_last = 0;
2545 static char *mail_to_last = 0;
2547 QuickDialog Quick_input =
2548 {50, MAIL_DLG_HEIGHT, -1, 0, N_(" Mail "),
2549 "[Input Line Keys]", 0};
2551 QuickWidget quick_widgets[] =
2553 {quick_button, 6, 10, 9, MAIL_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
2554 0, NULL},
2555 {quick_button, 2, 10, 9, MAIL_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
2556 0, NULL},
2557 {quick_input, 3, 50, 8, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2558 0, "mail-dlg-input"},
2559 {quick_label, 2, 50, 7, MAIL_DLG_HEIGHT, N_(" Copies to"), 0, 0, 0,
2560 0, 0},
2561 {quick_input, 3, 50, 6, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2562 0, "mail-dlg-input-2"},
2563 {quick_label, 2, 50, 5, MAIL_DLG_HEIGHT, N_(" Subject"), 0, 0, 0,
2564 0, 0},
2565 {quick_input, 3, 50, 4, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2566 0, "mail-dlg-input-3"},
2567 {quick_label, 2, 50, 3, MAIL_DLG_HEIGHT, N_(" To"), 0, 0, 0,
2568 0, 0},
2569 {quick_label, 2, 50, 2, MAIL_DLG_HEIGHT, N_(" mail -s <subject> -c <cc> <to>"), 0, 0, 0,
2570 0, 0},
2571 {0}};
2573 quick_widgets[2].str_result = &tmail_cc;
2574 quick_widgets[2].text = mail_cc_last ? mail_cc_last : "";
2575 quick_widgets[4].str_result = &tmail_subject;
2576 quick_widgets[4].text = mail_subject_last ? mail_subject_last : "";
2577 quick_widgets[6].str_result = &tmail_to;
2578 quick_widgets[6].text = mail_to_last ? mail_to_last : "";
2580 Quick_input.widgets = quick_widgets;
2582 if (quick_dialog (&Quick_input) != B_CANCEL) {
2583 if (mail_cc_last)
2584 g_free (mail_cc_last);
2585 if (mail_subject_last)
2586 g_free (mail_subject_last);
2587 if (mail_to_last)
2588 g_free (mail_to_last);
2589 mail_cc_last = *(quick_widgets[2].str_result);
2590 mail_subject_last = *(quick_widgets[4].str_result);
2591 mail_to_last = *(quick_widgets[6].str_result);
2592 pipe_mail (edit, mail_to_last, mail_subject_last, mail_cc_last);
2597 /*******************/
2598 /* Word Completion */
2599 /*******************/
2602 /* find first character of current word */
2603 static int edit_find_word_start (WEdit *edit, long *word_start, int *word_len)
2605 int i, c, last;
2607 /* return if at begin of file */
2608 if (edit->curs1 <= 0)
2609 return 0;
2611 c = (unsigned char) edit_get_byte (edit, edit->curs1 - 1);
2612 /* return if not at end or in word */
2613 if (isspace (c) || !(isalnum (c) || c == '_'))
2614 return 0;
2616 /* search start of word to be completed */
2617 for (i = 2;; i++) {
2618 /* return if at begin of file */
2619 if (edit->curs1 - i < 0)
2620 return 0;
2622 last = c;
2623 c = (unsigned char) edit_get_byte (edit, edit->curs1 - i);
2625 if (!(isalnum (c) || c == '_')) {
2626 /* return if word starts with digit */
2627 if (isdigit (last))
2628 return 0;
2630 *word_start = edit->curs1 - (i - 1); /* start found */
2631 *word_len = i - 1;
2632 break;
2635 /* success */
2636 return 1;
2640 /* (re)set search parameters to the given values */
2641 static void edit_set_search_parameters (int rs, int rb, int rr, int rw, int rc)
2643 replace_scanf = rs;
2644 replace_backwards = rb;
2645 replace_regexp = rr;
2646 replace_whole = rw;
2647 replace_case = rc;
2651 #define MAX_WORD_COMPLETIONS 100 /* in listbox */
2653 /* collect the possible completions */
2654 static int
2655 edit_collect_completions (WEdit *edit, long start, int word_len,
2656 char *match_expr, struct selection *compl,
2657 int *num)
2659 int len, max_len = 0, i, skip;
2660 char *bufpos;
2662 /* collect max MAX_WORD_COMPLETIONS completions */
2663 while (*num < MAX_WORD_COMPLETIONS) {
2664 /* get next match */
2665 start =
2666 edit_find (start - 1, (unsigned char *) match_expr, &len,
2667 edit->last_byte,
2668 (int (*)(void *, long)) edit_get_byte,
2669 (void *) edit, 0);
2671 /* not matched */
2672 if (start < 0)
2673 break;
2675 /* add matched completion if not yet added */
2676 bufpos =
2677 &edit->
2678 buffers1[start >> S_EDIT_BUF_SIZE][start & M_EDIT_BUF_SIZE];
2679 skip = 0;
2680 for (i = 0; i < *num; i++) {
2681 if (strncmp
2682 (&compl[i].text[word_len], &bufpos[word_len],
2683 max (len, compl[i].len) - word_len) == 0) {
2684 skip = 1;
2685 break; /* skip it, already added */
2688 if (skip)
2689 continue;
2691 compl[*num].text = g_malloc (len + 1);
2692 compl[*num].len = len;
2693 for (i = 0; i < len; i++)
2694 compl[*num].text[i] = *(bufpos + i);
2695 compl[*num].text[i] = '\0';
2696 (*num)++;
2698 /* note the maximal length needed for the completion dialog */
2699 if (len > max_len)
2700 max_len = len;
2702 return max_len;
2706 static int
2707 compllist_callback (void *data)
2709 return 0;
2713 /* let the user select its preferred completion */
2714 static void
2715 edit_completion_dialog (WEdit *edit, int max_len, int word_len,
2716 struct selection *compl, int num_compl)
2718 int start_x, start_y, offset, i;
2719 char *curr = NULL;
2720 Dlg_head *compl_dlg;
2721 WListbox *compl_list;
2722 unsigned int compl_dlg_h; /* completion dialog height */
2723 unsigned int compl_dlg_w; /* completion dialog width */
2725 /* calculate the dialog metrics */
2726 compl_dlg_h = num_compl + 2;
2727 compl_dlg_w = max_len + 4;
2728 start_x = edit->curs_col + edit->start_col - (compl_dlg_w / 2);
2729 start_y = edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + 1;
2731 if (start_x < 0)
2732 start_x = 0;
2733 if (compl_dlg_w > COLS)
2734 compl_dlg_w = COLS;
2735 if (compl_dlg_h > LINES - 2)
2736 compl_dlg_h = LINES - 2;
2738 offset = start_x + compl_dlg_w - COLS;
2739 if (offset > 0)
2740 start_x -= offset;
2741 offset = start_y + compl_dlg_h - LINES;
2742 if (offset > 0)
2743 start_y -= (offset + 1);
2745 /* create the dialog */
2746 compl_dlg =
2747 create_dlg (start_y, start_x, compl_dlg_h, compl_dlg_w,
2748 dialog_colors, NULL, "[Completion]", NULL,
2749 DLG_COMPACT);
2751 /* create the listbox */
2752 compl_list =
2753 listbox_new (1, 1, compl_dlg_w - 2, compl_dlg_h - 2, 0,
2754 compllist_callback, NULL);
2756 /* add the dialog */
2757 add_widget (compl_dlg, compl_list);
2759 /* fill the listbox with the completions */
2760 for (i = 0; i < num_compl; i++)
2761 listbox_add_item (compl_list, 0, 0, compl[i].text, NULL);
2763 /* pop up the dialog */
2764 run_dlg (compl_dlg);
2766 /* apply the choosen completion */
2767 if (compl_dlg->ret_value == B_ENTER) {
2768 listbox_get_current (compl_list, &curr, NULL);
2769 if (curr)
2770 for (curr += word_len; *curr; curr++)
2771 edit_insert (edit, *curr);
2774 /* destroy dialog before return */
2775 destroy_dlg (compl_dlg);
2780 * Complete current word using regular expression search
2781 * backwards beginning at the current cursor position.
2783 void
2784 edit_complete_word_cmd (WEdit *edit)
2786 int word_len = 0, i, num_compl = 0, max_len;
2787 long word_start = 0;
2788 char *bufpos;
2789 char match_expr[MAX_REPL_LEN];
2790 struct selection compl[MAX_WORD_COMPLETIONS]; /* completions */
2792 /* don't want to disturb another search */
2793 int old_rs = replace_scanf;
2794 int old_rb = replace_backwards;
2795 int old_rr = replace_regexp;
2796 int old_rw = replace_whole;
2797 int old_rc = replace_case;
2799 /* search start of word to be completed */
2800 if (!edit_find_word_start (edit, &word_start, &word_len))
2801 return;
2803 /* prepare match expression */
2804 bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE]
2805 [word_start & M_EDIT_BUF_SIZE];
2806 strncpy (match_expr, bufpos, word_len);
2807 match_expr[word_len] = '\0';
2808 strcat (match_expr, "[a-zA-Z_0-9]+");
2810 /* init search: backward, regexp, whole word, case sensitive */
2811 edit_set_search_parameters (0, 1, 1, 1, 1);
2813 /* collect the possible completions */
2814 /* start search from curs1 down to begin of file */
2815 max_len =
2816 edit_collect_completions (edit, word_start, word_len, match_expr,
2817 (struct selection *) &compl, &num_compl);
2819 if (num_compl > 0) {
2820 /* insert completed word if there is only one match */
2821 if (num_compl == 1) {
2822 for (i = word_len; i < compl[0].len; i++)
2823 edit_insert (edit, *(compl[0].text + i));
2825 /* more than one possible completion => ask the user */
2826 else {
2827 /* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
2828 /* !!! pressed again the selection dialog pops up, but that !!! */
2829 /* !!! seems to require a further internal state !!! */
2830 /*beep (); */
2832 /* let the user select the preferred completion */
2833 edit_completion_dialog (edit, max_len, word_len,
2834 (struct selection *) &compl,
2835 num_compl);
2839 /* release memory before return */
2840 for (i = 0; i < num_compl; i++)
2841 g_free (compl[i].text);
2843 /* restore search parameters */
2844 edit_set_search_parameters (old_rs, old_rb, old_rr, old_rw, old_rc);