Compare commits

..

2 commits

Author SHA1 Message Date
9ce826690d Only build
All checks were successful
continuous-integration/drone/push Build is passing
2020-08-31 21:02:03 +02:00
e8bc00ffd2 Add dirl to README
Some checks failed
continuous-integration/drone/push Build is failing
2020-08-31 21:00:05 +02:00
10 changed files with 200 additions and 776 deletions

View file

@ -1,137 +0,0 @@
---
Language: Cpp
# BasedOnStyle: Mozilla
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveMacros: false
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlines: Right
AlignOperands: true
AlignTrailingComments: true
AllowAllArgumentsOnNextLine: true
AllowAllConstructorInitializersOnNextLine: true
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Never
AllowShortCaseLabelsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Inline
AllowShortLambdasOnASingleLine: All
AllowShortIfStatementsOnASingleLine: Never
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: TopLevel
AlwaysBreakAfterReturnType: TopLevel
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BraceWrapping:
AfterCaseLabel: false
AfterClass: true
AfterControlStatement: false
AfterEnum: true
AfterFunction: true
AfterNamespace: false
AfterObjCDeclaration: false
AfterStruct: true
AfterUnion: true
AfterExternBlock: true
BeforeCatch: false
BeforeElse: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: false
SplitEmptyNamespace: true
BreakBeforeBinaryOperators: None
BreakBeforeBraces: Mozilla
BreakBeforeInheritanceComma: false
BreakInheritanceList: BeforeComma
BreakBeforeTernaryOperators: true
BreakConstructorInitializersBeforeComma: false
BreakConstructorInitializers: BeforeComma
BreakAfterJavaFieldAnnotations: false
BreakStringLiterals: true
ColumnLimit: 80
CommentPragmas: '^ IWYU pragma:'
CompactNamespaces: false
ConstructorInitializerAllOnOneLineOrOnePerLine: false
ConstructorInitializerIndentWidth: 2
ContinuationIndentWidth: 2
Cpp11BracedListStyle: false
DeriveLineEnding: true
DerivePointerAlignment: false
DisableFormat: false
ExperimentalAutoDetectBinPacking: false
FixNamespaceComments: false
ForEachMacros:
- foreach
- Q_FOREACH
- BOOST_FOREACH
IncludeBlocks: Preserve
IncludeCategories:
- Regex: '^"(llvm|llvm-c|clang|clang-c)/'
Priority: 2
SortPriority: 0
- Regex: '^(<|"(gtest|gmock|isl|json)/)'
Priority: 3
SortPriority: 0
- Regex: '.*'
Priority: 1
SortPriority: 0
IncludeIsMainRegex: '(Test)?$'
IncludeIsMainSourceRegex: ''
IndentCaseLabels: true
IndentGotoLabels: true
IndentPPDirectives: None
IndentWidth: 2
IndentWrappedFunctionNames: false
JavaScriptQuotes: Leave
JavaScriptWrapImports: true
KeepEmptyLinesAtTheStartOfBlocks: true
MacroBlockBegin: ''
MacroBlockEnd: ''
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
ObjCBinPackProtocolList: Auto
ObjCBlockIndentWidth: 2
ObjCSpaceAfterProperty: true
ObjCSpaceBeforeProtocolList: false
PenaltyBreakAssignment: 2
PenaltyBreakBeforeFirstCallParameter: 19
PenaltyBreakComment: 300
PenaltyBreakFirstLessLess: 120
PenaltyBreakString: 1000
PenaltyBreakTemplateDeclaration: 10
PenaltyExcessCharacter: 1000000
PenaltyReturnTypeOnItsOwnLine: 200
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
SortUsingDeclarations: true
SpaceAfterCStyleCast: false
SpaceAfterLogicalNot: false
SpaceAfterTemplateKeyword: false
SpaceBeforeAssignmentOperators: true
SpaceBeforeCpp11BracedList: false
SpaceBeforeCtorInitializerColon: true
SpaceBeforeInheritanceColon: true
SpaceBeforeParens: ControlStatements
SpaceBeforeRangeBasedForLoopColon: true
SpaceInEmptyBlock: false
SpaceInEmptyParentheses: false
SpacesBeforeTrailingComments: 1
SpacesInAngles: false
SpacesInConditionalStatement: false
SpacesInContainerLiterals: true
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
SpaceBeforeSquareBrackets: false
Standard: Latest
StatementMacros:
- Q_UNUSED
- QT_REQUIRE_VERSION
TabWidth: 8
UseCRLF: false
UseTab: Never
...

View file

@ -7,16 +7,3 @@ steps:
image: gcc image: gcc
commands: commands:
- make - make
- mv quark quark-dirl
- name: publish
image: appleboy/drone-scp
settings:
host: friedl.net
username:
from_secret: deploy_user
password:
from_secret: deploy_password
port: 22
target: /var/services/dirlist/repo/bin/suckless/quark
source: quark-dirl

4
.gitignore vendored
View file

@ -1,4 +0,0 @@
*.o
.gdb_history
config.h
quark

View file

@ -4,12 +4,11 @@
include config.mk include config.mk
COMPONENTS = data http sock util dirl COMPONENTS = data http sock util
all: quark all: quark
data.o: data.c data.h util.h http.h dirl.h config.mk data.o: data.c data.h util.h http.h config.mk
dirl.o: dirl.c dirl.h util.h http.h config.mk
http.o: http.c http.h util.h http.h data.h config.h config.mk http.o: http.c http.h util.h http.h data.h config.h config.mk
main.o: main.c util.h sock.h http.h arg.h config.h config.mk main.o: main.c util.h sock.h http.h arg.h config.h config.mk
sock.o: sock.c sock.h util.h config.mk sock.o: sock.c sock.h util.h config.mk

100
README.md
View file

@ -5,94 +5,36 @@ found at https://git.suckless.org/quark.
Quark is a small http server. Quark is a small http server.
# DIRL # Feature Patches
dirl is a quark extension for customized directory listings. ## Dirl: Customizable directory listing
[dirl](https://git.friedl.net/playground/suckless-quark/src/branch/dirlist) lets
you serve a fully customizable directory listing.
Per default dirl generates html for a directory listing like this: You can compile `dirl` from the `dirlist` branch, download a pre-compiled [musl
binary](https://dirlist.friedl.net/bin/suckless/quark/quark-dirl) or even pull a
pre-made [docker
image](https://hub.docker.com/repository/docker/arminfriedl/quark).
```html You can find an example deployment of [here](https://dirlist.friedl.net/). It
<!DOCTYPE HTML PUBLIC " - // W3C//DTD HTML 3.2 Final//EN"> uses the default template just with a custom css. You can define your own
<!-- Header Section --> templates too for full customization. For details see the dirl
<html> [README.md](https://git.friedl.net/playground/suckless-quark/src/branch/dirlist/README.md).
<head>
<link rel="stylesheet" href="style.css">
<title>Index of {uri}</title>
</head>
<body>
<h1>Index of {uri}</h1>
<p><a href="..">&crarr; Parent Directory</a></p>
<hr />
<table>
<tr><th>Name</th><th>Modified</th><th>Size</th></tr>
<!-- /Header Section -->
<!-- Entry Section --> # Issues
<!-- (repeated for each entry in the directory) -->
<tr>
<td><a href="{entry}">{entry}{suffix}</a>
<td>{modified}</td>
<td>{size}</td>
</tr>
<!-- /Entry Section -->
<!-- Footer Section --> ## fork: Resource temporarily unavailable
</table> When running [quark](http://tools.suckless.org/quark/) (#6606994) on my system
<hr /> with `sudo ./quark -p 9763 -u <user> -g <group>` it dies with `./quark: fork:
<p> Resource temporarily unavailable` at `fork()`.
Served by <a href="http://tools.suckless.org/quark/">quark</a> and <a href="https://git.friedl.net/playground/suckless-quark/src/branch/dirlist">dirl</a>
</p>
</body>
</html>
<!-- /Footer Section -->
```
## Customize Reason being that by default quark sets the RLIMIT_NPROC to 512 processes. When running as a non-exclusive user this limit is easily reached before even starting quark.
The default listing can be styled by a `style.css` in the root directory. `resource-depletion-fix` contains a small forkbomb (`minibomb.c`) to simulate a user with > 512 processes. Compile it with `make minibomb`. When running the minibomb and quark with the same user quark fails.
You can also use your fully customized template by creating one or all the The `resource-depletion-fix` branch contains a fix by setting the RLIMIT_NPROC only if the current system limit is lower than what would be set by quark. You can [download the patch](https://dirlist.friedl.net/suckless/quark/), or compile from the `resource-depletion-fix` branch.
template files for each section. Per default the section templates are named:
- .header.tpl
- .entry.tpl (repeated for each directory entry)
- .footer.tpl
Note that if you only provide some of the template files, they have to be Note that quark also has a `-n` parameter with which the max number of processes can be set as an alternative to this patch.
compatible with the generated default for the other sections.
For each of these templates you can use placeholders that are replaced by their respective values:
- header
* `{uri}`: Replaced by the current path
- entry
* `{entry}`: Name of the entry
* `{suffix}`: A suffix for the entry, mostly useful to distinguish directories (suffix '/') from files
* `{modified}`: Date the entry was last modified
* `{size}`: Size of the entry (if available)
### Subdirectory styling
dirl tries to the closest template for the currently visited path. This gives
you the opportunity to override templates in subdirectories. dirl walks the
directory hierarchy upwards from the currently visited path. As soon as it finds
one of the template files in a directory, it stops searching and uses the
templates in that directory.
In case no templates are found up until and including root, the default
templates are used.
### Customize names
The files defined as templates and style are ignored in the directory listing
itself. In case you need to list one of these directories, or have any other
reason to choose different names, the filenames can be configured in `dirl.h`.
Note that you need to compile your own quark version then.
# Download
You can also download CI builds for [quark-dirl](https://dirlist.friedl.net/bin/suckless/quark/).
There are no official releases. Quark has no dependencies and you can easily
build it from source. Don't forget to read up on the [suckless
philosophy](http://suckless.org/philosophy/).
# Github Users # Github Users
If you are visiting this repository on GitHub, you are on a mirror of If you are visiting this repository on GitHub, you are on a mirror of

117
data.c
View file

@ -10,7 +10,6 @@
#include "http.h" #include "http.h"
#include "data.h" #include "data.h"
#include "util.h" #include "util.h"
#include "dirl.h"
static int static int
compareent(const struct dirent **d1, const struct dirent **d2) compareent(const struct dirent **d1, const struct dirent **d2)
@ -26,6 +25,64 @@ compareent(const struct dirent **d1, const struct dirent **d2)
return strcmp((*d1)->d_name, (*d2)->d_name); return strcmp((*d1)->d_name, (*d2)->d_name);
} }
static char *
suffix(int t)
{
switch (t) {
case DT_FIFO: return "|";
case DT_DIR: return "/";
case DT_LNK: return "@";
case DT_SOCK: return "=";
}
return "";
}
static void
html_escape(const char *src, char *dst, size_t dst_siz)
{
const struct {
char c;
char *s;
} escape[] = {
{ '&', "&amp;" },
{ '<', "&lt;" },
{ '>', "&gt;" },
{ '"', "&quot;" },
{ '\'', "&#x27;" },
};
size_t i, j, k, esclen;
for (i = 0, j = 0; src[i] != '\0'; i++) {
for (k = 0; k < LEN(escape); k++) {
if (src[i] == escape[k].c) {
break;
}
}
if (k == LEN(escape)) {
/* no escape char at src[i] */
if (j == dst_siz - 1) {
/* silent truncation */
break;
} else {
dst[j++] = src[i];
}
} else {
/* escape char at src[i] */
esclen = strlen(escape[k].s);
if (j >= dst_siz - esclen) {
/* silent truncation */
break;
} else {
memcpy(&dst[j], escape[k].s, esclen);
j += esclen;
}
}
}
dst[j] = '\0';
}
enum status enum status
data_send_dirlisting(int fd, const struct response *res) data_send_dirlisting(int fd, const struct response *res)
{ {
@ -33,38 +90,48 @@ data_send_dirlisting(int fd, const struct response *res)
struct dirent **e; struct dirent **e;
size_t i; size_t i;
int dirlen; int dirlen;
char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */
/* read directory */ /* read directory */
if ((dirlen = scandir(res->path, &e, NULL, compareent)) < 0) { if ((dirlen = scandir(res->path, &e, NULL, compareent)) < 0) {
return S_FORBIDDEN; return S_FORBIDDEN;
} }
/* read templates */ /* listing header (we use esc because sizeof(esc) >= PATH_MAX) */
struct dirl_templ templates = dirl_read_templ(res->uri); html_escape(res->uri, esc, MIN(PATH_MAX, sizeof(esc)));
if (dprintf(fd,
/* listing header */ "<!DOCTYPE html>\n<html>\n\t<head>"
if ((ret = dirl_header(fd, res, &templates))) { "<title>Index of %s</title></head>\n"
return ret; "\t<body>\n\t\t<a href=\"..\">..</a>",
} esc) < 0) {
ret = S_REQUEST_TIMEOUT;
/* entries */ goto cleanup;
for (i = 0; i < (size_t)dirlen; i++) {
/* skip dirl special files */
if(dirl_skip(e[i]->d_name)) {
continue;
}
/* entry line */
if ((ret = dirl_entry(fd, e[i], res, &templates))) {
goto cleanup;
}
} }
/* listing footer */ /* listing */
if ((ret = dirl_footer(fd, &templates))) { for (i = 0; i < (size_t)dirlen; i++) {
goto cleanup; /* skip hidden files, "." and ".." */
} if (e[i]->d_name[0] == '.') {
continue;
}
/* entry line */
html_escape(e[i]->d_name, esc, sizeof(esc));
if (dprintf(fd, "<br />\n\t\t<a href=\"%s%s\">%s%s</a>",
esc,
(e[i]->d_type == DT_DIR) ? "/" : "",
esc,
suffix(e[i]->d_type)) < 0) {
ret = S_REQUEST_TIMEOUT;
goto cleanup;
}
}
/* listing footer */
if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
ret = S_REQUEST_TIMEOUT;
goto cleanup;
}
cleanup: cleanup:
while (dirlen--) { while (dirlen--) {

250
dirl.c
View file

@ -1,250 +0,0 @@
/* See LICENSE file for copyright and license details. */
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <linux/limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/sendfile.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include "config.h"
#include "dirl.h"
#include "http.h"
#include "util.h"
static char*
suffix(int t)
{
switch (t) {
case DT_FIFO:
return "|";
case DT_DIR:
return "/";
case DT_LNK:
return "@";
case DT_SOCK:
return "=";
}
return "";
}
static void
html_escape(const char* src, char* dst, size_t dst_siz)
{
const struct
{
char c;
char* s;
} escape[] = {
{ '&', "&amp;" }, { '<', "&lt;" }, { '>', "&gt;" },
{ '"', "&quot;" }, { '\'', "&#x27;" },
};
size_t i, j, k, esclen;
for (i = 0, j = 0; src[i] != '\0'; i++) {
for (k = 0; k < LEN(escape); k++) {
if (src[i] == escape[k].c) {
break;
}
}
if (k == LEN(escape)) {
/* no escape char at src[i] */
if (j == dst_siz - 1) {
/* silent truncation */
break;
} else {
dst[j++] = src[i];
}
} else {
/* escape char at src[i] */
esclen = strlen(escape[k].s);
if (j >= dst_siz - esclen) {
/* silent truncation */
break;
} else {
memcpy(&dst[j], escape[k].s, esclen);
j += esclen;
}
}
}
dst[j] = '\0';
}
/* Try to find templates up until root
*
* Iterates the directory hierarchy upwards. Returns the closest path containing
* one of the template files or NULL if none could be found.
*
* Note that we are chrooted.
*/
static char*
dirl_find_templ_dir(const char* start_path)
{
char* path_buf = calloc(sizeof(char), strlen(start_path)+1);
strcat(path_buf, start_path);
if (strlen(path_buf) > 1 && path_buf[strlen(path_buf) - 1] == '/') {
// don't read last dir twice, except at root
path_buf[strlen(path_buf) - 1] = '\0';
}
while (strlen(path_buf) != 0) {
DIR* cur = opendir(path_buf);
struct dirent* de;
errno = 0;
while ((de = readdir(cur))) {
if (de->d_type == DT_REG) {
if (!strcmp(DIRL_HEADER, de->d_name) || !strcmp(DIRL_ENTRY, de->d_name) ||
!strcmp(DIRL_FOOTER, de->d_name)) {
closedir(cur);
return path_buf;
}
}
}
if (strlen(path_buf) > 1) {
char* parent = strrchr(path_buf, '/');
(*parent) = '\0'; // strip tail from path_buf
if (strlen(path_buf) == 0) { // we stripped root, let it loop once more
path_buf[0] = '/';
path_buf[1] = '\0';
}
} else {
path_buf[0] = '\0'; // we checked root, now terminate loop
}
}
free(path_buf);
return NULL;
}
/* Helper function to fill template from base+name if file exists */
static void
dirl_fill_templ(char** templ, char* base, char* name, char* def)
{
if (!base || !name) {
*templ = def;
return;
}
char* path = calloc(sizeof(char), strlen(base) + strlen(name));
strcpy(path, base);
strcat(path, name);
char* file_buf = read_file(path);
free(path);
if (file_buf) {
*templ = file_buf;
} else {
*templ = def;
}
}
struct dirl_templ
dirl_read_templ(const char* path)
{
struct dirl_templ templ;
char* templ_dir = dirl_find_templ_dir(path);
dirl_fill_templ(&templ.header, templ_dir, DIRL_HEADER, DIRL_HEADER_DEFAULT);
dirl_fill_templ(&templ.entry, templ_dir, DIRL_ENTRY, DIRL_ENTRY_DEFAULT);
dirl_fill_templ(&templ.footer, templ_dir, DIRL_FOOTER, DIRL_FOOTER_DEFAULT);
free(templ_dir);
return templ;
}
enum status
dirl_header(int fd, const struct response* res, const struct dirl_templ* templ)
{
/* Replace placeholder */
char* nhead = calloc(sizeof(char), strlen(templ->header) + 1);
memcpy(nhead, templ->header, strlen(templ->header) + 1);
replace(&nhead, "{uri}", res->uri);
/* Write header */
write(fd, nhead, strlen(nhead));
free(nhead);
/* listing header */
return 0;
}
enum status
dirl_entry(int fd,
const struct dirent* entry,
const struct response* res,
const struct dirl_templ* templ)
{
struct stat stat_buf;
char* path_buf = calloc(sizeof(char), strlen(res->path)+strlen(entry->d_name));
strcat(path_buf, res->uri);
strcat(path_buf, entry->d_name);
lstat(path_buf, &stat_buf);
char* nentry = calloc(sizeof(char), strlen(templ->entry) + 1);
memcpy(nentry, templ->entry, strlen(templ->entry) + 1);
/* Replace placeholder */
char esc[PATH_MAX * 6];
html_escape(entry->d_name, esc, PATH_MAX * 6);
replace(&nentry, "{entry}", entry->d_name);
replace(&nentry, "{suffix}", suffix(entry->d_type));
char size_buf[1024];
if (entry->d_type == DT_REG) {
snprintf(size_buf, 1024, "%ld", stat_buf.st_size);
} else {
sprintf(size_buf, "-");
}
replace(&nentry, "{size}", size_buf);
char time_buf[1024];
struct tm tm;
gmtime_r(&stat_buf.st_mtim.tv_sec, &tm);
strftime(time_buf, 1024, "%F %H:%m", &tm);
replace(&nentry, "{modified}", time_buf);
/* Write entry */
write(fd, nentry, strlen(nentry));
free(nentry);
return 0;
}
enum status
dirl_footer(int fd, const struct dirl_templ* templ)
{
/* Replace placeholder */
char* nfoot = calloc(sizeof(char), strlen(templ->footer) + 1);
memcpy(nfoot, templ->footer, strlen(templ->footer) + 1);
/* Write footer */
write(fd, nfoot, strlen(nfoot));
free(nfoot);
return 0;
}
int
dirl_skip(const char* name)
{
return name[0] == '.' //
|| !strcmp(name, DIRL_HEADER) //
|| !strcmp(name, DIRL_ENTRY) //
|| !strcmp(name, DIRL_FOOTER) //
|| !strcmp(name, DIRL_STYLE); //
}

91
dirl.h
View file

@ -1,91 +0,0 @@
/* See LICENSE file for copyright and license details. */
#ifndef DIRL_H
#define DIRL_H
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include "http.h"
#define DIRL_HEADER ".header.tpl"
#define DIRL_ENTRY ".entry.tpl"
#define DIRL_FOOTER ".footer.tpl"
#define DIRL_STYLE "style.css"
/* Default template definitions
*
* Used if no template files can be found
*/
#define DIRL_HEADER_DEFAULT \
"<!DOCTYPE HTML PUBLIC \" - // W3C//DTD HTML 3.2 Final//EN\">\n" \
"<html>\n" \
" <head>\n" \
" <link rel=\"stylesheet\" href=\"/" DIRL_STYLE "\">\n" \
" <title>Index of {uri}</title>\n" \
" </head>\n" \
" <body>\n" \
" <h1>Index of {uri}</h1>\n" \
" <p><a href=\"..\">&crarr; Parent Directory</a></p>\n" \
" <hr />\n" \
" <table>\n" \
" <tr><th>Name</th><th>Modified</th><th>Size</th></tr>"
#define DIRL_ENTRY_DEFAULT \
" <tr>\n" \
" <td><a href=\"{entry}\">{entry}{suffix}</a>\n" \
" <td>{modified}</td>\n" \
" <td>{size}</td>\n" \
" </tr>\n"
#define DIRL_FOOTER_DEFAULT \
" </table>\n" \
" <hr />\n" \
" <p>" \
"Served by" \
" <a href=\"http://tools.suckless.org/quark/\">quark</a>" \
" and" \
" <a href=" \
"\"https://git.friedl.net/playground/suckless-quark/src/branch/dirlist\">" \
"dirl" \
"</a>" \
"</p>\n" \
"</body>\n" \
"</html>"
struct dirl_templ
{
char* header;
char* entry;
char* footer;
};
struct dirl_templ
dirl_read_templ(const char* path);
/* Determine if an dirlist entry should be skipped
*
* Skips:
* - hidden files and directories
* - special directory entries (., ..)
* - header template: DIRL_HEADER
* - entry template: DIRL_ENTRY
* - footer template: DIRL_FOOTER
* - dirlist style: DRIL_STYLE
*/
int
dirl_skip(const char*);
/* Print header into the response */
enum status
dirl_header(int, const struct response*, const struct dirl_templ*);
/* Print entry into the response */
enum status
dirl_entry(int, const struct dirent*, const struct response*, const struct dirl_templ*);
/* Print footer into the response */
enum status
dirl_footer(int, const struct dirl_templ*);
#endif /* DIRL_H */

257
util.c
View file

@ -20,194 +20,107 @@ char *argv0;
static void static void
verr(const char *fmt, va_list ap) verr(const char *fmt, va_list ap)
{ {
if (argv0 && strncmp(fmt, "usage", sizeof("usage") - 1)) { if (argv0 && strncmp(fmt, "usage", sizeof("usage") - 1)) {
fprintf(stderr, "%s: ", argv0); fprintf(stderr, "%s: ", argv0);
} }
vfprintf(stderr, fmt, ap); vfprintf(stderr, fmt, ap);
if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
fputc(' ', stderr); fputc(' ', stderr);
perror(NULL); perror(NULL);
} else { } else {
fputc('\n', stderr); fputc('\n', stderr);
} }
} }
void void
warn(const char *fmt, ...) warn(const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
verr(fmt, ap); verr(fmt, ap);
va_end(ap); va_end(ap);
} }
void void
die(const char *fmt, ...) die(const char *fmt, ...)
{ {
va_list ap; va_list ap;
va_start(ap, fmt); va_start(ap, fmt);
verr(fmt, ap); verr(fmt, ap);
va_end(ap); va_end(ap);
exit(1); exit(1);
} }
void void
epledge(const char *promises, const char *execpromises) epledge(const char *promises, const char *execpromises)
{ {
(void)promises; (void)promises;
(void)execpromises; (void)execpromises;
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (pledge(promises, execpromises) == -1) { if (pledge(promises, execpromises) == -1) {
die("pledge:"); die("pledge:");
} }
#endif /* __OpenBSD__ */ #endif /* __OpenBSD__ */
} }
void void
eunveil(const char *path, const char *permissions) eunveil(const char *path, const char *permissions)
{ {
(void)path; (void)path;
(void)permissions; (void)permissions;
#ifdef __OpenBSD__ #ifdef __OpenBSD__
if (unveil(path, permissions) == -1) { if (unveil(path, permissions) == -1) {
die("unveil:"); die("unveil:");
} }
#endif /* __OpenBSD__ */ #endif /* __OpenBSD__ */
} }
int int
timestamp(char *buf, size_t len, time_t t) timestamp(char *buf, size_t len, time_t t)
{ {
struct tm tm; struct tm tm;
if (gmtime_r(&t, &tm) == NULL || if (gmtime_r(&t, &tm) == NULL ||
strftime(buf, len, "%a, %d %b %Y %T GMT", &tm) == 0) { strftime(buf, len, "%a, %d %b %Y %T GMT", &tm) == 0) {
return 1; return 1;
} }
return 0; return 0;
} }
int int
esnprintf(char *str, size_t size, const char *fmt, ...) esnprintf(char *str, size_t size, const char *fmt, ...)
{ {
va_list ap; va_list ap;
int ret; int ret;
va_start(ap, fmt); va_start(ap, fmt);
ret = vsnprintf(str, size, fmt, ap); ret = vsnprintf(str, size, fmt, ap);
va_end(ap); va_end(ap);
return (ret < 0 || (size_t)ret >= size); return (ret < 0 || (size_t)ret >= size);
} }
int int
prepend(char *str, size_t size, const char *prefix) prepend(char *str, size_t size, const char *prefix)
{ {
size_t len = strlen(str), prefixlen = strlen(prefix); size_t len = strlen(str), prefixlen = strlen(prefix);
if (len + prefixlen + 1 > size) { if (len + prefixlen + 1 > size) {
return 1; return 1;
} }
memmove(str + prefixlen, str, len + 1); memmove(str + prefixlen, str, len + 1);
memcpy(str, prefix, prefixlen); memcpy(str, prefix, prefixlen);
return 0; return 0;
}
void
replace(char **src, const char *old, const char* new) {
int old_len = strlen(old);
int new_len = strlen(new);
int src_len = strlen(*src);
/* Count needed replacements */
const char* tmp = *src;
int replc=0;
while ((tmp=strstr(tmp, old))) {
replc++;
tmp += old_len;
}
/* Allocate enough space for the new string */
size_t buf_size = src_len + replc * (new_len - old_len) + 1;
char* buf = calloc(sizeof(char), buf_size);
/* Now start replacing */
const char *srcidx = *src;
const char *srcidx_old = *src;
char *bufidx = buf;
while (replc--) {
srcidx_old = strstr(srcidx, old);
long repl_len = labs(srcidx_old - srcidx);
bufidx = strncpy(bufidx, srcidx, repl_len) + repl_len;
bufidx = strcpy(bufidx, new) + new_len;
srcidx = srcidx_old+old_len;
}
strncpy(bufidx, srcidx, strlen(srcidx)); // copy tail
free(*src);
*src = buf;
}
char*
read_file(const char* path){
FILE* tpl_fp;
if (!(tpl_fp = fopen(path, "r"))) {
return NULL;
}
/* Get size of template */
if (fseek(tpl_fp, 0L, SEEK_END) < 0) {
fclose(tpl_fp);
return NULL;
}
long tpl_size;
if ((tpl_size = ftell(tpl_fp)) < 0) {
fclose(tpl_fp);
return NULL;
}
rewind(tpl_fp);
/* Read template into tpl_buf */
char* tpl_buf = (char*)calloc(sizeof(char), tpl_size);
if (tpl_buf == NULL) {
fclose(tpl_fp);
free(tpl_buf);
return NULL;
}
if (fread(tpl_buf, 1, tpl_size, tpl_fp) < (size_t) tpl_size) {
if (feof(tpl_fp)) {
warn("Reached end of file %s prematurely", path);
} else if (ferror(tpl_fp)) {
warn("Error while reading file %s", path);
}
fclose(tpl_fp);
free(tpl_buf);
clearerr(tpl_fp);
return NULL;
}
return tpl_buf;
} }
#define INVALID 1 #define INVALID 1
@ -218,39 +131,39 @@ long long
strtonum(const char *numstr, long long minval, long long maxval, strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp) const char **errstrp)
{ {
long long ll = 0; long long ll = 0;
int error = 0; int error = 0;
char *ep; char *ep;
struct errval { struct errval {
const char *errstr; const char *errstr;
int err; int err;
} ev[4] = { } ev[4] = {
{ NULL, 0 }, { NULL, 0 },
{ "invalid", EINVAL }, { "invalid", EINVAL },
{ "too small", ERANGE }, { "too small", ERANGE },
{ "too large", ERANGE }, { "too large", ERANGE },
}; };
ev[0].err = errno; ev[0].err = errno;
errno = 0; errno = 0;
if (minval > maxval) { if (minval > maxval) {
error = INVALID; error = INVALID;
} else { } else {
ll = strtoll(numstr, &ep, 10); ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0') if (numstr == ep || *ep != '\0')
error = INVALID; error = INVALID;
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval) else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
error = TOOSMALL; error = TOOSMALL;
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval) else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
error = TOOLARGE; error = TOOLARGE;
} }
if (errstrp != NULL) if (errstrp != NULL)
*errstrp = ev[error].errstr; *errstrp = ev[error].errstr;
errno = ev[error].err; errno = ev[error].err;
if (error) if (error)
ll = 0; ll = 0;
return ll; return ll;
} }
/* /*
@ -262,10 +175,10 @@ strtonum(const char *numstr, long long minval, long long maxval,
void * void *
reallocarray(void *optr, size_t nmemb, size_t size) reallocarray(void *optr, size_t nmemb, size_t size)
{ {
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) && if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) { nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM; errno = ENOMEM;
return NULL; return NULL;
} }
return realloc(optr, size * nmemb); return realloc(optr, size * nmemb);
} }

2
util.h
View file

@ -52,8 +52,6 @@ void eunveil(const char *, const char *);
int timestamp(char *, size_t, time_t); int timestamp(char *, size_t, time_t);
int esnprintf(char *, size_t, const char *, ...); int esnprintf(char *, size_t, const char *, ...);
int prepend(char *, size_t, const char *); int prepend(char *, size_t, const char *);
void replace(char **, const char *, const char *);
char *read_file(const char* path);
void *reallocarray(void *, size_t, size_t); void *reallocarray(void *, size_t, size_t);
long long strtonum(const char *, long long, long long, const char **); long long strtonum(const char *, long long, long long, const char **);