Speculative fix for crash with untranslated messages
[ardour.git] / libs / pbd / convert.cc
1 /*
2     Copyright (C) 2006 Paul Davis 
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 as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8
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.
13
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
18 */
19
20 #include <cmath>
21 #include <stdint.h>
22 #include <stdlib.h>
23 #include <cstdio>
24 #include <ctype.h>
25 #include <cstring>
26 #ifndef __STDC_FORMAT_MACROS
27 #define __STDC_FORMAT_MACROS
28 #endif
29 #include <inttypes.h>
30
31 #include "pbd/convert.h"
32
33 #include "i18n.h"
34
35 using std::string;
36 using std::vector;
37 using Glib::ustring;
38
39 namespace PBD {
40
41 string
42 capitalize (const string& str)
43 {
44         string ret = str;
45         if (!str.empty()) {
46                 /* XXX not unicode safe */
47                 ret[0] = toupper (str[0]);
48         }
49         return ret;
50 }
51
52 string
53 short_version (string orig, string::size_type target_length)
54 {
55         /* this tries to create a recognizable abbreviation
56            of "orig" by removing characters until we meet
57            a certain target length.
58
59            note that we deliberately leave digits in the result
60            without modification.
61         */
62
63
64         string::size_type pos;
65
66         /* remove white-space and punctuation, starting at end */
67
68         while (orig.length() > target_length) {
69                 if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
70                         break;
71                 }
72                 orig.replace (pos, 1, "");
73         }
74
75         /* remove lower-case vowels, starting at end */
76
77         while (orig.length() > target_length) {
78                 if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
79                         break;
80                 }
81                 orig.replace (pos, 1, "");
82         }
83
84         /* remove upper-case vowels, starting at end */
85
86         while (orig.length() > target_length) {
87                 if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
88                         break;
89                 }
90                 orig.replace (pos, 1, "");
91         }
92
93         /* remove lower-case consonants, starting at end */
94
95         while (orig.length() > target_length) {
96                 if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
97                         break;
98                 }
99                 orig.replace (pos, 1, "");
100         }
101
102         /* remove upper-case consonants, starting at end */
103
104         while (orig.length() > target_length) {
105                 if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
106                         break;
107                 }
108                 orig.replace (pos, 1, "");
109         }
110
111         /* whatever the length is now, use it */
112         
113         return orig;
114 }
115
116 int
117 atoi (const string& s)
118 {
119         return ::atoi (s.c_str());
120 }
121
122 int32_t
123 atol (const string& s)
124 {
125         return (int32_t) ::atol (s.c_str());
126 }
127
128 int64_t
129 atoll (const string& s)
130 {
131         return (int64_t) ::atoll (s.c_str());
132 }
133
134 double
135 atof (const string& s)
136 {
137         return ::atof (s.c_str());
138 }
139
140 vector<string>
141 internationalize (const char *package_name, const char **array)
142 {
143         vector<string> v;
144
145         for (uint32_t i = 0; array[i]; ++i) {
146                 v.push_back (dgettext(package_name, array[i]));
147         }
148
149         return v;
150 }
151
152 static int32_t 
153 int_from_hex (char hic, char loc) 
154 {
155         int hi;         /* hi byte */
156         int lo;         /* low byte */
157
158         hi = (int) hic;
159
160         if( ('0'<=hi) && (hi<='9') ) {
161                 hi -= '0';
162         } else if( ('a'<= hi) && (hi<= 'f') ) {
163                 hi -= ('a'-10);
164         } else if( ('A'<=hi) && (hi<='F') ) {
165                 hi -= ('A'-10);
166         }
167         
168         lo = (int) loc;
169         
170         if( ('0'<=lo) && (lo<='9') ) {
171                 lo -= '0';
172         } else if( ('a'<=lo) && (lo<='f') ) {
173                 lo -= ('a'-10);
174         } else if( ('A'<=lo) && (lo<='F') ) {
175                 lo -= ('A'-10);
176         }
177
178         return lo + (16 * hi);
179 }
180
181 void
182 url_decode (string& url)
183 {
184         string::iterator last;
185         string::iterator next;
186
187         for (string::iterator i = url.begin(); i != url.end(); ++i) {
188                 if ((*i) == '+') {
189                         *i = ' ';
190                 }
191         }
192
193         if (url.length() <= 3) {
194                 return;
195         }
196
197         last = url.end();
198
199         --last; /* points at last char */
200         --last; /* points at last char - 1 */
201
202         for (string::iterator i = url.begin(); i != last; ) {
203
204                 if (*i == '%') {
205
206                         next = i;
207
208                         url.erase (i);
209                         
210                         i = next;
211                         ++next;
212                         
213                         if (isxdigit (*i) && isxdigit (*next)) {
214                                 /* replace first digit with char */
215                                 *i = int_from_hex (*i,*next);
216                                 ++i; /* points at 2nd of 2 digits */
217                                 url.erase (i);
218                         }
219                 } else {
220                         ++i;
221                 }
222         }
223 }
224
225 void
226 url_decode (ustring& url)
227 {
228         ustring::iterator last;
229         ustring::iterator next;
230
231         for (ustring::iterator i = url.begin(); i != url.end(); ++i) {
232                 if ((*i) == '+') {
233                         next = i;
234                         ++next;
235                         url.replace (i, next, 1, ' ');
236                 }
237         }
238
239         if (url.length() <= 3) {
240                 return;
241         }
242
243         last = url.end();
244
245         --last; /* points at last char */
246         --last; /* points at last char - 1 */
247
248         for (ustring::iterator i = url.begin(); i != last; ) {
249
250                 if (*i == '%') {
251
252                         next = i;
253
254                         url.erase (i);
255                         
256                         i = next;
257                         ++next;
258                         
259                         if (isxdigit (*i) && isxdigit (*next)) {
260                                 /* replace first digit with char */
261                                 url.replace (i, next, 1, (gunichar) int_from_hex (*i,*next));
262                                 ++i; /* points at 2nd of 2 digits */
263                                 url.erase (i);
264                         }
265                 } else {
266                         ++i;
267                 }
268         }
269 }
270
271 #if 0
272 string
273 length2string (const int32_t frames, const float sample_rate)
274 {
275     int32_t secs = (int32_t) (frames / sample_rate);
276     int32_t hrs =  secs / 3600;
277     secs -= (hrs * 3600);
278     int32_t mins = secs / 60;
279     secs -= (mins * 60);
280
281     int32_t total_secs = (hrs * 3600) + (mins * 60) + secs;
282     int32_t frames_remaining = (int) floor (frames - (total_secs * sample_rate));
283     float fractional_secs = (float) frames_remaining / sample_rate;
284
285     char duration_str[32];
286     sprintf (duration_str, "%02" PRIi32 ":%02" PRIi32 ":%05.2f", hrs, mins, (float) secs + fractional_secs);
287
288     return duration_str;
289 }
290 #endif
291
292 string
293 length2string (const int64_t frames, const double sample_rate)
294 {
295         int64_t secs = (int64_t) floor (frames / sample_rate);
296         int64_t hrs =  secs / 3600LL;
297         secs -= (hrs * 3600LL);
298         int64_t mins = secs / 60LL;
299         secs -= (mins * 60LL);
300         
301         int64_t total_secs = (hrs * 3600LL) + (mins * 60LL) + secs;
302         int64_t frames_remaining = (int64_t) floor (frames - (total_secs * sample_rate));
303         float fractional_secs = (float) frames_remaining / sample_rate;
304         
305         char duration_str[64];
306         sprintf (duration_str, "%02" PRIi64 ":%02" PRIi64 ":%05.2f", hrs, mins, (float) secs + fractional_secs);
307         
308         return duration_str;
309 }
310
311 static bool 
312 chars_equal_ignore_case(char x, char y)
313 {
314         /* app should have called setlocale() if its wants this comparison to be
315            locale sensitive.
316         */
317         return toupper (x) == toupper (y);
318 }
319
320 bool 
321 strings_equal_ignore_case (const string& a, const string& b)
322 {
323         if (a.length() == b.length()) {
324                 return std::equal (a.begin(), a.end(), b.begin(), chars_equal_ignore_case);
325         }
326         return false;
327 }
328
329 /** A wrapper for dgettext that takes a msgid of the form Context|Text.
330  *  If Context|Text is translated, the translation is returned, otherwise
331  *  just Text is returned.  Useful for getting translations of words or phrases
332  *  that have different meanings in different contexts.
333  */
334 const char *
335 sgettext (const char* domain_name, const char* msgid)
336 {
337         const char * msgval = dgettext (domain_name, msgid);
338         if (msgval == msgid) {
339                 const char * p = strrchr (msgid, '|');
340                 if (p) {
341                         msgval = p + 1;
342                 }
343         }
344         return msgval;
345 }
346
347 } // namespace PBD