Updated italian translatio
[midnight-commander.git] / edit / editcmd.c
blobe52cf1728847d423467f6521b4c94d1432e54b17
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/layout.h" /* clr_scr() */
38 #include "../src/main.h" /* mc_home */
39 #include "../src/help.h" /* interactive_display() */
40 #include "../src/key.h" /* XCTRL */
41 #include "../src/dialog.h" /* do_refresh() */
42 #include "../src/wtools.h" /* message() */
43 #include "../src/charsets.h"
45 #define edit_get_load_file(f,h) input_dialog (h, _(" Enter file name: "), f)
46 #define edit_get_save_file(f,h) input_dialog (h, _(" Enter file name: "), f)
48 struct selection {
49 unsigned char * text;
50 int len;
53 /* globals: */
55 /* search and replace: */
56 static int replace_scanf = 0;
57 static int replace_regexp = 0;
58 static int replace_all = 0;
59 static int replace_prompt = 1;
60 static int replace_whole = 0;
61 static int replace_case = 0;
62 static int replace_backwards = 0;
63 static int search_create_bookmark = 0;
65 /* queries on a save */
66 int edit_confirm_save = 1;
68 #define NUM_REPL_ARGS 64
69 #define MAX_REPL_LEN 1024
71 static int edit_save_cmd (WEdit *edit);
72 static unsigned char *edit_get_block (WEdit *edit, long start,
73 long finish, int *l);
75 static inline int my_lower_case (int c)
77 return tolower(c & 0xFF);
80 static char *strcasechr (const unsigned char *s, int c)
82 for (c = my_lower_case (c); my_lower_case ((int) *s) != c; ++s)
83 if (*s == '\0')
84 return 0;
85 return (char *) s;
88 #ifndef HAVE_MEMMOVE
89 /* for Christophe */
90 static void *memmove (void *dest, const void *src, size_t n)
92 char *t, *s;
94 if (dest <= src) {
95 t = (char *) dest;
96 s = (char *) src;
97 while (n--)
98 *t++ = *s++;
99 } else {
100 t = (char *) dest + n;
101 s = (char *) src + n;
102 while (n--)
103 *--t = *--s;
105 return dest;
107 #endif /* !HAVE_MEMMOVE */
109 /* #define itoa MY_itoa <---- this line is now in edit.h */
110 static char *
111 MY_itoa (int i)
113 static char t[14];
114 char *s = t + 13;
115 int j = i;
116 *s-- = 0;
117 do {
118 *s-- = i % 10 + '0';
119 } while ((i = i / 10));
120 if (j < 0)
121 *s-- = '-';
122 return ++s;
125 /* Temporary strings */
126 static char *stacked[16];
129 This joins strings end on end and allocates memory for the result.
130 The result is later automatically free'd and must not be free'd
131 by the caller.
133 char *catstrs (const char *first,...)
135 static int i = 0;
136 va_list ap;
137 int len;
138 char *data;
140 if (!first)
141 return 0;
143 len = strlen (first);
144 va_start (ap, first);
146 while ((data = va_arg (ap, char *)) != 0)
147 len += strlen (data);
149 len++;
151 i = (i + 1) % 16;
152 g_free (stacked[i]);
154 stacked[i] = g_malloc (len);
155 va_end (ap);
156 va_start (ap, first);
157 strcpy (stacked[i], first);
158 while ((data = va_arg (ap, char *)) != 0)
159 strcat (stacked[i], data);
160 va_end (ap);
162 return stacked[i];
165 /* Free temporary strings */
166 void freestrs(void)
168 int i;
170 for (i = 0; i < sizeof(stacked) / sizeof(stacked[0]); i++) {
171 g_free (stacked[i]);
172 stacked[i] = NULL;
176 void edit_help_cmd (WEdit * edit)
178 interactive_display (NULL, "[Internal File Editor]");
179 edit->force |= REDRAW_COMPLETELY;
182 void edit_refresh_cmd (WEdit * edit)
184 #ifndef HAVE_SLANG
185 clr_scr();
186 do_refresh();
187 #else
189 int color;
190 edit_get_syntax_color (edit, -1, &color);
192 touchwin(stdscr);
193 #endif /* !HAVE_SLANG */
194 mc_refresh();
195 doupdate();
198 /* If 0 (quick save) then a) create/truncate <filename> file,
199 b) save to <filename>;
200 if 1 (safe save) then a) save to <tempnam>,
201 b) rename <tempnam> to <filename>;
202 if 2 (do backups) then a) save to <tempnam>,
203 b) rename <filename> to <filename.backup_ext>,
204 c) rename <tempnam> to <filename>. */
206 /* returns 0 on error */
207 static int
208 edit_save_file (WEdit *edit, const char *filename)
210 char *p;
211 long filelen = 0;
212 char *savename = 0;
213 int this_save_mode, fd;
215 if (!filename)
216 return 0;
217 if (!*filename)
218 return 0;
220 if (*filename != PATH_SEP && edit->dir) {
221 savename = concat_dir_and_file (edit->dir, filename);
222 filename = catstrs (savename, NULL);
223 g_free (savename);
226 if (!vfs_file_is_local (filename) ||
227 (fd = mc_open (filename, O_WRONLY | O_BINARY)) == -1) {
229 * The file does not exists yet, so no safe save or
230 * backup are necessary.
232 this_save_mode = EDIT_QUICK_SAVE;
233 } else {
234 mc_close (fd);
235 this_save_mode = option_save_mode;
238 if (this_save_mode != EDIT_QUICK_SAVE) {
239 char *savedir, *slashpos, *saveprefix;
240 slashpos = strrchr (filename, PATH_SEP);
241 if (slashpos) {
242 savedir = g_strdup (filename);
243 savedir[slashpos - filename + 1] = '\0';
244 } else
245 savedir = g_strdup (".");
246 saveprefix = concat_dir_and_file (savedir, "cooledit");
247 g_free (savedir);
248 fd = mc_mkstemps (&savename, saveprefix, NULL);
249 g_free (saveprefix);
250 if (!savename)
251 return 0;
252 /* FIXME:
253 * Close for now because mc_mkstemps use pure open system call
254 * to create temporary file and it needs to be reopened by
255 * VFS-aware mc_open().
257 close (fd);
258 } else
259 savename = g_strdup (filename);
261 mc_chown (savename, edit->stat1.st_uid, edit->stat1.st_gid);
262 mc_chmod (savename, edit->stat1.st_mode);
264 if ((fd =
265 mc_open (savename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
266 edit->stat1.st_mode)) == -1)
267 goto error_save;
269 /* pipe save */
270 if ((p = edit_get_write_filter (savename, filename))) {
271 FILE *file;
273 mc_close (fd);
274 file = (FILE *) popen (p, "w");
276 if (file) {
277 filelen = edit_write_stream (edit, file);
278 #if 1
279 pclose (file);
280 #else
281 if (pclose (file) != 0) {
282 edit_error_dialog (_("Error"),
283 catstrs (_(" Error writing to pipe: "),
284 p, " ", 0));
285 g_free (p);
286 goto error_save;
288 #endif
289 } else {
290 edit_error_dialog (_("Error"),
291 get_sys_error (catstrs
293 (" Cannot open pipe for writing: "),
294 p, " ", 0)));
295 g_free (p);
296 goto error_save;
298 g_free (p);
299 } else {
300 long buf;
301 buf = 0;
302 filelen = edit->last_byte;
303 while (buf <= (edit->curs1 >> S_EDIT_BUF_SIZE) - 1) {
304 if (mc_write (fd, (char *) edit->buffers1[buf], EDIT_BUF_SIZE)
305 != EDIT_BUF_SIZE) {
306 mc_close (fd);
307 goto error_save;
309 buf++;
311 if (mc_write
312 (fd, (char *) edit->buffers1[buf],
313 edit->curs1 & M_EDIT_BUF_SIZE) !=
314 (edit->curs1 & M_EDIT_BUF_SIZE)) {
315 filelen = -1;
316 } else if (edit->curs2) {
317 edit->curs2--;
318 buf = (edit->curs2 >> S_EDIT_BUF_SIZE);
319 if (mc_write
320 (fd,
321 (char *) edit->buffers2[buf] + EDIT_BUF_SIZE -
322 (edit->curs2 & M_EDIT_BUF_SIZE) - 1,
323 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) !=
324 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) {
325 filelen = -1;
326 } else {
327 while (--buf >= 0) {
328 if (mc_write
329 (fd, (char *) edit->buffers2[buf],
330 EDIT_BUF_SIZE) != EDIT_BUF_SIZE) {
331 filelen = -1;
332 break;
336 edit->curs2++;
338 if (mc_close (fd))
339 goto error_save;
342 if (filelen != edit->last_byte)
343 goto error_save;
344 if (this_save_mode == EDIT_DO_BACKUP)
345 if (mc_rename (filename, catstrs (filename, option_backup_ext, 0))
346 == -1)
347 goto error_save;
348 if (this_save_mode != EDIT_QUICK_SAVE)
349 if (mc_rename (savename, filename) == -1)
350 goto error_save;
351 g_free (savename);
352 return 1;
353 error_save:
354 /* FIXME: Is this safe ?
355 * if (this_save_mode != EDIT_QUICK_SAVE)
356 * mc_unlink (savename);
358 g_free (savename);
359 return 0;
363 I changed this from Oleg's original routine so
364 that option_backup_ext works with coolwidgets as well. This
365 does mean there is a memory leak - paul.
367 void menu_save_mode_cmd (void)
369 #define DLG_X 38
370 #define DLG_Y 10
371 static char *str_result;
372 static int save_mode_new;
373 static char *str[] =
375 N_("Quick save "),
376 N_("Safe save "),
377 N_("Do backups -->")};
378 static QuickWidget widgets[] =
380 {quick_button, 18, DLG_X, 7, DLG_Y, N_("&Cancel"), 0,
381 B_CANCEL, 0, 0, "c"},
382 {quick_button, 6, DLG_X, 7, DLG_Y, N_("&OK"), 0,
383 B_ENTER, 0, 0, "o"},
384 {quick_input, 23, DLG_X, 5, DLG_Y, 0, 9,
385 0, 0, &str_result, "edit-backup-ext"},
386 {quick_label, 22, DLG_X, 4, DLG_Y, N_("Extension:"), 0,
387 0, 0, 0, "savemext"},
388 {quick_radio, 4, DLG_X, 3, DLG_Y, "", 3,
389 0, &save_mode_new, str, "t"},
390 {0}};
391 static QuickDialog dialog =
392 {DLG_X, DLG_Y, -1, -1, N_(" Edit Save Mode "), "[Edit Save Mode]",
393 widgets};
394 static int i18n_flag = 0;
396 if (!i18n_flag) {
397 int i;
398 int maxlen = 0;
399 int dlg_x;
400 int l1;
402 /* OK/Cancel buttons */
403 l1 = strlen (_(widgets[0].text)) + strlen (_(widgets[1].text)) + 5;
404 maxlen = max (maxlen, l1);
406 for (i = 0; i < 3; i++ ) {
407 str[i] = _(str[i]);
408 maxlen = max (maxlen, strlen (str[i]) + 7);
410 i18n_flag = 1;
412 dlg_x = maxlen + strlen (_(widgets[3].text)) + 5 + 1;
413 widgets[2].hotkey_pos = strlen (_(widgets[3].text)); /* input field length */
414 dlg_x = min (COLS, dlg_x);
415 dialog.xlen = dlg_x;
417 i = (dlg_x - l1)/3;
418 widgets[1].relative_x = i;
419 widgets[0].relative_x = i + strlen (_(widgets[1].text)) + i + 4;
421 widgets[2].relative_x = widgets[3].relative_x = maxlen + 2;
423 for (i = 0; i < sizeof (widgets)/sizeof (widgets[0]); i++)
424 widgets[i].x_divisions = dlg_x;
427 widgets[2].text = option_backup_ext;
428 widgets[4].value = option_save_mode;
429 if (quick_dialog (&dialog) != B_ENTER)
430 return;
431 option_save_mode = save_mode_new;
432 option_backup_ext = str_result; /* this is a memory leak */
433 option_backup_ext_int = 0;
434 str_result[min (strlen (str_result), sizeof (int))] = '\0';
435 memcpy ((char *) &option_backup_ext_int, str_result, strlen (option_backup_ext));
438 void
439 edit_set_filename (WEdit *edit, const char *f)
441 if (edit->filename)
442 g_free (edit->filename);
443 if (!f)
444 f = "";
445 edit->filename = g_strdup (f);
446 if (edit->dir == NULL && *f != PATH_SEP)
447 #ifdef USE_VFS
448 edit->dir = g_strdup (vfs_get_current_dir ());
449 #else
450 edit->dir = g_get_current_dir ();
451 #endif
454 /* Here we want to warn the users of overwriting an existing file,
455 but only if they have made a change to the filename */
456 /* returns 1 on success */
458 edit_save_as_cmd (WEdit *edit)
460 /* This heads the 'Save As' dialog box */
461 char *exp;
462 int save_lock = 0;
463 int different_filename = 0;
465 exp = edit_get_save_file (edit->filename, _(" Save As "));
466 edit_push_action (edit, KEY_PRESS + edit->start_display);
468 if (exp) {
469 if (!*exp) {
470 g_free (exp);
471 edit->force |= REDRAW_COMPLETELY;
472 return 0;
473 } else {
474 if (strcmp (edit->filename, exp)) {
475 int file;
476 different_filename = 1;
477 if ((file = mc_open (exp, O_RDONLY | O_BINARY)) != -1) {
478 /* the file exists */
479 mc_close (file);
480 /* Overwrite the current file or cancel the operation */
481 if (edit_query_dialog2
482 (_("Warning"),
483 _(" A file already exists with this name. "),
484 _("Overwrite"), _("Cancel"))) {
485 edit->force |= REDRAW_COMPLETELY;
486 g_free (exp);
487 return 0;
490 save_lock = edit_lock_file (exp);
491 } else {
492 /* filenames equal, check if already locked */
493 if (!edit->locked && !edit->delete_file)
494 save_lock = edit_lock_file (exp);
497 if (edit_save_file (edit, exp)) {
498 /* Succesful, so unlock both files */
499 if (strcmp (edit->filename, exp)) {
500 if (save_lock)
501 edit_unlock_file (exp);
502 if (edit->locked)
503 edit->locked = edit_unlock_file (edit->filename);
504 } else {
505 if (edit->locked || save_lock)
506 edit->locked = edit_unlock_file (edit->filename);
509 edit_set_filename (edit, exp);
510 g_free (exp);
511 edit->modified = 0;
512 edit->delete_file = 0;
513 if (different_filename)
514 edit_load_syntax (edit, 0, 0);
515 edit->force |= REDRAW_COMPLETELY;
516 return 1;
517 } else {
518 /* Failed, so maintain modify (not save) lock */
519 if (strcmp (edit->filename, exp) && save_lock)
520 edit_unlock_file (exp);
521 if (save_lock)
522 edit->locked = edit_unlock_file (edit->filename);
523 g_free (exp);
524 edit_error_dialog (_(" Save As "),
525 get_sys_error (_
526 (" Cannot save file. ")));
527 edit->force |= REDRAW_COMPLETELY;
528 return 0;
532 edit->force |= REDRAW_COMPLETELY;
533 return 0;
536 /* {{{ Macro stuff starts here */
538 static cb_ret_t
539 raw_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
541 switch (msg) {
542 case DLG_KEY:
543 h->running = 0;
544 h->ret_value = parm;
545 return MSG_HANDLED;
546 default:
547 return default_dlg_callback (h, msg, parm);
551 /* gets a raw key from the keyboard. Passing cancel = 1 draws
552 a cancel button thus allowing c-c etc. Alternatively, cancel = 0
553 will return the next key pressed. ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
554 and Esc are cannot returned */
556 edit_raw_key_query (char *heading, char *query, int cancel)
558 int w = strlen (query) + 7;
559 struct Dlg_head *raw_dlg =
560 create_dlg (0, 0, 7, w, dialog_colors, raw_callback,
561 NULL, heading,
562 DLG_CENTER | DLG_TRYUP | DLG_WANT_TAB);
563 add_widget (raw_dlg,
564 input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0));
565 add_widget (raw_dlg, label_new (3 - cancel, 2, query));
566 if (cancel)
567 add_widget (raw_dlg,
568 button_new (4, w / 2 - 5, B_CANCEL, NORMAL_BUTTON,
569 _("Cancel"), 0));
570 run_dlg (raw_dlg);
571 w = raw_dlg->ret_value;
572 destroy_dlg (raw_dlg);
573 if (cancel) {
574 if (w == XCTRL ('g') || w == XCTRL ('c') || w == ESC_CHAR
575 || w == B_CANCEL)
576 return 0;
579 return w;
582 /* creates a macro file if it doesn't exist */
583 static FILE *edit_open_macro_file (const char *r)
585 char *filename;
586 int file;
587 filename = catstrs (home_dir, MACRO_FILE, 0);
588 if ((file = open (filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
589 return 0;
590 close (file);
591 return fopen (filename, r);
594 #define MAX_MACROS 1024
595 static int saved_macro[MAX_MACROS + 1];
596 static int saved_macros_loaded = 0;
599 This is just to stop the macro file be loaded over and over for keys
600 that aren't defined to anything. On slow systems this could be annoying.
602 static int
603 macro_exists (int k)
605 int i;
606 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++)
607 if (saved_macro[i] == k)
608 return i;
609 return -1;
612 /* returns 1 on error */
613 static int
614 edit_delete_macro (WEdit * edit, int k)
616 struct macro macro[MAX_MACRO_LENGTH];
617 FILE *f, *g;
618 int s, i, n, j = 0;
620 if (saved_macros_loaded)
621 if ((j = macro_exists (k)) < 0)
622 return 0;
623 g = fopen (catstrs (home_dir, TEMP_FILE, 0), "w");
624 if (!g) {
625 /* This heads the delete macro error dialog box */
626 edit_error_dialog (_(" Delete macro "),
627 /* 'Open' = load temp file */
628 get_sys_error (_(" Cannot open temp file ")));
629 return 1;
631 f = edit_open_macro_file ("r");
632 if (!f) {
633 /* This heads the delete macro error dialog box */
634 edit_error_dialog (_(" Delete macro "),
635 /* 'Open' = load temp file */
636 get_sys_error (_(" Cannot open macro file ")));
637 fclose (g);
638 return 1;
640 for (;;) {
641 n = fscanf (f, ("key '%d 0': "), &s);
642 if (!n || n == EOF)
643 break;
644 n = 0;
645 while (fscanf (f, "%hd %hd, ", &macro[n].command, &macro[n].ch))
646 n++;
647 fscanf (f, ";\n");
648 if (s != k) {
649 fprintf (g, ("key '%d 0': "), s);
650 for (i = 0; i < n; i++)
651 fprintf (g, "%hd %hd, ", macro[i].command, macro[i].ch);
652 fprintf (g, ";\n");
655 fclose (f);
656 fclose (g);
657 if (rename (catstrs (home_dir, TEMP_FILE, 0), catstrs (home_dir, MACRO_FILE, 0)) == -1) {
658 /* This heads the delete macro error dialog box */
659 edit_error_dialog (_(" Delete macro "),
660 get_sys_error (_(" Cannot overwrite macro file ")));
661 return 1;
663 if (saved_macros_loaded)
664 memmove (saved_macro + j, saved_macro + j + 1, sizeof (int) * (MAX_MACROS - j - 1));
665 return 0;
668 /* returns 0 on error */
669 int edit_save_macro_cmd (WEdit * edit, struct macro macro[], int n)
671 FILE *f;
672 int s, i;
674 edit_push_action (edit, KEY_PRESS + edit->start_display);
675 /* This heads the 'Macro' dialog box */
676 s = edit_raw_key_query (_(" Save macro "),
677 /* Input line for a single key press follows the ':' */
678 _(" Press the macro's new hotkey: "), 1);
679 edit->force |= REDRAW_COMPLETELY;
680 if (s) {
681 if (edit_delete_macro (edit, s))
682 return 0;
683 f = edit_open_macro_file ("a+");
684 if (f) {
685 fprintf (f, ("key '%d 0': "), s);
686 for (i = 0; i < n; i++)
687 fprintf (f, "%hd %hd, ", macro[i].command, macro[i].ch);
688 fprintf (f, ";\n");
689 fclose (f);
690 if (saved_macros_loaded) {
691 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++);
692 saved_macro[i] = s;
694 return 1;
695 } else
696 /* This heads the 'Save Macro' dialog box */
697 edit_error_dialog (_(" Save macro "), get_sys_error (_(" Cannot open macro file ")));
699 return 0;
702 void edit_delete_macro_cmd (WEdit * edit)
704 int command;
706 command = edit_raw_key_query (_ (" Delete macro "),
707 _ (" Press macro hotkey: "), 1);
709 if (!command)
710 return;
712 edit_delete_macro (edit, command);
715 /* return 0 on error */
716 int edit_load_macro_cmd (WEdit * edit, struct macro macro[], int *n, int k)
718 FILE *f;
719 int s, i = 0, found = 0;
721 if (saved_macros_loaded)
722 if (macro_exists (k) < 0)
723 return 0;
725 if ((f = edit_open_macro_file ("r"))) {
726 struct macro dummy;
727 do {
728 int u;
729 u = fscanf (f, ("key '%d 0': "), &s);
730 if (!u || u == EOF)
731 break;
732 if (!saved_macros_loaded)
733 saved_macro[i++] = s;
734 if (!found) {
735 *n = 0;
736 while (*n < MAX_MACRO_LENGTH && 2 == fscanf (f, "%hd %hd, ", &macro[*n].command, &macro[*n].ch))
737 (*n)++;
738 } else {
739 while (2 == fscanf (f, "%hd %hd, ", &dummy.command, &dummy.ch));
741 fscanf (f, ";\n");
742 if (s == k)
743 found = 1;
744 } while (!found || !saved_macros_loaded);
745 if (!saved_macros_loaded) {
746 saved_macro[i] = 0;
747 saved_macros_loaded = 1;
749 fclose (f);
750 return found;
751 } else
752 /* This heads the 'Load Macro' dialog box */
753 edit_error_dialog (_(" Load macro "),
754 get_sys_error (_(" Cannot open macro file ")));
755 return 0;
758 /* }}} Macro stuff starts here */
760 /* returns 1 on success */
761 int edit_save_confirm_cmd (WEdit * edit)
763 char *f;
765 if (edit_confirm_save) {
766 f = catstrs (_(" Confirm save file? : "), edit->filename, " ", 0);
767 /* Buttons to 'Confirm save file' query */
768 if (edit_query_dialog2 (_(" Save file "), f, _("Save"), _("Cancel")))
769 return 0;
771 return edit_save_cmd (edit);
775 /* returns 1 on success */
776 static int
777 edit_save_cmd (WEdit *edit)
779 int res, save_lock = 0;
781 if (!edit->locked && !edit->delete_file)
782 save_lock = edit_lock_file (edit->filename);
783 res = edit_save_file (edit, edit->filename);
785 /* Maintain modify (not save) lock on failure */
786 if ((res && edit->locked) || save_lock)
787 edit->locked = edit_unlock_file (edit->filename);
789 /* On failure try 'save as', it does locking on its own */
790 if (!res)
791 return edit_save_as_cmd (edit);
792 edit->force |= REDRAW_COMPLETELY;
793 edit->delete_file = 0;
794 edit->modified = 0;
796 return 1;
800 /* returns 1 on success */
801 int edit_new_cmd (WEdit * edit)
803 if (edit->modified) {
804 if (edit_query_dialog2 (_ ("Warning"), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("Continue"), _ ("Cancel"))) {
805 edit->force |= REDRAW_COMPLETELY;
806 return 0;
809 edit->force |= REDRAW_COMPLETELY;
811 if (edit->locked)
812 edit->locked = edit_unlock_file (edit->filename);
813 return edit_renew (edit); /* if this gives an error, something has really screwed up */
816 /* returns 1 on error */
817 static int
818 edit_load_file_from_filename (WEdit * edit, char *exp)
820 int prev_locked = edit->locked;
821 char *prev_filename = g_strdup (edit->filename);
823 if (!edit_reload (edit, exp)) {
824 g_free (prev_filename);
825 return 1;
828 if (prev_locked)
829 edit_unlock_file (prev_filename);
830 g_free (prev_filename);
831 return 0;
835 edit_load_cmd (WEdit *edit)
837 char *exp;
839 if (edit->modified) {
840 if (edit_query_dialog2
841 (_("Warning"),
842 _(" Current text was modified without a file save. \n"
843 " Continue discards these changes. "), _("Continue"),
844 _("Cancel"))) {
845 edit->force |= REDRAW_COMPLETELY;
846 return 0;
850 exp = edit_get_load_file (edit->filename, _(" Load "));
852 if (exp) {
853 if (*exp)
854 edit_load_file_from_filename (edit, exp);
855 g_free (exp);
857 edit->force |= REDRAW_COMPLETELY;
858 return 0;
862 if mark2 is -1 then marking is from mark1 to the cursor.
863 Otherwise its between the markers. This handles this.
864 Returns 1 if no text is marked.
866 int eval_marks (WEdit * edit, long *start_mark, long *end_mark)
868 if (edit->mark1 != edit->mark2) {
869 if (edit->mark2 >= 0) {
870 *start_mark = min (edit->mark1, edit->mark2);
871 *end_mark = max (edit->mark1, edit->mark2);
872 } else {
873 *start_mark = min (edit->mark1, edit->curs1);
874 *end_mark = max (edit->mark1, edit->curs1);
875 edit->column2 = edit->curs_col;
877 return 0;
878 } else {
879 *start_mark = *end_mark = 0;
880 edit->column2 = edit->column1 = 0;
881 return 1;
885 #define space_width 1
887 static void
888 edit_insert_column_of_text (WEdit * edit, unsigned char *data, int size, int width)
890 long cursor;
891 int i, col;
892 cursor = edit->curs1;
893 col = edit_get_col (edit);
894 for (i = 0; i < size; i++) {
895 if (data[i] == '\n') { /* fill in and move to next line */
896 int l;
897 long p;
898 if (edit_get_byte (edit, edit->curs1) != '\n') {
899 l = width - (edit_get_col (edit) - col);
900 while (l > 0) {
901 edit_insert (edit, ' ');
902 l -= space_width;
905 for (p = edit->curs1;; p++) {
906 if (p == edit->last_byte) {
907 edit_cursor_move (edit, edit->last_byte - edit->curs1);
908 edit_insert_ahead (edit, '\n');
909 p++;
910 break;
912 if (edit_get_byte (edit, p) == '\n') {
913 p++;
914 break;
917 edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->curs1);
918 l = col - edit_get_col (edit);
919 while (l >= space_width) {
920 edit_insert (edit, ' ');
921 l -= space_width;
923 continue;
925 edit_insert (edit, data[i]);
927 edit_cursor_move (edit, cursor - edit->curs1);
931 void
932 edit_block_copy_cmd (WEdit *edit)
934 long start_mark, end_mark, current = edit->curs1;
935 int size, x;
936 unsigned char *copy_buf;
938 edit_update_curs_col (edit);
939 x = edit->curs_col;
940 if (eval_marks (edit, &start_mark, &end_mark))
941 return;
942 if (column_highlighting)
943 if ((x >= edit->column1 && x < edit->column2)
944 || (x > edit->column2 && x <= edit->column1))
945 return;
947 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
949 /* all that gets pushed are deletes hence little space is used on the stack */
951 edit_push_markers (edit);
953 if (column_highlighting) {
954 edit_insert_column_of_text (edit, copy_buf, size,
955 abs (edit->column2 - edit->column1));
956 } else {
957 while (size--)
958 edit_insert_ahead (edit, copy_buf[size]);
961 g_free (copy_buf);
962 edit_scroll_screen_over_cursor (edit);
964 if (column_highlighting) {
965 edit_set_markers (edit, 0, 0, 0, 0);
966 edit_push_action (edit, COLUMN_ON);
967 column_highlighting = 0;
968 } else if (start_mark < current && end_mark > current)
969 edit_set_markers (edit, start_mark,
970 end_mark + end_mark - start_mark, 0, 0);
972 edit->force |= REDRAW_PAGE;
976 void
977 edit_block_move_cmd (WEdit *edit)
979 long count;
980 long current;
981 unsigned char *copy_buf;
982 long start_mark, end_mark;
983 int deleted = 0;
984 int x = 0;
986 if (eval_marks (edit, &start_mark, &end_mark))
987 return;
988 if (column_highlighting) {
989 edit_update_curs_col (edit);
990 x = edit->curs_col;
991 if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
992 if ((x > edit->column1 && x < edit->column2)
993 || (x > edit->column2 && x < edit->column1))
994 return;
995 } else if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
996 return;
998 if ((end_mark - start_mark) > option_max_undo / 2)
999 if (edit_query_dialog2
1000 (_("Warning"),
1002 (" Block is large, you may not be able to undo this action. "),
1003 _("Continue"), _("Cancel")))
1004 return;
1006 edit_push_markers (edit);
1007 current = edit->curs1;
1008 if (column_highlighting) {
1009 int size, c1, c2, line;
1010 line = edit->curs_line;
1011 if (edit->mark2 < 0)
1012 edit_mark_cmd (edit, 0);
1013 c1 = min (edit->column1, edit->column2);
1014 c2 = max (edit->column1, edit->column2);
1015 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
1016 if (x < c2) {
1017 edit_block_delete_cmd (edit);
1018 deleted = 1;
1020 edit_move_to_line (edit, line);
1021 edit_cursor_move (edit,
1022 edit_move_forward3 (edit,
1023 edit_bol (edit, edit->curs1),
1024 x, 0) - edit->curs1);
1025 edit_insert_column_of_text (edit, copy_buf, size, c2 - c1);
1026 if (!deleted) {
1027 line = edit->curs_line;
1028 edit_update_curs_col (edit);
1029 x = edit->curs_col;
1030 edit_block_delete_cmd (edit);
1031 edit_move_to_line (edit, line);
1032 edit_cursor_move (edit,
1033 edit_move_forward3 (edit,
1034 edit_bol (edit,
1035 edit->curs1),
1036 x, 0) - edit->curs1);
1038 edit_set_markers (edit, 0, 0, 0, 0);
1039 edit_push_action (edit, COLUMN_ON);
1040 column_highlighting = 0;
1041 } else {
1042 copy_buf = g_malloc (end_mark - start_mark);
1043 edit_cursor_move (edit, start_mark - edit->curs1);
1044 edit_scroll_screen_over_cursor (edit);
1045 count = start_mark;
1046 while (count < end_mark) {
1047 copy_buf[end_mark - count - 1] = edit_delete (edit);
1048 count++;
1050 edit_scroll_screen_over_cursor (edit);
1051 edit_cursor_move (edit,
1052 current - edit->curs1 -
1053 (((current - edit->curs1) >
1054 0) ? end_mark - start_mark : 0));
1055 edit_scroll_screen_over_cursor (edit);
1056 while (count-- > start_mark)
1057 edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
1058 edit_set_markers (edit, edit->curs1,
1059 edit->curs1 + end_mark - start_mark, 0, 0);
1061 edit_scroll_screen_over_cursor (edit);
1062 g_free (copy_buf);
1063 edit->force |= REDRAW_PAGE;
1066 static void
1067 edit_delete_column_of_text (WEdit * edit)
1069 long p, q, r, m1, m2;
1070 int b, c, d;
1071 int n;
1073 eval_marks (edit, &m1, &m2);
1074 n = edit_move_forward (edit, m1, 0, m2) + 1;
1075 c = edit_move_forward3 (edit, edit_bol (edit, m1), 0, m1);
1076 d = edit_move_forward3 (edit, edit_bol (edit, m2), 0, m2);
1078 b = min (c, d);
1079 c = max (c, d);
1081 while (n--) {
1082 r = edit_bol (edit, edit->curs1);
1083 p = edit_move_forward3 (edit, r, b, 0);
1084 q = edit_move_forward3 (edit, r, c, 0);
1085 if (p < m1)
1086 p = m1;
1087 if (q > m2)
1088 q = m2;
1089 edit_cursor_move (edit, p - edit->curs1);
1090 while (q > p) { /* delete line between margins */
1091 if (edit_get_byte (edit, edit->curs1) != '\n')
1092 edit_delete (edit);
1093 q--;
1095 if (n) /* move to next line except on the last delete */
1096 edit_cursor_move (edit, edit_move_forward (edit, edit->curs1, 1, 0) - edit->curs1);
1100 /* if success return 0 */
1101 static int
1102 edit_block_delete (WEdit *edit)
1104 long count;
1105 long start_mark, end_mark;
1106 if (eval_marks (edit, &start_mark, &end_mark))
1107 return 0;
1108 if (column_highlighting && edit->mark2 < 0)
1109 edit_mark_cmd (edit, 0);
1110 if ((end_mark - start_mark) > option_max_undo / 2) {
1111 /* Warning message with a query to continue or cancel the operation */
1112 if (edit_query_dialog2
1113 (_("Warning"),
1115 (" Block is large, you may not be able to undo this action. "),
1116 _("Continue"), _("Cancel"))) {
1117 return 1;
1120 edit_push_markers (edit);
1121 edit_cursor_move (edit, start_mark - edit->curs1);
1122 edit_scroll_screen_over_cursor (edit);
1123 count = start_mark;
1124 if (start_mark < end_mark) {
1125 if (column_highlighting) {
1126 if (edit->mark2 < 0)
1127 edit_mark_cmd (edit, 0);
1128 edit_delete_column_of_text (edit);
1129 } else {
1130 while (count < end_mark) {
1131 edit_delete (edit);
1132 count++;
1136 edit_set_markers (edit, 0, 0, 0, 0);
1137 edit->force |= REDRAW_PAGE;
1138 return 0;
1141 /* returns 1 if canceelled by user */
1142 int edit_block_delete_cmd (WEdit * edit)
1144 long start_mark, end_mark;
1145 if (eval_marks (edit, &start_mark, &end_mark)) {
1146 edit_delete_line (edit);
1147 return 0;
1149 return edit_block_delete (edit);
1153 #define INPUT_INDEX 9
1154 #define SEARCH_DLG_WIDTH 58
1155 #define SEARCH_DLG_HEIGHT 10
1156 #define REPLACE_DLG_WIDTH 58
1157 #define REPLACE_DLG_HEIGHT 15
1158 #define CONFIRM_DLG_WIDTH 79
1159 #define CONFIRM_DLG_HEIGTH 6
1160 #define B_REPLACE_ALL (B_USER+1)
1161 #define B_REPLACE_ONE (B_USER+2)
1162 #define B_SKIP_REPLACE (B_USER+3)
1164 static int
1165 edit_replace_prompt (WEdit * edit, char *replace_text, int xpos, int ypos)
1167 QuickWidget quick_widgets[] =
1169 {quick_button, 63, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Cancel"),
1170 0, B_CANCEL, 0, 0, NULL},
1171 {quick_button, 50, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("O&ne"),
1172 0, B_REPLACE_ONE, 0, 0, NULL},
1173 {quick_button, 37, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("A&ll"),
1174 0, B_REPLACE_ALL, 0, 0, NULL},
1175 {quick_button, 21, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Skip"),
1176 0, B_SKIP_REPLACE, 0, 0, NULL},
1177 {quick_button, 4, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Replace"),
1178 0, B_ENTER, 0, 0, NULL},
1179 {quick_label, 2, CONFIRM_DLG_WIDTH, 2, CONFIRM_DLG_HEIGTH, 0,
1180 0, 0, 0, 0, 0},
1181 {0}};
1183 #ifdef HAVE_CHARSET
1184 char *msg = _(" Replace with: ");
1186 quick_widgets[5].text = catstrs (msg, replace_text, 0);
1188 if (*replace_text)
1189 convert_to_display (quick_widgets[5].text + strlen (msg));
1190 #else
1191 quick_widgets[5].text = catstrs (_ (" Replace with: "), replace_text, 0);
1192 #endif /* !HAVE_CHARSET */
1195 QuickDialog Quick_input =
1196 {CONFIRM_DLG_WIDTH, CONFIRM_DLG_HEIGTH, 0, 0, N_ (" Confirm replace "),
1197 "[Input Line Keys]", 0 /*quick_widgets */ };
1199 Quick_input.widgets = quick_widgets;
1201 Quick_input.xpos = xpos;
1203 /* Sometimes menu can hide replaced text. I don't like it */
1205 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + CONFIRM_DLG_HEIGTH - 1))
1206 ypos -= CONFIRM_DLG_HEIGTH;
1208 Quick_input.ypos = ypos;
1209 return quick_dialog (&Quick_input);
1213 static void
1214 edit_replace_dialog (WEdit * edit, char **search_text, char **replace_text, char **arg_order)
1216 int treplace_scanf = replace_scanf;
1217 int treplace_regexp = replace_regexp;
1218 int treplace_all = replace_all;
1219 int treplace_prompt = replace_prompt;
1220 int treplace_backwards = replace_backwards;
1221 int treplace_whole = replace_whole;
1222 int treplace_case = replace_case;
1224 QuickWidget quick_widgets[] =
1226 {quick_button, 6, 10, 12, REPLACE_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1227 0, NULL},
1228 {quick_button, 2, 10, 12, REPLACE_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1229 0, NULL},
1230 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1231 0, 0, NULL},
1232 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("replace &All"), 0, 0,
1233 0, 0, NULL},
1234 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("pr&Ompt on replace"), 0, 0,
1235 0, 0, NULL},
1236 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1237 0, 0, NULL},
1238 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1239 0, 0, NULL},
1240 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1241 0, 0, NULL},
1242 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 8, REPLACE_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1243 0, 0, NULL},
1244 {quick_input, 3, REPLACE_DLG_WIDTH, 7, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1245 0, "edit-argord"},
1246 {quick_label, 2, REPLACE_DLG_WIDTH, 6, REPLACE_DLG_HEIGHT, N_(" Enter replacement argument order eg. 3,2,1,4 "), 0, 0,
1247 0, 0, 0},
1248 {quick_input, 3, REPLACE_DLG_WIDTH, 5, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1249 0, "edit-replace"},
1250 {quick_label, 2, REPLACE_DLG_WIDTH, 4, REPLACE_DLG_HEIGHT, N_(" Enter replacement string:"), 0, 0, 0,
1251 0, 0},
1252 {quick_input, 3, REPLACE_DLG_WIDTH, 3, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1253 0, "edit-search"},
1254 {quick_label, 2, REPLACE_DLG_WIDTH, 2, REPLACE_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1255 0, 0},
1256 {0}};
1258 quick_widgets[2].result = &treplace_scanf;
1259 quick_widgets[3].result = &treplace_all;
1260 quick_widgets[4].result = &treplace_prompt;
1261 quick_widgets[5].result = &treplace_backwards;
1262 quick_widgets[6].result = &treplace_regexp;
1263 quick_widgets[7].result = &treplace_whole;
1264 quick_widgets[8].result = &treplace_case;
1265 quick_widgets[9].str_result = arg_order;
1266 quick_widgets[9].text = *arg_order;
1267 quick_widgets[11].str_result = replace_text;
1268 quick_widgets[11].text = *replace_text;
1269 quick_widgets[13].str_result = search_text;
1270 quick_widgets[13].text = *search_text;
1272 QuickDialog Quick_input =
1273 {REPLACE_DLG_WIDTH, REPLACE_DLG_HEIGHT, -1, 0, N_(" Replace "),
1274 "[Input Line Keys]", 0 /*quick_widgets */ };
1276 Quick_input.widgets = quick_widgets;
1278 if (quick_dialog (&Quick_input) != B_CANCEL) {
1279 replace_scanf = treplace_scanf;
1280 replace_backwards = treplace_backwards;
1281 replace_regexp = treplace_regexp;
1282 replace_all = treplace_all;
1283 replace_prompt = treplace_prompt;
1284 replace_whole = treplace_whole;
1285 replace_case = treplace_case;
1286 return;
1287 } else {
1288 *arg_order = NULL;
1289 *replace_text = NULL;
1290 *search_text = NULL;
1291 return;
1297 static void
1298 edit_search_dialog (WEdit * edit, char **search_text)
1300 int treplace_scanf = replace_scanf;
1301 int treplace_regexp = replace_regexp;
1302 int treplace_whole = replace_whole;
1303 int treplace_case = replace_case;
1304 int treplace_backwards = replace_backwards;
1306 QuickWidget quick_widgets[] =
1308 {quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1309 0, NULL},
1310 {quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1311 0, NULL},
1312 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1313 0, 0, NULL },
1314 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1315 0, 0, NULL},
1316 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1317 0, 0, NULL},
1318 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1319 0, 0, NULL},
1320 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1321 0, 0, NULL},
1322 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, 0,
1323 0, "edit-search"},
1324 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1325 0, 0},
1326 {0}};
1328 quick_widgets[2].result = &treplace_scanf;
1329 quick_widgets[3].result = &treplace_backwards;
1330 quick_widgets[4].result = &treplace_regexp;
1331 quick_widgets[5].result = &treplace_whole;
1332 quick_widgets[6].result = &treplace_case;
1333 quick_widgets[7].str_result = search_text;
1334 quick_widgets[7].text = *search_text;
1337 QuickDialog Quick_input =
1338 {SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
1339 "[Input Line Keys]", 0 /*quick_widgets */ };
1341 Quick_input.widgets = quick_widgets;
1343 if (quick_dialog (&Quick_input) != B_CANCEL) {
1344 replace_scanf = treplace_scanf;
1345 replace_backwards = treplace_backwards;
1346 replace_regexp = treplace_regexp;
1347 replace_whole = treplace_whole;
1348 replace_case = treplace_case;
1349 } else {
1350 *search_text = NULL;
1356 static long sargs[NUM_REPL_ARGS][256 / sizeof (long)];
1358 #define SCANF_ARGS sargs[0], sargs[1], sargs[2], sargs[3], \
1359 sargs[4], sargs[5], sargs[6], sargs[7], \
1360 sargs[8], sargs[9], sargs[10], sargs[11], \
1361 sargs[12], sargs[13], sargs[14], sargs[15]
1363 #define PRINTF_ARGS sargs[argord[0]], sargs[argord[1]], sargs[argord[2]], sargs[argord[3]], \
1364 sargs[argord[4]], sargs[argord[5]], sargs[argord[6]], sargs[argord[7]], \
1365 sargs[argord[8]], sargs[argord[9]], sargs[argord[10]], sargs[argord[11]], \
1366 sargs[argord[12]], sargs[argord[13]], sargs[argord[14]], sargs[argord[15]]
1369 /* This function is a modification of mc-3.2.10/src/view.c:regexp_view_search() */
1370 /* returns -3 on error in pattern, -1 on not found, found_len = 0 if either */
1371 static int
1372 string_regexp_search (char *pattern, char *string, int len, int match_type,
1373 int match_bol, int icase, int *found_len, void *d)
1375 static regex_t r;
1376 static char *old_pattern = NULL;
1377 static int old_type, old_icase;
1378 regmatch_t *pmatch;
1379 static regmatch_t s[1];
1381 pmatch = (regmatch_t *) d;
1382 if (!pmatch)
1383 pmatch = s;
1385 if (!old_pattern || strcmp (old_pattern, pattern)
1386 || old_type != match_type || old_icase != icase) {
1387 if (old_pattern) {
1388 regfree (&r);
1389 g_free (old_pattern);
1390 old_pattern = 0;
1392 if (regcomp (&r, pattern, REG_EXTENDED | (icase ? REG_ICASE : 0))) {
1393 *found_len = 0;
1394 return -3;
1396 old_pattern = g_strdup (pattern);
1397 old_type = match_type;
1398 old_icase = icase;
1400 if (regexec
1401 (&r, string, d ? NUM_REPL_ARGS : 1, pmatch,
1402 ((match_bol
1403 || match_type != match_normal) ? 0 : REG_NOTBOL)) != 0) {
1404 *found_len = 0;
1405 return -1;
1407 *found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1408 return (pmatch[0].rm_so);
1411 /* thanks to Liviu Daia <daia@stoilow.imar.ro> for getting this
1412 (and the above) routines to work properly - paul */
1414 static long
1415 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)
1417 long p, q = 0;
1418 long l = strlen ((char *) exp), f = 0;
1419 int n = 0;
1421 for (p = 0; p < l; p++) /* count conversions... */
1422 if (exp[p] == '%')
1423 if (exp[++p] != '%') /* ...except for "%%" */
1424 n++;
1426 if (replace_scanf || replace_regexp) {
1427 int c;
1428 unsigned char *buf;
1429 unsigned char mbuf[MAX_REPL_LEN * 2 + 3];
1431 replace_scanf = (!replace_regexp); /* can't have both */
1433 buf = mbuf;
1435 if (replace_scanf) {
1436 unsigned char e[MAX_REPL_LEN];
1437 if (n >= NUM_REPL_ARGS)
1438 return -3;
1440 if (replace_case) {
1441 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++)
1442 buf[p - start] = (*get_byte) (data, p);
1443 } else {
1444 for (p = 0; exp[p] != 0; p++)
1445 exp[p] = my_lower_case (exp[p]);
1446 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++) {
1447 c = (*get_byte) (data, p);
1448 buf[p - start] = my_lower_case (c);
1452 buf[(q = p - start)] = 0;
1453 strcpy ((char *) e, (char *) exp);
1454 strcat ((char *) e, "%n");
1455 exp = e;
1457 while (q) {
1458 *((int *) sargs[n]) = 0; /* --> here was the problem - now fixed: good */
1459 if (n == sscanf ((char *) buf, (char *) exp, SCANF_ARGS)) {
1460 if (*((int *) sargs[n])) {
1461 *len = *((int *) sargs[n]);
1462 return start;
1465 if (once_only)
1466 return -2;
1467 if (q + start < last_byte) {
1468 if (replace_case) {
1469 buf[q] = (*get_byte) (data, q + start);
1470 } else {
1471 c = (*get_byte) (data, q + start);
1472 buf[q] = my_lower_case (c);
1474 q++;
1476 buf[q] = 0;
1477 start++;
1478 buf++; /* move the window along */
1479 if (buf == mbuf + MAX_REPL_LEN) { /* the window is about to go past the end of array, so... */
1480 memmove (mbuf, buf, strlen ((char *) buf) + 1); /* reset it */
1481 buf = mbuf;
1483 q--;
1485 } else { /* regexp matching */
1486 long offset = 0;
1487 int found_start, match_bol, move_win = 0;
1489 while (start + offset < last_byte) {
1490 match_bol = (offset == 0 || (*get_byte) (data, start + offset - 1) == '\n');
1491 if (!move_win) {
1492 p = start + offset;
1493 q = 0;
1495 for (; p < last_byte && q < MAX_REPL_LEN; p++, q++) {
1496 mbuf[q] = (*get_byte) (data, p);
1497 if (mbuf[q] == '\n')
1498 break;
1500 q++;
1501 offset += q;
1502 mbuf[q] = 0;
1504 buf = mbuf;
1505 while (q) {
1506 found_start = string_regexp_search ((char *) exp, (char *) buf, q, match_normal, match_bol, !replace_case, len, d);
1508 if (found_start <= -2) { /* regcomp/regexec error */
1509 *len = 0;
1510 return -3;
1512 else if (found_start == -1) /* not found: try next line */
1513 break;
1514 else if (*len == 0) { /* null pattern: try again at next character */
1515 q--;
1516 buf++;
1517 match_bol = 0;
1518 continue;
1520 else /* found */
1521 return (start + offset - q + found_start);
1523 if (once_only)
1524 return -2;
1526 if (buf[q - 1] != '\n') { /* incomplete line: try to recover */
1527 buf = mbuf + MAX_REPL_LEN / 2;
1528 q = strlen ((char *) buf);
1529 memmove (mbuf, buf, q);
1530 p = start + q;
1531 move_win = 1;
1533 else
1534 move_win = 0;
1537 } else {
1538 *len = strlen ((char *) exp);
1539 if (replace_case) {
1540 for (p = start; p <= last_byte - l; p++) {
1541 if ((*get_byte) (data, p) == (unsigned char)exp[0]) { /* check if first char matches */
1542 for (f = 0, q = 0; q < l && f < 1; q++)
1543 if ((*get_byte) (data, q + p) != (unsigned char)exp[q])
1544 f = 1;
1545 if (f == 0)
1546 return p;
1548 if (once_only)
1549 return -2;
1551 } else {
1552 for (p = 0; exp[p] != 0; p++)
1553 exp[p] = my_lower_case (exp[p]);
1555 for (p = start; p <= last_byte - l; p++) {
1556 if (my_lower_case ((*get_byte) (data, p)) == (unsigned char)exp[0]) {
1557 for (f = 0, q = 0; q < l && f < 1; q++)
1558 if (my_lower_case ((*get_byte) (data, q + p)) != (unsigned char)exp[q])
1559 f = 1;
1560 if (f == 0)
1561 return p;
1563 if (once_only)
1564 return -2;
1568 return -2;
1572 static long
1573 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)
1574 { /*front end to find_string to check for
1575 whole words */
1576 long p;
1577 p = search_start;
1579 while ((p = edit_find_string (p, exp, len, last_byte, get_byte, data, once_only, d)) >= 0) {
1580 if (replace_whole) {
1581 /*If the bordering chars are not in option_whole_chars_search then word is whole */
1582 if (!strcasechr (option_whole_chars_search, (*get_byte) (data, p - 1))
1583 && !strcasechr (option_whole_chars_search, (*get_byte) (data, p + *len)))
1584 return p;
1585 if (once_only)
1586 return -2;
1587 } else
1588 return p;
1589 if (once_only)
1590 break;
1591 p++; /*not a whole word so continue search. */
1593 return p;
1596 static long
1597 edit_find (long search_start, unsigned char *exp, int *len, long last_byte, int (*get_byte) (void *, long), void *data, void *d)
1599 long p;
1600 if (replace_backwards) {
1601 while (search_start >= 0) {
1602 p = edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 1, d);
1603 if (p == search_start)
1604 return p;
1605 search_start--;
1607 } else {
1608 return edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 0, d);
1610 return -2;
1613 #define is_digit(x) ((x) >= '0' && (x) <= '9')
1615 #define snprintf(v) { \
1616 *p1++ = *p++; \
1617 *p1++ = '%'; \
1618 *p1++ = 'n'; \
1619 *p1 = '\0'; \
1620 sprintf(s,q1,v,&n); \
1621 s += n; \
1624 /* this function uses the sprintf command to do a vprintf */
1625 /* it takes pointers to arguments instead of the arguments themselves */
1626 static int sprintf_p (char *str, const char *fmt,...)
1627 __attribute__ ((format (printf, 2, 3)));
1629 static int sprintf_p (char *str, const char *fmt,...)
1631 va_list ap;
1632 int n;
1633 char *q, *p, *s = str;
1634 char q1[32];
1635 char *p1;
1637 va_start (ap, fmt);
1638 p = q = (char *) fmt;
1640 while ((p = strchr (p, '%'))) {
1641 n = p - q;
1642 strncpy (s, q, n); /* copy stuff between format specifiers */
1643 s += n;
1644 *s = 0;
1645 q = p;
1646 p1 = q1;
1647 *p1++ = *p++;
1648 if (*p == '%') {
1649 p++;
1650 *s++ = '%';
1651 q = p;
1652 continue;
1654 if (*p == 'n') {
1655 p++;
1656 /* do nothing */
1657 q = p;
1658 continue;
1660 if (*p == '#')
1661 *p1++ = *p++;
1662 if (*p == '0')
1663 *p1++ = *p++;
1664 if (*p == '-')
1665 *p1++ = *p++;
1666 if (*p == '+')
1667 *p1++ = *p++;
1668 if (*p == '*') {
1669 p++;
1670 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace field width with a number */
1671 p1 += strlen (p1);
1672 } else {
1673 while (is_digit (*p))
1674 *p1++ = *p++;
1676 if (*p == '.')
1677 *p1++ = *p++;
1678 if (*p == '*') {
1679 p++;
1680 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace precision with a number */
1681 p1 += strlen (p1);
1682 } else {
1683 while (is_digit (*p))
1684 *p1++ = *p++;
1686 /* flags done, now get argument */
1687 if (*p == 's') {
1688 snprintf (va_arg (ap, char *));
1689 } else if (*p == 'h') {
1690 if (strchr ("diouxX", *p))
1691 snprintf (*va_arg (ap, short *));
1692 } else if (*p == 'l') {
1693 *p1++ = *p++;
1694 if (strchr ("diouxX", *p))
1695 snprintf (*va_arg (ap, long *));
1696 } else if (strchr ("cdiouxX", *p)) {
1697 snprintf (*va_arg (ap, int *));
1698 } else if (*p == 'L') {
1699 *p1++ = *p++;
1700 if (strchr ("EefgG", *p))
1701 snprintf (*va_arg (ap, double *)); /* should be long double */
1702 } else if (strchr ("EefgG", *p)) {
1703 snprintf (*va_arg (ap, double *));
1704 } else if (strchr ("DOU", *p)) {
1705 snprintf (*va_arg (ap, long *));
1706 } else if (*p == 'p') {
1707 snprintf (*va_arg (ap, void **));
1709 q = p;
1711 va_end (ap);
1712 sprintf (s, q); /* print trailing leftover */
1713 return s - str + strlen (s);
1716 static void regexp_error (WEdit *edit)
1718 /* "Error: Syntax error in regular expression, or scanf expression contained too many %'s */
1719 edit_error_dialog (_("Error"), _(" Invalid regular expression, or scanf expression with to many conversions "));
1722 /* call with edit = 0 before shutdown to close memory leaks */
1723 void
1724 edit_replace_cmd (WEdit *edit, int again)
1726 static regmatch_t pmatch[NUM_REPL_ARGS];
1727 static char *old1 = NULL;
1728 static char *old2 = NULL;
1729 static char *old3 = NULL;
1730 char *exp1 = "";
1731 char *exp2 = "";
1732 char *exp3 = "";
1733 int replace_yes;
1734 int replace_continue;
1735 int treplace_prompt = 0;
1736 int i = 0;
1737 long times_replaced = 0, last_search;
1738 int argord[NUM_REPL_ARGS];
1740 if (!edit) {
1741 if (old1) {
1742 g_free (old1);
1743 old1 = 0;
1745 if (old2) {
1746 g_free (old2);
1747 old2 = 0;
1749 if (old3) {
1750 g_free (old3);
1751 old3 = 0;
1753 return;
1755 last_search = edit->last_byte;
1757 edit->force |= REDRAW_COMPLETELY;
1759 exp1 = old1 ? old1 : exp1;
1760 exp2 = old2 ? old2 : exp2;
1761 exp3 = old3 ? old3 : exp3;
1763 if (again) {
1764 if (!old1 || !old2)
1765 return;
1766 exp1 = g_strdup (old1);
1767 exp2 = g_strdup (old2);
1768 exp3 = g_strdup (old3);
1769 } else {
1770 edit_push_action (edit, KEY_PRESS + edit->start_display);
1772 convert_to_display (exp1);
1773 convert_to_display (exp2);
1775 edit_replace_dialog (edit, &exp1, &exp2, &exp3);
1777 convert_from_input (exp1);
1778 convert_from_input (exp2);
1780 treplace_prompt = replace_prompt;
1783 if (!exp1 || !*exp1) {
1784 edit->force = REDRAW_COMPLETELY;
1785 g_free (exp1);
1786 g_free (exp2);
1787 g_free (exp3);
1788 return;
1790 g_free (old1);
1791 g_free (old2);
1792 g_free (old3);
1793 old1 = g_strdup (exp1);
1794 old2 = g_strdup (exp2);
1795 old3 = g_strdup (exp3);
1798 char *s;
1799 int ord;
1800 while ((s = strchr (exp3, ' ')))
1801 memmove (s, s + 1, strlen (s));
1802 s = exp3;
1803 for (i = 0; i < NUM_REPL_ARGS; i++) {
1804 if (s != (char *) 1 && *s) {
1805 ord = atoi (s);
1806 if ((ord > 0) && (ord < NUM_REPL_ARGS))
1807 argord[i] = ord - 1;
1808 else
1809 argord[i] = i;
1810 s = strchr (s, ',') + 1;
1811 } else
1812 argord[i] = i;
1816 replace_continue = replace_all;
1818 if (edit->found_len && edit->search_start == edit->found_start + 1
1819 && replace_backwards)
1820 edit->search_start--;
1822 if (edit->found_len && edit->search_start == edit->found_start - 1
1823 && !replace_backwards)
1824 edit->search_start++;
1826 do {
1827 int len = 0;
1828 long new_start;
1829 new_start =
1830 edit_find (edit->search_start, (unsigned char *) exp1, &len,
1831 last_search, (int (*)(void *, long)) edit_get_byte,
1832 (void *) edit, pmatch);
1833 if (new_start == -3) {
1834 regexp_error (edit);
1835 break;
1837 edit->search_start = new_start;
1838 /*returns negative on not found or error in pattern */
1840 if (edit->search_start >= 0) {
1841 edit->found_start = edit->search_start;
1842 i = edit->found_len = len;
1844 edit_cursor_move (edit, edit->search_start - edit->curs1);
1845 edit_scroll_screen_over_cursor (edit);
1847 replace_yes = 1;
1849 if (treplace_prompt) {
1850 int l;
1851 l = edit->curs_row - edit->num_widget_lines / 3;
1852 if (l > 0)
1853 edit_scroll_downward (edit, l);
1854 if (l < 0)
1855 edit_scroll_upward (edit, -l);
1857 edit_scroll_screen_over_cursor (edit);
1858 edit->force |= REDRAW_PAGE;
1859 edit_render_keypress (edit);
1861 /*so that undo stops at each query */
1862 edit_push_key_press (edit);
1864 switch (edit_replace_prompt (edit, exp2, /* and prompt 2/3 down */
1865 (edit->num_widget_columns -
1866 CONFIRM_DLG_WIDTH) / 2,
1867 edit->num_widget_lines * 2 /
1868 3)) {
1869 case B_ENTER:
1870 break;
1871 case B_SKIP_REPLACE:
1872 replace_yes = 0;
1873 break;
1874 case B_REPLACE_ALL:
1875 treplace_prompt = 0;
1876 replace_continue = 1;
1877 break;
1878 case B_REPLACE_ONE:
1879 replace_continue = 0;
1880 break;
1881 case B_CANCEL:
1882 replace_yes = 0;
1883 replace_continue = 0;
1884 break;
1887 if (replace_yes) { /* delete then insert new */
1888 if (replace_scanf || replace_regexp) {
1889 char repl_str[MAX_REPL_LEN + 2];
1891 /* we need to fill in sargs just like with scanf */
1892 if (replace_regexp) {
1893 int k, j;
1894 for (k = 1;
1895 k < NUM_REPL_ARGS && pmatch[k].rm_eo >= 0;
1896 k++) {
1897 unsigned char *t;
1898 t = (unsigned char *) &sargs[k - 1][0];
1899 for (j = 0;
1900 j < pmatch[k].rm_eo - pmatch[k].rm_so
1901 && j < 255; j++, t++)
1902 *t = (unsigned char) edit_get_byte (edit,
1903 edit->
1904 search_start
1906 pmatch
1907 [0].
1908 rm_so +
1909 pmatch
1910 [k].
1911 rm_so +
1913 *t = '\0';
1915 for (; k <= NUM_REPL_ARGS; k++)
1916 sargs[k - 1][0] = 0;
1918 if (sprintf_p (repl_str, exp2, PRINTF_ARGS) >= 0) {
1919 times_replaced++;
1920 while (i--)
1921 edit_delete (edit);
1922 while (repl_str[++i])
1923 edit_insert (edit, repl_str[i]);
1924 } else {
1925 edit_error_dialog (_(" Replace "),
1927 (" Error in replacement format string. "));
1928 replace_continue = 0;
1930 } else {
1931 times_replaced++;
1932 while (i--)
1933 edit_delete (edit);
1934 while (exp2[++i])
1935 edit_insert (edit, exp2[i]);
1937 edit->found_len = i;
1939 /* so that we don't find the same string again */
1940 if (replace_backwards) {
1941 last_search = edit->search_start;
1942 edit->search_start--;
1943 } else {
1944 edit->search_start += i;
1945 last_search = edit->last_byte;
1947 edit_scroll_screen_over_cursor (edit);
1948 } else {
1949 char *msg = _(" Replace ");
1950 /* try and find from right here for next search */
1951 edit->search_start = edit->curs1;
1952 edit_update_curs_col (edit);
1954 edit->force |= REDRAW_PAGE;
1955 edit_render_keypress (edit);
1956 if (times_replaced) {
1957 message (0, msg, _(" %ld replacements made. "),
1958 times_replaced);
1959 } else
1960 edit_message_dialog (msg, _(" Search string not found "));
1961 replace_continue = 0;
1963 } while (replace_continue);
1965 g_free (exp1);
1966 g_free (exp2);
1967 g_free (exp3);
1968 edit->force = REDRAW_COMPLETELY;
1969 edit_scroll_screen_over_cursor (edit);
1975 void edit_search_cmd (WEdit * edit, int again)
1977 static char *old = NULL;
1978 char *exp = "";
1980 if (!edit) {
1981 if (old) {
1982 g_free (old);
1983 old = 0;
1985 return;
1987 exp = old ? old : exp;
1988 if (again) { /*ctrl-hotkey for search again. */
1989 if (!old)
1990 return;
1991 exp = g_strdup (old);
1992 } else {
1994 #ifdef HAVE_CHARSET
1995 if (exp && *exp)
1996 convert_to_display (exp);
1997 #endif /* HAVE_CHARSET */
1999 edit_search_dialog (edit, &exp);
2001 #ifdef HAVE_CHARSET
2002 if (exp && *exp)
2003 convert_from_input (exp);
2004 #endif /* HAVE_CHARSET */
2006 edit_push_action (edit, KEY_PRESS + edit->start_display);
2009 if (exp) {
2010 if (*exp) {
2011 int len = 0;
2012 if (old)
2013 g_free (old);
2014 old = g_strdup (exp);
2016 if (search_create_bookmark) {
2017 int found = 0, books = 0;
2018 int l = 0, l_last = -1;
2019 long p, q = 0;
2020 for (;;) {
2021 p = edit_find (q, (unsigned char *) exp, &len, edit->last_byte,
2022 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
2023 if (p < 0)
2024 break;
2025 found++;
2026 l += edit_count_lines (edit, q, p);
2027 if (l != l_last) {
2028 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
2029 books++;
2031 l_last = l;
2032 q = p + 1;
2034 if (found) {
2035 /* in response to number of bookmarks added because of string being found %d times */
2036 message (0, _("Search"), _(" %d finds made, %d bookmarks added "), found, books);
2037 } else {
2038 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
2040 } else {
2042 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
2043 edit->search_start--;
2045 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
2046 edit->search_start++;
2048 edit->search_start = edit_find (edit->search_start, (unsigned char *) exp, &len, edit->last_byte,
2049 (int (*)(void *, long)) edit_get_byte, (void *) edit, 0);
2051 if (edit->search_start >= 0) {
2052 edit->found_start = edit->search_start;
2053 edit->found_len = len;
2055 edit_cursor_move (edit, edit->search_start - edit->curs1);
2056 edit_scroll_screen_over_cursor (edit);
2057 if (replace_backwards)
2058 edit->search_start--;
2059 else
2060 edit->search_start++;
2061 } else if (edit->search_start == -3) {
2062 edit->search_start = edit->curs1;
2063 regexp_error (edit);
2064 } else {
2065 edit->search_start = edit->curs1;
2066 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
2070 g_free (exp);
2072 edit->force |= REDRAW_COMPLETELY;
2073 edit_scroll_screen_over_cursor (edit);
2078 * Check if it's OK to close the editor. If there are unsaved changes,
2079 * ask user. Return 1 if it's OK to exit, 0 to continue editing.
2082 edit_ok_to_exit (WEdit *edit)
2084 if (!edit->modified)
2085 return 1;
2087 switch (edit_query_dialog3
2088 (_("Quit"), _(" File was modified, Save with exit? "),
2089 _("Cancel quit"), _("&Yes"), _("&No"))) {
2090 case 1:
2091 edit_push_markers (edit);
2092 edit_set_markers (edit, 0, 0, 0, 0);
2093 if (!edit_save_cmd (edit))
2094 return 0;
2095 break;
2096 case 2:
2097 if (edit->locked)
2098 edit->locked = edit_unlock_file (edit->filename);
2099 break;
2100 case 0:
2101 case -1:
2102 return 0;
2105 return 1;
2109 #define TEMP_BUF_LEN 1024
2111 /* Return a null terminated length of text. Result must be g_free'd */
2112 static unsigned char *
2113 edit_get_block (WEdit *edit, long start, long finish, int *l)
2115 unsigned char *s, *r;
2116 r = s = g_malloc (finish - start + 1);
2117 if (column_highlighting) {
2118 *l = 0;
2119 /* copy from buffer, excluding chars that are out of the column 'margins' */
2120 while (start < finish) {
2121 int c, x;
2122 x = edit_move_forward3 (edit, edit_bol (edit, start), 0,
2123 start);
2124 c = edit_get_byte (edit, start);
2125 if ((x >= edit->column1 && x < edit->column2)
2126 || (x >= edit->column2 && x < edit->column1) || c == '\n') {
2127 *s++ = c;
2128 (*l)++;
2130 start++;
2132 } else {
2133 *l = finish - start;
2134 while (start < finish)
2135 *s++ = edit_get_byte (edit, start++);
2137 *s = 0;
2138 return r;
2141 /* save block, returns 1 on success */
2143 edit_save_block (WEdit * edit, const char *filename, long start,
2144 long finish)
2146 int len, file;
2148 if ((file =
2149 mc_open (filename, O_CREAT | O_WRONLY | O_TRUNC,
2150 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY)) == -1)
2151 return 0;
2153 if (column_highlighting) {
2154 unsigned char *block, *p;
2155 int r;
2156 p = block = edit_get_block (edit, start, finish, &len);
2157 while (len) {
2158 r = mc_write (file, p, len);
2159 if (r < 0)
2160 break;
2161 p += r;
2162 len -= r;
2164 g_free (block);
2165 } else {
2166 unsigned char *buf;
2167 int i = start, end;
2168 len = finish - start;
2169 buf = g_malloc (TEMP_BUF_LEN);
2170 while (start != finish) {
2171 end = min (finish, start + TEMP_BUF_LEN);
2172 for (; i < end; i++)
2173 buf[i - start] = edit_get_byte (edit, i);
2174 len -= mc_write (file, (char *) buf, end - start);
2175 start = end;
2177 g_free (buf);
2179 mc_close (file);
2180 if (len)
2181 return 0;
2182 return 1;
2185 /* copies a block to clipboard file */
2186 static int edit_save_block_to_clip_file (WEdit * edit, long start, long finish)
2188 return edit_save_block (edit, catstrs (home_dir, CLIP_FILE, 0), start, finish);
2192 void edit_paste_from_history (WEdit *edit)
2196 int edit_copy_to_X_buf_cmd (WEdit * edit)
2198 long start_mark, end_mark;
2199 if (eval_marks (edit, &start_mark, &end_mark))
2200 return 0;
2201 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2202 edit_error_dialog (_(" Copy to clipboard "), get_sys_error (_(" Unable to save to file. ")));
2203 return 1;
2205 edit_mark_cmd (edit, 1);
2206 return 0;
2209 int edit_cut_to_X_buf_cmd (WEdit * edit)
2211 long start_mark, end_mark;
2212 if (eval_marks (edit, &start_mark, &end_mark))
2213 return 0;
2214 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2215 edit_error_dialog (_(" Cut to clipboard "), _(" Unable to save to file. "));
2216 return 1;
2218 edit_block_delete_cmd (edit);
2219 edit_mark_cmd (edit, 1);
2220 return 0;
2223 void edit_paste_from_X_buf_cmd (WEdit * edit)
2225 edit_insert_file (edit, catstrs (home_dir, CLIP_FILE, 0));
2230 * Ask user for the line and go to that line.
2231 * Negative numbers mean line from the end (i.e. -1 is the last line).
2233 void
2234 edit_goto_cmd (WEdit *edit)
2236 char *f;
2237 static long line = 0; /* line as typed, saved as default */
2238 long l;
2239 char *error;
2240 char s[32];
2242 g_snprintf (s, sizeof (s), "%ld", line);
2243 f = input_dialog (_(" Goto line "), _(" Enter line: "), line ? s : "");
2244 if (!f)
2245 return;
2247 if (!*f) {
2248 g_free (f);
2249 return;
2252 l = strtol (f, &error, 0);
2253 if (*error) {
2254 g_free (f);
2255 return;
2258 line = l;
2259 if (l < 0)
2260 l = edit->total_lines + l + 2;
2261 edit_move_display (edit, l - edit->num_widget_lines / 2 - 1);
2262 edit_move_to_line (edit, l - 1);
2263 edit->force |= REDRAW_COMPLETELY;
2264 g_free (f);
2268 /* Return 1 on success */
2270 edit_save_block_cmd (WEdit *edit)
2272 long start_mark, end_mark;
2273 char *exp;
2274 if (eval_marks (edit, &start_mark, &end_mark))
2275 return 1;
2276 exp =
2277 edit_get_save_file (catstrs (home_dir, CLIP_FILE, 0),
2278 _(" Save Block "));
2279 edit_push_action (edit, KEY_PRESS + edit->start_display);
2280 if (exp) {
2281 if (!*exp) {
2282 g_free (exp);
2283 return 0;
2284 } else {
2285 if (edit_save_block (edit, exp, start_mark, end_mark)) {
2286 g_free (exp);
2287 edit->force |= REDRAW_COMPLETELY;
2288 return 1;
2289 } else {
2290 g_free (exp);
2291 edit_error_dialog (_(" Save Block "),
2292 get_sys_error (_
2293 (" Cannot save file. ")));
2297 edit->force |= REDRAW_COMPLETELY;
2298 return 0;
2302 /* returns 1 on success */
2304 edit_insert_file_cmd (WEdit *edit)
2306 char *exp = edit_get_load_file (catstrs (home_dir, CLIP_FILE, 0),
2307 _(" Insert File "));
2308 edit_push_action (edit, KEY_PRESS + edit->start_display);
2309 if (exp) {
2310 if (!*exp) {
2311 g_free (exp);
2312 return 0;
2313 } else {
2314 if (edit_insert_file (edit, exp)) {
2315 g_free (exp);
2316 edit->force |= REDRAW_COMPLETELY;
2317 return 1;
2318 } else {
2319 g_free (exp);
2320 edit_error_dialog (_(" Insert File "),
2321 get_sys_error (_
2322 (" Cannot insert file. ")));
2326 edit->force |= REDRAW_COMPLETELY;
2327 return 0;
2330 /* sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
2331 int edit_sort_cmd (WEdit * edit)
2333 static char *old = 0;
2334 char *exp;
2335 long start_mark, end_mark;
2336 int e;
2338 if (eval_marks (edit, &start_mark, &end_mark)) {
2339 edit_error_dialog (_(" Sort block "), _(" You must first highlight a block of text. "));
2340 return 0;
2342 edit_save_block (edit, catstrs (home_dir, BLOCK_FILE, 0), start_mark, end_mark);
2344 exp = old ? old : "";
2346 exp = input_dialog (_(" Run Sort "),
2347 _(" Enter sort options (see manpage) separated by whitespace: "), exp);
2349 if (!exp)
2350 return 1;
2351 if (old)
2352 g_free (old);
2353 old = exp;
2355 e = system (catstrs (" sort ", exp, " ", home_dir, BLOCK_FILE, " > ", home_dir, TEMP_FILE, 0));
2356 if (e) {
2357 if (e == -1 || e == 127) {
2358 edit_error_dialog (_(" Sort "),
2359 get_sys_error (_(" Cannot execute sort command ")));
2360 } else {
2361 char q[8];
2362 sprintf (q, "%d ", e);
2363 edit_error_dialog (_(" Sort "),
2364 catstrs (_(" Sort returned non-zero: "), q, 0));
2366 return -1;
2369 edit->force |= REDRAW_COMPLETELY;
2371 if (edit_block_delete_cmd (edit))
2372 return 1;
2373 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, 0));
2374 return 0;
2378 * Ask user for a command, execute it and paste its output back to the
2379 * editor.
2382 edit_ext_cmd (WEdit *edit)
2384 char *exp;
2385 int e;
2387 exp =
2388 input_dialog (_("Paste output of external command"),
2389 _("Enter shell command(s):"), NULL);
2391 if (!exp)
2392 return 1;
2394 e = system (catstrs (exp, " > ", home_dir, TEMP_FILE, 0));
2395 g_free (exp);
2397 if (e) {
2398 edit_error_dialog (_("External command"),
2399 get_sys_error (_("Cannot execute command")));
2400 return -1;
2403 edit->force |= REDRAW_COMPLETELY;
2405 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, 0));
2406 return 0;
2409 /* if block is 1, a block must be highlighted and the shell command
2410 processes it. If block is 0 the shell command is a straight system
2411 command, that just produces some output which is to be inserted */
2412 void
2413 edit_block_process_cmd (WEdit *edit, const char *shell_cmd, int block)
2415 long start_mark, end_mark;
2416 char buf[BUFSIZ];
2417 FILE *script_home = NULL;
2418 FILE *script_src = NULL;
2419 FILE *block_file = NULL;
2420 char *o = NULL;
2421 char *h = NULL;
2422 char *b = NULL;
2423 char *quoted_name = NULL;
2425 o = catstrs (mc_home, shell_cmd, 0); /* original source script */
2426 h = catstrs (home_dir, EDIT_DIR, shell_cmd, 0); /* home script */
2427 b = catstrs (home_dir, BLOCK_FILE, 0); /* block file */
2429 if (!(script_home = fopen (h, "r"))) {
2430 if (!(script_home = fopen (h, "w"))) {
2431 edit_error_dialog ("", get_sys_error (catstrs
2433 ("Error creating script:"),
2434 h, 0)));
2435 return;
2437 if (!(script_src = fopen (o, "r"))) {
2438 fclose (script_home);
2439 unlink (h);
2440 edit_error_dialog ("", get_sys_error (catstrs
2441 (_("Error reading script:"),
2442 o, 0)));
2443 return;
2445 while (fgets (buf, sizeof (buf), script_src))
2446 fputs (buf, script_home);
2447 if (fclose (script_home)) {
2448 edit_error_dialog ("", get_sys_error (catstrs
2450 ("Error closing script:"),
2451 h, 0)));
2452 return;
2454 chmod (h, 0700);
2455 edit_error_dialog ("", get_sys_error (catstrs
2456 (_("Script created:"), h, 0)));
2459 open_error_pipe ();
2461 if (block) { /* for marked block run indent formatter */
2462 if (eval_marks (edit, &start_mark, &end_mark)) {
2463 edit_error_dialog (_("Process block"),
2465 (" You must first highlight a block of text. "));
2466 return;
2468 edit_save_block (edit, b, start_mark, end_mark);
2469 quoted_name = name_quote (edit->filename, 0);
2471 * Run script.
2472 * Initial space is to avoid polluting bash history.
2473 * Arguments:
2474 * $1 - name of the edited file (to check its extension etc).
2475 * $2 - file containing the current block.
2476 * $3 - file where error messages should be put
2477 * (for compatibility with old scripts).
2479 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ", quoted_name,
2480 " ", home_dir, BLOCK_FILE " /dev/null", NULL));
2482 } else {
2484 * No block selected, just execute the command for the file.
2485 * Arguments:
2486 * $1 - name of the edited file.
2488 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2489 quoted_name, NULL));
2491 g_free (quoted_name);
2492 close_error_pipe (0, 0);
2494 edit_refresh_cmd (edit);
2495 edit->force |= REDRAW_COMPLETELY;
2497 /* insert result block */
2498 if (block) {
2499 if (edit_block_delete_cmd (edit))
2500 return;
2501 edit_insert_file (edit, b);
2502 if ((block_file = fopen (b, "w")))
2503 fclose (block_file);
2504 return;
2507 return;
2510 /* prints at the cursor */
2511 /* returns the number of chars printed */
2512 int edit_print_string (WEdit * e, const char *s)
2514 int i = 0;
2515 while (s[i])
2516 edit_execute_cmd (e, -1, (unsigned char) s[i++]);
2517 e->force |= REDRAW_COMPLETELY;
2518 edit_update_screen (e);
2519 return i;
2523 static void pipe_mail (WEdit *edit, char *to, char *subject, char *cc)
2525 FILE *p = 0;
2526 char *s;
2528 to = name_quote (to, 0);
2529 subject = name_quote (subject, 0);
2530 cc = name_quote (cc, 0);
2531 s = g_strdup_printf ("mail -s %s -c %s %s", subject, cc, to);
2532 g_free (to);
2533 g_free (subject);
2534 g_free (cc);
2536 if (s) {
2537 p = popen (s, "w");
2538 g_free (s);
2541 if (p) {
2542 long i;
2543 for (i = 0; i < edit->last_byte; i++)
2544 fputc (edit_get_byte (edit, i), p);
2545 pclose (p);
2549 #define MAIL_DLG_HEIGHT 12
2551 void edit_mail_dialog (WEdit * edit)
2553 char *tmail_to;
2554 char *tmail_subject;
2555 char *tmail_cc;
2557 static char *mail_cc_last = 0;
2558 static char *mail_subject_last = 0;
2559 static char *mail_to_last = 0;
2561 QuickDialog Quick_input =
2562 {50, MAIL_DLG_HEIGHT, -1, 0, N_(" Mail "),
2563 "[Input Line Keys]", 0};
2565 QuickWidget quick_widgets[] =
2567 {quick_button, 6, 10, 9, MAIL_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
2568 0, NULL},
2569 {quick_button, 2, 10, 9, MAIL_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
2570 0, NULL},
2571 {quick_input, 3, 50, 8, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2572 0, "mail-dlg-input"},
2573 {quick_label, 2, 50, 7, MAIL_DLG_HEIGHT, N_(" Copies to"), 0, 0, 0,
2574 0, 0},
2575 {quick_input, 3, 50, 6, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2576 0, "mail-dlg-input-2"},
2577 {quick_label, 2, 50, 5, MAIL_DLG_HEIGHT, N_(" Subject"), 0, 0, 0,
2578 0, 0},
2579 {quick_input, 3, 50, 4, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2580 0, "mail-dlg-input-3"},
2581 {quick_label, 2, 50, 3, MAIL_DLG_HEIGHT, N_(" To"), 0, 0, 0,
2582 0, 0},
2583 {quick_label, 2, 50, 2, MAIL_DLG_HEIGHT, N_(" mail -s <subject> -c <cc> <to>"), 0, 0, 0,
2584 0, 0},
2585 {0}};
2587 quick_widgets[2].str_result = &tmail_cc;
2588 quick_widgets[2].text = mail_cc_last ? mail_cc_last : "";
2589 quick_widgets[4].str_result = &tmail_subject;
2590 quick_widgets[4].text = mail_subject_last ? mail_subject_last : "";
2591 quick_widgets[6].str_result = &tmail_to;
2592 quick_widgets[6].text = mail_to_last ? mail_to_last : "";
2594 Quick_input.widgets = quick_widgets;
2596 if (quick_dialog (&Quick_input) != B_CANCEL) {
2597 if (mail_cc_last)
2598 g_free (mail_cc_last);
2599 if (mail_subject_last)
2600 g_free (mail_subject_last);
2601 if (mail_to_last)
2602 g_free (mail_to_last);
2603 mail_cc_last = tmail_cc;
2604 mail_subject_last = tmail_subject;
2605 mail_to_last = tmail_to;
2606 pipe_mail (edit, mail_to_last, mail_subject_last, mail_cc_last);
2611 /*******************/
2612 /* Word Completion */
2613 /*******************/
2616 /* find first character of current word */
2617 static int edit_find_word_start (WEdit *edit, long *word_start, int *word_len)
2619 int i, c, last;
2621 /* return if at begin of file */
2622 if (edit->curs1 <= 0)
2623 return 0;
2625 c = (unsigned char) edit_get_byte (edit, edit->curs1 - 1);
2626 /* return if not at end or in word */
2627 if (isspace (c) || !(isalnum (c) || c == '_'))
2628 return 0;
2630 /* search start of word to be completed */
2631 for (i = 2;; i++) {
2632 /* return if at begin of file */
2633 if (edit->curs1 - i < 0)
2634 return 0;
2636 last = c;
2637 c = (unsigned char) edit_get_byte (edit, edit->curs1 - i);
2639 if (!(isalnum (c) || c == '_')) {
2640 /* return if word starts with digit */
2641 if (isdigit (last))
2642 return 0;
2644 *word_start = edit->curs1 - (i - 1); /* start found */
2645 *word_len = i - 1;
2646 break;
2649 /* success */
2650 return 1;
2654 /* (re)set search parameters to the given values */
2655 static void edit_set_search_parameters (int rs, int rb, int rr, int rw, int rc)
2657 replace_scanf = rs;
2658 replace_backwards = rb;
2659 replace_regexp = rr;
2660 replace_whole = rw;
2661 replace_case = rc;
2665 #define MAX_WORD_COMPLETIONS 100 /* in listbox */
2667 /* collect the possible completions */
2668 static int
2669 edit_collect_completions (WEdit *edit, long start, int word_len,
2670 char *match_expr, struct selection *compl,
2671 int *num)
2673 int len, max_len = 0, i, skip;
2674 char *bufpos;
2676 /* collect max MAX_WORD_COMPLETIONS completions */
2677 while (*num < MAX_WORD_COMPLETIONS) {
2678 /* get next match */
2679 start =
2680 edit_find (start - 1, (unsigned char *) match_expr, &len,
2681 edit->last_byte,
2682 (int (*)(void *, long)) edit_get_byte,
2683 (void *) edit, 0);
2685 /* not matched */
2686 if (start < 0)
2687 break;
2689 /* add matched completion if not yet added */
2690 bufpos =
2691 &edit->
2692 buffers1[start >> S_EDIT_BUF_SIZE][start & M_EDIT_BUF_SIZE];
2693 skip = 0;
2694 for (i = 0; i < *num; i++) {
2695 if (strncmp
2696 (&compl[i].text[word_len], &bufpos[word_len],
2697 max (len, compl[i].len) - word_len) == 0) {
2698 skip = 1;
2699 break; /* skip it, already added */
2702 if (skip)
2703 continue;
2705 compl[*num].text = g_malloc (len + 1);
2706 compl[*num].len = len;
2707 for (i = 0; i < len; i++)
2708 compl[*num].text[i] = *(bufpos + i);
2709 compl[*num].text[i] = '\0';
2710 (*num)++;
2712 /* note the maximal length needed for the completion dialog */
2713 if (len > max_len)
2714 max_len = len;
2716 return max_len;
2720 /* let the user select its preferred completion */
2721 static void
2722 edit_completion_dialog (WEdit *edit, int max_len, int word_len,
2723 struct selection *compl, int num_compl)
2725 int start_x, start_y, offset, i;
2726 char *curr = NULL;
2727 Dlg_head *compl_dlg;
2728 WListbox *compl_list;
2729 unsigned int compl_dlg_h; /* completion dialog height */
2730 unsigned int compl_dlg_w; /* completion dialog width */
2732 /* calculate the dialog metrics */
2733 compl_dlg_h = num_compl + 2;
2734 compl_dlg_w = max_len + 4;
2735 start_x = edit->curs_col + edit->start_col - (compl_dlg_w / 2);
2736 start_y = edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + 1;
2738 if (start_x < 0)
2739 start_x = 0;
2740 if (compl_dlg_w > COLS)
2741 compl_dlg_w = COLS;
2742 if (compl_dlg_h > LINES - 2)
2743 compl_dlg_h = LINES - 2;
2745 offset = start_x + compl_dlg_w - COLS;
2746 if (offset > 0)
2747 start_x -= offset;
2748 offset = start_y + compl_dlg_h - LINES;
2749 if (offset > 0)
2750 start_y -= (offset + 1);
2752 /* create the dialog */
2753 compl_dlg =
2754 create_dlg (start_y, start_x, compl_dlg_h, compl_dlg_w,
2755 dialog_colors, NULL, "[Completion]", NULL,
2756 DLG_COMPACT);
2758 /* create the listbox */
2759 compl_list =
2760 listbox_new (1, 1, compl_dlg_w - 2, compl_dlg_h - 2, NULL);
2762 /* add the dialog */
2763 add_widget (compl_dlg, compl_list);
2765 /* fill the listbox with the completions */
2766 for (i = 0; i < num_compl; i++)
2767 listbox_add_item (compl_list, 0, 0, compl[i].text, NULL);
2769 /* pop up the dialog */
2770 run_dlg (compl_dlg);
2772 /* apply the choosen completion */
2773 if (compl_dlg->ret_value == B_ENTER) {
2774 listbox_get_current (compl_list, &curr, NULL);
2775 if (curr)
2776 for (curr += word_len; *curr; curr++)
2777 edit_insert (edit, *curr);
2780 /* destroy dialog before return */
2781 destroy_dlg (compl_dlg);
2786 * Complete current word using regular expression search
2787 * backwards beginning at the current cursor position.
2789 void
2790 edit_complete_word_cmd (WEdit *edit)
2792 int word_len = 0, i, num_compl = 0, max_len;
2793 long word_start = 0;
2794 char *bufpos;
2795 char match_expr[MAX_REPL_LEN];
2796 struct selection compl[MAX_WORD_COMPLETIONS]; /* completions */
2798 /* don't want to disturb another search */
2799 int old_rs = replace_scanf;
2800 int old_rb = replace_backwards;
2801 int old_rr = replace_regexp;
2802 int old_rw = replace_whole;
2803 int old_rc = replace_case;
2805 /* search start of word to be completed */
2806 if (!edit_find_word_start (edit, &word_start, &word_len))
2807 return;
2809 /* prepare match expression */
2810 bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE]
2811 [word_start & M_EDIT_BUF_SIZE];
2812 strncpy (match_expr, bufpos, word_len);
2813 match_expr[word_len] = '\0';
2814 strcat (match_expr, "[a-zA-Z_0-9]+");
2816 /* init search: backward, regexp, whole word, case sensitive */
2817 edit_set_search_parameters (0, 1, 1, 1, 1);
2819 /* collect the possible completions */
2820 /* start search from curs1 down to begin of file */
2821 max_len =
2822 edit_collect_completions (edit, word_start, word_len, match_expr,
2823 (struct selection *) &compl, &num_compl);
2825 if (num_compl > 0) {
2826 /* insert completed word if there is only one match */
2827 if (num_compl == 1) {
2828 for (i = word_len; i < compl[0].len; i++)
2829 edit_insert (edit, *(compl[0].text + i));
2831 /* more than one possible completion => ask the user */
2832 else {
2833 /* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
2834 /* !!! pressed again the selection dialog pops up, but that !!! */
2835 /* !!! seems to require a further internal state !!! */
2836 /*beep (); */
2838 /* let the user select the preferred completion */
2839 edit_completion_dialog (edit, max_len, word_len,
2840 (struct selection *) &compl,
2841 num_compl);
2845 /* release memory before return */
2846 for (i = 0; i < num_compl; i++)
2847 g_free (compl[i].text);
2849 /* restore search parameters */
2850 edit_set_search_parameters (old_rs, old_rb, old_rr, old_rw, old_rc);