2 # Copyright (C) 2001 Canon Research Centre Europe (CRE).
3 # Copyright (C) 2002-2009 Neil Bowers
4 # Copyright (c) 2010-2014 Sullivan Beck
5 # This program is free software; you can redistribute it and/or modify it
6 # under the same terms as Perl itself.
13 use Locale::Codes::Constants;
15 #=======================================================================
16 # Public Global Variables
17 #=======================================================================
19 # This module is not called directly... %Data is filled in by the
22 our($VERSION,%Data,%Retired);
24 # $Data{ TYPE }{ code2id }{ CODESET } { CODE } = [ ID, I ]
25 # { id2code }{ CODESET } { ID } = CODE
26 # { id2names }{ ID } = [ NAME, NAME, ... ]
27 # { alias2id }{ NAME } = [ ID, I ]
28 # { id } = FIRST_UNUSED_ID
29 # { codealias }{ CODESET } { ALIAS } = CODE
31 # $Retired{ TYPE }{ CODESET }{ code }{ CODE } = NAME
32 # { name }{ NAME } = [CODE,NAME] (the key is lowercase)
36 #=======================================================================
38 # _code ( TYPE,CODE,CODESET )
40 #=======================================================================
45 my($type,$code,$codeset) = @_;
46 $code = '' if (! $code);
48 # Determine the codeset
50 $codeset = $ALL_CODESETS{$type}{'default'}
51 if (! defined($codeset) || $codeset eq '');
52 $codeset = lc($codeset);
53 return 1 if (! exists $ALL_CODESETS{$type}{'codesets'}{$codeset});
54 return (0,$code,$codeset) if (! $code);
56 # Determine the properties of the codeset
58 my($op,@args) = @{ $ALL_CODESETS{$type}{'codesets'}{$codeset} };
63 } elsif ($op eq 'uc') {
66 } elsif ($op eq 'ucfirst') {
67 $code = ucfirst(lc($code));
69 } elsif ($op eq 'numeric') {
70 return (1) unless ($code =~ /^\d+$/);
72 $code = sprintf("%.${l}d", $code);
75 return (0,$code,$codeset);
78 #=======================================================================
80 # _code2name ( TYPE,CODE [,CODESET] [,'retired'] )
82 #=======================================================================
87 if (@args > 0 && $args[$#args] && $args[$#args] eq 'retired') {
92 my($err,$code,$codeset) = _code($type,@args);
93 return undef if ($err ||
96 $code = $Data{$type}{'codealias'}{$codeset}{$code}
97 if (exists $Data{$type}{'codealias'}{$codeset}{$code});
99 if (exists $Data{$type}{'code2id'}{$codeset} &&
100 exists $Data{$type}{'code2id'}{$codeset}{$code}) {
101 my ($id,$i) = @{ $Data{$type}{'code2id'}{$codeset}{$code} };
102 my $name = $Data{$type}{'id2names'}{$id}[$i];
105 } elsif ($retired && exists $Retired{$type}{$codeset}{'code'}{$code}) {
106 return $Retired{$type}{$codeset}{'code'}{$code};
113 #=======================================================================
115 # _name2code ( TYPE,NAME [,CODESET] [,'retired'] )
117 #=======================================================================
120 my($type,$name,@args) = @_;
121 return undef if (! $name);
125 if (@args > 0 && $args[$#args] && $args[$#args] eq 'retired') {
130 my($err,$tmp,$codeset) = _code($type,'',@args);
131 return undef if ($err);
133 if (exists $Data{$type}{'alias2id'}{$name}) {
134 my $id = $Data{$type}{'alias2id'}{$name}[0];
135 if (exists $Data{$type}{'id2code'}{$codeset}{$id}) {
136 return $Data{$type}{'id2code'}{$codeset}{$id};
139 } elsif ($retired && exists $Retired{$type}{$codeset}{'name'}{$name}) {
140 return $Retired{$type}{$codeset}{'name'}{$name}[0];
146 #=======================================================================
148 # _code2code ( TYPE,CODE,CODESET )
150 #=======================================================================
153 my($type,@args) = @_;
154 (@args == 3) or croak "${type}_code2code() takes 3 arguments!";
156 my($code,$inset,$outset) = @args;
158 ($err,$code,$inset) = _code($type,$code,$inset);
159 return undef if ($err);
160 ($err,$tmp,$outset) = _code($type,'',$outset);
161 return undef if ($err);
163 my $name = _code2name($type,$code,$inset);
164 my $outcode = _name2code($type,$name,$outset);
168 #=======================================================================
170 # _all_codes ( TYPE [,CODESET] [,'retired'] )
172 #=======================================================================
175 my($type,@args) = @_;
177 if (@args > 0 && $args[$#args] && $args[$#args] eq 'retired') {
182 my ($err,$tmp,$codeset) = _code($type,'',@args);
185 if (! exists $Data{$type}{'code2id'}{$codeset}) {
188 my @codes = keys %{ $Data{$type}{'code2id'}{$codeset} };
189 push(@codes,keys %{ $Retired{$type}{$codeset}{'code'} }) if ($retired);
190 return (sort @codes);
193 #=======================================================================
195 # _all_names ( TYPE [,CODESET] [,'retired'] )
197 #=======================================================================
200 my($type,@args) = @_;
202 if (@args > 0 && $args[$#args] && $args[$#args] eq 'retired') {
207 my ($err,$tmp,$codeset) = _code($type,'',@args);
210 my @codes = _all_codes($type,$codeset);
213 foreach my $code (@codes) {
214 my($id,$i) = @{ $Data{$type}{'code2id'}{$codeset}{$code} };
215 my $name = $Data{$type}{'id2names'}{$id}[$i];
219 foreach my $lc (keys %{ $Retired{$type}{$codeset}{'name'} }) {
220 my $name = $Retired{$type}{$codeset}{'name'}{$lc}[1];
224 return (sort @names);
227 #=======================================================================
229 # _rename ( TYPE,CODE,NAME,CODESET )
231 # Change the official name for a code. The original is retained
232 # as an alias, but the new name will be returned if you lookup the
235 #=======================================================================
238 my($type,$code,$new_name,@args) = @_;
241 $nowarn = 1, pop(@args) if (@args && $args[$#args] eq "nowarn");
243 my $codeset = shift(@args);
245 ($err,$code,$codeset) = _code($type,$code,$codeset);
248 carp "rename_$type(): unknown codeset\n" unless ($nowarn);
252 $code = $Data{$type}{'codealias'}{$codeset}{$code}
253 if (exists $Data{$type}{'codealias'}{$codeset}{$code});
255 # Check that $code exists in the codeset.
258 if (exists $Data{$type}{'code2id'}{$codeset}{$code}) {
259 $id = $Data{$type}{'code2id'}{$codeset}{$code}[0];
261 carp "rename_$type(): unknown code: $code\n" unless ($nowarn);
266 # 1. Renaming to a name which exists with a different ID
269 # 2. Renaming to a name which exists with the same ID
270 # Just change code2id (I value)
272 # 3. Renaming to a new name
274 # Change code2id (I value)
276 if (exists $Data{$type}{'alias2id'}{lc($new_name)}) {
277 # Existing name (case 1 and 2)
279 my ($new_id,$i) = @{ $Data{$type}{'alias2id'}{lc($new_name)} };
280 if ($new_id != $id) {
282 carp "rename_$type(): rename to an existing $type not allowed\n"
289 $Data{$type}{'code2id'}{$codeset}{$code}[1] = $i;
295 push @{ $Data{$type}{'id2names'}{$id} },$new_name;
296 my $i = $#{ $Data{$type}{'id2names'}{$id} };
297 $Data{$type}{'alias2id'}{lc($new_name)} = [ $id,$i ];
298 $Data{$type}{'code2id'}{$codeset}{$code}[1] = $i;
304 #=======================================================================
306 # _add_code ( TYPE,CODE,NAME,CODESET )
308 # Add a new code to the codeset. Both CODE and NAME must be
309 # unused in the code set.
311 #=======================================================================
314 my($type,$code,$name,@args) = @_;
317 $nowarn = 1, pop(@args) if (@args && $args[$#args] eq "nowarn");
319 my $codeset = shift(@args);
321 ($err,$code,$codeset) = _code($type,$code,$codeset);
324 carp "add_$type(): unknown codeset\n" unless ($nowarn);
328 # Check that $code is unused.
330 if (exists $Data{$type}{'code2id'}{$codeset}{$code} ||
331 exists $Data{$type}{'codealias'}{$codeset}{$code}) {
332 carp "add_$type(): code already in use: $code\n" unless ($nowarn);
336 # Check to see that $name is unused in this code set. If it is
337 # used (but not in this code set), we'll use that ID. Otherwise,
338 # we'll need to get the next available ID.
341 if (exists $Data{$type}{'alias2id'}{lc($name)}) {
342 ($id,$i) = @{ $Data{$type}{'alias2id'}{lc($name)} };
343 if (exists $Data{$type}{'id2code'}{$codeset}{$id}) {
344 carp "add_$type(): name already in use: $name\n" unless ($nowarn);
349 $id = $Data{$type}{'id'}++;
351 $Data{$type}{'alias2id'}{lc($name)} = [ $id,$i ];
352 $Data{$type}{'id2names'}{$id} = [ $name ];
357 $Data{$type}{'code2id'}{$codeset}{$code} = [ $id,$i ];
358 $Data{$type}{'id2code'}{$codeset}{$id} = $code;
363 #=======================================================================
365 # _delete_code ( TYPE,CODE,CODESET )
367 # Delete a code from the codeset.
369 #=======================================================================
372 my($type,$code,@args) = @_;
375 $nowarn = 1, pop(@args) if (@args && $args[$#args] eq "nowarn");
377 my $codeset = shift(@args);
379 ($err,$code,$codeset) = _code($type,$code,$codeset);
382 carp "delete_$type(): unknown codeset\n" unless ($nowarn);
386 $code = $Data{$type}{'codealias'}{$codeset}{$code}
387 if (exists $Data{$type}{'codealias'}{$codeset}{$code});
389 # Check that $code is valid.
391 if (! exists $Data{$type}{'code2id'}{$codeset}{$code}) {
392 carp "delete_$type(): code does not exist: $code\n" unless ($nowarn);
398 my $id = $Data{$type}{'code2id'}{$codeset}{$code}[0];
399 delete $Data{$type}{'code2id'}{$codeset}{$code};
400 delete $Data{$type}{'id2code'}{$codeset}{$id};
402 # Delete any aliases that are linked to this code
404 foreach my $alias (keys %{ $Data{$type}{'codealias'}{$codeset} }) {
405 next if ($Data{$type}{'codealias'}{$codeset}{$alias} ne $code);
406 delete $Data{$type}{'codealias'}{$codeset}{$alias};
409 # If this ID is not used in any other codeset, delete it completely.
411 foreach my $c (keys %{ $Data{$type}{'id2code'} }) {
412 return 1 if (exists $Data{$type}{'id2code'}{$c}{$id});
415 my @names = @{ $Data{$type}{'id2names'}{$id} };
416 delete $Data{$type}{'id2names'}{$id};
418 foreach my $name (@names) {
419 delete $Data{$type}{'alias2id'}{lc($name)};
425 #=======================================================================
427 # _add_alias ( TYPE,NAME,NEW_NAME )
429 # Add a new alias. NAME must exist, and NEW_NAME must be unused.
431 #=======================================================================
434 my($type,$name,$new_name,$nowarn) = @_;
436 $nowarn = (defined($nowarn) && $nowarn eq "nowarn" ? 1 : 0);
438 # Check that $name is used and $new_name is new.
441 if (exists $Data{$type}{'alias2id'}{lc($name)}) {
442 $id = $Data{$type}{'alias2id'}{lc($name)}[0];
444 carp "add_${type}_alias(): name does not exist: $name\n" unless ($nowarn);
448 if (exists $Data{$type}{'alias2id'}{lc($new_name)}) {
449 carp "add_${type}_alias(): alias already in use: $new_name\n" unless ($nowarn);
455 push @{ $Data{$type}{'id2names'}{$id} },$new_name;
456 my $i = $#{ $Data{$type}{'id2names'}{$id} };
457 $Data{$type}{'alias2id'}{lc($new_name)} = [ $id,$i ];
462 #=======================================================================
464 # _delete_alias ( TYPE,NAME )
466 # This deletes a name from the list of names used by an element.
467 # NAME must be used, but must NOT be the only name in the list.
469 # Any id2name that references this name will be changed to
470 # refer to the first name in the list.
472 #=======================================================================
475 my($type,$name,$nowarn) = @_;
477 $nowarn = (defined($nowarn) && $nowarn eq "nowarn" ? 1 : 0);
479 # Check that $name is used.
482 if (exists $Data{$type}{'alias2id'}{lc($name)}) {
483 ($id,$i) = @{ $Data{$type}{'alias2id'}{lc($name)} };
485 carp "delete_${type}_alias(): name does not exist: $name\n" unless ($nowarn);
489 my $n = $#{ $Data{$type}{'id2names'}{$id} } + 1;
491 carp "delete_${type}_alias(): only one name defined (use _delete_${type} instead)\n"
498 splice (@{ $Data{$type}{'id2names'}{$id} },$i,1);
499 delete $Data{$type}{'alias2id'}{lc($name)};
501 # Every element that refers to this ID:
504 # Decrement if I > $i
506 foreach my $codeset (keys %{ $Data{'code2id'} }) {
507 foreach my $code (keys %{ $Data{'code2id'}{$codeset} }) {
508 my($jd,$j) = @{ $Data{'code2id'}{$codeset}{$code} };
509 next if ($jd ne $id ||
512 $Data{'code2id'}{$codeset}{$code}[1] = 0;
514 $Data{'code2id'}{$codeset}{$code}[1]--;
522 #=======================================================================
524 # _rename_code ( TYPE,CODE,NEW_CODE,CODESET )
526 # Change the official code. The original is retained as an alias, but
527 # the new name will be returned if you lookup the code from name.
529 #=======================================================================
532 my($type,$code,$new_code,@args) = @_;
535 $nowarn = 1, pop(@args) if (@args && $args[$#args] eq "nowarn");
537 my $codeset = shift(@args);
539 ($err,$code,$codeset) = _code($type,$code,$codeset);
540 ($err,$new_code,$codeset) = _code($type,$new_code,$codeset)
544 carp "rename_$type(): unknown codeset\n" unless ($nowarn);
548 $code = $Data{$type}{'codealias'}{$codeset}{$code}
549 if (exists $Data{$type}{'codealias'}{$codeset}{$code});
551 # Check that $code exists in the codeset.
553 if (! exists $Data{$type}{'code2id'}{$codeset}{$code}) {
554 carp "rename_$type(): unknown code: $code\n" unless ($nowarn);
559 # 1. Renaming code to an existing alias of this code:
560 # Make the alias real and the code an alias
562 # 2. Renaming code to some other existing alias:
565 # 3. Renaming code to some other code:
568 # 4. Renaming code to a new code:
569 # Make code into an alias
570 # Replace code with new_code.
572 if (exists $Data{$type}{'codealias'}{$codeset}{$new_code}) {
574 if ($Data{$type}{'codealias'}{$codeset}{$new_code} eq $code) {
577 delete $Data{$type}{'codealias'}{$codeset}{$new_code};
581 carp "rename_$type(): new code already in use: $new_code\n" unless ($nowarn);
585 } elsif (exists $Data{$type}{'code2id'}{$codeset}{$new_code}) {
587 carp "rename_$type(): new code already in use: $new_code\n" unless ($nowarn);
593 $Data{$type}{'codealias'}{$codeset}{$code} = $new_code;
595 my $id = $Data{$type}{'code2id'}{$codeset}{$code}[0];
596 $Data{$type}{'code2id'}{$codeset}{$new_code} = $Data{$type}{'code2id'}{$codeset}{$code};
597 delete $Data{$type}{'code2id'}{$codeset}{$code};
599 $Data{$type}{'id2code'}{$codeset}{$id} = $new_code;
604 #=======================================================================
606 # _add_code_alias ( TYPE,CODE,NEW_CODE,CODESET )
608 # Adds an alias for the code.
610 #=======================================================================
612 sub _add_code_alias {
613 my($type,$code,$new_code,@args) = @_;
616 $nowarn = 1, pop(@args) if (@args && $args[$#args] eq "nowarn");
618 my $codeset = shift(@args);
620 ($err,$code,$codeset) = _code($type,$code,$codeset);
621 ($err,$new_code,$codeset) = _code($type,$new_code,$codeset)
625 carp "add_${type}_code_alias(): unknown codeset\n" unless ($nowarn);
629 $code = $Data{$type}{'codealias'}{$codeset}{$code}
630 if (exists $Data{$type}{'codealias'}{$codeset}{$code});
632 # Check that $code exists in the codeset and that $new_code
635 if (! exists $Data{$type}{'code2id'}{$codeset}{$code}) {
636 carp "add_${type}_code_alias(): unknown code: $code\n" unless ($nowarn);
640 if (exists $Data{$type}{'code2id'}{$codeset}{$new_code} ||
641 exists $Data{$type}{'codealias'}{$codeset}{$new_code}) {
642 carp "add_${type}_code_alias(): code already in use: $new_code\n" unless ($nowarn);
648 $Data{$type}{'codealias'}{$codeset}{$new_code} = $code;
653 #=======================================================================
655 # _delete_code_alias ( TYPE,CODE,CODESET )
657 # Deletes an alias for the code.
659 #=======================================================================
661 sub _delete_code_alias {
662 my($type,$code,@args) = @_;
665 $nowarn = 1, pop(@args) if (@args && $args[$#args] eq "nowarn");
667 my $codeset = shift(@args);
669 ($err,$code,$codeset) = Locale::Codes::_code($type,$code,$codeset);
672 carp "delete_${type}_code_alias(): unknown codeset\n" unless ($nowarn);
676 # Check that $code exists in the codeset as an alias.
678 if (! exists $Data{$type}{'codealias'}{$codeset}{$code}) {
679 carp "delete_${type}_code_alias(): no alias defined: $code\n" unless ($nowarn);
685 delete $Data{$type}{'codealias'}{$codeset}{$code};
693 # indent-tabs-mode: nil
694 # cperl-indent-level: 3
695 # cperl-continued-statement-offset: 2
696 # cperl-continued-brace-offset: 0
697 # cperl-brace-offset: 0
698 # cperl-brace-imaginary-offset: 0
699 # cperl-label-offset: -2