reg: \n(.H and \n(.V
[neatroff.git] / reg.c
blobc120f2b858aa1f37aa984d12d0708c4f8fe33145
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", ".hy", ".hyp", ".i0", ".l0",
41 ".L0", ".m0", ".n0", ".s0", ".ss", ".ssh", ".sss",
42 ".ti", ".lt", ".lt0", ".v0",
45 /* return the address of a number register */
46 int *nreg(int id)
48 if (eregs_idx[id])
49 return &env->eregs[eregs_idx[id]];
50 return &nregs[id];
53 static int num_fmt(char *s, int n, int fmt);
55 /* the contents of a number register (returns a static buffer) */
56 char *num_str(int id)
58 static char numbuf[128];
59 char *s = map_name(id);
60 if (!nregs_fmt[id])
61 nregs_fmt[id] = '0';
62 numbuf[0] = '\0';
63 if (s[0] == '.' && !s[2]) {
64 switch (s[1]) {
65 case 'b':
66 sprintf(numbuf, "%d", font_getbd(dev_font(n_f)));
67 return numbuf;
68 case 'c':
69 sprintf(numbuf, "%d", in_lnum());
70 return numbuf;
71 case 'k':
72 sprintf(numbuf, "%d", f_hpos());
73 return numbuf;
74 case 'm':
75 sprintf(numbuf, "#%02x%02x%02x",
76 CLR_R(n_m), CLR_G(n_m), CLR_B(n_m));
77 return numbuf;
78 case 't':
79 sprintf(numbuf, "%d", f_nexttrap());
80 return numbuf;
81 case 'z':
82 if (f_divreg() >= 0)
83 sprintf(numbuf, "%s", map_name(f_divreg()));
84 return numbuf;
85 case 'F':
86 sprintf(numbuf, "%s", in_filename());
87 return numbuf;
88 case '$':
89 sprintf(numbuf, "%d", in_nargs());
90 return numbuf;
93 if (s[0] == '.' && !strcmp(".neat", s))
94 return "1";
95 if (s[0] == '.' && s[1] == 'e' && s[2] == 'v' && !s[3])
96 return map_name(env_id);
97 if (s[0] == '$' && s[1] == '$' && !s[2]) {
98 sprintf(numbuf, "%d", getpid());
99 return numbuf;
101 if (s[0] == 'y' && s[1] == 'r' && !s[2]) {
102 sprintf(numbuf, "%02d", *nreg(id));
103 return numbuf;
105 if (!nregs_fmt[id] || num_fmt(numbuf, *nreg(id), nregs_fmt[id]))
106 sprintf(numbuf, "%d", *nreg(id));
107 return numbuf;
110 void num_set(int id, int val)
112 if (!nregs_fmt[id])
113 nregs_fmt[id] = '0';
114 *nreg(id) = val;
117 void num_setinc(int id, int val)
119 nregs_inc[id] = val;
122 void num_inc(int id, int pos)
124 *nreg(id) += pos > 0 ? nregs_inc[id] : -nregs_inc[id];
127 void num_del(int id)
129 *nreg(id) = 0;
130 nregs_inc[id] = 0;
131 nregs_fmt[id] = 0;
134 void str_set(int id, char *s)
136 int len = strlen(s) + 1;
137 if (sregs[id])
138 free(sregs[id]);
139 sregs[id] = xmalloc(len);
140 memcpy(sregs[id], s, len);
141 sregs_dat[id] = NULL;
144 char *str_get(int id)
146 return sregs[id];
149 void *str_dget(int id)
151 return sregs_dat[id];
154 void str_dset(int id, void *d)
156 sregs_dat[id] = d;
159 void str_rm(int id)
161 if (sregs[id])
162 free(sregs[id]);
163 sregs[id] = NULL;
164 sregs_dat[id] = NULL;
167 void str_rn(int src, int dst)
169 str_rm(dst);
170 sregs[dst] = sregs[src];
171 sregs_dat[dst] = sregs_dat[src];
172 sregs[src] = NULL;
173 sregs_dat[src] = NULL;
176 static struct env *env_alloc(void)
178 struct env *env = xmalloc(sizeof(*env));
179 memset(env, 0, sizeof(*env));
180 wb_init(&env->wb);
181 env->fmt = fmt_alloc();
182 return env;
185 static void env_free(struct env *env)
187 fmt_free(env->fmt);
188 wb_done(&env->wb);
189 free(env);
192 static void env_set(int id)
194 int i;
195 env = envs[id];
196 env_id = id;
197 if (!env) {
198 envs[id] = env_alloc();
199 env = envs[id];
200 n_f = 1;
201 n_i = 0;
202 n_j = AD_B;
203 n_l = SC_IN * 65 / 10;
204 n_L = 1;
205 n_s = 10;
206 n_u = 1;
207 n_v = 12 * SC_PT;
208 n_s0 = n_s;
209 n_f0 = n_f;
210 n_na = 0;
211 n_lt = SC_IN * 65 / 10;
212 n_hy = 1;
213 n_ss = 12;
214 n_sss = 12;
215 n_nM = 1;
216 n_nS = 1;
217 strcpy(env->hc, "\\%");
218 strcpy(env->lc, ".");
219 for (i = 0; i < NTABS; i++)
220 env->tabs[i] = i * SC_IN / 2;
224 static void init_time(void)
226 time_t t = time(NULL);
227 struct tm *tm = localtime(&t);
228 num_set(map("dw"), tm->tm_wday + 1);
229 num_set(map("dy"), tm->tm_mday);
230 num_set(map("mo"), tm->tm_mon + 1);
231 num_set(map("yr"), tm->tm_year % 100);
234 static void init_globals(void)
236 n_o = SC_IN;
237 n_p = SC_IN * 11;
238 n_lg = 1;
239 n_kn = 0;
240 num_set(map(".H"), 1);
241 num_set(map(".V"), 1);
244 void env_init(void)
246 int i;
247 init_time();
248 init_globals();
249 for (i = 0; i < LEN(eregs); i++)
250 eregs_idx[map(eregs[i])] = i + 1;
251 env_set(map("0"));
254 void env_done(void)
256 int i;
257 for (i = 0; i < LEN(envs); i++)
258 if (envs[i])
259 env_free(envs[i]);
262 static int oenv[NPREV]; /* environment stack */
263 static int nenv;
265 void tr_ev(char **args)
267 int id = -1;
268 if (args[1])
269 id = map(args[1]);
270 else
271 id = nenv ? oenv[--nenv] : -1;
272 if (id < 0)
273 return;
274 if (args[1] && env && nenv < NPREV)
275 oenv[nenv++] = env_id;
276 env_set(id);
279 struct fmt *env_fmt(void)
281 return env->fmt;
284 struct wb *env_wb(void)
286 return &env->wb;
289 char *env_hc(void)
291 return env->hc;
294 char *env_mc(void)
296 return env->mc;
299 char *env_tc(void)
301 return env->tc;
304 char *env_lc(void)
306 return env->lc;
309 /* saving and restoring registers around diverted lines */
310 struct odiv {
311 int f, s, m, f0, s0, m0;
314 static struct odiv odivs[NPREV]; /* state before diverted text */
315 static int nodivs;
317 /* begin outputting diverted line */
318 void odiv_beg(void)
320 struct odiv *o = &odivs[nodivs++];
321 o->f = n_f;
322 o->s = n_s;
323 o->m = n_m;
324 o->f0 = n_f0;
325 o->s0 = n_s0;
326 o->m0 = n_m0;
329 /* end outputting diverted line */
330 void odiv_end(void)
332 struct odiv *o = &odivs[--nodivs];
333 n_f = o->f;
334 n_s = o->s;
335 n_m = o->m;
336 n_f0 = o->f0;
337 n_s0 = o->s0;
338 n_m0 = o->m0;
341 void tr_ta(char **args)
343 int i;
344 char *s;
345 for (i = 0; i < NARGS && args[i]; i++) {
346 env->tabs[i] = eval_re(args[i], i > 0 ? env->tabs[i - 1] : 0, 'm');
347 s = args[i][0] ? strchr(args[i], '\0') - 1 : "";
348 env->tabs_type[i] = strchr("LRC", *s) ? *s : 'L';
352 static int tab_idx(int pos)
354 int i;
355 for (i = 0; i < LEN(env->tabs); i++)
356 if (env->tabs[i] > pos)
357 return i;
358 return -1;
361 int tab_next(int pos)
363 int i = tab_idx(pos);
364 return i >= 0 ? env->tabs[i] : pos;
367 int tab_type(int pos)
369 int i = tab_idx(pos);
370 return i >= 0 && env->tabs_type[i] ? env->tabs_type[i] : 'L';
373 /* number register format (.af) */
374 #define NF_LSH 8 /* number format length shifts */
375 #define NF_FMT 0x00ff /* number format mask */
377 /* the format of a number register (returns a static buffer) */
378 char *num_getfmt(int id)
380 static char fmtbuf[128];
381 char *s = fmtbuf;
382 int i;
383 if ((nregs_fmt[id] & NF_FMT) == '0') {
384 *s++ = '0';
385 i = nregs_fmt[id] >> NF_LSH;
386 while (i-- > 1)
387 *s++ = '0';
388 } else if (nregs_fmt[id]) {
389 *s++ = nregs_fmt[id] & NF_FMT;
391 *s = '\0';
392 return fmtbuf;
395 void num_setfmt(int id, char *s)
397 int i = 0;
398 if (strchr("iIaA", s[0])) {
399 nregs_fmt[id] = s[0];
400 } else {
401 while (isdigit(s[i]))
402 i++;
403 nregs_fmt[id] = '0' | (i << NF_LSH);
407 static void nf_reverse(char *s)
409 char r[128];
410 int i, l;
411 strcpy(r, s);
412 l = strlen(r);
413 for (i = 0; i < l; i++)
414 s[i] = r[l - i - 1];
417 static void nf_roman(char *s, int n, char *I, char *V)
419 int i;
420 if (!n)
421 return;
422 if (n % 5 == 4) {
423 *s++ = n % 10 == 9 ? I[1] : V[0];
424 *s++ = I[0];
425 } else {
426 for (i = 0; i < n % 5; i++)
427 *s++ = I[0];
428 if (n % 10 >= 5)
429 *s++ = V[0];
431 *s = '\0';
432 nf_roman(s, n / 10, I + 1, V + 1);
435 static void nf_alpha(char *s, int n, int a)
437 while (n) {
438 *s++ = a + ((n - 1) % 26);
439 n /= 26;
441 *s = '\0';
444 /* returns nonzero on failure */
445 static int num_fmt(char *s, int n, int fmt)
447 int type = fmt & NF_FMT;
448 int len;
449 if (n < 0) {
450 n = -n;
451 *s++ = '-';
453 if ((type == 'i' || type == 'I') && n > 0 && n < 40000) {
454 if (type == 'i')
455 nf_roman(s, n, "ixcmz", "vldw");
456 else
457 nf_roman(s, n, "IXCMZ", "VLDW");
458 nf_reverse(s);
459 return 0;
461 if ((type == 'a' || type == 'A') && n > 0) {
462 nf_alpha(s, n, type);
463 nf_reverse(s);
464 return 0;
466 if (type == '0') {
467 sprintf(s, "%d", n);
468 len = strlen(s);
469 while (len++ < fmt >> NF_LSH)
470 *s++ = '0';
471 sprintf(s, "%d", n);
472 return 0;
474 return 1;