Bug Summary

File:src/gnu/lib/libreadline/histfile.c
Warning:line 119, column 3
Value stored to 'home_len' is never read

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.0 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name histfile.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -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 -fhalf-no-semantic-interposition -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -mconstructor-aliases -munwind-tables -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/gnu/lib/libreadline/obj -resource-dir /usr/local/lib/clang/13.0.0 -D HAVE_CONFIG_H -I /usr/src/gnu/lib/libreadline -D PIC -internal-isystem /usr/local/lib/clang/13.0.0/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/gnu/lib/libreadline/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -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/vmm/scan-build/2022-01-12-194120-40624-1 -x c /usr/src/gnu/lib/libreadline/histfile.c
1/* histfile.c - functions to manipulate the history file. */
2
3/* Copyright (C) 1989, 1992 Free Software Foundation, Inc.
4
5 This file contains the GNU History Library (the Library), a set of
6 routines for managing the text of previously typed lines.
7
8 The Library is free software; you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation; either version 2, or (at your option)
11 any later version.
12
13 The Library is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 General Public License for more details.
17
18 The GNU General Public License is often shipped with GNU software, and
19 is generally kept in a file called COPYING or LICENSE. If you do not
20 have a copy of the license, write to the Free Software Foundation,
21 59 Temple Place, Suite 330, Boston, MA 02111 USA. */
22
23/* The goal is to make the implementation transparent, so that you
24 don't have to know what data types are used, just what functions
25 you can call. I think I have done that. */
26#define READLINE_LIBRARY
27
28#if defined (HAVE_CONFIG_H1)
29# include <config.h>
30#endif
31
32#include <stdio.h>
33
34#include <sys/types.h>
35#ifndef _MINIX
36# include <sys/file.h>
37#endif
38#include "posixstat.h"
39#include <fcntl.h>
40
41#if defined (HAVE_STDLIB_H1)
42# include <stdlib.h>
43#else
44# include "ansi_stdlib.h"
45#endif /* HAVE_STDLIB_H */
46
47#if defined (HAVE_UNISTD_H1)
48# include <unistd.h>
49#endif
50
51#if defined (__EMX__) || defined (__CYGWIN__)
52# undef HAVE_MMAP
53#endif
54
55#ifdef HAVE_MMAP
56# include <sys/mman.h>
57
58# ifdef MAP_FILE
59# define MAP_RFLAGS (MAP_FILE|MAP_PRIVATE)
60# define MAP_WFLAGS (MAP_FILE|MAP_SHARED)
61# else
62# define MAP_RFLAGS MAP_PRIVATE
63# define MAP_WFLAGS MAP_SHARED
64# endif
65
66# ifndef MAP_FAILED
67# define MAP_FAILED ((void *)-1)
68# endif
69
70#endif /* HAVE_MMAP */
71
72/* If we're compiling for __EMX__ (OS/2) or __CYGWIN__ (cygwin32 environment
73 on win 95/98/nt), we want to open files with O_BINARY mode so that there
74 is no \n -> \r\n conversion performed. On other systems, we don't want to
75 mess around with O_BINARY at all, so we ensure that it's defined to 0. */
76#if defined (__EMX__) || defined (__CYGWIN__)
77# ifndef O_BINARY0
78# define O_BINARY0 0
79# endif
80#else /* !__EMX__ && !__CYGWIN__ */
81# undef O_BINARY0
82# define O_BINARY0 0
83#endif /* !__EMX__ && !__CYGWIN__ */
84
85#include <errno(*__errno()).h>
86#if !defined (errno(*__errno()))
87extern int errno(*__errno());
88#endif /* !errno */
89
90#include "history.h"
91#include "histlib.h"
92
93#include "rlshell.h"
94#include "xmalloc.h"
95
96/* Return the string that should be used in the place of this
97 filename. This only matters when you don't specify the
98 filename to read_history (), or write_history (). */
99static char *
100history_filename (filename)
101 const char *filename;
102{
103 char *return_val;
104 const char *home;
105 int home_len;
106 char dot;
107
108 return_val = filename ? savestring (filename)xstrdup(filename) : (char *)NULL((void *)0);
109
110 if (return_val)
111 return (return_val);
112
113 home = sh_get_env_value ("HOME");
114
115 if (home == 0 || *home == '\0') {
116 errno(*__errno()) = ENOENT2;
117 return (NULL((void *)0));
118 }
119 home_len = strlen (home);
Value stored to 'home_len' is never read
120
121#if defined (__MSDOS__)
122 dot = '_';
123#else
124 dot = '.';
125#endif
126 if (asprintf(&return_val, "%s/%c%s", home, dot, "history") == -1)
127 memory_error_and_abort("asprintf");
128 return (return_val);
129}
130
131/* Add the contents of FILENAME to the history list, a line at a time.
132 If FILENAME is NULL, then read from ~/.history. Returns 0 if
133 successful, or errno if not. */
134int
135read_history (filename)
136 const char *filename;
137{
138 return (read_history_range (filename, 0, -1));
139}
140
141/* Read a range of lines from FILENAME, adding them to the history list.
142 Start reading at the FROM'th line and end at the TO'th. If FROM
143 is zero, start at the beginning. If TO is less than FROM, read
144 until the end of the file. If FILENAME is NULL, then read from
145 ~/.history. Returns 0 if successful, or errno if not. */
146int
147read_history_range (filename, from, to)
148 const char *filename;
149 int from, to;
150{
151 register char *line_start, *line_end;
152 char *input, *buffer, *bufend;
153 int file, current_line, chars_read;
154 struct stat finfo;
155 size_t file_size;
156
157 buffer = (char *)NULL((void *)0);
158 if ((input = history_filename (filename)))
159 file = open (input, O_RDONLY0x0000|O_BINARY0, 0666);
160 else
161 file = -1;
162
163 if ((file < 0) || (fstat (file, &finfo) == -1))
164 goto error_and_exit;
165
166 file_size = (size_t)finfo.st_size;
167
168 /* check for overflow on very large files */
169 if (file_size != finfo.st_size || file_size + 1 < file_size)
170 {
171#if defined (EFBIG27)
172 errno(*__errno()) = EFBIG27;
173#elif defined (EOVERFLOW87)
174 errno(*__errno()) = EOVERFLOW87;
175#endif
176 goto error_and_exit;
177 }
178
179#ifdef HAVE_MMAP
180 /* We map read/write and private so we can change newlines to NULs without
181 affecting the underlying object. */
182 buffer = (char *)mmap (0, file_size, PROT_READ|PROT_WRITE, MAP_RFLAGS, file, 0);
183 if ((void *)buffer == MAP_FAILED)
184 goto error_and_exit;
185 chars_read = file_size;
186#else
187 buffer = (char *)malloc (file_size + 1);
188 if (buffer == 0)
189 goto error_and_exit;
190
191 chars_read = read (file, buffer, file_size);
192#endif
193 if (chars_read < 0)
194 {
195 error_and_exit:
196 chars_read = errno(*__errno());
197 if (file >= 0)
198 close (file);
199
200 FREE (input)if (input) free (input);
201#ifndef HAVE_MMAP
202 FREE (buffer)if (buffer) free (buffer);
203#endif
204
205 return (chars_read);
206 }
207
208 close (file);
209
210 /* Set TO to larger than end of file if negative. */
211 if (to < 0)
212 to = chars_read;
213
214 /* Start at beginning of file, work to end. */
215 bufend = buffer + chars_read;
216 current_line = 0;
217
218 /* Skip lines until we are at FROM. */
219 for (line_start = line_end = buffer; line_end < bufend && current_line < from; line_end++)
220 if (*line_end == '\n')
221 {
222 current_line++;
223 line_start = line_end + 1;
224 }
225
226 /* If there are lines left to gobble, then gobble them now. */
227 for (line_end = line_start; line_end < bufend; line_end++)
228 if (*line_end == '\n')
229 {
230 *line_end = '\0';
231
232 if (*line_start)
233 add_history (line_start);
234
235 current_line++;
236
237 if (current_line >= to)
238 break;
239
240 line_start = line_end + 1;
241 }
242
243 FREE (input)if (input) free (input);
244#ifndef HAVE_MMAP
245 FREE (buffer)if (buffer) free (buffer);
246#else
247 munmap (buffer, file_size);
248#endif
249
250 return (0);
251}
252
253/* Truncate the history file FNAME, leaving only LINES trailing lines.
254 If FNAME is NULL, then use ~/.history. Returns 0 on success, errno
255 on failure. */
256int
257history_truncate_file (fname, lines)
258 const char *fname;
259 int lines;
260{
261 char *buffer, *filename, *bp;
262 int file, chars_read, rv;
263 struct stat finfo;
264 size_t file_size;
265
266 buffer = (char *)NULL((void *)0);
267 if ((filename = history_filename (fname)))
268 file = open (filename, O_RDONLY0x0000|O_BINARY0, 0666);
269 else
270 file = -1;
271 rv = 0;
272
273 /* Don't try to truncate non-regular files. */
274 if (file == -1 || fstat (file, &finfo) == -1)
275 {
276 rv = errno(*__errno());
277 if (file != -1)
278 close (file);
279 goto truncate_exit;
280 }
281
282 if (S_ISREG (finfo.st_mode)((finfo.st_mode & 0170000) == 0100000) == 0)
283 {
284 close (file);
285#ifdef EFTYPE79
286 rv = EFTYPE79;
287#else
288 rv = EINVAL22;
289#endif
290 goto truncate_exit;
291 }
292
293 file_size = (size_t)finfo.st_size;
294
295 /* check for overflow on very large files */
296 if (file_size != finfo.st_size || file_size + 1 < file_size)
297 {
298 close (file);
299#if defined (EFBIG27)
300 rv = errno(*__errno()) = EFBIG27;
301#elif defined (EOVERFLOW87)
302 rv = errno(*__errno()) = EOVERFLOW87;
303#else
304 rv = errno(*__errno()) = EINVAL22;
305#endif
306 goto truncate_exit;
307 }
308
309 buffer = (char *)malloc (file_size + 1);
310 if (buffer == 0)
311 {
312 close (file);
313 goto truncate_exit;
314 }
315
316 chars_read = read (file, buffer, file_size);
317 close (file);
318
319 if (chars_read <= 0)
320 {
321 rv = (chars_read < 0) ? errno(*__errno()) : 0;
322 goto truncate_exit;
323 }
324
325 /* Count backwards from the end of buffer until we have passed
326 LINES lines. */
327 for (bp = buffer + chars_read - 1; lines && bp > buffer; bp--)
328 {
329 if (*bp == '\n')
330 lines--;
331 }
332
333 /* If this is the first line, then the file contains exactly the
334 number of lines we want to truncate to, so we don't need to do
335 anything. It's the first line if we don't find a newline between
336 the current value of i and 0. Otherwise, write from the start of
337 this line until the end of the buffer. */
338 for ( ; bp > buffer; bp--)
339 if (*bp == '\n')
340 {
341 bp++;
342 break;
343 }
344
345 /* Write only if there are more lines in the file than we want to
346 truncate to. */
347 if (bp > buffer && ((file = open (filename, O_WRONLY0x0001|O_TRUNC0x0400|O_BINARY0, 0600)) != -1))
348 {
349 write (file, bp, chars_read - (bp - buffer));
350
351#if defined (__BEOS__)
352 /* BeOS ignores O_TRUNC. */
353 ftruncate (file, chars_read - (bp - buffer));
354#endif
355
356 close (file);
357 }
358
359 truncate_exit:
360
361 FREE (buffer)if (buffer) free (buffer);
362
363 free (filename);
364 return rv;
365}
366
367/* Workhorse function for writing history. Writes NELEMENT entries
368 from the history list to FILENAME. OVERWRITE is non-zero if you
369 wish to replace FILENAME with the entries. */
370static int
371history_do_write (filename, nelements, overwrite)
372 const char *filename;
373 int nelements, overwrite;
374{
375 register int i;
376 char *output;
377 int file, mode, rv;
378#ifdef HAVE_MMAP
379 size_t cursize;
380#endif
381
382#ifdef HAVE_MMAP
383 mode = overwrite ? O_RDWR0x0002|O_CREAT0x0200|O_TRUNC0x0400|O_BINARY0 : O_RDWR0x0002|O_APPEND0x0008|O_BINARY0;
384#else
385 mode = overwrite ? O_WRONLY0x0001|O_CREAT0x0200|O_TRUNC0x0400|O_BINARY0 : O_WRONLY0x0001|O_APPEND0x0008|O_BINARY0;
386#endif
387 output = history_filename (filename);
388 rv = 0;
389
390 if (!output || (file = open (output, mode, 0600)) == -1)
391 {
392 FREE (output)if (output) free (output);
393 return (errno(*__errno()));
394 }
395
396#ifdef HAVE_MMAP
397 cursize = overwrite ? 0 : lseek (file, 0, SEEK_END2);
398#endif
399
400 if (nelements > history_length)
401 nelements = history_length;
402
403 /* Build a buffer of all the lines to write, and write them in one syscall.
404 Suggested by Peter Ho (peter@robosts.oxford.ac.uk). */
405 {
406 HIST_ENTRY **the_history; /* local */
407 int buffer_size;
408 char *buffer;
409
410 the_history = history_list ();
411 /* Calculate the total number of bytes to write. */
412 for (buffer_size = 1, i = history_length - nelements; i < history_length; i++)
413 buffer_size += 1 + strlen (the_history[i]->line);
414
415 /* Allocate the buffer, and fill it. */
416#ifdef HAVE_MMAP
417 if (ftruncate (file, buffer_size+cursize) == -1)
418 goto mmap_error;
419 buffer = (char *)mmap (0, buffer_size, PROT_READ|PROT_WRITE, MAP_WFLAGS, file, cursize);
420 if ((void *)buffer == MAP_FAILED)
421 {
422mmap_error:
423 rv = errno(*__errno());
424 FREE (output)if (output) free (output);
425 close (file);
426 return rv;
427 }
428#else
429 buffer = (char *)malloc (buffer_size);
430 if (buffer == 0)
431 {
432 rv = errno(*__errno());
433 FREE (output)if (output) free (output);
434 close (file);
435 return rv;
436 }
437#endif
438 buffer[0] = '\0';
439
440 for (i = history_length - nelements; i < history_length; i++)
441 {
442 strlcat (buffer, the_history[i]->line, buffer_size);
443 strlcat (buffer, "\n", buffer_size);
444 }
445
446#ifdef HAVE_MMAP
447 if (msync (buffer, buffer_size, 0) != 0 || munmap (buffer, buffer_size) != 0)
448 rv = errno(*__errno());
449#else
450 if (write (file, buffer, buffer_size - 1) < 0)
451 rv = errno(*__errno());
452 free (buffer);
453#endif
454 }
455
456 close (file);
457
458 FREE (output)if (output) free (output);
459
460 return (rv);
461}
462
463/* Append NELEMENT entries to FILENAME. The entries appended are from
464 the end of the list minus NELEMENTs up to the end of the list. */
465int
466append_history (nelements, filename)
467 int nelements;
468 const char *filename;
469{
470 return (history_do_write (filename, nelements, HISTORY_APPEND0));
471}
472
473/* Overwrite FILENAME with the current history. If FILENAME is NULL,
474 then write the history list to ~/.history. Values returned
475 are as in read_history ().*/
476int
477write_history (filename)
478 const char *filename;
479{
480 return (history_do_write (filename, history_length, HISTORY_OVERWRITE1));
481}