* extfs/uarj.in (mcarjfs_copyout): Use the regular extract command,
[midnight-commander.git] / edit / editcmd.c
blob4b28ec1035febad96aa647a1e8b51a14b2ca1659
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 <stdio.h>
30 #include <stdarg.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <sys/stat.h>
37 #include <errno.h>
39 #include <stdlib.h>
41 #include "../src/global.h"
43 #include "edit.h"
44 #include "editlock.h"
45 #include "editcmddef.h"
46 #include "edit-widget.h"
48 #include "../src/color.h" /* dialog_colors */
49 #include "../src/tty.h" /* LINES */
50 #include "../src/widget.h" /* listbox_new() */
51 #include "../src/layout.h" /* clr_scr() */
52 #include "../src/main.h" /* mc_home */
53 #include "../src/help.h" /* interactive_display() */
54 #include "../src/key.h" /* XCTRL */
55 #include "../src/dialog.h" /* do_refresh() */
56 #include "../src/wtools.h" /* message() */
57 #include "../src/charsets.h"
59 #define edit_get_load_file(f,h) input_expand_dialog (h, _(" Enter file name: "), f)
60 #define edit_get_save_file(f,h) input_expand_dialog (h, _(" Enter file name: "), f)
62 struct selection {
63 unsigned char * text;
64 int len;
67 /* globals: */
69 /* search and replace: */
70 static int replace_scanf = 0;
71 static int replace_regexp = 0;
72 static int replace_all = 0;
73 static int replace_prompt = 1;
74 static int replace_whole = 0;
75 static int replace_case = 0;
76 static int replace_backwards = 0;
77 static int search_create_bookmark = 0;
79 /* queries on a save */
80 int edit_confirm_save = 1;
82 #define NUM_REPL_ARGS 64
83 #define MAX_REPL_LEN 1024
85 static int edit_save_cmd (WEdit *edit);
86 static unsigned char *edit_get_block (WEdit *edit, long start,
87 long finish, int *l);
89 static inline int my_lower_case (int c)
91 return tolower(c & 0xFF);
94 static const char *strcasechr (const unsigned char *s, int c)
96 for (c = my_lower_case (c); my_lower_case ((int) *s) != c; ++s)
97 if (*s == '\0')
98 return 0;
99 return (const char *) s;
102 #ifndef HAVE_MEMMOVE
103 /* for Christophe */
104 static void *memmove (void *dest, const void *src, size_t n)
106 char *t;
107 const char *s;
109 if (dest <= src) {
110 t = (char *) dest;
111 s = (const char *) src;
112 while (n--)
113 *t++ = *s++;
114 } else {
115 t = (char *) dest + n;
116 s = (const char *) src + n;
117 while (n--)
118 *--t = *--s;
120 return dest;
122 #endif /* !HAVE_MEMMOVE */
124 /* #define itoa MY_itoa <---- this line is now in edit.h */
125 static char *
126 MY_itoa (int i)
128 static char t[14];
129 char *s = t + 13;
130 int j = i;
131 *s-- = 0;
132 do {
133 *s-- = i % 10 + '0';
134 } while ((i = i / 10));
135 if (j < 0)
136 *s-- = '-';
137 return ++s;
140 /* Temporary strings */
141 static char *stacked[16];
144 This joins strings end on end and allocates memory for the result.
145 The result is later automatically free'd and must not be free'd
146 by the caller.
148 static const char *
149 catstrs (const char *first,...)
151 static int i = 0;
152 va_list ap;
153 int len;
154 char *data;
156 if (!first)
157 return 0;
159 len = strlen (first);
160 va_start (ap, first);
162 while ((data = va_arg (ap, char *)) != 0)
163 len += strlen (data);
165 len++;
167 i = (i + 1) % 16;
168 g_free (stacked[i]);
170 stacked[i] = g_malloc (len);
171 va_end (ap);
172 va_start (ap, first);
173 strcpy (stacked[i], first);
174 while ((data = va_arg (ap, char *)) != 0)
175 strcat (stacked[i], data);
176 va_end (ap);
178 return stacked[i];
181 /* Free temporary strings */
182 void freestrs(void)
184 size_t i;
186 for (i = 0; i < sizeof(stacked) / sizeof(stacked[0]); i++) {
187 g_free (stacked[i]);
188 stacked[i] = NULL;
192 void edit_help_cmd (WEdit * edit)
194 interactive_display (NULL, "[Internal File Editor]");
195 edit->force |= REDRAW_COMPLETELY;
198 void edit_refresh_cmd (WEdit * edit)
200 #ifndef HAVE_SLANG
201 clr_scr();
202 do_refresh();
203 #else
205 int color;
206 edit_get_syntax_color (edit, -1, &color);
208 touchwin(stdscr);
209 #endif /* !HAVE_SLANG */
210 mc_refresh();
211 doupdate();
214 /* If 0 (quick save) then a) create/truncate <filename> file,
215 b) save to <filename>;
216 if 1 (safe save) then a) save to <tempnam>,
217 b) rename <tempnam> to <filename>;
218 if 2 (do backups) then a) save to <tempnam>,
219 b) rename <filename> to <filename.backup_ext>,
220 c) rename <tempnam> to <filename>. */
222 /* returns 0 on error */
223 static int
224 edit_save_file (WEdit *edit, const char *filename)
226 char *p;
227 long filelen = 0;
228 char *savename = 0;
229 int this_save_mode, fd;
231 if (!filename)
232 return 0;
233 if (!*filename)
234 return 0;
236 if (*filename != PATH_SEP && edit->dir) {
237 savename = concat_dir_and_file (edit->dir, filename);
238 filename = catstrs (savename, (char *) NULL);
239 g_free (savename);
242 if (!vfs_file_is_local (filename) ||
243 (fd = mc_open (filename, O_WRONLY | O_BINARY)) == -1) {
245 * The file does not exists yet, so no safe save or
246 * backup are necessary.
248 this_save_mode = EDIT_QUICK_SAVE;
249 } else {
250 mc_close (fd);
251 this_save_mode = option_save_mode;
254 if (this_save_mode != EDIT_QUICK_SAVE) {
255 char *savedir, *saveprefix;
256 const char *slashpos;
257 slashpos = strrchr (filename, PATH_SEP);
258 if (slashpos) {
259 savedir = g_strdup (filename);
260 savedir[slashpos - filename + 1] = '\0';
261 } else
262 savedir = g_strdup (".");
263 saveprefix = concat_dir_and_file (savedir, "cooledit");
264 g_free (savedir);
265 fd = mc_mkstemps (&savename, saveprefix, NULL);
266 g_free (saveprefix);
267 if (!savename)
268 return 0;
269 /* FIXME:
270 * Close for now because mc_mkstemps use pure open system call
271 * to create temporary file and it needs to be reopened by
272 * VFS-aware mc_open().
274 close (fd);
275 } else
276 savename = g_strdup (filename);
278 mc_chown (savename, edit->stat1.st_uid, edit->stat1.st_gid);
279 mc_chmod (savename, edit->stat1.st_mode);
281 if ((fd =
282 mc_open (savename, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY,
283 edit->stat1.st_mode)) == -1)
284 goto error_save;
286 /* pipe save */
287 if ((p = edit_get_write_filter (savename, filename))) {
288 FILE *file;
290 mc_close (fd);
291 file = (FILE *) popen (p, "w");
293 if (file) {
294 filelen = edit_write_stream (edit, file);
295 #if 1
296 pclose (file);
297 #else
298 if (pclose (file) != 0) {
299 edit_error_dialog (_("Error"),
300 catstrs (_(" Error writing to pipe: "),
301 p, " ", (char *) NULL));
302 g_free (p);
303 goto error_save;
305 #endif
306 } else {
307 edit_error_dialog (_("Error"),
308 get_sys_error (catstrs
310 (" Cannot open pipe for writing: "),
311 p, " ", (char *) NULL)));
312 g_free (p);
313 goto error_save;
315 g_free (p);
316 } else {
317 long buf;
318 buf = 0;
319 filelen = edit->last_byte;
320 while (buf <= (edit->curs1 >> S_EDIT_BUF_SIZE) - 1) {
321 if (mc_write (fd, (char *) edit->buffers1[buf], EDIT_BUF_SIZE)
322 != EDIT_BUF_SIZE) {
323 mc_close (fd);
324 goto error_save;
326 buf++;
328 if (mc_write
329 (fd, (char *) edit->buffers1[buf],
330 edit->curs1 & M_EDIT_BUF_SIZE) !=
331 (edit->curs1 & M_EDIT_BUF_SIZE)) {
332 filelen = -1;
333 } else if (edit->curs2) {
334 edit->curs2--;
335 buf = (edit->curs2 >> S_EDIT_BUF_SIZE);
336 if (mc_write
337 (fd,
338 (char *) edit->buffers2[buf] + EDIT_BUF_SIZE -
339 (edit->curs2 & M_EDIT_BUF_SIZE) - 1,
340 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) !=
341 1 + (edit->curs2 & M_EDIT_BUF_SIZE)) {
342 filelen = -1;
343 } else {
344 while (--buf >= 0) {
345 if (mc_write
346 (fd, (char *) edit->buffers2[buf],
347 EDIT_BUF_SIZE) != EDIT_BUF_SIZE) {
348 filelen = -1;
349 break;
353 edit->curs2++;
355 if (mc_close (fd))
356 goto error_save;
359 if (filelen != edit->last_byte)
360 goto error_save;
361 if (this_save_mode == EDIT_DO_BACKUP)
362 if (mc_rename (filename, catstrs (filename, option_backup_ext, (char *) NULL))
363 == -1)
364 goto error_save;
365 if (this_save_mode != EDIT_QUICK_SAVE)
366 if (mc_rename (savename, filename) == -1)
367 goto error_save;
368 g_free (savename);
369 return 1;
370 error_save:
371 /* FIXME: Is this safe ?
372 * if (this_save_mode != EDIT_QUICK_SAVE)
373 * mc_unlink (savename);
375 g_free (savename);
376 return 0;
380 I changed this from Oleg's original routine so
381 that option_backup_ext works with coolwidgets as well. This
382 does mean there is a memory leak - paul.
384 void menu_save_mode_cmd (void)
386 #define DLG_X 38
387 #define DLG_Y 10
388 static char *str_result;
389 static int save_mode_new;
390 static const char *str[] =
392 N_("Quick save "),
393 N_("Safe save "),
394 N_("Do backups -->")};
395 static QuickWidget widgets[] =
397 {quick_button, 18, DLG_X, 7, DLG_Y, N_("&Cancel"), 0,
398 B_CANCEL, 0, 0, "c"},
399 {quick_button, 6, DLG_X, 7, DLG_Y, N_("&OK"), 0,
400 B_ENTER, 0, 0, "o"},
401 {quick_input, 23, DLG_X, 5, DLG_Y, 0, 9,
402 0, 0, &str_result, "edit-backup-ext"},
403 {quick_label, 22, DLG_X, 4, DLG_Y, N_("Extension:"), 0,
404 0, 0, 0, "savemext"},
405 {quick_radio, 4, DLG_X, 3, DLG_Y, "", 3,
406 0, &save_mode_new, (char **) str, "t"},
407 NULL_QuickWidget};
408 static QuickDialog dialog =
409 {DLG_X, DLG_Y, -1, -1, N_(" Edit Save Mode "), "[Edit Save Mode]",
410 widgets, 0};
411 static int i18n_flag = 0;
413 if (!i18n_flag) {
414 size_t i;
415 size_t maxlen = 0;
416 int dlg_x;
417 size_t l1;
419 /* OK/Cancel buttons */
420 l1 = strlen (_(widgets[0].text)) + strlen (_(widgets[1].text)) + 5;
421 maxlen = max (maxlen, l1);
423 for (i = 0; i < 3; i++ ) {
424 str[i] = _(str[i]);
425 maxlen = max (maxlen, strlen (str[i]) + 7);
427 i18n_flag = 1;
429 dlg_x = maxlen + strlen (_(widgets[3].text)) + 5 + 1;
430 widgets[2].hotkey_pos = strlen (_(widgets[3].text)); /* input field length */
431 dlg_x = min (COLS, dlg_x);
432 dialog.xlen = dlg_x;
434 i = (dlg_x - l1)/3;
435 widgets[1].relative_x = i;
436 widgets[0].relative_x = i + strlen (_(widgets[1].text)) + i + 4;
438 widgets[2].relative_x = widgets[3].relative_x = maxlen + 2;
440 for (i = 0; i < sizeof (widgets)/sizeof (widgets[0]); i++)
441 widgets[i].x_divisions = dlg_x;
444 widgets[2].text = option_backup_ext;
445 widgets[4].value = option_save_mode;
446 if (quick_dialog (&dialog) != B_ENTER)
447 return;
448 option_save_mode = save_mode_new;
449 option_backup_ext = str_result; /* this is a memory leak */
450 option_backup_ext_int = 0;
451 str_result[min (strlen (str_result), sizeof (int))] = '\0';
452 memcpy (&option_backup_ext_int, str_result, strlen (option_backup_ext));
455 void
456 edit_set_filename (WEdit *edit, const char *f)
458 g_free (edit->filename);
459 if (!f)
460 f = "";
461 edit->filename = g_strdup (f);
462 if (edit->dir == NULL && *f != PATH_SEP)
463 #ifdef USE_VFS
464 edit->dir = g_strdup (vfs_get_current_dir ());
465 #else
466 edit->dir = g_get_current_dir ();
467 #endif
470 /* Here we want to warn the users of overwriting an existing file,
471 but only if they have made a change to the filename */
472 /* returns 1 on success */
474 edit_save_as_cmd (WEdit *edit)
476 /* This heads the 'Save As' dialog box */
477 char *exp;
478 int save_lock = 0;
479 int different_filename = 0;
481 exp = edit_get_save_file (edit->filename, _(" Save As "));
482 edit_push_action (edit, KEY_PRESS + edit->start_display);
484 if (exp) {
485 if (!*exp) {
486 g_free (exp);
487 edit->force |= REDRAW_COMPLETELY;
488 return 0;
489 } else {
490 if (strcmp (edit->filename, exp)) {
491 int file;
492 different_filename = 1;
493 if ((file = mc_open (exp, O_RDONLY | O_BINARY)) != -1) {
494 /* the file exists */
495 mc_close (file);
496 /* Overwrite the current file or cancel the operation */
497 if (edit_query_dialog2
498 (_("Warning"),
499 _(" A file already exists with this name. "),
500 _("&Overwrite"), _("&Cancel"))) {
501 edit->force |= REDRAW_COMPLETELY;
502 g_free (exp);
503 return 0;
506 save_lock = edit_lock_file (exp);
507 } else {
508 /* filenames equal, check if already locked */
509 if (!edit->locked && !edit->delete_file)
510 save_lock = edit_lock_file (exp);
513 if (edit_save_file (edit, exp)) {
514 /* Succesful, so unlock both files */
515 if (strcmp (edit->filename, exp)) {
516 if (save_lock)
517 edit_unlock_file (exp);
518 if (edit->locked)
519 edit->locked = edit_unlock_file (edit->filename);
520 } else {
521 if (edit->locked || save_lock)
522 edit->locked = edit_unlock_file (edit->filename);
525 edit_set_filename (edit, exp);
526 g_free (exp);
527 edit->modified = 0;
528 edit->delete_file = 0;
529 if (different_filename)
530 edit_load_syntax (edit, 0, 0);
531 edit->force |= REDRAW_COMPLETELY;
532 return 1;
533 } else {
534 /* Failed, so maintain modify (not save) lock */
535 if (strcmp (edit->filename, exp) && save_lock)
536 edit_unlock_file (exp);
537 if (save_lock)
538 edit->locked = edit_unlock_file (edit->filename);
539 g_free (exp);
540 edit_error_dialog (_(" Save As "),
541 get_sys_error (_
542 (" Cannot save file. ")));
543 edit->force |= REDRAW_COMPLETELY;
544 return 0;
548 edit->force |= REDRAW_COMPLETELY;
549 return 0;
552 /* {{{ Macro stuff starts here */
554 static cb_ret_t
555 raw_callback (struct Dlg_head *h, dlg_msg_t msg, int parm)
557 switch (msg) {
558 case DLG_KEY:
559 h->running = 0;
560 h->ret_value = parm;
561 return MSG_HANDLED;
562 default:
563 return default_dlg_callback (h, msg, parm);
567 /* gets a raw key from the keyboard. Passing cancel = 1 draws
568 a cancel button thus allowing c-c etc. Alternatively, cancel = 0
569 will return the next key pressed. ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
570 and Esc are cannot returned */
572 edit_raw_key_query (const char *heading, const char *query, int cancel)
574 int w = strlen (query) + 7;
575 struct Dlg_head *raw_dlg =
576 create_dlg (0, 0, 7, w, dialog_colors, raw_callback,
577 NULL, heading,
578 DLG_CENTER | DLG_TRYUP | DLG_WANT_TAB);
579 add_widget (raw_dlg,
580 input_new (3 - cancel, w - 5, INPUT_COLOR, 2, "", 0));
581 add_widget (raw_dlg, label_new (3 - cancel, 2, query));
582 if (cancel)
583 add_widget (raw_dlg,
584 button_new (4, w / 2 - 5, B_CANCEL, NORMAL_BUTTON,
585 _("Cancel"), 0));
586 run_dlg (raw_dlg);
587 w = raw_dlg->ret_value;
588 destroy_dlg (raw_dlg);
589 if (cancel) {
590 if (w == XCTRL ('g') || w == XCTRL ('c') || w == ESC_CHAR
591 || w == B_CANCEL)
592 return 0;
595 return w;
598 /* creates a macro file if it doesn't exist */
599 static FILE *edit_open_macro_file (const char *r)
601 const char *filename;
602 int file;
603 filename = catstrs (home_dir, MACRO_FILE, (char *) NULL);
604 if ((file = open (filename, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1)
605 return 0;
606 close (file);
607 return fopen (filename, r);
610 #define MAX_MACROS 1024
611 static int saved_macro[MAX_MACROS + 1];
612 static int saved_macros_loaded = 0;
615 This is just to stop the macro file be loaded over and over for keys
616 that aren't defined to anything. On slow systems this could be annoying.
618 static int
619 macro_exists (int k)
621 int i;
622 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++)
623 if (saved_macro[i] == k)
624 return i;
625 return -1;
628 /* returns 1 on error */
629 static int
630 edit_delete_macro (WEdit * edit, int k)
632 struct macro macro[MAX_MACRO_LENGTH];
633 FILE *f, *g;
634 int s, i, n, j = 0;
636 (void) edit;
638 if (saved_macros_loaded)
639 if ((j = macro_exists (k)) < 0)
640 return 0;
641 g = fopen (catstrs (home_dir, TEMP_FILE, (char *) NULL), "w");
642 if (!g) {
643 edit_error_dialog (_(" Delete macro "),
644 get_sys_error (_(" Cannot open temp file ")));
645 return 1;
647 f = edit_open_macro_file ("r");
648 if (!f) {
649 edit_error_dialog (_(" Delete macro "),
650 get_sys_error (_(" Cannot open macro file ")));
651 fclose (g);
652 return 1;
654 for (;;) {
655 n = fscanf (f, ("key '%d 0': "), &s);
656 if (!n || n == EOF)
657 break;
658 n = 0;
659 while (fscanf (f, "%hd %hd, ", &macro[n].command, &macro[n].ch))
660 n++;
661 fscanf (f, ";\n");
662 if (s != k) {
663 fprintf (g, ("key '%d 0': "), s);
664 for (i = 0; i < n; i++)
665 fprintf (g, "%hd %hd, ", macro[i].command, macro[i].ch);
666 fprintf (g, ";\n");
669 fclose (f);
670 fclose (g);
671 if (rename (catstrs (home_dir, TEMP_FILE, (char *) NULL), catstrs (home_dir, MACRO_FILE, (char *) NULL)) == -1) {
672 edit_error_dialog (_(" Delete macro "),
673 get_sys_error (_(" Cannot overwrite macro file ")));
674 return 1;
676 if (saved_macros_loaded)
677 memmove (saved_macro + j, saved_macro + j + 1, sizeof (int) * (MAX_MACROS - j - 1));
678 return 0;
681 /* returns 0 on error */
682 int edit_save_macro_cmd (WEdit * edit, struct macro macro[], int n)
684 FILE *f;
685 int s, i;
687 edit_push_action (edit, KEY_PRESS + edit->start_display);
688 s = edit_raw_key_query (_(" Save macro "),
689 _(" Press the macro's new hotkey: "), 1);
690 edit->force |= REDRAW_COMPLETELY;
691 if (s) {
692 if (edit_delete_macro (edit, s))
693 return 0;
694 f = edit_open_macro_file ("a+");
695 if (f) {
696 fprintf (f, ("key '%d 0': "), s);
697 for (i = 0; i < n; i++)
698 fprintf (f, "%hd %hd, ", macro[i].command, macro[i].ch);
699 fprintf (f, ";\n");
700 fclose (f);
701 if (saved_macros_loaded) {
702 for (i = 0; i < MAX_MACROS && saved_macro[i]; i++);
703 saved_macro[i] = s;
705 return 1;
706 } else
707 edit_error_dialog (_(" Save macro "), get_sys_error (_(" Cannot open macro file ")));
709 return 0;
712 void edit_delete_macro_cmd (WEdit * edit)
714 int command;
716 command = edit_raw_key_query (_ (" Delete macro "),
717 _ (" Press macro hotkey: "), 1);
719 if (!command)
720 return;
722 edit_delete_macro (edit, command);
725 /* return 0 on error */
726 int edit_load_macro_cmd (WEdit * edit, struct macro macro[], int *n, int k)
728 FILE *f;
729 int s, i = 0, found = 0;
731 (void) edit;
733 if (saved_macros_loaded)
734 if (macro_exists (k) < 0)
735 return 0;
737 if ((f = edit_open_macro_file ("r"))) {
738 struct macro dummy;
739 do {
740 int u;
741 u = fscanf (f, ("key '%d 0': "), &s);
742 if (!u || u == EOF)
743 break;
744 if (!saved_macros_loaded)
745 saved_macro[i++] = s;
746 if (!found) {
747 *n = 0;
748 while (*n < MAX_MACRO_LENGTH && 2 == fscanf (f, "%hd %hd, ", &macro[*n].command, &macro[*n].ch))
749 (*n)++;
750 } else {
751 while (2 == fscanf (f, "%hd %hd, ", &dummy.command, &dummy.ch));
753 fscanf (f, ";\n");
754 if (s == k)
755 found = 1;
756 } while (!found || !saved_macros_loaded);
757 if (!saved_macros_loaded) {
758 saved_macro[i] = 0;
759 saved_macros_loaded = 1;
761 fclose (f);
762 return found;
763 } else
764 edit_error_dialog (_(" Load macro "),
765 get_sys_error (_(" Cannot open macro file ")));
766 return 0;
769 /* }}} Macro stuff starts here */
771 /* returns 1 on success */
772 int edit_save_confirm_cmd (WEdit * edit)
774 const char *f;
776 if (edit_confirm_save) {
777 f = catstrs (_(" Confirm save file? : "), edit->filename, " ", (char *) NULL);
778 if (edit_query_dialog2 (_(" Save file "), f, _("&Save"), _("&Cancel")))
779 return 0;
781 return edit_save_cmd (edit);
785 /* returns 1 on success */
786 static int
787 edit_save_cmd (WEdit *edit)
789 int res, save_lock = 0;
791 if (!edit->locked && !edit->delete_file)
792 save_lock = edit_lock_file (edit->filename);
793 res = edit_save_file (edit, edit->filename);
795 /* Maintain modify (not save) lock on failure */
796 if ((res && edit->locked) || save_lock)
797 edit->locked = edit_unlock_file (edit->filename);
799 /* On failure try 'save as', it does locking on its own */
800 if (!res)
801 return edit_save_as_cmd (edit);
802 edit->force |= REDRAW_COMPLETELY;
803 edit->delete_file = 0;
804 edit->modified = 0;
806 return 1;
810 /* returns 1 on success */
811 int edit_new_cmd (WEdit * edit)
813 if (edit->modified) {
814 if (edit_query_dialog2 (_ ("Warning"), _ (" Current text was modified without a file save. \n Continue discards these changes. "), _ ("C&ontinue"), _ ("&Cancel"))) {
815 edit->force |= REDRAW_COMPLETELY;
816 return 0;
819 edit->force |= REDRAW_COMPLETELY;
821 if (edit->locked)
822 edit->locked = edit_unlock_file (edit->filename);
823 return edit_renew (edit); /* if this gives an error, something has really screwed up */
826 /* returns 1 on error */
827 static int
828 edit_load_file_from_filename (WEdit * edit, char *exp)
830 int prev_locked = edit->locked;
831 char *prev_filename = g_strdup (edit->filename);
833 if (!edit_reload (edit, exp)) {
834 g_free (prev_filename);
835 return 1;
838 if (prev_locked)
839 edit_unlock_file (prev_filename);
840 g_free (prev_filename);
841 return 0;
845 edit_load_cmd (WEdit *edit)
847 char *exp;
849 if (edit->modified) {
850 if (edit_query_dialog2
851 (_("Warning"),
852 _(" Current text was modified without a file save. \n"
853 " Continue discards these changes. "), _("C&ontinue"),
854 _("&Cancel"))) {
855 edit->force |= REDRAW_COMPLETELY;
856 return 0;
860 exp = edit_get_load_file (edit->filename, _(" Load "));
862 if (exp) {
863 if (*exp)
864 edit_load_file_from_filename (edit, exp);
865 g_free (exp);
867 edit->force |= REDRAW_COMPLETELY;
868 return 0;
872 if mark2 is -1 then marking is from mark1 to the cursor.
873 Otherwise its between the markers. This handles this.
874 Returns 1 if no text is marked.
876 int eval_marks (WEdit * edit, long *start_mark, long *end_mark)
878 if (edit->mark1 != edit->mark2) {
879 if (edit->mark2 >= 0) {
880 *start_mark = min (edit->mark1, edit->mark2);
881 *end_mark = max (edit->mark1, edit->mark2);
882 } else {
883 *start_mark = min (edit->mark1, edit->curs1);
884 *end_mark = max (edit->mark1, edit->curs1);
885 edit->column2 = edit->curs_col;
887 return 0;
888 } else {
889 *start_mark = *end_mark = 0;
890 edit->column2 = edit->column1 = 0;
891 return 1;
895 #define space_width 1
897 static void
898 edit_insert_column_of_text (WEdit * edit, unsigned char *data, int size, int width)
900 long cursor;
901 int i, col;
902 cursor = edit->curs1;
903 col = edit_get_col (edit);
904 for (i = 0; i < size; i++) {
905 if (data[i] == '\n') { /* fill in and move to next line */
906 int l;
907 long p;
908 if (edit_get_byte (edit, edit->curs1) != '\n') {
909 l = width - (edit_get_col (edit) - col);
910 while (l > 0) {
911 edit_insert (edit, ' ');
912 l -= space_width;
915 for (p = edit->curs1;; p++) {
916 if (p == edit->last_byte) {
917 edit_cursor_move (edit, edit->last_byte - edit->curs1);
918 edit_insert_ahead (edit, '\n');
919 p++;
920 break;
922 if (edit_get_byte (edit, p) == '\n') {
923 p++;
924 break;
927 edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->curs1);
928 l = col - edit_get_col (edit);
929 while (l >= space_width) {
930 edit_insert (edit, ' ');
931 l -= space_width;
933 continue;
935 edit_insert (edit, data[i]);
937 edit_cursor_move (edit, cursor - edit->curs1);
941 void
942 edit_block_copy_cmd (WEdit *edit)
944 long start_mark, end_mark, current = edit->curs1;
945 int size, x;
946 unsigned char *copy_buf;
948 edit_update_curs_col (edit);
949 x = edit->curs_col;
950 if (eval_marks (edit, &start_mark, &end_mark))
951 return;
952 if (column_highlighting)
953 if ((x >= edit->column1 && x < edit->column2)
954 || (x > edit->column2 && x <= edit->column1))
955 return;
957 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
959 /* all that gets pushed are deletes hence little space is used on the stack */
961 edit_push_markers (edit);
963 if (column_highlighting) {
964 edit_insert_column_of_text (edit, copy_buf, size,
965 abs (edit->column2 - edit->column1));
966 } else {
967 while (size--)
968 edit_insert_ahead (edit, copy_buf[size]);
971 g_free (copy_buf);
972 edit_scroll_screen_over_cursor (edit);
974 if (column_highlighting) {
975 edit_set_markers (edit, 0, 0, 0, 0);
976 edit_push_action (edit, COLUMN_ON);
977 column_highlighting = 0;
978 } else if (start_mark < current && end_mark > current)
979 edit_set_markers (edit, start_mark,
980 end_mark + end_mark - start_mark, 0, 0);
982 edit->force |= REDRAW_PAGE;
986 void
987 edit_block_move_cmd (WEdit *edit)
989 long count;
990 long current;
991 unsigned char *copy_buf;
992 long start_mark, end_mark;
993 int deleted = 0;
994 int x = 0;
996 if (eval_marks (edit, &start_mark, &end_mark))
997 return;
998 if (column_highlighting) {
999 edit_update_curs_col (edit);
1000 x = edit->curs_col;
1001 if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
1002 if ((x > edit->column1 && x < edit->column2)
1003 || (x > edit->column2 && x < edit->column1))
1004 return;
1005 } else if (start_mark <= edit->curs1 && end_mark >= edit->curs1)
1006 return;
1008 if ((end_mark - start_mark) > option_max_undo / 2)
1009 if (edit_query_dialog2
1010 (_("Warning"),
1012 (" Block is large, you may not be able to undo this action. "),
1013 _("C&ontinue"), _("&Cancel")))
1014 return;
1016 edit_push_markers (edit);
1017 current = edit->curs1;
1018 if (column_highlighting) {
1019 int size, c1, c2, line;
1020 line = edit->curs_line;
1021 if (edit->mark2 < 0)
1022 edit_mark_cmd (edit, 0);
1023 c1 = min (edit->column1, edit->column2);
1024 c2 = max (edit->column1, edit->column2);
1025 copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
1026 if (x < c2) {
1027 edit_block_delete_cmd (edit);
1028 deleted = 1;
1030 edit_move_to_line (edit, line);
1031 edit_cursor_move (edit,
1032 edit_move_forward3 (edit,
1033 edit_bol (edit, edit->curs1),
1034 x, 0) - edit->curs1);
1035 edit_insert_column_of_text (edit, copy_buf, size, c2 - c1);
1036 if (!deleted) {
1037 line = edit->curs_line;
1038 edit_update_curs_col (edit);
1039 x = edit->curs_col;
1040 edit_block_delete_cmd (edit);
1041 edit_move_to_line (edit, line);
1042 edit_cursor_move (edit,
1043 edit_move_forward3 (edit,
1044 edit_bol (edit,
1045 edit->curs1),
1046 x, 0) - edit->curs1);
1048 edit_set_markers (edit, 0, 0, 0, 0);
1049 edit_push_action (edit, COLUMN_ON);
1050 column_highlighting = 0;
1051 } else {
1052 copy_buf = g_malloc (end_mark - start_mark);
1053 edit_cursor_move (edit, start_mark - edit->curs1);
1054 edit_scroll_screen_over_cursor (edit);
1055 count = start_mark;
1056 while (count < end_mark) {
1057 copy_buf[end_mark - count - 1] = edit_delete (edit);
1058 count++;
1060 edit_scroll_screen_over_cursor (edit);
1061 edit_cursor_move (edit,
1062 current - edit->curs1 -
1063 (((current - edit->curs1) >
1064 0) ? end_mark - start_mark : 0));
1065 edit_scroll_screen_over_cursor (edit);
1066 while (count-- > start_mark)
1067 edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
1068 edit_set_markers (edit, edit->curs1,
1069 edit->curs1 + end_mark - start_mark, 0, 0);
1071 edit_scroll_screen_over_cursor (edit);
1072 g_free (copy_buf);
1073 edit->force |= REDRAW_PAGE;
1076 static void
1077 edit_delete_column_of_text (WEdit * edit)
1079 long p, q, r, m1, m2;
1080 int b, c, d;
1081 int n;
1083 eval_marks (edit, &m1, &m2);
1084 n = edit_move_forward (edit, m1, 0, m2) + 1;
1085 c = edit_move_forward3 (edit, edit_bol (edit, m1), 0, m1);
1086 d = edit_move_forward3 (edit, edit_bol (edit, m2), 0, m2);
1088 b = min (c, d);
1089 c = max (c, d);
1091 while (n--) {
1092 r = edit_bol (edit, edit->curs1);
1093 p = edit_move_forward3 (edit, r, b, 0);
1094 q = edit_move_forward3 (edit, r, c, 0);
1095 if (p < m1)
1096 p = m1;
1097 if (q > m2)
1098 q = m2;
1099 edit_cursor_move (edit, p - edit->curs1);
1100 while (q > p) { /* delete line between margins */
1101 if (edit_get_byte (edit, edit->curs1) != '\n')
1102 edit_delete (edit);
1103 q--;
1105 if (n) /* move to next line except on the last delete */
1106 edit_cursor_move (edit, edit_move_forward (edit, edit->curs1, 1, 0) - edit->curs1);
1110 /* if success return 0 */
1111 static int
1112 edit_block_delete (WEdit *edit)
1114 long count;
1115 long start_mark, end_mark;
1116 if (eval_marks (edit, &start_mark, &end_mark))
1117 return 0;
1118 if (column_highlighting && edit->mark2 < 0)
1119 edit_mark_cmd (edit, 0);
1120 if ((end_mark - start_mark) > option_max_undo / 2) {
1121 /* Warning message with a query to continue or cancel the operation */
1122 if (edit_query_dialog2
1123 (_("Warning"),
1125 (" Block is large, you may not be able to undo this action. "),
1126 _("C&ontinue"), _("&Cancel"))) {
1127 return 1;
1130 edit_push_markers (edit);
1131 edit_cursor_move (edit, start_mark - edit->curs1);
1132 edit_scroll_screen_over_cursor (edit);
1133 count = start_mark;
1134 if (start_mark < end_mark) {
1135 if (column_highlighting) {
1136 if (edit->mark2 < 0)
1137 edit_mark_cmd (edit, 0);
1138 edit_delete_column_of_text (edit);
1139 } else {
1140 while (count < end_mark) {
1141 edit_delete (edit);
1142 count++;
1146 edit_set_markers (edit, 0, 0, 0, 0);
1147 edit->force |= REDRAW_PAGE;
1148 return 0;
1151 /* returns 1 if canceelled by user */
1152 int edit_block_delete_cmd (WEdit * edit)
1154 long start_mark, end_mark;
1155 if (eval_marks (edit, &start_mark, &end_mark)) {
1156 edit_delete_line (edit);
1157 return 0;
1159 return edit_block_delete (edit);
1163 #define INPUT_INDEX 9
1164 #define SEARCH_DLG_WIDTH 58
1165 #define SEARCH_DLG_HEIGHT 10
1166 #define REPLACE_DLG_WIDTH 58
1167 #define REPLACE_DLG_HEIGHT 15
1168 #define CONFIRM_DLG_WIDTH 79
1169 #define CONFIRM_DLG_HEIGTH 6
1170 #define B_REPLACE_ALL (B_USER+1)
1171 #define B_REPLACE_ONE (B_USER+2)
1172 #define B_SKIP_REPLACE (B_USER+3)
1174 static int
1175 edit_replace_prompt (WEdit * edit, char *replace_text, int xpos, int ypos)
1177 QuickWidget quick_widgets[] =
1179 {quick_button, 63, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Cancel"),
1180 0, B_CANCEL, 0, 0, NULL},
1181 {quick_button, 50, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("O&ne"),
1182 0, B_REPLACE_ONE, 0, 0, NULL},
1183 {quick_button, 37, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("A&ll"),
1184 0, B_REPLACE_ALL, 0, 0, NULL},
1185 {quick_button, 21, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Skip"),
1186 0, B_SKIP_REPLACE, 0, 0, NULL},
1187 {quick_button, 4, CONFIRM_DLG_WIDTH, 3, CONFIRM_DLG_HEIGTH, N_ ("&Replace"),
1188 0, B_ENTER, 0, 0, NULL},
1189 {quick_label, 2, CONFIRM_DLG_WIDTH, 2, CONFIRM_DLG_HEIGTH, 0,
1190 0, 0, 0, 0, 0},
1191 NULL_QuickWidget};
1193 GString *label_text = g_string_new (_(" Replace with: "));
1194 if (*replace_text) {
1195 size_t label_len;
1196 label_len = label_text->len;
1197 g_string_append (label_text, replace_text);
1198 convert_to_display (label_text->str + label_len);
1200 quick_widgets[5].text = label_text->str;
1203 int retval;
1204 QuickDialog Quick_input =
1205 {CONFIRM_DLG_WIDTH, CONFIRM_DLG_HEIGTH, 0, 0, N_ (" Confirm replace "),
1206 "[Input Line Keys]", 0 /*quick_widgets */, 0 };
1208 Quick_input.widgets = quick_widgets;
1210 Quick_input.xpos = xpos;
1212 /* Sometimes menu can hide replaced text. I don't like it */
1214 if ((edit->curs_row >= ypos - 1) && (edit->curs_row <= ypos + CONFIRM_DLG_HEIGTH - 1))
1215 ypos -= CONFIRM_DLG_HEIGTH;
1217 Quick_input.ypos = ypos;
1218 retval = quick_dialog (&Quick_input);
1219 g_string_free (label_text, TRUE);
1220 return retval;
1224 static void
1225 edit_replace_dialog (WEdit * edit, const char *search_default,
1226 const char *replace_default, const char *argorder_default,
1227 /*@out@*/ char **search_text, /*@out@*/ char **replace_text,
1228 /*@out@*/ char **arg_order)
1230 int treplace_scanf = replace_scanf;
1231 int treplace_regexp = replace_regexp;
1232 int treplace_all = replace_all;
1233 int treplace_prompt = replace_prompt;
1234 int treplace_backwards = replace_backwards;
1235 int treplace_whole = replace_whole;
1236 int treplace_case = replace_case;
1238 QuickWidget quick_widgets[] =
1240 {quick_button, 6, 10, 12, REPLACE_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1241 0, NULL},
1242 {quick_button, 2, 10, 12, REPLACE_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1243 0, NULL},
1244 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1245 0, 0, NULL},
1246 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("replace &All"), 0, 0,
1247 0, 0, NULL},
1248 {quick_checkbox, 33, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("pr&Ompt on replace"), 0, 0,
1249 0, 0, NULL},
1250 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 11, REPLACE_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1251 0, 0, NULL},
1252 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 10, REPLACE_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1253 0, 0, NULL},
1254 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 9, REPLACE_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1255 0, 0, NULL},
1256 {quick_checkbox, 4, REPLACE_DLG_WIDTH, 8, REPLACE_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1257 0, 0, NULL},
1258 {quick_input, 3, REPLACE_DLG_WIDTH, 7, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1259 0, "edit-argord"},
1260 {quick_label, 2, REPLACE_DLG_WIDTH, 6, REPLACE_DLG_HEIGHT, N_(" Enter replacement argument order eg. 3,2,1,4 "), 0, 0,
1261 0, 0, 0},
1262 {quick_input, 3, REPLACE_DLG_WIDTH, 5, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1263 0, "edit-replace"},
1264 {quick_label, 2, REPLACE_DLG_WIDTH, 4, REPLACE_DLG_HEIGHT, N_(" Enter replacement string:"), 0, 0, 0,
1265 0, 0},
1266 {quick_input, 3, REPLACE_DLG_WIDTH, 3, REPLACE_DLG_HEIGHT, "", 52, 0, 0,
1267 0, "edit-search"},
1268 {quick_label, 2, REPLACE_DLG_WIDTH, 2, REPLACE_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1269 0, 0},
1270 NULL_QuickWidget};
1272 (void) edit;
1274 quick_widgets[2].result = &treplace_scanf;
1275 quick_widgets[3].result = &treplace_all;
1276 quick_widgets[4].result = &treplace_prompt;
1277 quick_widgets[5].result = &treplace_backwards;
1278 quick_widgets[6].result = &treplace_regexp;
1279 quick_widgets[7].result = &treplace_whole;
1280 quick_widgets[8].result = &treplace_case;
1281 quick_widgets[9].str_result = arg_order;
1282 quick_widgets[9].text = argorder_default;
1283 quick_widgets[11].str_result = replace_text;
1284 quick_widgets[11].text = replace_default;
1285 quick_widgets[13].str_result = search_text;
1286 quick_widgets[13].text = search_default;
1288 QuickDialog Quick_input =
1289 {REPLACE_DLG_WIDTH, REPLACE_DLG_HEIGHT, -1, 0, N_(" Replace "),
1290 "[Input Line Keys]", 0 /*quick_widgets */, 0};
1292 Quick_input.widgets = quick_widgets;
1294 if (quick_dialog (&Quick_input) != B_CANCEL) {
1295 replace_scanf = treplace_scanf;
1296 replace_backwards = treplace_backwards;
1297 replace_regexp = treplace_regexp;
1298 replace_all = treplace_all;
1299 replace_prompt = treplace_prompt;
1300 replace_whole = treplace_whole;
1301 replace_case = treplace_case;
1302 return;
1303 } else {
1304 *arg_order = NULL;
1305 *replace_text = NULL;
1306 *search_text = NULL;
1307 return;
1313 static void
1314 edit_search_dialog (WEdit * edit, char **search_text)
1316 int treplace_scanf = replace_scanf;
1317 int treplace_regexp = replace_regexp;
1318 int treplace_whole = replace_whole;
1319 int treplace_case = replace_case;
1320 int treplace_backwards = replace_backwards;
1322 QuickWidget quick_widgets[] =
1324 {quick_button, 6, 10, 7, SEARCH_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
1325 0, NULL},
1326 {quick_button, 2, 10, 7, SEARCH_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
1327 0, NULL},
1328 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("scanf &Expression"), 0, 0,
1329 0, 0, NULL },
1330 {quick_checkbox, 33, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Backwards"), 0, 0,
1331 0, 0, NULL},
1332 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 6, SEARCH_DLG_HEIGHT, N_("&Regular expression"), 0, 0,
1333 0, 0, NULL},
1334 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 5, SEARCH_DLG_HEIGHT, N_("&Whole words only"), 0, 0,
1335 0, 0, NULL},
1336 {quick_checkbox, 4, SEARCH_DLG_WIDTH, 4, SEARCH_DLG_HEIGHT, N_("case &Sensitive"), 0, 0,
1337 0, 0, NULL},
1338 {quick_input, 3, SEARCH_DLG_WIDTH, 3, SEARCH_DLG_HEIGHT, "", 52, 0, 0,
1339 0, "edit-search"},
1340 {quick_label, 2, SEARCH_DLG_WIDTH, 2, SEARCH_DLG_HEIGHT, N_(" Enter search string:"), 0, 0, 0,
1341 0, 0},
1342 NULL_QuickWidget};
1344 (void) edit;
1346 quick_widgets[2].result = &treplace_scanf;
1347 quick_widgets[3].result = &treplace_backwards;
1348 quick_widgets[4].result = &treplace_regexp;
1349 quick_widgets[5].result = &treplace_whole;
1350 quick_widgets[6].result = &treplace_case;
1351 quick_widgets[7].str_result = search_text;
1352 quick_widgets[7].text = *search_text;
1355 QuickDialog Quick_input =
1356 {SEARCH_DLG_WIDTH, SEARCH_DLG_HEIGHT, -1, 0, N_("Search"),
1357 "[Input Line Keys]", 0 /*quick_widgets */, 0};
1359 Quick_input.widgets = quick_widgets;
1361 if (quick_dialog (&Quick_input) != B_CANCEL) {
1362 replace_scanf = treplace_scanf;
1363 replace_backwards = treplace_backwards;
1364 replace_regexp = treplace_regexp;
1365 replace_whole = treplace_whole;
1366 replace_case = treplace_case;
1367 } else {
1368 *search_text = NULL;
1374 static long sargs[NUM_REPL_ARGS][256 / sizeof (long)];
1376 #define SCANF_ARGS sargs[0], sargs[1], sargs[2], sargs[3], \
1377 sargs[4], sargs[5], sargs[6], sargs[7], \
1378 sargs[8], sargs[9], sargs[10], sargs[11], \
1379 sargs[12], sargs[13], sargs[14], sargs[15]
1381 #define PRINTF_ARGS sargs[argord[0]], sargs[argord[1]], sargs[argord[2]], sargs[argord[3]], \
1382 sargs[argord[4]], sargs[argord[5]], sargs[argord[6]], sargs[argord[7]], \
1383 sargs[argord[8]], sargs[argord[9]], sargs[argord[10]], sargs[argord[11]], \
1384 sargs[argord[12]], sargs[argord[13]], sargs[argord[14]], sargs[argord[15]]
1387 /* This function is a modification of mc-3.2.10/src/view.c:regexp_view_search() */
1388 /* returns -3 on error in pattern, -1 on not found, found_len = 0 if either */
1389 static int
1390 string_regexp_search (char *pattern, char *string, int match_type,
1391 int match_bol, int icase, int *found_len, void *d)
1393 static regex_t r;
1394 static char *old_pattern = NULL;
1395 static int old_type, old_icase;
1396 regmatch_t *pmatch;
1397 static regmatch_t s[1];
1399 pmatch = (regmatch_t *) d;
1400 if (!pmatch)
1401 pmatch = s;
1403 if (!old_pattern || strcmp (old_pattern, pattern)
1404 || old_type != match_type || old_icase != icase) {
1405 if (old_pattern) {
1406 regfree (&r);
1407 g_free (old_pattern);
1408 old_pattern = 0;
1410 if (regcomp (&r, pattern, REG_EXTENDED | (icase ? REG_ICASE : 0))) {
1411 *found_len = 0;
1412 return -3;
1414 old_pattern = g_strdup (pattern);
1415 old_type = match_type;
1416 old_icase = icase;
1418 if (regexec
1419 (&r, string, d ? NUM_REPL_ARGS : 1, pmatch,
1420 ((match_bol
1421 || match_type != match_normal) ? 0 : REG_NOTBOL)) != 0) {
1422 *found_len = 0;
1423 return -1;
1425 *found_len = pmatch[0].rm_eo - pmatch[0].rm_so;
1426 return (pmatch[0].rm_so);
1429 /* thanks to Liviu Daia <daia@stoilow.imar.ro> for getting this
1430 (and the above) routines to work properly - paul */
1432 typedef int (*edit_getbyte_fn) (WEdit *, long);
1434 static long
1435 edit_find_string (long start, unsigned char *exp, int *len, long last_byte, edit_getbyte_fn get_byte, void *data, int once_only, void *d)
1437 long p, q = 0;
1438 long l = strlen ((char *) exp), f = 0;
1439 int n = 0;
1441 for (p = 0; p < l; p++) /* count conversions... */
1442 if (exp[p] == '%')
1443 if (exp[++p] != '%') /* ...except for "%%" */
1444 n++;
1446 if (replace_scanf || replace_regexp) {
1447 int c;
1448 unsigned char *buf;
1449 unsigned char mbuf[MAX_REPL_LEN * 2 + 3];
1451 replace_scanf = (!replace_regexp); /* can't have both */
1453 buf = mbuf;
1455 if (replace_scanf) {
1456 unsigned char e[MAX_REPL_LEN];
1457 if (n >= NUM_REPL_ARGS)
1458 return -3;
1460 if (replace_case) {
1461 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++)
1462 buf[p - start] = (*get_byte) (data, p);
1463 } else {
1464 for (p = 0; exp[p] != 0; p++)
1465 exp[p] = my_lower_case (exp[p]);
1466 for (p = start; p < last_byte && p < start + MAX_REPL_LEN; p++) {
1467 c = (*get_byte) (data, p);
1468 buf[p - start] = my_lower_case (c);
1472 buf[(q = p - start)] = 0;
1473 strcpy ((char *) e, (char *) exp);
1474 strcat ((char *) e, "%n");
1475 exp = e;
1477 while (q) {
1478 *((int *) sargs[n]) = 0; /* --> here was the problem - now fixed: good */
1479 if (n == sscanf ((char *) buf, (char *) exp, SCANF_ARGS)) {
1480 if (*((int *) sargs[n])) {
1481 *len = *((int *) sargs[n]);
1482 return start;
1485 if (once_only)
1486 return -2;
1487 if (q + start < last_byte) {
1488 if (replace_case) {
1489 buf[q] = (*get_byte) (data, q + start);
1490 } else {
1491 c = (*get_byte) (data, q + start);
1492 buf[q] = my_lower_case (c);
1494 q++;
1496 buf[q] = 0;
1497 start++;
1498 buf++; /* move the window along */
1499 if (buf == mbuf + MAX_REPL_LEN) { /* the window is about to go past the end of array, so... */
1500 memmove (mbuf, buf, strlen ((char *) buf) + 1); /* reset it */
1501 buf = mbuf;
1503 q--;
1505 } else { /* regexp matching */
1506 long offset = 0;
1507 int found_start, match_bol, move_win = 0;
1509 while (start + offset < last_byte) {
1510 match_bol = (offset == 0 || (*get_byte) (data, start + offset - 1) == '\n');
1511 if (!move_win) {
1512 p = start + offset;
1513 q = 0;
1515 for (; p < last_byte && q < MAX_REPL_LEN; p++, q++) {
1516 mbuf[q] = (*get_byte) (data, p);
1517 if (mbuf[q] == '\n')
1518 break;
1520 q++;
1521 offset += q;
1522 mbuf[q] = 0;
1524 buf = mbuf;
1525 while (q) {
1526 found_start = string_regexp_search ((char *) exp, (char *) buf, match_normal, match_bol, !replace_case, len, d);
1528 if (found_start <= -2) { /* regcomp/regexec error */
1529 *len = 0;
1530 return -3;
1532 else if (found_start == -1) /* not found: try next line */
1533 break;
1534 else if (*len == 0) { /* null pattern: try again at next character */
1535 q--;
1536 buf++;
1537 match_bol = 0;
1538 continue;
1540 else /* found */
1541 return (start + offset - q + found_start);
1543 if (once_only)
1544 return -2;
1546 if (buf[q - 1] != '\n') { /* incomplete line: try to recover */
1547 buf = mbuf + MAX_REPL_LEN / 2;
1548 q = strlen ((const char *) buf);
1549 memmove (mbuf, buf, q);
1550 p = start + q;
1551 move_win = 1;
1553 else
1554 move_win = 0;
1557 } else {
1558 *len = strlen ((const char *) exp);
1559 if (replace_case) {
1560 for (p = start; p <= last_byte - l; p++) {
1561 if ((*get_byte) (data, p) == (unsigned char)exp[0]) { /* check if first char matches */
1562 for (f = 0, q = 0; q < l && f < 1; q++)
1563 if ((*get_byte) (data, q + p) != (unsigned char)exp[q])
1564 f = 1;
1565 if (f == 0)
1566 return p;
1568 if (once_only)
1569 return -2;
1571 } else {
1572 for (p = 0; exp[p] != 0; p++)
1573 exp[p] = my_lower_case (exp[p]);
1575 for (p = start; p <= last_byte - l; p++) {
1576 if (my_lower_case ((*get_byte) (data, p)) == (unsigned char)exp[0]) {
1577 for (f = 0, q = 0; q < l && f < 1; q++)
1578 if (my_lower_case ((*get_byte) (data, q + p)) != (unsigned char)exp[q])
1579 f = 1;
1580 if (f == 0)
1581 return p;
1583 if (once_only)
1584 return -2;
1588 return -2;
1592 static long
1593 edit_find_forwards (long search_start, unsigned char *exp, int *len, long last_byte, edit_getbyte_fn get_byte, void *data, int once_only, void *d)
1594 { /*front end to find_string to check for
1595 whole words */
1596 long p;
1597 p = search_start;
1599 while ((p = edit_find_string (p, exp, len, last_byte, get_byte, data, once_only, d)) >= 0) {
1600 if (replace_whole) {
1601 /*If the bordering chars are not in option_whole_chars_search then word is whole */
1602 if (!strcasechr (option_whole_chars_search, (*get_byte) (data, p - 1))
1603 && !strcasechr (option_whole_chars_search, (*get_byte) (data, p + *len)))
1604 return p;
1605 if (once_only)
1606 return -2;
1607 } else
1608 return p;
1609 if (once_only)
1610 break;
1611 p++; /*not a whole word so continue search. */
1613 return p;
1616 static long
1617 edit_find (long search_start, unsigned char *exp, int *len, long last_byte, edit_getbyte_fn get_byte, void *data, void *d)
1619 long p;
1620 if (replace_backwards) {
1621 while (search_start >= 0) {
1622 p = edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 1, d);
1623 if (p == search_start)
1624 return p;
1625 search_start--;
1627 } else {
1628 return edit_find_forwards (search_start, exp, len, last_byte, get_byte, data, 0, d);
1630 return -2;
1633 #define is_digit(x) ((x) >= '0' && (x) <= '9')
1635 #define snprint(v) { \
1636 *p1++ = *p++; \
1637 *p1 = '\0'; \
1638 n = snprintf(s,e-s,q1,v); \
1639 if (n >= (size_t) (e - s)) goto nospc; \
1640 s += n; \
1643 /* this function uses the sprintf command to do a vprintf */
1644 /* it takes pointers to arguments instead of the arguments themselves */
1645 /* The return value is the number of bytes written excluding '\0'
1646 if successfull, -1 if the resulting string would be too long and
1647 -2 if the format string is errorneous. */
1648 static int snprintf_p (char *str, size_t size, const char *fmt,...)
1649 __attribute__ ((format (printf, 3, 4)));
1651 static int snprintf_p (char *str, size_t size, const char *fmt,...)
1653 va_list ap;
1654 size_t n;
1655 const char *q, *p;
1656 char *s = str, *e = str + size;
1657 char q1[40];
1658 char *p1;
1659 int nargs = 0;
1661 va_start (ap, fmt);
1662 p = q = fmt;
1664 while ((p = strchr (p, '%'))) {
1665 n = p - q;
1666 if (n >= (size_t) (e - s))
1667 goto nospc;
1668 memcpy (s, q, n); /* copy stuff between format specifiers */
1669 s += n;
1670 q = p;
1671 p1 = q1;
1672 *p1++ = *p++;
1673 if (*p == '%') {
1674 p++;
1675 *s++ = '%';
1676 if (s == e)
1677 goto nospc;
1678 q = p;
1679 continue;
1681 if (*p == 'n')
1682 goto err;
1683 /* We were passed only 16 arguments. */
1684 if (++nargs == 16)
1685 goto err;
1686 if (*p == '#')
1687 *p1++ = *p++;
1688 if (*p == '0')
1689 *p1++ = *p++;
1690 if (*p == '-')
1691 *p1++ = *p++;
1692 if (*p == '+')
1693 *p1++ = *p++;
1694 if (*p == '*') {
1695 p++;
1696 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace field width with a number */
1697 p1 += strlen (p1);
1698 } else {
1699 while (is_digit (*p) && p1 < q1 + 20)
1700 *p1++ = *p++;
1701 if (is_digit (*p))
1702 goto err;
1704 if (*p == '.')
1705 *p1++ = *p++;
1706 if (*p == '*') {
1707 p++;
1708 strcpy (p1, MY_itoa (*va_arg (ap, int *))); /* replace precision with a number */
1709 p1 += strlen (p1);
1710 } else {
1711 while (is_digit (*p) && p1 < q1 + 32)
1712 *p1++ = *p++;
1713 if (is_digit (*p))
1714 goto err;
1716 /* flags done, now get argument */
1717 if (*p == 's') {
1718 snprint (va_arg (ap, char *));
1719 } else if (*p == 'h') {
1720 if (strchr ("diouxX", *p))
1721 snprint (*va_arg (ap, short *));
1722 } else if (*p == 'l') {
1723 *p1++ = *p++;
1724 if (strchr ("diouxX", *p))
1725 snprint (*va_arg (ap, long *));
1726 } else if (strchr ("cdiouxX", *p)) {
1727 snprint (*va_arg (ap, int *));
1728 } else if (*p == 'L') {
1729 *p1++ = *p++;
1730 if (strchr ("EefgG", *p))
1731 snprint (*va_arg (ap, double *)); /* should be long double */
1732 } else if (strchr ("EefgG", *p)) {
1733 snprint (*va_arg (ap, double *));
1734 } else if (strchr ("DOU", *p)) {
1735 snprint (*va_arg (ap, long *));
1736 } else if (*p == 'p') {
1737 snprint (*va_arg (ap, void **));
1738 } else
1739 goto err;
1740 q = p;
1742 va_end (ap);
1743 n = strlen (q);
1744 if (n >= (size_t) (e - s))
1745 return -1;
1746 memcpy (s, q, n + 1);
1747 return s + n - str;
1748 nospc:
1749 va_end (ap);
1750 return -1;
1751 err:
1752 va_end (ap);
1753 return -2;
1756 static void regexp_error (WEdit *edit)
1758 (void) edit;
1759 edit_error_dialog (_("Error"), _(" Invalid regular expression, or scanf expression with too many conversions "));
1762 /* call with edit = 0 before shutdown to close memory leaks */
1763 void
1764 edit_replace_cmd (WEdit *edit, int again)
1766 static regmatch_t pmatch[NUM_REPL_ARGS];
1767 /* 1 = search string, 2 = replace with, 3 = argument order */
1768 static char *saved1 = NULL; /* saved default[123] */
1769 static char *saved2 = NULL;
1770 static char *saved3 = NULL;
1771 char *input1 = NULL; /* user input from the dialog */
1772 char *input2 = NULL;
1773 char *input3 = NULL;
1774 int replace_yes;
1775 int replace_continue;
1776 int treplace_prompt = 0;
1777 long times_replaced = 0, last_search;
1778 int argord[NUM_REPL_ARGS];
1780 if (!edit) {
1781 g_free (saved1), saved1 = NULL;
1782 g_free (saved2), saved2 = NULL;
1783 g_free (saved3), saved3 = NULL;
1784 return;
1787 last_search = edit->last_byte;
1789 edit->force |= REDRAW_COMPLETELY;
1791 if (again && !saved1 && !saved2)
1792 again = 0;
1794 if (again) {
1795 input1 = g_strdup (saved1 ? saved1 : "");
1796 input2 = g_strdup (saved2 ? saved2 : "");
1797 input3 = g_strdup (saved3 ? saved3 : "");
1798 } else {
1799 char *disp1 = g_strdup (saved1 ? saved1 : "");
1800 char *disp2 = g_strdup (saved2 ? saved2 : "");
1801 char *disp3 = g_strdup (saved3 ? saved3 : "");
1803 convert_to_display (disp1);
1804 convert_to_display (disp2);
1805 convert_to_display (disp3);
1807 edit_push_action (edit, KEY_PRESS + edit->start_display);
1808 edit_replace_dialog (edit, disp1, disp2, disp3, &input1, &input2,
1809 &input3);
1811 g_free (disp1);
1812 g_free (disp2);
1813 g_free (disp3);
1815 convert_from_input (input1);
1816 convert_from_input (input2);
1817 convert_from_input (input3);
1819 treplace_prompt = replace_prompt;
1820 if (input1 == NULL || *input1 == '\0') {
1821 edit->force = REDRAW_COMPLETELY;
1822 goto cleanup;
1825 g_free (saved1), saved1 = g_strdup (input1);
1826 g_free (saved2), saved2 = g_strdup (input2);
1827 g_free (saved3), saved3 = g_strdup (input3);
1832 const char *s;
1833 int ord;
1834 size_t i;
1836 s = input3;
1837 for (i = 0; i < NUM_REPL_ARGS; i++) {
1838 if (s != NULL && *s != '\0') {
1839 ord = atoi (s);
1840 if ((ord > 0) && (ord <= NUM_REPL_ARGS))
1841 argord[i] = ord - 1;
1842 else
1843 argord[i] = i;
1844 s = strchr (s, ',');
1845 if (s != NULL)
1846 s++;
1847 } else
1848 argord[i] = i;
1852 replace_continue = replace_all;
1854 if (edit->found_len && edit->search_start == edit->found_start + 1
1855 && replace_backwards)
1856 edit->search_start--;
1858 if (edit->found_len && edit->search_start == edit->found_start - 1
1859 && !replace_backwards)
1860 edit->search_start++;
1862 do {
1863 int len = 0;
1864 long new_start;
1865 new_start =
1866 edit_find (edit->search_start, (unsigned char *) input1, &len,
1867 last_search, edit_get_byte, (void *) edit, pmatch);
1868 if (new_start == -3) {
1869 regexp_error (edit);
1870 break;
1872 edit->search_start = new_start;
1873 /*returns negative on not found or error in pattern */
1875 if (edit->search_start >= 0) {
1876 int i;
1878 edit->found_start = edit->search_start;
1879 i = edit->found_len = len;
1881 edit_cursor_move (edit, edit->search_start - edit->curs1);
1882 edit_scroll_screen_over_cursor (edit);
1884 replace_yes = 1;
1886 if (treplace_prompt) {
1887 int l;
1888 l = edit->curs_row - edit->num_widget_lines / 3;
1889 if (l > 0)
1890 edit_scroll_downward (edit, l);
1891 if (l < 0)
1892 edit_scroll_upward (edit, -l);
1894 edit_scroll_screen_over_cursor (edit);
1895 edit->force |= REDRAW_PAGE;
1896 edit_render_keypress (edit);
1898 /*so that undo stops at each query */
1899 edit_push_key_press (edit);
1901 switch (edit_replace_prompt (edit, input2, /* and prompt 2/3 down */
1902 (edit->num_widget_columns -
1903 CONFIRM_DLG_WIDTH) / 2,
1904 edit->num_widget_lines * 2 /
1905 3)) {
1906 case B_ENTER:
1907 break;
1908 case B_SKIP_REPLACE:
1909 replace_yes = 0;
1910 break;
1911 case B_REPLACE_ALL:
1912 treplace_prompt = 0;
1913 replace_continue = 1;
1914 break;
1915 case B_REPLACE_ONE:
1916 replace_continue = 0;
1917 break;
1918 case B_CANCEL:
1919 replace_yes = 0;
1920 replace_continue = 0;
1921 break;
1924 if (replace_yes) { /* delete then insert new */
1925 if (replace_scanf || replace_regexp) {
1926 char repl_str[MAX_REPL_LEN + 2];
1927 int ret = 0;
1929 /* we need to fill in sargs just like with scanf */
1930 if (replace_regexp) {
1931 int k, j;
1932 for (k = 1;
1933 k < NUM_REPL_ARGS && pmatch[k].rm_eo >= 0;
1934 k++) {
1935 unsigned char *t;
1937 if (pmatch[k].rm_eo - pmatch[k].rm_so > 255) {
1938 ret = -1;
1939 break;
1941 t = (unsigned char *) &sargs[k - 1][0];
1942 for (j = 0;
1943 j < pmatch[k].rm_eo - pmatch[k].rm_so
1944 && j < 255; j++, t++)
1945 *t = (unsigned char) edit_get_byte (edit,
1946 edit->
1947 search_start
1949 pmatch
1950 [0].
1951 rm_so +
1952 pmatch
1953 [k].
1954 rm_so +
1956 *t = '\0';
1958 for (; k <= NUM_REPL_ARGS; k++)
1959 sargs[k - 1][0] = 0;
1961 if (!ret)
1962 ret =
1963 snprintf_p (repl_str, MAX_REPL_LEN + 2, input2,
1964 PRINTF_ARGS);
1965 if (ret >= 0) {
1966 times_replaced++;
1967 while (i--)
1968 edit_delete (edit);
1969 while (repl_str[++i])
1970 edit_insert (edit, repl_str[i]);
1971 } else {
1972 edit_error_dialog (_(" Replace "),
1973 ret ==
1974 -2 ?
1976 (" Error in replacement format string. ")
1977 : _(" Replacement too long. "));
1978 replace_continue = 0;
1980 } else {
1981 times_replaced++;
1982 while (i--)
1983 edit_delete (edit);
1984 while (input2[++i])
1985 edit_insert (edit, input2[i]);
1987 edit->found_len = i;
1989 /* so that we don't find the same string again */
1990 if (replace_backwards) {
1991 last_search = edit->search_start;
1992 edit->search_start--;
1993 } else {
1994 edit->search_start += i;
1995 last_search = edit->last_byte;
1997 edit_scroll_screen_over_cursor (edit);
1998 } else {
1999 const char *msg = _(" Replace ");
2000 /* try and find from right here for next search */
2001 edit->search_start = edit->curs1;
2002 edit_update_curs_col (edit);
2004 edit->force |= REDRAW_PAGE;
2005 edit_render_keypress (edit);
2006 if (times_replaced) {
2007 message (0, msg, _(" %ld replacements made. "),
2008 times_replaced);
2009 } else
2010 edit_message_dialog (msg, _(" Search string not found "));
2011 replace_continue = 0;
2013 } while (replace_continue);
2015 edit->force = REDRAW_COMPLETELY;
2016 edit_scroll_screen_over_cursor (edit);
2017 cleanup:
2018 g_free (input1);
2019 g_free (input2);
2020 g_free (input3);
2026 void edit_search_cmd (WEdit * edit, int again)
2028 static char *old = NULL;
2029 char *exp = "";
2031 if (!edit) {
2032 g_free (old);
2033 old = 0;
2034 return;
2036 exp = old ? old : exp;
2037 if (again) { /*ctrl-hotkey for search again. */
2038 if (!old)
2039 return;
2040 exp = g_strdup (old);
2041 } else {
2043 #ifdef HAVE_CHARSET
2044 if (exp && *exp)
2045 convert_to_display (exp);
2046 #endif /* HAVE_CHARSET */
2048 edit_search_dialog (edit, &exp);
2050 #ifdef HAVE_CHARSET
2051 if (exp && *exp)
2052 convert_from_input (exp);
2053 #endif /* HAVE_CHARSET */
2055 edit_push_action (edit, KEY_PRESS + edit->start_display);
2058 if (exp) {
2059 if (*exp) {
2060 int len = 0;
2061 g_free (old);
2062 old = g_strdup (exp);
2064 if (search_create_bookmark) {
2065 int found = 0, books = 0;
2066 int l = 0, l_last = -1;
2067 long p, q = 0;
2068 for (;;) {
2069 p = edit_find (q, (unsigned char *) exp, &len, edit->last_byte,
2070 edit_get_byte, (void *) edit, 0);
2071 if (p < 0)
2072 break;
2073 found++;
2074 l += edit_count_lines (edit, q, p);
2075 if (l != l_last) {
2076 book_mark_insert (edit, l, BOOK_MARK_FOUND_COLOR);
2077 books++;
2079 l_last = l;
2080 q = p + 1;
2082 if (found) {
2083 /* in response to number of bookmarks added because of string being found %d times */
2084 message (0, _("Search"), _(" %d items found, %d bookmarks added "), found, books);
2085 } else {
2086 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
2088 } else {
2090 if (edit->found_len && edit->search_start == edit->found_start + 1 && replace_backwards)
2091 edit->search_start--;
2093 if (edit->found_len && edit->search_start == edit->found_start - 1 && !replace_backwards)
2094 edit->search_start++;
2096 edit->search_start = edit_find (edit->search_start, (unsigned char *) exp, &len, edit->last_byte,
2097 edit_get_byte, (void *) edit, 0);
2099 if (edit->search_start >= 0) {
2100 edit->found_start = edit->search_start;
2101 edit->found_len = len;
2103 edit_cursor_move (edit, edit->search_start - edit->curs1);
2104 edit_scroll_screen_over_cursor (edit);
2105 if (replace_backwards)
2106 edit->search_start--;
2107 else
2108 edit->search_start++;
2109 } else if (edit->search_start == -3) {
2110 edit->search_start = edit->curs1;
2111 regexp_error (edit);
2112 } else {
2113 edit->search_start = edit->curs1;
2114 edit_error_dialog (_ ("Search"), _ (" Search string not found "));
2118 g_free (exp);
2120 edit->force |= REDRAW_COMPLETELY;
2121 edit_scroll_screen_over_cursor (edit);
2126 * Check if it's OK to close the editor. If there are unsaved changes,
2127 * ask user. Return 1 if it's OK to exit, 0 to continue editing.
2130 edit_ok_to_exit (WEdit *edit)
2132 if (!edit->modified)
2133 return 1;
2135 switch (edit_query_dialog3
2136 (_("Quit"), _(" File was modified, Save with exit? "),
2137 _("&Cancel quit"), _("&Yes"), _("&No"))) {
2138 case 1:
2139 edit_push_markers (edit);
2140 edit_set_markers (edit, 0, 0, 0, 0);
2141 if (!edit_save_cmd (edit))
2142 return 0;
2143 break;
2144 case 2:
2145 if (edit->locked)
2146 edit->locked = edit_unlock_file (edit->filename);
2147 break;
2148 case 0:
2149 case -1:
2150 return 0;
2153 return 1;
2157 #define TEMP_BUF_LEN 1024
2159 /* Return a null terminated length of text. Result must be g_free'd */
2160 static unsigned char *
2161 edit_get_block (WEdit *edit, long start, long finish, int *l)
2163 unsigned char *s, *r;
2164 r = s = g_malloc (finish - start + 1);
2165 if (column_highlighting) {
2166 *l = 0;
2167 /* copy from buffer, excluding chars that are out of the column 'margins' */
2168 while (start < finish) {
2169 int c, x;
2170 x = edit_move_forward3 (edit, edit_bol (edit, start), 0,
2171 start);
2172 c = edit_get_byte (edit, start);
2173 if ((x >= edit->column1 && x < edit->column2)
2174 || (x >= edit->column2 && x < edit->column1) || c == '\n') {
2175 *s++ = c;
2176 (*l)++;
2178 start++;
2180 } else {
2181 *l = finish - start;
2182 while (start < finish)
2183 *s++ = edit_get_byte (edit, start++);
2185 *s = 0;
2186 return r;
2189 /* save block, returns 1 on success */
2191 edit_save_block (WEdit * edit, const char *filename, long start,
2192 long finish)
2194 int len, file;
2196 if ((file =
2197 mc_open (filename, O_CREAT | O_WRONLY | O_TRUNC,
2198 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY)) == -1)
2199 return 0;
2201 if (column_highlighting) {
2202 unsigned char *block, *p;
2203 int r;
2204 p = block = edit_get_block (edit, start, finish, &len);
2205 while (len) {
2206 r = mc_write (file, p, len);
2207 if (r < 0)
2208 break;
2209 p += r;
2210 len -= r;
2212 g_free (block);
2213 } else {
2214 unsigned char *buf;
2215 int i = start, end;
2216 len = finish - start;
2217 buf = g_malloc (TEMP_BUF_LEN);
2218 while (start != finish) {
2219 end = min (finish, start + TEMP_BUF_LEN);
2220 for (; i < end; i++)
2221 buf[i - start] = edit_get_byte (edit, i);
2222 len -= mc_write (file, (char *) buf, end - start);
2223 start = end;
2225 g_free (buf);
2227 mc_close (file);
2228 if (len)
2229 return 0;
2230 return 1;
2233 /* copies a block to clipboard file */
2234 static int edit_save_block_to_clip_file (WEdit * edit, long start, long finish)
2236 return edit_save_block (edit, catstrs (home_dir, CLIP_FILE, (char *) NULL), start, finish);
2240 void edit_paste_from_history (WEdit *edit)
2242 (void) edit;
2243 edit_error_dialog (_(" Error "), _(" This function is not implemented. "));
2246 int edit_copy_to_X_buf_cmd (WEdit * edit)
2248 long start_mark, end_mark;
2249 if (eval_marks (edit, &start_mark, &end_mark))
2250 return 0;
2251 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2252 edit_error_dialog (_(" Copy to clipboard "), get_sys_error (_(" Unable to save to file. ")));
2253 return 1;
2255 edit_mark_cmd (edit, 1);
2256 return 0;
2259 int edit_cut_to_X_buf_cmd (WEdit * edit)
2261 long start_mark, end_mark;
2262 if (eval_marks (edit, &start_mark, &end_mark))
2263 return 0;
2264 if (!edit_save_block_to_clip_file (edit, start_mark, end_mark)) {
2265 edit_error_dialog (_(" Cut to clipboard "), _(" Unable to save to file. "));
2266 return 1;
2268 edit_block_delete_cmd (edit);
2269 edit_mark_cmd (edit, 1);
2270 return 0;
2273 void edit_paste_from_X_buf_cmd (WEdit * edit)
2275 edit_insert_file (edit, catstrs (home_dir, CLIP_FILE, (char *) NULL));
2280 * Ask user for the line and go to that line.
2281 * Negative numbers mean line from the end (i.e. -1 is the last line).
2283 void
2284 edit_goto_cmd (WEdit *edit)
2286 char *f;
2287 static long line = 0; /* line as typed, saved as default */
2288 long l;
2289 char *error;
2290 char s[32];
2292 g_snprintf (s, sizeof (s), "%ld", line);
2293 f = input_dialog (_(" Goto line "), _(" Enter line: "), line ? s : "");
2294 if (!f)
2295 return;
2297 if (!*f) {
2298 g_free (f);
2299 return;
2302 l = strtol (f, &error, 0);
2303 if (*error) {
2304 g_free (f);
2305 return;
2308 line = l;
2309 if (l < 0)
2310 l = edit->total_lines + l + 2;
2311 edit_move_display (edit, l - edit->num_widget_lines / 2 - 1);
2312 edit_move_to_line (edit, l - 1);
2313 edit->force |= REDRAW_COMPLETELY;
2314 g_free (f);
2318 /* Return 1 on success */
2320 edit_save_block_cmd (WEdit *edit)
2322 long start_mark, end_mark;
2323 char *exp;
2324 if (eval_marks (edit, &start_mark, &end_mark))
2325 return 1;
2326 exp =
2327 edit_get_save_file (catstrs (home_dir, CLIP_FILE, (char *) NULL),
2328 _(" Save Block "));
2329 edit_push_action (edit, KEY_PRESS + edit->start_display);
2330 if (exp) {
2331 if (!*exp) {
2332 g_free (exp);
2333 return 0;
2334 } else {
2335 if (edit_save_block (edit, exp, start_mark, end_mark)) {
2336 g_free (exp);
2337 edit->force |= REDRAW_COMPLETELY;
2338 return 1;
2339 } else {
2340 g_free (exp);
2341 edit_error_dialog (_(" Save Block "),
2342 get_sys_error (_
2343 (" Cannot save file. ")));
2347 edit->force |= REDRAW_COMPLETELY;
2348 return 0;
2352 /* returns 1 on success */
2354 edit_insert_file_cmd (WEdit *edit)
2356 char *exp = edit_get_load_file (catstrs (home_dir, CLIP_FILE, (char *) NULL),
2357 _(" Insert File "));
2358 edit_push_action (edit, KEY_PRESS + edit->start_display);
2359 if (exp) {
2360 if (!*exp) {
2361 g_free (exp);
2362 return 0;
2363 } else {
2364 if (edit_insert_file (edit, exp)) {
2365 g_free (exp);
2366 edit->force |= REDRAW_COMPLETELY;
2367 return 1;
2368 } else {
2369 g_free (exp);
2370 edit_error_dialog (_(" Insert File "),
2371 get_sys_error (_
2372 (" Cannot insert file. ")));
2376 edit->force |= REDRAW_COMPLETELY;
2377 return 0;
2380 /* sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
2381 int edit_sort_cmd (WEdit * edit)
2383 static char *old = 0;
2384 char *exp;
2385 long start_mark, end_mark;
2386 int e;
2388 if (eval_marks (edit, &start_mark, &end_mark)) {
2389 edit_error_dialog (_(" Sort block "), _(" You must first highlight a block of text. "));
2390 return 0;
2392 edit_save_block (edit, catstrs (home_dir, BLOCK_FILE, (char *) NULL), start_mark, end_mark);
2394 exp = old ? old : "";
2396 exp = input_dialog (_(" Run Sort "),
2397 _(" Enter sort options (see manpage) separated by whitespace: "), exp);
2399 if (!exp)
2400 return 1;
2401 g_free (old);
2402 old = exp;
2404 e = system (catstrs (" sort ", exp, " ", home_dir, BLOCK_FILE, " > ", home_dir, TEMP_FILE, (char *) NULL));
2405 if (e) {
2406 if (e == -1 || e == 127) {
2407 edit_error_dialog (_(" Sort "),
2408 get_sys_error (_(" Cannot execute sort command ")));
2409 } else {
2410 char q[8];
2411 sprintf (q, "%d ", e);
2412 edit_error_dialog (_(" Sort "),
2413 catstrs (_(" Sort returned non-zero: "), q, (char *) NULL));
2415 return -1;
2418 edit->force |= REDRAW_COMPLETELY;
2420 if (edit_block_delete_cmd (edit))
2421 return 1;
2422 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, (char *) NULL));
2423 return 0;
2427 * Ask user for a command, execute it and paste its output back to the
2428 * editor.
2431 edit_ext_cmd (WEdit *edit)
2433 char *exp;
2434 int e;
2436 exp =
2437 input_dialog (_("Paste output of external command"),
2438 _("Enter shell command(s):"), NULL);
2440 if (!exp)
2441 return 1;
2443 e = system (catstrs (exp, " > ", home_dir, TEMP_FILE, (char *) NULL));
2444 g_free (exp);
2446 if (e) {
2447 edit_error_dialog (_("External command"),
2448 get_sys_error (_("Cannot execute command")));
2449 return -1;
2452 edit->force |= REDRAW_COMPLETELY;
2454 edit_insert_file (edit, catstrs (home_dir, TEMP_FILE, (char *) NULL));
2455 return 0;
2458 /* if block is 1, a block must be highlighted and the shell command
2459 processes it. If block is 0 the shell command is a straight system
2460 command, that just produces some output which is to be inserted */
2461 void
2462 edit_block_process_cmd (WEdit *edit, const char *shell_cmd, int block)
2464 long start_mark, end_mark;
2465 char buf[BUFSIZ];
2466 FILE *script_home = NULL;
2467 FILE *script_src = NULL;
2468 FILE *block_file = NULL;
2469 const char *o = NULL;
2470 const char *h = NULL;
2471 const char *b = NULL;
2472 char *quoted_name = NULL;
2474 o = catstrs (mc_home, shell_cmd, (char *) NULL); /* original source script */
2475 h = catstrs (home_dir, EDIT_DIR, shell_cmd, (char *) NULL); /* home script */
2476 b = catstrs (home_dir, BLOCK_FILE, (char *) NULL); /* block file */
2478 if (!(script_home = fopen (h, "r"))) {
2479 if (!(script_home = fopen (h, "w"))) {
2480 edit_error_dialog ("", get_sys_error (catstrs
2482 ("Error creating script:"),
2483 h, (char *) NULL)));
2484 return;
2486 if (!(script_src = fopen (o, "r"))) {
2487 fclose (script_home);
2488 unlink (h);
2489 edit_error_dialog ("", get_sys_error (catstrs
2490 (_("Error reading script:"),
2491 o, (char *) NULL)));
2492 return;
2494 while (fgets (buf, sizeof (buf), script_src))
2495 fputs (buf, script_home);
2496 if (fclose (script_home)) {
2497 edit_error_dialog ("", get_sys_error (catstrs
2499 ("Error closing script:"),
2500 h, (char *) NULL)));
2501 return;
2503 chmod (h, 0700);
2504 edit_error_dialog ("", get_sys_error (catstrs
2505 (_("Script created:"), h, (char *) NULL)));
2508 open_error_pipe ();
2510 if (block) { /* for marked block run indent formatter */
2511 if (eval_marks (edit, &start_mark, &end_mark)) {
2512 edit_error_dialog (_("Process block"),
2514 (" You must first highlight a block of text. "));
2515 return;
2517 edit_save_block (edit, b, start_mark, end_mark);
2518 quoted_name = name_quote (edit->filename, 0);
2520 * Run script.
2521 * Initial space is to avoid polluting bash history.
2522 * Arguments:
2523 * $1 - name of the edited file (to check its extension etc).
2524 * $2 - file containing the current block.
2525 * $3 - file where error messages should be put
2526 * (for compatibility with old scripts).
2528 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ", quoted_name,
2529 " ", home_dir, BLOCK_FILE " /dev/null", (char *) NULL));
2531 } else {
2533 * No block selected, just execute the command for the file.
2534 * Arguments:
2535 * $1 - name of the edited file.
2537 system (catstrs (" ", home_dir, EDIT_DIR, shell_cmd, " ",
2538 quoted_name, (char *) NULL));
2540 g_free (quoted_name);
2541 close_error_pipe (0, 0);
2543 edit_refresh_cmd (edit);
2544 edit->force |= REDRAW_COMPLETELY;
2546 /* insert result block */
2547 if (block) {
2548 if (edit_block_delete_cmd (edit))
2549 return;
2550 edit_insert_file (edit, b);
2551 if ((block_file = fopen (b, "w")))
2552 fclose (block_file);
2553 return;
2556 return;
2559 /* prints at the cursor */
2560 /* returns the number of chars printed */
2561 int edit_print_string (WEdit * e, const char *s)
2563 int i = 0;
2564 while (s[i])
2565 edit_execute_cmd (e, -1, (unsigned char) s[i++]);
2566 e->force |= REDRAW_COMPLETELY;
2567 edit_update_screen (e);
2568 return i;
2572 static void pipe_mail (WEdit *edit, char *to, char *subject, char *cc)
2574 FILE *p = 0;
2575 char *s;
2577 to = name_quote (to, 0);
2578 subject = name_quote (subject, 0);
2579 cc = name_quote (cc, 0);
2580 s = g_strconcat ("mail -s ", subject, *cc ? " -c " : "" , cc, " ", to, (char *) NULL);
2581 g_free (to);
2582 g_free (subject);
2583 g_free (cc);
2585 if (s) {
2586 p = popen (s, "w");
2587 g_free (s);
2590 if (p) {
2591 long i;
2592 for (i = 0; i < edit->last_byte; i++)
2593 fputc (edit_get_byte (edit, i), p);
2594 pclose (p);
2598 #define MAIL_DLG_HEIGHT 12
2600 void edit_mail_dialog (WEdit * edit)
2602 char *tmail_to;
2603 char *tmail_subject;
2604 char *tmail_cc;
2606 static char *mail_cc_last = 0;
2607 static char *mail_subject_last = 0;
2608 static char *mail_to_last = 0;
2610 QuickDialog Quick_input =
2611 {50, MAIL_DLG_HEIGHT, -1, 0, N_(" Mail "),
2612 "[Input Line Keys]", 0, 0};
2614 QuickWidget quick_widgets[] =
2616 {quick_button, 6, 10, 9, MAIL_DLG_HEIGHT, N_("&Cancel"), 0, B_CANCEL, 0,
2617 0, NULL},
2618 {quick_button, 2, 10, 9, MAIL_DLG_HEIGHT, N_("&OK"), 0, B_ENTER, 0,
2619 0, NULL},
2620 {quick_input, 3, 50, 8, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2621 0, "mail-dlg-input"},
2622 {quick_label, 2, 50, 7, MAIL_DLG_HEIGHT, N_(" Copies to"), 0, 0, 0,
2623 0, 0},
2624 {quick_input, 3, 50, 6, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2625 0, "mail-dlg-input-2"},
2626 {quick_label, 2, 50, 5, MAIL_DLG_HEIGHT, N_(" Subject"), 0, 0, 0,
2627 0, 0},
2628 {quick_input, 3, 50, 4, MAIL_DLG_HEIGHT, "", 44, 0, 0,
2629 0, "mail-dlg-input-3"},
2630 {quick_label, 2, 50, 3, MAIL_DLG_HEIGHT, N_(" To"), 0, 0, 0,
2631 0, 0},
2632 {quick_label, 2, 50, 2, MAIL_DLG_HEIGHT, N_(" mail -s <subject> -c <cc> <to>"), 0, 0, 0,
2633 0, 0},
2634 NULL_QuickWidget};
2636 quick_widgets[2].str_result = &tmail_cc;
2637 quick_widgets[2].text = mail_cc_last ? mail_cc_last : "";
2638 quick_widgets[4].str_result = &tmail_subject;
2639 quick_widgets[4].text = mail_subject_last ? mail_subject_last : "";
2640 quick_widgets[6].str_result = &tmail_to;
2641 quick_widgets[6].text = mail_to_last ? mail_to_last : "";
2643 Quick_input.widgets = quick_widgets;
2645 if (quick_dialog (&Quick_input) != B_CANCEL) {
2646 g_free (mail_cc_last);
2647 g_free (mail_subject_last);
2648 g_free (mail_to_last);
2649 mail_cc_last = tmail_cc;
2650 mail_subject_last = tmail_subject;
2651 mail_to_last = tmail_to;
2652 pipe_mail (edit, mail_to_last, mail_subject_last, mail_cc_last);
2657 /*******************/
2658 /* Word Completion */
2659 /*******************/
2662 /* find first character of current word */
2663 static int edit_find_word_start (WEdit *edit, long *word_start, int *word_len)
2665 int i, c, last;
2667 /* return if at begin of file */
2668 if (edit->curs1 <= 0)
2669 return 0;
2671 c = (unsigned char) edit_get_byte (edit, edit->curs1 - 1);
2672 /* return if not at end or in word */
2673 if (isspace (c) || !(isalnum (c) || c == '_'))
2674 return 0;
2676 /* search start of word to be completed */
2677 for (i = 2;; i++) {
2678 /* return if at begin of file */
2679 if (edit->curs1 - i < 0)
2680 return 0;
2682 last = c;
2683 c = (unsigned char) edit_get_byte (edit, edit->curs1 - i);
2685 if (!(isalnum (c) || c == '_')) {
2686 /* return if word starts with digit */
2687 if (isdigit (last))
2688 return 0;
2690 *word_start = edit->curs1 - (i - 1); /* start found */
2691 *word_len = i - 1;
2692 break;
2695 /* success */
2696 return 1;
2700 /* (re)set search parameters to the given values */
2701 static void edit_set_search_parameters (int rs, int rb, int rr, int rw, int rc)
2703 replace_scanf = rs;
2704 replace_backwards = rb;
2705 replace_regexp = rr;
2706 replace_whole = rw;
2707 replace_case = rc;
2711 #define MAX_WORD_COMPLETIONS 100 /* in listbox */
2713 /* collect the possible completions */
2714 static int
2715 edit_collect_completions (WEdit *edit, long start, int word_len,
2716 char *match_expr, struct selection *compl,
2717 int *num)
2719 int len, max_len = 0, i, skip;
2720 char *bufpos;
2722 /* collect max MAX_WORD_COMPLETIONS completions */
2723 while (*num < MAX_WORD_COMPLETIONS) {
2724 /* get next match */
2725 start =
2726 edit_find (start - 1, (unsigned char *) match_expr, &len,
2727 edit->last_byte,
2728 edit_get_byte,
2729 (void *) edit, 0);
2731 /* not matched */
2732 if (start < 0)
2733 break;
2735 /* add matched completion if not yet added */
2736 bufpos =
2737 &edit->
2738 buffers1[start >> S_EDIT_BUF_SIZE][start & M_EDIT_BUF_SIZE];
2739 skip = 0;
2740 for (i = 0; i < *num; i++) {
2741 if (strncmp
2742 (&compl[i].text[word_len], &bufpos[word_len],
2743 max (len, compl[i].len) - word_len) == 0) {
2744 skip = 1;
2745 break; /* skip it, already added */
2748 if (skip)
2749 continue;
2751 compl[*num].text = g_malloc (len + 1);
2752 compl[*num].len = len;
2753 for (i = 0; i < len; i++)
2754 compl[*num].text[i] = *(bufpos + i);
2755 compl[*num].text[i] = '\0';
2756 (*num)++;
2758 /* note the maximal length needed for the completion dialog */
2759 if (len > max_len)
2760 max_len = len;
2762 return max_len;
2766 /* let the user select its preferred completion */
2767 static void
2768 edit_completion_dialog (WEdit *edit, int max_len, int word_len,
2769 struct selection *compl, int num_compl)
2771 int start_x, start_y, offset, i;
2772 char *curr = NULL;
2773 Dlg_head *compl_dlg;
2774 WListbox *compl_list;
2775 int compl_dlg_h; /* completion dialog height */
2776 int compl_dlg_w; /* completion dialog width */
2778 /* calculate the dialog metrics */
2779 compl_dlg_h = num_compl + 2;
2780 compl_dlg_w = max_len + 4;
2781 start_x = edit->curs_col + edit->start_col - (compl_dlg_w / 2);
2782 start_y = edit->curs_row + EDIT_TEXT_VERTICAL_OFFSET + 1;
2784 if (start_x < 0)
2785 start_x = 0;
2786 if (compl_dlg_w > COLS)
2787 compl_dlg_w = COLS;
2788 if (compl_dlg_h > LINES - 2)
2789 compl_dlg_h = LINES - 2;
2791 offset = start_x + compl_dlg_w - COLS;
2792 if (offset > 0)
2793 start_x -= offset;
2794 offset = start_y + compl_dlg_h - LINES;
2795 if (offset > 0)
2796 start_y -= (offset + 1);
2798 /* create the dialog */
2799 compl_dlg =
2800 create_dlg (start_y, start_x, compl_dlg_h, compl_dlg_w,
2801 dialog_colors, NULL, "[Completion]", NULL,
2802 DLG_COMPACT);
2804 /* create the listbox */
2805 compl_list =
2806 listbox_new (1, 1, compl_dlg_w - 2, compl_dlg_h - 2, NULL);
2808 /* add the dialog */
2809 add_widget (compl_dlg, compl_list);
2811 /* fill the listbox with the completions */
2812 for (i = 0; i < num_compl; i++)
2813 listbox_add_item (compl_list, 0, 0, compl[i].text, NULL);
2815 /* pop up the dialog */
2816 run_dlg (compl_dlg);
2818 /* apply the choosen completion */
2819 if (compl_dlg->ret_value == B_ENTER) {
2820 listbox_get_current (compl_list, &curr, NULL);
2821 if (curr)
2822 for (curr += word_len; *curr; curr++)
2823 edit_insert (edit, *curr);
2826 /* destroy dialog before return */
2827 destroy_dlg (compl_dlg);
2832 * Complete current word using regular expression search
2833 * backwards beginning at the current cursor position.
2835 void
2836 edit_complete_word_cmd (WEdit *edit)
2838 int word_len = 0, i, num_compl = 0, max_len;
2839 long word_start = 0;
2840 char *bufpos;
2841 char *match_expr;
2842 struct selection compl[MAX_WORD_COMPLETIONS]; /* completions */
2844 /* don't want to disturb another search */
2845 int old_rs = replace_scanf;
2846 int old_rb = replace_backwards;
2847 int old_rr = replace_regexp;
2848 int old_rw = replace_whole;
2849 int old_rc = replace_case;
2851 /* search start of word to be completed */
2852 if (!edit_find_word_start (edit, &word_start, &word_len))
2853 return;
2855 /* prepare match expression */
2856 bufpos = &edit->buffers1[word_start >> S_EDIT_BUF_SIZE]
2857 [word_start & M_EDIT_BUF_SIZE];
2858 match_expr = g_strdup_printf ("%.*s[a-zA-Z_0-9]+", word_len, bufpos);
2860 /* init search: backward, regexp, whole word, case sensitive */
2861 edit_set_search_parameters (0, 1, 1, 1, 1);
2863 /* collect the possible completions */
2864 /* start search from curs1 down to begin of file */
2865 max_len =
2866 edit_collect_completions (edit, word_start, word_len, match_expr,
2867 (struct selection *) &compl, &num_compl);
2869 if (num_compl > 0) {
2870 /* insert completed word if there is only one match */
2871 if (num_compl == 1) {
2872 for (i = word_len; i < compl[0].len; i++)
2873 edit_insert (edit, *(compl[0].text + i));
2875 /* more than one possible completion => ask the user */
2876 else {
2877 /* !!! usually only a beep is expected and when <ALT-TAB> is !!! */
2878 /* !!! pressed again the selection dialog pops up, but that !!! */
2879 /* !!! seems to require a further internal state !!! */
2880 /*beep (); */
2882 /* let the user select the preferred completion */
2883 edit_completion_dialog (edit, max_len, word_len,
2884 (struct selection *) &compl,
2885 num_compl);
2889 g_free (match_expr);
2890 /* release memory before return */
2891 for (i = 0; i < num_compl; i++)
2892 g_free (compl[i].text);
2894 /* restore search parameters */
2895 edit_set_search_parameters (old_rs, old_rb, old_rr, old_rw, old_rc);