66 use iso_fortran_env,
only : iostat_end
75 implicit none;
private
80 integer,
parameter,
private :: INCLUDE_TYPE_SYSTEM = 1
81 integer,
parameter,
private :: INCLUDE_TYPE_LOCAL = 2
83 integer,
parameter,
private :: MAX_PATH_LEN = 256
85 integer,
parameter,
private :: MAX_PATH_LEN = 4096
105 type(
context),
intent(in) :: ctx
106 integer,
intent(in) :: ounit
108 type(
macro),
allocatable,
intent(inout) :: macros(:)
109 character(*),
intent(in) :: token
111 type(
string),
allocatable,
save :: include_stack(:)
112 character(:),
allocatable :: include_file
113 character(:),
allocatable :: dir, ifile
114 integer :: i, iunit, ierr, pos, include_type, closing
118 dir = dirpath(ctx%path)
121 include_file =
trim(adjustl(ctx%content(pos:)))
124 if (include_file(1:1) ==
'"')
then
125 include_type = include_type_local
126 closing =
index(include_file(2:),
'"')
127 if (closing == 0)
then
129 message=
'Malformed #include directive', &
130 label=
label_type(
'Missing closing quotation mark', &
131 index(ctx%content,
'"'),1), &
132 source=
trim(ctx%path)), &
133 ctx%content, ctx%line))
136 include_file = include_file(2:closing)
137 else if (include_file(1:1) ==
'<')
then
138 include_type = include_type_system
139 closing =
index(include_file(2:),
'>')
140 if (closing == 0)
then
142 message=
'Malformed #include directive', &
143 label=
label_type(
'Missing closing quotation mark', &
144 index(ctx%content,
'"'),1), &
145 source=
trim(ctx%path)), &
146 ctx%content, ctx%line))
149 include_file = include_file(2:closing)
153 message=
'Malformed #include directive', &
154 label=
label_type(
'Filepath should either be delimited by "<...>" or "..."',
index(ctx%content, include_file), &
155 len(include_file)), &
156 source=
trim(ctx%path)), &
157 ctx%content, ctx%line))
164 inquire(file=ifile, exist=exists)
169 message=
'File not found', &
170 label=
label_type(
'Cannot find include file ' //
trim(include_file),
index(ctx%content, include_file), &
171 len(include_file)), &
172 source=
trim(ctx%path)), &
173 ctx%content, ctx%line))
180 if (include_type == include_type_local)
then
181 ifile =
join(dir, include_file)
182 inquire(file=ifile, exist=exists)
189 if (.not. exists .and.
allocated(
global%includedir))
then
190 do i = 1,
size(
global%includedir)
191 ifile =
join(
global%includedir(i), include_file)
192 inquire(file=ifile, exist=exists)
201 if (.not. exists)
then
203 character(:),
allocatable :: ipaths(:)
205 ipaths = get_system_paths()
206 do i = 1,
size(ipaths)
207 ifile =
join(ipaths(i), include_file)
208 inquire(file=ifile, exist=exists)
217 if (.not. exists)
then
218 ifile =
join(
cwd(), include_file)
219 inquire(file=ifile, exist=exists)
226 if (.not. exists)
then
228 message=
'File not found', &
229 label=
label_type(
'Cannot find include file ' //
trim(include_file),
index(ctx%content, include_file),
len(&
231 source=
trim(ctx%path)), &
232 ctx%content, ctx%line))
237 if (.not.
allocated(include_stack))
allocate(include_stack(0))
239 if (include_stack .contains. include_file)
then
241 message=
'Recursive include detected', &
242 label=
label_type(
'File already included in current include chain', &
243 index(ctx%content,
trim(include_file)), &
245 source=
trim(ctx%path)), &
246 ctx%content, ctx%line))
251 open(newunit=iunit, file=include_file, status=
'old', action=
'read', iostat=ierr)
254 message=
'File not found', &
255 label=
label_type(
'Cannot open include file ' //
trim(include_file),
index(ctx%content, include_file),
len(&
257 source=
trim(ctx%path)), &
258 ctx%content, ctx%line))
262 include_stack = [include_stack,
string(include_file)]
264 call preprocess(iunit, ounit, macros, .true.)
266 if (
size(include_stack) > 1)
then
267 include_stack = include_stack(:
size(include_stack)-1)
269 deallocate(include_stack)
278 function get_system_paths()
result(paths)
279 character(:),
allocatable :: paths(:)
281 character(:),
allocatable :: path_env, tmp(:)
282 integer :: lpath, i, n_paths, start_pos, end_pos, count
283 character(len=1) :: path_sep
292 call get_environment_variable(
'INCLUDE', length=lpath)
294 allocate(
character(len=0) :: paths(0)); return
298 allocate(
character(len=lpath) :: path_env)
299 call get_environment_variable(
'INCLUDE',
value=path_env)
303 do i = 1, len(path_env)
304 if (path_env(i:i) == path_sep) n_paths = n_paths + 1
308 allocate(
character(len=MAX_PATH_LEN) :: tmp(n_paths))
313 do i = 1, len(path_env) + 1
314 if (i > len(path_env) .or. path_env(i:i) == path_sep)
then
315 if (i > len(path_env))
then
321 if (end_pos >= start_pos)
then
323 tmp(count) = trim(adjustl(path_env(start_pos:end_pos)))
331 allocate(
character(len=MAX_PATH_LEN) :: paths(count))
332 paths(:) = tmp(1:count)
334 allocate(
character(len=0) :: paths(0))
type(global_settings), public global
Global preprocessor configuration instance.
recursive subroutine, public handle_include(ctx, ounit, preprocess, macros, token)
Process a include directive encountered during preprocessing Resolves the include file name (quoted o...
pure logical function, public is_rooted(filepath)
Returns .true. if the path is rooted (starts with a separator) or is absolute. A rooted path begins w...
character(:) function, allocatable, public cwd()
Returns the current working directory as a deferred-length character string. Returns an empty string ...
pure character(len_trim(str)) function, public lowercase(str)
Convert string to lower case (respects contents of quotes).
Generic renderer for diagnostics and source excerpts.
Abstract interface to the top-level preprocessing routine.
Join path components using the platform separator.
Locate the position of a substring.
Return the trimmed length of a string object.
Return the length of a string object.
Remove trailing blanks from a string object.
Snapshot of a source location within the preprocessing stream.
Structured compiler diagnostic.
Diagnostic label identifying a region of source text.
Representation of a preprocessor macro.
Represents text as a sequence of ASCII code units. The derived type wraps an allocatable character ar...