@@ -68,6 +68,8 @@ static struct curl_slist *no_pragma_header;
6868
6969static struct active_request_slot * active_queue_head ;
7070
71+ static char * cached_accept_language ;
72+
7173size_t fread_buffer (char * ptr , size_t eltsize , size_t nmemb , void * buffer_ )
7274{
7375 size_t size = eltsize * nmemb ;
@@ -515,6 +517,9 @@ void http_cleanup(void)
515517 cert_auth .password = NULL ;
516518 }
517519 ssl_cert_password_required = 0 ;
520+
521+ free (cached_accept_language );
522+ cached_accept_language = NULL ;
518523}
519524
520525struct active_request_slot * get_active_slot (void )
@@ -986,6 +991,142 @@ static void extract_content_type(struct strbuf *raw, struct strbuf *type,
986991 strbuf_addstr (charset , "ISO-8859-1" );
987992}
988993
994+ /*
995+ * Guess the user's preferred languages from the value in LANGUAGE environment
996+ * variable and LC_MESSAGES locale category if NO_GETTEXT is not defined.
997+ *
998+ * The result can be a colon-separated list like "ko:ja:en".
999+ */
1000+ static const char * get_preferred_languages (void )
1001+ {
1002+ const char * retval ;
1003+
1004+ retval = getenv ("LANGUAGE" );
1005+ if (retval && * retval )
1006+ return retval ;
1007+
1008+ #ifndef NO_GETTEXT
1009+ retval = setlocale (LC_MESSAGES , NULL );
1010+ if (retval && * retval &&
1011+ strcmp (retval , "C" ) &&
1012+ strcmp (retval , "POSIX" ))
1013+ return retval ;
1014+ #endif
1015+
1016+ return NULL ;
1017+ }
1018+
1019+ static void write_accept_language (struct strbuf * buf )
1020+ {
1021+ /*
1022+ * MAX_DECIMAL_PLACES must not be larger than 3. If it is larger than
1023+ * that, q-value will be smaller than 0.001, the minimum q-value the
1024+ * HTTP specification allows. See
1025+ * http://tools.ietf.org/html/rfc7231#section-5.3.1 for q-value.
1026+ */
1027+ const int MAX_DECIMAL_PLACES = 3 ;
1028+ const int MAX_LANGUAGE_TAGS = 1000 ;
1029+ const int MAX_ACCEPT_LANGUAGE_HEADER_SIZE = 4000 ;
1030+ char * * language_tags = NULL ;
1031+ int num_langs = 0 ;
1032+ const char * s = get_preferred_languages ();
1033+ int i ;
1034+ struct strbuf tag = STRBUF_INIT ;
1035+
1036+ /* Don't add Accept-Language header if no language is preferred. */
1037+ if (!s )
1038+ return ;
1039+
1040+ /*
1041+ * Split the colon-separated string of preferred languages into
1042+ * language_tags array.
1043+ */
1044+ do {
1045+ /* collect language tag */
1046+ for (; * s && (isalnum (* s ) || * s == '_' ); s ++ )
1047+ strbuf_addch (& tag , * s == '_' ? '-' : * s );
1048+
1049+ /* skip .codeset, @modifier and any other unnecessary parts */
1050+ while (* s && * s != ':' )
1051+ s ++ ;
1052+
1053+ if (tag .len ) {
1054+ num_langs ++ ;
1055+ REALLOC_ARRAY (language_tags , num_langs );
1056+ language_tags [num_langs - 1 ] = strbuf_detach (& tag , NULL );
1057+ if (num_langs >= MAX_LANGUAGE_TAGS - 1 ) /* -1 for '*' */
1058+ break ;
1059+ }
1060+ } while (* s ++ );
1061+
1062+ /* write Accept-Language header into buf */
1063+ if (num_langs ) {
1064+ int last_buf_len = 0 ;
1065+ int max_q ;
1066+ int decimal_places ;
1067+ char q_format [32 ];
1068+
1069+ /* add '*' */
1070+ REALLOC_ARRAY (language_tags , num_langs + 1 );
1071+ language_tags [num_langs ++ ] = "*" ; /* it's OK; this won't be freed */
1072+
1073+ /* compute decimal_places */
1074+ for (max_q = 1 , decimal_places = 0 ;
1075+ max_q < num_langs && decimal_places <= MAX_DECIMAL_PLACES ;
1076+ decimal_places ++ , max_q *= 10 )
1077+ ;
1078+
1079+ sprintf (q_format , ";q=0.%%0%dd" , decimal_places );
1080+
1081+ strbuf_addstr (buf , "Accept-Language: " );
1082+
1083+ for (i = 0 ; i < num_langs ; i ++ ) {
1084+ if (i > 0 )
1085+ strbuf_addstr (buf , ", " );
1086+
1087+ strbuf_addstr (buf , language_tags [i ]);
1088+
1089+ if (i > 0 )
1090+ strbuf_addf (buf , q_format , max_q - i );
1091+
1092+ if (buf -> len > MAX_ACCEPT_LANGUAGE_HEADER_SIZE ) {
1093+ strbuf_remove (buf , last_buf_len , buf -> len - last_buf_len );
1094+ break ;
1095+ }
1096+
1097+ last_buf_len = buf -> len ;
1098+ }
1099+ }
1100+
1101+ /* free language tags -- last one is a static '*' */
1102+ for (i = 0 ; i < num_langs - 1 ; i ++ )
1103+ free (language_tags [i ]);
1104+ free (language_tags );
1105+ }
1106+
1107+ /*
1108+ * Get an Accept-Language header which indicates user's preferred languages.
1109+ *
1110+ * Examples:
1111+ * LANGUAGE= -> ""
1112+ * LANGUAGE=ko:en -> "Accept-Language: ko, en; q=0.9, *; q=0.1"
1113+ * LANGUAGE=ko_KR.UTF-8:sr@latin -> "Accept-Language: ko-KR, sr; q=0.9, *; q=0.1"
1114+ * LANGUAGE=ko LANG=en_US.UTF-8 -> "Accept-Language: ko, *; q=0.1"
1115+ * LANGUAGE= LANG=en_US.UTF-8 -> "Accept-Language: en-US, *; q=0.1"
1116+ * LANGUAGE= LANG=C -> ""
1117+ */
1118+ static const char * get_accept_language (void )
1119+ {
1120+ if (!cached_accept_language ) {
1121+ struct strbuf buf = STRBUF_INIT ;
1122+ write_accept_language (& buf );
1123+ if (buf .len > 0 )
1124+ cached_accept_language = strbuf_detach (& buf , NULL );
1125+ }
1126+
1127+ return cached_accept_language ;
1128+ }
1129+
9891130/* http_request() targets */
9901131#define HTTP_REQUEST_STRBUF 0
9911132#define HTTP_REQUEST_FILE 1
@@ -998,6 +1139,7 @@ static int http_request(const char *url,
9981139 struct slot_results results ;
9991140 struct curl_slist * headers = NULL ;
10001141 struct strbuf buf = STRBUF_INIT ;
1142+ const char * accept_language ;
10011143 int ret ;
10021144
10031145 slot = get_active_slot ();
@@ -1023,6 +1165,11 @@ static int http_request(const char *url,
10231165 fwrite_buffer );
10241166 }
10251167
1168+ accept_language = get_accept_language ();
1169+
1170+ if (accept_language )
1171+ headers = curl_slist_append (headers , accept_language );
1172+
10261173 strbuf_addstr (& buf , "Pragma:" );
10271174 if (options && options -> no_cache )
10281175 strbuf_addstr (& buf , " no-cache" );
0 commit comments