📄 sdiff.c
字号:
int i; for (i = 0; diffargv[i]; i++) cmdsize += quote_system_arg (0, diffargv[i]) + 1; command = p = xmalloc (cmdsize); for (i = 0; diffargv[i]; i++) { p += quote_system_arg (p, diffargv[i]); *p++ = ' '; } p[-1] = 0; errno = 0; diffout = popen (command, "r"); if (! diffout) perror_fatal (command); free (command); }#else { int diff_fds[2];# if HAVE_WORKING_VFORK sigset_t procmask; sigset_t blocked;# endif if (pipe (diff_fds) != 0) perror_fatal ("pipe");# if HAVE_WORKING_VFORK /* Block SIGINT and SIGPIPE. */ sigemptyset (&blocked); sigaddset (&blocked, SIGINT); sigaddset (&blocked, SIGPIPE); sigprocmask (SIG_BLOCK, &blocked, &procmask);# endif diffpid = vfork (); if (diffpid < 0) perror_fatal ("fork"); if (! diffpid) { /* Alter the child's SIGINT and SIGPIPE handlers; this may munge the parent. The child ignores SIGINT in case the user interrupts the editor. The child does not ignore SIGPIPE, even if the parent does. */ if (initial_handler (handler_index_of_SIGINT) != SIG_IGN) signal_handler (SIGINT, SIG_IGN); signal_handler (SIGPIPE, SIG_DFL);# if HAVE_WORKING_VFORK /* Stop blocking SIGINT and SIGPIPE in the child. */ sigprocmask (SIG_SETMASK, &procmask, 0);# endif close (diff_fds[0]); if (diff_fds[1] != STDOUT_FILENO) { dup2 (diff_fds[1], STDOUT_FILENO); close (diff_fds[1]); } execvp (diffargv[0], (char **) diffargv); _exit (errno == ENOENT ? 127 : 126); }# if HAVE_WORKING_VFORK /* Restore the parent's SIGINT and SIGPIPE behavior. */ if (initial_handler (handler_index_of_SIGINT) != SIG_IGN) signal_handler (SIGINT, catchsig); if (initial_handler (handler_index_of_SIGPIPE) != SIG_IGN) signal_handler (SIGPIPE, catchsig); else signal_handler (SIGPIPE, SIG_IGN); /* Stop blocking SIGINT and SIGPIPE in the parent. */ sigprocmask (SIG_SETMASK, &procmask, 0);# endif close (diff_fds[1]); diffout = fdopen (diff_fds[0], "r"); if (! diffout) perror_fatal ("fdopen"); }#endif lf_init (&diff_filt, diffout); lf_init (&lfilt, left); lf_init (&rfilt, right); interact_ok = interact (&diff_filt, &lfilt, lname, &rfilt, rname, out); ck_fclose (left); ck_fclose (right); ck_fclose (out); { int wstatus; int werrno = 0;#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) wstatus = pclose (diffout); if (wstatus == -1) werrno = errno;#else ck_fclose (diffout); while (waitpid (diffpid, &wstatus, 0) < 0) if (errno == EINTR) checksigs (); else perror_fatal ("waitpid"); diffpid = 0;#endif if (tmpname) { unlink (tmpname); tmpname = 0; } if (! interact_ok) exiterr (); check_child_status (werrno, wstatus, EXIT_FAILURE, diffargv[0]); untrapsig (0); checksigs (); exit (WEXITSTATUS (wstatus)); } } return EXIT_SUCCESS; /* Fool `-Wall'. */}static voiddiffarg (char const *a){ static size_t diffargs, diffarglim; if (diffargs == diffarglim) { if (! diffarglim) diffarglim = 16; else if (PTRDIFF_MAX / (2 * sizeof *diffargv) <= diffarglim) xalloc_die (); else diffarglim *= 2; diffargv = xrealloc (diffargv, diffarglim * sizeof *diffargv); } diffargv[diffargs++] = a;}/* Signal handling */static bool volatile ignore_SIGINT;static int volatile signal_received;static bool sigs_trapped;static voidcatchsig (int s){#if ! HAVE_SIGACTION signal (s, SIG_IGN);#endif if (! (s == SIGINT && ignore_SIGINT)) signal_received = s;}#if HAVE_SIGACTIONstatic struct sigaction catchaction;static voidsignal_handler (int sig, void (*handler) (int)){ catchaction.sa_handler = handler; sigaction (sig, &catchaction, 0);}#endifstatic voidtrapsigs (void){ int i;#if HAVE_SIGACTION catchaction.sa_flags = SA_RESTART; sigemptyset (&catchaction.sa_mask); for (i = 0; i < NUM_SIGS; i++) sigaddset (&catchaction.sa_mask, sigs[i]);#endif for (i = 0; i < NUM_SIGS; i++) {#if HAVE_SIGACTION sigaction (sigs[i], 0, &initial_action[i]);#else initial_action[i] = signal (sigs[i], SIG_IGN);#endif if (initial_handler (i) != SIG_IGN) signal_handler (sigs[i], catchsig); }#ifdef SIGCHLD /* System V fork+wait does not work if SIGCHLD is ignored. */ signal (SIGCHLD, SIG_DFL);#endif sigs_trapped = true;}/* Untrap signal S, or all trapped signals if S is zero. */static voiduntrapsig (int s){ int i; if (sigs_trapped) for (i = 0; i < NUM_SIGS; i++) if ((! s || sigs[i] == s) && initial_handler (i) != SIG_IGN) {#if HAVE_SIGACTION sigaction (sigs[i], &initial_action[i], 0);#else signal (sigs[i], initial_action[i]);#endif }}/* Exit if a signal has been received. */static voidchecksigs (void){ int s = signal_received; if (s) { cleanup (0); /* Yield an exit status indicating that a signal was received. */ untrapsig (s); kill (getpid (), s); /* That didn't work, so exit with error status. */ exit (EXIT_TROUBLE); }}static voidgive_help (void){ fprintf (stderr, "%s", _("\ed:\tEdit then use both versions, each decorated with a header.\n\eb:\tEdit then use both versions.\n\el:\tEdit then use the left version.\n\er:\tEdit then use the right version.\n\e:\tEdit a new version.\n\l:\tUse the left version.\n\r:\tUse the right version.\n\s:\tSilently include common lines.\n\v:\tVerbosely include common lines.\n\q:\tQuit.\n\"));}static intskip_white (void){ int c; for (;;) { c = getchar (); if (! isspace (c) || c == '\n') break; checksigs (); } if (ferror (stdin)) perror_fatal (_("read failed")); return c;}static voidflush_line (void){ int c; while ((c = getchar ()) != '\n' && c != EOF) continue; if (ferror (stdin)) perror_fatal (_("read failed"));}/* interpret an edit command */static booledit (struct line_filter *left, char const *lname, lin lline, lin llen, struct line_filter *right, char const *rname, lin rline, lin rlen, FILE *outfile){ for (;;) { int cmd0, cmd1; bool gotcmd = false; cmd1 = 0; /* Pacify `gcc -W'. */ while (! gotcmd) { if (putchar ('%') != '%') perror_fatal (_("write failed")); ck_fflush (stdout); cmd0 = skip_white (); switch (cmd0) { case 'l': case 'r': case 's': case 'v': case 'q': if (skip_white () != '\n') { give_help (); flush_line (); continue; } gotcmd = true; break; case 'e': cmd1 = skip_white (); switch (cmd1) { case 'b': case 'd': case 'l': case 'r': if (skip_white () != '\n') { give_help (); flush_line (); continue; } gotcmd = true; break; case '\n': gotcmd = true; break; default: give_help (); flush_line (); continue; } break; case EOF: if (feof (stdin)) { gotcmd = true; cmd0 = 'q'; break; } /* Fall through. */ default: flush_line (); /* Fall through. */ case '\n': give_help (); continue; } } switch (cmd0) { case 'l': lf_copy (left, llen, outfile); lf_skip (right, rlen); return true; case 'r': lf_copy (right, rlen, outfile); lf_skip (left, llen); return true; case 's': suppress_common_lines = true; break; case 'v': suppress_common_lines = false; break; case 'q': return false; case 'e': { int fd; if (tmpname) tmp = fopen (tmpname, "w"); else { if ((fd = temporary_file ()) < 0) perror_fatal ("mkstemp"); tmp = fdopen (fd, "w"); } if (! tmp) perror_fatal (tmpname); switch (cmd1) { case 'd': if (llen) { if (llen == 1) fprintf (tmp, "--- %s %ld\n", lname, (long int) lline); else fprintf (tmp, "--- %s %ld,%ld\n", lname, (long int) lline, (long int) (lline + llen - 1)); } /* Fall through. */ case 'b': case 'l': lf_copy (left, llen, tmp); break; default: lf_skip (left, llen); break; } switch (cmd1) { case 'd': if (rlen) { if (rlen == 1) fprintf (tmp, "+++ %s %ld\n", rname, (long int) rline); else fprintf (tmp, "+++ %s %ld,%ld\n", rname, (long int) rline, (long int) (rline + rlen - 1)); } /* Fall through. */ case 'b': case 'r': lf_copy (right, rlen, tmp); break; default: lf_skip (right, rlen); break; } ck_fclose (tmp); { int wstatus; int werrno = 0; ignore_SIGINT = true; checksigs (); {#if ! (HAVE_WORKING_FORK || HAVE_WORKING_VFORK) char *command = xmalloc (quote_system_arg (0, editor_program) + 1 + strlen (tmpname) + 1); sprintf (command + quote_system_arg (command, editor_program), " %s", tmpname); wstatus = system (command); if (wstatus == -1) werrno = errno; free (command);#else pid_t pid; pid = vfork (); if (pid == 0) { char const *argv[3]; int i = 0; argv[i++] = editor_program; argv[i++] = tmpname; argv[i] = 0; execvp (editor_program, (char **) argv); _exit (errno == ENOENT ? 127 : 126); } if (pid < 0) perror_fatal ("fork"); while (waitpid (pid, &wstatus, 0) < 0) if (errno == EINTR) checksigs (); else perror_fatal ("waitpid");#endif } ignore_SIGINT = false; check_child_status (werrno, wstatus, EXIT_SUCCESS, editor_program); } { char buf[SDIFF_BUFSIZE]; size_t size; tmp = ck_fopen (tmpname, "r"); while ((size = ck_fread (buf, SDIFF_BUFSIZE, tmp)) != 0) { checksigs (); ck_fwrite (buf, size, outfile); } ck_fclose (tmp); } return true; } default: give_help (); break; } }}/* Alternately reveal bursts of diff output and handle user commands. */static boolinteract (struct line_filter *diff, struct line_filter *left, char const *lname, struct line_filter *right, char const *rname, FILE *outfile){ lin lline = 1, rline = 1; for (;;) { char diff_help[256]; int snarfed = lf_snarf (diff, diff_help, sizeof diff_help); if (snarfed <= 0) return snarfed != 0; checksigs (); if (diff_help[0] == ' ') puts (diff_help + 1); else { char *numend; uintmax_t val; lin llen, rlen, lenmax; errno = 0; llen = val = strtoumax (diff_help + 1, &numend, 10); if (llen < 0 || llen != val || errno || *numend != ',') fatal (diff_help); rlen = val = strtoumax (numend + 1, &numend, 10); if (rlen < 0 || rlen != val || errno || *numend) fatal (diff_help); lenmax = MAX (llen, rlen); switch (diff_help[0]) { case 'i': if (suppress_common_lines) lf_skip (diff, lenmax); else lf_copy (diff, lenmax, stdout); lf_copy (left, llen, outfile); lf_skip (right, rlen); break; case 'c': lf_copy (diff, lenmax, stdout); if (! edit (left, lname, lline, llen, right, rname, rline, rlen, outfile)) return false; break; default: fatal (diff_help); } lline += llen; rline += rlen; } }}/* Return true if DIR is an existing directory. */static booldiraccess (char const *dir){ struct stat buf; return stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);}#ifndef P_tmpdir# define P_tmpdir "/tmp"#endif#ifndef TMPDIR_ENV# define TMPDIR_ENV "TMPDIR"#endif/* Open a temporary file and return its file descriptor. Put into tmpname the address of a newly allocated buffer that holds the file's name. Use the prefix "sdiff". */static inttemporary_file (void){ char const *tmpdir = getenv (TMPDIR_ENV); char const *dir = tmpdir ? tmpdir : P_tmpdir; char *buf = xmalloc (strlen (dir) + 1 + 5 + 6 + 1); int fd; int e; sigset_t procmask; sigset_t blocked; sprintf (buf, "%s/sdiffXXXXXX", dir); sigemptyset (&blocked); sigaddset (&blocked, SIGINT); sigprocmask (SIG_BLOCK, &blocked, &procmask); fd = mkstemp (buf); e = errno; if (0 <= fd) tmpname = buf; sigprocmask (SIG_SETMASK, &procmask, 0); errno = e; return fd;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -