File: vector.c

package info (click to toggle)
libpam-afs-session 2.6-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,684 kB
  • sloc: sh: 11,779; ansic: 7,910; perl: 270; makefile: 174
file content (287 lines) | stat: -rw-r--r-- 7,910 bytes parent folder | download | duplicates (5)
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
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
/*
 * Vector handling (counted lists of char *'s).
 *
 * A vector is a table for handling a list of strings with less overhead than
 * linked list.  The intention is for vectors, once allocated, to be reused;
 * this saves on memory allocations once the array of char *'s reaches a
 * stable size.
 *
 * This is based on the util/vector.c library, but that library uses xmalloc
 * routines to exit the program if memory allocation fails.  This is a
 * modified version of the vector library that instead returns false on
 * failure to allocate memory, allowing the caller to do appropriate recovery.
 *
 * Vectors require list of strings, not arbitrary binary data, and cannot
 * handle data elements containing nul characters.
 *
 * Only the portions of the vector library used by PAM modules are
 * implemented.
 *
 * The canonical version of this file is maintained in the rra-c-util package,
 * which can be found at <http://www.eyrie.org/~eagle/software/rra-c-util/>.
 *
 * Written by Russ Allbery <eagle@eyrie.org>
 *
 * The authors hereby relinquish any claim to any copyright that they may have
 * in this work, whether granted under contract or by operation of law or
 * international treaty, and hereby commit to the public, at large, that they
 * shall not, at any time in the future, seek to enforce any copyright in this
 * work against any person or entity, or prevent any person or entity from
 * copying, publishing, distributing or creating derivative works of this
 * work.
 */

#include <config.h>
#include <portable/system.h>

#include <pam-util/vector.h>


/*
 * Allocate a new, empty vector.  Returns NULL if memory allocation fails.
 */
struct vector *
vector_new(void)
{
    return calloc(1, sizeof(struct vector));
}


/*
 * Allocate a new vector that's a copy of an existing vector.  Returns NULL if
 * memory allocation fails.
 */
struct vector *
vector_copy(const struct vector *old)
{
    struct vector *vector;
    size_t i;

    vector = vector_new();
    if (!vector_resize(vector, old->count)) {
        vector_free(vector);
        return NULL;
    }
    vector->count = old->count;
    for (i = 0; i < old->count; i++) {
        vector->strings[i] = strdup(old->strings[i]);
        if (vector->strings[i] == NULL) {
            vector_free(vector);
            return NULL;
        }
    }
    return vector;
}


/*
 * Resize a vector (using reallocarray to resize the table).  Return false if
 * memory allocation fails.
 */
bool
vector_resize(struct vector *vector, size_t size)
{
    size_t i;
    char **strings;

    if (vector->count > size) {
        for (i = size; i < vector->count; i++)
            free(vector->strings[i]);
        vector->count = size;
    }
    if (size == 0) {
        free(vector->strings);
        vector->strings = NULL;
    } else {
        strings = reallocarray(vector->strings, size, sizeof(char *));
        if (strings == NULL)
            return false;
        vector->strings = strings;
    }
    vector->allocated = size;
    return true;
}


/*
 * Add a new string to the vector, resizing the vector as necessary.  The
 * vector is resized an element at a time; if a lot of resizes are expected,
 * vector_resize should be called explicitly with a more suitable size.
 * Return false if memory allocation fails.
 */
bool
vector_add(struct vector *vector, const char *string)
{
    size_t next = vector->count;

    if (vector->count == vector->allocated)
        if (!vector_resize(vector, vector->allocated + 1))
            return false;
    vector->strings[next] = strdup(string);
    if (vector->strings[next] == NULL)
        return false;
    vector->count++;
    return true;
}


/*
 * Empty a vector but keep the allocated memory for the pointer table.
 */
void
vector_clear(struct vector *vector)
{
    size_t i;

    for (i = 0; i < vector->count; i++)
        if (vector->strings[i] != NULL)
            free(vector->strings[i]);
    vector->count = 0;
}


/*
 * Free a vector completely.
 */
void
vector_free(struct vector *vector)
{
    if (vector == NULL)
        return;
    vector_clear(vector);
    free(vector->strings);
    free(vector);
}


/*
 * Given a vector that we may be reusing, clear it out.  If the first argument
 * is NULL, allocate a new vector.  Used by vector_split*.  Returns NULL if
 * memory allocation fails.
 */
static struct vector *
vector_reuse(struct vector *vector)
{
    if (vector == NULL)
        return vector_new();
    else {
        vector_clear(vector);
        return vector;
    }
}


/*
 * Given a string and a set of separators expressed as a string, count the
 * number of strings that it will split into when splitting on those
 * separators.
 */
static size_t
split_multi_count(const char *string, const char *seps)
{
    const char *p;
    size_t count;

    if (*string == '\0')
        return 0;
    for (count = 1, p = string + 1; *p != '\0'; p++)
        if (strchr(seps, *p) != NULL && strchr(seps, p[-1]) == NULL)
            count++;

    /*
     * If the string ends in separators, we've overestimated the number of
     * strings by one.
     */
    if (strchr(seps, p[-1]) != NULL)
        count--;
    return count;
}


/*
 * Given a string, split it at any of the provided separators to form a
 * vector, copying each string segment.  If the third argument isn't NULL,
 * reuse that vector; otherwise, allocate a new one.  Any number of
 * consecutive separators are considered a single separator.  Returns NULL on
 * memory allocation failure, after which the provided vector may only have
 * partial results.
 */
struct vector *
vector_split_multi(const char *string, const char *seps,
                   struct vector *vector)
{
    const char *p, *start;
    size_t i, count;
    bool created = false;

    if (vector == NULL)
        created = true;
    vector = vector_reuse(vector);
    if (vector == NULL)
        return NULL;

    count = split_multi_count(string, seps);
    if (vector->allocated < count && !vector_resize(vector, count))
        goto fail;

    vector->count = 0;
    for (start = string, p = string, i = 0; *p != '\0'; p++)
        if (strchr(seps, *p) != NULL) {
            if (start != p) {
                vector->strings[i] = strndup(start, (size_t) (p - start));
                if (vector->strings[i] == NULL)
                    goto fail;
                i++;
                vector->count++;
            }
            start = p + 1;
        }
    if (start != p) {
        vector->strings[i] = strndup(start, (size_t) (p - start));
        if (vector->strings[i] == NULL)
            goto fail;
        vector->count++;
    }
    return vector;

fail:
    if (created)
        vector_free(vector);
    return NULL;
}


/*
 * Given a vector and a path to a program, exec that program with the vector
 * as its arguments.  This requires adding a NULL terminator to the vector and
 * casting it appropriately.  Returns 0 on success and -1 on error, like exec
 * does.
 */
int
vector_exec(const char *path, struct vector *vector)
{
    if (vector->allocated == vector->count)
        if (!vector_resize(vector, vector->count + 1))
            return -1;
    vector->strings[vector->count] = NULL;
    return execv(path, (char * const *) vector->strings);
}


/*
 * Given a vector, a path to a program, and the environment, exec that program
 * with the vector as its arguments and the given environment.  This requires
 * adding a NULL terminator to the vector and casting it appropriately.
 * Returns 0 on success and -1 on error, like exec does.
 */
int
vector_exec_env(const char *path, struct vector *vector,
                const char * const env[])
{
    if (vector->allocated == vector->count)
        if (!vector_resize(vector, vector->count + 1))
            return -1;
    vector->strings[vector->count] = NULL;
    return execve(path, (char * const *) vector->strings,
                  (char * const *) env);
}