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