Compare commits

..

11 commits

Author SHA1 Message Date
48db5eea1d Fix path for root case
All checks were successful
continuous-integration/drone/push Build is passing
In case of root, res->path returns '.' which does not mix well with
concatenating the entry name. For example this may result in `.bin` for a folder
`bin` in root.
2020-08-31 18:58:01 +02:00
66558aa615 Stat full path, allocate enough space
All checks were successful
continuous-integration/drone/push Build is passing
Not stat'ing the full path (just entry->d_name) strangely works on glibc/fedora
but fails on musl/alpine. Stat'ing the full path is the right thing to do,
anyways.

There was a heap corruption in `dirl_find_templ_dir` due not allocating enough
space for the terminating NULL in `path_buf`. This again only showed up in
musl/alpine.
2020-08-31 06:42:23 +02:00
d06c37fbd5 Add README for dirl, publish CI builds
All checks were successful
continuous-integration/drone/push Build is passing
2020-08-30 19:07:57 +02:00
2a3e25dc6b Fix merge leftovers, add attribution footer by default 2020-08-30 18:07:23 +02:00
e262166522 Re-add replace and read_file to util
Some checks failed
continuous-integration/drone/push Build is failing
2020-08-30 14:32:48 +02:00
30ca105c66 Merge branch 'master' into dirlist 2020-08-30 14:16:41 +02:00
eba491b4b5 Find template in root, don't search tail dir twice
Some checks failed
continuous-integration/drone/push Build is failing
2020-08-30 09:38:02 +02:00
bd51ff593d Refactoring
Some checks failed
continuous-integration/drone/push Build is failing
2020-08-30 08:43:10 +02:00
a22ac176ca Template overrides in subdirs, prefetch templates
Some checks failed
continuous-integration/drone/push Build is failing
2020-08-29 20:36:10 +02:00
5b6f8e5083 Read from template files with placeholder replacement
Some checks failed
continuous-integration/drone/push Build is failing
2020-08-28 00:46:08 +02:00
329dedc33b Read dirlist from templates
Some checks failed
continuous-integration/drone/push Build is failing
Reads header, footer and entry templates from files defined in config.h.
Defaults to original output if not found.
2020-08-25 21:08:23 +02:00
10 changed files with 773 additions and 197 deletions

137
.clang-format Normal file
View file

@ -0,0 +1,137 @@
---
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,3 +7,16 @@ steps:
image: gcc
commands:
- 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 Normal file
View file

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

View file

@ -4,11 +4,12 @@
include config.mk
COMPONENTS = data http sock util
COMPONENTS = data http sock util dirl
all: quark
data.o: data.c data.h util.h http.h config.mk
data.o: data.c data.h util.h http.h dirl.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
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

100
README.md
View file

@ -5,36 +5,94 @@ found at https://git.suckless.org/quark.
Quark is a small http server.
# Feature Patches
# DIRL
## Dirl: Customizable directory listing
[dirl](https://git.friedl.net/playground/suckless-quark/src/branch/dirlist) lets
you serve a fully customizable directory listing.
dirl is a quark extension for customized directory listings.
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).
Per default dirl generates html for a directory listing like this:
You can find an example deployment of [here](https://dirlist.friedl.net/). It
uses the default template just with a custom css. You can define your own
templates too for full customization. For details see the dirl
[README.md](https://git.friedl.net/playground/suckless-quark/src/branch/dirlist/README.md).
```html
<!DOCTYPE HTML PUBLIC " - // W3C//DTD HTML 3.2 Final//EN">
<!-- Header Section -->
<html>
<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 -->
# Issues
<!-- Entry Section -->
<!-- (repeated for each entry in the directory) -->
<tr>
<td><a href="{entry}">{entry}{suffix}</a>
<td>{modified}</td>
<td>{size}</td>
</tr>
<!-- /Entry Section -->
## fork: Resource temporarily unavailable
When running [quark](http://tools.suckless.org/quark/) (#6606994) on my system
with `sudo ./quark -p 9763 -u <user> -g <group>` it dies with `./quark: fork:
Resource temporarily unavailable` at `fork()`.
<!-- Footer Section -->
</table>
<hr />
<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>
</body>
</html>
<!-- /Footer Section -->
```
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.
## Customize
`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.
The default listing can be styled by a `style.css` in the root directory.
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.
You can also use your fully customized template by creating one or all the
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 quark also has a `-n` parameter with which the max number of processes can be set as an alternative to this patch.
Note that if you only provide some of the template files, they have to be
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
If you are visiting this repository on GitHub, you are on a mirror of

111
data.c
View file

@ -10,6 +10,7 @@
#include "http.h"
#include "data.h"
#include "util.h"
#include "dirl.h"
static int
compareent(const struct dirent **d1, const struct dirent **d2)
@ -25,64 +26,6 @@ compareent(const struct dirent **d1, const struct dirent **d2)
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
data_send_dirlisting(int fd, const struct response *res)
{
@ -90,48 +33,38 @@ data_send_dirlisting(int fd, const struct response *res)
struct dirent **e;
size_t i;
int dirlen;
char esc[PATH_MAX /* > NAME_MAX */ * 6]; /* strlen("&...;") <= 6 */
/* read directory */
if ((dirlen = scandir(res->path, &e, NULL, compareent)) < 0) {
return S_FORBIDDEN;
}
/* listing header (we use esc because sizeof(esc) >= PATH_MAX) */
html_escape(res->uri, esc, MIN(PATH_MAX, sizeof(esc)));
if (dprintf(fd,
"<!DOCTYPE html>\n<html>\n\t<head>"
"<title>Index of %s</title></head>\n"
"\t<body>\n\t\t<a href=\"..\">..</a>",
esc) < 0) {
ret = S_REQUEST_TIMEOUT;
goto cleanup;
}
/* read templates */
struct dirl_templ templates = dirl_read_templ(res->uri);
/* listing */
/* listing header */
if ((ret = dirl_header(fd, res, &templates))) {
return ret;
}
/* entries */
for (i = 0; i < (size_t)dirlen; i++) {
/* skip hidden files, "." and ".." */
if (e[i]->d_name[0] == '.') {
continue;
}
/* 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;
}
/* 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;
}
/* listing footer */
if ((ret = dirl_footer(fd, &templates))) {
goto cleanup;
}
cleanup:
while (dirlen--) {

250
dirl.c Normal file
View file

@ -0,0 +1,250 @@
/* 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 Normal file
View file

@ -0,0 +1,91 @@
/* 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,107 +20,194 @@ char *argv0;
static void
verr(const char *fmt, va_list ap)
{
if (argv0 && strncmp(fmt, "usage", sizeof("usage") - 1)) {
fprintf(stderr, "%s: ", argv0);
}
if (argv0 && strncmp(fmt, "usage", sizeof("usage") - 1)) {
fprintf(stderr, "%s: ", argv0);
}
vfprintf(stderr, fmt, ap);
vfprintf(stderr, fmt, ap);
if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
if (fmt[0] && fmt[strlen(fmt) - 1] == ':') {
fputc(' ', stderr);
perror(NULL);
} else {
fputc('\n', stderr);
}
}
void
warn(const char *fmt, ...)
{
va_list ap;
va_list ap;
va_start(ap, fmt);
verr(fmt, ap);
va_end(ap);
va_start(ap, fmt);
verr(fmt, ap);
va_end(ap);
}
void
die(const char *fmt, ...)
{
va_list ap;
va_list ap;
va_start(ap, fmt);
verr(fmt, ap);
va_end(ap);
va_start(ap, fmt);
verr(fmt, ap);
va_end(ap);
exit(1);
exit(1);
}
void
epledge(const char *promises, const char *execpromises)
{
(void)promises;
(void)execpromises;
(void)promises;
(void)execpromises;
#ifdef __OpenBSD__
if (pledge(promises, execpromises) == -1) {
die("pledge:");
}
if (pledge(promises, execpromises) == -1) {
die("pledge:");
}
#endif /* __OpenBSD__ */
}
void
eunveil(const char *path, const char *permissions)
{
(void)path;
(void)permissions;
(void)path;
(void)permissions;
#ifdef __OpenBSD__
if (unveil(path, permissions) == -1) {
die("unveil:");
}
if (unveil(path, permissions) == -1) {
die("unveil:");
}
#endif /* __OpenBSD__ */
}
int
timestamp(char *buf, size_t len, time_t t)
{
struct tm tm;
struct tm tm;
if (gmtime_r(&t, &tm) == NULL ||
strftime(buf, len, "%a, %d %b %Y %T GMT", &tm) == 0) {
return 1;
}
if (gmtime_r(&t, &tm) == NULL ||
strftime(buf, len, "%a, %d %b %Y %T GMT", &tm) == 0) {
return 1;
}
return 0;
return 0;
}
int
esnprintf(char *str, size_t size, const char *fmt, ...)
{
va_list ap;
int ret;
va_list ap;
int ret;
va_start(ap, fmt);
ret = vsnprintf(str, size, fmt, ap);
va_end(ap);
va_start(ap, fmt);
ret = vsnprintf(str, size, fmt, ap);
va_end(ap);
return (ret < 0 || (size_t)ret >= size);
return (ret < 0 || (size_t)ret >= size);
}
int
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) {
return 1;
}
if (len + prefixlen + 1 > size) {
return 1;
}
memmove(str + prefixlen, str, len + 1);
memcpy(str, prefix, prefixlen);
memmove(str + prefixlen, str, len + 1);
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
@ -131,39 +218,39 @@ long long
strtonum(const char *numstr, long long minval, long long maxval,
const char **errstrp)
{
long long ll = 0;
int error = 0;
char *ep;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
};
long long ll = 0;
int error = 0;
char *ep;
struct errval {
const char *errstr;
int err;
} ev[4] = {
{ NULL, 0 },
{ "invalid", EINVAL },
{ "too small", ERANGE },
{ "too large", ERANGE },
};
ev[0].err = errno;
errno = 0;
if (minval > maxval) {
error = INVALID;
} else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
error = TOOSMALL;
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
error = TOOLARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
ev[0].err = errno;
errno = 0;
if (minval > maxval) {
error = INVALID;
} else {
ll = strtoll(numstr, &ep, 10);
if (numstr == ep || *ep != '\0')
error = INVALID;
else if ((ll == LLONG_MIN && errno == ERANGE) || ll < minval)
error = TOOSMALL;
else if ((ll == LLONG_MAX && errno == ERANGE) || ll > maxval)
error = TOOLARGE;
}
if (errstrp != NULL)
*errstrp = ev[error].errstr;
errno = ev[error].err;
if (error)
ll = 0;
return ll;
return ll;
}
/*
@ -175,10 +262,10 @@ strtonum(const char *numstr, long long minval, long long maxval,
void *
reallocarray(void *optr, size_t nmemb, size_t size)
{
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
return NULL;
}
return realloc(optr, size * nmemb);
if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
nmemb > 0 && SIZE_MAX / nmemb < size) {
errno = ENOMEM;
return NULL;
}
return realloc(optr, size * nmemb);
}

2
util.h
View file

@ -52,6 +52,8 @@ void eunveil(const char *, const char *);
int timestamp(char *, size_t, time_t);
int esnprintf(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);
long long strtonum(const char *, long long, long long, const char **);