source: rxuls/trunk/rxuls.c@ 46

Last change on this file since 46 was 46, checked in by Alex Taylor, 7 years ago

Fix ULSVersion version string (incremented to 0.6.2).
ULSTransform internal refactoring; function no longer requires input string to be at least 2 characters.

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