caml-list - the Caml user's mailing list
 help / color / mirror / Atom feed
* OCaml sync tool for Rex organizer
@ 2000-10-14 15:59 bcpierce
  2000-10-16  0:52 ` Seeking pratical tutorial or examples Francisco Reyes
  0 siblings, 1 reply; 16+ messages in thread
From: bcpierce @ 2000-10-14 15:59 UTC (permalink / raw)
  To: caml-list

The other day someone showed me a Rex organizer -- a $100 PDA in the form
of a PCMCIA card with 512K memory, a small LCD display on the front, and
a few buttons for navigation.  I was instantly hooked, bought one, and
started looking around for Linux support for it.  Of course, the end of
the story was that, after looking at what a few people had done, I got a
hack attack and ended by writing my own... in OCaml, obviously.  Now I
can dump all my favorite data (addresses and arbitrary text files) in
seconds to an organizer the size of a credit card.

It's nothing fancy, but in the interests of helping others have fun and
waste time, here it is (in its entirety)...

Share and enjoy,

     Benjamin 


(* bcprex.ml -- A little hack for downloading data to a Xircom Rex
                organizer from a linux system.

   Author: Benjamin C. Pierce
   This code may be distributed under the terms of the GNU Public License.

   This _very simple and unpolished_ little program takes all the
   information in the directory where it is started and formats it so
   that it can be downloaded to a Xircom Rex organizer.  It handles just
   the features I needed, and no more.  You will probably need to hack
   it yourself to adapt it to your needs.  
   
   WHAT YOU WILL NEED:
     1) A Xircom RexPro organizer (buy from your favorite gadget store; 
        they cost about $100)
     2) A copy of the "TruSync linux beta developer pack" from StarFish
        software.  Download this from
           http://www.starfish.com/tsdn/linux.html
        unpack it, and put the binary file ProConnect somewhere in your
        search path.
     3) An Objective Caml compiler to compile this file.  Download the
        O'Caml system from http://caml.inria.fr and install according to 
        the instructions.  (If you haven't installed O'Caml before, it's
        easy.  RPMs are provided for Redhat systems.)
        
   INSTRUCTIONS:
     1) Compile the present file like this:
           ocamlc -o bcprex unix.cma str.cma bcprex.ml -cclib -lunix
     2) Put the executable file bcprex in your search path
     3) Cd to the directory where you will keep the files you want to download
        to the rex
     4) Initialize the Rex (if you haven't already): tell it your name,
        the current time, etc.
     5) Insert the Rex card in your PCMCIA slot
     6) Check that /dev/mem0c now exists.  If it does, make sure that
        it's readable by ProConnect, either by doing
            chmod a+rw /dev/mem0c
        or by making ProConnect itself owned by root and setuid.
     7) Upload the current state by typing
           ProConnect -o init.rexdump -f /dev/mem0c
     8) Create some files that you want to download (see information
        below on formats)
     9) Download your data by executing 'bcprex' (from in this directory)

   SUPPORTED FORMATS:
     * TEXT FILES with extension .txt are downloaded as Rex memos.  The first
       line of the file is used as the title of the memo, the rest as the
       body.  
     * VCARD FILES with extension .vcd are interpreted as lists of VCards
       and downloaded as contact records.  Each VCard looks like this:

           BEGIN:VCARD
           <fields>
           END:VCARD

       where each <field> can be:

           N:lastname;firstname;middlename;title;suffix
               (all fields optional, but the ; separators are required)

           TEL;HOME:<number>

           TEL;WORK:<number>

           <FIELD>:<BODY>
               where <FIELD> is any other field name and <BODY> is a
               string terminated by a carriage return not immediately
               followed by a space (a CR followed by a space is ignored). 

       Sample VCard:

         BEGIN:VCARD
         N:Smith;John;;;
         TEL;WORK:123 222 3333
         TEL;HOME:987 654 3210
         ADR:222 Main St., Philadelphia, PA 19191
         NOTE:Some information.
          Some more information.
         END:VCARD

       The N, TEL;WORK, and TEL;HOME fields are treated specially.  All other
       fields are concatenated and dumped into the contact record as a note.

   WISH LIST:
     * It would be nice to download appointments as Rex calendar records
     * At the moment, Rex categories are not supported in any way
     * I couldn't figure out how to get a quote or hash character into the
       Rex: the string escape convention in the TruSync thing seems
       broken.
*)



open Printf
   
let outchan = ref None

let theoutchan() =
  match !outchan with
    None -> assert false
  | Some(ch) -> ch

let emit s = output_string (theoutchan()) s
let emitln s = emit s; emit "\n"

let error s = print_string s; exit 1

let readdir f = 
  let dir = Unix.opendir f in
  let rec loop files =
    let f = try Unix.readdir dir with End_of_file -> "" in
    if f="" then files
    else if f="." || f=".." then loop files
    else loop (f::files)
  in
  let files = loop [] in
  Unix.closedir dir;
  files

let rec trim s =
  let l = String.length s in 
  if l=0 then s
  else if s.[0]=' ' || s.[0]='\t' || s.[0]='\n' || s.[0]='\r' then
    trim (String.sub s 1 (l-1))
  else if s.[l-1]=' ' || s.[l-1]='\t' || s.[l-1]='\n' || s.[l-1]='\r' then
    trim (String.sub s 0 (l-1))
  else
    s

let rec trimEnd s =
  let l = String.length s in 
  if l=0 then s
  else if s.[l-1]=' ' || s.[l-1]='\t' || s.[l-1]='\n' || s.[l-1]='\r' then
    trimEnd (String.sub s 0 (l-1))
  else
    s

let readfile f =
  try
    let ch = open_in f in
    let rec loop lines =
      match
        try Some(input_line ch) with End_of_file -> None
      with
        None -> List.rev lines
      | Some(l) -> loop ((trimEnd l)::lines) in 
    let res = loop [] in
    close_in ch;
    res
  with
    Sys_error _ -> []

(* ---------------------------------------------------------------------- *)

let quotechar = Str.regexp "\""
let hashchar = Str.regexp "#"
let slashchar = Str.regexp "/"
let semichar = Str.regexp ";"

let escape s = 
  let s = Str.global_replace slashchar "//" s in
(*  let s = Str.global_replace quotechar "/\"" s in *)
  let s = Str.global_replace quotechar "'" s in 
  let s = Str.global_replace hashchar "<hash>" s in
  s

let processMemo f =
  printf "Memo %s\n" f;
  let ll = readfile f in
  let title = List.hd ll in
  let body = List.tl ll in
  emit (sprintf "memo 1 \"%s\" \"" title);
  List.iter (fun l -> emit (escape l); emit "  #\n") body;
  emit "\"\n"

(* ---------------------------------------------------------------------- *)

let collectVCards ll =
  let rec nextCard cards = function
      [] -> List.rev cards
    | "BEGIN:VCARD" :: rest -> nextField cards [] rest
    | "" :: rest -> nextCard cards rest
    | s :: rest -> error (sprintf "Unrecognized vcard format: %s\n" s)
  and nextField cards fields = function
      [] -> error "End of file while processing a VCARD"
    | "END:VCARD"::rest -> nextCard ((List.rev fields)::cards) rest
    | ""::rest -> nextField cards fields rest
    | s::rest when s.[0] = ' ' ->
        if fields=[] then
          error "Line beginning with blank at beginning of VCARD"
        else
          nextField cards (((List.hd fields) ^ s) :: (List.tl fields)) rest
    | s::rest ->
        nextField cards (s::fields) rest
  in nextCard [] ll

let notes = ref ""

let dumpfield f =
  let colonpos =
    try
      String.index f ':'
    with Not_found ->
      error (sprintf "VCard field '%s' does not contain a colon" f) in 
  let kind = String.sub f 0 colonpos in
  let body =
    escape (String.sub f (colonpos+1) (String.length f - colonpos - 1)) in
  match kind with
    "N" ->
      let parts = Str.split_delim semichar body in
      if List.length parts <> 5 then
        error (sprintf "VCARD name field should have five (not %d) parts (%s)"
                 (List.length parts) body);
      emit "  1 0 \"";
      emit (List.nth parts 0);
      emit "\"\n";
      emit "  2 0 \"";
      if (List.nth parts 1) <> "" then begin
        emit (List.nth parts 1);
        emit " ";
      end;
      emit (List.nth parts 2);
      if (List.nth parts 3) <> "" then begin
        emit " ";
        emit (List.nth parts 3);
      end;
      if (List.nth parts 4) <> "" then begin
        emit " ";
        emit (List.nth parts 4);
      end;
      emit "\"\n";
  | "TEL;WORK" -> 
      emit "  17 0 \"";
      emit body;
      emit "\"\n"
  | "TEL;HOME" -> 
      emit "  18 0 \"";
      emit body;
      emit "\"\n"
  | _ ->
      notes := !notes ^ " || " ^ body

let dumpnotes() =
  if !notes <> "" then begin
    emit "  0 0 \"";
    emit !notes;
    emit "\"\n";
  end;
  notes := ""

let dumpcard c =
  emit "contact 1 0\n";
  List.iter dumpfield c;
  dumpnotes()
    
let processAddresses f =
  List.iter dumpcard (collectVCards (readfile f))

(* ---------------------------------------------------------------------- *)

let processfile f =
  if Filename.check_suffix f ".txt" then processMemo f
  else if Filename.check_suffix f ".sp" then processMemo f
  else if Filename.check_suffix f ".todo" then processMemo f
  else if Filename.check_suffix f ".vcd" then processAddresses f
  else printf "Skipping %s\n" f

let initrex() =
  let init = readfile "init.rexdump" in
  List.iter emitln init  

let main() =
  outchan := Some(open_out "rexdump.tmp");
  initrex();
  let files = readdir "." in
  List.iter processfile files;
  emit "done\n";
  close_out (theoutchan());
  flush stdout;
(*
  let ll = readfile "rexdump.tmp" in
  List.iter (fun l -> print_string l; print_newline()) ll
*)
  let result = Sys.command "ProConnect -i rexdump.tmp -f /dev/mem0c" in
  if result <> 0 then printf "ProConnect failed with exit code %d\n" result;
  exit result

;; main()




^ permalink raw reply	[flat|nested] 16+ messages in thread
* RE: Seeking pratical tutorial or examples.
@ 2000-10-16 21:46 Brent Fulgham
  2000-10-17 10:10 ` Francisco Reyes
  2000-10-17 17:51 ` Chris Hecker
  0 siblings, 2 replies; 16+ messages in thread
From: Brent Fulgham @ 2000-10-16 21:46 UTC (permalink / raw)
  To: fran; +Cc: caml-list

Francisco (and others),

Could it be that your mind is merely reeling from the unfamiliar 
functional programming paradigm?  Those of use "raised" in the school
of procedural C programming, or working in the Dot-Com trenches in
Perl are used to thinking in terms of step 1, step2, ..., etc.

Those of us who have at least been exposed to Lisp or Scheme have
a leg up, because we were forced to think recursively and look at
a program's output as the result of the evaulation of functions.

You are probably right that some code snippets might be helpful,
but many of the references you site include many such examples.
Pierre Weis has already listed the references I have used to reach
my current novice-state familiarity with Caml, and you would be
well served to examine those documents.

I would also highly recommend Cousineau and Mauny's excellent
"The Functional Approach to Programming".  While not a Caml
tutorial, it teaches you the language as you go, and has the
added benefit of teaching you about functional programming.
If you like the venerable "Structure and Interpretation of
Computer Programming", this will be right up your alley.  Cousineau
and Mauny also include lots of useful examples of things like
quicksort (using Caml's functional approach, then again in a
more procedural way to contrast), parsing systems, tree traversal,
graphs, and more.  Highly recommended.

And of course, have fun!

-Brent



^ permalink raw reply	[flat|nested] 16+ messages in thread
* RE: Seeking pratical tutorial or examples.
@ 2000-10-17 19:21 Brent Fulgham
  0 siblings, 0 replies; 16+ messages in thread
From: Brent Fulgham @ 2000-10-17 19:21 UTC (permalink / raw)
  To: Chris Hecker; +Cc: caml-list

> >Could it be that your mind is merely reeling from the unfamiliar 
> >functional programming paradigm?  Those of use "raised" in the school
> >of procedural C programming, or working in the Dot-Com trenches in
> >Perl are used to thinking in terms of step 1, step2, ..., etc.
> 
> Uh, speaking for myself, I think this comment is a really 
> good way to turn off people interested in exploring ocaml.  
> There's a difference between "my mind is reeling" and "I'm 
> trying to figure out where I need to put a damn semicolon".
>

Yikes -- I can see my attempt at humor was a dud.  I was just trying
to see if his problems were similar to the confusion I felt when
dealing with functional programming after many years using the
procedural style.  I apologize if I've offended anyone!  :-\
 
> The fact of the matter is that there is a certain amount of 
> logistical stuff one has to do to get a program to compile in 
> any new language.  We're (or I am) asking for examples of 
> that in the main docs.  I had to go download a bunch of 
> sample programs from unrelated sites to see how to do it.
> 
By all means -- if the information is not easily found, it should
be included in the documentation.

-Brent



^ permalink raw reply	[flat|nested] 16+ messages in thread
[parent not found: <20001019101803A.garrigue@kurims.kyoto-u.ac.jp>]

end of thread, other threads:[~2000-10-19 21:50 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2000-10-14 15:59 OCaml sync tool for Rex organizer bcpierce
2000-10-16  0:52 ` Seeking pratical tutorial or examples Francisco Reyes
2000-10-16  5:42   ` Alan Schmitt
2000-10-16 11:20     ` Francisco Reyes
2000-10-16  7:49   ` Pierre Weis
2000-10-16 17:57     ` Chris Hecker
2000-10-17 17:40       ` Stefan Monnier
2000-10-18  5:52       ` Francisco Reyes
2000-10-18  7:20         ` Chris Hecker
2000-10-18 13:31         ` Stephan Houben
2000-10-16 21:46 Brent Fulgham
2000-10-17 10:10 ` Francisco Reyes
2000-10-17 17:51 ` Chris Hecker
2000-10-18  5:58   ` Francisco Reyes
2000-10-17 19:21 Brent Fulgham
     [not found] <20001019101803A.garrigue@kurims.kyoto-u.ac.jp>
2000-10-19 19:02 ` Francisco Reyes

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).