2

I am developing a Linux program that receives some user parameters by the command line.

The user interface was made in C and data processing is in Fortran, so the main function in C passes some parameters to the Fortran subroutine. Some of these parameters are strings and it is with them that I am having problems. Here are some parts of the codes:

The C main function:

extern void anaconv_(char *FileName,int *n,char *PlanName,int *m,char *TipoDados,char *FrontName,int *l,char *Dirdat, int *h, bool *kvuge, bool *ch_serie, bool *ch_shuntb, bool *simulacao);
void ajuda_anaconv(void);

int main (int argc, char **argv)
{
  int  i;
  int  n,m,l,h;
  bool flagk     = 0;
  bool flags     = 0;
  bool flagp     = 0;
  bool flagsimul = 0;
  char *dirpwf     = malloc(sizeof(char)*80);
  char *dirbar     = malloc(sizeof(char)*80);
  char *dirfro     = malloc(sizeof(char)*80);
  char *dirdat     = malloc(sizeof(char)*500);
  char *tpdados    = malloc(sizeof(char));
  ...
  dirpwf  = argv[i+1];
  i++;
  ...
  dirbar  = argv[i+1];
  i++;
  ...
  dirfro  = argv[i+1];
  i++;
  ...
  dirdat  = argv[i+1];
  i++;
  ...
  tpdados = argv[i+1];
  i++;
  ...
  n = strlen(dirpwf)
  m = strlen(dirbar)
  l = strlen(dirfro)
  h = strlen(dirdat)
  ...
  anaconv_(dirpwf,&n,dirbar,&m,tpdados,dirfro,&l,dirdat,&h,&flagk,&flags,&flagp,&flagsimul);
  ...
  free(dirpwf);
  free(dirbar);
  free(dirfro);
  free(dirdat);
  free(dirdatcomp);
  free(tpdados);
  ...
  return 1;
}

And them, the Fortran subroutine is something like:

SUBROUTINE ANACONV
 +(FileName,FileNameSize,PlanName,PlanNameSize,TpData,FrontName,FrontNameSize,Dirdat,DirdatSize,kVUGE,CHSERIE,CHSHUNTB,SIMUL)

    IMPLICIT NONE
    CHARACTER*80  FileName,PlanName,FrontName
    CHARACTER*500 Dirdat        
    INTEGER*4     FileNameSize,PlanNameSize,FrontNameSize,DirdatSize
    CHARACTER*1   TpData
    LOGICAL*1     kVUGE,CHSERIE,CHSHUNTB,SIMUL
    ...
END SUBROUTINE ANACONV

The compiler is gcc version 4.8.5 20150623 and I'm running the program in a CentOS Linux release 7.3.1611.

Although the code is compiling without problems, when I run the program passing some parameters, I get the following message:

Fortran runtime error: Actual string length is shorter than the declared one for dummy argument 'filename' (-136420716/80)

I tried to find the problem debugging with gdb, but I could not find the error. The FileName string gets the correct value from the dirpwf pointer of the main function in C, and the string size stored in the FileNameSize variable is also correct, whereas Fortran understands that the received size (apparently -136420716) is less than expected (80 ).

How can I fix this?

francescalus
  • 30,576
  • 16
  • 61
  • 96
Bruno Fonseca
  • 93
  • 1
  • 1
  • 9
  • `FileName` is of length 80, regardless of the value of `FileNameSize`. – francescalus Jan 26 '18 at 16:23
  • Are you free to change the Fortran subroutine, or do you need to work with that and get the C to play nicely? If you can change the Fortran, then the C interoperability facility will solve your problems (probably). There is some detail in the [tag:fortran-iso-c-binding] wiki. – francescalus Jan 26 '18 at 16:27
  • Your Fortran subroutine source is probably meant to be in fixed form, so you'll need to pay attention to how that is formatted here. – francescalus Jan 26 '18 at 16:28
  • You can see [this question](https://stackoverflow.com/q/8207997/3157076) for example. – francescalus Jan 26 '18 at 16:30
  • There's no point in the `malloc()` in `char *dirdat = malloc(sizeof(char)*500);` if it's followed by `dirdat = argv[i+1];`. All you're doing is leaking memory. And `sizeof(char)` is always one, by definition. – Andrew Henle Jan 26 '18 at 16:30
  • The FORTRAN is in free form and yes, I can make some changes in the Fortran subroutine. I will read the recomended link and see if I can find some help. Thanks! – Bruno Fonseca Jan 26 '18 at 16:33
  • About the malloc, the compiler had some issues when I did not use that and the size of char really was a mistake :) – Bruno Fonseca Jan 26 '18 at 16:36

1 Answers1

2

Fortran and C keep track of string lengths in different ways. C uses a NUL character at the end as a terminator, while Fortran keeps the length separately. Just how Fortran does that is implementation-dependent. For calling Fortran procedures, most compilers look for the length passed as additional arguments after the named ones, as an address-sized integer passed by value. But this is not universal and not portable. You have passed these lengths, but have done so immediately after the string addresses - that's not where gfortran wants them.

Fortran 2003 introduced C interoperability features that allow you to mix Fortran and C in a portable manner. Fortran 2008 extended that somewhat and Fortran 2018 extends it more. Unfortunately, passing strings from C to Fortran is still messy, even in F2018.

One might think that the simple solution is to add BIND(C) to the Fortran subroutine header - this tells Fortran to not use hidden arguments, among other things. But your use of CHARACTER*80, etc., is then not allowed.

The simplest fix is to move n, m, l and h to the end of the argument list in the C call, remove the Fortran arguments for the sizes, and make n, m, l and h size_t rather than int. Also, don't use & to pass them. This is still non-portable, but it will get you going for now.

Steve Lionel
  • 6,972
  • 18
  • 31
  • Thank you very much. Apparently this worked, but there was yet another small problem. The function in FORTRAN continues to complain about the size of the parameters passed. The difference is that now it complains but using the correct sizes of stings, for example: Fortran runtime error: Current string length is shorter than the one declared for dummy argument 'filename' (52/80). The size of "FileName" really is 52 and 80 is the maximum size of this variable in FORTRAN. I'm thinking of using dynamic allocation to solve this. Or do you know how I can solve it differently? – Bruno Fonseca Jan 26 '18 at 16:56
  • 1
    Just change the declared string lengths from `80` and `500` to `(*)` so as to use the passed string lengths. – user5713492 Jan 26 '18 at 17:18
  • Thanks! Problem solved! Just used "CHARACTER(LEN=*)" – Bruno Fonseca Jan 26 '18 at 17:40
  • 2
    Explicit lengths for character dummy arguments are never a good idea. – Steve Lionel Jan 26 '18 at 22:56
  • As an aside, currently all released versions of GFortran use int for the hidden string length argument. In the soon-to-be released GCC 8 this has changed to size_t. – janneb Jan 27 '18 at 10:48