| File: | src/usr.bin/vi/build/../ex/ex_read.c |
| Warning: | line 280, column 7 Branch condition evaluates to a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* $OpenBSD: ex_read.c,v 1.14 2017/04/18 01:45:35 deraadt Exp $ */ | |||
| 2 | ||||
| 3 | /*- | |||
| 4 | * Copyright (c) 1992, 1993, 1994 | |||
| 5 | * The Regents of the University of California. All rights reserved. | |||
| 6 | * Copyright (c) 1992, 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/stat.h> | |||
| 17 | #include <sys/time.h> | |||
| 18 | ||||
| 19 | #include <bitstring.h> | |||
| 20 | #include <ctype.h> | |||
| 21 | #include <errno(*__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 | /* | |||
| 31 | * ex_read -- :read [file] | |||
| 32 | * :read [!cmd] | |||
| 33 | * Read from a file or utility. | |||
| 34 | * | |||
| 35 | * !!! | |||
| 36 | * Historical vi wouldn't undo a filter read, for no apparent reason. | |||
| 37 | * | |||
| 38 | * PUBLIC: int ex_read(SCR *, EXCMD *); | |||
| 39 | */ | |||
| 40 | int | |||
| 41 | ex_read(SCR *sp, EXCMD *cmdp) | |||
| 42 | { | |||
| 43 | enum { R_ARG, R_EXPANDARG, R_FILTER } which; | |||
| 44 | struct stat sb; | |||
| 45 | CHAR_T *arg, *name; | |||
| 46 | EX_PRIVATE *exp; | |||
| 47 | FILE *fp; | |||
| 48 | FREF *frp; | |||
| 49 | GS *gp; | |||
| 50 | MARK rm; | |||
| 51 | recno_t nlines; | |||
| ||||
| 52 | size_t arglen; | |||
| 53 | int argc, rval; | |||
| 54 | char *p; | |||
| 55 | ||||
| 56 | gp = sp->gp; | |||
| 57 | ||||
| 58 | /* | |||
| 59 | * 0 args: read the current pathname. | |||
| 60 | * 1 args: check for "read !arg". | |||
| 61 | */ | |||
| 62 | switch (cmdp->argc) { | |||
| 63 | case 0: | |||
| 64 | which = R_ARG; | |||
| 65 | arg = NULL((void *)0); /* unused */ | |||
| 66 | arglen = 0; /* unused */ | |||
| 67 | break; | |||
| 68 | case 1: | |||
| 69 | arg = cmdp->argv[0]->bp; | |||
| 70 | arglen = cmdp->argv[0]->len; | |||
| 71 | if (*arg == '!') { | |||
| 72 | ++arg; | |||
| 73 | --arglen; | |||
| 74 | which = R_FILTER; | |||
| 75 | ||||
| 76 | /* Secure means no shell access. */ | |||
| 77 | if (O_ISSET(sp, O_SECURE)((((&(((sp)))->opts[(((O_SECURE)))])->flags) & ( (0x01))) ? (((sp)))->gp->opts[(((sp)))->opts[(((O_SECURE )))].o_cur.val].o_cur.val : (((sp)))->opts[(((O_SECURE)))] .o_cur.val)) { | |||
| 78 | ex_emsg(sp, cmdp->cmd->name, EXM_SECURE_F); | |||
| 79 | return (1); | |||
| 80 | } | |||
| 81 | } else | |||
| 82 | which = R_EXPANDARG; | |||
| 83 | break; | |||
| 84 | default: | |||
| 85 | abort(); | |||
| 86 | /* NOTREACHED */ | |||
| 87 | } | |||
| 88 | ||||
| 89 | /* Load a temporary file if no file being edited. */ | |||
| 90 | if (sp->ep == NULL((void *)0)) { | |||
| 91 | if ((frp = file_add(sp, NULL((void *)0))) == NULL((void *)0)) | |||
| 92 | return (1); | |||
| 93 | if (file_init(sp, frp, NULL((void *)0), 0)) | |||
| 94 | return (1); | |||
| 95 | } | |||
| 96 | ||||
| 97 | switch (which) { | |||
| 98 | case R_FILTER: | |||
| 99 | /* | |||
| 100 | * File name and bang expand the user's argument. If | |||
| 101 | * we don't get an additional argument, it's illegal. | |||
| 102 | */ | |||
| 103 | argc = cmdp->argc; | |||
| 104 | if (argv_exp1(sp, cmdp, arg, arglen, 1)) | |||
| 105 | return (1); | |||
| 106 | if (argc == cmdp->argc) { | |||
| 107 | ex_emsg(sp, cmdp->cmd->usage, EXM_USAGE); | |||
| 108 | return (1); | |||
| 109 | } | |||
| 110 | argc = cmdp->argc - 1; | |||
| 111 | ||||
| 112 | /* Set the last bang command. */ | |||
| 113 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 114 | free(exp->lastbcomm); | |||
| 115 | if ((exp->lastbcomm = | |||
| 116 | strdup(cmdp->argv[argc]->bp)) == NULL((void *)0)) { | |||
| 117 | msgq(sp, M_SYSERR, NULL((void *)0)); | |||
| 118 | return (1); | |||
| 119 | } | |||
| 120 | ||||
| 121 | /* | |||
| 122 | * Vi redisplayed the user's argument if it changed, ex | |||
| 123 | * always displayed a !, plus the user's argument if it | |||
| 124 | * changed. | |||
| 125 | */ | |||
| 126 | if (F_ISSET(sp, SC_VI)(((sp)->flags) & ((0x00000002)))) { | |||
| 127 | if (F_ISSET(cmdp, E_MODIFY)(((cmdp)->flags) & ((0x00200000)))) | |||
| 128 | (void)vs_update(sp, "!", cmdp->argv[argc]->bp); | |||
| 129 | } else { | |||
| 130 | if (F_ISSET(cmdp, E_MODIFY)(((cmdp)->flags) & ((0x00200000)))) | |||
| 131 | (void)ex_printf(sp, | |||
| 132 | "!%s\n", cmdp->argv[argc]->bp); | |||
| 133 | else | |||
| 134 | (void)ex_puts(sp, "!\n"); | |||
| 135 | (void)ex_fflush(sp); | |||
| 136 | } | |||
| 137 | ||||
| 138 | /* | |||
| 139 | * Historically, filter reads as the first ex command didn't | |||
| 140 | * wait for the user. If SC_SCR_EXWROTE not already set, set | |||
| 141 | * the don't-wait flag. | |||
| 142 | */ | |||
| 143 | if (!F_ISSET(sp, SC_SCR_EXWROTE)(((sp)->flags) & ((0x00000010)))) | |||
| 144 | F_SET(sp, SC_EX_WAIT_NO)(((sp)->flags) |= ((0x00080000))); | |||
| 145 | ||||
| 146 | /* | |||
| 147 | * Switch into ex canonical mode. The reason to restore the | |||
| 148 | * original terminal modes for read filters is so that users | |||
| 149 | * can do things like ":r! cat /dev/tty". | |||
| 150 | * | |||
| 151 | * !!! | |||
| 152 | * We do not output an extra <newline>, so that we don't touch | |||
| 153 | * the screen on a normal read. | |||
| 154 | */ | |||
| 155 | if (F_ISSET(sp, SC_VI)(((sp)->flags) & ((0x00000002)))) { | |||
| 156 | if (gp->scr_screen(sp, SC_EX0x00000001)) { | |||
| 157 | ex_emsg(sp, cmdp->cmd->name, EXM_NOCANON_F); | |||
| 158 | return (1); | |||
| 159 | } | |||
| 160 | /* | |||
| 161 | * !!! | |||
| 162 | * Historically, the read command doesn't switch to | |||
| 163 | * the alternate X11 xterm screen, if doing a filter | |||
| 164 | * read -- don't set SA_ALTERNATE. | |||
| 165 | */ | |||
| 166 | F_SET(sp, SC_SCR_EX | SC_SCR_EXWROTE)(((sp)->flags) |= ((0x00000004 | 0x00000010))); | |||
| 167 | } | |||
| 168 | ||||
| 169 | if (ex_filter(sp, cmdp, &cmdp->addr1, | |||
| 170 | NULL((void *)0), &rm, cmdp->argv[argc]->bp, FILTER_READ)) | |||
| 171 | return (1); | |||
| 172 | ||||
| 173 | /* The filter version of read set the autoprint flag. */ | |||
| 174 | F_SET(cmdp, E_AUTOPRINT)(((cmdp)->flags) |= ((0x00000040))); | |||
| 175 | ||||
| 176 | /* | |||
| 177 | * If in vi mode, move to the first nonblank. Might have | |||
| 178 | * switched into ex mode, so saved the original SC_VI value. | |||
| 179 | */ | |||
| 180 | sp->lno = rm.lno; | |||
| 181 | if (F_ISSET(sp, SC_VI)(((sp)->flags) & ((0x00000002)))) { | |||
| 182 | sp->cno = 0; | |||
| 183 | (void)nonblank(sp, sp->lno, &sp->cno); | |||
| 184 | } | |||
| 185 | return (0); | |||
| 186 | case R_ARG: | |||
| 187 | name = sp->frp->name; | |||
| 188 | break; | |||
| 189 | case R_EXPANDARG: | |||
| 190 | if (argv_exp2(sp, cmdp, arg, arglen)) | |||
| 191 | return (1); | |||
| 192 | /* | |||
| 193 | * 0 args: impossible. | |||
| 194 | * 1 args: impossible (I hope). | |||
| 195 | * 2 args: read it. | |||
| 196 | * >2 args: object, too many args. | |||
| 197 | * | |||
| 198 | * The 1 args case depends on the argv_sexp() function refusing | |||
| 199 | * to return success without at least one non-blank character. | |||
| 200 | */ | |||
| 201 | switch (cmdp->argc) { | |||
| 202 | case 0: | |||
| 203 | case 1: | |||
| 204 | abort(); | |||
| 205 | /* NOTREACHED */ | |||
| 206 | case 2: | |||
| 207 | name = cmdp->argv[1]->bp; | |||
| 208 | /* | |||
| 209 | * !!! | |||
| 210 | * Historically, the read and write commands renamed | |||
| 211 | * "unnamed" files, or, if the file had a name, set | |||
| 212 | * the alternate file name. | |||
| 213 | */ | |||
| 214 | if (F_ISSET(sp->frp, FR_TMPFILE)(((sp->frp)->flags) & ((0x0080))) && | |||
| 215 | !F_ISSET(sp->frp, FR_EXNAMED)(((sp->frp)->flags) & ((0x0004)))) { | |||
| 216 | if ((p = v_strdup(sp, cmdp->argv[1]->bp, | |||
| 217 | cmdp->argv[1]->len)) != NULL((void *)0)) { | |||
| 218 | free(sp->frp->name); | |||
| 219 | sp->frp->name = p; | |||
| 220 | } | |||
| 221 | /* | |||
| 222 | * The file has a real name, it's no longer a | |||
| 223 | * temporary, clear the temporary file flags. | |||
| 224 | */ | |||
| 225 | F_CLR(sp->frp, FR_TMPEXIT | FR_TMPFILE)(((sp->frp)->flags) &= ~((0x0040 | 0x0080))); | |||
| 226 | F_SET(sp->frp, FR_NAMECHANGE | FR_EXNAMED)(((sp->frp)->flags) |= ((0x0008 | 0x0004))); | |||
| 227 | ||||
| 228 | /* Notify the screen. */ | |||
| 229 | (void)sp->gp->scr_rename(sp, sp->frp->name, 1); | |||
| 230 | } else | |||
| 231 | set_alt_name(sp, name); | |||
| 232 | break; | |||
| 233 | default: | |||
| 234 | ex_emsg(sp, cmdp->argv[0]->bp, EXM_FILECOUNT); | |||
| 235 | return (1); | |||
| 236 | ||||
| 237 | } | |||
| 238 | break; | |||
| 239 | default: | |||
| 240 | abort(); | |||
| 241 | /* NOTREACHED */ | |||
| 242 | } | |||
| 243 | ||||
| 244 | /* | |||
| 245 | * !!! | |||
| 246 | * Historically, vi did not permit reads from non-regular files, nor | |||
| 247 | * did it distinguish between "read !" and "read!", so there was no | |||
| 248 | * way to "force" it. We permit reading from named pipes too, since | |||
| 249 | * they didn't exist when the original implementation of vi was done | |||
| 250 | * and they seem a reasonable addition. | |||
| 251 | */ | |||
| 252 | if ((fp = fopen(name, "r")) == NULL((void *)0) || fstat(fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), &sb)) { | |||
| 253 | msgq_str(sp, M_SYSERR, name, "%s"); | |||
| 254 | return (1); | |||
| 255 | } | |||
| 256 | if (!S_ISFIFO(sb.st_mode)((sb.st_mode & 0170000) == 0010000) && !S_ISREG(sb.st_mode)((sb.st_mode & 0170000) == 0100000)) { | |||
| 257 | (void)fclose(fp); | |||
| 258 | msgq(sp, M_ERR, | |||
| 259 | "Only regular files and named pipes may be read"); | |||
| 260 | return (1); | |||
| 261 | } | |||
| 262 | ||||
| 263 | /* Try and get a lock. */ | |||
| 264 | if (file_lock(sp, NULL((void *)0), NULL((void *)0), fileno(fp)(!__isthreaded ? ((fp)->_file) : (fileno)(fp)), 0) == LOCK_UNAVAIL) | |||
| 265 | msgq(sp, M_ERR, "%s: read lock was unavailable", name); | |||
| 266 | ||||
| 267 | rval = ex_readfp(sp, name, fp, &cmdp->addr1, &nlines, 0); | |||
| 268 | ||||
| 269 | /* | |||
| 270 | * In vi, set the cursor to the first line read in, if anything read | |||
| 271 | * in, otherwise, the address. (Historic vi set it to the line after | |||
| 272 | * the address regardless, but since that line may not exist we don't | |||
| 273 | * bother.) | |||
| 274 | * | |||
| 275 | * In ex, set the cursor to the last line read in, if anything read in, | |||
| 276 | * otherwise, the address. | |||
| 277 | */ | |||
| 278 | if (F_ISSET(sp, SC_VI)(((sp)->flags) & ((0x00000002)))) { | |||
| 279 | sp->lno = cmdp->addr1.lno; | |||
| 280 | if (nlines) | |||
| ||||
| 281 | ++sp->lno; | |||
| 282 | } else | |||
| 283 | sp->lno = cmdp->addr1.lno + nlines; | |||
| 284 | return (rval); | |||
| 285 | } | |||
| 286 | ||||
| 287 | /* | |||
| 288 | * ex_readfp -- | |||
| 289 | * Read lines into the file. | |||
| 290 | * | |||
| 291 | * PUBLIC: int ex_readfp(SCR *, char *, FILE *, MARK *, recno_t *, int); | |||
| 292 | */ | |||
| 293 | int | |||
| 294 | ex_readfp(SCR *sp, char *name, FILE *fp, MARK *fm, recno_t *nlinesp, | |||
| 295 | int silent) | |||
| 296 | { | |||
| 297 | EX_PRIVATE *exp; | |||
| 298 | GS *gp; | |||
| 299 | recno_t lcnt, lno; | |||
| 300 | size_t len; | |||
| 301 | u_long ccnt; /* XXX: can't print off_t portably. */ | |||
| 302 | int nf, rval; | |||
| 303 | char *p; | |||
| 304 | ||||
| 305 | gp = sp->gp; | |||
| 306 | exp = EXP(sp)((EX_PRIVATE *)((sp)->ex_private)); | |||
| 307 | ||||
| 308 | /* | |||
| 309 | * Add in the lines from the output. Insertion starts at the line | |||
| 310 | * following the address. | |||
| 311 | */ | |||
| 312 | ccnt = 0; | |||
| 313 | lcnt = 0; | |||
| 314 | p = "Reading..."; | |||
| 315 | for (lno = fm->lno; !ex_getline(sp, fp, &len); ++lno, ++lcnt) { | |||
| 316 | if ((lcnt + 1) % INTERRUPT_CHECK100 == 0) { | |||
| 317 | if (INTERRUPTED(sp)(((((sp)->gp)->flags) & ((0x0004))) || (!v_event_get ((sp), ((void *)0), 0, 0x001) && ((((sp)->gp)-> flags) & ((0x0004)))))) | |||
| 318 | break; | |||
| 319 | if (!silent) { | |||
| 320 | gp->scr_busy(sp, p, | |||
| 321 | p == NULL((void *)0) ? BUSY_UPDATE : BUSY_ON); | |||
| 322 | p = NULL((void *)0); | |||
| 323 | } | |||
| 324 | } | |||
| 325 | if (db_append(sp, 1, lno, exp->ibp, len)) | |||
| 326 | goto err; | |||
| 327 | ccnt += len; | |||
| 328 | } | |||
| 329 | ||||
| 330 | if (ferror(fp)(!__isthreaded ? (((fp)->_flags & 0x0040) != 0) : (ferror )(fp)) || fclose(fp)) | |||
| 331 | goto err; | |||
| 332 | ||||
| 333 | /* Return the number of lines read in. */ | |||
| 334 | if (nlinesp != NULL((void *)0)) | |||
| 335 | *nlinesp = lcnt; | |||
| 336 | ||||
| 337 | if (!silent) { | |||
| 338 | p = msg_print(sp, name, &nf); | |||
| 339 | msgq(sp, M_INFO, | |||
| 340 | "%s: %lu lines, %lu characters", p, lcnt, ccnt); | |||
| 341 | if (nf) | |||
| 342 | FREE_SPACE(sp, p, 0){ GS *L__gp = (sp) == ((void *)0) ? ((void *)0) : (sp)->gp ; if (L__gp != ((void *)0) && (p) == L__gp->tmp_bp ) (((L__gp)->flags) &= ~((0x0100))); else free(p); }; | |||
| 343 | } | |||
| 344 | ||||
| 345 | rval = 0; | |||
| 346 | if (0) { | |||
| 347 | err: msgq_str(sp, M_SYSERR, name, "%s"); | |||
| 348 | (void)fclose(fp); | |||
| 349 | rval = 1; | |||
| 350 | } | |||
| 351 | ||||
| 352 | if (!silent
| |||
| 353 | gp->scr_busy(sp, NULL((void *)0), BUSY_OFF); | |||
| 354 | return (rval); | |||
| 355 | } |