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
ex_next(SCR * sp,EXCMD * cmdp)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
ex_N_next(SCR * sp,EXCMD * cmdp)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
ex_prev(SCR * sp,EXCMD * cmdp)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
ex_rew(SCR * sp,EXCMD * cmdp)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
ex_args(SCR * sp,EXCMD * cmdp)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 **
ex_buildargv(SCR * sp,EXCMD * cmdp,char * name)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