1 /*
2    Editor high level editing commands
3 
4    Copyright (C) 1996-2021
5    Free Software Foundation, Inc.
6 
7    Written by:
8    Paul Sheer, 1996, 1997
9    Andrew Borodin <aborodin@vmail.ru>, 2012-2021
10    Ilia Maslakov <il.smind@gmail.com>, 2012
11 
12    This file is part of the Midnight Commander.
13 
14    The Midnight Commander is free software: you can redistribute it
15    and/or modify it under the terms of the GNU General Public License as
16    published by the Free Software Foundation, either version 3 of the License,
17    or (at your option) any later version.
18 
19    The Midnight Commander is distributed in the hope that it will be useful,
20    but WITHOUT ANY WARRANTY; without even the implied warranty of
21    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22    GNU General Public License for more details.
23 
24    You should have received a copy of the GNU General Public License
25    along with this program.  If not, see <http://www.gnu.org/licenses/>.
26  */
27 
28 /** \file
29  *  \brief Source: editor high level editing commands
30  *  \author Paul Sheer
31  *  \date 1996, 1997
32  */
33 
34 /* #define PIPE_BLOCKS_SO_READ_BYTE_BY_BYTE */
35 
36 #include <config.h>
37 
38 #include <ctype.h>
39 #include <stdio.h>
40 #include <stdarg.h>
41 #include <sys/types.h>
42 #include <unistd.h>
43 #include <string.h>
44 #include <errno.h>
45 #include <sys/stat.h>
46 #include <stdlib.h>
47 
48 #include "lib/global.h"
49 #include "lib/tty/tty.h"
50 #include "lib/tty/key.h"        /* XCTRL */
51 #include "lib/strutil.h"        /* utf string functions */
52 #include "lib/fileloc.h"
53 #include "lib/lock.h"
54 #include "lib/util.h"           /* tilde_expand() */
55 #include "lib/vfs/vfs.h"
56 #include "lib/widget.h"
57 #include "lib/event.h"          /* mc_event_raise() */
58 #ifdef HAVE_CHARSET
59 #include "lib/charsets.h"
60 #endif
61 
62 #include "src/history.h"
63 #include "src/file_history.h"   /* show_file_history() */
64 #include "src/setup.h"          /* option_tab_spacing */
65 #ifdef HAVE_CHARSET
66 #include "src/selcodepage.h"
67 #endif
68 #include "src/util.h"           /* check_for_default() */
69 
70 #include "edit-impl.h"
71 #include "editwidget.h"
72 #include "editsearch.h"
73 #include "etags.h"
74 
75 /*** global variables ****************************************************************************/
76 
77 /* search and replace: */
78 int search_create_bookmark = FALSE;
79 
80 /* queries on a save */
81 gboolean edit_confirm_save = TRUE;
82 
83 /* whether we need to drop selection on copy to buffer */
84 gboolean option_drop_selection_on_copy = TRUE;
85 
86 /*** file scope macro definitions ****************************************************************/
87 
88 #define space_width 1
89 
90 #define TEMP_BUF_LEN 1024
91 
92 /*** file scope type declarations ****************************************************************/
93 
94 /*** file scope variables ************************************************************************/
95 
96 static unsigned long edit_save_mode_radio_id, edit_save_mode_input_id;
97 
98 /* --------------------------------------------------------------------------------------------- */
99 /*** file scope functions ************************************************************************/
100 /* --------------------------------------------------------------------------------------------- */
101 
102 static cb_ret_t
edit_save_mode_callback(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)103 edit_save_mode_callback (Widget * w, Widget * sender, widget_msg_t msg, int parm, void *data)
104 {
105     switch (msg)
106     {
107     case MSG_CHANGED_FOCUS:
108         if (sender != NULL && sender->id == edit_save_mode_radio_id)
109         {
110             Widget *ww;
111 
112             ww = widget_find_by_id (w, edit_save_mode_input_id);
113             widget_disable (ww, RADIO (sender)->sel != 2);
114             return MSG_HANDLED;
115         }
116         return MSG_NOT_HANDLED;
117 
118     default:
119         return dlg_default_callback (w, sender, msg, parm, data);
120     }
121 }
122 
123 /* --------------------------------------------------------------------------------------------- */
124 
125 /*  If 0 (quick save) then  a) create/truncate <filename> file,
126    b) save to <filename>;
127    if 1 (safe save) then   a) save to <tempnam>,
128    b) rename <tempnam> to <filename>;
129    if 2 (do backups) then  a) save to <tempnam>,
130    b) rename <filename> to <filename.backup_ext>,
131    c) rename <tempnam> to <filename>. */
132 
133 /* returns 0 on error, -1 on abort */
134 
135 static int
edit_save_file(WEdit * edit,const vfs_path_t * filename_vpath)136 edit_save_file (WEdit * edit, const vfs_path_t * filename_vpath)
137 {
138     char *p;
139     gchar *tmp;
140     off_t filelen = 0;
141     int this_save_mode, rv, fd = -1;
142     vfs_path_t *real_filename_vpath;
143     vfs_path_t *savename_vpath = NULL;
144     const char *start_filename;
145     const vfs_path_element_t *vpath_element;
146     struct stat sb;
147 
148     vpath_element = vfs_path_get_by_index (filename_vpath, 0);
149     if (vpath_element == NULL)
150         return 0;
151 
152     start_filename = vpath_element->path;
153     if (*start_filename == '\0')
154         return 0;
155 
156     if (!IS_PATH_SEP (*start_filename) && edit->dir_vpath != NULL)
157         real_filename_vpath = vfs_path_append_vpath_new (edit->dir_vpath, filename_vpath, NULL);
158     else
159         real_filename_vpath = vfs_path_clone (filename_vpath);
160 
161     this_save_mode = option_save_mode;
162     if (this_save_mode != EDIT_QUICK_SAVE)
163     {
164         if (!vfs_file_is_local (real_filename_vpath))
165             /* The file does not exists yet, so no safe save or backup are necessary. */
166             this_save_mode = EDIT_QUICK_SAVE;
167         else
168         {
169             fd = mc_open (real_filename_vpath, O_RDONLY | O_BINARY);
170             if (fd == -1)
171                 /* The file does not exists yet, so no safe save or backup are necessary. */
172                 this_save_mode = EDIT_QUICK_SAVE;
173         }
174 
175         if (fd != -1)
176             mc_close (fd);
177     }
178 
179     rv = mc_stat (real_filename_vpath, &sb);
180     if (rv == 0)
181     {
182         if (this_save_mode == EDIT_QUICK_SAVE && !edit->skip_detach_prompt && sb.st_nlink > 1)
183         {
184             rv = edit_query_dialog3 (_("Warning"),
185                                      _("File has hard-links. Detach before saving?"),
186                                      _("&Yes"), _("&No"), _("&Cancel"));
187             switch (rv)
188             {
189             case 0:
190                 this_save_mode = EDIT_SAFE_SAVE;
191                 MC_FALLTHROUGH;
192             case 1:
193                 edit->skip_detach_prompt = 1;
194                 break;
195             default:
196                 vfs_path_free (real_filename_vpath, TRUE);
197                 return -1;
198             }
199         }
200 
201         /* Prevent overwriting changes from other editor sessions. */
202         if (edit->stat1.st_mtime != 0 && edit->stat1.st_mtime != sb.st_mtime)
203         {
204             /* The default action is "Cancel". */
205             query_set_sel (1);
206 
207             rv = edit_query_dialog2 (_("Warning"),
208                                      _("The file has been modified in the meantime. Save anyway?"),
209                                      _("&Yes"), _("&Cancel"));
210             if (rv != 0)
211             {
212                 vfs_path_free (real_filename_vpath, TRUE);
213                 return -1;
214             }
215         }
216     }
217 
218     if (this_save_mode == EDIT_QUICK_SAVE)
219         savename_vpath = vfs_path_clone (real_filename_vpath);
220     else
221     {
222         char *savedir, *saveprefix;
223 
224         savedir = vfs_path_tokens_get (real_filename_vpath, 0, -1);
225         if (savedir == NULL)
226             savedir = g_strdup (".");
227 
228         /* Token-related function never return leading slash, so we need add it manually */
229         saveprefix = mc_build_filename (PATH_SEP_STR, savedir, "cooledit", (char *) NULL);
230         g_free (savedir);
231         fd = mc_mkstemps (&savename_vpath, saveprefix, NULL);
232         g_free (saveprefix);
233         if (savename_vpath == NULL)
234         {
235             vfs_path_free (real_filename_vpath, TRUE);
236             return 0;
237         }
238         /* FIXME:
239          * Close for now because mc_mkstemps use pure open system call
240          * to create temporary file and it needs to be reopened by
241          * VFS-aware mc_open().
242          */
243         close (fd);
244     }
245 
246     (void) mc_chown (savename_vpath, edit->stat1.st_uid, edit->stat1.st_gid);
247     (void) mc_chmod (savename_vpath, edit->stat1.st_mode);
248 
249     fd = mc_open (savename_vpath, O_CREAT | O_WRONLY | O_TRUNC | O_BINARY, edit->stat1.st_mode);
250     if (fd == -1)
251         goto error_save;
252 
253     /* pipe save */
254     p = edit_get_write_filter (savename_vpath, real_filename_vpath);
255     if (p != NULL)
256     {
257         FILE *file;
258 
259         mc_close (fd);
260         file = (FILE *) popen (p, "w");
261 
262         if (file != NULL)
263         {
264             filelen = edit_write_stream (edit, file);
265 #if 1
266             pclose (file);
267 #else
268             if (pclose (file) != 0)
269             {
270                 tmp = g_strdup_printf (_("Error writing to pipe: %s"), p);
271                 edit_error_dialog (_("Error"), tmp);
272                 g_free (tmp);
273                 g_free (p);
274                 goto error_save;
275             }
276 #endif
277         }
278         else
279         {
280             tmp = g_strdup_printf (_("Cannot open pipe for writing: %s"), p);
281             edit_error_dialog (_("Error"), get_sys_error (tmp));
282             g_free (p);
283             g_free (tmp);
284             goto error_save;
285         }
286         g_free (p);
287     }
288     else if (edit->lb == LB_ASIS)
289     {                           /* do not change line breaks */
290         filelen = edit_buffer_write_file (&edit->buffer, fd);
291 
292         if (filelen != edit->buffer.size)
293         {
294             mc_close (fd);
295             goto error_save;
296         }
297 
298         if (mc_close (fd) != 0)
299             goto error_save;
300 
301         /* Update the file information, especially the mtime. */
302         if (mc_stat (savename_vpath, &edit->stat1) == -1)
303             goto error_save;
304     }
305     else
306     {                           /* change line breaks */
307         FILE *file;
308         const vfs_path_element_t *path_element;
309 
310         mc_close (fd);
311 
312         path_element = vfs_path_get_by_index (savename_vpath, -1);
313         file = (FILE *) fopen (path_element->path, "w");
314         if (file != NULL)
315         {
316             filelen = edit_write_stream (edit, file);
317             fclose (file);
318         }
319         else
320         {
321             char *msg;
322 
323             msg = g_strdup_printf (_("Cannot open file for writing: %s"), path_element->path);
324             edit_error_dialog (_("Error"), msg);
325             g_free (msg);
326             goto error_save;
327         }
328     }
329 
330     if (filelen != edit->buffer.size)
331         goto error_save;
332 
333     if (this_save_mode == EDIT_DO_BACKUP)
334     {
335         char *tmp_store_filename;
336         vfs_path_element_t *last_vpath_element;
337         vfs_path_t *tmp_vpath;
338         gboolean ok;
339 
340         g_assert (option_backup_ext != NULL);
341 
342         /* add backup extension to the path */
343         tmp_vpath = vfs_path_clone (real_filename_vpath);
344         last_vpath_element = (vfs_path_element_t *) vfs_path_get_by_index (tmp_vpath, -1);
345         tmp_store_filename = last_vpath_element->path;
346         last_vpath_element->path = g_strdup_printf ("%s%s", tmp_store_filename, option_backup_ext);
347         g_free (tmp_store_filename);
348 
349         ok = (mc_rename (real_filename_vpath, tmp_vpath) != -1);
350         vfs_path_free (tmp_vpath, TRUE);
351         if (!ok)
352             goto error_save;
353     }
354 
355     if (this_save_mode != EDIT_QUICK_SAVE && mc_rename (savename_vpath, real_filename_vpath) == -1)
356         goto error_save;
357 
358     vfs_path_free (real_filename_vpath, TRUE);
359     vfs_path_free (savename_vpath, TRUE);
360     return 1;
361   error_save:
362     /*  FIXME: Is this safe ?
363      *  if (this_save_mode != EDIT_QUICK_SAVE)
364      *      mc_unlink (savename);
365      */
366     vfs_path_free (real_filename_vpath, TRUE);
367     vfs_path_free (savename_vpath, TRUE);
368     return 0;
369 }
370 
371 /* --------------------------------------------------------------------------------------------- */
372 
373 static gboolean
edit_check_newline(const edit_buffer_t * buf)374 edit_check_newline (const edit_buffer_t * buf)
375 {
376     return !(option_check_nl_at_eof && buf->size > 0
377              && edit_buffer_get_byte (buf, buf->size - 1) != '\n'
378              && edit_query_dialog2 (_("Warning"),
379                                     _("The file you are saving does not end with a newline."),
380                                     _("C&ontinue"), _("&Cancel")) != 0);
381 }
382 
383 /* --------------------------------------------------------------------------------------------- */
384 
385 static vfs_path_t *
edit_get_save_file_as(WEdit * edit)386 edit_get_save_file_as (WEdit * edit)
387 {
388     static LineBreaks cur_lb = LB_ASIS;
389     char *filename_res;
390     vfs_path_t *ret_vpath = NULL;
391 
392     const char *lb_names[LB_NAMES] = {
393         N_("&Do not change"),
394         N_("&Unix format (LF)"),
395         N_("&Windows/DOS format (CR LF)"),
396         N_("&Macintosh format (CR)")
397     };
398 
399     quick_widget_t quick_widgets[] = {
400         /* *INDENT-OFF* */
401         QUICK_LABELED_INPUT (N_("Enter file name:"), input_label_above,
402                              vfs_path_as_str (edit->filename_vpath), "save-as",
403                              &filename_res, NULL, FALSE, FALSE, INPUT_COMPLETE_FILENAMES),
404         QUICK_SEPARATOR (TRUE),
405         QUICK_LABEL (N_("Change line breaks to:"), NULL),
406         QUICK_RADIO (LB_NAMES, lb_names, (int *) &cur_lb, NULL),
407         QUICK_BUTTONS_OK_CANCEL,
408         QUICK_END
409         /* *INDENT-ON* */
410     };
411 
412     quick_dialog_t qdlg = {
413         -1, -1, 64,
414         N_("Save As"), "[Save File As]",
415         quick_widgets, NULL, NULL
416     };
417 
418     if (quick_dialog (&qdlg) != B_CANCEL)
419     {
420         char *fname;
421 
422         edit->lb = cur_lb;
423         fname = tilde_expand (filename_res);
424         g_free (filename_res);
425         ret_vpath = vfs_path_from_str (fname);
426         g_free (fname);
427     }
428 
429     return ret_vpath;
430 }
431 
432 /* --------------------------------------------------------------------------------------------- */
433 
434 /** returns TRUE on success */
435 
436 static gboolean
edit_save_cmd(WEdit * edit)437 edit_save_cmd (WEdit * edit)
438 {
439     int res, save_lock = 0;
440 
441     if (!edit->locked && !edit->delete_file)
442         save_lock = lock_file (edit->filename_vpath);
443     res = edit_save_file (edit, edit->filename_vpath);
444 
445     /* Maintain modify (not save) lock on failure */
446     if ((res > 0 && edit->locked) || save_lock)
447         edit->locked = unlock_file (edit->filename_vpath);
448 
449     /* On failure try 'save as', it does locking on its own */
450     if (res == 0)
451         return edit_save_as_cmd (edit);
452 
453     if (res > 0)
454     {
455         edit->delete_file = 0;
456         edit->modified = 0;
457     }
458 
459     edit->force |= REDRAW_COMPLETELY;
460 
461     return TRUE;
462 }
463 
464 /* --------------------------------------------------------------------------------------------- */
465 /**
466  * Load file content
467  *
468  * @param h screen the owner of editor window
469  * @param vpath vfs file path
470  * @return TRUE if file content was successfully loaded, FALSE otherwise
471  */
472 
473 static inline gboolean
edit_load_file_from_filename(WDialog * h,const vfs_path_t * vpath)474 edit_load_file_from_filename (WDialog * h, const vfs_path_t * vpath)
475 {
476     Widget *w = WIDGET (h);
477 
478     return edit_add_window (h, w->y + 1, w->x, w->lines - 2, w->cols, vpath, 0);
479 }
480 
481 /* --------------------------------------------------------------------------------------------- */
482 
483 static void
edit_delete_column_of_text(WEdit * edit)484 edit_delete_column_of_text (WEdit * edit)
485 {
486     off_t m1, m2;
487     off_t n;
488     long b, c, d;
489 
490     eval_marks (edit, &m1, &m2);
491     n = edit_buffer_get_forward_offset (&edit->buffer, m1, 0, m2) + 1;
492     c = (long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, m1), 0, m1);
493     d = (long) edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, m2), 0, m2);
494     b = MAX (MIN (c, d), MIN (edit->column1, edit->column2));
495     c = MAX (c, MAX (edit->column1, edit->column2));
496 
497     while (n-- != 0)
498     {
499         off_t r, p, q;
500 
501         r = edit_buffer_get_current_bol (&edit->buffer);
502         p = edit_move_forward3 (edit, r, b, 0);
503         q = edit_move_forward3 (edit, r, c, 0);
504         p = MAX (p, m1);
505         q = MIN (q, m2);
506         edit_cursor_move (edit, p - edit->buffer.curs1);
507         /* delete line between margins */
508         for (; q > p; q--)
509             if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
510                 edit_delete (edit, TRUE);
511 
512         /* move to next line except on the last delete */
513         if (n != 0)
514             edit_cursor_move (edit,
515                               edit_buffer_get_forward_offset (&edit->buffer, edit->buffer.curs1, 1,
516                                                               0) - edit->buffer.curs1);
517     }
518 }
519 
520 /* --------------------------------------------------------------------------------------------- */
521 /** if success return 0 */
522 
523 static int
edit_block_delete(WEdit * edit)524 edit_block_delete (WEdit * edit)
525 {
526     off_t start_mark, end_mark;
527     off_t curs_pos;
528     long curs_line, c1, c2;
529 
530     if (!eval_marks (edit, &start_mark, &end_mark))
531         return 0;
532 
533     if (edit->column_highlight && edit->mark2 < 0)
534         edit_mark_cmd (edit, FALSE);
535 
536     /* Warning message with a query to continue or cancel the operation */
537     if ((end_mark - start_mark) > option_max_undo / 2 &&
538         edit_query_dialog2 (_("Warning"),
539                             ("Block is large, you may not be able to undo this action"),
540                             _("C&ontinue"), _("&Cancel")) != 0)
541         return 1;
542 
543     c1 = MIN (edit->column1, edit->column2);
544     c2 = MAX (edit->column1, edit->column2);
545     edit->column1 = c1;
546     edit->column2 = c2;
547 
548     edit_push_markers (edit);
549 
550     curs_line = edit->buffer.curs_line;
551 
552     curs_pos = edit->curs_col + edit->over_col;
553 
554     /* move cursor to start of selection */
555     edit_cursor_move (edit, start_mark - edit->buffer.curs1);
556     edit_scroll_screen_over_cursor (edit);
557 
558     if (start_mark < end_mark)
559     {
560         if (edit->column_highlight)
561         {
562             off_t line_width;
563 
564             if (edit->mark2 < 0)
565                 edit_mark_cmd (edit, FALSE);
566             edit_delete_column_of_text (edit);
567             /* move cursor to the saved position */
568             edit_move_to_line (edit, curs_line);
569             /* calculate line width and cursor position before cut */
570             line_width = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), 0,
571                                              edit_buffer_get_current_eol (&edit->buffer));
572             if (option_cursor_beyond_eol && curs_pos > line_width)
573                 edit->over_col = curs_pos - line_width;
574         }
575         else
576         {
577             off_t count;
578 
579             for (count = start_mark; count < end_mark; count++)
580                 edit_delete (edit, TRUE);
581         }
582     }
583 
584     edit_set_markers (edit, 0, 0, 0, 0);
585     edit->force |= REDRAW_PAGE;
586 
587     return 0;
588 }
589 
590 /* --------------------------------------------------------------------------------------------- */
591 /** Return a null terminated length of text. Result must be g_free'd */
592 
593 static unsigned char *
edit_get_block(WEdit * edit,off_t start,off_t finish,off_t * l)594 edit_get_block (WEdit * edit, off_t start, off_t finish, off_t * l)
595 {
596     unsigned char *s, *r;
597 
598     r = s = g_malloc0 (finish - start + 1);
599 
600     if (edit->column_highlight)
601     {
602         *l = 0;
603 
604         /* copy from buffer, excluding chars that are out of the column 'margins' */
605         for (; start < finish; start++)
606         {
607             int c;
608             off_t x;
609 
610             x = edit_move_forward3 (edit, edit_buffer_get_bol (&edit->buffer, start), 0, start);
611             c = edit_buffer_get_byte (&edit->buffer, start);
612             if ((x >= edit->column1 && x < edit->column2)
613                 || (x >= edit->column2 && x < edit->column1) || c == '\n')
614             {
615                 *s++ = c;
616                 (*l)++;
617             }
618         }
619     }
620     else
621     {
622         *l = finish - start;
623 
624         for (; start < finish; start++)
625             *s++ = edit_buffer_get_byte (&edit->buffer, start);
626     }
627 
628     *s = '\0';
629 
630     return r;
631 }
632 
633 /* --------------------------------------------------------------------------------------------- */
634 /** copies a block to clipboard file */
635 
636 static gboolean
edit_save_block_to_clip_file(WEdit * edit,off_t start,off_t finish)637 edit_save_block_to_clip_file (WEdit * edit, off_t start, off_t finish)
638 {
639     gboolean ret;
640     gchar *tmp;
641 
642     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
643     ret = edit_save_block (edit, tmp, start, finish);
644     g_free (tmp);
645 
646     return ret;
647 }
648 
649 /* --------------------------------------------------------------------------------------------- */
650 
651 static void
pipe_mail(const edit_buffer_t * buf,char * to,char * subject,char * cc)652 pipe_mail (const edit_buffer_t * buf, char *to, char *subject, char *cc)
653 {
654     FILE *p = 0;
655     char *s;
656 
657     to = name_quote (to, FALSE);
658     subject = name_quote (subject, FALSE);
659     cc = name_quote (cc, FALSE);
660     s = g_strconcat ("mail -s ", subject, *cc ? " -c " : "", cc, " ", to, (char *) NULL);
661     g_free (to);
662     g_free (subject);
663     g_free (cc);
664 
665     if (s != NULL)
666     {
667         p = popen (s, "w");
668         g_free (s);
669     }
670 
671     if (p != NULL)
672     {
673         off_t i;
674 
675         for (i = 0; i < buf->size; i++)
676             if (fputc (edit_buffer_get_byte (buf, i), p) < 0)
677                 break;
678         pclose (p);
679     }
680 }
681 
682 /* --------------------------------------------------------------------------------------------- */
683 
684 static void
edit_insert_column_of_text(WEdit * edit,unsigned char * data,off_t size,long width,off_t * start_pos,off_t * end_pos,long * col1,long * col2)685 edit_insert_column_of_text (WEdit * edit, unsigned char *data, off_t size, long width,
686                             off_t * start_pos, off_t * end_pos, long *col1, long *col2)
687 {
688     off_t i, cursor;
689     long col;
690 
691     cursor = edit->buffer.curs1;
692     col = edit_get_col (edit);
693 
694     for (i = 0; i < size; i++)
695     {
696         if (data[i] != '\n')
697             edit_insert (edit, data[i]);
698         else
699         {                       /* fill in and move to next line */
700             long l;
701             off_t p;
702 
703             if (edit_buffer_get_current_byte (&edit->buffer) != '\n')
704             {
705                 for (l = width - (edit_get_col (edit) - col); l > 0; l -= space_width)
706                     edit_insert (edit, ' ');
707             }
708             for (p = edit->buffer.curs1;; p++)
709             {
710                 if (p == edit->buffer.size)
711                 {
712                     edit_cursor_move (edit, edit->buffer.size - edit->buffer.curs1);
713                     edit_insert_ahead (edit, '\n');
714                     p++;
715                     break;
716                 }
717                 if (edit_buffer_get_byte (&edit->buffer, p) == '\n')
718                 {
719                     p++;
720                     break;
721                 }
722             }
723             edit_cursor_move (edit, edit_move_forward3 (edit, p, col, 0) - edit->buffer.curs1);
724 
725             for (l = col - edit_get_col (edit); l >= space_width; l -= space_width)
726                 edit_insert (edit, ' ');
727         }
728     }
729 
730     *col1 = col;
731     *col2 = col + width;
732     *start_pos = cursor;
733     *end_pos = edit->buffer.curs1;
734     edit_cursor_move (edit, cursor - edit->buffer.curs1);
735 }
736 
737 /* --------------------------------------------------------------------------------------------- */
738 /**
739  * Callback for the iteration of objects in the 'editors' array.
740  * Toggle syntax highlighting in editor object.
741  *
742  * @param data      probably WEdit object
743  * @param user_data unused
744  */
745 
746 static void
edit_syntax_onoff_cb(void * data,void * user_data)747 edit_syntax_onoff_cb (void *data, void *user_data)
748 {
749     (void) user_data;
750 
751     if (edit_widget_is_editor (CONST_WIDGET (data)))
752     {
753         WEdit *edit = (WEdit *) data;
754 
755         if (option_syntax_highlighting)
756             edit_load_syntax (edit, NULL, edit->syntax_type);
757         edit->force |= REDRAW_PAGE;
758     }
759 }
760 
761 /* --------------------------------------------------------------------------------------------- */
762 
763 static cb_ret_t
editcmd_dialog_raw_key_query_cb(Widget * w,Widget * sender,widget_msg_t msg,int parm,void * data)764 editcmd_dialog_raw_key_query_cb (Widget * w, Widget * sender, widget_msg_t msg, int parm,
765                                  void *data)
766 {
767     WDialog *h = DIALOG (w);
768 
769     switch (msg)
770     {
771     case MSG_KEY:
772         h->ret_value = parm;
773         dlg_stop (h);
774         return MSG_HANDLED;
775     default:
776         return dlg_default_callback (w, sender, msg, parm, data);
777     }
778 }
779 
780 /* --------------------------------------------------------------------------------------------- */
781 /*** public functions ****************************************************************************/
782 /* --------------------------------------------------------------------------------------------- */
783 
784 void
edit_refresh_cmd(void)785 edit_refresh_cmd (void)
786 {
787     tty_clear_screen ();
788     repaint_screen ();
789     tty_keypad (TRUE);
790 }
791 
792 /* --------------------------------------------------------------------------------------------- */
793 /**
794  * Toggle syntax highlighting in all editor windows.
795  *
796  * @param h root widget for all windows
797  */
798 
799 void
edit_syntax_onoff_cmd(WDialog * h)800 edit_syntax_onoff_cmd (WDialog * h)
801 {
802     option_syntax_highlighting = !option_syntax_highlighting;
803     g_list_foreach (GROUP (h)->widgets, edit_syntax_onoff_cb, NULL);
804     widget_draw (WIDGET (h));
805 }
806 
807 /* --------------------------------------------------------------------------------------------- */
808 /**
809  * Toggle tabs showing in all editor windows.
810  *
811  * @param h root widget for all windows
812  */
813 
814 void
edit_show_tabs_tws_cmd(WDialog * h)815 edit_show_tabs_tws_cmd (WDialog * h)
816 {
817     enable_show_tabs_tws = !enable_show_tabs_tws;
818     widget_draw (WIDGET (h));
819 }
820 
821 /* --------------------------------------------------------------------------------------------- */
822 /**
823  * Toggle right margin showing in all editor windows.
824  *
825  * @param h root widget for all windows
826  */
827 
828 void
edit_show_margin_cmd(WDialog * h)829 edit_show_margin_cmd (WDialog * h)
830 {
831     show_right_margin = !show_right_margin;
832     widget_draw (WIDGET (h));
833 }
834 
835 /* --------------------------------------------------------------------------------------------- */
836 /**
837  * Toggle line numbers showing in all editor windows.
838  *
839  * @param h root widget for all windows
840  */
841 
842 void
edit_show_numbers_cmd(WDialog * h)843 edit_show_numbers_cmd (WDialog * h)
844 {
845     option_line_state = !option_line_state;
846     option_line_state_width = option_line_state ? LINE_STATE_WIDTH : 0;
847     widget_draw (WIDGET (h));
848 }
849 
850 /* --------------------------------------------------------------------------------------------- */
851 
852 void
edit_save_mode_cmd(void)853 edit_save_mode_cmd (void)
854 {
855     char *str_result = NULL;
856 
857     const char *str[] = {
858         N_("&Quick save"),
859         N_("&Safe save"),
860         N_("&Do backups with following extension:")
861     };
862 
863 #ifdef ENABLE_NLS
864     size_t i;
865 
866     for (i = 0; i < 3; i++)
867         str[i] = _(str[i]);
868 #endif
869 
870     g_assert (option_backup_ext != NULL);
871 
872     {
873         quick_widget_t quick_widgets[] = {
874             /* *INDENT-OFF* */
875             QUICK_RADIO (3, str, &option_save_mode, &edit_save_mode_radio_id),
876             QUICK_INPUT (option_backup_ext, "edit-backup-ext", &str_result,
877                          &edit_save_mode_input_id, FALSE, FALSE, INPUT_COMPLETE_NONE),
878             QUICK_SEPARATOR (TRUE),
879             QUICK_CHECKBOX (N_("Check &POSIX new line"), &option_check_nl_at_eof, NULL),
880             QUICK_BUTTONS_OK_CANCEL,
881             QUICK_END
882             /* *INDENT-ON* */
883         };
884 
885         quick_dialog_t qdlg = {
886             -1, -1, 38,
887             N_("Edit Save Mode"), "[Edit Save Mode]",
888             quick_widgets, edit_save_mode_callback, NULL
889         };
890 
891         if (quick_dialog (&qdlg) != B_CANCEL)
892         {
893             g_free (option_backup_ext);
894             option_backup_ext = str_result;
895         }
896     }
897 }
898 
899 /* --------------------------------------------------------------------------------------------- */
900 
901 void
edit_set_filename(WEdit * edit,const vfs_path_t * name_vpath)902 edit_set_filename (WEdit * edit, const vfs_path_t * name_vpath)
903 {
904     vfs_path_free (edit->filename_vpath, TRUE);
905     edit->filename_vpath = vfs_path_clone (name_vpath);
906 
907     if (edit->dir_vpath == NULL)
908         edit->dir_vpath = vfs_path_clone (vfs_get_raw_current_dir ());
909 }
910 
911 /* --------------------------------------------------------------------------------------------- */
912 /* Here we want to warn the users of overwriting an existing file,
913    but only if they have made a change to the filename */
914 /* returns TRUE on success */
915 gboolean
edit_save_as_cmd(WEdit * edit)916 edit_save_as_cmd (WEdit * edit)
917 {
918     /* This heads the 'Save As' dialog box */
919     vfs_path_t *exp_vpath;
920     int save_lock = 0;
921     gboolean different_filename = FALSE;
922     gboolean ret = FALSE;
923 
924     if (!edit_check_newline (&edit->buffer))
925         return FALSE;
926 
927     exp_vpath = edit_get_save_file_as (edit);
928     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
929 
930     if (exp_vpath != NULL && vfs_path_len (exp_vpath) != 0)
931     {
932         int rv;
933 
934         if (!vfs_path_equal (edit->filename_vpath, exp_vpath))
935         {
936             int file;
937             struct stat sb;
938 
939             if (mc_stat (exp_vpath, &sb) == 0 && !S_ISREG (sb.st_mode))
940             {
941                 edit_error_dialog (_("Save as"),
942                                    get_sys_error (_
943                                                   ("Cannot save: destination is not a regular file")));
944                 goto ret;
945             }
946 
947             different_filename = TRUE;
948             file = mc_open (exp_vpath, O_RDONLY | O_BINARY);
949 
950             if (file == -1)
951                 edit->stat1.st_mode |= S_IWUSR;
952             else
953             {
954                 /* the file exists */
955                 mc_close (file);
956                 /* Overwrite the current file or cancel the operation */
957                 if (edit_query_dialog2
958                     (_("Warning"),
959                      _("A file already exists with this name"), _("&Overwrite"), _("&Cancel")))
960                     goto ret;
961             }
962 
963             save_lock = lock_file (exp_vpath);
964         }
965         else if (!edit->locked && !edit->delete_file)
966             /* filenames equal, check if already locked */
967             save_lock = lock_file (exp_vpath);
968 
969         if (different_filename)
970             /* Allow user to write into saved (under another name) file
971              * even if original file had r/o user permissions. */
972             edit->stat1.st_mode |= S_IWUSR;
973 
974         rv = edit_save_file (edit, exp_vpath);
975         switch (rv)
976         {
977         case 1:
978             /* Successful, so unlock both files */
979             if (different_filename)
980             {
981                 if (save_lock)
982                     unlock_file (exp_vpath);
983                 if (edit->locked)
984                     edit->locked = unlock_file (edit->filename_vpath);
985             }
986             else if (edit->locked || save_lock)
987                 edit->locked = unlock_file (edit->filename_vpath);
988 
989             edit_set_filename (edit, exp_vpath);
990             if (edit->lb != LB_ASIS)
991                 edit_reload (edit, exp_vpath);
992             edit->modified = 0;
993             edit->delete_file = 0;
994             if (different_filename)
995                 edit_load_syntax (edit, NULL, edit->syntax_type);
996             ret = TRUE;
997             break;
998 
999         default:
1000             edit_error_dialog (_("Save as"), get_sys_error (_("Cannot save file")));
1001             MC_FALLTHROUGH;
1002 
1003         case -1:
1004             /* Failed, so maintain modify (not save) lock */
1005             if (save_lock)
1006                 unlock_file (exp_vpath);
1007             break;
1008         }
1009     }
1010 
1011   ret:
1012     vfs_path_free (exp_vpath, TRUE);
1013     edit->force |= REDRAW_COMPLETELY;
1014     return ret;
1015 }
1016 
1017 /* --------------------------------------------------------------------------------------------- */
1018 /** returns TRUE on success */
1019 
1020 gboolean
edit_save_confirm_cmd(WEdit * edit)1021 edit_save_confirm_cmd (WEdit * edit)
1022 {
1023     if (edit->filename_vpath == NULL)
1024         return edit_save_as_cmd (edit);
1025 
1026     if (!edit_check_newline (&edit->buffer))
1027         return FALSE;
1028 
1029     if (edit_confirm_save)
1030     {
1031         char *f;
1032         gboolean ok;
1033 
1034         f = g_strdup_printf (_("Confirm save file: \"%s\""),
1035                              vfs_path_as_str (edit->filename_vpath));
1036         ok = (edit_query_dialog2 (_("Save file"), f, _("&Save"), _("&Cancel")) == 0);
1037         g_free (f);
1038         if (!ok)
1039             return FALSE;
1040     }
1041 
1042     return edit_save_cmd (edit);
1043 }
1044 
1045 /* --------------------------------------------------------------------------------------------- */
1046 /**
1047   * Ask file to edit and load it.
1048   *
1049   * @return TRUE on success or cancel of ask.
1050   */
1051 
1052 gboolean
edit_load_cmd(WDialog * h)1053 edit_load_cmd (WDialog * h)
1054 {
1055     char *exp;
1056     gboolean ret = TRUE;        /* possible cancel */
1057 
1058     exp = input_expand_dialog (_("Load"), _("Enter file name:"),
1059                                MC_HISTORY_EDIT_LOAD, INPUT_LAST_TEXT,
1060                                INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_CD);
1061 
1062     if (exp != NULL && *exp != '\0')
1063     {
1064         vfs_path_t *exp_vpath;
1065 
1066         exp_vpath = vfs_path_from_str (exp);
1067         ret = edit_load_file_from_filename (h, exp_vpath);
1068         vfs_path_free (exp_vpath, TRUE);
1069     }
1070 
1071     g_free (exp);
1072 
1073     return ret;
1074 }
1075 
1076 /* --------------------------------------------------------------------------------------------- */
1077 /**
1078   * Show history od edited or viewed files and open selected file.
1079   *
1080   * @return TRUE on success, FALSE otherwise.
1081   */
1082 
1083 gboolean
edit_load_file_from_history(WDialog * h)1084 edit_load_file_from_history (WDialog * h)
1085 {
1086     char *exp;
1087     int action;
1088     gboolean ret = TRUE;        /* possible cancel */
1089 
1090     exp = show_file_history (CONST_WIDGET (h), &action);
1091     if (exp != NULL && (action == CK_Edit || action == CK_Enter))
1092     {
1093         vfs_path_t *exp_vpath;
1094 
1095         exp_vpath = vfs_path_from_str (exp);
1096         ret = edit_load_file_from_filename (h, exp_vpath);
1097         vfs_path_free (exp_vpath, TRUE);
1098     }
1099 
1100     g_free (exp);
1101 
1102     return ret;
1103 }
1104 
1105 /* --------------------------------------------------------------------------------------------- */
1106 /**
1107   * Load syntax file to edit.
1108   *
1109   * @return TRUE on success
1110   */
1111 
1112 gboolean
edit_load_syntax_file(WDialog * h)1113 edit_load_syntax_file (WDialog * h)
1114 {
1115     vfs_path_t *extdir_vpath;
1116     int dir = 0;
1117     gboolean ret = FALSE;
1118 
1119     if (geteuid () == 0)
1120         dir = query_dialog (_("Syntax file edit"),
1121                             _("Which syntax file you want to edit?"), D_NORMAL, 2,
1122                             _("&User"), _("&System wide"));
1123 
1124     extdir_vpath =
1125         vfs_path_build_filename (mc_global.sysconfig_dir, "syntax", "Syntax", (char *) NULL);
1126     if (!exist_file (vfs_path_get_last_path_str (extdir_vpath)))
1127     {
1128         vfs_path_free (extdir_vpath, TRUE);
1129         extdir_vpath =
1130             vfs_path_build_filename (mc_global.share_data_dir, "syntax", "Syntax", (char *) NULL);
1131     }
1132 
1133     if (dir == 0)
1134     {
1135         vfs_path_t *user_syntax_file_vpath;
1136 
1137         user_syntax_file_vpath = mc_config_get_full_vpath (EDIT_HOME_SYNTAX_FILE);
1138         check_for_default (extdir_vpath, user_syntax_file_vpath);
1139         ret = edit_load_file_from_filename (h, user_syntax_file_vpath);
1140         vfs_path_free (user_syntax_file_vpath, TRUE);
1141     }
1142     else if (dir == 1)
1143         ret = edit_load_file_from_filename (h, extdir_vpath);
1144 
1145     vfs_path_free (extdir_vpath, TRUE);
1146 
1147     return ret;
1148 }
1149 
1150 /* --------------------------------------------------------------------------------------------- */
1151 /**
1152   * Load menu file to edit.
1153   *
1154   * @return TRUE on success
1155   */
1156 
1157 gboolean
edit_load_menu_file(WDialog * h)1158 edit_load_menu_file (WDialog * h)
1159 {
1160     vfs_path_t *buffer_vpath;
1161     vfs_path_t *menufile_vpath;
1162     int dir;
1163     gboolean ret;
1164 
1165     query_set_sel (1);
1166     dir = query_dialog (_("Menu edit"),
1167                         _("Which menu file do you want to edit?"), D_NORMAL,
1168                         geteuid () != 0 ? 2 : 3, _("&Local"), _("&User"), _("&System wide"));
1169 
1170     menufile_vpath =
1171         vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1172     if (!exist_file (vfs_path_get_last_path_str (menufile_vpath)))
1173     {
1174         vfs_path_free (menufile_vpath, TRUE);
1175         menufile_vpath =
1176             vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1177     }
1178 
1179     switch (dir)
1180     {
1181     case 0:
1182         buffer_vpath = vfs_path_from_str (EDIT_LOCAL_MENU);
1183         check_for_default (menufile_vpath, buffer_vpath);
1184         chmod (vfs_path_get_last_path_str (buffer_vpath), 0600);
1185         break;
1186 
1187     case 1:
1188         buffer_vpath = mc_config_get_full_vpath (EDIT_HOME_MENU);
1189         check_for_default (menufile_vpath, buffer_vpath);
1190         break;
1191 
1192     case 2:
1193         buffer_vpath =
1194             vfs_path_build_filename (mc_global.sysconfig_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1195         if (!exist_file (vfs_path_get_last_path_str (buffer_vpath)))
1196         {
1197             vfs_path_free (buffer_vpath, TRUE);
1198             buffer_vpath =
1199                 vfs_path_build_filename (mc_global.share_data_dir, EDIT_GLOBAL_MENU, (char *) NULL);
1200         }
1201         break;
1202 
1203     default:
1204         vfs_path_free (menufile_vpath, TRUE);
1205         return FALSE;
1206     }
1207 
1208     ret = edit_load_file_from_filename (h, buffer_vpath);
1209 
1210     vfs_path_free (buffer_vpath, TRUE);
1211     vfs_path_free (menufile_vpath, TRUE);
1212 
1213     return ret;
1214 }
1215 
1216 /* --------------------------------------------------------------------------------------------- */
1217 /**
1218   * Close window with opened file.
1219   *
1220   * @return TRUE if file was closed.
1221   */
1222 
1223 gboolean
edit_close_cmd(WEdit * edit)1224 edit_close_cmd (WEdit * edit)
1225 {
1226     gboolean ret;
1227 
1228     ret = (edit != NULL) && edit_ok_to_exit (edit);
1229 
1230     if (ret)
1231     {
1232         Widget *w = WIDGET (edit);
1233         WGroup *g = w->owner;
1234 
1235         if (edit->locked != 0)
1236             unlock_file (edit->filename_vpath);
1237 
1238         group_remove_widget (w);
1239         widget_destroy (w);
1240 
1241         if (edit_widget_is_editor (CONST_WIDGET (g->current->data)))
1242             edit = (WEdit *) (g->current->data);
1243         else
1244         {
1245             edit = find_editor (DIALOG (g));
1246             if (edit != NULL)
1247                 widget_select (WIDGET (edit));
1248         }
1249     }
1250 
1251     if (edit != NULL)
1252         edit->force |= REDRAW_COMPLETELY;
1253 
1254     return ret;
1255 }
1256 
1257 /* --------------------------------------------------------------------------------------------- */
1258 /**
1259    if mark2 is -1 then marking is from mark1 to the cursor.
1260    Otherwise its between the markers. This handles this.
1261    Returns FALSE if no text is marked.
1262  */
1263 
1264 gboolean
eval_marks(WEdit * edit,off_t * start_mark,off_t * end_mark)1265 eval_marks (WEdit * edit, off_t * start_mark, off_t * end_mark)
1266 {
1267     long end_mark_curs;
1268 
1269     if (edit->mark1 == edit->mark2)
1270     {
1271         *start_mark = *end_mark = 0;
1272         edit->column2 = edit->column1 = 0;
1273         return FALSE;
1274     }
1275 
1276     if (edit->end_mark_curs < 0)
1277         end_mark_curs = edit->buffer.curs1;
1278     else
1279         end_mark_curs = edit->end_mark_curs;
1280 
1281     if (edit->mark2 >= 0)
1282     {
1283         *start_mark = MIN (edit->mark1, edit->mark2);
1284         *end_mark = MAX (edit->mark1, edit->mark2);
1285     }
1286     else
1287     {
1288         *start_mark = MIN (edit->mark1, end_mark_curs);
1289         *end_mark = MAX (edit->mark1, end_mark_curs);
1290         edit->column2 = edit->curs_col + edit->over_col;
1291     }
1292 
1293     if (edit->column_highlight
1294         && ((edit->mark1 > end_mark_curs && edit->column1 < edit->column2)
1295             || (edit->mark1 < end_mark_curs && edit->column1 > edit->column2)))
1296     {
1297         off_t start_bol, start_eol;
1298         off_t end_bol, end_eol;
1299         long col1, col2;
1300         off_t diff1, diff2;
1301 
1302         start_bol = edit_buffer_get_bol (&edit->buffer, *start_mark);
1303         start_eol = edit_buffer_get_eol (&edit->buffer, start_bol - 1) + 1;
1304         end_bol = edit_buffer_get_bol (&edit->buffer, *end_mark);
1305         end_eol = edit_buffer_get_eol (&edit->buffer, *end_mark);
1306         col1 = MIN (edit->column1, edit->column2);
1307         col2 = MAX (edit->column1, edit->column2);
1308 
1309         diff1 = edit_move_forward3 (edit, start_bol, col2, 0) -
1310             edit_move_forward3 (edit, start_bol, col1, 0);
1311         diff2 = edit_move_forward3 (edit, end_bol, col2, 0) -
1312             edit_move_forward3 (edit, end_bol, col1, 0);
1313 
1314         *start_mark -= diff1;
1315         *end_mark += diff2;
1316         *start_mark = MAX (*start_mark, start_eol);
1317         *end_mark = MIN (*end_mark, end_eol);
1318     }
1319 
1320     return TRUE;
1321 }
1322 
1323 /* --------------------------------------------------------------------------------------------- */
1324 
1325 void
edit_block_copy_cmd(WEdit * edit)1326 edit_block_copy_cmd (WEdit * edit)
1327 {
1328     off_t start_mark, end_mark, current = edit->buffer.curs1;
1329     off_t mark1 = 0, mark2 = 0;
1330     long c1 = 0, c2 = 0;
1331     off_t size;
1332     unsigned char *copy_buf;
1333 
1334     edit_update_curs_col (edit);
1335     if (!eval_marks (edit, &start_mark, &end_mark))
1336         return;
1337 
1338     copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
1339 
1340     /* all that gets pushed are deletes hence little space is used on the stack */
1341 
1342     edit_push_markers (edit);
1343 
1344     if (edit->column_highlight)
1345     {
1346         long col_delta;
1347 
1348         col_delta = labs (edit->column2 - edit->column1);
1349         edit_insert_column_of_text (edit, copy_buf, size, col_delta, &mark1, &mark2, &c1, &c2);
1350     }
1351     else
1352     {
1353         int size_orig = size;
1354 
1355         while (size-- != 0)
1356             edit_insert_ahead (edit, copy_buf[size]);
1357 
1358         /* Place cursor at the end of text selection */
1359         if (option_cursor_after_inserted_block)
1360             edit_cursor_move (edit, size_orig);
1361     }
1362 
1363     g_free (copy_buf);
1364     edit_scroll_screen_over_cursor (edit);
1365 
1366     if (edit->column_highlight)
1367         edit_set_markers (edit, edit->buffer.curs1, mark2, c1, c2);
1368     else if (start_mark < current && end_mark > current)
1369         edit_set_markers (edit, start_mark, end_mark + end_mark - start_mark, 0, 0);
1370 
1371     edit->force |= REDRAW_PAGE;
1372 }
1373 
1374 
1375 /* --------------------------------------------------------------------------------------------- */
1376 
1377 void
edit_block_move_cmd(WEdit * edit)1378 edit_block_move_cmd (WEdit * edit)
1379 {
1380     off_t current;
1381     unsigned char *copy_buf = NULL;
1382     off_t start_mark, end_mark;
1383 
1384     if (!eval_marks (edit, &start_mark, &end_mark))
1385         return;
1386 
1387     if (!edit->column_highlight && edit->buffer.curs1 > start_mark && edit->buffer.curs1 < end_mark)
1388         return;
1389 
1390     if (edit->mark2 < 0)
1391         edit_mark_cmd (edit, FALSE);
1392     edit_push_markers (edit);
1393 
1394     if (edit->column_highlight)
1395     {
1396         off_t mark1, mark2;
1397         off_t size;
1398         long c1, c2, b_width;
1399         long x, x2;
1400 
1401         c1 = MIN (edit->column1, edit->column2);
1402         c2 = MAX (edit->column1, edit->column2);
1403         b_width = c2 - c1;
1404 
1405         edit_update_curs_col (edit);
1406 
1407         x = edit->curs_col;
1408         x2 = x + edit->over_col;
1409 
1410         /* do nothing when cursor inside first line of selected area */
1411         if ((edit_buffer_get_eol (&edit->buffer, edit->buffer.curs1) ==
1412              edit_buffer_get_eol (&edit->buffer, start_mark)) && x2 > c1 && x2 <= c2)
1413             return;
1414 
1415         if (edit->buffer.curs1 > start_mark
1416             && edit->buffer.curs1 < edit_buffer_get_eol (&edit->buffer, end_mark))
1417         {
1418             if (x > c2)
1419                 x -= b_width;
1420             else if (x > c1 && x <= c2)
1421                 x = c1;
1422         }
1423         /* save current selection into buffer */
1424         copy_buf = edit_get_block (edit, start_mark, end_mark, &size);
1425 
1426         /* remove current selection */
1427         edit_block_delete_cmd (edit);
1428 
1429         edit->over_col = MAX (0, edit->over_col - b_width);
1430         /* calculate the cursor pos after delete block */
1431         current = edit_move_forward3 (edit, edit_buffer_get_current_bol (&edit->buffer), x, 0);
1432         edit_cursor_move (edit, current - edit->buffer.curs1);
1433         edit_scroll_screen_over_cursor (edit);
1434 
1435         /* add TWS if need before block insertion */
1436         if (option_cursor_beyond_eol && edit->over_col > 0)
1437             edit_insert_over (edit);
1438 
1439         edit_insert_column_of_text (edit, copy_buf, size, b_width, &mark1, &mark2, &c1, &c2);
1440         edit_set_markers (edit, mark1, mark2, c1, c2);
1441     }
1442     else
1443     {
1444         off_t count, count_orig;
1445 
1446         current = edit->buffer.curs1;
1447         copy_buf = g_malloc0 (end_mark - start_mark);
1448         edit_cursor_move (edit, start_mark - edit->buffer.curs1);
1449         edit_scroll_screen_over_cursor (edit);
1450 
1451         for (count = start_mark; count < end_mark; count++)
1452             copy_buf[end_mark - count - 1] = edit_delete (edit, TRUE);
1453 
1454         edit_scroll_screen_over_cursor (edit);
1455         edit_cursor_move (edit,
1456                           current - edit->buffer.curs1 -
1457                           (((current - edit->buffer.curs1) > 0) ? end_mark - start_mark : 0));
1458         edit_scroll_screen_over_cursor (edit);
1459         count_orig = count;
1460         while (count-- > start_mark)
1461             edit_insert_ahead (edit, copy_buf[end_mark - count - 1]);
1462 
1463         edit_set_markers (edit, edit->buffer.curs1, edit->buffer.curs1 + end_mark - start_mark, 0,
1464                           0);
1465 
1466         /* Place cursor at the end of text selection */
1467         if (option_cursor_after_inserted_block)
1468             edit_cursor_move (edit, count_orig - start_mark);
1469     }
1470 
1471     edit_scroll_screen_over_cursor (edit);
1472     g_free (copy_buf);
1473     edit->force |= REDRAW_PAGE;
1474 }
1475 
1476 /* --------------------------------------------------------------------------------------------- */
1477 /** returns 1 if canceelled by user */
1478 
1479 int
edit_block_delete_cmd(WEdit * edit)1480 edit_block_delete_cmd (WEdit * edit)
1481 {
1482     off_t start_mark, end_mark;
1483 
1484     if (eval_marks (edit, &start_mark, &end_mark))
1485         return edit_block_delete (edit);
1486 
1487     edit_delete_line (edit);
1488 
1489     return 0;
1490 }
1491 
1492 /* --------------------------------------------------------------------------------------------- */
1493 /**
1494   * Check if it's OK to close the file. If there are unsaved changes, ask user.
1495   *
1496   * @return TRUE if it's OK to exit, FALSE to continue editing.
1497   */
1498 
1499 gboolean
edit_ok_to_exit(WEdit * edit)1500 edit_ok_to_exit (WEdit * edit)
1501 {
1502     const char *fname = N_("[NoName]");
1503     char *msg;
1504     int act;
1505 
1506     if (!edit->modified)
1507         return TRUE;
1508 
1509     if (edit->filename_vpath != NULL)
1510         fname = vfs_path_as_str (edit->filename_vpath);
1511 #ifdef ENABLE_NLS
1512     else
1513         fname = _(fname);
1514 #endif
1515 
1516     if (!mc_global.midnight_shutdown)
1517     {
1518         query_set_sel (2);
1519 
1520         msg = g_strdup_printf (_("File %s was modified.\nSave before close?"), fname);
1521         act = edit_query_dialog3 (_("Close file"), msg, _("&Yes"), _("&No"), _("&Cancel"));
1522     }
1523     else
1524     {
1525         msg = g_strdup_printf (_("Midnight Commander is being shut down.\nSave modified file %s?"),
1526                                fname);
1527         act = edit_query_dialog2 (_("Quit"), msg, _("&Yes"), _("&No"));
1528 
1529         /* Esc is No */
1530         if (act == -1)
1531             act = 1;
1532     }
1533 
1534     g_free (msg);
1535 
1536     switch (act)
1537     {
1538     case 0:                    /* Yes */
1539         if (!mc_global.midnight_shutdown && !edit_check_newline (&edit->buffer))
1540             return FALSE;
1541         edit_push_markers (edit);
1542         edit_set_markers (edit, 0, 0, 0, 0);
1543         if (!edit_save_cmd (edit) || mc_global.midnight_shutdown)
1544             return mc_global.midnight_shutdown;
1545         break;
1546     case 1:                    /* No */
1547     default:
1548         break;
1549     case 2:                    /* Cancel quit */
1550     case -1:                   /* Esc */
1551         return FALSE;
1552     }
1553 
1554     return TRUE;
1555 }
1556 
1557 /* --------------------------------------------------------------------------------------------- */
1558 /** save block, returns TRUE on success */
1559 
1560 gboolean
edit_save_block(WEdit * edit,const char * filename,off_t start,off_t finish)1561 edit_save_block (WEdit * edit, const char *filename, off_t start, off_t finish)
1562 {
1563     int file;
1564     off_t len = 1;
1565     vfs_path_t *vpath;
1566 
1567     vpath = vfs_path_from_str (filename);
1568     file = mc_open (vpath, O_CREAT | O_WRONLY | O_TRUNC,
1569                     S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH | O_BINARY);
1570     vfs_path_free (vpath, TRUE);
1571     if (file == -1)
1572         return FALSE;
1573 
1574     if (edit->column_highlight)
1575     {
1576         int r;
1577 
1578         r = mc_write (file, VERTICAL_MAGIC, sizeof (VERTICAL_MAGIC));
1579         if (r > 0)
1580         {
1581             unsigned char *block, *p;
1582 
1583             p = block = edit_get_block (edit, start, finish, &len);
1584             while (len != 0)
1585             {
1586                 r = mc_write (file, p, len);
1587                 if (r < 0)
1588                     break;
1589                 p += r;
1590                 len -= r;
1591             }
1592             g_free (block);
1593         }
1594     }
1595     else
1596     {
1597         unsigned char *buf;
1598         off_t i = start;
1599 
1600         len = finish - start;
1601         buf = g_malloc0 (TEMP_BUF_LEN);
1602         while (start != finish)
1603         {
1604             off_t end;
1605 
1606             end = MIN (finish, start + TEMP_BUF_LEN);
1607             for (; i < end; i++)
1608                 buf[i - start] = edit_buffer_get_byte (&edit->buffer, i);
1609             len -= mc_write (file, (char *) buf, end - start);
1610             start = end;
1611         }
1612         g_free (buf);
1613     }
1614     mc_close (file);
1615 
1616     return (len == 0);
1617 }
1618 
1619 /* --------------------------------------------------------------------------------------------- */
1620 
1621 void
edit_paste_from_history(WEdit * edit)1622 edit_paste_from_history (WEdit * edit)
1623 {
1624     (void) edit;
1625     edit_error_dialog (_("Error"), _("This function is not implemented"));
1626 }
1627 
1628 /* --------------------------------------------------------------------------------------------- */
1629 
1630 gboolean
edit_copy_to_X_buf_cmd(WEdit * edit)1631 edit_copy_to_X_buf_cmd (WEdit * edit)
1632 {
1633     off_t start_mark, end_mark;
1634 
1635     if (!eval_marks (edit, &start_mark, &end_mark))
1636         return TRUE;
1637 
1638     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1639     {
1640         edit_error_dialog (_("Copy to clipboard"), get_sys_error (_("Unable to save to file")));
1641         return FALSE;
1642     }
1643     /* try use external clipboard utility */
1644     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1645 
1646     if (option_drop_selection_on_copy)
1647         edit_mark_cmd (edit, TRUE);
1648 
1649     return TRUE;
1650 }
1651 
1652 /* --------------------------------------------------------------------------------------------- */
1653 
1654 gboolean
edit_cut_to_X_buf_cmd(WEdit * edit)1655 edit_cut_to_X_buf_cmd (WEdit * edit)
1656 {
1657     off_t start_mark, end_mark;
1658 
1659     if (!eval_marks (edit, &start_mark, &end_mark))
1660         return TRUE;
1661 
1662     if (!edit_save_block_to_clip_file (edit, start_mark, end_mark))
1663     {
1664         edit_error_dialog (_("Cut to clipboard"), _("Unable to save to file"));
1665         return FALSE;
1666     }
1667     /* try use external clipboard utility */
1668     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_to_ext_clip", NULL);
1669 
1670     edit_block_delete_cmd (edit);
1671     edit_mark_cmd (edit, TRUE);
1672 
1673     return TRUE;
1674 }
1675 
1676 /* --------------------------------------------------------------------------------------------- */
1677 
1678 gboolean
edit_paste_from_X_buf_cmd(WEdit * edit)1679 edit_paste_from_X_buf_cmd (WEdit * edit)
1680 {
1681     vfs_path_t *tmp;
1682     gboolean ret;
1683 
1684     /* try use external clipboard utility */
1685     mc_event_raise (MCEVENT_GROUP_CORE, "clipboard_file_from_ext_clip", NULL);
1686     tmp = mc_config_get_full_vpath (EDIT_HOME_CLIP_FILE);
1687     ret = (edit_insert_file (edit, tmp) >= 0);
1688     vfs_path_free (tmp, TRUE);
1689 
1690     return ret;
1691 }
1692 
1693 /* --------------------------------------------------------------------------------------------- */
1694 /**
1695  * Ask user for the line and go to that line.
1696  * Negative numbers mean line from the end (i.e. -1 is the last line).
1697  */
1698 
1699 void
edit_goto_cmd(WEdit * edit)1700 edit_goto_cmd (WEdit * edit)
1701 {
1702     static gboolean first_run = TRUE;
1703 
1704     char *f;
1705     long l;
1706     char *error;
1707 
1708     f = input_dialog (_("Goto line"), _("Enter line:"), MC_HISTORY_EDIT_GOTO_LINE,
1709                       first_run ? NULL : INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1710     if (f == NULL || *f == '\0')
1711     {
1712         g_free (f);
1713         return;
1714     }
1715 
1716     l = strtol (f, &error, 0);
1717     if (*error != '\0')
1718     {
1719         g_free (f);
1720         return;
1721     }
1722 
1723     if (l < 0)
1724         l = edit->buffer.lines + l + 2;
1725 
1726     edit_move_display (edit, l - WIDGET (edit)->lines / 2 - 1);
1727     edit_move_to_line (edit, l - 1);
1728     edit->force |= REDRAW_COMPLETELY;
1729 
1730     g_free (f);
1731     first_run = FALSE;
1732 }
1733 
1734 /* --------------------------------------------------------------------------------------------- */
1735 /** Return TRUE on success */
1736 
1737 gboolean
edit_save_block_cmd(WEdit * edit)1738 edit_save_block_cmd (WEdit * edit)
1739 {
1740     off_t start_mark, end_mark;
1741     char *exp, *tmp;
1742     gboolean ret = FALSE;
1743 
1744     if (!eval_marks (edit, &start_mark, &end_mark))
1745         return TRUE;
1746 
1747     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1748     exp =
1749         input_expand_dialog (_("Save block"), _("Enter file name:"),
1750                              MC_HISTORY_EDIT_SAVE_BLOCK, tmp, INPUT_COMPLETE_FILENAMES);
1751     g_free (tmp);
1752     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1753 
1754     if (exp != NULL && *exp != '\0')
1755     {
1756         if (edit_save_block (edit, exp, start_mark, end_mark))
1757             ret = TRUE;
1758         else
1759             edit_error_dialog (_("Save block"), get_sys_error (_("Cannot save file")));
1760 
1761         edit->force |= REDRAW_COMPLETELY;
1762     }
1763 
1764     g_free (exp);
1765 
1766     return ret;
1767 }
1768 
1769 /* --------------------------------------------------------------------------------------------- */
1770 
1771 /** returns TRUE on success */
1772 gboolean
edit_insert_file_cmd(WEdit * edit)1773 edit_insert_file_cmd (WEdit * edit)
1774 {
1775     char *tmp;
1776     char *exp;
1777     gboolean ret = FALSE;
1778 
1779     tmp = mc_config_get_full_path (EDIT_HOME_CLIP_FILE);
1780     exp = input_expand_dialog (_("Insert file"), _("Enter file name:"),
1781                                MC_HISTORY_EDIT_INSERT_FILE, tmp, INPUT_COMPLETE_FILENAMES);
1782     g_free (tmp);
1783 
1784     edit_push_undo_action (edit, KEY_PRESS + edit->start_display);
1785 
1786     if (exp != NULL && *exp != '\0')
1787     {
1788         vfs_path_t *exp_vpath;
1789 
1790         exp_vpath = vfs_path_from_str (exp);
1791         ret = (edit_insert_file (edit, exp_vpath) >= 0);
1792         vfs_path_free (exp_vpath, TRUE);
1793 
1794         if (!ret)
1795             edit_error_dialog (_("Insert file"), get_sys_error (_("Cannot insert file")));
1796     }
1797 
1798     g_free (exp);
1799 
1800     edit->force |= REDRAW_COMPLETELY;
1801     return ret;
1802 }
1803 
1804 /* --------------------------------------------------------------------------------------------- */
1805 /** sorts a block, returns -1 on system fail, 1 on cancel and 0 on success */
1806 
1807 int
edit_sort_cmd(WEdit * edit)1808 edit_sort_cmd (WEdit * edit)
1809 {
1810     char *exp, *tmp, *tmp_edit_block_name, *tmp_edit_temp_name;
1811     off_t start_mark, end_mark;
1812     int e;
1813 
1814     if (!eval_marks (edit, &start_mark, &end_mark))
1815     {
1816         edit_error_dialog (_("Sort block"), _("You must first highlight a block of text"));
1817         return 0;
1818     }
1819 
1820     tmp = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1821     edit_save_block (edit, tmp, start_mark, end_mark);
1822     g_free (tmp);
1823 
1824     exp = input_dialog (_("Run sort"),
1825                         _("Enter sort options (see manpage) separated by whitespace:"),
1826                         MC_HISTORY_EDIT_SORT, INPUT_LAST_TEXT, INPUT_COMPLETE_NONE);
1827 
1828     if (exp == NULL)
1829         return 1;
1830 
1831     tmp_edit_block_name = mc_config_get_full_path (EDIT_HOME_BLOCK_FILE);
1832     tmp_edit_temp_name = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1833     tmp =
1834         g_strconcat (" sort ", exp, " ", tmp_edit_block_name,
1835                      " > ", tmp_edit_temp_name, (char *) NULL);
1836     g_free (tmp_edit_temp_name);
1837     g_free (tmp_edit_block_name);
1838     g_free (exp);
1839 
1840     e = system (tmp);
1841     g_free (tmp);
1842     if (e != 0)
1843     {
1844         if (e == -1 || e == 127)
1845             edit_error_dialog (_("Sort"), get_sys_error (_("Cannot execute sort command")));
1846         else
1847         {
1848             char q[8];
1849 
1850             sprintf (q, "%d ", e);
1851             tmp = g_strdup_printf (_("Sort returned non-zero: %s"), q);
1852             edit_error_dialog (_("Sort"), tmp);
1853             g_free (tmp);
1854         }
1855 
1856         return -1;
1857     }
1858 
1859     edit->force |= REDRAW_COMPLETELY;
1860 
1861     if (edit_block_delete_cmd (edit))
1862         return 1;
1863 
1864     {
1865         vfs_path_t *tmp_vpath;
1866 
1867         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1868         edit_insert_file (edit, tmp_vpath);
1869         vfs_path_free (tmp_vpath, TRUE);
1870     }
1871 
1872     return 0;
1873 }
1874 
1875 /* --------------------------------------------------------------------------------------------- */
1876 /**
1877  * Ask user for a command, execute it and paste its output back to the
1878  * editor.
1879  */
1880 
1881 int
edit_ext_cmd(WEdit * edit)1882 edit_ext_cmd (WEdit * edit)
1883 {
1884     char *exp, *tmp, *tmp_edit_temp_file;
1885     int e;
1886 
1887     exp =
1888         input_dialog (_("Paste output of external command"),
1889                       _("Enter shell command(s):"), MC_HISTORY_EDIT_PASTE_EXTCMD, INPUT_LAST_TEXT,
1890                       INPUT_COMPLETE_FILENAMES | INPUT_COMPLETE_VARIABLES | INPUT_COMPLETE_USERNAMES
1891                       | INPUT_COMPLETE_HOSTNAMES | INPUT_COMPLETE_CD | INPUT_COMPLETE_COMMANDS |
1892                       INPUT_COMPLETE_SHELL_ESC);
1893 
1894     if (!exp)
1895         return 1;
1896 
1897     tmp_edit_temp_file = mc_config_get_full_path (EDIT_HOME_TEMP_FILE);
1898     tmp = g_strconcat (exp, " > ", tmp_edit_temp_file, (char *) NULL);
1899     g_free (tmp_edit_temp_file);
1900     e = system (tmp);
1901     g_free (tmp);
1902     g_free (exp);
1903 
1904     if (e != 0)
1905     {
1906         edit_error_dialog (_("External command"), get_sys_error (_("Cannot execute command")));
1907         return -1;
1908     }
1909 
1910     edit->force |= REDRAW_COMPLETELY;
1911 
1912     {
1913         vfs_path_t *tmp_vpath;
1914 
1915         tmp_vpath = mc_config_get_full_vpath (EDIT_HOME_TEMP_FILE);
1916         edit_insert_file (edit, tmp_vpath);
1917         vfs_path_free (tmp_vpath, TRUE);
1918     }
1919 
1920     return 0;
1921 }
1922 
1923 /* --------------------------------------------------------------------------------------------- */
1924 /** if block is 1, a block must be highlighted and the shell command
1925    processes it. If block is 0 the shell command is a straight system
1926    command, that just produces some output which is to be inserted */
1927 
1928 void
edit_block_process_cmd(WEdit * edit,int macro_number)1929 edit_block_process_cmd (WEdit * edit, int macro_number)
1930 {
1931     char *fname;
1932     char *macros_fname = NULL;
1933 
1934     fname = g_strdup_printf ("%s.%i.sh", EDIT_HOME_MACRO_FILE, macro_number);
1935     macros_fname = g_build_filename (mc_config_get_data_path (), fname, (char *) NULL);
1936     user_menu (edit, macros_fname, 0);
1937     g_free (fname);
1938     g_free (macros_fname);
1939     edit->force |= REDRAW_COMPLETELY;
1940 }
1941 
1942 /* --------------------------------------------------------------------------------------------- */
1943 
1944 void
edit_mail_dialog(WEdit * edit)1945 edit_mail_dialog (WEdit * edit)
1946 {
1947     char *mail_to, *mail_subject, *mail_cc;
1948 
1949     quick_widget_t quick_widgets[] = {
1950         /* *INDENT-OFF* */
1951         QUICK_LABEL (N_("mail -s <subject> -c <cc> <to>"), NULL),
1952         QUICK_LABELED_INPUT (N_("To"), input_label_above,
1953                              INPUT_LAST_TEXT, "mail-dlg-input-3",
1954                              &mail_to, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1955         QUICK_LABELED_INPUT (N_("Subject"), input_label_above,
1956                               INPUT_LAST_TEXT, "mail-dlg-input-2",
1957                               &mail_subject, NULL, FALSE, FALSE, INPUT_COMPLETE_NONE),
1958         QUICK_LABELED_INPUT (N_("Copies to"), input_label_above,
1959                              INPUT_LAST_TEXT, "mail-dlg-input",
1960                              &mail_cc, NULL, FALSE, FALSE, INPUT_COMPLETE_USERNAMES),
1961         QUICK_BUTTONS_OK_CANCEL,
1962         QUICK_END
1963         /* *INDENT-ON* */
1964     };
1965 
1966     quick_dialog_t qdlg = {
1967         -1, -1, 50,
1968         N_("Mail"), "[Input Line Keys]",
1969         quick_widgets, NULL, NULL
1970     };
1971 
1972     if (quick_dialog (&qdlg) != B_CANCEL)
1973     {
1974         pipe_mail (&edit->buffer, mail_to, mail_subject, mail_cc);
1975         g_free (mail_to);
1976         g_free (mail_subject);
1977         g_free (mail_cc);
1978     }
1979 }
1980 
1981 /* --------------------------------------------------------------------------------------------- */
1982 
1983 #ifdef HAVE_CHARSET
1984 void
edit_select_codepage_cmd(WEdit * edit)1985 edit_select_codepage_cmd (WEdit * edit)
1986 {
1987     if (do_select_codepage ())
1988         edit_set_codeset (edit);
1989 
1990     edit->force = REDRAW_PAGE;
1991     widget_draw (WIDGET (edit));
1992 }
1993 #endif
1994 
1995 /* --------------------------------------------------------------------------------------------- */
1996 
1997 void
edit_insert_literal_cmd(WEdit * edit)1998 edit_insert_literal_cmd (WEdit * edit)
1999 {
2000     int char_for_insertion;
2001 
2002     char_for_insertion = editcmd_dialog_raw_key_query (_("Insert literal"),
2003                                                        _("Press any key:"), FALSE);
2004     edit_execute_key_command (edit, -1, ascii_alpha_to_cntrl (char_for_insertion));
2005 }
2006 
2007 /* --------------------------------------------------------------------------------------------- */
2008 
2009 gboolean
edit_load_forward_cmd(WEdit * edit)2010 edit_load_forward_cmd (WEdit * edit)
2011 {
2012     if (edit->modified
2013         && edit_query_dialog2 (_("Warning"),
2014                                _("Current text was modified without a file save.\n"
2015                                  "Continue discards these changes."), _("C&ontinue"),
2016                                _("&Cancel")) == 1)
2017     {
2018         edit->force |= REDRAW_COMPLETELY;
2019         return TRUE;
2020     }
2021 
2022     if (edit_stack_iterator + 1 >= MAX_HISTORY_MOVETO)
2023         return FALSE;
2024 
2025     if (edit_history_moveto[edit_stack_iterator + 1].line < 1)
2026         return FALSE;
2027 
2028     edit_stack_iterator++;
2029     if (edit_history_moveto[edit_stack_iterator].filename_vpath != NULL)
2030         return edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath,
2031                                  edit_history_moveto[edit_stack_iterator].line);
2032 
2033     return FALSE;
2034 }
2035 
2036 /* --------------------------------------------------------------------------------------------- */
2037 
2038 gboolean
edit_load_back_cmd(WEdit * edit)2039 edit_load_back_cmd (WEdit * edit)
2040 {
2041     if (edit->modified
2042         && edit_query_dialog2 (_("Warning"),
2043                                _("Current text was modified without a file save.\n"
2044                                  "Continue discards these changes."), _("C&ontinue"),
2045                                _("&Cancel")) == 1)
2046     {
2047         edit->force |= REDRAW_COMPLETELY;
2048         return TRUE;
2049     }
2050 
2051     /* we are in the bottom of the stack, NO WAY! */
2052     if (edit_stack_iterator == 0)
2053         return FALSE;
2054 
2055     edit_stack_iterator--;
2056     if (edit_history_moveto[edit_stack_iterator].filename_vpath != NULL)
2057         return edit_reload_line (edit, edit_history_moveto[edit_stack_iterator].filename_vpath,
2058                                  edit_history_moveto[edit_stack_iterator].line);
2059 
2060     return FALSE;
2061 }
2062 
2063 /* --------------------------------------------------------------------------------------------- */
2064 
2065 /* gets a raw key from the keyboard. Passing cancel = 1 draws
2066    a cancel button thus allowing c-c etc.  Alternatively, cancel = 0
2067    will return the next key pressed.  ctrl-a (=B_CANCEL), ctrl-g, ctrl-c,
2068    and Esc are cannot returned */
2069 
2070 int
editcmd_dialog_raw_key_query(const char * heading,const char * query,gboolean cancel)2071 editcmd_dialog_raw_key_query (const char *heading, const char *query, gboolean cancel)
2072 {
2073     int w, wq;
2074     int y = 2;
2075     WDialog *raw_dlg;
2076     WGroup *g;
2077 
2078     w = str_term_width1 (heading) + 6;
2079     wq = str_term_width1 (query);
2080     w = MAX (w, wq + 3 * 2 + 1 + 2);
2081 
2082     raw_dlg =
2083         dlg_create (TRUE, 0, 0, cancel ? 7 : 5, w, WPOS_CENTER | WPOS_TRYUP, FALSE, dialog_colors,
2084                     editcmd_dialog_raw_key_query_cb, NULL, NULL, heading);
2085     g = GROUP (raw_dlg);
2086     widget_want_tab (WIDGET (raw_dlg), TRUE);
2087 
2088     group_add_widget (g, label_new (y, 3, query));
2089     group_add_widget (g,
2090                       input_new (y++, 3 + wq + 1, input_colors, w - (6 + wq + 1), "", 0,
2091                                  INPUT_COMPLETE_NONE));
2092     if (cancel)
2093     {
2094         group_add_widget (g, hline_new (y++, -1, -1));
2095         /* Button w/o hotkey to allow use any key as raw or macro one */
2096         group_add_widget_autopos (g, button_new (y, 1, B_CANCEL, NORMAL_BUTTON, _("Cancel"), NULL),
2097                                   WPOS_KEEP_TOP | WPOS_CENTER_HORZ, NULL);
2098     }
2099 
2100     w = dlg_run (raw_dlg);
2101     widget_destroy (WIDGET (raw_dlg));
2102 
2103     return (cancel && (w == ESC_CHAR || w == B_CANCEL)) ? 0 : w;
2104 }
2105 
2106 /* --------------------------------------------------------------------------------------------- */
2107