diff --git a/tools/Jamfile b/tools/Jamfile index f8587b6e3..b8ccfaf31 100644 --- a/tools/Jamfile +++ b/tools/Jamfile @@ -35,7 +35,6 @@ project tools static ; -exe parse_access_log : parse_access_log.cpp ; exe dht : dht_put.cpp : ../ed25519/src ; exe session_log_alerts : session_log_alerts.cpp ; diff --git a/tools/Makefile.am b/tools/Makefile.am index 5a31e3089..61ef0b84c 100644 --- a/tools/Makefile.am +++ b/tools/Makefile.am @@ -1,4 +1,5 @@ tool_programs = \ + dht_put \ session_log_alerts if ENABLE_EXAMPLES @@ -7,19 +8,16 @@ endif EXTRA_PROGRAMS = $(tool_programs) EXTRA_DIST = Jamfile \ - parse_bandwidth_log.py \ - parse_buffer_log.py \ parse_dht_log.py \ parse_dht_rtt.py \ parse_dht_stats.py \ - parse_disk_buffer_log.py\ - parse_memory_log.py \ parse_peer_log.py \ parse_sample.py \ parse_session_stats.py \ parse_utp_log.py session_log_alerts_SOURCES = session_log_alerts.cpp +dht_put_SOURCES = dht_put.cpp LDADD = $(top_builddir)/src/libtorrent-rasterbar.la diff --git a/tools/clean.py b/tools/clean.py index a6f081ad3..6771f2428 100755 --- a/tools/clean.py +++ b/tools/clean.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 import os import shutil diff --git a/tools/dht_flood.py b/tools/dht_flood.py index dd4a222b2..467c9f8ae 100755 --- a/tools/dht_flood.py +++ b/tools/dht_flood.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 import socket import sys diff --git a/tools/dht_put.cpp b/tools/dht_put.cpp index a5ec15041..16705483e 100644 --- a/tools/dht_put.cpp +++ b/tools/dht_put.cpp @@ -206,7 +206,14 @@ void load_dht_state(lt::session& s) { std::vector state; state.resize(size); - fread(&state[0], 1, state.size(), f); + std::size_t ret = fread(&state[0], 1, state.size(), f); + if (ret != state.size()) + { + std::fprintf(stderr, "failed to read .dht: (%d) %s" + , errno, strerror(errno)); + std::fclose(f); + return; + } bdecode_node e; error_code ec; diff --git a/tools/gen_fwd.py b/tools/gen_fwd.py index 9813801ae..2fa7372b6 100644 --- a/tools/gen_fwd.py +++ b/tools/gen_fwd.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 import os diff --git a/tools/parse_access_log.cpp b/tools/parse_access_log.cpp deleted file mode 100644 index 3046347fa..000000000 --- a/tools/parse_access_log.cpp +++ /dev/null @@ -1,180 +0,0 @@ -/* - -Copyright (c) 2011, Arvid Norberg -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above copyright - notice, this list of conditions and the following disclaimer in - the documentation and/or other materials provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE -LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - -*/ - -#include "libtorrent/config.hpp" -#include "libtorrent/io.hpp" -#include -#include -#include -#include -#include -#include -#include // for PRId64 et.al. - -using namespace lt; -using namespace lt::detail; // for write_* and read_* - -void print_usage() -{ - std::fprintf(stderr, "usage: parse_access_log log-file\n\n" - "prints a gnuplot readable data file to stdout\n"); - exit(1); -} - -struct file_op -{ - std::uint64_t timestamp; - std::uint64_t offset; - std::uint8_t event; -}; - -int main(int argc, char* argv[]) -{ - if (argc != 2) print_usage(); - - FILE* log_file = std::fopen(argv[1], "r"); - if (log_file == nullptr) - { - std::fprintf(stderr, "failed to open logfile: %s\n%d: %s\n" - , argv[1], errno, strerror(errno)); - return 1; - } - - FILE* writes_file = std::fopen("writes.log", "w+"); - FILE* reads_file = std::fopen("reads.log", "w+"); - - FILE* writes_elev_file = std::fopen("writes_elevator.log", "w+"); - FILE* reads_elev_file = std::fopen("reads_elevator.log", "w+"); - - - using op_map = std::map; - op_map outstanding_ops; - - std::uint64_t first_timestamp = 0; - - for (;;) - { - char entry[21]; - char* ptr = entry; - int ret = int(fread(&entry, 1, sizeof(entry), log_file)); - if (ret != sizeof(entry)) break; - - file_op op; - op.timestamp = read_uint64(ptr); - op.offset = read_uint64(ptr); - std::uint32_t event_id = read_uint32(ptr); - op.event = read_uint8(ptr); - - if (first_timestamp == 0) first_timestamp = op.timestamp; - - bool write = (op.event & 1) != 0; - bool complete = (op.event & 2) != 0; - if (complete) - { - FILE* out_file = nullptr; - op_map::iterator i = outstanding_ops.find(event_id); - if (i != outstanding_ops.end()) - { - if (i->second.timestamp > op.timestamp) - { - std::fprintf(stderr, "end-event stamped before " - "start-event: %" PRId64 " started at: %f\n" - , op.offset, double(i->second.timestamp) / 1000000.f); - i->second.timestamp = op.timestamp; - } - - out_file = write ? writes_file : reads_file; - double start_time = double(i->second.timestamp - first_timestamp) / 1000000.0; - double end_time = double(op.timestamp - first_timestamp) / 1000000.0; - double duration_time = double(op.timestamp - i->second.timestamp) / 1000000.0; - std::fprintf(out_file, "%f\t%" PRId64 "\t%f\n" - , start_time, op.offset, duration_time); - - out_file = write ? writes_elev_file : reads_elev_file; - std::fprintf(out_file, "%f\t%" PRId64 "\n", end_time, op.offset); - - outstanding_ops.erase(i); - } - else - { - std::fprintf(stderr, "no start event for (%u): %" PRId64 " ended at: %f\n" - , event_id, op.offset, double(op.timestamp) / 1000000.f); - } - } - else - { - op_map::iterator i = outstanding_ops.find(event_id); - if (i != outstanding_ops.end()) - { - std::fprintf(stderr, "duplicate start event for (%u): %" PRId64 " at: %f" - "(current start is at: %f)\n" - , event_id, op.offset, double(i->second.timestamp - first_timestamp) / 1000000.f - , double(op.timestamp - first_timestamp) / 1000000.f); - } - else - { - outstanding_ops[event_id] = op; - } - } - } - - std::fclose(writes_file); - std::fclose(reads_file); - std::fclose(writes_elev_file); - std::fclose(reads_elev_file); - std::fclose(log_file); - - FILE* gnuplot = std::fopen("file_access.gnuplot", "w+"); - - char const* gnuplot_file = - "set term png size 1400,1024\n" - "set output \"file_access.png\"\n" - "set xlabel \"time (s)\"\n" - "set ylabel \"file offset\"\n" - "set style line 1 lc rgb \"#ff8888\"\n" - "set style line 2 lc rgb \"#88ff88\"\n" - "set style arrow 1 nohead ls 1\n" - "set style arrow 2 nohead ls 2\n" - "plot \"writes.log\" using 1:2:3:(0) title \"writes\" with vectors arrowstyle 1, " - "\"reads.log\" using 1:2:3:(0) title \"reads\" with vectors arrowstyle 2\n"; - - std::fwrite(gnuplot_file, strlen(gnuplot_file), 1, gnuplot); - std::fclose(gnuplot); - - system("gnuplot file_access.gnuplot"); - - assert(outstanding_ops.empty()); - - return 0; -} - - diff --git a/tools/parse_bandwidth_log.py b/tools/parse_bandwidth_log.py deleted file mode 100755 index cf5108bda..000000000 --- a/tools/parse_bandwidth_log.py +++ /dev/null @@ -1,26 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import os -import sys - -keys = [['upload rate', 'x1y1', 6], ['history entries', 'x1y2', 10], ['queue', 'x1y2', 4]] - -out = open('bandwidth.gnuplot', 'wb') -print("set term png size 1200,700", file=out) -print('set output "bandwidth_manager.png"', file=out) -print('set xrange [0:*]', file=out) -print('set xlabel "time (ms)"', file=out) -print('set ylabel "Rate (B/s)"', file=out) -print('set ytics 10000', file=out) -print('set y2label "number"', file=out) -print('set y2range [0:*]', file=out) -# print("set style data lines", file=out) -print("set key box", file=out) -print('plot', end=' ', file=out) -for k, a, c in keys: - print(' "%s" using 1:%d title "%s" axes %s with steps,' % (sys.argv[1], c, k, a), end=' ', file=out) -print('x=0', file=out) -out.close() - -os.system('gnuplot bandwidth.gnuplot') diff --git a/tools/parse_buffer_log.py b/tools/parse_buffer_log.py deleted file mode 100755 index fc9aeb119..000000000 --- a/tools/parse_buffer_log.py +++ /dev/null @@ -1,98 +0,0 @@ -#!/usr/bin/env python -# Copyright Arvid Norberg 2008. Use, modification and distribution is -# subject to the Boost Software License, Version 1.0. (See accompanying -# file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - -from __future__ import print_function - -import os -import sys - -lines = open(sys.argv[1], 'rb').readlines() - -# keys = ['send_buffer_utilization'] -keys = ['send_buffer_size', 'used_send_buffer', 'protocol_buffer'] -# keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer'] -# keys = ['send_buffer_alloc', 'send_buffer', 'allocate_buffer_alloc', 'allocate_buffer', 'protocol_buffer', -# 'append_send_buffer'] - -average = ['send_buffer_utilization', 'send_buffer_size', 'used_send_buffer'] -average_interval = 120000 -render = 'lines' - -time_limit = -1 -if len(sys.argv) > 2: - time_limit = int(sys.argv[2]) - - -# logfile format: -# -# example: -# 16434 allocate_buffer: 17 -for k in keys: - - last_sample = 0 - average_accumulator = 0 - average_samples = 0 - peak = 0 - - out = open(k + '.dat', 'wb') - eval_average = False - if k in average: - eval_average = True - peak_out = open(k + '_peak.dat', 'wb') - - for line in lines: - line = line.split(' ') - if len(line) != 3: - print(line) - continue - try: - if line[1] == k + ':': - if time_limit != -1 and int(line[0]) > time_limit: - break - time = line[0] - value = line[2] - if eval_average: - while int(time) > last_sample + average_interval: - last_sample = last_sample + average_interval - if average_samples < 1: - average_samples = 1 - print('%d %f' % (last_sample, average_accumulator / average_samples), file=out) - print('%d %f' % (last_sample, peak), file=peak_out) - average_accumulator = 0 - average_samples = 0 - peak = 0 - average_accumulator = average_accumulator + float(value) - average_samples = average_samples + 1 - if float(value) > peak: - peak = float(value) - else: - print(time + ' ' + value, end=' ', file=out) - except BaseException: - print(line) - - out.close() - peak_out.close() - -out = open('send_buffer.gnuplot', 'wb') -print("set term png size 1200,700", file=out) -print('set output "send_buffer.png"', file=out) -print('set xrange [0:*]', file=out) -print('set xlabel "time (ms)"', file=out) -print('set ylabel "bytes (B)"', file=out) -print("set style data lines", file=out) -print("set key box", file=out) -print('plot', end=' ', file=out) -for k in keys: - if k in average: - print(' "%s.dat" using 1:2 title "%s %d seconds average" with %s,' % - (k, k, average_interval / 1000., render), end=' ', file=out) - print(' "%s_peak.dat" using 1:2 title "%s %d seconds peak" with %s,' % - (k, k, average_interval / 1000., render), end=' ', file=out) - else: - print(' "%s.dat" using 1:2 title "%s" with %s,' % (k, k, render), end=' ', file=out) -print('x=0', file=out) -out.close() - -os.system('gnuplot send_buffer.gnuplot') diff --git a/tools/parse_dht_log.py b/tools/parse_dht_log.py index 88b398164..948e542f4 100755 --- a/tools/parse_dht_log.py +++ b/tools/parse_dht_log.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 + from __future__ import print_function import sys diff --git a/tools/parse_dht_rtt.py b/tools/parse_dht_rtt.py index cb75e181f..6629cfe13 100755 --- a/tools/parse_dht_rtt.py +++ b/tools/parse_dht_rtt.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 + from __future__ import print_function import sys diff --git a/tools/parse_dht_stats.py b/tools/parse_dht_stats.py index 74f8e49e7..c0baa91b4 100755 --- a/tools/parse_dht_stats.py +++ b/tools/parse_dht_stats.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 + import sys import os diff --git a/tools/parse_disk_buffer_log.py b/tools/parse_disk_buffer_log.py deleted file mode 100755 index df387127b..000000000 --- a/tools/parse_disk_buffer_log.py +++ /dev/null @@ -1,97 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import os -import sys - -lines = open(sys.argv[1], 'rb').readlines() - -# logfile format: -# : -# example: -# 16434 read cache: 17 - -key_order = ['receive buffer', 'send buffer', 'released send buffer', 'posted send buffer', - 'received send buffer', 'dispatched send buffer', 'queued send buffer', - 'write cache', 'read cache', 'hash temp'] -colors = ['30f030', '001070', '101080', '2040a0', - '4070d0', '80a0f0', 'f03030', - '80f080', 'f08080', '4040ff'] - -keys = [] -fields = {} -maximum = {} -out = open('disk_buffer_log.dat', 'w+') - -field_sum = {} -field_num_samples = {} -field_timestamp = {} - -for c in key_order: - keys.append(c) - fields[c] = 0 - maximum[c] = 0 - field_sum[c] = 0 - field_num_samples[c] = 0 - field_timestamp[c] = 0 - -last_t = 0 -for l in lines: - try: - t = int(l[0:l.find(' ')]) - c = l[l.find(' ') + 1:l.find(':')] - n = int(l[l.find(':') + 1:-1]) - except BaseException: - print(l) - continue - - if last_t != t: - print('%d\t' % last_t, end=' ', file=out) - for i in keys: - print('%d\t' % maximum[i], end=' ', file=out) - print('\n', end=' ', file=out) - - if c not in keys: - continue - - field_sum[c] += fields[c] * float(t - field_timestamp[c]) - field_timestamp[c] = t - - fields[c] = n - - if n > maximum[c]: - maximum[c] = n - - if last_t != t: - last_t = t - maximum = fields - -for i in keys: - print('%s: avg: %f' % (i, field_sum[i] / last_t)) -print() - -out.close() - -out = open('disk_buffer.gnuplot', 'wb') -print("set term png size 1200,700", file=out) -print('set output "disk_buffer.png"', file=out) -print('set xrange [0:*]', file=out) -print('set xlabel "time (ms)"', file=out) -print('set ylabel "buffers"', file=out) -print("set style data lines", file=out) -print("set key box", file=out) -print('plot', end=' ', file=out) -count = 1 + len(keys) -keys.reverse() -comma = '' -for k in keys: - expr = "$%d" % count - for i in range(2, count): - expr += "+$%d" % i - count -= 1 - print(' %s"disk_buffer_log.dat" using 1:(%s) title "%s" with filledcurves x1 lt rgb "#%s"' % - (comma, expr, k, colors[count - 1]), end=' ', file=out) - comma = ',' -out.close() - -os.system('gnuplot disk_buffer.gnuplot') diff --git a/tools/parse_lookup_log.py b/tools/parse_lookup_log.py index c751c8512..b7a79ee57 100755 --- a/tools/parse_lookup_log.py +++ b/tools/parse_lookup_log.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 + # this is meant to parse the dht_lookups.log generated by parse_dht_log.py from __future__ import print_function diff --git a/tools/parse_memory_log.py b/tools/parse_memory_log.py deleted file mode 100755 index 22439c6be..000000000 --- a/tools/parse_memory_log.py +++ /dev/null @@ -1,132 +0,0 @@ -#!/usr/bin/env python -from __future__ import print_function - -import os -import sys - -# usage: memory.log memory_index.log - -lines = open(sys.argv[1], 'rb').readlines() -index = open(sys.argv[2], 'rb').readlines() - -# logfile format: -# #
-# example: -# #12 38 A 0xd902a0 16 16 0 16 - -allocation_points_to_print = 30 - - -def print_allocation_point(ap): - print('space_time: %d kBms' % (ap['spacetime'] / 1024)) - print('allocations: %d' % ap['allocations']) - print('peak: %d kB' % (ap['peak'] / 1024)) - print('stack: ') - counter = 0 - for e in ap['stack']: - print('#%d %s' % (counter, e)) - counter += 1 - - -allocation_points = [] -for line in index: - line = line.split('#') - line.pop(0) - ap = {'allocations': 0, 'peak': 0, 'spacetime': 0, 'allocation_point': len(allocation_points), 'stack': line} - allocation_points.append(ap) - -for line in lines: - line = line.lstrip('#').rstrip('\n').split(' ') - if len(line) != 8: - print(line) - continue - try: - ap = int(line[0]) - allocation_points[ap]['allocations'] += 1 - allocation_points[ap]['peak'] = int(line[7]) - allocation_points[ap]['spacetime'] = int(line[6]) - except Exception as e: - print(type(e), e, line) - -print('=== space time ===') - -hot_ap = [] -allocation_points.sort(key=lambda x: x['spacetime'], reverse=True) -counter = 0 -for ap in allocation_points[0:allocation_points_to_print]: - print('== %d ==' % counter) - counter += 1 - print_allocation_point(ap) - hot_ap.append(ap['allocation_point']) - -print('=== allocations ===') - -allocation_points.sort(key=lambda x: x['allocations'], reverse=True) -for ap in allocation_points[0:allocation_points_to_print]: - print_allocation_point(ap) - -print('=== peak ===') - -allocation_points.sort(key=lambda x: x['peak'], reverse=True) -for ap in allocation_points[0:allocation_points_to_print]: - print_allocation_point(ap) - -# generate graph -lines = open(sys.argv[1], 'rb').readlines() - -out = open('memory.dat', 'wb') -cur_line = [0] * allocation_points_to_print -prev_line = [0] * allocation_points_to_print -last_time = 0 - -for line in lines: - line = line.lstrip('#').rstrip('\n').split(' ') - if len(line) != 8: - print(line) - continue - try: - time = int(line[1]) - if time != last_time: - print(last_time, '\t', end=' ', file=out) - for i in range(allocation_points_to_print): - if cur_line[i] == -1: - print(prev_line[i], '\t', end=' ', file=out) - else: - print(cur_line[i], '\t', end=' ', file=out) - prev_line[i] = cur_line[i] - print(file=out) - cur_line = [-1] * allocation_points_to_print - last_time = time - - size = int(line[5]) - ap = int(line[0]) - if ap in hot_ap: - index = hot_ap.index(ap) - cur_line[index] = max(cur_line[index], size) - - except Exception as e: - print(type(e), e, line) - -out.close() - -out = open('memory.gnuplot', 'wb') -print("set term png size 1200,700", file=out) -print('set output "memory.png"', file=out) -print('set xrange [0:*]', file=out) -print('set xlabel "time (ms)"', file=out) -print('set ylabel "bytes (B)"', file=out) -print("set style data lines", file=out) -print("set key box", file=out) -print('plot', end=' ', file=out) -for k in range(allocation_points_to_print): - print(' "memory.dat" using 1:(', end=' ', file=out) - for i in range(k, allocation_points_to_print): - if i == k: - print('$%d' % (i + 2), end=' ', file=out) - else: - print('+$%d' % (i + 2), end=' ', file=out) - print(') title "%d" with filledcurves x1, \\' % k, file=out) -print('x=0', file=out) -out.close() - -os.system('gnuplot memory.gnuplot') diff --git a/tools/parse_peer_log.py b/tools/parse_peer_log.py index 5436a7d0d..8462595f2 100755 --- a/tools/parse_peer_log.py +++ b/tools/parse_peer_log.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 from __future__ import print_function diff --git a/tools/parse_sample.py b/tools/parse_sample.py index bb8c03b4d..58666dbf8 100755 --- a/tools/parse_sample.py +++ b/tools/parse_sample.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 import sys diff --git a/tools/parse_session_stats.py b/tools/parse_session_stats.py index de89faa3c..5b403aaa3 100755 --- a/tools/parse_session_stats.py +++ b/tools/parse_session_stats.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 + from __future__ import print_function # Copyright (c) 2016, Arvid Norberg diff --git a/tools/parse_utp_log.py b/tools/parse_utp_log.py index a409e685c..540625bf1 100755 --- a/tools/parse_utp_log.py +++ b/tools/parse_utp_log.py @@ -1,4 +1,6 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 + from __future__ import print_function import os diff --git a/tools/run_benchmark.py b/tools/run_benchmark.py index bbe49effc..02d0e9fc6 100755 --- a/tools/run_benchmark.py +++ b/tools/run_benchmark.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 import os import time diff --git a/tools/set_version.py b/tools/set_version.py index 82f406c85..7accdb069 100755 --- a/tools/set_version.py +++ b/tools/set_version.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 import os import sys diff --git a/tools/update_copyright.py b/tools/update_copyright.py index eb1ba5ed9..4473cc7f7 100755 --- a/tools/update_copyright.py +++ b/tools/update_copyright.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 import glob import datetime