2

I have a rather simple Pascal program, that is to call some C functions I have written.

My C code:

void connect_default(int* errorcode)
{
    // Connects the default user and places any error code in errorcode
}

void connect(char user[129], char password[129], int* errorcode)
{
    // Connects the given user and places any error code in errorcode
}

I'm trying to call these two functions in my Pascal application. This works fine:

program pascaltest(INPUT, OUTPUT);
procedure connect_default(var errorcode: integer); external;

var
errorcode : integer := 0;

begin
    connect_default(errorcode);
    if errorcode <> 0 then
        writeln('Failed to connect with error code ', errorcode);
end.

But I have a hard time finding out what data type to use in Pascal, that corresponds to a null terminated char array in C. A Pascal string does not seem to be it, because this passes nothing to the C function.

program pascaltest(INPUT, OUTPUT);
procedure connect(user : string, password : string, var errorcode: integer); external;

var
errorcode : integer := 0;

begin
    connect('MyUser', 'MyPassword', errorcode);
    if errorcode <> 0 then
        writeln('Failed to connect with error code ', errorcode);
end.

What datatype in Pascal corresponds to a null terminated C char array? My environment is a HP OpenVMS machine and not Free Pascal, meaning that I do not have access to the types pchar and ansistring that I have read about.

The C functions need to stay as general as possible and I cannot make any changes to them, creating custom structs (like what is described here Declaring Pascal-style strings in C), as the C functions are already successfully called by similar programs written in C, Fortran and Cobol, where I managed to find the data types needed.

Helena
  • 1,041
  • 2
  • 12
  • 24
  • Respect for keeping the platform alive! :D – cxw Mar 22 '18 at 12:11
  • Wasn't Pascal string length stored to the first byte of the array? Couldn't you just move the whole string with `memmove` one byte left, and set the terminating `'\0'` to the end. – Janne Tuukkanen Mar 22 '18 at 12:14
  • @JanneTuukkanen as described at the end of the post, I cannot make any changes to the C functions. Such a solution would probably work for Pascal, but then I would mess the C functions up for the calling Fortran and Cobol programs, that do not store the string length in the first byte. – Helena Mar 22 '18 at 12:17
  • Some pascal compilers can use the `PChar` type, which is a null terminated character array. `connect(PChar('MyUser'), ...` – LU RD Mar 22 '18 at 12:34
  • It seems the `OpenVMS Pascal` has a `C_STR_T` type, which holds a null terminated array of char, and some helper functions, for example `MALLOC_C_STR()`, which allocates a `C_STR_T` from a string and returns a pointer to the structure. – LU RD Mar 22 '18 at 12:53
  • @LURD: actually, a `PChar` is ***not*** a null terminated array at all. It is simply a pointer to a `Char`, but in C, that is how you point to the beginning or a zero terminated array of characters that form a string. So `PChar` is probably the binary equivalent of the C `char *`. But it depends on the version of Pascal whether that means it can be interpreted as a "C string". – Rudy Velthuis Mar 25 '18 at 02:43
  • @RudyVelthuis, I don't know what you are getting at, but in Delphi, FreePascal and Turbo Pascal, a `PChar` type defines a null-terminated string. – LU RD Mar 25 '18 at 05:54
  • @LURD: no, it doesn't. It is interpreted as such, and there are implicit conversions from `PChar` to `string`, but a `PChar` is still just a `^Char`. See ["PChars: no strings attached"](http://rvelthuis.de/articles/articles-pchars.html). It is certainly **not an array**, it just often **points** to a possibly zero terminated array. But that doesn't mean anything in the Pascal mentioned here anyway. – Rudy Velthuis Mar 25 '18 at 15:07
  • @RudyVelthuis, you may twist and shout, but docs definitely says that it defines a null-terminated string. – LU RD Mar 25 '18 at 17:57
  • @LURD: I am not "twisting" anything. It is a simple fact that a `PChar` **is not an array**. It is a **pointer** to a char that often happens to be the first one of a zero-terminated string (or, in many routines, pointing *into* one). But it is **never, in any way, an array**. Nor is it a string. It just often points to one. Docs merely say that a PChar (often) *defines* a zero-terminated string. Not that it **is** one. – Rudy Velthuis Mar 25 '18 at 19:07
  • @RudyVelthuis, I did not say anything else than quote documentation. I can't see why you can't accept that. This is not Embas forum where you can twist everything for the sake of argumentation. – LU RD Mar 26 '18 at 07:46

2 Answers2

2

As you can see in official HP documentation, you Pascal version supports null-terminated strings: http://h41379.www4.hpe.com/doc/82final/6083/6083pro_005.html#null_strings

2.7 Null-Terminated Strings

HP Pascal includes routines and a built-in type to better coexist with null-terminated strings in the C language. The C_STR_T datatype is equivalent to:

C_STR_T = ^ ARRAY [0..0] OF CHAR;

C_STR_T is a pointer to an ARRAY OF CHARs. It does not allocate memory for any character data. C_STR_T behaves like a normal pointer type in that you can assign NIL into it and the optional pointer checking code will check for dereferencing of a NIL pointer. The individual characters can be used by dereferencing the pointer and using an array index.

In these cases, no bounds checking will be performed even if array bounds checking is enabled. However, you cannot dereference a C_STR_T pointer without also indexing a single character. If you want to access an entire null-terminated string, see the PAS_STR function.

Then you can use that C-style strings as a read-only references or convert them into standalone Pascal strings via PAS_STR function: http://h41379.www4.hpe.com/doc/82final/6083/6083pro_016.html#pas_str_func

And PAS_STRCPY would do opposite thing right away: http://h41379.www4.hpe.com/doc/82final/6083/6083pro_016.html#pas_strcpy

Yury Schkatula
  • 5,291
  • 2
  • 18
  • 42
  • Thank you very much for pointing me in the right direction! Do you have any experience with it? I've been trying this now for a couple of hours, and I don't understand what C receives, but it's not right. If I call C_STR('MyUser'), place it in a C_STR_T variable and then send that to my C function, when I use printf in the C function on the parameter I received it prints the character "x". – Helena Mar 22 '18 at 15:07
  • I'm not with Pascal for many years and have no OpenVMS experience at all. Same time, I can say you need to use MALLOC_C_STR('MyUser') that would return proper C_STR_T result, see http://h41379.www4.hpe.com/doc/82final/6083/6083pro_015.html#malloc_func – Yury Schkatula Mar 22 '18 at 16:15
  • I thought so too, so I tried both. Using MALLOC_C_STR instead does produce more than one character, but the characters are still wrong. I think I will have to investigate the options to my compiler. – Helena Mar 23 '18 at 07:20
1

I found a workaround for now that does not affect my C code at all, but is not very dynamic. In my specific case, the user name and password are hard-coded.

I simply use a character array in Pascal, and set the characters one by one. It is however not a good solution in the general case, and I want to accept Yury Schkatula's answer as the correct one. That is the intended use. I cannot, however, since I never got it to work. I will update this thread if I find out why.

program pascaltest(INPUT, OUTPUT);

type username = array [0..6] of char;
type password = array [0..10] of char;

procedure connect(var user : username, var pass : password, var errorcode: integer); external;

var
errorcode : integer := 0;
user : username;
pass : password;

begin
   user[0] := 'M';
   user[1] := 'y';
   user[2] := 'U';
   user[3] := 's';
   user[4] := 'e';
   user[5] := 'r';
   user[6] := chr(0);

   pass[0] := 'M';
   pass[1] := 'y';
   pass[2] := 'P';
   pass[3] := 'a';
   pass[4] := 's';
   pass[5] := 's';
   pass[6] := 'w';
   pass[7] := 'o';
   pass[8] := 'r';
   pass[9] := 'd';
   pass[10] := chr(0);

   connect(user, pass, errorcode);
   if errorcode <> 0 then
      writeln('Failed to connect with error code ', errorcode);
end.
Helena
  • 1,041
  • 2
  • 12
  • 24
  • You should add a null character at the end of both arrays to conform to C null terminated strings. – LU RD Mar 26 '18 at 07:55