From: bcpierce@cis.upenn.edu
To: caml-list@pauillac.inria.fr
Subject: OCaml sync tool for Rex organizer
Date: Sat, 14 Oct 2000 11:59:32 -0400 [thread overview]
Message-ID: <25507.971539172@silvercomet.emperorlinux.com> (raw)
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()
next reply other threads:[~2000-10-15 8:56 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2000-10-14 15:59 bcpierce [this message]
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
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=25507.971539172@silvercomet.emperorlinux.com \
--to=bcpierce@cis.upenn.edu \
--cc=caml-list@pauillac.inria.fr \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox