// This macro is designed to extract names & phone numbers from the title block often // included at the end of an email. // The best guess at Name, Email Address, Work phone, Fax, Mobile, Home phone, Job Title // are placed in Text Registers 81-89. // These are then further processed into Text Registers 91-96 based on user options. // Ian Binnie 11/11/2001 // Ian Binnie 25/11/2001 // Ian Binnie 23/01/2002 // Ian Binnie 26/03/2002 // Ian Binnie 09/04/2002 // Ian Binnie 23/04/2002 fix mobile formatting; allow numbers, "-" in titles // Ian Binnie 11/07/2002 numbers w/o formatting // Ian Binnie 23/07/2002 international with area code // ************************User options Reg_Set(99,"+61") // Own Country Code - e.g. "+61"; "-" if no replacement wanted Reg_Set(98,"0") // (optional) Char to replace Own Country Code - usually "0" Reg_Set(97,"02") // Own Area Code - e.g. "02"; "-" if no replacement wanted #98=1 // flag to strip () from area code Reg_Set(80,"C:\vedit\Palm.txt") Reg_Set(79,"C:\vedit\Test.vcf") // Text Register Usage // 81 Full Name // 82 Email Address // 83 Work phone (raw) // 84 Fax (raw) // 85 Mobile (raw) // 86 Home phone (raw) // 87 Title // 88 Organisation // 89 // 90 // 91 First Name // 92 Proper Name // 93 Work phone (formatted) // 94 Fax (formatted) // 95 Mobile (formatted) // 96 Home phone (formatted) // 97 Own Area Code - e.g. "02"; "-" if no replacement wanted // 98 (optional) Char to replace Own Country Code - usually "0" // 99 Own Country Code - e.g. "+61"; "-" if no replacement wanted // Numeric Register Usage // #80 counter (temp) // #81 line containing Full Name // #82 first line containing number // #83 // #84 // #85 // #86 // #87 // #88 // #89 number input register (temp) // #90 // #91 last line // #92 // #93 current line // #94 end of buffer // #95 // #96 // #97 // #98 flag to strip () from area code // #99 number output register (temp) for (#80=81; #80<=96; #80++) { reg_empty(#80) #@80 = 0 } if (Block_Begin != -1) { if (Block_End == -1) // If block-end not set { Block_End(Cur_Pos) // Set it at current cursor pos } } else { // Else process entire file bb(bob_pos) be(eob_pos) } goto_pos(be) #91 = Cur_Line // last line Replace_Block("|W|>","",bb,be,ALL+NOERR+BEGIN) // Remove trailing spaces #94 = be // save block end goto_pos(bb) line(1) Block_End(Cur_Pos-1) // Set it at end of line Call("FindName") // search for name on current line be(#94) // restore buffer if(Reg_Size(81)==0) // name not found yet { Call("FindName") // search for name elsewhere } // Search for email address goto_pos(bb) Search_Block("[a-zA-Z0-9\_\-\.]+@[a-zA-Z0-9\_\-\.]+\.[a-zA-Z][a-zA-Z]+",bb,be,REGEXP+MAX+ADVANCE+NOERR) if (!Error_Match) { #82 = Cur_Line Reg_Copy_Block(82,CP-Chars_Matched,CP) // Copy e-mail address to T-Reg } // Search for phone numbers Search_Block("|<|{Phone:,Ph:,Work:,Tel:,W:,Phone}",bb,be,BEGIN+ADVANCE+NOERR) if (!Error_Match) { #80 = Cur_Line Search_Block("[0-9\+(]+[0-9\_\-\s\t()]*",bb,be,REGEXP+MAX+NOERR+ADVANCE) if(!Error_Match&P == Cur_Line) // found number on same line { if(#91 > Cur_Line) { #91 = Cur_Line } Reg_Copy_Block(83,CP-Chars_Matched,CP) } } if(Reg_Size(83)==0) // number not found yet { // not found try any number goto_pos(bb) do { Search_Block("[0-9\+(]+[0-9\_\-\s\t()]*",bb,be,REGEXP+MAX+ERRBREAK+ADVANCE) } while(!Error_Match&Chars_Matched<8) if (!Error_Match&Chars_Matched>7) { if(#91 > Cur_Line) { #91 = Cur_Line } Reg_Copy_Block(83,CP-Chars_Matched,CP) } } goto_pos(bb) Search_Block("|{Fax:,F:,Fax,facsimile}",bb,be,BEGIN+ADVANCE+NOERR) if (!Error_Match) // found { #80 = Cur_Line Search_Block("[0-9\+(]+[0-9\_\-\s\t()]*",bb,be,REGEXP+MAX+NOERR+ADVANCE) if(!Error_Match&P == Cur_Line) // found number on same line { if(#91 > Cur_Line) { #91 = Cur_Line } Reg_Copy_Block(84,CP-Chars_Matched,CP) } } Search_Block("|{Mobile:,Cell,Mobile,Mob,Mb:}",bb,be,BEGIN+ADVANCE+NOERR) // Reg_Copy_Block(85,CP-Chars_Matched,CP) if (!Error_Match) // found { #80 = Cur_Line Search_Block("[0-9\+(]+[0-9\_\-\s\t()]*",bb,be,REGEXP+MAX+NOERR+ADVANCE) if (!Error_Match) // found number { if(#80 == Cur_Line) // found number on same line { if(#91 > Cur_Line) { #91 = Cur_Line } Reg_Copy_Block(85,CP-Chars_Matched,CP) } else { Search_Block("|{Mobile:,Cell,Mobile,Mob,Mb:}",cp,be,ADVANCE+NOERR) // try again if (!Error_Match) // found { #80 = Cur_Line Reg_Copy_Block(85,CP-Chars_Matched,CP) Search_Block("[0-9\+(]+[0-9\_\-\s\t()]*",bb,be,REGEXP+MAX+NOERR+ADVANCE) if(!Error_Match&P == Cur_Line) // found number on same line { if(#91 > Cur_Line) { #91 = Cur_Line } Reg_Copy_Block(85,CP-Chars_Matched,CP) } } } } } Search_Block("|{Home:,H:}",bb,be,BEGIN+CASE+ADVANCE+NOERR) if (!Error_Match) // found { #80 = Cur_Line if(#91 > Cur_Line) { #91 = Cur_Line } Search_Block("[0-9\+(]+[0-9\_\-\s\t()]*",bb,be,REGEXP+MAX+NOERR+ADVANCE) if(!Error_Match&P == Cur_Line) { Reg_Copy_Block(86,CP-Chars_Matched,CP) } } // Search for company name Search_Block("|{Telstra,Optus,Ericsson,Motorola,Vodafone,AAPT,ACA,ACIF,Alcatel,Cisco,Nortel,Powertel,Primus,Samsung,Siemens}",bb,be,BEGIN+NOERR+WORD) if (!Error_Match) // found { Reg_Copy_Block(88,CP,CP+Chars_Matched) } Search_Block("|{Title:}",bb,be,BEGIN+ADVANCE+NOERR) if (!Error_Match) // found { #80 = Cur_Line Search_Block("[A-Z][A-Za-z0-9\s\t&,\-]+$",bb,be,REGEXP+NOERR+ADVANCE) if(#80 == Cur_Line) { if(#91 > Cur_Line) { #91 = Cur_Line } Reg_Copy_Block(87,CP-Chars_Matched,CP) } } else { // A text line following name is most likely a title if(#81 && (#91-#81 > 1)) { goto_line(#81+1) // Search_Block("^[A-Za-z\s\t&,]+$",bb,be,REGEXP+NOERR+ADVANCE) Search_Block("^[A-Z][A-Za-z0-9\s\t&,\-]+$",bb,be,REGEXP+NOERR+ADVANCE) if (!Error_Match) // found { Reg_Copy_Block(87,CP-Chars_Matched,CP) } } } // Copy First & Proper names Goto_Line(#81) Search_Block("[A-Z][a-z]+",bb,be,REGEXP+NOERR+ADVANCE) RegCopyBlock(91,CP-Chars_Matched,CP) // Copy First name into reg Search_Block("[A-Z][a-z]+",bb,be,REGEXP+NOERR+ADVANCE) RegCopyBlock(92,CP-Chars_Matched,CP) // Copy Proper name into reg // Format phone numbers #89=83 #99=93 Call("PhoneFormat") #89=84 #99=94 Call("PhoneFormat") #89=85 #99=95 Call("PhoneFormat") #89=86 #99=96 Call("PhoneFormat") /*************************** Select Output Format Call("Vcard_Output_Record") goto OutputRecord return // ******************** Subroutine to find names :FindName: // Find Proper Name goto_pos(bb) // Find Proper Name // First search for ordinary name on a line e.g. Bill Smith | Bill A. Smith Search_Block("^[A-Z][a-z]+ +[A-Z][a-z]+$|^[A-Z][a-z]+ +[A-Z]\.? +[A-Z][a-z]+$",bb,be,REGEXP+NOERR+ADVANCE) if (!Error_Match) // found given name { RegCopyBlock(81,CP-Chars_Matched,CP) // Copy name into reg 81 #81 = Cur_Line } else // search for given name at start of line e.g. Bill | Bill A. { Search_Block("^[A-Z][a-z]+ +[A-Z]\.? +|^[A-Z][a-z]+ +",bb,be,REGEXP+MAX+NOERR+ADVANCE) // Then check if followed by common European name e.g. de di la von van Mc if (!Error_Match) // found given name { #80 = CP #90 = CP-Chars_Matched Search_Block("la {[A-Z][a-z]+}$|{Mc[A-Za-z][a-z]+}$|{d[ei] +[A-Z][a-z]+}$|{O'[A-Z][a-z]+}$",cp,be,REGEXP+NOERR+ADVANCE) if (!Error_Match&&(CP-Chars_Matched==#80)) // found { RegCopyBlock(81,#90,CP) // Copy name into reg 81 #81 = Cur_Line } else // try hyphenated name e.g. Smith-Jones - even include the Scots { Search_Block("[A-Z][a-z]+-[A-Z][a-z]+$|Mc[A-Z][a-z]+-[A-Z][a-z]+$|[A-Z][a-z]+-Mc[A-Z][a-z]+$",cp,be,REGEXP+NOERR+ADVANCE) if (!Error_Match&&(CP-Chars_Matched==#80)) // found { RegCopyBlock(81,#90,CP) // Copy name into reg 81 #81 = Cur_Line } } } } if(Reg_Size(81)==0) // not found yet { // try 3 part name (Asian or middle name) Search_Block("^[A-Z][a-z]+ +[A-Z][a-z]+ +[A-Z][a-z]+$",bb,be,BEGIN+REGEXP+NOERR) if (!Error_Match) // found name { RegCopyBlock(81,CP, CP+Chars_Matched) // Copy name into reg 81 #81 = Cur_Line } else { // try for name anywhere Search_Block("[A-Z][a-z]+ +[A-Z][a-z]+|[A-Z][a-z]+ +[A-Z]\.? +[A-Z][a-z]+",bb,be,BEGIN+REGEXP+MAX+NOERR) // Search_Block("[A-Z][a-z]+ +[A-Z][a-z][A-Z]?[a-z]*",bb,be,BEGIN+REGEXP+MAX+NOERR) if (!Error_Match) // found name { RegCopyBlock(81,CP, CP+Chars_Matched) // Copy name into reg 81 #81 = Cur_Line } } } return // ******************** Subroutine to format phone numbers :PhoneFormat: // #89 number input register // #99 number output register #104=Buf_Num // Current Edit Buffer #105=BUF_FREE // temporary bufer Buf_Switch(#105) buf_empty(OK) Reg_Ins(#89) Replace("|W|>","",NOERR+BEGIN) // Remove trailing spaces Search("+",BEGIN+NOERR) // use international number if present if (!Error_Match) // found international { del_line(0) // delete preceding Replace("+|w","+",BEGIN+NOERR) // Replace("(|[0)]","",NOERR) // international number should not contain "(" Replace("(|[0]","",NOERR) // international number should not contain "(" or "(0" Replace(")"," ",NOERR) Replace("[\s\t\-]+"," ",begin+all+REGEXP+MAX+NOERR) // tidy up //break_out bof if(match(@99)==0) // strip Own Country Code { Del_Char(Chars_Matched) Replace("|<|W","",NOERR+BEGIN) // Remove leading spaces Reg_Ins(98) // Char to replace Own Country Code - usually "0" // Replace("\+[1-9][0-9]* ",@98,REGEXP) // finds + followed by CC } } else { Search("[0-9]+[0-9][0-9][0-9][0-9]$",BEGIN+REGEXP+MAX+NOERR) // number w/o delimiters if (!Error_Match) { Replace("{[0-9]+}{[0-9][0-9][0-9][0-9]}$","\1 \2",REGEXP+MAX+NOERR) // insert space before last 4 digits } bof if(match("^[1-9][0-9]+[\s\t\-]+[0-9][0-9][0-9]+$",REGEXP+MAX+NOERR)==0) // Local number { Replace("{[1-9][0-9]+}[\s\t\-]+{[0-9][0-9][0-9]+}","\1 \2",REGEXP+MAX+NOERR) } else { // tidy up national number (delete "-", multiple spaces if(#98) { // finds area code with optional () followed by number - strip () Replace("(?{[0-9][0-9]+})?[\s\t\-]*{[0-9][0-9]+}[\s\t\-]+{[0-9][0-9][0-9]+}","\1 \2 \3",REGEXP+MAX+NOERR) } else { // finds area code with optional () followed by number Replace("{(?[0-9][0-9]+)?}[\s\t\-]*{[0-9][0-9]+}[\s\t\-]+{[0-9][0-9][0-9]+}","\1 \2 \3",REGEXP+MAX+NOERR) } } } bof if(match(@97)==0) // strip Own Area Code { Del_Char(Chars_Matched) Replace("|<|W","",NOERR+BEGIN) // Remove leading spaces // Replace("[0-9]* ","",REGEXP) } // *********** this is my personal formatting preference for Australian mobile numbers Replace("04 *{[0-9]} *{[0-9]} *{[0-9]} *{[0-9]} *{[0-9]} *{[0-9][0-9][0-9]}","04\1\2 \3\4\5 \6",REGEXP+MAX+NOERR) RegCopyBlock(#99,bob_pos,eob_pos) // copy formatted number to outreg buf_quit(OK) Buf_Switch(#104) return :OutputRecord: #104=Buf_Num // Current Edit Buffer File_Open("|@(80)") // Open or switch to "output" file eof() Reg_Ins(92) ic(9) Reg_Ins(91) ic(9) Reg_Ins(87) ic(9) Reg_Ins(88) ic(9) Reg_Ins(93) ic(9) Reg_Ins(94) ic(9) Reg_Ins(95) ic(9) Reg_Ins(96) ic(9) Reg_Ins(82) in(1) Buf_Switch(#104) return :Vcard_Output_Record: #104=Buf_Num // Current Edit Buffer File_Open("|@(79)") // Open or switch to "output" file eof() in(1) Ins_Text("BEGIN:VCARD") in(1) Ins_Text("VERSION:2.1") in(1) Ins_Text("N:") Reg_Ins(92) it(";") Reg_Ins(91) in(1) Ins_Text("FN:") Reg_Ins(81) in(1) if(Reg_Size(87)) { Ins_Text("TITLE:") Reg_Ins(87) in(1) } if(Reg_Size(88)) { Ins_Text("ORG:") Reg_Ins(88) in(1) } if(Reg_Size(93)) { Ins_Text("TEL;WORK:") Reg_Ins(93) in(1) } if(Reg_Size(94)) { Ins_Text("TEL;FAX:") Reg_Ins(94) in(1) } if(Reg_Size(95)) { Ins_Text("TEL;CELL:") Reg_Ins(95) in(1) } if(Reg_Size(96)) { Ins_Text("TEL;HOME:") Reg_Ins(96) in(1) } if(Reg_Size(82)) { Ins_Text("EMAIL;PREF:") Reg_Ins(82) in(1) } // Novell Groupwise won't play without PREF //if(Reg_Size(9)) { Ins_Text(":") Reg_Ins(9) in(1) } Ins_Text("END:VCARD") in(1)