File: op_fnzpeek.c

package info (click to toggle)
fis-gtm 7.1-006-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 32,908 kB
  • sloc: ansic: 344,906; asm: 5,184; csh: 4,859; sh: 2,000; awk: 294; makefile: 73; sed: 13
file content (756 lines) | stat: -rw-r--r-- 28,077 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
/****************************************************************
 *								*
 * Copyright (c) 2013-2024 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/
#include "mdef.h"

#include <sys/types.h>

#include "gtm_signal.h"
#include "gtm_unistd.h"
#include "gtm_string.h"
#include "gtm_time.h"

#include "send_msg.h"
#include "error.h"
#include "stringpool.h"
#include "util.h"
#include "op.h"
#include "nametabtyp.h"
#include "namelook.h"
#include "gdsroot.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "repl_msg.h"
#include "gtmsource.h"
#include "gtmrecv.h"
#include "anticipatory_freeze.h"
#include "gtm_caseconv.h"
#include "jnl.h"
#include "dollarh.h"

error_def(ERR_BADZPEEKARG);
error_def(ERR_BADZPEEKFMT);
error_def(ERR_BADZPEEKRANGE);
error_def(ERR_MAXSTRLEN);
error_def(ERR_ZPEEKNOJNLINFO);
error_def(ERR_ZPEEKNORPLINFO);

#define FMTHEXDGT(spfree, digit) *spfree++ = digit + ((digit <= 9) ? '0' : ('A' - 0x0A))
#define ARGUMENT_MAX_LEN	MAX_MIDENT_LEN
#define	PASS1			1
#define	PASS2			2

/* Codes for peek operation mnemonics */
typedef enum
{
	PO_CSAREG = 0,	/* 0 Region information - sgmnt_addrs struct - process private structure */
	PO_FHREG,	/* 1 Fileheader information from sgmnt_data for specified region */
	PO_GDRREG,	/* 2 Region information - gd_region struct - process private structure */
	PO_NLREG,	/* 3 Fileheader information from node_local for specified region (transient - non permanent) */
	PO_NLREPL,	/* 4 Fileheader information from node_local for replication dummy region */
	PO_GLFREPL,	/* 5 Replication information from gtmsrc_lcl_array structure */
	PO_GSLREPL,	/* 6 Replication information from gtmsource_local_array structure */
	PO_JPCREPL,	/* 7 Replication information from jnlpool_ctl structure */
	PO_PEEK,	/* 8 Generalized peek specifying (base) address argument */
	PO_RIHREPL,	/* 9 Replication information from repl_inst_hdr structure */
	PO_RPCREPL,	/* 10 Replication information from recvpool_ctl_struct */
	PO_UPLREPL,	/* 11 Replication information from upd_proc_local_struct */
	PO_GRLREPL,	/* 12 Replication information from gtmrecv_local_struct */
	PO_UHCREPL,	/* 13 Replication information from upd_helper_ctl */
	PO_JNLREG,	/* 14 Journal information - jnl_private_control */
	PO_JBFREG	/* 15 Journal buffer information - jnl_buffer_ptr_t */
} zpeek_mnemonic;

GBLREF boolean_t        	created_core;
GBLREF sigset_t			blockalrm;
GBLREF gd_addr			*gd_header;
GBLREF int			pool_init;
GBLREF boolean_t		jnlpool_init_needed;
GBLREF jnlpool_addrs_ptr_t	jnlpool;
GBLREF recvpool_addrs		recvpool;
#ifdef DEBUG
GBLREF	int			process_exiting;
#endif

LITREF unsigned char lower_to_upper_table[];

STATICFNDCL void op_fnzpeek_signal_handler(int sig, siginfo_t *info, void *context);
STATICFNDCL int op_fnzpeek_stpcopy(char *zpeekadr, int len, mval *ret, char fmtcode);
STATICFNDCL uchar_ptr_t op_fnzpeek_uint64fmt(uchar_ptr_t p, gtm_uint64_t n);
STATICFNDCL uchar_ptr_t op_fnzpeek_hexfmt(uchar_ptr_t p, gtm_uint64_t n, int fmtlen);
STATICFNDEF boolean_t op_fnzpeek_attach_jnlpool(void);
STATICFNDEF boolean_t op_fnzpeek_attach_recvpool(void);

typedef struct
{
	int		peekop;		/* Peek operation mnemonic id */
	boolean_t	allowargs;	/* Number of arguments allowed */
} zpeek_data_typ;

/* Lookup tables for first argument - Note names are limited to NAME_ENTRY_SZ bytes each */
LITDEF nametabent zpeek_names[] =
{					/* Array offsets */
	{3, "CSA"}, {6, "CSAREG"}	/* 0, 1 */
	,{2, "FH"}, {5, "FHREG"}	/* 2, 3 */
	,{3, "GDR"}, {6, "GDRREG"}	/* 4, 5 */
	,{3, "GLF"}, {7, "GLFREPL"}	/* 6, 7 */
	,{3, "GRL"}, {7, "GRLREPL"}	/* 8, 9 */
	,{3, "GSL"}, {7, "GSLREPL"}	/* 10, 11 */
	,{3, "JBF"}, {6, "JBFREG"}	/* 12, 13 */
	,{3, "JPC"}, {7, "JPCREPL"}	/* 14, 15 */
	,{3, "JNL"}, {6, "JNLREG"}	/* 16, 17 */
	,{2, "NL"}, {5, "NLREG"}	/* 18, 19 */
	,{6, "NLREPL"}			/* 20 */
	,{4, "PEEK"}			/* 21 */
	,{3, "RIH"}, {7, "RIHREPL"}	/* 22, 23 */
	,{3, "RPC"}, {7, "RPCREPL"}	/* 24, 25 */
	,{3, "UHC"}, {7, "UHCREPL"}	/* 26, 27 */
	,{3, "UPL"}, {7, "UPLREPL"}	/* 28, 29 */
	                                /* Total length 30 */
};
/* Index to first entry with given starting letter */
LITDEF unsigned char zpeek_index[] =
{
	 0,  0,  0,  2,  2,  2,  4, 12, 12,	/* a b c d e f g h i */
	12, 18, 18, 18, 18, 21, 21, 22, 22,	/* j k l m n o p q r */
	26, 26, 26, 30, 30, 30, 30, 30, 30	/* s t u v w x y z ~ */
};
/* Associated fetch code for each entry with flag for whether arguments accepted after code (e.g. CSAREG:MUMPS) */
LITDEF zpeek_data_typ zpeek_data[] =
{
	{PO_CSAREG, 1}, {PO_CSAREG, 1}
	,{PO_FHREG, 1}, {PO_FHREG, 1}
	,{PO_GDRREG, 1}, {PO_GDRREG, 1}
	,{PO_GLFREPL, 1}, {PO_GLFREPL, 1}
	,{PO_GRLREPL, 0}, {PO_GRLREPL, 0}
	,{PO_GSLREPL, 1}, {PO_GSLREPL, 1}
	,{PO_JBFREG, 1}, {PO_JBFREG, 1}
	,{PO_JPCREPL, 0}, {PO_JPCREPL, 0}
	,{PO_JNLREG, 1}, {PO_JNLREG, 1}
	,{PO_NLREG, 1}, {PO_NLREG, 1}
	,{PO_NLREPL, 0}
	,{PO_PEEK, 1}
	,{PO_RIHREPL, 0}, {PO_RIHREPL, 0}
	,{PO_RPCREPL, 0}, {PO_RPCREPL, 0}
	,{PO_UHCREPL, 0}, {PO_UHCREPL, 0}
	,{PO_UPLREPL, 0}, {PO_UPLREPL, 0}
};

/* Condition handler for use during copy of memory range to the stringpool for return. Note this condition handler is itself
 * never tripped but serves as an unwind target for the signal handler defined below (see its comments).
 */
CONDITION_HANDLER(op_fnzpeek_ch)
{
	START_CH(TRUE);
	NEXTCH;		/* In the unlikely event it gets driven, just be a pass-thru */
}

/* $ZPEEK() is processing a process memory range specified by an M routine so is definitely capable of getting
 * user inspired address type exceptions. We protect against this by setting up our signal handler to catch any
 * such exceptions for the duration of this routine and just unwind them so we can throw a non-fatal error
 * message instead.
 */
void op_fnzpeek_signal_handler(int sig, siginfo_t *info, void *context)
{
	/* We basically want to do UNWIND(NULL, NULL) logic but the UNWIND macro can only be used in a condition
	 * handler so next is a block that pretends it is our condition handler and does the needful. Note in order
	 * for this to work, we need to be wrapped in a condition handler even if that condition handler is never
	 * actually invoked to serve as the target for the UNWIND().
	 */
	{	/* Needs new block since START_CH declares a new var used in UNWIND() */
		int arg = 0;	/* Needed for START_CH macro if debugging enabled */
		START_CH(TRUE);
		assert(!process_exiting);
		UNWIND(NULL, NULL);
	}
}

/* Routine to convert gtm_uint64_t to ascii value not losing any precision. Routine is based on i2asc() but
 * uses gtm_uint64_t as the type.
 */
STATICFNDCL uchar_ptr_t op_fnzpeek_uint64fmt(uchar_ptr_t p, gtm_uint64_t n)
{
	unsigned char	ar[MAX_DIGITS_IN_INT8], *q;
	gtm_uint64_t	m;
	int		len;

	q = ar + SIZEOF(ar);
	if (!n)
		*--q = '0';
	else
	{
		while (n)
		{
			m = n / 10;
			*--q = n - (m * 10) + '0';
			n = m;
		}
	}
	assert((uintszofptr_t)q >= (uintszofptr_t)ar);
	len = (unsigned int)(ar + SIZEOF(ar) - q);
	memcpy(p, q, len);
	return p + len;
}

/* Routine to format hex output to given length with format 0xhh<hh<hhhh<hhhhhhhh>>>. Similar to i2asclx().
 *
 * p		- Output buffer (generally stringpool.free)
 * n		- Hex value to format
 * fmtlen	- Length in bytes of output value
 */
STATICFNDCL uchar_ptr_t op_fnzpeek_hexfmt(uchar_ptr_t p, gtm_uint64_t n, int fmtlen)
{
	unsigned char	ar[MAX_HEX_DIGITS_IN_INT8], *q;
	int		m, digits;

	q = ar + SIZEOF(ar);
	for (digits = fmtlen; (0 < digits); --digits)
	{
		m = n & 0xF;
		if (m <= 9)
			*--q = m + '0';
		else
			*--q = m - 0xa + 'A';
		n = n >> 4;
	}
	assert(0 == n);		/* Verify complete number has been output (no truncated digits) */
	memcpy(p, q, fmtlen);
	return p + fmtlen;
}

/* Routine to extract and optionally format the requested data leaving it in the stringpool. This routine is protected
 * by a signal handler for data access against SIGSEGV or SIGBUS signals. Note the fields that are sub-integer (1 or
 * 2 bytes) are pulled into integer forms before processing.
 */
STATICFNDEF int op_fnzpeek_stpcopy(char *zpeekadr, int len, mval *ret, char fmtcode)
{
	unsigned int	uint;
	boolean_t	negative;
	gtm_uint64_t	uint64;
	unsigned char	*numstrstart, *numstrend;
	unsigned char	*hexchr, *hexchrend, hexc, hexdgt, *spfree;
	char		timebuff[MAXNUMLEN + 1];
	time_t		seconds;
	uint4		days;

	ESTABLISH_RET(op_fnzpeek_ch, ERR_BADZPEEKRANGE);		/* If get an exception, likely due to bad range */
	ret->mvtype = 0;						/* Prevent GC of incomplete field */
	switch(fmtcode)
	{
		case 'S':						/* Null terminated string processing */
			STRNLEN(zpeekadr, len, len);			/* Reset len to actual len, fall into "C" processing */
			/* warning - fall through */
		case 'C':						/* Character area (no processing - just copy */
			if (len > MAX_STRLEN)
			{	/* Requested string return is too large */
				REVERT;
				return ERR_MAXSTRLEN;
			}
			ENSURE_STP_FREE_SPACE(len);
			memcpy(stringpool.free, zpeekadr, len);
			ret->str.addr = (char *)stringpool.free;
			ret->str.len = len;
			stringpool.free += len;
			break;
		case 'I':						/* Initially, treat signed/unsigned the same */
		case 'U':
			negative = FALSE;
			switch(len)
			{
				case SIZEOF(gtm_uint64_t):
					/* Dealing with 8 byte integer style values is not GT.M's forte since its internal
					 * number scheme is limited to 20 digits. So use our own routine to do the conversion.
					 * Note: we could use this routine for all the below cases but on 32 bit platforms
					 * with no native 8 byte values, they would run far slower so only use this for the
					 * 8 byte values we deal with.
					 */
					uint64 = *(gtm_uint64_t *)zpeekadr;
					if ('I' == fmtcode)
					{	/* If signed, check if need to add minus sign to value and change value to
						 * positive.
						 */
						negative = (0 > (gtm_int64_t)uint64);
						if (negative)
							uint64 = (gtm_uint64_t)(-(gtm_int64_t)uint64);
					}
					fmtcode = 'u';			/* Change fmtcode to skip negative value check below */
					break;
				case SIZEOF(unsigned int):
					uint = *(unsigned int *)zpeekadr;
					break;
				case SIZEOF(short):
					uint = (unsigned int)*(unsigned short *)zpeekadr;
					break;
				case SIZEOF(char):
					uint = (unsigned int)*(unsigned char *)zpeekadr;
					break;
				default:
					REVERT;
					return ERR_BADZPEEKFMT;
			}
			if ('I' == fmtcode)
			{	/* If signed, check if need to add minus sign to value and change value to positive. Note this test
				 * is bypassed for uint64 types because the check is already made (in a differet/longer value).
				 */
				negative = (0 > (signed int)uint);
				if (negative)
					uint = (unsigned int)(-(signed int)uint);
			}
			ENSURE_STP_FREE_SPACE(MAX_DIGITS_IN_INT + negative);	/* Space to hold # */
			numstrstart = stringpool.free;
			if (negative)
				*stringpool.free++ = '-';		/* Value is negative, record in output */
			/* Use the correct formmating routine based on size */
			numstrend = (SIZEOF(gtm_uint64_t) != len) ? i2asc(stringpool.free, uint)
				: op_fnzpeek_uint64fmt(stringpool.free, uint64);
			ret->str.addr = (char *)numstrstart;
			ret->str.len = INTCAST(numstrend - numstrstart);
			stringpool.free = numstrend;
			break;
		case 'X':						/* Hex format for numeric values */
			switch(len)
			{
				case SIZEOF(gtm_uint64_t):
					uint64 = *(gtm_uint64_t *)zpeekadr;
					break;
				case SIZEOF(unsigned int):
					uint64 = (gtm_uint64_t)*(unsigned int *)zpeekadr;
					break;
				case SIZEOF(unsigned short):
					uint64 = (gtm_uint64_t)*(unsigned short *)zpeekadr;
					break;
				case SIZEOF(unsigned char):
					uint64 = (gtm_uint64_t)*(unsigned char *)zpeekadr;
					break;
				default:
					REVERT;
					return ERR_BADZPEEKFMT;
			}
			ENSURE_STP_FREE_SPACE((len * 2) + 2);
			numstrstart = stringpool.free;
			*stringpool.free++ = '0';
			*stringpool.free++ = 'x';
			numstrend = op_fnzpeek_hexfmt(stringpool.free, uint64, (len * 2));
			ret->str.addr = (char *)numstrstart;
			ret->str.len = INTCAST(numstrend - numstrstart);
			stringpool.free = numstrend;
			break;
		case 'T':						/* Time ($HOROLOG) format */
			if ((SIZEOF(uint4) != len) && (SIZEOF(gtm_uint64_t) != len))
				return ERR_BADZPEEKFMT;
			ENSURE_STP_FREE_SPACE(MAXNUMLEN + 1);
			seconds = (SIZEOF(gtm_uint64_t) == len) ? *(gtm_uint64_t *)zpeekadr : *(uint4 *)zpeekadr;
			dollarh(seconds, &days, &seconds);
			ret->str.addr = (char *)stringpool.free;
			stringpool.free  = i2asc(stringpool.free, days);
			*stringpool.free++ = ',';
			stringpool.free = i2asc(stringpool.free, (uint4)seconds);
			ret->str.len = INTCAST((char *)stringpool.free - ret->str.addr);
			break;
		case 'Z':						/* Hex format (no 0x prefix) of storage as it exists */
			if ((len * 2) > MAX_STRLEN)
			{	/* Requested string return is too large */
				REVERT;
				return ERR_MAXSTRLEN;
			}
			ENSURE_STP_FREE_SPACE(len * 2);			/* Need enough space for hex string */
			spfree = stringpool.free;
			ret->str.addr = (char *)spfree;
			hexchr = (unsigned char *)zpeekadr;
			hexchrend = hexchr + len;
			if (hexchr > hexchrend)				/* Wrapped address - range error */
			{
				REVERT;
				return ERR_BADZPEEKRANGE;
			}
			for (; hexchr < hexchrend; ++hexchr)
			{	/* Format 2 digits in each character encountered */
				hexc = *hexchr;
				hexdgt = (hexc & 0xF0) >> 4;
				FMTHEXDGT(spfree, hexdgt);
				hexdgt = (hexc & 0x0F);
				FMTHEXDGT(spfree, hexdgt);
			}
			stringpool.free = spfree;			/* "commit" string to stringpool */
			ret->str.len = len * 2;
			break;
		default:
			REVERT;
			return ERR_BADZPEEKARG;
	}
	REVERT;
	ret->mvtype = MV_STR;
	return 0;
}

/* A condition handler for when we are attaching to either the jnlpool or the gtmrecv pool. We don't
 * care why we can't get to them. On the fact that we can't is material for $ZPEEK().
 */
CONDITION_HANDLER(op_fnzpeek_getpool_ch)
{
	START_CH(TRUE);
	if (DUMPABLE)
		NEXTCH;		/* Let next (more robust) handler deal with it */
	UNWIND(NULL, NULL);
}

/* Attach to the journal pool. Separate routine so can be wrapped in a condition handler */
STATICFNDEF boolean_t op_fnzpeek_attach_jnlpool(void)
{
	ESTABLISH_RET(op_fnzpeek_getpool_ch, FALSE);
	jnlpool_init(GTMRELAXED, FALSE, NULL, NULL);		/* Attach to journal pool */
	REVERT;
	return pool_init;
}

/* Attach to the receive pool. Separate routine so can be wrapped in a condition handler */
STATICFNDEF boolean_t op_fnzpeek_attach_recvpool(void)
{
	ESTABLISH_RET(op_fnzpeek_getpool_ch, FALSE);
	recvpool_init(GTMZPEEK, FALSE);			/* Attach to receive pool */
	REVERT;
	return ((NULL != recvpool.recvpool_ctl) && recvpool.recvpool_ctl->initialized);
}

/* Generalized peek facility:
 *
 * structid - String that describes the structure
 * offset   - Offset of item within that structure.
 * len      - Length of the fetch.
 * format   - Option format character - codes described below
 * ret	    - Return mval
 */
void	op_fnzpeek(mval *structid, int offset, int len, mval *format, mval *ret)
{
	void			*zpeekadr;
	UINTPTR_T		prmpeekadr = 0;
	struct sigaction	new_action, prev_action_bus, prev_action_segv;
	sigset_t		savemask;
	int			errtoraise, rc, rslt;
	unsigned char		fmtcode;
	boolean_t		arg_supplied, attach_success;
	unsigned char		mnemonic[NAME_ENTRY_SZ], *nptr, *cptr, *cptrend, *argptr;
	int			mnemonic_len, mnemonic_index, mnemonic_opcode, arglen, arryidx = -1;
	gd_region		*r_top, *r_ptr = NULL;
	replpool_identifier	replpool_id;
	unsigned int		full_len;
	unsigned char		argument_uc_buf[ARGUMENT_MAX_LEN];
	sgmnt_addrs		*csa;
	int			iter;
	DCL_THREADGBL_ACCESS;

	SETUP_THREADGBL_ACCESS;
	/* Make sure lookup table is setup correctly */
	assert(zpeek_index[26] == (SIZEOF(zpeek_names) / SIZEOF(nametabent)));
	assert((SIZEOF(zpeek_names) / SIZEOF(nametabent)) == (SIZEOF(zpeek_data) / SIZEOF(zpeek_data_typ)));
	/* Initialize */
	fmtcode = 'C';			/* If arg is NULL string (noundef default), provide default */
	MV_FORCE_STR(structid);
	MV_FORCE_STR(format);
	/* Parse and lookup the first arg's mnemonic and arg (if supplied) */
	for (nptr = mnemonic, cptr = (unsigned char *)structid->str.addr, cptrend = cptr + structid->str.len;
	     cptr < cptrend; ++cptr)
	{
		if (':' == *cptr)
			break;		/* End of mnemonic, start of arg */
		*nptr++ = *cptr;
	}
	arg_supplied = (cptr < cptrend);
	mnemonic_len = INTCAST(nptr - mnemonic);
	mnemonic_index = namelook(zpeek_index, zpeek_names, (char *)mnemonic, mnemonic_len);
	if (0 > mnemonic_index)
		RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic type"));
	mnemonic_opcode = zpeek_data[mnemonic_index].peekop;
	if ((arg_supplied && !zpeek_data[mnemonic_index].allowargs) || (!arg_supplied && zpeek_data[mnemonic_index].allowargs))
		RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument"));
	if (arg_supplied)
	{	/* Parse supplied argument */
		argptr = ++cptr;	/* Bump past ":" - if now have end-of-arg then arg is missing */
		if (argptr == cptrend)
			RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument"));
		arglen = INTCAST(cptrend - cptr);
		if (ARGUMENT_MAX_LEN < arglen)
			RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("mnemonic argument"));
		switch(mnemonic_opcode)
		{
		case PO_CSAREG:			/* These types have a region name argument */
		case PO_FHREG:
		case PO_GDRREG:
		case PO_NLREG:
		case PO_JNLREG:
		case PO_JBFREG:
			/* Uppercase the region name since that is what GDE does when creating them.
			 * But we want the ability to do $zpeek on statsdb regions (lower-case regions)
			 * so first check if region as is does exist. If so use that. If not, do uppercase.
			 */
			assert(arglen);
			iter = (ISLOWER_ASCII(argptr[0]) ? PASS1 : PASS2);
			for ( ; ; )
			{
				if (PASS1 != iter)
				{
					lower_to_upper(argument_uc_buf, argptr, arglen);
					argptr = argument_uc_buf;	/* Reset now to point to upper case version */
				}
				/* See if region recently used so can avoid lookup */
				if ((arglen == TREF(zpeek_regname_len)) && (0 == memcmp(argptr, TADR(zpeek_regname), arglen)))
				{	/* Fast path - no lookup necessary */
					r_ptr = TREF(zpeek_reg_ptr);
					break;
				}
				/* Region now defined - make sure it is open */
				if (!gd_header)		/* If gd_header is NULL, open gbldir */
					gvinit();
				r_ptr = gd_header->regions;
				for (r_top = r_ptr + gd_header->n_regions; ; r_ptr++)
				{
					if (r_ptr >= r_top)
					{
						if (PASS1 == iter)
							break;
						RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2,
							RTS_ERROR_LITERAL("mnemonic argument (region name)"));
					}
					if ((r_ptr->rname_len == arglen) && (0 == memcmp(r_ptr->rname, argptr, arglen)))
						break;
				}
				if (r_ptr == r_top)
				{	/* Could not find lower-case region specified. Try upper-casing it.
					 * Note that even though GDE guarantees all upper-case region names have a corresponding
					 * lower-case region name, it is possible the user specified a region name with
					 * a lower-case letter as the first letter and at least one upper-case letter in the
					 * region name. In that case, it is possible we find the region in the gld only when we
					 * convert the entire region name into upper case.
					 */
					assert(PASS1 == iter);
					iter = PASS2;
					continue;
				}
				/* Cache new region access for followup references */
				memcpy(TADR(zpeek_regname), argptr, arglen);
				TREF(zpeek_regname_len) = arglen;
				TREF(zpeek_reg_ptr) = r_ptr;
				break;
			}
			/* PO_GDRREG opcode examines only the region's fields so does not need the region to be open.
			 * All the rest need it to be open. If there are any errors in the open (e.g. statsdb specified
			 * and gtm_statsdir env var is too long etc.) then handle it by issuing an error.
			 */
			assert((PO_GDRREG == mnemonic_opcode) || r_ptr);
			if ((PO_GDRREG != mnemonic_opcode) && !r_ptr->open)
			{
				gv_init_reg(r_ptr, NULL);
				if (!r_ptr->open)
					RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2,
						RTS_ERROR_LITERAL("mnemonic argument (region name could not be opened)"));
			}
			break;
		case PO_GLFREPL:		/* These types have an array index argument */
		case PO_GSLREPL:
			arryidx = asc2i(argptr, arglen);
			if ((0 > arryidx) || (NUM_GTMSRC_LCL <= arryidx))
				RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2,
					RTS_ERROR_LITERAL("mnemonic argument (array index)"));
			break;
		case PO_PEEK:			/* Argument is address of form 0Xhhhhhhhh[hhhhhhhh] */
			if (('0' != *cptr++) || (('x' != *cptr) && ('X' != *cptr)))
				RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2,
					RTS_ERROR_LITERAL("mnemonic argument (peek base address)"));
			cptr++;			/* Bump past 'x' or 'X' - rest of arg should be hex value */
			prmpeekadr = (UINTPTR_T)GTM64_ONLY(asc_hex2l)NON_GTM64_ONLY(asc_hex2i)(cptr, arglen - 2);
			if (-1 == (INTPTR_T)prmpeekadr)
				/* Either an error occurred or the user specified the maximum address. So it's
				 * either an error from the conversion routine or an otherwise useless value.
				 */
				RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2,
					RTS_ERROR_LITERAL("mnemonic argument (peek base address)"));
			break;
		default:
			assert(FALSE);		/* Only the above types should ever have an argument */
		}
	}
	/* Figure out the address of each block to return */
	switch(mnemonic_opcode)
	{
		case PO_CSAREG:		/* r_ptr set from option processing */
			assert(r_ptr);
			zpeekadr = &FILE_INFO(r_ptr)->s_addrs;
			break;
		case PO_FHREG:		/* r_ptr set from option processing */
			assert(r_ptr);
			zpeekadr = (&FILE_INFO(r_ptr)->s_addrs)->hdr;
			break;
		case PO_GDRREG:		/* r_ptr set from option processing */
			assert(arg_supplied);	/* 4SCA: Assigned value is garbage or undefined, even though args are required */
			assert(r_ptr);
			zpeekadr = r_ptr;
			break;
		case PO_NLREG:		/* r_ptr set from option processing */
			assert(r_ptr);
			zpeekadr = (&FILE_INFO(r_ptr)->s_addrs)->nl;
			break;
		case PO_JNLREG:		/* r_ptr set from option processing */
		case PO_JBFREG:
			assert(r_ptr);
			csa = &FILE_INFO(r_ptr)->s_addrs;
			if (NULL == csa->jnl)
				RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_ZPEEKNOJNLINFO, 2, REG_LEN_STR(r_ptr));
			zpeekadr = (PO_JNLREG == mnemonic_opcode) ? (void *)csa->jnl : (void *)csa->jnl->jnl_buff;
			break;
		case PO_GLFREPL:	/* This set of opcodes all require the journal pool to be initialized. Verify it */
		case PO_GSLREPL:
		case PO_JPCREPL:
		case PO_NLREPL:
		case PO_RIHREPL:
			/* Make sure jnlpool_addrs are availble */
			if (!REPL_INST_AVAILABLE(jnlpool && jnlpool->pool_init ? jnlpool->gd_ptr : NULL))
			{
				assert(gd_header);
				RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO);
			}
			if (!pool_init)
			{
				attach_success = op_fnzpeek_attach_jnlpool();
				if (!attach_success)
					RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO);
			}
			switch(mnemonic_opcode)
			{
				case PO_GLFREPL:	/* arryidx set by option processing */
					assert(-1 != arryidx);
					zpeekadr = (jnlpool->gtmsrc_lcl_array + arryidx);
					break;
				case PO_GSLREPL:	/* arryidx set by option processing */
					assert(-1 != arryidx);
					zpeekadr = (jnlpool->gtmsource_local_array + arryidx);
					break;
				case PO_NLREPL:
					zpeekadr = (&FILE_INFO(jnlpool->jnlpool_dummy_reg)->s_addrs)->nl;
					break;
				case PO_JPCREPL:
					zpeekadr = jnlpool->jnlpool_ctl;
					break;
				case PO_RIHREPL:
					zpeekadr = jnlpool->repl_inst_filehdr;
					break;
				default:
					assert(FALSE);
					GTM_UNREACHABLE();
			}
			break;
		case PO_RPCREPL:	/* This set of opcodes all require the receive pool to be initialized. Verify it */
		case PO_GRLREPL:
		case PO_UPLREPL:
		case PO_UHCREPL:
			/* Make sure recvpool_addrs are available */
			if (!REPL_INST_AVAILABLE(jnlpool && jnlpool->pool_init ? jnlpool->gd_ptr : NULL))
				RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO);
			if (NULL == recvpool.recvpool_ctl)
			{
				attach_success = op_fnzpeek_attach_recvpool();
				if (!attach_success)
					RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(1) ERR_ZPEEKNORPLINFO);
			}
			switch(mnemonic_opcode)
			{
				case PO_RPCREPL:
					zpeekadr = recvpool.recvpool_ctl;
					break;
				case PO_GRLREPL:
					zpeekadr = recvpool.gtmrecv_local;
					break;
				case PO_UPLREPL:
					zpeekadr = recvpool.upd_proc_local;
					break;
				case PO_UHCREPL:
					zpeekadr = recvpool.upd_helper_ctl;
					break;
				default:
					assert(FALSE);
					GTM_UNREACHABLE();
			}
			break;
		case PO_PEEK:		/* prmpeekadr set up in argument processing */
			assert(prmpeekadr); /* 4SCA: Assigned value is garbage or undefined */
			zpeekadr = (void *)prmpeekadr;
			break;
		default:
			assert(FALSE);
			GTM_UNREACHABLE();
	}
	assert(NULL != zpeekadr);
	/* Check the rest of the args */
	if (0 > offset)
		RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("offset"));
	zpeekadr = (void *)((char *)zpeekadr + offset);
	if ((0 > len) || (MAX_STRLEN < len))
		RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("length"));
	if (1 < format->str.len)
		RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("format"));
	else if (1 == format->str.len)
	{	/* Validate format option */
		fmtcode = *format->str.addr;
		fmtcode = TOUPPER(fmtcode);
		switch(fmtcode)
		{
			case 'C':	/* Character data - returned as is */
			case 'I':	/* Signed integer format - up to 31 bits */
			case 'S':	/* String data - Same as 'C' except string is NULL terminated */
			case 'T':	/* Time ($H) format - length must be of 4 or 8 byte unsigned int */
			case 'U':	/* Unsigned integer format - up to 64 bits */
			case 'X':	/* Humeric hex format: e.g. 0x12AB. Total length is (2 * bytes) + 2 */
			case 'Z':	/* Hex format - not treated as numeric. Shown as occurs in memory (subject to endian)
					 * and is returned with no 0x prefix.
					 */
				break;
			default:
				RTS_ERROR_CSA_ABT(NULL, VARLSTCNT(4) ERR_BADZPEEKARG, 2, RTS_ERROR_LITERAL("format"));
		}
	}
	/* Block out timer calls that might trigger processing that could fail. We especially want to prevent
	 * nesting of signal handlers since the longjump() function used by the UNWIND macro is undefined on
	 * Tru64 when signal handlers are nested.
	 */
	SIGPROCMASK(SIG_BLOCK, &blockalrm, &savemask, rc);
	/* Setup new signal handler to just drive condition handler which will do the right thing */
	memset(&new_action, 0, SIZEOF(new_action));
	sigemptyset(&new_action.sa_mask);
	new_action.sa_flags = SA_SIGINFO;
#	ifdef __sparc
	new_action.sa_handler = op_fnzpeek_signal_handler;
#	else
	new_action.sa_sigaction = op_fnzpeek_signal_handler;
#	endif
	sigaction(SIGBUS, &new_action, &prev_action_bus);
	sigaction(SIGSEGV, &new_action, &prev_action_segv);
	/* Attempt to copy return string to stringpool which protected by our handlers. If the copy completes, the return
	 * mval is updated to point to the return string. Even errors return here so these sigactions can be reversed.
	 */
	errtoraise = op_fnzpeek_stpcopy(zpeekadr, len, ret, fmtcode);
	/* Can restore handlers now that access verified */
	sigaction(SIGBUS, &prev_action_bus, NULL);
	sigaction(SIGSEGV, &prev_action_segv, NULL);
	/* Let the timers pop again.. */
	SIGPROCMASK(SIG_SETMASK, &savemask, NULL, rc);
	/* If we didn't complete correctly, raise error */
	if (0 != errtoraise)
	{	/* The only time ERR_BADZPEEKARG is driven is when the format code is not recognized so give that error
		 * specifically with the additional args. Else just raise the error.
		 */
		if (ERR_BADZPEEKARG == errtoraise)
			rts_error_csa(CSA_ARG(NULL) VARLSTCNT(4) errtoraise, 2, RTS_ERROR_LITERAL("format"));
		rts_error_csa(CSA_ARG(NULL) VARLSTCNT(1) errtoraise);
	}
	return;
}