home *** CD-ROM | disk | FTP | other *** search
/ Software Du Jour / SoftwareDuJour.iso / BUSINESS / DBASE / CLIPUDF.ARC / FIRSTCAP.PRG < prev    next >
Encoding:
Text File  |  1986-02-04  |  14.9 KB  |  419 lines

  1. EDITORS NOTE:
  2. NPN member Denny Dias has supplied us with a handy Clipper UDF
  3. (User Defined Function). As the our first publication of an
  4. outside article, I would like to encourage all potential NPN
  5. authors to check out Denny's formating and composition. This is
  6. the type of 'copy' that editors dream of. Thanks Denny!
  7.  
  8. Because of the length of this article, and because I am
  9. presuming that most people will not be reading it online, there
  10. will not be any page breaks from this page on. If you do intend
  11. on reading the article online, use the CTRL S and CTRL Q keys to
  12. stop and start the text. If you wish to leave the article type
  13. CTRL P.
  14.  
  15. So here comes FIRSTCAP. Fire it up...its fast...you'll love it!
  16.  
  17. -ROGER
  18.  
  19. FIRSTCAP . . . THE FUNCTION
  20. ---------------------------
  21. by Dennis L. Dias (NAN449)
  22.  
  23. Sometimes it is necessary to capitalize the first character of
  24. each word in a character string. This is true for titles,
  25. headings, and proper names. There is a procedure listed on the
  26. Ashton-Tate network that has as similar purpose; however that
  27. routine has shortcomings that inspired me to write my own.
  28.  
  29. For one thing, the Ashton-Tate program is a procedure not a
  30. function; therefore it can only return a result by altering its
  31. input. Consequently, it can only recieve a memory variable as
  32. input.
  33.  
  34. Another problem is the use of the AT() function to find the next
  35. space between words. That algorithm will return "Ashton-tate"
  36. (the 't' is left uncapitalized) and "C&h Sugar".
  37.  
  38. Admittedly, the routines presented here are not perfect. There
  39. is no test for context or grammer. However, most of the
  40. problems have been resolved and the function may be used in many
  41. ways.  For example:
  42.  
  43.  LIST FIRSTCAP(TRIM(last) + ", " + TRIM(first) + " " + TRIM(middle))
  44.  
  45. The main problem with the first version of FIRSTCAP() is speed.
  46. The routine is much too slow..so slow that I would hesitate to use
  47. it for anything more than a heading.  Repeated calls are out of
  48. the question.  It is possible to gain a little speed by
  49. capitalizing the first word before entering the main loop and
  50. eliminating the ( LEN() > 0 ) test in the first CASE statement.
  51. However, that is not enough to permit the repeated calls of a LIST
  52. command.
  53.  
  54. This led me to the second version.  In the second version I make
  55. use of assembly language to access the full speed of the CPU.
  56. This version is so fast that it is completely invisible during
  57. program execution.  It also demonstrates one method of using
  58. assembly language within a user defined function.
  59.  
  60. Here, then, are both versions of FIRSTCAP() along with the
  61. subfunction for version 1 and assembler routine necessary to
  62. utilize version 2.
  63.  
  64.  
  65. *********************************************
  66. *           V E R S I O N   1               *
  67. *********************************************
  68. *
  69. *  Function.....: Firstcap() . . . Version 1
  70. *  Author.......: Dennis L. Dias
  71. *  Source ID....: NAN449
  72. *  Date.........: 12/14/85
  73. *
  74. *  Syntax:  FIRSTCAP( <expC> )
  75. *  Return:  Character string with first letter of each word
  76. *           capitalized and the rest lower case.  The special
  77. *           words "and", "but", "for", "the", "by", "an", "of",
  78. *           "on", "in", "if", "to", "or", "at" and "a" will remain
  79. *           lower case unless: (A) The word is the first word in
  80. *           the string with no leading word separators; (B) The
  81. *           word is followed by a non-space character or (C) The
  82. *           word is the last word in the string.
  83. *
  84. *  Notes:   This first version is written entirely in dBASE.  It
  85. *           calls upon a second function (ISCHAR()..also written
  86. *           in dBASE) to determine which characters are "in-word"
  87. *           characters and which are not.  The execution speed is
  88. *           slow..unacceptable for repeated calls.
  89. *
  90. FUNCTION FIRSTCAP
  91. *
  92. PARAMETERS fl_strg
  93. PRIVATE fl_begn,fl_len,fl_pos,fl_outstr,fl_part,fl_true
  94. *
  95. fl_begn = 0
  96. fl_len = 0
  97. fl_pos = 1
  98. IF LEN(fl_strg) = 1
  99.     fl_outstr = UPPER(fl_strg)
  100. ELSE
  101.     fl_outstr = ""
  102. ENDIF
  103. fl_part = ""
  104. fl_true = LEN(fl_strg) > 1
  105. DO WHILE fl_true
  106.     fl_begn = fl_pos
  107.     *
  108.     *  Scan past in-word characters
  109.     *
  110.     DO WHILE ISCHAR(SUBSTR(fl_strg,fl_pos,1)) .AND. fl_pos < LEN(fl_strg)
  111.         fl_pos = fl_pos + 1
  112.     ENDDO
  113.     *
  114.     *  Scan past word separators
  115.     *
  116.     DO WHILE .NOT. ISCHAR(SUBSTR(fl_strg,fl_pos,1)) .AND.;
  117.             fl_pos < LEN(fl_strg)
  118.         fl_pos = fl_pos + 1
  119.     ENDDO
  120.     *
  121.     *  Determine if this is the last word
  122.     *
  123.     IF fl_pos = LEN(fl_strg) .AND. (ISCHAR(SUBSTR(fl_strg,fl_pos - 1,1));
  124.             .OR. fl_begn = fl_pos)
  125.         fl_len = fl_pos - fl_begn + 1
  126.         *
  127.         *  Set false to exit main loop
  128.         *
  129.         fl_true = .F.
  130.     ELSE
  131.         fl_len = fl_pos - fl_begn
  132.     ENDIF
  133.     *
  134.     *  Isolate one word and convert it to lower case
  135.     *
  136.     fl_part = LOWER(SUBSTR(fl_strg,fl_begn,fl_len))
  137.     *
  138.     *  Test for special word or a one character word at the end of
  139.     *  the line.  The use of (" " + TRIM() + " ") protects against
  140.     *  an unwanted substring match such as ("he " $ "the ").
  141.     *
  142.     DO CASE
  143.         CASE (" " + TRIM(fl_part) + " ") $ " and the for but to or by an" +;
  144.                 " in of on at if a " .AND. LEN(fl_outstr) > 0 .AND.;
  145.                 fl_pos < LEN(fl_strg)
  146.             *
  147.             *  Special word found in mid-string position where it should
  148.             *  not be capitalized..concatenate the word as is
  149.             *
  150.             fl_outstr = fl_outstr + fl_part
  151.         CASE LEN(fl_part) = 1
  152.             *
  153.             *  LEN() = 1 only occurs when a one character word appears
  154.             *  at the end of the string..capitalize and concatenate
  155.             *
  156.             fl_outstr = fl_outstr + UPPER(fl_part)
  157.         OTHERWISE
  158.             *
  159.             *  Capitalize the first character and concatenate
  160.             *  the word to the output string
  161.             *
  162.             fl_outstr = fl_outstr + UPPER(SUBSTR(fl_part,1,1)) +;
  163.                 SUBSTR(fl_part,2)
  164.     ENDCASE
  165. ENDDO
  166. RETURN(fl_outstr)
  167. *
  168. **********************************
  169.  
  170. **********************************
  171. *
  172. *  Function.....: Ischar()
  173. *  Author.......: Dennis L. Dias
  174. *  Source ID....: NAN449
  175. *  Date.........: 12/14/85
  176. *
  177. *  Syntax: ISCHAR( <expC> )
  178. *  Return: Logical true if the first character in <expC> is alpha
  179. *          or STR() or an apostrophe (')
  180. *
  181. FUNCTION ISCHAR
  182. *
  183. PARAMETERS fl_string
  184. *
  185. RETURN(UPPER(SUBSTR(fl_string,1,1)) $;
  186.     "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ'")
  187. *
  188.  
  189. *********************************************
  190. *           V E R S I O N   2               *
  191. *********************************************
  192. *
  193. *  Function.....: Firstcap() . . . Version 2
  194. *  Author.......: Dennis L. Dias
  195. *  Source ID....: NAN449
  196. *  Date.........: 12/14/85
  197. *
  198. *  Syntax:  FIRSTCAP( <expC> )
  199. *  Same syntax and output as version 1 except that the first word
  200. *  will always be capitalized..even if a special word is preceded
  201. *  by one or more leading word separators such as "...A Fine
  202. *  Madness...".
  203. *
  204. *  Notes:  This version calls an assembly language routine to do
  205. *          most of the work. The character string passed to the
  206. *          assembler module must first be converted to lower case.
  207. *          Execution speed is very fast..completely invisible.
  208. *
  209. FUNCTION FIRSTCAP
  210. *
  211. PARAMETERS fl_strg
  212. PRIVATE fl_outstr
  213. *
  214. fl_outstr = LOWER(fl_strg)
  215. CALL fcap with fl_outstr
  216. RETURN(fl_outstr)
  217. *
  218. ***************************
  219.  
  220. ;-------------------------------:
  221. ; Program File.: FCAP.ASM       :
  222. ; Author.......: Dennis L. Dias :
  223. ; Source ID....: NAN449         :
  224. ; Date.........: 12/14/85       :
  225. ;-------------------------------:
  226. ;
  227. ;-----------------------------------------------------------------:
  228. ;This assembly language routine is to be called by Clipper to     :
  229. ;   convert the  first letter of each word of the input string to :
  230. ;   caps. The special words listed as "special" below will remain :
  231. ;   lower case unless they appear as the first or last word or if :
  232. ;   they are followed by a non- space character. The input string :
  233. ;   must be passed as lower case.    The apostrophe (') is        :
  234. ;   considered a character.                                       :
  235. ;-----------------------------------------------------------------:
  236. ;
  237.         public  fcap            ;Public procedure
  238. ;
  239. _prog   segment byte            ;Clipper segment
  240.         assume  cs:_prog,ds:_prog,es:nothing,ss:nothing
  241. ;
  242. ;----------------:
  243. ;Local data area :
  244. ;----------------:
  245. ;
  246. ;Special words are grouped according to length. Each group is
  247. ;    preceeded by the length plus one space, and the number of words
  248. ;    of that length. This greatly reduces the number of comparisons
  249. ;    required to search the entire list.  These numbers MUST be
  250. ;    changed when adding or removing wods from the list.
  251. ;
  252. special db      4,4,"and the for but "
  253.         db      3,9,"to or by an in of on if at "
  254.         db      2,1,"a ",0      ;Special words are terminated with null
  255. ;
  256. ;List of characters to be considered "in-word" characters
  257. ;
  258. char    db      "0123456789abcdefghijklmnopqrstuvwxyz'"
  259. ;
  260. ;------------------------------------------------------:
  261. ;Primary routine..far procedure as required by Clipper :
  262. ;------------------------------------------------------:
  263. ;
  264. fcap    proc    far
  265. ;
  266.         push    bp              ;Required by Clipper
  267.         mov     bp,sp           ;To address variable at SP + 6
  268.         push    ds              ;Must save seg regs
  269.         push    es
  270.         cld                     ;Forward direction flag
  271. ;
  272.         lds     si,dword ptr[bp+6] ;Point to input string with DS:SI
  273.         push    cs
  274.         pop     es              ;Address local data via ES register
  275. ;
  276.         lea     di,char         ;ES:DI points to in-word characters
  277.         mov     cx,37           ;Length of in-word characters
  278. ;
  279. ;Capitalize the first word no matter what it is
  280. ;
  281. fc1:    lodsb                   ;Fetch char
  282.         or      al,al           ;Check for null terminator
  283.         jz      fc_ret          ;Quit if null..no characters at all
  284. ;
  285.         call    fl_scan         ;Test for in-word character
  286.         jnz     fc1             ;Scan off word separators
  287.         mov     bx,si
  288.         dec     bx             ;DS:BX points to first character
  289.         call    fl_cap          ;Cap first letter
  290. ;
  291. ;Return here for each new word
  292. ;
  293. fc2:    mov     bx,si
  294.         dec     bx              ;DS:BX points to first character
  295. ;
  296. fc3:    lodsb                   ;Fetch character
  297.         or      al,al           ;Check for null terminator
  298.         jz      fc5             ;Cap last word
  299. ;
  300.         call    fl_scan         ;Test for in-word character
  301.         jz      fc3            ;Scan past in-word characters
  302. ;
  303. fc4:    mov     dx,si
  304.         sub     dx,bx           ;DX has size of word plus 1
  305.         cmp     dl,4            ;Special words are 3 chars
  306.         ja      fc5             ;   plus 1 space max
  307. ;
  308.         call    fl_com          ;Test for special word
  309.         jc      fc6             ;Found..don't cap
  310. ;
  311. fc5:    call    fl_cap          ;Cap first letter
  312. ;
  313. fc6:    dec     si              ;Point to end of prev word + 1
  314.         lea     di,char         ;Point to in-word characters
  315.         mov     cx,37           ;Length of in-word characters
  316. ;
  317. fc7:    lodsb                   ;Fetch char
  318.         or      al,al           ;Check for null terminator
  319.         jz      fc_ret          ;Quit if null
  320. ;
  321.         call    fl_scan
  322.         jnz     fc7             ;Scan off word separators
  323.         jmp     fc2             ;Go process next word
  324. ;
  325. ;Done
  326. ;
  327. fc_ret: pop     es              ;Restore registers for Clipper
  328.         pop     ds
  329.         pop     bp
  330.         ret                     ;Far return to Clipper
  331. ;
  332. fcap    endp
  333. ;
  334. ;------------------:
  335. ;Local subroutines :
  336. ;------------------:
  337. ;
  338. ;The following procedure tests the character in AL as being in-word or
  339. ;    not. The zero flag returns set if the character is in-word.
  340. ;
  341. fl_scan proc    near
  342. ;
  343.         push    di              ;Save pointer to in-word characters
  344.         push    cx              ;Save length of in-word characters
  345.         repne   scasb           ;Look for match
  346.         pop     cx              ;Restore saved values and return
  347.         pop     di              ;   result in Zero flag
  348.         ret                     ;Near return
  349. ;
  350. fl_scan endp
  351. ;
  352. ;Convert lower case to upper case..DS:BX points to the character
  353. ;
  354. fl_cap  proc    near
  355. ;
  356.         cmp     byte ptr[bx],"a"
  357.         jb      fl_cap_ret      ;Less than "a" not lower
  358.         cmp     byte ptr[bx],"z"
  359.         ja      fl_cap_ret      ;Above "z" not lower
  360.         sub     byte ptr[bx],20h ;Convert to upper case
  361. ;
  362. fl_cap_ret:     ret             ;Near return
  363. ;
  364. fl_cap  endp
  365. ;
  366. ;Look for special words..return carry flag set if found..on entry
  367. ;     DS:BX points to first character of the word to test. Begin by
  368. ;     exchanging the contents of ES with DS to facilitate the use of
  369. ;     the string instructions. AX, CX, DX, and DI destroyed.
  370. ;
  371. fl_com  proc    near
  372. ;
  373.         push    si              ;Save current pointer
  374.         push    ds
  375.         pop     es              ;Clipper segment to ES
  376.         push    cs
  377.         pop     ds              ;Local segment to DS
  378.         mov     di,bx           ;ES:DI points to current word
  379.         lea     si,special      ;DS:SI points to local list of words
  380. ;
  381. fl_c1:  lodsb                   ;Fetch word size
  382.         or      al,al           ;Check for null terminator
  383.         jz      fl_clc          ;Exit routine..special word not found
  384. ;
  385.         mov     cl,al           ;Size of next group of words to count reg
  386.         xor     ch,ch           ;Zero high byte
  387.         lodsb                   ;Fetch word count
  388.         mov     dh,al           ;To DH for outer loop
  389.         mov     ax,cx           ;Save count in AX
  390. ;
  391. fl_c2:  push    di              ;Save pointer to test word
  392.         mov     cx,ax           ;Word size to counter
  393.         repe    cmpsb           ;Compare bytes
  394.         je      fl_stc          ;Jump if special word found
  395.         add     si,cx           ;Point to next word or next group
  396.         pop     di              ;Recover pointer to test word
  397.         dec     dh              ;Reduce outer loop counter
  398.         jnz     fl_c2           ;Anoter word..same length
  399.         jmp     fl_c1           ;Go for next group of words
  400. ;
  401. fl_clc: clc                     ;Clear carry..special word not found
  402. ;
  403. fl_com_ret: pop si              ;Restore registers and return
  404.         push    es
  405.         pop     ds              ;Clipper segment to DS
  406.         push    cs
  407.         pop     es              ;Local segment to ES
  408.         ret                     ;Near return
  409. ;
  410. ;Special word found..return result in carry flag
  411. ;
  412. fl_stc: pop     di              ;Clear stack
  413.         stc                     ;Set carry flag
  414.         jmp     fl_com_ret      ;Restore and return
  415. ;
  416. fl_com  endp
  417. _prog   ends
  418.         end
  419.