commit 4167d62b8f987c4ca550981e3aa66d65dcb6542e from: Marcel Rodrigues date: Sat Jun 06 16:26:13 2015 UTC Don't make async-unsafe calls in signal handlers. This change removes any code that is not asynchronous-safe from signal handlers, leaving those operations to be performed in the main flow of the program. The same strategy cannot be used for SIGSEGV since its handler is not allowed to return normally. In this case, the old handler was simply removed. The reset command may be used to restore the terminal state if Rover ever crashes. commit - 7d6f72fe0f27b157fe4a83d8f01dd6d5d4900d54 commit + 4167d62b8f987c4ca550981e3aa66d65dcb6542e blob - 63fb9dc88702817ac24f1d16239ff35e8b660f08 blob + 992c0c10944e12b3c6a3eb6ffd4fb6b6530ced1d --- rover.c +++ rover.c @@ -78,6 +78,7 @@ static struct Rover { Marks marks; Edit edit; int edit_scroll; + volatile sig_atomic_t pending_winch; } rover; /* Macros for accessing global state. */ @@ -203,29 +204,10 @@ free_marks(Marks *marks) free(marks->entries); } -static void update_view(); - -/* SIGSEGV handler: clean up curses before exiting. */ static void -handle_segv(int sig) -{ - (void) sig; - endwin(); - fprintf(stderr, "Received SIGSEGV (segmentation fault).\n"); - exit(1); -} - -/* SIGWINCH handler: resize application according to new terminal settings. */ -static void handle_winch(int sig) { - (void) sig; - delwin(rover.window); - endwin(); - refresh(); - clear(); - rover.window = subwin(stdscr, LINES - 2, COLS, 1, 0); - update_view(); + rover.pending_winch = 1; } static void @@ -234,8 +216,6 @@ enable_handlers() struct sigaction sa; memset(&sa, 0, sizeof (struct sigaction)); - sa.sa_handler = handle_segv; - sigaction(SIGSEGV, &sa, NULL); sa.sa_handler = handle_winch; sigaction(SIGWINCH, &sa, NULL); } @@ -247,10 +227,40 @@ disable_handlers() memset(&sa, 0, sizeof (struct sigaction)); sa.sa_handler = SIG_DFL; - sigaction(SIGSEGV, &sa, NULL); sigaction(SIGWINCH, &sa, NULL); } +static void update_view(); + +/* Handle any signals received since last call. */ +static void +sync_signals() +{ + if (rover.pending_winch) { + /* SIGWINCH received: resize application accordingly. */ + delwin(rover.window); + endwin(); + refresh(); + clear(); + rover.window = subwin(stdscr, LINES - 2, COLS, 1, 0); + SCROLL = MAX(ESEL - HEIGHT, 0); + update_view(); + rover.pending_winch = 0; + } +} + +/* This function should be used in place of getch(). + * It handles signals while waiting for user input. */ +static int +rover_getch() +{ + int ch; + + while ((ch = getch()) == ERR) + sync_signals(); + return ch; +} + /* Do a fork-exec to external program (e.g. $EDITOR). */ static void spawn() @@ -280,6 +290,7 @@ init_term() setlocale(LC_ALL, ""); initscr(); cbreak(); /* Get one character at a time. */ + timeout(100); /* For getch(). */ noecho(); nonl(); /* No NL->CR/NL on output. */ intrflush(stdscr, FALSE); @@ -656,8 +667,10 @@ static int cpyfile(const char *srcpath) { strcat(dstpath, srcpath + strlen(rover.marks.dirpath)); ret = dst = creat(dstpath, st.st_mode); if (ret < 0) return ret; - while ((size = read(src, buf, BUFSIZ)) > 0) + while ((size = read(src, buf, BUFSIZ)) > 0) { write(dst, buf, size); + sync_signals(); + } close(src); close(dst); return 0; @@ -700,7 +713,7 @@ start_line_edit(const char *init_input) static EditStat get_line_edit() { - int ch = getch(); + int ch = rover_getch(); if (ch == '\r' || ch == '\n' || ch == KEY_ENTER) { curs_set(FALSE); return CONFIRM; @@ -817,7 +830,7 @@ main(int argc, char *argv[]) init_marks(&rover.marks); cd(1); while (1) { - ch = getch(); + ch = rover_getch(); key = keyname(ch); clear_message(); if (!strcmp(key, RVK_QUIT)) break; @@ -1062,7 +1075,7 @@ main(int argc, char *argv[]) } else if (!strcmp(key, RVK_DELETE)) { if (rover.nfiles) { message("Delete selected entry? (Y to confirm)", YELLOW); - if (getch() == 'Y') { + if (rover_getch() == 'Y') { const char *name = ENAME(ESEL); int ret = S_ISDIR(EMODE(ESEL)) ? deldir(name) : delfile(name); reload(); @@ -1099,7 +1112,7 @@ main(int argc, char *argv[]) } else if (!strcmp(key, RVK_MARK_DELETE)) { if (rover.marks.nentries) { message("Delete marked entries? (Y to confirm)", YELLOW); - if (getch() == 'Y') + if (rover_getch() == 'Y') process_marked(NULL, delfile, deldir); else clear_message();