#! /usr/bin/perl -w ## ## This routine should be enhanced locally: ## sub uname_not_taken{ my ($uname) = @_; unless (%uname_db) { my @a; setpwent; while (@a = getpwent) { $uname_db{$a[0]} = 1; } endpwent; } return ! $uname_db{$uname}; } ## ## Remove special characters from name. ## sub conv_name{ my ($s) = @_; $s =~ s/[Ææ]/ae/g; # Special mapping of æ and å, $s =~ s/[Åå]/aa/g; # more palatable for Norwegians. $s =~ s/Ð/Dh/g; $s =~ s/ð/dh/g; $s =~ s/Þ/Th/g; $s =~ s/þ/th/g; $s =~ s/ß/ss/g; # Remove accents. Convert from ISO 646-60 to ISO 8859-1 ([\]{|}). # Also some æøå variants from the Macintosh character set. $s =~ tr (ÆØÅæø¿åÀÁÂÃÄÇÈÉÊËÌÍÎÏÑÒÓÔÕÖÙÚÛÜÝàáâãäçèéêëìíîïñòóôõöùúûüý{[}]|¦\\) (AOAaooaAAAAACEEEEIIIINOOOOOUUUUYaaaaaceeeeiiiinooooouuuuyaAaAooO); $s =~ s/[\200-\377]/x/g; $s =~ tr/A-Z/a-z/; return $s; } ## ## The main routine ## ## $name is the full name of the user, e.g., "Richard Mulhouse Nixon". ## $goal is the minimum number of alternatives to find. sub user_suggest_unames { my ($name, $goal) = @_; my ($fname, $initial, $lname); my $firstinit; my @potuname = (); $name = &conv_name ($name); # Remember just the first initials. if ($name =~ /^(.*)[ -]+(\S+)\s+(\S+)$/) { # at least three names $firstinit = $1; $firstinit =~ s/([- ])(\S)[^- ]*/$1$2/g; $firstinit =~ s/^(\S).*?($|[- ])/$1/; $firstinit =~ s/[- ]//g; } # Remove hyphens. People called "Geir-Ove Johnsen Hansen" generally # prefer "geirove" to just "geir". $name =~ s/-//g; if ($name =~ /^(\S+)(.*\s+(\S)\S*)?\s+(\S+)$/) { $fname = substr ($1, 0, 8); $initial = $3; $lname = substr ($4, 0, 8); } else { # Only one name. The results are suboptimal $fname = $name; $initial = undef; $lname = $name; } # print "DEBUG: split into '$fname' '$initial' '$lname'\n"; # First try the obvious, first and last name. DISABLED! Should # be restricted to interactively created accounts (== usually # non-student). if (0) { push (@potuname, $fname) if &uname_not_taken($fname); push (@potuname, $lname) if &uname_not_taken($lname); } # For people with many names, we prefer to use all initials: # Example: Geir-Ove Johnsen Hansen # ffff fff i llllll # Here, firstinit is "GO" and initial is "J". # # gohansen gojhanse gohanse gojhans ... gojh goh # ssllllll ssilllll sslllll ssillll ssil ssl # # ("ss" means firstinit, "i" means initial, "l" means last name) my ($i, $j, $try); if ($firstinit && length ($firstinit) > 1) { $i = length ($firstinit); my $llen = length ($lname); $llen = 8 - $i if ($llen > 8 - $i); for ($j = $llen; $j > 0; $j--) { $try = $firstinit . substr ($lname, 0, $j); push (@potuname, $try) if &uname_not_taken($try); if ($j > 1 && $initial) { $try = $firstinit . $initial . substr ($lname, 0, $j-1); push (@potuname, $try) if &uname_not_taken($try); } last if @potuname >= $goal; } } # Now try different substrings from first and last name. # # geiroveh, # fffffffl # geirovh geirovha geirovjh, # ffffffl ffffffll ffffffil # geiroh geirojh geiroha geirojha geirohan, # fffffl fffffil fffffll fffffill ffffflll # geirh geirjh geirha geirjha geirhan geirjhan geirhans # ffffl ffffil ffffll ffffill fffflll ffffilll ffffllll # ... # gjh gh gjha gha gjhan ghan ... gjhansen ghansen # fil fl fill fll filll flll fillllll fllllll my $flen = length ($fname); $flen = 7 if $flen > 7; for ($i = $flen; $i > 0; $i--) { my $llim = length ($lname); $llim = 8 - $i if ($llim > 8 - $i); for ($j = 1; $j <= $llim; $j++) { if ($initial) { # Is there room for an initial? if ($j == $llim && $i + $llim < 8) { $try = substr ($fname, 0, $i) . $initial . substr ($lname, 0, $j); push (@potuname, $try) if &uname_not_taken($try); } # Is there room for an initial if we chop a letter off # last name? if ($j > 1) { $try = substr ($fname, 0, $i) . $initial . substr ($lname, 0, $j-1); push (@potuname, $try) if &uname_not_taken($try); } } $try = substr ($fname, 0, $i) . substr ($lname, 0, $j); push (@potuname, $try) if &uname_not_taken($try); } last if @potuname >= $goal; } # Absolutely last ditch effort: geirov1, geirov2 etc. $i = 1; $flen = 6 if $flen > 6; while (@potuname < $goal && $i < 100) { $try = substr ($fname, 0, $flen) . $i++; push (@potuname, $try) if &uname_not_taken($try); } return @potuname; } ## ## Example invocation: ## while (<>) { chomp; @list = &user_suggest_unames ($_, 3); printf("%-50s %s\n", $_, join ("/", @list)); # Remember to mark the user name as used if chosen! $uname_db{$list[0]} = 1; }