mirror of
https://git.zx2c4.com/cgit
synced 2025-08-09 04:15:52 +02:00
Compare commits
No commits in common. "master" and "v0.11.1" have entirely different histories.
76 changed files with 3293 additions and 4128 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
@ -1,3 +1,3 @@
|
|||
[submodule "git"]
|
||||
url = https://git.kernel.org/pub/scm/git/git.git
|
||||
url = git://git.kernel.org/pub/scm/git/git.git
|
||||
path = git
|
||||
|
|
4
.mailmap
4
.mailmap
|
@ -5,6 +5,6 @@ Lars Hjemli <hjemli@gmail.com> <larsh@hal-2004.(none)>
|
|||
Lars Hjemli <hjemli@gmail.com> <larsh@hatman.(none)>
|
||||
Lars Hjemli <hjemli@gmail.com> <larsh@slackbox.hjemli.net>
|
||||
Lars Hjemli <hjemli@gmail.com> <larsh@slaptop.hjemli.net>
|
||||
Lukas Fleischer <lfleischer@lfos.de> <cgit@cryptocrack.de>
|
||||
Lukas Fleischer <lfleischer@lfos.de> <info@cryptocrack.de>
|
||||
Lukas Fleischer <cgit@cryptocrack.de> <cgit@crytocrack.de>
|
||||
Lukas Fleischer <cgit@cryptocrack.de> <info@cryptocrack.de>
|
||||
Stefan Bühler <source@stbuehler.de> <lighttpd@stbuehler.de>
|
||||
|
|
39
COPYING
39
COPYING
|
@ -1,12 +1,12 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
|
@ -15,7 +15,7 @@ software--to make sure the software is free for all its users. This
|
|||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
|
@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all.
|
|||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
|
@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions:
|
|||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
|
@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent
|
|||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
|
@ -225,7 +225,7 @@ impose that choice.
|
|||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
|
@ -255,7 +255,7 @@ make exceptions for this. Our decision will be guided by the two goals
|
|||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
|
@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
|||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
|
@ -303,9 +303,10 @@ the "copyright" line and a pointer to where the full notice is found.
|
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
|
@ -335,5 +336,5 @@ necessary. Here is a sample; alter the names:
|
|||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
|
|
26
Makefile
26
Makefile
|
@ -1,6 +1,6 @@
|
|||
all::
|
||||
|
||||
CGIT_VERSION = v1.2.3
|
||||
CGIT_VERSION = v0.11.1
|
||||
CGIT_SCRIPT_NAME = cgit.cgi
|
||||
CGIT_SCRIPT_PATH = /var/www/htdocs/cgit
|
||||
CGIT_DATA_PATH = $(CGIT_SCRIPT_PATH)
|
||||
|
@ -14,8 +14,8 @@ htmldir = $(docdir)
|
|||
pdfdir = $(docdir)
|
||||
mandir = $(prefix)/share/man
|
||||
SHA1_HEADER = <openssl/sha.h>
|
||||
GIT_VER = 2.46.0
|
||||
GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.xz
|
||||
GIT_VER = 2.3.1
|
||||
GIT_URL = https://www.kernel.org/pub/software/scm/git/git-$(GIT_VER).tar.gz
|
||||
INSTALL = install
|
||||
COPYTREE = cp -r
|
||||
MAN5_TXT = $(wildcard *.5.txt)
|
||||
|
@ -24,12 +24,6 @@ DOC_MAN5 = $(patsubst %.txt,%,$(MAN5_TXT))
|
|||
DOC_HTML = $(patsubst %.txt,%.html,$(MAN_TXT))
|
||||
DOC_PDF = $(patsubst %.txt,%.pdf,$(MAN_TXT))
|
||||
|
||||
ASCIIDOC = asciidoc
|
||||
ASCIIDOC_EXTRA =
|
||||
ASCIIDOC_HTML = xhtml11
|
||||
ASCIIDOC_COMMON = $(ASCIIDOC) $(ASCIIDOC_EXTRA)
|
||||
TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
|
||||
|
||||
# Define NO_C99_FORMAT if your formatted IO functions (printf/scanf et.al.)
|
||||
# do not support the 'size specifiers' introduced by C99, namely ll, hh,
|
||||
# j, z, t. (representing long long int, char, intmax_t, size_t, ptrdiff_t).
|
||||
|
@ -39,7 +33,6 @@ TXT_TO_HTML = $(ASCIIDOC_COMMON) -b $(ASCIIDOC_HTML)
|
|||
|
||||
#-include config.mak
|
||||
|
||||
-include git/config.mak.uname
|
||||
#
|
||||
# Let the user override the above settings.
|
||||
#
|
||||
|
@ -54,7 +47,7 @@ export CGIT_VERSION CGIT_SCRIPT_NAME CGIT_SCRIPT_PATH CGIT_DATA_PATH CGIT_CONFIG
|
|||
QUIET_SUBDIR0 = +$(MAKE) -C # space to separate -C and subdir
|
||||
QUIET_SUBDIR1 =
|
||||
|
||||
ifneq ($(findstring w,$(MAKEFLAGS)),w)
|
||||
ifneq ($(findstring $(MAKEFLAGS),w),w)
|
||||
PRINT_DIR = --no-print-directory
|
||||
else # "make -w"
|
||||
NO_SUBDIR = :
|
||||
|
@ -75,9 +68,6 @@ all:: cgit
|
|||
cgit:
|
||||
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk ../cgit $(EXTRA_GIT_TARGETS) NO_CURL=1
|
||||
|
||||
sparse:
|
||||
$(QUIET_SUBDIR0)git $(QUIET_SUBDIR1) -f ../cgit.mk NO_CURL=1 cgit-sparse
|
||||
|
||||
test:
|
||||
@$(MAKE) --no-print-directory cgit EXTRA_GIT_TARGETS=all
|
||||
$(QUIET_SUBDIR0)tests $(QUIET_SUBDIR1) all
|
||||
|
@ -87,12 +77,11 @@ install: all
|
|||
$(INSTALL) -m 0755 cgit $(DESTDIR)$(CGIT_SCRIPT_PATH)/$(CGIT_SCRIPT_NAME)
|
||||
$(INSTALL) -m 0755 -d $(DESTDIR)$(CGIT_DATA_PATH)
|
||||
$(INSTALL) -m 0644 cgit.css $(DESTDIR)$(CGIT_DATA_PATH)/cgit.css
|
||||
$(INSTALL) -m 0644 cgit.js $(DESTDIR)$(CGIT_DATA_PATH)/cgit.js
|
||||
$(INSTALL) -m 0644 cgit.png $(DESTDIR)$(CGIT_DATA_PATH)/cgit.png
|
||||
$(INSTALL) -m 0644 favicon.ico $(DESTDIR)$(CGIT_DATA_PATH)/favicon.ico
|
||||
$(INSTALL) -m 0644 robots.txt $(DESTDIR)$(CGIT_DATA_PATH)/robots.txt
|
||||
$(INSTALL) -m 0755 -d $(DESTDIR)$(filterdir)
|
||||
$(COPYTREE) filters/* $(DESTDIR)$(filterdir)
|
||||
$(COPYTREE) filters/* $(DESTDIR)$(filterdir)
|
||||
|
||||
install-doc: install-man install-html install-pdf
|
||||
|
||||
|
@ -141,8 +130,7 @@ doc-pdf: $(DOC_PDF)
|
|||
a2x -f manpage $<
|
||||
|
||||
$(DOC_HTML): %.html : %.txt
|
||||
$(TXT_TO_HTML) -o $@+ $< && \
|
||||
mv $@+ $@
|
||||
a2x -f xhtml --stylesheet=cgit-doc.css $<
|
||||
|
||||
$(DOC_PDF): %.pdf : %.txt
|
||||
a2x -f pdf cgitrc.5.txt
|
||||
|
@ -158,7 +146,7 @@ clean-doc:
|
|||
$(RM) cgitrc.5 cgitrc.5.html cgitrc.5.pdf cgitrc.5.xml cgitrc.5.fo
|
||||
|
||||
get-git:
|
||||
curl -L $(GIT_URL) | tar -xJf - && rm -rf git && mv git-$(GIT_VER) git
|
||||
curl -L $(GIT_URL) | tar -xzf - && rm -rf git && mv git-$(GIT_VER) git
|
||||
|
||||
tags:
|
||||
$(QUIET_TAGS)find . -name '*.[ch]' | xargs ctags
|
||||
|
|
4
README
4
README
|
@ -92,8 +92,8 @@ the HTTP headers `Modified` and `Expires`.
|
|||
Online presence
|
||||
---------------
|
||||
|
||||
* The cgit homepage is hosted by cgit at <https://git.zx2c4.com/cgit/about/>
|
||||
* The cgit homepage is hosted by cgit at <http://git.zx2c4.com/cgit/about/>
|
||||
|
||||
* Patches, bug reports, discussions and support should go to the cgit
|
||||
mailing list: <cgit@lists.zx2c4.com>. To sign up, visit
|
||||
<https://lists.zx2c4.com/mailman/listinfo/cgit>
|
||||
<http://lists.zx2c4.com/mailman/listinfo/cgit>
|
||||
|
|
97
cache.c
97
cache.c
|
@ -13,23 +13,22 @@
|
|||
*
|
||||
*/
|
||||
|
||||
#include "cgit.h"
|
||||
#include "cache.h"
|
||||
#include "html.h"
|
||||
#ifdef HAVE_LINUX_SENDFILE
|
||||
#include <sys/sendfile.h>
|
||||
#endif
|
||||
#include "cgit.h"
|
||||
#include "cache.h"
|
||||
#include "html.h"
|
||||
|
||||
#define CACHE_BUFSIZE (1024 * 4)
|
||||
|
||||
struct cache_slot {
|
||||
const char *key;
|
||||
size_t keylen;
|
||||
int keylen;
|
||||
int ttl;
|
||||
cache_fill_fn fn;
|
||||
int cache_fd;
|
||||
int lock_fd;
|
||||
int stdout_fd;
|
||||
const char *cache_name;
|
||||
const char *lock_name;
|
||||
int match;
|
||||
|
@ -45,7 +44,7 @@ struct cache_slot {
|
|||
static int open_slot(struct cache_slot *slot)
|
||||
{
|
||||
char *bufz;
|
||||
ssize_t bufkeylen = -1;
|
||||
int bufkeylen = -1;
|
||||
|
||||
slot->cache_fd = open(slot->cache_name, O_RDONLY);
|
||||
if (slot->cache_fd == -1)
|
||||
|
@ -62,9 +61,8 @@ static int open_slot(struct cache_slot *slot)
|
|||
if (bufz)
|
||||
bufkeylen = bufz - slot->buf;
|
||||
|
||||
if (slot->key)
|
||||
slot->match = bufkeylen == slot->keylen &&
|
||||
!memcmp(slot->key, slot->buf, bufkeylen + 1);
|
||||
slot->match = bufkeylen == slot->keylen &&
|
||||
!memcmp(slot->key, slot->buf, bufkeylen + 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -85,45 +83,40 @@ static int close_slot(struct cache_slot *slot)
|
|||
/* Print the content of the active cache slot (but skip the key). */
|
||||
static int print_slot(struct cache_slot *slot)
|
||||
{
|
||||
off_t off;
|
||||
#ifdef HAVE_LINUX_SENDFILE
|
||||
off_t size;
|
||||
#endif
|
||||
off_t start_off;
|
||||
int ret;
|
||||
|
||||
off = slot->keylen + 1;
|
||||
|
||||
#ifdef HAVE_LINUX_SENDFILE
|
||||
size = slot->cache_st.st_size;
|
||||
start_off = slot->keylen + 1;
|
||||
|
||||
do {
|
||||
ssize_t ret;
|
||||
ret = sendfile(STDOUT_FILENO, slot->cache_fd, &off, size - off);
|
||||
ret = sendfile(STDOUT_FILENO, slot->cache_fd, &start_off,
|
||||
slot->cache_st.st_size - start_off);
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN || errno == EINTR)
|
||||
continue;
|
||||
/* Fall back to read/write on EINVAL or ENOSYS */
|
||||
if (errno == EINVAL || errno == ENOSYS)
|
||||
break;
|
||||
return errno;
|
||||
}
|
||||
if (off == size)
|
||||
return 0;
|
||||
return 0;
|
||||
} while (1);
|
||||
#endif
|
||||
#else
|
||||
ssize_t i, j;
|
||||
|
||||
if (lseek(slot->cache_fd, off, SEEK_SET) != off)
|
||||
i = lseek(slot->cache_fd, slot->keylen + 1, SEEK_SET);
|
||||
if (i != slot->keylen + 1)
|
||||
return errno;
|
||||
|
||||
do {
|
||||
ssize_t ret;
|
||||
ret = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
|
||||
if (ret < 0)
|
||||
return errno;
|
||||
if (ret == 0)
|
||||
return 0;
|
||||
if (write_in_full(STDOUT_FILENO, slot->buf, ret) < 0)
|
||||
return errno;
|
||||
} while (1);
|
||||
i = j = xread(slot->cache_fd, slot->buf, sizeof(slot->buf));
|
||||
if (i > 0)
|
||||
j = xwrite(STDOUT_FILENO, slot->buf, i);
|
||||
} while (i > 0 && j == i);
|
||||
|
||||
if (i < 0 || j != i)
|
||||
return errno;
|
||||
else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Check if the slot has expired */
|
||||
|
@ -203,13 +196,6 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
|
|||
else
|
||||
err = unlink(slot->lock_name);
|
||||
|
||||
/* Restore stdout and close the temporary FD. */
|
||||
if (slot->stdout_fd >= 0) {
|
||||
dup2(slot->stdout_fd, STDOUT_FILENO);
|
||||
close(slot->stdout_fd);
|
||||
slot->stdout_fd = -1;
|
||||
}
|
||||
|
||||
if (err)
|
||||
return errno;
|
||||
|
||||
|
@ -221,9 +207,11 @@ static int unlock_slot(struct cache_slot *slot, int replace_old_slot)
|
|||
*/
|
||||
static int fill_slot(struct cache_slot *slot)
|
||||
{
|
||||
int tmp;
|
||||
|
||||
/* Preserve stdout */
|
||||
slot->stdout_fd = dup(STDOUT_FILENO);
|
||||
if (slot->stdout_fd == -1)
|
||||
tmp = dup(STDOUT_FILENO);
|
||||
if (tmp == -1)
|
||||
return errno;
|
||||
|
||||
/* Redirect stdout to lockfile */
|
||||
|
@ -233,14 +221,18 @@ static int fill_slot(struct cache_slot *slot)
|
|||
/* Generate cache content */
|
||||
slot->fn();
|
||||
|
||||
/* Make sure any buffered data is flushed to the file */
|
||||
if (fflush(stdout))
|
||||
return errno;
|
||||
|
||||
/* update stat info */
|
||||
if (fstat(slot->lock_fd, &slot->cache_st))
|
||||
return errno;
|
||||
|
||||
/* Restore stdout */
|
||||
if (dup2(tmp, STDOUT_FILENO) == -1)
|
||||
return errno;
|
||||
|
||||
/* Close the temporary filedescriptor */
|
||||
if (close(tmp))
|
||||
return errno;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -313,7 +305,7 @@ static int process_slot(struct cache_slot *slot)
|
|||
/* If the cache slot does not exist (or its key doesn't match the
|
||||
* current key), lets try to create a new cache slot for this
|
||||
* request. If this fails (for whatever reason), lets just generate
|
||||
* the content without caching it and fool the caller to believe
|
||||
* the content without caching it and fool the caller to belive
|
||||
* everything worked out (but print a warning on stdout).
|
||||
*/
|
||||
|
||||
|
@ -388,7 +380,6 @@ int cache_process(int size, const char *path, const char *key, int ttl,
|
|||
strbuf_addstr(&lockname, ".lock");
|
||||
slot.fn = fn;
|
||||
slot.ttl = ttl;
|
||||
slot.stdout_fd = -1;
|
||||
slot.cache_name = filename.buf;
|
||||
slot.lock_name = lockname.buf;
|
||||
slot.key = key;
|
||||
|
@ -406,12 +397,12 @@ int cache_process(int size, const char *path, const char *key, int ttl,
|
|||
static char *sprintftime(const char *format, time_t time)
|
||||
{
|
||||
static char buf[64];
|
||||
struct tm tm;
|
||||
struct tm *tm;
|
||||
|
||||
if (!time)
|
||||
return NULL;
|
||||
gmtime_r(&time, &tm);
|
||||
strftime(buf, sizeof(buf)-1, format, &tm);
|
||||
tm = gmtime(&time);
|
||||
strftime(buf, sizeof(buf)-1, format, tm);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
@ -420,7 +411,7 @@ int cache_ls(const char *path)
|
|||
DIR *dir;
|
||||
struct dirent *ent;
|
||||
int err = 0;
|
||||
struct cache_slot slot = { NULL };
|
||||
struct cache_slot slot = { 0 };
|
||||
struct strbuf fullname = STRBUF_INIT;
|
||||
size_t prefixlen;
|
||||
|
||||
|
|
2
cache.h
2
cache.h
|
@ -19,7 +19,7 @@ typedef void (*cache_fill_fn)(void);
|
|||
* fn content generator function for this key
|
||||
*
|
||||
* Return value
|
||||
* 0 indicates success, everything else is an error
|
||||
* 0 indicates success, everyting else is an error
|
||||
*/
|
||||
extern int cache_process(int size, const char *path, const char *key, int ttl,
|
||||
cache_fill_fn fn);
|
||||
|
|
3
cgit-doc.css
Normal file
3
cgit-doc.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
div.variablelist dt {
|
||||
margin-top: 1em;
|
||||
}
|
265
cgit.c
265
cgit.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "cache.h"
|
||||
#include "cmd.h"
|
||||
|
@ -21,21 +19,11 @@
|
|||
|
||||
const char *cgit_version = CGIT_VERSION;
|
||||
|
||||
__attribute__((constructor))
|
||||
static void constructor_environment()
|
||||
{
|
||||
/* Do not look in /etc/ for gitconfig and gitattributes. */
|
||||
setenv("GIT_CONFIG_NOSYSTEM", "1", 1);
|
||||
setenv("GIT_ATTR_NOSYSTEM", "1", 1);
|
||||
unsetenv("HOME");
|
||||
unsetenv("XDG_CONFIG_HOME");
|
||||
}
|
||||
|
||||
static void add_mimetype(const char *name, const char *value)
|
||||
{
|
||||
struct string_list_item *item;
|
||||
|
||||
item = string_list_insert(&ctx.cfg.mimetypes, name);
|
||||
item = string_list_insert(&ctx.cfg.mimetypes, xstrdup(name));
|
||||
item->util = xstrdup(value);
|
||||
}
|
||||
|
||||
|
@ -43,7 +31,6 @@ static void process_cached_repolist(const char *path);
|
|||
|
||||
static void repo_config(struct cgit_repo *repo, const char *name, const char *value)
|
||||
{
|
||||
const char *path;
|
||||
struct string_list_item *item;
|
||||
|
||||
if (!strcmp(name, "name"))
|
||||
|
@ -54,16 +41,10 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
|
|||
repo->desc = xstrdup(value);
|
||||
else if (!strcmp(name, "owner"))
|
||||
repo->owner = xstrdup(value);
|
||||
else if (!strcmp(name, "homepage"))
|
||||
repo->homepage = xstrdup(value);
|
||||
else if (!strcmp(name, "defbranch"))
|
||||
repo->defbranch = xstrdup(value);
|
||||
else if (!strcmp(name, "extra-head-content"))
|
||||
repo->extra_head_content = xstrdup(value);
|
||||
else if (!strcmp(name, "snapshots"))
|
||||
repo->snapshots = ctx.cfg.snapshots & cgit_parse_snapshots_mask(value);
|
||||
else if (!strcmp(name, "enable-blame"))
|
||||
repo->enable_blame = atoi(value);
|
||||
else if (!strcmp(name, "enable-commit-graph"))
|
||||
repo->enable_commit_graph = atoi(value);
|
||||
else if (!strcmp(name, "enable-log-filecount"))
|
||||
|
@ -74,8 +55,6 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
|
|||
repo->enable_remote_branches = atoi(value);
|
||||
else if (!strcmp(name, "enable-subject-links"))
|
||||
repo->enable_subject_links = atoi(value);
|
||||
else if (!strcmp(name, "enable-html-serving"))
|
||||
repo->enable_html_serving = atoi(value);
|
||||
else if (!strcmp(name, "branch-sort")) {
|
||||
if (!strcmp(value, "age"))
|
||||
repo->branch_sort = 1;
|
||||
|
@ -90,13 +69,11 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
|
|||
repo->max_stats = cgit_find_stats_period(value, NULL);
|
||||
else if (!strcmp(name, "module-link"))
|
||||
repo->module_link= xstrdup(value);
|
||||
else if (skip_prefix(name, "module-link.", &path)) {
|
||||
item = string_list_append(&repo->submodules, xstrdup(path));
|
||||
else if (starts_with(name, "module-link.")) {
|
||||
item = string_list_append(&repo->submodules, xstrdup(name + 12));
|
||||
item->util = xstrdup(value);
|
||||
} else if (!strcmp(name, "section"))
|
||||
repo->section = xstrdup(value);
|
||||
else if (!strcmp(name, "snapshot-prefix"))
|
||||
repo->snapshot_prefix = xstrdup(value);
|
||||
else if (!strcmp(name, "readme") && value != NULL) {
|
||||
if (repo->readme.items == ctx.cfg.readme.items)
|
||||
memset(&repo->readme, 0, sizeof(repo->readme));
|
||||
|
@ -105,10 +82,6 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
|
|||
repo->logo = xstrdup(value);
|
||||
else if (!strcmp(name, "logo-link") && value != NULL)
|
||||
repo->logo_link = xstrdup(value);
|
||||
else if (!strcmp(name, "hide"))
|
||||
repo->hide = atoi(value);
|
||||
else if (!strcmp(name, "ignore"))
|
||||
repo->ignore = atoi(value);
|
||||
else if (ctx.cfg.enable_filter_overrides) {
|
||||
if (!strcmp(name, "about-filter"))
|
||||
repo->about_filter = cgit_new_filter(value, ABOUT);
|
||||
|
@ -120,22 +93,24 @@ static void repo_config(struct cgit_repo *repo, const char *name, const char *va
|
|||
repo->email_filter = cgit_new_filter(value, EMAIL);
|
||||
else if (!strcmp(name, "owner-filter"))
|
||||
repo->owner_filter = cgit_new_filter(value, OWNER);
|
||||
} else if (!strcmp(name, "hide")) {
|
||||
repo->hide = atoi(value);
|
||||
} else if (!strcmp(name, "ignore")) {
|
||||
repo->ignore = atoi(value);
|
||||
}
|
||||
}
|
||||
|
||||
static void config_cb(const char *name, const char *value)
|
||||
{
|
||||
const char *arg;
|
||||
|
||||
if (!strcmp(name, "section"))
|
||||
if (!strcmp(name, "section") || !strcmp(name, "repo.group"))
|
||||
ctx.cfg.section = xstrdup(value);
|
||||
else if (!strcmp(name, "repo.url"))
|
||||
ctx.repo = cgit_add_repo(value);
|
||||
else if (ctx.repo && !strcmp(name, "repo.path"))
|
||||
ctx.repo->path = trim_end(value, '/');
|
||||
else if (ctx.repo && skip_prefix(name, "repo.", &arg))
|
||||
repo_config(ctx.repo, arg, value);
|
||||
else if (!strcmp(name, "readme"))
|
||||
else if (ctx.repo && starts_with(name, "repo."))
|
||||
repo_config(ctx.repo, name + 5, value);
|
||||
else if (!strcmp(name, "readme") && value != NULL)
|
||||
string_list_append(&ctx.cfg.readme, xstrdup(value));
|
||||
else if (!strcmp(name, "root-title"))
|
||||
ctx.cfg.root_title = xstrdup(value);
|
||||
|
@ -144,9 +119,7 @@ static void config_cb(const char *name, const char *value)
|
|||
else if (!strcmp(name, "root-readme"))
|
||||
ctx.cfg.root_readme = xstrdup(value);
|
||||
else if (!strcmp(name, "css"))
|
||||
string_list_append(&ctx.cfg.css, xstrdup(value));
|
||||
else if (!strcmp(name, "js"))
|
||||
string_list_append(&ctx.cfg.js, xstrdup(value));
|
||||
ctx.cfg.css = xstrdup(value);
|
||||
else if (!strcmp(name, "favicon"))
|
||||
ctx.cfg.favicon = xstrdup(value);
|
||||
else if (!strcmp(name, "footer"))
|
||||
|
@ -157,14 +130,20 @@ static void config_cb(const char *name, const char *value)
|
|||
ctx.cfg.header = xstrdup(value);
|
||||
else if (!strcmp(name, "logo"))
|
||||
ctx.cfg.logo = xstrdup(value);
|
||||
else if (!strcmp(name, "index-header"))
|
||||
ctx.cfg.index_header = xstrdup(value);
|
||||
else if (!strcmp(name, "index-info"))
|
||||
ctx.cfg.index_info = xstrdup(value);
|
||||
else if (!strcmp(name, "logo-link"))
|
||||
ctx.cfg.logo_link = xstrdup(value);
|
||||
else if (!strcmp(name, "module-link"))
|
||||
ctx.cfg.module_link = xstrdup(value);
|
||||
else if (!strcmp(name, "strict-export"))
|
||||
ctx.cfg.strict_export = xstrdup(value);
|
||||
else if (!strcmp(name, "virtual-root"))
|
||||
else if (!strcmp(name, "virtual-root")) {
|
||||
ctx.cfg.virtual_root = ensure_end(value, '/');
|
||||
} else if (!strcmp(name, "nocache"))
|
||||
ctx.cfg.nocache = atoi(value);
|
||||
else if (!strcmp(name, "noplainemail"))
|
||||
ctx.cfg.noplainemail = atoi(value);
|
||||
else if (!strcmp(name, "noheader"))
|
||||
|
@ -173,16 +152,12 @@ static void config_cb(const char *name, const char *value)
|
|||
ctx.cfg.snapshots = cgit_parse_snapshots_mask(value);
|
||||
else if (!strcmp(name, "enable-filter-overrides"))
|
||||
ctx.cfg.enable_filter_overrides = atoi(value);
|
||||
else if (!strcmp(name, "enable-follow-links"))
|
||||
ctx.cfg.enable_follow_links = atoi(value);
|
||||
else if (!strcmp(name, "enable-http-clone"))
|
||||
ctx.cfg.enable_http_clone = atoi(value);
|
||||
else if (!strcmp(name, "enable-index-links"))
|
||||
ctx.cfg.enable_index_links = atoi(value);
|
||||
else if (!strcmp(name, "enable-index-owner"))
|
||||
ctx.cfg.enable_index_owner = atoi(value);
|
||||
else if (!strcmp(name, "enable-blame"))
|
||||
ctx.cfg.enable_blame = atoi(value);
|
||||
else if (!strcmp(name, "enable-commit-graph"))
|
||||
ctx.cfg.enable_commit_graph = atoi(value);
|
||||
else if (!strcmp(name, "enable-log-filecount"))
|
||||
|
@ -193,8 +168,6 @@ static void config_cb(const char *name, const char *value)
|
|||
ctx.cfg.enable_remote_branches = atoi(value);
|
||||
else if (!strcmp(name, "enable-subject-links"))
|
||||
ctx.cfg.enable_subject_links = atoi(value);
|
||||
else if (!strcmp(name, "enable-html-serving"))
|
||||
ctx.cfg.enable_html_serving = atoi(value);
|
||||
else if (!strcmp(name, "enable-tree-linenumbers"))
|
||||
ctx.cfg.enable_tree_linenumbers = atoi(value);
|
||||
else if (!strcmp(name, "enable-git-config"))
|
||||
|
@ -241,16 +214,14 @@ static void config_cb(const char *name, const char *value)
|
|||
ctx.cfg.max_repodesc_len = atoi(value);
|
||||
else if (!strcmp(name, "max-blob-size"))
|
||||
ctx.cfg.max_blob_size = atoi(value);
|
||||
else if (!strcmp(name, "max-repo-count")) {
|
||||
else if (!strcmp(name, "max-repo-count"))
|
||||
ctx.cfg.max_repo_count = atoi(value);
|
||||
if (ctx.cfg.max_repo_count <= 0)
|
||||
ctx.cfg.max_repo_count = INT_MAX;
|
||||
} else if (!strcmp(name, "max-commit-count"))
|
||||
else if (!strcmp(name, "max-commit-count"))
|
||||
ctx.cfg.max_commit_count = atoi(value);
|
||||
else if (!strcmp(name, "project-list"))
|
||||
ctx.cfg.project_list = xstrdup(expand_macros(value));
|
||||
else if (!strcmp(name, "scan-path"))
|
||||
if (ctx.cfg.cache_size)
|
||||
if (!ctx.cfg.nocache && ctx.cfg.cache_size)
|
||||
process_cached_repolist(expand_macros(value));
|
||||
else if (ctx.cfg.project_list)
|
||||
scan_projects(expand_macros(value),
|
||||
|
@ -301,8 +272,8 @@ static void config_cb(const char *name, const char *value)
|
|||
ctx.cfg.branch_sort = 1;
|
||||
if (!strcmp(value, "name"))
|
||||
ctx.cfg.branch_sort = 0;
|
||||
} else if (skip_prefix(name, "mimetype.", &arg))
|
||||
add_mimetype(arg, value);
|
||||
} else if (starts_with(name, "mimetype."))
|
||||
add_mimetype(name + 9, value);
|
||||
else if (!strcmp(name, "include"))
|
||||
parse_configfile(expand_macros(value), config_cb);
|
||||
}
|
||||
|
@ -330,17 +301,19 @@ static void querystring_cb(const char *name, const char *value)
|
|||
ctx.qry.head = xstrdup(value);
|
||||
ctx.qry.has_symref = 1;
|
||||
} else if (!strcmp(name, "id")) {
|
||||
ctx.qry.oid = xstrdup(value);
|
||||
ctx.qry.has_oid = 1;
|
||||
ctx.qry.sha1 = xstrdup(value);
|
||||
ctx.qry.has_sha1 = 1;
|
||||
} else if (!strcmp(name, "id2")) {
|
||||
ctx.qry.oid2 = xstrdup(value);
|
||||
ctx.qry.has_oid = 1;
|
||||
ctx.qry.sha2 = xstrdup(value);
|
||||
ctx.qry.has_sha1 = 1;
|
||||
} else if (!strcmp(name, "ofs")) {
|
||||
ctx.qry.ofs = atoi(value);
|
||||
} else if (!strcmp(name, "path")) {
|
||||
ctx.qry.path = trim_end(value, '/');
|
||||
} else if (!strcmp(name, "name")) {
|
||||
ctx.qry.name = xstrdup(value);
|
||||
} else if (!strcmp(name, "mimetype")) {
|
||||
ctx.qry.mimetype = xstrdup(value);
|
||||
} else if (!strcmp(name, "s")) {
|
||||
ctx.qry.sort = xstrdup(value);
|
||||
} else if (!strcmp(name, "showmsg")) {
|
||||
|
@ -360,8 +333,6 @@ static void querystring_cb(const char *name, const char *value)
|
|||
ctx.qry.context = atoi(value);
|
||||
} else if (!strcmp(name, "ignorews")) {
|
||||
ctx.qry.ignorews = atoi(value);
|
||||
} else if (!strcmp(name, "follow")) {
|
||||
ctx.qry.follow = atoi(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,6 +340,7 @@ static void prepare_context(void)
|
|||
{
|
||||
memset(&ctx, 0, sizeof(ctx));
|
||||
ctx.cfg.agefile = "info/web/last-modified";
|
||||
ctx.cfg.nocache = 0;
|
||||
ctx.cfg.cache_size = 0;
|
||||
ctx.cfg.cache_max_create_time = 5;
|
||||
ctx.cfg.cache_root = CGIT_CACHE_ROOT;
|
||||
|
@ -382,6 +354,7 @@ static void prepare_context(void)
|
|||
ctx.cfg.case_sensitive_sort = 1;
|
||||
ctx.cfg.branch_sort = 0;
|
||||
ctx.cfg.commit_sort = 0;
|
||||
ctx.cfg.css = "/cgit.css";
|
||||
ctx.cfg.logo = "/cgit.png";
|
||||
ctx.cfg.favicon = "/favicon.ico";
|
||||
ctx.cfg.local_time = 0;
|
||||
|
@ -433,7 +406,7 @@ static void prepare_context(void)
|
|||
ctx.page.modified = time(NULL);
|
||||
ctx.page.expires = ctx.page.modified;
|
||||
ctx.page.etag = NULL;
|
||||
string_list_init_dup(&ctx.cfg.mimetypes);
|
||||
memset(&ctx.cfg.mimetypes, 0, sizeof(struct string_list));
|
||||
if (ctx.env.script_name)
|
||||
ctx.cfg.script_name = xstrdup(ctx.env.script_name);
|
||||
if (ctx.env.query_string)
|
||||
|
@ -448,7 +421,7 @@ struct refmatch {
|
|||
int match;
|
||||
};
|
||||
|
||||
static int find_current_ref(const char *refname, const struct object_id *oid,
|
||||
static int find_current_ref(const char *refname, const unsigned char *sha1,
|
||||
int flags, void *cb_data)
|
||||
{
|
||||
struct refmatch *info;
|
||||
|
@ -475,8 +448,7 @@ static char *find_default_branch(struct cgit_repo *repo)
|
|||
info.req_ref = repo->defbranch;
|
||||
info.first_ref = NULL;
|
||||
info.match = 0;
|
||||
refs_for_each_branch_ref(get_main_ref_store(the_repository),
|
||||
find_current_ref, &info);
|
||||
for_each_branch_ref(find_current_ref, &info);
|
||||
if (info.match)
|
||||
ref = info.req_ref;
|
||||
else
|
||||
|
@ -490,16 +462,14 @@ static char *find_default_branch(struct cgit_repo *repo)
|
|||
|
||||
static char *guess_defbranch(void)
|
||||
{
|
||||
const char *ref, *refname;
|
||||
struct object_id oid;
|
||||
const char *ref;
|
||||
unsigned char sha1[20];
|
||||
|
||||
ref = refs_resolve_ref_unsafe(get_main_ref_store(the_repository),
|
||||
"HEAD", 0, &oid, NULL);
|
||||
if (!ref || !skip_prefix(ref, "refs/heads/", &refname))
|
||||
ref = resolve_ref_unsafe("HEAD", 0, sha1, NULL);
|
||||
if (!ref || !starts_with(ref, "refs/heads/"))
|
||||
return "master";
|
||||
return xstrdup(refname);
|
||||
return xstrdup(ref + 11);
|
||||
}
|
||||
|
||||
/* The caller must free filename and ref after calling this. */
|
||||
static inline void parse_readme(const char *readme, char **filename, char **ref, struct cgit_repo *repo)
|
||||
{
|
||||
|
@ -514,11 +484,9 @@ static inline void parse_readme(const char *readme, char **filename, char **ref,
|
|||
/* Check if the readme is tracked in the git repo. */
|
||||
colon = strchr(readme, ':');
|
||||
if (colon && strlen(colon) > 1) {
|
||||
/* If it starts with a colon, we want to use head given
|
||||
* from query or the default branch */
|
||||
if (colon == readme && ctx.qry.head)
|
||||
*ref = xstrdup(ctx.qry.head);
|
||||
else if (colon == readme && repo->defbranch)
|
||||
/* If it starts with a colon, we want to use
|
||||
* the default branch */
|
||||
if (colon == readme && repo->defbranch)
|
||||
*ref = xstrdup(repo->defbranch);
|
||||
else
|
||||
*ref = xstrndup(readme, colon - readme);
|
||||
|
@ -579,22 +547,26 @@ static void print_no_repo_clone_urls(const char *url)
|
|||
html("</a></td></tr>\n");
|
||||
}
|
||||
|
||||
static void prepare_repo_env(int *nongit)
|
||||
static int prepare_repo_cmd(void)
|
||||
{
|
||||
unsigned char sha1[20];
|
||||
int nongit = 0;
|
||||
int rc;
|
||||
|
||||
/* The path to the git repository. */
|
||||
setenv("GIT_DIR", ctx.repo->path, 1);
|
||||
|
||||
/* Do not look in /etc/ for gitconfig and gitattributes. */
|
||||
setenv("GIT_CONFIG_NOSYSTEM", "1", 1);
|
||||
setenv("GIT_ATTR_NOSYSTEM", "1", 1);
|
||||
unsetenv("HOME");
|
||||
unsetenv("XDG_CONFIG_HOME");
|
||||
|
||||
/* Setup the git directory and initialize the notes system. Both of these
|
||||
* load local configuration from the git repository, so we do them both while
|
||||
* the HOME variables are unset. */
|
||||
setup_git_directory_gently(nongit);
|
||||
load_display_notes(NULL);
|
||||
}
|
||||
|
||||
static int prepare_repo_cmd(int nongit)
|
||||
{
|
||||
struct object_id oid;
|
||||
int rc;
|
||||
setup_git_directory_gently(&nongit);
|
||||
init_display_notes(NULL);
|
||||
|
||||
if (nongit) {
|
||||
const char *name = ctx.repo->name;
|
||||
|
@ -627,7 +599,6 @@ static int prepare_repo_cmd(int nongit)
|
|||
cgit_print_error("Repository seems to be empty");
|
||||
if (!strcmp(ctx.qry.page, "summary")) {
|
||||
html("<table class='list'><tr class='nohover'><td> </td></tr><tr class='nohover'><th class='left'>Clone</th></tr>\n");
|
||||
cgit_prepare_repo_env(ctx.repo);
|
||||
cgit_add_clone_urls(print_no_repo_clone_urls);
|
||||
html("</table>\n");
|
||||
}
|
||||
|
@ -635,12 +606,17 @@ static int prepare_repo_cmd(int nongit)
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (repo_get_oid(the_repository, ctx.qry.head, &oid)) {
|
||||
char *old_head = ctx.qry.head;
|
||||
ctx.qry.head = xstrdup(ctx.repo->defbranch);
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Invalid branch: %s", old_head);
|
||||
free(old_head);
|
||||
if (get_sha1(ctx.qry.head, sha1)) {
|
||||
char *tmp = xstrdup(ctx.qry.head);
|
||||
ctx.qry.head = ctx.repo->defbranch;
|
||||
ctx.page.status = 404;
|
||||
ctx.page.statusmsg = "Not found";
|
||||
cgit_print_http_headers();
|
||||
cgit_print_docstart();
|
||||
cgit_print_pageheader();
|
||||
cgit_print_error("Invalid branch: %s", tmp);
|
||||
cgit_print_docend();
|
||||
free(tmp);
|
||||
return 1;
|
||||
}
|
||||
string_list_sort(&ctx.repo->submodules);
|
||||
|
@ -661,7 +637,7 @@ static inline void open_auth_filter(const char *function)
|
|||
ctx.env.https ? ctx.env.https : "",
|
||||
ctx.qry.repo ? ctx.qry.repo : "",
|
||||
ctx.qry.page ? ctx.qry.page : "",
|
||||
cgit_currentfullurl(),
|
||||
ctx.qry.url ? ctx.qry.url : "",
|
||||
cgit_loginurl());
|
||||
}
|
||||
|
||||
|
@ -675,13 +651,13 @@ static inline void open_auth_filter(const char *function)
|
|||
static inline void authenticate_post(void)
|
||||
{
|
||||
char buffer[MAX_AUTHENTICATION_POST_BYTES];
|
||||
ssize_t len;
|
||||
int len;
|
||||
|
||||
open_auth_filter("authenticate-post");
|
||||
len = ctx.env.content_length;
|
||||
if (len > MAX_AUTHENTICATION_POST_BYTES)
|
||||
len = MAX_AUTHENTICATION_POST_BYTES;
|
||||
if ((len = read(STDIN_FILENO, buffer, len)) < 0)
|
||||
if (read(STDIN_FILENO, buffer, len) < 0)
|
||||
die_errno("Could not read POST from stdin");
|
||||
if (write(STDOUT_FILENO, buffer, len) < 0)
|
||||
die_errno("Could not write POST to stdout");
|
||||
|
@ -714,7 +690,6 @@ static inline void authenticate_cookie(void)
|
|||
static void process_request(void)
|
||||
{
|
||||
struct cgit_cmd *cmd;
|
||||
int nongit = 0;
|
||||
|
||||
/* If we're not yet authenticated, no matter what page we're on,
|
||||
* display the authentication body from the auth_filter. This should
|
||||
|
@ -730,25 +705,21 @@ static void process_request(void)
|
|||
return;
|
||||
}
|
||||
|
||||
if (ctx.repo)
|
||||
prepare_repo_env(&nongit);
|
||||
|
||||
cmd = cgit_get_cmd();
|
||||
if (!cmd) {
|
||||
ctx.page.title = "cgit error";
|
||||
cgit_print_error_page(404, "Not found", "Invalid request");
|
||||
ctx.page.status = 404;
|
||||
ctx.page.statusmsg = "Not found";
|
||||
cgit_print_http_headers();
|
||||
cgit_print_docstart();
|
||||
cgit_print_pageheader();
|
||||
cgit_print_error("Invalid request");
|
||||
cgit_print_docend();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ctx.cfg.enable_http_clone && cmd->is_clone) {
|
||||
ctx.page.title = "cgit error";
|
||||
cgit_print_error_page(404, "Not found", "Invalid request");
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd->want_repo && !ctx.repo) {
|
||||
cgit_print_error_page(400, "Bad request",
|
||||
"No repository selected");
|
||||
html_status(404, "Not found", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -758,10 +729,28 @@ static void process_request(void)
|
|||
*/
|
||||
ctx.qry.vpath = cmd->want_vpath ? ctx.qry.path : NULL;
|
||||
|
||||
if (ctx.repo && prepare_repo_cmd(nongit))
|
||||
if (cmd->want_repo && !ctx.repo) {
|
||||
cgit_print_http_headers();
|
||||
cgit_print_docstart();
|
||||
cgit_print_pageheader();
|
||||
cgit_print_error("No repository selected");
|
||||
cgit_print_docend();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.repo && prepare_repo_cmd())
|
||||
return;
|
||||
|
||||
if (cmd->want_layout) {
|
||||
cgit_print_http_headers();
|
||||
cgit_print_docstart();
|
||||
cgit_print_pageheader();
|
||||
}
|
||||
|
||||
cmd->fn();
|
||||
|
||||
if (cmd->want_layout)
|
||||
cgit_print_docend();
|
||||
}
|
||||
|
||||
static int cmp_repos(const void *a, const void *b)
|
||||
|
@ -776,7 +765,7 @@ static char *build_snapshot_setting(int bitmap)
|
|||
struct strbuf result = STRBUF_INIT;
|
||||
|
||||
for (f = cgit_snapshot_formats; f->suffix; f++) {
|
||||
if (cgit_snapshot_format_bit(f) & bitmap) {
|
||||
if (f->bit & bitmap) {
|
||||
if (result.len)
|
||||
strbuf_addch(&result, ' ');
|
||||
strbuf_addstr(&result, f->suffix);
|
||||
|
@ -815,18 +804,12 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
|
|||
}
|
||||
if (repo->defbranch)
|
||||
fprintf(f, "repo.defbranch=%s\n", repo->defbranch);
|
||||
if (repo->extra_head_content)
|
||||
fprintf(f, "repo.extra-head-content=%s\n", repo->extra_head_content);
|
||||
if (repo->module_link)
|
||||
fprintf(f, "repo.module-link=%s\n", repo->module_link);
|
||||
if (repo->section)
|
||||
fprintf(f, "repo.section=%s\n", repo->section);
|
||||
if (repo->homepage)
|
||||
fprintf(f, "repo.homepage=%s\n", repo->homepage);
|
||||
if (repo->clone_url)
|
||||
fprintf(f, "repo.clone-url=%s\n", repo->clone_url);
|
||||
fprintf(f, "repo.enable-blame=%d\n",
|
||||
repo->enable_blame);
|
||||
fprintf(f, "repo.enable-commit-graph=%d\n",
|
||||
repo->enable_commit_graph);
|
||||
fprintf(f, "repo.enable-log-filecount=%d\n",
|
||||
|
@ -848,8 +831,6 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
|
|||
fprintf(f, "repo.snapshots=%s\n", tmp ? tmp : "");
|
||||
free(tmp);
|
||||
}
|
||||
if (repo->snapshot_prefix)
|
||||
fprintf(f, "repo.snapshot-prefix=%s\n", repo->snapshot_prefix);
|
||||
if (repo->max_stats != ctx.cfg.max_stats)
|
||||
fprintf(f, "repo.max-stats=%s\n",
|
||||
cgit_find_stats_periodname(repo->max_stats));
|
||||
|
@ -859,7 +840,6 @@ static void print_repo(FILE *f, struct cgit_repo *repo)
|
|||
fprintf(f, "repo.logo-link=%s\n", repo->logo_link);
|
||||
fprintf(f, "repo.enable-remote-branches=%d\n", repo->enable_remote_branches);
|
||||
fprintf(f, "repo.enable-subject-links=%d\n", repo->enable_subject_links);
|
||||
fprintf(f, "repo.enable-html-serving=%d\n", repo->enable_html_serving);
|
||||
if (repo->branch_sort == 1)
|
||||
fprintf(f, "repo.branch-sort=age\n");
|
||||
if (repo->commit_sort) {
|
||||
|
@ -967,12 +947,11 @@ out:
|
|||
static void cgit_parse_args(int argc, const char **argv)
|
||||
{
|
||||
int i;
|
||||
const char *arg;
|
||||
int scan = 0;
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (!strcmp(argv[i], "--version")) {
|
||||
printf("CGit %s | https://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION);
|
||||
printf("CGit %s | http://git.zx2c4.com/cgit/\n\nCompiled in features:\n", CGIT_VERSION);
|
||||
#ifdef NO_LUA
|
||||
printf("[-] ");
|
||||
#else
|
||||
|
@ -988,26 +967,28 @@ static void cgit_parse_args(int argc, const char **argv)
|
|||
|
||||
exit(0);
|
||||
}
|
||||
if (skip_prefix(argv[i], "--cache=", &arg)) {
|
||||
ctx.cfg.cache_root = xstrdup(arg);
|
||||
if (starts_with(argv[i], "--cache=")) {
|
||||
ctx.cfg.cache_root = xstrdup(argv[i] + 8);
|
||||
} else if (!strcmp(argv[i], "--nocache")) {
|
||||
ctx.cfg.nocache = 1;
|
||||
} else if (!strcmp(argv[i], "--nohttp")) {
|
||||
ctx.env.no_http = "1";
|
||||
} else if (skip_prefix(argv[i], "--query=", &arg)) {
|
||||
ctx.qry.raw = xstrdup(arg);
|
||||
} else if (skip_prefix(argv[i], "--repo=", &arg)) {
|
||||
ctx.qry.repo = xstrdup(arg);
|
||||
} else if (skip_prefix(argv[i], "--page=", &arg)) {
|
||||
ctx.qry.page = xstrdup(arg);
|
||||
} else if (skip_prefix(argv[i], "--head=", &arg)) {
|
||||
ctx.qry.head = xstrdup(arg);
|
||||
} else if (starts_with(argv[i], "--query=")) {
|
||||
ctx.qry.raw = xstrdup(argv[i] + 8);
|
||||
} else if (starts_with(argv[i], "--repo=")) {
|
||||
ctx.qry.repo = xstrdup(argv[i] + 7);
|
||||
} else if (starts_with(argv[i], "--page=")) {
|
||||
ctx.qry.page = xstrdup(argv[i] + 7);
|
||||
} else if (starts_with(argv[i], "--head=")) {
|
||||
ctx.qry.head = xstrdup(argv[i] + 7);
|
||||
ctx.qry.has_symref = 1;
|
||||
} else if (skip_prefix(argv[i], "--oid=", &arg)) {
|
||||
ctx.qry.oid = xstrdup(arg);
|
||||
ctx.qry.has_oid = 1;
|
||||
} else if (skip_prefix(argv[i], "--ofs=", &arg)) {
|
||||
ctx.qry.ofs = atoi(arg);
|
||||
} else if (skip_prefix(argv[i], "--scan-tree=", &arg) ||
|
||||
skip_prefix(argv[i], "--scan-path=", &arg)) {
|
||||
} else if (starts_with(argv[i], "--sha1=")) {
|
||||
ctx.qry.sha1 = xstrdup(argv[i] + 7);
|
||||
ctx.qry.has_sha1 = 1;
|
||||
} else if (starts_with(argv[i], "--ofs=")) {
|
||||
ctx.qry.ofs = atoi(argv[i] + 6);
|
||||
} else if (starts_with(argv[i], "--scan-tree=") ||
|
||||
starts_with(argv[i], "--scan-path=")) {
|
||||
/*
|
||||
* HACK: The global snapshot bit mask defines the set
|
||||
* of allowed snapshot formats, but the config file
|
||||
|
@ -1021,7 +1002,7 @@ static void cgit_parse_args(int argc, const char **argv)
|
|||
*/
|
||||
ctx.cfg.snapshots = 0xFF;
|
||||
scan++;
|
||||
scan_tree(arg, repo_config);
|
||||
scan_tree(argv[i] + 12, repo_config);
|
||||
}
|
||||
}
|
||||
if (scan) {
|
||||
|
@ -1032,7 +1013,7 @@ static void cgit_parse_args(int argc, const char **argv)
|
|||
}
|
||||
}
|
||||
|
||||
static int calc_ttl(void)
|
||||
static int calc_ttl()
|
||||
{
|
||||
if (!ctx.repo)
|
||||
return ctx.cfg.cache_root_ttl;
|
||||
|
@ -1046,7 +1027,7 @@ static int calc_ttl(void)
|
|||
if (!strcmp(ctx.qry.page, "snapshot"))
|
||||
return ctx.cfg.cache_snapshot_ttl;
|
||||
|
||||
if (ctx.qry.has_oid)
|
||||
if (ctx.qry.has_sha1)
|
||||
return ctx.cfg.cache_static_ttl;
|
||||
|
||||
if (ctx.qry.has_symref)
|
||||
|
@ -1055,7 +1036,7 @@ static int calc_ttl(void)
|
|||
return ctx.cfg.cache_repo_ttl;
|
||||
}
|
||||
|
||||
int cmd_main(int argc, const char **argv)
|
||||
int main(int argc, const char **argv)
|
||||
{
|
||||
const char *path;
|
||||
int err, ttl;
|
||||
|
@ -1110,6 +1091,8 @@ int cmd_main(int argc, const char **argv)
|
|||
else
|
||||
ctx.page.expires += ttl * 60;
|
||||
if (!ctx.env.authenticated || (ctx.env.request_method && !strcmp(ctx.env.request_method, "HEAD")))
|
||||
ctx.cfg.nocache = 1;
|
||||
if (ctx.cfg.nocache)
|
||||
ctx.cfg.cache_size = 0;
|
||||
err = cache_process(ctx.cfg.cache_size, ctx.cfg.cache_root,
|
||||
ctx.qry.raw, ttl, process_request);
|
||||
|
|
100
cgit.css
100
cgit.css
|
@ -18,7 +18,7 @@ div#cgit a:hover {
|
|||
}
|
||||
|
||||
div#cgit table {
|
||||
border-collapse: collapse;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
div#cgit table#header {
|
||||
|
@ -85,12 +85,6 @@ div#cgit table.tabs td a.active {
|
|||
background-color: #ccc;
|
||||
}
|
||||
|
||||
div#cgit table.tabs a[href^="http://"]:after, div#cgit table.tabs a[href^="https://"]:after {
|
||||
content: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAQAAAAnOwc2AAAAAmJLR0QA/4ePzL8AAAAJcEhZcwAACxMAAAsTAQCanBgAAAAHdElNRQfgAhcJDQY+gm2TAAAAHWlUWHRDb21tZW50AAAAAABDcmVhdGVkIHdpdGggR0lNUGQuZQcAAABbSURBVAhbY2BABs4MU4CwhYHBh2Erww4wrGFQZHjI8B8IgUIscJWyDHcggltQhI4zGDCcRwhChPggHIggP1QoAVmQkSETrGoHsiAEsACtBYN0oDAMbgU6EBcAAL2eHUt4XUU4AAAAAElFTkSuQmCC);
|
||||
opacity: 0.5;
|
||||
margin: 0 0 0 5px;
|
||||
}
|
||||
|
||||
div#cgit table.tabs td.form {
|
||||
text-align: right;
|
||||
}
|
||||
|
@ -134,34 +128,14 @@ div#cgit table.list tr.logheader {
|
|||
background: #eee;
|
||||
}
|
||||
|
||||
div#cgit table.list tr:nth-child(even) {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
div#cgit table.list tr:nth-child(odd) {
|
||||
background: white;
|
||||
}
|
||||
|
||||
div#cgit table.list tr:hover {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
div#cgit table.list tr.nohover {
|
||||
background: white;
|
||||
}
|
||||
|
||||
div#cgit table.list tr.nohover:hover {
|
||||
background: white;
|
||||
}
|
||||
|
||||
div#cgit table.list tr.nohover-highlight:hover:nth-child(even) {
|
||||
background: #f7f7f7;
|
||||
}
|
||||
|
||||
div#cgit table.list tr.nohover-highlight:hover:nth-child(odd) {
|
||||
background: white;
|
||||
}
|
||||
|
||||
div#cgit table.list th {
|
||||
font-weight: bold;
|
||||
/* color: #888;
|
||||
|
@ -280,7 +254,7 @@ div#cgit div.error {
|
|||
margin: 1em 2em;
|
||||
}
|
||||
|
||||
div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit .ls-mod {
|
||||
div#cgit a.ls-blob, div#cgit a.ls-dir, div#cgit a.ls-mod {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
|
@ -300,7 +274,6 @@ div#cgit table.blob {
|
|||
border-top: solid 1px black;
|
||||
}
|
||||
|
||||
div#cgit table.blob td.hashes,
|
||||
div#cgit table.blob td.lines {
|
||||
margin: 0; padding: 0 0 0 0.5em;
|
||||
vertical-align: top;
|
||||
|
@ -330,43 +303,6 @@ div#cgit table.ssdiff td.lineno a:hover {
|
|||
color: black;
|
||||
}
|
||||
|
||||
div#cgit table.blame td.hashes,
|
||||
div#cgit table.blame td.lines,
|
||||
div#cgit table.blame td.linenumbers {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div#cgit table.blame td.hashes div.alt,
|
||||
div#cgit table.blame td.lines div.alt {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
div#cgit table.blame td.linenumbers div.alt {
|
||||
padding: 0 0.5em 0 0;
|
||||
}
|
||||
|
||||
div#cgit table.blame div.alt:nth-child(even) {
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
div#cgit table.blame div.alt:nth-child(odd) {
|
||||
background: white;
|
||||
}
|
||||
|
||||
div#cgit table.blame td.lines > div {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
div#cgit table.blame td.lines > div > pre {
|
||||
padding: 0 0 0 0.5em;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
div#cgit table.blame .oid {
|
||||
font-size: 100%;
|
||||
}
|
||||
|
||||
div#cgit table.bin-blob {
|
||||
margin-top: 0.5em;
|
||||
border: solid 1px black;
|
||||
|
@ -565,7 +501,7 @@ div#cgit table.diff td div.del {
|
|||
color: red;
|
||||
}
|
||||
|
||||
div#cgit .oid {
|
||||
div#cgit .sha1 {
|
||||
font-family: monospace;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
@ -648,31 +584,19 @@ div#cgit span.age-months {
|
|||
div#cgit span.age-years {
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
div#cgit span.insertions {
|
||||
color: #080;
|
||||
}
|
||||
|
||||
div#cgit span.deletions {
|
||||
color: #800;
|
||||
}
|
||||
|
||||
div#cgit div.footer {
|
||||
margin-top: 0.5em;
|
||||
text-align: center;
|
||||
font-size: 80%;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
div#cgit div.footer a {
|
||||
color: #ccc;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
div#cgit div.footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
div#cgit a.branch-deco {
|
||||
color: #000;
|
||||
margin: 0px 0.5em;
|
||||
|
@ -680,7 +604,6 @@ div#cgit a.branch-deco {
|
|||
background-color: #88ff88;
|
||||
border: solid 1px #007700;
|
||||
}
|
||||
|
||||
div#cgit a.tag-deco {
|
||||
color: #000;
|
||||
margin: 0px 0.5em;
|
||||
|
@ -688,15 +611,6 @@ div#cgit a.tag-deco {
|
|||
background-color: #ffff88;
|
||||
border: solid 1px #777700;
|
||||
}
|
||||
|
||||
div#cgit a.tag-annotated-deco {
|
||||
color: #000;
|
||||
margin: 0px 0.5em;
|
||||
padding: 0px 0.25em;
|
||||
background-color: #ffcc88;
|
||||
border: solid 1px #777700;
|
||||
}
|
||||
|
||||
div#cgit a.remote-deco {
|
||||
color: #000;
|
||||
margin: 0px 0.5em;
|
||||
|
@ -704,7 +618,6 @@ div#cgit a.remote-deco {
|
|||
background-color: #ccccff;
|
||||
border: solid 1px #000077;
|
||||
}
|
||||
|
||||
div#cgit a.deco {
|
||||
color: #000;
|
||||
margin: 0px 0.5em;
|
||||
|
@ -715,7 +628,6 @@ div#cgit a.deco {
|
|||
|
||||
div#cgit div.commit-subject a.branch-deco,
|
||||
div#cgit div.commit-subject a.tag-deco,
|
||||
div#cgit div.commit-subject a.tag-annotated-deco,
|
||||
div#cgit div.commit-subject a.remote-deco,
|
||||
div#cgit div.commit-subject a.deco {
|
||||
margin-left: 1em;
|
||||
|
@ -885,9 +797,9 @@ div#cgit table.ssdiff td.head div.head {
|
|||
|
||||
div#cgit table.ssdiff td.foot {
|
||||
border-top: solid 1px #aaa;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
div#cgit table.ssdiff td.space {
|
||||
|
|
96
cgit.h
96
cgit.h
|
@ -1,39 +1,35 @@
|
|||
#ifndef CGIT_H
|
||||
#define CGIT_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include <git-compat-util.h>
|
||||
|
||||
#include <archive.h>
|
||||
#include <commit.h>
|
||||
#include <diffcore.h>
|
||||
#include <diff.h>
|
||||
#include <environment.h>
|
||||
#include <graph.h>
|
||||
#include <cache.h>
|
||||
#include <grep.h>
|
||||
#include <hex.h>
|
||||
#include <log-tree.h>
|
||||
#include <notes.h>
|
||||
#include <object.h>
|
||||
#include <object-name.h>
|
||||
#include <object-store.h>
|
||||
#include <path.h>
|
||||
#include <tree.h>
|
||||
#include <commit.h>
|
||||
#include <tag.h>
|
||||
#include <diff.h>
|
||||
#include <diffcore.h>
|
||||
#include <argv-array.h>
|
||||
#include <refs.h>
|
||||
#include <revision.h>
|
||||
#include <setup.h>
|
||||
#include <log-tree.h>
|
||||
#include <archive.h>
|
||||
#include <string-list.h>
|
||||
#include <strvec.h>
|
||||
#include <tag.h>
|
||||
#include <tree.h>
|
||||
#include <utf8.h>
|
||||
#include <wrapper.h>
|
||||
#include <xdiff-interface.h>
|
||||
#include <xdiff/xdiff.h>
|
||||
#include <utf8.h>
|
||||
#include <notes.h>
|
||||
#include <graph.h>
|
||||
|
||||
/* Add isgraph(x) to Git's sane ctype support (see git-compat-util.h) */
|
||||
#undef isgraph
|
||||
#define isgraph(x) (isprint((x)) && !isspace((x)))
|
||||
|
||||
/*
|
||||
* Dateformats used on misc. pages
|
||||
*/
|
||||
#define FMT_LONGDATE "%Y-%m-%d %H:%M:%S (%Z)"
|
||||
#define FMT_SHORTDATE "%Y-%m-%d"
|
||||
#define FMT_ATOMDATE "%Y-%m-%dT%H:%M:%SZ"
|
||||
|
||||
|
||||
/*
|
||||
|
@ -52,8 +48,6 @@
|
|||
*/
|
||||
#define PAGE_ENCODING "UTF-8"
|
||||
|
||||
#define BIT(x) (1U << (x))
|
||||
|
||||
typedef void (*configfn)(const char *name, const char *value);
|
||||
typedef void (*filepair_fn)(struct diff_filepair *pair);
|
||||
typedef void (*linediff_fn)(char *line, int len);
|
||||
|
@ -69,7 +63,7 @@ typedef enum {
|
|||
struct cgit_filter {
|
||||
int (*open)(struct cgit_filter *, va_list ap);
|
||||
int (*close)(struct cgit_filter *);
|
||||
void (*fprintfp)(struct cgit_filter *, FILE *, const char *prefix);
|
||||
void (*fprintf)(struct cgit_filter *, FILE *, const char *prefix);
|
||||
void (*cleanup)(struct cgit_filter *);
|
||||
int argument_count;
|
||||
};
|
||||
|
@ -79,6 +73,7 @@ struct cgit_exec_filter {
|
|||
char *cmd;
|
||||
char **argv;
|
||||
int old_stdout;
|
||||
int pipe_fh[2];
|
||||
int pid;
|
||||
};
|
||||
|
||||
|
@ -87,9 +82,7 @@ struct cgit_repo {
|
|||
char *name;
|
||||
char *path;
|
||||
char *desc;
|
||||
char *extra_head_content;
|
||||
char *owner;
|
||||
char *homepage;
|
||||
char *defbranch;
|
||||
char *module_link;
|
||||
struct string_list readme;
|
||||
|
@ -97,15 +90,12 @@ struct cgit_repo {
|
|||
char *clone_url;
|
||||
char *logo;
|
||||
char *logo_link;
|
||||
char *snapshot_prefix;
|
||||
int snapshots;
|
||||
int enable_blame;
|
||||
int enable_commit_graph;
|
||||
int enable_log_filecount;
|
||||
int enable_log_linecount;
|
||||
int enable_remote_branches;
|
||||
int enable_subject_links;
|
||||
int enable_html_serving;
|
||||
int max_stats;
|
||||
int branch_sort;
|
||||
int commit_sort;
|
||||
|
@ -134,11 +124,9 @@ struct commitinfo {
|
|||
char *author;
|
||||
char *author_email;
|
||||
unsigned long author_date;
|
||||
int author_tz;
|
||||
char *committer;
|
||||
char *committer_email;
|
||||
unsigned long committer_date;
|
||||
int committer_tz;
|
||||
char *subject;
|
||||
char *msg;
|
||||
char *msg_encoding;
|
||||
|
@ -148,7 +136,6 @@ struct taginfo {
|
|||
char *tagger;
|
||||
char *tagger_email;
|
||||
unsigned long tagger_date;
|
||||
int tagger_tz;
|
||||
char *msg;
|
||||
};
|
||||
|
||||
|
@ -169,7 +156,7 @@ struct reflist {
|
|||
|
||||
struct cgit_query {
|
||||
int has_symref;
|
||||
int has_oid;
|
||||
int has_sha1;
|
||||
int has_difftype;
|
||||
char *raw;
|
||||
char *repo;
|
||||
|
@ -177,10 +164,11 @@ struct cgit_query {
|
|||
char *search;
|
||||
char *grep;
|
||||
char *head;
|
||||
char *oid;
|
||||
char *oid2;
|
||||
char *sha1;
|
||||
char *sha2;
|
||||
char *path;
|
||||
char *name;
|
||||
char *mimetype;
|
||||
char *url;
|
||||
char *period;
|
||||
int ofs;
|
||||
|
@ -191,7 +179,6 @@ struct cgit_query {
|
|||
int show_all;
|
||||
int context;
|
||||
int ignorews;
|
||||
int follow;
|
||||
char *vpath;
|
||||
};
|
||||
|
||||
|
@ -200,17 +187,19 @@ struct cgit_config {
|
|||
char *cache_root;
|
||||
char *clone_prefix;
|
||||
char *clone_url;
|
||||
char *css;
|
||||
char *favicon;
|
||||
char *footer;
|
||||
char *head_include;
|
||||
char *header;
|
||||
char *index_header;
|
||||
char *index_info;
|
||||
char *logo;
|
||||
char *logo_link;
|
||||
char *mimetype_file;
|
||||
char *module_link;
|
||||
char *project_list;
|
||||
struct string_list readme;
|
||||
struct string_list css;
|
||||
char *robots;
|
||||
char *root_title;
|
||||
char *root_desc;
|
||||
|
@ -232,17 +221,14 @@ struct cgit_config {
|
|||
int case_sensitive_sort;
|
||||
int embedded;
|
||||
int enable_filter_overrides;
|
||||
int enable_follow_links;
|
||||
int enable_http_clone;
|
||||
int enable_index_links;
|
||||
int enable_index_owner;
|
||||
int enable_blame;
|
||||
int enable_commit_graph;
|
||||
int enable_log_filecount;
|
||||
int enable_log_linecount;
|
||||
int enable_remote_branches;
|
||||
int enable_subject_links;
|
||||
int enable_html_serving;
|
||||
int enable_tree_linenumbers;
|
||||
int enable_git_config;
|
||||
int local_time;
|
||||
|
@ -254,6 +240,7 @@ struct cgit_config {
|
|||
int max_repodesc_len;
|
||||
int max_blob_size;
|
||||
int max_stats;
|
||||
int nocache;
|
||||
int noplainemail;
|
||||
int noheader;
|
||||
int renamelimit;
|
||||
|
@ -269,7 +256,6 @@ struct cgit_config {
|
|||
int branch_sort;
|
||||
int commit_sort;
|
||||
struct string_list mimetypes;
|
||||
struct string_list js;
|
||||
struct cgit_filter *about_filter;
|
||||
struct cgit_filter *commit_filter;
|
||||
struct cgit_filter *source_filter;
|
||||
|
@ -322,6 +308,7 @@ struct cgit_snapshot_format {
|
|||
const char *suffix;
|
||||
const char *mimetype;
|
||||
write_archive_fn_t write_func;
|
||||
int bit;
|
||||
};
|
||||
|
||||
extern const char *cgit_version;
|
||||
|
@ -341,28 +328,26 @@ extern int chk_non_negative(int result, char *msg);
|
|||
|
||||
extern char *trim_end(const char *str, char c);
|
||||
extern char *ensure_end(const char *str, char c);
|
||||
extern char *strlpart(char *txt, int maxlen);
|
||||
extern char *strrpart(char *txt, int maxlen);
|
||||
|
||||
extern void strbuf_ensure_end(struct strbuf *sb, char c);
|
||||
|
||||
extern void cgit_add_ref(struct reflist *list, struct refinfo *ref);
|
||||
extern void cgit_free_reflist_inner(struct reflist *list);
|
||||
extern int cgit_refs_cb(const char *refname, const struct object_id *oid,
|
||||
extern int cgit_refs_cb(const char *refname, const unsigned char *sha1,
|
||||
int flags, void *cb_data);
|
||||
|
||||
extern void cgit_free_commitinfo(struct commitinfo *info);
|
||||
extern void cgit_free_taginfo(struct taginfo *info);
|
||||
extern void *cgit_free_commitinfo(struct commitinfo *info);
|
||||
|
||||
void cgit_diff_tree_cb(struct diff_queue_struct *q,
|
||||
struct diff_options *options, void *data);
|
||||
|
||||
extern int cgit_diff_files(const struct object_id *old_oid,
|
||||
const struct object_id *new_oid,
|
||||
extern int cgit_diff_files(const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1,
|
||||
unsigned long *old_size, unsigned long *new_size,
|
||||
int *binary, int context, int ignorews,
|
||||
linediff_fn fn);
|
||||
|
||||
extern void cgit_diff_tree(const struct object_id *old_oid,
|
||||
const struct object_id *new_oid,
|
||||
extern void cgit_diff_tree(const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1,
|
||||
filepair_fn fn, const char *prefix, int ignorews);
|
||||
|
||||
extern void cgit_diff_commit(struct commit *commit, filepair_fn fn,
|
||||
|
@ -381,9 +366,6 @@ extern void cgit_parse_url(const char *url);
|
|||
extern const char *cgit_repobasename(const char *reponame);
|
||||
|
||||
extern int cgit_parse_snapshots_mask(const char *str);
|
||||
extern const struct object_id *cgit_snapshot_get_sig(const char *ref,
|
||||
const struct cgit_snapshot_format *f);
|
||||
extern const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f);
|
||||
|
||||
extern int cgit_open_filter(struct cgit_filter *filter, ...);
|
||||
extern int cgit_close_filter(struct cgit_filter *filter);
|
||||
|
@ -399,6 +381,4 @@ extern int readfile(const char *path, char **buf, size_t *size);
|
|||
|
||||
extern char *expand_macros(const char *txt);
|
||||
|
||||
extern char *get_mimetype_for_filename(const char *filename);
|
||||
|
||||
#endif /* CGIT_H */
|
||||
|
|
68
cgit.js
68
cgit.js
|
@ -1,68 +0,0 @@
|
|||
/* cgit.js: javacript functions for cgit
|
||||
*
|
||||
* Copyright (C) 2006-2018 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
*
|
||||
* Licensed under GNU General Public License v2
|
||||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
(function () {
|
||||
|
||||
/* This follows the logic and suffixes used in ui-shared.c */
|
||||
|
||||
var age_classes = [ "age-mins", "age-hours", "age-days", "age-weeks", "age-months", "age-years" ];
|
||||
var age_suffix = [ "min.", "hours", "days", "weeks", "months", "years", "years" ];
|
||||
var age_next = [ 60, 3600, 24 * 3600, 7 * 24 * 3600, 30 * 24 * 3600, 365 * 24 * 3600, 365 * 24 * 3600 ];
|
||||
var age_limit = [ 7200, 24 * 7200, 7 * 24 * 7200, 30 * 24 * 7200, 365 * 25 * 7200, 365 * 25 * 7200 ];
|
||||
var update_next = [ 10, 5 * 60, 1800, 24 * 3600, 24 * 3600, 24 * 3600, 24 * 3600 ];
|
||||
|
||||
function render_age(e, age) {
|
||||
var t, n;
|
||||
|
||||
for (n = 0; n < age_classes.length; n++)
|
||||
if (age < age_limit[n])
|
||||
break;
|
||||
|
||||
t = Math.round(age / age_next[n]) + " " + age_suffix[n];
|
||||
|
||||
if (e.textContent != t) {
|
||||
e.textContent = t;
|
||||
if (n == age_classes.length)
|
||||
n--;
|
||||
if (e.className != age_classes[n])
|
||||
e.className = age_classes[n];
|
||||
}
|
||||
}
|
||||
|
||||
function aging() {
|
||||
var n, next = 24 * 3600,
|
||||
now_ut = Math.round((new Date().getTime() / 1000));
|
||||
|
||||
for (n = 0; n < age_classes.length; n++) {
|
||||
var m, elems = document.getElementsByClassName(age_classes[n]);
|
||||
|
||||
if (elems.length && update_next[n] < next)
|
||||
next = update_next[n];
|
||||
|
||||
for (m = 0; m < elems.length; m++) {
|
||||
var age = now_ut - elems[m].getAttribute("data-ut");
|
||||
|
||||
render_age(elems[m], age);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* We only need to come back when the age might have changed.
|
||||
* Eg, if everything is counted in hours already, once per
|
||||
* 5 minutes is accurate enough.
|
||||
*/
|
||||
|
||||
window.setTimeout(aging, next * 1000);
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
/* we can do the aging on DOM content load since no layout dependency */
|
||||
aging();
|
||||
}, false);
|
||||
|
||||
})();
|
22
cgit.mk
22
cgit.mk
|
@ -21,8 +21,6 @@ CGIT_CFLAGS += -DCGIT_CONFIG='"$(CGIT_CONFIG)"'
|
|||
CGIT_CFLAGS += -DCGIT_SCRIPT_NAME='"$(CGIT_SCRIPT_NAME)"'
|
||||
CGIT_CFLAGS += -DCGIT_CACHE_ROOT='"$(CACHE_ROOT)"'
|
||||
|
||||
PKG_CONFIG ?= pkg-config
|
||||
|
||||
ifdef NO_C99_FORMAT
|
||||
CFLAGS += -DNO_C99_FORMAT
|
||||
endif
|
||||
|
@ -33,7 +31,7 @@ ifdef NO_LUA
|
|||
else
|
||||
ifeq ($(LUA_PKGCONFIG),)
|
||||
LUA_PKGCONFIG := $(shell for pc in luajit lua lua5.2 lua5.1; do \
|
||||
$(PKG_CONFIG) --exists $$pc 2>/dev/null && echo $$pc && break; \
|
||||
pkg-config --exists $$pc 2>/dev/null && echo $$pc && break; \
|
||||
done)
|
||||
LUA_MODE := autodetected
|
||||
else
|
||||
|
@ -41,8 +39,8 @@ else
|
|||
endif
|
||||
ifneq ($(LUA_PKGCONFIG),)
|
||||
LUA_MESSAGE := linking with $(LUA_MODE) $(LUA_PKGCONFIG)
|
||||
LUA_LIBS := $(shell $(PKG_CONFIG) --libs $(LUA_PKGCONFIG) 2>/dev/null)
|
||||
LUA_CFLAGS := $(shell $(PKG_CONFIG) --cflags $(LUA_PKGCONFIG) 2>/dev/null)
|
||||
LUA_LIBS := $(shell pkg-config --libs $(LUA_PKGCONFIG) 2>/dev/null)
|
||||
LUA_CFLAGS := $(shell pkg-config --cflags $(LUA_PKGCONFIG) 2>/dev/null)
|
||||
CGIT_LIBS += $(LUA_LIBS)
|
||||
CGIT_CFLAGS += $(LUA_CFLAGS)
|
||||
else
|
||||
|
@ -53,8 +51,8 @@ endif
|
|||
|
||||
endif
|
||||
|
||||
# Add -ldl to linker flags on systems that commonly use GNU libc.
|
||||
ifneq (,$(filter $(uname_S),Linux GNU GNU/kFreeBSD))
|
||||
# Add -ldl to linker flags on non-BSD systems.
|
||||
ifeq ($(findstring BSD,$(uname_S)),)
|
||||
CGIT_LIBS += -ldl
|
||||
endif
|
||||
|
||||
|
@ -77,7 +75,6 @@ CGIT_OBJ_NAMES += parsing.o
|
|||
CGIT_OBJ_NAMES += scan-tree.o
|
||||
CGIT_OBJ_NAMES += shared.o
|
||||
CGIT_OBJ_NAMES += ui-atom.o
|
||||
CGIT_OBJ_NAMES += ui-blame.o
|
||||
CGIT_OBJ_NAMES += ui-blob.o
|
||||
CGIT_OBJ_NAMES += ui-clone.o
|
||||
CGIT_OBJ_NAMES += ui-commit.o
|
||||
|
@ -99,7 +96,7 @@ CGIT_OBJS := $(addprefix $(CGIT_PREFIX),$(CGIT_OBJ_NAMES))
|
|||
|
||||
# Only cgit.c reference CGIT_VERSION so we only rebuild its objects when the
|
||||
# version changes.
|
||||
CGIT_VERSION_OBJS := $(addprefix $(CGIT_PREFIX),cgit.o cgit.sp)
|
||||
CGIT_VERSION_OBJS := $(addprefix $(CGIT_PREFIX),cgit.o)
|
||||
$(CGIT_VERSION_OBJS): $(CGIT_PREFIX)VERSION
|
||||
$(CGIT_VERSION_OBJS): EXTRA_CPPFLAGS = \
|
||||
-DCGIT_VERSION='"$(CGIT_VERSION)"'
|
||||
|
@ -132,10 +129,3 @@ $(CGIT_OBJS): %.o: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS $(missing_dep_dirs)
|
|||
$(CGIT_PREFIX)cgit: $(CGIT_OBJS) GIT-LDFLAGS $(GITLIBS)
|
||||
@echo 1>&1 " * $(LUA_MESSAGE)"
|
||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS) $(CGIT_LIBS)
|
||||
|
||||
CGIT_SP_OBJS := $(patsubst %.o,%.sp,$(CGIT_OBJS))
|
||||
|
||||
$(CGIT_SP_OBJS): %.sp: %.c GIT-CFLAGS $(CGIT_PREFIX)CGIT-CFLAGS FORCE
|
||||
$(QUIET_SP)cgcc -no-compile $(ALL_CFLAGS) $(EXTRA_CPPFLAGS) $(CGIT_CFLAGS) $(SPARSE_FLAGS) $<
|
||||
|
||||
cgit-sparse: $(CGIT_SP_OBJS)
|
||||
|
|
265
cgitrc.5.txt
265
cgitrc.5.txt
|
@ -54,10 +54,14 @@ branch-sort::
|
|||
list, and when set to "name" enables ordering by branch name. Default
|
||||
value: "name".
|
||||
|
||||
cache-about-ttl::
|
||||
cache-root::
|
||||
Path used to store the cgit cache entries. Default value:
|
||||
"/var/cache/cgit". See also: "MACRO EXPANSION".
|
||||
|
||||
cache-static-ttl::
|
||||
Number which specifies the time-to-live, in minutes, for the cached
|
||||
version of the repository about page. See also: "CACHE". Default
|
||||
value: "15".
|
||||
version of repository pages accessed with a fixed SHA1. See also:
|
||||
"CACHE". Default value: -1".
|
||||
|
||||
cache-dynamic-ttl::
|
||||
Number which specifies the time-to-live, in minutes, for the cached
|
||||
|
@ -69,10 +73,6 @@ cache-repo-ttl::
|
|||
version of the repository summary page. See also: "CACHE". Default
|
||||
value: "5".
|
||||
|
||||
cache-root::
|
||||
Path used to store the cgit cache entries. Default value:
|
||||
"/var/cache/cgit". See also: "MACRO EXPANSION".
|
||||
|
||||
cache-root-ttl::
|
||||
Number which specifies the time-to-live, in minutes, for the cached
|
||||
version of the repository index page. See also: "CACHE". Default
|
||||
|
@ -83,22 +83,22 @@ cache-scanrc-ttl::
|
|||
of scanning a path for git repositories. See also: "CACHE". Default
|
||||
value: "15".
|
||||
|
||||
case-sensitive-sort::
|
||||
Sort items in the repo list case sensitively. Default value: "1".
|
||||
See also: repository-sort, section-sort.
|
||||
|
||||
cache-size::
|
||||
The maximum number of entries in the cgit cache. When set to "0",
|
||||
caching is disabled. See also: "CACHE". Default value: "0"
|
||||
cache-about-ttl::
|
||||
Number which specifies the time-to-live, in minutes, for the cached
|
||||
version of the repository about page. See also: "CACHE". Default
|
||||
value: "15".
|
||||
|
||||
cache-snapshot-ttl::
|
||||
Number which specifies the time-to-live, in minutes, for the cached
|
||||
version of snapshots. See also: "CACHE". Default value: "5".
|
||||
|
||||
cache-static-ttl::
|
||||
Number which specifies the time-to-live, in minutes, for the cached
|
||||
version of repository pages accessed with a fixed SHA1. See also:
|
||||
"CACHE". Default value: -1".
|
||||
cache-size::
|
||||
The maximum number of entries in the cgit cache. When set to "0",
|
||||
caching is disabled. See also: "CACHE". Default value: "0"
|
||||
|
||||
case-sensitive-sort::
|
||||
Sort items in the repo list case sensitively. Default value: "1".
|
||||
See also: repository-sort, section-sort.
|
||||
|
||||
clone-prefix::
|
||||
Space-separated list of common prefixes which, when combined with a
|
||||
|
@ -126,8 +126,7 @@ commit-sort::
|
|||
|
||||
css::
|
||||
Url which specifies the css document to include in all cgit pages.
|
||||
Default value: "/cgit.css". May be given multiple times, each
|
||||
css URL path is added in the head section of the document in turn.
|
||||
Default value: "/cgit.css".
|
||||
|
||||
email-filter::
|
||||
Specifies a command which will be invoked to format names and email
|
||||
|
@ -142,11 +141,6 @@ embedded::
|
|||
suitable for embedding in other html pages. Default value: none. See
|
||||
also: "noheader".
|
||||
|
||||
enable-blame::
|
||||
Flag which, when set to "1", will allow cgit to provide a "blame" page
|
||||
for files, and will make it generate links to that page in appropriate
|
||||
places. Default value: "0".
|
||||
|
||||
enable-commit-graph::
|
||||
Flag which, when set to "1", will make cgit print an ASCII-art commit
|
||||
history graph to the left of the commit messages in the repository
|
||||
|
@ -156,33 +150,12 @@ enable-filter-overrides::
|
|||
Flag which, when set to "1", allows all filter settings to be
|
||||
overridden in repository-specific cgitrc files. Default value: none.
|
||||
|
||||
enable-follow-links::
|
||||
Flag which, when set to "1", allows users to follow a file in the log
|
||||
view. Default value: "0".
|
||||
|
||||
enable-git-config::
|
||||
Flag which, when set to "1", will allow cgit to use git config to set
|
||||
any repo specific settings. This option is used in conjunction with
|
||||
"scan-path", and must be defined prior, to augment repo-specific
|
||||
settings. The keys gitweb.owner, gitweb.category, gitweb.description,
|
||||
and gitweb.homepage will map to the cgit keys repo.owner, repo.section,
|
||||
repo.desc, and repo.homepage respectively. All git config keys that begin
|
||||
with "cgit." will be mapped to the corresponding "repo." key in cgit.
|
||||
Default value: "0". See also: scan-path, section-from-path.
|
||||
|
||||
enable-http-clone::
|
||||
If set to "1", cgit will act as a dumb HTTP endpoint for git clones.
|
||||
If set to "1", cgit will act as an dumb HTTP endpoint for git clones.
|
||||
You can add "http://$HTTP_HOST$SCRIPT_NAME/$CGIT_REPO_URL" to clone-url
|
||||
to expose this feature. If you use an alternate way of serving git
|
||||
repositories, you may wish to disable this. Default value: "1".
|
||||
|
||||
enable-html-serving::
|
||||
Flag which, when set to "1", will allow the /plain handler to serve
|
||||
mimetype headers that result in the file being treated as HTML by the
|
||||
browser. When set to "0", such file types are returned instead as
|
||||
text/plain or application/octet-stream. Default value: "0". See also:
|
||||
"repo.enable-html-serving".
|
||||
|
||||
enable-index-links::
|
||||
Flag which, when set to "1", will make cgit generate extra links for
|
||||
each repo in the repository index (specifically, to the "summary",
|
||||
|
@ -217,6 +190,16 @@ enable-tree-linenumbers::
|
|||
Flag which, when set to "1", will make cgit generate linenumber links
|
||||
for plaintext blobs printed in the tree view. Default value: "1".
|
||||
|
||||
enable-git-config::
|
||||
Flag which, when set to "1", will allow cgit to use git config to set
|
||||
any repo specific settings. This option is used in conjunction with
|
||||
"scan-path", and must be defined prior, to augment repo-specific
|
||||
settings. The keys gitweb.owner, gitweb.category, and gitweb.description
|
||||
will map to the cgit keys repo.owner, repo.section, and repo.desc,
|
||||
respectively. All git config keys that begin with "cgit." will be mapped
|
||||
to the corresponding "repo." key in cgit. Default value: "0". See also:
|
||||
scan-path, section-from-path.
|
||||
|
||||
favicon::
|
||||
Url used as link to a shortcut icon for cgit. It is suggested to use
|
||||
the value "/favicon.ico" since certain browsers will ignore other
|
||||
|
@ -239,10 +222,17 @@ include::
|
|||
Name of a configfile to include before the rest of the current config-
|
||||
file is parsed. Default value: none. See also: "MACRO EXPANSION".
|
||||
|
||||
js::
|
||||
Url which specifies the javascript script document to include in all cgit
|
||||
pages. Default value: "/cgit.js". Setting this to an empty string will
|
||||
disable generation of the link to this file in the head section.
|
||||
index-header::
|
||||
The content of the file specified with this option will be included
|
||||
verbatim above the repository index. This setting is deprecated, and
|
||||
will not be supported by cgit-1.0 (use root-readme instead). Default
|
||||
value: none.
|
||||
|
||||
index-info::
|
||||
The content of the file specified with this option will be included
|
||||
verbatim below the heading on the repository index page. This setting
|
||||
is deprecated, and will not be supported by cgit-1.0 (use root-desc
|
||||
instead). Default value: none.
|
||||
|
||||
local-time::
|
||||
Flag which, if set to "1", makes cgit print commit and tag times in the
|
||||
|
@ -257,14 +247,19 @@ logo-link::
|
|||
calculated url of the repository index page will be used. Default
|
||||
value: none.
|
||||
|
||||
owner-filter::
|
||||
Specifies a command which will be invoked to format the Owner
|
||||
column of the main page. The command will get the owner on STDIN,
|
||||
and the STDOUT from the command will be included verbatim in the
|
||||
table. This can be used to link to additional context such as an
|
||||
owners home page. When active this filter is used instead of the
|
||||
default owner query url. Default value: none.
|
||||
See also: "FILTER API".
|
||||
|
||||
max-atom-items::
|
||||
Specifies the number of items to display in atom feeds view. Default
|
||||
value: "10".
|
||||
|
||||
max-blob-size::
|
||||
Specifies the maximum size of a blob to display HTML for in KBytes.
|
||||
Default value: "0" (limit disabled).
|
||||
|
||||
max-commit-count::
|
||||
Specifies the number of entries to list per page in "log" view. Default
|
||||
value: "50".
|
||||
|
@ -275,13 +270,16 @@ max-message-length::
|
|||
|
||||
max-repo-count::
|
||||
Specifies the number of entries to list per page on the repository
|
||||
index page. The value "0" shows all repositories without limitation.
|
||||
Default value: "50".
|
||||
index page. Default value: "50".
|
||||
|
||||
max-repodesc-length::
|
||||
Specifies the maximum number of repo description characters to display
|
||||
on the repository index page. Default value: "80".
|
||||
|
||||
max-blob-size::
|
||||
Specifies the maximum size of a blob to display HTML for in KBytes.
|
||||
Default value: "0" (limit disabled).
|
||||
|
||||
max-stats::
|
||||
Set the default maximum statistics period. Valid values are "week",
|
||||
"month", "quarter" and "year". If unspecified, statistics are
|
||||
|
@ -309,6 +307,11 @@ module-link::
|
|||
formatstring are the path and SHA1 of the submodule commit. Default
|
||||
value: none.
|
||||
|
||||
nocache::
|
||||
If set to the value "1" caching will be disabled. This settings is
|
||||
deprecated, and will not be honored starting with cgit-1.0. Default
|
||||
value: "0".
|
||||
|
||||
noplainemail::
|
||||
If set to "1" showing full author email addresses will be disabled.
|
||||
Default value: "0".
|
||||
|
@ -317,15 +320,6 @@ noheader::
|
|||
Flag which, when set to "1", will make cgit omit the standard header
|
||||
on all pages. Default value: none. See also: "embedded".
|
||||
|
||||
owner-filter::
|
||||
Specifies a command which will be invoked to format the Owner
|
||||
column of the main page. The command will get the owner on STDIN,
|
||||
and the STDOUT from the command will be included verbatim in the
|
||||
table. This can be used to link to additional context such as an
|
||||
owners home page. When active this filter is used instead of the
|
||||
default owner query url. Default value: none.
|
||||
See also: "FILTER API".
|
||||
|
||||
project-list::
|
||||
A list of subdirectories inside of scan-path, relative to it, that
|
||||
should loaded as git repositories. This must be defined prior to
|
||||
|
@ -349,6 +343,10 @@ renamelimit::
|
|||
"-1" uses the compiletime value in git (for further info, look at
|
||||
`man git-diff`). Default value: "-1".
|
||||
|
||||
repo.group::
|
||||
Legacy alias for "section". This option is deprecated and will not be
|
||||
supported in cgit-1.0.
|
||||
|
||||
repository-sort::
|
||||
The way in which repositories in each section are sorted. Valid values
|
||||
are "name" for sorting by the repo name or "age" for sorting by the
|
||||
|
@ -414,12 +412,8 @@ side-by-side-diffs::
|
|||
snapshots::
|
||||
Text which specifies the default set of snapshot formats that cgit
|
||||
generates links for. The value is a space-separated list of zero or
|
||||
more of the values "tar", "tar.gz", "tar.bz2", "tar.lz", "tar.xz",
|
||||
"tar.zst" and "zip". The special value "all" enables all snapshot
|
||||
formats. Default value: none.
|
||||
All compressors use default settings. Some settings can be influenced
|
||||
with environment variables, for example set ZSTD_CLEVEL=10 in web
|
||||
server environment for higher (but slower) zstd compression.
|
||||
more of the values "tar", "tar.gz", "tar.bz2", "tar.xz" and "zip".
|
||||
Default value: none.
|
||||
|
||||
source-filter::
|
||||
Specifies a command which will be invoked to format plaintext blobs
|
||||
|
@ -495,18 +489,10 @@ repo.email-filter::
|
|||
Override the default email-filter. Default value: none. See also:
|
||||
"enable-filter-overrides". See also: "FILTER API".
|
||||
|
||||
repo.enable-blame::
|
||||
A flag which can be used to disable the global setting
|
||||
`enable-blame'. Default value: none.
|
||||
|
||||
repo.enable-commit-graph::
|
||||
A flag which can be used to disable the global setting
|
||||
`enable-commit-graph'. Default value: none.
|
||||
|
||||
repo.enable-html-serving::
|
||||
A flag which can be used to override the global setting
|
||||
`enable-html-serving`. Default value: none.
|
||||
|
||||
repo.enable-log-filecount::
|
||||
A flag which can be used to disable the global setting
|
||||
`enable-log-filecount'. Default value: none.
|
||||
|
@ -523,18 +509,11 @@ repo.enable-subject-links::
|
|||
A flag which can be used to override the global setting
|
||||
`enable-subject-links'. Default value: none.
|
||||
|
||||
repo.extra-head-content::
|
||||
This value will be added verbatim to the head section of each page
|
||||
displayed for this repo. Default value: none.
|
||||
|
||||
repo.hide::
|
||||
Flag which, when set to "1", hides the repository from the repository
|
||||
index. The repository can still be accessed by providing a direct path.
|
||||
Default value: "0". See also: "repo.ignore".
|
||||
|
||||
repo.homepage::
|
||||
The value to show as repository homepage. Default value: none.
|
||||
|
||||
repo.ignore::
|
||||
Flag which, when set to "1", ignores the repository. The repository
|
||||
is not shown in the index and cannot be accessed by providing a direct
|
||||
|
@ -549,6 +528,10 @@ repo.logo-link::
|
|||
calculated url of the repository index page will be used. Default
|
||||
value: global logo-link.
|
||||
|
||||
repo.owner-filter::
|
||||
Override the default owner-filter. Default value: none. See also:
|
||||
"enable-filter-overrides". See also: "FILTER API".
|
||||
|
||||
repo.module-link::
|
||||
Text which will be used as the formatstring for a hyperlink when a
|
||||
submodule is printed in a directory listing. The arguments for the
|
||||
|
@ -573,10 +556,6 @@ repo.owner::
|
|||
A value used to identify the owner of the repository. Default value:
|
||||
none.
|
||||
|
||||
repo.owner-filter::
|
||||
Override the default owner-filter. Default value: none. See also:
|
||||
"enable-filter-overrides". See also: "FILTER API".
|
||||
|
||||
repo.path::
|
||||
An absolute path to the repository directory. For non-bare repositories
|
||||
this is the .git-directory. Default value: none.
|
||||
|
@ -586,27 +565,20 @@ repo.readme::
|
|||
verbatim as the "About" page for this repo. You may also specify a
|
||||
git refspec by head or by hash by prepending the refspec followed by
|
||||
a colon. For example, "master:docs/readme.mkd". If the value begins
|
||||
with a colon, i.e. ":docs/readme.rst", the head giving in query or
|
||||
the default branch of the repository will be used. Sharing any file
|
||||
will expose that entire directory tree to the "/about/PATH" endpoints,
|
||||
so be sure that there are no non-public files located in the same
|
||||
directory as the readme file. Default value: <readme>.
|
||||
|
||||
repo.section::
|
||||
Override the current section name for this repository. Default value:
|
||||
none.
|
||||
with a colon, i.e. ":docs/readme.rst", the default branch of the
|
||||
repository will be used. Sharing any file will expose that entire
|
||||
directory tree to the "/about/PATH" endpoints, so be sure that there
|
||||
are no non-public files located in the same directory as the readme
|
||||
file. Default value: <readme>.
|
||||
|
||||
repo.snapshots::
|
||||
A mask of snapshot formats for this repo that cgit generates links for,
|
||||
restricted by the global "snapshots" setting. Default value:
|
||||
<snapshots>.
|
||||
|
||||
repo.snapshot-prefix::
|
||||
Prefix to use for snapshot links instead of the repository basename.
|
||||
For example, the "linux-stable" repository may wish to set this to
|
||||
"linux" so that snapshots are in the format "linux-3.15.4" instead
|
||||
of "linux-stable-3.15.4". Default value: <empty> meaning to use
|
||||
the repository basename.
|
||||
repo.section::
|
||||
Override the current section name for this repository. Default value:
|
||||
none.
|
||||
|
||||
repo.source-filter::
|
||||
Override the default source-filter. Default value: none. See also:
|
||||
|
@ -680,6 +652,30 @@ about filter::
|
|||
The about text that is to be filtered is available on standard input
|
||||
and the filtered text is expected on standard output.
|
||||
|
||||
commit filter::
|
||||
This filter is given no arguments. The commit message text that is to
|
||||
be filtered is available on standard input and the filtered text is
|
||||
expected on standard output.
|
||||
|
||||
email filter::
|
||||
This filter is given two parameters: the email address of the relevent
|
||||
author and a string indicating the originating page. The filter will
|
||||
then receive the text string to format on standard input and is
|
||||
expected to write to standard output the formatted text to be included
|
||||
in the page.
|
||||
|
||||
owner filter::
|
||||
This filter is given no arguments. The owner text is avilable on
|
||||
standard input and the filter is expected to write to standard
|
||||
output. The output is included in the Owner column.
|
||||
|
||||
source filter::
|
||||
This filter is given a single parameter: the filename of the source
|
||||
file to filter. The filter can use the filename to determine (for
|
||||
example) the syntax highlighting mode. The contents of the source
|
||||
file that is to be filtered is available on standard input and the
|
||||
filtered contents is expected on standard output.
|
||||
|
||||
auth filter::
|
||||
The authentication filter receives 12 parameters:
|
||||
- filter action, explained below, which specifies which action the
|
||||
|
@ -706,30 +702,6 @@ auth filter::
|
|||
Please see `filters/simple-authentication.lua` for a clear example
|
||||
script that may be modified.
|
||||
|
||||
commit filter::
|
||||
This filter is given no arguments. The commit message text that is to
|
||||
be filtered is available on standard input and the filtered text is
|
||||
expected on standard output.
|
||||
|
||||
email filter::
|
||||
This filter is given two parameters: the email address of the relevant
|
||||
author and a string indicating the originating page. The filter will
|
||||
then receive the text string to format on standard input and is
|
||||
expected to write to standard output the formatted text to be included
|
||||
in the page.
|
||||
|
||||
owner filter::
|
||||
This filter is given no arguments. The owner text is available on
|
||||
standard input and the filter is expected to write to standard
|
||||
output. The output is included in the Owner column.
|
||||
|
||||
source filter::
|
||||
This filter is given a single parameter: the filename of the source
|
||||
file to filter. The filter can use the filename to determine (for
|
||||
example) the syntax highlighting mode. The contents of the source
|
||||
file that is to be filtered is available on standard input and the
|
||||
filtered contents is expected on standard output.
|
||||
|
||||
|
||||
All filters are handed the following environment variables:
|
||||
|
||||
|
@ -773,7 +745,7 @@ the environment variables defined in "FILTER API":
|
|||
|
||||
|
||||
CACHE
|
||||
-----
|
||||
------
|
||||
|
||||
All cache ttl values are in minutes. Negative ttl values indicate that a page
|
||||
type will never expire, and thus the first time a URL is accessed, the result
|
||||
|
@ -781,33 +753,6 @@ will be cached indefinitely, even if the underlying git repository changes.
|
|||
Conversely, when a ttl value is zero, the cache is disabled for that
|
||||
particular page type, and the page type is never cached.
|
||||
|
||||
SIGNATURES
|
||||
----------
|
||||
|
||||
Cgit can host .asc signatures corresponding to various snapshot formats,
|
||||
through use of git notes. For example, the following command may be used to
|
||||
add a signature to a .tar.xz archive:
|
||||
|
||||
git notes --ref=refs/notes/signatures/tar.xz add -C "$(
|
||||
gpg --output - --armor --detach-sign cgit-1.1.tar.xz |
|
||||
git hash-object -w --stdin
|
||||
)" v1.1
|
||||
|
||||
If it is instead desirable to attach a signature of the underlying .tar, this
|
||||
will be linked, as a special case, beside a .tar.* link that does not have its
|
||||
own signature. For example, a signature of a tarball of the latest tag might
|
||||
be added with a similar command:
|
||||
|
||||
tag="$(git describe --abbrev=0)"
|
||||
git notes --ref=refs/notes/signatures/tar add -C "$(
|
||||
git archive --format tar --prefix "cgit-${tag#v}/" "$tag" |
|
||||
gpg --output - --armor --detach-sign |
|
||||
git hash-object -w --stdin
|
||||
)" "$tag"
|
||||
|
||||
Since git-archive(1) is expected to produce stable output between versions,
|
||||
this allows one to generate a long-term signature of the contents of a given
|
||||
tag.
|
||||
|
||||
EXAMPLE CGITRC FILE
|
||||
-------------------
|
||||
|
@ -836,10 +781,6 @@ enable-http-clone=1
|
|||
enable-index-links=1
|
||||
|
||||
|
||||
# Enable blame page and create links to it from tree page
|
||||
enable-blame=1
|
||||
|
||||
|
||||
# Enable ASCII art commit history graph on the log pages
|
||||
enable-commit-graph=1
|
||||
|
||||
|
|
99
cmd.c
99
cmd.c
|
@ -1,6 +1,6 @@
|
|||
/* cmd.c: the cgit command dispatcher
|
||||
*
|
||||
* Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
*
|
||||
* Licensed under GNU General Public License v2
|
||||
* (see COPYING for full license text)
|
||||
|
@ -11,7 +11,6 @@
|
|||
#include "cache.h"
|
||||
#include "ui-shared.h"
|
||||
#include "ui-atom.h"
|
||||
#include "ui-blame.h"
|
||||
#include "ui-blob.h"
|
||||
#include "ui-clone.h"
|
||||
#include "ui-commit.h"
|
||||
|
@ -39,57 +38,30 @@ static void atom_fn(void)
|
|||
|
||||
static void about_fn(void)
|
||||
{
|
||||
if (ctx.repo) {
|
||||
size_t path_info_len = ctx.env.path_info ? strlen(ctx.env.path_info) : 0;
|
||||
if (!ctx.qry.path &&
|
||||
ctx.qry.url[strlen(ctx.qry.url) - 1] != '/' &&
|
||||
(!path_info_len || ctx.env.path_info[path_info_len - 1] != '/')) {
|
||||
char *currenturl = cgit_currenturl();
|
||||
char *redirect = fmtalloc("%s/", currenturl);
|
||||
cgit_redirect(redirect, true);
|
||||
free(currenturl);
|
||||
free(redirect);
|
||||
} else if (ctx.repo->readme.nr)
|
||||
cgit_print_repo_readme(ctx.qry.path);
|
||||
else if (ctx.repo->homepage)
|
||||
cgit_redirect(ctx.repo->homepage, false);
|
||||
else {
|
||||
char *currenturl = cgit_currenturl();
|
||||
char *redirect = fmtalloc("%s../", currenturl);
|
||||
cgit_redirect(redirect, false);
|
||||
free(currenturl);
|
||||
free(redirect);
|
||||
}
|
||||
} else
|
||||
cgit_print_site_readme();
|
||||
}
|
||||
|
||||
static void blame_fn(void)
|
||||
{
|
||||
if (ctx.repo->enable_blame)
|
||||
cgit_print_blame();
|
||||
if (ctx.repo)
|
||||
cgit_print_repo_readme(ctx.qry.path);
|
||||
else
|
||||
cgit_print_error_page(403, "Forbidden", "Blame is disabled");
|
||||
cgit_print_site_readme();
|
||||
}
|
||||
|
||||
static void blob_fn(void)
|
||||
{
|
||||
cgit_print_blob(ctx.qry.oid, ctx.qry.path, ctx.qry.head, 0);
|
||||
cgit_print_blob(ctx.qry.sha1, ctx.qry.path, ctx.qry.head, 0);
|
||||
}
|
||||
|
||||
static void commit_fn(void)
|
||||
{
|
||||
cgit_print_commit(ctx.qry.oid, ctx.qry.path);
|
||||
cgit_print_commit(ctx.qry.sha1, ctx.qry.path);
|
||||
}
|
||||
|
||||
static void diff_fn(void)
|
||||
{
|
||||
cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 0);
|
||||
cgit_print_diff(ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1, 0);
|
||||
}
|
||||
|
||||
static void rawdiff_fn(void)
|
||||
{
|
||||
cgit_print_diff(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path, 1, 1);
|
||||
cgit_print_diff(ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path, 1, 1);
|
||||
}
|
||||
|
||||
static void info_fn(void)
|
||||
|
@ -99,7 +71,7 @@ static void info_fn(void)
|
|||
|
||||
static void log_fn(void)
|
||||
{
|
||||
cgit_print_log(ctx.qry.oid, ctx.qry.ofs, ctx.cfg.max_commit_count,
|
||||
cgit_print_log(ctx.qry.sha1, ctx.qry.ofs, ctx.cfg.max_commit_count,
|
||||
ctx.qry.grep, ctx.qry.search, ctx.qry.path, 1,
|
||||
ctx.repo->enable_commit_graph,
|
||||
ctx.repo->commit_sort);
|
||||
|
@ -125,7 +97,7 @@ static void repolist_fn(void)
|
|||
|
||||
static void patch_fn(void)
|
||||
{
|
||||
cgit_print_patch(ctx.qry.oid, ctx.qry.oid2, ctx.qry.path);
|
||||
cgit_print_patch(ctx.qry.sha1, ctx.qry.sha2, ctx.qry.path);
|
||||
}
|
||||
|
||||
static void plain_fn(void)
|
||||
|
@ -140,7 +112,7 @@ static void refs_fn(void)
|
|||
|
||||
static void snapshot_fn(void)
|
||||
{
|
||||
cgit_print_snapshot(ctx.qry.head, ctx.qry.oid, ctx.qry.path,
|
||||
cgit_print_snapshot(ctx.qry.head, ctx.qry.sha1, ctx.qry.path,
|
||||
ctx.qry.nohead);
|
||||
}
|
||||
|
||||
|
@ -156,41 +128,40 @@ static void summary_fn(void)
|
|||
|
||||
static void tag_fn(void)
|
||||
{
|
||||
cgit_print_tag(ctx.qry.oid);
|
||||
cgit_print_tag(ctx.qry.sha1);
|
||||
}
|
||||
|
||||
static void tree_fn(void)
|
||||
{
|
||||
cgit_print_tree(ctx.qry.oid, ctx.qry.path);
|
||||
cgit_print_tree(ctx.qry.sha1, ctx.qry.path);
|
||||
}
|
||||
|
||||
#define def_cmd(name, want_repo, want_vpath, is_clone) \
|
||||
{#name, name##_fn, want_repo, want_vpath, is_clone}
|
||||
#define def_cmd(name, want_repo, want_layout, want_vpath, is_clone) \
|
||||
{#name, name##_fn, want_repo, want_layout, want_vpath, is_clone}
|
||||
|
||||
struct cgit_cmd *cgit_get_cmd(void)
|
||||
{
|
||||
static struct cgit_cmd cmds[] = {
|
||||
def_cmd(HEAD, 1, 0, 1),
|
||||
def_cmd(atom, 1, 0, 0),
|
||||
def_cmd(about, 0, 0, 0),
|
||||
def_cmd(blame, 1, 1, 0),
|
||||
def_cmd(blob, 1, 0, 0),
|
||||
def_cmd(commit, 1, 1, 0),
|
||||
def_cmd(diff, 1, 1, 0),
|
||||
def_cmd(info, 1, 0, 1),
|
||||
def_cmd(log, 1, 1, 0),
|
||||
def_cmd(ls_cache, 0, 0, 0),
|
||||
def_cmd(objects, 1, 0, 1),
|
||||
def_cmd(patch, 1, 1, 0),
|
||||
def_cmd(plain, 1, 0, 0),
|
||||
def_cmd(rawdiff, 1, 1, 0),
|
||||
def_cmd(refs, 1, 0, 0),
|
||||
def_cmd(repolist, 0, 0, 0),
|
||||
def_cmd(snapshot, 1, 0, 0),
|
||||
def_cmd(stats, 1, 1, 0),
|
||||
def_cmd(summary, 1, 0, 0),
|
||||
def_cmd(tag, 1, 0, 0),
|
||||
def_cmd(tree, 1, 1, 0),
|
||||
def_cmd(HEAD, 1, 0, 0, 1),
|
||||
def_cmd(atom, 1, 0, 0, 0),
|
||||
def_cmd(about, 0, 1, 0, 0),
|
||||
def_cmd(blob, 1, 0, 0, 0),
|
||||
def_cmd(commit, 1, 1, 1, 0),
|
||||
def_cmd(diff, 1, 1, 1, 0),
|
||||
def_cmd(info, 1, 0, 0, 1),
|
||||
def_cmd(log, 1, 1, 1, 0),
|
||||
def_cmd(ls_cache, 0, 0, 0, 0),
|
||||
def_cmd(objects, 1, 0, 0, 1),
|
||||
def_cmd(patch, 1, 0, 1, 0),
|
||||
def_cmd(plain, 1, 0, 0, 0),
|
||||
def_cmd(rawdiff, 1, 0, 1, 0),
|
||||
def_cmd(refs, 1, 1, 0, 0),
|
||||
def_cmd(repolist, 0, 0, 0, 0),
|
||||
def_cmd(snapshot, 1, 0, 0, 0),
|
||||
def_cmd(stats, 1, 1, 1, 0),
|
||||
def_cmd(summary, 1, 1, 0, 0),
|
||||
def_cmd(tag, 1, 1, 0, 0),
|
||||
def_cmd(tree, 1, 1, 1, 0),
|
||||
};
|
||||
int i;
|
||||
|
||||
|
|
1
cmd.h
1
cmd.h
|
@ -7,6 +7,7 @@ struct cgit_cmd {
|
|||
const char *name;
|
||||
cgit_cmd_fn fn;
|
||||
unsigned int want_repo:1,
|
||||
want_layout:1,
|
||||
want_vpath:1,
|
||||
is_clone:1;
|
||||
};
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#include <git-compat-util.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include "configfile.h"
|
||||
|
||||
static int next_char(FILE *f)
|
||||
|
@ -39,9 +40,7 @@ static int read_config_line(FILE *f, struct strbuf *name, struct strbuf *value)
|
|||
|
||||
/* Skip comments and preceding spaces. */
|
||||
for(;;) {
|
||||
if (c == EOF)
|
||||
return 0;
|
||||
else if (c == '#' || c == ';')
|
||||
if (c == '#' || c == ';')
|
||||
skip_line(f);
|
||||
else if (!isspace(c))
|
||||
break;
|
||||
|
|
|
@ -1,19 +0,0 @@
|
|||
#!/bin/sh
|
||||
#
|
||||
# An example hook to update the "agefile" for CGit's idle time calculation.
|
||||
#
|
||||
# This hook assumes that you are using the default agefile location of
|
||||
# "info/web/last-modified". If you change the value in your cgitrc then you
|
||||
# must also change it here.
|
||||
#
|
||||
# To install the hook, copy (or link) it to the file "hooks/post-receive" in
|
||||
# each of your repositories.
|
||||
#
|
||||
|
||||
agefile="$(git rev-parse --git-dir)"/info/web/last-modified
|
||||
|
||||
mkdir -p "$(dirname "$agefile")" &&
|
||||
git for-each-ref \
|
||||
--sort=-authordate --count=1 \
|
||||
--format='%(authordate:iso8601)' \
|
||||
>"$agefile"
|
103
filter.c
103
filter.c
|
@ -8,13 +8,23 @@
|
|||
|
||||
#include "cgit.h"
|
||||
#include "html.h"
|
||||
#ifndef NO_LUA
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#ifndef NO_LUA
|
||||
#include <lua.h>
|
||||
#include <lualib.h>
|
||||
#include <lauxlib.h>
|
||||
#endif
|
||||
|
||||
static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
|
||||
static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
|
||||
static struct cgit_filter *current_write_filter = NULL;
|
||||
|
||||
static inline void reap_filter(struct cgit_filter *filter)
|
||||
{
|
||||
if (filter && filter->cleanup)
|
||||
|
@ -39,10 +49,40 @@ void cgit_cleanup_filters(void)
|
|||
}
|
||||
}
|
||||
|
||||
void cgit_init_filters(void)
|
||||
{
|
||||
libc_write = dlsym(RTLD_NEXT, "write");
|
||||
if (!libc_write)
|
||||
die("Could not locate libc's write function");
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
if (fd != STDOUT_FILENO || !filter_write)
|
||||
return libc_write(fd, buf, count);
|
||||
return filter_write(current_write_filter, buf, count);
|
||||
}
|
||||
|
||||
static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count))
|
||||
{
|
||||
/* We want to avoid buggy nested patterns. */
|
||||
assert(filter_write == NULL);
|
||||
assert(current_write_filter == NULL);
|
||||
current_write_filter = filter;
|
||||
filter_write = new_write;
|
||||
}
|
||||
|
||||
static inline void unhook_write()
|
||||
{
|
||||
assert(filter_write != NULL);
|
||||
assert(current_write_filter != NULL);
|
||||
filter_write = NULL;
|
||||
current_write_filter = NULL;
|
||||
}
|
||||
|
||||
static int open_exec_filter(struct cgit_filter *base, va_list ap)
|
||||
{
|
||||
struct cgit_exec_filter *filter = (struct cgit_exec_filter *)base;
|
||||
int pipe_fh[2];
|
||||
int i;
|
||||
|
||||
for (i = 0; i < filter->base.argument_count; i++)
|
||||
|
@ -50,19 +90,19 @@ static int open_exec_filter(struct cgit_filter *base, va_list ap)
|
|||
|
||||
filter->old_stdout = chk_positive(dup(STDOUT_FILENO),
|
||||
"Unable to duplicate STDOUT");
|
||||
chk_zero(pipe(pipe_fh), "Unable to create pipe to subprocess");
|
||||
chk_zero(pipe(filter->pipe_fh), "Unable to create pipe to subprocess");
|
||||
filter->pid = chk_non_negative(fork(), "Unable to create subprocess");
|
||||
if (filter->pid == 0) {
|
||||
close(pipe_fh[1]);
|
||||
chk_non_negative(dup2(pipe_fh[0], STDIN_FILENO),
|
||||
close(filter->pipe_fh[1]);
|
||||
chk_non_negative(dup2(filter->pipe_fh[0], STDIN_FILENO),
|
||||
"Unable to use pipe as STDIN");
|
||||
execvp(filter->cmd, filter->argv);
|
||||
die_errno("Unable to exec subprocess %s", filter->cmd);
|
||||
}
|
||||
close(pipe_fh[0]);
|
||||
chk_non_negative(dup2(pipe_fh[1], STDOUT_FILENO),
|
||||
close(filter->pipe_fh[0]);
|
||||
chk_non_negative(dup2(filter->pipe_fh[1], STDOUT_FILENO),
|
||||
"Unable to use pipe as STDOUT");
|
||||
close(pipe_fh[1]);
|
||||
close(filter->pipe_fh[1]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -128,7 +168,7 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar
|
|||
memset(filter, 0, sizeof(*filter));
|
||||
filter->base.open = open_exec_filter;
|
||||
filter->base.close = close_exec_filter;
|
||||
filter->base.fprintfp = fprintf_exec_filter;
|
||||
filter->base.fprintf = fprintf_exec_filter;
|
||||
filter->base.cleanup = cleanup_exec_filter;
|
||||
filter->cmd = cmd;
|
||||
filter->argv = argv;
|
||||
|
@ -136,48 +176,7 @@ void cgit_exec_filter_init(struct cgit_exec_filter *filter, char *cmd, char **ar
|
|||
filter->base.argument_count = 0;
|
||||
}
|
||||
|
||||
#ifdef NO_LUA
|
||||
void cgit_init_filters(void)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef NO_LUA
|
||||
static ssize_t (*libc_write)(int fd, const void *buf, size_t count);
|
||||
static ssize_t (*filter_write)(struct cgit_filter *base, const void *buf, size_t count) = NULL;
|
||||
static struct cgit_filter *current_write_filter = NULL;
|
||||
|
||||
void cgit_init_filters(void)
|
||||
{
|
||||
libc_write = dlsym(RTLD_NEXT, "write");
|
||||
if (!libc_write)
|
||||
die("Could not locate libc's write function");
|
||||
}
|
||||
|
||||
ssize_t write(int fd, const void *buf, size_t count)
|
||||
{
|
||||
if (fd != STDOUT_FILENO || !filter_write)
|
||||
return libc_write(fd, buf, count);
|
||||
return filter_write(current_write_filter, buf, count);
|
||||
}
|
||||
|
||||
static inline void hook_write(struct cgit_filter *filter, ssize_t (*new_write)(struct cgit_filter *base, const void *buf, size_t count))
|
||||
{
|
||||
/* We want to avoid buggy nested patterns. */
|
||||
assert(filter_write == NULL);
|
||||
assert(current_write_filter == NULL);
|
||||
current_write_filter = filter;
|
||||
filter_write = new_write;
|
||||
}
|
||||
|
||||
static inline void unhook_write(void)
|
||||
{
|
||||
assert(filter_write != NULL);
|
||||
assert(current_write_filter != NULL);
|
||||
filter_write = NULL;
|
||||
current_write_filter = NULL;
|
||||
}
|
||||
|
||||
struct lua_filter {
|
||||
struct cgit_filter base;
|
||||
char *script_file;
|
||||
|
@ -353,7 +352,7 @@ static struct cgit_filter *new_lua_filter(const char *cmd, int argument_count)
|
|||
memset(filter, 0, sizeof(*filter));
|
||||
filter->base.open = open_lua_filter;
|
||||
filter->base.close = close_lua_filter;
|
||||
filter->base.fprintfp = fprintf_lua_filter;
|
||||
filter->base.fprintf = fprintf_lua_filter;
|
||||
filter->base.cleanup = cleanup_lua_filter;
|
||||
filter->base.argument_count = argument_count;
|
||||
filter->script_file = xstrdup(cmd);
|
||||
|
@ -385,7 +384,7 @@ int cgit_close_filter(struct cgit_filter *filter)
|
|||
|
||||
void cgit_fprintf_filter(struct cgit_filter *filter, FILE *f, const char *prefix)
|
||||
{
|
||||
filter->fprintfp(filter, f, prefix);
|
||||
filter->fprintf(filter, f, prefix);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# This may be used with the about-filter or repo.about-filter setting in cgitrc.
|
||||
# It passes formatting of about pages to differing programs, depending on the usage.
|
||||
|
||||
# Markdown support requires python and markdown-python.
|
||||
# Markdown support requires perl.
|
||||
# RestructuredText support requires python and docutils.
|
||||
# Man page support requires groff.
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ regex=''
|
|||
|
||||
# This expression generates links to commits referenced by their SHA1.
|
||||
regex=$regex'
|
||||
s|\b([0-9a-fA-F]{7,64})\b|<a href="./?id=\1">\1</a>|g'
|
||||
s|\b([0-9a-fA-F]{7,40})\b|<a href="./?id=\1">\1</a>|g'
|
||||
|
||||
# This expression generates links to a fictional bugtracker.
|
||||
regex=$regex'
|
||||
|
|
|
@ -3,24 +3,15 @@
|
|||
-- prefix in filters. It is much faster than the corresponding python script.
|
||||
--
|
||||
-- Requirements:
|
||||
-- luaossl
|
||||
-- <http://25thandclement.com/~william/projects/luaossl.html>
|
||||
-- luacrypto >= 0.3
|
||||
-- <http://mkottman.github.io/luacrypto/>
|
||||
--
|
||||
|
||||
local digest = require("openssl.digest")
|
||||
|
||||
function md5_hex(input)
|
||||
local b = digest.new("md5"):final(input)
|
||||
local x = ""
|
||||
for i = 1, #b do
|
||||
x = x .. string.format("%.2x", string.byte(b, i))
|
||||
end
|
||||
return x
|
||||
end
|
||||
local crypto = require("crypto")
|
||||
|
||||
function filter_open(email, page)
|
||||
buffer = ""
|
||||
md5 = md5_hex(email:sub(2, -2):lower())
|
||||
md5 = crypto.digest("md5", email:sub(2, -2):lower())
|
||||
end
|
||||
|
||||
function filter_close()
|
||||
|
|
|
@ -3,24 +3,15 @@
|
|||
-- prefix in filters.
|
||||
--
|
||||
-- Requirements:
|
||||
-- luaossl
|
||||
-- <http://25thandclement.com/~william/projects/luaossl.html>
|
||||
-- luacrypto >= 0.3
|
||||
-- <http://mkottman.github.io/luacrypto/>
|
||||
--
|
||||
|
||||
local digest = require("openssl.digest")
|
||||
|
||||
function md5_hex(input)
|
||||
local b = digest.new("md5"):final(input)
|
||||
local x = ""
|
||||
for i = 1, #b do
|
||||
x = x .. string.format("%.2x", string.byte(b, i))
|
||||
end
|
||||
return x
|
||||
end
|
||||
local crypto = require("crypto")
|
||||
|
||||
function filter_open(email, page)
|
||||
buffer = ""
|
||||
md5 = md5_hex(email:sub(2, -2):lower())
|
||||
md5 = crypto.digest("md5", email:sub(2, -2):lower())
|
||||
end
|
||||
|
||||
function filter_close()
|
||||
|
|
|
@ -1,359 +0,0 @@
|
|||
-- This script may be used with the auth-filter.
|
||||
--
|
||||
-- Requirements:
|
||||
-- luaossl
|
||||
-- <http://25thandclement.com/~william/projects/luaossl.html>
|
||||
-- luaposix
|
||||
-- <https://github.com/luaposix/luaposix>
|
||||
--
|
||||
local sysstat = require("posix.sys.stat")
|
||||
local unistd = require("posix.unistd")
|
||||
local rand = require("openssl.rand")
|
||||
local hmac = require("openssl.hmac")
|
||||
|
||||
-- This file should contain a series of lines in the form of:
|
||||
-- username1:hash1
|
||||
-- username2:hash2
|
||||
-- username3:hash3
|
||||
-- ...
|
||||
-- Hashes can be generated using something like `mkpasswd -m sha-512 -R 300000`.
|
||||
-- This file should not be world-readable.
|
||||
local users_filename = "/etc/cgit-auth/users"
|
||||
|
||||
-- This file should contain a series of lines in the form of:
|
||||
-- groupname1:username1,username2,username3,...
|
||||
-- ...
|
||||
local groups_filename = "/etc/cgit-auth/groups"
|
||||
|
||||
-- This file should contain a series of lines in the form of:
|
||||
-- reponame1:groupname1,groupname2,groupname3,...
|
||||
-- ...
|
||||
local repos_filename = "/etc/cgit-auth/repos"
|
||||
|
||||
-- Set this to a path this script can write to for storing a persistent
|
||||
-- cookie secret, which should not be world-readable.
|
||||
local secret_filename = "/var/cache/cgit/auth-secret"
|
||||
|
||||
--
|
||||
--
|
||||
-- Authentication functions follow below. Swap these out if you want different authentication semantics.
|
||||
--
|
||||
--
|
||||
|
||||
-- Looks up a hash for a given user.
|
||||
function lookup_hash(user)
|
||||
local line
|
||||
for line in io.lines(users_filename) do
|
||||
local u, h = string.match(line, "(.-):(.+)")
|
||||
if u:lower() == user:lower() then
|
||||
return h
|
||||
end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Looks up users for a given repo.
|
||||
function lookup_users(repo)
|
||||
local users = nil
|
||||
local groups = nil
|
||||
local line, group, user
|
||||
for line in io.lines(repos_filename) do
|
||||
local r, g = string.match(line, "(.-):(.+)")
|
||||
if r == repo then
|
||||
groups = { }
|
||||
for group in string.gmatch(g, "([^,]+)") do
|
||||
groups[group:lower()] = true
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
if groups == nil then
|
||||
return nil
|
||||
end
|
||||
for line in io.lines(groups_filename) do
|
||||
local g, u = string.match(line, "(.-):(.+)")
|
||||
if groups[g:lower()] then
|
||||
if users == nil then
|
||||
users = { }
|
||||
end
|
||||
for user in string.gmatch(u, "([^,]+)") do
|
||||
users[user:lower()] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
return users
|
||||
end
|
||||
|
||||
|
||||
-- Sets HTTP cookie headers based on post and sets up redirection.
|
||||
function authenticate_post()
|
||||
local hash = lookup_hash(post["username"])
|
||||
local redirect = validate_value("redirect", post["redirect"])
|
||||
|
||||
if redirect == nil then
|
||||
not_found()
|
||||
return 0
|
||||
end
|
||||
|
||||
redirect_to(redirect)
|
||||
|
||||
if hash == nil or hash ~= unistd.crypt(post["password"], hash) then
|
||||
set_cookie("cgitauth", "")
|
||||
else
|
||||
-- One week expiration time
|
||||
local username = secure_value("username", post["username"], os.time() + 604800)
|
||||
set_cookie("cgitauth", username)
|
||||
end
|
||||
|
||||
html("\n")
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
-- Returns 1 if the cookie is valid and 0 if it is not.
|
||||
function authenticate_cookie()
|
||||
accepted_users = lookup_users(cgit["repo"])
|
||||
if accepted_users == nil then
|
||||
-- We return as valid if the repo is not protected.
|
||||
return 1
|
||||
end
|
||||
|
||||
local username = validate_value("username", get_cookie(http["cookie"], "cgitauth"))
|
||||
if username == nil or not accepted_users[username:lower()] then
|
||||
return 0
|
||||
else
|
||||
return 1
|
||||
end
|
||||
end
|
||||
|
||||
-- Prints the html for the login form.
|
||||
function body()
|
||||
html("<h2>Authentication Required</h2>")
|
||||
html("<form method='post' action='")
|
||||
html_attr(cgit["login"])
|
||||
html("'>")
|
||||
html("<input type='hidden' name='redirect' value='")
|
||||
html_attr(secure_value("redirect", cgit["url"], 0))
|
||||
html("' />")
|
||||
html("<table>")
|
||||
html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
|
||||
html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
|
||||
html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
|
||||
html("</table></form>")
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
|
||||
--
|
||||
--
|
||||
-- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
|
||||
--
|
||||
--
|
||||
|
||||
local actions = {}
|
||||
actions["authenticate-post"] = authenticate_post
|
||||
actions["authenticate-cookie"] = authenticate_cookie
|
||||
actions["body"] = body
|
||||
|
||||
function filter_open(...)
|
||||
action = actions[select(1, ...)]
|
||||
|
||||
http = {}
|
||||
http["cookie"] = select(2, ...)
|
||||
http["method"] = select(3, ...)
|
||||
http["query"] = select(4, ...)
|
||||
http["referer"] = select(5, ...)
|
||||
http["path"] = select(6, ...)
|
||||
http["host"] = select(7, ...)
|
||||
http["https"] = select(8, ...)
|
||||
|
||||
cgit = {}
|
||||
cgit["repo"] = select(9, ...)
|
||||
cgit["page"] = select(10, ...)
|
||||
cgit["url"] = select(11, ...)
|
||||
cgit["login"] = select(12, ...)
|
||||
|
||||
end
|
||||
|
||||
function filter_close()
|
||||
return action()
|
||||
end
|
||||
|
||||
function filter_write(str)
|
||||
post = parse_qs(str)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
--
|
||||
-- Utility functions based on keplerproject/wsapi.
|
||||
--
|
||||
--
|
||||
|
||||
function url_decode(str)
|
||||
if not str then
|
||||
return ""
|
||||
end
|
||||
str = string.gsub(str, "+", " ")
|
||||
str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
|
||||
str = string.gsub(str, "\r\n", "\n")
|
||||
return str
|
||||
end
|
||||
|
||||
function url_encode(str)
|
||||
if not str then
|
||||
return ""
|
||||
end
|
||||
str = string.gsub(str, "\n", "\r\n")
|
||||
str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
|
||||
str = string.gsub(str, " ", "+")
|
||||
return str
|
||||
end
|
||||
|
||||
function parse_qs(qs)
|
||||
local tab = {}
|
||||
for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
|
||||
tab[url_decode(key)] = url_decode(val)
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
function get_cookie(cookies, name)
|
||||
cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
|
||||
return url_decode(string.match(cookies, ";" .. name .. "=(.-);"))
|
||||
end
|
||||
|
||||
function tohex(b)
|
||||
local x = ""
|
||||
for i = 1, #b do
|
||||
x = x .. string.format("%.2x", string.byte(b, i))
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Cookie construction and validation helpers.
|
||||
--
|
||||
--
|
||||
|
||||
local secret = nil
|
||||
|
||||
-- Loads a secret from a file, creates a secret, or returns one from memory.
|
||||
function get_secret()
|
||||
if secret ~= nil then
|
||||
return secret
|
||||
end
|
||||
local secret_file = io.open(secret_filename, "r")
|
||||
if secret_file == nil then
|
||||
local old_umask = sysstat.umask(63)
|
||||
local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
|
||||
local temporary_file = io.open(temporary_filename, "w")
|
||||
if temporary_file == nil then
|
||||
os.exit(177)
|
||||
end
|
||||
temporary_file:write(tohex(rand.bytes(32)))
|
||||
temporary_file:close()
|
||||
unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
|
||||
unistd.unlink(temporary_filename)
|
||||
sysstat.umask(old_umask)
|
||||
secret_file = io.open(secret_filename, "r")
|
||||
end
|
||||
if secret_file == nil then
|
||||
os.exit(177)
|
||||
end
|
||||
secret = secret_file:read()
|
||||
secret_file:close()
|
||||
if secret:len() ~= 64 then
|
||||
os.exit(177)
|
||||
end
|
||||
return secret
|
||||
end
|
||||
|
||||
-- Returns value of cookie if cookie is valid. Otherwise returns nil.
|
||||
function validate_value(expected_field, cookie)
|
||||
local i = 0
|
||||
local value = ""
|
||||
local field = ""
|
||||
local expiration = 0
|
||||
local salt = ""
|
||||
local chmac = ""
|
||||
|
||||
if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
|
||||
return nil
|
||||
end
|
||||
|
||||
for component in string.gmatch(cookie, "[^|]+") do
|
||||
if i == 0 then
|
||||
field = component
|
||||
elseif i == 1 then
|
||||
value = component
|
||||
elseif i == 2 then
|
||||
expiration = tonumber(component)
|
||||
if expiration == nil then
|
||||
expiration = -1
|
||||
end
|
||||
elseif i == 3 then
|
||||
salt = component
|
||||
elseif i == 4 then
|
||||
chmac = component
|
||||
else
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if chmac == nil or chmac:len() == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Lua hashes strings, so these comparisons are time invariant.
|
||||
if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
|
||||
return nil
|
||||
end
|
||||
|
||||
if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
|
||||
return nil
|
||||
end
|
||||
|
||||
if url_decode(field) ~= expected_field then
|
||||
return nil
|
||||
end
|
||||
|
||||
return url_decode(value)
|
||||
end
|
||||
|
||||
function secure_value(field, value, expiration)
|
||||
if value == nil or value:len() <= 0 then
|
||||
return ""
|
||||
end
|
||||
|
||||
local authstr = ""
|
||||
local salt = tohex(rand.bytes(16))
|
||||
value = url_encode(value)
|
||||
field = url_encode(field)
|
||||
authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
|
||||
authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
|
||||
return authstr
|
||||
end
|
||||
|
||||
function set_cookie(cookie, value)
|
||||
html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
|
||||
if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
|
||||
html("; secure")
|
||||
end
|
||||
html("\n")
|
||||
end
|
||||
|
||||
function redirect_to(url)
|
||||
html("Status: 302 Redirect\n")
|
||||
html("Cache-Control: no-cache, no-store\n")
|
||||
html("Location: " .. url .. "\n")
|
||||
end
|
||||
|
||||
function not_found()
|
||||
html("Status: 404 Not Found\n")
|
||||
html("Cache-Control: no-cache, no-store\n\n")
|
||||
end
|
|
@ -1,360 +0,0 @@
|
|||
-- This script may be used with the auth-filter. Be sure to configure it as you wish.
|
||||
--
|
||||
-- Requirements:
|
||||
-- luaossl
|
||||
-- <http://25thandclement.com/~william/projects/luaossl.html>
|
||||
-- lualdap >= 1.2
|
||||
-- <https://git.zx2c4.com/lualdap/about/>
|
||||
-- luaposix
|
||||
-- <https://github.com/luaposix/luaposix>
|
||||
--
|
||||
local sysstat = require("posix.sys.stat")
|
||||
local unistd = require("posix.unistd")
|
||||
local lualdap = require("lualdap")
|
||||
local rand = require("openssl.rand")
|
||||
local hmac = require("openssl.hmac")
|
||||
|
||||
--
|
||||
--
|
||||
-- Configure these variables for your settings.
|
||||
--
|
||||
--
|
||||
|
||||
-- A list of password protected repositories, with which gentooAccess
|
||||
-- group is allowed to access each one.
|
||||
local protected_repos = {
|
||||
glouglou = "infra",
|
||||
portage = "dev"
|
||||
}
|
||||
|
||||
-- Set this to a path this script can write to for storing a persistent
|
||||
-- cookie secret, which should be guarded.
|
||||
local secret_filename = "/var/cache/cgit/auth-secret"
|
||||
|
||||
|
||||
--
|
||||
--
|
||||
-- Authentication functions follow below. Swap these out if you want different authentication semantics.
|
||||
--
|
||||
--
|
||||
|
||||
-- Sets HTTP cookie headers based on post and sets up redirection.
|
||||
function authenticate_post()
|
||||
local redirect = validate_value("redirect", post["redirect"])
|
||||
|
||||
if redirect == nil then
|
||||
not_found()
|
||||
return 0
|
||||
end
|
||||
|
||||
redirect_to(redirect)
|
||||
|
||||
local groups = gentoo_ldap_user_groups(post["username"], post["password"])
|
||||
if groups == nil then
|
||||
set_cookie("cgitauth", "")
|
||||
else
|
||||
-- One week expiration time
|
||||
set_cookie("cgitauth", secure_value("gentoogroups", table.concat(groups, ","), os.time() + 604800))
|
||||
end
|
||||
|
||||
html("\n")
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
-- Returns 1 if the cookie is valid and 0 if it is not.
|
||||
function authenticate_cookie()
|
||||
local required_group = protected_repos[cgit["repo"]]
|
||||
if required_group == nil then
|
||||
-- We return as valid if the repo is not protected.
|
||||
return 1
|
||||
end
|
||||
|
||||
local user_groups = validate_value("gentoogroups", get_cookie(http["cookie"], "cgitauth"))
|
||||
if user_groups == nil or user_groups == "" then
|
||||
return 0
|
||||
end
|
||||
for group in string.gmatch(user_groups, "[^,]+") do
|
||||
if group == required_group then
|
||||
return 1
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
-- Prints the html for the login form.
|
||||
function body()
|
||||
html("<h2>Gentoo LDAP Authentication Required</h2>")
|
||||
html("<form method='post' action='")
|
||||
html_attr(cgit["login"])
|
||||
html("'>")
|
||||
html("<input type='hidden' name='redirect' value='")
|
||||
html_attr(secure_value("redirect", cgit["url"], 0))
|
||||
html("' />")
|
||||
html("<table>")
|
||||
html("<tr><td><label for='username'>Username:</label></td><td><input id='username' name='username' autofocus /></td></tr>")
|
||||
html("<tr><td><label for='password'>Password:</label></td><td><input id='password' name='password' type='password' /></td></tr>")
|
||||
html("<tr><td colspan='2'><input value='Login' type='submit' /></td></tr>")
|
||||
html("</table></form>")
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Gentoo LDAP support.
|
||||
--
|
||||
--
|
||||
|
||||
function gentoo_ldap_user_groups(username, password)
|
||||
-- Ensure the user is alphanumeric
|
||||
if username == nil or username:match("%W") then
|
||||
return nil
|
||||
end
|
||||
|
||||
local who = "uid=" .. username .. ",ou=devs,dc=gentoo,dc=org"
|
||||
|
||||
local ldap, err = lualdap.open_simple {
|
||||
uri = "ldap://ldap1.gentoo.org",
|
||||
who = who,
|
||||
password = password,
|
||||
starttls = true,
|
||||
certfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.crt",
|
||||
keyfile = "/var/www/uwsgi/cgit/gentoo-ldap/star.gentoo.org.key",
|
||||
cacertfile = "/var/www/uwsgi/cgit/gentoo-ldap/ca.pem"
|
||||
}
|
||||
if ldap == nil then
|
||||
return nil
|
||||
end
|
||||
|
||||
local group_suffix = ".group"
|
||||
local group_suffix_len = group_suffix:len()
|
||||
local groups = {}
|
||||
for dn, attribs in ldap:search { base = who, scope = "subtree" } do
|
||||
local access = attribs["gentooAccess"]
|
||||
if dn == who and access ~= nil then
|
||||
for i, v in ipairs(access) do
|
||||
local vlen = v:len()
|
||||
if vlen > group_suffix_len and v:sub(-group_suffix_len) == group_suffix then
|
||||
table.insert(groups, v:sub(1, vlen - group_suffix_len))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
ldap:close()
|
||||
|
||||
return groups
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Wrapper around filter API, exposing the http table, the cgit table, and the post table to the above functions.
|
||||
--
|
||||
--
|
||||
|
||||
local actions = {}
|
||||
actions["authenticate-post"] = authenticate_post
|
||||
actions["authenticate-cookie"] = authenticate_cookie
|
||||
actions["body"] = body
|
||||
|
||||
function filter_open(...)
|
||||
action = actions[select(1, ...)]
|
||||
|
||||
http = {}
|
||||
http["cookie"] = select(2, ...)
|
||||
http["method"] = select(3, ...)
|
||||
http["query"] = select(4, ...)
|
||||
http["referer"] = select(5, ...)
|
||||
http["path"] = select(6, ...)
|
||||
http["host"] = select(7, ...)
|
||||
http["https"] = select(8, ...)
|
||||
|
||||
cgit = {}
|
||||
cgit["repo"] = select(9, ...)
|
||||
cgit["page"] = select(10, ...)
|
||||
cgit["url"] = select(11, ...)
|
||||
cgit["login"] = select(12, ...)
|
||||
|
||||
end
|
||||
|
||||
function filter_close()
|
||||
return action()
|
||||
end
|
||||
|
||||
function filter_write(str)
|
||||
post = parse_qs(str)
|
||||
end
|
||||
|
||||
|
||||
--
|
||||
--
|
||||
-- Utility functions based on keplerproject/wsapi.
|
||||
--
|
||||
--
|
||||
|
||||
function url_decode(str)
|
||||
if not str then
|
||||
return ""
|
||||
end
|
||||
str = string.gsub(str, "+", " ")
|
||||
str = string.gsub(str, "%%(%x%x)", function(h) return string.char(tonumber(h, 16)) end)
|
||||
str = string.gsub(str, "\r\n", "\n")
|
||||
return str
|
||||
end
|
||||
|
||||
function url_encode(str)
|
||||
if not str then
|
||||
return ""
|
||||
end
|
||||
str = string.gsub(str, "\n", "\r\n")
|
||||
str = string.gsub(str, "([^%w ])", function(c) return string.format("%%%02X", string.byte(c)) end)
|
||||
str = string.gsub(str, " ", "+")
|
||||
return str
|
||||
end
|
||||
|
||||
function parse_qs(qs)
|
||||
local tab = {}
|
||||
for key, val in string.gmatch(qs, "([^&=]+)=([^&=]*)&?") do
|
||||
tab[url_decode(key)] = url_decode(val)
|
||||
end
|
||||
return tab
|
||||
end
|
||||
|
||||
function get_cookie(cookies, name)
|
||||
cookies = string.gsub(";" .. cookies .. ";", "%s*;%s*", ";")
|
||||
return string.match(cookies, ";" .. name .. "=(.-);")
|
||||
end
|
||||
|
||||
function tohex(b)
|
||||
local x = ""
|
||||
for i = 1, #b do
|
||||
x = x .. string.format("%.2x", string.byte(b, i))
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
-- Cookie construction and validation helpers.
|
||||
--
|
||||
--
|
||||
|
||||
local secret = nil
|
||||
|
||||
-- Loads a secret from a file, creates a secret, or returns one from memory.
|
||||
function get_secret()
|
||||
if secret ~= nil then
|
||||
return secret
|
||||
end
|
||||
local secret_file = io.open(secret_filename, "r")
|
||||
if secret_file == nil then
|
||||
local old_umask = sysstat.umask(63)
|
||||
local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
|
||||
local temporary_file = io.open(temporary_filename, "w")
|
||||
if temporary_file == nil then
|
||||
os.exit(177)
|
||||
end
|
||||
temporary_file:write(tohex(rand.bytes(32)))
|
||||
temporary_file:close()
|
||||
unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
|
||||
unistd.unlink(temporary_filename)
|
||||
sysstat.umask(old_umask)
|
||||
secret_file = io.open(secret_filename, "r")
|
||||
end
|
||||
if secret_file == nil then
|
||||
os.exit(177)
|
||||
end
|
||||
secret = secret_file:read()
|
||||
secret_file:close()
|
||||
if secret:len() ~= 64 then
|
||||
os.exit(177)
|
||||
end
|
||||
return secret
|
||||
end
|
||||
|
||||
-- Returns value of cookie if cookie is valid. Otherwise returns nil.
|
||||
function validate_value(expected_field, cookie)
|
||||
local i = 0
|
||||
local value = ""
|
||||
local field = ""
|
||||
local expiration = 0
|
||||
local salt = ""
|
||||
local chmac = ""
|
||||
|
||||
if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
|
||||
return nil
|
||||
end
|
||||
|
||||
for component in string.gmatch(cookie, "[^|]+") do
|
||||
if i == 0 then
|
||||
field = component
|
||||
elseif i == 1 then
|
||||
value = component
|
||||
elseif i == 2 then
|
||||
expiration = tonumber(component)
|
||||
if expiration == nil then
|
||||
expiration = -1
|
||||
end
|
||||
elseif i == 3 then
|
||||
salt = component
|
||||
elseif i == 4 then
|
||||
chmac = component
|
||||
else
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if chmac == nil or chmac:len() == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Lua hashes strings, so these comparisons are time invariant.
|
||||
if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
|
||||
return nil
|
||||
end
|
||||
|
||||
if expiration == -1 or (expiration ~= 0 and expiration <= os.time()) then
|
||||
return nil
|
||||
end
|
||||
|
||||
if url_decode(field) ~= expected_field then
|
||||
return nil
|
||||
end
|
||||
|
||||
return url_decode(value)
|
||||
end
|
||||
|
||||
function secure_value(field, value, expiration)
|
||||
if value == nil or value:len() <= 0 then
|
||||
return ""
|
||||
end
|
||||
|
||||
local authstr = ""
|
||||
local salt = tohex(rand.bytes(16))
|
||||
value = url_encode(value)
|
||||
field = url_encode(field)
|
||||
authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
|
||||
authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
|
||||
return authstr
|
||||
end
|
||||
|
||||
function set_cookie(cookie, value)
|
||||
html("Set-Cookie: " .. cookie .. "=" .. value .. "; HttpOnly")
|
||||
if http["https"] == "yes" or http["https"] == "on" or http["https"] == "1" then
|
||||
html("; secure")
|
||||
end
|
||||
html("\n")
|
||||
end
|
||||
|
||||
function redirect_to(url)
|
||||
html("Status: 302 Redirect\n")
|
||||
html("Cache-Control: no-cache, no-store\n")
|
||||
html("Location: " .. url .. "\n")
|
||||
end
|
||||
|
||||
function not_found()
|
||||
html("Status: 404 Not Found\n")
|
||||
html("Cache-Control: no-cache, no-store\n\n")
|
||||
end
|
|
@ -2,3 +2,4 @@
|
|||
echo "<div style=\"font-family: monospace\">"
|
||||
groff -mandoc -T html -P -r -P -l | egrep -v '(<html>|<head>|<meta|<title>|</title>|</head>|<body>|</body>|</html>|<!DOCTYPE|"http://www.w3.org)'
|
||||
echo "</div>"
|
||||
|
||||
|
|
|
@ -1,304 +1,2 @@
|
|||
#!/usr/bin/env python3
|
||||
import markdown
|
||||
import sys
|
||||
import io
|
||||
from pygments.formatters import HtmlFormatter
|
||||
from markdown.extensions.toc import TocExtension
|
||||
sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8')
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8')
|
||||
sys.stdout.write('''
|
||||
<style>
|
||||
.markdown-body {
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
overflow: hidden;
|
||||
}
|
||||
.markdown-body>*:first-child {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
.markdown-body>*:last-child {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.markdown-body a.absent {
|
||||
color: #c00;
|
||||
}
|
||||
.markdown-body a.anchor {
|
||||
display: block;
|
||||
padding-left: 30px;
|
||||
margin-left: -30px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 {
|
||||
margin: 20px 0 10px;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
cursor: text;
|
||||
position: relative;
|
||||
}
|
||||
.markdown-body h1 .mini-icon-link, .markdown-body h2 .mini-icon-link, .markdown-body h3 .mini-icon-link, .markdown-body h4 .mini-icon-link, .markdown-body h5 .mini-icon-link, .markdown-body h6 .mini-icon-link {
|
||||
display: none;
|
||||
color: #000;
|
||||
}
|
||||
.markdown-body h1:hover a.anchor, .markdown-body h2:hover a.anchor, .markdown-body h3:hover a.anchor, .markdown-body h4:hover a.anchor, .markdown-body h5:hover a.anchor, .markdown-body h6:hover a.anchor {
|
||||
text-decoration: none;
|
||||
line-height: 1;
|
||||
padding-left: 0;
|
||||
margin-left: -22px;
|
||||
top: 15%;
|
||||
}
|
||||
.markdown-body h1:hover a.anchor .mini-icon-link, .markdown-body h2:hover a.anchor .mini-icon-link, .markdown-body h3:hover a.anchor .mini-icon-link, .markdown-body h4:hover a.anchor .mini-icon-link, .markdown-body h5:hover a.anchor .mini-icon-link, .markdown-body h6:hover a.anchor .mini-icon-link {
|
||||
display: inline-block;
|
||||
}
|
||||
div#cgit .markdown-body h1 a.toclink, div#cgit .markdown-body h2 a.toclink, div#cgit .markdown-body h3 a.toclink, div#cgit .markdown-body h4 a.toclink, div#cgit .markdown-body h5 a.toclink, div#cgit .markdown-body h6 a.toclink {
|
||||
color: black;
|
||||
}
|
||||
.markdown-body h1 tt, .markdown-body h1 code, .markdown-body h2 tt, .markdown-body h2 code, .markdown-body h3 tt, .markdown-body h3 code, .markdown-body h4 tt, .markdown-body h4 code, .markdown-body h5 tt, .markdown-body h5 code, .markdown-body h6 tt, .markdown-body h6 code {
|
||||
font-size: inherit;
|
||||
}
|
||||
.markdown-body h1 {
|
||||
font-size: 28px;
|
||||
color: #000;
|
||||
}
|
||||
.markdown-body h2 {
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #ccc;
|
||||
color: #000;
|
||||
}
|
||||
.markdown-body h3 {
|
||||
font-size: 18px;
|
||||
}
|
||||
.markdown-body h4 {
|
||||
font-size: 16px;
|
||||
}
|
||||
.markdown-body h5 {
|
||||
font-size: 14px;
|
||||
}
|
||||
.markdown-body h6 {
|
||||
color: #777;
|
||||
font-size: 14px;
|
||||
}
|
||||
.markdown-body p, .markdown-body blockquote, .markdown-body ul, .markdown-body ol, .markdown-body dl, .markdown-body table, .markdown-body pre {
|
||||
margin: 15px 0;
|
||||
}
|
||||
.markdown-body hr {
|
||||
border: 2px solid #ccc;
|
||||
}
|
||||
.markdown-body>h2:first-child, .markdown-body>h1:first-child, .markdown-body>h1:first-child+h2, .markdown-body>h3:first-child, .markdown-body>h4:first-child, .markdown-body>h5:first-child, .markdown-body>h6:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
.markdown-body a:first-child h1, .markdown-body a:first-child h2, .markdown-body a:first-child h3, .markdown-body a:first-child h4, .markdown-body a:first-child h5, .markdown-body a:first-child h6 {
|
||||
margin-top: 0;
|
||||
padding-top: 0;
|
||||
}
|
||||
.markdown-body h1+p, .markdown-body h2+p, .markdown-body h3+p, .markdown-body h4+p, .markdown-body h5+p, .markdown-body h6+p {
|
||||
margin-top: 0;
|
||||
}
|
||||
.markdown-body li p.first {
|
||||
display: inline-block;
|
||||
}
|
||||
.markdown-body ul, .markdown-body ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
.markdown-body ul.no-list, .markdown-body ol.no-list {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
.markdown-body ul li>:first-child, .markdown-body ul li ul:first-of-type, .markdown-body ul li ol:first-of-type, .markdown-body ol li>:first-child, .markdown-body ol li ul:first-of-type, .markdown-body ol li ol:first-of-type {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.markdown-body ul li p:last-of-type, .markdown-body ol li p:last-of-type {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.markdown-body ul ul, .markdown-body ul ol, .markdown-body ol ol, .markdown-body ol ul {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.markdown-body dl {
|
||||
padding: 0;
|
||||
}
|
||||
.markdown-body dl dt {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
padding: 0;
|
||||
margin: 15px 0 5px;
|
||||
}
|
||||
.markdown-body dl dt:first-child {
|
||||
padding: 0;
|
||||
}
|
||||
.markdown-body dl dt>:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.markdown-body dl dt>:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.markdown-body dl dd {
|
||||
margin: 0 0 15px;
|
||||
padding: 0 15px;
|
||||
}
|
||||
.markdown-body dl dd>:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.markdown-body dl dd>:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.markdown-body blockquote {
|
||||
border-left: 4px solid #DDD;
|
||||
padding: 0 15px;
|
||||
color: #777;
|
||||
}
|
||||
.markdown-body blockquote>:first-child {
|
||||
margin-top: 0px;
|
||||
}
|
||||
.markdown-body blockquote>:last-child {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.markdown-body table th {
|
||||
font-weight: bold;
|
||||
}
|
||||
.markdown-body table th, .markdown-body table td {
|
||||
border: 1px solid #ccc;
|
||||
padding: 6px 13px;
|
||||
}
|
||||
.markdown-body table tr {
|
||||
border-top: 1px solid #ccc;
|
||||
background-color: #fff;
|
||||
}
|
||||
.markdown-body table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8;
|
||||
}
|
||||
.markdown-body img {
|
||||
max-width: 100%;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.markdown-body span.frame {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
}
|
||||
.markdown-body span.frame>span {
|
||||
border: 1px solid #ddd;
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
padding: 7px;
|
||||
width: auto;
|
||||
}
|
||||
.markdown-body span.frame span img {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
.markdown-body span.frame span span {
|
||||
clear: both;
|
||||
color: #333;
|
||||
display: block;
|
||||
padding: 5px 0 0;
|
||||
}
|
||||
.markdown-body span.align-center {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
.markdown-body span.align-center>span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: center;
|
||||
}
|
||||
.markdown-body span.align-center span img {
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
}
|
||||
.markdown-body span.align-right {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both;
|
||||
}
|
||||
.markdown-body span.align-right>span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
text-align: right;
|
||||
}
|
||||
.markdown-body span.align-right span img {
|
||||
margin: 0;
|
||||
text-align: right;
|
||||
}
|
||||
.markdown-body span.float-left {
|
||||
display: block;
|
||||
margin-right: 13px;
|
||||
overflow: hidden;
|
||||
float: left;
|
||||
}
|
||||
.markdown-body span.float-left span {
|
||||
margin: 13px 0 0;
|
||||
}
|
||||
.markdown-body span.float-right {
|
||||
display: block;
|
||||
margin-left: 13px;
|
||||
overflow: hidden;
|
||||
float: right;
|
||||
}
|
||||
.markdown-body span.float-right>span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: right;
|
||||
}
|
||||
.markdown-body code, .markdown-body tt {
|
||||
margin: 0 2px;
|
||||
padding: 0px 5px;
|
||||
border: 1px solid #eaeaea;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.markdown-body code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.markdown-body pre>code {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: pre;
|
||||
border: none;
|
||||
background: transparent;
|
||||
}
|
||||
.markdown-body .highlight pre, .markdown-body pre {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #ccc;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
overflow: auto;
|
||||
padding: 6px 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.markdown-body pre code, .markdown-body pre tt {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
border: none;
|
||||
}
|
||||
''')
|
||||
sys.stdout.write(HtmlFormatter(style='pastie').get_style_defs('.highlight'))
|
||||
sys.stdout.write('''
|
||||
</style>
|
||||
''')
|
||||
sys.stdout.write("<div class='markdown-body'>")
|
||||
sys.stdout.flush()
|
||||
# Note: you may want to run this through bleach for sanitization
|
||||
markdown.markdownFromFile(
|
||||
output_format="html5",
|
||||
extensions=[
|
||||
"markdown.extensions.fenced_code",
|
||||
"markdown.extensions.codehilite",
|
||||
"markdown.extensions.tables",
|
||||
"markdown.extensions.sane_lists",
|
||||
TocExtension(anchorlink=True)],
|
||||
extension_configs={
|
||||
"markdown.extensions.codehilite":{"css_class":"highlight"}})
|
||||
sys.stdout.write("</div>")
|
||||
#!/bin/sh
|
||||
exec "$(dirname "$0")/resources/markdown.pl"
|
||||
|
|
1727
filters/html-converters/resources/markdown.pl
Executable file
1727
filters/html-converters/resources/markdown.pl
Executable file
File diff suppressed because it is too large
Load diff
4
filters/html-converters/resources/rst-template.txt
Normal file
4
filters/html-converters/resources/rst-template.txt
Normal file
|
@ -0,0 +1,4 @@
|
|||
%(stylesheet)s
|
||||
%(body_pre_docinfo)s
|
||||
%(docinfo)s
|
||||
%(body)s
|
|
@ -1,2 +1,2 @@
|
|||
#!/bin/bash
|
||||
exec rst2html.py --template <(echo -e "%(stylesheet)s\n%(body_pre_docinfo)s\n%(docinfo)s\n%(body)s")
|
||||
#!/bin/sh
|
||||
rst2html.py --template="$(dirname $0)/resources/rst-template.txt"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#!/bin/sh
|
||||
echo "<pre>"
|
||||
sed "s|&|\\&|g;s|'|\\'|g;s|\"|\\"|g;s|<|\\<|g;s|>|\\>|g"
|
||||
cat
|
||||
echo "</pre>"
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
-- This script may be used with the auth-filter. Be sure to configure it as you wish.
|
||||
--
|
||||
-- Requirements:
|
||||
-- luaossl
|
||||
-- <http://25thandclement.com/~william/projects/luaossl.html>
|
||||
-- luaposix
|
||||
-- <https://github.com/luaposix/luaposix>
|
||||
-- luacrypto >= 0.3
|
||||
-- <http://mkottman.github.io/luacrypto/>
|
||||
--
|
||||
local sysstat = require("posix.sys.stat")
|
||||
local unistd = require("posix.unistd")
|
||||
local rand = require("openssl.rand")
|
||||
local hmac = require("openssl.hmac")
|
||||
|
||||
|
||||
--
|
||||
--
|
||||
|
@ -23,16 +18,24 @@ local protected_repos = {
|
|||
qt = { jason = true, bob = true }
|
||||
}
|
||||
|
||||
-- A list of users and hashes, generated with `mkpasswd -m sha-512 -R 300000`.
|
||||
-- Please note that, in production, you'll want to replace this simple lookup
|
||||
-- table with either a table of salted and hashed passwords (using something
|
||||
-- smart like scrypt), or replace this table lookup with an external support,
|
||||
-- such as consulting your system's pam / shadow system, or an external
|
||||
-- database, or an external validating web service. For testing, or for
|
||||
-- extremely low-security usage, you may be able, however, to get away with
|
||||
-- compromising on hardcoding the passwords in cleartext, as we have done here.
|
||||
local users = {
|
||||
jason = "$6$rounds=300000$YYJct3n/o.ruYK$HhpSeuCuW1fJkpvMZOZzVizeLsBKcGA/aF2UPuV5v60JyH2MVSG6P511UMTj2F3H75.IT2HIlnvXzNb60FcZH1",
|
||||
laurent = "$6$rounds=300000$dP0KNHwYb3JKigT$pN/LG7rWxQ4HniFtx5wKyJXBJUKP7R01zTNZ0qSK/aivw8ywGAOdfYiIQFqFhZFtVGvr11/7an.nesvm8iJUi.",
|
||||
bob = "$6$rounds=300000$jCLCCt6LUpTz$PI1vvd1yaVYcCzqH8QAJFcJ60b6W/6sjcOsU7mAkNo7IE8FRGW1vkjF8I/T5jt/auv5ODLb1L4S2s.CAyZyUC"
|
||||
jason = "secretpassword",
|
||||
laurent = "s3cr3t",
|
||||
bob = "ilikelua"
|
||||
}
|
||||
|
||||
-- Set this to a path this script can write to for storing a persistent
|
||||
-- cookie secret, which should be guarded.
|
||||
local secret_filename = "/var/cache/cgit/auth-secret"
|
||||
-- All cookies will be authenticated based on this secret. Make it something
|
||||
-- totally random and impossible to guess. It should be large.
|
||||
local secret = "BE SURE TO CUSTOMIZE THIS STRING TO SOMETHING BIG AND RANDOM"
|
||||
|
||||
|
||||
|
||||
--
|
||||
--
|
||||
|
@ -42,7 +45,7 @@ local secret_filename = "/var/cache/cgit/auth-secret"
|
|||
|
||||
-- Sets HTTP cookie headers based on post and sets up redirection.
|
||||
function authenticate_post()
|
||||
local hash = users[post["username"]]
|
||||
local password = users[post["username"]]
|
||||
local redirect = validate_value("redirect", post["redirect"])
|
||||
|
||||
if redirect == nil then
|
||||
|
@ -52,7 +55,8 @@ function authenticate_post()
|
|||
|
||||
redirect_to(redirect)
|
||||
|
||||
if hash == nil or hash ~= unistd.crypt(post["password"], hash) then
|
||||
-- Lua hashes strings, so these comparisons are time invariant.
|
||||
if password == nil or password ~= post["password"] then
|
||||
set_cookie("cgitauth", "")
|
||||
else
|
||||
-- One week expiration time
|
||||
|
@ -180,13 +184,6 @@ function get_cookie(cookies, name)
|
|||
return url_decode(string.match(cookies, ";" .. name .. "=(.-);"))
|
||||
end
|
||||
|
||||
function tohex(b)
|
||||
local x = ""
|
||||
for i = 1, #b do
|
||||
x = x .. string.format("%.2x", string.byte(b, i))
|
||||
end
|
||||
return x
|
||||
end
|
||||
|
||||
--
|
||||
--
|
||||
|
@ -194,38 +191,7 @@ end
|
|||
--
|
||||
--
|
||||
|
||||
local secret = nil
|
||||
|
||||
-- Loads a secret from a file, creates a secret, or returns one from memory.
|
||||
function get_secret()
|
||||
if secret ~= nil then
|
||||
return secret
|
||||
end
|
||||
local secret_file = io.open(secret_filename, "r")
|
||||
if secret_file == nil then
|
||||
local old_umask = sysstat.umask(63)
|
||||
local temporary_filename = secret_filename .. ".tmp." .. tohex(rand.bytes(16))
|
||||
local temporary_file = io.open(temporary_filename, "w")
|
||||
if temporary_file == nil then
|
||||
os.exit(177)
|
||||
end
|
||||
temporary_file:write(tohex(rand.bytes(32)))
|
||||
temporary_file:close()
|
||||
unistd.link(temporary_filename, secret_filename) -- Intentionally fails in the case that another process is doing the same.
|
||||
unistd.unlink(temporary_filename)
|
||||
sysstat.umask(old_umask)
|
||||
secret_file = io.open(secret_filename, "r")
|
||||
end
|
||||
if secret_file == nil then
|
||||
os.exit(177)
|
||||
end
|
||||
secret = secret_file:read()
|
||||
secret_file:close()
|
||||
if secret:len() ~= 64 then
|
||||
os.exit(177)
|
||||
end
|
||||
return secret
|
||||
end
|
||||
local crypto = require("crypto")
|
||||
|
||||
-- Returns value of cookie if cookie is valid. Otherwise returns nil.
|
||||
function validate_value(expected_field, cookie)
|
||||
|
@ -234,7 +200,7 @@ function validate_value(expected_field, cookie)
|
|||
local field = ""
|
||||
local expiration = 0
|
||||
local salt = ""
|
||||
local chmac = ""
|
||||
local hmac = ""
|
||||
|
||||
if cookie == nil or cookie:len() < 3 or cookie:sub(1, 1) == "|" then
|
||||
return nil
|
||||
|
@ -253,19 +219,19 @@ function validate_value(expected_field, cookie)
|
|||
elseif i == 3 then
|
||||
salt = component
|
||||
elseif i == 4 then
|
||||
chmac = component
|
||||
hmac = component
|
||||
else
|
||||
break
|
||||
end
|
||||
i = i + 1
|
||||
end
|
||||
|
||||
if chmac == nil or chmac:len() == 0 then
|
||||
if hmac == nil or hmac:len() == 0 then
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Lua hashes strings, so these comparisons are time invariant.
|
||||
if chmac ~= tohex(hmac.new(get_secret(), "sha256"):final(field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt)) then
|
||||
if hmac ~= crypto.hmac.digest("sha1", field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt, secret) then
|
||||
return nil
|
||||
end
|
||||
|
||||
|
@ -286,11 +252,11 @@ function secure_value(field, value, expiration)
|
|||
end
|
||||
|
||||
local authstr = ""
|
||||
local salt = tohex(rand.bytes(16))
|
||||
local salt = crypto.hex(crypto.rand.bytes(16))
|
||||
value = url_encode(value)
|
||||
field = url_encode(field)
|
||||
authstr = field .. "|" .. value .. "|" .. tostring(expiration) .. "|" .. salt
|
||||
authstr = authstr .. "|" .. tohex(hmac.new(get_secret(), "sha256"):final(authstr))
|
||||
authstr = authstr .. "|" .. crypto.hmac.digest("sha1", authstr, secret)
|
||||
return authstr
|
||||
end
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/usr/bin/env python3
|
||||
#!/usr/bin/env python2
|
||||
|
||||
# This script uses Pygments and Python3. You must have both installed
|
||||
# This script uses Pygments and Python2. You must have both installed
|
||||
# for this to work.
|
||||
#
|
||||
# http://pygments.org/
|
||||
|
@ -21,7 +21,6 @@
|
|||
|
||||
|
||||
import sys
|
||||
import io
|
||||
from pygments import highlight
|
||||
from pygments.util import ClassNotFound
|
||||
from pygments.lexers import TextLexer
|
||||
|
@ -30,26 +29,25 @@ from pygments.lexers import guess_lexer_for_filename
|
|||
from pygments.formatters import HtmlFormatter
|
||||
|
||||
|
||||
sys.stdin = io.TextIOWrapper(sys.stdin.buffer, encoding='utf-8', errors='replace')
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace')
|
||||
data = sys.stdin.read()
|
||||
# read stdin and decode to utf-8. ignore any unkown signs.
|
||||
data = sys.stdin.read().decode(encoding='utf-8', errors='ignore')
|
||||
filename = sys.argv[1]
|
||||
formatter = HtmlFormatter(style='pastie', nobackground=True)
|
||||
formatter = HtmlFormatter(encoding='utf-8', style='pastie')
|
||||
|
||||
try:
|
||||
lexer = guess_lexer_for_filename(filename, data)
|
||||
lexer = guess_lexer_for_filename(filename, data, encoding='utf-8')
|
||||
except ClassNotFound:
|
||||
# check if there is any shebang
|
||||
if data[0:2] == '#!':
|
||||
lexer = guess_lexer(data)
|
||||
lexer = guess_lexer(data, encoding='utf-8')
|
||||
else:
|
||||
lexer = TextLexer()
|
||||
lexer = TextLexer(encoding='utf-8')
|
||||
except TypeError:
|
||||
lexer = TextLexer()
|
||||
lexer = TextLexer(encoding='utf-8')
|
||||
|
||||
# highlight! :-)
|
||||
# printout pygments' css definitions as well
|
||||
sys.stdout.write('<style>')
|
||||
sys.stdout.write(formatter.get_style_defs('.highlight'))
|
||||
sys.stdout.write('</style>')
|
||||
sys.stdout.write(highlight(data, lexer, formatter, outfile=None))
|
||||
highlight(data, lexer, formatter, outfile=sys.stdout)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
# This script can be used to implement syntax highlighting in the cgit
|
||||
# tree-view by referring to this file with the source-filter or repo.source-
|
||||
# tree-view by refering to this file with the source-filter or repo.source-
|
||||
# filter options in cgitrc.
|
||||
#
|
||||
# This script requires a shell supporting the ${var##pattern} syntax.
|
||||
|
|
2
git
2
git
|
@ -1 +1 @@
|
|||
Subproject commit 39bf06adf96da25b87c9aa7d35a32ef3683eb4a4
|
||||
Subproject commit 8004647a217c26f708bce2aa26ed94a4a0ed575d
|
195
html.c
195
html.c
|
@ -8,42 +8,37 @@
|
|||
|
||||
#include "cgit.h"
|
||||
#include "html.h"
|
||||
#include "url.h"
|
||||
#include <unistd.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
/* Percent-encoding of each character, except: a-zA-Z0-9!$()*,./:;@- */
|
||||
static const char* url_escape_table[256] = {
|
||||
"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07",
|
||||
"%08", "%09", "%0a", "%0b", "%0c", "%0d", "%0e", "%0f",
|
||||
"%10", "%11", "%12", "%13", "%14", "%15", "%16", "%17",
|
||||
"%18", "%19", "%1a", "%1b", "%1c", "%1d", "%1e", "%1f",
|
||||
"%20", NULL, "%22", "%23", NULL, "%25", "%26", "%27",
|
||||
NULL, NULL, NULL, "%2b", NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, "%3c", "%3d", "%3e", "%3f",
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, "%5c", NULL, "%5e", NULL,
|
||||
"%60", NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, "%7b", "%7c", "%7d", NULL, "%7f",
|
||||
"%80", "%81", "%82", "%83", "%84", "%85", "%86", "%87",
|
||||
"%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
|
||||
"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97",
|
||||
"%98", "%99", "%9a", "%9b", "%9c", "%9d", "%9e", "%9f",
|
||||
"%a0", "%a1", "%a2", "%a3", "%a4", "%a5", "%a6", "%a7",
|
||||
"%a8", "%a9", "%aa", "%ab", "%ac", "%ad", "%ae", "%af",
|
||||
"%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
|
||||
"%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf",
|
||||
"%c0", "%c1", "%c2", "%c3", "%c4", "%c5", "%c6", "%c7",
|
||||
"%c8", "%c9", "%ca", "%cb", "%cc", "%cd", "%ce", "%cf",
|
||||
"%d0", "%d1", "%d2", "%d3", "%d4", "%d5", "%d6", "%d7",
|
||||
"%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
|
||||
"%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7",
|
||||
"%e8", "%e9", "%ea", "%eb", "%ec", "%ed", "%ee", "%ef",
|
||||
"%f0", "%f1", "%f2", "%f3", "%f4", "%f5", "%f6", "%f7",
|
||||
"%f8", "%f9", "%fa", "%fb", "%fc", "%fd", "%fe", "%ff"
|
||||
"%00", "%01", "%02", "%03", "%04", "%05", "%06", "%07", "%08", "%09",
|
||||
"%0a", "%0b", "%0c", "%0d", "%0e", "%0f", "%10", "%11", "%12", "%13",
|
||||
"%14", "%15", "%16", "%17", "%18", "%19", "%1a", "%1b", "%1c", "%1d",
|
||||
"%1e", "%1f", "%20", 0, "%22", "%23", 0, "%25", "%26", "%27", 0, 0, 0,
|
||||
"%2b", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%3c", "%3d",
|
||||
"%3e", "%3f", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, "%5c", 0, "%5e", 0, "%60", 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "%7b",
|
||||
"%7c", "%7d", 0, "%7f", "%80", "%81", "%82", "%83", "%84", "%85",
|
||||
"%86", "%87", "%88", "%89", "%8a", "%8b", "%8c", "%8d", "%8e", "%8f",
|
||||
"%90", "%91", "%92", "%93", "%94", "%95", "%96", "%97", "%98", "%99",
|
||||
"%9a", "%9b", "%9c", "%9d", "%9e", "%9f", "%a0", "%a1", "%a2", "%a3",
|
||||
"%a4", "%a5", "%a6", "%a7", "%a8", "%a9", "%aa", "%ab", "%ac", "%ad",
|
||||
"%ae", "%af", "%b0", "%b1", "%b2", "%b3", "%b4", "%b5", "%b6", "%b7",
|
||||
"%b8", "%b9", "%ba", "%bb", "%bc", "%bd", "%be", "%bf", "%c0", "%c1",
|
||||
"%c2", "%c3", "%c4", "%c5", "%c6", "%c7", "%c8", "%c9", "%ca", "%cb",
|
||||
"%cc", "%cd", "%ce", "%cf", "%d0", "%d1", "%d2", "%d3", "%d4", "%d5",
|
||||
"%d6", "%d7", "%d8", "%d9", "%da", "%db", "%dc", "%dd", "%de", "%df",
|
||||
"%e0", "%e1", "%e2", "%e3", "%e4", "%e5", "%e6", "%e7", "%e8", "%e9",
|
||||
"%ea", "%eb", "%ec", "%ed", "%ee", "%ef", "%f0", "%f1", "%f2", "%f3",
|
||||
"%f4", "%f5", "%f6", "%f7", "%f8", "%f9", "%fa", "%fb", "%fc", "%fd",
|
||||
"%fe", "%ff"
|
||||
};
|
||||
|
||||
char *fmt(const char *format, ...)
|
||||
|
@ -59,7 +54,7 @@ char *fmt(const char *format, ...)
|
|||
va_start(args, format);
|
||||
len = vsnprintf(buf[bufidx], sizeof(buf[bufidx]), format, args);
|
||||
va_end(args);
|
||||
if (len >= sizeof(buf[bufidx])) {
|
||||
if (len > sizeof(buf[bufidx])) {
|
||||
fprintf(stderr, "[html.c] string truncated: %s\n", format);
|
||||
exit(1);
|
||||
}
|
||||
|
@ -122,22 +117,38 @@ void html_vtxtf(const char *format, va_list ap)
|
|||
strbuf_release(&buf);
|
||||
}
|
||||
|
||||
void html_txt(const char *txt)
|
||||
void html_status(int code, const char *msg, int more_headers)
|
||||
{
|
||||
if (txt)
|
||||
html_ntxt(txt, strlen(txt));
|
||||
htmlf("Status: %d %s\n", code, msg);
|
||||
if (!more_headers)
|
||||
html("\n");
|
||||
}
|
||||
|
||||
ssize_t html_ntxt(const char *txt, size_t len)
|
||||
void html_txt(const char *txt)
|
||||
{
|
||||
const char *t = txt;
|
||||
ssize_t slen;
|
||||
while (t && *t) {
|
||||
int c = *t;
|
||||
if (c == '<' || c == '>' || c == '&') {
|
||||
html_raw(txt, t - txt);
|
||||
if (c == '>')
|
||||
html(">");
|
||||
else if (c == '<')
|
||||
html("<");
|
||||
else if (c == '&')
|
||||
html("&");
|
||||
txt = t + 1;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
if (t != txt)
|
||||
html(txt);
|
||||
}
|
||||
|
||||
if (len > SSIZE_MAX)
|
||||
return -1;
|
||||
|
||||
slen = (ssize_t) len;
|
||||
while (t && *t && slen--) {
|
||||
void html_ntxt(int len, const char *txt)
|
||||
{
|
||||
const char *t = txt;
|
||||
while (t && *t && len--) {
|
||||
int c = *t;
|
||||
if (c == '<' || c == '>' || c == '&') {
|
||||
html_raw(txt, t - txt);
|
||||
|
@ -153,7 +164,8 @@ ssize_t html_ntxt(const char *txt, size_t len)
|
|||
}
|
||||
if (t != txt)
|
||||
html_raw(txt, t - txt);
|
||||
return slen;
|
||||
if (len < 0)
|
||||
html("...");
|
||||
}
|
||||
|
||||
void html_attrf(const char *fmt, ...)
|
||||
|
@ -230,32 +242,6 @@ void html_url_arg(const char *txt)
|
|||
html(txt);
|
||||
}
|
||||
|
||||
void html_header_arg_in_quotes(const char *txt)
|
||||
{
|
||||
const char *t = txt;
|
||||
while (t && *t) {
|
||||
unsigned char c = *t;
|
||||
const char *e = NULL;
|
||||
if (c == '\\')
|
||||
e = "\\\\";
|
||||
else if (c == '\r')
|
||||
e = "\\r";
|
||||
else if (c == '\n')
|
||||
e = "\\n";
|
||||
else if (c == '"')
|
||||
e = "\\\"";
|
||||
if (e) {
|
||||
html_raw(txt, t - txt);
|
||||
html(e);
|
||||
txt = t + 1;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
if (t != txt)
|
||||
html(txt);
|
||||
|
||||
}
|
||||
|
||||
void html_hidden(const char *name, const char *value)
|
||||
{
|
||||
html("<input type='hidden' name='");
|
||||
|
@ -328,17 +314,64 @@ int html_include(const char *filename)
|
|||
return 0;
|
||||
}
|
||||
|
||||
void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value))
|
||||
static int hextoint(char c)
|
||||
{
|
||||
const char *t = txt;
|
||||
if (c >= 'a' && c <= 'f')
|
||||
return 10 + c - 'a';
|
||||
else if (c >= 'A' && c <= 'F')
|
||||
return 10 + c - 'A';
|
||||
else if (c >= '0' && c <= '9')
|
||||
return c - '0';
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (t && *t) {
|
||||
char *name = url_decode_parameter_name(&t);
|
||||
if (*name) {
|
||||
char *value = url_decode_parameter_value(&t);
|
||||
fn(name, value);
|
||||
free(value);
|
||||
}
|
||||
free(name);
|
||||
static char *convert_query_hexchar(char *txt)
|
||||
{
|
||||
int d1, d2, n;
|
||||
n = strlen(txt);
|
||||
if (n < 3) {
|
||||
*txt = '\0';
|
||||
return txt-1;
|
||||
}
|
||||
d1 = hextoint(*(txt + 1));
|
||||
d2 = hextoint(*(txt + 2));
|
||||
if (d1 < 0 || d2 < 0) {
|
||||
memmove(txt, txt + 3, n - 2);
|
||||
return txt-1;
|
||||
} else {
|
||||
*txt = d1 * 16 + d2;
|
||||
memmove(txt + 1, txt + 3, n - 2);
|
||||
return txt;
|
||||
}
|
||||
}
|
||||
|
||||
int http_parse_querystring(const char *txt_, void (*fn)(const char *name, const char *value))
|
||||
{
|
||||
char *o, *t, *txt, *value = NULL, c;
|
||||
|
||||
if (!txt_)
|
||||
return 0;
|
||||
|
||||
o = t = txt = xstrdup(txt_);
|
||||
while ((c=*t) != '\0') {
|
||||
if (c == '=') {
|
||||
*t = '\0';
|
||||
value = t + 1;
|
||||
} else if (c == '+') {
|
||||
*t = ' ';
|
||||
} else if (c == '%') {
|
||||
t = convert_query_hexchar(t);
|
||||
} else if (c == '&') {
|
||||
*t = '\0';
|
||||
(*fn)(txt, value);
|
||||
txt = t + 1;
|
||||
value = NULL;
|
||||
}
|
||||
t++;
|
||||
}
|
||||
if (t != txt)
|
||||
(*fn)(txt, value);
|
||||
free(o);
|
||||
return 0;
|
||||
}
|
||||
|
|
6
html.h
6
html.h
|
@ -18,12 +18,12 @@ extern void html_vtxtf(const char *format, va_list ap);
|
|||
__attribute__((format (printf,1,2)))
|
||||
extern void html_attrf(const char *format,...);
|
||||
|
||||
extern void html_status(int code, const char *msg, int more_headers);
|
||||
extern void html_txt(const char *txt);
|
||||
extern ssize_t html_ntxt(const char *txt, size_t len);
|
||||
extern void html_ntxt(int len, const char *txt);
|
||||
extern void html_attr(const char *txt);
|
||||
extern void html_url_path(const char *txt);
|
||||
extern void html_url_arg(const char *txt);
|
||||
extern void html_header_arg_in_quotes(const char *txt);
|
||||
extern void html_hidden(const char *name, const char *value);
|
||||
extern void html_option(const char *value, const char *text, const char *selected_value);
|
||||
extern void html_intoption(int value, const char *text, int selected_value);
|
||||
|
@ -32,6 +32,6 @@ extern void html_link_close(void);
|
|||
extern void html_fileperm(unsigned short mode);
|
||||
extern int html_include(const char *filename);
|
||||
|
||||
extern void http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value));
|
||||
extern int http_parse_querystring(const char *txt, void (*fn)(const char *name, const char *value));
|
||||
|
||||
#endif /* HTML_H */
|
||||
|
|
31
parsing.c
31
parsing.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
|
||||
/*
|
||||
|
@ -22,10 +20,10 @@ void cgit_parse_url(const char *url)
|
|||
char *c, *cmd, *p;
|
||||
struct cgit_repo *repo;
|
||||
|
||||
ctx.repo = NULL;
|
||||
if (!url || url[0] == '\0')
|
||||
return;
|
||||
|
||||
ctx.qry.page = NULL;
|
||||
ctx.repo = cgit_get_repoinfo(url);
|
||||
if (ctx.repo) {
|
||||
ctx.qry.repo = ctx.repo->url;
|
||||
|
@ -55,6 +53,7 @@ void cgit_parse_url(const char *url)
|
|||
}
|
||||
if (cmd[1])
|
||||
ctx.qry.page = xstrdup(cmd + 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,11 +64,12 @@ static char *substr(const char *head, const char *tail)
|
|||
if (tail < head)
|
||||
return xstrdup("");
|
||||
buf = xmalloc(tail - head + 1);
|
||||
strlcpy(buf, head, tail - head + 1);
|
||||
strncpy(buf, head, tail - head);
|
||||
buf[tail - head] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void parse_user(const char *t, char **name, char **email, unsigned long *date, int *tz)
|
||||
static void parse_user(const char *t, char **name, char **email, unsigned long *date)
|
||||
{
|
||||
struct ident_split ident;
|
||||
unsigned email_len;
|
||||
|
@ -79,12 +79,10 @@ static void parse_user(const char *t, char **name, char **email, unsigned long *
|
|||
|
||||
email_len = ident.mail_end - ident.mail_begin;
|
||||
*email = xmalloc(strlen("<") + email_len + strlen(">") + 1);
|
||||
xsnprintf(*email, email_len + 3, "<%.*s>", email_len, ident.mail_begin);
|
||||
sprintf(*email, "<%.*s>", email_len, ident.mail_begin);
|
||||
|
||||
if (ident.date_begin)
|
||||
*date = strtoul(ident.date_begin, NULL, 10);
|
||||
if (ident.tz_begin)
|
||||
*tz = atoi(ident.tz_begin);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -129,8 +127,9 @@ static int end_of_header(const char *p)
|
|||
|
||||
struct commitinfo *cgit_parse_commit(struct commit *commit)
|
||||
{
|
||||
const int sha1hex_len = 40;
|
||||
struct commitinfo *ret;
|
||||
const char *p = repo_get_commit_buffer(the_repository, commit, NULL);
|
||||
const char *p = get_cached_commit_buffer(commit, NULL);
|
||||
const char *t;
|
||||
|
||||
ret = xcalloc(1, sizeof(struct commitinfo));
|
||||
|
@ -140,21 +139,21 @@ struct commitinfo *cgit_parse_commit(struct commit *commit)
|
|||
return ret;
|
||||
|
||||
if (!skip_prefix(p, "tree ", &p))
|
||||
die("Bad commit: %s", oid_to_hex(&commit->object.oid));
|
||||
p += the_hash_algo->hexsz + 1;
|
||||
die("Bad commit: %s", sha1_to_hex(commit->object.sha1));
|
||||
p += sha1hex_len + 1;
|
||||
|
||||
while (skip_prefix(p, "parent ", &p))
|
||||
p += the_hash_algo->hexsz + 1;
|
||||
p += sha1hex_len + 1;
|
||||
|
||||
if (p && skip_prefix(p, "author ", &p)) {
|
||||
parse_user(p, &ret->author, &ret->author_email,
|
||||
&ret->author_date, &ret->author_tz);
|
||||
&ret->author_date);
|
||||
p = next_header_line(p);
|
||||
}
|
||||
|
||||
if (p && skip_prefix(p, "committer ", &p)) {
|
||||
parse_user(p, &ret->committer, &ret->committer_email,
|
||||
&ret->committer_date, &ret->committer_tz);
|
||||
&ret->committer_date);
|
||||
p = next_header_line(p);
|
||||
}
|
||||
|
||||
|
@ -200,7 +199,7 @@ struct taginfo *cgit_parse_tag(struct tag *tag)
|
|||
const char *p;
|
||||
struct taginfo *ret = NULL;
|
||||
|
||||
data = repo_read_object_file(the_repository, &tag->object.oid, &type, &size);
|
||||
data = read_sha1_file(tag->object.sha1, &type, &size);
|
||||
if (!data || type != OBJ_TAG)
|
||||
goto cleanup;
|
||||
|
||||
|
@ -209,7 +208,7 @@ struct taginfo *cgit_parse_tag(struct tag *tag)
|
|||
for (p = data; !end_of_header(p); p = next_header_line(p)) {
|
||||
if (skip_prefix(p, "tagger ", &p)) {
|
||||
parse_user(p, &ret->tagger, &ret->tagger_email,
|
||||
&ret->tagger_date, &ret->tagger_tz);
|
||||
&ret->tagger_date);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
User-agent: *
|
||||
Disallow: /*/snapshot/*
|
||||
Disallow: /*/blame/*
|
||||
Allow: /
|
||||
|
|
21
scan-tree.c
21
scan-tree.c
|
@ -10,7 +10,6 @@
|
|||
#include "scan-tree.h"
|
||||
#include "configfile.h"
|
||||
#include "html.h"
|
||||
#include <config.h>
|
||||
|
||||
/* return 1 if path contains a objects/ directory and a HEAD file */
|
||||
static int is_git_dir(const char *path)
|
||||
|
@ -46,28 +45,24 @@ out:
|
|||
return result;
|
||||
}
|
||||
|
||||
static struct cgit_repo *repo;
|
||||
static repo_config_fn config_fn;
|
||||
struct cgit_repo *repo;
|
||||
repo_config_fn config_fn;
|
||||
|
||||
static void scan_tree_repo_config(const char *name, const char *value)
|
||||
static void repo_config(const char *name, const char *value)
|
||||
{
|
||||
config_fn(repo, name, value);
|
||||
}
|
||||
|
||||
static int gitconfig_config(const char *key, const char *value, const struct config_context *, void *cb)
|
||||
static int gitconfig_config(const char *key, const char *value, void *cb)
|
||||
{
|
||||
const char *name;
|
||||
|
||||
if (!strcmp(key, "gitweb.owner"))
|
||||
config_fn(repo, "owner", value);
|
||||
else if (!strcmp(key, "gitweb.description"))
|
||||
config_fn(repo, "desc", value);
|
||||
else if (!strcmp(key, "gitweb.category"))
|
||||
config_fn(repo, "section", value);
|
||||
else if (!strcmp(key, "gitweb.homepage"))
|
||||
config_fn(repo, "homepage", value);
|
||||
else if (skip_prefix(key, "cgit.", &name))
|
||||
config_fn(repo, name, value);
|
||||
else if (starts_with(key, "cgit."))
|
||||
config_fn(repo, key + 5, value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -179,7 +174,7 @@ static void add_repo(const char *base, struct strbuf *path, repo_config_fn fn)
|
|||
|
||||
strbuf_addstr(path, "cgitrc");
|
||||
if (!stat(path->buf, &st))
|
||||
parse_configfile(path->buf, &scan_tree_repo_config);
|
||||
parse_configfile(xstrdup(path->buf), &repo_config);
|
||||
|
||||
strbuf_release(&rel);
|
||||
}
|
||||
|
@ -249,7 +244,7 @@ void scan_projects(const char *path, const char *projectsfile, repo_config_fn fn
|
|||
projectsfile, strerror(errno), errno);
|
||||
return;
|
||||
}
|
||||
while (strbuf_getline(&line, projects) != EOF) {
|
||||
while (strbuf_getline(&line, projects, '\n') != EOF) {
|
||||
if (!line.len)
|
||||
continue;
|
||||
strbuf_insert(&line, 0, "/", 1);
|
||||
|
|
159
shared.c
159
shared.c
|
@ -6,9 +6,8 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include <stdio.h>
|
||||
|
||||
struct cgit_repolist cgit_repolist;
|
||||
struct cgit_context ctx;
|
||||
|
@ -55,18 +54,14 @@ struct cgit_repo *cgit_add_repo(const char *url)
|
|||
ret->name = ret->url;
|
||||
ret->path = NULL;
|
||||
ret->desc = cgit_default_repo_desc;
|
||||
ret->extra_head_content = NULL;
|
||||
ret->owner = NULL;
|
||||
ret->homepage = NULL;
|
||||
ret->section = ctx.cfg.section;
|
||||
ret->snapshots = ctx.cfg.snapshots;
|
||||
ret->enable_blame = ctx.cfg.enable_blame;
|
||||
ret->enable_commit_graph = ctx.cfg.enable_commit_graph;
|
||||
ret->enable_log_filecount = ctx.cfg.enable_log_filecount;
|
||||
ret->enable_log_linecount = ctx.cfg.enable_log_linecount;
|
||||
ret->enable_remote_branches = ctx.cfg.enable_remote_branches;
|
||||
ret->enable_subject_links = ctx.cfg.enable_subject_links;
|
||||
ret->enable_html_serving = ctx.cfg.enable_html_serving;
|
||||
ret->max_stats = ctx.cfg.max_stats;
|
||||
ret->branch_sort = ctx.cfg.branch_sort;
|
||||
ret->commit_sort = ctx.cfg.commit_sort;
|
||||
|
@ -99,7 +94,7 @@ struct cgit_repo *cgit_get_repoinfo(const char *url)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
void cgit_free_commitinfo(struct commitinfo *info)
|
||||
void *cgit_free_commitinfo(struct commitinfo *info)
|
||||
{
|
||||
free(info->author);
|
||||
free(info->author_email);
|
||||
|
@ -109,6 +104,7 @@ void cgit_free_commitinfo(struct commitinfo *info)
|
|||
free(info->msg);
|
||||
free(info->msg_encoding);
|
||||
free(info);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *trim_end(const char *str, char c)
|
||||
|
@ -146,6 +142,37 @@ void strbuf_ensure_end(struct strbuf *sb, char c)
|
|||
strbuf_addch(sb, c);
|
||||
}
|
||||
|
||||
char *strlpart(char *txt, int maxlen)
|
||||
{
|
||||
char *result;
|
||||
|
||||
if (!txt)
|
||||
return txt;
|
||||
|
||||
if (strlen(txt) <= maxlen)
|
||||
return txt;
|
||||
result = xmalloc(maxlen + 1);
|
||||
memcpy(result, txt, maxlen - 3);
|
||||
result[maxlen-1] = result[maxlen-2] = result[maxlen-3] = '.';
|
||||
result[maxlen] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
char *strrpart(char *txt, int maxlen)
|
||||
{
|
||||
char *result;
|
||||
|
||||
if (!txt)
|
||||
return txt;
|
||||
|
||||
if (strlen(txt) <= maxlen)
|
||||
return txt;
|
||||
result = xmalloc(maxlen + 1);
|
||||
memcpy(result + 3, txt + strlen(txt) - maxlen + 4, maxlen - 3);
|
||||
result[0] = result[1] = result[2] = '.';
|
||||
return result;
|
||||
}
|
||||
|
||||
void cgit_add_ref(struct reflist *list, struct refinfo *ref)
|
||||
{
|
||||
size_t size;
|
||||
|
@ -158,13 +185,13 @@ void cgit_add_ref(struct reflist *list, struct refinfo *ref)
|
|||
list->refs[list->count++] = ref;
|
||||
}
|
||||
|
||||
static struct refinfo *cgit_mk_refinfo(const char *refname, const struct object_id *oid)
|
||||
static struct refinfo *cgit_mk_refinfo(const char *refname, const unsigned char *sha1)
|
||||
{
|
||||
struct refinfo *ref;
|
||||
|
||||
ref = xmalloc(sizeof (struct refinfo));
|
||||
ref->refname = xstrdup(refname);
|
||||
ref->object = parse_object(the_repository, oid);
|
||||
ref->object = parse_object(sha1);
|
||||
switch (ref->object->type) {
|
||||
case OBJ_TAG:
|
||||
ref->tag = cgit_parse_tag((struct tag *)ref->object);
|
||||
|
@ -176,7 +203,7 @@ static struct refinfo *cgit_mk_refinfo(const char *refname, const struct object_
|
|||
return ref;
|
||||
}
|
||||
|
||||
void cgit_free_taginfo(struct taginfo *tag)
|
||||
static void cgit_free_taginfo(struct taginfo *tag)
|
||||
{
|
||||
if (tag->tagger)
|
||||
free(tag->tagger);
|
||||
|
@ -212,19 +239,19 @@ void cgit_free_reflist_inner(struct reflist *list)
|
|||
free(list->refs);
|
||||
}
|
||||
|
||||
int cgit_refs_cb(const char *refname, const struct object_id *oid, int flags,
|
||||
int cgit_refs_cb(const char *refname, const unsigned char *sha1, int flags,
|
||||
void *cb_data)
|
||||
{
|
||||
struct reflist *list = (struct reflist *)cb_data;
|
||||
struct refinfo *info = cgit_mk_refinfo(refname, oid);
|
||||
struct refinfo *info = cgit_mk_refinfo(refname, sha1);
|
||||
|
||||
if (info)
|
||||
cgit_add_ref(list, info);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cgit_diff_tree_cb(struct diff_queue_struct *q,
|
||||
struct diff_options *options, void *data)
|
||||
static void cgit_diff_tree_cb(struct diff_queue_struct *q,
|
||||
struct diff_options *options, void *data)
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -235,15 +262,15 @@ void cgit_diff_tree_cb(struct diff_queue_struct *q,
|
|||
}
|
||||
}
|
||||
|
||||
static int load_mmfile(mmfile_t *file, const struct object_id *oid)
|
||||
static int load_mmfile(mmfile_t *file, const unsigned char *sha1)
|
||||
{
|
||||
enum object_type type;
|
||||
|
||||
if (is_null_oid(oid)) {
|
||||
if (is_null_sha1(sha1)) {
|
||||
file->ptr = (char *)"";
|
||||
file->size = 0;
|
||||
} else {
|
||||
file->ptr = repo_read_object_file(the_repository, oid, &type,
|
||||
file->ptr = read_sha1_file(sha1, &type,
|
||||
(unsigned long *)&file->size);
|
||||
}
|
||||
return 1;
|
||||
|
@ -257,8 +284,8 @@ static int load_mmfile(mmfile_t *file, const struct object_id *oid)
|
|||
* ripped from git and modified to use globals instead of
|
||||
* a special callback-struct.
|
||||
*/
|
||||
static char *diffbuf = NULL;
|
||||
static int buflen = 0;
|
||||
char *diffbuf = NULL;
|
||||
int buflen = 0;
|
||||
|
||||
static int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
|
||||
{
|
||||
|
@ -294,8 +321,8 @@ static int filediff_cb(void *priv, mmbuffer_t *mb, int nbuf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int cgit_diff_files(const struct object_id *old_oid,
|
||||
const struct object_id *new_oid, unsigned long *old_size,
|
||||
int cgit_diff_files(const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1, unsigned long *old_size,
|
||||
unsigned long *new_size, int *binary, int context,
|
||||
int ignorews, linediff_fn fn)
|
||||
{
|
||||
|
@ -304,7 +331,7 @@ int cgit_diff_files(const struct object_id *old_oid,
|
|||
xdemitconf_t emit_params;
|
||||
xdemitcb_t emit_cb;
|
||||
|
||||
if (!load_mmfile(&file1, old_oid) || !load_mmfile(&file2, new_oid))
|
||||
if (!load_mmfile(&file1, old_sha1) || !load_mmfile(&file2, new_sha1))
|
||||
return 1;
|
||||
|
||||
*old_size = file1.size;
|
||||
|
@ -328,7 +355,7 @@ int cgit_diff_files(const struct object_id *old_oid,
|
|||
diff_params.flags |= XDF_IGNORE_WHITESPACE;
|
||||
emit_params.ctxlen = context > 0 ? context : 3;
|
||||
emit_params.flags = XDL_EMIT_FUNCNAMES;
|
||||
emit_cb.out_line = filediff_cb;
|
||||
emit_cb.outf = filediff_cb;
|
||||
emit_cb.priv = fn;
|
||||
xdl_diff(&file1, &file2, &diff_params, &emit_params, &emit_cb);
|
||||
if (file1.size)
|
||||
|
@ -338,46 +365,46 @@ int cgit_diff_files(const struct object_id *old_oid,
|
|||
return 0;
|
||||
}
|
||||
|
||||
void cgit_diff_tree(const struct object_id *old_oid,
|
||||
const struct object_id *new_oid,
|
||||
void cgit_diff_tree(const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1,
|
||||
filepair_fn fn, const char *prefix, int ignorews)
|
||||
{
|
||||
struct diff_options opt;
|
||||
struct pathspec_item *item;
|
||||
struct pathspec_item item;
|
||||
|
||||
repo_diff_setup(the_repository, &opt);
|
||||
memset(&item, 0, sizeof(item));
|
||||
diff_setup(&opt);
|
||||
opt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
opt.detect_rename = 1;
|
||||
opt.rename_limit = ctx.cfg.renamelimit;
|
||||
opt.flags.recursive = 1;
|
||||
DIFF_OPT_SET(&opt, RECURSIVE);
|
||||
if (ignorews)
|
||||
DIFF_XDL_SET(&opt, IGNORE_WHITESPACE);
|
||||
opt.format_callback = cgit_diff_tree_cb;
|
||||
opt.format_callback_data = fn;
|
||||
if (prefix) {
|
||||
item = xcalloc(1, sizeof(*item));
|
||||
item->match = xstrdup(prefix);
|
||||
item->len = strlen(prefix);
|
||||
item.match = prefix;
|
||||
item.len = strlen(prefix);
|
||||
opt.pathspec.nr = 1;
|
||||
opt.pathspec.items = item;
|
||||
opt.pathspec.items = &item;
|
||||
}
|
||||
diff_setup_done(&opt);
|
||||
|
||||
if (old_oid && !is_null_oid(old_oid))
|
||||
diff_tree_oid(old_oid, new_oid, "", &opt);
|
||||
if (old_sha1 && !is_null_sha1(old_sha1))
|
||||
diff_tree_sha1(old_sha1, new_sha1, "", &opt);
|
||||
else
|
||||
diff_root_tree_oid(new_oid, "", &opt);
|
||||
diff_root_tree_sha1(new_sha1, "", &opt);
|
||||
diffcore_std(&opt);
|
||||
diff_flush(&opt);
|
||||
}
|
||||
|
||||
void cgit_diff_commit(struct commit *commit, filepair_fn fn, const char *prefix)
|
||||
{
|
||||
const struct object_id *old_oid = NULL;
|
||||
unsigned char *old_sha1 = NULL;
|
||||
|
||||
if (commit->parents)
|
||||
old_oid = &commit->parents->item->object.oid;
|
||||
cgit_diff_tree(old_oid, &commit->object.oid, fn, prefix,
|
||||
old_sha1 = commit->parents->item->object.sha1;
|
||||
cgit_diff_tree(old_sha1, commit->object.sha1, fn, prefix,
|
||||
ctx.qry.ignorews);
|
||||
}
|
||||
|
||||
|
@ -392,9 +419,6 @@ int cgit_parse_snapshots_mask(const char *str)
|
|||
if (atoi(str))
|
||||
return 1;
|
||||
|
||||
if (strcmp(str, "all") == 0)
|
||||
return INT_MAX;
|
||||
|
||||
string_list_split(&tokens, str, ' ', -1);
|
||||
string_list_remove_empty_items(&tokens, 0);
|
||||
|
||||
|
@ -402,7 +426,7 @@ int cgit_parse_snapshots_mask(const char *str)
|
|||
for (f = cgit_snapshot_formats; f->suffix; f++) {
|
||||
if (!strcmp(item->string, f->suffix) ||
|
||||
!strcmp(item->string, f->suffix + 1)) {
|
||||
rv |= cgit_snapshot_format_bit(f);
|
||||
rv |= f->bit;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -477,16 +501,15 @@ static int is_token_char(char c)
|
|||
static char *expand_macro(char *name, int maxlength)
|
||||
{
|
||||
char *value;
|
||||
size_t len;
|
||||
int len;
|
||||
|
||||
len = 0;
|
||||
value = getenv(name);
|
||||
if (value) {
|
||||
len = strlen(value) + 1;
|
||||
len = strlen(value);
|
||||
if (len > maxlength)
|
||||
len = maxlength;
|
||||
strlcpy(name, value, len);
|
||||
--len;
|
||||
strncpy(name, value, len);
|
||||
}
|
||||
return name + len;
|
||||
}
|
||||
|
@ -538,47 +561,3 @@ char *expand_macros(const char *txt)
|
|||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
char *get_mimetype_for_filename(const char *filename)
|
||||
{
|
||||
char *ext, *mimetype, line[1024];
|
||||
struct string_list list = STRING_LIST_INIT_NODUP;
|
||||
int i;
|
||||
FILE *file;
|
||||
struct string_list_item *mime;
|
||||
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
ext = strrchr(filename, '.');
|
||||
if (!ext)
|
||||
return NULL;
|
||||
++ext;
|
||||
if (!ext[0])
|
||||
return NULL;
|
||||
mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
|
||||
if (mime)
|
||||
return xstrdup(mime->util);
|
||||
|
||||
if (!ctx.cfg.mimetype_file)
|
||||
return NULL;
|
||||
file = fopen(ctx.cfg.mimetype_file, "r");
|
||||
if (!file)
|
||||
return NULL;
|
||||
while (fgets(line, sizeof(line), file)) {
|
||||
if (!line[0] || line[0] == '#')
|
||||
continue;
|
||||
string_list_split_in_place(&list, line, " \t\r\n", -1);
|
||||
string_list_remove_empty_items(&list, 0);
|
||||
mimetype = list.items[0].string;
|
||||
for (i = 1; i < list.nr; i++) {
|
||||
if (!strcasecmp(ext, list.items[i].string)) {
|
||||
fclose(file);
|
||||
return xstrdup(mimetype);
|
||||
}
|
||||
}
|
||||
string_list_clear(&list, 0);
|
||||
}
|
||||
fclose(file);
|
||||
return NULL;
|
||||
}
|
||||
|
|
|
@ -1,15 +1,11 @@
|
|||
include ../git/config.mak.uname
|
||||
-include ../cgit.conf
|
||||
|
||||
SHELL_PATH ?= $(SHELL)
|
||||
SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
|
||||
|
||||
T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
|
||||
|
||||
all: $(T)
|
||||
|
||||
$(T):
|
||||
@'$(SHELL_PATH_SQ)' $@ $(CGIT_TEST_OPTS)
|
||||
@./$@ $(CGIT_TEST_OPTS)
|
||||
|
||||
clean:
|
||||
$(RM) -rf trash
|
||||
|
|
|
@ -80,17 +80,13 @@ mkrepo() {
|
|||
git commit -m "commit $n"
|
||||
n=$(expr $n + 1)
|
||||
done
|
||||
case "$3" in
|
||||
testplus)
|
||||
if test "$3" = "testplus"
|
||||
then
|
||||
echo "hello" >a+b
|
||||
git add a+b
|
||||
git commit -m "add a+b"
|
||||
git branch "1+2"
|
||||
;;
|
||||
commit-graph)
|
||||
git commit-graph write
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -99,7 +95,7 @@ setup_repos()
|
|||
rm -rf cache
|
||||
mkdir -p cache
|
||||
mkrepo repos/foo 5 >/dev/null
|
||||
mkrepo repos/bar 50 commit-graph >/dev/null
|
||||
mkrepo repos/bar 50 >/dev/null
|
||||
mkrepo repos/foo+bar 10 testplus >/dev/null
|
||||
mkrepo "repos/with space" 2 >/dev/null
|
||||
mkrepo repos/filter 5 testplus >/dev/null
|
||||
|
@ -108,7 +104,7 @@ virtual-root=/
|
|||
cache-root=$PWD/cache
|
||||
|
||||
cache-size=1021
|
||||
snapshots=tar.gz tar.bz tar.lz tar.xz tar.zst zip
|
||||
snapshots=tar.gz tar.bz zip
|
||||
enable-log-filecount=1
|
||||
enable-log-linecount=1
|
||||
summary-log=5
|
||||
|
|
|
@ -1,9 +1,5 @@
|
|||
#!/bin/sh
|
||||
|
||||
if [ "${CGIT_TEST_NO_GIT_VERSION}" = "YesPlease" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
test_description='Check Git version is correct'
|
||||
CGIT_TEST_NO_CREATE_REPOS=YesPlease
|
||||
. ./setup.sh
|
||||
|
@ -33,11 +29,11 @@ test_expect_success 'test submodule version matches Makefile' '
|
|||
else
|
||||
(
|
||||
cd ../.. &&
|
||||
sm_oid=$(git ls-files --stage -- git |
|
||||
sm_sha1=$(git ls-files --stage -- git |
|
||||
sed -e "s/^[0-9]* \\([0-9a-f]*\\) [0-9] .*$/\\1/") &&
|
||||
cd git &&
|
||||
git describe --match "v[0-9]*" $sm_oid
|
||||
) | sed -e "s/^v//" -e "s/-/./" >sm_version &&
|
||||
git describe --match "v[0-9]*" $sm_sha1
|
||||
) | sed -e "s/^v//" >sm_version &&
|
||||
test_cmp sm_version makefile_version
|
||||
fi
|
||||
'
|
||||
|
|
|
@ -25,7 +25,7 @@ test_expect_success 'get root commit' '
|
|||
'
|
||||
|
||||
test_expect_success 'root commit contains diffstat' '
|
||||
grep "<a href=./foo/diff/file-1.id=[0-9a-f]\{40,64\}.>file-1</a>" tmp
|
||||
grep "<a href=./foo/diff/file-1.id=[0-9a-f]\{40\}.>file-1</a>" tmp
|
||||
'
|
||||
|
||||
test_expect_success 'root commit contains diff' '
|
||||
|
|
|
@ -25,7 +25,7 @@ test_expect_success 'verify gzip format' '
|
|||
|
||||
test_expect_success 'untar' '
|
||||
rm -rf master &&
|
||||
gzip -dc master.tar.gz | tar -xf -
|
||||
tar -xzf master.tar.gz
|
||||
'
|
||||
|
||||
test_expect_success 'count files' '
|
||||
|
@ -38,129 +38,6 @@ test_expect_success 'verify untarred file-5' '
|
|||
test_line_count = 1 master/file-5
|
||||
'
|
||||
|
||||
if test -n "$(which lzip 2>/dev/null)"; then
|
||||
test_set_prereq LZIP
|
||||
else
|
||||
say 'Skipping LZIP validation tests: lzip not found'
|
||||
fi
|
||||
|
||||
test_expect_success LZIP 'get foo/snapshot/master.tar.lz' '
|
||||
cgit_url "foo/snapshot/master.tar.lz" >tmp
|
||||
'
|
||||
|
||||
test_expect_success LZIP 'check html headers' '
|
||||
head -n 1 tmp |
|
||||
grep "Content-Type: application/x-lzip" &&
|
||||
|
||||
head -n 2 tmp |
|
||||
grep "Content-Disposition: inline; filename=.master.tar.lz."
|
||||
'
|
||||
|
||||
test_expect_success LZIP 'strip off the header lines' '
|
||||
strip_headers <tmp >master.tar.lz
|
||||
'
|
||||
|
||||
test_expect_success LZIP 'verify lzip format' '
|
||||
lzip --test master.tar.lz
|
||||
'
|
||||
|
||||
test_expect_success LZIP 'untar' '
|
||||
rm -rf master &&
|
||||
lzip -dc master.tar.lz | tar -xf -
|
||||
'
|
||||
|
||||
test_expect_success LZIP 'count files' '
|
||||
ls master/ >output &&
|
||||
test_line_count = 5 output
|
||||
'
|
||||
|
||||
test_expect_success LZIP 'verify untarred file-5' '
|
||||
grep "^5$" master/file-5 &&
|
||||
test_line_count = 1 master/file-5
|
||||
'
|
||||
|
||||
if test -n "$(which xz 2>/dev/null)"; then
|
||||
test_set_prereq XZ
|
||||
else
|
||||
say 'Skipping XZ validation tests: xz not found'
|
||||
fi
|
||||
|
||||
test_expect_success XZ 'get foo/snapshot/master.tar.xz' '
|
||||
cgit_url "foo/snapshot/master.tar.xz" >tmp
|
||||
'
|
||||
|
||||
test_expect_success XZ 'check html headers' '
|
||||
head -n 1 tmp |
|
||||
grep "Content-Type: application/x-xz" &&
|
||||
|
||||
head -n 2 tmp |
|
||||
grep "Content-Disposition: inline; filename=.master.tar.xz."
|
||||
'
|
||||
|
||||
test_expect_success XZ 'strip off the header lines' '
|
||||
strip_headers <tmp >master.tar.xz
|
||||
'
|
||||
|
||||
test_expect_success XZ 'verify xz format' '
|
||||
xz --test master.tar.xz
|
||||
'
|
||||
|
||||
test_expect_success XZ 'untar' '
|
||||
rm -rf master &&
|
||||
xz -dc master.tar.xz | tar -xf -
|
||||
'
|
||||
|
||||
test_expect_success XZ 'count files' '
|
||||
ls master/ >output &&
|
||||
test_line_count = 5 output
|
||||
'
|
||||
|
||||
test_expect_success XZ 'verify untarred file-5' '
|
||||
grep "^5$" master/file-5 &&
|
||||
test_line_count = 1 master/file-5
|
||||
'
|
||||
|
||||
if test -n "$(which zstd 2>/dev/null)"; then
|
||||
test_set_prereq ZSTD
|
||||
else
|
||||
say 'Skipping ZSTD validation tests: zstd not found'
|
||||
fi
|
||||
|
||||
test_expect_success ZSTD 'get foo/snapshot/master.tar.zst' '
|
||||
cgit_url "foo/snapshot/master.tar.zst" >tmp
|
||||
'
|
||||
|
||||
test_expect_success ZSTD 'check html headers' '
|
||||
head -n 1 tmp |
|
||||
grep "Content-Type: application/x-zstd" &&
|
||||
|
||||
head -n 2 tmp |
|
||||
grep "Content-Disposition: inline; filename=.master.tar.zst."
|
||||
'
|
||||
|
||||
test_expect_success ZSTD 'strip off the header lines' '
|
||||
strip_headers <tmp >master.tar.zst
|
||||
'
|
||||
|
||||
test_expect_success ZSTD 'verify zstd format' '
|
||||
zstd --test master.tar.zst
|
||||
'
|
||||
|
||||
test_expect_success ZSTD 'untar' '
|
||||
rm -rf master &&
|
||||
zstd -dc master.tar.zst | tar -xf -
|
||||
'
|
||||
|
||||
test_expect_success ZSTD 'count files' '
|
||||
ls master/ >output &&
|
||||
test_line_count = 5 output
|
||||
'
|
||||
|
||||
test_expect_success ZSTD 'verify untarred file-5' '
|
||||
grep "^5$" master/file-5 &&
|
||||
test_line_count = 1 master/file-5
|
||||
'
|
||||
|
||||
test_expect_success 'get foo/snapshot/master.zip' '
|
||||
cgit_url "foo/snapshot/master.zip" >tmp
|
||||
'
|
||||
|
|
|
@ -9,23 +9,17 @@ test -n "$(which strace 2>/dev/null)" || {
|
|||
exit
|
||||
}
|
||||
|
||||
strace true 2>/dev/null || {
|
||||
skip_all='Skipping access validation tests: strace not functional'
|
||||
test_done
|
||||
exit
|
||||
}
|
||||
|
||||
test_no_home_access () {
|
||||
non_existent_path="/path/to/some/place/that/does/not/possibly/exist"
|
||||
while test -d "$non_existent_path"; do
|
||||
non_existent_path="$non_existent_path/$(date +%N)"
|
||||
non_existant_path="/path/to/some/place/that/does/not/possibly/exist"
|
||||
while test -d "$non_existant_path"; do
|
||||
non_existant_path="$non_existant_path/$(date +%N)"
|
||||
done &&
|
||||
strace \
|
||||
-E HOME="$non_existent_path" \
|
||||
-E HOME="$non_existant_path" \
|
||||
-E CGIT_CONFIG="$PWD/cgitrc" \
|
||||
-E QUERY_STRING="url=$1" \
|
||||
-e access -f -o strace.out cgit &&
|
||||
! grep "$non_existent_path" strace.out
|
||||
test_must_fail grep "$non_existant_path" strace.out
|
||||
}
|
||||
|
||||
test_no_home_access_success() {
|
||||
|
|
|
@ -8,8 +8,8 @@ test_expect_success 'generate foo/rawdiff' '
|
|||
'
|
||||
|
||||
test_expect_success 'compare with output of git-diff(1)' '
|
||||
git --git-dir="$PWD/repos/foo/.git" diff HEAD^.. >tmp2 &&
|
||||
sed "1,4d" tmp >tmp_ &&
|
||||
git --git-dir="$PWD/repos/foo/.git" diff HEAD^.. >tmp2
|
||||
sed "1,4d" tmp >tmp_
|
||||
cmp tmp_ tmp2
|
||||
'
|
||||
|
||||
|
@ -22,20 +22,20 @@ test_expect_success 'generate diff for initial commit' '
|
|||
'
|
||||
|
||||
test_expect_success 'compare with output of git-diff-tree(1)' '
|
||||
git --git-dir="$PWD/repos/foo/.git" diff-tree -p --no-commit-id --root "$root" >tmp2 &&
|
||||
sed "1,4d" tmp >tmp_ &&
|
||||
git --git-dir="$PWD/repos/foo/.git" diff-tree -p --no-commit-id --root "$root" >tmp2
|
||||
sed "1,4d" tmp >tmp_
|
||||
cmp tmp_ tmp2
|
||||
'
|
||||
|
||||
test_expect_success 'generate diff for multiple commits' '
|
||||
id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD) &&
|
||||
id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3) &&
|
||||
id=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD)
|
||||
id2=$(git --git-dir="$PWD/repos/foo/.git" rev-parse HEAD~3)
|
||||
cgit_query "url=foo/rawdiff&id=$id&id2=$id2" >tmp
|
||||
'
|
||||
|
||||
test_expect_success 'compare with output of git-diff(1)' '
|
||||
git --git-dir="$PWD/repos/foo/.git" diff HEAD~3..HEAD >tmp2 &&
|
||||
sed "1,4d" tmp >tmp_ &&
|
||||
git --git-dir="$PWD/repos/foo/.git" diff HEAD~3..HEAD >tmp2
|
||||
sed "1,4d" tmp >tmp_
|
||||
cmp tmp_ tmp2
|
||||
'
|
||||
|
||||
|
|
60
ui-atom.c
60
ui-atom.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-atom.h"
|
||||
#include "html.h"
|
||||
|
@ -21,14 +19,13 @@ static void add_entry(struct commit *commit, const char *host)
|
|||
struct commitinfo *info;
|
||||
|
||||
info = cgit_parse_commit(commit);
|
||||
hex = oid_to_hex(&commit->object.oid);
|
||||
hex = sha1_to_hex(commit->object.sha1);
|
||||
html("<entry>\n");
|
||||
html("<title>");
|
||||
html_txt(info->subject);
|
||||
html("</title>\n");
|
||||
html("<updated>");
|
||||
html_txt(show_date(info->committer_date, 0,
|
||||
date_mode_from_type(DATE_ISO8601_STRICT)));
|
||||
cgit_print_date(info->committer_date, FMT_ATOMDATE, 0);
|
||||
html("</updated>\n");
|
||||
html("<author>\n");
|
||||
if (info->author) {
|
||||
|
@ -53,41 +50,41 @@ static void add_entry(struct commit *commit, const char *host)
|
|||
}
|
||||
html("</author>\n");
|
||||
html("<published>");
|
||||
html_txt(show_date(info->author_date, 0,
|
||||
date_mode_from_type(DATE_ISO8601_STRICT)));
|
||||
cgit_print_date(info->author_date, FMT_ATOMDATE, 0);
|
||||
html("</published>\n");
|
||||
if (host) {
|
||||
char *pageurl;
|
||||
html("<link rel='alternate' type='text/html' href='");
|
||||
html(cgit_httpscheme());
|
||||
html_attr(host);
|
||||
pageurl = cgit_pageurl(ctx.repo->url, "commit", NULL);
|
||||
html_attr(pageurl);
|
||||
html_attr(cgit_pageurl(ctx.repo->url, "commit", NULL));
|
||||
if (ctx.cfg.virtual_root)
|
||||
delim = '?';
|
||||
html_attrf("%cid=%s", delim, hex);
|
||||
htmlf("%cid=%s", delim, hex);
|
||||
html("'/>\n");
|
||||
free(pageurl);
|
||||
}
|
||||
html("<id>");
|
||||
html_txtf("urn:%s:%s", the_hash_algo->name, hex);
|
||||
html("</id>\n");
|
||||
htmlf("<id>%s</id>\n", hex);
|
||||
html("<content type='text'>\n");
|
||||
html_txt(info->msg);
|
||||
html("</content>\n");
|
||||
html("<content type='xhtml'>\n");
|
||||
html("<div xmlns='http://www.w3.org/1999/xhtml'>\n");
|
||||
html("<pre>\n");
|
||||
html_txt(info->msg);
|
||||
html("</pre>\n");
|
||||
html("</div>\n");
|
||||
html("</content>\n");
|
||||
html("</entry>\n");
|
||||
cgit_free_commitinfo(info);
|
||||
}
|
||||
|
||||
|
||||
void cgit_print_atom(char *tip, const char *path, int max_count)
|
||||
void cgit_print_atom(char *tip, char *path, int max_count)
|
||||
{
|
||||
char *host;
|
||||
const char *host;
|
||||
const char *argv[] = {NULL, tip, NULL, NULL, NULL};
|
||||
struct commit *commit;
|
||||
struct rev_info rev;
|
||||
int argc = 2;
|
||||
bool first = true;
|
||||
|
||||
if (ctx.qry.show_all)
|
||||
argv[1] = "--all";
|
||||
|
@ -99,7 +96,7 @@ void cgit_print_atom(char *tip, const char *path, int max_count)
|
|||
argv[argc++] = path;
|
||||
}
|
||||
|
||||
repo_init_revisions(the_repository, &rev, NULL);
|
||||
init_revisions(&rev, NULL);
|
||||
rev.abbrev = DEFAULT_ABBREV;
|
||||
rev.commit_format = CMIT_FMT_DEFAULT;
|
||||
rev.verbose_header = 1;
|
||||
|
@ -128,32 +125,17 @@ void cgit_print_atom(char *tip, const char *path, int max_count)
|
|||
html_txt(ctx.repo->desc);
|
||||
html("</subtitle>\n");
|
||||
if (host) {
|
||||
char *fullurl = cgit_currentfullurl();
|
||||
char *repourl = cgit_repourl(ctx.repo->url);
|
||||
html("<id>");
|
||||
html_txtf("%s%s%s", cgit_httpscheme(), host, fullurl);
|
||||
html("</id>\n");
|
||||
html("<link rel='self' href='");
|
||||
html_attrf("%s%s%s", cgit_httpscheme(), host, fullurl);
|
||||
html("'/>\n");
|
||||
html("<link rel='alternate' type='text/html' href='");
|
||||
html_attrf("%s%s%s", cgit_httpscheme(), host, repourl);
|
||||
html(cgit_httpscheme());
|
||||
html_attr(host);
|
||||
html_attr(cgit_repourl(ctx.repo->url));
|
||||
html("'/>\n");
|
||||
free(fullurl);
|
||||
free(repourl);
|
||||
}
|
||||
while ((commit = get_revision(&rev)) != NULL) {
|
||||
if (first) {
|
||||
html("<updated>");
|
||||
html_txt(show_date(commit->date, 0,
|
||||
date_mode_from_type(DATE_ISO8601_STRICT)));
|
||||
html("</updated>\n");
|
||||
first = false;
|
||||
}
|
||||
add_entry(commit, host);
|
||||
release_commit_memory(the_repository->parsed_objects, commit);
|
||||
free_commit_buffer(commit);
|
||||
free_commit_list(commit->parents);
|
||||
commit->parents = NULL;
|
||||
}
|
||||
html("</feed>\n");
|
||||
free(host);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#ifndef UI_ATOM_H
|
||||
#define UI_ATOM_H
|
||||
|
||||
extern void cgit_print_atom(char *tip, const char *path, int max_count);
|
||||
extern void cgit_print_atom(char *tip, char *path, int max_count);
|
||||
|
||||
#endif
|
||||
|
|
317
ui-blame.c
317
ui-blame.c
|
@ -1,317 +0,0 @@
|
|||
/* ui-blame.c: functions for blame output
|
||||
*
|
||||
* Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
*
|
||||
* Licensed under GNU General Public License v2
|
||||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-blame.h"
|
||||
#include "html.h"
|
||||
#include "ui-shared.h"
|
||||
#include "strvec.h"
|
||||
#include "blame.h"
|
||||
|
||||
|
||||
static char *emit_suspect_detail(struct blame_origin *suspect)
|
||||
{
|
||||
struct commitinfo *info;
|
||||
struct strbuf detail = STRBUF_INIT;
|
||||
|
||||
info = cgit_parse_commit(suspect->commit);
|
||||
|
||||
strbuf_addf(&detail, "author %s", info->author);
|
||||
if (!ctx.cfg.noplainemail)
|
||||
strbuf_addf(&detail, " %s", info->author_email);
|
||||
strbuf_addf(&detail, " %s\n",
|
||||
show_date(info->author_date, info->author_tz,
|
||||
cgit_date_mode(DATE_ISO8601)));
|
||||
|
||||
strbuf_addf(&detail, "committer %s", info->committer);
|
||||
if (!ctx.cfg.noplainemail)
|
||||
strbuf_addf(&detail, " %s", info->committer_email);
|
||||
strbuf_addf(&detail, " %s\n\n",
|
||||
show_date(info->committer_date, info->committer_tz,
|
||||
cgit_date_mode(DATE_ISO8601)));
|
||||
|
||||
strbuf_addstr(&detail, info->subject);
|
||||
|
||||
cgit_free_commitinfo(info);
|
||||
return strbuf_detach(&detail, NULL);
|
||||
}
|
||||
|
||||
static void emit_blame_entry_hash(struct blame_entry *ent)
|
||||
{
|
||||
struct blame_origin *suspect = ent->suspect;
|
||||
struct object_id *oid = &suspect->commit->object.oid;
|
||||
unsigned long line = 0;
|
||||
|
||||
char *detail = emit_suspect_detail(suspect);
|
||||
html("<span class='oid'>");
|
||||
cgit_commit_link(repo_find_unique_abbrev(the_repository, oid, DEFAULT_ABBREV), detail,
|
||||
NULL, ctx.qry.head, oid_to_hex(oid), suspect->path);
|
||||
html("</span>");
|
||||
free(detail);
|
||||
|
||||
if (!repo_parse_commit(the_repository, suspect->commit) && suspect->commit->parents) {
|
||||
struct commit *parent = suspect->commit->parents->item;
|
||||
|
||||
html(" ");
|
||||
cgit_blame_link("^", "Blame the previous revision", NULL,
|
||||
ctx.qry.head, oid_to_hex(&parent->object.oid),
|
||||
suspect->path);
|
||||
}
|
||||
|
||||
while (line++ < ent->num_lines)
|
||||
html("\n");
|
||||
}
|
||||
|
||||
static void emit_blame_entry_linenumber(struct blame_entry *ent)
|
||||
{
|
||||
const char *numberfmt = "<a id='n%1$d' href='#n%1$d'>%1$d</a>\n";
|
||||
|
||||
unsigned long lineno = ent->lno;
|
||||
while (lineno < ent->lno + ent->num_lines)
|
||||
htmlf(numberfmt, ++lineno);
|
||||
}
|
||||
|
||||
static void emit_blame_entry_line_background(struct blame_scoreboard *sb,
|
||||
struct blame_entry *ent)
|
||||
{
|
||||
unsigned long line;
|
||||
size_t len, maxlen = 2;
|
||||
const char* pos, *endpos;
|
||||
|
||||
for (line = ent->lno; line < ent->lno + ent->num_lines; line++) {
|
||||
html("\n");
|
||||
pos = blame_nth_line(sb, line);
|
||||
endpos = blame_nth_line(sb, line + 1);
|
||||
len = 0;
|
||||
while (pos < endpos) {
|
||||
len++;
|
||||
if (*pos++ == '\t')
|
||||
len = (len + 7) & ~7;
|
||||
}
|
||||
if (len > maxlen)
|
||||
maxlen = len;
|
||||
}
|
||||
|
||||
for (len = 0; len < maxlen - 1; len++)
|
||||
html(" ");
|
||||
}
|
||||
|
||||
struct walk_tree_context {
|
||||
char *curr_rev;
|
||||
int match_baselen;
|
||||
int state;
|
||||
};
|
||||
|
||||
static void print_object(const struct object_id *oid, const char *path,
|
||||
const char *basename, const char *rev)
|
||||
{
|
||||
enum object_type type;
|
||||
char *buf;
|
||||
unsigned long size;
|
||||
struct strvec rev_argv = STRVEC_INIT;
|
||||
struct rev_info revs;
|
||||
struct blame_scoreboard sb;
|
||||
struct blame_origin *o;
|
||||
struct blame_entry *ent = NULL;
|
||||
|
||||
type = oid_object_info(the_repository, oid, &size);
|
||||
if (type == OBJ_BAD) {
|
||||
cgit_print_error_page(404, "Not found", "Bad object name: %s",
|
||||
oid_to_hex(oid));
|
||||
return;
|
||||
}
|
||||
|
||||
buf = repo_read_object_file(the_repository, oid, &type, &size);
|
||||
if (!buf) {
|
||||
cgit_print_error_page(500, "Internal server error",
|
||||
"Error reading object %s", oid_to_hex(oid));
|
||||
return;
|
||||
}
|
||||
|
||||
strvec_push(&rev_argv, "blame");
|
||||
strvec_push(&rev_argv, rev);
|
||||
repo_init_revisions(the_repository, &revs, NULL);
|
||||
revs.diffopt.flags.allow_textconv = 1;
|
||||
setup_revisions(rev_argv.nr, rev_argv.v, &revs, NULL);
|
||||
init_scoreboard(&sb);
|
||||
sb.revs = &revs;
|
||||
sb.repo = the_repository;
|
||||
sb.path = path;
|
||||
setup_scoreboard(&sb, &o);
|
||||
o->suspects = blame_entry_prepend(NULL, 0, sb.num_lines, o);
|
||||
prio_queue_put(&sb.commits, o->commit);
|
||||
blame_origin_decref(o);
|
||||
sb.ent = NULL;
|
||||
sb.path = path;
|
||||
assign_blame(&sb, 0);
|
||||
blame_sort_final(&sb);
|
||||
blame_coalesce(&sb);
|
||||
|
||||
cgit_set_title_from_path(path);
|
||||
|
||||
cgit_print_layout_start();
|
||||
htmlf("blob: %s (", oid_to_hex(oid));
|
||||
cgit_plain_link("plain", NULL, NULL, ctx.qry.head, rev, path);
|
||||
html(") (");
|
||||
cgit_tree_link("tree", NULL, NULL, ctx.qry.head, rev, path);
|
||||
html(")\n");
|
||||
|
||||
if (buffer_is_binary(buf, size)) {
|
||||
html("<div class='error'>blob is binary.</div>");
|
||||
goto cleanup;
|
||||
}
|
||||
if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
|
||||
htmlf("<div class='error'>blob size (%ldKB)"
|
||||
" exceeds display size limit (%dKB).</div>",
|
||||
size / 1024, ctx.cfg.max_blob_size);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
html("<table class='blame blob'>\n<tr>\n");
|
||||
|
||||
/* Commit hashes */
|
||||
html("<td class='hashes'>");
|
||||
for (ent = sb.ent; ent; ent = ent->next) {
|
||||
html("<div class='alt'><pre>");
|
||||
emit_blame_entry_hash(ent);
|
||||
html("</pre></div>");
|
||||
}
|
||||
html("</td>\n");
|
||||
|
||||
/* Line numbers */
|
||||
if (ctx.cfg.enable_tree_linenumbers) {
|
||||
html("<td class='linenumbers'>");
|
||||
for (ent = sb.ent; ent; ent = ent->next) {
|
||||
html("<div class='alt'><pre>");
|
||||
emit_blame_entry_linenumber(ent);
|
||||
html("</pre></div>");
|
||||
}
|
||||
html("</td>\n");
|
||||
}
|
||||
|
||||
html("<td class='lines'><div>");
|
||||
|
||||
/* Colored bars behind lines */
|
||||
html("<div>");
|
||||
for (ent = sb.ent; ent; ) {
|
||||
struct blame_entry *e = ent->next;
|
||||
html("<div class='alt'><pre>");
|
||||
emit_blame_entry_line_background(&sb, ent);
|
||||
html("</pre></div>");
|
||||
free(ent);
|
||||
ent = e;
|
||||
}
|
||||
html("</div>");
|
||||
|
||||
free((void *)sb.final_buf);
|
||||
|
||||
/* Lines */
|
||||
html("<pre><code>");
|
||||
if (ctx.repo->source_filter) {
|
||||
char *filter_arg = xstrdup(basename);
|
||||
cgit_open_filter(ctx.repo->source_filter, filter_arg);
|
||||
html_raw(buf, size);
|
||||
cgit_close_filter(ctx.repo->source_filter);
|
||||
free(filter_arg);
|
||||
} else {
|
||||
html_txt(buf);
|
||||
}
|
||||
html("</code></pre>");
|
||||
|
||||
html("</div></td>\n");
|
||||
|
||||
html("</tr>\n</table>\n");
|
||||
|
||||
cgit_print_layout_end();
|
||||
|
||||
cleanup:
|
||||
free(buf);
|
||||
}
|
||||
|
||||
static int walk_tree(const struct object_id *oid, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, void *cbdata)
|
||||
{
|
||||
struct walk_tree_context *walk_tree_ctx = cbdata;
|
||||
|
||||
if (base->len == walk_tree_ctx->match_baselen) {
|
||||
if (S_ISREG(mode)) {
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
strbuf_addbuf(&buffer, base);
|
||||
strbuf_addstr(&buffer, pathname);
|
||||
print_object(oid, buffer.buf, pathname,
|
||||
walk_tree_ctx->curr_rev);
|
||||
strbuf_release(&buffer);
|
||||
walk_tree_ctx->state = 1;
|
||||
} else if (S_ISDIR(mode)) {
|
||||
walk_tree_ctx->state = 2;
|
||||
}
|
||||
} else if (base->len < INT_MAX
|
||||
&& (int)base->len > walk_tree_ctx->match_baselen) {
|
||||
walk_tree_ctx->state = 2;
|
||||
} else if (S_ISDIR(mode)) {
|
||||
return READ_TREE_RECURSIVE;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int basedir_len(const char *path)
|
||||
{
|
||||
char *p = strrchr(path, '/');
|
||||
if (p)
|
||||
return p - path + 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cgit_print_blame(void)
|
||||
{
|
||||
const char *rev = ctx.qry.oid;
|
||||
struct object_id oid;
|
||||
struct commit *commit;
|
||||
struct pathspec_item path_items = {
|
||||
.match = ctx.qry.path,
|
||||
.len = ctx.qry.path ? strlen(ctx.qry.path) : 0
|
||||
};
|
||||
struct pathspec paths = {
|
||||
.nr = 1,
|
||||
.items = &path_items
|
||||
};
|
||||
struct walk_tree_context walk_tree_ctx = {
|
||||
.state = 0
|
||||
};
|
||||
|
||||
if (!rev)
|
||||
rev = ctx.qry.head;
|
||||
|
||||
if (repo_get_oid(the_repository, rev, &oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Invalid revision name: %s", rev);
|
||||
return;
|
||||
}
|
||||
commit = lookup_commit_reference(the_repository, &oid);
|
||||
if (!commit || repo_parse_commit(the_repository, commit)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Invalid commit reference: %s", rev);
|
||||
return;
|
||||
}
|
||||
|
||||
walk_tree_ctx.curr_rev = xstrdup(rev);
|
||||
walk_tree_ctx.match_baselen = (path_items.match) ?
|
||||
basedir_len(path_items.match) : -1;
|
||||
|
||||
read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
|
||||
&paths, walk_tree, &walk_tree_ctx);
|
||||
if (!walk_tree_ctx.state)
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
else if (walk_tree_ctx.state == 2)
|
||||
cgit_print_error_page(404, "No blame for folders",
|
||||
"Blame is not available for folders.");
|
||||
|
||||
free(walk_tree_ctx.curr_rev);
|
||||
}
|
|
@ -1,6 +0,0 @@
|
|||
#ifndef UI_BLAME_H
|
||||
#define UI_BLAME_H
|
||||
|
||||
extern void cgit_print_blame(void);
|
||||
|
||||
#endif /* UI_BLAME_H */
|
101
ui-blob.c
101
ui-blob.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-blob.h"
|
||||
#include "html.h"
|
||||
|
@ -15,13 +13,13 @@
|
|||
|
||||
struct walk_tree_context {
|
||||
const char *match_path;
|
||||
struct object_id *matched_oid;
|
||||
unsigned int found_path:1;
|
||||
unsigned int file_only:1;
|
||||
unsigned char *matched_sha1;
|
||||
int found_path:1;
|
||||
int file_only:1;
|
||||
};
|
||||
|
||||
static int walk_tree(const struct object_id *oid, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, void *cbdata)
|
||||
static int walk_tree(const unsigned char *sha1, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, int stage, void *cbdata)
|
||||
{
|
||||
struct walk_tree_context *walk_tree_ctx = cbdata;
|
||||
|
||||
|
@ -30,17 +28,17 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base,
|
|||
if (strncmp(base->buf, walk_tree_ctx->match_path, base->len)
|
||||
|| strcmp(walk_tree_ctx->match_path + base->len, pathname))
|
||||
return READ_TREE_RECURSIVE;
|
||||
oidcpy(walk_tree_ctx->matched_oid, oid);
|
||||
memmove(walk_tree_ctx->matched_sha1, sha1, 20);
|
||||
walk_tree_ctx->found_path = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int cgit_ref_path_exists(const char *path, const char *ref, int file_only)
|
||||
{
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
unsigned long size;
|
||||
struct pathspec_item path_items = {
|
||||
.match = xstrdup(path),
|
||||
.match = path,
|
||||
.len = strlen(path)
|
||||
};
|
||||
struct pathspec paths = {
|
||||
|
@ -49,27 +47,22 @@ int cgit_ref_path_exists(const char *path, const char *ref, int file_only)
|
|||
};
|
||||
struct walk_tree_context walk_tree_ctx = {
|
||||
.match_path = path,
|
||||
.matched_oid = &oid,
|
||||
.matched_sha1 = sha1,
|
||||
.found_path = 0,
|
||||
.file_only = file_only
|
||||
};
|
||||
|
||||
if (repo_get_oid(the_repository, ref, &oid))
|
||||
goto done;
|
||||
if (oid_object_info(the_repository, &oid, &size) != OBJ_COMMIT)
|
||||
goto done;
|
||||
read_tree(the_repository,
|
||||
repo_get_commit_tree(the_repository, lookup_commit_reference(the_repository, &oid)),
|
||||
&paths, walk_tree, &walk_tree_ctx);
|
||||
|
||||
done:
|
||||
free(path_items.match);
|
||||
if (get_sha1(ref, sha1))
|
||||
return 0;
|
||||
if (sha1_object_info(sha1, &size) != OBJ_COMMIT)
|
||||
return 0;
|
||||
read_tree_recursive(lookup_commit_reference(sha1)->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
|
||||
return walk_tree_ctx.found_path;
|
||||
}
|
||||
|
||||
int cgit_print_file(char *path, const char *head, int file_only)
|
||||
{
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
enum object_type type;
|
||||
char *buf;
|
||||
unsigned long size;
|
||||
|
@ -84,36 +77,34 @@ int cgit_print_file(char *path, const char *head, int file_only)
|
|||
};
|
||||
struct walk_tree_context walk_tree_ctx = {
|
||||
.match_path = path,
|
||||
.matched_oid = &oid,
|
||||
.matched_sha1 = sha1,
|
||||
.found_path = 0,
|
||||
.file_only = file_only
|
||||
};
|
||||
|
||||
if (repo_get_oid(the_repository, head, &oid))
|
||||
if (get_sha1(head, sha1))
|
||||
return -1;
|
||||
type = oid_object_info(the_repository, &oid, &size);
|
||||
if (type == OBJ_COMMIT) {
|
||||
commit = lookup_commit_reference(the_repository, &oid);
|
||||
read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
|
||||
&paths, walk_tree, &walk_tree_ctx);
|
||||
type = sha1_object_info(sha1, &size);
|
||||
if (type == OBJ_COMMIT && path) {
|
||||
commit = lookup_commit_reference(sha1);
|
||||
read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
|
||||
if (!walk_tree_ctx.found_path)
|
||||
return -1;
|
||||
type = oid_object_info(the_repository, &oid, &size);
|
||||
type = sha1_object_info(sha1, &size);
|
||||
}
|
||||
if (type == OBJ_BAD)
|
||||
return -1;
|
||||
buf = repo_read_object_file(the_repository, &oid, &type, &size);
|
||||
buf = read_sha1_file(sha1, &type, &size);
|
||||
if (!buf)
|
||||
return -1;
|
||||
buf[size] = '\0';
|
||||
html_raw(buf, size);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cgit_print_blob(const char *hex, char *path, const char *head, int file_only)
|
||||
{
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
enum object_type type;
|
||||
char *buf;
|
||||
unsigned long size;
|
||||
|
@ -128,57 +119,51 @@ void cgit_print_blob(const char *hex, char *path, const char *head, int file_onl
|
|||
};
|
||||
struct walk_tree_context walk_tree_ctx = {
|
||||
.match_path = path,
|
||||
.matched_oid = &oid,
|
||||
.matched_sha1 = sha1,
|
||||
.found_path = 0,
|
||||
.file_only = file_only
|
||||
};
|
||||
|
||||
if (hex) {
|
||||
if (get_oid_hex(hex, &oid)) {
|
||||
cgit_print_error_page(400, "Bad request",
|
||||
"Bad hex value: %s", hex);
|
||||
if (get_sha1_hex(hex, sha1)) {
|
||||
cgit_print_error("Bad hex value: %s", hex);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (repo_get_oid(the_repository, head, &oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad ref: %s", head);
|
||||
if (get_sha1(head, sha1)) {
|
||||
cgit_print_error("Bad ref: %s", head);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
type = oid_object_info(the_repository, &oid, &size);
|
||||
type = sha1_object_info(sha1, &size);
|
||||
|
||||
if ((!hex) && type == OBJ_COMMIT && path) {
|
||||
commit = lookup_commit_reference(the_repository, &oid);
|
||||
read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
|
||||
&paths, walk_tree, &walk_tree_ctx);
|
||||
type = oid_object_info(the_repository, &oid, &size);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
|
||||
type = sha1_object_info(sha1,&size);
|
||||
}
|
||||
|
||||
if (type == OBJ_BAD) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad object name: %s", hex);
|
||||
cgit_print_error("Bad object name: %s", hex);
|
||||
return;
|
||||
}
|
||||
|
||||
buf = repo_read_object_file(the_repository, &oid, &type, &size);
|
||||
buf = read_sha1_file(sha1, &type, &size);
|
||||
if (!buf) {
|
||||
cgit_print_error_page(500, "Internal server error",
|
||||
"Error reading object %s", hex);
|
||||
cgit_print_error("Error reading object %s", hex);
|
||||
return;
|
||||
}
|
||||
|
||||
buf[size] = '\0';
|
||||
if (buffer_is_binary(buf, size))
|
||||
ctx.page.mimetype = "application/octet-stream";
|
||||
else
|
||||
ctx.page.mimetype = "text/plain";
|
||||
ctx.page.mimetype = ctx.qry.mimetype;
|
||||
if (!ctx.page.mimetype) {
|
||||
if (buffer_is_binary(buf, size))
|
||||
ctx.page.mimetype = "application/octet-stream";
|
||||
else
|
||||
ctx.page.mimetype = "text/plain";
|
||||
}
|
||||
ctx.page.filename = path;
|
||||
|
||||
html("X-Content-Type-Options: nosniff\n");
|
||||
html("Content-Security-Policy: default-src 'none'\n");
|
||||
cgit_print_http_headers();
|
||||
html_raw(buf, size);
|
||||
free(buf);
|
||||
}
|
||||
|
|
54
ui-clone.c
54
ui-clone.c
|
@ -7,28 +7,24 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-clone.h"
|
||||
#include "html.h"
|
||||
#include "ui-shared.h"
|
||||
#include "packfile.h"
|
||||
#include "object-store.h"
|
||||
|
||||
static int print_ref_info(const char *refname, const struct object_id *oid,
|
||||
static int print_ref_info(const char *refname, const unsigned char *sha1,
|
||||
int flags, void *cb_data)
|
||||
{
|
||||
struct object *obj;
|
||||
|
||||
if (!(obj = parse_object(the_repository, oid)))
|
||||
if (!(obj = parse_object(sha1)))
|
||||
return 0;
|
||||
|
||||
htmlf("%s\t%s\n", oid_to_hex(oid), refname);
|
||||
htmlf("%s\t%s\n", sha1_to_hex(sha1), refname);
|
||||
if (obj->type == OBJ_TAG) {
|
||||
if (!(obj = deref_tag(the_repository, obj, refname, 0)))
|
||||
if (!(obj = deref_tag(obj, refname, 0)))
|
||||
return 0;
|
||||
htmlf("%s\t%s^{}\n", oid_to_hex(&obj->oid), refname);
|
||||
htmlf("%s\t%s^{}\n", sha1_to_hex(obj->sha1), refname);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
@ -41,8 +37,8 @@ static void print_pack_info(void)
|
|||
ctx.page.mimetype = "text/plain";
|
||||
ctx.page.filename = "objects/info/packs";
|
||||
cgit_print_http_headers();
|
||||
reprepare_packed_git(the_repository);
|
||||
for (pack = get_packed_git(the_repository); pack; pack = pack->next) {
|
||||
prepare_packed_git();
|
||||
for (pack = packed_git; pack; pack = pack->next) {
|
||||
if (pack->pack_local) {
|
||||
offset = strrchr(pack->pack_name, '/');
|
||||
if (offset && offset[1] != '\0')
|
||||
|
@ -54,20 +50,20 @@ static void print_pack_info(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void send_file(const char *path)
|
||||
static void send_file(char *path)
|
||||
{
|
||||
struct stat st;
|
||||
|
||||
if (stat(path, &st)) {
|
||||
switch (errno) {
|
||||
case ENOENT:
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
html_status(404, "Not found", 0);
|
||||
break;
|
||||
case EACCES:
|
||||
cgit_print_error_page(403, "Forbidden", "Forbidden");
|
||||
html_status(403, "Forbidden", 0);
|
||||
break;
|
||||
default:
|
||||
cgit_print_error_page(400, "Bad request", "Bad request");
|
||||
html_status(400, "Bad request", 0);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -82,45 +78,29 @@ static void send_file(const char *path)
|
|||
void cgit_clone_info(void)
|
||||
{
|
||||
if (!ctx.qry.path || strcmp(ctx.qry.path, "refs")) {
|
||||
cgit_print_error_page(400, "Bad request", "Bad request");
|
||||
html_status(400, "Bad request", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx.page.mimetype = "text/plain";
|
||||
ctx.page.filename = "info/refs";
|
||||
cgit_print_http_headers();
|
||||
refs_for_each_ref(get_main_ref_store(the_repository),
|
||||
print_ref_info, NULL);
|
||||
for_each_ref(print_ref_info, NULL);
|
||||
}
|
||||
|
||||
void cgit_clone_objects(void)
|
||||
{
|
||||
char *p;
|
||||
|
||||
if (!ctx.qry.path)
|
||||
goto err;
|
||||
if (!ctx.qry.path) {
|
||||
html_status(400, "Bad request", 0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strcmp(ctx.qry.path, "info/packs")) {
|
||||
print_pack_info();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Avoid directory traversal by forbidding "..", but also work around
|
||||
* other funny business by just specifying a fairly strict format. For
|
||||
* example, now we don't have to stress out about the Cygwin port.
|
||||
*/
|
||||
for (p = ctx.qry.path; *p; ++p) {
|
||||
if (*p == '.' && *(p + 1) == '.')
|
||||
goto err;
|
||||
if (!isalnum(*p) && *p != '/' && *p != '.' && *p != '-')
|
||||
goto err;
|
||||
}
|
||||
|
||||
send_file(git_path("objects/%s", ctx.qry.path));
|
||||
return;
|
||||
|
||||
err:
|
||||
cgit_print_error_page(400, "Bad request", "Bad request");
|
||||
}
|
||||
|
||||
void cgit_clone_head(void)
|
||||
|
|
52
ui-commit.c
52
ui-commit.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-commit.h"
|
||||
#include "html.h"
|
||||
|
@ -21,32 +19,28 @@ void cgit_print_commit(char *hex, const char *prefix)
|
|||
struct commitinfo *info, *parent_info;
|
||||
struct commit_list *p;
|
||||
struct strbuf notes = STRBUF_INIT;
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
char *tmp, *tmp2;
|
||||
int parents = 0;
|
||||
|
||||
if (!hex)
|
||||
hex = ctx.qry.head;
|
||||
|
||||
if (repo_get_oid(the_repository, hex, &oid)) {
|
||||
cgit_print_error_page(400, "Bad request",
|
||||
"Bad object id: %s", hex);
|
||||
if (get_sha1(hex, sha1)) {
|
||||
cgit_print_error("Bad object id: %s", hex);
|
||||
return;
|
||||
}
|
||||
commit = lookup_commit_reference(the_repository, &oid);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad commit reference: %s", hex);
|
||||
cgit_print_error("Bad commit reference: %s", hex);
|
||||
return;
|
||||
}
|
||||
info = cgit_parse_commit(commit);
|
||||
|
||||
format_display_notes(&oid, ¬es, PAGE_ENCODING, 1);
|
||||
format_display_notes(sha1, ¬es, PAGE_ENCODING, 0);
|
||||
|
||||
load_ref_decorations(NULL, DECORATE_FULL_REFS);
|
||||
load_ref_decorations(DECORATE_FULL_REFS);
|
||||
|
||||
ctx.page.title = fmtalloc("%s - %s", info->subject, ctx.page.title);
|
||||
cgit_print_layout_start();
|
||||
cgit_print_diff_ctrls();
|
||||
html("<table summary='commit info' class='commit-info'>\n");
|
||||
html("<tr><th>author</th><td>");
|
||||
|
@ -58,8 +52,7 @@ void cgit_print_commit(char *hex, const char *prefix)
|
|||
}
|
||||
cgit_close_filter(ctx.repo->email_filter);
|
||||
html("</td><td class='right'>");
|
||||
html_txt(show_date(info->author_date, info->author_tz,
|
||||
cgit_date_mode(DATE_ISO8601)));
|
||||
cgit_print_date(info->author_date, FMT_LONGDATE, ctx.cfg.local_time);
|
||||
html("</td></tr>\n");
|
||||
html("<tr><th>committer</th><td>");
|
||||
cgit_open_filter(ctx.repo->email_filter, info->committer_email, "commit");
|
||||
|
@ -70,18 +63,17 @@ void cgit_print_commit(char *hex, const char *prefix)
|
|||
}
|
||||
cgit_close_filter(ctx.repo->email_filter);
|
||||
html("</td><td class='right'>");
|
||||
html_txt(show_date(info->committer_date, info->committer_tz,
|
||||
cgit_date_mode(DATE_ISO8601)));
|
||||
cgit_print_date(info->committer_date, FMT_LONGDATE, ctx.cfg.local_time);
|
||||
html("</td></tr>\n");
|
||||
html("<tr><th>commit</th><td colspan='2' class='oid'>");
|
||||
tmp = oid_to_hex(&commit->object.oid);
|
||||
html("<tr><th>commit</th><td colspan='2' class='sha1'>");
|
||||
tmp = sha1_to_hex(commit->object.sha1);
|
||||
cgit_commit_link(tmp, NULL, NULL, ctx.qry.head, tmp, prefix);
|
||||
html(" (");
|
||||
cgit_patch_link("patch", NULL, NULL, NULL, tmp, prefix);
|
||||
html(")</td></tr>\n");
|
||||
html("<tr><th>tree</th><td colspan='2' class='oid'>");
|
||||
html("<tr><th>tree</th><td colspan='2' class='sha1'>");
|
||||
tmp = xstrdup(hex);
|
||||
cgit_tree_link(oid_to_hex(get_commit_tree_oid(commit)), NULL, NULL,
|
||||
cgit_tree_link(sha1_to_hex(commit->tree->object.sha1), NULL, NULL,
|
||||
ctx.qry.head, tmp, NULL);
|
||||
if (prefix) {
|
||||
html(" /");
|
||||
|
@ -90,7 +82,7 @@ void cgit_print_commit(char *hex, const char *prefix)
|
|||
free(tmp);
|
||||
html("</td></tr>\n");
|
||||
for (p = commit->parents; p; p = p->next) {
|
||||
parent = lookup_commit_reference(the_repository, &p->item->object.oid);
|
||||
parent = lookup_commit_reference(p->item->object.sha1);
|
||||
if (!parent) {
|
||||
html("<tr><td colspan='3'>");
|
||||
cgit_print_error("Error reading parent commit");
|
||||
|
@ -98,8 +90,8 @@ void cgit_print_commit(char *hex, const char *prefix)
|
|||
continue;
|
||||
}
|
||||
html("<tr><th>parent</th>"
|
||||
"<td colspan='2' class='oid'>");
|
||||
tmp = tmp2 = oid_to_hex(&p->item->object.oid);
|
||||
"<td colspan='2' class='sha1'>");
|
||||
tmp = tmp2 = sha1_to_hex(p->item->object.sha1);
|
||||
if (ctx.repo->enable_subject_links) {
|
||||
parent_info = cgit_parse_commit(parent);
|
||||
tmp2 = parent_info->subject;
|
||||
|
@ -107,13 +99,14 @@ void cgit_print_commit(char *hex, const char *prefix)
|
|||
cgit_commit_link(tmp2, NULL, NULL, ctx.qry.head, tmp, prefix);
|
||||
html(" (");
|
||||
cgit_diff_link("diff", NULL, NULL, ctx.qry.head, hex,
|
||||
oid_to_hex(&p->item->object.oid), prefix);
|
||||
sha1_to_hex(p->item->object.sha1), prefix);
|
||||
html(")</td></tr>");
|
||||
parents++;
|
||||
}
|
||||
if (ctx.repo->snapshots) {
|
||||
html("<tr><th>download</th><td colspan='2' class='oid'>");
|
||||
cgit_print_snapshot_links(ctx.repo, hex, "<br/>");
|
||||
html("<tr><th>download</th><td colspan='2' class='sha1'>");
|
||||
cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
|
||||
hex, ctx.repo->snapshots);
|
||||
html("</td></tr>");
|
||||
}
|
||||
html("</table>\n");
|
||||
|
@ -139,12 +132,11 @@ void cgit_print_commit(char *hex, const char *prefix)
|
|||
}
|
||||
if (parents < 3) {
|
||||
if (parents)
|
||||
tmp = oid_to_hex(&commit->parents->item->object.oid);
|
||||
tmp = sha1_to_hex(commit->parents->item->object.sha1);
|
||||
else
|
||||
tmp = NULL;
|
||||
cgit_print_diff(ctx.qry.oid, tmp, prefix, 0, 0);
|
||||
cgit_print_diff(ctx.qry.sha1, tmp, prefix, 0, 0);
|
||||
}
|
||||
strbuf_release(¬es);
|
||||
cgit_free_commitinfo(info);
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
|
164
ui-diff.c
164
ui-diff.c
|
@ -6,16 +6,14 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-diff.h"
|
||||
#include "html.h"
|
||||
#include "ui-shared.h"
|
||||
#include "ui-ssdiff.h"
|
||||
|
||||
struct object_id old_rev_oid[1];
|
||||
struct object_id new_rev_oid[1];
|
||||
unsigned char old_rev_sha1[20];
|
||||
unsigned char new_rev_sha1[20];
|
||||
|
||||
static int files, slots;
|
||||
static int total_adds, total_rems, max_changes;
|
||||
|
@ -23,8 +21,8 @@ static int lines_added, lines_removed;
|
|||
|
||||
static struct fileinfo {
|
||||
char status;
|
||||
struct object_id old_oid[1];
|
||||
struct object_id new_oid[1];
|
||||
unsigned char old_sha1[20];
|
||||
unsigned char new_sha1[20];
|
||||
unsigned short old_mode;
|
||||
unsigned short new_mode;
|
||||
char *old_path;
|
||||
|
@ -33,12 +31,11 @@ static struct fileinfo {
|
|||
unsigned int removed;
|
||||
unsigned long old_size;
|
||||
unsigned long new_size;
|
||||
unsigned int binary:1;
|
||||
int binary:1;
|
||||
} *items;
|
||||
|
||||
static int use_ssdiff = 0;
|
||||
static struct diff_filepair *current_filepair;
|
||||
static const char *current_prefix;
|
||||
|
||||
struct diff_filespec *cgit_get_current_old_file(void)
|
||||
{
|
||||
|
@ -84,23 +81,23 @@ static void print_fileinfo(struct fileinfo *info)
|
|||
}
|
||||
|
||||
html("<tr>");
|
||||
html("<td class='mode'>");
|
||||
if (is_null_oid(info->new_oid)) {
|
||||
htmlf("<td class='mode'>");
|
||||
if (is_null_sha1(info->new_sha1)) {
|
||||
cgit_print_filemode(info->old_mode);
|
||||
} else {
|
||||
cgit_print_filemode(info->new_mode);
|
||||
}
|
||||
|
||||
if (info->old_mode != info->new_mode &&
|
||||
!is_null_oid(info->old_oid) &&
|
||||
!is_null_oid(info->new_oid)) {
|
||||
!is_null_sha1(info->old_sha1) &&
|
||||
!is_null_sha1(info->new_sha1)) {
|
||||
html("<span class='modechange'>[");
|
||||
cgit_print_filemode(info->old_mode);
|
||||
html("]</span>");
|
||||
}
|
||||
htmlf("</td><td class='%s'>", class);
|
||||
cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
|
||||
ctx.qry.oid2, info->new_path);
|
||||
cgit_diff_link(info->new_path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
|
||||
ctx.qry.sha2, info->new_path);
|
||||
if (info->status == DIFF_STATUS_COPIED || info->status == DIFF_STATUS_RENAMED) {
|
||||
htmlf(" (%s from ",
|
||||
info->status == DIFF_STATUS_COPIED ? "copied" : "renamed");
|
||||
|
@ -135,34 +132,15 @@ static void count_diff_lines(char *line, int len)
|
|||
}
|
||||
}
|
||||
|
||||
static int show_filepair(struct diff_filepair *pair)
|
||||
{
|
||||
/* Always show if we have no limiting prefix. */
|
||||
if (!current_prefix)
|
||||
return 1;
|
||||
|
||||
/* Show if either path in the pair begins with the prefix. */
|
||||
if (starts_with(pair->one->path, current_prefix) ||
|
||||
starts_with(pair->two->path, current_prefix))
|
||||
return 1;
|
||||
|
||||
/* Otherwise we don't want to show this filepair. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void inspect_filepair(struct diff_filepair *pair)
|
||||
{
|
||||
int binary = 0;
|
||||
unsigned long old_size = 0;
|
||||
unsigned long new_size = 0;
|
||||
|
||||
if (!show_filepair(pair))
|
||||
return;
|
||||
|
||||
files++;
|
||||
lines_added = 0;
|
||||
lines_removed = 0;
|
||||
cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size, &new_size,
|
||||
cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size, &new_size,
|
||||
&binary, 0, ctx.qry.ignorews, count_diff_lines);
|
||||
if (files >= slots) {
|
||||
if (slots == 0)
|
||||
|
@ -172,8 +150,8 @@ static void inspect_filepair(struct diff_filepair *pair)
|
|||
items = xrealloc(items, slots * sizeof(struct fileinfo));
|
||||
}
|
||||
items[files-1].status = pair->status;
|
||||
oidcpy(items[files-1].old_oid, &pair->one->oid);
|
||||
oidcpy(items[files-1].new_oid, &pair->two->oid);
|
||||
hashcpy(items[files-1].old_sha1, pair->one->sha1);
|
||||
hashcpy(items[files-1].new_sha1, pair->two->sha1);
|
||||
items[files-1].old_mode = pair->one->mode;
|
||||
items[files-1].new_mode = pair->two->mode;
|
||||
items[files-1].old_path = xstrdup(pair->one->path);
|
||||
|
@ -189,15 +167,15 @@ static void inspect_filepair(struct diff_filepair *pair)
|
|||
total_rems += lines_removed;
|
||||
}
|
||||
|
||||
static void cgit_print_diffstat(const struct object_id *old_oid,
|
||||
const struct object_id *new_oid,
|
||||
static void cgit_print_diffstat(const unsigned char *old_sha1,
|
||||
const unsigned char *new_sha1,
|
||||
const char *prefix)
|
||||
{
|
||||
int i;
|
||||
|
||||
html("<div class='diffstat-header'>");
|
||||
cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.oid,
|
||||
ctx.qry.oid2, NULL);
|
||||
cgit_diff_link("Diffstat", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
|
||||
ctx.qry.sha2, NULL);
|
||||
if (prefix) {
|
||||
html(" (limited to '");
|
||||
html_txt(prefix);
|
||||
|
@ -206,7 +184,7 @@ static void cgit_print_diffstat(const struct object_id *old_oid,
|
|||
html("</div>");
|
||||
html("<table summary='diffstat' class='diffstat'>");
|
||||
max_changes = 0;
|
||||
cgit_diff_tree(old_oid, new_oid, inspect_filepair, prefix,
|
||||
cgit_diff_tree(old_sha1, new_sha1, inspect_filepair, prefix,
|
||||
ctx.qry.ignorews);
|
||||
for (i = 0; i<files; i++)
|
||||
print_fileinfo(&items[i]);
|
||||
|
@ -240,8 +218,8 @@ static void print_line(char *line, int len)
|
|||
line[len-1] = c;
|
||||
}
|
||||
|
||||
static void header(const struct object_id *oid1, char *path1, int mode1,
|
||||
const struct object_id *oid2, char *path2, int mode2)
|
||||
static void header(unsigned char *sha1, char *path1, int mode1,
|
||||
unsigned char *sha2, char *path2, int mode2)
|
||||
{
|
||||
char *abbrev1, *abbrev2;
|
||||
int subproject;
|
||||
|
@ -260,8 +238,8 @@ static void header(const struct object_id *oid1, char *path1, int mode1,
|
|||
htmlf("<br/>deleted file mode %.6o", mode1);
|
||||
|
||||
if (!subproject) {
|
||||
abbrev1 = xstrdup(repo_find_unique_abbrev(the_repository, oid1, DEFAULT_ABBREV));
|
||||
abbrev2 = xstrdup(repo_find_unique_abbrev(the_repository, oid2, DEFAULT_ABBREV));
|
||||
abbrev1 = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV));
|
||||
abbrev2 = xstrdup(find_unique_abbrev(sha2, DEFAULT_ABBREV));
|
||||
htmlf("<br/>index %s..%s", abbrev1, abbrev2);
|
||||
free(abbrev1);
|
||||
free(abbrev2);
|
||||
|
@ -270,24 +248,24 @@ static void header(const struct object_id *oid1, char *path1, int mode1,
|
|||
if (mode2 != mode1)
|
||||
htmlf("..%.6o", mode2);
|
||||
}
|
||||
if (is_null_oid(oid1)) {
|
||||
if (is_null_sha1(sha1)) {
|
||||
path1 = "dev/null";
|
||||
html("<br/>--- /");
|
||||
} else
|
||||
html("<br/>--- a/");
|
||||
if (mode1 != 0)
|
||||
cgit_tree_link(path1, NULL, NULL, ctx.qry.head,
|
||||
oid_to_hex(old_rev_oid), path1);
|
||||
sha1_to_hex(old_rev_sha1), path1);
|
||||
else
|
||||
html_txt(path1);
|
||||
if (is_null_oid(oid2)) {
|
||||
if (is_null_sha1(sha2)) {
|
||||
path2 = "dev/null";
|
||||
html("<br/>+++ /");
|
||||
} else
|
||||
html("<br/>+++ b/");
|
||||
if (mode2 != 0)
|
||||
cgit_tree_link(path2, NULL, NULL, ctx.qry.head,
|
||||
oid_to_hex(new_rev_oid), path2);
|
||||
sha1_to_hex(new_rev_sha1), path2);
|
||||
else
|
||||
html_txt(path2);
|
||||
}
|
||||
|
@ -301,28 +279,25 @@ static void filepair_cb(struct diff_filepair *pair)
|
|||
int binary = 0;
|
||||
linediff_fn print_line_fn = print_line;
|
||||
|
||||
if (!show_filepair(pair))
|
||||
return;
|
||||
|
||||
current_filepair = pair;
|
||||
if (use_ssdiff) {
|
||||
cgit_ssdiff_header_begin();
|
||||
print_line_fn = cgit_ssdiff_line_cb;
|
||||
}
|
||||
header(&pair->one->oid, pair->one->path, pair->one->mode,
|
||||
&pair->two->oid, pair->two->path, pair->two->mode);
|
||||
header(pair->one->sha1, pair->one->path, pair->one->mode,
|
||||
pair->two->sha1, pair->two->path, pair->two->mode);
|
||||
if (use_ssdiff)
|
||||
cgit_ssdiff_header_end();
|
||||
if (S_ISGITLINK(pair->one->mode) || S_ISGITLINK(pair->two->mode)) {
|
||||
if (S_ISGITLINK(pair->one->mode))
|
||||
print_line_fn(fmt("-Subproject %s", oid_to_hex(&pair->one->oid)), 52);
|
||||
print_line_fn(fmt("-Subproject %s", sha1_to_hex(pair->one->sha1)), 52);
|
||||
if (S_ISGITLINK(pair->two->mode))
|
||||
print_line_fn(fmt("+Subproject %s", oid_to_hex(&pair->two->oid)), 52);
|
||||
print_line_fn(fmt("+Subproject %s", sha1_to_hex(pair->two->sha1)), 52);
|
||||
if (use_ssdiff)
|
||||
cgit_ssdiff_footer();
|
||||
return;
|
||||
}
|
||||
if (cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
|
||||
if (cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
|
||||
&new_size, &binary, ctx.qry.context,
|
||||
ctx.qry.ignorews, print_line_fn))
|
||||
cgit_print_error("Error running diff");
|
||||
|
@ -336,13 +311,13 @@ static void filepair_cb(struct diff_filepair *pair)
|
|||
cgit_ssdiff_footer();
|
||||
}
|
||||
|
||||
void cgit_print_diff_ctrls(void)
|
||||
void cgit_print_diff_ctrls()
|
||||
{
|
||||
int i, curr;
|
||||
|
||||
html("<div class='cgit-panel'>");
|
||||
html("<b>diff options</b>");
|
||||
html("<form method='get'>");
|
||||
html("<form method='get' action='.'>");
|
||||
cgit_add_hidden_formfields(1, 0, ctx.qry.page);
|
||||
html("<table>");
|
||||
html("<tr><td colspan='2'/></tr>");
|
||||
|
@ -387,75 +362,59 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
|
|||
const char *prefix, int show_ctrls, int raw)
|
||||
{
|
||||
struct commit *commit, *commit2;
|
||||
const struct object_id *old_tree_oid, *new_tree_oid;
|
||||
const unsigned char *old_tree_sha1, *new_tree_sha1;
|
||||
diff_type difftype;
|
||||
|
||||
/*
|
||||
* If "follow" is set then the diff machinery needs to examine the
|
||||
* entire commit to detect renames so we must limit the paths in our
|
||||
* own callbacks and not pass the prefix to the diff machinery.
|
||||
*/
|
||||
if (ctx.qry.follow && ctx.cfg.enable_follow_links) {
|
||||
current_prefix = prefix;
|
||||
prefix = "";
|
||||
} else {
|
||||
current_prefix = NULL;
|
||||
}
|
||||
|
||||
if (!new_rev)
|
||||
new_rev = ctx.qry.head;
|
||||
if (repo_get_oid(the_repository, new_rev, new_rev_oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad object name: %s", new_rev);
|
||||
if (get_sha1(new_rev, new_rev_sha1)) {
|
||||
cgit_print_error("Bad object name: %s", new_rev);
|
||||
return;
|
||||
}
|
||||
commit = lookup_commit_reference(the_repository, new_rev_oid);
|
||||
if (!commit || repo_parse_commit(the_repository, commit)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad commit: %s", oid_to_hex(new_rev_oid));
|
||||
commit = lookup_commit_reference(new_rev_sha1);
|
||||
if (!commit || parse_commit(commit)) {
|
||||
cgit_print_error("Bad commit: %s", sha1_to_hex(new_rev_sha1));
|
||||
return;
|
||||
}
|
||||
new_tree_oid = get_commit_tree_oid(commit);
|
||||
new_tree_sha1 = commit->tree->object.sha1;
|
||||
|
||||
if (old_rev) {
|
||||
if (repo_get_oid(the_repository, old_rev, old_rev_oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad object name: %s", old_rev);
|
||||
if (get_sha1(old_rev, old_rev_sha1)) {
|
||||
cgit_print_error("Bad object name: %s", old_rev);
|
||||
return;
|
||||
}
|
||||
} else if (commit->parents && commit->parents->item) {
|
||||
oidcpy(old_rev_oid, &commit->parents->item->object.oid);
|
||||
hashcpy(old_rev_sha1, commit->parents->item->object.sha1);
|
||||
} else {
|
||||
oidclr(old_rev_oid, the_repository->hash_algo);
|
||||
hashclr(old_rev_sha1);
|
||||
}
|
||||
|
||||
if (!is_null_oid(old_rev_oid)) {
|
||||
commit2 = lookup_commit_reference(the_repository, old_rev_oid);
|
||||
if (!commit2 || repo_parse_commit(the_repository, commit2)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad commit: %s", oid_to_hex(old_rev_oid));
|
||||
if (!is_null_sha1(old_rev_sha1)) {
|
||||
commit2 = lookup_commit_reference(old_rev_sha1);
|
||||
if (!commit2 || parse_commit(commit2)) {
|
||||
cgit_print_error("Bad commit: %s", sha1_to_hex(old_rev_sha1));
|
||||
return;
|
||||
}
|
||||
old_tree_oid = get_commit_tree_oid(commit2);
|
||||
old_tree_sha1 = commit2->tree->object.sha1;
|
||||
} else {
|
||||
old_tree_oid = NULL;
|
||||
old_tree_sha1 = NULL;
|
||||
}
|
||||
|
||||
if (raw) {
|
||||
struct diff_options diffopt;
|
||||
|
||||
repo_diff_setup(the_repository, &diffopt);
|
||||
diff_setup(&diffopt);
|
||||
diffopt.output_format = DIFF_FORMAT_PATCH;
|
||||
diffopt.flags.recursive = 1;
|
||||
DIFF_OPT_SET(&diffopt, RECURSIVE);
|
||||
diff_setup_done(&diffopt);
|
||||
|
||||
ctx.page.mimetype = "text/plain";
|
||||
cgit_print_http_headers();
|
||||
if (old_tree_oid) {
|
||||
diff_tree_oid(old_tree_oid, new_tree_oid, "",
|
||||
if (old_tree_sha1) {
|
||||
diff_tree_sha1(old_tree_sha1, new_tree_sha1, "",
|
||||
&diffopt);
|
||||
} else {
|
||||
diff_root_tree_oid(new_tree_oid, "", &diffopt);
|
||||
diff_root_tree_sha1(new_tree_sha1, "", &diffopt);
|
||||
}
|
||||
diffcore_std(&diffopt);
|
||||
diff_flush(&diffopt);
|
||||
|
@ -466,10 +425,8 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
|
|||
difftype = ctx.qry.has_difftype ? ctx.qry.difftype : ctx.cfg.difftype;
|
||||
use_ssdiff = difftype == DIFF_SSDIFF;
|
||||
|
||||
if (show_ctrls) {
|
||||
cgit_print_layout_start();
|
||||
if (show_ctrls)
|
||||
cgit_print_diff_ctrls();
|
||||
}
|
||||
|
||||
/*
|
||||
* Clicking on a link to a file in the diff stat should show a diff
|
||||
|
@ -481,7 +438,7 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
|
|||
if (difftype == DIFF_STATONLY)
|
||||
ctx.qry.difftype = ctx.cfg.difftype;
|
||||
|
||||
cgit_print_diffstat(old_rev_oid, new_rev_oid, prefix);
|
||||
cgit_print_diffstat(old_rev_sha1, new_rev_sha1, prefix);
|
||||
|
||||
if (difftype == DIFF_STATONLY)
|
||||
return;
|
||||
|
@ -492,12 +449,9 @@ void cgit_print_diff(const char *new_rev, const char *old_rev,
|
|||
html("<table summary='diff' class='diff'>");
|
||||
html("<tr><td>");
|
||||
}
|
||||
cgit_diff_tree(old_rev_oid, new_rev_oid, filepair_cb, prefix,
|
||||
cgit_diff_tree(old_rev_sha1, new_rev_sha1, filepair_cb, prefix,
|
||||
ctx.qry.ignorews);
|
||||
if (!use_ssdiff)
|
||||
html("</td></tr>");
|
||||
html("</table>");
|
||||
|
||||
if (show_ctrls)
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef UI_DIFF_H
|
||||
#define UI_DIFF_H
|
||||
|
||||
extern void cgit_print_diff_ctrls(void);
|
||||
extern void cgit_print_diff_ctrls();
|
||||
|
||||
extern void cgit_print_diff(const char *new_hex, const char *old_hex,
|
||||
const char *prefix, int show_ctrls, int raw);
|
||||
|
@ -9,7 +9,7 @@ extern void cgit_print_diff(const char *new_hex, const char *old_hex,
|
|||
extern struct diff_filespec *cgit_get_current_old_file(void);
|
||||
extern struct diff_filespec *cgit_get_current_new_file(void);
|
||||
|
||||
extern struct object_id old_rev_oid[1];
|
||||
extern struct object_id new_rev_oid[1];
|
||||
extern unsigned char old_rev_sha1[20];
|
||||
extern unsigned char new_rev_sha1[20];
|
||||
|
||||
#endif /* UI_DIFF_H */
|
||||
|
|
273
ui-log.c
273
ui-log.c
|
@ -6,15 +6,13 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-log.h"
|
||||
#include "html.h"
|
||||
#include "ui-shared.h"
|
||||
#include "strvec.h"
|
||||
#include "argv-array.h"
|
||||
|
||||
static int files, add_lines, rem_lines, lines_counted;
|
||||
int files, add_lines, rem_lines;
|
||||
|
||||
/*
|
||||
* The list of available column colors in the commit graph.
|
||||
|
@ -51,7 +49,7 @@ static void inspect_files(struct diff_filepair *pair)
|
|||
|
||||
files++;
|
||||
if (ctx.repo->enable_log_linecount)
|
||||
cgit_diff_files(&pair->one->oid, &pair->two->oid, &old_size,
|
||||
cgit_diff_files(pair->one->sha1, pair->two->sha1, &old_size,
|
||||
&new_size, &binary, 0, ctx.qry.ignorews,
|
||||
count_lines);
|
||||
}
|
||||
|
@ -63,118 +61,43 @@ void show_commit_decorations(struct commit *commit)
|
|||
|
||||
buf[sizeof(buf) - 1] = 0;
|
||||
deco = get_name_decoration(&commit->object);
|
||||
if (!deco)
|
||||
return;
|
||||
html("<span class='decoration'>");
|
||||
while (deco) {
|
||||
struct object_id oid_tag, peeled;
|
||||
int is_annotated = 0;
|
||||
|
||||
strlcpy(buf, prettify_refname(deco->name), sizeof(buf));
|
||||
switch(deco->type) {
|
||||
case DECORATION_NONE:
|
||||
/* If the git-core doesn't recognize it,
|
||||
* don't display anything. */
|
||||
break;
|
||||
case DECORATION_REF_LOCAL:
|
||||
if (starts_with(deco->name, "refs/heads/")) {
|
||||
strncpy(buf, deco->name + 11, sizeof(buf) - 1);
|
||||
cgit_log_link(buf, NULL, "branch-deco", buf, NULL,
|
||||
ctx.qry.vpath, 0, NULL, NULL,
|
||||
ctx.qry.showmsg, 0);
|
||||
break;
|
||||
case DECORATION_REF_TAG:
|
||||
if (!refs_read_ref(get_main_ref_store(the_repository), deco->name, &oid_tag) &&
|
||||
!peel_iterated_oid(the_repository, &oid_tag, &peeled))
|
||||
is_annotated = !oideq(&oid_tag, &peeled);
|
||||
cgit_tag_link(buf, NULL, is_annotated ? "tag-annotated-deco" : "tag-deco", buf);
|
||||
break;
|
||||
case DECORATION_REF_REMOTE:
|
||||
if (!ctx.repo->enable_remote_branches)
|
||||
break;
|
||||
cgit_log_link(buf, NULL, "remote-deco", NULL,
|
||||
oid_to_hex(&commit->object.oid),
|
||||
ctx.qry.vpath, 0, NULL, NULL,
|
||||
ctx.qry.showmsg, 0);
|
||||
break;
|
||||
default:
|
||||
cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
|
||||
oid_to_hex(&commit->object.oid),
|
||||
ctx.qry.vpath);
|
||||
break;
|
||||
ctx.qry.vpath, 0, NULL, NULL,
|
||||
ctx.qry.showmsg);
|
||||
}
|
||||
else if (starts_with(deco->name, "tag: refs/tags/")) {
|
||||
strncpy(buf, deco->name + 15, sizeof(buf) - 1);
|
||||
cgit_tag_link(buf, NULL, "tag-deco", buf);
|
||||
}
|
||||
else if (starts_with(deco->name, "refs/tags/")) {
|
||||
strncpy(buf, deco->name + 10, sizeof(buf) - 1);
|
||||
cgit_tag_link(buf, NULL, "tag-deco", buf);
|
||||
}
|
||||
else if (starts_with(deco->name, "refs/remotes/")) {
|
||||
if (!ctx.repo->enable_remote_branches)
|
||||
goto next;
|
||||
strncpy(buf, deco->name + 13, sizeof(buf) - 1);
|
||||
cgit_log_link(buf, NULL, "remote-deco", NULL,
|
||||
sha1_to_hex(commit->object.sha1),
|
||||
ctx.qry.vpath, 0, NULL, NULL,
|
||||
ctx.qry.showmsg);
|
||||
}
|
||||
else {
|
||||
strncpy(buf, deco->name, sizeof(buf) - 1);
|
||||
cgit_commit_link(buf, NULL, "deco", ctx.qry.head,
|
||||
sha1_to_hex(commit->object.sha1),
|
||||
ctx.qry.vpath);
|
||||
}
|
||||
next:
|
||||
deco = deco->next;
|
||||
}
|
||||
html("</span>");
|
||||
}
|
||||
|
||||
static void handle_rename(struct diff_filepair *pair)
|
||||
{
|
||||
/*
|
||||
* After we have seen a rename, we generate links to the previous
|
||||
* name of the file so that commit & diff views get fed the path
|
||||
* that is correct for the commit they are showing, avoiding the
|
||||
* need to walk the entire history leading back to every commit we
|
||||
* show in order detect renames.
|
||||
*/
|
||||
if (0 != strcmp(ctx.qry.vpath, pair->two->path)) {
|
||||
free(ctx.qry.vpath);
|
||||
ctx.qry.vpath = xstrdup(pair->two->path);
|
||||
}
|
||||
inspect_files(pair);
|
||||
}
|
||||
|
||||
static int show_commit(struct commit *commit, struct rev_info *revs)
|
||||
{
|
||||
struct commit_list *parents = commit->parents;
|
||||
struct commit *parent;
|
||||
int found = 0, saved_fmt;
|
||||
struct diff_flags saved_flags = revs->diffopt.flags;
|
||||
|
||||
/* Always show if we're not in "follow" mode with a single file. */
|
||||
if (!ctx.qry.follow)
|
||||
return 1;
|
||||
|
||||
/*
|
||||
* In "follow" mode, we don't show merges. This is consistent with
|
||||
* "git log --follow -- <file>".
|
||||
*/
|
||||
if (parents && parents->next)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* If this is the root commit, do what rev_info tells us.
|
||||
*/
|
||||
if (!parents)
|
||||
return revs->show_root_diff;
|
||||
|
||||
/* When we get here we have precisely one parent. */
|
||||
parent = parents->item;
|
||||
/* If we can't parse the commit, let print_commit() report an error. */
|
||||
if (repo_parse_commit(the_repository, parent))
|
||||
return 1;
|
||||
|
||||
files = 0;
|
||||
add_lines = 0;
|
||||
rem_lines = 0;
|
||||
|
||||
revs->diffopt.flags.recursive = 1;
|
||||
diff_tree_oid(get_commit_tree_oid(parent),
|
||||
get_commit_tree_oid(commit),
|
||||
"", &revs->diffopt);
|
||||
diffcore_std(&revs->diffopt);
|
||||
|
||||
found = !diff_queue_is_empty(&revs->diffopt);
|
||||
saved_fmt = revs->diffopt.output_format;
|
||||
revs->diffopt.output_format = DIFF_FORMAT_CALLBACK;
|
||||
revs->diffopt.format_callback = cgit_diff_tree_cb;
|
||||
revs->diffopt.format_callback_data = handle_rename;
|
||||
diff_flush(&revs->diffopt);
|
||||
revs->diffopt.output_format = saved_fmt;
|
||||
revs->diffopt.flags = saved_flags;
|
||||
|
||||
lines_counted = 1;
|
||||
return found;
|
||||
}
|
||||
|
||||
static void print_commit(struct commit *commit, struct rev_info *revs)
|
||||
{
|
||||
struct commitinfo *info;
|
||||
|
@ -211,7 +134,7 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
|
|||
}
|
||||
else {
|
||||
html("<td>");
|
||||
cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
|
||||
cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
|
||||
html("</td>");
|
||||
}
|
||||
|
||||
|
@ -238,11 +161,11 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
|
|||
strbuf_add(&msgbuf, "\n\n", 2);
|
||||
|
||||
/* Place wrap_symbol at position i in info->subject */
|
||||
strlcpy(info->subject + i, wrap_symbol, subject_len - i + 1);
|
||||
strcpy(info->subject + i, wrap_symbol);
|
||||
}
|
||||
}
|
||||
cgit_commit_link(info->subject, NULL, NULL, ctx.qry.head,
|
||||
oid_to_hex(&commit->object.oid), ctx.qry.vpath);
|
||||
sha1_to_hex(commit->object.sha1), ctx.qry.vpath);
|
||||
show_commit_decorations(commit);
|
||||
html("</td><td>");
|
||||
cgit_open_filter(ctx.repo->email_filter, info->author_email, "log");
|
||||
|
@ -251,11 +174,10 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
|
|||
|
||||
if (revs->graph) {
|
||||
html("</td><td>");
|
||||
cgit_print_age(info->committer_date, info->committer_tz, TM_WEEK * 2);
|
||||
cgit_print_age(commit->date, TM_WEEK * 2, FMT_SHORTDATE);
|
||||
}
|
||||
|
||||
if (!lines_counted && (ctx.repo->enable_log_filecount ||
|
||||
ctx.repo->enable_log_linecount)) {
|
||||
if (ctx.repo->enable_log_filecount || ctx.repo->enable_log_linecount) {
|
||||
files = 0;
|
||||
add_lines = 0;
|
||||
rem_lines = 0;
|
||||
|
@ -265,14 +187,12 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
|
|||
if (ctx.repo->enable_log_filecount)
|
||||
htmlf("</td><td>%d", files);
|
||||
if (ctx.repo->enable_log_linecount)
|
||||
htmlf("</td><td><span class='deletions'>-%d</span>/"
|
||||
"<span class='insertions'>+%d</span>", rem_lines, add_lines);
|
||||
htmlf("</td><td>-%d/+%d", rem_lines, add_lines);
|
||||
|
||||
html("</td></tr>\n");
|
||||
|
||||
if ((revs->graph && !graph_is_commit_finished(revs->graph))
|
||||
|| ctx.qry.showmsg) { /* Print a second table row */
|
||||
html("<tr class='nohover-highlight'>");
|
||||
if (revs->graph || ctx.qry.showmsg) { /* Print a second table row */
|
||||
html("<tr class='nohover'>");
|
||||
|
||||
if (ctx.qry.showmsg) {
|
||||
/* Concatenate commit message + notes in msgbuf */
|
||||
|
@ -280,7 +200,7 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
|
|||
strbuf_addstr(&msgbuf, info->msg);
|
||||
strbuf_addch(&msgbuf, '\n');
|
||||
}
|
||||
format_display_notes(&commit->object.oid,
|
||||
format_display_notes(commit->object.sha1,
|
||||
&msgbuf, PAGE_ENCODING, 0);
|
||||
strbuf_addch(&msgbuf, '\n');
|
||||
strbuf_ltrim(&msgbuf);
|
||||
|
@ -329,11 +249,11 @@ static void print_commit(struct commit *commit, struct rev_info *revs)
|
|||
|
||||
static const char *disambiguate_ref(const char *ref, int *must_free_result)
|
||||
{
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
struct strbuf longref = STRBUF_INIT;
|
||||
|
||||
strbuf_addf(&longref, "refs/heads/%s", ref);
|
||||
if (repo_get_oid(the_repository, longref.buf, &oid) == 0) {
|
||||
if (get_sha1(longref.buf, sha1) == 0) {
|
||||
*must_free_result = 1;
|
||||
return strbuf_detach(&longref, NULL);
|
||||
}
|
||||
|
@ -366,27 +286,27 @@ static char *next_token(char **src)
|
|||
}
|
||||
|
||||
void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern,
|
||||
const char *path, int pager, int commit_graph, int commit_sort)
|
||||
char *path, int pager, int commit_graph, int commit_sort)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct commit *commit;
|
||||
struct strvec rev_argv = STRVEC_INIT;
|
||||
struct argv_array rev_argv = ARGV_ARRAY_INIT;
|
||||
int i, columns = commit_graph ? 4 : 3;
|
||||
int must_free_tip = 0;
|
||||
|
||||
/* rev_argv.argv[0] will be ignored by setup_revisions */
|
||||
strvec_push(&rev_argv, "log_rev_setup");
|
||||
argv_array_push(&rev_argv, "log_rev_setup");
|
||||
|
||||
if (!tip)
|
||||
tip = ctx.qry.head;
|
||||
tip = disambiguate_ref(tip, &must_free_tip);
|
||||
strvec_push(&rev_argv, tip);
|
||||
argv_array_push(&rev_argv, tip);
|
||||
|
||||
if (grep && pattern && *pattern) {
|
||||
pattern = xstrdup(pattern);
|
||||
if (!strcmp(grep, "grep") || !strcmp(grep, "author") ||
|
||||
!strcmp(grep, "committer")) {
|
||||
strvec_pushf(&rev_argv, "--%s=%s", grep, pattern);
|
||||
argv_array_pushf(&rev_argv, "--%s=%s", grep, pattern);
|
||||
} else if (!strcmp(grep, "range")) {
|
||||
char *arg;
|
||||
/* Split the pattern at whitespace and add each token
|
||||
|
@ -394,69 +314,48 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
|
|||
* rev-list options. Also, replace the previously
|
||||
* pushed tip (it's no longer relevant).
|
||||
*/
|
||||
strvec_pop(&rev_argv);
|
||||
argv_array_pop(&rev_argv);
|
||||
while ((arg = next_token(&pattern))) {
|
||||
if (*arg == '-') {
|
||||
fprintf(stderr, "Bad range expr: %s\n",
|
||||
arg);
|
||||
break;
|
||||
}
|
||||
strvec_push(&rev_argv, arg);
|
||||
argv_array_push(&rev_argv, arg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!path || !ctx.cfg.enable_follow_links) {
|
||||
/*
|
||||
* If we don't have a path, "follow" is a no-op so make sure
|
||||
* the variable is set to false to avoid needing to check
|
||||
* both this and whether we have a path everywhere.
|
||||
*/
|
||||
ctx.qry.follow = 0;
|
||||
}
|
||||
|
||||
if (commit_graph && !ctx.qry.follow) {
|
||||
strvec_push(&rev_argv, "--graph");
|
||||
strvec_push(&rev_argv, "--color");
|
||||
if (commit_graph) {
|
||||
argv_array_push(&rev_argv, "--graph");
|
||||
argv_array_push(&rev_argv, "--color");
|
||||
graph_set_column_colors(column_colors_html,
|
||||
COLUMN_COLORS_HTML_MAX);
|
||||
}
|
||||
|
||||
if (commit_sort == 1)
|
||||
strvec_push(&rev_argv, "--date-order");
|
||||
argv_array_push(&rev_argv, "--date-order");
|
||||
else if (commit_sort == 2)
|
||||
strvec_push(&rev_argv, "--topo-order");
|
||||
argv_array_push(&rev_argv, "--topo-order");
|
||||
|
||||
if (path && ctx.qry.follow)
|
||||
strvec_push(&rev_argv, "--follow");
|
||||
strvec_push(&rev_argv, "--");
|
||||
argv_array_push(&rev_argv, "--");
|
||||
if (path)
|
||||
strvec_push(&rev_argv, path);
|
||||
argv_array_push(&rev_argv, path);
|
||||
|
||||
repo_init_revisions(the_repository, &rev, NULL);
|
||||
init_revisions(&rev, NULL);
|
||||
rev.abbrev = DEFAULT_ABBREV;
|
||||
rev.commit_format = CMIT_FMT_DEFAULT;
|
||||
rev.verbose_header = 1;
|
||||
rev.show_root_diff = 0;
|
||||
rev.ignore_missing = 1;
|
||||
rev.simplify_history = 1;
|
||||
setup_revisions(rev_argv.nr, rev_argv.v, &rev, NULL);
|
||||
load_ref_decorations(NULL, DECORATE_FULL_REFS);
|
||||
setup_revisions(rev_argv.argc, rev_argv.argv, &rev, NULL);
|
||||
load_ref_decorations(DECORATE_FULL_REFS);
|
||||
rev.show_decorations = 1;
|
||||
rev.grep_filter.ignore_case = 1;
|
||||
|
||||
rev.diffopt.detect_rename = 1;
|
||||
rev.diffopt.rename_limit = ctx.cfg.renamelimit;
|
||||
if (ctx.qry.ignorews)
|
||||
DIFF_XDL_SET(&rev.diffopt, IGNORE_WHITESPACE);
|
||||
|
||||
rev.grep_filter.regflags |= REG_ICASE;
|
||||
compile_grep_patterns(&rev.grep_filter);
|
||||
prepare_revision_walk(&rev);
|
||||
|
||||
if (pager) {
|
||||
cgit_print_layout_start();
|
||||
if (pager)
|
||||
html("<table class='list nowrap'>");
|
||||
}
|
||||
|
||||
html("<tr class='nohover'>");
|
||||
if (commit_graph)
|
||||
|
@ -467,14 +366,13 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
|
|||
if (pager) {
|
||||
html(" (");
|
||||
cgit_log_link(ctx.qry.showmsg ? "Collapse" : "Expand", NULL,
|
||||
NULL, ctx.qry.head, ctx.qry.oid,
|
||||
NULL, ctx.qry.head, ctx.qry.sha1,
|
||||
ctx.qry.vpath, ctx.qry.ofs, ctx.qry.grep,
|
||||
ctx.qry.search, ctx.qry.showmsg ? 0 : 1,
|
||||
ctx.qry.follow);
|
||||
ctx.qry.search, ctx.qry.showmsg ? 0 : 1);
|
||||
html(")");
|
||||
}
|
||||
html("</th><th class='left'>Author</th>");
|
||||
if (rev.graph)
|
||||
if (commit_graph)
|
||||
html("<th class='left'>Age</th>");
|
||||
if (ctx.repo->enable_log_filecount) {
|
||||
html("<th class='left'>Files</th>");
|
||||
|
@ -489,31 +387,16 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
|
|||
if (ofs<0)
|
||||
ofs = 0;
|
||||
|
||||
for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; /* nop */) {
|
||||
if (show_commit(commit, &rev))
|
||||
i++;
|
||||
release_commit_memory(the_repository->parsed_objects, commit);
|
||||
for (i = 0; i < ofs && (commit = get_revision(&rev)) != NULL; i++) {
|
||||
free_commit_buffer(commit);
|
||||
free_commit_list(commit->parents);
|
||||
commit->parents = NULL;
|
||||
}
|
||||
|
||||
for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; /* nop */) {
|
||||
/*
|
||||
* In "follow" mode, we must count the files and lines the
|
||||
* first time we invoke diff on a given commit, and we need
|
||||
* to do that to see if the commit touches the path we care
|
||||
* about, so we do it in show_commit. Hence we must clear
|
||||
* lines_counted here.
|
||||
*
|
||||
* This has the side effect of avoiding running diff twice
|
||||
* when we are both following renames and showing file
|
||||
* and/or line counts.
|
||||
*/
|
||||
lines_counted = 0;
|
||||
if (show_commit(commit, &rev)) {
|
||||
i++;
|
||||
print_commit(commit, &rev);
|
||||
}
|
||||
release_commit_memory(the_repository->parsed_objects, commit);
|
||||
for (i = 0; i < cnt && (commit = get_revision(&rev)) != NULL; i++) {
|
||||
print_commit(commit, &rev);
|
||||
free_commit_buffer(commit);
|
||||
free_commit_list(commit->parents);
|
||||
commit->parents = NULL;
|
||||
}
|
||||
if (pager) {
|
||||
|
@ -521,28 +404,24 @@ void cgit_print_log(const char *tip, int ofs, int cnt, char *grep, char *pattern
|
|||
if (ofs > 0) {
|
||||
html("<li>");
|
||||
cgit_log_link("[prev]", NULL, NULL, ctx.qry.head,
|
||||
ctx.qry.oid, ctx.qry.vpath,
|
||||
ctx.qry.sha1, ctx.qry.vpath,
|
||||
ofs - cnt, ctx.qry.grep,
|
||||
ctx.qry.search, ctx.qry.showmsg,
|
||||
ctx.qry.follow);
|
||||
ctx.qry.search, ctx.qry.showmsg);
|
||||
html("</li>");
|
||||
}
|
||||
if ((commit = get_revision(&rev)) != NULL) {
|
||||
html("<li>");
|
||||
cgit_log_link("[next]", NULL, NULL, ctx.qry.head,
|
||||
ctx.qry.oid, ctx.qry.vpath,
|
||||
ctx.qry.sha1, ctx.qry.vpath,
|
||||
ofs + cnt, ctx.qry.grep,
|
||||
ctx.qry.search, ctx.qry.showmsg,
|
||||
ctx.qry.follow);
|
||||
ctx.qry.search, ctx.qry.showmsg);
|
||||
html("</li>");
|
||||
}
|
||||
html("</ul>");
|
||||
cgit_print_layout_end();
|
||||
} else if ((commit = get_revision(&rev)) != NULL) {
|
||||
htmlf("<tr class='nohover'><td colspan='%d'>", columns);
|
||||
cgit_log_link("[...]", NULL, NULL, ctx.qry.head, NULL,
|
||||
ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg,
|
||||
ctx.qry.follow);
|
||||
ctx.qry.vpath, 0, NULL, NULL, ctx.qry.showmsg);
|
||||
html("</td></tr>\n");
|
||||
}
|
||||
|
||||
|
|
2
ui-log.h
2
ui-log.h
|
@ -2,7 +2,7 @@
|
|||
#define UI_LOG_H
|
||||
|
||||
extern void cgit_print_log(const char *tip, int ofs, int cnt, char *grep,
|
||||
char *pattern, const char *path, int pager,
|
||||
char *pattern, char *path, int pager,
|
||||
int commit_graph, int commit_sort);
|
||||
extern void show_commit_decorations(struct commit *commit);
|
||||
|
||||
|
|
56
ui-patch.c
56
ui-patch.c
|
@ -6,67 +6,54 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-patch.h"
|
||||
#include "html.h"
|
||||
#include "ui-shared.h"
|
||||
|
||||
/* two commit hashes with two dots in between and termination */
|
||||
#define REV_RANGE_LEN 2 * GIT_MAX_HEXSZ + 3
|
||||
|
||||
void cgit_print_patch(const char *new_rev, const char *old_rev,
|
||||
const char *prefix)
|
||||
{
|
||||
struct rev_info rev;
|
||||
struct commit *commit;
|
||||
struct object_id new_rev_oid, old_rev_oid;
|
||||
char rev_range[REV_RANGE_LEN];
|
||||
const char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range, "--", prefix, NULL };
|
||||
int rev_argc = ARRAY_SIZE(rev_argv) - 1;
|
||||
unsigned char new_rev_sha1[20], old_rev_sha1[20];
|
||||
char rev_range[2 * 40 + 3];
|
||||
char *rev_argv[] = { NULL, "--reverse", "--format=email", rev_range };
|
||||
char *patchname;
|
||||
|
||||
if (!prefix)
|
||||
rev_argc--;
|
||||
|
||||
if (!new_rev)
|
||||
new_rev = ctx.qry.head;
|
||||
|
||||
if (repo_get_oid(the_repository, new_rev, &new_rev_oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad object id: %s", new_rev);
|
||||
if (get_sha1(new_rev, new_rev_sha1)) {
|
||||
cgit_print_error("Bad object id: %s", new_rev);
|
||||
return;
|
||||
}
|
||||
commit = lookup_commit_reference(the_repository, &new_rev_oid);
|
||||
commit = lookup_commit_reference(new_rev_sha1);
|
||||
if (!commit) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad commit reference: %s", new_rev);
|
||||
cgit_print_error("Bad commit reference: %s", new_rev);
|
||||
return;
|
||||
}
|
||||
|
||||
if (old_rev) {
|
||||
if (repo_get_oid(the_repository, old_rev, &old_rev_oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad object id: %s", old_rev);
|
||||
if (get_sha1(old_rev, old_rev_sha1)) {
|
||||
cgit_print_error("Bad object id: %s", old_rev);
|
||||
return;
|
||||
}
|
||||
if (!lookup_commit_reference(the_repository, &old_rev_oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad commit reference: %s", old_rev);
|
||||
if (!lookup_commit_reference(old_rev_sha1)) {
|
||||
cgit_print_error("Bad commit reference: %s", old_rev);
|
||||
return;
|
||||
}
|
||||
} else if (commit->parents && commit->parents->item) {
|
||||
oidcpy(&old_rev_oid, &commit->parents->item->object.oid);
|
||||
hashcpy(old_rev_sha1, commit->parents->item->object.sha1);
|
||||
} else {
|
||||
oidclr(&old_rev_oid, the_repository->hash_algo);
|
||||
hashclr(old_rev_sha1);
|
||||
}
|
||||
|
||||
if (is_null_oid(&old_rev_oid)) {
|
||||
memcpy(rev_range, oid_to_hex(&new_rev_oid), the_hash_algo->hexsz + 1);
|
||||
if (is_null_sha1(old_rev_sha1)) {
|
||||
memcpy(rev_range, sha1_to_hex(new_rev_sha1), 41);
|
||||
} else {
|
||||
xsnprintf(rev_range, REV_RANGE_LEN, "%s..%s", oid_to_hex(&old_rev_oid),
|
||||
oid_to_hex(&new_rev_oid));
|
||||
sprintf(rev_range, "%s..%s", sha1_to_hex(old_rev_sha1),
|
||||
sha1_to_hex(new_rev_sha1));
|
||||
}
|
||||
|
||||
patchname = fmt("%s.patch", rev_range);
|
||||
|
@ -80,7 +67,7 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
|
|||
"%s%n%n%w(0)%b";
|
||||
}
|
||||
|
||||
repo_init_revisions(the_repository, &rev, NULL);
|
||||
init_revisions(&rev, NULL);
|
||||
rev.abbrev = DEFAULT_ABBREV;
|
||||
rev.verbose_header = 1;
|
||||
rev.diff = 1;
|
||||
|
@ -88,13 +75,14 @@ void cgit_print_patch(const char *new_rev, const char *old_rev,
|
|||
rev.max_parents = 1;
|
||||
rev.diffopt.output_format |= DIFF_FORMAT_DIFFSTAT |
|
||||
DIFF_FORMAT_PATCH | DIFF_FORMAT_SUMMARY;
|
||||
if (prefix)
|
||||
rev.diffopt.stat_sep = fmt("(limited to '%s')\n\n", prefix);
|
||||
setup_revisions(rev_argc, rev_argv, &rev, NULL);
|
||||
setup_revisions(ARRAY_SIZE(rev_argv), (const char **)rev_argv, &rev,
|
||||
NULL);
|
||||
prepare_revision_walk(&rev);
|
||||
|
||||
while ((commit = get_revision(&rev)) != NULL) {
|
||||
log_tree_commit(&rev, commit);
|
||||
printf("-- \ncgit %s\n\n", cgit_version);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
|
151
ui-plain.c
151
ui-plain.c
|
@ -6,8 +6,7 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include <stdio.h>
|
||||
#include "cgit.h"
|
||||
#include "ui-plain.h"
|
||||
#include "html.h"
|
||||
|
@ -18,37 +17,81 @@ struct walk_tree_context {
|
|||
int match;
|
||||
};
|
||||
|
||||
static int print_object(const struct object_id *oid, const char *path)
|
||||
static char *get_mimetype_from_file(const char *filename, const char *ext)
|
||||
{
|
||||
enum object_type type;
|
||||
char *buf, *mimetype;
|
||||
unsigned long size;
|
||||
static const char *delimiters;
|
||||
char *result;
|
||||
FILE *fd;
|
||||
char line[1024];
|
||||
char *mimetype;
|
||||
char *token;
|
||||
|
||||
type = oid_object_info(the_repository, oid, &size);
|
||||
if (type == OBJ_BAD) {
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
return 0;
|
||||
}
|
||||
if (!filename)
|
||||
return NULL;
|
||||
|
||||
buf = repo_read_object_file(the_repository, oid, &type, &size);
|
||||
if (!buf) {
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
return 0;
|
||||
}
|
||||
fd = fopen(filename, "r");
|
||||
if (!fd)
|
||||
return NULL;
|
||||
|
||||
mimetype = get_mimetype_for_filename(path);
|
||||
ctx.page.mimetype = mimetype;
|
||||
delimiters = " \t\r\n";
|
||||
result = NULL;
|
||||
|
||||
if (!ctx.repo->enable_html_serving) {
|
||||
html("X-Content-Type-Options: nosniff\n");
|
||||
html("Content-Security-Policy: default-src 'none'\n");
|
||||
if (mimetype) {
|
||||
/* Built-in white list allows PDF and everything that isn't text/ and application/ */
|
||||
if ((!strncmp(mimetype, "text/", 5) || !strncmp(mimetype, "application/", 12)) && strcmp(mimetype, "application/pdf"))
|
||||
ctx.page.mimetype = NULL;
|
||||
/* loop over all lines in the file */
|
||||
while (!result && fgets(line, sizeof(line), fd)) {
|
||||
mimetype = strtok(line, delimiters);
|
||||
|
||||
/* skip empty lines and comment lines */
|
||||
if (!mimetype || (mimetype[0] == '#'))
|
||||
continue;
|
||||
|
||||
/* loop over all extensions of mimetype */
|
||||
while ((token = strtok(NULL, delimiters))) {
|
||||
if (!strcasecmp(ext, token)) {
|
||||
result = xstrdup(mimetype);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
fclose(fd);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static int print_object(const unsigned char *sha1, const char *path)
|
||||
{
|
||||
enum object_type type;
|
||||
char *buf, *ext;
|
||||
unsigned long size;
|
||||
struct string_list_item *mime;
|
||||
int freemime;
|
||||
|
||||
type = sha1_object_info(sha1, &size);
|
||||
if (type == OBJ_BAD) {
|
||||
html_status(404, "Not found", 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = read_sha1_file(sha1, &type, &size);
|
||||
if (!buf) {
|
||||
html_status(404, "Not found", 0);
|
||||
return 0;
|
||||
}
|
||||
ctx.page.mimetype = NULL;
|
||||
ext = strrchr(path, '.');
|
||||
freemime = 0;
|
||||
if (ext && *(++ext)) {
|
||||
mime = string_list_lookup(&ctx.cfg.mimetypes, ext);
|
||||
if (mime) {
|
||||
ctx.page.mimetype = (char *)mime->util;
|
||||
ctx.page.charset = NULL;
|
||||
} else {
|
||||
ctx.page.mimetype = get_mimetype_from_file(ctx.cfg.mimetype_file, ext);
|
||||
if (ctx.page.mimetype) {
|
||||
freemime = 1;
|
||||
ctx.page.charset = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!ctx.page.mimetype) {
|
||||
if (buffer_is_binary(buf, size)) {
|
||||
ctx.page.mimetype = "application/octet-stream";
|
||||
|
@ -59,11 +102,12 @@ static int print_object(const struct object_id *oid, const char *path)
|
|||
}
|
||||
ctx.page.filename = path;
|
||||
ctx.page.size = size;
|
||||
ctx.page.etag = oid_to_hex(oid);
|
||||
ctx.page.etag = sha1_to_hex(sha1);
|
||||
cgit_print_http_headers();
|
||||
html_raw(buf, size);
|
||||
free(mimetype);
|
||||
free(buf);
|
||||
/* If we allocated this, then casting away const is safe. */
|
||||
if (freemime)
|
||||
free((char*) ctx.page.mimetype);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -75,7 +119,7 @@ static char *buildpath(const char *base, int baselen, const char *path)
|
|||
return fmtalloc("%.*s/", baselen, base);
|
||||
}
|
||||
|
||||
static void print_dir(const struct object_id *oid, const char *base,
|
||||
static void print_dir(const unsigned char *sha1, const char *base,
|
||||
int baselen, const char *path)
|
||||
{
|
||||
char *fullpath, *slash;
|
||||
|
@ -83,7 +127,7 @@ static void print_dir(const struct object_id *oid, const char *base,
|
|||
|
||||
fullpath = buildpath(base, baselen, path);
|
||||
slash = (fullpath[0] == '/' ? "" : "/");
|
||||
ctx.page.etag = oid_to_hex(oid);
|
||||
ctx.page.etag = sha1_to_hex(sha1);
|
||||
cgit_print_http_headers();
|
||||
htmlf("<html><head><title>%s", slash);
|
||||
html_txt(fullpath);
|
||||
|
@ -96,19 +140,17 @@ static void print_dir(const struct object_id *oid, const char *base,
|
|||
slash = strrchr(fullpath, '/');
|
||||
if (slash)
|
||||
*(slash + 1) = 0;
|
||||
else {
|
||||
free(fullpath);
|
||||
else
|
||||
fullpath = NULL;
|
||||
}
|
||||
html("<li>");
|
||||
cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.oid,
|
||||
cgit_plain_link("../", NULL, NULL, ctx.qry.head, ctx.qry.sha1,
|
||||
fullpath);
|
||||
html("</li>\n");
|
||||
}
|
||||
free(fullpath);
|
||||
}
|
||||
|
||||
static void print_dir_entry(const struct object_id *oid, const char *base,
|
||||
static void print_dir_entry(const unsigned char *sha1, const char *base,
|
||||
int baselen, const char *path, unsigned mode)
|
||||
{
|
||||
char *fullpath;
|
||||
|
@ -118,9 +160,9 @@ static void print_dir_entry(const struct object_id *oid, const char *base,
|
|||
fullpath[strlen(fullpath) - 1] = 0;
|
||||
html(" <li>");
|
||||
if (S_ISGITLINK(mode)) {
|
||||
cgit_submodule_link(NULL, fullpath, oid_to_hex(oid));
|
||||
cgit_submodule_link(NULL, fullpath, sha1_to_hex(sha1));
|
||||
} else
|
||||
cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.oid,
|
||||
cgit_plain_link(path, NULL, NULL, ctx.qry.head, ctx.qry.sha1,
|
||||
fullpath);
|
||||
html("</li>\n");
|
||||
free(fullpath);
|
||||
|
@ -131,22 +173,22 @@ static void print_dir_tail(void)
|
|||
html(" </ul>\n</body></html>\n");
|
||||
}
|
||||
|
||||
static int walk_tree(const struct object_id *oid, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, void *cbdata)
|
||||
static int walk_tree(const unsigned char *sha1, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, int stage, void *cbdata)
|
||||
{
|
||||
struct walk_tree_context *walk_tree_ctx = cbdata;
|
||||
|
||||
if (base->len == walk_tree_ctx->match_baselen) {
|
||||
if (S_ISREG(mode) || S_ISLNK(mode)) {
|
||||
if (print_object(oid, pathname))
|
||||
if (S_ISREG(mode)) {
|
||||
if (print_object(sha1, pathname))
|
||||
walk_tree_ctx->match = 1;
|
||||
} else if (S_ISDIR(mode)) {
|
||||
print_dir(oid, base->buf, base->len, pathname);
|
||||
print_dir(sha1, base->buf, base->len, pathname);
|
||||
walk_tree_ctx->match = 2;
|
||||
return READ_TREE_RECURSIVE;
|
||||
}
|
||||
} else if (base->len < INT_MAX && (int)base->len > walk_tree_ctx->match_baselen) {
|
||||
print_dir_entry(oid, base->buf, base->len, pathname, mode);
|
||||
} else if (base->len > walk_tree_ctx->match_baselen) {
|
||||
print_dir_entry(sha1, base->buf, base->len, pathname, mode);
|
||||
walk_tree_ctx->match = 2;
|
||||
} else if (S_ISDIR(mode)) {
|
||||
return READ_TREE_RECURSIVE;
|
||||
|
@ -165,8 +207,8 @@ static int basedir_len(const char *path)
|
|||
|
||||
void cgit_print_plain(void)
|
||||
{
|
||||
const char *rev = ctx.qry.oid;
|
||||
struct object_id oid;
|
||||
const char *rev = ctx.qry.sha1;
|
||||
unsigned char sha1[20];
|
||||
struct commit *commit;
|
||||
struct pathspec_item path_items = {
|
||||
.match = ctx.qry.path,
|
||||
|
@ -183,27 +225,26 @@ void cgit_print_plain(void)
|
|||
if (!rev)
|
||||
rev = ctx.qry.head;
|
||||
|
||||
if (repo_get_oid(the_repository, rev, &oid)) {
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
if (get_sha1(rev, sha1)) {
|
||||
html_status(404, "Not found", 0);
|
||||
return;
|
||||
}
|
||||
commit = lookup_commit_reference(the_repository, &oid);
|
||||
if (!commit || repo_parse_commit(the_repository, commit)) {
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit || parse_commit(commit)) {
|
||||
html_status(404, "Not found", 0);
|
||||
return;
|
||||
}
|
||||
if (!path_items.match) {
|
||||
path_items.match = "";
|
||||
walk_tree_ctx.match_baselen = -1;
|
||||
print_dir(get_commit_tree_oid(commit), "", 0, "");
|
||||
print_dir(commit->tree->object.sha1, "", 0, "");
|
||||
walk_tree_ctx.match = 2;
|
||||
}
|
||||
else
|
||||
walk_tree_ctx.match_baselen = basedir_len(path_items.match);
|
||||
read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
|
||||
&paths, walk_tree, &walk_tree_ctx);
|
||||
read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
|
||||
if (!walk_tree_ctx.match)
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
html_status(404, "Not found", 0);
|
||||
else if (walk_tree_ctx.match == 2)
|
||||
print_dir_tail();
|
||||
}
|
||||
|
|
66
ui-refs.c
66
ui-refs.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-refs.h"
|
||||
#include "html.h"
|
||||
|
@ -65,7 +63,7 @@ static int print_branch(struct refinfo *ref)
|
|||
return 1;
|
||||
html("<tr><td>");
|
||||
cgit_log_link(name, NULL, NULL, name, NULL, NULL, 0, NULL, NULL,
|
||||
ctx.qry.showmsg, 0);
|
||||
ctx.qry.showmsg);
|
||||
html("</td><td>");
|
||||
|
||||
if (ref->object->type == OBJ_COMMIT) {
|
||||
|
@ -75,7 +73,7 @@ static int print_branch(struct refinfo *ref)
|
|||
html_txt(info->author);
|
||||
cgit_close_filter(ctx.repo->email_filter);
|
||||
html("</td><td colspan='2'>");
|
||||
cgit_print_age(info->committer_date, info->committer_tz, -1);
|
||||
cgit_print_age(info->commit->date, -1, NULL);
|
||||
} else {
|
||||
html("</td><td></td><td>");
|
||||
cgit_object_link(ref->object);
|
||||
|
@ -84,7 +82,7 @@ static int print_branch(struct refinfo *ref)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static void print_tag_header(void)
|
||||
static void print_tag_header()
|
||||
{
|
||||
html("<tr class='nohover'><th class='left'>Tag</th>"
|
||||
"<th class='left'>Download</th>"
|
||||
|
@ -92,6 +90,40 @@ static void print_tag_header(void)
|
|||
"<th class='left' colspan='2'>Age</th></tr>\n");
|
||||
}
|
||||
|
||||
static void print_tag_downloads(const struct cgit_repo *repo, const char *ref)
|
||||
{
|
||||
const struct cgit_snapshot_format* f;
|
||||
struct strbuf filename = STRBUF_INIT;
|
||||
const char *basename;
|
||||
int free_ref = 0;
|
||||
|
||||
if (!ref || strlen(ref) < 1)
|
||||
return;
|
||||
|
||||
basename = cgit_repobasename(repo->url);
|
||||
if (!starts_with(ref, basename)) {
|
||||
if ((ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]))
|
||||
ref++;
|
||||
if (isdigit(ref[0])) {
|
||||
ref = fmtalloc("%s-%s", basename, ref);
|
||||
free_ref = 1;
|
||||
}
|
||||
}
|
||||
|
||||
for (f = cgit_snapshot_formats; f->suffix; f++) {
|
||||
if (!(repo->snapshots & f->bit))
|
||||
continue;
|
||||
strbuf_reset(&filename);
|
||||
strbuf_addf(&filename, "%s%s", ref, f->suffix);
|
||||
cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL, filename.buf);
|
||||
html(" ");
|
||||
}
|
||||
|
||||
if (free_ref)
|
||||
free((char *)ref);
|
||||
strbuf_release(&filename);
|
||||
}
|
||||
|
||||
static int print_tag(struct refinfo *ref)
|
||||
{
|
||||
struct tag *tag = NULL;
|
||||
|
@ -103,7 +135,7 @@ static int print_tag(struct refinfo *ref)
|
|||
tag = (struct tag *)obj;
|
||||
obj = tag->tagged;
|
||||
info = ref->tag;
|
||||
if (!info)
|
||||
if (!tag || !info)
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -111,7 +143,7 @@ static int print_tag(struct refinfo *ref)
|
|||
cgit_tag_link(name, NULL, NULL, name);
|
||||
html("</td><td>");
|
||||
if (ctx.repo->snapshots && (obj->type == OBJ_COMMIT))
|
||||
cgit_print_snapshot_links(ctx.repo, name, " ");
|
||||
print_tag_downloads(ctx.repo, name);
|
||||
else
|
||||
cgit_object_link(obj);
|
||||
html("</td><td>");
|
||||
|
@ -129,16 +161,16 @@ static int print_tag(struct refinfo *ref)
|
|||
html("</td><td colspan='2'>");
|
||||
if (info) {
|
||||
if (info->tagger_date > 0)
|
||||
cgit_print_age(info->tagger_date, info->tagger_tz, -1);
|
||||
cgit_print_age(info->tagger_date, -1, NULL);
|
||||
} else if (ref->object->type == OBJ_COMMIT) {
|
||||
cgit_print_age(ref->commit->commit->date, 0, -1);
|
||||
cgit_print_age(ref->commit->commit->date, -1, NULL);
|
||||
}
|
||||
html("</td></tr>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_refs_link(const char *path)
|
||||
static void print_refs_link(char *path)
|
||||
{
|
||||
html("<tr class='nohover'><td colspan='5'>");
|
||||
cgit_refs_link("[...]", NULL, NULL, ctx.qry.head, NULL, path);
|
||||
|
@ -157,11 +189,9 @@ void cgit_print_branches(int maxcount)
|
|||
|
||||
list.refs = NULL;
|
||||
list.alloc = list.count = 0;
|
||||
refs_for_each_branch_ref(get_main_ref_store(the_repository),
|
||||
cgit_refs_cb, &list);
|
||||
for_each_branch_ref(cgit_refs_cb, &list);
|
||||
if (ctx.repo->enable_remote_branches)
|
||||
refs_for_each_remote_ref(get_main_ref_store(the_repository),
|
||||
cgit_refs_cb, &list);
|
||||
for_each_remote_ref(cgit_refs_cb, &list);
|
||||
|
||||
if (maxcount == 0 || maxcount > list.count)
|
||||
maxcount = list.count;
|
||||
|
@ -186,8 +216,7 @@ void cgit_print_tags(int maxcount)
|
|||
|
||||
list.refs = NULL;
|
||||
list.alloc = list.count = 0;
|
||||
refs_for_each_tag_ref(get_main_ref_store(the_repository),
|
||||
cgit_refs_cb, &list);
|
||||
for_each_tag_ref(cgit_refs_cb, &list);
|
||||
if (list.count == 0)
|
||||
return;
|
||||
qsort(list.refs, list.count, sizeof(*list.refs), cmp_tag_age);
|
||||
|
@ -205,9 +234,9 @@ void cgit_print_tags(int maxcount)
|
|||
cgit_free_reflist_inner(&list);
|
||||
}
|
||||
|
||||
void cgit_print_refs(void)
|
||||
void cgit_print_refs()
|
||||
{
|
||||
cgit_print_layout_start();
|
||||
|
||||
html("<table class='list nowrap'>");
|
||||
|
||||
if (ctx.qry.path && starts_with(ctx.qry.path, "heads"))
|
||||
|
@ -220,5 +249,4 @@ void cgit_print_refs(void)
|
|||
cgit_print_tags(0);
|
||||
}
|
||||
html("</table>");
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,6 @@
|
|||
|
||||
extern void cgit_print_branches(int maxcount);
|
||||
extern void cgit_print_tags(int maxcount);
|
||||
extern void cgit_print_refs(void);
|
||||
extern void cgit_print_refs();
|
||||
|
||||
#endif /* UI_REFS_H */
|
||||
|
|
123
ui-repolist.c
123
ui-repolist.c
|
@ -10,18 +10,17 @@
|
|||
#include "ui-repolist.h"
|
||||
#include "html.h"
|
||||
#include "ui-shared.h"
|
||||
#include <strings.h>
|
||||
|
||||
static time_t read_agefile(const char *path)
|
||||
static time_t read_agefile(char *path)
|
||||
{
|
||||
time_t result;
|
||||
size_t size;
|
||||
char *buf = NULL;
|
||||
char *buf;
|
||||
struct strbuf date_buf = STRBUF_INIT;
|
||||
|
||||
if (readfile(path, &buf, &size)) {
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
if (readfile(path, &buf, &size))
|
||||
return -1;
|
||||
|
||||
if (parse_date(buf, &date_buf) == 0)
|
||||
result = strtoul(date_buf.buf, NULL, 10);
|
||||
|
@ -79,7 +78,7 @@ static void print_modtime(struct cgit_repo *repo)
|
|||
{
|
||||
time_t t;
|
||||
if (get_repo_modtime(repo, &t))
|
||||
cgit_print_age(t, 0, -1);
|
||||
cgit_print_age(t, -1, NULL);
|
||||
}
|
||||
|
||||
static int is_match(struct cgit_repo *repo)
|
||||
|
@ -106,41 +105,19 @@ static int is_in_url(struct cgit_repo *repo)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int is_visible(struct cgit_repo *repo)
|
||||
{
|
||||
if (repo->hide || repo->ignore)
|
||||
return 0;
|
||||
if (!(is_match(repo) && is_in_url(repo)))
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int any_repos_visible(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < cgit_repolist.count; i++) {
|
||||
if (is_visible(&cgit_repolist.repos[i]))
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void print_sort_header(const char *title, const char *sort)
|
||||
{
|
||||
char *currenturl = cgit_currenturl();
|
||||
html("<th class='left'><a href='");
|
||||
html_attr(currenturl);
|
||||
html_attr(cgit_currenturl());
|
||||
htmlf("?s=%s", sort);
|
||||
if (ctx.qry.search) {
|
||||
html("&q=");
|
||||
html_url_arg(ctx.qry.search);
|
||||
}
|
||||
htmlf("'>%s</a></th>", title);
|
||||
free(currenturl);
|
||||
}
|
||||
|
||||
static void print_header(void)
|
||||
static void print_header()
|
||||
{
|
||||
html("<tr class='nohover'>");
|
||||
print_sort_header("Name", "name");
|
||||
|
@ -184,6 +161,27 @@ static int cmp(const char *s1, const char *s2)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int sort_section(const void *a, const void *b)
|
||||
{
|
||||
const struct cgit_repo *r1 = a;
|
||||
const struct cgit_repo *r2 = b;
|
||||
int result;
|
||||
time_t t;
|
||||
|
||||
result = cmp(r1->section, r2->section);
|
||||
if (!result) {
|
||||
if (!strcmp(ctx.cfg.repository_sort, "age")) {
|
||||
// get_repo_modtime caches the value in r->mtime, so we don't
|
||||
// have to worry about inefficiencies here.
|
||||
if (get_repo_modtime(r1, &t) && get_repo_modtime(r2, &t))
|
||||
result = r2->mtime - r1->mtime;
|
||||
}
|
||||
if (!result)
|
||||
result = cmp(r1->name, r2->name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static int sort_name(const void *a, const void *b)
|
||||
{
|
||||
const struct cgit_repo *r1 = a;
|
||||
|
@ -220,28 +218,12 @@ static int sort_idle(const void *a, const void *b)
|
|||
return t2 - t1;
|
||||
}
|
||||
|
||||
static int sort_section(const void *a, const void *b)
|
||||
{
|
||||
const struct cgit_repo *r1 = a;
|
||||
const struct cgit_repo *r2 = b;
|
||||
int result;
|
||||
|
||||
result = cmp(r1->section, r2->section);
|
||||
if (!result) {
|
||||
if (!strcmp(ctx.cfg.repository_sort, "age"))
|
||||
result = sort_idle(r1, r2);
|
||||
if (!result)
|
||||
result = cmp(r1->name, r2->name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
struct sortcolumn {
|
||||
const char *name;
|
||||
int (*fn)(const void *a, const void *b);
|
||||
};
|
||||
|
||||
static const struct sortcolumn sortcolumn[] = {
|
||||
struct sortcolumn sortcolumn[] = {
|
||||
{"section", sort_section},
|
||||
{"name", sort_name},
|
||||
{"desc", sort_desc},
|
||||
|
@ -252,7 +234,7 @@ static const struct sortcolumn sortcolumn[] = {
|
|||
|
||||
static int sort_repolist(char *field)
|
||||
{
|
||||
const struct sortcolumn *column;
|
||||
struct sortcolumn *column;
|
||||
|
||||
for (column = &sortcolumn[0]; column->name; column++) {
|
||||
if (strcmp(field, column->name))
|
||||
|
@ -265,19 +247,13 @@ static int sort_repolist(char *field)
|
|||
}
|
||||
|
||||
|
||||
void cgit_print_repolist(void)
|
||||
void cgit_print_repolist()
|
||||
{
|
||||
int i, columns = 3, hits = 0, header = 0;
|
||||
char *last_section = NULL;
|
||||
char *section;
|
||||
char *repourl;
|
||||
int sorted = 0;
|
||||
|
||||
if (!any_repos_visible()) {
|
||||
cgit_print_error_page(404, "Not found", "No repositories found");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ctx.cfg.enable_index_links)
|
||||
++columns;
|
||||
if (ctx.cfg.enable_index_owner)
|
||||
|
@ -288,6 +264,9 @@ void cgit_print_repolist(void)
|
|||
cgit_print_docstart();
|
||||
cgit_print_pageheader();
|
||||
|
||||
if (ctx.cfg.index_header)
|
||||
html_include(ctx.cfg.index_header);
|
||||
|
||||
if (ctx.qry.sort)
|
||||
sorted = sort_repolist(ctx.qry.sort);
|
||||
else if (ctx.cfg.section_sort)
|
||||
|
@ -296,7 +275,9 @@ void cgit_print_repolist(void)
|
|||
html("<table summary='repository list' class='list nowrap'>");
|
||||
for (i = 0; i < cgit_repolist.count; i++) {
|
||||
ctx.repo = &cgit_repolist.repos[i];
|
||||
if (!is_visible(ctx.repo))
|
||||
if (ctx.repo->hide || ctx.repo->ignore)
|
||||
continue;
|
||||
if (!(is_match(ctx.repo) && is_in_url(ctx.repo)))
|
||||
continue;
|
||||
hits++;
|
||||
if (hits <= ctx.qry.ofs)
|
||||
|
@ -313,7 +294,7 @@ void cgit_print_repolist(void)
|
|||
(last_section != NULL && section == NULL) ||
|
||||
(last_section != NULL && section != NULL &&
|
||||
strcmp(section, last_section)))) {
|
||||
htmlf("<tr class='nohover-highlight'><td colspan='%d' class='reposection'>",
|
||||
htmlf("<tr class='nohover'><td colspan='%d' class='reposection'>",
|
||||
columns);
|
||||
html_txt(section);
|
||||
html("</td></tr>");
|
||||
|
@ -321,13 +302,10 @@ void cgit_print_repolist(void)
|
|||
}
|
||||
htmlf("<tr><td class='%s'>",
|
||||
!sorted && section ? "sublevel-repo" : "toplevel-repo");
|
||||
cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
|
||||
cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
|
||||
html("</td><td>");
|
||||
repourl = cgit_repourl(ctx.repo->url);
|
||||
html_link_open(repourl, NULL, NULL);
|
||||
free(repourl);
|
||||
if (html_ntxt(ctx.repo->desc, ctx.cfg.max_repodesc_len) < 0)
|
||||
html("...");
|
||||
html_link_open(cgit_repourl(ctx.repo->url), NULL, NULL);
|
||||
html_ntxt(ctx.cfg.max_repodesc_len, ctx.repo->desc);
|
||||
html_link_close();
|
||||
html("</td><td>");
|
||||
if (ctx.cfg.enable_index_owner) {
|
||||
|
@ -336,15 +314,13 @@ void cgit_print_repolist(void)
|
|||
html_txt(ctx.repo->owner);
|
||||
cgit_close_filter(ctx.repo->owner_filter);
|
||||
} else {
|
||||
char *currenturl = cgit_currenturl();
|
||||
html("<a href='");
|
||||
html_attr(currenturl);
|
||||
html_attr(cgit_currenturl());
|
||||
html("?q=");
|
||||
html_url_arg(ctx.repo->owner);
|
||||
html("'>");
|
||||
html_txt(ctx.repo->owner);
|
||||
html("</a>");
|
||||
free(currenturl);
|
||||
}
|
||||
html("</td><td>");
|
||||
}
|
||||
|
@ -354,26 +330,25 @@ void cgit_print_repolist(void)
|
|||
html("<td>");
|
||||
cgit_summary_link("summary", NULL, "button", NULL);
|
||||
cgit_log_link("log", NULL, "button", NULL, NULL, NULL,
|
||||
0, NULL, NULL, ctx.qry.showmsg, 0);
|
||||
0, NULL, NULL, ctx.qry.showmsg);
|
||||
cgit_tree_link("tree", NULL, "button", NULL, NULL, NULL);
|
||||
html("</td>");
|
||||
}
|
||||
html("</tr>\n");
|
||||
}
|
||||
html("</table>");
|
||||
if (hits > ctx.cfg.max_repo_count)
|
||||
if (!hits)
|
||||
cgit_print_error("No repositories found");
|
||||
else if (hits > ctx.cfg.max_repo_count)
|
||||
print_pager(hits, ctx.cfg.max_repo_count, ctx.qry.search, ctx.qry.sort);
|
||||
cgit_print_docend();
|
||||
}
|
||||
|
||||
void cgit_print_site_readme(void)
|
||||
void cgit_print_site_readme()
|
||||
{
|
||||
cgit_print_layout_start();
|
||||
if (!ctx.cfg.root_readme)
|
||||
goto done;
|
||||
return;
|
||||
cgit_open_filter(ctx.cfg.about_filter, ctx.cfg.root_readme);
|
||||
html_include(ctx.cfg.root_readme);
|
||||
cgit_close_filter(ctx.cfg.about_filter);
|
||||
done:
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef UI_REPOLIST_H
|
||||
#define UI_REPOLIST_H
|
||||
|
||||
extern void cgit_print_repolist(void);
|
||||
extern void cgit_print_site_readme(void);
|
||||
extern void cgit_print_repolist();
|
||||
extern void cgit_print_site_readme();
|
||||
|
||||
#endif /* UI_REPOLIST_H */
|
||||
|
|
551
ui-shared.c
551
ui-shared.c
|
@ -1,21 +1,19 @@
|
|||
/* ui-shared.c: common web output functions
|
||||
*
|
||||
* Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
*
|
||||
* Licensed under GNU General Public License v2
|
||||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-shared.h"
|
||||
#include "cmd.h"
|
||||
#include "html.h"
|
||||
#include "version.h"
|
||||
|
||||
static const char cgit_doctype[] =
|
||||
"<!DOCTYPE html>\n";
|
||||
const char cgit_doctype[] =
|
||||
"<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n"
|
||||
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n";
|
||||
|
||||
static char *http_date(time_t t)
|
||||
{
|
||||
|
@ -24,11 +22,10 @@ static char *http_date(time_t t)
|
|||
static char month[][4] =
|
||||
{"Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||||
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
|
||||
struct tm tm;
|
||||
gmtime_r(&t, &tm);
|
||||
return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm.tm_wday],
|
||||
tm.tm_mday, month[tm.tm_mon], 1900 + tm.tm_year,
|
||||
tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
struct tm *tm = gmtime(&t);
|
||||
return fmt("%s, %02d %s %04d %02d:%02d:%02d GMT", day[tm->tm_wday],
|
||||
tm->tm_mday, month[tm->tm_mon], 1900 + tm->tm_year,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
}
|
||||
|
||||
void cgit_print_error(const char *fmt, ...)
|
||||
|
@ -49,7 +46,7 @@ void cgit_vprint_error(const char *fmt, va_list ap)
|
|||
html("</div>\n");
|
||||
}
|
||||
|
||||
const char *cgit_httpscheme(void)
|
||||
const char *cgit_httpscheme()
|
||||
{
|
||||
if (ctx.env.https && !strcmp(ctx.env.https, "on"))
|
||||
return "https://";
|
||||
|
@ -57,63 +54,25 @@ const char *cgit_httpscheme(void)
|
|||
return "http://";
|
||||
}
|
||||
|
||||
char *cgit_hosturl(void)
|
||||
const char *cgit_hosturl()
|
||||
{
|
||||
if (ctx.env.http_host)
|
||||
return xstrdup(ctx.env.http_host);
|
||||
return ctx.env.http_host;
|
||||
if (!ctx.env.server_name)
|
||||
return NULL;
|
||||
if (!ctx.env.server_port || atoi(ctx.env.server_port) == 80)
|
||||
return xstrdup(ctx.env.server_name);
|
||||
return ctx.env.server_name;
|
||||
return fmtalloc("%s:%s", ctx.env.server_name, ctx.env.server_port);
|
||||
}
|
||||
|
||||
char *cgit_currenturl(void)
|
||||
const char *cgit_currenturl()
|
||||
{
|
||||
const char *root = cgit_rooturl();
|
||||
|
||||
if (!ctx.qry.url)
|
||||
return xstrdup(root);
|
||||
if (root[0] && root[strlen(root) - 1] == '/')
|
||||
return fmtalloc("%s%s", root, ctx.qry.url);
|
||||
return fmtalloc("%s/%s", root, ctx.qry.url);
|
||||
return cgit_rooturl();
|
||||
return ctx.qry.url;
|
||||
}
|
||||
|
||||
char *cgit_currentfullurl(void)
|
||||
{
|
||||
const char *root = cgit_rooturl();
|
||||
const char *orig_query = ctx.env.query_string ? ctx.env.query_string : "";
|
||||
size_t len = strlen(orig_query);
|
||||
char *query = xmalloc(len + 2), *start_url, *ret;
|
||||
|
||||
/* Remove all url=... parts from query string */
|
||||
memcpy(query + 1, orig_query, len + 1);
|
||||
query[0] = '?';
|
||||
start_url = query;
|
||||
while ((start_url = strstr(start_url, "url=")) != NULL) {
|
||||
if (start_url[-1] == '?' || start_url[-1] == '&') {
|
||||
const char *end_url = strchr(start_url, '&');
|
||||
if (end_url)
|
||||
memmove(start_url, end_url + 1, strlen(end_url));
|
||||
else
|
||||
start_url[0] = '\0';
|
||||
} else
|
||||
++start_url;
|
||||
}
|
||||
if (!query[1])
|
||||
query[0] = '\0';
|
||||
|
||||
if (!ctx.qry.url)
|
||||
ret = fmtalloc("%s%s", root, query);
|
||||
else if (root[0] && root[strlen(root) - 1] == '/')
|
||||
ret = fmtalloc("%s%s%s", root, ctx.qry.url, query);
|
||||
else
|
||||
ret = fmtalloc("%s/%s%s", root, ctx.qry.url, query);
|
||||
free(query);
|
||||
return ret;
|
||||
}
|
||||
|
||||
const char *cgit_rooturl(void)
|
||||
const char *cgit_rooturl()
|
||||
{
|
||||
if (ctx.cfg.virtual_root)
|
||||
return ctx.cfg.virtual_root;
|
||||
|
@ -121,9 +80,9 @@ const char *cgit_rooturl(void)
|
|||
return ctx.cfg.script_name;
|
||||
}
|
||||
|
||||
const char *cgit_loginurl(void)
|
||||
const char *cgit_loginurl()
|
||||
{
|
||||
static const char *login_url;
|
||||
static const char *login_url = 0;
|
||||
if (!login_url)
|
||||
login_url = fmtalloc("%s?p=login", cgit_rooturl());
|
||||
return login_url;
|
||||
|
@ -160,7 +119,7 @@ char *cgit_fileurl(const char *reponame, const char *pagename,
|
|||
char *cgit_pageurl(const char *reponame, const char *pagename,
|
||||
const char *query)
|
||||
{
|
||||
return cgit_fileurl(reponame, pagename, NULL, query);
|
||||
return cgit_fileurl(reponame, pagename, 0, query);
|
||||
}
|
||||
|
||||
const char *cgit_repobasename(const char *reponame)
|
||||
|
@ -169,49 +128,33 @@ const char *cgit_repobasename(const char *reponame)
|
|||
static char rvbuf[1024];
|
||||
int p;
|
||||
const char *rv;
|
||||
size_t len;
|
||||
|
||||
len = strlcpy(rvbuf, reponame, sizeof(rvbuf));
|
||||
if (len >= sizeof(rvbuf))
|
||||
strncpy(rvbuf, reponame, sizeof(rvbuf));
|
||||
if (rvbuf[sizeof(rvbuf)-1])
|
||||
die("cgit_repobasename: truncated repository name '%s'", reponame);
|
||||
p = len - 1;
|
||||
p = strlen(rvbuf)-1;
|
||||
/* strip trailing slashes */
|
||||
while (p && rvbuf[p] == '/')
|
||||
rvbuf[p--] = '\0';
|
||||
while (p && rvbuf[p] == '/') rvbuf[p--] = 0;
|
||||
/* strip trailing .git */
|
||||
if (p >= 3 && starts_with(&rvbuf[p-3], ".git")) {
|
||||
p -= 3;
|
||||
rvbuf[p--] = '\0';
|
||||
p -= 3; rvbuf[p--] = 0;
|
||||
}
|
||||
/* strip more trailing slashes if any */
|
||||
while (p && rvbuf[p] == '/')
|
||||
rvbuf[p--] = '\0';
|
||||
while ( p && rvbuf[p] == '/') rvbuf[p--] = 0;
|
||||
/* find last slash in the remaining string */
|
||||
rv = strrchr(rvbuf, '/');
|
||||
rv = strrchr(rvbuf,'/');
|
||||
if (rv)
|
||||
return ++rv;
|
||||
return rvbuf;
|
||||
}
|
||||
|
||||
const char *cgit_snapshot_prefix(const struct cgit_repo *repo)
|
||||
{
|
||||
if (repo->snapshot_prefix)
|
||||
return repo->snapshot_prefix;
|
||||
|
||||
return cgit_repobasename(repo->url);
|
||||
}
|
||||
|
||||
static void site_url(const char *page, const char *search, const char *sort, int ofs, int always_root)
|
||||
{
|
||||
char *delim = "?";
|
||||
|
||||
if (always_root || page)
|
||||
html_attr(cgit_rooturl());
|
||||
else {
|
||||
char *currenturl = cgit_currenturl();
|
||||
html_attr(currenturl);
|
||||
free(currenturl);
|
||||
}
|
||||
else
|
||||
html_attr(cgit_currenturl());
|
||||
|
||||
if (page) {
|
||||
htmlf("?p=%s", page);
|
||||
|
@ -304,7 +247,7 @@ static char *repolink(const char *title, const char *class, const char *page,
|
|||
}
|
||||
delim = "&";
|
||||
}
|
||||
if (head && ctx.repo->defbranch && strcmp(head, ctx.repo->defbranch)) {
|
||||
if (head && strcmp(head, ctx.repo->defbranch)) {
|
||||
html(delim);
|
||||
html("h=");
|
||||
html_url_arg(head);
|
||||
|
@ -354,16 +297,9 @@ void cgit_plain_link(const char *name, const char *title, const char *class,
|
|||
reporevlink("plain", name, title, class, head, rev, path);
|
||||
}
|
||||
|
||||
void cgit_blame_link(const char *name, const char *title, const char *class,
|
||||
const char *head, const char *rev, const char *path)
|
||||
{
|
||||
reporevlink("blame", name, title, class, head, rev, path);
|
||||
}
|
||||
|
||||
void cgit_log_link(const char *name, const char *title, const char *class,
|
||||
const char *head, const char *rev, const char *path,
|
||||
int ofs, const char *grep, const char *pattern, int showmsg,
|
||||
int follow)
|
||||
int ofs, const char *grep, const char *pattern, int showmsg)
|
||||
{
|
||||
char *delim;
|
||||
|
||||
|
@ -392,20 +328,22 @@ void cgit_log_link(const char *name, const char *title, const char *class,
|
|||
if (showmsg) {
|
||||
html(delim);
|
||||
html("showmsg=1");
|
||||
delim = "&";
|
||||
}
|
||||
if (follow) {
|
||||
html(delim);
|
||||
html("follow=1");
|
||||
}
|
||||
html("'>");
|
||||
html_txt(name);
|
||||
html("</a>");
|
||||
}
|
||||
|
||||
void cgit_commit_link(const char *name, const char *title, const char *class,
|
||||
void cgit_commit_link(char *name, const char *title, const char *class,
|
||||
const char *head, const char *rev, const char *path)
|
||||
{
|
||||
if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
|
||||
name[ctx.cfg.max_msg_len] = '\0';
|
||||
name[ctx.cfg.max_msg_len - 1] = '.';
|
||||
name[ctx.cfg.max_msg_len - 2] = '.';
|
||||
name[ctx.cfg.max_msg_len - 3] = '.';
|
||||
}
|
||||
|
||||
char *delim;
|
||||
|
||||
delim = repolink(title, class, "commit", head, path);
|
||||
|
@ -431,18 +369,10 @@ void cgit_commit_link(const char *name, const char *title, const char *class,
|
|||
html("ignorews=1");
|
||||
delim = "&";
|
||||
}
|
||||
if (ctx.qry.follow) {
|
||||
html(delim);
|
||||
html("follow=1");
|
||||
}
|
||||
html("'>");
|
||||
if (name[0] != '\0') {
|
||||
if (strlen(name) > ctx.cfg.max_msg_len && ctx.cfg.max_msg_len >= 15) {
|
||||
html_ntxt(name, ctx.cfg.max_msg_len - 3);
|
||||
html("...");
|
||||
} else
|
||||
html_txt(name);
|
||||
} else
|
||||
if (name[0] != '\0')
|
||||
html_txt(name);
|
||||
else
|
||||
html_txt("(no commit message)");
|
||||
html("</a>");
|
||||
}
|
||||
|
@ -495,10 +425,6 @@ void cgit_diff_link(const char *name, const char *title, const char *class,
|
|||
html("ignorews=1");
|
||||
delim = "&";
|
||||
}
|
||||
if (ctx.qry.follow) {
|
||||
html(delim);
|
||||
html("follow=1");
|
||||
}
|
||||
html("'>");
|
||||
html_txt(name);
|
||||
html("</a>");
|
||||
|
@ -524,45 +450,41 @@ static void cgit_self_link(char *name, const char *title, const char *class)
|
|||
else if (!strcmp(ctx.qry.page, "summary"))
|
||||
cgit_summary_link(name, title, class, ctx.qry.head);
|
||||
else if (!strcmp(ctx.qry.page, "tag"))
|
||||
cgit_tag_link(name, title, class, ctx.qry.has_oid ?
|
||||
ctx.qry.oid : ctx.qry.head);
|
||||
cgit_tag_link(name, title, class, ctx.qry.has_sha1 ?
|
||||
ctx.qry.sha1 : ctx.qry.head);
|
||||
else if (!strcmp(ctx.qry.page, "tree"))
|
||||
cgit_tree_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "plain"))
|
||||
cgit_plain_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "blame"))
|
||||
cgit_blame_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "log"))
|
||||
cgit_log_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
|
||||
ctx.qry.path, ctx.qry.ofs,
|
||||
ctx.qry.grep, ctx.qry.search,
|
||||
ctx.qry.showmsg, ctx.qry.follow);
|
||||
ctx.qry.showmsg);
|
||||
else if (!strcmp(ctx.qry.page, "commit"))
|
||||
cgit_commit_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "patch"))
|
||||
cgit_patch_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "refs"))
|
||||
cgit_refs_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "snapshot"))
|
||||
cgit_snapshot_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.has_oid ? ctx.qry.oid : NULL,
|
||||
ctx.qry.has_sha1 ? ctx.qry.sha1 : NULL,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "diff"))
|
||||
cgit_diff_link(name, title, class, ctx.qry.head,
|
||||
ctx.qry.oid, ctx.qry.oid2,
|
||||
ctx.qry.sha1, ctx.qry.sha2,
|
||||
ctx.qry.path);
|
||||
else if (!strcmp(ctx.qry.page, "stats"))
|
||||
cgit_stats_link(name, title, class, ctx.qry.head,
|
||||
|
@ -582,7 +504,7 @@ void cgit_object_link(struct object *obj)
|
|||
{
|
||||
char *page, *shortrev, *fullrev, *name;
|
||||
|
||||
fullrev = oid_to_hex(&obj->oid);
|
||||
fullrev = sha1_to_hex(obj->sha1);
|
||||
shortrev = xstrdup(fullrev);
|
||||
shortrev[10] = '\0';
|
||||
if (obj->type == OBJ_COMMIT) {
|
||||
|
@ -595,7 +517,7 @@ void cgit_object_link(struct object *obj)
|
|||
page = "tag";
|
||||
else
|
||||
page = "blob";
|
||||
name = fmt("%s %s...", type_name(obj->type), shortrev);
|
||||
name = fmt("%s %s...", typename(obj->type), shortrev);
|
||||
reporevlink(page, name, NULL, NULL, ctx.qry.head, fullrev, NULL);
|
||||
}
|
||||
|
||||
|
@ -633,54 +555,63 @@ void cgit_submodule_link(const char *class, char *path, const char *rev)
|
|||
item = lookup_path(list, path);
|
||||
}
|
||||
}
|
||||
if (item || ctx.repo->module_link) {
|
||||
html("<a ");
|
||||
if (class)
|
||||
htmlf("class='%s' ", class);
|
||||
html("href='");
|
||||
if (item) {
|
||||
html_attrf(item->util, rev);
|
||||
} else {
|
||||
dir = strrchr(path, '/');
|
||||
if (dir)
|
||||
dir++;
|
||||
else
|
||||
dir = path;
|
||||
html_attrf(ctx.repo->module_link, dir, rev);
|
||||
}
|
||||
html("'>");
|
||||
html_txt(path);
|
||||
html("</a>");
|
||||
html("<a ");
|
||||
if (class)
|
||||
htmlf("class='%s' ", class);
|
||||
html("href='");
|
||||
if (item) {
|
||||
html_attrf(item->util, rev);
|
||||
} else if (ctx.repo->module_link) {
|
||||
dir = strrchr(path, '/');
|
||||
if (dir)
|
||||
dir++;
|
||||
else
|
||||
dir = path;
|
||||
html_attrf(ctx.repo->module_link, dir, rev);
|
||||
} else {
|
||||
html("<span");
|
||||
if (class)
|
||||
htmlf(" class='%s'", class);
|
||||
html(">");
|
||||
html_txt(path);
|
||||
html("</span>");
|
||||
html("#");
|
||||
}
|
||||
html("'>");
|
||||
html_txt(path);
|
||||
html("</a>");
|
||||
html_txtf(" @ %.7s", rev);
|
||||
if (item && tail)
|
||||
path[len - 1] = tail;
|
||||
}
|
||||
|
||||
const struct date_mode cgit_date_mode(enum date_mode_type type)
|
||||
void cgit_print_date(time_t secs, const char *format, int local_time)
|
||||
{
|
||||
static struct date_mode mode;
|
||||
mode.type = type;
|
||||
mode.local = ctx.cfg.local_time;
|
||||
return mode;
|
||||
char buf[64];
|
||||
struct tm *time;
|
||||
|
||||
if (!secs)
|
||||
return;
|
||||
if (local_time)
|
||||
time = localtime(&secs);
|
||||
else
|
||||
time = gmtime(&secs);
|
||||
strftime(buf, sizeof(buf)-1, format, time);
|
||||
html_txt(buf);
|
||||
}
|
||||
|
||||
static void print_rel_date(time_t t, int tz, double value,
|
||||
static void print_rel_date(time_t t, double value,
|
||||
const char *class, const char *suffix)
|
||||
{
|
||||
htmlf("<span class='%s' data-ut='%" PRIu64 "' title='", class, (uint64_t)t);
|
||||
html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601)));
|
||||
char buf[64];
|
||||
struct tm *time;
|
||||
|
||||
if (ctx.cfg.local_time)
|
||||
time = localtime(&t);
|
||||
else
|
||||
time = gmtime(&t);
|
||||
strftime(buf, sizeof(buf) - 1, FMT_LONGDATE, time);
|
||||
|
||||
htmlf("<span class='%s' title='", class);
|
||||
html_attr(buf);
|
||||
htmlf("'>%.0f %s</span>", value, suffix);
|
||||
}
|
||||
|
||||
void cgit_print_age(time_t t, int tz, time_t max_relative)
|
||||
void cgit_print_age(time_t t, time_t max_relative, const char *format)
|
||||
{
|
||||
time_t now, secs;
|
||||
|
||||
|
@ -692,35 +623,31 @@ void cgit_print_age(time_t t, int tz, time_t max_relative)
|
|||
secs = 0;
|
||||
|
||||
if (secs > max_relative && max_relative >= 0) {
|
||||
html("<span title='");
|
||||
html_attr(show_date(t, tz, cgit_date_mode(DATE_ISO8601)));
|
||||
html("'>");
|
||||
html_txt(show_date(t, tz, cgit_date_mode(DATE_SHORT)));
|
||||
html("</span>");
|
||||
cgit_print_date(t, format, ctx.cfg.local_time);
|
||||
return;
|
||||
}
|
||||
|
||||
if (secs < TM_HOUR * 2) {
|
||||
print_rel_date(t, tz, secs * 1.0 / TM_MIN, "age-mins", "min.");
|
||||
print_rel_date(t, secs * 1.0 / TM_MIN, "age-mins", "min.");
|
||||
return;
|
||||
}
|
||||
if (secs < TM_DAY * 2) {
|
||||
print_rel_date(t, tz, secs * 1.0 / TM_HOUR, "age-hours", "hours");
|
||||
print_rel_date(t, secs * 1.0 / TM_HOUR, "age-hours", "hours");
|
||||
return;
|
||||
}
|
||||
if (secs < TM_WEEK * 2) {
|
||||
print_rel_date(t, tz, secs * 1.0 / TM_DAY, "age-days", "days");
|
||||
print_rel_date(t, secs * 1.0 / TM_DAY, "age-days", "days");
|
||||
return;
|
||||
}
|
||||
if (secs < TM_MONTH * 2) {
|
||||
print_rel_date(t, tz, secs * 1.0 / TM_WEEK, "age-weeks", "weeks");
|
||||
print_rel_date(t, secs * 1.0 / TM_WEEK, "age-weeks", "weeks");
|
||||
return;
|
||||
}
|
||||
if (secs < TM_YEAR * 2) {
|
||||
print_rel_date(t, tz, secs * 1.0 / TM_MONTH, "age-months", "months");
|
||||
print_rel_date(t, secs * 1.0 / TM_MONTH, "age-months", "months");
|
||||
return;
|
||||
}
|
||||
print_rel_date(t, tz, secs * 1.0 / TM_YEAR, "age-years", "years");
|
||||
print_rel_date(t, secs * 1.0 / TM_YEAR, "age-years", "years");
|
||||
}
|
||||
|
||||
void cgit_print_http_headers(void)
|
||||
|
@ -737,11 +664,9 @@ void cgit_print_http_headers(void)
|
|||
htmlf("Content-Type: %s\n", ctx.page.mimetype);
|
||||
if (ctx.page.size)
|
||||
htmlf("Content-Length: %zd\n", ctx.page.size);
|
||||
if (ctx.page.filename) {
|
||||
html("Content-Disposition: inline; filename=\"");
|
||||
html_header_arg_in_quotes(ctx.page.filename);
|
||||
html("\"\n");
|
||||
}
|
||||
if (ctx.page.filename)
|
||||
htmlf("Content-Disposition: inline; filename=\"%s\"\n",
|
||||
ctx.page.filename);
|
||||
if (!ctx.env.authenticated)
|
||||
html("Cache-Control: no-cache, no-store\n");
|
||||
htmlf("Last-Modified: %s\n", http_date(ctx.page.modified));
|
||||
|
@ -753,14 +678,6 @@ void cgit_print_http_headers(void)
|
|||
exit(0);
|
||||
}
|
||||
|
||||
void cgit_redirect(const char *url, bool permanent)
|
||||
{
|
||||
htmlf("Status: %d %s\n", permanent ? 301 : 302, permanent ? "Moved" : "Found");
|
||||
html("Location: ");
|
||||
html_url_path(url);
|
||||
html("\n\n");
|
||||
}
|
||||
|
||||
static void print_rel_vcs_link(const char *url)
|
||||
{
|
||||
html("<link rel='vcs-git' href='");
|
||||
|
@ -770,50 +687,17 @@ static void print_rel_vcs_link(const char *url)
|
|||
html(" Git repository'/>\n");
|
||||
}
|
||||
|
||||
static int emit_css_link(struct string_list_item *s, void *arg)
|
||||
{
|
||||
/* Do not emit anything if css= is specified. */
|
||||
if (s && *s->string == '\0')
|
||||
return 0;
|
||||
|
||||
html("<link rel='stylesheet' type='text/css' href='");
|
||||
if (s)
|
||||
html_attr(s->string);
|
||||
else
|
||||
html_attr((const char *)arg);
|
||||
html("'/>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int emit_js_link(struct string_list_item *s, void *arg)
|
||||
{
|
||||
/* Do not emit anything if js= is specified. */
|
||||
if (s && *s->string == '\0')
|
||||
return 0;
|
||||
|
||||
html("<script type='text/javascript' src='");
|
||||
if (s)
|
||||
html_attr(s->string);
|
||||
else
|
||||
html_attr((const char *)arg);
|
||||
html("'></script>\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void cgit_print_docstart(void)
|
||||
{
|
||||
char *host = cgit_hosturl();
|
||||
|
||||
if (ctx.cfg.embedded) {
|
||||
if (ctx.cfg.header)
|
||||
html_include(ctx.cfg.header);
|
||||
return;
|
||||
}
|
||||
|
||||
const char *host = cgit_hosturl();
|
||||
html(cgit_doctype);
|
||||
html("<html lang='en'>\n");
|
||||
html("<html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'>\n");
|
||||
html("<head>\n");
|
||||
html("<title>");
|
||||
html_txt(ctx.page.title);
|
||||
|
@ -821,51 +705,37 @@ void cgit_print_docstart(void)
|
|||
htmlf("<meta name='generator' content='cgit %s'/>\n", cgit_version);
|
||||
if (ctx.cfg.robots && *ctx.cfg.robots)
|
||||
htmlf("<meta name='robots' content='%s'/>\n", ctx.cfg.robots);
|
||||
|
||||
if (ctx.cfg.css.items)
|
||||
for_each_string_list(&ctx.cfg.css, emit_css_link, NULL);
|
||||
else
|
||||
emit_css_link(NULL, "/cgit.css");
|
||||
|
||||
if (ctx.cfg.js.items)
|
||||
for_each_string_list(&ctx.cfg.js, emit_js_link, NULL);
|
||||
else
|
||||
emit_js_link(NULL, "/cgit.js");
|
||||
|
||||
html("<link rel='stylesheet' type='text/css' href='");
|
||||
html_attr(ctx.cfg.css);
|
||||
html("'/>\n");
|
||||
if (ctx.cfg.favicon) {
|
||||
html("<link rel='shortcut icon' href='");
|
||||
html_attr(ctx.cfg.favicon);
|
||||
html("'/>\n");
|
||||
}
|
||||
if (host && ctx.repo && ctx.qry.head) {
|
||||
char *fileurl;
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
strbuf_addf(&sb, "h=%s", ctx.qry.head);
|
||||
|
||||
html("<link rel='alternate' title='Atom feed' href='");
|
||||
html(cgit_httpscheme());
|
||||
html_attr(host);
|
||||
fileurl = cgit_fileurl(ctx.repo->url, "atom", ctx.qry.vpath,
|
||||
sb.buf);
|
||||
html_attr(fileurl);
|
||||
html_attr(cgit_hosturl());
|
||||
html_attr(cgit_fileurl(ctx.repo->url, "atom", ctx.qry.vpath,
|
||||
sb.buf));
|
||||
html("' type='application/atom+xml'/>\n");
|
||||
strbuf_release(&sb);
|
||||
free(fileurl);
|
||||
}
|
||||
if (ctx.repo)
|
||||
cgit_add_clone_urls(print_rel_vcs_link);
|
||||
if (ctx.cfg.head_include)
|
||||
html_include(ctx.cfg.head_include);
|
||||
if (ctx.repo && ctx.repo->extra_head_content)
|
||||
html(ctx.repo->extra_head_content);
|
||||
html("</head>\n");
|
||||
html("<body>\n");
|
||||
if (ctx.cfg.header)
|
||||
html_include(ctx.cfg.header);
|
||||
free(host);
|
||||
}
|
||||
|
||||
void cgit_print_docend(void)
|
||||
void cgit_print_docend()
|
||||
{
|
||||
html("</div> <!-- class=content -->\n");
|
||||
if (ctx.cfg.embedded) {
|
||||
|
@ -877,40 +747,15 @@ void cgit_print_docend(void)
|
|||
if (ctx.cfg.footer)
|
||||
html_include(ctx.cfg.footer);
|
||||
else {
|
||||
htmlf("<div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit %s</a> "
|
||||
"(<a href='https://git-scm.com/'>git %s</a>) at ", cgit_version, git_version_string);
|
||||
html_txt(show_date(time(NULL), 0, cgit_date_mode(DATE_ISO8601)));
|
||||
htmlf("<div class='footer'>generated by <a href='http://git.zx2c4.com/cgit/about/'>cgit %s</a> at ",
|
||||
cgit_version);
|
||||
cgit_print_date(time(NULL), FMT_LONGDATE, ctx.cfg.local_time);
|
||||
html("</div>\n");
|
||||
}
|
||||
html("</div> <!-- id=cgit -->\n");
|
||||
html("</body>\n</html>\n");
|
||||
}
|
||||
|
||||
void cgit_print_error_page(int code, const char *msg, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
ctx.page.expires = ctx.cfg.cache_dynamic_ttl;
|
||||
ctx.page.status = code;
|
||||
ctx.page.statusmsg = msg;
|
||||
cgit_print_layout_start();
|
||||
va_start(ap, fmt);
|
||||
cgit_vprint_error(fmt, ap);
|
||||
va_end(ap);
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
||||
void cgit_print_layout_start(void)
|
||||
{
|
||||
cgit_print_http_headers();
|
||||
cgit_print_docstart();
|
||||
cgit_print_pageheader();
|
||||
}
|
||||
|
||||
void cgit_print_layout_end(void)
|
||||
{
|
||||
cgit_print_docend();
|
||||
}
|
||||
|
||||
static void add_clone_urls(void (*fn)(const char *), char *txt, char *suffix)
|
||||
{
|
||||
struct strbuf **url_list = strbuf_split_str(txt, ' ', 0);
|
||||
|
@ -936,7 +781,7 @@ void cgit_add_clone_urls(void (*fn)(const char *))
|
|||
add_clone_urls(fn, ctx.cfg.clone_prefix, ctx.repo->url);
|
||||
}
|
||||
|
||||
static int print_branch_option(const char *refname, const struct object_id *oid,
|
||||
static int print_branch_option(const char *refname, const unsigned char *sha1,
|
||||
int flags, void *cb_data)
|
||||
{
|
||||
char *name = (char *)refname;
|
||||
|
@ -961,10 +806,10 @@ void cgit_add_hidden_formfields(int incl_head, int incl_search,
|
|||
strcmp(ctx.qry.head, ctx.repo->defbranch))
|
||||
html_hidden("h", ctx.qry.head);
|
||||
|
||||
if (ctx.qry.oid)
|
||||
html_hidden("id", ctx.qry.oid);
|
||||
if (ctx.qry.oid2)
|
||||
html_hidden("id2", ctx.qry.oid2);
|
||||
if (ctx.qry.sha1)
|
||||
html_hidden("id", ctx.qry.sha1);
|
||||
if (ctx.qry.sha2)
|
||||
html_hidden("id2", ctx.qry.sha2);
|
||||
if (ctx.qry.showmsg)
|
||||
html_hidden("showmsg", "1");
|
||||
|
||||
|
@ -978,9 +823,6 @@ void cgit_add_hidden_formfields(int incl_head, int incl_search,
|
|||
|
||||
static const char *hc(const char *page)
|
||||
{
|
||||
if (!ctx.qry.page)
|
||||
return NULL;
|
||||
|
||||
return strcmp(ctx.qry.page, page) ? NULL : "active";
|
||||
}
|
||||
|
||||
|
@ -988,13 +830,12 @@ static void cgit_print_path_crumbs(char *path)
|
|||
{
|
||||
char *old_path = ctx.qry.path;
|
||||
char *p = path, *q, *end = path + strlen(path);
|
||||
int levels = 0;
|
||||
|
||||
ctx.qry.path = NULL;
|
||||
cgit_self_link("root", NULL, NULL);
|
||||
ctx.qry.path = p = path;
|
||||
while (p < end) {
|
||||
if (!(q = strchr(p, '/')) || levels > 15)
|
||||
if (!(q = strchr(p, '/')))
|
||||
q = end;
|
||||
*q = '\0';
|
||||
html_txt("/");
|
||||
|
@ -1002,7 +843,6 @@ static void cgit_print_path_crumbs(char *path)
|
|||
if (q < end)
|
||||
*q = '/';
|
||||
p = q + 1;
|
||||
++levels;
|
||||
}
|
||||
ctx.qry.path = old_path;
|
||||
}
|
||||
|
@ -1037,19 +877,15 @@ static void print_header(void)
|
|||
if (ctx.repo) {
|
||||
cgit_index_link("index", NULL, NULL, NULL, NULL, 0, 1);
|
||||
html(" : ");
|
||||
cgit_summary_link(ctx.repo->name, NULL, NULL, NULL);
|
||||
cgit_summary_link(ctx.repo->name, ctx.repo->name, NULL, NULL);
|
||||
if (ctx.env.authenticated) {
|
||||
html("</td><td class='form'>");
|
||||
html("<form method='get'>\n");
|
||||
html("<form method='get' action=''>\n");
|
||||
cgit_add_hidden_formfields(0, 1, ctx.qry.page);
|
||||
html("<select name='h' onchange='this.form.submit();'>\n");
|
||||
refs_for_each_branch_ref(get_main_ref_store(the_repository),
|
||||
print_branch_option, ctx.qry.head);
|
||||
if (ctx.repo->enable_remote_branches)
|
||||
refs_for_each_remote_ref(get_main_ref_store(the_repository),
|
||||
print_branch_option, ctx.qry.head);
|
||||
for_each_branch_ref(print_branch_option, ctx.qry.head);
|
||||
html("</select> ");
|
||||
html("<input type='submit' value='switch'/>");
|
||||
html("<input type='submit' name='' value='switch'/>");
|
||||
html("</form>");
|
||||
}
|
||||
} else
|
||||
|
@ -1060,16 +896,12 @@ static void print_header(void)
|
|||
if (ctx.repo) {
|
||||
html_txt(ctx.repo->desc);
|
||||
html("</td><td class='sub right'>");
|
||||
if (ctx.repo->owner_filter) {
|
||||
cgit_open_filter(ctx.repo->owner_filter);
|
||||
html_txt(ctx.repo->owner);
|
||||
cgit_close_filter(ctx.repo->owner_filter);
|
||||
} else {
|
||||
html_txt(ctx.repo->owner);
|
||||
}
|
||||
html_txt(ctx.repo->owner);
|
||||
} else {
|
||||
if (ctx.cfg.root_desc)
|
||||
html_txt(ctx.cfg.root_desc);
|
||||
else if (ctx.cfg.index_info)
|
||||
html_include(ctx.cfg.index_info);
|
||||
}
|
||||
html("</td></tr></table>\n");
|
||||
}
|
||||
|
@ -1089,36 +921,24 @@ void cgit_print_pageheader(void)
|
|||
cgit_summary_link("summary", NULL, hc("summary"),
|
||||
ctx.qry.head);
|
||||
cgit_refs_link("refs", NULL, hc("refs"), ctx.qry.head,
|
||||
ctx.qry.oid, NULL);
|
||||
ctx.qry.sha1, NULL);
|
||||
cgit_log_link("log", NULL, hc("log"), ctx.qry.head,
|
||||
NULL, ctx.qry.vpath, 0, NULL, NULL,
|
||||
ctx.qry.showmsg, ctx.qry.follow);
|
||||
if (ctx.qry.page && !strcmp(ctx.qry.page, "blame"))
|
||||
cgit_blame_link("blame", NULL, hc("blame"), ctx.qry.head,
|
||||
ctx.qry.oid, ctx.qry.vpath);
|
||||
else
|
||||
cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
|
||||
ctx.qry.oid, ctx.qry.vpath);
|
||||
ctx.qry.showmsg);
|
||||
cgit_tree_link("tree", NULL, hc("tree"), ctx.qry.head,
|
||||
ctx.qry.sha1, ctx.qry.vpath);
|
||||
cgit_commit_link("commit", NULL, hc("commit"),
|
||||
ctx.qry.head, ctx.qry.oid, ctx.qry.vpath);
|
||||
ctx.qry.head, ctx.qry.sha1, ctx.qry.vpath);
|
||||
cgit_diff_link("diff", NULL, hc("diff"), ctx.qry.head,
|
||||
ctx.qry.oid, ctx.qry.oid2, ctx.qry.vpath);
|
||||
ctx.qry.sha1, ctx.qry.sha2, ctx.qry.vpath);
|
||||
if (ctx.repo->max_stats)
|
||||
cgit_stats_link("stats", NULL, hc("stats"),
|
||||
ctx.qry.head, ctx.qry.vpath);
|
||||
if (ctx.repo->homepage) {
|
||||
html("<a href='");
|
||||
html_attr(ctx.repo->homepage);
|
||||
html("'>homepage</a>");
|
||||
}
|
||||
html("</td><td class='form'>");
|
||||
html("<form class='right' method='get' action='");
|
||||
if (ctx.cfg.virtual_root) {
|
||||
char *fileurl = cgit_fileurl(ctx.qry.repo, "log",
|
||||
ctx.qry.vpath, NULL);
|
||||
html_url_path(fileurl);
|
||||
free(fileurl);
|
||||
}
|
||||
if (ctx.cfg.virtual_root)
|
||||
html_url_path(cgit_fileurl(ctx.qry.repo, "log",
|
||||
ctx.qry.vpath, NULL));
|
||||
html("'>\n");
|
||||
cgit_add_hidden_formfields(1, 0, "log");
|
||||
html("<select name='qt'>\n");
|
||||
|
@ -1127,41 +947,31 @@ void cgit_print_pageheader(void)
|
|||
html_option("committer", "committer", ctx.qry.grep);
|
||||
html_option("range", "range", ctx.qry.grep);
|
||||
html("</select>\n");
|
||||
html("<input class='txt' type='search' size='10' name='q' value='");
|
||||
html("<input class='txt' type='text' size='10' name='q' value='");
|
||||
html_attr(ctx.qry.search);
|
||||
html("'/>\n");
|
||||
html("<input type='submit' value='search'/>\n");
|
||||
html("</form>\n");
|
||||
} else if (ctx.env.authenticated) {
|
||||
char *currenturl = cgit_currenturl();
|
||||
site_link(NULL, "index", NULL, hc("repolist"), NULL, NULL, 0, 1);
|
||||
if (ctx.cfg.root_readme)
|
||||
site_link("about", "about", NULL, hc("about"),
|
||||
NULL, NULL, 0, 1);
|
||||
html("</td><td class='form'>");
|
||||
html("<form method='get' action='");
|
||||
html_attr(currenturl);
|
||||
html_attr(cgit_currenturl());
|
||||
html("'>\n");
|
||||
html("<input type='search' name='q' size='10' value='");
|
||||
html("<input type='text' name='q' size='10' value='");
|
||||
html_attr(ctx.qry.search);
|
||||
html("'/>\n");
|
||||
html("<input type='submit' value='search'/>\n");
|
||||
html("</form>");
|
||||
free(currenturl);
|
||||
}
|
||||
html("</td></tr></table>\n");
|
||||
if (ctx.env.authenticated && ctx.repo && ctx.qry.vpath) {
|
||||
if (ctx.env.authenticated && ctx.qry.vpath) {
|
||||
html("<div class='path'>");
|
||||
html("path: ");
|
||||
cgit_print_path_crumbs(ctx.qry.vpath);
|
||||
if (ctx.cfg.enable_follow_links && !strcmp(ctx.qry.page, "log")) {
|
||||
html(" (");
|
||||
ctx.qry.follow = !ctx.qry.follow;
|
||||
cgit_self_link(ctx.qry.follow ? "follow" : "unfollow",
|
||||
NULL, NULL);
|
||||
ctx.qry.follow = !ctx.qry.follow;
|
||||
html(")");
|
||||
}
|
||||
html("</div>");
|
||||
}
|
||||
html("<div class='content'>");
|
||||
|
@ -1182,80 +992,27 @@ void cgit_print_filemode(unsigned short mode)
|
|||
html_fileperm(mode);
|
||||
}
|
||||
|
||||
void cgit_compose_snapshot_prefix(struct strbuf *filename, const char *base,
|
||||
const char *ref)
|
||||
void cgit_print_snapshot_links(const char *repo, const char *head,
|
||||
const char *hex, int snapshots)
|
||||
{
|
||||
struct object_id oid;
|
||||
|
||||
/*
|
||||
* Prettify snapshot names by stripping leading "v" or "V" if the tag
|
||||
* name starts with {v,V}[0-9] and the prettify mapping is injective,
|
||||
* i.e. each stripped tag can be inverted without ambiguities.
|
||||
*/
|
||||
if (repo_get_oid(the_repository, fmt("refs/tags/%s", ref), &oid) == 0 &&
|
||||
(ref[0] == 'v' || ref[0] == 'V') && isdigit(ref[1]) &&
|
||||
((repo_get_oid(the_repository, fmt("refs/tags/%s", ref + 1), &oid) == 0) +
|
||||
(repo_get_oid(the_repository, fmt("refs/tags/v%s", ref + 1), &oid) == 0) +
|
||||
(repo_get_oid(the_repository, fmt("refs/tags/V%s", ref + 1), &oid) == 0) == 1))
|
||||
ref++;
|
||||
|
||||
strbuf_addf(filename, "%s-%s", base, ref);
|
||||
}
|
||||
|
||||
void cgit_print_snapshot_links(const struct cgit_repo *repo, const char *ref,
|
||||
const char *separator)
|
||||
{
|
||||
const struct cgit_snapshot_format *f;
|
||||
const struct cgit_snapshot_format* f;
|
||||
struct strbuf filename = STRBUF_INIT;
|
||||
const char *basename;
|
||||
size_t prefixlen;
|
||||
unsigned char sha1[20];
|
||||
|
||||
basename = cgit_snapshot_prefix(repo);
|
||||
if (starts_with(ref, basename))
|
||||
strbuf_addstr(&filename, ref);
|
||||
else
|
||||
cgit_compose_snapshot_prefix(&filename, basename, ref);
|
||||
|
||||
if (get_sha1(fmt("refs/tags/%s", hex), sha1) == 0 &&
|
||||
(hex[0] == 'v' || hex[0] == 'V') && isdigit(hex[1]))
|
||||
hex++;
|
||||
strbuf_addf(&filename, "%s-%s", cgit_repobasename(repo), hex);
|
||||
prefixlen = filename.len;
|
||||
for (f = cgit_snapshot_formats; f->suffix; f++) {
|
||||
if (!(repo->snapshots & cgit_snapshot_format_bit(f)))
|
||||
if (!(snapshots & f->bit))
|
||||
continue;
|
||||
strbuf_setlen(&filename, prefixlen);
|
||||
strbuf_addstr(&filename, f->suffix);
|
||||
cgit_snapshot_link(filename.buf, NULL, NULL, NULL, NULL,
|
||||
filename.buf);
|
||||
if (cgit_snapshot_get_sig(ref, f)) {
|
||||
strbuf_addstr(&filename, ".asc");
|
||||
html(" (");
|
||||
cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
|
||||
filename.buf);
|
||||
html(")");
|
||||
} else if (starts_with(f->suffix, ".tar") && cgit_snapshot_get_sig(ref, &cgit_snapshot_formats[0])) {
|
||||
strbuf_setlen(&filename, strlen(filename.buf) - strlen(f->suffix));
|
||||
strbuf_addstr(&filename, ".tar.asc");
|
||||
html(" (");
|
||||
cgit_snapshot_link("sig", NULL, NULL, NULL, NULL,
|
||||
filename.buf);
|
||||
html(")");
|
||||
}
|
||||
html(separator);
|
||||
html("<br/>");
|
||||
}
|
||||
strbuf_release(&filename);
|
||||
}
|
||||
|
||||
void cgit_set_title_from_path(const char *path)
|
||||
{
|
||||
struct strbuf sb = STRBUF_INIT;
|
||||
const char *slash, *last_slash;
|
||||
|
||||
if (!path)
|
||||
return;
|
||||
|
||||
for (last_slash = path + strlen(path); (slash = memrchr(path, '/', last_slash - path)) != NULL; last_slash = slash) {
|
||||
strbuf_add(&sb, slash + 1, last_slash - slash - 1);
|
||||
strbuf_addstr(&sb, " \xc2\xab ");
|
||||
}
|
||||
strbuf_add(&sb, path, last_slash - path);
|
||||
strbuf_addf(&sb, " - %s", ctx.page.title);
|
||||
ctx.page.title = strbuf_detach(&sb, NULL);
|
||||
}
|
||||
|
|
39
ui-shared.h
39
ui-shared.h
|
@ -1,12 +1,11 @@
|
|||
#ifndef UI_SHARED_H
|
||||
#define UI_SHARED_H
|
||||
|
||||
extern const char *cgit_httpscheme(void);
|
||||
extern char *cgit_hosturl(void);
|
||||
extern const char *cgit_rooturl(void);
|
||||
extern char *cgit_currenturl(void);
|
||||
extern char *cgit_currentfullurl(void);
|
||||
extern const char *cgit_loginurl(void);
|
||||
extern const char *cgit_httpscheme();
|
||||
extern const char *cgit_hosturl();
|
||||
extern const char *cgit_rooturl();
|
||||
extern const char *cgit_currenturl();
|
||||
extern const char *cgit_loginurl();
|
||||
extern char *cgit_repourl(const char *reponame);
|
||||
extern char *cgit_fileurl(const char *reponame, const char *pagename,
|
||||
const char *filename, const char *query);
|
||||
|
@ -27,14 +26,11 @@ extern void cgit_tree_link(const char *name, const char *title,
|
|||
extern void cgit_plain_link(const char *name, const char *title,
|
||||
const char *class, const char *head,
|
||||
const char *rev, const char *path);
|
||||
extern void cgit_blame_link(const char *name, const char *title,
|
||||
const char *class, const char *head,
|
||||
const char *rev, const char *path);
|
||||
extern void cgit_log_link(const char *name, const char *title,
|
||||
const char *class, const char *head, const char *rev,
|
||||
const char *path, int ofs, const char *grep,
|
||||
const char *pattern, int showmsg, int follow);
|
||||
extern void cgit_commit_link(const char *name, const char *title,
|
||||
const char *pattern, int showmsg);
|
||||
extern void cgit_commit_link(char *name, const char *title,
|
||||
const char *class, const char *head,
|
||||
const char *rev, const char *path);
|
||||
extern void cgit_patch_link(const char *name, const char *title,
|
||||
|
@ -58,30 +54,19 @@ extern void cgit_object_link(struct object *obj);
|
|||
extern void cgit_submodule_link(const char *class, char *path,
|
||||
const char *rev);
|
||||
|
||||
extern void cgit_print_layout_start(void);
|
||||
extern void cgit_print_layout_end(void);
|
||||
|
||||
__attribute__((format (printf,1,2)))
|
||||
extern void cgit_print_error(const char *fmt, ...);
|
||||
__attribute__((format (printf,1,0)))
|
||||
extern void cgit_vprint_error(const char *fmt, va_list ap);
|
||||
extern const struct date_mode cgit_date_mode(enum date_mode_type type);
|
||||
extern void cgit_print_age(time_t t, int tz, time_t max_relative);
|
||||
extern void cgit_print_date(time_t secs, const char *format, int local_time);
|
||||
extern void cgit_print_age(time_t t, time_t max_relative, const char *format);
|
||||
extern void cgit_print_http_headers(void);
|
||||
extern void cgit_redirect(const char *url, bool permanent);
|
||||
extern void cgit_print_docstart(void);
|
||||
extern void cgit_print_docend(void);
|
||||
__attribute__((format (printf,3,4)))
|
||||
extern void cgit_print_error_page(int code, const char *msg, const char *fmt, ...);
|
||||
extern void cgit_print_docend();
|
||||
extern void cgit_print_pageheader(void);
|
||||
extern void cgit_print_filemode(unsigned short mode);
|
||||
extern void cgit_compose_snapshot_prefix(struct strbuf *filename,
|
||||
const char *base, const char *ref);
|
||||
extern void cgit_print_snapshot_links(const struct cgit_repo *repo,
|
||||
const char *ref, const char *separator);
|
||||
extern const char *cgit_snapshot_prefix(const struct cgit_repo *repo);
|
||||
extern void cgit_print_snapshot_links(const char *repo, const char *head,
|
||||
const char *hex, int snapshots);
|
||||
extern void cgit_add_hidden_formfields(int incl_head, int incl_search,
|
||||
const char *page);
|
||||
|
||||
extern void cgit_set_title_from_path(const char *path);
|
||||
#endif /* UI_SHARED_H */
|
||||
|
|
191
ui-snapshot.c
191
ui-snapshot.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-snapshot.h"
|
||||
#include "html.h"
|
||||
|
@ -15,32 +13,32 @@
|
|||
|
||||
static int write_archive_type(const char *format, const char *hex, const char *prefix)
|
||||
{
|
||||
struct strvec argv = STRVEC_INIT;
|
||||
struct argv_array argv = ARGV_ARRAY_INIT;
|
||||
const char **nargv;
|
||||
int result;
|
||||
strvec_push(&argv, "snapshot");
|
||||
strvec_push(&argv, format);
|
||||
argv_array_push(&argv, "snapshot");
|
||||
argv_array_push(&argv, format);
|
||||
if (prefix) {
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
strbuf_addstr(&buf, prefix);
|
||||
strbuf_addch(&buf, '/');
|
||||
strvec_push(&argv, "--prefix");
|
||||
strvec_push(&argv, buf.buf);
|
||||
argv_array_push(&argv, "--prefix");
|
||||
argv_array_push(&argv, buf.buf);
|
||||
strbuf_release(&buf);
|
||||
}
|
||||
strvec_push(&argv, hex);
|
||||
argv_array_push(&argv, hex);
|
||||
/*
|
||||
* Now we need to copy the pointers to arguments into a new
|
||||
* structure because write_archive will rearrange its arguments
|
||||
* which may result in duplicated/missing entries causing leaks
|
||||
* or double-frees in strvec_clear.
|
||||
* or double-frees in argv_array_clear.
|
||||
*/
|
||||
nargv = xmalloc(sizeof(char *) * (argv.nr + 1));
|
||||
/* strvec guarantees a trailing NULL entry. */
|
||||
memcpy(nargv, argv.v, sizeof(char *) * (argv.nr + 1));
|
||||
nargv = xmalloc(sizeof(char *) * (argv.argc + 1));
|
||||
/* argv_array guarantees a trailing NULL entry. */
|
||||
memcpy(nargv, argv.argv, sizeof(char *) * (argv.argc + 1));
|
||||
|
||||
result = write_archive(argv.nr, nargv, NULL, the_repository, NULL, 0);
|
||||
strvec_clear(&argv);
|
||||
result = write_archive(argv.argc, nargv, NULL, 1, NULL, 0);
|
||||
argv_array_clear(&argv);
|
||||
free(nargv);
|
||||
return result;
|
||||
}
|
||||
|
@ -81,61 +79,21 @@ static int write_tar_bzip2_archive(const char *hex, const char *prefix)
|
|||
return write_compressed_tar_archive(hex, prefix, argv);
|
||||
}
|
||||
|
||||
static int write_tar_lzip_archive(const char *hex, const char *prefix)
|
||||
{
|
||||
char *argv[] = { "lzip", NULL };
|
||||
return write_compressed_tar_archive(hex, prefix, argv);
|
||||
}
|
||||
|
||||
static int write_tar_xz_archive(const char *hex, const char *prefix)
|
||||
{
|
||||
char *argv[] = { "xz", NULL };
|
||||
return write_compressed_tar_archive(hex, prefix, argv);
|
||||
}
|
||||
|
||||
static int write_tar_zstd_archive(const char *hex, const char *prefix)
|
||||
{
|
||||
char *argv[] = { "zstd", "-T0", NULL };
|
||||
return write_compressed_tar_archive(hex, prefix, argv);
|
||||
}
|
||||
|
||||
const struct cgit_snapshot_format cgit_snapshot_formats[] = {
|
||||
/* .tar must remain the 0 index */
|
||||
{ ".tar", "application/x-tar", write_tar_archive },
|
||||
{ ".tar.gz", "application/x-gzip", write_tar_gzip_archive },
|
||||
{ ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive },
|
||||
{ ".tar.lz", "application/x-lzip", write_tar_lzip_archive },
|
||||
{ ".tar.xz", "application/x-xz", write_tar_xz_archive },
|
||||
{ ".tar.zst", "application/x-zstd", write_tar_zstd_archive },
|
||||
{ ".zip", "application/x-zip", write_zip_archive },
|
||||
{ ".zip", "application/x-zip", write_zip_archive, 0x01 },
|
||||
{ ".tar.gz", "application/x-gzip", write_tar_gzip_archive, 0x02 },
|
||||
{ ".tar.bz2", "application/x-bzip2", write_tar_bzip2_archive, 0x04 },
|
||||
{ ".tar", "application/x-tar", write_tar_archive, 0x08 },
|
||||
{ ".tar.xz", "application/x-xz", write_tar_xz_archive, 0x10 },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
static struct notes_tree snapshot_sig_notes[ARRAY_SIZE(cgit_snapshot_formats)];
|
||||
|
||||
const struct object_id *cgit_snapshot_get_sig(const char *ref,
|
||||
const struct cgit_snapshot_format *f)
|
||||
{
|
||||
struct notes_tree *tree;
|
||||
struct object_id oid;
|
||||
|
||||
if (repo_get_oid(the_repository, ref, &oid))
|
||||
return NULL;
|
||||
|
||||
tree = &snapshot_sig_notes[f - &cgit_snapshot_formats[0]];
|
||||
if (!tree->initialized) {
|
||||
struct strbuf notes_ref = STRBUF_INIT;
|
||||
|
||||
strbuf_addf(¬es_ref, "refs/notes/signatures/%s",
|
||||
f->suffix + 1);
|
||||
|
||||
init_notes(tree, notes_ref.buf, combine_notes_ignore, 0);
|
||||
strbuf_release(¬es_ref);
|
||||
}
|
||||
|
||||
return get_note(tree, &oid);
|
||||
}
|
||||
|
||||
static const struct cgit_snapshot_format *get_format(const char *filename)
|
||||
{
|
||||
const struct cgit_snapshot_format *fmt;
|
||||
|
@ -147,69 +105,28 @@ static const struct cgit_snapshot_format *get_format(const char *filename)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
const unsigned cgit_snapshot_format_bit(const struct cgit_snapshot_format *f)
|
||||
{
|
||||
return BIT(f - &cgit_snapshot_formats[0]);
|
||||
}
|
||||
|
||||
static int make_snapshot(const struct cgit_snapshot_format *format,
|
||||
const char *hex, const char *prefix,
|
||||
const char *filename)
|
||||
{
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
|
||||
if (repo_get_oid(the_repository, hex, &oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad object id: %s", hex);
|
||||
if (get_sha1(hex, sha1)) {
|
||||
cgit_print_error("Bad object id: %s", hex);
|
||||
return 1;
|
||||
}
|
||||
if (!lookup_commit_reference(the_repository, &oid)) {
|
||||
cgit_print_error_page(400, "Bad request",
|
||||
"Not a commit reference: %s", hex);
|
||||
if (!lookup_commit_reference(sha1)) {
|
||||
cgit_print_error("Not a commit reference: %s", hex);
|
||||
return 1;
|
||||
}
|
||||
ctx.page.etag = oid_to_hex(&oid);
|
||||
ctx.page.etag = sha1_to_hex(sha1);
|
||||
ctx.page.mimetype = xstrdup(format->mimetype);
|
||||
ctx.page.filename = xstrdup(filename);
|
||||
cgit_print_http_headers();
|
||||
init_archivers();
|
||||
format->write_func(hex, prefix);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int write_sig(const struct cgit_snapshot_format *format,
|
||||
const char *hex, const char *archive,
|
||||
const char *filename)
|
||||
{
|
||||
const struct object_id *note = cgit_snapshot_get_sig(hex, format);
|
||||
enum object_type type;
|
||||
unsigned long size;
|
||||
char *buf;
|
||||
|
||||
if (!note) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"No signature for %s", archive);
|
||||
return 0;
|
||||
}
|
||||
|
||||
buf = repo_read_object_file(the_repository, note, &type, &size);
|
||||
if (!buf) {
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
return 0;
|
||||
}
|
||||
|
||||
html("X-Content-Type-Options: nosniff\n");
|
||||
html("Content-Security-Policy: default-src 'none'\n");
|
||||
ctx.page.etag = oid_to_hex(note);
|
||||
ctx.page.mimetype = xstrdup("application/pgp-signature");
|
||||
ctx.page.filename = xstrdup(filename);
|
||||
cgit_print_http_headers();
|
||||
|
||||
html_raw(buf, size);
|
||||
free(buf);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Try to guess the requested revision from the requested snapshot name.
|
||||
* First the format extension is stripped, e.g. "cgit-0.7.2.tar.gz" become
|
||||
* "cgit-0.7.2". If this is a valid commit object name we've got a winner.
|
||||
|
@ -220,22 +137,21 @@ static int write_sig(const struct cgit_snapshot_format *format,
|
|||
* pending a 'v' or a 'V' to the remaining snapshot name ("0.7.2" ->
|
||||
* "v0.7.2") gives us something valid.
|
||||
*/
|
||||
static const char *get_ref_from_filename(const struct cgit_repo *repo,
|
||||
const char *filename,
|
||||
static const char *get_ref_from_filename(const char *url, const char *filename,
|
||||
const struct cgit_snapshot_format *format)
|
||||
{
|
||||
const char *reponame;
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
struct strbuf snapshot = STRBUF_INIT;
|
||||
int result = 1;
|
||||
|
||||
strbuf_addstr(&snapshot, filename);
|
||||
strbuf_setlen(&snapshot, snapshot.len - strlen(format->suffix));
|
||||
|
||||
if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
|
||||
if (get_sha1(snapshot.buf, sha1) == 0)
|
||||
goto out;
|
||||
|
||||
reponame = cgit_snapshot_prefix(repo);
|
||||
reponame = cgit_repobasename(url);
|
||||
if (starts_with(snapshot.buf, reponame)) {
|
||||
const char *new_start = snapshot.buf;
|
||||
new_start += strlen(reponame);
|
||||
|
@ -244,15 +160,15 @@ static const char *get_ref_from_filename(const struct cgit_repo *repo,
|
|||
strbuf_splice(&snapshot, 0, new_start - snapshot.buf, "", 0);
|
||||
}
|
||||
|
||||
if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
|
||||
if (get_sha1(snapshot.buf, sha1) == 0)
|
||||
goto out;
|
||||
|
||||
strbuf_insert(&snapshot, 0, "v", 1);
|
||||
if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
|
||||
if (get_sha1(snapshot.buf, sha1) == 0)
|
||||
goto out;
|
||||
|
||||
strbuf_splice(&snapshot, 0, 1, "V", 1);
|
||||
if (repo_get_oid(the_repository, snapshot.buf, &oid) == 0)
|
||||
if (get_sha1(snapshot.buf, sha1) == 0)
|
||||
goto out;
|
||||
|
||||
result = 0;
|
||||
|
@ -262,40 +178,42 @@ out:
|
|||
return result ? strbuf_detach(&snapshot, NULL) : NULL;
|
||||
}
|
||||
|
||||
__attribute__((format (printf, 1, 2)))
|
||||
static void show_error(char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
|
||||
ctx.page.mimetype = "text/html";
|
||||
cgit_print_http_headers();
|
||||
cgit_print_docstart();
|
||||
cgit_print_pageheader();
|
||||
va_start(ap, fmt);
|
||||
cgit_vprint_error(fmt, ap);
|
||||
va_end(ap);
|
||||
cgit_print_docend();
|
||||
}
|
||||
|
||||
void cgit_print_snapshot(const char *head, const char *hex,
|
||||
const char *filename, int dwim)
|
||||
{
|
||||
const struct cgit_snapshot_format* f;
|
||||
const char *sig_filename = NULL;
|
||||
char *adj_filename = NULL;
|
||||
char *prefix = NULL;
|
||||
|
||||
if (!filename) {
|
||||
cgit_print_error_page(400, "Bad request",
|
||||
"No snapshot name specified");
|
||||
show_error("No snapshot name specified");
|
||||
return;
|
||||
}
|
||||
|
||||
if (ends_with(filename, ".asc")) {
|
||||
sig_filename = filename;
|
||||
|
||||
/* Strip ".asc" from filename for common format processing */
|
||||
adj_filename = xstrdup(filename);
|
||||
adj_filename[strlen(adj_filename) - 4] = '\0';
|
||||
filename = adj_filename;
|
||||
}
|
||||
|
||||
f = get_format(filename);
|
||||
if (!f || (!sig_filename && !(ctx.repo->snapshots & cgit_snapshot_format_bit(f)))) {
|
||||
cgit_print_error_page(400, "Bad request",
|
||||
"Unsupported snapshot format: %s", filename);
|
||||
if (!f) {
|
||||
show_error("Unsupported snapshot format: %s", filename);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!hex && dwim) {
|
||||
hex = get_ref_from_filename(ctx.repo, filename, f);
|
||||
hex = get_ref_from_filename(ctx.repo->url, filename, f);
|
||||
if (hex == NULL) {
|
||||
cgit_print_error_page(404, "Not found", "Not found");
|
||||
html_status(404, "Not found", 0);
|
||||
return;
|
||||
}
|
||||
prefix = xstrdup(filename);
|
||||
|
@ -306,13 +224,8 @@ void cgit_print_snapshot(const char *head, const char *hex,
|
|||
hex = head;
|
||||
|
||||
if (!prefix)
|
||||
prefix = xstrdup(cgit_snapshot_prefix(ctx.repo));
|
||||
|
||||
if (sig_filename)
|
||||
write_sig(f, hex, filename, sig_filename);
|
||||
else
|
||||
make_snapshot(f, hex, prefix, filename);
|
||||
prefix = xstrdup(cgit_repobasename(ctx.repo->url));
|
||||
|
||||
make_snapshot(f, hex, prefix, filename);
|
||||
free(prefix);
|
||||
free(adj_filename);
|
||||
}
|
||||
|
|
67
ui-ssdiff.c
67
ui-ssdiff.c
|
@ -18,7 +18,7 @@ struct deferred_lines {
|
|||
static struct deferred_lines *deferred_old, *deferred_old_last;
|
||||
static struct deferred_lines *deferred_new, *deferred_new_last;
|
||||
|
||||
static void create_or_reset_lcs_table(void)
|
||||
static void create_or_reset_lcs_table()
|
||||
{
|
||||
int i;
|
||||
|
||||
|
@ -92,7 +92,7 @@ static char *longest_common_subsequence(char *A, char *B)
|
|||
static int line_from_hunk(char *line, char type)
|
||||
{
|
||||
char *buf1, *buf2;
|
||||
int len, res;
|
||||
int len;
|
||||
|
||||
buf1 = strchr(line, type);
|
||||
if (buf1 == NULL)
|
||||
|
@ -103,8 +103,9 @@ static int line_from_hunk(char *line, char type)
|
|||
return 0;
|
||||
len = buf2 - buf1;
|
||||
buf2 = xmalloc(len + 1);
|
||||
strlcpy(buf2, buf1, len + 1);
|
||||
res = atoi(buf2);
|
||||
strncpy(buf2, buf1, len);
|
||||
buf2[len] = '\0';
|
||||
int res = atoi(buf2);
|
||||
free(buf2);
|
||||
return res;
|
||||
}
|
||||
|
@ -113,11 +114,11 @@ static char *replace_tabs(char *line)
|
|||
{
|
||||
char *prev_buf = line;
|
||||
char *cur_buf;
|
||||
size_t linelen = strlen(line);
|
||||
int linelen = strlen(line);
|
||||
int n_tabs = 0;
|
||||
int i;
|
||||
char *result;
|
||||
size_t result_len;
|
||||
char *spaces = " ";
|
||||
|
||||
if (linelen == 0) {
|
||||
result = xmalloc(1);
|
||||
|
@ -125,26 +126,20 @@ static char *replace_tabs(char *line)
|
|||
return result;
|
||||
}
|
||||
|
||||
for (i = 0; i < linelen; i++) {
|
||||
for (i = 0; i < linelen; i++)
|
||||
if (line[i] == '\t')
|
||||
n_tabs += 1;
|
||||
}
|
||||
result_len = linelen + n_tabs * 8;
|
||||
result = xmalloc(result_len + 1);
|
||||
result = xmalloc(linelen + n_tabs * 8 + 1);
|
||||
result[0] = '\0';
|
||||
|
||||
for (;;) {
|
||||
while (1) {
|
||||
cur_buf = strchr(prev_buf, '\t');
|
||||
if (!cur_buf) {
|
||||
linelen = strlen(result);
|
||||
strlcpy(&result[linelen], prev_buf, result_len - linelen + 1);
|
||||
strcat(result, prev_buf);
|
||||
break;
|
||||
} else {
|
||||
linelen = strlen(result);
|
||||
strlcpy(&result[linelen], prev_buf, cur_buf - prev_buf + 1);
|
||||
linelen = strlen(result);
|
||||
memset(&result[linelen], ' ', 8 - (linelen % 8));
|
||||
result[linelen + 8 - (linelen % 8)] = '\0';
|
||||
strncat(result, prev_buf, cur_buf - prev_buf);
|
||||
strncat(result, spaces, 8 - (strlen(result) % 8));
|
||||
}
|
||||
prev_buf = cur_buf + 1;
|
||||
}
|
||||
|
@ -209,13 +204,11 @@ static void print_part_with_lcs(char *class, char *line, char *lcs)
|
|||
}
|
||||
} else if (line[i] == lcs[j]) {
|
||||
same = 1;
|
||||
html("</span>");
|
||||
htmlf("</span>");
|
||||
j += 1;
|
||||
}
|
||||
html_txt(c);
|
||||
}
|
||||
if (!same)
|
||||
html("</span>");
|
||||
}
|
||||
|
||||
static void print_ssdiff_line(char *class,
|
||||
|
@ -236,14 +229,12 @@ static void print_ssdiff_line(char *class,
|
|||
if (old_line_no > 0) {
|
||||
struct diff_filespec *old_file = cgit_get_current_old_file();
|
||||
char *lineno_str = fmt("n%d", old_line_no);
|
||||
char *id_str = fmt("id=%s#%s", is_null_oid(&old_file->oid)?"HEAD":oid_to_hex(old_rev_oid), lineno_str);
|
||||
char *fileurl = cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str);
|
||||
char *id_str = fmt("id=%s#%s", is_null_sha1(old_file->sha1)?"HEAD":sha1_to_hex(old_rev_sha1), lineno_str);
|
||||
html("<td class='lineno'><a href='");
|
||||
html(fileurl);
|
||||
htmlf("'>%s</a>", lineno_str + 1);
|
||||
html(cgit_fileurl(ctx.repo->url, "tree", old_file->path, id_str));
|
||||
htmlf("' id='%s'>%s</a>", lineno_str, lineno_str + 1);
|
||||
html("</td>");
|
||||
htmlf("<td class='%s'>", class);
|
||||
free(fileurl);
|
||||
} else if (old_line)
|
||||
htmlf("<td class='lineno'></td><td class='%s'>", class);
|
||||
else
|
||||
|
@ -259,14 +250,12 @@ static void print_ssdiff_line(char *class,
|
|||
if (new_line_no > 0) {
|
||||
struct diff_filespec *new_file = cgit_get_current_new_file();
|
||||
char *lineno_str = fmt("n%d", new_line_no);
|
||||
char *id_str = fmt("id=%s#%s", is_null_oid(&new_file->oid)?"HEAD":oid_to_hex(new_rev_oid), lineno_str);
|
||||
char *fileurl = cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str);
|
||||
char *id_str = fmt("id=%s#%s", is_null_sha1(new_file->sha1)?"HEAD":sha1_to_hex(new_rev_sha1), lineno_str);
|
||||
html("<td class='lineno'><a href='");
|
||||
html(fileurl);
|
||||
htmlf("'>%s</a>", lineno_str + 1);
|
||||
html(cgit_fileurl(ctx.repo->url, "tree", new_file->path, id_str));
|
||||
htmlf("' id='%s'>%s</a>", lineno_str, lineno_str + 1);
|
||||
html("</td>");
|
||||
htmlf("<td class='%s'>", class);
|
||||
free(fileurl);
|
||||
} else if (new_line)
|
||||
htmlf("<td class='lineno'></td><td class='%s'>", class);
|
||||
else
|
||||
|
@ -287,7 +276,7 @@ static void print_ssdiff_line(char *class,
|
|||
free(old_line);
|
||||
}
|
||||
|
||||
static void print_deferred_old_lines(void)
|
||||
static void print_deferred_old_lines()
|
||||
{
|
||||
struct deferred_lines *iter_old, *tmp;
|
||||
iter_old = deferred_old;
|
||||
|
@ -300,7 +289,7 @@ static void print_deferred_old_lines(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void print_deferred_new_lines(void)
|
||||
static void print_deferred_new_lines()
|
||||
{
|
||||
struct deferred_lines *iter_new, *tmp;
|
||||
iter_new = deferred_new;
|
||||
|
@ -313,7 +302,7 @@ static void print_deferred_new_lines(void)
|
|||
}
|
||||
}
|
||||
|
||||
static void print_deferred_changed_lines(void)
|
||||
static void print_deferred_changed_lines()
|
||||
{
|
||||
struct deferred_lines *iter_old, *iter_new, *tmp;
|
||||
int n_old_lines = calc_deferred_lines(deferred_old);
|
||||
|
@ -348,7 +337,7 @@ static void print_deferred_changed_lines(void)
|
|||
}
|
||||
}
|
||||
|
||||
void cgit_ssdiff_print_deferred_lines(void)
|
||||
void cgit_ssdiff_print_deferred_lines()
|
||||
{
|
||||
if (!deferred_old && !deferred_new)
|
||||
return;
|
||||
|
@ -399,7 +388,7 @@ void cgit_ssdiff_line_cb(char *line, int len)
|
|||
line[len - 1] = c;
|
||||
}
|
||||
|
||||
void cgit_ssdiff_header_begin(void)
|
||||
void cgit_ssdiff_header_begin()
|
||||
{
|
||||
current_old_line = -1;
|
||||
current_new_line = -1;
|
||||
|
@ -407,12 +396,12 @@ void cgit_ssdiff_header_begin(void)
|
|||
html("<tr><td class='head' colspan='4'>");
|
||||
}
|
||||
|
||||
void cgit_ssdiff_header_end(void)
|
||||
void cgit_ssdiff_header_end()
|
||||
{
|
||||
html("</td></tr>");
|
||||
html("</td><tr>");
|
||||
}
|
||||
|
||||
void cgit_ssdiff_footer(void)
|
||||
void cgit_ssdiff_footer()
|
||||
{
|
||||
if (deferred_old || deferred_new)
|
||||
cgit_ssdiff_print_deferred_lines();
|
||||
|
|
|
@ -13,13 +13,13 @@
|
|||
#endif
|
||||
#define MAX_SSDIFF_SIZE ((MAX_SSDIFF_M) * (MAX_SSDIFF_N))
|
||||
|
||||
extern void cgit_ssdiff_print_deferred_lines(void);
|
||||
extern void cgit_ssdiff_print_deferred_lines();
|
||||
|
||||
extern void cgit_ssdiff_line_cb(char *line, int len);
|
||||
|
||||
extern void cgit_ssdiff_header_begin(void);
|
||||
extern void cgit_ssdiff_header_end(void);
|
||||
extern void cgit_ssdiff_header_begin();
|
||||
extern void cgit_ssdiff_header_end();
|
||||
|
||||
extern void cgit_ssdiff_footer(void);
|
||||
extern void cgit_ssdiff_footer();
|
||||
|
||||
#endif /* UI_SSDIFF_H */
|
||||
|
|
106
ui-stats.c
106
ui-stats.c
|
@ -1,18 +1,14 @@
|
|||
/* ui-stats.c: generate stats view
|
||||
*
|
||||
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
*
|
||||
* Licensed under GNU General Public License v2
|
||||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-stats.h"
|
||||
#include "html.h"
|
||||
#include "ui-shared.h"
|
||||
|
||||
#ifdef NO_C99_FORMAT
|
||||
#define SZ_FMT "%u"
|
||||
#else
|
||||
#define SZ_FMT "%zu"
|
||||
#endif
|
||||
|
||||
struct authorstat {
|
||||
long total;
|
||||
struct string_list list;
|
||||
|
@ -129,7 +125,7 @@ static char *pretty_year(struct tm *tm)
|
|||
return fmt("%d", tm->tm_year + 1900);
|
||||
}
|
||||
|
||||
static const struct cgit_period periods[] = {
|
||||
struct cgit_period periods[] = {
|
||||
{'w', "week", 12, 4, trunc_week, dec_week, inc_week, pretty_week},
|
||||
{'m', "month", 12, 4, trunc_month, dec_month, inc_month, pretty_month},
|
||||
{'q', "quarter", 12, 4, trunc_quarter, dec_quarter, inc_quarter, pretty_quarter},
|
||||
|
@ -140,7 +136,7 @@ static const struct cgit_period periods[] = {
|
|||
* and update the period pointer to the correcsponding struct.
|
||||
* If no matching code is found, return 0.
|
||||
*/
|
||||
int cgit_find_stats_period(const char *expr, const struct cgit_period **period)
|
||||
int cgit_find_stats_period(const char *expr, struct cgit_period **period)
|
||||
{
|
||||
int i;
|
||||
char code = '\0';
|
||||
|
@ -169,16 +165,15 @@ const char *cgit_find_stats_periodname(int idx)
|
|||
}
|
||||
|
||||
static void add_commit(struct string_list *authors, struct commit *commit,
|
||||
const struct cgit_period *period)
|
||||
struct cgit_period *period)
|
||||
{
|
||||
struct commitinfo *info;
|
||||
struct string_list_item *author, *item;
|
||||
struct authorstat *authorstat;
|
||||
struct string_list *items;
|
||||
char *tmp;
|
||||
struct tm date;
|
||||
struct tm *date;
|
||||
time_t t;
|
||||
uintptr_t *counter;
|
||||
|
||||
info = cgit_parse_commit(commit);
|
||||
tmp = xstrdup(info->author);
|
||||
|
@ -190,15 +185,13 @@ static void add_commit(struct string_list *authors, struct commit *commit,
|
|||
authorstat = author->util;
|
||||
items = &authorstat->list;
|
||||
t = info->committer_date;
|
||||
gmtime_r(&t, &date);
|
||||
period->trunc(&date);
|
||||
tmp = xstrdup(period->pretty(&date));
|
||||
date = gmtime(&t);
|
||||
period->trunc(date);
|
||||
tmp = xstrdup(period->pretty(date));
|
||||
item = string_list_insert(items, tmp);
|
||||
counter = (uintptr_t *)&item->util;
|
||||
if (*counter)
|
||||
if (item->util)
|
||||
free(tmp);
|
||||
(*counter)++;
|
||||
|
||||
item->util++;
|
||||
authorstat->total++;
|
||||
cgit_free_commitinfo(info);
|
||||
}
|
||||
|
@ -216,7 +209,7 @@ static int cmp_total_commits(const void *a1, const void *a2)
|
|||
/* Walk the commit DAG and collect number of commits per author per
|
||||
* timeperiod into a nested string_list collection.
|
||||
*/
|
||||
static struct string_list collect_stats(const struct cgit_period *period)
|
||||
static struct string_list collect_stats(struct cgit_period *period)
|
||||
{
|
||||
struct string_list authors;
|
||||
struct rev_info rev;
|
||||
|
@ -225,22 +218,22 @@ static struct string_list collect_stats(const struct cgit_period *period)
|
|||
int argc = 3;
|
||||
time_t now;
|
||||
long i;
|
||||
struct tm tm;
|
||||
struct tm *tm;
|
||||
char tmp[11];
|
||||
|
||||
time(&now);
|
||||
gmtime_r(&now, &tm);
|
||||
period->trunc(&tm);
|
||||
tm = gmtime(&now);
|
||||
period->trunc(tm);
|
||||
for (i = 1; i < period->count; i++)
|
||||
period->dec(&tm);
|
||||
strftime(tmp, sizeof(tmp), "%Y-%m-%d", &tm);
|
||||
period->dec(tm);
|
||||
strftime(tmp, sizeof(tmp), "%Y-%m-%d", tm);
|
||||
argv[2] = xstrdup(fmt("--since=%s", tmp));
|
||||
if (ctx.qry.path) {
|
||||
argv[3] = "--";
|
||||
argv[4] = ctx.qry.path;
|
||||
argc += 2;
|
||||
}
|
||||
repo_init_revisions(the_repository, &rev, NULL);
|
||||
init_revisions(&rev, NULL);
|
||||
rev.abbrev = DEFAULT_ABBREV;
|
||||
rev.commit_format = CMIT_FMT_DEFAULT;
|
||||
rev.max_parents = 1;
|
||||
|
@ -251,7 +244,8 @@ static struct string_list collect_stats(const struct cgit_period *period)
|
|||
memset(&authors, 0, sizeof(authors));
|
||||
while ((commit = get_revision(&rev)) != NULL) {
|
||||
add_commit(&authors, commit, period);
|
||||
release_commit_memory(the_repository->parsed_objects, commit);
|
||||
free_commit_buffer(commit);
|
||||
free_commit_list(commit->parents);
|
||||
commit->parents = NULL;
|
||||
}
|
||||
return authors;
|
||||
|
@ -262,7 +256,7 @@ static void print_combined_authorrow(struct string_list *authors, int from,
|
|||
const char *leftclass,
|
||||
const char *centerclass,
|
||||
const char *rightclass,
|
||||
const struct cgit_period *period)
|
||||
struct cgit_period *period)
|
||||
{
|
||||
struct string_list_item *author;
|
||||
struct authorstat *authorstat;
|
||||
|
@ -270,21 +264,21 @@ static void print_combined_authorrow(struct string_list *authors, int from,
|
|||
struct string_list_item *date;
|
||||
time_t now;
|
||||
long i, j, total, subtotal;
|
||||
struct tm tm;
|
||||
struct tm *tm;
|
||||
char *tmp;
|
||||
|
||||
time(&now);
|
||||
gmtime_r(&now, &tm);
|
||||
period->trunc(&tm);
|
||||
tm = gmtime(&now);
|
||||
period->trunc(tm);
|
||||
for (i = 1; i < period->count; i++)
|
||||
period->dec(&tm);
|
||||
period->dec(tm);
|
||||
|
||||
total = 0;
|
||||
htmlf("<tr><td class='%s'>%s</td>", leftclass,
|
||||
fmt(name, to - from + 1));
|
||||
for (j = 0; j < period->count; j++) {
|
||||
tmp = period->pretty(&tm);
|
||||
period->inc(&tm);
|
||||
tmp = period->pretty(tm);
|
||||
period->inc(tm);
|
||||
subtotal = 0;
|
||||
for (i = from; i <= to; i++) {
|
||||
author = &authors->items[i];
|
||||
|
@ -292,7 +286,7 @@ static void print_combined_authorrow(struct string_list *authors, int from,
|
|||
items = &authorstat->list;
|
||||
date = string_list_lookup(items, tmp);
|
||||
if (date)
|
||||
subtotal += (uintptr_t)date->util;
|
||||
subtotal += (size_t)date->util;
|
||||
}
|
||||
htmlf("<td class='%s'>%ld</td>", centerclass, subtotal);
|
||||
total += subtotal;
|
||||
|
@ -301,7 +295,7 @@ static void print_combined_authorrow(struct string_list *authors, int from,
|
|||
}
|
||||
|
||||
static void print_authors(struct string_list *authors, int top,
|
||||
const struct cgit_period *period)
|
||||
struct cgit_period *period)
|
||||
{
|
||||
struct string_list_item *author;
|
||||
struct authorstat *authorstat;
|
||||
|
@ -309,20 +303,20 @@ static void print_authors(struct string_list *authors, int top,
|
|||
struct string_list_item *date;
|
||||
time_t now;
|
||||
long i, j, total;
|
||||
struct tm tm;
|
||||
struct tm *tm;
|
||||
char *tmp;
|
||||
|
||||
time(&now);
|
||||
gmtime_r(&now, &tm);
|
||||
period->trunc(&tm);
|
||||
tm = gmtime(&now);
|
||||
period->trunc(tm);
|
||||
for (i = 1; i < period->count; i++)
|
||||
period->dec(&tm);
|
||||
period->dec(tm);
|
||||
|
||||
html("<table class='stats'><tr><th>Author</th>");
|
||||
for (j = 0; j < period->count; j++) {
|
||||
tmp = period->pretty(&tm);
|
||||
tmp = period->pretty(tm);
|
||||
htmlf("<th>%s</th>", tmp);
|
||||
period->inc(&tm);
|
||||
period->inc(tm);
|
||||
}
|
||||
html("<th>Total</th></tr>\n");
|
||||
|
||||
|
@ -338,16 +332,16 @@ static void print_authors(struct string_list *authors, int top,
|
|||
items = &authorstat->list;
|
||||
total = 0;
|
||||
for (j = 0; j < period->count; j++)
|
||||
period->dec(&tm);
|
||||
period->dec(tm);
|
||||
for (j = 0; j < period->count; j++) {
|
||||
tmp = period->pretty(&tm);
|
||||
period->inc(&tm);
|
||||
tmp = period->pretty(tm);
|
||||
period->inc(tm);
|
||||
date = string_list_lookup(items, tmp);
|
||||
if (!date)
|
||||
html("<td>0</td>");
|
||||
else {
|
||||
htmlf("<td>%lu</td>", (uintptr_t)date->util);
|
||||
total += (uintptr_t)date->util;
|
||||
htmlf("<td>"SZ_FMT"</td>", (size_t)date->util);
|
||||
total += (size_t)date->util;
|
||||
}
|
||||
}
|
||||
htmlf("<td class='sum'>%ld</td></tr>", total);
|
||||
|
@ -369,7 +363,7 @@ static void print_authors(struct string_list *authors, int top,
|
|||
void cgit_show_stats(void)
|
||||
{
|
||||
struct string_list authors;
|
||||
const struct cgit_period *period;
|
||||
struct cgit_period *period;
|
||||
int top, i;
|
||||
const char *code = "w";
|
||||
|
||||
|
@ -378,13 +372,11 @@ void cgit_show_stats(void)
|
|||
|
||||
i = cgit_find_stats_period(code, &period);
|
||||
if (!i) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Unknown statistics type: %c", code[0]);
|
||||
cgit_print_error("Unknown statistics type: %c", code[0]);
|
||||
return;
|
||||
}
|
||||
if (i > ctx.repo->max_stats) {
|
||||
cgit_print_error_page(400, "Bad request",
|
||||
"Statistics type disabled: %s", period->name);
|
||||
cgit_print_error("Statistics type disabled: %s", period->name);
|
||||
return;
|
||||
}
|
||||
authors = collect_stats(period);
|
||||
|
@ -395,10 +387,9 @@ void cgit_show_stats(void)
|
|||
if (!top)
|
||||
top = 10;
|
||||
|
||||
cgit_print_layout_start();
|
||||
html("<div class='cgit-panel'>");
|
||||
html("<b>stat options</b>");
|
||||
html("<form method='get'>");
|
||||
html("<form method='get' action=''>");
|
||||
cgit_add_hidden_formfields(1, 0, "stats");
|
||||
html("<table><tr><td colspan='2'/></tr>");
|
||||
if (ctx.repo->max_stats > 1) {
|
||||
|
@ -430,6 +421,5 @@ void cgit_show_stats(void)
|
|||
}
|
||||
html("</h2>");
|
||||
print_authors(&authors, top, period);
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
||||
|
|
|
@ -20,7 +20,7 @@ struct cgit_period {
|
|||
char *(*pretty)(struct tm *tm);
|
||||
};
|
||||
|
||||
extern int cgit_find_stats_period(const char *expr, const struct cgit_period **period);
|
||||
extern int cgit_find_stats_period(const char *expr, struct cgit_period **period);
|
||||
extern const char *cgit_find_stats_periodname(int idx);
|
||||
|
||||
extern void cgit_show_stats(void);
|
||||
|
|
30
ui-summary.c
30
ui-summary.c
|
@ -9,11 +9,11 @@
|
|||
#include "cgit.h"
|
||||
#include "ui-summary.h"
|
||||
#include "html.h"
|
||||
#include "ui-blob.h"
|
||||
#include "ui-log.h"
|
||||
#include "ui-plain.h"
|
||||
#include "ui-refs.h"
|
||||
#include "ui-blob.h"
|
||||
#include "ui-shared.h"
|
||||
#include <libgen.h>
|
||||
|
||||
static int urls;
|
||||
|
||||
|
@ -40,7 +40,7 @@ static void print_url(const char *url)
|
|||
html("</a></td></tr>\n");
|
||||
}
|
||||
|
||||
void cgit_print_summary(void)
|
||||
void cgit_print_summary()
|
||||
{
|
||||
int columns = 3;
|
||||
|
||||
|
@ -49,7 +49,6 @@ void cgit_print_summary(void)
|
|||
if (ctx.repo->enable_log_linecount)
|
||||
columns++;
|
||||
|
||||
cgit_print_layout_start();
|
||||
html("<table summary='repository info' class='list nowrap'>");
|
||||
cgit_print_branches(ctx.cfg.summary_branches);
|
||||
htmlf("<tr class='nohover'><td colspan='%d'> </td></tr>", columns);
|
||||
|
@ -62,7 +61,6 @@ void cgit_print_summary(void)
|
|||
urls = 0;
|
||||
cgit_add_clone_urls(print_url);
|
||||
html("</table>");
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
||||
/* The caller must free the return value. */
|
||||
|
@ -99,24 +97,13 @@ static char* append_readme_path(const char *filename, const char *ref, const cha
|
|||
return full_path;
|
||||
}
|
||||
|
||||
void cgit_print_repo_readme(const char *path)
|
||||
void cgit_print_repo_readme(char *path)
|
||||
{
|
||||
char *filename, *ref, *mimetype;
|
||||
char *filename, *ref;
|
||||
int free_filename = 0;
|
||||
|
||||
mimetype = get_mimetype_for_filename(path);
|
||||
if (mimetype && (!strncmp(mimetype, "image/", 6) || !strncmp(mimetype, "video/", 6))) {
|
||||
ctx.page.mimetype = mimetype;
|
||||
ctx.page.charset = NULL;
|
||||
cgit_print_plain();
|
||||
free(mimetype);
|
||||
return;
|
||||
}
|
||||
free(mimetype);
|
||||
|
||||
cgit_print_layout_start();
|
||||
if (ctx.repo->readme.nr == 0)
|
||||
goto done;
|
||||
return;
|
||||
|
||||
filename = ctx.repo->readme.items[0].string;
|
||||
ref = ctx.repo->readme.items[0].util;
|
||||
|
@ -125,7 +112,7 @@ void cgit_print_repo_readme(const char *path)
|
|||
free_filename = 1;
|
||||
filename = append_readme_path(filename, ref, path);
|
||||
if (!filename)
|
||||
goto done;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Print the calculated readme, either from the git repo or from the
|
||||
|
@ -142,7 +129,4 @@ void cgit_print_repo_readme(const char *path)
|
|||
html("</div>");
|
||||
if (free_filename)
|
||||
free(filename);
|
||||
|
||||
done:
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifndef UI_SUMMARY_H
|
||||
#define UI_SUMMARY_H
|
||||
|
||||
extern void cgit_print_summary(void);
|
||||
extern void cgit_print_repo_readme(const char *path);
|
||||
extern void cgit_print_summary();
|
||||
extern void cgit_print_repo_readme(char *path);
|
||||
|
||||
#endif /* UI_SUMMARY_H */
|
||||
|
|
47
ui-tag.c
47
ui-tag.c
|
@ -6,8 +6,6 @@
|
|||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include "cgit.h"
|
||||
#include "ui-tag.h"
|
||||
#include "html.h"
|
||||
|
@ -35,51 +33,46 @@ static void print_tag_content(char *buf)
|
|||
|
||||
static void print_download_links(char *revname)
|
||||
{
|
||||
html("<tr><th>download</th><td class='oid'>");
|
||||
cgit_print_snapshot_links(ctx.repo, revname, "<br/>");
|
||||
html("<tr><th>download</th><td class='sha1'>");
|
||||
cgit_print_snapshot_links(ctx.qry.repo, ctx.qry.head,
|
||||
revname, ctx.repo->snapshots);
|
||||
html("</td></tr>");
|
||||
}
|
||||
|
||||
void cgit_print_tag(char *revname)
|
||||
{
|
||||
struct strbuf fullref = STRBUF_INIT;
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
struct object *obj;
|
||||
struct tag *tag;
|
||||
struct taginfo *info;
|
||||
|
||||
if (!revname)
|
||||
revname = ctx.qry.head;
|
||||
|
||||
strbuf_addf(&fullref, "refs/tags/%s", revname);
|
||||
if (repo_get_oid(the_repository, fullref.buf, &oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad tag reference: %s", revname);
|
||||
if (get_sha1(fullref.buf, sha1)) {
|
||||
cgit_print_error("Bad tag reference: %s", revname);
|
||||
goto cleanup;
|
||||
}
|
||||
obj = parse_object(the_repository, &oid);
|
||||
obj = parse_object(sha1);
|
||||
if (!obj) {
|
||||
cgit_print_error_page(500, "Internal server error",
|
||||
"Bad object id: %s", oid_to_hex(&oid));
|
||||
cgit_print_error("Bad object id: %s", sha1_to_hex(sha1));
|
||||
goto cleanup;
|
||||
}
|
||||
if (obj->type == OBJ_TAG) {
|
||||
struct tag *tag;
|
||||
struct taginfo *info;
|
||||
|
||||
tag = lookup_tag(the_repository, &oid);
|
||||
tag = lookup_tag(sha1);
|
||||
if (!tag || parse_tag(tag) || !(info = cgit_parse_tag(tag))) {
|
||||
cgit_print_error_page(500, "Internal server error",
|
||||
"Bad tag object: %s", revname);
|
||||
cgit_print_error("Bad tag object: %s", revname);
|
||||
goto cleanup;
|
||||
}
|
||||
cgit_print_layout_start();
|
||||
html("<table class='commit-info'>\n");
|
||||
html("<tr><td>tag name</td><td>");
|
||||
htmlf("<tr><td>tag name</td><td>");
|
||||
html_txt(revname);
|
||||
htmlf(" (%s)</td></tr>\n", oid_to_hex(&oid));
|
||||
htmlf(" (%s)</td></tr>\n", sha1_to_hex(sha1));
|
||||
if (info->tagger_date > 0) {
|
||||
html("<tr><td>tag date</td><td>");
|
||||
html_txt(show_date(info->tagger_date, info->tagger_tz,
|
||||
cgit_date_mode(DATE_ISO8601)));
|
||||
cgit_print_date(info->tagger_date, FMT_LONGDATE, ctx.cfg.local_time);
|
||||
html("</td></tr>\n");
|
||||
}
|
||||
if (info->tagger) {
|
||||
|
@ -93,28 +86,24 @@ void cgit_print_tag(char *revname)
|
|||
cgit_close_filter(ctx.repo->email_filter);
|
||||
html("</td></tr>\n");
|
||||
}
|
||||
html("<tr><td>tagged object</td><td class='oid'>");
|
||||
html("<tr><td>tagged object</td><td class='sha1'>");
|
||||
cgit_object_link(tag->tagged);
|
||||
html("</td></tr>\n");
|
||||
if (ctx.repo->snapshots)
|
||||
print_download_links(revname);
|
||||
html("</table>\n");
|
||||
print_tag_content(info->msg);
|
||||
cgit_print_layout_end();
|
||||
cgit_free_taginfo(info);
|
||||
} else {
|
||||
cgit_print_layout_start();
|
||||
html("<table class='commit-info'>\n");
|
||||
html("<tr><td>tag name</td><td>");
|
||||
htmlf("<tr><td>tag name</td><td>");
|
||||
html_txt(revname);
|
||||
html("</td></tr>\n");
|
||||
html("<tr><td>tagged object</td><td class='oid'>");
|
||||
html("<tr><td>Tagged object</td><td class='sha1'>");
|
||||
cgit_object_link(obj);
|
||||
html("</td></tr>\n");
|
||||
if (ctx.repo->snapshots)
|
||||
print_download_links(revname);
|
||||
html("</table>\n");
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
||||
cleanup:
|
||||
|
|
196
ui-tree.c
196
ui-tree.c
|
@ -1,13 +1,12 @@
|
|||
/* ui-tree.c: functions for tree output
|
||||
*
|
||||
* Copyright (C) 2006-2017 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
* Copyright (C) 2006-2014 cgit Development Team <cgit@lists.zx2c4.com>
|
||||
*
|
||||
* Licensed under GNU General Public License v2
|
||||
* (see COPYING for full license text)
|
||||
*/
|
||||
|
||||
#define USE_THE_REPOSITORY_VARIABLE
|
||||
|
||||
#include <ctype.h>
|
||||
#include "cgit.h"
|
||||
#include "ui-tree.h"
|
||||
#include "html.h"
|
||||
|
@ -86,39 +85,27 @@ static void print_binary_buffer(char *buf, unsigned long size)
|
|||
html("</table>\n");
|
||||
}
|
||||
|
||||
static void print_object(const struct object_id *oid, const char *path, const char *basename, const char *rev)
|
||||
static void print_object(const unsigned char *sha1, char *path, const char *basename, const char *rev)
|
||||
{
|
||||
enum object_type type;
|
||||
char *buf;
|
||||
unsigned long size;
|
||||
bool is_binary;
|
||||
|
||||
type = oid_object_info(the_repository, oid, &size);
|
||||
type = sha1_object_info(sha1, &size);
|
||||
if (type == OBJ_BAD) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Bad object name: %s", oid_to_hex(oid));
|
||||
cgit_print_error("Bad object name: %s", sha1_to_hex(sha1));
|
||||
return;
|
||||
}
|
||||
|
||||
buf = repo_read_object_file(the_repository, oid, &type, &size);
|
||||
buf = read_sha1_file(sha1, &type, &size);
|
||||
if (!buf) {
|
||||
cgit_print_error_page(500, "Internal server error",
|
||||
"Error reading object %s", oid_to_hex(oid));
|
||||
cgit_print_error("Error reading object %s", sha1_to_hex(sha1));
|
||||
return;
|
||||
}
|
||||
is_binary = buffer_is_binary(buf, size);
|
||||
|
||||
cgit_set_title_from_path(path);
|
||||
|
||||
cgit_print_layout_start();
|
||||
htmlf("blob: %s (", oid_to_hex(oid));
|
||||
htmlf("blob: %s (", sha1_to_hex(sha1));
|
||||
cgit_plain_link("plain", NULL, NULL, ctx.qry.head,
|
||||
rev, path);
|
||||
if (ctx.repo->enable_blame && !is_binary) {
|
||||
html(") (");
|
||||
cgit_blame_link("blame", NULL, NULL, ctx.qry.head,
|
||||
rev, path);
|
||||
}
|
||||
html(")\n");
|
||||
|
||||
if (ctx.cfg.max_blob_size && size / 1024 > ctx.cfg.max_blob_size) {
|
||||
|
@ -127,102 +114,34 @@ static void print_object(const struct object_id *oid, const char *path, const ch
|
|||
return;
|
||||
}
|
||||
|
||||
if (is_binary)
|
||||
if (buffer_is_binary(buf, size))
|
||||
print_binary_buffer(buf, size);
|
||||
else
|
||||
print_text_buffer(basename, buf, size);
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
struct single_tree_ctx {
|
||||
struct strbuf *path;
|
||||
struct object_id oid;
|
||||
char *name;
|
||||
size_t count;
|
||||
};
|
||||
|
||||
static int single_tree_cb(const struct object_id *oid, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, void *cbdata)
|
||||
{
|
||||
struct single_tree_ctx *ctx = cbdata;
|
||||
|
||||
if (++ctx->count > 1)
|
||||
return -1;
|
||||
|
||||
if (!S_ISDIR(mode)) {
|
||||
ctx->count = 2;
|
||||
return -1;
|
||||
}
|
||||
|
||||
ctx->name = xstrdup(pathname);
|
||||
oidcpy(&ctx->oid, oid);
|
||||
strbuf_addf(ctx->path, "/%s", pathname);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void write_tree_link(const struct object_id *oid, char *name,
|
||||
char *rev, struct strbuf *fullpath)
|
||||
{
|
||||
size_t initial_length = fullpath->len;
|
||||
struct tree *tree;
|
||||
struct single_tree_ctx tree_ctx = {
|
||||
.path = fullpath,
|
||||
.count = 1,
|
||||
};
|
||||
struct pathspec paths = {
|
||||
.nr = 0
|
||||
};
|
||||
|
||||
oidcpy(&tree_ctx.oid, oid);
|
||||
|
||||
while (tree_ctx.count == 1) {
|
||||
cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head, rev,
|
||||
fullpath->buf);
|
||||
|
||||
tree = lookup_tree(the_repository, &tree_ctx.oid);
|
||||
if (!tree)
|
||||
return;
|
||||
|
||||
free(tree_ctx.name);
|
||||
tree_ctx.name = NULL;
|
||||
tree_ctx.count = 0;
|
||||
|
||||
read_tree(the_repository, tree, &paths, single_tree_cb, &tree_ctx);
|
||||
|
||||
if (tree_ctx.count != 1)
|
||||
break;
|
||||
|
||||
html(" / ");
|
||||
name = tree_ctx.name;
|
||||
}
|
||||
|
||||
strbuf_setlen(fullpath, initial_length);
|
||||
}
|
||||
|
||||
static int ls_item(const struct object_id *oid, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, void *cbdata)
|
||||
static int ls_item(const unsigned char *sha1, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, int stage, void *cbdata)
|
||||
{
|
||||
struct walk_tree_context *walk_tree_ctx = cbdata;
|
||||
char *name;
|
||||
struct strbuf fullpath = STRBUF_INIT;
|
||||
struct strbuf linkpath = STRBUF_INIT;
|
||||
struct strbuf class = STRBUF_INIT;
|
||||
enum object_type type;
|
||||
unsigned long size = 0;
|
||||
char *buf;
|
||||
|
||||
name = xstrdup(pathname);
|
||||
strbuf_addf(&fullpath, "%s%s%s", ctx.qry.path ? ctx.qry.path : "",
|
||||
ctx.qry.path ? "/" : "", name);
|
||||
|
||||
if (!S_ISGITLINK(mode)) {
|
||||
type = oid_object_info(the_repository, oid, &size);
|
||||
type = sha1_object_info(sha1, &size);
|
||||
if (type == OBJ_BAD) {
|
||||
htmlf("<tr><td colspan='3'>Bad object: %s %s</td></tr>",
|
||||
name,
|
||||
oid_to_hex(oid));
|
||||
goto cleanup;
|
||||
sha1_to_hex(sha1));
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -230,10 +149,10 @@ static int ls_item(const struct object_id *oid, struct strbuf *base,
|
|||
cgit_print_filemode(mode);
|
||||
html("</td><td>");
|
||||
if (S_ISGITLINK(mode)) {
|
||||
cgit_submodule_link("ls-mod", fullpath.buf, oid_to_hex(oid));
|
||||
cgit_submodule_link("ls-mod", fullpath.buf, sha1_to_hex(sha1));
|
||||
} else if (S_ISDIR(mode)) {
|
||||
write_tree_link(oid, name, walk_tree_ctx->curr_rev,
|
||||
&fullpath);
|
||||
cgit_tree_link(name, NULL, "ls-dir", ctx.qry.head,
|
||||
walk_tree_ctx->curr_rev, fullpath.buf);
|
||||
} else {
|
||||
char *ext = strrchr(name, '.');
|
||||
strbuf_addstr(&class, "ls-blob");
|
||||
|
@ -242,48 +161,27 @@ static int ls_item(const struct object_id *oid, struct strbuf *base,
|
|||
cgit_tree_link(name, NULL, class.buf, ctx.qry.head,
|
||||
walk_tree_ctx->curr_rev, fullpath.buf);
|
||||
}
|
||||
if (S_ISLNK(mode)) {
|
||||
html(" -> ");
|
||||
buf = repo_read_object_file(the_repository, oid, &type, &size);
|
||||
if (!buf) {
|
||||
htmlf("Error reading object: %s", oid_to_hex(oid));
|
||||
goto cleanup;
|
||||
}
|
||||
strbuf_addbuf(&linkpath, &fullpath);
|
||||
strbuf_addf(&linkpath, "/../%s", buf);
|
||||
strbuf_normalize_path(&linkpath);
|
||||
cgit_tree_link(buf, NULL, class.buf, ctx.qry.head,
|
||||
walk_tree_ctx->curr_rev, linkpath.buf);
|
||||
free(buf);
|
||||
strbuf_release(&linkpath);
|
||||
}
|
||||
htmlf("</td><td class='ls-size'>%li</td>", size);
|
||||
|
||||
html("<td>");
|
||||
cgit_log_link("log", NULL, "button", ctx.qry.head,
|
||||
walk_tree_ctx->curr_rev, fullpath.buf, 0, NULL, NULL,
|
||||
ctx.qry.showmsg, 0);
|
||||
ctx.qry.showmsg);
|
||||
if (ctx.repo->max_stats)
|
||||
cgit_stats_link("stats", NULL, "button", ctx.qry.head,
|
||||
fullpath.buf);
|
||||
if (!S_ISGITLINK(mode))
|
||||
cgit_plain_link("plain", NULL, "button", ctx.qry.head,
|
||||
walk_tree_ctx->curr_rev, fullpath.buf);
|
||||
if (!S_ISDIR(mode) && ctx.repo->enable_blame)
|
||||
cgit_blame_link("blame", NULL, "button", ctx.qry.head,
|
||||
walk_tree_ctx->curr_rev, fullpath.buf);
|
||||
html("</td></tr>\n");
|
||||
|
||||
cleanup:
|
||||
free(name);
|
||||
strbuf_release(&fullpath);
|
||||
strbuf_release(&class);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ls_head(void)
|
||||
static void ls_head()
|
||||
{
|
||||
cgit_print_layout_start();
|
||||
html("<table summary='tree listing' class='list'>\n");
|
||||
html("<tr class='nohover'>");
|
||||
html("<th class='left'>Mode</th>");
|
||||
|
@ -293,59 +191,52 @@ static void ls_head(void)
|
|||
html("</tr>\n");
|
||||
}
|
||||
|
||||
static void ls_tail(void)
|
||||
static void ls_tail()
|
||||
{
|
||||
html("</table>\n");
|
||||
cgit_print_layout_end();
|
||||
}
|
||||
|
||||
static void ls_tree(const struct object_id *oid, const char *path, struct walk_tree_context *walk_tree_ctx)
|
||||
static void ls_tree(const unsigned char *sha1, char *path, struct walk_tree_context *walk_tree_ctx)
|
||||
{
|
||||
struct tree *tree;
|
||||
struct pathspec paths = {
|
||||
.nr = 0
|
||||
};
|
||||
|
||||
tree = parse_tree_indirect(oid);
|
||||
tree = parse_tree_indirect(sha1);
|
||||
if (!tree) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Not a tree object: %s", oid_to_hex(oid));
|
||||
cgit_print_error("Not a tree object: %s", sha1_to_hex(sha1));
|
||||
return;
|
||||
}
|
||||
|
||||
ls_head();
|
||||
read_tree(the_repository, tree, &paths, ls_item, walk_tree_ctx);
|
||||
read_tree_recursive(tree, "", 0, 1, &paths, ls_item, walk_tree_ctx);
|
||||
ls_tail();
|
||||
}
|
||||
|
||||
|
||||
static int walk_tree(const struct object_id *oid, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, void *cbdata)
|
||||
static int walk_tree(const unsigned char *sha1, struct strbuf *base,
|
||||
const char *pathname, unsigned mode, int stage, void *cbdata)
|
||||
{
|
||||
struct walk_tree_context *walk_tree_ctx = cbdata;
|
||||
static char buffer[PATH_MAX];
|
||||
|
||||
if (walk_tree_ctx->state == 0) {
|
||||
struct strbuf buffer = STRBUF_INIT;
|
||||
|
||||
strbuf_addbuf(&buffer, base);
|
||||
strbuf_addstr(&buffer, pathname);
|
||||
if (strcmp(walk_tree_ctx->match_path, buffer.buf))
|
||||
memcpy(buffer, base->buf, base->len);
|
||||
strcpy(buffer + base->len, pathname);
|
||||
if (strcmp(walk_tree_ctx->match_path, buffer))
|
||||
return READ_TREE_RECURSIVE;
|
||||
|
||||
if (S_ISDIR(mode)) {
|
||||
walk_tree_ctx->state = 1;
|
||||
cgit_set_title_from_path(buffer.buf);
|
||||
strbuf_release(&buffer);
|
||||
ls_head();
|
||||
return READ_TREE_RECURSIVE;
|
||||
} else {
|
||||
walk_tree_ctx->state = 2;
|
||||
print_object(oid, buffer.buf, pathname, walk_tree_ctx->curr_rev);
|
||||
strbuf_release(&buffer);
|
||||
print_object(sha1, buffer, pathname, walk_tree_ctx->curr_rev);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
ls_item(oid, base, pathname, mode, walk_tree_ctx);
|
||||
ls_item(sha1, base, pathname, mode, stage, walk_tree_ctx);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -356,7 +247,7 @@ static int walk_tree(const struct object_id *oid, struct strbuf *base,
|
|||
*/
|
||||
void cgit_print_tree(const char *rev, char *path)
|
||||
{
|
||||
struct object_id oid;
|
||||
unsigned char sha1[20];
|
||||
struct commit *commit;
|
||||
struct pathspec_item path_items = {
|
||||
.match = path,
|
||||
|
@ -374,33 +265,26 @@ void cgit_print_tree(const char *rev, char *path)
|
|||
if (!rev)
|
||||
rev = ctx.qry.head;
|
||||
|
||||
if (repo_get_oid(the_repository, rev, &oid)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Invalid revision name: %s", rev);
|
||||
if (get_sha1(rev, sha1)) {
|
||||
cgit_print_error("Invalid revision name: %s", rev);
|
||||
return;
|
||||
}
|
||||
commit = lookup_commit_reference(the_repository, &oid);
|
||||
if (!commit || repo_parse_commit(the_repository, commit)) {
|
||||
cgit_print_error_page(404, "Not found",
|
||||
"Invalid commit reference: %s", rev);
|
||||
commit = lookup_commit_reference(sha1);
|
||||
if (!commit || parse_commit(commit)) {
|
||||
cgit_print_error("Invalid commit reference: %s", rev);
|
||||
return;
|
||||
}
|
||||
|
||||
walk_tree_ctx.curr_rev = xstrdup(rev);
|
||||
|
||||
if (path == NULL) {
|
||||
ls_tree(get_commit_tree_oid(commit), NULL, &walk_tree_ctx);
|
||||
ls_tree(commit->tree->object.sha1, NULL, &walk_tree_ctx);
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
read_tree(the_repository, repo_get_commit_tree(the_repository, commit),
|
||||
&paths, walk_tree, &walk_tree_ctx);
|
||||
read_tree_recursive(commit->tree, "", 0, 0, &paths, walk_tree, &walk_tree_ctx);
|
||||
if (walk_tree_ctx.state == 1)
|
||||
ls_tail();
|
||||
else if (walk_tree_ctx.state == 2)
|
||||
cgit_print_layout_end();
|
||||
else
|
||||
cgit_print_error_page(404, "Not found", "Path not found");
|
||||
|
||||
cleanup:
|
||||
free(walk_tree_ctx.curr_rev);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue