/* {{{ crt_malloc.c -- CrT's own silly malloc wrappers for debugging. */
/* This file is formatted for use with folding.el for emacs, sort */
/* of an outline-mode for programmers, ftp-able from elisp archives */
/* such as tut.cis.ohio-state.edu (128.146.8.52). If you don't use */
/* folding-mode and/or emacs, you may want to prepare a table of */
/* contents for this file by doing "grep '{{{' thisfile.c". */
/* Can be used to generate malloc statistic listing looking like:
File Line CurBlks CurBytes MaxBlks MaxBytes MxByTime TotBlks TotBytes
------------- ---- ------- -------- ------- -------- -------- ------- --------
db.c 377: 0 0 2 197 06232835 366 7174
db.c 1316: 0 0 1 33 06232836 1 33
db.c 1376: 0 0 1 17 06232900 1 17
db.c 873: 0 0 2554 30648 06232919 4954 59448
db.c 899: 0 0 2554 72068 06232919 4954 131094
compile.c 242: 0 0 50 1297 06232909 517 11317
compile.c 503: 0 0 1 106 06232919 4938 130373
compile.c 542: 0 0 5 41 06232912 12155 67649
compile.c 1768: 0 0 7443 148860 06232924 13703 274060
compile.c 1326: 0 0 181 2172 06232924 342 4104
compile.c 1327: 0 0 181 2438 06232924 342 4357
compile.c 1339: 0 0 5 60 06232923 577 6924
compile.c 847: 0 0 1 97 06232909 1493 28733
edit.c 168: 0 0 1 79 06232908 184 3844
compile.c 575: 0 0 2 177 06232921 795 33994
compile.c 1365: 0 0 2 24 06232914 14 168
compile.c 1378: 0 0 2 24 06232914 15 180
compile.c 220: 0 0 1 49 06232910 612 9619
compile.c 1352: 0 0 4 48 06232911 233 2796
interp.c 155: 0 0 1 432 06232900 11 4752
interface.c 1208: 0 0 1 1024 06232907 4 4096
interp.c 578: 0 0 2 864 06232913 9 3888
compile.c 1532: 0 0 4 38 06232913 6 56
interface.c 1008: 27 2012 27 2012 06233122 79 5060
timequeue.c 83: 1 14 3 42 06232900 4 56
interface.c 961: 1 14 1 14 06232907 1 14
game.c 435: 1 15 1 15 06232835 1 15
reg.c 352: 1 16 1 16 06232835 1 16
game.c 466: 1 16 1 16 06232900 1 16
timequeue.c 82: 1 17 3 25 06232900 4 41
boolexp.c 95: 1 20 1 20 06232907 1 20
interface.c 1007: 35 560 35 560 06233122 87 1392
compile.c 1125: 4 47 4 47 06232914 4 47
compile.c 1123: 4 48 4 48 06232914 4 48
interface.c 940: 1 92 1 92 06232907 1 92
timequeue.c 72: 3 168 3 168 06232900 3 168
compile.c 1138: 22 264 22 264 06232914 22 264
compile.c 1141: 22 340 22 340 06232914 22 340
edit.c 183: 183 1605 183 1605 06232835 183 1605
edit.c 176: 183 3660 183 3660 06232835 183 3660
edit.c 184: 183 5569 183 5569 06232835 183 5569
interp.c 127: 1 13200 1 13200 06232900 1 13200
property.c 124: 1493 30167 1493 30167 06232926 1494 30181
db.c 1256: 5440 38387 5440 38387 06232900 5440 38387
hashtab.c 102: 5711 42144 5761 42602 06232912 6228 46528
stringutil.c 386: 1759 50761 2768 82507 06232924 3667 104609
props.c 154: 1643 56120 1643 56120 06232926 1643 56120
hashtab.c 96: 5711 68532 5761 69132 06232909 6228 74736
db.c 1246: 22861 91484 22861 91484 06232900 22861 91484
compile.c 1721: 16 109752 16 109752 06232924 16 109752
db.c 1129: 56911 793132 56911 793132 06232900 56911 793132
db.c 167: 1 9420800 1 9420800 06232836 1 9420800
Cumulative totals: 102221 10728956 151500 11586028
* First two columns are source file and line number of a [cm]alloc() call;
*
* 'CurBlks' is number of ram blocks currently in existence
* created by that call;
File Line CurBlks CurBytes MaxBlks MaxBytes MxByTime TotBlks TotBytes
* 'CurBytes' is number of ram bytes current in existence
* created by that call;
* 'TotBlks' is total number of ram blocks ever created by that call;
* 'TotBytes' is total number of ram bytes ever allocated by that call.
* 'MaxBytes' is max value 'CurBytes' has ever had for that call.
* 'MxByTime' is time 'MaxBytes' value was recorded, as day:hour:minute.
*
* The code adds 8 bytes overhead per ram block allocated.
*
* In addition, by defining MALLOC_PROFILING_EXTRA to be TRUE, this file
* can be configured to check for heap-trashing bugs, free()s of
* unallocated memory, and so forth. As configured, (CRT_NEW_TO_CHECK
* and CRT_OLD_TO_CHECK both set to 128) this option takes ABOUT 10x
* MORE CPU TIME THAN VANILLA FUZZBALL. You may reduce these numbers
* to reduce the CPU overhead, at the cost of decreased likelihood of
* detecting heap-trashing bugs promptly. This option adds an additional
* 13 bytes of overhead per string allocated.
*
* Together, the above two options can DOUBLE FUZZBALL'S RAM CONSUMPTION.
*/
/*
* History
* \|/
* 94May08CrT: Nasty compile.c bug successfully pinpointed: CD08< (Splat!)
* 94May06CrT: Debug check option. /|\
* 94Feb16CrT: Added Whitefire's (excellent) idea of tracking peak ram usage.
* 93Oct16(?)CrT: Created.
*/
/* }}} */
/* {{{ Header stuff */
/* {{{ #includes */
#include "config.h"
#include "externs.h"
#include "db.h"
#ifdef MALLOC_PROFILING
/* }}} */
/* {{{ #defines you might want to configure. */
/* Select whether to do debug checks also. */
/* These checks eat an additional 8 bytes */
/* per allocated block, but can be some */
/* help in tracking down obscure memory */
/* trashing pointer bugs. */
/* #define MALLOC_PROFILING_EXTRA */
/* When debugging is selected, we check the */
/* last CRT_NEW_TO_CHECK blocks allocated */
/* each time we are called, on the theory */
/* that they are the most likely to have */
/* been trashed by buggy code: */
#ifndef CRT_NEW_TO_CHECK
#define CRT_NEW_TO_CHECK (128) /* Make it a nonzero power of two. */
#endif
/* When debugging is selected we also check */
/* CRT_OLD_TO_CHECK blocks on the used-ram */
/* cycling through all allocated blocks in */
/* time: */
#ifndef CRT_OLD_TO_CHECK
#define CRT_OLD_TO_CHECK (128)
#endif
/* When MALLOC_PROFILING_EXTRA is true, we add the */
/* following value to the end of block, so */
/* we can later check to see if it got */
/* overwritten by something: */
#ifndef CRT_MAGIC
#define CRT_MAGIC ((char)231)
#endif
/* Something we write into the magic byte */
/* of a block just before free()ing it, so */
/* as to maybe diagnose repeated free()s: */
#ifndef CRT_FREE_MAGIC
#define CRT_FREE_MAGIC ((char)~CRT_MAGIC)
#endif
/* }}} */
/* {{{ block_list, a list of all malloc/free/etc calls in host program. */
/* Central data structure: a blocklist with one entry */
/* for each textually distinct [mc]alloc() call: */
struct CrT_block_rec {
const char *file;
int line;
long tot_bytes_alloc;
long tot_allocs_done;
long live_blocks;
long live_bytes;
long max_blocks;
long max_bytes;
time_t max_bytes_time;
struct CrT_block_rec *next;
};
typedef struct CrT_block_rec A_Block;
typedef struct CrT_block_rec *Block;
static Block block_list = NULL;
/* }}} */
/* {{{ Header, a header we add to each block allocated: */
struct CrT_header_rec {
Block b;
size_t size;
#ifdef MALLOC_PROFILING_EXTRA
struct CrT_header_rec *next;
struct CrT_header_rec *prev;
char *end;
#endif
};
typedef struct CrT_header_rec A_Header;
typedef struct CrT_header_rec *Header;
/* }}} */
/* {{{ Globals supporting debug functionality. */
#ifdef MALLOC_PROFILING_EXTRA
static A_Block Root_Owner = {
__FILE__, /* file */
__LINE__, /* line */
0, /* tot_bytes_alloc */
0, /* tot_allocs_done */
0, /* live_blocks */
0, /* live_bytes */
0, /* max_blocks */
0, /* max_bytes */
0, /* max_bytes_time */
NULL /* next */
};
static char root_end = CRT_MAGIC;
static A_Header root = {
&Root_Owner, /* b */
0, /* size */
&root, /* next */
&root, /* prev */
&root_end, /* end */
};
/* Clockhand that we run circularly around */
/* allocated-block list, looking for probs: */
static Header rover = &root;
/* Macro to insert block m into 'root' linklist: */
#undef INSERT
#define INSERT(m) { \
root. next->prev = (m); \
(m) ->next = root.next; \
(m) ->prev = &root; \
root. next = (m); \
}
/* Macro to remove block m from 'root' linklist: */
#undef REMOVE
#define REMOVE(m) { \
(m)->next->prev = (m)->prev; \
(m)->prev->next = (m)->next; \
}
/* An array tracking last CRT_NEW_TO_CHECK blocks touched: */
static Header just_touched[CRT_NEW_TO_CHECK] = {
/* I can't think of an elegant way of generating the */
/* right number of initializers automatically via */
/* cpp :( so: */
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
&root, &root, &root, &root,
};
static int next_touched = 0; /* Always indexes above. */
#endif
/* }}} */
/* {{{ Random internal macro stuff */
/* Undefine the macros which make the rest */
/* of the system call our wrappers instead */
/* of calling malloc &Co directly: */
#undef malloc
#undef calloc
#undef realloc
#undef free
/* Number of bytes of overhead we add to a block: */
#ifdef MALLOC_PROFILING_EXTRA
#define CRT_OVERHEAD_BYTES (sizeof(A_Header) +1)
#else
#define CRT_OVERHEAD_BYTES (sizeof(A_Header) )
#endif
/* }}} */
/* }}} */
/* {{{ sorting -- Some fns to implement a simple block_list merge-sort. */
/* Currently, sorts on live_bytes field. */
/* {{{ block_count -- Count number of blocks in a blocklist. */
static int
block_count(Block b)
{
int i;
for (i = 0; b; b = b->next)
++i;
return i;
}
/* }}} */
/* {{{ blocks_nth -- Return nth block in a blocklist. */
static Block
blocks_nth( /* Return nth block in list, first is #0. */
Block b, int n)
{
int i;
for (i = 0; i < n; b = b->next)
++i;
return b;
}
/* }}} */
/* {{{ blocks_split -- Split blocklist 'b' into two lists, return 2nd. */
static Block
blocks_split(Block b, int n /* Nth block becomes first in 2nd list. */
)
{
Block tail = blocks_nth(b, n - 1);
Block head = tail->next;
tail->next = NULL;
return head;
}
/* }}} */
/* {{{ blocks_merge -- Merge two sorted lists into one sorted list. */
static Block
blocks_merge( /* Merge two sorted lists into one. */
Block b0, Block b1)
{
A_Block head;
Block tail = &head;
while (b0 || b1) {
Block nxt;
if (!b1) {
nxt = b0;
b0 = b0->next;
} else if (!b0) {
nxt = b1;
b1 = b1->next;
} else if (b0->live_bytes < b1->live_bytes) {
nxt = b0;
b0 = b0->next;
} else {
nxt = b1;
b1 = b1->next;
}
tail->next = nxt;
tail = nxt;
}
tail->next = NULL;
return head.next;
}
/* }}} */
/* {{{ blocks_merge -- Sort blocklist on 'live_bytes'. */
static Block
blocks_sort( /* Sort 'b1' on 'live_bytes'. */
Block b1)
{
int len = block_count(b1);
if (len < 2)
return b1;
{
Block b2 = blocks_split(b1, len / 2);
return blocks_merge(blocks_sort(b1), blocks_sort(b2)
);
}
}
/* }}} */
/* }}} */
/* {{{ blocks_alloc -- Alloc block for given file/line. */
static Block
block_alloc(const char *file, int line)
{
Block b = malloc(sizeof(A_Block));
b->file = file;
b->line = line;
b->tot_bytes_alloc = 0;
b->tot_allocs_done = 0;
b->live_blocks = 0;
b->live_bytes = 0;
b->max_blocks = 0;
b->max_bytes = 0;
b->next = block_list;
block_list = b;
return b;
}
/* }}} */
/* {{{ blocks_find -- Find block for file/line, maybe creating. */
static Block
block_find(const char *file, int line)
{
Block b;
for (b = block_list; b; b = b->next) {
if (b->line == line && !strcmp(b->file, file)
) {
return b;
}
}
return block_alloc(file, line);
}
/* }}} */
/* Report generation functions: */
/* {{{ CrT_summarize -- Summarize ram usage statistics. */
/* {{{ CrT_timestr -- Return ascii timestring in static buffer. */
const char *
CrT_timestr(time_t when)
{
static char buf[20];
struct tm *da_time;
#ifndef WIN32
da_time = localtime(&when);
#else
da_time = uw32localtime(&when);
#endif
snprintf(buf, sizeof(buf), "%02d%02d%02d%02d",
da_time->tm_mday, da_time->tm_hour, da_time->tm_min, da_time->tm_sec);
return buf;
}
/* }}} */
static dbref summarize_player;
static void
summarize(void (*fn) (char *)
)
{
Block b;
int sum_blks = 0;
int sum_byts = 0;
int sum_totblks = 0;
int sum_totbyts = 0;
char buf[256];
block_list = blocks_sort(block_list);
#undef X
#define X(t) (*fn)(t);
X(" File Line CurBlks CurBytes MaxBlks MaxBytes MxByTime TotBlks TotBytes");
X("------------- ---- ------- -------- ------- -------- -------- ------- --------")
for (b = block_list; b; b = b->next) {
sum_blks += b->live_blocks;
sum_byts += b->live_bytes;
sum_totblks += b->tot_allocs_done;
sum_totbyts += b->tot_bytes_alloc;
snprintf(buf, sizeof(buf),
"%13s%5d:%7d %8d %7d %8d %8s %7d %8d",
b->file,
b->line,
b->live_blocks,
b->live_bytes,
b->max_blocks,
b->max_bytes,
CrT_timestr(b->max_bytes_time), b->tot_allocs_done, b->tot_bytes_alloc);
X(buf);
}
snprintf(buf, sizeof(buf), " Cumulative totals:%7d %8d %7d %8d",
sum_blks, sum_byts, sum_totblks, sum_totbyts);
X(buf);
}
static void
summarize_notify(char *t)
{
notify(summarize_player, t);
}
void
CrT_summarize(dbref player)
{
summarize_player = player;
summarize(summarize_notify);
}
/* }}} */
/* {{{ CrT_summarize_to_file -- Summarize ram usage in given logfile. */
static FILE *summarize_fd;
static void
summarize_to_file(char *t)
{
fprintf(summarize_fd, "%s\n", t);
}
void
CrT_summarize_to_file(const char *file, const char *comment)
{
if ((summarize_fd = fopen(file, "a")) == 0)
return;
if (comment && *comment) {
fprintf(summarize_fd, "%s\n", comment);
} {
time_t lt = time(NULL);
fprintf(summarize_fd, "%s", ctime(<));
}
summarize(summarize_to_file);
fclose(summarize_fd);
}
/* }}} */
#ifdef MALLOC_PROFILING_EXTRA
/* Debug support functions: */
/* {{{ CrT_check -- abort if we can find any trashed malloc blocks. */
/* {{{ crash -- coredump with diagnostic. */
static void
crash2(char *err)
{
abort();
}
static void
crash(char *err, Header m, const char *file, int line)
{
char buf[256];
snprintf(buf, sizeof(buf), "Err found at %s:%d in block from %s:%d", file, line, m->b->file, m->b->line);
crash2(buf); /* Makes above easy to read from dbx 'where'. */
}
/* }}} */
/* {{{ check_block */
static void
check_block(Header m, const char *file, int line)
{
if (*m->end != CRT_MAGIC)
crash("Bottom overwritten", m, file, line);
if (m->next->prev != m)
crash("next->prev != m", m, file, line);
if (m->prev->next != m)
crash("prev->next != m", m, file, line);
}
/* }}} */
/* {{{ check_old_blocks */
static void
check_old_blocks(const char *file, int line)
{
int i = CRT_OLD_TO_CHECK;
while (i-- > 0) {
check_block(rover, file, line);
rover = rover->next;
}
}
/* }}} */
/* {{{ check_new_blocks */
static void
check_new_blocks(const char *file, int line)
{
int i = CRT_NEW_TO_CHECK;
while (i-- > 0)
check_block(just_touched[i], file, line);
}
/* }}} */
void
CrT_check(const char *file, int line)
{
check_old_blocks(file, line);
check_new_blocks(file, line);
}
/* }}} */
/* {{{ CrT_check_everything -- same, but check entire ram arena. */
int
CrT_check_everything(const char *file, int line)
{
Header m = root.next;
int i = 0;;
for (; m != &root; ++i, m = m->next)
check_block(m, file, line);
return i;
}
/* }}} */
#endif /* CRT_DEUG_ALSO */
/* The actual wrappers for malloc/calloc/realloc/free: */
/* {{{ CrT_malloc */
void *
CrT_malloc(size_t size, const char *file, int line)
{
Block b = block_find(file, line);
Header m = (Header) malloc(size + CRT_OVERHEAD_BYTES);
if (!m) {
fprintf(stderr, "CrT_malloc(): Out of Memory!\n");
abort();
}
m->b = b;
m->size = size;
#ifdef MALLOC_PROFILING_EXTRA
/* Look around for trashed ram blocks: */
CrT_check(file, line);
/* Remember where end of block is: */
m->end = &((char *) m)[size + (CRT_OVERHEAD_BYTES - 1)];
/* Write a sacred value at end of block: */
*m->end = CRT_MAGIC;
/* Thread m into linklist of allocated blocks: */
INSERT(m);
/* Remember we've touched 'm' recently: */
just_touched[++next_touched & (CRT_NEW_TO_CHECK - 1)] = m;
#endif
b->tot_bytes_alloc += size;
b->tot_allocs_done++;
b->live_blocks++;
b->live_bytes += size;
if (b->live_bytes > b->max_bytes) {
b->max_bytes = b->live_bytes;
b->max_bytes_time = time(NULL);
}
if (b->live_blocks > b->max_blocks)
b->max_blocks = b->live_blocks;
return (void *) (m + 1);
}
/* }}} */
/* {{{ CrT_calloc */
void *
CrT_calloc(size_t num, size_t siz, const char *file, int line)
{
size_t size = siz * num;
Block b = block_find(file, line);
Header m = (Header) calloc(size + CRT_OVERHEAD_BYTES, 1);
if (!m) {
fprintf(stderr, "CrT_calloc(): Out of Memory!\n");
abort();
}
m->b = b;
m->size = size;
#ifdef MALLOC_PROFILING_EXTRA
/* Look around for trashed ram blocks: */
CrT_check(file, line);
/* Remember where end of block is: */
m->end = &((char *) m)[size + (CRT_OVERHEAD_BYTES - 1)];
/* Write a sacred value at end of block: */
*m->end = CRT_MAGIC;
/* Thread m into linklist of allocated blocks: */
INSERT(m);
/* Remember we've touched 'm' recently: */
just_touched[++next_touched & (CRT_NEW_TO_CHECK - 1)] = m;
#endif
b->tot_bytes_alloc += size;
b->tot_allocs_done++;
b->live_blocks++;
b->live_bytes += size;
if (b->live_bytes > b->max_bytes) {
b->max_bytes = b->live_bytes;
b->max_bytes_time = time(NULL);
}
if (b->live_blocks > b->max_blocks)
b->max_blocks = b->live_blocks;
return (void *) (m + 1);
}
/* }}} */
/* {{{ CrT_realloc */
void *
CrT_realloc(void *p, size_t size, const char *file, int line)
{
Header m = ((Header) p) - 1;
Block b = m->b;
#ifdef MALLOC_PROFILING_EXTRA
/* Look around for trashed ram blocks: */
check_block(m, __FILE__, __LINE__);
CrT_check(file, line);
/* Don't clobber 'rover': */
if (rover == m) {
rover = rover->next;
}
/* Remove m from linklist of allocated blocks: */
REMOVE(m);
/* Remove m from just_touched[], if present: */
{
int i = CRT_NEW_TO_CHECK;
while (i-- > 0) {
if (just_touched[i] == m) {
just_touched[i] = &root;
}
}
}
#endif
m = (Header) realloc(m, size + CRT_OVERHEAD_BYTES);
if (!m) {
fprintf(stderr, "CrT_realloc(): Out of Memory!\n");
abort();
}
b->live_bytes -= m->size;
b->live_bytes += size;
m->size = size;
#ifdef MALLOC_PROFILING_EXTRA
/* Remember where end of block is: */
m->end = &((char *) m)[size + (CRT_OVERHEAD_BYTES - 1)];
/* Write a sacred value at end of block: */
*m->end = CRT_MAGIC;
/* Thread m into linklist of allocated blocks: */
INSERT(m);
/* Remember we've touched 'm' recently: */
just_touched[++next_touched & (CRT_NEW_TO_CHECK - 1)] = m;
#endif
if (b->live_bytes > b->max_bytes) {
b->max_bytes = b->live_bytes;
b->max_bytes_time = time(NULL);
}
return (void *) (m + 1);
}
/* }}} */
/* {{{ CrT_free */
void
CrT_free(void *p, const char *file, int line)
{
Header m = ((Header) p) - 1;
Block b = m->b;
#ifdef MALLOC_PROFILING_EXTRA
/* Look around for trashed ram blocks: */
if (*m->end == CRT_FREE_MAGIC)
crash("Duplicate free()", m, file, line);
check_block(m, __FILE__, __LINE__);
CrT_check(file, line);
/* Don't clobber 'rover': */
if (rover == m) {
rover = rover->next;
}
/* Remove m from linklist of allocated blocks: */
REMOVE(m);
/* Remove m from just_touched[], if present: */
{
int i = CRT_NEW_TO_CHECK;
while (i-- > 0) {
if (just_touched[i] == m) {
just_touched[i] = &root;
}
}
}
/* Mark m so as to give some chance of */
/* diagnosing repeat free()s of same */
/* block: */
*m->end = CRT_FREE_MAGIC;
#endif
b->live_bytes -= m->size;
b->live_blocks--;
free(m);
}
/* }}} */
/* Some convenience functions: */
/* {{{ CrT_alloc_string */
char *
CrT_alloc_string(const char *string, const char *file, int line)
{
char *s;
/* NULL, "" -> NULL */
if (!string || !*string)
return 0;
if ((s = (char *) CrT_malloc(strlen(string) + 1, file, line)) == 0) {
abort();
}
strcpy(s, string);
return s;
}
/* }}} */
/* {{{ CrT_alloc_prog_string */
struct shared_string *
CrT_alloc_prog_string(const char *s, const char *file, int line)
{
struct shared_string *ss;
int length;
if (s == NULL || *s == '\0')
return (NULL);
length = strlen(s);
if ((ss = (struct shared_string *)
CrT_malloc(sizeof(struct shared_string) + length, file, line)) == NULL)
abort();
ss->links = 1;
ss->length = length;
bcopy(s, ss->data, ss->length + 1);
return (ss);
}
/* }}} */
/* {{{ CrT_string_dup */
char *
CrT_string_dup(const char *s, const char *file, int line)
{
char *p;
p = (char *) CrT_malloc(1 + strlen(s), file, line);
if (p)
strcpy(p, s);
return (p);
}
/* }}} */
#endif /* MALLOC_PROFILING */
/* {{{ File variables */
/*
Local variables:
case-fold-search: nil
folded-file: t
fold-fold-on-startup: nil
End:
*/
/* }}} */