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