freebsd-dev/contrib/nvi/common/util.c
Baptiste Daroussin 755cc40c21 Update nvi to 2.2.0-05ed8b9
This version incorporates many fixes in particular a fix for vi -w
Another approach was proposed to merge those fixes (see review), I find
it easier to track changes if we keep importing snapshot on regular
basis

PR:		241985
Reported by:	fernape
Differential Revision:	https://reviews.freebsd.org/D26158
2020-10-01 04:46:23 +00:00

384 lines
7.3 KiB
C

/*-
* Copyright (c) 1991, 1993, 1994
* The Regents of the University of California. All rights reserved.
* Copyright (c) 1991, 1993, 1994, 1995, 1996
* Keith Bostic. All rights reserved.
*
* See the LICENSE file for redistribution information.
*/
#include "config.h"
#include <sys/types.h>
#include <sys/queue.h>
#ifdef __APPLE__
#include <mach/clock.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#endif
#include <bitstring.h>
#include <ctype.h>
#include <errno.h>
#include <limits.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include "common.h"
/*
* binc --
* Increase the size of a buffer.
*
* PUBLIC: void *binc(SCR *, void *, size_t *, size_t);
*/
void *
binc(SCR *sp, /* sp MAY BE NULL!!! */
void *bp, size_t *bsizep, size_t min)
{
size_t csize;
/* If already larger than the minimum, just return. */
if (min && *bsizep >= min)
return (bp);
csize = p2roundup(MAX(min, 256));
REALLOC(sp, bp, void *, csize);
if (bp == NULL) {
*bsizep = 0;
return (NULL);
}
/*
* Memory is guaranteed to be zero-filled, various parts of
* nvi depend on this.
*/
memset((char *)bp + *bsizep, 0, csize - *bsizep);
*bsizep = csize;
return (bp);
}
/*
* nonblank --
* Set the column number of the first non-blank character
* including or after the starting column. On error, set
* the column to 0, it's safest.
*
* PUBLIC: int nonblank(SCR *, recno_t, size_t *);
*/
int
nonblank(SCR *sp, recno_t lno, size_t *cnop)
{
CHAR_T *p;
size_t cnt, len, off;
int isempty;
/* Default. */
off = *cnop;
*cnop = 0;
/* Get the line, succeeding in an empty file. */
if (db_eget(sp, lno, &p, &len, &isempty))
return (!isempty);
/* Set the offset. */
if (len == 0 || off >= len)
return (0);
for (cnt = off, p = &p[off],
len -= off; len && ISBLANK(*p); ++cnt, ++p, --len);
/* Set the return. */
*cnop = len ? cnt : cnt - 1;
return (0);
}
/*
* join --
* Join two paths; need free.
*
* PUBLIC: char *join(char *, char *);
*/
char *
join(char *path1, char *path2)
{
char *p;
if (path1[0] == '\0' || path2[0] == '/')
return strdup(path2);
if (asprintf(&p, path1[strlen(path1)-1] == '/' ?
"%s%s" : "%s/%s", path1, path2) == -1)
return NULL;
return p;
}
/*
* expanduser --
* Return a "~" or "~user" expanded path; need free.
*
* PUBLIC: char *expanduser(char *);
*/
char *
expanduser(char *str)
{
struct passwd *pwd;
char *p, *t, *u, *h;
/*
* This function always expands the content between the
* leading '~' and the first '/' or '\0' from the input.
* Return NULL whenever we fail to do so.
*/
if (*str != '~')
return (NULL);
p = str + 1;
for (t = p; *t != '/' && *t != '\0'; ++t)
continue;
if (t == p) {
/* ~ */
#ifdef __GLIBC__
extern char *secure_getenv(const char *);
if ((h = secure_getenv("HOME")) == NULL) {
#else
if (issetugid() != 0 ||
(h = getenv("HOME")) == NULL) {
#endif
if (((h = getlogin()) != NULL &&
(pwd = getpwnam(h)) != NULL) ||
(pwd = getpwuid(getuid())) != NULL)
h = pwd->pw_dir;
else
return (NULL);
}
} else {
/* ~user */
if ((u = strndup(p, t - p)) == NULL)
return (NULL);
if ((pwd = getpwnam(u)) == NULL) {
free(u);
return (NULL);
} else
h = pwd->pw_dir;
free(u);
}
for (; *t == '/' && *t != '\0'; ++t)
continue;
return (join(h, t));
}
/*
* quote --
* Return a escaped string for /bin/sh; need free.
*
* PUBLIC: char *quote(char *);
*/
char *
quote(char *str)
{
char *p, *t;
size_t i = 0, n = 0;
int unsafe = 0;
for (p = str; *p != '\0'; p++, i++) {
if (*p == '\'')
n++;
if (unsafe)
continue;
if (isascii((u_char)*p)) {
if (isalnum((u_char)*p))
continue;
switch (*p) {
case '%': case '+': case ',': case '-': case '.':
case '/': case ':': case '=': case '@': case '_':
continue;
}
}
unsafe = 1;
}
if (!unsafe)
t = strdup(str);
#define SQT "'\\''"
else if ((p = t = malloc(i + n * (sizeof(SQT) - 2) + 3)) != NULL) {
*p++ = '\'';
for (; *str != '\0'; str++) {
if (*str == '\'') {
(void)memcpy(p, SQT, sizeof(SQT) - 1);
p += sizeof(SQT) - 1;
} else
*p++ = *str;
}
*p++ = '\'';
*p = '\0';
}
return t;
}
/*
* v_strdup --
* Strdup for 8-bit character strings with an associated length.
*
* PUBLIC: char *v_strdup(SCR *, const char *, size_t);
*/
char *
v_strdup(SCR *sp, const char *str, size_t len)
{
char *copy;
MALLOC(sp, copy, len + 1);
if (copy == NULL)
return (NULL);
memcpy(copy, str, len);
copy[len] = '\0';
return (copy);
}
/*
* v_wstrdup --
* Strdup for wide character strings with an associated length.
*
* PUBLIC: CHAR_T *v_wstrdup(SCR *, const CHAR_T *, size_t);
*/
CHAR_T *
v_wstrdup(SCR *sp, const CHAR_T *str, size_t len)
{
CHAR_T *copy;
MALLOC(sp, copy, (len + 1) * sizeof(CHAR_T));
if (copy == NULL)
return (NULL);
MEMCPY(copy, str, len);
copy[len] = '\0';
return (copy);
}
/*
* nget_uslong --
* Get an unsigned long, checking for overflow.
*
* PUBLIC: enum nresult nget_uslong(u_long *, const CHAR_T *, CHAR_T **, int);
*/
enum nresult
nget_uslong(u_long *valp, const CHAR_T *p, CHAR_T **endp, int base)
{
errno = 0;
*valp = STRTOUL(p, endp, base);
if (errno == 0)
return (NUM_OK);
if (errno == ERANGE && *valp == ULONG_MAX)
return (NUM_OVER);
return (NUM_ERR);
}
/*
* nget_slong --
* Convert a signed long, checking for overflow and underflow.
*
* PUBLIC: enum nresult nget_slong(long *, const CHAR_T *, CHAR_T **, int);
*/
enum nresult
nget_slong(long *valp, const CHAR_T *p, CHAR_T **endp, int base)
{
errno = 0;
*valp = STRTOL(p, endp, base);
if (errno == 0)
return (NUM_OK);
if (errno == ERANGE) {
if (*valp == LONG_MAX)
return (NUM_OVER);
if (*valp == LONG_MIN)
return (NUM_UNDER);
}
return (NUM_ERR);
}
/*
* timepoint_steady --
* Get a timestamp from a monotonic clock.
*
* PUBLIC: void timepoint_steady(struct timespec *);
*/
void
timepoint_steady(struct timespec *ts)
{
#ifdef __APPLE__
static mach_timebase_info_data_t base = { 0 };
uint64_t val;
uint64_t ns;
if (base.denom == 0)
(void)mach_timebase_info(&base);
val = mach_absolute_time();
ns = val * base.numer / base.denom;
ts->tv_sec = ns / 1000000000;
ts->tv_nsec = ns % 1000000000;
#else
#ifdef CLOCK_MONOTONIC_FAST
(void)clock_gettime(CLOCK_MONOTONIC_FAST, ts);
#else
(void)clock_gettime(CLOCK_MONOTONIC, ts);
#endif
#endif
}
/*
* timepoint_system --
* Get the current calendar time.
*
* PUBLIC: void timepoint_system(struct timespec *);
*/
void
timepoint_system(struct timespec *ts)
{
#ifdef __APPLE__
clock_serv_t clk;
mach_timespec_t mts;
kern_return_t kr;
kr = host_get_clock_service(mach_host_self(), CALENDAR_CLOCK, &clk);
if (kr != KERN_SUCCESS)
return;
(void)clock_get_time(clk, &mts);
(void)mach_port_deallocate(mach_task_self(), clk);
ts->tv_sec = mts.tv_sec;
ts->tv_nsec = mts.tv_nsec;
#else
#ifdef CLOCK_REALTIME_FAST
(void)clock_gettime(CLOCK_REALTIME_FAST, ts);
#else
(void)clock_gettime(CLOCK_REALTIME, ts);
#endif
#endif
}
#ifdef DEBUG
#include <stdarg.h>
/*
* TRACE --
* debugging trace routine.
*
* PUBLIC: void TRACE(SCR *, const char *, ...);
*/
void
TRACE(SCR *sp, const char *fmt, ...)
{
FILE *tfp;
va_list ap;
if ((tfp = sp->gp->tracefp) == NULL)
return;
va_start(ap, fmt);
(void)vfprintf(tfp, fmt, ap);
va_end(ap);
(void)fflush(tfp);
}
#endif