xref: /inferno-os/libkern/fmt.c (revision 46439007cf417cbd9ac8049bb4122c890097a0fa)
1  /*
2   * The authors of this software are Rob Pike and Ken Thompson.
3   *              Copyright (c) 2002 by Lucent Technologies.
4   * Permission to use, copy, modify, and distribute this software for any
5   * purpose without fee is hereby granted, provided that this entire notice
6   * is included in all copies of any software which is or includes a copy
7   * or modification of this software and in all copies of the supporting
8   * documentation for such software.
9   * THIS SOFTWARE IS BEING PROVIDED "AS IS", WITHOUT ANY EXPRESS OR IMPLIED
10   * WARRANTY.  IN PARTICULAR, NEITHER THE AUTHORS NOR LUCENT TECHNOLOGIES MAKE ANY
11   * REPRESENTATION OR WARRANTY OF ANY KIND CONCERNING THE MERCHANTABILITY
12   * OF THIS SOFTWARE OR ITS FITNESS FOR ANY PARTICULAR PURPOSE.
13   */
14  #include "lib9.h"
15  #include "fmtdef.h"
16  
17  enum
18  {
19  	Maxfmt = 64
20  };
21  
22  typedef struct Convfmt Convfmt;
23  struct Convfmt
24  {
25  	int	c;
26  	volatile	Fmts	fmt;	/* for spin lock in fmtfmt; avoids race due to write order */
27  };
28  
29  struct
30  {
31  	/* lock by calling _fmtlock, _fmtunlock */
32  	int	nfmt;
33  	Convfmt	fmt[Maxfmt];
34  } fmtalloc;
35  
36  static Convfmt knownfmt[] = {
37  	' ',	_flagfmt,
38  	'#',	_flagfmt,
39  	'%',	_percentfmt,
40  	'+',	_flagfmt,
41  	',',	_flagfmt,
42  	'-',	_flagfmt,
43  	'C',	_runefmt,
44  	'S',	_runesfmt,
45  	'X',	_ifmt,
46  	'b',	_ifmt,
47  	'c',	_charfmt,
48  	'd',	_ifmt,
49  	'h',	_flagfmt,
50  	'l',	_flagfmt,
51  	'n',	_countfmt,
52  	'o',	_ifmt,
53  	'p',	_ifmt,
54  	'r',	errfmt,
55  	's',	_strfmt,
56  	'u',	_flagfmt,
57  	'x',	_ifmt,
58  	0,	nil,
59  };
60  
61  int	(*doquote)(int);
62  
63  static Fmts
64  fmtfmt(int c)
65  {
66  	Convfmt *p, *ep;
67  
68  	ep = &fmtalloc.fmt[fmtalloc.nfmt];
69  	for(p=fmtalloc.fmt; p<ep; p++)
70  		if(p->c == c)
71  			return p->fmt;
72  
73  	/* is this a predefined format char? */
74  	for(p=knownfmt; p->c; p++)
75  		if(p->c == c){
76  			/* no need to lock; fmtinstall is idempotent */
77  			fmtinstall(p->c, p->fmt);
78  			while(p->fmt == nil)	/* loop until value is updated */
79  				;
80  			return p->fmt;
81  		}
82  
83  	return _badfmt;
84  }
85  
86  int
87  fmtinstall(int c, Fmts f)
88  {
89  	Convfmt *p, *ep;
90  
91  	if(c<=0 || c>=65536)
92  		return -1;
93  	if(!f)
94  		f = _badfmt;
95  
96  	_fmtlock();
97  
98  	ep = &fmtalloc.fmt[fmtalloc.nfmt];
99  	for(p=fmtalloc.fmt; p<ep; p++)
100  		if(p->c == c)
101  			break;
102  
103  	if(p == &fmtalloc.fmt[Maxfmt]){
104  		_fmtunlock();
105  		return -1;
106  	}
107  
108  	p->fmt = f;
109  	if(p == ep){	/* installing a new format character */
110  		fmtalloc.nfmt++;
111  		p->c = c;
112  	}
113  
114  	_fmtunlock();
115  	return 0;
116  }
117  
118  void*
119  _fmtdispatch(Fmt *f, void *fmt, int isrunes)
120  {
121  	Rune rune, r;
122  	int i, n;
123  
124  	f->flags = 0;
125  	f->width = f->prec = 0;
126  
127  	for(;;){
128  		if(isrunes){
129  			r = *(Rune*)fmt;
130  			fmt = (Rune*)fmt + 1;
131  		}else{
132  			fmt = (char*)fmt + chartorune(&rune, fmt);
133  			r = rune;
134  		}
135  		f->r = r;
136  		switch(r){
137  		case '\0':
138  			return nil;
139  		case '.':
140  			f->flags |= FmtWidth|FmtPrec;
141  			continue;
142  		case '0':
143  			if(!(f->flags & FmtWidth)){
144  				f->flags |= FmtZero;
145  				continue;
146  			}
147  			/* fall through */
148  		case '1': case '2': case '3': case '4':
149  		case '5': case '6': case '7': case '8': case '9':
150  			i = 0;
151  			while(r >= '0' && r <= '9'){
152  				i = i * 10 + r - '0';
153  				if(isrunes){
154  					r = *(Rune*)fmt;
155  					fmt = (Rune*)fmt + 1;
156  				}else{
157  					r = *(char*)fmt;
158  					fmt = (char*)fmt + 1;
159  				}
160  			}
161  			if(isrunes)
162  				fmt = (Rune*)fmt - 1;
163  			else
164  				fmt = (char*)fmt - 1;
165  		numflag:
166  			if(f->flags & FmtWidth){
167  				f->flags |= FmtPrec;
168  				f->prec = i;
169  			}else{
170  				f->flags |= FmtWidth;
171  				f->width = i;
172  			}
173  			continue;
174  		case '*':
175  			i = va_arg(f->args, int);
176  			if(i < 0){
177  				i = -i;
178  				f->flags |= FmtLeft;
179  			}
180  			goto numflag;
181  		}
182  		n = (*fmtfmt(r))(f);
183  		if(n < 0)
184  			return nil;
185  		if(n == 0)
186  			return fmt;
187  	}
188  }
189