NCEPLIBS-bufr  12.1.0
strings.F90
Go to the documentation of this file.
1 
5 
24 subroutine string(str,lun,i1,io)
25 
26  use modv_vars, only: mxs
27 
28  use moda_msgcwd
29 
30  implicit none
31 
32  character*(*), intent(in) :: str
33  character*128 bort_str1, bort_str2
34  character*80 usr, ust
35 
36  integer, intent(in) :: lun, i1, io
37  integer, parameter :: jcons = 52
38  integer mstr, nstr, lstr, lux, icon, jcon, iord, iorx, nxt, ind, j, n
39 
40  logical incache
41 
42  ! Note that lstr, mstr and nstr were initialized via a prior call to subroutine strcln(), which itself was called by
43  ! subroutine makestab().
44  common /stcach/ mstr, nstr, lstr, lux(mxs,2), usr(mxs), icon(jcons,mxs)
45  common /usrstr/ jcon(jcons)
46  common /stords/ iord(mxs), iorx(mxs)
47 
48  nxt = 0
49  ust = str
50  ind = inode(lun)
51  if(len(str)>80) then
52  write(bort_str1,'("BUFRLIB: STRING - INPUT STRING (",A,") HAS")') str
53  write(bort_str2,'(18X,"LENGTH (",I4,"), > LIMIT OF 80 CHAR.")') len(str)
54  call bort2(bort_str1,bort_str2)
55  endif
56 
57  ! See if the string is already in the cache
58 
59  incache = .false.
60  do n=1,nstr
61  if(lux(iord(n),2)==ind) then
62  iorx(nxt+1) = iord(n)
63  nxt = nxt+1
64  endif
65  enddo
66  do n=1,nxt
67  if(ust==usr(iorx(n))) then
68 
69  ! Yes, so copy parameters from the cache
70 
71  incache = .true.
72  do j=1,jcons
73  jcon(j) = icon(j,iorx(n))
74  enddo
75  exit
76  endif
77  enddo
78  if (.not.incache) then
79 
80  ! No, so add it to the cache
81 
82  call parusr(str,lun,i1,io)
83  lstr = max(mod(lstr+1,mstr+1),1)
84  nstr = min(nstr+1,mstr)
85  lux(lstr,1) = lun
86  lux(lstr,2) = ind
87  usr(lstr) = str
88  do j=1,jcons
89  icon(j,lstr) = jcon(j)
90  enddo
91 
92  ! Rearrange the cache order following this update
93 
94  do n=nstr,2,-1
95  iord(n) = iord(n-1)
96  enddo
97  iord(1) = lstr
98  endif
99 
100  if(jcon(1)>i1) then
101  write(bort_str1,'("BUFRLIB: STRING - INPUT STRING (",A,")")') str
102  write(bort_str2,'(18X,"HAS",I5," STORE NODES (MNEMONICS) - THE LIMIT (THIRD INPUT ARGUMENT) IS",I5)') jcon(1), i1
103  call bort2(bort_str1,bort_str2)
104  endif
105 
106  return
107 end subroutine string
108 
115 subroutine strcln
116 
117  use modv_vars, only: mxs
118 
119  implicit none
120 
121  integer mstr, nstr, lstr, luns, icon
122 
123  character*80 usrs
124 
125  common /stcach/ mstr, nstr, lstr, luns(mxs,2), usrs(mxs), icon(52,mxs)
126 
127  mstr = mxs
128  nstr = 0
129  lstr = 0
130 
131  return
132 end subroutine strcln
133 
148 subroutine parusr(str,lun,i1,io)
149 
150  use modv_vars, only: iac
151 
152  implicit none
153 
154  integer, intent(in) :: lun, i1, io
155  integer, parameter :: maxusr = 30, maxnod = 20, maxcon = 10
156  integer nnod, ncon, nods, nodc, ivls, kons, i, j, n, ntot, nod, kon, irpc, lstjpb
157 
158  character*(*), intent(in) :: str
159  character*128 bort_str1, bort_str2
160  character*80 ust
161  character*20 utg(maxusr)
162 
163  real val
164 
165  logical bump
166 
167  common /usrstr/ nnod, ncon, nods(maxnod), nodc(maxcon), ivls(maxcon), kons(maxcon)
168 
169  ust = str
170  if(len(str)>80) then
171  write(bort_str1,'("BUFRLIB: PARUSR - INPUT STRING (",A,") HAS ")') str
172  write(bort_str2,'(18X,"LENGTH (",I4,"), > LIMIT OF 80 CHAR.")') len(str)
173  call bort2(bort_str1,bort_str2)
174  endif
175 
176  ncon = 0
177  nnod = 0
178 
179  ! Parse the string
180 
181  call parstr(ust,utg,maxusr,ntot,' ',.true.)
182 
183  do n=1,ntot
184  ! For each mnemonic, determine if it's a condition node or a store node
185  call parutg(lun,io,utg(n),nod,kon,val)
186  if(kon/=0) then
187  ! It's a condition node
188  ncon = ncon+1
189  if(ncon>maxcon) then
190  write(bort_str1,'("BUFRLIB: PARUSR - THE NUMBER OF CONDITION NODES IN INPUT STRING")')
191  write(bort_str2,'(18X,A,") EXCEEDS THE MAXIMUM (",I3,")")') str,maxcon
192  call bort2(bort_str1,bort_str2)
193  endif
194  nodc(ncon) = nod
195  kons(ncon) = kon
196  ivls(ncon) = nint(val)
197  else
198  ! It's a store node
199  nnod = nnod+1
200  if(nnod>maxnod) then
201  write(bort_str1,'("BUFRLIB: PARUSR - THE NUMBER OF STORE NODES IN INPUT STRING")')
202  write(bort_str2,'(18X,A,") EXCEEDS THE MAXIMUM (",I3,")")') str,maxnod
203  call bort2(bort_str1,bort_str2)
204  endif
205  nods(nnod) = nod
206  endif
207  enddo
208 
209  ! Sort condition nodes in jump/link table order
210 
211  do i=1,ncon
212  do j=i+1,ncon
213  if(nodc(i)>nodc(j)) then
214  nod = nodc(i)
215  nodc(i) = nodc(j)
216  nodc(j) = nod
217  kon = kons(i)
218  kons(i) = kons(j)
219  kons(j) = kon
220  val = ivls(i)
221  ivls(i) = ivls(j)
222  ivls(j) = nint(val)
223  endif
224  enddo
225  enddo
226 
227  ! Check on special rules for conditional nodes that are bump nodes
228 
229  bump = .false.
230  do n=1,ncon
231  if(kons(n)==5) then
232  if(io==0) then
233  write(bort_str1,'("BUFRLIB: PARUSR - BUMP NODE (^ IN INPUT STRING ",A)') str
234  write(bort_str2,'(18X,"IS SPECIFIED FOR A BUFR FILE OPEN FOR INPUT, THE BUFR FILE MUST BE OPEN FOR OUTPUT")')
235  call bort2(bort_str1,bort_str2)
236  endif
237  if(n/=ncon) then
238  write(bort_str1,'("BUFRLIB: PARUSR - INPUT STRING (",A,") CONTAINS")') str
239  write(bort_str2,'(18X,"CONDITIONAL NODES IN ADDITION TO BUMP NODE - THE BUMP MUST BE ON THE INNER NODE")')
240  call bort2(bort_str1,bort_str2)
241  endif
242  bump = .true.
243  endif
244  enddo
245 
246  ! Check store node count and alignment
247 
248  if(.not.bump .and. nnod==0) then
249  write(bort_str1,'("BUFRLIB: PARUSR - INPUT STRING (",A,") HAS")') str
250  write(bort_str2,'(18X,"NO STORE NODES")')
251  call bort2(bort_str1,bort_str2)
252  endif
253  if(nnod>i1) then
254  write(bort_str1,'("BUFRLIB: PARUSR - INPUT STRING (",A,")")') str
255  write(bort_str2,'(18X,"HAS",I5," STORE NODES (MNEMONICS) - THE LIMIT {THIRD (INPUT) ARGUMENT} IS",I5)') nnod,i1
256  call bort2(bort_str1,bort_str2)
257  endif
258 
259  irpc = -1
260  do i=1,nnod
261  if(nods(i)>0) then
262  if(irpc<0) irpc = lstjpb(nods(i),lun,'RPC')
263  if(irpc/=lstjpb(nods(i),lun,'RPC').and.iac==0) then
264  write(bort_str1,'("BUFRLIB: PARUSR - INPUT STRING (",A,") CONTAINS")') str
265  write(bort_str2,'(18X,"STORE NODES (MNEMONICS) THAT ARE IN MORE THAN ONE REPLICATION GROUP")')
266  call bort2(bort_str1,bort_str2)
267  endif
268  endif
269  enddo
270 
271  return
272 end subroutine parusr
273 
348 subroutine parutg(lun,io,utg,nod,kon,val)
349 
350  use moda_msgcwd
351  use moda_tables
352 
353  implicit none
354 
355  integer, intent(in) :: lun, io
356  integer, intent(out) :: nod, kon
357  integer, parameter :: nchk = 8, ncond = 6
358  integer, parameter :: iok(nchk) = (/-1, -1, -1, -1, -1, 0, 0, 0/)
359  integer ltg, icv, inod, i, j, num, ier
360 
361  character*(*), intent(in) :: utg
362  character*128 bort_str1, bort_str2
363  character*20 atag
364  character*3 atyp
365  character*3, parameter :: btyp(nchk) = (/'SUB','SEQ','REP','RPC','RPS','DRB','DRP','DRS'/)
366  character, parameter :: cond(ncond) = (/'=', '!', '<', '>', '^', '#'/)
367 
368  real, intent(out) :: val
369 
370  ! For now, set picky (see below) to always be .false.
371  logical, parameter :: picky = .false.
372 
373  atag = ' '
374  atyp = ' '
375  kon = 0
376  nod = 0
377  val = 0
378  ltg = min(20,len(utg))
379 
380  ! Parse utg, saving into atag only the characters prior to any condition character.
381 
382  ! But first, take care of the special case where utg denotes the short (i.e. 1-bit) delayed replication of a Table D
383  ! mnemonic. This will prevent confusion later on since '<' and '>' are each also valid as condition characters.
384  if((utg(1:1)=='<').and.(index(utg(3:),'>')/=0)) then
385  atag = utg
386  else
387  outer: do i=1,ltg
388  if(utg(i:i)==' ') exit
389  do j=1,ncond
390  if(utg(i:i)==cond(j)) then
391  kon = j
392  icv = i+1
393  exit outer
394  endif
395  enddo
396  atag(i:i) = utg(i:i)
397  enddo outer
398  endif
399 
400  ! Find the node associated with atag in the subset table
401 
402  inod = inode(lun)
403  do nod=inod,isc(inod)
404  if(atag==tag(nod)) then
405  ! We found it, now make sure it has a valid node type
406  if(kon==5) then
407  ! Condition char "^" must be associated with a delayed replication sequence (this is a "bump" node). This is an
408  ! obsolete feature but remains in the library for compatibility with older application programs.
409  if(typ(nod-1)/='DRP' .and. typ(nod-1)/='DRS') then
410  write(bort_str1,'("BUFRLIB: PARUTG - BUMP NODE (MNEMONIC ",A,")'// &
411  ' MUST REFER TO A DELAYED REPLICATION SEQUENCE, HERE TYPE IS ",A)') atag,typ(nod-1)
412  call bort(bort_str1)
413  endif
414  elseif(kon/=6) then
415  ! Allow reading (but not writing) of delayed replication factors.
416  atyp = typ(nod)
417  do i=1,nchk
418  if(atyp==btyp(i) .and. io>iok(i)) then
419  write(bort_str1,'("BUFRLIB: PARUTG - ILLEGAL NODE TYPE: ",A," FOR MNEMONIC ",A)') atyp,atag
420  call bort(bort_str1)
421  endif
422  enddo
423  endif
424  ! If it's a condition node, then get the condition value which is a number following it
425  if(kon/=0) then
426  call strnum(utg(icv:ltg),num,ier)
427  if(ier<0) then
428  write(bort_str1,'("BUFRLIB: PARUTG - CONDITION VALUE IN MNEMONIC ",A," CONTAINS NON-NUMERIC CHARACTERS")') utg
429  call bort(bort_str1)
430  endif
431  val = num
432  endif
433  return
434  endif
435  enddo
436 
437  ! atag was not found in the subset table
438 
439  ! So what do we want to do? We could be "picky" and abort right here, or we could allow for the possibility that, e.g. a
440  ! user application has been streamlined to always call subroutine ufbint() with the same str, even though some of the
441  ! mnemonics contained within that str may not exist within the sequence definition of every possible type/subtype that is
442  ! being written by the application. In such cases, by not being "picky", we could just allow the library to subsequently
443  ! (and quietly, if iprt happened to be set to -1 in common /quiet/) not actually store the value corresponding to such
444  ! mnemonics, rather than loudly complaining and aborting.
445 
446  if(kon==0 .and. (io==0 .or. atag=='NUL' .or. .not.picky)) then
447  nod = 0
448  else
449  write(bort_str1,'("BUFRLIB: PARUTG - TRYING TO WRITE A MNEMONIC'// &
450  ' (",A,") WHICH DOES NOT EXIST IN SUBSET TABLE")') atag
451  write(bort_str2,'(18X,"(UPON INPUT, IT CONTAINED THE CONDITION CHARACTER ",A,")")') utg(icv-1:icv-1)
452  call bort2(bort_str1,bort_str2)
453  endif
454 
455  return
456 end subroutine parutg
457 
472 subroutine parstr(str,tags,mtag,ntag,sep,limit80)
473 
474  implicit none
475 
476  integer, intent(in) :: mtag
477  integer, intent(out) :: ntag
478  integer i, lstr, ltag, nchr
479 
480  character*(*), intent(in) :: str
481  character*(*), intent(out) :: tags(mtag)
482  character, intent(in) :: sep
483  character*128 bort_str1, bort_str2
484 
485  logical, intent(in) :: limit80
486  logical substr
487 
488  lstr = len(str)
489  ltag = len(tags(1))
490  if( limit80 .and. (lstr>80) ) then
491  write(bort_str1,'("BUFRLIB: PARSTR - INPUT STRING (",A,") HAS ")') str
492  write(bort_str2,'(18X,"LENGTH (",I4,"), > LIMIT OF 80 CHAR.")') lstr
493  call bort2(bort_str1,bort_str2)
494  endif
495  ntag = 0
496  nchr = 0
497  substr = .false.
498 
499  do i=1,lstr
500  if( .not.substr .and. (str(i:i)/=sep) ) then
501  ntag = ntag+1
502  if(ntag>mtag) then
503  write(bort_str1,'("BUFRLIB: PARSTR - INPUT STRING (",A,") CONTAINS",I4)') str,ntag
504  write(bort_str2,'(18X,"SUBSTRINGS, EXCEEDING THE LIMIT {",I4," - THIRD (INPUT) ARGUMENT}")') mtag
505  call bort2(bort_str1,bort_str2)
506  endif
507  tags(ntag) = ' '
508  endif
509  if( substr .and. (str(i:i)==sep) ) nchr = 0
510  substr = str(i:i)/=sep
511  if(substr) then
512  nchr = nchr+1
513  if(nchr>ltag) then
514  write(bort_str1,'("BUFRLIB: PARSTR - INPUT STRING (",A,") ")') str
515  write(bort_str2,'(18X,"CONTAINS A PARSED SUBSTRING WITH LENGTH EXCEEDING THE MAXIMUM OF",I4," CHARACTERS")') ltag
516  call bort2(bort_str1,bort_str2)
517  endif
518  tags(ntag)(nchr:nchr) = str(i:i)
519  endif
520  enddo
521 
522  return
523 end subroutine parstr
subroutine bort(str)
Log an error message, then abort the application program.
Definition: borts.F90:15
subroutine bort2(str1, str2)
Log two error messages, then abort the application program.
Definition: borts.F90:39
recursive subroutine strnum(str, num, iret)
Decode an integer from a character string.
Definition: misc.F90:177
Declare arrays used to store information about the current BUFR message that is in the process of bei...
integer, dimension(:), allocatable inode
Table A mnemonic for type of BUFR message.
Declare arrays and variables used to store the internal jump/link table.
integer, dimension(:), allocatable isc
Scale factors corresponding to tag and typ:
character *3, dimension(:), allocatable typ
Type indicators corresponding to tag:
character *10, dimension(:), allocatable tag
Mnemonics in the jump/link table.
subroutine strcln
Reset the internal mnemonic string cache.
Definition: strings.F90:116
subroutine parstr(str, tags, mtag, ntag, sep, limit80)
Parse a string containing one or more substrings into an array of substrings.
Definition: strings.F90:473
subroutine parutg(lun, io, utg, nod, kon, val)
Parse a mnemonic from a character string.
Definition: strings.F90:349
subroutine string(str, lun, i1, io)
Check whether a string is in the internal mnemonic string cache.
Definition: strings.F90:25
subroutine parusr(str, lun, i1, io)
Initiate the process to parse out mnemonics from a user-specified character string,...
Definition: strings.F90:149