aboutsummaryrefslogtreecommitdiff
path: root/sys/fmtio/fprfmt.x
blob: d5e68fb6dc3172acfbb2daa88880d64deec41035 (plain) (blame)
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
# Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.

include	<ctype.h>
include	<printf.h>

.help
.nf _________________________________________________________________________
Process a format descriptor, setting the variables "decpl", "fill_char",
"format_char", and "width" in the fmtio common.  Called from PARG_
to determine the format specification for printing a variable.

Format: "%[w[.d]]C[n]", where W is the field width, D the number of decimal
places or precision, C the format type character, and N the radix numeral,
for format type FMT_RADIX only.  A negative field width signifies left
justification.  A leading zero in the W field sets the fill character to the
numeral zero (when right justifying).  Default values will be supplied if
any of the fields are omitted.  The minimum format is "%C".

If any of the fields (wdCn) have the value GET_FIELD (= "*") the value of
the field will be taken from the next PARG_ call, rather than from the
format string.  This makes it easy to vary the format specification at run
time.  For example, "%10.*g" would print a number in G-floating format,
with a constant field width of 10, and with the number of digits of precision
being given by a PARGI call at execution time (followed by a PARG_ call to
pass the value to be printed).
.endhelp ____________________________________________________________________

# The following macro marks the position in the FPRFMT procedure (saves the
# code for the needed field), and returns the not done status to PARG_.
# A subsequent call to a PARG_ (with the value of the field we are waiting for
# as argument) causes FPRFMT to be reentered at the point where we left off.

define	(waitfor,	if (ival_already_used) { fmt_state = $1; return (NOT_DONE_YET) } ; $1 ival_already_used = true)

#define	(waitfor,	if (ival_already_used) {
#			    fmt_state = $1
#			    return (NOT_DONE_YET)
#			}
#			$1 ival_already_used = true)

# FPRFMT -- Process a %W.Dn format specification.  ALL_DONE is returned when
# the format specification has been fully processed, else NOT_DONE_YET is
# returned, indicating that an additional PARG call is required to complete
# the format (which therefore contained one or more "*" specifiers).

int procedure fprfmt (ival)

int	ival					# argument value (from parg_)
bool	ival_already_used			# wait for next parg
int	ctoi(), stridx()
char	ch, chrlwr()
include "fmt.com"

begin
	# This routine functions as a coroutine.  If one of the fields in
	# the format spec is to be given in a pargi call, an early return
	# is taken.  The routine is later reentered with the value of the
	# needed field, and execution continues at the point it left off.
	# (Sorry, I could not think of a simpler way to do it...)

	switch (fmt_state) {				# return from "waitfor"
	case FMT_START:					# initial state
	    ival_already_used = false
	case GET_WIDTH_1:				# "%*.dC"
	    goto GET_WIDTH_1
	case GET_WIDTH_2:				# "%-0*.dC"
	    goto GET_WIDTH_2
	case GET_DECPL:					# "%w.*C"
	    goto GET_DECPL
	case GET_FMTCHAR:				# "%w.d*"
	    goto GET_FMTCHAR
	case GET_RADIX:					# "%w.dr*"
	    goto GET_RADIX
	case GET_OPERAND:				# used ival for format
	    goto GET_OPERAND
	}
	
	# It is not an error if there is no format string.
	if (format[ip] == EOS || format[ip] != START_OF_FORMAT) {
	    width = USE_DEFAULT
	    decpl = USE_DEFAULT
	    format_char = USE_DEFAULT
	    fill_char = ' '
	    left_justify = NO
	    fmt_state = FMT_START
	    return (ALL_DONE)
	} else
	    ip = ip + 1					# eat the "%"
	    
	if (format[ip] == GET_FIELD) {			# "%*.dC"
	    ip = ip + 1
	    waitfor (GET_WIDTH_1)			# go get field width...
	    if (ival < 0)				# ...and come back here
		left_justify = YES
	    else
		left_justify = NO

	    fill_char = ' '
	    width = abs (ival)

	} else {					# "%-0*.dC"
	    if (format[ip] == '-') {			# left or right justify
		left_justify = YES
		ip = ip + 1
	    } else
		left_justify = NO

	    fill_char = ' '				# zero or blank fill
	    if (format[ip] == '0') {
		if (IS_DIGIT (format[ip+1]) || format[ip+1] == GET_FIELD) {
		    fill_char = '0'
		    ip = ip + 1
		} else
		    fill_char = ' '
	    }

	    if (format[ip] == GET_FIELD) {
		ip = ip + 1
		waitfor (GET_WIDTH_2)			# go get field width...
		if (ival < 0)				# ... and come back here
		    left_justify = YES
		else
		    left_justify = NO
		width = abs (ival)

	    } else if (ctoi (format, ip, width) <= 0)	# "%N.dC"
		width = USE_DEFAULT
	}

	if (width == 0)					# make as big as needed
	    width = USE_DEFAULT

	if (format[ip] == '.') {			# get decpl field
	    ip = ip + 1
	    if (format[ip] == GET_FIELD) {		# "%w.*C"
		ip = ip + 1
		waitfor (GET_DECPL)
		decpl = ival
	    } else if (ctoi (format, ip, decpl) <= 0)	# "%w.NC"
		decpl = USE_DEFAULT
	} else
	    decpl = USE_DEFAULT

	if (format[ip] == GET_FIELD) {			# "%w.d*"
	    ip = ip + 1
	    waitfor (GET_FMTCHAR)
	    format_char = ival
	} else {
	    format_char = format[ip]			# "%w.dC"
	    ip = ip + 1
	}

	ch = format_char
	if (stridx (ch, "bcdefghHmMorstuwxz") <= 0) {
	    call putline (STDERR, "Warning: Unknown format type char\n")
	    call fmt_err ("", format, ip-1)
	    format_char = USE_DEFAULT

	} else if (format_char == FMT_RADIX) {		# get radix
	    ch = chrlwr (format[ip])
	    ip = ip + 1
	    if (ch == GET_FIELD) {			# "%w.dr*"
		waitfor (GET_RADIX)
		radix = ival
	    } else if (IS_DIGIT (ch)) {
		radix = TO_INTEG (ch)
	    } else if (IS_LOWER (ch)) {
		radix = ch - 'a' + 10
	    } else {
		radix = DECIMAL
		ip = ip - 1
	    }

	} else if (format_char == FMT_WHITESPACE || format_char == FMT_TOCOLUMN)
	    ival_already_used = false			# no operand

	waitfor (GET_OPERAND)				# used ival for format,
	fmt_state = FMT_START				# need to get another
	return (ALL_DONE)
end