reg: use snprintf for string values in num_str()
[neatroff.git] / reg.c
blob0df8505ac7a7c99da0a6f4a14ea822d97c50f738
1 /* registers and environments */
2 #include <ctype.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <stdlib.h>
6 #include <string.h>
7 #include <time.h>
8 #include <unistd.h>
9 #include "roff.h"
11 #define NENVS 64 /* number of environment registers */
13 struct env {
14 int eregs[NENVS]; /* environment-specific number registers */
15 int tabs[NTABS]; /* tab stops */
16 char tabs_type[NTABS]; /* type of tabs: L, C, R */
17 struct fmt *fmt; /* per environment line formatting buffer */
18 struct wb wb; /* per environment partial word */
19 char tc[GNLEN]; /* tab character (.tc) */
20 char lc[GNLEN]; /* leader character (.lc) */
21 char hc[GNLEN]; /* hyphenation character (.hc) */
22 char mc[GNLEN]; /* margin character (.mc) */
25 static int nregs[NREGS]; /* global number registers */
26 static int nregs_inc[NREGS]; /* number register auto-increment size */
27 static int nregs_fmt[NREGS]; /* number register format */
28 static char *sregs[NREGS]; /* global string registers */
29 static void *sregs_dat[NREGS]; /* builtin function data */
30 static struct env *envs[NREGS];/* environments */
31 static struct env *env; /* current enviroment */
32 static int env_id; /* current environment id */
33 static int eregs_idx[NREGS]; /* register environment index in eregs[] */
35 static char *eregs[] = { /* environment-specific number registers */
36 "ln", ".f", ".i", ".j", ".l",
37 ".L", ".nI", ".nm", ".nM", ".nn",
38 ".nS", ".m", ".s", ".u", ".v",
39 ".it", ".itn", ".mc", ".mcn",
40 ".ce", ".f0", ".i0", ".l0",
41 ".hy", ".hycost", ".hycost2", ".hycost3", ".hlm",
42 ".L0", ".m0", ".n0", ".s0", ".ss", ".ssh", ".sss", ".pmll", ".pmllcost",
43 ".ti", ".lt", ".lt0", ".v0",
44 ".I", ".I0", ".tI", ".td", ".cd",
47 /* return the address of a number register */
48 int *nreg(int id)
50 if (eregs_idx[id])
51 return &env->eregs[eregs_idx[id]];
52 return &nregs[id];
55 static char *directory(char *path)
57 static char dst[PATHLEN];
58 char *s = strrchr(path, '/');
59 if (!s)
60 return ".";
61 if (path == s)
62 return "/";
63 memcpy(dst, path, s - path);
64 dst[s - path] = '\0';
65 return dst;
68 static char *num_tabs(void)
70 static char tabs[16 * NTABS];
71 int i;
72 char *s = tabs;
73 for (i = 0; i < NTABS && env->tabs_type[i]; i++)
74 s += sprintf(s, "%du%c ", env->tabs[i], env->tabs_type[i]);
75 return tabs;
78 static int num_fmt(char *s, int n, int fmt);
80 /* the contents of a number register (returns a static buffer) */
81 char *num_str(int id)
83 static char numbuf[128];
84 char *s = map_name(id);
85 if (!nregs_fmt[id])
86 nregs_fmt[id] = '0';
87 numbuf[0] = '\0';
88 if (s[0] == '.' && !s[2]) {
89 switch (s[1]) {
90 case 'b':
91 sprintf(numbuf, "%d", font_getbd(dev_font(n_f)));
92 return numbuf;
93 case 'c':
94 sprintf(numbuf, "%d", in_lnum());
95 return numbuf;
96 case 'k':
97 sprintf(numbuf, "%d", f_hpos());
98 return numbuf;
99 case 'm':
100 sprintf(numbuf, "#%02x%02x%02x",
101 CLR_R(n_m), CLR_G(n_m), CLR_B(n_m));
102 return numbuf;
103 case 't':
104 sprintf(numbuf, "%d", f_nexttrap());
105 return numbuf;
106 case 'z':
107 if (f_divreg() >= 0)
108 snprintf(numbuf, sizeof(numbuf), "%s", map_name(f_divreg()));
109 return numbuf;
110 case 'F':
111 snprintf(numbuf, sizeof(numbuf), "%s", in_filename());
112 return numbuf;
113 case 'D':
114 snprintf(numbuf, sizeof(numbuf), "%s", directory(in_filename()));
115 return numbuf;
116 case '$':
117 sprintf(numbuf, "%d", in_nargs() - 1);
118 return numbuf;
121 if (s[0] == '.' && !strcmp(".neat", s))
122 return "1";
123 if (s[0] == '.' && s[1] == 'e' && s[2] == 'v' && !s[3])
124 return map_name(env_id);
125 if (s[0] == '$' && s[1] == '$' && !s[2]) {
126 sprintf(numbuf, "%d", getpid());
127 return numbuf;
129 if (s[0] == 'y' && s[1] == 'r' && !s[2]) {
130 sprintf(numbuf, "%02d", *nreg(id));
131 return numbuf;
133 if (s[0] == '.' && !strcmp(".tabs", s))
134 return num_tabs();
135 if (!nregs_fmt[id] || num_fmt(numbuf, *nreg(id), nregs_fmt[id]))
136 sprintf(numbuf, "%d", *nreg(id));
137 return numbuf;
140 void num_set(int id, int val)
142 if (!nregs_fmt[id])
143 nregs_fmt[id] = '0';
144 *nreg(id) = val;
147 void num_setinc(int id, int val)
149 nregs_inc[id] = val;
152 void num_inc(int id, int pos)
154 *nreg(id) += pos > 0 ? nregs_inc[id] : -nregs_inc[id];
157 void num_del(int id)
159 *nreg(id) = 0;
160 nregs_inc[id] = 0;
161 nregs_fmt[id] = 0;
164 void str_set(int id, char *s)
166 int len = strlen(s) + 1;
167 if (sregs[id])
168 free(sregs[id]);
169 sregs[id] = xmalloc(len);
170 memcpy(sregs[id], s, len);
171 sregs_dat[id] = NULL;
174 char *str_get(int id)
176 return sregs[id];
179 void *str_dget(int id)
181 return sregs_dat[id];
184 void str_dset(int id, void *d)
186 sregs_dat[id] = d;
189 void str_rm(int id)
191 if (sregs[id])
192 free(sregs[id]);
193 sregs[id] = NULL;
194 sregs_dat[id] = NULL;
197 void str_rn(int src, int dst)
199 if (!sregs[src] && !sregs_dat[src])
200 return;
201 str_rm(dst);
202 sregs[dst] = sregs[src];
203 sregs_dat[dst] = sregs_dat[src];
204 sregs[src] = NULL;
205 sregs_dat[src] = NULL;
208 static struct env *env_alloc(void)
210 struct env *env = xmalloc(sizeof(*env));
211 memset(env, 0, sizeof(*env));
212 wb_init(&env->wb);
213 env->fmt = fmt_alloc();
214 return env;
217 static void env_free(struct env *env)
219 fmt_free(env->fmt);
220 wb_done(&env->wb);
221 free(env);
224 static void env_set(int id)
226 int i;
227 env = envs[id];
228 env_id = id;
229 if (!env) {
230 envs[id] = env_alloc();
231 env = envs[id];
232 n_f = 1;
233 n_i = 0;
234 n_I = 0;
235 n_j = AD_B;
236 n_l = SC_IN * 65 / 10;
237 n_L = 1;
238 n_s = 10;
239 n_u = 1;
240 n_v = 12 * SC_PT;
241 n_s0 = n_s;
242 n_f0 = n_f;
243 n_na = 0;
244 n_lt = SC_IN * 65 / 10;
245 n_hy = 1;
246 n_ss = 12;
247 n_sss = 12;
248 n_nM = 1;
249 n_nS = 1;
250 strcpy(env->hc, "\\%");
251 strcpy(env->lc, ".");
252 for (i = 0; i < NTABS; i++) {
253 env->tabs[i] = i * SC_IN / 2;
254 env->tabs_type[i] = 'L';
259 static void init_time(void)
261 time_t t = time(NULL);
262 struct tm *tm = localtime(&t);
263 num_set(map("dw"), tm->tm_wday + 1);
264 num_set(map("dy"), tm->tm_mday);
265 num_set(map("mo"), tm->tm_mon + 1);
266 num_set(map("yr"), tm->tm_year % 100);
267 num_set(map(".yr"), 1900 + tm->tm_year);
270 static void init_globals(void)
272 n_o = SC_IN;
273 n_p = SC_IN * 11;
274 n_lg = 1;
275 n_kn = 1;
276 num_set(map(".H"), 1);
277 num_set(map(".V"), 1);
280 void env_init(void)
282 int i;
283 init_time();
284 init_globals();
285 for (i = 0; i < LEN(eregs); i++)
286 eregs_idx[map(eregs[i])] = i + 1;
287 env_set(map("0"));
290 void env_done(void)
292 int i;
293 for (i = 0; i < LEN(envs); i++)
294 if (envs[i])
295 env_free(envs[i]);
296 for (i = 0; i < LEN(sregs); i++)
297 free(sregs[i]);
300 static int oenv[NPREV]; /* environment stack */
301 static int nenv;
303 void tr_ev(char **args)
305 int id = -1;
306 if (args[1])
307 id = map(args[1]);
308 else
309 id = nenv ? oenv[--nenv] : -1;
310 if (id < 0)
311 return;
312 if (args[1] && env && nenv < NPREV)
313 oenv[nenv++] = env_id;
314 env_set(id);
317 struct fmt *env_fmt(void)
319 return env->fmt;
322 struct wb *env_wb(void)
324 return &env->wb;
327 char *env_hc(void)
329 return env->hc;
332 char *env_mc(void)
334 return env->mc;
337 char *env_tc(void)
339 return env->tc;
342 char *env_lc(void)
344 return env->lc;
347 /* saving and restoring registers around diverted lines */
348 struct odiv {
349 int f, s, m, f0, s0, m0, cd;
352 static struct odiv odivs[NPREV]; /* state before diverted text */
353 static int nodivs;
355 /* begin outputting diverted line */
356 void odiv_beg(void)
358 struct odiv *o = &odivs[nodivs++];
359 o->f = n_f;
360 o->s = n_s;
361 o->m = n_m;
362 o->f0 = n_f0;
363 o->s0 = n_s0;
364 o->m0 = n_m0;
365 o->cd = n_cd;
368 /* end outputting diverted line */
369 void odiv_end(void)
371 struct odiv *o = &odivs[--nodivs];
372 n_f = o->f;
373 n_s = o->s;
374 n_m = o->m;
375 n_f0 = o->f0;
376 n_s0 = o->s0;
377 n_m0 = o->m0;
378 n_cd = o->cd;
381 void tr_ta(char **args)
383 int i;
384 int c;
385 for (i = 0; i < NTABS; i++) {
386 if (i + 1 < NARGS && args[i + 1]) {
387 char *a = args[i + 1];
388 env->tabs[i] = eval_re(a, i > 0 ? env->tabs[i - 1] : 0, 'm');
389 c = a[0] ? (unsigned char) strchr(a, '\0')[-1] : 0;
390 env->tabs_type[i] = strchr("LRC", c) ? c : 'L';
391 } else {
392 env->tabs[i] = 0;
393 env->tabs_type[i] = 0;
398 static int tab_idx(int pos)
400 int i;
401 for (i = 0; i < LEN(env->tabs); i++)
402 if (env->tabs[i] > pos)
403 return i;
404 return -1;
407 int tab_next(int pos)
409 int i = tab_idx(pos);
410 return i >= 0 ? env->tabs[i] : pos;
413 int tab_type(int pos)
415 int i = tab_idx(pos);
416 return i >= 0 && env->tabs_type[i] ? env->tabs_type[i] : 'L';
419 /* number register format (.af) */
420 #define NF_LSH 8 /* number format length shifts */
421 #define NF_FMT 0x00ff /* number format mask */
423 /* the format of a number register (returns a static buffer) */
424 char *num_getfmt(int id)
426 static char fmtbuf[128];
427 char *s = fmtbuf;
428 int fmt = nregs_fmt[id] & NF_FMT;
429 int i;
430 if (fmt == '0' || fmt == 'x' || fmt == 'X') {
431 i = nregs_fmt[id] >> NF_LSH;
432 while (i-- > 1)
433 *s++ = '0';
434 *s++ = fmt;
435 } else if (nregs_fmt[id]) {
436 *s++ = fmt;
438 *s = '\0';
439 return fmtbuf;
442 void num_setfmt(int id, char *s)
444 int i = 0;
445 if (strchr("iIaA", s[0])) {
446 nregs_fmt[id] = s[0];
447 } else {
448 while (isdigit((unsigned char) s[i]))
449 i++;
450 if (s[i] == 'x' || s[i] == 'X')
451 nregs_fmt[id] = s[i] | ((i + 1) << NF_LSH);
452 else
453 nregs_fmt[id] = '0' | (i << NF_LSH);
457 static void nf_reverse(char *s)
459 char r[128];
460 int i, l;
461 strcpy(r, s);
462 l = strlen(r);
463 for (i = 0; i < l; i++)
464 s[i] = r[l - i - 1];
467 static void nf_roman(char *s, int n, char *I, char *V)
469 int i;
470 if (!n)
471 return;
472 if (n % 5 == 4) {
473 *s++ = n % 10 == 9 ? I[1] : V[0];
474 *s++ = I[0];
475 } else {
476 for (i = 0; i < n % 5; i++)
477 *s++ = I[0];
478 if (n % 10 >= 5)
479 *s++ = V[0];
481 *s = '\0';
482 nf_roman(s, n / 10, I + 1, V + 1);
485 static void nf_alpha(char *s, int n, int a)
487 while (n) {
488 *s++ = a + ((n - 1) % 26);
489 n = (n - 1) / 26;
491 *s = '\0';
494 /* returns nonzero on failure */
495 static int num_fmt(char *s, int n, int fmt)
497 int type = fmt & NF_FMT;
498 if (n < 0) {
499 n = -n;
500 *s++ = '-';
502 if ((type == 'i' || type == 'I') && n > 0 && n < 40000) {
503 if (type == 'i')
504 nf_roman(s, n, "ixcmz", "vldw");
505 else
506 nf_roman(s, n, "IXCMZ", "VLDW");
507 nf_reverse(s);
508 return 0;
510 if ((type == 'a' || type == 'A') && n > 0) {
511 nf_alpha(s, n, type);
512 nf_reverse(s);
513 return 0;
515 if (type == '0' || type == 'x' || type == 'X') {
516 char pat[16];
517 sprintf(pat, "%%0%d%c", fmt >> NF_LSH, type == '0' ? 'd' : type);
518 sprintf(s, pat, n);
519 return 0;
521 return 1;