fix off by 1 error
[rrdtool.git] / src / rrd_utils.c
1 /**
2  * RRDtool - src/rrd_utils.c
3  * Copyright (C) 2009 Kevin Brintnall
4  * Copyright (C) 2008 Sebastian Harl
5  *
6  * This program is free software; you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the
8  * Free Software Foundation; only version 2 of the License is applicable.
9  *
10  * This program is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along
16  * with this program; if not, write to the Free Software Foundation, Inc.,
17  * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
18  *
19  * Authors:
20  *   kevin brintnall <kbrint@rufus.net>
21  *   Sebastian Harl <sh@tokkee.org>
22  **/
23
24 #include "rrd_tool.h"
25
26 #include <assert.h>
27 #include <errno.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #ifndef _MSC_VER
33 #include <libgen.h>
34 #include <unistd.h>
35 #endif
36 #ifdef WIN32
37 #       define random() rand()
38 #       define srandom(x) srand(x)
39 #       define getpid() 0
40 #endif /* WIN32 */
41
42 #ifndef S_ISDIR
43 #define S_ISDIR(x) (((x) & S_IFMT) == S_IFDIR)
44 #endif
45
46 /* make sure that the random number generator seeded EXACTLY ONCE */
47 long rrd_random(void)
48 {
49     static int rand_init = 0;
50     if (!rand_init) {
51         srandom((unsigned int) time(NULL) + (unsigned int) getpid());
52         rand_init++;
53     }
54
55     return random();
56 }
57
58 /* rrd_add_ptr_chunk: add a pointer to a dynamically sized array of
59  * pointers, realloc as necessary in multiples of "chunk".
60  *
61  * "alloc" is the number of pointers allocated
62  * "dest_size" is the number of valid pointers
63  *
64  * returns 1 on success, 0 on failure.
65  */
66
67 int rrd_add_ptr_chunk(void ***dest, size_t *dest_size, void *src,
68                       size_t *alloc, size_t chunk)
69 {
70     void **temp;
71
72     assert(dest != NULL);
73     assert(alloc != NULL);
74     assert(*alloc >= *dest_size);
75
76     if (*alloc == *dest_size)
77     {
78         temp = (void **) rrd_realloc(*dest, (*alloc+chunk) * sizeof(*dest));
79         if (!temp)
80             return 0;
81
82         *dest = temp;
83         *alloc += chunk;
84     }
85
86     (*dest)[*dest_size] = src;
87     (*dest_size)++;
88
89     return 1;
90 }
91
92 /* rrd_add_ptr: add a pointer to a dynamically sized array of pointers,
93  * realloc as necessary.  returns 1 on success, 0 on failure.
94  */
95 int rrd_add_ptr(void ***dest, size_t *dest_size, void *src)
96 {
97     size_t alloc = *dest_size;
98
99     return rrd_add_ptr_chunk(dest, dest_size, src, &alloc, 1);
100 }
101
102 /* like rrd_add_ptr_chunk, but calls strdup() on a string first. */
103 int rrd_add_strdup_chunk(char ***dest, size_t *dest_size, char *src,
104                          size_t *alloc, size_t chunk)
105 {
106     char *dup_src;
107     int add_ok;
108
109     assert(dest != NULL);
110     assert(src  != NULL);
111
112     dup_src = strdup(src);
113     if (!dup_src)
114         return 0;
115
116     add_ok = rrd_add_ptr_chunk((void ***)dest, dest_size, (void *)dup_src, alloc, chunk);
117     if (!add_ok)
118         free(dup_src);
119
120     return add_ok;
121 }
122
123 int rrd_add_strdup(char ***dest, size_t *dest_size, char *src)
124 {
125     size_t alloc = *dest_size;
126
127     return rrd_add_strdup_chunk(dest, dest_size, src, &alloc, 1);
128 }
129
130 void rrd_free_ptrs(void ***src, size_t *cnt)
131 {
132     void **sp;
133
134     assert(src != NULL);
135     sp = *src;
136
137     if (sp == NULL)
138         return;
139
140     while (*cnt > 0) {
141         (*cnt)--;
142         free(sp[*cnt]);
143     }
144
145     free (sp);
146     *src = NULL;
147 }
148
149 /* recursively create the directory named by 'pathname'
150  * (similar to "mkdir -p" on the command line) */
151 int rrd_mkdir_p(const char *pathname, mode_t mode)
152 {
153     struct stat sb;
154
155     char *pathname_copy;
156     char *base_dir;
157
158     if ((NULL == pathname) || ('\0' == *pathname)) {
159         errno = EINVAL;
160         return -1;
161     }
162
163     if (0 == stat(pathname, &sb)) {
164         if (! S_ISDIR(sb.st_mode)) {
165             errno = ENOTDIR;
166             return -1;
167         }
168         return 0;
169     }
170
171     /* keep errno as set by stat() */
172     if (ENOENT != errno)
173         return -1;
174
175     /* dirname might modify its first argument */
176     if (NULL == (pathname_copy = strdup(pathname)))
177         return -1;
178
179 #ifndef _MSC_VER
180     /* the data pointedd too by dirname might change too (bsd) */
181     if (NULL == (base_dir = strdup(dirname(pathname_copy)))) {
182         free(pathname_copy);
183         return -1;
184     }
185 #else
186     _splitpath(pathname_copy, NULL, base_dir, NULL, NULL);
187 #endif
188
189     if (0 != rrd_mkdir_p(base_dir, mode)) {
190         int orig_errno = errno;
191         free(pathname_copy);
192 #ifndef _MSC_VER
193         free(base_dir);
194 #endif
195         errno = orig_errno;
196         return -1;
197     }
198
199     free(pathname_copy);
200 #ifndef _MSC_VER
201     free(base_dir);
202 #endif
203
204     /* keep errno as set by mkdir() */
205 #ifdef _MSC_VER
206     if (0 != mkdir(pathname))
207         return -1;
208 #else
209     if (0 != mkdir(pathname, mode))
210         return -1;
211 #endif
212     return 0;
213 } /* rrd_mkdir_p */
214