/* ** Print to stdout a dependency list for input file specified on the command ** line. A dependency is anything that is referenced by a "#include"' or ** f90 "use" statement. In addition to these dependencies, write a dependency ** rule of "file.d" for each "file.F" or "file.c". This is to accomodate the ** default "make" procedure for CCM. ** ** The name of the module being "use"d is assumed to be case sensitive even ** though the Fortran language is not. In addition, Fortran source files are ** assumed to end in .F. For example, the statement "use Xxx" will translate ** into a dependency of Xxx.o, and the file searched for will be Xxx.F. ** ** Only files which exist in at least one directory named in the current ** directory or one or more "-I" command line arguments will be considered. ** ** An ANSI C compiler is required to build this code. */ #include /* printf, puts */ #include /* malloc, getopt */ #include /* strcpy */ #include /* access */ #include /* isspace, isalnum, tolower */ #define MAXLEN 256 #define TRUE 1 #define FALSE 0 /* ** Linked list struct used for directories to search, and filenames already ** found. */ struct node { char *name; struct node *next; }; /* ** lists of dependencies already found: prevents duplicates. */ static struct node *list = NULL; /* For #include */ static struct node *uselist = NULL; /* For use */ static struct node *suffix_list; /* List of Fortran suffixes to look for */ /* ** Function prototypes */ static void check (char *, struct node *, char *, int); static int already_found (char *, struct node *); main (int argc, char **argv) { int lastdot; /* points to the last . in fname */ int c; /* return from getopt */ int recursive = FALSE; /* flag asks for recursive check: ** i.e. check the thing being #included for #includes */ FILE *fpFname; char line[MAXLEN]; /* line read from input file */ char doto[MAXLEN]; /* name of .o file (from input file) */ char dotd[MAXLEN]; /* name of .o file (from input file) */ char fullpath[MAXLEN]; /* full pathname to potential dependency for .F files */ char depnam[MAXLEN]; /* dependency name (from #include or use) */ char srcfile[MAXLEN]; /* source file .F name from "use" */ char *lptr; /* points into line */ char *fname; /* input file name from command line */ char *fptr; /* pointer to copy into depnam */ char *relpath; /* input file name or path to it */ struct node *dirlist; /* list of directories to search */ struct node *dirptr; /* loop through dirlist */ struct node *newnode; /* malloc'd node */ struct node *last; /* last entry in #include list of found dependencies */ struct node *uselast; /* last entry in "use" list of found dependencies */ struct node *sptr; /* pointer into suffix_list */ /* ** Always put "." first in Filepath since gnumake will put "." first ** regardless of whether it is specified in VPATH */ dirlist = dirptr = (struct node *) malloc (sizeof (struct node)); dirptr->name = (char *) malloc (2); strcpy (dirptr->name, "."); dirptr->next = NULL; /* ** Always look for .F and .F90 files. List can be augmented via "-s" cmd line arg(s). */ suffix_list = (struct node *) malloc (sizeof (struct node)); suffix_list-> name = (char *) malloc (3); strcpy (suffix_list->name, ".f"); suffix_list->next = (struct node *) malloc (sizeof (struct node)); sptr = suffix_list->next; sptr->name = (char *) malloc (5); strcpy (sptr->name, ".F90"); sptr->next = NULL; while ((c = getopt (argc, argv, "I:r:s:f")) != -1) { switch(c) { case 'f': /* this arg is for backward compatibility */ break; case 'I': dirptr->next = (struct node *) malloc (sizeof (struct node)); dirptr = dirptr->next; dirptr->name = (char *) malloc (strlen (optarg) + 1); strcpy (dirptr->name, optarg); dirptr->next = NULL; break; case 's': sptr->next = (struct node *) malloc (sizeof (struct node)); sptr = sptr->next; sptr->name = (char *) malloc (strlen (optarg) + 2); strcpy (sptr->name, "."); strcat (sptr->name, optarg); sptr->next = NULL; break; case 'r': recursive = TRUE; break; case '?': /* Unknown option */ fprintf (stderr, "%s: Unknown option encountered\n", argv[0]); } } if (argc == optind+1) { relpath = argv[optind]; } else { fprintf (stderr, "Usage: %s [-Idir] [-r] [-s suffix] file\n", argv[0]); exit (-1); } /* ** Retain only the filename of the input file for which dependencies are ** being generated. */ fname = relpath + strlen (relpath) - 1; while (*fname != '/' && fname > relpath) fname--; if (*fname == '/') fname++; /* ** Define the .o file by changing tail to ".o" */ strcpy (doto, fname); for (lastdot = strlen (fname) - 1; doto[lastdot] != '.' && lastdot > 0; lastdot--); if (lastdot == 0) { fprintf (stderr, "Input file %s needs a head\n", fname); exit (1); } doto[lastdot] = '\0'; strcpy (dotd, doto); strcat (doto, ".o "); strcat (dotd, ".d "); /* ** write the blah.o blah.d: blah.F (or .c or whatever) dependency to stdout */ fputs (doto , stdout); fputs (dotd , stdout); fputs (": " , stdout); fputs (fname , stdout); fputs ("\n" , stdout); if ((fpFname = fopen (relpath, "r")) == NULL) { fprintf (stderr, "Can't open file %s\n", relpath); exit (1); } while (fgets (line, MAXLEN, fpFname) != NULL) { /* ** Check for dependencies of the cpp "include" variety. Allow for lines ** of the form "# include" */ if (line[0] == '#') { for (lptr = line+1; isspace (*lptr); lptr++); if (strncmp (lptr, "include ", 8) == 0) { for (lptr += 8; *lptr != '<' && *lptr != '"' && *lptr != '\0'; lptr++); if (*lptr == '\0') break; /* Bad input line: ignore */ /* ** Fill in depnam with the dependency (i.e. the thing being ** #included. Syntax check is not perfect. */ for (fptr = depnam; *++lptr != '>' && *lptr != '"' && *lptr != '\0'; fptr++) *fptr = *lptr; if (*lptr == '\0') break; /* Bad input line: ignore */ *fptr = '\0'; if ( ! already_found (depnam, list)) { /* Skip any duplicates */ /* ** Include only dependencies which are specified by -Ixxx on the ** command line. These directories are defined by the linked list ** pointed to by dirlist. */ for (dirptr = dirlist; dirptr != NULL; dirptr = dirptr->next) { strcpy (fullpath, dirptr->name); strcat (fullpath, "/"); strcat (fullpath, depnam); /* ** If the file exists and is readable, add an entry to the "found" ** list, then write a dependency rule to stdout. */ if (access (fullpath, R_OK) == 0) { newnode = malloc (sizeof (struct node)); newnode->name = malloc (strlen (depnam) + 1); strcpy (newnode->name, depnam); newnode->next = NULL; if (list == NULL) list = newnode; else last->next = newnode; last = newnode; fputs (doto , stdout); fputs (": " , stdout); fputs (depnam, stdout); fputs ("\n", stdout); /* ** Check for nested #include's if flag was set */ if (recursive) check (fullpath, dirlist, doto, 0); break; /* Dependency found: process next line */ } } } } } else { /* ** Check for dependencies of the f90 "use" variety. To strictly adhere ** to fortran std, should allow for spaces between chars of "use". */ for (lptr = line; isspace (*lptr); lptr++); if (tolower ((int) lptr[0]) == 'u' && tolower ((int) lptr[1]) == 's' && tolower ((int) lptr[2]) == 'e') { for (lptr += 3; isspace (*lptr); lptr++); /* ** Fill in depnam with the dependency (i.e. the thing being "use"d. ** Strictly speaking, should disallow numeric starting character. */ for (fptr = depnam; isalnum (*lptr) || *lptr == '_'; (fptr++, lptr++)) *fptr = *lptr; *fptr = '\0'; /* ** srcfile is the source file name from which the dependency is ** generated. Note case sensitivity of depnam. */ if ( ! already_found (depnam, uselist)) { /* Skip any duplicates */ /* ** Loop through suffix list */ for (sptr = suffix_list; sptr != NULL; sptr = sptr->next) { strcpy (srcfile, depnam); strcat (srcfile, sptr->name); /* ** Include only dependencies which are specified by -Ixxx on the ** command line. These directories are defined by the linked list ** pointed to by dirlist. */ for (dirptr = dirlist; dirptr != NULL; dirptr = dirptr->next) { strcpy (fullpath, dirptr->name); strcat (fullpath, "/"); strcat (fullpath, srcfile); /* ** If the file exists and is readable, add an entry to the "found" ** list, then write a dependency rule to stdout. */ if (access (fullpath, R_OK) == 0) { newnode = malloc (sizeof (struct node)); newnode->name = malloc (strlen (srcfile) + 1); strcpy (newnode->name, depnam); newnode->next = NULL; if (uselist == NULL) uselist = newnode; else uselast->next = newnode; uselast = newnode; fputs (doto , stdout); fputs (": " , stdout); fputs (depnam, stdout); fputs (".o" , stdout); fputs ("\n" , stdout); goto read_next_line; /* Dependency found: process next line */ } /* if (access (fullpath... */ } /* loop through linked list of directories from Filepath */ } /* loop through linked list of suffixes */ } /* if ( ! already_found (srcfile... */ } /* if (lptr points to "use " */ } /* else branch of if (line[0] == '#') */ read_next_line: continue; } /* Looping over lines in the file */ fclose (fpFname); return (0); } void check (char *file, struct node *dirlist, char *doto, int recurse_level) { FILE *fpFile; char line[MAXLEN], fullpath[MAXLEN]; char depnam[MAXLEN]; char *lptr, *fptr; struct node *dirptr; /* ** Don't bother checking beyond 3 levels of recursion */ if (recurse_level > 3) { fprintf (stderr, "More than 3 levels of recursion detected: bailing out\n"); return; } if ((fpFile = fopen (file, "r")) == NULL) { fprintf (stderr, "Can't open file %s\n", file); exit (1); } while (fgets (line, MAXLEN, fpFile) != NULL) { /* ** Check for dependencies of the cpp "include" variety. Allow for lines ** of the form "# include" */ if (line[0] == '#') { for (lptr = line+1; isspace (*lptr); lptr++); if (strncmp (lptr, "include ", 8) == 0) { for (lptr += 8; *lptr != '<' && *lptr != '"' && *lptr != '\0'; lptr++); if (*lptr == '\0') break; /* Bad input line: ignore */ /* ** Fill in depnam with the dependency (i.e. the thing being ** #included. Syntax check is not perfect. */ for (fptr = depnam; *++lptr != '>' && *lptr != '"' && *lptr != '\0'; fptr++) *fptr = *lptr; if (*lptr == '\0') break; /* Bad input line: ignore */ *fptr = '\0'; /* ** Don't include dependencies which are not in the Filepath */ for (dirptr = dirlist; dirptr != NULL; dirptr = dirptr->next) { strcpy (fullpath, dirptr->name); strcat (fullpath, "/"); strcat (fullpath, depnam); /* ** If the file exists and is readable, add an entry to the "found" ** list, then write a dependency rule to stdout. */ if (access (fullpath, R_OK) == 0) { fputs (doto , stdout); fputs (": " , stdout); fputs (depnam, stdout); fputs ("\n", stdout); /* ** Check for nested #include's */ check (fullpath, dirlist, doto, recurse_level+1); break; } } } } } fclose (fpFile); return; } int already_found (char *name, struct node *list) { struct node *ptr; for (ptr = list; ptr != NULL; ptr = ptr->next) { if (strcmp (ptr->name, name) == 0) return (1); } return (0); }