[Pkg-ocaml-maint-commits] [ocaml-csv] 04/10: Imported Upstream version 1.4.2

Stéphane Glondu glondu at moszumanska.debian.org
Tue Jan 26 14:31:09 UTC 2016


This is an automated email from the git hooks/post-receive script.

glondu pushed a commit to branch master
in repository ocaml-csv.

commit 825a995a48f15042cc84cf7cfff8b234cf87c021
Author: Stephane Glondu <steph at glondu.net>
Date:   Tue Jan 26 15:04:40 2016 +0100

    Imported Upstream version 1.4.2
---
 _oasis              |   2 +-
 examples/csvtool.ml | 157 ++++++++++++++++++++++++++++++++++++----------------
 2 files changed, 111 insertions(+), 48 deletions(-)

diff --git a/_oasis b/_oasis
index 7ca476f..86f74b8 100644
--- a/_oasis
+++ b/_oasis
@@ -1,7 +1,7 @@
 #								-*-conf-*-
 OASISFormat: 0.4
 Name:        csv
-Version:     1.4.1
+Version:     1.4.2
 Synopsis:    A pure OCaml library to read and write CSV files.
 Description: This is a pure OCaml library to read and write CSV files, 
   including all extensions used by Excel — e.g. quotes, newlines, 
diff --git a/examples/csvtool.ml b/examples/csvtool.ml
index 83fcc66..d2ece8a 100644
--- a/examples/csvtool.ml
+++ b/examples/csvtool.ml
@@ -190,21 +190,27 @@ let cmd_readable ~input_sep ~chan files =
   let csv = List.concat (List.map (Csv.load ~separator:input_sep) files) in
   Csv.save_out_readable chan csv
 
+let iter_csv_rows ~input_sep ~f files =
+  List.iter (fun filename ->
+             let in_chan, close =
+               match filename with
+               | "-" -> stdin, false
+               | filename -> open_in filename, true in
+             try
+               Csv.iter ~f (Csv.of_channel ~separator:input_sep in_chan);
+               if close then close_in in_chan
+             with Exit ->
+               if close then close_in in_chan
+            ) files
+
+
 let cmd_cat ~input_sep ~output_sep ~chan files =
   (* Avoid loading the whole file into memory. *)
   let chan = Csv.to_channel ~separator:output_sep chan in
   let f row =
     Csv.output_record chan row
   in
-  List.iter (
-    fun filename ->
-      let in_chan, close =
-        match filename with
-        | "-" -> stdin, false
-        | filename -> open_in filename, true in
-      Csv.iter ~f (Csv.of_channel ~separator:input_sep in_chan);
-      if close then close_in in_chan
-  ) files
+  iter_csv_rows ~input_sep ~f files
 
 let cmd_paste ~input_sep ~output_sep ~chan files =
   (* Return the 1st row, concatenation of all 1st rows; whether all
@@ -305,15 +311,7 @@ let cmd_set_columns ~input_sep ~output_sep ~chan cols files =
     let csv = Csv.set_columns ~cols csv in
     Csv.output_all (Csv.to_channel ~separator:output_sep chan) csv
   in
-  List.iter (
-    fun filename ->
-      let in_chan, close =
-        match filename with
-        | "-" -> stdin, false
-        | filename -> open_in filename, true in
-      Csv.iter ~f (Csv.of_channel ~separator:input_sep in_chan);
-      if close then close_in in_chan
-  ) files
+  iter_csv_rows ~input_sep ~f files
 
 let cmd_set_rows ~input_sep ~output_sep ~chan rows files =
   let csv = List.concat (List.map (Csv.load ~separator:input_sep) files) in
@@ -331,18 +329,9 @@ let cmd_head ~input_sep ~output_sep ~chan rows files =
       decr nr_rows;
       Csv.output_record chan row
     )
+    else raise Exit
   in
-  List.iter (
-    fun filename ->
-      if !nr_rows > 0 then (
-        let in_chan, close =
-          match filename with
-          | "-" -> stdin, false
-          | filename -> open_in filename, true in
-        Csv.iter ~f (Csv.of_channel ~separator:input_sep in_chan);
-        if close then close_in in_chan
-      )
-  ) files
+  iter_csv_rows ~input_sep ~f files
 
 let cmd_drop ~input_sep ~output_sep ~chan rows files =
   (* Avoid loading the whole file into memory. *)
@@ -354,15 +343,7 @@ let cmd_drop ~input_sep ~output_sep ~chan rows files =
     else
       decr nr_rows
   in
-  List.iter (
-    fun filename ->
-      let in_chan, close =
-        match filename with
-        | "-" -> stdin, false
-        | filename -> open_in filename, true in
-      Csv.iter ~f (Csv.of_channel ~separator:input_sep in_chan);
-      if close then close_in in_chan
-  ) files
+  iter_csv_rows ~input_sep ~f files
 
 let cmd_square ~input_sep ~output_sep ~chan files =
   let csv = List.concat (List.map (Csv.load ~separator:input_sep) files) in
@@ -404,6 +385,82 @@ let cmd_transpose ~input_sep ~output_sep ~chan files =
              Csv.output_all (Csv.to_channel ~separator:output_sep chan) tr
             ) files
 
+
+type format_el = String of string | Col of int
+
+let is_digit c = '0' <= c && c <= '9'
+
+(* Return the non-negative number starting at [i0 < i1 < len_s =
+   String.length s] and the index of the first character after that
+   number.  It is expected that [s.[i0]] be a digit, otherwise
+   [Failure] is be raised. *)
+let rec get_digit s len_s i0 i1 =
+  if i1 < len_s then
+    if is_digit s.[i1] then get_digit s len_s i0 (i1 + 1)
+    else (int_of_string(String.sub s i0 (i1 - i0)), i1)
+  else (* i0 < i1 (>)= len_s *)
+    (int_of_string(String.sub s i0 (len_s - i0)), len_s)
+
+(* Prepend to the format [fmt] the substring s.[i0 .. i1 - 1] unless
+   it is empty. *)
+let prepend_substring s i0 i1 fmt =
+  if i0 < i1 then String(String.sub s i0 (i1 - i0)) :: fmt
+  else (* i0 ≥ i1, empty substring *) fmt
+
+(* [i0 ≤ i1 ≤ len_s] *)
+let rec split_format s len_s i0 i1 =
+  if i1 >= len_s then
+    if i0 < len_s then [String(String.sub s i0 (len_s - i0))]
+    else []
+  else if s.[i1] = '%' then
+    let i2 = i1 + 1 in
+    if i2 >= len_s then
+      split_format s len_s i0 i2 (* consider a final '%' as a normal char *)
+    else if is_digit s.[i2] then
+      let col, i3 = get_digit s len_s i2 (i2 + 1) in
+      prepend_substring s i0 i1 (Col col :: split_format s len_s i3 i3)
+    else if s.[i2] = '(' then
+      if i2 + 1 < len_s && is_digit s.[i2 + 1] then (
+        let col, i3 = get_digit s len_s (i2 + 1) (i2 + 2) in
+        if i3 >= len_s || s.[i3] <> ')' then (
+          let r = String.sub s i1 (i3 - i1) in
+          failwith(sprintf "Column format %S not terminated by ')'" r)
+        );
+        prepend_substring
+          s i0 i1 (Col col :: split_format s len_s (i3 + 1) (i3 + 1))
+      )
+      else failwith "Column format %( not followed by a number"
+    else if s.[i2] = '%' then
+      let i3 = i2 + 1 in
+      String(String.sub s i0 (i1 - i0 + 1)) :: split_format s len_s i3 i3
+    else (* % + non-digit, consider it a literal '%' *)
+      split_format s len_s i0 i2
+  else if s.[i1] = '\\' then
+    (* Handle usual escapes. *)
+    let i2 = i1 + 1 in
+    if i2 >= len_s then split_format s len_s i0 i2
+    else if s.[i2] = 'n' then
+      let i3 = i2 + 1 in
+      prepend_substring s i0 i1 (String "\n" :: split_format s len_s i3 i3)
+    else if s.[i2] = 'r' then
+      let i3 = i2 + 1 in
+      prepend_substring s i0 i1 (String "\r" :: split_format s len_s i3 i3)
+    else if s.[i2] = 't' then
+      let i3 = i2 + 1 in
+      prepend_substring s i0 i1 (String "\t" :: split_format s len_s i3 i3)
+    else split_format s len_s i0 i2
+  else
+    split_format s len_s i0 (i1 + 1)
+
+let print_format row = function
+  | String s -> print_string s
+  | Col c -> try print_string (List.nth row (c - 1)) with _ -> ()
+
+let cmd_format ~input_sep fmt files =
+  let fmt = split_format fmt (String.length fmt) 0 0 in
+  iter_csv_rows ~input_sep files
+                ~f:(fun row -> List.iter (print_format row) fmt)
+
 let cmd_call ~input_sep command files =
   (* Avoid loading the whole file into memory. *)
   (* Use bash if it exists to enable the [command] to be an exported
@@ -419,15 +476,7 @@ let cmd_call ~input_sep command files =
       exit code
     )
   in
-  List.iter (
-    fun filename ->
-      let in_chan, close =
-        match filename with
-        | "-" -> stdin, false
-        | filename -> open_in filename, true in
-      Csv.iter ~f (Csv.of_channel ~separator:input_sep in_chan);
-      if close then close_in in_chan
-  ) files
+  iter_csv_rows ~input_sep ~f files
 
 let rec uniq = function
   | [] -> []
@@ -508,6 +557,7 @@ and trim_flags flags =
 (* Process the arguments. *)
 let usage =
   "csvtool - Copyright (C) 2005-2006 Richard W.M. Jones, Merjis Ltd.
+           - Copyright (C) 2007- Richard W.M. Jones & Christophe Troestler
 
 csvtool is a tool for performing manipulations on CSV files from shell scripts.
 
@@ -634,6 +684,17 @@ Commands:
   transpose input.csv
     Transpose the lines and columns of the CSV file.
 
+  format fmt
+    Print each row of the files according to the format 'fmt'.
+    Each occurrence of \"%i\" or \"%(i)\" (where 'i' is a number) in
+    'fmt' is replaced by the content of column number 'i' (remember
+    that the leftmost column is numbered 1 in the traditional
+    spreadsheet fashion).  A literal percent is obtained by doubling it.
+    The usual escape sequences \\n, \\r, and \\t are recognized.
+
+      Example:
+        csvtool format '%(1) -> %8%%\\n' input.csv
+
   call command
     This calls the external command (or shell function) 'command'
     followed by a parameter for each column in the CSV file.  The
@@ -802,6 +863,8 @@ let () =
          cmd_drop ~input_sep ~output_sep ~chan rows files
      | "transpose" :: files ->
          cmd_transpose ~input_sep ~output_sep ~chan files
+     | "format" :: fmt :: files ->
+        cmd_format ~input_sep fmt files
      | "call" :: command :: files ->
          cmd_call ~input_sep command files
      | "trim" :: flags :: files ->

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ocaml-maint/packages/ocaml-csv.git



More information about the Pkg-ocaml-maint-commits mailing list