1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
|
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
include <ctype.h>
include <chars.h>
include <pattern.h>
include <syserr.h>
include <diropen.h>
.help fntgfn
.nf _________________________________________________________________________
File Name Template Package
This package contains routines to expand a file name template string into a
list of file names, and to access the individual elements of the list. The
template is a list of file names, patterns, and/or list file names. The
concatenation operator may be used within input list elements to form new
output filenames. String substitution may also be used to form new filenames.
Sample template string:
alpha, *.x, data* // .pix, [a-m]*, @list_file
This template would be expanded as the file "alpha", followed in successive
calls by all the files in the current directory whose names end in ".x",
followed by all files whose names begin with "data" with the extension ".pix"
appended, and so on. The @ character signifies a list file (file containing
regular file names).
String substitution uses the first string given for the template, expands
the template, and for each filename generated by the template, substitutes
the second string to generate a new filename. Some examples follow.
*.%x%y% change extension to `y'
*%%_abc%.imh append `_abc' to root
nite%1%2%.1024.imh change `nite1' to `nite2'
Main entry points:
fntopnb - expand template and open a buffered filename list
fntgfnb - get next filename from buffered list (sequential)
fntrfnb - get next filename from buffered list (random)
fntclsb - close buffered list
fntlenb - get number of filenames in a buffered list
fntrewb - rewind the list
Low Level Entry Points:
fntopn - open an unbuffered filename list
fntgfn - get next filename from unbuffered list
fntcls - close unbuffered list
The B suffix routines are the highest level and most convenient to use.
The remaining routines expand a template "on the fly" and do not permit
sorting or determination of the length of the list.
.endhelp ____________________________________________________________________
# FNTB descriptor structure.
define LEN_FNTBHDR 5
define FNTB_MAGIC 5164
define B_MAGIC Memi[$1]
define B_SBUFPTR Memi[$1+1] # string buffer pointer
define B_NSTR Memi[$1+2] # number of strings
define B_STRNUM Memi[$1+3] # used to read list
define B_STRINDX Memi[$1+$2-1+4] # index of string
# FNTU descriptor structure.
define LEN_FNTUHDR (10+1024+256)
define FNTU_MAGIC 5664
define U_MAGIC Memi[$1]
define U_FILDES Memi[$1+1]
define U_TEMPLATE Memi[$1+2] # pointer
define U_TEMPLATE_INDEX Memi[$1+3]
define U_PATTERN (P2C($1+10))
define U_LDIR (P2C($1+1034))
# Special characters and size limiting definitions.
define TOK_DELIM ',' # token delimiter
define LIST_FILE_CHAR '@' # @listfile
define CH_EDIT '%' # string substitution metachar
define SZ_PATTERN 1023
define SZ_LDIR 255
define SZ_PATSTR 1023
define MAX_EDIT 8
define MAX_PATTERNS 8
# Tokens.
define EO_TEMPLATE 1
define LIST_FILE 2
define PATTERN_STRING 3
define FILE_NAME 4
# Size limiting definitions (initial buffer sizes).
define SZ_DEFSTRBUF 2048 # default string buffer size
define LEN_INDEXVECTOR 256 # initial length of index vector
# FNTOPNB -- General open buffered list routine, for any type of filename list.
# Expand template into string buffer, sort if so indicated.
int procedure fntopnb (template, sort)
char template[ARB] # filename template
int sort # sort expanded patterns
int nedit[MAX_PATTERNS], junk, nchars
bool is_template[MAX_PATTERNS], is_edit[MAX_PATTERNS], sortlist, is_url
pointer sp, pbuf, fname, rname, extn, ebuf, sbuf, list, ip, op, ep, pp
pointer patp[MAX_PATTERNS], flist[MAX_PATTERNS], editp[MAX_EDIT]
int nlists, npat, nstr, maxstr, nextch, sz_sbuf, ix, first_string, ch, i
int fntopn(), fntgfn(), fnt_getpat(), gstrcpy(), fnt_edit(), stridx()
int patmake(), patmatch(), strncmp()
errchk fntopn, fntgfn, syserr, malloc, realloc
begin
call smark (sp)
call salloc (rname, SZ_FNAME, TY_CHAR)
call salloc (fname, SZ_FNAME, TY_CHAR)
call salloc (extn, SZ_FNAME, TY_CHAR)
call salloc (pbuf, SZ_LINE, TY_CHAR)
call salloc (ebuf, SZ_LINE, TY_CHAR)
# Allocate list descriptor.
call malloc (list, LEN_FNTBHDR + LEN_INDEXVECTOR, TY_INT)
call malloc (sbuf, SZ_DEFSTRBUF, TY_CHAR)
B_MAGIC(list) = FNTB_MAGIC
maxstr = LEN_INDEXVECTOR
sz_sbuf = SZ_DEFSTRBUF
nextch = 1 # offset into string buffer
nstr = 0
# Read the file names into the string buffer. Dynamically adjust
# the size of the string buffer and/or index vector as necessary.
# There must always be at least SZ_FNAME chars left in the string
# buffer. The outer loop is over comma delimited fields of the
# filename template. The inner loop is over individual filenames.
ix = 1
while (fnt_getpat (template, ix, patp, npat, pbuf, SZ_LINE) > 0) {
first_string = nstr + 1
sortlist = (sort == YES)
nlists = 0
ep = ebuf
# Each piece of the current comma delimited template may consist
# of several sublists to be independently expanded and concatenated
# to form each output filename. The lists must either be degenerate
# (a simple string) or actual lists to be expanded with FNTOPN.
do i = 1, npat {
is_template[i] = false
is_edit[i] = false
nedit[i] = 0
op = patp[i]
# Examine sublist to see if it is a template or a string
# constant. If template, open file list. Template
# metacharacters may be escaped to be included in filenames.
# If the pattern contains edit substitution sequences it
# must be processed to remove the substitution strings.
is_url = false
for (ip=op; Memc[ip] != EOS; ip=ip+1) {
ch = Memc[ip]
if (ch == ':' && strncmp (Memc[ip+1], "//", 2) == 0) {
# URL string.
is_template[i] = false
is_edit[i] = false
is_url = true
} else if (!is_url && stridx (Memc[ip], "@*?[%") > 0) {
if (ip > patp[i] && Memc[ip-1] == '\\') {
Memc[op-1] = ch
ip = ip + 1
ch = Memc[ip]
} else if (ch == CH_EDIT) {
is_edit[i] = true
} else {
if (ch == '@' && op == ip)
sortlist = false
if (!is_url)
is_template[i] = true
}
}
Memc[op] = ch
op = op + 1
}
Memc[op] = EOS
# Open filename template if pattern contained metacharacters.
# A string constant containing edit string substitution is a
# special case, eg. "file%%_2%.ext".
if (is_template[i] || is_edit[i]) {
editp[i] = ep
call fnt_mkpat (Memc[patp[i]], Memc[fname], SZ_FNAME,
ep, nedit[i])
flist[i] = fntopn (Memc[fname])
# In the case of a string constant edit we do not really
# have a file template, but we open one anyhow just to
# make use of the common code and the descriptor.
if (!is_template[i]) {
# Encode the pattern (containing the %%).
junk = patmake (Memc[fname], Memc[U_PATTERN(flist[i])],
SZ_PATTERN)
# Strip the %% from the pattern, leaving the "input"
# filename in patp[i].
op = patp[i]
for (ip=fname; Memc[ip] != EOS; ip=ip+1)
if (Memc[ip] != CH_EDIT) {
Memc[op] = Memc[ip]
op = op + 1
}
Memc[op] = EOS
# Now match the stripped pattern against the %%
# pattern. This sets up U_PATTERN for the edit.
junk = patmatch (Memc[patp[i]],
Memc[U_PATTERN(flist[i])])
} else
nlists = nlists + 1
}
}
# Expand the template into a sequence of filenames in the string
# buffer, saving the indices of the list elements in the STRINDX
# array. Reallocate a larger buffer if necessary. If the sublists
# are not all the same length the shortest list will terminate the
# output list.
repeat {
# Concatenate the next element from each sublist; the sublists
# may be either real lists or string constants. Concatenate
# only to the root filename.
Memc[extn] = EOS
op = fname
do i = 1, npat {
# Save first extension field encountered and set op to
# end of root.
if (Memc[extn] == EOS)
for (ip=op-1; ip > fname; ip=ip-1)
if (Memc[ip] == '.') {
call strcpy (Memc[ip], Memc[extn], SZ_FNAME)
op = ip
break
}
# Concatenate the next file element. This can be either a
# file name from a file template, a constant file name from
# a string edit expression, or a simple string constant.
if (!is_url && (is_template[i] || is_edit[i])) {
ip = rname
pp = flist[i]
if (is_template[i]) {
if (fntgfn (pp, Memc[rname], SZ_FNAME) == EOF) {
op = fname
break
} else if (U_FILDES(pp) != NULL) {
# Reading from a directory or list; set offset
# of substring to be edited to exclude any
# ldir prefix, since this will not have been
# used for the pattern match.
nchars = gstrcpy (Memc[U_LDIR(pp)],Memc[op],ARB)
op = op + nchars
ip = ip + nchars
}
} else
call strcpy (Memc[patp[i]], Memc[rname], SZ_FNAME)
op = op + fnt_edit (Memc[ip], Memc[op], editp[i],
nedit[i], Memc[U_PATTERN(pp)])
} else {
op = op + gstrcpy (Memc[patp[i]], Memc[op], ARB)
}
}
# End of list if nothing returned.
if (op == fname)
break
# Tack extension back on.
if (Memc[extn] != EOS)
op = op + gstrcpy (Memc[extn], Memc[op], ARB)
# Need more room for list element pointers?
nstr = nstr + 1
if (nstr > maxstr) {
maxstr = maxstr + LEN_INDEXVECTOR
call realloc (list, LEN_FNTBHDR + maxstr, TY_INT)
}
# Out of space in string buffer?
if (nextch + (op - fname) >= sz_sbuf) {
sz_sbuf = sz_sbuf + SZ_DEFSTRBUF
call realloc (sbuf, sz_sbuf, TY_CHAR)
}
# Save index of list element, move chars to string buffer.
# Allow space for the EOS after each string.
B_STRINDX(list,nstr) = nextch
nextch = nextch +
gstrcpy (Memc[fname], Memc[sbuf+nextch-1], ARB) + 1
} until (nlists == 0)
do i = 1, npat
if (is_template[i] || is_edit[i])
call fntcls (flist[i])
# If sorting is desired and the pattern did not specify an explicit
# list (e.g., "@listfile"), sort the last batch of filenames.
if (sortlist && nstr > first_string)
call strsrt (B_STRINDX(list,first_string), Memc[sbuf],
nstr - first_string + 1)
}
# Update the string buffer descriptor, return unused buffer space.
# Rewind the list in preparation for reading (set strnum=1).
call realloc (sbuf, nextch, TY_CHAR)
call realloc (list, LEN_FNTBHDR + nstr, TY_INT)
B_NSTR(list) = nstr
B_STRNUM(list) = 1
B_SBUFPTR(list) = sbuf
call sfree (sp)
return (list)
end
# FNT_MKPAT -- Take a pattern string possibly containing %a%b% string
# substitution sequences, returning a pattern string as required for PATMAKE,
# and a sequence of substitution strings for later use by FNT_EDIT to edit
# filenames matched by FNTGFN.
procedure fnt_mkpat (pat, patstr, maxch, ep, nedit)
char pat[ARB] # pattern with embedded substitution sequences
char patstr[maxch] # receives pattern as req'd by PATMAKE
int maxch
pointer ep # where to put substitution string chars
int nedit # number of substitution chars
int nhat
int ip, op
begin
nedit = 0
nhat = 0
op = 1
for (ip=1; pat[ip] != EOS; ip=ip+1) {
if (pat[ip] == CH_EDIT) {
if (ip > 1 && pat[ip-1] == '\\') {
# Moved escaped metacharacter to pattern string.
patstr[op] = pat[ip]
op = op + 1
} else if (nhat > 0) {
# Copy substitution string to ebuf.
patstr[op] = pat[ip]
op = op + 1
nedit = nedit + 1
ip = ip + 1
while (pat[ip] != EOS && pat[ip] != CH_EDIT) {
Memc[ep] = pat[ip]
ep = ep + 1
ip = ip + 1
}
Memc[ep] = EOS
ep = ep + 1
if (pat[ip] == EOS)
ip = ip - 1
nhat = 0
} else {
patstr[op] = pat[ip]
op = op + 1
nhat = nhat + 1
}
} else {
patstr[op] = pat[ip]
op = op + 1
if (op > maxch)
break
}
}
patstr[op] = EOS
end
# FNT_EDIT -- Perform string substitution on a matched filename, using the
# list of substitution strings written by FNT_MKPAT, the first of which is
# pointed to by EDITP. The regions to be replaced were marked symbolically
# by the CH_EDIT characters in the user supplied pattern. The actual indices
# of these regions depend upon the actual filename and are saved by the
# pattern matching code in the encoded pattern buffer PATBUF, for retrieval
# by PATINDEX. Carry out the substitution and return the length of the
# output string as the function argument.
int procedure fnt_edit (in, out, editp, nedit, patbuf)
char in[ARB] # input string to be edited
char out[ARB] # receives edited string
pointer editp # pointer to first substitution string
int nedit # number of edits required
char patbuf[ARB] # encoded pattern
pointer ep
int ip1, ip2, ip, op, i
int patindex()
begin
ep = editp - 1
ip = 1
op = 1
do i = 1, nedit {
# Get indices of first and last+1 characters to be substituted for
# in the input string.
ip1 = patindex (patbuf, (i-1) * 2 + 1)
ip2 = patindex (patbuf, (i-1) * 2 + 2)
if (ip1 == 0 || ip2 == 0 || ip1 > ip2)
break # cannot happen
# Copy up to first char to be replaced.
for (; ip < ip1; ip=ip+1) {
out[op] = in[ip]
op = op + 1
}
# Append substitution string.
for (ep=ep+1; Memc[ep] != EOS; ep=ep+1) {
out[op] = Memc[ep]
op = op + 1
}
# Continue at character IP2 in the input string.
ip = ip2
}
# Copy remainder of input string to the output string.
for (; in[ip] != EOS; ip=ip+1) {
out[op] = in[ip]
op = op + 1
}
out[op] = EOS
return (op - 1)
end
# FNTGFNB -- Return the next filename from the list.
int procedure fntgfnb (list, fname, maxch)
pointer list # list descriptor pointer
char fname[ARB] # output filename
int maxch
pointer strptr
int file_number
int gstrcpy()
errchk syserr
begin
if (B_MAGIC(list) != FNTB_MAGIC)
call syserr (SYS_FNTMAGIC)
file_number = B_STRNUM(list)
if (file_number > B_NSTR(list))
return (EOF)
else {
B_STRNUM(list) = file_number + 1
strptr = B_SBUFPTR(list) + B_STRINDX(list,file_number) - 1
return (gstrcpy (Memc[strptr], fname, maxch))
}
end
# FNTRFNB -- Return the indexed filename from the list. For applications
# which need to access the list at random. Returns len(fname) or EOF for
# references to nonexistent list elements.
int procedure fntrfnb (list, index, fname, maxch)
pointer list # list descriptor pointer
int index # index of list element to be returned
char fname[ARB] # output filename
int maxch
pointer strptr
int gstrcpy()
errchk syserr
begin
if (B_MAGIC(list) != FNTB_MAGIC)
call syserr (SYS_FNTMAGIC)
if (index < 1 || index > B_NSTR(list))
return (EOF)
else {
strptr = B_SBUFPTR(list) + B_STRINDX(list,index) - 1
return (gstrcpy (Memc[strptr], fname, maxch))
}
end
# FNTCLSB -- Close a buffered list and return all storage.
procedure fntclsb (list)
pointer list # list descriptor pointer
errchk syserr
begin
if (B_MAGIC(list) != FNTB_MAGIC)
call syserr (SYS_FNTMAGIC)
call mfree (B_SBUFPTR(list), TY_CHAR)
call mfree (list, TY_INT)
end
# FNTREWB -- Rewind a buffered filename list.
procedure fntrewb (list)
pointer list # list descriptor pointer
errchk syserr
begin
if (B_MAGIC(list) != FNTB_MAGIC)
call syserr (SYS_FNTMAGIC)
B_STRNUM(list) = 1
end
# FNTLENB -- Return the number of filenames in the list.
int procedure fntlenb (list)
pointer list # list descriptor pointer
errchk syserr
begin
if (B_MAGIC(list) != FNTB_MAGIC)
call syserr (SYS_FNTMAGIC)
return (B_NSTR(list))
end
# FNT_GETPAT -- Return the next comma delimited field from the template string
# with any leading or trailing whitespace stripped off. The field may consist
# of a simple string constant, a filename template, or a sequence of either
# delimited by concatenation operators //. We do not make any distinction here
# between string constants and patterns; return the \ with all escape sequences
# as this will be stripped by the higher level code if used to include pattern
# matching metacharacters in filenames.
int procedure fnt_getpat (template, ix, patp, npat, sbuf, maxch)
char template[ARB] # template from which to extract field
int ix # next char in template
pointer patp[MAX_PATTERNS] # receives pointers to sublists (patterns)
int npat # receives number of PATP elements set
pointer sbuf # used to store output strings
int maxch # maxch chars out
int ch, peek
bool is_url
pointer op
int strncmp(), stridx()
errchk syserr
begin
while (IS_WHITE(template[ix]) || template[ix] == ',')
ix = ix + 1
patp[1] = sbuf
npat = 1
op = sbuf
is_url = false
#for (ch=template[ix]; ch != EOS && ch != ','; ch=template[ix]) {
for (ch=template[ix]; ch != EOS; ch=template[ix]) {
peek = template[ix+1]
if (IS_WHITE (ch)) {
# Ignore all whitespace.
ix = ix + 1
next
} else if ((is_url && ch == ',')) {
if (stridx (peek, "+-.0123456789") == 0) {
break
} else {
# Keep a comma in a URL followed by a digit
Memc[op] = ','
op = op + 1
ix = ix + 1
}
} else if (!is_url && ch == ',') {
break
} else if (ch == '\\' && template[ix+1] == ',') {
# Escape a comma.
Memc[op] = ','
op = op + 1
ix = ix + 2
} else if (!is_url && (ch == '/' && template[ix+1] == '/')) {
# Concatenation operator: start a new sublist.
Memc[op] = EOS
op = op + 1
ix = ix + 2
npat = npat + 1
if (npat > MAX_PATTERNS)
call syserr (SYS_FNTMAXPAT)
patp[npat] = op
} else if (ch == ':' && strncmp ("//", template[ix+1], 2) == 0) {
# Start of URL string, deposit in output list.
Memc[op] = ch
op = op + 1
ix = ix + 1
is_url = true
} else {
# Ordinary character, deposit in output list.
Memc[op] = ch
op = op + 1
ix = ix + 1
}
if (op - sbuf > maxch)
break
}
Memc[op] = EOS
return (op - sbuf)
end
# FNTGFN -- Get the next file name from the named parameter (template).
# This is the guy that does all the work. A file name may be selected from
# a directory file or list file by pattern matching, or may come from the
# template list string itself.
int procedure fntgfn (pp, outstr, maxch)
pointer pp # pattern pointer
char outstr[ARB] # output filename
int maxch
bool match
pointer ip, sp, linebuf, fname, patstr
int nchars, token, first_ch, last_ch, status
bool streq()
int getline(), gpatmatch(), patmake(), nowhite(), gstrcat()
int fnt_read_template(), fnt_open_list()
errchk salloc, getline, close, fnt_open_list, syserr
begin
if (pp == NULL || U_MAGIC(pp) != FNTU_MAGIC)
call syserr (SYS_FNTMAGIC)
call smark (sp) # get buffers
call salloc (linebuf, SZ_LINE, TY_CHAR)
call salloc (patstr, SZ_PATSTR, TY_CHAR)
call salloc (fname, SZ_PATHNAME, TY_CHAR)
repeat {
# Read file names from either list file or directory file, until
# one is found which matches pattern, or until EOF is reached.
# Make sure pattern matches the ENTIRE file name string, rather
# than a substring.
if (U_FILDES(pp) != NULL) { # reading from a file?
while (getline (U_FILDES(pp), Memc[linebuf]) != EOF) {
for (ip=linebuf; IS_WHITE (Memc[ip]); ip=ip+1)
;
nchars = nowhite (Memc[ip], Memc[fname], maxch)
if (nchars == 0) # skip blank lines
next
# If the encoded pattern is the null string match anything.
if (Memc[U_PATTERN(pp)] == EOS) {
match = true
} else if (gpatmatch (Memc[fname], Memc[U_PATTERN(pp)],
first_ch, last_ch) > 0) {
match = (first_ch == 1 && last_ch == nchars)
} else
match = false
if (match) {
call strcpy (Memc[U_LDIR(pp)], outstr, maxch)
nchars = gstrcat (Memc[fname], outstr, maxch)
call sfree (sp)
return (nchars)
}
}
call close (U_FILDES(pp))
U_FILDES(pp) = NULL
}
switch (fnt_read_template (pp, Memc[linebuf], SZ_LINE, token)) {
case EO_TEMPLATE:
nchars = EOF
outstr[1] = EOS
call sfree (sp)
return (nchars)
case LIST_FILE, PATTERN_STRING:
# Break the pattern string into a list file or directory
# name and a pattern.
if (token == PATTERN_STRING) {
Memc[patstr] = '^'
ip = patstr + 1
} else
ip = patstr
U_FILDES(pp) = fnt_open_list (Memc[linebuf], Memc[ip],
SZ_PATSTR-1, Memc[fname], Memc[U_LDIR(pp)], token)
# Encode the pattern. If the pattern is matchall set encoded
# pattern string to NULL and pattern matching will be skipped.
if (streq (Memc[patstr], "?*"))
Memc[U_PATTERN(pp)] = EOS
else {
status = patmake (Memc[patstr], Memc[U_PATTERN(pp)],
SZ_PATTERN)
if (status == ERR)
call syserr (SYS_FNTBADPAT)
}
default: # simple file name
nchars = nowhite (Memc[linebuf], outstr, maxch)
if (nchars > 0) {
call sfree (sp)
return (nchars)
}
}
}
end
# FNT_READ_TEMPLATE -- Get next token from template string, return integer
# code identifying the type of token.
int procedure fnt_read_template (pp, outstr, maxch, token)
pointer pp #I pointer to param descriptor
char outstr[maxch] #O receives token
int maxch #I max chars out
int token #O token type code
int nseen, i
pointer ip, ip_start, op, cp
int stridx(), strncmp()
begin
ip = U_TEMPLATE_INDEX(pp) # retrieve pointer
while (IS_WHITE (Memc[ip]))
ip = ip + 1
switch (Memc[ip]) {
case EOS:
op = 1
token = EO_TEMPLATE
case LIST_FILE_CHAR: # list file spec
ip = ip + 1 # skip the @
for (op=1; Memc[ip] != TOK_DELIM && Memc[ip] != EOS; op=op+1) {
outstr[op] = Memc[ip]
ip = ip + 1
}
token = LIST_FILE
if (Memc[ip] == TOK_DELIM)
ip = ip + 1
default: # fname or pat string
token = FILE_NAME
# Extract token. Determine if regular file name or pattern string.
# Disable metacharacters not useful for file name patterns.
ip_start = ip
for (op=1; Memc[ip] != EOS; ip=ip+1) {
if (Memc[ip] == CH_ESCAPE && Memc[ip+1] != EOS) {
# Escape sequence. Pass both the escape and the escaped
# character on to the lower level code.
outstr[op] = CH_ESCAPE
op = op + 1
ip = ip + 1
} else if (Memc[ip] == TOK_DELIM) {
ip = ip + 1
break
} else if (Memc[ip] == FNLDIR_CHAR || Memc[ip] == '/') {
token = FILE_NAME
} else if (Memc[ip] == '*') {
# Map "*" into "?*".
token = PATTERN_STRING
outstr[op] = '?'
op = op + 1
} else if (Memc[ip] == '%') {
# The % metacharacter must appear twice (not three times,
# as the high level code strips the subsitution field) to
# be recognized as the pattern substitution metacharacter.
nseen = 0
do i = 1, ARB {
cp = ip_start + i - 1
if (Memc[cp] == EOS || Memc[cp] == TOK_DELIM)
break
else if (Memc[cp] == '%' && Memc[cp-1] != '\\')
nseen = nseen + 1
}
if (nseen < 2) {
outstr[op] = CH_ESCAPE
op = op + 1
}
} else if (stridx (Memc[ip], "[?{") > 0)
token = PATTERN_STRING
outstr[op] = Memc[ip]
op = op + 1
}
}
# Remove any trailing whitespace.
op = op - 1
while (op > 0 && IS_WHITE (outstr[op]))
op = op - 1
outstr[op+1] = EOS
if (op > 0)
if (outstr[op] == FNLDIR_CHAR || outstr[op] == '/')
token = PATTERN_STRING
U_TEMPLATE_INDEX(pp) = ip # update pointer
return (token)
end
# FNT_OPEN_LIST -- Open list file or directory. If reading from a directory,
# open the current directory if a directory name is not given. Extract
# pattern string (if any), and return in PATSTR. If no pattern string is
# given, return a pattern which will match all files in the list.
int procedure fnt_open_list (str, patstr, maxch, fname, ldir, ftype)
int maxch, ftype
char ldir[SZ_LDIR]
char str[ARB], patstr[maxch], fname[SZ_FNAME]
int fd, ip, op, fnt_delim, pat_start, dirmode
int open(), diropen()
errchk open, diropen, fpathname
begin
op = 1
fnt_delim = NULL
pat_start = NULL
# Search for a valid directory prefix.
for (ip=1; str[ip] != EOS; ip=ip+1) {
fname[op] = str[ip]
if (ftype != LIST_FILE)
if (fname[op] == FNLDIR_CHAR || fname[op] == '//')
if (op == 1 || fname[op-1] != '\\') {
fnt_delim = op
pat_start = ip + 1
}
op = op + 1
}
fname[op] = EOS
if (ftype == LIST_FILE) {
if (fnt_delim != NULL)
fname[fnt_delim] = EOS
fd = open (fname, READ_ONLY, TEXT_FILE)
ldir[1] = EOS
} else {
if (fnt_delim != NULL) # specific directory
fname[fnt_delim+1] = EOS
else # current directory
fname[1] = EOS
call fpathname (fname, ldir, SZ_LDIR)
dirmode = SKIP_HIDDEN_FILES
if (pat_start != NULL) {
if (str[pat_start] == '.')
dirmode = PASS_HIDDEN_FILES
} else if (ftype != LIST_FILE && str[1] == '.')
dirmode = PASS_HIDDEN_FILES
fd = diropen (ldir, dirmode)
call strcpy (fname, ldir, SZ_LDIR)
}
# If pattern string is appended to list file name, extract
# it, otherwise set the default pattern "match all" (*).
op = 1
if (pat_start != NULL)
ip = pat_start
else if (ftype != LIST_FILE)
ip = 1
for (; str[ip] != EOS; ip=ip+1) {
patstr[op] = str[ip]
op = op + 1
}
# No pattern string given, default to "?*".
if (op == 1) {
patstr[1] = CH_ANY
patstr[2] = CH_CLOSURE
op = 3
}
patstr[op] = EOS
return (fd)
end
# FNTOPN -- Open and initialize the template descriptor.
pointer procedure fntopn (template)
char template[ARB]
pointer pp
int nchars
int strlen()
errchk calloc, malloc
begin
nchars = strlen (template)
call calloc (pp, LEN_FNTUHDR, TY_STRUCT)
call malloc (U_TEMPLATE(pp), nchars, TY_CHAR)
call strcpy (template, Memc[U_TEMPLATE(pp)], nchars)
U_TEMPLATE_INDEX(pp) = U_TEMPLATE(pp)
U_MAGIC(pp) = FNTU_MAGIC
return (pp)
end
# FNTCLS -- Close the template descriptor, return space.
procedure fntcls (pp)
pointer pp
errchk syserr
begin
if (pp == NULL || U_MAGIC(pp) != FNTU_MAGIC)
call syserr (SYS_FNTMAGIC)
if (U_FILDES(pp) != NULL)
call close (U_FILDES(pp))
call mfree (U_TEMPLATE(pp), TY_CHAR)
call mfree (pp, TY_STRUCT)
end
|