sfeed/util.c

238 lines
5.2 KiB
C
Raw Normal View History

#include <sys/types.h>
#include <ctype.h>
#include <err.h>
2015-01-03 11:44:32 +00:00
#include <errno.h>
2015-06-20 22:19:22 +00:00
#include <libgen.h>
2015-01-03 11:44:32 +00:00
#include <limits.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include "util.h"
static void
encodehex(unsigned char c, char *s)
2015-06-20 22:20:12 +00:00
{
static const char *table = "0123456789ABCDEF";
s[0] = table[((c - (c % 16)) / 16) % 16];
s[1] = table[c % 16];
}
int
parseuri(const char *s, struct uri *u, int rel)
{
const char *p = s;
2015-06-20 22:20:12 +00:00
size_t i;
memset(u, 0, sizeof(struct uri));
if (!*s)
return 0;
/* prefix is "//", don't read protocol, skip to domain parsing */
if (!strncmp(p, "//", 2)) {
p += 2; /* skip "//" */
} else {
/* protocol part */
for (p = s; *p && (isalpha((int)*p) || isdigit((int)*p) ||
*p == '+' || *p == '-' || *p == '.'); p++)
;
if (!strncmp(p, "://", 3)) {
if (p - s + 1 >= (ssize_t)sizeof(u->proto))
return -1; /* protocol too long */
memcpy(u->proto, s, p - s);
p += 3; /* skip "://" */
} else {
p = s; /* no protocol format, set to start */
/* relative url: read rest as path, else as domain */
if (rel)
goto readpath;
}
2015-06-20 22:20:12 +00:00
}
/* domain / host part, skip until "/" or end. */
i = strcspn(p, "/");
if (i + 1 >= sizeof(u->host))
return -1; /* host too long */
memcpy(u->host, p, i);
p = &p[i];
readpath:
if (u->host[0]) {
p = &p[strspn(p, "/")];
strlcpy(u->path, "/", sizeof(u->path));
} else {
/* having no host is an error in this case */
if (!rel)
return -1;
}
/* treat truncation as an error */
return strlcat(u->path, p, sizeof(u->path)) >= sizeof(u->path) ? -1 : 0;
2015-06-20 22:20:12 +00:00
}
2015-07-31 20:29:34 +00:00
/* Get absolute uri; if `link` is relative use `base` to make it absolute.
* the returned string in `buf` is uri encoded, see: encodeuri(). */
int
absuri(const char *link, const char *base, char *buf, size_t bufsiz)
{
struct uri ulink, ubase;
char tmp[4096] = "", *p;
int r = -1, c;
size_t i;
buf[0] = '\0';
if (parseuri(base, &ubase, 0) == -1 ||
parseuri(link, &ulink, 1) == -1)
return -1;
if (!ulink.host[0] && !ubase.host[0])
return -1;
r = snprintf(tmp, sizeof(tmp), "%s://%s",
ulink.proto[0] ?
ulink.proto :
(ubase.proto[0] ? ubase.proto : "http"),
!strncmp(link, "//", 2) ?
ulink.host :
(ulink.host[0] ? ulink.host : ubase.host));
if (r == -1 || (size_t)r >= sizeof(tmp))
return -1;
/* relative to root */
if (!ulink.host[0] && ulink.path[0] != '/') {
/* relative to base url path */
if (ulink.path[0]) {
if ((p = strrchr(ubase.path, '/'))) {
/* temporary null-terminate */
c = *(++p);
*p = '\0';
i = strlcat(tmp, ubase.path, sizeof(tmp));
*p = c; /* restore */
if (i >= sizeof(tmp))
return -1;
}
} else {
if (strlcat(tmp, ubase.path, sizeof(tmp)) >= sizeof(tmp))
return -1;
}
}
if (strlcat(tmp, ulink.path, sizeof(tmp)) >= sizeof(tmp))
return -1;
return encodeuri(tmp, buf, bufsiz);
}
int
encodeuri(const char *s, char *buf, size_t bufsiz)
{
size_t i, b;
if (!bufsiz)
return -1;
for (i = 0, b = 0; s[i]; i++) {
if ((int)s[i] == ' ' ||
(unsigned char)s[i] > 127 ||
iscntrl((int)s[i])) {
if (b + 3 >= bufsiz)
return -1;
buf[b++] = '%';
encodehex(s[i], &buf[b]);
b += 2;
} else {
if (b >= bufsiz)
return -1;
buf[b++] = s[i];
}
}
if (b >= bufsiz)
return -1;
buf[b] = '\0';
return 0;
}
2015-07-31 20:29:34 +00:00
/* Read a field-separated line from 'fp',
* separated by a character 'separator',
* 'fields' is a list of pointer with a maximum size of 'maxfields'.
2015-06-20 22:20:54 +00:00
* 'maxfields' must be > 0.
* 'line' buffer is allocated using malloc, 'size' will contain the
* allocated buffer size.
* returns: amount of fields read or -1 on error. */
int
parseline(char **line, size_t *size, char **fields,
unsigned int maxfields, int separator, FILE *fp)
{
char *prev, *s;
unsigned int i;
2015-07-28 19:56:46 +00:00
if (getline(line, size, fp) <= 0)
return -1;
2015-07-28 19:56:46 +00:00
for (prev = *line, i = 0;
2015-06-20 22:20:54 +00:00
(s = strchr(prev, separator)) && i < maxfields - 1;
i++) {
*s = '\0';
fields[i] = prev;
prev = s + 1;
}
fields[i++] = prev;
/* make non-parsed fields empty. */
2015-07-28 19:56:46 +00:00
for (; i < maxfields; i++)
fields[i] = "";
return (int)i;
}
2015-07-31 20:29:34 +00:00
/* Parse time to time_t, assumes time_t is signed. */
2015-01-03 11:44:32 +00:00
int
strtotime(const char *s, time_t *t)
{
long l;
errno = 0;
l = strtol(s, NULL, 10);
2015-07-28 19:56:46 +00:00
if (errno != 0)
2015-01-03 11:44:32 +00:00
return -1;
*t = (time_t)l;
return 0;
}
/* Escape characters below as HTML 2.0 / XML 1.0. */
void
xmlencode(const char *s, FILE *fp)
{
for (; *s; s++) {
switch(*s) {
case '<': fputs("&lt;", fp); break;
case '>': fputs("&gt;", fp); break;
case '\'': fputs("&apos;", fp); break;
case '&': fputs("&amp;", fp); break;
case '"': fputs("&quot;", fp); break;
default: fputc(*s, fp);
}
}
}
2015-06-20 22:19:22 +00:00
/* Some implementations of basename(3) return a pointer to a static
* internal buffer (OpenBSD). Others modify the contents of `path` (POSIX).
* This is a wrapper function that is compatible with both versions.
* The program will error out if basename(3) failed, this can only happen
2015-07-31 20:29:34 +00:00
* with the OpenBSD version. */
2015-06-20 22:19:22 +00:00
char *
xbasename(const char *path)
{
char *p, *b;
2015-07-28 19:56:46 +00:00
if (!(p = strdup(path)))
2015-06-20 22:19:22 +00:00
err(1, "strdup");
2015-07-28 19:56:46 +00:00
if (!(b = basename(p)))
2015-06-20 22:19:22 +00:00
err(1, "basename");
2015-07-28 19:56:46 +00:00
if (!(b = strdup(b)))
2015-06-20 22:19:22 +00:00
err(1, "strdup");
free(p);
return b;
}