roff: call xmalloc() to report when neatroff is out of memory
[neatroff.git] / reg.c
blob7df8712d2914692926629fe30f03cec540023880
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 "roff.h"
10 #define NENVS 64 /* number of environment registers */
12 struct env {
13 int eregs[NENVS]; /* environment-specific number registers */
14 int tabs[NTABS]; /* tab stops */
15 char tabs_type[NTABS]; /* type of tabs: L, C, R */
16 struct fmt *fmt; /* per environment line formatting buffer */
17 struct wb wb; /* per environment partial word */
18 char tc[GNLEN]; /* tab character (.tc) */
19 char lc[GNLEN]; /* leader character (.lc) */
20 char hc[GNLEN]; /* hyphenation character (.hc) */
21 char mc[GNLEN]; /* margin character (.mc) */
24 static int nregs[NREGS]; /* global number registers */
25 static int nregs_inc[NREGS]; /* number register auto-increment size */
26 static int nregs_fmt[NREGS]; /* number register format */
27 static char *sregs[NREGS]; /* global string registers */
28 static void *sregs_dat[NREGS]; /* builtin function data */
29 static struct env *envs[NREGS];/* environments */
30 static struct env *env; /* current enviroment */
31 static int env_id; /* current environment id */
32 static int eregs_idx[NREGS]; /* register environment index in eregs[] */
34 static char *eregs[] = { /* environment-specific number registers */
35 "ln", ".f", ".i", ".j", ".l",
36 ".L", ".nI", ".nm", ".nM", ".nn",
37 ".nS", ".m", ".s", ".u", ".v",
38 ".it", ".itn", ".mc", ".mcn",
39 ".ce", ".f0", ".hy", ".hyp", ".i0", ".l0",
40 ".L0", ".m0", ".n0", ".s0", ".ss", ".ssh", ".sss",
41 ".ti", ".lt", ".lt0", ".v0",
44 /* return the address of a number register */
45 int *nreg(int id)
47 if (eregs_idx[id])
48 return &env->eregs[eregs_idx[id]];
49 return &nregs[id];
52 static int num_fmt(char *s, int n, int fmt);
54 /* the contents of a number register (returns a static buffer) */
55 char *num_str(int id)
57 static char numbuf[128];
58 char *s = map_name(id);
59 numbuf[0] = '\0';
60 if (s[0] == '.' && !s[2]) {
61 switch (s[1]) {
62 case 'b':
63 sprintf(numbuf, "%d", dev_getbd(n_f));
64 return numbuf;
65 case 'c':
66 sprintf(numbuf, "%d", in_lnum());
67 return numbuf;
68 case 'k':
69 sprintf(numbuf, "%d", f_hpos());
70 return numbuf;
71 case 'm':
72 sprintf(numbuf, "#%02x%02x%02x",
73 CLR_R(n_m), CLR_G(n_m), CLR_B(n_m));
74 return numbuf;
75 case 't':
76 sprintf(numbuf, "%d", f_nexttrap());
77 return numbuf;
78 case 'z':
79 if (f_divreg() >= 0)
80 sprintf(numbuf, "%s", map_name(f_divreg()));
81 return numbuf;
82 case 'F':
83 sprintf(numbuf, "%s", in_filename());
84 return numbuf;
85 case '$':
86 sprintf(numbuf, "%d", in_nargs());
87 return numbuf;
90 if (s[0] == '.' && !strcmp(".neat", s))
91 return "1";
92 if (id == map("yr")) {
93 sprintf(numbuf, "%02d", nregs[id]);
94 return numbuf;
96 if (!nregs_fmt[id] || num_fmt(numbuf, *nreg(id), nregs_fmt[id]))
97 sprintf(numbuf, "%d", *nreg(id));
98 return numbuf;
101 void num_set(int id, int val)
103 *nreg(id) = val;
106 void num_inc(int id, int val)
108 nregs_inc[id] = val;
111 void num_del(int id)
113 *nreg(id) = 0;
114 nregs_inc[id] = 0;
115 nregs_fmt[id] = 0;
118 int num_get(int id, int inc)
120 if (inc)
121 *nreg(id) += inc > 0 ? nregs_inc[id] : -nregs_inc[id];
122 return *nreg(id);
125 void str_set(int id, char *s)
127 int len = strlen(s) + 1;
128 if (sregs[id])
129 free(sregs[id]);
130 sregs[id] = xmalloc(len);
131 memcpy(sregs[id], s, len);
132 sregs_dat[id] = NULL;
135 char *str_get(int id)
137 return sregs[id];
140 void *str_dget(int id)
142 return sregs_dat[id];
145 void str_dset(int id, void *d)
147 sregs_dat[id] = d;
150 void str_rm(int id)
152 if (sregs[id])
153 free(sregs[id]);
154 sregs[id] = NULL;
155 sregs_dat[id] = NULL;
158 void str_rn(int src, int dst)
160 str_rm(dst);
161 sregs[dst] = sregs[src];
162 sregs_dat[dst] = sregs_dat[src];
163 sregs[src] = NULL;
164 sregs_dat[src] = NULL;
167 static struct env *env_alloc(void)
169 struct env *env = xmalloc(sizeof(*env));
170 memset(env, 0, sizeof(*env));
171 wb_init(&env->wb);
172 env->fmt = fmt_alloc();
173 return env;
176 static void env_free(struct env *env)
178 fmt_free(env->fmt);
179 wb_done(&env->wb);
180 free(env);
183 static void env_set(int id)
185 int i;
186 env = envs[id];
187 env_id = id;
188 if (!env) {
189 envs[id] = env_alloc();
190 env = envs[id];
191 n_f = 1;
192 n_i = 0;
193 n_j = AD_B;
194 n_l = SC_IN * 65 / 10;
195 n_L = 1;
196 n_s = 10;
197 n_u = 1;
198 n_v = 12 * SC_PT;
199 n_s0 = n_s;
200 n_f0 = n_f;
201 n_na = 0;
202 n_lt = SC_IN * 65 / 10;
203 n_hy = 1;
204 n_ss = 12;
205 n_sss = 12;
206 n_nM = 1;
207 n_nS = 1;
208 strcpy(env->hc, "\\%");
209 strcpy(env->lc, ".");
210 for (i = 0; i < NTABS; i++)
211 env->tabs[i] = i * SC_IN / 2;
215 static void init_time(void)
217 time_t t = time(NULL);
218 struct tm *tm = localtime(&t);
219 nregs[map("dw")] = tm->tm_wday + 1;
220 nregs[map("dy")] = tm->tm_mday;
221 nregs[map("mo")] = tm->tm_mon + 1;
222 nregs[map("yr")] = tm->tm_year % 100;
225 static void init_globals(void)
227 n_o = SC_IN;
228 n_p = SC_IN * 11;
229 n_lg = 1;
230 n_kn = 0;
233 void env_init(void)
235 int i;
236 init_time();
237 init_globals();
238 for (i = 0; i < LEN(eregs); i++)
239 eregs_idx[map(eregs[i])] = i + 1;
240 env_set(0);
243 void env_done(void)
245 int i;
246 for (i = 0; i < LEN(envs); i++)
247 if (envs[i])
248 env_free(envs[i]);
251 static int oenv[NPREV]; /* environment stack */
252 static int nenv;
254 void tr_ev(char **args)
256 int id = -1;
257 if (args[1])
258 id = map(args[1]);
259 else
260 id = nenv ? oenv[--nenv] : -1;
261 if (id < 0)
262 return;
263 if (args[1] && env && nenv < NPREV)
264 oenv[nenv++] = env_id;
265 env_set(id);
268 struct fmt *env_fmt(void)
270 return env->fmt;
273 struct wb *env_wb(void)
275 return &env->wb;
278 char *env_hc(void)
280 return env->hc;
283 char *env_mc(void)
285 return env->mc;
288 char *env_tc(void)
290 return env->tc;
293 char *env_lc(void)
295 return env->lc;
298 /* saving and restoring registers around diverted lines */
299 struct odiv {
300 int f, s, m, f0, s0, m0;
303 static struct odiv odivs[NPREV]; /* state before diverted text */
304 static int nodivs;
306 /* begin outputting diverted line */
307 void odiv_beg(void)
309 struct odiv *o = &odivs[nodivs++];
310 o->f = n_f;
311 o->s = n_s;
312 o->m = n_m;
313 o->f0 = n_f0;
314 o->s0 = n_s0;
315 o->m0 = n_m0;
318 /* end outputting diverted line */
319 void odiv_end(void)
321 struct odiv *o = &odivs[--nodivs];
322 n_f = o->f;
323 n_s = o->s;
324 n_m = o->m;
325 n_f0 = o->f0;
326 n_s0 = o->s0;
327 n_m0 = o->m0;
330 void tr_ta(char **args)
332 int i;
333 char *s;
334 for (i = 0; i < NARGS && args[i]; i++) {
335 env->tabs[i] = eval_re(args[i], i > 0 ? env->tabs[i - 1] : 0, 'm');
336 s = args[i][0] ? strchr(args[i], '\0') - 1 : "";
337 env->tabs_type[i] = strchr("LRC", *s) ? *s : 'L';
341 static int tab_idx(int pos)
343 int i;
344 for (i = 0; i < LEN(env->tabs); i++)
345 if (env->tabs[i] > pos)
346 return i;
347 return -1;
350 int tab_next(int pos)
352 int i = tab_idx(pos);
353 return i >= 0 ? env->tabs[i] : pos;
356 int tab_type(int pos)
358 int i = tab_idx(pos);
359 return i >= 0 && env->tabs_type[i] ? env->tabs_type[i] : 'L';
362 /* number register format (.af) */
363 #define NF_LSH 8 /* number format length shifts */
364 #define NF_FMT 0x00ff /* number format mask */
366 /* the format of a number register (returns a static buffer) */
367 char *num_getfmt(int id)
369 static char fmtbuf[128];
370 char *s = fmtbuf;
371 int i;
372 if (!nregs_fmt[id] || (nregs_fmt[id] & NF_FMT) == '0') {
373 *s++ = '0';
374 i = nregs_fmt[id] >> NF_LSH;
375 while (i-- > 1)
376 *s++ = '0';
377 } else {
378 *s++ = nregs_fmt[id] & NF_FMT;
380 *s = '\0';
381 return fmtbuf;
384 void num_setfmt(int id, char *s)
386 int i = 0;
387 if (strchr("iIaA", s[0])) {
388 nregs_fmt[id] = s[0];
389 } else {
390 while (isdigit(s[i]))
391 i++;
392 nregs_fmt[id] = '0' | (i << NF_LSH);
396 static void nf_reverse(char *s)
398 char r[128];
399 int i, l;
400 strcpy(r, s);
401 l = strlen(r);
402 for (i = 0; i < l; i++)
403 s[i] = r[l - i - 1];
406 static void nf_roman(char *s, int n, char *I, char *V)
408 int i;
409 if (!n)
410 return;
411 if (n % 5 == 4) {
412 *s++ = n % 10 == 9 ? I[1] : V[0];
413 *s++ = I[0];
414 } else {
415 for (i = 0; i < n % 5; i++)
416 *s++ = I[0];
417 if (n % 10 >= 5)
418 *s++ = V[0];
420 *s = '\0';
421 nf_roman(s, n / 10, I + 1, V + 1);
424 static void nf_alpha(char *s, int n, int a)
426 while (n) {
427 *s++ = a + ((n - 1) % 26);
428 n /= 26;
430 *s = '\0';
433 /* returns nonzero on failure */
434 static int num_fmt(char *s, int n, int fmt)
436 int type = fmt & NF_FMT;
437 int len;
438 if (n < 0) {
439 n = -n;
440 *s++ = '-';
442 if ((type == 'i' || type == 'I') && n > 0 && n < 40000) {
443 if (type == 'i')
444 nf_roman(s, n, "ixcmz", "vldw");
445 else
446 nf_roman(s, n, "IXCMZ", "VLDW");
447 nf_reverse(s);
448 return 0;
450 if ((type == 'a' || type == 'A') && n > 0) {
451 nf_alpha(s, n, type);
452 nf_reverse(s);
453 return 0;
455 if (type == '0') {
456 sprintf(s, "%d", n);
457 len = strlen(s);
458 while (len++ < fmt >> NF_LSH)
459 *s++ = '0';
460 sprintf(s, "%d", n);
461 return 0;
463 return 1;