diff --git a/src/common/csv.c b/src/common/csv.c
new file mode 100644
--- /dev/null
+++ b/src/common/csv.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright 2010 Nathan Phillip Brink
+ *
+ * This file is a part of DistRen.
+ *
+ * DistRen is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * DistRen is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with DistRen. If not, see .
+ */
+
+#include "common/config.h"
+
+#include "common/csv.h"
+
+#include
+#include
+#include
+#include
+
+/**
+ * \todo
+ * replace with a more generic function for rendering a CSV file
+ */
+static int csv_print_row(size_t *columns, char *row[])
+{
+ size_t c;
+
+ for (c = 0; c < *columns; c ++)
+ {
+ printf("%s", row[c]);
+ if (c < *columns - 1)
+ printf(", ");
+ }
+ printf("\n");
+
+ return TRUE;
+}
+
+csv_row_t csv_row_init(size_t columns)
+{
+ csv_row_t row;
+
+ row = malloc(sizeof(struct csv_row));
+ if (!row)
+ return NULL;
+
+ row->cols = malloc(sizeof(char *) * columns);
+ if (!row->cols)
+ {
+ free(row);
+ return NULL;
+ }
+ row->num_cols = columns;
+
+ return row;
+}
+
+void csv_row_free(csv_row_t row)
+{
+ size_t c;
+
+ for (c = 0; c < row->num_cols; c ++)
+ free(row->cols[c]);
+ free(row);
+}
+
+list_t csv_parse(const char *inbuf, size_t columns)
+{
+ list_t rows;
+ size_t seek;
+ const char newline = '\n';
+ csv_row_t row;
+
+ size_t cur_col;
+
+ rows = list_init();
+ while(*inbuf)
+ {
+ row = csv_row_init(columns);
+ if (!row)
+ {
+ list_free(rows, (list_dealloc_func_t)&csv_row_free);
+ return NULL;
+ }
+
+ for(cur_col = 0; cur_col < columns; cur_col ++)
+ {
+ for(seek = 0; inbuf[seek]; seek ++)
+ if (inbuf[seek] == newline
+ || inbuf[seek] == ',')
+ break;
+ row->cols[cur_col] = malloc(seek + 1);
+ if (seek)
+ memcpy(row->cols[cur_col], inbuf, seek);
+ row->cols[cur_col][seek] = '\0';
+
+ /* get us to the ',' (or NULL or newline char, if things are bad) */
+ inbuf += seek;
+ /* skip a comma, but not a NULL char or newline */
+ if (*inbuf == ',')
+ inbuf ++;
+ }
+
+ list_insert_after(rows, row, 0);
+
+ /* skip the newline char */
+ while (*inbuf && *inbuf != newline)
+ inbuf ++;
+ if (*inbuf == newline)
+ inbuf ++;
+ }
+
+ return rows;
+}