Compare commits
11 commits
Author | SHA1 | Date | |
---|---|---|---|
48db5eea1d | |||
66558aa615 | |||
d06c37fbd5 | |||
2a3e25dc6b | |||
e262166522 | |||
30ca105c66 | |||
eba491b4b5 | |||
bd51ff593d | |||
a22ac176ca | |||
5b6f8e5083 | |||
329dedc33b |
10 changed files with 773 additions and 197 deletions
137
.clang-format
Normal file
137
.clang-format
Normal 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
|
||||||
|
...
|
||||||
|
|
13
.drone.yml
13
.drone.yml
|
@ -7,3 +7,16 @@ 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
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
*.o
|
||||||
|
.gdb_history
|
||||||
|
config.h
|
||||||
|
quark
|
5
Makefile
5
Makefile
|
@ -4,11 +4,12 @@
|
||||||
|
|
||||||
include config.mk
|
include config.mk
|
||||||
|
|
||||||
COMPONENTS = data http sock util
|
COMPONENTS = data http sock util dirl
|
||||||
|
|
||||||
all: quark
|
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
|
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
100
README.md
|
@ -5,36 +5,94 @@ found at https://git.suckless.org/quark.
|
||||||
|
|
||||||
Quark is a small http server.
|
Quark is a small http server.
|
||||||
|
|
||||||
# Feature Patches
|
# DIRL
|
||||||
|
|
||||||
## Dirl: Customizable directory listing
|
dirl is a quark extension for customized directory listings.
|
||||||
[dirl](https://git.friedl.net/playground/suckless-quark/src/branch/dirlist) lets
|
|
||||||
you serve a fully customizable directory listing.
|
|
||||||
|
|
||||||
You can compile `dirl` from the `dirlist` branch, download a pre-compiled [musl
|
Per default dirl generates html for a directory listing like this:
|
||||||
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).
|
|
||||||
|
|
||||||
You can find an example deployment of [here](https://dirlist.friedl.net/). It
|
```html
|
||||||
uses the default template just with a custom css. You can define your own
|
<!DOCTYPE HTML PUBLIC " - // W3C//DTD HTML 3.2 Final//EN">
|
||||||
templates too for full customization. For details see the dirl
|
<!-- Header Section -->
|
||||||
[README.md](https://git.friedl.net/playground/suckless-quark/src/branch/dirlist/README.md).
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="style.css">
|
||||||
|
<title>Index of {uri}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Index of {uri}</h1>
|
||||||
|
<p><a href="..">↵ 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
|
<!-- Footer Section -->
|
||||||
When running [quark](http://tools.suckless.org/quark/) (#6606994) on my system
|
</table>
|
||||||
with `sudo ./quark -p 9763 -u <user> -g <group>` it dies with `./quark: fork:
|
<hr />
|
||||||
Resource temporarily unavailable` at `fork()`.
|
<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
|
# 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
|
||||||
|
|
111
data.c
111
data.c
|
@ -10,6 +10,7 @@
|
||||||
#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)
|
||||||
|
@ -25,64 +26,6 @@ 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[] = {
|
|
||||||
{ '&', "&" },
|
|
||||||
{ '<', "<" },
|
|
||||||
{ '>', ">" },
|
|
||||||
{ '"', """ },
|
|
||||||
{ '\'', "'" },
|
|
||||||
};
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
|
@ -90,48 +33,38 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* listing header (we use esc because sizeof(esc) >= PATH_MAX) */
|
/* read templates */
|
||||||
html_escape(res->uri, esc, MIN(PATH_MAX, sizeof(esc)));
|
struct dirl_templ templates = dirl_read_templ(res->uri);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* listing */
|
/* listing header */
|
||||||
|
if ((ret = dirl_header(fd, res, &templates))) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* entries */
|
||||||
for (i = 0; i < (size_t)dirlen; i++) {
|
for (i = 0; i < (size_t)dirlen; i++) {
|
||||||
/* skip hidden files, "." and ".." */
|
/* skip dirl special files */
|
||||||
if (e[i]->d_name[0] == '.') {
|
if(dirl_skip(e[i]->d_name)) {
|
||||||
continue;
|
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 */
|
/* listing footer */
|
||||||
if (dprintf(fd, "\n\t</body>\n</html>\n") < 0) {
|
if ((ret = dirl_footer(fd, &templates))) {
|
||||||
ret = S_REQUEST_TIMEOUT;
|
goto cleanup;
|
||||||
goto cleanup;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
cleanup:
|
||||||
while (dirlen--) {
|
while (dirlen--) {
|
||||||
|
|
250
dirl.c
Normal file
250
dirl.c
Normal 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[] = {
|
||||||
|
{ '&', "&" }, { '<', "<" }, { '>', ">" },
|
||||||
|
{ '"', """ }, { '\'', "'" },
|
||||||
|
};
|
||||||
|
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
91
dirl.h
Normal 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=\"..\">↵ 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
257
util.c
|
@ -20,107 +20,194 @@ 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
|
||||||
|
@ -131,39 +218,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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -175,10 +262,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
2
util.h
|
@ -52,6 +52,8 @@ 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 **);
|
||||||
|
|
Loading…
Reference in a new issue