line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Noid; |
2
|
|
|
|
|
|
|
|
3
|
2
|
|
|
2
|
|
2174
|
use 5.000000; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
91
|
|
4
|
2
|
|
|
2
|
|
10
|
use strict; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
69
|
|
5
|
2
|
|
|
2
|
|
11
|
use warnings; |
|
2
|
|
|
|
|
6
|
|
|
2
|
|
|
|
|
381
|
|
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
require Exporter; |
8
|
|
|
|
|
|
|
our @ISA = qw(Exporter); |
9
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
our $VERSION; |
11
|
|
|
|
|
|
|
$VERSION = sprintf "%d.%02d", q$Name: Release-0-424 $ =~ /Release-(\d+)-(\d+)/; |
12
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
13
|
|
|
|
|
|
|
addmsg bind checkchar dbopen dbclose dbcreate dbinfo |
14
|
|
|
|
|
|
|
errmsg fetch getnoid hold hold_release hold_set |
15
|
|
|
|
|
|
|
locktest logmsg mint n2xdig note parse_template queue |
16
|
|
|
|
|
|
|
sample scope validate VERSION xdig |
17
|
|
|
|
|
|
|
); |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# Noid - Nice opaque identifiers (Perl module) |
20
|
|
|
|
|
|
|
# |
21
|
|
|
|
|
|
|
# Author: John A. Kunze, jak@ucop.edu, California Digital Library |
22
|
|
|
|
|
|
|
# Originally created, UCSF/CKM, November 2002 |
23
|
|
|
|
|
|
|
# |
24
|
|
|
|
|
|
|
# --------- |
25
|
|
|
|
|
|
|
# Copyright (c) 2002-2006 UC Regents |
26
|
|
|
|
|
|
|
# |
27
|
|
|
|
|
|
|
# Permission to use, copy, modify, distribute, and sell this software and |
28
|
|
|
|
|
|
|
# its documentation for any purpose is hereby granted without fee, provided |
29
|
|
|
|
|
|
|
# that (i) the above copyright notices and this permission notice appear in |
30
|
|
|
|
|
|
|
# all copies of the software and related documentation, and (ii) the names |
31
|
|
|
|
|
|
|
# of the UC Regents and the University of California are not used in any |
32
|
|
|
|
|
|
|
# advertising or publicity relating to the software without the specific, |
33
|
|
|
|
|
|
|
# prior written permission of the University of California. |
34
|
|
|
|
|
|
|
# |
35
|
|
|
|
|
|
|
# THE SOFTWARE IS PROVIDED "AS-IS" AND WITHOUT WARRANTY OF ANY KIND, |
36
|
|
|
|
|
|
|
# EXPRESS, IMPLIED OR OTHERWISE, INCLUDING WITHOUT LIMITATION, ANY |
37
|
|
|
|
|
|
|
# WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. |
38
|
|
|
|
|
|
|
# |
39
|
|
|
|
|
|
|
# IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE FOR ANY |
40
|
|
|
|
|
|
|
# SPECIAL, INCIDENTAL, INDIRECT OR CONSEQUENTIAL DAMAGES OF ANY KIND, |
41
|
|
|
|
|
|
|
# OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, |
42
|
|
|
|
|
|
|
# WHETHER OR NOT ADVISED OF THE POSSIBILITY OF DAMAGE, AND ON ANY |
43
|
|
|
|
|
|
|
# THEORY OF LIABILITY, ARISING OUT OF OR IN CONNECTION WITH THE USE |
44
|
|
|
|
|
|
|
# OR PERFORMANCE OF THIS SOFTWARE. |
45
|
|
|
|
|
|
|
# --------- |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
# Perl style note -- this code makes frequent use of a fast, big boolean |
48
|
|
|
|
|
|
|
# version of an if-elsif-else idiom that Perl encourages because entering |
49
|
|
|
|
|
|
|
# a { block } is relatively expensive, but it looks strange if you're not |
50
|
|
|
|
|
|
|
# used to it. Instead of |
51
|
|
|
|
|
|
|
# |
52
|
|
|
|
|
|
|
# if ( e1 && e2 && e3 ) { |
53
|
|
|
|
|
|
|
# s1; |
54
|
|
|
|
|
|
|
# s2; |
55
|
|
|
|
|
|
|
# ...; |
56
|
|
|
|
|
|
|
# } |
57
|
|
|
|
|
|
|
# elsif ( e4 || e5 && e6 ) { |
58
|
|
|
|
|
|
|
# s3; |
59
|
|
|
|
|
|
|
# } |
60
|
|
|
|
|
|
|
# else { |
61
|
|
|
|
|
|
|
# s4; |
62
|
|
|
|
|
|
|
# s5; |
63
|
|
|
|
|
|
|
# } |
64
|
|
|
|
|
|
|
# |
65
|
|
|
|
|
|
|
# we can write this series of test expressions and statements as |
66
|
|
|
|
|
|
|
# |
67
|
|
|
|
|
|
|
# e1 && e2 && e3 and |
68
|
|
|
|
|
|
|
# s1, |
69
|
|
|
|
|
|
|
# s2, |
70
|
|
|
|
|
|
|
# 1 or |
71
|
|
|
|
|
|
|
# e4 || e5 && e6 and |
72
|
|
|
|
|
|
|
# s3, |
73
|
|
|
|
|
|
|
# 1 or |
74
|
|
|
|
|
|
|
# s4, |
75
|
|
|
|
|
|
|
# s5, |
76
|
|
|
|
|
|
|
# 1; |
77
|
|
|
|
|
|
|
# |
78
|
|
|
|
|
|
|
# If we KNOW (not safest) that s2 and s3 are "true", we shorten it to |
79
|
|
|
|
|
|
|
# |
80
|
|
|
|
|
|
|
# e1 && e2 && e3 and |
81
|
|
|
|
|
|
|
# s1, |
82
|
|
|
|
|
|
|
# s2 |
83
|
|
|
|
|
|
|
# or |
84
|
|
|
|
|
|
|
# e4 || e5 && e6 and |
85
|
|
|
|
|
|
|
# s3 |
86
|
|
|
|
|
|
|
# or |
87
|
|
|
|
|
|
|
# s4, |
88
|
|
|
|
|
|
|
# s5 |
89
|
|
|
|
|
|
|
# ; |
90
|
|
|
|
|
|
|
# |
91
|
|
|
|
|
|
|
# For the big boolean form to work, you'll be well-advised to call make |
92
|
|
|
|
|
|
|
# your Perl calls with the parenthesized syntax, so that the commas |
93
|
|
|
|
|
|
|
# terminating the boolean statements don't get swallowed up by the Perl |
94
|
|
|
|
|
|
|
# functions and built-ins that you're using (eg, can get into trouble |
95
|
|
|
|
|
|
|
# unless you parenthesize your "print" statements). |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
# yyy many comment blocks are very out of date -- need thorough review |
98
|
|
|
|
|
|
|
# yyy make it so that http://uclibs.org/PID/foo maps to |
99
|
|
|
|
|
|
|
# ark.cdlib.org/ark:/13030/xzfoo [ requirement from SCP meeting May 2004] |
100
|
|
|
|
|
|
|
# yyy use "wantarray" function to return either number or message |
101
|
|
|
|
|
|
|
# when bailing out. |
102
|
|
|
|
|
|
|
# yyy add cdlpid doc to pod ? |
103
|
|
|
|
|
|
|
# yyy write about comparison with PURLs |
104
|
|
|
|
|
|
|
# yyy check chars, authentication, ordinal stored in metadata |
105
|
|
|
|
|
|
|
# yyy implement mod 4/8/16 distribution within large counter regions? |
106
|
|
|
|
|
|
|
# yyy implement count-down counters as well as count-up? |
107
|
|
|
|
|
|
|
# yyy make a shadow DB |
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# yyy upgrade ark-service and ERC.pm (which still use PDB.pm) |
110
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
# yyy bindallow(), binddeny() ???? |
112
|
|
|
|
|
|
|
|
113
|
2
|
|
|
2
|
|
11
|
use constant NOLIMIT => -1; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
190
|
|
114
|
2
|
|
|
2
|
|
12
|
use constant SEQNUM_MIN => 1; |
|
2
|
|
|
|
|
3
|
|
|
2
|
|
|
|
|
91
|
|
115
|
2
|
|
|
2
|
|
9
|
use constant SEQNUM_MAX => 1000000; |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
129
|
|
116
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
# The database must hold nearly arbitrary user-level identifiers |
118
|
|
|
|
|
|
|
# alongside various admin variables. In order not to conflict, we |
119
|
|
|
|
|
|
|
# require all admin variables to start with ":/", eg, ":/oacounter". |
120
|
|
|
|
|
|
|
# We use "$R/" frequently as our "reserved root" prefix. |
121
|
|
|
|
|
|
|
# |
122
|
|
|
|
|
|
|
my $R = ":"; # prefix for global top level of admin db variables |
123
|
|
|
|
|
|
|
|
124
|
2
|
|
|
2
|
|
8
|
use Fcntl qw(:DEFAULT :flock); |
|
2
|
|
|
|
|
4
|
|
|
2
|
|
|
|
|
1143
|
|
125
|
2
|
|
|
2
|
|
3331
|
use BerkeleyDB; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# Global %opendbtab is a hash that maps a hashref (as key) to a database |
128
|
|
|
|
|
|
|
# reference. At a minimum, we need opendbtab so that we avoid passing a |
129
|
|
|
|
|
|
|
# db reference to dbclose, which cannot do the final "untie" (see |
130
|
|
|
|
|
|
|
# "untie gotcha" documentation) while the caller's db reference is |
131
|
|
|
|
|
|
|
# still defined. |
132
|
|
|
|
|
|
|
# |
133
|
|
|
|
|
|
|
my %opendbtab; |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# To iterate over all Noids in the database, use |
136
|
|
|
|
|
|
|
# |
137
|
|
|
|
|
|
|
# each %hash |
138
|
|
|
|
|
|
|
# return $db or null |
139
|
|
|
|
|
|
|
# $flags one of O_RDONLY, O_RDWR, O_CREAT |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
our ($legalstring, $alphacount, $digitcount); |
142
|
|
|
|
|
|
|
our $locktest = 0; |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
# Adds an error message for a database pointer/object. If the message |
145
|
|
|
|
|
|
|
# pertains to a failed open, the pointer is null, in which case the |
146
|
|
|
|
|
|
|
# message gets saved to what essentially acts like a global (possible |
147
|
|
|
|
|
|
|
# threading conflict). |
148
|
|
|
|
|
|
|
# |
149
|
|
|
|
|
|
|
sub addmsg{ my( $noid, $message )=@_; |
150
|
|
|
|
|
|
|
|
151
|
|
|
|
|
|
|
$noid ||= ""; # act like a global in case $noid undefined |
152
|
|
|
|
|
|
|
$opendbtab{"msg/$noid"} .= $message . "\n"; |
153
|
|
|
|
|
|
|
return 1; |
154
|
|
|
|
|
|
|
} |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
# Returns accumulated messages for a database pointer/object. If the |
157
|
|
|
|
|
|
|
# second argument is non-zero, also reset the message to the empty string. |
158
|
|
|
|
|
|
|
# |
159
|
|
|
|
|
|
|
sub errmsg{ my( $noid, $reset )=@_; |
160
|
|
|
|
|
|
|
|
161
|
|
|
|
|
|
|
$noid ||= ""; # act like a global in case $noid undefined |
162
|
|
|
|
|
|
|
my $s = $opendbtab{"msg/$noid"}; |
163
|
|
|
|
|
|
|
$reset and |
164
|
|
|
|
|
|
|
$opendbtab{"msg/$noid"} = ""; |
165
|
|
|
|
|
|
|
return $s; |
166
|
|
|
|
|
|
|
} |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
sub logmsg{ my( $noid, $message )=@_; |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
$noid ||= ""; # act like a global in case $noid undefined |
171
|
|
|
|
|
|
|
my $logfhandle = $opendbtab{"log/$noid"}; |
172
|
|
|
|
|
|
|
defined($logfhandle) and |
173
|
|
|
|
|
|
|
print($logfhandle $message, "\n"); |
174
|
|
|
|
|
|
|
# yyy file was opened for append -- hopefully that means always |
175
|
|
|
|
|
|
|
# append even if others have appended to it since our last append; |
176
|
|
|
|
|
|
|
# possible sync problems... |
177
|
|
|
|
|
|
|
return 1; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub storefile { my( $fname, $contents )=@_; |
181
|
|
|
|
|
|
|
! open(OUT, ">$fname") and |
182
|
|
|
|
|
|
|
return 0; |
183
|
|
|
|
|
|
|
print OUT $contents; |
184
|
|
|
|
|
|
|
close(OUT); |
185
|
|
|
|
|
|
|
return 1; |
186
|
|
|
|
|
|
|
} |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
# Legal values of $how for the bind function. |
189
|
|
|
|
|
|
|
# |
190
|
|
|
|
|
|
|
my @valid_hows = qw( |
191
|
|
|
|
|
|
|
new replace set |
192
|
|
|
|
|
|
|
append prepend add insert |
193
|
|
|
|
|
|
|
delete purge mint peppermint |
194
|
|
|
|
|
|
|
); |
195
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
# |
197
|
|
|
|
|
|
|
# --- begin alphabetic listing (with a few exceptions) of functions --- |
198
|
|
|
|
|
|
|
# |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# Returns ANVL message on success, undef on error. |
201
|
|
|
|
|
|
|
# |
202
|
|
|
|
|
|
|
sub bind { my( $noid, $contact, $validate, $how, $id, $elem, $value )=@_; |
203
|
|
|
|
|
|
|
# yyy to add: incr, decr for $how; possibly other ops (* + - / **) |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
# Validate identifier and element if necessary. |
206
|
|
|
|
|
|
|
# |
207
|
|
|
|
|
|
|
# yyy to do: check $elem against controlled vocab |
208
|
|
|
|
|
|
|
# (for errors more than for security) |
209
|
|
|
|
|
|
|
# yyy should this genonly setting be so capable of contradicting |
210
|
|
|
|
|
|
|
# the $validate arg? |
211
|
|
|
|
|
|
|
$$noid{"$R/genonly"} && $validate |
212
|
|
|
|
|
|
|
&& ! validate($noid, "-", $id) and |
213
|
|
|
|
|
|
|
return(undef) |
214
|
|
|
|
|
|
|
or |
215
|
|
|
|
|
|
|
! defined($id) || $id eq "" and |
216
|
|
|
|
|
|
|
addmsg($noid, "error: bind needs an identifier specified."), |
217
|
|
|
|
|
|
|
return(undef) |
218
|
|
|
|
|
|
|
; |
219
|
|
|
|
|
|
|
! defined($elem) || $elem eq "" and |
220
|
|
|
|
|
|
|
addmsg($noid, qq@error: "bind $how" requires an element name.@), |
221
|
|
|
|
|
|
|
return(undef); |
222
|
|
|
|
|
|
|
|
223
|
|
|
|
|
|
|
# Transform and place a "hold" (if "long" term and we're not deleting) |
224
|
|
|
|
|
|
|
# on a special identifier. Right now that means a user-entrered Id |
225
|
|
|
|
|
|
|
# of the form :idmap/Idpattern. In this case, change it to a database |
226
|
|
|
|
|
|
|
# Id of the form "$R/idmap/$elem", and change $elem to hold Idpattern; |
227
|
|
|
|
|
|
|
# this makes lookup faster and easier. |
228
|
|
|
|
|
|
|
# |
229
|
|
|
|
|
|
|
# First save original id and element names in $oid and $oelem to |
230
|
|
|
|
|
|
|
# use for all user messages; we use whatever is in $id and $elem |
231
|
|
|
|
|
|
|
# for actual database operations. |
232
|
|
|
|
|
|
|
# |
233
|
|
|
|
|
|
|
my ($oid, $oelem, $hold) = ($id, $elem, 0); |
234
|
|
|
|
|
|
|
if ($id =~ /^:/) { |
235
|
|
|
|
|
|
|
$id !~ m|^:idmap/(.+)| and |
236
|
|
|
|
|
|
|
addmsg($noid, qq@error: $oid: id cannot begin with ":"@ |
237
|
|
|
|
|
|
|
. qq@ unless of the form ":idmap/Idpattern".@), |
238
|
|
|
|
|
|
|
return(undef); |
239
|
|
|
|
|
|
|
($id, $elem) = ("$R/idmap/$oelem", $1); |
240
|
|
|
|
|
|
|
$$noid{"$R/longterm"} and |
241
|
|
|
|
|
|
|
$hold = 1; |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
# yyy transform other ids beginning with ":"? |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
# Check circulation status. Error if term is "long" and the id |
246
|
|
|
|
|
|
|
# hasn't been issued unless a hold was placed on it. |
247
|
|
|
|
|
|
|
# |
248
|
|
|
|
|
|
|
# If no circ record and no hold... |
249
|
|
|
|
|
|
|
if (! defined($$noid{"$id\t$R/c"}) && ! exists($$noid{"$id\t$R/h"})) { |
250
|
|
|
|
|
|
|
$$noid{"$R/longterm"} and |
251
|
|
|
|
|
|
|
addmsg($noid, "error: " |
252
|
|
|
|
|
|
|
. qq@$oid: "long" term disallows binding @ |
253
|
|
|
|
|
|
|
. "an unissued identifier unless a hold is " |
254
|
|
|
|
|
|
|
. "first placed on it."), |
255
|
|
|
|
|
|
|
return(undef) |
256
|
|
|
|
|
|
|
or |
257
|
|
|
|
|
|
|
logmsg($noid, "warning:" |
258
|
|
|
|
|
|
|
. " $oid: binding an unissued identifier" |
259
|
|
|
|
|
|
|
. " that has no hold placed on it.") |
260
|
|
|
|
|
|
|
; |
261
|
|
|
|
|
|
|
} |
262
|
|
|
|
|
|
|
if (grep(/^$how$/, @valid_hows) != 1) { |
263
|
|
|
|
|
|
|
addmsg($noid, "error: bind how? What does $how mean?"); |
264
|
|
|
|
|
|
|
return(undef); |
265
|
|
|
|
|
|
|
} |
266
|
|
|
|
|
|
|
my $peppermint = ($how eq "peppermint"); |
267
|
|
|
|
|
|
|
$peppermint and |
268
|
|
|
|
|
|
|
# yyy to do |
269
|
|
|
|
|
|
|
addmsg($noid, qq@error: bind "peppermint" not implemented.@), |
270
|
|
|
|
|
|
|
return(undef); |
271
|
|
|
|
|
|
|
# YYY bind mint file Elem Value -- put into FILE by itself |
272
|
|
|
|
|
|
|
# YYY bind mint stuff_into_big_file Elem Value -- cat into file |
273
|
|
|
|
|
|
|
if ($how eq "mint" || $how eq "peppermint") { |
274
|
|
|
|
|
|
|
$id ne "new" and |
275
|
|
|
|
|
|
|
addmsg(qq@error: bind "mint" requires id to be @ |
276
|
|
|
|
|
|
|
. qq@given as "new".@), |
277
|
|
|
|
|
|
|
return(undef); |
278
|
|
|
|
|
|
|
! ($id = $oid = mint($noid, $contact, $peppermint)) and |
279
|
|
|
|
|
|
|
return(undef); |
280
|
|
|
|
|
|
|
} |
281
|
|
|
|
|
|
|
$how eq "delete" || $how eq "purge" and |
282
|
|
|
|
|
|
|
(defined($value) && $value eq "" and |
283
|
|
|
|
|
|
|
addmsg($noid, qq@error: why does "bind $how" @ |
284
|
|
|
|
|
|
|
. "have a supplied value ($value)?"), |
285
|
|
|
|
|
|
|
return(undef)), |
286
|
|
|
|
|
|
|
$value = "", |
287
|
|
|
|
|
|
|
1 |
288
|
|
|
|
|
|
|
or |
289
|
|
|
|
|
|
|
! defined($value) and |
290
|
|
|
|
|
|
|
addmsg($noid, |
291
|
|
|
|
|
|
|
qq@error: "bind $how $elem" requires a value to bind.@), |
292
|
|
|
|
|
|
|
return(undef) |
293
|
|
|
|
|
|
|
; |
294
|
|
|
|
|
|
|
# If we get here, $value is defined and we can use with impunity. |
295
|
|
|
|
|
|
|
|
296
|
|
|
|
|
|
|
dblock(); |
297
|
|
|
|
|
|
|
if (! defined($$noid{"$id\t$elem"})) { # currently unbound |
298
|
|
|
|
|
|
|
grep(/^$how$/, qw( replace append prepend delete )) == 1 and |
299
|
|
|
|
|
|
|
addmsg($noid, qq@error: for "bind $how", "$oid $oelem" @ |
300
|
|
|
|
|
|
|
. "must already be bound."), |
301
|
|
|
|
|
|
|
dbunlock(), |
302
|
|
|
|
|
|
|
return(undef); |
303
|
|
|
|
|
|
|
$$noid{"$id\t$elem"} = ""; # can concatenate with impunity |
304
|
|
|
|
|
|
|
} |
305
|
|
|
|
|
|
|
else { # currently bound |
306
|
|
|
|
|
|
|
grep(/^$how$/, qw( new mint peppermint )) == 1 and |
307
|
|
|
|
|
|
|
addmsg($noid, qq@error: for "bind $how", "$oid $oelem" @ |
308
|
|
|
|
|
|
|
. " cannot already be bound."), |
309
|
|
|
|
|
|
|
dbunlock(), |
310
|
|
|
|
|
|
|
return(undef); |
311
|
|
|
|
|
|
|
} |
312
|
|
|
|
|
|
|
# We don't care about bound/unbound for: set, add, insert, purge |
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
my $oldlen = length($$noid{"$id\t$elem"}); |
315
|
|
|
|
|
|
|
my $newlen = length($value); |
316
|
|
|
|
|
|
|
my $statmsg = "$newlen bytes written"; |
317
|
|
|
|
|
|
|
|
318
|
|
|
|
|
|
|
$how eq "delete" || $how eq "purge" and |
319
|
|
|
|
|
|
|
delete($$noid{"$id\t$elem"}), |
320
|
|
|
|
|
|
|
$statmsg = "$oldlen bytes removed" |
321
|
|
|
|
|
|
|
or |
322
|
|
|
|
|
|
|
$how eq "add" || $how eq "append" and |
323
|
|
|
|
|
|
|
$$noid{"$id\t$elem"} .= $value, |
324
|
|
|
|
|
|
|
$statmsg .= " to the end of $oldlen bytes", |
325
|
|
|
|
|
|
|
or |
326
|
|
|
|
|
|
|
$how eq "insert" || $how eq "prepend" and |
327
|
|
|
|
|
|
|
$$noid{"$id\t$elem"} = $value . $$noid{"$id\t$elem"}, |
328
|
|
|
|
|
|
|
$statmsg .= " to the beginning of $oldlen bytes", |
329
|
|
|
|
|
|
|
or |
330
|
|
|
|
|
|
|
$$noid{"$id\t$elem"} = $value, |
331
|
|
|
|
|
|
|
$statmsg .= ", replacing $oldlen bytes", |
332
|
|
|
|
|
|
|
; |
333
|
|
|
|
|
|
|
$hold and exists($$noid{"$id\t$elem"}) and ! hold_set($noid, $id) and |
334
|
|
|
|
|
|
|
$hold = -1; # don't just bail out -- we need to unlock |
335
|
|
|
|
|
|
|
|
336
|
|
|
|
|
|
|
# yyy $contact info ? mainly for "long" term identifiers? |
337
|
|
|
|
|
|
|
dbunlock(); |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
return( |
340
|
|
|
|
|
|
|
# yyy should this $id be or not be $oid??? |
341
|
|
|
|
|
|
|
# yyy should labels for Id and Element be lowercased??? |
342
|
|
|
|
|
|
|
"Id: $id |
343
|
|
|
|
|
|
|
Element: $elem |
344
|
|
|
|
|
|
|
Bind: $how |
345
|
|
|
|
|
|
|
Status: " . ($hold == -1 ? errmsg($noid) : "ok, $statmsg") . "\n"); |
346
|
|
|
|
|
|
|
} |
347
|
|
|
|
|
|
|
|
348
|
|
|
|
|
|
|
# Primes: |
349
|
|
|
|
|
|
|
# 2 3 5 7 |
350
|
|
|
|
|
|
|
# 11 13 17 19 |
351
|
|
|
|
|
|
|
# 23 29 31 37 |
352
|
|
|
|
|
|
|
# 41 43 47 53 |
353
|
|
|
|
|
|
|
# 59 61 67 71 |
354
|
|
|
|
|
|
|
# 73 79 83 89 |
355
|
|
|
|
|
|
|
# 97 101 103 107 |
356
|
|
|
|
|
|
|
# 109 113 127 131 |
357
|
|
|
|
|
|
|
# 137 139 149 151 |
358
|
|
|
|
|
|
|
# 157 163 167 173 |
359
|
|
|
|
|
|
|
# 179 181 191 193 |
360
|
|
|
|
|
|
|
# 197 199 211 223 |
361
|
|
|
|
|
|
|
# 227 229 233 239 |
362
|
|
|
|
|
|
|
# 241 251 257 263 |
363
|
|
|
|
|
|
|
# 269 271 277 281 |
364
|
|
|
|
|
|
|
# 283 293 307 311 |
365
|
|
|
|
|
|
|
# 313 317 331 337 |
366
|
|
|
|
|
|
|
# 347 349 353 359 |
367
|
|
|
|
|
|
|
# 367 373 379 383 |
368
|
|
|
|
|
|
|
# 389 397 401 409 |
369
|
|
|
|
|
|
|
# 419 421 431 433 |
370
|
|
|
|
|
|
|
# 439 443 449 457 |
371
|
|
|
|
|
|
|
# 461 463 467 479 |
372
|
|
|
|
|
|
|
# 487 491 499 503 ... |
373
|
|
|
|
|
|
|
|
374
|
|
|
|
|
|
|
# yyy other character subsets? eg, 0-9, a-z, and _ (37 chars, with 37 prime) |
375
|
|
|
|
|
|
|
# this could be mask character 'w' ? |
376
|
|
|
|
|
|
|
# yyy there are 94 printable ASCII characters, with nearest lower prime = 89 |
377
|
|
|
|
|
|
|
# a radix of 89 would result in a huge, compact space with check chars |
378
|
|
|
|
|
|
|
# mask character 'c' ? |
379
|
|
|
|
|
|
|
|
380
|
|
|
|
|
|
|
# Extended digits array. Maps ordinal value to ASCII character. |
381
|
|
|
|
|
|
|
my @xdig = ( |
382
|
|
|
|
|
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', |
383
|
|
|
|
|
|
|
'b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'm', 'n', |
384
|
|
|
|
|
|
|
'p', 'q', 'r', 's', 't', 'v', 'w', 'x', 'z' |
385
|
|
|
|
|
|
|
); |
386
|
|
|
|
|
|
|
# $legalstring should be 0123456789bcdfghjkmnpqrstvwxz |
387
|
|
|
|
|
|
|
$legalstring = join('', @xdig); |
388
|
|
|
|
|
|
|
$alphacount = scalar(@xdig); # extended digits count |
389
|
|
|
|
|
|
|
$digitcount = 10; # pure digit count |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
# Ordinal value hash for extended digits. Maps ASCII characters to ordinals. |
392
|
|
|
|
|
|
|
my %ordxdig = ( |
393
|
|
|
|
|
|
|
'0' => 0, '1' => 1, '2' => 2, '3' => 3, '4' => 4, |
394
|
|
|
|
|
|
|
'5' => 5, '6' => 6, '7' => 7, '8' => 8, '9' => 9, |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
'b' => 10, 'c' => 11, 'd' => 12, 'f' => 13, 'g' => 14, |
397
|
|
|
|
|
|
|
'h' => 15, 'j' => 16, 'k' => 17, 'm' => 18, 'n' => 19, |
398
|
|
|
|
|
|
|
|
399
|
|
|
|
|
|
|
'p' => 20, 'q' => 21, 'r' => 22, 's' => 23, 't' => 24, |
400
|
|
|
|
|
|
|
'v' => 25, 'w' => 26, 'x' => 27, 'z' => 28 |
401
|
|
|
|
|
|
|
); |
402
|
|
|
|
|
|
|
|
403
|
|
|
|
|
|
|
# Compute check character for given identifier. If identifier ends in '+' |
404
|
|
|
|
|
|
|
# (plus), replace it with a check character computed from the preceding chars, |
405
|
|
|
|
|
|
|
# and return the modified identifier. If not, isolate the last char and |
406
|
|
|
|
|
|
|
# compute a check character using the preceding chars; return the original |
407
|
|
|
|
|
|
|
# identifier if the computed char matches the isolated char, or undef if not. |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
# User explanation: check digits help systems to catch transcription |
410
|
|
|
|
|
|
|
# errors that users might not be aware of upon retrieval; while users |
411
|
|
|
|
|
|
|
# often have other knowledge with which to determine that the wrong |
412
|
|
|
|
|
|
|
# retrieval occurred, this error is sometimes not readily apparent. |
413
|
|
|
|
|
|
|
# Check digits reduce the chances of this kind of error. |
414
|
|
|
|
|
|
|
# yyy ask Steve Silberstein (of III) about check digits? |
415
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
sub checkchar{ my( $id )=@_; |
417
|
|
|
|
|
|
|
return undef |
418
|
|
|
|
|
|
|
if (! $id ); |
419
|
|
|
|
|
|
|
my $lastchar = chop($id); |
420
|
|
|
|
|
|
|
my $pos = 1; |
421
|
|
|
|
|
|
|
my $sum = 0; |
422
|
|
|
|
|
|
|
my $c; |
423
|
|
|
|
|
|
|
for $c (split(//, $id)) { |
424
|
|
|
|
|
|
|
# if character undefined, it's ordinal value is zero |
425
|
|
|
|
|
|
|
$sum += $pos * (defined($ordxdig{"$c"}) ? $ordxdig{"$c"} : 0); |
426
|
|
|
|
|
|
|
$pos++; |
427
|
|
|
|
|
|
|
} |
428
|
|
|
|
|
|
|
my $checkchar = $xdig[$sum % $alphacount]; |
429
|
|
|
|
|
|
|
#print "RADIX=$alphacount, mod=", $sum % $alphacount, "\n"; |
430
|
|
|
|
|
|
|
return $id . $checkchar |
431
|
|
|
|
|
|
|
if ($lastchar eq "+" || $lastchar eq $checkchar); |
432
|
|
|
|
|
|
|
return undef; # must be request to check, but failed match |
433
|
|
|
|
|
|
|
# xxx test if check char changes on permutations |
434
|
|
|
|
|
|
|
# XXX include test of length to make sure < than 29 (R) chars long |
435
|
|
|
|
|
|
|
# yyy will this work for doi/handles? |
436
|
|
|
|
|
|
|
} |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
# Returns an array of cleared ids and byte counts if $verbose is set, |
439
|
|
|
|
|
|
|
# otherwise returns an empty array. Set $verbose when we want to report what |
440
|
|
|
|
|
|
|
# was cleared. Admin bindings aren't touched; they must be cleared manually. |
441
|
|
|
|
|
|
|
# |
442
|
|
|
|
|
|
|
# We always check for bindings before issuing, because even a previously |
443
|
|
|
|
|
|
|
# unissued id may have been bound (unusual for many minter situations). |
444
|
|
|
|
|
|
|
# |
445
|
|
|
|
|
|
|
# Use dblock() before and dbunlock() after calling this routine. |
446
|
|
|
|
|
|
|
# |
447
|
|
|
|
|
|
|
sub clear_bindings { my( $noid, $id, $verbose )=@_; |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
my @retvals; |
450
|
|
|
|
|
|
|
my $db = $opendbtab{"bdb/$noid"}; |
451
|
|
|
|
|
|
|
my $cursor = $db->db_cursor(); |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
# yyy right now "$id\t" defines how we bind stuff to an id, but in the |
454
|
|
|
|
|
|
|
# future that could change. in particular we don't bind (now) |
455
|
|
|
|
|
|
|
# anything to just "$id" (without a tab after it) |
456
|
|
|
|
|
|
|
my ($first, $skip, $done) = ("$id\t", 0, 0); |
457
|
|
|
|
|
|
|
my ($key, $value) = ($first, 0); |
458
|
|
|
|
|
|
|
my $status = $cursor->c_get($key, $value, DB_SET_RANGE); |
459
|
|
|
|
|
|
|
$status == 0 and |
460
|
|
|
|
|
|
|
$skip = ($key =~ m|^$first$R/|), |
461
|
|
|
|
|
|
|
$done = ($key !~ m|^$first|), |
462
|
|
|
|
|
|
|
1 or |
463
|
|
|
|
|
|
|
$done = 1 |
464
|
|
|
|
|
|
|
; |
465
|
|
|
|
|
|
|
while (! $done) { |
466
|
|
|
|
|
|
|
! $skip and $verbose and |
467
|
|
|
|
|
|
|
# if $verbose (ie, fetch), include label and |
468
|
|
|
|
|
|
|
# remember to strip "Id\t" from front of $key |
469
|
|
|
|
|
|
|
push(@retvals, ($key =~ /^[^\t]*\t(.*)/ ? $1 : $key) |
470
|
|
|
|
|
|
|
. ": clearing " . length($value) . " bytes"), |
471
|
|
|
|
|
|
|
delete($$noid{$key}); |
472
|
|
|
|
|
|
|
$status = $cursor->c_get($key, $value, DB_NEXT); |
473
|
|
|
|
|
|
|
$status != 0 || $key !~ /^$first/ and |
474
|
|
|
|
|
|
|
$done = 1 # no more elements under id |
475
|
|
|
|
|
|
|
or |
476
|
|
|
|
|
|
|
$skip = ($key =~ m|^$first$R/|) |
477
|
|
|
|
|
|
|
; |
478
|
|
|
|
|
|
|
} |
479
|
|
|
|
|
|
|
undef($cursor); |
480
|
|
|
|
|
|
|
return($verbose ? @retvals : ()); |
481
|
|
|
|
|
|
|
} |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
# Returns a short printable message on success, undef on error. |
484
|
|
|
|
|
|
|
# |
485
|
|
|
|
|
|
|
sub dbcreate { my( $dbdir, $contact, $template, $term, |
486
|
|
|
|
|
|
|
$naan, $naa, $subnaa )=@_; |
487
|
|
|
|
|
|
|
|
488
|
|
|
|
|
|
|
my ($total, $noid); |
489
|
|
|
|
|
|
|
my $dir = "$dbdir/NOID"; |
490
|
|
|
|
|
|
|
my $dbname = "$dir/noid.bdb"; |
491
|
|
|
|
|
|
|
# yyy try to use "die" to communicate to caller (graceful?) |
492
|
|
|
|
|
|
|
# yyy how come tie doesn't complain if it exists already? |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
-e $dbname and |
495
|
|
|
|
|
|
|
addmsg(undef, "error: a NOID database already exists in " |
496
|
|
|
|
|
|
|
. ($dbdir ne "." ? "\"$dbdir\"." |
497
|
|
|
|
|
|
|
: "the current directory.") . "\n" |
498
|
|
|
|
|
|
|
. "\tTo permit creation of a new minter, rename\n" |
499
|
|
|
|
|
|
|
. "\tor remove the entire NOID subdirectory."), |
500
|
|
|
|
|
|
|
return(undef); |
501
|
|
|
|
|
|
|
! -d $dir && ! mkdir($dir) and |
502
|
|
|
|
|
|
|
addmsg(undef, "error: couldn't create database directory\n" |
503
|
|
|
|
|
|
|
. "$dir: $!\n"), |
504
|
|
|
|
|
|
|
return(undef); |
505
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
my ($prefix, $mask, $gen_type, $msg, $genonly); |
507
|
|
|
|
|
|
|
! defined($template) and |
508
|
|
|
|
|
|
|
$genonly = 0, |
509
|
|
|
|
|
|
|
$template = ".zd" |
510
|
|
|
|
|
|
|
or |
511
|
|
|
|
|
|
|
$genonly = 1, # not generated ids only |
512
|
|
|
|
|
|
|
; |
513
|
|
|
|
|
|
|
$total = parse_template($template, $prefix, $mask, $gen_type, $msg); |
514
|
|
|
|
|
|
|
! $total and |
515
|
|
|
|
|
|
|
addmsg($noid, $msg), |
516
|
|
|
|
|
|
|
return(undef); |
517
|
|
|
|
|
|
|
my $synonym = "noid" . ($genonly ? "_$msg" : "any"); |
518
|
|
|
|
|
|
|
|
519
|
|
|
|
|
|
|
# Type check various parameters. |
520
|
|
|
|
|
|
|
# |
521
|
|
|
|
|
|
|
! defined($contact) || $contact !~ /\S/ and |
522
|
|
|
|
|
|
|
addmsg($noid, "error: contact ($contact) must be non-empty."), |
523
|
|
|
|
|
|
|
return(undef); |
524
|
|
|
|
|
|
|
|
525
|
|
|
|
|
|
|
$term ||= "-"; |
526
|
|
|
|
|
|
|
$term ne "long" && $term ne "medium" |
527
|
|
|
|
|
|
|
&& $term ne "-" && $term ne "short" and |
528
|
|
|
|
|
|
|
addmsg($noid, "error: term ($term) must be either " |
529
|
|
|
|
|
|
|
. qq@"long", "medium", "-", or "short".@), |
530
|
|
|
|
|
|
|
return(undef); |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
! defined($naa) and $naa = ""; |
533
|
|
|
|
|
|
|
! defined($naan) and $naan = ""; |
534
|
|
|
|
|
|
|
! defined($subnaa) and $subnaa = ""; |
535
|
|
|
|
|
|
|
|
536
|
|
|
|
|
|
|
$term eq "long" && |
537
|
|
|
|
|
|
|
($naan !~ /\S/ || $naa !~ /\S/ || $subnaa !~ /\S/) and |
538
|
|
|
|
|
|
|
addmsg($noid, qq@error: longterm identifiers require @ |
539
|
|
|
|
|
|
|
. "an NAA Number, NAA, and SubNAA."), |
540
|
|
|
|
|
|
|
return(undef); |
541
|
|
|
|
|
|
|
# xxx should be able to check naa and naan live against registry |
542
|
|
|
|
|
|
|
# yyy code should invite to apply for NAAN by email to ark@cdlib.org |
543
|
|
|
|
|
|
|
# yyy ARK only? why not DOI/handle? |
544
|
|
|
|
|
|
|
$term eq "long" && ($naan !~ /\d\d\d\d\d/) and |
545
|
|
|
|
|
|
|
addmsg($noid, qq@error: term of "long" requires a @ |
546
|
|
|
|
|
|
|
. "5-digit NAAN (00000 if none), and non-empty " |
547
|
|
|
|
|
|
|
. "string values for NAA and SubNAA."), |
548
|
|
|
|
|
|
|
return(undef); |
549
|
|
|
|
|
|
|
|
550
|
|
|
|
|
|
|
# Create log and logbdb files from scratch and make them writable |
551
|
|
|
|
|
|
|
# before calling dbopen(). |
552
|
|
|
|
|
|
|
# |
553
|
|
|
|
|
|
|
! storefile("$dir/log", "") || ! chmod(0666, "$dir/log") and |
554
|
|
|
|
|
|
|
addmsg(undef, "Couldn't chmod log file: $!"), |
555
|
|
|
|
|
|
|
return(undef); |
556
|
|
|
|
|
|
|
! storefile("$dir/logbdb", "") || ! chmod(0666, "$dir/logbdb") and |
557
|
|
|
|
|
|
|
addmsg(undef, "Couldn't chmod logbdb file: $!"), |
558
|
|
|
|
|
|
|
return(undef); |
559
|
|
|
|
|
|
|
! ($noid = dbopen($dbname, DB_CREATE)) and |
560
|
|
|
|
|
|
|
addmsg(undef, "can't create database file: $!"), |
561
|
|
|
|
|
|
|
return(undef); |
562
|
|
|
|
|
|
|
logmsg($noid, ($template ? |
563
|
|
|
|
|
|
|
qq@Creating database for template "$template".@ |
564
|
|
|
|
|
|
|
: "Creating database for bind-only minter.")); |
565
|
|
|
|
|
|
|
|
566
|
|
|
|
|
|
|
# Database info |
567
|
|
|
|
|
|
|
# yyy should be using db-> ops directly (for efficiency and?) |
568
|
|
|
|
|
|
|
# so we can use DB_DUP flag |
569
|
|
|
|
|
|
|
$$noid{"$R/naa"} = $naa; |
570
|
|
|
|
|
|
|
$$noid{"$R/naan"} = $naan; |
571
|
|
|
|
|
|
|
$$noid{"$R/subnaa"} = $subnaa || ""; |
572
|
|
|
|
|
|
|
|
573
|
|
|
|
|
|
|
$$noid{"$R/longterm"} = ($term eq "long"); |
574
|
|
|
|
|
|
|
$$noid{"$R/wrap"} = ($term eq "short"); # yyy follow through |
575
|
|
|
|
|
|
|
|
576
|
|
|
|
|
|
|
$$noid{"$R/template"} = $template; |
577
|
|
|
|
|
|
|
$$noid{"$R/prefix"} = $prefix; |
578
|
|
|
|
|
|
|
$$noid{"$R/mask"} = $mask; |
579
|
|
|
|
|
|
|
$$noid{"$R/firstpart"} = ($naan ? $naan . "/" : "") . $prefix; |
580
|
|
|
|
|
|
|
$$noid{"$R/addcheckchar"} = ($mask =~ /k$/); # boolean answer |
581
|
|
|
|
|
|
|
|
582
|
|
|
|
|
|
|
$$noid{"$R/generator_type"} = $gen_type; |
583
|
|
|
|
|
|
|
$$noid{"$R/genonly"} = $genonly; |
584
|
|
|
|
|
|
|
|
585
|
|
|
|
|
|
|
$$noid{"$R/total"} = $total; |
586
|
|
|
|
|
|
|
$$noid{"$R/padwidth"} = ($total == NOLIMIT ? 16 : 2) + length($mask); |
587
|
|
|
|
|
|
|
# yyy kludge -- padwidth of 16 enough for most lvf sorting |
588
|
|
|
|
|
|
|
|
589
|
|
|
|
|
|
|
# Some variables: |
590
|
|
|
|
|
|
|
# oacounter overall counter's current value (last value minted) |
591
|
|
|
|
|
|
|
# oatop overall counter's greatest possible value of counter |
592
|
|
|
|
|
|
|
# held total with "hold" placed |
593
|
|
|
|
|
|
|
# queued total currently in the queue |
594
|
|
|
|
|
|
|
$$noid{"$R/oacounter"} = 0; |
595
|
|
|
|
|
|
|
$$noid{"$R/oatop"} = $total; |
596
|
|
|
|
|
|
|
$$noid{"$R/held"} = 0; |
597
|
|
|
|
|
|
|
$$noid{"$R/queued"} = 0; |
598
|
|
|
|
|
|
|
|
599
|
|
|
|
|
|
|
$$noid{"$R/fseqnum"} = SEQNUM_MIN; # see queue() and mint() |
600
|
|
|
|
|
|
|
$$noid{"$R/gseqnum"} = SEQNUM_MIN; # see queue() |
601
|
|
|
|
|
|
|
$$noid{"$R/gseqnum_date"} = 0; # see queue() |
602
|
|
|
|
|
|
|
|
603
|
|
|
|
|
|
|
$$noid{"$R/version"} = $VERSION; |
604
|
|
|
|
|
|
|
|
605
|
|
|
|
|
|
|
# yyy should verify that a given NAAN and NAA are registered, |
606
|
|
|
|
|
|
|
# and should offer to register them if not.... ? |
607
|
|
|
|
|
|
|
|
608
|
|
|
|
|
|
|
# Capture the properties of this minter. |
609
|
|
|
|
|
|
|
# |
610
|
|
|
|
|
|
|
# There are seven properties, represented by a string of seven |
611
|
|
|
|
|
|
|
# capital letters or a hyphen if the property does not apply. |
612
|
|
|
|
|
|
|
# The maximal string is GRANITE (we first had GRANT, then GARNET). |
613
|
|
|
|
|
|
|
# We don't allow 'l' as an extended digit (good for minimizing |
614
|
|
|
|
|
|
|
# visual transcriptions errors), but we don't get a chance to brag |
615
|
|
|
|
|
|
|
# about that here. |
616
|
|
|
|
|
|
|
# |
617
|
|
|
|
|
|
|
# Note that on the Mohs mineral hardness scale from 1 - 10, |
618
|
|
|
|
|
|
|
# the hardest is diamonds (which are forever), but granites |
619
|
|
|
|
|
|
|
# (combinations of feldspar and quartz) are 5.5 to 7 in hardness. |
620
|
|
|
|
|
|
|
# From http://geology.about.com/library/bl/blmohsscale.htm ; see also |
621
|
|
|
|
|
|
|
# http://www.mineraltown.com/infocoleccionar/mohs_scale_of_hardness.htm |
622
|
|
|
|
|
|
|
# |
623
|
|
|
|
|
|
|
# These are far from perfect measures of identifier durability, |
624
|
|
|
|
|
|
|
# and of course they are only from the assigner's point of view. |
625
|
|
|
|
|
|
|
# For example, an alphabetical restriction doesn't guarantee |
626
|
|
|
|
|
|
|
# opaqueness, but it indicates that semantics will be limited. |
627
|
|
|
|
|
|
|
# |
628
|
|
|
|
|
|
|
# yyy document that (I)mpressionable has to do with printing, does |
629
|
|
|
|
|
|
|
# not apply to general URLs, but does apply to phone numbers and |
630
|
|
|
|
|
|
|
# ISBNs and ISSNs |
631
|
|
|
|
|
|
|
# yyy document that the opaqueness test is English-centric -- these |
632
|
|
|
|
|
|
|
# measures work to some extent in English, but not in Welsh(?) |
633
|
|
|
|
|
|
|
# or "l33t" |
634
|
|
|
|
|
|
|
# yyy document that the properties are numerous enough to look for |
635
|
|
|
|
|
|
|
# a compact acronym, that the choice of acronym is sort of |
636
|
|
|
|
|
|
|
# arbitrary, so (GRANITE) was chosen since it's easy to remember |
637
|
|
|
|
|
|
|
# |
638
|
|
|
|
|
|
|
# $pre and $msk are in service of the letter "A" below. |
639
|
|
|
|
|
|
|
(my $pre = $prefix) =~ s/[a-z]/e/ig; |
640
|
|
|
|
|
|
|
(my $msk = $mask) =~ s/k/e/g; |
641
|
|
|
|
|
|
|
$msk =~ s/^ze/zeeee/; # initial 'e' can become many later on |
642
|
|
|
|
|
|
|
|
643
|
|
|
|
|
|
|
my $properties = |
644
|
|
|
|
|
|
|
($naan ne "" && $naan ne "00000" ? "G" : "-") |
645
|
|
|
|
|
|
|
. ($gen_type eq "random" ? "R" : "-") |
646
|
|
|
|
|
|
|
# yyy substr is supposed to cut off first char |
647
|
|
|
|
|
|
|
. ($genonly && ($pre . substr($msk, 1)) !~ /eee/ ? "A" : "-") |
648
|
|
|
|
|
|
|
. ($term eq "long" ? "N" : "-") |
649
|
|
|
|
|
|
|
. ($genonly && $prefix !~ /-/ ? "I" : "-") |
650
|
|
|
|
|
|
|
. ($$noid{"$R/addcheckchar"} ? "T" : "-") |
651
|
|
|
|
|
|
|
# yyy "E" mask test anticipates future extensions to alphabets |
652
|
|
|
|
|
|
|
. ($genonly && ($prefix =~ /[aeiouy]/i || $mask =~ /[^rszdek]/) |
653
|
|
|
|
|
|
|
? "-" : "E") # Elided vowels or not |
654
|
|
|
|
|
|
|
; |
655
|
|
|
|
|
|
|
$$noid{"$R/properties"} = $properties; |
656
|
|
|
|
|
|
|
|
657
|
|
|
|
|
|
|
# Now figure out "where" element. |
658
|
|
|
|
|
|
|
# |
659
|
|
|
|
|
|
|
use Sys::Hostname; |
660
|
|
|
|
|
|
|
my $host = hostname(); |
661
|
|
|
|
|
|
|
|
662
|
|
|
|
|
|
|
# my $child_process_id; |
663
|
|
|
|
|
|
|
# unless (defined($child_process_id = open(CHILD, "-|"))) { |
664
|
|
|
|
|
|
|
# die "unable to start child process, $!, stopped"; |
665
|
|
|
|
|
|
|
# } |
666
|
|
|
|
|
|
|
# if ($child_process_id == 0) { |
667
|
|
|
|
|
|
|
# # We are in the child. Set the PATH environment variable. |
668
|
|
|
|
|
|
|
# $ENV{"PATH"} = "/bin:/usr/bin"; |
669
|
|
|
|
|
|
|
# # Run the command we want, with its STDOUT redirected |
670
|
|
|
|
|
|
|
# # to the pipe that goes back to the parent. |
671
|
|
|
|
|
|
|
# exec "/bin/hostname"; |
672
|
|
|
|
|
|
|
# die "unable to execute \"/bin/hostname\", $!, stopped"; |
673
|
|
|
|
|
|
|
# } |
674
|
|
|
|
|
|
|
# else { |
675
|
|
|
|
|
|
|
# # We are in the parent, and the CHILD file handle is |
676
|
|
|
|
|
|
|
# # the read end of the pipe that has its write end as |
677
|
|
|
|
|
|
|
# # STDOUT of the child. |
678
|
|
|
|
|
|
|
# $host = ; |
679
|
|
|
|
|
|
|
# close(CHILD); |
680
|
|
|
|
|
|
|
# chomp $host; |
681
|
|
|
|
|
|
|
# } |
682
|
|
|
|
|
|
|
|
683
|
|
|
|
|
|
|
my $cwd = $dbdir; # by default, assuming $dbdir is absolute path |
684
|
|
|
|
|
|
|
if ($dbdir !~ m|^/|) { |
685
|
|
|
|
|
|
|
$cwd = $ENV{"PWD"} . "/$dbdir"; |
686
|
|
|
|
|
|
|
} |
687
|
|
|
|
|
|
|
|
688
|
|
|
|
|
|
|
# Adjust some empty values for short-term display purposes. |
689
|
|
|
|
|
|
|
# |
690
|
|
|
|
|
|
|
$naa ||= "no Name Assigning Authority"; |
691
|
|
|
|
|
|
|
$subnaa ||= "no sub authority"; |
692
|
|
|
|
|
|
|
$naan ||= "no NAA Number"; |
693
|
|
|
|
|
|
|
|
694
|
|
|
|
|
|
|
# Create a human- and machine-readable report. |
695
|
|
|
|
|
|
|
# |
696
|
|
|
|
|
|
|
my @p = split(//, $properties); # split into letters |
697
|
|
|
|
|
|
|
s/-/_ not/ || s/./_____/ |
698
|
|
|
|
|
|
|
for (@p); |
699
|
|
|
|
|
|
|
my $random_sample; # undefined on purpose |
700
|
|
|
|
|
|
|
$total == NOLIMIT and |
701
|
|
|
|
|
|
|
$random_sample = int(rand(10)); # first sample less than 10 |
702
|
|
|
|
|
|
|
my $sample1 = sample($noid, $random_sample); |
703
|
|
|
|
|
|
|
$total == NOLIMIT and |
704
|
|
|
|
|
|
|
$random_sample = int(rand(100000)); # second sample bigger |
705
|
|
|
|
|
|
|
my $sample2 = sample($noid, $random_sample); |
706
|
|
|
|
|
|
|
|
707
|
|
|
|
|
|
|
my $htotal = ($total == NOLIMIT ? "unlimited" : human_num($total)); |
708
|
|
|
|
|
|
|
my $what = ($total == NOLIMIT ? "unlimited" : $total) |
709
|
|
|
|
|
|
|
. qq@ $gen_type identifiers of form $template |
710
|
|
|
|
|
|
|
A Noid minting and binding database has been created that will bind |
711
|
|
|
|
|
|
|
@ |
712
|
|
|
|
|
|
|
. ($genonly ? "" : "any identifier ") . "and mint " |
713
|
|
|
|
|
|
|
. ($total == NOLIMIT ? qq@an unbounded number of identifiers |
714
|
|
|
|
|
|
|
with the template "$template".@ |
715
|
|
|
|
|
|
|
: $htotal . qq@ identifiers with the template "$template".@) |
716
|
|
|
|
|
|
|
. qq@ |
717
|
|
|
|
|
|
|
Sample identifiers would be "$sample1" and "$sample2". |
718
|
|
|
|
|
|
|
Minting order is $gen_type.@; |
719
|
|
|
|
|
|
|
|
720
|
|
|
|
|
|
|
$$noid{"$R/erc"} = |
721
|
|
|
|
|
|
|
qq@# Creation record for the identifier generator in NOID/noid.bdb. |
722
|
|
|
|
|
|
|
# |
723
|
|
|
|
|
|
|
erc: |
724
|
|
|
|
|
|
|
who: $contact |
725
|
|
|
|
|
|
|
what: $what |
726
|
|
|
|
|
|
|
when: @ . temper() . qq@ |
727
|
|
|
|
|
|
|
where: $host:$cwd |
728
|
|
|
|
|
|
|
Version: Noid $VERSION |
729
|
|
|
|
|
|
|
Size: @ . ($total == NOLIMIT ? "unlimited" : $total) . qq@ |
730
|
|
|
|
|
|
|
Template: @ . (! $template ? "(:none)" : $template . qq@ |
731
|
|
|
|
|
|
|
A suggested parent directory for this template is "$synonym". Note: |
732
|
|
|
|
|
|
|
separate minters need separate directories, and templates can suggest |
733
|
|
|
|
|
|
|
short names; e.g., the template "xz.redek" suggests the parent directory |
734
|
|
|
|
|
|
|
"noid_xz4" since identifiers are "xz" followed by 4 characters.@) . qq@ |
735
|
|
|
|
|
|
|
Policy: (:$properties) |
736
|
|
|
|
|
|
|
This minter's durability summary is (maximum possible being "GRANITE") |
737
|
|
|
|
|
|
|
"$properties", which breaks down, property by property, as follows. |
738
|
|
|
|
|
|
|
^^^^^^^ |
739
|
|
|
|
|
|
|
|||||||_$p[6] (E)lided of vowels to avoid creating words by accident |
740
|
|
|
|
|
|
|
||||||_$p[5] (T)ranscription safe due to a generated check character |
741
|
|
|
|
|
|
|
|||||_$p[4] (I)mpression safe from ignorable typesetter-added hyphens |
742
|
|
|
|
|
|
|
||||_$p[3] (N)on-reassignable in life of Name Assigning Authority |
743
|
|
|
|
|
|
|
|||_$p[2] (A)lphabetic-run-limited to pairs to avoid acronyms |
744
|
|
|
|
|
|
|
||_$p[1] (R)andomly sequenced to avoid series semantics |
745
|
|
|
|
|
|
|
|_$p[0] (G)lobally unique within a registered namespace (currently |
746
|
|
|
|
|
|
|
tests only ARK namespaces; apply for one at ark@ |
747
|
|
|
|
|
|
|
. '@' . qq@cdlib.org) |
748
|
|
|
|
|
|
|
Authority: $naa | $subnaa |
749
|
|
|
|
|
|
|
NAAN: $naan |
750
|
|
|
|
|
|
|
@; |
751
|
|
|
|
|
|
|
! storefile("$dir/README", $$noid{"$R/erc"}) |
752
|
|
|
|
|
|
|
and return(undef); |
753
|
|
|
|
|
|
|
# yyy useful for quick info on a minter from just doing 'ls NOID'?? |
754
|
|
|
|
|
|
|
# storefile("$dir/T=$prefix.$mask", "foo\n"); |
755
|
|
|
|
|
|
|
|
756
|
|
|
|
|
|
|
my $report = qq@Created: minter for $what @ |
757
|
|
|
|
|
|
|
. qq@See $dir/README for details.\n@; |
758
|
|
|
|
|
|
|
|
759
|
|
|
|
|
|
|
! $template and |
760
|
|
|
|
|
|
|
dbclose($noid), |
761
|
|
|
|
|
|
|
return($report); |
762
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
init_counters($noid); |
764
|
|
|
|
|
|
|
dbclose($noid); |
765
|
|
|
|
|
|
|
return($report); |
766
|
|
|
|
|
|
|
} |
767
|
|
|
|
|
|
|
|
768
|
|
|
|
|
|
|
# Report values according to $level. Values of $level: |
769
|
|
|
|
|
|
|
# "brief" (default) user vals and interesting admin vals |
770
|
|
|
|
|
|
|
# "full" user vals and all admin vals |
771
|
|
|
|
|
|
|
# "dump" all vals, including all identifier bindings |
772
|
|
|
|
|
|
|
# |
773
|
|
|
|
|
|
|
sub dbinfo { my( $noid, $level )=@_; |
774
|
|
|
|
|
|
|
my $db = $opendbtab{"bdb/$noid"}; |
775
|
|
|
|
|
|
|
my $cursor = $db->db_cursor(); |
776
|
|
|
|
|
|
|
my ($key, $value) = ("$R/", 0); |
777
|
|
|
|
|
|
|
if ($level eq "dump") { |
778
|
|
|
|
|
|
|
print "$key: $value\n" |
779
|
|
|
|
|
|
|
while ($cursor->c_get($key, $value, DB_NEXT) == 0); |
780
|
|
|
|
|
|
|
return 1; |
781
|
|
|
|
|
|
|
} |
782
|
|
|
|
|
|
|
my $status = $cursor->c_get($key, $value, DB_SET_RANGE); |
783
|
|
|
|
|
|
|
if ($status) { |
784
|
|
|
|
|
|
|
addmsg($noid, "c_get status/errno ($status/$!)"); |
785
|
|
|
|
|
|
|
return 0; |
786
|
|
|
|
|
|
|
} |
787
|
|
|
|
|
|
|
if ($key =~ m|^$R/$R/|) { |
788
|
|
|
|
|
|
|
print "User Assigned Values\n"; |
789
|
|
|
|
|
|
|
print " $key: $value\n"; |
790
|
|
|
|
|
|
|
while ($cursor->c_get($key, $value, DB_NEXT) == 0) { |
791
|
|
|
|
|
|
|
last |
792
|
|
|
|
|
|
|
if ($key !~ m|^$R/$R/|); |
793
|
|
|
|
|
|
|
print " $key: $value\n"; |
794
|
|
|
|
|
|
|
} |
795
|
|
|
|
|
|
|
print "\n"; |
796
|
|
|
|
|
|
|
} |
797
|
|
|
|
|
|
|
print "Admin Values\n"; |
798
|
|
|
|
|
|
|
print " $key: $value\n"; |
799
|
|
|
|
|
|
|
while ($cursor->c_get($key, $value, DB_NEXT) == 0) { |
800
|
|
|
|
|
|
|
last |
801
|
|
|
|
|
|
|
if ($key !~ m|^$R/|); |
802
|
|
|
|
|
|
|
print " $key: $value\n" |
803
|
|
|
|
|
|
|
if ($level eq "full" or |
804
|
|
|
|
|
|
|
$key !~ m|^$R/c\d| && |
805
|
|
|
|
|
|
|
$key !~ m|^$R/saclist| && |
806
|
|
|
|
|
|
|
$key !~ m|^$R/recycle/|); |
807
|
|
|
|
|
|
|
} |
808
|
|
|
|
|
|
|
print "\n"; |
809
|
|
|
|
|
|
|
undef $cursor; |
810
|
|
|
|
|
|
|
return 1; |
811
|
|
|
|
|
|
|
} |
812
|
|
|
|
|
|
|
|
813
|
|
|
|
|
|
|
# yyy eventually we would like to do fancy fine-grained locking with |
814
|
|
|
|
|
|
|
# BerkeleyDB features. For now, lock before tie(), unlock after untie(). |
815
|
|
|
|
|
|
|
sub dblock{ return 1; # placeholder |
816
|
|
|
|
|
|
|
} |
817
|
|
|
|
|
|
|
sub dbunlock{ return 1; # placeholder |
818
|
|
|
|
|
|
|
} |
819
|
|
|
|
|
|
|
|
820
|
|
|
|
|
|
|
# returns noid: a listref |
821
|
|
|
|
|
|
|
# $flags can be DB_RDONLY, DB_CREATE, or 0 (for read/write, the default) |
822
|
|
|
|
|
|
|
# |
823
|
|
|
|
|
|
|
sub dbopen { my( $dbname, $flags )=@_; |
824
|
|
|
|
|
|
|
|
825
|
|
|
|
|
|
|
# yyy to test: can we now open more than one noid at once? |
826
|
|
|
|
|
|
|
|
827
|
|
|
|
|
|
|
my ($env, $envhome); |
828
|
|
|
|
|
|
|
($envhome = $dbname) =~ s|[^/]+$||; # path ending in "NOID/" |
829
|
|
|
|
|
|
|
! -d $envhome and |
830
|
|
|
|
|
|
|
addmsg(undef, "$envhome not a directory"), |
831
|
|
|
|
|
|
|
return undef; |
832
|
|
|
|
|
|
|
# yyy probably these envflags are overkill right now |
833
|
|
|
|
|
|
|
my $envflags = DB_INIT_LOCK | DB_INIT_TXN | DB_INIT_MPOOL; |
834
|
|
|
|
|
|
|
#my $envflags = DB_INIT_CDB | DB_INIT_MPOOL; |
835
|
|
|
|
|
|
|
($flags & DB_CREATE) and |
836
|
|
|
|
|
|
|
$envflags |= DB_CREATE; |
837
|
|
|
|
|
|
|
my @envargs = ( |
838
|
|
|
|
|
|
|
-Home => $envhome, |
839
|
|
|
|
|
|
|
-Flags => $envflags, |
840
|
|
|
|
|
|
|
-Verbose => 1 |
841
|
|
|
|
|
|
|
); |
842
|
|
|
|
|
|
|
|
843
|
|
|
|
|
|
|
# If it exists and is writable, use log file to inscribe BDB errors. |
844
|
|
|
|
|
|
|
# |
845
|
|
|
|
|
|
|
my ($logfile, $logfhandle, $log_opened, $logbdb); |
846
|
|
|
|
|
|
|
|
847
|
|
|
|
|
|
|
$logfile = $envhome . "log"; |
848
|
|
|
|
|
|
|
$log_opened = open($logfhandle, ">>$logfile"); |
849
|
|
|
|
|
|
|
$logbdb = $envhome . "logbdb"; |
850
|
|
|
|
|
|
|
-w $logbdb and |
851
|
|
|
|
|
|
|
push(@envargs, ( -ErrFile => $logbdb )); |
852
|
|
|
|
|
|
|
# yyy should we complain if can't open log file? |
853
|
|
|
|
|
|
|
|
854
|
|
|
|
|
|
|
$env = new BerkeleyDB::Env @envargs; |
855
|
|
|
|
|
|
|
! defined($env) and |
856
|
|
|
|
|
|
|
addmsg(undef, "no \"Env\" object ($BerkeleyDB::Error)"), |
857
|
|
|
|
|
|
|
return undef; |
858
|
|
|
|
|
|
|
|
859
|
|
|
|
|
|
|
#=for deleting |
860
|
|
|
|
|
|
|
# |
861
|
|
|
|
|
|
|
# print "OK so far\n"; exit(0); |
862
|
|
|
|
|
|
|
# if ($flags && DB_CREATE) { |
863
|
|
|
|
|
|
|
# # initialize environment files |
864
|
|
|
|
|
|
|
# print "envhome=$envhome\n"; |
865
|
|
|
|
|
|
|
# $env = new BerkeleyDB::Env @envargs; |
866
|
|
|
|
|
|
|
# ! defined($env) and |
867
|
|
|
|
|
|
|
# addmsg(undef, |
868
|
|
|
|
|
|
|
# "no \"Env\" object ($BerkeleyDB::Error)"), |
869
|
|
|
|
|
|
|
# return undef; |
870
|
|
|
|
|
|
|
# } |
871
|
|
|
|
|
|
|
# else { |
872
|
|
|
|
|
|
|
# print "flags=$flags\n"; |
873
|
|
|
|
|
|
|
# } |
874
|
|
|
|
|
|
|
# print "OK so far\n"; exit(0); |
875
|
|
|
|
|
|
|
# $env = new BerkeleyDB::Env @envargs; |
876
|
|
|
|
|
|
|
# unless (defined($env)) { |
877
|
|
|
|
|
|
|
# die "unable to get a \"BerkeleyDB::Env\" object ($BerkeleyDB::Error), stopped"; |
878
|
|
|
|
|
|
|
# } |
879
|
|
|
|
|
|
|
# |
880
|
|
|
|
|
|
|
#=cut |
881
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
my $noid = {}; # eventual minter database handle |
883
|
|
|
|
|
|
|
|
884
|
|
|
|
|
|
|
# For now we use simple database-level file locking with a timeout. |
885
|
|
|
|
|
|
|
# Unlocking is implicit when the NOIDLOCK file handle is closed |
886
|
|
|
|
|
|
|
# either explicitly or upon process termination. |
887
|
|
|
|
|
|
|
# |
888
|
|
|
|
|
|
|
my $lockfile = $envhome . "lock"; |
889
|
|
|
|
|
|
|
my $timeout = 5; # max number of seconds to wait for lock |
890
|
|
|
|
|
|
|
my $locktype = (($flags & DB_RDONLY) ? LOCK_SH : LOCK_EX); |
891
|
|
|
|
|
|
|
|
892
|
|
|
|
|
|
|
! sysopen(NOIDLOCK, $lockfile, O_RDWR | O_CREAT) and |
893
|
|
|
|
|
|
|
addmsg(undef, "cannot open \"$lockfile\": $!"), |
894
|
|
|
|
|
|
|
return undef; |
895
|
|
|
|
|
|
|
eval { |
896
|
|
|
|
|
|
|
|
897
|
|
|
|
|
|
|
local $SIG{ALRM} = sub { die("lock timeout after $timeout " |
898
|
|
|
|
|
|
|
. "seconds; consider removing \"$lockfile\"\n") |
899
|
|
|
|
|
|
|
}; |
900
|
|
|
|
|
|
|
alarm $timeout; # alarm goes off in $timeout seconds |
901
|
|
|
|
|
|
|
eval { # yyy if system has no flock, say in dbcreate profile? |
902
|
|
|
|
|
|
|
flock(NOIDLOCK, $locktype) # blocking lock |
903
|
|
|
|
|
|
|
or die("cannot flock: $!"); |
904
|
|
|
|
|
|
|
}; |
905
|
|
|
|
|
|
|
alarm 0; # cancel the alarm |
906
|
|
|
|
|
|
|
die $@ if $@; # re-raise the exception |
907
|
|
|
|
|
|
|
}; |
908
|
|
|
|
|
|
|
alarm 0; # race condition protection |
909
|
|
|
|
|
|
|
if ($@) { # re-raise the exception |
910
|
|
|
|
|
|
|
addmsg(undef, "error: $@"); |
911
|
|
|
|
|
|
|
return undef; |
912
|
|
|
|
|
|
|
} |
913
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
my $db = tie(%$noid, "BerkeleyDB::Btree", |
915
|
|
|
|
|
|
|
-Filename => "noid.bdb", # env has path to it |
916
|
|
|
|
|
|
|
-Flags => $flags, |
917
|
|
|
|
|
|
|
## yyy -Property => DB_DUP, |
918
|
|
|
|
|
|
|
-Env => $env) |
919
|
|
|
|
|
|
|
or addmsg(undef, "tie failed on $dbname: $BerkeleyDB::Error") |
920
|
|
|
|
|
|
|
and return undef; |
921
|
|
|
|
|
|
|
# yyy how to set error code or return string? |
922
|
|
|
|
|
|
|
# or die("Can't open database file: $!\n"); |
923
|
|
|
|
|
|
|
#print "dbopen: returning hashref=$noid, db=$db\n"; |
924
|
|
|
|
|
|
|
$opendbtab{"bdb/$noid"} = $db; |
925
|
|
|
|
|
|
|
$opendbtab{"msg/$noid"} = ""; |
926
|
|
|
|
|
|
|
$opendbtab{"log/$noid"} = ($log_opened ? $logfhandle : undef); |
927
|
|
|
|
|
|
|
|
928
|
|
|
|
|
|
|
$locktest and |
929
|
|
|
|
|
|
|
print("locktest: holding lock for $locktest seconds...\n"), |
930
|
|
|
|
|
|
|
sleep($locktest); |
931
|
|
|
|
|
|
|
|
932
|
|
|
|
|
|
|
return $noid; |
933
|
|
|
|
|
|
|
} |
934
|
|
|
|
|
|
|
|
935
|
|
|
|
|
|
|
# Call with number of seconds to sleep at end of each open. |
936
|
|
|
|
|
|
|
# This exists only for the purpose of testing the locking mechanism. |
937
|
|
|
|
|
|
|
# |
938
|
|
|
|
|
|
|
sub locktest { my( $sleepvalue )=@_; |
939
|
|
|
|
|
|
|
$locktest = $sleepvalue; # set global variable for locktest |
940
|
|
|
|
|
|
|
return 1; |
941
|
|
|
|
|
|
|
} |
942
|
|
|
|
|
|
|
|
943
|
|
|
|
|
|
|
sub dbclose { my( $noid )=@_; |
944
|
|
|
|
|
|
|
undef $opendbtab{"msg/$noid"}; |
945
|
|
|
|
|
|
|
defined($opendbtab{"log/$noid"}) and |
946
|
|
|
|
|
|
|
close($opendbtab{"log/$noid"}); |
947
|
|
|
|
|
|
|
undef $opendbtab{"bdb/$noid"}; |
948
|
|
|
|
|
|
|
untie %$noid; |
949
|
|
|
|
|
|
|
close NOIDLOCK; # let go of lock |
950
|
|
|
|
|
|
|
} |
951
|
|
|
|
|
|
|
|
952
|
|
|
|
|
|
|
# yyy is this needed? in present form? |
953
|
|
|
|
|
|
|
# |
954
|
|
|
|
|
|
|
# get next value and, if no error, change the 2nd and 3rd parameters and |
955
|
|
|
|
|
|
|
# return 1, else return 0. To start at the beginning, the 2nd parameter, |
956
|
|
|
|
|
|
|
# key (key), should be set to zero by caller, who might do this: |
957
|
|
|
|
|
|
|
# $key = 0; while (each($noid, $key, $value)) { ... } |
958
|
|
|
|
|
|
|
# The 3rd parameter will contain the corresponding value. |
959
|
|
|
|
|
|
|
|
960
|
|
|
|
|
|
|
sub eachnoid { my( $noid, $key, $value )=@_; |
961
|
|
|
|
|
|
|
# yyy check that $db is tied? this is assumed for now |
962
|
|
|
|
|
|
|
# yyy need to get next non-admin key/value pair |
963
|
|
|
|
|
|
|
my $db = $opendbtab{"bdb/$noid"}; |
964
|
|
|
|
|
|
|
#was: my $flag = ($key ? R_NEXT : R_FIRST); |
965
|
|
|
|
|
|
|
# fix from Jim Fullton: |
966
|
|
|
|
|
|
|
my $flag = ($key ? DB_NEXT : DB_FIRST); |
967
|
|
|
|
|
|
|
my $cursor = $db->db_cursor(); |
968
|
|
|
|
|
|
|
if ($cursor->c_get($key, $value, $flag)) { |
969
|
|
|
|
|
|
|
return 0; |
970
|
|
|
|
|
|
|
} |
971
|
|
|
|
|
|
|
$_[1] = $key; |
972
|
|
|
|
|
|
|
$_[2] = $value; |
973
|
|
|
|
|
|
|
return 1; |
974
|
|
|
|
|
|
|
} |
975
|
|
|
|
|
|
|
|
976
|
|
|
|
|
|
|
# A no-op function to call instead of checkchar(). |
977
|
|
|
|
|
|
|
# |
978
|
|
|
|
|
|
|
sub echo { |
979
|
|
|
|
|
|
|
return $_[0]; |
980
|
|
|
|
|
|
|
} |
981
|
|
|
|
|
|
|
|
982
|
|
|
|
|
|
|
# $verbose is 1 if we want labels, 0 if we don't |
983
|
|
|
|
|
|
|
# yyy do we need to be able to "get/fetch" with a discriminant, |
984
|
|
|
|
|
|
|
# eg, for smart multiple resolution?? |
985
|
|
|
|
|
|
|
sub fetch { my( $noid, $verbose, $id, @elems )=@_; |
986
|
|
|
|
|
|
|
|
987
|
|
|
|
|
|
|
! defined($id) and |
988
|
|
|
|
|
|
|
addmsg($noid, "error: " . ($verbose ? "fetch" : "get") |
989
|
|
|
|
|
|
|
. " requires that an identifier be specified."), |
990
|
|
|
|
|
|
|
return(undef); |
991
|
|
|
|
|
|
|
|
992
|
|
|
|
|
|
|
my ($hdr, $retval) = ("", ""); |
993
|
|
|
|
|
|
|
$verbose and $hdr = "id: $id" |
994
|
|
|
|
|
|
|
. (exists($$noid{"$id\t$R/h"}) ? " hold" : "") . "\n" |
995
|
|
|
|
|
|
|
. (validate($noid, "-", $id) ? "" : errmsg($noid) . "\n") |
996
|
|
|
|
|
|
|
. "Circ: " . ($$noid{"$id\t$R/c"} |
997
|
|
|
|
|
|
|
? $$noid{"$id\t$R/c"} : "uncirculated") . "\n"; |
998
|
|
|
|
|
|
|
|
999
|
|
|
|
|
|
|
my $db = $opendbtab{"bdb/$noid"}; |
1000
|
|
|
|
|
|
|
my $cursor = $db->db_cursor(); |
1001
|
|
|
|
|
|
|
|
1002
|
|
|
|
|
|
|
if ($#elems < 0) { # No elements were specified, so find them. |
1003
|
|
|
|
|
|
|
my ($first, $skip, $done) = ("$id\t", 0, 0); |
1004
|
|
|
|
|
|
|
my ($key, $value) = ($first, 0); |
1005
|
|
|
|
|
|
|
my $status = $cursor->c_get($key, $value, DB_SET_RANGE); |
1006
|
|
|
|
|
|
|
$status == 0 and |
1007
|
|
|
|
|
|
|
$skip = ($key =~ m|^$first$R/|), |
1008
|
|
|
|
|
|
|
$done = ($key !~ m|^$first|), |
1009
|
|
|
|
|
|
|
1 or |
1010
|
|
|
|
|
|
|
$done = 1 |
1011
|
|
|
|
|
|
|
; |
1012
|
|
|
|
|
|
|
while (! $done) { |
1013
|
|
|
|
|
|
|
! $skip and |
1014
|
|
|
|
|
|
|
# if $verbose (ie, fetch), include label and |
1015
|
|
|
|
|
|
|
# remember to strip "Id\t" from front of $key |
1016
|
|
|
|
|
|
|
$retval .= ($verbose ? |
1017
|
|
|
|
|
|
|
($key =~ /^[^\t]*\t(.*)/ ? $1 : $key) |
1018
|
|
|
|
|
|
|
. ": " : "") . "$value\n"; |
1019
|
|
|
|
|
|
|
$status = $cursor->c_get($key, $value, DB_NEXT); |
1020
|
|
|
|
|
|
|
$status != 0 || $key !~ /^$first/ and |
1021
|
|
|
|
|
|
|
$done = 1 # no more elements under id |
1022
|
|
|
|
|
|
|
or |
1023
|
|
|
|
|
|
|
$skip = ($key =~ m|^$first$R/|) |
1024
|
|
|
|
|
|
|
; |
1025
|
|
|
|
|
|
|
} |
1026
|
|
|
|
|
|
|
undef($cursor); |
1027
|
|
|
|
|
|
|
! $retval and |
1028
|
|
|
|
|
|
|
addmsg($noid, $hdr |
1029
|
|
|
|
|
|
|
. "note: no elements bound under $id."), |
1030
|
|
|
|
|
|
|
return(undef); |
1031
|
|
|
|
|
|
|
return($hdr . $retval); |
1032
|
|
|
|
|
|
|
} |
1033
|
|
|
|
|
|
|
# yyy should this work for elem names with regexprs in them? |
1034
|
|
|
|
|
|
|
# XXX idmap won't bind with longterm ??? |
1035
|
|
|
|
|
|
|
my $idmapped; |
1036
|
|
|
|
|
|
|
for my $elem (@elems) { |
1037
|
|
|
|
|
|
|
$$noid{"$id\t$elem"} and |
1038
|
|
|
|
|
|
|
($verbose and |
1039
|
|
|
|
|
|
|
$retval .= "$elem: "), |
1040
|
|
|
|
|
|
|
$retval .= $$noid{"$id\t$elem"} . "\n" |
1041
|
|
|
|
|
|
|
or |
1042
|
|
|
|
|
|
|
$idmapped = id2elemval($cursor, $verbose, $id, $elem), |
1043
|
|
|
|
|
|
|
($verbose and |
1044
|
|
|
|
|
|
|
$retval .= ($idmapped ? "$idmapped\nnote: " |
1045
|
|
|
|
|
|
|
. "previous result produced by :idmap\n" |
1046
|
|
|
|
|
|
|
: qq@error: "$id $elem" is not bound.\n@) |
1047
|
|
|
|
|
|
|
or |
1048
|
|
|
|
|
|
|
$retval .= "$idmapped\n" |
1049
|
|
|
|
|
|
|
) |
1050
|
|
|
|
|
|
|
; |
1051
|
|
|
|
|
|
|
} |
1052
|
|
|
|
|
|
|
undef($cursor); |
1053
|
|
|
|
|
|
|
return($hdr . $retval); |
1054
|
|
|
|
|
|
|
} |
1055
|
|
|
|
|
|
|
|
1056
|
|
|
|
|
|
|
# Generate the actual next id to give out. May be randomly or sequentially |
1057
|
|
|
|
|
|
|
# selected. This routine should not be called if there are ripe recyclable |
1058
|
|
|
|
|
|
|
# identifiers to use. |
1059
|
|
|
|
|
|
|
# |
1060
|
|
|
|
|
|
|
# This routine and n2xdig comprise the real heart of the minter software. |
1061
|
|
|
|
|
|
|
# |
1062
|
|
|
|
|
|
|
sub genid { my( $noid )=@_; |
1063
|
|
|
|
|
|
|
dblock(); |
1064
|
|
|
|
|
|
|
|
1065
|
|
|
|
|
|
|
# Variables: |
1066
|
|
|
|
|
|
|
# oacounter overall counter's current value (last value minted) |
1067
|
|
|
|
|
|
|
# oatop overall counter's greatest possible value of counter |
1068
|
|
|
|
|
|
|
# saclist (sub) active counters list |
1069
|
|
|
|
|
|
|
# siclist (sub) inactive counters list |
1070
|
|
|
|
|
|
|
# c$n/value subcounter name's ($scn) value |
1071
|
|
|
|
|
|
|
|
1072
|
|
|
|
|
|
|
my $oacounter = $$noid{"$R/oacounter"}; |
1073
|
|
|
|
|
|
|
|
1074
|
|
|
|
|
|
|
# yyy what are we going to do with counters for held? queued? |
1075
|
|
|
|
|
|
|
|
1076
|
|
|
|
|
|
|
if ($$noid{"$R/oatop"} != NOLIMIT && $oacounter >= $$noid{"$R/oatop"}) { |
1077
|
|
|
|
|
|
|
|
1078
|
|
|
|
|
|
|
# Critical test of whether we're willing to re-use identifiers |
1079
|
|
|
|
|
|
|
# by re-setting (wrapping) the counter to zero. To be extra |
1080
|
|
|
|
|
|
|
# careful we check both the longterm and wrap settings, even |
1081
|
|
|
|
|
|
|
# though, in theory, wrap won't be set if longterm is set. |
1082
|
|
|
|
|
|
|
# |
1083
|
|
|
|
|
|
|
if ($$noid{"$R/longterm"} || ! $$noid{"$R/wrap"}) { |
1084
|
|
|
|
|
|
|
dbunlock(); |
1085
|
|
|
|
|
|
|
my $m = "error: identifiers exhausted (stopped at " |
1086
|
|
|
|
|
|
|
. $$noid{"$R/oatop"} . ")."; |
1087
|
|
|
|
|
|
|
addmsg($noid, $m); |
1088
|
|
|
|
|
|
|
logmsg($noid, $m); |
1089
|
|
|
|
|
|
|
return undef; |
1090
|
|
|
|
|
|
|
} |
1091
|
|
|
|
|
|
|
# If we get here, term is not "long". |
1092
|
|
|
|
|
|
|
logmsg($noid, temper() . ": Resetting counter to zero; " |
1093
|
|
|
|
|
|
|
. "previously issued identifiers will be re-issued"); |
1094
|
|
|
|
|
|
|
if ($$noid{"$R/generator_type"} eq "sequential") { |
1095
|
|
|
|
|
|
|
$$noid{"$R/oacounter"} = 0; |
1096
|
|
|
|
|
|
|
} |
1097
|
|
|
|
|
|
|
else { |
1098
|
|
|
|
|
|
|
init_counters($noid); # yyy calls dblock -- problem? |
1099
|
|
|
|
|
|
|
} |
1100
|
|
|
|
|
|
|
$oacounter = 0; |
1101
|
|
|
|
|
|
|
} |
1102
|
|
|
|
|
|
|
# If we get here, the counter may actually have just been reset. |
1103
|
|
|
|
|
|
|
|
1104
|
|
|
|
|
|
|
# Deal with the easy sequential generator case and exit early. |
1105
|
|
|
|
|
|
|
# |
1106
|
|
|
|
|
|
|
if ($$noid{"$R/generator_type"} eq "sequential") { |
1107
|
|
|
|
|
|
|
my $id = &n2xdig($$noid{"$R/oacounter"}, $$noid{"$R/mask"}); |
1108
|
|
|
|
|
|
|
$$noid{"$R/oacounter"}++; # incr to reflect new total |
1109
|
|
|
|
|
|
|
dbunlock(); |
1110
|
|
|
|
|
|
|
return $id; |
1111
|
|
|
|
|
|
|
} |
1112
|
|
|
|
|
|
|
|
1113
|
|
|
|
|
|
|
# If we get here, the generator must be of type "random". |
1114
|
|
|
|
|
|
|
# |
1115
|
|
|
|
|
|
|
my $len = (my @saclist = split(/ /, $$noid{"$R/saclist"})); |
1116
|
|
|
|
|
|
|
if ($len < 1) { |
1117
|
|
|
|
|
|
|
dbunlock(); |
1118
|
|
|
|
|
|
|
addmsg($noid, "error: no active counters panic, " |
1119
|
|
|
|
|
|
|
. "but $oacounter identifiers left?"); |
1120
|
|
|
|
|
|
|
return undef; |
1121
|
|
|
|
|
|
|
} |
1122
|
|
|
|
|
|
|
my $randn = int(rand($len)); # pick a specific counter name |
1123
|
|
|
|
|
|
|
my $sctrn = $saclist[$randn]; # at random; then pull its $n |
1124
|
|
|
|
|
|
|
my $n = substr($sctrn, 1); # numeric equivalent from the name |
1125
|
|
|
|
|
|
|
#print "randn=$randn, sctrn=$sctrn, counter n=$n\t"; |
1126
|
|
|
|
|
|
|
my $sctr = $$noid{"$R/${sctrn}/value"}; # and get its value |
1127
|
|
|
|
|
|
|
$sctr++; # increment and |
1128
|
|
|
|
|
|
|
$$noid{"$R/${sctrn}/value"} = $sctr; # store new current value |
1129
|
|
|
|
|
|
|
$$noid{"$R/oacounter"}++; # incr overall counter - some |
1130
|
|
|
|
|
|
|
# redundancy for sanity's sake |
1131
|
|
|
|
|
|
|
|
1132
|
|
|
|
|
|
|
# deal with an exhausted subcounter |
1133
|
|
|
|
|
|
|
if ($sctr >= $$noid{"$R/${sctrn}/top"}) { |
1134
|
|
|
|
|
|
|
my ($c, $modsaclist) = ("", ""); |
1135
|
|
|
|
|
|
|
# remove from active counters list |
1136
|
|
|
|
|
|
|
foreach $c (@saclist) { # drop $sctrn, but add it to |
1137
|
|
|
|
|
|
|
next if ($c eq $sctrn); # inactive subcounters |
1138
|
|
|
|
|
|
|
$modsaclist .= "$c "; |
1139
|
|
|
|
|
|
|
} |
1140
|
|
|
|
|
|
|
$$noid{"$R/saclist"} = $modsaclist; # update saclist |
1141
|
|
|
|
|
|
|
$$noid{"$R/siclist"} .= " $sctrn"; # and siclist |
1142
|
|
|
|
|
|
|
#print "===> Exhausted counter $sctrn\n"; |
1143
|
|
|
|
|
|
|
} |
1144
|
|
|
|
|
|
|
|
1145
|
|
|
|
|
|
|
# $sctr holds counter value, $n holds ordinal of the counter itself |
1146
|
|
|
|
|
|
|
my $id = &n2xdig( |
1147
|
|
|
|
|
|
|
$sctr + ($n * $$noid{"$R/percounter"}), |
1148
|
|
|
|
|
|
|
$$noid{"$R/mask"}); |
1149
|
|
|
|
|
|
|
dbunlock(); |
1150
|
|
|
|
|
|
|
return $id; |
1151
|
|
|
|
|
|
|
} |
1152
|
|
|
|
|
|
|
|
1153
|
|
|
|
|
|
|
# Identifier admin info is stored in three places: |
1154
|
|
|
|
|
|
|
# |
1155
|
|
|
|
|
|
|
# id\t:/h hold status: if exists = hold, else no hold |
1156
|
|
|
|
|
|
|
# id\t:/c circulation record, if it exists, is |
1157
|
|
|
|
|
|
|
# circ_status_history_vector|when|contact(who)|oacounter |
1158
|
|
|
|
|
|
|
# where circ_status_history_vector is a string of [iqu] |
1159
|
|
|
|
|
|
|
# and oacounter is current overall counter value, FWIW; |
1160
|
|
|
|
|
|
|
# circ status goes first to make record easy to update |
1161
|
|
|
|
|
|
|
# id\t:/p pepper |
1162
|
|
|
|
|
|
|
# |
1163
|
|
|
|
|
|
|
# Returns a single letter circulation status, which must be one |
1164
|
|
|
|
|
|
|
# of 'i', 'q', or 'u'. Returns the empty string on error. |
1165
|
|
|
|
|
|
|
# |
1166
|
|
|
|
|
|
|
sub get_circ_svec { my( $noid, $id )=@_; |
1167
|
|
|
|
|
|
|
|
1168
|
|
|
|
|
|
|
my $circ_rec = $$noid{"$id\t$R/c"}; |
1169
|
|
|
|
|
|
|
! defined($circ_rec) and |
1170
|
|
|
|
|
|
|
return ''; |
1171
|
|
|
|
|
|
|
|
1172
|
|
|
|
|
|
|
# Circulation status vector (string of letter codes) is the 1st |
1173
|
|
|
|
|
|
|
# element, elements being separated by '|'. We don't care about |
1174
|
|
|
|
|
|
|
# the other elements for now because we can find everything we |
1175
|
|
|
|
|
|
|
# need at the beginning of the string (without splitting it). |
1176
|
|
|
|
|
|
|
# Let errors hit the log file rather than bothering the caller. |
1177
|
|
|
|
|
|
|
# |
1178
|
|
|
|
|
|
|
my $circ_svec = (split(/\|/, $circ_rec))[0]; |
1179
|
|
|
|
|
|
|
|
1180
|
|
|
|
|
|
|
! defined($circ_svec) || $circ_svec eq "" and |
1181
|
|
|
|
|
|
|
logmsg($noid, "error: id $id has no circ status vector -- " |
1182
|
|
|
|
|
|
|
. "circ record is $circ_rec"), |
1183
|
|
|
|
|
|
|
return ''; |
1184
|
|
|
|
|
|
|
$circ_svec !~ /^([iqu])[iqu]*$/ and |
1185
|
|
|
|
|
|
|
logmsg($noid, "error: id $id has a circ status vector " |
1186
|
|
|
|
|
|
|
. "containing letters other than 'i', " |
1187
|
|
|
|
|
|
|
. "'q', or 'u' -- circ record is $circ_rec"), |
1188
|
|
|
|
|
|
|
return ''; |
1189
|
|
|
|
|
|
|
return $1; |
1190
|
|
|
|
|
|
|
} |
1191
|
|
|
|
|
|
|
|
1192
|
|
|
|
|
|
|
# As a last step of issuing or queuing an identifier, adjust the circulation |
1193
|
|
|
|
|
|
|
# status record. We place a "hold" if we're both issuing an identifier and |
1194
|
|
|
|
|
|
|
# the minter is for "long" term ids. If we're issuing, we also purge any |
1195
|
|
|
|
|
|
|
# element bindings that exist; this means that a queued identifier's bindings |
1196
|
|
|
|
|
|
|
# will by default last until it is re-minted. |
1197
|
|
|
|
|
|
|
# |
1198
|
|
|
|
|
|
|
# The caller must know what they're doing because we don't check parameters |
1199
|
|
|
|
|
|
|
# for errors; this routine is not externally visible anyway. Returns the |
1200
|
|
|
|
|
|
|
# input identifier on success, or undef on error. |
1201
|
|
|
|
|
|
|
# |
1202
|
|
|
|
|
|
|
sub set_circ_rec { my( $noid, $id, $circ_svec, $date, $contact )=@_; |
1203
|
|
|
|
|
|
|
|
1204
|
|
|
|
|
|
|
my $status = 1; |
1205
|
|
|
|
|
|
|
my $circ_rec = "$circ_svec|$date|$contact|" . $$noid{"$R/oacounter"}; |
1206
|
|
|
|
|
|
|
|
1207
|
|
|
|
|
|
|
# yyy do we care what the previous circ record was? since right now |
1208
|
|
|
|
|
|
|
# we just clobber without looking at it |
1209
|
|
|
|
|
|
|
|
1210
|
|
|
|
|
|
|
dblock(); |
1211
|
|
|
|
|
|
|
|
1212
|
|
|
|
|
|
|
# Check for and clear any bindings if we're issuing an identifier. |
1213
|
|
|
|
|
|
|
# We ignore the return value from clear_bindings(). |
1214
|
|
|
|
|
|
|
# Replace or clear admin bindings by hand, including pepper if any. |
1215
|
|
|
|
|
|
|
# yyy pepper not implemented yet |
1216
|
|
|
|
|
|
|
# If issuing a longterm id, we automatically place a hold on it. |
1217
|
|
|
|
|
|
|
# |
1218
|
|
|
|
|
|
|
$circ_svec =~ /^i/ and |
1219
|
|
|
|
|
|
|
clear_bindings($noid, $id, 0), |
1220
|
|
|
|
|
|
|
delete($$noid{"$id\t$R/p"}), |
1221
|
|
|
|
|
|
|
($$noid{"$R/longterm"} and |
1222
|
|
|
|
|
|
|
$status = hold_set($noid, $id)), |
1223
|
|
|
|
|
|
|
; |
1224
|
|
|
|
|
|
|
$$noid{"$id\t$R/c"} = $circ_rec; |
1225
|
|
|
|
|
|
|
|
1226
|
|
|
|
|
|
|
dbunlock(); |
1227
|
|
|
|
|
|
|
|
1228
|
|
|
|
|
|
|
# This next logmsg should account for the bulk of the log when |
1229
|
|
|
|
|
|
|
# longterm identifiers are in effect. |
1230
|
|
|
|
|
|
|
# |
1231
|
|
|
|
|
|
|
$$noid{"$R/longterm"} and |
1232
|
|
|
|
|
|
|
logmsg($noid, "m: $circ_rec" |
1233
|
|
|
|
|
|
|
. ($status ? "" : " -- hold failed")); |
1234
|
|
|
|
|
|
|
|
1235
|
|
|
|
|
|
|
! $status and # must be an error in hold_set() |
1236
|
|
|
|
|
|
|
return(undef); |
1237
|
|
|
|
|
|
|
return $id; |
1238
|
|
|
|
|
|
|
} |
1239
|
|
|
|
|
|
|
|
1240
|
|
|
|
|
|
|
# Get the value of any named internal variable (prefaced by $R) |
1241
|
|
|
|
|
|
|
# given an open database reference. |
1242
|
|
|
|
|
|
|
# |
1243
|
|
|
|
|
|
|
sub getnoid { my( $noid, $varname )=@_; |
1244
|
|
|
|
|
|
|
return $$noid{"$R/$varname"}; |
1245
|
|
|
|
|
|
|
} |
1246
|
|
|
|
|
|
|
|
1247
|
|
|
|
|
|
|
#=for deleting |
1248
|
|
|
|
|
|
|
## Simple ancillary counter that we currently use to pair a sequence number |
1249
|
|
|
|
|
|
|
## with each minted identifier. However, these are independent actions. |
1250
|
|
|
|
|
|
|
## The direction parameter is negative, zero, or positive to count down, |
1251
|
|
|
|
|
|
|
## reset, or count up upon call. Returns the current counter value. |
1252
|
|
|
|
|
|
|
## |
1253
|
|
|
|
|
|
|
## (yyy should we make it do zero-padding on the left to a fixed width |
1254
|
|
|
|
|
|
|
## determined by number of digits in the total?) |
1255
|
|
|
|
|
|
|
## |
1256
|
|
|
|
|
|
|
#sub count { my( $noid, $direction )=@_; |
1257
|
|
|
|
|
|
|
# |
1258
|
|
|
|
|
|
|
# $direction > 0 |
1259
|
|
|
|
|
|
|
# and return ++$$noid{"$R/seqnum"}; |
1260
|
|
|
|
|
|
|
# $direction < 0 |
1261
|
|
|
|
|
|
|
# and return --$$noid{"$R/seqnum"}; |
1262
|
|
|
|
|
|
|
# # $direction must == 0 |
1263
|
|
|
|
|
|
|
# return $$noid{"$R/seqnum"} = 0; |
1264
|
|
|
|
|
|
|
#} |
1265
|
|
|
|
|
|
|
#=cut |
1266
|
|
|
|
|
|
|
|
1267
|
|
|
|
|
|
|
# A hold may be placed on an identifier to keep it from being minted/issued. |
1268
|
|
|
|
|
|
|
# Returns 1 on success, 0 on error. Sets errmsg() in either case. |
1269
|
|
|
|
|
|
|
# |
1270
|
|
|
|
|
|
|
sub hold { my( $noid, $contact, $on_off, @ids )=@_; |
1271
|
|
|
|
|
|
|
|
1272
|
|
|
|
|
|
|
# yyy what makes sense in this case? |
1273
|
|
|
|
|
|
|
#! $$noid{"$R/template"} and |
1274
|
|
|
|
|
|
|
# addmsg($noid, |
1275
|
|
|
|
|
|
|
# "error: holding makes no sense in a bind-only minter."), |
1276
|
|
|
|
|
|
|
# return(0); |
1277
|
|
|
|
|
|
|
! defined($contact) and |
1278
|
|
|
|
|
|
|
addmsg($noid, "error: contact undefined"), |
1279
|
|
|
|
|
|
|
return(0); |
1280
|
|
|
|
|
|
|
! defined($on_off) and |
1281
|
|
|
|
|
|
|
addmsg($noid, qq@error: hold "set" or "release"?@), |
1282
|
|
|
|
|
|
|
return(0); |
1283
|
|
|
|
|
|
|
! @ids and |
1284
|
|
|
|
|
|
|
addmsg($noid, qq@error: no Id(s) specified@), |
1285
|
|
|
|
|
|
|
return(0); |
1286
|
|
|
|
|
|
|
$on_off ne "set" && $on_off ne "release" and |
1287
|
|
|
|
|
|
|
addmsg($noid, "error: unrecognized hold directive ($on_off)"), |
1288
|
|
|
|
|
|
|
return(0); |
1289
|
|
|
|
|
|
|
|
1290
|
|
|
|
|
|
|
my $release = $on_off eq "release"; |
1291
|
|
|
|
|
|
|
# yyy what is sensible thing to do if no ids are present? |
1292
|
|
|
|
|
|
|
my $iderror = ""; |
1293
|
|
|
|
|
|
|
$$noid{"$R/genonly"} and |
1294
|
|
|
|
|
|
|
($iderror = validate($noid, "-", @ids)) !~ /error:/ and |
1295
|
|
|
|
|
|
|
$iderror = ""; |
1296
|
|
|
|
|
|
|
$iderror and |
1297
|
|
|
|
|
|
|
addmsg($noid, "error: hold operation not started -- one or " |
1298
|
|
|
|
|
|
|
. "more ids did not validate:\n$iderror"), |
1299
|
|
|
|
|
|
|
return(0); |
1300
|
|
|
|
|
|
|
my $status; |
1301
|
|
|
|
|
|
|
my $n = 0; |
1302
|
|
|
|
|
|
|
for my $id (@ids) { |
1303
|
|
|
|
|
|
|
if ($release) { # no hold means key doesn't exist |
1304
|
|
|
|
|
|
|
logmsg($noid, temper() . " $id: releasing hold") |
1305
|
|
|
|
|
|
|
if ($$noid{"$R/longterm"}); |
1306
|
|
|
|
|
|
|
dblock(); |
1307
|
|
|
|
|
|
|
$status = hold_release($noid, $id); |
1308
|
|
|
|
|
|
|
} |
1309
|
|
|
|
|
|
|
else { # "hold" means key exists |
1310
|
|
|
|
|
|
|
logmsg($noid, temper() . " $id: placing hold") |
1311
|
|
|
|
|
|
|
if ($$noid{"$R/longterm"}); |
1312
|
|
|
|
|
|
|
dblock(); |
1313
|
|
|
|
|
|
|
$status = hold_set($noid, $id); |
1314
|
|
|
|
|
|
|
} |
1315
|
|
|
|
|
|
|
dbunlock(); |
1316
|
|
|
|
|
|
|
! $status and |
1317
|
|
|
|
|
|
|
return(0); |
1318
|
|
|
|
|
|
|
$n++; # xxx should report number |
1319
|
|
|
|
|
|
|
|
1320
|
|
|
|
|
|
|
# Incr/Decrement for each id rather than by scalar(@ids); |
1321
|
|
|
|
|
|
|
# if something goes wrong in the loop, we won't be way off. |
1322
|
|
|
|
|
|
|
|
1323
|
|
|
|
|
|
|
# XXX should we refuse to hold if "long" and issued? |
1324
|
|
|
|
|
|
|
# else we cannot use "hold" in the sense of either |
1325
|
|
|
|
|
|
|
# "reserved for future use" or "reserved, never issued" |
1326
|
|
|
|
|
|
|
# |
1327
|
|
|
|
|
|
|
} |
1328
|
|
|
|
|
|
|
addmsg($noid, "ok: $n hold" . ($n == 1 ? "" : "s") . " placed"); |
1329
|
|
|
|
|
|
|
return(1); |
1330
|
|
|
|
|
|
|
} |
1331
|
|
|
|
|
|
|
|
1332
|
|
|
|
|
|
|
# Returns 1 on success, 0 on error. Use dblock() before and dbunlock() |
1333
|
|
|
|
|
|
|
# after calling this routine. |
1334
|
|
|
|
|
|
|
# yyy don't care if hold was in effect or not |
1335
|
|
|
|
|
|
|
# |
1336
|
|
|
|
|
|
|
sub hold_set { my( $noid, $id )=@_; |
1337
|
|
|
|
|
|
|
|
1338
|
|
|
|
|
|
|
$$noid{"$id\t$R/h"} = 1; # value doesn't matter |
1339
|
|
|
|
|
|
|
$$noid{"$R/held"}++; |
1340
|
|
|
|
|
|
|
if ($$noid{"$R/total"} != NOLIMIT # ie, if total is non-zero |
1341
|
|
|
|
|
|
|
&& $$noid{"$R/held"} > $$noid{"$R/oatop"}) { |
1342
|
|
|
|
|
|
|
my $m = "error: hold count (" . $$noid{"$R/held"} |
1343
|
|
|
|
|
|
|
. ") exceeding total possible on id $id"; |
1344
|
|
|
|
|
|
|
addmsg($noid, $m); |
1345
|
|
|
|
|
|
|
logmsg($noid, $m); |
1346
|
|
|
|
|
|
|
return(0); |
1347
|
|
|
|
|
|
|
} |
1348
|
|
|
|
|
|
|
return(1); |
1349
|
|
|
|
|
|
|
} |
1350
|
|
|
|
|
|
|
|
1351
|
|
|
|
|
|
|
# Returns 1 on success, 0 on error. Use dblock() before and dbunlock() |
1352
|
|
|
|
|
|
|
# after calling this routine. |
1353
|
|
|
|
|
|
|
# yyy don't care if hold was in effect or not |
1354
|
|
|
|
|
|
|
# |
1355
|
|
|
|
|
|
|
sub hold_release { my( $noid, $id )=@_; |
1356
|
|
|
|
|
|
|
|
1357
|
|
|
|
|
|
|
delete($$noid{"$id\t$R/h"}); |
1358
|
|
|
|
|
|
|
$$noid{"$R/held"}--; |
1359
|
|
|
|
|
|
|
if ($$noid{"$R/held"} < 0) { |
1360
|
|
|
|
|
|
|
my $m = "error: hold count (" . $$noid{"$R/held"} |
1361
|
|
|
|
|
|
|
. ") going negative on id $id"; |
1362
|
|
|
|
|
|
|
addmsg($noid, $m); |
1363
|
|
|
|
|
|
|
logmsg($noid, $m); |
1364
|
|
|
|
|
|
|
return(0); |
1365
|
|
|
|
|
|
|
} |
1366
|
|
|
|
|
|
|
return(1); |
1367
|
|
|
|
|
|
|
} |
1368
|
|
|
|
|
|
|
|
1369
|
|
|
|
|
|
|
# Return printable form of an integer after adding commas to separate |
1370
|
|
|
|
|
|
|
# groups of 3 digits. |
1371
|
|
|
|
|
|
|
# |
1372
|
|
|
|
|
|
|
sub human_num { my( $num )=@_; |
1373
|
|
|
|
|
|
|
|
1374
|
|
|
|
|
|
|
$num ||= 0; |
1375
|
|
|
|
|
|
|
my $numstr = sprintf("%u", $num); |
1376
|
|
|
|
|
|
|
if ($numstr =~ /^\d\d\d\d+$/) { # if num is 4 or more digits |
1377
|
|
|
|
|
|
|
$numstr .= ","; # prepare to add commas |
1378
|
|
|
|
|
|
|
while ($numstr =~ s/(\d)(\d\d\d,)/$1,$2/) {}; |
1379
|
|
|
|
|
|
|
chop($numstr); |
1380
|
|
|
|
|
|
|
} |
1381
|
|
|
|
|
|
|
return $numstr; |
1382
|
|
|
|
|
|
|
} |
1383
|
|
|
|
|
|
|
|
1384
|
|
|
|
|
|
|
# Return $elem: $val or error string. |
1385
|
|
|
|
|
|
|
# |
1386
|
|
|
|
|
|
|
sub id2elemval { my( $cursor, $verbose, $id, $elem )=@_; |
1387
|
|
|
|
|
|
|
|
1388
|
|
|
|
|
|
|
my $first = "$R/idmap/$elem\t"; |
1389
|
|
|
|
|
|
|
my ($key, $value) = ($first, 0); |
1390
|
|
|
|
|
|
|
my $status = $cursor->c_get($key, $value, DB_SET_RANGE); |
1391
|
|
|
|
|
|
|
$status and |
1392
|
|
|
|
|
|
|
return "error: id2elemval: c_get status/errno ($status/$!)"; |
1393
|
|
|
|
|
|
|
$key !~ /^$first/ and |
1394
|
|
|
|
|
|
|
return ""; |
1395
|
|
|
|
|
|
|
my ($pattern, $newval); |
1396
|
|
|
|
|
|
|
while (1) { # exhaustively visit all patterns for this element |
1397
|
|
|
|
|
|
|
($pattern) = ($key =~ m|$first(.+)|); |
1398
|
|
|
|
|
|
|
$newval = $id; |
1399
|
|
|
|
|
|
|
defined($pattern) and |
1400
|
|
|
|
|
|
|
# yyy kludgy use of unlikely delimiters |
1401
|
|
|
|
|
|
|
(eval '$newval =~ ' . qq@s$pattern$value@ and |
1402
|
|
|
|
|
|
|
# replaced, so return |
1403
|
|
|
|
|
|
|
return ($verbose ? "$elem: " : "") . $newval), |
1404
|
|
|
|
|
|
|
($@ and |
1405
|
|
|
|
|
|
|
return "error: id2elemval eval: $@") |
1406
|
|
|
|
|
|
|
; |
1407
|
|
|
|
|
|
|
$cursor->c_get($key, $value, DB_NEXT) != 0 and |
1408
|
|
|
|
|
|
|
return ""; |
1409
|
|
|
|
|
|
|
$key !~ /^$first/ and # no match and ran out of rules |
1410
|
|
|
|
|
|
|
return ""; |
1411
|
|
|
|
|
|
|
} |
1412
|
|
|
|
|
|
|
} |
1413
|
|
|
|
|
|
|
|
1414
|
|
|
|
|
|
|
# Initialize counters. |
1415
|
|
|
|
|
|
|
# |
1416
|
|
|
|
|
|
|
sub init_counters { my( $noid )=@_; |
1417
|
|
|
|
|
|
|
|
1418
|
|
|
|
|
|
|
# Variables: |
1419
|
|
|
|
|
|
|
# oacounter overall counter's current value (last value minted) |
1420
|
|
|
|
|
|
|
# saclist (sub) active counters list |
1421
|
|
|
|
|
|
|
# siclist (sub) inactive counters list |
1422
|
|
|
|
|
|
|
# c$n/value subcounter name's ($n) value |
1423
|
|
|
|
|
|
|
# c$n/top subcounter name's greatest possible value |
1424
|
|
|
|
|
|
|
|
1425
|
|
|
|
|
|
|
dblock(); |
1426
|
|
|
|
|
|
|
|
1427
|
|
|
|
|
|
|
$$noid{"$R/oacounter"} = 0; |
1428
|
|
|
|
|
|
|
my $total = $$noid{"$R/total"}; |
1429
|
|
|
|
|
|
|
|
1430
|
|
|
|
|
|
|
my $maxcounters = 293; # prime, a little more than 29*10 |
1431
|
|
|
|
|
|
|
# |
1432
|
|
|
|
|
|
|
# Using a prime under the theory (unverified) that it may help even |
1433
|
|
|
|
|
|
|
# out distribution across the more significant digits of generated |
1434
|
|
|
|
|
|
|
# identifiers. In this way, for example, a method for mapping an |
1435
|
|
|
|
|
|
|
# identifier to a pathname (eg, fk9tmb35x -> fk/9t/mb/35/x/, which |
1436
|
|
|
|
|
|
|
# could be a directory holding all files related to the named |
1437
|
|
|
|
|
|
|
# object), would result in a reasonably balanced filesystem tree |
1438
|
|
|
|
|
|
|
# -- no subdirectories too unevenly loaded. That's the hope anyway. |
1439
|
|
|
|
|
|
|
|
1440
|
|
|
|
|
|
|
$$noid{"$R/percounter"} = # max per counter, last has fewer |
1441
|
|
|
|
|
|
|
int($total / $maxcounters + 1); # round up to be > 0 |
1442
|
|
|
|
|
|
|
|
1443
|
|
|
|
|
|
|
my $n = 0; |
1444
|
|
|
|
|
|
|
my $t = $total; |
1445
|
|
|
|
|
|
|
my $pctr = $$noid{"$R/percounter"}; |
1446
|
|
|
|
|
|
|
my $saclist = ""; |
1447
|
|
|
|
|
|
|
while ($t > 0) { |
1448
|
|
|
|
|
|
|
$$noid{"$R/c${n}/top"} = ($t >= $pctr ? $pctr : $t); |
1449
|
|
|
|
|
|
|
$$noid{"$R/c${n}/value"} = 0; # yyy or 1? |
1450
|
|
|
|
|
|
|
$saclist .= "c$n "; |
1451
|
|
|
|
|
|
|
$t -= $pctr; |
1452
|
|
|
|
|
|
|
$n++; |
1453
|
|
|
|
|
|
|
} |
1454
|
|
|
|
|
|
|
$$noid{"$R/saclist"} = $saclist; |
1455
|
|
|
|
|
|
|
$$noid{"$R/siclist"} = ""; |
1456
|
|
|
|
|
|
|
$n--; |
1457
|
|
|
|
|
|
|
|
1458
|
|
|
|
|
|
|
dbunlock(); |
1459
|
|
|
|
|
|
|
|
1460
|
|
|
|
|
|
|
#print "saclist: $$noid{"$R/saclist"}\nfinal top: " |
1461
|
|
|
|
|
|
|
# . $$noid{"$R/c${n}/top"} . "\npercounter=$pctr\n"; |
1462
|
|
|
|
|
|
|
#foreach $c ($$saclist) { |
1463
|
|
|
|
|
|
|
# print "$c, "; |
1464
|
|
|
|
|
|
|
#} |
1465
|
|
|
|
|
|
|
#print "\n"; |
1466
|
|
|
|
|
|
|
} |
1467
|
|
|
|
|
|
|
|
1468
|
|
|
|
|
|
|
# This routine produces a new identifier by taking a previously recycled |
1469
|
|
|
|
|
|
|
# identifier from a queue (usually, a "used" identifier, but it might |
1470
|
|
|
|
|
|
|
# have been pre-recycled) or by generating a brand new one. |
1471
|
|
|
|
|
|
|
# |
1472
|
|
|
|
|
|
|
# The $contact should be the initials or descriptive string to help |
1473
|
|
|
|
|
|
|
# track who or what was happening at time of minting. |
1474
|
|
|
|
|
|
|
# |
1475
|
|
|
|
|
|
|
# Returns undef on error. |
1476
|
|
|
|
|
|
|
# |
1477
|
|
|
|
|
|
|
sub mint { my( $noid, $contact, $pepper )=@_; |
1478
|
|
|
|
|
|
|
|
1479
|
|
|
|
|
|
|
! defined($contact) and |
1480
|
|
|
|
|
|
|
addmsg($noid, "contact undefined"), |
1481
|
|
|
|
|
|
|
return undef; |
1482
|
|
|
|
|
|
|
|
1483
|
|
|
|
|
|
|
! $$noid{"$R/template"} and |
1484
|
|
|
|
|
|
|
addmsg($noid, "error: this minter does not generate " |
1485
|
|
|
|
|
|
|
. "identifiers (it does accept user-defined " |
1486
|
|
|
|
|
|
|
. "identifier and element bindings)."), |
1487
|
|
|
|
|
|
|
return undef; |
1488
|
|
|
|
|
|
|
# Check if the head of the queue is ripe. See comments under queue() |
1489
|
|
|
|
|
|
|
# for an explanation of how the queue works. |
1490
|
|
|
|
|
|
|
# |
1491
|
|
|
|
|
|
|
my $currdate = temper(); # fyi, 14 digits long |
1492
|
|
|
|
|
|
|
my $first = "$R/q/"; |
1493
|
|
|
|
|
|
|
my $db = $opendbtab{"bdb/$noid"}; |
1494
|
|
|
|
|
|
|
! (my $cursor = $db->db_cursor()) and |
1495
|
|
|
|
|
|
|
addmsg($noid, "couldn't create cursor"), |
1496
|
|
|
|
|
|
|
return undef; |
1497
|
|
|
|
|
|
|
|
1498
|
|
|
|
|
|
|
# The following is not a proper loop. Normally it should run once, |
1499
|
|
|
|
|
|
|
# but several cycles may be needed to weed out anomalies with the id |
1500
|
|
|
|
|
|
|
# at the head of the queue. If all goes well and we found something |
1501
|
|
|
|
|
|
|
# to mint from the queue, the last line in the loop exits the routine. |
1502
|
|
|
|
|
|
|
# If we drop out of the loop, it's because the queue wasn't ripe. |
1503
|
|
|
|
|
|
|
# |
1504
|
|
|
|
|
|
|
my ($id, $status, $key, $qdate, $circ_svec); |
1505
|
|
|
|
|
|
|
while (1) { |
1506
|
|
|
|
|
|
|
$key = $first; |
1507
|
|
|
|
|
|
|
$status = $cursor->c_get($key, $id, DB_SET_RANGE); |
1508
|
|
|
|
|
|
|
$status and |
1509
|
|
|
|
|
|
|
addmsg($noid, "mint: c_get status/errno ($status/$!)"), |
1510
|
|
|
|
|
|
|
return undef; |
1511
|
|
|
|
|
|
|
# The cursor, key and value are now set at the first item |
1512
|
|
|
|
|
|
|
# whose key is greater than or equal to $first. If the |
1513
|
|
|
|
|
|
|
# queue was empty, there should be no items under "$R/q/". |
1514
|
|
|
|
|
|
|
# |
1515
|
|
|
|
|
|
|
($qdate) = ($key =~ m|$R/q/(\d{14})|); |
1516
|
|
|
|
|
|
|
! defined($qdate) and # nothing in queue |
1517
|
|
|
|
|
|
|
# this is our chance -- see queue() comments for why |
1518
|
|
|
|
|
|
|
($$noid{"$R/fseqnum"} > SEQNUM_MIN and |
1519
|
|
|
|
|
|
|
$$noid{"$R/fseqnum"} = SEQNUM_MIN), |
1520
|
|
|
|
|
|
|
last; # so move on |
1521
|
|
|
|
|
|
|
# If the date of the earliest item to re-use hasn't arrived |
1522
|
|
|
|
|
|
|
$currdate < $qdate and |
1523
|
|
|
|
|
|
|
last; # move on |
1524
|
|
|
|
|
|
|
|
1525
|
|
|
|
|
|
|
# If we get here, head of queue is ripe. Remove from queue. |
1526
|
|
|
|
|
|
|
# Any "next" statement from now on in this loop discards the |
1527
|
|
|
|
|
|
|
# queue element. |
1528
|
|
|
|
|
|
|
# |
1529
|
|
|
|
|
|
|
$db->db_del($key); |
1530
|
|
|
|
|
|
|
if ($$noid{"$R/queued"}-- <= 0) { |
1531
|
|
|
|
|
|
|
my $m = "error: queued count (" . $$noid{"$R/queued"} |
1532
|
|
|
|
|
|
|
. ") going negative on id $id"; |
1533
|
|
|
|
|
|
|
addmsg($noid, $m); |
1534
|
|
|
|
|
|
|
logmsg($noid, $m); |
1535
|
|
|
|
|
|
|
return(undef); |
1536
|
|
|
|
|
|
|
} |
1537
|
|
|
|
|
|
|
|
1538
|
|
|
|
|
|
|
# We perform a few checks first to see if we're actually |
1539
|
|
|
|
|
|
|
# going to use this identifier. First, if there's a hold, |
1540
|
|
|
|
|
|
|
# remove it from the queue and check the queue again. |
1541
|
|
|
|
|
|
|
# |
1542
|
|
|
|
|
|
|
exists($$noid{"$id\t$R/h"}) and # if there's a hold |
1543
|
|
|
|
|
|
|
$$noid{"$R/longterm"} && logmsg($noid, "warning: id " |
1544
|
|
|
|
|
|
|
. "$id found in queue with a hold placed on " |
1545
|
|
|
|
|
|
|
. "it -- removed from queue."), |
1546
|
|
|
|
|
|
|
next; |
1547
|
|
|
|
|
|
|
# yyy this means id on "hold" can still have a 'q' circ status? |
1548
|
|
|
|
|
|
|
|
1549
|
|
|
|
|
|
|
$circ_svec = get_circ_svec($noid, $id); |
1550
|
|
|
|
|
|
|
|
1551
|
|
|
|
|
|
|
$circ_svec =~ /^i/ and |
1552
|
|
|
|
|
|
|
logmsg($noid, "error: id $id appears to have been " |
1553
|
|
|
|
|
|
|
. "issued while still in the queue -- " |
1554
|
|
|
|
|
|
|
. "circ record is " . $$noid{"$id\t$R/c"}), |
1555
|
|
|
|
|
|
|
next |
1556
|
|
|
|
|
|
|
; |
1557
|
|
|
|
|
|
|
$circ_svec =~ /^u/ and |
1558
|
|
|
|
|
|
|
logmsg($noid, "note: id $id, marked as unqueued, is " |
1559
|
|
|
|
|
|
|
. "now being removed/skipped in the queue -- " |
1560
|
|
|
|
|
|
|
. "circ record is " . $$noid{"$id\t$R/c"}), |
1561
|
|
|
|
|
|
|
next |
1562
|
|
|
|
|
|
|
; |
1563
|
|
|
|
|
|
|
$circ_svec =~ /^([^q])/ and |
1564
|
|
|
|
|
|
|
logmsg($noid, "error: id $id found in queue has an " |
1565
|
|
|
|
|
|
|
. "unknown circ status ($1) -- " |
1566
|
|
|
|
|
|
|
. "circ record is " . $$noid{"$id\t$R/c"}), |
1567
|
|
|
|
|
|
|
next |
1568
|
|
|
|
|
|
|
; |
1569
|
|
|
|
|
|
|
|
1570
|
|
|
|
|
|
|
# Finally, if there's no circulation record, it means that |
1571
|
|
|
|
|
|
|
# it was queued to get it minted earlier or later than it |
1572
|
|
|
|
|
|
|
# would normally be minted. Log if term is "long". |
1573
|
|
|
|
|
|
|
# |
1574
|
|
|
|
|
|
|
$circ_svec eq "" and |
1575
|
|
|
|
|
|
|
($$noid{"$R/longterm"} && logmsg($noid, "note: " |
1576
|
|
|
|
|
|
|
. "queued id $id coming out of queue on first " |
1577
|
|
|
|
|
|
|
. "minting (pre-cycled)")) |
1578
|
|
|
|
|
|
|
; |
1579
|
|
|
|
|
|
|
|
1580
|
|
|
|
|
|
|
# If we get here, our identifier has now passed its tests. |
1581
|
|
|
|
|
|
|
# Do final identifier signoff and return. |
1582
|
|
|
|
|
|
|
# |
1583
|
|
|
|
|
|
|
return(set_circ_rec($noid, |
1584
|
|
|
|
|
|
|
$id, 'i' . $circ_svec, $currdate, $contact)); |
1585
|
|
|
|
|
|
|
} |
1586
|
|
|
|
|
|
|
|
1587
|
|
|
|
|
|
|
# If we get here, we're not getting an id from the queue. |
1588
|
|
|
|
|
|
|
# Instead we have to generate one. |
1589
|
|
|
|
|
|
|
# |
1590
|
|
|
|
|
|
|
# As above, the following is not a proper loop. Normally it should |
1591
|
|
|
|
|
|
|
# run once, but several cycles may be needed to weed out anomalies |
1592
|
|
|
|
|
|
|
# with the generated id (eg, there's a hold on the id, or it was |
1593
|
|
|
|
|
|
|
# queued to delay issue). |
1594
|
|
|
|
|
|
|
# |
1595
|
|
|
|
|
|
|
while (1) { |
1596
|
|
|
|
|
|
|
|
1597
|
|
|
|
|
|
|
# Next is the important seeding of random number generator. |
1598
|
|
|
|
|
|
|
# We need this so that we get the same exact series of |
1599
|
|
|
|
|
|
|
# pseudo-random numbers, just in case we have to wipe out a |
1600
|
|
|
|
|
|
|
# generator and start over. That way, the n-th identifier |
1601
|
|
|
|
|
|
|
# will be the same, no matter how often we have to start |
1602
|
|
|
|
|
|
|
# over. This step has no effect when $generator_type == |
1603
|
|
|
|
|
|
|
# "sequential". |
1604
|
|
|
|
|
|
|
# |
1605
|
|
|
|
|
|
|
srand($$noid{"$R/oacounter"}); |
1606
|
|
|
|
|
|
|
|
1607
|
|
|
|
|
|
|
# The id returned in this next step may have a "+" character |
1608
|
|
|
|
|
|
|
# that n2xdig() appended to it. The checkchar() routine |
1609
|
|
|
|
|
|
|
# will convert it to a check character. |
1610
|
|
|
|
|
|
|
# |
1611
|
|
|
|
|
|
|
$id = genid($noid); |
1612
|
|
|
|
|
|
|
! defined($id) |
1613
|
|
|
|
|
|
|
and return undef; |
1614
|
|
|
|
|
|
|
|
1615
|
|
|
|
|
|
|
# Prepend NAAN and separator if there is a NAAN. |
1616
|
|
|
|
|
|
|
# |
1617
|
|
|
|
|
|
|
$$noid{"$R/firstpart"} and |
1618
|
|
|
|
|
|
|
$id = $$noid{"$R/firstpart"} . $id; |
1619
|
|
|
|
|
|
|
|
1620
|
|
|
|
|
|
|
# Add check character if called for. |
1621
|
|
|
|
|
|
|
# |
1622
|
|
|
|
|
|
|
$$noid{"$R/addcheckchar"} and |
1623
|
|
|
|
|
|
|
$id = &checkchar($id); |
1624
|
|
|
|
|
|
|
|
1625
|
|
|
|
|
|
|
# There may be a hold on an id, meaning that it is not to |
1626
|
|
|
|
|
|
|
# be issued (or re-issued). |
1627
|
|
|
|
|
|
|
# |
1628
|
|
|
|
|
|
|
exists($$noid{"$id\t$R/h"}) and # if there's a hold |
1629
|
|
|
|
|
|
|
next; # do genid() again |
1630
|
|
|
|
|
|
|
|
1631
|
|
|
|
|
|
|
# It's usual to find no circulation record. However, |
1632
|
|
|
|
|
|
|
# there may be a circulation record if the generator term |
1633
|
|
|
|
|
|
|
# is not "long" and we've wrapped (restarted) the counter, |
1634
|
|
|
|
|
|
|
# of if it was queued before first minting. If the term |
1635
|
|
|
|
|
|
|
# is "long", the generated id automatically gets a hold. |
1636
|
|
|
|
|
|
|
# |
1637
|
|
|
|
|
|
|
$circ_svec = get_circ_svec($noid, $id); |
1638
|
|
|
|
|
|
|
|
1639
|
|
|
|
|
|
|
# A little unusual is the case when something has a |
1640
|
|
|
|
|
|
|
# circulation status of 'q', meaning it has been queued |
1641
|
|
|
|
|
|
|
# before first issue, presumably to get it minted earlier or |
1642
|
|
|
|
|
|
|
# later than it would normally be minted; if the id we just |
1643
|
|
|
|
|
|
|
# generated is marked as being in the queue (clearly not at |
1644
|
|
|
|
|
|
|
# the head of the queue, or we would have seen it in the |
1645
|
|
|
|
|
|
|
# previous while loop), we go to generate another id. If |
1646
|
|
|
|
|
|
|
# term is "long", log that we skipped this one. |
1647
|
|
|
|
|
|
|
# |
1648
|
|
|
|
|
|
|
$circ_svec =~ /^q/ and |
1649
|
|
|
|
|
|
|
($$noid{"$R/longterm"} && logmsg($noid, |
1650
|
|
|
|
|
|
|
"note: will not issue genid()'d $id as it's " |
1651
|
|
|
|
|
|
|
. "status is 'q', circ_rec is " |
1652
|
|
|
|
|
|
|
. $$noid{"$id\t$R/c"})), |
1653
|
|
|
|
|
|
|
next |
1654
|
|
|
|
|
|
|
; |
1655
|
|
|
|
|
|
|
|
1656
|
|
|
|
|
|
|
# If the circulation status is 'i' it means that the id is |
1657
|
|
|
|
|
|
|
# being re-issued. This shouldn't happen unless the counter |
1658
|
|
|
|
|
|
|
# has wrapped around to the beginning. If term is "long", |
1659
|
|
|
|
|
|
|
# an id can be re-issued only if (a) its hold was released |
1660
|
|
|
|
|
|
|
# and (b) it was placed in the queue (thus marked with 'q'). |
1661
|
|
|
|
|
|
|
# |
1662
|
|
|
|
|
|
|
$circ_svec =~ /^i/ && ($$noid{"$R/longterm"} |
1663
|
|
|
|
|
|
|
|| ! $$noid{"$R/wrap"}) and |
1664
|
|
|
|
|
|
|
logmsg($noid, "error: id $id cannot be " |
1665
|
|
|
|
|
|
|
. "re-issued except by going through the " |
1666
|
|
|
|
|
|
|
. "queue, circ_rec " . $$noid{"$id\t$R/c"}), |
1667
|
|
|
|
|
|
|
next |
1668
|
|
|
|
|
|
|
; |
1669
|
|
|
|
|
|
|
$circ_svec =~ /^u/ and |
1670
|
|
|
|
|
|
|
logmsg($noid, "note: generating id $id, currently " |
1671
|
|
|
|
|
|
|
. "marked as unqueued, circ record is " |
1672
|
|
|
|
|
|
|
. $$noid{"$id\t$R/c"}), |
1673
|
|
|
|
|
|
|
next |
1674
|
|
|
|
|
|
|
; |
1675
|
|
|
|
|
|
|
$circ_svec =~ /^([^iqu])/ and |
1676
|
|
|
|
|
|
|
logmsg($noid, "error: id $id has unknown circulation " |
1677
|
|
|
|
|
|
|
. "status ($1), circ_rec " |
1678
|
|
|
|
|
|
|
. $$noid{"$id\t$R/c"}), |
1679
|
|
|
|
|
|
|
next |
1680
|
|
|
|
|
|
|
; |
1681
|
|
|
|
|
|
|
# |
1682
|
|
|
|
|
|
|
# Note that it's OK/normal if $circ_svec was an empty string. |
1683
|
|
|
|
|
|
|
|
1684
|
|
|
|
|
|
|
# If we get here, our identifier has now passed its tests. |
1685
|
|
|
|
|
|
|
# Do final identifier signoff and return. |
1686
|
|
|
|
|
|
|
# |
1687
|
|
|
|
|
|
|
return(set_circ_rec($noid, |
1688
|
|
|
|
|
|
|
$id, 'i' . $circ_svec, $currdate, $contact)); |
1689
|
|
|
|
|
|
|
} |
1690
|
|
|
|
|
|
|
# yyy |
1691
|
|
|
|
|
|
|
# Note that we don't assign any value to the very important key=$id. |
1692
|
|
|
|
|
|
|
# What should it be bound to? Let's decide later. |
1693
|
|
|
|
|
|
|
|
1694
|
|
|
|
|
|
|
# yyy |
1695
|
|
|
|
|
|
|
# Often we want to bind an id initially even if the object or record |
1696
|
|
|
|
|
|
|
# it identifies is "in progress", as this gives way to begin tracking, |
1697
|
|
|
|
|
|
|
# eg, back to the person responsible. |
1698
|
|
|
|
|
|
|
# |
1699
|
|
|
|
|
|
|
} |
1700
|
|
|
|
|
|
|
|
1701
|
|
|
|
|
|
|
# Record user (":/:/...") values in admin area. |
1702
|
|
|
|
|
|
|
sub note { my( $noid, $contact, $key, $value )=@_; |
1703
|
|
|
|
|
|
|
my $db = $opendbtab{"bdb/$noid"}; |
1704
|
|
|
|
|
|
|
dblock(); |
1705
|
|
|
|
|
|
|
my $status = $db->db_put("$R/$R/$key", $value); |
1706
|
|
|
|
|
|
|
dbunlock(); |
1707
|
|
|
|
|
|
|
$$noid{"$R/longterm"} and |
1708
|
|
|
|
|
|
|
logmsg($noid, "note: note attempt under $key by $contact" |
1709
|
|
|
|
|
|
|
. ($status ? "" : " -- note failed")); |
1710
|
|
|
|
|
|
|
if ($status) { |
1711
|
|
|
|
|
|
|
addmsg($noid, "db->db_put status/errno ($status/$!)"); |
1712
|
|
|
|
|
|
|
return 0; |
1713
|
|
|
|
|
|
|
} |
1714
|
|
|
|
|
|
|
return 1; |
1715
|
|
|
|
|
|
|
} |
1716
|
|
|
|
|
|
|
|
1717
|
|
|
|
|
|
|
# Convert a number to an extended digit according to $mask and $generator_type |
1718
|
|
|
|
|
|
|
# and return (without prefix or NAAN). A $mask character of 'k' gets |
1719
|
|
|
|
|
|
|
# converted to '+' in the returned string; post-processing will eventually |
1720
|
|
|
|
|
|
|
# turn it into a computed check character. |
1721
|
|
|
|
|
|
|
# |
1722
|
|
|
|
|
|
|
sub n2xdig { my( $num, $mask )=@_; |
1723
|
|
|
|
|
|
|
my $s = ''; |
1724
|
|
|
|
|
|
|
my ($div, $remainder, $c); |
1725
|
|
|
|
|
|
|
|
1726
|
|
|
|
|
|
|
# Confirm well-formedness of $mask before proceeding. |
1727
|
|
|
|
|
|
|
# |
1728
|
|
|
|
|
|
|
$mask !~ /^[rsz][de]+k?$/ |
1729
|
|
|
|
|
|
|
and return undef; |
1730
|
|
|
|
|
|
|
|
1731
|
|
|
|
|
|
|
my $varwidth = 0; # we start in fixed width part of the mask |
1732
|
|
|
|
|
|
|
my @rmask = reverse(split(//, $mask)); # process each char in reverse |
1733
|
|
|
|
|
|
|
while ($num != 0 || ! $varwidth) { |
1734
|
|
|
|
|
|
|
if (! $varwidth) { |
1735
|
|
|
|
|
|
|
$c = shift @rmask; # check next mask character, |
1736
|
|
|
|
|
|
|
! defined($c) |
1737
|
|
|
|
|
|
|
|| $c =~ /[rs]/ # terminate on r or s even if |
1738
|
|
|
|
|
|
|
and last; # $num is not all used up yet |
1739
|
|
|
|
|
|
|
$c =~ /e/ and |
1740
|
|
|
|
|
|
|
$div = $alphacount |
1741
|
|
|
|
|
|
|
or |
1742
|
|
|
|
|
|
|
$c =~ /d/ and |
1743
|
|
|
|
|
|
|
$div = $digitcount |
1744
|
|
|
|
|
|
|
or |
1745
|
|
|
|
|
|
|
$c =~ /z/ and |
1746
|
|
|
|
|
|
|
$varwidth = 1 # re-uses last $div value |
1747
|
|
|
|
|
|
|
and next |
1748
|
|
|
|
|
|
|
or |
1749
|
|
|
|
|
|
|
$c =~ /k/ and |
1750
|
|
|
|
|
|
|
next |
1751
|
|
|
|
|
|
|
; |
1752
|
|
|
|
|
|
|
#=for later |
1753
|
|
|
|
|
|
|
## why is this slower? should be faster since it does NOT use regexprs |
1754
|
|
|
|
|
|
|
# ! defined($c) || # terminate on r or s even if |
1755
|
|
|
|
|
|
|
# $c eq 'r' || $c eq 's' |
1756
|
|
|
|
|
|
|
# and last; # $num is not all used up yet |
1757
|
|
|
|
|
|
|
# $c eq 'e' and |
1758
|
|
|
|
|
|
|
# $div = $alphacount |
1759
|
|
|
|
|
|
|
# or |
1760
|
|
|
|
|
|
|
# $c eq 'd' and |
1761
|
|
|
|
|
|
|
# $div = $digitcount |
1762
|
|
|
|
|
|
|
# or |
1763
|
|
|
|
|
|
|
# $c eq 'z' and |
1764
|
|
|
|
|
|
|
# $varwidth = 1 # re-uses last $div value |
1765
|
|
|
|
|
|
|
# and next |
1766
|
|
|
|
|
|
|
# or |
1767
|
|
|
|
|
|
|
# $c eq 'k' and |
1768
|
|
|
|
|
|
|
# next |
1769
|
|
|
|
|
|
|
# ; |
1770
|
|
|
|
|
|
|
#=cut |
1771
|
|
|
|
|
|
|
} |
1772
|
|
|
|
|
|
|
$remainder = $num % $div; |
1773
|
|
|
|
|
|
|
$num = int($num / $div); |
1774
|
|
|
|
|
|
|
$s = $xdig[$remainder] . $s; |
1775
|
|
|
|
|
|
|
} |
1776
|
|
|
|
|
|
|
$mask =~ /k$/ and # if it ends in a check character |
1777
|
|
|
|
|
|
|
$s .= "+"; # represent it with plus in new id |
1778
|
|
|
|
|
|
|
return $s; |
1779
|
|
|
|
|
|
|
} |
1780
|
|
|
|
|
|
|
|
1781
|
|
|
|
|
|
|
# yyy templates should probably have names, eg, jk##.. could be jk4 |
1782
|
|
|
|
|
|
|
# or jk22, as in "./noid testdb/jk4 ... " |
1783
|
|
|
|
|
|
|
|
1784
|
|
|
|
|
|
|
# Reads template looking for errors and returns the total number of |
1785
|
|
|
|
|
|
|
# identifiers that it is capable of generating, using NOLIMIT to mean |
1786
|
|
|
|
|
|
|
# indefinite (unbounded). Returns 0 on error. Variables $prefix, |
1787
|
|
|
|
|
|
|
# $mask, and $generator_type are output parameters. |
1788
|
|
|
|
|
|
|
# |
1789
|
|
|
|
|
|
|
# $message will always be set; 0 return with error, 1 return with synonym |
1790
|
|
|
|
|
|
|
|
1791
|
|
|
|
|
|
|
# |
1792
|
|
|
|
|
|
|
sub parse_template { my( $template, $prefix, $mask, $gen_type, $message )=@_; |
1793
|
|
|
|
|
|
|
|
1794
|
|
|
|
|
|
|
my $dirname; |
1795
|
|
|
|
|
|
|
my $msg = \$_[4]; # so we can modify $message argument easily |
1796
|
|
|
|
|
|
|
$$msg = ""; |
1797
|
|
|
|
|
|
|
|
1798
|
|
|
|
|
|
|
# Strip final spaces and slashes. If there's a pathname, |
1799
|
|
|
|
|
|
|
# save directory and final component separately. |
1800
|
|
|
|
|
|
|
# |
1801
|
|
|
|
|
|
|
$template ||= ""; |
1802
|
|
|
|
|
|
|
$template =~ s|[/\s]+$||; # strip final spaces or slashes |
1803
|
|
|
|
|
|
|
($dirname, $template) = $template =~ m|^(.*/)?([^/]+)$|; |
1804
|
|
|
|
|
|
|
$dirname ||= ""; # make sure $dirname is defined |
1805
|
|
|
|
|
|
|
|
1806
|
|
|
|
|
|
|
! $template || $template eq "-" and |
1807
|
|
|
|
|
|
|
$$msg = "parse_template: no minting possible.", |
1808
|
|
|
|
|
|
|
$_[1] = $_[2] = $_[3] = "", |
1809
|
|
|
|
|
|
|
return NOLIMIT; |
1810
|
|
|
|
|
|
|
$template !~ /^([^\.]*)\.(\w+)/ and |
1811
|
|
|
|
|
|
|
$$msg = "parse_template: no template mask - " |
1812
|
|
|
|
|
|
|
. "can't generate identifiers.", |
1813
|
|
|
|
|
|
|
return 0; |
1814
|
|
|
|
|
|
|
($prefix, $mask) = ($1 || "", $2); |
1815
|
|
|
|
|
|
|
|
1816
|
|
|
|
|
|
|
$mask !~ /^[rsz]/ and |
1817
|
|
|
|
|
|
|
$$msg = "parse_template: mask must begin with one of " |
1818
|
|
|
|
|
|
|
. "the letters\n'r' (random), 's' (sequential), " |
1819
|
|
|
|
|
|
|
. "or 'z' (sequential unlimited).", |
1820
|
|
|
|
|
|
|
return 0; |
1821
|
|
|
|
|
|
|
|
1822
|
|
|
|
|
|
|
$mask !~ /^.[^k]+k?$/ and |
1823
|
|
|
|
|
|
|
$$msg = "parse_template: exactly one check character " |
1824
|
|
|
|
|
|
|
. "(k) is allowed, and it may\nonly appear at the " |
1825
|
|
|
|
|
|
|
. "end of a string of one or more mask characters.", |
1826
|
|
|
|
|
|
|
return 0; |
1827
|
|
|
|
|
|
|
|
1828
|
|
|
|
|
|
|
$mask !~ /^.[de]+k?$/ and |
1829
|
|
|
|
|
|
|
$$msg = "parse_template: a mask may contain only the " |
1830
|
|
|
|
|
|
|
. "letters 'd' or 'e'.", |
1831
|
|
|
|
|
|
|
return 0; |
1832
|
|
|
|
|
|
|
|
1833
|
|
|
|
|
|
|
# Check prefix for errors. |
1834
|
|
|
|
|
|
|
# |
1835
|
|
|
|
|
|
|
my $c; |
1836
|
|
|
|
|
|
|
my $has_cc = ($mask =~ /k$/); |
1837
|
|
|
|
|
|
|
for $c (split //, $prefix) { |
1838
|
|
|
|
|
|
|
if ($has_cc && $c ne '/' && ! exists($ordxdig{$c})) { |
1839
|
|
|
|
|
|
|
$$msg = "parse_template: with a check character " |
1840
|
|
|
|
|
|
|
. "at the end, a mask may contain only " |
1841
|
|
|
|
|
|
|
. qq@characters from "$legalstring".@; |
1842
|
|
|
|
|
|
|
return 0; |
1843
|
|
|
|
|
|
|
} |
1844
|
|
|
|
|
|
|
} |
1845
|
|
|
|
|
|
|
|
1846
|
|
|
|
|
|
|
# If we get here, the mask is well-formed. Now try to come up with |
1847
|
|
|
|
|
|
|
# a short synonym for the template; it should start with the |
1848
|
|
|
|
|
|
|
# template's prefix and then an integer representing the number of |
1849
|
|
|
|
|
|
|
# letters in identifiers generated by the template. For example, |
1850
|
|
|
|
|
|
|
# a template of "ft.rddeek" would be "ft5". |
1851
|
|
|
|
|
|
|
# |
1852
|
|
|
|
|
|
|
my $masklen = length($mask) - 1; # subtract one for [rsz] |
1853
|
|
|
|
|
|
|
$$msg = $prefix . $masklen; |
1854
|
|
|
|
|
|
|
$mask =~ /^z/ and # "+" indicates length can grow |
1855
|
|
|
|
|
|
|
$$msg .= "+"; |
1856
|
|
|
|
|
|
|
|
1857
|
|
|
|
|
|
|
# r means random; |
1858
|
|
|
|
|
|
|
# s means sequential, limited; |
1859
|
|
|
|
|
|
|
# z means sequential, no limit, and repeat most significant mask |
1860
|
|
|
|
|
|
|
# char as needed; |
1861
|
|
|
|
|
|
|
|
1862
|
|
|
|
|
|
|
my $total = 1; |
1863
|
|
|
|
|
|
|
for $c (split //, $mask) { |
1864
|
|
|
|
|
|
|
# Mask chars it could be are: d e k |
1865
|
|
|
|
|
|
|
$c =~ /e/ and |
1866
|
|
|
|
|
|
|
$total *= $alphacount |
1867
|
|
|
|
|
|
|
or |
1868
|
|
|
|
|
|
|
$c =~ /d/ and |
1869
|
|
|
|
|
|
|
$total *= $digitcount |
1870
|
|
|
|
|
|
|
or |
1871
|
|
|
|
|
|
|
$c =~ /[krsz]/ and |
1872
|
|
|
|
|
|
|
next |
1873
|
|
|
|
|
|
|
; |
1874
|
|
|
|
|
|
|
} |
1875
|
|
|
|
|
|
|
|
1876
|
|
|
|
|
|
|
$_[1] = $prefix; |
1877
|
|
|
|
|
|
|
$_[2] = $mask; |
1878
|
|
|
|
|
|
|
$_[3] = $gen_type = ($mask =~ /^r/ ? "random" : "sequential"); |
1879
|
|
|
|
|
|
|
# $_[4] was set to the synonym already |
1880
|
|
|
|
|
|
|
return ($mask =~ /^z/ ? NOLIMIT : $total); |
1881
|
|
|
|
|
|
|
} |
1882
|
|
|
|
|
|
|
|
1883
|
|
|
|
|
|
|
# An identifier may be queued to be issued/minted. Usually this is used |
1884
|
|
|
|
|
|
|
# to recycle a previously issued identifier, but it may also be used to |
1885
|
|
|
|
|
|
|
# delay or advance the birth of an identifier that would normally be |
1886
|
|
|
|
|
|
|
# issued in its own good time. The $when argument may be "first", "lvf", |
1887
|
|
|
|
|
|
|
# "delete", or a number and a letter designating units of seconds ('s', |
1888
|
|
|
|
|
|
|
# the default) or days ('d') which is a delay added to the current time; |
1889
|
|
|
|
|
|
|
# a $when of "now" means use the current time with no delay. |
1890
|
|
|
|
|
|
|
|
1891
|
|
|
|
|
|
|
# The queue is composed of keys of the form $R/q/$qdate/$seqnum/$paddedid, |
1892
|
|
|
|
|
|
|
# with the correponding values being the actual queued identifiers. The |
1893
|
|
|
|
|
|
|
# Btree allows us to step sequentially through the queue in an ordering |
1894
|
|
|
|
|
|
|
# that is a side-effect of our key structure. Left-to-right, it is |
1895
|
|
|
|
|
|
|
# |
1896
|
|
|
|
|
|
|
# :/q/ $R/q/, 4 characters wide |
1897
|
|
|
|
|
|
|
# $qdate 14 digits wide, or 14 zeroes if "first" or "lvf" |
1898
|
|
|
|
|
|
|
# $seqnum 6 digits wide, or 000000 if "lvf" |
1899
|
|
|
|
|
|
|
# $paddedid id "value", zero-padded on left, for "lvf" |
1900
|
|
|
|
|
|
|
# |
1901
|
|
|
|
|
|
|
# The $seqnum is there to help ensure queue order for up to a million queue |
1902
|
|
|
|
|
|
|
# requests in a second (the granularity of our clock). [ yyy $seqnum would |
1903
|
|
|
|
|
|
|
# probably be obviated if we were using DB_DUP, but there's much conversion |
1904
|
|
|
|
|
|
|
# involved with that ] |
1905
|
|
|
|
|
|
|
# |
1906
|
|
|
|
|
|
|
# We base our $seqnum (min is 1) on one of two stored sources: "fseqnum" |
1907
|
|
|
|
|
|
|
# for queue "first" requests or "gseqnum" for queue with a real time stamp |
1908
|
|
|
|
|
|
|
# ("now" or delayed). To implement queue "first", we use an artificial |
1909
|
|
|
|
|
|
|
# time stamp of all zeroes, just like for "lvf"; to keep all "lvf" sorted |
1910
|
|
|
|
|
|
|
# before "first" requests, we reset fseqnum and gseqnum to 1 (not zero). |
1911
|
|
|
|
|
|
|
# We reset gseqnum whenever we use it at a different time from last time |
1912
|
|
|
|
|
|
|
# since sort order will be guaranteed by different values of $qdate. We |
1913
|
|
|
|
|
|
|
# don't have that guarantee with the all-zeroes time stamp and fseqnum, |
1914
|
|
|
|
|
|
|
# so we put off resetting fseqnum until it is over 500,000 and the queue |
1915
|
|
|
|
|
|
|
# is empty, so we do then when checking the queue in mint(). |
1916
|
|
|
|
|
|
|
# |
1917
|
|
|
|
|
|
|
# This key structure should ensure that the queue is sorted first by date. |
1918
|
|
|
|
|
|
|
# As long as fewer than a million queue requests come in within a second, |
1919
|
|
|
|
|
|
|
# we can make sure queue ordering is fifo. To support "lvf" (lowest value |
1920
|
|
|
|
|
|
|
# first) recycling, the $date and $seqnum fields are all zero, so the |
1921
|
|
|
|
|
|
|
# ordering is determined entirely by the numeric "value" of identifier |
1922
|
|
|
|
|
|
|
# (really only makes sense for a sequential generator); to achieve the |
1923
|
|
|
|
|
|
|
# numeric sorting in the lexical Btree ordering, we strip off any prefix, |
1924
|
|
|
|
|
|
|
# right-justify the identifier, and zero-pad on the left to create a number |
1925
|
|
|
|
|
|
|
# that is 16 digits wider than the Template mask [yyy kludge that doesn't |
1926
|
|
|
|
|
|
|
# take any overflow into account, or bigints for that matter]. |
1927
|
|
|
|
|
|
|
# |
1928
|
|
|
|
|
|
|
# Returns the array of corresponding strings (errors and "id:" strings) |
1929
|
|
|
|
|
|
|
# or an empty array on error. |
1930
|
|
|
|
|
|
|
# |
1931
|
|
|
|
|
|
|
sub queue { my( $noid, $contact, $when, @ids )=@_; |
1932
|
|
|
|
|
|
|
|
1933
|
|
|
|
|
|
|
! $$noid{"$R/template"} and |
1934
|
|
|
|
|
|
|
addmsg($noid, |
1935
|
|
|
|
|
|
|
"error: queuing makes no sense in a bind-only minter."), |
1936
|
|
|
|
|
|
|
return(()); |
1937
|
|
|
|
|
|
|
! defined($contact) and |
1938
|
|
|
|
|
|
|
addmsg($noid, "error: contact undefined"), |
1939
|
|
|
|
|
|
|
return(()); |
1940
|
|
|
|
|
|
|
! defined($when) || $when !~ /\S/ and |
1941
|
|
|
|
|
|
|
addmsg($noid, "error: queue when? (eg, first, lvf, 30d, now)"), |
1942
|
|
|
|
|
|
|
return(()); |
1943
|
|
|
|
|
|
|
# yyy what is sensible thing to do if no ids are present? |
1944
|
|
|
|
|
|
|
scalar(@ids) < 1 and |
1945
|
|
|
|
|
|
|
addmsg($noid, "error: must specify at least one id to queue."), |
1946
|
|
|
|
|
|
|
return(()); |
1947
|
|
|
|
|
|
|
my ($seqnum, $delete) = (0, 0, 0); |
1948
|
|
|
|
|
|
|
my ($fixsqn, $qdate); # purposely undefined |
1949
|
|
|
|
|
|
|
|
1950
|
|
|
|
|
|
|
# You can express a delay in days (d) or seconds (s, default). |
1951
|
|
|
|
|
|
|
# |
1952
|
|
|
|
|
|
|
if ($when =~ /^(\d+)([ds]?)$/) { # current time plus a delay |
1953
|
|
|
|
|
|
|
# The number of seconds in one day is 86400. |
1954
|
|
|
|
|
|
|
my $multiplier = (defined($2) && $2 eq "d" ? 86400 : 1); |
1955
|
|
|
|
|
|
|
$qdate = temper(time() + $1 * $multiplier); |
1956
|
|
|
|
|
|
|
} |
1957
|
|
|
|
|
|
|
elsif ($when eq "now") { # a synonym for current time |
1958
|
|
|
|
|
|
|
$qdate = temper(time()); |
1959
|
|
|
|
|
|
|
} |
1960
|
|
|
|
|
|
|
elsif ($when eq "first") { |
1961
|
|
|
|
|
|
|
# Lowest value first (lvf) requires $qdate of all zeroes. |
1962
|
|
|
|
|
|
|
# To achieve "first" semantics, we use a $qdate of all |
1963
|
|
|
|
|
|
|
# zeroes (default above), which means this key will be |
1964
|
|
|
|
|
|
|
# selected even earlier than a key that became ripe in the |
1965
|
|
|
|
|
|
|
# queue 85 days ago but wasn't selected because no one |
1966
|
|
|
|
|
|
|
# minted anything in the last 85 days. |
1967
|
|
|
|
|
|
|
# |
1968
|
|
|
|
|
|
|
$seqnum = $$noid{"$R/fseqnum"}; |
1969
|
|
|
|
|
|
|
# |
1970
|
|
|
|
|
|
|
# NOTE: fseqnum is reset only when queue is empty; see mint(). |
1971
|
|
|
|
|
|
|
# If queue never empties fseqnum will simply keep growing, |
1972
|
|
|
|
|
|
|
# so we effectively truncate on the left to 6 digits with mod |
1973
|
|
|
|
|
|
|
# arithmetic when we convert it to $fixsqn via sprintf(). |
1974
|
|
|
|
|
|
|
} |
1975
|
|
|
|
|
|
|
elsif ($when eq "delete") { |
1976
|
|
|
|
|
|
|
$delete = 1; |
1977
|
|
|
|
|
|
|
} |
1978
|
|
|
|
|
|
|
elsif ($when ne "lvf") { |
1979
|
|
|
|
|
|
|
addmsg($noid, "error: unrecognized queue time: $when"); |
1980
|
|
|
|
|
|
|
return(()); |
1981
|
|
|
|
|
|
|
} |
1982
|
|
|
|
|
|
|
|
1983
|
|
|
|
|
|
|
defined($qdate) and # current time plus optional delay |
1984
|
|
|
|
|
|
|
($qdate > $$noid{"$R/gseqnum_date"} and |
1985
|
|
|
|
|
|
|
$seqnum = $$noid{"$R/gseqnum"} = SEQNUM_MIN, |
1986
|
|
|
|
|
|
|
$$noid{"$R/gseqnum_date"} = $qdate, |
1987
|
|
|
|
|
|
|
1 or |
1988
|
|
|
|
|
|
|
$seqnum = $$noid{"$R/gseqnum"}), |
1989
|
|
|
|
|
|
|
1 or |
1990
|
|
|
|
|
|
|
$qdate = "00000000000000", # this needs to be 14 zeroes |
1991
|
|
|
|
|
|
|
1; |
1992
|
|
|
|
|
|
|
|
1993
|
|
|
|
|
|
|
my $iderror = ""; |
1994
|
|
|
|
|
|
|
$$noid{"$R/genonly"} and |
1995
|
|
|
|
|
|
|
($iderror = validate($noid, "-", @ids)) !~ /error:/ and |
1996
|
|
|
|
|
|
|
$iderror = ""; |
1997
|
|
|
|
|
|
|
$iderror and |
1998
|
|
|
|
|
|
|
addmsg($noid, "error: queue operation not started -- one or " |
1999
|
|
|
|
|
|
|
. "more ids did not validate:\n$iderror"), |
2000
|
|
|
|
|
|
|
return(()); |
2001
|
|
|
|
|
|
|
my $firstpart = $$noid{"$R/firstpart"}; |
2002
|
|
|
|
|
|
|
my $padwidth = $$noid{"$R/padwidth"}; |
2003
|
|
|
|
|
|
|
my $currdate = temper(); |
2004
|
|
|
|
|
|
|
my (@retvals, $m, $idval, $paddedid, $circ_svec); |
2005
|
|
|
|
|
|
|
for my $id (@ids) { |
2006
|
|
|
|
|
|
|
exists($$noid{"$id\t$R/h"}) and # if there's a hold |
2007
|
|
|
|
|
|
|
$m = qq@error: a hold has been set for "$id" and @ |
2008
|
|
|
|
|
|
|
. "must be released before the identifier can " |
2009
|
|
|
|
|
|
|
. "be queued for minting.", |
2010
|
|
|
|
|
|
|
logmsg($noid, $m), |
2011
|
|
|
|
|
|
|
push(@retvals, $m), |
2012
|
|
|
|
|
|
|
next |
2013
|
|
|
|
|
|
|
; |
2014
|
|
|
|
|
|
|
|
2015
|
|
|
|
|
|
|
# If there's no circulation record, it means that it was |
2016
|
|
|
|
|
|
|
# queued to get it minted earlier or later than it would |
2017
|
|
|
|
|
|
|
# normally be minted. Log if term is "long". |
2018
|
|
|
|
|
|
|
# |
2019
|
|
|
|
|
|
|
$circ_svec = get_circ_svec($noid, $id); |
2020
|
|
|
|
|
|
|
|
2021
|
|
|
|
|
|
|
$circ_svec =~ /^q/ && ! $delete and |
2022
|
|
|
|
|
|
|
$m = "error: id $id cannot be queued since " |
2023
|
|
|
|
|
|
|
. "it appears to be in the queue already -- " |
2024
|
|
|
|
|
|
|
. "circ record is " . $$noid{"$id\t$R/c"}, |
2025
|
|
|
|
|
|
|
logmsg($noid, $m), |
2026
|
|
|
|
|
|
|
push(@retvals, $m), |
2027
|
|
|
|
|
|
|
next |
2028
|
|
|
|
|
|
|
; |
2029
|
|
|
|
|
|
|
$circ_svec =~ /^u/ && $delete and |
2030
|
|
|
|
|
|
|
$m = "error: id $id has been unqueued already -- " |
2031
|
|
|
|
|
|
|
. "circ record is " . $$noid{"$id\t$R/c"}, |
2032
|
|
|
|
|
|
|
logmsg($noid, $m), |
2033
|
|
|
|
|
|
|
push(@retvals, $m), |
2034
|
|
|
|
|
|
|
next |
2035
|
|
|
|
|
|
|
; |
2036
|
|
|
|
|
|
|
$circ_svec !~ /^q/ && $delete and |
2037
|
|
|
|
|
|
|
$m = "error: id $id cannot be unqueued since its circ " |
2038
|
|
|
|
|
|
|
. "record does not indicate its being queued, " |
2039
|
|
|
|
|
|
|
. "circ record is " . $$noid{"$id\t$R/c"}, |
2040
|
|
|
|
|
|
|
logmsg($noid, $m), |
2041
|
|
|
|
|
|
|
push(@retvals, $m), |
2042
|
|
|
|
|
|
|
next |
2043
|
|
|
|
|
|
|
; |
2044
|
|
|
|
|
|
|
# If we get here and we're deleting, circ_svec must be 'q'. |
2045
|
|
|
|
|
|
|
|
2046
|
|
|
|
|
|
|
$circ_svec eq "" and |
2047
|
|
|
|
|
|
|
($$noid{"$R/longterm"} && logmsg($noid, "note: " |
2048
|
|
|
|
|
|
|
. "id $id being queued before first " |
2049
|
|
|
|
|
|
|
. "minting (to be pre-cycled)")), |
2050
|
|
|
|
|
|
|
1 or |
2051
|
|
|
|
|
|
|
$circ_svec =~ /^i/ and |
2052
|
|
|
|
|
|
|
($$noid{"$R/longterm"} && logmsg($noid, "note: " |
2053
|
|
|
|
|
|
|
. "longterm id $id being queued for re-issue")) |
2054
|
|
|
|
|
|
|
; |
2055
|
|
|
|
|
|
|
|
2056
|
|
|
|
|
|
|
# yyy ignore return OK? |
2057
|
|
|
|
|
|
|
set_circ_rec($noid, $id, |
2058
|
|
|
|
|
|
|
($delete ? 'u' : 'q') . $circ_svec, |
2059
|
|
|
|
|
|
|
$currdate, $contact); |
2060
|
|
|
|
|
|
|
|
2061
|
|
|
|
|
|
|
($idval = $id) =~ s/^$firstpart//; |
2062
|
|
|
|
|
|
|
$paddedid = sprintf("%0$padwidth" . "s", $idval); |
2063
|
|
|
|
|
|
|
$fixsqn = sprintf("%06d", $seqnum % SEQNUM_MAX); |
2064
|
|
|
|
|
|
|
|
2065
|
|
|
|
|
|
|
dblock(); |
2066
|
|
|
|
|
|
|
|
2067
|
|
|
|
|
|
|
$$noid{"$R/queued"}++; |
2068
|
|
|
|
|
|
|
if ($$noid{"$R/total"} != NOLIMIT # if total is non-zero |
2069
|
|
|
|
|
|
|
&& $$noid{"$R/queued"} > $$noid{"$R/oatop"}) { |
2070
|
|
|
|
|
|
|
|
2071
|
|
|
|
|
|
|
dbunlock(); |
2072
|
|
|
|
|
|
|
|
2073
|
|
|
|
|
|
|
$m = "error: queue count (" . $$noid{"$R/queued"} |
2074
|
|
|
|
|
|
|
. ") exceeding total possible on id $id. " |
2075
|
|
|
|
|
|
|
. "Queue operation aborted."; |
2076
|
|
|
|
|
|
|
logmsg($noid, $m); |
2077
|
|
|
|
|
|
|
push @retvals, $m; |
2078
|
|
|
|
|
|
|
last; |
2079
|
|
|
|
|
|
|
} |
2080
|
|
|
|
|
|
|
$$noid{"$R/q/$qdate/$fixsqn/$paddedid"} = $id; |
2081
|
|
|
|
|
|
|
|
2082
|
|
|
|
|
|
|
dbunlock(); |
2083
|
|
|
|
|
|
|
|
2084
|
|
|
|
|
|
|
$$noid{"$R/longterm"} and |
2085
|
|
|
|
|
|
|
logmsg($noid, "id: " |
2086
|
|
|
|
|
|
|
. $$noid{"$R/q/$qdate/$fixsqn/$paddedid"} |
2087
|
|
|
|
|
|
|
. " added to queue under " |
2088
|
|
|
|
|
|
|
. "$R/q/$qdate/$seqnum/$paddedid"); |
2089
|
|
|
|
|
|
|
push @retvals, "id: $id"; |
2090
|
|
|
|
|
|
|
$seqnum and # it's zero for "lvf" and "delete" |
2091
|
|
|
|
|
|
|
$seqnum++; |
2092
|
|
|
|
|
|
|
} |
2093
|
|
|
|
|
|
|
dblock(); |
2094
|
|
|
|
|
|
|
$when eq "first" and |
2095
|
|
|
|
|
|
|
$$noid{"$R/fseqnum"} = $seqnum, |
2096
|
|
|
|
|
|
|
1 or |
2097
|
|
|
|
|
|
|
$qdate > 0 and |
2098
|
|
|
|
|
|
|
$$noid{"$R/gseqnum"} = $seqnum, |
2099
|
|
|
|
|
|
|
1; |
2100
|
|
|
|
|
|
|
dbunlock(); |
2101
|
|
|
|
|
|
|
return(@retvals); |
2102
|
|
|
|
|
|
|
} |
2103
|
|
|
|
|
|
|
|
2104
|
|
|
|
|
|
|
# Generate a sample id for testing purposes. |
2105
|
|
|
|
|
|
|
sub sample{ my( $noid, $num )=@_; |
2106
|
|
|
|
|
|
|
|
2107
|
|
|
|
|
|
|
my $upper; |
2108
|
|
|
|
|
|
|
! defined($num) and |
2109
|
|
|
|
|
|
|
$upper = $$noid{"$R/total"}, |
2110
|
|
|
|
|
|
|
($upper == NOLIMIT and $upper = 100000), |
2111
|
|
|
|
|
|
|
$num = int(rand($upper)); |
2112
|
|
|
|
|
|
|
my $mask = $$noid{"$R/mask"}; |
2113
|
|
|
|
|
|
|
my $firstpart = $$noid{"$R/firstpart"}; |
2114
|
|
|
|
|
|
|
my $func = ($$noid{"$R/addcheckchar"} ? \&checkchar : \&echo); |
2115
|
|
|
|
|
|
|
return &$func($firstpart . n2xdig($num, $mask)); |
2116
|
|
|
|
|
|
|
} |
2117
|
|
|
|
|
|
|
|
2118
|
|
|
|
|
|
|
sub scope { my( $noid )=@_; |
2119
|
|
|
|
|
|
|
|
2120
|
|
|
|
|
|
|
! $$noid{"$R/template"} and |
2121
|
|
|
|
|
|
|
print("This minter does not generate identifiers, but it\n" |
2122
|
|
|
|
|
|
|
. "does accept user-defined identifier and element " |
2123
|
|
|
|
|
|
|
. "bindings.\n"); |
2124
|
|
|
|
|
|
|
my $total = $$noid{"$R/total"}; |
2125
|
|
|
|
|
|
|
my $totalstr = human_num($total); |
2126
|
|
|
|
|
|
|
my $naan = $$noid{"$R/naan"} || ""; |
2127
|
|
|
|
|
|
|
$naan and |
2128
|
|
|
|
|
|
|
$naan .= "/"; |
2129
|
|
|
|
|
|
|
|
2130
|
|
|
|
|
|
|
my ($prefix, $mask, $gen_type) = |
2131
|
|
|
|
|
|
|
($$noid{"$R/prefix"}, $$noid{"$R/mask"}, $$noid{"$R/generator_type"}); |
2132
|
|
|
|
|
|
|
|
2133
|
|
|
|
|
|
|
print "Template ", $$noid{"$R/template"}, " will yield ", |
2134
|
|
|
|
|
|
|
($total < 0 ? "an unbounded number of" : $totalstr), |
2135
|
|
|
|
|
|
|
" $gen_type unique ids\n"; |
2136
|
|
|
|
|
|
|
my $tminus1 = ($total < 0 ? 987654321 : $total - 1); |
2137
|
|
|
|
|
|
|
|
2138
|
|
|
|
|
|
|
# See if we need to compute a check character. |
2139
|
|
|
|
|
|
|
my $func = ($$noid{"$R/addcheckchar"} ? \&checkchar : \&echo); |
2140
|
|
|
|
|
|
|
print |
2141
|
|
|
|
|
|
|
"in the range " . &$func($naan . &n2xdig( 0, $mask)) . |
2142
|
|
|
|
|
|
|
", " . &$func($naan . &n2xdig( 1, $mask)) . |
2143
|
|
|
|
|
|
|
", " . &$func($naan . &n2xdig( 2, $mask)); |
2144
|
|
|
|
|
|
|
28 < $total - 1 and print |
2145
|
|
|
|
|
|
|
", ..., " . &$func($naan . &n2xdig(28, $mask)); |
2146
|
|
|
|
|
|
|
29 < $total - 1 and print |
2147
|
|
|
|
|
|
|
", " . &$func($naan . &n2xdig(29, $mask)); |
2148
|
|
|
|
|
|
|
print |
2149
|
|
|
|
|
|
|
", ... up to " |
2150
|
|
|
|
|
|
|
. &$func($naan . &n2xdig($tminus1, $mask)) |
2151
|
|
|
|
|
|
|
. ($total < 0 ? " and beyond.\n" : ".\n") |
2152
|
|
|
|
|
|
|
; |
2153
|
|
|
|
|
|
|
$mask !~ /^r/ and |
2154
|
|
|
|
|
|
|
return 1; |
2155
|
|
|
|
|
|
|
print "A sampling of random values (may already be in use): "; |
2156
|
|
|
|
|
|
|
my $i = 5; |
2157
|
|
|
|
|
|
|
print sample($noid) . " " |
2158
|
|
|
|
|
|
|
while ($i-- > 0); |
2159
|
|
|
|
|
|
|
print "\n"; |
2160
|
|
|
|
|
|
|
return 1; |
2161
|
|
|
|
|
|
|
} |
2162
|
|
|
|
|
|
|
|
2163
|
|
|
|
|
|
|
# Return local date/time stamp in TEMPER format. Use supplied time (in seconds) |
2164
|
|
|
|
|
|
|
# if any, or the current time. |
2165
|
|
|
|
|
|
|
# |
2166
|
|
|
|
|
|
|
sub temper { my( $time )=@_; |
2167
|
|
|
|
|
|
|
|
2168
|
|
|
|
|
|
|
my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdat) |
2169
|
|
|
|
|
|
|
= localtime(defined($time) ? $time : time()); |
2170
|
|
|
|
|
|
|
$year += 1900; # add the missing the century |
2171
|
|
|
|
|
|
|
$mon++; # zero-based, so increment |
2172
|
|
|
|
|
|
|
return sprintf("%04.4s%02.2s%02.2s%02.2s%02.2s%02.2s", |
2173
|
|
|
|
|
|
|
$year, $mon, $mday, $hour, $min, $sec); |
2174
|
|
|
|
|
|
|
} |
2175
|
|
|
|
|
|
|
|
2176
|
|
|
|
|
|
|
# Check that identifier matches a given template, where "-" means the |
2177
|
|
|
|
|
|
|
# default template for this generator. This is a complete check of all |
2178
|
|
|
|
|
|
|
# characteristics _except_ whether the identifier is stored in the |
2179
|
|
|
|
|
|
|
# database. |
2180
|
|
|
|
|
|
|
# |
2181
|
|
|
|
|
|
|
# Returns an array of strings that are messages corresponding to any ids |
2182
|
|
|
|
|
|
|
# that were passed in. Error strings # that pertain to identifiers |
2183
|
|
|
|
|
|
|
# begin with "iderr: ". |
2184
|
|
|
|
|
|
|
# |
2185
|
|
|
|
|
|
|
sub validate { my( $noid, $template, @ids )=@_; |
2186
|
|
|
|
|
|
|
|
2187
|
|
|
|
|
|
|
my ($first, $prefix, $mask, $gen_type, $msg); |
2188
|
|
|
|
|
|
|
my @retvals; |
2189
|
|
|
|
|
|
|
|
2190
|
|
|
|
|
|
|
! @ids and |
2191
|
|
|
|
|
|
|
addmsg($noid, "error: must specify a template and at least " |
2192
|
|
|
|
|
|
|
. "one identifier."), |
2193
|
|
|
|
|
|
|
return(()); |
2194
|
|
|
|
|
|
|
! defined($template) and |
2195
|
|
|
|
|
|
|
# If $noid is undefined, the caller looks in errmsg(undef). |
2196
|
|
|
|
|
|
|
addmsg($noid, "error: no template given to validate against."), |
2197
|
|
|
|
|
|
|
return(()); |
2198
|
|
|
|
|
|
|
|
2199
|
|
|
|
|
|
|
if ($template eq "-") { |
2200
|
|
|
|
|
|
|
($prefix, $mask) = ($$noid{"$R/prefix"}, $$noid{"$R/mask"}); |
2201
|
|
|
|
|
|
|
# push(@retvals, "template: " . $$noid{"$R/template"}); |
2202
|
|
|
|
|
|
|
if (! $$noid{"$R/template"}) { # do blanket validation |
2203
|
|
|
|
|
|
|
my @nonulls = grep(s/^(.)/id: $1/, @ids); |
2204
|
|
|
|
|
|
|
! @nonulls and |
2205
|
|
|
|
|
|
|
return(()); |
2206
|
|
|
|
|
|
|
push(@retvals, @nonulls); |
2207
|
|
|
|
|
|
|
return(@retvals); |
2208
|
|
|
|
|
|
|
} |
2209
|
|
|
|
|
|
|
} |
2210
|
|
|
|
|
|
|
elsif (! parse_template($template, $prefix, $mask, $gen_type, $msg)) { |
2211
|
|
|
|
|
|
|
addmsg($noid, "error: template $template bad: $msg"); |
2212
|
|
|
|
|
|
|
return(()); |
2213
|
|
|
|
|
|
|
} |
2214
|
|
|
|
|
|
|
|
2215
|
|
|
|
|
|
|
my ($id, @maskchars, $c, $m, $varpart); |
2216
|
|
|
|
|
|
|
my $should_have_checkchar = (($m = $mask) =~ s/k$//); |
2217
|
|
|
|
|
|
|
my $naan = $$noid{"$R/naan"}; |
2218
|
|
|
|
|
|
|
ID: for $id (@ids) { |
2219
|
|
|
|
|
|
|
! defined($id) || $id =~ /^\s*$/ and |
2220
|
|
|
|
|
|
|
push(@retvals, |
2221
|
|
|
|
|
|
|
"iderr: can't validate an empty identifier"), |
2222
|
|
|
|
|
|
|
next; |
2223
|
|
|
|
|
|
|
|
2224
|
|
|
|
|
|
|
# Automatically reject ids starting with "$R/", unless it's an |
2225
|
|
|
|
|
|
|
# "idmap", in which case automatically validate. For an idmap, |
2226
|
|
|
|
|
|
|
# the $id should be of the form $R/idmap/ElementName, with |
2227
|
|
|
|
|
|
|
# element, Idpattern, and value, ReplacementPattern. |
2228
|
|
|
|
|
|
|
# |
2229
|
|
|
|
|
|
|
$id =~ m|^$R/| and |
2230
|
|
|
|
|
|
|
push(@retvals, ($id =~ m|^$R/idmap/.+| |
2231
|
|
|
|
|
|
|
? "id: $id" |
2232
|
|
|
|
|
|
|
: "iderr: identifiers must not start" |
2233
|
|
|
|
|
|
|
. qq@ with "$R/".@)), |
2234
|
|
|
|
|
|
|
next; |
2235
|
|
|
|
|
|
|
|
2236
|
|
|
|
|
|
|
$first = $naan; # ... if any |
2237
|
|
|
|
|
|
|
$first and |
2238
|
|
|
|
|
|
|
$first .= "/"; |
2239
|
|
|
|
|
|
|
$first .= $prefix; # ... if any |
2240
|
|
|
|
|
|
|
($varpart = $id) !~ s/^$first// and |
2241
|
|
|
|
|
|
|
#yyy ($varpart = $id) !~ s/^$prefix// and |
2242
|
|
|
|
|
|
|
push(@retvals, "iderr: $id should begin with $first."), |
2243
|
|
|
|
|
|
|
next; |
2244
|
|
|
|
|
|
|
# yyy this checkchar algorithm will need an arg when we |
2245
|
|
|
|
|
|
|
# expand into other alphabets |
2246
|
|
|
|
|
|
|
$should_have_checkchar && ! checkchar($id) and |
2247
|
|
|
|
|
|
|
push(@retvals, "iderr: $id has a check character error"), |
2248
|
|
|
|
|
|
|
next; |
2249
|
|
|
|
|
|
|
## xxx fix so that a length problem is reported before (or |
2250
|
|
|
|
|
|
|
# in addition to) a check char problem |
2251
|
|
|
|
|
|
|
|
2252
|
|
|
|
|
|
|
# yyy needed? |
2253
|
|
|
|
|
|
|
#length($first) + length($mask) - 1 != length($id) |
2254
|
|
|
|
|
|
|
# and push(@retvals, |
2255
|
|
|
|
|
|
|
# "error: $id has should have length " |
2256
|
|
|
|
|
|
|
# . (length($first) + length($mask) - 1) |
2257
|
|
|
|
|
|
|
# and next; |
2258
|
|
|
|
|
|
|
|
2259
|
|
|
|
|
|
|
# Maskchar-by-Idchar checking. |
2260
|
|
|
|
|
|
|
# |
2261
|
|
|
|
|
|
|
@maskchars = split(//, $mask); |
2262
|
|
|
|
|
|
|
shift @maskchars; # toss 'r', 's', or 'z' |
2263
|
|
|
|
|
|
|
for $c (split(//, $varpart)) { |
2264
|
|
|
|
|
|
|
! defined($m = shift @maskchars) and |
2265
|
|
|
|
|
|
|
push(@retvals, "iderr: $id longer than " |
2266
|
|
|
|
|
|
|
. "specified template ($template)"), |
2267
|
|
|
|
|
|
|
next ID; |
2268
|
|
|
|
|
|
|
$m =~ /e/ && $legalstring !~ /$c/ and |
2269
|
|
|
|
|
|
|
push(@retvals, "iderr: $id char '$c' conflicts" |
2270
|
|
|
|
|
|
|
. " with template ($template)" |
2271
|
|
|
|
|
|
|
. " char '$m' (extended digit)"), |
2272
|
|
|
|
|
|
|
next ID |
2273
|
|
|
|
|
|
|
or |
2274
|
|
|
|
|
|
|
$m =~ /d/ && '0123456789' !~ /$c/ and |
2275
|
|
|
|
|
|
|
push(@retvals, "iderr: $id char '$c' conflicts" |
2276
|
|
|
|
|
|
|
. " with template ($template)" |
2277
|
|
|
|
|
|
|
. " char '$m' (digit)"), |
2278
|
|
|
|
|
|
|
next ID |
2279
|
|
|
|
|
|
|
; # or $m =~ /k/, in which case skip |
2280
|
|
|
|
|
|
|
} |
2281
|
|
|
|
|
|
|
defined($m = shift @maskchars) and |
2282
|
|
|
|
|
|
|
push(@retvals, "iderr: $id shorter " |
2283
|
|
|
|
|
|
|
. "than specified template ($template)"), |
2284
|
|
|
|
|
|
|
next ID; |
2285
|
|
|
|
|
|
|
|
2286
|
|
|
|
|
|
|
# If we get here, the identifier checks out. |
2287
|
|
|
|
|
|
|
push(@retvals, "id: $id"); |
2288
|
|
|
|
|
|
|
} |
2289
|
|
|
|
|
|
|
return(@retvals); |
2290
|
|
|
|
|
|
|
} |
2291
|
|
|
|
|
|
|
|
2292
|
|
|
|
|
|
|
1; |
2293
|
|
|
|
|
|
|
|
2294
|
|
|
|
|
|
|
__END__ |