Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Any Way to Pass Character String Arguments from C to Basic?

186 views
Skip to first unread message

Craig Dedo

unread,
Oct 21, 2019, 11:31:33 AM10/21/19
to
Is there any way to pass character string arguments from a C program or procedure to a procedure written in Basic? For both languages I'm using the current versions of the languages:
VSI C V7.4-002 on OpenVMS Alpha V8.4-2L2
VSI BASIC V1.8-005 on OpenVMS V8.4-2L2

I need to pass character string variables from a main program or procedure written in C to a subroutine written in Basic and get back character string data from the Basic subroutine. I had to write the subroutine in Basic because it needs to call a utility subroutine in Basic that uses arrays of varying-length character strings.

Everything compiles and links correctly; no errors or warnings at all. When I run the program everything works fine until I try to assign character string data back into character string arguments that are associated with character strings in the C program.
* Assignment of character string arguments from the C program to variables in the Basic subroutine works fine.
* Assignment of integer results from the Basic subroutine to the integer arguments in the C program works fine.
* BUT when I try to assign character string values to a character string argument in the C program, I get a fatal error:
%STR-F-FATINTERR, fatal internal error

I have already documented the fatal error and reported it to our firm's OpenVMS system manager who reported it to VMS Support at VSI a few minutes ago.

While we are waiting for a reply from VSI, is there any way around this defect in the String module of the OpenVMS Run Time Library? Here are the procedure interfaces:

C side (caller):
void Get_DIA_Return_Eligibility ( struct dsc$descriptor_d *Customer_Location_Code_adsc, struct dsc$descriptor_d *Part_Number_adsc, long *DR3_Quantity_al, long *DR2_Quantity_al, struct dsc$descriptor_d *Oldest_Invoice_Number_adsc, struct dsc$descriptor_d *SQL_Status_adsc, struct dsc$descriptor_d *SQL_Message_adsc );

Basic side (callee):
Sub Get_DIA_Return_Eligibility ( String Customer_Location_Code_as, String Part_Number_as, Long DR3_Quantity_al, Long DR2_Quantity_al, String Oldest_Invoice_Number_as, String SQL_Status_as, String SQL_Message_as )

BTW, this is NOT an academic assignment. It is a real production problem in a commercial environment with real profit and loss responsibility and real money at stake.

Any help would be very much appreciated.

Sincerely,
Craig T. Dedo. OpenVMS Software Development Consultant
Navistar, Inc.
2701 Navistar Drive
Lisle, IL 60532

Robert A. Brooks

unread,
Oct 21, 2019, 12:18:10 PM10/21/19
to
On 10/21/2019 11:31 AM, Craig Dedo wrote:


> Everything compiles and links correctly; no errors or warnings at all.
> When I run the program everything works fine until I try to assign character string data back into character string arguments> that are associated with character strings in the C program.
> * Assignment of character string arguments from the C program to variables in the Basic subroutine works fine.
> * Assignment of integer results from the Basic subroutine to the integer arguments in the C program works fine.

> * BUT when I try to assign character string values to a character string argument in the C program, I get a fatal error:
> %STR-F-FATINTERR, fatal internal error
To be clear, you get this error not when you are calling the BASIC subroutine, but when you attempt to do the string assignment in the C source
prior to calling the BASIC subroutine?

Let's see the C actual source.

I don't see this support call in our internal bug tracking system yet.

--

-- Rob

Bob Gezelter

unread,
Oct 21, 2019, 12:38:03 PM10/21/19
to
Craig,

The key here is that BASIC is expecting strings to be passed by descriptor. The C default is by reference.

One must also be careful about the difference between read only and read/write.

Look in the C include files for the definition of $DESCRIPTOR. This allows the definition of read-only strings with a descriptor. Writable strings have to be defined differently.

I hope that the above is helpful. If I have not been clear, contact me on or off list.

- Bob Gezelter, http://www.rlgsc.com

Dave Froble

unread,
Oct 21, 2019, 1:26:28 PM10/21/19
to
It may be that he needs to determine what type of string descriptor is
used in Basic. I got bit by this in the past.

Use different types of descriptors, and, things can get interesting.


--
David Froble Tel: 724-529-0450
Dave Froble Enterprises, Inc. E-Mail: da...@tsoft-inc.com
DFE Ultralights, Inc.
170 Grimplin Road
Vanderbilt, PA 15486

Craig Dedo

unread,
Oct 21, 2019, 1:43:24 PM10/21/19
to
Bill Hall, our OpenVMS system manager, submitted the bug report at 10:07 a.m. Central Time (11:07 a.m. Eastern Time) today.

No, this error is in the Basic subroutine, NOT the C program calling it. Here are the relevant statements in the Basic subroutine:
Declare String Output_Results_lsa ( nItems_lkl )
. . .
Call Returns_Elig ( Procedure_Name_ls, Input_Data_lsa(), Output_Results_lsa(), Message_ls )
DR3_Quantity_al = Integer ( Output_Results_lsa (1), Long )
DR2_Quantity_al = Integer ( Output_Results_lsa (2), Long )
Oldest_Invoice_Number_as = Output_Results_lsa (3)

The suffixes _ls and _lsa refer to "local string" and "local string array", respectively.

The error occurs at the assignment to Oldest_Invoice_Number_as. Interestingly, the value is assigned properly before the error condition is triggered. I know that from looking at the variables in the OpenVMS debugger.

The string variables are passed from C to basic using variable-length string descriptors. The reading of the input string variables, passed the same way, works properly but not assigning to string variables.

Craig Dedo

unread,
Oct 21, 2019, 2:00:43 PM10/21/19
to
I forgot to include the two statements before the statement Call Returns_Elig().
Input_Data_lsa (1) = Customer_Location_Code_as
Input_Data_lsa (2) = Part_Number_as

Both of these statements execute flawlessly. All three string variables, Customer_Location_Code_as, Part_Number_as, and Oldest_Invoice_Number_as are passed in exactly the same manner.

VAXman-

unread,
Oct 21, 2019, 2:10:41 PM10/21/19
to
In article <41fb3094-0be6-4e52...@googlegroups.com>, Craig Dedo <craig...@gmail.com> writes:
>On Monday, October 21, 2019 at 11:18:10 AM UTC-5, Robert A. Brooks wrote:
>> On 10/21/2019 11:31 AM, Craig Dedo wrote:
>>
>>
>> > Everything compiles and links correctly; no errors or warnings at all.
>> > When I run the program everything works fine until I try to assign character string data back into character string arguments> that are associated with character strings in the C program.
>> > * Assignment of character string arguments from the C program to variables in the Basic subroutine works fine.
>> > * Assignment of integer results from the Basic subroutine to the integer arguments in the C program works fine.
>>
>> > * BUT when I try to assign character string values to a character string argument in the C program, I get a fatal error:
>> > %STR-F-FATINTERR, fatal internal error
>> To be clear, you get this error not when you are calling the BASIC subroutine, but when you attempt to do the string assignment in the C source
>> prior to calling the BASIC subroutine?
>>
>> Let's see the C actual source.
>>
>> I don't see this support call in our internal bug tracking system yet.
>>
>> --
>>
>> -- Rob
>
>Bill Hall, our OpenVMS system manager, submitted the bug report at 10:07 a.m. Central Time (11:07 a.m. Eastern Time) today.
>
>No, this error is in the Basic subroutine, NOT the C program calling it. Here are the relevant statements in the Basic subroutine:
>Declare String Output_Results_lsa ( nItems_lkl )
>.. . .
>Call Returns_Elig ( Procedure_Name_ls, Input_Data_lsa(), Output_Results_lsa(), Message_ls )
>DR3_Quantity_al = Integer ( Output_Results_lsa (1), Long )
>DR2_Quantity_al = Integer ( Output_Results_lsa (2), Long )
>Oldest_Invoice_Number_as = Output_Results_lsa (3)
>
>The suffixes _ls and _lsa refer to "local string" and "local string array", respectively.
>
>The error occurs at the assignment to Oldest_Invoice_Number_as. Interestingly, the value is assigned properly before the error condition is triggered. I know that from looking at the variables in the OpenVMS debugger.
>
>The string variables are passed from C to basic using variable-length string descriptors. The reading of the input string variables, passed the same way, works properly but not assigning to string variables.
>
>Sincerely,
>Craig T. Dedo. OpenVMS Software Development Consultant
>Navistar, Inc.
>2701 Navistar Drive
>Lisle, IL 60532

Try calling STR$COPY_R in the C code to a dynamic string descriptor.

==>> STR$COPY_R destination-string ,word-integer-source-length, source-string-address

destination-string will need tobe a dynamic string descriptor which C knows
NOTHING about but you can easily compose one. The word-integer-source-length
will the the length of the C sting and source-string-address will be the addy
of the C string.

Then, call the BASIC routine passing it the destination-string dynamic string
descriptor. Be sure you invoke STR$FREE1_DX on the dynamic string descriptor
when you are done with the dynamic string or you *could* risk leaking memory.

--
VAXman- A Bored Certified VMS Kernel Mode Hacker VAXman(at)TMESIS(dot)ORG

I speak to machines with the voice of humanity.

John Reagan

unread,
Oct 21, 2019, 2:25:52 PM10/21/19
to
Like Rob, I want to see the C code that is building the descriptor and that you are building a CLASS_D, not a CLASS_S. Plus the descriptor needs to be writeable since the BASIC code (via the STR$ routines) will want to update it.

And who says descriptors make things easier?

Craig Dedo

unread,
Oct 21, 2019, 3:19:42 PM10/21/19
to
As you asked, here are the three descriptor definitions.

struct dsc$descriptor_d Customer_Location_Code_ldsc = { sizeof (Customer_Location_Code_lsz) - 1, DSC$K_DTYPE_T, DSC$K_CLASS_D, Customer_Location_Code_lsz };

struct dsc$descriptor_d Oldest_Invoice_Number_ldsc = { sizeof (Oldest_Invoice_Number_lsz) - 1, DSC$K_DTYPE_T, DSC$K_CLASS_D, Oldest_Invoice_Number_lsz };

struct dsc$descriptor_d Part_Number_ldsc = { sizeof (Part_Number_lsz) - 1, DSC$K_DTYPE_T, DSC$K_CLASS_D, Part_Number_lsz };

John Reagan

unread,
Oct 21, 2019, 4:27:08 PM10/21/19
to
For CLASS_D, consider what happens if one of those STR$ routines needs to extend the string... It will allocate new memory, copy the bytes as needed, and then attempt to free the existing buffer pointed to by the descriptor... And that's what you hit.

For CLASS_D, you have to let the underlying routines manage the data completely.

Stephen Hoffman

unread,
Oct 21, 2019, 7:07:13 PM10/21/19
to
On 2019-10-21 19:19:40 +0000, Craig Dedo said:

> As you asked, here are the three descriptor definitions.
>
> struct dsc$descriptor_d Customer_Location_Code_ldsc = { sizeof
> (Customer_Location_Code_lsz) - 1, DSC$K_DTYPE_T, DSC$K_CLASS_D,
> Customer_Location_Code_lsz };
>
> struct dsc$descriptor_d Oldest_Invoice_Number_ldsc = { sizeof
> (Oldest_Invoice_Number_lsz) - 1, DSC$K_DTYPE_T, DSC$K_CLASS_D,
> Oldest_Invoice_Number_lsz };
>
> struct dsc$descriptor_d Part_Number_ldsc = { sizeof (Part_Number_lsz) -
> 1, DSC$K_DTYPE_T, DSC$K_CLASS_D, Part_Number_lsz };

That user C code is broken. Badly.

You're also tunneled in on the BASIC code tipping over, and that's due
to it being passed garbage input.

RTL calls that can allocate a dynamic descriptor include lib$sget1_dd,
lib$scopy_r_dx, lib$scopy_r_dx_64, lib$analyze_sdesc, and others. See
also lib$sfree1_dd, too.

A developer is not permitted to initialize a dynamic descriptor with a
non-zero length and with any associated storage. Only with a
zero-length and a zero buffer pointer, when creating a dynamic
descriptor in C, C++, Macro32, etc.

struct dsc$descriptor_d Oldest_Invoice_Number_ldsc = { 0,
DSC$K_DTYPE_T, DSC$K_CLASS_D, NULL };

Then lib$scopy_r_dx the data into the descriptor or such, and the RTL
call will allocate the storage.

From then on, it's only RTL calls that can allocate or deallocate
storage or otherwise change the length of the dynamic descriptor.

And C, C++, or Macro32 code can need to deallocate a dynamic string, if
there's associated storage. That's via lib$sfree1_dd or analogous.
Otherwise the code can or will leak storage.

Yes, allocating stack or user-controlled storage is permitted with and
is required with static descriptors. But dynamic descriptors are
allocated from the heap, and the associated storage is entirely managed
by the RTL.

You've effectively corrupted the heap with this pattern, when
subsequent RTL calls try to free or try to re-size the storage.

Which is why the called BASIC code is crashing.

Whether the calling code can contend with a static descriptor as
input—as differentiated from a dynamic descriptor—will depend on
whether the calling code tries to re-size the descriptor. Given that
the called BASIC code is crashing, the called BASIC code is likely
modifying the descriptor. Maybe for a return? And that's where the
called code is failing. As it should fail really, as it was passed an
invalid dynamic descriptor.

C on OpenVMS is a mess here, and is still poorly documented and this
having just checked the
https://vmssoftware.com/docs/VSI_OpenVMS_BaseOS/5492_c_users_gd.pdf C
User's Guide. (Effectively no mentions of using dynamic descriptors,
and little or none of 64-bit addressing and 64-bit descriptors, etc.
You're assumed to know the calling standard here, unfortunately. All
discussions of C compiler extension gaps and C library descriptor gaps
aside, calling into BASIC and Fortran and ilk from C or C++ can mean
absurd heaps of glue code.

Unfortunately for any potential future remediation efforts that might
arise here, OpenVMS-style descriptors are dated and limited. Efforts
toward hauling C and C++ and the existing OpenVMS APIs forward are an
increasingly-larger project, and one that may well involve far more
work and updates well past descriptors should VSI decide to haul this
whole area forward. There are other APIU design approaches which are
more flexible and more capable and much better for upward
compatibility, such as the CLR
https://docs.microsoft.com/en-us/dotnet/standard/clr and as the CLR
looks a great deal like the OpenVMS calling standard evolved forward a
decade or three. There are other examples from other vendors.

You might also want to mention which calls are involved in the BASIC
code, as those may well also be available from C. That'll eliminate
the BASIC code and the descriptors.



--
Pure Personal Opinion | HoffmanLabs LLC

Stephen Hoffman

unread,
Oct 21, 2019, 7:11:58 PM10/21/19
to
On 2019-10-21 23:07:08 +0000, Stephen Hoffman said:

> RTL calls that can allocate a dynamic descriptor include lib$sget1_dd,
> lib$scopy_r_dx, lib$scopy_r_dx_64, lib$analyze_sdesc, and others. See
> also lib$sfree1_dd, too.

Yeah, I know, lib$analyze_sdesc doesn't allocate storage. It's how C
code can read a descriptor "directly"; without having to code for the
various different sorts of descriptors.

Arne Vajhøj

unread,
Oct 21, 2019, 11:02:18 PM10/21/19
to
On 10/21/2019 11:31 AM, Craig Dedo wrote:
I think other already have answered your question.

But here is some complete code:

$ type b.bas
sub f1(string s)

print 's=' + s

end sub

sub f2(string s)

s = 'Some very long text'

end sub
$ bas b
$ type c.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <descrip.h>
#include <lib$routines.h>

void f1(struct dsc$descriptor *s);
void f2(struct dsc$descriptor *s);

int main(int argc,char *argv[])
{
struct dsc$descriptor_d ss;
struct dsc$descriptor_d ds;
char *tmp;
char *test = "Test";
int testlen = strlen(test);

ss.dsc$w_length = testlen;
ss.dsc$b_dtype = DSC$K_DTYPE_T;
ss.dsc$b_class = DSC$K_CLASS_S;
ss.dsc$a_pointer = test;
printf("call f1 with S descriptor:\n");
f1((struct dsc$descriptor *)&ss);

lib$sget1_dd(&testlen, &ds);
memcpy(ds.dsc$a_pointer, test, testlen);
printf("call f1 with D descriptor:\n");
f1((struct dsc$descriptor *)&ds);

printf("call f2 with D descriptor:\n");
printf("before length=%d pointer=%p\n", ds.dsc$w_length,
ds.dsc$a_pointer);
f2((struct dsc$descriptor *)&ds);
printf("after length=%d pointer=%p\n", ds.dsc$w_length,
ds.dsc$a_pointer);
tmp = malloc(ds.dsc$w_length + 1);
memcpy(tmp, ds.dsc$a_pointer, ds.dsc$w_length);
tmp[ds.dsc$w_length] = '\0';
printf("string = %s\n", tmp);
free(tmp);
//
return 0;
}
$ cc c
$ link c+b
$ run c
call f1 with S descriptor:
s=Test
call f1 with D descriptor:
s=Test
call f2 with D descriptor:
before length=4 pointer=34804
after length=19 pointer=34824
string = Some very long text

Arne

Jonathan

unread,
Oct 22, 2019, 2:39:32 PM10/22/19
to
If you don't want to use descriptors in C, you can alias the argument types in BASIC.
b1 is safe, b2 and b3 open to error, but more useful/fast for some purposes.

JUC$ type/nopage c.c
#include <string.h>

void b1(char *s, short n);
void b2(char *s, short n);
void b3(char *s, short n);

int main(int argc,char *argv[])
{
char *test = "Test";
int testlen = strlen(test);

b1(test,testlen);
b2(test,testlen);
b3(test,testlen);

return 0;
}

JUC$ type/nopage b.bas
function long b1( long a by value, word n by value )

%include "$DSCDEF" %from %library "SYS$SHARE:BASIC$STARLET"

external long function b1a( DSCDEF1 )

declare DSCDEF1 dsc

dsc::DSC$A_POINTER = a
dsc::DSC$W_MAXSTRLEN = n
dsc::DSC$B_DTYPE = DSC$K_DTYPE_T
dsc::DSC$B_CLASS = DSC$K_CLASS_S

end function b1a( dsc )


function long b1a( string s )

print "s=";s

end function


function long b2( string x = 32767 by ref, word n by value )

external long function b1a( string )

end function b1a( left$( x, n ) )



function long b3( byte b(32766) by ref, word n by value )

print "s=";
for i = 0 to n - 1
print chr$(b(i));
next i
print

end function
JUC$ cc c
JUC$ basic b
JUC$ link c+b
JUC$ run c
s=Test
s=Test
s=Test

seasoned_geek

unread,
Oct 23, 2019, 8:51:16 AM10/23/19
to
On Monday, October 21, 2019 at 10:31:33 AM UTC-5, Craig Dedo wrote:
> Is there any way to pass character string arguments from a C program or procedure to a procedure written in Basic? For both languages I'm using the current versions of the languages:
> VSI C V7.4-002 on OpenVMS Alpha V8.4-2L2
> VSI BASIC V1.8-005 on OpenVMS V8.4-2L2
>

You __really__ need to get a copy of this book.

https://theminimumyouneedtoknow.com/app_book.html

0 new messages