source: rxuls/trunk/rxuls.c@ 28

Last change on this file since 28 was 28, checked in by Alex Taylor, 10 years ago

Initial import of RXULS.

File size: 110.2 KB
Line 
1/******************************************************************************
2 * REXX Universal Language Support library (RXULS.DLL) *
3 * (C) 2008 Alex Taylor. *
4 * *
5 * LICENSE: *
6 * *
7 * Redistribution and use in source and binary forms, with or without *
8 * modification, are permitted provided that the following conditions are *
9 * met: *
10 * *
11 * 1. Redistributions of source code must retain the above copyright *
12 * notice, this list of conditions and the following disclaimer. *
13 * *
14 * 2. Redistributions in binary form must reproduce the above copyright *
15 * notice, this list of conditions and the following disclaimer in the *
16 * documentation and/or other materials provided with the distribution. *
17 * *
18 * 3. The name of the author may not be used to endorse or promote products *
19 * derived from this software without specific prior written permission. *
20 * *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ''AS IS'' AND ANY EXPRESS OR *
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED *
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
24 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, *
25 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES *
26 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR *
27 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) *
28 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, *
29 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN *
30 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE *
31 * POSSIBILITY OF SUCH DAMAGE. *
32 * *
33 ******************************************************************************/
34
35#define INCL_WINATOM
36#define INCL_WINCLIPBOARD
37#define INCL_WINERRORS
38#define INCL_DOSPROCESS
39#ifndef OS2_INCLUDED
40 #include <os2.h>
41#endif
42#include <stdio.h>
43#include <stdlib.h>
44#include <string.h>
45#include <ctype.h>
46#include <time.h>
47// #include <errno.h>
48#include <uconv.h>
49
50#define INCL_RXSHV
51#define INCL_RXFUNC
52#include <rexxsaa.h>
53
54
55// MACROS
56
57// Convert UniChars to and from separate byte values
58#define UNICHARFROM2BYTES( bFirst, bSecond ) (( bFirst << 8 ) | bSecond )
59#define BYTE1FROMUNICHAR( uniChar ) ( uniChar >> 8 )
60#define BYTE2FROMUNICHAR( uniChar ) ( uniChar & 0x00FF )
61
62
63// CONSTANTS
64
65#define SZ_LIBRARY_NAME "RXULS" // Name of this library
66#define SZ_ERROR_NAME "ULSERR" // REXX variable used to store error codes
67
68#define US_VERSION_MAJOR 0 // Major version number of this library
69#define US_VERSION_MINOR 5 // Minor version number of this library
70#define US_VERSION_REFRESH 9 // Refresh level of this library
71
72#define US_CPSPEC_MAXZ 72 // Maximum length of a conversion specifier
73#define LOCALE_BUF_MAX 4096 // Maximum length of the locale list
74
75#define US_COMPOUND_MAXZ 250 // Maximum length of a compound variable
76#define US_INTEGER_MAXZ 12 // Maximum length of an integer string
77#define US_STEM_MAXZ ( US_COMPOUND_MAXZ - US_INTEGER_MAXZ ) // Maximum length of a stem
78#define US_ERRSTR_MAXZ 250 // Maximum length of an error string
79#define US_STRFTIME_MAXZ 256 // Maximum length of a formatted time string
80
81// Values for conversion modifiers (0 values are defaults)
82#define CONTROLS_MAP_DATA 0 // Use "@map=data"
83#define CONTROLS_MAP_DISPLAY 1 // Use "@map=display"
84#define CONTROLS_MAP_CDRA 2 // Use "@map=cdra"
85#define CONTROLS_MAP_CRLF 3 // Use "@map=crlf"
86
87#define UCS_ENDIAN_SYSTEM 0 // Use "@endian=system"
88#define UCS_ENDIAN_BIG 1 // Use "@endian=big"
89#define UCS_ENDIAN_LITTLE UCS_ENDIAN_SYSTEM
90
91#define DBCS_PATH_YES 0 // Use "@path=yes"
92#define DBCS_PATH_NO 1 // Use "@path=no"
93
94#define CONV_FLAG_INVALID 0xFFFF // An invalid value was specified
95
96// List of functions to be registered by ULSLoadFuncs
97static PSZ RxFunctionTbl[] = {
98 "ULSConvertCodepage",
99 "ULSCountryLocale",
100 "ULSDropFuncs",
101 "ULSFindAttr",
102 "ULSFormatTime",
103 "ULSGetLocales",
104 "ULSGetUnicodeClipboard",
105 "ULSPutUnicodeClipboard",
106 "ULSQueryAttr",
107 "ULSQueryLocaleItem",
108 "ULSTransform",
109 "ULSVersion"
110};
111
112
113// FUNCTION DECLARATIONS
114
115// Exported REXX functions
116RexxFunctionHandler ULSConvertCodepage;
117RexxFunctionHandler ULSCountryLocale;
118RexxFunctionHandler ULSDropFuncs;
119RexxFunctionHandler ULSFindAttr;
120RexxFunctionHandler ULSFormatTime;
121RexxFunctionHandler ULSGetLocales;
122RexxFunctionHandler ULSGetUnicodeClipboard;
123RexxFunctionHandler ULSLoadFuncs;
124RexxFunctionHandler ULSPutUnicodeClipboard;
125RexxFunctionHandler ULSQueryAttr;
126RexxFunctionHandler ULSQueryLocaleItem;
127RexxFunctionHandler ULSTransform;
128RexxFunctionHandler ULSVersion;
129
130
131// Private internal functions
132USHORT ParseUconvMapFlag( RXSTRING rxstring );
133USHORT ParseUconvPathFlag( RXSTRING rxstring );
134ULONG CreateUconvObject( UconvObject *uco, ULONG ulCP, USHORT fEndian, USHORT fMap, USHORT fPath, USHORT ucSubChar );
135BOOL InputStringToUnicode( RXSTRING rxstring, UniChar **psuOutput, UconvObject uconv, ULONG ulSourceCP );
136BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes );
137void WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue );
138void WriteErrorCode( ULONG ulError, PSZ pszContext );
139
140
141// Used for debugging: dump a UCS-2 string to STDOUT in hexadecimal.
142void DumpUCS( UniChar *psu )
143{
144 UniChar c;
145 if ( psu == NULL ) return;
146 for ( c = 0; psu[c] > 0; c++ )
147 printf("%04X ", psu[c] );
148 printf("\n");
149}
150
151
152/* ------------------------------------------------------------------------- *
153 * ULSLoadFuncs *
154 * *
155 * Should be self-explanatory... *
156 * *
157 * REXX ARGUMENTS: None *
158 * REXX RETURN VALUE: "" *
159 * ------------------------------------------------------------------------- */
160ULONG APIENTRY ULSLoadFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
161{
162 int entries,
163 i;
164
165 // Reset the error indicator
166 WriteErrorCode( 0, NULL );
167
168 if ( argc > 0 ) return ( 40 );
169 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
170 for ( i = 0; i < entries; i++ )
171 RexxRegisterFunctionDll( RxFunctionTbl[i], SZ_LIBRARY_NAME, RxFunctionTbl[i] );
172
173 MAKERXSTRING( *prsResult, "", 0 );
174 return ( 0 );
175}
176
177
178/* ------------------------------------------------------------------------- *
179 * ULSDropFuncs *
180 * *
181 * Ditto. *
182 * *
183 * REXX ARGUMENTS: None *
184 * REXX RETURN VALUE: "" *
185 * ------------------------------------------------------------------------- */
186ULONG APIENTRY ULSDropFuncs( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
187{
188 int entries,
189 i;
190
191 // Reset the error indicator
192 WriteErrorCode( 0, NULL );
193
194 if ( argc > 0 ) return ( 40 );
195 entries = sizeof(RxFunctionTbl) / sizeof(PSZ);
196 for ( i = 0; i < entries; i++ )
197 RexxDeregisterFunction( RxFunctionTbl[i] );
198
199 MAKERXSTRING( *prsResult, "", 0 );
200 return ( 0 );
201}
202
203
204/* ------------------------------------------------------------------------- *
205 * ULSVersion *
206 * *
207 * Returns the current RXULS library version. *
208 * *
209 * REXX ARGUMENTS: None *
210 * REXX RETURN VALUE: Current version in the form "major.minor.refresh" *
211 * ------------------------------------------------------------------------- */
212ULONG APIENTRY ULSVersion( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
213{
214 CHAR szVersion[ 12 ];
215
216 // Reset the error indicator
217 WriteErrorCode( 0, NULL );
218
219 if ( argc > 0 ) return ( 40 );
220 sprintf( szVersion, "%d.%d.%d", US_VERSION_MAJOR, US_VERSION_MINOR, US_VERSION_REFRESH );
221
222 MAKERXSTRING( *prsResult, szVersion, strlen(szVersion) );
223 return ( 0 );
224}
225
226
227/* ------------------------------------------------------------------------- *
228 * ULSConvertCodepage *
229 * *
230 * Convert a string from one codepage to another. *
231 * *
232 * REXX ARGUMENTS: *
233 * 1. String to be converted (REQUIRED) *
234 * 2. Source codepage number (DEFAULT: current) *
235 * 3. Target codepage number (DEFAULT: current) *
236 * 4. Substitution character (hex) (DEFAULT: varies by codepage) *
237 * 5. Control flag (how to treat byte values 0x00-0x19,0x7F), one of: *
238 * D = data, treat as control bytes and do not convert (DEFAULT) *
239 * G = glyphs, treat as glyphs and convert according to codepage *
240 * C = cdra, treat as control bytes and convert using IBM mapping *
241 * L = linebreak, treat CR and LF as controls, all others as glyphs *
242 * 6. Path conversion flag (only applies to DBCS codepages), one of: *
243 * Y = yes, assume string contains a path specifier (DEFAULT) *
244 * N = no, assume string doesn't contain a path specifier *
245 * *
246 * REXX RETURN VALUE: Converted string *
247 * ------------------------------------------------------------------------- */
248ULONG APIENTRY ULSConvertCodepage( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
249{
250 UconvObject uconvSource = NULL, // conversion object for source codepage
251 uconvTarget = NULL; // conversion object for target codepage
252 UniChar *psuConverted, // converted (intermediate) UCS-2 string
253 *psuOffset; // pointer to current UniChar
254 PSZ pszFinal, // converted final string
255 pszOffset; // pointer to current character
256 ULONG ulSourceCP = 0, // requested source codepage
257 ulTargetCP = 0, // requested target codepage
258 ulRC = 0, // return code
259 ulBytes = 0, // size in bytes of output string
260 i, j; // loop indices
261 USHORT fMap = 0, // control-mapping flag
262 fPath = 0, // path-conversion flag
263 usSub = 0; // substitution character value
264 size_t stInLength = 0, // length of input buffer (UniUconv*Ucs)
265 stOutLength = 0, // length of output buffer (UniUconv*Ucs)
266 stSubstitutions = 0; // substitution count (UniUconv*Ucs)
267
268
269 // Reset the error indicator
270 WriteErrorCode( 0, NULL );
271
272 // Make sure we have at least one valid argument (the input string)
273 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
274 // Other arguments: these are optional, but must be correct if specified
275 if ( argc >= 2 ) {
276 // second argument: source codepage
277 if ( RXVALIDSTRING(argv[1]) && sscanf(argv[1].strptr, "%u", &ulSourceCP) < 1 )
278 return ( 40 );
279 }
280 if ( argc >= 3 ) {
281 // third argument: target codepage
282 if ( RXVALIDSTRING(argv[2]) && sscanf(argv[2].strptr, "%u", &ulTargetCP) < 1 )
283 return ( 40 );
284 }
285 if ( argc >= 4 ) {
286 // fourth argument: substitution character
287 if ( RXVALIDSTRING(argv[3]) && sscanf(argv[3].strptr, "%4X", &usSub ) < 1 )
288 return ( 40 );
289 }
290 if ( argc >= 5 ) {
291 // fifth argument: control flag
292 if (( fMap = ParseUconvMapFlag( argv[4] )) == CONV_FLAG_INVALID )
293 return ( 40 );
294 }
295 if ( argc >= 6 ) {
296 // sixth argument: path flag
297 if (( fPath = ParseUconvPathFlag( argv[5] )) == CONV_FLAG_INVALID )
298 return ( 40 );
299 }
300
301 // If source and target codepages are the same, just return the input string
302 if ( ulSourceCP == ulTargetCP ) {
303 MAKERXSTRING( *prsResult, argv[0].strptr, argv[0].strlength );
304 return ( 0 );
305 }
306
307 // Create the source-to-UCS conversion object
308 if ( ulSourceCP != 1200 )
309 ulRC = CreateUconvObject( &uconvSource, ulSourceCP, UCS_ENDIAN_SYSTEM, fMap, fPath, usSub );
310
311 if ( ulRC == ULS_SUCCESS ) {
312
313 if ( InputStringToUnicode( argv[0], &psuConverted, uconvSource, ulSourceCP )) {
314/*
315 if ( ulSourceCP == 1200 ) {
316 // Input string is already UCS-2; read it directly into a UniChar array
317 psuConverted = (UniChar *) malloc( argv[0].strlength + sizeof(UniChar) );
318 ulChars = argv[0].strlength / 2;
319 j = 0;
320 for ( i = 0; i < ulChars; i++ ) {
321 ucFirst = argv[0].strptr[ j++ ];
322 ucSecond = argv[0].strptr[ j++ ];
323 psuConverted[ i ] = UNICHARFROM2BYTES( ucFirst, ucSecond );
324 }
325 psuConverted[ i ] = 0x0000;
326 } else {
327 // Convert the input string to UCS-2
328 stInLength = argv[0].strlength;
329 stOutLength = stInLength;
330 psuConverted = (UniChar *) calloc( stOutLength + 1, sizeof(UniChar) );
331 pszOffset = argv[0].strptr;
332 psuOffset = psuConverted;
333 memset( psuConverted, 0, (stOutLength+1) * sizeof(UniChar) );
334 ulRC = UniUconvToUcs( uconvSource, (PPVOID) &pszOffset, &stInLength,
335 &psuOffset, &stOutLength, &stSubstitutions );
336 }
337 if ( ulRC == ULS_SUCCESS ) {
338*/
339 if ( ulTargetCP == 1200 ) {
340 // Converting to UCS-2; simply return the UniChar array as bytes
341 ulBytes = UniStrlen( psuConverted ) * sizeof( UniChar );
342 pszFinal = (PSZ) malloc( ulBytes );
343 j = 0;
344 for ( i = 0; i < UniStrlen(psuConverted); i++ ) {
345 pszFinal[j++] = BYTE1FROMUNICHAR( psuConverted[i] );
346 pszFinal[j++] = BYTE2FROMUNICHAR( psuConverted[i] );
347 }
348 if ( ! SaveResultString( prsResult, pszFinal, ulBytes )) {
349 MAKERXSTRING( *prsResult, "", 0 );
350 }
351 free( pszFinal );
352 } else {
353 // Create the UCS-to-target conversion object
354 ulRC = CreateUconvObject( &uconvTarget, ulTargetCP, UCS_ENDIAN_SYSTEM, fMap, fPath, usSub );
355 if ( ulRC == ULS_SUCCESS ) {
356
357 // Convert the string to the target codepage
358 // (Allow up to 4x the length of the original string)
359 stInLength = UniStrlen( psuConverted );
360 stOutLength = ( argv[0].strlength * 4 );
361 pszFinal = (PSZ) malloc( stOutLength + 1 );
362 psuOffset = psuConverted;
363 pszOffset = pszFinal;
364 stSubstitutions = 0;
365 memset( pszFinal, 0, stOutLength + 1 );
366 ulRC = UniUconvFromUcs( uconvTarget, &psuOffset, &stInLength,
367 (PVOID *) &pszOffset, &stOutLength, &stSubstitutions );
368 if ( ulRC == ULS_SUCCESS ) {
369 // Return the final converted string
370 if ( ! SaveResultString( prsResult, pszFinal, strlen(pszFinal) )) {
371 MAKERXSTRING( *prsResult, "", 0 );
372 }
373 } else {
374 // UniUconvFromUcs failed
375 WriteErrorCode( ulRC, "UniUconvFromUcs");
376 MAKERXSTRING( *prsResult, "", 0 );
377 }
378 free( pszFinal );
379 UniFreeUconvObject( uconvTarget );
380
381 } else {
382 // Failed to create target UconvObject
383 WriteErrorCode( ulRC, "UniCreateUconvObject");
384 MAKERXSTRING( *prsResult, "", 0 );
385 }
386 }
387 free( psuConverted );
388
389 } else {
390 // Conversion to UCS failed
391 MAKERXSTRING( *prsResult, "", 0 );
392 }
393
394 if ( ulSourceCP != 1200 )
395 UniFreeUconvObject( uconvSource );
396
397 } else {
398 // Failed to create source UconvObject
399 WriteErrorCode( ulRC, "UniCreateUconvObject");
400 MAKERXSTRING( *prsResult, "", 0 );
401 }
402
403 return ( 0 );
404}
405
406
407/* ------------------------------------------------------------------------- *
408 * ULSCountryLocale *
409 * *
410 * Returns the locale name associated with the specified integer identifier. *
411 * This integer is almost, but not quite, the same as the standard OS/2 *
412 * "country code" as used by DosQueryDBCSEnv(). Since it ISN'T quite the *
413 * same, this function is probably of rather limited usefulness; for this *
414 * reason, and to help prevent confusion on the issue, I may end up leaving *
415 * it out of the public RXULS documentation. *
416 * *
417 * REXX ARGUMENTS: *
418 * 1. The numeric locale code being queried. *
419 * *
420 * REXX RETURN VALUE: Locale name *
421 * ------------------------------------------------------------------------- */
422ULONG APIENTRY ULSCountryLocale( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
423{
424 UconvObject uconv;
425 UniChar suLocale[ ULS_LNAMEMAX ];
426 CHAR szLocale[ ULS_LNAMEMAX ];
427 ULONG ulCountry,
428 ulRC;
429
430 // Reset the error indicator
431 WriteErrorCode( 0, NULL );
432
433 // Make sure we have exactly one valid argument
434 if ( argc != 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
435 if ( RXVALIDSTRING(argv[0]) && sscanf(argv[0].strptr, "%d", &ulCountry) < 1 )
436 return ( 40 );
437
438 ulRC = UniMapCtryToLocale( ulCountry, suLocale, ULS_LNAMEMAX );
439 if ( ulRC == ULS_SUCCESS ) {
440 // The above returns a UniChar string; convert it to a codepage string
441 ulRC = UniCreateUconvObject( (UniChar *)L"@map=display", &uconv );
442 if ( ulRC == ULS_SUCCESS ) {
443 ulRC = UniStrFromUcs( uconv, szLocale, suLocale, ULS_LNAMEMAX );
444 if ( ulRC != ULS_SUCCESS ) sprintf( szLocale, "%ls", suLocale );
445 UniFreeUconvObject( uconv );
446 } else
447 sprintf( szLocale, "%ls", suLocale );
448 MAKERXSTRING( *prsResult, szLocale, strlen(szLocale) );
449 } else {
450 WriteErrorCode( ulRC, "UniMapCtryToLocale");
451 MAKERXSTRING( *prsResult, "", 0 );
452 }
453
454 return ( 0 );
455}
456
457
458/* ------------------------------------------------------------------------- *
459 * ULSGetLocales *
460 * *
461 * Returns a list of locales defined on the system. *
462 * *
463 * REXX ARGUMENTS: *
464 * 1. Type of locales to list. One of the following: *
465 * B = Both system and user locales (DEFAULT) *
466 * S = System locales *
467 * U = User locales *
468 * 2. Name of the REXX stem variable to return the results (REQUIRED) *
469 * *
470 * REXX RETURN VALUE: Number of locales returned *
471 * ------------------------------------------------------------------------- */
472ULONG APIENTRY ULSGetLocales( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
473{
474 UconvObject uconv;
475 UniChar suLocales[ LOCALE_BUF_MAX ], // list of null-separated locales
476 *psuLocale; // a single UCS-2 locale name
477 CHAR szLocale[ ULS_LNAMEMAX ], // a converted locale name
478 szStem[ US_STEM_MAXZ ], // the name of our stem variable
479 szNumber[ US_INTEGER_MAXZ ]; // the total number of locales
480 ULONG fLocaleType, // locale type flag
481 ulOffset, // current array position
482 ulCount, // locale count
483 ulRC; // return code
484
485 // Reset the error indicator
486 WriteErrorCode( 0, NULL );
487
488 // Check the arguments
489 if ( argc != 2 || !RXVALIDSTRING(argv[1]) )
490 return ( 40 );
491
492 if ( RXVALIDSTRING(argv[0]) ) {
493 strupr( argv[0].strptr );
494 if (strcspn(argv[0].strptr, "BSU") > 0 ) return ( 40 );
495 switch ( argv[0].strptr[0] ) {
496 case 'S': fLocaleType = UNI_SYSTEM_LOCALES; break;
497 case 'U': fLocaleType = UNI_USER_LOCALES; break;
498 default : fLocaleType = UNI_SYSTEM_LOCALES | UNI_USER_LOCALES; break;
499 }
500 } else fLocaleType = UNI_SYSTEM_LOCALES | UNI_USER_LOCALES;
501
502 // Generate the stem variable name from the argument (stripping any final dot)
503 if ( argv[1].strptr[ argv[1].strlength-1 ] == '.') argv[1].strlength--;
504 strncpy( szStem, argv[1].strptr, RXSTRLEN(argv[1]) );
505 szStem[ RXSTRLEN(argv[1]) ] = '\0';
506
507 // Get the requested list of locales
508 ulRC = UniQueryLocaleList( fLocaleType, suLocales, LOCALE_BUF_MAX );
509 if ( ulRC == ULS_SUCCESS ) {
510
511 // Create a conversion object for the current codepage
512 ulRC = UniCreateUconvObject( (UniChar *)L"@map=display", &uconv );
513 if ( ulRC != ULS_SUCCESS ) uconv = NULL;
514
515 // Now parse each null-delimited locale name from the list
516 ulOffset = 0;
517 ulCount = 0;
518 while (( ulOffset < LOCALE_BUF_MAX ) && ( suLocales[ulOffset] != 0 )) {
519 psuLocale = suLocales + ulOffset;
520 // Convert the locale name to a codepage string and add to the stem
521 if (( uconv == NULL ) || ( UniStrFromUcs( uconv, szLocale, psuLocale, ULS_LNAMEMAX ) == ULS_SUCCESS ))
522 sprintf( szLocale, "%ls", psuLocale );
523 WriteStemElement( szStem, ++ulCount, szLocale );
524 ulOffset += UniStrlen( psuLocale ) + 1;
525 }
526
527 // Write the stem.0 element (the number of locales), and also return it
528 sprintf( szNumber, "%d", ulCount );
529 WriteStemElement( szStem, 0, szNumber );
530 MAKERXSTRING( *prsResult, szNumber, strlen(szNumber) );
531
532 if ( uconv != NULL ) UniFreeUconvObject( uconv );
533
534 } else {
535 WriteErrorCode( ulRC, "UniQueryLocaleList");
536 MAKERXSTRING( *prsResult, "", 0 );
537 }
538
539 return ( 0 );
540}
541
542
543/* ------------------------------------------------------------------------- *
544 * ULSFindAttr *
545 * *
546 * Searches a string for the first character with the specified attribute. *
547 * *
548 * REXX ARGUMENTS: *
549 * 1. String being searched (REQUIRED) *
550 * 2. Name of attribute to check for (REQUIRED) *
551 * 3. First character to search (DEFAULT: start of string, i.e. 1) *
552 * 4. Maximum number of characters to search (DEFAULT: entire string) *
553 * 5. Type of search: *
554 * T = True, locate first matching character (DEFAULT) *
555 * F = False, locate first non-matching character *
556 * 6. Source codepage number (DEFAULT: current) *
557 * 7. Name of locale (DEFAULT: as per current environment settings) *
558 * *
559 * REXX RETURN VALUE: Position of the first match, or 0 if not found *
560 * ------------------------------------------------------------------------- */
561ULONG APIENTRY ULSFindAttr( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
562{
563 UconvObject uconvCP; // conversion object
564 AttrObject attrib; // attribute object
565 LocaleObject locale; // handle to the requested locale
566 UniChar *psuString, // input UniChar string
567// *psuOffset,
568 *psuSearch, // pointer to start of searched text
569 *psuAttrib; // name of the requested attribute
570// PSZ pszOffset;
571// UCHAR ucFirst, // first byte of a UCS-2 pair
572// ucSecond; // second byte of a UCS-2 pair
573 ULONG ulSourceCP = 0, // source codepage number
574// ulChars, // number of UniChars in string
575 ulStart = 0, // starting position of search
576 ulRC = 0; // return code
577 BOOL fInverse = FALSE; // inverse (negative) search flag
578 size_t stLimit = 0, // length limit of search
579 stPos = 0; // character position
580// ULONG i, j; // counters
581// size_t stInLen,
582// stOutLen,
583// stSubs;
584
585 // Reset the error indicator
586 WriteErrorCode( 0, NULL );
587
588 // Make sure we have at least two valid arguments (and no more than seven)
589 if ( argc < 2 || argc > 7 || !RXVALIDSTRING(argv[0]) || !RXVALIDSTRING(argv[1]) )
590 return ( 40 );
591 if ( argv[0].strlength < 2 ) return ( 40 );
592
593 // UniCreateAttrObject() requires the attribute name in lowercase
594 strlwr( argv[1].strptr );
595
596 // Other arguments: these are optional, but must be correct if specified
597 if ( argc >= 3 && RXVALIDSTRING(argv[2]) ) {
598 if ( sscanf(argv[2].strptr, "%d", &ulStart ) < 1 )
599 return ( 40 );
600 if ( ulStart > (argv[0].strlength / 2) || ulStart < 1 )
601 return ( 40 );
602 ulStart--;
603 }
604 if ( argc >= 4 ) {
605 if ( RXVALIDSTRING(argv[3]) && sscanf(argv[3].strptr, "%d", &stLimit ) < 1 )
606 return ( 40 );
607 }
608 if ( argc >= 5 && RXVALIDSTRING(argv[4]) ) {
609 strupr( argv[4].strptr );
610 if (strcspn(argv[4].strptr, "TF") > 0 ) return ( 40 );
611 switch ( argv[4].strptr[0] ) {
612 case 'F': fInverse = TRUE; break;
613 default : fInverse = FALSE; break;
614 }
615 }
616 if ( argc >= 6 ) {
617 // Parse the codepage number
618 if ( RXVALIDSTRING(argv[5]) && sscanf(argv[5].strptr, "%d", &ulSourceCP) < 1 )
619 return ( 40 );
620 // For UCS-2 input, make sure we have at least one valid character...
621 if (( ulSourceCP == 1200 ) && ( argv[0].strlength < 2 ))
622 return ( 40 );
623 }
624 // Parse the optional locale argument and create a handle for that locale
625 if ( argc >= 7 && RXVALIDSTRING(argv[6]) )
626 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, argv[6].strptr, &locale );
627 else
628 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, "", &locale );
629
630 if ( ulRC == ULS_SUCCESS ) {
631
632 if ( ulSourceCP != 1200 )
633 ulRC = CreateUconvObject( &uconvCP, ulSourceCP,
634 UCS_ENDIAN_SYSTEM, CONTROLS_MAP_DATA, DBCS_PATH_NO, 0 );
635
636 if ( ulRC == ULS_SUCCESS ) {
637
638 if ( InputStringToUnicode( argv[0], &psuString, uconvCP, ulSourceCP )) {
639/*
640 if ( ulSourceCP == 1200 ) {
641 // Input string is already UCS-2; read it directly into a UniChar array
642 psuString = (UniChar *) malloc( argv[0].strlength + sizeof(UniChar) );
643 ulChars = ( argv[0].strlength + 1 ) / 2;
644 j = 0;
645 for ( i = 0; i < ulChars; i++ ) {
646 ucFirst = argv[0].strptr[ j++ ];
647 ucSecond = argv[0].strptr[ j++ ];
648 psuString[ i ] = UNICHARFROM2BYTES( ucFirst, ucSecond );
649 }
650 psuString[ i ] = 0x0000;
651 } else {
652 // Convert the input text to UCS-2
653 stInLen = argv[0].strlength;
654 stOutLen = stInLen;
655 stSubs = 0;
656 psuString = (UniChar *) calloc( stOutLen + 1, sizeof(UniChar) );
657 pszOffset = argv[0].strptr;
658 psuOffset = psuString;
659 ulRC = UniUconvToUcs( uconvCP, (PPVOID) &pszOffset, &stInLen, &psuOffset, &stOutLen, &stSubs );
660 if ( ulRC != ULS_SUCCESS ) {
661 WriteErrorCode( ulRC, "UniUconvToUcs");
662 MAKERXSTRING( *prsResult, "", 0 );
663 }
664 }
665
666 if ( ulRC == ULS_SUCCESS ) {
667*/
668 // Convert the attribute to UCS-2 (we can just use mbstowcs
669 // instead of the UCS functions, because all valid attribute
670 // names are pure ASCII).
671 psuAttrib = (UniChar *) calloc( argv[1].strlength + 1, sizeof(UniChar) );
672 if ( psuAttrib && ( mbstowcs( psuAttrib, argv[1].strptr, argv[1].strlength ) > 0 )) {
673 // Create an AttrObject for the requested attribute
674 ulRC = UniCreateAttrObject( locale, psuAttrib, &attrib );
675 if ( ulRC == ULS_SUCCESS ) {
676
677 // Set the search boundaries
678 psuSearch = psuString + ulStart;
679 if ( stLimit < 1 ) stLimit = UniStrlen( psuSearch );
680
681 // Now scan the string for the requested attribute
682 ulRC = UniScanForAttr( attrib, psuSearch, stLimit, fInverse, &stPos );
683 if ( ulRC == ULS_NOMATCH ) {
684 sprintf( prsResult->strptr, "0");
685 prsResult->strlength = strlen( prsResult->strptr );
686 } else if ( ulRC == ULS_SUCCESS ) {
687 sprintf( prsResult->strptr, "%d", stPos + 1 );
688 prsResult->strlength = strlen( prsResult->strptr );
689 } else {
690 WriteErrorCode( ulRC, "UniScanForAttr");
691 MAKERXSTRING( *prsResult, "", 0 );
692 }
693
694 UniFreeAttrObject( attrib );
695 } else {
696 WriteErrorCode( ulRC, "UniCreateAttrObject");
697 MAKERXSTRING( *prsResult, "", 0 );
698 }
699 } else {
700 if ( psuAttrib ) WriteErrorCode( 0, "mbstowcs");
701 else WriteErrorCode( 0, "calloc");
702 MAKERXSTRING( *prsResult, "", 0 );
703 }
704 if ( psuAttrib ) free( psuAttrib );
705 }
706 if ( ulSourceCP != 1200 ) UniFreeUconvObject( uconvCP );
707
708 } else {
709 WriteErrorCode( ulRC, "UniCreateUconvObject");
710 MAKERXSTRING( *prsResult, "", 0 );
711 }
712
713 UniFreeLocaleObject( locale );
714 free( psuString );
715
716 } else {
717 WriteErrorCode( ulRC, "UniCreateLocaleObject");
718 MAKERXSTRING( *prsResult, "", 0 );
719 }
720
721 return ( 0 );
722}
723
724
725/* ------------------------------------------------------------------------- *
726 * ULSQueryAttr *
727 * *
728 * Queries whether the specified multi-byte character has the specified *
729 * character attribute. *
730 * *
731 * REXX ARGUMENTS: *
732 * 1. 1-4 byte codepage character being queried (REQUIRED) *
733 * 2. Name of attribute to check for (REQUIRED) *
734 * 3. Source codepage number (DEFAULT: current) *
735 * 4. Name of locale (DEFAULT: as per current environment settings) *
736 * *
737 * REXX RETURN VALUE: *
738 * '0' : Character does not have specified attribute *
739 * '1' : Character has specified attribute *
740 * ------------------------------------------------------------------------- */
741ULONG APIENTRY ULSQueryAttr( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
742{
743 UconvObject uconvCP; // conversion object
744 AttrObject attrib; // attribute object
745 LocaleObject locale; // handle to the requested locale
746 UniChar suChar[ 2 ], // input UniChar (as string)
747 *psuOffset,
748 *psuAttrib; // name of the requested attribute
749 PSZ pszOffset;
750 ULONG ulSourceCP = 0, // source codepage number
751 ulRC = 0; // return code
752 size_t stInLen,
753 stOutLen,
754 stSubs;
755
756
757 // Reset the error indicator
758 WriteErrorCode( 0, NULL );
759
760 // Make sure we have at least two valid arguments (and no more than four)
761 if ( argc < 2 || argc > 4 || !RXVALIDSTRING(argv[0]) || !RXVALIDSTRING(argv[1]) )
762 return ( 40 );
763
764 // UniCreateAttrObject() requires the attribute name in lowercase
765 strlwr( argv[1].strptr );
766
767 // Parse the codepage number
768 if ( argc >= 3 ) {
769 if ( RXVALIDSTRING(argv[2]) && sscanf(argv[2].strptr, "%d", &ulSourceCP) < 1 )
770 return ( 40 );
771 // For UCS-2 input, make sure we have a valid character...
772 if (( ulSourceCP == 1200 ) && ( argv[0].strlength < 2 ))
773 return ( 40 );
774 }
775 // Parse the optional locale argument and create a handle for that locale
776 if ( argc >= 4 && RXVALIDSTRING(argv[3]))
777 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, argv[3].strptr, &locale );
778 else
779 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, "", &locale );
780
781 if ( ulRC == ULS_SUCCESS ) {
782
783 ulRC = CreateUconvObject( &uconvCP, ulSourceCP,
784 UCS_ENDIAN_SYSTEM, CONTROLS_MAP_DATA, DBCS_PATH_NO, 0 );
785 if ( ulRC == ULS_SUCCESS ) {
786 memset( suChar, 0, sizeof(suChar) );
787 if ( ulSourceCP == 1200 ) {
788 // Input text is already UCS-2; parse it directly as such
789 suChar[ 0 ] = UNICHARFROM2BYTES( argv[0].strptr[0], argv[0].strptr[1] );
790 } else {
791 // Convert the input text to UCS-2
792 stInLen = argv[0].strlength;
793 stOutLen = 2;
794 stSubs = 0;
795 pszOffset = argv[0].strptr;
796 psuOffset = suChar;
797 ulRC = UniUconvToUcs( uconvCP, (PPVOID) &pszOffset, &stInLen, &psuOffset, &stOutLen, &stSubs );
798 if ( ulRC != ULS_SUCCESS ) {
799 WriteErrorCode( ulRC, "UniUconvToUcs");
800 MAKERXSTRING( *prsResult, "", 0 );
801 }
802 }
803
804 if ( ulRC == ULS_SUCCESS ) {
805 // Convert the requested attribute name into UCS-2
806 psuAttrib = (UniChar *) calloc( argv[1].strlength + 1, sizeof(UniChar) );
807 if ( psuAttrib && ( mbstowcs( psuAttrib, argv[1].strptr, argv[1].strlength ) > 0 ))
808 {
809 // Create an AttrObject for the requested attribute
810 ulRC = UniCreateAttrObject( locale, psuAttrib, &attrib );
811 if ( ulRC == ULS_SUCCESS ) {
812
813 // Now do the query and return the result
814 if ( UniQueryCharAttr( attrib, suChar[0] ) == 0 ) {
815 MAKERXSTRING( *prsResult, "0", 1 );
816 } else {
817 MAKERXSTRING( *prsResult, "1", 1 );
818 }
819 UniFreeAttrObject( attrib );
820
821 } else {
822 WriteErrorCode( ulRC, "UniCreateAttrObject");
823 MAKERXSTRING( *prsResult, "", 0 );
824 }
825 } else {
826 if ( psuAttrib ) WriteErrorCode( 0, "mbstowcs");
827 else WriteErrorCode( 0, "calloc");
828 MAKERXSTRING( *prsResult, "", 0 );
829 }
830 if ( psuAttrib ) free( psuAttrib );
831 }
832 UniFreeUconvObject( uconvCP );
833
834 } else {
835 WriteErrorCode( ulRC, "UniCreateUconvObject");
836 MAKERXSTRING( *prsResult, "", 0 );
837 }
838 UniFreeLocaleObject( locale );
839
840 } else {
841 WriteErrorCode( ulRC, "UniCreateLocaleObject");
842 MAKERXSTRING( *prsResult, "", 0 );
843 }
844
845 return ( 0 );
846}
847
848
849/* ------------------------------------------------------------------------- *
850 * ULSQueryLocaleItem *
851 * *
852 * Queries the value of the specified locale item. *
853 * *
854 * REXX ARGUMENTS: *
855 * 1. Name of locale item (REQUIRED) *
856 * 2. Name of locale (DEFAULT: as per current environment settings) *
857 * 3. Target codepage number (DEFAULT: current) *
858 * 4. Substitution character (hex) (DEFAULT: varies by codepage) *
859 * *
860 * REXX RETURN VALUE: The requested item value *
861 * ------------------------------------------------------------------------- */
862ULONG APIENTRY ULSQueryLocaleItem( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
863{
864 UconvObject uconvCP; // conversion object
865 LocaleObject locale; // handle to the requested locale
866 LocaleItem item; // the locale item being queried
867 UniChar *psuValue, // the returned item value
868 *psuOffset; // pointer into psuValue
869 PSZ pszFinal, // converted item value
870 pszOffset; // pointer into pszFinal
871 ULONG ulCP = 0, // target codepage number
872 ulBytes = 0, // length of pszFinal
873 ulRC = 0, // return code
874 i, j; // indices
875 USHORT usSub = 0; // substitution character value
876 size_t stInLength = 0, // input buffer length (UniUconvFromUcs)
877 stOutLength = 0, // output buffer length (UniUconvFromUcs)
878 stSubs = 0; // substitution count (UniUconvFromUcs)
879
880
881 // Reset the error indicator
882 WriteErrorCode( 0, NULL );
883
884 // Make sure we have at least one valid argument (and no more than four)
885 if ( argc < 1 || argc > 4 || !RXVALIDSTRING(argv[0]) )
886 return ( 40 );
887
888 // Parse the optional locale argument and create a handle for that locale
889 if ( argc >= 2 && RXVALIDSTRING(argv[1]) )
890 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, argv[1].strptr, &locale );
891 else
892 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, "", &locale );
893
894 // Parse the target codepage number
895 if ( argc >= 3 ) {
896 if ( RXVALIDSTRING(argv[2]) && sscanf(argv[2].strptr, "%d", &ulCP) < 1 )
897 return ( 40 );
898 }
899
900 // Parse the substitution character option
901 if ( argc >= 4 ) {
902 if ( RXVALIDSTRING(argv[3]) && sscanf(argv[3].strptr, "%4X", &usSub ) < 1 )
903 return ( 40 );
904 }
905
906 if ( ulRC == ULS_SUCCESS ) {
907
908 // Parse the name or number of the locale item requested
909 if ( sscanf( argv[0].strptr, "%d", &item ) < 1 ) {
910 // name string specified
911 strupr( argv[0].strptr );
912 if ( strcmp( argv[0].strptr, "SDATETIME") == 0 ) item = LOCI_sDateTime;
913 else if ( strcmp( argv[0].strptr, "SSHORTDATE") == 0 ) item = LOCI_sShortDate;
914 else if ( strcmp( argv[0].strptr, "STIMEFORMAT") == 0 ) item = LOCI_sTimeFormat;
915 else if ( strcmp( argv[0].strptr, "S1159") == 0 ) item = LOCI_s1159;
916 else if ( strcmp( argv[0].strptr, "S2359") == 0 ) item = LOCI_s2359;
917 else if ( strcmp( argv[0].strptr, "SABBREVDAYNAME7") == 0 ) item = LOCI_sAbbrevDayName7;
918 else if ( strcmp( argv[0].strptr, "SABBREVDAYNAME1") == 0 ) item = LOCI_sAbbrevDayName1;
919 else if ( strcmp( argv[0].strptr, "SABBREVDAYNAME2") == 0 ) item = LOCI_sAbbrevDayName2;
920 else if ( strcmp( argv[0].strptr, "SABBREVDAYNAME3") == 0 ) item = LOCI_sAbbrevDayName3;
921 else if ( strcmp( argv[0].strptr, "SABBREVDAYNAME4") == 0 ) item = LOCI_sAbbrevDayName4;
922 else if ( strcmp( argv[0].strptr, "SABBREVDAYNAME5") == 0 ) item = LOCI_sAbbrevDayName5;
923 else if ( strcmp( argv[0].strptr, "SABBREVDAYNAME6") == 0 ) item = LOCI_sAbbrevDayName6;
924 else if ( strcmp( argv[0].strptr, "SDAYNAME7") == 0 ) item = LOCI_sDayName7;
925 else if ( strcmp( argv[0].strptr, "SDAYNAME1") == 0 ) item = LOCI_sDayName1;
926 else if ( strcmp( argv[0].strptr, "SDAYNAME2") == 0 ) item = LOCI_sDayName2;
927 else if ( strcmp( argv[0].strptr, "SDAYNAME3") == 0 ) item = LOCI_sDayName3;
928 else if ( strcmp( argv[0].strptr, "SDAYNAME4") == 0 ) item = LOCI_sDayName4;
929 else if ( strcmp( argv[0].strptr, "SDAYNAME5") == 0 ) item = LOCI_sDayName5;
930 else if ( strcmp( argv[0].strptr, "SDAYNAME6") == 0 ) item = LOCI_sDayName6;
931 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME1") == 0 ) item = LOCI_sAbbrevMonthName1;
932 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME2") == 0 ) item = LOCI_sAbbrevMonthName2;
933 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME3") == 0 ) item = LOCI_sAbbrevMonthName3;
934 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME4") == 0 ) item = LOCI_sAbbrevMonthName4;
935 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME5") == 0 ) item = LOCI_sAbbrevMonthName5;
936 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME6") == 0 ) item = LOCI_sAbbrevMonthName6;
937 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME7") == 0 ) item = LOCI_sAbbrevMonthName7;
938 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME8") == 0 ) item = LOCI_sAbbrevMonthName8;
939 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME9") == 0 ) item = LOCI_sAbbrevMonthName9;
940 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME10") == 0 ) item = LOCI_sAbbrevMonthName10;
941 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME11") == 0 ) item = LOCI_sAbbrevMonthName11;
942 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME12") == 0 ) item = LOCI_sAbbrevMonthName12;
943 else if ( strcmp( argv[0].strptr, "SMONTHNAME1") == 0 ) item = LOCI_sMonthName1;
944 else if ( strcmp( argv[0].strptr, "SMONTHNAME2") == 0 ) item = LOCI_sMonthName2;
945 else if ( strcmp( argv[0].strptr, "SMONTHNAME3") == 0 ) item = LOCI_sMonthName3;
946 else if ( strcmp( argv[0].strptr, "SMONTHNAME4") == 0 ) item = LOCI_sMonthName4;
947 else if ( strcmp( argv[0].strptr, "SMONTHNAME5") == 0 ) item = LOCI_sMonthName5;
948 else if ( strcmp( argv[0].strptr, "SMONTHNAME6") == 0 ) item = LOCI_sMonthName6;
949 else if ( strcmp( argv[0].strptr, "SMONTHNAME7") == 0 ) item = LOCI_sMonthName7;
950 else if ( strcmp( argv[0].strptr, "SMONTHNAME8") == 0 ) item = LOCI_sMonthName8;
951 else if ( strcmp( argv[0].strptr, "SMONTHNAME9") == 0 ) item = LOCI_sMonthName9;
952 else if ( strcmp( argv[0].strptr, "SMONTHNAME10") == 0 ) item = LOCI_sMonthName10;
953 else if ( strcmp( argv[0].strptr, "SMONTHNAME11") == 0 ) item = LOCI_sMonthName11;
954 else if ( strcmp( argv[0].strptr, "SMONTHNAME12") == 0 ) item = LOCI_sMonthName12;
955 else if ( strcmp( argv[0].strptr, "SDECIMAL") == 0 ) item = LOCI_sDecimal;
956 else if ( strcmp( argv[0].strptr, "STHOUSAND") == 0 ) item = LOCI_sThousand;
957 else if ( strcmp( argv[0].strptr, "SYESSTRING") == 0 ) item = LOCI_sYesString;
958 else if ( strcmp( argv[0].strptr, "SNOSTRING") == 0 ) item = LOCI_sNoString;
959 else if ( strcmp( argv[0].strptr, "SCURRENCY") == 0 ) item = LOCI_sCurrency;
960 else if ( strcmp( argv[0].strptr, "SCODESET") == 0 ) item = LOCI_sCodeSet;
961 else if ( strcmp( argv[0].strptr, "XLOCALETOKEN") == 0 ) item = LOCI_xLocaleToken;
962 else if ( strcmp( argv[0].strptr, "XWINLOCALE") == 0 ) item = LOCI_xWinLocale;
963 else if ( strcmp( argv[0].strptr, "ILOCALERESNUM") == 0 ) item = LOCI_iLocaleResnum;
964 else if ( strcmp( argv[0].strptr, "SNATIVEDIGITS") == 0 ) item = LOCI_sNativeDigits;
965 else if ( strcmp( argv[0].strptr, "IMAXITEM") == 0 ) item = LOCI_iMaxItem;
966 else if ( strcmp( argv[0].strptr, "STIMEMARK") == 0 ) item = LOCI_sTimeMark;
967 else if ( strcmp( argv[0].strptr, "SERA") == 0 ) item = LOCI_sEra;
968 else if ( strcmp( argv[0].strptr, "SALTSHORTDATE") == 0 ) item = LOCI_sAltShortDate;
969 else if ( strcmp( argv[0].strptr, "SALTDATETIME") == 0 ) item = LOCI_sAltDateTime;
970 else if ( strcmp( argv[0].strptr, "SALTTIMEFORMAT") == 0 ) item = LOCI_sAltTimeFormat;
971 else if ( strcmp( argv[0].strptr, "SALTDIGITS") == 0 ) item = LOCI_sAltDigits;
972 else if ( strcmp( argv[0].strptr, "SYESEXPR") == 0 ) item = LOCI_sYesExpr;
973 else if ( strcmp( argv[0].strptr, "SNOEXPR") == 0 ) item = LOCI_sNoExpr;
974 else if ( strcmp( argv[0].strptr, "SDATE") == 0 ) item = LOCI_sDate;
975 else if ( strcmp( argv[0].strptr, "STIME") == 0 ) item = LOCI_sTime;
976 else if ( strcmp( argv[0].strptr, "SLIST") == 0 ) item = LOCI_sList;
977 else if ( strcmp( argv[0].strptr, "SMONDECIMALSEP") == 0 ) item = LOCI_sMonDecimalSep;
978 else if ( strcmp( argv[0].strptr, "SMONTHOUSANDSEP") == 0 ) item = LOCI_sMonThousandSep;
979 else if ( strcmp( argv[0].strptr, "SGROUPING") == 0 ) item = LOCI_sGrouping;
980 else if ( strcmp( argv[0].strptr, "SMONGROUPING") == 0 ) item = LOCI_sMonGrouping;
981 else if ( strcmp( argv[0].strptr, "IMEASURE") == 0 ) item = LOCI_iMeasure;
982 else if ( strcmp( argv[0].strptr, "IPAPER") == 0 ) item = LOCI_iPaper;
983 else if ( strcmp( argv[0].strptr, "IDIGITS") == 0 ) item = LOCI_iDigits;
984 else if ( strcmp( argv[0].strptr, "ITIME") == 0 ) item = LOCI_iTime;
985 else if ( strcmp( argv[0].strptr, "IDATE") == 0 ) item = LOCI_iDate;
986 else if ( strcmp( argv[0].strptr, "ICURRENCY") == 0 ) item = LOCI_iCurrency;
987 else if ( strcmp( argv[0].strptr, "ICURRDIGITS") == 0 ) item = LOCI_iCurrDigits;
988 else if ( strcmp( argv[0].strptr, "ILZERO") == 0 ) item = LOCI_iLzero;
989 else if ( strcmp( argv[0].strptr, "INEGNUMBER") == 0 ) item = LOCI_iNegNumber;
990 else if ( strcmp( argv[0].strptr, "ILDATE") == 0 ) item = LOCI_iLDate;
991 else if ( strcmp( argv[0].strptr, "ICALENDARTYPE") == 0 ) item = LOCI_iCalendarType;
992 else if ( strcmp( argv[0].strptr, "IFIRSTDAYOFWEEK") == 0 ) item = LOCI_iFirstDayOfWeek;
993 else if ( strcmp( argv[0].strptr, "IFIRSTWEEKOFYEAR") == 0 ) item = LOCI_iFirstWeekOfYear;
994 else if ( strcmp( argv[0].strptr, "INEGCURR") == 0 ) item = LOCI_iNegCurr;
995 else if ( strcmp( argv[0].strptr, "ITLZERO") == 0 ) item = LOCI_iTLzero;
996 else if ( strcmp( argv[0].strptr, "ITIMEPREFIX") == 0 ) item = LOCI_iTimePrefix;
997 else if ( strcmp( argv[0].strptr, "IOPTIONALCALENDAR") == 0 ) item = LOCI_iOptionalCalendar;
998 else if ( strcmp( argv[0].strptr, "SINTLSYMBOL") == 0 ) item = LOCI_sIntlSymbol;
999 else if ( strcmp( argv[0].strptr, "SABBREVLANGNAME") == 0 ) item = LOCI_sAbbrevLangName;
1000 else if ( strcmp( argv[0].strptr, "SCOLLATE") == 0 ) item = LOCI_sCollate;
1001 else if ( strcmp( argv[0].strptr, "IUPPERTYPE") == 0 ) item = LOCI_iUpperType;
1002 else if ( strcmp( argv[0].strptr, "IUPPERMISSING") == 0 ) item = LOCI_iUpperMissing;
1003 else if ( strcmp( argv[0].strptr, "SPOSITIVESIGN") == 0 ) item = LOCI_sPositiveSign;
1004 else if ( strcmp( argv[0].strptr, "SNEGATIVESIGN") == 0 ) item = LOCI_sNegativeSign;
1005 else if ( strcmp( argv[0].strptr, "SLEFTNEGATIVE") == 0 ) item = LOCI_sLeftNegative;
1006 else if ( strcmp( argv[0].strptr, "SRIGHTNEGATIVE") == 0 ) item = LOCI_sRightNegative;
1007 else if ( strcmp( argv[0].strptr, "SLONGDATE") == 0 ) item = LOCI_sLongDate;
1008 else if ( strcmp( argv[0].strptr, "SALTLONGDATE") == 0 ) item = LOCI_sAltLongDate;
1009 else if ( strcmp( argv[0].strptr, "SMONTHNAME13") == 0 ) item = LOCI_sMonthName13;
1010 else if ( strcmp( argv[0].strptr, "SABBREVMONTHNAME13") == 0 ) item = LOCI_sAbbrevMonthName13;
1011 else if ( strcmp( argv[0].strptr, "SNAME") == 0 ) item = LOCI_sName;
1012 else if ( strcmp( argv[0].strptr, "SLANGUAGEID") == 0 ) item = LOCI_sLanguageID;
1013 else if ( strcmp( argv[0].strptr, "SCOUNTRYID") == 0 ) item = LOCI_sCountryID;
1014 else if ( strcmp( argv[0].strptr, "SENGLANGUAGE") == 0 ) item = LOCI_sEngLanguage;
1015 else if ( strcmp( argv[0].strptr, "SLANGUAGE") == 0 ) item = LOCI_sLanguage;
1016 else if ( strcmp( argv[0].strptr, "SENGCOUNTRY") == 0 ) item = LOCI_sEngCountry;
1017 else if ( strcmp( argv[0].strptr, "SCOUNTRY") == 0 ) item = LOCI_sCountry;
1018 else if ( strcmp( argv[0].strptr, "SNATIVECTRYNAME") == 0 ) item = LOCI_sNativeCtryName;
1019 else if ( strcmp( argv[0].strptr, "ICOUNTRY") == 0 ) item = LOCI_iCountry;
1020 else if ( strcmp( argv[0].strptr, "SISOCODEPAGE") == 0 ) item = LOCI_sISOCodepage;
1021 else if ( strcmp( argv[0].strptr, "IANSICODEPAGE") == 0 ) item = LOCI_iAnsiCodepage;
1022 else if ( strcmp( argv[0].strptr, "ICODEPAGE") == 0 ) item = LOCI_iCodepage;
1023 else if ( strcmp( argv[0].strptr, "IALTCODEPAGE") == 0 ) item = LOCI_iAltCodepage;
1024 else if ( strcmp( argv[0].strptr, "IMACCODEPAGE") == 0 ) item = LOCI_iMacCodepage;
1025 else if ( strcmp( argv[0].strptr, "IEBCDICCODEPAGE") == 0 ) item = LOCI_iEbcdicCodepage;
1026 else if ( strcmp( argv[0].strptr, "SOTHERCODEPAGES") == 0 ) item = LOCI_sOtherCodepages;
1027 else if ( strcmp( argv[0].strptr, "SSETCODEPAGE") == 0 ) item = LOCI_sSetCodepage;
1028 else if ( strcmp( argv[0].strptr, "SKEYBOARD") == 0 ) item = LOCI_sKeyboard;
1029 else if ( strcmp( argv[0].strptr, "SALTKEYBOARD") == 0 ) item = LOCI_sAltKeyboard;
1030 else if ( strcmp( argv[0].strptr, "SSETKEYBOARD") == 0 ) item = LOCI_sSetKeyboard;
1031 else if ( strcmp( argv[0].strptr, "SDEBIT") == 0 ) item = LOCI_sDebit;
1032 else if ( strcmp( argv[0].strptr, "SCREDIT") == 0 ) item = LOCI_sCredit;
1033 else if ( strcmp( argv[0].strptr, "SLATIN1LOCALE") == 0 ) item = LOCI_sLatin1Locale;
1034 else if ( strcmp( argv[0].strptr, "WTIMEFORMAT") == 0 ) item = LOCI_wTimeFormat;
1035 else if ( strcmp( argv[0].strptr, "WSHORTDATE") == 0 ) item = LOCI_wShortDate;
1036 else if ( strcmp( argv[0].strptr, "WLONGDATE") == 0 ) item = LOCI_wLongDate;
1037 else if ( strcmp( argv[0].strptr, "JISO3COUNTRYNAME") == 0 ) item = LOCI_jISO3CountryName;
1038 else if ( strcmp( argv[0].strptr, "JPERCENTPATTERN") == 0 ) item = LOCI_jPercentPattern;
1039 else if ( strcmp( argv[0].strptr, "JPERCENTSIGN") == 0 ) item = LOCI_jPercentSign;
1040 else if ( strcmp( argv[0].strptr, "JEXPONENT") == 0 ) item = LOCI_jExponent;
1041 else if ( strcmp( argv[0].strptr, "JFULLTIMEFORMAT") == 0 ) item = LOCI_jFullTimeFormat;
1042 else if ( strcmp( argv[0].strptr, "JLONGTIMEFORMAT") == 0 ) item = LOCI_jLongTimeFormat;
1043 else if ( strcmp( argv[0].strptr, "JSHORTTIMEFORMAT") == 0 ) item = LOCI_jShortTimeFormat;
1044 else if ( strcmp( argv[0].strptr, "JFULLDATEFORMAT") == 0 ) item = LOCI_jFullDateFormat;
1045 else if ( strcmp( argv[0].strptr, "JMEDIUMDATEFORMAT") == 0 ) item = LOCI_jMediumDateFormat;
1046 else if ( strcmp( argv[0].strptr, "JDATETIMEPATTERN") == 0 ) item = LOCI_jDateTimePattern;
1047 else if ( strcmp( argv[0].strptr, "JERASTRINGS") == 0 ) item = LOCI_jEraStrings;
1048 else return ( 40 );
1049 }
1050
1051 // Now query the item's value
1052 ulRC = UniQueryLocaleItem( locale, item, &psuValue );
1053 if ( ulRC == ULS_SUCCESS ) {
1054 // Query succeeded, now convert the result into the requested codepage
1055 if ( ulCP == 1200 ) {
1056 // UCS-2 requested, so simply return the UniChar array as bytes
1057 ulBytes = UniStrlen( psuValue ) * sizeof( UniChar );
1058 pszFinal = (PSZ) malloc( ulBytes );
1059 j = 0;
1060 for ( i = 0; i < UniStrlen(psuValue); i++ ) {
1061 pszFinal[j++] = BYTE1FROMUNICHAR( psuValue[i] );
1062 pszFinal[j++] = BYTE2FROMUNICHAR( psuValue[i] );
1063 }
1064 if ( ! SaveResultString( prsResult, pszFinal, ulBytes )) {
1065 MAKERXSTRING( *prsResult, "", 0 );
1066 }
1067 free( pszFinal );
1068 } else {
1069 // Create the UCS-to-target conversion object
1070 ulRC = CreateUconvObject( &uconvCP, ulCP,
1071 UCS_ENDIAN_SYSTEM, CONTROLS_MAP_DATA, DBCS_PATH_NO, usSub );
1072 if ( ulRC == ULS_SUCCESS ) {
1073
1074 // Convert the string to the target codepage
1075 // (Allow up to 4x the length of the original string)
1076 stInLength = UniStrlen( psuValue );
1077 stOutLength = stInLength * 4;
1078 pszFinal = (PSZ) malloc( stOutLength + 1 );
1079 psuOffset = psuValue;
1080 pszOffset = pszFinal;
1081 stSubs = 0;
1082 memset( pszFinal, 0, stOutLength + 1 );
1083 ulRC = UniUconvFromUcs( uconvCP, &psuOffset, &stInLength,
1084 (PVOID *) &pszOffset, &stOutLength, &stSubs );
1085 if ( ulRC == ULS_SUCCESS ) {
1086 // Return the final converted string
1087 if ( ! SaveResultString( prsResult, pszFinal, strlen(pszFinal) )) {
1088 MAKERXSTRING( *prsResult, "", 0 );
1089 }
1090 UniFreeUconvObject( uconvCP );
1091 } else {
1092 // UniUconvFromUcs failed
1093 WriteErrorCode( ulRC, "UniUconvFromUcs");
1094 MAKERXSTRING( *prsResult, "", 0 );
1095 }
1096 free( pszFinal );
1097
1098 } else {
1099 // Failed to create UconvObject
1100 WriteErrorCode( ulRC, "UniCreateUconvObject");
1101 MAKERXSTRING( *prsResult, "", 0 );
1102 }
1103 }
1104 UniFreeMem( psuValue );
1105
1106 } else
1107 WriteErrorCode( ulRC, "UniQueryLocaleItem");
1108
1109 UniFreeLocaleObject( locale );
1110 } else {
1111 WriteErrorCode( ulRC, "UniCreateLocaleObject");
1112 MAKERXSTRING( *prsResult, "", 0 );
1113 }
1114
1115 return ( 0 );
1116}
1117
1118
1119/* ------------------------------------------------------------------------- *
1120 * ULSTransform *
1121 * *
1122 * Transform a string according to one of the transformation types defined *
1123 * for all locales. The actual effect is locale-dependent. *
1124 * *
1125 * REXX ARGUMENTS: *
1126 * 1. String to be transformed (REQUIRED) *
1127 * 2. Name of transformation (REQUIRED) *
1128 * 3. Source codepage number (DEFAULT: current) *
1129 * 4. Name of locale (DEFAULT: as per current environment settings) *
1130 * *
1131 * REXX RETURN VALUE: Transformed string *
1132 * ------------------------------------------------------------------------- */
1133ULONG APIENTRY ULSTransform( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1134{
1135 XformObject xformation; // transformation object
1136 LocaleObject locale; // handle to the requested locale
1137 UconvObject uconvCP; // conversion object
1138 UniChar *psuString, // input UniChar string
1139 *psuOffset,
1140 *psuFinal, // transformed output UniChar string
1141 *psuXform; // name of the requested transformation
1142 PSZ pszOffset,
1143 pszFinal;
1144 ULONG ulSourceCP = 0, // source codepage number
1145// ulChars, // length of input UniChar string
1146 ulBytes, // length of returned string in bytes
1147 ulRC = 0, // return code
1148 i, j; // counters
1149// UCHAR ucFirst, // first byte of a UCS-2 pair
1150// ucSecond; // second byte of a UCS-2 pair
1151 size_t stInLen,
1152 stOutLen,
1153 stSubs;
1154 int iInLength, // input buffer length (UniTransformStr)
1155 iOutLength; // output buffer length (UniTransformStr)
1156
1157
1158 // Reset the error indicator
1159 WriteErrorCode( 0, NULL );
1160
1161 // Make sure we have at least two valid arguments
1162 if ( argc < 2 || !RXVALIDSTRING(argv[0]) || !RXVALIDSTRING(argv[1]) )
1163 return ( 40 );
1164 if ( argv[0].strlength < 2 ) return ( 40 );
1165
1166 // UniCreateTransformObject() requires a lowercase transformation name
1167 strlwr( argv[1].strptr );
1168
1169 // Parse the codepage number
1170 if ( argc >= 3 ) {
1171 if ( RXVALIDSTRING(argv[2]) && sscanf(argv[2].strptr, "%d", &ulSourceCP) < 1 )
1172 return ( 40 );
1173 // For UCS-2 input, make sure we have a valid character...
1174 if (( ulSourceCP == 1200 ) && ( argv[0].strlength < 2 ))
1175 return ( 40 );
1176 }
1177 // Parse the optional locale argument and create a handle for that locale
1178 if ( argc >= 4 && RXVALIDSTRING(argv[3]))
1179 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, argv[3].strptr, &locale );
1180 else
1181 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, "", &locale );
1182
1183 if ( ulRC == ULS_SUCCESS ) {
1184 ulRC = CreateUconvObject( &uconvCP, ulSourceCP,
1185 UCS_ENDIAN_SYSTEM, CONTROLS_MAP_DATA, DBCS_PATH_NO, 0 );
1186
1187 if ( ulRC == ULS_SUCCESS ) {
1188
1189 if ( InputStringToUnicode( argv[0], &psuString, uconvCP, ulSourceCP )) {
1190/*
1191 if ( ulSourceCP == 1200 ) {
1192 // Input text is already UCS-2; parse it directly as a UniChar array
1193 psuString = (UniChar *) malloc( argv[0].strlength + sizeof(UniChar) );
1194 ulChars = (argv[0].strlength + 1) / 2;
1195 j = 0;
1196 for ( i = 0; i < ulChars; i++ ) {
1197 ucFirst = argv[0].strptr[ j++ ];
1198 ucSecond = argv[0].strptr[ j++ ];
1199 psuString[ i ] = UNICHARFROM2BYTES( ucFirst, ucSecond );
1200 }
1201 psuString[ i ] = 0x0000;
1202 } else {
1203 // Convert the input text to UCS-2
1204 stInLen = argv[0].strlength;
1205 stOutLen = stInLen;
1206 stSubs = 0;
1207 psuString = (UniChar *) calloc( stOutLen + 1, sizeof(UniChar) );
1208 pszOffset = argv[0].strptr;
1209 psuOffset = psuString;
1210 ulRC = UniUconvToUcs( uconvCP, (PPVOID) &pszOffset, &stInLen, &psuOffset, &stOutLen, &stSubs );
1211 if ( ulRC != ULS_SUCCESS ) {
1212 WriteErrorCode( ulRC, "UniUconvToUcs");
1213 MAKERXSTRING( *prsResult, "", 0 );
1214 }
1215 }
1216
1217 if ( ulRC == ULS_SUCCESS ) {
1218*/
1219 psuXform = (UniChar *) calloc( argv[1].strlength + 1, sizeof(UniChar) );
1220 if ( psuXform && ( mbstowcs( psuXform, argv[1].strptr, argv[1].strlength ) > 0 )) {
1221
1222 // Create a transformation object for the requested transformation type
1223 ulRC = UniCreateTransformObject( locale, psuXform, &xformation );
1224 if ( ulRC == ULS_SUCCESS ) {
1225
1226 // Perform the transformation
1227 iInLength = argv[0].strlength;
1228 // (allow 4x the input string length for decomposition)
1229 iOutLength = ( iInLength * 4 ) + 1;
1230 psuFinal = (UniChar *) calloc( iOutLength, sizeof(UniChar) );
1231 ulRC = UniTransformStr( xformation, psuString, &iInLength, psuFinal, &iOutLength );
1232
1233 // Now convert the string back to the original codepage
1234 if ( ulRC == ULS_SUCCESS ) {
1235
1236 if ( ulSourceCP == 1200 ) {
1237 // Converting to UCS-2; simply return the UniChar array as bytes
1238 ulBytes = UniStrlen( psuFinal ) * sizeof( UniChar );
1239 pszFinal = (PSZ) malloc( ulBytes );
1240 j = 0;
1241 for ( i = 0; i < UniStrlen(psuFinal); i++ ) {
1242 pszFinal[j++] = BYTE1FROMUNICHAR( psuFinal[i] );
1243 pszFinal[j++] = BYTE2FROMUNICHAR( psuFinal[i] );
1244 }
1245 if ( ! SaveResultString( prsResult, pszFinal, ulBytes )) {
1246 MAKERXSTRING( *prsResult, "", 0 );
1247 }
1248 } else {
1249 // Convert the string back to the source codepage
1250 // (Allow up to 4x the length of the original string)
1251 stInLen = UniStrlen( psuFinal );
1252 stOutLen = stInLen * 4;
1253 pszFinal = (PSZ) malloc( stOutLen + 1 );
1254 psuOffset = psuFinal;
1255 pszOffset = pszFinal;
1256 stSubs = 0;
1257 memset( pszFinal, 0, stOutLen + 1 );
1258 ulRC = UniUconvFromUcs( uconvCP, &psuOffset, &stInLen,
1259 (PPVOID) &pszOffset, &stOutLen, &stSubs );
1260 if ( ulRC == ULS_SUCCESS ) {
1261 if ( ! SaveResultString( prsResult, pszFinal, strlen(pszFinal) )) {
1262 MAKERXSTRING( *prsResult, "", 0 );
1263 }
1264 } else {
1265 WriteErrorCode( ulRC, "UniUconvFromUcs");
1266 MAKERXSTRING( *prsResult, "", 0 );
1267 }
1268 }
1269 free ( pszFinal );
1270 } else {
1271 WriteErrorCode( ulRC, "UniTransformStr");
1272 MAKERXSTRING( *prsResult, "", 0 );
1273 }
1274 free( psuFinal );
1275 UniFreeTransformObject( xformation );
1276
1277 } else {
1278 WriteErrorCode( ulRC, "UniCreateTransformObject");
1279 MAKERXSTRING( *prsResult, "", 0 );
1280 }
1281 } else {
1282 if ( psuXform ) WriteErrorCode( 0, "mbstowcs");
1283 else WriteErrorCode( 0, "calloc");
1284 MAKERXSTRING( *prsResult, "", 0 );
1285 }
1286 if ( psuXform ) free( psuXform );
1287
1288 }
1289 free( psuString );
1290 UniFreeUconvObject( uconvCP );
1291
1292 } else {
1293 WriteErrorCode( ulRC, "UniCreateUconvObject");
1294 MAKERXSTRING( *prsResult, "", 0 );
1295 }
1296 UniFreeLocaleObject( locale );
1297
1298 } else {
1299 WriteErrorCode( ulRC, "UniCreateLocaleObject");
1300 MAKERXSTRING( *prsResult, "", 0 );
1301 }
1302
1303 return ( 0 );
1304}
1305
1306
1307/* ------------------------------------------------------------------------- *
1308 * ULSPutUnicodeClipboard *
1309 * *
1310 * Write a string to the clipboard (converted to UCS-2) in "text/unicode" *
1311 * format. (This is the format used by Mozilla, and is also compatible with *
1312 * various other applications.) *
1313 * *
1314 * REXX ARGUMENTS: *
1315 * 1. String to be written to the clipboard (REQUIRED) *
1316 * 2. Source codepage number (DEFAULT: current) *
1317 * 3. Control flag (how to treat byte values 0x00-0x19,0x7F), one of: *
1318 * D = data, treat as control bytes and do not convert (DEFAULT) *
1319 * G = glyphs, treat as glyphs and convert according to codepage *
1320 * C = cdra, treat as control bytes and convert using IBM mapping *
1321 * L = linebreak, treat CR and LF as controls, all others as glyphs *
1322 * 4. Path conversion flag (only applies to DBCS codepages), one of: *
1323 * Y = yes, assume string contains a path specifier (DEFAULT) *
1324 * N = no, assume string doesn't contain a path specifier *
1325 * *
1326 * REXX RETURN VALUE: "" *
1327 * ------------------------------------------------------------------------- */
1328ULONG APIENTRY ULSPutUnicodeClipboard( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1329{
1330 UconvObject uconvSource; // conversion object for source codepage
1331 UniChar *psuConverted, // converted UCS-2 string
1332// *psuOffset, // pointer to current UniChar
1333 *psuShareMem; // Unicode text in clipboard
1334// PSZ pszOffset; // pointer to current char
1335 ULONG ulSourceCP = 0, // requested source codepage
1336 ulRC = 0, // return code
1337// ulChars = 0, // size in UniChars of input string
1338 ulPType = 0; // process-type flag
1339// i, j; // loop indices
1340 USHORT fMap = 0, // control-mapping flag
1341 fPath = 0; // path-conversion flag
1342// UCHAR ucFirst, // first byte of a UCS-2 pair
1343// ucSecond; // second byte of a UCS-2 pair
1344// size_t stInLength = 0, // length of input buffer (UniUconv*Ucs)
1345 size_t stOutLength = 0; // length of output buffer (UniUconv*Ucs)
1346// stSubstitutions = 0; // substitution count (UniUconv*Ucs)
1347 BOOL fHabTerm = TRUE; // terminate HAB ourselves?
1348 HAB hab; // anchor-block handle (for Win*)
1349 HMQ hmq; // message-queue handle
1350 HATOMTBL hSATbl; // handle to system atom table
1351 ATOM cf_TextUnicode; // "text/unicode" clipboard format atom
1352 PPIB ppib; // process information block
1353 PTIB ptib; // thread information block
1354
1355
1356 // Reset the error indicator
1357 WriteErrorCode( 0, NULL );
1358
1359 // Make sure we have at least one valid argument (the input string)
1360 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1361
1362 // Other arguments: these are optional, but must be correct if specified
1363 if ( argc >= 2 ) {
1364 // second argument: source codepage
1365 if ( RXVALIDSTRING(argv[1]) && sscanf(argv[1].strptr, "%d", &ulSourceCP) < 1 )
1366 return ( 40 );
1367 }
1368 if ( argc >= 3 ) {
1369 // third argument: control flag
1370 if ( RXVALIDSTRING(argv[2]) ) {
1371 strupr( argv[2].strptr );
1372 if ( strcspn(argv[2].strptr, "DGCL") > 0 ) return ( 40 );
1373 switch ( argv[2].strptr[0] ) {
1374 case 'G': fMap = CONTROLS_MAP_DISPLAY; break;
1375 case 'C': fMap = CONTROLS_MAP_CDRA; break;
1376 case 'L': fMap = CONTROLS_MAP_CRLF; break;
1377 default : fMap = CONTROLS_MAP_DATA; break;
1378 }
1379 } else fMap = CONTROLS_MAP_DATA;
1380 }
1381 if ( argc >= 4 ) {
1382 // fourth argument: path flag
1383 if ( RXVALIDSTRING(argv[3]) ) {
1384 strupr( argv[3].strptr );
1385 if ( strcspn(argv[3].strptr, "YN") > 0 ) return ( 40 );
1386 switch ( argv[3].strptr[0] ) {
1387 case 'N': fPath = DBCS_PATH_NO; break;
1388 default : fPath = DBCS_PATH_YES; break;
1389 }
1390 } else fPath = DBCS_PATH_YES;
1391 }
1392
1393 // Create the source-to-UCS conversion object
1394 if ( ulSourceCP != 1200 )
1395 ulRC = CreateUconvObject( &uconvSource, ulSourceCP, 0, fMap, fPath, 0 );
1396
1397 if ( ulRC == ULS_SUCCESS ) {
1398
1399 if ( InputStringToUnicode( argv[0], &psuConverted, uconvSource, ulSourceCP )) {
1400/*
1401 if ( ulSourceCP == 1200 ) {
1402 // Input string is already UCS-2; read it directly into a UniChar array
1403 psuConverted = (UniChar *) malloc( argv[0].strlength + sizeof(UniChar) );
1404 ulChars = argv[0].strlength / 2;
1405 j = 0;
1406 for ( i = 0; i < ulChars; i++ ) {
1407 ucFirst = argv[0].strptr[ j++ ];
1408 ucSecond = argv[0].strptr[ j++ ];
1409 psuConverted[ i ] = UNICHARFROM2BYTES( ucFirst, ucSecond );
1410 }
1411 psuConverted[ i ] = 0x0000;
1412 } else {
1413 // Convert the input string to UCS-2
1414 stInLength = argv[0].strlength;
1415 stOutLength = stInLength;
1416 psuConverted = (UniChar *) calloc( stOutLength + 1, sizeof(UniChar) );
1417 pszOffset = argv[0].strptr;
1418 psuOffset = psuConverted;
1419 memset( psuConverted, 0, (stOutLength+1) * sizeof(UniChar) );
1420 ulRC = UniUconvToUcs( uconvSource, (PPVOID) &pszOffset, &stInLength,
1421 &psuOffset, &stOutLength, &stSubstitutions );
1422 }
1423
1424 if ( ulRC == ULS_SUCCESS ) {
1425*/
1426 // Initialize the PM API
1427 DosGetInfoBlocks( &ptib, &ppib );
1428 ulPType = ppib->pib_ultype;
1429 ppib->pib_ultype = 3;
1430 hab = WinInitialize( 0 );
1431 if ( !hab ) {
1432 fHabTerm = FALSE;
1433 hab = 1;
1434 }
1435
1436 /* Try to create a message-queue if one doesn't exist. We don't
1437 * need to check the result, because it could fail if a message
1438 * queue already exists (in the calling process), which is also OK.
1439 */
1440 hmq = WinCreateMsgQueue( hab, 0);
1441
1442 // Make sure the Unicode clipboard format is registered
1443 hSATbl = WinQuerySystemAtomTable();
1444 cf_TextUnicode = WinAddAtom( hSATbl, "text/unicode");
1445
1446 // Place the UCS-2 string on the clipboard as "text/unicode"
1447 ulRC = WinOpenClipbrd( hab );
1448 if ( ulRC ) {
1449 stOutLength = argv[0].strlength;
1450 ulRC = DosAllocSharedMem( (PVOID) &psuShareMem, NULL, (stOutLength + 1) * sizeof(UniChar),
1451 PAG_READ | PAG_WRITE | PAG_COMMIT | OBJ_GIVEABLE );
1452 if ( ulRC == 0 ) {
1453 memset( psuShareMem, 0, (stOutLength+1) * sizeof(UniChar) );
1454 UniStrncpy( psuShareMem, psuConverted, stOutLength );
1455 if ( ! WinSetClipbrdData( hab, (ULONG) psuShareMem, cf_TextUnicode, CFI_POINTER ))
1456 WriteErrorCode( ERRORIDERROR(WinGetLastError(hab)), "WinSetClipbrdData");
1457 else
1458 MAKERXSTRING( *prsResult, "", 0 );
1459 } else {
1460 WriteErrorCode( ulRC, "DosAllocSharedMem");
1461 MAKERXSTRING( *prsResult, "", 0 );
1462 }
1463 WinCloseClipbrd( hab );
1464 } else {
1465 WriteErrorCode( ulRC, "WinOpenClipbrd");
1466 MAKERXSTRING( *prsResult, "", 0 );
1467 }
1468
1469 WinDeleteAtom( hSATbl, cf_TextUnicode );
1470 if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq );
1471 if ( fHabTerm ) WinTerminate( hab );
1472
1473 ppib->pib_ultype = ulPType;
1474 free( psuConverted );
1475 } else {
1476 // UniUconvToUcs failed
1477 WriteErrorCode( ulRC, "UniUconvFromUcs");
1478 MAKERXSTRING( *prsResult, "", 0 );
1479 }
1480
1481 if ( ulSourceCP != 1200 )
1482 UniFreeUconvObject( uconvSource );
1483
1484 } else {
1485 // Failed to create source UconvObject
1486 WriteErrorCode( ulRC, "UniCreateUconvObject");
1487 MAKERXSTRING( *prsResult, "", 0 );
1488 }
1489
1490 return ( 0 );
1491}
1492
1493
1494/* ------------------------------------------------------------------------- *
1495 * ULSGetUnicodeClipboard *
1496 * *
1497 * Retrieve a "text/unicode" string from the clipboard if one exists *
1498 * (converting it into the target codepage, if applicable). *
1499 * *
1500 * REXX ARGUMENTS: *
1501 * 1. Target codepage number (DEFAULT: current) *
1502 * 2. Substitution character (hex) (DEFAULT: varies by codepage) *
1503 * 3. Control flag (how to treat byte values 0x00-0x19,0x7F), one of: *
1504 * D = data, treat as control bytes and do not convert (DEFAULT) *
1505 * G = glyphs, treat as glyphs and convert according to codepage *
1506 * C = cdra, treat as control bytes and convert using IBM mapping *
1507 * L = linebreak, treat CR and LF as controls, all others as glyphs *
1508 * 4. Path conversion flag (only applies to DBCS codepages), one of: *
1509 * Y = yes, assume string contains a path specifier (DEFAULT) *
1510 * N = no, assume string doesn't contain a path specifier *
1511 * *
1512 * REXX RETURN VALUE: The converted clipboard string *
1513 * ------------------------------------------------------------------------- */
1514ULONG APIENTRY ULSGetUnicodeClipboard( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1515{
1516 UconvObject uconvTarget; // conversion object for target codepage
1517 UniChar *psuClipText, // UCS-2 string from the clipboard
1518 *psuOffset; // pointer to current UniChar
1519 PSZ pszFinal, // converted final string
1520 pszOffset; // pointer to current character
1521 ULONG ulTargetCP = 0, // requested target codepage
1522 ulRC = 0, // return code
1523 ulBytes = 0, // size in bytes of output string
1524 ulChars = 0, // size in UniChars of clipboard string
1525 ulPType = 0, // process-type flag
1526 i, j; // loop indices
1527 USHORT fMap = 0, // control-mapping flag
1528 fPath = 0, // path-conversion flag
1529 usSub = 0; // substitution character
1530 size_t stInLength = 0, // length of input buffer (UniUconv*Ucs)
1531 stOutLength = 0, // length of output buffer (UniUconv*Ucs)
1532 stSubstitutions = 0; // substitution count (UniUconv*Ucs)
1533 BOOL fHabTerm = TRUE; // terminate HAB ourselves?
1534 HAB hab; // anchor-block handle (for Win*)
1535 HMQ hmq; // message-queue handle
1536 HATOMTBL hSATbl; // handle to system atom table
1537 ATOM cf_TextUnicode; // "text/unicode" clipboard format atom
1538 PPIB ppib; // process information block
1539 PTIB ptib; // thread information block
1540
1541
1542 // Reset the error indicator
1543 WriteErrorCode( 0, NULL );
1544
1545 // Arguments: these are all optional, but must be correct if specified
1546 if ( argc >= 1 ) {
1547 // first argument: target codepage
1548 if ( RXVALIDSTRING(argv[0]) && sscanf(argv[0].strptr, "%d", &ulTargetCP) < 1 )
1549 return ( 40 );
1550 }
1551 if ( argc >= 2 ) {
1552 // second argument: substitution character
1553 if ( RXVALIDSTRING(argv[1]) && sscanf(argv[1].strptr, "%4X", &usSub ) < 1 )
1554 return ( 40 );
1555 }
1556 if ( argc >= 3 ) {
1557 // third argument: control flag
1558 if ( RXVALIDSTRING(argv[2]) ) {
1559 strupr( argv[2].strptr );
1560 if ( strcspn(argv[2].strptr, "DGCL") > 0 ) return ( 40 );
1561 switch ( argv[2].strptr[0] ) {
1562 case 'G': fMap = CONTROLS_MAP_DISPLAY; break;
1563 case 'C': fMap = CONTROLS_MAP_CDRA; break;
1564 case 'L': fMap = CONTROLS_MAP_CRLF; break;
1565 default : fMap = CONTROLS_MAP_DATA; break;
1566 }
1567 } else fMap = CONTROLS_MAP_DATA;
1568 }
1569 if ( argc >= 4 ) {
1570 // fourth argument: path flag
1571 if ( RXVALIDSTRING(argv[3]) ) {
1572 strupr( argv[3].strptr );
1573 if ( strcspn(argv[3].strptr, "YN") > 0 ) return ( 40 );
1574 switch ( argv[3].strptr[0] ) {
1575 case 'N': fPath = DBCS_PATH_NO; break;
1576 default : fPath = DBCS_PATH_YES; break;
1577 }
1578 } else fPath = DBCS_PATH_YES;
1579 }
1580
1581 // Initialize the PM API
1582 DosGetInfoBlocks( &ptib, &ppib );
1583 ulPType = ppib->pib_ultype;
1584 ppib->pib_ultype = 3;
1585 hab = WinInitialize( 0 );
1586 if ( !hab ) {
1587 fHabTerm = FALSE;
1588 hab = 1;
1589 }
1590
1591 /* Note: A message-queue must exist before we can access the clipboard. We
1592 * don't actually use the returned value. In fact, we don't even
1593 * verify it, because it could be NULLHANDLE if this function was
1594 * called from a PM process (e.g. VX-REXX) - in which case, a message
1595 * queue should already exist, and we can proceed anyway.
1596 */
1597 hmq = WinCreateMsgQueue( hab, 0 );
1598
1599 // Make sure the Unicode clipboard format is registered
1600 hSATbl = WinQuerySystemAtomTable();
1601 cf_TextUnicode = WinAddAtom( hSATbl, "text/unicode");
1602
1603 // Open the clipboard
1604 ulRC = WinOpenClipbrd( hab );
1605 if ( ulRC ) {
1606 // Read Unicode text from the clipboard, if available
1607 if (( psuClipText = (UniChar *) WinQueryClipbrdData( hab, cf_TextUnicode )) != NULL ) {
1608
1609 ulChars = UniStrlen( psuClipText );
1610 // Convert it to the target codepage
1611 if ( ulTargetCP == 1200 ) {
1612 // "Converting" to UCS-2; simply return the UniChar array as bytes
1613 ulBytes = ulChars * sizeof( UniChar );
1614 pszFinal = (PSZ) malloc( ulBytes );
1615 j = 0;
1616 for ( i = 0; i < ulChars; i++ ) {
1617 pszFinal[j++] = BYTE1FROMUNICHAR( psuClipText[i] );
1618 pszFinal[j++] = BYTE2FROMUNICHAR( psuClipText[i] );
1619 }
1620 if ( ! SaveResultString( prsResult, pszFinal, ulBytes )) {
1621 MAKERXSTRING( *prsResult, "", 0 );
1622 }
1623 free( pszFinal );
1624 } else {
1625 // Create the UCS-to-target conversion object
1626 ulRC = CreateUconvObject( &uconvTarget, ulTargetCP, 0, fMap, fPath, usSub );
1627 if ( ulRC == ULS_SUCCESS ) {
1628
1629 // Convert the string to the target codepage
1630 // (Allow up to 4x the length of the Unicode string)
1631 stInLength = ulChars;
1632 stOutLength = stInLength * 4;
1633 pszFinal = (PSZ) malloc( stOutLength + 1 );
1634 psuOffset = psuClipText;
1635 pszOffset = pszFinal;
1636 stSubstitutions = 0;
1637 memset( pszFinal, 0, stOutLength + 1 );
1638 ulRC = UniUconvFromUcs( uconvTarget, &psuOffset, &stInLength,
1639 (PVOID *) &pszOffset, &stOutLength, &stSubstitutions );
1640 if ( ulRC == ULS_SUCCESS ) {
1641 // Return the final converted string
1642 if ( ! SaveResultString( prsResult, pszFinal, strlen(pszFinal) )) {
1643 MAKERXSTRING( *prsResult, "", 0 );
1644 }
1645 } else {
1646 // UniUconvFromUcs failed
1647 WriteErrorCode( ulRC, "UniUconvFromUcs");
1648 MAKERXSTRING( *prsResult, "", 0 );
1649 }
1650 free( pszFinal );
1651 UniFreeUconvObject( uconvTarget );
1652
1653 } else {
1654 // Failed to create target UconvObject
1655 WriteErrorCode( ulRC, "UniCreateUconvObject");
1656 MAKERXSTRING( *prsResult, "", 0 );
1657 }
1658 }
1659 } else {
1660 // Either no text exists, or clipboard is not readable
1661 MAKERXSTRING( *prsResult, "", 0 );
1662 }
1663
1664 WinCloseClipbrd( hab );
1665 } else {
1666 WriteErrorCode( ulRC, "WinOpenClipbrd");
1667 MAKERXSTRING( *prsResult, "", 0 );
1668 }
1669
1670 WinDeleteAtom( hSATbl, cf_TextUnicode );
1671 if ( hmq != NULLHANDLE ) WinDestroyMsgQueue( hmq );
1672 if ( fHabTerm ) WinTerminate( hab );
1673
1674 ppib->pib_ultype = ulPType;
1675
1676 return ( 0 );
1677}
1678
1679
1680/* ------------------------------------------------------------------------- *
1681 * UlsFormatTime *
1682 * *
1683 * Convert a number of seconds from the epoch (1970-01-01 0:00:00) into a *
1684 * formatted date and time string for the specified locale. *
1685 * *
1686 * REXX ARGUMENTS: *
1687 * 1. Number of seconds (a positive integer) to be converted. (REQUIRED) *
1688 * 2. UniStrftime format string. DEFAULT: full date/time string ('%c') *
1689 * 3. UTC flag (indicates whether to return the time in UTC or local time, *
1690 * one of: *
1691 * Y = return in Universal Coordinated Time *
1692 * N = return in local time (DEFAULT) *
1693 * 4. Target locale name (DEFAULT: current locale) *
1694 * 5. Target codepage number (DEFAULT: current codepage) *
1695 * 6. Substitution character (hex) (DEFAULT: varies by codepage) *
1696 * 7. Control flag (how to treat byte values 0x00-0x19,0x7F), one of: *
1697 * D = data, treat as control bytes and do not convert (DEFAULT) *
1698 * G = glyphs, treat as glyphs and convert according to codepage *
1699 * C = cdra, treat as control bytes and convert using IBM mapping *
1700 * L = linebreak, treat CR and LF as controls, all others as glyphs *
1701 * *
1702 * REXX RETURN VALUE: The formatted time string *
1703 * ------------------------------------------------------------------------- */
1704ULONG APIENTRY ULSFormatTime( PSZ pszName, ULONG argc, RXSTRING argv[], PSZ pszQueue, PRXSTRING prsResult )
1705{
1706 UconvObject uconv;
1707 LocaleObject locale;
1708 UniChar psuTime[ US_STRFTIME_MAXZ ] = {0},
1709 *psuOffset,
1710 *psuFormat;
1711 PSZ pszTime,
1712 pszOffset;
1713 ULONG ulCP = 0,
1714 ulRC = 0;
1715 USHORT usSub = 0,
1716 fMap = CONTROLS_MAP_DATA;
1717 BOOL fUTC = FALSE;
1718 time_t ttValue;
1719 struct tm *timeptr;
1720 size_t stInLength,
1721 stOutLength,
1722 stSubs;
1723
1724
1725 // Reset the error indicator
1726 WriteErrorCode( 0, NULL );
1727
1728 // Make sure we have at least one valid argument (the time value)
1729 if ( argc < 1 || ( !RXVALIDSTRING(argv[0]) )) return ( 40 );
1730 if (( sscanf( argv[0].strptr, "%u", &ttValue )) != 1 ) return ( 40 );
1731
1732 // Other arguments are optional but must be correct if specified
1733
1734 // Second argument: UniStrftime() format string
1735 if ( argc >= 2 && RXVALIDSTRING(argv[1]) ) {
1736 psuFormat = (UniChar *) calloc( argv[1].strlength + 1, sizeof(UniChar) );
1737 ulRC = UniCreateUconvObject( (UniChar *) L"@map=display,path=yes", &uconv );
1738 if ( ulRC == ULS_SUCCESS ) {
1739 ulRC = UniStrToUcs( uconv, psuFormat, argv[1].strptr, argv[1].strlength + 1 );
1740 UniFreeUconvObject( uconv );
1741 if ( ulRC != ULS_SUCCESS ) {
1742 WriteErrorCode( ulRC, "UniStrToUcs");
1743 MAKERXSTRING( *prsResult, "", 0 );
1744 return 0;
1745 }
1746 } else {
1747 WriteErrorCode( ulRC, "UniCreateUconvObject");
1748 MAKERXSTRING( *prsResult, "", 0 );
1749 return 0;
1750 }
1751 } else {
1752 psuFormat = (UniChar *) calloc( 3, sizeof(UniChar) );
1753 psuFormat[ 0 ] = L'%';
1754 psuFormat[ 1 ] = L'c';
1755 }
1756
1757 // Third argument: conversion flag
1758 if ( argc >= 3 ) {
1759 if ( RXVALIDSTRING(argv[2]) ) {
1760 strupr( argv[2].strptr );
1761 if ( strcspn(argv[2].strptr, "YN") > 0 ) {
1762 free( psuFormat );
1763 return ( 40 );
1764 }
1765 switch ( argv[2].strptr[0] ) {
1766 case 'Y': fUTC = TRUE; break;
1767 default : fUTC = FALSE; break;
1768 }
1769 }
1770 }
1771
1772 // Fourth argument: locale
1773 if ( argc >= 4 && RXVALIDSTRING(argv[3]) )
1774 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, argv[3].strptr, &locale );
1775 else
1776 ulRC = UniCreateLocaleObject( UNI_MBS_STRING_POINTER, "", &locale );
1777 if ( ulRC != ULS_SUCCESS ) {
1778 free( psuFormat );
1779 WriteErrorCode( ulRC, "UniCreateLocaleObject");
1780 MAKERXSTRING( *prsResult, "", 0 );
1781 return 0;
1782 }
1783
1784 // Fifth argument: target codepage number
1785 if ( argc >= 5 ) {
1786 if ( RXVALIDSTRING(argv[4]) && sscanf(argv[4].strptr, "%u", &ulCP) < 1 ) {
1787 free( psuFormat );
1788 UniFreeLocaleObject( locale );
1789 return ( 40 );
1790 }
1791 }
1792
1793 // Sixth argument: substitution character
1794 if ( argc >= 6 ) {
1795 if ( RXVALIDSTRING(argv[5]) && sscanf(argv[5].strptr, "%4X", &usSub ) < 1 ) {
1796 free( psuFormat );
1797 UniFreeLocaleObject( locale );
1798 return ( 40 );
1799 }
1800 }
1801
1802 // Seventh argument: control flag
1803 if ( argc >= 7 && RXVALIDSTRING(argv[6]) ) {
1804 if (( fMap = ParseUconvMapFlag( argv[6] )) == CONV_FLAG_INVALID ) {
1805 free( psuFormat );
1806 UniFreeLocaleObject( locale );
1807 return ( 40 );
1808 }
1809 }
1810
1811 tzset();
1812 if ( fUTC ) {
1813 if (( timeptr = gmtime( &ttValue )) == NULL ) {
1814 free( psuFormat );
1815 UniFreeLocaleObject( locale );
1816 WriteErrorCode( 0, "gmtime");
1817 MAKERXSTRING( *prsResult, "", 0 );
1818 return ( 0 );
1819 }
1820 } else {
1821 if (( timeptr = localtime( &ttValue )) == NULL ) {
1822 free( psuFormat );
1823 UniFreeLocaleObject( locale );
1824 WriteErrorCode( 0, "localtime");
1825 MAKERXSTRING( *prsResult, "", 0 );
1826 return ( 0 );
1827 }
1828 }
1829
1830 if (( ulRC = UniStrftime( locale, psuTime, US_STRFTIME_MAXZ - 1, psuFormat, timeptr )) > 0 )
1831 {
1832 if (( ulRC = CreateUconvObject( &uconv, ulCP, UCS_ENDIAN_SYSTEM, fMap, DBCS_PATH_YES, usSub )) == ULS_SUCCESS )
1833 {
1834 stInLength = UniStrlen( psuTime );
1835 stOutLength = stInLength * 4;
1836 pszTime = (PSZ) calloc( stOutLength + 1 , sizeof(char) );
1837 psuOffset = psuTime;
1838 pszOffset = pszTime;
1839 stSubs = 0;
1840 if (( ulRC = UniUconvFromUcs( uconv, &psuOffset, &stInLength,
1841 (PPVOID) &pszOffset, &stOutLength, &stSubs )) == ULS_SUCCESS )
1842 {
1843 if ( ! SaveResultString( prsResult, pszTime, strlen(pszTime) ))
1844 MAKERXSTRING( *prsResult, "", 0 );
1845 } else {
1846 WriteErrorCode( ulRC, "UniUconvFromUcs");
1847 MAKERXSTRING( *prsResult, "", 0 );
1848 }
1849 free( pszTime );
1850 UniFreeUconvObject( uconv );
1851
1852 } else {
1853 WriteErrorCode( ulRC, "UniCreateUconvObject");
1854 MAKERXSTRING( *prsResult, "", 0 );
1855 }
1856
1857 } else {
1858 WriteErrorCode( ulRC, "UniStrftime");
1859 MAKERXSTRING( *prsResult, "", 0 );
1860 }
1861
1862 UniFreeLocaleObject( locale );
1863 return 0;
1864}
1865
1866
1867/* ------------------------------------------------------------------------- *
1868 * ParseUconvMapFlag *
1869 * *
1870 * Parses the 'map' flag used by various functions that do conversions. *
1871 * *
1872 * ARGUMENTS: *
1873 * RXSTRING rxstring: Input RXSTRING containing the specified flag. (I) *
1874 * *
1875 * RETURNS: USHORT *
1876 * The requested flag value. *
1877 * ------------------------------------------------------------------------- */
1878USHORT ParseUconvMapFlag( RXSTRING rxstring )
1879{
1880 USHORT fMap;
1881
1882 if ( RXVALIDSTRING(rxstring) ) {
1883 switch ( toupper(rxstring.strptr[0]) ) {
1884 case 'G': fMap = CONTROLS_MAP_DISPLAY; break;
1885 case 'C': fMap = CONTROLS_MAP_CDRA; break;
1886 case 'L': fMap = CONTROLS_MAP_CRLF; break;
1887 case 'D': fMap = CONTROLS_MAP_DATA; break;
1888 default : fMap = CONV_FLAG_INVALID; break;
1889 }
1890 } else fMap = CONTROLS_MAP_DATA;
1891 return ( fMap );
1892}
1893
1894
1895/* ------------------------------------------------------------------------- *
1896 * ParseUconvPathFlag *
1897 * *
1898 * Parses the 'path' flag used by various functions that do conversions. *
1899 * *
1900 * ARGUMENTS: *
1901 * RXSTRING rxstring: Input RXSTRING containing the specified flag (I). *
1902 * *
1903 * RETURNS: USHORT *
1904 * The requested flag value. *
1905 * ------------------------------------------------------------------------- */
1906USHORT ParseUconvPathFlag( RXSTRING rxstring )
1907{
1908 USHORT fPath;
1909
1910 if ( RXVALIDSTRING(rxstring) ) {
1911 switch ( toupper(rxstring.strptr[0]) ) {
1912 case 'N': fPath = DBCS_PATH_NO; break;
1913 case 'Y': fPath = DBCS_PATH_YES; break;
1914 default : fPath = CONV_FLAG_INVALID; break;
1915 }
1916 } else fPath = DBCS_PATH_YES;
1917 return ( fPath );
1918}
1919
1920
1921/* ------------------------------------------------------------------------- *
1922 * CreateUconvObject *
1923 * *
1924 * Creates a conversion object (UconvObject) for the specified codepage with *
1925 * the specified modifiers. (This object should be freed when no longer *
1926 * needed using UniFreeUconvObject.) *
1927 * *
1928 * ARGUMENTS: *
1929 * UconvObject *uco : Pointer to the UconvObject to be created (O) *
1930 * ULONG ulCP : The requested codepage (I) *
1931 * USHORT fEndian : The endian type to use (I) *
1932 * USHORT fMap : The control-character mapping to use (I) *
1933 * USHORT fPath : The DBCS path flag to use (I) *
1934 * UCHAR ucSubChar: The codepage substitution character to use (I) *
1935 * *
1936 * RETURNS: ULONG *
1937 * Return code from UniCreateUconvObject. *
1938 * ------------------------------------------------------------------------- */
1939ULONG CreateUconvObject( UconvObject *uco, ULONG ulCP, USHORT fEndian, USHORT fMap, USHORT fPath, USHORT ucSubChar )
1940{
1941 UniChar suCP[ US_CPSPEC_MAXZ ], // conversion specifier
1942 suSub[ 3 ]; // used to store subchar modifier
1943 UCHAR szSub[ 3 ]; // used to generate subchar modifier
1944 ULONG ulRC; // return code
1945// uconv_attribute_t ucattr;
1946// int i;
1947
1948 memset( suCP, 0, sizeof(suCP) );
1949
1950 // Generate the codepage name string
1951 if ( ulCP == 0 ) suCP[ 0 ] = L'@';
1952 else {
1953 ulRC = UniMapCpToUcsCp( ulCP, suCP, 12 );
1954 if ( ulRC != ULS_SUCCESS ) return ( ulRC );
1955 UniStrcat( suCP, (UniChar *) L"@");
1956 }
1957
1958 // Generate the conversion modifier string
1959 switch ( fEndian ) {
1960 case UCS_ENDIAN_BIG: UniStrcat( suCP, (UniChar *) L"endian=big,"); break;
1961 default:
1962 case UCS_ENDIAN_SYSTEM: UniStrcat( suCP, (UniChar *) L"endian=system,"); break;
1963 }
1964 switch ( fMap ) {
1965 case CONTROLS_MAP_DISPLAY: UniStrcat( suCP, (UniChar *) L"map=display,"); break;
1966 case CONTROLS_MAP_CDRA: UniStrcat( suCP, (UniChar *) L"map=cdra,"); break;
1967 case CONTROLS_MAP_CRLF: UniStrcat( suCP, (UniChar *) L"map=crlf,"); break;
1968 default:
1969 case CONTROLS_MAP_DATA: UniStrcat( suCP, (UniChar *) L"map=data,"); break;
1970 }
1971 switch ( fPath ) {
1972 case DBCS_PATH_NO: UniStrcat( suCP, (UniChar *) L"path=no"); break;
1973 default:
1974 case DBCS_PATH_YES: UniStrcat( suCP, (UniChar *) L"path=yes"); break;
1975 }
1976 if ( ucSubChar > 0 ) {
1977 sprintf( szSub, "%02X", ucSubChar );
1978 suSub[0] = (UniChar) szSub[0];
1979 suSub[1] = (UniChar) szSub[1];
1980 suSub[2] = 0x0000;
1981 UniStrcat( suCP, (UniChar *) L",subchar=\\x");
1982 UniStrcat( suCP, suSub );
1983 }
1984
1985 // Create the conversion object
1986// printf("\n[Conversion: \"%ls\"]\n", suCP );
1987 ulRC = UniCreateUconvObject( suCP, uco );
1988 return ( ulRC );
1989}
1990
1991
1992/* ------------------------------------------------------------------------- *
1993 * InputStringToUnicode *
1994 * *
1995 * Converts an input RXSTRING to Unicode (i.e. a UCS-2 UniChar sequence), *
1996 * assuming that it starts out in the specified codepage. If the input *
1997 * codepage is given as UCS-2 already (or if the input UconvObject is NULL), *
1998 * the input string is simply copied directly into the output buffer. *
1999 * Otherwise, a valid UconvObject must be provided. *
2000 * *
2001 * A pointer to the output string must be provided. This will be allocated *
2002 * by the function; the caller is responsible for free()ing it. *
2003 * *
2004 * ARGUMENTS: *
2005 * RXSTRING rxstring : The RXSTRING to be converted. (I) *
2006 * UniChar **psuOutput : The converted UCS-2 output sequence. (O) *
2007 * UconvObject uconv : The UCS conversion object to be used. (I) *
2008 * ULONG ulSourceCP: 1200 if the source codepage is UCS-2; may be 0 *
2009 * otherwise. (I) *
2010 * *
2011 * RETURNS: BOOL *
2012 * TRUE if the conversion succeeded, in which case *psuOutput will be *
2013 * non-NULL. FALSE if an error occurred, in which case *psuOutput will be *
2014 * unallocated. *
2015 * ------------------------------------------------------------------------- */
2016BOOL InputStringToUnicode( RXSTRING rxstring, UniChar **psuOutput, UconvObject uconv, ULONG ulSourceCP )
2017{
2018 UniChar *psuConverted, // converted output sequence (UCS-2)
2019 *psuOffset; // offset into psuConverted
2020 PSZ pszOffset; // offset into the input string
2021 ULONG ulChars, // length of psuConverted (in UniChars)
2022 ulRC, // return code
2023 i, j; // index counters
2024 UCHAR ucFirst, // first byte of a UCS-2 pair
2025 ucSecond; // second byte of a UCS-2 pair
2026 size_t stInLength = 0, // length of input buffer (UniUconv*Ucs)
2027 stOutLength = 0, // length of output buffer (UniUconv*Ucs)
2028 stSubstitutions = 0; // substitution count (UniUconv*Ucs)
2029 BOOL fSuccess = TRUE; // success indicator to return
2030
2031
2032 if (( ulSourceCP == 1200 ) || ( uconv == NULL )) {
2033 // Input string is already UCS-2; read it directly into a UniChar array
2034 psuConverted = (UniChar *) malloc( rxstring.strlength + sizeof(UniChar) );
2035 if ( psuConverted ) {
2036 ulChars = rxstring.strlength / 2;
2037 j = 0;
2038 for ( i = 0; i < ulChars; i++ ) {
2039 ucFirst = rxstring.strptr[ j++ ];
2040 ucSecond = rxstring.strptr[ j++ ];
2041 psuConverted[ i ] = UNICHARFROM2BYTES( ucFirst, ucSecond );
2042 }
2043 psuConverted[ i ] = 0x0000;
2044 } else {
2045 WriteErrorCode( errno, "malloc");
2046 fSuccess = FALSE;
2047 }
2048 } else {
2049 // Convert the input string to UCS-2
2050 stInLength = rxstring.strlength;
2051 stOutLength = stInLength;
2052 psuConverted = (UniChar *) calloc( stOutLength + 1, sizeof(UniChar) );
2053 pszOffset = rxstring.strptr;
2054 psuOffset = psuConverted;
2055 if ( psuConverted ) {
2056 ulRC = UniUconvToUcs( uconv, (PPVOID) &pszOffset, &stInLength,
2057 &psuOffset, &stOutLength, &stSubstitutions );
2058 if ( ulRC != ULS_SUCCESS ) {
2059 WriteErrorCode( ulRC, "UniUconvToUcs");
2060 fSuccess = FALSE;
2061 free( psuConverted );
2062 }
2063 } else {
2064 WriteErrorCode( errno, "malloc");
2065 fSuccess = FALSE;
2066 }
2067 }
2068
2069 *psuOutput = psuConverted;
2070 return ( fSuccess );
2071}
2072
2073
2074/* ------------------------------------------------------------------------- *
2075 * SaveResultString *
2076 * *
2077 * Writes new string contents to the specified RXSTRING, allocating any *
2078 * additional memory that may be required. If the string to be written has *
2079 * zero length, nothing is done. *
2080 * *
2081 * ARGUMENTS: *
2082 * PRXSTRING prsResult: Pointer to an existing RXSTRING for writing. *
2083 * PCH pchBytes : The string contents to write to prsResult. *
2084 * ULONG ulBytes : The number of bytes in pchBytes to write. *
2085 * *
2086 * RETURNS: BOOL *
2087 * TRUE if prsResult was successfully updated. FALSE otherwise. *
2088 * ------------------------------------------------------------------------- */
2089BOOL SaveResultString( PRXSTRING prsResult, PCH pchBytes, ULONG ulBytes )
2090{
2091 ULONG ulRC;
2092 PCH pchNew;
2093
2094 if ( ulBytes == 0 ) return ( FALSE );
2095 if ( ulBytes > 256 ) {
2096 // REXX provides 256 bytes by default; allocate more if necessary
2097 ulRC = DosAllocMem( (PVOID) &pchNew, ulBytes, PAG_WRITE | PAG_COMMIT );
2098 if ( ulRC != 0 ) {
2099 WriteErrorCode( ulRC, "DosAllocMem");
2100 return ( FALSE );
2101 }
2102 prsResult->strptr = pchNew;
2103 }
2104 memcpy( prsResult->strptr, pchBytes, ulBytes );
2105 prsResult->strlength = ulBytes;
2106
2107 return ( TRUE );
2108}
2109
2110
2111/* ------------------------------------------------------------------------- *
2112 * WriteStemElement *
2113 * *
2114 * Creates a stem element (compound variable) in the calling REXX program *
2115 * using the REXX shared variable pool interface. *
2116 * *
2117 * ARGUMENTS: *
2118 * PSZ pszStem : The name of the stem (before the '.') *
2119 * ULONG ulIndex : The number of the stem element (after the '.') *
2120 * PSZ pszValue : The value to write to the compound variable. *
2121 * *
2122 * RETURNS: N/A *
2123 * ------------------------------------------------------------------------- */
2124void WriteStemElement( PSZ pszStem, ULONG ulIndex, PSZ pszValue )
2125{
2126 SHVBLOCK shvVar; // REXX shared variable pool block
2127 ULONG ulRc;
2128 CHAR szCompoundName[ US_COMPOUND_MAXZ ];
2129
2130 sprintf( szCompoundName, "%s.%d", pszStem, ulIndex );
2131 MAKERXSTRING( shvVar.shvname, szCompoundName, strlen(szCompoundName) );
2132 MAKERXSTRING( shvVar.shvvalue, pszValue, strlen(pszValue) );
2133 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2134 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2135 shvVar.shvcode = RXSHV_SYSET;
2136 shvVar.shvnext = NULL;
2137 ulRc = RexxVariablePool( &shvVar );
2138 if ( ulRc > 1 )
2139 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
2140
2141}
2142
2143
2144/* ------------------------------------------------------------------------- *
2145 * WriteErrorCode *
2146 * *
2147 * Writes an error code to a special variable in the calling REXX program *
2148 * using the REXX shared variable pool interface. This is used to return *
2149 * API error codes to the REXX program, since the REXX functions themselves *
2150 * normally return string values. *
2151 * *
2152 * ARGUMENTS: *
2153 * ULONG ulError : The error code returned by the failing API call. *
2154 * PSZ pszContext: A string describing the API call that failed. *
2155 * *
2156 * RETURNS: N/A *
2157 * ------------------------------------------------------------------------- */
2158void WriteErrorCode( ULONG ulError, PSZ pszContext )
2159{
2160 SHVBLOCK shvVar; // REXX shared variable pool block
2161 ULONG ulRc;
2162 CHAR szErrorText[ US_ERRSTR_MAXZ ];
2163
2164 if ( pszContext == NULL )
2165 sprintf( szErrorText, "%X", ulError );
2166 else
2167 sprintf( szErrorText, "%X: %s", ulError, pszContext );
2168 MAKERXSTRING( shvVar.shvname, SZ_ERROR_NAME, strlen(SZ_ERROR_NAME) );
2169 MAKERXSTRING( shvVar.shvvalue, szErrorText, strlen(szErrorText) );
2170 shvVar.shvnamelen = RXSTRLEN( shvVar.shvname );
2171 shvVar.shvvaluelen = RXSTRLEN( shvVar.shvvalue );
2172 shvVar.shvcode = RXSHV_SYSET;
2173 shvVar.shvnext = NULL;
2174 ulRc = RexxVariablePool( &shvVar );
2175 if ( ulRc > 1 )
2176 printf("Unable to set %s: rc = %d\n", shvVar.shvname.strptr, shvVar.shvret );
2177}
2178
2179
Note: See TracBrowser for help on using the repository browser.