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