diff --git a/Makefile.am b/Makefile.am index a0ba399..7c5996c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,12 +15,14 @@ sbin_PROGRAMS = iftop iftop_SOURCES = addr_hash.c edline.c hash.c iftop.c ns_hash.c \ options.c resolver.c screenfilter.c serv_hash.c \ sorted_list.c threadprof.c ui.c util.c \ - addrs_ioctl.c addrs_dlpi.c dlcommon.c + addrs_ioctl.c addrs_dlpi.c dlcommon.c \ + stringmap.c cfgfile.c vector.c noinst_HEADERS = addr_hash.h ether.h ethertype.h extract.h hash.h iftop.h \ integers.h ip.h llc.h ns_hash.h options.h resolver.h \ screenfilter.h serv_hash.h sll.h sorted_list.h tcp.h \ - threadprof.h token.h ui.h dlcommon.h + threadprof.h token.h ui.h dlcommon.h stringmap.h \ + vector.h man_MANS = iftop.8 diff --git a/TODO b/TODO index 559d7fe..058ed51 100644 --- a/TODO +++ b/TODO @@ -24,3 +24,6 @@ $Id$ * Linear bar graphs. +* Count obscured connections. + +* Startup warnings cause pause. diff --git a/cfgfile.c b/cfgfile.c new file mode 100644 index 0000000..bb3bf69 --- /dev/null +++ b/cfgfile.c @@ -0,0 +1,251 @@ +/* + * cfgfile.c: + * + * Copyright (c) 2003 DecisionSoft Ltd. + * + */ + +#include +#include + +#include "stringmap.h" +#include "iftop.h" +#include "options.h" +#include "cfgfile.h" + +#define CONFIG_TYPE_STRING 0 +#define CONFIG_TYPE_BOOL 1 +#define CONFIG_TYPE_INT 2 + +#define MAX_CONFIG_LINE 2048 + +typedef struct { + char * name; + int type; +} config_item_type; + +config_item_type config_directives[] = { + { "interface", CONFIG_TYPE_STRING }, + { "dns-resolution", CONFIG_TYPE_BOOL }, + { "port-resolution", CONFIG_TYPE_BOOL }, + { "filter-code", CONFIG_TYPE_STRING }, + { "show-bars", CONFIG_TYPE_BOOL }, + { "promiscuous", CONFIG_TYPE_BOOL }, + { "show-ports", CONFIG_TYPE_INT }, + { "hide-source", CONFIG_TYPE_INT }, + { "hide-destination", CONFIG_TYPE_INT }, + { "use-bytes", CONFIG_TYPE_INT }, + { "sort", CONFIG_TYPE_INT }, + { "line-display", CONFIG_TYPE_INT }, + { "show-totals", CONFIG_TYPE_INT }, + { "log-scale", CONFIG_TYPE_INT }, + { "max-bandwidth", CONFIG_TYPE_INT }, + { "net-filter", CONFIG_TYPE_INT }, + { "port-display", CONFIG_TYPE_INT }, + { NULL, 0} +}; + +stringmap config; + +extern options_t options ; + +int is_cfgdirective_valid(const char *s) { + config_item_type* t; + for (t = config_directives; t->name != NULL; ++t) + if (strcmp(s, t->name) == 0) return 1; + return 0; +} + +int config_init() { + config = stringmap_new(); + return config != NULL; +} + +/* read_config_file: + * Read a configuration file consisting of key: value tuples, returning a + * stringmap of the results. Prints errors to stderr, rather than using + * syslog, since this file is called at program startup. Returns 1 on success + * or 0 on failure. */ +int read_config_file(const char *f) { + int ret = 0; + FILE *fp; + char *line; + int i = 1; + + line = xmalloc(MAX_CONFIG_LINE); + + fp = fopen(f, "rt"); + if (!fp) { + fprintf(stderr, "%s: %s\n", f, strerror(errno)); + goto fail; + } + + while (fgets(line, MAX_CONFIG_LINE, fp)) { + char *key, *value, *r; + + for (r = line + strlen(line) - 1; r > line && *r == '\n'; *(r--) = 0); + + /* Get continuation lines. Ugly. */ + while (*(line + strlen(line) - 1) == '\\') { + if (!fgets(line + strlen(line) - 1, MAX_CONFIG_LINE - strlen(line), fp)) + break; + for (r = line + strlen(line) - 1; r > line && *r == '\n'; *(r--) = 0); + } + + /* Strip comment. */ + key = strpbrk(line, "#\n"); + if (key) *key = 0; + + /* foo : bar baz quux + * key^ ^value */ + key = line + strspn(line, " \t"); + value = strchr(line, ':'); + + if (value) { + /* foo : bar baz quux + * key^ ^r ^value */ + ++value; + + r = key + strcspn(key, " \t:"); + if (r != key) { + item *I; + *r = 0; + + /* foo\0: bar baz quux + * key^ ^value ^r */ + value += strspn(value, " \t"); + r = value + strlen(value) - 1; + while (strchr(" \t", *r) && r > value) --r; + *(r + 1) = 0; + + /* (Removed check for zero length value.) */ + + /* Check that this is a valid key. */ + if (!is_cfgdirective_valid(key)) + fprintf(stderr, "%s:%d: warning: unknown directive \"%s\"\n", f, i, key); + else if ((I = stringmap_insert(config, key, item_ptr(xstrdup(value))))) + /* Don't warn of repeated directives, because they + * may have been specified via the command line + * Previous option takes precedence. + */ + fprintf(stderr, "%s:%d: warning: repeated directive \"%s\"\n", f, i, key); + } + } + + memset(line, 0, MAX_CONFIG_LINE); /* security paranoia */ + + ++i; + } + + ret = 1; + +fail: + if (fp) fclose(fp); + if (line) xfree(line); + + return ret; +} + +int config_get_int(const char *directive, int *value) { + stringmap S; + char *s, *t; + + if (!value) return -1; + + S = stringmap_find(config, directive); + if (!S) return 0; + + s = (char*)S->d.v; + if (!*s) return -1; + errno = 0; + *value = strtol(s, &t, 10); + if (*t) return -1; + + return errno == ERANGE ? -1 : 1; +} + +/* config_get_float: + * Get an integer value from a config string. Returns 1 on success, -1 on + * failure, or 0 if no value was found. */ +int config_get_float(const char *directive, float *value) { + item *I; + char *s, *t; + + if (!value) return -1; + + I = stringmap_find(config, directive); + if (!I) return 0; + + s = (char*)I->v; + if (!*s) return -1; + errno = 0; + *value = strtod(s, &t); + if (*t) return -1; + + return errno == ERANGE ? -1 : 1; +} + +/* config_get_string; + * Get a string value from the config file. Returns NULL if it is not + * present. */ +char *config_get_string(const char *directive) { + stringmap S; + + S = stringmap_find(config, directive); + if (S) return (char*)S->d.v; + else return NULL; +} + +/* config_get_bool: + * Get a boolean value from the config file. Returns false if not present. */ +int config_get_bool(const char *directive) { + char *s; + + s = config_get_string(directive); + if (s && (strcmp(s, "yes") == 0 || strcmp(s, "true") == 0)) + return 1; + else + return 0; +} + +/* config_get_enum: + * Get an enumeration value from the config file. Returns false if not + * present or an invalid value is found. */ +int config_get_enum(const char *directive, config_enumeration_type *enumeration, int *value) { + char *s; + config_enumeration_type *t; + s = config_get_string(directive); + if(s) { + for(t = enumeration; t->name; t++) { + if(strcmp(s,t->name) == 0) { + *value = t->value; + return 1; + } + } + fprintf(stderr,"Invalid enumeration value \"%s\" for directive \"%s\"\n", s, directive); + } + return 0; +} + +/* config_set_string; Sets a value in the config, possibly overriding + * an existing value + */ +void config_set_string(const char *directive, const char* s) { + stringmap S; + + S = stringmap_find(config, directive); + if (S) stringmap_delete_free(S); + stringmap_insert(config, directive, item_ptr(xstrdup(s))); +} + +int read_config(char *file) { + config_item_type* t; + void* o; + + read_config_file(file); + if(config == NULL) { + fprintf(stderr,"Unable to read config file\n"); + return 0; + } + return 1; +} diff --git a/cfgfile.h b/cfgfile.h new file mode 100644 index 0000000..38532ad --- /dev/null +++ b/cfgfile.h @@ -0,0 +1,26 @@ +/* + * cfgfile.h: + * + * Copyright (c) 2003 DecisionSoft Ltd. + * + */ + +#ifndef __CFGFILE_H_ /* include guard */ +#define __CFGFILE_H_ + +typedef struct { + char *name; + int value; +} config_enumeration_type; + +int read_config(); + +char *config_get_string(const char *directive); +int config_get_bool(const char *directive); +int config_get_int(const char *directive, int *value); +int config_get_float(const char *directive, float *value); +int config_init(); + + + +#endif /* __CFGFILE_H_ */ diff --git a/iftop.c b/iftop.c index 6448c8c..0f937e5 100644 --- a/iftop.c +++ b/iftop.c @@ -42,6 +42,7 @@ #include "llc.h" #include "extract.h" #include "ethertype.h" +#include "cfgfile.h" /* ethernet address of interface. */ @@ -517,7 +518,12 @@ int main(int argc, char **argv) { pthread_t thread; struct sigaction sa = {}; - options_read(argc, argv); + /* read command line options and config file */ + config_init(); + options_set_defaults(); + options_read_args(argc, argv); + read_config(options.config_file); + options_make(); sa.sa_handler = finish; sigaction(SIGINT, &sa, NULL); diff --git a/options.c b/options.c index 0d9f8ac..9e63202 100644 --- a/options.c +++ b/options.c @@ -21,6 +21,7 @@ #include "iftop.h" #include "options.h" +#include "cfgfile.h" #if !defined(HAVE_INET_ATON) && defined(HAVE_INET_PTON) # define inet_aton(a, b) inet_pton(AF_INET, (a), (b)) @@ -39,14 +40,39 @@ char optstr[] = "+i:f:nN:hpbBPm:"; * likely to want to listen. We also compare candidate interfaces to lo. */ static char *bad_interface_names[] = { "lo:", - "lo", - "stf", /* pseudo-device 6to4 tunnel interface */ - "gif", /* psuedo-device generic tunnel interface */ + "lo", + "stf", /* pseudo-device 6to4 tunnel interface */ + "gif", /* psuedo-device generic tunnel interface */ "dummy", "vmnet", NULL /* last entry must be NULL */ }; +config_enumeration_type sort_enumeration[] = { + { "2s", OPTION_SORT_DIV1 }, + { "10", OPTION_SORT_DIV2 }, + { "40", OPTION_SORT_DIV3 }, + { "source", OPTION_SORT_SRC }, + { "destination", OPTION_SORT_SRC }, + { NULL, -1 } +}; + +config_enumeration_type linedisplay_enumeration[] = { + { "two-line", OPTION_LINEDISPLAY_TWO_LINE }, + { "one-line-both", OPTION_LINEDISPLAY_ONE_LINE_BOTH }, + { "one-line-sent", OPTION_LINEDISPLAY_ONE_LINE_SENT }, + { "one-line-received", OPTION_LINEDISPLAY_ONE_LINE_RECV }, + { NULL, -1 } +}; + +config_enumeration_type showports_enumeration[] = { + { "off", OPTION_PORTS_OFF }, + { "source-only", OPTION_PORTS_SRC }, + { "destination-only", OPTION_PORTS_DEST }, + { "on", OPTION_PORTS_ON }, + { NULL, -1 } +}; + static int is_bad_interface_name(char *i) { char **p; for (p = bad_interface_names; *p; ++p) @@ -65,7 +91,7 @@ static char *get_first_interface(void) { nameindex = if_nameindex(); if(nameindex == NULL) { - return NULL; + return NULL; } while(nameindex[j].if_index != 0) { @@ -79,7 +105,8 @@ static char *get_first_interface(void) { return i; } -static void set_defaults() { +void options_set_defaults() { + char *s; /* Should go through the list of interfaces, and find the first one which * is up and is not lo or dummy*. */ options.interface = get_first_interface(); @@ -115,6 +142,18 @@ static void set_defaults() { options.max_bandwidth = 0; /* auto */ options.log_scale = 0; options.bar_interval = 1; + + s = getenv("HOME"); + if(s != NULL) { + int i = strlen(s) + 9 + 1; + options.config_file = xmalloc(i); + snprintf(options.config_file,i,"%s/.iftoprc",s); + fprintf(stderr,options.config_file); + } + else { + options.config_file = xstrdup("iftoprc"); + } + } static void die(char *msg) { @@ -216,11 +255,9 @@ static void usage(FILE *fp) { ); } -void options_read(int argc, char **argv) { +void options_read_args(int argc, char **argv) { int opt; - set_defaults(); - opterr = 0; while ((opt = getopt(argc, argv, optstr)) != -1) { switch (opt) { @@ -229,41 +266,39 @@ void options_read(int argc, char **argv) { exit(0); case 'n': - options.dnsresolution = 0; + config_set_string("dns-resolution","true"); break; case 'i': - options.interface = optarg; + config_set_string("interface", optarg); break; case 'f': - options.filtercode = xstrdup(optarg); + config_set_string("filter-code", optarg); break; case 'p': - options.promiscuous = 1; - options.promiscuous_but_choosy = 0; + config_set_string("promiscuous", "true"); break; case 'P': - options.showports = OPTION_PORTS_ON; + config_set_string("port-display", "on"); break; case 'N': - set_net_filter(optarg); + config_set_string("net-filter", optarg); break; case 'm': - set_max_bandwidth(optarg); + config_set_string("max-bandwidth", optarg); break; - case 'b': - options.showbars = 0; + config_set_string("show-bars", "true"); break; case 'B': - options.bandwidth_in_bytes = 1; + config_set_string("use-bytes", "true"); break; case '?': @@ -285,3 +320,168 @@ void options_read(int argc, char **argv) { exit(1); } } + +/* options_config_get_string: + * Gets a value from the config, sets *value to a copy of the value, if + * found. Leaves the option unchanged otherwise. */ +int options_config_get_string(const char *name, char** value) { + char *s; + s = config_get_string(name); + if(s != NULL) { + *value = xstrdup(s); + return 1; + } + return 0; +} + +int options_config_get_bool(const char *name, int* value) { + if(config_get_string(name)) { + *value = config_get_bool(name); + return 1; + } + return 0; +} + +int options_config_get_int(const char *name, int* value) { + if(config_get_string(name)) { + config_get_int(name, value); + return 1; + } + return 0; +} + +int options_config_get_enum(char *name, config_enumeration_type* enumeration, int *result) { + int i; + if(config_get_string(name)) { + if(config_get_enum(name, enumeration, &i)) { + *result = i; + return 1; + } + } + return 0; +} + +int options_config_get_promiscuous() { + if(config_get_string("promiscuous")) { + options.promiscuous = config_get_bool("promiscuous"); + if(options.promiscuous) { + /* User has explicitly requested promiscuous mode, so don't be + * choosy */ + options.promiscuous_but_choosy = 0; + } + return 1; + } + return 0; +} + +int options_config_get_bw_rate(char *directive, long long* result) { + char* units; + long long mult = 1; + long long value; + char *s; + s = config_get_string(directive); + if(s) { + units = s + strspn(s, "0123456789"); + if(strlen(units) > 1) { + fprintf(stderr, "Invalid units in value: %s\n", s); + return 0; + } + if(strlen(units) == 1) { + if(*units == 'k' || *units == 'K') { + mult = 1024; + } + else if(*units == 'm' || *units == 'M') { + mult = 1024 * 1024; + } + else if(*units == 'g' || *units == 'G') { + mult = 1024 * 1024 * 1024; + } + else if(*units == 'b' || *units == 'B') { + /* bits => mult = 1 */ + } + else { + fprintf(stderr, "Invalid units in value: %s\n", s); + return 0; + } + } + *units = '\0'; + if(sscanf(s, "%lld", &value) != 1) { + fprintf(stderr, "Error reading rate: %s\n", s); + } + options.max_bandwidth = value * mult; + return 1; + } + return 0; +} + +/* + * Read the net filter option. + */ +int options_config_get_net_filter() { + char* s; + s = config_get_string("net-filter"); + if(s) { + char* mask; + + mask = strchr(s, '/'); + if (mask == NULL) { + fprintf(stderr, "Could not parse net/mask: %s\n", s); + return 0; + } + *mask = '\0'; + mask++; + if (inet_aton(s, &options.netfilternet) == 0) { + fprintf(stderr, "Invalid network address: %s\n", s); + return 0; + } + /* Accept a netmask like /24 or /255.255.255.0. */ + if (mask[strspn(mask, "0123456789")] == '\0') { + /* Whole string is numeric */ + int n; + n = atoi(mask); + if (n > 32) { + fprintf(stderr, "Invalid netmask: %s\n", s); + } + else { + if(n == 32) { + /* This needs to be special cased, although I don't fully + * understand why -pdw + */ + options.netfiltermask.s_addr = htonl(0xffffffffl); + } + else { + u_int32_t mm = 0xffffffffl; + mm >>= n; + options.netfiltermask.s_addr = htonl(~mm); + } + } + } + else if (inet_aton(mask, &options.netfiltermask) == 0) { + fprintf(stderr, "Invalid netmask: %s\n", s); + } + options.netfilternet.s_addr = options.netfilternet.s_addr & options.netfiltermask.s_addr; + options.netfilter = 1; + return 1; + } + return 0; +} + + +void options_make() { + options_config_get_string("interface", &options.interface); + options_config_get_bool("dns-resolution", &options.dnsresolution); + options_config_get_bool("port-resolution", &options.portresolution); + options_config_get_string("filter-code", &options.filtercode); + options_config_get_bool("show-bars", &options.showbars); + options_config_get_promiscuous(); + options_config_get_bool("hide-source", &options.aggregate_src); + options_config_get_bool("hide-destination", &options.aggregate_dest); + options_config_get_bool("use-bytes", &options.bandwidth_in_bytes); + options_config_get_enum("sort", sort_enumeration, (int*)&options.sort); + options_config_get_enum("line-display", linedisplay_enumeration, (int*)&options.linedisplay); + options_config_get_bool("show-totals", &options.show_totals); + options_config_get_bool("log-scale", &options.log_scale); + options_config_get_bw_rate("max-bandwidth", &options.max_bandwidth); + options_config_get_enum("port-display", showports_enumeration, (int*)&options.showports); + options_config_get_net_filter(); +}; diff --git a/options.h b/options.h index cd3befe..590b650 100644 --- a/options.h +++ b/options.h @@ -33,23 +33,24 @@ typedef enum { OPTION_LINEDISPLAY_ONE_LINE_SENT } option_linedisplay_t; +/* + * This structure has to be defined in the same order as the config + * directives in cfgfile.c. Clearly this is EBW. + */ typedef struct { /* interface on which to listen */ char *interface; + int dnsresolution; + int portresolution; /* pcap filter code */ char *filtercode; - /* Cross network filter */ - int netfilter; - struct in_addr netfilternet; - struct in_addr netfiltermask; - int dnsresolution; - int portresolution; - int promiscuous; - int promiscuous_but_choosy; int showbars; option_port_t showports; + + int promiscuous; + int promiscuous_but_choosy; int aggregate_src; int aggregate_dest; int paused; @@ -71,6 +72,17 @@ typedef struct { long long max_bandwidth; int log_scale; + /* Cross network filter */ + int netfilter; + struct in_addr netfilternet; + struct in_addr netfiltermask; + + char *config_file; + } options_t; + +void options_set_defaults(); +void options_read(int argc, char **argv); + #endif /* __OPTIONS_H_ */ diff --git a/stringmap.c b/stringmap.c new file mode 100644 index 0000000..0712a06 --- /dev/null +++ b/stringmap.c @@ -0,0 +1,111 @@ +/* + * stringmap.c: sucky implementation of binary trees + * + * This makes no attempt to balance the tree, so has bad worst-case behaviour. + * Also, I haven't implemented removal of items from the tree. So sue me. + * + * Copyright (c) 2001 Chris Lightfoot. All rights reserved. + * + */ + +static const char rcsid[] = "$Id$"; + + +#include +#include + +#include "stringmap.h" +#include "vector.h" +/*include "util.h"*/ + +/* stringmap_new: + * Allocate memory for a new stringmap. */ +stringmap stringmap_new() { + stringmap S; + + S = xcalloc(sizeof *S, 1); + + return S; +} + +/* stringmap_delete: + * Free memory for a stringmap. */ +void stringmap_delete(stringmap S) { + if (!S) return; + if (S->l) stringmap_delete(S->l); + if (S->g) stringmap_delete(S->g); + + xfree(S->key); + xfree(S); +} + +/* stringmap_delete_free: + * Free memory for a stringmap, and the objects contained in it, assuming that + * they are pointers to memory allocated by xmalloc(3). */ +void stringmap_delete_free(stringmap S) { + if (!S) return; + if (S->l) stringmap_delete_free(S->l); + if (S->g) stringmap_delete_free(S->g); + + xfree(S->key); + xfree(S->d.v); + xfree(S); +} + +/* stringmap_insert: + * Insert into S an item having key k and value d. Returns an existing key + * or NULL if it was inserted. + */ +item *stringmap_insert(stringmap S, const char *k, const item d) { + if (!S) return 0; + if (S->key == NULL) { + S->key = xstrdup(k); + S->d = d; + return NULL; + } else { + stringmap S2; + for (S2 = S;;) { + int i = strcmp(k, S2->key); + if (i == 0) { + return &(S2->d); + } + else if (i < 0) { + if (S2->l) S2 = S2->l; + else { + if (!(S2->l = stringmap_new())) return NULL; + S2->l->key = xstrdup(k); + S2->l->d = d; + return NULL; + } + } else if (i > 0) { + if (S2->g) S2 = S2->g; + else { + if (!(S2->g = stringmap_new())) return NULL; + S2->g->key = xstrdup(k); + S2->g->d = d; + return NULL; + } + } + } + } +} + +/* stringmap_find: + * Find in d an item having key k in the stringmap S, returning the item found + * on success NULL if no key was found. */ +stringmap stringmap_find(const stringmap S, const char *k) { + stringmap S2; + int i; + if (!S || S->key == NULL) return 0; + for (S2 = S;;) { + i = strcmp(k, S2->key); + if (i == 0) return S2; + else if (i < 0) { + if (S2->l) S2 = S2->l; + else return NULL; + } else if (i > 0) { + if (S2->g) S2 = S2->g; + else return NULL; + } + } +} diff --git a/stringmap.h b/stringmap.h new file mode 100644 index 0000000..f5d0153 --- /dev/null +++ b/stringmap.h @@ -0,0 +1,33 @@ +/* + * stringmap.h: + * map of strings + * + * Copyright (c) 2001 Chris Lightfoot. All rights reserved. + * + * $Id$ + * + */ + +#ifndef __STRINGMAP_H_ /* include guard */ +#define __STRINGMAP_H_ + +#include "vector.h" + +typedef struct _stringmap { + char *key; + item d; + struct _stringmap *l, *g; +} *stringmap; + +stringmap stringmap_new(void); +void stringmap_delete(stringmap); +void stringmap_delete_free(stringmap); + +/* Try to insert an item into a stringmap, returning 1 if the map already + * contained an item with that key. + */ +item *stringmap_insert(stringmap, const char*, const item); +/* Find an item in a stringmap */ +stringmap stringmap_find(const stringmap, const char*); + +#endif /* __STRINGMAP_H_ */ diff --git a/vector.c b/vector.c new file mode 100644 index 0000000..08df5f8 --- /dev/null +++ b/vector.c @@ -0,0 +1,81 @@ +/* + * vector.c: + * simple vectors + * + * Copyright (c) 2001 Chris Lightfoot. All rights reserved. + * + */ + +static const char rcsid[] = "$Id$"; + +#ifdef HAVE_CONFIG_H +/*include "configuration.h"*/ +#endif /* HAVE_CONFIG_H */ + +#include +#include + +#include "vector.h" +/*include "util.h"*/ + +vector vector_new(void) { + vector v; + + v = xcalloc(1, sizeof *v); + if (!v) return NULL; + + v->ary = xcalloc(16, sizeof *v->ary); + v->n = 16; + v->n_used = 0; + return v; +} + +void vector_delete(vector v) { + xfree(v->ary); + xfree(v); +} + +void vector_delete_free(vector v) { + item *i; + vector_iterate(v, i) { + xfree(i->v); + } + xfree(v->ary); + xfree(v); +} + +void vector_push_back(vector v, const item t) { + if (v->n_used == v->n) vector_reallocate(v, v->n * 2); + v->ary[v->n_used++] = t; +} + +void vector_pop_back(vector v) { + if (v->n_used > 0) { + --v->n_used; + if (v->n_used < v->n / 2) vector_reallocate(v, v->n / 2); + } +} + +item vector_back(vector v) { + return v->ary[v->n_used - 1]; +} + +item *vector_remove(vector v, item *t) { + if (t >= v->ary + v->n_used) return NULL; + if (t < v->ary + v->n_used - 1) + memmove(t, t + 1, (v->n_used - (t - v->ary)) * sizeof(item)); + memset(v->ary + v->n_used--, 0, sizeof(item)); + if (v->n_used < v->n / 2 && v->n > 16) { + size_t i = t - v->ary; + vector_reallocate(v, v->n / 2); + t = v->ary + i; + } + return t; +} + +void vector_reallocate(vector v, const size_t n) { + if (n < v->n_used || n <= 0) return; + v->ary = xrealloc(v->ary, n * sizeof(item)); + memset(v->ary + v->n_used, 0, (v->n - v->n_used) * sizeof(item)); + v->n = n; +} diff --git a/vector.h b/vector.h new file mode 100644 index 0000000..9caf9d8 --- /dev/null +++ b/vector.h @@ -0,0 +1,44 @@ +/* + * vector.h: + * simple vectors + * + * Copyright (c) 2001 Chris Lightfoot. All rights reserved. + * + * $Id$ + * + */ + +#ifndef __VECTOR_H_ /* include guard */ +#define __VECTOR_H_ + +typedef union _item { + void *v; + long l; +} item; + +#define _inline inline + +static _inline item item_long(const long l) { item u; u.l = l; return u; } +static _inline item item_ptr(void *const v) { item u; u.v = v; return u; } + +typedef struct _vector{ + item *ary; + size_t n, n_used; +} *vector; + +vector vector_new(void); +void vector_delete(vector); +void vector_delete_free(vector); + +void vector_push_back(vector, const item); +void vector_pop_back(vector); +item vector_back(const vector); + +item *vector_remove(vector, item *t); + +void vector_reallocate(vector, const size_t n); + +/* A macro to iterate over a vector */ +#define vector_iterate(_v, _t) for ((_t) = (_v)->ary; (_t) < (_v)->ary + (_v)->n_used; ++(_t)) + +#endif /* __VECTOR_H_ */