forked from mirrors/kore
allow modules to add headers to the response before calling http_response()
by calling http_response_header_add(). fix wrong overflow check in spdy_stream_get_header(). html_inject now exports last modified timestamp for the files that are compiled into the module in the format static_mtime_<type>_<name>. modules can now look into the request headers using http_request_header_get().
This commit is contained in:
parent
a228cdba0e
commit
a9ebf37cae
|
@ -47,9 +47,27 @@ int
|
||||||
betrippin_serve_style_css(struct http_request *req)
|
betrippin_serve_style_css(struct http_request *req)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
char *date;
|
||||||
|
time_t tstamp;
|
||||||
|
|
||||||
ret = http_response(req, 200, static_css_style,
|
if (http_request_header_get(req, "if-modified-since", &date)) {
|
||||||
static_len_css_style, "text/css");
|
tstamp = kore_date_to_time(date);
|
||||||
|
free(date);
|
||||||
|
|
||||||
|
kore_log("header was present with %ld", tstamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tstamp != 0 && tstamp <= static_mtime_css_style) {
|
||||||
|
ret = http_response(req, 304, NULL, 0);
|
||||||
|
} else {
|
||||||
|
date = kore_time_to_date(static_mtime_css_style);
|
||||||
|
if (date != NULL)
|
||||||
|
http_response_header_add(req, "last-modified", date);
|
||||||
|
|
||||||
|
http_response_header_add(req, "content-type", "text/css");
|
||||||
|
ret = http_response(req, 200, static_css_style,
|
||||||
|
static_len_css_style);
|
||||||
|
}
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
@ -59,8 +77,9 @@ betrippin_serve_index(struct http_request *req)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
http_response_header_add(req, "content-type", "text/html");
|
||||||
ret = http_response(req, 200, static_html_index,
|
ret = http_response(req, 200, static_html_index,
|
||||||
static_len_html_index, "text/html");
|
static_len_html_index);
|
||||||
|
|
||||||
return (ret);
|
return (ret);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <sys/param.h>
|
#include <sys/param.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
int
|
int
|
||||||
main(int argc, char *argv[])
|
main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
|
struct stat st;
|
||||||
size_t len;
|
size_t len;
|
||||||
FILE *fp, *hdr;
|
FILE *fp, *hdr;
|
||||||
char *ext, *p, *c, buf[1024];
|
char *ext, *p, *c, buf[1024];
|
||||||
|
@ -40,6 +42,11 @@ main(int argc, char *argv[])
|
||||||
if ((ext = strchr(argv[2], '.')) != NULL)
|
if ((ext = strchr(argv[2], '.')) != NULL)
|
||||||
*(ext)++ = '\0';
|
*(ext)++ = '\0';
|
||||||
|
|
||||||
|
if (stat(argv[1], &st) == -1) {
|
||||||
|
printf("stat(%s) failed: %d\n", argv[1], errno);
|
||||||
|
exit(99);
|
||||||
|
}
|
||||||
|
|
||||||
printf("/**** AUTO GENERATED BY MAKEFILE - DO NOT TOUCH ****/\n");
|
printf("/**** AUTO GENERATED BY MAKEFILE - DO NOT TOUCH ****/\n");
|
||||||
printf("#include <sys/param.h>\n\n");
|
printf("#include <sys/param.h>\n\n");
|
||||||
printf("u_int8_t *static_%s_%s = (u_int8_t *)", ext, argv[2]);
|
printf("u_int8_t *static_%s_%s = (u_int8_t *)", ext, argv[2]);
|
||||||
|
@ -64,10 +71,12 @@ main(int argc, char *argv[])
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
|
|
||||||
printf(";\n\n");
|
printf(";\n\n");
|
||||||
printf("u_int32_t static_len_%s_%s = %ld;", ext, argv[2], len);
|
printf("u_int32_t static_len_%s_%s = %ld;\n", ext, argv[2], len);
|
||||||
|
printf("time_t static_mtime_%s_%s = %ld;\n", ext, argv[2], st.st_mtime);
|
||||||
|
|
||||||
fprintf(hdr, "extern u_int8_t *static_%s_%s;\n", ext, argv[2]);
|
fprintf(hdr, "extern u_int8_t *static_%s_%s;\n", ext, argv[2]);
|
||||||
fprintf(hdr, "extern u_int32_t static_len_%s_%s;\n", ext, argv[2]);
|
fprintf(hdr, "extern u_int32_t static_len_%s_%s;\n", ext, argv[2]);
|
||||||
|
fprintf(hdr, "extern u_int32_t static_mtime_%s_%s;\n", ext, argv[2]);
|
||||||
fclose(hdr);
|
fclose(hdr);
|
||||||
|
|
||||||
return (0);
|
return (0);
|
||||||
|
|
|
@ -17,6 +17,13 @@
|
||||||
#ifndef __H_HTTP_H
|
#ifndef __H_HTTP_H
|
||||||
#define __H_HTTP_H
|
#define __H_HTTP_H
|
||||||
|
|
||||||
|
struct http_header {
|
||||||
|
char *header;
|
||||||
|
char *value;
|
||||||
|
|
||||||
|
TAILQ_ENTRY(http_header) list;
|
||||||
|
};
|
||||||
|
|
||||||
struct http_request {
|
struct http_request {
|
||||||
char *host;
|
char *host;
|
||||||
char *method;
|
char *method;
|
||||||
|
@ -25,6 +32,7 @@ struct http_request {
|
||||||
struct connection *owner;
|
struct connection *owner;
|
||||||
struct spdy_stream *stream;
|
struct spdy_stream *stream;
|
||||||
|
|
||||||
|
TAILQ_HEAD(, http_header) headers;
|
||||||
TAILQ_ENTRY(http_request) list;
|
TAILQ_ENTRY(http_request) list;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -33,8 +41,10 @@ void http_process(void);
|
||||||
time_t http_date_to_time(char *);
|
time_t http_date_to_time(char *);
|
||||||
void http_request_free(struct http_request *);
|
void http_request_free(struct http_request *);
|
||||||
int http_response(struct http_request *, int,
|
int http_response(struct http_request *, int,
|
||||||
u_int8_t *, u_int32_t, char *);
|
u_int8_t *, u_int32_t);
|
||||||
int http_new_request(struct connection *, struct spdy_stream *,
|
int http_request_header_get(struct http_request *, char *, char **);
|
||||||
|
void http_response_header_add(struct http_request *, char *, char *);
|
||||||
|
int http_request_new(struct connection *, struct spdy_stream *,
|
||||||
char *, char *, char *);
|
char *, char *, char *);
|
||||||
|
|
||||||
#endif /* !__H_HTTP_H */
|
#endif /* !__H_HTTP_H */
|
||||||
|
|
|
@ -96,6 +96,7 @@ void *kore_malloc(size_t);
|
||||||
void *kore_calloc(size_t, size_t);
|
void *kore_calloc(size_t, size_t);
|
||||||
void *kore_realloc(void *, size_t);
|
void *kore_realloc(void *, size_t);
|
||||||
time_t kore_date_to_time(char *);
|
time_t kore_date_to_time(char *);
|
||||||
|
char *kore_time_to_date(time_t);
|
||||||
char *kore_strdup(const char *);
|
char *kore_strdup(const char *);
|
||||||
void kore_parse_config(const char *);
|
void kore_parse_config(const char *);
|
||||||
void kore_strlcpy(char *, const char *, size_t);
|
void kore_strlcpy(char *, const char *, size_t);
|
||||||
|
@ -126,8 +127,10 @@ int net_recv_expand(struct connection *c, struct netbuf *, size_t,
|
||||||
int net_send_queue(struct connection *, u_int8_t *, size_t,
|
int net_send_queue(struct connection *, u_int8_t *, size_t,
|
||||||
struct netbuf **, int (*cb)(struct netbuf *));
|
struct netbuf **, int (*cb)(struct netbuf *));
|
||||||
|
|
||||||
struct spdy_stream *spdy_stream_lookup(struct connection *, u_int32_t);
|
|
||||||
struct spdy_header_block *spdy_header_block_create(int);
|
struct spdy_header_block *spdy_header_block_create(int);
|
||||||
|
struct spdy_stream *spdy_stream_lookup(struct connection *, u_int32_t);
|
||||||
|
int spdy_stream_get_header(struct spdy_header_block *,
|
||||||
|
char *, char **);
|
||||||
|
|
||||||
int spdy_frame_recv(struct netbuf *);
|
int spdy_frame_recv(struct netbuf *);
|
||||||
int spdy_frame_send(struct connection *, u_int16_t,
|
int spdy_frame_send(struct connection *, u_int16_t,
|
||||||
|
|
60
src/http.c
60
src/http.c
|
@ -49,12 +49,12 @@ http_init(void)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
http_new_request(struct connection *c, struct spdy_stream *s, char *host,
|
http_request_new(struct connection *c, struct spdy_stream *s, char *host,
|
||||||
char *method, char *path)
|
char *method, char *path)
|
||||||
{
|
{
|
||||||
struct http_request *req;
|
struct http_request *req;
|
||||||
|
|
||||||
kore_log("http_new_request(%p, %p, %s, %s, %s)", c, s,
|
kore_log("http_request_new(%p, %p, %s, %s, %s)", c, s,
|
||||||
host, method, path);
|
host, method, path);
|
||||||
|
|
||||||
req = (struct http_request *)kore_malloc(sizeof(*req));
|
req = (struct http_request *)kore_malloc(sizeof(*req));
|
||||||
|
@ -63,14 +63,39 @@ http_new_request(struct connection *c, struct spdy_stream *s, char *host,
|
||||||
req->host = kore_strdup(host);
|
req->host = kore_strdup(host);
|
||||||
req->path = kore_strdup(path);
|
req->path = kore_strdup(path);
|
||||||
req->method = kore_strdup(method);
|
req->method = kore_strdup(method);
|
||||||
|
TAILQ_INIT(&(req->headers));
|
||||||
TAILQ_INSERT_TAIL(&http_requests, req, list);
|
TAILQ_INSERT_TAIL(&http_requests, req, list);
|
||||||
|
|
||||||
return (KORE_RESULT_OK);
|
return (KORE_RESULT_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
http_response_header_add(struct http_request *req, char *header, char *value)
|
||||||
|
{
|
||||||
|
struct http_header *hdr;
|
||||||
|
|
||||||
|
kore_log("http_response_header_add(%p, %s, %s)", req, header, value);
|
||||||
|
|
||||||
|
hdr = (struct http_header *)kore_malloc(sizeof(*hdr));
|
||||||
|
hdr->header = kore_strdup(header);
|
||||||
|
hdr->value = kore_strdup(value);
|
||||||
|
TAILQ_INSERT_TAIL(&(req->headers), hdr, list);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
http_request_free(struct http_request *req)
|
http_request_free(struct http_request *req)
|
||||||
{
|
{
|
||||||
|
struct http_header *hdr, *next;
|
||||||
|
|
||||||
|
for (hdr = TAILQ_FIRST(&(req->headers)); hdr != NULL; hdr = next) {
|
||||||
|
next = TAILQ_NEXT(hdr, list);
|
||||||
|
|
||||||
|
TAILQ_REMOVE(&(req->headers), hdr, list);
|
||||||
|
free(hdr->header);
|
||||||
|
free(hdr->value);
|
||||||
|
free(hdr);
|
||||||
|
}
|
||||||
|
|
||||||
free(req->method);
|
free(req->method);
|
||||||
free(req->path);
|
free(req->path);
|
||||||
free(req->host);
|
free(req->host);
|
||||||
|
@ -78,25 +103,25 @@ http_request_free(struct http_request *req)
|
||||||
}
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
http_response(struct http_request *req, int status, u_int8_t *d,
|
http_response(struct http_request *req, int status, u_int8_t *d, u_int32_t len)
|
||||||
u_int32_t len, char *content_type)
|
|
||||||
{
|
{
|
||||||
u_int32_t hlen;
|
u_int32_t hlen;
|
||||||
|
struct http_header *hdr;
|
||||||
u_int8_t *htext;
|
u_int8_t *htext;
|
||||||
char sbuf[4];
|
|
||||||
struct spdy_header_block *hblock;
|
struct spdy_header_block *hblock;
|
||||||
|
char sbuf[4];
|
||||||
|
|
||||||
kore_log("http_response(%p, %d, %p, %d)", req, status, d, len);
|
kore_log("http_response(%p, %d, %p, %d)", req, status, d, len);
|
||||||
|
|
||||||
if (req->owner->proto == CONN_PROTO_SPDY) {
|
if (req->owner->proto == CONN_PROTO_SPDY) {
|
||||||
snprintf(sbuf, sizeof(sbuf), "%d", status);
|
snprintf(sbuf, sizeof(sbuf), "%d", status);
|
||||||
|
|
||||||
hblock = spdy_header_block_create(SPDY_HBLOCK_NORMAL);
|
hblock = spdy_header_block_create(SPDY_HBLOCK_NORMAL);
|
||||||
spdy_header_block_add(hblock, ":status", sbuf);
|
spdy_header_block_add(hblock, ":status", sbuf);
|
||||||
spdy_header_block_add(hblock, ":version", "HTTP/1.1");
|
spdy_header_block_add(hblock, ":version", "HTTP/1.1");
|
||||||
if (content_type != NULL) {
|
TAILQ_FOREACH(hdr, &(req->headers), list)
|
||||||
spdy_header_block_add(hblock,
|
spdy_header_block_add(hblock, hdr->header, hdr->value);
|
||||||
"content-type", content_type);
|
|
||||||
}
|
|
||||||
htext = spdy_header_block_release(req->owner, hblock, &hlen);
|
htext = spdy_header_block_release(req->owner, hblock, &hlen);
|
||||||
if (htext == NULL)
|
if (htext == NULL)
|
||||||
return (KORE_RESULT_ERROR);
|
return (KORE_RESULT_ERROR);
|
||||||
|
@ -126,6 +151,21 @@ http_response(struct http_request *req, int status, u_int8_t *d,
|
||||||
return (KORE_RESULT_OK);
|
return (KORE_RESULT_OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
http_request_header_get(struct http_request *req, char *header, char **out)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
if (req->owner->proto == CONN_PROTO_SPDY) {
|
||||||
|
r = spdy_stream_get_header(req->stream->hblock, header, out);
|
||||||
|
} else {
|
||||||
|
kore_log("http not supported yet");
|
||||||
|
r = KORE_RESULT_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (r);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
http_process(void)
|
http_process(void)
|
||||||
{
|
{
|
||||||
|
@ -160,5 +200,5 @@ http_generic_404(struct http_request *req)
|
||||||
kore_log("http_generic_404(%s, %s, %s)",
|
kore_log("http_generic_404(%s, %s, %s)",
|
||||||
req->host, req->method, req->path);
|
req->host, req->method, req->path);
|
||||||
|
|
||||||
return (http_response(req, 404, NULL, 0, NULL));
|
return (http_response(req, 404, NULL, 0));
|
||||||
}
|
}
|
||||||
|
|
103
src/spdy.c
103
src/spdy.c
|
@ -40,8 +40,6 @@
|
||||||
static int spdy_ctrl_frame_syn_stream(struct netbuf *);
|
static int spdy_ctrl_frame_syn_stream(struct netbuf *);
|
||||||
static int spdy_ctrl_frame_settings(struct netbuf *);
|
static int spdy_ctrl_frame_settings(struct netbuf *);
|
||||||
static int spdy_ctrl_frame_ping(struct netbuf *);
|
static int spdy_ctrl_frame_ping(struct netbuf *);
|
||||||
static int spdy_stream_get_header(struct spdy_header_block *,
|
|
||||||
char *, char **);
|
|
||||||
|
|
||||||
static int spdy_zlib_inflate(struct connection *, u_int8_t *,
|
static int spdy_zlib_inflate(struct connection *, u_int8_t *,
|
||||||
size_t, u_int8_t **, u_int32_t *);
|
size_t, u_int8_t **, u_int32_t *);
|
||||||
|
@ -237,6 +235,59 @@ spdy_header_block_release(struct connection *c,
|
||||||
return (deflated);
|
return (deflated);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
spdy_stream_get_header(struct spdy_header_block *s, char *header, char **out)
|
||||||
|
{
|
||||||
|
char *cmp, t[128];
|
||||||
|
u_int8_t *p, *end;
|
||||||
|
u_int32_t i, nlen, vlen;
|
||||||
|
|
||||||
|
kore_log("spdy_stream_get_header(%p, %s) <%d>", s, header,
|
||||||
|
s->header_pairs);
|
||||||
|
|
||||||
|
p = s->header_block + 4;
|
||||||
|
end = s->header_block + s->header_block_len;
|
||||||
|
|
||||||
|
if (p >= end) {
|
||||||
|
kore_log("p >= end when looking for headers");
|
||||||
|
return (KORE_RESULT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < s->header_pairs; i++) {
|
||||||
|
nlen = net_read32(p);
|
||||||
|
if ((p + nlen + 4) > end) {
|
||||||
|
kore_log("nlen out of bounds on %d (%d)", i, nlen);
|
||||||
|
return (KORE_RESULT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
vlen = net_read32(p + nlen + 4);
|
||||||
|
if ((p + nlen + vlen + 8) > end) {
|
||||||
|
kore_log("vlen out of bounds on %d (%d)", i, vlen);
|
||||||
|
return (KORE_RESULT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
cmp = (char *)(p + 4);
|
||||||
|
memcpy(t, cmp, nlen);
|
||||||
|
t[nlen] = '\0';
|
||||||
|
kore_log("header %s", t);
|
||||||
|
|
||||||
|
if (!strncasecmp(cmp, header, nlen)) {
|
||||||
|
kore_log("found %s header", header);
|
||||||
|
|
||||||
|
cmp = (char *)(p + nlen + 8);
|
||||||
|
*out = (char *)kore_malloc(vlen + 1);
|
||||||
|
kore_strlcpy(*out, cmp, vlen + 1);
|
||||||
|
return (KORE_RESULT_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
kore_log("pair name %d bytes, value %d bytes", nlen, vlen);
|
||||||
|
|
||||||
|
p += nlen + vlen + 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (KORE_RESULT_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
spdy_ctrl_frame_syn_stream(struct netbuf *nb)
|
spdy_ctrl_frame_syn_stream(struct netbuf *nb)
|
||||||
{
|
{
|
||||||
|
@ -321,7 +372,7 @@ spdy_ctrl_frame_syn_stream(struct netbuf *nb)
|
||||||
GET_HEADER(":path", &path);
|
GET_HEADER(":path", &path);
|
||||||
GET_HEADER(":method", &method);
|
GET_HEADER(":method", &method);
|
||||||
GET_HEADER(":host", &host);
|
GET_HEADER(":host", &host);
|
||||||
if (!http_new_request(c, s, host, method, path)) {
|
if (!http_request_new(c, s, host, method, path)) {
|
||||||
free(s->hblock->header_block);
|
free(s->hblock->header_block);
|
||||||
free(s->hblock);
|
free(s->hblock);
|
||||||
free(s);
|
free(s);
|
||||||
|
@ -365,52 +416,6 @@ spdy_ctrl_frame_ping(struct netbuf *nb)
|
||||||
return (spdy_frame_send(c, SPDY_CTRL_FRAME_PING, 0, 4, id));
|
return (spdy_frame_send(c, SPDY_CTRL_FRAME_PING, 0, 4, id));
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
spdy_stream_get_header(struct spdy_header_block *s, char *header, char **out)
|
|
||||||
{
|
|
||||||
char *cmp;
|
|
||||||
u_int8_t *p, *end;
|
|
||||||
u_int32_t i, nlen, vlen;
|
|
||||||
|
|
||||||
p = s->header_block + 4;
|
|
||||||
end = s->header_block + s->header_block_len;
|
|
||||||
|
|
||||||
if (p >= end) {
|
|
||||||
kore_log("p >= end when looking for headers");
|
|
||||||
return (KORE_RESULT_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (i = 0; i < s->header_pairs; i++) {
|
|
||||||
nlen = net_read32(p);
|
|
||||||
if ((p + nlen + 4) >= end) {
|
|
||||||
kore_log("nlen out of bounds (%d)", nlen);
|
|
||||||
return (KORE_RESULT_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
vlen = net_read32(p + nlen + 4);
|
|
||||||
if ((p + nlen + vlen + 8) >= end) {
|
|
||||||
kore_log("vlen out of bounds (%d)", vlen);
|
|
||||||
return (KORE_RESULT_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
cmp = (char *)(p + 4);
|
|
||||||
if (!strncasecmp(cmp, header, nlen)) {
|
|
||||||
kore_log("found %s header", header);
|
|
||||||
|
|
||||||
cmp = (char *)(p + nlen + 8);
|
|
||||||
*out = (char *)kore_malloc(vlen + 1);
|
|
||||||
kore_strlcpy(*out, cmp, vlen + 1);
|
|
||||||
return (KORE_RESULT_OK);
|
|
||||||
}
|
|
||||||
|
|
||||||
kore_log("pair name %d bytes, value %d bytes", nlen, vlen);
|
|
||||||
|
|
||||||
p += nlen + vlen + 8;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (KORE_RESULT_ERROR);
|
|
||||||
}
|
|
||||||
|
|
||||||
static int
|
static int
|
||||||
spdy_zlib_inflate(struct connection *c, u_int8_t *src, size_t len,
|
spdy_zlib_inflate(struct connection *c, u_int8_t *src, size_t len,
|
||||||
u_int8_t **dst, u_int32_t *olen)
|
u_int8_t **dst, u_int32_t *olen)
|
||||||
|
|
20
src/utils.c
20
src/utils.c
|
@ -255,6 +255,26 @@ out:
|
||||||
return (t);
|
return (t);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
char *
|
||||||
|
kore_time_to_date(time_t now)
|
||||||
|
{
|
||||||
|
struct tm *tm;
|
||||||
|
static time_t last = 0;
|
||||||
|
static char tbuf[32];
|
||||||
|
|
||||||
|
if (now != last) {
|
||||||
|
last = now;
|
||||||
|
|
||||||
|
tm = gmtime(&now);
|
||||||
|
if (!strftime(tbuf, sizeof(tbuf), "%a, %d %b %Y %T GMT", tm)) {
|
||||||
|
kore_log("strftime() gave us NULL (%ld)", now);
|
||||||
|
return (NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (tbuf);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
fatal(const char *fmt, ...)
|
fatal(const char *fmt, ...)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue