| 1 | /*- |
| 2 | * Copyright (c) 1991, 1993 |
| 3 | * The Regents of the University of California. All rights reserved. |
| 4 | * |
| 5 | * Redistribution and use in source and binary forms, with or without |
| 6 | * modification, are permitted provided that the following conditions |
| 7 | * are met: |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * 2. Redistributions in binary form must reproduce the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer in the |
| 12 | * documentation and/or other materials provided with the distribution. |
| 13 | * 3. All advertising materials mentioning features or use of this software |
| 14 | * must display the following acknowledgement: |
| 15 | * This product includes software developed by the University of |
| 16 | * California, Berkeley and its contributors. |
| 17 | * 4. Neither the name of the University nor the names of its contributors |
| 18 | * may be used to endorse or promote products derived from this software |
| 19 | * without specific prior written permission. |
| 20 | * |
| 21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
| 22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
| 25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 31 | * SUCH DAMAGE. |
| 32 | */ |
| 33 | |
| 34 | #ifndef lint |
| 35 | static char sccsid[] = "@(#)ex_args.c 8.13 (Berkeley) 12/20/93"; |
| 36 | #endif /* not lint */ |
| 37 | |
| 38 | #include <sys/types.h> |
| 39 | |
| 40 | #include <errno.h> |
| 41 | #include <stdlib.h> |
| 42 | #include <string.h> |
| 43 | |
| 44 | #include "vi.h" |
| 45 | #include "excmd.h" |
| 46 | |
| 47 | /* |
| 48 | * ex_next -- :next [files] |
| 49 | * Edit the next file, optionally setting the list of files. |
| 50 | * |
| 51 | * !!! |
| 52 | * The :next command behaved differently from the :rewind command in |
| 53 | * historic vi. See nvi/docs/autowrite for details, but the basic |
| 54 | * idea was that it ignored the force flag if the autowrite flag was |
| 55 | * set. This implementation handles them all identically. |
| 56 | */ |
| 57 | int |
| 58 | ex_next(sp, ep, cmdp) |
| 59 | SCR *sp; |
| 60 | EXF *ep; |
| 61 | EXCMDARG *cmdp; |
| 62 | { |
| 63 | ARGS **argv; |
| 64 | FREF *frp; |
| 65 | char *name; |
| 66 | |
| 67 | MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); |
| 68 | |
| 69 | if (cmdp->argc) { |
| 70 | /* Mark all the current files as ignored. */ |
| 71 | for (frp = sp->frefq.cqh_first; |
| 72 | frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) |
| 73 | F_SET(frp, FR_IGNORE); |
| 74 | |
| 75 | /* Add the new files into the file list. */ |
| 76 | for (argv = cmdp->argv; argv[0]->len != 0; ++argv) |
| 77 | if (file_add(sp, NULL, argv[0]->bp, 0) == NULL) |
| 78 | return (1); |
| 79 | |
| 80 | if ((frp = file_first(sp)) == NULL) |
| 81 | return (1); |
| 82 | } else if ((frp = file_next(sp, sp->a_frp)) == NULL) { |
| 83 | msgq(sp, M_ERR, "No more files to edit."); |
| 84 | return (1); |
| 85 | } |
| 86 | |
| 87 | /* |
| 88 | * There's a tricky sequence, where the user edits two files, e.g. |
| 89 | * "x" and "y". While in "x", they do ":e y|:f foo", which changes |
| 90 | * the name of that FRP entry. Then, the :n command finds the file |
| 91 | * "y" with a name change. If the file name has been changed, get |
| 92 | * a new FREF for the original file name, and make it be the one that |
| 93 | * is displayed in the argument list, not the one with the name change. |
| 94 | */ |
| 95 | if (frp->cname != NULL) { |
| 96 | F_SET(frp, FR_IGNORE); |
| 97 | name = frp->name == NULL ? frp->tname : frp->name; |
| 98 | if ((frp = file_add(sp, sp->a_frp, name, 0)) == NULL) |
| 99 | return (1); |
| 100 | } |
| 101 | if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) |
| 102 | return (1); |
| 103 | sp->a_frp = frp; |
| 104 | F_SET(sp, S_FSWITCH); |
| 105 | return (0); |
| 106 | } |
| 107 | |
| 108 | /* |
| 109 | * ex_prev -- :prev |
| 110 | * Edit the previous file. |
| 111 | */ |
| 112 | int |
| 113 | ex_prev(sp, ep, cmdp) |
| 114 | SCR *sp; |
| 115 | EXF *ep; |
| 116 | EXCMDARG *cmdp; |
| 117 | { |
| 118 | FREF *frp; |
| 119 | char *name; |
| 120 | |
| 121 | MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); |
| 122 | |
| 123 | if ((frp = file_prev(sp, sp->a_frp)) == NULL) { |
| 124 | msgq(sp, M_ERR, "No previous files to edit."); |
| 125 | return (1); |
| 126 | } |
| 127 | |
| 128 | /* See comment in ex_next(). */ |
| 129 | if (frp->cname != NULL) { |
| 130 | F_SET(frp, FR_IGNORE); |
| 131 | name = frp->name == NULL ? frp->tname : frp->name; |
| 132 | if ((frp = file_add(sp, frp, name, 0)) == NULL) |
| 133 | return (1); |
| 134 | } |
| 135 | if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) |
| 136 | return (1); |
| 137 | sp->a_frp = frp; |
| 138 | F_SET(sp, S_FSWITCH); |
| 139 | return (0); |
| 140 | } |
| 141 | |
| 142 | /* |
| 143 | * ex_rew -- :rew |
| 144 | * Re-edit the list of files. |
| 145 | */ |
| 146 | int |
| 147 | ex_rew(sp, ep, cmdp) |
| 148 | SCR *sp; |
| 149 | EXF *ep; |
| 150 | EXCMDARG *cmdp; |
| 151 | { |
| 152 | FREF *frp, *tfrp; |
| 153 | |
| 154 | /* |
| 155 | * !!! |
| 156 | * Historic practice -- you can rewind to the current file. |
| 157 | */ |
| 158 | if ((frp = file_first(sp)) == NULL) { |
| 159 | msgq(sp, M_ERR, "No previous files to rewind."); |
| 160 | return (1); |
| 161 | } |
| 162 | |
| 163 | MODIFY_CHECK(sp, ep, F_ISSET(cmdp, E_FORCE)); |
| 164 | |
| 165 | /* |
| 166 | * !!! |
| 167 | * Historic practice, turn off the edited bit. The :next and :prev |
| 168 | * code will discard any name changes, so ignore them here. Start |
| 169 | * at the beginning of the file, too. |
| 170 | */ |
| 171 | for (tfrp = sp->frefq.cqh_first; |
| 172 | tfrp != (FREF *)&sp->frefq; tfrp = tfrp->q.cqe_next) |
| 173 | F_CLR(tfrp, FR_CHANGEWRITE | FR_CURSORSET | FR_EDITED); |
| 174 | |
| 175 | if (file_init(sp, frp, NULL, F_ISSET(cmdp, E_FORCE))) |
| 176 | return (1); |
| 177 | sp->a_frp = frp; |
| 178 | F_SET(sp, S_FSWITCH); |
| 179 | return (0); |
| 180 | } |
| 181 | |
| 182 | /* |
| 183 | * ex_args -- :args |
| 184 | * Display the list of files. |
| 185 | */ |
| 186 | int |
| 187 | ex_args(sp, ep, cmdp) |
| 188 | SCR *sp; |
| 189 | EXF *ep; |
| 190 | EXCMDARG *cmdp; |
| 191 | { |
| 192 | FREF *frp; |
| 193 | int cnt, col, iscur, len, nlen, sep; |
| 194 | char *name; |
| 195 | |
| 196 | /* |
| 197 | * !!! |
| 198 | * Ignore files that aren't in the "argument" list unless they are the |
| 199 | * one we're currently editing. I'm not sure this is right, but the |
| 200 | * historic vi behavior of not showing the current file if it was the |
| 201 | * result of a ":e" command, or if the file name was changed was wrong. |
| 202 | * This is actually pretty tricky, don't modify it without thinking it |
| 203 | * through. There have been a lot of problems in here. |
| 204 | * |
| 205 | * Also, historic practice was to display the original name of the file |
| 206 | * even if the user had used a file command to change the file name. |
| 207 | * Confusing, at best. We show both names: the original as that's what |
| 208 | * the user will get in a next, prev or rewind, and the new one since |
| 209 | * that's what the user is actually editing now. |
| 210 | * |
| 211 | * When we find the "argument" FREF, i.e. the current location in the |
| 212 | * user's argument list, if it's not the same as the current FREF, we |
| 213 | * display the current FREF as following the argument in the list. |
| 214 | * This means that if the user edits three files, "x", "y" and "z", and |
| 215 | * then does a :e command in the file "x" to edit "z", "z" will appear |
| 216 | * in the list twice. |
| 217 | */ |
| 218 | col = len = sep = 0; |
| 219 | for (cnt = 1, frp = sp->frefq.cqh_first; |
| 220 | frp != (FREF *)&sp->frefq; frp = frp->q.cqe_next) { |
| 221 | iscur = 0; |
| 222 | /* |
| 223 | * If the last argument FREF structure, and we're editing |
| 224 | * it, set the current bit. Otherwise, we'll display it, |
| 225 | * then the file we're editing, and the latter will have |
| 226 | * the current bit set. |
| 227 | */ |
| 228 | if (frp == sp->a_frp) { |
| 229 | if (frp == sp->frp && frp->cname == NULL) |
| 230 | iscur = 1; |
| 231 | } else if (F_ISSET(frp, FR_IGNORE)) |
| 232 | continue; |
| 233 | name = frp->name == NULL ? frp->tname : frp->name; |
| 234 | /* |
| 235 | * Mistake. The user edited a temporary file (vi /tmp), then |
| 236 | * switched to another file (:e file). The argument FREF is |
| 237 | * pointing to the temporary file, but it doesn't have a name. |
| 238 | * Gracefully recover through the creative use of goto's. |
| 239 | */ |
| 240 | if (name == NULL) |
| 241 | goto testcur; |
| 242 | extra: nlen = strlen(name); |
| 243 | col += len = nlen + sep + (iscur ? 2 : 0); |
| 244 | if (col >= sp->cols - 1) { |
| 245 | col = len; |
| 246 | sep = 0; |
| 247 | (void)ex_printf(EXCOOKIE, "\n"); |
| 248 | } else if (cnt != 1) { |
| 249 | sep = 1; |
| 250 | (void)ex_printf(EXCOOKIE, " "); |
| 251 | } |
| 252 | ++cnt; |
| 253 | |
| 254 | if (iscur) |
| 255 | (void)ex_printf(EXCOOKIE, "[%s]", name); |
| 256 | else { |
| 257 | (void)ex_printf(EXCOOKIE, "%s", name); |
| 258 | testcur: if (frp == sp->a_frp) { |
| 259 | if (frp != sp->frp) |
| 260 | name = FILENAME(sp->frp); |
| 261 | else |
| 262 | name = frp->cname; |
| 263 | iscur = 1; |
| 264 | goto extra; |
| 265 | } |
| 266 | } |
| 267 | } |
| 268 | /* This should never happen; left in because it's been known to. */ |
| 269 | if (cnt == 1) |
| 270 | (void)ex_printf(EXCOOKIE, "No files.\n"); |
| 271 | else |
| 272 | (void)ex_printf(EXCOOKIE, "\n"); |
| 273 | return (0); |
| 274 | } |