Bug Summary

File:src/usr.bin/mg/region.c
Warning:line 290, column 8
Value stored to 'prefix' during its initialization is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name region.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.bin/mg/obj -resource-dir /usr/local/llvm16/lib/clang/16 -D REGEX -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.bin/mg/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.bin/mg/region.c
1/* $OpenBSD: region.c,v 1.44 2023/03/28 14:47:28 op Exp $ */
2
3/* This file is in the public domain. */
4
5/*
6 * Region based commands.
7 * The routines in this file deal with the region, that magic space between
8 * "." and mark. Some functions are commands. Some functions are just for
9 * internal use.
10 */
11
12#include <sys/queue.h>
13#include <sys/socket.h>
14#include <sys/types.h>
15#include <sys/wait.h>
16#include <errno(*__errno()).h>
17#include <fcntl.h>
18#include <paths.h>
19#include <poll.h>
20#include <signal.h>
21#include <stdio.h>
22#include <stdlib.h>
23#include <string.h>
24#include <unistd.h>
25
26#include "def.h"
27
28#define TIMEOUT10000 10000
29
30static char leftover[BUFSIZ1024];
31
32static int getregion(struct region *);
33static int iomux(int, char * const, int, struct buffer *);
34static int preadin(int, struct buffer *);
35static void pwriteout(int, char **, int *);
36static int setsize(struct region *, RSIZE);
37static int shellcmdoutput(char * const, char * const, int);
38
39/*
40 * Kill the region. Ask "getregion" to figure out the bounds of the region.
41 * Move "." to the start, and kill the characters. Mark is cleared afterwards.
42 */
43int
44killregion(int f, int n)
45{
46 int s;
47 struct region region;
48
49 if ((s = getregion(&region)) != TRUE1)
50 return (s);
51 /* This is a kill-type command, so do magic kill buffer stuff. */
52 if ((lastflag & CFKILL0x0002) == 0)
53 kdelete();
54 thisflag |= CFKILL0x0002;
55 curwp->w_dotp = region.r_linep;
56 curwp->w_doto = region.r_offset;
57 curwp->w_dotline = region.r_lineno;
58 s = ldelete(region.r_size, KFORW0x01 | KREG0x04);
59 clearmark(FFARG7, 0);
60
61 return (s);
62}
63
64/*
65 * Copy all of the characters in the region to the kill buffer,
66 * clearing the mark afterwards.
67 * This is a bit like a kill region followed by a yank.
68 */
69int
70copyregion(int f, int n)
71{
72 struct line *linep;
73 struct region region;
74 int loffs;
75 int s;
76
77 if ((s = getregion(&region)) != TRUE1)
78 return (s);
79
80 /* kill type command */
81 if ((lastflag & CFKILL0x0002) == 0)
82 kdelete();
83 thisflag |= CFKILL0x0002;
84
85 /* current line */
86 linep = region.r_linep;
87
88 /* current offset */
89 loffs = region.r_offset;
90
91 while (region.r_size--) {
92 if (loffs == llength(linep)((linep)->l_used)) { /* End of line. */
93 if ((s = kinsert(*curbp->b_nlchr, KFORW0x01)) != TRUE1)
94 return (s);
95 linep = lforw(linep)((linep)->l_fp);
96 loffs = 0;
97 } else { /* Middle of line. */
98 if ((s = kinsert(lgetc(linep, loffs)(((unsigned char) ((linep)->l_text[(loffs)]))), KFORW0x01)) != TRUE1)
99 return (s);
100 ++loffs;
101 }
102 }
103 clearmark(FFARG7, 0);
104
105 return (TRUE1);
106}
107
108/*
109 * Lower case region. Zap all of the upper case characters in the region to
110 * lower case. Use the region code to set the limits. Scan the buffer, doing
111 * the changes. Call "lchange" to ensure that redisplay is done in all
112 * buffers.
113 */
114int
115lowerregion(int f, int n)
116{
117 struct line *linep;
118 struct region region;
119 int loffs, c, s;
120
121 if ((s = checkdirty(curbp)) != TRUE1)
122 return (s);
123 if (curbp->b_flag & BFREADONLY0x10) {
124 dobeep();
125 ewprintf("Buffer is read-only");
126 return (FALSE0);
127 }
128
129 if ((s = getregion(&region)) != TRUE1)
130 return (s);
131
132 undo_add_change(region.r_linep, region.r_offset, region.r_size);
133
134 lchange(WFFULL0x08);
135 linep = region.r_linep;
136 loffs = region.r_offset;
137 while (region.r_size--) {
138 if (loffs == llength(linep)((linep)->l_used)) {
139 linep = lforw(linep)((linep)->l_fp);
140 loffs = 0;
141 } else {
142 c = lgetc(linep, loffs)(((unsigned char) ((linep)->l_text[(loffs)])));
143 if (ISUPPER(c)((cinfo[((unsigned char) (c))]&0x02)!=0) != FALSE0)
144 lputc(linep, loffs, TOLOWER(c))((linep)->l_text[(loffs)]=(((c)+0x20)));
145 ++loffs;
146 }
147 }
148 return (TRUE1);
149}
150
151/*
152 * Upper case region. Zap all of the lower case characters in the region to
153 * upper case. Use the region code to set the limits. Scan the buffer,
154 * doing the changes. Call "lchange" to ensure that redisplay is done in all
155 * buffers.
156 */
157int
158upperregion(int f, int n)
159{
160 struct line *linep;
161 struct region region;
162 int loffs, c, s;
163
164 if ((s = checkdirty(curbp)) != TRUE1)
165 return (s);
166 if (curbp->b_flag & BFREADONLY0x10) {
167 dobeep();
168 ewprintf("Buffer is read-only");
169 return (FALSE0);
170 }
171 if ((s = getregion(&region)) != TRUE1)
172 return (s);
173
174 undo_add_change(region.r_linep, region.r_offset, region.r_size);
175
176 lchange(WFFULL0x08);
177 linep = region.r_linep;
178 loffs = region.r_offset;
179 while (region.r_size--) {
180 if (loffs == llength(linep)((linep)->l_used)) {
181 linep = lforw(linep)((linep)->l_fp);
182 loffs = 0;
183 } else {
184 c = lgetc(linep, loffs)(((unsigned char) ((linep)->l_text[(loffs)])));
185 if (ISLOWER(c)((cinfo[((unsigned char) (c))]&0x04)!=0) != FALSE0)
186 lputc(linep, loffs, TOUPPER(c))((linep)->l_text[(loffs)]=(((c)-0x20)));
187 ++loffs;
188 }
189 }
190 return (TRUE1);
191}
192
193/*
194 * This routine figures out the bound of the region in the current window,
195 * and stores the results into the fields of the REGION structure. Dot and
196 * mark are usually close together, but I don't know the order, so I scan
197 * outward from dot, in both directions, looking for mark. The size is kept
198 * in a long. At the end, after the size is figured out, it is assigned to
199 * the size field of the region structure. If this assignment loses any bits,
200 * then we print an error. This is "type independent" overflow checking. All
201 * of the callers of this routine should be ready to get an ABORT status,
202 * because I might add a "if regions is big, ask before clobbering" flag.
203 */
204static int
205getregion(struct region *rp)
206{
207 struct line *flp, *blp;
208 long fsize, bsize;
209
210 if (curwp->w_markp == NULL((void *)0)) {
211 dobeep();
212 ewprintf("No mark set in this window");
213 return (FALSE0);
214 }
215
216 /* "r_size" always ok */
217 if (curwp->w_dotp == curwp->w_markp) {
218 rp->r_linep = curwp->w_dotp;
219 rp->r_lineno = curwp->w_dotline;
220 if (curwp->w_doto < curwp->w_marko) {
221 rp->r_offset = curwp->w_doto;
222 rp->r_size = (RSIZE)(curwp->w_marko - curwp->w_doto);
223 } else {
224 rp->r_offset = curwp->w_marko;
225 rp->r_size = (RSIZE)(curwp->w_doto - curwp->w_marko);
226 }
227 return (TRUE1);
228 }
229 /* get region size */
230 flp = blp = curwp->w_dotp;
231 bsize = curwp->w_doto;
232 fsize = llength(flp)((flp)->l_used) - curwp->w_doto + 1;
233 while (lforw(flp)((flp)->l_fp) != curbp->b_headp || lback(blp)((blp)->l_bp) != curbp->b_headp) {
234 if (lforw(flp)((flp)->l_fp) != curbp->b_headp) {
235 flp = lforw(flp)((flp)->l_fp);
236 if (flp == curwp->w_markp) {
237 rp->r_linep = curwp->w_dotp;
238 rp->r_offset = curwp->w_doto;
239 rp->r_lineno = curwp->w_dotline;
240 return (setsize(rp,
241 (RSIZE)(fsize + curwp->w_marko)));
242 }
243 fsize += llength(flp)((flp)->l_used) + 1;
244 }
245 if (lback(blp)((blp)->l_bp) != curbp->b_headp) {
246 blp = lback(blp)((blp)->l_bp);
247 bsize += llength(blp)((blp)->l_used) + 1;
248 if (blp == curwp->w_markp) {
249 rp->r_linep = blp;
250 rp->r_offset = curwp->w_marko;
251 rp->r_lineno = curwp->w_markline;
252 return (setsize(rp,
253 (RSIZE)(bsize - curwp->w_marko)));
254 }
255 }
256 }
257 dobeep();
258 ewprintf("Bug: lost mark");
259 return (FALSE0);
260}
261
262/*
263 * Set size, and check for overflow.
264 */
265static int
266setsize(struct region *rp, RSIZE size)
267{
268 rp->r_size = size;
269 if (rp->r_size != size) {
270 dobeep();
271 ewprintf("Region is too large");
272 return (FALSE0);
273 }
274 return (TRUE1);
275}
276
277#define PREFIXLENGTH40 40
278static char prefix_string[PREFIXLENGTH40] = {'>', '\0'};
279
280/*
281 * Prefix the region with whatever is in prefix_string. Leaves dot at the
282 * beginning of the line after the end of the region. If an argument is
283 * given, prompts for the line prefix string.
284 */
285int
286prefixregion(int f, int n)
287{
288 struct line *first, *last;
289 struct region region;
290 char *prefix = prefix_string;
Value stored to 'prefix' during its initialization is never read
291 int nline;
292 int s;
293
294 if ((s = checkdirty(curbp)) != TRUE1)
295 return (s);
296 if (curbp->b_flag & BFREADONLY0x10) {
297 dobeep();
298 ewprintf("Buffer is read-only");
299 return (FALSE0);
300 }
301 if ((f == TRUE1) && ((s = setprefix(FFRAND8, 1)) != TRUE1))
302 return (s);
303
304 /* get # of lines to affect */
305 if ((s = getregion(&region)) != TRUE1)
306 return (s);
307 first = region.r_linep;
308 last = (first == curwp->w_dotp) ? curwp->w_markp : curwp->w_dotp;
309 for (nline = 1; first != last; nline++)
310 first = lforw(first)((first)->l_fp);
311
312 /* move to beginning of region */
313 curwp->w_dotp = region.r_linep;
314 curwp->w_doto = region.r_offset;
315 curwp->w_dotline = region.r_lineno;
316
317 /* for each line, go to beginning and insert the prefix string */
318 while (nline--) {
319 (void)gotobol(FFRAND8, 1);
320 for (prefix = prefix_string; *prefix; prefix++)
321 (void)linsert(1, *prefix);
322 (void)forwline(FFRAND8, 1);
323 }
324 (void)gotobol(FFRAND8, 1);
325 return (TRUE1);
326}
327
328/*
329 * Set line prefix string. Used by prefixregion.
330 */
331int
332setprefix(int f, int n)
333{
334 char buf[PREFIXLENGTH40], *rep;
335 int retval;
336
337 if (prefix_string[0] == '\0')
338 rep = eread("Prefix string: ", buf, sizeof(buf),
339 EFNEW0x0008 | EFCR0x0010);
340 else
341 rep = eread("Prefix string (default %s): ", buf, sizeof(buf),
342 EFNUL0x0040 | EFNEW0x0008 | EFCR0x0010, prefix_string);
343 if (rep == NULL((void *)0))
344 return (ABORT2);
345 if (rep[0] != '\0') {
346 (void)strlcpy(prefix_string, rep, sizeof(prefix_string));
347 retval = TRUE1;
348 } else if (rep[0] == '\0' && prefix_string[0] != '\0') {
349 /* CR -- use old one */
350 retval = TRUE1;
351 } else
352 retval = FALSE0;
353 return (retval);
354}
355
356int
357region_get_data(struct region *reg, char *buf, int len)
358{
359 int i, off;
360 struct line *lp;
361
362 off = reg->r_offset;
363 lp = reg->r_linep;
364 for (i = 0; i < len; i++) {
365 if (off == llength(lp)((lp)->l_used)) {
366 lp = lforw(lp)((lp)->l_fp);
367 if (lp == curbp->b_headp)
368 break;
369 off = 0;
370 buf[i] = *curbp->b_nlchr;
371 } else {
372 buf[i] = lgetc(lp, off)(((unsigned char) ((lp)->l_text[(off)])));
373 off++;
374 }
375 }
376 buf[i] = '\0';
377 return (i);
378}
379
380void
381region_put_data(const char *buf, int len)
382{
383 int i;
384
385 for (i = 0; buf[i] != '\0' && i < len; i++) {
386 if (buf[i] == *curbp->b_nlchr)
387 lnewline();
388 else
389 linsert(1, buf[i]);
390 }
391}
392
393/*
394 * Mark whole buffer by first traversing to end-of-buffer
395 * and then to beginning-of-buffer. Mark, dot are implicitly
396 * set to eob, bob respectively during traversal.
397 */
398int
399markbuffer(int f, int n)
400{
401 if (gotoeob(f,n) == FALSE0)
402 return (FALSE0);
403 (void) clearmark(f, n);
404 if (gotobob(f,n) == FALSE0)
405 return (FALSE0);
406 return (TRUE1);
407}
408
409/*
410 * Pipe text from current region to external command.
411 */
412int
413piperegion(int f, int n)
414{
415 struct region region;
416 int len;
417 char *cmd, cmdbuf[NFILEN1024], *text;
418
419 /* C-u M-| is not supported yet */
420 if (n > 1)
421 return (ABORT2);
422
423 if (curwp->w_markp == NULL((void *)0)) {
424 dobeep();
425 ewprintf("The mark is not set now, so there is no region");
426 return (FALSE0);
427 }
428
429 if ((cmd = eread("Shell command on region: ", cmdbuf, sizeof(cmdbuf),
430 EFNEW0x0008 | EFCR0x0010)) == NULL((void *)0) || (cmd[0] == '\0'))
431 return (ABORT2);
432
433 if (getregion(&region) != TRUE1)
434 return (FALSE0);
435
436 len = region.r_size;
437
438 if ((text = malloc(len + 1)) == NULL((void *)0)) {
439 dobeep();
440 ewprintf("Cannot allocate memory.");
441 return (FALSE0);
442 }
443
444 region_get_data(&region, text, len);
445
446 return shellcmdoutput(cmd, text, len);
447}
448
449/*
450 * Get command from mini-buffer and execute externally.
451 */
452int
453shellcommand(int f, int n)
454{
455 char *cmd, cmdbuf[NFILEN1024];
456
457 if (n > 1)
458 return (ABORT2);
459
460 if ((cmd = eread("Shell command: ", cmdbuf, sizeof(cmdbuf),
461 EFNEW0x0008 | EFCR0x0010)) == NULL((void *)0) || (cmd[0] == '\0'))
462 return (ABORT2);
463
464 return shellcmdoutput(cmd, NULL((void *)0), 0);
465}
466
467int
468shellcmdoutput(char* const cmd, char* const text, int len)
469{
470 struct buffer *bp;
471 char *argv[] = {NULL((void *)0), "-c", cmd, NULL((void *)0)};
472 char *shellp;
473 int ret;
474
475 bp = bfind("*Shell Command Output*", TRUE1);
476 bp->b_flag |= BFREADONLY0x10;
477 if (bclear(bp) != TRUE1) {
478 free(text);
479 return (FALSE0);
480 }
481
482 if ((shellp = getenv("SHELL")) == NULL((void *)0))
483 shellp = _PATH_BSHELL"/bin/sh";
484
485 if ((argv[0] = strrchr(shellp, '/')) != NULL((void *)0))
486 argv[0]++;
487 else
488 argv[0] = shellp;
489
490 ret = pipeio(shellp, argv, text, len, bp);
491
492 if (ret == TRUE1) {
493 eerase();
494 if (lforw(bp->b_headp)((bp->b_headp)->l_fp) == bp->b_headp)
495 addline(bp, "(Shell command succeeded with no output)")addlinef(bp, "%s", "(Shell command succeeded with no output)"
)
;
496 }
497
498 free(text);
499 return (ret);
500}
501
502/*
503 * Create a socketpair, fork and execv path with argv.
504 * STDIN, STDOUT and STDERR of child process are redirected to socket.
505 * Parent writes len chars from text to socket.
506 */
507int
508pipeio(const char* const path, char* const argv[], char* const text, int len,
509 struct buffer *outbp)
510{
511 int s[2], ret;
512 char *err;
513 pid_t pid;
514
515 if (socketpair(AF_UNIX1, SOCK_STREAM1, PF_UNSPEC0, s) == -1) {
516 dobeep();
517 ewprintf("socketpair error");
518 return (FALSE0);
519 }
520
521 switch((pid = fork())) {
522 case -1:
523 dobeep();
524 ewprintf("Can't fork");
525 return (FALSE0);
526 case 0:
527 /* Child process */
528 close(s[0]);
529 if (dup2(s[1], STDIN_FILENO0) == -1)
530 _exit(1);
531 if (dup2(s[1], STDOUT_FILENO1) == -1)
532 _exit(1);
533 if (dup2(s[1], STDERR_FILENO2) == -1)
534 _exit(1);
535
536 execv(path, argv);
537 err = strerror(errno(*__errno()));
538 write(s[1], err, strlen(err));
539 _exit(1);
540 default:
541 /* Parent process */
542 close(s[1]);
543 ret = iomux(s[0], text, len, outbp);
544 waitpid(pid, NULL((void *)0), 0); /* Collect child to prevent zombies */
545
546 return (ret);
547 }
548 return (FALSE0);
549}
550
551/*
552 * Multiplex read, write on socket fd passed. Put output in outbp
553 * Poll on the fd for both read and write readiness.
554 */
555int
556iomux(int fd, char* const text, int len, struct buffer *outbp)
557{
558 struct pollfd pfd[1];
559 int nfds;
560 char *textcopy;
561
562 textcopy = text;
563 fcntl(fd, F_SETFL4, O_NONBLOCK0x0004);
564 pfd[0].fd = fd;
565
566 /* There is nothing to write if len is zero
567 * but the cmd's output should be read so shutdown
568 * the socket for writing only and don't wait for POLLOUT
569 */
570 if (len == 0) {
571 shutdown(fd, SHUT_WR1);
572 pfd[0].events = POLLIN0x0001;
573 } else
574 pfd[0].events = POLLIN0x0001 | POLLOUT0x0004;
575
576 while ((nfds = poll(pfd, 1, TIMEOUT10000)) != -1 ||
577 (pfd[0].revents & (POLLERR0x0008 | POLLHUP0x0010 | POLLNVAL0x0020))) {
578 if (pfd[0].revents & POLLOUT0x0004 && len > 0)
579 pwriteout(fd, &textcopy, &len);
580 else if (pfd[0].revents & POLLIN0x0001)
581 if (preadin(fd, outbp) == FALSE0)
582 break;
583 if (len == 0 && pfd[0].events & POLLOUT0x0004)
584 pfd[0].events = POLLIN0x0001;
585 }
586 close(fd);
587
588 /* In case if last line doesn't have a '\n' add the leftover
589 * characters to buffer.
590 */
591 if (leftover[0] != '\0') {
592 addline(outbp, leftover)addlinef(outbp, "%s", leftover);
593 leftover[0] = '\0';
594 }
595 if (nfds == 0) {
596 dobeep();
597 ewprintf("poll timed out");
598 return (FALSE0);
599 } else if (nfds == -1) {
600 dobeep();
601 ewprintf("poll error");
602 return (FALSE0);
603 }
604 return (popbuftop(outbp, WNONE0x00));
605}
606
607/*
608 * Write some text from region to fd. Once done shutdown the
609 * write end.
610 */
611void
612pwriteout(int fd, char **text, int *len)
613{
614 int w;
615
616 if (((w = send(fd, *text, *len, MSG_NOSIGNAL0x400)) == -1)) {
617 switch(errno(*__errno())) {
618 case EPIPE32:
619 *len = -1;
620 break;
621 case EAGAIN35:
622 return;
623 }
624 } else
625 *len -= w;
626
627 *text += w;
628 if (*len <= 0)
629 shutdown(fd, SHUT_WR1);
630}
631
632/*
633 * Read some data from socket fd, break on '\n' and add
634 * to buffer. If couldn't break on newline hold leftover
635 * characters and append in next iteration.
636 */
637int
638preadin(int fd, struct buffer *bp)
639{
640 int len;
641 char buf[BUFSIZ1024], *p, *q;
642
643 if ((len = read(fd, buf, BUFSIZ1024 - 1)) <= 0)
644 return (FALSE0);
645
646 buf[len] = '\0';
647 p = q = buf;
648 if (leftover[0] != '\0' && ((q = strchr(p, *bp->b_nlchr)) != NULL((void *)0))) {
649 *q++ = '\0';
650 if (strlcat(leftover, p, sizeof(leftover)) >=
651 sizeof(leftover)) {
652 dobeep();
653 ewprintf("line too long");
654 return (FALSE0);
655 }
656 addline(bp, leftover)addlinef(bp, "%s", leftover);
657 leftover[0] = '\0';
658 p = q;
659 }
660 while ((q = strchr(p, *bp->b_nlchr)) != NULL((void *)0)) {
661 *q++ = '\0';
662 addline(bp, p)addlinef(bp, "%s", p);
663 p = q;
664 }
665 if (strlcpy(leftover, p, sizeof(leftover)) >= sizeof(leftover)) {
666 dobeep();
667 ewprintf("line too long");
668 return (FALSE0);
669 }
670 return (TRUE1);
671}