tw.help.c revision 167465
1123579Sgibbs/* $Header: /p/tcsh/cvsroot/tcsh/tw.help.c,v 3.27 2006/08/24 20:56:31 christos Exp $ */
2123579Sgibbs/* tw.help.c: actually look up and print documentation on a file.
3123579Sgibbs *	      Look down the path for an appropriate file, then print it.
4123579Sgibbs *	      Note that the printing is NOT PAGED.  This is because the
5123579Sgibbs *	      function is NOT meant to look at manual pages, it only does so
6123579Sgibbs *	      if there is no .help file to look in.
7123579Sgibbs */
8123579Sgibbs/*-
9123579Sgibbs * Copyright (c) 1980, 1991 The Regents of the University of California.
10123579Sgibbs * All rights reserved.
11123579Sgibbs *
12123579Sgibbs * Redistribution and use in source and binary forms, with or without
13123579Sgibbs * modification, are permitted provided that the following conditions
14123579Sgibbs * are met:
15123579Sgibbs * 1. Redistributions of source code must retain the above copyright
16123579Sgibbs *    notice, this list of conditions and the following disclaimer.
17123579Sgibbs * 2. Redistributions in binary form must reproduce the above copyright
18123579Sgibbs *    notice, this list of conditions and the following disclaimer in the
19123579Sgibbs *    documentation and/or other materials provided with the distribution.
20123579Sgibbs * 3. Neither the name of the University nor the names of its contributors
21123579Sgibbs *    may be used to endorse or promote products derived from this software
22123579Sgibbs *    without specific prior written permission.
23123579Sgibbs *
24123579Sgibbs * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25123579Sgibbs * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26123579Sgibbs * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27123579Sgibbs * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28123579Sgibbs * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29123579Sgibbs * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30123579Sgibbs * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31123579Sgibbs * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32123679Sgibbs * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33123579Sgibbs * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34123579Sgibbs * SUCH DAMAGE.
35123579Sgibbs */
36123579Sgibbs#include "sh.h"
37123579Sgibbs
38123579SgibbsRCSID("$tcsh: tw.help.c,v 3.27 2006/08/24 20:56:31 christos Exp $")
39123579Sgibbs
40123579Sgibbs#include "tw.h"
41123579Sgibbs#include "tc.h"
42123579Sgibbs
43123579Sgibbs
44123579Sgibbsstatic int f = -1;
45123579Sgibbsstatic	void		 cleanf		(int);
46123579Sgibbsstatic	Char    	*skipslist	(Char *);
47123579Sgibbsstatic	void		 nextslist 	(const Char *, Char *);
48123579Sgibbs
49133911Sgibbsstatic const char *const h_ext[] = {
50123579Sgibbs    ".help", ".1", ".8", ".6", "", NULL
51123579Sgibbs};
52123579Sgibbs
53123579Sgibbsvoid
54123579Sgibbsdo_help(const Char *command)
55123579Sgibbs{
56123579Sgibbs    Char   *name, *cmd_p;
57123579Sgibbs
58123579Sgibbs    /* trim off the whitespace at the beginning */
59123579Sgibbs    while (*command == ' ' || *command == '\t')
60123579Sgibbs        command++;
61123579Sgibbs
62123579Sgibbs    /* copy the string to a safe place */
63123579Sgibbs    name = Strsave(command);
64123579Sgibbs    cleanup_push(name, xfree);
65123579Sgibbs
66123579Sgibbs    /* trim off the whitespace that may be at the end */
67123579Sgibbs    for (cmd_p = name;
68123579Sgibbs	 *cmd_p != ' ' && *cmd_p != '\t' && *cmd_p != '\0'; cmd_p++)
69123579Sgibbs	continue;
70123579Sgibbs    *cmd_p = '\0';
71123579Sgibbs
72123579Sgibbs    /* if nothing left, return */
73123579Sgibbs    if (*name == '\0') {
74123579Sgibbs	cleanup_until(name);
75123579Sgibbs	return;
76123579Sgibbs    }
77123579Sgibbs
78123579Sgibbs    if (adrof1(STRhelpcommand, &aliases)) {	/* if we have an alias */
79123579Sgibbs	jmp_buf_t osetexit;
80123579Sgibbs	size_t omark;
81123579Sgibbs
82123579Sgibbs	getexit(osetexit);	/* make sure to come back here */
83123579Sgibbs	omark = cleanup_push_mark();
84123579Sgibbs	if (setexit() == 0)
85123579Sgibbs	    aliasrun(2, STRhelpcommand, name);	/* then use it. */
86123579Sgibbs	cleanup_pop_mark(omark);
87123579Sgibbs	resexit(osetexit);	/* and finish up */
88123579Sgibbs    }
89123579Sgibbs    else {			/* else cat something to them */
90123579Sgibbs	Char *thpath, *hpath;	/* The environment parameter */
91123579Sgibbs	Char *curdir;	        /* Current directory being looked at */
92123579Sgibbs	struct Strbuf full = Strbuf_INIT;
93123579Sgibbs
94123579Sgibbs	/* got is, now "cat" the file based on the path $HPATH */
95123579Sgibbs
96123579Sgibbs	hpath = str2short(getenv(SEARCHLIST));
97123579Sgibbs	if (hpath == NULL)
98123579Sgibbs	    hpath = str2short(DEFAULTLIST);
99123579Sgibbs	thpath = hpath = Strsave(hpath);
100123579Sgibbs	cleanup_push(thpath, xfree);
101123579Sgibbs	curdir = xmalloc((Strlen(thpath) + 1) * sizeof (*curdir));
102123579Sgibbs	cleanup_push(curdir, xfree);
103123579Sgibbs	cleanup_push(&full, Strbuf_cleanup);
104123579Sgibbs
105123579Sgibbs	for (;;) {
106123579Sgibbs	    const char *const *sp;
107123579Sgibbs	    size_t ep;
108123579Sgibbs
109123579Sgibbs	    if (!*hpath) {
110123579Sgibbs		xprintf(CGETS(29, 1, "No help file for %S\n"), name);
111123579Sgibbs		break;
112123579Sgibbs	    }
113123579Sgibbs	    nextslist(hpath, curdir);
114123579Sgibbs	    hpath = skipslist(hpath);
115123579Sgibbs
116123579Sgibbs	    /*
117123579Sgibbs	     * now make the full path name - try first /bar/foo.help, then
118123579Sgibbs	     * /bar/foo.1, /bar/foo.8, then finally /bar/foo.6.  This is so
119123579Sgibbs	     * that you don't spit a binary at the tty when $HPATH == $PATH.
120123579Sgibbs	     */
121123579Sgibbs	    full.len = 0;
122123579Sgibbs	    Strbuf_append(&full, curdir);
123123579Sgibbs	    Strbuf_append(&full, STRslash);
124123679Sgibbs	    Strbuf_append(&full, name);
125123579Sgibbs	    ep = full.len;
126123679Sgibbs	    for (sp = h_ext; *sp; sp++) {
127123679Sgibbs		full.len = ep;
128123579Sgibbs		Strbuf_append(&full, str2short(*sp));
129123679Sgibbs		Strbuf_terminate(&full);
130123679Sgibbs		if ((f = xopen(short2str(full.s), O_RDONLY|O_LARGEFILE)) != -1)
131123679Sgibbs		    break;
132123679Sgibbs	    }
133123579Sgibbs	    if (f != -1) {
134123579Sgibbs	        unsigned char buf[512];
135123579Sgibbs		sigset_t oset, set;
136123579Sgibbs		struct sigaction osa, sa;
137123679Sgibbs		ssize_t len;
138123579Sgibbs
139123579Sgibbs		/* so cat it to the terminal */
140123579Sgibbs		cleanup_push(&f, open_cleanup);
141123579Sgibbs		sa.sa_handler = cleanf;
142123579Sgibbs		sigemptyset(&sa.sa_mask);
143123579Sgibbs		sa.sa_flags = 0;
144123579Sgibbs		(void)sigaction(SIGINT, &sa, &osa);
145123679Sgibbs		cleanup_push(&osa, sigint_cleanup);
146123679Sgibbs		(void)sigprocmask(SIG_UNBLOCK, &set, &oset);
147123679Sgibbs		cleanup_push(&oset, sigprocmask_cleanup);
148123679Sgibbs		while ((len = xread(f, buf, sizeof(buf))) > 0)
149123679Sgibbs		    (void) xwrite(SHOUT, buf, len);
150123679Sgibbs		cleanup_until(&f);
151123679Sgibbs#ifdef convex
152123679Sgibbs		/* print error in case file is migrated */
153123679Sgibbs		if (len == -1)
154123679Sgibbs		    stderror(ERR_SYSTEM, progname, strerror(errno));
155123679Sgibbs#endif /* convex */
156123679Sgibbs		break;
157123679Sgibbs	    }
158123679Sgibbs	}
159123679Sgibbs    }
160123679Sgibbs    cleanup_until(name);
161123679Sgibbs}
162123679Sgibbs
163123679Sgibbsstatic void
164123679Sgibbs/*ARGSUSED*/
165123679Sgibbscleanf(int snum)
166123679Sgibbs{
167123679Sgibbs    USE(snum);
168123679Sgibbs    if (f != -1)
169	xclose(f);
170    f = -1;
171}
172
173/* these next two are stolen from CMU's man(1) command for looking down
174 * paths.  they are prety straight forward. */
175
176/*
177 * nextslist takes a search list and copies the next path in it
178 * to np.  A null search list entry is expanded to ".".
179 * If there are no entries in the search list, then np will point
180 * to a null string.
181 */
182
183static void
184nextslist(const Char *sl, Char *np)
185{
186    if (!*sl)
187	*np = '\000';
188    else if (*sl == ':') {
189	*np++ = '.';
190	*np = '\000';
191    }
192    else {
193	while (*sl && *sl != ':')
194	    *np++ = *sl++;
195	*np = '\000';
196    }
197}
198
199/*
200 * skipslist returns the pointer to the next entry in the search list.
201 */
202
203static Char *
204skipslist(Char *sl)
205{
206    while (*sl && *sl++ != ':')
207	continue;
208    return (sl);
209}
210