Converting Rexx to thinBasic
Contents of Article
Introduction
If you come from an IBM ISPF background, you may have used or written TSO/Rexx scripts to interface with ISPF. While our thinBasic scripts and the capabilities of SPFLite are not exactly the same, there are enough similarities that you may be able to convert some existing Rexx scripts to thinBasic and have them perform useful work in the SPFLite environment.
You will not be able to convert ISPF-specific features, like access to the Dialog Manager, Table Services, File Tailoring, etc. but for scripts that simply interface with an edit session, there is enough functional commonality that a workable conversion may be possible.
We can't give you every possible conversion strategy and technique, or cover every syntax requirement, but the following may be a good starting point to help you. Because this topic is large and complicated, what is presented here should be considered as the first draft of a work in progress. We invite all users with ISPF/Rexx experience to test out these ideas, and we welcome any and all suggestions for improving on these conversion strategies.
Keep in mind, in addition to ISPF vs. SPFLite differences, that the thinBasic functions and features are comparable but may not be identical to Rexx. Be sure to consult the thinBasic documentation for any features you are not familiar with, and to ensure that function parameters and results are what you expected.
- A major difference between Rexx and thinBasic is that Rexx treats all values as strings, which you don't need to declare, whereas thinBasic is a more "strongly typed" language, and does require you to predeclare string and numeric variables before you use them.
- If you have Rexx code that is checking return codes from ISREDIT and ISPEXEC calls, be aware the SPFLite return codes are different. Both treat a return code of 0 as "normal" and non-zero is usually treated as a "problem occurred". Beyond that, there is no similarity.
- Rexx scripts return messages back to ISPF using a call to ISPEXEC SETMSG MSG(msgID). In many cases, this call will reference two Dialog variables, ZerrSm and ZerrLm, the "short error message" and "long error message". ISPF will first display a "short" message, and if you press F1, it will first display the longer message, then launch a Help panel. SPFLite does not implement this two-stage messaging; there is only a single message displayed by SET_MSG. If you are converting ISPF Rexx scripts with ISPEXEC SETMSG calls, you will have to pick the message you like the best (either short or long) as the one and only message displayed. Because the SPFLite screen can handle fairly long messages, you may want to choose the ZerrLm value for SET_MSG. If you really need both the short and long messages, you may wish to call MSGBOX instead of SET_MSG. (You might use the "long" message as the MSGBOX message, and the "short" message as the MSGBOX title.) Some message ID codes for ISPEXEC SETMSG refer to fixed messages in the ISPF message library, and do not use the contents of Dialog variables ZerrSm and ZerrLm. You would need to know what those messages were, and/or have access to a copy of that library, to be able to convert such messages to SPFLite.
- ISREDIT commands that deal with lines use visible line number values. SPFLite uses the Line Pointer concept. In most cases, you can treat Line Pointers as if they were line numbers, for purposes of converting the Rexx script. You will need to add code to ensure that IS_DATA(linePointer) is TRUE, so you don't attempt to process non-data lines in your edit file.
- Any Rexx statements involving LINE functions such as LINEIN and LINEOUT that you wish to convert to thinBasic will require a USES "FILE" statement in your macro. Be aware that if you attempt to perform file I/O on files that SPFLite already has opened, it could result in runtime conflicts.
- Rexx has a command called INTERPRET, which will take a string and treat it as a Rexx statement, which is immediately parsed and executed. thinBasic does not have an INTERPRET capability per se, but it does have a series of functions that begin with "Eval" which are used to evaluate string and math expressions. If you are converting a Rexx script with INTERPRET, one of the Eval functions may work for you, if the Rexx INTERPRET operation is limited to dynamic expression evaluation. Consult the thinBasic documentation for more information on the Eval functions.
- Rexx scripts tend to frequently use the PARSE facility. Often this is simply to grab arguments passed to a subroutine or to read values from a file or queue. However, you may run into cases where PARSE is being used in more complex ways. thinBasic has a PARSE function, which operates differently than Rexx, but it may be enough for simpler cases. Otherwise, you may have a bit of work to do converting this.
- Rexx has a novel way of storing string values, in which you can define a fixed "base" variable name and append a dynamic "stem" to it. Rexx uses this stem approach to substitute for arrays, which is doesn't support. If you have a Rexx script that uses stemmed variables, you may have considerable work to convert them. Often, Rexx stem variables are simply used as arrays, and thinBasic supports these natively. If your Rexx script does anything fancier than that with stems, you might be able to use SPFLite Globally Stored Data as a substitute for stem variables.
- ISPF has a way to store variable values in the "shared pool" or the "profile pool" using ISPEXEC VGET and ISPEXEC VPUT. If you have a script that does this, you will have to rewrite it. Here are some strategies for going about this:
- You can get edit Profile values using Get_Profile$, and can set Profile values using a SPF_CMD call, like SET_CMD("START FIRST")
- Other than standard Profile values, you cannot add user-defined values to an SPFLite edit profile
- For "shared variables", Globally Stored Data may be sufficient, as long as you understand it is not persistent across SPFLite instances. Once you close down SPFLite and restart it, the values will be gone.
- If you want to store persistent values, you can use Set_SETVAR and Get_SETVAR$ to access the SPFLite SET variable pool. If you do this, be careful not to inadvertently write over any existing values. You can use SET names that have dots in them; this allows you to form "qualified SET names". Adopt a consistent naming convention for the SET symbols you define this way.
- Because SPFLite SET names are persistent, you need to evaluate whether the script you are converting intended its variables to be persistent or not. If not, using SET names may not be not the right approach, and you may wish to use Globally Stored Data instead.
- For example, if you are converting a script that stored name ABC in the shared pool, and XYZ in the profile pool, you could use those pool names as SET name qualifiers. Keep in mind that if you call Get_SETVAR for an undefined name, the function will return an empty (null) string. Example:
Rexx
"ISPEXEC VGET ABC SHARED"
"ISPEXEC VGET XYZ PROFILE"
"ISPEXEC VPUT ABC SHARED"
"ISPEXEC VPUT XYZ PROFILE"
SPFLite
DIM ABC,XYZ AS STRING
ABC = Get_SETVAR ("SHARED.ABC")
XYZ = Get_SETVAR ("PROFILE.XYZ")
Set_SETVAR ("SHARED.ABC", ABC)
Set_SETVAR ("PROFILE.XYZ", XYZ)
Rexx |
thinBasic |
ADDRESS |
not needed |
ARG, PARSE ARG |
Get_Arg$ UCASE$(Get_Arg()) |
CALL routine |
routine [CALL keyword not used] |
DO |
see Program Control Structures below |
DROP |
REDIM may apply, but probably not needed |
EXIT |
HALT |
EXIT returnCode |
HALT (returnCode, "message") |
IF |
see Program Control Structures below |
INTERPRET |
thinBasic Eval functions may apply. See thinBasic help for more information. |
ITERATE |
ITERATE FOR|WHILE|DO thinBasic ITERATE cannot reference an outer nested block, but only the closest one |
LEAVE |
EXIT FOR|WHILE|DO thinBasic EXIT has a "number of times" operand to exit from multiple FOR/NEXT blocks |
LINEIN |
USES "FILE" various I/O statements; see thinBasic Help |
LINEOUT |
USES "FILE" various I/O statements; see thinBasic Help |
NOP (used for "do nothing" branches of IF statements) |
NOP -- thinBasic does not natively have a NOP statement, but SPFLite has added one. It is essentially a SUB named NOP that does nothing. -- |
NUMERIC |
not supported |
PARSE |
thinBasic has a number of parsing functions, including PARSE, PARSE$, PARSECOUNT and PARSESET$, as well as several other string functions. See the thinBasic documentation for more information. |
PROCEDURE |
SUB and FUNCTION |
PULL |
not supported |
PUSH |
not supported |
QUEUE |
not supported |
RAISE |
not supported. In Rexx, RAISE is sometimes used to simulate a GOTO statement, which thinBasic does not support either. It is possible some use of EXIT may be applied. |
RETURN [expression] |
RETURN [expression] |
SAY SAY string |
SPF_DEBUG SET_MSG MSGBOX USES "CONSOLE" PRINTL string ' displays on a console window ' PRINT instead of PRINTL will output the ' string without a line terminator. ' You can use CLS to clear the screen ' See CONSOLE module documentation for more ' details. |
SIGNAL |
not supported. In Rexx, SIGNAL is sometimes used to simulate a GOTO statement, which thinBasic does not support either. It is possible some use of EXIT may be applied. |
TRACE |
SPF_TRACE functionality is different |
The main difference here is that Rexx uses a common DO ... END structure for all blocks of code (like PL/1 which it is modeled after) while thinBasic has unique delimiters for each type of block. You will also notice that in Rexx you will often see an END prior to an ELSE, ELSE IF or WHEN/OTHERWISE clauses, whereas in thinBasic for ELSE, ELSEIF and CASE, that is not necessary.
Note that in thinBasic:
- ELSEIF is all one word
- END IF is two words, where the IF part is required
- END SELECT is two words, where the SELECT part is required
- In FOR/NEXT loops, NEXT does not require an index name afterwards like NEXT N. It's just NEXT. Specifying the N value is supported and is probably a good idea for documenting purposes.
- SELECT CASE defines a common expression, which is tested by each CASE clause, whereas a Rexx SELECT defines a test at each WHEN clause, and there is no common expression on the Rexx SELECT clause. This means there is more to do than merely changing keywords; may will have to restructure your SELECT statements. If your WHEN clauses compare against a common value, you can move that value into the SELECT CASE expression. Otherwise, you can specify this as SELECT CASE OF, and ensure that each WHEN clause is rewritten to be a CASE clause with an expression that produces a "truth" value of -1 or 0. You can ensure that by enclosing each expression in the thinBasic function IsTrue.
- The OF keyword is a bit of "magic" supported by SPFLite; just code it as is when converting Rexx SELECT statements.
- If you are converting a Rexx WHEN clause that is already in the form of a comparison like COUNT = 5, the expression will automatically produce a thinBasic "truth value" of -1 or 0, and so enclosing it in a thinBasic function IsTrue would not be required.
Rexx |
thinBasic |
statement1 ; statement2 |
statement1 : statement2 |
if cond then statement |
if cond then statement |
if cond1 then do statement1 end else if cond2 then statement2 end else do statement3 end |
if cond1 then statement1 elseif cond2 then statement2 else statement3 end if |
do i = 1 to 10 if cond1 then leave statement if cond2 then iterate end do i = 1 to 10 by -1 if cond1 then leave statement if cond2 then iterate end |
for i = 1 to 10 if cond1 then exit for statement if cond2 then iterate for next for i = 1 to 10 step -1 if cond1 then exit for statement if cond2 then iterate for next |
do while cond if cond1 then leave statement if cond2 then iterate end |
do while cond if cond1 then exit do statement if cond2 then iterate do loop |
do forever statement end |
do statement loop |
select when cond1 do statement1 end when var = value do statement2 end when var1 = value1, var2 = value2 do statement3 end /* ... */ otherwise do statement3 end end ' NOTE 1: In Rexx, a comma-separated expr ' list on WHEN implies an AND test |
select case of case IsTrue(cond1) statement1 case var = value statement2 case (var1 = value1) and (var2 = value2) statement3 case else statement3 end select ' NOTE 1: the "of" after SELECT CASE is a ' keyword supported only within SPFLite. ' It won't work in stand-alone thinBasic. ' NOTE 2: In thinBasic, a comma-separated expr ' list on CASE implies an OR test |
, (comma as line continuation) |
_ (underscore as line continuation) |
/* line comment */ /* block comment */ |
/* line comment */ (or) ' line comment /* block comment */ ' NOTE: Using /* */ as block comments is ' non-standard for BASIC, but thinBasic ' allows it. |
Operators that are the same are not mentioned
Rexx |
thinBasic |
& |
AND |
| |
OR |
|| (concatenate) |
& + |
/= \= ¬= <> >< |
<> |
<< <<= >> >>= == |
< <= > >= = |
¬ \ |
NOT |
one two (concatenation with blank) (one)(two) (abuttal) |
one & " " & two one&two |
/ (real divide) |
/ |
% (integer divide) |
\ |
** (power) |
^ |
&& |
XOR |
Rexx |
thinBasic |
/* REXX other comments */ "ISREDIT MACRO (arg1,arg2) PROCESS" |
' macname.MACRO DIM arg1,arg2 AS STRING arg1 = Get_Arg$(1) arg2 = Get_Arg$(2) |
/* REXX other comments */ "ISREDIT MACRO (arg1,arg2) NOPROCESS" "ISREDIT PROCESS RANGE C" |
' macname.MACRO DIM arg1,arg2 AS STRING DIM _ZFRANGE,_ZLRANGE AS NUMBER = 0 arg1 = Get_Arg$(1) arg2 = Get_Arg$(2) if is_line_cmd and _ (get_src_lcmd$="C" or get_src_lcmd$="CC") then _ZFRANGE = get_src1_lptr _ZLRANGE = get_src2_lptr end if |
Rexx |
thinBasic |
"ISREDIT AUTOSAVE ON|OFF PROMPT|NOPROMPT" |
SPF_CMD("AUTOSAVE ON|OFF PROMPT|NOPROMPT") |
"ISREDIT (var) = BLKSIZE" |
var = GET_PROFILE$("LRECL") |
"ISREDIT BOUNDS left right" |
SPF_CMD("BOUNDS left right") |
"ISREDIT (left,right) = BOUNDS" |
left = Get_Lbound right = Get_Rbound |
"ISREDIT (var) = CAPS" |
var = GET_PROFILE$("CAPS") |
"ISREDIT CAPS = ON|OFF" |
SPF_CMD("CAPS ON|OFF") |
"ISREDIT CHANGE operands" |
SPF_CMD("C operands") |
"ISREDIT (times) = CHANGE_COUNTS" "ISREDIT (times,lines) = CHANGE_COUNTS" |
DIM msg, times, lines AS STRING SPF_CMD("CHANGE operands") msg = GET_MSG$ times = PARSE$(msg, " ", 4) lines = PARSE$(msg, " ", 7) if lines = "" then lines = times |
"ISREDIT COPY operands" |
SPF_CMD("COPY operands") |
"ISREDIT (line,col) = CURSOR" |
line = Get_LNUM(Get_CSR_LPTR)) col = Get_CSR_COL |
"ISREDIT CURSOR = line col" |
SET_CSR(line,col,0) |
"ISREDIT CUT operands" |
SPF_CMD("CUT operands") |
"ISREDIT (var) = DATA_CHANGED" |
var = IIF$(GET_MODIFIED,"YES","NO") |
"ISREDIT DELETE operands" |
SPF_CMD("DELETE operands") |
"ISREDIT EXCLUDE operands" |
SPF_CMD("EXCLUDE operands") SPF_CMD("X operands") |
"ISREDIT FIND operands" |
SPF_CMD("FIND operands") SPF_CMD("F operands") |
"ISREDIT (times) = FIND_COUNTS" "ISREDIT (times,lines) = FIND_COUNTS" |
DIM msg, times, lines AS STRING SPF_CMD("FIND operands") msg = GET_MSG$ times = PARSE$(msg, " ", 4) lines = PARSE$(msg, " ", 7) if lines = "" then lines = times |
"ISREDIT FLIP operands" |
SPF_CMD("FLIP operands") |
"ISREDIT HEX = ON|OFF" |
SPF_CMD("HEX ON|OFF") |
"ISREDIT var = HEX" |
var = GET_PROFILE$("HEX") |
"ISREDIT HIDE operand" |
SPF_CMD("HIDE") |
"ISREDIT RESET HIDE" |
SPF_CMD("RESET HIDE") |
"ISREDIT HILITE [ON|OFF] [AUTO] [FIND]" |
SPF_CMD("HILITE [ON|OFF] [AUTO] [FIND]") |
"ISREDIT INSERT label|linenum [lines]" |
SPF_CMD("LINE I" & TSTR$(lines) & label) SPF_CMD("LINE N" & TSTR$(lines) & label) |
"ISREDIT (var) = LABEL .label|linenum" |
var = GET_Label$(GET_LPTR(label)) |
"ISREDIT LABEL .label|linenum = .newlabel |
SPF_CMD("LINE '.newlabel' .label") |
"ISREDIT (var) = LINE .label|linenum" |
var = GET_LINE$(GET_LPTR(label_or_linenum)) |
"ISREDIT LINE .label|linenum = data" |
SET_LINE(GET_LPTR(label_or_linenum),data) |
"ISREDIT LINE_AFTER .label|linenum = data" |
SPF_CMD("LINE N label_or_linenum") SET_LINE(GET_LPTR(label_or_linenum)+1,data) |
"ISREDIT LINE_AFTER .label|linenum = NOTELINE data" |
SPF_CMD("LINE NOTE label_or_linenum") SET_LINE(GET_LPTR(label_or_linenum)+1,data) |
"ISREDIT LINE_BEFORE .label|linenum = data" |
DIM LPTR AS NUMBER LPTR = GET_LPTR(.label|linenum) - 1 IF LPTR > 0 THEN SPF_CMD("LINE N !" & TSTR$(LPTR)) SET_LINE(LPTR+1, data) END IF |
"ISREDIT LINE_BEFORE .label|linenum = NOTELINE data" |
DIM LPTR AS NUMBER LPTR = GET_LPTR(.label|linenum) - 1 IF LPTR > 0 THEN SPF_CMD("LINE NOTE !" & TSTR$(LPTR)) SET_LINE(LPTR+1, data) END IF |
"ISREDIT (var) = LINENUM .label" |
var = GET_LNUM(GET_LPTR(".label")) |
"ISREDIT LOCATE operands" |
SPF_CMD("LOCATE operands") |
"ISREDIT (var) = LRECL" |
var = GET_PROFILE$("LRECL") |
"ISREDIT (var) = MEMBER" |
var = GET_FILEBASE$ |
"ISREDIT PASTE operands" |
SPF_CMD("PASTE operands") |
"ISREDIT PRESERVE ON|OFF|C" |
SPF_CMD("PRESERVE ON|OFF|C") |
"ISREDIT (var) = PRESERVE" |
var = GET_PROFILE$("PRESERVE") |
"ISREDIT PROCESS [DEST] [RANGE cmd1 [cmd2]]" |
The ISPF line command names cmd1 and cmd2 can be up to 6 characters and can contain any alphabetic or special character except blank, hyphen (-), or apostrophe (’). SPFLite line command names are not predeclared in this way, but use the GET_SRC_LCMD$ and GET_DEST_LCMD$ functions. You cannot define arbitrary line command names for use in a macro the way ISPF does this. If you use the macro as a primary command, possible source commands are C/CC and M/MM, while line-command macros use the name of the macro itself as the name of the source line range. Possible destination commands are A/AA, B/BB, H/HH, W/WW, O/RR and OR/ORR for both types of macros. SPFLite line command names are currently limited to four characters. |
"ISREDIT (var) = RANGE_CMD" |
var = GET_SRC_LCMD$ -- or -- var = GET_DEST_LCMD$ |
"ISREDIT RCHANGE" |
SPF_CMD("RCHANGE") |
"ISREDIT (var) = RECFM" |
var = GET_PROFILE$("RECFM") |
"ISREDIT REPLACE operands" |
SPF_CMD("REPLACE operands") |
"ISREDIT RFIND" |
SPF_CMD("RFIND") |
"ISREDIT SEEK operands" |
SPF_CMD("FIND operands DX") |
"ISREDIT (times) = SEEK_COUNTS" "ISREDIT (times,lines) = SEEK_COUNTS" |
DIM msg, times, lines AS STRING SPF_CMD("FIND operands DX") msg = GET_MSG$ times = PARSE$(msg, " ", 4) lines = PARSE$(msg, " ", 7) if lines = "" then lines = times |
"ISREDIT (var) = SESSION" -- may return EDIT or VIEW -- |
var = GET_SESSION_TYPE$ -- may return EDIT, BROWSE or other values -- |
ZerrSm = "short message" ZerrLm = "long message" "ISPEXEC "SETMSG MSG(msgCode) |
Set_Msg ([rc,] ZerrLm) -- see notes above about ISPEXEC SETMSG -- |
"ISREDIT SETUNDO operands" |
SPF_CMD("SETUNDO number") -- SPFLite SETUNDO not compatible with ISPF -- |
"ISREDIT SHIFT ( lnum_or_label [n]" "ISREDIT SHIFT ) lnum_or_label [n]" "ISREDIT SHIFT < lnum_or_label [n]" "ISREDIT SHIFT > lnum_or_label [n]" |
SPF_CMD("LINE (n !" & GET_LPTR(lnum_or_label))) SPF_CMD("LINE )n !" & GET_LPTR(lnum_or_label))) SPF_CMD("LINE <n !" & GET_LPTR(lnum_or_label))) SPF_CMD("LINE >n !" & GET_LPTR(lnum_or_label))) |
"ISREDIT SORT operands" |
SPF_CMD("SORT operands") |
"ISREDIT SOURCE operand" "ISREDIT (var) = SOURCE" "ISREDIT RESET SOURCE" |
SPF_CMD("SOURCE operand") var = GET_PROFILE$("SOURCE") SPF_CMD("RESET SOURCE") |
"ISREDIT SUBMIT operands" |
SPF_CMD("SUBMIT operands") |
"ISREDIT TABS ON|OFF" "ISREDIT (var) = TABS" |
SPF_CMD("TABS ON|OFF") var = GET_PROFILE$("TABS") |
"ISREDIT (var) = TABSLINE" |
DIM var AS STRING SPF_CMD("LINE TABS !2") var = GET_LINE$(3) SPF_CMD("LINE D !3") |
"ISREDIT = TABSLINE = data" |
SPF_CMD("LINE TABS !2") SET_LINE$(3,data) SPF_CMD("LINE D !3") |
"ISREDIT TFLOW lnum_or_label [n]" |
SPF_CMD("LINE TF" & n & "lnum_or_label") |
"ISREDIT TSPLIT lnum_or_label [n]" |
SPF_CMD("LINE TS" & n & "lnum_or_label") |
Rexx |
thinBasic |
abbrev(longString,shortString) |
StartsWith(longString,shortString,0) |
abs(value) |
abs(value) |
address() |
n/a |
arg() |
get_arg_count |
arg(n) |
get-arg$(n) |
center(string,len) center(string,len,pad) |
cset$(string,len) cset$(string,len,pad) |
changestr(needle,haystack,newneedle) |
replace$(haystack,needle,newneedle) |
copies(string,n) |
repeat$(n,string) |
countstr(needle,haystack) |
tally(haystack,needle) |
date() |
date$() (format operands are different) |
delstr(string,pos,len) |
strdelete$(string,pos,len) |
str = directory() directory(newdirectory) |
USES "FILE" str = DIR_GetCurrent USES "FILE" DIR_Change(newdirectory) |
filespec("Drive",fullpath) filespec("Path",fullpath) filespec("Location",fullpath) filespec("Name",fullpath) filespec("Extension",fullpath) |
USES "FILE" ' -- for all calls LEFT$(FILE_PATHSPLIT(fullpath,%Path_root),2) MID$(FILE_PATHSPLIT(fullpath,%Path_Rootpath),3) FILE_PATHSPLIT(fullpath,%Path_Rootpath) FILE_PATHSPLIT(fullpath,%Path_FileExt) FILE_PATHSPLIT(fullpath,%Path_Ext) |
format() |
format$() (format operands are different) |
insert(new,target,pos) |
strinsert$(target,new,pos) |
left(string,len) left(string,len,pad) |
left$(string,len) lset$(left$(string,len),len,pad) |
lastpos(needle,haystack) lastpos(needle,haystack,start) |
instr(-1,haystack,needle) instr(start-len(haystack)-1,haystack,needle) |
length(string) |
len(string) |
lower(string) |
lcase$(string) |
max(str1,str2,etc) max(num1,num2,etc) |
max$(str1,str2,etc) max(num1,num2,etc) |
min(str1,str2,etc) min(num1,num2,etc) |
min$(str1,str2,etc) min(num1,num2,etc) |
pos(needle,haystack) pos(needle,haystack,start) |
instr(haystack,needle) instr(start,haystack,needle) |
reverse(string) |
strreverse$(string) |
right(string,len) |
right$(string,len) |
sign(number) |
sgn(number) |
space(string) space(string,n) space(string,0) |
trimfull$(string) replace$(trimfull$(string)," ",lset$("",3)) remove$(string, " ") |
strip(string) strip(string,"Both") strip(string,"Leading") strip(string,"Trailing") strip(string,,char) strip(string,"Both",char) strip(string,"Leading",char) strip(string,"Trailing",char) |
trim$(string) trim$(string) ltrim$(string) rtrim$(string) trim$(string,char) trim$(string,char) ltrim$(string,char) rtrim$(string,char) |
substr(data,pos,len) substr(data,pos) |
mid$(data,pos,len) mid$(data,pos) |
time() |
time$ (format is different) |
translate(string) |
ucase$(string) |
translate(string,newCharSet,oldCharSet) |
replace$(string, ANY oldCharSet, newCharSet) |
trunc(string) ' trunc is often used to extract a "qualified ' option" like "3" from a string like "3.4" |
extract$(string,".") |
upper(string) |
ucase$(string) |
verify(string,charset) |
verify(,string,charset) |
Created with the Personal Edition of HelpNDoc: Maximize Your Productivity with a Help Authoring Tool