Introduction
Introduction Statistics Contact Development Disclaimer Help
ledit.c - ledit - Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/ledit.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/…
Log
Files
Refs
README
LICENSE
---
ledit.c (25669B)
---
1 /* FIXME: generally optimize redrawing */
2 /* FIXME: Just use int for everything? size_t just doesn't seem to be wo…
3 /* FIXME: Make scrolling more smooth */
4 /* FIXME: Only redraw part of screen if needed */
5 /* FIXME: overflow in repeated commands */
6 /* FIXME: Use PANGO_PIXELS() */
7 /* FIXME: Fix cursor movement, especially buffer->trailing and writing a…
8 /* FIXME: horizontal scrolling (also need cache to avoid too large pixma…
9 /* TODO: allow extending selection with shift+mouse like in e.g. gtk */
10
11 #include <pwd.h>
12 #include <time.h>
13 #if TEST
14 #include <fcntl.h>
15 #endif
16 #include <errno.h>
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <string.h>
20 #include <locale.h>
21 #include <unistd.h>
22 #include <sys/stat.h>
23
24 #include <X11/Xlib.h>
25 #include <X11/XKBlib.h>
26 #include <X11/extensions/Xdbe.h>
27 #include <X11/extensions/XKBrules.h>
28
29 #include "util.h"
30 #include "view.h"
31 #include "buffer.h"
32 #include "common.h"
33 #include "window.h"
34 #include "search.h"
35 #include "macros.h"
36 #include "memory.h"
37 #include "config.h"
38 #include "cleanup.h"
39 #include "keys.h"
40 #include "keys_basic.h"
41 #include "keys_command.h"
42 #include "configparser.h"
43
44 static void mainloop(void);
45 static void setup(int argc, char *argv[]);
46 static void redraw(void);
47
48 static void change_keyboard(char *lang);
49 static void key_press_event(ledit_view *view, XEvent *event);
50 static void key_press(ledit_view *view, unsigned int key_state, KeySym s…
51
52 ledit_common common;
53 ledit_clipboard *clipboard = NULL;
54 ledit_buffer *buffer = NULL;
55 size_t cur_lang = 0;
56
57 #if TEST
58 static struct {
59 char *read; /* text read from stdin */
60 size_t read_len; /* length of text in read buffer */
61 size_t read_alloc; /* size of read buffer */
62 size_t line_start; /* start of current line */
63 size_t read_cur; /* length of text already read */
64 } test_status = {NULL, 0, 0, 0, 0};
65
66 #define READ_BLK_SIZE 128
67
68 /* Read up to READ_BLK_SIZE bytes from stdin.
69 Returns 1 if an error occurred, -1 if not new data available, 0 other…
70 static int
71 read_input(void) {
72 if (test_status.read_cur > 0) {
73 memmove(test_status.read, test_status.read + test_status…
74 test_status.read_len -= test_status.read_cur;
75 test_status.read_cur = 0;
76 }
77 int nread;
78 test_status.read_alloc = ideal_array_size(test_status.read_alloc…
79 test_status.read = ledit_realloc(test_status.read, test_status.r…
80 nread = read(fileno(stdin), test_status.read + test_status.read_…
81 if (nread == -1 && errno == EAGAIN)
82 return -1;
83 else if (nread == -1 || nread == 0)
84 return 1;
85 test_status.read_len += nread;
86
87 return 0;
88 }
89
90 /* based partially on OpenBSD's strtonum */
91 static int
92 read_rangeint(long long *ret, int end, long long min, long long max) {
93 if (test_status.read_cur >= test_status.read_len || test_status.…
94 return 1;
95 char end_char = end ? '\n' : ' ';
96 size_t len = 0;
97 test_status.read_cur++;
98 char *str = test_status.read + test_status.read_cur;
99 int found = 0;
100 for (; test_status.read_cur < test_status.read_len; test_status.…
101 if (test_status.read[test_status.read_cur] == end_char) {
102 found = 1;
103 break;
104 }
105 len++;
106 }
107 if (!found || len == 0)
108 return 1;
109 /* the string needs to be nul-terminated
110 if it contains more than 11 characters (10 digits + sign),
111 it's illegal anyways (at least for these testing purposes...)…
112 if (len > 11)
113 return 1;
114 char nstr[12];
115 strncpy(nstr, str, len);
116 nstr[len] = '\0';
117 char *num_end;
118 long long ll = strtoll(nstr, &num_end, 10);
119 if (nstr == num_end || *num_end != '\0' ||
120 ll < min || ll > max || ((ll == LLONG_MIN ||
121 ll == LLONG_MAX) && errno == ERANGE)) {
122 return 1;
123 }
124 *ret = ll;
125 if (end)
126 test_status.read_cur++;
127 return 0;
128 }
129
130 static int
131 read_uint(unsigned int *ret, int end) {
132 long long l;
133 int err = read_rangeint(&l, end, 0, UINT_MAX);
134 *ret = (unsigned int)l;
135 return err;
136 }
137
138 static int
139 read_int(int *ret, int end) {
140 long long l;
141 int err = read_rangeint(&l, end, INT_MIN, INT_MAX);
142 *ret = (int)l;
143 return err;
144 }
145
146 static int
147 read_text(char **text, size_t *text_len) {
148 if (test_status.read_cur >= test_status.read_len || test_status.…
149 return 1;
150 int bs = 0;
151 int offset = 0;
152 test_status.read_cur++;
153 size_t start = test_status.read_cur;
154 *text = test_status.read + test_status.read_cur;
155 int found = 0;
156 for (; test_status.read_cur < test_status.read_len; test_status.…
157 if (test_status.read[test_status.read_cur] == '\\') {
158 bs++;
159 if (bs / 2)
160 offset++;
161 bs %= 2;
162 test_status.read[test_status.read_cur - offset] …
163 } else if (test_status.read[test_status.read_cur] == '\n…
164 if (!bs) {
165 found = 1;
166 break;
167 } else {
168 bs = 0;
169 offset++;
170 test_status.read[test_status.read_cur - …
171 }
172 } else {
173 test_status.read[test_status.read_cur - offset] …
174 bs = 0;
175 }
176 }
177 if (!found)
178 return 1;
179 *text_len = test_status.read_cur - start - offset;
180 test_status.read_cur++;
181 return 0;
182 }
183
184 static int
185 read_filename(char **text, size_t *text_len) {
186 if (read_text(text, text_len))
187 return 1;
188 for (size_t i = 0; i < *text_len; i++) {
189 if ((*text)[i] == '/' || (*text)[i] == '\0')
190 return 1;
191 }
192 return 0;
193 }
194
195 static unsigned int view_num = 0;
196 /* Process commands in test_status.
197 Returns 0 if no complete commands are contained in read buffer, 1 oth…
198 static int
199 process_commands(void) {
200 int bs = 0;
201 int found = 0;
202 size_t nl_index = 0;
203 for (size_t i = test_status.read_cur; i < test_status.read_len; …
204 if (test_status.read[i] == '\\') {
205 bs++;
206 bs %= 2;
207 } else if (test_status.read[i] == '\n' && bs == 0) {
208 found = 1;
209 nl_index = i;
210 break;
211 } else {
212 bs = 0;
213 }
214 }
215 if (!found)
216 return 0;
217 unsigned int key_state, button_num, keysym, new_view;
218 char *text, *term, *errstr;
219 size_t text_len;
220 int x, y;
221 XEvent e;
222 FILE *file;
223 test_status.read_cur += 1;
224 ledit_view *view = buffer->views[view_num];
225 switch (test_status.read[test_status.read_cur-1]) {
226 case 'k':
227 /* key press */
228 /* k key_state keysym text */
229 if (read_uint(&key_state, 0))
230 goto error;
231 if (read_uint(&keysym, 0))
232 goto error;
233 if (read_text(&text, &text_len))
234 goto error;
235 key_press(view, key_state, keysym, text, (int)te…
236 break;
237 case 'p':
238 /* mouse button press */
239 /* p button_num x y */
240 if (read_uint(&button_num, 0))
241 goto error;
242 if (read_int(&x, 0))
243 goto error;
244 if (read_int(&y, 1))
245 goto error;
246 e = (XEvent){.xbutton = {.type = ButtonPress, .b…
247 window_register_button_press(view->window, &e);
248 break;
249 case 'r':
250 /* mouse button release */
251 /* r button_num x y */
252 if (read_uint(&button_num, 0))
253 goto error;
254 if (read_int(&x, 0))
255 goto error;
256 if (read_int(&y, 1))
257 goto error;
258 e = (XEvent){.xbutton = {.type = ButtonRelease, …
259 window_button_release(view->window, &e);
260 break;
261 case 'm':
262 /* mouse motion */
263 /* m x y */
264 if (read_int(&x, 0))
265 goto error;
266 if (read_int(&y, 1))
267 goto error;
268 e = (XEvent){.xmotion = {.type = MotionNotify, .…
269 window_register_motion(view->window, &e);
270 break;
271 case 'l':
272 /* language switch */
273 /* l lang_name */
274 if (read_text(&text, &text_len))
275 goto error;
276 term = ledit_strndup(text, text_len);
277 change_keyboard(term);
278 free(term);
279 break;
280 case 's':
281 /* switch view */
282 /* s view_num */
283 if (read_uint(&new_view, 1))
284 goto error;
285 if (new_view >= buffer->views_num)
286 fprintf(stderr, "Invalid view number %u\…
287 else
288 view_num = new_view;
289 break;
290 case 'w':
291 /* write contents of buffer */
292 /* w file_name */
293 if (read_filename(&text, &text_len))
294 goto error;
295 term = ledit_strndup(text, text_len);
296 if (buffer_write_to_filename(buffer, term, &errs…
297 fprintf(stderr, "Error writing %s: %s\n"…
298 free(term);
299 break;
300 case 'd':
301 /* dump other info to file */
302 /* d file_name */
303 if (read_filename(&text, &text_len))
304 goto error;
305 term = ledit_strndup(text, text_len);
306 file = fopen(term, "w");
307 if (!file) {
308 fprintf(stderr, "Unable to open file %s\…
309 } else {
310 fprintf(
311 file,
312 "cursor_line: %zu, cursor_byte: %zu,…
313 "sel_line1: %zu, sel_byte1: %zu, "
314 "sel_line2: %zu, sel_byte2: %zu\n",
315 view->cur_line, view->cur_index, vie…
316 view->sel.line1, view->sel.byte1,
317 view->sel.line2, view->sel.byte2
318 );
319 fclose(file);
320 }
321 free(term);
322 break;
323 case 'u':
324 /* dump undo stack to file */
325 if (read_filename(&text, &text_len))
326 goto error;
327 /* u file_name */
328 term = ledit_strndup(text, text_len);
329 file = fopen(term, "w");
330 if (!file) {
331 fprintf(stderr, "Unable to open file %s\…
332 } else {
333 dump_undo_stack(file, buffer->undo);
334 fclose(file);
335 }
336 free(term);
337 break;
338 default:
339 goto error;
340 }
341 return 1;
342 error:
343 fprintf(stderr, "Error parsing command.\n");
344 test_status.read_cur = nl_index + 1;
345 return 1;
346 }
347 #endif
348
349 /* can only be set to 1 when compiled with TEST */
350 static int test_extra = 0;
351
352 static void
353 mainloop(void) {
354 #if TEST
355 int flags = fcntl(fileno(stdin), F_GETFL, 0);
356 if (flags == -1) {
357 fprintf(stderr, "Unable to set non-blocking mode on stdi…
358 return;
359 }
360 if (fcntl(fileno(stdin), F_SETFL, flags | O_NONBLOCK)) {
361 fprintf(stderr, "Unable to set non-blocking mode on stdi…
362 return;
363 }
364 #endif
365 XEvent event;
366 int xkb_event_type;
367 int major, minor;
368 if (!XkbQueryExtension(common.dpy, 0, &xkb_event_type, NULL, &ma…
369 fprintf(stderr, "XKB not supported.");
370 ledit_cleanup();
371 exit(1);
372 }
373 /*printf("XKB (%d.%d) supported.\n", major, minor);*/
374 /* This should select the events when the keyboard mapping chang…
375 * When e.g. 'setxkbmap us' is executed, two events are sent, bu…
376 * haven't figured out how to change that. When the xkb layout
377 * switching is used (e.g. 'setxkbmap -option grp:shifts_toggle'…
378 * this issue does not occur because only a state event is sent.…
379 XkbSelectEvents(
380 common.dpy, XkbUseCoreKbd,
381 XkbNewKeyboardNotifyMask, XkbNewKeyboardNotifyMask
382 );
383 XkbSelectEventDetails(
384 common.dpy, XkbUseCoreKbd, XkbStateNotify,
385 XkbAllStateComponentsMask, XkbGroupStateMask
386 );
387 XSync(common.dpy, False);
388 int running = 1;
389 int change_kbd = 1;
390
391 redraw();
392 /* store last draw time so framerate can be limited */
393 struct timespec now, elapsed, last, sleep_time;
394 clock_gettime(CLOCK_MONOTONIC, &last);
395 sleep_time.tv_sec = 0;
396 while (running) {
397 /* This "lazy destroying" is not entirely ideal yet, but…
398 necessary to avoid a crash when closing a view (I'm n…
399 entirely sure what exactly causes the crash)
400 -> Update: The cause of the crash was something diffe…
401 but I'm still leaving it as is for now because the…
402 may be other reasons for doing it lazily. */
403 for (size_t i = 0; i < buffer->views_num; i++) {
404 if (buffer->views[i]->destroy) {
405 buffer_remove_view(buffer, buffer->views…
406 if (buffer->views_num == 0) {
407 ledit_cleanup();
408 exit(0);
409 }
410 /* only delete one - otherwise,
411 the loop would need to be
412 modified
413 I guess it's unrealistic to
414 assume that the deletion cmd
415 will be called multiple times
416 in such a short time anyways */
417 break;
418 }
419 }
420 while (XPending(common.dpy)) {
421 XNextEvent(common.dpy, &event);
422 if (event.type == xkb_event_type) {
423 change_kbd = 1;
424 continue;
425 }
426 if (clipboard_filter_event(clipboard, &event))
427 continue;
428 if (XFilterEvent(&event, None))
429 continue;
430 ledit_view *view = NULL;
431 /* FIXME: abstract view handling a bit (don't ac…
432 for (size_t i = 0; i < buffer->views_num; i++) {
433 if (buffer->views[i]->window->xwin == ev…
434 view = buffer->views[i];
435 break;
436 }
437 }
438 if (view == NULL)
439 continue; /* shouldn't happen */
440 ledit_window *window = view->window;
441 switch (event.type) {
442 case Expose:
443 view->redraw = 1;
444 break;
445 case ConfigureNotify:
446 window_register_resize(view->window, &ev…
447 break;
448 case ButtonPress:
449 if (!test_extra)
450 window_register_button_press(vie…
451 break;
452 case ButtonRelease:
453 if (!test_extra)
454 window_button_release(view->wind…
455 break;
456 case MotionNotify:
457 if (!test_extra)
458 window_register_motion(window, &…
459 break;
460 case KeyPress:
461 if (!test_extra)
462 key_press_event(view, &event);
463 break;
464 case ClientMessage:
465 if ((Atom)event.xclient.data.l[0] == vie…
466 buffer_remove_view(buffer, view);
467 if (buffer->views_num == 0)
468 running = 0;
469 }
470 break;
471 default:
472 break;
473 }
474 };
475
476 #if TEST
477 int ret;
478 if ((ret = read_input()) == 1) {
479 fprintf(stderr, "Unable to read text from stdin.…
480 } else if (ret == 0) {
481 while (process_commands()) {
482 /* NOP */
483 }
484 }
485 #endif
486
487 for (size_t i = 0; i < buffer->views_num; i++) {
488 window_handle_filtered_events(buffer->views[i]->…
489 }
490
491 if (!test_extra && change_kbd) {
492 change_kbd = 0;
493 XkbDescPtr desc = XkbGetMap(
494 common.dpy, 0, XkbUseCoreKbd
495 );
496 if (!desc || XkbGetNames(common.dpy, XkbGroupNam…
497 /* FIXME: maybe show this as error messa…
498 fprintf(
499 stderr,
500 "Unable to obtain keyboard layou…
501 );
502 if (desc)
503 XkbFreeClientMap(desc, 0, True);
504 } else {
505 XkbStateRec s;
506 XkbGetState(common.dpy, XkbUseCoreKbd, &…
507 char *group = XGetAtomName(
508 common.dpy, desc->names->groups[s.gr…
509 );
510 change_keyboard(group);
511 XFree(group);
512 XkbFreeNames(desc, XkbGroupNamesMask, Tr…
513 XkbFreeClientMap(desc, 0, True);
514 }
515 }
516 redraw();
517
518 clock_gettime(CLOCK_MONOTONIC, &now);
519 ledit_timespecsub(&now, &last, &elapsed);
520 if (elapsed.tv_sec == 0 && elapsed.tv_nsec < TICK) {
521 sleep_time.tv_nsec = TICK - elapsed.tv_nsec;
522 nanosleep(&sleep_time, NULL);
523 }
524 last = now;
525 }
526 }
527
528 extern char *optarg;
529 extern int optind;
530
531 static void
532 setup(int argc, char *argv[]) {
533 setlocale(LC_CTYPE, "");
534 XSetLocaleModifiers("");
535
536 char c;
537 char *opt_filename = NULL;
538 #if TEST
539 char *opts = "tc:";
540 #else
541 char *opts = "c:";
542 #endif
543 while ((c = getopt(argc, argv, opts)) != -1) {
544 switch (c) {
545 case 'c':
546 opt_filename = optarg;
547 break;
548 #if TEST
549 case 't':
550 test_extra = 1;
551 break;
552 #endif
553 default:
554 fprintf(stderr, "USAGE: ledit [-c config] [file]…
555 exit(1);
556 break;
557 }
558 }
559 argc -= optind;
560 argv += optind;
561
562 common.dpy = XOpenDisplay(NULL);
563 common.screen = DefaultScreen(common.dpy);
564 /* FIXME: fallback when no db support */
565 /* based on http://wili.cc/blog/xdbe.html */
566 int major, minor;
567 if (XdbeQueryExtension(common.dpy, &major, &minor)) {
568 int num_screens = 1;
569 Drawable screens[] = {DefaultRootWindow(common.dpy)};
570 XdbeScreenVisualInfo *info = XdbeGetVisualInfo(
571 common.dpy, screens, &num_screens
572 );
573 if (!info || num_screens < 1 || info->count < 1) {
574 fprintf(stderr, "No visuals support Xdbe.\n");
575 ledit_cleanup();
576 exit(1);
577 }
578 XVisualInfo xvisinfo_templ;
579 /* we know there's at least one */
580 xvisinfo_templ.visualid = info->visinfo[0].visual;
581 xvisinfo_templ.screen = 0;
582 xvisinfo_templ.depth = info->visinfo[0].depth;
583 int matches;
584 XVisualInfo *xvisinfo_match = XGetVisualInfo(
585 common.dpy,
586 VisualIDMask | VisualScreenMask | VisualDepthMask,
587 &xvisinfo_templ, &matches
588 );
589 if (!xvisinfo_match || matches < 1) {
590 fprintf(
591 stderr,
592 "Couldn't match a Visual with double bufferi…
593 );
594 ledit_cleanup();
595 exit(1);
596 }
597 common.vis = xvisinfo_match->visual;
598 XFree(xvisinfo_match);
599 XdbeFreeVisualInfo(info);
600 } else {
601 fprintf(stderr, "No Xdbe support.\n");
602 ledit_cleanup();
603 exit(1);
604 }
605
606 common.depth = DefaultDepth(common.dpy, common.screen);
607 common.cm = DefaultColormap(common.dpy, common.screen);
608
609 #ifdef LEDIT_DEBUG
610 struct timespec now, elapsed, last;
611 clock_gettime(CLOCK_MONOTONIC, &last);
612 #endif
613
614 /* FIXME: Technically, there's a race condition between checking…
615 opening the files. This is mainly important when checking if …
616 file because that is not an error for functions like fopen, s…
617 if a non-regular file (e.g. a directory) is given to one of t…
618 functions. However, I don't know of any portable way to have …
619 check that, so this is the best I can do. */
620 char *stat_errstr = NULL, *load_errstr = NULL, *load_default_err…
621 char *cfgfile = NULL;
622 if (!opt_filename) {
623 uid_t uid = getuid();
624 struct passwd *pw = getpwuid(uid);
625 if (!pw) {
626 stat_errstr = ledit_strdup("Unable to determine …
627 } else {
628 cfgfile = ledit_strcat(pw->pw_dir, "/.leditrc");
629 struct stat cfgst;
630 if (stat(cfgfile, &cfgst)) {
631 free(cfgfile);
632 cfgfile = NULL;
633 if (errno != ENOENT) {
634 stat_errstr = print_fmt("Unable …
635 }
636 } else if (!S_ISREG(cfgst.st_mode)) {
637 stat_errstr = ledit_strdup("Unable to lo…
638 free(cfgfile);
639 cfgfile = NULL;
640 }
641 }
642 } else {
643 struct stat cfgst;
644 if (stat(opt_filename, &cfgst)) {
645 stat_errstr = print_fmt("Unable to load configur…
646 } else if (!S_ISREG(cfgst.st_mode)) {
647 stat_errstr = print_fmt("Unable to load configur…
648 } else {
649 cfgfile = ledit_strdup(opt_filename);
650 }
651 }
652 if (stat_errstr)
653 fprintf(stderr, "%s\n", stat_errstr);
654 if (config_loadfile(&common, cfgfile, &load_errstr)) {
655 fprintf(stderr, "%s\n", load_errstr);
656 fprintf(stderr, "Unable to load configuration '%s'\n", c…
657 int failure = 1;
658 if (cfgfile) {
659 /* retry with default config */
660 failure = config_loadfile(&common, NULL, &load_d…
661 }
662 if (failure) {
663 if (load_default_errstr) {
664 fprintf(stderr, "%s\n", load_default_err…
665 fprintf(stderr, "Also unable to load def…
666 }
667 free(stat_errstr);
668 free(load_errstr);
669 free(load_default_errstr);
670 ledit_cleanup();
671 exit(1);
672 }
673 }
674 free(load_default_errstr);
675 free(cfgfile);
676
677 #ifdef LEDIT_DEBUG
678 clock_gettime(CLOCK_MONOTONIC, &now);
679 ledit_timespecsub(&now, &last, &elapsed);
680 ledit_debug_fmt("Time to load config (total): %lld seconds, %ld …
681 #endif
682
683 clipboard = clipboard_create(&common);
684
685 buffer = buffer_create(&common, clipboard);
686 buffer_add_view(buffer, NORMAL, 0, 0, 0);
687 /* FIXME: don't access view directly here */
688 ledit_view *view = buffer->views[0];
689 /* FIXME: this message may be wiped immediately */
690 /* -> maybe allow showing multiple messages? */
691 /* currently, the more important message is just prioritized */
692 int show_error = 0;
693 if (stat_errstr || load_errstr) {
694 show_error = 1;
695 if (stat_errstr)
696 window_show_message(view->window, stat_errstr, -…
697 else if (load_errstr)
698 window_show_message(view->window, load_errstr, -…
699 free(stat_errstr);
700 free(load_errstr);
701 }
702 view_set_line_cursor_attrs(view, view->cur_line, view->cur_index…
703 /* FIXME: maybe also log all errors instead of just showing them…
704 /* FIXME: Support multiple buffers/files */
705 /* FIXME: check if file may be binary */
706 if (argc >= 1) {
707 /* FIXME: move this to different file */
708 char *load_err;
709 struct stat sb;
710 int newfile = 0;
711 int readonly = 0;
712 int error = 0;
713 /* FIXME: maybe copy vi and open file in /tmp by default…
714 /* FIXME: when other methods of opening files (:r, etc.)…
715 all this checking needs to be moved to a place where …
716 if (stat(argv[0], &sb)) {
717 if (errno == ENOENT) {
718 /* note that there may still be a failure
719 when trying to write if a directory in
720 the path does not exist */
721 newfile = 1;
722 } else {
723 fprintf(
724 stderr, "Error opening file '%s': %s…
725 argv[0], strerror(errno)
726 );
727 window_show_message_fmt(
728 view->window, "Error opening file '%…
729 argv[0], strerror(errno)
730 );
731 error = 1;
732 }
733 } else if (!S_ISREG(sb.st_mode)) {
734 fprintf(stderr, "Error opening file '%s': Is not…
735 window_show_message_fmt(
736 view->window, "Error opening file '%s': Is n…
737 );
738 error = 1;
739 }
740 if (access(argv[0], W_OK)) {
741 readonly = 1;
742 }
743 if (!newfile && !error) {
744 if (buffer_load_file(buffer, argv[0], 0, &load_e…
745 fprintf(
746 stderr, "Error opening file '%s': %s…
747 argv[0], load_err
748 );
749 window_show_message_fmt(
750 view->window, "Error opening file '%…
751 argv[0], load_err
752 );
753 error = 1;
754 }
755 buffer->file_mtime = sb.st_mtim;
756 }
757 if (!error) {
758 buffer->filename = ledit_strdup(argv[0]);
759 if (!show_error) {
760 if (newfile) {
761 window_show_message_fmt(view->wi…
762 } else if (readonly) {
763 window_show_message_fmt(view->wi…
764 } else {
765 window_show_message(view->window…
766 }
767 }
768 }
769 }
770
771 redraw();
772 }
773
774 /* FIXME: maybe also write diagnostic information, e.g. number of lines …
775 void
776 ledit_emergencydump(const char *filename, int line, const char *func, co…
777 /* FIXME: pre-allocate memory for template to avoid memory error…
778 -> probably overkill since something else will fail anyways */
779 if (!buffer)
780 return;
781 /* FIXME: maybe write assertion message to file? */
782 char *orig = buffer->filename ? buffer->filename : "ledit";
783 char *suffix = "-emergency-dump-XXXXXXXXXX";
784 size_t len1, len2;
785 len1 = strlen(orig);
786 len2 = strlen(suffix);
787 /* This doesn't use ledit_strcat so a memory allocation
788 failure doesn't interfere with the abort in the assertion
789 that calls this function. */
790 char *template = malloc(len1 + len2 + 1);
791 /* FIXME: print error here */
792 if (!template)
793 return;
794 strcpy(template, orig);
795 strcpy(template + len1, suffix);
796 int fd = mkstemp(template);
797 if (fd == -1) {
798 fprintf(
799 stderr,
800 "Unable to open file for emergency dump: %s\n",
801 strerror(errno)
802 );
803 goto error;
804 }
805 char *errstr;
806 /* buffer_write_to_fd closes the file descriptor, so this has to…
807 int dupfd = dup(fd);
808 /* FIXME: improve error messages here; maybe only try to write e…
809 if (buffer_write_to_fd(buffer, fd, &errstr)) {
810 fprintf(
811 stderr,
812 "Unable to perform emergency dump: %s\n",
813 errstr
814 );
815 } else {
816 fprintf(
817 stderr,
818 "Wrote emergency dump to %s\n",
819 template
820 );
821 }
822 if (dupfd == -1) {
823 fprintf(
824 stderr,
825 "Unable to duplicate file descriptor for emergency d…
826 strerror(errno)
827 );
828 goto error;
829 }
830 FILE *file = fdopen(dupfd, "w");
831 if (!file) {
832 fprintf(
833 stderr,
834 "Unable to fdopen file descriptor for emergency dump…
835 strerror(errno)
836 );
837 if (close(dupfd)) {
838 fprintf(
839 stderr,
840 "Unable to close duplicated file descriptor …
841 strerror(errno)
842 );
843 }
844 goto error;
845 }
846 fprintf(
847 file,
848 "ERROR MESSAGE:\n\"%s\" in \"%s\", line %d, function \"%s\"\…
849 failedexpr, filename, line, func
850 );
851 if (fclose(file))
852 fprintf(stderr, "Unable to close file for emergency dump…
853 error:
854 free(template);
855 return;
856 }
857
858 void
859 ledit_cleanup(void) {
860 search_cleanup();
861 basic_key_cleanup();
862 command_key_cleanup();
863 key_processing_cleanup();
864 if (clipboard)
865 clipboard_destroy(clipboard);
866 if (buffer)
867 buffer_destroy(buffer);
868 config_cleanup(&common);
869 XCloseDisplay(common.dpy);
870 }
871
872 static void
873 redraw(void) {
874 for (size_t i = 0; i < buffer->views_num; i++) {
875 view_redraw(buffer->views[i], cur_lang);
876 }
877 }
878
879 static void
880 change_keyboard(char *lang) {
881 ledit_debug_fmt("New keyboard layout: %s\n", lang);
882 if (config_get_language_index(lang, &cur_lang)) {
883 for (size_t i = 0; i < buffer->views_num; i++) {
884 window_show_message_fmt(
885 buffer->views[i]->window,
886 "No mapping for language \"%s\", using defau…
887 lang
888 );
889 }
890 cur_lang = 0;
891 }
892 }
893
894 static void
895 key_press(ledit_view *view, unsigned int key_state, KeySym sym, char *bu…
896 /* FIXME: just let view handle this since the action is part
897 of it anyways now */
898 if (view->cur_action.type == ACTION_GRABKEY && view->cur_action.…
899 view->cur_action = view->cur_action.callback(view, key_s…
900 } else {
901 view->cur_action = basic_key_handler(view, key_state, sy…
902 }
903 }
904
905 static void
906 key_press_event(ledit_view *view, XEvent *event) {
907 char *buf = NULL;
908 KeySym sym = NoSymbol;
909 int n;
910 unsigned int key_state = event->xkey.state;
911 preprocess_key(view->window, &event->xkey, &sym, &buf, &n);
912 key_press(view, key_state, sym, buf, n);
913 }
914
915 int
916 main(int argc, char *argv[]) {
917 setup(argc, argv);
918 mainloop();
919 ledit_cleanup();
920
921 return 0;
922 }
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.