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