]> git.draconx.ca Git - cdecl99.git/blob - src/explain.c
Initial support for explaining declarations.
[cdecl99.git] / src / explain.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdarg.h>
4 #include "cdecl.h"
5 #include "typemap.h"
6
7 /* declare [ident] as [storage] [function specs] [other crap] [qualifiers] [type specs] */
8
9 static size_t
10 voutput(char *buf, size_t n, size_t off, const char *fmt, va_list ap)
11 {
12         if (off >= n)
13                 return vsnprintf(NULL, 0, fmt, ap);
14         return vsnprintf(buf+off, n-off, fmt, ap);
15 }
16
17 static size_t output(char *buf, size_t n, size_t off, const char *fmt, ...)
18 {
19         va_list ap;
20         size_t ret;
21
22         va_start(ap, fmt);
23         ret = voutput(buf, n, off, fmt, ap);
24         va_end(ap);
25
26         return ret;
27 }
28
29 /* Renders the type qualifiers and type specifiers in canonical form. */
30 static size_t
31 explain_post_specs(char *buf, size_t n, struct cdecl_declspec *s)
32 {
33         unsigned long qualmap = 0, typemap;
34         const char *tag = NULL;
35         size_t ret = 0;
36
37         typemap = cdecl__build_typemap(s);
38         if (typemap == -1)
39                 return 0;
40
41         for (struct cdecl_declspec *c = s; c; c = c->next) {
42                 switch (cdecl_spec_kind(c)) {
43                 case CDECL_SPEC_QUAL:
44                         qualmap |= 1ul << (c->type & 0xff);
45                         break;
46                 case CDECL_SPEC_TYPE:
47                         /* Valid C types have at most one identifier. */
48                         if (c->ident)
49                                 tag = c->ident;
50                         break;
51                 }
52         }
53
54         if (qualmap & (1ul << (CDECL_QUAL_RESTRICT & 0xff)))
55                 ret += output(buf, n, ret, "restrict ");
56         if (qualmap & (1ul << (CDECL_QUAL_VOLATILE & 0xff)))
57                 ret += output(buf, n, ret, "volatile ");
58         if (qualmap & (1ul << (CDECL_QUAL_CONST & 0xff)))
59                 ret += output(buf, n, ret, "const ");
60
61         ret += output(buf, n, ret, "%s", cdecl__explain_typemap(typemap));
62         if (tag)
63                 ret += output(buf, n, ret, " %s", tag);
64         return ret;
65 }
66
67 static const char *explain_storage(unsigned spec)
68 {
69         switch (spec) {
70         case CDECL_STOR_TYPEDEF:
71                 return "typedef";
72         case CDECL_STOR_EXTERN:
73                 return "extern";
74         case CDECL_STOR_STATIC:
75                 return "static";
76         case CDECL_STOR_AUTO:
77                 return "auto";
78         case CDECL_STOR_REGISTER:
79                 return "register";
80         default:
81                 abort();
82         }
83 }
84
85 /* Renders the storage-class and function specifiers in canonical form. */
86 static size_t explain_pre_specs(char *buf, size_t n, struct cdecl_declspec *s)
87 {
88         unsigned long funcmap = 0;
89         const char *storage = NULL;
90         size_t ret = 0;
91
92         for (struct cdecl_declspec *c = s; c; c = c->next) {
93                 switch (cdecl_spec_kind(c)) {
94                 case CDECL_SPEC_FUNC:
95                         funcmap |= 1ul << (c->type & 0xff);
96                         break;
97                 case CDECL_SPEC_STOR:
98                         /* Valid C declarations have at most one
99                          * storage-class specifier. */
100                         storage = explain_storage(c->type);
101                         break;
102                 }
103         }
104
105         if (storage)
106                 ret += output(buf, n, ret, "%s", storage);
107         if (funcmap & (1ul << (CDECL_FUNC_INLINE & 0xff)))
108                 ret += output(buf, n, ret, "%.*s%s", !!ret, "", "inline");
109         return ret;
110 }
111
112 static size_t advance(char **buf, size_t *n, size_t amount)
113 {
114         if (!amount)
115                 return 0;
116
117         if (amount >= *n) {
118                 *n   = 0;
119                 *buf = 0;
120         } else {
121                 (*buf)[amount] = ' ';
122                 if (amount + 1 >= *n) {
123                         *buf = 0;
124                         *n = 0;
125                 } else {
126                         *buf += amount + 1;
127                         *n   -= amount + 1;
128                 }
129         }
130
131         return amount + 1;
132 }
133
134 size_t cdecl_explain(char *buf, size_t n, struct cdecl *decl)
135 {
136         size_t ret = 0, rc;
137
138         /*
139          * XXX: This won't work when we add support for more complicated
140          * declarators.
141          */
142         rc = output(buf, n, 0, "declare %s as", decl->declarators->ident);
143         ret += advance(&buf, &n, rc);
144
145         rc = explain_pre_specs(buf, n, decl->specifiers);
146         ret += advance(&buf, &n, rc);
147
148         return ret + explain_post_specs(buf, n, decl->specifiers);
149 }