2

I am trying to implement REDEFINES logic used in COBOL in C language.

Below is the COBOL Program:

   IDENTIFICATION DIVISION.
   ENVIRONMENT DIVISION.
   DATA DIVISION.
   WORKING-STORAGE SECTION.

   01  DATE-MMDDYY.
       10  DATE-MM               PIC 9(02).
       10  DATE-DD               PIC 9(02).
       10  DATE-YY               PIC 9(02).
   01  SYSTEM-DATE-MMDDYY REDEFINES DATE-MMDDYY PIC X(6).

   PROCEDURE DIVISION.

       MOVE '011817' TO SYSTEM-DATE-MMDDYY.
       DISPLAY 'SYSTEM-DATE-MMDDYY: ' SYSTEM-DATE-MMDDYY.
       DISPLAY 'DATE-MM: ' DATE-MM.
       DISPLAY 'DATE-DD: ' DATE-DD.
       DISPLAY 'DATE-YY: ' DATE-YY.

       DISPLAY 'CHANGING DATE-YY = 18'
       MOVE '18' TO DATE-YY.
       DISPLAY 'New SYSTEM-DATE-MMDDYY: ' SYSTEM-DATE-MMDDYY.

       STOP RUN.

And below is the execution of above program:

SYSTEM-DATE-MMDDYY: 011817
DATE-MM: 01
DATE-DD: 18
DATE-YY: 17
CHANGING DATE-YY = 18
New SYSTEM-DATE-MMDDYY: 011818

I understand that UNION in C can be used to achieve similar thing. But it is not working for me.

Below is the C program which I wrote:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

union redef
{
        struct date_mmddyy{
                char date_mm[2];
                char date_dd[2];
                char date_yy[2];
        }date_mmddyy;
        char system_date_mmddyy[6];
};
typedef union redef redef;
int main(){
        redef redef;
        strcpy(redef.date_mmddyy.date_mm, "01");
        strcpy(redef.date_mmddyy.date_dd, "18");
        strcpy(redef.date_mmddyy.date_yy, "17");
        printf("%s\n",redef.date_mmddyy.date_mm);
        printf("%s\n",redef.date_mmddyy.date_dd);
        printf("%s\n",redef.date_mmddyy.date_yy);
        printf("%s\n",redef.system_date_mmddyy);

        strcpy(redef.system_date_mmddyy, "021918");
        printf("%s\n",redef.date_mmddyy.date_mm);
        printf("%s\n",redef.date_mmddyy.date_dd);
        printf("%s\n",redef.date_mmddyy.date_yy);
        printf("%s\n",redef.system_date_mmddyy);

        return 0;
}

And it runs as below:

011817
1817
17
011817
021918
1918
18
021918

Can you please share some idea?

UPDATE 1:

I have terminated all char array with \0 now. Below is the changed code:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

union redef
{
        struct date_mmddyy{
                char date_mm[3];
                char date_dd[3];
                char date_yy[3];
        }date_mmddyy;
        char system_date_mmddyy[7];
};
typedef union redef redef;
int main(){
        redef redef;
        redef.date_mmddyy.date_mm[2] = '\0';
        redef.date_mmddyy.date_dd[2] = '\0';
        redef.date_mmddyy.date_yy[2] = '\0';
        redef.system_date_mmddyy[6] = '\0';

        strcpy(redef.date_mmddyy.date_mm, "01");
        strcpy(redef.date_mmddyy.date_dd, "18");
        strcpy(redef.date_mmddyy.date_yy, "17");
        printf("%s\n",redef.date_mmddyy.date_mm);
        printf("%s\n",redef.date_mmddyy.date_dd);
        printf("%s\n",redef.date_mmddyy.date_yy);
        printf("%s\n",redef.system_date_mmddyy);

        strcpy(redef.system_date_mmddyy, "021918");
        printf("%s\n",redef.date_mmddyy.date_mm);
        printf("%s\n",redef.date_mmddyy.date_dd);
        printf("%s\n",redef.date_mmddyy.date_yy);
        printf("%s\n",redef.system_date_mmddyy);

        return 0;
}

And below is the execution:

01
18
17
01
021918
918

021918

Output goes far away from what it is in COBOL.

Rohit
  • 604
  • 1
  • 10
  • 25
  • 4
    There is no language called C/C++. There is either C, or C++. What you have written (and asking about) is C. – Algirdas Preidžius Jan 18 '17 at 16:39
  • Yes I know :). I mentioned it just for tagging purpose. Thank you. – Rohit Jan 18 '17 at 16:41
  • C strings are null terminated – KevinDTimm Jan 18 '17 at 16:42
  • 4
    @Rohit Don't spam tags. Only tag what is relevant. C++ was **not** relevant in your question. – Algirdas Preidžius Jan 18 '17 at 16:42
  • After adding NULs, `date_mmddyy` and `system_date_mmddyy` are no longer equal. In the first you have "01\018\017\0" and in the second "011817\0". – Ingo Leonhardt Jan 18 '17 at 16:51
  • Try: `strcpy(redef.date_mmddyy.date_mm, "01");` -->> `memcpy(redef.date_mmddyy.date_mm, "01", 2);` (similar for dd and yy) – wildplasser Jan 18 '17 at 16:54
  • Does C have any "substring" capability? In COBOL, those are not "strings", they are just data. I assume "substring" use would not dictate that the source is a string. Can you give a better example of what you want to do? This REDEFINES is "fake". DATE-MMDDYY could be used directly to get your six-byte data. Your major issue is trying to output the data as "already strings". – Bill Woodger Jan 18 '17 at 18:04
  • 1
    GnuCOBOL (SourceForge.Net) compiles to C. You could get yourself a copy, generate the C code for your COBOL program, and look at how that deals with a more realistic REDEFINES. – Bill Woodger Jan 18 '17 at 18:05
  • 2
    The first union was correct, but COBOL PIC X(nn) fields are _not_ NUL terminated, so using strcpy will not work right. You could use memcpy instead of strcpy so that you can copy the data without the trailing NULs. – Scott Nelson Jan 19 '17 at 14:34
  • @Rohit Did the provided answer "worked for you"? If yes: please accept as working answer, if there's something unclear add this in its comment and I'll try to add the missing bits. stackoverflow.com/help/someone-answers – Simon Sobisch Oct 31 '17 at 21:42

3 Answers3

4

If you want to use the REDEFINE logic from COBOL in C there is only one option: Don't use C strings for COBOL storage as COBOL data is using only character arrays for data structure. and: Be aware of structure padding(for any item not declared as PIC X)!

This would lead to something like

#include <stdio.h>

union redef
{
        struct date_mmddyy{
               char date_mm[2];
               char date_dd[2];
               char date_yy[2];
        }date_mmddyy;
        char system_date_mmddyy[6];
};
typedef union redef redef;
int main(){
        redef redef;
        redef.date_mmddyy.date_mm[0] = '0';
        redef.date_mmddyy.date_mm[1] = '1';
        redef.date_mmddyy.date_dd[0] = '1';
        redef.date_mmddyy.date_dd[1] = '8';
        redef.date_mmddyy.date_yy[0] = '1';
        redef.date_mmddyy.date_yy[1] = '7';
        // or:
        memcpy((void *)redef.date_mmddyy, (void *)"011817", 6);

        printf("%c%c\n",redef.date_mmddyy.date_mm[0],
                        redef.date_mmddyy.date_mm[1]);
        [...]

To make this actually usable you'd likely use two things:

  • additional to the struct which contains the data add a field structure containing at least a pointer to the storage, the size and the type (in your sample 'x' and 'numeric-display' are enough
  • add helper functions to set/get a field value

Something like

#include <stdio.h>

enum cob_type {
    T_DISPLAY = 0, 
    T_NUMERIC_DISPLAY
};

struct cob_field{
       char *data;
       int size;
       enum cob_type;     
};

/* one for the actual storage - could be a simple unnamed char array */
struct date_mmddyy{
       char date_mm[2];
       char date_dd[2];
       char date_yy[2];
} date_mmddyy;

/* fields with pointers to the storage and the size */
cob_field date_mmddyy = {&date_mmddyy, 6, T_DISPLAY};
cob_field date_mm     = {&date_mmddyy.date_mm, 2, T_NUMERIC_DISPLAY};
cob_field date_dd     = {&date_mmddyy.date_dd, 2, T_NUMERIC_DISPLAY};
cob_field date_yy     = {&date_mmddyy.date_yy, 2, T_NUMERIC_DISPLAY};
cob_field system_date_mmddyy = {&date_mmddyy, 6, T_DISPLAY};

int main(){
        set_field_data(system_date_mmddyy, "011817");
        printf("SYSTEM-DATE-MMDDYY: %s\n', get_field_data(system_date_mmddyy));
        printf("DATE-MM: %s\n', get_field_data(date_mm));
        printf("DATE-DD: %s\n', get_field_data(date_dd));
        printf("DATE-YY: %s\n', get_field_data(date_yy));

        puts("CHANGING DATE-YY = 18");
        set_field_data(date_yy, "18");
        printf("SYSTEM-DATE-MMDDYY: %s\n', get_field_data(system_date_mmddyy));

        return 0;
}

Given that COBOL has many different types you'd need to write lots of logic in the helper functions - the ones needed here void set_field (cob_field *f, char *data) and char *get_field (cob_field *f) are quite simply, this changes when using all types COBOL has, gets much more complicated when using more than MOVE from literal (adding implicit type conversions) and DISPLAY.

BTW: You may want to check GnuCOBOL - it translates COBOL to C...

Community
  • 1
  • 1
Simon Sobisch
  • 6,263
  • 1
  • 18
  • 38
2

Your union is working. The problem comes from missing NULL terminator character in your string when you are displaying the structure fields.

If you don't want to modify your structure, maybe could you adapt the format qualifiers.

printf("%2s\n",redef.date_mmddyy.date_mm);
printf("%2s\n",redef.date_mmddyy.date_dd);
printf("%2s\n",redef.date_mmddyy.date_yy);
printf("%6s\n",redef.system_date_mmddyy);
Jeandey Boris
  • 743
  • 4
  • 9
1

The problem is the output itself. In C all strings (at least those that you want to print with printf("%s",...)) need a 0 byte at the end. Since your "strings" are meant to have a fixed length of 2 or 6 bytes, this will not work, because every of the 2 byte "strings" would require a third byte to finalize the string.

So, actually it "works", but the result is an array of characters and that is something different than a string in C, so you can not use the usual string operations.

It is not what you want to hear, but I would suggest to forget about the strings and work with integers and use conversion functions. For example

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

struct Date {
    int day, month, year;
};

void dateFromString(char* input, struct Date* output) {
    char tmp[3];
    tmp[2] = '\0';
    memcpy(tmp, &input[0], 2);
    output->day = atoi(tmp);
    memcpy(tmp, &input[2], 2);
    output->month = atoi(tmp);
    memcpy(tmp, &input[4], 2);
    output->year = atoi(tmp);
}

void stringFromDate(struct Date* input, char* output) {
    sprintf(output,"%02i%02i%02i",input->day,input->month,input->year);
}

int main() {
    char text[] = "120456";
    struct Date d;
    dateFromString(text, &d);

    printf("%i\n",d.day);
    printf("%i\n",d.month);
    printf("%i\n",d.year);

    char buffer[7];
    stringFromDate(&d,buffer);
    printf("%s\n",buffer);
}
koalo
  • 2,113
  • 20
  • 31
  • Updated with your suggestion. That doesn't help either. :( – Rohit Jan 18 '17 at 16:48
  • No that makes it worse ;-) This is because now the positions in the string do no longer match. Actually it does not seem to be sensible in C to do it with a union. I will add a suggestion in an edit. – koalo Jan 18 '17 at 16:50