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