summary refs log tree commit diff stats
path: root/Makefile
blob: 5d8ba2f9e95af5855a0132a70cd76c1739affe43 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# Copyright (C) 2009, 2010  Roman Zimbelmann <romanz@lavabit.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# 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, see <http://www.gnu.org/licenses/>.

NAME = ranger
VERSION = $(shell grep -m 1 -o '[0-9][0-9.]\+' README)
SNAPSHOT_NAME ?= $(NAME)-$(VERSION)-$(shell git rev-parse HEAD | cut -b 1-8).tar.gz
# Find suitable python version (need python >= 2.6 or 3.1):
PYTHON ?= $(shell python -c 'import sys; sys.exit(sys.version < "2.6")' && \
	which python || which python3.2 || which python3.1 || which python3 || \
	which python2.6)
SETUPOPTS ?= '--record=install_log.txt'
DOCDIR ?= doc/pydoc
DESTDIR ?= /
PYOPTIMIZE ?= 1

CWD = $(shell pwd)

default: compile
	@echo 'Run `make options` for a list of all options'

options: help
	@echo
	@echo 'Options:'
	@echo 'PYTHON = $(PYTHON)'
	@echo 'PYOPTIMIZE = $(PYOPTIMIZE)'
	@echo 'DOCDIR = $(DOCDIR)'

help:
	@echo 'make install:  Install $(NAME)'
	@echo 'make clean:    Remove the compiled files (*.pyc, *.pyo)'
	@echo 'make doc:      Create the pydoc documentation'
	@echo 'make cleandoc: Remove the pydoc documentation'
	@echo 'make man:      Compile the manpage with "pod2man"'
	@echo 'make manhtml:  Compile the html manpage with "pod2html"'
	@echo 'make snapshot: Create a tar.gz of the current git revision'
	@echo 'make todo:     Look for TODO and XXX markers in the source code'

install:
	$(PYTHON) setup.py install $(SETUPOPTS) \
		'--root=$(DESTDIR)' --optimize=$(PYOPTIMIZE)

compile: clean
	PYTHONOPTIMIZE=$(PYOPTIMIZE) $(PYTHON) -m compileall -q ranger

clean:
	find ranger -regex .\*.py[co]\$$ -delete
	find ranger -depth -name __pycache__ -type d -exec rm -rf -- {} \;

doc: cleandoc
	mkdir -p $(DOCDIR)
	cd $(DOCDIR); \
		$(PYTHON) -c 'import pydoc, sys; \
		sys.path[0] = "$(CWD)"; \
		pydoc.writedocs("$(CWD)")'
	find . -name \*.html -exec sed -i 's|'$(CWD)'|../..|g' -- {} \;

man:
	pod2man --stderr --center='ranger manual' --date='$(NAME)-$(VERSION)' \
		--release=$(shell date +%x) doc/ranger.pod doc/ranger.1

manhtml:
	pod2html doc/ranger.pod --outfile=doc/ranger.1.html

cleandoc:
	test -d $(DOCDIR) && rm -f -- $(DOCDIR)/*.html || true

snapshot:
	git archive --prefix='$(NAME)-$(VERSION)/' --format=tar HEAD | gzip > $(SNAPSHOT_NAME)

todo:
	@grep --color -Ion '\(TODO\|XXX\).*' -r ranger

.PHONY: default options compile clean doc cleandoc snapshot install man todo
class="w"> -1) expected_indent = indent; else assert(expected_indent == indent); int start = indent+1; /* skip '>' */ luaL_addstring(&b, &line[start]); /* guaranteed to at least be null */ } luaL_pushresult(&b); /* final state of line goes out into the world */ } /* leave a single table on stack containing the next top-level definition from the file */ void teliva_load_definition(lua_State* L, FILE* in) { lua_newtable(L); int def_idx = lua_gettop(L); char line[1024] = {'\0'}; do { if (feof(in) || fgets(line, 1024, in) == NULL) { lua_pushnil(L); return; } } while (line[0] == '#'); /* comment at start of file */ assert(line[strlen(line)-1] == '\n'); do { assert(line[0] == '-' || line[0] == ' '); assert(line[1] == ' '); /* key/value pair always indented at 0, never empty, unambiguously not a multiline string */ char key[512] = {'\0'}; char value[1024] = {'\0'}; assert(line[2] != ' '); assert(line[2] != '>'); assert(line[2] != '\n'); assert(line[2] != '\0'); memset(key, 0, 512); memset(value, 0, 1024); sscanf(line+2, "%s%s", key, value); assert(key[strlen(key)-1] == ':'); key[strlen(key)-1] = '\0'; lua_pushstring(L, key); if (value[0] != '\0') { lua_pushstring(L, value); /* value string on same line */ char c = fgetc(in); ungetc(c, in); if (c == '-') { strcpy(line, "-\n"); } else { memset(line, '\0', 1024); fgets(line, 1024, in); } } else { teliva_load_multiline_string(L, in, line, 1024); /* load from later lines */ } lua_settable(L, def_idx); } while (line[0] == ' '); } void load_tlv(lua_State* L, char* filename) { lua_newtable(L); int history_array = lua_gettop(L); FILE* in = fopen(filename, "r"); if (in == NULL) { endwin(); fprintf(stderr, "no such file\n"); exit(1); } for (int i = 1; !feof(in); ++i) { teliva_load_definition(L, in); if (lua_isnil(L, -1)) break; lua_rawseti(L, history_array, i); } fclose(in); lua_setglobal(L, "teliva_program"); } void emit_multiline_string(FILE* out, const char* value) { fprintf(out, " >"); for (const char* curr = value; *curr != '\0'; ++curr) { if (*curr == '\n' && *(curr+1) != '\0') fprintf(out, "\n >"); else fprintf(out, "%c", *curr); } } extern int mkstemp(char *template); extern FILE *fdopen(int fd, const char *mode); void save_tlv(lua_State* L, char* filename) { lua_getglobal(L, "teliva_program"); int history_array = lua_gettop(L); int history_array_size = luaL_getn(L, history_array); char outfilename[] = "teliva_out_XXXXXX"; int outfd = mkstemp(outfilename); if (outfd == -1) { endwin(); perror("save_tlv: error in creating temporary file"); abort(); } FILE *out = fdopen(outfd, "w"); fprintf(out, "# .tlv file generated by https://github.com/akkartik/teliva\n"); fprintf(out, "# You may edit it if you are careful; however, you may see cryptic errors if you\n"); fprintf(out, "# violate Teliva's assumptions.\n"); fprintf(out, "#\n"); fprintf(out, "# .tlv files are representations of Teliva programs. Teliva programs consist of\n"); fprintf(out, "# sequences of definitions. Each definition is a table of key/value pairs. Keys\n"); fprintf(out, "# and values are both strings.\n"); fprintf(out, "#\n"); fprintf(out, "# Lines in .tlv files always follow exactly one of the following forms:\n"); fprintf(out, "# - comment lines at the top of the file starting with '#' at column 0\n"); fprintf(out, "# - beginnings of definitions starting with '- ' at column 0, followed by a\n"); fprintf(out, "# key/value pair\n"); fprintf(out, "# - key/value pairs consisting of ' ' at column 0, containing either a\n"); fprintf(out, "# spaceless value on the same line, or a multi-line value\n"); fprintf(out, "# - multiline values indented by more than 2 spaces, starting with a '>'\n"); fprintf(out, "#\n"); fprintf(out, "# If these constraints are violated, Teliva may unceremoniously crash. Please\n"); fprintf(out, "# report bugs at http://akkartik.name/contact\n"); for (int i = 1; i <= history_array_size; ++i) { lua_rawgeti(L, history_array, i); int table = lua_gettop(L); int first = 1; for (lua_pushnil(L); lua_next(L, table) != 0; lua_pop(L, 1)) { if (first) fprintf(out, "- "); else fprintf(out, " "); first = 0; const char* key = lua_tostring(L, -2); if (strcmp(key, "__teliva_undo") == 0) { fprintf(out, "%s: %ld\n", key, lua_tointeger(L, -1)); continue; } const char* value = lua_tostring(L, -1); if (strchr(value, ' ') || strchr(value, '\n')) { fprintf(out, "%s:\n", key); emit_multiline_string(out, value); } else { fprintf(out, "%s: %s\n", key, value); } } lua_pop(L, 1); } fclose(out); rename(outfilename, filename); lua_pop(L, 1); } static const char* special_history_keys[] = { "__teliva_timestamp", "__teliva_undo", "__teliva_note", NULL, }; int is_special_history_key(const char* key) { for (const char** curr = special_history_keys; *curr != NULL; ++curr) { if (strcmp(*curr, key) == 0) return 1; } return 0; }