#include "cvs.h"
#include "canonicalize.h"
#include "canon-host.h"
#include "getline.h"
#include "vasprintf.h"
#include "vasnprintf.h"
#ifdef HAVE_WINT_T
# include <wchar.h>
#endif
extern char *getlogin (void);
void
expand_string (char **strptr, size_t *n, size_t newsize)
{
while (*n < newsize)
*strptr = x2realloc (*strptr, n);
}
char *
Xreadlink (const char *link, size_t size)
{
char *file = xreadlink (link, size);
if (file == NULL)
error (1, errno, "cannot readlink %s", link);
return file;
}
void
xrealloc_and_strcat (char **str, size_t *lenp, const char *src)
{
bool newstr = !*lenp;
expand_string (str, lenp, (newstr ? 0 : strlen (*str)) + strlen (src) + 1);
if (newstr)
strcpy (*str, src);
else
strcat (*str, src);
}
int
strip_trailing_newlines (char *str)
{
size_t index, origlen;
index = origlen = strlen (str);
while (index > 0 && str[index-1] == '\n')
str[--index] = '\0';
return index != origlen;
}
int
pathname_levels (const char *p)
{
int level;
int max_level;
if (p == NULL) return 0;
max_level = 0;
level = 0;
do
{
if (p[0] == '.' && p[1] == '.' && (p[2] == '\0' || ISSLASH (p[2])))
{
--level;
if (-level > max_level)
max_level = -level;
}
else if (p[0] == '\0' || ISSLASH (p[0]) ||
(p[0] == '.' && (p[1] == '\0' || ISSLASH (p[1]))))
;
else
++level;
while (*p != '\0' && !ISSLASH (*p)) p++;
if (*p != '\0') p++;
} while (*p != '\0');
return max_level;
}
void
free_names (int *pargc, char **argv)
{
register int i;
for (i = 0; i < *pargc; i++)
{
free (argv[i]);
}
free (argv);
*pargc = 0;
}
void
line2argv (int *pargc, char ***argv, char *line, char *sepchars)
{
char *cp;
int argv_allocated;
argv_allocated = 1;
*argv = xnmalloc (argv_allocated, sizeof (**argv));
*pargc = 0;
for (cp = strtok (line, sepchars); cp; cp = strtok (NULL, sepchars))
{
if (*pargc == argv_allocated)
{
argv_allocated *= 2;
*argv = xnrealloc (*argv, argv_allocated, sizeof (**argv));
}
(*argv)[*pargc] = xstrdup (cp);
(*pargc)++;
}
}
int
numdots (const char *s)
{
int dots = 0;
for (; *s; s++)
{
if (*s == '.')
dots++;
}
return (dots);
}
int
compare_revnums (const char *rev1, const char *rev2)
{
const char *sp, *tp;
char *snext, *tnext;
int result = 0;
sp = rev1;
tp = rev2;
while (result == 0)
{
result = strtoul (sp, &snext, 10) - strtoul (tp, &tnext, 10);
if (*snext == '\0' || *tnext == '\0')
break;
sp = snext + 1;
tp = tnext + 1;
}
return result;
}
char *
increment_revnum (const char *rev)
{
char *newrev, *p;
size_t len = strlen (rev);
newrev = xmalloc (len + 2);
memcpy (newrev, rev, len + 1);
for (p = newrev + len; p != newrev; )
{
--p;
if (!isdigit(*p))
{
++p;
break;
}
if (*p != '9')
{
++*p;
return newrev;
}
*p = '0';
}
*p = '1';
p = newrev + len;
*p++ = '0';
*p = '\0';
return newrev;
}
char *
getcaller (void)
{
#ifndef SYSTEM_GETCALLER
static char *cache;
struct passwd *pw;
uid_t uid;
#endif
#ifdef AUTH_SERVER_SUPPORT
if (CVS_Username != NULL)
return CVS_Username;
#endif
#ifdef SYSTEM_GETCALLER
return SYSTEM_GETCALLER ();
#else
if (cache != NULL)
return cache;
uid = getuid ();
if (uid == (uid_t) 0)
{
char *name;
if (((name = getlogin ()) || (name = getenv("LOGNAME")) ||
(name = getenv("USER"))) && *name)
{
cache = xstrdup (name);
return cache;
}
}
if ((pw = (struct passwd *) getpwuid (uid)) == NULL)
{
cache = Xasprintf ("uid%lu", (unsigned long) uid);
return cache;
}
cache = xstrdup (pw->pw_name);
return cache;
#endif
}
#ifdef lint
# ifndef __GNUC__
bool
get_date (struct timespec *result, char const *p, struct timespec const *now)
{
result->tv_sec = 0;
result->tv_nsec = 0;
return false;
}
# endif
#endif
char *
previous_rev (RCSNode *rcs, const char *rev)
{
char *p;
char *tmp = xstrdup (rev);
long r1;
char *retval;
retval = xmalloc (strlen (rev) + 1);
p = strrchr (tmp, '.');
*p = '\0';
r1 = strtol (p+1, NULL, 10);
do {
if (--r1 == 0)
{
p = strrchr (tmp, '.');
if (p == NULL)
retval = NULL;
else
{
*p = '\0';
sprintf (retval, "%s", tmp);
}
break;
}
sprintf (retval, "%s.%ld", tmp, r1);
} while (!RCS_exist_rev (rcs, retval));
free (tmp);
return retval;
}
char *
gca (const char *rev1, const char *rev2)
{
int dots;
char *gca, *g;
const char *p1, *p2;
int r1, r2;
char *retval;
if (rev1 == NULL || rev2 == NULL)
{
error (0, 0, "sanity failure in gca");
abort();
}
g = gca = xmalloc (strlen (rev1) + strlen (rev2) + 100);
p1 = rev1;
p2 = rev2;
do
{
r1 = strtol (p1, (char **) &p1, 10);
r2 = strtol (p2, (char **) &p2, 10);
(void) sprintf (g, "%d.", r1 < r2 ? r1 : r2);
g += strlen (g);
if (*p1 == '.') ++p1;
else break;
if (*p2 == '.') ++p2;
else break;
} while (r1 == r2);
*--g = '\0';
dots = numdots (gca);
if (dots == 0)
{
if (r2 < r1) p1 = p2;
if (*p1 == '\0')
{
error (0, 0, "bad revisions %s or %s", rev1, rev2);
abort();
}
else
{
*g++ = '.';
while (*p1 != '.' && *p1 != '\0')
*g++ = *p1++;
*g = '\0';
}
}
else if ((dots & 1) == 0)
{
g = strrchr (gca, '.');
*g = '\0';
}
retval = xstrdup (gca);
free (gca);
return retval;
}
void
check_numeric (const char *rev, int argc, char **argv)
{
if (rev == NULL || !isdigit ((unsigned char) *rev))
return;
if (argc != 1
|| (!wrap_name_has (argv[0], WRAP_TOCVS) && isdir (argv[0])))
{
error (0, 0, "while processing more than one file:");
error (1, 0, "attempt to specify a numeric revision");
}
}
char *
make_message_rcsvalid (const char *message)
{
char *dst, *dp;
const char *mp;
if (message == NULL) message = "";
dp = dst = (char *) xmalloc (strlen (message) + 1);
for (mp = message; *mp != '\0'; ++mp)
{
if (*mp == '\n')
{
while (dp > dst && (dp[-1] == ' ' || dp[-1] == '\t'))
--dp;
}
*dp++ = *mp;
}
while (dp > dst && isspace ((unsigned char) dp[-1]))
--dp;
*dp = '\0';
if (*dst == '\0')
{
free (dst);
dst = xstrdup ("*** empty log message ***");
}
return dst;
}
int
file_has_markers (const struct file_info *finfo)
{
FILE *fp;
char *line = NULL;
size_t line_allocated = 0;
int result;
result = 0;
fp = CVS_FOPEN (finfo->file, "r");
if (fp == NULL)
error (1, errno, "cannot open %s", finfo->fullname);
while (getline (&line, &line_allocated, fp) > 0)
{
if (strncmp (line, RCS_MERGE_PAT_1, sizeof RCS_MERGE_PAT_1 - 1) == 0 ||
strncmp (line, RCS_MERGE_PAT_2, sizeof RCS_MERGE_PAT_2 - 1) == 0 ||
strncmp (line, RCS_MERGE_PAT_3, sizeof RCS_MERGE_PAT_3 - 1) == 0)
{
result = 1;
goto out;
}
}
if (ferror (fp))
error (0, errno, "cannot read %s", finfo->fullname);
out:
if (fclose (fp) < 0)
error (0, errno, "cannot close %s", finfo->fullname);
if (line != NULL)
free (line);
return result;
}
void
get_file (const char *name, const char *fullname, const char *mode, char **buf,
size_t *bufsize, size_t *len)
{
struct stat s;
size_t nread;
char *tobuf;
FILE *e;
size_t filesize;
if (name == NULL)
{
e = stdin;
filesize = 100;
}
else
{
if (stat (name, &s) < 0)
error (1, errno, "can't stat %s", fullname);
filesize = s.st_size;
e = xfopen (name, mode);
}
if (*buf == NULL || *bufsize <= filesize)
{
*bufsize = filesize + 1;
*buf = xrealloc (*buf, *bufsize);
}
tobuf = *buf;
nread = 0;
while (1)
{
size_t got;
got = fread (tobuf, 1, *bufsize - (tobuf - *buf), e);
if (ferror (e))
error (1, errno, "can't read %s", fullname);
nread += got;
tobuf += got;
if (feof (e))
break;
if (tobuf == *buf + *bufsize)
{
int c;
long off;
c = getc (e);
if (c == EOF)
break;
off = tobuf - *buf;
expand_string (buf, bufsize, *bufsize + 100);
tobuf = *buf + off;
*tobuf++ = c;
++nread;
}
}
if (e != stdin && fclose (e) < 0)
error (0, errno, "cannot close %s", fullname);
*len = nread;
if (nread == *bufsize)
expand_string (buf, bufsize, *bufsize + 1);
(*buf)[nread] = '\0';
}
void
resolve_symlink (char **filename)
{
ssize_t rsize;
if (filename == NULL || *filename == NULL)
return;
while ((rsize = islink (*filename)) > 0)
{
#ifdef HAVE_READLINK
char *newname = Xreadlink (*filename, rsize);
if (ISABSOLUTE (newname))
{
free (*filename);
*filename = newname;
}
else
{
const char *oldname = last_component (*filename);
int dirlen = oldname - *filename;
char *fullnewname = xmalloc (dirlen + strlen (newname) + 1);
strncpy (fullnewname, *filename, dirlen);
strcpy (fullnewname + dirlen, newname);
free (newname);
free (*filename);
*filename = fullnewname;
}
#else
error (1, 0, "internal error: islink doesn't like readlink");
#endif
}
}
char *
backup_file (const char *filename, const char *suffix)
{
char *backup_name = Xasprintf ("%s%s%s%s", BAKPREFIX, filename,
suffix ? "." : "", suffix ? suffix : "");
if (isfile (filename))
copy_file (filename, backup_name);
return backup_name;
}
char *
shell_escape(char *buf, const char *str)
{
static const char meta[] = "$`\\\"";
const char *p;
for (;;)
{
p = strpbrk(str, meta);
if (!p) p = str + strlen(str);
if (p > str)
{
memcpy(buf, str, p - str);
buf += p - str;
}
if (!*p) break;
*buf++ = '\\';
*buf++ = *p++;
str = p;
}
*buf = '\0';
return buf;
}
void
sleep_past (time_t desttime)
{
time_t t;
long s;
long us;
while (time (&t) <= desttime)
{
#ifdef HAVE_GETTIMEOFDAY
struct timeval tv;
gettimeofday (&tv, NULL);
if (tv.tv_sec > desttime)
break;
s = desttime - tv.tv_sec;
if (tv.tv_usec > 0)
us = 1000000 - tv.tv_usec;
else
{
s++;
us = 0;
}
#else
s = desttime - t;
us = 20000;
#endif
{
struct timespec ts;
ts.tv_sec = s;
ts.tv_nsec = us * 1000;
(void)nanosleep (&ts, NULL);
}
}
}
typedef int (*CONVPROC_t) (Node *, void *);
struct cmdline_bindings
{
char conversion;
void *data;
CONVPROC_t convproc;
void *closure;
};
static void
cmdline_bindings_hash_node_delete (Node *p)
{
struct cmdline_bindings *b = p->data;
if (b->conversion != ',')
{
free (b->data);
}
free (b);
}
char *
cmdlinequote (char quotes, char *s)
{
char *quoted = cmdlineescape (quotes, s);
char *buf = Xasprintf ("%c%s%c", quotes, quoted, quotes);
free (quoted);
return buf;
}
char *
cmdlineescape (char quotes, char *s)
{
char *buf = NULL;
size_t length = 0;
char *d = NULL;
size_t doff;
char *lastspace;
lastspace = s - 1;
do
{
if ( isspace( *s ) ) lastspace = s;
if( quotes
? ( *s == quotes
|| ( quotes == '"'
&& ( *s == '$' || *s == '`' || *s == '\\' ) ) )
: ( strchr( "\\$`'\"*?", *s )
|| isspace( *s )
|| ( lastspace == ( s - 1 )
&& *s == '~' ) ) )
{
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
*d++ = '\\';
}
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
} while ((*d++ = *s++) != '\0');
return (buf);
}
char *
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
format_cmdline (bool oldway, const char *srepos, const char *format, ...)
#else
format_cmdline (const char *format, ...)
#endif
{
va_list args;
char *buf;
size_t length;
char *pfmt;
char *fmt;
size_t flen;
char *d, *q, *r;
const char *s;
size_t doff, qoff;
char inquotes;
List *pflist = getlist();
Node *p;
struct cmdline_bindings *b;
static int warned_of_deprecation = 0;
char key[] = "?";
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
int onearg;
int subbedsomething;
#endif
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (oldway && !warned_of_deprecation)
{
warned_of_deprecation = 1;
error (0, 0,
"warning: Set to use deprecated info format strings. Establish\n"
"compatibility with the new info file format strings (add a temporary '1' in\n"
"all info files after each '%%' which doesn't represent a literal percent)\n"
"and set UseNewInfoFmtStrings=yes in CVSROOT/config. After that, convert\n"
"individual command lines and scripts to handle the new format at your\n"
"leisure.");
}
#endif
va_start (args, format);
while ((pfmt = va_arg (args, char *)) != NULL)
{
char *conversion = va_arg (args, char *);
char conversion_error = 0;
char char_conversion = 0;
char decimal_conversion = 0;
char integer_conversion = 0;
char string_conversion = 0;
b = xmalloc(sizeof(struct cmdline_bindings));
b->convproc = NULL;
b->closure = NULL;
s = conversion;
length = 0;
while (!length && *s)
{
switch (*s)
{
case 'h':
integer_conversion = 1;
if (s[1] == 'h')
{
length = sizeof (char);
s += 2;
}
else
{
char_conversion = 1;
length = sizeof (short);
s++;
}
break;
#ifdef HAVE_INTMAX_T
case 'j':
integer_conversion = 1;
length = sizeof (intmax_t);
s++;
break;
#endif
case 'l':
integer_conversion = 1;
if (s[1] == 'l')
{
#ifdef HAVE_LONG_LONG
length = sizeof (long long);
#endif
s += 2;
}
else
{
char_conversion = 2;
string_conversion = 2;
length = sizeof (long);
s++;
}
break;
case 't':
integer_conversion = 1;
length = sizeof (ptrdiff_t);
s++;
break;
case 'z':
integer_conversion = 1;
length = sizeof (size_t);
s++;
break;
#ifdef HAVE_LONG_DOUBLE
case 'L':
decimal_conversion = 1;
length = sizeof (long double);
s++;
break;
#endif
default:
char_conversion = 1;
decimal_conversion = 1;
integer_conversion = 1;
string_conversion = 1;
length = -1;
break;
}
}
buf = xmalloc (strlen(conversion) + 2);
*buf = '%';
strcpy (buf+1, conversion);
switch (*s)
{
case 'c':
if (!char_conversion)
{
conversion_error = 1;
break;
}
if (char_conversion == 2)
{
#ifdef HAVE_WINT_T
length = sizeof (wint_t);
#else
conversion_error = 1;
break;
#endif
}
else
length = sizeof (char);
case 'd':
case 'i':
case 'o':
case 'u':
case 'x':
case 'X':
if (!integer_conversion)
{
conversion_error = 1;
break;
}
if (length == -1)
{
length = sizeof (int);
}
switch (length)
{
case sizeof(char):
{
char arg_char = (char) va_arg (args, int);
b->data = Xasprintf (buf, arg_char);
break;
}
#ifdef UNIQUE_INT_TYPE_WINT_T
case sizeof(wint_t):
{
wint_t arg_wint_t = va_arg (args, wint_t);
b->data = Xasprintf (buf, arg_wint_t);
break;
}
#endif
#ifdef UNIQUE_INT_TYPE_SHORT
case sizeof(short):
{
short arg_short = (short) va_arg (args, int);
b->data = Xasprintf (buf, arg_short);
break;
}
#endif
#ifdef UNIQUE_INT_TYPE_INT
case sizeof(int):
{
int arg_int = va_arg (args, int);
b->data = Xasprintf(buf, arg_int);
break;
}
#endif
#ifdef UNIQUE_INT_TYPE_LONG
case sizeof(long):
{
long arg_long = va_arg (args, long);
b->data = Xasprintf (buf, arg_long);
break;
}
#endif
#ifdef UNIQUE_INT_TYPE_LONG_LONG
case sizeof(long long):
{
long long arg_long_long = va_arg (args, long long);
b->data = Xasprintf (buf, arg_long_long);
break;
}
#endif
#ifdef UNIQUE_INT_TYPE_INTMAX_T
case sizeof(intmax_t):
{
intmax_t arg_intmax_t = va_arg (args, intmax_t);
b->data = Xasprintf (buf, arg_intmax_t);
break;
}
#endif
#ifdef UNIQUE_INT_TYPE_SIZE_T
case sizeof(size_t):
{
size_t arg_size_t = va_arg (args, size_t);
b->data = Xasprintf (buf, arg_size_t);
break;
}
#endif
#ifdef UNIQUE_INT_TYPE_PTRDIFF_T
case sizeof(ptrdiff_t):
{
ptrdiff_t arg_ptrdiff_t = va_arg (args, ptrdiff_t);
b->data = Xasprintf (buf, arg_ptrdiff_t);
break;
}
#endif
default:
dellist(&pflist);
free(b);
error (1, 0,
"internal error: unknown integer arg size (%d)",
length);
break;
}
break;
case 'a':
case 'A':
case 'e':
case 'E':
case 'f':
case 'F':
case 'g':
case 'G':
if (!decimal_conversion)
{
conversion_error = 1;
break;
}
if (length == -1)
{
length = sizeof (double);
}
switch (length)
{
case sizeof(double):
{
double arg_double = va_arg (args, double);
b->data = Xasprintf (buf, arg_double);
break;
}
#ifdef UNIQUE_FLOAT_TYPE_LONG_DOUBLE
case sizeof(long double):
{
long double arg_long_double = va_arg (args, long double);
b->data = Xasprintf (buf, arg_long_double);
break;
}
#endif
default:
dellist(&pflist);
free(b);
error (1, 0,
"internal error: unknown floating point arg size (%d)",
length);
break;
}
break;
case 's':
switch (string_conversion)
{
case 1:
b->data = xstrdup (va_arg (args, char *));
break;
#ifdef HAVE_WCHAR_T
case 2:
{
wchar_t *arg_wchar_t_string = va_arg (args, wchar_t *);
b->data = Xasprintf (buf, arg_wchar_t_string);
break;
}
#endif
default:
conversion_error = 1;
break;
}
break;
case ',':
if (length != -1)
{
conversion_error = 1;
break;
}
b->data = va_arg (args, List *);
b->convproc = va_arg (args, CONVPROC_t);
b->closure = va_arg (args, void *);
break;
default:
conversion_error = 1;
break;
}
free (buf);
if (conversion_error || s[1])
{
error (1, 0,
"internal error (format_cmdline): '%s' is not a valid conversion!!!",
conversion);
}
b->conversion = *s;
q = pfmt;
while (*q)
{
struct cmdline_bindings *tb;
if (*q == '{')
{
s = q + 1;
while (*++q && *q != '}');
r = q + 1;
}
else
{
s = q++;
r = q;
}
if (*r)
{
tb = xmalloc(sizeof(struct cmdline_bindings));
if (b->conversion == ',')
{
tb->data = b->data;
}
else
{
tb->data = xstrdup(b->data);
}
tb->conversion = b->conversion;
tb->convproc = b->convproc;
tb->closure = b->closure;
}
else
{
tb = b;
}
p = getnode();
p->key = xmalloc((q - s) + 1);
strncpy (p->key, s, q - s);
p->key[q-s] = '\0';
p->data = tb;
p->delproc = cmdline_bindings_hash_node_delete;
addnode(pflist,p);
}
}
va_end(args);
b = xmalloc(sizeof(struct cmdline_bindings));
b->conversion = 's';
b->convproc = NULL;
b->closure = NULL;
b->data = xstrdup( "" );
p = getnode();
p->key = xstrdup( "n" );
p->data = b;
p->delproc = cmdline_bindings_hash_node_delete;
addnode( pflist,p );
fmt = NULL;
flen = 0;
s = format;
d = buf = NULL;
length = 0;
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
inquotes = '\0';
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
subbedsomething = 0;
#endif
while ((*d++ = *s) != '\0')
{
int list = 0;
switch (*s++)
{
case '\\':
if (*s)
{
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
*d++ = *s++;
}
break;
case '\'':
case '"':
if (!inquotes) inquotes = s[-1];
else if (s[-1] == inquotes) inquotes = '\0';
break;
case '%':
if (*s == '%')
{
s++;
break;
}
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (oldway && subbedsomething)
{
break;
}
onearg = oldway ? 1 : 0;
subbedsomething = 1;
#endif
d--;
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (*s == '1')
{
onearg = 1;
s++;
if (!oldway)
{
error (0, 0,
"Using deprecated info format strings. Convert your scripts to use\n"
"the new argument format and remove '1's from your info file format strings.");
}
}
#endif
if (*s == '{')
{
list = 1;
s++;
}
q = fmt;
do
{
qoff = q - fmt;
expand_string (&fmt, &flen, qoff + 1);
q = fmt + qoff;
} while ((*q = *s++) && list && *q++ != '}');
if (!s[-1])
{
error(1, 0,
"unterminated format string encountered in command spec.\n"
"This error is likely to have been caused by an invalid line in a hook script\n"
"spec (see taginfo, loginfo, verifymsginfo, etc. in the Cederqvist). Most\n"
"likely the offending line would end with a '%%' character or contain a string\n"
"beginning \"%%{\" and no closing '}' before the end of the line.");
}
if (list)
{
q[-1] = '\0';
}
else
{
q++;
qoff = q - fmt;
expand_string (&fmt, &flen, qoff + 1);
q = fmt + qoff;
*q = '\0';
}
q = fmt;
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (onearg)
{
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
*d++ = '"';
}
#endif
key[0] = *q;
if ((p = findnode (pflist, key)) != NULL)
{
b = p->data;
if (b->conversion == ',')
{
struct format_cmdline_walklist_closure c;
c.format = q;
c.buf = &buf;
c.length = &length;
c.d = &d;
c.quotes = inquotes;
c.closure = b->closure;
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
c.onearg = onearg;
c.firstpass = 1;
c.srepos = srepos;
#endif
walklist(b->data, b->convproc, &c);
d--;
q += strlen(q);
}
else
{
char *outstr;
if (strlen(q) > 1)
{
error (1, 0,
"Multiple non-list variables are not allowed in a single format string.");
}
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (onearg)
{
outstr = b->data;
}
else
{
#endif
if (!inquotes)
{
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
*d++ = '"';
}
outstr = cmdlineescape (inquotes ? inquotes : '"', b->data);
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
}
#endif
doff = d - buf;
expand_string (&buf, &length, doff + strlen(outstr));
d = buf + doff;
strncpy(d, outstr, strlen(outstr));
d += strlen(outstr);
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (!onearg)
{
free(outstr);
#endif
if (!inquotes)
{
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
*d++ = '"';
}
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
}
#endif
q++;
}
}
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
else if (onearg)
{
q++;
doff = d - buf;
expand_string (&buf, &length, doff + strlen(srepos));
d = buf + doff;
strncpy(d, srepos, strlen(srepos));
d += strlen(srepos);
}
#endif
else
{
error (1, 0,
"Unknown format character in info file ('%s').\n"
"Info files are the hook files, verifymsg, taginfo, commitinfo, etc.",
q);
}
#ifdef SUPPORT_OLD_INFO_FMT_STRINGS
if (onearg)
{
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
*d++ = '"';
}
#endif
break;
}
doff = d - buf;
expand_string (&buf, &length, doff + 1);
d = buf + doff;
}
if (fmt) free (fmt);
if (inquotes)
{
error (1, 0, "unterminated quote in format string: %s", format);
}
dellist (&pflist);
return buf;
}
char *
Xstrdup (const char *string)
{
if (string == NULL) return NULL;
return xmemdup (string, strlen (string) + 1);
}
char *
Xasprintf (const char *format, ...)
{
va_list args;
char *result;
va_start (args, format);
if (vasprintf (&result, format, args) < 0)
error (1, errno, "Failed to write to string.");
va_end (args);
return result;
}
char *
Xasnprintf (char *resultbuf, size_t *lengthp, const char *format, ...)
{
va_list args;
char *result;
va_start (args, format);
result = vasnprintf (resultbuf, lengthp, format, args);
if (result == NULL)
error (1, errno, "Failed to write to string.");
va_end (args);
return result;
}
bool
readBool (const char *infopath, const char *option, const char *p, bool *val)
{
TRACE (TRACE_FLOW, "readBool (%s, %s, %s)", infopath, option, p);
if (!strcasecmp (p, "no") || !strcasecmp (p, "false")
|| !strcasecmp (p, "off") || !strcmp (p, "0"))
{
TRACE (TRACE_DATA, "Read %d for %s", *val, option);
*val = false;
return true;
}
else if (!strcasecmp (p, "yes") || !strcasecmp (p, "true")
|| !strcasecmp (p, "on") || !strcmp (p, "1"))
{
TRACE (TRACE_DATA, "Read %d for %s", *val, option);
*val = true;
return true;
}
error (0, 0, "%s: unrecognized value `%s' for `%s'",
infopath, p, option);
return false;
}
FILE *
xfopen (const char *name, const char *mode)
{
FILE *fp;
if (!(fp = fopen (name, mode)))
error (1, errno, "cannot open %s", name);
return fp;
}
char *
xcanonicalize_file_name (const char *path)
{
char *hardpath = canonicalize_file_name (path);
if (!hardpath)
error (1, errno, "Failed to resolve path: `%s'", path);
return hardpath;
}
extern char *server_hostname;
bool
isThisHost (const char *otherhost)
{
char *fqdno;
char *fqdns;
bool retval;
if (!strcasecmp (server_hostname, otherhost))
return true;
fqdno = canon_host (otherhost);
if (!fqdno)
error (1, 0, "Name lookup failed for `%s': %s",
otherhost, ch_strerror ());
fqdns = canon_host (server_hostname);
if (!fqdns)
error (1, 0, "Name lookup failed for `%s': %s",
server_hostname, ch_strerror ());
retval = !strcasecmp (fqdns, fqdno);
free (fqdno);
free (fqdns);
return retval;
}
bool
isSamePath (const char *path1_in, const char *path2_in)
{
char *p1, *p2;
bool same;
if (!strcmp (path1_in, path2_in))
return true;
if (!isdir (path1_in) || !isdir (path2_in))
return false;
p1 = xcanonicalize_file_name (path1_in);
p2 = xcanonicalize_file_name (path2_in);
if (strcmp (p1, p2))
same = false;
else
same = true;
free (p1);
free (p2);
return same;
}