Oppolzer - Informatik / Blog

Blog-Hauptseite      Neuester Artikel      Älterer Artikel      Neuerer Artikel      Älterer gleiche Kategorie      Neuerer gleiche Kategorie

PL1-L - Diskussion über PL/1-Interface für PCRE (Perl compatible Reg. Ex)


Re: calling C from PL/I....


Bernd Oppolzer <bernd.oppolzer@T-ONLINE.DE>


PL1 (language) discussions <PL1-L@LISTSERV.DARTMOUTH.EDU>


2014.02.25 09:59:51

Hello, Bernd.

Thank you very much for your help with this, it's been of great use.

PCRE stands for "Perl Compatible Regular Expressions" and Z.A. has ported
the C library over to z/OS, with APIs for both COBOL (which works) adn PL/I
(which doesn't).

The COMPILE8 routine takes a regular expression and returns a pointer to the
pcre structure, which, in turn, is used by the 'exec" routine to do the actual
matching against a text string.

In C code, it is invoked like this:

         pcre *re;
         const char *error;
         int erroffset;

         re = pcre_compile(
           "^A.*Z",          /* the pattern */
           0,                /* default options */
           &error,           /* for error message */
           &erroffset,       /* for error offset */
           NULL);            /* use default character tables */

The pattern is a C string terminated by a binary zero, and is passed in the
pattern argument. A pointer to a single block of memory that is obtained via
pcre_malloc is returned. This contains the compiled code and related data. The
pcre type is defined for the returned block; this is a typedef for a structure
whose contents are not externally defined. It is up to the caller to free the
memory (via pcre_free) when it is no longer required.

The options argument contains various bit settings that affect the compilation.
It should be zero if no options are required.

If errptr is NULL, pcre_compile() returns NULL immediately. Otherwise, if
compilation of a pattern fails, pcre_compile() returns NULL, and sets the
variable pointed to by errptr to point to a textual error mes- sage. This is a
static string that is part of the library. Normally, the offset from the start
of the pattern to the data unit that was being processed when the error was
discovered is placed in the variable pointed to by erroffset, which must not be
NULL (if it is, an immediate error is given). This is the confusing part to
me....how the offset and error message text are handled....

As P.E. has pointed out, the PL/I available on z/OS has significantly
more capabilities for handling this sort of problem than the PL/I we have
available on z/VM. I plan on fixing this by writing a small bit of PL/I "glue"
code that would be callable by the old PL/I for MVS and VM and have it do the
real call, since PL/I code compiled on z/OS is runnable without change (almost)
on z/VM.

Thanks again.


On 02/25/2014 02:28 AM, Bernd Oppolzer wrote:
> Hello D. and P.,
> sorry for jumping in late,
> in fact P. has made most of the comments that I would have made.
> I would like to take a look at the C prototype again:
> extern pcre *COMPILE8(const char *, int, const char **, int *,
>                   const unsigned char *);
> I don't know exactly what the function is supposed to do, but:
> from earlier posts I recall that the 3. and 4. parameter are pointers
> to pointers to errortext buffers and pointers to error code. Dont forget
> the second, which is an int byvalue.
> Another issue could be that it may be necessary to define the struct pcre
> at the PL/1 side - if the caller needs access to the individual components.
> My approach would be
> dcl compile8 entry
>    (ptr byvalue,
>     bin fixed (31) byvalue,
>     ptr byaddr,
>     bin fixed (31) byaddr,
>     ptr byvalue)
> returns (ptr byvalue);
> then the call would look like this:
> pcre_result = compile8
>    (addr (charz1), int1, ptr2, int2, addr (charz3));
> ptr2 and int2 are results from the compile8 call, too.
> I hope, I got the meaning of the function right.
> When defining structures in PL/1 with a given C layout,
> you need to keep in mind that the alignment logic of C and PL/1
> is different !!!
> To keep it short: in C every structure starts with a boundary that is
> defined by the highest alignment need which appears inside the
> structure (2, 4, 8) - in PL/1, the beginning of the structure is selected
> in such a way that paddings are minimized, that is: a structure
> containing BIN FIXED (31) may start on a uneven address, if,
> for example, a CHAR(3) field is located before the first BIN FIXED (31).
> This leads to strange errors, if you are not aware of this.
> To cope with this problem, we regularly define a DEC FLOAT field
> on top of each structure in PL/1 and C. Doing this, all structures
> start on an 8 byte boundary, and the alignment logic of PL/1 and C
> is then the same.
> Then, last comment for today:
> Porting a C function or library and moving it to z/OS, for example
> (and making it available for PL/1) is not "reinventing the wheel";
> in fact, it normally takes one or two hours, and the function or library
> will run without issues, if done properly. Sometimes there are
> portability issues with the character set, but in this case, this has
> already been fixed, because there is a working z/OS solution, which can
> be accessed from COBOL. So the one or two hours in my opinion
> are well worth the effort.
> Z.A.: I'm sorry, that I didn't find the time to support you more
> in doing this, but I hope, my comments on those topics where helpful
> anyway. And thanks to D. for jumping in.
> Kind regards
> Bernd
> Am 24.02.2014 19:50, schrieb D.J.:
>> Hi, Bernd.
>> Many thanks for the tips here; I appreciate it.
>> Since C is not my native language (PL/I and Rexx are, btw), let me see
>> if I understand what you are saying. The pure C code after being through
>> the preprocessor, is this:
>> typedef struct real_pcre {
>>    pcre_uint32 magic_number;
>>    pcre_uint32 size;
>>    pcre_uint32 options;
>>    pcre_uint16 flags;
>>    pcre_uint16 max_lookbehind;
>>    pcre_uint16 top_bracket;
>>    pcre_uint16 top_backref;
>>    pcre_uint16 first_char;
>>    pcre_uint16 req_char;
>>    pcre_uint16 name_table_offset;
>>    pcre_uint16 name_entry_size;
>>    pcre_uint16 name_count;
>>    pcre_uint16 ref_count;
>>    const pcre_uint8 *tables;
>>    const pcre_uint8 *nullpad;
>> } real_pcre;
>> struct real_pcre;
>> typedef struct real_pcre pcre;
>> extern pcre *COMPILE8(const char *, int, const char **, int *,
>>                    const unsigned char *);
>> In PL/I it would look like this:
>>         char(*) varyingz BYADDR,
>>         FIXED BIN(31) BYVAUE,
>>         char(*) varyingz BYADDR,
>>         FIXED BIN(31) BYADDR,
>>         char(*) varyingz BYADDR)
>>    RETURNS(byvalue pcre):
>> and the call would be:
>> pcre =  compile8( string1  /* a varyingz string */
>>                  some_int,  /* fixed bin(31)  */
>>                  addr(string2),  /* need addr because of indirection */
>>                  int2, /* fixed bin(31) by addr here */
>>                  some_string /* another varyingz string */  );
>> How close did I get?  Did I get the double "* *" correct?
>> Another question: would this be possible using the older PL/I for MVS
>> and VM that is still in use at some sites?
>> Thanks again.
>> DJ


Blog-Hauptseite      Neuester Artikel      Älterer Artikel      Neuerer Artikel      Älterer gleiche Kategorie      Neuerer gleiche Kategorie