Import nvi2 version 2.1.3 to vendor branch
[dragonfly.git] / contrib / nvi2 / ex / ex_init.c
1 /*-
2  * Copyright (c) 1992, 1993, 1994
3  *      The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1992, 1993, 1994, 1995, 1996
5  *      Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9
10 #include "config.h"
11
12 #ifndef lint
13 static const char sccsid[] = "$Id: ex_init.c,v 10.33 2012/04/11 19:12:34 zy Exp $";
14 #endif /* not lint */
15
16 #include <sys/types.h>
17 #include <sys/queue.h>
18 #include <sys/stat.h>
19
20 #include <bitstring.h>
21 #include <fcntl.h>
22 #include <limits.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27
28 #include "../common/common.h"
29 #include "tag.h"
30 #include "pathnames.h"
31
32 enum rc { NOEXIST, NOPERM, RCOK };
33 static enum rc  exrc_isok(SCR *, struct stat *, char *, int, int);
34
35 static int ex_run_file(SCR *, char *);
36
37 /*
38  * ex_screen_copy --
39  *      Copy ex screen.
40  *
41  * PUBLIC: int ex_screen_copy(SCR *, SCR *);
42  */
43 int
44 ex_screen_copy(SCR *orig, SCR *sp)
45 {
46         EX_PRIVATE *oexp, *nexp;
47
48         /* Create the private ex structure. */
49         CALLOC_RET(orig, nexp, EX_PRIVATE *, 1, sizeof(EX_PRIVATE));
50         sp->ex_private = nexp;
51
52         /* Initialize queues. */
53         TAILQ_INIT(nexp->tq);
54         TAILQ_INIT(nexp->tagfq);
55         SLIST_INIT(nexp->cscq);
56
57         if (orig == NULL) {
58         } else {
59                 oexp = EXP(orig);
60
61                 if (oexp->lastbcomm != NULL &&
62                     (nexp->lastbcomm = v_wstrdup(sp, oexp->lastbcomm, 
63                                      STRLEN(oexp->lastbcomm))) == NULL) {
64                         msgq(sp, M_SYSERR, NULL);
65                         return(1);
66                 }
67                 if (ex_tag_copy(orig, sp))
68                         return (1);
69         }
70         return (0);
71 }
72
73 /*
74  * ex_screen_end --
75  *      End a vi screen.
76  *
77  * PUBLIC: int ex_screen_end(SCR *);
78  */
79 int
80 ex_screen_end(SCR *sp)
81 {
82         EX_PRIVATE *exp;
83         int rval;
84
85         if ((exp = EXP(sp)) == NULL)
86                 return (0);
87
88         rval = 0;
89
90         /* Close down script connections. */
91         if (F_ISSET(sp, SC_SCRIPT) && sscr_end(sp))
92                 rval = 1;
93
94         if (argv_free(sp))
95                 rval = 1;
96
97         if (exp->ibp != NULL)
98                 free(exp->ibp);
99
100         if (exp->lastbcomm != NULL)
101                 free(exp->lastbcomm);
102
103         if (exp->ibcw.bp1.c != NULL)
104                 free(exp->ibcw.bp1.c);
105
106         if (ex_tag_free(sp))
107                 rval = 1;
108
109         if (cscope_end(sp))
110                 rval = 1;
111
112         /* Free private memory. */
113         free(exp);
114         sp->ex_private = NULL;
115
116         return (rval);
117 }
118
119 /*
120  * ex_optchange --
121  *      Handle change of options for ex.
122  *
123  * PUBLIC: int ex_optchange(SCR *, int, char *, u_long *);
124  */
125 int
126 ex_optchange(SCR *sp, int offset, char *str, u_long *valp)
127 {
128         switch (offset) {
129         case O_TAGS:
130                 return (ex_tagf_alloc(sp, str));
131         }
132         return (0);
133 }
134
135 /*
136  * ex_exrc --
137  *      Read the EXINIT environment variable and the startup exrc files,
138  *      and execute their commands.
139  *
140  * PUBLIC: int ex_exrc(SCR *);
141  */
142 int
143 ex_exrc(SCR *sp)
144 {
145         struct stat hsb, lsb;
146         char *p, *path;
147         CHAR_T *wp;
148         size_t wlen;
149
150         /*
151          * Source the system, environment, $HOME and local .exrc values.
152          * Vi historically didn't check $HOME/.exrc if the environment
153          * variable EXINIT was set.  This is all done before the file is
154          * read in, because things in the .exrc information can set, for
155          * example, the recovery directory.
156          *
157          * !!!
158          * While nvi can handle any of the options settings of historic vi,
159          * the converse is not true.  Since users are going to have to have
160          * files and environmental variables that work with both, we use nvi
161          * versions of both the $HOME and local startup files if they exist,
162          * otherwise the historic ones.
163          *
164          * !!!
165          * For a discussion of permissions and when what .exrc files are
166          * read, see the comment above the exrc_isok() function below.
167          *
168          * !!!
169          * If the user started the historic of vi in $HOME, vi read the user's
170          * .exrc file twice, as $HOME/.exrc and as ./.exrc.  We avoid this, as
171          * it's going to make some commands behave oddly, and I can't imagine
172          * anyone depending on it.
173          */
174         switch (exrc_isok(sp, &hsb, _PATH_SYSEXRC, 1, 0)) {
175         case NOEXIST:
176         case NOPERM:
177                 break;
178         case RCOK:
179                 if (ex_run_file(sp, _PATH_SYSEXRC))
180                         return (1);
181                 break;
182         }
183
184         /* Run the commands. */
185         if (EXCMD_RUNNING(sp->gp))
186                 (void)ex_cmd(sp);
187         if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE))
188                 return (0);
189
190         if ((p = getenv("NEXINIT")) != NULL) {
191                 CHAR2INT(sp, p, strlen(p) + 1, wp, wlen);
192                 if (ex_run_str(sp, "NEXINIT", wp, wlen - 1, 1, 0))
193                         return (1);
194         } else if ((p = getenv("EXINIT")) != NULL) {
195                 CHAR2INT(sp, p, strlen(p) + 1, wp, wlen);
196                 if (ex_run_str(sp, "EXINIT", wp, wlen - 1, 1, 0))
197                         return (1);
198         } else if ((p = getenv("HOME")) != NULL && *p) {
199                 int st = 0;
200
201                 if ((path = join(p, _PATH_NEXRC)) == NULL) {
202                         msgq(sp, M_SYSERR, NULL);
203                         return (1);
204                 }
205                 switch (exrc_isok(sp, &hsb, path, 0, 1)) {
206                 case NOEXIST:
207                         free(path);
208                         if ((path = join(p, _PATH_EXRC)) == NULL) {
209                                 msgq(sp, M_SYSERR, NULL);
210                                 return (1);
211                         }
212                         if (exrc_isok(sp,
213                             &hsb, path, 0, 1) == RCOK && ex_run_file(sp, path))
214                                 st = 1;
215                         break;
216                 case NOPERM:
217                         break;
218                 case RCOK:
219                         if (ex_run_file(sp, path))
220                                 st = 1;
221                         break;
222                 }
223                 free(path);
224                 if (st)
225                         return st;
226         }
227
228         /* Run the commands. */
229         if (EXCMD_RUNNING(sp->gp))
230                 (void)ex_cmd(sp);
231         if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE))
232                 return (0);
233
234         /* Previous commands may have set the exrc option. */
235         if (O_ISSET(sp, O_EXRC)) {
236                 switch (exrc_isok(sp, &lsb, _PATH_NEXRC, 0, 0)) {
237                 case NOEXIST:
238                         if (exrc_isok(sp, &lsb, _PATH_EXRC, 0, 0) == RCOK &&
239                             (lsb.st_dev != hsb.st_dev ||
240                             lsb.st_ino != hsb.st_ino) &&
241                             ex_run_file(sp, _PATH_EXRC))
242                                 return (1);
243                         break;
244                 case NOPERM:
245                         break;
246                 case RCOK:
247                         if ((lsb.st_dev != hsb.st_dev ||
248                             lsb.st_ino != hsb.st_ino) &&
249                             ex_run_file(sp, _PATH_NEXRC))
250                                 return (1);
251                         break;
252                 }
253                 /* Run the commands. */
254                 if (EXCMD_RUNNING(sp->gp))
255                         (void)ex_cmd(sp);
256                 if (F_ISSET(sp, SC_EXIT | SC_EXIT_FORCE))
257                         return (0);
258         }
259
260         return (0);
261 }
262
263 /*
264  * ex_run_file --
265  *      Set up a file of ex commands to run.
266  */
267 static int
268 ex_run_file(SCR *sp, char *name)
269 {
270         EXCMD cmd;
271         CHAR_T *wp;
272         size_t wlen;
273
274         ex_cinit(sp, &cmd, C_SOURCE, 0, OOBLNO, OOBLNO, 0);
275         CHAR2INT(sp, name, strlen(name)+1, wp, wlen);
276         argv_exp0(sp, &cmd, wp, wlen - 1);
277         return (ex_source(sp, &cmd));
278 }
279
280 /*
281  * ex_run_str --
282  *      Set up a string of ex commands to run.
283  *
284  * PUBLIC: int ex_run_str(SCR *, char *, CHAR_T *, size_t, int, int);
285  */
286 int
287 ex_run_str(SCR *sp, char *name, CHAR_T *str, size_t len, int ex_flags, int nocopy)
288 {
289         GS *gp;
290         EXCMD *ecp;
291
292         gp = sp->gp;
293         if (EXCMD_RUNNING(gp)) {
294                 CALLOC_RET(sp, ecp, EXCMD *, 1, sizeof(EXCMD));
295                 SLIST_INSERT_HEAD(gp->ecq, ecp, q);
296         } else
297                 ecp = &gp->excmd;
298
299         F_INIT(ecp,
300             ex_flags ? E_BLIGNORE | E_NOAUTO | E_NOPRDEF | E_VLITONLY : 0);
301
302         if (nocopy)
303                 ecp->cp = str;
304         else
305                 if ((ecp->cp = v_wstrdup(sp, str, len)) == NULL)
306                         return (1);
307         ecp->clen = len;
308
309         if (name == NULL)
310                 ecp->if_name = NULL;
311         else {
312                 if ((ecp->if_name = v_strdup(sp, name, strlen(name))) == NULL)
313                         return (1);
314                 ecp->if_lno = 1;
315                 F_SET(ecp, E_NAMEDISCARD);
316         }
317
318         return (0);
319 }
320
321 /*
322  * exrc_isok --
323  *      Check a .exrc file for source-ability.
324  *
325  * !!!
326  * Historically, vi read the $HOME and local .exrc files if they were owned
327  * by the user's real ID, or the "sourceany" option was set, regardless of
328  * any other considerations.  We no longer support the sourceany option as
329  * it's a security problem of mammoth proportions.  We require the system
330  * .exrc file to be owned by root, the $HOME .exrc file to be owned by the
331  * user's effective ID (or that the user's effective ID be root) and the
332  * local .exrc files to be owned by the user's effective ID.  In all cases,
333  * the file cannot be writeable by anyone other than its owner.
334  *
335  * In O'Reilly ("Learning the VI Editor", Fifth Ed., May 1992, page 106),
336  * it notes that System V release 3.2 and later has an option "[no]exrc".
337  * The behavior is that local .exrc files are read only if the exrc option
338  * is set.  The default for the exrc option was off, so, by default, local
339  * .exrc files were not read.  The problem this was intended to solve was
340  * that System V permitted users to give away files, so there's no possible
341  * ownership or writeability test to ensure that the file is safe.
342  *
343  * POSIX 1003.2-1992 standardized exrc as an option.  It required the exrc
344  * option to be off by default, thus local .exrc files are not to be read
345  * by default.  The Rationale noted (incorrectly) that this was a change
346  * to historic practice, but correctly noted that a default of off improves
347  * system security.  POSIX also required that vi check the effective user
348  * ID instead of the real user ID, which is why we've switched from historic
349  * practice.
350  *
351  * We initialize the exrc variable to off.  If it's turned on by the system
352  * or $HOME .exrc files, and the local .exrc file passes the ownership and
353  * writeability tests, then we read it.  This breaks historic 4BSD practice,
354  * but it gives us a measure of security on systems where users can give away
355  * files.
356  */
357 static enum rc
358 exrc_isok(SCR *sp, struct stat *sbp, char *path, int rootown, int rootid)
359 {
360         enum { ROOTOWN, OWN, WRITER } etype;
361         uid_t euid;
362         int nf1, nf2;
363         char *a, *b, *buf;
364
365         /* Check for the file's existence. */
366         if (stat(path, sbp))
367                 return (NOEXIST);
368
369         /* Check ownership permissions. */
370         euid = geteuid();
371         if (!(rootown && sbp->st_uid == 0) &&
372             !(rootid && euid == 0) && sbp->st_uid != euid) {
373                 etype = rootown ? ROOTOWN : OWN;
374                 goto denied;
375         }
376
377         /* Check writeability. */
378         if (sbp->st_mode & (S_IWGRP | S_IWOTH)) {
379                 etype = WRITER;
380                 goto denied;
381         }
382         return (RCOK);
383
384 denied: a = msg_print(sp, path, &nf1);
385         if (strchr(path, '/') == NULL && (buf = getcwd(NULL, 0)) != NULL) {
386                 char *p;
387
388                 b = msg_print(sp, buf, &nf2);
389                 if ((p = join(b, a)) == NULL) {
390                         msgq(sp, M_SYSERR, NULL);
391                         goto err;
392                 }
393                 switch (etype) {
394                 case ROOTOWN:
395                         msgq(sp, M_ERR,
396                             "128|%s: not sourced: not owned by you or root", p);
397                         break;
398                 case OWN:
399                         msgq(sp, M_ERR,
400                             "129|%s: not sourced: not owned by you", p);
401                         break;
402                 case WRITER:
403                         msgq(sp, M_ERR,
404     "130|%s: not sourced: writeable by a user other than the owner", p);
405                         break;
406                 }
407                 free(p);
408 err:            free(buf);
409                 if (nf2)
410                         FREE_SPACE(sp, b, 0);
411         } else
412                 switch (etype) {
413                 case ROOTOWN:
414                         msgq(sp, M_ERR,
415                             "128|%s: not sourced: not owned by you or root", a);
416                         break;
417                 case OWN:
418                         msgq(sp, M_ERR,
419                             "129|%s: not sourced: not owned by you", a);
420                         break;
421                 case WRITER:
422                         msgq(sp, M_ERR,
423             "130|%s: not sourced: writeable by a user other than the owner", a);
424                         break;
425                 }
426
427         if (nf1)
428                 FREE_SPACE(sp, a, 0);
429         return (NOPERM);
430 }