2
votes

I need to translate a piece of C code into Delphi/Pascal code, however I'm having trouble understanding a couple of lines.

C Code:

static char* get_boundary(apr_pool_t* p, const char* ctype) {
  char* ret = NULL ;
  if ( ctype ) {
    char* lctype = lccopy(p, ctype) ;
    char* bdy = strstr(lctype, "boundary") ;
    if ( bdy ) {
      char* ptr = strchr(bdy, '=') ;
      if ( ptr ) {
    // what is the following line supposed to do?
    bdy = (char*) ctype + ( ptr - lctype ) + 1;
    // and this? I understand it's a loop, but *ptr and ++ptr is ugly!
    for ( ptr = bdy; *ptr; ++ptr )
      if ( *ptr == ';' || isspace(*ptr) )
        *ptr = 0 ;
    ret = apr_pstrdup(p, bdy) ;
      }
    }
  }
  return ret ;
}   

My current translation:

function get_boundary(p: Papr_pool_t; const ctype: PChar): PChar;
var
  LCType: PChar;
  LBody: PChar;
begin
  Result := NIL;
  LCType := lccopy(p, ctype);
  LBody := strpos(LCType, 'boundary');
  if LBody <> NIL then begin
    // now what? (:
  end; // if LBody <> NIL then begin
end;

lccopy is creating a copy of the ctype parameter and make it lowercase.

Some details regarding translation are highly appreciated, like 'bdy = (char*) ctype + ( ptr - lctype ) + 1;' and the for loop.

FYI I'm translating mod_upload.c.

2
Perhaps you could write some glue code to call your C code from Pascal!Basile Starynkevitch
@BasileStarynkevitch I prefer not to use duck tape all over the place, besides, this will help me understand C a bit better.user497849
1. Use string rather than PChar. C doesn't have a string object. Don't let C's capabilities drive your Delphi design. 2. strchr and strstr serve the same purpose as Delphi's Pos.David Heffernan
@DavidHeffernan thank you, didn't knew!user497849
Dorin, I love your spelling of 'duck tape';-)Jan Doggen

2 Answers

6
votes
        bdy = (char*) ctype + ( ptr - lctype ) + 1;

So... ( ptr - lctype ) is pointer arithmetic to find how far into lctype ptr points. It's the difference between the addresses held in the pointers, divided by the size of the data type they point at (in this case char, so that size is just 1).

So bdy = (char*) ctype + ( ptr - lctype ) + 1; points bdy at the character following the '=' found previously, but in the original string ctype instead of the lowercase copy lctype.

        for ( ptr = bdy; *ptr; ++ptr )

This is not a terribly strange way to iterate through a string in C. ptr points to each character while iterating through, and *ptr gives the character; so *ptr will test as FALSE when the terminating null byte is reached to end the loop. ++ptr is more pointer arithmetic to move to the pointer to the next character. Even if this seems messy, it's a pretty natural way to do it in C.

So the loop moves through each character of the string pointed to by bdy, and during each iteration *ptr accesses the current character.

          if ( *ptr == ';' || isspace(*ptr) )
            *ptr = 0 ;

It appears the purpose of the loop was to terminate the string (by placing an earlier null terminator) at the next semicolon or whitespace character found.


There's enough difference between Delphi and C when it comes to string manipulation that you might be better off just figuring out what the function is doing, and then writing a Delphi equivalent from scratch rather than trying to translate it directly like this.

It looks like the function looks for "boundary" (case insensitively) in ctype, then skips past the next "=" found, and returns a copy of everything from there up to the next semicolon or whitespace character. You could do the same thing in Delphi easily with Delphi strings and functions, using very different code if you're willing to convert the strings first...

Also, if it matters, it looks like the original C code ignores anything between "boundary" and "=" -- so it would accept, say, "boundary asdf jidlsah;lkdsf =Value" as well as "boundary=Value"

3
votes
// what is the following line supposed to do?
bdy = (char*) ctype + ( ptr - lctype ) + 1;

ptr is the location of the "="-sign following "boundary" within lctype, hence ptr - lctype is the distance from the start of lctype to this specific "="-sign. Thus, ctype + (ptr-lctype) + 1 is the location of the first character after this "="-sign in the original string.

This has probably been done in order to search for substrings while ignoring the case of the characters. A simple strncasecmp() may have accomplished the same effect.

// and this? I understand it's a loop, but *ptr and ++ptr is ugly!
for ( ptr = bdy; *ptr; ++ptr )

It's indeed a loop, but instead of using an index variable and accessing the string by bdy[i], it uses a temporary pointer, ptr. This pointer iterates over the string pointed to by bdy. It starts at the first character, continues until the end of the string is reached (that is, if *ptr evaluates to 0 (in C, strings are null-terminated)), and increments the loop pointer by 1 after each iteration.

Using a pointer instead of an index variable is a common idiom in C, and preferred by many over the naive for (i=0; i < strlen(bdy); i++), since it saves the call to strlen().

Unfortunately for you, I stopped programming in Pascal 15 years ago, so I can't help you with the translation. However, it should be no problem, now that you know what those two lines mean.