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
28static 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 */
42int
43ex_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 */
123static int
124ex_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 */
165int
166ex_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 */
210int
211ex_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 */
248int
249ex_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 */
287char **
288ex_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