1 | /*****************************************************************************/ |
2 | /* */ |
3 | /* attr.c */ |
4 | /* */ |
5 | /* Command line attributes */ |
6 | /* */ |
7 | /* */ |
8 | /* */ |
9 | /* (C) 2012, Ullrich von Bassewitz */ |
10 | /* Roemerstrasse 52 */ |
11 | /* D-70794 Filderstadt */ |
12 | /* EMail: uz@cc65.org */ |
13 | /* */ |
14 | /* */ |
15 | /* This software is provided 'as-is', without any expressed or implied */ |
16 | /* warranty. In no event will the authors be held liable for any damages */ |
17 | /* arising from the use of this software. */ |
18 | /* */ |
19 | /* Permission is granted to anyone to use this software for any purpose, */ |
20 | /* including commercial applications, and to alter it and redistribute it */ |
21 | /* freely, subject to the following restrictions: */ |
22 | /* */ |
23 | /* 1. The origin of this software must not be misrepresented; you must not */ |
24 | /* claim that you wrote the original software. If you use this software */ |
25 | /* in a product, an acknowledgment in the product documentation would be */ |
26 | /* appreciated but is not required. */ |
27 | /* 2. Altered source versions must be plainly marked as such, and must not */ |
28 | /* be misrepresented as being the original software. */ |
29 | /* 3. This notice may not be removed or altered from any source */ |
30 | /* distribution. */ |
31 | /* */ |
32 | /*****************************************************************************/ |
33 | |
34 | |
35 | |
36 | #include <stdio.h> |
37 | #include <string.h> |
38 | |
39 | /* common */ |
40 | #include "chartype.h" |
41 | #include "strbuf.h" |
42 | #include "xmalloc.h" |
43 | |
44 | /* sp65 */ |
45 | #include "attr.h" |
46 | #include "error.h" |
47 | |
48 | |
49 | |
50 | /*****************************************************************************/ |
51 | /* Code */ |
52 | /*****************************************************************************/ |
53 | |
54 | |
55 | |
56 | Attr* NewAttr (const char* Name, const char* Value) |
57 | /* Create a new attribute */ |
58 | { |
59 | /* Determine the string lengths */ |
60 | unsigned NameLen = strlen (Name); |
61 | unsigned ValueLen = strlen (Value); |
62 | |
63 | /* Allocate memory */ |
64 | Attr* A = xmalloc (sizeof (Attr) + ValueLen + NameLen + 1); |
65 | |
66 | /* Initialize the fields */ |
67 | A->Name = A->Value + ValueLen + 1; |
68 | memcpy (A->Value, Value, ValueLen + 1); |
69 | memcpy (A->Name, Name, NameLen + 1); |
70 | |
71 | /* Return the new struct */ |
72 | return A; |
73 | } |
74 | |
75 | |
76 | |
77 | void FreeAttr (Attr* A) |
78 | /* Free an attribute structure */ |
79 | { |
80 | xfree (A); |
81 | } |
82 | |
83 | |
84 | |
85 | void DumpAttrColl (const Collection* C) |
86 | /* Dump a collection of attribute/value pairs for debugging */ |
87 | { |
88 | unsigned I; |
89 | for (I = 0; I < CollCount (C); ++I) { |
90 | const Attr* A = CollConstAt (C, I); |
91 | printf ("%s=%s\n" , A->Name, A->Value); |
92 | } |
93 | } |
94 | |
95 | |
96 | |
97 | int FindAttr (const Collection* C, const char* Name, unsigned* Index) |
98 | /* Search for an attribute with the given name in the collection. If it is |
99 | ** found, the function returns true and Index contains the index of the |
100 | ** entry. If Name isn't found, the function returns false and Index |
101 | ** will contain the insert position. |
102 | */ |
103 | { |
104 | /* Do a binary search */ |
105 | int Lo = 0; |
106 | int Hi = (int) CollCount (C) - 1; |
107 | while (Lo <= Hi) { |
108 | |
109 | /* Mid of range */ |
110 | int Cur = (Lo + Hi) / 2; |
111 | |
112 | /* Get item */ |
113 | const Attr* A = CollAt (C, Cur); |
114 | |
115 | /* Compare */ |
116 | int Res = strcmp (A->Name, Name); |
117 | |
118 | /* Found? */ |
119 | if (Res < 0) { |
120 | Lo = Cur + 1; |
121 | } else if (Res > 0) { |
122 | Hi = Cur - 1; |
123 | } else { |
124 | /* Found! */ |
125 | *Index = Cur; |
126 | return 1; |
127 | } |
128 | } |
129 | |
130 | /* Pass back the insert position */ |
131 | *Index = Lo; |
132 | return 0; |
133 | } |
134 | |
135 | |
136 | |
137 | const Attr* GetAttr (const Collection* C, const char* Name) |
138 | /* Search for an attribute with the given name and return it. The function |
139 | ** returns NULL if the attribute wasn't found. |
140 | */ |
141 | { |
142 | /* Search for the attribute and return it */ |
143 | unsigned Index; |
144 | if (FindAttr (C, Name, &Index)) { |
145 | return CollConstAt (C, Index); |
146 | } else { |
147 | /* Not found */ |
148 | return 0; |
149 | } |
150 | } |
151 | |
152 | |
153 | |
154 | const Attr* NeedAttr (const Collection* C, const char* Name, const char* Op) |
155 | /* Search for an attribute with the given name and return it. If the attribute |
156 | ** is not found, the function terminates with an error using Op as additional |
157 | ** context in the error message. |
158 | */ |
159 | { |
160 | /* Search for the attribute and return it */ |
161 | unsigned Index; |
162 | if (!FindAttr (C, Name, &Index)) { |
163 | Error ("Found no attribute named '%s' for operation %s" , Name, Op); |
164 | } |
165 | return CollConstAt (C, Index); |
166 | } |
167 | |
168 | |
169 | |
170 | const char* GetAttrVal (const Collection* C, const char* Name) |
171 | /* Search for an attribute with the given name and return its value. The |
172 | ** function returns NULL if the attribute wasn't found. |
173 | */ |
174 | { |
175 | const Attr* A = GetAttr (C, Name); |
176 | return (A == 0)? 0 : A->Value; |
177 | } |
178 | |
179 | |
180 | |
181 | const char* NeedAttrVal (const Collection* C, const char* Name, const char* Op) |
182 | /* Search for an attribute with the given name and return its value. If the |
183 | ** attribute wasn't not found, the function terminates with an error using |
184 | ** Op as additional context in the error message. |
185 | */ |
186 | { |
187 | const Attr* A = NeedAttr (C, Name, Op); |
188 | return (A == 0)? 0 : A->Value; |
189 | } |
190 | |
191 | |
192 | |
193 | void AddAttr (Collection* C, const char* Name, const char* Value) |
194 | /* Add an attribute to an alphabetically sorted attribute collection */ |
195 | { |
196 | /* Create a new attribute entry */ |
197 | Attr* A = NewAttr (Name, Value); |
198 | |
199 | /* Search for the attribute. If it is there, we have a duplicate, otherwise |
200 | ** we have the insert position. |
201 | */ |
202 | unsigned Index; |
203 | if (FindAttr (C, Name, &Index)) { |
204 | Error ("Duplicate command line attribute '%s'" , Name); |
205 | } |
206 | |
207 | /* Insert the attribute */ |
208 | CollInsert (C, A, Index); |
209 | } |
210 | |
211 | |
212 | |
213 | void SplitAddAttr (Collection* C, const char* Combined, const char* Name) |
214 | /* Split a combined name/value pair and add it as an attribute to C. Some |
215 | ** attributes may not need a name. If the name is missing, use Name. If |
216 | ** Name is NULL, terminate with an error. |
217 | */ |
218 | { |
219 | /* Name and value are separated by an equal sign */ |
220 | const char* Pos = strchr (Combined, '='); |
221 | if (Pos == 0) { |
222 | /* Combined is actually a value */ |
223 | if (Name == 0) { |
224 | Error ("Command line attribute '%s' doesn't contain a name" , Combined); |
225 | } |
226 | AddAttr (C, Name, Combined); |
227 | } else { |
228 | /* Must split name and value */ |
229 | StrBuf N = AUTO_STRBUF_INITIALIZER; |
230 | SB_CopyBuf (&N, Combined, Pos - Combined); |
231 | SB_Terminate (&N); |
232 | |
233 | /* Add the attribute */ |
234 | AddAttr (C, SB_GetConstBuf (&N), Pos+1); |
235 | |
236 | /* Release memory */ |
237 | SB_Done (&N); |
238 | } |
239 | } |
240 | |
241 | |
242 | |
243 | Collection* ParseAttrList (const char* List, const char* const* NameList, unsigned NameCount) |
244 | /* Parse a list containing name/value pairs into a sorted collection. Some |
245 | ** attributes may not need a name, so NameList contains these names. If there |
246 | ** were no errors, the function returns a alphabetically sorted collection |
247 | ** containing Attr entries. |
248 | */ |
249 | { |
250 | const char* Name; |
251 | |
252 | /* Create a new collection */ |
253 | Collection* C = NewCollection (); |
254 | |
255 | /* Name/value pairs are separated by commas */ |
256 | const char* L = List; |
257 | StrBuf B = AUTO_STRBUF_INITIALIZER; |
258 | while (1) { |
259 | if (*L == ',' || *L == ':' || *L == '\0') { |
260 | |
261 | /* Terminate the string */ |
262 | SB_Terminate (&B); |
263 | |
264 | /* Determine the default name */ |
265 | if (CollCount (C) >= NameCount) { |
266 | Name = 0; |
267 | } else { |
268 | Name = NameList[CollCount (C)]; |
269 | } |
270 | |
271 | /* Split and add this attribute/value pair */ |
272 | SplitAddAttr (C, SB_GetConstBuf (&B), Name); |
273 | |
274 | /* Done, clear the buffer. */ |
275 | SB_Clear (&B); |
276 | if (*L == '\0') { |
277 | break; |
278 | } |
279 | } else { |
280 | SB_AppendChar (&B, *L); |
281 | } |
282 | ++L; |
283 | } |
284 | |
285 | /* Free memory */ |
286 | SB_Done (&B); |
287 | |
288 | /* Return the collection with the attributes */ |
289 | return C; |
290 | } |
291 | |
292 | |
293 | |
294 | void FreeAttrList (Collection* C) |
295 | /* Free a list of attributes */ |
296 | { |
297 | unsigned I; |
298 | |
299 | /* Walk over the collection and free all attributes */ |
300 | for (I = 0; I < CollCount (C); ++I) { |
301 | FreeAttr (CollAtUnchecked (C, I)); |
302 | } |
303 | |
304 | /* Free the collection itself */ |
305 | FreeCollection (C); |
306 | } |
307 | |