xref: /freebsd/contrib/nvi/common/util.c (revision 8aac90f18aef7c9eea906c3ff9a001ca7b94f375)
1 /*-
2  * Copyright (c) 1991, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  * Copyright (c) 1991, 1993, 1994, 1995, 1996
5  *	Keith Bostic.  All rights reserved.
6  *
7  * See the LICENSE file for redistribution information.
8  */
9 
10 #include "config.h"
11 
12 #include <sys/types.h>
13 #include <sys/queue.h>
14 
15 #ifdef __APPLE__
16 #include <mach/clock.h>
17 #include <mach/mach.h>
18 #include <mach/mach_time.h>
19 #endif
20 
21 #include <bitstring.h>
22 #include <ctype.h>
23 #include <errno.h>
24 #include <limits.h>
25 #include <pwd.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <time.h>
30 #include <unistd.h>
31 
32 #include "common.h"
33 
34 /*
35  * binc --
36  *	Increase the size of a buffer.
37  *
38  * PUBLIC: void *binc(SCR *, void *, size_t *, size_t);
39  */
40 void *
41 binc(SCR *sp,			/* sp MAY BE NULL!!! */
42     void *bp, size_t *bsizep, size_t min)
43 {
44 	size_t csize;
45 
46 	/* If already larger than the minimum, just return. */
47 	if (min && *bsizep >= min)
48 		return (bp);
49 
50 	csize = p2roundup(MAX(min, 256));
51 	REALLOC(sp, bp, void *, csize);
52 
53 	if (bp == NULL) {
54 		*bsizep = 0;
55 		return (NULL);
56 	}
57 	/*
58 	 * Memory is guaranteed to be zero-filled, various parts of
59 	 * nvi depend on this.
60 	 */
61 	memset((char *)bp + *bsizep, 0, csize - *bsizep);
62 	*bsizep = csize;
63 	return (bp);
64 }
65 
66 /*
67  * nonblank --
68  *	Set the column number of the first non-blank character
69  *	including or after the starting column.  On error, set
70  *	the column to 0, it's safest.
71  *
72  * PUBLIC: int nonblank(SCR *, recno_t, size_t *);
73  */
74 int
75 nonblank(SCR *sp, recno_t lno, size_t *cnop)
76 {
77 	CHAR_T *p;
78 	size_t cnt, len, off;
79 	int isempty;
80 
81 	/* Default. */
82 	off = *cnop;
83 	*cnop = 0;
84 
85 	/* Get the line, succeeding in an empty file. */
86 	if (db_eget(sp, lno, &p, &len, &isempty))
87 		return (!isempty);
88 
89 	/* Set the offset. */
90 	if (len == 0 || off >= len)
91 		return (0);
92 
93 	for (cnt = off, p = &p[off],
94 	    len -= off; len && ISBLANK(*p); ++cnt, ++p, --len);
95 
96 	/* Set the return. */
97 	*cnop = len ? cnt : cnt - 1;
98 	return (0);
99 }
100 
101 /*
102  * join --
103  *	Join two paths; need free.
104  *
105  * PUBLIC: char *join(char *, char *);
106  */
107 char *
108 join(char *path1, char *path2)
109 {
110 	char *p;
111 
112 	if (path1[0] == '\0' || path2[0] == '/')
113 		return strdup(path2);
114 	if (asprintf(&p, path1[strlen(path1)-1] == '/' ?
115 	    "%s%s" : "%s/%s", path1, path2) == -1)
116 		return NULL;
117 	return p;
118 }
119 
120 /*
121  * expanduser --
122  *	Return a "~" or "~user" expanded path; need free.
123  *
124  * PUBLIC: char *expanduser(char *);
125  */
126 char *
127 expanduser(char *str)
128 {
129 	struct passwd *pwd;
130 	char *p, *t, *u, *h;
131 
132 	/*
133 	 * This function always expands the content between the
134 	 * leading '~' and the first '/' or '\0' from the input.
135 	 * Return NULL whenever we fail to do so.
136 	 */
137 	if (*str != '~')
138 		return (NULL);
139 	p = str + 1;
140 	for (t = p; *t != '/' && *t != '\0'; ++t)
141 		continue;
142 	if (t == p) {
143 		/* ~ */
144 #ifdef __GLIBC__
145 		extern char *secure_getenv(const char *);
146 		if ((h = secure_getenv("HOME")) == NULL) {
147 #else
148 		if (issetugid() != 0 ||
149 		    (h = getenv("HOME")) == NULL) {
150 #endif
151 			if (((h = getlogin()) != NULL &&
152 			     (pwd = getpwnam(h)) != NULL) ||
153 			    (pwd = getpwuid(getuid())) != NULL)
154 				h = pwd->pw_dir;
155 			else
156 				return (NULL);
157 		}
158 	} else {
159 		/* ~user */
160 		if ((u = strndup(p, t - p)) == NULL)
161 			return (NULL);
162 		if ((pwd = getpwnam(u)) == NULL) {
163 			free(u);
164 			return (NULL);
165 		} else
166 			h = pwd->pw_dir;
167 		free(u);
168 	}
169 
170 	for (; *t == '/' && *t != '\0'; ++t)
171 		continue;
172 	return (join(h, t));
173 }
174 
175 /*
176  * quote --
177  *	Return a escaped string for /bin/sh; need free.
178  *
179  * PUBLIC: char *quote(char *);
180  */
181 char *
182 quote(char *str)
183 {
184 	char *p, *t;
185 	size_t i = 0, n = 0;
186 	int unsafe = 0;
187 
188 	for (p = str; *p != '\0'; p++, i++) {
189 		if (*p == '\'')
190 			n++;
191 		if (unsafe)
192 			continue;
193 		if (isascii((u_char)*p)) {
194 			if (isalnum((u_char)*p))
195 				continue;
196 			switch (*p) {
197 			case '%': case '+': case ',': case '-': case '.':
198 			case '/': case ':': case '=': case '@': case '_':
199 				continue;
200 			}
201 		}
202 		unsafe = 1;
203 	}
204 	if (!unsafe)
205 		t = strdup(str);
206 #define SQT "'\\''"
207 	else if ((p = t = malloc(i + n * (sizeof(SQT) - 2) + 3)) != NULL) {
208 		*p++ = '\'';
209 		for (; *str != '\0'; str++) {
210 			if (*str == '\'') {
211 				(void)memcpy(p, SQT, sizeof(SQT) - 1);
212 				p += sizeof(SQT) - 1;
213 			} else
214 				*p++ = *str;
215 		}
216 		*p++ = '\'';
217 		*p = '\0';
218 	}
219 	return t;
220 }
221 
222 /*
223  * v_strdup --
224  *	Strdup for 8-bit character strings with an associated length.
225  *
226  * PUBLIC: char *v_strdup(SCR *, const char *, size_t);
227  */
228 char *
229 v_strdup(SCR *sp, const char *str, size_t len)
230 {
231 	char *copy;
232 
233 	MALLOC(sp, copy, len + 1);
234 	if (copy == NULL)
235 		return (NULL);
236 	memcpy(copy, str, len);
237 	copy[len] = '\0';
238 	return (copy);
239 }
240 
241 /*
242  * v_wstrdup --
243  *	Strdup for wide character strings with an associated length.
244  *
245  * PUBLIC: CHAR_T *v_wstrdup(SCR *, const CHAR_T *, size_t);
246  */
247 CHAR_T *
248 v_wstrdup(SCR *sp, const CHAR_T *str, size_t len)
249 {
250 	CHAR_T *copy;
251 
252 	MALLOC(sp, copy, (len + 1) * sizeof(CHAR_T));
253 	if (copy == NULL)
254 		return (NULL);
255 	MEMCPY(copy, str, len);
256 	copy[len] = '\0';
257 	return (copy);
258 }
259 
260 /*
261  * nget_uslong --
262  *      Get an unsigned long, checking for overflow.
263  *
264  * PUBLIC: enum nresult nget_uslong(u_long *, const CHAR_T *, CHAR_T **, int);
265  */
266 enum nresult
267 nget_uslong(u_long *valp, const CHAR_T *p, CHAR_T **endp, int base)
268 {
269 	errno = 0;
270 	*valp = STRTOUL(p, endp, base);
271 	if (errno == 0)
272 		return (NUM_OK);
273 	if (errno == ERANGE && *valp == ULONG_MAX)
274 		return (NUM_OVER);
275 	return (NUM_ERR);
276 }
277 
278 /*
279  * nget_slong --
280  *      Convert a signed long, checking for overflow and underflow.
281  *
282  * PUBLIC: enum nresult nget_slong(long *, const CHAR_T *, CHAR_T **, int);
283  */
284 enum nresult
285 nget_slong(long *valp, const CHAR_T *p, CHAR_T **endp, int base)
286 {
287 	errno = 0;
288 	*valp = STRTOL(p, endp, base);
289 	if (errno == 0)
290 		return (NUM_OK);
291 	if (errno == ERANGE) {
292 		if (*valp == LONG_MAX)
293 			return (NUM_OVER);
294 		if (*valp == LONG_MIN)
295 			return (NUM_UNDER);
296 	}
297 	return (NUM_ERR);
298 }
299 
300 /*
301  * timepoint_steady --
302  *      Get a timestamp from a monotonic clock.
303  *
304  * PUBLIC: void timepoint_steady(struct timespec *);
305  */
306 void
307 timepoint_steady(struct timespec *ts)
308 {
309 #ifdef __APPLE__
310 	static mach_timebase_info_data_t base = { 0 };
311 	uint64_t val;
312 	uint64_t ns;
313 
314 	if (base.denom == 0)
315 		(void)mach_timebase_info(&base);
316 
317 	val = mach_absolute_time();
318 	ns = val * base.numer / base.denom;
319 	ts->tv_sec = ns / 1000000000;
320 	ts->tv_nsec = ns % 1000000000;
321 #else
322 #ifdef CLOCK_MONOTONIC_FAST
323 	(void)clock_gettime(CLOCK_MONOTONIC_FAST, ts);
324 #else
325 	(void)clock_gettime(CLOCK_MONOTONIC, ts);
326 #endif
327 #endif
328 }
329 
330 /*
331  * timepoint_system --
332  *      Get the current calendar time.
333  *
334  * PUBLIC: void timepoint_system(struct timespec *);
335  */
336 void
337 timepoint_system(struct timespec *ts)
338 {
339 #ifdef __APPLE__
340 	clock_serv_t clk;
341 	mach_timespec_t mts;
342 	kern_return_t kr;
343 
344 	kr = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clk);
345 	if (kr != KERN_SUCCESS)
346 		return;
347 	(void)clock_get_time(clk, &mts);
348 	(void)mach_port_deallocate(mach_task_self(), clk);
349 	ts->tv_sec = mts.tv_sec;
350 	ts->tv_nsec = mts.tv_nsec;
351 #else
352 #ifdef CLOCK_REALTIME_FAST
353 	(void)clock_gettime(CLOCK_REALTIME_FAST, ts);
354 #else
355 	(void)clock_gettime(CLOCK_REALTIME, ts);
356 #endif
357 #endif
358 }
359 
360 #ifdef DEBUG
361 #include <stdarg.h>
362 
363 /*
364  * TRACE --
365  *	debugging trace routine.
366  *
367  * PUBLIC: void TRACE(SCR *, const char *, ...);
368  */
369 void
370 TRACE(SCR *sp, const char *fmt, ...)
371 {
372 	FILE *tfp;
373 	va_list ap;
374 
375 	if ((tfp = sp->gp->tracefp) == NULL)
376 		return;
377 	va_start(ap, fmt);
378 	(void)vfprintf(tfp, fmt, ap);
379 	va_end(ap);
380 
381 	(void)fflush(tfp);
382 }
383 #endif
384