#include #include #include #include #include #include #include #include "pline.h" #include "screen.h" #include "help.h" extern const char * const helptext; /* * Write help text to a descriptor. This assumes the descriptor is one * that supports send(), because we use MSG_NOSIGNAL to avoid needing * to block/ignore SIGPIPE. */ static void write_help_text(int fd) { int n; int w; int o; int l; o = 0; l = strlen(helptext); while (1) { n = l - o; if (n < 1) return; w = send(fd,helptext+o,n,MSG_NOSIGNAL); if (w < 0) { if (errno != EPIPE) pline("Help text write error: %s",strerror(errno)); return; } if (w == 0) { pline("Help text zero write"); return; } o += w; } } /* * Display help. We fork less(1) with input coming from a pipe to us, * to which we write our help text. */ void help(void) { pid_t kid; pid_t dead; int dp[2]; int xp[2]; int e; int n; int status; if (socketpair(AF_LOCAL,SOCK_STREAM,0,&dp[0]) < 0) { pline("data socketpair(): %s",strerror(errno)); return; } do { if (socketpair(AF_LOCAL,SOCK_STREAM,0,&xp[0]) < 0) { pline("exec socketpair(): %s",strerror(errno)); break; } fcntl(xp[0],F_SETFD,1); cleanupscreen(); do { fflush(0); kid = fork(); if (kid < 0) { pline("fork(): %s",strerror(errno)); break; } if (kid == 0) { if (dp[0] != 0) { dup2(dp[0],0); close(dp[0]); } close(dp[1]); close(xp[1]); execlp("less","less",(const char *)0); e = errno; write(xp[0],&e,sizeof(int)); _exit(0); } close(xp[0]); xp[0] = -1; do { n = recv(xp[1],&e,sizeof(e),MSG_WAITALL); if (n < 0) { pline("exec recv error: %s",strerror(errno)); break; } if (n == sizeof(e)) { pline("can't exec less: %s",strerror(e)); break; } if (n != 0) { pline("exec protocol error: got %d, not %d",n,(int)sizeof(e)); break; } close(xp[1]); xp[1] = -1; close(dp[0]); dp[0] = -1; write_help_text(dp[1]); close(dp[1]); dp[1] = -1; while (1) { dead = wait(&status); if (dead < 0) { if (errno == EINTR) continue; pline("wait: %s",strerror(errno)); break; } if (dead != kid) continue; kid = -1; if (WIFEXITED(status)) { if (WEXITSTATUS(status) != 0) { pline("less: exit %d",WEXITSTATUS(status)); } } else if (WIFSIGNALED(status)) { e = WTERMSIG(status); if ((e < 1) || (e >= NSIG)) { pline("less: signal %d%s",e,WCOREDUMP(status)?" (core dumped)":""); } else { pline("less: signal %d [%s]%s",e,sys_signame[e],WCOREDUMP(status)?" (core dumped)":""); } } else { pline("less: incomprehensible status %d",status); } break; } } while (0); if (kid > 1) { kill(kid,SIGKILL); wait(0); } } while (0); initscreen(); if (xp[0] >= 0) close(xp[0]); if (xp[1] >= 0) close(xp[1]); } while (0); if (dp[0] >= 0) close(dp[0]); if (dp[1] >= 0) close(dp[1]); }