Anonymous avatar Anonymous committed 9c67ad4

initial commit

Comments (0)

Files changed (20)

+#!/bin/sh
+
+# bfc - brainfuck compiler
+# usage: bfc file.b [cc options]
+# notes: mktemp -d because --suffix isn't portable
+#        seems that gnu sed doesn't get rid of multibyte characters with that first line
+#        1s/// and $s/// instead of 1i and $a as some seds expect \<newline> following i and a
+#        could add #include <stdio.h> but gcc doesn't seem to mind...
+#        int main instead of void main so we get an exit status of 0
+
+d="$(mktemp -d)" || exit 1
+c="$d/tmp.c"
+sed '
+s/[^][><.,+-]//g
+s/[+-]/&&*p;/g
+s/>/++p;/g
+s/</--p;/g
+s/\./putchar(*p);/g
+s/,/*p=getchar();/g
+s/\[/while(*p){/g
+s/]/}/g
+1s/^/int main(){char a[10000]={},*p=a;/
+$s/$/return 0;}/
+/^$/d
+' "$1" > "$c"
+shift
+cc "$c" "$@"
+e=$?
+rm -r "$d"
+exit "$e"

crypt/ciphersaber.c

+#include <stdlib.h>
+#include <unistd.h>
+
+/*
+ * n defaults to 1 if not specified (cs1)
+ * set n to 20 for default cs2
+ * recommended n > 1000000
+ *
+ * to decode:  ./cs "key" [n] < encrypted | dd bs=1 skip=10 of=plaintext
+ * to encode:  { dd bs=1 count=10 if=/dev/random; cat plaintext; } | ./cs "key" [n] > encrypted
+ */
+
+#define SWAP(x, y, t) do { (t) = (x); (x) = (y); (y) = (t); } while (0)
+
+int main(int argc, char **argv)
+{
+	unsigned char t, i = 0, j = 0, k[256], s[256];
+	int c, n = 1;
+
+	if      (argc == 3) n = atoi(argv[2]);
+	else if (argc != 2) return 1;
+
+	for (c = 0; (k[c] = argv[1][c]); c++)
+		;
+
+	if (read (0, k + c, 10) != 10) return 1;
+	if (write(1, k + c, 10) != 10) return 1;
+	c += 10;
+
+	do
+		s[i] = i;
+	while(++i);
+
+	do {
+		j += s[i] + k[i % c];
+		SWAP(s[i], s[j], t);
+	} while (++i || --n);
+
+	for (j = 0; read(0, &c, 1); write(1, &c, 1)) {
+		j += s[++i];
+		SWAP(s[i], s[j], t);
+		c ^= s[0xff & (s[i] + s[j])];
+	}
+
+	return 0;
+}
+# rc4
+#
+# supply key as only argument as hex digits
+# supply plaintext on stdin
+# read ciphertext from stdout
+#
+# Example from wikipedia, key is Key, output as hex to compare
+# https://en.wikipedia.org/wiki/Rc4#Test_vectors
+#
+# $ printf Plaintext | rc4 "$(bin_to_hex Key)" | bin_to_hex
+# bbf3e840af0ad3
+# 
+
+bin_to_hex() {
+	local str c
+
+	if (($#)); then
+		for str; do
+			printf %s "$str" |
+			while IFS= read -r -d '' -n 1 c; do
+				LC_CTYPE=C printf %02x "'$c"
+			done
+		done
+	else
+		while IFS= read -r -d '' -n 1 c; do
+			LC_CTYPE=C printf %02x "'$c"
+		done
+	fi
+}
+
+hex_to_bin() {
+	local str x
+
+	if (($#)); then
+		for str; do
+			printf %s "$str" |
+			while IFS= read -r -d '' -n 2 x; do
+				printf %b "\x$x"
+			done
+		done
+	else
+		while IFS= read -r -d '' -n 2 x; do
+			printf %b "\x$x"
+		done
+	fi
+}
+
+rc4() {
+	local c i j k s t x
+
+	while IFS= read -r -d '' -n 2 x; do
+		[[ $x == [[:xdigit:]][[:xdigit:]] ]] || break
+		k+=( $((0x$x)) )
+	done <<< "$1"
+
+	for ((i = 0; i < 256; i++)); do
+		((s[i] = i))
+	done
+
+	for ((j = i = 0; i < 256; i++)); do
+		((j += s[i] + k[i % ${#k[@]}], j &= 0xff,
+		  t = s[i], s[i] = s[j], s[j] = t))
+	done
+
+	((i = j = 0))
+	while IFS= read -r -d '' -n 1 c; do
+		LC_CTYPE=C printf -v c "%d" "'$c"
+		((i++      , i &= 0xff,
+		  j += s[i], j &= 0xff,
+		  t = s[i], s[i] = s[j], s[j] = t,
+		  k = s[(s[i] + s[j]) & 0xff],
+		  c ^= k))
+		printf %b "$(printf '\\x%x' "$c")"
+	done
+}
+#include <stdio.h>
+#include <unistd.h>
+
+/*
+ * rc4 encryption
+ *
+ * supply  hex key as only argument
+ * supply  plaintext  or ciphertext on stdin
+ * receive ciphertext or plaintext  on stdout respectively
+ */
+
+#define SWAP(x, y, t) do { (t) = (x); (x) = (y); (y) = (t); } while (0)
+
+int main(int argc, char **argv)
+{
+	unsigned char t, i = 0, j = 0, k[256], s[256], *h = k;
+	int c;
+
+	if (argc != 2)
+		return 1;
+
+	for (argv++; **argv && sscanf(*argv, "%2hhx", h); *argv += 2, h++)
+		;
+
+	if (**argv)
+		return 1;
+
+	do
+		s[i] = i;
+	while (++i);
+
+	do {
+		j += s[i] + k[i % (h - k)];
+		SWAP(s[i], s[j], t);
+	} while (++i);
+
+	for (j = 0; read(0, &c, 1); write(1, &c, 1)) {
+		j += s[++i];
+		SWAP(s[i], s[j], t);
+		c ^= s[0xff & (s[i] + s[j])];
+	}
+
+	return 0;
+}
+/*
+ * mkfifo f1 f2 f3 f4
+ * ./distribute f1 f2 f3 f4 < file
+ *
+ * distribute lines from file to named fifos when they are available to write
+ */
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/select.h>
+
+int main(int argc, char ** argv)
+{
+	FILE *fifos[argc - 1];
+	fd_set writefds;
+	char buf[128];
+	int i, nfds, fds[argc - 1];
+
+	for (i = nfds = 0; i < argc - 1; i++) {
+		fifos[i] = fopen(argv[i + 1], "w");
+		if (!fifos[i])
+			err(EXIT_FAILURE, "failed to open %s", argv[i + 1]);
+		fds[i] = fileno(fifos[i]);
+		if (fds[i] > nfds)
+			nfds = fds[i];
+	}
+	nfds++;
+
+	for (;;) {
+		FD_ZERO(&writefds);
+
+		for (i = 0; i < argc - 1; i++)
+			FD_SET(fds[i], &writefds);
+
+		if (select(nfds, NULL, &writefds, NULL, NULL) < 0)
+			err(EXIT_FAILURE, "select() failed");
+
+		for (i = 0; i < argc - 1; i++) {
+			if (FD_ISSET(fds[i], &writefds)) {
+				if (!fgets(buf, sizeof(buf), stdin))
+					goto cleanup;
+				fputs(buf, fifos[i]);
+				fflush(fifos[i]);
+			}
+		}
+	}
+
+cleanup:
+	for (i = 0; i < argc - 1; i++)
+		fclose(fifos[i]);
+
+	return 0;
+}

empty_line_every_20

+add a blank line after every 20 lines
+
+sed
+===
+sed ':a;$!{N;ba;};s/\([^\n]*\n\)\{20\}/&\n/g'
+sed '1~20a\\'    # GNUism
+
+awk
+===
+awk '1;!(NR%20){printf("\n")}'
+
+sh
+==
+while IFS= read -r line; do printf "%s\n" "$line"; [ "$((++i%20))" -eq 0 ] && echo; done
+
+C
+=
+#include <stdio.h>
+int main(void) { int c, i = 0; while ((c = getchar()) != EOF) { if (c == '\n' && !(++i % 20)) putchar(c); putchar(c); } return 0; }
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+/*
+ * ./exec echo foo bar
+ * runs the given command, sets real userid to effective userid
+ * if setuid bit is set, run commands as another user
+ */
+
+int main(int argc, char **argv)
+{
+	pid_t pid;
+
+	if ((pid = fork())) {
+		int status;
+		waitpid(pid, &status, 0);
+		return WEXITSTATUS(status);
+	} else {
+		char *args[argc];
+		int err;
+
+		if (setuid(geteuid()))
+			perror("setuid failed");
+
+		args[argc - 1] = NULL;
+		while (argc--)
+			args[argc] = argv[argc + 1];
+
+		execvp(args[0], args);
+		err = errno;
+		perror("exec failed");
+		return err;
+	}
+}
+/*
+ * mkfifo f1 f2 f3 f4
+ * ./gather f1 f2 f3 f4
+ *
+ * print lines from named fifos as they are available to read
+ */
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sys/select.h>
+
+int main(int argc, char **argv)
+{
+	FILE *fifos[argc - 1];
+	fd_set readfds;
+	char buf[128];
+	int i, f, nfds, fds[argc - 1];
+
+	for (i = nfds = 0; i < argc - 1; i++) {
+		fifos[i] = fopen(argv[i + 1], "r");
+		if (!fifos[i])
+			err(EXIT_FAILURE, "failed to open %s", argv[i + 1]);
+		fds[i] = fileno(fifos[i]);
+		if (fds[i] > nfds)
+			nfds = fds[i];
+	}
+	nfds++;
+
+	for (;;) {
+		FD_ZERO(&readfds);
+
+		for (i = f = 0; i < argc - 1; i++) {
+			if (fifos[i]) {
+				FD_SET(fds[i], &readfds);
+				f = 1;
+			}
+		}
+		if (!f)
+			break;
+
+		if (select(nfds, &readfds, NULL, NULL, NULL) < 0)
+			err(EXIT_FAILURE, "select() failed");
+
+		for (i = 0; i < argc - 1; i++) {
+			if (FD_ISSET(fds[i], &readfds)) {
+				if (!fgets(buf, sizeof(buf), fifos[i])) {
+					fclose(fifos[i]);
+					fifos[i] = NULL;
+				} else {
+					printf("%s %s", argv[i + 1], buf);
+					fflush(stdout);
+				}
+			}
+		}
+	}
+
+	return 0;
+}
+/*
+ * ./hilight_match "$(tput setaf 1)" "$(tput sgr0)" foobar
+ *
+ * hilight longest match of foobar at beginning of lines
+ * hilight by printing first argument, end by printing second
+ */
+#include <stdio.h>
+
+int main(int argc, char **argv)
+{
+	char *beg_color, *end_color, *query, *q;
+	int  c, on = 1;
+
+	if (argc != 4) return 1;
+
+	beg_color = argv[1];
+	end_color = argv[2];
+	query = q = argv[3];
+
+	while ((c = getc(stdin)) != EOF) {
+		switch (on) {
+			case 1: if (c != *q) { on = 0; break;                               }
+			        else         { on = 2; fputs(beg_color, stdout);            }
+			case 2: if (c != *q) { on = 0; fputs(end_color, stdout); q = query; }
+			        else         { q++;                                         }
+		}
+		if (c == '\n') on = 1;
+		putc(c, stdout);
+	}
+
+	return 0;
+}

offsets/offsets.awk

+# offsets.awk
+#
+# given a list of word offset pairs, accumulate offsets and print
+# word [offset,...] pairs
+# use with offsets.c
+# no good way to shebang awk, so just do awk -f manually
+
+{
+	a[$1] = a[$1] (a[$1] ? "," : "") $2;
+}
+END {
+	for (w in a)
+		printf("%s [%s]\n", w, a[w]);
+}

offsets/offsets.bash

+#!/usr/bin/env bash
+ 
+# 
+# offsets.bash
+# 
+# for each word (grouping of alphanumeric characters) print the word and the
+# byte offset into the file
+# combine with offsets.awk to get a listing of word [offset,...] pairs
+# 
+
+unset word off
+while IFS= read -r -d '' -n 1 c; do
+	if [[ $c == [[:alnum:]] ]]; then
+		word+=$c
+	elif [[ $word ]]; then
+		printf "%s %zu\n" "$word" "$((off - ${#word}))"
+		unset word
+	fi
+	((off++))
+done

offsets/offsets.c

+/*
+ * offsets.c
+ *
+ * for each word (grouping of alphanumeric characters) print the word and the
+ * byte offset into the file
+ * combine with offsets.awk to get a listing of word [offset,...] pairs
+ */
+
+#include <ctype.h>
+#include <stdio.h>
+
+int main(void)
+{
+	size_t off;
+	char c, buf[256], *p = buf;
+
+	for (off = 0; (c = getchar()) != EOF; off++) {
+		if (isalnum(c)) {
+			*p++ = c;
+		} else if (p != buf) {
+			*p = '\0';
+			printf("%s %zu\n", buf, off - (p - buf));
+			p = buf;
+		}
+	}
+	return 0;
+}

rpn_spreadsheet.c

+/******************************************************************************
+ * Evan Gates                                                                 *
+ * 2012/12/29                                                                 *
+ * rpn_spreadsheet.c                                                          *
+ *                                                                            *
+ * PURPOSE: this program reads in a spreadsheet of positive integers, cell    *
+ *          references, and postfix expressions, then outputs the resulting   *
+ *          calculations as a spreadsheet of doubles.                         *
+ * USAGE  : compile with gcc, supply spreadsheet on stdin                     *
+ * EXAMPLE: gcc -o rpn rpn_spreadsheet.c                                      *
+ *          ./rpn < test1.in                                                  *
+ * NOTES  : tested with gcc 4.7.2 and glibc 2.17                              *
+ *                                                                            *
+ * Spreadsheet Format                                                         *
+ * ------------------                                                         *
+ * A spreadsheet consists of a two-dimensional array of cells, labeled A1, A2,*
+ * A3, ... Each cell contains either a number (its value) or an expression.   *
+ * Expressions contain integers, cell references, and the operators '+', '-', *
+ * '*', '/'. There are a maximum of 26 rows. Expressions are given in Reverse *
+ * Polish Notation.                                                           *
+ *                                                                            *
+ * The spreadsheet is represented in a plain text file as follows:            *
+ *   - Line 1: two integers, seperated by white space, defining the number of *
+ *     columns and rows respectively (n, m)                                   *
+ *   - n*m lines each containing an expression which is the value of the      *
+ *     corresponding cell (cells enumerated in the order A1, A2, ... , A<n>,  *
+ *     B1, B2, ... , B<n>, ...)                                               *
+ *                                                                            *
+ * Example                                                                    *
+ * -------                                                                    *
+ * The following spreadsheet:                                                 *
+ *                                                                            *
+ *             1           2           3                                      *
+ *        -----------------------------------                                 *
+ *     A |    A2     |   4 5 *   | 1 2 3 + / |                                *
+ *        -----------------------------------                                 *
+ *     B |  A1 B2 /  |     3     | 4 7 - 3 * |                                *
+ *        -----------------------------------                                 *
+ *                                                                            *
+ * Is represented as:                                                         *
+ * 3 2                                                                        *
+ * A2                                                                         *
+ * 4 5 *                                                                      *
+ * 1 2 3 + /                                                                  *
+ * A1 B2 /                                                                    *
+ * 3                                                                          *
+ * 4 7 - 3 *                                                                  *
+ *                                                                            *
+ * And results in:                                                            *
+ * 3 2                                                                        *
+ * 20.000000                                                                  *
+ * 20.000000                                                                  *
+ * 0.200000                                                                   *
+ * 6.666667                                                                   *
+ * 3.000000                                                                   *
+ * -9.000000                                                                  *
+ ******************************************************************************/
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define MAX_ROWS 26
+
+#define die(fmt, args...) ({                                                                                  \
+	fprintf(stderr, "%s: " fmt "%s%s\n", prog_name, ##args, errno ? ": " : "", errno ? strerror(errno) : ""); \
+	cleanup();                                                                                                \
+	exit(EXIT_FAILURE);                                                                                       \
+})
+
+struct stack {
+	double *nums;
+	int    size;
+	int    depth;
+};
+
+char   *prog_name = NULL;
+
+char  **sheet[MAX_ROWS] = {}; // read lines   in
+double *out  [MAX_ROWS] = {}; // spit doubles out
+
+int     rows = 0, cols = 0;   // size of spreadsheet
+int     cycle;                // boolean to find cyclic dependencies
+int     cur_row, cur_col;
+
+// free individual cells, then rows
+void cleanup(void)
+{
+	int i, j;
+
+	for (i = 0; i < rows; i++) {
+		for (j = 0; j < cols; j++)
+			free(sheet[i][j]);
+		free(sheet[i]);
+		free(out  [i]);
+	}
+}
+
+// NOTE: used explicitly (outside of push and pop) to free stacks in case of fatal errors
+void resize(struct stack *s, int size)
+{
+	double *tmp;
+
+	if (s->nums == NULL && size == 0) {
+		return;                          // called from a fatal error, but stack is already empty
+	} else if ((tmp = realloc(s->nums, size * sizeof(double))) == NULL && size != 0) {
+		int e = errno;
+		free(s->nums);                   // "if realloc() fails the original block is left untouched; it is not freed or moved" from malloc(3)
+		errno = e;
+		die("failed to realloc stack");
+	} else {
+		s->nums = size ? tmp : NULL;     // "if size was equal to 0, either NULL or a pointer suitable to be passed to free() is returned" from malloc(3)
+		s->size = size;
+	}
+}
+
+// double size when full
+void push(struct stack *s, double d)
+{
+	if (s->depth == s->size)
+		resize(s, s->size ? s->size * 2 : 1);
+
+	s->nums[s->depth++] = d;
+}
+
+// halve size when 3/4 empty (free when empty)
+double pop(struct stack *s)
+{
+	double ret = s->nums[--s->depth];
+
+	if (s->depth <= s->size / 4)
+		resize(s, s->depth * 2);
+
+	return (ret);
+}
+
+// recurse on references, push numbers on stack, pop and operate
+// return 0 on success, 1 on cycle, 2 on out of bounds, 3 on malformed token
+// NOTE: beware, there are multiple return points
+// (for a function this small I prefer that to gotos for errors and cleanup)
+enum err_codes {success, err_cycle, err_oobounds, err_badcell};
+
+int eval_cell(int row, int col)
+{
+	struct stack s = {};
+	int i, j, err;
+	char *tok, *saveptr;
+
+	if (sheet[row][col] == NULL) // already evaluated due to reference
+		return (success);
+
+	if (row == cur_row && col == cur_col && cycle++)
+		return (err_cycle);
+
+	for (tok = strtok_r(sheet[row][col], " \t\n", &saveptr); tok; tok = strtok_r(NULL, " \t\n", &saveptr)) {
+		switch (*tok) {
+			// reference. translate to row col, recurse, push result
+			case 'a' ... 'z':
+				*tok = toupper(*tok);
+			case 'A' ... 'Z':
+				i = *tok - 'A';
+				j = atoi(tok + 1) - 1;
+				if (i < 0 || i >= rows || j < 0 || j >= cols) {
+					cur_row = row;
+					cur_col = col;
+					resize(&s, 0);
+					return (err_oobounds);
+				}
+				if ((err = eval_cell(i, j)) != success) {
+					resize(&s, 0);
+					return (err);
+				}
+				push(&s, out[i][j]);
+				break;
+			// number. push
+			case '0' ... '9':
+				push(&s, strtod(tok, NULL));
+				break;
+			// operator. pop 2, operate, push result
+			case '+': push(&s,     pop(&s) + pop(&s)); break;
+			case '-': push(&s, 0.0-pop(&s) + pop(&s)); break;
+			case '*': push(&s,     pop(&s) * pop(&s)); break;
+			case '/': push(&s, 1.0/pop(&s) * pop(&s)); break;
+			// for debugging purposes
+			default :
+				cur_row = row;
+				cur_col = col;
+				resize(&s, 0);
+				return (err_badcell);
+		}
+	}
+
+	out[row][col] = pop(&s);
+	free(sheet[row][col]);
+	sheet[row][col] = NULL;
+	return (success);
+}
+
+int main(int argc, char **argv)
+{
+	int i, j;
+	size_t n; // unused, needed for getline(3)
+
+	prog_name = argv[0];
+
+	if (argc != 1)
+		die("Do not use arguments. Supply input on stdin.");
+
+	if (fscanf(stdin, "%d %d\n", &cols, &rows) != 2) // specification says width then height, n*m, A<n>
+		die("failed to read size of spreadsheet");
+
+	if (rows > MAX_ROWS)
+		die("too many rows (maximum %d rows)", MAX_ROWS);
+
+	for (i = 0; i < rows; i++) {
+		if ((sheet[i] = calloc(cols, sizeof(char *))) == NULL) die("failed to allocate spreadsheet");
+		if ((out  [i] = calloc(cols, sizeof(double))) == NULL) die("failed to allocate spreadsheet");
+	}
+
+	// read the spreadsheet
+	for (i = 0; i < rows; i++)
+		for (j = 0; j < cols; j++)
+			if (getline(&sheet[i][j], &n, stdin) < 0)
+				die("failed to read cell %c%d", 'A' + i, j + 1);
+
+	// evaluate the spreadsheet
+	for (cur_row = 0; cur_row < rows; cur_row++) {
+		for (cur_col = 0; cur_col < cols; cur_col++) {
+			cycle = 0;
+			switch (eval_cell(cur_row, cur_col)) {
+				case err_cycle   : die("cycle detected starting in %c%d", 'A' + cur_row, cur_col + 1); break;
+				case err_oobounds: die("out of bounds reference in %c%d", 'A' + cur_row, cur_col + 1); break;
+				case err_badcell : die("unsupported tokens in cell %c%d", 'A' + cur_row, cur_col + 1); break;
+			}
+		}
+	}
+
+	// print the results
+	printf("%d %d\n", cols, rows);
+
+	for (i = 0; i < rows; i++)
+		for (j = 0; j < cols; j++)
+			printf("%f\n", out[i][j]);
+
+	cleanup();
+
+	return (0);
+}
+main;
+print_array()
+{
+	declare -n _arr=$1
+	printf "["
+	printf "%s," "${_arr[@]:0:${#_arr[@]}-1}"
+	printf "%s]\n" "${_arr[@]: -1}"
+}
+
+swap_in_array()
+{
+	declare -n _arr=$1
+	declare -i _a=$2 _b=$3
+	declare _t
+
+	_t=${_arr[$_a]}
+	_arr[$_a]=${_arr[$_b]}
+	_arr[$_b]=$_t
+}
+
+shuffle_array()
+{
+	declare -n _arr=$1
+
+	declare -i _i
+
+	for ((_i = ${#_arr[@]} - 1; _i > 0; _i--)); do
+		swap_in_array "$1" $((_i)) $((RANDOM % (_i + 1)))
+	done
+}
+
+str_cmp()
+{
+	declare -i a b
+	[[ $1 > $2 ]] && a=1
+	[[ $2 > $1 ]] && b=1
+	printf "%d" $((a - b))
+}
+
+int_cmp()
+{
+	printf "%d" $((($1 > $2) - ($2 > $1)))
+}
+
+compar()
+{
+	if declare -f "$cmp_func" >/dev/null 2>&1; then
+		"$cmp_func" "$@"
+	else
+		str_cmp "$@"
+	fi
+}
+
+is_sorted()
+{
+	declare -n _arr=$1
+
+	for ((i = 0; i < ${#_arr[@]} - 1; i++)); do
+		(("$(compar "${_arr[$i]}" "${_arr[$((i + 1))]}")" <= 0)) || return 1
+	done
+
+	return 0
+}
+
+is_heap()
+{
+	declare -n _arr=$1
+
+	for ((i = 1; i < ${#_arr[@]}; i++)); do
+		(("$(compar "${_arr[i]}" "${_arr[$(((i - 1) / 2))]}")" <= 0)) || return 1
+	done
+
+	return 0
+}
+
+bogo_sort()
+{
+	while ! is_sorted "$1"; do
+		shuffle_array "$1"
+	done
+}
+
+bubble_sort()
+{
+	declare -n _arr=$1
+
+	declare -i _left _right
+
+	for ((_right = ${#_arr[@]} - 1; _right > 0; _right--)); do
+		for ((_left = 0; _left < _right; _left++)); do
+			if (("$(compar "${_arr[$_left]}" "${_arr[$_right]}")" > 0)); then
+				swap_in_array "$1" $((_left)) $((_right))
+			fi
+		done
+	done
+}
+
+select_sort()
+{
+	declare -n _arr=$1
+
+	declare -i _left _right _min
+
+	for ((_left = 0; _left < ${#_arr[@]}; _left++)); do
+		for ((_min = _right = _left; _right < ${#_arr[@]}; _right++)); do
+			if (("$(compar "${_arr[$_right]}" "${_arr[$_min]}")" < 0)); then
+				((_min = _right))
+			fi
+		done
+		swap_in_array "$1" $((_min)) $((_left))
+	done
+}
+
+sift_down()
+{
+	declare -n _arr=$1
+	declare -i _beg=$2 _end=$3
+
+	declare -i _child _swap
+
+	for ((; _beg * 2 + 1 <= _end; _beg = _child)); do
+		((_child = _beg * 2 + 1))
+		if ((_child + 1 <= _end && "$(compar "${_arr[$_child]}" "${_arr[$((_child + 1))]}")" < 0)); then
+			((_child++))
+		fi
+		if (("$(compar "${_arr[$_child]}" "${_arr[$_beg]}")" < 0)); then
+			return
+		fi
+		swap_in_array "$1" $((_child)) $((_beg))
+	done
+}
+
+sift_up()
+{
+	declare -n _arr=$1
+	declare -i _beg=$2 _end=$3
+
+	declare -i _child _parent
+
+	for ((_child = _end; _child > _beg; _child = _parent)); do
+		((_parent = (_child - 1) / 2))
+		if (("$(compar "${_arr[$_parent]}" "${_arr[$_child]}")" < 0)); then
+			swap_in_array "$1" $((_parent)) $((_child))
+		else
+			break
+		fi
+	done
+}
+
+heapify()
+{
+	declare -n _arr=$1
+
+	declare -i _count=${#_arr[@]} _beg
+
+	for ((_beg = (_count - 2) / 2; _beg >= 0; _beg--)); do
+		sift_down "$1" $_beg $((_count - 1))
+	done
+}
+
+heap_sort()
+{
+	declare -n _arr=$1
+
+	declare -i _count=${#_arr[@]} _end
+
+	heapify "$1"
+
+	for ((_end = _count - 1; _end > 0;)); do
+		swap_in_array "$1" $((_end)) $((0))
+		sift_down "$1" $((0)) $((--_end))
+	done
+}
+
+partition()
+{
+	declare -n _arr=$1
+	declare -i _left=$2 _right=$3
+
+	declare -i _i
+
+	for ((_i = _left; _i < _right; _i++)); do
+		if (("$(compar "${_arr[$_i]}" "${_arr[$_right]}")" < 0)); then
+			swap_in_array "$1" $((_i)) $((_left++))
+		fi
+	done
+	swap_in_array "$1" $((_left)) $((_right))
+	((_piv = _left))
+}
+
+quick_sort()
+{
+	declare -n _arr=$1
+	declare -i _left _right
+
+	if (($# == 1)); then
+		((_left = 0, _right = ${#_arr[@]} - 1))
+	else
+		((_left = $1, _right = $2))
+	fi
+
+	if ((_right - _left < 2)); then
+		return
+	fi
+
+	declare -i _piv # set in partition()
+
+	partition "$1" $((_left)) $((_right))
+
+	qsort "$1" $((_left)) $((_piv - 1))
+	qsort "$1" $((_piv + 1)) $((_right))
+}
+/*
+ * In a recent interview I was asked to write a function to sort an array on the whiteboard. I
+ * wrote a simple compare function, included stdlib.h, and called qsort(3). I was told that didn't
+ * count and I had to manually sort the array. Seeing as it had been several years since I had
+ * done that, it was more challenging than it should have been. After the interview I decided to
+ * brush up on my sorting algorithms, which lead to this.
+ *
+ * The following sorting algorithms are not optimized. They are short and simple, meant purely
+ * to refresh my memory and be easy to understand. There are helper functions along the way that
+ * take arguments that may not be used, but I have left those in place so I can go back later
+ * and add error checking/asserts if I so wish.
+ *
+ * Enjoy.
+ */
+
+#include <limits.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+/*
+ * not safe, but saves declaration and assignment, and I'm using it correctly...for now
+ * (4 days from now "ow, what's this hole in my foot?")
+ */
+#define MIN(x, y) ((x) <= (y) ? (x) : (y))
+//#define MIN(x, y) ({ typeof(x) _x = (x), _y = (y); _x <= _y ? _x : _y; })
+
+/*
+ * swap two chunks of memory of size 'size' pointed to by 'x' and 'y' one 'type' at a time
+ * based on glibc's SWAP for qsort
+ * NOTES: I hope/assume the compiler changes the division to a shift because sizeof(type)
+ *        should always be a power of 2. How can I go from sizeof() to log2(sizeof()) so I
+ *        can make it a shift anyway?
+ *        I also assume here that the single divide/shift up front with decrements in the
+ *        loop is faster than no divide/shift and doing 'size -= sizeof()' in the loop
+ * TODO:  Figure out why glibc uses do while instead of while. Performance? Style?
+ */
+#define SWAP(type, x, y, size) ({                  \
+    register size_t _size = (size) / sizeof(type); \
+    register type *_x = (type *)(x);               \
+    register type *_y = (type *)(y);               \
+    while (_size-- > 0) {                          \
+        type _t = *_x;                             \
+        *_x++ = *_y;                               \
+        *_y++ = _t;                                \
+    }                                              \
+})
+
+#define SWAP_BYTEWISE(x, y, size) SWAP(char     , x, y, size)
+#define SWAP_WORDWISE(x, y, size) SWAP(uintptr_t, x, y, size)
+
+/*
+ * find nearest word aligned address, and how far we are from it
+ * do this both in the forward and backward directions
+ * NOTE: assuming uintptr_t is word sized. Is this a good assumption?
+ */
+#define ALIGN(x)  ((uintptr_t)((x) + sizeof(uintptr_t) - 1) & ~(sizeof(uintptr_t) - 1))
+#define OFFSET(x) (ALIGN(x) - (uintptr_t)(x))
+
+#define ALIGN_BACK(x)  ((uintptr_t)(x) & ~(sizeof(uintptr_t) - 1))
+#define OFFSET_BACK(x) ((uintptr_t)(x) - (ALIGN_BACK(x)))
+
+/*
+ * perform the swaps a word at a time when possible, taking care of leading and trailing bytes
+ * when is it better to just use a big temporary buffer and memcpy around for the swap?
+ * NOTES: I felt it was too big to be a macro, but is called often enough that it should be inline,
+ *        is that a good idea or a bad idea? In its current form, memswap causes my quicksort to be
+ *        faster than glibc's qsort under specific circumstances. e.g. large array of unsigned long long
+ *        and compiled with -O3 (very specific circumstances...).
+ * TODO:  figure out if it's better to have an 'else' clause, or to set x, y, and size to leftovers
+ *        and have a bytewise swap outside the if that takes care of innequal offsets and leftovers.
+ *        Compile glibc's qsort with their SWAP() and this memswap and compare
+ *        Figure out if this is faster due to word boundaries, or just because it's swaping 8 times as
+ *        much every time
+ *        If the offsets are not equal, could I still do this by reading/masking/writing out of phase?
+ */
+inline void memswap(char *x, char *y, size_t size)
+{
+	size_t xoff = OFFSET(x);
+
+	if (xoff == OFFSET(y)) { // if x and y have the same offset from word alignment
+		SWAP_BYTEWISE(x                   , y                   , xoff                 );
+		SWAP_WORDWISE(x + xoff            , y + xoff            , size - xoff          );
+		SWAP_BYTEWISE(ALIGN_BACK(x + size), ALIGN_BACK(y + size), OFFSET_BACK(x + size));
+	} else {
+		SWAP_BYTEWISE(x, y, size);
+	}
+}
+
+/*
+ * macro to define comparison functions for native types
+ */
+#define CMP_FUNC(name, type)            \
+int name(const void *a, const void *b)  \
+{                                       \
+    const type *_a = (const type *)a;   \
+    const type *_b = (const type *)b;   \
+    return ((*_a > *_b) - (*_b > *_a)); \
+}
+
+CMP_FUNC( char_cmp, char              )
+CMP_FUNC(short_cmp, short             )
+CMP_FUNC(  int_cmp, int               )
+CMP_FUNC(  ull_cmp, unsigned long long)
+
+/*
+ * macro to define print functions for native types
+ */
+#define PRINT_FUNC(name, type, conv) void name(const void *a) { printf("%" conv " ", *(type*)a); }
+
+PRINT_FUNC(print_char , char              , "hhd")
+PRINT_FUNC(print_short, short             , "hd" )
+PRINT_FUNC(print_int  , int               , "d"  )
+PRINT_FUNC(print_ull  , unsigned long long, "llu")
+
+/*
+ * print an array using 'print' on each element
+ */
+void print_array(void *base, size_t nmemb, size_t size, void (*print)(const void *))
+{
+	char *p, *end;
+
+	for(p = (char*)base, end = p + nmemb * size; p < end; p += size)
+		print((void*)p);
+}
+
+/*
+ * shuffle an arary
+ */
+void shuffle(void *base, size_t nmemb, size_t size)
+{
+	size_t i;
+	char *pbase = (char *) base;
+
+	for (i = nmemb - 1; i > 0; i--)
+		memswap(pbase + i * size, pbase + (random() % (i + 1)) * size, size);
+}
+
+/*
+ * check if an array is sorted
+ */
+int is_sorted(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	char *p = (char *)base, *end = p + nmemb * size;
+
+	for (p += size; p < end && compar((void*)p - size, (void*)p) <= 0; p += size)
+		;
+
+	return (p == end);
+}
+
+/*
+ * check if an array is a heap using 'compar' to compare elements
+ */
+int is_heap(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	char *pbase = (char *)base;
+	size_t i;
+
+	for (i = 1; i < nmemb && compar((void*)(pbase + i * size), (void*)(pbase + (i - 1) / 2 * size)) <= 0; i++)
+		;
+
+	return (i == nmemb);
+}
+
+/*
+ * sorting algorithms
+ */
+
+/*
+ * bubble sort: let's make some computer scientists cry
+ * repeatedly step through array, swapping adjacent elements if they are out of order
+ * after each pass one more element at the end of the array is quaranteed to be in place
+ */
+void bubble_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	char *left, *right, *pbase = (char *)base;
+
+	for (right = pbase + nmemb * size - size; right > pbase; right -= size)
+		for (left = pbase; left < right; left += size)
+			if (compar((void*)left, (void*)right) > 0)
+				memswap(left, right, size);
+}
+
+/*
+ * insertion sort: for each element (starting with the second), copy it, move backwards
+ * through the array shifting up each element we pass and then insert the current element
+ * into the correct place
+ */
+void insert_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	char *p, *ins, tmp[size], *pbase = (char *)base, *end = pbase + size * nmemb;
+
+	for (p = pbase + size; p < end; p += size) {
+		memcpy(tmp, p, size);
+		for (ins = p - size; ins >= pbase && compar((void*)ins, (void*)tmp) > 0; ins -= size)
+			memcpy(ins + size, ins, size);
+		memcpy(ins + size, tmp, size);
+	}
+}
+
+/*
+ * selection sort: loop through the array to find the minimum element, then swap it with the
+ * leftmost unsorted element.
+ */
+void select_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	char *left, *right, *min, *pbase = (char *)base, *end = pbase + size * nmemb;
+
+	for (left = pbase; left < end; left += size) {
+		for (min = right = left; right < end; right += size)
+			if (compar((void*)right, (void*)min) < 0)
+				min = right;
+		memswap(min, left, size);
+	}
+}
+
+/*
+ * bogo sort: this one's worse than bubble sort. More of a joke, it was too easy to leave out.
+ * while the array isn't sorted, shuffle and try again
+ */
+void bogo_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	while(!is_sorted(base, nmemb, size, compar))
+		shuffle(base, nmemb, size);
+}
+
+/*
+ * sift down: move an element down a heap until it is correctly placed
+ */
+void sift_down(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *), size_t top, size_t bottom)
+{
+	char *pbase = (char *)base;
+	size_t child;
+
+	for (; top * 2 + 1 <= bottom; top = child) {
+		child = top * 2 + 1;
+		if (child + 1 <= bottom && compar((void*)(pbase + child * size), (void*)(pbase + (child + 1) * size)) < 0)
+			child++;
+		if (compar((void*)(pbase + child * size), (void*)(pbase + top * size)) < 0)
+			return;
+		memswap(pbase + child * size, pbase + top * size, size);
+	}
+}
+
+/*
+ * sift_up: move an element up a heap until it is correctly placed
+ */
+void sift_up(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *), size_t top, size_t bottom)
+{
+	char *pbase = (char *)base;
+	size_t child, parent;
+
+	for (child = bottom; child > top; child = parent) {
+		parent = (child - 1) / 2;
+		if (compar((void*)(pbase + parent * size), (void*)(pbase + child * size)) < 0)
+			memswap(pbase + parent * size, pbase + child * size, size);
+		else
+			return;
+	}
+}
+
+/*
+ * heapify: given an array, shift elements around to satisfy the heap condition
+ * NOTE: it's possible to do this with sift_up or sift_down. According to wikipedia (and I think
+ *       I understand) sift_down gives O(n) time and sift_up gives O(nlogn) time.
+ */
+void heapify(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	/*
+	size_t bottom;
+
+	for (bottom = 1; bottom < nmemb; bottom++)
+		sift_up(base, nmemb, size, compar, 0, bottom);
+	*/
+	int top;
+
+	for (top = (nmemb - 2) / 2; top >= 0; top--)
+		sift_down(base, nmemb, size, compar, top, nmemb - 1);
+}
+
+/*
+ * heap sort: heapify the array, then repeatedly swap the max element (array[0] due to the heap)
+ * into the correct place at the end of the unsorted array, and restore the heap condition by
+ * sifting down the element now in array[0]
+ */
+void heap_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	char *pbase = (char *)base;
+	int bottom;
+
+	heapify(base, nmemb, size, compar);
+
+	for (bottom = nmemb - 1; bottom > 0; sift_down(base, nmemb, size, compar, 0, --bottom))
+		memswap(pbase, pbase + bottom * size, size);
+}
+
+/*
+ * merge: two sorted lists, both stored in 'base', starting at indices 'left' and 'right, will be
+ * merged in sorted order and placed into 'merged' starting at index 'left'
+ */
+void merge(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *), size_t left, size_t right, size_t end, void *merged)
+{
+	char *pbase = (char *)base;
+	size_t m, i = left, j = right; // index into merged, left, and right respectively
+
+	for (m = left; m < end; m++)
+		if (i < right && (j >= end || compar((void*)(pbase + i * size), (void*)(pbase + j * size)) <= 0))
+			memcpy(merged + m * size, pbase + i++ * size, size);
+		else
+			memcpy(merged + m * size, pbase + j++ * size, size);
+}
+
+/*
+ * merge sort: starting with n lists of size 1, lists are merged in pairs, halving the number of
+ * lists and doubling the size. A bottom up approach to merge sort that avoids recursion.
+ * NOTE: A buffer of the same size as the array being sorted must be supplied. This is used to merge
+ *       into each time.
+ *       Interesting tidbit, this merge_sort uses the exact same number of compares as glibc's qsort
+ */
+void merge_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *), void *buf)
+{
+	char *pbase = (char *)base, *pbuf = (char *)buf;
+	size_t width, i, nswaps = 0;
+
+	for (width = 1; width < nmemb; width *= 2) {
+		for (i = 0; i < nmemb; i += 2 * width)
+			merge(pbase, nmemb, size, compar, i, MIN(i + width, nmemb), MIN(i + 2 * width, nmemb), pbuf);
+		memswap((char*)&pbase, (char*)&pbuf, sizeof(pbase));
+		nswaps = !nswaps;
+	}
+	if (nswaps) // final list is in buf, copy it to base
+		memcpy(base, buf, nmemb * size);
+}
+
+/*
+ * partition: partitions an array for use with quicksort, using the last element as the pivot.
+ * all elements less than and greater than the pivot come before and after the pivot respectively
+ * return a pointer to the pivot
+ */
+char *partition(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *), char *low, char *high)
+{
+	char *p;
+
+	for (p = low; p < high; p += size) {
+		if (compar((void*)p, (void*)high) < 0) {
+			memswap(p, low, size);
+			low += size;
+		}
+	}
+	memswap(low, high, size);
+	return low;
+}
+
+/*
+ * these macros are taken from glibc's qsort.c
+ * they implement a fast and simple stack for use with quicksort in order to avoid recursion
+ * as long as the smaller of the two partitions is sorted first, the stack will never grow larger
+ * than log(n). for 32 bit machines this is 32 entries (64 on 64 bit machines). instead of calculating
+ * just use that size, comes out to 256 bytes on 32 bit machines and 1024 on 64 bit
+ * NOTE: these aren't pretty, and aren't very safe, don't use them outside of quick_sort please
+ */
+#define STACK_SIZE (CHAR_BIT * sizeof(size_t))
+#define PUSH(lo, hi) ((void) ((top->low = (lo)), (top->high = (hi)), top++))
+#define POP(lo, hi)  ((void) (top--, ((lo) = top->low), ((hi) = top->high)))
+#define STACK_NOT_EMPTY (stack < top)
+struct stack_node {
+	char *low, *high;
+};
+
+/*
+ * quick sort: parition the array, push the larger partition, push the smaller partition, repeat
+ */
+void quick_sort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *))
+{
+	struct stack_node stack[STACK_SIZE];
+	struct stack_node *top = stack;
+	char *low, *mid, *high, *pbase = (char *)base;
+
+	PUSH(pbase, pbase + (nmemb - 1) * size);
+
+	while(STACK_NOT_EMPTY) {
+		POP(low, high);
+
+		mid = partition(base, nmemb, size, compar, low, high);
+
+		if (mid - low > high - mid) {
+			if (mid - size > low)  PUSH(low, mid - size);
+			if (mid + size < high) PUSH(mid + size, high);
+		} else {
+			if (mid + size < high) PUSH(mid + size, high);
+			if (mid - size > low)  PUSH(low, mid - size);
+		}
+	}
+}
+
+
+/*
+ * easily change the data type I'm testing with
+ */
+void no_print(const void *a) {}
+#define TYPE  unsigned long long
+#define CMP   ull_cmp
+#define PRINT no_print
+
+/*
+ * test all the sorting algorithms. on data sets large enough to see the difference between the fast algorithms
+ * the slow ones just take way too long.
+ *
+ * TODO: test with big structures, something multiple words in length, and compare different swap techniques
+ */
+int main()
+{
+	int i, len = 1024 * 1024 * 2;
+	TYPE *a, *b; // b is used as a buffer for merge sort
+
+	if ((a = malloc(len * sizeof(TYPE))) == NULL) { printf("failed to malloc\n"); return 1; }
+	if ((b = malloc(len * sizeof(TYPE))) == NULL) { printf("failed to malloc\n"); return 1; }
+
+	/*
+	// test unaligned pointers with same offset
+	a = (TYPE *)((char *)a + 1);
+	b = (TYPE *)((char *)b + 1);
+
+	len--;
+	*/
+
+	/*
+	// test unaligned pointers with different offset
+	a = (TYPE *)((char *)a + 1);
+	b = (TYPE *)((char *)b + 2);
+
+	len--;
+	*/
+
+	// fill array, with some duplicates
+	for (i = 0; i < len; i++)
+		a[i] = (random() % 2) ? i : i + 1;
+
+	printf("qsort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	qsort      ((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("merge sort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	merge_sort ((void*)a, len, sizeof(*a), CMP, (void*)b); print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("quick sort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	quick_sort ((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("heapify\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	heapify    ((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_heap  ((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("heap sort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	heap_sort  ((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("select sort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	select_sort((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("insert sort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	insert_sort((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("bubble sort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	bubble_sort((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	printf("bogo sort\n");
+	shuffle    ((void*)a, len, sizeof(*a));                print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	bogo_sort  ((void*)a, len, sizeof(*a), CMP);           print_array((void*)a, len, sizeof(*a), PRINT); printf("\n");
+	printf("%s\n\n", is_sorted((void*)a, len, sizeof(*a), CMP) ? "worked" : "failed");
+
+	return 0;
+}
+#include <czmq.h>
+#include <stdlib.h>
+#include <zmq.h>
+
+#include "zerr.h"
+
+/*
+ * read message, switch first two frames (from and to), send it back out
+ * arg is the socket to which the message should be sent. For the front_end arg
+ * is back_end, and vice versa.
+ *
+ * NOTE: If a zmsg_push() succeeds, the frame is owned by the message, and will
+ *       be destroyed along with the message on zmsg_send().
+ *       If a zmsg_push() fails, we still own the frame and must destroy it
+ *       I asked on the zmq mailing list about this, and will submit a patch
+ *       so these calls nullify the reference.
+ * TODO: Log errors to syslog, log EHOSTUNREACH errors once per unreachable
+ *       host until reconnect.
+ *       Find out if any of these errors should be fatal.
+ */
+int msg_handler(zloop_t *loop, zmq_pollitem_t *item, void *arg)
+{
+	zmsg_t   *msg = NULL;
+	zframe_t *src = NULL;
+	zframe_t *dst = NULL;
+	void     *out = arg;
+
+	zcheck_goto(cleanup, msg  = zmsg_recv(item->socket), "zmsg_recv failed"                            );
+	zcheck_goto(cleanup,   2 <= zmsg_size( msg)        , "message too small (need 2 address frames)"   );
+	zcheck_goto(cleanup, src  = zmsg_pop ( msg)        , "zmsg_pop failed to pop source address"       );
+	zcheck_goto(cleanup, dst  = zmsg_pop ( msg)        , "zmsg_pop failed to pop destination address"  );
+	zcheck_goto(cleanup,   0 == zmsg_push( msg, src)   , "zmsg_push failed to push source address"     );
+	src = NULL;
+	zcheck_goto(cleanup,   0 == zmsg_push( msg, dst)   , "zmsg_push failed to push destination address");
+	dst = NULL;
+	zcheck_goto(cleanup,   0 == zmsg_send(&msg, out)   , "zmsg_send failed"                            );
+
+	return (0);
+
+cleanup:
+	if (src) { zframe_print(src, "source addr:"); zframe_destroy(&src); }
+	if (dst) { zframe_print(dst, "dest   addr:"); zframe_destroy(&dst); }
+	if (msg) { zmsg_dump(msg)                   ; zmsg_destroy  (&msg); }
+	return (0); // returning -1 causes the zloop to exit. we want to complain, but continue running
+}
+
+int main(int argc, char **argv)
+{
+	zctx_t         *ctx        = NULL;
+	void           *front_end  = NULL;
+	void           *back_end   = NULL;
+	zloop_t        *loop       = NULL;
+	zmq_pollitem_t  front_poll = {};
+	zmq_pollitem_t  back_poll  = {};
+	int             status     = EXIT_FAILURE;
+
+	zcheck_err(EXIT_FAILURE, argc == 4, "Usage: %s <protocol://front_address> <protocol://back_address> <broker_id>", argv[0]);
+
+	zcheck_goto(cleanup, ctx       = zctx_new()                  , "zctx_new failed to create zeromq context"     );
+	zcheck_goto(cleanup, front_end = zsocket_new(ctx, ZMQ_ROUTER), "zsocket_new failed to create front_end socket");
+	zcheck_goto(cleanup, back_end  = zsocket_new(ctx, ZMQ_ROUTER), "zsocket_new failed to create back_end socket" );
+	zcheck_goto(cleanup, loop      = zloop_new()                 , "zloop_new failed"                             );
+
+	zsocket_set_router_mandatory(front_end, 1);
+	zsocket_set_router_mandatory(back_end , 1);
+	zsocket_set_identity(front_end, argv[3]);
+	zsocket_set_identity(back_end , argv[3]);
+
+	front_poll = (zmq_pollitem_t){ .socket = front_end, .fd = 0, .events = ZMQ_POLLIN, .revents = 0 };
+	back_poll  = (zmq_pollitem_t){ .socket = back_end , .fd = 0, .events = ZMQ_POLLIN, .revents = 0 };
+
+	//TODO: use zeromq's perf tools to test IPC vs TCP for back_end
+	zcheck_goto(cleanup, 0 <= zsocket_bind(front_end, argv[1])                       , "zsocket_bind failed to bind front_end socket"     );
+	zcheck_goto(cleanup, 0 <= zsocket_bind(back_end , argv[2])                       , "zsocket_bind failed to bind back_end socket"      );
+	zcheck_goto(cleanup, 0 == zloop_poller(loop, &front_poll, msg_handler, back_end ), "zloop_poller failed to register front_end handler");
+	zcheck_goto(cleanup, 0 == zloop_poller(loop, &back_poll , msg_handler, front_end), "zloop_poller failed to register back_end handler" );
+
+	status = (zloop_start(loop) ? EXIT_FAILURE : EXIT_SUCCESS);
+
+cleanup:
+	if (loop) zloop_destroy(&loop);
+	if (ctx ) zctx_destroy(&ctx);
+	return (status);
+}

zmq/czmq_router_echo.c

+#include <czmq.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <zmq.h>
+
+#define zcheck_err(eval, condition, fmt, args...)                                                                     \
+((void)({                                                                                                             \
+    errno = 0;                                                                                                        \
+    if (!(condition)) {                                                                                               \
+        fprintf(stderr, "%s: " fmt "%s%s\n", prog_name, ##args, errno ? ": " : "", errno ? zmq_strerror(errno) : ""); \
+        exit(eval);                                                                                                   \
+    }                                                                                                                 \
+}))
+
+char *prog_name;
+
+int echo(zloop_t *loop, zmq_pollitem_t *item, void *arg)
+{
+	zmsg_t    *msg;
+	static int con = 0;
+
+	zcheck_err(EXIT_FAILURE, msg  = zmsg_recv(item->socket)      , "Error receiving message");
+	zcheck_err(EXIT_FAILURE,   0 == zmsg_send(&msg, item->socket), "Error sending message"  );
+	printf("Received: %d ", ++con);
+
+	return 0;
+}
+
+/*
+ * Create ZMQ_ROUTER socket, bind, echo messages.
+ */
+int main(int argc, char **argv)
+{
+	zctx_t         *ctx;
+	void           *soc;
+	zloop_t        *loop;
+	zmq_pollitem_t  poller;
+
+	prog_name = argv[0];
+
+	zcheck_err(EXIT_FAILURE, argc == 2                           , "Supply endpoint as only argument"   );
+	zcheck_err(EXIT_FAILURE, ctx   = zctx_new()                  , "Error creating context"             );
+	zcheck_err(EXIT_FAILURE, soc   = zsocket_new(ctx, ZMQ_ROUTER), "Error creating socket"              );
+	zcheck_err(EXIT_FAILURE,    0 <= zsocket_bind(soc, argv[1])  , "Error binding socket to %s", argv[1]);
+	zcheck_err(EXIT_FAILURE, loop  = zloop_new()                 , "Error creating zloop"               );
+
+	poller = (zmq_pollitem_t){ .socket = soc, .fd = 0, .events = ZMQ_POLLIN, .revents = 0 };
+
+	zcheck_err(EXIT_FAILURE, 0 == zloop_poller(loop, &poller, echo, NULL), "Error registering poller");
+
+	setbuf(stdout, NULL);
+	zloop_start(loop);
+
+	return 0;
+}

zmq/router_echo.c

+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <zmq.h>
+
+#define zcheck_err(eval, condition, fmt, args...)                                                                     \
+((void)({                                                                                                             \
+    errno = 0;                                                                                                        \
+    if (!(condition)) {                                                                                               \
+        fprintf(stderr, "%s: " fmt "%s%s\n", prog_name, ##args, errno ? ": " : "", errno ? zmq_strerror(errno) : ""); \
+        exit(eval);                                                                                                   \
+    }                                                                                                                 \
+}))
+
+char *prog_name;
+
+/*
+ * Create ZMQ_ROUTER socket, bind, echo messages.
+ */
+int main(int argc, char **argv)
+{
+	int       con;
+	void     *ctx, *soc;
+	zmq_msg_t msg;
+
+	prog_name = argv[0];
+
+	zcheck_err(EXIT_FAILURE,   2 == argc                       , "Supply endpoint as only argument"     );
+	zcheck_err(EXIT_FAILURE, ctx  = zmq_ctx_new()              , "Error creating context"               );
+	zcheck_err(EXIT_FAILURE, soc  = zmq_socket(ctx, ZMQ_ROUTER), "Error creating socket"                );
+	zcheck_err(EXIT_FAILURE,   0 == zmq_bind(soc, argv[1])     , "Error binding to endpoint %s", argv[1]);
+	zcheck_err(EXIT_FAILURE,   0 == zmq_msg_init(&msg)         , "Error initialise mesage"              );
+
+	setbuf(stdout, NULL);
+	for (con = 0;;) {
+		int    more;
+		size_t size = sizeof(more);
+		zcheck_err(EXIT_FAILURE, -1 != zmq_recvmsg(soc, &msg, 0)                     , "Error receiving message"       );
+		zcheck_err(EXIT_FAILURE,  0 == zmq_getsockopt(soc, ZMQ_RCVMORE, &more, &size), "Error checking for ZMQ_RCVMORE");
+		zcheck_err(EXIT_FAILURE, -1 != zmq_sendmsg(soc, &msg, more ? ZMQ_SNDMORE : 0), "Error echoing message"         );
+		if (!more)
+			printf("Received: %d ", ++con);
+	}
+
+	return 0;
+}
+#ifndef ZERR_H
+#define ZERR_H
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE // for program_invocation_name
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <zmq.h>
+
+/*
+ * I measured a 5-10% increase in performance with this...
+ */
+#define likely(  x) __builtin_expect ((x), 1)
+#define unlikely(x) __builtin_expect ((x), 0)
+
+/*
+ * our own versions of err(3) and warn(3) because we need to use zmq_strerror(3)
+ * check for errno and decide whether or not to zmq_strerror(3) instead of
+ * having separate errx(3) and warnx(3) variants
+ * NOTE: void cast for fprintf(3) so we don't accidently use the return value anywhere
+ *       exit(3), and goto are already void
+ */
+#ifdef NDEBUG
+#define zwarn(fmt, args...) ((void)0)
+#define CLEAR_ERRNO()       ((void)0)
+#else // NDEBUG
+
+extern char *program_invocation_short_name;
+#define zwarn(fmt, args...) ((void)fprintf(stderr, "%s: " fmt "%s%s\n", program_invocation_short_name, ##args, errno ? ": " : "", errno ? zmq_strerror(errno) : ""))
+#define CLEAR_ERRNO()       ((void)(errno = 0))
+
+#endif // NDEBUG
+
+#define zerr(eval, fmt, args...) ({ zwarn(fmt, ##args); exit(eval); })
+
+#define zcheck_warn(       condition, fmt, args...) ({ CLEAR_ERRNO(); if (unlikely(!(condition)))   zwarn(      fmt, ##args);               })
+#define zcheck_err( eval , condition, fmt, args...) ({ CLEAR_ERRNO(); if (unlikely(!(condition)))   zerr (eval, fmt, ##args);               })
+#define zcheck_goto(label, condition, fmt, args...) ({ CLEAR_ERRNO(); if (unlikely(!(condition))) { zwarn(      fmt, ##args); goto label; } })
+
+#endif // ZERR_H
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.