Merge branch 'master' into cairocanvas
[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 <glib.h>
32
33 #include "pbd/convert.h"
34
35 #include "i18n.h"
36
37 using std::string;
38 using std::vector;
39 using Glib::ustring;
40
41 namespace PBD {
42
43 string
44 capitalize (const string& str)
45 {
46         string ret = str;
47         if (!str.empty()) {
48                 /* XXX not unicode safe */
49                 ret[0] = toupper (str[0]);
50         }
51         return ret;
52 }
53
54 string
55 short_version (string orig, string::size_type target_length)
56 {
57         /* this tries to create a recognizable abbreviation
58            of "orig" by removing characters until we meet
59            a certain target length.
60
61            note that we deliberately leave digits in the result
62            without modification.
63         */
64
65
66         string::size_type pos;
67
68         /* remove white-space and punctuation, starting at end */
69
70         while (orig.length() > target_length) {
71                 if ((pos = orig.find_last_of (_("\"\n\t ,<.>/?:;'[{}]~`!@#$%^&*()_-+="))) == string::npos) {
72                         break;
73                 }
74                 orig.replace (pos, 1, "");
75         }
76
77         /* remove lower-case vowels, starting at end */
78
79         while (orig.length() > target_length) {
80                 if ((pos = orig.find_last_of (_("aeiou"))) == string::npos) {
81                         break;
82                 }
83                 orig.replace (pos, 1, "");
84         }
85
86         /* remove upper-case vowels, starting at end */
87
88         while (orig.length() > target_length) {
89                 if ((pos = orig.find_last_of (_("AEIOU"))) == string::npos) {
90                         break;
91                 }
92                 orig.replace (pos, 1, "");
93         }
94
95         /* remove lower-case consonants, starting at end */
96
97         while (orig.length() > target_length) {
98                 if ((pos = orig.find_last_of (_("bcdfghjklmnpqrtvwxyz"))) == string::npos) {
99                         break;
100                 }
101                 orig.replace (pos, 1, "");
102         }
103
104         /* remove upper-case consonants, starting at end */
105
106         while (orig.length() > target_length) {
107                 if ((pos = orig.find_last_of (_("BCDFGHJKLMNPQRTVWXYZ"))) == string::npos) {
108                         break;
109                 }
110                 orig.replace (pos, 1, "");
111         }
112
113         /* whatever the length is now, use it */
114         
115         return orig;
116 }
117
118 int
119 atoi (const string& s)
120 {
121         return ::atoi (s.c_str());
122 }
123
124 int32_t
125 atol (const string& s)
126 {
127         return (int32_t) ::atol (s.c_str());
128 }
129
130 int64_t
131 atoll (const string& s)
132 {
133         return (int64_t) ::atoll (s.c_str());
134 }
135
136 double
137 atof (const string& s)
138 {
139         return ::atof (s.c_str());
140 }
141
142 vector<string>
143 internationalize (const char *package_name, const char **array)
144 {
145         vector<string> v;
146
147         for (uint32_t i = 0; array[i]; ++i) {
148                 v.push_back (dgettext(package_name, array[i]));
149         }
150
151         return v;
152 }
153
154 static int32_t 
155 int_from_hex (char hic, char loc) 
156 {
157         int hi;         /* hi byte */
158         int lo;         /* low byte */
159
160         hi = (int) hic;
161
162         if( ('0'<=hi) && (hi<='9') ) {
163                 hi -= '0';
164         } else if( ('a'<= hi) && (hi<= 'f') ) {
165                 hi -= ('a'-10);
166         } else if( ('A'<=hi) && (hi<='F') ) {
167                 hi -= ('A'-10);
168         }
169         
170         lo = (int) loc;
171         
172         if( ('0'<=lo) && (lo<='9') ) {
173                 lo -= '0';
174         } else if( ('a'<=lo) && (lo<='f') ) {
175                 lo -= ('a'-10);
176         } else if( ('A'<=lo) && (lo<='F') ) {
177                 lo -= ('A'-10);
178         }
179
180         return lo + (16 * hi);
181 }
182
183 string
184 url_decode (string const & url)
185 {
186         string decoded;
187
188         for (string::size_type i = 0; i < url.length(); ++i) {
189                 if (url[i] == '+') {
190                         decoded += ' ';
191                 } else if (url[i] == '%' && i <= url.length() - 3) {
192                         decoded += char (int_from_hex (url[i + 1], url[i + 2]));
193                         i += 2;
194                 } else {
195                         decoded += url[i];
196                 }
197         }
198
199         return decoded;
200 }
201
202 #if 0
203 string
204 length2string (const int32_t frames, const float sample_rate)
205 {
206     int32_t secs = (int32_t) (frames / sample_rate);
207     int32_t hrs =  secs / 3600;
208     secs -= (hrs * 3600);
209     int32_t mins = secs / 60;
210     secs -= (mins * 60);
211
212     int32_t total_secs = (hrs * 3600) + (mins * 60) + secs;
213     int32_t frames_remaining = (int) floor (frames - (total_secs * sample_rate));
214     float fractional_secs = (float) frames_remaining / sample_rate;
215
216     char duration_str[32];
217     sprintf (duration_str, "%02" PRIi32 ":%02" PRIi32 ":%05.2f", hrs, mins, (float) secs + fractional_secs);
218
219     return duration_str;
220 }
221 #endif
222
223 string
224 length2string (const int64_t frames, const double sample_rate)
225 {
226         int64_t secs = (int64_t) floor (frames / sample_rate);
227         int64_t hrs =  secs / 3600LL;
228         secs -= (hrs * 3600LL);
229         int64_t mins = secs / 60LL;
230         secs -= (mins * 60LL);
231         
232         int64_t total_secs = (hrs * 3600LL) + (mins * 60LL) + secs;
233         int64_t frames_remaining = (int64_t) floor (frames - (total_secs * sample_rate));
234         float fractional_secs = (float) frames_remaining / sample_rate;
235         
236         char duration_str[64];
237         sprintf (duration_str, "%02" PRIi64 ":%02" PRIi64 ":%05.2f", hrs, mins, (float) secs + fractional_secs);
238         
239         return duration_str;
240 }
241
242 static bool 
243 chars_equal_ignore_case(char x, char y)
244 {
245         /* app should have called setlocale() if its wants this comparison to be
246            locale sensitive.
247         */
248         return toupper (x) == toupper (y);
249 }
250
251 bool 
252 strings_equal_ignore_case (const string& a, const string& b)
253 {
254         if (a.length() == b.length()) {
255                 return std::equal (a.begin(), a.end(), b.begin(), chars_equal_ignore_case);
256         }
257         return false;
258 }
259
260 bool
261 string_is_affirmative (const std::string& str)
262 {
263         /* to be used only with XML data - not intended to handle user input */
264
265         if (str.empty ()) {
266                 return false;
267         }
268
269         /* the use of g_ascii_strncasecmp() is solely to get around issues with
270          * charsets posed by trying to use C++ for the same
271          * comparison. switching a std::string to its lower- or upper-case
272          * version has several issues, but handled by default
273          * in the way we desire when doing it in C.
274          */
275
276         return str == "1" || str == "y" || str == "Y" || (!g_ascii_strncasecmp(str.c_str(), "yes", str.length())) ||
277                 (!g_ascii_strncasecmp(str.c_str(), "true", str.length()));
278 }
279
280 /** A wrapper for dgettext that takes a msgid of the form Context|Text.
281  *  If Context|Text is translated, the translation is returned, otherwise
282  *  just Text is returned.  Useful for getting translations of words or phrases
283  *  that have different meanings in different contexts.
284  */
285 const char *
286 sgettext (const char* domain_name, const char* msgid)
287 {
288         const char * msgval = dgettext (domain_name, msgid);
289         if (msgval == msgid) {
290                 const char * p = strrchr (msgid, '|');
291                 if (p) {
292                         msgval = p + 1;
293                 }
294         }
295         return msgval;
296 }
297
298 } // namespace PBD