summaryrefslogtreecommitdiff
path: root/src/lib/eina/eina_debug_bt_file.c
blob: f5dfb9ce60a52f649b931760509f381a7c67e64d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/* EINA - EFL data type library
 * Copyright (C) 2015 Carsten Haitzler
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library;
 * if not, see <http://www.gnu.org/licenses/>.
 */

#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#ifdef _WIN32
#include <evil_private.h> /* realpath */
#endif

#ifndef _MSC_VER
# include <unistd.h>
#endif

#include "eina_debug_private.h"

extern Eina_Spinlock _eina_debug_lock;

static unsigned int _table_num = 0;
static unsigned int _table_size = 0;
static const char **_table = NULL;

// a very simple "fast lookup" of a filename to a path. we expect this table
// of lookups to remain very small as it most likely includes just the
// application executable itself as this was run from $PATH or using
// a relative path relative to cwd of shell at time of exec. this is really
// the only likely content, but just in case, handle more. it is much faster
// than going through systemcalls like realpath() every time (well this libc
// function will at least be a system call or use system calls to do its work)
static const char *
_eina_debug_file_lookup(const char *fname)
{
   unsigned int n;

   if (!_table) return NULL;
   for (n = 0; _table[n]; n += 2)
     {
        if (!strcmp(_table[n], fname)) return _table[n + 1];
     }
   return NULL;
}

// record a new filename -> path entry in our table. the table really is just
// odd/even strings like fname, path, fname2, path2, fnamr3, path3, ...
// and we are unlikely to have much more than 1 entry here. see above
static const char *
_eina_debug_file_store(const char *fname, const char *file)
{
   static const char **table2;

   _table_num += 2;
   if (_table_num >= _table_size)
     {
        _table_size += 32;
        table2 = _eina_debug_chunk_realloc(_table_size * sizeof(const char *));
        if (!table2) return NULL;
        _table = table2;
     }
   _table[_table_num - 2] = _eina_debug_chunk_strdup(fname);
   _table[_table_num - 1] = _eina_debug_chunk_strdup(file);
   return _table[_table_num - 1];
}

// do a "fast lookup" of a filename to a file path for debug output. this
// relies on caching to avoid system calls and assumes that once we know
// the full path of a given filename, we can know its full path reliably.
// if we can't we'd be in trouble anyway as the filename and path lookup
// failure is due maybe to a deleted file or renamed file and then we are
// going to have a bad day either way.
const char *
_eina_debug_file_get(const char *fname)
{
   char buf[4096];
   const char *file;
   static const char **path = NULL;
   static char *pathstrs = NULL;

   // no filename provided
   if ((!fname) || (!fname[0])) return NULL;
   // it's a full path so return as-is
   if (fname[0] == '/') return fname;
   // first look in cache for filename -> full path lookup and if
   // there, return that (yes - assuming filesystem paths doesn't change
   // which is unlikely as they were set up at star most likely)
   eina_spinlock_take(&_eina_debug_lock);
   file = _eina_debug_file_lookup(fname);
   eina_spinlock_release(&_eina_debug_lock);
   if (file) return file;

   // store PATH permanently - yes. if it changes runtime it will break.
   // for speed reasons we need to assume it won't change. store path broken
   // down into an array of ptrs to strings with NULL ptr at the end. this
   // will only execute once as an "init" for a breoken up path so it should
   // not matter speed-wise
   eina_spinlock_take(&_eina_debug_lock);
   if (!path)
     {
        unsigned int n;
        char *p1, *p2;
        const char *p;
        const char *pathstr = getenv("PATH");

        if (!pathstr)
          {
             eina_spinlock_release(&_eina_debug_lock);
             return NULL;
          }
        // dup the entire env as we will rpelace : with 0 bytes to break str
        pathstrs = _eina_debug_chunk_strdup(pathstr);
        for (n = 0, p = pathstr; *p;)
          {
             n++;
             p = strchr(p, ':');
             if (!p) break;
             p++;
          }
        path = _eina_debug_chunk_push(sizeof(const char *) * (n + 1));
        for (n = 0, p1 = pathstrs; *p1; n++)
          {
             path[n] = p1;
             p2 = strchr(p1, ':');
             if (!p2) break;
             *p2 = 0;
             p1 = p2 + 1;
          }
        path[n] = NULL;
     }
   eina_spinlock_release(&_eina_debug_lock);

   // a relative path - resolve with realpath. due to the cache store above
   // we shouldn't have to do this very often
   if ((!strncmp(fname, "./", 2)) || (!strncmp(fname, "../", 3)))
     { // relative path
        if (realpath(fname, buf)) file = buf;
        else file = NULL;
     }
   // search in $PATH for the file then - this should also be very rare as
   // we will store and cache results permanently
   else if (path)
     {
        struct stat st;
        unsigned int n;

        for (n = 0; path[n]; n++)
          {
             snprintf(buf, sizeof(buf), "%s/%s", path[n], fname);
             if (stat(buf, &st) == 0)
               {
                  file = buf;
                  break;
               }
          }
     }
   // if it's found - store it in cache for later
   if (file)
     {
        eina_spinlock_take(&_eina_debug_lock);
        file = _eina_debug_file_store(fname, file);
        eina_spinlock_release(&_eina_debug_lock);
     }
   return file;
}