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
13static 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(SCR *, void *, size_t *, size_t);
43 */
44void *
45binc(
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(SCR *, recno_t, size_t *);
80 */
81int
82nonblank(
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(char *);
116 */
117char *
118tail(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(char *, char *);
132 */
133char *
134join(
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(char *);
152 */
153char *
154expanduser(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(char *);
202 */
203char *
204quote(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(SCR *, const char *, size_t);
249 */
250char *
251v_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(SCR *, const CHAR_T *, size_t);
271 */
272CHAR_T *
273v_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(u_long *, const CHAR_T *, CHAR_T **, int);
292 */
293enum nresult
294nget_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(long *, const CHAR_T *, CHAR_T **, int);
314 */
315enum nresult
316nget_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(struct timespec *);
340 */
341void
342timepoint_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(struct timespec *);
371 */
372void
373timepoint_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(SCR *, const char *, ...);
405 */
406void
407TRACE(
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