clocale.pp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team.
  4. Init rtl formating variables based on libc locales
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. { Initial implementation by petr kristan }
  12. unit clocale;
  13. {$mode objfpc}
  14. interface
  15. {$ifdef localedebug}
  16. // for easier debugging, allows to print untransformed values in test
  17. Type TOrgFormatSettings = record
  18. ShortDateFormat,
  19. LongDateFormat ,
  20. ShortTimeFormat,
  21. LongTimeFormat ,
  22. CurrencyString1,
  23. CurrencyString2: string;
  24. end;
  25. var OrgFormatSettings : TOrgFormatSettings;
  26. {$endif}
  27. implementation
  28. {$ifndef symobi}
  29. {$linklib c}
  30. {$endif}
  31. Uses
  32. SysUtils, unixtype, initc;
  33. Const
  34. {$if defined(BSD) or defined(SUNOS)}
  35. // Darwin and FreeBSD. Note the lead underscores are added.
  36. {$i clocale.inc}
  37. {$else}
  38. // checked for Linux only, but might be general glibc.
  39. __LC_CTYPE = 0;
  40. __LC_NUMERIC = 1;
  41. __LC_TIME = 2;
  42. __LC_COLLATE = 3;
  43. __LC_MONETARY = 4;
  44. __LC_MESSAGES = 5;
  45. __LC_ALL = 6;
  46. ABDAY_1 = (__LC_TIME shl 16);
  47. DAY_1 = (ABDAY_1)+7;
  48. ABMON_1 = (ABDAY_1)+14;
  49. MON_1 = (ABDAY_1)+26;
  50. AM_STR = (ABDAY_1)+38;
  51. PM_STR = (ABDAY_1)+39;
  52. D_T_FMT = (ABDAY_1)+40;
  53. D_FMT = (ABDAY_1)+41;
  54. T_FMT = (ABDAY_1)+42;
  55. T_FMT_AMPM = (ABDAY_1)+43;
  56. __DECIMAL_POINT = (__LC_NUMERIC shl 16);
  57. RADIXCHAR = __DECIMAL_POINT;
  58. __THOUSANDS_SEP = (__DECIMAL_POINT)+1;
  59. __INT_CURR_SYMBOL = (__LC_MONETARY shl 16);
  60. __CURRENCY_SYMBOL = (__INT_CURR_SYMBOL)+1;
  61. __MON_DECIMAL_POINT = (__INT_CURR_SYMBOL)+2;
  62. __MON_THOUSANDS_SEP = (__INT_CURR_SYMBOL)+3;
  63. __MON_GROUPING = (__INT_CURR_SYMBOL)+4;
  64. __POSITIVE_SIGN = (__INT_CURR_SYMBOL)+5;
  65. __NEGATIVE_SIGN = (__INT_CURR_SYMBOL)+6;
  66. __INT_FRAC_DIGITS = (__INT_CURR_SYMBOL)+7;
  67. __FRAC_DIGITS = (__INT_CURR_SYMBOL)+8;
  68. __P_CS_PRECEDES = (__INT_CURR_SYMBOL)+9;
  69. __P_SEP_BY_SPACE = (__INT_CURR_SYMBOL)+10;
  70. __N_CS_PRECEDES = (__INT_CURR_SYMBOL)+11;
  71. __N_SEP_BY_SPACE = (__INT_CURR_SYMBOL)+12;
  72. __P_SIGN_POSN = (__INT_CURR_SYMBOL)+13;
  73. __N_SIGN_POSN = (__INT_CURR_SYMBOL)+14;
  74. _NL_MONETARY_CRNCYSTR = (__INT_CURR_SYMBOL)+15;
  75. {$endif}
  76. function setlocale(category: cint; locale: pchar): pchar; cdecl; external clib name 'setlocale';
  77. function nl_langinfo(__item: cint):Pchar;cdecl;external clib name 'nl_langinfo';
  78. procedure GetFormatSettings(out fmts: TFormatSettings);
  79. function GetLocaleStr(item: cint): string;
  80. begin
  81. GetLocaleStr := AnsiString(nl_langinfo(item));
  82. end;
  83. function GetLocaleChar(item: cint): char;
  84. begin
  85. GetLocaleChar := nl_langinfo(item)^;
  86. end;
  87. procedure OmitModifiers(const s: string; var i: integer);
  88. var
  89. l: Integer;
  90. begin
  91. l := Length(s);
  92. //possible flag, with specifier or modifier - glibc exension
  93. while (i<=l) and (s[i] in ['0'..'9', '_', '-', '^', '#', 'E', 'O']) do
  94. inc(i);
  95. end;
  96. function FindSeparator(const s: string; Def: char): char;
  97. var
  98. i: integer;
  99. begin
  100. FindSeparator := Def;
  101. i := Pos('%', s);
  102. if i=0 then
  103. Exit;
  104. inc(i);
  105. OmitModifiers(s, i);
  106. inc(i);
  107. if i<=Length(s) then
  108. FindSeparator := s[i];
  109. end;
  110. function TransformFormatStr(const s: string): string;
  111. var
  112. i, l: integer;
  113. clock12:boolean;
  114. begin
  115. clock12:=false; // should ampm get appended?
  116. TransformFormatStr := '';
  117. i := 1;
  118. l := Length(s);
  119. while i<=l do begin
  120. if s[i]='%' then begin
  121. inc(i);
  122. OmitModifiers(s, i);
  123. if i>l then
  124. Exit;
  125. case s[i] of
  126. 'a': TransformFormatStr := TransformFormatStr + 'ddd';
  127. 'A': TransformFormatStr := TransformFormatStr + 'dddd';
  128. 'b': TransformFormatStr := TransformFormatStr + 'mmm';
  129. 'B': TransformFormatStr := TransformFormatStr + 'mmmm';
  130. 'c': TransformFormatStr := TransformFormatStr + 'c';
  131. //'C':
  132. 'd': TransformFormatStr := TransformFormatStr + 'dd';
  133. 'D': TransformFormatStr := TransformFormatStr + 'mm"/"dd"/"yy';
  134. 'e': TransformFormatStr := TransformFormatStr + 'd';
  135. 'F': TransformFormatStr := TransformFormatStr + 'yyyy-mm-dd';
  136. 'g': TransformFormatStr := TransformFormatStr + 'yy';
  137. 'G': TransformFormatStr := TransformFormatStr + 'yyyy';
  138. 'h': TransformFormatStr := TransformFormatStr + 'mmm';
  139. 'H': TransformFormatStr := TransformFormatStr + 'hh';
  140. 'I': begin
  141. TransformFormatStr := TransformFormatStr + 'hh';
  142. clock12:=true;
  143. end;
  144. //'j':
  145. 'k': TransformFormatStr := TransformFormatStr + 'h';
  146. 'l': begin
  147. TransformFormatStr := TransformFormatStr + 'h';
  148. clock12:=true;
  149. end;
  150. 'm': TransformFormatStr := TransformFormatStr + 'mm';
  151. 'M': TransformFormatStr := TransformFormatStr + 'nn';
  152. 'n': TransformFormatStr := TransformFormatStr + sLineBreak;
  153. 'p','P':
  154. begin
  155. TransformFormatStr := TransformFormatStr + 'ampm';
  156. clock12:=false;
  157. end;
  158. 'r': begin
  159. TransformFormatStr := TransformFormatStr + 'hh:nn:ss';
  160. clock12:=true;
  161. end;
  162. 'R': TransformFormatStr := TransformFormatStr + 'hh:nn';
  163. //'s':
  164. 'S': TransformFormatStr := TransformFormatStr + 'ss';
  165. 't': TransformFormatStr := TransformFormatStr + #9;
  166. 'T': TransformFormatStr := TransformFormatStr + 'hh:nn:ss';
  167. //'u':
  168. //'U':
  169. //'V':
  170. //'w':
  171. //'W':
  172. 'x': TransformFormatStr := TransformFormatStr + 'ddddd';
  173. 'X': TransformFormatStr := TransformFormatStr + 't';
  174. 'y': TransformFormatStr := TransformFormatStr + 'yy';
  175. 'Y': TransformFormatStr := TransformFormatStr + 'yyyy';
  176. //'z':
  177. //'Z':
  178. '%': TransformFormatStr := TransformFormatStr + '%';
  179. end;
  180. end else
  181. TransformFormatStr := TransformFormatStr + s[i];
  182. inc(i);
  183. end;
  184. i:=length(TransformFormatStr);
  185. if clock12 and (i>0) then
  186. begin
  187. if transformformatstr[i]<>' ' then
  188. TransformFormatStr := TransformFormatStr + ' ';
  189. TransformFormatStr := TransformFormatStr + 'ampm';
  190. end;
  191. end;
  192. const
  193. // sign prec sep
  194. NegFormatsTable: array [0..4, 0..1, 0..1] of byte = (
  195. ( (4, 15), (0, 14) ), //Parentheses surround the quantity and currency_symbol
  196. ( (5, 8), (1, 9) ), //The sign string precedes the quantity and currency_symbol
  197. ( (7, 10), (3, 11) ), //The sign string follows the quantity and currency_symbol
  198. ( (6, 13), (1, 9) ), //The sign string immediately precedes the currency_symbol
  199. ( (7, 10), (2, 12) ) //The sign string immediately follows the currency_symbol
  200. );
  201. var
  202. i: integer;
  203. prec, sep, signp: byte;
  204. {$if defined(BSD) or defined(SUNOS)}
  205. plocale : plconv;
  206. {$ENDIF}
  207. begin
  208. setlocale(__LC_ALL,'');
  209. for i := 1 to 12 do
  210. begin
  211. fmts.ShortMonthNames[i]:=GetLocaleStr(ABMON_1+i-1);
  212. fmts.LongMonthNames[i]:=GetLocaleStr(MON_1+i-1);
  213. end;
  214. for i := 1 to 7 do
  215. begin
  216. fmts.ShortDayNames[i]:=GetLocaleStr(ABDAY_1+i-1);
  217. fmts.LongDayNames[i]:=GetLocaleStr(DAY_1+i-1);
  218. end;
  219. //Date stuff
  220. fmts.ShortDateFormat := GetLocaleStr(D_FMT);
  221. {$ifdef localedebug}
  222. OrgFormatSettings.ShortDateFormat:=fmts.shortdateformat;
  223. {$endif}
  224. fmts.DateSeparator := FindSeparator(fmts.ShortDateFormat, fmts.DateSeparator);
  225. fmts.ShortDateFormat := TransformFormatStr(fmts.ShortDateFormat);
  226. fmts.LongDateFormat := GetLocaleStr(D_FMT);
  227. {$ifdef localedebug}
  228. OrgFormatSettings.LongDateFormat:=fmts.longdateformat;
  229. {$endif}
  230. fmts.LongDateFormat := TransformFormatStr(fmts.LongDateFormat);
  231. //Time stuff
  232. fmts.TimeAMString := GetLocaleStr(AM_STR);
  233. fmts.TimePMString := GetLocaleStr(PM_STR);
  234. fmts.ShortTimeFormat := GetLocaleStr(T_FMT);
  235. {$ifdef localedebug}
  236. OrgFormatSettings.ShortTimeFormat:=fmts.shorttimeformat;
  237. {$endif}
  238. fmts.TimeSeparator := FindSeparator(fmts.ShortTimeFormat, fmts.TimeSeparator);
  239. fmts.ShortTimeFormat := TransformFormatStr(fmts.ShortTimeFormat);
  240. fmts.LongTimeFormat := GetLocaleStr(T_FMT_AMPM);
  241. {$ifdef localedebug}
  242. OrgFormatSettings.LongTimeFormat:=fmts.longtimeformat;
  243. {$endif}
  244. if (fmts.LongTimeFormat='') then
  245. fmts.LongTimeFormat:=fmts.ShortTimeFormat
  246. else
  247. fmts.LongTimeFormat := TransformFormatStr(fmts.LongTimeFormat);
  248. {$if defined(BSD) or defined(SUNOS)}
  249. plocale:=localeconv;
  250. // for these fields there is a separate BSD derived POSIX function.
  251. if not assigned(plocale) then exit; // for now.
  252. fmts.CurrencyString:=plocale^.currency_symbol; // int_CURR_SYMBOL (in latin chars)
  253. if fmts.CurrencyString='' then
  254. fmts.CurrencyString:=plocale^.int_curr_symbol;
  255. fmts.CurrencyDecimals:=ord(plocale^.FRAC_DIGITS);
  256. {$ifdef localedebug}
  257. OrgFormatSettings.CurrencyString1:=plocale^.currency_symbol;
  258. OrgFormatSettings.CurrencyString2:=plocale^.int_curr_symbol;
  259. {$endif}
  260. prec:=ord(plocale^.P_CS_PRECEDES);
  261. sep:=ord(plocale^.P_SEP_BY_SPACE);
  262. if (prec<=1) and (sep<=1) then
  263. fmts.CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  264. prec := ord(plocale^.N_CS_PRECEDES);
  265. sep := ord(plocale^.N_SEP_BY_SPACE);
  266. signp := ord(plocale^.N_SIGN_POSN);
  267. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  268. fmts.NegCurrFormat := NegFormatsTable[signp, prec, sep];
  269. //Number stuff
  270. fmts.ThousandSeparator:=plocale^.THOUSANDS_SEP[0];
  271. {$else}
  272. //Currency stuff
  273. fmts.CurrencyString := GetLocaleStr(_NL_MONETARY_CRNCYSTR);
  274. {$ifdef localedebug}
  275. OrgFormatSettings.CurrencyString1:=fmts.currencystring;
  276. OrgFormatSettings.CurrencyString2:='';
  277. {$endif}
  278. fmts.CurrencyString := Copy(fmts.CurrencyString, 2, Length(fmts.CurrencyString));
  279. fmts.CurrencyDecimals := StrToIntDef(GetLocaleStr(__FRAC_DIGITS), fmts.CurrencyDecimals);
  280. prec := byte(GetLocaleChar(__P_CS_PRECEDES));
  281. sep := byte(GetLocaleChar(__P_SEP_BY_SPACE));
  282. if (prec<=1) and (sep<=1) then
  283. fmts.CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  284. prec := byte(GetLocaleChar(__N_CS_PRECEDES));
  285. sep := byte(GetLocaleChar(__N_SEP_BY_SPACE));
  286. signp := byte(GetLocaleChar(__N_SIGN_POSN));
  287. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  288. fmts.NegCurrFormat := NegFormatsTable[signp, prec, sep];
  289. //Number stuff
  290. fmts.ThousandSeparator:=GetLocaleChar(__THOUSANDS_SEP);
  291. Sep := ord(GetLocaleChar(__MON_THOUSANDS_SEP));
  292. if fmts.ThousandSeparator=#0 then
  293. fmts.ThousandSeparator := char(Sep);
  294. {$endif}
  295. fmts.DecimalSeparator:=GetLocaleChar(RADIXCHAR);
  296. end;
  297. initialization
  298. GetFormatSettings(DefaultFormatSettings);
  299. end.