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 26 static 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 */ 40 int 41 ex_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 */ 126 static int 127 ex_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 */ 171 int 172 ex_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 */ 220 int 221 ex_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 */ 258 int 259 ex_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 */ 299 char ** 300 ex_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