LCOV - code coverage report
Current view: top level - libs/libgroff - searchpath.cpp (source / functions) Hit Total Coverage
Test: GNU roff Lines: 129 150 86.0 %
Date: 2026-01-16 17:51:41 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright 1989-2024 Free Software Foundation, Inc.
       2             :      Written by James Clark (jjc@jclark.com)
       3             : 
       4             : This file is part of groff, the GNU roff typesetting system.
       5             : 
       6             : groff is free software; you can redistribute it and/or modify it under
       7             : the terms of the GNU General Public License as published by the Free
       8             : Software Foundation, either version 3 of the License, or
       9             : (at your option) any later version.
      10             : 
      11             : groff is distributed in the hope that it will be useful, but WITHOUT ANY
      12             : WARRANTY; without even the implied warranty of MERCHANTABILITY or
      13             : FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
      14             : for more details.
      15             : 
      16             : You should have received a copy of the GNU General Public License
      17             : along with this program.  If not, see <http://www.gnu.org/licenses/>. */
      18             : 
      19             : #ifdef HAVE_CONFIG_H
      20             : #include <config.h>
      21             : #endif
      22             : 
      23             : #include <assert.h>
      24             : #include <errno.h>
      25             : #include <stdlib.h>
      26             : 
      27             : // for stat(2)
      28             : #include <sys/types.h>
      29             : #include <sys/stat.h>
      30             : #include <unistd.h>
      31             : 
      32             : #include "lib.h"
      33             : 
      34             : #include "searchpath.h"
      35             : #include "nonposix.h"
      36             : 
      37             : #ifdef _WIN32
      38             : # include "relocate.h"
      39             : #else
      40             : # define relocate(path) strsave(path)
      41             : #endif
      42             : 
      43       56720 : static bool is_directory(const char *name)
      44             : {
      45             :   struct stat statbuf;
      46             :   // If stat() fails, a later fopen() will fail anyway (he said
      47             :   // TOCTTOUishly).
      48       56720 :   if ((stat(name, &statbuf) == 0)
      49       56720 :       && ((statbuf.st_mode & S_IFMT) == S_IFDIR))
      50           0 :     return true;
      51       56720 :   return false;
      52             : }
      53             : 
      54       10013 : search_path::search_path(const char *envvar, const char *standard,
      55       10013 :                          int add_home, int add_current)
      56             : {
      57       10013 :   char *home = 0 /* nullptr */;
      58       10013 :   if (add_home)
      59        2994 :     home = getenv("HOME");
      60       10013 :   char *e = 0 /* nullptr */;
      61       10013 :   if (envvar != 0 /* nullptr */)
      62        8456 :     e = getenv(envvar);
      63       10013 :   dirs = new char[((e && *e) ? strlen(e) + 1 : 0)
      64       10013 :                   + (add_current ? 1 + 1 : 0)
      65       10013 :                   + ((home && *home) ? strlen(home) + 1 : 0)
      66       10013 :                   + ((standard && *standard) ? strlen(standard) : 0)
      67       10013 :                   + 1];
      68       10013 :   *dirs = '\0';
      69       10013 :   if (e && *e) {
      70        8208 :     strcat(dirs, e);
      71        8208 :     strcat(dirs, PATH_SEP);
      72             :   }
      73       10013 :   if (add_current) {
      74        3054 :     strcat(dirs, ".");
      75        3054 :     strcat(dirs, PATH_SEP);
      76             :   }
      77       10013 :   if (home && *home) {
      78        2994 :     strcat(dirs, home);
      79        2994 :     strcat(dirs, PATH_SEP);
      80             :   }
      81       10013 :   if (standard && *standard)
      82        8456 :     strcat(dirs, standard);
      83       10013 :   init_len = strlen(dirs);
      84       10013 : }
      85             : 
      86       20026 : search_path::~search_path()
      87             : {
      88             :   // dirs is always allocated
      89       10013 :   delete[] dirs;
      90       10013 : }
      91             : 
      92         852 : void search_path::command_line_dir(const char *s)
      93             : {
      94         852 :   char *old = dirs;
      95         852 :   unsigned old_len = strlen(old);
      96         852 :   unsigned slen = strlen(s);
      97         852 :   dirs = new char[old_len + 1 + slen + 1];
      98         852 :   memcpy(dirs, old, old_len - init_len);
      99         852 :   char *p = dirs;
     100         852 :   p += old_len - init_len;
     101         852 :   if (init_len == 0)
     102           0 :     *p++ = PATH_SEP_CHAR;
     103         852 :   memcpy(p, s, slen);
     104         852 :   p += slen;
     105         852 :   if (init_len > 0) {
     106         852 :     *p++ = PATH_SEP_CHAR;
     107         852 :     memcpy(p, old + old_len - init_len, init_len);
     108         852 :     p += init_len;
     109             :   }
     110         852 :   *p++ = '\0';
     111         852 :   delete[] old;
     112         852 : }
     113             : 
     114       43190 : FILE *search_path::open_file(const char *name, char **pathp)
     115             : {
     116       43190 :   assert(name != 0 /* nullptr */);
     117       43190 :   if (IS_ABSOLUTE(name) || *dirs == '\0') {
     118           0 :     if (is_directory(name)) {
     119           0 :       errno = EISDIR;
     120           0 :       return 0 /* nullptr */;
     121             :     }
     122           0 :     FILE *fp = fopen(name, "r");
     123           0 :     if (fp != 0 /* nullptr */) {
     124           0 :       if (pathp != 0 /* nullptr */)
     125           0 :         *pathp = strsave(name);
     126           0 :       return fp;
     127             :     }
     128             :     else
     129           0 :       return 0 /* nullptr */;
     130             :   }
     131       43190 :   unsigned namelen = strlen(name);
     132       43190 :   char *p = dirs;
     133             :   for (;;) {
     134       56628 :     char *end = strchr(p, PATH_SEP_CHAR);
     135       56628 :     if (0 /* nullptr */ == end)
     136          69 :       end = strchr(p, '\0');
     137       56628 :     int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
     138       56628 :     char *origpath = new char[(end - p) + need_slash + namelen + 1];
     139       56628 :     memcpy(origpath, p, end - p);
     140       56628 :     if (need_slash)
     141       56628 :       origpath[end - p] = '/';
     142       56628 :     strcpy(origpath + (end - p) + need_slash, name);
     143             : #if 0
     144             :     fprintf(stderr, "origpath '%s'\n", origpath);
     145             : #endif
     146       56628 :     char *path = relocate(origpath);
     147       56628 :     delete[] origpath;
     148             : #if 0
     149             :     fprintf(stderr, "trying '%s'\n", path);
     150             : #endif
     151       56628 :     if (is_directory(name)) {
     152           0 :       errno = EISDIR;
     153           0 :       return 0 /* nullptr */;
     154             :     }
     155       56628 :     FILE *fp = fopen(path, "r");
     156       56628 :     int err = errno;
     157       56628 :     if (fp != 0 /* nullptr */) {
     158       43121 :       if (pathp != 0 /* nullptr */)
     159       43121 :         *pathp = path;
     160             :       else {
     161           0 :         free(path);
     162           0 :         errno = err;
     163             :       }
     164       43121 :       return fp;
     165             :     }
     166       13507 :     free(path);
     167       13507 :     errno = err;
     168       13507 :     if (*end == '\0')
     169          69 :       break;
     170       13438 :     p = end + 1;
     171       13438 :   }
     172          69 :   return 0 /* nullptr */;
     173             : }
     174             : 
     175         130 : FILE *search_path::open_file_cautiously(const char *name, char **pathp,
     176             :                                         const char *mode)
     177             : {
     178         130 :   if (0 /* nullptr */ == mode)
     179         125 :     mode = "r";
     180         130 :   bool reading = (strchr(mode, 'r') != 0 /* nullptr */);
     181         130 :   if (0 /* nullptr */ == name || strcmp(name, "-") == 0) {
     182          63 :     if (pathp != 0)
     183          63 :       *pathp = strsave(reading ? "stdin" : "stdout");
     184          63 :     return (reading ? stdin : stdout);
     185             :   }
     186          67 :   if (!reading || IS_ABSOLUTE(name) || *dirs == '\0') {
     187           4 :     if (is_directory(name)) {
     188           0 :       errno = EISDIR;
     189           0 :       return 0 /* nullptr */;
     190             :     }
     191           4 :     FILE *fp = fopen(name, mode);
     192           4 :     if (fp != 0 /* nullptr */) {
     193           4 :       if (pathp != 0 /* nullptr */)
     194           3 :         *pathp = strsave(name);
     195           4 :       return fp;
     196             :     }
     197             :     else
     198           0 :       return 0 /* nullptr */;
     199             :   }
     200          63 :   unsigned namelen = strlen(name);
     201          63 :   char *p = dirs;
     202             :   for (;;) {
     203          88 :     char *end = strchr(p, PATH_SEP_CHAR);
     204          88 :     if (0 /* nullptr */ == end)
     205           1 :       end = strchr(p, '\0');
     206          88 :     int need_slash = (end > p
     207          88 :                       && strchr(DIR_SEPS, end[-1]) == 0 /* nullptr */);
     208          88 :     char *origpath = new char[(end - p) + need_slash + namelen + 1];
     209          88 :     memcpy(origpath, p, end - p);
     210          88 :     if (need_slash)
     211          87 :       origpath[end - p] = '/';
     212          88 :     strcpy(origpath + (end - p) + need_slash, name);
     213             : #if 0
     214             :     fprintf(stderr, "origpath '%s'\n", origpath);
     215             : #endif
     216          88 :     char *path = relocate(origpath);
     217          88 :     delete[] origpath;
     218             : #if 0
     219             :     fprintf(stderr, "trying '%s'\n", path);
     220             : #endif
     221          88 :     if (is_directory(name)) {
     222           0 :       errno = EISDIR;
     223           0 :       return 0 /* nullptr */;
     224             :     }
     225          88 :     FILE *fp = fopen(path, mode);
     226          88 :     int err = errno;
     227          88 :     if (fp != 0 /* nullptr */) {
     228          62 :       if (pathp != 0 /* nullptr */)
     229          12 :         *pathp = path;
     230             :       else {
     231          50 :         free(path);
     232          50 :         errno = err;
     233             :       }
     234          62 :       return fp;
     235             :     }
     236          26 :     free(path);
     237          26 :     errno = err;
     238          26 :     if (err != ENOENT)
     239           0 :       return 0 /* nullptr */;
     240          26 :     if (*end == '\0')
     241           1 :       break;
     242          25 :     p = end + 1;
     243          25 :   }
     244           1 :   errno = ENOENT;
     245           1 :   return 0 /* nullptr */;
     246             : }
     247             : 
     248             : // Local Variables:
     249             : // fill-column: 72
     250             : // mode: C++
     251             : // End:
     252             : // vim: set cindent noexpandtab shiftwidth=2 textwidth=72:

Generated by: LCOV version 1.14