Merge remote-tracking branch 'origin/dirlist'

This commit is contained in:
Armin Friedl 2020-09-01 22:49:39 +02:00
commit d3e3257464
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 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
View file

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

View file

@ -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
View file

@ -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="..">&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 <!-- 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

93
data.c
View file

@ -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[] = {
{ '&', "&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)
{ {
@ -90,46 +33,36 @@ 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>" /* listing header */
"<title>Index of %s</title></head>\n" if ((ret = dirl_header(fd, res, &templates))) {
"\t<body>\n\t\t<a href=\"..\">..</a>", return ret;
esc) < 0) {
ret = S_REQUEST_TIMEOUT;
goto cleanup;
} }
/* listing */ /* 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 */ /* entry line */
html_escape(e[i]->d_name, esc, sizeof(esc)); if ((ret = dirl_entry(fd, e[i], res, &templates))) {
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; 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;
} }

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 */

87
util.c
View file

@ -123,6 +123,93 @@ prepend(char *str, size_t size, const char *prefix)
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
#define TOOSMALL 2 #define TOOSMALL 2
#define TOOLARGE 3 #define TOOLARGE 3

2
util.h
View file

@ -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 **);