xref: /openbsd/usr.bin/vi/ex/ex_args.c (revision 4bdff4be)
1 /*	$OpenBSD: ex_args.c,v 1.12 2016/01/06 22:28:52 millert Exp $	*/
2 
3 /*-
4  * Copyright (c) 1991, 1993, 1994
5  *	The Regents of the University of California.  All rights reserved.
6  * Copyright (c) 1991, 1993, 1994, 1995, 1996
7  *	Keith Bostic.  All rights reserved.
8  *
9  * See the LICENSE file for redistribution information.
10  */
11 
12 #include "config.h"
13 
14 #include <sys/types.h>
15 #include <sys/queue.h>
16 #include <sys/time.h>
17 
18 #include <bitstring.h>
19 #include <errno.h>
20 #include <limits.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 
25 #include "../common/common.h"
26 #include "../vi/vi.h"
27 
28 static int ex_N_next(SCR *, EXCMD *);
29 
30 /*
31  * ex_next -- :next [+cmd] [files]
32  *	Edit the next file, optionally setting the list of files.
33  *
34  * !!!
35  * The :next command behaved differently from the :rewind command in
36  * historic vi.  See nvi/docs/autowrite for details, but the basic
37  * idea was that it ignored the force flag if the autowrite flag was
38  * set.  This implementation handles them all identically.
39  *
40  * PUBLIC: int ex_next(SCR *, EXCMD *);
41  */
42 int
43 ex_next(SCR *sp, EXCMD *cmdp)
44 {
45 	ARGS **argv;
46 	FREF *frp;
47 	int noargs;
48 	char **ap;
49 
50 	/* Check for file to move to. */
51 	if (cmdp->argc == 0 && (sp->cargv == NULL || sp->cargv[1] == NULL)) {
52 		msgq(sp, M_ERR, "No more files to edit");
53 		return (1);
54 	}
55 
56 	if (F_ISSET(cmdp, E_NEWSCREEN)) {
57 		/* By default, edit the next file in the old argument list. */
58 		if (cmdp->argc == 0) {
59 			if (argv_exp0(sp,
60 			    cmdp, sp->cargv[1], strlen(sp->cargv[1])))
61 				return (1);
62 			return (ex_edit(sp, cmdp));
63 		}
64 		return (ex_N_next(sp, cmdp));
65 	}
66 
67 	/* Check modification. */
68 	if (file_m1(sp,
69 	    FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE))
70 		return (1);
71 
72 	/* Any arguments are a replacement file list. */
73 	if (cmdp->argc) {
74 		/* Free the current list. */
75 		if (!F_ISSET(sp, SC_ARGNOFREE) && sp->argv != NULL) {
76 			for (ap = sp->argv; *ap != NULL; ++ap)
77 				free(*ap);
78 			free(sp->argv);
79 		}
80 		F_CLR(sp, SC_ARGNOFREE | SC_ARGRECOVER);
81 		sp->cargv = NULL;
82 
83 		/* Create a new list. */
84 		CALLOC_RET(sp,
85 		    sp->argv, cmdp->argc + 1, sizeof(char *));
86 		for (ap = sp->argv,
87 		    argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv)
88 			if ((*ap =
89 			    v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL)
90 				return (1);
91 		*ap = NULL;
92 
93 		/* Switch to the first file. */
94 		sp->cargv = sp->argv;
95 		if ((frp = file_add(sp, *sp->cargv)) == NULL)
96 			return (1);
97 		noargs = 0;
98 
99 		/* Display a file count with the welcome message. */
100 		F_SET(sp, SC_STATUS_CNT);
101 	} else {
102 		if ((frp = file_add(sp, sp->cargv[1])) == NULL)
103 			return (1);
104 		if (F_ISSET(sp, SC_ARGRECOVER))
105 			F_SET(frp, FR_RECOVER);
106 		noargs = 1;
107 	}
108 
109 	if (file_init(sp, frp, NULL, FS_SETALT |
110 	    (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
111 		return (1);
112 	if (noargs)
113 		++sp->cargv;
114 
115 	F_SET(sp, SC_FSWITCH);
116 	return (0);
117 }
118 
119 /*
120  * ex_N_next --
121  *	New screen version of ex_next.
122  */
123 static int
124 ex_N_next(SCR *sp, EXCMD *cmdp)
125 {
126 	SCR *new;
127 	FREF *frp;
128 
129 	/* Get a new screen. */
130 	if (screen_init(sp->gp, sp, &new))
131 		return (1);
132 	if (vs_split(sp, new, 0)) {
133 		(void)screen_end(new);
134 		return (1);
135 	}
136 
137 	/* Get a backing file. */
138 	if ((frp = file_add(new, cmdp->argv[0]->bp)) == NULL ||
139 	    file_init(new, frp, NULL,
140 	    (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0))) {
141 		(void)vs_discard(new, NULL);
142 		(void)screen_end(new);
143 		return (1);
144 	}
145 
146 	/* The arguments are a replacement file list. */
147 	new->cargv = new->argv = ex_buildargv(sp, cmdp, NULL);
148 
149 	/* Display a file count with the welcome message. */
150 	F_SET(new, SC_STATUS_CNT);
151 
152 	/* Set up the switch. */
153 	sp->nextdisp = new;
154 	F_SET(sp, SC_SSWITCH);
155 
156 	return (0);
157 }
158 
159 /*
160  * ex_prev -- :prev
161  *	Edit the previous file.
162  *
163  * PUBLIC: int ex_prev(SCR *, EXCMD *);
164  */
165 int
166 ex_prev(SCR *sp, EXCMD *cmdp)
167 {
168 	FREF *frp;
169 
170 	if (sp->cargv == sp->argv) {
171 		msgq(sp, M_ERR, "No previous files to edit");
172 		return (1);
173 	}
174 
175 	if (F_ISSET(cmdp, E_NEWSCREEN)) {
176 		if (argv_exp0(sp, cmdp, sp->cargv[-1], strlen(sp->cargv[-1])))
177 			return (1);
178 		return (ex_edit(sp, cmdp));
179 	}
180 
181 	if (file_m1(sp,
182 	    FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE))
183 		return (1);
184 
185 	if ((frp = file_add(sp, sp->cargv[-1])) == NULL)
186 		return (1);
187 
188 	if (file_init(sp, frp, NULL, FS_SETALT |
189 	    (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
190 		return (1);
191 	--sp->cargv;
192 
193 	F_SET(sp, SC_FSWITCH);
194 	return (0);
195 }
196 
197 /*
198  * ex_rew -- :rew
199  *	Re-edit the list of files.
200  *
201  * !!!
202  * Historic practice was that all files would start editing at the beginning
203  * of the file.  We don't get this right because we may have multiple screens
204  * and we can't clear the FR_CURSORSET bit for a single screen.  I don't see
205  * anyone noticing, but if they do, we'll have to put information into the SCR
206  * structure so we can keep track of it.
207  *
208  * PUBLIC: int ex_rew(SCR *, EXCMD *);
209  */
210 int
211 ex_rew(SCR *sp, EXCMD *cmdp)
212 {
213 	FREF *frp;
214 
215 	/*
216 	 * !!!
217 	 * Historic practice -- you can rewind to the current file.
218 	 */
219 	if (sp->argv == NULL) {
220 		msgq(sp, M_ERR, "No previous files to rewind");
221 		return (1);
222 	}
223 
224 	if (file_m1(sp,
225 	    FL_ISSET(cmdp->iflags, E_C_FORCE), FS_ALL | FS_POSSIBLE))
226 		return (1);
227 
228 	/* Switch to the first one. */
229 	sp->cargv = sp->argv;
230 	if ((frp = file_add(sp, *sp->cargv)) == NULL)
231 		return (1);
232 	if (file_init(sp, frp, NULL, FS_SETALT |
233 	    (FL_ISSET(cmdp->iflags, E_C_FORCE) ? FS_FORCE : 0)))
234 		return (1);
235 
236 	/* Switch and display a file count with the welcome message. */
237 	F_SET(sp, SC_FSWITCH | SC_STATUS_CNT);
238 
239 	return (0);
240 }
241 
242 /*
243  * ex_args -- :args
244  *	Display the list of files.
245  *
246  * PUBLIC: int ex_args(SCR *, EXCMD *);
247  */
248 int
249 ex_args(SCR *sp, EXCMD *cmdp)
250 {
251 	int cnt, col, len, sep;
252 	char **ap;
253 
254 	if (sp->argv == NULL) {
255 		(void)msgq(sp, M_ERR, "No file list to display");
256 		return (0);
257 	}
258 
259 	col = len = sep = 0;
260 	for (cnt = 1, ap = sp->argv; *ap != NULL; ++ap) {
261 		col += len = strlen(*ap) + sep + (ap == sp->cargv ? 2 : 0);
262 		if (col >= sp->cols - 1) {
263 			col = len;
264 			sep = 0;
265 			(void)ex_puts(sp, "\n");
266 		} else if (cnt != 1) {
267 			sep = 1;
268 			(void)ex_puts(sp, " ");
269 		}
270 		++cnt;
271 
272 		(void)ex_printf(sp, "%s%s%s", ap == sp->cargv ? "[" : "",
273 		    *ap, ap == sp->cargv ? "]" : "");
274 		if (INTERRUPTED(sp))
275 			break;
276 	}
277 	(void)ex_puts(sp, "\n");
278 	return (0);
279 }
280 
281 /*
282  * ex_buildargv --
283  *	Build a new file argument list.
284  *
285  * PUBLIC: char **ex_buildargv(SCR *, EXCMD *, char *);
286  */
287 char **
288 ex_buildargv(SCR *sp, EXCMD *cmdp, char *name)
289 {
290 	ARGS **argv;
291 	int argc;
292 	char **ap, **s_argv;
293 
294 	argc = cmdp == NULL ? 1 : cmdp->argc;
295 	CALLOC(sp, s_argv, argc + 1, sizeof(char *));
296 	if ((ap = s_argv) == NULL)
297 		return (NULL);
298 
299 	if (cmdp == NULL) {
300 		if ((*ap = v_strdup(sp, name, strlen(name))) == NULL) {
301 			free(s_argv);
302 			return (NULL);
303 		}
304 		++ap;
305 	} else
306 		for (argv = cmdp->argv; argv[0]->len != 0; ++ap, ++argv)
307 			if ((*ap =
308 			    v_strdup(sp, argv[0]->bp, argv[0]->len)) == NULL) {
309 				while (--ap >= s_argv)
310 					free(*ap);
311 				free(s_argv);
312 				return (NULL);
313 			}
314 	*ap = NULL;
315 	return (s_argv);
316 }
317