-# $Id: Makefile,v 1.3 2005-02-17 15:51:47 rich Exp $
+# $Id: Makefile,v 1.4 2005-05-24 13:52:50 rich Exp $
PACKAGE := ocaml-csv
-VERSION := 1.0.2
+VERSION := 1.0.3
OCAMLC := ocamlc
OCAMLCINCS :=
OBJS := csv.cmo
XOBJS := $(OBJS:.cmo=.cmx)
-all: csv.cma csv.cmxa example
+all: csv.cma csv.cmxa example csvtool
csv.cma: $(OBJS)
$(OCAMLC) $(OCAMLCFLAGS) -a -o $@ $^
$(OCAMLC) $^ -o $@
./test
+csvtool: csv.cmxa csvtool.ml
+ $(OCAMLOPT) $^ -o $@
+
# Common rules for building OCaml objects.
.mli.cmi:
# Build a distribution.
dist:
- tar zcf $(PACKAGE)-$(VERSION).tar.gz `cat MANIFEST`
\ No newline at end of file
+ $(MAKE) check-manifest
+ rm -rf $(PACKAGE)-$(VERSION)
+ mkdir $(PACKAGE)-$(VERSION)
+ tar -cf - -T MANIFEST | tar -C $(PACKAGE)-$(VERSION) -xf -
+ tar zcf $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION)
+ rm -rf $(PACKAGE)-$(VERSION)
+ ls -l $(PACKAGE)-$(VERSION).tar.gz
+
+check-manifest:
+ @for d in `find -type d -name CVS | grep -v '^\./debian/'`; \
+ do \
+ b=`dirname $$d`/; \
+ awk -F/ '$$1 != "D" {print $$2}' $$d/Entries | \
+ sed -e "s|^|$$b|" -e "s|^\./||"; \
+ done | sort > .check-manifest; \
+ sort MANIFEST > .orig-manifest; \
+ diff -u .orig-manifest .check-manifest; rv=$$?; \
+ rm -f .orig-manifest .check-manifest; \
+ exit $$rv
+
+.PHONY: depend dist check-manifest
--- /dev/null
+(* Handy tool for managing CSV files.
+ * $Id: csvtool.ml,v 1.1 2005-05-24 13:52:50 rich Exp $
+ *)
+
+open Printf
+open Csv
+
+let cmd_cols ~separator ~csv ~chan cols =
+ let cols = List.map int_of_string cols in
+
+ let output = List.map (
+ fun row ->
+ let n = List.length row in
+ let row = List.map (
+ fun col_wanted ->
+ if 0 <= col_wanted && col_wanted < n then
+ List.nth row col_wanted
+ else
+ ""
+ ) cols in
+ row
+ ) csv in
+ save_out ~separator chan output
+
+let cmd_namedcols ~separator ~csv ~chan names =
+ let header, data =
+ match csv with
+ | [] -> failwith "no rows in this CSV file"
+ | h :: t -> h, t in
+ let data = associate header data in
+ let data = List.map (
+ fun row -> List.map (fun name -> List.assoc name row) names
+ ) data in
+ save_out ~separator chan data
+
+let cmd_width ~csv ~chan () =
+ fprintf chan "%d\n" (columns csv)
+
+let cmd_height ~csv ~chan () =
+ fprintf chan "%d\n" (lines csv)
+
+let cmd_readable ~csv ~chan () =
+ save_out_readable chan csv
+
+(* Process the arguments. *)
+let usage =
+ "csvtool - Copyright (C) 2005 Richard W.M. Jones, Merjis Ltd.
+
+csvtool is a tool for performing manipulations on CSV files from shell scripts.
+
+Summary:
+ csvtool [-options] command [command-args] < input.csv
+
+Commands:
+ col [col1] [col2] ...
+ Return one or more columns from the CSV file. Columns are numbered
+ starting from zero.
+
+ namedcol [name1] [name2] ...
+ Assuming the first row of the CSV file is a list of column headings,
+ this returned the column(s) with the named headings.
+
+ width
+ Return the maximum width of the CSV file (number of columns in the
+ widest row).
+
+ height
+ Return the number of rows in the CSV file.
+
+ readable
+ Print the input CSV in a readable format.
+
+Input and output files:
+ csvtool normally processes its input from stdin and writes its output
+ to stdout. Use the -i and -o options to override this behaviour.
+
+Options:"
+
+let () =
+ let input_sep = ref ',' in
+ let set_input_sep = function
+ | "TAB" -> input_sep := '\t'
+ | "COMMA" -> input_sep := ','
+ | s -> input_sep := s.[0]
+ in
+
+ let output_sep = ref ',' in
+ let set_output_sep = function
+ | "TAB" -> output_sep := '\t'
+ | "COMMA" -> output_sep := ','
+ | s -> output_sep := s.[0]
+ in
+
+ let input_file = ref "" in
+ let output_file = ref "" in
+
+ let argspec = [
+ "-t", Arg.String set_input_sep,
+ "Input separator char. Use -t TAB for tab separated input.";
+ "-u", Arg.String set_output_sep,
+ "Output separator char. Use -t TAB for tab separated output.";
+ "-i", Arg.Set_string input_file,
+ "Read CSV input from file (instead of stdin)";
+ "-o", Arg.Set_string output_file,
+ "Write output to file (instead of stdout)"
+ ] in
+
+ let rest = ref [] in
+ let set_rest str =
+ rest := str :: !rest
+ in
+
+ Arg.parse argspec set_rest usage;
+
+ let input_sep = !input_sep in
+ let output_sep = !output_sep in
+ let input_file = !input_file in
+ let output_file = !output_file in
+ let rest = List.rev !rest in
+
+ let cmd, args =
+ match rest with
+ | [] -> prerr_endline (Sys.executable_name ^ " --help for usage"); exit 1
+ | h :: t -> h, t in
+
+ (* Read the input file. *)
+ let input =
+ if input_file <> "" then load ~separator:input_sep input_file
+ else load_in ~separator:input_sep stdin in
+
+ (* Set up the output file. *)
+ let chan =
+ if output_file <> "" then open_out output_file
+ else stdout in
+
+ (match cmd with
+ | "col" | "cols" ->
+ cmd_cols ~separator:output_sep ~csv:input ~chan args
+ | "namedcol" | "namedcols" ->
+ cmd_namedcols ~separator:output_sep ~csv:input ~chan args
+ | "width" ->
+ cmd_width ~csv:input ~chan ()
+ | "height" ->
+ cmd_height ~csv:input ~chan ()
+ | "readable" ->
+ cmd_readable ~csv:input ~chan ()
+ | _ -> prerr_endline (Sys.executable_name ^ " --help for usage")
+ );
+
+ if output_file <> "" then close_out chan