1 /*****************************************************************************
2  *  Written by Chris Dunlap <cdunlap@llnl.gov>.
3  *  Copyright (C) 2007-2018 Lawrence Livermore National Security, LLC.
4  *  Copyright (C) 2001-2007 The Regents of the University of California.
5  *  UCRL-CODE-2002-009.
6  *
7  *  This file is part of ConMan: The Console Manager.
8  *  For details, see <https://dun.github.io/conman/>.
9  *
10  *  ConMan is free software: you can redistribute it and/or modify it under
11  *  the terms of the GNU General Public License as published by the Free
12  *  Software Foundation, either version 3 of the License, or (at your option)
13  *  any later version.
14  *
15  *  ConMan is distributed in the hope that it will be useful, but WITHOUT
16  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
17  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
18  *  for more details.
19  *
20  *  You should have received a copy of the GNU General Public License along
21  *  with ConMan.  If not, see <http://www.gnu.org/licenses/>.
22  *****************************************************************************
23  *  Refer to "util-str.h" for documentation on public functions.
24  *****************************************************************************/
25 
26 
27 #if HAVE_CONFIG_H
28 #  include <config.h>
29 #endif /* HAVE_CONFIG_H */
30 
31 #include <assert.h>
32 #include <ctype.h>
33 #include <errno.h>
34 #include <pthread.h>
35 #include <stdarg.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <time.h>
40 #include "log.h"
41 #include "util-str.h"
42 #include "util.h"
43 #include "wrapper.h"
44 
45 
46 #define MAX_STR_SIZE 1024
47 
48 
create_string(const char * str)49 char * create_string(const char *str)
50 {
51     char *p;
52 
53     if (!str) {
54         return(NULL);
55     }
56     if (!(p = strdup(str))) {
57         out_of_memory();
58     }
59     return(p);
60 }
61 
62 
create_format_string(const char * fmt,...)63 char * create_format_string(const char *fmt, ...)
64 {
65     char buf[MAX_STR_SIZE];
66     va_list vargs;
67     char *p;
68 
69     if (!fmt) {
70         return(NULL);
71     }
72     va_start(vargs, fmt);
73     vsnprintf(buf, sizeof(buf), fmt, vargs);
74     va_end(vargs);
75 
76     buf[sizeof(buf) - 1] = '\0';        /* ensure buf is NUL-terminated */
77 
78     if (!(p = strdup(buf))) {
79         out_of_memory();
80     }
81     return(p);
82 }
83 
84 
replace_string(char ** dst,const char * src)85 int replace_string(char **dst, const char *src)
86 {
87     if (!dst) {
88         return(-1);
89     }
90     if (*dst) {
91         free(*dst);
92     }
93     if (!(*dst = strdup(src))) {
94         out_of_memory();
95     }
96     return(0);
97 }
98 
99 
destroy_string(char * str)100 void destroy_string(char *str)
101 {
102     if (str) {
103         free(str);
104     }
105     return;
106 }
107 
108 
is_empty_string(const char * str)109 int is_empty_string(const char *str)
110 {
111     if (!str) {
112         return(-1);
113     }
114     for (; *str; str++) {
115         if (!isspace((int) *str)) {
116             return(0);
117         }
118     }
119     return(1);
120 }
121 
122 
parse_string(char * src,char ** dst_p,char ** ptr_p,char * quote_p)123 int parse_string(char *src, char **dst_p, char **ptr_p, char *quote_p)
124 {
125     char *p;
126     char *q;
127     char c = 0;
128 
129     if (!dst_p) {
130         errno = EINVAL;
131         return(-1);
132     }
133     if (!src || !ptr_p) {
134         *dst_p = NULL;
135         errno = EINVAL;
136         return(-1);
137     }
138     if (*ptr_p == NULL) {
139         *ptr_p = src;
140     }
141     for (p = *ptr_p; *p && isspace((int) *p); p++) {
142         ;
143     }
144     if (*p == '\0') {
145         *dst_p = *ptr_p = p;
146         return(0);
147     }
148     for (q = p+1; *q; q++) {
149         if ((*p == '"') || (*p == '\'')) {
150             if ((*q == *p) && (isspace((int) *(q+1)) || (*(q+1) == '\0'))) {
151                 c = *p++;
152                 *q++ = '\0';
153                 break;
154             }
155             else if (*(q+1) == '\0') {
156                 errno = EIO;
157                 *dst_p = p;
158                 *ptr_p = q + 1;
159                 return(-1);
160             }
161         }
162         else if (isspace((int) *q)) {
163             *q++ = '\0';
164             break;
165         }
166     }
167     *dst_p = p;
168     *ptr_p = q;
169     if (quote_p) {
170         *quote_p = c;
171     }
172     return(1);
173 }
174 
175 
append_format_string(char * dst,size_t size,const char * fmt,...)176 int append_format_string(char *dst, size_t size, const char *fmt, ...)
177 {
178     char *p;
179     size_t num_left;
180     size_t num_orig;
181     va_list vargs;
182     int n;
183 
184     if ((dst == NULL) || (fmt == NULL)) {
185         errno = EINVAL;
186         return(-1);
187     }
188     if (size == 0) {
189         return(0);
190     }
191     p = dst;
192     num_left = size;
193     while ((*p != '\0') && (num_left > 0)) {
194         p++;
195         num_left--;
196     }
197     /*  If (num_left == 0), dst[] is full but requires null-termination.
198      *  If (num_left == 1), dst[] is full but is already null-terminated.
199      */
200     if (num_left <= 1) {
201         dst[size - 1] = '\0';
202         return(-1);
203     }
204     num_orig = p - dst;
205     assert(num_left + num_orig == size);
206 
207     va_start(vargs, fmt);
208     n = vsnprintf(p, num_left, fmt, vargs);
209     va_end(vargs);
210 
211     if ((n < 0) || ((size_t) n >= num_left)) {
212         return(-1);
213     }
214     return((int) num_orig + n);
215 }
216 
217 
substitute_string(char * dst,size_t dstlen,const char * src,char c,const char * sub)218 int substitute_string(char *dst, size_t dstlen, const char *src,
219     char c, const char *sub)
220 {
221     const char *p;
222     char *q;
223     int len;
224     int n;
225 
226     if (!dst || (dstlen <= 0) || !src || !c) {
227         errno = EINVAL;
228         return (-1);
229     }
230     p = src;
231     q = dst;
232     len = dstlen;
233 
234     while (*p && (len > 0)) {
235         if ((*p == '%') && (*(p+1) == c)) {
236             if (sub) {
237                 n = strlcpy(q, sub, len);
238                 q += n;
239                 len -= n;
240             }
241             p += 2;
242         }
243         else {
244             *q++ = *p++;
245             len--;
246         }
247     }
248     if (len > 0) {
249         *q = '\0';
250         return(dstlen - len);
251     }
252     else {
253         dst[dstlen - 1] = '\0';
254         return (-1);
255     }
256 }
257 
258 
create_long_time_string(time_t t)259 char * create_long_time_string(time_t t)
260 {
261     char *p;
262     struct tm tm;
263     const int len = 32;
264 
265     if (!(p = malloc(len))) {
266         out_of_memory();
267     }
268     get_localtime(&t, &tm);
269 
270     if (strftime(p, len, "%Y-%m-%d %H:%M:%S %Z", &tm) == 0) {
271         log_err(0, "strftime() failed");
272     }
273     return(p);
274 }
275 
276 
create_short_time_string(time_t t)277 char * create_short_time_string(time_t t)
278 {
279     char *p;
280     struct tm tm;
281     const int len = 12;                 /* MM-DD HH:MM + NUL */
282 
283     if (!(p = malloc(len))) {
284         out_of_memory();
285     }
286     get_localtime(&t, &tm);
287 
288     if (strftime(p, len, "%m-%d %H:%M", &tm) == 0) {
289         log_err(0, "strftime() failed");
290     }
291     return(p);
292 }
293 
294 
create_time_delta_string(time_t t0,time_t t1)295 char * create_time_delta_string(time_t t0, time_t t1)
296 {
297     long n;
298     int years, weeks, days, hours, minutes, seconds;
299     char buf[25];
300 
301     if (t1 == (time_t) -1) {
302         if (time(&t1) == (time_t) -1) {
303             log_err(errno, "time() failed");
304         }
305     }
306     n = difftime(t1, t0);
307     assert(n >= 0);
308 
309     seconds = n % 60;
310     n /= 60;
311     minutes = n % 60;
312     n /= 60;
313     hours = n % 24;
314     n /= 24;
315     days = n % 7;
316     n /= 7;
317     weeks = n % 52;
318     n /= 52;
319     years = n;
320 
321     if (years > 0) {
322         n = snprintf(buf, sizeof(buf), "%dy%dw%dd%dh%dm%ds",
323             years, weeks, days, hours, minutes, seconds);
324     }
325     else if (weeks > 0) {
326         n = snprintf(buf, sizeof(buf), "%dw%dd%dh%dm%ds",
327             weeks, days, hours, minutes, seconds);
328     }
329     else if (days > 0) {
330         n = snprintf(buf, sizeof(buf), "%dd%dh%dm%ds",
331             days, hours, minutes, seconds);
332     }
333     else if (hours > 0) {
334         n = snprintf(buf, sizeof(buf), "%dh%dm%ds", hours, minutes, seconds);
335     }
336     else if (minutes > 0) {
337         n = snprintf(buf, sizeof(buf), "%dm%ds", minutes, seconds);
338     }
339     else {
340         n = snprintf(buf, sizeof(buf), "%ds", seconds);
341     }
342     assert((n >= 0) && ((size_t) n < sizeof(buf)));
343     return(create_string(buf));
344 }
345 
346 
write_time_string(time_t t,char * dst,size_t dstlen)347 int write_time_string(time_t t, char *dst, size_t dstlen)
348 {
349     struct tm tm;
350     int n;
351 
352     if (dstlen <= 20) {                 /* "YYYY-MM-DD HH:MM:SS " + NUL */
353         return(0);
354     }
355     get_localtime(&t, &tm);
356 
357     if (!(n = strftime(dst, dstlen, "%Y-%m-%d %H:%M:%S ", &tm))) {
358         return(0);
359     }
360     assert(n == 20);
361     return(n);
362 }
363 
364 
get_localtime(time_t * tPtr,struct tm * tmPtr)365 struct tm * get_localtime(time_t *tPtr, struct tm *tmPtr)
366 {
367 #if ! HAVE_LOCALTIME_R
368 
369     static pthread_mutex_t localtimeLock = PTHREAD_MUTEX_INITIALIZER;
370     struct tm *tmTmpPtr;
371 
372 #endif /* !HAVE_LOCALTIME_R */
373 
374     assert(tPtr != NULL);
375     assert(tmPtr != NULL);
376 
377     if (*tPtr == 0) {
378         if (time(tPtr) == (time_t) -1) {
379             log_err(errno, "time() failed");
380         }
381     }
382 
383 #if ! HAVE_LOCALTIME_R
384 
385     /*  localtime() is not thread-safe, so it is protected by a mutex.
386      */
387     x_pthread_mutex_lock(&localtimeLock);
388     if (!(tmTmpPtr = localtime(tPtr))) {
389         log_err(errno, "localtime() failed");
390     }
391     *tmPtr = *tmTmpPtr;
392     x_pthread_mutex_unlock(&localtimeLock);
393 
394 #else /* HAVE_LOCALTIME_R */
395 
396     if (!localtime_r(tPtr, tmPtr)) {
397         log_err(errno, "localtime_r() failed");
398     }
399 
400 #endif /* !HAVE_LOCALTIME_R */
401 
402     return(tmPtr);
403 }
404 
405 
406 #if ! HAVE_STRCASECMP
strcasecmp(const char * s1,const char * s2)407 int strcasecmp(const char *s1, const char *s2)
408 {
409     const char *p, *q;
410 
411     p = s1;
412     q = s2;
413     while (*p && toupper((int) *p) == toupper((int) *q)) {
414         p++, q++;
415     }
416     return(toupper((int) *p) - toupper((int) *q));
417 }
418 #endif /* !HAVE_STRCASECMP */
419 
420 
421 #if ! HAVE_STRNCASECMP
strncasecmp(const char * s1,const char * s2,size_t n)422 int strncasecmp(const char *s1, const char *s2, size_t n)
423 {
424     const char *p, *q;
425 
426     if (!n) {
427         return(0);
428     }
429     p = s1;
430     q = s2;
431     while (--n && *p && toupper((int) *p) == toupper((int) *q)) {
432         p++, q++;
433     }
434     return(toupper((int) *p) - toupper((int) *q));
435 }
436 #endif /* !HAVE_STRNCASECMP */
437 
438 
439 #if ! HAVE_TOINT
toint(int c)440 int toint(int c)
441 {
442 /*  Returns the "weight" (0-15) of a hexadecimal digit 'c'.
443  *
444  *  Implementation from "C: A Reference Manual, 5e" by Harbison & Steele.
445  */
446     if (c >= '0' && c <= '9') {
447         return(c - '0');
448     }
449     if (c >= 'A' && c <= 'F') {
450         return(c - 'A' + 10);
451     }
452     if (c >= 'a' && c <= 'f') {
453         return(c - 'a' + 10);
454     }
455     return(0);
456 }
457 #endif /* !HAVE_TOINT */
458