xref: /freebsd/contrib/pkgconf/cli/renderer-msvc.c (revision a3cefe7f2b4df0f70ff92d4570ce18e517af43ec)
1 /*
2  * renderer-msvc.c
3  * MSVC library syntax renderer
4  *
5  * Copyright (c) 2017 pkgconf authors (see AUTHORS).
6  *
7  * Permission to use, copy, modify, and/or distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * This software is provided 'as is' and without any warranty, express or
12  * implied.  In no event shall the authors be liable for any damages arising
13  * from the use of this software.
14  */
15 
16 #include <string.h>
17 #include <stdlib.h>
18 
19 #include <libpkgconf/libpkgconf.h>
20 #include "renderer-msvc.h"
21 
22 static inline bool
fragment_should_quote(const pkgconf_fragment_t * frag)23 fragment_should_quote(const pkgconf_fragment_t *frag)
24 {
25 	const char *src;
26 
27 	if (frag->data == NULL)
28 		return false;
29 
30 	for (src = frag->data; *src; src++)
31 	{
32 		if (((*src < ' ') ||
33 		    (*src >= (' ' + (frag->children.head != NULL ? 1 : 0)) && *src < '$') ||
34 		    (*src > '$' && *src < '(') ||
35 		    (*src > ')' && *src < '+') ||
36 		    (*src > ':' && *src < '=') ||
37 		    (*src > '=' && *src < '@') ||
38 		    (*src > 'Z' && *src < '^') ||
39 		    (*src == '`') ||
40 		    (*src > 'z' && *src < '~') ||
41 		    (*src > '~')))
42 			return true;
43 	}
44 
45 	return false;
46 }
47 
48 static inline size_t
fragment_len(const pkgconf_fragment_t * frag)49 fragment_len(const pkgconf_fragment_t *frag)
50 {
51 	size_t len = 1;
52 
53 	if (frag->type)
54 		len += 2;
55 
56 	if (frag->data != NULL)
57 	{
58 		len += strlen(frag->data);
59 
60 		if (fragment_should_quote(frag))
61 			len += 2;
62 	}
63 
64 	return len;
65 }
66 
67 static inline bool
allowed_fragment(const pkgconf_fragment_t * frag)68 allowed_fragment(const pkgconf_fragment_t *frag)
69 {
70 	return !(!frag->type || frag->data == NULL || strchr("DILl", frag->type) == NULL);
71 }
72 
73 static size_t
msvc_renderer_render_len(const pkgconf_list_t * list,bool escape)74 msvc_renderer_render_len(const pkgconf_list_t *list, bool escape)
75 {
76 	(void) escape;
77 
78 	size_t out = 1;		/* trailing nul */
79 	pkgconf_node_t *node;
80 
81 	PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
82 	{
83 		const pkgconf_fragment_t *frag = node->data;
84 
85 		if (!allowed_fragment(frag))
86 			continue;
87 
88 		switch (frag->type)
89 		{
90 			case 'L':
91 				out += 9; /* "/libpath:" */
92 				break;
93 			case 'l':
94 				out += 4; /* ".lib" */
95 				break;
96 			default:
97 				break;
98 		}
99 
100 		out += fragment_len(frag);
101 	}
102 
103 	return out;
104 }
105 
106 static void
msvc_renderer_render_buf(const pkgconf_list_t * list,char * buf,size_t buflen,bool escape)107 msvc_renderer_render_buf(const pkgconf_list_t *list, char *buf, size_t buflen, bool escape)
108 {
109 	pkgconf_node_t *node;
110 	char *bptr = buf;
111 
112 	memset(buf, 0, buflen);
113 
114 	PKGCONF_FOREACH_LIST_ENTRY(list->head, node)
115 	{
116 		const pkgconf_fragment_t *frag = node->data;
117 		size_t buf_remaining = buflen - (bptr - buf);
118 		size_t cnt;
119 
120 		if (!allowed_fragment(frag))
121 			continue;
122 
123 		if (fragment_len(frag) > buf_remaining)
124 			break;
125 
126 		switch(frag->type) {
127 		case 'D':
128 		case 'I':
129 			*bptr++ = '/';
130 			*bptr++ = frag->type;
131 			break;
132 		case 'L':
133 			cnt = pkgconf_strlcpy(bptr, "/libpath:", buf_remaining);
134 			bptr += cnt;
135 			buf_remaining -= cnt;
136 			break;
137 		}
138 
139 		escape = fragment_should_quote(frag);
140 
141 		if (escape)
142 			*bptr++ = '"';
143 
144 		cnt = pkgconf_strlcpy(bptr, frag->data, buf_remaining);
145 		bptr += cnt;
146 		buf_remaining -= cnt;
147 
148 		if (frag->type == 'l')
149 		{
150 			cnt = pkgconf_strlcpy(bptr, ".lib", buf_remaining);
151 			bptr += cnt;
152 		}
153 
154 		if (escape)
155 			*bptr++ = '"';
156 
157 		*bptr++ = ' ';
158 	}
159 
160 	*bptr = '\0';
161 }
162 
163 static const pkgconf_fragment_render_ops_t msvc_renderer_ops = {
164 	.render_len = msvc_renderer_render_len,
165 	.render_buf = msvc_renderer_render_buf
166 };
167 
168 const pkgconf_fragment_render_ops_t *
msvc_renderer_get(void)169 msvc_renderer_get(void)
170 {
171 	return &msvc_renderer_ops;
172 }
173