don't touch files with hardlinks (unless -f)
[soepkiptng.git] / soepkiptng.lib
blob76073f541ac41f56e090f829c4a23bf225680682
1 ############################################################################
2 # soepkiptng (c) copyright 2000 Eric Lammerts <eric@lammerts.org>.
3 ############################################################################
4 # This program is free software; you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License, version 2, as 
6 # published by the Free Software Foundation.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 # GNU General Public License for more details.
13 # A copy of the GNU General Public License is available on the World Wide Web
14 # at `http://www.gnu.org/copyleft/gpl.html'.  You can also obtain it by
15 # writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
16 # Boston, MA 02111-1307, USA.
17 ############################################################################
19 use IO::Socket;
20 use Socket;
21 use POSIX qw(locale_h);
23 setlocale(LC_CTYPE, "en_US.ISO-8859-1");
25 my @globalconfigfiles = ($ENV{SOEPKIPTNG_CONFIGFILE},
26    "$ENV{HOME}/.soepkiptng.conf", "/etc/soepkiptng.conf");
28 our @playmodes = ("all", "requested", "recent", "never played");
30 sub read_configfile1($$);
32 sub read_configfile1($$) {
33         my ($conf, $cf) = @_;
34         local *F;
36         open F, $cf or return undef;
37         while(<F>) {
38                 /^#/ and next;
39                 s/\s+$//;
40                 /./ or next;
41                 if(/^include\s+(.*\S)/) {
42                         read_configfile1($conf, $1);
43                         next;
44                 }
45                 if(/^(\w[-.\w]*)\s*=\s*(.*?)\s*$/) {
46                         $f = lc $1;
47                         $conf->{$f} = $2;
48                 } elsif(/^\s+(.*?)\s*$/) {
49                         # continuation line
50                         $conf->{$f} .= "\n$1";
51                 } else {
52                         die "$cf line $.: invalid format\n";
53                 }
54         }
55         close F;
56         return 1;
59 sub read_configfile($;@) {
60         my ($conf, @extraconfigfiles) = @_;
61         my $cf;
63         foreach $cf ((@extraconfigfiles, @globalconfigfiles)) {
64                 defined($cf) and read_configfile1($conf, $cf) and return;
65         }
66         die sprintf "no configuration file found (tried %s)\n",
67                 join(" ", @extraconfigfiles, @globalconfigfiles);
70 sub connect_to_db($) {
71         my ($conf) = @_;
72         my $dbh = DBI->connect("DBI:$conf->{db_type}:$conf->{db_name}:$conf->{db_host}", $conf->{db_user}, $conf->{db_pass})
73                 or die "can't connect to database";
74         $dbh->{mysql_auto_reconnect} = 1;
75         return $dbh;
78 %latin9_to_ascii = (
79         128 => '_', 129 => '_', 130 => '_', 131 => '_',
80         132 => '_', 133 => '_', 134 => '_', 135 => '_',
81         136 => '_', 137 => '_', 138 => '_', 139 => '_',
82         140 => '_', 141 => '_', 142 => '_', 143 => '_',
83         144 => '_', 145 => '_', 146 => '_', 147 => '_',
84         148 => '_', 149 => '_', 150 => '_', 151 => '_',
85         152 => '_', 153 => '_', 154 => '_', 155 => '_',
86         156 => '_', 157 => '_', 158 => '_', 159 => '_',
87         160 => '_',             # no-break space
88         161 => '_',             # ¡ inverted exclamation mark
89         162 => 'c',             # ¢ cent sign
90         163 => 'L',             # £ pound sign
91         164 => 'EUR',           # ¤ euro sign
92         165 => 'Y',             # ¥ yen sign
93         166 => 'S',             # ¦ latin capital letter s with caron
94         167 => '_',             # § section sign
95         168 => 's',             # ¨ latin small letter s with caron
96         169 => 'C',             # © copyright sign
97         170 => 'a',             # ª feminine ordinal indicator
98         171 => '_',             # « left-pointing double angle quotation mark
99         172 => '_',             # ¬ not sign
100         173 => '_',             # ­ soft hyphen
101         174 => 'R',             # ® registered sign
102         175 => '_',             # ¯ macron
103         176 => 'o',             # ° degree sign
104         177 => '_',             # ± plus-minus sign
105         178 => '2',             # ² superscript two
106         179 => '3',             # ³ superscript three
107         180 => 'Z',             # ´ latin capital letter z with caron
108         181 => 'u',             # µ micro sign
109         182 => '_',             # ¶ pilcrow sign
110         183 => '_',             # · middle dot
111         184 => 'z',             # ¸ latin small letter z with caron
112         185 => '1',             # ¹ superscript one
113         186 => 'o',             # º masculine ordinal indicator
114         187 => '_',             # » right-pointing double angle quotation mark
115         188 => 'OE',            # ¼ latin capital ligature oe
116         189 => 'oe',            # ½ latin small ligature oe
117         190 => 'IJ',            # ¾ latin capital letter y with diaeresis
118         191 => '_',             # ¿ inverted question mark
119         192 => 'A',             # capital A, grave accent
120         193 => 'A',             # capital A, acute accent
121         194 => 'A',             # capital A, circumflex accent
122         195 => 'A',             # capital A, tilde
123         196 => 'A',             # capital A, dieresis or umlaut mark
124         197 => 'A',             # capital A, ring
125         198 => 'AE',            # capital AE diphthong (ligature)
126         199 => 'C',             # capital C, cedilla
127         200 => 'E',             # capital E, grave accent
128         201 => 'E',             # capital E, acute accent
129         202 => 'E',             # capital E, circumflex accent
130         203 => 'E',             # capital E, dieresis or umlaut mark
131         204 => 'I',             # capital I, grave accent
132         205 => 'I',             # capital I, acute accent
133         206 => 'I',             # capital I, circumflex accent
134         207 => 'I',             # capital I, dieresis or umlaut mark
135         208 => '_',             # Ð latin capital letter eth
136         209 => 'N',             # capital N, tilde
137         210 => 'O',             # capital O, grave accent
138         211 => 'O',             # capital O, acute accent
139         212 => 'O',             # capital O, circumflex accent
140         213 => 'O',             # capital O, tilde
141         214 => 'O',             # capital O, dieresis or umlaut mark
142         215 => 'x',             # ×, multiplication sign
143         216 => 'O',             # capital O, slash
144         217 => 'U',             # capital U, grave accent
145         218 => 'U',             # capital U, acute accent
146         219 => 'U',             # capital U, circumflex accent
147         220 => 'U',             # capital U, dieresis or umlaut mark
148         221 => 'Y',             # capital Y, acute accent
149         222 => '_',             # Þ latin capital letter thorn
150         223 => 'ss',            # small sharp s, German (sz ligature)
151         224 => 'a',             # small a, grave accent
152         225 => 'a',             # small a, acute accent
153         226 => 'a',             # small a, circumflex accent
154         227 => 'a',             # small a, tilde
155         228 => 'a',             # small a, dieresis or umlaut mark
156         229 => 'a',             # small a, ring
157         230 => 'ae',            # small ae diphthong (ligature)
158         231 => 'c',             # small c, cedilla
159         232 => 'e',             # small e, grave accent
160         233 => 'e',             # small e, acute accent
161         234 => 'e',             # small e, circumflex accent
162         235 => 'e',             # small e, dieresis or umlaut mark
163         236 => 'i',             # small i, grave accent
164         237 => 'i',             # small i, acute accent
165         238 => 'i',             # small i, circumflex accent
166         239 => 'i',             # small i, dieresis or umlaut mark
167         240 => '_',             # ð latin small letter eth
168         241 => 'n',             # small n, tilde
169         242 => 'o',             # small o, grave accent
170         243 => 'o',             # small o, acute accent
171         244 => 'o',             # small o, circumflex accent
172         245 => 'o',             # small o, tilde
173         246 => 'o',             # small o, dieresis or umlaut mark
174         247 => '_',             # ÷ division sign
175         248 => 'o',             # small o, slash
176         249 => 'u',             # small u, grave accent
177         250 => 'u',             # small u, acute accent
178         251 => 'u',             # small u, circumflex accent
179         252 => 'u',             # small u, dieresis or umlaut mark
180         253 => 'y',             # small y, acute accent
181         254 => '_',             # þ latin small letter thorn
182         255 => 'ij',            # small y, dieresis or umlaut mark
185 sub string_to_filename($;$) {
186         my ($a, $lang) = @_;
188         my $ampr = $lang eq "dut"? "en" : $lang eq "fre"? "et" : $lang eq "ger"? "und" : "and";
190         $a =~ s/-/_/g;
191         $a =~ s/[\[(](.*)/-$1/;
192         $a =~ s/[ _]?&[_ ]?/_${ampr}_/g;
193         $a =~ s/([\x80-\xff])/lc($latin9_to_ascii{ord($1)}) || $1/ge;
194         $a =~ s/[^-A-Za-z0-9]+/_/g;
195         $a =~ s/_?-_?/-/g;
196         $a =~ s/_$//;
197         return lc($a);
200 sub encurl($) {
201         my ($a) = @_;
203         $a =~ s|([^-./\w])|sprintf "%%%02x", ord($1)|ge;
204 #       $a =~ s| |+|g;
205         $a;
208 sub enchtml($;$) {
209         my ($a, $do_nbsp) = @_;
211         $a =~ s|&|&amp;|g;
212         $a =~ s|"|&quot;|g;
213         $a =~ s|<|&lt;|g;
214         $a =~ s|>|&gt;|g;
215         $a =~ s| |&nbsp;|g if $do_nbsp;
216         $a;
219 sub del_song($$@) {
220         my ($db, $tbl, @ids) = @_;
221         @ids or return;
222         $db->do("DELETE FROM $tbl WHERE song_id=" . join(" OR song_id=", @ids));
225 sub add_song_nolock($$$$@) {
226         my ($db, $tbl, $order, $user, @ids) = @_;
228         @ids or return;
229         my $firstid = shift @ids;
230         my $q = "INSERT INTO $tbl (song_order, song_id, user) " .
231                 "VALUES ($order, $firstid, \"$user\")";
232         foreach(@ids) {
233                 $order++;
234                 $q .= ",($order,$_,\"$user\")";
235         }
236         $db->do($q) or return undef;
237         return 1;
240 sub get_table_ids($$$) {
241         my ($db, $tbl, $rest) = @_;
243         my $res = $db->selectcol_arrayref("SELECT song_id FROM $tbl $rest");
244         return @$res;
248 sub add_song($$$@) {
249         my ($db, $tbl, $user, @ids) = @_;
251         $db->ping;
252         $db->do("LOCK TABLES $tbl WRITE");
253         del_song($db, $tbl, @ids);
254         my ($order) = $db->selectrow_array("SELECT MAX(song_order) FROM $tbl");
255         $order = 0 if $order < 0;
256         my $retval = add_song_nolock($db, $tbl, $order + 1, $user, @ids);
257         $db->do("UNLOCK TABLES");
258         return $retval;
261 sub reorder_table($$$@) {
262         my ($db, $tbl, $order, @ids) = @_;
264         foreach(@ids) {
265                 $db->do("UPDATE $tbl SET song_order=$order WHERE song_id=$_");
266                 $order++;
267         }
270 sub move_song_to_top($$@) {
271         my ($db, $tbl, @ids) = @_;
273         $db->ping;
274         $db->do("LOCK TABLES $tbl WRITE");
275         reorder_table($db, $tbl, $#ids + 2, get_table_ids($db, $tbl, "ORDER BY song_order"));
276         reorder_table($db, $tbl, 1, @ids);
277         $db->do("UNLOCK TABLES");
280 sub move_song_to_bottom($$@) {
281         my ($db, $tbl, @ids) = @_;
282         my %ids;
284         foreach(@ids) { $ids{$_} = 1; };
285         $db->ping;
286         $db->do("LOCK TABLES $tbl WRITE");
287         my @q = get_table_ids($db, $tbl, "ORDER BY song_order");
288         my (@q1, @q2);
289         foreach(@q) {
290                 if($ids{$_}) { push @q2, $_; } else { push @q1, $_; }
291         }
292         reorder_table($db, $tbl, 1, @q1, @q2);
293         $db->do("UNLOCK TABLES");
296 sub shuffle_table($$) {
297         my ($db, $tbl) = @_;
299         $db->ping;
300         $db->do("LOCK TABLES $tbl WRITE");
301         reorder_table($db, $tbl, 1, get_table_ids($db, $tbl, "ORDER BY rand()"));
302         $db->do("UNLOCK TABLES");
305 sub kill_song(;$$) {
306         my ($user, $id) = @_;
307         my ($plid, $host, $port);
308         local *F;
310         open F, $conf{statusfile}
311                 or die "$conf{statusfile}: $!\n";
312         $plid = <F>;
313         <F>; <F>; <F>;
314         chop($host = <F>);
315         $port = <F>;
316         close F;
318         if(defined($id) && $id != $plid) { return undef; }
320         if($conf{kill_song_external}) {
321                 system $conf{kill_song_external}, $user || '';
323                 open F, $conf{statusfile}
324                         or die "$conf{statusfile}: $!\n";
325                 my @lines;
326                 chop(@lines = <F>);
327                 close F;
328                 return @lines;
329         }
330         return kill_song_internal($host, $port);
333 sub kill_song_internal($$) {
334         my ($host, $port) = @_;
335         local *F;
337         $host && $port or return undef;
339         socket(F, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
340                 or die "socket: $!\n";
341         connect(F, sockaddr_in($port, inet_aton($host)))
342                 or die "connect $host:$port: $!\n";
343         my @status;
344         @status = <F>;
345         close F;
346         return @status;
349 sub player_cmd(;@) {
350         my $res = 1;
351         local *F;
352         my $host;
354         open F, $conf{statusfile}
355                 or die "$conf{statusfile}: $!\n";
356         <F>; <F>; <F>; <F>;
357         chop($host = <F>);
358         close F;
360         my $sock = IO::Socket::INET->new("$host:2222") or return undef;
361         $sock->autoflush(1);
362         $response = <$sock>;    #greeting
363         foreach(@_) {
364                 $sock->print("$_\n");
365                 $res = 0 if <$sock> !~ /^\+/;
366         }
367         $sock->close;
368         return $res;
372 sub get_player_pid() {
373         local *F;
374         my $pid;
376         open F, $conf{statusfile}
377                 or die "$conf{statusfile}: $!\n";
378         <F>; <F>; <F>;
379         $pid = 0 + <F>;
380         close F;
381         return $pid;
384 sub get_id($$$) {
385         my ($db, $table, $value) = @_;
387         my $sth = $db->prepare("SELECT id,name FROM $table WHERE binary name=?");
388         if($sth->execute($value || "") >= 1) {
389                 my ($id, $v) = $sth->fetchrow_array;
390                 return $id;
391         } else {
392                 $db->do("REPLACE INTO $table SET name=?", undef, $value)
393                         or die;
394                 return $db->{'mysql_insertid'};
395         }
398 sub get_playlist_contents($$) {
399         my ($dbh, $list) = @_;
400         my %songids;
401         my (%artistids, %albumsids, %listids);
402         my (%artistids_done, %albumsids_done, %listids_done);
404         my $sth_list = $dbh->prepare("SELECT type,entity_id" .
405                 " FROM list_contents WHERE list_id=?");
406         my $sth_artist = $dbh->prepare("SELECT id FROM song WHERE artist_id=?");
407         my $sth_album = $dbh->prepare("SELECT id FROM song WHERE album_id=?");
408         
409         $listids{$list} = 1;
410         while(%artistids || %albumsids || %listids) {
411                 foreach(keys %listids) {
412                         $listids_done{$_} = 1;
413                         delete $listids{$_};
415                         $sth_list->execute($_);
416                         while($_ = $sth_list->fetchrow_hashref) {
417                                 if($_->{'type'} eq 'list') {
418                                         next if $listids_done{$_->{'entity_id'}};
419                                         $listids{$_->{'entity_id'}} = 1;
420                                 } elsif($_->{'type'} eq 'artist') {
421                                         next if $artistids_done{$_->{'entity_id'}};
422                                         $artistids{$_->{'entity_id'}} = 1;
423                                 } elsif($_->{'type'} eq 'album') {
424                                         next if $albumids_done{$_->{'entity_id'}};
425                                         $albumids{$_->{'entity_id'}} = 1;
426                                 } elsif($_->{'type'} eq 'song') {
427                                         $songids{$_->{'entity_id'}} = 1;
428                                 }
429                         }
430                 }
431                 foreach(keys %artistids) {
432                         $artistids_done{$_} = 1;
433                         delete $artistids{$_};
435                         $sth_artist->execute($_);
436                         while($_ = $sth_artist->fetchrow_hashref) {
437                                 $songids{$_->{'id'}} = 1;
438                         }
439                 }
440                 foreach(keys %albumids) {
441                         $albumids_done{$_} = 1;
442                         delete $albumids{$_};
444                         $sth_album->execute($_);
445                         while($_ = $sth_album->fetchrow_hashref) {
446                                 $songids{$_->{'id'}} = 1;
447                         }
448                 }
449         }
450         return keys %songids;
453 sub get_nowplaying(;$) {
454         my ($dbh) = @_;
455         local *F;
456         my $s = undef;
458         open F, $conf{statusfile} or return undef;
459         chop (($s->{id}, $s->{filename}, $s->{pid}, $s->{cdrplaypid},
460                $s->{killhost}, $s->{killport}, $s->{type},
461                $s->{user}, $s->{artist}, $s->{title}, $s->{album},
462                $s->{track}, $s->{length}, $s->{encoding},
463                $s->{artisturl}, $s->{albumurl}, $s->{titleurl}) = <F>);
464         close F;
466         if($dbh && $s->{id}) {
467                 my $sth = $dbh->prepare(
468                         "SELECT artist.name as artist,album.name as album," .
469                         " song.artist_id as arid,song.album_id as alid, song.*" .
470                         " FROM song,artist,album WHERE song.artist_id=artist.id" .
471                         " AND song.album_id=album.id AND song.id=?");
472                 $sth->execute($s->{id});
473                 if(my $d = $sth->fetchrow_hashref) {
474                         $s->{alid} = $d->{alid};
475                         $s->{arid} = $d->{arid};
476                         $s->{filename} = $d->{filename};
477                         $s->{artist} = $d->{artist};
478                         $s->{title} = $d->{title};
479                         $s->{album} = $d->{album};
480                         $s->{track} = $d->{track};
481                         $s->{length} = $d->{length};
482                         $s->{encoding} = $d->{encoding};
483                 }
484         }
485         return $s;
488 sub construct_url($@) {
489         my ($baseurl, $argsref) = @_;
490         my $sep = "?";
491         foreach(keys %$argsref) {
492                 $baseurl .= "$sep$_=" . encurl($$argsref{$_});
493                 $sep = "&";
494         }
495         return $baseurl;
498 sub cleanup_name {
499         use locale;
500         my ($s) = @_;
501         $s =~ s/\.mp3\s*$//i;
502         $s =~ s/%([0-9a-f][0-9a-f])/chr(hex($1))/eig;
503         $s =~ s/([a-zA-Z])([A-Z][a-z])/$1 $2/g;
504         $s = lc($s);
505         $s =~ s/_/ /g;
506         if($s !~ / /) { $s =~ s/\./ /g; }
507         $s =~ s/^\s+|\s+$//g;
508         $s =~ s/([^'\w\xc0-\xff]|^)(\w)/$1\U$2/g;
509         $s =~ s/\b(o'\w)/\U$1/ig;
510         $s =~ s/n T\b/n't/g;
511         $s =~ s/ S\b/'s/g;
512         $s =~ s/\bI M\b/I'm/g;
513         $s =~ s/ ll\b/'ll/ig;
514         $s =~ s/\b(i+)\b/\U$1/ig;
515         $s =~ s/([eiuy]) ([rv]e)\b/$1'\L$2/ig;
516         $s =~ s/\s*-\s*/ - /g;
517         $s;
520 sub add_song_next($$$;$) {
521         my ($dbh, $table, $currentsong, $user) = @_;
523         my $ids = $dbh->selectcol_arrayref("SELECT song_id FROM $table,song ".
524                 "WHERE $table.song_id=song.id AND song.present");
525         my %idsq;
526         foreach(@$ids) { $idsq{$_} = 1; }
528         my $sth = $dbh->prepare("SELECT song2.* FROM song AS song1,".
529                 " song AS song2 WHERE song1.id=$currentsong AND".
530                 " song1.artist_id = song2.artist_id AND".
531                 " song1.album_id = song2.album_id AND".
532                 " song2.track > song1.track AND".
533                 " song2.present ORDER BY song2.track");
534         $sth->execute;
535         while($_ = $sth->fetchrow_hashref) {
536                 if(!$idsq{$_->{id}}) {
537                         add_song($dbh, $table, $user, $_->{id})
538                                 or warn "can't add song.\n";
539                         return $_;
540                 }
541         }
544 %iso_639_2 = (
545         'aar' => 'Afar',
546         'abk' => 'Abkhazian',
547         'ace' => 'Achinese',
548         'ach' => 'Acoli',
549         'ada' => 'Adangme',
550         'afa' => 'Afro-Asiatic',
551         'afh' => 'Afrihili',
552         'afr' => 'Afrikaans',
553         'aka' => 'Akan',
554         'akk' => 'Akkadian',
555         'alb' => 'Albanian',
556         'ale' => 'Aleut',
557         'alg' => 'Algonquian Languages',
558         'amh' => 'Amharic',
559         'ang' => 'English, Old',
560         'apa' => 'Apache Languages',
561         'ara' => 'Arabic',
562         'arc' => 'Aramaic',
563         'arm' => 'Armenian',
564         'arn' => 'Araucanian',
565         'arp' => 'Arapaho',
566         'art' => 'Artificial',
567         'arw' => 'Arawak',
568         'asm' => 'Assamese',
569         'ath' => 'Athapascan Languages',
570         'ava' => 'Avaric',
571         'ave' => 'Avestan',
572         'awa' => 'Awadhi',
573         'aym' => 'Aymara',
574         'aze' => 'Azerbaijani',
575         'bad' => 'Banda',
576         'bai' => 'Bamileke Languages',
577         'bak' => 'Bashkir',
578         'bal' => 'Baluchi',
579         'bam' => 'Bambara',
580         'ban' => 'Balinese',
581         'baq' => 'Basque',
582         'bas' => 'Basa',
583         'bat' => 'Baltic',
584         'bej' => 'Beja',
585         'bel' => 'Byelorussian',
586         'bem' => 'Bemba',
587         'ben' => 'Bengali',
588         'ber' => 'Berber',
589         'bho' => 'Bhojpuri',
590         'bih' => 'Bihari',
591         'bik' => 'Bikol',
592         'bin' => 'Bini',
593         'bis' => 'Bislama',
594         'bla' => 'Siksika',
595         'bnt' => 'Bantu',
596         'bod' => 'Tibetan',
597         'bra' => 'Braj',
598         'bre' => 'Breton',
599         'bua' => 'Buriat',
600         'bug' => 'Buginese',
601         'bul' => 'Bulgarian',
602         'bur' => 'Burmese',
603         'cad' => 'Caddo',
604         'cai' => 'Central American Indian',
605         'car' => 'Carib',
606         'cat' => 'Catalan',
607         'cau' => 'Caucasian',
608         'ceb' => 'Cebuano',
609         'cel' => 'Celtic',
610         'ces' => 'Czech',
611         'cha' => 'Chamorro',
612         'chb' => 'Chibcha',
613         'che' => 'Chechen',
614         'chg' => 'Chagatai',
615         'chi' => 'Chinese',
616         'chm' => 'Mari',
617         'chn' => 'Chinook jargon',
618         'cho' => 'Choctaw',
619         'chr' => 'Cherokee',
620         'chu' => 'Church Slavic',
621         'chv' => 'Chuvash',
622         'chy' => 'Cheyenne',
623         'cop' => 'Coptic',
624         'cor' => 'Cornish',
625         'cos' => 'Corsican',
626         'cpe' => 'Creoles and Pidgins, English-based',
627         'cpf' => 'Creoles and Pidgins, French-based',
628         'cpp' => 'Creoles and Pidgins, Portuguese-based',
629         'cre' => 'Cree',
630         'crp' => 'Creoles and Pidgins',
631         'cus' => 'Cushitic',
632         'cym' => 'Welsh',
633         'cze' => 'Czech',
634         'dak' => 'Dakota',
635         'dan' => 'Danish',
636         'del' => 'Delaware',
637         'deu' => 'German',
638         'din' => 'Dinka',
639         'div' => 'Divehi',
640         'doi' => 'Dogri',
641         'dra' => 'Dravidian',
642         'dua' => 'Duala',
643         'dum' => 'Dutch, Middle',
644         'dut' => 'Dutch',
645         'dyu' => 'Dyula',
646         'dzo' => 'Dzongkha',
647         'efi' => 'Efik',
648         'egy' => 'Egyptian (Ancient)',
649         'eka' => 'Ekajuk',
650         'ell' => 'Greek, Modern',
651         'elx' => 'Elamite',
652         'eng' => 'English',
653         'enm' => 'English, Middle',
654         'epo' => 'Esperanto',
655         'esk' => 'Eskimo',
656         'esl' => 'Spanish',
657         'est' => 'Estonian',
658         'eus' => 'Basque',
659         'ewe' => 'Ewe',
660         'ewo' => 'Ewondo',
661         'fan' => 'Fang',
662         'fao' => 'Faroese',
663         'fas' => 'Persian',
664         'fat' => 'Fanti',
665         'fij' => 'Fijian',
666         'fin' => 'Finnish',
667         'fiu' => 'Finno-Ugrian',
668         'fon' => 'Fon',
669         'fra' => 'French',
670         'fre' => 'French',
671         'frm' => 'French, Middle',
672         'fro' => 'French, Old',
673         'fry' => 'Frisian',
674         'ful' => 'Fulah',
675         'gaa' => 'Ga',
676         'gae' => 'Gaelic (Scots)',
677         'gai' => 'Irish',
678         'gay' => 'Gayo',
679         'gdh' => 'Gaelic (Scots)',
680         'gem' => 'Germanic',
681         'geo' => 'Georgian',
682         'ger' => 'German',
683         'gez' => 'Geez',
684         'gil' => 'Gilbertese',
685         'glg' => 'Gallegan',
686         'gmh' => 'German, Middle High',
687         'goh' => 'German, Old High',
688         'gon' => 'Gondi',
689         'got' => 'Gothic',
690         'grb' => 'Grebo',
691         'grc' => 'Greek, Ancient',
692         'gre' => 'Greek, Modern',
693         'grn' => 'Guarani',
694         'guj' => 'Gujarati',
695         'hai' => 'Haida',
696         'hau' => 'Hausa',
697         'haw' => 'Hawaiian',
698         'heb' => 'Hebrew',
699         'her' => 'Herero',
700         'hil' => 'Hiligaynon',
701         'him' => 'Himachali',
702         'hin' => 'Hindi',
703         'hmo' => 'Hiri Motu',
704         'hun' => 'Hungarian',
705         'hup' => 'Hupa',
706         'hye' => 'Armenian',
707         'iba' => 'Iban',
708         'ibo' => 'Igbo',
709         'ice' => 'Icelandic',
710         'ijo' => 'Ijo',
711         'iku' => 'Inuktitut',
712         'ilo' => 'Iloko',
713         'ina' => 'Interlingua',
714         'inc' => 'Indic',
715         'ind' => 'Indonesian',
716         'ine' => 'Indo-European',
717         'ine' => 'Interlingue',
718         'ipk' => 'Inupiak',
719         'ira' => 'Iranian',
720         'iri' => 'Irish',
721         'iro' => 'Iroquoian uages',
722         'isl' => 'Icelandic',
723         'ita' => 'Italian',
724         'jav' => 'Javanese',
725         'jaw' => 'Javanese',
726         'jpn' => 'Japanese',
727         'jpr' => 'Judeo-Persian',
728         'jrb' => 'Judeo-Arabic',
729         'kaa' => 'Kara-Kalpak',
730         'kab' => 'Kabyle',
731         'kac' => 'Kachin',
732         'kal' => 'Greenlandic',
733         'kam' => 'Kamba',
734         'kan' => 'Kannada',
735         'kar' => 'Karen',
736         'kas' => 'Kashmiri',
737         'kat' => 'Georgian',
738         'kau' => 'Kanuri',
739         'kaw' => 'Kawi',
740         'kaz' => 'Kazakh',
741         'kha' => 'Khasi',
742         'khi' => 'Khoisan',
743         'khm' => 'Khmer',
744         'kho' => 'Khotanese',
745         'kik' => 'Kikuyu',
746         'kin' => 'Kinyarwanda',
747         'kir' => 'Kirghiz',
748         'kok' => 'Konkani',
749         'kom' => 'Komi',
750         'kon' => 'Kongo',
751         'kor' => 'Korean',
752         'kpe' => 'Kpelle',
753         'kro' => 'Kru',
754         'kru' => 'Kurukh',
755         'kua' => 'Kuanyama',
756         'kum' => 'Kumyk',
757         'kur' => 'Kurdish',
758         'kus' => 'Kusaie',
759         'kut' => 'Kutenai',
760         'lad' => 'Ladino',
761         'lah' => 'Lahnda',
762         'lam' => 'Lamba',
763         'lao' => 'Lao',
764         'lat' => 'Latin',
765         'lav' => 'Latvian',
766         'lez' => 'Lezghian',
767         'lin' => 'Lingala',
768         'lit' => 'Lithuanian',
769         'lol' => 'Mongo',
770         'loz' => 'Lozi',
771         'ltz' => 'Letzeburgesch',
772         'lub' => 'Luba-Katanga',
773         'lug' => 'Ganda',
774         'lui' => 'Luiseno',
775         'lun' => 'Lunda',
776         'luo' => 'Luo (Kenya and Tanzania)',
777         'mac' => 'Macedonian',
778         'mad' => 'Madurese',
779         'mag' => 'Magahi',
780         'mah' => 'Marshall',
781         'mai' => 'Maithili',
782         'mak' => 'Macedonian',
783         'mak' => 'Makasar',
784         'mal' => 'Malayalam',
785         'man' => 'Mandingo',
786         'mao' => 'Maori',
787         'map' => 'Austronesian',
788         'mar' => 'Marathi',
789         'mas' => 'Masai',
790         'max' => 'Manx',
791         'may' => 'Malay',
792         'men' => 'Mende',
793         'mga' => 'Irish, Middle',
794         'mic' => 'Micmac',
795         'min' => 'Minangkabau',
796         'mis' => 'Miscellaneous',
797         'mkh' => 'Mon-Kmer',
798         'mlg' => 'Malagasy',
799         'mlt' => 'Maltese',
800         'mni' => 'Manipuri',
801         'mno' => 'Manobo Languages',
802         'moh' => 'Mohawk',
803         'mol' => 'Moldavian',
804         'mon' => 'Mongolian',
805         'mos' => 'Mossi',
806         'mri' => 'Maori',
807         'msa' => 'Malay',
808         'mul' => 'Multiple Languages',
809         'mun' => 'Munda Languages',
810         'mus' => 'Creek',
811         'mwr' => 'Marwari',
812         'mya' => 'Burmese',
813         'myn' => 'Mayan Languages',
814         'nah' => 'Aztec',
815         'nai' => 'North American Indian',
816         'nau' => 'Nauru',
817         'nav' => 'Navajo',
818         'nbl' => 'Ndebele, South',
819         'nde' => 'Ndebele, North',
820         'ndo' => 'Ndongo',
821         'nep' => 'Nepali',
822         'new' => 'Newari',
823         'nic' => 'Niger-Kordofanian',
824         'niu' => 'Niuean',
825         'nla' => 'Dutch',
826         'nno' => 'Norwegian (Nynorsk)',
827         'non' => 'Norse, Old',
828         'nor' => 'Norwegian',
829         'nso' => 'Sotho, Northern',
830         'nub' => 'Nubian Languages',
831         'nya' => 'Nyanja',
832         'nym' => 'Nyamwezi',
833         'nyn' => 'Nyankole',
834         'nyo' => 'Nyoro',
835         'nzi' => 'Nzima',
836         'oci' => 'Langue d\'Oc',
837         'oji' => 'Ojibwa',
838         'ori' => 'Oriya',
839         'orm' => 'Oromo',
840         'osa' => 'Osage',
841         'oss' => 'Ossetic',
842         'ota' => 'Turkish, Ottoman',
843         'oto' => 'Otomian Languages',
844         'paa' => 'Papuan-Australian',
845         'pag' => 'Pangasinan',
846         'pal' => 'Pahlavi',
847         'pam' => 'Pampanga',
848         'pan' => 'Panjabi',
849         'pap' => 'Papiamento',
850         'pau' => 'Palauan',
851         'peo' => 'Persian, Old',
852         'per' => 'Persian',
853         'phn' => 'Phoenician',
854         'pli' => 'Pali',
855         'pol' => 'Polish',
856         'pon' => 'Ponape',
857         'por' => 'Portuguese',
858         'pra' => 'Prakrit uages',
859         'pro' => 'Provencal, Old',
860         'pus' => 'Pushto',
861         'que' => 'Quechua',
862         'raj' => 'Rajasthani',
863         'rar' => 'Rarotongan',
864         'roa' => 'Romance',
865         'roh' => 'Rhaeto-Romance',
866         'rom' => 'Romany',
867         'ron' => 'Romanian',
868         'rum' => 'Romanian',
869         'run' => 'Rundi',
870         'rus' => 'Russian',
871         'sad' => 'Sandawe',
872         'sag' => 'Sango',
873         'sah' => 'Yakut',
874         'sai' => 'South American Indian',
875         'sal' => 'Salishan Languages',
876         'sam' => 'Samaritan Aramaic',
877         'san' => 'Sanskrit',
878         'sco' => 'Scots',
879         'scr' => 'Serbo-Croatian',
880         'sel' => 'Selkup',
881         'sem' => 'Semitic',
882         'sga' => 'Irish, Old',
883         'shn' => 'Shan',
884         'sid' => 'Sidamo',
885         'sin' => 'Singhalese',
886         'sio' => 'Siouan Languages',
887         'sit' => 'Sino-Tibetan',
888         'sla' => 'Slavic',
889         'slk' => 'Slovak',
890         'slo' => 'Slovak',
891         'slv' => 'Slovenian',
892         'smi' => 'Sami Languages',
893         'smo' => 'Samoan',
894         'sna' => 'Shona',
895         'snd' => 'Sindhi',
896         'sog' => 'Sogdian',
897         'som' => 'Somali',
898         'son' => 'Songhai',
899         'sot' => 'Sotho, Southern',
900         'spa' => 'Spanish',
901         'sqi' => 'Albanian',
902         'srd' => 'Sardinian',
903         'srr' => 'Serer',
904         'ssa' => 'Nilo-Saharan',
905         'ssw' => 'Siswant',
906         'ssw' => 'Swazi',
907         'suk' => 'Sukuma',
908         'sun' => 'Sudanese',
909         'sus' => 'Susu',
910         'sux' => 'Sumerian',
911         'sve' => 'Swedish',
912         'swa' => 'Swahili',
913         'swe' => 'Swedish',
914         'syr' => 'Syriac',
915         'tah' => 'Tahitian',
916         'tam' => 'Tamil',
917         'tat' => 'Tatar',
918         'tel' => 'Telugu',
919         'tem' => 'Timne',
920         'ter' => 'Tereno',
921         'tgk' => 'Tajik',
922         'tgl' => 'Tagalog',
923         'tha' => 'Thai',
924         'tib' => 'Tibetan',
925         'tig' => 'Tigre',
926         'tir' => 'Tigrinya',
927         'tiv' => 'Tivi',
928         'tli' => 'Tlingit',
929         'tmh' => 'Tamashek',
930         'tog' => 'Tonga (Nyasa)',
931         'ton' => 'Tonga (Tonga Islands)',
932         'tru' => 'Truk',
933         'tsi' => 'Tsimshian',
934         'tsn' => 'Tswana',
935         'tso' => 'Tsonga',
936         'tuk' => 'Turkmen',
937         'tum' => 'Tumbuka',
938         'tur' => 'Turkish',
939         'tut' => 'Altaic',
940         'twi' => 'Twi',
941         'tyv' => 'Tuvinian',
942         'uga' => 'Ugaritic',
943         'uig' => 'Uighur',
944         'ukr' => 'Ukrainian',
945         'umb' => 'Umbundu',
946         'und' => 'Undetermined',
947         'urd' => 'Urdu',
948         'uzb' => 'Uzbek',
949         'vai' => 'Vai',
950         'ven' => 'Venda',
951         'vie' => 'Vietnamese',
952         'vol' => 'Volapu:k',
953         'vot' => 'Votic',
954         'wak' => 'Wakashan Languages',
955         'wal' => 'Walamo',
956         'war' => 'Waray',
957         'was' => 'Washo',
958         'wel' => 'Welsh',
959         'wen' => 'Sorbian Languages',
960         'wol' => 'Wolof',
961         'xho' => 'Xhosa',
962         'yao' => 'Yao',
963         'yap' => 'Yap',
964         'yid' => 'Yiddish',
965         'yor' => 'Yoruba',
966         'zap' => 'Zapotec',
967         'zen' => 'Zenaga',
968         'zha' => 'Zhuang',
969         'zho' => 'Chinese',
970         'zul' => 'Zulu',
971         'zun' => 'Zuni',