1 /* $NetBSD: argv.c,v 1.2 2017/02/14 01:16:48 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* argv 3 6 /* SUMMARY 7 /* string array utilities 8 /* SYNOPSIS 9 /* #include <argv.h> 10 /* 11 /* ARGV *argv_alloc(len) 12 /* ssize_t len; 13 /* 14 /* ARGV *argv_sort(argvp) 15 /* ARGV *argvp; 16 /* 17 /* ARGV *argv_free(argvp) 18 /* ARGV *argvp; 19 /* 20 /* void argv_add(argvp, arg, ..., ARGV_END) 21 /* ARGV *argvp; 22 /* char *arg; 23 /* 24 /* void argv_addn(argvp, arg, arg_len, ..., ARGV_END) 25 /* ARGV *argvp; 26 /* char *arg; 27 /* ssize_t arg_len; 28 /* 29 /* void argv_terminate(argvp); 30 /* ARGV *argvp; 31 /* 32 /* void argv_truncate(argvp, len); 33 /* ARGV *argvp; 34 /* ssize_t len; 35 /* 36 /* void argv_insert_one(argvp, pos, arg) 37 /* ARGV *argvp; 38 /* ssize_t pos; 39 /* const char *arg; 40 /* 41 /* void argv_replace_one(argvp, pos, arg) 42 /* ARGV *argvp; 43 /* ssize_t pos; 44 /* const char *arg; 45 /* 46 /* void argv_delete(argvp, pos, how_many) 47 /* ARGV *argvp; 48 /* ssize_t pos; 49 /* ssize_t how_many; 50 /* 51 /* void ARGV_FAKE_BEGIN(argv, arg) 52 /* const char *arg; 53 /* 54 /* void ARGV_FAKE_END 55 /* DESCRIPTION 56 /* The functions in this module manipulate arrays of string 57 /* pointers. An ARGV structure contains the following members: 58 /* .IP len 59 /* The length of the \fIargv\fR array member. 60 /* .IP argc 61 /* The number of \fIargv\fR elements used. 62 /* .IP argv 63 /* An array of pointers to null-terminated strings. 64 /* .PP 65 /* argv_alloc() returns an empty string array of the requested 66 /* length. The result is ready for use by argv_add(). The array 67 /* is null terminated. 68 /* 69 /* argv_sort() sorts the elements of argvp in place returning 70 /* the original array. 71 /* 72 /* argv_add() copies zero or more strings and adds them to the 73 /* specified string array. The array is null terminated. 74 /* Terminate the argument list with a null pointer. The manifest 75 /* constant ARGV_END provides a convenient notation for this. 76 /* 77 /* argv_addn() is like argv_add(), but each string is followed 78 /* by a string length argument. 79 /* 80 /* argv_free() releases storage for a string array, and conveniently 81 /* returns a null pointer. 82 /* 83 /* argv_terminate() null-terminates its string array argument. 84 /* 85 /* argv_truncate() trucates its argument to the specified 86 /* number of entries, but does not reallocate memory. The 87 /* result is null-terminated. 88 /* 89 /* argv_insert_one() inserts one string at the specified array 90 /* position. 91 /* 92 /* argv_replace_one() replaces one string at the specified 93 /* position. The old string is destroyed after the update is 94 /* made. 95 /* 96 /* argv_delete() deletes the specified number of elements 97 /* starting at the specified array position. The result is 98 /* null-terminated. 99 /* 100 /* ARGV_FAKE_BEGIN/END are an optimization for the case where 101 /* a single string needs to be passed into an ARGV-based 102 /* interface. ARGV_FAKE_BEGIN() opens a statement block and 103 /* allocates a stack-based ARGV structure named after the first 104 /* argument, that encapsulates the second argument. This 105 /* implementation allocates no heap memory and creates no copy 106 /* of the second argument. ARGV_FAKE_END closes the statement 107 /* block and thereby releases storage. 108 /* SEE ALSO 109 /* msg(3) diagnostics interface 110 /* DIAGNOSTICS 111 /* Fatal errors: memory allocation problem. 112 /* LICENSE 113 /* .ad 114 /* .fi 115 /* The Secure Mailer license must be distributed with this software. 116 /* AUTHOR(S) 117 /* Wietse Venema 118 /* IBM T.J. Watson Research 119 /* P.O. Box 704 120 /* Yorktown Heights, NY 10598, USA 121 /*--*/ 122 123 /* System libraries. */ 124 125 #include <sys_defs.h> 126 #include <stdlib.h> /* 44BSD stdarg.h uses abort() */ 127 #include <stdarg.h> 128 #include <string.h> 129 130 /* Application-specific. */ 131 132 #include "mymalloc.h" 133 #include "msg.h" 134 #include "argv.h" 135 136 /* argv_free - destroy string array */ 137 138 ARGV *argv_free(ARGV *argvp) 139 { 140 char **cpp; 141 142 for (cpp = argvp->argv; cpp < argvp->argv + argvp->argc; cpp++) 143 myfree(*cpp); 144 myfree((void *) argvp->argv); 145 myfree((void *) argvp); 146 return (0); 147 } 148 149 /* argv_alloc - initialize string array */ 150 151 ARGV *argv_alloc(ssize_t len) 152 { 153 ARGV *argvp; 154 ssize_t sane_len; 155 156 /* 157 * Make sure that always argvp->argc < argvp->len. 158 */ 159 argvp = (ARGV *) mymalloc(sizeof(*argvp)); 160 argvp->len = 0; 161 sane_len = (len < 2 ? 2 : len); 162 argvp->argv = (char **) mymalloc((sane_len + 1) * sizeof(char *)); 163 argvp->len = sane_len; 164 argvp->argc = 0; 165 argvp->argv[0] = 0; 166 return (argvp); 167 } 168 169 static int argv_cmp(const void *e1, const void *e2) 170 { 171 const char *s1 = *(const char **) e1; 172 const char *s2 = *(const char **) e2; 173 174 return strcmp(s1, s2); 175 } 176 177 /* argv_sort - sort array in place */ 178 179 ARGV *argv_sort(ARGV *argvp) 180 { 181 qsort(argvp->argv, argvp->argc, sizeof(argvp->argv[0]), argv_cmp); 182 return (argvp); 183 } 184 185 /* argv_extend - extend array */ 186 187 static void argv_extend(ARGV *argvp) 188 { 189 ssize_t new_len; 190 191 new_len = argvp->len * 2; 192 argvp->argv = (char **) 193 myrealloc((void *) argvp->argv, (new_len + 1) * sizeof(char *)); 194 argvp->len = new_len; 195 } 196 197 /* argv_add - add string to vector */ 198 199 void argv_add(ARGV *argvp,...) 200 { 201 char *arg; 202 va_list ap; 203 204 /* 205 * Make sure that always argvp->argc < argvp->len. 206 */ 207 #define ARGV_SPACE_LEFT(a) ((a)->len - (a)->argc - 1) 208 209 va_start(ap, argvp); 210 while ((arg = va_arg(ap, char *)) != 0) { 211 if (ARGV_SPACE_LEFT(argvp) <= 0) 212 argv_extend(argvp); 213 argvp->argv[argvp->argc++] = mystrdup(arg); 214 } 215 va_end(ap); 216 argvp->argv[argvp->argc] = 0; 217 } 218 219 /* argv_addn - add string to vector */ 220 221 void argv_addn(ARGV *argvp,...) 222 { 223 char *arg; 224 ssize_t len; 225 va_list ap; 226 227 /* 228 * Make sure that always argvp->argc < argvp->len. 229 */ 230 va_start(ap, argvp); 231 while ((arg = va_arg(ap, char *)) != 0) { 232 if ((len = va_arg(ap, ssize_t)) < 0) 233 msg_panic("argv_addn: bad string length %ld", (long) len); 234 if (ARGV_SPACE_LEFT(argvp) <= 0) 235 argv_extend(argvp); 236 argvp->argv[argvp->argc++] = mystrndup(arg, len); 237 } 238 va_end(ap); 239 argvp->argv[argvp->argc] = 0; 240 } 241 242 /* argv_terminate - terminate string array */ 243 244 void argv_terminate(ARGV *argvp) 245 { 246 247 /* 248 * Trust that argvp->argc < argvp->len. 249 */ 250 argvp->argv[argvp->argc] = 0; 251 } 252 253 /* argv_truncate - truncate string array */ 254 255 void argv_truncate(ARGV *argvp, ssize_t len) 256 { 257 char **cpp; 258 259 /* 260 * Sanity check. 261 */ 262 if (len < 0) 263 msg_panic("argv_truncate: bad length %ld", (long) len); 264 265 if (len < argvp->argc) { 266 for (cpp = argvp->argv + len; cpp < argvp->argv + argvp->argc; cpp++) 267 myfree(*cpp); 268 argvp->argc = len; 269 argvp->argv[argvp->argc] = 0; 270 } 271 } 272 273 /* argv_insert_one - insert one string into array */ 274 275 void argv_insert_one(ARGV *argvp, ssize_t where, const char *arg) 276 { 277 ssize_t pos; 278 279 /* 280 * Sanity check. 281 */ 282 if (where < 0 || where > argvp->argc) 283 msg_panic("argv_insert_one bad position: %ld", (long) where); 284 285 if (ARGV_SPACE_LEFT(argvp) <= 0) 286 argv_extend(argvp); 287 for (pos = argvp->argc; pos >= where; pos--) 288 argvp->argv[pos + 1] = argvp->argv[pos]; 289 argvp->argv[where] = mystrdup(arg); 290 argvp->argc += 1; 291 } 292 293 /* argv_replace_one - replace one string in array */ 294 295 void argv_replace_one(ARGV *argvp, ssize_t where, const char *arg) 296 { 297 char *temp; 298 299 /* 300 * Sanity check. 301 */ 302 if (where < 0 || where >= argvp->argc) 303 msg_panic("argv_replace_one bad position: %ld", (long) where); 304 305 temp = argvp->argv[where]; 306 argvp->argv[where] = mystrdup(arg); 307 myfree(temp); 308 } 309 310 /* argv_delete - remove string(s) from array */ 311 312 void argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many) 313 { 314 ssize_t pos; 315 316 /* 317 * Sanity check. 318 */ 319 if (first < 0 || how_many < 0 || first + how_many > argvp->argc) 320 msg_panic("argv_delete bad range: (start=%ld count=%ld)", 321 (long) first, (long) how_many); 322 323 for (pos = first; pos < first + how_many; pos++) 324 myfree(argvp->argv[pos]); 325 for (pos = first; pos <= argvp->argc - how_many; pos++) 326 argvp->argv[pos] = argvp->argv[pos + how_many]; 327 argvp->argc -= how_many; 328 } 329